{"input": "/*\n *  Copyright 2021 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/blocking_kernel.cuh>\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/cuda_stream.cuh>\n#include <nvbench/detail/throw.cuh>\n#include <nvbench/types.cuh>\n\n#include <cuda/std/chrono>\n\n#include <cuda_runtime.h>\n\n#include <cstdio>\n#include <cstdlib>\n\nnamespace\n{\n\n// Once launched, this kernel will block the stream until `flag` updates is\n// non-zero. If `timeout` seconds pass, an error will be printed and the stream\n// will unblock.\n__global__ void block_stream(const volatile nvbench::int32_t *flag,\n                             volatile nvbench::int32_t *timeout_flag,\n                             nvbench::float64_t timeout)\n{\n  const auto start_point = cuda::std::chrono::high_resolution_clock::now();\n  const auto timeout_ns =\n    cuda::std::chrono::nanoseconds(static_cast<nvbench::int64_t>(timeout * 1e9));\n  const auto timeout_point = start_point + timeout_ns;\n\n  const bool use_timeout = timeout >= 0.;\n  auto now               = cuda::std::chrono::high_resolution_clock::now();\n  while (!(*flag) && (!use_timeout || now < timeout_point))\n  {\n    now = cuda::std::chrono::high_resolution_clock::now();\n  }\n\n  if (now >= timeout_point)\n  {\n    *timeout_flag = 1;\n    __threadfence_system(); // Ensure timeout flag visibility on host.\n    printf(\"\\n\"\n           \"######################################################################\\n\"\n           \"##################### Possible Deadlock Detected #####################\\n\"\n           \"######################################################################\\n\"\n           \"\\n\"\n           \"Forcing unblock: The current measurement appears to have deadlocked\\n\"\n           \"and the results cannot be trusted.\\n\"\n           \"\\n\"\n           \"This happens when the KernelLauncher synchronizes the CUDA device.\\n\"\n           \"If this is the case, pass the `sync` exec_tag to the `exec` call:\\n\"\n           \"\\n\"\n           \"    state.exec(<KernelLauncher>); // Deadlock\\n\"\n           \"    state.exec(nvbench::exec_tag::sync, <KernelLauncher>); // Safe\\n\"\n           \"\\n\"\n           \"This tells NVBench about the sync so it can run the benchmark safely.\\n\"\n           \"\\n\"\n           \"If the KernelLauncher does not synchronize but has a very long \\n\"\n           \"execution time, this may be a false positive. If so, disable this\\n\"\n           \"check with:\\n\"\n           \"\\n\"\n           \"    state.set_blocking_kernel_timeout(-1);\\n\"\n           \"\\n\"\n           \"The current timeout is set to %0.5g seconds.\\n\"\n           \"\\n\"\n           \"For more information, see the 'Benchmarks that sync' section of the\\n\"\n           \"NVBench documentation.\\n\"\n           \"\\n\"\n           \"If this happens while profiling with an external tool,\\n\"\n           \"pass the `--profile` flag to the executable to disable use of blocking kernel\\n\"\n           \"and to also run the benchmark only once.\\n\"\n           \"\\n\"\n           \"For more information, see the 'Benchmark Properties' section of the\\n\"\n           \"NVBench documentation.\\n\\n\",\n           timeout);\n  }\n}\n\n} // namespace\n\nnamespace nvbench\n{\n\nblocking_kernel::blocking_kernel()\n{\n  NVBENCH_CUDA_CALL(cudaHostRegister(&m_host_flag, sizeof(m_host_flag), cudaHostRegisterMapped));\n  NVBENCH_CUDA_CALL(cudaHostGetDevicePointer(&m_device_flag, &m_host_flag, 0));\n  NVBENCH_CUDA_CALL(\n    cudaHostRegister(&m_host_timeout_flag, sizeof(m_host_timeout_flag), cudaHostRegisterMapped));\n  NVBENCH_CUDA_CALL(cudaHostGetDevicePointer(&m_device_timeout_flag, &m_host_timeout_flag, 0));\n}\n\nblocking_kernel::~blocking_kernel()\n{\n  NVBENCH_CUDA_CALL_NOEXCEPT(cudaHostUnregister(&m_host_flag));\n  NVBENCH_CUDA_CALL_NOEXCEPT(cudaHostUnregister(&m_host_timeout_flag));\n}\n\nvoid blocking_kernel::block(const nvbench::cuda_stream &stream, nvbench::float64_t timeout)\n{\n  m_host_flag         = 0;\n  m_host_timeout_flag = 0;\n  block_stream<<<1, 1, 0, stream>>>(m_device_flag, m_device_timeout_flag, timeout);\n}\n\nvoid blocking_kernel::timeout_detected()\n{\n  NVBENCH_THROW(std::runtime_error,\n                \"{}\",\n                \"Deadlock detected -- missing nvbench::exec_tag::sync? \"\n                \"See stdout for details.\");\n}\n\n} // namespace nvbench\n", "output": "/*\n *  Copyright 2021 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/blocking_kernel.cuh>\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/cuda_stream.cuh>\n#include <nvbench/detail/throw.cuh>\n#include <nvbench/types.cuh>\n\n#include <cuda/std/chrono>\n\n#include <hip/hip_runtime.h>\n\n#include <cstdio>\n#include <cstdlib>\n\nnamespace\n{\n\n// Once launched, this kernel will block the stream until `flag` updates is\n// non-zero. If `timeout` seconds pass, an error will be printed and the stream\n// will unblock.\n__global__ void block_stream(const volatile nvbench::int32_t *flag,\n                             volatile nvbench::int32_t *timeout_flag,\n                             nvbench::float64_t timeout)\n{\n  const auto start_point = cuda::std::chrono::high_resolution_clock::now();\n  const auto timeout_ns =\n    cuda::std::chrono::nanoseconds(static_cast<nvbench::int64_t>(timeout * 1e9));\n  const auto timeout_point = start_point + timeout_ns;\n\n  const bool use_timeout = timeout >= 0.;\n  auto now               = cuda::std::chrono::high_resolution_clock::now();\n  while (!(*flag) && (!use_timeout || now < timeout_point))\n  {\n    now = cuda::std::chrono::high_resolution_clock::now();\n  }\n\n  if (now >= timeout_point)\n  {\n    *timeout_flag = 1;\n    __threadfence_system(); // Ensure timeout flag visibility on host.\n    printf(\"\\n\"\n           \"######################################################################\\n\"\n           \"##################### Possible Deadlock Detected #####################\\n\"\n           \"######################################################################\\n\"\n           \"\\n\"\n           \"Forcing unblock: The current measurement appears to have deadlocked\\n\"\n           \"and the results cannot be trusted.\\n\"\n           \"\\n\"\n           \"This happens when the KernelLauncher synchronizes the CUDA device.\\n\"\n           \"If this is the case, pass the `sync` exec_tag to the `exec` call:\\n\"\n           \"\\n\"\n           \"    state.exec(<KernelLauncher>); // Deadlock\\n\"\n           \"    state.exec(nvbench::exec_tag::sync, <KernelLauncher>); // Safe\\n\"\n           \"\\n\"\n           \"This tells NVBench about the sync so it can run the benchmark safely.\\n\"\n           \"\\n\"\n           \"If the KernelLauncher does not synchronize but has a very long \\n\"\n           \"execution time, this may be a false positive. If so, disable this\\n\"\n           \"check with:\\n\"\n           \"\\n\"\n           \"    state.set_blocking_kernel_timeout(-1);\\n\"\n           \"\\n\"\n           \"The current timeout is set to %0.5g seconds.\\n\"\n           \"\\n\"\n           \"For more information, see the 'Benchmarks that sync' section of the\\n\"\n           \"NVBench documentation.\\n\"\n           \"\\n\"\n           \"If this happens while profiling with an external tool,\\n\"\n           \"pass the `--profile` flag to the executable to disable use of blocking kernel\\n\"\n           \"and to also run the benchmark only once.\\n\"\n           \"\\n\"\n           \"For more information, see the 'Benchmark Properties' section of the\\n\"\n           \"NVBench documentation.\\n\\n\",\n           timeout);\n  }\n}\n\n} // namespace\n\nnamespace nvbench\n{\n\nblocking_kernel::blocking_kernel()\n{\n  NVBENCH_CUDA_CALL(hipHostRegister(&m_host_flag, sizeof(m_host_flag), hipHostRegisterMapped));\n  NVBENCH_CUDA_CALL(hipHostGetDevicePointer(&m_device_flag, &m_host_flag, 0));\n  NVBENCH_CUDA_CALL(\n    hipHostRegister(&m_host_timeout_flag, sizeof(m_host_timeout_flag), hipHostRegisterMapped));\n  NVBENCH_CUDA_CALL(hipHostGetDevicePointer(&m_device_timeout_flag, &m_host_timeout_flag, 0));\n}\n\nblocking_kernel::~blocking_kernel()\n{\n  NVBENCH_CUDA_CALL_NOEXCEPT(hipHostUnregister(&m_host_flag));\n  NVBENCH_CUDA_CALL_NOEXCEPT(hipHostUnregister(&m_host_timeout_flag));\n}\n\nvoid blocking_kernel::block(const nvbench::cuda_stream &stream, nvbench::float64_t timeout)\n{\n  m_host_flag         = 0;\n  m_host_timeout_flag = 0;\n  block_stream<<<1, 1, 0, stream>>>(m_device_flag, m_device_timeout_flag, timeout);\n}\n\nvoid blocking_kernel::timeout_detected()\n{\n  NVBENCH_THROW(std::runtime_error,\n                \"{}\",\n                \"Deadlock detected -- missing nvbench::exec_tag::sync? \"\n                \"See stdout for details.\");\n}\n\n} // namespace nvbench\n", "source": "/kaggle/working/nvbench/nvbench/blocking_kernel.cu"}
{"input": "/*\n *  Copyright 2025 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/cuda_stream.cuh>\n#include <nvbench/detail/timestamps_kernel.cuh>\n#include <nvbench/types.cuh>\n\n#include <cuda_runtime.h>\n\n#include <cstdio>\n#include <cstdlib>\n\nnamespace\n{\n\n__global__ void get_timestamps_kernel(nvbench::uint64_t *global_timestamp,\n                                      nvbench::uint64_t *sm0_timestamp)\n{\n  nvbench::uint32_t smid;\n  asm volatile(\"mov.u32 %0, %%smid;\" : \"=r\"(smid));\n  if (smid == 0)\n  {\n    nvbench::uint64_t gts, lts;\n    asm volatile(\"mov.u64 %0, %%globaltimer;\" : \"=l\"(gts));\n    lts = clock64();\n\n    *global_timestamp = gts;\n    *sm0_timestamp    = lts;\n  }\n}\n\n} // namespace\n\nnamespace nvbench::detail\n{\n\ntimestamps_kernel::timestamps_kernel()\n{\n  NVBENCH_CUDA_CALL(\n    cudaHostRegister(&m_host_timestamps, sizeof(nvbench::uint64_t) * 2, cudaHostRegisterMapped));\n  NVBENCH_CUDA_CALL(cudaHostGetDevicePointer(&m_device_timestamps, &m_host_timestamps, 0));\n}\n\ntimestamps_kernel::~timestamps_kernel()\n{\n  NVBENCH_CUDA_CALL_NOEXCEPT(cudaHostUnregister(&m_host_timestamps));\n}\n\nvoid timestamps_kernel::record(const nvbench::cuda_stream &stream)\n{\n  m_host_timestamps[0] = 0;\n  m_host_timestamps[1] = 0;\n\n  int device_id = 0;\n  int num_sms   = 0;\n\n  NVBENCH_CUDA_CALL(cudaGetDevice(&device_id));\n  NVBENCH_CUDA_CALL(cudaDeviceGetAttribute(&num_sms, cudaDevAttrMultiProcessorCount, device_id));\n\n  get_timestamps_kernel<<<static_cast<unsigned int>(num_sms), 1, 0, stream.get_stream()>>>(\n    m_device_timestamps,\n    m_device_timestamps + 1);\n}\n\n} // namespace nvbench::detail\n", "output": "/*\n *  Copyright 2025 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/cuda_stream.cuh>\n#include <nvbench/detail/timestamps_kernel.cuh>\n#include <nvbench/types.cuh>\n\n#include <hip/hip_runtime.h>\n\n#include <cstdio>\n#include <cstdlib>\n\nnamespace\n{\n\n__global__ void get_timestamps_kernel(nvbench::uint64_t *global_timestamp,\n                                      nvbench::uint64_t *sm0_timestamp)\n{\n  nvbench::uint32_t smid;\n  asm volatile(\"mov.u32 %0, %%smid;\" : \"=r\"(smid));\n  if (smid == 0)\n  {\n    nvbench::uint64_t gts, lts;\n    asm volatile(\"mov.u64 %0, %%globaltimer;\" : \"=l\"(gts));\n    lts = clock64();\n\n    *global_timestamp = gts;\n    *sm0_timestamp    = lts;\n  }\n}\n\n} // namespace\n\nnamespace nvbench::detail\n{\n\ntimestamps_kernel::timestamps_kernel()\n{\n  NVBENCH_CUDA_CALL(\n    hipHostRegister(&m_host_timestamps, sizeof(nvbench::uint64_t) * 2, hipHostRegisterMapped));\n  NVBENCH_CUDA_CALL(hipHostGetDevicePointer(&m_device_timestamps, &m_host_timestamps, 0));\n}\n\ntimestamps_kernel::~timestamps_kernel()\n{\n  NVBENCH_CUDA_CALL_NOEXCEPT(hipHostUnregister(&m_host_timestamps));\n}\n\nvoid timestamps_kernel::record(const nvbench::cuda_stream &stream)\n{\n  m_host_timestamps[0] = 0;\n  m_host_timestamps[1] = 0;\n\n  int device_id = 0;\n  int num_sms   = 0;\n\n  NVBENCH_CUDA_CALL(hipGetDevice(&device_id));\n  NVBENCH_CUDA_CALL(hipDeviceGetAttribute(&num_sms, hipDeviceAttributeMultiprocessorCount, device_id));\n\n  get_timestamps_kernel<<<static_cast<unsigned int>(num_sms), 1, 0, stream.get_stream()>>>(\n    m_device_timestamps,\n    m_device_timestamps + 1);\n}\n\n} // namespace nvbench::detail\n", "source": "/kaggle/working/nvbench/nvbench/detail/timestamps_kernel.cu"}
{"input": "/*\n *  Copyright 2021 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/nvbench.cuh>\n\n// Thrust vectors simplify memory management:\n#include <thrust/device_vector.h>\n\ntemplate <int ItemsPerThread>\n__global__ void kernel(std::size_t stride,\n                       std::size_t elements,\n                       const nvbench::int32_t *__restrict__ in,\n                       nvbench::int32_t *__restrict__ out)\n{\n  const std::size_t tid  = threadIdx.x + blockIdx.x * blockDim.x;\n  const std::size_t step = gridDim.x * blockDim.x;\n\n  for (std::size_t i = stride * tid; i < stride * elements; i += stride * step)\n  {\n    for (int j = 0; j < ItemsPerThread; j++)\n    {\n      const auto read_id  = (ItemsPerThread * i + j) % elements;\n      const auto write_id = tid + j * elements;\n      out[write_id]       = in[read_id];\n    }\n  }\n}\n\n// `throughput_bench` copies a 128 MiB buffer of int32_t, and reports throughput\n// and cache hit rates.\n//\n// Calling state.collect_*() enables particular metric collection if nvbench\n// was build with CUPTI support (CMake option: -DNVBench_ENABLE_CUPTI=ON).\ntemplate <int ItemsPerThread>\nvoid throughput_bench(nvbench::state &state, nvbench::type_list<nvbench::enum_type<ItemsPerThread>>)\n{\n  // Allocate input data:\n  const std::size_t stride   = static_cast<std::size_t>(state.get_int64(\"Stride\"));\n  const std::size_t elements = 128 * 1024 * 1024 / sizeof(nvbench::int32_t);\n  thrust::device_vector<nvbench::int32_t> input(elements);\n  thrust::device_vector<nvbench::int32_t> output(elements * ItemsPerThread);\n\n  // Provide throughput information:\n  state.add_element_count(elements, \"Elements\");\n  state.collect_dram_throughput();\n  state.collect_l1_hit_rates();\n  state.collect_l2_hit_rates();\n  state.collect_loads_efficiency();\n  state.collect_stores_efficiency();\n\n  const auto threads_in_block = 256;\n  const auto blocks_in_grid =\n    static_cast<int>((elements + threads_in_block - 1) / threads_in_block);\n\n  state.exec([&](nvbench::launch &launch) {\n    kernel<ItemsPerThread><<<blocks_in_grid, threads_in_block, 0, launch.get_stream()>>>(\n      stride,\n      elements,\n      thrust::raw_pointer_cast(input.data()),\n      thrust::raw_pointer_cast(output.data()));\n  });\n}\n\nusing items_per_thread = nvbench::enum_type_list<1, 2>;\n\nNVBENCH_BENCH_TYPES(throughput_bench, NVBENCH_TYPE_AXES(items_per_thread))\n  .set_type_axes_names({\"ItemsPerThread\"})\n  .add_int64_axis(\"Stride\", nvbench::range(1, 4, 3));\n", "output": "/*\n *  Copyright 2021 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <nvbench/nvbench.cuh>\n\n// Thrust vectors simplify memory management:\n#include <thrust/device_vector.h>\n\ntemplate <int ItemsPerThread>\n__global__ void kernel(std::size_t stride,\n                       std::size_t elements,\n                       const nvbench::int32_t *__restrict__ in,\n                       nvbench::int32_t *__restrict__ out)\n{\n  const std::size_t tid  = threadIdx.x + blockIdx.x * blockDim.x;\n  const std::size_t step = gridDim.x * blockDim.x;\n\n  for (std::size_t i = stride * tid; i < stride * elements; i += stride * step)\n  {\n    for (int j = 0; j < ItemsPerThread; j++)\n    {\n      const auto read_id  = (ItemsPerThread * i + j) % elements;\n      const auto write_id = tid + j * elements;\n      out[write_id]       = in[read_id];\n    }\n  }\n}\n\n// `throughput_bench` copies a 128 MiB buffer of int32_t, and reports throughput\n// and cache hit rates.\n//\n// Calling state.collect_*() enables particular metric collection if nvbench\n// was build with CUPTI support (CMake option: -DNVBench_ENABLE_CUPTI=ON).\ntemplate <int ItemsPerThread>\nvoid throughput_bench(nvbench::state &state, nvbench::type_list<nvbench::enum_type<ItemsPerThread>>)\n{\n  // Allocate input data:\n  const std::size_t stride   = static_cast<std::size_t>(state.get_int64(\"Stride\"));\n  const std::size_t elements = 128 * 1024 * 1024 / sizeof(nvbench::int32_t);\n  thrust::device_vector<nvbench::int32_t> input(elements);\n  thrust::device_vector<nvbench::int32_t> output(elements * ItemsPerThread);\n\n  // Provide throughput information:\n  state.add_element_count(elements, \"Elements\");\n  state.collect_dram_throughput();\n  state.collect_l1_hit_rates();\n  state.collect_l2_hit_rates();\n  state.collect_loads_efficiency();\n  state.collect_stores_efficiency();\n\n  const auto threads_in_block = 256;\n  const auto blocks_in_grid =\n    static_cast<int>((elements + threads_in_block - 1) / threads_in_block);\n\n  state.exec([&](nvbench::launch &launch) {\n    kernel<ItemsPerThread><<<blocks_in_grid, threads_in_block, 0, launch.get_stream()>>>(\n      stride,\n      elements,\n      thrust::raw_pointer_cast(input.data()),\n      thrust::raw_pointer_cast(output.data()));\n  });\n}\n\nusing items_per_thread = nvbench::enum_type_list<1, 2>;\n\nNVBENCH_BENCH_TYPES(throughput_bench, NVBENCH_TYPE_AXES(items_per_thread))\n  .set_type_axes_names({\"ItemsPerThread\"})\n  .add_int64_axis(\"Stride\", nvbench::range(1, 4, 3));\n", "source": "/kaggle/working/nvbench/examples/auto_throughput.cu"}
{"input": "#include <nvbench/cuda_call.cuh>\n\n#include \"test_asserts.cuh\"\n\nnamespace\n{\n__global__ void multiply5(const int32_t *__restrict__ a, int32_t *__restrict__ b)\n{\n  const auto id = blockIdx.x * blockDim.x + threadIdx.x;\n  b[id]         = 5 * a[id];\n}\n} // namespace\n\nint main()\n{\n  multiply5<<<256, 256>>>(nullptr, nullptr);\n\n  try\n  {\n    NVBENCH_CUDA_CALL(cudaStreamSynchronize(0));\n    ASSERT(false);\n  }\n  catch (const std::runtime_error &)\n  {\n    ASSERT(cudaGetLastError() == cudaError_t::cudaSuccess);\n  }\n\n  return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <nvbench/cuda_call.cuh>\n\n#include \"test_asserts.cuh\"\n\nnamespace\n{\n__global__ void multiply5(const int32_t *__restrict__ a, int32_t *__restrict__ b)\n{\n  const auto id = blockIdx.x * blockDim.x + threadIdx.x;\n  b[id]         = 5 * a[id];\n}\n} // namespace\n\nint main()\n{\n  multiply5<<<256, 256>>>(nullptr, nullptr);\n\n  try\n  {\n    NVBENCH_CUDA_CALL(hipStreamSynchronize(0));\n    ASSERT(false);\n  }\n  catch (const std::runtime_error &)\n  {\n    ASSERT(hipGetLastError() == hipError_t::hipSuccess);\n  }\n\n  return 0;\n}\n", "source": "/kaggle/working/nvbench/testing/reset_error.cu"}
{"input": "/*\n *  Copyright 2024 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/nvbench.cuh>\n\n/******************************************************************************\n * Install custom parser.\n * sSee <nvbench/main.cuh> for more details.\n ******************************************************************************/\n\n//\n// Step 1: Define a custom argument handler that accepts a vector of strings.\n//          - This handler should modify the vector in place to remove any custom\n//            arguments it handles. NVbench will then parse the remaining arguments.\n//          - The handler should also update any application state needed to handle\n//            the custom arguments.\n//\n\n// User code to handle a specific argument:\nvoid handle_my_custom_arg();\n\n// NVBench hook for modifying the command line arguments before parsing:\nvoid custom_arg_handler(std::vector<std::string> &args)\n{\n  // Handle and remove \"--my-custom-arg\"\n  if (auto it = std::find(args.begin(), args.end(), \"--my-custom-arg\"); it != args.end())\n  {\n    handle_my_custom_arg();\n    args.erase(it);\n  }\n}\n\n//\n// Step 2: Install the custom argument handler.\n//         - This is done by defining a macro that invokes the custom argument handler.\n//\n\n// Install the custom argument handler:\n// Either define this before any NVBench headers are included, or undefine and redefine:\n#undef NVBENCH_MAIN_CUSTOM_ARGS_HANDLER\n#define NVBENCH_MAIN_CUSTOM_ARGS_HANDLER(args) custom_arg_handler(args)\n\n// Step 3: Define `main`\n//\n// After installing the custom argument handler, define the main function using:\n//\n// ```\n// NVBENCH_MAIN\n// ```\n//\n// Here, this is done at the end of this file.\n\n/******************************************************************************\n * Unit test verification:\n ******************************************************************************/\n\n// Track whether the args are found / handled.\nbool h_custom_arg_found             = false;\nbool h_handled_on_device            = false;\n__device__ bool d_custom_arg_found  = false;\n__device__ bool d_handled_on_device = false;\n\n// Copy host values to device:\nvoid copy_host_state_to_device()\n{\n  NVBENCH_CUDA_CALL(cudaMemcpyToSymbol(d_custom_arg_found, &h_custom_arg_found, sizeof(bool)));\n  NVBENCH_CUDA_CALL(cudaMemcpyToSymbol(d_handled_on_device, &h_handled_on_device, sizeof(bool)));\n}\n\n// Copy device values to host:\nvoid copy_device_state_to_host()\n{\n  NVBENCH_CUDA_CALL(cudaMemcpyFromSymbol(&h_custom_arg_found, d_custom_arg_found, sizeof(bool)));\n  NVBENCH_CUDA_CALL(cudaMemcpyFromSymbol(&h_handled_on_device, d_handled_on_device, sizeof(bool)));\n}\n\nvoid handle_my_custom_arg()\n{\n  h_custom_arg_found = true;\n  copy_host_state_to_device();\n}\n\nvoid verify()\n{\n  copy_device_state_to_host();\n  if (!h_custom_arg_found)\n  {\n    throw std::runtime_error(\"Custom argument not detected.\");\n  }\n  if (!h_handled_on_device)\n  {\n    throw std::runtime_error(\"Custom argument not handled on device.\");\n  }\n}\n\n// Install a verification check to ensure the custom argument was handled.\n// Use the `PRE` finalize hook to ensure we check device state before resetting the context.\n#undef NVBENCH_MAIN_FINALIZE_CUSTOM_PRE\n#define NVBENCH_MAIN_FINALIZE_CUSTOM_PRE() verify()\n\n// Simple kernel/benchmark to make sure that the handler can successfully modify CUDA state:\n__global__ void kernel()\n{\n  if (d_custom_arg_found)\n  {\n    d_handled_on_device = true;\n  }\n}\nvoid bench(nvbench::state &state)\n{\n  state.exec([](nvbench::launch &) { kernel<<<1, 1>>>(); });\n}\nNVBENCH_BENCH(bench);\n\n// Define the customized main function:\nNVBENCH_MAIN\n", "output": "/*\n *  Copyright 2024 NVIDIA Corporation\n *\n *  Licensed under the Apache License, Version 2.0 with the LLVM exception\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.\n *\n *  You may obtain a copy of the License at\n *\n *      http://llvm.org/foundation/relicensing/LICENSE.txt\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <nvbench/cuda_call.cuh>\n#include <nvbench/nvbench.cuh>\n\n/******************************************************************************\n * Install custom parser.\n * sSee <nvbench/main.cuh> for more details.\n ******************************************************************************/\n\n//\n// Step 1: Define a custom argument handler that accepts a vector of strings.\n//          - This handler should modify the vector in place to remove any custom\n//            arguments it handles. NVbench will then parse the remaining arguments.\n//          - The handler should also update any application state needed to handle\n//            the custom arguments.\n//\n\n// User code to handle a specific argument:\nvoid handle_my_custom_arg();\n\n// NVBench hook for modifying the command line arguments before parsing:\nvoid custom_arg_handler(std::vector<std::string> &args)\n{\n  // Handle and remove \"--my-custom-arg\"\n  if (auto it = std::find(args.begin(), args.end(), \"--my-custom-arg\"); it != args.end())\n  {\n    handle_my_custom_arg();\n    args.erase(it);\n  }\n}\n\n//\n// Step 2: Install the custom argument handler.\n//         - This is done by defining a macro that invokes the custom argument handler.\n//\n\n// Install the custom argument handler:\n// Either define this before any NVBench headers are included, or undefine and redefine:\n#undef NVBENCH_MAIN_CUSTOM_ARGS_HANDLER\n#define NVBENCH_MAIN_CUSTOM_ARGS_HANDLER(args) custom_arg_handler(args)\n\n// Step 3: Define `main`\n//\n// After installing the custom argument handler, define the main function using:\n//\n// ```\n// NVBENCH_MAIN\n// ```\n//\n// Here, this is done at the end of this file.\n\n/******************************************************************************\n * Unit test verification:\n ******************************************************************************/\n\n// Track whether the args are found / handled.\nbool h_custom_arg_found             = false;\nbool h_handled_on_device            = false;\n__device__ bool d_custom_arg_found  = false;\n__device__ bool d_handled_on_device = false;\n\n// Copy host values to device:\nvoid copy_host_state_to_device()\n{\n  NVBENCH_CUDA_CALL(hipMemcpyToSymbol(HIP_SYMBOL(d_custom_arg_found), &h_custom_arg_found, sizeof(bool)));\n  NVBENCH_CUDA_CALL(hipMemcpyToSymbol(HIP_SYMBOL(d_handled_on_device), &h_handled_on_device, sizeof(bool)));\n}\n\n// Copy device values to host:\nvoid copy_device_state_to_host()\n{\n  NVBENCH_CUDA_CALL(hipMemcpyFromSymbol(&h_custom_arg_found,HIP_SYMBOL(d_custom_arg_found), sizeof(bool)));\n  NVBENCH_CUDA_CALL(hipMemcpyFromSymbol(&h_handled_on_device,HIP_SYMBOL(d_handled_on_device), sizeof(bool)));\n}\n\nvoid handle_my_custom_arg()\n{\n  h_custom_arg_found = true;\n  copy_host_state_to_device();\n}\n\nvoid verify()\n{\n  copy_device_state_to_host();\n  if (!h_custom_arg_found)\n  {\n    throw std::runtime_error(\"Custom argument not detected.\");\n  }\n  if (!h_handled_on_device)\n  {\n    throw std::runtime_error(\"Custom argument not handled on device.\");\n  }\n}\n\n// Install a verification check to ensure the custom argument was handled.\n// Use the `PRE` finalize hook to ensure we check device state before resetting the context.\n#undef NVBENCH_MAIN_FINALIZE_CUSTOM_PRE\n#define NVBENCH_MAIN_FINALIZE_CUSTOM_PRE() verify()\n\n// Simple kernel/benchmark to make sure that the handler can successfully modify CUDA state:\n__global__ void kernel()\n{\n  if (d_custom_arg_found)\n  {\n    d_handled_on_device = true;\n  }\n}\nvoid bench(nvbench::state &state)\n{\n  state.exec([](nvbench::launch &) { kernel<<<1, 1>>>(); });\n}\nNVBENCH_BENCH(bench);\n\n// Define the customized main function:\nNVBENCH_MAIN\n", "source": "/kaggle/working/nvbench/testing/custom_main_custom_args.cu"}
{"input": "#include <cuda_device_runtime_api.h>\n#include <cuda_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include \"utils.cuh\"\n#include <curand_kernel.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n\n#include <string.h>\n#include <sys/stat.h> // For mkdir\n#include <sys/types.h> // For mode_t\n\nnamespace cg = cooperative_groups;\n\n// ------------------------------------------------------------------------------------------------\n// Linear layer\ntypedef struct {\n    float* w; // OC, C\n    float* b; // OC\n} LinearParams;\n\ntypedef struct {\n    float* inp; // N, C\n    float* out; // N, OC\n} LinearActs;\n\n\nvoid linear_set_param_ptrs(\n    LinearParams* params,\n    float* params_memory,\n    int C, int OC\n) {\n    params->w = params_memory;\n    params_memory += OC * C;\n    params->b = params_memory;\n}\n\ninline size_t linear_count_params(int C, int OC) {\n    return OC * C + OC;\n}\n// ---------------------------------------------\n// kernels\n\n// copy the matmul forward and backward kernels from llm.c\n\n// is there no better way other than just adding bias with a whole separate kernel?\n// this is a highly memory-bound operation, should be fused into the matmul kernel\n// but i can't seem to find a cuBLAS function that does this\n__global__ void add_bias(float* out, const float* bias, int N, int OC) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = idx; i < N * OC; i += stride) {\n        int col = i % OC;\n        out[i] += bias[col];\n    }\n}\n\n// kernel 2 calls cuBLAS, which should be very efficient\nvoid matmul_forward2(cublasHandle_t cublas_handle,\n                     float* out,\n                     const float* inp, const float* weight, const float* bias,\n                     int N, int C, int OC,\n                     const int block_size) {\n    // for reference API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n    // for us, inp is (B*T, C), weight is (OC, C), out is (B*T, OC)\n    // cuBLAS does C = alpha * A * B + beta * C\n    // where A is mxk, B is kxn, C is mxn\n    // now, because we use row-major storage, cuBLAS (which is column-major) sees our matrices transposed.\n    // algorithmically / in e.g. PyTorch we want to do: out = inp @ weight.T\n    // but because cuBLAS is column-major, we actually want to get it to calculate out.T . Mathematically, this is:\n    // out.T = weight @ inp.T\n    // but again, our variables look transposed, so using the actual weight/inp we have here in this function, this becomes\n    // out.T = weight.T @ inp\n    // so we need to get cuBLAS to calculate weight.T @ inp (the variables here are the actual ones in this function)\n    // => need to call cuBLAS with A = weight, B = inp\n    // => need to call cuBLAS with transa = CUBLAS_OP_T, transb = CUBLAS_OP_N\n\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, N, C, &alpha, weight, C, inp, C, &beta, out, OC));\n    // and now we still have to add the bias... (ew)\n    if (bias != NULL) {\n        int grid_size = ceil_div(OC * N, block_size);\n        add_bias<<<grid_size, block_size>>>(out, bias, N, OC);\n        cudaCheck(cudaGetLastError());\n    }\n}\n\n// use shared memory and coarsening + reductions\n__global__ void matmul_backward_bias_kernel_faster(float* dbias, const float* dout, int N, int OC) {\n    extern __shared__ float shared[];\n    int o = blockIdx.x; // range [0, OC)\n    int tid = threadIdx.x; // range [0, block_size)\n    int block_size = blockDim.x;\n    const float* x = dout + o;\n    // thread coarsening\n    double sum = 0.0;\n    for (int i = tid; i < N; i += block_size) {\n        sum += x[i * OC];\n    }\n    shared[tid] = (float) sum;\n    __syncthreads();\n    // reductions\n    for (int stride = block_size / 2; stride >= 1; stride /= 2) {\n        __syncthreads();\n        if (tid < stride) {\n            shared[tid] += shared[tid + stride];\n        }\n    }\n    // write the final result (at thread 0) to global memory\n    if (tid == 0) {\n        dbias[o] = shared[0];\n    }\n}\n\n// version1: simple cuBLAS calls\nvoid matmul_backward1(cublasHandle_t cublas_handle,\n                      float* dinp, float* dweight, float* dbias,\n                      float* dout, float* inp, float* weight,\n                      int N, int C, int OC) {\n    float alpha = 1.0f;\n    float beta = 0.0f; // note we must use beta = 1.0 so that we do a +=, as we should, because gradients add\n\n    // for reference the API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n\n    // recall the forward pass was calculated with alpha = 1.0f, beta = 0.0f as:\n    // cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, B*T, C, &alpha, weight, C, inp, C, &beta, out, OC);\n\n    // backward to input\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, C, N, OC, &alpha, weight, C, dout, OC, &beta, dinp, C));\n    // backward to weight\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, C, OC, N, &alpha, inp, C, dout, OC, &beta, dweight, C));\n    // backward to bias, if given\n    if (dbias != NULL) {\n\n        // sum over B,T using matrix vector multiplication with cuBLAS\n        // for reference this API is:\n        // cublasStatus_t cublasSgemv(cublasHandle_t handle, cublasOperation_t trans,\n        //                    int m, int n,\n        //                    const float           *alpha,\n        //                    const float           *A, int lda,\n        //                    const float           *x, int incx,\n        //                    const float           *beta,\n        //                    float           *y, int incy)\n        // dout is (B,T,OC), or in 2D terms (B*T, OC)\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_N, B*T, OC, &alpha, dout, B*T, ones, 1, &beta, dbias, 1));\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_T, OC, B*T, &alpha, dout, OC, ones, 1, &beta, dbias, 1));\n\n        // ugh the above isn't working...\n        // let's just do naive calculation for now, fix later\n        // const int block_size=128;\n        // const int grid_size=(OC + block_size - 1) / block_size;\n        // matmul_backward_bias_kernel<<<grid_size, block_size>>>(dbias, dout, B, T, OC);\n\n        // bit faster\n        const int block_size=512;\n        dim3 block_dim(block_size);\n        dim3 grid_dim(OC);\n        size_t shared_mem_size = block_size * sizeof(float);\n        matmul_backward_bias_kernel_faster<<<grid_dim, block_dim, shared_mem_size>>>(dbias, dout, N, OC);\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Broadcast tensor layer\n\n// kernel to broadcast a tensor from shape (B, C) to (B, C, H, W)\n// Assuming one thread per pixel\n__global__ void broadcast_last_dims_forward_kernel(\n    const float* x,\n    float* out,\n    int N, int H, int W, int n_bk_per_img\n) {\n    int n = blockIdx.x / n_bk_per_img;\n    __shared__ float x_sh[1];\n    if (threadIdx.x == 0) {\n        x_sh[0] = x[n];\n    }\n    __syncthreads();\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N * H * W) {\n        out[idx] = x_sh[0];\n    }\n}\n\nvoid broadcast_last_dims_forward(\n    const float* x,\n    float* out,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    int n_bk_per_img = ceil_div(H * W, block_size);\n    broadcast_last_dims_forward_kernel<<<N * n_bk_per_img, block_size>>>(x, out, N, H, W, n_bk_per_img);\n};\n\n// backwards for broadcast\n// use one warp per image\n__global__ void broadcast_last_dims_backward_kernel(\n    const float* dout,\n    float* dx,\n    int N, int H, int W\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    int idx = blockIdx.x * warp.meta_group_size() + warp.meta_group_rank();\n    if (idx >= N) { return; }\n\n    int img_size = H * W;\n    dout += idx * img_size;\n    dx += idx;\n\n    float sum = 0.0f;\n    for (int i = warp.thread_rank(); i < img_size; i += warp.size()) {\n        sum += dout[i];\n    }\n    sum = cg::reduce(warp, sum, cg::plus<float>());\n    if (warp.thread_rank() == 0) {\n        dx[0] = sum;\n    }\n}\n\nvoid broadcast_last_dims_backward(\n    const float* dout,\n    float* dx,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    assert(block_size % 32 == 0);\n    int warps_per_block = block_size / 32;\n    int gridDim = ceil_div(N, warps_per_block);\n    broadcast_last_dims_backward_kernel<<<gridDim, block_size>>>(dout, dx, N, H, W);\n};\n\n// ------------------------------------------------------------------------------------------------\n// Add layer\n__global__ void add_forward_kernel(\n    const float* a, const float* b,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        out[idx] = a[idx] + b[idx];\n    }\n}\n\nvoid add_forward(\n    const float* a, const float* b,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_forward_kernel<<<n_blk, block_size>>>(a, b, out, N);\n}\n\n// add A and B in place, and store result in B\n__global__ void add_inplace_forward_kernel(\n    const float* a, float* b,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        b[idx] += a[idx];\n    }\n}\n\nvoid add_inplace_forward(\n    const float* a, float* b,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_inplace_forward_kernel<<<n_blk, block_size>>>(a, b, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// SiLU layer\n\ntypedef struct {\n    float* x;\n    float* out;\n} SiluActs;\n\n// ----------------------------------------------\n// kernels\n\n__global__ void silu_forward_kernel(\n    const float* x,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n\n    float x_val = x[idx];\n    out[idx] = x_val / (1.0f + expf(-x_val));\n}\n\nvoid silu_forward(\n    const float* x,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_forward_kernel<<<n_blk, block_size>>>(x, out, N);\n}\n\n__global__ void silu_backward_kernel(\n    const float* dout, const float* x,\n    float* dx,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n    float out_val = dout[idx];\n    float x_val = x[idx];\n    float expx = expf(-x_val);\n    float grad_silu = (1.0f + x_val * expx / (1.0f + expx)) / (1.0f + expx);\n    dx[idx] = out_val * grad_silu;\n}\n\nvoid silu_backward(\n    const float* dout, const float* x,\n    float* dx,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_backward_kernel<<<n_blk, block_size>>>(dout, x, dx, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Upsample\ntypedef struct {\n    float* x;\n    float* out;\n} UpsampleActs;\n\n// one thread per pixel\n__global__ void upsample_forward_kernel(\n    const float* x,\n    float* out,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx >= B * C * img_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_size);\n    int c = (flat_idx / img_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + i * W + j;\n    out += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float x_val = x[0];\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            out[ii * W_out + jj] = x_val;\n        }\n    }\n}\n\nvoid upsample_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\n// number of threads equals the number of pixels\n// in downsampled image\n__global__ void upsample_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_in_size = H * W;\n    if (flat_idx >= B * C * img_in_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_in_size);\n    int c = (flat_idx / img_in_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    dx += b * C * img_in_size + c * img_in_size + i * W + j;\n    dout += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float dout_sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dout_sum += dout[ii * W_out + jj];\n        }\n    }\n    dx[0] = dout_sum;\n}\n\nvoid upsample_backward1(\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Avgpool layer\n\ntypedef struct {\n    float* x;\n    float* out;\n} AvgpoolActs;\n\n__global__ void avgpool_2d_forward_kernel(\n    const float* x,  // (B, C, H, W)\n    float* out, // (B, C, H/2, W/2)\n    int B, int C, int H, int W\n) {\n    //printf(\"started kernel\\n\");\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int H_2 = H / 2;\n    int W_2 = W / 2;\n    int img_out_size = H_2 * W_2;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int img_size = H * W;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_2) % H_2;\n    int j = flat_idx % W_2;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + 2 * i * W + 2 * j;\n    out += b * C * img_out_size + c * img_out_size + i * W_2 + j;\n\n    float sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            sum += x[ii * W + jj];\n        }\n    }\n    out[0] = sum / 4.0f;\n}\n\nvoid avgpool_2d_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int gridDim = ceil_div(B * C * (H / 2) * (W / 2), block_size);\n    avgpool_2d_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\n__global__ void avgpool_2d_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H_out, int W_out // H_out and W_out are the shape of the output from forward, i.e. half the original sizes\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_out_size = H_out * W_out;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int H_in = H_out * 2;\n    int W_in = W_out * 2;\n    int img_in_size = H_in * W_in;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_out) % H_out;\n    int j = flat_idx % W_out;\n\n    // move pointers\n    dout += b * C * img_out_size + c * img_out_size + i * W_out + j;\n    dx += b * C * img_in_size + c * img_in_size + 2 * i * W_in + 2 * j;\n\n    float d = dout[0] / 4.0f;\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dx[ii * W_in + jj] = d;\n        }\n    }\n}\n\nvoid avgpool_2d_backward1(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int H_out = H / 2;\n    int W_out = W / 2;\n    int gridDim = ceil_div(B * C * H_out * W_out, block_size);\n    avgpool_2d_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Concat channel layer\n// used to concat unet skip connections in the channel dimension\ntypedef struct {\n    float* x1;\n    float* x2;\n    float* out;\n} ConcatChannelActs;\n\n__global__ void concat_channel_forward_kernel(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        // copy input from x1\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n        out[out_idx] = x1[idx];\n    }\n    if (idx < B * C2 * H * W) {\n        // copy input from x2\n        // move over from x1\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        out[out_idx] = x2[idx];\n    }\n}\n\nvoid concat_channel_forward(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_forward_kernel<<<n_blk, block_size>>>(x1, x2, out, B, C1, C2, H, W);\n}\n\n__global__ void concat_channel_backward_kernel(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n\n        dx1[idx] = dout[out_idx];\n    }\n    if (idx < B * C2 * H * W) {\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        dx2[idx] = dout[out_idx];\n    }\n}\n\nvoid concat_channel_backward(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_backward_kernel<<<n_blk, block_size>>>(dout, dx1, dx2, B, C1, C2, H, W);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Conv2d 1x1 layer\n\ntemplate <const int BO, const int BH, const int TO, const int TH>\n__global__ void conv2d_k1_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int H, int W, int O\n) {\n    // constants\n    const int BC = 16;\n    assert(O % BO == 0);\n    assert(C % BC == 0);\n    assert(H * W % BH == 0);\n    // n_threads per block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int block_col = blockIdx.x % blocks_per_col;\n    const int batch = blockIdx.x / blocks_per_col;\n\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for weight and x\n    const int weight_s_sz = BO * BC;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[weight_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // local registers for results;\n    float thread_results[TO * TH] = {0.0};\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // get offsets to x and weight\n    int x_block_offset = batch * C * H * W + block_col * BH;\n    int w_block_offset = block_row * BO * C;\n\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx;\n        // load weights to smem\n        for (int i = threadIdx.x; i < weight_s_sz; i += blockDim.x) {\n            int w_i = i / BC;\n            int w_j = i % BC;\n            int w_abs_idx = w_block_offset + w_inner_offset + w_i * C + w_j;\n            w_s[w_i * BC + w_j] = weight[w_abs_idx];\n        }\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            int x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            x_s[x_i * BH + x_j] = x[x_abs_idx];\n        }\n        __syncthreads();\n\n\n        // calculate thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            for (int i = 0; i < TO; i++) {\n                reg_w[i] = w_s[(thread_row * TO + i) * BC + dot_idx];\n            }\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int i = 0; i < TO; i++) {\n                for (int j = 0; j < TH; j++) {\n                    thread_results[i * TH + j] += reg_w[i] * reg_x[j];\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // set out and bias directly to thread offset\n    int out_block_offset = batch * O * H * W + block_row * BO * H * W + block_col * BH;\n    int bias_offset = block_row * BO;\n    for (int i = 0; i < TO; i++) {\n        for (int j = 0; j < TH; j++) {\n            int bias_idx = bias_offset + thread_row * TO + i;\n            float out_val = thread_results[i * TH + j] + bias[bias_idx];\n            int out_idx = out_block_offset + thread_row * TO * H * W + thread_col * TH + i * H * W + j;\n            atomicAdd(out + out_idx, out_val);\n        }\n    }\n}\n\nvoid conv2d_k1_forward2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int H, int W, int C_out\n) {\n    cudaCheck(cudaMemset(out, 0, B * C_out * H * W * sizeof(float)));\n    const int BO = 64;\n    const int BH = 64;\n    const int TO = 4;\n    const int TH = 4;\n    dim3 gridDim(B * H * W / BH, C_out / BO);\n    dim3 blockDim((BO * BH) / (TO * TH));\n    conv2d_k1_forward_kernel<BO, BH, TO, TH><<<gridDim, blockDim>>>(x, weight, bias, out, B, C_in, H, W, C_out);\n}\n\n__global__ void permute_dout_x_kernel(\n    const float* x, const float* dout,\n    float* x_perm, float* dout_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    int h = (flat_idx / W) % H;\n    int w = flat_idx % W;\n\n    if (flat_idx < B * C_in * H * W) {\n        // permute x\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_perm[out_ind] = x[flat_idx];\n    }\n\n    if (flat_idx < B * C_out * H * W) {\n        // permute dout\n        int b = flat_idx / (C_out * img_size);\n        int c = (flat_idx / img_size) % C_out;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        dout_perm[out_ind] = dout[flat_idx];\n    }\n}\n\n\n__global__ void permute_dx_kernel(\n    const float* dx_perm,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx < B * C_in * img_size) {\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n        int out_ind = b * C_in * img_size + c * img_size + h * W + w;\n        dx[out_ind] = dx_perm[flat_idx];\n    }\n}\n\n\n// we want to sum the B, H, W dimensions of dout\n// since dout_perm is shaped as (C_out, B, H, W)\n// we will have each block take care of one entry of C_out\n// and the threads in each block will use coorperative_groups to do the sum\n__global__ void dbias_kernel(\n    const float* dout_perm, // shape (C_out, B, H, W)\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    extern __shared__ float shared[];\n\n    // move dout_perm to the C_out index\n    if (threadIdx.x < bhw) {\n        dout_perm += blockIdx.x * B * H * W;\n        float th_sum = 0.0f;\n        for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n            th_sum += dout_perm[i];\n        }\n        shared[threadIdx.x] = th_sum;\n    }\n    __syncthreads();\n\n    // reduce the shared memory\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            shared[threadIdx.x] += shared[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = shared[0];\n    }\n}\n\n// uses cublas\nvoid conv2d_k1_backward1(\n    cublasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // shapes:\n    // x, dx: (B, C_in, H, W)\n    // weight: (C_out, C_in)\n    // bias: (C_out,)\n    // dout: (B, C_out, H, W)\n\n    // plan (the shapes are raw array shapes, note that cublas interprets the arrays as transposed):\n    // 1. transpose x to x_perm of shape (C_in, B*H*W), and dout to dout_perm of shape (C_out, B*H*W)\n    // 2. do one sgemm call to get dw = x.T @ dout_perm, and another call to get dx_perm = dout @ w.T\n    // 3. dx_perm is of shape (C_in, B*H*W), so call a kernel to permute it\n    // 4. reduce to get dbias\n    float *x_perm, *dout_perm, *dx_perm;\n    cudaCheck(cudaMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&dout_perm, C_out * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&dx_perm, C_in * B * H * W * sizeof(float)));\n\n    // 1. permute x and dout\n    int total_th1 = (C_in > C_out) ? C_in * B * H * W : C_out * B * H * W;\n    int n_blk1 = ceil_div(total_th1, block_size);\n    permute_dout_x_kernel<<<n_blk1, block_size>>>(x, dout, x_perm, dout_perm, B, C_in, C_out, H, W);\n\n    // 2.a sgemm call for dw = x_perm.T @ dout_perm\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, C_in, C_out, non_C_dim, &alpha, x_perm, non_C_dim, dout_perm, non_C_dim, &beta, dweight, C_in));\n\n    // 2.b sgemm call for dx_perm = dout_perm @ weight.T\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, non_C_dim, C_in, C_out, &alpha, dout_perm, non_C_dim, weight, C_in, &beta, dx_perm, non_C_dim));\n\n    // 3. permute dx_perm from (C_out, B, H, W) to (B, C_out, H, W)\n    int total_th2 = C_in * B * H * W;\n    int n_blk2 = ceil_div(total_th2, block_size);\n    permute_dx_kernel<<<n_blk2, block_size>>>(dx_perm, dx, B, C_in, H, W);\n\n    // 4. reduce dout to get dbias\n    dbias_kernel<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(cudaFree(x_perm));\n    cudaCheck(cudaFree(dout_perm));\n    cudaCheck(cudaFree(dx_perm));\n}\n\n// ------------------------------------------------------------------------------------------------\n// Conv2d 3x3 layer\ntypedef struct {\n    float* w;\n    float* b;\n} ConvK3Params;\n\ninline void convk3_set_param_ptrs(\n    ConvK3Params* params,\n    float* params_memory,\n    int C_in, int C_out\n) {\n    params->w = params_memory;\n    params->b = params->w + C_in * C_out * 9;\n}\n\ninline size_t convk3_count_params(int C_in, int C_out) {\n    return C_in * C_out * 9 + C_out;\n}\n\ntypedef struct {\n    float* inp;\n    float* out;\n} ConvK3Acts;\n\n// ---------------------------------------------\n// kernels\n\n// this kernel will roughly follow kernel 5 here: https://github.com/siboehm/SGEMM_CUDA, with a few differences\n// the main operation is convolving the input X with the weights\n// weights are shaped (O, Cx9), and X is shaped (B, C, HxW)\n// We are morally doing a matmul with dimensions O, C, HxW\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_j * BO + w_i] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n// almost same as 2, but we vectorize the loads and use float4\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    assert(TH == 4); // needed for reg_x loads later, so we can do one vectorized load for each thread\n    const int C9 = C * 9;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (w_s_width / 4);\n            int inner_row = i / (w_s_width / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            w_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            w_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            w_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            w_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        // load x into smem\n        for (int i = threadIdx.x; i < x_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&x_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            // NOTE: relying on the fact that TH = 4\n            reinterpret_cast<float4 *>(reg_x)[0] = reinterpret_cast<float4 *>(&x_s[dot_idx * BH + thread_col * TH])[0];\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\nvoid conv2d_k3_forward3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // since the kernel will add to the output, we need to zero it first\n    cudaCheck(cudaMemset(out, 0, B * C_out * H * W * sizeof(float)));\n\n    if (H * W == 64) {\n        const int BH = 64;\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_in == 3) {\n        const int BH = 64;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        const int BC = 3;\n        assert(C_out % 64 == 0);\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_out == 3) {\n        const int BH = 64 * 2;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_in % 8 == 0);\n        const int BC = 8;\n        const int BO = 3;\n        const int TO = 3;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else {\n        const int BH = 64 * 4;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 8;\n        const int TO = 2;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    }\n    cudaCheck(cudaGetLastError());\n}\n\n// almost the same as conv2d_k3_forward_kernel, except that the compute and the writes\n// are different, because now we want to treat the C dimension as the inner product dimension\ntemplate <const int BO, const int BH, const int BC, const int TC, const int TH>\n__global__ void dx_backward_kernel1(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int C9 = C * 9;\n    // n_threads per block = (BC * BH) / (TC * TH)\n    // set gridDim to (BxHxW / BH, C / TC)\n    // find the output block that this thread block is responsible for\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TCxTH block that the thread is responsible\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for weights and dout\n    const int w_s_width = BC * 9;\n    const int w_s_sz = BO * w_s_width;\n    const int dout_s_sz = BO * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float dout_s[dout_s_sz];\n\n    // registers for local results\n    float thread_results[TC * TH * 9] = {0.0};\n    // regs for weights and dout\n    float reg_w[TC * 9] = {0.0};\n    float reg_dout[TH] = {0.0};\n\n    // calculate the block offsets\n    const int dout_block_offset = batch * O * H * W + block_col * BH;\n    const int w_block_offset = block_row * w_s_width;\n\n    // outer loop over BO sizes of channel O\n    for (int o_idx = 0; o_idx < O; o_idx += BO) {\n        // offsets for this inner block\n        int w_inner_offset = o_idx * C9;\n        int dout_inner_offset = o_idx * H * W;\n\n        // load weights to smem\n        if (BC % 4 == 0) {\n            // vectorize dram load\n            for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n                int inner_col = i % (w_s_width / 4);\n                int inner_row = i / (w_s_width / 4);\n                reinterpret_cast<float4 *>(&w_s[inner_row * w_s_width + inner_col * 4])[0] = \n                    reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            }\n        } else {\n            for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n                int inner_col = i % w_s_width;\n                int inner_row = i / w_s_width;\n                w_s[inner_row * w_s_width + inner_col] = weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col];\n            }\n        }\n        // load dout\n        for (int i = threadIdx.x; i < dout_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&dout_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n        \n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BO; dot_idx++) {\n            // load dout and w into registers\n            for (int j = 0; j < (TH / 4); j++) {\n                reinterpret_cast<float4 *>(&reg_dout[j * 4])[0] = \n                    reinterpret_cast<float4 *>(&dout_s[dot_idx * BH + thread_col * TH + j * 4])[0];\n            }\n            // non vectorized load\n            //for (int j = 0; j < TH; j++) {\n            //    reg_dout[j] = dout_s[dot_idx * BH + thread_col * TH + j];\n            //}\n            if (TC % 4 == 0) {\n                // vectorize smem load\n                for (int i = 0; i < (TC * 9 / 4); i++) {\n                    reinterpret_cast<float4 *>(&reg_w[i * 4])[0] = \n                        reinterpret_cast<float4 *>(&w_s[dot_idx * w_s_width + thread_row * TC * 9 + i * 4])[0];\n                }\n            } else {\n                for (int i = 0; i < TC * 9; i++) {\n                    reg_w[i] = w_s[dot_idx * w_s_width + thread_row * TC * 9 + i];\n                }\n            }\n\n            for (int k = 0; k < 9; k++) {\n                for (int i = 0; i < TC; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        thread_results[k * TC * TH + i * TH + j] += \n                            reg_w[i * 9 + k] * reg_dout[j];\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    int out_batch_offset = batch * C * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TC; i++) {\n            int c_abs = block_row * BC + thread_row * TC + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs - 1 + k_i;\n                int w_target_abs = w_abs - 1 + k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + c_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TC * TH + i * TH + j;\n                    atomicAdd(dx + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n        }\n    }\n}\n\nvoid dx_backward(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    if (H < 16) {\n        const int BH = 64;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_in == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 3;\n        const int TC = 3;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_out == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 3;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    }\n}\n\n// same as dweight backward 4, except we also do some of the reduce for dbias\n// so it doesn't have to reduce an entire BxHxW length for each O channel\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_dbias_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf, float* dbias_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = ceil_div((int)blockDim.x, 32);\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n    float thread_bias_results[TO] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    int h_step = (NH < H) ? NH - 1 : H;\n    // if NH == H, then we are loading the entire image in one go, so step by H\n    for (int h = 0; h < H; h += h_step) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1) {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1 && H > NH) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1 && W > WSZ) {\n                        // if x_w is at the last pixel, and we have more than one width sections, and we are not are the last section\n                        // then don't compute 1, 4, 7 kernels and wait for the next section to compute them\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                    // update dbias when k = 4, where dout_h and dout_w match x_h and x_w\n                    if (k == 4 && block_col == 0) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_bias_results[i] += reg_dout[i];\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out dweight results\n    for (int i = 0; i < TO; i++) {\n        int o_abs = block_row * BO + thread_row * TO + i;\n        for (int j = 0; j < TC; j++) {\n            int c_abs = block_col * BC + thread_col * TC + j;\n            for (int k = 0; k < 9; k++) {\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n        // write out dbias results\n        if (block_col == 0) {\n            dbias_buf[o_abs * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_bias_results[i];\n        }\n    }\n}\n\n// C * O blocks\n// 288 (9 * 32) threads per block, use one warp to reduce each batch dim\n__global__ void dweight_reduce_kernel(\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O\n) {\n    assert(gridDim.x == O * C);\n    int o = blockIdx.x / C;\n    int c = blockIdx.x % C;\n    // move input pointer\n    dweight_buf += o * C * 9 * B + c * 9 * B;\n    assert(blockDim.x == 32 * 9);\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        thread_sum += dweight_buf[warp_id * B + i];\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dweight[o * C * 9 + c * 9 + warp_id] = warp_sum;\n    }\n}\n\n__global__ void dbias_reduce_kernel1(\n    float* dbias_buf, float* dbias,\n    const int B\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    dbias_buf += blockIdx.x * B;\n\n    int lane_id = threadIdx.x % 32;\n\n    float db_thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        db_thread_sum += dbias_buf[i];\n    }\n\n    float db_warp_sum = cg::reduce(warp, db_thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dbias[blockIdx.x] = db_warp_sum;\n    }\n}\n\nvoid dweight_dbias_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    float* dbias_buf, float* dbias,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    if (H * W == 64) {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8;\n        const int BW = WSZ * 8;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B);\n    } else if (C == 3) {\n        const int BC = 3;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 3;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else if (O == 3) {\n        const int BC = 64;\n        const int BO = 3;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 3;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    }\n}\n\nvoid conv2d_k3_backward2(\n    float* dout, float* x, float* weight,\n    float* dweight_buf, float* dbias_buf,\n    float* dx, float* dweight, float* dbias,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // dx currently requires the output buffer to be zeroed\n    cudaCheck(cudaMemset(dx, 0, B * C_in * H * W * sizeof(float)));\n    dx_backward(dout, weight, dx, B, C_in, C_out, H, W);\n    dweight_dbias_backward1(dout, x, dweight_buf, dweight, dbias_buf, dbias, B, C_in, C_out, H, W);\n}\n\n// ------------------------------------------------------------------------------------------------\n// GroupNorm\n\ntypedef struct {\n    float* w;\n    float* b;\n} GroupNormParams;\n\ninline void gn_set_param_ptrs(\n    GroupNormParams* params,\n    float* params_memory,\n    int C\n) {\n    params->w = params_memory;\n    params->b = params->w + C;\n}\n\ntypedef struct {\n    float* x;\n    float* out;\n    float* mean;\n    float* rstd;\n} GroupNormActs;\n\ninline void gn_set_act_ptrs(\n    GroupNormActs* acts,\n    float* acts_memory,\n    int B, int C, int H, int W, int n_groups\n) {\n    acts->out = acts_memory;\n    acts->mean = acts->out + B * C * H * W;\n    acts->rstd = acts->mean + B * n_groups;\n}\n\ntypedef struct {\n    float* dx;\n    float* dout;\n} GroupNormBackActs;\n\n// ---------------------------------------------\n// kernels\n\n// Essentially taken from llm.c's kernel 5\n// using kernel 5 because for images, each \"channel\" is effectively\n// H * W * group_size, which is quite large\n// we will have one block per group, which means there are B * C / group_size blocks\n__global__ void groupnorm_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    x += blockIdx.x * img_size * group_size;\n    out += blockIdx.x * img_size * group_size;\n    // each block will only every acces group_size channels\n    weight += g * group_size;\n    bias += g * group_size;\n\n    float thread_sum = 0.0f;\n    float thread_sum2 = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        float val = x[i];\n        thread_sum += val;\n        thread_sum2 += val * val;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    float warp_sum2 = cg:: reduce(warp, thread_sum2, cg::plus<float>{});\n    // store warp sum into shared memory\n    shared_sum[warp_id] = warp_sum;\n    shared_sum2[warp_id] = warp_sum2;\n    __syncthreads();\n    \n    // load warp sums from shared memory\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    warp_sum2 = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n    float block_sum2 = cg::reduce(warp, warp_sum2, cg::plus<float>{});\n    block_sum /= block_pixels;\n    block_sum2 /= block_pixels;\n    float m = block_sum;\n    float var = block_sum2 - m * m;\n    float s = rsqrtf(var + 1e-5f);\n    if (threadIdx.x == 0 && mean != nullptr) {\n        mean[blockIdx.x] = m;\n    }\n    if (threadIdx.x == 0 && rstd != nullptr) {\n        rstd[blockIdx.x] = s;\n    }\n\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float n = s * (x[i] - m);\n        out[i] = n * weight[c_mod_group] + bias[c_mod_group];\n    }\n}\n\nvoid groupnorm_forward(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32);\n    groupnorm_forward_kernel<<<n_blocks, block_size>>>(\n        x, weight, bias, out, mean, rstd, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(cudaGetLastError());\n}\n\n// most similar to kernel 2 in llm.c\n// not doing any float16 optimizations yet\n// main change is to have each block allocated to a single group\n__global__ void groupnorm_backward_kernel(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    dout += blockIdx.x * img_size * group_size;\n    x += blockIdx.x * img_size * group_size;\n    dx += blockIdx.x * img_size * group_size;\n    weight += g * group_size;\n    dweight += g * group_size;\n    dbias += g * group_size;\n\n    float m_val = mean[blockIdx.x];\n    float rstd_val = rstd[blockIdx.x];\n\n\n    // calculate the two mean terms in the group dimension\n    // first is dout * weight, and second is dout * weight * norm\n    // where norm = (x - mean) * rstd\n    float w_dout_thread = 0.0f;\n    float w_dout_norm_thread = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float cur_w_dout = weight[c_mod_group] * dout[i];\n        w_dout_thread += cur_w_dout;\n        float norm = (x[i] - m_val) * rstd_val;\n        w_dout_norm_thread += cur_w_dout * norm;\n    }\n    // warp reduce\n    float w_dout_warp = cg::reduce(warp, w_dout_thread, cg::plus<float>{});\n    float w_dout_norm_warp = cg::reduce(warp, w_dout_norm_thread, cg::plus<float>{});\n    // store warp sum in shared mem\n    shared_sum[warp_id] = w_dout_warp;\n    shared_sum2[warp_id] = w_dout_norm_warp;\n    __syncthreads();\n\n    // load warp sums from shared memory\n    w_dout_warp = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    w_dout_norm_warp = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float w_dout_block = cg::reduce(warp, w_dout_warp, cg::plus<float>{});\n    float w_dout_norm_block = cg::reduce(warp, w_dout_norm_warp, cg::plus<float>{});\n    w_dout_block /= block_pixels;\n    w_dout_norm_block /= block_pixels;\n\n    // update dx\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        // in bounds of image\n        // accumulate dw and db\n        float dout_val = dout[i];\n        float norm = (x[i] - m_val) * rstd_val;\n\n        // update dx\n        int c_mod_group = (i / img_size) % group_size;\n        float w_dout = weight[c_mod_group] * dout_val;\n        dx[i] = (w_dout - w_dout_block - norm * w_dout_norm_block) * rstd_val;\n    }\n    // update dw and db\n    // use different methods when the image size is large or small\n\n    // if the image size is larger than the block size\n    // loop over the channels and use the whole block on each channel\n    // otherwise, assign each warp to a channel\n    // in either case image size must be larger than the warp size\n    //assert(img_size % warp.size() == 0);\n    assert(blockDim.x % warp.size() == 0);\n    if (img_size % blockDim.x == 0) {\n        for (int c = 0; c < group_size; c++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n            for (int i = threadIdx.x; i < img_size; i += blockDim.x) {\n                float dout_val = dout[i];\n                db_thread += dout_val;\n                float norm = (x[i] - m_val) * rstd_val;\n                dw_thread += dout_val * norm;\n            }\n\n            // move pointers\n            dout += img_size;\n            x += img_size;\n\n            // warp reduce\n            float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n            float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n            ////// store warp sum in shared mem\n            if (lane_id == 0) {\n                shared_sum[warp_id] = dw_warp;\n                shared_sum2[warp_id] = db_warp;\n            }\n            __syncthreads();\n            // use the first thread to reduce the shared memory sums and save to global memory\n            if (threadIdx.x == 0) {\n                float dw_block = 0.0f;\n                float db_block = 0.0f;\n                for (int i = 0; i < num_warps; i++) {\n                    dw_block += shared_sum[i];\n                    db_block += shared_sum2[i];\n                }\n                atomicAdd(dweight + c, dw_block);\n                atomicAdd(dbias + c, db_block);\n            }\n        }\n    } else {\n        // if group size is large, need to loop over the group channels with the whole block\n        int block_reps = ceil_div(group_size, num_warps);\n        for (int br = 0; br < block_reps; br++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n\n            int ch = br * num_warps + warp_id;\n            if (ch < group_size) {\n                const float* dout_ch = dout + ch * img_size;\n                const float* x_ch = x + ch * img_size;\n                for (int i = lane_id; i < img_size; i += warp.size()) {\n                    float dout_val = dout_ch[i];\n                    db_thread += dout_val;\n                    float norm = (x_ch[i] - m_val) * rstd_val;\n                    dw_thread += dout_val * norm;\n                }\n                \n                // warp reduce\n                float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n                float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n                // since each warp takes care of an entire image\n                // directly store result\n                if (lane_id == 0) {\n                    atomicAdd(dweight + ch, dw_warp);\n                    atomicAdd(dbias + ch, db_warp);\n                }\n            }\n        }\n    }\n}\n\nvoid groupnorm_backward(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32 * group_size);\n    groupnorm_backward_kernel<<<n_blocks, block_size>>>(\n        dout, x, mean, rstd, weight, dx, dweight, dbias, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(cudaGetLastError());\n}\n// ------------------------------------------------------------------------------------------------\n// ResBlock\n\n#define NUM_RES_PARAM_TENSORS 12\ntypedef struct {\n    float* gn1_w; // C\n    float* gn1_b; // C\n    float* cv3_1_w; // C_out, C, 3, 3\n    float* cv3_1_b; // C_out\n    float* l_emb_w; // C_out, C_emb\n    float* l_emb_b; // C_out\n    float* gn2_w; // C_out\n    float* gn2_b; // C_out\n    float* cv3_2_w; // C_out, C_out, 3, 3\n    float* cv3_2_b; // C_out\n    float* res_cv1_w; // C_out, C\n    float* res_cv1_b; // C_out\n    size_t param_sizes[NUM_RES_PARAM_TENSORS];\n    size_t n_params;\n} ResBlockParameters;\n\n#define NUM_RES_ACT_TENSORS 18 // not counting input and emb, which will come from outs of previous layers\ntypedef struct {\n    float* gn1; // B, C, H, W\n    float* gn1_mean; // B, n_groups\n    float* gn1_rstd; // B, n_groups\n    float* silu1; // B, C, H, W\n    float* ud_h; // B, C, H_out, W_out\n    float* ud_x; // B, C, H_out, W_out\n    float* cv3_1; // B, C_out, H_out, W_out\n    float* silu_emb; // B, C_emb\n    float* l_emb; // B, C_out\n    float* broad_emb; // B, C_out, H_out, W_out\n    float* add1; // B, C_out, H_out, W_out\n    float* gn2; // B, C_out, H_out, W_out\n    float* gn2_mean; // B, n_groups\n    float* gn2_rstd; // B, n_groups\n    float* silu2; // B, C_out, H_out, W_out\n    float* cv3_2; // B, C_out, H_out, W_out\n    float* res_cv1; // B, C_out, H_out, W_out\n    float* add2; // B, C_out, H_out, W_out\n    size_t act_sizes[NUM_RES_ACT_TENSORS];\n    size_t n_acts;\n    float* input; // B, C, H, W\n    float* emb; // B, C_emb\n} ResBlockActivations;\n\n\n// the last two backward tensors, dx and demb\n// we don't need to allocate extra space for these\n// instead we will just overwrite forward pass activations acts->input and acts->emb\n#define NUM_RES_BACKWARD_TENSORS 7\ntypedef struct {\n    float* buf_BCemb;\n    float* buf_BCHoWo;\n    float* buf1_BCHW;\n    float* buf2_BCHW;\n    float* dout; // B, C_out, H_out, W_out\n    float* dweight_buf;\n    float* dbias_buf;\n    float* dx; // B, C, H, W\n    float* demb; // B, C_emb\n    size_t back_sizes[NUM_RES_BACKWARD_TENSORS];\n    size_t n_backs;\n} ResBlockBackwardActivations;\n\nvoid resblock_count_params(\n    ResBlockParameters* params,\n    int C, int C_emb, int C_out, int B, int H, int W, int up, int down, int gn_n_groups\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // fill and count param sizes\n    size_t* param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn1_w\n    param_sizes[1] = C; // gn1_b\n    param_sizes[2] = C_out * C * 9; // cv3_1_w\n    param_sizes[3] = C_out; // cv3_1_b\n    param_sizes[4] = C_emb * C_out; // l_emb_w\n    param_sizes[5] = C_out; // l_emb_b\n    param_sizes[6] = C_out; // gn2_w\n    param_sizes[7] = C_out; // gn2_b\n    param_sizes[8] = C_out * C_out * 9; // cv3_2_w\n    param_sizes[9] = C_out; // cv3_2_b\n    param_sizes[10] = C_out * C; // res_cv1_w\n    param_sizes[11] = C_out; // res_cv1_b\n    if (C_out != C) {\n        param_sizes[10] = C_out * C; // res_cv1_w\n        param_sizes[11] = C_out; // res_cv1_b\n    } else {\n        param_sizes[10] = 0;\n        param_sizes[11] = 0;\n    }\n\n    // count number of params\n    size_t num_parameters = 0;\n    for  (size_t i = 0; i < NUM_RES_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\n\nvoid resblock_count_acts(\n    ResBlockActivations* acts,\n    int C, int C_emb, int C_out, int B, int H, int W, int up, int down, int gn_n_groups\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // fill and count activation sizes\n    size_t* acts_sizes = acts->act_sizes;\n    acts_sizes[0] = B * C * H * W; // gn1\n    acts_sizes[1] = B * gn_n_groups; // gn1_mean\n    acts_sizes[2] = B * gn_n_groups; // gn1_rstd\n    acts_sizes[3] = B * C * H * W; // silu1\n    acts_sizes[4] = B * C * H_out * W_out; // ud_h\n    acts_sizes[5] = B * C * H_out * W_out; // ud_x\n    acts_sizes[6] = B * C_out * H_out * W_out; // cv3_1\n    acts_sizes[7] = B * C_emb; // silu_emb\n    acts_sizes[8] = B * C_out; // l_emb\n    acts_sizes[9] = B * C_out * H_out * W_out; // broad_emb\n    acts_sizes[10] = B * C_out * H_out * W_out; // add1\n    acts_sizes[11] = B * C_out * H_out * W_out; // gn2\n    acts_sizes[12] = B * gn_n_groups; // gn2_mean\n    acts_sizes[13] = B * gn_n_groups; // gn2_rstd\n    acts_sizes[14] = B * C_out * H_out * W_out; // silu2\n    acts_sizes[15] = B * C_out * H_out * W_out; // cv3_2\n    acts_sizes[16] = (C_out == C) ? 0 : B * C_out * H_out * W_out; // res_cv1\n    acts_sizes[17] = B * C_out * H_out * W_out; // add2\n\n    size_t num_acts_params = 0;\n    for (size_t i = 0; i < NUM_RES_ACT_TENSORS; i++) {\n        num_acts_params += acts_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\nvoid set_resblock_params_ptrs(\n    int C,\n    int C_out,\n    ResBlockParameters *params,\n    float* params_memory_gpu\n) {\n    float** ptrs[] = {\n        &params->gn1_w, &params->gn1_b, &params->cv3_1_w, &params->cv3_1_b,\n        &params->l_emb_w, &params->l_emb_b, &params->gn2_w, &params->gn2_b,\n        &params->cv3_2_w, &params->cv3_2_b, &params->res_cv1_w, &params->res_cv1_b\n    };\n    int num_param_tensors = NUM_RES_PARAM_TENSORS;\n    if (C_out == C) {\n        num_param_tensors -= 2;\n    }\n    float* params_ptr = params_memory_gpu;\n    for (int i = 0; i < num_param_tensors; i++) {\n        *(ptrs[i]) = params_ptr;\n        params_ptr += params->param_sizes[i];\n    }\n    if (C_out == C) {\n        params->res_cv1_w = nullptr;\n        params->res_cv1_b = nullptr;\n    }\n}\n\nvoid set_resblock_acts_ptrs(\n    int C,\n    int C_out,\n    ResBlockActivations *acts,\n    float* acts_memory\n) {\n    float** acts_ptrs[] = {\n        &acts->gn1, &acts->gn1_mean, &acts->gn1_rstd, &acts->silu1,\n        &acts->ud_h, &acts->ud_x, &acts->cv3_1, &acts->silu_emb, &acts->l_emb, &acts->broad_emb,\n        &acts->add1, &acts->gn2, &acts->gn2_mean, &acts->gn2_rstd, &acts->silu2, &acts->cv3_2,\n        &acts->res_cv1, &acts->add2\n    };\n    float* acts_ptr = acts_memory;\n    for (int i = 0; i < NUM_RES_ACT_TENSORS - 2; i++) {\n        *(acts_ptrs[i]) = acts_ptr;\n        acts_ptr += acts->act_sizes[i];\n    }\n\n    if (C_out == C) {\n        acts->res_cv1 = nullptr;\n        acts->add2 = acts_ptr;\n    } else {\n        acts->res_cv1 = acts_ptr;\n        acts_ptr += acts->act_sizes[NUM_RES_ACT_TENSORS - 2];\n        acts->add2 = acts_ptr;\n    }\n}\n\n// assume all memory is allocated, run the forward pass\nvoid resblock_forward(\n    cublasHandle_t cublas_handle,\n    int C,\n    int C_emb,\n    int C_out,\n    int B,\n    int H,\n    int W,\n    int block_size,\n    int up,\n    int down,\n    int gn_n_groups,\n    ResBlockParameters *params,\n    ResBlockActivations *acts\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // up to first 3x3 conv in BigGAN res block\n    // note: residual stream needs a convolution at the end if C != C_out\n    groupnorm_forward(acts->input, params->gn1_w, params->gn1_b, acts->gn1, acts->gn1_mean, acts->gn1_rstd,\n        B, C, H, W, gn_n_groups);\n    cudaCheck(cudaGetLastError());\n    silu_forward(acts->gn1, acts->silu1, B * C * H * W, block_size);\n    cudaCheck(cudaGetLastError());\n    if (up) {\n        upsample_forward1(acts->ud_h, acts->silu1, B, C, H, W, block_size);\n        upsample_forward1(acts->ud_x, acts->input, B, C, H, W, block_size);\n    } else if (down) {\n        avgpool_2d_forward1(acts->ud_h, acts->silu1, B, C, H, W, block_size);\n        avgpool_2d_forward1(acts->ud_x, acts->input, B, C, H, W, block_size);\n    } else {\n        cudaCheck(cudaMemcpy(acts->ud_h, acts->silu1, B * C * H * W * sizeof(float), cudaMemcpyDeviceToDevice));\n        cudaCheck(cudaMemcpy(acts->ud_x, acts->input, B * C * H * W * sizeof(float), cudaMemcpyDeviceToDevice));\n    }\n    cudaCheck(cudaGetLastError());\n    conv2d_k3_forward3(acts->ud_h, params->cv3_1_w, params->cv3_1_b, acts->cv3_1, B, C, C_out, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n\n    // project and add embedding layer\n    silu_forward(acts->emb, acts->silu_emb, B * C_emb, block_size);\n    cudaCheck(cudaGetLastError());\n    matmul_forward2(cublas_handle, acts->l_emb, acts->silu_emb, params->l_emb_w, params->l_emb_b, B, C_emb, C_out, block_size);\n    cudaCheck(cudaGetLastError());\n    broadcast_last_dims_forward(acts->l_emb, acts->broad_emb, B * C_out, H_out, W_out, block_size);\n    cudaCheck(cudaGetLastError());\n    add_forward(acts->cv3_1, acts->broad_emb, acts->add1, B * C_out * H_out * W_out, block_size);\n    cudaCheck(cudaGetLastError());\n\n    // second group of norm -> silu -> 3x3 conv\n    groupnorm_forward(acts->add1, params->gn2_w, params->gn2_b, acts->gn2, acts->gn2_mean, acts->gn2_rstd,\n        B, C_out, H_out, W_out, gn_n_groups);\n    cudaCheck(cudaGetLastError());\n    silu_forward(acts->gn2, acts->silu2, B * C_out * H_out * W_out, block_size);\n    cudaCheck(cudaGetLastError());\n    conv2d_k3_forward3(acts->silu2, params->cv3_2_w, params->cv3_2_b, acts->cv3_2, B, C_out, C_out, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n\n    // add residual (change channels with 1x1 conv if neccessary)\n    if (C_out == C) {\n        add_forward(acts->cv3_2, acts->ud_x, acts->add2, B * C_out * H_out * W_out, block_size);\n        cudaCheck(cudaGetLastError());\n    } else {\n        conv2d_k1_forward2(acts->ud_x, params->res_cv1_w, params->res_cv1_b, acts->res_cv1, B, C, H_out, W_out, C_out);\n        cudaCheck(cudaGetLastError());\n        add_forward(acts->cv3_2, acts->res_cv1, acts->add2, B * C_out * H_out * W_out, block_size);\n        cudaCheck(cudaGetLastError());\n    }\n}\n\nvoid resblock_backward(\n    cublasHandle_t cublas_handle,\n    int C,\n    int C_emb,\n    int C_out,\n    int B,\n    int H,\n    int W,\n    int block_size,\n    int up,\n    int down,\n    int gn_n_groups,\n    ResBlockParameters *params,\n    ResBlockParameters *grads,\n    ResBlockActivations *acts,\n    ResBlockBackwardActivations *back_acts\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // Note: we're using the activation of the final conv layer as a buffer to store\n    // backward pass gradients, because we don't need it in the backward pass anymore.\n    float *demb = back_acts->demb;\n    float *dx = back_acts->dx;\n    float* dout = back_acts->dout;\n    float *res_dx = back_acts->buf_BCHoWo;\n    float* buf1_BCoHoWo = acts->cv3_2;\n    float* buf2_BCoHoWo = acts->add2;\n    cudaCheck(cudaMemset(buf1_BCoHoWo, 0, B * C_out * H_out * W_out * sizeof(float)));\n    cudaCheck(cudaMemset(buf2_BCoHoWo, 0, B * C_out * H_out * W_out * sizeof(float)));\n    // Note: we don't need to run a separate kernel to backprop through an add operation\n    // we can just share the dout buffer between the two inputs\n    if (C_out != C) {\n        conv2d_k1_backward1(cublas_handle, dout, acts->ud_x, params->res_cv1_w, res_dx, grads->res_cv1_w, grads->res_cv1_b, B, C, C_out, H_out, W_out, block_size);\n        cudaCheck(cudaGetLastError());\n    } else {\n        // point res_dx to dout\n        res_dx = dout;\n    }\n    // NOTE: after backprop through cv1 above, we can use ud_x as a (B, C, H_out, W_out) buffer\n    float* buf1_BCHoWo = acts->ud_x;\n\n    // second group of norm -> silu -> 3x3 conv\n    conv2d_k3_backward2(dout, acts->silu2, params->cv3_2_w, back_acts->dweight_buf, back_acts->dbias_buf, buf1_BCoHoWo, grads->cv3_2_w, grads->cv3_2_b, B, C_out, C_out, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n    silu_backward(buf1_BCoHoWo, acts->gn2, buf2_BCoHoWo, B * C_out * H_out * W_out, block_size);\n    cudaCheck(cudaGetLastError());\n    groupnorm_backward(buf2_BCoHoWo, acts->add1, acts->gn2_mean, acts->gn2_rstd, params->gn2_w, buf1_BCoHoWo, grads->gn2_w, grads->gn2_b, B, C_out, H_out, W_out, gn_n_groups);\n    cudaCheck(cudaGetLastError());\n\n    // emb layer\n    // note at this point we can also use the last layer activations as buffers\n    broadcast_last_dims_backward(buf1_BCoHoWo, acts->l_emb, B * C_out, H_out, W_out, block_size);\n    cudaCheck(cudaGetLastError());\n    matmul_backward1(cublas_handle, back_acts->buf_BCemb, grads->l_emb_w, grads->l_emb_b, acts->l_emb, acts->silu_emb, params->l_emb_w, B, C_emb, C_out);\n    cudaCheck(cudaGetLastError());\n    silu_backward(back_acts->buf_BCemb, acts->emb, demb, B * C_emb, block_size);\n    cudaCheck(cudaGetLastError());\n\n    // first layers\n    conv2d_k3_backward2(buf1_BCoHoWo, acts->ud_h, params->cv3_1_w, back_acts->dweight_buf, back_acts->dbias_buf, buf1_BCHoWo, grads->cv3_1_w, grads->cv3_1_b, B, C, C_out, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n    // use acts->ud_x as a buffer for dx\n    float* buf1_BCHW = back_acts->buf1_BCHW;\n    float* buf2_BCHW = back_acts->buf2_BCHW;\n    if (up) {\n        upsample_backward1(buf1_BCHW, buf1_BCHoWo, B, C, H, W, block_size);\n        cudaCheck(cudaGetLastError());\n        upsample_backward1(buf2_BCHW, res_dx, B, C, H, W, block_size);\n        cudaCheck(cudaGetLastError());\n    } else if (down) {\n        avgpool_2d_backward1(buf1_BCHoWo, buf1_BCHW, B, C, H, W, block_size);\n        cudaCheck(cudaGetLastError());\n        avgpool_2d_backward1(res_dx, buf2_BCHW, B, C, H, W, block_size);\n        cudaCheck(cudaGetLastError());\n    } else {\n        cudaCheck(cudaMemcpy(buf1_BCHW, buf1_BCHoWo, B * C * H * W * sizeof(float), cudaMemcpyDeviceToDevice));\n        cudaCheck(cudaGetLastError());\n        cudaCheck(cudaMemcpy(buf2_BCHW, res_dx, B * C * H * W * sizeof(float), cudaMemcpyDeviceToDevice));\n        cudaCheck(cudaGetLastError());\n    }\n    // use silu as the dx buffer\n    silu_backward(buf1_BCHW, acts->gn1, acts->silu1, B * C * H * W, block_size);\n    cudaCheck(cudaGetLastError());\n    groupnorm_backward(acts->silu1, acts->input, acts->gn1_mean, acts->gn1_rstd, params->gn1_w, buf1_BCHW, grads->gn1_w, grads->gn1_b, B, C, H, W, gn_n_groups);\n    cudaCheck(cudaGetLastError());\n\n    add_forward(buf1_BCHW, buf2_BCHW, dx, B * C * H * W, block_size);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// self attention block\n// assumes you have Q, K, V\n__global__ void permute_kernel(float* q, float* k, float* v,\n                               const float* inp,\n                               int B, int N, int NH, int d) {\n    // okay so now, this kernel wants Q,K,V to all be of shape (B, NH, N, d)\n    // but instead, we have a single tensor QKV (inp) of shape (B, N, 3, NH, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Q[b][nh_][n][d_] = inp[b][n][0][nh_][d_]\n\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = \\\n            (b * N * 3 * NH * d)\n            +   (n * 3 * NH * d)\n            +       (0 * NH * d)\n            +          (nh_ * d)\n            +                d_;\n\n        q[idx] = inp[inp_idx];\n        k[idx] = inp[inp_idx + NH * d];\n        v[idx] = inp[inp_idx + 2 * (NH * d)];\n    }\n}\n\n__global__ void scale_kernel(float* inp, float scale, int B, int NH, int T) {\n    // scales the pre-softmax attention scores by scale\n    // we don't want causal attention, so we don't set any values to infinity\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * T * T) {\n        inp[idx] *= scale;\n    }\n}\n\n// warp-level reduction for finding the maximum value\n__device__ float warpReduceMax(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xFFFFFFFF, val, offset));\n    }\n    return val;\n}\n\n// warp-level reduction for summing values\n__device__ float warpReduceSum(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xFFFFFFFF, val, offset);\n    }\n    return val;\n}\n\n__global__ void softmax_forward_kernel4(float* out, const float* inp, int N, int C) {\n    // out is (N, C) just like inp. Each row of inp will get softmaxed.\n    // same as kernel3, but can handle any block size (multiple of 32)\n    // each row of C elements is handled by block_size threads\n    // furthermore, each block_size threads get executed in warps of 32 threads\n\n    // special reduction operations warpReduceMax/warpReduceSum are used for intra-warp reductions\n    // shared memory is used for inter-warp reduction\n    extern __shared__ float shared[];\n    int idx = blockIdx.x;\n    int tid = threadIdx.x;\n    int warpId = threadIdx.x / 32; // warp index within a block\n    int laneId = threadIdx.x % 32; // thread index within a warp\n\n    // the number of warps per block. recall that blockDim.x is block_size\n    int warpsPerBlock = blockDim.x / 32;\n\n    // shared[] must be allocated to have 2 * warpsPerBlock elements\n    // first half for max values, the second half for sum values\n    float* maxvals = shared;\n    float* sumvals = &shared[warpsPerBlock];\n\n    // one row of inp, i.e. inp[idx, :] of shape (C,)\n    const float* x = inp + idx * C;\n\n    // first, thread coarsening by directly accessing global memory in series\n    float maxval = -INFINITY;\n    for (int i = tid; i < C; i += blockDim.x) {\n        maxval = fmaxf(maxval, x[i]);\n    }\n    // now within-warp reductions for maxval\n    maxval = warpReduceMax(maxval);\n\n    // the 0th thread of each warp writes the maxval of that warp to shared memory\n    if (laneId == 0) maxvals[warpId] = maxval;\n    __syncthreads();\n\n    // now the 0th thread reduces the maxvals in shared memory, i.e. across warps\n    if (tid == 0) {\n        float val = maxvals[tid];\n        for (int i = 1; i < warpsPerBlock; i++) {\n            val = fmaxf(val, maxvals[i]);\n        }\n        // store the final max in the first position\n        maxvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the max to all threads\n    float offset = maxvals[0];\n\n    // compute expf and write the result to global memory\n    for (int i = tid; i < C; i += blockDim.x) {\n        // subtract max for numerical stability\n        out[idx * C + i] = expf(x[i] - offset);\n    }\n\n    // okay now we calculated exp(x - max(x))\n    // step 2: sum all the values and divide by the sum\n\n    // thread coarsening for sum\n    x = out + idx * C;\n    float sumval = 0.0f;\n    for (int i = tid; i < C; i += blockDim.x) {\n        sumval += x[i];\n    }\n    // within-warp reduction for sumval\n    sumval = warpReduceSum(sumval);\n\n    // write sumval to shared memory\n    if (laneId == 0) sumvals[warpId] = sumval;\n    __syncthreads();\n\n    // inter-thread reduction of sum\n    if (tid == 0) {\n        float val = sumvals[tid];\n        for (int i = 1; i < warpsPerBlock; ++i) {\n            val += sumvals[i];\n        }\n        sumvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the sum to all threads\n    float sum = sumvals[0];\n\n    // divide the whole row by the sum\n    for (int i = tid; i < C; i += blockDim.x) {\n        out[idx * C + i] = x[i] / sum;\n    }\n}\n\n__global__ void unpermute_kernel(const float* inp, float *out, int B, int N, int NH, int d) {\n   // out has shape (B, nh, N, d) but we need to unpermute it to (B, N, nh, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // out[b][n][nh_][d_] <- inp[b][nh_][n][d_]\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        out[other_idx] = inp[idx];\n    }\n}\n\n// a simple attention forward function taken directly from llm.c kernel 3\nvoid attention_forward1\n(\n    cublasHandle_t cublas_handle,\n    float* out, float* qkvr, float* preatt, float* att,\n    float* inp,\n    int B, int T, int C, int NH,\n    const int block_size\n) {\n    // note: resure inp as a scratch buffer because it is not used in backward\n    // inp is (B, T, 3C) QKV\n    // preatt, att are (B, NH, T, T)\n    // output is (B, T, C)\n    int HS = C / NH; // head size\n\n    // permute and separate inp from (B, T, 3, NH, HS) to 3X (B, NH, T, HS)\n    float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    int total_threads = B * NH * T * HS;\n    int num_blocks = ceil_div(total_threads, block_size);\n    permute_kernel<<<num_blocks, block_size>>>(q, k, v, inp, B, T, NH, HS);\n\n    // batched matrix multiply with cuBLAS\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle,\n                            CUBLAS_OP_T, CUBLAS_OP_N,\n                            T, T, HS,\n                            &alpha,\n                            k, HS, T * HS,\n                            q, HS, T * HS,\n                            &beta,\n                            preatt, T, T * T,\n                            B * NH));\n\n    // multiply all elements of preatt elementwise by scale\n    float scale = 1.0f / sqrtf(HS);\n    total_threads = B * NH * T * T;\n    num_blocks = ceil_div(total_threads, block_size);\n    scale_kernel<<<num_blocks, block_size>>>(preatt, scale, B, NH, T);\n\n    // softmax. preatt is (B, NH, T, T) but we view it as (B * NH * T, T) and use the softmax kernel\n    int softmax_block_size = 256;\n    int grid_size = B * NH * T;\n    size_t shared_mem_size = 2 * softmax_block_size / 32 * sizeof(float);\n    softmax_forward_kernel4<<<grid_size, softmax_block_size, shared_mem_size>>>(att, preatt, B * NH * T, T);\n\n    // new approach: first cuBLAS another batched matmul\n    // y = att @ v # (B, nh, T, T) @ (B, nh, T, hs) -> (B, nh, T, hs)\n    float* vaccum = inp;\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle,\n                            CUBLAS_OP_N, CUBLAS_OP_N,\n                            HS, T, T,\n                            &alpha,\n                            v, HS, T * HS,\n                            att, T, T * T,\n                            &beta,\n                            vaccum, HS, T * HS,\n                            B * NH));\n\n    // now unpermute\n    // y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side\n    num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel<<<num_blocks, block_size>>>(vaccum, out, B, T, NH, HS);\n}\n\n__global__ void permute_kernel_backward(float* dinp,\n                                        const float* dq, const float* dk, const float* dv,\n                                        int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_;\n        dinp[inp_idx] = dq[idx];\n        dinp[inp_idx + NH * d] = dk[idx];\n        dinp[inp_idx + 2 * (NH * d)] = dv[idx];\n    }\n}\n\n__global__ void unpermute_kernel_backward(float* dinp, const float *dout, int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        dinp[idx] = dout[other_idx];\n    }\n}\n\n// slight edit from the softmax_autoregressive_backward_kernel from train_gpt2_fp32\n// our attention is no longer autoregressive\n__global__ void softmax_backward_kernel(float* dpreatt, const float* datt, const float* att,\n                                                       int B, int T, int C, float scale) {\n    constexpr const int BlockSize = 256;\n    constexpr int T_per_block = 4;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float block_acc[32];\n\n    int idx = blockIdx.y;\n    // go through blocks in reverse order, so the slowest block starts first\n    int t0 = T - 1 - T_per_block*blockIdx.x;\n\n    att += idx * T * T;\n    datt += idx * T * T;\n    dpreatt += idx * T * T;\n\n    if (warp.meta_group_rank() == 0) {\n        block_acc[warp.thread_rank()] = 0;\n    }\n\n    for(int to = 0; to < T_per_block; ++to) {\n        int t = t0 - to;\n        if(t < 0) return;\n        const float* att_bth = att + t * T;\n        const float* datt_bth = datt + t * T;\n        float* dpreatt_bth = dpreatt + t * T;\n\n        float local_sum = 0.0f;\n        for (int t2 = block.thread_rank(); t2 < T; t2 += BlockSize) {\n            local_sum += att_bth[t2] * datt_bth[t2];\n        }\n\n        block_acc[warp.meta_group_rank()] = cg::reduce(warp, local_sum, cg::plus<float>{});\n        block.sync();\n        local_sum = cg::reduce(warp, block_acc[warp.thread_rank()], cg::plus<float>{});\n\n        for (int t3 = block.thread_rank(); t3 < T; t3 += BlockSize) {\n            // don't touch the cache. Some parts will still be here from the previous loop, and\n            // we want to exploit those.\n            float acc = __ldcs(att_bth + t3) * (__ldcs(datt_bth + t3) - local_sum);\n            __stcs(dpreatt_bth + t3, scale * acc);\n        }\n    }\n}\n\n// the sequence of transformations in this compound op is:\n// inp (B,T,3C) -> qkvr (B,T,3C) -> preatt (B,NH,T,T) -> att (B,NH,T,T) -> vaccum (B,T,C) -> out (B,T,C)\nvoid attention_backward\n(\n    cublasHandle_t cublas_handle,\n    float* dinp, float* dqkvr, float* dpreatt, float* datt, float* scratch,\n    const float* dout,\n    const float* qkvr, const float* att,\n    int B, int T, int C, int NH\n) {\n    const int block_size = 256;\n    int HS = C / NH; // head size\n    const float one = 1.0f;\n    const float zero = 0.0f; // note beta = 1.0f so that we accumulate gradients (+=)\n    // unpack convenience pointers into q, k, v\n    const float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    float *dq, *dk, *dv;\n    dq = dqkvr + 0 * B * T * C;\n    dk = dqkvr + 1 * B * T * C;\n    dv = dqkvr + 2 * B * T * C;\n    // backward through the unpermute operation\n    int num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel_backward<<<num_blocks, block_size>>>(scratch, dout, B, T, NH, HS);\n    cudaCheck(cudaGetLastError());\n    // backward into datt\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, T, T, HS, &one, v, HS, T * HS, scratch, HS, T * HS, &zero, datt, T, T * T, B * NH));\n    // backward into dv\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, HS, T, T, &one, scratch, HS, T * HS, att, T, T * T, &zero, dv, HS, T * HS, B * NH));\n    // backward into preatt\n    int hs = C / NH; // head size\n    float scale = 1.0f / sqrtf(hs);\n    softmax_backward_kernel<<<dim3(T / 4, B * NH), 256>>>(dpreatt, datt, att, B, T, C, scale);\n    cudaCheck(cudaGetLastError());\n    // backward into q\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, HS, T, T, &one, k, HS, T * HS, dpreatt, T, T * T, &zero, dq, HS, T * HS, B * NH));\n    // backward into k\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, HS, T, T, &one, q, HS, T * HS, dpreatt, T, T * T, &zero, dk, HS, T * HS, B * NH));\n    // backward into inp\n    num_blocks = ceil_div(B * NH * T * HS, block_size);\n    permute_kernel_backward<<<num_blocks, block_size>>>(dinp, dq, dk, dv, B, T, NH, HS);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Attention layer, including groupnorm, qkv projection, and residual stream\n\n#define NUM_ATT_PARAM_TENSORS 6\ntypedef struct {\n    float* gn_w; // C\n    float* gn_b; // C\n    float* qkv_w; // 3xC, C\n    float* qkv_b; // 3xC\n    float* proj_w; // C, C\n    float* proj_b; // C\n    size_t param_sizes[NUM_ATT_PARAM_TENSORS];\n    size_t n_params;\n} AttentionParams;\n\n#define NUM_ATT_ACT_TENSORS 12 // not counting input, which will reuse output memory from previous layer\ntypedef struct {\n    float* gn; // B, C, H, W\n    float* gn_mean; // B, n_groups\n    float* gn_rstd; // B, n_groups\n    float* perm1; // B, HxW, C\n    float* qkv1; // B, HxW, 3xC\n    float* qkv2; // B, HxW, 3xC\n    float* preatt; // B, NH, T, T\n    float* att; // B, NH, T, T\n    float* att_out; // B, HxW, C\n    float* proj; // B, HxW, C\n    float* perm2; // B, C, H, W\n    float* add; // B, C, H, W\n    size_t act_sizes[NUM_ATT_ACT_TENSORS];\n    size_t n_acts;\n    float* input; // B, C, H, W\n} AttentionActs;\n\n#define NUM_ATT_BACKWARD_ACTS_TENSORS 7  // dinp will use input memory\ntypedef struct {\n    float* buf1_BCHW; // B, C, H, W\n    float* buf2_BCHW; // B, C, H, W\n    float* buf_B3CHW; // B, 3xC, H, W\n    float* dqkvr; // B, HxW, 3xC\n    float* dpreatt; // B, NH, T, T\n    float* datt; // B, NH, T, T\n    float* dout; // B, C, H, W\n    float* dinp; // B, C, H, W\n    size_t back_sizes[NUM_ATT_BACKWARD_ACTS_TENSORS];\n    size_t n_backs;\n} AttentionBackwardActs;\n\nvoid attention_block_count_params(AttentionParams* params, int C) {\n    size_t *param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn_w\n    param_sizes[1] = C; // gn_b\n    param_sizes[2] = 3 * C * C; // qkv_w\n    param_sizes[3] = 3 * C; // qkv_b\n    param_sizes[4] = C * C; // proj_w\n    param_sizes[5] = C; // proj_b\n\n    size_t num_parameters = 0;\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\nvoid attention_block_count_acts(\n    AttentionActs* acts,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    int C3 = 3 * C;\n    int T = H * W;\n    int NH = C / HS;\n    size_t *act_sizes = acts->act_sizes;\n    act_sizes[0] = B * C * H * W; // gn\n    act_sizes[1] = B * gn_n_groups; // gn_mean\n    act_sizes[2] = B * gn_n_groups; // gn_rstd\n    act_sizes[3] = B * T * C; // perm1\n    act_sizes[4] = B * T * C3; // qkv1\n    act_sizes[5] = B * T * C3; // qkv2\n    act_sizes[6] = B * NH * T * T; // preatt\n    act_sizes[7] = B * NH * T * T; // att\n    act_sizes[8] = B * T * C; // att_out\n    act_sizes[9] = B * T * C; // proj\n    act_sizes[10] = B * C * H * W; // perm2\n    act_sizes[11] = B * C * H * W; // add\n\n    size_t num_acts_params = 0;\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        num_acts_params += act_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\nvoid set_attention_params_pointers(\n    AttentionParams* params,\n    float* params_memory\n) {\n    float** ptrs[] = {\n        &params->gn_w,\n        &params->gn_b,\n        &params->qkv_w,\n        &params->qkv_b,\n        &params->proj_w,\n        &params->proj_b\n    };\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        *(ptrs[i]) = params_memory;\n        params_memory += params->param_sizes[i];\n    }\n}\n\nvoid set_attention_acts_pointers(\n    AttentionActs* acts,\n    float* acts_memory\n) {\n    float** ptrs[] = {\n        &acts->gn,\n        &acts->gn_mean,\n        &acts->gn_rstd,\n        &acts->perm1,\n        &acts->qkv1,\n        &acts->qkv2,\n        &acts->preatt,\n        &acts->att,\n        &acts->att_out,\n        &acts->proj,\n        &acts->perm2,\n        &acts->add\n    };\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        *(ptrs[i]) = acts_memory;\n        acts_memory += acts->act_sizes[i];\n    }\n}\n\n// ----------------------------------------------------\n// kernels\n\n// this is an opportunity for improvement:\n// rewrite the attention layer so we don't need to permute the input\n__global__ void attention_permute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, C, H, W to B, HxW, C\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[b * C * img_size + h * C * W + w * C + c] = inp[idx];\n}\n\nvoid attention_permute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_permute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n__global__ void attention_unpermute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, HxW, C to B, C, H, W\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[idx] = inp[b * C * img_size + h * C * W + w * C + c];\n}\nvoid attention_unpermute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_unpermute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\nvoid attention_block_forward\n(\n    cublasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts\n) {\n    // apply groupnorm\n    groupnorm_forward(acts->input, params->gn_w, params->gn_b, acts->gn, acts->gn_mean, acts->gn_rstd, B, C, H, W, gn_n_groups);\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(acts->gn, acts->perm1, B, C, H, W, block_size);\n    // get qkv\n    matmul_forward2(cublas_handle, acts->qkv1, acts->perm1, params->qkv_w, params->qkv_b, B * H * W, C, 3 * C, block_size);\n    // apply attention\n    attention_forward1(cublas_handle, acts->att_out, acts->qkv2, acts->preatt, acts->att, acts->qkv1, B, H * W, C, C / HS, block_size);\n    // attention projection\n    matmul_forward2(cublas_handle, acts->proj, acts->att_out, params->proj_w, params->proj_b, B * H * W, C, C, block_size);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(acts->proj, acts->perm2, B, C, H, W, block_size);\n    // add back residual stream\n    add_forward(acts->input, acts->perm2, acts->add, B * C * H * W, block_size);\n}\n\nvoid attention_block_backward\n(\n    cublasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts,\n    AttentionBackwardActs* back_acts, AttentionParams* grads\n) {\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(back_acts->dout, back_acts->buf1_BCHW, B, C, H, W, block_size);\n    // backward through projection\n    matmul_backward1(cublas_handle, back_acts->buf2_BCHW, grads->proj_w, grads->proj_b, back_acts->buf1_BCHW, acts->att_out, params->proj_w, B * H * W, C, C);\n    // backward through attention\n    attention_backward(cublas_handle, back_acts->buf_B3CHW, back_acts->dqkvr, back_acts->dpreatt, back_acts->datt, back_acts->buf1_BCHW, back_acts->buf2_BCHW, acts->qkv2, acts->att, B, H * W, C, C / HS);\n    // backward through qkv\n    matmul_backward1(cublas_handle, back_acts->buf1_BCHW, grads->qkv_w, grads->qkv_b, back_acts->buf_B3CHW, acts->perm1, params->qkv_w, B * H * W, C, 3 * C);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(back_acts->buf1_BCHW, back_acts->buf2_BCHW, B, C, H, W, block_size);\n    // backward through groupnorm\n    groupnorm_backward(back_acts->buf2_BCHW, acts->input, acts->gn_mean, acts->gn_rstd, params->gn_w, back_acts->buf1_BCHW, grads->gn_w, grads->gn_b, B, C, H, W, gn_n_groups);\n    // add residual gradient\n    add_forward(back_acts->buf1_BCHW, back_acts->dout, back_acts->dinp, B * C * H * W, block_size);\n}\n\n// ------------------------------------------------------------------------------------------------\n// MSE loss\n// this will only be called with one block\n__global__ void mse_forward_kernel(\n    const float* inp, const float* y, float* loss, int N\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    __shared__ float shared_sum[32]; // max 32 warps\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        float diff = inp[i] - y[i];\n        thread_sum += diff * diff;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n    \n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        loss[0] = block_sum / N;\n    }\n}\n\nvoid mse_forward(const float* inp, const float* y, float* loss, int N, int block_size) {\n    mse_forward_kernel<<<1, block_size>>>(inp, y, loss, N);\n}\n\n// not calculating dy in backward\n// note that backward for MSE doesn't require the loss value\n__global__ void mse_backward_kernel(\n    const float* inp, const float* y, float* dinp, int N\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < N) {\n        float x = inp[idx];\n        dinp[idx] = 2.0f * (x - y[idx]) / N;\n    }\n}\n\nvoid mse_backward(const float* inp, const float* y, float* dinp, int N, int block_size) {\n    int n_blocks = ceil_div(N, block_size);\n    mse_backward_kernel<<<n_blocks, block_size>>>(inp, y, dinp, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// DataLoader\n\ntypedef struct {\n    int B;\n    int C_in;\n    int H;\n    int W;\n    int img_size;\n    int n_imgs;\n    FILE* img_file;\n    long cur_position;\n    long file_size;\n    long header_offset;\n    long num_batches;\n    float* input;\n} DataLoader;\n\nvoid dataloader_init(DataLoader* loader, const char* filename, int B) {\n    loader->B = B;\n\n    loader->img_file = fopenCheck(filename, \"rb\");\n    int data_header[256];\n    loader->header_offset = 256 * sizeof(int);\n    freadCheck(data_header, sizeof(int), 256, loader->img_file);\n    if (data_header[0] != 20240620) {\n        fprintf(stderr, \"Invalid magic data file\\n\");\n        exit(1);\n    }\n    loader->n_imgs = data_header[1];\n    loader->C_in = data_header[2];\n    loader->H = data_header[3];\n    loader->W = data_header[4];\n    loader->img_size = loader->C_in * loader->H * loader->W;\n\n    // determine file size\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    fseekCheck(loader->img_file, 0, SEEK_END);\n    loader->file_size = ftell(loader->img_file);\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    assert((loader->file_size - loader->header_offset) / (loader->img_size * sizeof(float)) == loader->n_imgs);\n    \n    loader->cur_position = loader->header_offset;\n    loader->num_batches = loader->n_imgs / B;\n\n    // allocate cuda CPU pinned memory for inputs\n    cudaMallocHost((void**)&loader->input, B * loader->img_size * sizeof(float));\n}\n\nvoid dataloader_reset(DataLoader* loader) {\n    loader->cur_position = loader->header_offset;\n}\n\nvoid dataloader_next_batch(DataLoader* loader) {\n    int B = loader->B;\n    int img_size = loader->img_size;\n    if (loader->cur_position + (B * img_size * sizeof(float)) > loader->file_size) {\n        dataloader_reset(loader);\n    }\n    fseekCheck(loader->img_file, loader->cur_position, SEEK_SET);\n    freadCheck(loader->input, sizeof(float), B * img_size, loader->img_file);\n    loader->cur_position += B * img_size * sizeof(float);\n}\n\nvoid dataloader_free(DataLoader* loader) {\n    fcloseCheck(loader->img_file);\n    cudaFreeHost(loader->input);\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// Diffusion\ntypedef struct {\n    int B;\n    int img_size;\n    int max_period;\n    float* betas; // (max_period,)\n    float* sqrt_alphas_cumprod; // (max_period,)\n    float* sqrt_one_minus_alphas_cumprod; // (max_period,)\n    int block_size;\n    curandState* curand_states;\n} GaussianDiffusion;\n\n__global__ void init_curand_states(curandState *state, unsigned long seed, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N) {\n        curand_init(seed, idx, 0, &state[idx]);\n    }\n}\n\nvoid diffusion_init(\n    GaussianDiffusion* diffusion, int B, int C_in, int H, int W, int max_period, int block_size = 512,\n    unsigned long seed = 0, float beta_start = 1e-4, float beta_end = 0.02f\n) {\n    diffusion->B = B;\n    diffusion->max_period = max_period;\n    diffusion->block_size = block_size;\n    diffusion->img_size = C_in * H * W;\n\n    float scale = 1000.0f / max_period;\n    beta_start *= scale;\n    beta_end *= scale;\n\n    // calculate betas and alphas on host then move to device\n    float* betas = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_one_minus_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n\n    float alpha_cumprod = 1.0f;\n    for (int t = 1; t <= max_period; t++) {\n        // linear interpolate for beta\n        betas[t - 1] = (beta_start * (max_period - t) + beta_end * (t - 1)) / (max_period - 1);\n        alpha_cumprod *= 1.0f - betas[t - 1];\n        sqrt_alphas_cumprod[t - 1] = sqrtf(alpha_cumprod);\n        sqrt_one_minus_alphas_cumprod[t - 1] = sqrtf(1.0f - alpha_cumprod);\n    }\n\n    float *d_betas, *d_sqrt_alphas_cumprod, *d_sqrt_one_minus_alphas_cumprod;\n    cudaCheck(cudaMalloc(&d_betas, max_period * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_sqrt_alphas_cumprod, max_period * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_sqrt_one_minus_alphas_cumprod, max_period * sizeof(float)));\n\n    cudaCheck(cudaMemcpy(d_betas, betas, max_period * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_sqrt_alphas_cumprod, sqrt_alphas_cumprod, max_period * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_sqrt_one_minus_alphas_cumprod, sqrt_one_minus_alphas_cumprod, max_period * sizeof(float), cudaMemcpyHostToDevice));\n\n    free(betas);\n    free(sqrt_alphas_cumprod);\n    free(sqrt_one_minus_alphas_cumprod);\n\n    diffusion->betas = d_betas;\n    diffusion->sqrt_alphas_cumprod = d_sqrt_alphas_cumprod;\n    diffusion->sqrt_one_minus_alphas_cumprod = d_sqrt_one_minus_alphas_cumprod;\n\n    // init curand states for sampling noise of the size of one batch of inputs\n    if (diffusion->curand_states == nullptr) {\n        printf(\"initializing curand states\\n\");\n        int total_states = B * C_in * H * W;\n        int n_blocks = ceil_div(total_states, block_size);\n        cudaCheck(cudaMalloc(&diffusion->curand_states, total_states * sizeof(curandState)));\n        init_curand_states<<<n_blocks, block_size>>>(diffusion->curand_states, seed, total_states);\n    }\n}\n\n// fill BxN noise array with standard gaussians\n__global__ void diffusion_draw_normal_kernel(\n    float* noise, int B, int N, curandState* curand_states\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        noise[idx] = curand_normal(&curand_states[idx]);\n    }\n}\n\nvoid diffusion_draw_normal(\n    GaussianDiffusion *diffusion,\n    float* noise\n) {\n    int B = diffusion->B;\n    int img_size = diffusion->img_size;\n    int total_states = B * img_size;\n    int n_blocks = ceil_div(total_states, diffusion->block_size);\n    diffusion_draw_normal_kernel<<<n_blocks, diffusion->block_size>>>(noise, B, img_size, diffusion->curand_states);\n}\n\n// given input x of shape (B, N) and timesteps of shape (B,)\n// replace each x entry by the result of flowing forward by diffusion for time t\n// with randomness coming from noise\n__global__ void diffusion_forward_by_t_kernel(\n    float* x, float* timesteps, float* noise,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    int B, int N, curandState* curand_states\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        int b = idx / N;\n        float x_val = x[idx];\n        float noise_val = noise[idx];\n        int t = (int)timesteps[b];\n        float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n        float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n        x[idx] = x_val * sqrt_alpha_t + noise_val * sqrt_one_minus_alpha_t;\n    }\n}\n\nvoid diffusion_forward_by_t(\n    GaussianDiffusion* diffusion,\n    float* x, float* timesteps, float* noise\n) {\n    int B = diffusion->B;\n    int img_size = diffusion->img_size;\n    int block_size = diffusion->block_size;\n    int total_states = B * img_size;\n    int n_blocks = ceil_div(total_states, block_size);\n    diffusion_forward_by_t_kernel<<<n_blocks, block_size>>>(\n        x, timesteps, noise, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, B, img_size, diffusion->curand_states\n    );\n}\n\nvoid diffusion_free(GaussianDiffusion* diffusion) {\n    cudaCheck(cudaFree(diffusion->betas));\n    cudaCheck(cudaFree(diffusion->sqrt_alphas_cumprod));\n    cudaCheck(cudaFree(diffusion->sqrt_one_minus_alphas_cumprod));\n    cudaCheck(cudaFree(diffusion->curand_states));\n}\n\n// one block with B threads\n__global__ void sample_timestep_kernel(\n    float* timesteps, int B,\n    curandState* curand_states, int max_period\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B) {\n        int t = curand(&curand_states[idx]) % max_period;\n        timesteps[idx] = (float)t;\n    }\n}\n\n// samples B timesteps in range [0, max_period), convert them to floats and\n// store in timesteps\nvoid sample_timesteps(GaussianDiffusion *diffusion, float* timesteps) {\n    sample_timestep_kernel<<<1, diffusion->B>>>(timesteps, diffusion->B, diffusion->curand_states, diffusion->max_period);\n}\n\n// ------------------------------------------------------------------------------------------------\n// timestep embedding states\ntypedef struct {\n    float* freqs;\n    int half_dim;\n    int B;\n    int max_period;\n} TimestepEmbedding;\n\n__global__ void fill_freqs_kernel(float* freqs, int half_dim, int max_period) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < half_dim) {\n        float log_period = logf(max_period);\n        float logit = -log_period * idx / half_dim;\n        freqs[idx] = expf(logit);\n    }\n}\n\nvoid init_timestep_embedding(TimestepEmbedding* emb, int dim, int B, int max_period) {\n    assert(dim % 2 == 0);\n    int half = dim / 2;\n    emb->half_dim = half;\n    emb->max_period = max_period;\n    emb->B = B;\n    cudaCheck(cudaMalloc(&emb->freqs, half * sizeof(float)));\n    // fill freq values\n    int block_size = 512;\n    int n_blocks = ceil_div(half, block_size);\n    fill_freqs_kernel<<<n_blocks, block_size>>>(emb->freqs, half, max_period);\n}\n\nvoid free_timestep_embedding(TimestepEmbedding* emb) {\n    cudaCheck(cudaFree(emb->freqs));\n}\n\n__global__ void fill_embeddings_kernel(\n    const float* timesteps, const float* freqs, float* embeddings, int half, int B\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < B * half) {\n        int i = idx / half;\n        int j = idx % half;\n        float arg = timesteps[i] * freqs[j];\n        embeddings[i * 2 * half + j] = cosf(arg);\n        embeddings[i * 2 * half + j + half] = sinf(arg);\n    }\n}\n\n// timesteps is a (B,) array of timesteps that are integers but saved as floats\n// out is a (B, dim) array\nvoid get_timestep_embeddings(\n    TimestepEmbedding* emb, const float* timesteps, float* out\n) {\n    int block_size = 512;\n    int n_blocks = ceil_div(emb->half_dim * emb->B, block_size);\n    fill_embeddings_kernel<<<n_blocks, block_size>>>(timesteps, emb->freqs, out, emb->half_dim, emb->B);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet init\n\ntypedef struct {\n    int C_in;\n    int C_model;\n    int C_time;\n    int C_out;\n    int B;\n    int H;\n    int W;\n    int gn_n_groups;\n    int HS;\n    int block_size;\n    int n_layers;\n    int n_levels;\n    int n_res;\n    int att_start_level;\n    int* channel_mult;\n    int max_period;\n} UnetConfig;\n\n// we just need a single set of buffers that are shared for all the backward pass activations\n#define NUM_SKIP_CONNECTIONS 12 // (n_res + 1) * n_levels\n#define NUM_BACK_BUFS 10\n#define UNET_NUM_BACK_ACTS 22\ntypedef struct {\n    float* dinp_buf1; // used to store the input gradient, which will be reused as dout for the previous layer\n    float* dinp_buf2;\n    float* buf1_BCHW; // NOTE: first 4 buffers will have  max B * C * H * W size\n    float* buf2_BCHW;\n    float* buf3_BCHW;\n    float* buf4_BCHW;\n    float* buf1_B3CHW; // NOTE: last 2 buffers will have max B * 3 * C * H * W size for attention layers\n    float* buf2_B3CHW; // so max of H * W is only over dimensions that have attention\n    float* demb_buf1; // used to store cumulative gradients w.r.t. timestep embeddings\n    float* demb_buf2;\n    float* skip_dinps[NUM_SKIP_CONNECTIONS];\n} UnetBackActs;\n\n// This is used as a block to store all the params / grads / acts for all the layers\ntypedef struct {\n    float* mem_ptr;\n    size_t total_size;\n    int n_layers;\n    void** layer_ptrs;\n    size_t* layer_sizes;\n} UnetBlock;\n\ntypedef struct {\n    float* back_mem;\n    UnetBackActs back_acts;\n    size_t back_sizes[UNET_NUM_BACK_ACTS];\n    size_t n_backs;\n} UnetBackBlock;\n\ntypedef struct {\n    UnetConfig config;\n    float* input;\n    float* timestep_emb; // this is the already computed embeddings of shape (B, C_model)\n    UnetBlock params;\n    UnetBlock grads;\n    UnetBlock acts;\n    UnetBackBlock back;\n    float* skip_acts[NUM_SKIP_CONNECTIONS]; // pointers to skip connetion activations\n    // the next two are for debug purposes only, just to keep track of the buffer that contains the grad w.r.t. inputs\n    float* dinp;\n    // model output\n    float* output;\n    // targets and losses\n    float* target;\n    float mean_loss;\n    float* gpu_loss;\n    // buffers for AdamW\n    float* m_memory;\n    float* v_memory;\n    TimestepEmbedding time_emb_state;\n} Unet;\n\nvoid unet_init(Unet* unet) {\n    unet->input = nullptr;\n    unet->timestep_emb = nullptr;\n    unet->params.mem_ptr = nullptr;\n    unet->grads.mem_ptr = nullptr;\n    unet->acts.mem_ptr = nullptr;\n    unet->back.back_mem = nullptr;\n    unet->dinp = nullptr;\n    unet->output = nullptr;\n    unet->target = nullptr;\n    unet->mean_loss = -1.0f;\n    unet->gpu_loss = nullptr;\n    unet->m_memory = nullptr;\n    unet->v_memory = nullptr;\n\n    init_timestep_embedding(&unet->time_emb_state, unet->config.C_model, unet->config.B, unet->config.max_period);\n}\n\n\ntemplate <typename T>\nvoid free_not_null(T** ptr) {\n    if (*ptr != nullptr) {\n        free(*ptr);\n        *ptr = nullptr;\n    }\n}\n\nvoid free_unet_block(UnetBlock* params) {\n    free_not_null(&params->layer_sizes);\n    for (int l = 0; l < params->n_layers; l++) {\n        free_not_null(&(params->layer_ptrs[l]));\n    }\n    free_not_null(&params->layer_ptrs);\n    cudaCheck(cudaFree(params->mem_ptr));\n}\n\nvoid free_unet(Unet* unet) {\n    //free(unet->config.channel_mult);\n\n    cudaCheck(cudaFree(unet->input));\n    cudaCheck(cudaFree(unet->timestep_emb));\n    cudaCheck(cudaFree(unet->gpu_loss));\n    cudaCheck(cudaFree(unet->m_memory));\n    cudaCheck(cudaFree(unet->v_memory));\n\n    free_unet_block(&(unet->params));\n    free_unet_block(&(unet->grads));\n    free_unet_block(&(unet->acts));\n\n    cudaCheck(cudaFree(unet->back.back_mem));\n    free_timestep_embedding(&unet->time_emb_state);\n}\n\nvoid unet_count_layers(UnetConfig* config) {\n    int n_layers = 0;\n    int n_res = config->n_res;\n    int n_levels = config->n_levels;\n    int att_start_level = config->att_start_level;\n\n    // timestep emb\n    n_layers += 3;\n\n    // initial conv\n    n_layers += 1;\n\n    // add downsample resblock layers\n    n_layers += n_res * n_levels;\n\n    // add downsample attention\n    n_layers += n_res * (n_levels - att_start_level);\n\n    // add avgpool layers\n    n_layers += n_levels - 1;\n\n    // add middle layers\n    n_layers += 3;\n\n    // add upsample concat layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample resblock layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample attention layers\n    n_layers += (n_res + 1) * (n_levels - att_start_level);\n\n    // add upsample layers\n    n_layers += n_levels - 1;\n\n    // output layers\n    n_layers += 3;\n\n    config->n_layers = n_layers;\n}\n\n// ------------------------------------------------------------------------------------------------\n// allocate memory for structs with info for each layer and count params\n\nint make_linear_params_ptrs(\n    UnetBlock* params,\n    int layer,\n    int C_in, int C_out\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearParams));\n    params->layer_sizes[layer] = linear_count_params(C_in, C_out);\n    return layer + 1;\n}\n\nint make_silu_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_convk3_params_ptrs(\n    UnetBlock* params, int layer,\n    int C_in, int OC, int B, int H, int W\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Params));\n    params->layer_sizes[layer] = convk3_count_params(C_in, OC);\n    return layer + 1;\n}\n\nint make_resblock_params_ptrs(\n    UnetBlock* params, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockParameters));\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    resblock_count_params(resblock_params, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    params->layer_sizes[layer] = resblock_params->n_params;\n    return layer + 1;\n}\n\nint make_avgpool_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_attention_block_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionParams));\n    AttentionParams* attention_params = (AttentionParams*)params->layer_ptrs[layer];\n    attention_block_count_params(attention_params, C);\n    params->layer_sizes[layer] = attention_params->n_params;\n    return layer + 1;\n}\n\nint make_concat_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_upsample_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_groupnorm_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormParams));\n    params->layer_sizes[layer] = C * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_params(UnetConfig* config, UnetBlock* params) {\n    assert (config->n_layers > 0);\n\n    params->n_layers = config->n_layers;\n    params->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    params->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_params_ptrs(params, layer, C_model, C_time);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_linear_params_ptrs(params, layer, C_time, C_time);\n    int OC = C_model;\n    layer = make_convk3_params_ptrs(params, layer, C_in, OC, B, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_params_ptrs(params, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_params_ptrs(params, layer);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    // middle layers\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_params_ptrs(params, layer, C);\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards: concat then resblock\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = C_model * mult;\n        for (int r = 0; r < n_res + 1; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = C_model * prev_mult;\n            }\n            OC = C_model * mult;\n            layer = make_concat_params_ptrs(params, layer);\n            layer = make_resblock_params_ptrs(params, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_params_ptrs(params, layer);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    layer = make_groupnorm_params_ptrs(params, layer, C);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_convk3_params_ptrs(params, layer, C, C_out, B, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += params->layer_sizes[i];\n    }\n    params->total_size = total_size;\n}\n\n// ------------------------------------------------------------------------------------------------\n// setup Unet activations pointers\nint make_linear_acts_ptrs(\n    UnetBlock* acts,\n    int layer,\n    int B, int C_out\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearActs));\n    // acts buffers only hold outputs\n    acts->layer_sizes[layer] = B * C_out;\n    return layer + 1;\n}\n\nint make_silu_acts_ptrs(UnetBlock* acts, int layer, int N) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(SiluActs));\n    acts->layer_sizes[layer] = N;\n    return layer + 1;\n}\n\nint make_convk3_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int OC, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Acts));\n    acts->layer_sizes[layer] = B * OC * H * W;\n    return layer + 1;\n}\n\nint make_resblock_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockActivations));\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    resblock_count_acts(resblock_acts, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    acts->layer_sizes[layer] = resblock_acts->n_acts;\n    return layer + 1;\n}\n\nint make_avgpool_acts_ptrs(UnetBlock* acts, int layer, int B, int C, int H, int W) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AvgpoolActs));\n    acts->layer_sizes[layer] = B * C * H/2 * W/2;\n    return layer + 1;\n}\n\nint make_attention_block_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionActs));\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    attention_block_count_acts(att_acts, B, C, H, W, gn_n_groups, HS);\n    acts->layer_sizes[layer] = att_acts->n_acts;\n    return layer + 1;\n}\n\nint make_concat_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C1, int C2, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConcatChannelActs));\n    acts->layer_sizes[layer] = B * (C1 + C2) * H * W;\n    return layer + 1;\n}\n\nint make_upsample_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(UpsampleActs));\n    acts->layer_sizes[layer] = B * C * H * 2 * W * 2;\n    return layer + 1;\n}\n\nint make_groupnorm_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormActs));\n    // out (B, C, H, W), mean and rstd (B, gn_n_groups)\n    acts->layer_sizes[layer] = B * C * H * W + B * gn_n_groups * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_acts(UnetConfig* config, UnetBlock* acts) {\n    assert (config->n_layers > 0);\n\n    acts->n_layers = config->n_layers;\n    acts->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    acts->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int HS = config->HS;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    layer = make_silu_acts_ptrs(acts, layer, B * C_time);\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    int OC = C_model;\n    layer = make_convk3_acts_ptrs(acts, layer, B, OC, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_acts_ptrs(acts, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_acts_ptrs(acts, layer, B, C, H, W);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = prev_mult * C_model;\n            }\n            layer = make_concat_acts_ptrs(acts, layer, B, C, C_skip, H, W);\n            OC = mult * C_model;\n            layer = make_resblock_acts_ptrs(acts, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_acts_ptrs(acts, layer, B, C, H, W);\n            H *= 2;\n            W *= 2;\n        }\n    }\n    layer = make_groupnorm_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups);\n    layer = make_silu_acts_ptrs(acts, layer, B * C * H * W);\n    layer = make_convk3_acts_ptrs(acts, layer, B, C_out, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += acts->layer_sizes[i];\n    }\n    acts->total_size = total_size;\n}\n\nvoid unet_setup_and_count_backs(UnetConfig* config, UnetBackBlock* back) {\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int H = config->H;\n    int W = config->W;\n\n    size_t max_BCHW = 3 * B * C_model * H * W;\n    size_t max_B3CHW = 2 * B * 3 * C_model * H * W;\n    back->back_sizes[0] = max_BCHW;\n    back->back_sizes[1] = max_BCHW;\n    back->back_sizes[2] = max_BCHW;\n    back->back_sizes[3] = max_BCHW;\n    back->back_sizes[4] = max_BCHW;\n    back->back_sizes[5] = max_BCHW;\n    back->back_sizes[6] = max_B3CHW;\n    back->back_sizes[7] = max_B3CHW;\n    back->back_sizes[8] = B * C_time;\n    back->back_sizes[9] = B * C_time;\n\n    // fill the sizes of the skip connection backward acts\n    int C = C_model;\n    for (int l = 0; l < config->n_levels; l++) {\n        for (int r = 0; r < (config->n_res + 1); r++) {\n            int s = l * 3 + r + NUM_BACK_BUFS;\n            assert(s < UNET_NUM_BACK_ACTS);\n            back->back_sizes[s] = B * C * H * W;\n            int mult = config->channel_mult[l];\n            C = C_model * mult;\n            if (l > 0 && r == 0) {\n                H /= 2;\n                W /= 2;\n            }\n        }\n    }\n\n    size_t n_backs = 0;\n    for (int i = 0; i < UNET_NUM_BACK_ACTS; i++) {\n        n_backs += back->back_sizes[i];\n    }\n    back->n_backs = n_backs;\n}\n\n\nvoid unet_make_ptrs_and_count_memory(Unet* unet)\n{\n    unet_setup_and_count_params(&(unet->config), &(unet->params));\n    unet_setup_and_count_params(&(unet->config), &(unet->grads));\n    unet_setup_and_count_acts(&(unet->config), &(unet->acts));\n    unet_setup_and_count_backs(&(unet->config), &(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// malloc unet memory and set pointers\ntypedef struct {\n    float* input;\n    float* prev_out;\n    int layer;\n    float* emb;\n    float* emb_cum; // cumulative demb\n    float* mem_ptr;\n    int skip_idx;\n} LayerInfo;\n\n\nvoid set_linear_params_ptrs(\n    UnetBlock* params,\n    LayerInfo* l_info,\n    int C_in, int C_out\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)params->layer_ptrs[layer];\n    linear_set_param_ptrs(linear_params, l_info->mem_ptr, C_in, C_out);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_convk3_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C_in, int OC) {\n    int layer = l_info->layer;\n    ConvK3Params* convk3_params = (ConvK3Params*)params->layer_ptrs[layer];\n    convk3_set_param_ptrs(convk3_params, l_info->mem_ptr, C_in, OC);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    set_resblock_params_ptrs(C, OC, resblock_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_attention_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)params->layer_ptrs[layer];\n    set_attention_params_pointers(att_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_params = (GroupNormParams*)params->layer_ptrs[layer];\n    gn_set_param_ptrs(gn_params, l_info->mem_ptr, C);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\n\nvoid point_unet_params(UnetConfig* config, UnetBlock* params) {\n    // common constants\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = params->mem_ptr;\n    set_linear_params_ptrs(params, &l_info, C_model, C_time);\n    l_info.layer++; // skip silu layer\n    set_linear_params_ptrs(params, &l_info, C_time, C_time);\n    int OC = C_model;\n    set_convk3_params_ptrs(params, &l_info, C_in, OC);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_params_ptrs(params, &l_info, C, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l < n_levels - 1) {\n            l_info.layer++; // skip avgpool layer\n        }\n    }\n\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n    set_attention_layer_params_ptrs(params, &l_info);\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n\n    // upward: skip one layer for concat\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            l_info.layer++;\n            OC = mult * C_model;\n            set_resblock_layer_params_ptrs(params, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l > 0) {\n            l_info.layer++; // upsample\n        }\n    }\n\n    set_groupnorm_layer_params_ptrs(params, &l_info, C);\n    l_info.layer++; // silu\n    set_convk3_params_ptrs(params, &l_info, C, C_out);\n}\n\nvoid set_linear_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    LinearActs* linear_acts = (LinearActs*)acts->layer_ptrs[layer];\n    linear_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    linear_acts->inp = l_info->input;\n    l_info->input = linear_acts->out;\n    l_info->layer++;\n}\n\nvoid set_silu_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    SiluActs* silu_acts = (SiluActs*)acts->layer_ptrs[layer];\n    silu_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    silu_acts->x = l_info->input;\n    l_info->input = silu_acts->out;\n    l_info->layer++;\n}\n\nvoid set_convk3_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    ConvK3Acts* convk3_acts = (ConvK3Acts*)acts->layer_ptrs[layer];\n    convk3_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    convk3_acts->inp = l_info->input;\n    l_info->input = convk3_acts->out;\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    set_resblock_acts_ptrs(C, OC, resblock_acts, l_info->mem_ptr);\n    resblock_acts->input = l_info->input;\n    resblock_acts->emb = l_info->emb;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = resblock_acts->add2;\n    l_info->layer++;\n}\n\nvoid set_avgpool_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)acts->layer_ptrs[layer];\n    avgpool_acts->out = l_info->mem_ptr;\n    avgpool_acts->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = avgpool_acts->out;\n    l_info->layer++;\n}\n\nvoid set_attention_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    set_attention_acts_pointers(att_acts, l_info->mem_ptr);\n    att_acts->input = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = att_acts->add;\n    l_info->layer++;\n}\n\nvoid set_concat_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, float** skip_acts) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)acts->layer_ptrs[layer];\n    cat_acts->out = l_info->mem_ptr;\n    cat_acts->x1 = l_info->input;\n    cat_acts->x2 = skip_acts[l_info->skip_idx];\n\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = cat_acts->out;\n    l_info->layer++;\n    l_info->skip_idx--;\n}\n\nvoid set_upsample_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)acts->layer_ptrs[layer];\n    up_acts->x = l_info->input;\n    up_acts->out = l_info->mem_ptr;\n\n    l_info->mem_ptr += acts-> layer_sizes[layer];\n    l_info->input = up_acts->out;\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_acts_ptrs(\n    UnetBlock* acts, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormActs* gn_a = (GroupNormActs*)acts->layer_ptrs[layer];\n    gn_set_act_ptrs(gn_a, l_info->mem_ptr, B, C, H, W, gn_n_groups);\n    gn_a->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = gn_a->out;\n    l_info->layer++;\n}\n\nvoid point_unet_acts(UnetConfig* config, UnetBlock* acts, float** skip_acts, float* input, float* timestep_emb) {\n    // common constants\n    int B = config->B;\n    int C_model = config->C_model;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = acts->mem_ptr;\n\n    // timestep emb\n    l_info.input = timestep_emb;\n    set_linear_acts_ptrs(acts, &l_info);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_linear_acts_ptrs(acts, &l_info);\n    // point input to model input, and set outputu of the last linear layer as the embedding input\n    l_info.emb = l_info.input;\n    l_info.input = input;\n\n    int OC = C_model;\n    set_convk3_acts_ptrs(acts, &l_info);\n    int skip_idx = 0;\n    skip_acts[skip_idx] = l_info.input; // set the output of the previouse layer as input to the skip connection\n    skip_idx++;\n\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_acts_ptrs(acts, &l_info, C, OC);\n            if (l < att_start_level) {\n                // for the first 2 layers, the skip connection comes from the resblocks\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n        }\n        if (l < n_levels - 1) {\n            set_avgpool_layer_acts_ptrs(acts, &l_info);\n            skip_acts[skip_idx] = l_info.input;\n            skip_idx++;\n        }\n    }\n\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n    set_attention_layer_acts_ptrs(acts, &l_info);\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n\n    // upward: set concat\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            set_concat_layer_acts_ptrs(acts, &l_info, skip_acts);\n            OC = mult * C_model;\n            set_resblock_layer_acts_ptrs(acts, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n            }\n        }\n        if (l > 0) {\n            set_upsample_layer_acts_ptrs(acts, &l_info);\n        }\n    }\n    set_groupnorm_layer_acts_ptrs(acts, &l_info, B, C, H, W, gn_n_groups);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_convk3_acts_ptrs(acts, &l_info);\n}\n\nvoid set_unet_back_pointers(UnetBackBlock* back) {\n    UnetBackActs* back_acts = &(back->back_acts);\n    float* back_mem_ptr = back->back_mem;\n\n    float** ptrs[] = {\n        &(back_acts->dinp_buf1),\n        &(back_acts->dinp_buf2),\n        &(back_acts->buf1_BCHW),\n        &(back_acts->buf2_BCHW),\n        &(back_acts->buf3_BCHW),\n        &(back_acts->buf4_BCHW),\n        &(back_acts->buf1_B3CHW),\n        &(back_acts->buf2_B3CHW),\n        &(back_acts->demb_buf1),\n        &(back_acts->demb_buf2)\n    };\n\n    for (int i = 0; i < NUM_BACK_BUFS; i++) {\n        *(ptrs[i]) = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[i];\n    }\n\n    // point the back acts for skip connections\n    for (int s = 0; s < NUM_SKIP_CONNECTIONS; s++) {\n        back_acts->skip_dinps[s] = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[NUM_BACK_BUFS + s];\n    }\n}\n\nvoid malloc_and_point(Unet* unet) {\n    float *param_mem, *grad_mem, *act_mem, *back_mem;\n    cudaCheck(cudaMalloc(&param_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&grad_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&act_mem, unet->acts.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&back_mem, unet->back.n_backs * sizeof(float)));\n\n    cudaCheck(cudaMemset(param_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(grad_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(act_mem, 0, unet->acts.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(back_mem, 0, unet->back.n_backs * sizeof(float)));\n\n    // constants\n    int B = unet->config.B;\n    int C_in = unet->config.C_in;\n    int C_model = unet->config.C_model;\n    int H = unet->config.H;\n    int W = unet->config.W;\n\n    // malloc and point input and timestep emb\n    cudaCheck(cudaMalloc(&(unet->input), B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&(unet->timestep_emb), B * C_model * sizeof(float)));\n    // malloc a nicely aligned buffer for the loss\n    cudaCheck(cudaMalloc(&(unet->gpu_loss), 64 * sizeof(float)));\n\n    unet->params.mem_ptr = param_mem;\n    unet->grads.mem_ptr = grad_mem;\n    unet->acts.mem_ptr = act_mem;\n    unet->back.back_mem = back_mem;\n\n    point_unet_params(&(unet->config), &(unet->params));\n    point_unet_params(&(unet->config), &(unet->grads));\n    point_unet_acts(&(unet->config), &(unet->acts), unet->skip_acts, unet->input, unet->timestep_emb);\n    set_unet_back_pointers(&(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// forward pass\n\nvoid linear_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int N, int C, int OC, int block_size\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_forward2(cublas_handle, linear_acts->out, linear_acts->inp, linear_params->w, linear_params->b, N, C, OC, block_size);\n    l_info->layer++;\n}\n\nvoid silu_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_forward(acts->x, acts->out, N, block_size);\n    l_info->layer++;\n}\n\nvoid convk3_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    conv2d_k3_forward3(conv_acts->inp, conv_params->w, conv_params->b, conv_acts->out, B, C, OC, H, W);\n    l_info->layer++;\n}\nvoid resblock_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    resblock_forward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_acts);\n    l_info->layer++;\n}\n\nvoid avgpool_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)unet->acts.layer_ptrs[layer];\n    avgpool_2d_forward1(avgpool_acts->out, avgpool_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid attention_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts);\n    l_info->layer++;\n}\n\nvoid concat_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)unet->acts.layer_ptrs[layer];\n    concat_channel_forward(cat_acts->x1, cat_acts->x2, cat_acts->out, B, C1, C2, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid upsample_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)unet->acts.layer_ptrs[layer];\n    upsample_forward1(up_acts->out, up_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid groupnorm_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_forward(gn_a->x, gn_p->w, gn_p->b, gn_a->out, gn_a->mean, gn_a->rstd, B, C, H, W, gn_n_groups);\n    l_info->layer++;\n}\n\nvoid unet_forward(\n    cublasHandle_t cublas_handle,\n    Unet* unet\n) {\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_model, C_time, block_size);\n    silu_layer_forward(unet, &l_info, B * C_time, block_size);\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_time, C_time, block_size);\n    int OC = C_model;\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config.channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size);\n            C = OC;\n            if (l >= att_start_level) {\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n            }\n        }\n        if (l < n_levels - 1) {\n            avgpool_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n\n    // upward\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int C_skip = C_model * config.channel_mult[l];\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            }\n            concat_layer_forward(unet, &l_info, B, C, C_skip, H, W, block_size);\n            OC = C_model * config.channel_mult[l];\n            resblock_layer_forward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            C = OC;\n            if (l >= att_start_level) {\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n            }\n        }\n        if (l > 0) {\n            upsample_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    groupnorm_layer_forward(unet, &l_info, B, C, H, W, gn_n_groups);\n    silu_layer_forward(unet, &l_info, B * C * H * W, block_size);\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n\n    ConvK3Acts* last_acts = (ConvK3Acts*)unet->acts.layer_ptrs[l_info.layer - 1];\n    unet->output = last_acts->out;\n    if (unet->target == nullptr) {\n        // don't have targets, not computing loss\n        unet->mean_loss = -1.0f;\n    } else {\n        mse_forward(unet->output, unet->target, unet->gpu_loss, B * C_out * H * W, block_size);\n        cudaCheck(cudaMemcpy(&(unet->mean_loss), unet->gpu_loss, sizeof(float), cudaMemcpyDeviceToHost));\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet backward\n\nvoid swap_layer_in_out_ptrs(LayerInfo* l_info) {\n    float* temp = l_info->input;\n    l_info->input = l_info->prev_out;\n    l_info->prev_out = temp;\n}\n\nvoid linear_layer_backward(\n    Unet* unet,\n    LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int N, int C, int OC\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearParams* linear_grads = (LinearParams*)unet->grads.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_backward1(cublas_handle, l_info->prev_out, linear_grads->w, linear_grads->b, l_info->input, linear_acts->inp, linear_params->w, N, C, OC);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid silu_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_backward(l_info->input, acts->x, l_info->prev_out, N, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid convk3_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * OC * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Params* conv_grads = (ConvK3Params*)unet->grads.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n    conv2d_k3_backward2(l_info->input, conv_acts->inp, conv_params->w, unet_back_acts->buf1_BCHW, unet_back_acts->buf2_BCHW, l_info->prev_out, conv_grads->w, conv_grads->b, B, C, OC, H, W);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid resblock_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * C_out * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockParameters* resblock_grads = (ResBlockParameters*)unet->grads.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    ResBlockBackwardActivations back_acts;\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n\n    // point back_acts to buffers\n    back_acts.buf_BCemb = unet_back_acts->buf1_BCHW;\n    back_acts.buf_BCHoWo = unet_back_acts->buf2_BCHW;\n    back_acts.buf1_BCHW = unet_back_acts->buf3_BCHW;\n    back_acts.buf2_BCHW = unet_back_acts->buf4_BCHW;\n    back_acts.dweight_buf = unet_back_acts->buf1_B3CHW;\n    back_acts.dbias_buf = unet_back_acts->buf2_B3CHW;\n    back_acts.dout = l_info->input;\n    back_acts.dx = l_info->prev_out;\n    back_acts.demb = l_info->emb;\n\n    resblock_backward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_grads, resblock_acts, &back_acts);\n    swap_layer_in_out_ptrs(l_info);\n\n    // add demb to cumulative demb\n    add_inplace_forward(l_info->emb, l_info->emb_cum, B * C_emb, block_size);\n    l_info->layer--;\n}\n\nvoid attention_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"att adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionParams* att_grads = (AttentionParams*)unet->grads.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    AttentionBackwardActs att_backs;\n    UnetBackActs* unet_backs = &(unet->back.back_acts);\n    att_backs.buf1_BCHW = unet_backs->buf1_BCHW;\n    att_backs.buf2_BCHW = unet_backs->buf2_BCHW;\n    att_backs.buf_B3CHW = unet_backs->buf1_B3CHW;\n    att_backs.dqkvr = unet_backs->buf2_B3CHW;\n    att_backs.dpreatt = unet_backs->buf3_BCHW;\n    att_backs.datt = unet_backs->buf4_BCHW;\n    att_backs.dout = l_info->input;\n    att_backs.dinp = l_info->prev_out;\n\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts, &att_backs, att_grads);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid concat_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n    //printf(\"concat skip %d\\n\", l_info->skip_idx);\n    concat_channel_backward(l_info->input, l_info->prev_out, skip_dinp, B, C1, C2, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n    l_info->skip_idx++;\n}\n\n\nvoid avgpool_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"avgpool adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H/2 * W/2, block_size);\n        l_info->skip_idx--;\n    }\n    avgpool_2d_backward1(l_info->input, l_info->prev_out, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid upsample_layer_backward(\n    Unet* unet, LayerInfo *l_info,\n    int B, int C,  int H, int W, int block_size\n) {\n    upsample_backward1(l_info->prev_out, l_info->input, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid groupnorm_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormParams* gn_g = (GroupNormParams*)unet->grads.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_backward(l_info->input, gn_a->x, gn_a->mean, gn_a->rstd, gn_p->w, l_info->prev_out, gn_g->w, gn_g->b, B, C, H, W, gn_n_groups);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid unet_backward(\n    cublasHandle_t cublas_handle,\n    Unet* unet\n   \n) {\n    if (unet->mean_loss == -1.0f or unet->target == nullptr) {\n        printf(\"Error: must run forward with targets before calling backward\\n\");\n        exit(EXIT_FAILURE);\n    }\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    UnetBackActs* back_acts = &(unet->back.back_acts);\n    mse_backward(unet->output, unet->target, back_acts->dinp_buf1, B * C_out * H * W, block_size);\n\n    LayerInfo l_info;\n    l_info.layer = config.n_layers - 1;\n    l_info.input = back_acts->dinp_buf1;\n    l_info.prev_out = back_acts->dinp_buf2;\n    l_info.emb = back_acts->demb_buf1;\n    l_info.emb_cum = back_acts->demb_buf2;\n    l_info.skip_idx = 0;\n\n    int C;\n    int OC;\n    int C_skip;\n    C = C_model;\n    OC = C_model;\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n    silu_layer_backward(unet, &l_info, B * C * H * W, block_size);\n    groupnorm_layer_backward(unet, &l_info, B, C, H, W, gn_n_groups);\n\n    for (int l = 0; l < n_levels; l++) {\n        for (int r = n_res; r >= 0; r--) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            } else {\n                C_skip = C_model * config.channel_mult[l];\n            }\n            if (l >= att_start_level) {\n                attention_layer_backward(unet, &l_info, cublas_handle, B, OC, H, W, HS, gn_n_groups, block_size);\n            }\n            if (r == 0 && l < n_levels - 1) {\n                C = C_model * config.channel_mult[l + 1];\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            concat_layer_backward(unet, &l_info, B, C, C_skip, H, W, block_size);\n        }\n        if (l < n_levels - 1) {\n            H /= 2;\n            W /= 2;\n            upsample_layer_backward(unet, &l_info, B, C, H, W, block_size);\n            OC = C_model * config.channel_mult[l + 1];\n        }\n    }\n\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n\n    // we will need to change the backward for all the other blocks below to include the dout contribution from the skip connection.\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        if (l < n_levels - 1) {\n            H *= 2;\n            W *= 2;\n            avgpool_layer_backward(unet, &l_info, B, C, H, W, block_size, 1);\n        }\n        for (int r = 0; r < n_res; r++) {\n            if (l >= att_start_level) {\n                attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, 1);\n            }\n            int res_add_skip = (l < att_start_level) ? 1 : 0;\n            if (r == n_res - 1 && l > 0) {\n                int prev_mult = config.channel_mult[l - 1];\n                C = prev_mult * C_model;\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size, res_add_skip);\n            OC = C;\n        }\n    }\n\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size, 1);\n    unet->dinp = l_info.input;\n\n    // reset dout to point to emb_cum\n    l_info.input = l_info.emb_cum;\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_time, C_time);\n    silu_layer_backward(unet, &l_info, B * C_time, block_size);\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_model, C_time);\n}\n\n// ------------------------------------------------------------------------------------------------\n// zero grad and ADAMW\n\nvoid unet_zero_grad(Unet* unet) {\n    cudaCheck(cudaMemset(unet->grads.mem_ptr, 0, unet->grads.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(unet->back.back_mem, 0, unet->back.n_backs * sizeof(float)));\n    //cudaCheck(cudaMemset(unet->acts.mem_ptr, 0, unet->acts.total_size * sizeof(float)));\n}\n\n// below copied from llm.c\n\n// Implements linear interpolation using only two floating-point operations (as opposed to three in a naive implementation).\n// Reference: https://developer.nvidia.com/blog/lerp-faster-cuda\n__device__ inline float lerp(float start, float end, float weight) {\n    return fma(weight, end, fma(-weight, start, start));\n}\n\n__global__ void adamw_kernel2(float* params_memory, float* grads_memory, float* m_memory, float* v_memory, long num_parameters,\n                              float learning_rate, float beta1, float beta2, float beta1_correction, float beta2_correction, float eps, float weight_decay) {\n   int i = blockIdx.x * blockDim.x + threadIdx.x;\n   if (i >= num_parameters) return;  // guard\n   float grad = grads_memory[i];\n   float m = m_memory[i];\n   float v = v_memory[i];\n   // update the first moment (momentum)\n   m = lerp(grad, m, beta1);\n   m_memory[i] = m;\n   // update the second moment (RMSprop)\n   v = lerp(grad * grad, v, beta2);\n   v_memory[i] = v;\n   m /= beta1_correction;  // m_hat\n   v /= beta2_correction;  // v_hat\n   params_memory[i] -= learning_rate * (m / (sqrtf(v) + eps) + weight_decay * params_memory[i]);\n}\n\nvoid unet_update(Unet* unet, float learning_rate, float beta1, float beta2, float eps, float weight_decay, int t) {\n    // lazy allocation\n    if (unet->m_memory == nullptr) {\n        size_t param_size = unet->params.total_size * sizeof(float);\n        cudaCheck(cudaMalloc(&(unet->m_memory), param_size));\n        cudaCheck(cudaMalloc(&(unet->v_memory), param_size));\n        cudaCheck(cudaMemset(unet->m_memory, 0, param_size));\n        cudaCheck(cudaMemset(unet->v_memory, 0, param_size));\n        printf(\"allocated %zu MiB for AdamW state m\\n\", param_size / 1024 / 1024);\n        printf(\"allocated %zu MiB for AdamW state v\\n\", param_size / 1024 / 1024);\n    }\n\n    int block_size = unet->config.block_size;\n    int n_blocks = ceil_div(unet->params.total_size, (size_t)block_size);\n    float beta1_correction = 1.0f - powf(beta1, t);\n    float beta2_correction = 1.0f - powf(beta2, t);\n    adamw_kernel2<<<n_blocks, block_size>>>(unet->params.mem_ptr, unet->grads.mem_ptr, unet->m_memory, unet->v_memory, unet->params.total_size,\n                    learning_rate, beta1, beta2, beta1_correction, beta2_correction, eps, weight_decay);\n    cudaCheck(cudaGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// save model states\n\nvoid save_unet_states(Unet* unet, GaussianDiffusion* diffusion, const char* filename) {\n    // create header\n    int model_header[256];\n    model_header[0] = 12345678; // magic number\n    model_header[1] = unet->config.B;\n    model_header[2] = unet->config.C_in;\n    model_header[3] = unet->config.C_model;\n    model_header[4] = unet->config.C_out;\n    model_header[5] = unet->config.H;\n    model_header[6] = unet->config.W;\n    model_header[7] = unet->config.max_period;\n    model_header[8] = 1; // whether to save the AdamW states, this will be 0 if the model is saved from pytorch\n    model_header[9] = 1; // whether to save rng states\n\n    FILE* model_file = fopenCheck(filename, \"wb\");\n    printf(\"\\nsaving model details\");\n    int result = fwrite(model_header, sizeof(int), 256, model_file);\n    assert(result == 256);\n\n    // save params\n    float* params_cpu_buf = (float*)mallocCheck(unet->params.total_size * sizeof(float));\n    cudaCheck(cudaMemcpy(params_cpu_buf, unet->params.mem_ptr, unet->params.total_size * sizeof(float), cudaMemcpyDeviceToHost));\n    printf(\"saving model params\\n\");\n    result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n    assert(result == unet->params.total_size);\n\n    if (model_header[8]) {\n        // save AdamW states\n        cudaCheck(cudaMemcpy(params_cpu_buf, unet->m_memory, unet->params.total_size * sizeof(float), cudaMemcpyDeviceToHost));\n        printf(\"saving AdamW state m\\n\");\n        result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n        assert(result == unet->params.total_size);\n        cudaCheck(cudaMemcpy(params_cpu_buf, unet->v_memory, unet->params.total_size * sizeof(float), cudaMemcpyDeviceToHost));\n        printf(\"saving AdamW state v\\n\");\n        result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n        assert(result == unet->params.total_size);\n    }\n\n    if (model_header[9]) {\n        // save rngs\n        int n_states = diffusion->B * diffusion->img_size;\n        void *curand_state_buf = mallocCheck(n_states * sizeof(curandState_t));\n        cudaCheck(cudaMemcpy(curand_state_buf, diffusion->curand_states, diffusion->B * diffusion->img_size * sizeof(curandState_t), cudaMemcpyDeviceToHost));\n        printf(\"saving rng states\\n\");\n        result = fwrite(curand_state_buf, sizeof(curandState_t), n_states, model_file);\n        assert(result == n_states);\n        free(curand_state_buf);\n    }\n\n    fcloseCheck(model_file);\n    free(params_cpu_buf);\n    printf(\"saved model and diffusion states\\n\\n\");\n}\n\n// ------------------------------------------------------------------------------------------------\n// load unet and diffusion states\n\nvoid load_unet_and_diffusion_states(\n    Unet* unet, GaussianDiffusion* diffusion, const char* filename\n) {\n    FILE *model_file = fopenCheck(filename, \"rb\");\n    // read header\n    printf(\"\\nloading model header\\n\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file\\n\");\n        exit(1);\n    }\n    int B = model_header[1];\n    int C_in = model_header[2];\n    int C_model = model_header[3];\n    int C_out = model_header[4];\n    int H = model_header[5];\n    int W = model_header[6];\n    int max_period = model_header[7];\n    int save_adamw = model_header[8];\n    int save_rng = model_header[9];\n\n    // TODO: cleanup this code\n    int* channel_mult = (int*)mallocCheck(4 * sizeof(int));\n    channel_mult[0] = 1;\n    channel_mult[1] = 2;\n    channel_mult[2] = 3;\n    channel_mult[3] = 4;\n\n    // setup unet config\n    UnetConfig* config = &(unet->config);\n    config->B = B;\n    config->C_in = C_in;\n    config->C_model = C_model;\n    config->C_time = C_model * 4;\n    config->C_out = C_out;\n    config->H = H;\n    config->W = W;\n    config->block_size = 512;\n    config->gn_n_groups = 32;\n    config->n_levels = 4;\n    config->channel_mult = channel_mult;\n    config->HS = 32;\n    config->n_res = 2;\n    config->max_period = max_period;\n    config->att_start_level = 2;\n\n    unet_init(unet);\n    unet_count_layers(config);\n    unet_make_ptrs_and_count_memory(unet);\n    malloc_and_point(unet);\n\n    // read params\n    float* params_mem_cpu = (float*)mallocCheck(unet->params.total_size * sizeof(float));\n    printf(\"loading model params\\n\");\n    freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n    cudaCheck(cudaMemcpy(unet->params.mem_ptr, params_mem_cpu, unet->params.total_size * sizeof(float), cudaMemcpyHostToDevice));\n\n    if (save_adamw) {\n        // read AdamW states\n        printf(\"loading AdamW states\\n\");\n        freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n        cudaCheck(cudaMalloc(&(unet->m_memory), unet->params.total_size * sizeof(float)));\n        cudaCheck(cudaMemcpy(unet->m_memory, params_mem_cpu, unet->params.total_size * sizeof(float), cudaMemcpyHostToDevice));\n        freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n        cudaCheck(cudaMalloc(&(unet->v_memory), unet->params.total_size * sizeof(float)));\n        cudaCheck(cudaMemcpy(unet->v_memory, params_mem_cpu, unet->params.total_size * sizeof(float), cudaMemcpyHostToDevice));\n    } else {\n        printf(\"not loading AdamW states\\n\");\n        unet->m_memory = nullptr;\n        unet->v_memory = nullptr;\n    }\n\n    if (save_rng) {\n        // read curand rng states\n        printf(\"loading rng states\\n\");\n        int n_states = B * C_in * H * W;\n        void *curand_state_buf = mallocCheck(n_states * sizeof(curandState_t));\n        freadCheck(curand_state_buf, sizeof(curandState_t), n_states, model_file);\n        // initialize diffusion curant buffer\n        cudaCheck(cudaMalloc(&(diffusion->curand_states), n_states * sizeof(curandState_t)));\n        cudaCheck(cudaMemcpy(diffusion->curand_states, curand_state_buf, n_states * sizeof(curandState_t), cudaMemcpyHostToDevice));\n        free(curand_state_buf);\n    } else {\n        printf(\"not loading rng states\\n\");\n        diffusion->curand_states = nullptr;\n    }\n    diffusion_init(diffusion, B, C_in, H, W, 1000);\n\n    free(params_mem_cpu);\n    fcloseCheck(model_file);\n    printf(\"loaded model and diffusion states \\n\\n\");\n}\n\n\n\n// ------------------------------------------------------------------------------------------------\n\ntypedef struct {\n    int count;\n    float mean_loss;\n} LossCounter;\n\nvoid update_loss_counter(LossCounter* counter, float loss) {\n    float old_mean = counter->mean_loss;\n    float old_count = (float)counter->count;\n    counter->mean_loss = old_mean * old_count / (old_count + 1) + loss / (old_count + 1);\n    counter->count++;\n}\n\nvoid clear_loss_counter(LossCounter* counter) {\n    counter->count = 0;\n    counter->mean_loss = 0.0f;\n}\n\nvoid create_directory(const char *dir) {\n    struct stat st = {0};\n    if (stat(dir, &st) == -1) {\n        mkdir(dir, 0700); // Create directory with permissions 0700\n    }\n}\n\nint main(int argc, char **argv) {\n    // Default values\n    const char *model_weights_file = \"unet_init.bin\";\n    const char *data_file = \"data/elephant_train.bin\";\n    const char *log_filename = \"log.txt\";\n\n    // Parse command line arguments\n    for (int i = 1; i < argc; ++i) {\n        if (strcmp(argv[i], \"--model_weights\") == 0 && i + 1 < argc) {\n            model_weights_file = argv[++i];\n        } else if (strcmp(argv[i], \"--data_file\") == 0 && i + 1 < argc) {\n            data_file = argv[++i];\n        } else if (strcmp(argv[i], \"--log_file\") == 0 && i + 1 < argc) {\n            log_filename = argv[++i];\n        }\n    }\n    printf(\"loading model weights from %s\\n\", model_weights_file);\n    printf(\"loading data from %s\\n\", data_file);\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n    printf(\"device registers per block: %d\\n\", deviceProp.regsPerBlock);\n    printf(\"shared mem per block: %ld\\n\", deviceProp.sharedMemPerBlock);\n\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    Unet unet;\n    GaussianDiffusion diffusion;\n    load_unet_and_diffusion_states(&unet, &diffusion, model_weights_file);\n\n    int B = unet.config.B;\n    int C_in = unet.config.C_in;\n    int C_out = unet.config.C_out;\n    int H = unet.config.H;\n    int W = unet.config.W;\n\n    DataLoader loader;\n    dataloader_init(&loader, data_file, B);\n\n    // allocate memory for target separately, since unet can still be run without targets for inference\n    cudaCheck(cudaMalloc(&(unet.target), B * C_out * H * W * sizeof(float)));\n    float *d_timesteps_buf;\n    cudaCheck(cudaMalloc(&d_timesteps_buf, B * sizeof(float)));\n\n\n    int n_iters = 100000;\n    int n_log_iter = 100;\n    int n_save_iter = 10000;\n    //int n_iters = 100;\n    //int n_log_iter = 5;\n    //int n_save_iter = 50;\n\n    // wipe existing logs\n    FILE* log_file = fopen(log_filename, \"w\");\n    fprintf(log_file, \"training unet,\\nmodel file %s,\\ndata filename %s\\n\", model_weights_file, data_file);\n    fprintf(log_file, \"starting training for %d iterations\\n\", n_iters);\n    fcloseCheck(log_file);\n\n    const char *model_save_dir = \"./models/\";\n    create_directory(model_save_dir);\n\n    LossCounter loss_counter;\n    clear_loss_counter(&loss_counter);\n\n    printf(\"Training for %d iterations\\n\", n_iters);\n    float time_elapsed = 0.0;\n    cudaEvent_t start, end;\n    cudaCheck(cudaEventCreate(&start));\n    cudaCheck(cudaEventCreate(&end));\n    cudaCheck(cudaEventRecord(start));\n    cudaCheck(cudaEventSynchronize(start));\n    for (int iter = 1; iter <= n_iters; iter++) {\n        unet_zero_grad(&unet);\n        dataloader_next_batch(&loader);\n        // copy loaded data into input\n        cudaCheck(cudaMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n        // sample timesteps and get timestep embeddings\n        sample_timesteps(&diffusion, d_timesteps_buf);\n        get_timestep_embeddings(&unet.time_emb_state, d_timesteps_buf, unet.timestep_emb);\n        \n        // target will be injected random noise\n        diffusion_draw_normal(&diffusion, unet.target);\n\n        // push input through forward diffusion process by t to get x_t\n        diffusion_forward_by_t(&diffusion, unet.input, d_timesteps_buf, unet.target);\n\n        unet_forward(cublas_handle, &unet);\n        unet_backward(cublas_handle, &unet);\n        unet_update(&unet, 0.0001f, 0.9f, 0.999f, 1e-8f, 0.0f, iter);\n\n        update_loss_counter(&loss_counter, unet.mean_loss);\n\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(end));\n        cudaCheck(cudaEventElapsedTime(&time_elapsed, start, end));\n\n        if (iter % n_log_iter == 0) {\n            printf(\"step %4d/%d | loss %7.6f | mean loss %7.6f | cur time %.4f s\\n\", iter, n_iters, unet.mean_loss, loss_counter.mean_loss, time_elapsed / 1000);\n            FILE *log_file = fopen(log_filename, \"a\");\n            fprintf(log_file, \"step %4d/%d | loss %7.6f | mean loss %7.6f | cur time %.4f s\\n\", iter, n_iters, unet.mean_loss, loss_counter.mean_loss, time_elapsed / 1000);\n            fclose(log_file);\n            clear_loss_counter(&loss_counter);\n        }\n\n        if (iter % n_save_iter == 0) {\n            char save_filename[256];\n            sprintf(save_filename, \"./models/model_%d.bin\", iter);\n            save_unet_states(&unet, &diffusion, save_filename);\n        }\n    }\n    printf(\"average time per iteration: %f ms\\n\", time_elapsed / n_iters);\n\n    cudaCheck(cudaEventDestroy(start));\n    cudaCheck(cudaEventDestroy(end));\n    cublasCheck(cublasDestroy(cublas_handle));\n    cudaCheck(cudaFree(d_timesteps_buf));\n    cudaCheck(cudaFree(unet.target));\n\n    dataloader_free(&loader);\n    diffusion_free(&diffusion);\n    free_unet(&unet);\n\n    printf(\"success\\n\");\n}", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda_device_runtime_api.h>\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include \"utils.cuh\"\n#include <hiprand/hiprand_kernel.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n\n#include <string.h>\n#include <sys/stat.h> // For mkdir\n#include <sys/types.h> // For mode_t\n\nnamespace cg = cooperative_groups;\n\n// ------------------------------------------------------------------------------------------------\n// Linear layer\ntypedef struct {\n    float* w; // OC, C\n    float* b; // OC\n} LinearParams;\n\ntypedef struct {\n    float* inp; // N, C\n    float* out; // N, OC\n} LinearActs;\n\n\nvoid linear_set_param_ptrs(\n    LinearParams* params,\n    float* params_memory,\n    int C, int OC\n) {\n    params->w = params_memory;\n    params_memory += OC * C;\n    params->b = params_memory;\n}\n\ninline size_t linear_count_params(int C, int OC) {\n    return OC * C + OC;\n}\n// ---------------------------------------------\n// kernels\n\n// copy the matmul forward and backward kernels from llm.c\n\n// is there no better way other than just adding bias with a whole separate kernel?\n// this is a highly memory-bound operation, should be fused into the matmul kernel\n// but i can't seem to find a cuBLAS function that does this\n__global__ void add_bias(float* out, const float* bias, int N, int OC) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = idx; i < N * OC; i += stride) {\n        int col = i % OC;\n        out[i] += bias[col];\n    }\n}\n\n// kernel 2 calls cuBLAS, which should be very efficient\nvoid matmul_forward2(hipblasHandle_t cublas_handle,\n                     float* out,\n                     const float* inp, const float* weight, const float* bias,\n                     int N, int C, int OC,\n                     const int block_size) {\n    // for reference API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n    // for us, inp is (B*T, C), weight is (OC, C), out is (B*T, OC)\n    // cuBLAS does C = alpha * A * B + beta * C\n    // where A is mxk, B is kxn, C is mxn\n    // now, because we use row-major storage, cuBLAS (which is column-major) sees our matrices transposed.\n    // algorithmically / in e.g. PyTorch we want to do: out = inp @ weight.T\n    // but because cuBLAS is column-major, we actually want to get it to calculate out.T . Mathematically, this is:\n    // out.T = weight @ inp.T\n    // but again, our variables look transposed, so using the actual weight/inp we have here in this function, this becomes\n    // out.T = weight.T @ inp\n    // so we need to get cuBLAS to calculate weight.T @ inp (the variables here are the actual ones in this function)\n    // => need to call cuBLAS with A = weight, B = inp\n    // => need to call cuBLAS with transa = CUBLAS_OP_T, transb = CUBLAS_OP_N\n\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, OC, N, C, &alpha, weight, C, inp, C, &beta, out, OC));\n    // and now we still have to add the bias... (ew)\n    if (bias != NULL) {\n        int grid_size = ceil_div(OC * N, block_size);\n        add_bias<<<grid_size, block_size>>>(out, bias, N, OC);\n        cudaCheck(hipGetLastError());\n    }\n}\n\n// use shared memory and coarsening + reductions\n__global__ void matmul_backward_bias_kernel_faster(float* dbias, const float* dout, int N, int OC) {\n    extern __shared__ float shared[];\n    int o = blockIdx.x; // range [0, OC)\n    int tid = threadIdx.x; // range [0, block_size)\n    int block_size = blockDim.x;\n    const float* x = dout + o;\n    // thread coarsening\n    double sum = 0.0;\n    for (int i = tid; i < N; i += block_size) {\n        sum += x[i * OC];\n    }\n    shared[tid] = (float) sum;\n    __syncthreads();\n    // reductions\n    for (int stride = block_size / 2; stride >= 1; stride /= 2) {\n        __syncthreads();\n        if (tid < stride) {\n            shared[tid] += shared[tid + stride];\n        }\n    }\n    // write the final result (at thread 0) to global memory\n    if (tid == 0) {\n        dbias[o] = shared[0];\n    }\n}\n\n// version1: simple cuBLAS calls\nvoid matmul_backward1(hipblasHandle_t cublas_handle,\n                      float* dinp, float* dweight, float* dbias,\n                      float* dout, float* inp, float* weight,\n                      int N, int C, int OC) {\n    float alpha = 1.0f;\n    float beta = 0.0f; // note we must use beta = 1.0 so that we do a +=, as we should, because gradients add\n\n    // for reference the API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n\n    // recall the forward pass was calculated with alpha = 1.0f, beta = 0.0f as:\n    // cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, B*T, C, &alpha, weight, C, inp, C, &beta, out, OC);\n\n    // backward to input\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, C, N, OC, &alpha, weight, C, dout, OC, &beta, dinp, C));\n    // backward to weight\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, C, OC, N, &alpha, inp, C, dout, OC, &beta, dweight, C));\n    // backward to bias, if given\n    if (dbias != NULL) {\n\n        // sum over B,T using matrix vector multiplication with cuBLAS\n        // for reference this API is:\n        // cublasStatus_t cublasSgemv(cublasHandle_t handle, cublasOperation_t trans,\n        //                    int m, int n,\n        //                    const float           *alpha,\n        //                    const float           *A, int lda,\n        //                    const float           *x, int incx,\n        //                    const float           *beta,\n        //                    float           *y, int incy)\n        // dout is (B,T,OC), or in 2D terms (B*T, OC)\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_N, B*T, OC, &alpha, dout, B*T, ones, 1, &beta, dbias, 1));\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_T, OC, B*T, &alpha, dout, OC, ones, 1, &beta, dbias, 1));\n\n        // ugh the above isn't working...\n        // let's just do naive calculation for now, fix later\n        // const int block_size=128;\n        // const int grid_size=(OC + block_size - 1) / block_size;\n        // matmul_backward_bias_kernel<<<grid_size, block_size>>>(dbias, dout, B, T, OC);\n\n        // bit faster\n        const int block_size=512;\n        dim3 block_dim(block_size);\n        dim3 grid_dim(OC);\n        size_t shared_mem_size = block_size * sizeof(float);\n        matmul_backward_bias_kernel_faster<<<grid_dim, block_dim, shared_mem_size>>>(dbias, dout, N, OC);\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Broadcast tensor layer\n\n// kernel to broadcast a tensor from shape (B, C) to (B, C, H, W)\n// Assuming one thread per pixel\n__global__ void broadcast_last_dims_forward_kernel(\n    const float* x,\n    float* out,\n    int N, int H, int W, int n_bk_per_img\n) {\n    int n = blockIdx.x / n_bk_per_img;\n    __shared__ float x_sh[1];\n    if (threadIdx.x == 0) {\n        x_sh[0] = x[n];\n    }\n    __syncthreads();\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N * H * W) {\n        out[idx] = x_sh[0];\n    }\n}\n\nvoid broadcast_last_dims_forward(\n    const float* x,\n    float* out,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    int n_bk_per_img = ceil_div(H * W, block_size);\n    broadcast_last_dims_forward_kernel<<<N * n_bk_per_img, block_size>>>(x, out, N, H, W, n_bk_per_img);\n};\n\n// backwards for broadcast\n// use one warp per image\n__global__ void broadcast_last_dims_backward_kernel(\n    const float* dout,\n    float* dx,\n    int N, int H, int W\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    int idx = blockIdx.x * warp.meta_group_size() + warp.meta_group_rank();\n    if (idx >= N) { return; }\n\n    int img_size = H * W;\n    dout += idx * img_size;\n    dx += idx;\n\n    float sum = 0.0f;\n    for (int i = warp.thread_rank(); i < img_size; i += warp.size()) {\n        sum += dout[i];\n    }\n    sum = cg::reduce(warp, sum, cg::plus<float>());\n    if (warp.thread_rank() == 0) {\n        dx[0] = sum;\n    }\n}\n\nvoid broadcast_last_dims_backward(\n    const float* dout,\n    float* dx,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    assert(block_size % 32 == 0);\n    int warps_per_block = block_size / 32;\n    int gridDim = ceil_div(N, warps_per_block);\n    broadcast_last_dims_backward_kernel<<<gridDim, block_size>>>(dout, dx, N, H, W);\n};\n\n// ------------------------------------------------------------------------------------------------\n// Add layer\n__global__ void add_forward_kernel(\n    const float* a, const float* b,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        out[idx] = a[idx] + b[idx];\n    }\n}\n\nvoid add_forward(\n    const float* a, const float* b,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_forward_kernel<<<n_blk, block_size>>>(a, b, out, N);\n}\n\n// add A and B in place, and store result in B\n__global__ void add_inplace_forward_kernel(\n    const float* a, float* b,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        b[idx] += a[idx];\n    }\n}\n\nvoid add_inplace_forward(\n    const float* a, float* b,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_inplace_forward_kernel<<<n_blk, block_size>>>(a, b, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// SiLU layer\n\ntypedef struct {\n    float* x;\n    float* out;\n} SiluActs;\n\n// ----------------------------------------------\n// kernels\n\n__global__ void silu_forward_kernel(\n    const float* x,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n\n    float x_val = x[idx];\n    out[idx] = x_val / (1.0f + expf(-x_val));\n}\n\nvoid silu_forward(\n    const float* x,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_forward_kernel<<<n_blk, block_size>>>(x, out, N);\n}\n\n__global__ void silu_backward_kernel(\n    const float* dout, const float* x,\n    float* dx,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n    float out_val = dout[idx];\n    float x_val = x[idx];\n    float expx = expf(-x_val);\n    float grad_silu = (1.0f + x_val * expx / (1.0f + expx)) / (1.0f + expx);\n    dx[idx] = out_val * grad_silu;\n}\n\nvoid silu_backward(\n    const float* dout, const float* x,\n    float* dx,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_backward_kernel<<<n_blk, block_size>>>(dout, x, dx, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Upsample\ntypedef struct {\n    float* x;\n    float* out;\n} UpsampleActs;\n\n// one thread per pixel\n__global__ void upsample_forward_kernel(\n    const float* x,\n    float* out,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx >= B * C * img_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_size);\n    int c = (flat_idx / img_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + i * W + j;\n    out += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float x_val = x[0];\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            out[ii * W_out + jj] = x_val;\n        }\n    }\n}\n\nvoid upsample_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\n// number of threads equals the number of pixels\n// in downsampled image\n__global__ void upsample_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_in_size = H * W;\n    if (flat_idx >= B * C * img_in_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_in_size);\n    int c = (flat_idx / img_in_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    dx += b * C * img_in_size + c * img_in_size + i * W + j;\n    dout += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float dout_sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dout_sum += dout[ii * W_out + jj];\n        }\n    }\n    dx[0] = dout_sum;\n}\n\nvoid upsample_backward1(\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Avgpool layer\n\ntypedef struct {\n    float* x;\n    float* out;\n} AvgpoolActs;\n\n__global__ void avgpool_2d_forward_kernel(\n    const float* x,  // (B, C, H, W)\n    float* out, // (B, C, H/2, W/2)\n    int B, int C, int H, int W\n) {\n    //printf(\"started kernel\\n\");\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int H_2 = H / 2;\n    int W_2 = W / 2;\n    int img_out_size = H_2 * W_2;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int img_size = H * W;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_2) % H_2;\n    int j = flat_idx % W_2;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + 2 * i * W + 2 * j;\n    out += b * C * img_out_size + c * img_out_size + i * W_2 + j;\n\n    float sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            sum += x[ii * W + jj];\n        }\n    }\n    out[0] = sum / 4.0f;\n}\n\nvoid avgpool_2d_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int gridDim = ceil_div(B * C * (H / 2) * (W / 2), block_size);\n    avgpool_2d_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\n__global__ void avgpool_2d_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H_out, int W_out // H_out and W_out are the shape of the output from forward, i.e. half the original sizes\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_out_size = H_out * W_out;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int H_in = H_out * 2;\n    int W_in = W_out * 2;\n    int img_in_size = H_in * W_in;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_out) % H_out;\n    int j = flat_idx % W_out;\n\n    // move pointers\n    dout += b * C * img_out_size + c * img_out_size + i * W_out + j;\n    dx += b * C * img_in_size + c * img_in_size + 2 * i * W_in + 2 * j;\n\n    float d = dout[0] / 4.0f;\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dx[ii * W_in + jj] = d;\n        }\n    }\n}\n\nvoid avgpool_2d_backward1(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int H_out = H / 2;\n    int W_out = W / 2;\n    int gridDim = ceil_div(B * C * H_out * W_out, block_size);\n    avgpool_2d_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H_out, W_out);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Concat channel layer\n// used to concat unet skip connections in the channel dimension\ntypedef struct {\n    float* x1;\n    float* x2;\n    float* out;\n} ConcatChannelActs;\n\n__global__ void concat_channel_forward_kernel(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        // copy input from x1\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n        out[out_idx] = x1[idx];\n    }\n    if (idx < B * C2 * H * W) {\n        // copy input from x2\n        // move over from x1\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        out[out_idx] = x2[idx];\n    }\n}\n\nvoid concat_channel_forward(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_forward_kernel<<<n_blk, block_size>>>(x1, x2, out, B, C1, C2, H, W);\n}\n\n__global__ void concat_channel_backward_kernel(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n\n        dx1[idx] = dout[out_idx];\n    }\n    if (idx < B * C2 * H * W) {\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        dx2[idx] = dout[out_idx];\n    }\n}\n\nvoid concat_channel_backward(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_backward_kernel<<<n_blk, block_size>>>(dout, dx1, dx2, B, C1, C2, H, W);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Conv2d 1x1 layer\n\ntemplate <const int BO, const int BH, const int TO, const int TH>\n__global__ void conv2d_k1_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int H, int W, int O\n) {\n    // constants\n    const int BC = 16;\n    assert(O % BO == 0);\n    assert(C % BC == 0);\n    assert(H * W % BH == 0);\n    // n_threads per block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int block_col = blockIdx.x % blocks_per_col;\n    const int batch = blockIdx.x / blocks_per_col;\n\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for weight and x\n    const int weight_s_sz = BO * BC;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[weight_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // local registers for results;\n    float thread_results[TO * TH] = {0.0};\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // get offsets to x and weight\n    int x_block_offset = batch * C * H * W + block_col * BH;\n    int w_block_offset = block_row * BO * C;\n\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx;\n        // load weights to smem\n        for (int i = threadIdx.x; i < weight_s_sz; i += blockDim.x) {\n            int w_i = i / BC;\n            int w_j = i % BC;\n            int w_abs_idx = w_block_offset + w_inner_offset + w_i * C + w_j;\n            w_s[w_i * BC + w_j] = weight[w_abs_idx];\n        }\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            int x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            x_s[x_i * BH + x_j] = x[x_abs_idx];\n        }\n        __syncthreads();\n\n\n        // calculate thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            for (int i = 0; i < TO; i++) {\n                reg_w[i] = w_s[(thread_row * TO + i) * BC + dot_idx];\n            }\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int i = 0; i < TO; i++) {\n                for (int j = 0; j < TH; j++) {\n                    thread_results[i * TH + j] += reg_w[i] * reg_x[j];\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // set out and bias directly to thread offset\n    int out_block_offset = batch * O * H * W + block_row * BO * H * W + block_col * BH;\n    int bias_offset = block_row * BO;\n    for (int i = 0; i < TO; i++) {\n        for (int j = 0; j < TH; j++) {\n            int bias_idx = bias_offset + thread_row * TO + i;\n            float out_val = thread_results[i * TH + j] + bias[bias_idx];\n            int out_idx = out_block_offset + thread_row * TO * H * W + thread_col * TH + i * H * W + j;\n            atomicAdd(out + out_idx, out_val);\n        }\n    }\n}\n\nvoid conv2d_k1_forward2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int H, int W, int C_out\n) {\n    cudaCheck(hipMemset(out, 0, B * C_out * H * W * sizeof(float)));\n    const int BO = 64;\n    const int BH = 64;\n    const int TO = 4;\n    const int TH = 4;\n    dim3 gridDim(B * H * W / BH, C_out / BO);\n    dim3 blockDim((BO * BH) / (TO * TH));\n    conv2d_k1_forward_kernel<BO, BH, TO, TH><<<gridDim, blockDim>>>(x, weight, bias, out, B, C_in, H, W, C_out);\n}\n\n__global__ void permute_dout_x_kernel(\n    const float* x, const float* dout,\n    float* x_perm, float* dout_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    int h = (flat_idx / W) % H;\n    int w = flat_idx % W;\n\n    if (flat_idx < B * C_in * H * W) {\n        // permute x\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_perm[out_ind] = x[flat_idx];\n    }\n\n    if (flat_idx < B * C_out * H * W) {\n        // permute dout\n        int b = flat_idx / (C_out * img_size);\n        int c = (flat_idx / img_size) % C_out;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        dout_perm[out_ind] = dout[flat_idx];\n    }\n}\n\n\n__global__ void permute_dx_kernel(\n    const float* dx_perm,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx < B * C_in * img_size) {\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n        int out_ind = b * C_in * img_size + c * img_size + h * W + w;\n        dx[out_ind] = dx_perm[flat_idx];\n    }\n}\n\n\n// we want to sum the B, H, W dimensions of dout\n// since dout_perm is shaped as (C_out, B, H, W)\n// we will have each block take care of one entry of C_out\n// and the threads in each block will use coorperative_groups to do the sum\n__global__ void dbias_kernel(\n    const float* dout_perm, // shape (C_out, B, H, W)\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    extern __shared__ float shared[];\n\n    // move dout_perm to the C_out index\n    if (threadIdx.x < bhw) {\n        dout_perm += blockIdx.x * B * H * W;\n        float th_sum = 0.0f;\n        for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n            th_sum += dout_perm[i];\n        }\n        shared[threadIdx.x] = th_sum;\n    }\n    __syncthreads();\n\n    // reduce the shared memory\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            shared[threadIdx.x] += shared[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = shared[0];\n    }\n}\n\n// uses cublas\nvoid conv2d_k1_backward1(\n    hipblasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // shapes:\n    // x, dx: (B, C_in, H, W)\n    // weight: (C_out, C_in)\n    // bias: (C_out,)\n    // dout: (B, C_out, H, W)\n\n    // plan (the shapes are raw array shapes, note that cublas interprets the arrays as transposed):\n    // 1. transpose x to x_perm of shape (C_in, B*H*W), and dout to dout_perm of shape (C_out, B*H*W)\n    // 2. do one sgemm call to get dw = x.T @ dout_perm, and another call to get dx_perm = dout @ w.T\n    // 3. dx_perm is of shape (C_in, B*H*W), so call a kernel to permute it\n    // 4. reduce to get dbias\n    float *x_perm, *dout_perm, *dx_perm;\n    cudaCheck(hipMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&dout_perm, C_out * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&dx_perm, C_in * B * H * W * sizeof(float)));\n\n    // 1. permute x and dout\n    int total_th1 = (C_in > C_out) ? C_in * B * H * W : C_out * B * H * W;\n    int n_blk1 = ceil_div(total_th1, block_size);\n    permute_dout_x_kernel<<<n_blk1, block_size>>>(x, dout, x_perm, dout_perm, B, C_in, C_out, H, W);\n\n    // 2.a sgemm call for dw = x_perm.T @ dout_perm\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, C_in, C_out, non_C_dim, &alpha, x_perm, non_C_dim, dout_perm, non_C_dim, &beta, dweight, C_in));\n\n    // 2.b sgemm call for dx_perm = dout_perm @ weight.T\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, non_C_dim, C_in, C_out, &alpha, dout_perm, non_C_dim, weight, C_in, &beta, dx_perm, non_C_dim));\n\n    // 3. permute dx_perm from (C_out, B, H, W) to (B, C_out, H, W)\n    int total_th2 = C_in * B * H * W;\n    int n_blk2 = ceil_div(total_th2, block_size);\n    permute_dx_kernel<<<n_blk2, block_size>>>(dx_perm, dx, B, C_in, H, W);\n\n    // 4. reduce dout to get dbias\n    dbias_kernel<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(hipFree(x_perm));\n    cudaCheck(hipFree(dout_perm));\n    cudaCheck(hipFree(dx_perm));\n}\n\n// ------------------------------------------------------------------------------------------------\n// Conv2d 3x3 layer\ntypedef struct {\n    float* w;\n    float* b;\n} ConvK3Params;\n\ninline void convk3_set_param_ptrs(\n    ConvK3Params* params,\n    float* params_memory,\n    int C_in, int C_out\n) {\n    params->w = params_memory;\n    params->b = params->w + C_in * C_out * 9;\n}\n\ninline size_t convk3_count_params(int C_in, int C_out) {\n    return C_in * C_out * 9 + C_out;\n}\n\ntypedef struct {\n    float* inp;\n    float* out;\n} ConvK3Acts;\n\n// ---------------------------------------------\n// kernels\n\n// this kernel will roughly follow kernel 5 here: https://github.com/siboehm/SGEMM_CUDA, with a few differences\n// the main operation is convolving the input X with the weights\n// weights are shaped (O, Cx9), and X is shaped (B, C, HxW)\n// We are morally doing a matmul with dimensions O, C, HxW\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_j * BO + w_i] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n// almost same as 2, but we vectorize the loads and use float4\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    assert(TH == 4); // needed for reg_x loads later, so we can do one vectorized load for each thread\n    const int C9 = C * 9;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (w_s_width / 4);\n            int inner_row = i / (w_s_width / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            w_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            w_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            w_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            w_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        // load x into smem\n        for (int i = threadIdx.x; i < x_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&x_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            // NOTE: relying on the fact that TH = 4\n            reinterpret_cast<float4 *>(reg_x)[0] = reinterpret_cast<float4 *>(&x_s[dot_idx * BH + thread_col * TH])[0];\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\nvoid conv2d_k3_forward3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // since the kernel will add to the output, we need to zero it first\n    cudaCheck(hipMemset(out, 0, B * C_out * H * W * sizeof(float)));\n\n    if (H * W == 64) {\n        const int BH = 64;\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_in == 3) {\n        const int BH = 64;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        const int BC = 3;\n        assert(C_out % 64 == 0);\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_out == 3) {\n        const int BH = 64 * 2;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_in % 8 == 0);\n        const int BC = 8;\n        const int BO = 3;\n        const int TO = 3;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else {\n        const int BH = 64 * 4;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 8;\n        const int TO = 2;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    }\n    cudaCheck(hipGetLastError());\n}\n\n// almost the same as conv2d_k3_forward_kernel, except that the compute and the writes\n// are different, because now we want to treat the C dimension as the inner product dimension\ntemplate <const int BO, const int BH, const int BC, const int TC, const int TH>\n__global__ void dx_backward_kernel1(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int C9 = C * 9;\n    // n_threads per block = (BC * BH) / (TC * TH)\n    // set gridDim to (BxHxW / BH, C / TC)\n    // find the output block that this thread block is responsible for\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TCxTH block that the thread is responsible\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for weights and dout\n    const int w_s_width = BC * 9;\n    const int w_s_sz = BO * w_s_width;\n    const int dout_s_sz = BO * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float dout_s[dout_s_sz];\n\n    // registers for local results\n    float thread_results[TC * TH * 9] = {0.0};\n    // regs for weights and dout\n    float reg_w[TC * 9] = {0.0};\n    float reg_dout[TH] = {0.0};\n\n    // calculate the block offsets\n    const int dout_block_offset = batch * O * H * W + block_col * BH;\n    const int w_block_offset = block_row * w_s_width;\n\n    // outer loop over BO sizes of channel O\n    for (int o_idx = 0; o_idx < O; o_idx += BO) {\n        // offsets for this inner block\n        int w_inner_offset = o_idx * C9;\n        int dout_inner_offset = o_idx * H * W;\n\n        // load weights to smem\n        if (BC % 4 == 0) {\n            // vectorize dram load\n            for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n                int inner_col = i % (w_s_width / 4);\n                int inner_row = i / (w_s_width / 4);\n                reinterpret_cast<float4 *>(&w_s[inner_row * w_s_width + inner_col * 4])[0] = \n                    reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            }\n        } else {\n            for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n                int inner_col = i % w_s_width;\n                int inner_row = i / w_s_width;\n                w_s[inner_row * w_s_width + inner_col] = weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col];\n            }\n        }\n        // load dout\n        for (int i = threadIdx.x; i < dout_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&dout_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n        \n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BO; dot_idx++) {\n            // load dout and w into registers\n            for (int j = 0; j < (TH / 4); j++) {\n                reinterpret_cast<float4 *>(&reg_dout[j * 4])[0] = \n                    reinterpret_cast<float4 *>(&dout_s[dot_idx * BH + thread_col * TH + j * 4])[0];\n            }\n            // non vectorized load\n            //for (int j = 0; j < TH; j++) {\n            //    reg_dout[j] = dout_s[dot_idx * BH + thread_col * TH + j];\n            //}\n            if (TC % 4 == 0) {\n                // vectorize smem load\n                for (int i = 0; i < (TC * 9 / 4); i++) {\n                    reinterpret_cast<float4 *>(&reg_w[i * 4])[0] = \n                        reinterpret_cast<float4 *>(&w_s[dot_idx * w_s_width + thread_row * TC * 9 + i * 4])[0];\n                }\n            } else {\n                for (int i = 0; i < TC * 9; i++) {\n                    reg_w[i] = w_s[dot_idx * w_s_width + thread_row * TC * 9 + i];\n                }\n            }\n\n            for (int k = 0; k < 9; k++) {\n                for (int i = 0; i < TC; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        thread_results[k * TC * TH + i * TH + j] += \n                            reg_w[i * 9 + k] * reg_dout[j];\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    int out_batch_offset = batch * C * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TC; i++) {\n            int c_abs = block_row * BC + thread_row * TC + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs - 1 + k_i;\n                int w_target_abs = w_abs - 1 + k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + c_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TC * TH + i * TH + j;\n                    atomicAdd(dx + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n        }\n    }\n}\n\nvoid dx_backward(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    if (H < 16) {\n        const int BH = 64;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_in == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 3;\n        const int TC = 3;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_out == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 3;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    }\n}\n\n// same as dweight backward 4, except we also do some of the reduce for dbias\n// so it doesn't have to reduce an entire BxHxW length for each O channel\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_dbias_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf, float* dbias_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = ceil_div((int)blockDim.x, 32);\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n    float thread_bias_results[TO] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    int h_step = (NH < H) ? NH - 1 : H;\n    // if NH == H, then we are loading the entire image in one go, so step by H\n    for (int h = 0; h < H; h += h_step) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1) {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1 && H > NH) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1 && W > WSZ) {\n                        // if x_w is at the last pixel, and we have more than one width sections, and we are not are the last section\n                        // then don't compute 1, 4, 7 kernels and wait for the next section to compute them\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                    // update dbias when k = 4, where dout_h and dout_w match x_h and x_w\n                    if (k == 4 && block_col == 0) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_bias_results[i] += reg_dout[i];\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out dweight results\n    for (int i = 0; i < TO; i++) {\n        int o_abs = block_row * BO + thread_row * TO + i;\n        for (int j = 0; j < TC; j++) {\n            int c_abs = block_col * BC + thread_col * TC + j;\n            for (int k = 0; k < 9; k++) {\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n        // write out dbias results\n        if (block_col == 0) {\n            dbias_buf[o_abs * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_bias_results[i];\n        }\n    }\n}\n\n// C * O blocks\n// 288 (9 * 32) threads per block, use one warp to reduce each batch dim\n__global__ void dweight_reduce_kernel(\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O\n) {\n    assert(gridDim.x == O * C);\n    int o = blockIdx.x / C;\n    int c = blockIdx.x % C;\n    // move input pointer\n    dweight_buf += o * C * 9 * B + c * 9 * B;\n    assert(blockDim.x == 32 * 9);\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        thread_sum += dweight_buf[warp_id * B + i];\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dweight[o * C * 9 + c * 9 + warp_id] = warp_sum;\n    }\n}\n\n__global__ void dbias_reduce_kernel1(\n    float* dbias_buf, float* dbias,\n    const int B\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    dbias_buf += blockIdx.x * B;\n\n    int lane_id = threadIdx.x % 32;\n\n    float db_thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        db_thread_sum += dbias_buf[i];\n    }\n\n    float db_warp_sum = cg::reduce(warp, db_thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dbias[blockIdx.x] = db_warp_sum;\n    }\n}\n\nvoid dweight_dbias_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    float* dbias_buf, float* dbias,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    if (H * W == 64) {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8;\n        const int BW = WSZ * 8;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B);\n    } else if (C == 3) {\n        const int BC = 3;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 3;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else if (O == 3) {\n        const int BC = 64;\n        const int BO = 3;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 3;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    }\n}\n\nvoid conv2d_k3_backward2(\n    float* dout, float* x, float* weight,\n    float* dweight_buf, float* dbias_buf,\n    float* dx, float* dweight, float* dbias,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // dx currently requires the output buffer to be zeroed\n    cudaCheck(hipMemset(dx, 0, B * C_in * H * W * sizeof(float)));\n    dx_backward(dout, weight, dx, B, C_in, C_out, H, W);\n    dweight_dbias_backward1(dout, x, dweight_buf, dweight, dbias_buf, dbias, B, C_in, C_out, H, W);\n}\n\n// ------------------------------------------------------------------------------------------------\n// GroupNorm\n\ntypedef struct {\n    float* w;\n    float* b;\n} GroupNormParams;\n\ninline void gn_set_param_ptrs(\n    GroupNormParams* params,\n    float* params_memory,\n    int C\n) {\n    params->w = params_memory;\n    params->b = params->w + C;\n}\n\ntypedef struct {\n    float* x;\n    float* out;\n    float* mean;\n    float* rstd;\n} GroupNormActs;\n\ninline void gn_set_act_ptrs(\n    GroupNormActs* acts,\n    float* acts_memory,\n    int B, int C, int H, int W, int n_groups\n) {\n    acts->out = acts_memory;\n    acts->mean = acts->out + B * C * H * W;\n    acts->rstd = acts->mean + B * n_groups;\n}\n\ntypedef struct {\n    float* dx;\n    float* dout;\n} GroupNormBackActs;\n\n// ---------------------------------------------\n// kernels\n\n// Essentially taken from llm.c's kernel 5\n// using kernel 5 because for images, each \"channel\" is effectively\n// H * W * group_size, which is quite large\n// we will have one block per group, which means there are B * C / group_size blocks\n__global__ void groupnorm_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    x += blockIdx.x * img_size * group_size;\n    out += blockIdx.x * img_size * group_size;\n    // each block will only every acces group_size channels\n    weight += g * group_size;\n    bias += g * group_size;\n\n    float thread_sum = 0.0f;\n    float thread_sum2 = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        float val = x[i];\n        thread_sum += val;\n        thread_sum2 += val * val;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    float warp_sum2 = cg:: reduce(warp, thread_sum2, cg::plus<float>{});\n    // store warp sum into shared memory\n    shared_sum[warp_id] = warp_sum;\n    shared_sum2[warp_id] = warp_sum2;\n    __syncthreads();\n    \n    // load warp sums from shared memory\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    warp_sum2 = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n    float block_sum2 = cg::reduce(warp, warp_sum2, cg::plus<float>{});\n    block_sum /= block_pixels;\n    block_sum2 /= block_pixels;\n    float m = block_sum;\n    float var = block_sum2 - m * m;\n    float s = rsqrtf(var + 1e-5f);\n    if (threadIdx.x == 0 && mean != nullptr) {\n        mean[blockIdx.x] = m;\n    }\n    if (threadIdx.x == 0 && rstd != nullptr) {\n        rstd[blockIdx.x] = s;\n    }\n\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float n = s * (x[i] - m);\n        out[i] = n * weight[c_mod_group] + bias[c_mod_group];\n    }\n}\n\nvoid groupnorm_forward(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32);\n    groupnorm_forward_kernel<<<n_blocks, block_size>>>(\n        x, weight, bias, out, mean, rstd, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(hipGetLastError());\n}\n\n// most similar to kernel 2 in llm.c\n// not doing any float16 optimizations yet\n// main change is to have each block allocated to a single group\n__global__ void groupnorm_backward_kernel(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    dout += blockIdx.x * img_size * group_size;\n    x += blockIdx.x * img_size * group_size;\n    dx += blockIdx.x * img_size * group_size;\n    weight += g * group_size;\n    dweight += g * group_size;\n    dbias += g * group_size;\n\n    float m_val = mean[blockIdx.x];\n    float rstd_val = rstd[blockIdx.x];\n\n\n    // calculate the two mean terms in the group dimension\n    // first is dout * weight, and second is dout * weight * norm\n    // where norm = (x - mean) * rstd\n    float w_dout_thread = 0.0f;\n    float w_dout_norm_thread = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float cur_w_dout = weight[c_mod_group] * dout[i];\n        w_dout_thread += cur_w_dout;\n        float norm = (x[i] - m_val) * rstd_val;\n        w_dout_norm_thread += cur_w_dout * norm;\n    }\n    // warp reduce\n    float w_dout_warp = cg::reduce(warp, w_dout_thread, cg::plus<float>{});\n    float w_dout_norm_warp = cg::reduce(warp, w_dout_norm_thread, cg::plus<float>{});\n    // store warp sum in shared mem\n    shared_sum[warp_id] = w_dout_warp;\n    shared_sum2[warp_id] = w_dout_norm_warp;\n    __syncthreads();\n\n    // load warp sums from shared memory\n    w_dout_warp = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    w_dout_norm_warp = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float w_dout_block = cg::reduce(warp, w_dout_warp, cg::plus<float>{});\n    float w_dout_norm_block = cg::reduce(warp, w_dout_norm_warp, cg::plus<float>{});\n    w_dout_block /= block_pixels;\n    w_dout_norm_block /= block_pixels;\n\n    // update dx\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        // in bounds of image\n        // accumulate dw and db\n        float dout_val = dout[i];\n        float norm = (x[i] - m_val) * rstd_val;\n\n        // update dx\n        int c_mod_group = (i / img_size) % group_size;\n        float w_dout = weight[c_mod_group] * dout_val;\n        dx[i] = (w_dout - w_dout_block - norm * w_dout_norm_block) * rstd_val;\n    }\n    // update dw and db\n    // use different methods when the image size is large or small\n\n    // if the image size is larger than the block size\n    // loop over the channels and use the whole block on each channel\n    // otherwise, assign each warp to a channel\n    // in either case image size must be larger than the warp size\n    //assert(img_size % warp.size() == 0);\n    assert(blockDim.x % warp.size() == 0);\n    if (img_size % blockDim.x == 0) {\n        for (int c = 0; c < group_size; c++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n            for (int i = threadIdx.x; i < img_size; i += blockDim.x) {\n                float dout_val = dout[i];\n                db_thread += dout_val;\n                float norm = (x[i] - m_val) * rstd_val;\n                dw_thread += dout_val * norm;\n            }\n\n            // move pointers\n            dout += img_size;\n            x += img_size;\n\n            // warp reduce\n            float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n            float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n            ////// store warp sum in shared mem\n            if (lane_id == 0) {\n                shared_sum[warp_id] = dw_warp;\n                shared_sum2[warp_id] = db_warp;\n            }\n            __syncthreads();\n            // use the first thread to reduce the shared memory sums and save to global memory\n            if (threadIdx.x == 0) {\n                float dw_block = 0.0f;\n                float db_block = 0.0f;\n                for (int i = 0; i < num_warps; i++) {\n                    dw_block += shared_sum[i];\n                    db_block += shared_sum2[i];\n                }\n                atomicAdd(dweight + c, dw_block);\n                atomicAdd(dbias + c, db_block);\n            }\n        }\n    } else {\n        // if group size is large, need to loop over the group channels with the whole block\n        int block_reps = ceil_div(group_size, num_warps);\n        for (int br = 0; br < block_reps; br++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n\n            int ch = br * num_warps + warp_id;\n            if (ch < group_size) {\n                const float* dout_ch = dout + ch * img_size;\n                const float* x_ch = x + ch * img_size;\n                for (int i = lane_id; i < img_size; i += warp.size()) {\n                    float dout_val = dout_ch[i];\n                    db_thread += dout_val;\n                    float norm = (x_ch[i] - m_val) * rstd_val;\n                    dw_thread += dout_val * norm;\n                }\n                \n                // warp reduce\n                float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n                float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n                // since each warp takes care of an entire image\n                // directly store result\n                if (lane_id == 0) {\n                    atomicAdd(dweight + ch, dw_warp);\n                    atomicAdd(dbias + ch, db_warp);\n                }\n            }\n        }\n    }\n}\n\nvoid groupnorm_backward(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32 * group_size);\n    groupnorm_backward_kernel<<<n_blocks, block_size>>>(\n        dout, x, mean, rstd, weight, dx, dweight, dbias, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(hipGetLastError());\n}\n// ------------------------------------------------------------------------------------------------\n// ResBlock\n\n#define NUM_RES_PARAM_TENSORS 12\ntypedef struct {\n    float* gn1_w; // C\n    float* gn1_b; // C\n    float* cv3_1_w; // C_out, C, 3, 3\n    float* cv3_1_b; // C_out\n    float* l_emb_w; // C_out, C_emb\n    float* l_emb_b; // C_out\n    float* gn2_w; // C_out\n    float* gn2_b; // C_out\n    float* cv3_2_w; // C_out, C_out, 3, 3\n    float* cv3_2_b; // C_out\n    float* res_cv1_w; // C_out, C\n    float* res_cv1_b; // C_out\n    size_t param_sizes[NUM_RES_PARAM_TENSORS];\n    size_t n_params;\n} ResBlockParameters;\n\n#define NUM_RES_ACT_TENSORS 18 // not counting input and emb, which will come from outs of previous layers\ntypedef struct {\n    float* gn1; // B, C, H, W\n    float* gn1_mean; // B, n_groups\n    float* gn1_rstd; // B, n_groups\n    float* silu1; // B, C, H, W\n    float* ud_h; // B, C, H_out, W_out\n    float* ud_x; // B, C, H_out, W_out\n    float* cv3_1; // B, C_out, H_out, W_out\n    float* silu_emb; // B, C_emb\n    float* l_emb; // B, C_out\n    float* broad_emb; // B, C_out, H_out, W_out\n    float* add1; // B, C_out, H_out, W_out\n    float* gn2; // B, C_out, H_out, W_out\n    float* gn2_mean; // B, n_groups\n    float* gn2_rstd; // B, n_groups\n    float* silu2; // B, C_out, H_out, W_out\n    float* cv3_2; // B, C_out, H_out, W_out\n    float* res_cv1; // B, C_out, H_out, W_out\n    float* add2; // B, C_out, H_out, W_out\n    size_t act_sizes[NUM_RES_ACT_TENSORS];\n    size_t n_acts;\n    float* input; // B, C, H, W\n    float* emb; // B, C_emb\n} ResBlockActivations;\n\n\n// the last two backward tensors, dx and demb\n// we don't need to allocate extra space for these\n// instead we will just overwrite forward pass activations acts->input and acts->emb\n#define NUM_RES_BACKWARD_TENSORS 7\ntypedef struct {\n    float* buf_BCemb;\n    float* buf_BCHoWo;\n    float* buf1_BCHW;\n    float* buf2_BCHW;\n    float* dout; // B, C_out, H_out, W_out\n    float* dweight_buf;\n    float* dbias_buf;\n    float* dx; // B, C, H, W\n    float* demb; // B, C_emb\n    size_t back_sizes[NUM_RES_BACKWARD_TENSORS];\n    size_t n_backs;\n} ResBlockBackwardActivations;\n\nvoid resblock_count_params(\n    ResBlockParameters* params,\n    int C, int C_emb, int C_out, int B, int H, int W, int up, int down, int gn_n_groups\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // fill and count param sizes\n    size_t* param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn1_w\n    param_sizes[1] = C; // gn1_b\n    param_sizes[2] = C_out * C * 9; // cv3_1_w\n    param_sizes[3] = C_out; // cv3_1_b\n    param_sizes[4] = C_emb * C_out; // l_emb_w\n    param_sizes[5] = C_out; // l_emb_b\n    param_sizes[6] = C_out; // gn2_w\n    param_sizes[7] = C_out; // gn2_b\n    param_sizes[8] = C_out * C_out * 9; // cv3_2_w\n    param_sizes[9] = C_out; // cv3_2_b\n    param_sizes[10] = C_out * C; // res_cv1_w\n    param_sizes[11] = C_out; // res_cv1_b\n    if (C_out != C) {\n        param_sizes[10] = C_out * C; // res_cv1_w\n        param_sizes[11] = C_out; // res_cv1_b\n    } else {\n        param_sizes[10] = 0;\n        param_sizes[11] = 0;\n    }\n\n    // count number of params\n    size_t num_parameters = 0;\n    for  (size_t i = 0; i < NUM_RES_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\n\nvoid resblock_count_acts(\n    ResBlockActivations* acts,\n    int C, int C_emb, int C_out, int B, int H, int W, int up, int down, int gn_n_groups\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // fill and count activation sizes\n    size_t* acts_sizes = acts->act_sizes;\n    acts_sizes[0] = B * C * H * W; // gn1\n    acts_sizes[1] = B * gn_n_groups; // gn1_mean\n    acts_sizes[2] = B * gn_n_groups; // gn1_rstd\n    acts_sizes[3] = B * C * H * W; // silu1\n    acts_sizes[4] = B * C * H_out * W_out; // ud_h\n    acts_sizes[5] = B * C * H_out * W_out; // ud_x\n    acts_sizes[6] = B * C_out * H_out * W_out; // cv3_1\n    acts_sizes[7] = B * C_emb; // silu_emb\n    acts_sizes[8] = B * C_out; // l_emb\n    acts_sizes[9] = B * C_out * H_out * W_out; // broad_emb\n    acts_sizes[10] = B * C_out * H_out * W_out; // add1\n    acts_sizes[11] = B * C_out * H_out * W_out; // gn2\n    acts_sizes[12] = B * gn_n_groups; // gn2_mean\n    acts_sizes[13] = B * gn_n_groups; // gn2_rstd\n    acts_sizes[14] = B * C_out * H_out * W_out; // silu2\n    acts_sizes[15] = B * C_out * H_out * W_out; // cv3_2\n    acts_sizes[16] = (C_out == C) ? 0 : B * C_out * H_out * W_out; // res_cv1\n    acts_sizes[17] = B * C_out * H_out * W_out; // add2\n\n    size_t num_acts_params = 0;\n    for (size_t i = 0; i < NUM_RES_ACT_TENSORS; i++) {\n        num_acts_params += acts_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\nvoid set_resblock_params_ptrs(\n    int C,\n    int C_out,\n    ResBlockParameters *params,\n    float* params_memory_gpu\n) {\n    float** ptrs[] = {\n        &params->gn1_w, &params->gn1_b, &params->cv3_1_w, &params->cv3_1_b,\n        &params->l_emb_w, &params->l_emb_b, &params->gn2_w, &params->gn2_b,\n        &params->cv3_2_w, &params->cv3_2_b, &params->res_cv1_w, &params->res_cv1_b\n    };\n    int num_param_tensors = NUM_RES_PARAM_TENSORS;\n    if (C_out == C) {\n        num_param_tensors -= 2;\n    }\n    float* params_ptr = params_memory_gpu;\n    for (int i = 0; i < num_param_tensors; i++) {\n        *(ptrs[i]) = params_ptr;\n        params_ptr += params->param_sizes[i];\n    }\n    if (C_out == C) {\n        params->res_cv1_w = nullptr;\n        params->res_cv1_b = nullptr;\n    }\n}\n\nvoid set_resblock_acts_ptrs(\n    int C,\n    int C_out,\n    ResBlockActivations *acts,\n    float* acts_memory\n) {\n    float** acts_ptrs[] = {\n        &acts->gn1, &acts->gn1_mean, &acts->gn1_rstd, &acts->silu1,\n        &acts->ud_h, &acts->ud_x, &acts->cv3_1, &acts->silu_emb, &acts->l_emb, &acts->broad_emb,\n        &acts->add1, &acts->gn2, &acts->gn2_mean, &acts->gn2_rstd, &acts->silu2, &acts->cv3_2,\n        &acts->res_cv1, &acts->add2\n    };\n    float* acts_ptr = acts_memory;\n    for (int i = 0; i < NUM_RES_ACT_TENSORS - 2; i++) {\n        *(acts_ptrs[i]) = acts_ptr;\n        acts_ptr += acts->act_sizes[i];\n    }\n\n    if (C_out == C) {\n        acts->res_cv1 = nullptr;\n        acts->add2 = acts_ptr;\n    } else {\n        acts->res_cv1 = acts_ptr;\n        acts_ptr += acts->act_sizes[NUM_RES_ACT_TENSORS - 2];\n        acts->add2 = acts_ptr;\n    }\n}\n\n// assume all memory is allocated, run the forward pass\nvoid resblock_forward(\n    hipblasHandle_t cublas_handle,\n    int C,\n    int C_emb,\n    int C_out,\n    int B,\n    int H,\n    int W,\n    int block_size,\n    int up,\n    int down,\n    int gn_n_groups,\n    ResBlockParameters *params,\n    ResBlockActivations *acts\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // up to first 3x3 conv in BigGAN res block\n    // note: residual stream needs a convolution at the end if C != C_out\n    groupnorm_forward(acts->input, params->gn1_w, params->gn1_b, acts->gn1, acts->gn1_mean, acts->gn1_rstd,\n        B, C, H, W, gn_n_groups);\n    cudaCheck(hipGetLastError());\n    silu_forward(acts->gn1, acts->silu1, B * C * H * W, block_size);\n    cudaCheck(hipGetLastError());\n    if (up) {\n        upsample_forward1(acts->ud_h, acts->silu1, B, C, H, W, block_size);\n        upsample_forward1(acts->ud_x, acts->input, B, C, H, W, block_size);\n    } else if (down) {\n        avgpool_2d_forward1(acts->ud_h, acts->silu1, B, C, H, W, block_size);\n        avgpool_2d_forward1(acts->ud_x, acts->input, B, C, H, W, block_size);\n    } else {\n        cudaCheck(hipMemcpy(acts->ud_h, acts->silu1, B * C * H * W * sizeof(float), hipMemcpyDeviceToDevice));\n        cudaCheck(hipMemcpy(acts->ud_x, acts->input, B * C * H * W * sizeof(float), hipMemcpyDeviceToDevice));\n    }\n    cudaCheck(hipGetLastError());\n    conv2d_k3_forward3(acts->ud_h, params->cv3_1_w, params->cv3_1_b, acts->cv3_1, B, C, C_out, H_out, W_out);\n    cudaCheck(hipGetLastError());\n\n    // project and add embedding layer\n    silu_forward(acts->emb, acts->silu_emb, B * C_emb, block_size);\n    cudaCheck(hipGetLastError());\n    matmul_forward2(cublas_handle, acts->l_emb, acts->silu_emb, params->l_emb_w, params->l_emb_b, B, C_emb, C_out, block_size);\n    cudaCheck(hipGetLastError());\n    broadcast_last_dims_forward(acts->l_emb, acts->broad_emb, B * C_out, H_out, W_out, block_size);\n    cudaCheck(hipGetLastError());\n    add_forward(acts->cv3_1, acts->broad_emb, acts->add1, B * C_out * H_out * W_out, block_size);\n    cudaCheck(hipGetLastError());\n\n    // second group of norm -> silu -> 3x3 conv\n    groupnorm_forward(acts->add1, params->gn2_w, params->gn2_b, acts->gn2, acts->gn2_mean, acts->gn2_rstd,\n        B, C_out, H_out, W_out, gn_n_groups);\n    cudaCheck(hipGetLastError());\n    silu_forward(acts->gn2, acts->silu2, B * C_out * H_out * W_out, block_size);\n    cudaCheck(hipGetLastError());\n    conv2d_k3_forward3(acts->silu2, params->cv3_2_w, params->cv3_2_b, acts->cv3_2, B, C_out, C_out, H_out, W_out);\n    cudaCheck(hipGetLastError());\n\n    // add residual (change channels with 1x1 conv if neccessary)\n    if (C_out == C) {\n        add_forward(acts->cv3_2, acts->ud_x, acts->add2, B * C_out * H_out * W_out, block_size);\n        cudaCheck(hipGetLastError());\n    } else {\n        conv2d_k1_forward2(acts->ud_x, params->res_cv1_w, params->res_cv1_b, acts->res_cv1, B, C, H_out, W_out, C_out);\n        cudaCheck(hipGetLastError());\n        add_forward(acts->cv3_2, acts->res_cv1, acts->add2, B * C_out * H_out * W_out, block_size);\n        cudaCheck(hipGetLastError());\n    }\n}\n\nvoid resblock_backward(\n    hipblasHandle_t cublas_handle,\n    int C,\n    int C_emb,\n    int C_out,\n    int B,\n    int H,\n    int W,\n    int block_size,\n    int up,\n    int down,\n    int gn_n_groups,\n    ResBlockParameters *params,\n    ResBlockParameters *grads,\n    ResBlockActivations *acts,\n    ResBlockBackwardActivations *back_acts\n) {\n    int H_out = H;\n    int W_out = W;\n    if (up) {\n        H_out *= 2;\n        W_out *= 2;\n    } else if (down) {\n        H_out /= 2;\n        W_out /= 2;\n    }\n    // Note: we're using the activation of the final conv layer as a buffer to store\n    // backward pass gradients, because we don't need it in the backward pass anymore.\n    float *demb = back_acts->demb;\n    float *dx = back_acts->dx;\n    float* dout = back_acts->dout;\n    float *res_dx = back_acts->buf_BCHoWo;\n    float* buf1_BCoHoWo = acts->cv3_2;\n    float* buf2_BCoHoWo = acts->add2;\n    cudaCheck(hipMemset(buf1_BCoHoWo, 0, B * C_out * H_out * W_out * sizeof(float)));\n    cudaCheck(hipMemset(buf2_BCoHoWo, 0, B * C_out * H_out * W_out * sizeof(float)));\n    // Note: we don't need to run a separate kernel to backprop through an add operation\n    // we can just share the dout buffer between the two inputs\n    if (C_out != C) {\n        conv2d_k1_backward1(cublas_handle, dout, acts->ud_x, params->res_cv1_w, res_dx, grads->res_cv1_w, grads->res_cv1_b, B, C, C_out, H_out, W_out, block_size);\n        cudaCheck(hipGetLastError());\n    } else {\n        // point res_dx to dout\n        res_dx = dout;\n    }\n    // NOTE: after backprop through cv1 above, we can use ud_x as a (B, C, H_out, W_out) buffer\n    float* buf1_BCHoWo = acts->ud_x;\n\n    // second group of norm -> silu -> 3x3 conv\n    conv2d_k3_backward2(dout, acts->silu2, params->cv3_2_w, back_acts->dweight_buf, back_acts->dbias_buf, buf1_BCoHoWo, grads->cv3_2_w, grads->cv3_2_b, B, C_out, C_out, H_out, W_out);\n    cudaCheck(hipGetLastError());\n    silu_backward(buf1_BCoHoWo, acts->gn2, buf2_BCoHoWo, B * C_out * H_out * W_out, block_size);\n    cudaCheck(hipGetLastError());\n    groupnorm_backward(buf2_BCoHoWo, acts->add1, acts->gn2_mean, acts->gn2_rstd, params->gn2_w, buf1_BCoHoWo, grads->gn2_w, grads->gn2_b, B, C_out, H_out, W_out, gn_n_groups);\n    cudaCheck(hipGetLastError());\n\n    // emb layer\n    // note at this point we can also use the last layer activations as buffers\n    broadcast_last_dims_backward(buf1_BCoHoWo, acts->l_emb, B * C_out, H_out, W_out, block_size);\n    cudaCheck(hipGetLastError());\n    matmul_backward1(cublas_handle, back_acts->buf_BCemb, grads->l_emb_w, grads->l_emb_b, acts->l_emb, acts->silu_emb, params->l_emb_w, B, C_emb, C_out);\n    cudaCheck(hipGetLastError());\n    silu_backward(back_acts->buf_BCemb, acts->emb, demb, B * C_emb, block_size);\n    cudaCheck(hipGetLastError());\n\n    // first layers\n    conv2d_k3_backward2(buf1_BCoHoWo, acts->ud_h, params->cv3_1_w, back_acts->dweight_buf, back_acts->dbias_buf, buf1_BCHoWo, grads->cv3_1_w, grads->cv3_1_b, B, C, C_out, H_out, W_out);\n    cudaCheck(hipGetLastError());\n    // use acts->ud_x as a buffer for dx\n    float* buf1_BCHW = back_acts->buf1_BCHW;\n    float* buf2_BCHW = back_acts->buf2_BCHW;\n    if (up) {\n        upsample_backward1(buf1_BCHW, buf1_BCHoWo, B, C, H, W, block_size);\n        cudaCheck(hipGetLastError());\n        upsample_backward1(buf2_BCHW, res_dx, B, C, H, W, block_size);\n        cudaCheck(hipGetLastError());\n    } else if (down) {\n        avgpool_2d_backward1(buf1_BCHoWo, buf1_BCHW, B, C, H, W, block_size);\n        cudaCheck(hipGetLastError());\n        avgpool_2d_backward1(res_dx, buf2_BCHW, B, C, H, W, block_size);\n        cudaCheck(hipGetLastError());\n    } else {\n        cudaCheck(hipMemcpy(buf1_BCHW, buf1_BCHoWo, B * C * H * W * sizeof(float), hipMemcpyDeviceToDevice));\n        cudaCheck(hipGetLastError());\n        cudaCheck(hipMemcpy(buf2_BCHW, res_dx, B * C * H * W * sizeof(float), hipMemcpyDeviceToDevice));\n        cudaCheck(hipGetLastError());\n    }\n    // use silu as the dx buffer\n    silu_backward(buf1_BCHW, acts->gn1, acts->silu1, B * C * H * W, block_size);\n    cudaCheck(hipGetLastError());\n    groupnorm_backward(acts->silu1, acts->input, acts->gn1_mean, acts->gn1_rstd, params->gn1_w, buf1_BCHW, grads->gn1_w, grads->gn1_b, B, C, H, W, gn_n_groups);\n    cudaCheck(hipGetLastError());\n\n    add_forward(buf1_BCHW, buf2_BCHW, dx, B * C * H * W, block_size);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// self attention block\n// assumes you have Q, K, V\n__global__ void permute_kernel(float* q, float* k, float* v,\n                               const float* inp,\n                               int B, int N, int NH, int d) {\n    // okay so now, this kernel wants Q,K,V to all be of shape (B, NH, N, d)\n    // but instead, we have a single tensor QKV (inp) of shape (B, N, 3, NH, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Q[b][nh_][n][d_] = inp[b][n][0][nh_][d_]\n\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = \\\n            (b * N * 3 * NH * d)\n            +   (n * 3 * NH * d)\n            +       (0 * NH * d)\n            +          (nh_ * d)\n            +                d_;\n\n        q[idx] = inp[inp_idx];\n        k[idx] = inp[inp_idx + NH * d];\n        v[idx] = inp[inp_idx + 2 * (NH * d)];\n    }\n}\n\n__global__ void scale_kernel(float* inp, float scale, int B, int NH, int T) {\n    // scales the pre-softmax attention scores by scale\n    // we don't want causal attention, so we don't set any values to infinity\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * T * T) {\n        inp[idx] *= scale;\n    }\n}\n\n// warp-level reduction for finding the maximum value\n__device__ float warpReduceMax(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xFFFFFFFF, val, offset));\n    }\n    return val;\n}\n\n// warp-level reduction for summing values\n__device__ float warpReduceSum(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xFFFFFFFF, val, offset);\n    }\n    return val;\n}\n\n__global__ void softmax_forward_kernel4(float* out, const float* inp, int N, int C) {\n    // out is (N, C) just like inp. Each row of inp will get softmaxed.\n    // same as kernel3, but can handle any block size (multiple of 32)\n    // each row of C elements is handled by block_size threads\n    // furthermore, each block_size threads get executed in warps of 32 threads\n\n    // special reduction operations warpReduceMax/warpReduceSum are used for intra-warp reductions\n    // shared memory is used for inter-warp reduction\n    extern __shared__ float shared[];\n    int idx = blockIdx.x;\n    int tid = threadIdx.x;\n    int warpId = threadIdx.x / 32; // warp index within a block\n    int laneId = threadIdx.x % 32; // thread index within a warp\n\n    // the number of warps per block. recall that blockDim.x is block_size\n    int warpsPerBlock = blockDim.x / 32;\n\n    // shared[] must be allocated to have 2 * warpsPerBlock elements\n    // first half for max values, the second half for sum values\n    float* maxvals = shared;\n    float* sumvals = &shared[warpsPerBlock];\n\n    // one row of inp, i.e. inp[idx, :] of shape (C,)\n    const float* x = inp + idx * C;\n\n    // first, thread coarsening by directly accessing global memory in series\n    float maxval = -INFINITY;\n    for (int i = tid; i < C; i += blockDim.x) {\n        maxval = fmaxf(maxval, x[i]);\n    }\n    // now within-warp reductions for maxval\n    maxval = warpReduceMax(maxval);\n\n    // the 0th thread of each warp writes the maxval of that warp to shared memory\n    if (laneId == 0) maxvals[warpId] = maxval;\n    __syncthreads();\n\n    // now the 0th thread reduces the maxvals in shared memory, i.e. across warps\n    if (tid == 0) {\n        float val = maxvals[tid];\n        for (int i = 1; i < warpsPerBlock; i++) {\n            val = fmaxf(val, maxvals[i]);\n        }\n        // store the final max in the first position\n        maxvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the max to all threads\n    float offset = maxvals[0];\n\n    // compute expf and write the result to global memory\n    for (int i = tid; i < C; i += blockDim.x) {\n        // subtract max for numerical stability\n        out[idx * C + i] = expf(x[i] - offset);\n    }\n\n    // okay now we calculated exp(x - max(x))\n    // step 2: sum all the values and divide by the sum\n\n    // thread coarsening for sum\n    x = out + idx * C;\n    float sumval = 0.0f;\n    for (int i = tid; i < C; i += blockDim.x) {\n        sumval += x[i];\n    }\n    // within-warp reduction for sumval\n    sumval = warpReduceSum(sumval);\n\n    // write sumval to shared memory\n    if (laneId == 0) sumvals[warpId] = sumval;\n    __syncthreads();\n\n    // inter-thread reduction of sum\n    if (tid == 0) {\n        float val = sumvals[tid];\n        for (int i = 1; i < warpsPerBlock; ++i) {\n            val += sumvals[i];\n        }\n        sumvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the sum to all threads\n    float sum = sumvals[0];\n\n    // divide the whole row by the sum\n    for (int i = tid; i < C; i += blockDim.x) {\n        out[idx * C + i] = x[i] / sum;\n    }\n}\n\n__global__ void unpermute_kernel(const float* inp, float *out, int B, int N, int NH, int d) {\n   // out has shape (B, nh, N, d) but we need to unpermute it to (B, N, nh, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // out[b][n][nh_][d_] <- inp[b][nh_][n][d_]\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        out[other_idx] = inp[idx];\n    }\n}\n\n// a simple attention forward function taken directly from llm.c kernel 3\nvoid attention_forward1\n(\n    hipblasHandle_t cublas_handle,\n    float* out, float* qkvr, float* preatt, float* att,\n    float* inp,\n    int B, int T, int C, int NH,\n    const int block_size\n) {\n    // note: resure inp as a scratch buffer because it is not used in backward\n    // inp is (B, T, 3C) QKV\n    // preatt, att are (B, NH, T, T)\n    // output is (B, T, C)\n    int HS = C / NH; // head size\n\n    // permute and separate inp from (B, T, 3, NH, HS) to 3X (B, NH, T, HS)\n    float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    int total_threads = B * NH * T * HS;\n    int num_blocks = ceil_div(total_threads, block_size);\n    permute_kernel<<<num_blocks, block_size>>>(q, k, v, inp, B, T, NH, HS);\n\n    // batched matrix multiply with cuBLAS\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle,\n                            HIPBLAS_OP_T, HIPBLAS_OP_N,\n                            T, T, HS,\n                            &alpha,\n                            k, HS, T * HS,\n                            q, HS, T * HS,\n                            &beta,\n                            preatt, T, T * T,\n                            B * NH));\n\n    // multiply all elements of preatt elementwise by scale\n    float scale = 1.0f / sqrtf(HS);\n    total_threads = B * NH * T * T;\n    num_blocks = ceil_div(total_threads, block_size);\n    scale_kernel<<<num_blocks, block_size>>>(preatt, scale, B, NH, T);\n\n    // softmax. preatt is (B, NH, T, T) but we view it as (B * NH * T, T) and use the softmax kernel\n    int softmax_block_size = 256;\n    int grid_size = B * NH * T;\n    size_t shared_mem_size = 2 * softmax_block_size / 32 * sizeof(float);\n    softmax_forward_kernel4<<<grid_size, softmax_block_size, shared_mem_size>>>(att, preatt, B * NH * T, T);\n\n    // new approach: first cuBLAS another batched matmul\n    // y = att @ v # (B, nh, T, T) @ (B, nh, T, hs) -> (B, nh, T, hs)\n    float* vaccum = inp;\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle,\n                            HIPBLAS_OP_N, HIPBLAS_OP_N,\n                            HS, T, T,\n                            &alpha,\n                            v, HS, T * HS,\n                            att, T, T * T,\n                            &beta,\n                            vaccum, HS, T * HS,\n                            B * NH));\n\n    // now unpermute\n    // y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side\n    num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel<<<num_blocks, block_size>>>(vaccum, out, B, T, NH, HS);\n}\n\n__global__ void permute_kernel_backward(float* dinp,\n                                        const float* dq, const float* dk, const float* dv,\n                                        int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_;\n        dinp[inp_idx] = dq[idx];\n        dinp[inp_idx + NH * d] = dk[idx];\n        dinp[inp_idx + 2 * (NH * d)] = dv[idx];\n    }\n}\n\n__global__ void unpermute_kernel_backward(float* dinp, const float *dout, int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        dinp[idx] = dout[other_idx];\n    }\n}\n\n// slight edit from the softmax_autoregressive_backward_kernel from train_gpt2_fp32\n// our attention is no longer autoregressive\n__global__ void softmax_backward_kernel(float* dpreatt, const float* datt, const float* att,\n                                                       int B, int T, int C, float scale) {\n    constexpr const int BlockSize = 256;\n    constexpr int T_per_block = 4;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float block_acc[32];\n\n    int idx = blockIdx.y;\n    // go through blocks in reverse order, so the slowest block starts first\n    int t0 = T - 1 - T_per_block*blockIdx.x;\n\n    att += idx * T * T;\n    datt += idx * T * T;\n    dpreatt += idx * T * T;\n\n    if (warp.meta_group_rank() == 0) {\n        block_acc[warp.thread_rank()] = 0;\n    }\n\n    for(int to = 0; to < T_per_block; ++to) {\n        int t = t0 - to;\n        if(t < 0) return;\n        const float* att_bth = att + t * T;\n        const float* datt_bth = datt + t * T;\n        float* dpreatt_bth = dpreatt + t * T;\n\n        float local_sum = 0.0f;\n        for (int t2 = block.thread_rank(); t2 < T; t2 += BlockSize) {\n            local_sum += att_bth[t2] * datt_bth[t2];\n        }\n\n        block_acc[warp.meta_group_rank()] = cg::reduce(warp, local_sum, cg::plus<float>{});\n        block.sync();\n        local_sum = cg::reduce(warp, block_acc[warp.thread_rank()], cg::plus<float>{});\n\n        for (int t3 = block.thread_rank(); t3 < T; t3 += BlockSize) {\n            // don't touch the cache. Some parts will still be here from the previous loop, and\n            // we want to exploit those.\n            float acc = __ldcs(att_bth + t3) * (__ldcs(datt_bth + t3) - local_sum);\n            __stcs(dpreatt_bth + t3, scale * acc);\n        }\n    }\n}\n\n// the sequence of transformations in this compound op is:\n// inp (B,T,3C) -> qkvr (B,T,3C) -> preatt (B,NH,T,T) -> att (B,NH,T,T) -> vaccum (B,T,C) -> out (B,T,C)\nvoid attention_backward\n(\n    hipblasHandle_t cublas_handle,\n    float* dinp, float* dqkvr, float* dpreatt, float* datt, float* scratch,\n    const float* dout,\n    const float* qkvr, const float* att,\n    int B, int T, int C, int NH\n) {\n    const int block_size = 256;\n    int HS = C / NH; // head size\n    const float one = 1.0f;\n    const float zero = 0.0f; // note beta = 1.0f so that we accumulate gradients (+=)\n    // unpack convenience pointers into q, k, v\n    const float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    float *dq, *dk, *dv;\n    dq = dqkvr + 0 * B * T * C;\n    dk = dqkvr + 1 * B * T * C;\n    dv = dqkvr + 2 * B * T * C;\n    // backward through the unpermute operation\n    int num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel_backward<<<num_blocks, block_size>>>(scratch, dout, B, T, NH, HS);\n    cudaCheck(hipGetLastError());\n    // backward into datt\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, T, T, HS, &one, v, HS, T * HS, scratch, HS, T * HS, &zero, datt, T, T * T, B * NH));\n    // backward into dv\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, HS, T, T, &one, scratch, HS, T * HS, att, T, T * T, &zero, dv, HS, T * HS, B * NH));\n    // backward into preatt\n    int hs = C / NH; // head size\n    float scale = 1.0f / sqrtf(hs);\n    softmax_backward_kernel<<<dim3(T / 4, B * NH), 256>>>(dpreatt, datt, att, B, T, C, scale);\n    cudaCheck(hipGetLastError());\n    // backward into q\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, HS, T, T, &one, k, HS, T * HS, dpreatt, T, T * T, &zero, dq, HS, T * HS, B * NH));\n    // backward into k\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, HS, T, T, &one, q, HS, T * HS, dpreatt, T, T * T, &zero, dk, HS, T * HS, B * NH));\n    // backward into inp\n    num_blocks = ceil_div(B * NH * T * HS, block_size);\n    permute_kernel_backward<<<num_blocks, block_size>>>(dinp, dq, dk, dv, B, T, NH, HS);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Attention layer, including groupnorm, qkv projection, and residual stream\n\n#define NUM_ATT_PARAM_TENSORS 6\ntypedef struct {\n    float* gn_w; // C\n    float* gn_b; // C\n    float* qkv_w; // 3xC, C\n    float* qkv_b; // 3xC\n    float* proj_w; // C, C\n    float* proj_b; // C\n    size_t param_sizes[NUM_ATT_PARAM_TENSORS];\n    size_t n_params;\n} AttentionParams;\n\n#define NUM_ATT_ACT_TENSORS 12 // not counting input, which will reuse output memory from previous layer\ntypedef struct {\n    float* gn; // B, C, H, W\n    float* gn_mean; // B, n_groups\n    float* gn_rstd; // B, n_groups\n    float* perm1; // B, HxW, C\n    float* qkv1; // B, HxW, 3xC\n    float* qkv2; // B, HxW, 3xC\n    float* preatt; // B, NH, T, T\n    float* att; // B, NH, T, T\n    float* att_out; // B, HxW, C\n    float* proj; // B, HxW, C\n    float* perm2; // B, C, H, W\n    float* add; // B, C, H, W\n    size_t act_sizes[NUM_ATT_ACT_TENSORS];\n    size_t n_acts;\n    float* input; // B, C, H, W\n} AttentionActs;\n\n#define NUM_ATT_BACKWARD_ACTS_TENSORS 7  // dinp will use input memory\ntypedef struct {\n    float* buf1_BCHW; // B, C, H, W\n    float* buf2_BCHW; // B, C, H, W\n    float* buf_B3CHW; // B, 3xC, H, W\n    float* dqkvr; // B, HxW, 3xC\n    float* dpreatt; // B, NH, T, T\n    float* datt; // B, NH, T, T\n    float* dout; // B, C, H, W\n    float* dinp; // B, C, H, W\n    size_t back_sizes[NUM_ATT_BACKWARD_ACTS_TENSORS];\n    size_t n_backs;\n} AttentionBackwardActs;\n\nvoid attention_block_count_params(AttentionParams* params, int C) {\n    size_t *param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn_w\n    param_sizes[1] = C; // gn_b\n    param_sizes[2] = 3 * C * C; // qkv_w\n    param_sizes[3] = 3 * C; // qkv_b\n    param_sizes[4] = C * C; // proj_w\n    param_sizes[5] = C; // proj_b\n\n    size_t num_parameters = 0;\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\nvoid attention_block_count_acts(\n    AttentionActs* acts,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    int C3 = 3 * C;\n    int T = H * W;\n    int NH = C / HS;\n    size_t *act_sizes = acts->act_sizes;\n    act_sizes[0] = B * C * H * W; // gn\n    act_sizes[1] = B * gn_n_groups; // gn_mean\n    act_sizes[2] = B * gn_n_groups; // gn_rstd\n    act_sizes[3] = B * T * C; // perm1\n    act_sizes[4] = B * T * C3; // qkv1\n    act_sizes[5] = B * T * C3; // qkv2\n    act_sizes[6] = B * NH * T * T; // preatt\n    act_sizes[7] = B * NH * T * T; // att\n    act_sizes[8] = B * T * C; // att_out\n    act_sizes[9] = B * T * C; // proj\n    act_sizes[10] = B * C * H * W; // perm2\n    act_sizes[11] = B * C * H * W; // add\n\n    size_t num_acts_params = 0;\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        num_acts_params += act_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\nvoid set_attention_params_pointers(\n    AttentionParams* params,\n    float* params_memory\n) {\n    float** ptrs[] = {\n        &params->gn_w,\n        &params->gn_b,\n        &params->qkv_w,\n        &params->qkv_b,\n        &params->proj_w,\n        &params->proj_b\n    };\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        *(ptrs[i]) = params_memory;\n        params_memory += params->param_sizes[i];\n    }\n}\n\nvoid set_attention_acts_pointers(\n    AttentionActs* acts,\n    float* acts_memory\n) {\n    float** ptrs[] = {\n        &acts->gn,\n        &acts->gn_mean,\n        &acts->gn_rstd,\n        &acts->perm1,\n        &acts->qkv1,\n        &acts->qkv2,\n        &acts->preatt,\n        &acts->att,\n        &acts->att_out,\n        &acts->proj,\n        &acts->perm2,\n        &acts->add\n    };\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        *(ptrs[i]) = acts_memory;\n        acts_memory += acts->act_sizes[i];\n    }\n}\n\n// ----------------------------------------------------\n// kernels\n\n// this is an opportunity for improvement:\n// rewrite the attention layer so we don't need to permute the input\n__global__ void attention_permute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, C, H, W to B, HxW, C\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[b * C * img_size + h * C * W + w * C + c] = inp[idx];\n}\n\nvoid attention_permute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_permute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n__global__ void attention_unpermute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, HxW, C to B, C, H, W\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[idx] = inp[b * C * img_size + h * C * W + w * C + c];\n}\nvoid attention_unpermute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_unpermute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\nvoid attention_block_forward\n(\n    hipblasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts\n) {\n    // apply groupnorm\n    groupnorm_forward(acts->input, params->gn_w, params->gn_b, acts->gn, acts->gn_mean, acts->gn_rstd, B, C, H, W, gn_n_groups);\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(acts->gn, acts->perm1, B, C, H, W, block_size);\n    // get qkv\n    matmul_forward2(cublas_handle, acts->qkv1, acts->perm1, params->qkv_w, params->qkv_b, B * H * W, C, 3 * C, block_size);\n    // apply attention\n    attention_forward1(cublas_handle, acts->att_out, acts->qkv2, acts->preatt, acts->att, acts->qkv1, B, H * W, C, C / HS, block_size);\n    // attention projection\n    matmul_forward2(cublas_handle, acts->proj, acts->att_out, params->proj_w, params->proj_b, B * H * W, C, C, block_size);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(acts->proj, acts->perm2, B, C, H, W, block_size);\n    // add back residual stream\n    add_forward(acts->input, acts->perm2, acts->add, B * C * H * W, block_size);\n}\n\nvoid attention_block_backward\n(\n    hipblasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts,\n    AttentionBackwardActs* back_acts, AttentionParams* grads\n) {\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(back_acts->dout, back_acts->buf1_BCHW, B, C, H, W, block_size);\n    // backward through projection\n    matmul_backward1(cublas_handle, back_acts->buf2_BCHW, grads->proj_w, grads->proj_b, back_acts->buf1_BCHW, acts->att_out, params->proj_w, B * H * W, C, C);\n    // backward through attention\n    attention_backward(cublas_handle, back_acts->buf_B3CHW, back_acts->dqkvr, back_acts->dpreatt, back_acts->datt, back_acts->buf1_BCHW, back_acts->buf2_BCHW, acts->qkv2, acts->att, B, H * W, C, C / HS);\n    // backward through qkv\n    matmul_backward1(cublas_handle, back_acts->buf1_BCHW, grads->qkv_w, grads->qkv_b, back_acts->buf_B3CHW, acts->perm1, params->qkv_w, B * H * W, C, 3 * C);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(back_acts->buf1_BCHW, back_acts->buf2_BCHW, B, C, H, W, block_size);\n    // backward through groupnorm\n    groupnorm_backward(back_acts->buf2_BCHW, acts->input, acts->gn_mean, acts->gn_rstd, params->gn_w, back_acts->buf1_BCHW, grads->gn_w, grads->gn_b, B, C, H, W, gn_n_groups);\n    // add residual gradient\n    add_forward(back_acts->buf1_BCHW, back_acts->dout, back_acts->dinp, B * C * H * W, block_size);\n}\n\n// ------------------------------------------------------------------------------------------------\n// MSE loss\n// this will only be called with one block\n__global__ void mse_forward_kernel(\n    const float* inp, const float* y, float* loss, int N\n) {\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    __shared__ float shared_sum[32]; // max 32 warps\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        float diff = inp[i] - y[i];\n        thread_sum += diff * diff;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n    \n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        loss[0] = block_sum / N;\n    }\n}\n\nvoid mse_forward(const float* inp, const float* y, float* loss, int N, int block_size) {\n    mse_forward_kernel<<<1, block_size>>>(inp, y, loss, N);\n}\n\n// not calculating dy in backward\n// note that backward for MSE doesn't require the loss value\n__global__ void mse_backward_kernel(\n    const float* inp, const float* y, float* dinp, int N\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < N) {\n        float x = inp[idx];\n        dinp[idx] = 2.0f * (x - y[idx]) / N;\n    }\n}\n\nvoid mse_backward(const float* inp, const float* y, float* dinp, int N, int block_size) {\n    int n_blocks = ceil_div(N, block_size);\n    mse_backward_kernel<<<n_blocks, block_size>>>(inp, y, dinp, N);\n}\n\n// ------------------------------------------------------------------------------------------------\n// DataLoader\n\ntypedef struct {\n    int B;\n    int C_in;\n    int H;\n    int W;\n    int img_size;\n    int n_imgs;\n    FILE* img_file;\n    long cur_position;\n    long file_size;\n    long header_offset;\n    long num_batches;\n    float* input;\n} DataLoader;\n\nvoid dataloader_init(DataLoader* loader, const char* filename, int B) {\n    loader->B = B;\n\n    loader->img_file = fopenCheck(filename, \"rb\");\n    int data_header[256];\n    loader->header_offset = 256 * sizeof(int);\n    freadCheck(data_header, sizeof(int), 256, loader->img_file);\n    if (data_header[0] != 20240620) {\n        fprintf(stderr, \"Invalid magic data file\\n\");\n        exit(1);\n    }\n    loader->n_imgs = data_header[1];\n    loader->C_in = data_header[2];\n    loader->H = data_header[3];\n    loader->W = data_header[4];\n    loader->img_size = loader->C_in * loader->H * loader->W;\n\n    // determine file size\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    fseekCheck(loader->img_file, 0, SEEK_END);\n    loader->file_size = ftell(loader->img_file);\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    assert((loader->file_size - loader->header_offset) / (loader->img_size * sizeof(float)) == loader->n_imgs);\n    \n    loader->cur_position = loader->header_offset;\n    loader->num_batches = loader->n_imgs / B;\n\n    // allocate cuda CPU pinned memory for inputs\n    hipHostMalloc((void**)&loader->input, B * loader->img_size * sizeof(float));\n}\n\nvoid dataloader_reset(DataLoader* loader) {\n    loader->cur_position = loader->header_offset;\n}\n\nvoid dataloader_next_batch(DataLoader* loader) {\n    int B = loader->B;\n    int img_size = loader->img_size;\n    if (loader->cur_position + (B * img_size * sizeof(float)) > loader->file_size) {\n        dataloader_reset(loader);\n    }\n    fseekCheck(loader->img_file, loader->cur_position, SEEK_SET);\n    freadCheck(loader->input, sizeof(float), B * img_size, loader->img_file);\n    loader->cur_position += B * img_size * sizeof(float);\n}\n\nvoid dataloader_free(DataLoader* loader) {\n    fcloseCheck(loader->img_file);\n    hipHostFree(loader->input);\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// Diffusion\ntypedef struct {\n    int B;\n    int img_size;\n    int max_period;\n    float* betas; // (max_period,)\n    float* sqrt_alphas_cumprod; // (max_period,)\n    float* sqrt_one_minus_alphas_cumprod; // (max_period,)\n    int block_size;\n    hiprandState* curand_states;\n} GaussianDiffusion;\n\n__global__ void init_curand_states(hiprandState *state, unsigned long seed, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N) {\n        hiprand_init(seed, idx, 0, &state[idx]);\n    }\n}\n\nvoid diffusion_init(\n    GaussianDiffusion* diffusion, int B, int C_in, int H, int W, int max_period, int block_size = 512,\n    unsigned long seed = 0, float beta_start = 1e-4, float beta_end = 0.02f\n) {\n    diffusion->B = B;\n    diffusion->max_period = max_period;\n    diffusion->block_size = block_size;\n    diffusion->img_size = C_in * H * W;\n\n    float scale = 1000.0f / max_period;\n    beta_start *= scale;\n    beta_end *= scale;\n\n    // calculate betas and alphas on host then move to device\n    float* betas = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_one_minus_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n\n    float alpha_cumprod = 1.0f;\n    for (int t = 1; t <= max_period; t++) {\n        // linear interpolate for beta\n        betas[t - 1] = (beta_start * (max_period - t) + beta_end * (t - 1)) / (max_period - 1);\n        alpha_cumprod *= 1.0f - betas[t - 1];\n        sqrt_alphas_cumprod[t - 1] = sqrtf(alpha_cumprod);\n        sqrt_one_minus_alphas_cumprod[t - 1] = sqrtf(1.0f - alpha_cumprod);\n    }\n\n    float *d_betas, *d_sqrt_alphas_cumprod, *d_sqrt_one_minus_alphas_cumprod;\n    cudaCheck(hipMalloc(&d_betas, max_period * sizeof(float)));\n    cudaCheck(hipMalloc(&d_sqrt_alphas_cumprod, max_period * sizeof(float)));\n    cudaCheck(hipMalloc(&d_sqrt_one_minus_alphas_cumprod, max_period * sizeof(float)));\n\n    cudaCheck(hipMemcpy(d_betas, betas, max_period * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_sqrt_alphas_cumprod, sqrt_alphas_cumprod, max_period * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_sqrt_one_minus_alphas_cumprod, sqrt_one_minus_alphas_cumprod, max_period * sizeof(float), hipMemcpyHostToDevice));\n\n    free(betas);\n    free(sqrt_alphas_cumprod);\n    free(sqrt_one_minus_alphas_cumprod);\n\n    diffusion->betas = d_betas;\n    diffusion->sqrt_alphas_cumprod = d_sqrt_alphas_cumprod;\n    diffusion->sqrt_one_minus_alphas_cumprod = d_sqrt_one_minus_alphas_cumprod;\n\n    // init curand states for sampling noise of the size of one batch of inputs\n    if (diffusion->curand_states == nullptr) {\n        printf(\"initializing hiprand states\\n\");\n        int total_states = B * C_in * H * W;\n        int n_blocks = ceil_div(total_states, block_size);\n        cudaCheck(hipMalloc(&diffusion->curand_states, total_states * sizeof(hiprandState)));\n        init_curand_states<<<n_blocks, block_size>>>(diffusion->curand_states, seed, total_states);\n    }\n}\n\n// fill BxN noise array with standard gaussians\n__global__ void diffusion_draw_normal_kernel(\n    float* noise, int B, int N, hiprandState* curand_states\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        noise[idx] = hiprand_normal(&curand_states[idx]);\n    }\n}\n\nvoid diffusion_draw_normal(\n    GaussianDiffusion *diffusion,\n    float* noise\n) {\n    int B = diffusion->B;\n    int img_size = diffusion->img_size;\n    int total_states = B * img_size;\n    int n_blocks = ceil_div(total_states, diffusion->block_size);\n    diffusion_draw_normal_kernel<<<n_blocks, diffusion->block_size>>>(noise, B, img_size, diffusion->curand_states);\n}\n\n// given input x of shape (B, N) and timesteps of shape (B,)\n// replace each x entry by the result of flowing forward by diffusion for time t\n// with randomness coming from noise\n__global__ void diffusion_forward_by_t_kernel(\n    float* x, float* timesteps, float* noise,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    int B, int N, hiprandState* curand_states\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        int b = idx / N;\n        float x_val = x[idx];\n        float noise_val = noise[idx];\n        int t = (int)timesteps[b];\n        float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n        float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n        x[idx] = x_val * sqrt_alpha_t + noise_val * sqrt_one_minus_alpha_t;\n    }\n}\n\nvoid diffusion_forward_by_t(\n    GaussianDiffusion* diffusion,\n    float* x, float* timesteps, float* noise\n) {\n    int B = diffusion->B;\n    int img_size = diffusion->img_size;\n    int block_size = diffusion->block_size;\n    int total_states = B * img_size;\n    int n_blocks = ceil_div(total_states, block_size);\n    diffusion_forward_by_t_kernel<<<n_blocks, block_size>>>(\n        x, timesteps, noise, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, B, img_size, diffusion->curand_states\n    );\n}\n\nvoid diffusion_free(GaussianDiffusion* diffusion) {\n    cudaCheck(hipFree(diffusion->betas));\n    cudaCheck(hipFree(diffusion->sqrt_alphas_cumprod));\n    cudaCheck(hipFree(diffusion->sqrt_one_minus_alphas_cumprod));\n    cudaCheck(hipFree(diffusion->curand_states));\n}\n\n// one block with B threads\n__global__ void sample_timestep_kernel(\n    float* timesteps, int B,\n    hiprandState* curand_states, int max_period\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B) {\n        int t = hiprand(&curand_states[idx]) % max_period;\n        timesteps[idx] = (float)t;\n    }\n}\n\n// samples B timesteps in range [0, max_period), convert them to floats and\n// store in timesteps\nvoid sample_timesteps(GaussianDiffusion *diffusion, float* timesteps) {\n    sample_timestep_kernel<<<1, diffusion->B>>>(timesteps, diffusion->B, diffusion->curand_states, diffusion->max_period);\n}\n\n// ------------------------------------------------------------------------------------------------\n// timestep embedding states\ntypedef struct {\n    float* freqs;\n    int half_dim;\n    int B;\n    int max_period;\n} TimestepEmbedding;\n\n__global__ void fill_freqs_kernel(float* freqs, int half_dim, int max_period) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < half_dim) {\n        float log_period = logf(max_period);\n        float logit = -log_period * idx / half_dim;\n        freqs[idx] = expf(logit);\n    }\n}\n\nvoid init_timestep_embedding(TimestepEmbedding* emb, int dim, int B, int max_period) {\n    assert(dim % 2 == 0);\n    int half = dim / 2;\n    emb->half_dim = half;\n    emb->max_period = max_period;\n    emb->B = B;\n    cudaCheck(hipMalloc(&emb->freqs, half * sizeof(float)));\n    // fill freq values\n    int block_size = 512;\n    int n_blocks = ceil_div(half, block_size);\n    fill_freqs_kernel<<<n_blocks, block_size>>>(emb->freqs, half, max_period);\n}\n\nvoid free_timestep_embedding(TimestepEmbedding* emb) {\n    cudaCheck(hipFree(emb->freqs));\n}\n\n__global__ void fill_embeddings_kernel(\n    const float* timesteps, const float* freqs, float* embeddings, int half, int B\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < B * half) {\n        int i = idx / half;\n        int j = idx % half;\n        float arg = timesteps[i] * freqs[j];\n        embeddings[i * 2 * half + j] = cosf(arg);\n        embeddings[i * 2 * half + j + half] = sinf(arg);\n    }\n}\n\n// timesteps is a (B,) array of timesteps that are integers but saved as floats\n// out is a (B, dim) array\nvoid get_timestep_embeddings(\n    TimestepEmbedding* emb, const float* timesteps, float* out\n) {\n    int block_size = 512;\n    int n_blocks = ceil_div(emb->half_dim * emb->B, block_size);\n    fill_embeddings_kernel<<<n_blocks, block_size>>>(timesteps, emb->freqs, out, emb->half_dim, emb->B);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet init\n\ntypedef struct {\n    int C_in;\n    int C_model;\n    int C_time;\n    int C_out;\n    int B;\n    int H;\n    int W;\n    int gn_n_groups;\n    int HS;\n    int block_size;\n    int n_layers;\n    int n_levels;\n    int n_res;\n    int att_start_level;\n    int* channel_mult;\n    int max_period;\n} UnetConfig;\n\n// we just need a single set of buffers that are shared for all the backward pass activations\n#define NUM_SKIP_CONNECTIONS 12 // (n_res + 1) * n_levels\n#define NUM_BACK_BUFS 10\n#define UNET_NUM_BACK_ACTS 22\ntypedef struct {\n    float* dinp_buf1; // used to store the input gradient, which will be reused as dout for the previous layer\n    float* dinp_buf2;\n    float* buf1_BCHW; // NOTE: first 4 buffers will have  max B * C * H * W size\n    float* buf2_BCHW;\n    float* buf3_BCHW;\n    float* buf4_BCHW;\n    float* buf1_B3CHW; // NOTE: last 2 buffers will have max B * 3 * C * H * W size for attention layers\n    float* buf2_B3CHW; // so max of H * W is only over dimensions that have attention\n    float* demb_buf1; // used to store cumulative gradients w.r.t. timestep embeddings\n    float* demb_buf2;\n    float* skip_dinps[NUM_SKIP_CONNECTIONS];\n} UnetBackActs;\n\n// This is used as a block to store all the params / grads / acts for all the layers\ntypedef struct {\n    float* mem_ptr;\n    size_t total_size;\n    int n_layers;\n    void** layer_ptrs;\n    size_t* layer_sizes;\n} UnetBlock;\n\ntypedef struct {\n    float* back_mem;\n    UnetBackActs back_acts;\n    size_t back_sizes[UNET_NUM_BACK_ACTS];\n    size_t n_backs;\n} UnetBackBlock;\n\ntypedef struct {\n    UnetConfig config;\n    float* input;\n    float* timestep_emb; // this is the already computed embeddings of shape (B, C_model)\n    UnetBlock params;\n    UnetBlock grads;\n    UnetBlock acts;\n    UnetBackBlock back;\n    float* skip_acts[NUM_SKIP_CONNECTIONS]; // pointers to skip connetion activations\n    // the next two are for debug purposes only, just to keep track of the buffer that contains the grad w.r.t. inputs\n    float* dinp;\n    // model output\n    float* output;\n    // targets and losses\n    float* target;\n    float mean_loss;\n    float* gpu_loss;\n    // buffers for AdamW\n    float* m_memory;\n    float* v_memory;\n    TimestepEmbedding time_emb_state;\n} Unet;\n\nvoid unet_init(Unet* unet) {\n    unet->input = nullptr;\n    unet->timestep_emb = nullptr;\n    unet->params.mem_ptr = nullptr;\n    unet->grads.mem_ptr = nullptr;\n    unet->acts.mem_ptr = nullptr;\n    unet->back.back_mem = nullptr;\n    unet->dinp = nullptr;\n    unet->output = nullptr;\n    unet->target = nullptr;\n    unet->mean_loss = -1.0f;\n    unet->gpu_loss = nullptr;\n    unet->m_memory = nullptr;\n    unet->v_memory = nullptr;\n\n    init_timestep_embedding(&unet->time_emb_state, unet->config.C_model, unet->config.B, unet->config.max_period);\n}\n\n\ntemplate <typename T>\nvoid free_not_null(T** ptr) {\n    if (*ptr != nullptr) {\n        free(*ptr);\n        *ptr = nullptr;\n    }\n}\n\nvoid free_unet_block(UnetBlock* params) {\n    free_not_null(&params->layer_sizes);\n    for (int l = 0; l < params->n_layers; l++) {\n        free_not_null(&(params->layer_ptrs[l]));\n    }\n    free_not_null(&params->layer_ptrs);\n    cudaCheck(hipFree(params->mem_ptr));\n}\n\nvoid free_unet(Unet* unet) {\n    //free(unet->config.channel_mult);\n\n    cudaCheck(hipFree(unet->input));\n    cudaCheck(hipFree(unet->timestep_emb));\n    cudaCheck(hipFree(unet->gpu_loss));\n    cudaCheck(hipFree(unet->m_memory));\n    cudaCheck(hipFree(unet->v_memory));\n\n    free_unet_block(&(unet->params));\n    free_unet_block(&(unet->grads));\n    free_unet_block(&(unet->acts));\n\n    cudaCheck(hipFree(unet->back.back_mem));\n    free_timestep_embedding(&unet->time_emb_state);\n}\n\nvoid unet_count_layers(UnetConfig* config) {\n    int n_layers = 0;\n    int n_res = config->n_res;\n    int n_levels = config->n_levels;\n    int att_start_level = config->att_start_level;\n\n    // timestep emb\n    n_layers += 3;\n\n    // initial conv\n    n_layers += 1;\n\n    // add downsample resblock layers\n    n_layers += n_res * n_levels;\n\n    // add downsample attention\n    n_layers += n_res * (n_levels - att_start_level);\n\n    // add avgpool layers\n    n_layers += n_levels - 1;\n\n    // add middle layers\n    n_layers += 3;\n\n    // add upsample concat layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample resblock layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample attention layers\n    n_layers += (n_res + 1) * (n_levels - att_start_level);\n\n    // add upsample layers\n    n_layers += n_levels - 1;\n\n    // output layers\n    n_layers += 3;\n\n    config->n_layers = n_layers;\n}\n\n// ------------------------------------------------------------------------------------------------\n// allocate memory for structs with info for each layer and count params\n\nint make_linear_params_ptrs(\n    UnetBlock* params,\n    int layer,\n    int C_in, int C_out\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearParams));\n    params->layer_sizes[layer] = linear_count_params(C_in, C_out);\n    return layer + 1;\n}\n\nint make_silu_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_convk3_params_ptrs(\n    UnetBlock* params, int layer,\n    int C_in, int OC, int B, int H, int W\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Params));\n    params->layer_sizes[layer] = convk3_count_params(C_in, OC);\n    return layer + 1;\n}\n\nint make_resblock_params_ptrs(\n    UnetBlock* params, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockParameters));\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    resblock_count_params(resblock_params, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    params->layer_sizes[layer] = resblock_params->n_params;\n    return layer + 1;\n}\n\nint make_avgpool_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_attention_block_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionParams));\n    AttentionParams* attention_params = (AttentionParams*)params->layer_ptrs[layer];\n    attention_block_count_params(attention_params, C);\n    params->layer_sizes[layer] = attention_params->n_params;\n    return layer + 1;\n}\n\nint make_concat_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_upsample_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_groupnorm_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormParams));\n    params->layer_sizes[layer] = C * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_params(UnetConfig* config, UnetBlock* params) {\n    assert (config->n_layers > 0);\n\n    params->n_layers = config->n_layers;\n    params->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    params->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_params_ptrs(params, layer, C_model, C_time);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_linear_params_ptrs(params, layer, C_time, C_time);\n    int OC = C_model;\n    layer = make_convk3_params_ptrs(params, layer, C_in, OC, B, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_params_ptrs(params, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_params_ptrs(params, layer);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    // middle layers\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_params_ptrs(params, layer, C);\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards: concat then resblock\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = C_model * mult;\n        for (int r = 0; r < n_res + 1; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = C_model * prev_mult;\n            }\n            OC = C_model * mult;\n            layer = make_concat_params_ptrs(params, layer);\n            layer = make_resblock_params_ptrs(params, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_params_ptrs(params, layer);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    layer = make_groupnorm_params_ptrs(params, layer, C);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_convk3_params_ptrs(params, layer, C, C_out, B, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += params->layer_sizes[i];\n    }\n    params->total_size = total_size;\n}\n\n// ------------------------------------------------------------------------------------------------\n// setup Unet activations pointers\nint make_linear_acts_ptrs(\n    UnetBlock* acts,\n    int layer,\n    int B, int C_out\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearActs));\n    // acts buffers only hold outputs\n    acts->layer_sizes[layer] = B * C_out;\n    return layer + 1;\n}\n\nint make_silu_acts_ptrs(UnetBlock* acts, int layer, int N) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(SiluActs));\n    acts->layer_sizes[layer] = N;\n    return layer + 1;\n}\n\nint make_convk3_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int OC, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Acts));\n    acts->layer_sizes[layer] = B * OC * H * W;\n    return layer + 1;\n}\n\nint make_resblock_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockActivations));\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    resblock_count_acts(resblock_acts, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    acts->layer_sizes[layer] = resblock_acts->n_acts;\n    return layer + 1;\n}\n\nint make_avgpool_acts_ptrs(UnetBlock* acts, int layer, int B, int C, int H, int W) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AvgpoolActs));\n    acts->layer_sizes[layer] = B * C * H/2 * W/2;\n    return layer + 1;\n}\n\nint make_attention_block_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionActs));\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    attention_block_count_acts(att_acts, B, C, H, W, gn_n_groups, HS);\n    acts->layer_sizes[layer] = att_acts->n_acts;\n    return layer + 1;\n}\n\nint make_concat_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C1, int C2, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConcatChannelActs));\n    acts->layer_sizes[layer] = B * (C1 + C2) * H * W;\n    return layer + 1;\n}\n\nint make_upsample_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(UpsampleActs));\n    acts->layer_sizes[layer] = B * C * H * 2 * W * 2;\n    return layer + 1;\n}\n\nint make_groupnorm_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormActs));\n    // out (B, C, H, W), mean and rstd (B, gn_n_groups)\n    acts->layer_sizes[layer] = B * C * H * W + B * gn_n_groups * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_acts(UnetConfig* config, UnetBlock* acts) {\n    assert (config->n_layers > 0);\n\n    acts->n_layers = config->n_layers;\n    acts->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    acts->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int HS = config->HS;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    layer = make_silu_acts_ptrs(acts, layer, B * C_time);\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    int OC = C_model;\n    layer = make_convk3_acts_ptrs(acts, layer, B, OC, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_acts_ptrs(acts, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_acts_ptrs(acts, layer, B, C, H, W);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = prev_mult * C_model;\n            }\n            layer = make_concat_acts_ptrs(acts, layer, B, C, C_skip, H, W);\n            OC = mult * C_model;\n            layer = make_resblock_acts_ptrs(acts, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_acts_ptrs(acts, layer, B, C, H, W);\n            H *= 2;\n            W *= 2;\n        }\n    }\n    layer = make_groupnorm_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups);\n    layer = make_silu_acts_ptrs(acts, layer, B * C * H * W);\n    layer = make_convk3_acts_ptrs(acts, layer, B, C_out, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += acts->layer_sizes[i];\n    }\n    acts->total_size = total_size;\n}\n\nvoid unet_setup_and_count_backs(UnetConfig* config, UnetBackBlock* back) {\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int H = config->H;\n    int W = config->W;\n\n    size_t max_BCHW = 3 * B * C_model * H * W;\n    size_t max_B3CHW = 2 * B * 3 * C_model * H * W;\n    back->back_sizes[0] = max_BCHW;\n    back->back_sizes[1] = max_BCHW;\n    back->back_sizes[2] = max_BCHW;\n    back->back_sizes[3] = max_BCHW;\n    back->back_sizes[4] = max_BCHW;\n    back->back_sizes[5] = max_BCHW;\n    back->back_sizes[6] = max_B3CHW;\n    back->back_sizes[7] = max_B3CHW;\n    back->back_sizes[8] = B * C_time;\n    back->back_sizes[9] = B * C_time;\n\n    // fill the sizes of the skip connection backward acts\n    int C = C_model;\n    for (int l = 0; l < config->n_levels; l++) {\n        for (int r = 0; r < (config->n_res + 1); r++) {\n            int s = l * 3 + r + NUM_BACK_BUFS;\n            assert(s < UNET_NUM_BACK_ACTS);\n            back->back_sizes[s] = B * C * H * W;\n            int mult = config->channel_mult[l];\n            C = C_model * mult;\n            if (l > 0 && r == 0) {\n                H /= 2;\n                W /= 2;\n            }\n        }\n    }\n\n    size_t n_backs = 0;\n    for (int i = 0; i < UNET_NUM_BACK_ACTS; i++) {\n        n_backs += back->back_sizes[i];\n    }\n    back->n_backs = n_backs;\n}\n\n\nvoid unet_make_ptrs_and_count_memory(Unet* unet)\n{\n    unet_setup_and_count_params(&(unet->config), &(unet->params));\n    unet_setup_and_count_params(&(unet->config), &(unet->grads));\n    unet_setup_and_count_acts(&(unet->config), &(unet->acts));\n    unet_setup_and_count_backs(&(unet->config), &(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// malloc unet memory and set pointers\ntypedef struct {\n    float* input;\n    float* prev_out;\n    int layer;\n    float* emb;\n    float* emb_cum; // cumulative demb\n    float* mem_ptr;\n    int skip_idx;\n} LayerInfo;\n\n\nvoid set_linear_params_ptrs(\n    UnetBlock* params,\n    LayerInfo* l_info,\n    int C_in, int C_out\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)params->layer_ptrs[layer];\n    linear_set_param_ptrs(linear_params, l_info->mem_ptr, C_in, C_out);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_convk3_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C_in, int OC) {\n    int layer = l_info->layer;\n    ConvK3Params* convk3_params = (ConvK3Params*)params->layer_ptrs[layer];\n    convk3_set_param_ptrs(convk3_params, l_info->mem_ptr, C_in, OC);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    set_resblock_params_ptrs(C, OC, resblock_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_attention_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)params->layer_ptrs[layer];\n    set_attention_params_pointers(att_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_params = (GroupNormParams*)params->layer_ptrs[layer];\n    gn_set_param_ptrs(gn_params, l_info->mem_ptr, C);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\n\nvoid point_unet_params(UnetConfig* config, UnetBlock* params) {\n    // common constants\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = params->mem_ptr;\n    set_linear_params_ptrs(params, &l_info, C_model, C_time);\n    l_info.layer++; // skip silu layer\n    set_linear_params_ptrs(params, &l_info, C_time, C_time);\n    int OC = C_model;\n    set_convk3_params_ptrs(params, &l_info, C_in, OC);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_params_ptrs(params, &l_info, C, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l < n_levels - 1) {\n            l_info.layer++; // skip avgpool layer\n        }\n    }\n\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n    set_attention_layer_params_ptrs(params, &l_info);\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n\n    // upward: skip one layer for concat\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            l_info.layer++;\n            OC = mult * C_model;\n            set_resblock_layer_params_ptrs(params, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l > 0) {\n            l_info.layer++; // upsample\n        }\n    }\n\n    set_groupnorm_layer_params_ptrs(params, &l_info, C);\n    l_info.layer++; // silu\n    set_convk3_params_ptrs(params, &l_info, C, C_out);\n}\n\nvoid set_linear_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    LinearActs* linear_acts = (LinearActs*)acts->layer_ptrs[layer];\n    linear_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    linear_acts->inp = l_info->input;\n    l_info->input = linear_acts->out;\n    l_info->layer++;\n}\n\nvoid set_silu_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    SiluActs* silu_acts = (SiluActs*)acts->layer_ptrs[layer];\n    silu_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    silu_acts->x = l_info->input;\n    l_info->input = silu_acts->out;\n    l_info->layer++;\n}\n\nvoid set_convk3_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    ConvK3Acts* convk3_acts = (ConvK3Acts*)acts->layer_ptrs[layer];\n    convk3_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    convk3_acts->inp = l_info->input;\n    l_info->input = convk3_acts->out;\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    set_resblock_acts_ptrs(C, OC, resblock_acts, l_info->mem_ptr);\n    resblock_acts->input = l_info->input;\n    resblock_acts->emb = l_info->emb;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = resblock_acts->add2;\n    l_info->layer++;\n}\n\nvoid set_avgpool_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)acts->layer_ptrs[layer];\n    avgpool_acts->out = l_info->mem_ptr;\n    avgpool_acts->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = avgpool_acts->out;\n    l_info->layer++;\n}\n\nvoid set_attention_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    set_attention_acts_pointers(att_acts, l_info->mem_ptr);\n    att_acts->input = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = att_acts->add;\n    l_info->layer++;\n}\n\nvoid set_concat_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, float** skip_acts) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)acts->layer_ptrs[layer];\n    cat_acts->out = l_info->mem_ptr;\n    cat_acts->x1 = l_info->input;\n    cat_acts->x2 = skip_acts[l_info->skip_idx];\n\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = cat_acts->out;\n    l_info->layer++;\n    l_info->skip_idx--;\n}\n\nvoid set_upsample_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)acts->layer_ptrs[layer];\n    up_acts->x = l_info->input;\n    up_acts->out = l_info->mem_ptr;\n\n    l_info->mem_ptr += acts-> layer_sizes[layer];\n    l_info->input = up_acts->out;\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_acts_ptrs(\n    UnetBlock* acts, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormActs* gn_a = (GroupNormActs*)acts->layer_ptrs[layer];\n    gn_set_act_ptrs(gn_a, l_info->mem_ptr, B, C, H, W, gn_n_groups);\n    gn_a->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = gn_a->out;\n    l_info->layer++;\n}\n\nvoid point_unet_acts(UnetConfig* config, UnetBlock* acts, float** skip_acts, float* input, float* timestep_emb) {\n    // common constants\n    int B = config->B;\n    int C_model = config->C_model;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = acts->mem_ptr;\n\n    // timestep emb\n    l_info.input = timestep_emb;\n    set_linear_acts_ptrs(acts, &l_info);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_linear_acts_ptrs(acts, &l_info);\n    // point input to model input, and set outputu of the last linear layer as the embedding input\n    l_info.emb = l_info.input;\n    l_info.input = input;\n\n    int OC = C_model;\n    set_convk3_acts_ptrs(acts, &l_info);\n    int skip_idx = 0;\n    skip_acts[skip_idx] = l_info.input; // set the output of the previouse layer as input to the skip connection\n    skip_idx++;\n\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_acts_ptrs(acts, &l_info, C, OC);\n            if (l < att_start_level) {\n                // for the first 2 layers, the skip connection comes from the resblocks\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n        }\n        if (l < n_levels - 1) {\n            set_avgpool_layer_acts_ptrs(acts, &l_info);\n            skip_acts[skip_idx] = l_info.input;\n            skip_idx++;\n        }\n    }\n\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n    set_attention_layer_acts_ptrs(acts, &l_info);\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n\n    // upward: set concat\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            set_concat_layer_acts_ptrs(acts, &l_info, skip_acts);\n            OC = mult * C_model;\n            set_resblock_layer_acts_ptrs(acts, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n            }\n        }\n        if (l > 0) {\n            set_upsample_layer_acts_ptrs(acts, &l_info);\n        }\n    }\n    set_groupnorm_layer_acts_ptrs(acts, &l_info, B, C, H, W, gn_n_groups);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_convk3_acts_ptrs(acts, &l_info);\n}\n\nvoid set_unet_back_pointers(UnetBackBlock* back) {\n    UnetBackActs* back_acts = &(back->back_acts);\n    float* back_mem_ptr = back->back_mem;\n\n    float** ptrs[] = {\n        &(back_acts->dinp_buf1),\n        &(back_acts->dinp_buf2),\n        &(back_acts->buf1_BCHW),\n        &(back_acts->buf2_BCHW),\n        &(back_acts->buf3_BCHW),\n        &(back_acts->buf4_BCHW),\n        &(back_acts->buf1_B3CHW),\n        &(back_acts->buf2_B3CHW),\n        &(back_acts->demb_buf1),\n        &(back_acts->demb_buf2)\n    };\n\n    for (int i = 0; i < NUM_BACK_BUFS; i++) {\n        *(ptrs[i]) = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[i];\n    }\n\n    // point the back acts for skip connections\n    for (int s = 0; s < NUM_SKIP_CONNECTIONS; s++) {\n        back_acts->skip_dinps[s] = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[NUM_BACK_BUFS + s];\n    }\n}\n\nvoid malloc_and_point(Unet* unet) {\n    float *param_mem, *grad_mem, *act_mem, *back_mem;\n    cudaCheck(hipMalloc(&param_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&grad_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&act_mem, unet->acts.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&back_mem, unet->back.n_backs * sizeof(float)));\n\n    cudaCheck(hipMemset(param_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMemset(grad_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMemset(act_mem, 0, unet->acts.total_size * sizeof(float)));\n    cudaCheck(hipMemset(back_mem, 0, unet->back.n_backs * sizeof(float)));\n\n    // constants\n    int B = unet->config.B;\n    int C_in = unet->config.C_in;\n    int C_model = unet->config.C_model;\n    int H = unet->config.H;\n    int W = unet->config.W;\n\n    // malloc and point input and timestep emb\n    cudaCheck(hipMalloc(&(unet->input), B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&(unet->timestep_emb), B * C_model * sizeof(float)));\n    // malloc a nicely aligned buffer for the loss\n    cudaCheck(hipMalloc(&(unet->gpu_loss), 64 * sizeof(float)));\n\n    unet->params.mem_ptr = param_mem;\n    unet->grads.mem_ptr = grad_mem;\n    unet->acts.mem_ptr = act_mem;\n    unet->back.back_mem = back_mem;\n\n    point_unet_params(&(unet->config), &(unet->params));\n    point_unet_params(&(unet->config), &(unet->grads));\n    point_unet_acts(&(unet->config), &(unet->acts), unet->skip_acts, unet->input, unet->timestep_emb);\n    set_unet_back_pointers(&(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// forward pass\n\nvoid linear_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int N, int C, int OC, int block_size\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_forward2(cublas_handle, linear_acts->out, linear_acts->inp, linear_params->w, linear_params->b, N, C, OC, block_size);\n    l_info->layer++;\n}\n\nvoid silu_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_forward(acts->x, acts->out, N, block_size);\n    l_info->layer++;\n}\n\nvoid convk3_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    conv2d_k3_forward3(conv_acts->inp, conv_params->w, conv_params->b, conv_acts->out, B, C, OC, H, W);\n    l_info->layer++;\n}\nvoid resblock_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    resblock_forward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_acts);\n    l_info->layer++;\n}\n\nvoid avgpool_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)unet->acts.layer_ptrs[layer];\n    avgpool_2d_forward1(avgpool_acts->out, avgpool_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid attention_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts);\n    l_info->layer++;\n}\n\nvoid concat_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)unet->acts.layer_ptrs[layer];\n    concat_channel_forward(cat_acts->x1, cat_acts->x2, cat_acts->out, B, C1, C2, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid upsample_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)unet->acts.layer_ptrs[layer];\n    upsample_forward1(up_acts->out, up_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid groupnorm_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_forward(gn_a->x, gn_p->w, gn_p->b, gn_a->out, gn_a->mean, gn_a->rstd, B, C, H, W, gn_n_groups);\n    l_info->layer++;\n}\n\nvoid unet_forward(\n    hipblasHandle_t cublas_handle,\n    Unet* unet\n) {\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_model, C_time, block_size);\n    silu_layer_forward(unet, &l_info, B * C_time, block_size);\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_time, C_time, block_size);\n    int OC = C_model;\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config.channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size);\n            C = OC;\n            if (l >= att_start_level) {\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n            }\n        }\n        if (l < n_levels - 1) {\n            avgpool_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n\n    // upward\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int C_skip = C_model * config.channel_mult[l];\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            }\n            concat_layer_forward(unet, &l_info, B, C, C_skip, H, W, block_size);\n            OC = C_model * config.channel_mult[l];\n            resblock_layer_forward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            C = OC;\n            if (l >= att_start_level) {\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n            }\n        }\n        if (l > 0) {\n            upsample_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    groupnorm_layer_forward(unet, &l_info, B, C, H, W, gn_n_groups);\n    silu_layer_forward(unet, &l_info, B * C * H * W, block_size);\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n\n    ConvK3Acts* last_acts = (ConvK3Acts*)unet->acts.layer_ptrs[l_info.layer - 1];\n    unet->output = last_acts->out;\n    if (unet->target == nullptr) {\n        // don't have targets, not computing loss\n        unet->mean_loss = -1.0f;\n    } else {\n        mse_forward(unet->output, unet->target, unet->gpu_loss, B * C_out * H * W, block_size);\n        cudaCheck(hipMemcpy(&(unet->mean_loss), unet->gpu_loss, sizeof(float), hipMemcpyDeviceToHost));\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet backward\n\nvoid swap_layer_in_out_ptrs(LayerInfo* l_info) {\n    float* temp = l_info->input;\n    l_info->input = l_info->prev_out;\n    l_info->prev_out = temp;\n}\n\nvoid linear_layer_backward(\n    Unet* unet,\n    LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int N, int C, int OC\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearParams* linear_grads = (LinearParams*)unet->grads.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_backward1(cublas_handle, l_info->prev_out, linear_grads->w, linear_grads->b, l_info->input, linear_acts->inp, linear_params->w, N, C, OC);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid silu_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_backward(l_info->input, acts->x, l_info->prev_out, N, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid convk3_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * OC * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Params* conv_grads = (ConvK3Params*)unet->grads.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n    conv2d_k3_backward2(l_info->input, conv_acts->inp, conv_params->w, unet_back_acts->buf1_BCHW, unet_back_acts->buf2_BCHW, l_info->prev_out, conv_grads->w, conv_grads->b, B, C, OC, H, W);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid resblock_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * C_out * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockParameters* resblock_grads = (ResBlockParameters*)unet->grads.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    ResBlockBackwardActivations back_acts;\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n\n    // point back_acts to buffers\n    back_acts.buf_BCemb = unet_back_acts->buf1_BCHW;\n    back_acts.buf_BCHoWo = unet_back_acts->buf2_BCHW;\n    back_acts.buf1_BCHW = unet_back_acts->buf3_BCHW;\n    back_acts.buf2_BCHW = unet_back_acts->buf4_BCHW;\n    back_acts.dweight_buf = unet_back_acts->buf1_B3CHW;\n    back_acts.dbias_buf = unet_back_acts->buf2_B3CHW;\n    back_acts.dout = l_info->input;\n    back_acts.dx = l_info->prev_out;\n    back_acts.demb = l_info->emb;\n\n    resblock_backward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_grads, resblock_acts, &back_acts);\n    swap_layer_in_out_ptrs(l_info);\n\n    // add demb to cumulative demb\n    add_inplace_forward(l_info->emb, l_info->emb_cum, B * C_emb, block_size);\n    l_info->layer--;\n}\n\nvoid attention_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"att adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionParams* att_grads = (AttentionParams*)unet->grads.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    AttentionBackwardActs att_backs;\n    UnetBackActs* unet_backs = &(unet->back.back_acts);\n    att_backs.buf1_BCHW = unet_backs->buf1_BCHW;\n    att_backs.buf2_BCHW = unet_backs->buf2_BCHW;\n    att_backs.buf_B3CHW = unet_backs->buf1_B3CHW;\n    att_backs.dqkvr = unet_backs->buf2_B3CHW;\n    att_backs.dpreatt = unet_backs->buf3_BCHW;\n    att_backs.datt = unet_backs->buf4_BCHW;\n    att_backs.dout = l_info->input;\n    att_backs.dinp = l_info->prev_out;\n\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts, &att_backs, att_grads);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid concat_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n    //printf(\"concat skip %d\\n\", l_info->skip_idx);\n    concat_channel_backward(l_info->input, l_info->prev_out, skip_dinp, B, C1, C2, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n    l_info->skip_idx++;\n}\n\n\nvoid avgpool_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"avgpool adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H/2 * W/2, block_size);\n        l_info->skip_idx--;\n    }\n    avgpool_2d_backward1(l_info->input, l_info->prev_out, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid upsample_layer_backward(\n    Unet* unet, LayerInfo *l_info,\n    int B, int C,  int H, int W, int block_size\n) {\n    upsample_backward1(l_info->prev_out, l_info->input, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid groupnorm_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormParams* gn_g = (GroupNormParams*)unet->grads.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_backward(l_info->input, gn_a->x, gn_a->mean, gn_a->rstd, gn_p->w, l_info->prev_out, gn_g->w, gn_g->b, B, C, H, W, gn_n_groups);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid unet_backward(\n    hipblasHandle_t cublas_handle,\n    Unet* unet\n   \n) {\n    if (unet->mean_loss == -1.0f or unet->target == nullptr) {\n        printf(\"Error: must run forward with targets before calling backward\\n\");\n        exit(EXIT_FAILURE);\n    }\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    UnetBackActs* back_acts = &(unet->back.back_acts);\n    mse_backward(unet->output, unet->target, back_acts->dinp_buf1, B * C_out * H * W, block_size);\n\n    LayerInfo l_info;\n    l_info.layer = config.n_layers - 1;\n    l_info.input = back_acts->dinp_buf1;\n    l_info.prev_out = back_acts->dinp_buf2;\n    l_info.emb = back_acts->demb_buf1;\n    l_info.emb_cum = back_acts->demb_buf2;\n    l_info.skip_idx = 0;\n\n    int C;\n    int OC;\n    int C_skip;\n    C = C_model;\n    OC = C_model;\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n    silu_layer_backward(unet, &l_info, B * C * H * W, block_size);\n    groupnorm_layer_backward(unet, &l_info, B, C, H, W, gn_n_groups);\n\n    for (int l = 0; l < n_levels; l++) {\n        for (int r = n_res; r >= 0; r--) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            } else {\n                C_skip = C_model * config.channel_mult[l];\n            }\n            if (l >= att_start_level) {\n                attention_layer_backward(unet, &l_info, cublas_handle, B, OC, H, W, HS, gn_n_groups, block_size);\n            }\n            if (r == 0 && l < n_levels - 1) {\n                C = C_model * config.channel_mult[l + 1];\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            concat_layer_backward(unet, &l_info, B, C, C_skip, H, W, block_size);\n        }\n        if (l < n_levels - 1) {\n            H /= 2;\n            W /= 2;\n            upsample_layer_backward(unet, &l_info, B, C, H, W, block_size);\n            OC = C_model * config.channel_mult[l + 1];\n        }\n    }\n\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n\n    // we will need to change the backward for all the other blocks below to include the dout contribution from the skip connection.\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        if (l < n_levels - 1) {\n            H *= 2;\n            W *= 2;\n            avgpool_layer_backward(unet, &l_info, B, C, H, W, block_size, 1);\n        }\n        for (int r = 0; r < n_res; r++) {\n            if (l >= att_start_level) {\n                attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, 1);\n            }\n            int res_add_skip = (l < att_start_level) ? 1 : 0;\n            if (r == n_res - 1 && l > 0) {\n                int prev_mult = config.channel_mult[l - 1];\n                C = prev_mult * C_model;\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size, res_add_skip);\n            OC = C;\n        }\n    }\n\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size, 1);\n    unet->dinp = l_info.input;\n\n    // reset dout to point to emb_cum\n    l_info.input = l_info.emb_cum;\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_time, C_time);\n    silu_layer_backward(unet, &l_info, B * C_time, block_size);\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_model, C_time);\n}\n\n// ------------------------------------------------------------------------------------------------\n// zero grad and ADAMW\n\nvoid unet_zero_grad(Unet* unet) {\n    cudaCheck(hipMemset(unet->grads.mem_ptr, 0, unet->grads.total_size * sizeof(float)));\n    cudaCheck(hipMemset(unet->back.back_mem, 0, unet->back.n_backs * sizeof(float)));\n    //cudaCheck(cudaMemset(unet->acts.mem_ptr, 0, unet->acts.total_size * sizeof(float)));\n}\n\n// below copied from llm.c\n\n// Implements linear interpolation using only two floating-point operations (as opposed to three in a naive implementation).\n// Reference: https://developer.nvidia.com/blog/lerp-faster-cuda\n__device__ inline float lerp(float start, float end, float weight) {\n    return fma(weight, end, fma(-weight, start, start));\n}\n\n__global__ void adamw_kernel2(float* params_memory, float* grads_memory, float* m_memory, float* v_memory, long num_parameters,\n                              float learning_rate, float beta1, float beta2, float beta1_correction, float beta2_correction, float eps, float weight_decay) {\n   int i = blockIdx.x * blockDim.x + threadIdx.x;\n   if (i >= num_parameters) return;  // guard\n   float grad = grads_memory[i];\n   float m = m_memory[i];\n   float v = v_memory[i];\n   // update the first moment (momentum)\n   m = lerp(grad, m, beta1);\n   m_memory[i] = m;\n   // update the second moment (RMSprop)\n   v = lerp(grad * grad, v, beta2);\n   v_memory[i] = v;\n   m /= beta1_correction;  // m_hat\n   v /= beta2_correction;  // v_hat\n   params_memory[i] -= learning_rate * (m / (sqrtf(v) + eps) + weight_decay * params_memory[i]);\n}\n\nvoid unet_update(Unet* unet, float learning_rate, float beta1, float beta2, float eps, float weight_decay, int t) {\n    // lazy allocation\n    if (unet->m_memory == nullptr) {\n        size_t param_size = unet->params.total_size * sizeof(float);\n        cudaCheck(hipMalloc(&(unet->m_memory), param_size));\n        cudaCheck(hipMalloc(&(unet->v_memory), param_size));\n        cudaCheck(hipMemset(unet->m_memory, 0, param_size));\n        cudaCheck(hipMemset(unet->v_memory, 0, param_size));\n        printf(\"allocated %zu MiB for AdamW state m\\n\", param_size / 1024 / 1024);\n        printf(\"allocated %zu MiB for AdamW state v\\n\", param_size / 1024 / 1024);\n    }\n\n    int block_size = unet->config.block_size;\n    int n_blocks = ceil_div(unet->params.total_size, (size_t)block_size);\n    float beta1_correction = 1.0f - powf(beta1, t);\n    float beta2_correction = 1.0f - powf(beta2, t);\n    adamw_kernel2<<<n_blocks, block_size>>>(unet->params.mem_ptr, unet->grads.mem_ptr, unet->m_memory, unet->v_memory, unet->params.total_size,\n                    learning_rate, beta1, beta2, beta1_correction, beta2_correction, eps, weight_decay);\n    cudaCheck(hipGetLastError());\n}\n\n// ------------------------------------------------------------------------------------------------\n// save model states\n\nvoid save_unet_states(Unet* unet, GaussianDiffusion* diffusion, const char* filename) {\n    // create header\n    int model_header[256];\n    model_header[0] = 12345678; // magic number\n    model_header[1] = unet->config.B;\n    model_header[2] = unet->config.C_in;\n    model_header[3] = unet->config.C_model;\n    model_header[4] = unet->config.C_out;\n    model_header[5] = unet->config.H;\n    model_header[6] = unet->config.W;\n    model_header[7] = unet->config.max_period;\n    model_header[8] = 1; // whether to save the AdamW states, this will be 0 if the model is saved from pytorch\n    model_header[9] = 1; // whether to save rng states\n\n    FILE* model_file = fopenCheck(filename, \"wb\");\n    printf(\"\\nsaving model details\");\n    int result = fwrite(model_header, sizeof(int), 256, model_file);\n    assert(result == 256);\n\n    // save params\n    float* params_cpu_buf = (float*)mallocCheck(unet->params.total_size * sizeof(float));\n    cudaCheck(hipMemcpy(params_cpu_buf, unet->params.mem_ptr, unet->params.total_size * sizeof(float), hipMemcpyDeviceToHost));\n    printf(\"saving model params\\n\");\n    result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n    assert(result == unet->params.total_size);\n\n    if (model_header[8]) {\n        // save AdamW states\n        cudaCheck(hipMemcpy(params_cpu_buf, unet->m_memory, unet->params.total_size * sizeof(float), hipMemcpyDeviceToHost));\n        printf(\"saving AdamW state m\\n\");\n        result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n        assert(result == unet->params.total_size);\n        cudaCheck(hipMemcpy(params_cpu_buf, unet->v_memory, unet->params.total_size * sizeof(float), hipMemcpyDeviceToHost));\n        printf(\"saving AdamW state v\\n\");\n        result = fwrite(params_cpu_buf, sizeof(float), unet->params.total_size, model_file);\n        assert(result == unet->params.total_size);\n    }\n\n    if (model_header[9]) {\n        // save rngs\n        int n_states = diffusion->B * diffusion->img_size;\n        void *curand_state_buf = mallocCheck(n_states * sizeof(hiprandState_t));\n        cudaCheck(hipMemcpy(curand_state_buf, diffusion->curand_states, diffusion->B * diffusion->img_size * sizeof(hiprandState_t), hipMemcpyDeviceToHost));\n        printf(\"saving rng states\\n\");\n        result = fwrite(curand_state_buf, sizeof(hiprandState_t), n_states, model_file);\n        assert(result == n_states);\n        free(curand_state_buf);\n    }\n\n    fcloseCheck(model_file);\n    free(params_cpu_buf);\n    printf(\"saved model and diffusion states\\n\\n\");\n}\n\n// ------------------------------------------------------------------------------------------------\n// load unet and diffusion states\n\nvoid load_unet_and_diffusion_states(\n    Unet* unet, GaussianDiffusion* diffusion, const char* filename\n) {\n    FILE *model_file = fopenCheck(filename, \"rb\");\n    // read header\n    printf(\"\\nloading model header\\n\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file\\n\");\n        exit(1);\n    }\n    int B = model_header[1];\n    int C_in = model_header[2];\n    int C_model = model_header[3];\n    int C_out = model_header[4];\n    int H = model_header[5];\n    int W = model_header[6];\n    int max_period = model_header[7];\n    int save_adamw = model_header[8];\n    int save_rng = model_header[9];\n\n    // TODO: cleanup this code\n    int* channel_mult = (int*)mallocCheck(4 * sizeof(int));\n    channel_mult[0] = 1;\n    channel_mult[1] = 2;\n    channel_mult[2] = 3;\n    channel_mult[3] = 4;\n\n    // setup unet config\n    UnetConfig* config = &(unet->config);\n    config->B = B;\n    config->C_in = C_in;\n    config->C_model = C_model;\n    config->C_time = C_model * 4;\n    config->C_out = C_out;\n    config->H = H;\n    config->W = W;\n    config->block_size = 512;\n    config->gn_n_groups = 32;\n    config->n_levels = 4;\n    config->channel_mult = channel_mult;\n    config->HS = 32;\n    config->n_res = 2;\n    config->max_period = max_period;\n    config->att_start_level = 2;\n\n    unet_init(unet);\n    unet_count_layers(config);\n    unet_make_ptrs_and_count_memory(unet);\n    malloc_and_point(unet);\n\n    // read params\n    float* params_mem_cpu = (float*)mallocCheck(unet->params.total_size * sizeof(float));\n    printf(\"loading model params\\n\");\n    freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n    cudaCheck(hipMemcpy(unet->params.mem_ptr, params_mem_cpu, unet->params.total_size * sizeof(float), hipMemcpyHostToDevice));\n\n    if (save_adamw) {\n        // read AdamW states\n        printf(\"loading AdamW states\\n\");\n        freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n        cudaCheck(hipMalloc(&(unet->m_memory), unet->params.total_size * sizeof(float)));\n        cudaCheck(hipMemcpy(unet->m_memory, params_mem_cpu, unet->params.total_size * sizeof(float), hipMemcpyHostToDevice));\n        freadCheck(params_mem_cpu, sizeof(float), unet->params.total_size, model_file);\n        cudaCheck(hipMalloc(&(unet->v_memory), unet->params.total_size * sizeof(float)));\n        cudaCheck(hipMemcpy(unet->v_memory, params_mem_cpu, unet->params.total_size * sizeof(float), hipMemcpyHostToDevice));\n    } else {\n        printf(\"not loading AdamW states\\n\");\n        unet->m_memory = nullptr;\n        unet->v_memory = nullptr;\n    }\n\n    if (save_rng) {\n        // read curand rng states\n        printf(\"loading rng states\\n\");\n        int n_states = B * C_in * H * W;\n        void *curand_state_buf = mallocCheck(n_states * sizeof(hiprandState_t));\n        freadCheck(curand_state_buf, sizeof(hiprandState_t), n_states, model_file);\n        // initialize diffusion curant buffer\n        cudaCheck(hipMalloc(&(diffusion->curand_states), n_states * sizeof(hiprandState_t)));\n        cudaCheck(hipMemcpy(diffusion->curand_states, curand_state_buf, n_states * sizeof(hiprandState_t), hipMemcpyHostToDevice));\n        free(curand_state_buf);\n    } else {\n        printf(\"not loading rng states\\n\");\n        diffusion->curand_states = nullptr;\n    }\n    diffusion_init(diffusion, B, C_in, H, W, 1000);\n\n    free(params_mem_cpu);\n    fcloseCheck(model_file);\n    printf(\"loaded model and diffusion states \\n\\n\");\n}\n\n\n\n// ------------------------------------------------------------------------------------------------\n\ntypedef struct {\n    int count;\n    float mean_loss;\n} LossCounter;\n\nvoid update_loss_counter(LossCounter* counter, float loss) {\n    float old_mean = counter->mean_loss;\n    float old_count = (float)counter->count;\n    counter->mean_loss = old_mean * old_count / (old_count + 1) + loss / (old_count + 1);\n    counter->count++;\n}\n\nvoid clear_loss_counter(LossCounter* counter) {\n    counter->count = 0;\n    counter->mean_loss = 0.0f;\n}\n\nvoid create_directory(const char *dir) {\n    struct stat st = {0};\n    if (stat(dir, &st) == -1) {\n        mkdir(dir, 0700); // Create directory with permissions 0700\n    }\n}\n\nint main(int argc, char **argv) {\n    // Default values\n    const char *model_weights_file = \"unet_init.bin\";\n    const char *data_file = \"data/elephant_train.bin\";\n    const char *log_filename = \"log.txt\";\n\n    // Parse command line arguments\n    for (int i = 1; i < argc; ++i) {\n        if (strcmp(argv[i], \"--model_weights\") == 0 && i + 1 < argc) {\n            model_weights_file = argv[++i];\n        } else if (strcmp(argv[i], \"--data_file\") == 0 && i + 1 < argc) {\n            data_file = argv[++i];\n        } else if (strcmp(argv[i], \"--log_file\") == 0 && i + 1 < argc) {\n            log_filename = argv[++i];\n        }\n    }\n    printf(\"loading model weights from %s\\n\", model_weights_file);\n    printf(\"loading data from %s\\n\", data_file);\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n    printf(\"device registers per block: %d\\n\", deviceProp.regsPerBlock);\n    printf(\"shared mem per block: %ld\\n\", deviceProp.sharedMemPerBlock);\n\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    Unet unet;\n    GaussianDiffusion diffusion;\n    load_unet_and_diffusion_states(&unet, &diffusion, model_weights_file);\n\n    int B = unet.config.B;\n    int C_in = unet.config.C_in;\n    int C_out = unet.config.C_out;\n    int H = unet.config.H;\n    int W = unet.config.W;\n\n    DataLoader loader;\n    dataloader_init(&loader, data_file, B);\n\n    // allocate memory for target separately, since unet can still be run without targets for inference\n    cudaCheck(hipMalloc(&(unet.target), B * C_out * H * W * sizeof(float)));\n    float *d_timesteps_buf;\n    cudaCheck(hipMalloc(&d_timesteps_buf, B * sizeof(float)));\n\n\n    int n_iters = 100000;\n    int n_log_iter = 100;\n    int n_save_iter = 10000;\n    //int n_iters = 100;\n    //int n_log_iter = 5;\n    //int n_save_iter = 50;\n\n    // wipe existing logs\n    FILE* log_file = fopen(log_filename, \"w\");\n    fprintf(log_file, \"training unet,\\nmodel file %s,\\ndata filename %s\\n\", model_weights_file, data_file);\n    fprintf(log_file, \"starting training for %d iterations\\n\", n_iters);\n    fcloseCheck(log_file);\n\n    const char *model_save_dir = \"./models/\";\n    create_directory(model_save_dir);\n\n    LossCounter loss_counter;\n    clear_loss_counter(&loss_counter);\n\n    printf(\"Training for %d iterations\\n\", n_iters);\n    float time_elapsed = 0.0;\n    hipEvent_t start, end;\n    cudaCheck(hipEventCreate(&start));\n    cudaCheck(hipEventCreate(&end));\n    cudaCheck(hipEventRecord(start));\n    cudaCheck(hipEventSynchronize(start));\n    for (int iter = 1; iter <= n_iters; iter++) {\n        unet_zero_grad(&unet);\n        dataloader_next_batch(&loader);\n        // copy loaded data into input\n        cudaCheck(hipMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n        // sample timesteps and get timestep embeddings\n        sample_timesteps(&diffusion, d_timesteps_buf);\n        get_timestep_embeddings(&unet.time_emb_state, d_timesteps_buf, unet.timestep_emb);\n        \n        // target will be injected random noise\n        diffusion_draw_normal(&diffusion, unet.target);\n\n        // push input through forward diffusion process by t to get x_t\n        diffusion_forward_by_t(&diffusion, unet.input, d_timesteps_buf, unet.target);\n\n        unet_forward(cublas_handle, &unet);\n        unet_backward(cublas_handle, &unet);\n        unet_update(&unet, 0.0001f, 0.9f, 0.999f, 1e-8f, 0.0f, iter);\n\n        update_loss_counter(&loss_counter, unet.mean_loss);\n\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(end));\n        cudaCheck(hipEventElapsedTime(&time_elapsed, start, end));\n\n        if (iter % n_log_iter == 0) {\n            printf(\"step %4d/%d | loss %7.6f | mean loss %7.6f | cur time %.4f s\\n\", iter, n_iters, unet.mean_loss, loss_counter.mean_loss, time_elapsed / 1000);\n            FILE *log_file = fopen(log_filename, \"a\");\n            fprintf(log_file, \"step %4d/%d | loss %7.6f | mean loss %7.6f | cur time %.4f s\\n\", iter, n_iters, unet.mean_loss, loss_counter.mean_loss, time_elapsed / 1000);\n            fclose(log_file);\n            clear_loss_counter(&loss_counter);\n        }\n\n        if (iter % n_save_iter == 0) {\n            char save_filename[256];\n            sprintf(save_filename, \"./models/model_%d.bin\", iter);\n            save_unet_states(&unet, &diffusion, save_filename);\n        }\n    }\n    printf(\"average time per iteration: %f ms\\n\", time_elapsed / n_iters);\n\n    cudaCheck(hipEventDestroy(start));\n    cudaCheck(hipEventDestroy(end));\n    cublasCheck(hipblasDestroy(cublas_handle));\n    cudaCheck(hipFree(d_timesteps_buf));\n    cudaCheck(hipFree(unet.target));\n\n    dataloader_free(&loader);\n    diffusion_free(&diffusion);\n    free_unet(&unet);\n\n    printf(\"success\\n\");\n}", "source": "/kaggle/working/unet.cu/train_unet.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"avgpool.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n__global__ void avgpool_2d_forward_kernel(\n    const float* x,  // (B, C, H, W)\n    float* out, // (B, C, H/2, W/2)\n    int B, int C, int H, int W\n) {\n    //printf(\"started kernel\\n\");\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int H_2 = H / 2;\n    int W_2 = W / 2;\n    int img_out_size = H_2 * W_2;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int img_size = H * W;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_2) % H_2;\n    int j = flat_idx % W_2;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + 2 * i * W + 2 * j;\n    out += b * C * img_out_size + c * img_out_size + i * W_2 + j;\n\n    float sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            sum += x[ii * W + jj];\n        }\n    }\n    out[0] = sum / 4.0f;\n}\n\n\n__global__ void avgpool_2d_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H_out, int W_out // H_out and W_out are the shape of the output from forward, i.e. half the original sizes\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_out_size = H_out * W_out;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int H_in = H_out * 2;\n    int W_in = W_out * 2;\n    int img_in_size = H_in * W_in;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_out) % H_out;\n    int j = flat_idx % W_out;\n\n    // move pointers\n    dout += b * C * img_out_size + c * img_out_size + i * W_out + j;\n    dx += b * C * img_in_size + c * img_in_size + 2 * i * W_in + 2 * j;\n\n    float d = dout[0] / 4.0f;\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dx[ii * W_in + jj] = d;\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n// kernel launcher\n\nvoid avgpool_2d_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int gridDim = ceil_div(B * C * (H / 2) * (W / 2), block_size);\n    avgpool_2d_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\nvoid avgpool_2d_backward1(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int H_out = H / 2;\n    int W_out = W / 2;\n    int gridDim = ceil_div(B * C * H_out * W_out, block_size);\n    avgpool_2d_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H_out, W_out);\n    cudaCheck(cudaGetLastError());\n}\n\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 1;\n    int C = 64;\n    int H = 32;\n    int W = 32;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C * H * W * sizeof(float));\n    float* out = (float*)malloc(B * C * H/2 * W/2 * sizeof(float));\n    float* dx = (float*)malloc(B * C * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C * H/2 * W/2 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"down.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * H * W, file);\n    freadCheck(out, sizeof(float), B * C * H/2 * W/2, file);\n    freadCheck(dout, sizeof(float), B * C * H/2 * W/2, file);\n    freadCheck(dx, sizeof(float), B * C * H * W, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(cudaMalloc(&d_x, B * C * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * C * H/2 * W/2 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * C * H/2 * W/2 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, B * C * H * W * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, B * C * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * C * H/2 * W/2 * sizeof(float), cudaMemcpyHostToDevice));\n\n    printf(\"Checking forward pass\\n\");\n    int block_sizes[] = {32, 64, 128};\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        avgpool_2d_forward1(d_out, d_x, B, C, H, W, block_size);\n        validate_result(d_out, out, \"out\", B * C * (H/2) * (W/2));\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        avgpool_2d_backward1(d_dout, d_dx, B, C, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", B * C * H * W);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, avgpool_2d_forward1,\n                                              d_out, d_x,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, avgpool_2d_backward1,\n                                              d_dout, d_dx,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n    cudaFree(d_x);\n    cudaFree(d_out);\n    cudaFree(d_dout);\n    cudaFree(d_dx);\n\n    return 0;\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"avgpool.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n__global__ void avgpool_2d_forward_kernel(\n    const float* x,  // (B, C, H, W)\n    float* out, // (B, C, H/2, W/2)\n    int B, int C, int H, int W\n) {\n    //printf(\"started kernel\\n\");\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int H_2 = H / 2;\n    int W_2 = W / 2;\n    int img_out_size = H_2 * W_2;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int img_size = H * W;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_2) % H_2;\n    int j = flat_idx % W_2;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + 2 * i * W + 2 * j;\n    out += b * C * img_out_size + c * img_out_size + i * W_2 + j;\n\n    float sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            sum += x[ii * W + jj];\n        }\n    }\n    out[0] = sum / 4.0f;\n}\n\n\n__global__ void avgpool_2d_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H_out, int W_out // H_out and W_out are the shape of the output from forward, i.e. half the original sizes\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_out_size = H_out * W_out;\n    if (flat_idx >= B * C * img_out_size) {\n        return;\n    }\n    int H_in = H_out * 2;\n    int W_in = W_out * 2;\n    int img_in_size = H_in * W_in;\n    int b = flat_idx / (C * img_out_size);\n    int c = (flat_idx / img_out_size) % C;\n    int i = (flat_idx / W_out) % H_out;\n    int j = flat_idx % W_out;\n\n    // move pointers\n    dout += b * C * img_out_size + c * img_out_size + i * W_out + j;\n    dx += b * C * img_in_size + c * img_in_size + 2 * i * W_in + 2 * j;\n\n    float d = dout[0] / 4.0f;\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dx[ii * W_in + jj] = d;\n        }\n    }\n}\n\n// ----------------------------------------------------------------------------\n// kernel launcher\n\nvoid avgpool_2d_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int gridDim = ceil_div(B * C * (H / 2) * (W / 2), block_size);\n    avgpool_2d_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\nvoid avgpool_2d_backward1(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W,\n    const int block_size\n) {\n    int H_out = H / 2;\n    int W_out = W / 2;\n    int gridDim = ceil_div(B * C * H_out * W_out, block_size);\n    avgpool_2d_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H_out, W_out);\n    cudaCheck(hipGetLastError());\n}\n\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 1;\n    int C = 64;\n    int H = 32;\n    int W = 32;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C * H * W * sizeof(float));\n    float* out = (float*)malloc(B * C * H/2 * W/2 * sizeof(float));\n    float* dx = (float*)malloc(B * C * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C * H/2 * W/2 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"down.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * H * W, file);\n    freadCheck(out, sizeof(float), B * C * H/2 * W/2, file);\n    freadCheck(dout, sizeof(float), B * C * H/2 * W/2, file);\n    freadCheck(dx, sizeof(float), B * C * H * W, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(hipMalloc(&d_x, B * C * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * C * H/2 * W/2 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * C * H/2 * W/2 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, B * C * H * W * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, B * C * H * W * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, B * C * H/2 * W/2 * sizeof(float), hipMemcpyHostToDevice));\n\n    printf(\"Checking forward pass\\n\");\n    int block_sizes[] = {32, 64, 128};\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        avgpool_2d_forward1(d_out, d_x, B, C, H, W, block_size);\n        validate_result(d_out, out, \"out\", B * C * (H/2) * (W/2));\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        avgpool_2d_backward1(d_dout, d_dx, B, C, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", B * C * H * W);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, avgpool_2d_forward1,\n                                              d_out, d_x,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, avgpool_2d_backward1,\n                                              d_dout, d_dx,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n    hipFree(d_x);\n    hipFree(d_out);\n    hipFree(d_dout);\n    hipFree(d_dx);\n\n    return 0;\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/avgpool.cu"}
{"input": "#include <cstdio>\n#include <cmath>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <cuda_device_runtime_api.h>\n#include <cuda_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include <assert.h>\n#include \"common.h\"\n#include \"conv2d_k3.cuh\"\n\n\n__global__ void permute_w_tile_x_kernel1(\n    const float* x, const float* weight,\n    float* x_tiled, float* weight_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < C_out * C_in * 9) {\n        // permute weight\n        int c_out = idx / (C_in * 9);\n        int c_in = (idx / 9) % C_in;\n        int k = idx % 9;\n        int out_ind = k * C_out * C_in + c_out * C_in + c_in;\n        weight_perm[out_ind] = weight[idx];\n    }\n\n    if (idx < B * C_in * H * W) {\n        // tile x to shape (9, C_in, B, H, W)\n        // for a given first dimension k, the entry at an index should be\n        // what the k-th kernel is multiplied with in the convolution for that pixel\n        // (not a good explanation... but obvious with a picture)\n        // for example, a value at (H, W) = (0, 0) will never be convolved with kernels 5, 7 and 8\n        // because it is on a corner\n        int x_offset = B * C_in * H * W;\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        float x_val = x[idx];\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[c * B * img_size + b * img_size + h_end * W + w_end] = x_val;\n                }\n                // shift pointer to next kernel\n                x_tiled += x_offset;\n            }\n        }\n    }\n}\n\n\n__global__ void reduce_transpose_add_bias_kernel1(\n    const float* out_tmp, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    // make sure we're in bound\n    assert(idx < B * C_out * H * W);\n    // make sure every block lies in the same channel\n    assert((B * H * W) % blockDim.x == 0);\n\n    int img_size = H * W;\n    int c = idx / (B * img_size);\n    int b = (idx / img_size) % B;\n    int h = (idx / W) % H;\n    int w = idx % W;\n\n    __shared__ float bias_sh[1];\n    if (threadIdx.x == 0) {\n        bias_sh[0] = bias[c];\n    }\n    __syncthreads();\n\n    float out_val = bias_sh[0];\n\n    for (int k = 0; k < 9; k++) {\n        float out_tmp_val = out_tmp[k * C_out * B * img_size + c * B * img_size + b * img_size + h * W + w];\n        out_val += out_tmp_val;\n    }\n\n    out[b * C_out * img_size + c * img_size + h * W + w] = out_val;\n}\n\nvoid conv2d_k3_forward1(\n    cublasHandle_t cublas_handle,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // implements Conv2D with kernel size 1 and padding 1\n    // shapes:\n    // x (B, C_in, H, W)\n    // weight (C_out, C_in, 3, 3)\n    // bias (C_out,)\n\n    // plan:\n    // 1. transpose and tile x into x_tiled of shape (9, C_in, B, H, W)\n    // 2. transpose weight into shape (9, C_out, C_in) (we will do this in the same kernel as one above)\n    // 3. call cublasSgemmStridedBatched with 9 batches\n    // 4. reduce over the 9 kernel dimensions, add bias, and transpose output to shape (B, C_out, H, W)\n\n    // 1. and 2.\n    // transpose weight and tile x\n    float *x_tiled, *weight_perm;\n    cudaCheck(cudaMalloc(&x_tiled, 9 * C_in * B * H * W * sizeof(float)));\n    cudaCheck(cudaMemset(x_tiled, 0, 9 * C_in * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&weight_perm, 9 * C_out * C_in * sizeof(float)));\n    int total_th_1 = max_int(B * C_in * H * W, C_in * C_out * 9);\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    permute_w_tile_x_kernel1<<<n_blk_1, block_size>>>(x, weight, x_tiled, weight_perm, B, C_in, C_out, H, W);\n\n    // 3. matmul via cublasSgemmStridedBatched\n    float* out_tmp;\n    cudaCheck(cudaMalloc(&out_tmp, 9 * C_out * B * H * W * sizeof(float)));\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, bhw, C_out, C_in, &alpha, x_tiled, bhw, C_in * bhw, weight_perm, C_in, C_out * C_in, &beta, out_tmp, bhw, C_out * bhw, 9));\n\n    // reduce, add bias, and transpose\n    int total_th_2 = B * C_out * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    reduce_transpose_add_bias_kernel1<<<n_blk_2, block_size>>>(out_tmp, bias, out, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(cudaFree(x_tiled));\n    cudaCheck(cudaFree(weight_perm));\n    cudaCheck(cudaFree(out_tmp));\n}\n\n\n__global__ void transpose_tile_x_kernel2(\n    const float* x,\n    float* x_tiled,\n    int B, int C_in, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * C_in * H * W) {\n        // similar to permute_w_tile_x_kernel1, except now the target shape is (C_in, 9, B, H, W)\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        float x_val = x[idx];\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[c * 9 * B * img_size + (i * 3 + j) * B * img_size + b * img_size + h_end * W + w_end] = x_val;\n                }\n            }\n        }\n    }\n}\n\n__global__ void transpose_out_add_bias_kernel2(\n    const float* out_tmp, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    // make sure we're in bound\n    assert(idx < B * C_out * H * W);\n    // make sure every block lies in the same channel\n    assert((B * H * W) % blockDim.x == 0);\n\n    int img_size = H * W;\n    int c = idx / (B * img_size);\n    int b = (idx / img_size) % B;\n    int h = (idx / W) % H;\n    int w = idx % W;\n\n    __shared__ float bias_sh[1];\n    if (threadIdx.x == 0) {\n        bias_sh[0] = bias[c];\n    }\n    __syncthreads();\n    \n    out[b * C_out * img_size + c * img_size + h * W + w] = bias_sh[0] + out_tmp[idx];\n}\n\nvoid conv2d_k3_forward2(\n    cublasHandle_t cublas_handle,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size,\n    float* t1, float* t2, float* t3\n) {\n    // similar to kernel 1, but transpose into a different shape so that we can just make one big matmul call\n    // and no need to reduce afterwards\n    // plan:\n    // 1. transpose and tile x into x_tiled of shape (C_in, 9, B, H, W)\n    // 2. call cublasSgemm on x_tiled and weight, no need for transposes!\n    // 3. add bias\n\n    // 1. transpose x to x_tiled\n    cudaEvent_t start, end1, end2, end3;\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end1));\n        cudaCheck(cudaEventCreate(&end2));\n        cudaCheck(cudaEventCreate(&end3));\n        cudaCheck(cudaEventRecord(start, nullptr));\n    }\n    float* x_tiled;\n    cudaCheck(cudaMalloc(&x_tiled, C_in * 9 * B * H * W * sizeof(float)));\n    cudaCheck(cudaMemset(x_tiled, 0, C_in * 9 * B * H * W * sizeof(float)));\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventRecord(end1, nullptr));\n    }\n    int total_th_1 = B * C_in * H * W;\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    transpose_tile_x_kernel2<<<n_blk_1, block_size>>>(x, x_tiled, B, C_in, H, W);\n\n    // 2. matmul with cublasSgemm\n    float* out_tmp;\n    cudaCheck(cudaMalloc(&out_tmp, C_out * B * H * W * sizeof(float)));\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, bhw, C_out, C_in * 9, &alpha, x_tiled, bhw, weight, C_in * 9, &beta, out_tmp, bhw));\n\n\n    // 3. transpose and add bias\n    int total_th_2 = B * C_out * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    transpose_out_add_bias_kernel2<<<n_blk_2, block_size>>>(out_tmp, bias, out, B, C_out, H, W);\n    if (t2 != nullptr) {\n        cudaCheck(cudaEventRecord(end2, nullptr));\n    }\n\n\n    // free memory\n    cudaCheck(cudaFree(x_tiled));\n    cudaCheck(cudaFree(out_tmp));\n    if (t3 != nullptr) {\n        cudaCheck(cudaEventRecord(end3, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end1));\n        cudaCheck(cudaEventSynchronize(end2));\n        cudaCheck(cudaEventSynchronize(end3));\n        float elapsed_time1, elapsed_time2, elapsed_time3;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time1, start, end1));\n        cudaCheck(cudaEventElapsedTime(&elapsed_time2, end1, end2));\n        cudaCheck(cudaEventElapsedTime(&elapsed_time3, end2, end3));\n        *t1 += elapsed_time1;\n        *t2 += elapsed_time2;\n        *t3 += elapsed_time3;\n    }\n}\n\n// this kernel will roughly follow kernel 5 here: https://github.com/siboehm/SGEMM_CUDA, with a few differences\n// the main operation is convolving the input X with the weights\n// weights are shaped (O, Cx9), and X is shaped (B, C, HxW)\n// We are morally doing a matmul with dimensions O, C, HxW\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel1(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_i * w_s_width + w_j] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(thread_row * TO + i) * w_s_width + dot_idx * 9 + conv_idx];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n\n// almost same as 1, except we load A as transposed\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_j * BO + w_i] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n\n// almost same as 2, but we vectorize the loads and use float4\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    assert(TH == 4); // needed for reg_x loads later, so we can do one vectorized load for each thread\n    const int C9 = C * 9;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (w_s_width / 4);\n            int inner_row = i / (w_s_width / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            w_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            w_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            w_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            w_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        // load x into smem\n        for (int i = threadIdx.x; i < x_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&x_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            // NOTE: relying on the fact that TH = 4\n            reinterpret_cast<float4 *>(reg_x)[0] = reinterpret_cast<float4 *>(&x_s[dot_idx * BH + thread_col * TH])[0];\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n// CPU version for debugging\nvoid conv_k3_forward_cpu(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int OC, int H, int W\n) {\n    for (int b = 0; b < B; b++) {\n        for (int oc = 0; oc < OC; oc++) {\n            for (int h = 0; h < H; h++) {\n                for (int w = 0; w < W; w++) {\n                    for (int k = 0; k < 9; k++) {\n                        // check if the output will actually be used, if not skip\n                        int k_i = k / 3;\n                        int k_j = k % 3;\n                        int h_target = h + 1 - k_i;\n                        int w_target = w + 1 - k_j;\n                        if (h_target < 0 || h_target >= H || w_target < 0 || w_target >= W) {\n                            continue;\n                        }\n                        float out_bohw = 0.0f;\n                        for (int c = 0; c < C; c++) {\n                            float new_val = weight[oc * C * 9 + c * 9 + k] * x[b * C * H * W + c * H * W + h * W + w];\n                            out_bohw += new_val;\n                            //if (b == 0 && h == 0 && w == 1 && k == 5 && oc == 0) {\n                            //    printf(\"(0, 1) OC 0 kern 5: contribution from %d: %f, out val %f\\n\", c, new_val, out_bohw);\n                            //}\n                        }\n                        int out_idx = b * OC * H * W + oc * H * W + h_target * W + w_target;\n                        //if (h_target == 0 && w_target == 0 && oc == 0 && b == 0) {\n                        //    printf(\"(0, 0) getting contribution from (%d, %d) at k %d: %f, prev_val %f\\n\", h, w, k, out_bohw, out[out_idx]);\n                        //}\n                        out[out_idx] += out_bohw;\n                        //if (h_target == 0 && w_target == 0 && oc == 0 && b == 0) {\n                        //    printf(\"(0, 0) getting contribution from (%d, %d) at k %d: %f, new val %f\\n\", h, w, k, out_bohw, out[out_idx]);\n                        //}\n                    }\n                    \n                    //if (h == 0 && w == 0 && oc == 0 && b == 0) {\n                    //    printf(\"(0, 0) adding bias %f\\n\", bias[oc]);\n                    //}\n                    out[b * OC * H * W + oc * H * W + h * W + w] += bias[oc];\n                }\n            }\n            printf(\"(0, 0) final value: %f\\n\", out[0]);\n            return;\n        }\n    }\n}\n\nvoid conv2d_k3_forward3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // since the kernel will add to the output, we need to zero it first\n    cudaCheck(cudaMemset(out, 0, B * C_out * H * W * sizeof(float)));\n\n    if (H * W == 64) {\n        const int BH = 64;\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_in == 3) {\n        const int BH = 64;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        const int BC = 3;\n        assert(C_out % 64 == 0);\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_out == 3) {\n        const int BH = 64 * 2;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_in % 8 == 0);\n        const int BC = 8;\n        const int BO = 3;\n        const int TO = 3;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else {\n        const int BH = 64 * 4;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 8;\n        const int TO = 2;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    }\n    cudaCheck(cudaGetLastError());\n}\n\n// ----------------------------------------------------------------------------\n// backward pass\n\n__global__ void permute_dout_tile_x_kernel(\n    const float* dout, const float* x,\n    float* dout_perm, float* x_tiled,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx < B * C_out * H * W) {\n        // permute dout\n        int b = idx / (C_out * img_size);\n        int c = (idx / img_size) % C_out;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        dout_perm[c * B * img_size + b * img_size + h * W + w] = dout[idx];\n    }\n\n    if (idx < B * C_in * H * W) {\n        // tile x_tiled, see permute_w_tile_x_kernel1 for explanation\n        // except that we tile into a shape of (B, H, W, C_in, 9), the transpose of the tiling there\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[b * img_size * C_in * 9 + h_end * W * C_in * 9 + w_end * C_in * 9 + c * 9 + i * 3 + j]\n                        = x[idx];\n                }\n            }\n        }\n    }\n}\n\n__global__ void reduce_dx_kernel(\n    const float* dx_tiled,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * C_in * H * W) {\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_end = (idx / W) % H;\n        int w_end = idx % W;\n        float sum = 0.0f;\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_start = h_end + 1 - i;\n                int w_start = w_end + 1 - j;\n                if (h_start >= 0 && h_start < H && w_start >= 0 && w_start < W) {\n                    sum += dx_tiled[c * 9 * B * img_size + (i * 3 + j) * B * img_size + b * img_size + h_start * W + w_start];\n                }\n            }\n        }\n        dx[idx] = sum;\n    }\n}\n\n\n__global__ void reduce_dbias_kernel1(\n    const float* dout_perm,\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    // dout_perm has shape (C_out, B, H, W)\n    // each block will reduce over one channel\n    dout_perm += blockIdx.x * B * H * W;\n    int bhw = B * H * W;\n    assert(blockDim.x <= bhw);\n    extern __shared__ float sdata[];\n    \n    float sum = 0.0f;\n    for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n        sum += dout_perm[i];\n    }\n    sdata[threadIdx.x] = sum;\n\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            sdata[threadIdx.x] += sdata[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = sdata[0];\n    }\n}\n\n// use cg to do warp reduce\n// doesn't really seem faster\n__global__ void reduce_dbias_kernel2(\n    const float* dout_perm,\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    assert(blockDim.x <= bhw);\n    // move dout_perm to the right block\n    dout_perm += blockIdx.x * bhw;\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32];\n\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n        thread_sum += dout_perm[i];\n    }\n    \n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n\n    // warp reduce\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = block_sum;\n    }\n}\n\nvoid conv2d_k3_backward1(\n    cublasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size,\n    float* t1, float* t2, float* t3, float* t4, float* t5, float* t6\n) {\n    // shapes:\n    // dout (B, C_out, H, W)\n    // x (B, C_in, H, W)\n    // weight (C_out, C_in, 3, 3)\n    // bias (C_out,)\n\n    // we need to calculate dx, dweight, dbias separately\n    // dbias is a single reduce\n    // for dweight, we need to tile x into x_tile of shape (B * H * W, C_in * 9)\n    // then do a matmul with dout_perm (shape (C_out, B * H * W))\n    // for dx, we do dout_perm @ weight.T (store it in x_tile), and then reduce to get dx\n    // If we don't want to do a reduce when computing dx, we need to make 9 copies of dout\n    // which might be ok but increases memory usage. We can try it later.\n    cudaEvent_t start, end1, end2, end3, end4, end5, end6;\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end1));\n        cudaCheck(cudaEventCreate(&end2));\n        cudaCheck(cudaEventCreate(&end3));\n        cudaCheck(cudaEventCreate(&end4));\n        cudaCheck(cudaEventCreate(&end5));\n        cudaCheck(cudaEventCreate(&end6));\n        cudaCheck(cudaEventRecord(start, nullptr));\n    }\n\n    // 1. transpose to dout_perm and tile x_tile\n    float *dout_perm, *x_tiled;\n    cudaCheck(cudaMalloc(&x_tiled, B * H * W * C_in * 9 * sizeof(float)));\n    cudaCheck(cudaMemset(x_tiled, 0, B * H * W * C_in * 9 * sizeof(float)));\n    cudaCheck(cudaMalloc(&dout_perm, B * C_out * H * W * sizeof(float)));\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventRecord(end1, nullptr));\n    }\n    int total_th_1 = max_int(C_in, C_out) * B * H * W;\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    permute_dout_tile_x_kernel<<<n_blk_1, block_size>>>(dout, x, dout_perm, x_tiled, B, C_in, C_out, H, W);\n    cudaCheck(cudaGetLastError());\n    if (t2 != nullptr) {\n        cudaCheck(cudaEventRecord(end2, nullptr));\n    }\n\n    // 2. get dweight by single matmul between x_tiled and dout_perm\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, C_in * 9, C_out, bhw, &alpha, x_tiled, C_in * 9, dout_perm, bhw, &beta, dweight, C_in * 9));\n\n    if (t2 != nullptr) {\n        cudaCheck(cudaEventRecord(end3, nullptr));\n    }\n\n    // 3. get dx_tile by matmul between dout_perm and weight.T, and save output in x_tiled\n    // note: beta = 0 overwrites existing values in x_tiled\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, bhw, C_in * 9, C_out, &alpha, dout_perm, bhw, weight, C_in * 9, &beta, x_tiled, bhw));\n\n\n    // 4. reduce x_tiled to get dx\n    int total_th_2 = B * C_in * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    reduce_dx_kernel<<<n_blk_2, block_size>>>(x_tiled, dx, B, C_in, H, W);\n\n    if (t2 != nullptr) {\n        cudaCheck(cudaEventRecord(end4, nullptr));\n    }\n\n    // 5. reduce dbias\n    //reduce_dbias_kernel1<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n    reduce_dbias_kernel2<<<C_out, block_size>>>(dout_perm, dbias, B, C_out, H, W);\n\n    if (t5 != nullptr) {\n        cudaCheck(cudaEventRecord(end5, nullptr));\n    }\n\n    // free memory\n    cudaCheck(cudaFree(dout_perm));\n    cudaCheck(cudaFree(x_tiled));\n    if (t5 != nullptr) {\n        cudaCheck(cudaEventRecord(end6, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end1));\n        cudaCheck(cudaEventSynchronize(end2));\n        cudaCheck(cudaEventSynchronize(end3));\n        cudaCheck(cudaEventSynchronize(end4));\n        cudaCheck(cudaEventSynchronize(end5));\n        cudaCheck(cudaEventSynchronize(end6));\n        float elapsed1, elapsed2, elapsed3, elapsed4, elapsed5, elapsed6;\n        cudaCheck(cudaEventElapsedTime(&elapsed1, start, end1));\n        cudaCheck(cudaEventElapsedTime(&elapsed2, end1, end2));\n        cudaCheck(cudaEventElapsedTime(&elapsed3, end2, end3));\n        cudaCheck(cudaEventElapsedTime(&elapsed4, end3, end4));\n        cudaCheck(cudaEventElapsedTime(&elapsed5, end4, end5));\n        cudaCheck(cudaEventElapsedTime(&elapsed6, end5, end6));\n        *t1 += elapsed1;\n        *t2 += elapsed2;\n        *t3 += elapsed3;\n        *t4 += elapsed4;\n        *t5 += elapsed5;\n        *t6 += elapsed6;\n    }\n}\n\n// almost the same as conv2d_k3_forward_kernel, except that the compute and the writes\n// are different, because now we want to treat the C dimension as the inner product dimension\ntemplate <const int BO, const int BH, const int BC, const int TC, const int TH>\n__global__ void dx_backward_kernel1(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int C9 = C * 9;\n    // n_threads per block = (BC * BH) / (TC * TH)\n    // set gridDim to (BxHxW / BH, C / TC)\n    // find the output block that this thread block is responsible for\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TCxTH block that the thread is responsible\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for weights and dout\n    const int w_s_width = BC * 9;\n    const int w_s_sz = BO * w_s_width;\n    const int dout_s_sz = BO * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float dout_s[dout_s_sz];\n\n    // registers for local results\n    float thread_results[TC * TH * 9] = {0.0};\n    // regs for weights and dout\n    float reg_w[TC * 9] = {0.0};\n    float reg_dout[TH] = {0.0};\n\n    // calculate the block offsets\n    const int dout_block_offset = batch * O * H * W + block_col * BH;\n    const int w_block_offset = block_row * w_s_width;\n\n    // outer loop over BO sizes of channel O\n    for (int o_idx = 0; o_idx < O; o_idx += BO) {\n        // offsets for this inner block\n        int w_inner_offset = o_idx * C9;\n        int dout_inner_offset = o_idx * H * W;\n\n        // load weights to smem\n        if (BC % 4 == 0) {\n            // vectorize dram load\n            for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n                int inner_col = i % (w_s_width / 4);\n                int inner_row = i / (w_s_width / 4);\n                reinterpret_cast<float4 *>(&w_s[inner_row * w_s_width + inner_col * 4])[0] = \n                    reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            }\n        } else {\n            for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n                int inner_col = i % w_s_width;\n                int inner_row = i / w_s_width;\n                w_s[inner_row * w_s_width + inner_col] = weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col];\n            }\n        }\n        // load dout\n        for (int i = threadIdx.x; i < dout_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&dout_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n        \n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BO; dot_idx++) {\n            // load dout and w into registers\n            for (int j = 0; j < (TH / 4); j++) {\n                reinterpret_cast<float4 *>(&reg_dout[j * 4])[0] = \n                    reinterpret_cast<float4 *>(&dout_s[dot_idx * BH + thread_col * TH + j * 4])[0];\n            }\n            // non vectorized load\n            //for (int j = 0; j < TH; j++) {\n            //    reg_dout[j] = dout_s[dot_idx * BH + thread_col * TH + j];\n            //}\n            if (TC % 4 == 0) {\n                // vectorize smem load\n                for (int i = 0; i < (TC * 9 / 4); i++) {\n                    reinterpret_cast<float4 *>(&reg_w[i * 4])[0] = \n                        reinterpret_cast<float4 *>(&w_s[dot_idx * w_s_width + thread_row * TC * 9 + i * 4])[0];\n                }\n            } else {\n                for (int i = 0; i < TC * 9; i++) {\n                    reg_w[i] = w_s[dot_idx * w_s_width + thread_row * TC * 9 + i];\n                }\n            }\n\n            for (int k = 0; k < 9; k++) {\n                for (int i = 0; i < TC; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        thread_results[k * TC * TH + i * TH + j] += \n                            reg_w[i * 9 + k] * reg_dout[j];\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    int out_batch_offset = batch * C * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TC; i++) {\n            int c_abs = block_row * BC + thread_row * TC + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs - 1 + k_i;\n                int w_target_abs = w_abs - 1 + k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + c_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TC * TH + i * TH + j;\n                    atomicAdd(dx + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n        }\n    }\n}\n\n// I wonder how fast does a transposition kernel take for the weights\n__global__ void transpose_weight_kernel(\n    float* weight, float* out,\n    int C, int O\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < C * O * 9) {\n        int o = idx / (C * 9);\n        int c = (idx % C * 9) / 9;\n        int k = idx % 9;\n        out[c * O * 9 + o * 9 + k] = weight[idx];\n    }\n}\n\nvoid dx_backward(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    if (H < 16) {\n        const int BH = 64;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_in == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 3;\n        const int TC = 3;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_out == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 3;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    }\n}\n\nvoid dx_backward_cpu(\n    float* dout, float* weight, float* dx,\n    int B, int C, int O, int H, int W\n) {\n    printf(\"dx[0] before zeroing: %f\\n\", dx[0]);\n    for (int b = 0; b < B; b++) {\n        for (int c = 0; c < C; c++) {\n            for (int h = 0; h < H; h++) {\n                for (int w = 0; w < W; w++) {\n                    for (int k = 0; k < 9; k++) {\n                        int k_i = k / 3;\n                        int k_j = k % 3;\n                        int h_target = h - 1 + k_i;\n                        int w_target = w - 1 + k_j;\n                        if (h_target < 0 || h_target >= H || w_target < 0 || w_target >= W) {\n                            continue;\n                        }\n                        float out_bchw = 0.0f;\n                        for (int o = 0; o < O; o++) {\n                            float new_val = weight[o * C * 9 + c * 9 + k] * dout[b * O * H * W + o * H * W + h * W + w];\n                            out_bchw += new_val;\n                        }\n                        int out_idx = b * C * H * W + c * H * W + h_target * W + w_target;\n                        dx[out_idx] += out_bchw;\n                    }\n                }\n            }\n            if (b == 0 && c == 0) {\n                printf(\"dx[0]: %f\\n\", dx[0]);\n                return;\n            }\n        }\n    }\n}\n\n// dweight_buf will have shape (O, C, 9, B)\n// and then we have to reduce over the last dimension to get the actual weight\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC>\n__global__ void dweight_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // ensure that we load entire image widths into smem\n    // so we can do all 9 kernels at once\n    assert(BW % W == 0);\n\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    // number of image widths we will load to smem in one round\n    const int NW = BW / W;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NW - 1)) {\n        // load dout into smem\n        int dout_inner_offset = h * W;\n        int nh = (h < H - (NW - 1)) ? NW : NW - 1;\n        int dout_load_sz = nh * W * BO;\n        for (int i = threadIdx.x; i < dout_load_sz / 4; i++) {\n            int inner_col = i % (nh * W / 4);\n            int inner_row = i / (nh * W / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n            dout_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            dout_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            dout_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            dout_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        \n        // load x to smem\n        int x_inner_offset = h * W;\n        int x_load_sz = nh * W * BC;\n        for (int i = threadIdx.x; i < x_load_sz / 4; i++) {\n            int inner_col = i % (nh * W / 4);\n            int inner_row = i / (nh * W / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n            x_s[(inner_col * 4 + 0) * BC + inner_row] = tmp.x;\n            x_s[(inner_col * 4 + 1) * BC + inner_row] = tmp.y;\n            x_s[(inner_col * 4 + 2) * BC + inner_row] = tmp.z;\n            x_s[(inner_col * 4 + 3) * BC + inner_row] = tmp.w;\n        }\n        __syncthreads();\n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NW - 1) {\n            for (int i = threadIdx.x; i < W * BO; i++) {\n                int inner_col = i % W;\n                int inner_row = i / W;\n                dout_s[(inner_col + nh * W) * BO + inner_row] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < W * BC; i++) {\n                int inner_col = i % W;\n                int inner_row = i / W;\n                x_s[(inner_col + nh * W) * BC + inner_row] = 0.0f;\n            } \n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < BW; x_dot_idx++) {\n            int x_h = x_dot_idx / W;\n            int x_w = x_dot_idx % W;\n            // load one pixel with TC of the C channels into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NW && dout_w >= 0 && dout_w < W) {\n                    // this logic is a bit tricky: for the last row in smem\n                    // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                    // for kernels 4, 5, 6 we need to only compute them once\n                    // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                    // so it will be computed as part of the first row next time\n                    if (x_h == NW - 1 && k_i == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * W + dout_w;\n                    }\n                }\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_results[k * TO * TC + i * TC + j] += reg_dout[i] * reg_x[j];\n                        }\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\n\n// C * O blocks\n// 288 (9 * 32) threads per block, use one warp to reduce each batch dim\n__global__ void dweight_reduce_kernel(\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O\n) {\n    assert(gridDim.x == O * C);\n    int o = blockIdx.x / C;\n    int c = blockIdx.x % C;\n    // move input pointer\n    dweight_buf += o * C * 9 * B + c * 9 * B;\n    assert(blockDim.x == 32 * 9);\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        thread_sum += dweight_buf[warp_id * B + i];\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dweight[o * C * 9 + c * 9 + warp_id] = warp_sum;\n    }\n}\n\nvoid dweight_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 32;\n    const int BO = 32;\n    const int BW = 2 * 64;\n    const int TO = 4;\n    const int TC = 4;\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TO * TC));\n    dweight_backward_kernel1<BC, BO, BW, TO, TC><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(cudaGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// instead of having to load multiples of entire widths of the image into smem\n// we now only load fractions of the width each time\n// so the outer loop needs to loop over the height multiples (NH - 1) as well as the width multiples (WS - 1)\n// results: This did seem to help with memory access: compute intensity is higher\n// right now this one is much slower than kernel 1\n// I think this is because the smem loads are now not aligned, which causes a lot of uncoalesced loads\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel2(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    assert(BW % WSZ == 0);\n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        for (int w_offset = 0; w_offset < W; w_offset += (WSZ - 1)) {\n            // load dout into smem\n            int h_offset = h * W;\n            int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n            int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n            int dout_load_sz = nh * wsz * BO;\n            for (int i = threadIdx.x; i < dout_load_sz; i++) {\n                int inner_w = i % wsz;\n                int inner_h = (i / wsz) % nh;\n                int inner_o = i / (wsz * nh);\n                dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                    dout[dout_block_offset + h_offset + inner_h * W + w_offset + inner_w + inner_o * H * W];\n            }\n            \n            // load x to smem\n            int x_load_sz = nh * wsz * BC;\n            for (int i = threadIdx.x; i < x_load_sz; i++) {\n                int inner_w = i % wsz;\n                int inner_h = (i / wsz) % nh;\n                int inner_c = i / (wsz * nh);\n                x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                    x[x_block_offset + h_offset + inner_h * W + w_offset + inner_w + inner_c * H * W];\n            }\n\n            __syncthreads();\n\n            // if loading the last block, fill in the last block's buffer to be 0\n            if (nh == NH - 1) {\n                for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_o = i / WSZ;\n                    dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_c = i / WSZ;\n\n                    x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n                } \n            }\n            // if loading the last width section, fill in the final value in the buffer to 0\n            if (wsz == WSZ - 1)  {\n                for (int i = threadIdx.x; i < nh * BO; i++) {\n                    int inner_o = i % BO;\n                    int inner_h = i / BO;\n                    dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < nh * BC; i++) {\n                    int inner_c = i % BC;\n                    int inner_h = i / BC;\n                    x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n                }\n            }\n            __syncthreads();\n\n            // calculate per thread results\n            for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n                int x_h = x_dot_idx / WSZ;\n                int x_w = x_dot_idx % WSZ;\n                // load TC of the C channels of one image pixel into reg_w\n                for (int j = 0; j < TC; j++) {\n                    reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n                }\n\n                // for each conv filter, load a different row and calculate the outer product\n                for (int k = 0; k < 9; k++) {\n                    int k_i = k / 3;\n                    int k_j = k % 3;\n                    // calculate the corresponding (h, w) that should be loaded from dout\n                    int dout_h = x_h + 1 - k_i;\n                    int dout_w = x_w + 1 - k_j;\n                    int dout_h_abs = h + dout_h;\n                    int dout_w_abs = w_offset + dout_w;\n                    int dout_dot_idx = -1;\n                    if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                        if (x_h == NH - 1 && k_i == 1) {\n                            // this logic is a bit tricky: for the last row in smem\n                            // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                            // for kernels 4, 5, 6 we need to only compute them once\n                            // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                            // so it will be computed as part of the first row next time\n                            dout_dot_idx = -1;\n                        } else if (x_w == WSZ - 1 && k_j == 1) {\n                            dout_dot_idx = -1;\n                        } else {\n                            dout_dot_idx = dout_h * WSZ + dout_w;\n                        }\n                    }\n\n                    // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                    // and add the products to the thread results\n                    // otherwise the contribution is 0\n                    if (dout_dot_idx != -1) {\n                        for (int i = 0; i < TO; i++) {\n                            assert(dout_dot_idx * BO + thread_row * TO + i < BO * BW);\n                            reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                        }\n                        for (int j = 0; j < TC; j++) {\n                            for (int i = 0; i < TO; i++) {\n                                float new_val = reg_dout[i] * reg_x[j];\n                                thread_results[k * TO * TC + i * TC + j] += new_val;\n                            }\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward2(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 32;\n    const int BO = 32;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel2<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(cudaDeviceSynchronize());\n    cudaCheck(cudaGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// kernel 2 does something silly: because are now loading WSZ sections of widths,\n// and WSZ is definitely not a nice power of 2, we need to be careful\n// which thread we are using when loading global memory.\n// This kernel only changes the way loading is done compared to kernel 2\n// effectively we do some warp tiling when loading from global mem\n// kernel 3 is now quite fast (2.6ms)\n// profiling tells us that we are now using very few blocks, so seems like a good idea\n// to spread out the different width sections on different blocks and do a bigger reduce afterwards\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel3(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        for (int w_offset = 0; w_offset < W; w_offset += (WSZ - 1)) {\n            // load dout into smem\n            int h_offset = h * W;\n            int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n            int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n            int o_per_warp = ceil_div(BO, n_warps);\n            if (lane_id < wsz) {\n                for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                    int inner_o = warp_id * o_per_warp + warp_o;\n                    if (inner_o < BO) {\n                        for (int inner_h = 0; inner_h < nh; inner_h++) {\n                            int inner_w = lane_id;\n                            dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                                dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                        }\n                    }\n                }\n            }\n\n            int c_per_warp = ceil_div(BC, n_warps);\n            if (lane_id < wsz) {\n                for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                    int inner_c = warp_id * c_per_warp + warp_c;\n                    if (inner_c < BC) {\n                        for (int inner_h = 0; inner_h < nh; inner_h++) {\n                            int inner_w = lane_id;\n                            x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                                x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                        }\n                    }\n                }\n            }\n\n            __syncthreads();\n            \n\n            // if loading the last block, fill in the last block's buffer to be 0\n            if (nh == NH - 1) {\n                for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_o = i / WSZ;\n                    dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_c = i / WSZ;\n\n                    x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n                } \n            }\n            // if loading the last width section, fill in the final value in the buffer to 0\n            if (wsz == WSZ - 1)  {\n                for (int i = threadIdx.x; i < nh * BO; i++) {\n                    int inner_o = i % BO;\n                    int inner_h = i / BO;\n                    dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < nh * BC; i++) {\n                    int inner_c = i % BC;\n                    int inner_h = i / BC;\n                    x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n                }\n            }\n            __syncthreads();\n\n            // calculate per thread results\n            for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n                int x_h = x_dot_idx / WSZ;\n                int x_w = x_dot_idx % WSZ;\n                // load TC of the C channels of one image pixel into reg_w\n                for (int j = 0; j < TC; j++) {\n                    reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n                }\n\n                // for each conv filter, load a different row and calculate the outer product\n                for (int k = 0; k < 9; k++) {\n                    int k_i = k / 3;\n                    int k_j = k % 3;\n                    // calculate the corresponding (h, w) that should be loaded from dout\n                    int dout_h = x_h + 1 - k_i;\n                    int dout_w = x_w + 1 - k_j;\n                    int dout_h_abs = h + dout_h;\n                    int dout_w_abs = w_offset + dout_w;\n                    int dout_dot_idx = -1;\n                    if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                        if (x_h == NH - 1 && k_i == 1) {\n                            // this logic is a bit tricky: for the last row in smem\n                            // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                            // for kernels 4, 5, 6 we need to only compute them once\n                            // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                            // so it will be computed as part of the first row next time\n                            dout_dot_idx = -1;\n                        } else if (x_w == WSZ - 1 && k_j == 1) {\n                            dout_dot_idx = -1;\n                        } else {\n                            dout_dot_idx = dout_h * WSZ + dout_w;\n                        }\n                    }\n\n                    // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                    // and add the products to the thread results\n                    // otherwise the contribution is 0\n                    if (dout_dot_idx != -1) {\n                        for (int i = 0; i < TO; i++) {\n                            reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                        }\n                        for (int j = 0; j < TC; j++) {\n                            for (int i = 0; i < TO; i++) {\n                                float new_val = reg_dout[i] * reg_x[j];\n                                thread_results[k * TO * TC + i * TC + j] += new_val;\n                            }\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward3(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel3<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(cudaDeviceSynchronize());\n    cudaCheck(cudaGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// trying to improve on kernel 3 by splitting the widths of each image\n// to be calculated on different blocks\n// Output will now be of shape (O, C, 9, B, W / (WSZ - 1))\n// and we will need to reduce over the last two dimensions\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel4(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1)  {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward4(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel4<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(cudaDeviceSynchronize());\n    cudaCheck(cudaGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n}\n\n// same as kernel 4, but vectorize loads to speed up smem load\n// unfortunately as is this doesn't give any speedup\n// probably because we are now using too few threads to load\n// I could try to shuffle and make each warp load more heights as well, but that doesn't seem first order right now\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel5(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < ceil_div(wsz, 4)) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        float4 tmp = \n                            reinterpret_cast<float4 *>(&dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + lane_id * 4])[0];\n                        dout_s[(inner_h * WSZ + lane_id * 4 + 0) * BO + inner_o] = tmp.x;\n                        if (lane_id < wsz / 4) {\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 1) * BO + inner_o] = tmp.y;\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 2) * BO + inner_o] = tmp.z;\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 3) * BO + inner_o] = tmp.w;\n                        }\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < ceil_div(wsz, 4)) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        float4 tmp = \n                            reinterpret_cast<float4 *>(&x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + lane_id * 4])[0];\n                        x_s[(inner_h * WSZ + lane_id * 4 + 0) * BC + inner_c] = tmp.x;\n                        if (lane_id < wsz / 4) {\n                            x_s[(inner_h * WSZ + lane_id * 4 + 1) * BC + inner_c] = tmp.y;\n                            x_s[(inner_h * WSZ + lane_id * 4 + 2) * BC + inner_c] = tmp.z;\n                            x_s[(inner_h * WSZ + lane_id * 4 + 3) * BC + inner_c] = tmp.w;\n                        }\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1)  {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward5(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel5<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(cudaDeviceSynchronize());\n    cudaCheck(cudaGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n}\n\n// same as dweight backward 4, except we also do some of the reduce for dbias\n// so it doesn't have to reduce an entire BxHxW length for each O channel\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_dbias_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf, float* dbias_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = ceil_div((int)blockDim.x, 32);\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n    float thread_bias_results[TO] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    int h_step = (NH < H) ? NH - 1 : H;\n    // if NH == H, then we are loading the entire image in one go, so step by H\n    for (int h = 0; h < H; h += h_step) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1) {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1 && H > NH) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1 && W > WSZ) {\n                        // if x_w is at the last pixel, and we have more than one width sections, and we are not are the last section\n                        // then don't compute 1, 4, 7 kernels and wait for the next section to compute them\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                    // update dbias when k = 4, where dout_h and dout_w match x_h and x_w\n                    if (k == 4 && block_col == 0) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_bias_results[i] += reg_dout[i];\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out dweight results\n    for (int i = 0; i < TO; i++) {\n        int o_abs = block_row * BO + thread_row * TO + i;\n        for (int j = 0; j < TC; j++) {\n            int c_abs = block_col * BC + thread_col * TC + j;\n            for (int k = 0; k < 9; k++) {\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n        // write out dbias results\n        if (block_col == 0) {\n            dbias_buf[o_abs * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_bias_results[i];\n        }\n    }\n}\n\n\n__global__ void dbias_reduce_kernel1(\n    float* dbias_buf, float* dbias,\n    const int B\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    dbias_buf += blockIdx.x * B;\n\n    int lane_id = threadIdx.x % 32;\n\n    float db_thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        db_thread_sum += dbias_buf[i];\n    }\n\n    float db_warp_sum = cg::reduce(warp, db_thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dbias[blockIdx.x] = db_warp_sum;\n    }\n}\n\nvoid dweight_dbias_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    float* dbias_buf, float* dbias,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    if (H * W == 64) {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8;\n        const int BW = WSZ * 8;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B);\n    } else if (C == 3) {\n        const int BC = 3;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 3;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else if (O == 3) {\n        const int BC = 64;\n        const int BO = 3;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 3;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(cudaDeviceSynchronize());\n        cudaCheck(cudaGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    }\n}\n\nvoid conv2d_k3_backward2(\n    float* dout, float* x, float* weight,\n    float* dweight_buf, float* dbias_buf,\n    float* dx, float* dweight, float* dbias,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // dx currently requires the output buffer to be zeroed\n    cudaCheck(cudaMemset(dx, 0, B * C_in * H * W * sizeof(float)));\n    dx_backward(dout, weight, dx, B, C_in, C_out, H, W);\n    dweight_dbias_backward1(dout, x, dweight_buf, dweight, dbias_buf, dbias, B, C_in, C_out, H, W);\n}\n// ----------------------------------------------------------------------------\n// kernel launcher\nvoid conv2d_k3_forward(\n    cublasHandle_t cublas_handle,\n    int kernel_num,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            conv2d_k3_forward1(cublas_handle, x, weight, bias, out, B, C_in, C_out, H, W, block_size);\n            break;\n        case 2:\n            conv2d_k3_forward2(cublas_handle, x, weight, bias, out, B, C_in, C_out, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            break;\n    }\n}\n\n\n#ifndef LINKING\nint main( int argc, char **argv) {\n    const int B = 32;\n    const int C_in = 192;\n    const int C_out = 64;\n    const int H = 64;\n    const int W = 64;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n    printf(\"device registers per block: %d\\n\", deviceProp.regsPerBlock);\n    printf(\"shared mem per block: %ld\\n\", deviceProp.sharedMemPerBlock);\n\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* weight = (float*)malloc(C_out * C_in * 3 * 3 * sizeof(float));\n    float* bias = (float*)malloc(C_out * sizeof(float));\n    float* out = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dx = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* dweight = (float*)malloc(C_out * C_in * 3 * 3 * sizeof(float));\n    float* dbias = (float*)malloc(C_out * sizeof(float));\n    // debug\n    float* dout_perm = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* x_tiled = (float*)malloc(B * H * W * C_in * 9 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"conv2d_k3.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C_in * H * W, file);\n    freadCheck(weight, sizeof(float), C_out * C_in * 3 * 3, file);\n    freadCheck(bias, sizeof(float), C_out, file);\n    freadCheck(out, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dout, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dx, sizeof(float), B * C_in * H * W, file);\n    freadCheck(dweight, sizeof(float), C_out * C_in * 3 * 3, file);\n    freadCheck(dbias, sizeof(float), C_out, file);\n    freadCheck(dout_perm, sizeof(float), B * C_out * H * W, file);\n    freadCheck(x_tiled, sizeof(float), B * H * W * C_in * 9, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(cudaMalloc(&d_x, B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_weight, C_out * C_in * 3 * 3 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_bias, C_out * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * C_out * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * C_out * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dweight, C_out * C_in * 3 * 3 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dbias, C_out * sizeof(float)));\n\n    cudaCheck(cudaMemset(d_out, 0, B * C_out * H * W * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_weight, weight, C_out * C_in * 3 * 3 * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_bias, bias, C_out * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * C_out * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n    // read kernel number from command line\n    int kernel_num = 2;\n    if (argc > 1) {\n        kernel_num = atoi(argv[1]);\n    }\n    printf(\"Running kernel %d\\n\", kernel_num);\n\n    printf(\"Checking forward pass\\n\");\n    float forward_acc = 1e-1;\n\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n    //    printf(\"\\nBlock size: %d\\n\", block_size);\n    //    conv2d_k3_forward(cublas_handle, kernel_num, d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W, block_size);\n    //    validate_result(d_out, out, \"out\", B * C_out * H * W, forward_acc);\n    //}\n    //printf(\"\\nChecking cpu forward\\n\");\n    //float* out_cpu_buf = (float*)malloc(B * C_out * H * W * sizeof(float));\n    //conv_k3_forward_cpu(x, weight, bias, out_cpu_buf, B, C_in, C_out, H, W);\n    //float* out_gpu_buf;\n    //cudaCheck(cudaMalloc(&out_gpu_buf, B * C_out * H * W * sizeof(float)));\n    //cudaCheck(cudaMemcpy(out_gpu_buf, out, B * C_out * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    //validate_result(out_gpu_buf, out, \"out cpu\", B * C_out * H * W);\n    //cudaCheck(cudaFree(out_gpu_buf));\n    //free(out_cpu_buf);\n\n    printf(\"\\nChecking new kernel\\n\");\n    conv2d_k3_forward3(d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W);\n    cudaCheck(cudaGetLastError());\n    printf(\"\\nChecking new kernel results\\n\");\n    validate_result(d_out, out, \"out\", B * C_out * H * W, forward_acc);\n    printf(\"Forward pass successful\\n\\n\");\n\n    printf(\"\\nmanually benchmark new forward kernel\\n\");\n    void* flush_buffer;\n    cudaCheck(cudaMalloc(&flush_buffer, deviceProp.l2CacheSize));\n    int repeat_times = 1000;\n    float new_time = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        cudaEvent_t start, end;\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end));\n        cudaCheck(cudaEventRecord(start, nullptr));\n        conv2d_k3_forward3(d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W);\n        cudaCheck(cudaEventRecord(end, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n\n    printf(\"Checking backward pass\\n\");\n\n    float dw_acc = 1e-2;\n    float* d_dweight_buf;\n    cudaCheck(cudaMalloc(&d_dweight_buf, C_out * C_in * 3 * 3 * B * 32 * sizeof(float)));\n    float* d_dbias_buf;\n    cudaCheck(cudaMalloc(&d_dbias_buf, C_out * 256 * sizeof(float)));\n    conv2d_k3_backward2(d_dout, d_x, d_weight, d_dweight_buf, d_dbias_buf, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W);\n\n    printf(\"\\nChecking dweight\\n\");\n    validate_result(d_dweight, dweight, \"dweight\", C_out * C_in * 3 * 3, dw_acc);\n\n    printf(\"\\nChecking dbias\\n\");\n    validate_result(d_dbias, dbias, \"dbias\", C_out, dw_acc);\n\n    printf(\"\\nChecking dx\\n\");\n    validate_result(d_dx, dx, \"dx\", B * C_in * H * W, dw_acc);\n\n    new_time = 0.0f;\n    printf(\"\\nManually benchmarking new dweight kernel\\n\");\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        cudaEvent_t start, end;\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end));\n        cudaCheck(cudaEventRecord(start, nullptr));\n        conv2d_k3_backward2(d_dout, d_x, d_weight, d_dweight_buf, d_dbias_buf, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W);\n        cudaCheck(cudaEventRecord(end, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n    cudaCheck(cudaFree(d_dweight_buf));\n    cudaCheck(cudaFree(d_dbias_buf));\n\n\n\n    printf(\"Backward pass successful\\n\");\n\n    // old benchmarking code\n    //printf(\"All results match. Starting benchmarks.\\n\\n\");\n    //printf(\"Forward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k3_forward,\n    //                                          cublas_handle, kernel_num, d_x, d_weight, d_bias, d_out,\n    //                                          B, C_in, C_out, H, W, block_size);\n\n    //    float tflops = (float)B * H * W * C_in * C_out * 9 * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"\\n\\n Old benchmarks\\n\");\n    printf(\"Running our own forward benchmarks\\n\");\n    float t1 = 0.0f;\n    float t2 = 0.0f;\n    float t3 = 0.0f;\n    float t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k3_forward2(cublas_handle, d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W, 512, &t1, &t2, &t3);\n    }\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t_total = t1 + t2 + t3;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    //printf(\"\\nBackward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k3_backward1,\n    //                                          cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias,\n    //                                          B, C_in, C_out, H, W, block_size);\n\n    //    float tflops = (float)B * H * W * C_in * C_out * (9 + 1) * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"\\nRunning our own backward benchmarks\\n\");\n    t1 = 0.0f;\n    t2 = 0.0f;\n    t3 = 0.0f;\n    float t4 = 0.0f;\n    float t5 = 0.0f;\n    float t6 = 0.0f;\n    t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k3_backward1(cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W, 512, &t1, &t2, &t3, &t4, &t5, &t6);\n    }\n    cudaCheck(cudaFree(flush_buffer));\n\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t4 /= repeat_times;\n    t5 /= repeat_times;\n    t6 /= repeat_times;\n    t_total = t1 + t2 + t3 + t4 + t5 + t6;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t4: %.4f ms\\n\", t4);\n    printf(\"t5: %.4f ms\\n\", t5);\n    printf(\"t6: %.4f ms\\n\", t6);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    // free memory\n    free(x);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dx);\n    free(dweight);\n    free(dbias);\n    free(dout_perm);\n    free(x_tiled);\n    cudaCheck(cudaFree(d_x));\n    cudaCheck(cudaFree(d_weight));\n    cudaCheck(cudaFree(d_bias));\n    cudaCheck(cudaFree(d_out));\n    cudaCheck(cudaFree(d_dout));\n    cudaCheck(cudaFree(d_dx));\n    cudaCheck(cudaFree(d_dweight));\n    cudaCheck(cudaFree(d_dbias));\n    cublasCheck(cublasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "output": "#include <cstdio>\n#include <cmath>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <cuda_device_runtime_api.h>\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include <assert.h>\n#include \"common.h\"\n#include \"conv2d_k3.cuh\"\n\n\n__global__ void permute_w_tile_x_kernel1(\n    const float* x, const float* weight,\n    float* x_tiled, float* weight_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < C_out * C_in * 9) {\n        // permute weight\n        int c_out = idx / (C_in * 9);\n        int c_in = (idx / 9) % C_in;\n        int k = idx % 9;\n        int out_ind = k * C_out * C_in + c_out * C_in + c_in;\n        weight_perm[out_ind] = weight[idx];\n    }\n\n    if (idx < B * C_in * H * W) {\n        // tile x to shape (9, C_in, B, H, W)\n        // for a given first dimension k, the entry at an index should be\n        // what the k-th kernel is multiplied with in the convolution for that pixel\n        // (not a good explanation... but obvious with a picture)\n        // for example, a value at (H, W) = (0, 0) will never be convolved with kernels 5, 7 and 8\n        // because it is on a corner\n        int x_offset = B * C_in * H * W;\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        float x_val = x[idx];\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[c * B * img_size + b * img_size + h_end * W + w_end] = x_val;\n                }\n                // shift pointer to next kernel\n                x_tiled += x_offset;\n            }\n        }\n    }\n}\n\n\n__global__ void reduce_transpose_add_bias_kernel1(\n    const float* out_tmp, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    // make sure we're in bound\n    assert(idx < B * C_out * H * W);\n    // make sure every block lies in the same channel\n    assert((B * H * W) % blockDim.x == 0);\n\n    int img_size = H * W;\n    int c = idx / (B * img_size);\n    int b = (idx / img_size) % B;\n    int h = (idx / W) % H;\n    int w = idx % W;\n\n    __shared__ float bias_sh[1];\n    if (threadIdx.x == 0) {\n        bias_sh[0] = bias[c];\n    }\n    __syncthreads();\n\n    float out_val = bias_sh[0];\n\n    for (int k = 0; k < 9; k++) {\n        float out_tmp_val = out_tmp[k * C_out * B * img_size + c * B * img_size + b * img_size + h * W + w];\n        out_val += out_tmp_val;\n    }\n\n    out[b * C_out * img_size + c * img_size + h * W + w] = out_val;\n}\n\nvoid conv2d_k3_forward1(\n    hipblasHandle_t cublas_handle,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // implements Conv2D with kernel size 1 and padding 1\n    // shapes:\n    // x (B, C_in, H, W)\n    // weight (C_out, C_in, 3, 3)\n    // bias (C_out,)\n\n    // plan:\n    // 1. transpose and tile x into x_tiled of shape (9, C_in, B, H, W)\n    // 2. transpose weight into shape (9, C_out, C_in) (we will do this in the same kernel as one above)\n    // 3. call cublasSgemmStridedBatched with 9 batches\n    // 4. reduce over the 9 kernel dimensions, add bias, and transpose output to shape (B, C_out, H, W)\n\n    // 1. and 2.\n    // transpose weight and tile x\n    float *x_tiled, *weight_perm;\n    cudaCheck(hipMalloc(&x_tiled, 9 * C_in * B * H * W * sizeof(float)));\n    cudaCheck(hipMemset(x_tiled, 0, 9 * C_in * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&weight_perm, 9 * C_out * C_in * sizeof(float)));\n    int total_th_1 = max_int(B * C_in * H * W, C_in * C_out * 9);\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    permute_w_tile_x_kernel1<<<n_blk_1, block_size>>>(x, weight, x_tiled, weight_perm, B, C_in, C_out, H, W);\n\n    // 3. matmul via cublasSgemmStridedBatched\n    float* out_tmp;\n    cudaCheck(hipMalloc(&out_tmp, 9 * C_out * B * H * W * sizeof(float)));\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, bhw, C_out, C_in, &alpha, x_tiled, bhw, C_in * bhw, weight_perm, C_in, C_out * C_in, &beta, out_tmp, bhw, C_out * bhw, 9));\n\n    // reduce, add bias, and transpose\n    int total_th_2 = B * C_out * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    reduce_transpose_add_bias_kernel1<<<n_blk_2, block_size>>>(out_tmp, bias, out, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(hipFree(x_tiled));\n    cudaCheck(hipFree(weight_perm));\n    cudaCheck(hipFree(out_tmp));\n}\n\n\n__global__ void transpose_tile_x_kernel2(\n    const float* x,\n    float* x_tiled,\n    int B, int C_in, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * C_in * H * W) {\n        // similar to permute_w_tile_x_kernel1, except now the target shape is (C_in, 9, B, H, W)\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        float x_val = x[idx];\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[c * 9 * B * img_size + (i * 3 + j) * B * img_size + b * img_size + h_end * W + w_end] = x_val;\n                }\n            }\n        }\n    }\n}\n\n__global__ void transpose_out_add_bias_kernel2(\n    const float* out_tmp, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    // make sure we're in bound\n    assert(idx < B * C_out * H * W);\n    // make sure every block lies in the same channel\n    assert((B * H * W) % blockDim.x == 0);\n\n    int img_size = H * W;\n    int c = idx / (B * img_size);\n    int b = (idx / img_size) % B;\n    int h = (idx / W) % H;\n    int w = idx % W;\n\n    __shared__ float bias_sh[1];\n    if (threadIdx.x == 0) {\n        bias_sh[0] = bias[c];\n    }\n    __syncthreads();\n    \n    out[b * C_out * img_size + c * img_size + h * W + w] = bias_sh[0] + out_tmp[idx];\n}\n\nvoid conv2d_k3_forward2(\n    hipblasHandle_t cublas_handle,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size,\n    float* t1, float* t2, float* t3\n) {\n    // similar to kernel 1, but transpose into a different shape so that we can just make one big matmul call\n    // and no need to reduce afterwards\n    // plan:\n    // 1. transpose and tile x into x_tiled of shape (C_in, 9, B, H, W)\n    // 2. call cublasSgemm on x_tiled and weight, no need for transposes!\n    // 3. add bias\n\n    // 1. transpose x to x_tiled\n    hipEvent_t start, end1, end2, end3;\n    if (t1 != nullptr) {\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end1));\n        cudaCheck(hipEventCreate(&end2));\n        cudaCheck(hipEventCreate(&end3));\n        cudaCheck(hipEventRecord(start, nullptr));\n    }\n    float* x_tiled;\n    cudaCheck(hipMalloc(&x_tiled, C_in * 9 * B * H * W * sizeof(float)));\n    cudaCheck(hipMemset(x_tiled, 0, C_in * 9 * B * H * W * sizeof(float)));\n    if (t1 != nullptr) {\n        cudaCheck(hipEventRecord(end1, nullptr));\n    }\n    int total_th_1 = B * C_in * H * W;\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    transpose_tile_x_kernel2<<<n_blk_1, block_size>>>(x, x_tiled, B, C_in, H, W);\n\n    // 2. matmul with cublasSgemm\n    float* out_tmp;\n    cudaCheck(hipMalloc(&out_tmp, C_out * B * H * W * sizeof(float)));\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, bhw, C_out, C_in * 9, &alpha, x_tiled, bhw, weight, C_in * 9, &beta, out_tmp, bhw));\n\n\n    // 3. transpose and add bias\n    int total_th_2 = B * C_out * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    transpose_out_add_bias_kernel2<<<n_blk_2, block_size>>>(out_tmp, bias, out, B, C_out, H, W);\n    if (t2 != nullptr) {\n        cudaCheck(hipEventRecord(end2, nullptr));\n    }\n\n\n    // free memory\n    cudaCheck(hipFree(x_tiled));\n    cudaCheck(hipFree(out_tmp));\n    if (t3 != nullptr) {\n        cudaCheck(hipEventRecord(end3, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end1));\n        cudaCheck(hipEventSynchronize(end2));\n        cudaCheck(hipEventSynchronize(end3));\n        float elapsed_time1, elapsed_time2, elapsed_time3;\n        cudaCheck(hipEventElapsedTime(&elapsed_time1, start, end1));\n        cudaCheck(hipEventElapsedTime(&elapsed_time2, end1, end2));\n        cudaCheck(hipEventElapsedTime(&elapsed_time3, end2, end3));\n        *t1 += elapsed_time1;\n        *t2 += elapsed_time2;\n        *t3 += elapsed_time3;\n    }\n}\n\n// this kernel will roughly follow kernel 5 here: https://github.com/siboehm/SGEMM_CUDA, with a few differences\n// the main operation is convolving the input X with the weights\n// weights are shaped (O, Cx9), and X is shaped (B, C, HxW)\n// We are morally doing a matmul with dimensions O, C, HxW\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel1(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_i * w_s_width + w_j] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(thread_row * TO + i) * w_s_width + dot_idx * 9 + conv_idx];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n\n// almost same as 1, except we load A as transposed\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    const int C9 = C * 9;\n    const int weight_size = O * C * 9;\n    const int x_size = B * C * H * W;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        int w_abs_idx;\n        for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n            int w_i = i / w_s_width;\n            int w_j = i % w_s_width;\n            w_abs_idx = w_block_offset + w_inner_offset + w_i * C9 + w_j;\n            if (w_abs_idx < weight_size) {\n                w_s[w_j * BO + w_i] = weight[w_abs_idx];\n            }\n        }\n        // load x into smem\n        int x_abs_idx;\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            if (x_abs_idx < x_size) {\n                x_s[x_i * BH + x_j] = x[x_abs_idx];\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n\n// almost same as 2, but we vectorize the loads and use float4\ntemplate <const int BO, const int BH, const int BC, const int TO, const int TH>\n__global__ void conv2d_k3_forward_kernel3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // constants\n    assert(TH == 4); // needed for reg_x loads later, so we can do one vectorized load for each thread\n    const int C9 = C * 9;\n    // n_threads / block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // we will set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // for compute step, to determine which of the size TOxTH block the thread is responsible for\n    const int threads_per_col = ceil_div(BH, TH);\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for loading weights and X\n    const int w_s_sz = BO * BC * 9;\n    const int w_s_width = BC * 9;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // registers to store local results\n    float thread_results[TO * TH * 9] = {0.0};\n    // registers for weights and x\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // calculate offset in x for bounds checking later\n    const int x_block_offset = batch * C * H * W + block_col * BH;\n    // calculate weight offset for bounds checking\n    const int w_block_offset = block_row * BO * C9;\n\n    // outer loop over BC size blocks of channel C\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        // x and weight offsets for this block\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx * 9;\n        // load weight into smem\n        for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (w_s_width / 4);\n            int inner_row = i / (w_s_width / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            w_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            w_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            w_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            w_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        // load x into smem\n        for (int i = threadIdx.x; i < x_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&x_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            // load x_s into registers\n            // NOTE: relying on the fact that TH = 4\n            reinterpret_cast<float4 *>(reg_x)[0] = reinterpret_cast<float4 *>(&x_s[dot_idx * BH + thread_col * TH])[0];\n            for (int conv_idx = 0; conv_idx < 9; conv_idx++) {\n                for (int i = 0; i < TO; i++) {\n                    reg_w[i] = w_s[(dot_idx * 9 + conv_idx) * BO + thread_row * TO + i];\n                }\n\n                for (int i = 0; i < TO; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        float val = reg_w[i] * reg_x[j];\n                        thread_results[conv_idx * TO * TH + i * TH + j] += val;\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // need to be careful here: for each pixel at (h_abs, w_abs) and its product with kernel k (k < 9)\n    // There are 2 options\n    // 1. the product should be added to some pixel, then do an atomiAdd\n    // 2. the product should not contribute to any pixel because it would be out of bound, in this case just skip\n    int out_batch_offset = batch * O * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TO; i++) {\n            int o_abs = block_row * BO + thread_row * TO + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs + 1 - k_i;\n                int w_target_abs = w_abs + 1 - k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + o_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TO * TH + i * TH + j;\n                    atomicAdd(out + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n            int out_abs = out_batch_offset + o_abs * H * W + h_abs * W + w_abs;\n            atomicAdd(out + out_abs, bias[o_abs]);\n        }\n    }\n}\n\n// CPU version for debugging\nvoid conv_k3_forward_cpu(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int OC, int H, int W\n) {\n    for (int b = 0; b < B; b++) {\n        for (int oc = 0; oc < OC; oc++) {\n            for (int h = 0; h < H; h++) {\n                for (int w = 0; w < W; w++) {\n                    for (int k = 0; k < 9; k++) {\n                        // check if the output will actually be used, if not skip\n                        int k_i = k / 3;\n                        int k_j = k % 3;\n                        int h_target = h + 1 - k_i;\n                        int w_target = w + 1 - k_j;\n                        if (h_target < 0 || h_target >= H || w_target < 0 || w_target >= W) {\n                            continue;\n                        }\n                        float out_bohw = 0.0f;\n                        for (int c = 0; c < C; c++) {\n                            float new_val = weight[oc * C * 9 + c * 9 + k] * x[b * C * H * W + c * H * W + h * W + w];\n                            out_bohw += new_val;\n                            //if (b == 0 && h == 0 && w == 1 && k == 5 && oc == 0) {\n                            //    printf(\"(0, 1) OC 0 kern 5: contribution from %d: %f, out val %f\\n\", c, new_val, out_bohw);\n                            //}\n                        }\n                        int out_idx = b * OC * H * W + oc * H * W + h_target * W + w_target;\n                        //if (h_target == 0 && w_target == 0 && oc == 0 && b == 0) {\n                        //    printf(\"(0, 0) getting contribution from (%d, %d) at k %d: %f, prev_val %f\\n\", h, w, k, out_bohw, out[out_idx]);\n                        //}\n                        out[out_idx] += out_bohw;\n                        //if (h_target == 0 && w_target == 0 && oc == 0 && b == 0) {\n                        //    printf(\"(0, 0) getting contribution from (%d, %d) at k %d: %f, new val %f\\n\", h, w, k, out_bohw, out[out_idx]);\n                        //}\n                    }\n                    \n                    //if (h == 0 && w == 0 && oc == 0 && b == 0) {\n                    //    printf(\"(0, 0) adding bias %f\\n\", bias[oc]);\n                    //}\n                    out[b * OC * H * W + oc * H * W + h * W + w] += bias[oc];\n                }\n            }\n            printf(\"(0, 0) final value: %f\\n\", out[0]);\n            return;\n        }\n    }\n}\n\nvoid conv2d_k3_forward3(\n    float* x, float* weight, float* bias,\n    float* out,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // since the kernel will add to the output, we need to zero it first\n    cudaCheck(hipMemset(out, 0, B * C_out * H * W * sizeof(float)));\n\n    if (H * W == 64) {\n        const int BH = 64;\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_in == 3) {\n        const int BH = 64;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        const int BC = 3;\n        assert(C_out % 64 == 0);\n        const int BO = 64;\n        const int TO = 4;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else if (C_out == 3) {\n        const int BH = 64 * 2;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_in % 8 == 0);\n        const int BC = 8;\n        const int BO = 3;\n        const int TO = 3;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel2<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    } else {\n        const int BH = 64 * 4;\n        assert(H * W % BH == 0);\n        const int TH = 4;\n        assert(C_out % 64 == 0 && C_in % 8 == 0);\n        const int BC = 16;\n        const int BO = 8;\n        const int TO = 2;\n        dim3 gridDim(B * H * W / BH, C_out / BO);\n        dim3 blockDim((BO * BH) / (TO * TH));\n        conv2d_k3_forward_kernel3<BO, BH, BC, TO, TH><<<gridDim, blockDim>>>(\n            x, weight, bias, out, B, C_in, C_out, H, W\n        );\n    }\n    cudaCheck(hipGetLastError());\n}\n\n// ----------------------------------------------------------------------------\n// backward pass\n\n__global__ void permute_dout_tile_x_kernel(\n    const float* dout, const float* x,\n    float* dout_perm, float* x_tiled,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx < B * C_out * H * W) {\n        // permute dout\n        int b = idx / (C_out * img_size);\n        int c = (idx / img_size) % C_out;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        dout_perm[c * B * img_size + b * img_size + h * W + w] = dout[idx];\n    }\n\n    if (idx < B * C_in * H * W) {\n        // tile x_tiled, see permute_w_tile_x_kernel1 for explanation\n        // except that we tile into a shape of (B, H, W, C_in, 9), the transpose of the tiling there\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_start = (idx / W) % H;\n        int w_start = idx % W;\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_end = h_start + 1 - i;\n                int w_end = w_start + 1 - j;\n                if (h_end >= 0 && h_end < H && w_end >= 0 && w_end < W) {\n                    x_tiled[b * img_size * C_in * 9 + h_end * W * C_in * 9 + w_end * C_in * 9 + c * 9 + i * 3 + j]\n                        = x[idx];\n                }\n            }\n        }\n    }\n}\n\n__global__ void reduce_dx_kernel(\n    const float* dx_tiled,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * C_in * H * W) {\n        int img_size = H * W;\n        int b = idx / (C_in * img_size);\n        int c = (idx / img_size) % C_in;\n        int h_end = (idx / W) % H;\n        int w_end = idx % W;\n        float sum = 0.0f;\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                int h_start = h_end + 1 - i;\n                int w_start = w_end + 1 - j;\n                if (h_start >= 0 && h_start < H && w_start >= 0 && w_start < W) {\n                    sum += dx_tiled[c * 9 * B * img_size + (i * 3 + j) * B * img_size + b * img_size + h_start * W + w_start];\n                }\n            }\n        }\n        dx[idx] = sum;\n    }\n}\n\n\n__global__ void reduce_dbias_kernel1(\n    const float* dout_perm,\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    // dout_perm has shape (C_out, B, H, W)\n    // each block will reduce over one channel\n    dout_perm += blockIdx.x * B * H * W;\n    int bhw = B * H * W;\n    assert(blockDim.x <= bhw);\n    extern __shared__ float sdata[];\n    \n    float sum = 0.0f;\n    for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n        sum += dout_perm[i];\n    }\n    sdata[threadIdx.x] = sum;\n\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            sdata[threadIdx.x] += sdata[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = sdata[0];\n    }\n}\n\n// use cg to do warp reduce\n// doesn't really seem faster\n__global__ void reduce_dbias_kernel2(\n    const float* dout_perm,\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    assert(blockDim.x <= bhw);\n    // move dout_perm to the right block\n    dout_perm += blockIdx.x * bhw;\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32];\n\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n        thread_sum += dout_perm[i];\n    }\n    \n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n\n    // warp reduce\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = block_sum;\n    }\n}\n\nvoid conv2d_k3_backward1(\n    hipblasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size,\n    float* t1, float* t2, float* t3, float* t4, float* t5, float* t6\n) {\n    // shapes:\n    // dout (B, C_out, H, W)\n    // x (B, C_in, H, W)\n    // weight (C_out, C_in, 3, 3)\n    // bias (C_out,)\n\n    // we need to calculate dx, dweight, dbias separately\n    // dbias is a single reduce\n    // for dweight, we need to tile x into x_tile of shape (B * H * W, C_in * 9)\n    // then do a matmul with dout_perm (shape (C_out, B * H * W))\n    // for dx, we do dout_perm @ weight.T (store it in x_tile), and then reduce to get dx\n    // If we don't want to do a reduce when computing dx, we need to make 9 copies of dout\n    // which might be ok but increases memory usage. We can try it later.\n    hipEvent_t start, end1, end2, end3, end4, end5, end6;\n    if (t1 != nullptr) {\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end1));\n        cudaCheck(hipEventCreate(&end2));\n        cudaCheck(hipEventCreate(&end3));\n        cudaCheck(hipEventCreate(&end4));\n        cudaCheck(hipEventCreate(&end5));\n        cudaCheck(hipEventCreate(&end6));\n        cudaCheck(hipEventRecord(start, nullptr));\n    }\n\n    // 1. transpose to dout_perm and tile x_tile\n    float *dout_perm, *x_tiled;\n    cudaCheck(hipMalloc(&x_tiled, B * H * W * C_in * 9 * sizeof(float)));\n    cudaCheck(hipMemset(x_tiled, 0, B * H * W * C_in * 9 * sizeof(float)));\n    cudaCheck(hipMalloc(&dout_perm, B * C_out * H * W * sizeof(float)));\n    if (t1 != nullptr) {\n        cudaCheck(hipEventRecord(end1, nullptr));\n    }\n    int total_th_1 = max_int(C_in, C_out) * B * H * W;\n    int n_blk_1 = ceil_div(total_th_1, block_size);\n    permute_dout_tile_x_kernel<<<n_blk_1, block_size>>>(dout, x, dout_perm, x_tiled, B, C_in, C_out, H, W);\n    cudaCheck(hipGetLastError());\n    if (t2 != nullptr) {\n        cudaCheck(hipEventRecord(end2, nullptr));\n    }\n\n    // 2. get dweight by single matmul between x_tiled and dout_perm\n    float alpha = 1.0f;\n    float beta = 0.0f;\n    int bhw = B * H * W;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, C_in * 9, C_out, bhw, &alpha, x_tiled, C_in * 9, dout_perm, bhw, &beta, dweight, C_in * 9));\n\n    if (t2 != nullptr) {\n        cudaCheck(hipEventRecord(end3, nullptr));\n    }\n\n    // 3. get dx_tile by matmul between dout_perm and weight.T, and save output in x_tiled\n    // note: beta = 0 overwrites existing values in x_tiled\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, bhw, C_in * 9, C_out, &alpha, dout_perm, bhw, weight, C_in * 9, &beta, x_tiled, bhw));\n\n\n    // 4. reduce x_tiled to get dx\n    int total_th_2 = B * C_in * H * W;\n    int n_blk_2 = ceil_div(total_th_2, block_size);\n    reduce_dx_kernel<<<n_blk_2, block_size>>>(x_tiled, dx, B, C_in, H, W);\n\n    if (t2 != nullptr) {\n        cudaCheck(hipEventRecord(end4, nullptr));\n    }\n\n    // 5. reduce dbias\n    //reduce_dbias_kernel1<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n    reduce_dbias_kernel2<<<C_out, block_size>>>(dout_perm, dbias, B, C_out, H, W);\n\n    if (t5 != nullptr) {\n        cudaCheck(hipEventRecord(end5, nullptr));\n    }\n\n    // free memory\n    cudaCheck(hipFree(dout_perm));\n    cudaCheck(hipFree(x_tiled));\n    if (t5 != nullptr) {\n        cudaCheck(hipEventRecord(end6, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end1));\n        cudaCheck(hipEventSynchronize(end2));\n        cudaCheck(hipEventSynchronize(end3));\n        cudaCheck(hipEventSynchronize(end4));\n        cudaCheck(hipEventSynchronize(end5));\n        cudaCheck(hipEventSynchronize(end6));\n        float elapsed1, elapsed2, elapsed3, elapsed4, elapsed5, elapsed6;\n        cudaCheck(hipEventElapsedTime(&elapsed1, start, end1));\n        cudaCheck(hipEventElapsedTime(&elapsed2, end1, end2));\n        cudaCheck(hipEventElapsedTime(&elapsed3, end2, end3));\n        cudaCheck(hipEventElapsedTime(&elapsed4, end3, end4));\n        cudaCheck(hipEventElapsedTime(&elapsed5, end4, end5));\n        cudaCheck(hipEventElapsedTime(&elapsed6, end5, end6));\n        *t1 += elapsed1;\n        *t2 += elapsed2;\n        *t3 += elapsed3;\n        *t4 += elapsed4;\n        *t5 += elapsed5;\n        *t6 += elapsed6;\n    }\n}\n\n// almost the same as conv2d_k3_forward_kernel, except that the compute and the writes\n// are different, because now we want to treat the C dimension as the inner product dimension\ntemplate <const int BO, const int BH, const int BC, const int TC, const int TH>\n__global__ void dx_backward_kernel1(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int C9 = C * 9;\n    // n_threads per block = (BC * BH) / (TC * TH)\n    // set gridDim to (BxHxW / BH, C / TC)\n    // find the output block that this thread block is responsible for\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TCxTH block that the thread is responsible\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for weights and dout\n    const int w_s_width = BC * 9;\n    const int w_s_sz = BO * w_s_width;\n    const int dout_s_sz = BO * BH;\n    __shared__ float w_s[w_s_sz];\n    __shared__ float dout_s[dout_s_sz];\n\n    // registers for local results\n    float thread_results[TC * TH * 9] = {0.0};\n    // regs for weights and dout\n    float reg_w[TC * 9] = {0.0};\n    float reg_dout[TH] = {0.0};\n\n    // calculate the block offsets\n    const int dout_block_offset = batch * O * H * W + block_col * BH;\n    const int w_block_offset = block_row * w_s_width;\n\n    // outer loop over BO sizes of channel O\n    for (int o_idx = 0; o_idx < O; o_idx += BO) {\n        // offsets for this inner block\n        int w_inner_offset = o_idx * C9;\n        int dout_inner_offset = o_idx * H * W;\n\n        // load weights to smem\n        if (BC % 4 == 0) {\n            // vectorize dram load\n            for (int i = threadIdx.x; i < w_s_sz / 4; i += blockDim.x) {\n                int inner_col = i % (w_s_width / 4);\n                int inner_row = i / (w_s_width / 4);\n                reinterpret_cast<float4 *>(&w_s[inner_row * w_s_width + inner_col * 4])[0] = \n                    reinterpret_cast<float4 *>(&weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col * 4])[0];\n            }\n        } else {\n            for (int i = threadIdx.x; i < w_s_sz; i += blockDim.x) {\n                int inner_col = i % w_s_width;\n                int inner_row = i / w_s_width;\n                w_s[inner_row * w_s_width + inner_col] = weight[w_block_offset + w_inner_offset + inner_row * C9 + inner_col];\n            }\n        }\n        // load dout\n        for (int i = threadIdx.x; i < dout_s_sz / 4; i += blockDim.x) {\n            int inner_col = i % (BH / 4);\n            int inner_row = i / (BH / 4);\n            reinterpret_cast<float4 *>(&dout_s[inner_row * BH + inner_col * 4])[0] = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n        }\n        __syncthreads();\n        \n\n        // calculate per thread results\n        for (int dot_idx = 0; dot_idx < BO; dot_idx++) {\n            // load dout and w into registers\n            for (int j = 0; j < (TH / 4); j++) {\n                reinterpret_cast<float4 *>(&reg_dout[j * 4])[0] = \n                    reinterpret_cast<float4 *>(&dout_s[dot_idx * BH + thread_col * TH + j * 4])[0];\n            }\n            // non vectorized load\n            //for (int j = 0; j < TH; j++) {\n            //    reg_dout[j] = dout_s[dot_idx * BH + thread_col * TH + j];\n            //}\n            if (TC % 4 == 0) {\n                // vectorize smem load\n                for (int i = 0; i < (TC * 9 / 4); i++) {\n                    reinterpret_cast<float4 *>(&reg_w[i * 4])[0] = \n                        reinterpret_cast<float4 *>(&w_s[dot_idx * w_s_width + thread_row * TC * 9 + i * 4])[0];\n                }\n            } else {\n                for (int i = 0; i < TC * 9; i++) {\n                    reg_w[i] = w_s[dot_idx * w_s_width + thread_row * TC * 9 + i];\n                }\n            }\n\n            for (int k = 0; k < 9; k++) {\n                for (int i = 0; i < TC; i++) {\n                    for (int j = 0; j < TH; j++) {\n                        thread_results[k * TC * TH + i * TH + j] += \n                            reg_w[i * 9 + k] * reg_dout[j];\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    int out_batch_offset = batch * C * H * W;\n    for (int j = 0; j < TH; j++) {\n        int hw_abs = block_col * BH + thread_col * TH + j;\n        int h_abs = hw_abs / W;\n        int w_abs = hw_abs % W;\n        for (int i = 0; i < TC; i++) {\n            int c_abs = block_row * BC + thread_row * TC + i;\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                int h_target_abs = h_abs - 1 + k_i;\n                int w_target_abs = w_abs - 1 + k_j;\n                if (h_target_abs >= 0 && h_target_abs < H && w_target_abs >= 0 && w_target_abs < W) {\n                    int out_target_abs = out_batch_offset + c_abs * H * W + h_target_abs * W + w_target_abs;\n                    int th_res_idx = k * TC * TH + i * TH + j;\n                    atomicAdd(dx + out_target_abs, thread_results[th_res_idx]);\n                }\n            }\n        }\n    }\n}\n\n// I wonder how fast does a transposition kernel take for the weights\n__global__ void transpose_weight_kernel(\n    float* weight, float* out,\n    int C, int O\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < C * O * 9) {\n        int o = idx / (C * 9);\n        int c = (idx % C * 9) / 9;\n        int k = idx % 9;\n        out[c * O * 9 + o * 9 + k] = weight[idx];\n    }\n}\n\nvoid dx_backward(\n    float* dout, float* weight,\n    float* dx,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    if (H < 16) {\n        const int BH = 64;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_in == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 3;\n        const int TC = 3;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else if (C_out == 3) {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 3;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    } else {\n        const int BH = 64 * 4;\n        const int TH = 4;\n        const int BC = 16;\n        const int TC = 4;\n        const int BO = 16;\n        dim3 gridDim(B * H * W / BH, C_in / BC);\n        dim3 blockDim((BC * BH) / (TC * TH));\n        dx_backward_kernel1<BO, BH, BC, TC, TH><<<gridDim, blockDim>>>(dout, weight, dx, B, C_in, C_out, H, W);\n    }\n}\n\nvoid dx_backward_cpu(\n    float* dout, float* weight, float* dx,\n    int B, int C, int O, int H, int W\n) {\n    printf(\"dx[0] before zeroing: %f\\n\", dx[0]);\n    for (int b = 0; b < B; b++) {\n        for (int c = 0; c < C; c++) {\n            for (int h = 0; h < H; h++) {\n                for (int w = 0; w < W; w++) {\n                    for (int k = 0; k < 9; k++) {\n                        int k_i = k / 3;\n                        int k_j = k % 3;\n                        int h_target = h - 1 + k_i;\n                        int w_target = w - 1 + k_j;\n                        if (h_target < 0 || h_target >= H || w_target < 0 || w_target >= W) {\n                            continue;\n                        }\n                        float out_bchw = 0.0f;\n                        for (int o = 0; o < O; o++) {\n                            float new_val = weight[o * C * 9 + c * 9 + k] * dout[b * O * H * W + o * H * W + h * W + w];\n                            out_bchw += new_val;\n                        }\n                        int out_idx = b * C * H * W + c * H * W + h_target * W + w_target;\n                        dx[out_idx] += out_bchw;\n                    }\n                }\n            }\n            if (b == 0 && c == 0) {\n                printf(\"dx[0]: %f\\n\", dx[0]);\n                return;\n            }\n        }\n    }\n}\n\n// dweight_buf will have shape (O, C, 9, B)\n// and then we have to reduce over the last dimension to get the actual weight\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC>\n__global__ void dweight_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // ensure that we load entire image widths into smem\n    // so we can do all 9 kernels at once\n    assert(BW % W == 0);\n\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    // number of image widths we will load to smem in one round\n    const int NW = BW / W;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NW - 1)) {\n        // load dout into smem\n        int dout_inner_offset = h * W;\n        int nh = (h < H - (NW - 1)) ? NW : NW - 1;\n        int dout_load_sz = nh * W * BO;\n        for (int i = threadIdx.x; i < dout_load_sz / 4; i++) {\n            int inner_col = i % (nh * W / 4);\n            int inner_row = i / (nh * W / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&dout[dout_block_offset + dout_inner_offset + inner_row * H * W + inner_col * 4])[0];\n            dout_s[(inner_col * 4 + 0) * BO + inner_row] = tmp.x;\n            dout_s[(inner_col * 4 + 1) * BO + inner_row] = tmp.y;\n            dout_s[(inner_col * 4 + 2) * BO + inner_row] = tmp.z;\n            dout_s[(inner_col * 4 + 3) * BO + inner_row] = tmp.w;\n        }\n        \n        // load x to smem\n        int x_inner_offset = h * W;\n        int x_load_sz = nh * W * BC;\n        for (int i = threadIdx.x; i < x_load_sz / 4; i++) {\n            int inner_col = i % (nh * W / 4);\n            int inner_row = i / (nh * W / 4);\n            float4 tmp = \n                reinterpret_cast<float4 *>(&x[x_block_offset + x_inner_offset + inner_row * H * W + inner_col * 4])[0];\n            x_s[(inner_col * 4 + 0) * BC + inner_row] = tmp.x;\n            x_s[(inner_col * 4 + 1) * BC + inner_row] = tmp.y;\n            x_s[(inner_col * 4 + 2) * BC + inner_row] = tmp.z;\n            x_s[(inner_col * 4 + 3) * BC + inner_row] = tmp.w;\n        }\n        __syncthreads();\n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NW - 1) {\n            for (int i = threadIdx.x; i < W * BO; i++) {\n                int inner_col = i % W;\n                int inner_row = i / W;\n                dout_s[(inner_col + nh * W) * BO + inner_row] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < W * BC; i++) {\n                int inner_col = i % W;\n                int inner_row = i / W;\n                x_s[(inner_col + nh * W) * BC + inner_row] = 0.0f;\n            } \n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < BW; x_dot_idx++) {\n            int x_h = x_dot_idx / W;\n            int x_w = x_dot_idx % W;\n            // load one pixel with TC of the C channels into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NW && dout_w >= 0 && dout_w < W) {\n                    // this logic is a bit tricky: for the last row in smem\n                    // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                    // for kernels 4, 5, 6 we need to only compute them once\n                    // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                    // so it will be computed as part of the first row next time\n                    if (x_h == NW - 1 && k_i == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * W + dout_w;\n                    }\n                }\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_results[k * TO * TC + i * TC + j] += reg_dout[i] * reg_x[j];\n                        }\n                    }\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\n\n// C * O blocks\n// 288 (9 * 32) threads per block, use one warp to reduce each batch dim\n__global__ void dweight_reduce_kernel(\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O\n) {\n    assert(gridDim.x == O * C);\n    int o = blockIdx.x / C;\n    int c = blockIdx.x % C;\n    // move input pointer\n    dweight_buf += o * C * 9 * B + c * 9 * B;\n    assert(blockDim.x == 32 * 9);\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        thread_sum += dweight_buf[warp_id * B + i];\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dweight[o * C * 9 + c * 9 + warp_id] = warp_sum;\n    }\n}\n\nvoid dweight_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 32;\n    const int BO = 32;\n    const int BW = 2 * 64;\n    const int TO = 4;\n    const int TC = 4;\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TO * TC));\n    dweight_backward_kernel1<BC, BO, BW, TO, TC><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(hipGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// instead of having to load multiples of entire widths of the image into smem\n// we now only load fractions of the width each time\n// so the outer loop needs to loop over the height multiples (NH - 1) as well as the width multiples (WS - 1)\n// results: This did seem to help with memory access: compute intensity is higher\n// right now this one is much slower than kernel 1\n// I think this is because the smem loads are now not aligned, which causes a lot of uncoalesced loads\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel2(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    assert(BW % WSZ == 0);\n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        for (int w_offset = 0; w_offset < W; w_offset += (WSZ - 1)) {\n            // load dout into smem\n            int h_offset = h * W;\n            int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n            int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n            int dout_load_sz = nh * wsz * BO;\n            for (int i = threadIdx.x; i < dout_load_sz; i++) {\n                int inner_w = i % wsz;\n                int inner_h = (i / wsz) % nh;\n                int inner_o = i / (wsz * nh);\n                dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                    dout[dout_block_offset + h_offset + inner_h * W + w_offset + inner_w + inner_o * H * W];\n            }\n            \n            // load x to smem\n            int x_load_sz = nh * wsz * BC;\n            for (int i = threadIdx.x; i < x_load_sz; i++) {\n                int inner_w = i % wsz;\n                int inner_h = (i / wsz) % nh;\n                int inner_c = i / (wsz * nh);\n                x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                    x[x_block_offset + h_offset + inner_h * W + w_offset + inner_w + inner_c * H * W];\n            }\n\n            __syncthreads();\n\n            // if loading the last block, fill in the last block's buffer to be 0\n            if (nh == NH - 1) {\n                for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_o = i / WSZ;\n                    dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_c = i / WSZ;\n\n                    x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n                } \n            }\n            // if loading the last width section, fill in the final value in the buffer to 0\n            if (wsz == WSZ - 1)  {\n                for (int i = threadIdx.x; i < nh * BO; i++) {\n                    int inner_o = i % BO;\n                    int inner_h = i / BO;\n                    dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < nh * BC; i++) {\n                    int inner_c = i % BC;\n                    int inner_h = i / BC;\n                    x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n                }\n            }\n            __syncthreads();\n\n            // calculate per thread results\n            for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n                int x_h = x_dot_idx / WSZ;\n                int x_w = x_dot_idx % WSZ;\n                // load TC of the C channels of one image pixel into reg_w\n                for (int j = 0; j < TC; j++) {\n                    reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n                }\n\n                // for each conv filter, load a different row and calculate the outer product\n                for (int k = 0; k < 9; k++) {\n                    int k_i = k / 3;\n                    int k_j = k % 3;\n                    // calculate the corresponding (h, w) that should be loaded from dout\n                    int dout_h = x_h + 1 - k_i;\n                    int dout_w = x_w + 1 - k_j;\n                    int dout_h_abs = h + dout_h;\n                    int dout_w_abs = w_offset + dout_w;\n                    int dout_dot_idx = -1;\n                    if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                        if (x_h == NH - 1 && k_i == 1) {\n                            // this logic is a bit tricky: for the last row in smem\n                            // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                            // for kernels 4, 5, 6 we need to only compute them once\n                            // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                            // so it will be computed as part of the first row next time\n                            dout_dot_idx = -1;\n                        } else if (x_w == WSZ - 1 && k_j == 1) {\n                            dout_dot_idx = -1;\n                        } else {\n                            dout_dot_idx = dout_h * WSZ + dout_w;\n                        }\n                    }\n\n                    // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                    // and add the products to the thread results\n                    // otherwise the contribution is 0\n                    if (dout_dot_idx != -1) {\n                        for (int i = 0; i < TO; i++) {\n                            assert(dout_dot_idx * BO + thread_row * TO + i < BO * BW);\n                            reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                        }\n                        for (int j = 0; j < TC; j++) {\n                            for (int i = 0; i < TO; i++) {\n                                float new_val = reg_dout[i] * reg_x[j];\n                                thread_results[k * TO * TC + i * TC + j] += new_val;\n                            }\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward2(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 32;\n    const int BO = 32;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel2<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(hipDeviceSynchronize());\n    cudaCheck(hipGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// kernel 2 does something silly: because are now loading WSZ sections of widths,\n// and WSZ is definitely not a nice power of 2, we need to be careful\n// which thread we are using when loading global memory.\n// This kernel only changes the way loading is done compared to kernel 2\n// effectively we do some warp tiling when loading from global mem\n// kernel 3 is now quite fast (2.6ms)\n// profiling tells us that we are now using very few blocks, so seems like a good idea\n// to spread out the different width sections on different blocks and do a bigger reduce afterwards\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel3(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * C / BC, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = C / BC;\n    assert(gridDim.x == B * blocks_per_col);\n    const int batch = blockIdx.x / blocks_per_col;\n    const int block_col = blockIdx.x % blocks_per_col;\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        for (int w_offset = 0; w_offset < W; w_offset += (WSZ - 1)) {\n            // load dout into smem\n            int h_offset = h * W;\n            int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n            int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n            int o_per_warp = ceil_div(BO, n_warps);\n            if (lane_id < wsz) {\n                for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                    int inner_o = warp_id * o_per_warp + warp_o;\n                    if (inner_o < BO) {\n                        for (int inner_h = 0; inner_h < nh; inner_h++) {\n                            int inner_w = lane_id;\n                            dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                                dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                        }\n                    }\n                }\n            }\n\n            int c_per_warp = ceil_div(BC, n_warps);\n            if (lane_id < wsz) {\n                for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                    int inner_c = warp_id * c_per_warp + warp_c;\n                    if (inner_c < BC) {\n                        for (int inner_h = 0; inner_h < nh; inner_h++) {\n                            int inner_w = lane_id;\n                            x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                                x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                        }\n                    }\n                }\n            }\n\n            __syncthreads();\n            \n\n            // if loading the last block, fill in the last block's buffer to be 0\n            if (nh == NH - 1) {\n                for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_o = i / WSZ;\n                    dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                    int inner_w = i % WSZ;\n                    int inner_c = i / WSZ;\n\n                    x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n                } \n            }\n            // if loading the last width section, fill in the final value in the buffer to 0\n            if (wsz == WSZ - 1)  {\n                for (int i = threadIdx.x; i < nh * BO; i++) {\n                    int inner_o = i % BO;\n                    int inner_h = i / BO;\n                    dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n                }\n                for (int i = threadIdx.x; i < nh * BC; i++) {\n                    int inner_c = i % BC;\n                    int inner_h = i / BC;\n                    x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n                }\n            }\n            __syncthreads();\n\n            // calculate per thread results\n            for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n                int x_h = x_dot_idx / WSZ;\n                int x_w = x_dot_idx % WSZ;\n                // load TC of the C channels of one image pixel into reg_w\n                for (int j = 0; j < TC; j++) {\n                    reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n                }\n\n                // for each conv filter, load a different row and calculate the outer product\n                for (int k = 0; k < 9; k++) {\n                    int k_i = k / 3;\n                    int k_j = k % 3;\n                    // calculate the corresponding (h, w) that should be loaded from dout\n                    int dout_h = x_h + 1 - k_i;\n                    int dout_w = x_w + 1 - k_j;\n                    int dout_h_abs = h + dout_h;\n                    int dout_w_abs = w_offset + dout_w;\n                    int dout_dot_idx = -1;\n                    if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                        if (x_h == NH - 1 && k_i == 1) {\n                            // this logic is a bit tricky: for the last row in smem\n                            // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                            // for kernels 4, 5, 6 we need to only compute them once\n                            // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                            // so it will be computed as part of the first row next time\n                            dout_dot_idx = -1;\n                        } else if (x_w == WSZ - 1 && k_j == 1) {\n                            dout_dot_idx = -1;\n                        } else {\n                            dout_dot_idx = dout_h * WSZ + dout_w;\n                        }\n                    }\n\n                    // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                    // and add the products to the thread results\n                    // otherwise the contribution is 0\n                    if (dout_dot_idx != -1) {\n                        for (int i = 0; i < TO; i++) {\n                            reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                        }\n                        for (int j = 0; j < TC; j++) {\n                            for (int i = 0; i < TO; i++) {\n                                float new_val = reg_dout[i] * reg_x[j];\n                                thread_results[k * TO * TC + i * TC + j] += new_val;\n                            }\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B + c_abs * 9 * B + k * B + batch] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward3(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * C / BC, O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel3<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(hipDeviceSynchronize());\n    cudaCheck(hipGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n}\n\n\n// trying to improve on kernel 3 by splitting the widths of each image\n// to be calculated on different blocks\n// Output will now be of shape (O, C, 9, B, W / (WSZ - 1))\n// and we will need to reduce over the last two dimensions\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel4(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1)  {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward4(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel4<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(hipDeviceSynchronize());\n    cudaCheck(hipGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n}\n\n// same as kernel 4, but vectorize loads to speed up smem load\n// unfortunately as is this doesn't give any speedup\n// probably because we are now using too few threads to load\n// I could try to shuffle and make each warp load more heights as well, but that doesn't seem first order right now\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_backward_kernel5(\n    float* dout, float* x,\n    float* dweight_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = blockDim.x / 32;\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    for (int h = 0; h < H; h += (NH - 1)) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < ceil_div(wsz, 4)) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        float4 tmp = \n                            reinterpret_cast<float4 *>(&dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + lane_id * 4])[0];\n                        dout_s[(inner_h * WSZ + lane_id * 4 + 0) * BO + inner_o] = tmp.x;\n                        if (lane_id < wsz / 4) {\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 1) * BO + inner_o] = tmp.y;\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 2) * BO + inner_o] = tmp.z;\n                            dout_s[(inner_h * WSZ + lane_id * 4 + 3) * BO + inner_o] = tmp.w;\n                        }\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < ceil_div(wsz, 4)) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        float4 tmp = \n                            reinterpret_cast<float4 *>(&x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + lane_id * 4])[0];\n                        x_s[(inner_h * WSZ + lane_id * 4 + 0) * BC + inner_c] = tmp.x;\n                        if (lane_id < wsz / 4) {\n                            x_s[(inner_h * WSZ + lane_id * 4 + 1) * BC + inner_c] = tmp.y;\n                            x_s[(inner_h * WSZ + lane_id * 4 + 2) * BC + inner_c] = tmp.z;\n                            x_s[(inner_h * WSZ + lane_id * 4 + 3) * BC + inner_c] = tmp.w;\n                        }\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1)  {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1) {\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out results\n    for (int j = 0; j < TC; j++) {\n        for (int i = 0; i < TO; i++) {\n            for (int k = 0; k < 9; k++) {\n                int o_abs = block_row * BO + thread_row * TO + i;\n                int c_abs = block_col * BC + thread_col * TC + j;\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n    }\n}\n\nvoid dweight_backward5(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    const int BC = 64;\n    const int BO = 64;\n    const int WSZ = 8 + 1;\n    const int BW = WSZ * 9;\n    const int TO = 4;\n    const int TC = 4;\n    assert(BW % WSZ == 0);\n    dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n    dim3 blockDim(BC * BO / (TC * TO));\n    dweight_backward_kernel5<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, B, C, O, H, W);\n    cudaCheck(hipDeviceSynchronize());\n    cudaCheck(hipGetLastError());\n\n    int block_size = 32 * 9;\n    int n_blocks = O * C;\n    dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n}\n\n// same as dweight backward 4, except we also do some of the reduce for dbias\n// so it doesn't have to reduce an entire BxHxW length for each O channel\ntemplate <const int BC, const int BO, const int BW, const int TO, const int TC, const int WSZ>\n__global__ void dweight_dbias_backward_kernel1(\n    float* dout, float* x,\n    float* dweight_buf, float* dbias_buf,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    // number of heights we need to load into smem in one go \n    const int NH = BW / WSZ;\n    // n_threads per block = (BC * BO) / (TO * TC)\n    // gridDim will be (B * (W / (WSZ - 1)) * (C / BC), O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_chan = C / BC;\n    const int blocks_per_width = W / (WSZ - 1);\n    const int block_w = blockIdx.x % blocks_per_width;\n    const int block_col = (blockIdx.x / blocks_per_width) % blocks_per_chan;\n    const int batch = blockIdx.x / (blocks_per_chan * blocks_per_width);\n    const int w_offset = block_w * (WSZ - 1);\n\n    // warp indices for loading from global mem\n    const int n_warps = ceil_div((int)blockDim.x, 32);\n    const int warp_id = threadIdx.x / 32;\n    const int lane_id = threadIdx.x % 32;\n    // make sure that the width sections can be loaded by a single warp\n    assert(WSZ <= 32);\n\n    // index for the TO*TC thread block\n    const int threads_per_col = BC / TC;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // smem for dout and x\n    __shared__ float dout_s[BO * BW];\n    __shared__ float x_s[BC * BW];\n\n    // registers for local mem and local results\n    float thread_results[TO * TC * 9] = {0.0};\n    float reg_dout[TO] = {0.0};\n    float reg_x[TC] = {0.0};\n    float thread_bias_results[TO] = {0.0};\n\n    // block offsets\n    const int dout_block_offset = batch * O * H * W + block_row * BO * H * W;\n    const int x_block_offset = batch * C * H * W + block_col * BC * H * W;\n\n    //int debug_idx = 0;\n\n    // we will fuse the calculation for all 9 kernel filters\n    // which means we will have to reload some of the image widths\n    // we can load NW blocks into smem in one go, which means we will be stepping\n    // over the H dimension in (NW - 1) steps\n    // this is the outer loop over H\n    int h_step = (NH < H) ? NH - 1 : H;\n    // if NH == H, then we are loading the entire image in one go, so step by H\n    for (int h = 0; h < H; h += h_step) {\n        // load dout into smem\n        int h_offset = h * W;\n        int nh = (h < H - (NH - 1)) ? NH : NH - 1;\n        int wsz = (w_offset < W - (WSZ - 1)) ? WSZ : WSZ - 1;\n        int o_per_warp = ceil_div(BO, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_o = 0; warp_o < o_per_warp; warp_o++) {\n                int inner_o = warp_id * o_per_warp + warp_o;\n                if (inner_o < BO) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        dout_s[(inner_h * WSZ + inner_w) * BO + inner_o] = \n                            dout[dout_block_offset + inner_o * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        int c_per_warp = ceil_div(BC, n_warps);\n        if (lane_id < wsz) {\n            for (int warp_c = 0; warp_c < c_per_warp; warp_c++) {\n                int inner_c = warp_id * c_per_warp + warp_c;\n                if (inner_c < BC) {\n                    for (int inner_h = 0; inner_h < nh; inner_h++) {\n                        int inner_w = lane_id;\n                        x_s[(inner_h * WSZ + inner_w) * BC + inner_c] = \n                            x[x_block_offset + inner_c * H * W + h_offset + inner_h * W + w_offset + inner_w];\n                    }\n                }\n            }\n        }\n\n        __syncthreads();\n        \n\n        // if loading the last block, fill in the last block's buffer to be 0\n        if (nh == NH - 1) {\n            for (int i = threadIdx.x; i < WSZ * BO; i++) {\n                int inner_w = i % WSZ;\n                int inner_o = i / WSZ;\n                dout_s[(inner_w + nh * WSZ) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < WSZ * BC; i++) {\n                int inner_w = i % WSZ;\n                int inner_c = i / WSZ;\n\n                x_s[(inner_w + nh * WSZ) * BC + inner_c] = 0.0f;\n            } \n        }\n        // if loading the last width section, fill in the final value in the buffer to 0\n        if (wsz == WSZ - 1) {\n            for (int i = threadIdx.x; i < nh * BO; i++) {\n                int inner_o = i % BO;\n                int inner_h = i / BO;\n                dout_s[(inner_h * WSZ + wsz) * BO + inner_o] = 0.0f;\n            }\n            for (int i = threadIdx.x; i < nh * BC; i++) {\n                int inner_c = i % BC;\n                int inner_h = i / BC;\n                x_s[(inner_h * WSZ + wsz) * BC + inner_c] = 0.0f;\n            }\n        }\n        __syncthreads();\n\n        // calculate per thread results\n        for (int x_dot_idx = 0; x_dot_idx < WSZ * nh; x_dot_idx++) {\n            int x_h = x_dot_idx / WSZ;\n            int x_w = x_dot_idx % WSZ;\n            // load TC of the C channels of one image pixel into reg_w\n            for (int j = 0; j < TC; j++) {\n                reg_x[j] = x_s[x_dot_idx * BC + thread_col * TC + j];\n            }\n\n            // for each conv filter, load a different row and calculate the outer product\n            for (int k = 0; k < 9; k++) {\n                int k_i = k / 3;\n                int k_j = k % 3;\n                // calculate the corresponding (h, w) that should be loaded from dout\n                int dout_h = x_h + 1 - k_i;\n                int dout_w = x_w + 1 - k_j;\n                int dout_h_abs = h + dout_h;\n                int dout_w_abs = w_offset + dout_w;\n                int dout_dot_idx = -1;\n                if (dout_h >= 0 && dout_h < NH && dout_w >= 0 && dout_w < WSZ && dout_w_abs < W && dout_h_abs < H) {\n                    if (x_h == NH - 1 && k_i == 1 && H > NH) {\n                        // this logic is a bit tricky: for the last row in smem\n                        // because it will be loaded as part of the next block as well, and it will be the first row in the next block\n                        // for kernels 4, 5, 6 we need to only compute them once\n                        // this statement ensures that we don't compute the 4, 5, 6 kernels for the last row in smem\n                        // so it will be computed as part of the first row next time\n                        dout_dot_idx = -1;\n                    } else if (x_w == WSZ - 1 && k_j == 1 && W > WSZ) {\n                        // if x_w is at the last pixel, and we have more than one width sections, and we are not are the last section\n                        // then don't compute 1, 4, 7 kernels and wait for the next section to compute them\n                        dout_dot_idx = -1;\n                    } else {\n                        dout_dot_idx = dout_h * WSZ + dout_w;\n                    }\n                }\n\n                // if in bounds of the dout section in smem, load the corresponding pixel from smem\n                // and add the products to the thread results\n                // otherwise the contribution is 0\n                if (dout_dot_idx != -1) {\n                    for (int i = 0; i < TO; i++) {\n                        reg_dout[i] = dout_s[dout_dot_idx * BO + thread_row * TO + i];\n                    }\n                    for (int j = 0; j < TC; j++) {\n                        for (int i = 0; i < TO; i++) {\n                            float new_val = reg_dout[i] * reg_x[j];\n                            thread_results[k * TO * TC + i * TC + j] += new_val;\n                        }\n                    }\n                    // update dbias when k = 4, where dout_h and dout_w match x_h and x_w\n                    if (k == 4 && block_col == 0) {\n                        for (int i = 0; i < TO; i++) {\n                            thread_bias_results[i] += reg_dout[i];\n                        }\n                    }\n                }\n            }\n            __syncthreads();\n        }\n    }\n\n    // write out dweight results\n    for (int i = 0; i < TO; i++) {\n        int o_abs = block_row * BO + thread_row * TO + i;\n        for (int j = 0; j < TC; j++) {\n            int c_abs = block_col * BC + thread_col * TC + j;\n            for (int k = 0; k < 9; k++) {\n                dweight_buf[o_abs * C * 9 * B * blocks_per_width + c_abs * 9 * B * blocks_per_width + k * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_results[k * TO * TC + i * TC + j];\n            }\n        }\n        // write out dbias results\n        if (block_col == 0) {\n            dbias_buf[o_abs * B * blocks_per_width + batch * blocks_per_width + block_w] = thread_bias_results[i];\n        }\n    }\n}\n\n\n__global__ void dbias_reduce_kernel1(\n    float* dbias_buf, float* dbias,\n    const int B\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    dbias_buf += blockIdx.x * B;\n\n    int lane_id = threadIdx.x % 32;\n\n    float db_thread_sum = 0.0f;\n    for (int i = lane_id; i < B; i += 32) {\n        db_thread_sum += dbias_buf[i];\n    }\n\n    float db_warp_sum = cg::reduce(warp, db_thread_sum, cg::plus<float>{});\n\n    if (lane_id == 0) {\n        dbias[blockIdx.x] = db_warp_sum;\n    }\n}\n\nvoid dweight_dbias_backward1(\n    float* dout, float* x,\n    float* dweight_buf, float* dweight,\n    float* dbias_buf, float* dbias,\n    const int B, const int C, const int O, const int H, const int W\n) {\n    if (H * W == 64) {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8;\n        const int BW = WSZ * 8;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B, C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B);\n    } else if (C == 3) {\n        const int BC = 3;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 3;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else if (O == 3) {\n        const int BC = 64;\n        const int BO = 3;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 3;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    } else {\n        const int BC = 64;\n        const int BO = 64;\n        const int WSZ = 8 + 1;\n        const int BW = WSZ * 9;\n        const int TO = 4;\n        const int TC = 4;\n        assert(BW % WSZ == 0);\n        dim3 gridDim(B * (W / (WSZ - 1)) * (C / BC), O / BO);\n        dim3 blockDim(BC * BO / (TC * TO));\n        dweight_dbias_backward_kernel1<BC, BO, BW, TO, TC, WSZ><<<gridDim, blockDim>>>(dout, x, dweight_buf, dbias_buf, B, C, O, H, W);\n        cudaCheck(hipDeviceSynchronize());\n        cudaCheck(hipGetLastError());\n\n        int block_size = 32 * 9;\n        int n_blocks = O * C;\n        dweight_reduce_kernel<<<n_blocks, block_size>>>(dweight_buf, dweight, B * W / (WSZ - 1), C, O);\n        dbias_reduce_kernel1<<<O, 32>>>(dbias_buf, dbias, B * W / (WSZ - 1));\n    }\n}\n\nvoid conv2d_k3_backward2(\n    float* dout, float* x, float* weight,\n    float* dweight_buf, float* dbias_buf,\n    float* dx, float* dweight, float* dbias,\n    const int B, const int C_in, const int C_out, const int H, const int W\n) {\n    // dx currently requires the output buffer to be zeroed\n    cudaCheck(hipMemset(dx, 0, B * C_in * H * W * sizeof(float)));\n    dx_backward(dout, weight, dx, B, C_in, C_out, H, W);\n    dweight_dbias_backward1(dout, x, dweight_buf, dweight, dbias_buf, dbias, B, C_in, C_out, H, W);\n}\n// ----------------------------------------------------------------------------\n// kernel launcher\nvoid conv2d_k3_forward(\n    hipblasHandle_t cublas_handle,\n    int kernel_num,\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            conv2d_k3_forward1(cublas_handle, x, weight, bias, out, B, C_in, C_out, H, W, block_size);\n            break;\n        case 2:\n            conv2d_k3_forward2(cublas_handle, x, weight, bias, out, B, C_in, C_out, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            break;\n    }\n}\n\n\n#ifndef LINKING\nint main( int argc, char **argv) {\n    const int B = 32;\n    const int C_in = 192;\n    const int C_out = 64;\n    const int H = 64;\n    const int W = 64;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n    printf(\"device registers per block: %d\\n\", deviceProp.regsPerBlock);\n    printf(\"shared mem per block: %ld\\n\", deviceProp.sharedMemPerBlock);\n\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* weight = (float*)malloc(C_out * C_in * 3 * 3 * sizeof(float));\n    float* bias = (float*)malloc(C_out * sizeof(float));\n    float* out = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dx = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* dweight = (float*)malloc(C_out * C_in * 3 * 3 * sizeof(float));\n    float* dbias = (float*)malloc(C_out * sizeof(float));\n    // debug\n    float* dout_perm = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* x_tiled = (float*)malloc(B * H * W * C_in * 9 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"conv2d_k3.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C_in * H * W, file);\n    freadCheck(weight, sizeof(float), C_out * C_in * 3 * 3, file);\n    freadCheck(bias, sizeof(float), C_out, file);\n    freadCheck(out, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dout, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dx, sizeof(float), B * C_in * H * W, file);\n    freadCheck(dweight, sizeof(float), C_out * C_in * 3 * 3, file);\n    freadCheck(dbias, sizeof(float), C_out, file);\n    freadCheck(dout_perm, sizeof(float), B * C_out * H * W, file);\n    freadCheck(x_tiled, sizeof(float), B * H * W * C_in * 9, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(hipMalloc(&d_x, B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_weight, C_out * C_in * 3 * 3 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_bias, C_out * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * C_out * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * C_out * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dweight, C_out * C_in * 3 * 3 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dbias, C_out * sizeof(float)));\n\n    cudaCheck(hipMemset(d_out, 0, B * C_out * H * W * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, B * C_in * H * W * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_weight, weight, C_out * C_in * 3 * 3 * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_bias, bias, C_out * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, B * C_out * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n    // read kernel number from command line\n    int kernel_num = 2;\n    if (argc > 1) {\n        kernel_num = atoi(argv[1]);\n    }\n    printf(\"Running kernel %d\\n\", kernel_num);\n\n    printf(\"Checking forward pass\\n\");\n    float forward_acc = 1e-1;\n\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n    //    printf(\"\\nBlock size: %d\\n\", block_size);\n    //    conv2d_k3_forward(cublas_handle, kernel_num, d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W, block_size);\n    //    validate_result(d_out, out, \"out\", B * C_out * H * W, forward_acc);\n    //}\n    //printf(\"\\nChecking cpu forward\\n\");\n    //float* out_cpu_buf = (float*)malloc(B * C_out * H * W * sizeof(float));\n    //conv_k3_forward_cpu(x, weight, bias, out_cpu_buf, B, C_in, C_out, H, W);\n    //float* out_gpu_buf;\n    //cudaCheck(cudaMalloc(&out_gpu_buf, B * C_out * H * W * sizeof(float)));\n    //cudaCheck(cudaMemcpy(out_gpu_buf, out, B * C_out * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    //validate_result(out_gpu_buf, out, \"out cpu\", B * C_out * H * W);\n    //cudaCheck(cudaFree(out_gpu_buf));\n    //free(out_cpu_buf);\n\n    printf(\"\\nChecking new kernel\\n\");\n    conv2d_k3_forward3(d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W);\n    cudaCheck(hipGetLastError());\n    printf(\"\\nChecking new kernel results\\n\");\n    validate_result(d_out, out, \"out\", B * C_out * H * W, forward_acc);\n    printf(\"Forward pass successful\\n\\n\");\n\n    printf(\"\\nmanually benchmark new forward kernel\\n\");\n    void* flush_buffer;\n    cudaCheck(hipMalloc(&flush_buffer, deviceProp.l2CacheSize));\n    int repeat_times = 1000;\n    float new_time = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        hipEvent_t start, end;\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end));\n        cudaCheck(hipEventRecord(start, nullptr));\n        conv2d_k3_forward3(d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W);\n        cudaCheck(hipEventRecord(end, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n\n    printf(\"Checking backward pass\\n\");\n\n    float dw_acc = 1e-2;\n    float* d_dweight_buf;\n    cudaCheck(hipMalloc(&d_dweight_buf, C_out * C_in * 3 * 3 * B * 32 * sizeof(float)));\n    float* d_dbias_buf;\n    cudaCheck(hipMalloc(&d_dbias_buf, C_out * 256 * sizeof(float)));\n    conv2d_k3_backward2(d_dout, d_x, d_weight, d_dweight_buf, d_dbias_buf, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W);\n\n    printf(\"\\nChecking dweight\\n\");\n    validate_result(d_dweight, dweight, \"dweight\", C_out * C_in * 3 * 3, dw_acc);\n\n    printf(\"\\nChecking dbias\\n\");\n    validate_result(d_dbias, dbias, \"dbias\", C_out, dw_acc);\n\n    printf(\"\\nChecking dx\\n\");\n    validate_result(d_dx, dx, \"dx\", B * C_in * H * W, dw_acc);\n\n    new_time = 0.0f;\n    printf(\"\\nManually benchmarking new dweight kernel\\n\");\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        hipEvent_t start, end;\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end));\n        cudaCheck(hipEventRecord(start, nullptr));\n        conv2d_k3_backward2(d_dout, d_x, d_weight, d_dweight_buf, d_dbias_buf, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W);\n        cudaCheck(hipEventRecord(end, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n    cudaCheck(hipFree(d_dweight_buf));\n    cudaCheck(hipFree(d_dbias_buf));\n\n\n\n    printf(\"Backward pass successful\\n\");\n\n    // old benchmarking code\n    //printf(\"All results match. Starting benchmarks.\\n\\n\");\n    //printf(\"Forward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k3_forward,\n    //                                          cublas_handle, kernel_num, d_x, d_weight, d_bias, d_out,\n    //                                          B, C_in, C_out, H, W, block_size);\n\n    //    float tflops = (float)B * H * W * C_in * C_out * 9 * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"\\n\\n Old benchmarks\\n\");\n    printf(\"Running our own forward benchmarks\\n\");\n    float t1 = 0.0f;\n    float t2 = 0.0f;\n    float t3 = 0.0f;\n    float t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k3_forward2(cublas_handle, d_x, d_weight, d_bias, d_out, B, C_in, C_out, H, W, 512, &t1, &t2, &t3);\n    }\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t_total = t1 + t2 + t3;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    //printf(\"\\nBackward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k3_backward1,\n    //                                          cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias,\n    //                                          B, C_in, C_out, H, W, block_size);\n\n    //    float tflops = (float)B * H * W * C_in * C_out * (9 + 1) * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"\\nRunning our own backward benchmarks\\n\");\n    t1 = 0.0f;\n    t2 = 0.0f;\n    t3 = 0.0f;\n    float t4 = 0.0f;\n    float t5 = 0.0f;\n    float t6 = 0.0f;\n    t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k3_backward1(cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W, 512, &t1, &t2, &t3, &t4, &t5, &t6);\n    }\n    cudaCheck(hipFree(flush_buffer));\n\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t4 /= repeat_times;\n    t5 /= repeat_times;\n    t6 /= repeat_times;\n    t_total = t1 + t2 + t3 + t4 + t5 + t6;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t4: %.4f ms\\n\", t4);\n    printf(\"t5: %.4f ms\\n\", t5);\n    printf(\"t6: %.4f ms\\n\", t6);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    // free memory\n    free(x);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dx);\n    free(dweight);\n    free(dbias);\n    free(dout_perm);\n    free(x_tiled);\n    cudaCheck(hipFree(d_x));\n    cudaCheck(hipFree(d_weight));\n    cudaCheck(hipFree(d_bias));\n    cudaCheck(hipFree(d_out));\n    cudaCheck(hipFree(d_dout));\n    cudaCheck(hipFree(d_dx));\n    cudaCheck(hipFree(d_dweight));\n    cudaCheck(hipFree(d_dbias));\n    cublasCheck(hipblasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/conv2d_k3.cu"}
{"input": "#include <cuda_runtime.h>\n#include \"common.h\"\n#include <assert.h>\n#include <cuda_runtime_api.h>\n#include \"timestep_embedding.cuh\"\n\n\n\n\n__global__ void fill_freqs_kernel(float* freqs, int half_dim, int max_period) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < half_dim) {\n        float log_period = logf(max_period);\n        float logit = -log_period * idx / half_dim;\n        freqs[idx] = expf(logit);\n    }\n}\n\nvoid init_timestep_embedding(TimestepEmbedding* emb, int dim, int B, int max_period) {\n    assert(dim % 2 == 0);\n    int half = dim / 2;\n    emb->half_dim = half;\n    emb->max_period = max_period;\n    emb->B = B;\n    cudaCheck(cudaMalloc(&emb->freqs, half * sizeof(float)));\n    // fill freq values\n    int block_size = 512;\n    int n_blocks = ceil_div(half, block_size);\n    fill_freqs_kernel<<<n_blocks, block_size>>>(emb->freqs, half, max_period);\n}\n\nvoid free_timestep_embedding(TimestepEmbedding* emb) {\n    cudaCheck(cudaFree(emb->freqs));\n}\n\n__global__ void fill_embeddings_kernel(\n    const float* timesteps, const float* freqs, float* embeddings, int half, int B\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < B * half) {\n        int i = idx / half;\n        int j = idx % half;\n        float arg = timesteps[i] * freqs[j];\n        embeddings[i * 2 * half + j] = cosf(arg);\n        embeddings[i * 2 * half + j + half] = sinf(arg);\n    }\n}\n\n\n// timesteps is a (B,) array of timesteps that are integers but saved as floats\n// out is a (B, dim) array\nvoid get_timestep_embeddings(\n    TimestepEmbedding* emb, const float* timesteps, float* out\n) {\n    int block_size = 512;\n    int n_blocks = ceil_div(emb->half_dim * emb->B, block_size);\n    fill_embeddings_kernel<<<n_blocks, block_size>>>(timesteps, emb->freqs, out, emb->half_dim, emb->B);\n    cudaCheck(cudaGetLastError());\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int B = 32;\n    int dim = 64;\n    int max_period = 1000;\n\n    // read ground truth\n    float* timesteps = (float*)malloc(B * sizeof(float));\n    float* embeddings = (float*)malloc(B * dim * sizeof(float));\n    float* freqs = (float*)malloc(dim / 2 * sizeof(float));\n    FILE *file = fopenCheck(\"time_emb.bin\", \"rb\");\n    freadCheck(timesteps, sizeof(float), B, file);\n    freadCheck(embeddings, sizeof(float), B * dim, file);\n    freadCheck(freqs, sizeof(float), dim / 2, file);\n    fcloseCheck(file);\n\n    TimestepEmbedding t_emb;\n    init_timestep_embedding(&t_emb, dim, B, max_period);\n    float *d_timesteps, *d_embeddings;\n    cudaCheck(cudaMalloc(&d_timesteps, B * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_embeddings, B * dim * sizeof(float)));\n\n    cudaCheck(cudaMemcpy(d_timesteps, timesteps, B * sizeof(float), cudaMemcpyHostToDevice));\n\n\n    printf(\"Checking freqs\\n\");\n    validate_result(t_emb.freqs, freqs, \"freqs\", dim / 2);\n\n\n    get_timestep_embeddings(&t_emb, d_timesteps, d_embeddings);\n\n    printf(\"Checking timestep embeddings\\n\");\n    float emb_acc = 1e-3;\n    validate_result(d_embeddings, embeddings, \"embeddings\", B * dim, emb_acc);\n\n    free_timestep_embedding(&t_emb);\n    free(timesteps);\n    free(embeddings);\n    free(freqs);\n    cudaFree(d_timesteps);\n}\n#endif", "output": "#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include <assert.h>\n#include <hip/hip_runtime_api.h>\n#include \"timestep_embedding.cuh\"\n\n\n\n\n__global__ void fill_freqs_kernel(float* freqs, int half_dim, int max_period) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < half_dim) {\n        float log_period = logf(max_period);\n        float logit = -log_period * idx / half_dim;\n        freqs[idx] = expf(logit);\n    }\n}\n\nvoid init_timestep_embedding(TimestepEmbedding* emb, int dim, int B, int max_period) {\n    assert(dim % 2 == 0);\n    int half = dim / 2;\n    emb->half_dim = half;\n    emb->max_period = max_period;\n    emb->B = B;\n    cudaCheck(hipMalloc(&emb->freqs, half * sizeof(float)));\n    // fill freq values\n    int block_size = 512;\n    int n_blocks = ceil_div(half, block_size);\n    fill_freqs_kernel<<<n_blocks, block_size>>>(emb->freqs, half, max_period);\n}\n\nvoid free_timestep_embedding(TimestepEmbedding* emb) {\n    cudaCheck(hipFree(emb->freqs));\n}\n\n__global__ void fill_embeddings_kernel(\n    const float* timesteps, const float* freqs, float* embeddings, int half, int B\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < B * half) {\n        int i = idx / half;\n        int j = idx % half;\n        float arg = timesteps[i] * freqs[j];\n        embeddings[i * 2 * half + j] = cosf(arg);\n        embeddings[i * 2 * half + j + half] = sinf(arg);\n    }\n}\n\n\n// timesteps is a (B,) array of timesteps that are integers but saved as floats\n// out is a (B, dim) array\nvoid get_timestep_embeddings(\n    TimestepEmbedding* emb, const float* timesteps, float* out\n) {\n    int block_size = 512;\n    int n_blocks = ceil_div(emb->half_dim * emb->B, block_size);\n    fill_embeddings_kernel<<<n_blocks, block_size>>>(timesteps, emb->freqs, out, emb->half_dim, emb->B);\n    cudaCheck(hipGetLastError());\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int B = 32;\n    int dim = 64;\n    int max_period = 1000;\n\n    // read ground truth\n    float* timesteps = (float*)malloc(B * sizeof(float));\n    float* embeddings = (float*)malloc(B * dim * sizeof(float));\n    float* freqs = (float*)malloc(dim / 2 * sizeof(float));\n    FILE *file = fopenCheck(\"time_emb.bin\", \"rb\");\n    freadCheck(timesteps, sizeof(float), B, file);\n    freadCheck(embeddings, sizeof(float), B * dim, file);\n    freadCheck(freqs, sizeof(float), dim / 2, file);\n    fcloseCheck(file);\n\n    TimestepEmbedding t_emb;\n    init_timestep_embedding(&t_emb, dim, B, max_period);\n    float *d_timesteps, *d_embeddings;\n    cudaCheck(hipMalloc(&d_timesteps, B * sizeof(float)));\n    cudaCheck(hipMalloc(&d_embeddings, B * dim * sizeof(float)));\n\n    cudaCheck(hipMemcpy(d_timesteps, timesteps, B * sizeof(float), hipMemcpyHostToDevice));\n\n\n    printf(\"Checking freqs\\n\");\n    validate_result(t_emb.freqs, freqs, \"freqs\", dim / 2);\n\n\n    get_timestep_embeddings(&t_emb, d_timesteps, d_embeddings);\n\n    printf(\"Checking timestep embeddings\\n\");\n    float emb_acc = 1e-3;\n    validate_result(d_embeddings, embeddings, \"embeddings\", B * dim, emb_acc);\n\n    free_timestep_embedding(&t_emb);\n    free(timesteps);\n    free(embeddings);\n    free(freqs);\n    hipFree(d_timesteps);\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/timestep_embedding.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n\n\n__global__ void permute_kernel(float* q, float* k, float* v,\n                               const float* inp,\n                               int B, int N, int NH, int d) {\n    // okay so now, this kernel wants Q,K,V to all be of shape (B, NH, N, d)\n    // but instead, we have a single tensor QKV (inp) of shape (B, N, 3, NH, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Q[b][nh_][n][d_] = inp[b][n][0][nh_][d_]\n\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = \\\n            (b * N * 3 * NH * d)\n            +   (n * 3 * NH * d)\n            +       (0 * NH * d)\n            +          (nh_ * d)\n            +                d_;\n\n        q[idx] = inp[inp_idx];\n        k[idx] = inp[inp_idx + NH * d];\n        v[idx] = inp[inp_idx + 2 * (NH * d)];\n    }\n}\n\n__global__ void scale_kernel(float* inp, float scale, int B, int NH, int T) {\n    // scales the pre-softmax attention scores by scale\n    // we don't want causal attention, so we don't set any values to infinity\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * T * T) {\n        inp[idx] *= scale;\n    }\n}\n\n// warp-level reduction for finding the maximum value\n__device__ float warpReduceMax(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xFFFFFFFF, val, offset));\n    }\n    return val;\n}\n\n// warp-level reduction for summing values\n__device__ float warpReduceSum(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xFFFFFFFF, val, offset);\n    }\n    return val;\n}\n\n__global__ void softmax_forward_kernel4(float* out, const float* inp, int N, int C) {\n    // out is (N, C) just like inp. Each row of inp will get softmaxed.\n    // same as kernel3, but can handle any block size (multiple of 32)\n    // each row of C elements is handled by block_size threads\n    // furthermore, each block_size threads get executed in warps of 32 threads\n\n    // special reduction operations warpReduceMax/warpReduceSum are used for intra-warp reductions\n    // shared memory is used for inter-warp reduction\n    extern __shared__ float shared[];\n    int idx = blockIdx.x;\n    int tid = threadIdx.x;\n    int warpId = threadIdx.x / 32; // warp index within a block\n    int laneId = threadIdx.x % 32; // thread index within a warp\n\n    // the number of warps per block. recall that blockDim.x is block_size\n    int warpsPerBlock = blockDim.x / 32;\n\n    // shared[] must be allocated to have 2 * warpsPerBlock elements\n    // first half for max values, the second half for sum values\n    float* maxvals = shared;\n    float* sumvals = &shared[warpsPerBlock];\n\n    // one row of inp, i.e. inp[idx, :] of shape (C,)\n    const float* x = inp + idx * C;\n\n    // first, thread coarsening by directly accessing global memory in series\n    float maxval = -INFINITY;\n    for (int i = tid; i < C; i += blockDim.x) {\n        maxval = fmaxf(maxval, x[i]);\n    }\n    // now within-warp reductions for maxval\n    maxval = warpReduceMax(maxval);\n\n    // the 0th thread of each warp writes the maxval of that warp to shared memory\n    if (laneId == 0) maxvals[warpId] = maxval;\n    __syncthreads();\n\n    // now the 0th thread reduces the maxvals in shared memory, i.e. across warps\n    if (tid == 0) {\n        float val = maxvals[tid];\n        for (int i = 1; i < warpsPerBlock; i++) {\n            val = fmaxf(val, maxvals[i]);\n        }\n        // store the final max in the first position\n        maxvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the max to all threads\n    float offset = maxvals[0];\n\n    // compute expf and write the result to global memory\n    for (int i = tid; i < C; i += blockDim.x) {\n        // subtract max for numerical stability\n        out[idx * C + i] = expf(x[i] - offset);\n    }\n\n    // okay now we calculated exp(x - max(x))\n    // step 2: sum all the values and divide by the sum\n\n    // thread coarsening for sum\n    x = out + idx * C;\n    float sumval = 0.0f;\n    for (int i = tid; i < C; i += blockDim.x) {\n        sumval += x[i];\n    }\n    // within-warp reduction for sumval\n    sumval = warpReduceSum(sumval);\n\n    // write sumval to shared memory\n    if (laneId == 0) sumvals[warpId] = sumval;\n    __syncthreads();\n\n    // inter-thread reduction of sum\n    if (tid == 0) {\n        float val = sumvals[tid];\n        for (int i = 1; i < warpsPerBlock; ++i) {\n            val += sumvals[i];\n        }\n        sumvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the sum to all threads\n    float sum = sumvals[0];\n\n    // divide the whole row by the sum\n    for (int i = tid; i < C; i += blockDim.x) {\n        out[idx * C + i] = x[i] / sum;\n    }\n}\n\n__global__ void unpermute_kernel(const float* inp, float *out, int B, int N, int NH, int d) {\n   // out has shape (B, nh, N, d) but we need to unpermute it to (B, N, nh, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // out[b][n][nh_][d_] <- inp[b][nh_][n][d_]\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        out[other_idx] = inp[idx];\n    }\n}\n\n// a simple attention forward function taken directly from llm.c kernel 3\nvoid attention_forward1\n(\n    cublasHandle_t cublas_handle,\n    float* out, float* qkvr, float* preatt, float* att,\n    float* inp,\n    int B, int T, int C, int NH,\n    const int block_size\n) {\n    // note: resure inp as a scratch buffer because it is not used in backward\n    // inp is (B, T, 3C) QKV\n    // preatt, att are (B, NH, T, T)\n    // output is (B, T, C)\n    int HS = C / NH; // head size\n\n    // permute and separate inp from (B, T, 3, NH, HS) to 3X (B, NH, T, HS)\n    float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    int total_threads = B * NH * T * HS;\n    int num_blocks = ceil_div(total_threads, block_size);\n    permute_kernel<<<num_blocks, block_size>>>(q, k, v, inp, B, T, NH, HS);\n\n    // batched matrix multiply with cuBLAS\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle,\n                            CUBLAS_OP_T, CUBLAS_OP_N,\n                            T, T, HS,\n                            &alpha,\n                            k, HS, T * HS,\n                            q, HS, T * HS,\n                            &beta,\n                            preatt, T, T * T,\n                            B * NH));\n\n    // multiply all elements of preatt elementwise by scale\n    float scale = 1.0f / sqrtf(HS);\n    total_threads = B * NH * T * T;\n    num_blocks = ceil_div(total_threads, block_size);\n    scale_kernel<<<num_blocks, block_size>>>(preatt, scale, B, NH, T);\n\n    // softmax. preatt is (B, NH, T, T) but we view it as (B * NH * T, T) and use the softmax kernel\n    int softmax_block_size = 256;\n    int grid_size = B * NH * T;\n    size_t shared_mem_size = 2 * softmax_block_size / 32 * sizeof(float);\n    softmax_forward_kernel4<<<grid_size, softmax_block_size, shared_mem_size>>>(att, preatt, B * NH * T, T);\n\n    // new approach: first cuBLAS another batched matmul\n    // y = att @ v # (B, nh, T, T) @ (B, nh, T, hs) -> (B, nh, T, hs)\n    float* vaccum = inp;\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle,\n                            CUBLAS_OP_N, CUBLAS_OP_N,\n                            HS, T, T,\n                            &alpha,\n                            v, HS, T * HS,\n                            att, T, T * T,\n                            &beta,\n                            vaccum, HS, T * HS,\n                            B * NH));\n\n    // now unpermute\n    // y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side\n    num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel<<<num_blocks, block_size>>>(vaccum, out, B, T, NH, HS);\n}\n\n__global__ void permute_kernel_backward(float* dinp,\n                                        const float* dq, const float* dk, const float* dv,\n                                        int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_;\n        dinp[inp_idx] = dq[idx];\n        dinp[inp_idx + NH * d] = dk[idx];\n        dinp[inp_idx + 2 * (NH * d)] = dv[idx];\n    }\n}\n\n__global__ void unpermute_kernel_backward(float* dinp, const float *dout, int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        dinp[idx] = dout[other_idx];\n    }\n}\n\n// slight edit from the softmax_autoregressive_backward_kernel from train_gpt2_fp32\n// our attention is no longer autoregressive\n__global__ void softmax_backward_kernel(float* dpreatt, const float* datt, const float* att,\n                                                       int B, int T, int C, float scale) {\n    constexpr const int BlockSize = 256;\n    constexpr int T_per_block = 4;\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float block_acc[32];\n\n    int idx = blockIdx.y;\n    // go through blocks in reverse order, so the slowest block starts first\n    int t0 = T - 1 - T_per_block*blockIdx.x;\n\n    att += idx * T * T;\n    datt += idx * T * T;\n    dpreatt += idx * T * T;\n\n    if (warp.meta_group_rank() == 0) {\n        block_acc[warp.thread_rank()] = 0;\n    }\n\n    for(int to = 0; to < T_per_block; ++to) {\n        int t = t0 - to;\n        if(t < 0) return;\n        const float* att_bth = att + t * T;\n        const float* datt_bth = datt + t * T;\n        float* dpreatt_bth = dpreatt + t * T;\n\n        float local_sum = 0.0f;\n        for (int t2 = block.thread_rank(); t2 < T; t2 += BlockSize) {\n            local_sum += att_bth[t2] * datt_bth[t2];\n        }\n\n        block_acc[warp.meta_group_rank()] = cg::reduce(warp, local_sum, cg::plus<float>{});\n        block.sync();\n        local_sum = cg::reduce(warp, block_acc[warp.thread_rank()], cg::plus<float>{});\n\n        for (int t3 = block.thread_rank(); t3 < T; t3 += BlockSize) {\n            // don't touch the cache. Some parts will still be here from the previous loop, and\n            // we want to exploit those.\n            float acc = __ldcs(att_bth + t3) * (__ldcs(datt_bth + t3) - local_sum);\n            __stcs(dpreatt_bth + t3, scale * acc);\n        }\n    }\n}\n\n// the sequence of transformations in this compound op is:\n// inp (B,T,3C) -> qkvr (B,T,3C) -> preatt (B,NH,T,T) -> att (B,NH,T,T) -> vaccum (B,T,C) -> out (B,T,C)\nvoid attention_backward\n(\n    cublasHandle_t cublas_handle,\n    float* dinp, float* dqkvr, float* dpreatt, float* datt, float* scratch,\n    const float* dout,\n    const float* qkvr, const float* att,\n    int B, int T, int C, int NH\n) {\n    const int block_size = 256;\n    int HS = C / NH; // head size\n    const float one = 1.0f;\n    const float zero = 0.0f; // note beta = 1.0f so that we accumulate gradients (+=)\n    // unpack convenience pointers into q, k, v\n    const float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    float *dq, *dk, *dv;\n    dq = dqkvr + 0 * B * T * C;\n    dk = dqkvr + 1 * B * T * C;\n    dv = dqkvr + 2 * B * T * C;\n    // backward through the unpermute operation\n    int num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel_backward<<<num_blocks, block_size>>>(scratch, dout, B, T, NH, HS);\n    cudaCheck(cudaGetLastError());\n    // backward into datt\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, T, T, HS, &one, v, HS, T * HS, scratch, HS, T * HS, &zero, datt, T, T * T, B * NH));\n    // backward into dv\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, HS, T, T, &one, scratch, HS, T * HS, att, T, T * T, &zero, dv, HS, T * HS, B * NH));\n    // backward into preatt\n    int hs = C / NH; // head size\n    float scale = 1.0f / sqrtf(hs);\n    softmax_backward_kernel<<<dim3(T / 4, B * NH), 256>>>(dpreatt, datt, att, B, T, C, scale);\n    cudaCheck(cudaGetLastError());\n    // backward into q\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, HS, T, T, &one, k, HS, T * HS, dpreatt, T, T * T, &zero, dq, HS, T * HS, B * NH));\n    // backward into k\n    cublasCheck(cublasSgemmStridedBatched(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, HS, T, T, &one, q, HS, T * HS, dpreatt, T, T * T, &zero, dk, HS, T * HS, B * NH));\n    // backward into inp\n    num_blocks = ceil_div(B * NH * T * HS, block_size);\n    permute_kernel_backward<<<num_blocks, block_size>>>(dinp, dq, dk, dv, B, T, NH, HS);\n    cudaCheck(cudaGetLastError());\n}\n\n\n#ifndef LINKING\nint main(int argc, char** argv) {\n    int B = 4;\n    int T = 1024;\n    int C = 256;\n    int HS = 32;\n    int NH = C / HS;\n\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* qkv = (float*)malloc(B * T * 3 * C * sizeof(float));\n    float* out = (float*)malloc(B * T * C * sizeof(float));\n    float* dout = (float*)malloc(B * T * C * sizeof(float));\n    float* dqkv = (float*)malloc(B * T * 3 * C * sizeof(float));\n\n    // read saved output\n    FILE * file = fopen(\"attention.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(qkv, sizeof(float), B * T * 3 * C, file);\n    freadCheck(out, sizeof(float), B * T * C, file);\n    freadCheck(dout, sizeof(float), B * T * C, file);\n    freadCheck(dqkv, sizeof(float), B * T * 3 * C, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_inp, *d_qkvr, *d_out, *d_preatt, *d_att, *d_dout, *d_dinp, *d_dqkvr, *d_dpreatt, *d_datt, *d_scratch;\n    cudaCheck(cudaMalloc(&d_inp, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_inp, qkv, B * T * 3 * C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMalloc(&d_qkvr, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMemset(d_qkvr, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * T * C * sizeof(float)));\n    cudaCheck(cudaMemset(d_out, 0, B * T * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_preatt, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMemset(d_preatt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_att, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMemset(d_att, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * T * C * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * T * C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMalloc(&d_dinp, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMemset(d_dinp, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dqkvr, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMemset(d_dqkvr, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dpreatt, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMemset(d_dpreatt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_datt, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMemset(d_datt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_scratch, B * T * C * sizeof(float)));\n    cudaCheck(cudaMemset(d_scratch, 0, B * T * C * sizeof(float)));\n\n    // forward pass\n    int block_sizes[] = {256, 512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        attention_forward1(cublas_handle, d_out, d_qkvr, d_preatt, d_att, d_inp, B, T, C, NH, block_size);\n        validate_result(d_out, out, \"out\", B * T * C);\n        // reset d_inp, which is used as a buffer\n        cudaCheck(cudaMemcpy(d_inp, qkv, B * T * 3 * C * sizeof(float), cudaMemcpyHostToDevice));\n    }\n\n    printf(\"\\nChecking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        attention_backward(cublas_handle, d_dinp, d_dqkvr, d_dpreatt, d_datt, d_scratch, d_dout, d_qkvr, d_att, B, T, C, NH);\n        validate_result(d_dinp, dqkv, \"dinp\", B * T * 3 * C);\n    }\n\n    printf(\"\\nAll results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    int repeat_times = 100;\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j ++) {\n        int block_size = block_sizes[j];\n        float elapsed_time = benchmark_kernel(\n            repeat_times, attention_forward1,\n            cublas_handle, d_out, d_qkvr, d_preatt, d_att, d_inp, B, T, C, NH, block_size\n        );\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j ++) {\n        int block_size = block_sizes[j];\n        float elapsed_time = benchmark_kernel(\n            repeat_times, attention_backward,\n            cublas_handle, d_dinp, d_dqkvr, d_dpreatt, d_datt, d_scratch, d_dout, d_qkvr, d_att, B, T, C, NH\n        );\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(qkv);\n    free(out);\n    free(dout);\n    free(dqkv);\n    cudaCheck(cudaFree(d_inp));\n    cudaCheck(cudaFree(d_qkvr));\n    cudaCheck(cudaFree(d_out));\n    cudaCheck(cudaFree(d_preatt));\n    cudaCheck(cudaFree(d_att));\n    cudaCheck(cudaFree(d_dout));\n    cudaCheck(cudaFree(d_dinp));\n    cudaCheck(cudaFree(d_dqkvr));\n    cudaCheck(cudaFree(d_dpreatt));\n    cudaCheck(cudaFree(d_datt));\n    cudaCheck(cudaFree(d_scratch));\n\n    // destroy cublas handle\n    cublasCheck(cublasDestroy(cublas_handle));\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n\n\n__global__ void permute_kernel(float* q, float* k, float* v,\n                               const float* inp,\n                               int B, int N, int NH, int d) {\n    // okay so now, this kernel wants Q,K,V to all be of shape (B, NH, N, d)\n    // but instead, we have a single tensor QKV (inp) of shape (B, N, 3, NH, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Q[b][nh_][n][d_] = inp[b][n][0][nh_][d_]\n\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = \\\n            (b * N * 3 * NH * d)\n            +   (n * 3 * NH * d)\n            +       (0 * NH * d)\n            +          (nh_ * d)\n            +                d_;\n\n        q[idx] = inp[inp_idx];\n        k[idx] = inp[inp_idx + NH * d];\n        v[idx] = inp[inp_idx + 2 * (NH * d)];\n    }\n}\n\n__global__ void scale_kernel(float* inp, float scale, int B, int NH, int T) {\n    // scales the pre-softmax attention scores by scale\n    // we don't want causal attention, so we don't set any values to infinity\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * T * T) {\n        inp[idx] *= scale;\n    }\n}\n\n// warp-level reduction for finding the maximum value\n__device__ float warpReduceMax(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xFFFFFFFF, val, offset));\n    }\n    return val;\n}\n\n// warp-level reduction for summing values\n__device__ float warpReduceSum(float val) {\n    for (int offset = 16; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xFFFFFFFF, val, offset);\n    }\n    return val;\n}\n\n__global__ void softmax_forward_kernel4(float* out, const float* inp, int N, int C) {\n    // out is (N, C) just like inp. Each row of inp will get softmaxed.\n    // same as kernel3, but can handle any block size (multiple of 32)\n    // each row of C elements is handled by block_size threads\n    // furthermore, each block_size threads get executed in warps of 32 threads\n\n    // special reduction operations warpReduceMax/warpReduceSum are used for intra-warp reductions\n    // shared memory is used for inter-warp reduction\n    extern __shared__ float shared[];\n    int idx = blockIdx.x;\n    int tid = threadIdx.x;\n    int warpId = threadIdx.x / 32; // warp index within a block\n    int laneId = threadIdx.x % 32; // thread index within a warp\n\n    // the number of warps per block. recall that blockDim.x is block_size\n    int warpsPerBlock = blockDim.x / 32;\n\n    // shared[] must be allocated to have 2 * warpsPerBlock elements\n    // first half for max values, the second half for sum values\n    float* maxvals = shared;\n    float* sumvals = &shared[warpsPerBlock];\n\n    // one row of inp, i.e. inp[idx, :] of shape (C,)\n    const float* x = inp + idx * C;\n\n    // first, thread coarsening by directly accessing global memory in series\n    float maxval = -INFINITY;\n    for (int i = tid; i < C; i += blockDim.x) {\n        maxval = fmaxf(maxval, x[i]);\n    }\n    // now within-warp reductions for maxval\n    maxval = warpReduceMax(maxval);\n\n    // the 0th thread of each warp writes the maxval of that warp to shared memory\n    if (laneId == 0) maxvals[warpId] = maxval;\n    __syncthreads();\n\n    // now the 0th thread reduces the maxvals in shared memory, i.e. across warps\n    if (tid == 0) {\n        float val = maxvals[tid];\n        for (int i = 1; i < warpsPerBlock; i++) {\n            val = fmaxf(val, maxvals[i]);\n        }\n        // store the final max in the first position\n        maxvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the max to all threads\n    float offset = maxvals[0];\n\n    // compute expf and write the result to global memory\n    for (int i = tid; i < C; i += blockDim.x) {\n        // subtract max for numerical stability\n        out[idx * C + i] = expf(x[i] - offset);\n    }\n\n    // okay now we calculated exp(x - max(x))\n    // step 2: sum all the values and divide by the sum\n\n    // thread coarsening for sum\n    x = out + idx * C;\n    float sumval = 0.0f;\n    for (int i = tid; i < C; i += blockDim.x) {\n        sumval += x[i];\n    }\n    // within-warp reduction for sumval\n    sumval = warpReduceSum(sumval);\n\n    // write sumval to shared memory\n    if (laneId == 0) sumvals[warpId] = sumval;\n    __syncthreads();\n\n    // inter-thread reduction of sum\n    if (tid == 0) {\n        float val = sumvals[tid];\n        for (int i = 1; i < warpsPerBlock; ++i) {\n            val += sumvals[i];\n        }\n        sumvals[0] = val;\n    }\n    __syncthreads();\n    // broadcast the sum to all threads\n    float sum = sumvals[0];\n\n    // divide the whole row by the sum\n    for (int i = tid; i < C; i += blockDim.x) {\n        out[idx * C + i] = x[i] / sum;\n    }\n}\n\n__global__ void unpermute_kernel(const float* inp, float *out, int B, int N, int NH, int d) {\n   // out has shape (B, nh, N, d) but we need to unpermute it to (B, N, nh, d)\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // out[b][n][nh_][d_] <- inp[b][nh_][n][d_]\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        out[other_idx] = inp[idx];\n    }\n}\n\n// a simple attention forward function taken directly from llm.c kernel 3\nvoid attention_forward1\n(\n    hipblasHandle_t cublas_handle,\n    float* out, float* qkvr, float* preatt, float* att,\n    float* inp,\n    int B, int T, int C, int NH,\n    const int block_size\n) {\n    // note: resure inp as a scratch buffer because it is not used in backward\n    // inp is (B, T, 3C) QKV\n    // preatt, att are (B, NH, T, T)\n    // output is (B, T, C)\n    int HS = C / NH; // head size\n\n    // permute and separate inp from (B, T, 3, NH, HS) to 3X (B, NH, T, HS)\n    float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    int total_threads = B * NH * T * HS;\n    int num_blocks = ceil_div(total_threads, block_size);\n    permute_kernel<<<num_blocks, block_size>>>(q, k, v, inp, B, T, NH, HS);\n\n    // batched matrix multiply with cuBLAS\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle,\n                            HIPBLAS_OP_T, HIPBLAS_OP_N,\n                            T, T, HS,\n                            &alpha,\n                            k, HS, T * HS,\n                            q, HS, T * HS,\n                            &beta,\n                            preatt, T, T * T,\n                            B * NH));\n\n    // multiply all elements of preatt elementwise by scale\n    float scale = 1.0f / sqrtf(HS);\n    total_threads = B * NH * T * T;\n    num_blocks = ceil_div(total_threads, block_size);\n    scale_kernel<<<num_blocks, block_size>>>(preatt, scale, B, NH, T);\n\n    // softmax. preatt is (B, NH, T, T) but we view it as (B * NH * T, T) and use the softmax kernel\n    int softmax_block_size = 256;\n    int grid_size = B * NH * T;\n    size_t shared_mem_size = 2 * softmax_block_size / 32 * sizeof(float);\n    softmax_forward_kernel4<<<grid_size, softmax_block_size, shared_mem_size>>>(att, preatt, B * NH * T, T);\n\n    // new approach: first cuBLAS another batched matmul\n    // y = att @ v # (B, nh, T, T) @ (B, nh, T, hs) -> (B, nh, T, hs)\n    float* vaccum = inp;\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle,\n                            HIPBLAS_OP_N, HIPBLAS_OP_N,\n                            HS, T, T,\n                            &alpha,\n                            v, HS, T * HS,\n                            att, T, T * T,\n                            &beta,\n                            vaccum, HS, T * HS,\n                            B * NH));\n\n    // now unpermute\n    // y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side\n    num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel<<<num_blocks, block_size>>>(vaccum, out, B, T, NH, HS);\n}\n\n__global__ void permute_kernel_backward(float* dinp,\n                                        const float* dq, const float* dk, const float* dv,\n                                        int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n\n        int inp_idx = (b * N * 3 * NH * d) + (n * 3 * NH * d) + (0 * NH * d) + (nh_ * d) + d_;\n        dinp[inp_idx] = dq[idx];\n        dinp[inp_idx + NH * d] = dk[idx];\n        dinp[inp_idx + 2 * (NH * d)] = dv[idx];\n    }\n}\n\n__global__ void unpermute_kernel_backward(float* dinp, const float *dout, int B, int N, int NH, int d) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * NH * N * d) {\n        int b = idx / (NH * N * d);\n        int rest = idx % (NH * N * d);\n        int nh_ = rest / (N * d);\n        rest = rest % (N * d);\n        int n = rest / d;\n        int d_ = rest % d;\n        int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;\n        dinp[idx] = dout[other_idx];\n    }\n}\n\n// slight edit from the softmax_autoregressive_backward_kernel from train_gpt2_fp32\n// our attention is no longer autoregressive\n__global__ void softmax_backward_kernel(float* dpreatt, const float* datt, const float* att,\n                                                       int B, int T, int C, float scale) {\n    constexpr const int BlockSize = 256;\n    constexpr int T_per_block = 4;\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float block_acc[32];\n\n    int idx = blockIdx.y;\n    // go through blocks in reverse order, so the slowest block starts first\n    int t0 = T - 1 - T_per_block*blockIdx.x;\n\n    att += idx * T * T;\n    datt += idx * T * T;\n    dpreatt += idx * T * T;\n\n    if (warp.meta_group_rank() == 0) {\n        block_acc[warp.thread_rank()] = 0;\n    }\n\n    for(int to = 0; to < T_per_block; ++to) {\n        int t = t0 - to;\n        if(t < 0) return;\n        const float* att_bth = att + t * T;\n        const float* datt_bth = datt + t * T;\n        float* dpreatt_bth = dpreatt + t * T;\n\n        float local_sum = 0.0f;\n        for (int t2 = block.thread_rank(); t2 < T; t2 += BlockSize) {\n            local_sum += att_bth[t2] * datt_bth[t2];\n        }\n\n        block_acc[warp.meta_group_rank()] = cg::reduce(warp, local_sum, cg::plus<float>{});\n        block.sync();\n        local_sum = cg::reduce(warp, block_acc[warp.thread_rank()], cg::plus<float>{});\n\n        for (int t3 = block.thread_rank(); t3 < T; t3 += BlockSize) {\n            // don't touch the cache. Some parts will still be here from the previous loop, and\n            // we want to exploit those.\n            float acc = __ldcs(att_bth + t3) * (__ldcs(datt_bth + t3) - local_sum);\n            __stcs(dpreatt_bth + t3, scale * acc);\n        }\n    }\n}\n\n// the sequence of transformations in this compound op is:\n// inp (B,T,3C) -> qkvr (B,T,3C) -> preatt (B,NH,T,T) -> att (B,NH,T,T) -> vaccum (B,T,C) -> out (B,T,C)\nvoid attention_backward\n(\n    hipblasHandle_t cublas_handle,\n    float* dinp, float* dqkvr, float* dpreatt, float* datt, float* scratch,\n    const float* dout,\n    const float* qkvr, const float* att,\n    int B, int T, int C, int NH\n) {\n    const int block_size = 256;\n    int HS = C / NH; // head size\n    const float one = 1.0f;\n    const float zero = 0.0f; // note beta = 1.0f so that we accumulate gradients (+=)\n    // unpack convenience pointers into q, k, v\n    const float *q, *k, *v;\n    q = qkvr + 0 * B * T * C;\n    k = qkvr + 1 * B * T * C;\n    v = qkvr + 2 * B * T * C;\n    float *dq, *dk, *dv;\n    dq = dqkvr + 0 * B * T * C;\n    dk = dqkvr + 1 * B * T * C;\n    dv = dqkvr + 2 * B * T * C;\n    // backward through the unpermute operation\n    int num_blocks = ceil_div(B * T * C, block_size);\n    unpermute_kernel_backward<<<num_blocks, block_size>>>(scratch, dout, B, T, NH, HS);\n    cudaCheck(hipGetLastError());\n    // backward into datt\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, T, T, HS, &one, v, HS, T * HS, scratch, HS, T * HS, &zero, datt, T, T * T, B * NH));\n    // backward into dv\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, HS, T, T, &one, scratch, HS, T * HS, att, T, T * T, &zero, dv, HS, T * HS, B * NH));\n    // backward into preatt\n    int hs = C / NH; // head size\n    float scale = 1.0f / sqrtf(hs);\n    softmax_backward_kernel<<<dim3(T / 4, B * NH), 256>>>(dpreatt, datt, att, B, T, C, scale);\n    cudaCheck(hipGetLastError());\n    // backward into q\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, HS, T, T, &one, k, HS, T * HS, dpreatt, T, T * T, &zero, dq, HS, T * HS, B * NH));\n    // backward into k\n    cublasCheck(hipblasSgemmStridedBatched(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, HS, T, T, &one, q, HS, T * HS, dpreatt, T, T * T, &zero, dk, HS, T * HS, B * NH));\n    // backward into inp\n    num_blocks = ceil_div(B * NH * T * HS, block_size);\n    permute_kernel_backward<<<num_blocks, block_size>>>(dinp, dq, dk, dv, B, T, NH, HS);\n    cudaCheck(hipGetLastError());\n}\n\n\n#ifndef LINKING\nint main(int argc, char** argv) {\n    int B = 4;\n    int T = 1024;\n    int C = 256;\n    int HS = 32;\n    int NH = C / HS;\n\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* qkv = (float*)malloc(B * T * 3 * C * sizeof(float));\n    float* out = (float*)malloc(B * T * C * sizeof(float));\n    float* dout = (float*)malloc(B * T * C * sizeof(float));\n    float* dqkv = (float*)malloc(B * T * 3 * C * sizeof(float));\n\n    // read saved output\n    FILE * file = fopen(\"attention.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(qkv, sizeof(float), B * T * 3 * C, file);\n    freadCheck(out, sizeof(float), B * T * C, file);\n    freadCheck(dout, sizeof(float), B * T * C, file);\n    freadCheck(dqkv, sizeof(float), B * T * 3 * C, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_inp, *d_qkvr, *d_out, *d_preatt, *d_att, *d_dout, *d_dinp, *d_dqkvr, *d_dpreatt, *d_datt, *d_scratch;\n    cudaCheck(hipMalloc(&d_inp, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMemcpy(d_inp, qkv, B * T * 3 * C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMalloc(&d_qkvr, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMemset(d_qkvr, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * T * C * sizeof(float)));\n    cudaCheck(hipMemset(d_out, 0, B * T * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_preatt, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMemset(d_preatt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMalloc(&d_att, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMemset(d_att, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * T * C * sizeof(float)));\n    cudaCheck(hipMemcpy(d_dout, dout, B * T * C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMalloc(&d_dinp, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMemset(d_dinp, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dqkvr, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMemset(d_dqkvr, 0, B * T * 3 * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dpreatt, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMemset(d_dpreatt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMalloc(&d_datt, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMemset(d_datt, 0, B * NH * T * T * sizeof(float)));\n    cudaCheck(hipMalloc(&d_scratch, B * T * C * sizeof(float)));\n    cudaCheck(hipMemset(d_scratch, 0, B * T * C * sizeof(float)));\n\n    // forward pass\n    int block_sizes[] = {256, 512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        attention_forward1(cublas_handle, d_out, d_qkvr, d_preatt, d_att, d_inp, B, T, C, NH, block_size);\n        validate_result(d_out, out, \"out\", B * T * C);\n        // reset d_inp, which is used as a buffer\n        cudaCheck(hipMemcpy(d_inp, qkv, B * T * 3 * C * sizeof(float), hipMemcpyHostToDevice));\n    }\n\n    printf(\"\\nChecking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        attention_backward(cublas_handle, d_dinp, d_dqkvr, d_dpreatt, d_datt, d_scratch, d_dout, d_qkvr, d_att, B, T, C, NH);\n        validate_result(d_dinp, dqkv, \"dinp\", B * T * 3 * C);\n    }\n\n    printf(\"\\nAll results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    int repeat_times = 100;\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j ++) {\n        int block_size = block_sizes[j];\n        float elapsed_time = benchmark_kernel(\n            repeat_times, attention_forward1,\n            cublas_handle, d_out, d_qkvr, d_preatt, d_att, d_inp, B, T, C, NH, block_size\n        );\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j ++) {\n        int block_size = block_sizes[j];\n        float elapsed_time = benchmark_kernel(\n            repeat_times, attention_backward,\n            cublas_handle, d_dinp, d_dqkvr, d_dpreatt, d_datt, d_scratch, d_dout, d_qkvr, d_att, B, T, C, NH\n        );\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(qkv);\n    free(out);\n    free(dout);\n    free(dqkv);\n    cudaCheck(hipFree(d_inp));\n    cudaCheck(hipFree(d_qkvr));\n    cudaCheck(hipFree(d_out));\n    cudaCheck(hipFree(d_preatt));\n    cudaCheck(hipFree(d_att));\n    cudaCheck(hipFree(d_dout));\n    cudaCheck(hipFree(d_dinp));\n    cudaCheck(hipFree(d_dqkvr));\n    cudaCheck(hipFree(d_dpreatt));\n    cudaCheck(hipFree(d_datt));\n    cudaCheck(hipFree(d_scratch));\n\n    // destroy cublas handle\n    cublasCheck(hipblasDestroy(cublas_handle));\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/attention.cu"}
{"input": "#include <cstdio>\n#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"silu.cuh\"\n\n\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n\n__global__ void silu_forward_kernel(\n    const float* x,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n\n    float x_val = x[idx];\n    out[idx] = x_val / (1.0f + expf(-x_val));\n}\n\n\n__global__ void silu_backward_kernel(\n    const float* dout, const float* x,\n    float* dx,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n    float out_val = dout[idx];\n    float x_val = x[idx];\n    float expx = expf(-x_val);\n    float grad_silu = (1.0f + x_val * expx / (1.0f + expx)) / (1.0f + expx);\n    dx[idx] = out_val * grad_silu;\n}\n\n\n// ----------------------------------------------------------------------------\n// CUDA kernel launcher\n\nvoid silu_forward(\n    const float* x,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_forward_kernel<<<n_blk, block_size>>>(x, out, N);\n}\n\nvoid silu_backward(\n    const float* dout, const float* x,\n    float* dx,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_backward_kernel<<<n_blk, block_size>>>(dout, x, dx, N);\n}\n\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int B = 1;\n    int C = 32;\n    int H = 32;\n    int W = 32;\n    int N = B * C * H * W;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory and load data\n    float* x = (float*)malloc(N * sizeof(float));\n    float* out = (float*)malloc(N * sizeof(float));\n    float* dout = (float*)malloc(N * sizeof(float));\n    float* dx = (float*)malloc(N * sizeof(float));\n\n    // read saved output\n    FILE *file = fopenCheck(\"silu.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(out, sizeof(float), N, file);\n    freadCheck(dout, sizeof(float), N, file);\n    freadCheck(dx, sizeof(float), N, file);\n    fcloseCheck(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(cudaMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, N * sizeof(float), cudaMemcpyHostToDevice));\n\n    int block_sizes[] = {128, 256, 512, 1024};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        silu_forward(d_x, d_out, N, block_size);\n        validate_result(d_out, out, \"out\", N);\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        silu_backward(d_dout, d_x, d_dx, N, block_size);\n        validate_result(d_dx, dx, \"dx\", N);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, silu_forward,\n                                              d_x, d_out, N, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, silu_backward,\n                                              d_dout, d_x, d_dx,\n                                              N, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n\n    cudaCheck(cudaFree(d_x));\n    cudaCheck(cudaFree(d_out));\n    cudaCheck(cudaFree(d_dout));\n    cudaCheck(cudaFree(d_dx));\n}\n#endif", "output": "#include <cstdio>\n#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"silu.cuh\"\n\n\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n\n__global__ void silu_forward_kernel(\n    const float* x,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n\n    float x_val = x[idx];\n    out[idx] = x_val / (1.0f + expf(-x_val));\n}\n\n\n__global__ void silu_backward_kernel(\n    const float* dout, const float* x,\n    float* dx,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx >= N) {\n        return;\n    }\n    float out_val = dout[idx];\n    float x_val = x[idx];\n    float expx = expf(-x_val);\n    float grad_silu = (1.0f + x_val * expx / (1.0f + expx)) / (1.0f + expx);\n    dx[idx] = out_val * grad_silu;\n}\n\n\n// ----------------------------------------------------------------------------\n// CUDA kernel launcher\n\nvoid silu_forward(\n    const float* x,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_forward_kernel<<<n_blk, block_size>>>(x, out, N);\n}\n\nvoid silu_backward(\n    const float* dout, const float* x,\n    float* dx,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    silu_backward_kernel<<<n_blk, block_size>>>(dout, x, dx, N);\n}\n\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int B = 1;\n    int C = 32;\n    int H = 32;\n    int W = 32;\n    int N = B * C * H * W;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory and load data\n    float* x = (float*)malloc(N * sizeof(float));\n    float* out = (float*)malloc(N * sizeof(float));\n    float* dout = (float*)malloc(N * sizeof(float));\n    float* dx = (float*)malloc(N * sizeof(float));\n\n    // read saved output\n    FILE *file = fopenCheck(\"silu.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(out, sizeof(float), N, file);\n    freadCheck(dout, sizeof(float), N, file);\n    freadCheck(dx, sizeof(float), N, file);\n    fcloseCheck(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(hipMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, N * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, N * sizeof(float), hipMemcpyHostToDevice));\n\n    int block_sizes[] = {128, 256, 512, 1024};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        silu_forward(d_x, d_out, N, block_size);\n        validate_result(d_out, out, \"out\", N);\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        silu_backward(d_dout, d_x, d_dx, N, block_size);\n        validate_result(d_dx, dx, \"dx\", N);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, silu_forward,\n                                              d_x, d_out, N, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, silu_backward,\n                                              d_dout, d_x, d_dx,\n                                              N, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n\n    cudaCheck(hipFree(d_x));\n    cudaCheck(hipFree(d_out));\n    cudaCheck(hipFree(d_dout));\n    cudaCheck(hipFree(d_dx));\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/silu.cu"}
{"input": "#include <cuda_runtime.h>\n#include \"add.cuh\"\n#include \"common.h\"\n\n// simple kernel to add two tensors\n__global__ void add_forward_kernel(\n    const float* a, const float* b,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        out[idx] = a[idx] + b[idx];\n    }\n}\n\nvoid add_forward(\n    const float* a, const float* b,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_forward_kernel<<<n_blk, block_size>>>(a, b, out, N);\n}\n\n\n// add A and B in place, and store result in B\n__global__ void add_inplace_forward_kernel(\n    const float* a, float* b,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        b[idx] += a[idx];\n    }\n}\n\nvoid add_inplace_forward(\n    const float* a, float* b,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_inplace_forward_kernel<<<n_blk, block_size>>>(a, b, N);\n}", "output": "#include <hip/hip_runtime.h>\n#include \"add.cuh\"\n#include \"common.h\"\n\n// simple kernel to add two tensors\n__global__ void add_forward_kernel(\n    const float* a, const float* b,\n    float* out,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        out[idx] = a[idx] + b[idx];\n    }\n}\n\nvoid add_forward(\n    const float* a, const float* b,\n    float* out,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_forward_kernel<<<n_blk, block_size>>>(a, b, out, N);\n}\n\n\n// add A and B in place, and store result in B\n__global__ void add_inplace_forward_kernel(\n    const float* a, float* b,\n    int N\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < N) {\n        b[idx] += a[idx];\n    }\n}\n\nvoid add_inplace_forward(\n    const float* a, float* b,\n    int N, int block_size\n) {\n    int n_blk = ceil_div(N, block_size);\n    add_inplace_forward_kernel<<<n_blk, block_size>>>(a, b, N);\n}", "source": "/kaggle/working/unet.cu/dev/add.cu"}
{"input": "#include <cuda_profiler_api.h>\n#include <cuda_runtime_api.h>\n#include <curand_kernel.h>\n#include <unistd.h> // For POSIX systems to use getcwd\n#include <limits.h> // For PATH_MAX\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n#include \"common.h\"\n#include \"linear.cuh\"\n#include \"silu.cuh\"\n#include \"conv2d_k3.cuh\"\n#include \"resblock.cuh\"\n#include \"add.cuh\"\n#include \"avgpool.cuh\"\n#include \"attention_block.cuh\"\n#include \"concat_channel.cuh\"\n#include \"add.cuh\"\n#include \"upsample.cuh\"\n#include \"groupnorm.cuh\"\n#include \"mse.cuh\"\n#include \"timestep_embedding.cuh\"\n#include \"rand.h\"\n#include <chrono>\n\n\n\ntypedef struct {\n    int C_in;\n    int C_model;\n    int C_time;\n    int C_out;\n    int B;\n    int H;\n    int W;\n    int gn_n_groups;\n    int HS;\n    int block_size;\n    int n_layers;\n    int n_levels;\n    int n_res;\n    int att_start_level;\n    int* channel_mult;\n    int max_period;\n} UnetConfig;\n\n// ------------------------------------------------------------------------------------------------\n// DataLoader\n\nconst char* DATASET_FILE = \"../datasets/elephant_train.bin\";\ntypedef struct {\n    int B;\n    int C_in;\n    int H;\n    int W;\n    int img_size;\n    int n_imgs;\n    FILE* img_file;\n    long cur_position;\n    long file_size;\n    long header_offset;\n    long num_batches;\n    float* input;\n} DataLoader;\n\nvoid dataloader_init(DataLoader* loader, const char* filename, int B) {\n    loader->B = B;\n\n    loader->img_file = fopenCheck(filename, \"rb\");\n    int data_header[256];\n    loader->header_offset = 256 * sizeof(int);\n    freadCheck(data_header, sizeof(int), 256, loader->img_file);\n    if (data_header[0] != 20240620) {\n        fprintf(stderr, \"Invalid magic data file\\n\");\n        exit(1);\n    }\n    loader->n_imgs = data_header[1];\n    loader->C_in = data_header[2];\n    loader->H = data_header[3];\n    loader->W = data_header[4];\n    loader->img_size = loader->C_in * loader->H * loader->W;\n\n    // determine file size\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    fseekCheck(loader->img_file, 0, SEEK_END);\n    loader->file_size = ftell(loader->img_file);\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    assert((loader->file_size - loader->header_offset) / (loader->img_size * sizeof(float)) == loader->n_imgs);\n    \n    loader->cur_position = loader->header_offset;\n    loader->num_batches = loader->n_imgs / B;\n\n    // allocate cuda CPU pinned memory for inputs\n    cudaMallocHost((void**)&loader->input, B * loader->img_size * sizeof(float));\n}\n\nvoid dataloader_reset(DataLoader* loader) {\n    loader->cur_position = loader->header_offset;\n}\n\nvoid dataloader_next_batch(DataLoader* loader) {\n    int B = loader->B;\n    int img_size = loader->img_size;\n    if (loader->cur_position + (B * img_size * sizeof(float)) > loader->file_size) {\n        dataloader_reset(loader);\n    }\n    fseekCheck(loader->img_file, loader->cur_position, SEEK_SET);\n    freadCheck(loader->input, sizeof(float), B * img_size, loader->img_file);\n    loader->cur_position += B * img_size * sizeof(float);\n}\n\nvoid dataloader_free(DataLoader* loader) {\n    fcloseCheck(loader->img_file);\n    cudaFreeHost(loader->input);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Diffusion\ntypedef struct {\n    int B;\n    int img_size;\n    int max_period;\n    float* betas; // (max_period,)\n    float* sqrt_alphas_cumprod; // (max_period,)\n    float* sqrt_one_minus_alphas_cumprod; // (max_period,)\n    int block_size;\n    curandState* curand_states;\n    mt19937_state host_rng;\n} GaussianDiffusion;\n\n__global__ void init_curand_states(curandState *state, unsigned long seed, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N) {\n        curand_init(seed, idx, 0, &state[idx]);\n    }\n}\n\nvoid diffusion_init(\n    GaussianDiffusion* diffusion, int B, int C_in, int H, int W, int max_period, int block_size = 512,\n    unsigned long seed = 0, float beta_start = 1e-4, float beta_end = 0.02f\n) {\n    diffusion->B = B;\n    diffusion->max_period = max_period;\n    diffusion->block_size = block_size;\n    diffusion->img_size = C_in * H * W;\n\n    float scale = 1000.0f / max_period;\n    beta_start *= scale;\n    beta_end *= scale;\n\n    // calculate betas and alphas on host then move to device\n    float* betas = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_one_minus_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n\n    float alpha_cumprod = 1.0f;\n    for (int t = 1; t <= max_period; t++) {\n        // linear interpolate for beta\n        betas[t - 1] = (beta_start * (max_period - t) + beta_end * (t - 1)) / (max_period - 1);\n        alpha_cumprod *= 1.0f - betas[t - 1];\n        sqrt_alphas_cumprod[t - 1] = sqrtf(alpha_cumprod);\n        sqrt_one_minus_alphas_cumprod[t - 1] = sqrtf(1.0f - alpha_cumprod);\n    }\n\n    float *d_betas, *d_sqrt_alphas_cumprod, *d_sqrt_one_minus_alphas_cumprod;\n    cudaCheck(cudaMalloc(&d_betas, max_period * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_sqrt_alphas_cumprod, max_period * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_sqrt_one_minus_alphas_cumprod, max_period * sizeof(float)));\n\n    cudaCheck(cudaMemcpy(d_betas, betas, max_period * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_sqrt_alphas_cumprod, sqrt_alphas_cumprod, max_period * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_sqrt_one_minus_alphas_cumprod, sqrt_one_minus_alphas_cumprod, max_period * sizeof(float), cudaMemcpyHostToDevice));\n\n    free(betas);\n    free(sqrt_alphas_cumprod);\n    free(sqrt_one_minus_alphas_cumprod);\n\n    diffusion->betas = d_betas;\n    diffusion->sqrt_alphas_cumprod = d_sqrt_alphas_cumprod;\n    diffusion->sqrt_one_minus_alphas_cumprod = d_sqrt_one_minus_alphas_cumprod;\n\n    // init curand states for sampling noise of the size of one batch of inputs\n    int total_states = B * C_in * H * W;\n    int n_blocks = ceil_div(total_states, block_size);\n    cudaCheck(cudaMalloc(&diffusion->curand_states, total_states * sizeof(curandState)));\n    init_curand_states<<<n_blocks, block_size>>>(diffusion->curand_states, seed, total_states);\n\n    // init host rng for drawing random timesteps\n    manual_seed(&diffusion->host_rng, seed);\n}\n\n// gridDim will be of shape (N / block_size, B)\n// expect x and x_t to be of shape (B, N)\n// and timesteps to be of shape (B,)\n// expect x_t to be filled with standard normals\n__global__ void diffusion_sample_kernel(\n    float* timesteps, float* x, float* x_t,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    int B, int N\n) {\n    int b = blockIdx.y;\n    int n = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (n < N && b < B) {\n        int t = (int)timesteps[b];\n        float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n        float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n        float x_t_val = x_t[b * N + n]; // x_t starts out as standard normals\n        float x_val = x[b * N + n];\n        x_t[b * N + n] = x_val * sqrt_alpha_t + x_t_val * sqrt_one_minus_alpha_t;\n    }\n}\n\nvoid diffusion_sample(\n    float* timesteps, float* x, float* x_t,\n    GaussianDiffusion *diffusion,\n    int B, int N\n) {\n    dim3 gridDim(ceil_div(N, diffusion->block_size), B);\n    diffusion_sample_kernel<<<gridDim, diffusion->block_size>>>(timesteps, x, x_t, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, B, N);\n    cudaCheck(cudaDeviceSynchronize());\n}\n\n// a kernel that creates x_t = sqrt_alpha * x + sqrt(1 - alpha) * noise in one go\n// that draws the noise from standard normals, gets alpha from the timesteps and the precomputed alpha cumprod\n// and saves output to x_t\n// if the input x, timesteps and the alpha buffers are null, then it will just fill x_t with standard normals\n// to be called with B * N total threads\n__global__ void diffusion_draw_and_sample_xt_kernel(\n    float* x_t, int B, int N, curandState* curand_states,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    float* timesteps = nullptr, float* x = nullptr\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        float out_val = curand_normal(&curand_states[idx]);\n        if (timesteps != nullptr && x != nullptr) {\n            int b = idx / N;\n            int n = idx % N;\n            float x_val = x[b * N + n];\n            int t = (int)timesteps[b];\n            float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n            float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n            out_val = x_val * sqrt_alpha_t + out_val * sqrt_one_minus_alpha_t;\n        }\n        x_t[idx] = out_val;\n    }\n}\n\nvoid diffusion_draw_and_sample_xt(\n    GaussianDiffusion *diffusion,\n    float* x_t,\n    float* timesteps = nullptr, float* x = nullptr\n) {\n    int total_states = diffusion->B * diffusion->img_size;\n    int n_blocks = ceil_div(total_states, diffusion->block_size);\n    diffusion_draw_and_sample_xt_kernel<<<n_blocks, diffusion->block_size>>>(\n        x_t, diffusion->B, diffusion->img_size, diffusion->curand_states, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, timesteps, x\n    );\n}\n\nvoid diffusion_free(GaussianDiffusion* diffusion) {\n    cudaCheck(cudaFree(diffusion->betas));\n    cudaCheck(cudaFree(diffusion->sqrt_alphas_cumprod));\n    cudaCheck(cudaFree(diffusion->sqrt_one_minus_alphas_cumprod));\n    cudaCheck(cudaFree(diffusion->curand_states));\n}\n\n// samples B timesteps in range [0, max_period), convert them to floats and\n// store in timesteps\nvoid sample_timesteps(GaussianDiffusion *diffusion, float* timesteps, int N = -1) {\n    mt19937_state* rng = &diffusion->host_rng;\n    if (N < 0) {\n        N = diffusion->B;\n    }\n    int max_period = diffusion->max_period;\n    for (int i = 0; i < N; i++) {\n        float rand_float = randfloat32(rng);\n        timesteps[i] = (float)(int)(rand_float * max_period);\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet init\n\n// we just need a single set of buffers that are shared for all the backward pass activations\n#define NUM_SKIP_CONNECTIONS 12 // (n_res + 1) * n_levels\n#define NUM_BACK_BUFS 10\n#define UNET_NUM_BACK_ACTS 22\ntypedef struct {\n    float* dinp_buf1; // used to store the input gradient, which will be reused as dout for the previous layer\n    float* dinp_buf2;\n    float* buf1_BCHW; // NOTE: first 4 buffers will have  max B * C * H * W size\n    float* buf2_BCHW;\n    float* buf3_BCHW;\n    float* buf4_BCHW;\n    float* buf1_B3CHW; // NOTE: last 2 buffers will have max B * 3 * C * H * W size for attention layers\n    float* buf2_B3CHW; // so max of H * W is only over dimensions that have attention\n    float* demb_buf1; // used to store cumulative gradients w.r.t. timestep embeddings\n    float* demb_buf2;\n    float* skip_dinps[NUM_SKIP_CONNECTIONS];\n} UnetBackActs;\n\n// This is used as a block to store all the params / grads / acts for all the layers\ntypedef struct {\n    float* mem_ptr;\n    size_t total_size;\n    int n_layers;\n    void** layer_ptrs;\n    size_t* layer_sizes;\n} UnetBlock;\n\ntypedef struct {\n    float* back_mem;\n    UnetBackActs back_acts;\n    size_t back_sizes[UNET_NUM_BACK_ACTS];\n    size_t n_backs;\n} UnetBackBlock;\n\ntypedef struct {\n    UnetConfig config;\n    float* input;\n    float* timestep_emb; // this is the already computed embeddings of shape (B, C_model)\n    UnetBlock params;\n    UnetBlock grads;\n    UnetBlock acts;\n    UnetBackBlock back;\n    float* skip_acts[NUM_SKIP_CONNECTIONS]; // pointers to skip connetion activations\n    // the next two are for debug purposes only, just to keep track of the buffer that contains the grad w.r.t. inputs\n    float* dinp;\n    // model output\n    float* output;\n    // targets and losses\n    float* target;\n    float mean_loss;\n    float* gpu_loss;\n    // buffers for AdamW\n    float* m_memory;\n    float* v_memory;\n    TimestepEmbedding time_emb_state;\n} Unet;\n\nvoid unet_init(Unet* unet) {\n    unet->input = nullptr;\n    unet->timestep_emb = nullptr;\n    unet->params.mem_ptr = nullptr;\n    unet->grads.mem_ptr = nullptr;\n    unet->acts.mem_ptr = nullptr;\n    unet->back.back_mem = nullptr;\n    unet->dinp = nullptr;\n    unet->output = nullptr;\n    unet->target = nullptr;\n    unet->mean_loss = -1.0f;\n    unet->gpu_loss = nullptr;\n    unet->m_memory = nullptr;\n    unet->v_memory = nullptr;\n\n    init_timestep_embedding(&unet->time_emb_state, unet->config.C_model, unet->config.B, unet->config.max_period);\n}\n\n\ntemplate <typename T>\nvoid free_not_null(T** ptr) {\n    if (*ptr != nullptr) {\n        free(*ptr);\n        *ptr = nullptr;\n    }\n}\n\nvoid free_unet_block(UnetBlock* params) {\n    free_not_null(&params->layer_sizes);\n    for (int l = 0; l < params->n_layers; l++) {\n        free_not_null(&(params->layer_ptrs[l]));\n    }\n    free_not_null(&params->layer_ptrs);\n    cudaCheck(cudaFree(params->mem_ptr));\n}\n\nvoid free_unet(Unet* unet) {\n    cudaCheck(cudaFree(unet->input));\n    cudaCheck(cudaFree(unet->timestep_emb));\n    cudaCheck(cudaFree(unet->gpu_loss));\n    cudaCheck(cudaFree(unet->m_memory));\n    cudaCheck(cudaFree(unet->v_memory));\n\n    free_unet_block(&(unet->params));\n    free_unet_block(&(unet->grads));\n    free_unet_block(&(unet->acts));\n\n    cudaCheck(cudaFree(unet->back.back_mem));\n    free_timestep_embedding(&unet->time_emb_state);\n}\n\nvoid unet_count_layers(UnetConfig* config) {\n    int n_layers = 0;\n    int n_res = config->n_res;\n    int n_levels = config->n_levels;\n    int att_start_level = config->att_start_level;\n\n    // timestep emb\n    n_layers += 3;\n\n    // initial conv\n    n_layers += 1;\n\n    // add downsample resblock layers\n    n_layers += n_res * n_levels;\n\n    // add downsample attention\n    n_layers += n_res * (n_levels - att_start_level);\n\n    // add avgpool layers\n    n_layers += n_levels - 1;\n\n    // add middle layers\n    n_layers += 3;\n\n    // add upsample concat layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample resblock layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample attention layers\n    n_layers += (n_res + 1) * (n_levels - att_start_level);\n\n    // add upsample layers\n    n_layers += n_levels - 1;\n\n    // output layers\n    n_layers += 3;\n\n    config->n_layers = n_layers;\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// allocate memory for structs with info for each layer and count params\n\n\nint make_linear_params_ptrs(\n    UnetBlock* params,\n    int layer,\n    int C_in, int C_out\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearParams));\n    params->layer_sizes[layer] = linear_count_params(C_in, C_out);\n    return layer + 1;\n}\n\nint make_silu_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_convk3_params_ptrs(\n    UnetBlock* params, int layer,\n    int C_in, int OC, int B, int H, int W\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Params));\n    params->layer_sizes[layer] = convk3_count_params(C_in, OC);\n    return layer + 1;\n}\n\nint make_resblock_params_ptrs(\n    UnetBlock* params, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockParameters));\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    resblock_count_params(resblock_params, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    params->layer_sizes[layer] = resblock_params->n_params;\n    return layer + 1;\n}\n\nint make_avgpool_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_attention_block_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionParams));\n    AttentionParams* attention_params = (AttentionParams*)params->layer_ptrs[layer];\n    attention_block_count_params(attention_params, C);\n    params->layer_sizes[layer] = attention_params->n_params;\n    return layer + 1;\n}\n\nint make_concat_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_upsample_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_groupnorm_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormParams));\n    params->layer_sizes[layer] = C * 2;\n    return layer + 1;\n}\n\n\nvoid unet_setup_and_count_params(UnetConfig* config, UnetBlock* params) {\n    assert (config->n_layers > 0);\n\n    params->n_layers = config->n_layers;\n    params->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    params->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_params_ptrs(params, layer, C_model, C_time);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_linear_params_ptrs(params, layer, C_time, C_time);\n    int OC = C_model;\n    layer = make_convk3_params_ptrs(params, layer, C_in, OC, B, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_params_ptrs(params, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_params_ptrs(params, layer);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    // middle layers\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_params_ptrs(params, layer, C);\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards: concat then resblock\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = C_model * mult;\n        for (int r = 0; r < n_res + 1; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = C_model * prev_mult;\n            }\n            OC = C_model * mult;\n            layer = make_concat_params_ptrs(params, layer);\n            layer = make_resblock_params_ptrs(params, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_params_ptrs(params, layer);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    layer = make_groupnorm_params_ptrs(params, layer, C);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_convk3_params_ptrs(params, layer, C, C_out, B, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += params->layer_sizes[i];\n    }\n    params->total_size = total_size;\n}\n\nint make_linear_acts_ptrs(\n    UnetBlock* acts,\n    int layer,\n    int B, int C_out\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearActs));\n    // acts buffers only hold outputs\n    acts->layer_sizes[layer] = B * C_out;\n    return layer + 1;\n}\n\nint make_silu_acts_ptrs(UnetBlock* acts, int layer, int N) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(SiluActs));\n    acts->layer_sizes[layer] = N;\n    return layer + 1;\n}\n\nint make_convk3_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int OC, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Acts));\n    acts->layer_sizes[layer] = B * OC * H * W;\n    return layer + 1;\n}\n\nint make_resblock_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockActivations));\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    resblock_count_acts(resblock_acts, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    acts->layer_sizes[layer] = resblock_acts->n_acts;\n    return layer + 1;\n}\n\nint make_avgpool_acts_ptrs(UnetBlock* acts, int layer, int B, int C, int H, int W) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AvgpoolActs));\n    acts->layer_sizes[layer] = B * C * H/2 * W/2;\n    return layer + 1;\n}\n\nint make_attention_block_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionActs));\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    attention_block_count_acts(att_acts, B, C, H, W, gn_n_groups, HS);\n    acts->layer_sizes[layer] = att_acts->n_acts;\n    return layer + 1;\n}\n\nint make_concat_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C1, int C2, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConcatChannelActs));\n    acts->layer_sizes[layer] = B * (C1 + C2) * H * W;\n    return layer + 1;\n}\n\nint make_upsample_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(UpsampleActs));\n    acts->layer_sizes[layer] = B * C * H * 2 * W * 2;\n    return layer + 1;\n}\n\nint make_groupnorm_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormActs));\n    // out (B, C, H, W), mean and rstd (B, gn_n_groups)\n    acts->layer_sizes[layer] = B * C * H * W + B * gn_n_groups * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_acts(UnetConfig* config, UnetBlock* acts) {\n    assert (config->n_layers > 0);\n\n    acts->n_layers = config->n_layers;\n    acts->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    acts->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int HS = config->HS;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    layer = make_silu_acts_ptrs(acts, layer, B * C_time);\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    int OC = C_model;\n    layer = make_convk3_acts_ptrs(acts, layer, B, OC, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_acts_ptrs(acts, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_acts_ptrs(acts, layer, B, C, H, W);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = prev_mult * C_model;\n            }\n            layer = make_concat_acts_ptrs(acts, layer, B, C, C_skip, H, W);\n            OC = mult * C_model;\n            layer = make_resblock_acts_ptrs(acts, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_acts_ptrs(acts, layer, B, C, H, W);\n            H *= 2;\n            W *= 2;\n        }\n    }\n    layer = make_groupnorm_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups);\n    layer = make_silu_acts_ptrs(acts, layer, B * C * H * W);\n    layer = make_convk3_acts_ptrs(acts, layer, B, C_out, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += acts->layer_sizes[i];\n    }\n    acts->total_size = total_size;\n}\n\nvoid unet_setup_and_count_backs(UnetConfig* config, UnetBackBlock* back) {\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int H = config->H;\n    int W = config->W;\n\n    size_t max_BCHW = 3 * B * C_model * H * W;\n    size_t max_B3CHW = 2 * B * 3 * C_model * H * W;\n    back->back_sizes[0] = max_BCHW;\n    back->back_sizes[1] = max_BCHW;\n    back->back_sizes[2] = max_BCHW;\n    back->back_sizes[3] = max_BCHW;\n    back->back_sizes[4] = max_BCHW;\n    back->back_sizes[5] = max_BCHW;\n    back->back_sizes[6] = max_B3CHW;\n    back->back_sizes[7] = max_B3CHW;\n    back->back_sizes[8] = B * C_time;\n    back->back_sizes[9] = B * C_time;\n\n    // fill the sizes of the skip connection backward acts\n    int C = C_model;\n    for (int l = 0; l < config->n_levels; l++) {\n        for (int r = 0; r < (config->n_res + 1); r++) {\n            int s = l * 3 + r + NUM_BACK_BUFS;\n            assert(s < UNET_NUM_BACK_ACTS);\n            back->back_sizes[s] = B * C * H * W;\n            int mult = config->channel_mult[l];\n            C = C_model * mult;\n            if (l > 0 && r == 0) {\n                H /= 2;\n                W /= 2;\n            }\n        }\n    }\n\n    size_t n_backs = 0;\n    for (int i = 0; i < UNET_NUM_BACK_ACTS; i++) {\n        n_backs += back->back_sizes[i];\n    }\n    back->n_backs = n_backs;\n}\n\n\nvoid unet_make_ptrs_and_count_memory(Unet* unet)\n{\n    unet_setup_and_count_params(&(unet->config), &(unet->params));\n    unet_setup_and_count_params(&(unet->config), &(unet->grads));\n    unet_setup_and_count_acts(&(unet->config), &(unet->acts));\n    unet_setup_and_count_backs(&(unet->config), &(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// malloc unet memory and set pointers\n\ntypedef struct {\n    float* input;\n    float* prev_out;\n    int layer;\n    float* emb;\n    float* emb_cum; // cumulative demb\n    float* mem_ptr;\n    int skip_idx;\n} LayerInfo;\n\n\nvoid set_linear_params_ptrs(\n    UnetBlock* params,\n    LayerInfo* l_info,\n    int C_in, int C_out\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)params->layer_ptrs[layer];\n    linear_set_param_ptrs(linear_params, l_info->mem_ptr, C_in, C_out);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_convk3_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C_in, int OC) {\n    int layer = l_info->layer;\n    ConvK3Params* convk3_params = (ConvK3Params*)params->layer_ptrs[layer];\n    convk3_set_param_ptrs(convk3_params, l_info->mem_ptr, C_in, OC);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    set_resblock_params_ptrs(C, OC, resblock_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_attention_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)params->layer_ptrs[layer];\n    set_attention_params_pointers(att_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_params = (GroupNormParams*)params->layer_ptrs[layer];\n    gn_set_param_ptrs(gn_params, l_info->mem_ptr, C);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\n\nvoid point_unet_params(UnetConfig* config, UnetBlock* params) {\n    // common constants\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = params->mem_ptr;\n    set_linear_params_ptrs(params, &l_info, C_model, C_time);\n    l_info.layer++; // skip silu layer\n    set_linear_params_ptrs(params, &l_info, C_time, C_time);\n    int OC = C_model;\n    set_convk3_params_ptrs(params, &l_info, C_in, OC);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_params_ptrs(params, &l_info, C, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l < n_levels - 1) {\n            l_info.layer++; // skip avgpool layer\n        }\n    }\n\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n    set_attention_layer_params_ptrs(params, &l_info);\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n\n    // upward: skip one layer for concat\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            l_info.layer++;\n            OC = mult * C_model;\n            set_resblock_layer_params_ptrs(params, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l > 0) {\n            l_info.layer++; // upsample\n        }\n    }\n\n    set_groupnorm_layer_params_ptrs(params, &l_info, C);\n    l_info.layer++; // silu\n    set_convk3_params_ptrs(params, &l_info, C, C_out);\n}\n\nvoid set_linear_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    LinearActs* linear_acts = (LinearActs*)acts->layer_ptrs[layer];\n    linear_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    linear_acts->inp = l_info->input;\n    l_info->input = linear_acts->out;\n    l_info->layer++;\n}\n\nvoid set_silu_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    SiluActs* silu_acts = (SiluActs*)acts->layer_ptrs[layer];\n    silu_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    silu_acts->x = l_info->input;\n    l_info->input = silu_acts->out;\n    l_info->layer++;\n}\n\nvoid set_convk3_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    ConvK3Acts* convk3_acts = (ConvK3Acts*)acts->layer_ptrs[layer];\n    convk3_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    convk3_acts->inp = l_info->input;\n    l_info->input = convk3_acts->out;\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    set_resblock_acts_ptrs(C, OC, resblock_acts, l_info->mem_ptr);\n    resblock_acts->input = l_info->input;\n    resblock_acts->emb = l_info->emb;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = resblock_acts->add2;\n    l_info->layer++;\n}\n\nvoid set_avgpool_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)acts->layer_ptrs[layer];\n    avgpool_acts->out = l_info->mem_ptr;\n    avgpool_acts->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = avgpool_acts->out;\n    l_info->layer++;\n}\n\nvoid set_attention_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    set_attention_acts_pointers(att_acts, l_info->mem_ptr);\n    att_acts->input = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = att_acts->add;\n    l_info->layer++;\n}\n\nvoid set_concat_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, float** skip_acts) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)acts->layer_ptrs[layer];\n    cat_acts->out = l_info->mem_ptr;\n    cat_acts->x1 = l_info->input;\n    cat_acts->x2 = skip_acts[l_info->skip_idx];\n\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = cat_acts->out;\n    l_info->layer++;\n    l_info->skip_idx--;\n}\n\nvoid set_upsample_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)acts->layer_ptrs[layer];\n    up_acts->x = l_info->input;\n    up_acts->out = l_info->mem_ptr;\n\n    l_info->mem_ptr += acts-> layer_sizes[layer];\n    l_info->input = up_acts->out;\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_acts_ptrs(\n    UnetBlock* acts, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormActs* gn_a = (GroupNormActs*)acts->layer_ptrs[layer];\n    gn_set_act_ptrs(gn_a, l_info->mem_ptr, B, C, H, W, gn_n_groups);\n    gn_a->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = gn_a->out;\n    l_info->layer++;\n}\n\nvoid point_unet_acts(UnetConfig* config, UnetBlock* acts, float** skip_acts, float* input, float* timestep_emb) {\n    // common constants\n    int B = config->B;\n    int C_model = config->C_model;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = acts->mem_ptr;\n\n    // timestep emb\n    l_info.input = timestep_emb;\n    set_linear_acts_ptrs(acts, &l_info);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_linear_acts_ptrs(acts, &l_info);\n    // point input to model input, and set outputu of the last linear layer as the embedding input\n    l_info.emb = l_info.input;\n    l_info.input = input;\n\n    int OC = C_model;\n    set_convk3_acts_ptrs(acts, &l_info);\n    int skip_idx = 0;\n    skip_acts[skip_idx] = l_info.input; // set the output of the previouse layer as input to the skip connection\n    skip_idx++;\n\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_acts_ptrs(acts, &l_info, C, OC);\n            if (l < att_start_level) {\n                // for the first 2 layers, the skip connection comes from the resblocks\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n        }\n        if (l < n_levels - 1) {\n            set_avgpool_layer_acts_ptrs(acts, &l_info);\n            skip_acts[skip_idx] = l_info.input;\n            skip_idx++;\n        }\n    }\n\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n    set_attention_layer_acts_ptrs(acts, &l_info);\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n\n    // upward: set concat\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            set_concat_layer_acts_ptrs(acts, &l_info, skip_acts);\n            OC = mult * C_model;\n            set_resblock_layer_acts_ptrs(acts, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n            }\n        }\n        if (l > 0) {\n            set_upsample_layer_acts_ptrs(acts, &l_info);\n        }\n    }\n    set_groupnorm_layer_acts_ptrs(acts, &l_info, B, C, H, W, gn_n_groups);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_convk3_acts_ptrs(acts, &l_info);\n}\n\nvoid set_unet_back_pointers(UnetBackBlock* back) {\n    UnetBackActs* back_acts = &(back->back_acts);\n    float* back_mem_ptr = back->back_mem;\n\n    float** ptrs[] = {\n        &(back_acts->dinp_buf1),\n        &(back_acts->dinp_buf2),\n        &(back_acts->buf1_BCHW),\n        &(back_acts->buf2_BCHW),\n        &(back_acts->buf3_BCHW),\n        &(back_acts->buf4_BCHW),\n        &(back_acts->buf1_B3CHW),\n        &(back_acts->buf2_B3CHW),\n        &(back_acts->demb_buf1),\n        &(back_acts->demb_buf2)\n    };\n\n    for (int i = 0; i < NUM_BACK_BUFS; i++) {\n        *(ptrs[i]) = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[i];\n    }\n\n    // point the back acts for skip connections\n    for (int s = 0; s < NUM_SKIP_CONNECTIONS; s++) {\n        back_acts->skip_dinps[s] = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[NUM_BACK_BUFS + s];\n    }\n}\n\nvoid malloc_and_point(Unet* unet) {\n    float *param_mem, *grad_mem, *act_mem, *back_mem;\n    cudaCheck(cudaMalloc(&param_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&grad_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&act_mem, unet->acts.total_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&back_mem, unet->back.n_backs * sizeof(float)));\n\n    cudaCheck(cudaMemset(param_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(grad_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(act_mem, 0, unet->acts.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(back_mem, 0, unet->back.n_backs * sizeof(float)));\n\n    // constants\n    int B = unet->config.B;\n    int C_in = unet->config.C_in;\n    int C_model = unet->config.C_model;\n    int H = unet->config.H;\n    int W = unet->config.W;\n\n    // malloc and point input and timestep emb\n    cudaCheck(cudaMalloc(&(unet->input), B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&(unet->timestep_emb), B * C_model * sizeof(float)));\n    // malloc a nicely aligned buffer for the loss\n    cudaCheck(cudaMalloc(&(unet->gpu_loss), 64 * sizeof(float)));\n\n    unet->params.mem_ptr = param_mem;\n    unet->grads.mem_ptr = grad_mem;\n    unet->acts.mem_ptr = act_mem;\n    unet->back.back_mem = back_mem;\n\n    point_unet_params(&(unet->config), &(unet->params));\n    point_unet_params(&(unet->config), &(unet->grads));\n    point_unet_acts(&(unet->config), &(unet->acts), unet->skip_acts, unet->input, unet->timestep_emb);\n    set_unet_back_pointers(&(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// forward pass\n\nvoid linear_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int N, int C, int OC, int block_size\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_forward2(cublas_handle, linear_acts->out, linear_acts->inp, linear_params->w, linear_params->b, N, C, OC, block_size);\n    l_info->layer++;\n}\n\nvoid silu_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_forward(acts->x, acts->out, N, block_size);\n    l_info->layer++;\n}\n\nvoid convk3_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    conv2d_k3_forward3(conv_acts->inp, conv_params->w, conv_params->b, conv_acts->out, B, C, OC, H, W);\n    l_info->layer++;\n}\nvoid resblock_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    resblock_forward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_acts);\n    l_info->layer++;\n}\n\nvoid avgpool_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)unet->acts.layer_ptrs[layer];\n    avgpool_2d_forward1(avgpool_acts->out, avgpool_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid attention_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts);\n    l_info->layer++;\n}\n\nvoid concat_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)unet->acts.layer_ptrs[layer];\n    concat_channel_forward(cat_acts->x1, cat_acts->x2, cat_acts->out, B, C1, C2, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid upsample_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)unet->acts.layer_ptrs[layer];\n    upsample_forward1(up_acts->out, up_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid groupnorm_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_forward(gn_a->x, gn_p->w, gn_p->b, gn_a->out, gn_a->mean, gn_a->rstd, B, C, H, W, gn_n_groups);\n    l_info->layer++;\n}\n\n\nvoid unet_forward(\n    cublasHandle_t cublas_handle,\n    Unet* unet,\n    float* t_res = nullptr, float* t_att = nullptr\n) {\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    cudaEvent_t start, end;\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end));\n    }\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_model, C_time, block_size);\n    silu_layer_forward(unet, &l_info, B * C_time, block_size);\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_time, C_time, block_size);\n    int OC = C_model;\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config.channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(start));\n            }\n            resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(end));\n                cudaCheck(cudaEventSynchronize(start));\n                cudaCheck(cudaEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                *t_res += elapsed_time;\n                printf(\"down resblock: l %d, r %d, Cin %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, OC, H, W, elapsed_time);\n            }\n            C = OC;\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(start));\n                }\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(end));\n                    cudaCheck(cudaEventSynchronize(start));\n                    cudaCheck(cudaEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                    *t_att += elapsed_time;\n                    printf(\"down att: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                }\n            }\n        }\n        if (l < n_levels - 1) {\n            avgpool_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(start));\n    }\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        *t_res += elapsed_time;\n        printf(\"middle resblock 1: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        cudaCheck(cudaEventRecord(start));\n    }\n    attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        *t_att += elapsed_time;\n        printf(\"middle att: Cin %d, H %d, W %d, time %f\\n\", C, H, W, elapsed_time);\n        cudaCheck(cudaEventRecord(start));\n    }\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        printf(\"middle resblock 2: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        *t_res += elapsed_time;\n    }\n\n    // upward\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int C_skip = C_model * config.channel_mult[l];\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            }\n            concat_layer_forward(unet, &l_info, B, C, C_skip, H, W, block_size);\n            OC = C_model * config.channel_mult[l];\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(start));\n            }\n            resblock_layer_forward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(end));\n                cudaCheck(cudaEventSynchronize(start));\n                cudaCheck(cudaEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                printf(\"up resblock: l %d, r %d, Cin %d, Cskip %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, C_skip, OC, H, W, elapsed_time);\n                *t_res += elapsed_time;\n            }\n            C = OC;\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(start));\n                }\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(end));\n                    cudaCheck(cudaEventSynchronize(start));\n                    cudaCheck(cudaEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                    printf(\"up att: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                    *t_att += elapsed_time;\n                }\n            }\n        }\n        if (l > 0) {\n            upsample_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    groupnorm_layer_forward(unet, &l_info, B, C, H, W, gn_n_groups);\n    silu_layer_forward(unet, &l_info, B * C * H * W, block_size);\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n\n    ConvK3Acts* last_acts = (ConvK3Acts*)unet->acts.layer_ptrs[l_info.layer - 1];\n    unet->output = last_acts->out;\n    if (unet->target == nullptr) {\n        // don't have targets, not computing loss\n        unet->mean_loss = -1.0f;\n    } else {\n        mse_forward(unet->output, unet->target, unet->gpu_loss, B * C_out * H * W, block_size);\n        cudaCheck(cudaMemcpy(&(unet->mean_loss), unet->gpu_loss, sizeof(float), cudaMemcpyDeviceToHost));\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// backward pass\nvoid swap_layer_in_out_ptrs(LayerInfo* l_info) {\n    float* temp = l_info->input;\n    l_info->input = l_info->prev_out;\n    l_info->prev_out = temp;\n}\n\nvoid linear_layer_backward(\n    Unet* unet,\n    LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int N, int C, int OC\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearParams* linear_grads = (LinearParams*)unet->grads.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_backward1(cublas_handle, l_info->prev_out, linear_grads->w, linear_grads->b, l_info->input, linear_acts->inp, linear_params->w, N, C, OC);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid silu_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_backward(l_info->input, acts->x, l_info->prev_out, N, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid convk3_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * OC * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Params* conv_grads = (ConvK3Params*)unet->grads.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n    conv2d_k3_backward2(l_info->input, conv_acts->inp, conv_params->w, unet_back_acts->buf1_BCHW, unet_back_acts->buf2_BCHW, l_info->prev_out, conv_grads->w, conv_grads->b, B, C, OC, H, W);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid resblock_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * C_out * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockParameters* resblock_grads = (ResBlockParameters*)unet->grads.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    ResBlockBackwardActivations back_acts;\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n\n    // point back_acts to buffers\n    back_acts.buf_BCemb = unet_back_acts->buf1_BCHW;\n    back_acts.buf_BCHoWo = unet_back_acts->buf2_BCHW;\n    back_acts.buf1_BCHW = unet_back_acts->buf3_BCHW;\n    back_acts.buf2_BCHW = unet_back_acts->buf4_BCHW;\n    back_acts.dweight_buf = unet_back_acts->buf1_B3CHW;\n    back_acts.dbias_buf = unet_back_acts->buf2_B3CHW;\n    back_acts.dout = l_info->input;\n    back_acts.dx = l_info->prev_out;\n    back_acts.demb = l_info->emb;\n\n    resblock_backward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_grads, resblock_acts, &back_acts);\n    swap_layer_in_out_ptrs(l_info);\n\n    // add demb to cumulative demb\n    add_inplace_forward(l_info->emb, l_info->emb_cum, B * C_emb, block_size);\n    l_info->layer--;\n}\n\nvoid attention_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    cublasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"att adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionParams* att_grads = (AttentionParams*)unet->grads.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    AttentionBackwardActs att_backs;\n    UnetBackActs* unet_backs = &(unet->back.back_acts);\n    att_backs.buf1_BCHW = unet_backs->buf1_BCHW;\n    att_backs.buf2_BCHW = unet_backs->buf2_BCHW;\n    att_backs.buf_B3CHW = unet_backs->buf1_B3CHW;\n    att_backs.dqkvr = unet_backs->buf2_B3CHW;\n    att_backs.dpreatt = unet_backs->buf3_BCHW;\n    att_backs.datt = unet_backs->buf4_BCHW;\n    att_backs.dout = l_info->input;\n    att_backs.dinp = l_info->prev_out;\n\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts, &att_backs, att_grads);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid concat_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n    //printf(\"concat skip %d\\n\", l_info->skip_idx);\n    concat_channel_backward(l_info->input, l_info->prev_out, skip_dinp, B, C1, C2, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n    l_info->skip_idx++;\n}\n\n\nvoid avgpool_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"avgpool adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H/2 * W/2, block_size);\n        l_info->skip_idx--;\n    }\n    avgpool_2d_backward1(l_info->input, l_info->prev_out, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid upsample_layer_backward(\n    Unet* unet, LayerInfo *l_info,\n    int B, int C,  int H, int W, int block_size\n) {\n    upsample_backward1(l_info->prev_out, l_info->input, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid groupnorm_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormParams* gn_g = (GroupNormParams*)unet->grads.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_backward(l_info->input, gn_a->x, gn_a->mean, gn_a->rstd, gn_p->w, l_info->prev_out, gn_g->w, gn_g->b, B, C, H, W, gn_n_groups);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid unet_backward(\n    cublasHandle_t cublas_handle,\n    Unet* unet,\n    float* t_res = nullptr, float* t_att = nullptr\n) {\n    if (unet->mean_loss == -1.0f or unet->target == nullptr) {\n        printf(\"Error: must run forward with targets before calling backward\\n\");\n        exit(EXIT_FAILURE);\n    }\n    cudaEvent_t start, end;\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end));\n    }\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    UnetBackActs* back_acts = &(unet->back.back_acts);\n    mse_backward(unet->output, unet->target, back_acts->dinp_buf1, B * C_out * H * W, block_size);\n\n    LayerInfo l_info;\n    l_info.layer = config.n_layers - 1;\n    l_info.input = back_acts->dinp_buf1;\n    l_info.prev_out = back_acts->dinp_buf2;\n    l_info.emb = back_acts->demb_buf1;\n    l_info.emb_cum = back_acts->demb_buf2;\n    l_info.skip_idx = 0;\n\n    int C;\n    int OC;\n    int C_skip;\n    C = C_model;\n    OC = C_model;\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n    silu_layer_backward(unet, &l_info, B * C * H * W, block_size);\n    groupnorm_layer_backward(unet, &l_info, B, C, H, W, gn_n_groups);\n\n    for (int l = 0; l < n_levels; l++) {\n        for (int r = n_res; r >= 0; r--) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            } else {\n                C_skip = C_model * config.channel_mult[l];\n            }\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(start));\n                }\n                attention_layer_backward(unet, &l_info, cublas_handle, B, OC, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(end));\n                    cudaCheck(cudaEventSynchronize(start));\n                    cudaCheck(cudaEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                    *t_att += elapsed_time;\n                    printf(\"up att back: l %d, r %d, Cout %d, H %d, W %d, time %f\\n\", l, r, OC, H, W, elapsed_time);\n                }\n            }\n            if (r == 0 && l < n_levels - 1) {\n                C = C_model * config.channel_mult[l + 1];\n            }\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(start));\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(end));\n                cudaCheck(cudaEventSynchronize(start));\n                cudaCheck(cudaEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                *t_res += elapsed_time;\n                printf(\"up res back: l %d, r %d, Cin %d, Cskip %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, C_skip, OC, H, W, elapsed_time);\n            }\n            concat_layer_backward(unet, &l_info, B, C, C_skip, H, W, block_size);\n        }\n        if (l < n_levels - 1) {\n            H /= 2;\n            W /= 2;\n            upsample_layer_backward(unet, &l_info, B, C, H, W, block_size);\n            OC = C_model * config.channel_mult[l + 1];\n        }\n    }\n\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(start));\n    }\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        *t_res += elapsed_time;\n        printf(\"middle resblock 2: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        cudaCheck(cudaEventRecord(start));\n    }\n    attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        *t_att += elapsed_time;\n        printf(\"middle att: Cin %d, H %d, W %d, time %f\\n\", C, H, W, elapsed_time);\n        cudaCheck(cudaEventRecord(start));\n    }\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(cudaEventRecord(end));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        printf(\"middle resblock 1: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        *t_res += elapsed_time;\n    }\n\n    // we will need to change the backward for all the other blocks below to include the dout contribution from the skip connection.\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        if (l < n_levels - 1) {\n            H *= 2;\n            W *= 2;\n            avgpool_layer_backward(unet, &l_info, B, C, H, W, block_size, 1);\n        }\n        for (int r = 0; r < n_res; r++) {\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(start));\n                }\n                attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, 1);\n                if (t_res != nullptr) {\n                    cudaCheck(cudaEventRecord(end));\n                    cudaCheck(cudaEventSynchronize(start));\n                    cudaCheck(cudaEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                    printf(\"down att back: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                    *t_att += elapsed_time;\n                }\n            }\n            int res_add_skip = (l < att_start_level) ? 1 : 0;\n            if (r == n_res - 1 && l > 0) {\n                int prev_mult = config.channel_mult[l - 1];\n                C = prev_mult * C_model;\n            }\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(start));\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size, res_add_skip);\n            if (t_res != nullptr) {\n                cudaCheck(cudaEventRecord(end));\n                cudaCheck(cudaEventSynchronize(start));\n                cudaCheck(cudaEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n                printf(\"down res back: l %d, r %d, Cin %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, OC, H, W, elapsed_time);\n                *t_res += elapsed_time;\n            }\n            OC = C;\n        }\n    }\n\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size, 1);\n    unet->dinp = l_info.input;\n\n    // reset dout to point to emb_cum\n    l_info.input = l_info.emb_cum;\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_time, C_time);\n    silu_layer_backward(unet, &l_info, B * C_time, block_size);\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_model, C_time);\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// functions to update gradient\n\nvoid unet_zero_grad(Unet* unet) {\n    cudaCheck(cudaMemset(unet->grads.mem_ptr, 0, unet->grads.total_size * sizeof(float)));\n    cudaCheck(cudaMemset(unet->back.back_mem, 0, unet->back.n_backs * sizeof(float)));\n    //cudaCheck(cudaMemset(unet->acts.mem_ptr, 0, unet->acts.total_size * sizeof(float)));\n}\n\n// below copied from llm.c\n\n// Implements linear interpolation using only two floating-point operations (as opposed to three in a naive implementation).\n// Reference: https://developer.nvidia.com/blog/lerp-faster-cuda\n__device__ inline float lerp(float start, float end, float weight) {\n    return fma(weight, end, fma(-weight, start, start));\n}\n\n__global__ void adamw_kernel2(float* params_memory, float* grads_memory, float* m_memory, float* v_memory, long num_parameters,\n                              float learning_rate, float beta1, float beta2, float beta1_correction, float beta2_correction, float eps, float weight_decay) {\n   int i = blockIdx.x * blockDim.x + threadIdx.x;\n   if (i >= num_parameters) return;  // guard\n   float grad = grads_memory[i];\n   float m = m_memory[i];\n   float v = v_memory[i];\n   // update the first moment (momentum)\n   m = lerp(grad, m, beta1);\n   m_memory[i] = m;\n   // update the second moment (RMSprop)\n   v = lerp(grad * grad, v, beta2);\n   v_memory[i] = v;\n   m /= beta1_correction;  // m_hat\n   v /= beta2_correction;  // v_hat\n   params_memory[i] -= learning_rate * (m / (sqrtf(v) + eps) + weight_decay * params_memory[i]);\n}\n\nvoid unet_update(Unet* unet, float learning_rate, float beta1, float beta2, float eps, float weight_decay, int t) {\n    // lazy allocation\n    if (unet->m_memory == nullptr) {\n        size_t param_size = unet->params.total_size * sizeof(float);\n        cudaCheck(cudaMalloc(&(unet->m_memory), param_size));\n        cudaCheck(cudaMalloc(&(unet->v_memory), param_size));\n        cudaCheck(cudaMemset(unet->m_memory, 0, param_size));\n        cudaCheck(cudaMemset(unet->v_memory, 0, param_size));\n        printf(\"allocated %zu MiB for AdamW state m\\n\", param_size / 1024 / 1024);\n        printf(\"allocated %zu MiB for AdamW state v\\n\", param_size / 1024 / 1024);\n    }\n\n    int block_size = unet->config.block_size;\n    int n_blocks = ceil_div(unet->params.total_size, (size_t)block_size);\n    float beta1_correction = 1.0f - powf(beta1, t);\n    float beta2_correction = 1.0f - powf(beta2, t);\n    adamw_kernel2<<<n_blocks, block_size>>>(unet->params.mem_ptr, unet->grads.mem_ptr, unet->m_memory, unet->v_memory, unet->params.total_size,\n                    learning_rate, beta1, beta2, beta1_correction, beta2_correction, eps, weight_decay);\n    cudaCheck(cudaGetLastError());\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// run tests\n\n#define NUM_DEBUG_STATES 5\ntypedef struct {\n    float* input;\n    float* timestep_emb;\n    float* out;\n    float* dinp;\n    float* target;\n} DebugStates;\n\n\nint main(int argc, char **argv) {\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    char cwd[PATH_MAX];\n    if (getcwd(cwd, sizeof(cwd)) != NULL) {\n        //printf(\"Current working dir: %s\\n\", cwd);\n    } else {\n        perror(\"getcwd() error\");\n        return 1;\n    }\n\n    // read header\n    FILE *model_file = fopenCheck(\"unet_test_params.bin\", \"rb\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file\\n\");\n        exit(1);\n    }\n    int B = model_header[1];\n    int C_in = model_header[2];\n    int C_model = model_header[3];\n    int C_out = model_header[4];\n    int H = model_header[5];\n    int W = model_header[6];\n    int max_period = model_header[7];\n    int C_time = C_model * 4;\n    const int n_levels = 4;\n    int channel_mult[n_levels] = {1, 2, 3, 4};\n\n    // dataloader\n    DataLoader loader;\n    dataloader_init(&loader, DATASET_FILE, B);\n\n\n    Unet unet;\n    UnetConfig* config = &(unet.config);\n    config->B = B;\n    config->C_in = C_in;\n    config->C_model = C_model;\n    config->C_time = C_time;\n    config->C_out = C_out;\n    config->H = H;\n    config->W = W;\n    config->block_size = 512;\n    config->gn_n_groups = 32;\n    config->n_levels = n_levels;\n    config->channel_mult = channel_mult;\n    config->HS = 32;\n    config->n_res = 2;\n    config->max_period = max_period;\n    config->att_start_level = 2;\n\n    unet_init(&unet);\n\n    unet_count_layers(config);\n    unet_make_ptrs_and_count_memory(&unet);\n    malloc_and_point(&unet);\n\n    // allocate host memory for params\n    float* params_mem_cpu = (float*)mallocCheck(unet.params.total_size * sizeof(float));\n    freadCheck(params_mem_cpu, sizeof(float), unet.params.total_size, model_file);\n    fcloseCheck(model_file);\n    UnetBlock params_cpu;\n    unet_setup_and_count_params(config, &params_cpu);\n    params_cpu.mem_ptr = params_mem_cpu;\n    point_unet_params(config, &params_cpu);\n\n    // copy params to device\n    cudaCheck(cudaMemcpy(unet.params.mem_ptr, params_mem_cpu, unet.params.total_size * sizeof(float), cudaMemcpyHostToDevice));\n\n    // allocate host memory for debug states\n    FILE *debug_file = fopenCheck(\"unet_test_acts.bin\", \"rb\");\n    size_t state_sizes[NUM_DEBUG_STATES];\n    int out_size = B * C_out * H * W;\n    state_sizes[0] = B * C_in * H * W; // input\n    state_sizes[1] = B; // timestep emb\n    state_sizes[2] = out_size; // out\n    state_sizes[3] = B * C_in * H * W; // dinp\n    state_sizes[4] = out_size; // target\n\n    size_t num_debug_params = 0;\n    for (int i = 0; i < NUM_DEBUG_STATES; i++) {\n        num_debug_params += state_sizes[i];\n    }\n\n    float* debug_states_cpu = (float*)mallocCheck(num_debug_params * sizeof(float));\n    freadCheck(debug_states_cpu, sizeof(float), num_debug_params, debug_file);\n    // point to the debug states\n    DebugStates debug_states;\n    float** debug_ptrs[] = {\n        &(debug_states.input),\n        &(debug_states.timestep_emb),\n        &(debug_states.out),\n        &(debug_states.dinp),\n        &(debug_states.target),\n    };\n\n    float* debug_mem_ptr = debug_states_cpu;\n    for (int i = 0; i < NUM_DEBUG_STATES; i++) {\n        *(debug_ptrs[i]) = debug_mem_ptr;\n        debug_mem_ptr += state_sizes[i];\n    }\n\n    // read the groud truth grads\n    float* grads_mem_cpu = (float*)mallocCheck(unet.grads.total_size * sizeof(float));\n    freadCheck(grads_mem_cpu, sizeof(float), unet.grads.total_size, debug_file);\n    fcloseCheck(debug_file);\n\n\n    // read the losses\n    float losses[10];\n    FILE *loss_file = fopenCheck(\"unet_test_losses.bin\", \"rb\");\n    freadCheck(losses, sizeof(float), 10, loss_file);\n    fcloseCheck(loss_file);\n\n    // create a grads struct and point to cpu grads for easier validation\n    UnetBlock grads_cpu;\n    unet_setup_and_count_params(config, &grads_cpu);\n    grads_cpu.mem_ptr = grads_mem_cpu;\n    point_unet_params(config, &grads_cpu);\n\n    //// copy input to device\n    //cudaCheck(cudaMemcpy(unet.input, debug_states.input, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    // load input\n    dataloader_next_batch(&loader);\n    cudaCheck(cudaMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n    // try out the random number generator\n    GaussianDiffusion diffusion;\n    diffusion_init(&diffusion, B, C_in, H, W, 1000);\n    printf(\"successful gaussian init\\n\");\n\n    float rand_nums[B * 10];\n    sample_timesteps(&diffusion, rand_nums, B * 10);\n\n    // compute timestep emb\n    float* d_timesteps;\n    cudaCheck(cudaMalloc(&d_timesteps, B * sizeof(float)));\n    cudaMemcpy(d_timesteps, rand_nums, B * sizeof(float), cudaMemcpyHostToDevice);\n    get_timestep_embeddings(&unet.time_emb_state, d_timesteps, unet.timestep_emb);\n\n    // generate all the gaussian noise\n    float *all_noise = (float*)malloc(10 * B * C_in * H * W * sizeof(float));\n    normal_(all_noise, 10 * B * C_in * H * W, 0.0f, 1.0f, &diffusion.host_rng);\n\n\n    cudaCheck(cudaMalloc(&(unet.target), out_size * sizeof(float)));\n\n    //printf(\"drawing and saving x_t for verification\\n\");\n    //float* x_t_cpu = (float*)mallocCheck(B * C_in * H * W * sizeof(float));\n    //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n    //print_gpu_first_5(\"x_t\", unet.target);\n    //cudaCheck(cudaMemcpy(x_t_cpu, unet.target, B * C_in * H * W * sizeof(float), cudaMemcpyDeviceToHost));\n    //FILE *sample_debug_file = fopenCheck(\"./sample_debug.bin\", \"wb\");\n    //size_t result = fwrite(rand_nums, sizeof(float), B, sample_debug_file);\n    //if (result != B) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //result = fwrite(loader.input, sizeof(float), B * C_in * H * W, sample_debug_file);\n    //if (result != B * C_in * H * W) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //result = fwrite(x_t_cpu, sizeof(float), B * C_in * H * W, sample_debug_file);\n    //if (result != B * C_in * H * W) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //fcloseCheck(sample_debug_file);\n\n\n    // copy the targets to gpu\n    //cudaCheck(cudaMemcpy(unet.target, debug_states.target, out_size * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(unet.target, all_noise, out_size * sizeof(float), cudaMemcpyHostToDevice));\n    diffusion_sample(d_timesteps, unet.input, unet.target, &diffusion, B, C_in * H * W);\n    //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n\n\n\n    printf(\"\\nCheck that unet targets match the debug states\\n\");\n    validate_result(unet.target, debug_states.target, \"target\", out_size);\n\n\n    float acc = 1e-4;\n    float med_acc = 1e-2;\n    printf(\"Running 10 update loops\\n\");\n    // loop 1\n    printf(\"Checking forward pass\\n\");\n    float t_res = 0.0f;\n    float t_att = 0.0f;\n    unet_forward(cublas_handle, &unet, &t_res, &t_att);\n    printf(\"resblock time: %f ms, attention time: %f ms\\n\", t_res, t_att);\n    printf(\"\\nChecking out\\n\");\n    validate_result(unet.output, debug_states.out, \"out\", out_size, med_acc);\n    printf(\"cpu loss: %f, gpu loss: %f\\n\", losses[0], unet.mean_loss);\n    printf(\"\\nForward pass successful\\n\");\n\n    printf(\"\\nChecking backward pass\\n\");\n    t_res = 0.0f;\n    t_att = 0.0f;\n    unet_backward(cublas_handle, &unet, &t_res, &t_att);\n    printf(\"resblock back time: %f ms, attention back time: %f ms\\n\", t_res, t_att);\n    printf(\"\\nChecking grads\\n\");\n    validate_result(unet.grads.mem_ptr, grads_mem_cpu, \"grads\", unet.grads.total_size, med_acc);\n    printf(\"\\nChecking dinp\\n\");\n    validate_result(unet.dinp, debug_states.dinp, \"dinp\", B * C_in * H * W, acc);\n    unet_update(&unet, 1e-3f, 0.9f, 0.999f, 1e-8f, 0.01f, 1);\n    unet_zero_grad(&unet);\n\n    // loop 2-9\n    cudaEvent_t start, end;\n    cudaCheck(cudaEventCreate(&start));\n    cudaCheck(cudaEventCreate(&end));\n    cudaCheck(cudaProfilerStart());\n    cudaCheck(cudaEventRecord(start));\n    for (int i = 1; i < 10; i++) {\n        dataloader_next_batch(&loader);\n        cudaCheck(cudaMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n        cudaMemcpy(d_timesteps, rand_nums + i * B, B * sizeof(float), cudaMemcpyHostToDevice);\n        get_timestep_embeddings(&unet.time_emb_state, d_timesteps, unet.timestep_emb);\n\n        cudaCheck(cudaMemcpy(unet.target, all_noise + i * out_size, out_size * sizeof(float), cudaMemcpyHostToDevice));\n        diffusion_sample(d_timesteps, unet.input, unet.target, &diffusion, B, C_in * H * W);\n        //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n\n        unet_forward(cublas_handle, &unet);\n        unet_backward(cublas_handle, &unet);\n        printf(\"step %d, cpu loss: %f, gpu loss: %f\\n\", i+1, losses[i], unet.mean_loss);\n        unet_update(&unet, 1e-3f, 0.9f, 0.999f, 1e-8f, 0.01f, i+1);\n        unet_zero_grad(&unet);\n    }\n    cudaCheck(cudaEventRecord(end));\n    cudaCheck(cudaEventSynchronize(start));\n    cudaCheck(cudaEventSynchronize(end));\n    float time_elapsed;\n    cudaCheck(cudaEventElapsedTime(&time_elapsed, start, end));\n    time_elapsed /= 9;\n    printf(\"Average time per step: %f ms\\n\", time_elapsed);\n\n\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmark\\n\");\n    int repeat_times = 50;\n    float elapsed_time = benchmark_kernel(repeat_times, unet_forward, cublas_handle, &unet, nullptr, nullptr);\n    printf(\"Forward pass time: %f ms\\n\", elapsed_time);\n\n    printf(\"\\nBackward pass benchmark\\n\");\n    elapsed_time = benchmark_kernel(repeat_times, unet_backward, cublas_handle, &unet, nullptr, nullptr);\n    printf(\"Backward pass time: %f ms\\n\", elapsed_time);\n\n    // free memory\n    free(params_mem_cpu);\n    free(debug_states_cpu);\n    free_unet(&unet);\n    cudaCheck(cudaFree(unet.target));\n    cudaCheck(cudaFree(d_timesteps));\n    dataloader_free(&loader);\n    diffusion_free(&diffusion);\n    free(all_noise);\n\n    // free cublas\n    cublasCheck(cublasDestroy(cublas_handle));\n}", "output": "#include <hip/hip_runtime_api.h>\n#include <hiprand/hiprand_kernel.h>\n#include <unistd.h> // For POSIX systems to use getcwd\n#include <limits.h> // For PATH_MAX\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <hipblas.h>\n#include \"common.h\"\n#include \"linear.cuh\"\n#include \"silu.cuh\"\n#include \"conv2d_k3.cuh\"\n#include \"resblock.cuh\"\n#include \"add.cuh\"\n#include \"avgpool.cuh\"\n#include \"attention_block.cuh\"\n#include \"concat_channel.cuh\"\n#include \"add.cuh\"\n#include \"upsample.cuh\"\n#include \"groupnorm.cuh\"\n#include \"mse.cuh\"\n#include \"timestep_embedding.cuh\"\n#include \"rand.h\"\n#include <chrono>\n\n\n\ntypedef struct {\n    int C_in;\n    int C_model;\n    int C_time;\n    int C_out;\n    int B;\n    int H;\n    int W;\n    int gn_n_groups;\n    int HS;\n    int block_size;\n    int n_layers;\n    int n_levels;\n    int n_res;\n    int att_start_level;\n    int* channel_mult;\n    int max_period;\n} UnetConfig;\n\n// ------------------------------------------------------------------------------------------------\n// DataLoader\n\nconst char* DATASET_FILE = \"../datasets/elephant_train.bin\";\ntypedef struct {\n    int B;\n    int C_in;\n    int H;\n    int W;\n    int img_size;\n    int n_imgs;\n    FILE* img_file;\n    long cur_position;\n    long file_size;\n    long header_offset;\n    long num_batches;\n    float* input;\n} DataLoader;\n\nvoid dataloader_init(DataLoader* loader, const char* filename, int B) {\n    loader->B = B;\n\n    loader->img_file = fopenCheck(filename, \"rb\");\n    int data_header[256];\n    loader->header_offset = 256 * sizeof(int);\n    freadCheck(data_header, sizeof(int), 256, loader->img_file);\n    if (data_header[0] != 20240620) {\n        fprintf(stderr, \"Invalid magic data file\\n\");\n        exit(1);\n    }\n    loader->n_imgs = data_header[1];\n    loader->C_in = data_header[2];\n    loader->H = data_header[3];\n    loader->W = data_header[4];\n    loader->img_size = loader->C_in * loader->H * loader->W;\n\n    // determine file size\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    fseekCheck(loader->img_file, 0, SEEK_END);\n    loader->file_size = ftell(loader->img_file);\n    fseekCheck(loader->img_file, 0, SEEK_SET);\n    assert((loader->file_size - loader->header_offset) / (loader->img_size * sizeof(float)) == loader->n_imgs);\n    \n    loader->cur_position = loader->header_offset;\n    loader->num_batches = loader->n_imgs / B;\n\n    // allocate cuda CPU pinned memory for inputs\n    hipHostMalloc((void**)&loader->input, B * loader->img_size * sizeof(float));\n}\n\nvoid dataloader_reset(DataLoader* loader) {\n    loader->cur_position = loader->header_offset;\n}\n\nvoid dataloader_next_batch(DataLoader* loader) {\n    int B = loader->B;\n    int img_size = loader->img_size;\n    if (loader->cur_position + (B * img_size * sizeof(float)) > loader->file_size) {\n        dataloader_reset(loader);\n    }\n    fseekCheck(loader->img_file, loader->cur_position, SEEK_SET);\n    freadCheck(loader->input, sizeof(float), B * img_size, loader->img_file);\n    loader->cur_position += B * img_size * sizeof(float);\n}\n\nvoid dataloader_free(DataLoader* loader) {\n    fcloseCheck(loader->img_file);\n    hipHostFree(loader->input);\n}\n\n// ------------------------------------------------------------------------------------------------\n// Diffusion\ntypedef struct {\n    int B;\n    int img_size;\n    int max_period;\n    float* betas; // (max_period,)\n    float* sqrt_alphas_cumprod; // (max_period,)\n    float* sqrt_one_minus_alphas_cumprod; // (max_period,)\n    int block_size;\n    hiprandState* curand_states;\n    mt19937_state host_rng;\n} GaussianDiffusion;\n\n__global__ void init_curand_states(hiprandState *state, unsigned long seed, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N) {\n        hiprand_init(seed, idx, 0, &state[idx]);\n    }\n}\n\nvoid diffusion_init(\n    GaussianDiffusion* diffusion, int B, int C_in, int H, int W, int max_period, int block_size = 512,\n    unsigned long seed = 0, float beta_start = 1e-4, float beta_end = 0.02f\n) {\n    diffusion->B = B;\n    diffusion->max_period = max_period;\n    diffusion->block_size = block_size;\n    diffusion->img_size = C_in * H * W;\n\n    float scale = 1000.0f / max_period;\n    beta_start *= scale;\n    beta_end *= scale;\n\n    // calculate betas and alphas on host then move to device\n    float* betas = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n    float* sqrt_one_minus_alphas_cumprod = (float*)mallocCheck(max_period * sizeof(float));\n\n    float alpha_cumprod = 1.0f;\n    for (int t = 1; t <= max_period; t++) {\n        // linear interpolate for beta\n        betas[t - 1] = (beta_start * (max_period - t) + beta_end * (t - 1)) / (max_period - 1);\n        alpha_cumprod *= 1.0f - betas[t - 1];\n        sqrt_alphas_cumprod[t - 1] = sqrtf(alpha_cumprod);\n        sqrt_one_minus_alphas_cumprod[t - 1] = sqrtf(1.0f - alpha_cumprod);\n    }\n\n    float *d_betas, *d_sqrt_alphas_cumprod, *d_sqrt_one_minus_alphas_cumprod;\n    cudaCheck(hipMalloc(&d_betas, max_period * sizeof(float)));\n    cudaCheck(hipMalloc(&d_sqrt_alphas_cumprod, max_period * sizeof(float)));\n    cudaCheck(hipMalloc(&d_sqrt_one_minus_alphas_cumprod, max_period * sizeof(float)));\n\n    cudaCheck(hipMemcpy(d_betas, betas, max_period * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_sqrt_alphas_cumprod, sqrt_alphas_cumprod, max_period * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_sqrt_one_minus_alphas_cumprod, sqrt_one_minus_alphas_cumprod, max_period * sizeof(float), hipMemcpyHostToDevice));\n\n    free(betas);\n    free(sqrt_alphas_cumprod);\n    free(sqrt_one_minus_alphas_cumprod);\n\n    diffusion->betas = d_betas;\n    diffusion->sqrt_alphas_cumprod = d_sqrt_alphas_cumprod;\n    diffusion->sqrt_one_minus_alphas_cumprod = d_sqrt_one_minus_alphas_cumprod;\n\n    // init curand states for sampling noise of the size of one batch of inputs\n    int total_states = B * C_in * H * W;\n    int n_blocks = ceil_div(total_states, block_size);\n    cudaCheck(hipMalloc(&diffusion->curand_states, total_states * sizeof(hiprandState)));\n    init_curand_states<<<n_blocks, block_size>>>(diffusion->curand_states, seed, total_states);\n\n    // init host rng for drawing random timesteps\n    manual_seed(&diffusion->host_rng, seed);\n}\n\n// gridDim will be of shape (N / block_size, B)\n// expect x and x_t to be of shape (B, N)\n// and timesteps to be of shape (B,)\n// expect x_t to be filled with standard normals\n__global__ void diffusion_sample_kernel(\n    float* timesteps, float* x, float* x_t,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    int B, int N\n) {\n    int b = blockIdx.y;\n    int n = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (n < N && b < B) {\n        int t = (int)timesteps[b];\n        float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n        float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n        float x_t_val = x_t[b * N + n]; // x_t starts out as standard normals\n        float x_val = x[b * N + n];\n        x_t[b * N + n] = x_val * sqrt_alpha_t + x_t_val * sqrt_one_minus_alpha_t;\n    }\n}\n\nvoid diffusion_sample(\n    float* timesteps, float* x, float* x_t,\n    GaussianDiffusion *diffusion,\n    int B, int N\n) {\n    dim3 gridDim(ceil_div(N, diffusion->block_size), B);\n    diffusion_sample_kernel<<<gridDim, diffusion->block_size>>>(timesteps, x, x_t, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, B, N);\n    cudaCheck(hipDeviceSynchronize());\n}\n\n// a kernel that creates x_t = sqrt_alpha * x + sqrt(1 - alpha) * noise in one go\n// that draws the noise from standard normals, gets alpha from the timesteps and the precomputed alpha cumprod\n// and saves output to x_t\n// if the input x, timesteps and the alpha buffers are null, then it will just fill x_t with standard normals\n// to be called with B * N total threads\n__global__ void diffusion_draw_and_sample_xt_kernel(\n    float* x_t, int B, int N, hiprandState* curand_states,\n    float* sqrt_alphas_cumprod, float* sqrt_one_minus_alphas_cumprod,\n    float* timesteps = nullptr, float* x = nullptr\n) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < B * N) {\n        float out_val = hiprand_normal(&curand_states[idx]);\n        if (timesteps != nullptr && x != nullptr) {\n            int b = idx / N;\n            int n = idx % N;\n            float x_val = x[b * N + n];\n            int t = (int)timesteps[b];\n            float sqrt_alpha_t = sqrt_alphas_cumprod[t];\n            float sqrt_one_minus_alpha_t = sqrt_one_minus_alphas_cumprod[t];\n            out_val = x_val * sqrt_alpha_t + out_val * sqrt_one_minus_alpha_t;\n        }\n        x_t[idx] = out_val;\n    }\n}\n\nvoid diffusion_draw_and_sample_xt(\n    GaussianDiffusion *diffusion,\n    float* x_t,\n    float* timesteps = nullptr, float* x = nullptr\n) {\n    int total_states = diffusion->B * diffusion->img_size;\n    int n_blocks = ceil_div(total_states, diffusion->block_size);\n    diffusion_draw_and_sample_xt_kernel<<<n_blocks, diffusion->block_size>>>(\n        x_t, diffusion->B, diffusion->img_size, diffusion->curand_states, diffusion->sqrt_alphas_cumprod, diffusion->sqrt_one_minus_alphas_cumprod, timesteps, x\n    );\n}\n\nvoid diffusion_free(GaussianDiffusion* diffusion) {\n    cudaCheck(hipFree(diffusion->betas));\n    cudaCheck(hipFree(diffusion->sqrt_alphas_cumprod));\n    cudaCheck(hipFree(diffusion->sqrt_one_minus_alphas_cumprod));\n    cudaCheck(hipFree(diffusion->curand_states));\n}\n\n// samples B timesteps in range [0, max_period), convert them to floats and\n// store in timesteps\nvoid sample_timesteps(GaussianDiffusion *diffusion, float* timesteps, int N = -1) {\n    mt19937_state* rng = &diffusion->host_rng;\n    if (N < 0) {\n        N = diffusion->B;\n    }\n    int max_period = diffusion->max_period;\n    for (int i = 0; i < N; i++) {\n        float rand_float = randfloat32(rng);\n        timesteps[i] = (float)(int)(rand_float * max_period);\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// Unet init\n\n// we just need a single set of buffers that are shared for all the backward pass activations\n#define NUM_SKIP_CONNECTIONS 12 // (n_res + 1) * n_levels\n#define NUM_BACK_BUFS 10\n#define UNET_NUM_BACK_ACTS 22\ntypedef struct {\n    float* dinp_buf1; // used to store the input gradient, which will be reused as dout for the previous layer\n    float* dinp_buf2;\n    float* buf1_BCHW; // NOTE: first 4 buffers will have  max B * C * H * W size\n    float* buf2_BCHW;\n    float* buf3_BCHW;\n    float* buf4_BCHW;\n    float* buf1_B3CHW; // NOTE: last 2 buffers will have max B * 3 * C * H * W size for attention layers\n    float* buf2_B3CHW; // so max of H * W is only over dimensions that have attention\n    float* demb_buf1; // used to store cumulative gradients w.r.t. timestep embeddings\n    float* demb_buf2;\n    float* skip_dinps[NUM_SKIP_CONNECTIONS];\n} UnetBackActs;\n\n// This is used as a block to store all the params / grads / acts for all the layers\ntypedef struct {\n    float* mem_ptr;\n    size_t total_size;\n    int n_layers;\n    void** layer_ptrs;\n    size_t* layer_sizes;\n} UnetBlock;\n\ntypedef struct {\n    float* back_mem;\n    UnetBackActs back_acts;\n    size_t back_sizes[UNET_NUM_BACK_ACTS];\n    size_t n_backs;\n} UnetBackBlock;\n\ntypedef struct {\n    UnetConfig config;\n    float* input;\n    float* timestep_emb; // this is the already computed embeddings of shape (B, C_model)\n    UnetBlock params;\n    UnetBlock grads;\n    UnetBlock acts;\n    UnetBackBlock back;\n    float* skip_acts[NUM_SKIP_CONNECTIONS]; // pointers to skip connetion activations\n    // the next two are for debug purposes only, just to keep track of the buffer that contains the grad w.r.t. inputs\n    float* dinp;\n    // model output\n    float* output;\n    // targets and losses\n    float* target;\n    float mean_loss;\n    float* gpu_loss;\n    // buffers for AdamW\n    float* m_memory;\n    float* v_memory;\n    TimestepEmbedding time_emb_state;\n} Unet;\n\nvoid unet_init(Unet* unet) {\n    unet->input = nullptr;\n    unet->timestep_emb = nullptr;\n    unet->params.mem_ptr = nullptr;\n    unet->grads.mem_ptr = nullptr;\n    unet->acts.mem_ptr = nullptr;\n    unet->back.back_mem = nullptr;\n    unet->dinp = nullptr;\n    unet->output = nullptr;\n    unet->target = nullptr;\n    unet->mean_loss = -1.0f;\n    unet->gpu_loss = nullptr;\n    unet->m_memory = nullptr;\n    unet->v_memory = nullptr;\n\n    init_timestep_embedding(&unet->time_emb_state, unet->config.C_model, unet->config.B, unet->config.max_period);\n}\n\n\ntemplate <typename T>\nvoid free_not_null(T** ptr) {\n    if (*ptr != nullptr) {\n        free(*ptr);\n        *ptr = nullptr;\n    }\n}\n\nvoid free_unet_block(UnetBlock* params) {\n    free_not_null(&params->layer_sizes);\n    for (int l = 0; l < params->n_layers; l++) {\n        free_not_null(&(params->layer_ptrs[l]));\n    }\n    free_not_null(&params->layer_ptrs);\n    cudaCheck(hipFree(params->mem_ptr));\n}\n\nvoid free_unet(Unet* unet) {\n    cudaCheck(hipFree(unet->input));\n    cudaCheck(hipFree(unet->timestep_emb));\n    cudaCheck(hipFree(unet->gpu_loss));\n    cudaCheck(hipFree(unet->m_memory));\n    cudaCheck(hipFree(unet->v_memory));\n\n    free_unet_block(&(unet->params));\n    free_unet_block(&(unet->grads));\n    free_unet_block(&(unet->acts));\n\n    cudaCheck(hipFree(unet->back.back_mem));\n    free_timestep_embedding(&unet->time_emb_state);\n}\n\nvoid unet_count_layers(UnetConfig* config) {\n    int n_layers = 0;\n    int n_res = config->n_res;\n    int n_levels = config->n_levels;\n    int att_start_level = config->att_start_level;\n\n    // timestep emb\n    n_layers += 3;\n\n    // initial conv\n    n_layers += 1;\n\n    // add downsample resblock layers\n    n_layers += n_res * n_levels;\n\n    // add downsample attention\n    n_layers += n_res * (n_levels - att_start_level);\n\n    // add avgpool layers\n    n_layers += n_levels - 1;\n\n    // add middle layers\n    n_layers += 3;\n\n    // add upsample concat layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample resblock layers\n    n_layers += (n_res + 1) * n_levels;\n\n    // add upsample attention layers\n    n_layers += (n_res + 1) * (n_levels - att_start_level);\n\n    // add upsample layers\n    n_layers += n_levels - 1;\n\n    // output layers\n    n_layers += 3;\n\n    config->n_layers = n_layers;\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// allocate memory for structs with info for each layer and count params\n\n\nint make_linear_params_ptrs(\n    UnetBlock* params,\n    int layer,\n    int C_in, int C_out\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearParams));\n    params->layer_sizes[layer] = linear_count_params(C_in, C_out);\n    return layer + 1;\n}\n\nint make_silu_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_convk3_params_ptrs(\n    UnetBlock* params, int layer,\n    int C_in, int OC, int B, int H, int W\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Params));\n    params->layer_sizes[layer] = convk3_count_params(C_in, OC);\n    return layer + 1;\n}\n\nint make_resblock_params_ptrs(\n    UnetBlock* params, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockParameters));\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    resblock_count_params(resblock_params, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    params->layer_sizes[layer] = resblock_params->n_params;\n    return layer + 1;\n}\n\nint make_avgpool_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_attention_block_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionParams));\n    AttentionParams* attention_params = (AttentionParams*)params->layer_ptrs[layer];\n    attention_block_count_params(attention_params, C);\n    params->layer_sizes[layer] = attention_params->n_params;\n    return layer + 1;\n}\n\nint make_concat_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_upsample_params_ptrs(UnetBlock* params, int layer) {\n    params->layer_ptrs[layer] = nullptr;\n    params->layer_sizes[layer] = 0;\n    return layer + 1;\n}\n\nint make_groupnorm_params_ptrs(UnetBlock* params, int layer, int C) {\n    params->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormParams));\n    params->layer_sizes[layer] = C * 2;\n    return layer + 1;\n}\n\n\nvoid unet_setup_and_count_params(UnetConfig* config, UnetBlock* params) {\n    assert (config->n_layers > 0);\n\n    params->n_layers = config->n_layers;\n    params->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    params->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_params_ptrs(params, layer, C_model, C_time);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_linear_params_ptrs(params, layer, C_time, C_time);\n    int OC = C_model;\n    layer = make_convk3_params_ptrs(params, layer, C_in, OC, B, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_params_ptrs(params, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_params_ptrs(params, layer);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    // middle layers\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_params_ptrs(params, layer, C);\n    layer = make_resblock_params_ptrs(params, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards: concat then resblock\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = C_model * mult;\n        for (int r = 0; r < n_res + 1; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = C_model * prev_mult;\n            }\n            OC = C_model * mult;\n            layer = make_concat_params_ptrs(params, layer);\n            layer = make_resblock_params_ptrs(params, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_params_ptrs(params, layer, C);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_params_ptrs(params, layer);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    layer = make_groupnorm_params_ptrs(params, layer, C);\n    layer = make_silu_params_ptrs(params, layer);\n    layer = make_convk3_params_ptrs(params, layer, C, C_out, B, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += params->layer_sizes[i];\n    }\n    params->total_size = total_size;\n}\n\nint make_linear_acts_ptrs(\n    UnetBlock* acts,\n    int layer,\n    int B, int C_out\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(LinearActs));\n    // acts buffers only hold outputs\n    acts->layer_sizes[layer] = B * C_out;\n    return layer + 1;\n}\n\nint make_silu_acts_ptrs(UnetBlock* acts, int layer, int N) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(SiluActs));\n    acts->layer_sizes[layer] = N;\n    return layer + 1;\n}\n\nint make_convk3_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int OC, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConvK3Acts));\n    acts->layer_sizes[layer] = B * OC * H * W;\n    return layer + 1;\n}\n\nint make_resblock_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int C, int C_emb, int C_out, int B, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ResBlockActivations));\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    resblock_count_acts(resblock_acts, C, C_emb, C_out, B, H, W, 0, 0, gn_n_groups);\n    acts->layer_sizes[layer] = resblock_acts->n_acts;\n    return layer + 1;\n}\n\nint make_avgpool_acts_ptrs(UnetBlock* acts, int layer, int B, int C, int H, int W) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AvgpoolActs));\n    acts->layer_sizes[layer] = B * C * H/2 * W/2;\n    return layer + 1;\n}\n\nint make_attention_block_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(AttentionActs));\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    attention_block_count_acts(att_acts, B, C, H, W, gn_n_groups, HS);\n    acts->layer_sizes[layer] = att_acts->n_acts;\n    return layer + 1;\n}\n\nint make_concat_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C1, int C2, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(ConcatChannelActs));\n    acts->layer_sizes[layer] = B * (C1 + C2) * H * W;\n    return layer + 1;\n}\n\nint make_upsample_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(UpsampleActs));\n    acts->layer_sizes[layer] = B * C * H * 2 * W * 2;\n    return layer + 1;\n}\n\nint make_groupnorm_acts_ptrs(\n    UnetBlock* acts, int layer,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    acts->layer_ptrs[layer] = (void*)mallocCheck(sizeof(GroupNormActs));\n    // out (B, C, H, W), mean and rstd (B, gn_n_groups)\n    acts->layer_sizes[layer] = B * C * H * W + B * gn_n_groups * 2;\n    return layer + 1;\n}\n\nvoid unet_setup_and_count_acts(UnetConfig* config, UnetBlock* acts) {\n    assert (config->n_layers > 0);\n\n    acts->n_layers = config->n_layers;\n    acts->layer_ptrs = (void**)mallocCheck(config->n_layers * sizeof(void*));\n    acts->layer_sizes = (size_t*)mallocCheck(config->n_layers * sizeof(size_t));\n\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int HS = config->HS;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    int layer = 0;\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    layer = make_silu_acts_ptrs(acts, layer, B * C_time);\n    layer = make_linear_acts_ptrs(acts, layer, B, C_time);\n    int OC = C_model;\n    layer = make_convk3_acts_ptrs(acts, layer, B, OC, H, W);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            layer = make_resblock_acts_ptrs(acts, layer, C, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l < n_levels - 1) {\n            layer = make_avgpool_acts_ptrs(acts, layer, B, C, H, W);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n    layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n    layer = make_resblock_acts_ptrs(acts, layer, C, C_time, C, B, H, W, gn_n_groups);\n\n    // upwards\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                int prev_mult = config->channel_mult[l - 1];\n                C_skip = prev_mult * C_model;\n            }\n            layer = make_concat_acts_ptrs(acts, layer, B, C, C_skip, H, W);\n            OC = mult * C_model;\n            layer = make_resblock_acts_ptrs(acts, layer, C + C_skip, C_time, OC, B, H, W, gn_n_groups);\n            C = OC;\n            if (l >= att_start_level) {\n                layer = make_attention_block_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups, HS);\n            }\n        }\n        if (l > 0) {\n            layer = make_upsample_acts_ptrs(acts, layer, B, C, H, W);\n            H *= 2;\n            W *= 2;\n        }\n    }\n    layer = make_groupnorm_acts_ptrs(acts, layer, B, C, H, W, gn_n_groups);\n    layer = make_silu_acts_ptrs(acts, layer, B * C * H * W);\n    layer = make_convk3_acts_ptrs(acts, layer, B, C_out, H, W);\n\n    // count total size\n    size_t total_size = 0;\n    for (int i = 0; i < config->n_layers; i++) {\n        total_size += acts->layer_sizes[i];\n    }\n    acts->total_size = total_size;\n}\n\nvoid unet_setup_and_count_backs(UnetConfig* config, UnetBackBlock* back) {\n    int B = config->B;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int H = config->H;\n    int W = config->W;\n\n    size_t max_BCHW = 3 * B * C_model * H * W;\n    size_t max_B3CHW = 2 * B * 3 * C_model * H * W;\n    back->back_sizes[0] = max_BCHW;\n    back->back_sizes[1] = max_BCHW;\n    back->back_sizes[2] = max_BCHW;\n    back->back_sizes[3] = max_BCHW;\n    back->back_sizes[4] = max_BCHW;\n    back->back_sizes[5] = max_BCHW;\n    back->back_sizes[6] = max_B3CHW;\n    back->back_sizes[7] = max_B3CHW;\n    back->back_sizes[8] = B * C_time;\n    back->back_sizes[9] = B * C_time;\n\n    // fill the sizes of the skip connection backward acts\n    int C = C_model;\n    for (int l = 0; l < config->n_levels; l++) {\n        for (int r = 0; r < (config->n_res + 1); r++) {\n            int s = l * 3 + r + NUM_BACK_BUFS;\n            assert(s < UNET_NUM_BACK_ACTS);\n            back->back_sizes[s] = B * C * H * W;\n            int mult = config->channel_mult[l];\n            C = C_model * mult;\n            if (l > 0 && r == 0) {\n                H /= 2;\n                W /= 2;\n            }\n        }\n    }\n\n    size_t n_backs = 0;\n    for (int i = 0; i < UNET_NUM_BACK_ACTS; i++) {\n        n_backs += back->back_sizes[i];\n    }\n    back->n_backs = n_backs;\n}\n\n\nvoid unet_make_ptrs_and_count_memory(Unet* unet)\n{\n    unet_setup_and_count_params(&(unet->config), &(unet->params));\n    unet_setup_and_count_params(&(unet->config), &(unet->grads));\n    unet_setup_and_count_acts(&(unet->config), &(unet->acts));\n    unet_setup_and_count_backs(&(unet->config), &(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// malloc unet memory and set pointers\n\ntypedef struct {\n    float* input;\n    float* prev_out;\n    int layer;\n    float* emb;\n    float* emb_cum; // cumulative demb\n    float* mem_ptr;\n    int skip_idx;\n} LayerInfo;\n\n\nvoid set_linear_params_ptrs(\n    UnetBlock* params,\n    LayerInfo* l_info,\n    int C_in, int C_out\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)params->layer_ptrs[layer];\n    linear_set_param_ptrs(linear_params, l_info->mem_ptr, C_in, C_out);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_convk3_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C_in, int OC) {\n    int layer = l_info->layer;\n    ConvK3Params* convk3_params = (ConvK3Params*)params->layer_ptrs[layer];\n    convk3_set_param_ptrs(convk3_params, l_info->mem_ptr, C_in, OC);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)params->layer_ptrs[layer];\n    set_resblock_params_ptrs(C, OC, resblock_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_attention_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)params->layer_ptrs[layer];\n    set_attention_params_pointers(att_params, l_info->mem_ptr);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_params_ptrs(UnetBlock* params, LayerInfo* l_info, int C) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_params = (GroupNormParams*)params->layer_ptrs[layer];\n    gn_set_param_ptrs(gn_params, l_info->mem_ptr, C);\n    l_info->mem_ptr += params->layer_sizes[layer];\n    l_info->layer++;\n}\n\n\nvoid point_unet_params(UnetConfig* config, UnetBlock* params) {\n    // common constants\n    int C_in = config->C_in;\n    int C_model = config->C_model;\n    int C_time = config->C_time;\n    int C_out = config->C_out;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = params->mem_ptr;\n    set_linear_params_ptrs(params, &l_info, C_model, C_time);\n    l_info.layer++; // skip silu layer\n    set_linear_params_ptrs(params, &l_info, C_time, C_time);\n    int OC = C_model;\n    set_convk3_params_ptrs(params, &l_info, C_in, OC);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_params_ptrs(params, &l_info, C, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l < n_levels - 1) {\n            l_info.layer++; // skip avgpool layer\n        }\n    }\n\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n    set_attention_layer_params_ptrs(params, &l_info);\n    set_resblock_layer_params_ptrs(params, &l_info, C, C);\n\n    // upward: skip one layer for concat\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            l_info.layer++;\n            OC = mult * C_model;\n            set_resblock_layer_params_ptrs(params, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_params_ptrs(params, &l_info);\n            }\n        }\n        if (l > 0) {\n            l_info.layer++; // upsample\n        }\n    }\n\n    set_groupnorm_layer_params_ptrs(params, &l_info, C);\n    l_info.layer++; // silu\n    set_convk3_params_ptrs(params, &l_info, C, C_out);\n}\n\nvoid set_linear_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    LinearActs* linear_acts = (LinearActs*)acts->layer_ptrs[layer];\n    linear_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    linear_acts->inp = l_info->input;\n    l_info->input = linear_acts->out;\n    l_info->layer++;\n}\n\nvoid set_silu_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    SiluActs* silu_acts = (SiluActs*)acts->layer_ptrs[layer];\n    silu_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    silu_acts->x = l_info->input;\n    l_info->input = silu_acts->out;\n    l_info->layer++;\n}\n\nvoid set_convk3_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    ConvK3Acts* convk3_acts = (ConvK3Acts*)acts->layer_ptrs[layer];\n    convk3_acts->out = l_info->mem_ptr;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    convk3_acts->inp = l_info->input;\n    l_info->input = convk3_acts->out;\n    l_info->layer++;\n}\n\nvoid set_resblock_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, int C, int OC) {\n    int layer = l_info->layer;\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)acts->layer_ptrs[layer];\n    set_resblock_acts_ptrs(C, OC, resblock_acts, l_info->mem_ptr);\n    resblock_acts->input = l_info->input;\n    resblock_acts->emb = l_info->emb;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = resblock_acts->add2;\n    l_info->layer++;\n}\n\nvoid set_avgpool_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)acts->layer_ptrs[layer];\n    avgpool_acts->out = l_info->mem_ptr;\n    avgpool_acts->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = avgpool_acts->out;\n    l_info->layer++;\n}\n\nvoid set_attention_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    AttentionActs* att_acts = (AttentionActs*)acts->layer_ptrs[layer];\n    set_attention_acts_pointers(att_acts, l_info->mem_ptr);\n    att_acts->input = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = att_acts->add;\n    l_info->layer++;\n}\n\nvoid set_concat_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info, float** skip_acts) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)acts->layer_ptrs[layer];\n    cat_acts->out = l_info->mem_ptr;\n    cat_acts->x1 = l_info->input;\n    cat_acts->x2 = skip_acts[l_info->skip_idx];\n\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = cat_acts->out;\n    l_info->layer++;\n    l_info->skip_idx--;\n}\n\nvoid set_upsample_layer_acts_ptrs(UnetBlock* acts, LayerInfo* l_info) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)acts->layer_ptrs[layer];\n    up_acts->x = l_info->input;\n    up_acts->out = l_info->mem_ptr;\n\n    l_info->mem_ptr += acts-> layer_sizes[layer];\n    l_info->input = up_acts->out;\n    l_info->layer++;\n}\n\nvoid set_groupnorm_layer_acts_ptrs(\n    UnetBlock* acts, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormActs* gn_a = (GroupNormActs*)acts->layer_ptrs[layer];\n    gn_set_act_ptrs(gn_a, l_info->mem_ptr, B, C, H, W, gn_n_groups);\n    gn_a->x = l_info->input;\n    l_info->mem_ptr += acts->layer_sizes[layer];\n    l_info->input = gn_a->out;\n    l_info->layer++;\n}\n\nvoid point_unet_acts(UnetConfig* config, UnetBlock* acts, float** skip_acts, float* input, float* timestep_emb) {\n    // common constants\n    int B = config->B;\n    int C_model = config->C_model;\n    int H = config->H;\n    int W = config->W;\n    int gn_n_groups = config->gn_n_groups;\n    int n_levels = config->n_levels;\n    int n_res = config->n_res;\n    int att_start_level = config->att_start_level;\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    l_info.mem_ptr = acts->mem_ptr;\n\n    // timestep emb\n    l_info.input = timestep_emb;\n    set_linear_acts_ptrs(acts, &l_info);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_linear_acts_ptrs(acts, &l_info);\n    // point input to model input, and set outputu of the last linear layer as the embedding input\n    l_info.emb = l_info.input;\n    l_info.input = input;\n\n    int OC = C_model;\n    set_convk3_acts_ptrs(acts, &l_info);\n    int skip_idx = 0;\n    skip_acts[skip_idx] = l_info.input; // set the output of the previouse layer as input to the skip connection\n    skip_idx++;\n\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config->channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            set_resblock_layer_acts_ptrs(acts, &l_info, C, OC);\n            if (l < att_start_level) {\n                // for the first 2 layers, the skip connection comes from the resblocks\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n                skip_acts[skip_idx] = l_info.input;\n                skip_idx++;\n            }\n        }\n        if (l < n_levels - 1) {\n            set_avgpool_layer_acts_ptrs(acts, &l_info);\n            skip_acts[skip_idx] = l_info.input;\n            skip_idx++;\n        }\n    }\n\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n    set_attention_layer_acts_ptrs(acts, &l_info);\n    set_resblock_layer_acts_ptrs(acts, &l_info, C, C);\n\n    // upward: set concat\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int mult = config->channel_mult[l];\n        int C_skip = mult * C_model;\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config->channel_mult[l - 1];\n            }\n            set_concat_layer_acts_ptrs(acts, &l_info, skip_acts);\n            OC = mult * C_model;\n            set_resblock_layer_acts_ptrs(acts, &l_info, C + C_skip, OC);\n            C = OC;\n            if (l >= att_start_level) {\n                set_attention_layer_acts_ptrs(acts, &l_info);\n            }\n        }\n        if (l > 0) {\n            set_upsample_layer_acts_ptrs(acts, &l_info);\n        }\n    }\n    set_groupnorm_layer_acts_ptrs(acts, &l_info, B, C, H, W, gn_n_groups);\n    set_silu_acts_ptrs(acts, &l_info);\n    set_convk3_acts_ptrs(acts, &l_info);\n}\n\nvoid set_unet_back_pointers(UnetBackBlock* back) {\n    UnetBackActs* back_acts = &(back->back_acts);\n    float* back_mem_ptr = back->back_mem;\n\n    float** ptrs[] = {\n        &(back_acts->dinp_buf1),\n        &(back_acts->dinp_buf2),\n        &(back_acts->buf1_BCHW),\n        &(back_acts->buf2_BCHW),\n        &(back_acts->buf3_BCHW),\n        &(back_acts->buf4_BCHW),\n        &(back_acts->buf1_B3CHW),\n        &(back_acts->buf2_B3CHW),\n        &(back_acts->demb_buf1),\n        &(back_acts->demb_buf2)\n    };\n\n    for (int i = 0; i < NUM_BACK_BUFS; i++) {\n        *(ptrs[i]) = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[i];\n    }\n\n    // point the back acts for skip connections\n    for (int s = 0; s < NUM_SKIP_CONNECTIONS; s++) {\n        back_acts->skip_dinps[s] = back_mem_ptr;\n        back_mem_ptr += back->back_sizes[NUM_BACK_BUFS + s];\n    }\n}\n\nvoid malloc_and_point(Unet* unet) {\n    float *param_mem, *grad_mem, *act_mem, *back_mem;\n    cudaCheck(hipMalloc(&param_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&grad_mem, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&act_mem, unet->acts.total_size * sizeof(float)));\n    cudaCheck(hipMalloc(&back_mem, unet->back.n_backs * sizeof(float)));\n\n    cudaCheck(hipMemset(param_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMemset(grad_mem, 0, unet->params.total_size * sizeof(float)));\n    cudaCheck(hipMemset(act_mem, 0, unet->acts.total_size * sizeof(float)));\n    cudaCheck(hipMemset(back_mem, 0, unet->back.n_backs * sizeof(float)));\n\n    // constants\n    int B = unet->config.B;\n    int C_in = unet->config.C_in;\n    int C_model = unet->config.C_model;\n    int H = unet->config.H;\n    int W = unet->config.W;\n\n    // malloc and point input and timestep emb\n    cudaCheck(hipMalloc(&(unet->input), B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&(unet->timestep_emb), B * C_model * sizeof(float)));\n    // malloc a nicely aligned buffer for the loss\n    cudaCheck(hipMalloc(&(unet->gpu_loss), 64 * sizeof(float)));\n\n    unet->params.mem_ptr = param_mem;\n    unet->grads.mem_ptr = grad_mem;\n    unet->acts.mem_ptr = act_mem;\n    unet->back.back_mem = back_mem;\n\n    point_unet_params(&(unet->config), &(unet->params));\n    point_unet_params(&(unet->config), &(unet->grads));\n    point_unet_acts(&(unet->config), &(unet->acts), unet->skip_acts, unet->input, unet->timestep_emb);\n    set_unet_back_pointers(&(unet->back));\n}\n\n// ------------------------------------------------------------------------------------------------\n// forward pass\n\nvoid linear_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int N, int C, int OC, int block_size\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_forward2(cublas_handle, linear_acts->out, linear_acts->inp, linear_params->w, linear_params->b, N, C, OC, block_size);\n    l_info->layer++;\n}\n\nvoid silu_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_forward(acts->x, acts->out, N, block_size);\n    l_info->layer++;\n}\n\nvoid convk3_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    conv2d_k3_forward3(conv_acts->inp, conv_params->w, conv_params->b, conv_acts->out, B, C, OC, H, W);\n    l_info->layer++;\n}\nvoid resblock_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    resblock_forward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_acts);\n    l_info->layer++;\n}\n\nvoid avgpool_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    AvgpoolActs* avgpool_acts = (AvgpoolActs*)unet->acts.layer_ptrs[layer];\n    avgpool_2d_forward1(avgpool_acts->out, avgpool_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid attention_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size\n) {\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts);\n    l_info->layer++;\n}\n\nvoid concat_layer_forward(\n    Unet* unet,\n    LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    ConcatChannelActs* cat_acts = (ConcatChannelActs*)unet->acts.layer_ptrs[layer];\n    concat_channel_forward(cat_acts->x1, cat_acts->x2, cat_acts->out, B, C1, C2, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid upsample_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size\n) {\n    int layer = l_info->layer;\n    UpsampleActs* up_acts = (UpsampleActs*)unet->acts.layer_ptrs[layer];\n    upsample_forward1(up_acts->out, up_acts->x, B, C, H, W, block_size);\n    l_info->layer++;\n}\n\nvoid groupnorm_layer_forward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_forward(gn_a->x, gn_p->w, gn_p->b, gn_a->out, gn_a->mean, gn_a->rstd, B, C, H, W, gn_n_groups);\n    l_info->layer++;\n}\n\n\nvoid unet_forward(\n    hipblasHandle_t cublas_handle,\n    Unet* unet,\n    float* t_res = nullptr, float* t_att = nullptr\n) {\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    hipEvent_t start, end;\n    if (t_res != nullptr) {\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end));\n    }\n\n    LayerInfo l_info;\n    l_info.layer = 0;\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_model, C_time, block_size);\n    silu_layer_forward(unet, &l_info, B * C_time, block_size);\n    linear_layer_forward(unet, &l_info, cublas_handle, B, C_time, C_time, block_size);\n    int OC = C_model;\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size);\n\n    int C = OC;\n    for (int l = 0; l < n_levels; l++) {\n        int mult = config.channel_mult[l];\n        OC = C_model * mult;\n        for (int r = 0; r < n_res; r++) {\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(start));\n            }\n            resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(end));\n                cudaCheck(hipEventSynchronize(start));\n                cudaCheck(hipEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                *t_res += elapsed_time;\n                printf(\"down resblock: l %d, r %d, Cin %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, OC, H, W, elapsed_time);\n            }\n            C = OC;\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(start));\n                }\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(end));\n                    cudaCheck(hipEventSynchronize(start));\n                    cudaCheck(hipEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                    *t_att += elapsed_time;\n                    printf(\"down att: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                }\n            }\n        }\n        if (l < n_levels - 1) {\n            avgpool_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H /= 2;\n            W /= 2;\n        }\n    }\n\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(start));\n    }\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        *t_res += elapsed_time;\n        printf(\"middle resblock 1: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        cudaCheck(hipEventRecord(start));\n    }\n    attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        *t_att += elapsed_time;\n        printf(\"middle att: Cin %d, H %d, W %d, time %f\\n\", C, H, W, elapsed_time);\n        cudaCheck(hipEventRecord(start));\n    }\n    resblock_layer_forward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        printf(\"middle resblock 2: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        *t_res += elapsed_time;\n    }\n\n    // upward\n    for (int l = n_levels - 1; l >= 0; l--) {\n        int C_skip = C_model * config.channel_mult[l];\n        for (int r = 0; r <= n_res; r++) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            }\n            concat_layer_forward(unet, &l_info, B, C, C_skip, H, W, block_size);\n            OC = C_model * config.channel_mult[l];\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(start));\n            }\n            resblock_layer_forward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(end));\n                cudaCheck(hipEventSynchronize(start));\n                cudaCheck(hipEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                printf(\"up resblock: l %d, r %d, Cin %d, Cskip %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, C_skip, OC, H, W, elapsed_time);\n                *t_res += elapsed_time;\n            }\n            C = OC;\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(start));\n                }\n                attention_layer_forward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(end));\n                    cudaCheck(hipEventSynchronize(start));\n                    cudaCheck(hipEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                    printf(\"up att: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                    *t_att += elapsed_time;\n                }\n            }\n        }\n        if (l > 0) {\n            upsample_layer_forward(unet, &l_info, B, C, H, W, block_size);\n            H *= 2;\n            W *= 2;\n        }\n    }\n\n    groupnorm_layer_forward(unet, &l_info, B, C, H, W, gn_n_groups);\n    silu_layer_forward(unet, &l_info, B * C * H * W, block_size);\n    convk3_layer_forward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n\n    ConvK3Acts* last_acts = (ConvK3Acts*)unet->acts.layer_ptrs[l_info.layer - 1];\n    unet->output = last_acts->out;\n    if (unet->target == nullptr) {\n        // don't have targets, not computing loss\n        unet->mean_loss = -1.0f;\n    } else {\n        mse_forward(unet->output, unet->target, unet->gpu_loss, B * C_out * H * W, block_size);\n        cudaCheck(hipMemcpy(&(unet->mean_loss), unet->gpu_loss, sizeof(float), hipMemcpyDeviceToHost));\n    }\n}\n\n// ------------------------------------------------------------------------------------------------\n// backward pass\nvoid swap_layer_in_out_ptrs(LayerInfo* l_info) {\n    float* temp = l_info->input;\n    l_info->input = l_info->prev_out;\n    l_info->prev_out = temp;\n}\n\nvoid linear_layer_backward(\n    Unet* unet,\n    LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int N, int C, int OC\n) {\n    int layer = l_info->layer;\n    LinearParams* linear_params = (LinearParams*)unet->params.layer_ptrs[layer];\n    LinearParams* linear_grads = (LinearParams*)unet->grads.layer_ptrs[layer];\n    LinearActs* linear_acts = (LinearActs*)unet->acts.layer_ptrs[layer];\n    matmul_backward1(cublas_handle, l_info->prev_out, linear_grads->w, linear_grads->b, l_info->input, linear_acts->inp, linear_params->w, N, C, OC);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid silu_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int N, int block_size\n) {\n    SiluActs* acts = (SiluActs*)unet->acts.layer_ptrs[l_info->layer];\n    silu_backward(l_info->input, acts->x, l_info->prev_out, N, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid convk3_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int OC, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * OC * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ConvK3Params* conv_params = (ConvK3Params*)unet->params.layer_ptrs[layer];\n    ConvK3Params* conv_grads = (ConvK3Params*)unet->grads.layer_ptrs[layer];\n    ConvK3Acts* conv_acts = (ConvK3Acts*)unet->acts.layer_ptrs[layer];\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n    conv2d_k3_backward2(l_info->input, conv_acts->inp, conv_params->w, unet_back_acts->buf1_BCHW, unet_back_acts->buf2_BCHW, l_info->prev_out, conv_grads->w, conv_grads->b, B, C, OC, H, W);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid resblock_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int C_in, int C_emb, int C_out, int B, int H, int W, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        add_inplace_forward(skip_dinp, l_info->input, B * C_out * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    ResBlockParameters* resblock_params = (ResBlockParameters*)unet->params.layer_ptrs[layer];\n    ResBlockParameters* resblock_grads = (ResBlockParameters*)unet->grads.layer_ptrs[layer];\n    ResBlockActivations* resblock_acts = (ResBlockActivations*)unet->acts.layer_ptrs[layer];\n    ResBlockBackwardActivations back_acts;\n    UnetBackActs* unet_back_acts = &(unet->back.back_acts);\n\n    // point back_acts to buffers\n    back_acts.buf_BCemb = unet_back_acts->buf1_BCHW;\n    back_acts.buf_BCHoWo = unet_back_acts->buf2_BCHW;\n    back_acts.buf1_BCHW = unet_back_acts->buf3_BCHW;\n    back_acts.buf2_BCHW = unet_back_acts->buf4_BCHW;\n    back_acts.dweight_buf = unet_back_acts->buf1_B3CHW;\n    back_acts.dbias_buf = unet_back_acts->buf2_B3CHW;\n    back_acts.dout = l_info->input;\n    back_acts.dx = l_info->prev_out;\n    back_acts.demb = l_info->emb;\n\n    resblock_backward(cublas_handle, C_in, C_emb, C_out, B, H, W, block_size, 0, 0, gn_n_groups, resblock_params, resblock_grads, resblock_acts, &back_acts);\n    swap_layer_in_out_ptrs(l_info);\n\n    // add demb to cumulative demb\n    add_inplace_forward(l_info->emb, l_info->emb_cum, B * C_emb, block_size);\n    l_info->layer--;\n}\n\nvoid attention_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    hipblasHandle_t cublas_handle, int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"att adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H * W, block_size);\n        l_info->skip_idx--;\n    }\n    int layer = l_info->layer;\n    AttentionParams* att_params = (AttentionParams*)unet->params.layer_ptrs[layer];\n    AttentionParams* att_grads = (AttentionParams*)unet->grads.layer_ptrs[layer];\n    AttentionActs* att_acts = (AttentionActs*)unet->acts.layer_ptrs[layer];\n    AttentionBackwardActs att_backs;\n    UnetBackActs* unet_backs = &(unet->back.back_acts);\n    att_backs.buf1_BCHW = unet_backs->buf1_BCHW;\n    att_backs.buf2_BCHW = unet_backs->buf2_BCHW;\n    att_backs.buf_B3CHW = unet_backs->buf1_B3CHW;\n    att_backs.dqkvr = unet_backs->buf2_B3CHW;\n    att_backs.dpreatt = unet_backs->buf3_BCHW;\n    att_backs.datt = unet_backs->buf4_BCHW;\n    att_backs.dout = l_info->input;\n    att_backs.dinp = l_info->prev_out;\n\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, att_params, att_acts, &att_backs, att_grads);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid concat_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n    //printf(\"concat skip %d\\n\", l_info->skip_idx);\n    concat_channel_backward(l_info->input, l_info->prev_out, skip_dinp, B, C1, C2, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n    l_info->skip_idx++;\n}\n\n\nvoid avgpool_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int block_size,\n    int add_skip = 0\n) {\n    if (add_skip) {\n        // need to add a skip connection component to dout, only used in the input downward layers\n        float* skip_dinp = unet->back.back_acts.skip_dinps[l_info->skip_idx];\n        //printf(\"avgpool adding skip %d\\n\", l_info->skip_idx);\n        add_inplace_forward(skip_dinp, l_info->input, B * C * H/2 * W/2, block_size);\n        l_info->skip_idx--;\n    }\n    avgpool_2d_backward1(l_info->input, l_info->prev_out, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid upsample_layer_backward(\n    Unet* unet, LayerInfo *l_info,\n    int B, int C,  int H, int W, int block_size\n) {\n    upsample_backward1(l_info->prev_out, l_info->input, B, C, H, W, block_size);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid groupnorm_layer_backward(\n    Unet* unet, LayerInfo* l_info,\n    int B, int C, int H, int W, int gn_n_groups\n) {\n    int layer = l_info->layer;\n    GroupNormParams* gn_p = (GroupNormParams*)unet->params.layer_ptrs[layer];\n    GroupNormParams* gn_g = (GroupNormParams*)unet->grads.layer_ptrs[layer];\n    GroupNormActs* gn_a = (GroupNormActs*)unet->acts.layer_ptrs[layer];\n    groupnorm_backward(l_info->input, gn_a->x, gn_a->mean, gn_a->rstd, gn_p->w, l_info->prev_out, gn_g->w, gn_g->b, B, C, H, W, gn_n_groups);\n    swap_layer_in_out_ptrs(l_info);\n    l_info->layer--;\n}\n\nvoid unet_backward(\n    hipblasHandle_t cublas_handle,\n    Unet* unet,\n    float* t_res = nullptr, float* t_att = nullptr\n) {\n    if (unet->mean_loss == -1.0f or unet->target == nullptr) {\n        printf(\"Error: must run forward with targets before calling backward\\n\");\n        exit(EXIT_FAILURE);\n    }\n    hipEvent_t start, end;\n    if (t_res != nullptr) {\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end));\n    }\n    // common constants\n    UnetConfig config = unet->config;\n    int B = config.B;\n    int C_in = config.C_in;\n    int C_model = config.C_model;\n    int C_time = config.C_time;\n    int C_out = config.C_out;\n    int H = config.H;\n    int W = config.W;\n    int block_size = config.block_size;\n    int gn_n_groups = config.gn_n_groups;\n    int HS = config.HS;\n    int n_levels = config.n_levels;\n    int n_res = config.n_res;\n    int att_start_level = config.att_start_level;\n\n    UnetBackActs* back_acts = &(unet->back.back_acts);\n    mse_backward(unet->output, unet->target, back_acts->dinp_buf1, B * C_out * H * W, block_size);\n\n    LayerInfo l_info;\n    l_info.layer = config.n_layers - 1;\n    l_info.input = back_acts->dinp_buf1;\n    l_info.prev_out = back_acts->dinp_buf2;\n    l_info.emb = back_acts->demb_buf1;\n    l_info.emb_cum = back_acts->demb_buf2;\n    l_info.skip_idx = 0;\n\n    int C;\n    int OC;\n    int C_skip;\n    C = C_model;\n    OC = C_model;\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C, C_out, H, W, block_size);\n    silu_layer_backward(unet, &l_info, B * C * H * W, block_size);\n    groupnorm_layer_backward(unet, &l_info, B, C, H, W, gn_n_groups);\n\n    for (int l = 0; l < n_levels; l++) {\n        for (int r = n_res; r >= 0; r--) {\n            if (r == n_res && l > 0) {\n                C_skip = C_model * config.channel_mult[l - 1];\n            } else {\n                C_skip = C_model * config.channel_mult[l];\n            }\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(start));\n                }\n                attention_layer_backward(unet, &l_info, cublas_handle, B, OC, H, W, HS, gn_n_groups, block_size);\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(end));\n                    cudaCheck(hipEventSynchronize(start));\n                    cudaCheck(hipEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                    *t_att += elapsed_time;\n                    printf(\"up att back: l %d, r %d, Cout %d, H %d, W %d, time %f\\n\", l, r, OC, H, W, elapsed_time);\n                }\n            }\n            if (r == 0 && l < n_levels - 1) {\n                C = C_model * config.channel_mult[l + 1];\n            }\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(start));\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C + C_skip, C_time, OC, B, H, W, gn_n_groups, block_size);\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(end));\n                cudaCheck(hipEventSynchronize(start));\n                cudaCheck(hipEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                *t_res += elapsed_time;\n                printf(\"up res back: l %d, r %d, Cin %d, Cskip %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, C_skip, OC, H, W, elapsed_time);\n            }\n            concat_layer_backward(unet, &l_info, B, C, C_skip, H, W, block_size);\n        }\n        if (l < n_levels - 1) {\n            H /= 2;\n            W /= 2;\n            upsample_layer_backward(unet, &l_info, B, C, H, W, block_size);\n            OC = C_model * config.channel_mult[l + 1];\n        }\n    }\n\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(start));\n    }\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        *t_res += elapsed_time;\n        printf(\"middle resblock 2: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        cudaCheck(hipEventRecord(start));\n    }\n    attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        *t_att += elapsed_time;\n        printf(\"middle att: Cin %d, H %d, W %d, time %f\\n\", C, H, W, elapsed_time);\n        cudaCheck(hipEventRecord(start));\n    }\n    resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, C, B, H, W, gn_n_groups, block_size);\n    if (t_res != nullptr) {\n        cudaCheck(hipEventRecord(end));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        printf(\"middle resblock 1: Cin %d, Cout %d, H %d, W %d, time %f\\n\", C, OC, H, W, elapsed_time);\n        *t_res += elapsed_time;\n    }\n\n    // we will need to change the backward for all the other blocks below to include the dout contribution from the skip connection.\n    l_info.skip_idx = NUM_SKIP_CONNECTIONS - 1;\n    for (int l = n_levels - 1; l >= 0; l--) {\n        if (l < n_levels - 1) {\n            H *= 2;\n            W *= 2;\n            avgpool_layer_backward(unet, &l_info, B, C, H, W, block_size, 1);\n        }\n        for (int r = 0; r < n_res; r++) {\n            if (l >= att_start_level) {\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(start));\n                }\n                attention_layer_backward(unet, &l_info, cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, 1);\n                if (t_res != nullptr) {\n                    cudaCheck(hipEventRecord(end));\n                    cudaCheck(hipEventSynchronize(start));\n                    cudaCheck(hipEventSynchronize(end));\n                    float elapsed_time;\n                    cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                    printf(\"down att back: l %d, r %d, C %d, H %d, W %d, time %f\\n\", l, r, C, H, W, elapsed_time);\n                    *t_att += elapsed_time;\n                }\n            }\n            int res_add_skip = (l < att_start_level) ? 1 : 0;\n            if (r == n_res - 1 && l > 0) {\n                int prev_mult = config.channel_mult[l - 1];\n                C = prev_mult * C_model;\n            }\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(start));\n            }\n            resblock_layer_backward(unet, &l_info, cublas_handle, C, C_time, OC, B, H, W, gn_n_groups, block_size, res_add_skip);\n            if (t_res != nullptr) {\n                cudaCheck(hipEventRecord(end));\n                cudaCheck(hipEventSynchronize(start));\n                cudaCheck(hipEventSynchronize(end));\n                float elapsed_time;\n                cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n                printf(\"down res back: l %d, r %d, Cin %d, Cout %d, H %d, W %d, time %f\\n\", l, r, C, OC, H, W, elapsed_time);\n                *t_res += elapsed_time;\n            }\n            OC = C;\n        }\n    }\n\n    convk3_layer_backward(unet, &l_info, cublas_handle, B, C_in, OC, H, W, block_size, 1);\n    unet->dinp = l_info.input;\n\n    // reset dout to point to emb_cum\n    l_info.input = l_info.emb_cum;\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_time, C_time);\n    silu_layer_backward(unet, &l_info, B * C_time, block_size);\n    linear_layer_backward(unet, &l_info, cublas_handle, B, C_model, C_time);\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// functions to update gradient\n\nvoid unet_zero_grad(Unet* unet) {\n    cudaCheck(hipMemset(unet->grads.mem_ptr, 0, unet->grads.total_size * sizeof(float)));\n    cudaCheck(hipMemset(unet->back.back_mem, 0, unet->back.n_backs * sizeof(float)));\n    //cudaCheck(cudaMemset(unet->acts.mem_ptr, 0, unet->acts.total_size * sizeof(float)));\n}\n\n// below copied from llm.c\n\n// Implements linear interpolation using only two floating-point operations (as opposed to three in a naive implementation).\n// Reference: https://developer.nvidia.com/blog/lerp-faster-cuda\n__device__ inline float lerp(float start, float end, float weight) {\n    return fma(weight, end, fma(-weight, start, start));\n}\n\n__global__ void adamw_kernel2(float* params_memory, float* grads_memory, float* m_memory, float* v_memory, long num_parameters,\n                              float learning_rate, float beta1, float beta2, float beta1_correction, float beta2_correction, float eps, float weight_decay) {\n   int i = blockIdx.x * blockDim.x + threadIdx.x;\n   if (i >= num_parameters) return;  // guard\n   float grad = grads_memory[i];\n   float m = m_memory[i];\n   float v = v_memory[i];\n   // update the first moment (momentum)\n   m = lerp(grad, m, beta1);\n   m_memory[i] = m;\n   // update the second moment (RMSprop)\n   v = lerp(grad * grad, v, beta2);\n   v_memory[i] = v;\n   m /= beta1_correction;  // m_hat\n   v /= beta2_correction;  // v_hat\n   params_memory[i] -= learning_rate * (m / (sqrtf(v) + eps) + weight_decay * params_memory[i]);\n}\n\nvoid unet_update(Unet* unet, float learning_rate, float beta1, float beta2, float eps, float weight_decay, int t) {\n    // lazy allocation\n    if (unet->m_memory == nullptr) {\n        size_t param_size = unet->params.total_size * sizeof(float);\n        cudaCheck(hipMalloc(&(unet->m_memory), param_size));\n        cudaCheck(hipMalloc(&(unet->v_memory), param_size));\n        cudaCheck(hipMemset(unet->m_memory, 0, param_size));\n        cudaCheck(hipMemset(unet->v_memory, 0, param_size));\n        printf(\"allocated %zu MiB for AdamW state m\\n\", param_size / 1024 / 1024);\n        printf(\"allocated %zu MiB for AdamW state v\\n\", param_size / 1024 / 1024);\n    }\n\n    int block_size = unet->config.block_size;\n    int n_blocks = ceil_div(unet->params.total_size, (size_t)block_size);\n    float beta1_correction = 1.0f - powf(beta1, t);\n    float beta2_correction = 1.0f - powf(beta2, t);\n    adamw_kernel2<<<n_blocks, block_size>>>(unet->params.mem_ptr, unet->grads.mem_ptr, unet->m_memory, unet->v_memory, unet->params.total_size,\n                    learning_rate, beta1, beta2, beta1_correction, beta2_correction, eps, weight_decay);\n    cudaCheck(hipGetLastError());\n}\n\n\n// ------------------------------------------------------------------------------------------------\n// run tests\n\n#define NUM_DEBUG_STATES 5\ntypedef struct {\n    float* input;\n    float* timestep_emb;\n    float* out;\n    float* dinp;\n    float* target;\n} DebugStates;\n\n\nint main(int argc, char **argv) {\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    char cwd[PATH_MAX];\n    if (getcwd(cwd, sizeof(cwd)) != NULL) {\n        //printf(\"Current working dir: %s\\n\", cwd);\n    } else {\n        perror(\"getcwd() error\");\n        return 1;\n    }\n\n    // read header\n    FILE *model_file = fopenCheck(\"unet_test_params.bin\", \"rb\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file\\n\");\n        exit(1);\n    }\n    int B = model_header[1];\n    int C_in = model_header[2];\n    int C_model = model_header[3];\n    int C_out = model_header[4];\n    int H = model_header[5];\n    int W = model_header[6];\n    int max_period = model_header[7];\n    int C_time = C_model * 4;\n    const int n_levels = 4;\n    int channel_mult[n_levels] = {1, 2, 3, 4};\n\n    // dataloader\n    DataLoader loader;\n    dataloader_init(&loader, DATASET_FILE, B);\n\n\n    Unet unet;\n    UnetConfig* config = &(unet.config);\n    config->B = B;\n    config->C_in = C_in;\n    config->C_model = C_model;\n    config->C_time = C_time;\n    config->C_out = C_out;\n    config->H = H;\n    config->W = W;\n    config->block_size = 512;\n    config->gn_n_groups = 32;\n    config->n_levels = n_levels;\n    config->channel_mult = channel_mult;\n    config->HS = 32;\n    config->n_res = 2;\n    config->max_period = max_period;\n    config->att_start_level = 2;\n\n    unet_init(&unet);\n\n    unet_count_layers(config);\n    unet_make_ptrs_and_count_memory(&unet);\n    malloc_and_point(&unet);\n\n    // allocate host memory for params\n    float* params_mem_cpu = (float*)mallocCheck(unet.params.total_size * sizeof(float));\n    freadCheck(params_mem_cpu, sizeof(float), unet.params.total_size, model_file);\n    fcloseCheck(model_file);\n    UnetBlock params_cpu;\n    unet_setup_and_count_params(config, &params_cpu);\n    params_cpu.mem_ptr = params_mem_cpu;\n    point_unet_params(config, &params_cpu);\n\n    // copy params to device\n    cudaCheck(hipMemcpy(unet.params.mem_ptr, params_mem_cpu, unet.params.total_size * sizeof(float), hipMemcpyHostToDevice));\n\n    // allocate host memory for debug states\n    FILE *debug_file = fopenCheck(\"unet_test_acts.bin\", \"rb\");\n    size_t state_sizes[NUM_DEBUG_STATES];\n    int out_size = B * C_out * H * W;\n    state_sizes[0] = B * C_in * H * W; // input\n    state_sizes[1] = B; // timestep emb\n    state_sizes[2] = out_size; // out\n    state_sizes[3] = B * C_in * H * W; // dinp\n    state_sizes[4] = out_size; // target\n\n    size_t num_debug_params = 0;\n    for (int i = 0; i < NUM_DEBUG_STATES; i++) {\n        num_debug_params += state_sizes[i];\n    }\n\n    float* debug_states_cpu = (float*)mallocCheck(num_debug_params * sizeof(float));\n    freadCheck(debug_states_cpu, sizeof(float), num_debug_params, debug_file);\n    // point to the debug states\n    DebugStates debug_states;\n    float** debug_ptrs[] = {\n        &(debug_states.input),\n        &(debug_states.timestep_emb),\n        &(debug_states.out),\n        &(debug_states.dinp),\n        &(debug_states.target),\n    };\n\n    float* debug_mem_ptr = debug_states_cpu;\n    for (int i = 0; i < NUM_DEBUG_STATES; i++) {\n        *(debug_ptrs[i]) = debug_mem_ptr;\n        debug_mem_ptr += state_sizes[i];\n    }\n\n    // read the groud truth grads\n    float* grads_mem_cpu = (float*)mallocCheck(unet.grads.total_size * sizeof(float));\n    freadCheck(grads_mem_cpu, sizeof(float), unet.grads.total_size, debug_file);\n    fcloseCheck(debug_file);\n\n\n    // read the losses\n    float losses[10];\n    FILE *loss_file = fopenCheck(\"unet_test_losses.bin\", \"rb\");\n    freadCheck(losses, sizeof(float), 10, loss_file);\n    fcloseCheck(loss_file);\n\n    // create a grads struct and point to cpu grads for easier validation\n    UnetBlock grads_cpu;\n    unet_setup_and_count_params(config, &grads_cpu);\n    grads_cpu.mem_ptr = grads_mem_cpu;\n    point_unet_params(config, &grads_cpu);\n\n    //// copy input to device\n    //cudaCheck(cudaMemcpy(unet.input, debug_states.input, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    // load input\n    dataloader_next_batch(&loader);\n    cudaCheck(hipMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n    // try out the random number generator\n    GaussianDiffusion diffusion;\n    diffusion_init(&diffusion, B, C_in, H, W, 1000);\n    printf(\"successful gaussian init\\n\");\n\n    float rand_nums[B * 10];\n    sample_timesteps(&diffusion, rand_nums, B * 10);\n\n    // compute timestep emb\n    float* d_timesteps;\n    cudaCheck(hipMalloc(&d_timesteps, B * sizeof(float)));\n    hipMemcpy(d_timesteps, rand_nums, B * sizeof(float), hipMemcpyHostToDevice);\n    get_timestep_embeddings(&unet.time_emb_state, d_timesteps, unet.timestep_emb);\n\n    // generate all the gaussian noise\n    float *all_noise = (float*)malloc(10 * B * C_in * H * W * sizeof(float));\n    normal_(all_noise, 10 * B * C_in * H * W, 0.0f, 1.0f, &diffusion.host_rng);\n\n\n    cudaCheck(hipMalloc(&(unet.target), out_size * sizeof(float)));\n\n    //printf(\"drawing and saving x_t for verification\\n\");\n    //float* x_t_cpu = (float*)mallocCheck(B * C_in * H * W * sizeof(float));\n    //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n    //print_gpu_first_5(\"x_t\", unet.target);\n    //cudaCheck(cudaMemcpy(x_t_cpu, unet.target, B * C_in * H * W * sizeof(float), cudaMemcpyDeviceToHost));\n    //FILE *sample_debug_file = fopenCheck(\"./sample_debug.bin\", \"wb\");\n    //size_t result = fwrite(rand_nums, sizeof(float), B, sample_debug_file);\n    //if (result != B) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //result = fwrite(loader.input, sizeof(float), B * C_in * H * W, sample_debug_file);\n    //if (result != B * C_in * H * W) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //result = fwrite(x_t_cpu, sizeof(float), B * C_in * H * W, sample_debug_file);\n    //if (result != B * C_in * H * W) {\n    //    fprintf(stderr, \"Error writing to file\\n\");\n    //    exit(1);\n    //}\n    //fcloseCheck(sample_debug_file);\n\n\n    // copy the targets to gpu\n    //cudaCheck(cudaMemcpy(unet.target, debug_states.target, out_size * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(unet.target, all_noise, out_size * sizeof(float), hipMemcpyHostToDevice));\n    diffusion_sample(d_timesteps, unet.input, unet.target, &diffusion, B, C_in * H * W);\n    //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n\n\n\n    printf(\"\\nCheck that unet targets match the debug states\\n\");\n    validate_result(unet.target, debug_states.target, \"target\", out_size);\n\n\n    float acc = 1e-4;\n    float med_acc = 1e-2;\n    printf(\"Running 10 update loops\\n\");\n    // loop 1\n    printf(\"Checking forward pass\\n\");\n    float t_res = 0.0f;\n    float t_att = 0.0f;\n    unet_forward(cublas_handle, &unet, &t_res, &t_att);\n    printf(\"resblock time: %f ms, attention time: %f ms\\n\", t_res, t_att);\n    printf(\"\\nChecking out\\n\");\n    validate_result(unet.output, debug_states.out, \"out\", out_size, med_acc);\n    printf(\"cpu loss: %f, gpu loss: %f\\n\", losses[0], unet.mean_loss);\n    printf(\"\\nForward pass successful\\n\");\n\n    printf(\"\\nChecking backward pass\\n\");\n    t_res = 0.0f;\n    t_att = 0.0f;\n    unet_backward(cublas_handle, &unet, &t_res, &t_att);\n    printf(\"resblock back time: %f ms, attention back time: %f ms\\n\", t_res, t_att);\n    printf(\"\\nChecking grads\\n\");\n    validate_result(unet.grads.mem_ptr, grads_mem_cpu, \"grads\", unet.grads.total_size, med_acc);\n    printf(\"\\nChecking dinp\\n\");\n    validate_result(unet.dinp, debug_states.dinp, \"dinp\", B * C_in * H * W, acc);\n    unet_update(&unet, 1e-3f, 0.9f, 0.999f, 1e-8f, 0.01f, 1);\n    unet_zero_grad(&unet);\n\n    // loop 2-9\n    hipEvent_t start, end;\n    cudaCheck(hipEventCreate(&start));\n    cudaCheck(hipEventCreate(&end));\n    cudaCheck(hipProfilerStart());\n    cudaCheck(hipEventRecord(start));\n    for (int i = 1; i < 10; i++) {\n        dataloader_next_batch(&loader);\n        cudaCheck(hipMemcpy(unet.input, loader.input, B * C_in * H * W * sizeof(float), hipMemcpyHostToDevice));\n        hipMemcpy(d_timesteps, rand_nums + i * B, B * sizeof(float), hipMemcpyHostToDevice);\n        get_timestep_embeddings(&unet.time_emb_state, d_timesteps, unet.timestep_emb);\n\n        cudaCheck(hipMemcpy(unet.target, all_noise + i * out_size, out_size * sizeof(float), hipMemcpyHostToDevice));\n        diffusion_sample(d_timesteps, unet.input, unet.target, &diffusion, B, C_in * H * W);\n        //diffusion_draw_and_sample_xt(&diffusion, unet.target, d_timesteps, unet.input);\n\n        unet_forward(cublas_handle, &unet);\n        unet_backward(cublas_handle, &unet);\n        printf(\"step %d, cpu loss: %f, gpu loss: %f\\n\", i+1, losses[i], unet.mean_loss);\n        unet_update(&unet, 1e-3f, 0.9f, 0.999f, 1e-8f, 0.01f, i+1);\n        unet_zero_grad(&unet);\n    }\n    cudaCheck(hipEventRecord(end));\n    cudaCheck(hipEventSynchronize(start));\n    cudaCheck(hipEventSynchronize(end));\n    float time_elapsed;\n    cudaCheck(hipEventElapsedTime(&time_elapsed, start, end));\n    time_elapsed /= 9;\n    printf(\"Average time per step: %f ms\\n\", time_elapsed);\n\n\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmark\\n\");\n    int repeat_times = 50;\n    float elapsed_time = benchmark_kernel(repeat_times, unet_forward, cublas_handle, &unet, nullptr, nullptr);\n    printf(\"Forward pass time: %f ms\\n\", elapsed_time);\n\n    printf(\"\\nBackward pass benchmark\\n\");\n    elapsed_time = benchmark_kernel(repeat_times, unet_backward, cublas_handle, &unet, nullptr, nullptr);\n    printf(\"Backward pass time: %f ms\\n\", elapsed_time);\n\n    // free memory\n    free(params_mem_cpu);\n    free(debug_states_cpu);\n    free_unet(&unet);\n    cudaCheck(hipFree(unet.target));\n    cudaCheck(hipFree(d_timesteps));\n    dataloader_free(&loader);\n    diffusion_free(&diffusion);\n    free(all_noise);\n\n    // free cublas\n    cublasCheck(hipblasDestroy(cublas_handle));\n}", "source": "/kaggle/working/unet.cu/dev/unet_test.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"attention.cuh\"\n#include \"groupnorm.cuh\"\n#include \"linear.cuh\"\n#include \"add.cuh\"\n#include \"attention_block.cuh\"\n\n\n__global__ void attention_permute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, C, H, W to B, HxW, C\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[b * C * img_size + h * C * W + w * C + c] = inp[idx];\n}\n\nvoid attention_permute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_permute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n__global__ void attention_unpermute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, HxW, C to B, C, H, W\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[idx] = inp[b * C * img_size + h * C * W + w * C + c];\n}\n\nvoid attention_unpermute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_unpermute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n\nvoid attention_block_forward\n(\n    cublasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts\n) {\n    // apply groupnorm\n    groupnorm_forward(acts->input, params->gn_w, params->gn_b, acts->gn, acts->gn_mean, acts->gn_rstd, B, C, H, W, gn_n_groups);\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(acts->gn, acts->perm1, B, C, H, W, block_size);\n    // get qkv\n    matmul_forward2(cublas_handle, acts->qkv1, acts->perm1, params->qkv_w, params->qkv_b, B * H * W, C, 3 * C, block_size);\n    // apply attention\n    attention_forward1(cublas_handle, acts->att_out, acts->qkv2, acts->preatt, acts->att, acts->qkv1, B, H * W, C, C / HS, block_size);\n    // attention projection\n    matmul_forward2(cublas_handle, acts->proj, acts->att_out, params->proj_w, params->proj_b, B * H * W, C, C, block_size);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(acts->proj, acts->perm2, B, C, H, W, block_size);\n    // add back residual stream\n    add_forward(acts->input, acts->perm2, acts->add, B * C * H * W, block_size);\n}\n\n\nvoid attention_block_backward\n(\n    cublasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts,\n    AttentionBackwardActs* back_acts, AttentionParams* grads\n) {\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(back_acts->dout, back_acts->buf1_BCHW, B, C, H, W, block_size);\n    // backward through projection\n    matmul_backward1(cublas_handle, back_acts->buf2_BCHW, grads->proj_w, grads->proj_b, back_acts->buf1_BCHW, acts->att_out, params->proj_w, B * H * W, C, C);\n    // backward through attention\n    attention_backward(cublas_handle, back_acts->buf_B3CHW, back_acts->dqkvr, back_acts->dpreatt, back_acts->datt, back_acts->buf1_BCHW, back_acts->buf2_BCHW, acts->qkv2, acts->att, B, H * W, C, C / HS);\n    // backward through qkv\n    matmul_backward1(cublas_handle, back_acts->buf1_BCHW, grads->qkv_w, grads->qkv_b, back_acts->buf_B3CHW, acts->perm1, params->qkv_w, B * H * W, C, 3 * C);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(back_acts->buf1_BCHW, back_acts->buf2_BCHW, B, C, H, W, block_size);\n    // backward through groupnorm\n    groupnorm_backward(back_acts->buf2_BCHW, acts->input, acts->gn_mean, acts->gn_rstd, params->gn_w, back_acts->buf1_BCHW, grads->gn_w, grads->gn_b, B, C, H, W, gn_n_groups);\n    // add residual gradient\n    add_forward(back_acts->buf1_BCHW, back_acts->dout, back_acts->dinp, B * C * H * W, block_size);\n}\n\n\nvoid set_attention_params_pointers(\n    AttentionParams* params,\n    float* params_memory\n) {\n    float** ptrs[] = {\n        &params->gn_w,\n        &params->gn_b,\n        &params->qkv_w,\n        &params->qkv_b,\n        &params->proj_w,\n        &params->proj_b\n    };\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        *(ptrs[i]) = params_memory;\n        params_memory += params->param_sizes[i];\n    }\n}\n\nvoid set_attention_acts_pointers(\n    AttentionActs* acts,\n    float* acts_memory\n) {\n    float** ptrs[] = {\n        &acts->gn,\n        &acts->gn_mean,\n        &acts->gn_rstd,\n        &acts->perm1,\n        &acts->qkv1,\n        &acts->qkv2,\n        &acts->preatt,\n        &acts->att,\n        &acts->att_out,\n        &acts->proj,\n        &acts->perm2,\n        &acts->add\n    };\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        *(ptrs[i]) = acts_memory;\n        acts_memory += acts->act_sizes[i];\n    }\n}\n\nvoid set_attention_back_acts_pointers(\n    AttentionBackwardActs* back_acts,\n    float* back_acts_memory,\n    size_t* back_act_sizes\n) {\n    float** ptrs[] = {\n        &back_acts->buf1_BCHW,\n        &back_acts->buf2_BCHW,\n        &back_acts->buf_B3CHW,\n        &back_acts->dqkvr,\n        &back_acts->dpreatt,\n        &back_acts->datt,\n        &back_acts->dout,\n    };\n    for (int i = 0; i < NUM_ATT_BACKWARD_ACTS_TENSORS; i++) {\n        *(ptrs[i]) = back_acts_memory;\n        back_acts_memory += back_act_sizes[i];\n    }\n}\n\n\nvoid attention_block_count_backs(\n    AttentionBackwardActs* back_acts,\n    int B, int C, int H, int W, int HS, int gn_n_groups\n){\n    int NH = C / HS;\n    int C3 = 3 * C;\n    int T = H * W;\n\n    // count back acts\n    size_t *back_act_sizes = back_acts->back_sizes;\n    back_act_sizes[0] = B * C * H * W; // buf1_BCHW\n    back_act_sizes[1] = B * C * H * W; // buf2_BCHW\n    back_act_sizes[2] = B * C3 * H * W; // buf_B3CHW\n    back_act_sizes[3] = B * T * C3; // dqkvr\n    back_act_sizes[4] = B * NH * T * T; // dpreatt\n    back_act_sizes[5] = B * NH * T * T; // datt\n    back_act_sizes[6] = B * C * H * W; // dout\n    \n    size_t num_back_act_params = 0;\n    for (int i = 0; i < NUM_ATT_BACKWARD_ACTS_TENSORS; i++) {\n        num_back_act_params += back_act_sizes[i];\n    }\n    back_acts->n_backs = num_back_act_params;\n}\n\nvoid attention_block_count_params(AttentionParams* params, int C) {\n    size_t *param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn_w\n    param_sizes[1] = C; // gn_b\n    param_sizes[2] = 3 * C * C; // qkv_w\n    param_sizes[3] = 3 * C; // qkv_b\n    param_sizes[4] = C * C; // proj_w\n    param_sizes[5] = C; // proj_b\n\n    size_t num_parameters = 0;\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\nvoid attention_block_count_acts(\n    AttentionActs* acts,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    int C3 = 3 * C;\n    int T = H * W;\n    int NH = C / HS;\n    size_t *act_sizes = acts->act_sizes;\n    act_sizes[0] = B * C * H * W; // gn\n    act_sizes[1] = B * gn_n_groups; // gn_mean\n    act_sizes[2] = B * gn_n_groups; // gn_rstd\n    act_sizes[3] = B * T * C; // perm1\n    act_sizes[4] = B * T * C3; // qkv1\n    act_sizes[5] = B * T * C3; // qkv2\n    act_sizes[6] = B * NH * T * T; // preatt\n    act_sizes[7] = B * NH * T * T; // att\n    act_sizes[8] = B * T * C; // att_out\n    act_sizes[9] = B * T * C; // proj\n    act_sizes[10] = B * C * H * W; // perm2\n    act_sizes[11] = B * C * H * W; // add\n\n    size_t num_acts_params = 0;\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        num_acts_params += act_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    // read header\n    FILE *model_file = fopenCheck(\"attention_block_params.bin\", \"rb\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file, magic number %d\\n\", model_header[0]);\n        exit(1);\n    }\n    int B = model_header[1];\n    int C = model_header[2];\n    int H = model_header[3];\n    int W = model_header[4];\n    int HS = model_header[5];\n    int gn_n_groups = model_header[6];\n\n    AttentionParams params, grads, params_cpu;\n    attention_block_count_params(&params, C);\n    attention_block_count_params(&grads, C);\n    attention_block_count_params(&params_cpu, C);\n\n    size_t num_parameters = params.n_params;\n    // read params to host\n    float* params_memory_cpu = (float*)mallocCheck(num_parameters * sizeof(float));\n    freadCheck(params_memory_cpu, sizeof(float), num_parameters, model_file);\n    fcloseCheck(model_file);\n\n    // copy params to device\n    float* params_memory_gpu;\n    cudaCheck(cudaMalloc(&params_memory_gpu, num_parameters * sizeof(float)));\n    cudaCheck(cudaMemcpy(params_memory_gpu, params_memory_cpu, num_parameters * sizeof(float), cudaMemcpyHostToDevice));\n\n    // create device memory for grads\n    float* grads_memory_gpu;\n    cudaCheck(cudaMalloc(&grads_memory_gpu, num_parameters * sizeof(float)));\n    cudaCheck(cudaMemset(grads_memory_gpu, 0, num_parameters * sizeof(float)));\n\n    // set param pointers\n    set_attention_params_pointers(&params, params_memory_gpu);\n    set_attention_params_pointers(&grads, grads_memory_gpu);\n    set_attention_params_pointers(&params_cpu, params_memory_cpu);\n\n    // calculate activation sizes\n    int C3 = 3 * C;\n    int T = H * W;\n    AttentionActs acts;\n    attention_block_count_acts(&acts, B, C, H, W, gn_n_groups, HS);\n    size_t num_acts_params = acts.n_acts;\n\n    // allocate device act memory\n    float* acts_memory_gpu;\n    cudaCheck(cudaMalloc(&acts_memory_gpu, num_acts_params * sizeof(float)));\n    cudaCheck(cudaMemset(acts_memory_gpu, 0, num_acts_params * sizeof(float)));\n\n    // set acts pointers\n    set_attention_acts_pointers(&acts, acts_memory_gpu);\n\n    // malloc input, which is not part of the large block of acts memory\n    cudaCheck(cudaMalloc(&acts.input, B * C * H * W * sizeof(float)));\n\n    // create debug states\n    size_t debug_state_sizes[NUM_ATT_DEBUG_STATES];\n    debug_state_sizes[0] = B * C * H * W; // inp\n    debug_state_sizes[1] = B * C * H * W; // gn\n    debug_state_sizes[2] = B * C * H * W; // perm1\n    debug_state_sizes[3] = B * C3 * H * W; // qkv\n    debug_state_sizes[4] = B * C * H * W; // att\n    debug_state_sizes[5] = B * C * H * W; // proj\n    debug_state_sizes[6] = B * C * H * W; // out\n    debug_state_sizes[7] = B * C * H * W; // dout\n    debug_state_sizes[8] = B * C * H * W; // dinp\n\n    size_t num_debug_states = 0;\n    for (int i = 0; i < NUM_ATT_DEBUG_STATES; i++) {\n        num_debug_states += debug_state_sizes[i];\n    }\n\n    // allocate host debug state memory and host grads memory\n    float* debug_states_memory_cpu = (float*)mallocCheck(num_debug_states * sizeof(float));\n    float* grads_memory_cpu = (float*)mallocCheck(num_parameters * sizeof(float));\n    AttentionParams grads_cpu;\n    attention_block_count_params(&grads_cpu, C);\n    set_attention_params_pointers(&grads_cpu, grads_memory_cpu);\n    FILE *debug_states_file = fopenCheck(\"attention_block_states.bin\", \"rb\");\n    printf(\"num debug states: %ld\\n\", num_debug_states);\n    freadCheck(debug_states_memory_cpu, sizeof(float), num_debug_states, debug_states_file);\n    freadCheck(grads_memory_cpu, sizeof(float), num_parameters, debug_states_file);\n    fcloseCheck(debug_states_file);\n\n    // set debug states pointers\n    AttentionDebugStates debug_states;\n    float* debug_states_ptr = debug_states_memory_cpu;\n    float** debug_states_ptrs[] = {\n        &debug_states.inp,\n        &debug_states.gn,\n        &debug_states.perm1,\n        &debug_states.qkv,\n        &debug_states.att,\n        &debug_states.proj,\n        &debug_states.out,\n        &debug_states.dout,\n        &debug_states.dinp\n    };\n    for (int i = 0; i < NUM_ATT_DEBUG_STATES; i++) {\n        *(debug_states_ptrs[i]) = debug_states_ptr;\n        debug_states_ptr += debug_state_sizes[i];\n    }\n\n    // create attention backward acts\n    AttentionBackwardActs back_acts;\n    attention_block_count_backs(&back_acts, B, C, H, W, HS, gn_n_groups);\n    size_t num_back_act_params = back_acts.n_backs;\n\n    float* back_acts_memory_gpu;\n    cudaCheck(cudaMalloc(&back_acts_memory_gpu, num_back_act_params * sizeof(float)));\n    cudaCheck(cudaMemset(back_acts_memory_gpu, 0, num_back_act_params * sizeof(float)));\n\n    // set pointers\n    set_attention_back_acts_pointers(&back_acts, back_acts_memory_gpu, back_acts.back_sizes);\n    back_acts.dinp = acts.input;\n\n    // copy input and dout to device\n    cudaCheck(cudaMemcpy(acts.input, debug_states.inp, B * C * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(back_acts.dout, debug_states.dout, B * C * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n    int block_size = 512;\n\n    printf(\"Checking forward pass\\n\");\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts);\n\n    // validate results\n    float acc = 1e-4;\n    printf(\"\\nChecking gn\\n\");\n    validate_result(acts.gn, debug_states.gn, \"gn\", B * C * H * W, acc);\n    printf(\"\\nChecking perm1\\n\");\n    validate_result(acts.perm1, debug_states.perm1, \"perm1\", B * T * C, acc);\n    // don't check qkv because it is used as a buffer in attention step so values are wrong\n    //printf(\"\\nChecking qkv\\n\");\n    //validate_result(acts.qkv1, debug_states.qkv, \"qkv\", B * C3 * H * W, acc);\n    printf(\"\\nChecking att\\n\");\n    validate_result(acts.att_out, debug_states.att, \"att_out\", B * T * C, acc);\n    printf(\"\\nChecking proj\\n\");\n    validate_result(acts.proj, debug_states.proj, \"proj\", B * T * C, acc);\n    printf(\"\\nChecking out\\n\");\n    validate_result(acts.add, debug_states.out, \"out\", B * C * H * W, acc);\n\n    printf(\"\\nForward successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts, &back_acts, &grads);\n\n    printf(\"\\nChecking dproj_w\\n\");\n    validate_result(grads.proj_w, grads_cpu.proj_w, \"proj_w\", C * C, acc);\n    printf(\"\\nChecking dproj_b\\n\");\n    validate_result(grads.proj_b, grads_cpu.proj_b, \"proj_b\", C, acc);\n    printf(\"\\nChecking dqkv_w\\n\");\n    validate_result(grads.qkv_w, grads_cpu.qkv_w, \"qkv_w\", 3 * C * C, acc);\n    printf(\"\\nChecking dqkv_b\\n\");\n    validate_result(grads.qkv_b, grads_cpu.qkv_b, \"qkv_b\", 3 * C, acc);\n    printf(\"\\nChecking dgn_w\\n\");\n    validate_result(grads.gn_w, grads_cpu.gn_w, \"gn_w\", C, acc);\n    printf(\"\\nChecking dgn_b\\n\");\n    validate_result(grads.gn_b, grads_cpu.gn_b, \"gn_b\", C, acc);\n    printf(\"\\nChecking dinp\\n\");\n    validate_result(back_acts.dinp, debug_states.dinp, \"dinp\", B * C * H * W, acc);\n\n    printf(\"\\nAll results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmark\\n\");\n    int repeat_times = 100;\n    float elapsed_time = benchmark_kernel(\n        repeat_times, attention_block_forward,\n        cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts\n    );\n    printf(\"forward pass | time %.4f ms\\n\", elapsed_time);\n\n    printf(\"Backward pass benchmark\\n\");\n    elapsed_time = benchmark_kernel(\n        repeat_times, attention_block_backward,\n        cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts, &back_acts, &grads\n    );\n    printf(\"backward pass | time %.4f ms\\n\", elapsed_time);\n\n\n    cublasCheck(cublasDestroy(cublas_handle));\n    // free memory\n    cudaCheck(cudaFree(params_memory_gpu));\n    cudaCheck(cudaFree(acts_memory_gpu));\n    cudaCheck(cudaFree(back_acts_memory_gpu));\n    cudaCheck(cudaFree(grads_memory_gpu));\n    cudaCheck(cudaFree(acts.input));\n    free(params_memory_cpu);\n    free(debug_states_memory_cpu);\n    free(grads_memory_cpu);\n\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"attention.cuh\"\n#include \"groupnorm.cuh\"\n#include \"linear.cuh\"\n#include \"add.cuh\"\n#include \"attention_block.cuh\"\n\n\n__global__ void attention_permute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, C, H, W to B, HxW, C\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[b * C * img_size + h * C * W + w * C + c] = inp[idx];\n}\n\nvoid attention_permute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_permute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n__global__ void attention_unpermute_kernel(\n    const float* inp, float* out,\n    int B, int C, int H, int W\n) {\n    // from B, HxW, C to B, C, H, W\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (idx >= B * C * img_size) { return; }\n\n    int b = idx / (img_size * C);\n    int c = idx / img_size % C;\n    int h = idx / W % H;\n    int w = idx % W;\n\n    out[idx] = inp[b * C * img_size + h * C * W + w * C + c];\n}\n\nvoid attention_unpermute(\n    const float* inp, float* out,\n    int B, int C, int H, int W, int block_size\n) {\n    int num_threads = B * C * H * W;\n    attention_unpermute_kernel<<<ceil_div(num_threads, block_size), block_size>>>(inp, out, B, C, H, W);\n}\n\n\nvoid attention_block_forward\n(\n    hipblasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts\n) {\n    // apply groupnorm\n    groupnorm_forward(acts->input, params->gn_w, params->gn_b, acts->gn, acts->gn_mean, acts->gn_rstd, B, C, H, W, gn_n_groups);\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(acts->gn, acts->perm1, B, C, H, W, block_size);\n    // get qkv\n    matmul_forward2(cublas_handle, acts->qkv1, acts->perm1, params->qkv_w, params->qkv_b, B * H * W, C, 3 * C, block_size);\n    // apply attention\n    attention_forward1(cublas_handle, acts->att_out, acts->qkv2, acts->preatt, acts->att, acts->qkv1, B, H * W, C, C / HS, block_size);\n    // attention projection\n    matmul_forward2(cublas_handle, acts->proj, acts->att_out, params->proj_w, params->proj_b, B * H * W, C, C, block_size);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(acts->proj, acts->perm2, B, C, H, W, block_size);\n    // add back residual stream\n    add_forward(acts->input, acts->perm2, acts->add, B * C * H * W, block_size);\n}\n\n\nvoid attention_block_backward\n(\n    hipblasHandle_t cublas_handle,\n    int B, int C, int H, int W, int HS, int gn_n_groups, int block_size,\n    AttentionParams* params, AttentionActs* acts,\n    AttentionBackwardActs* back_acts, AttentionParams* grads\n) {\n    // permute from B, C, H, W to B, HxW, C\n    attention_permute(back_acts->dout, back_acts->buf1_BCHW, B, C, H, W, block_size);\n    // backward through projection\n    matmul_backward1(cublas_handle, back_acts->buf2_BCHW, grads->proj_w, grads->proj_b, back_acts->buf1_BCHW, acts->att_out, params->proj_w, B * H * W, C, C);\n    // backward through attention\n    attention_backward(cublas_handle, back_acts->buf_B3CHW, back_acts->dqkvr, back_acts->dpreatt, back_acts->datt, back_acts->buf1_BCHW, back_acts->buf2_BCHW, acts->qkv2, acts->att, B, H * W, C, C / HS);\n    // backward through qkv\n    matmul_backward1(cublas_handle, back_acts->buf1_BCHW, grads->qkv_w, grads->qkv_b, back_acts->buf_B3CHW, acts->perm1, params->qkv_w, B * H * W, C, 3 * C);\n    // permute from B, HxW, C to B, C, H, W\n    attention_unpermute(back_acts->buf1_BCHW, back_acts->buf2_BCHW, B, C, H, W, block_size);\n    // backward through groupnorm\n    groupnorm_backward(back_acts->buf2_BCHW, acts->input, acts->gn_mean, acts->gn_rstd, params->gn_w, back_acts->buf1_BCHW, grads->gn_w, grads->gn_b, B, C, H, W, gn_n_groups);\n    // add residual gradient\n    add_forward(back_acts->buf1_BCHW, back_acts->dout, back_acts->dinp, B * C * H * W, block_size);\n}\n\n\nvoid set_attention_params_pointers(\n    AttentionParams* params,\n    float* params_memory\n) {\n    float** ptrs[] = {\n        &params->gn_w,\n        &params->gn_b,\n        &params->qkv_w,\n        &params->qkv_b,\n        &params->proj_w,\n        &params->proj_b\n    };\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        *(ptrs[i]) = params_memory;\n        params_memory += params->param_sizes[i];\n    }\n}\n\nvoid set_attention_acts_pointers(\n    AttentionActs* acts,\n    float* acts_memory\n) {\n    float** ptrs[] = {\n        &acts->gn,\n        &acts->gn_mean,\n        &acts->gn_rstd,\n        &acts->perm1,\n        &acts->qkv1,\n        &acts->qkv2,\n        &acts->preatt,\n        &acts->att,\n        &acts->att_out,\n        &acts->proj,\n        &acts->perm2,\n        &acts->add\n    };\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        *(ptrs[i]) = acts_memory;\n        acts_memory += acts->act_sizes[i];\n    }\n}\n\nvoid set_attention_back_acts_pointers(\n    AttentionBackwardActs* back_acts,\n    float* back_acts_memory,\n    size_t* back_act_sizes\n) {\n    float** ptrs[] = {\n        &back_acts->buf1_BCHW,\n        &back_acts->buf2_BCHW,\n        &back_acts->buf_B3CHW,\n        &back_acts->dqkvr,\n        &back_acts->dpreatt,\n        &back_acts->datt,\n        &back_acts->dout,\n    };\n    for (int i = 0; i < NUM_ATT_BACKWARD_ACTS_TENSORS; i++) {\n        *(ptrs[i]) = back_acts_memory;\n        back_acts_memory += back_act_sizes[i];\n    }\n}\n\n\nvoid attention_block_count_backs(\n    AttentionBackwardActs* back_acts,\n    int B, int C, int H, int W, int HS, int gn_n_groups\n){\n    int NH = C / HS;\n    int C3 = 3 * C;\n    int T = H * W;\n\n    // count back acts\n    size_t *back_act_sizes = back_acts->back_sizes;\n    back_act_sizes[0] = B * C * H * W; // buf1_BCHW\n    back_act_sizes[1] = B * C * H * W; // buf2_BCHW\n    back_act_sizes[2] = B * C3 * H * W; // buf_B3CHW\n    back_act_sizes[3] = B * T * C3; // dqkvr\n    back_act_sizes[4] = B * NH * T * T; // dpreatt\n    back_act_sizes[5] = B * NH * T * T; // datt\n    back_act_sizes[6] = B * C * H * W; // dout\n    \n    size_t num_back_act_params = 0;\n    for (int i = 0; i < NUM_ATT_BACKWARD_ACTS_TENSORS; i++) {\n        num_back_act_params += back_act_sizes[i];\n    }\n    back_acts->n_backs = num_back_act_params;\n}\n\nvoid attention_block_count_params(AttentionParams* params, int C) {\n    size_t *param_sizes = params->param_sizes;\n    param_sizes[0] = C; // gn_w\n    param_sizes[1] = C; // gn_b\n    param_sizes[2] = 3 * C * C; // qkv_w\n    param_sizes[3] = 3 * C; // qkv_b\n    param_sizes[4] = C * C; // proj_w\n    param_sizes[5] = C; // proj_b\n\n    size_t num_parameters = 0;\n    for (int i = 0; i < NUM_ATT_PARAM_TENSORS; i++) {\n        num_parameters += param_sizes[i];\n    }\n    params->n_params = num_parameters;\n}\nvoid attention_block_count_acts(\n    AttentionActs* acts,\n    int B, int C, int H, int W, int gn_n_groups, int HS\n) {\n    int C3 = 3 * C;\n    int T = H * W;\n    int NH = C / HS;\n    size_t *act_sizes = acts->act_sizes;\n    act_sizes[0] = B * C * H * W; // gn\n    act_sizes[1] = B * gn_n_groups; // gn_mean\n    act_sizes[2] = B * gn_n_groups; // gn_rstd\n    act_sizes[3] = B * T * C; // perm1\n    act_sizes[4] = B * T * C3; // qkv1\n    act_sizes[5] = B * T * C3; // qkv2\n    act_sizes[6] = B * NH * T * T; // preatt\n    act_sizes[7] = B * NH * T * T; // att\n    act_sizes[8] = B * T * C; // att_out\n    act_sizes[9] = B * T * C; // proj\n    act_sizes[10] = B * C * H * W; // perm2\n    act_sizes[11] = B * C * H * W; // add\n\n    size_t num_acts_params = 0;\n    for (int i = 0; i < NUM_ATT_ACT_TENSORS; i++) {\n        num_acts_params += act_sizes[i];\n    }\n    acts->n_acts = num_acts_params;\n}\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    // read header\n    FILE *model_file = fopenCheck(\"attention_block_params.bin\", \"rb\");\n    int model_header[256];\n    freadCheck(model_header, sizeof(int), 256, model_file);\n    if (model_header[0] != 12345678) {\n        fprintf(stderr, \"Invalid magic model file, magic number %d\\n\", model_header[0]);\n        exit(1);\n    }\n    int B = model_header[1];\n    int C = model_header[2];\n    int H = model_header[3];\n    int W = model_header[4];\n    int HS = model_header[5];\n    int gn_n_groups = model_header[6];\n\n    AttentionParams params, grads, params_cpu;\n    attention_block_count_params(&params, C);\n    attention_block_count_params(&grads, C);\n    attention_block_count_params(&params_cpu, C);\n\n    size_t num_parameters = params.n_params;\n    // read params to host\n    float* params_memory_cpu = (float*)mallocCheck(num_parameters * sizeof(float));\n    freadCheck(params_memory_cpu, sizeof(float), num_parameters, model_file);\n    fcloseCheck(model_file);\n\n    // copy params to device\n    float* params_memory_gpu;\n    cudaCheck(hipMalloc(&params_memory_gpu, num_parameters * sizeof(float)));\n    cudaCheck(hipMemcpy(params_memory_gpu, params_memory_cpu, num_parameters * sizeof(float), hipMemcpyHostToDevice));\n\n    // create device memory for grads\n    float* grads_memory_gpu;\n    cudaCheck(hipMalloc(&grads_memory_gpu, num_parameters * sizeof(float)));\n    cudaCheck(hipMemset(grads_memory_gpu, 0, num_parameters * sizeof(float)));\n\n    // set param pointers\n    set_attention_params_pointers(&params, params_memory_gpu);\n    set_attention_params_pointers(&grads, grads_memory_gpu);\n    set_attention_params_pointers(&params_cpu, params_memory_cpu);\n\n    // calculate activation sizes\n    int C3 = 3 * C;\n    int T = H * W;\n    AttentionActs acts;\n    attention_block_count_acts(&acts, B, C, H, W, gn_n_groups, HS);\n    size_t num_acts_params = acts.n_acts;\n\n    // allocate device act memory\n    float* acts_memory_gpu;\n    cudaCheck(hipMalloc(&acts_memory_gpu, num_acts_params * sizeof(float)));\n    cudaCheck(hipMemset(acts_memory_gpu, 0, num_acts_params * sizeof(float)));\n\n    // set acts pointers\n    set_attention_acts_pointers(&acts, acts_memory_gpu);\n\n    // malloc input, which is not part of the large block of acts memory\n    cudaCheck(hipMalloc(&acts.input, B * C * H * W * sizeof(float)));\n\n    // create debug states\n    size_t debug_state_sizes[NUM_ATT_DEBUG_STATES];\n    debug_state_sizes[0] = B * C * H * W; // inp\n    debug_state_sizes[1] = B * C * H * W; // gn\n    debug_state_sizes[2] = B * C * H * W; // perm1\n    debug_state_sizes[3] = B * C3 * H * W; // qkv\n    debug_state_sizes[4] = B * C * H * W; // att\n    debug_state_sizes[5] = B * C * H * W; // proj\n    debug_state_sizes[6] = B * C * H * W; // out\n    debug_state_sizes[7] = B * C * H * W; // dout\n    debug_state_sizes[8] = B * C * H * W; // dinp\n\n    size_t num_debug_states = 0;\n    for (int i = 0; i < NUM_ATT_DEBUG_STATES; i++) {\n        num_debug_states += debug_state_sizes[i];\n    }\n\n    // allocate host debug state memory and host grads memory\n    float* debug_states_memory_cpu = (float*)mallocCheck(num_debug_states * sizeof(float));\n    float* grads_memory_cpu = (float*)mallocCheck(num_parameters * sizeof(float));\n    AttentionParams grads_cpu;\n    attention_block_count_params(&grads_cpu, C);\n    set_attention_params_pointers(&grads_cpu, grads_memory_cpu);\n    FILE *debug_states_file = fopenCheck(\"attention_block_states.bin\", \"rb\");\n    printf(\"num debug states: %ld\\n\", num_debug_states);\n    freadCheck(debug_states_memory_cpu, sizeof(float), num_debug_states, debug_states_file);\n    freadCheck(grads_memory_cpu, sizeof(float), num_parameters, debug_states_file);\n    fcloseCheck(debug_states_file);\n\n    // set debug states pointers\n    AttentionDebugStates debug_states;\n    float* debug_states_ptr = debug_states_memory_cpu;\n    float** debug_states_ptrs[] = {\n        &debug_states.inp,\n        &debug_states.gn,\n        &debug_states.perm1,\n        &debug_states.qkv,\n        &debug_states.att,\n        &debug_states.proj,\n        &debug_states.out,\n        &debug_states.dout,\n        &debug_states.dinp\n    };\n    for (int i = 0; i < NUM_ATT_DEBUG_STATES; i++) {\n        *(debug_states_ptrs[i]) = debug_states_ptr;\n        debug_states_ptr += debug_state_sizes[i];\n    }\n\n    // create attention backward acts\n    AttentionBackwardActs back_acts;\n    attention_block_count_backs(&back_acts, B, C, H, W, HS, gn_n_groups);\n    size_t num_back_act_params = back_acts.n_backs;\n\n    float* back_acts_memory_gpu;\n    cudaCheck(hipMalloc(&back_acts_memory_gpu, num_back_act_params * sizeof(float)));\n    cudaCheck(hipMemset(back_acts_memory_gpu, 0, num_back_act_params * sizeof(float)));\n\n    // set pointers\n    set_attention_back_acts_pointers(&back_acts, back_acts_memory_gpu, back_acts.back_sizes);\n    back_acts.dinp = acts.input;\n\n    // copy input and dout to device\n    cudaCheck(hipMemcpy(acts.input, debug_states.inp, B * C * H * W * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(back_acts.dout, debug_states.dout, B * C * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n    int block_size = 512;\n\n    printf(\"Checking forward pass\\n\");\n    attention_block_forward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts);\n\n    // validate results\n    float acc = 1e-4;\n    printf(\"\\nChecking gn\\n\");\n    validate_result(acts.gn, debug_states.gn, \"gn\", B * C * H * W, acc);\n    printf(\"\\nChecking perm1\\n\");\n    validate_result(acts.perm1, debug_states.perm1, \"perm1\", B * T * C, acc);\n    // don't check qkv because it is used as a buffer in attention step so values are wrong\n    //printf(\"\\nChecking qkv\\n\");\n    //validate_result(acts.qkv1, debug_states.qkv, \"qkv\", B * C3 * H * W, acc);\n    printf(\"\\nChecking att\\n\");\n    validate_result(acts.att_out, debug_states.att, \"att_out\", B * T * C, acc);\n    printf(\"\\nChecking proj\\n\");\n    validate_result(acts.proj, debug_states.proj, \"proj\", B * T * C, acc);\n    printf(\"\\nChecking out\\n\");\n    validate_result(acts.add, debug_states.out, \"out\", B * C * H * W, acc);\n\n    printf(\"\\nForward successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    attention_block_backward(cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts, &back_acts, &grads);\n\n    printf(\"\\nChecking dproj_w\\n\");\n    validate_result(grads.proj_w, grads_cpu.proj_w, \"proj_w\", C * C, acc);\n    printf(\"\\nChecking dproj_b\\n\");\n    validate_result(grads.proj_b, grads_cpu.proj_b, \"proj_b\", C, acc);\n    printf(\"\\nChecking dqkv_w\\n\");\n    validate_result(grads.qkv_w, grads_cpu.qkv_w, \"qkv_w\", 3 * C * C, acc);\n    printf(\"\\nChecking dqkv_b\\n\");\n    validate_result(grads.qkv_b, grads_cpu.qkv_b, \"qkv_b\", 3 * C, acc);\n    printf(\"\\nChecking dgn_w\\n\");\n    validate_result(grads.gn_w, grads_cpu.gn_w, \"gn_w\", C, acc);\n    printf(\"\\nChecking dgn_b\\n\");\n    validate_result(grads.gn_b, grads_cpu.gn_b, \"gn_b\", C, acc);\n    printf(\"\\nChecking dinp\\n\");\n    validate_result(back_acts.dinp, debug_states.dinp, \"dinp\", B * C * H * W, acc);\n\n    printf(\"\\nAll results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmark\\n\");\n    int repeat_times = 100;\n    float elapsed_time = benchmark_kernel(\n        repeat_times, attention_block_forward,\n        cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts\n    );\n    printf(\"forward pass | time %.4f ms\\n\", elapsed_time);\n\n    printf(\"Backward pass benchmark\\n\");\n    elapsed_time = benchmark_kernel(\n        repeat_times, attention_block_backward,\n        cublas_handle, B, C, H, W, HS, gn_n_groups, block_size, &params, &acts, &back_acts, &grads\n    );\n    printf(\"backward pass | time %.4f ms\\n\", elapsed_time);\n\n\n    cublasCheck(hipblasDestroy(cublas_handle));\n    // free memory\n    cudaCheck(hipFree(params_memory_gpu));\n    cudaCheck(hipFree(acts_memory_gpu));\n    cudaCheck(hipFree(back_acts_memory_gpu));\n    cudaCheck(hipFree(grads_memory_gpu));\n    cudaCheck(hipFree(acts.input));\n    free(params_memory_cpu);\n    free(debug_states_memory_cpu);\n    free(grads_memory_cpu);\n\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/attention_block.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"conv2d_k1.cuh\"\n\n\n\n__global__ void permute_x_kernel(\n    const float* x_in,\n    float* x_out,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (flat_idx < B * C_in * H * W) {\n        int img_size = H * W;\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n\n        // load from adjacent entries in x_in to enable coalescing\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_out[out_ind] = x_in[flat_idx]; \n    }\n}\n\n\n__global__ void permute_out_add_bias_kernel(\n    const float* out_perm, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (flat_idx < B * C_out * H * W) {\n        int img_size = H * W;\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n\n        int out_ind = b * C_out * img_size + c * img_size + h * W + w;\n        float val = out_perm[flat_idx];\n        if (bias != NULL) {\n            val += bias[c];\n        }\n        out[out_ind] = val;\n    }\n}\n\n\n// kernel that uses cuBLAS to do the 1D conv, then adds bias by hand\nvoid conv2d_k1_forward1(\n    cublasHandle_t cublas_handle,\n    float* out,\n    const float* x, const float* weight, const float* bias,\n    int B, int C_in, int H, int W, int C_out,\n    const int block_size,\n    float* t1, float* t2, float* t3\n) {\n    // x shape (B, C_in, H, W)\n    // weight (C_out, C_in, 1, 1), which is essentially (C_out, C_in)\n    // bias (C_out,)\n    // out (B, C_out, H, W)\n\n    // cublas stores matrices in column-major order\n    // so plan is as follows:\n    // 1. permute x to shape (C_in, B, H, W)\n    // 2. do x @ weight -> out with cublas, where out ends up with shape (C_out, B, H, W)\n    // 3. permute out to shape (B, C_out, H, W), and add bias at the same time.\n\n    // 1. allocate x_perm and permute x\n    cudaEvent_t start, end1, end2, end3;\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end1));\n        cudaCheck(cudaEventCreate(&end2));\n        cudaCheck(cudaEventCreate(&end3));\n        cudaCheck(cudaEventRecord(start, nullptr));\n    }\n    float *x_perm, *out_perm;\n    cudaCheck(cudaMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&out_perm, C_out * B * H * W * sizeof(float)));\n    int total_threads1 = B * C_in * H * W;\n    int num_blocks1 = ceil_div(total_threads1, block_size);\n    permute_x_kernel<<<num_blocks1, block_size>>>(x, x_perm, B, C_in, H, W);\n    if (t1 != nullptr) {\n        cudaCheck(cudaEventRecord(end1, nullptr));\n    }\n\n    // 2. x @ weight with cublas\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, non_C_dim, C_out, C_in, &alpha, x_perm, non_C_dim, weight, C_in, &beta, out_perm, non_C_dim));\n    if (t2 != nullptr) {\n        cudaCheck(cudaEventRecord(end2, nullptr));\n    }\n\n    // 3. permute back and add bias\n    int total_threads2 = B * C_out * H * W;\n    int num_blocks2 = ceil_div(total_threads2, block_size);\n    permute_out_add_bias_kernel<<<num_blocks2, block_size>>>(out_perm, bias, out, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(cudaFree(x_perm));\n    cudaCheck(cudaFree(out_perm));\n    if (t3 != nullptr) {\n        cudaCheck(cudaEventRecord(end3, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end1));\n        cudaCheck(cudaEventSynchronize(end2));\n        cudaCheck(cudaEventSynchronize(end3));\n        float elapsed_time1, elapsed_time2, elapsed_time3;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time1, start, end1));\n        cudaCheck(cudaEventElapsedTime(&elapsed_time2, end1, end2));\n        cudaCheck(cudaEventElapsedTime(&elapsed_time3, end2, end3));\n        *t1 += elapsed_time1;\n        *t2 += elapsed_time2;\n        *t3 += elapsed_time3;\n    }\n}\n\ntemplate <const int BO, const int BH, const int TO, const int TH>\n__global__ void conv2d_k1_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int H, int W, int O\n) {\n    // constants\n    const int BC = 16;\n    assert(O % BO == 0);\n    assert(C % BC == 0);\n    assert(H * W % BH == 0);\n    // n_threads per block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int block_col = blockIdx.x % blocks_per_col;\n    const int batch = blockIdx.x / blocks_per_col;\n\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for weight and x\n    const int weight_s_sz = BO * BC;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[weight_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // local registers for results;\n    float thread_results[TO * TH] = {0.0};\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // get offsets to x and weight\n    int x_block_offset = batch * C * H * W + block_col * BH;\n    int w_block_offset = block_row * BO * C;\n\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx;\n        // load weights to smem\n        for (int i = threadIdx.x; i < weight_s_sz; i += blockDim.x) {\n            int w_i = i / BC;\n            int w_j = i % BC;\n            int w_abs_idx = w_block_offset + w_inner_offset + w_i * C + w_j;\n            w_s[w_i * BC + w_j] = weight[w_abs_idx];\n        }\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            int x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            x_s[x_i * BH + x_j] = x[x_abs_idx];\n        }\n        __syncthreads();\n\n\n        // calculate thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            for (int i = 0; i < TO; i++) {\n                reg_w[i] = w_s[(thread_row * TO + i) * BC + dot_idx];\n            }\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int i = 0; i < TO; i++) {\n                for (int j = 0; j < TH; j++) {\n                    thread_results[i * TH + j] += reg_w[i] * reg_x[j];\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // set out and bias directly to thread offset\n    int out_block_offset = batch * O * H * W + block_row * BO * H * W + block_col * BH;\n    int bias_offset = block_row * BO;\n    for (int i = 0; i < TO; i++) {\n        for (int j = 0; j < TH; j++) {\n            int bias_idx = bias_offset + thread_row * TO + i;\n            float out_val = thread_results[i * TH + j] + bias[bias_idx];\n            int out_idx = out_block_offset + thread_row * TO * H * W + thread_col * TH + i * H * W + j;\n            atomicAdd(out + out_idx, out_val);\n        }\n    }\n}\n\nvoid conv2d_k1_forward2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int H, int W, int C_out\n) {\n    cudaCheck(cudaMemset(out, 0, B * C_out * H * W * sizeof(float)));\n    const int BO = 64;\n    const int BH = 64;\n    const int TO = 4;\n    const int TH = 4;\n    dim3 gridDim(B * H * W / BH, C_out / BO);\n    dim3 blockDim((BO * BH) / (TO * TH));\n    conv2d_k1_forward_kernel<BO, BH, TO, TH><<<gridDim, blockDim>>>(x, weight, bias, out, B, C_in, H, W, C_out);\n}\n\n\n\n// --------------------------------------------------------------------------------\n// backward pass\n\n\n__global__ void permute_dout_x_kernel(\n    const float* x, const float* dout,\n    float* x_perm, float* dout_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    int h = (flat_idx / W) % H;\n    int w = flat_idx % W;\n\n    if (flat_idx < B * C_in * H * W) {\n        // permute x\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_perm[out_ind] = x[flat_idx];\n    }\n\n    if (flat_idx < B * C_out * H * W) {\n        // permute dout\n        int b = flat_idx / (C_out * img_size);\n        int c = (flat_idx / img_size) % C_out;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        dout_perm[out_ind] = dout[flat_idx];\n    }\n}\n\n\n__global__ void permute_dx_kernel(\n    const float* dx_perm,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx < B * C_in * img_size) {\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n        int out_ind = b * C_in * img_size + c * img_size + h * W + w;\n        dx[out_ind] = dx_perm[flat_idx];\n    }\n}\n\n\n// we want to sum the B, H, W dimensions of dout\n// since dout_perm is shaped as (C_out, B, H, W)\n// we will have each block take care of one entry of C_out\n// and the threads in each block will use coorperative_groups to do the sum\n__global__ void dbias_kernel(\n    const float* dout_perm, // shape (C_out, B, H, W)\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    extern __shared__ float shared[];\n\n    // move dout_perm to the C_out index\n    if (threadIdx.x < bhw) {\n        dout_perm += blockIdx.x * B * H * W;\n        float th_sum = 0.0f;\n        for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n            th_sum += dout_perm[i];\n        }\n        shared[threadIdx.x] = th_sum;\n    }\n    __syncthreads();\n\n    // reduce the shared memory\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            shared[threadIdx.x] += shared[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = shared[0];\n    }\n}\n\n\n// uses cublas\nvoid conv2d_k1_backward1(\n    cublasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // shapes:\n    // x, dx: (B, C_in, H, W)\n    // weight: (C_out, C_in)\n    // bias: (C_out,)\n    // dout: (B, C_out, H, W)\n\n    // plan (the shapes are raw array shapes, note that cublas interprets the arrays as transposed):\n    // 1. transpose x to x_perm of shape (C_in, B*H*W), and dout to dout_perm of shape (C_out, B*H*W)\n    // 2. do one sgemm call to get dw = x.T @ dout_perm, and another call to get dx_perm = dout @ w.T\n    // 3. dx_perm is of shape (C_in, B*H*W), so call a kernel to permute it\n    // 4. reduce to get dbias\n    float *x_perm, *dout_perm, *dx_perm;\n    cudaCheck(cudaMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&dout_perm, C_out * B * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&dx_perm, C_in * B * H * W * sizeof(float)));\n\n    // 1. permute x and dout\n    int total_th1 = (C_in > C_out) ? C_in * B * H * W : C_out * B * H * W;\n    int n_blk1 = ceil_div(total_th1, block_size);\n    permute_dout_x_kernel<<<n_blk1, block_size>>>(x, dout, x_perm, dout_perm, B, C_in, C_out, H, W);\n\n    // 2.a sgemm call for dw = x_perm.T @ dout_perm\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, C_in, C_out, non_C_dim, &alpha, x_perm, non_C_dim, dout_perm, non_C_dim, &beta, dweight, C_in));\n\n    // 2.b sgemm call for dx_perm = dout_perm @ weight.T\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, non_C_dim, C_in, C_out, &alpha, dout_perm, non_C_dim, weight, C_in, &beta, dx_perm, non_C_dim));\n\n    // 3. permute dx_perm from (C_out, B, H, W) to (B, C_out, H, W)\n    int total_th2 = C_in * B * H * W;\n    int n_blk2 = ceil_div(total_th2, block_size);\n    permute_dx_kernel<<<n_blk2, block_size>>>(dx_perm, dx, B, C_in, H, W);\n\n    // 4. reduce dout to get dbias\n    dbias_kernel<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(cudaFree(x_perm));\n    cudaCheck(cudaFree(dout_perm));\n    cudaCheck(cudaFree(dx_perm));\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 32;\n    int C_in = 64;\n    int C_out = 128;\n    int H = 64;\n    int W = 64;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* weight = (float*)malloc(C_out * C_in * sizeof(float));\n    float* bias = (float*)malloc(C_out * sizeof(float));\n    float* out = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dx = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* dweight = (float*)malloc(C_out * C_in * sizeof(float));\n    float* dbias = (float*)malloc(C_out * sizeof(float));\n    \n    // read saved output\n    FILE *file = fopen(\"conv2d_k1.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C_in * H * W, file);\n    freadCheck(weight, sizeof(float), C_out * C_in, file);\n    freadCheck(bias, sizeof(float), C_out, file);\n    freadCheck(out, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dout, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dx, sizeof(float), B * C_in * H * W, file);\n    freadCheck(dweight, sizeof(float), C_out * C_in, file);\n    freadCheck(dbias, sizeof(float), C_out, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(cudaMalloc(&d_x, B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_weight, C_out * C_in * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_bias, C_out * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * C_out * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * C_out * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, B * C_in * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dweight, C_out * C_in * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dbias, C_out * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, B * C_in * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_weight, weight, C_out * C_in * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_bias, bias, C_out * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * C_out * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n    //int block_sizes[] = {128, 256, 512, 1024};\n    int block_sizes[] = {512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        printf(\"Checking block size %d\\n\", block_sizes[j]);\n        int block_size = block_sizes[j];\n        conv2d_k1_forward1(cublas_handle, d_out, d_x, d_weight, d_bias, B, C_in, H, W, C_out, block_size);\n        validate_result(d_out, out, \"out\", B * C_out * H * W);\n    }\n    printf(\"\\nChecking new kernel\\n\");\n    conv2d_k1_forward2(d_x, d_weight, d_bias, d_out, B, C_in, H, W, C_out);\n    validate_result(d_out, out, \"new_out\", B * C_out * H * W);\n    printf(\"Running our own forward benchmark for new kernel\\n\");\n    void* flush_buffer;\n    cudaCheck(cudaMalloc(&flush_buffer, deviceProp.l2CacheSize));\n    int repeat_times = 1000;\n    float new_time = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        cudaEvent_t start, end;\n        cudaCheck(cudaEventCreate(&start));\n        cudaCheck(cudaEventCreate(&end));\n        cudaCheck(cudaEventRecord(start, nullptr));\n        conv2d_k1_forward2(d_x, d_weight, d_bias, d_out, B, C_in, H, W, C_out);\n        cudaCheck(cudaEventRecord(end, nullptr));\n        cudaCheck(cudaEventSynchronize(start));\n        cudaCheck(cudaEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(cudaEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n    \n    printf(\"Forward pass successful\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    float accuracy = 1e-1;\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        conv2d_k1_backward1(cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W, block_size);\n        printf(\"\\nChecking dweight\\n\");\n        validate_result(d_dweight, dweight, \"dweight\", C_out * C_in, accuracy);\n        printf(\"\\nchecking dbias\\n\");\n        validate_result(d_dbias, dbias, \"dbias\", C_out, accuracy);\n        printf(\"\\nchecking dx\\n\");\n        validate_result(d_dx, dx, \"dx\", B * C_in * H * W);\n    }\n    printf(\"Backward pass successful\\n\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    //printf(\"Forward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k1_forward1,\n    //                                          cublas_handle, d_out, d_x, d_weight, d_bias,\n    //                                          B, C_in, H, W, C_out, block_size);\n\n    //    // napkin math: estimate the flops achieved\n    //    // e.g. A100 40GB PCIe is advertised at 19.5 TFLOPS fp32\n    //    float tflops = (float)B * H * W * C_in * C_out * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"Running our own forward benchmarks\\n\");\n    float t1 = 0.0f;\n    float t2 = 0.0f;\n    float t3 = 0.0f;\n    float t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(cudaMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k1_forward1(cublas_handle, d_out, d_x, d_weight, d_bias, B, C_in, H, W, C_out, 512, &t1, &t2, &t3);\n    }\n    cudaCheck(cudaFree(flush_buffer));\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t_total = t1 + t2 + t3;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, conv2d_k1_backward1,\n                                              cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias,\n                                              B, C_in, C_out, H, W, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n\n    // free memory\n    free(x);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dx);\n    free(dweight);\n    free(dbias);\n    cudaCheck(cudaFree(d_x));\n    cudaCheck(cudaFree(d_weight));\n    cudaCheck(cudaFree(d_bias));\n    cudaCheck(cudaFree(d_out));\n    cudaCheck(cudaFree(d_dout));\n    cudaCheck(cudaFree(d_dx));\n    cudaCheck(cudaFree(d_dweight));\n    cudaCheck(cudaFree(d_dbias));\n    cublasCheck(cublasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"conv2d_k1.cuh\"\n\n\n\n__global__ void permute_x_kernel(\n    const float* x_in,\n    float* x_out,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (flat_idx < B * C_in * H * W) {\n        int img_size = H * W;\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n\n        // load from adjacent entries in x_in to enable coalescing\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_out[out_ind] = x_in[flat_idx]; \n    }\n}\n\n\n__global__ void permute_out_add_bias_kernel(\n    const float* out_perm, const float* bias,\n    float* out,\n    int B, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (flat_idx < B * C_out * H * W) {\n        int img_size = H * W;\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n\n        int out_ind = b * C_out * img_size + c * img_size + h * W + w;\n        float val = out_perm[flat_idx];\n        if (bias != NULL) {\n            val += bias[c];\n        }\n        out[out_ind] = val;\n    }\n}\n\n\n// kernel that uses cuBLAS to do the 1D conv, then adds bias by hand\nvoid conv2d_k1_forward1(\n    hipblasHandle_t cublas_handle,\n    float* out,\n    const float* x, const float* weight, const float* bias,\n    int B, int C_in, int H, int W, int C_out,\n    const int block_size,\n    float* t1, float* t2, float* t3\n) {\n    // x shape (B, C_in, H, W)\n    // weight (C_out, C_in, 1, 1), which is essentially (C_out, C_in)\n    // bias (C_out,)\n    // out (B, C_out, H, W)\n\n    // cublas stores matrices in column-major order\n    // so plan is as follows:\n    // 1. permute x to shape (C_in, B, H, W)\n    // 2. do x @ weight -> out with cublas, where out ends up with shape (C_out, B, H, W)\n    // 3. permute out to shape (B, C_out, H, W), and add bias at the same time.\n\n    // 1. allocate x_perm and permute x\n    hipEvent_t start, end1, end2, end3;\n    if (t1 != nullptr) {\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end1));\n        cudaCheck(hipEventCreate(&end2));\n        cudaCheck(hipEventCreate(&end3));\n        cudaCheck(hipEventRecord(start, nullptr));\n    }\n    float *x_perm, *out_perm;\n    cudaCheck(hipMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&out_perm, C_out * B * H * W * sizeof(float)));\n    int total_threads1 = B * C_in * H * W;\n    int num_blocks1 = ceil_div(total_threads1, block_size);\n    permute_x_kernel<<<num_blocks1, block_size>>>(x, x_perm, B, C_in, H, W);\n    if (t1 != nullptr) {\n        cudaCheck(hipEventRecord(end1, nullptr));\n    }\n\n    // 2. x @ weight with cublas\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, non_C_dim, C_out, C_in, &alpha, x_perm, non_C_dim, weight, C_in, &beta, out_perm, non_C_dim));\n    if (t2 != nullptr) {\n        cudaCheck(hipEventRecord(end2, nullptr));\n    }\n\n    // 3. permute back and add bias\n    int total_threads2 = B * C_out * H * W;\n    int num_blocks2 = ceil_div(total_threads2, block_size);\n    permute_out_add_bias_kernel<<<num_blocks2, block_size>>>(out_perm, bias, out, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(hipFree(x_perm));\n    cudaCheck(hipFree(out_perm));\n    if (t3 != nullptr) {\n        cudaCheck(hipEventRecord(end3, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end1));\n        cudaCheck(hipEventSynchronize(end2));\n        cudaCheck(hipEventSynchronize(end3));\n        float elapsed_time1, elapsed_time2, elapsed_time3;\n        cudaCheck(hipEventElapsedTime(&elapsed_time1, start, end1));\n        cudaCheck(hipEventElapsedTime(&elapsed_time2, end1, end2));\n        cudaCheck(hipEventElapsedTime(&elapsed_time3, end2, end3));\n        *t1 += elapsed_time1;\n        *t2 += elapsed_time2;\n        *t3 += elapsed_time3;\n    }\n}\n\ntemplate <const int BO, const int BH, const int TO, const int TH>\n__global__ void conv2d_k1_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C, int H, int W, int O\n) {\n    // constants\n    const int BC = 16;\n    assert(O % BO == 0);\n    assert(C % BC == 0);\n    assert(H * W % BH == 0);\n    // n_threads per block = (BO * BH) / (TO * TH) = 256\n    // n_blocks = B * (O / BO) * (H * W / BH)\n    // set gridDim to (BxHxW / BH, O / BO)\n    const int block_row = blockIdx.y;\n    const int blocks_per_col = H * W / BH;\n    assert(gridDim.x == B * blocks_per_col);\n    const int block_col = blockIdx.x % blocks_per_col;\n    const int batch = blockIdx.x / blocks_per_col;\n\n    const int threads_per_col = BH / TH;\n    const int thread_col = threadIdx.x % threads_per_col;\n    const int thread_row = threadIdx.x / threads_per_col;\n\n    // shared mem for weight and x\n    const int weight_s_sz = BO * BC;\n    const int x_s_sz = BC * BH;\n    __shared__ float w_s[weight_s_sz];\n    __shared__ float x_s[x_s_sz];\n\n    // local registers for results;\n    float thread_results[TO * TH] = {0.0};\n    float reg_w[TO] = {0.0};\n    float reg_x[TH] = {0.0};\n\n    // get offsets to x and weight\n    int x_block_offset = batch * C * H * W + block_col * BH;\n    int w_block_offset = block_row * BO * C;\n\n    for (int c_idx = 0; c_idx < C; c_idx += BC) {\n        int x_inner_offset = c_idx * H * W;\n        int w_inner_offset = c_idx;\n        // load weights to smem\n        for (int i = threadIdx.x; i < weight_s_sz; i += blockDim.x) {\n            int w_i = i / BC;\n            int w_j = i % BC;\n            int w_abs_idx = w_block_offset + w_inner_offset + w_i * C + w_j;\n            w_s[w_i * BC + w_j] = weight[w_abs_idx];\n        }\n        for (int i = threadIdx.x; i < x_s_sz; i += blockDim.x) {\n            int x_i = i / BH;\n            int x_j = i % BH;\n            int x_abs_idx = x_block_offset + x_inner_offset + x_i * H * W + x_j;\n            x_s[x_i * BH + x_j] = x[x_abs_idx];\n        }\n        __syncthreads();\n\n\n        // calculate thread results\n        for (int dot_idx = 0; dot_idx < BC; dot_idx++) {\n            for (int i = 0; i < TO; i++) {\n                reg_w[i] = w_s[(thread_row * TO + i) * BC + dot_idx];\n            }\n            for (int j = 0; j < TH; j++) {\n                reg_x[j] = x_s[dot_idx * BH + thread_col * TH + j];\n            }\n            for (int i = 0; i < TO; i++) {\n                for (int j = 0; j < TH; j++) {\n                    thread_results[i * TH + j] += reg_w[i] * reg_x[j];\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    // write out results\n    // set out and bias directly to thread offset\n    int out_block_offset = batch * O * H * W + block_row * BO * H * W + block_col * BH;\n    int bias_offset = block_row * BO;\n    for (int i = 0; i < TO; i++) {\n        for (int j = 0; j < TH; j++) {\n            int bias_idx = bias_offset + thread_row * TO + i;\n            float out_val = thread_results[i * TH + j] + bias[bias_idx];\n            int out_idx = out_block_offset + thread_row * TO * H * W + thread_col * TH + i * H * W + j;\n            atomicAdd(out + out_idx, out_val);\n        }\n    }\n}\n\nvoid conv2d_k1_forward2(\n    const float* x, const float* weight, const float* bias,\n    float* out,\n    int B, int C_in, int H, int W, int C_out\n) {\n    cudaCheck(hipMemset(out, 0, B * C_out * H * W * sizeof(float)));\n    const int BO = 64;\n    const int BH = 64;\n    const int TO = 4;\n    const int TH = 4;\n    dim3 gridDim(B * H * W / BH, C_out / BO);\n    dim3 blockDim((BO * BH) / (TO * TH));\n    conv2d_k1_forward_kernel<BO, BH, TO, TH><<<gridDim, blockDim>>>(x, weight, bias, out, B, C_in, H, W, C_out);\n}\n\n\n\n// --------------------------------------------------------------------------------\n// backward pass\n\n\n__global__ void permute_dout_x_kernel(\n    const float* x, const float* dout,\n    float* x_perm, float* dout_perm,\n    int B, int C_in, int C_out, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    int h = (flat_idx / W) % H;\n    int w = flat_idx % W;\n\n    if (flat_idx < B * C_in * H * W) {\n        // permute x\n        int b = flat_idx / (C_in * img_size);\n        int c = (flat_idx / img_size) % C_in;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        x_perm[out_ind] = x[flat_idx];\n    }\n\n    if (flat_idx < B * C_out * H * W) {\n        // permute dout\n        int b = flat_idx / (C_out * img_size);\n        int c = (flat_idx / img_size) % C_out;\n\n        int out_ind = c * B * img_size + b * img_size + h * W + w;\n        dout_perm[out_ind] = dout[flat_idx];\n    }\n}\n\n\n__global__ void permute_dx_kernel(\n    const float* dx_perm,\n    float* dx,\n    int B, int C_in, int H, int W\n) {\n    int flat_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx < B * C_in * img_size) {\n        int c = flat_idx / (B * img_size);\n        int b = (flat_idx / img_size) % B;\n        int h = (flat_idx / W) % H;\n        int w = flat_idx % W;\n        int out_ind = b * C_in * img_size + c * img_size + h * W + w;\n        dx[out_ind] = dx_perm[flat_idx];\n    }\n}\n\n\n// we want to sum the B, H, W dimensions of dout\n// since dout_perm is shaped as (C_out, B, H, W)\n// we will have each block take care of one entry of C_out\n// and the threads in each block will use coorperative_groups to do the sum\n__global__ void dbias_kernel(\n    const float* dout_perm, // shape (C_out, B, H, W)\n    float* dbias,\n    int B, int C_out, int H, int W\n) {\n    int bhw = B * H * W;\n    extern __shared__ float shared[];\n\n    // move dout_perm to the C_out index\n    if (threadIdx.x < bhw) {\n        dout_perm += blockIdx.x * B * H * W;\n        float th_sum = 0.0f;\n        for (int i = threadIdx.x; i < bhw; i += blockDim.x) {\n            th_sum += dout_perm[i];\n        }\n        shared[threadIdx.x] = th_sum;\n    }\n    __syncthreads();\n\n    // reduce the shared memory\n    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {\n        if (threadIdx.x < stride) {\n            shared[threadIdx.x] += shared[threadIdx.x + stride];\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x == 0) {\n        dbias[blockIdx.x] = shared[0];\n    }\n}\n\n\n// uses cublas\nvoid conv2d_k1_backward1(\n    hipblasHandle_t cublas_handle,\n    const float* dout, const float* x, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C_in, int C_out, int H, int W,\n    int block_size\n) {\n    // shapes:\n    // x, dx: (B, C_in, H, W)\n    // weight: (C_out, C_in)\n    // bias: (C_out,)\n    // dout: (B, C_out, H, W)\n\n    // plan (the shapes are raw array shapes, note that cublas interprets the arrays as transposed):\n    // 1. transpose x to x_perm of shape (C_in, B*H*W), and dout to dout_perm of shape (C_out, B*H*W)\n    // 2. do one sgemm call to get dw = x.T @ dout_perm, and another call to get dx_perm = dout @ w.T\n    // 3. dx_perm is of shape (C_in, B*H*W), so call a kernel to permute it\n    // 4. reduce to get dbias\n    float *x_perm, *dout_perm, *dx_perm;\n    cudaCheck(hipMalloc(&x_perm, C_in * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&dout_perm, C_out * B * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&dx_perm, C_in * B * H * W * sizeof(float)));\n\n    // 1. permute x and dout\n    int total_th1 = (C_in > C_out) ? C_in * B * H * W : C_out * B * H * W;\n    int n_blk1 = ceil_div(total_th1, block_size);\n    permute_dout_x_kernel<<<n_blk1, block_size>>>(x, dout, x_perm, dout_perm, B, C_in, C_out, H, W);\n\n    // 2.a sgemm call for dw = x_perm.T @ dout_perm\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    int non_C_dim = B * H * W;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, C_in, C_out, non_C_dim, &alpha, x_perm, non_C_dim, dout_perm, non_C_dim, &beta, dweight, C_in));\n\n    // 2.b sgemm call for dx_perm = dout_perm @ weight.T\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, non_C_dim, C_in, C_out, &alpha, dout_perm, non_C_dim, weight, C_in, &beta, dx_perm, non_C_dim));\n\n    // 3. permute dx_perm from (C_out, B, H, W) to (B, C_out, H, W)\n    int total_th2 = C_in * B * H * W;\n    int n_blk2 = ceil_div(total_th2, block_size);\n    permute_dx_kernel<<<n_blk2, block_size>>>(dx_perm, dx, B, C_in, H, W);\n\n    // 4. reduce dout to get dbias\n    dbias_kernel<<<C_out, block_size, block_size * sizeof(float)>>>(dout_perm, dbias, B, C_out, H, W);\n\n    // free memory\n    cudaCheck(hipFree(x_perm));\n    cudaCheck(hipFree(dout_perm));\n    cudaCheck(hipFree(dx_perm));\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 32;\n    int C_in = 64;\n    int C_out = 128;\n    int H = 64;\n    int W = 64;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* weight = (float*)malloc(C_out * C_in * sizeof(float));\n    float* bias = (float*)malloc(C_out * sizeof(float));\n    float* out = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C_out * H * W * sizeof(float));\n    float* dx = (float*)malloc(B * C_in * H * W * sizeof(float));\n    float* dweight = (float*)malloc(C_out * C_in * sizeof(float));\n    float* dbias = (float*)malloc(C_out * sizeof(float));\n    \n    // read saved output\n    FILE *file = fopen(\"conv2d_k1.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C_in * H * W, file);\n    freadCheck(weight, sizeof(float), C_out * C_in, file);\n    freadCheck(bias, sizeof(float), C_out, file);\n    freadCheck(out, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dout, sizeof(float), B * C_out * H * W, file);\n    freadCheck(dx, sizeof(float), B * C_in * H * W, file);\n    freadCheck(dweight, sizeof(float), C_out * C_in, file);\n    freadCheck(dbias, sizeof(float), C_out, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(hipMalloc(&d_x, B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_weight, C_out * C_in * sizeof(float)));\n    cudaCheck(hipMalloc(&d_bias, C_out * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * C_out * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * C_out * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, B * C_in * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dweight, C_out * C_in * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dbias, C_out * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, B * C_in * H * W * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_weight, weight, C_out * C_in * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_bias, bias, C_out * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, B * C_out * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n    //int block_sizes[] = {128, 256, 512, 1024};\n    int block_sizes[] = {512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        printf(\"Checking block size %d\\n\", block_sizes[j]);\n        int block_size = block_sizes[j];\n        conv2d_k1_forward1(cublas_handle, d_out, d_x, d_weight, d_bias, B, C_in, H, W, C_out, block_size);\n        validate_result(d_out, out, \"out\", B * C_out * H * W);\n    }\n    printf(\"\\nChecking new kernel\\n\");\n    conv2d_k1_forward2(d_x, d_weight, d_bias, d_out, B, C_in, H, W, C_out);\n    validate_result(d_out, out, \"new_out\", B * C_out * H * W);\n    printf(\"Running our own forward benchmark for new kernel\\n\");\n    void* flush_buffer;\n    cudaCheck(hipMalloc(&flush_buffer, deviceProp.l2CacheSize));\n    int repeat_times = 1000;\n    float new_time = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        hipEvent_t start, end;\n        cudaCheck(hipEventCreate(&start));\n        cudaCheck(hipEventCreate(&end));\n        cudaCheck(hipEventRecord(start, nullptr));\n        conv2d_k1_forward2(d_x, d_weight, d_bias, d_out, B, C_in, H, W, C_out);\n        cudaCheck(hipEventRecord(end, nullptr));\n        cudaCheck(hipEventSynchronize(start));\n        cudaCheck(hipEventSynchronize(end));\n        float elapsed_time;\n        cudaCheck(hipEventElapsedTime(&elapsed_time, start, end));\n        new_time += elapsed_time;\n    }\n    new_time /= repeat_times;\n    printf(\"new kernel time: %.4f ms\\n\", new_time);\n    \n    printf(\"Forward pass successful\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    float accuracy = 1e-1;\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        conv2d_k1_backward1(cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias, B, C_in, C_out, H, W, block_size);\n        printf(\"\\nChecking dweight\\n\");\n        validate_result(d_dweight, dweight, \"dweight\", C_out * C_in, accuracy);\n        printf(\"\\nchecking dbias\\n\");\n        validate_result(d_dbias, dbias, \"dbias\", C_out, accuracy);\n        printf(\"\\nchecking dx\\n\");\n        validate_result(d_dx, dx, \"dx\", B * C_in * H * W);\n    }\n    printf(\"Backward pass successful\\n\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    //printf(\"Forward pass benchmarks:\\n\");\n    //for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n    //    int block_size = block_sizes[j];\n\n    //    int repeat_times = 100;\n    //    float elapsed_time = benchmark_kernel(repeat_times, conv2d_k1_forward1,\n    //                                          cublas_handle, d_out, d_x, d_weight, d_bias,\n    //                                          B, C_in, H, W, C_out, block_size);\n\n    //    // napkin math: estimate the flops achieved\n    //    // e.g. A100 40GB PCIe is advertised at 19.5 TFLOPS fp32\n    //    float tflops = (float)B * H * W * C_in * C_out * 2 / elapsed_time * 1e3f / 1e12f;\n    //    printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    //}\n    printf(\"Running our own forward benchmarks\\n\");\n    float t1 = 0.0f;\n    float t2 = 0.0f;\n    float t3 = 0.0f;\n    float t_total = 0.0f;\n    for (int i = 0; i < repeat_times; i++) {\n        // clear L2\n        cudaCheck(hipMemset(flush_buffer, 0, deviceProp.l2CacheSize));\n        conv2d_k1_forward1(cublas_handle, d_out, d_x, d_weight, d_bias, B, C_in, H, W, C_out, 512, &t1, &t2, &t3);\n    }\n    cudaCheck(hipFree(flush_buffer));\n    t1 /= repeat_times;\n    t2 /= repeat_times;\n    t3 /= repeat_times;\n    t_total = t1 + t2 + t3;\n\n    printf(\"t1: %.4f ms\\n\", t1);\n    printf(\"t2: %.4f ms\\n\", t2);\n    printf(\"t3: %.4f ms\\n\", t3);\n    printf(\"t_total: %.4f ms\\n\", t_total);\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, conv2d_k1_backward1,\n                                              cublas_handle, d_dout, d_x, d_weight, d_dx, d_dweight, d_dbias,\n                                              B, C_in, C_out, H, W, block_size);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n\n    // free memory\n    free(x);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dx);\n    free(dweight);\n    free(dbias);\n    cudaCheck(hipFree(d_x));\n    cudaCheck(hipFree(d_weight));\n    cudaCheck(hipFree(d_bias));\n    cudaCheck(hipFree(d_out));\n    cudaCheck(hipFree(d_dout));\n    cudaCheck(hipFree(d_dx));\n    cudaCheck(hipFree(d_dweight));\n    cudaCheck(hipFree(d_dbias));\n    cublasCheck(hipblasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/conv2d_k1.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"linear.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n// copy the matmul forward and backward kernels from llm.c\n\n// is there no better way other than just adding bias with a whole separate kernel?\n// this is a highly memory-bound operation, should be fused into the matmul kernel\n// but i can't seem to find a cuBLAS function that does this\n__global__ void add_bias(float* out, const float* bias, int N, int OC) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = idx; i < N * OC; i += stride) {\n        int col = i % OC;\n        out[i] += bias[col];\n    }\n}\n\n// kernel 2 calls cuBLAS, which should be very efficient\nvoid matmul_forward2(cublasHandle_t cublas_handle,\n                     float* out,\n                     const float* inp, const float* weight, const float* bias,\n                     int N, int C, int OC,\n                     const int block_size) {\n    // for reference API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n    // for us, inp is (B*T, C), weight is (OC, C), out is (B*T, OC)\n    // cuBLAS does C = alpha * A * B + beta * C\n    // where A is mxk, B is kxn, C is mxn\n    // now, because we use row-major storage, cuBLAS (which is column-major) sees our matrices transposed.\n    // algorithmically / in e.g. PyTorch we want to do: out = inp @ weight.T\n    // but because cuBLAS is column-major, we actually want to get it to calculate out.T . Mathematically, this is:\n    // out.T = weight @ inp.T\n    // but again, our variables look transposed, so using the actual weight/inp we have here in this function, this becomes\n    // out.T = weight.T @ inp\n    // so we need to get cuBLAS to calculate weight.T @ inp (the variables here are the actual ones in this function)\n    // => need to call cuBLAS with A = weight, B = inp\n    // => need to call cuBLAS with transa = CUBLAS_OP_T, transb = CUBLAS_OP_N\n\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, N, C, &alpha, weight, C, inp, C, &beta, out, OC));\n    // and now we still have to add the bias... (ew)\n    if (bias != NULL) {\n        int grid_size = ceil_div(OC * N, block_size);\n        add_bias<<<grid_size, block_size>>>(out, bias, N, OC);\n        cudaCheck(cudaGetLastError());\n    }\n}\n\n// use shared memory and coarsening + reductions\n__global__ void matmul_backward_bias_kernel_faster(float* dbias, const float* dout, int N, int OC) {\n    extern __shared__ float shared[];\n    int o = blockIdx.x; // range [0, OC)\n    int tid = threadIdx.x; // range [0, block_size)\n    int block_size = blockDim.x;\n    const float* x = dout + o;\n    // thread coarsening\n    double sum = 0.0;\n    for (int i = tid; i < N; i += block_size) {\n        sum += x[i * OC];\n    }\n    shared[tid] = (float) sum;\n    __syncthreads();\n    // reductions\n    for (int stride = block_size / 2; stride >= 1; stride /= 2) {\n        __syncthreads();\n        if (tid < stride) {\n            shared[tid] += shared[tid + stride];\n        }\n    }\n    // write the final result (at thread 0) to global memory\n    if (tid == 0) {\n        dbias[o] = shared[0];\n    }\n}\n\n// version1: simple cuBLAS calls\nvoid matmul_backward1(cublasHandle_t cublas_handle,\n                      float* dinp, float* dweight, float* dbias,\n                      float* dout, float* inp, float* weight,\n                      int N, int C, int OC) {\n    float alpha = 1.0f;\n    float beta = 0.0f; // note we must use beta = 1.0 so that we do a +=, as we should, because gradients add\n\n    // for reference the API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n\n    // recall the forward pass was calculated with alpha = 1.0f, beta = 0.0f as:\n    // cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, B*T, C, &alpha, weight, C, inp, C, &beta, out, OC);\n\n    // backward to input\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_N, C, N, OC, &alpha, weight, C, dout, OC, &beta, dinp, C));\n    // backward to weight\n    cublasCheck(cublasSgemm(cublas_handle, CUBLAS_OP_N, CUBLAS_OP_T, C, OC, N, &alpha, inp, C, dout, OC, &beta, dweight, C));\n    // backward to bias, if given\n    if (dbias != NULL) {\n\n        // sum over B,T using matrix vector multiplication with cuBLAS\n        // for reference this API is:\n        // cublasStatus_t cublasSgemv(cublasHandle_t handle, cublasOperation_t trans,\n        //                    int m, int n,\n        //                    const float           *alpha,\n        //                    const float           *A, int lda,\n        //                    const float           *x, int incx,\n        //                    const float           *beta,\n        //                    float           *y, int incy)\n        // dout is (B,T,OC), or in 2D terms (B*T, OC)\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_N, B*T, OC, &alpha, dout, B*T, ones, 1, &beta, dbias, 1));\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_T, OC, B*T, &alpha, dout, OC, ones, 1, &beta, dbias, 1));\n\n        // ugh the above isn't working...\n        // let's just do naive calculation for now, fix later\n        // const int block_size=128;\n        // const int grid_size=(OC + block_size - 1) / block_size;\n        // matmul_backward_bias_kernel<<<grid_size, block_size>>>(dbias, dout, B, T, OC);\n\n        // bit faster\n        const int block_size=512;\n        dim3 block_dim(block_size);\n        dim3 grid_dim(OC);\n        size_t shared_mem_size = block_size * sizeof(float);\n        matmul_backward_bias_kernel_faster<<<grid_dim, block_dim, shared_mem_size>>>(dbias, dout, N, OC);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nvoid linear_set_param_ptrs(\n    LinearParams* params,\n    float* params_memory,\n    int C, int OC\n) {\n    params->w = params_memory;\n    params_memory += OC * C;\n    params->b = params_memory;\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 32;\n    int C = 64;\n    int OC = 128;\n\n    // setup cublas\n    cublasHandle_t cublas_handle;\n    cublasCheck(cublasCreate(&cublas_handle));\n    cublasCheck(cublasSetMathMode(cublas_handle, CUBLAS_DEFAULT_MATH));\n\n\n    // create host memory\n    float *inp = (float*)malloc(N * C * sizeof(float));\n    float *weight = (float*)malloc(OC * C * sizeof(float));\n    float *bias = (float*)malloc(OC * sizeof(float));\n    float *out = (float*)malloc(N * OC * sizeof(float));\n    float *dout = (float*)malloc(N * OC * sizeof(float));\n    float *dinp = (float*)malloc(N * C * sizeof(float));\n    float *dweight = (float*)malloc(OC * C * sizeof(float));\n    float *dbias = (float*)malloc(OC * sizeof(float));\n\n    FILE *file = fopen(\"linear.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(inp, sizeof(float), N * C, file);\n    freadCheck(weight, sizeof(float), OC * C, file);\n    freadCheck(bias, sizeof(float), OC, file);\n    freadCheck(out, sizeof(float), N * OC, file);\n    freadCheck(dout, sizeof(float), N * OC, file);\n    freadCheck(dinp, sizeof(float), N * C, file);\n    freadCheck(dweight, sizeof(float), OC * C, file);\n    freadCheck(dbias, sizeof(float), OC, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_inp, *d_weight, *d_bias, *d_out, *d_dout, *d_dinp, *d_dweight, *d_dbias;\n    cudaCheck(cudaMalloc(&d_inp, N * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_weight, OC * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_bias, OC * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, N * OC * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, N * OC * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dinp, N * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dweight, OC * C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dbias, OC * sizeof(float)));\n\n    cudaCheck(cudaMemcpy(d_inp, inp, N * C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_weight, weight, OC * C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_bias, bias, OC * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, N * OC * sizeof(float), cudaMemcpyHostToDevice));\n\n    int block_sizes[] = {128, 256, 512, 1024};\n\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        matmul_forward2(cublas_handle, d_out, d_inp, d_weight, d_bias, N, C, OC, block_size);\n        validate_result(d_out, out, \"out\", N * OC);\n    }\n\n    printf(\"Forward pass successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        matmul_backward1(cublas_handle, d_dinp, d_dweight, d_dbias, d_dout, d_inp, d_weight, N, C, OC);\n        printf(\"Checking dbias\\n\");\n        validate_result(d_dbias, dbias, \"dbias\", OC);\n        printf(\"Checking dinp\\n\");\n        validate_result(d_dinp, dinp, \"dinp\", N * C);\n        printf(\"Checking dweight\\n\");\n        validate_result(d_dweight, dweight, \"dweight\", OC * C);\n    }\n    printf(\"Backward pass successful\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, matmul_forward2,\n                                              cublas_handle, d_out, d_inp, d_weight, d_bias,\n                                              N, C, OC, block_size);\n\n        float tflops = (float)N * C * OC * 2 / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, matmul_backward1,\n                                              cublas_handle, d_dinp, d_dweight, d_dbias, d_dout, d_inp, d_weight,\n                                              N, C, OC);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(inp);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dinp);\n    free(dweight);\n    free(dbias);\n\n    cudaCheck(cudaFree(d_inp));\n    cudaCheck(cudaFree(d_weight));\n    cudaCheck(cudaFree(d_bias));\n    cudaCheck(cudaFree(d_out));\n    cudaCheck(cudaFree(d_dout));\n    cudaCheck(cudaFree(d_dinp));\n    cudaCheck(cudaFree(d_dweight));\n    cudaCheck(cudaFree(d_dbias));\n\n    cublasCheck(cublasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"linear.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n// copy the matmul forward and backward kernels from llm.c\n\n// is there no better way other than just adding bias with a whole separate kernel?\n// this is a highly memory-bound operation, should be fused into the matmul kernel\n// but i can't seem to find a cuBLAS function that does this\n__global__ void add_bias(float* out, const float* bias, int N, int OC) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = idx; i < N * OC; i += stride) {\n        int col = i % OC;\n        out[i] += bias[col];\n    }\n}\n\n// kernel 2 calls cuBLAS, which should be very efficient\nvoid matmul_forward2(hipblasHandle_t cublas_handle,\n                     float* out,\n                     const float* inp, const float* weight, const float* bias,\n                     int N, int C, int OC,\n                     const int block_size) {\n    // for reference API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n    // for us, inp is (B*T, C), weight is (OC, C), out is (B*T, OC)\n    // cuBLAS does C = alpha * A * B + beta * C\n    // where A is mxk, B is kxn, C is mxn\n    // now, because we use row-major storage, cuBLAS (which is column-major) sees our matrices transposed.\n    // algorithmically / in e.g. PyTorch we want to do: out = inp @ weight.T\n    // but because cuBLAS is column-major, we actually want to get it to calculate out.T . Mathematically, this is:\n    // out.T = weight @ inp.T\n    // but again, our variables look transposed, so using the actual weight/inp we have here in this function, this becomes\n    // out.T = weight.T @ inp\n    // so we need to get cuBLAS to calculate weight.T @ inp (the variables here are the actual ones in this function)\n    // => need to call cuBLAS with A = weight, B = inp\n    // => need to call cuBLAS with transa = CUBLAS_OP_T, transb = CUBLAS_OP_N\n\n    const float alpha = 1.0f;\n    const float beta = 0.0f;\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_T, HIPBLAS_OP_N, OC, N, C, &alpha, weight, C, inp, C, &beta, out, OC));\n    // and now we still have to add the bias... (ew)\n    if (bias != NULL) {\n        int grid_size = ceil_div(OC * N, block_size);\n        add_bias<<<grid_size, block_size>>>(out, bias, N, OC);\n        cudaCheck(hipGetLastError());\n    }\n}\n\n// use shared memory and coarsening + reductions\n__global__ void matmul_backward_bias_kernel_faster(float* dbias, const float* dout, int N, int OC) {\n    extern __shared__ float shared[];\n    int o = blockIdx.x; // range [0, OC)\n    int tid = threadIdx.x; // range [0, block_size)\n    int block_size = blockDim.x;\n    const float* x = dout + o;\n    // thread coarsening\n    double sum = 0.0;\n    for (int i = tid; i < N; i += block_size) {\n        sum += x[i * OC];\n    }\n    shared[tid] = (float) sum;\n    __syncthreads();\n    // reductions\n    for (int stride = block_size / 2; stride >= 1; stride /= 2) {\n        __syncthreads();\n        if (tid < stride) {\n            shared[tid] += shared[tid + stride];\n        }\n    }\n    // write the final result (at thread 0) to global memory\n    if (tid == 0) {\n        dbias[o] = shared[0];\n    }\n}\n\n// version1: simple cuBLAS calls\nvoid matmul_backward1(hipblasHandle_t cublas_handle,\n                      float* dinp, float* dweight, float* dbias,\n                      float* dout, float* inp, float* weight,\n                      int N, int C, int OC) {\n    float alpha = 1.0f;\n    float beta = 0.0f; // note we must use beta = 1.0 so that we do a +=, as we should, because gradients add\n\n    // for reference the API is:\n    // cublasStatus_t cublasSgemm(cublasHandle_t handle,\n    //                        cublasOperation_t transa, cublasOperation_t transb,\n    //                        int m, int n, int k,\n    //                        const float           *alpha,\n    //                        const float           *A, int lda,\n    //                        const float           *B, int ldb,\n    //                        const float           *beta,\n    //                        float           *C, int ldc)\n\n    // recall the forward pass was calculated with alpha = 1.0f, beta = 0.0f as:\n    // cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_N, OC, B*T, C, &alpha, weight, C, inp, C, &beta, out, OC);\n\n    // backward to input\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_N, C, N, OC, &alpha, weight, C, dout, OC, &beta, dinp, C));\n    // backward to weight\n    cublasCheck(hipblasSgemm(cublas_handle, HIPBLAS_OP_N, HIPBLAS_OP_T, C, OC, N, &alpha, inp, C, dout, OC, &beta, dweight, C));\n    // backward to bias, if given\n    if (dbias != NULL) {\n\n        // sum over B,T using matrix vector multiplication with cuBLAS\n        // for reference this API is:\n        // cublasStatus_t cublasSgemv(cublasHandle_t handle, cublasOperation_t trans,\n        //                    int m, int n,\n        //                    const float           *alpha,\n        //                    const float           *A, int lda,\n        //                    const float           *x, int incx,\n        //                    const float           *beta,\n        //                    float           *y, int incy)\n        // dout is (B,T,OC), or in 2D terms (B*T, OC)\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_N, B*T, OC, &alpha, dout, B*T, ones, 1, &beta, dbias, 1));\n        // cublasCheck(cublasSgemv(cublas_handle, CUBLAS_OP_T, OC, B*T, &alpha, dout, OC, ones, 1, &beta, dbias, 1));\n\n        // ugh the above isn't working...\n        // let's just do naive calculation for now, fix later\n        // const int block_size=128;\n        // const int grid_size=(OC + block_size - 1) / block_size;\n        // matmul_backward_bias_kernel<<<grid_size, block_size>>>(dbias, dout, B, T, OC);\n\n        // bit faster\n        const int block_size=512;\n        dim3 block_dim(block_size);\n        dim3 grid_dim(OC);\n        size_t shared_mem_size = block_size * sizeof(float);\n        matmul_backward_bias_kernel_faster<<<grid_dim, block_dim, shared_mem_size>>>(dbias, dout, N, OC);\n    }\n}\n\n// ----------------------------------------------------------------------------\n\nvoid linear_set_param_ptrs(\n    LinearParams* params,\n    float* params_memory,\n    int C, int OC\n) {\n    params->w = params_memory;\n    params_memory += OC * C;\n    params->b = params_memory;\n}\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 32;\n    int C = 64;\n    int OC = 128;\n\n    // setup cublas\n    hipblasHandle_t cublas_handle;\n    cublasCheck(hipblasCreate(&cublas_handle));\n    cublasCheck(hipblasSetMathMode(cublas_handle, HIPBLAS_DEFAULT_MATH));\n\n\n    // create host memory\n    float *inp = (float*)malloc(N * C * sizeof(float));\n    float *weight = (float*)malloc(OC * C * sizeof(float));\n    float *bias = (float*)malloc(OC * sizeof(float));\n    float *out = (float*)malloc(N * OC * sizeof(float));\n    float *dout = (float*)malloc(N * OC * sizeof(float));\n    float *dinp = (float*)malloc(N * C * sizeof(float));\n    float *dweight = (float*)malloc(OC * C * sizeof(float));\n    float *dbias = (float*)malloc(OC * sizeof(float));\n\n    FILE *file = fopen(\"linear.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(inp, sizeof(float), N * C, file);\n    freadCheck(weight, sizeof(float), OC * C, file);\n    freadCheck(bias, sizeof(float), OC, file);\n    freadCheck(out, sizeof(float), N * OC, file);\n    freadCheck(dout, sizeof(float), N * OC, file);\n    freadCheck(dinp, sizeof(float), N * C, file);\n    freadCheck(dweight, sizeof(float), OC * C, file);\n    freadCheck(dbias, sizeof(float), OC, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_inp, *d_weight, *d_bias, *d_out, *d_dout, *d_dinp, *d_dweight, *d_dbias;\n    cudaCheck(hipMalloc(&d_inp, N * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_weight, OC * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_bias, OC * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, N * OC * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, N * OC * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dinp, N * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dweight, OC * C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dbias, OC * sizeof(float)));\n\n    cudaCheck(hipMemcpy(d_inp, inp, N * C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_weight, weight, OC * C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_bias, bias, OC * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, N * OC * sizeof(float), hipMemcpyHostToDevice));\n\n    int block_sizes[] = {128, 256, 512, 1024};\n\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        matmul_forward2(cublas_handle, d_out, d_inp, d_weight, d_bias, N, C, OC, block_size);\n        validate_result(d_out, out, \"out\", N * OC);\n    }\n\n    printf(\"Forward pass successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nBlock size: %d\\n\", block_size);\n        matmul_backward1(cublas_handle, d_dinp, d_dweight, d_dbias, d_dout, d_inp, d_weight, N, C, OC);\n        printf(\"Checking dbias\\n\");\n        validate_result(d_dbias, dbias, \"dbias\", OC);\n        printf(\"Checking dinp\\n\");\n        validate_result(d_dinp, dinp, \"dinp\", N * C);\n        printf(\"Checking dweight\\n\");\n        validate_result(d_dweight, dweight, \"dweight\", OC * C);\n    }\n    printf(\"Backward pass successful\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, matmul_forward2,\n                                              cublas_handle, d_out, d_inp, d_weight, d_bias,\n                                              N, C, OC, block_size);\n\n        float tflops = (float)N * C * OC * 2 / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, matmul_backward1,\n                                              cublas_handle, d_dinp, d_dweight, d_dbias, d_dout, d_inp, d_weight,\n                                              N, C, OC);\n\n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n\n    // free memory\n    free(inp);\n    free(weight);\n    free(bias);\n    free(out);\n    free(dout);\n    free(dinp);\n    free(dweight);\n    free(dbias);\n\n    cudaCheck(hipFree(d_inp));\n    cudaCheck(hipFree(d_weight));\n    cudaCheck(hipFree(d_bias));\n    cudaCheck(hipFree(d_out));\n    cudaCheck(hipFree(d_dout));\n    cudaCheck(hipFree(d_dinp));\n    cudaCheck(hipFree(d_dweight));\n    cudaCheck(hipFree(d_dbias));\n\n    cublasCheck(hipblasDestroy(cublas_handle));\n    return 0;\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/linear.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"concat_channel.cuh\"\n\n\n__global__ void concat_channel_forward_kernel(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        // copy input from x1\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n        out[out_idx] = x1[idx];\n    }\n    if (idx < B * C2 * H * W) {\n        // copy input from x2\n        // move over from x1\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        out[out_idx] = x2[idx];\n    }\n}\n\n__global__ void concat_channel_backward_kernel(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n\n        dx1[idx] = dout[out_idx];\n    }\n    if (idx < B * C2 * H * W) {\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        dx2[idx] = dout[out_idx];\n    }\n}\n\nvoid concat_channel_forward(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_forward_kernel<<<n_blk, block_size>>>(x1, x2, out, B, C1, C2, H, W);\n}\n\nvoid concat_channel_backward(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_backward_kernel<<<n_blk, block_size>>>(dout, dx1, dx2, B, C1, C2, H, W);\n}", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"concat_channel.cuh\"\n\n\n__global__ void concat_channel_forward_kernel(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        // copy input from x1\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n        out[out_idx] = x1[idx];\n    }\n    if (idx < B * C2 * H * W) {\n        // copy input from x2\n        // move over from x1\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        out[out_idx] = x2[idx];\n    }\n}\n\n__global__ void concat_channel_backward_kernel(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W\n) {\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx < B * C1 * H * W) {\n        int b = idx / (C1 * H * W);\n        int c = (idx / H / W) % C1;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + c * H * W + h * W + w;\n\n        dx1[idx] = dout[out_idx];\n    }\n    if (idx < B * C2 * H * W) {\n        int b = idx / (C2 * H * W);\n        int c = (idx / H / W) % C2;\n        int h = (idx / W) % H;\n        int w = idx % W;\n        int out_idx = b * (C1 + C2) * H * W + (C1 + c) * H * W + h * W + w;\n        \n        dx2[idx] = dout[out_idx];\n    }\n}\n\nvoid concat_channel_forward(\n    const float* x1, const float* x2,\n    float* out,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_forward_kernel<<<n_blk, block_size>>>(x1, x2, out, B, C1, C2, H, W);\n}\n\nvoid concat_channel_backward(\n    const float* dout,\n    float* dx1, float* dx2,\n    int B, int C1, int C2, int H, int W, int block_size\n) {\n    int N = B * max_int(C1, C2) * H * W;\n    int n_blk = ceil_div(N, block_size);\n    concat_channel_backward_kernel<<<n_blk, block_size>>>(dout, dx1, dx2, B, C1, C2, H, W);\n}", "source": "/kaggle/working/unet.cu/dev/concat_channel.cu"}
{"input": "#include <cuda_runtime.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n\n\n// this will only be called with one block\n__global__ void mse_forward_kernel(\n    const float* inp, const float* y, float* loss, int N\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    __shared__ float shared_sum[32]; // max 32 warps\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        float diff = inp[i] - y[i];\n        thread_sum += diff * diff;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n    \n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        loss[0] = block_sum / N;\n    }\n}\n\nvoid mse_forward(const float* inp, const float* y, float* loss, int N, int block_size) {\n    mse_forward_kernel<<<1, block_size>>>(inp, y, loss, N);\n}\n\n\n\n// not calculating dy in backward\n// note that backward for MSE doesn't require the loss value\n__global__ void mse_backward_kernel(\n    const float* inp, const float* y, float* dinp, int N\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < N) {\n        float x = inp[idx];\n        dinp[idx] = 2.0f * (x - y[idx]) / N;\n    }\n}\n\nvoid mse_backward(const float* inp, const float* y, float* dinp, int N, int block_size) {\n    int n_blocks = ceil_div(N, block_size);\n    mse_backward_kernel<<<n_blocks, block_size>>>(inp, y, dinp, N);\n}\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 32 * 3 * 64 * 64;\n    int block_size = 512;\n\n    // host memory\n    float* x = (float*)malloc(N * sizeof(float));\n    float* y = (float*)malloc(N * sizeof(float));\n    float* dx = (float*)malloc(N * sizeof(float));\n    float* loss = (float*)malloc(sizeof(float));\n\n    FILE *file = fopenCheck(\"mse.bin\", \"rb\");\n    if (!file) {\n        return -1;\n    }\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(y, sizeof(float), N, file);\n    freadCheck(dx, sizeof(float), N, file);\n    freadCheck(loss, sizeof(float), 1, file);\n    fcloseCheck(file);\n\n    // device memory\n    float *d_x, *d_y, *d_dx, *d_loss;\n    cudaCheck(cudaMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_y, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_loss, sizeof(float)));\n\n    cudaCheck(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_y, y, N * sizeof(float), cudaMemcpyHostToDevice));\n\n    printf(\"Checking forward pass\\n\");\n    mse_forward(d_x, d_y, d_loss, N, block_size);\n    validate_result(d_loss, loss, \"loss\", 1);\n    printf(\"Forward pass successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    mse_backward(d_x, d_y, d_dx, N, block_size);\n    validate_result(d_dx, dx, \"dinp\", N);\n    printf(\"Backward pass successful\\n\");\n}\n#endif", "output": "#include <hip/hip_runtime.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n\n\n// this will only be called with one block\n__global__ void mse_forward_kernel(\n    const float* inp, const float* y, float* loss, int N\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n\n    __shared__ float shared_sum[32]; // max 32 warps\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n\n    float thread_sum = 0.0f;\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        float diff = inp[i] - y[i];\n        thread_sum += diff * diff;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    shared_sum[warp_id] = warp_sum;\n    __syncthreads();\n    \n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n\n    if (threadIdx.x == 0) {\n        loss[0] = block_sum / N;\n    }\n}\n\nvoid mse_forward(const float* inp, const float* y, float* loss, int N, int block_size) {\n    mse_forward_kernel<<<1, block_size>>>(inp, y, loss, N);\n}\n\n\n\n// not calculating dy in backward\n// note that backward for MSE doesn't require the loss value\n__global__ void mse_backward_kernel(\n    const float* inp, const float* y, float* dinp, int N\n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < N) {\n        float x = inp[idx];\n        dinp[idx] = 2.0f * (x - y[idx]) / N;\n    }\n}\n\nvoid mse_backward(const float* inp, const float* y, float* dinp, int N, int block_size) {\n    int n_blocks = ceil_div(N, block_size);\n    mse_backward_kernel<<<n_blocks, block_size>>>(inp, y, dinp, N);\n}\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 32 * 3 * 64 * 64;\n    int block_size = 512;\n\n    // host memory\n    float* x = (float*)malloc(N * sizeof(float));\n    float* y = (float*)malloc(N * sizeof(float));\n    float* dx = (float*)malloc(N * sizeof(float));\n    float* loss = (float*)malloc(sizeof(float));\n\n    FILE *file = fopenCheck(\"mse.bin\", \"rb\");\n    if (!file) {\n        return -1;\n    }\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(y, sizeof(float), N, file);\n    freadCheck(dx, sizeof(float), N, file);\n    freadCheck(loss, sizeof(float), 1, file);\n    fcloseCheck(file);\n\n    // device memory\n    float *d_x, *d_y, *d_dx, *d_loss;\n    cudaCheck(hipMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_y, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_loss, sizeof(float)));\n\n    cudaCheck(hipMemcpy(d_x, x, N * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_y, y, N * sizeof(float), hipMemcpyHostToDevice));\n\n    printf(\"Checking forward pass\\n\");\n    mse_forward(d_x, d_y, d_loss, N, block_size);\n    validate_result(d_loss, loss, \"loss\", 1);\n    printf(\"Forward pass successful\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    mse_backward(d_x, d_y, d_dx, N, block_size);\n    validate_result(d_dx, dx, \"dinp\", N);\n    printf(\"Backward pass successful\\n\");\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/mse.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n#include <unistd.h>\n#include \"groupnorm.cuh\"\n\n\n// -----------------------------------------------------------------------------------------------\n// GPU kernels\n\n// Essentially taken from llm.c's kernel 5\n// using kernel 5 because for images, each \"channel\" is effectively\n// H * W * group_size, which is quite large\n// we will have one block per group, which means there are B * C / group_size blocks\n__global__ void groupnorm_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    x += blockIdx.x * img_size * group_size;\n    out += blockIdx.x * img_size * group_size;\n    // each block will only every acces group_size channels\n    weight += g * group_size;\n    bias += g * group_size;\n\n    float thread_sum = 0.0f;\n    float thread_sum2 = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        float val = x[i];\n        thread_sum += val;\n        thread_sum2 += val * val;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    float warp_sum2 = cg:: reduce(warp, thread_sum2, cg::plus<float>{});\n    // store warp sum into shared memory\n    shared_sum[warp_id] = warp_sum;\n    shared_sum2[warp_id] = warp_sum2;\n    __syncthreads();\n    \n    // load warp sums from shared memory\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    warp_sum2 = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n    float block_sum2 = cg::reduce(warp, warp_sum2, cg::plus<float>{});\n    block_sum /= block_pixels;\n    block_sum2 /= block_pixels;\n    float m = block_sum;\n    float var = block_sum2 - m * m;\n    float s = rsqrtf(var + 1e-5f);\n    if (threadIdx.x == 0 && mean != nullptr) {\n        mean[blockIdx.x] = m;\n    }\n    if (threadIdx.x == 0 && rstd != nullptr) {\n        rstd[blockIdx.x] = s;\n    }\n\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float n = s * (x[i] - m);\n        out[i] = n * weight[c_mod_group] + bias[c_mod_group];\n    }\n}\n\n// most similar to kernel 2 in llm.c\n// not doing any float16 optimizations yet\n// main change is to have each block allocated to a single group\n__global__ void groupnorm_backward_kernel(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    dout += blockIdx.x * img_size * group_size;\n    x += blockIdx.x * img_size * group_size;\n    dx += blockIdx.x * img_size * group_size;\n    weight += g * group_size;\n    dweight += g * group_size;\n    dbias += g * group_size;\n\n    float m_val = mean[blockIdx.x];\n    float rstd_val = rstd[blockIdx.x];\n\n\n    // calculate the two mean terms in the group dimension\n    // first is dout * weight, and second is dout * weight * norm\n    // where norm = (x - mean) * rstd\n    float w_dout_thread = 0.0f;\n    float w_dout_norm_thread = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float cur_w_dout = weight[c_mod_group] * dout[i];\n        w_dout_thread += cur_w_dout;\n        float norm = (x[i] - m_val) * rstd_val;\n        w_dout_norm_thread += cur_w_dout * norm;\n    }\n    // warp reduce\n    float w_dout_warp = cg::reduce(warp, w_dout_thread, cg::plus<float>{});\n    float w_dout_norm_warp = cg::reduce(warp, w_dout_norm_thread, cg::plus<float>{});\n    // store warp sum in shared mem\n    shared_sum[warp_id] = w_dout_warp;\n    shared_sum2[warp_id] = w_dout_norm_warp;\n    __syncthreads();\n\n    // load warp sums from shared memory\n    w_dout_warp = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    w_dout_norm_warp = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float w_dout_block = cg::reduce(warp, w_dout_warp, cg::plus<float>{});\n    float w_dout_norm_block = cg::reduce(warp, w_dout_norm_warp, cg::plus<float>{});\n    w_dout_block /= block_pixels;\n    w_dout_norm_block /= block_pixels;\n\n    // update dx\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        // in bounds of image\n        // accumulate dw and db\n        float dout_val = dout[i];\n        float norm = (x[i] - m_val) * rstd_val;\n\n        // update dx\n        int c_mod_group = (i / img_size) % group_size;\n        float w_dout = weight[c_mod_group] * dout_val;\n        dx[i] = (w_dout - w_dout_block - norm * w_dout_norm_block) * rstd_val;\n    }\n    // update dw and db\n    // use different methods when the image size is large or small\n\n    // if the image size is larger than the block size\n    // loop over the channels and use the whole block on each channel\n    // otherwise, assign each warp to a channel\n    // in either case image size must be larger than the warp size\n    //assert(img_size % warp.size() == 0);\n    assert(blockDim.x % warp.size() == 0);\n    if (img_size % blockDim.x == 0) {\n        for (int c = 0; c < group_size; c++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n            for (int i = threadIdx.x; i < img_size; i += blockDim.x) {\n                float dout_val = dout[i];\n                db_thread += dout_val;\n                float norm = (x[i] - m_val) * rstd_val;\n                dw_thread += dout_val * norm;\n            }\n\n            // move pointers\n            dout += img_size;\n            x += img_size;\n\n            // warp reduce\n            float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n            float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n            ////// store warp sum in shared mem\n            if (lane_id == 0) {\n                shared_sum[warp_id] = dw_warp;\n                shared_sum2[warp_id] = db_warp;\n            }\n            __syncthreads();\n            // use the first thread to reduce the shared memory sums and save to global memory\n            if (threadIdx.x == 0) {\n                float dw_block = 0.0f;\n                float db_block = 0.0f;\n                for (int i = 0; i < num_warps; i++) {\n                    dw_block += shared_sum[i];\n                    db_block += shared_sum2[i];\n                }\n                atomicAdd(dweight + c, dw_block);\n                atomicAdd(dbias + c, db_block);\n            }\n        }\n    } else {\n        // if group size is large, need to loop over the group channels with the whole block\n        int block_reps = ceil_div(group_size, num_warps);\n        for (int br = 0; br < block_reps; br++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n\n            int ch = br * num_warps + warp_id;\n            if (ch < group_size) {\n                const float* dout_ch = dout + ch * img_size;\n                const float* x_ch = x + ch * img_size;\n                for (int i = lane_id; i < img_size; i += warp.size()) {\n                    float dout_val = dout_ch[i];\n                    db_thread += dout_val;\n                    float norm = (x_ch[i] - m_val) * rstd_val;\n                    dw_thread += dout_val * norm;\n                }\n                \n                // warp reduce\n                float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n                float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n                // since each warp takes care of an entire image\n                // directly store result\n                if (lane_id == 0) {\n                    atomicAdd(dweight + ch, dw_warp);\n                    atomicAdd(dbias + ch, db_warp);\n                }\n            }\n        }\n    }\n}\n\n// -----------------------------------------------------------------------------------------------\n// CUDA kernel launcher\n\nvoid groupnorm_forward(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32);\n    groupnorm_forward_kernel<<<n_blocks, block_size>>>(\n        x, weight, bias, out, mean, rstd, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(cudaGetLastError());\n}\n\nvoid groupnorm_backward(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32 * group_size);\n    groupnorm_backward_kernel<<<n_blocks, block_size>>>(\n        dout, x, mean, rstd, weight, dx, dweight, dbias, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(cudaGetLastError());\n}\n\n\n// -----------------------------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    //int B = 32;\n    //int C = 64;\n    //int H = 32;\n    //int W = 32;\n    srand(0);\n    int B = 16;\n    int C = 128;\n    int H = 4;\n    int W = 8;\n\n    int img_size = H * W;\n    int n_groups = 32;\n\n\n    // create host memory to load data\n    float *x = (float*)malloc(B * C * img_size * sizeof(float));\n    float *weight = (float*)malloc(C * sizeof(float));\n    float *bias = (float*)malloc(C * sizeof(float));\n    float *out = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dout = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dx = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dweight = (float*)malloc(C * sizeof(float));\n    float *dbias = (float*)malloc(C * sizeof(float));\n\n\n    // read saved output\n    FILE *file = fopenCheck(\"groupnorm.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * img_size, file);\n    freadCheck(weight, sizeof(float), C, file);\n    freadCheck(bias, sizeof(float), C, file);\n    freadCheck(out, sizeof(float), B * C * img_size, file);\n    freadCheck(dout, sizeof(float), B * C * img_size, file);\n    freadCheck(dx, sizeof(float), B * C * img_size, file);\n    freadCheck(dweight, sizeof(float), C, file);\n    freadCheck(dbias, sizeof(float), C, file);\n    fcloseCheck(file);\n\n\n    // device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_mean, *d_rstd, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(cudaMalloc(&d_x, B * C * img_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_weight, C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_bias, C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * C * img_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_mean, B * n_groups * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_rstd, B * n_groups * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * C * img_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, B * C * img_size * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dweight, C * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dbias, C * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, B * C * img_size * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_weight, weight, C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_bias, bias, C * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * C * img_size * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemset(d_dx, 0, B * C * img_size * sizeof(float)));\n    cudaCheck(cudaMemset(d_dweight, 0, C * sizeof(float)));\n    cudaCheck(cudaMemset(d_dbias, 0, C * sizeof(float)));\n\n    printf(\"Checking forward pass\\n\");\n    float fwd_acc = 1e-1;\n    groupnorm_forward(d_x, d_weight, d_bias, d_out, d_mean, d_rstd, B, C, H, W, n_groups);\n    validate_result(d_out, out, \"out\", B * C * img_size, fwd_acc);\n    printf(\"Forward pass successful\\n\");\n\n    // check the values of rstd\n    float *rstd = (float*)malloc(B * n_groups * sizeof(float));\n\n    printf(\"Checking backward pass\\n\");\n    groupnorm_backward(d_dout, d_x, d_mean, d_rstd, d_weight, d_dx, d_dweight, d_dbias, B, C, H, W, n_groups);\n    float acc = 1e-3;\n    printf(\"Checking dbias\\n\");\n    validate_result(d_dbias, dbias, \"dbias\", C, acc);\n    printf(\"Checking dweight\\n\");\n    validate_result(d_dweight, dweight, \"dweight\", C, acc);\n    printf(\"Checking dx\\n\");\n    validate_result(d_dx, dx, \"dx\", B * C * img_size, 1.0f);\n    printf(\"Backward pass successful\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks\\n\");\n    int repeat_times = 100;\n    float elapsed_time = benchmark_kernel(repeat_times, groupnorm_forward,\n                                            d_x, d_weight, d_bias, d_out, d_mean, d_rstd,\n                                            B, C, H, W, n_groups);\n    printf(\"time %.4f ms\\n\", elapsed_time);\n\n    printf(\"Backward pass benchmarks\\n\");\n    elapsed_time = benchmark_kernel(repeat_times, groupnorm_backward,\n                                    d_dout, d_x, d_mean, d_rstd, d_weight, d_dx, d_dweight, d_dbias,\n                                    B, C, H, W, n_groups);\n    printf(\"time %.4f ms\\n\", elapsed_time);\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n#include <unistd.h>\n#include \"groupnorm.cuh\"\n\n\n// -----------------------------------------------------------------------------------------------\n// GPU kernels\n\n// Essentially taken from llm.c's kernel 5\n// using kernel 5 because for images, each \"channel\" is effectively\n// H * W * group_size, which is quite large\n// we will have one block per group, which means there are B * C / group_size blocks\n__global__ void groupnorm_forward_kernel(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    x += blockIdx.x * img_size * group_size;\n    out += blockIdx.x * img_size * group_size;\n    // each block will only every acces group_size channels\n    weight += g * group_size;\n    bias += g * group_size;\n\n    float thread_sum = 0.0f;\n    float thread_sum2 = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        float val = x[i];\n        thread_sum += val;\n        thread_sum2 += val * val;\n    }\n\n    // warp reduce\n    float warp_sum = cg::reduce(warp, thread_sum, cg::plus<float>{});\n    float warp_sum2 = cg:: reduce(warp, thread_sum2, cg::plus<float>{});\n    // store warp sum into shared memory\n    shared_sum[warp_id] = warp_sum;\n    shared_sum2[warp_id] = warp_sum2;\n    __syncthreads();\n    \n    // load warp sums from shared memory\n    warp_sum = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    warp_sum2 = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float block_sum = cg::reduce(warp, warp_sum, cg::plus<float>{});\n    float block_sum2 = cg::reduce(warp, warp_sum2, cg::plus<float>{});\n    block_sum /= block_pixels;\n    block_sum2 /= block_pixels;\n    float m = block_sum;\n    float var = block_sum2 - m * m;\n    float s = rsqrtf(var + 1e-5f);\n    if (threadIdx.x == 0 && mean != nullptr) {\n        mean[blockIdx.x] = m;\n    }\n    if (threadIdx.x == 0 && rstd != nullptr) {\n        rstd[blockIdx.x] = s;\n    }\n\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float n = s * (x[i] - m);\n        out[i] = n * weight[c_mod_group] + bias[c_mod_group];\n    }\n}\n\n// most similar to kernel 2 in llm.c\n// not doing any float16 optimizations yet\n// main change is to have each block allocated to a single group\n__global__ void groupnorm_backward_kernel(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int img_size, int group_size, int n_groups\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    __shared__ float shared_sum[32]; // block_size max is 1024 = 32 * 32 warps\n    __shared__ float shared_sum2[32]; // warps will be writing into shared memeory after warp-reduce\n    int num_warps = blockDim.x / 32;\n    int warp_id = threadIdx.x / 32;\n    int lane_id = threadIdx.x % 32;\n    int block_pixels = img_size * group_size;\n    // group index\n    int g = blockIdx.x % n_groups;\n\n    // move pointers\n    dout += blockIdx.x * img_size * group_size;\n    x += blockIdx.x * img_size * group_size;\n    dx += blockIdx.x * img_size * group_size;\n    weight += g * group_size;\n    dweight += g * group_size;\n    dbias += g * group_size;\n\n    float m_val = mean[blockIdx.x];\n    float rstd_val = rstd[blockIdx.x];\n\n\n    // calculate the two mean terms in the group dimension\n    // first is dout * weight, and second is dout * weight * norm\n    // where norm = (x - mean) * rstd\n    float w_dout_thread = 0.0f;\n    float w_dout_norm_thread = 0.0f;\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        int c_mod_group = (i / img_size) % group_size;\n        float cur_w_dout = weight[c_mod_group] * dout[i];\n        w_dout_thread += cur_w_dout;\n        float norm = (x[i] - m_val) * rstd_val;\n        w_dout_norm_thread += cur_w_dout * norm;\n    }\n    // warp reduce\n    float w_dout_warp = cg::reduce(warp, w_dout_thread, cg::plus<float>{});\n    float w_dout_norm_warp = cg::reduce(warp, w_dout_norm_thread, cg::plus<float>{});\n    // store warp sum in shared mem\n    shared_sum[warp_id] = w_dout_warp;\n    shared_sum2[warp_id] = w_dout_norm_warp;\n    __syncthreads();\n\n    // load warp sums from shared memory\n    w_dout_warp = (lane_id < num_warps) ? shared_sum[lane_id] : 0.0f;\n    w_dout_norm_warp = (lane_id < num_warps) ? shared_sum2[lane_id] : 0.0f;\n    float w_dout_block = cg::reduce(warp, w_dout_warp, cg::plus<float>{});\n    float w_dout_norm_block = cg::reduce(warp, w_dout_norm_warp, cg::plus<float>{});\n    w_dout_block /= block_pixels;\n    w_dout_norm_block /= block_pixels;\n\n    // update dx\n    for (int i = threadIdx.x; i < block_pixels; i += blockDim.x) {\n        // in bounds of image\n        // accumulate dw and db\n        float dout_val = dout[i];\n        float norm = (x[i] - m_val) * rstd_val;\n\n        // update dx\n        int c_mod_group = (i / img_size) % group_size;\n        float w_dout = weight[c_mod_group] * dout_val;\n        dx[i] = (w_dout - w_dout_block - norm * w_dout_norm_block) * rstd_val;\n    }\n    // update dw and db\n    // use different methods when the image size is large or small\n\n    // if the image size is larger than the block size\n    // loop over the channels and use the whole block on each channel\n    // otherwise, assign each warp to a channel\n    // in either case image size must be larger than the warp size\n    //assert(img_size % warp.size() == 0);\n    assert(blockDim.x % warp.size() == 0);\n    if (img_size % blockDim.x == 0) {\n        for (int c = 0; c < group_size; c++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n            for (int i = threadIdx.x; i < img_size; i += blockDim.x) {\n                float dout_val = dout[i];\n                db_thread += dout_val;\n                float norm = (x[i] - m_val) * rstd_val;\n                dw_thread += dout_val * norm;\n            }\n\n            // move pointers\n            dout += img_size;\n            x += img_size;\n\n            // warp reduce\n            float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n            float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n            ////// store warp sum in shared mem\n            if (lane_id == 0) {\n                shared_sum[warp_id] = dw_warp;\n                shared_sum2[warp_id] = db_warp;\n            }\n            __syncthreads();\n            // use the first thread to reduce the shared memory sums and save to global memory\n            if (threadIdx.x == 0) {\n                float dw_block = 0.0f;\n                float db_block = 0.0f;\n                for (int i = 0; i < num_warps; i++) {\n                    dw_block += shared_sum[i];\n                    db_block += shared_sum2[i];\n                }\n                atomicAdd(dweight + c, dw_block);\n                atomicAdd(dbias + c, db_block);\n            }\n        }\n    } else {\n        // if group size is large, need to loop over the group channels with the whole block\n        int block_reps = ceil_div(group_size, num_warps);\n        for (int br = 0; br < block_reps; br++) {\n            float dw_thread = 0.0f;\n            float db_thread = 0.0f;\n\n            int ch = br * num_warps + warp_id;\n            if (ch < group_size) {\n                const float* dout_ch = dout + ch * img_size;\n                const float* x_ch = x + ch * img_size;\n                for (int i = lane_id; i < img_size; i += warp.size()) {\n                    float dout_val = dout_ch[i];\n                    db_thread += dout_val;\n                    float norm = (x_ch[i] - m_val) * rstd_val;\n                    dw_thread += dout_val * norm;\n                }\n                \n                // warp reduce\n                float dw_warp = cg::reduce(warp, dw_thread, cg::plus<float>{});\n                float db_warp = cg::reduce(warp, db_thread, cg::plus<float>{});\n                // since each warp takes care of an entire image\n                // directly store result\n                if (lane_id == 0) {\n                    atomicAdd(dweight + ch, dw_warp);\n                    atomicAdd(dbias + ch, db_warp);\n                }\n            }\n        }\n    }\n}\n\n// -----------------------------------------------------------------------------------------------\n// CUDA kernel launcher\n\nvoid groupnorm_forward(\n    const float* x, const float* weight, const float* bias,\n    float* out, float* mean, float* rstd,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32);\n    groupnorm_forward_kernel<<<n_blocks, block_size>>>(\n        x, weight, bias, out, mean, rstd, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(hipGetLastError());\n}\n\nvoid groupnorm_backward(\n    const float* dout, const float* x, const float* mean, const float* rstd, const float* weight,\n    float* dx, float* dweight, float* dbias,\n    int B, int C, int H, int W, int n_groups\n) {\n    int img_size = H * W;\n    int group_size = C / n_groups;\n    int n_blocks = B * n_groups;\n    int block_size = max_int(min_int(512, img_size * group_size), 32 * group_size);\n    groupnorm_backward_kernel<<<n_blocks, block_size>>>(\n        dout, x, mean, rstd, weight, dx, dweight, dbias, B, C, img_size, group_size, n_groups\n    );\n    cudaCheck(hipGetLastError());\n}\n\n\n// -----------------------------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    //int B = 32;\n    //int C = 64;\n    //int H = 32;\n    //int W = 32;\n    srand(0);\n    int B = 16;\n    int C = 128;\n    int H = 4;\n    int W = 8;\n\n    int img_size = H * W;\n    int n_groups = 32;\n\n\n    // create host memory to load data\n    float *x = (float*)malloc(B * C * img_size * sizeof(float));\n    float *weight = (float*)malloc(C * sizeof(float));\n    float *bias = (float*)malloc(C * sizeof(float));\n    float *out = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dout = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dx = (float*)malloc(B * C * img_size * sizeof(float));\n    float *dweight = (float*)malloc(C * sizeof(float));\n    float *dbias = (float*)malloc(C * sizeof(float));\n\n\n    // read saved output\n    FILE *file = fopenCheck(\"groupnorm.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * img_size, file);\n    freadCheck(weight, sizeof(float), C, file);\n    freadCheck(bias, sizeof(float), C, file);\n    freadCheck(out, sizeof(float), B * C * img_size, file);\n    freadCheck(dout, sizeof(float), B * C * img_size, file);\n    freadCheck(dx, sizeof(float), B * C * img_size, file);\n    freadCheck(dweight, sizeof(float), C, file);\n    freadCheck(dbias, sizeof(float), C, file);\n    fcloseCheck(file);\n\n\n    // device memory\n    float *d_x, *d_weight, *d_bias, *d_out, *d_mean, *d_rstd, *d_dout, *d_dx, *d_dweight, *d_dbias;\n    cudaCheck(hipMalloc(&d_x, B * C * img_size * sizeof(float)));\n    cudaCheck(hipMalloc(&d_weight, C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_bias, C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * C * img_size * sizeof(float)));\n    cudaCheck(hipMalloc(&d_mean, B * n_groups * sizeof(float)));\n    cudaCheck(hipMalloc(&d_rstd, B * n_groups * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * C * img_size * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, B * C * img_size * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dweight, C * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dbias, C * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, B * C * img_size * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_weight, weight, C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_bias, bias, C * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, B * C * img_size * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemset(d_dx, 0, B * C * img_size * sizeof(float)));\n    cudaCheck(hipMemset(d_dweight, 0, C * sizeof(float)));\n    cudaCheck(hipMemset(d_dbias, 0, C * sizeof(float)));\n\n    printf(\"Checking forward pass\\n\");\n    float fwd_acc = 1e-1;\n    groupnorm_forward(d_x, d_weight, d_bias, d_out, d_mean, d_rstd, B, C, H, W, n_groups);\n    validate_result(d_out, out, \"out\", B * C * img_size, fwd_acc);\n    printf(\"Forward pass successful\\n\");\n\n    // check the values of rstd\n    float *rstd = (float*)malloc(B * n_groups * sizeof(float));\n\n    printf(\"Checking backward pass\\n\");\n    groupnorm_backward(d_dout, d_x, d_mean, d_rstd, d_weight, d_dx, d_dweight, d_dbias, B, C, H, W, n_groups);\n    float acc = 1e-3;\n    printf(\"Checking dbias\\n\");\n    validate_result(d_dbias, dbias, \"dbias\", C, acc);\n    printf(\"Checking dweight\\n\");\n    validate_result(d_dweight, dweight, \"dweight\", C, acc);\n    printf(\"Checking dx\\n\");\n    validate_result(d_dx, dx, \"dx\", B * C * img_size, 1.0f);\n    printf(\"Backward pass successful\\n\");\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks\\n\");\n    int repeat_times = 100;\n    float elapsed_time = benchmark_kernel(repeat_times, groupnorm_forward,\n                                            d_x, d_weight, d_bias, d_out, d_mean, d_rstd,\n                                            B, C, H, W, n_groups);\n    printf(\"time %.4f ms\\n\", elapsed_time);\n\n    printf(\"Backward pass benchmarks\\n\");\n    elapsed_time = benchmark_kernel(repeat_times, groupnorm_backward,\n                                    d_dout, d_x, d_mean, d_rstd, d_weight, d_dx, d_dweight, d_dbias,\n                                    B, C, H, W, n_groups);\n    printf(\"time %.4f ms\\n\", elapsed_time);\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/groupnorm.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <cuda_runtime.h>\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n#include \"broadcast.cuh\"\n\n// kernel to broadcast a tensor from shape (B, C) to (B, C, H, W)\n// Assuming one thread per pixel\n__global__ void broadcast_last_dims_forward_kernel(\n    const float* x,\n    float* out,\n    int N, int H, int W, int n_bk_per_img\n) {\n    int n = blockIdx.x / n_bk_per_img;\n    __shared__ float x_sh[1];\n    if (threadIdx.x == 0) {\n        x_sh[0] = x[n];\n    }\n    __syncthreads();\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N * H * W) {\n        out[idx] = x_sh[0];\n    }\n}\n\nvoid broadcast_last_dims_forward(\n    const float* x,\n    float* out,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    int n_bk_per_img = ceil_div(H * W, block_size);\n    broadcast_last_dims_forward_kernel<<<N * n_bk_per_img, block_size>>>(x, out, N, H, W, n_bk_per_img);\n};\n\n// backwards for broadcast\n// use one warp per image\n__global__ void broadcast_last_dims_backward_kernel(\n    const float* dout,\n    float* dx,\n    int N, int H, int W\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    int idx = blockIdx.x * warp.meta_group_size() + warp.meta_group_rank();\n    if (idx >= N) { return; }\n\n    int img_size = H * W;\n    dout += idx * img_size;\n    dx += idx;\n\n    float sum = 0.0f;\n    for (int i = warp.thread_rank(); i < img_size; i += warp.size()) {\n        sum += dout[i];\n    }\n    sum = cg::reduce(warp, sum, cg::plus<float>());\n    if (warp.thread_rank() == 0) {\n        dx[0] = sum;\n    }\n}\n\nvoid broadcast_last_dims_backward(\n    const float* dout,\n    float* dx,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    assert(block_size % 32 == 0);\n    int warps_per_block = block_size / 32;\n    int gridDim = ceil_div(N, warps_per_block);\n    broadcast_last_dims_backward_kernel<<<gridDim, block_size>>>(dout, dx, N, H, W);\n};\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 8 * 64;\n    int H = 16;\n    int W = 16;\n\n    // create host memory\n    float *x = (float*)malloc(N * sizeof(float));\n    float *out = (float*)malloc(N * H * W * sizeof(float));\n    float *dout = (float*)malloc(N * H * W * sizeof(float));\n    float *dx = (float*)malloc(N * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"broadcast.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(out, sizeof(float), N * H * W, file);\n    freadCheck(dout, sizeof(float), N * H * W, file);\n    freadCheck(dx, sizeof(float), N, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(cudaMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, N * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, N * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, N * H * W * sizeof(float), cudaMemcpyHostToDevice));\n\n    int block_sizes[] = {512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        broadcast_last_dims_forward(d_x, d_out, N, H, W, block_size);\n        validate_result(d_out, out, \"out\", N * H * W);\n    }\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        broadcast_last_dims_backward(d_dout, d_dx, N, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", N);\n    }\n\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, broadcast_last_dims_forward,\n            d_out, d_x, N, H, W, block_size);\n        \n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n    printf(\"Backward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, broadcast_last_dims_backward,\n            d_dout, d_dx, N, H, W, block_size);\n        \n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <hip/hip_runtime.h>\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include \"common.h\"\n#include \"broadcast.cuh\"\n\n// kernel to broadcast a tensor from shape (B, C) to (B, C, H, W)\n// Assuming one thread per pixel\n__global__ void broadcast_last_dims_forward_kernel(\n    const float* x,\n    float* out,\n    int N, int H, int W, int n_bk_per_img\n) {\n    int n = blockIdx.x / n_bk_per_img;\n    __shared__ float x_sh[1];\n    if (threadIdx.x == 0) {\n        x_sh[0] = x[n];\n    }\n    __syncthreads();\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < N * H * W) {\n        out[idx] = x_sh[0];\n    }\n}\n\nvoid broadcast_last_dims_forward(\n    const float* x,\n    float* out,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    int n_bk_per_img = ceil_div(H * W, block_size);\n    broadcast_last_dims_forward_kernel<<<N * n_bk_per_img, block_size>>>(x, out, N, H, W, n_bk_per_img);\n};\n\n// backwards for broadcast\n// use one warp per image\n__global__ void broadcast_last_dims_backward_kernel(\n    const float* dout,\n    float* dx,\n    int N, int H, int W\n) {\n    namespace cg = cooperative_groups;\n    cg::thread_block block = cg::this_thread_block();\n    cg::thread_block_tile<32> warp = cg::tiled_partition<32>(block);\n    int idx = blockIdx.x * warp.meta_group_size() + warp.meta_group_rank();\n    if (idx >= N) { return; }\n\n    int img_size = H * W;\n    dout += idx * img_size;\n    dx += idx;\n\n    float sum = 0.0f;\n    for (int i = warp.thread_rank(); i < img_size; i += warp.size()) {\n        sum += dout[i];\n    }\n    sum = cg::reduce(warp, sum, cg::plus<float>());\n    if (warp.thread_rank() == 0) {\n        dx[0] = sum;\n    }\n}\n\nvoid broadcast_last_dims_backward(\n    const float* dout,\n    float* dx,\n    int N, int H, int W, int block_size\n) {\n    block_size = min_int(block_size, H * W);\n    assert(block_size % 32 == 0);\n    int warps_per_block = block_size / 32;\n    int gridDim = ceil_div(N, warps_per_block);\n    broadcast_last_dims_backward_kernel<<<gridDim, block_size>>>(dout, dx, N, H, W);\n};\n\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    int N = 8 * 64;\n    int H = 16;\n    int W = 16;\n\n    // create host memory\n    float *x = (float*)malloc(N * sizeof(float));\n    float *out = (float*)malloc(N * H * W * sizeof(float));\n    float *dout = (float*)malloc(N * H * W * sizeof(float));\n    float *dx = (float*)malloc(N * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"broadcast.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), N, file);\n    freadCheck(out, sizeof(float), N * H * W, file);\n    freadCheck(dout, sizeof(float), N * H * W, file);\n    freadCheck(dx, sizeof(float), N, file);\n    fclose(file);\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(hipMalloc(&d_x, N * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, N * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, N * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, N * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, N * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, N * H * W * sizeof(float), hipMemcpyHostToDevice));\n\n    int block_sizes[] = {512};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        broadcast_last_dims_forward(d_x, d_out, N, H, W, block_size);\n        validate_result(d_out, out, \"out\", N * H * W);\n    }\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"\\nChecking block size %d\\n\", block_size);\n        broadcast_last_dims_backward(d_dout, d_dx, N, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", N);\n    }\n\n\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, broadcast_last_dims_forward,\n            d_out, d_x, N, H, W, block_size);\n        \n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n    printf(\"Backward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, broadcast_last_dims_backward,\n            d_dout, d_dx, N, H, W, block_size);\n        \n        printf(\"block_size %4d | time %.4f ms\\n\", block_size, elapsed_time);\n    }\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/broadcast.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"common.h\"\n#include \"upsample.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n// one thread per pixel\n__global__ void upsample_forward_kernel(\n    const float* x,\n    float* out,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx >= B * C * img_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_size);\n    int c = (flat_idx / img_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + i * W + j;\n    out += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float x_val = x[0];\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            out[ii * W_out + jj] = x_val;\n        }\n    }\n}\n\n\n// number of threads equals the number of pixels\n// in downsampled image\n__global__ void upsample_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_in_size = H * W;\n    if (flat_idx >= B * C * img_in_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_in_size);\n    int c = (flat_idx / img_in_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    dx += b * C * img_in_size + c * img_in_size + i * W + j;\n    dout += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float dout_sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dout_sum += dout[ii * W_out + jj];\n        }\n    }\n    dx[0] = dout_sum;\n}\n\n// ----------------------------------------------------------------------------\n// kernel launcher\nvoid upsample_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\nvoid upsample_backward1(\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H, W);\n    cudaCheck(cudaGetLastError());\n}\n\nvoid upsample_forward(\n    int kernel_num,\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            upsample_forward1(out, x, B, C, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            exit(1);\n    }\n}\n\nvoid upsample_backward(\n    int kernel_num,\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            upsample_backward1(dx, dout, B, C, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            exit(1);\n    }\n}\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 1;\n    int C = 64;\n    int H = 32;\n    int W = 32;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(cudaSetDevice(deviceIdx));\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C * H * W * sizeof(float));\n    float* out = (float*)malloc(B * C * H*2 * W*2 * sizeof(float));\n    float* dx = (float*)malloc(B * C * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C * H*2 * W*2 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"upsample.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * H * W, file);\n    freadCheck(out, sizeof(float), B * C * H*2 * W*2, file);\n    freadCheck(dout, sizeof(float), B * C * H*2 * W*2, file);\n    freadCheck(dx, sizeof(float), B * C * H * W, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(cudaMalloc(&d_x, B * C * H * W * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_out, B * C * H*2 * W*2 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dout, B * C * H*2 * W*2 * sizeof(float)));\n    cudaCheck(cudaMalloc(&d_dx, B * C * H * W * sizeof(float)));\n    cudaCheck(cudaMemcpy(d_x, x, B * C * H * W * sizeof(float), cudaMemcpyHostToDevice));\n    cudaCheck(cudaMemcpy(d_dout, dout, B * C * H*2 * W*2 * sizeof(float), cudaMemcpyHostToDevice));\n\n    // read kernel number from command line\n    int kernel_num = 1;\n    if (argc > 1) {\n        kernel_num = atoi(argv[1]);\n    }\n    printf(\"Running kernel %d\\n\", kernel_num);\n\n    int block_sizes[] = {32, 64, 128};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        upsample_forward(kernel_num, d_out, d_x, B, C, H, W, block_size);\n        validate_result(d_out, out, \"out\", B * C * (H/2) * (W/2));\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        upsample_backward(kernel_num, d_dx, d_dout, B, C, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", B * C * H * W);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, upsample_forward,\n                                              kernel_num, d_out, d_x,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, upsample_backward,\n                                              kernel_num, d_dx, d_dout,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n\n    cudaFree(d_x);\n    cudaFree(d_out);\n    cudaFree(d_dout);\n    cudaFree(d_dx);\n\n    return 0;\n}\n#endif", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"common.h\"\n#include \"upsample.cuh\"\n\n// ----------------------------------------------------------------------------\n// GPU kernels\n\n// one thread per pixel\n__global__ void upsample_forward_kernel(\n    const float* x,\n    float* out,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_size = H * W;\n    if (flat_idx >= B * C * img_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_size);\n    int c = (flat_idx / img_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    x += b * C * img_size + c * img_size + i * W + j;\n    out += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float x_val = x[0];\n\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            out[ii * W_out + jj] = x_val;\n        }\n    }\n}\n\n\n// number of threads equals the number of pixels\n// in downsampled image\n__global__ void upsample_backward_kernel(\n    const float* dout,\n    float* dx,\n    int B, int C, int H, int W\n) {\n    int flat_idx = blockDim.x * blockIdx.x + threadIdx.x;\n    int img_in_size = H * W;\n    if (flat_idx >= B * C * img_in_size) {\n        return;\n    }\n\n    int H_out = H*2;\n    int W_out = W*2;\n    int img_out_size = H_out * W_out;\n\n    int b = flat_idx / (C * img_in_size);\n    int c = (flat_idx / img_in_size) % C;\n    int i = (flat_idx / W) % H;\n    int j = flat_idx % W;\n\n    // move pointers\n    dx += b * C * img_in_size + c * img_in_size + i * W + j;\n    dout += b * C * img_out_size + c * img_out_size + 2 * i * W_out + 2 * j;\n\n    float dout_sum = 0.0f;\n    for (int ii = 0; ii < 2; ii++) {\n        for (int jj = 0; jj < 2; jj++) {\n            dout_sum += dout[ii * W_out + jj];\n        }\n    }\n    dx[0] = dout_sum;\n}\n\n// ----------------------------------------------------------------------------\n// kernel launcher\nvoid upsample_forward1(\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_forward_kernel<<<gridDim, block_size>>>(x, out, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\nvoid upsample_backward1(\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    int gridDim = ceil_div(B * C * H * W, block_size);\n    upsample_backward_kernel<<<gridDim, block_size>>>(dout, dx, B, C, H, W);\n    cudaCheck(hipGetLastError());\n}\n\nvoid upsample_forward(\n    int kernel_num,\n    float* out,\n    const float* x,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            upsample_forward1(out, x, B, C, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            exit(1);\n    }\n}\n\nvoid upsample_backward(\n    int kernel_num,\n    float* dx,\n    const float* dout,\n    int B, int C, int H, int W,\n    int block_size\n) {\n    switch (kernel_num) {\n        case 1:\n            upsample_backward1(dx, dout, B, C, H, W, block_size);\n            break;\n        default:\n            printf(\"Invalid kernel number\\n\");\n            exit(1);\n    }\n}\n// ----------------------------------------------------------------------------\n\n#ifndef LINKING\nint main(int argc, char **argv) {\n    srand(0);\n    int B = 1;\n    int C = 64;\n    int H = 32;\n    int W = 32;\n\n    // set up the device\n    int deviceIdx = 0;\n    cudaCheck(hipSetDevice(deviceIdx));\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, deviceIdx);\n    printf(\"Device %d: %s\\n\", deviceIdx, deviceProp.name);\n\n    // create host memory to load data\n    float* x = (float*)malloc(B * C * H * W * sizeof(float));\n    float* out = (float*)malloc(B * C * H*2 * W*2 * sizeof(float));\n    float* dx = (float*)malloc(B * C * H * W * sizeof(float));\n    float* dout = (float*)malloc(B * C * H*2 * W*2 * sizeof(float));\n\n    // read saved output\n    FILE *file = fopen(\"upsample.bin\", \"rb\");\n    if (!file) {\n        perror(\"Failed to load data\");\n        return -1;\n    }\n    freadCheck(x, sizeof(float), B * C * H * W, file);\n    freadCheck(out, sizeof(float), B * C * H*2 * W*2, file);\n    freadCheck(dout, sizeof(float), B * C * H*2 * W*2, file);\n    freadCheck(dx, sizeof(float), B * C * H * W, file);\n    fclose(file);\n\n\n    // allocate device memory\n    float *d_x, *d_out, *d_dout, *d_dx;\n    cudaCheck(hipMalloc(&d_x, B * C * H * W * sizeof(float)));\n    cudaCheck(hipMalloc(&d_out, B * C * H*2 * W*2 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dout, B * C * H*2 * W*2 * sizeof(float)));\n    cudaCheck(hipMalloc(&d_dx, B * C * H * W * sizeof(float)));\n    cudaCheck(hipMemcpy(d_x, x, B * C * H * W * sizeof(float), hipMemcpyHostToDevice));\n    cudaCheck(hipMemcpy(d_dout, dout, B * C * H*2 * W*2 * sizeof(float), hipMemcpyHostToDevice));\n\n    // read kernel number from command line\n    int kernel_num = 1;\n    if (argc > 1) {\n        kernel_num = atoi(argv[1]);\n    }\n    printf(\"Running kernel %d\\n\", kernel_num);\n\n    int block_sizes[] = {32, 64, 128};\n    printf(\"Checking forward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        upsample_forward(kernel_num, d_out, d_x, B, C, H, W, block_size);\n        validate_result(d_out, out, \"out\", B * C * (H/2) * (W/2));\n    }\n    printf(\"Forward pass: all results match\\n\\n\");\n\n    printf(\"Checking backward pass\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n        printf(\"Checking block size %d\\n\", block_size);\n        upsample_backward(kernel_num, d_dx, d_dout, B, C, H, W, block_size);\n        validate_result(d_dx, dx, \"dx\", B * C * H * W);\n    }\n    printf(\"Backward pass: all results match\\n\\n\");\n    printf(\"All results match. Starting benchmarks.\\n\\n\");\n    printf(\"Forward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, upsample_forward,\n                                              kernel_num, d_out, d_x,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    printf(\"\\nBackward pass benchmarks:\\n\");\n    for (int j = 0; j < sizeof(block_sizes) / sizeof(int); j++) {\n        int block_size = block_sizes[j];\n\n        int repeat_times = 100;\n        float elapsed_time = benchmark_kernel(repeat_times, upsample_backward,\n                                              kernel_num, d_dx, d_dout,\n                                              B, C, H, W, block_size);\n\n        float tflops = (float)B * H * W * C / elapsed_time * 1e3f / 1e12f;\n        printf(\"block_size %4d | time %.4f ms | tflops %.2f\\n\", block_size, elapsed_time, tflops);\n    }\n\n    // free memory\n    free(x);\n    free(out);\n    free(dout);\n    free(dx);\n\n    hipFree(d_x);\n    hipFree(d_out);\n    hipFree(d_dout);\n    hipFree(d_dx);\n\n    return 0;\n}\n#endif", "source": "/kaggle/working/unet.cu/dev/upsample.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 3 --experimental --amap --skip-excluded-preprocessor-conditional-blocks %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <stdio.h>\n\n#define NUM_WORK_ELEMENTS (512)\n#define WORK_BUFFER_SIZE (NUM_WORK_ELEMENTS * sizeof(float))\n#define NUM_NODES NUM_WORK_ELEMENTS\n#define NUM_FRAMES 1000\n\ntypedef struct {\n  int index;\n  float adder;\n} node_t;\n\ntypedef struct {\n  node_t *vector_table[NUM_NODES];\n  node_t nodes[NUM_NODES];\n} graph_control_t;\n\nfloat *host_in_p = nullptr;\nfloat *host_out_p = nullptr;\ngraph_control_t *host_gc_p = nullptr;\nfloat *dev_in_p = nullptr;\nfloat *dev_out_p = nullptr;\ngraph_control_t *dev_gc_p = nullptr;\nint global_index[NUM_NODES] = {0};\n\n// CHECK: hipGraph_t graph;\n// CHECK-NEXT: hipGraphExec_t graphExec;\n// CHECK-NEXT: hipGraphExec_t instance;\n// CHECK-NEXT: hipStream_t stream;\n// CHECK-NEXT: hipGraphNode_t node[NUM_NODES] = { 0 };\ncudaGraph_t graph;\ncudaGraphExec_t graphExec;\ncudaGraphExec_t instance;\ncudaStream_t stream;\ncudaGraphNode_t node[NUM_NODES] = { 0 };\n\n__global__\nvoid add(const void *index,\n         const graph_control_t *gc_p,\n         const float *a,\n         float *b) {\n  const int *node_index_p = static_cast<const int*>(index);\n  int node_index = *node_index_p;\n  float adder = 0.0f;\n  int i = threadIdx.x;\n  node_t *node_p =  gc_p->vector_table[node_index];\n  node_index = node_p->index;\n  adder = node_p->adder;\n  if (i == node_index) {\n    for (int j = 0; j < 100; ++j)\n      b[i] = a[i] + adder + node_index;\n  }\n}\n\nvoid init(void) {\n  int i = 0;\n  // CHECK: hipStreamCreate(&stream);\n  cudaStreamCreate(&stream);\n  host_in_p = (float*)malloc(sizeof(float)*WORK_BUFFER_SIZE);\n  host_out_p = (float*)malloc(sizeof(float)*WORK_BUFFER_SIZE);\n  host_gc_p = (graph_control_t*)malloc(sizeof(graph_control_t));\n  for (i = 0; i < WORK_BUFFER_SIZE; ++i) {\n    host_in_p[i] = 1;\n  }\n  for (i = 0; i < WORK_BUFFER_SIZE; ++i) {\n    host_out_p[i] = 42;\n  }\n  // CHECK: hipMalloc(&dev_in_p, WORK_BUFFER_SIZE);\n  // CHECK-NEXT: hipMalloc(&dev_out_p, WORK_BUFFER_SIZE);\n  // CHECK-NEXT: hipMalloc(&dev_gc_p, sizeof(graph_control_t));\n  cudaMalloc(&dev_in_p, WORK_BUFFER_SIZE);\n  cudaMalloc(&dev_out_p, WORK_BUFFER_SIZE);\n  cudaMalloc(&dev_gc_p, sizeof(graph_control_t));\n  for (i = 0; i < NUM_NODES; ++i) {\n    host_gc_p->nodes[i].adder = 0;\n    host_gc_p->nodes[i].index = i;\n    host_gc_p->vector_table[i] = &(dev_gc_p->nodes[i]);\n    global_index[i] = i;\n  }\n  // CHECK: if (hipGraphCreate(&graph, 0))\n  if (cudaGraphCreate(&graph, 0))\n    printf(\"Failed to create graph\\n\");\n\n  /*  Create one long vertical graph with dependency between each element\n      Would have parallel nodes and multiple instances of \n      it running in parallel if this was a real use-case,\n      but since that is not the point here, lets skip that.\n  */\n  for (i = 0; i < NUM_NODES; ++i) {\n    void *kargs[] = { &global_index[i],\n                      &dev_gc_p,\n                      &dev_in_p,\n                      &dev_out_p };\n    // CHECK: hipKernelNodeParams params = { .func           = (void*)add,\n    cudaKernelNodeParams params = { .func           = (void*)add,\n                                    .gridDim        = dim3(1, 1, 1),\n                                    .blockDim       = dim3(NUM_WORK_ELEMENTS, 1 ,1),\n                                    .sharedMemBytes = 0,\n                                    .kernelParams   = kargs,\n                                    .extra = NULL };\n    // CHECK: if (hipGraphAddKernelNode(&node[i],\n    if (cudaGraphAddKernelNode(&node[i],\n                               graph,\n                               0,\n                               0,\n                               &params))\n      printf(\"Failed to create kernel node\\n\");\n  }\n  for (i = 0; i < (NUM_NODES - 1); ++i)\n // [TODO][#2062] Rename all DO-NOT-CHECK back\n#if CUDA_VERSION < 13000\n    // DO-NOT-CHECK: hipGraphAddDependencies(graph,\n    cudaGraphAddDependencies(graph,\n                              &node[i],\n                              &node[i+1],\n                              1);\n#endif\n  // CHECK: hipGraphInstantiate(&graphExec,\n  cudaGraphInstantiate(&graphExec,\n                        graph,\n                        NULL,\n                        NULL,\n                        0);\n  /* Same input for dataplane every frame */\n  // CHECK: hipMemcpy(dev_in_p,\n  cudaMemcpy(dev_in_p,\n             host_in_p,\n             WORK_BUFFER_SIZE,\n  // CHECK: hipMemcpyHostToDevice);\n             cudaMemcpyHostToDevice);\n}\n\nvoid clean_up() {\n  free(host_in_p);\n  free(host_out_p);\n  free(host_gc_p);\n  // CHECK: hipFree(dev_in_p);\n  // CHECK-NEXT: hipFree(dev_out_p);\n  // CHECK-NEXT: hipFree(dev_gc_p);\n  // CHECK-NEXT: hipDeviceReset();\n  cudaFree(dev_in_p);\n  cudaFree(dev_out_p);\n  cudaFree(dev_gc_p);\n  cudaDeviceReset();\n}\n\nvoid graph_launch() {\n  // CHECK: hipGraphLaunch(graphExec, stream);\n  cudaGraphLaunch(graphExec, stream);\n}\n\n/* Set up graphs, update input, execute and read out output once per frame */\nint main (void) {\n  int i = 0;\n  /* Set up kernels (including parameters) and graphs once at init stage */\n  init();\n  /* Execute a bunch of frames */\n  for (int framenbr = 0; framenbr < NUM_FRAMES; ++framenbr) {\n    /* Set up control structure for one frame */\n    for(i = 0; i < NUM_NODES; ++i) {\n      host_gc_p->nodes[i].adder = framenbr * 1.0;\n    }\n    /* One copy for all data needed to execute one frame, queued in same stream as the rest */\n    // CHECK: hipMemcpyAsync(dev_gc_p,\n    cudaMemcpyAsync(dev_gc_p,\n                    host_gc_p,\n                    sizeof(graph_control_t),\n    // CHECK: hipMemcpyHostToDevice,\n                    cudaMemcpyHostToDevice,\n                    stream);\n    /* Kick graph */\n    graph_launch();\n    /*  One read-out for all data produced this frame, queued in same stream as the rest.   */\n    // CHECK: hipMemcpyAsync(host_out_p,\n    cudaMemcpyAsync(host_out_p,\n                        dev_out_p,\n                        WORK_BUFFER_SIZE,\n    // CHECK: hipMemcpyDeviceToHost,\n                        cudaMemcpyDeviceToHost,\n                        stream);\n\n    /*****************************************************************************************/\n    /*  Here, use the offloaded CPU core to do things involved in setting up the control and */ \n    /*  data plane info for next frame                                                       */\n    /*****************************************************************************************/\n\n    /* If still not done, wait for current frame to finish */\n    // CHECK: hipStreamSynchronize(stream);\n    cudaStreamSynchronize(stream);\n    /* Just some printouts to uncomment for quick sanity check of functional behaviour  */\n    printf(\"\\n\\nOutput buffer, frame %d:\\n\", framenbr);\n    for(i = 0; i < NUM_WORK_ELEMENTS; ++i) {\n      printf(\"%f \", host_out_p[i]);\n      if (i && i % 16 == 0)\n        printf(\"\\n\");\n    }\n  }\n  clean_up();\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 3 --experimental --amap --skip-excluded-preprocessor-conditional-blocks %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#define NUM_WORK_ELEMENTS (512)\n#define WORK_BUFFER_SIZE (NUM_WORK_ELEMENTS * sizeof(float))\n#define NUM_NODES NUM_WORK_ELEMENTS\n#define NUM_FRAMES 1000\n\ntypedef struct {\n  int index;\n  float adder;\n} node_t;\n\ntypedef struct {\n  node_t *vector_table[NUM_NODES];\n  node_t nodes[NUM_NODES];\n} graph_control_t;\n\nfloat *host_in_p = nullptr;\nfloat *host_out_p = nullptr;\ngraph_control_t *host_gc_p = nullptr;\nfloat *dev_in_p = nullptr;\nfloat *dev_out_p = nullptr;\ngraph_control_t *dev_gc_p = nullptr;\nint global_index[NUM_NODES] = {0};\n\n// CHECK: hipGraph_t graph;\n// CHECK-NEXT: hipGraphExec_t graphExec;\n// CHECK-NEXT: hipGraphExec_t instance;\n// CHECK-NEXT: hipStream_t stream;\n// CHECK-NEXT: hipGraphNode_t node[NUM_NODES] = { 0 };\nhipGraph_t graph;\nhipGraphExec_t graphExec;\nhipGraphExec_t instance;\nhipStream_t stream;\nhipGraphNode_t node[NUM_NODES] = { 0 };\n\n__global__\nvoid add(const void *index,\n         const graph_control_t *gc_p,\n         const float *a,\n         float *b) {\n  const int *node_index_p = static_cast<const int*>(index);\n  int node_index = *node_index_p;\n  float adder = 0.0f;\n  int i = threadIdx.x;\n  node_t *node_p =  gc_p->vector_table[node_index];\n  node_index = node_p->index;\n  adder = node_p->adder;\n  if (i == node_index) {\n    for (int j = 0; j < 100; ++j)\n      b[i] = a[i] + adder + node_index;\n  }\n}\n\nvoid init(void) {\n  int i = 0;\n  // CHECK: hipStreamCreate(&stream);\n  hipStreamCreate(&stream);\n  host_in_p = (float*)malloc(sizeof(float)*WORK_BUFFER_SIZE);\n  host_out_p = (float*)malloc(sizeof(float)*WORK_BUFFER_SIZE);\n  host_gc_p = (graph_control_t*)malloc(sizeof(graph_control_t));\n  for (i = 0; i < WORK_BUFFER_SIZE; ++i) {\n    host_in_p[i] = 1;\n  }\n  for (i = 0; i < WORK_BUFFER_SIZE; ++i) {\n    host_out_p[i] = 42;\n  }\n  // CHECK: hipMalloc(&dev_in_p, WORK_BUFFER_SIZE);\n  // CHECK-NEXT: hipMalloc(&dev_out_p, WORK_BUFFER_SIZE);\n  // CHECK-NEXT: hipMalloc(&dev_gc_p, sizeof(graph_control_t));\n  hipMalloc(&dev_in_p, WORK_BUFFER_SIZE);\n  hipMalloc(&dev_out_p, WORK_BUFFER_SIZE);\n  hipMalloc(&dev_gc_p, sizeof(graph_control_t));\n  for (i = 0; i < NUM_NODES; ++i) {\n    host_gc_p->nodes[i].adder = 0;\n    host_gc_p->nodes[i].index = i;\n    host_gc_p->vector_table[i] = &(dev_gc_p->nodes[i]);\n    global_index[i] = i;\n  }\n  // CHECK: if (hipGraphCreate(&graph, 0))\n  if (hipGraphCreate(&graph, 0))\n    printf(\"Failed to create graph\\n\");\n\n  /*  Create one long vertical graph with dependency between each element\n      Would have parallel nodes and multiple instances of \n      it running in parallel if this was a real use-case,\n      but since that is not the point here, lets skip that.\n  */\n  for (i = 0; i < NUM_NODES; ++i) {\n    void *kargs[] = { &global_index[i],\n                      &dev_gc_p,\n                      &dev_in_p,\n                      &dev_out_p };\n    // CHECK: hipKernelNodeParams params = { .func           = (void*)add,\n    hipKernelNodeParams params = { .func           = (void*)add,\n                                    .gridDim        = dim3(1, 1, 1),\n                                    .blockDim       = dim3(NUM_WORK_ELEMENTS, 1 ,1),\n                                    .sharedMemBytes = 0,\n                                    .kernelParams   = kargs,\n                                    .extra = NULL };\n    // CHECK: if (hipGraphAddKernelNode(&node[i],\n    if (hipGraphAddKernelNode(&node[i],\n                               graph,\n                               0,\n                               0,\n                               &params))\n      printf(\"Failed to create kernel node\\n\");\n  }\n  for (i = 0; i < (NUM_NODES - 1); ++i)\n // [TODO][#2062] Rename all DO-NOT-CHECK back\n#if CUDA_VERSION < 13000\n    // DO-NOT-CHECK: hipGraphAddDependencies(graph,\n    hipGraphAddDependencies(graph,\n                              &node[i],\n                              &node[i+1],\n                              1);\n#endif\n  // CHECK: hipGraphInstantiate(&graphExec,\n  hipGraphInstantiate(&graphExec,\n                        graph,\n                        NULL,\n                        NULL,\n                        0);\n  /* Same input for dataplane every frame */\n  // CHECK: hipMemcpy(dev_in_p,\n  hipMemcpy(dev_in_p,\n             host_in_p,\n             WORK_BUFFER_SIZE,\n  // CHECK: hipMemcpyHostToDevice);\n             hipMemcpyHostToDevice);\n}\n\nvoid clean_up() {\n  free(host_in_p);\n  free(host_out_p);\n  free(host_gc_p);\n  // CHECK: hipFree(dev_in_p);\n  // CHECK-NEXT: hipFree(dev_out_p);\n  // CHECK-NEXT: hipFree(dev_gc_p);\n  // CHECK-NEXT: hipDeviceReset();\n  hipFree(dev_in_p);\n  hipFree(dev_out_p);\n  hipFree(dev_gc_p);\n  hipDeviceReset();\n}\n\nvoid graph_launch() {\n  // CHECK: hipGraphLaunch(graphExec, stream);\n  hipGraphLaunch(graphExec, stream);\n}\n\n/* Set up graphs, update input, execute and read out output once per frame */\nint main (void) {\n  int i = 0;\n  /* Set up kernels (including parameters) and graphs once at init stage */\n  init();\n  /* Execute a bunch of frames */\n  for (int framenbr = 0; framenbr < NUM_FRAMES; ++framenbr) {\n    /* Set up control structure for one frame */\n    for(i = 0; i < NUM_NODES; ++i) {\n      host_gc_p->nodes[i].adder = framenbr * 1.0;\n    }\n    /* One copy for all data needed to execute one frame, queued in same stream as the rest */\n    // CHECK: hipMemcpyAsync(dev_gc_p,\n    hipMemcpyAsync(dev_gc_p,\n                    host_gc_p,\n                    sizeof(graph_control_t),\n    // CHECK: hipMemcpyHostToDevice,\n                    hipMemcpyHostToDevice,\n                    stream);\n    /* Kick graph */\n    graph_launch();\n    /*  One read-out for all data produced this frame, queued in same stream as the rest.   */\n    // CHECK: hipMemcpyAsync(host_out_p,\n    hipMemcpyAsync(host_out_p,\n                        dev_out_p,\n                        WORK_BUFFER_SIZE,\n    // CHECK: hipMemcpyDeviceToHost,\n                        hipMemcpyDeviceToHost,\n                        stream);\n\n    /*****************************************************************************************/\n    /*  Here, use the offloaded CPU core to do things involved in setting up the control and */ \n    /*  data plane info for next frame                                                       */\n    /*****************************************************************************************/\n\n    /* If still not done, wait for current frame to finish */\n    // CHECK: hipStreamSynchronize(stream);\n    hipStreamSynchronize(stream);\n    /* Just some printouts to uncomment for quick sanity check of functional behaviour  */\n    printf(\"\\n\\nOutput buffer, frame %d:\\n\", framenbr);\n    for(i = 0; i < NUM_WORK_ELEMENTS; ++i) {\n      printf(\"%f \", host_out_p[i]);\n      if (i && i % 16 == 0)\n        printf(\"\\n\");\n    }\n  }\n  clean_up();\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/graph/simple_mechs.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    cudaMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    cudaMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    hipMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    hipMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/options/kernel-execution-syntax/none-kernel-execution-syntax.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    cudaMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    cudaMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(add, dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    hipMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    hipMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(add, dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/options/kernel-execution-syntax/hip-kernel-execution-syntax.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --cuda-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    cudaMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    cudaMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --cuda-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    hipMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    hipMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/options/kernel-execution-syntax/cuda-kernel-execution-syntax.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --cuda-kernel-execution-syntax --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    cudaMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    cudaMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(add, dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --cuda-kernel-execution-syntax --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <math.h>\n\n__global__\nvoid add(int n, float *x, float *y)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = blockDim.x * gridDim.x;\n    for (int i = index; i < n; i += stride)\n        y[i] = x[i] + y[i];\n}\n\nint main(int argc, char *argv[])\n{\n    int numElements = 10;\n    bool testResult = true;\n    float *A, *B;\n    // CHECK: hipMallocManaged(&A, numElements * sizeof(float));\n    hipMallocManaged(&A, numElements * sizeof(float));\n    // CHECK: hipMallocManaged(&B, numElements * sizeof(float));\n    hipMallocManaged(&B, numElements * sizeof(float));\n    for (int i = 0; i < numElements; i++) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 256;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(add, dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; i++)\n        maxError = fmax(maxError, fabs(B[i]-3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if(maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/options/kernel-execution-syntax/both-kernel-execution-syntax.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 3 --amap --skip-excluded-preprocessor-conditional-blocks --experimental %clang_args -D__CUDA_API_VERSION_INTERNAL\n\n// CHECK: #include <hip/hip_runtime_api.h>\n// CHECK-NEXT: #include <hip/device_functions.h>\n// CHECK-NEXT: #include <hip/hip_math_constants.h>\n#include <cuda_runtime_api.h>\n#include <device_functions.h>\n#include <math_constants.h>\n#include <stdio.h>\n\n  __global__ __constant__ float INF_F;\n  __global__ __constant__ float NAN_F;\n  __global__ __constant__ float MIN_DENORM_F;\n  __global__ __constant__ float MAX_NORMAL_F;\n  __global__ __constant__ float NEG_ZERO_F;\n  __global__ __constant__ float ZERO_F;\n  __global__ __constant__ float ONE_F;\n  __global__ __constant__ float SQRT_HALF_F;\n  __global__ __constant__ float SQRT_HALF_HI_F;\n  __global__ __constant__ float SQRT_HALF_LO_F;\n  __global__ __constant__ float SQRT_TWO_F;\n  __global__ __constant__ float THIRD_F;\n  __global__ __constant__ float PIO4_F;\n  __global__ __constant__ float PIO2_F;\n  __global__ __constant__ float _3PIO4_F;\n  __global__ __constant__ float _2_OVER_PI_F;\n  __global__ __constant__ float SQRT_2_OVER_PI_F;\n  __global__ __constant__ float PI_F;\n  __global__ __constant__ float L2E_F;\n  __global__ __constant__ float L2T_F;\n  __global__ __constant__ float LG2_F;\n  __global__ __constant__ float LGE_F;\n  __global__ __constant__ float LN2_F;\n  __global__ __constant__ float LNT_F;\n  __global__ __constant__ float LNPI_F;\n  __global__ __constant__ float TWO_TO_M126_F;\n  __global__ __constant__ float TWO_TO_126_F;\n  __global__ __constant__ float NORM_HUGE_F;\n  __global__ __constant__ float TWO_TO_23_F;\n  __global__ __constant__ float TWO_TO_24_F;\n  __global__ __constant__ float TWO_TO_31_F;\n  __global__ __constant__ float TWO_TO_32_F;\n  __global__ __constant__ float REMQUO_BITS_F;\n  __global__ __constant__ float REMQUO_MASK_F;\n  __global__ __constant__ double TRIG_PLOSS_F;\n  __global__ __constant__ double INF;\n  __global__ __constant__ double NAN_;\n  __global__ __constant__ double NEG_ZERO;\n  __global__ __constant__ double MIN_DENORM;\n  __global__ __constant__ double ZERO;\n  __global__ __constant__ double ONE;\n  __global__ __constant__ double SQRT_TWO;\n  __global__ __constant__ double SQRT_HALF;\n  __global__ __constant__ double SQRT_HALF_HI;\n  __global__ __constant__ double SQRT_HALF_LO;\n  __global__ __constant__ double THIRD;\n  __global__ __constant__ double TWOTHIRD;\n  __global__ __constant__ double PIO4;\n  __global__ __constant__ double PIO4_HI;\n  __global__ __constant__ double PIO4_LO;\n  __global__ __constant__ double PIO2;\n  __global__ __constant__ double PIO2_HI;\n  __global__ __constant__ double PIO2_LO;\n  __global__ __constant__ double _3PIO4;\n  __global__ __constant__ double _2_OVER_PI;\n  __global__ __constant__ double _PI;\n  __global__ __constant__ double PI_HI;\n  __global__ __constant__ double PI_LO;\n  __global__ __constant__ double SQRT_2PI;\n  __global__ __constant__ double SQRT_2PI_HI;\n  __global__ __constant__ double SQRT_2PI_LO;\n  __global__ __constant__ double SQRT_PIO2;\n  __global__ __constant__ double SQRT_PIO2_HI;\n  __global__ __constant__ double SQRT_PIO2_LO;\n  __global__ __constant__ double SQRT_2OPI;\n  __global__ __constant__ double L2E;\n  __global__ __constant__ double L2E_HI;\n  __global__ __constant__ double L2E_LO;\n  __global__ __constant__ double L2T;\n  __global__ __constant__ double LG2;\n  __global__ __constant__ double LG2_HI;\n  __global__ __constant__ double LG2_LO;\n  __global__ __constant__ double LGE;\n  __global__ __constant__ double LGE_HI;\n  __global__ __constant__ double LGE_LO;\n  __global__ __constant__ double LN2;\n  __global__ __constant__ double LN2_HI;\n  __global__ __constant__ double LN2_LO;\n  __global__ __constant__ double LNT;\n  __global__ __constant__ double LNT_HI;\n  __global__ __constant__ double LNT_LO;\n  __global__ __constant__ double LNPI;\n  __global__ __constant__ double LN2_X_1024;\n  __global__ __constant__ double LN2_X_1025;\n  __global__ __constant__ double LN2_X_1075;\n  __global__ __constant__ double LG2_X_1024;\n  __global__ __constant__ double LG2_X_1075;\n  __global__ __constant__ double TWO_TO_23;\n  __global__ __constant__ double TWO_TO_52;\n  __global__ __constant__ double TWO_TO_53;\n  __global__ __constant__ double TWO_TO_54;\n  __global__ __constant__ double TWO_TO_M54;\n  __global__ __constant__ double TWO_TO_M1022;\n  __global__ __constant__ double TRIG_PLOSS;\n  __global__ __constant__ double DBL2INT_CVT;\n\n__global__ void init() {\n  // CHECK: INF_F = HIP_INF_F;\n  // CHECK-NEXT: NAN_F = HIP_NAN_F;\n  // CHECK-NEXT: MIN_DENORM_F = HIP_MIN_DENORM_F;\n  // CHECK-NEXT: MAX_NORMAL_F = HIP_MAX_NORMAL_F;\n  // CHECK-NEXT: NEG_ZERO_F = HIP_NEG_ZERO_F;\n  // CHECK-NEXT: ZERO_F = HIP_ZERO_F;\n  // CHECK-NEXT: ONE_F = HIP_ONE_F;\n  // CHECK-NEXT: SQRT_HALF_F = HIP_SQRT_HALF_F;\n  // CHECK-NEXT: SQRT_HALF_HI_F = HIP_SQRT_HALF_HI_F;\n  // CHECK-NEXT: SQRT_HALF_LO_F = HIP_SQRT_HALF_LO_F;\n  // CHECK-NEXT: SQRT_TWO_F = HIP_SQRT_TWO_F;\n  // CHECK-NEXT: THIRD_F = HIP_THIRD_F;\n  // CHECK-NEXT: PIO4_F = HIP_PIO4_F;\n  // CHECK-NEXT: PIO2_F = HIP_PIO2_F;\n  // CHECK-NEXT: _3PIO4_F = HIP_3PIO4_F;\n  // CHECK-NEXT: _2_OVER_PI_F = HIP_2_OVER_PI_F;\n  // CHECK-NEXT: SQRT_2_OVER_PI_F = HIP_SQRT_2_OVER_PI_F;\n  // CHECK-NEXT: PI_F = HIP_PI_F;\n  // CHECK-NEXT: L2E_F = HIP_L2E_F;\n  // CHECK-NEXT: L2T_F = HIP_L2T_F;\n  // CHECK-NEXT: LG2_F = HIP_LG2_F;\n  // CHECK-NEXT: LGE_F = HIP_LGE_F;\n  // CHECK-NEXT: LN2_F = HIP_LN2_F;\n  // CHECK-NEXT: LNT_F = HIP_LNT_F;\n  // CHECK-NEXT: LNPI_F = HIP_LNPI_F;\n  // CHECK-NEXT: TWO_TO_M126_F = HIP_TWO_TO_M126_F;\n  // CHECK-NEXT: TWO_TO_126_F = HIP_TWO_TO_126_F;\n  // CHECK-NEXT: NORM_HUGE_F = HIP_NORM_HUGE_F;\n  // CHECK-NEXT: TWO_TO_23_F = HIP_TWO_TO_23_F;\n  // CHECK-NEXT: TWO_TO_24_F = HIP_TWO_TO_24_F;\n  // CHECK-NEXT: TWO_TO_31_F = HIP_TWO_TO_31_F;\n  // CHECK-NEXT: TWO_TO_32_F = HIP_TWO_TO_32_F;\n  // CHECK-NEXT: REMQUO_BITS_F = HIP_REMQUO_BITS_F;\n  // CHECK-NEXT: REMQUO_MASK_F = HIP_REMQUO_MASK_F;\n  // CHECK-NEXT: TRIG_PLOSS_F = HIP_TRIG_PLOSS_F;\n  // CHECK-NEXT: INF = HIP_INF;\n  // CHECK-NEXT: NAN_ = HIP_NAN;\n  // CHECK-NEXT: NEG_ZERO = HIP_NEG_ZERO;\n  // CHECK-NEXT: MIN_DENORM = HIP_MIN_DENORM;\n  // CHECK-NEXT: ZERO = HIP_ZERO;\n  // CHECK-NEXT: ONE = HIP_ONE;\n  // CHECK-NEXT: SQRT_TWO = HIP_SQRT_TWO;\n  // CHECK-NEXT: SQRT_HALF = HIP_SQRT_HALF;\n  // CHECK-NEXT: SQRT_HALF_HI = HIP_SQRT_HALF_HI;\n  // CHECK-NEXT: SQRT_HALF_LO = HIP_SQRT_HALF_LO;\n  // CHECK-NEXT: THIRD = HIP_THIRD;\n  // CHECK-NEXT: TWOTHIRD = HIP_TWOTHIRD;\n  // CHECK-NEXT: PIO4 = HIP_PIO4;\n  // CHECK-NEXT: PIO4_HI = HIP_PIO4_HI;\n  // CHECK-NEXT: PIO4_LO = HIP_PIO4_LO;\n  // CHECK-NEXT: PIO2 = HIP_PIO2;\n  // CHECK-NEXT: PIO2_HI = HIP_PIO2_HI;\n  // CHECK-NEXT: PIO2_LO = HIP_PIO2_LO;\n  // CHECK-NEXT: _3PIO4 = HIP_3PIO4;\n  // CHECK-NEXT: _2_OVER_PI = HIP_2_OVER_PI;\n  // CHECK-NEXT: _PI = HIP_PI;\n  // CHECK-NEXT: PI_HI = HIP_PI_HI;\n  // CHECK-NEXT: PI_LO = HIP_PI_LO;\n  // CHECK-NEXT: SQRT_2PI = HIP_SQRT_2PI;\n  // CHECK-NEXT: SQRT_2PI_HI = HIP_SQRT_2PI_HI;\n  // CHECK-NEXT: SQRT_2PI_LO = HIP_SQRT_2PI_LO;\n  // CHECK-NEXT: SQRT_PIO2 = HIP_SQRT_PIO2;\n  // CHECK-NEXT: SQRT_PIO2_HI = HIP_SQRT_PIO2_HI;\n  // CHECK-NEXT: SQRT_PIO2_LO = HIP_SQRT_PIO2_LO;\n  // CHECK-NEXT: SQRT_2OPI = HIP_SQRT_2OPI;\n  // CHECK-NEXT: L2E = HIP_L2E;\n  // CHECK-NEXT: L2E_HI = HIP_L2E_HI;\n  // CHECK-NEXT: L2E_LO = HIP_L2E_LO;\n  // CHECK-NEXT: L2T = HIP_L2T;\n  // CHECK-NEXT: LG2 = HIP_LG2;\n  // CHECK-NEXT: LG2_HI = HIP_LG2_HI;\n  // CHECK-NEXT: LG2_LO = HIP_LG2_LO;\n  // CHECK-NEXT: LGE = HIP_LGE;\n  // CHECK-NEXT: LGE_HI = HIP_LGE_HI;\n  // CHECK-NEXT: LGE_LO = HIP_LGE_LO;\n  // CHECK-NEXT: LN2 = HIP_LN2;\n  // CHECK-NEXT: LN2_HI = HIP_LN2_HI;\n  // CHECK-NEXT: LN2_LO = HIP_LN2_LO;\n  // CHECK-NEXT: LNT = HIP_LNT;\n  // CHECK-NEXT: LNT_HI = HIP_LNT_HI;\n  // CHECK-NEXT: LNT_LO = HIP_LNT_LO;\n  // CHECK-NEXT: LNPI = HIP_LNPI;\n  // CHECK-NEXT: LN2_X_1024 = HIP_LN2_X_1024;\n  // CHECK-NEXT: LN2_X_1025 = HIP_LN2_X_1025;\n  // CHECK-NEXT: LN2_X_1075 = HIP_LN2_X_1075;\n  // CHECK-NEXT: LG2_X_1024 = HIP_LG2_X_1024;\n  // CHECK-NEXT: LG2_X_1075 = HIP_LG2_X_1075;\n  // CHECK-NEXT: TWO_TO_23 = HIP_TWO_TO_23;\n  // CHECK-NEXT: TWO_TO_52 = HIP_TWO_TO_52;\n  // CHECK-NEXT: TWO_TO_53 = HIP_TWO_TO_53;\n  // CHECK-NEXT: TWO_TO_54 = HIP_TWO_TO_54;\n  // CHECK-NEXT: TWO_TO_M54 = HIP_TWO_TO_M54;\n  // CHECK-NEXT: TWO_TO_M1022 = HIP_TWO_TO_M1022;\n  // CHECK-NEXT: TRIG_PLOSS = HIP_TRIG_PLOSS;\n  // CHECK-NEXT: DBL2INT_CVT = HIP_DBL2INT_CVT;\n  INF_F = CUDART_INF_F;\n  NAN_F = CUDART_NAN_F;\n  MIN_DENORM_F = CUDART_MIN_DENORM_F;\n  MAX_NORMAL_F = CUDART_MAX_NORMAL_F;\n  NEG_ZERO_F = CUDART_NEG_ZERO_F;\n  ZERO_F = CUDART_ZERO_F;\n  ONE_F = CUDART_ONE_F;\n  SQRT_HALF_F = CUDART_SQRT_HALF_F;\n  SQRT_HALF_HI_F = CUDART_SQRT_HALF_HI_F;\n  SQRT_HALF_LO_F = CUDART_SQRT_HALF_LO_F;\n  SQRT_TWO_F = CUDART_SQRT_TWO_F;\n  THIRD_F = CUDART_THIRD_F;\n  PIO4_F = CUDART_PIO4_F;\n  PIO2_F = CUDART_PIO2_F;\n  _3PIO4_F = CUDART_3PIO4_F;\n  _2_OVER_PI_F = CUDART_2_OVER_PI_F;\n  SQRT_2_OVER_PI_F = CUDART_SQRT_2_OVER_PI_F;\n  PI_F = CUDART_PI_F;\n  L2E_F = CUDART_L2E_F;\n  L2T_F = CUDART_L2T_F;\n  LG2_F = CUDART_LG2_F;\n  LGE_F = CUDART_LGE_F;\n  LN2_F = CUDART_LN2_F;\n  LNT_F = CUDART_LNT_F;\n  LNPI_F = CUDART_LNPI_F;\n  TWO_TO_M126_F = CUDART_TWO_TO_M126_F;\n  TWO_TO_126_F = CUDART_TWO_TO_126_F;\n  NORM_HUGE_F = CUDART_NORM_HUGE_F;\n  TWO_TO_23_F = CUDART_TWO_TO_23_F;\n  TWO_TO_24_F = CUDART_TWO_TO_24_F;\n  TWO_TO_31_F = CUDART_TWO_TO_31_F;\n  TWO_TO_32_F = CUDART_TWO_TO_32_F;\n  REMQUO_BITS_F = CUDART_REMQUO_BITS_F;\n  REMQUO_MASK_F = CUDART_REMQUO_MASK_F;\n  TRIG_PLOSS_F = CUDART_TRIG_PLOSS_F;\n  INF = CUDART_INF;\n  NAN_ = CUDART_NAN;\n  NEG_ZERO = CUDART_NEG_ZERO;\n  MIN_DENORM = CUDART_MIN_DENORM;\n  ZERO = CUDART_ZERO;\n  ONE = CUDART_ONE;\n  SQRT_TWO = CUDART_SQRT_TWO;\n  SQRT_HALF = CUDART_SQRT_HALF;\n  SQRT_HALF_HI = CUDART_SQRT_HALF_HI;\n  SQRT_HALF_LO = CUDART_SQRT_HALF_LO;\n  THIRD = CUDART_THIRD;\n  TWOTHIRD = CUDART_TWOTHIRD;\n  PIO4 = CUDART_PIO4;\n  PIO4_HI = CUDART_PIO4_HI;\n  PIO4_LO = CUDART_PIO4_LO;\n  PIO2 = CUDART_PIO2;\n  PIO2_HI = CUDART_PIO2_HI;\n  PIO2_LO = CUDART_PIO2_LO;\n  _3PIO4 = CUDART_3PIO4;\n  _2_OVER_PI = CUDART_2_OVER_PI;\n  _PI = CUDART_PI;\n  PI_HI = CUDART_PI_HI;\n  PI_LO = CUDART_PI_LO;\n  SQRT_2PI = CUDART_SQRT_2PI;\n  SQRT_2PI_HI = CUDART_SQRT_2PI_HI;\n  SQRT_2PI_LO = CUDART_SQRT_2PI_LO;\n  SQRT_PIO2 = CUDART_SQRT_PIO2;\n  SQRT_PIO2_HI = CUDART_SQRT_PIO2_HI;\n  SQRT_PIO2_LO = CUDART_SQRT_PIO2_LO;\n  SQRT_2OPI = CUDART_SQRT_2OPI;\n  L2E = CUDART_L2E;\n  L2E_HI = CUDART_L2E_HI;\n  L2E_LO = CUDART_L2E_LO;\n  L2T = CUDART_L2T;\n  LG2 = CUDART_LG2;\n  LG2_HI = CUDART_LG2_HI;\n  LG2_LO = CUDART_LG2_LO;\n  LGE = CUDART_LGE;\n  LGE_HI = CUDART_LGE_HI;\n  LGE_LO = CUDART_LGE_LO;\n  LN2 = CUDART_LN2;\n  LN2_HI = CUDART_LN2_HI;\n  LN2_LO = CUDART_LN2_LO;\n  LNT = CUDART_LNT;\n  LNT_HI = CUDART_LNT_HI;\n  LNT_LO = CUDART_LNT_LO;\n  LNPI = CUDART_LNPI;\n  LN2_X_1024 = CUDART_LN2_X_1024;\n  LN2_X_1025 = CUDART_LN2_X_1025;\n  LN2_X_1075 = CUDART_LN2_X_1075;\n  LG2_X_1024 = CUDART_LG2_X_1024;\n  LG2_X_1075 = CUDART_LG2_X_1075;\n  TWO_TO_23 = CUDART_TWO_TO_23;\n  TWO_TO_52 = CUDART_TWO_TO_52;\n  TWO_TO_53 = CUDART_TWO_TO_53;\n  TWO_TO_54 = CUDART_TWO_TO_54;\n  TWO_TO_M54 = CUDART_TWO_TO_M54;\n  TWO_TO_M1022 = CUDART_TWO_TO_M1022;\n  TRIG_PLOSS = CUDART_TRIG_PLOSS;\n  DBL2INT_CVT = CUDART_DBL2INT_CVT;\n}\n\nint main() {\n  printf(\"08. CUDA Runtime API Defines synthetic test\\n\");\n\n  // CHECK: int IPC_HANDLE_SIZE = HIP_IPC_HANDLE_SIZE;\n  // CHECK-NEXT: int ArrayDefault = hipArrayDefault;\n  // CHECK-NEXT: int ArrayLayered = hipArrayLayered;\n  // CHECK-NEXT: int ArraySurfaceLoadStore = hipArraySurfaceLoadStore;\n  // CHECK-NEXT: int ArrayCubemap = hipArrayCubemap;\n  // CHECK-NEXT: int ArrayTextureGather = hipArrayTextureGather;\n  // CHECK-NEXT: int DeviceBlockingSync = hipDeviceScheduleBlockingSync;\n  // CHECK-NEXT: int DeviceLmemResizeToMax = hipDeviceLmemResizeToMax;\n  // CHECK-NEXT: int DeviceMapHost = hipDeviceMapHost;\n  // CHECK-NEXT: int DeviceScheduleAuto = hipDeviceScheduleAuto;\n  // CHECK-NEXT: int DeviceScheduleSpin = hipDeviceScheduleSpin;\n  // CHECK-NEXT: int DeviceScheduleYield = hipDeviceScheduleYield;\n  // CHECK-NEXT: int DeviceScheduleBlockingSync = hipDeviceScheduleBlockingSync;\n  // CHECK-NEXT: int DeviceScheduleMask = hipDeviceScheduleMask;\n  // CHECK-NEXT: int EventDefault = hipEventDefault;\n  // CHECK-NEXT: int EventBlockingSync = hipEventBlockingSync;\n  // CHECK-NEXT: int EventDisableTiming = hipEventDisableTiming;\n  // CHECK-NEXT: int EventInterprocess = hipEventInterprocess;\n  // CHECK-NEXT: int HostAllocDefault = hipHostMallocDefault;\n  // CHECK-NEXT: int HostAllocPortable = hipHostMallocPortable;\n  // CHECK-NEXT: int HostAllocMapped = hipHostMallocMapped;\n  // CHECK-NEXT: int HostAllocWriteCombined = hipHostMallocWriteCombined;\n  // CHECK-NEXT: int HostRegisterDefault = hipHostRegisterDefault;\n  // CHECK-NEXT: int HostRegisterPortable = hipHostRegisterPortable;\n  // CHECK-NEXT: int HostRegisterMapped = hipHostRegisterMapped;\n  // CHECK-NEXT: int IpcMemLazyEnablePeerAccess = hipIpcMemLazyEnablePeerAccess;\n  // CHECK-NEXT: int MemAttachGlobal = hipMemAttachGlobal;\n  // CHECK-NEXT: int MemAttachHost = hipMemAttachHost;\n  // CHECK-NEXT: int MemAttachSingle = hipMemAttachSingle;\n  // CHECK-NEXT: int TextureType1D = hipTextureType1D;\n  // CHECK-NEXT: int TextureType2D = hipTextureType2D;\n  // CHECK-NEXT: int TextureType3D = hipTextureType3D;\n  // CHECK-NEXT: int TextureTypeCubemap = hipTextureTypeCubemap;\n  // CHECK-NEXT: int TextureType1DLayered = hipTextureType1DLayered;\n  // CHECK-NEXT: int TextureType2DLayered = hipTextureType2DLayered;\n  // CHECK-NEXT: int TextureTypeCubemapLayered = hipTextureTypeCubemapLayered;\n  // CHECK-NEXT: int OccupancyDefault = hipOccupancyDefault;\n  // CHECK-NEXT: int OccupancyDisableCachingOverride = hipOccupancyDisableCachingOverride;\n  // CHECK-NEXT: int StreamDefault = hipStreamDefault;\n  // CHECK-NEXT: int StreamNonBlocking = hipStreamNonBlocking;\n  // CHECK-NEXT: hipStream_t StreamPerThread = hipStreamPerThread;\n  int IPC_HANDLE_SIZE = CUDA_IPC_HANDLE_SIZE;\n  int ArrayDefault = cudaArrayDefault;\n  int ArrayLayered = cudaArrayLayered;\n  int ArraySurfaceLoadStore = cudaArraySurfaceLoadStore;\n  int ArrayCubemap = cudaArrayCubemap;\n  int ArrayTextureGather = cudaArrayTextureGather;\n  int DeviceBlockingSync = cudaDeviceBlockingSync;\n  int DeviceLmemResizeToMax = cudaDeviceLmemResizeToMax;\n  int DeviceMapHost = cudaDeviceMapHost;\n  int DeviceScheduleAuto = cudaDeviceScheduleAuto;\n  int DeviceScheduleSpin = cudaDeviceScheduleSpin;\n  int DeviceScheduleYield = cudaDeviceScheduleYield;\n  int DeviceScheduleBlockingSync = cudaDeviceScheduleBlockingSync;\n  int DeviceScheduleMask = cudaDeviceScheduleMask;\n  int EventDefault = cudaEventDefault;\n  int EventBlockingSync = cudaEventBlockingSync;\n  int EventDisableTiming = cudaEventDisableTiming;\n  int EventInterprocess = cudaEventInterprocess;\n  int HostAllocDefault = cudaHostAllocDefault;\n  int HostAllocPortable = cudaHostAllocPortable;\n  int HostAllocMapped = cudaHostAllocMapped;\n  int HostAllocWriteCombined = cudaHostAllocWriteCombined;\n  int HostRegisterDefault = cudaHostRegisterDefault;\n  int HostRegisterPortable = cudaHostRegisterPortable;\n  int HostRegisterMapped = cudaHostRegisterMapped;\n  int IpcMemLazyEnablePeerAccess = cudaIpcMemLazyEnablePeerAccess;\n  int MemAttachGlobal = cudaMemAttachGlobal;\n  int MemAttachHost = cudaMemAttachHost;\n  int MemAttachSingle = cudaMemAttachSingle;\n  int TextureType1D = cudaTextureType1D;\n  int TextureType2D = cudaTextureType2D;\n  int TextureType3D = cudaTextureType3D;\n  int TextureTypeCubemap = cudaTextureTypeCubemap;\n  int TextureType1DLayered = cudaTextureType1DLayered;\n  int TextureType2DLayered = cudaTextureType2DLayered;\n  int TextureTypeCubemapLayered = cudaTextureTypeCubemapLayered;\n  int OccupancyDefault = cudaOccupancyDefault;\n  int OccupancyDisableCachingOverride = cudaOccupancyDisableCachingOverride;\n  int StreamDefault = cudaStreamDefault;\n  int StreamNonBlocking = cudaStreamNonBlocking;\n  cudaStream_t StreamPerThread = cudaStreamPerThread;\n\n#if CUDA_VERSION >= 7050\n  // CHECK: int HostRegisterIoMemory = hipHostRegisterIoMemory;\n  int HostRegisterIoMemory = cudaHostRegisterIoMemory;\n#endif\n\n#if CUDA_VERSION >= 8000\n  // CHECK: int CpuDeviceId = hipCpuDeviceId;\n  // CHECK-NEXT: int InvalidDeviceId = hipInvalidDeviceId;\n  int CpuDeviceId = cudaCpuDeviceId;\n  int InvalidDeviceId = cudaInvalidDeviceId;\n#endif\n\n#if CUDA_VERSION >= 9000\n#if CUDA_VERSION < 13000\n  // CHECK: int CooperativeLaunchMultiDeviceNoPreSync = hipCooperativeLaunchMultiDeviceNoPreSync;\n  // CHECK-NEXT: int CooperativeLaunchMultiDeviceNoPostSync = hipCooperativeLaunchMultiDeviceNoPostSync;\n  int CooperativeLaunchMultiDeviceNoPreSync = cudaCooperativeLaunchMultiDeviceNoPreSync;\n  int CooperativeLaunchMultiDeviceNoPostSync = cudaCooperativeLaunchMultiDeviceNoPostSync;\n#endif\n  // CHECK: hipStream_t StreamLegacy = hipStreamLegacy;\n  cudaStream_t StreamLegacy = cudaStreamLegacy;\n#endif\n\n#if CUDA_VERSION >= 10000\n  // CHECK: int EXTERNAL_MEMORY_DEDICATED = hipExternalMemoryDedicated;\n  int EXTERNAL_MEMORY_DEDICATED = cudaExternalMemoryDedicated;\n#endif\n\n#if CUDA_VERSION >= 11010\n  // CHECK: int HostRegisterReadOnly = hipHostRegisterReadOnly;\n  int HostRegisterReadOnly = cudaHostRegisterReadOnly;\n#endif\n\n#if CUDA_VERSION >= 12020\n  // CHECK: int DevAttrHostNumaId = hipDeviceAttributeHostNumaId;\n  int DevAttrHostNumaId = cudaDevAttrHostNumaId;\n#endif\n\n#if CUDA_VERSION >= 12030\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_DEFAULT = hipGraphKernelNodePortDefault;\n  int GRAPH_KERNEL_NODE_PORT_DEFAULT = cudaGraphKernelNodePortDefault;\n\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_LAUNCH_ORDER = hipGraphKernelNodePortLaunchCompletion;\n  int GRAPH_KERNEL_NODE_PORT_LAUNCH_ORDER = cudaGraphKernelNodePortLaunchCompletion;\n\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_PROGRAMMATIC = hipGraphKernelNodePortProgrammatic;\n  int GRAPH_KERNEL_NODE_PORT_PROGRAMMATIC = cudaGraphKernelNodePortProgrammatic;\n#endif\n\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 3 --amap --skip-excluded-preprocessor-conditional-blocks --experimental %clang_args -D__CUDA_API_VERSION_INTERNAL\n\n// CHECK: #include <hip/hip_runtime_api.h>\n// CHECK-NEXT: #include <hip/device_functions.h>\n// CHECK-NEXT: #include <hip/hip_math_constants.h>\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <hip/device_functions.h>\n#include <hip/hip_math_constants.h>\n#include <stdio.h>\n\n  __global__ __constant__ float INF_F;\n  __global__ __constant__ float NAN_F;\n  __global__ __constant__ float MIN_DENORM_F;\n  __global__ __constant__ float MAX_NORMAL_F;\n  __global__ __constant__ float NEG_ZERO_F;\n  __global__ __constant__ float ZERO_F;\n  __global__ __constant__ float ONE_F;\n  __global__ __constant__ float SQRT_HALF_F;\n  __global__ __constant__ float SQRT_HALF_HI_F;\n  __global__ __constant__ float SQRT_HALF_LO_F;\n  __global__ __constant__ float SQRT_TWO_F;\n  __global__ __constant__ float THIRD_F;\n  __global__ __constant__ float PIO4_F;\n  __global__ __constant__ float PIO2_F;\n  __global__ __constant__ float _3PIO4_F;\n  __global__ __constant__ float _2_OVER_PI_F;\n  __global__ __constant__ float SQRT_2_OVER_PI_F;\n  __global__ __constant__ float PI_F;\n  __global__ __constant__ float L2E_F;\n  __global__ __constant__ float L2T_F;\n  __global__ __constant__ float LG2_F;\n  __global__ __constant__ float LGE_F;\n  __global__ __constant__ float LN2_F;\n  __global__ __constant__ float LNT_F;\n  __global__ __constant__ float LNPI_F;\n  __global__ __constant__ float TWO_TO_M126_F;\n  __global__ __constant__ float TWO_TO_126_F;\n  __global__ __constant__ float NORM_HUGE_F;\n  __global__ __constant__ float TWO_TO_23_F;\n  __global__ __constant__ float TWO_TO_24_F;\n  __global__ __constant__ float TWO_TO_31_F;\n  __global__ __constant__ float TWO_TO_32_F;\n  __global__ __constant__ float REMQUO_BITS_F;\n  __global__ __constant__ float REMQUO_MASK_F;\n  __global__ __constant__ double TRIG_PLOSS_F;\n  __global__ __constant__ double INF;\n  __global__ __constant__ double NAN_;\n  __global__ __constant__ double NEG_ZERO;\n  __global__ __constant__ double MIN_DENORM;\n  __global__ __constant__ double ZERO;\n  __global__ __constant__ double ONE;\n  __global__ __constant__ double SQRT_TWO;\n  __global__ __constant__ double SQRT_HALF;\n  __global__ __constant__ double SQRT_HALF_HI;\n  __global__ __constant__ double SQRT_HALF_LO;\n  __global__ __constant__ double THIRD;\n  __global__ __constant__ double TWOTHIRD;\n  __global__ __constant__ double PIO4;\n  __global__ __constant__ double PIO4_HI;\n  __global__ __constant__ double PIO4_LO;\n  __global__ __constant__ double PIO2;\n  __global__ __constant__ double PIO2_HI;\n  __global__ __constant__ double PIO2_LO;\n  __global__ __constant__ double _3PIO4;\n  __global__ __constant__ double _2_OVER_PI;\n  __global__ __constant__ double _PI;\n  __global__ __constant__ double PI_HI;\n  __global__ __constant__ double PI_LO;\n  __global__ __constant__ double SQRT_2PI;\n  __global__ __constant__ double SQRT_2PI_HI;\n  __global__ __constant__ double SQRT_2PI_LO;\n  __global__ __constant__ double SQRT_PIO2;\n  __global__ __constant__ double SQRT_PIO2_HI;\n  __global__ __constant__ double SQRT_PIO2_LO;\n  __global__ __constant__ double SQRT_2OPI;\n  __global__ __constant__ double L2E;\n  __global__ __constant__ double L2E_HI;\n  __global__ __constant__ double L2E_LO;\n  __global__ __constant__ double L2T;\n  __global__ __constant__ double LG2;\n  __global__ __constant__ double LG2_HI;\n  __global__ __constant__ double LG2_LO;\n  __global__ __constant__ double LGE;\n  __global__ __constant__ double LGE_HI;\n  __global__ __constant__ double LGE_LO;\n  __global__ __constant__ double LN2;\n  __global__ __constant__ double LN2_HI;\n  __global__ __constant__ double LN2_LO;\n  __global__ __constant__ double LNT;\n  __global__ __constant__ double LNT_HI;\n  __global__ __constant__ double LNT_LO;\n  __global__ __constant__ double LNPI;\n  __global__ __constant__ double LN2_X_1024;\n  __global__ __constant__ double LN2_X_1025;\n  __global__ __constant__ double LN2_X_1075;\n  __global__ __constant__ double LG2_X_1024;\n  __global__ __constant__ double LG2_X_1075;\n  __global__ __constant__ double TWO_TO_23;\n  __global__ __constant__ double TWO_TO_52;\n  __global__ __constant__ double TWO_TO_53;\n  __global__ __constant__ double TWO_TO_54;\n  __global__ __constant__ double TWO_TO_M54;\n  __global__ __constant__ double TWO_TO_M1022;\n  __global__ __constant__ double TRIG_PLOSS;\n  __global__ __constant__ double DBL2INT_CVT;\n\n__global__ void init() {\n  // CHECK: INF_F = HIP_INF_F;\n  // CHECK-NEXT: NAN_F = HIP_NAN_F;\n  // CHECK-NEXT: MIN_DENORM_F = HIP_MIN_DENORM_F;\n  // CHECK-NEXT: MAX_NORMAL_F = HIP_MAX_NORMAL_F;\n  // CHECK-NEXT: NEG_ZERO_F = HIP_NEG_ZERO_F;\n  // CHECK-NEXT: ZERO_F = HIP_ZERO_F;\n  // CHECK-NEXT: ONE_F = HIP_ONE_F;\n  // CHECK-NEXT: SQRT_HALF_F = HIP_SQRT_HALF_F;\n  // CHECK-NEXT: SQRT_HALF_HI_F = HIP_SQRT_HALF_HI_F;\n  // CHECK-NEXT: SQRT_HALF_LO_F = HIP_SQRT_HALF_LO_F;\n  // CHECK-NEXT: SQRT_TWO_F = HIP_SQRT_TWO_F;\n  // CHECK-NEXT: THIRD_F = HIP_THIRD_F;\n  // CHECK-NEXT: PIO4_F = HIP_PIO4_F;\n  // CHECK-NEXT: PIO2_F = HIP_PIO2_F;\n  // CHECK-NEXT: _3PIO4_F = HIP_3PIO4_F;\n  // CHECK-NEXT: _2_OVER_PI_F = HIP_2_OVER_PI_F;\n  // CHECK-NEXT: SQRT_2_OVER_PI_F = HIP_SQRT_2_OVER_PI_F;\n  // CHECK-NEXT: PI_F = HIP_PI_F;\n  // CHECK-NEXT: L2E_F = HIP_L2E_F;\n  // CHECK-NEXT: L2T_F = HIP_L2T_F;\n  // CHECK-NEXT: LG2_F = HIP_LG2_F;\n  // CHECK-NEXT: LGE_F = HIP_LGE_F;\n  // CHECK-NEXT: LN2_F = HIP_LN2_F;\n  // CHECK-NEXT: LNT_F = HIP_LNT_F;\n  // CHECK-NEXT: LNPI_F = HIP_LNPI_F;\n  // CHECK-NEXT: TWO_TO_M126_F = HIP_TWO_TO_M126_F;\n  // CHECK-NEXT: TWO_TO_126_F = HIP_TWO_TO_126_F;\n  // CHECK-NEXT: NORM_HUGE_F = HIP_NORM_HUGE_F;\n  // CHECK-NEXT: TWO_TO_23_F = HIP_TWO_TO_23_F;\n  // CHECK-NEXT: TWO_TO_24_F = HIP_TWO_TO_24_F;\n  // CHECK-NEXT: TWO_TO_31_F = HIP_TWO_TO_31_F;\n  // CHECK-NEXT: TWO_TO_32_F = HIP_TWO_TO_32_F;\n  // CHECK-NEXT: REMQUO_BITS_F = HIP_REMQUO_BITS_F;\n  // CHECK-NEXT: REMQUO_MASK_F = HIP_REMQUO_MASK_F;\n  // CHECK-NEXT: TRIG_PLOSS_F = HIP_TRIG_PLOSS_F;\n  // CHECK-NEXT: INF = HIP_INF;\n  // CHECK-NEXT: NAN_ = HIP_NAN;\n  // CHECK-NEXT: NEG_ZERO = HIP_NEG_ZERO;\n  // CHECK-NEXT: MIN_DENORM = HIP_MIN_DENORM;\n  // CHECK-NEXT: ZERO = HIP_ZERO;\n  // CHECK-NEXT: ONE = HIP_ONE;\n  // CHECK-NEXT: SQRT_TWO = HIP_SQRT_TWO;\n  // CHECK-NEXT: SQRT_HALF = HIP_SQRT_HALF;\n  // CHECK-NEXT: SQRT_HALF_HI = HIP_SQRT_HALF_HI;\n  // CHECK-NEXT: SQRT_HALF_LO = HIP_SQRT_HALF_LO;\n  // CHECK-NEXT: THIRD = HIP_THIRD;\n  // CHECK-NEXT: TWOTHIRD = HIP_TWOTHIRD;\n  // CHECK-NEXT: PIO4 = HIP_PIO4;\n  // CHECK-NEXT: PIO4_HI = HIP_PIO4_HI;\n  // CHECK-NEXT: PIO4_LO = HIP_PIO4_LO;\n  // CHECK-NEXT: PIO2 = HIP_PIO2;\n  // CHECK-NEXT: PIO2_HI = HIP_PIO2_HI;\n  // CHECK-NEXT: PIO2_LO = HIP_PIO2_LO;\n  // CHECK-NEXT: _3PIO4 = HIP_3PIO4;\n  // CHECK-NEXT: _2_OVER_PI = HIP_2_OVER_PI;\n  // CHECK-NEXT: _PI = HIP_PI;\n  // CHECK-NEXT: PI_HI = HIP_PI_HI;\n  // CHECK-NEXT: PI_LO = HIP_PI_LO;\n  // CHECK-NEXT: SQRT_2PI = HIP_SQRT_2PI;\n  // CHECK-NEXT: SQRT_2PI_HI = HIP_SQRT_2PI_HI;\n  // CHECK-NEXT: SQRT_2PI_LO = HIP_SQRT_2PI_LO;\n  // CHECK-NEXT: SQRT_PIO2 = HIP_SQRT_PIO2;\n  // CHECK-NEXT: SQRT_PIO2_HI = HIP_SQRT_PIO2_HI;\n  // CHECK-NEXT: SQRT_PIO2_LO = HIP_SQRT_PIO2_LO;\n  // CHECK-NEXT: SQRT_2OPI = HIP_SQRT_2OPI;\n  // CHECK-NEXT: L2E = HIP_L2E;\n  // CHECK-NEXT: L2E_HI = HIP_L2E_HI;\n  // CHECK-NEXT: L2E_LO = HIP_L2E_LO;\n  // CHECK-NEXT: L2T = HIP_L2T;\n  // CHECK-NEXT: LG2 = HIP_LG2;\n  // CHECK-NEXT: LG2_HI = HIP_LG2_HI;\n  // CHECK-NEXT: LG2_LO = HIP_LG2_LO;\n  // CHECK-NEXT: LGE = HIP_LGE;\n  // CHECK-NEXT: LGE_HI = HIP_LGE_HI;\n  // CHECK-NEXT: LGE_LO = HIP_LGE_LO;\n  // CHECK-NEXT: LN2 = HIP_LN2;\n  // CHECK-NEXT: LN2_HI = HIP_LN2_HI;\n  // CHECK-NEXT: LN2_LO = HIP_LN2_LO;\n  // CHECK-NEXT: LNT = HIP_LNT;\n  // CHECK-NEXT: LNT_HI = HIP_LNT_HI;\n  // CHECK-NEXT: LNT_LO = HIP_LNT_LO;\n  // CHECK-NEXT: LNPI = HIP_LNPI;\n  // CHECK-NEXT: LN2_X_1024 = HIP_LN2_X_1024;\n  // CHECK-NEXT: LN2_X_1025 = HIP_LN2_X_1025;\n  // CHECK-NEXT: LN2_X_1075 = HIP_LN2_X_1075;\n  // CHECK-NEXT: LG2_X_1024 = HIP_LG2_X_1024;\n  // CHECK-NEXT: LG2_X_1075 = HIP_LG2_X_1075;\n  // CHECK-NEXT: TWO_TO_23 = HIP_TWO_TO_23;\n  // CHECK-NEXT: TWO_TO_52 = HIP_TWO_TO_52;\n  // CHECK-NEXT: TWO_TO_53 = HIP_TWO_TO_53;\n  // CHECK-NEXT: TWO_TO_54 = HIP_TWO_TO_54;\n  // CHECK-NEXT: TWO_TO_M54 = HIP_TWO_TO_M54;\n  // CHECK-NEXT: TWO_TO_M1022 = HIP_TWO_TO_M1022;\n  // CHECK-NEXT: TRIG_PLOSS = HIP_TRIG_PLOSS;\n  // CHECK-NEXT: DBL2INT_CVT = HIP_DBL2INT_CVT;\n  INF_F = HIP_INF_F;\n  NAN_F = HIP_NAN_F;\n  MIN_DENORM_F = HIP_MIN_DENORM_F;\n  MAX_NORMAL_F = HIP_MAX_NORMAL_F;\n  NEG_ZERO_F = HIP_NEG_ZERO_F;\n  ZERO_F = HIP_ZERO_F;\n  ONE_F = HIP_ONE_F;\n  SQRT_HALF_F = HIP_SQRT_HALF_F;\n  SQRT_HALF_HI_F = HIP_SQRT_HALF_HI_F;\n  SQRT_HALF_LO_F = HIP_SQRT_HALF_LO_F;\n  SQRT_TWO_F = HIP_SQRT_TWO_F;\n  THIRD_F = HIP_THIRD_F;\n  PIO4_F = HIP_PIO4_F;\n  PIO2_F = HIP_PIO2_F;\n  _3PIO4_F = HIP_3PIO4_F;\n  _2_OVER_PI_F = HIP_2_OVER_PI_F;\n  SQRT_2_OVER_PI_F = HIP_SQRT_2_OVER_PI_F;\n  PI_F = HIP_PI_F;\n  L2E_F = HIP_L2E_F;\n  L2T_F = HIP_L2T_F;\n  LG2_F = HIP_LG2_F;\n  LGE_F = HIP_LGE_F;\n  LN2_F = HIP_LN2_F;\n  LNT_F = HIP_LNT_F;\n  LNPI_F = HIP_LNPI_F;\n  TWO_TO_M126_F = HIP_TWO_TO_M126_F;\n  TWO_TO_126_F = HIP_TWO_TO_126_F;\n  NORM_HUGE_F = HIP_NORM_HUGE_F;\n  TWO_TO_23_F = HIP_TWO_TO_23_F;\n  TWO_TO_24_F = HIP_TWO_TO_24_F;\n  TWO_TO_31_F = HIP_TWO_TO_31_F;\n  TWO_TO_32_F = HIP_TWO_TO_32_F;\n  REMQUO_BITS_F = HIP_REMQUO_BITS_F;\n  REMQUO_MASK_F = HIP_REMQUO_MASK_F;\n  TRIG_PLOSS_F = HIP_TRIG_PLOSS_F;\n  INF = HIP_INF;\n  NAN_ = HIP_NAN;\n  NEG_ZERO = HIP_NEG_ZERO;\n  MIN_DENORM = HIP_MIN_DENORM;\n  ZERO = HIP_ZERO;\n  ONE = HIP_ONE;\n  SQRT_TWO = HIP_SQRT_TWO;\n  SQRT_HALF = HIP_SQRT_HALF;\n  SQRT_HALF_HI = HIP_SQRT_HALF_HI;\n  SQRT_HALF_LO = HIP_SQRT_HALF_LO;\n  THIRD = HIP_THIRD;\n  TWOTHIRD = HIP_TWOTHIRD;\n  PIO4 = HIP_PIO4;\n  PIO4_HI = HIP_PIO4_HI;\n  PIO4_LO = HIP_PIO4_LO;\n  PIO2 = HIP_PIO2;\n  PIO2_HI = HIP_PIO2_HI;\n  PIO2_LO = HIP_PIO2_LO;\n  _3PIO4 = HIP_3PIO4;\n  _2_OVER_PI = HIP_2_OVER_PI;\n  _PI = HIP_PI;\n  PI_HI = HIP_PI_HI;\n  PI_LO = HIP_PI_LO;\n  SQRT_2PI = HIP_SQRT_2PI;\n  SQRT_2PI_HI = HIP_SQRT_2PI_HI;\n  SQRT_2PI_LO = HIP_SQRT_2PI_LO;\n  SQRT_PIO2 = HIP_SQRT_PIO2;\n  SQRT_PIO2_HI = HIP_SQRT_PIO2_HI;\n  SQRT_PIO2_LO = HIP_SQRT_PIO2_LO;\n  SQRT_2OPI = HIP_SQRT_2OPI;\n  L2E = HIP_L2E;\n  L2E_HI = HIP_L2E_HI;\n  L2E_LO = HIP_L2E_LO;\n  L2T = HIP_L2T;\n  LG2 = HIP_LG2;\n  LG2_HI = HIP_LG2_HI;\n  LG2_LO = HIP_LG2_LO;\n  LGE = HIP_LGE;\n  LGE_HI = HIP_LGE_HI;\n  LGE_LO = HIP_LGE_LO;\n  LN2 = HIP_LN2;\n  LN2_HI = HIP_LN2_HI;\n  LN2_LO = HIP_LN2_LO;\n  LNT = HIP_LNT;\n  LNT_HI = HIP_LNT_HI;\n  LNT_LO = HIP_LNT_LO;\n  LNPI = HIP_LNPI;\n  LN2_X_1024 = HIP_LN2_X_1024;\n  LN2_X_1025 = HIP_LN2_X_1025;\n  LN2_X_1075 = HIP_LN2_X_1075;\n  LG2_X_1024 = HIP_LG2_X_1024;\n  LG2_X_1075 = HIP_LG2_X_1075;\n  TWO_TO_23 = HIP_TWO_TO_23;\n  TWO_TO_52 = HIP_TWO_TO_52;\n  TWO_TO_53 = HIP_TWO_TO_53;\n  TWO_TO_54 = HIP_TWO_TO_54;\n  TWO_TO_M54 = HIP_TWO_TO_M54;\n  TWO_TO_M1022 = HIP_TWO_TO_M1022;\n  TRIG_PLOSS = HIP_TRIG_PLOSS;\n  DBL2INT_CVT = HIP_DBL2INT_CVT;\n}\n\nint main() {\n  printf(\"08. CUDA Runtime API Defines synthetic test\\n\");\n\n  // CHECK: int IPC_HANDLE_SIZE = HIP_IPC_HANDLE_SIZE;\n  // CHECK-NEXT: int ArrayDefault = hipArrayDefault;\n  // CHECK-NEXT: int ArrayLayered = hipArrayLayered;\n  // CHECK-NEXT: int ArraySurfaceLoadStore = hipArraySurfaceLoadStore;\n  // CHECK-NEXT: int ArrayCubemap = hipArrayCubemap;\n  // CHECK-NEXT: int ArrayTextureGather = hipArrayTextureGather;\n  // CHECK-NEXT: int DeviceBlockingSync = hipDeviceScheduleBlockingSync;\n  // CHECK-NEXT: int DeviceLmemResizeToMax = hipDeviceLmemResizeToMax;\n  // CHECK-NEXT: int DeviceMapHost = hipDeviceMapHost;\n  // CHECK-NEXT: int DeviceScheduleAuto = hipDeviceScheduleAuto;\n  // CHECK-NEXT: int DeviceScheduleSpin = hipDeviceScheduleSpin;\n  // CHECK-NEXT: int DeviceScheduleYield = hipDeviceScheduleYield;\n  // CHECK-NEXT: int DeviceScheduleBlockingSync = hipDeviceScheduleBlockingSync;\n  // CHECK-NEXT: int DeviceScheduleMask = hipDeviceScheduleMask;\n  // CHECK-NEXT: int EventDefault = hipEventDefault;\n  // CHECK-NEXT: int EventBlockingSync = hipEventBlockingSync;\n  // CHECK-NEXT: int EventDisableTiming = hipEventDisableTiming;\n  // CHECK-NEXT: int EventInterprocess = hipEventInterprocess;\n  // CHECK-NEXT: int HostAllocDefault = hipHostMallocDefault;\n  // CHECK-NEXT: int HostAllocPortable = hipHostMallocPortable;\n  // CHECK-NEXT: int HostAllocMapped = hipHostMallocMapped;\n  // CHECK-NEXT: int HostAllocWriteCombined = hipHostMallocWriteCombined;\n  // CHECK-NEXT: int HostRegisterDefault = hipHostRegisterDefault;\n  // CHECK-NEXT: int HostRegisterPortable = hipHostRegisterPortable;\n  // CHECK-NEXT: int HostRegisterMapped = hipHostRegisterMapped;\n  // CHECK-NEXT: int IpcMemLazyEnablePeerAccess = hipIpcMemLazyEnablePeerAccess;\n  // CHECK-NEXT: int MemAttachGlobal = hipMemAttachGlobal;\n  // CHECK-NEXT: int MemAttachHost = hipMemAttachHost;\n  // CHECK-NEXT: int MemAttachSingle = hipMemAttachSingle;\n  // CHECK-NEXT: int TextureType1D = hipTextureType1D;\n  // CHECK-NEXT: int TextureType2D = hipTextureType2D;\n  // CHECK-NEXT: int TextureType3D = hipTextureType3D;\n  // CHECK-NEXT: int TextureTypeCubemap = hipTextureTypeCubemap;\n  // CHECK-NEXT: int TextureType1DLayered = hipTextureType1DLayered;\n  // CHECK-NEXT: int TextureType2DLayered = hipTextureType2DLayered;\n  // CHECK-NEXT: int TextureTypeCubemapLayered = hipTextureTypeCubemapLayered;\n  // CHECK-NEXT: int OccupancyDefault = hipOccupancyDefault;\n  // CHECK-NEXT: int OccupancyDisableCachingOverride = hipOccupancyDisableCachingOverride;\n  // CHECK-NEXT: int StreamDefault = hipStreamDefault;\n  // CHECK-NEXT: int StreamNonBlocking = hipStreamNonBlocking;\n  // CHECK-NEXT: hipStream_t StreamPerThread = hipStreamPerThread;\n  int IPC_HANDLE_SIZE = HIP_IPC_HANDLE_SIZE;\n  int ArrayDefault = hipArrayDefault;\n  int ArrayLayered = hipArrayLayered;\n  int ArraySurfaceLoadStore = hipArraySurfaceLoadStore;\n  int ArrayCubemap = hipArrayCubemap;\n  int ArrayTextureGather = hipArrayTextureGather;\n  int DeviceBlockingSync = hipDeviceScheduleBlockingSync;\n  int DeviceLmemResizeToMax = hipDeviceLmemResizeToMax;\n  int DeviceMapHost = hipDeviceMapHost;\n  int DeviceScheduleAuto = hipDeviceScheduleAuto;\n  int DeviceScheduleSpin = hipDeviceScheduleSpin;\n  int DeviceScheduleYield = hipDeviceScheduleYield;\n  int DeviceScheduleBlockingSync = hipDeviceScheduleBlockingSync;\n  int DeviceScheduleMask = hipDeviceScheduleMask;\n  int EventDefault = hipEventDefault;\n  int EventBlockingSync = hipEventBlockingSync;\n  int EventDisableTiming = hipEventDisableTiming;\n  int EventInterprocess = hipEventInterprocess;\n  int HostAllocDefault = hipHostMallocDefault;\n  int HostAllocPortable = hipHostMallocPortable;\n  int HostAllocMapped = hipHostMallocMapped;\n  int HostAllocWriteCombined = hipHostMallocWriteCombined;\n  int HostRegisterDefault = hipHostRegisterDefault;\n  int HostRegisterPortable = hipHostRegisterPortable;\n  int HostRegisterMapped = hipHostRegisterMapped;\n  int IpcMemLazyEnablePeerAccess = hipIpcMemLazyEnablePeerAccess;\n  int MemAttachGlobal = hipMemAttachGlobal;\n  int MemAttachHost = hipMemAttachHost;\n  int MemAttachSingle = hipMemAttachSingle;\n  int TextureType1D = hipTextureType1D;\n  int TextureType2D = hipTextureType2D;\n  int TextureType3D = hipTextureType3D;\n  int TextureTypeCubemap = hipTextureTypeCubemap;\n  int TextureType1DLayered = hipTextureType1DLayered;\n  int TextureType2DLayered = hipTextureType2DLayered;\n  int TextureTypeCubemapLayered = hipTextureTypeCubemapLayered;\n  int OccupancyDefault = hipOccupancyDefault;\n  int OccupancyDisableCachingOverride = hipOccupancyDisableCachingOverride;\n  int StreamDefault = hipStreamDefault;\n  int StreamNonBlocking = hipStreamNonBlocking;\n  hipStream_t StreamPerThread = hipStreamPerThread;\n\n#if CUDA_VERSION >= 7050\n  // CHECK: int HostRegisterIoMemory = hipHostRegisterIoMemory;\n  int HostRegisterIoMemory = hipHostRegisterIoMemory;\n#endif\n\n#if CUDA_VERSION >= 8000\n  // CHECK: int CpuDeviceId = hipCpuDeviceId;\n  // CHECK-NEXT: int InvalidDeviceId = hipInvalidDeviceId;\n  int CpuDeviceId = hipCpuDeviceId;\n  int InvalidDeviceId = hipInvalidDeviceId;\n#endif\n\n#if CUDA_VERSION >= 9000\n#if CUDA_VERSION < 13000\n  // CHECK: int CooperativeLaunchMultiDeviceNoPreSync = hipCooperativeLaunchMultiDeviceNoPreSync;\n  // CHECK-NEXT: int CooperativeLaunchMultiDeviceNoPostSync = hipCooperativeLaunchMultiDeviceNoPostSync;\n  int CooperativeLaunchMultiDeviceNoPreSync = hipCooperativeLaunchMultiDeviceNoPreSync;\n  int CooperativeLaunchMultiDeviceNoPostSync = hipCooperativeLaunchMultiDeviceNoPostSync;\n#endif\n  // CHECK: hipStream_t StreamLegacy = hipStreamLegacy;\n  hipStream_t StreamLegacy = hipStreamLegacy;\n#endif\n\n#if CUDA_VERSION >= 10000\n  // CHECK: int EXTERNAL_MEMORY_DEDICATED = hipExternalMemoryDedicated;\n  int EXTERNAL_MEMORY_DEDICATED = hipExternalMemoryDedicated;\n#endif\n\n#if CUDA_VERSION >= 11010\n  // CHECK: int HostRegisterReadOnly = hipHostRegisterReadOnly;\n  int HostRegisterReadOnly = hipHostRegisterReadOnly;\n#endif\n\n#if CUDA_VERSION >= 12020\n  // CHECK: int DevAttrHostNumaId = hipDeviceAttributeHostNumaId;\n  int DevAttrHostNumaId = hipDeviceAttributeHostNumaId;\n#endif\n\n#if CUDA_VERSION >= 12030\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_DEFAULT = hipGraphKernelNodePortDefault;\n  int GRAPH_KERNEL_NODE_PORT_DEFAULT = hipGraphKernelNodePortDefault;\n\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_LAUNCH_ORDER = hipGraphKernelNodePortLaunchCompletion;\n  int GRAPH_KERNEL_NODE_PORT_LAUNCH_ORDER = hipGraphKernelNodePortLaunchCompletion;\n\n  // CHECK: int GRAPH_KERNEL_NODE_PORT_PROGRAMMATIC = hipGraphKernelNodePortProgrammatic;\n  int GRAPH_KERNEL_NODE_PORT_PROGRAMMATIC = hipGraphKernelNodePortProgrammatic;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/synthetic/runtime_defines.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\nfloat* y_new = nullptr;\n#ifdef SOME_MACRO\n  y_new = x;\n  // CHECK: axpy_kernel <<<1, 1>>> (a, y_new, x);\n  axpy_kernel <<<1, 1>>> (a, y_new, x);\n#endif\n\n#ifndef SOME_MACRO\n  y_new = y;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 2>>> (a, y_new, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 3>>> (a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 5>>> (a, y, x);\n  axpy_kernel <<<1, 5>>> (a, y, x);\n#elif defined SOME_MACRO_1\n  // CHECK: axpy_kernel <<<1, 6>>> (a, x, y);\n  axpy_kernel <<<1, 6>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(7), 0, 0, a, x, y);\n  axpy_kernel <<<1, 7>>> (a, x, y);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(8), 0, 0, a, y, x);\n  axpy_kernel <<<1, 8>>> (a, y, x);\n#elif !defined(SOME_MACRO_1)\n  // CHECK: axpy_kernel <<<1, 9>>> (a, x, y);\n  axpy_kernel <<<1, 9>>> (a, x, y);\n#else\n  // CHECK: axpy_kernel <<<1, 10>>> (a, x, y);\n  axpy_kernel <<<1, 10>>> (a, x, y);\n#endif\n\n}", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include \"hip/hip_runtime.h\"\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\nfloat* y_new = nullptr;\n#ifdef SOME_MACRO\n  y_new = x;\n  // CHECK: axpy_kernel <<<1, 1>>> (a, y_new, x);\n  axpy_kernel <<<1, 1>>> (a, y_new, x);\n#endif\n\n#ifndef SOME_MACRO\n  y_new = y;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 2>>> (a, y_new, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 3>>> (a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 5>>> (a, y, x);\n  axpy_kernel <<<1, 5>>> (a, y, x);\n#elif defined SOME_MACRO_1\n  // CHECK: axpy_kernel <<<1, 6>>> (a, x, y);\n  axpy_kernel <<<1, 6>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(7), 0, 0, a, x, y);\n  axpy_kernel <<<1, 7>>> (a, x, y);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(8), 0, 0, a, y, x);\n  axpy_kernel <<<1, 8>>> (a, y, x);\n#elif !defined(SOME_MACRO_1)\n  // CHECK: axpy_kernel <<<1, 9>>> (a, x, y);\n  axpy_kernel <<<1, 9>>> (a, x, y);\n#else\n  // CHECK: axpy_kernel <<<1, 10>>> (a, x, y);\n  axpy_kernel <<<1, 10>>> (a, x, y);\n#endif\n\n}", "source": "/kaggle/working/HIPIFY/tests/unit_tests/pp/pp_if_else_conditionals_01.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\nfloat* y_new = nullptr;\n#ifdef SOME_MACRO\n  y_new = x;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(1), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 1>>> (a, y_new, x);\n#endif\n\n#ifndef SOME_MACRO\n  y_new = y;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 2>>> (a, y_new, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(3), 0, 0, a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(5), 0, 0, a, y, x);\n  axpy_kernel <<<1, 5>>> (a, y, x);\n#elif defined SOME_MACRO_1\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(6), 0, 0, a, x, y);\n  axpy_kernel <<<1, 6>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(7), 0, 0, a, x, y);\n  axpy_kernel <<<1, 7>>> (a, x, y);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(8), 0, 0, a, y, x);\n  axpy_kernel <<<1, 8>>> (a, y, x);\n#elif !defined(SOME_MACRO_1)\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(9), 0, 0, a, x, y);\n  axpy_kernel <<<1, 9>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(10), 0, 0, a, x, y);\n  axpy_kernel <<<1, 10>>> (a, x, y);\n#endif\n\n}", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include \"hip/hip_runtime.h\"\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\nfloat* y_new = nullptr;\n#ifdef SOME_MACRO\n  y_new = x;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(1), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 1>>> (a, y_new, x);\n#endif\n\n#ifndef SOME_MACRO\n  y_new = y;\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y_new, x);\n  axpy_kernel <<<1, 2>>> (a, y_new, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(3), 0, 0, a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(5), 0, 0, a, y, x);\n  axpy_kernel <<<1, 5>>> (a, y, x);\n#elif defined SOME_MACRO_1\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(6), 0, 0, a, x, y);\n  axpy_kernel <<<1, 6>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(7), 0, 0, a, x, y);\n  axpy_kernel <<<1, 7>>> (a, x, y);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(8), 0, 0, a, y, x);\n  axpy_kernel <<<1, 8>>> (a, y, x);\n#elif !defined(SOME_MACRO_1)\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(9), 0, 0, a, x, y);\n  axpy_kernel <<<1, 9>>> (a, x, y);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(10), 0, 0, a, x, y);\n  axpy_kernel <<<1, 10>>> (a, x, y);\n#endif\n\n}", "source": "/kaggle/working/HIPIFY/tests/unit_tests/pp/pp_if_else_conditionals_01_LLVM_10.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include <cuda.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(1), 0, 0, a, y, x);\n  axpy_kernel <<<1, 1>>> (a, y, x);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y, x);\n  axpy_kernel <<<1, 2>>> (a, y, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(3), 0, 0, a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n}", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include <hip/hip_runtime.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(1), 0, 0, a, y, x);\n  axpy_kernel <<<1, 1>>> (a, y, x);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y, x);\n  axpy_kernel <<<1, 2>>> (a, y, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(3), 0, 0, a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n}", "source": "/kaggle/working/HIPIFY/tests/unit_tests/pp/pp_if_else_conditionals_LLVM_10.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include <cuda.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 1>>> (a, y, x);\n  axpy_kernel <<<1, 1>>> (a, y, x);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y, x);\n  axpy_kernel <<<1, 2>>> (a, y, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 3>>> (a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n}", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n\n#include <hip/hip_runtime.h>\n\n__global__ void axpy_kernel(float a, float* x, float* y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\nvoid axpy(float a, float* x, float* y) {\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 1>>> (a, y, x);\n  axpy_kernel <<<1, 1>>> (a, y, x);\n#endif\n\n#ifndef SOME_MACRO\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(2), 0, 0, a, y, x);\n  axpy_kernel <<<1, 2>>> (a, y, x);\n#endif\n\n#ifdef SOME_MACRO\n  // CHECK: axpy_kernel <<<1, 3>>> (a, y, x);\n  axpy_kernel <<<1, 3>>> (a, y, x);\n#else\n  // CHECK: hipLaunchKernelGGL(axpy_kernel, dim3(1), dim3(4), 0, 0, a, x, y);\n  axpy_kernel <<<1, 4>>> (a, x, y);\n#endif\n\n}", "source": "/kaggle/working/HIPIFY/tests/unit_tests/pp/pp_if_else_conditionals.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n#include \"inc.h\"\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  cudaEvent_t t##_start, t##_end;            \\\n  cudaEventCreate(&t##_start);               \\\n  cudaEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  cudaEventRecord(t##_start);                   \\\n  cudaEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  cudaEventRecord(t##_start);                                 \\\n  cudaEventSynchronize(t##_start);                            \\\n  cudaEventRecord(t##_end);                                   \\\n  cudaEventSynchronize(t##_end);                              \\\n  cudaEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipDeviceSynchronize();\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  cudaThreadSynchronize();\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess) {\n    printf(\"Error: %s\\n\", cudaGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  cudaGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  cudaFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  cudaMalloc(&gpu.r, N * sizeof(float4));\n  cudaMalloc(&gpu.v, N * sizeof(float4));\n  cudaMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  cudaMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  cudaMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  cudaFreeHost(cpu.r);\n  cudaFreeHost(cpu.v);\n  cudaFreeHost(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  cudaFree(gpu.r);\n  cudaFree(gpu.v);\n  cudaFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n#include \"inc.h\"\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  hipEvent_t t##_start, t##_end;            \\\n  hipEventCreate(&t##_start);               \\\n  hipEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  hipEventRecord(t##_start);                   \\\n  hipEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  hipEventRecord(t##_start);                                 \\\n  hipEventSynchronize(t##_start);                            \\\n  hipEventRecord(t##_end);                                   \\\n  hipEventSynchronize(t##_end);                              \\\n  hipEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipDeviceSynchronize();\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  hipDeviceSynchronize();\n  hipError_t err = hipGetLastError();\n  if (err != hipSuccess) {\n    printf(\"Error: %s\\n\", hipGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  hipGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  hipFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  hipMalloc(&gpu.r, N * sizeof(float4));\n  hipMalloc(&gpu.v, N * sizeof(float4));\n  hipMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  hipHostFree(cpu.r);\n  hipHostFree(cpu.v);\n  hipHostFree(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  hipFree(gpu.r);\n  hipFree(gpu.v);\n  hipFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/compilation_database/compilation_database_before_13000/cd_intro_before_13000.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n#include \"inc.h\"\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  cudaEvent_t t##_start, t##_end;            \\\n  cudaEventCreate(&t##_start);               \\\n  cudaEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  cudaEventRecord(t##_start);                   \\\n  cudaEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  cudaEventRecord(t##_start);                                 \\\n  cudaEventSynchronize(t##_start);                            \\\n  cudaEventRecord(t##_end);                                   \\\n  cudaEventSynchronize(t##_end);                              \\\n  cudaEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess) {\n    printf(\"Error: %s\\n\", cudaGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  cudaGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  cudaFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  cudaMalloc(&gpu.r, N * sizeof(float4));\n  cudaMalloc(&gpu.v, N * sizeof(float4));\n  cudaMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  cudaMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  cudaMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  cudaFreeHost(cpu.r);\n  cudaFreeHost(cpu.v);\n  cudaFreeHost(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  cudaFree(gpu.r);\n  cudaFree(gpu.v);\n  cudaFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n#include \"inc.h\"\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  hipEvent_t t##_start, t##_end;            \\\n  hipEventCreate(&t##_start);               \\\n  hipEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  hipEventRecord(t##_start);                   \\\n  hipEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  hipEventRecord(t##_start);                                 \\\n  hipEventSynchronize(t##_start);                            \\\n  hipEventRecord(t##_end);                                   \\\n  hipEventSynchronize(t##_end);                              \\\n  hipEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  hipError_t err = hipGetLastError();\n  if (err != hipSuccess) {\n    printf(\"Error: %s\\n\", hipGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  hipGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  hipFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  hipMalloc(&gpu.r, N * sizeof(float4));\n  hipMalloc(&gpu.v, N * sizeof(float4));\n  hipMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  hipHostFree(cpu.r);\n  hipHostFree(cpu.v);\n  hipHostFree(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  hipFree(gpu.r);\n  hipFree(gpu.v);\n  hipFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/compilation_database/compilation_database_13000/cd_intro.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// Synthetic test to warn only on device functions umin and umax as unsupported, but not on user defined ones.\n// ToDo: change lit testing in order to parse the output.\n\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n// CHECK: #include <hip/hip_runtime.h>\n#include <algorithm>\n\nnamespace my {\n  // user defined function\n  unsigned int umin(unsigned int arg1, unsigned int arg2) {\n    return (arg1 < arg2) ? arg1 : arg2;\n  }\n  // user defined function\n  unsigned int umax(unsigned int arg1, unsigned int arg2) {\n    return (arg1 > arg2) ? arg1 : arg2;\n  }\n}\n\n__global__ void uint_arithm(float* A, float* B, float* C, unsigned int u1, unsigned int u2)\n{\n  // device function call (warn if unsupported)\n  unsigned int _umin = umin ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umax = umax ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umin_global = ::umin ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umax_global = ::umax(u1, u2);\n  if (_umin != _umin_global) return;\n  if (_umax != _umax_global) return;\n  int i = threadIdx.x;\n  A[i] = i + _umin;\n  B[i] = i + _umax;\n  C[i] = A[i] + B[i];\n}\n\nint main() {\n  unsigned int u1 = 33;\n  unsigned int u2 = 34;\n  // user defined function call\n  unsigned int _min = my::umin(u1, u2);\n  // user defined function call\n  unsigned int _max = my::umax(u1, u2);\n  float *A, *B, *C;\n  // CHECK: hipMalloc((void**)&A, SIZE);\n  cudaMalloc((void**)&A, SIZE);\n  // CHECK: hipMalloc((void**)&B, SIZE);\n  cudaMalloc((void**)&B, SIZE);\n  // CHECK: hipMalloc((void**)&C, SIZE);\n  cudaMalloc((void**)&C, SIZE);\n  dim3 dimGrid(LEN / 512, 1, 1);\n  dim3 dimBlock(512, 1, 1);\n  // CHECK: hipLaunchKernelGGL(uint_arithm, dim3(dimGrid), dim3(dimBlock), 0, 0, A, B, C, u1, u2);\n  uint_arithm<<<dimGrid, dimBlock>>>(A, B, C, u1, u2);\n  return _min < _max;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// Synthetic test to warn only on device functions umin and umax as unsupported, but not on user defined ones.\n// ToDo: change lit testing in order to parse the output.\n\n#include \"hip/hip_runtime.h\"\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n// CHECK: #include <hip/hip_runtime.h>\n#include <algorithm>\n\nnamespace my {\n  // user defined function\n  unsigned int umin(unsigned int arg1, unsigned int arg2) {\n    return (arg1 < arg2) ? arg1 : arg2;\n  }\n  // user defined function\n  unsigned int umax(unsigned int arg1, unsigned int arg2) {\n    return (arg1 > arg2) ? arg1 : arg2;\n  }\n}\n\n__global__ void uint_arithm(float* A, float* B, float* C, unsigned int u1, unsigned int u2)\n{\n  // device function call (warn if unsupported)\n  unsigned int _umin = umin ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umax = umax ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umin_global = ::umin ( u1, u2 );\n  // device function call (warn if unsupported)\n  unsigned int _umax_global = ::umax(u1, u2);\n  if (_umin != _umin_global) return;\n  if (_umax != _umax_global) return;\n  int i = threadIdx.x;\n  A[i] = i + _umin;\n  B[i] = i + _umax;\n  C[i] = A[i] + B[i];\n}\n\nint main() {\n  unsigned int u1 = 33;\n  unsigned int u2 = 34;\n  // user defined function call\n  unsigned int _min = my::umin(u1, u2);\n  // user defined function call\n  unsigned int _max = my::umax(u1, u2);\n  float *A, *B, *C;\n  // CHECK: hipMalloc((void**)&A, SIZE);\n  hipMalloc((void**)&A, SIZE);\n  // CHECK: hipMalloc((void**)&B, SIZE);\n  hipMalloc((void**)&B, SIZE);\n  // CHECK: hipMalloc((void**)&C, SIZE);\n  hipMalloc((void**)&C, SIZE);\n  dim3 dimGrid(LEN / 512, 1, 1);\n  dim3 dimBlock(512, 1, 1);\n  // CHECK: hipLaunchKernelGGL(uint_arithm, dim3(dimGrid), dim3(dimBlock), 0, 0, A, B, C, u1, u2);\n  uint_arithm<<<dimGrid, dimBlock>>>(A, B, C, u1, u2);\n  return _min < _max;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/device/math_functions.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n#include <iostream>\n#include <assert.h>\n\n#define NUM 1024\n#define SIZE 1024 * 4\n\n__device__ int globalIn[NUM];\n__device__ int globalOut[NUM];\n\n__global__ void Assign(int* Out) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    Out[tid] = globalIn[tid];\n    globalOut[tid] = globalIn[tid];\n}\n\n__device__ __constant__ int globalConst[NUM];\n\n__global__ void checkAddress(int* addr, bool* out) {\n    *out = (globalConst == addr);\n}\n\nint main() {\n    int *A, *Am, *B, *Ad, *C, *Cm;\n    A = new int[NUM];\n    B = new int[NUM];\n    C = new int[NUM];\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -1 * i;\n        B[i] = 0;\n        C[i] = 0;\n    }\n    // CHECK: hipMalloc((void**)&Ad, SIZE);\n    cudaMalloc((void**)&Ad, SIZE);\n    // CHECK: hipHostMalloc((void**)&Am, SIZE, hipHostMallocDefault);\n    cudaMallocHost((void**)&Am, SIZE);\n    // CHECK: hipHostMalloc((void**)&Cm, SIZE, hipHostMallocDefault);\n    cudaMallocHost((void**)&Cm, SIZE);\n    for (int i = 0; i < NUM; ++i) {\n        Am[i] = -1 * i;\n        Cm[i] = 0;\n    }\n    // CHECK: hipStream_t stream = NULL;\n    cudaStream_t stream = NULL;\n    // CHECK: hipStreamCreate(&stream);\n    cudaStreamCreate(&stream);\n    // CHECK: hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), Am, SIZE, 0, hipMemcpyHostToDevice, stream);\n    cudaMemcpyToSymbolAsync(globalIn, Am, SIZE, 0, cudaMemcpyHostToDevice, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    cudaStreamSynchronize(stream);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    cudaMemcpy(B, Ad, SIZE, cudaMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbolAsync(Cm, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    cudaMemcpyFromSymbolAsync(Cm, globalOut, SIZE, 0, cudaMemcpyDeviceToHost, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    cudaStreamSynchronize(stream);\n    for (int i = 0; i < NUM; ++i) {\n        assert(Am[i] == B[i]);\n        assert(Am[i] == Cm[i]);\n    }\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -2 * i;\n        B[i] = 0;\n    }\n    // CHECK: hipMemcpyToSymbol(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice);\n    cudaMemcpyToSymbol(globalIn, A, SIZE, 0, cudaMemcpyHostToDevice);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    cudaMemcpy(B, Ad, SIZE, cudaMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbol(C, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost);\n    cudaMemcpyFromSymbol(C, globalOut, SIZE, 0, cudaMemcpyDeviceToHost);\n    for (int i = 0; i < NUM; ++i) {\n        assert(A[i] == B[i]);\n        assert(A[i] == C[i]);\n    }\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -3 * i;\n        B[i] = 0;\n    }\n    // CHECK: hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice, stream);\n    cudaMemcpyToSymbolAsync(globalIn, A, SIZE, 0, cudaMemcpyHostToDevice, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    cudaStreamSynchronize(stream);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    cudaMemcpy(B, Ad, SIZE, cudaMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbolAsync(C, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    cudaMemcpyFromSymbolAsync(C, globalOut, SIZE, 0, cudaMemcpyDeviceToHost, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    cudaStreamSynchronize(stream);\n    for (int i = 0; i < NUM; ++i) {\n        assert(A[i] == B[i]);\n        assert(A[i] == C[i]);\n    }\n    bool *checkOkD;\n    bool checkOk = false;\n    size_t symbolSize = 0;\n    int *symbolAddress;\n    // CHECK: hipGetSymbolSize(&symbolSize, HIP_SYMBOL(globalConst));\n    cudaGetSymbolSize(&symbolSize, globalConst);\n    // CHECK: hipGetSymbolAddress((void**) &symbolAddress, HIP_SYMBOL(globalConst));\n    cudaGetSymbolAddress((void**) &symbolAddress, globalConst);\n    // CHECK: hipMalloc((void**)&checkOkD, sizeof(bool));\n    cudaMalloc((void**)&checkOkD, sizeof(bool));\n    // CHECK: hipLaunchKernelGGL(checkAddress, dim3(1, 1, 1), dim3(1, 1, 1), 0, 0, symbolAddress, checkOkD);\n    checkAddress<<<dim3(1, 1, 1), dim3(1, 1, 1)>>>(symbolAddress, checkOkD);\n    // CHECK: hipMemcpy(&checkOk, checkOkD, sizeof(bool), hipMemcpyDeviceToHost);\n    cudaMemcpy(&checkOk, checkOkD, sizeof(bool), cudaMemcpyDeviceToHost);\n    // CHECK: hipFree(checkOkD);\n    cudaFree(checkOkD);\n    assert(checkOk);\n    assert(symbolSize == SIZE);\n    // CHECK: hipHostFree(Am);\n    cudaFreeHost(Am);\n    // CHECK: hipHostFree(Cm);\n    cudaFreeHost(Cm);\n    // CHECK: hipFree(Ad);\n    cudaFree(Ad);\n    delete[] A;\n    delete[] B;\n    delete[] C;\n    return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n#include <iostream>\n#include <assert.h>\n\n#define NUM 1024\n#define SIZE 1024 * 4\n\n__device__ int globalIn[NUM];\n__device__ int globalOut[NUM];\n\n__global__ void Assign(int* Out) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    Out[tid] = globalIn[tid];\n    globalOut[tid] = globalIn[tid];\n}\n\n__device__ __constant__ int globalConst[NUM];\n\n__global__ void checkAddress(int* addr, bool* out) {\n    *out = (globalConst == addr);\n}\n\nint main() {\n    int *A, *Am, *B, *Ad, *C, *Cm;\n    A = new int[NUM];\n    B = new int[NUM];\n    C = new int[NUM];\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -1 * i;\n        B[i] = 0;\n        C[i] = 0;\n    }\n    // CHECK: hipMalloc((void**)&Ad, SIZE);\n    hipMalloc((void**)&Ad, SIZE);\n    // CHECK: hipHostMalloc((void**)&Am, SIZE, hipHostMallocDefault);\n    hipHostMalloc((void**)&Am, SIZE);\n    // CHECK: hipHostMalloc((void**)&Cm, SIZE, hipHostMallocDefault);\n    hipHostMalloc((void**)&Cm, SIZE);\n    for (int i = 0; i < NUM; ++i) {\n        Am[i] = -1 * i;\n        Cm[i] = 0;\n    }\n    // CHECK: hipStream_t stream = NULL;\n    hipStream_t stream = NULL;\n    // CHECK: hipStreamCreate(&stream);\n    hipStreamCreate(&stream);\n    // CHECK: hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), Am, SIZE, 0, hipMemcpyHostToDevice, stream);\n    hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), Am, SIZE, 0, hipMemcpyHostToDevice, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    hipStreamSynchronize(stream);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbolAsync(Cm, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    hipMemcpyFromSymbolAsync(Cm,HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    hipStreamSynchronize(stream);\n    for (int i = 0; i < NUM; ++i) {\n        assert(Am[i] == B[i]);\n        assert(Am[i] == Cm[i]);\n    }\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -2 * i;\n        B[i] = 0;\n    }\n    // CHECK: hipMemcpyToSymbol(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice);\n    hipMemcpyToSymbol(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbol(C, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost);\n    hipMemcpyFromSymbol(C,HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost);\n    for (int i = 0; i < NUM; ++i) {\n        assert(A[i] == B[i]);\n        assert(A[i] == C[i]);\n    }\n    for (int i = 0; i < NUM; ++i) {\n        A[i] = -3 * i;\n        B[i] = 0;\n    }\n    // CHECK: hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice, stream);\n    hipMemcpyToSymbolAsync(HIP_SYMBOL(globalIn), A, SIZE, 0, hipMemcpyHostToDevice, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    hipStreamSynchronize(stream);\n    // CHECK: hipLaunchKernelGGL(Assign, dim3(1, 1, 1), dim3(NUM, 1, 1), 0, 0, Ad);\n    Assign<<<dim3(1, 1, 1), dim3(NUM, 1, 1)>>>(Ad);\n    // CHECK: hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    hipMemcpy(B, Ad, SIZE, hipMemcpyDeviceToHost);\n    // CHECK: hipMemcpyFromSymbolAsync(C, HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    hipMemcpyFromSymbolAsync(C,HIP_SYMBOL(globalOut), SIZE, 0, hipMemcpyDeviceToHost, stream);\n    // CHECK: hipStreamSynchronize(stream);\n    hipStreamSynchronize(stream);\n    for (int i = 0; i < NUM; ++i) {\n        assert(A[i] == B[i]);\n        assert(A[i] == C[i]);\n    }\n    bool *checkOkD;\n    bool checkOk = false;\n    size_t symbolSize = 0;\n    int *symbolAddress;\n    // CHECK: hipGetSymbolSize(&symbolSize, HIP_SYMBOL(globalConst));\n    hipGetSymbolSize(&symbolSize,HIP_SYMBOL(globalConst));\n    // CHECK: hipGetSymbolAddress((void**) &symbolAddress, HIP_SYMBOL(globalConst));\n    hipGetSymbolAddress((void**) &symbolAddress,HIP_SYMBOL(globalConst));\n    // CHECK: hipMalloc((void**)&checkOkD, sizeof(bool));\n    hipMalloc((void**)&checkOkD, sizeof(bool));\n    // CHECK: hipLaunchKernelGGL(checkAddress, dim3(1, 1, 1), dim3(1, 1, 1), 0, 0, symbolAddress, checkOkD);\n    checkAddress<<<dim3(1, 1, 1), dim3(1, 1, 1)>>>(symbolAddress, checkOkD);\n    // CHECK: hipMemcpy(&checkOk, checkOkD, sizeof(bool), hipMemcpyDeviceToHost);\n    hipMemcpy(&checkOk, checkOkD, sizeof(bool), hipMemcpyDeviceToHost);\n    // CHECK: hipFree(checkOkD);\n    hipFree(checkOkD);\n    assert(checkOk);\n    assert(symbolSize == SIZE);\n    // CHECK: hipHostFree(Am);\n    hipHostFree(Am);\n    // CHECK: hipHostFree(Cm);\n    hipHostFree(Cm);\n    // CHECK: hipFree(Ad);\n    hipFree(Ad);\n    delete[] A;\n    delete[] B;\n    delete[] C;\n    return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/device/device_symbols.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args \"-Xclang\" \"-fcuda-allow-variadic-functions\"\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include \"hip/hip_runtime.h\"\n#include \"cuda_runtime.h\"\n\n#include <algorithm>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n#include <type_traits>\n\n#define EXIT_WAIVED 2\n\nconst char* sampleName = \"hipSimpleAtomicsTest\";\n\nusing namespace std;\n// Auto-Verification Code\nbool testResult = true;\n\nbool computeGoldBitwise(...) {\n    return true;\n}\n\ntemplate<typename T, typename enable_if<is_integral<T>{}>::type* = nullptr>\nbool computeGoldBitwise(T* gpuData, int len) {\n    T val = 0xff;\n    for (int i = 0; i < len; ++i) {\n        // 9th element should be 1\n        val &= (2 * i + 7);\n    }\n    if (val != gpuData[8]) {\n        printf(\"atomicAnd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        // 10th element should be 0xff\n        val |= (1 << i);\n    }\n    if (val != gpuData[9]) {\n        printf(\"atomicOr failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n    for (int i = 0; i < len; ++i) {\n        // 11th element should be 0xff\n        val ^= i;\n    }\n    if (val != gpuData[10]) {\n        printf(\"atomicXor failed\\n\");\n        return false;\n    }\n\n    return true;\n}\n\ntemplate<typename T>\nbool computeGold(T* gpuData, int len) {\n    T val = 0;\n    for (int i = 0; i < len; ++i) {\n        val += 10;\n    }\n    if (val != gpuData[0]) {\n        printf(\"atomicAdd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val -= 10;\n    }\n    if (val != gpuData[1]) {\n        printf(\"atomicSub failed\\n\");\n        return false;\n    }\n\n    bool found = false;\n    for (T i = 0; i < len; ++i) {\n        // third element should be a member of [0, len)\n        if (i == gpuData[2]) {\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        printf(\"atomicExch failed\\n\");\n        return false;\n    }\n\n    val = -(1 << 8);\n    for (T i = 0; i < len; ++i) {\n        // fourth element should be len-1\n        val = max(val, i);\n    }\n    if (val != gpuData[3]) {\n        printf(\"atomicMax failed\\n\");\n        return false;\n    }\n\n    val = 1 << 8;\n    for (T i = 0; i < len; ++i) {\n        val = min(val, i);\n    }\n    if (val != gpuData[4]) {\n        printf(\"atomicMin failed\\n\");\n        return false;\n    }\n\n    int limit = 17;\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val = (val >= limit) ? 0 : val + 1;\n    }\n    if (val != gpuData[5]) {\n        printf(\"atomicInc failed\\n\");\n        return false;\n    }\n\n    limit = 137;\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val = ((val == 0) || (val > limit)) ? limit : val - 1;\n    }\n    if (val != gpuData[6]) {\n        printf(\"atomicDec failed\\n\");\n        return false;\n    }\n\n    found = false;\n    for (T i = 0; i < len; ++i) {\n        // eighth element should be a member of [0, len)\n        if (i == gpuData[7]) {\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        printf(\"atomicCAS failed\\n\");\n        return false;\n    }\n\n    return computeGoldBitwise(gpuData, len);\n}\n\n__device__\nvoid testKernelExch(...) {}\n\ntemplate<typename T, typename enable_if<!is_same<T, double>{}>::type* = nullptr>\n__device__\nvoid testKernelExch(T* g_odata) {\n    // access thread id\n    const T tid = blockDim.x * blockIdx.x + threadIdx.x;\n    // Atomic exchange\n    atomicExch(&g_odata[2], tid);\n}\n\n__device__\nvoid testKernelSub(...) {}\n\ntemplate<\n    typename T,\n    typename enable_if<\n        is_same<T, int>{} || is_same<T, unsigned int>{}>::type* = nullptr>\n__device__\nvoid testKernelSub(T* g_odata) {\n    // Atomic subtraction (final should be 0)\n    atomicSub(&g_odata[1], 10);\n}\n\n__device__\nvoid testKernelIntegral(...) {}\n\ntemplate<typename T, typename enable_if<is_integral<T>{}>::type* = nullptr>\n__device__\nvoid testKernelIntegral(T* g_odata) {\n    // access thread id\n    const T tid = blockDim.x * blockIdx.x + threadIdx.x;\n    // Atomic maximum\n    atomicMax(&g_odata[3], tid);\n    // Atomic minimum\n    atomicMin(&g_odata[4], tid);\n    // Atomic increment (modulo 17+1)\n    atomicInc((unsigned int*)&g_odata[5], 17);\n    // Atomic decrement\n    atomicDec((unsigned int*)&g_odata[6], 137);\n    // Atomic compare-and-swap\n    atomicCAS(&g_odata[7], tid - 1, tid);\n    // Atomic AND\n    atomicAnd(&g_odata[8], 2 * tid + 7);\n    // Atomic OR\n    atomicOr(&g_odata[9], 1 << tid);\n    // Atomic XOR\n    atomicXor(&g_odata[10], tid);\n    testKernelSub(g_odata);\n}\n\ntemplate<typename T>\n__global__ void testKernel(T* g_odata) {\n    // Atomic addition\n    atomicAdd(&g_odata[0], 10);\n    testKernelIntegral(g_odata);\n    testKernelExch(g_odata);\n}\n\ntemplate<typename T>\nvoid runTest() {\n    // CHECK: hipDeviceProp_t deviceProp;\n    cudaDeviceProp deviceProp;\n    deviceProp.major = 0;\n    deviceProp.minor = 0;\n    int dev = 0;\n    // CHECK: hipGetDeviceProperties(&deviceProp, dev);\n    cudaGetDeviceProperties(&deviceProp, dev);\n    // Statistics about the GPU device\n    printf(\n        \"> GPU device has %d Multi-Processors, \"\n        \"SM %d.%d compute capabilities\\n\\n\",\n        deviceProp.multiProcessorCount, deviceProp.major, deviceProp.minor);\n    unsigned int numThreads = 256;\n    unsigned int numBlocks = 64;\n    unsigned int numData = 11;\n    unsigned int memSize = sizeof(T) * numData;\n\n    // Allocate mem for the result on host side\n    T* hOData = (T*)malloc(memSize);\n    // Initialize the memory\n    for (unsigned int i = 0; i < numData; i++) hOData[i] = 0;\n    // To make the AND and XOR tests generate something other than 0...\n    hOData[8] = hOData[10] = 0xff;\n    // Allocate device memory for result\n    T* dOData;\n    // CHECK: hipMalloc((void**)&dOData, memSize);\n    cudaMalloc((void**)&dOData, memSize);\n    // Copy host memory to device to initialize to zero\n    // CHECK: hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice);\n    cudaMemcpy(dOData, hOData, memSize, cudaMemcpyHostToDevice);\n    // Execute the kernel\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(testKernel), dim3(numBlocks), dim3(numThreads), 0, 0, dOData);\n    testKernel<<<numBlocks, numThreads>>>(dOData);\n    // Copy result from device to host\n    // CHECK: hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost);\n    cudaMemcpy(hOData, dOData, memSize, cudaMemcpyDeviceToHost);\n    // Compute reference solution\n    testResult = computeGold(hOData, numThreads * numBlocks);\n    // Cleanup memory\n    free(hOData);\n    // CHECK: hipFree(dOData);\n    cudaFree(dOData);\n}\n\nint main(int argc, char** argv) {\n    printf(\"%s starting...\\n\", sampleName);\n    runTest<int>();\n    runTest<unsigned int>();\n    runTest<unsigned long long>();\n    runTest<float>();\n#if CUDA_VERSION >= 8000 && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 600\n    runTest<double>();\n#endif\n    // CHECK: hipDeviceReset();\n    cudaDeviceReset();\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --skip-excluded-preprocessor-conditional-blocks --hip-kernel-execution-syntax %clang_args \"-Xclang\" \"-fcuda-allow-variadic-functions\"\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include \"hip/hip_runtime.h\"\n#include \"hip/hip_runtime.h\"\n\n#include <algorithm>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n#include <type_traits>\n\n#define EXIT_WAIVED 2\n\nconst char* sampleName = \"hipSimpleAtomicsTest\";\n\nusing namespace std;\n// Auto-Verification Code\nbool testResult = true;\n\nbool computeGoldBitwise(...) {\n    return true;\n}\n\ntemplate<typename T, typename enable_if<is_integral<T>{}>::type* = nullptr>\nbool computeGoldBitwise(T* gpuData, int len) {\n    T val = 0xff;\n    for (int i = 0; i < len; ++i) {\n        // 9th element should be 1\n        val &= (2 * i + 7);\n    }\n    if (val != gpuData[8]) {\n        printf(\"atomicAnd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        // 10th element should be 0xff\n        val |= (1 << i);\n    }\n    if (val != gpuData[9]) {\n        printf(\"atomicOr failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n    for (int i = 0; i < len; ++i) {\n        // 11th element should be 0xff\n        val ^= i;\n    }\n    if (val != gpuData[10]) {\n        printf(\"atomicXor failed\\n\");\n        return false;\n    }\n\n    return true;\n}\n\ntemplate<typename T>\nbool computeGold(T* gpuData, int len) {\n    T val = 0;\n    for (int i = 0; i < len; ++i) {\n        val += 10;\n    }\n    if (val != gpuData[0]) {\n        printf(\"atomicAdd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val -= 10;\n    }\n    if (val != gpuData[1]) {\n        printf(\"atomicSub failed\\n\");\n        return false;\n    }\n\n    bool found = false;\n    for (T i = 0; i < len; ++i) {\n        // third element should be a member of [0, len)\n        if (i == gpuData[2]) {\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        printf(\"atomicExch failed\\n\");\n        return false;\n    }\n\n    val = -(1 << 8);\n    for (T i = 0; i < len; ++i) {\n        // fourth element should be len-1\n        val = max(val, i);\n    }\n    if (val != gpuData[3]) {\n        printf(\"atomicMax failed\\n\");\n        return false;\n    }\n\n    val = 1 << 8;\n    for (T i = 0; i < len; ++i) {\n        val = min(val, i);\n    }\n    if (val != gpuData[4]) {\n        printf(\"atomicMin failed\\n\");\n        return false;\n    }\n\n    int limit = 17;\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val = (val >= limit) ? 0 : val + 1;\n    }\n    if (val != gpuData[5]) {\n        printf(\"atomicInc failed\\n\");\n        return false;\n    }\n\n    limit = 137;\n    val = 0;\n    for (int i = 0; i < len; ++i) {\n        val = ((val == 0) || (val > limit)) ? limit : val - 1;\n    }\n    if (val != gpuData[6]) {\n        printf(\"atomicDec failed\\n\");\n        return false;\n    }\n\n    found = false;\n    for (T i = 0; i < len; ++i) {\n        // eighth element should be a member of [0, len)\n        if (i == gpuData[7]) {\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        printf(\"atomicCAS failed\\n\");\n        return false;\n    }\n\n    return computeGoldBitwise(gpuData, len);\n}\n\n__device__\nvoid testKernelExch(...) {}\n\ntemplate<typename T, typename enable_if<!is_same<T, double>{}>::type* = nullptr>\n__device__\nvoid testKernelExch(T* g_odata) {\n    // access thread id\n    const T tid = blockDim.x * blockIdx.x + threadIdx.x;\n    // Atomic exchange\n    atomicExch(&g_odata[2], tid);\n}\n\n__device__\nvoid testKernelSub(...) {}\n\ntemplate<\n    typename T,\n    typename enable_if<\n        is_same<T, int>{} || is_same<T, unsigned int>{}>::type* = nullptr>\n__device__\nvoid testKernelSub(T* g_odata) {\n    // Atomic subtraction (final should be 0)\n    atomicSub(&g_odata[1], 10);\n}\n\n__device__\nvoid testKernelIntegral(...) {}\n\ntemplate<typename T, typename enable_if<is_integral<T>{}>::type* = nullptr>\n__device__\nvoid testKernelIntegral(T* g_odata) {\n    // access thread id\n    const T tid = blockDim.x * blockIdx.x + threadIdx.x;\n    // Atomic maximum\n    atomicMax(&g_odata[3], tid);\n    // Atomic minimum\n    atomicMin(&g_odata[4], tid);\n    // Atomic increment (modulo 17+1)\n    atomicInc((unsigned int*)&g_odata[5], 17);\n    // Atomic decrement\n    atomicDec((unsigned int*)&g_odata[6], 137);\n    // Atomic compare-and-swap\n    atomicCAS(&g_odata[7], tid - 1, tid);\n    // Atomic AND\n    atomicAnd(&g_odata[8], 2 * tid + 7);\n    // Atomic OR\n    atomicOr(&g_odata[9], 1 << tid);\n    // Atomic XOR\n    atomicXor(&g_odata[10], tid);\n    testKernelSub(g_odata);\n}\n\ntemplate<typename T>\n__global__ void testKernel(T* g_odata) {\n    // Atomic addition\n    atomicAdd(&g_odata[0], 10);\n    testKernelIntegral(g_odata);\n    testKernelExch(g_odata);\n}\n\ntemplate<typename T>\nvoid runTest() {\n    // CHECK: hipDeviceProp_t deviceProp;\n    hipDeviceProp_t deviceProp;\n    deviceProp.major = 0;\n    deviceProp.minor = 0;\n    int dev = 0;\n    // CHECK: hipGetDeviceProperties(&deviceProp, dev);\n    hipGetDeviceProperties(&deviceProp, dev);\n    // Statistics about the GPU device\n    printf(\n        \"> GPU device has %d Multi-Processors, \"\n        \"SM %d.%d compute capabilities\\n\\n\",\n        deviceProp.multiProcessorCount, deviceProp.major, deviceProp.minor);\n    unsigned int numThreads = 256;\n    unsigned int numBlocks = 64;\n    unsigned int numData = 11;\n    unsigned int memSize = sizeof(T) * numData;\n\n    // Allocate mem for the result on host side\n    T* hOData = (T*)malloc(memSize);\n    // Initialize the memory\n    for (unsigned int i = 0; i < numData; i++) hOData[i] = 0;\n    // To make the AND and XOR tests generate something other than 0...\n    hOData[8] = hOData[10] = 0xff;\n    // Allocate device memory for result\n    T* dOData;\n    // CHECK: hipMalloc((void**)&dOData, memSize);\n    hipMalloc((void**)&dOData, memSize);\n    // Copy host memory to device to initialize to zero\n    // CHECK: hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice);\n    hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice);\n    // Execute the kernel\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(testKernel), dim3(numBlocks), dim3(numThreads), 0, 0, dOData);\n    testKernel<<<numBlocks, numThreads>>>(dOData);\n    // Copy result from device to host\n    // CHECK: hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost);\n    hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost);\n    // Compute reference solution\n    testResult = computeGold(hOData, numThreads * numBlocks);\n    // Cleanup memory\n    free(hOData);\n    // CHECK: hipFree(dOData);\n    hipFree(dOData);\n}\n\nint main(int argc, char** argv) {\n    printf(\"%s starting...\\n\", sampleName);\n    runTest<int>();\n    runTest<unsigned int>();\n    runTest<unsigned long long>();\n    runTest<float>();\n#if CUDA_VERSION >= 8000 && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 600\n    runTest<double>();\n#endif\n    // CHECK: hipDeviceReset();\n    hipDeviceReset();\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/device/atomics.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --amap --skip-excluded-preprocessor-conditional-blocks %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n// CHECK: #include \"hip/hip_complex.h\"\n#include \"cuComplex.h\"\n\n#define TYPEFLOAT\n#define DIMX 100\n#define DIMY 40\n#define moveX 2\n#define moveY 1\n\n#define MAXITERATIONS 10\n\n#ifdef TYPEFLOAT\n#define TYPE float\n// CHECK: #define cTYPE hipFloatComplex\n#define cTYPE cuFloatComplex\n// CHECK: #define cMakecuComplex(re,i) make_hipFloatComplex(re,i)\n#define cMakecuComplex(re,i) make_cuFloatComplex(re,i)\n#endif\n#ifdef TYPEDOUBLE\n// CHECK: #define TYPE hipDoubleComplex\n#define TYPE cuDoubleComplex\n// CHECK: #define cMakecuComplex(re,i) make_hipDoubleComplex(re,i)\n#define cMakecuComplex(re,i) make_cuDoubleComplex(re,i)\n#endif\n\n__device__ cTYPE juliaFunctor(cTYPE p, cTYPE c) {\n  // CHECK: return hipCaddf(hipCmulf(p, p), c);\n  return cuCaddf(cuCmulf(p, p), c);\n}\n\n__device__ cTYPE convertToComplex(int x, int y, float zoom) {\n  TYPE jx = 1.5 * (x - DIMX / 2) / (0.5 * zoom * DIMX) + moveX;\n  TYPE jy = (y - DIMY / 2) / (0.5 * zoom * DIMY) + moveY;\n  return cMakecuComplex(jx, jy);\n}\n\n__device__ int evolveComplexPoint(cTYPE p, cTYPE c) {\n  int it = 1;\n  // CHECK: while (it <= MAXITERATIONS && hipCabsf(p) <= 4) {\n  while (it <= MAXITERATIONS && cuCabsf(p) <= 4) {\n    p = juliaFunctor(p, c);\n    it++;\n  }\n  return it;\n}\n\n__global__ void computeJulia(int* data, cTYPE c, float zoom) {\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  int j = blockIdx.y * blockDim.y + threadIdx.y;\n\n  if (i<DIMX && j<DIMY) {\n    cTYPE p = convertToComplex(i, j, zoom);\n    data[i*DIMY + j] = evolveComplexPoint(p, c);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 2 --amap --skip-excluded-preprocessor-conditional-blocks %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n// CHECK: #include \"hip/hip_complex.h\"\n#include \"hip/hip_runtime.h\"\n#include \"hip/hip_complex.h\"\n\n#define TYPEFLOAT\n#define DIMX 100\n#define DIMY 40\n#define moveX 2\n#define moveY 1\n\n#define MAXITERATIONS 10\n\n#ifdef TYPEFLOAT\n#define TYPE float\n// CHECK: #define cTYPE hipFloatComplex\n#define cTYPE hipFloatComplex\n// CHECK: #define cMakecuComplex(re,i) make_hipFloatComplex(re,i)\n#define cMakecuComplex(re,i) make_hipFloatComplex(re,i)\n#endif\n#ifdef TYPEDOUBLE\n// CHECK: #define TYPE hipDoubleComplex\n#define TYPE hipDoubleComplex\n// CHECK: #define cMakecuComplex(re,i) make_hipDoubleComplex(re,i)\n#define cMakecuComplex(re,i) make_hipDoubleComplex(re,i)\n#endif\n\n__device__ cTYPE juliaFunctor(cTYPE p, cTYPE c) {\n  // CHECK: return hipCaddf(hipCmulf(p, p), c);\n  return hipCaddf(hipCmulf(p, p), c);\n}\n\n__device__ cTYPE convertToComplex(int x, int y, float zoom) {\n  TYPE jx = 1.5 * (x - DIMX / 2) / (0.5 * zoom * DIMX) + moveX;\n  TYPE jy = (y - DIMY / 2) / (0.5 * zoom * DIMY) + moveY;\n  return cMakecuComplex(jx, jy);\n}\n\n__device__ int evolveComplexPoint(cTYPE p, cTYPE c) {\n  int it = 1;\n  // CHECK: while (it <= MAXITERATIONS && hipCabsf(p) <= 4) {\n  while (it <= MAXITERATIONS && hipCabsf(p) <= 4) {\n    p = juliaFunctor(p, c);\n    it++;\n  }\n  return it;\n}\n\n__global__ void computeJulia(int* data, cTYPE c, float zoom) {\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  int j = blockIdx.y * blockDim.y + threadIdx.y;\n\n  if (i<DIMX && j<DIMY) {\n    cTYPE p = convertToComplex(i, j, zoom);\n    data[i*DIMY + j] = evolveComplexPoint(p, c);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/cuComplex/cuComplex_Julia.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// Taken from: http://docs.nvidia.com/cuda/curand/device-api-overview.html#poisson-api-example\n/*\n * This program uses CURAND library for Poisson distribution\n * to simulate queues in store for 16 hours. It shows the\n * difference of using 3 different APIs:\n * - HOST API -arrival of customers is described by Poisson(4)\n * - SIMPLE DEVICE API -arrival of customers is described by\n *     Poisson(4*(sin(x/100)+1)), where x is number of minutes\n *     from store opening time.\n * - ROBUST DEVICE API -arrival of customers is described by:\n *     - Poisson(2) for first 3 hours.\n *     - Poisson(1) for second 3 hours.\n *     - Poisson(3) after 6 hours.  \n */\n\n#include <stdio.h>\n#include <stdlib.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n// CHECK: #include <hiprand/hiprand_kernel.h>\n#include <curand_kernel.h>\n// CHECK: #include <hiprand/hiprand.h>\n#include <curand.h>\n\n// CHECK: #define CUDA_CALL(x) do { if((x) != hipSuccess) {\n#define CUDA_CALL(x) do { if((x) != cudaSuccess) { \\\n    printf(\"Error at %s:%d\\n\",__FILE__,__LINE__); \\\n    return EXIT_FAILURE;}} while(0)\n// CHECK: #define CURAND_CALL(x) do { if((x)!=HIPRAND_STATUS_SUCCESS) {\n#define CURAND_CALL(x) do { if((x)!=CURAND_STATUS_SUCCESS) { \\\n    printf(\"Error at %s:%d\\n\",__FILE__,__LINE__);\\\n    return EXIT_FAILURE;}} while(0)\n\n\n#define HOURS 16\n#define OPENING_HOUR 7\n#define CLOSING_HOUR (OPENING_HOUR + HOURS)\n\n#define access_2D(type, ptr, row, column, pitch)\\\n    *((type*)((char*)ptr + (row) * pitch) + column)\n\nenum API_TYPE {\n    HOST_API = 0,\n    SIMPLE_DEVICE_API = 1,\n    ROBUST_DEVICE_API = 2,\n};\n\n/* global variables */\nAPI_TYPE api;\nint report_break;\nint cashiers_load_h[HOURS];\n__constant__ int cashiers_load[HOURS];\n// CHECK: __global__ void setup_kernel(hiprandState *state)\n__global__ void setup_kernel(curandState *state)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    /* Each thread gets same seed, a different sequence \n       number, no offset */\n    // CHECK: hiprand_init(1234, id, 0, &state[id]);\n    curand_init(1234, id, 0, &state[id]);\n}\n\n__inline__ __device__\nvoid update_queue(int id, int min, unsigned int new_customers,\n                  unsigned int &queue_length,\n                  unsigned int *queue_lengths, size_t pitch)\n{\n    int balance;\n    balance = new_customers - 2 * cashiers_load[(min-1)/60];\n    if (balance + (int)queue_length <= 0){\n        queue_length = 0;\n    }else{\n        queue_length += balance;\n    }\n    /* Store results */\n    access_2D(unsigned int, queue_lengths, min-1, id, pitch)\n        = queue_length;\n}\n\n// CHECK: __global__ void simple_device_API_kernel(hiprandState *state,\n__global__ void simple_device_API_kernel(curandState *state,\n                    unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Copy state to local memory for efficiency */\n    // CHECK: hiprandState localState = state[id];\n    curandState localState = state[id];\n    /* Simulate queue in time */\n    for(int min = 1; min <= 60 * HOURS; min++) {\n        /* Draw number of new customers depending on API */\n        // CHECK: new_customers = hiprand_poisson(&localState,\n        new_customers = curand_poisson(&localState,\n                                4*(sin((float)min/100.0)+1));\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                     queue_lengths, pitch);       \n    }\n    /* Copy state back to global memory */\n    state[id] = localState;\n}\n\n\n__global__ void host_API_kernel(unsigned int *poisson_numbers,\n                    unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Simulate queue in time */\n    for(int min = 1; min <= 60 * HOURS; min++) {\n        /* Get random number from global memory */\n        new_customers = poisson_numbers\n                    [blockDim.x * gridDim.x * (min -1) + id];\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                     queue_lengths, pitch);\n    }\n}\n// CHECK: __global__ void robust_device_API_kernel(hiprandState *state,\n// CHECK: hiprandDiscreteDistribution_t poisson_1,\n// CHECK: hiprandDiscreteDistribution_t poisson_2,\n// CHECK: hiprandDiscreteDistribution_t poisson_3,\n__global__ void robust_device_API_kernel(curandState *state,\n                   curandDiscreteDistribution_t poisson_1,\n                   curandDiscreteDistribution_t poisson_2,\n                   curandDiscreteDistribution_t poisson_3,\n                   unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * 64;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Copy state to local memory for efficiency */\n    // CHECK: hiprandState localState = state[id];\n    curandState localState = state[id];\n    /* Simulate queue in time */\n    /* first 3 hours */\n    for(int min = 1; min <= 60 * 3; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_2);\n                    curand_discrete(&localState, poisson_2);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);\n    }\n    /* second 3 hours */\n    for(int min = 60 * 3 + 1; min <= 60 * 6; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_1);\n                    curand_discrete(&localState, poisson_1);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);       \n    }\n    /* after 6 hours */\n    for(int min = 60 * 6 + 1; min <= 60 * HOURS; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_3);\n                    curand_discrete(&localState, poisson_3);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);\n    }\n    /* Copy state back to global memory */\n    state[id] = localState;\n}\n\n/* Set time intervals between reports */\nvoid report_settings()\n{\n    do{\n        printf(\"Set time intervals between queue reports\");\n        printf(\"(in minutes > 0)\\n\");\n        if (scanf(\"%d\", &report_break) == 0) continue;\n    }while(report_break <= 0);\n}\n\n\n/* Set number of cashiers each hour */\nvoid add_cachiers(int *cashiers_load)\n{\n    int i, min, max, begin, end;\n    printf(\"Cashier serves 2 customers per minute...\\n\");\n    for (i = 0; i < HOURS; i++){\n        cashiers_load_h[i] = 0;\n    }\n    while (true){\n        printf(\"Adding cashier...\\n\");\n        min = OPENING_HOUR;\n        max = CLOSING_HOUR-1;\n        do{\n            printf(\"Set hour that cahier comes (%d-%d)\",\n                                                min, max);\n            printf(\" [type 0 to finish adding cashiers]\\n\");\n            if (scanf(\"%d\", &begin) == 0) continue;\n        }while (begin > max || (begin < min && begin != 0));\n        if (begin == 0) break;\n        min = begin+1;\n        max = CLOSING_HOUR;\n        do{\n            printf(\"Set hour that cahier leaves (%d-%d)\",\n                                                min, max);\n            printf(\" [type 0 to finish adding cashiers]\\n\");\n            if (scanf(\"%d\", &end) == 0) continue;\n        }while (end > max || (end < min && end != 0));\n        if (end == 0) break;\n        for (i = begin - OPENING_HOUR;\n             i < end   - OPENING_HOUR; i++){\n            cashiers_load_h[i]++;\n        }\n    }\n    for (i = OPENING_HOUR; i < CLOSING_HOUR; i++){\n        printf(\"\\n%2d:00 - %2d:00     %d cashier\",\n                i, i+1, cashiers_load_h[i-OPENING_HOUR]);\n        if (cashiers_load[i-OPENING_HOUR] != 1) printf(\"s\");\n    }\n    printf(\"\\n\");\n}\n\n/* Set API type */\nAPI_TYPE set_API_type()\n{\n    printf(\"Choose API type:\\n\");\n    int choose;\n    do{\n        printf(\"type 1 for HOST API\\n\");\n        printf(\"type 2 for SIMPLE DEVICE API\\n\");\n        printf(\"type 3 for ROBUST DEVICE API\\n\");\n        if (scanf(\"%d\", &choose) == 0) continue;\n    }while( choose < 1 || choose > 3);\n    switch(choose){\n        case 1: return HOST_API;\n        case 2: return SIMPLE_DEVICE_API;\n        case 3: return ROBUST_DEVICE_API;\n        default:\n            fprintf(stderr, \"wrong API\\n\");\n            return HOST_API;\n    }\n}\n\nvoid settings()\n{\n    add_cachiers(cashiers_load);\n    // CHECK: hipMemcpyToSymbol(HIP_SYMBOL(\"cashiers_load\"), cashiers_load_h,\n    // CHECK: HOURS * sizeof(int), 0, hipMemcpyHostToDevice);\n    cudaMemcpyToSymbol(\"cashiers_load\", cashiers_load_h,\n            HOURS * sizeof(int), 0, cudaMemcpyHostToDevice);\n    report_settings();\n    api = set_API_type();\n}\n\nvoid print_statistics(unsigned int *hostResults, size_t pitch)\n{\n    int min, i, hour, minute;\n    unsigned int sum;\n    for(min = report_break; min <= 60 * HOURS;\n                            min += report_break) {\n        sum = 0;\n        for(i = 0; i < 64 * 64; i++) {\n            sum += access_2D(unsigned int, hostResults,\n                                        min-1, i, pitch);\n        }\n        hour = OPENING_HOUR + min/60;\n        minute = min%60;\n        printf(\"%2d:%02d   # of waiting customers = %10.4g |\",\n                    hour, minute, (float)sum/(64.0 * 64.0));\n        printf(\"  # of cashiers = %d  |  \",\n                    cashiers_load_h[(min-1)/60]);\n        printf(\"# of new customers/min ~= \");\n        switch (api){\n            case HOST_API:\n                printf(\"%2.2f\\n\", 4.0);\n                break;\n            case SIMPLE_DEVICE_API:\n                printf(\"%2.2f\\n\",\n                            4*(sin((float)min/100.0)+1));\n                break;\n            case ROBUST_DEVICE_API:\n                if (min <= 3 * 60){\n                    printf(\"%2.2f\\n\", 2.0);\n                }else{\n                    if (min <= 6 * 60){\n                        printf(\"%2.2f\\n\", 1.0);\n                    }else{\n                        printf(\"%2.2f\\n\", 3.0);\n                    }\n                }\n                break;\n            default:\n                fprintf(stderr, \"Wrong API\\n\");\n        }\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    int n;\n    size_t pitch;\n    // CHECK: hiprandState *devStates;\n    curandState *devStates;\n    unsigned int *devResults, *hostResults;\n    unsigned int *poisson_numbers_d;\n    // CHECK: hiprandDiscreteDistribution_t poisson_1, poisson_2;\n    // CHECK: hiprandDiscreteDistribution_t poisson_3;\n    // CHECK: hiprandGenerator_t gen;\n    curandDiscreteDistribution_t poisson_1, poisson_2;\n    curandDiscreteDistribution_t poisson_3;\n    curandGenerator_t gen;\n\n    /* Setting cashiers, report and API */\n    settings();\n\n    /* Allocate space for results on device */\n    // CHECK: CUDA_CALL(hipMallocPitch((void **)&devResults, &pitch,\n    CUDA_CALL(cudaMallocPitch((void **)&devResults, &pitch,\n                64 * 64 * sizeof(unsigned int), 60 * HOURS));\n\n    /* Allocate space for results on host */\n    hostResults = (unsigned int *)calloc(pitch * 60 * HOURS,\n                sizeof(unsigned int));\n\n    /* Allocate space for prng states on device */\n    // CHECK: CUDA_CALL(hipMalloc((void **)&devStates, 64 * 64 *\n    // CHECK: sizeof(hiprandState)));\n    CUDA_CALL(cudaMalloc((void **)&devStates, 64 * 64 * \n              sizeof(curandState)));\n\n    /* Setup prng states */\n    if (api != HOST_API){\n        // CHECK: hipLaunchKernelGGL(setup_kernel, dim3(64), dim3(64), 0, 0, devStates);\n        setup_kernel<<<64, 64>>>(devStates);\n    }\n    /* Simulate queue  */\n    switch (api){\n        case HOST_API:\n            /* Create pseudo-random number generator */\n            // CHECK: CURAND_CALL(hiprandCreateGenerator(&gen,\n                                // CHECK: HIPRAND_RNG_PSEUDO_DEFAULT));\n            CURAND_CALL(curandCreateGenerator(&gen,\n                                CURAND_RNG_PSEUDO_DEFAULT));\n            /* Set seed */\n            // CHECK: CURAND_CALL(hiprandSetPseudoRandomGeneratorSeed(\n            CURAND_CALL(curandSetPseudoRandomGeneratorSeed(\n                                            gen, 1234ULL));\n            /* compute n */\n            n = 64 * 64 * HOURS * 60;\n            /* Allocate n unsigned ints on device */\n            // CHECK: CUDA_CALL(hipMalloc((void **)&poisson_numbers_d,\n            CUDA_CALL(cudaMalloc((void **)&poisson_numbers_d,\n                                n * sizeof(unsigned int)));\n            /* Generate n unsigned ints on device */\n            // CHECK: CURAND_CALL(hiprandGeneratePoisson(gen,\n            CURAND_CALL(curandGeneratePoisson(gen,\n                                poisson_numbers_d, n, 4.0));\n            // CHECK: hipLaunchKernelGGL(host_API_kernel, dim3(64), dim3(64), 0, 0, poisson_numbers_d,\n            host_API_kernel<<<64, 64>>>(poisson_numbers_d,\n                                        devResults, pitch);\n            /* Cleanup */\n            // CHECK: CURAND_CALL(hiprandDestroyGenerator(gen));\n            CURAND_CALL(curandDestroyGenerator(gen));\n            break;\n        case SIMPLE_DEVICE_API:\n            // CHECK: hipLaunchKernelGGL(simple_device_API_kernel, dim3(64), dim3(64), 0, 0, devStates,\n            simple_device_API_kernel<<<64, 64>>>(devStates,\n                                        devResults, pitch);\n            break;\n        case ROBUST_DEVICE_API:\n            /* Create histograms for Poisson(1) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(1.0,\n            CURAND_CALL(curandCreatePoissonDistribution(1.0,\n                                                &poisson_1));\n            /* Create histograms for Poisson(2) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(2.0,\n            CURAND_CALL(curandCreatePoissonDistribution(2.0,\n                                                &poisson_2));\n            /* Create histograms for Poisson(3) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(3.0,\n            CURAND_CALL(curandCreatePoissonDistribution(3.0,\n                                                &poisson_3));\n            // CHECK: hipLaunchKernelGGL(robust_device_API_kernel, dim3(64), dim3(64), 0, 0, devStates,\n            robust_device_API_kernel<<<64, 64>>>(devStates,\n                            poisson_1, poisson_2, poisson_3,\n                            devResults, pitch);\n            /* Cleanup */\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_1));\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_2));\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_3));\n            CURAND_CALL(curandDestroyDistribution(poisson_1));\n            CURAND_CALL(curandDestroyDistribution(poisson_2));\n            CURAND_CALL(curandDestroyDistribution(poisson_3));\n            break;\n        default:\n            fprintf(stderr, \"Wrong API\\n\");\n    }\n    /* Copy device memory to host */\n    // CHECK: CUDA_CALL(hipMemcpy2D(hostResults, pitch, devResults,\n    // CHECK: 60 * HOURS, hipMemcpyDeviceToHost));\n    CUDA_CALL(cudaMemcpy2D(hostResults, pitch, devResults,\n                pitch, 64 * 64 * sizeof(unsigned int),\n                60 * HOURS, cudaMemcpyDeviceToHost));\n    /* Show result */\n    print_statistics(hostResults, pitch);\n    /* Cleanup */\n    // CHECK: CUDA_CALL(hipFree(devStates));\n    // CHECK: CUDA_CALL(hipFree(devResults));\n    CUDA_CALL(cudaFree(devStates));\n    CUDA_CALL(cudaFree(devResults));\n    free(hostResults);\n    return EXIT_SUCCESS;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// Taken from: http://docs.nvidia.com/cuda/curand/device-api-overview.html#poisson-api-example\n/*\n * This program uses CURAND library for Poisson distribution\n * to simulate queues in store for 16 hours. It shows the\n * difference of using 3 different APIs:\n * - HOST API -arrival of customers is described by Poisson(4)\n * - SIMPLE DEVICE API -arrival of customers is described by\n *     Poisson(4*(sin(x/100)+1)), where x is number of minutes\n *     from store opening time.\n * - ROBUST DEVICE API -arrival of customers is described by:\n *     - Poisson(2) for first 3 hours.\n *     - Poisson(1) for second 3 hours.\n *     - Poisson(3) after 6 hours.  \n */\n\n#include <stdio.h>\n#include <stdlib.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n// CHECK: #include <hiprand/hiprand_kernel.h>\n#include <hiprand/hiprand_kernel.h>\n// CHECK: #include <hiprand/hiprand.h>\n#include <hiprand/hiprand.h>\n\n// CHECK: #define CUDA_CALL(x) do { if((x) != hipSuccess) {\n#define CUDA_CALL(x) do { if((x) != hipSuccess) { \\\n    printf(\"Error at %s:%d\\n\",__FILE__,__LINE__); \\\n    return EXIT_FAILURE;}} while(0)\n// CHECK: #define CURAND_CALL(x) do { if((x)!=HIPRAND_STATUS_SUCCESS) {\n#define CURAND_CALL(x) do { if((x)!=HIPRAND_STATUS_SUCCESS) { \\\n    printf(\"Error at %s:%d\\n\",__FILE__,__LINE__);\\\n    return EXIT_FAILURE;}} while(0)\n\n\n#define HOURS 16\n#define OPENING_HOUR 7\n#define CLOSING_HOUR (OPENING_HOUR + HOURS)\n\n#define access_2D(type, ptr, row, column, pitch)\\\n    *((type*)((char*)ptr + (row) * pitch) + column)\n\nenum API_TYPE {\n    HOST_API = 0,\n    SIMPLE_DEVICE_API = 1,\n    ROBUST_DEVICE_API = 2,\n};\n\n/* global variables */\nAPI_TYPE api;\nint report_break;\nint cashiers_load_h[HOURS];\n__constant__ int cashiers_load[HOURS];\n// CHECK: __global__ void setup_kernel(hiprandState *state)\n__global__ void setup_kernel(hiprandState *state)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    /* Each thread gets same seed, a different sequence \n       number, no offset */\n    // CHECK: hiprand_init(1234, id, 0, &state[id]);\n    hiprand_init(1234, id, 0, &state[id]);\n}\n\n__inline__ __device__\nvoid update_queue(int id, int min, unsigned int new_customers,\n                  unsigned int &queue_length,\n                  unsigned int *queue_lengths, size_t pitch)\n{\n    int balance;\n    balance = new_customers - 2 * cashiers_load[(min-1)/60];\n    if (balance + (int)queue_length <= 0){\n        queue_length = 0;\n    }else{\n        queue_length += balance;\n    }\n    /* Store results */\n    access_2D(unsigned int, queue_lengths, min-1, id, pitch)\n        = queue_length;\n}\n\n// CHECK: __global__ void simple_device_API_kernel(hiprandState *state,\n__global__ void simple_device_API_kernel(hiprandState *state,\n                    unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Copy state to local memory for efficiency */\n    // CHECK: hiprandState localState = state[id];\n    hiprandState localState = state[id];\n    /* Simulate queue in time */\n    for(int min = 1; min <= 60 * HOURS; min++) {\n        /* Draw number of new customers depending on API */\n        // CHECK: new_customers = hiprand_poisson(&localState,\n        new_customers = hiprand_poisson(&localState,\n                                4*(sin((float)min/100.0)+1));\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                     queue_lengths, pitch);       \n    }\n    /* Copy state back to global memory */\n    state[id] = localState;\n}\n\n\n__global__ void host_API_kernel(unsigned int *poisson_numbers,\n                    unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Simulate queue in time */\n    for(int min = 1; min <= 60 * HOURS; min++) {\n        /* Get random number from global memory */\n        new_customers = poisson_numbers\n                    [blockDim.x * gridDim.x * (min -1) + id];\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                     queue_lengths, pitch);\n    }\n}\n// CHECK: __global__ void robust_device_API_kernel(hiprandState *state,\n// CHECK: hiprandDiscreteDistribution_t poisson_1,\n// CHECK: hiprandDiscreteDistribution_t poisson_2,\n// CHECK: hiprandDiscreteDistribution_t poisson_3,\n__global__ void robust_device_API_kernel(hiprandState *state,\n                   hiprandDiscreteDistribution_t poisson_1,\n                   hiprandDiscreteDistribution_t poisson_2,\n                   hiprandDiscreteDistribution_t poisson_3,\n                   unsigned int *queue_lengths, size_t pitch)\n{\n    int id = threadIdx.x + blockIdx.x * 64;\n    unsigned int new_customers;\n    unsigned int queue_length = 0;\n    /* Copy state to local memory for efficiency */\n    // CHECK: hiprandState localState = state[id];\n    hiprandState localState = state[id];\n    /* Simulate queue in time */\n    /* first 3 hours */\n    for(int min = 1; min <= 60 * 3; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_2);\n                    hiprand_discrete(&localState, poisson_2);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);\n    }\n    /* second 3 hours */\n    for(int min = 60 * 3 + 1; min <= 60 * 6; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_1);\n                    hiprand_discrete(&localState, poisson_1);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);       \n    }\n    /* after 6 hours */\n    for(int min = 60 * 6 + 1; min <= 60 * HOURS; min++) {\n        /* draw number of new customers depending on API */\n        new_customers =\n                    // CHECK: hiprand_discrete(&localState, poisson_3);\n                    hiprand_discrete(&localState, poisson_3);\n        /* Update queue */\n        update_queue(id, min, new_customers, queue_length,\n                                    queue_lengths, pitch);\n    }\n    /* Copy state back to global memory */\n    state[id] = localState;\n}\n\n/* Set time intervals between reports */\nvoid report_settings()\n{\n    do{\n        printf(\"Set time intervals between queue reports\");\n        printf(\"(in minutes > 0)\\n\");\n        if (scanf(\"%d\", &report_break) == 0) continue;\n    }while(report_break <= 0);\n}\n\n\n/* Set number of cashiers each hour */\nvoid add_cachiers(int *cashiers_load)\n{\n    int i, min, max, begin, end;\n    printf(\"Cashier serves 2 customers per minute...\\n\");\n    for (i = 0; i < HOURS; i++){\n        cashiers_load_h[i] = 0;\n    }\n    while (true){\n        printf(\"Adding cashier...\\n\");\n        min = OPENING_HOUR;\n        max = CLOSING_HOUR-1;\n        do{\n            printf(\"Set hour that cahier comes (%d-%d)\",\n                                                min, max);\n            printf(\" [type 0 to finish adding cashiers]\\n\");\n            if (scanf(\"%d\", &begin) == 0) continue;\n        }while (begin > max || (begin < min && begin != 0));\n        if (begin == 0) break;\n        min = begin+1;\n        max = CLOSING_HOUR;\n        do{\n            printf(\"Set hour that cahier leaves (%d-%d)\",\n                                                min, max);\n            printf(\" [type 0 to finish adding cashiers]\\n\");\n            if (scanf(\"%d\", &end) == 0) continue;\n        }while (end > max || (end < min && end != 0));\n        if (end == 0) break;\n        for (i = begin - OPENING_HOUR;\n             i < end   - OPENING_HOUR; i++){\n            cashiers_load_h[i]++;\n        }\n    }\n    for (i = OPENING_HOUR; i < CLOSING_HOUR; i++){\n        printf(\"\\n%2d:00 - %2d:00     %d cashier\",\n                i, i+1, cashiers_load_h[i-OPENING_HOUR]);\n        if (cashiers_load[i-OPENING_HOUR] != 1) printf(\"s\");\n    }\n    printf(\"\\n\");\n}\n\n/* Set API type */\nAPI_TYPE set_API_type()\n{\n    printf(\"Choose API type:\\n\");\n    int choose;\n    do{\n        printf(\"type 1 for HOST API\\n\");\n        printf(\"type 2 for SIMPLE DEVICE API\\n\");\n        printf(\"type 3 for ROBUST DEVICE API\\n\");\n        if (scanf(\"%d\", &choose) == 0) continue;\n    }while( choose < 1 || choose > 3);\n    switch(choose){\n        case 1: return HOST_API;\n        case 2: return SIMPLE_DEVICE_API;\n        case 3: return ROBUST_DEVICE_API;\n        default:\n            fprintf(stderr, \"wrong API\\n\");\n            return HOST_API;\n    }\n}\n\nvoid settings()\n{\n    add_cachiers(cashiers_load);\n    // CHECK: hipMemcpyToSymbol(HIP_SYMBOL(\"cashiers_load\"), cashiers_load_h,\n    // CHECK: HOURS * sizeof(int), 0, hipMemcpyHostToDevice);\n    hipMemcpyToSymbol(HIP_SYMBOL(\"cashiers_load\"), cashiers_load_h,\n            HOURS * sizeof(int), 0, hipMemcpyHostToDevice);\n    report_settings();\n    api = set_API_type();\n}\n\nvoid print_statistics(unsigned int *hostResults, size_t pitch)\n{\n    int min, i, hour, minute;\n    unsigned int sum;\n    for(min = report_break; min <= 60 * HOURS;\n                            min += report_break) {\n        sum = 0;\n        for(i = 0; i < 64 * 64; i++) {\n            sum += access_2D(unsigned int, hostResults,\n                                        min-1, i, pitch);\n        }\n        hour = OPENING_HOUR + min/60;\n        minute = min%60;\n        printf(\"%2d:%02d   # of waiting customers = %10.4g |\",\n                    hour, minute, (float)sum/(64.0 * 64.0));\n        printf(\"  # of cashiers = %d  |  \",\n                    cashiers_load_h[(min-1)/60]);\n        printf(\"# of new customers/min ~= \");\n        switch (api){\n            case HOST_API:\n                printf(\"%2.2f\\n\", 4.0);\n                break;\n            case SIMPLE_DEVICE_API:\n                printf(\"%2.2f\\n\",\n                            4*(sin((float)min/100.0)+1));\n                break;\n            case ROBUST_DEVICE_API:\n                if (min <= 3 * 60){\n                    printf(\"%2.2f\\n\", 2.0);\n                }else{\n                    if (min <= 6 * 60){\n                        printf(\"%2.2f\\n\", 1.0);\n                    }else{\n                        printf(\"%2.2f\\n\", 3.0);\n                    }\n                }\n                break;\n            default:\n                fprintf(stderr, \"Wrong API\\n\");\n        }\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    int n;\n    size_t pitch;\n    // CHECK: hiprandState *devStates;\n    hiprandState *devStates;\n    unsigned int *devResults, *hostResults;\n    unsigned int *poisson_numbers_d;\n    // CHECK: hiprandDiscreteDistribution_t poisson_1, poisson_2;\n    // CHECK: hiprandDiscreteDistribution_t poisson_3;\n    // CHECK: hiprandGenerator_t gen;\n    hiprandDiscreteDistribution_t poisson_1, poisson_2;\n    hiprandDiscreteDistribution_t poisson_3;\n    hiprandGenerator_t gen;\n\n    /* Setting cashiers, report and API */\n    settings();\n\n    /* Allocate space for results on device */\n    // CHECK: CUDA_CALL(hipMallocPitch((void **)&devResults, &pitch,\n    CUDA_CALL(hipMallocPitch((void **)&devResults, &pitch,\n                64 * 64 * sizeof(unsigned int), 60 * HOURS));\n\n    /* Allocate space for results on host */\n    hostResults = (unsigned int *)calloc(pitch * 60 * HOURS,\n                sizeof(unsigned int));\n\n    /* Allocate space for prng states on device */\n    // CHECK: CUDA_CALL(hipMalloc((void **)&devStates, 64 * 64 *\n    // CHECK: sizeof(hiprandState)));\n    CUDA_CALL(hipMalloc((void **)&devStates, 64 * 64 * \n              sizeof(hiprandState)));\n\n    /* Setup prng states */\n    if (api != HOST_API){\n        // CHECK: hipLaunchKernelGGL(setup_kernel, dim3(64), dim3(64), 0, 0, devStates);\n        setup_kernel<<<64, 64>>>(devStates);\n    }\n    /* Simulate queue  */\n    switch (api){\n        case HOST_API:\n            /* Create pseudo-random number generator */\n            // CHECK: CURAND_CALL(hiprandCreateGenerator(&gen,\n                                // CHECK: HIPRAND_RNG_PSEUDO_DEFAULT));\n            CURAND_CALL(hiprandCreateGenerator(&gen,\n                                HIPRAND_RNG_PSEUDO_DEFAULT));\n            /* Set seed */\n            // CHECK: CURAND_CALL(hiprandSetPseudoRandomGeneratorSeed(\n            CURAND_CALL(hiprandSetPseudoRandomGeneratorSeed(\n                                            gen, 1234ULL));\n            /* compute n */\n            n = 64 * 64 * HOURS * 60;\n            /* Allocate n unsigned ints on device */\n            // CHECK: CUDA_CALL(hipMalloc((void **)&poisson_numbers_d,\n            CUDA_CALL(hipMalloc((void **)&poisson_numbers_d,\n                                n * sizeof(unsigned int)));\n            /* Generate n unsigned ints on device */\n            // CHECK: CURAND_CALL(hiprandGeneratePoisson(gen,\n            CURAND_CALL(hiprandGeneratePoisson(gen,\n                                poisson_numbers_d, n, 4.0));\n            // CHECK: hipLaunchKernelGGL(host_API_kernel, dim3(64), dim3(64), 0, 0, poisson_numbers_d,\n            host_API_kernel<<<64, 64>>>(poisson_numbers_d,\n                                        devResults, pitch);\n            /* Cleanup */\n            // CHECK: CURAND_CALL(hiprandDestroyGenerator(gen));\n            CURAND_CALL(hiprandDestroyGenerator(gen));\n            break;\n        case SIMPLE_DEVICE_API:\n            // CHECK: hipLaunchKernelGGL(simple_device_API_kernel, dim3(64), dim3(64), 0, 0, devStates,\n            simple_device_API_kernel<<<64, 64>>>(devStates,\n                                        devResults, pitch);\n            break;\n        case ROBUST_DEVICE_API:\n            /* Create histograms for Poisson(1) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(1.0,\n            CURAND_CALL(hiprandCreatePoissonDistribution(1.0,\n                                                &poisson_1));\n            /* Create histograms for Poisson(2) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(2.0,\n            CURAND_CALL(hiprandCreatePoissonDistribution(2.0,\n                                                &poisson_2));\n            /* Create histograms for Poisson(3) */\n            // CHECK: CURAND_CALL(hiprandCreatePoissonDistribution(3.0,\n            CURAND_CALL(hiprandCreatePoissonDistribution(3.0,\n                                                &poisson_3));\n            // CHECK: hipLaunchKernelGGL(robust_device_API_kernel, dim3(64), dim3(64), 0, 0, devStates,\n            robust_device_API_kernel<<<64, 64>>>(devStates,\n                            poisson_1, poisson_2, poisson_3,\n                            devResults, pitch);\n            /* Cleanup */\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_1));\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_2));\n            // CHECK: CURAND_CALL(hiprandDestroyDistribution(poisson_3));\n            CURAND_CALL(hiprandDestroyDistribution(poisson_1));\n            CURAND_CALL(hiprandDestroyDistribution(poisson_2));\n            CURAND_CALL(hiprandDestroyDistribution(poisson_3));\n            break;\n        default:\n            fprintf(stderr, \"Wrong API\\n\");\n    }\n    /* Copy device memory to host */\n    // CHECK: CUDA_CALL(hipMemcpy2D(hostResults, pitch, devResults,\n    // CHECK: 60 * HOURS, hipMemcpyDeviceToHost));\n    CUDA_CALL(hipMemcpy2D(hostResults, pitch, devResults,\n                pitch, 64 * 64 * sizeof(unsigned int),\n                60 * HOURS, hipMemcpyDeviceToHost));\n    /* Show result */\n    print_statistics(hostResults, pitch);\n    /* Cleanup */\n    // CHECK: CUDA_CALL(hipFree(devStates));\n    // CHECK: CUDA_CALL(hipFree(devResults));\n    CUDA_CALL(hipFree(devStates));\n    CUDA_CALL(hipFree(devResults));\n    free(hostResults);\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/cuRAND/poisson_api_example.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <iostream>\n// CHECK: #include <hiprand/hiprand.h>\n#include <curand.h>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <cub/cub.cuh>\n\n#include <iostream>\n\n// TODO:\n// using namespace cub;\n\ntemplate <typename T>\n__global__ void sort(const T* data_in, T* data_out){\n     // CHECK: typedef ::hipcub::BlockRadixSort<T, 1024, 4> BlockRadixSortT;\n     typedef ::cub::BlockRadixSort<T, 1024, 4> BlockRadixSortT;\n    __shared__ typename BlockRadixSortT::TempStorage tmp_sort;\n    double items[4];\n    int i0 = 4 * (blockIdx.x * blockDim.x + threadIdx.x);\n    for (int i = 0; i < 4; ++i){\n        items[i] = data_in[i0 + i];\n    }\n    BlockRadixSortT(tmp_sort).Sort(items);\n    for (int i = 0; i < 4; ++i){\n        data_out[i0 + i] = items[i];\n    }\n}\n\nint main(){\n    double* d_gpu = NULL;\n    double* result_gpu = NULL;\n    double* data_sorted = new double[4096];\n    // Allocate memory on the GPU\n    // CHECK: hipMalloc(&d_gpu, 4096 * sizeof(double));\n    cudaMalloc(&d_gpu, 4096 * sizeof(double));\n    // CHECK: hipMalloc(&result_gpu, 4096 * sizeof(double));\n    cudaMalloc(&result_gpu, 4096 * sizeof(double));\n    // CHECK: hiprandGenerator_t gen;\n    curandGenerator_t gen;\n    // Create generator\n    // CHECK: hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);\n    // Fill array with random numbers\n    // CHECK: hiprandGenerateNormalDouble(gen, d_gpu, 4096, 0.0, 1.0);\n    curandGenerateNormalDouble(gen, d_gpu, 4096, 0.0, 1.0);\n    // Destroy generator\n    // CHECK: hiprandDestroyGenerator(gen);\n    curandDestroyGenerator(gen);\n    // Sort data\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort), dim3(1), dim3(1024), 0, 0, d_gpu, result_gpu);\n    sort<<<1, 1024>>>(d_gpu, result_gpu);\n    // CHECK: hipMemcpy(data_sorted, result_gpu, 4096 * sizeof(double), hipMemcpyDeviceToHost);\n    cudaMemcpy(data_sorted, result_gpu, 4096 * sizeof(double), cudaMemcpyDeviceToHost);\n    // Write the sorted data to standard out\n    for (int i = 0; i < 4096; ++i){\n        std::cout << data_sorted[i] << \", \";\n    }\n    std::cout << std::endl;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n// CHECK: #include <hiprand/hiprand.h>\n#include <hiprand/hiprand.h>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <hipcub/hipcub.hpp>\n\n#include <iostream>\n\n// TODO:\n// using namespace cub;\n\ntemplate <typename T>\n__global__ void sort(const T* data_in, T* data_out){\n     // CHECK: typedef ::hipcub::BlockRadixSort<T, 1024, 4> BlockRadixSortT;\n     typedef ::hipcub::BlockRadixSort<T, 1024, 4> BlockRadixSortT;\n    __shared__ typename BlockRadixSortT::TempStorage tmp_sort;\n    double items[4];\n    int i0 = 4 * (blockIdx.x * blockDim.x + threadIdx.x);\n    for (int i = 0; i < 4; ++i){\n        items[i] = data_in[i0 + i];\n    }\n    BlockRadixSortT(tmp_sort).Sort(items);\n    for (int i = 0; i < 4; ++i){\n        data_out[i0 + i] = items[i];\n    }\n}\n\nint main(){\n    double* d_gpu = NULL;\n    double* result_gpu = NULL;\n    double* data_sorted = new double[4096];\n    // Allocate memory on the GPU\n    // CHECK: hipMalloc(&d_gpu, 4096 * sizeof(double));\n    hipMalloc(&d_gpu, 4096 * sizeof(double));\n    // CHECK: hipMalloc(&result_gpu, 4096 * sizeof(double));\n    hipMalloc(&result_gpu, 4096 * sizeof(double));\n    // CHECK: hiprandGenerator_t gen;\n    hiprandGenerator_t gen;\n    // Create generator\n    // CHECK: hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    // Fill array with random numbers\n    // CHECK: hiprandGenerateNormalDouble(gen, d_gpu, 4096, 0.0, 1.0);\n    hiprandGenerateNormalDouble(gen, d_gpu, 4096, 0.0, 1.0);\n    // Destroy generator\n    // CHECK: hiprandDestroyGenerator(gen);\n    hiprandDestroyGenerator(gen);\n    // Sort data\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort), dim3(1), dim3(1024), 0, 0, d_gpu, result_gpu);\n    sort<<<1, 1024>>>(d_gpu, result_gpu);\n    // CHECK: hipMemcpy(data_sorted, result_gpu, 4096 * sizeof(double), hipMemcpyDeviceToHost);\n    hipMemcpy(data_sorted, result_gpu, 4096 * sizeof(double), hipMemcpyDeviceToHost);\n    // Write the sorted data to standard out\n    for (int i = 0; i < 4096; ++i){\n        std::cout << data_sorted[i] << \", \";\n    }\n    std::cout << std::endl;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/CUB/cub_01.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <iostream>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <cub/cub.cuh>\n\n// using namespace hipcub;\nusing namespace cub;\n\n// Simple CUDA kernel for computing tiled partial sums\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD,\n          // CHECK: hipcub::BlockLoadAlgorithm LOAD_ALGO,\n          cub::BlockLoadAlgorithm LOAD_ALGO,\n          // CHECK: hipcub::BlockScanAlgorithm SCAN_ALGO>\n          cub::BlockScanAlgorithm SCAN_ALGO>\n__global__ void ScanTilesKernel(int *d_in, int *d_out) {\n  // Specialize collective types for problem context\n  // CHECK: typedef ::hipcub::BlockLoad<int*, BLOCK_THREADS, ITEMS_PER_THREAD, LOAD_ALGO> BlockLoadT;\n  typedef ::cub::BlockLoad<int*, BLOCK_THREADS, ITEMS_PER_THREAD, LOAD_ALGO> BlockLoadT;\n  typedef BlockScan<int, BLOCK_THREADS, SCAN_ALGO> BlockScanT;\n  // Allocate on-chip temporary storage\n  __shared__ union {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage reduce;\n  } temp_storage;\n  // Load data per thread\n  int thread_data[ITEMS_PER_THREAD];\n  int offset = blockIdx.x * (BLOCK_THREADS * ITEMS_PER_THREAD);\n  BlockLoadT(temp_storage.load).Load(d_in + offset, offset);\n  __syncthreads();\n  // Compute the block-wide prefix sum\n  BlockScanT(temp_storage).Sum(thread_data);\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <hipcub/hipcub.hpp>\n\n// using namespace hipcub;\nusing namespace hipcub;\n\n// Simple CUDA kernel for computing tiled partial sums\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD,\n          // CHECK: hipcub::BlockLoadAlgorithm LOAD_ALGO,\n          hipcub::BlockLoadAlgorithm LOAD_ALGO,\n          // CHECK: hipcub::BlockScanAlgorithm SCAN_ALGO>\n          hipcub::BlockScanAlgorithm SCAN_ALGO>\n__global__ void ScanTilesKernel(int *d_in, int *d_out) {\n  // Specialize collective types for problem context\n  // CHECK: typedef ::hipcub::BlockLoad<int*, BLOCK_THREADS, ITEMS_PER_THREAD, LOAD_ALGO> BlockLoadT;\n  typedef ::hipcub::BlockLoad<int*, BLOCK_THREADS, ITEMS_PER_THREAD, LOAD_ALGO> BlockLoadT;\n  typedef BlockScan<int, BLOCK_THREADS, SCAN_ALGO> BlockScanT;\n  // Allocate on-chip temporary storage\n  __shared__ union {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage reduce;\n  } temp_storage;\n  // Load data per thread\n  int thread_data[ITEMS_PER_THREAD];\n  int offset = blockIdx.x * (BLOCK_THREADS * ITEMS_PER_THREAD);\n  BlockLoadT(temp_storage.load).Load(d_in + offset, offset);\n  __syncthreads();\n  // Compute the block-wide prefix sum\n  BlockScanT(temp_storage).Sum(thread_data);\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/CUB/cub_03.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <iostream>\n// CHECK: #include <hiprand/hiprand.h>\n#include <curand.h>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <cub/cub.cuh>\n\n#include <iostream>\n\ntemplate <int BLOCK_WIDTH, int ITEMS_PER_THREAD,\n          // CHECK: hipcub::BlockLoadAlgorithm BLOCK_LOAD_ALGO,\n          cub::BlockLoadAlgorithm BLOCK_LOAD_ALGO,\n          // CHECK: hipcub::BlockStoreAlgorithm BLOCK_STORE_ALGO,\n          cub::BlockStoreAlgorithm BLOCK_STORE_ALGO,\n          typename T>\n__global__ void sort(const T* data_in, T* data_out){\n    // CHECK: typedef hipcub::BlockLoad<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_LOAD_ALGO> BlockLoadT;\n    typedef cub::BlockLoad<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_LOAD_ALGO> BlockLoadT;\n    // CHECK: typedef hipcub::BlockRadixSort<T, BLOCK_WIDTH, ITEMS_PER_THREAD> BlockRadixSortT;\n    typedef cub::BlockRadixSort<T, BLOCK_WIDTH, ITEMS_PER_THREAD> BlockRadixSortT;\n    // CHECK: typedef hipcub::BlockStore<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_STORE_ALGO> BlockStoreT;\n    typedef cub::BlockStore<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_STORE_ALGO> BlockStoreT;\n    __shared__ union {\n        typename BlockLoadT::TempStorage load;\n        typename BlockRadixSortT::TempStorage sort;\n        typename BlockStoreT::TempStorage store;\n    } tmp_storage;\n    T items[ITEMS_PER_THREAD];\n    BlockLoadT(tmp_storage.load).Load(data_in + blockIdx.x * BLOCK_WIDTH * ITEMS_PER_THREAD, items);\n    __syncthreads();\n    BlockRadixSortT(tmp_storage.sort).Sort(items);\n    __syncthreads();\n    BlockStoreT(tmp_storage.store).Store(data_out + blockIdx.x * BLOCK_WIDTH * ITEMS_PER_THREAD, items);\n}\n\nint main() {\n    double* d_gpu = NULL;\n    double* result_gpu = NULL;\n    double* data_sorted = new double[1000*4096];\n    // Allocate memory on the GPU\n    // CHECK: hipMalloc(&d_gpu, 1000*4096 * sizeof(double));\n    cudaMalloc(&d_gpu, 1000*4096 * sizeof(double));\n    // CHECK: hipMalloc(&result_gpu, 1000*4096 * sizeof(double));\n    cudaMalloc(&result_gpu, 1000*4096 * sizeof(double));\n    // CHECK: hiprandGenerator_t gen;\n    curandGenerator_t gen;\n    // Create generator\n    // CHECK: hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);\n    // Fill array with random numbers\n    // CHECK: hiprandGenerateNormalDouble(gen, d_gpu, 1000*4096, 0.0, 1.0);\n    curandGenerateNormalDouble(gen, d_gpu, 1000*4096, 0.0, 1.0);\n    // Destroy generator\n    // CHECK: hiprandDestroyGenerator(gen);\n    curandDestroyGenerator(gen);\n    // Sort data\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort<512, 8, hipcub::BLOCK_LOAD_TRANSPOSE, hipcub::BLOCK_STORE_TRANSPOSE>), dim3(1000), dim3(512), 0, 0, d_gpu, result_gpu);\n    sort<512, 8, cub::BLOCK_LOAD_TRANSPOSE, cub::BLOCK_STORE_TRANSPOSE><<<1000, 512>>>(d_gpu, result_gpu);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort<256, 16, hipcub::BLOCK_LOAD_DIRECT, hipcub::BLOCK_STORE_DIRECT>), dim3(1000), dim3(256), 0, 0, d_gpu, result_gpu);\n    sort<256, 16, cub::BLOCK_LOAD_DIRECT, cub::BLOCK_STORE_DIRECT><<<1000, 256>>>(d_gpu, result_gpu);\n    // CHECK: hipMemcpy(data_sorted, result_gpu, 1000*4096*sizeof(double), hipMemcpyDeviceToHost);\n    cudaMemcpy(data_sorted, result_gpu, 1000*4096*sizeof(double), cudaMemcpyDeviceToHost);\n    // Write the sorted data to standard out\n    for (int i = 0; i < 4095; ++i) {\n        std::cout << data_sorted[i] << \", \";\n    }\n    std::cout << data_sorted[4095] << std::endl;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n// CHECK: #include <hiprand/hiprand.h>\n#include <hiprand/hiprand.h>\n#define THRUST_NS_QUALIFIER ::thrust\n// CHECK: #include <hipcub/hipcub.hpp>\n#include <hipcub/hipcub.hpp>\n\n#include <iostream>\n\ntemplate <int BLOCK_WIDTH, int ITEMS_PER_THREAD,\n          // CHECK: hipcub::BlockLoadAlgorithm BLOCK_LOAD_ALGO,\n          hipcub::BlockLoadAlgorithm BLOCK_LOAD_ALGO,\n          // CHECK: hipcub::BlockStoreAlgorithm BLOCK_STORE_ALGO,\n          hipcub::BlockStoreAlgorithm BLOCK_STORE_ALGO,\n          typename T>\n__global__ void sort(const T* data_in, T* data_out){\n    // CHECK: typedef hipcub::BlockLoad<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_LOAD_ALGO> BlockLoadT;\n    typedef hipcub::BlockLoad<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_LOAD_ALGO> BlockLoadT;\n    // CHECK: typedef hipcub::BlockRadixSort<T, BLOCK_WIDTH, ITEMS_PER_THREAD> BlockRadixSortT;\n    typedef hipcub::BlockRadixSort<T, BLOCK_WIDTH, ITEMS_PER_THREAD> BlockRadixSortT;\n    // CHECK: typedef hipcub::BlockStore<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_STORE_ALGO> BlockStoreT;\n    typedef hipcub::BlockStore<T, BLOCK_WIDTH, ITEMS_PER_THREAD, BLOCK_STORE_ALGO> BlockStoreT;\n    __shared__ union {\n        typename BlockLoadT::TempStorage load;\n        typename BlockRadixSortT::TempStorage sort;\n        typename BlockStoreT::TempStorage store;\n    } tmp_storage;\n    T items[ITEMS_PER_THREAD];\n    BlockLoadT(tmp_storage.load).Load(data_in + blockIdx.x * BLOCK_WIDTH * ITEMS_PER_THREAD, items);\n    __syncthreads();\n    BlockRadixSortT(tmp_storage.sort).Sort(items);\n    __syncthreads();\n    BlockStoreT(tmp_storage.store).Store(data_out + blockIdx.x * BLOCK_WIDTH * ITEMS_PER_THREAD, items);\n}\n\nint main() {\n    double* d_gpu = NULL;\n    double* result_gpu = NULL;\n    double* data_sorted = new double[1000*4096];\n    // Allocate memory on the GPU\n    // CHECK: hipMalloc(&d_gpu, 1000*4096 * sizeof(double));\n    hipMalloc(&d_gpu, 1000*4096 * sizeof(double));\n    // CHECK: hipMalloc(&result_gpu, 1000*4096 * sizeof(double));\n    hipMalloc(&result_gpu, 1000*4096 * sizeof(double));\n    // CHECK: hiprandGenerator_t gen;\n    hiprandGenerator_t gen;\n    // Create generator\n    // CHECK: hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    hiprandCreateGenerator(&gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n    // Fill array with random numbers\n    // CHECK: hiprandGenerateNormalDouble(gen, d_gpu, 1000*4096, 0.0, 1.0);\n    hiprandGenerateNormalDouble(gen, d_gpu, 1000*4096, 0.0, 1.0);\n    // Destroy generator\n    // CHECK: hiprandDestroyGenerator(gen);\n    hiprandDestroyGenerator(gen);\n    // Sort data\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort<512, 8, hipcub::BLOCK_LOAD_TRANSPOSE, hipcub::BLOCK_STORE_TRANSPOSE>), dim3(1000), dim3(512), 0, 0, d_gpu, result_gpu);\n    sort<512, 8, hipcub::BLOCK_LOAD_TRANSPOSE, hipcub::BLOCK_STORE_TRANSPOSE><<<1000, 512>>>(d_gpu, result_gpu);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(sort<256, 16, hipcub::BLOCK_LOAD_DIRECT, hipcub::BLOCK_STORE_DIRECT>), dim3(1000), dim3(256), 0, 0, d_gpu, result_gpu);\n    sort<256, 16, hipcub::BLOCK_LOAD_DIRECT, hipcub::BLOCK_STORE_DIRECT><<<1000, 256>>>(d_gpu, result_gpu);\n    // CHECK: hipMemcpy(data_sorted, result_gpu, 1000*4096*sizeof(double), hipMemcpyDeviceToHost);\n    hipMemcpy(data_sorted, result_gpu, 1000*4096*sizeof(double), hipMemcpyDeviceToHost);\n    // Write the sorted data to standard out\n    for (int i = 0; i < 4095; ++i) {\n        std::cout << data_sorted[i] << \", \";\n    }\n    std::cout << data_sorted[4095] << std::endl;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/CUB/cub_02.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 4 --amap --hip-kernel-execution-syntax --skip-excluded-preprocessor-conditional-blocks --hipdnn %clang_args\n\n#include <iomanip>\n#include <iostream>\n#include <cstdlib>\n#include <vector>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n// CHECK: #include \"hipDNN.h\"\n#include \"cudnn.h\"\n\n// CHECK: hipError_t err = (f); \\\n// CHECK: if (err != hipSuccess) { \\\n\n#define CUDA_CALL(f) { \\\n  cudaError_t err = (f); \\\n  if (err != cudaSuccess) { \\\n    std::cout \\\n        << \"    Error occurred: \" << err << std::endl; \\\n    std::exit(1); \\\n  } \\\n}\n// CHECK: hipdnnStatus_t err = (f); \\\n// CHECK: if (err != HIPDNN_STATUS_SUCCESS) { \\\n\n#define CUDNN_CALL(f) { \\\n    cudnnStatus_t err = (f); \\\n    if (err != CUDNN_STATUS_SUCCESS) { \\\n    std::cout \\\n        << \"    Error occurred: \" << err << std::endl; \\\n    std::exit(1); \\\n  } \\\n}\n\n__global__ void dev_const(float *px, float k) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  px[tid] = k;\n}\n\n__global__ void dev_iota(float *px) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  px[tid] = tid;\n}\n\nvoid print(const float *data, int n, int c, int h, int w) {\n  std::vector<float> buffer(1 << 20);\n  // CHECK: CUDA_CALL(hipMemcpy(\n  CUDA_CALL(cudaMemcpy(\n        buffer.data(), data,\n        n * c * h * w * sizeof(float),\n        // CHECK: hipMemcpyDeviceToHost));\n        cudaMemcpyDeviceToHost));\n  int a = 0;\n  for (int i = 0; i < n; ++i) {\n    for (int j = 0; j < c; ++j) {\n      std::cout << \"n=\" << i << \", c=\" << j << \":\" << std::endl;\n      for (int k = 0; k < h; ++k) {\n        for (int l = 0; l < w; ++l) {\n          std::cout << std::setw(4) << std::right << buffer[a];\n          ++a;\n        }\n        std::cout << std::endl;\n      }\n    }\n  }\n  std::cout << std::endl;\n}\n\nint main() {\n  // CHECK: hipdnnHandle_t cudnn;\n  cudnnHandle_t cudnn;\n  // CHECK: CUDNN_CALL(hipdnnCreate(&cudnn));\n  CUDNN_CALL(cudnnCreate(&cudnn));\n\n  // input\n  const int in_n = 1;\n  const int in_c = 1;\n  const int in_h = 5;\n  const int in_w = 5;\n  std::cout << \"in_n: \" << in_n << std::endl;\n  std::cout << \"in_c: \" << in_c << std::endl;\n  std::cout << \"in_h: \" << in_h << std::endl;\n  std::cout << \"in_w: \" << in_w << std::endl;\n  std::cout << std::endl;\n  // CHECK: hipdnnTensorDescriptor_t in_desc;\n  cudnnTensorDescriptor_t in_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateTensorDescriptor(&in_desc));\n  CUDNN_CALL(cudnnCreateTensorDescriptor(&in_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n  CUDNN_CALL(cudnnSetTensor4dDescriptor(\n  // CHECK: in_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        in_desc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT,\n        in_n, in_c, in_h, in_w));\n\n  float *in_data;\n  // CHECK: CUDA_CALL(hipMalloc(\n  CUDA_CALL(cudaMalloc(\n        &in_data, in_n * in_c * in_h * in_w * sizeof(float)));\n\n  // filter\n  const int filt_k = 1;\n  const int filt_c = 1;\n  const int filt_h = 2;\n  const int filt_w = 2;\n  std::cout << \"filt_k: \" << filt_k << std::endl;\n  std::cout << \"filt_c: \" << filt_c << std::endl;\n  std::cout << \"filt_h: \" << filt_h << std::endl;\n  std::cout << \"filt_w: \" << filt_w << std::endl;\n  std::cout << std::endl;\n\n  // CHECK: hipdnnFilterDescriptor_t filt_desc;\n  cudnnFilterDescriptor_t filt_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateFilterDescriptor(&filt_desc));\n  CUDNN_CALL(cudnnCreateFilterDescriptor(&filt_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetFilter4dDescriptor(\n  CUDNN_CALL(cudnnSetFilter4dDescriptor(\n        // CHECK: filt_desc, HIPDNN_DATA_FLOAT, HIPDNN_TENSOR_NCHW,\n        filt_desc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW,\n        filt_k, filt_c, filt_h, filt_w));\n\n  float *filt_data;\n  //  CUDA_CALL(hipMalloc(\n  CUDA_CALL(cudaMalloc(\n      &filt_data, filt_k * filt_c * filt_h * filt_w * sizeof(float)));\n\n  // convolution\n  const int pad_h = 1;\n  const int pad_w = 1;\n  const int str_h = 1;\n  const int str_w = 1;\n  const int dil_h = 1;\n  const int dil_w = 1;\n  std::cout << \"pad_h: \" << pad_h << std::endl;\n  std::cout << \"pad_w: \" << pad_w << std::endl;\n  std::cout << \"str_h: \" << str_h << std::endl;\n  std::cout << \"str_w: \" << str_w << std::endl;\n  std::cout << \"dil_h: \" << dil_h << std::endl;\n  std::cout << \"dil_w: \" << dil_w << std::endl;\n  std::cout << std::endl;\n\n  // CHECK: hipdnnConvolutionDescriptor_t conv_desc;\n  cudnnConvolutionDescriptor_t conv_desc;\n  // CUDNN_CALL(hipdnnCreateConvolutionDescriptor(&conv_desc));\n  CUDNN_CALL(cudnnCreateConvolutionDescriptor(&conv_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetConvolution2dDescriptor(\n  CUDNN_CALL(cudnnSetConvolution2dDescriptor(\n        conv_desc,\n        pad_h, pad_w, str_h, str_w, dil_h, dil_w,\n        // CHECK: HIPDNN_CONVOLUTION, HIPDNN_DATA_FLOAT));\n        CUDNN_CONVOLUTION, CUDNN_DATA_FLOAT));\n\n  // output\n  int out_n;\n  int out_c;\n  int out_h;\n  int out_w;\n\n  // CHECK: CUDNN_CALL(hipdnnGetConvolution2dForwardOutputDim(\n  CUDNN_CALL(cudnnGetConvolution2dForwardOutputDim(\n        conv_desc, in_desc, filt_desc,\n        &out_n, &out_c, &out_h, &out_w));\n\n  std::cout << \"out_n: \" << out_n << std::endl;\n  std::cout << \"out_c: \" << out_c << std::endl;\n  std::cout << \"out_h: \" << out_h << std::endl;\n  std::cout << \"out_w: \" << out_w << std::endl;\n  std::cout << std::endl;\n  // CHECK: hipdnnTensorDescriptor_t out_desc;\n  cudnnTensorDescriptor_t out_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateTensorDescriptor(&out_desc));\n  CUDNN_CALL(cudnnCreateTensorDescriptor(&out_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n  CUDNN_CALL(cudnnSetTensor4dDescriptor(\n        // CHECK: out_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        out_desc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT,\n        out_n, out_c, out_h, out_w));\n\n\n  cudnnDataType_t *dataType = nullptr;\n  cudnnTensorFormat_t *tensorFormat = nullptr;\n  int *p_filt_k = nullptr;\n  int *p_filt_c = nullptr;\n  int *p_filt_h = nullptr;\n  int *p_filt_w = nullptr;\n\n  // CHECK: CUDNN_CALL(hipdnnGetFilter4dDescriptor(\n  CUDNN_CALL(cudnnGetFilter4dDescriptor(\n    filt_desc, dataType, tensorFormat,\n    p_filt_k, p_filt_c, p_filt_h, p_filt_w));\n\n  float *out_data;\n  // CHECK: CUDA_CALL(hipMalloc(\n  CUDA_CALL(cudaMalloc(\n        &out_data, out_n * out_c * out_h * out_w * sizeof(float)));\n\n  // algorithm\n  // CHECK: hipdnnConvolutionFwdAlgo_t algo;\n  cudnnConvolutionFwdAlgo_t algo;\n#if CUDNN_VERSION < 8001\n  // CHECK: CUDNN_CALL(hipdnnGetConvolutionForwardAlgorithm(\n  CUDNN_CALL(cudnnGetConvolutionForwardAlgorithm(\n        cudnn,\n        in_desc, filt_desc, conv_desc, out_desc,\n        // CHECK: HIPDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo));\n        CUDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo));\n#endif\n\n  std::cout << \"Convolution algorithm: \" << algo << std::endl;\n  std::cout << std::endl;\n\n  // workspace\n  size_t ws_size;\n  // CHECK: CUDNN_CALL(hipdnnGetConvolutionForwardWorkspaceSize(\n  CUDNN_CALL(cudnnGetConvolutionForwardWorkspaceSize(\n        cudnn, in_desc, filt_desc, conv_desc, out_desc, algo, &ws_size));\n\n  float *ws_data;\n  // CHECK: CUDA_CALL(hipMalloc(&ws_data, ws_size));\n  CUDA_CALL(cudaMalloc(&ws_data, ws_size));\n\n  std::cout << \"Workspace size: \" << ws_size << std::endl;\n  std::cout << std::endl;\n\n  // perform\n  float alpha = 1.f;\n  float beta = 0.f;\n  // CHECK: hipLaunchKernelGGL(dev_iota, dim3(in_w * in_h), dim3(in_n * in_c), 0, 0, in_data);\n  // CHECK: hipLaunchKernelGGL(dev_const, dim3(filt_w * filt_h), dim3(filt_k * filt_c), 0, 0, filt_data, 1.f);\n  dev_iota<<<in_w * in_h, in_n * in_c>>>(in_data);\n  dev_const<<<filt_w * filt_h, filt_k * filt_c>>>(filt_data, 1.f);\n  // CHECK: CUDNN_CALL(hipdnnConvolutionForward(\n  CUDNN_CALL(cudnnConvolutionForward(\n      cudnn,\n      &alpha, in_desc, in_data, filt_desc, filt_data,\n      conv_desc, algo, ws_data, ws_size,\n      &beta, out_desc, out_data));\n\n  // results\n  std::cout << \"in_data:\" << std::endl;\n  print(in_data, in_n, in_c, in_h, in_w);\n  \n  std::cout << \"filt_data:\" << std::endl;\n  print(filt_data, filt_k, filt_c, filt_h, filt_w);\n  \n  std::cout << \"out_data:\" << std::endl;\n  print(out_data, out_n, out_c, out_h, out_w);\n\n  // finalizing\n  // CHECK: CUDA_CALL(hipFree(ws_data));\n  CUDA_CALL(cudaFree(ws_data));\n  // CHECK: CUDA_CALL(hipFree(out_data));\n  CUDA_CALL(cudaFree(out_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyTensorDescriptor(out_desc));\n  CUDNN_CALL(cudnnDestroyTensorDescriptor(out_desc));\n  // CHECK: CUDNN_CALL(hipdnnDestroyConvolutionDescriptor(conv_desc));\n  CUDNN_CALL(cudnnDestroyConvolutionDescriptor(conv_desc));\n  // CHECK: CUDA_CALL(hipFree(filt_data));\n  CUDA_CALL(cudaFree(filt_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyFilterDescriptor(filt_desc));\n  CUDNN_CALL(cudnnDestroyFilterDescriptor(filt_desc));\n  // CHECK: CUDA_CALL(hipFree(in_data));\n  CUDA_CALL(cudaFree(in_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyTensorDescriptor(in_desc));\n  CUDNN_CALL(cudnnDestroyTensorDescriptor(in_desc));\n  // CHECK: CUDNN_CALL(hipdnnDestroy(cudnn));\n  CUDNN_CALL(cudnnDestroy(cudnn));\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 4 --amap --hip-kernel-execution-syntax --skip-excluded-preprocessor-conditional-blocks --hipdnn %clang_args\n\n#include <iomanip>\n#include <iostream>\n#include <cstdlib>\n#include <vector>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n// CHECK: #include \"hipDNN.h\"\n#include \"hipDNN.h\"\n\n// CHECK: hipError_t err = (f); \\\n// CHECK: if (err != hipSuccess) { \\\n\n#define CUDA_CALL(f) { \\\n  hipError_t err = (f); \\\n  if (err != hipSuccess) { \\\n    std::cout \\\n        << \"    Error occurred: \" << err << std::endl; \\\n    std::exit(1); \\\n  } \\\n}\n// CHECK: hipdnnStatus_t err = (f); \\\n// CHECK: if (err != HIPDNN_STATUS_SUCCESS) { \\\n\n#define CUDNN_CALL(f) { \\\n    hipdnnStatus_t err = (f); \\\n    if (err != HIPDNN_STATUS_SUCCESS) { \\\n    std::cout \\\n        << \"    Error occurred: \" << err << std::endl; \\\n    std::exit(1); \\\n  } \\\n}\n\n__global__ void dev_const(float *px, float k) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  px[tid] = k;\n}\n\n__global__ void dev_iota(float *px) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  px[tid] = tid;\n}\n\nvoid print(const float *data, int n, int c, int h, int w) {\n  std::vector<float> buffer(1 << 20);\n  // CHECK: CUDA_CALL(hipMemcpy(\n  CUDA_CALL(hipMemcpy(\n        buffer.data(), data,\n        n * c * h * w * sizeof(float),\n        // CHECK: hipMemcpyDeviceToHost));\n        hipMemcpyDeviceToHost));\n  int a = 0;\n  for (int i = 0; i < n; ++i) {\n    for (int j = 0; j < c; ++j) {\n      std::cout << \"n=\" << i << \", c=\" << j << \":\" << std::endl;\n      for (int k = 0; k < h; ++k) {\n        for (int l = 0; l < w; ++l) {\n          std::cout << std::setw(4) << std::right << buffer[a];\n          ++a;\n        }\n        std::cout << std::endl;\n      }\n    }\n  }\n  std::cout << std::endl;\n}\n\nint main() {\n  // CHECK: hipdnnHandle_t cudnn;\n  hipdnnHandle_t cudnn;\n  // CHECK: CUDNN_CALL(hipdnnCreate(&cudnn));\n  CUDNN_CALL(hipdnnCreate(&cudnn));\n\n  // input\n  const int in_n = 1;\n  const int in_c = 1;\n  const int in_h = 5;\n  const int in_w = 5;\n  std::cout << \"in_n: \" << in_n << std::endl;\n  std::cout << \"in_c: \" << in_c << std::endl;\n  std::cout << \"in_h: \" << in_h << std::endl;\n  std::cout << \"in_w: \" << in_w << std::endl;\n  std::cout << std::endl;\n  // CHECK: hipdnnTensorDescriptor_t in_desc;\n  hipdnnTensorDescriptor_t in_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateTensorDescriptor(&in_desc));\n  CUDNN_CALL(hipdnnCreateTensorDescriptor(&in_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n  CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n  // CHECK: in_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        in_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        in_n, in_c, in_h, in_w));\n\n  float *in_data;\n  // CHECK: CUDA_CALL(hipMalloc(\n  CUDA_CALL(hipMalloc(\n        &in_data, in_n * in_c * in_h * in_w * sizeof(float)));\n\n  // filter\n  const int filt_k = 1;\n  const int filt_c = 1;\n  const int filt_h = 2;\n  const int filt_w = 2;\n  std::cout << \"filt_k: \" << filt_k << std::endl;\n  std::cout << \"filt_c: \" << filt_c << std::endl;\n  std::cout << \"filt_h: \" << filt_h << std::endl;\n  std::cout << \"filt_w: \" << filt_w << std::endl;\n  std::cout << std::endl;\n\n  // CHECK: hipdnnFilterDescriptor_t filt_desc;\n  hipdnnFilterDescriptor_t filt_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateFilterDescriptor(&filt_desc));\n  CUDNN_CALL(hipdnnCreateFilterDescriptor(&filt_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetFilter4dDescriptor(\n  CUDNN_CALL(hipdnnSetFilter4dDescriptor(\n        // CHECK: filt_desc, HIPDNN_DATA_FLOAT, HIPDNN_TENSOR_NCHW,\n        filt_desc, HIPDNN_DATA_FLOAT, HIPDNN_TENSOR_NCHW,\n        filt_k, filt_c, filt_h, filt_w));\n\n  float *filt_data;\n  //  CUDA_CALL(hipMalloc(\n  CUDA_CALL(hipMalloc(\n      &filt_data, filt_k * filt_c * filt_h * filt_w * sizeof(float)));\n\n  // convolution\n  const int pad_h = 1;\n  const int pad_w = 1;\n  const int str_h = 1;\n  const int str_w = 1;\n  const int dil_h = 1;\n  const int dil_w = 1;\n  std::cout << \"pad_h: \" << pad_h << std::endl;\n  std::cout << \"pad_w: \" << pad_w << std::endl;\n  std::cout << \"str_h: \" << str_h << std::endl;\n  std::cout << \"str_w: \" << str_w << std::endl;\n  std::cout << \"dil_h: \" << dil_h << std::endl;\n  std::cout << \"dil_w: \" << dil_w << std::endl;\n  std::cout << std::endl;\n\n  // CHECK: hipdnnConvolutionDescriptor_t conv_desc;\n  hipdnnConvolutionDescriptor_t conv_desc;\n  // CUDNN_CALL(hipdnnCreateConvolutionDescriptor(&conv_desc));\n  CUDNN_CALL(hipdnnCreateConvolutionDescriptor(&conv_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetConvolution2dDescriptor(\n  CUDNN_CALL(hipdnnSetConvolution2dDescriptor(\n        conv_desc,\n        pad_h, pad_w, str_h, str_w, dil_h, dil_w,\n        // CHECK: HIPDNN_CONVOLUTION, HIPDNN_DATA_FLOAT));\n        HIPDNN_CONVOLUTION, HIPDNN_DATA_FLOAT));\n\n  // output\n  int out_n;\n  int out_c;\n  int out_h;\n  int out_w;\n\n  // CHECK: CUDNN_CALL(hipdnnGetConvolution2dForwardOutputDim(\n  CUDNN_CALL(hipdnnGetConvolution2dForwardOutputDim(\n        conv_desc, in_desc, filt_desc,\n        &out_n, &out_c, &out_h, &out_w));\n\n  std::cout << \"out_n: \" << out_n << std::endl;\n  std::cout << \"out_c: \" << out_c << std::endl;\n  std::cout << \"out_h: \" << out_h << std::endl;\n  std::cout << \"out_w: \" << out_w << std::endl;\n  std::cout << std::endl;\n  // CHECK: hipdnnTensorDescriptor_t out_desc;\n  hipdnnTensorDescriptor_t out_desc;\n  // CHECK: CUDNN_CALL(hipdnnCreateTensorDescriptor(&out_desc));\n  CUDNN_CALL(hipdnnCreateTensorDescriptor(&out_desc));\n  // CHECK: CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n  CUDNN_CALL(hipdnnSetTensor4dDescriptor(\n        // CHECK: out_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        out_desc, HIPDNN_TENSOR_NCHW, HIPDNN_DATA_FLOAT,\n        out_n, out_c, out_h, out_w));\n\n\n  hipdnnDataType_t *dataType = nullptr;\n  hipdnnTensorFormat_t *tensorFormat = nullptr;\n  int *p_filt_k = nullptr;\n  int *p_filt_c = nullptr;\n  int *p_filt_h = nullptr;\n  int *p_filt_w = nullptr;\n\n  // CHECK: CUDNN_CALL(hipdnnGetFilter4dDescriptor(\n  CUDNN_CALL(hipdnnGetFilter4dDescriptor(\n    filt_desc, dataType, tensorFormat,\n    p_filt_k, p_filt_c, p_filt_h, p_filt_w));\n\n  float *out_data;\n  // CHECK: CUDA_CALL(hipMalloc(\n  CUDA_CALL(hipMalloc(\n        &out_data, out_n * out_c * out_h * out_w * sizeof(float)));\n\n  // algorithm\n  // CHECK: hipdnnConvolutionFwdAlgo_t algo;\n  hipdnnConvolutionFwdAlgo_t algo;\n#if CUDNN_VERSION < 8001\n  // CHECK: CUDNN_CALL(hipdnnGetConvolutionForwardAlgorithm(\n  CUDNN_CALL(hipdnnGetConvolutionForwardAlgorithm(\n        cudnn,\n        in_desc, filt_desc, conv_desc, out_desc,\n        // CHECK: HIPDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo));\n        HIPDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo));\n#endif\n\n  std::cout << \"Convolution algorithm: \" << algo << std::endl;\n  std::cout << std::endl;\n\n  // workspace\n  size_t ws_size;\n  // CHECK: CUDNN_CALL(hipdnnGetConvolutionForwardWorkspaceSize(\n  CUDNN_CALL(hipdnnGetConvolutionForwardWorkspaceSize(\n        cudnn, in_desc, filt_desc, conv_desc, out_desc, algo, &ws_size));\n\n  float *ws_data;\n  // CHECK: CUDA_CALL(hipMalloc(&ws_data, ws_size));\n  CUDA_CALL(hipMalloc(&ws_data, ws_size));\n\n  std::cout << \"Workspace size: \" << ws_size << std::endl;\n  std::cout << std::endl;\n\n  // perform\n  float alpha = 1.f;\n  float beta = 0.f;\n  // CHECK: hipLaunchKernelGGL(dev_iota, dim3(in_w * in_h), dim3(in_n * in_c), 0, 0, in_data);\n  // CHECK: hipLaunchKernelGGL(dev_const, dim3(filt_w * filt_h), dim3(filt_k * filt_c), 0, 0, filt_data, 1.f);\n  dev_iota<<<in_w * in_h, in_n * in_c>>>(in_data);\n  dev_const<<<filt_w * filt_h, filt_k * filt_c>>>(filt_data, 1.f);\n  // CHECK: CUDNN_CALL(hipdnnConvolutionForward(\n  CUDNN_CALL(hipdnnConvolutionForward(\n      cudnn,\n      &alpha, in_desc, in_data, filt_desc, filt_data,\n      conv_desc, algo, ws_data, ws_size,\n      &beta, out_desc, out_data));\n\n  // results\n  std::cout << \"in_data:\" << std::endl;\n  print(in_data, in_n, in_c, in_h, in_w);\n  \n  std::cout << \"filt_data:\" << std::endl;\n  print(filt_data, filt_k, filt_c, filt_h, filt_w);\n  \n  std::cout << \"out_data:\" << std::endl;\n  print(out_data, out_n, out_c, out_h, out_w);\n\n  // finalizing\n  // CHECK: CUDA_CALL(hipFree(ws_data));\n  CUDA_CALL(hipFree(ws_data));\n  // CHECK: CUDA_CALL(hipFree(out_data));\n  CUDA_CALL(hipFree(out_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyTensorDescriptor(out_desc));\n  CUDNN_CALL(hipdnnDestroyTensorDescriptor(out_desc));\n  // CHECK: CUDNN_CALL(hipdnnDestroyConvolutionDescriptor(conv_desc));\n  CUDNN_CALL(hipdnnDestroyConvolutionDescriptor(conv_desc));\n  // CHECK: CUDA_CALL(hipFree(filt_data));\n  CUDA_CALL(hipFree(filt_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyFilterDescriptor(filt_desc));\n  CUDNN_CALL(hipdnnDestroyFilterDescriptor(filt_desc));\n  // CHECK: CUDA_CALL(hipFree(in_data));\n  CUDA_CALL(hipFree(in_data));\n  // CHECK: CUDNN_CALL(hipdnnDestroyTensorDescriptor(in_desc));\n  CUDNN_CALL(hipdnnDestroyTensorDescriptor(in_desc));\n  // CHECK: CUDNN_CALL(hipdnnDestroy(cudnn));\n  CUDNN_CALL(hipdnnDestroy(cudnn));\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/libraries/cuDNN/cudnn_convolution_forward.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n#include <stdint.h>\n\n// Random predefiend 32 and 64 bit values\nconstexpr uint32_t value32 = 0x70F0F0FF;\nconstexpr uint64_t value64 = 0x7FFF0000FFFF0000;\nconstexpr unsigned int writeFlag = 0;\n\n__global__\nvoid fn(float* px, float* py) {\n  bool a[42];\n  __shared__ double b[69];\n  for (auto&& x : b) x = *py++;\n  for (auto&& x : a) x = *px++ > 0.0;\n  for (auto&& x : a) if (x) *--py = *--px;\n}\n\nvoid testWrite() {\n  int64_t* signalPtr;\n  // CHECK: hipStream_t stream;\n  cudaStream_t stream;\n  // CHECK: hipStreamCreate(&stream);\n  cudaStreamCreate(&stream);\n  int64_t* host_ptr64 = (int64_t*)malloc(sizeof(int64_t));\n  int32_t* host_ptr32 = (int32_t*)malloc(sizeof(int32_t));\n  //  hipExtMallocWithFlags((void**)&signalPtr, 8, hipMallocSignalMemory);\n  void* device_ptr64;\n  void* device_ptr32;\n  *host_ptr64 = 0x0;\n  *host_ptr32 = 0x0;\n  *signalPtr = 0x0;\n  // CHECK: hipHostRegister(host_ptr64, sizeof(int64_t), 0);\n  cudaHostRegister(host_ptr64, sizeof(int64_t), 0);\n  // CHECK: hipHostRegister(host_ptr32, sizeof(int32_t), 0);\n  cudaHostRegister(host_ptr32, sizeof(int32_t), 0);\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(host_ptr64), value64, writeFlag);\n  cuStreamWriteValue64(stream, CUdeviceptr(host_ptr64), value64, writeFlag);\n  // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(host_ptr32), value32, writeFlag);\n  cuStreamWriteValue32(stream, CUdeviceptr(host_ptr32), value32, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  cudaStreamSynchronize(stream);\n  // CHECK: hipHostGetDevicePointer((void**)&device_ptr64, host_ptr64, 0);\n  cudaHostGetDevicePointer((void**)&device_ptr64, host_ptr64, 0);\n  // CHECK: hipHostGetDevicePointer((void**)&device_ptr32, host_ptr32, 0);\n  cudaHostGetDevicePointer((void**)&device_ptr32, host_ptr32, 0);\n  // Reset values\n  *host_ptr64 = 0x0;\n  *host_ptr32 = 0x0;\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(device_ptr64), value64, writeFlag);\n  cuStreamWriteValue64(stream, CUdeviceptr(device_ptr64), value64, writeFlag);\n  // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(device_ptr32), value32, writeFlag);\n  cuStreamWriteValue32(stream, CUdeviceptr(device_ptr32), value32, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  cudaStreamSynchronize(stream);\n  // Test Writing to Signal Memory\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(signalPtr), value64, writeFlag);\n  cuStreamWriteValue64(stream, CUdeviceptr(signalPtr), value64, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  cudaStreamSynchronize(stream);\n  // Cleanup\n  // CHECK: hipStreamDestroy(stream);\n  cudaStreamDestroy(stream);\n  // CHECK: hipHostUnregister(host_ptr64);\n  cudaHostUnregister(host_ptr64);\n  // CHECK: hipHostUnregister(host_ptr32);\n  cudaHostUnregister(host_ptr32);\n  // CHECK: hipFree(signalPtr);\n  cudaFree(signalPtr);\n  free(host_ptr32);\n  free(host_ptr64);\n}\n\nvoid testWait() {\n  int64_t* signalPtr;\n  // random data values\n  int32_t DATA_INIT = 0x1234;\n  int32_t DATA_UPDATE = 0X4321;\n\n  struct TEST_WAIT {\n    int compareOp;\n    uint64_t mask;\n    int64_t waitValue;\n    int64_t signalValueFail;\n    int64_t signalValuePass;\n  };\n\n  TEST_WAIT testCases[] = {\n    {\n      // mask will ignore few MSB bits\n      // CHECK: hipStreamWaitValueGte,\n      CU_STREAM_WAIT_VALUE_GEQ,\n      0x0000FFFFFFFFFFFF,\n      0x000000007FFF0001,\n      0x7FFF00007FFF0000,\n      0x000000007FFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueGte,\n      CU_STREAM_WAIT_VALUE_GEQ,\n      0xF,\n      0x4,\n      0x3,\n      0x6\n    },\n    {\n      // mask will ignore few MSB bits\n      // CHECK: hipStreamWaitValueEq,\n      CU_STREAM_WAIT_VALUE_EQ,\n      0x0000FFFFFFFFFFFF,\n      0x000000000FFF0001,\n      0x7FFF00000FFF0000,\n      0x7F0000000FFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      CU_STREAM_WAIT_VALUE_EQ,\n      0xFF,\n      0x11,\n      0x25,\n      0x11\n    },\n    {\n      // mask will discard bits 8 to 11\n      // CHECK: hipStreamWaitValueAnd,\n      CU_STREAM_WAIT_VALUE_AND,\n      0xFF,\n      0xF4A,\n      0xF35,\n      0X02\n    },\n    {\n      // mask is set to ignore the sign bit.\n      // CHECK: hipStreamWaitValueNor,\n      CU_STREAM_WAIT_VALUE_NOR,\n      0x7FFFFFFFFFFFFFFF,\n      0x7FFFFFFFFFFFF247,\n      0x7FFFFFFFFFFFFdbd,\n      0x7FFFFFFFFFFFFdb5\n    },\n    {\n      // mask is set to apply NOR for bits 0 to 3.\n      // CHECK: hipStreamWaitValueNor,\n      CU_STREAM_WAIT_VALUE_NOR,\n      0xF,\n      0x7E,\n      0x7D,\n      0x76\n    }\n  };\n\n  struct TEST_WAIT32_NO_MASK {\n    int compareOp;\n    int32_t waitValue;\n    int32_t signalValueFail;\n    int32_t signalValuePass;\n  };\n\n  // default mask 0xFFFFFFFF will be used.\n  TEST_WAIT32_NO_MASK testCasesNoMask32[] = {\n    {\n      // CHECK: hipStreamWaitValueGte,\n      CU_STREAM_WAIT_VALUE_GEQ,\n      0x7FFF0001,\n      0x7FFF0000,\n      0x7FFF0010\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      CU_STREAM_WAIT_VALUE_EQ,\n      0x7FFFFFFF,\n      0x7FFF0000,\n      0x7FFFFFFF\n    },\n    {\n      // CHECK: hipStreamWaitValueAnd,\n      CU_STREAM_WAIT_VALUE_AND,\n      0x70F0F0F0,\n      0x0F0F0F0F,\n      0X1F0F0F0F\n    },\n    {\n      // CHECK: hipStreamWaitValueNor,\n      CU_STREAM_WAIT_VALUE_NOR,\n      0x7AAAAAAA,\n      static_cast<int32_t>(0x85555555),\n      static_cast<int32_t>(0x9AAAAAAA)\n    }\n  };\n\n  struct TEST_WAIT64_NO_MASK {\n    int compareOp;\n    int64_t waitValue;\n    int64_t signalValueFail;\n    int64_t signalValuePass;\n  };\n\n  // default mask 0xFFFFFFFFFFFFFFFF will be used.\n  TEST_WAIT64_NO_MASK testCasesNoMask64[] = {\n    {\n      // CHECK: hipStreamWaitValueGte,\n      CU_STREAM_WAIT_VALUE_GEQ,\n      0x7FFFFFFFFFFF0001,\n      0x7FFFFFFFFFFF0000,\n      0x7FFFFFFFFFFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      CU_STREAM_WAIT_VALUE_EQ,\n      0x7FFFFFFFFFFFFFFF,\n      0x7FFFFFFF0FFF0000,\n      0x7FFFFFFFFFFFFFFF\n    },\n    {\n      // CHECK: hipStreamWaitValueAnd,\n      CU_STREAM_WAIT_VALUE_AND,\n      0x70F0F0F0F0F0F0F0,\n      0x0F0F0F0F0F0F0F0F,\n      0X1F0F0F0F0F0F0F0F\n    },\n    {\n      // CHECK: hipStreamWaitValueNor,\n      CU_STREAM_WAIT_VALUE_NOR,\n      0x4724724747247247,\n      static_cast<int64_t>(0xbddbddbdbddbddbd),\n      static_cast<int64_t>(0xbddbddbdbddbddb3)\n    }\n  };\n\n  // CHECK: hipStream_t stream;\n  cudaStream_t stream;\n  // CHECK: hipStreamCreate(&stream);\n  cudaStreamCreate(&stream);\n  // hipExtMallocWithFlags((void**)&signalPtr, 8, hipMallocSignalMemory);\n  int64_t* dataPtr64 = (int64_t*)malloc(sizeof(int64_t));\n  int32_t* dataPtr32 = (int32_t*)malloc(sizeof(int32_t));\n  // hipHostRegister(dataPtr64, sizeof(int64_t), 0);\n  cudaHostRegister(dataPtr64, sizeof(int64_t), 0);\n  // CHECK: hipHostRegister(dataPtr32, sizeof(int32_t), 0);\n  cudaHostRegister(dataPtr32, sizeof(int32_t), 0);\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto & tc : testCases) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr64 = DATA_INIT;\n      // CHECK: hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      cuStreamWaitValue64(stream, CUdeviceptr(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      cuStreamWriteValue64(stream, CUdeviceptr(dataPtr64), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (cudaStreamQuery(stream) != cudaErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: if (hipStreamQuery(stream) != hipSuccess) {}\n      if (cudaStreamQuery(stream) != cudaSuccess) {}\n      // CHECK: hipStreamSynchronize(stream);\n      cudaStreamSynchronize(stream);\n      if (*dataPtr64 != DATA_UPDATE) {}\n      // 32-bit API\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr32 = DATA_INIT;\n      // CHECK: hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      cuStreamWaitValue32(stream, CUdeviceptr(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      cuStreamWriteValue32(stream, CUdeviceptr(dataPtr32), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (cudaStreamQuery(stream) != cudaErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = static_cast<int32_t>(tc.signalValuePass);\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      cudaStreamSynchronize(stream);\n      if (*dataPtr32 != DATA_UPDATE) {}\n    }\n  }\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto& tc : testCasesNoMask32) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr32 = DATA_INIT;\n      // CHECK: hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      cuStreamWaitValue32(stream, CUdeviceptr(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      cuStreamWriteValue32(stream, CUdeviceptr(dataPtr32), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (cudaStreamQuery(stream) != cudaErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      cudaStreamSynchronize(stream);\n      if (*dataPtr32 != DATA_UPDATE) {}\n    }\n  }\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto& tc : testCasesNoMask64) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr64 = DATA_INIT;\n      // CHECK: hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      cuStreamWaitValue64(stream, CUdeviceptr(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      cuStreamWriteValue64(stream, CUdeviceptr(dataPtr64), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (cudaStreamQuery(stream) != cudaErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      cudaStreamSynchronize(stream);\n      if (*dataPtr64 != DATA_UPDATE) {}\n    }\n  }\n  // Cleanup\n  // CHECK: hipFree(signalPtr);\n  cudaFree(signalPtr);\n  // CHECK: hipHostUnregister(dataPtr64);\n  cudaHostUnregister(dataPtr64);\n  // CHECK: hipHostUnregister(dataPtr32);\n  cudaHostUnregister(dataPtr32);\n  free(dataPtr64);\n  free(dataPtr32);\n  // CHECK: hipStreamDestroy(stream);\n  cudaStreamDestroy(stream);\n}\n\nint main() {\n  // CHECK: hipFuncCache_t cacheConfig;\n  cudaFuncCache cacheConfig;\n  void* func;\n  // CHECK: hipFuncSetCacheConfig(reinterpret_cast<const void*>(func), cacheConfig);\n  cudaFuncSetCacheConfig(func, cacheConfig);\n  // CHECK: hipFuncAttributes attr{};\n  cudaFuncAttributes attr{};\n  // CHECK: auto r = hipFuncGetAttributes(&attr, reinterpret_cast<const void*>(&fn));\n  auto r = cudaFuncGetAttributes(&attr, &fn);\n  // CHECK: if (r != hipSuccess || attr.maxThreadsPerBlock == 0) {\n  if (r != cudaSuccess || attr.maxThreadsPerBlock == 0) {\n    return 1;\n  }\n  testWrite();\n  testWait();\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n#include <stdint.h>\n\n// Random predefiend 32 and 64 bit values\nconstexpr uint32_t value32 = 0x70F0F0FF;\nconstexpr uint64_t value64 = 0x7FFF0000FFFF0000;\nconstexpr unsigned int writeFlag = 0;\n\n__global__\nvoid fn(float* px, float* py) {\n  bool a[42];\n  __shared__ double b[69];\n  for (auto&& x : b) x = *py++;\n  for (auto&& x : a) x = *px++ > 0.0;\n  for (auto&& x : a) if (x) *--py = *--px;\n}\n\nvoid testWrite() {\n  int64_t* signalPtr;\n  // CHECK: hipStream_t stream;\n  hipStream_t stream;\n  // CHECK: hipStreamCreate(&stream);\n  hipStreamCreate(&stream);\n  int64_t* host_ptr64 = (int64_t*)malloc(sizeof(int64_t));\n  int32_t* host_ptr32 = (int32_t*)malloc(sizeof(int32_t));\n  //  hipExtMallocWithFlags((void**)&signalPtr, 8, hipMallocSignalMemory);\n  void* device_ptr64;\n  void* device_ptr32;\n  *host_ptr64 = 0x0;\n  *host_ptr32 = 0x0;\n  *signalPtr = 0x0;\n  // CHECK: hipHostRegister(host_ptr64, sizeof(int64_t), 0);\n  hipHostRegister(host_ptr64, sizeof(int64_t), 0);\n  // CHECK: hipHostRegister(host_ptr32, sizeof(int32_t), 0);\n  hipHostRegister(host_ptr32, sizeof(int32_t), 0);\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(host_ptr64), value64, writeFlag);\n  hipStreamWriteValue64(stream, hipDeviceptr_t(host_ptr64), value64, writeFlag);\n  // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(host_ptr32), value32, writeFlag);\n  hipStreamWriteValue32(stream, hipDeviceptr_t(host_ptr32), value32, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  hipStreamSynchronize(stream);\n  // CHECK: hipHostGetDevicePointer((void**)&device_ptr64, host_ptr64, 0);\n  hipHostGetDevicePointer((void**)&device_ptr64, host_ptr64, 0);\n  // CHECK: hipHostGetDevicePointer((void**)&device_ptr32, host_ptr32, 0);\n  hipHostGetDevicePointer((void**)&device_ptr32, host_ptr32, 0);\n  // Reset values\n  *host_ptr64 = 0x0;\n  *host_ptr32 = 0x0;\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(device_ptr64), value64, writeFlag);\n  hipStreamWriteValue64(stream, hipDeviceptr_t(device_ptr64), value64, writeFlag);\n  // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(device_ptr32), value32, writeFlag);\n  hipStreamWriteValue32(stream, hipDeviceptr_t(device_ptr32), value32, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  hipStreamSynchronize(stream);\n  // Test Writing to Signal Memory\n  // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(signalPtr), value64, writeFlag);\n  hipStreamWriteValue64(stream, hipDeviceptr_t(signalPtr), value64, writeFlag);\n  // CHECK: hipStreamSynchronize(stream);\n  hipStreamSynchronize(stream);\n  // Cleanup\n  // CHECK: hipStreamDestroy(stream);\n  hipStreamDestroy(stream);\n  // CHECK: hipHostUnregister(host_ptr64);\n  hipHostUnregister(host_ptr64);\n  // CHECK: hipHostUnregister(host_ptr32);\n  hipHostUnregister(host_ptr32);\n  // CHECK: hipFree(signalPtr);\n  hipFree(signalPtr);\n  free(host_ptr32);\n  free(host_ptr64);\n}\n\nvoid testWait() {\n  int64_t* signalPtr;\n  // random data values\n  int32_t DATA_INIT = 0x1234;\n  int32_t DATA_UPDATE = 0X4321;\n\n  struct TEST_WAIT {\n    int compareOp;\n    uint64_t mask;\n    int64_t waitValue;\n    int64_t signalValueFail;\n    int64_t signalValuePass;\n  };\n\n  TEST_WAIT testCases[] = {\n    {\n      // mask will ignore few MSB bits\n      // CHECK: hipStreamWaitValueGte,\n      hipStreamWaitValueGte,\n      0x0000FFFFFFFFFFFF,\n      0x000000007FFF0001,\n      0x7FFF00007FFF0000,\n      0x000000007FFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueGte,\n      hipStreamWaitValueGte,\n      0xF,\n      0x4,\n      0x3,\n      0x6\n    },\n    {\n      // mask will ignore few MSB bits\n      // CHECK: hipStreamWaitValueEq,\n      hipStreamWaitValueEq,\n      0x0000FFFFFFFFFFFF,\n      0x000000000FFF0001,\n      0x7FFF00000FFF0000,\n      0x7F0000000FFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      hipStreamWaitValueEq,\n      0xFF,\n      0x11,\n      0x25,\n      0x11\n    },\n    {\n      // mask will discard bits 8 to 11\n      // CHECK: hipStreamWaitValueAnd,\n      hipStreamWaitValueAnd,\n      0xFF,\n      0xF4A,\n      0xF35,\n      0X02\n    },\n    {\n      // mask is set to ignore the sign bit.\n      // CHECK: hipStreamWaitValueNor,\n      hipStreamWaitValueNor,\n      0x7FFFFFFFFFFFFFFF,\n      0x7FFFFFFFFFFFF247,\n      0x7FFFFFFFFFFFFdbd,\n      0x7FFFFFFFFFFFFdb5\n    },\n    {\n      // mask is set to apply NOR for bits 0 to 3.\n      // CHECK: hipStreamWaitValueNor,\n      hipStreamWaitValueNor,\n      0xF,\n      0x7E,\n      0x7D,\n      0x76\n    }\n  };\n\n  struct TEST_WAIT32_NO_MASK {\n    int compareOp;\n    int32_t waitValue;\n    int32_t signalValueFail;\n    int32_t signalValuePass;\n  };\n\n  // default mask 0xFFFFFFFF will be used.\n  TEST_WAIT32_NO_MASK testCasesNoMask32[] = {\n    {\n      // CHECK: hipStreamWaitValueGte,\n      hipStreamWaitValueGte,\n      0x7FFF0001,\n      0x7FFF0000,\n      0x7FFF0010\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      hipStreamWaitValueEq,\n      0x7FFFFFFF,\n      0x7FFF0000,\n      0x7FFFFFFF\n    },\n    {\n      // CHECK: hipStreamWaitValueAnd,\n      hipStreamWaitValueAnd,\n      0x70F0F0F0,\n      0x0F0F0F0F,\n      0X1F0F0F0F\n    },\n    {\n      // CHECK: hipStreamWaitValueNor,\n      hipStreamWaitValueNor,\n      0x7AAAAAAA,\n      static_cast<int32_t>(0x85555555),\n      static_cast<int32_t>(0x9AAAAAAA)\n    }\n  };\n\n  struct TEST_WAIT64_NO_MASK {\n    int compareOp;\n    int64_t waitValue;\n    int64_t signalValueFail;\n    int64_t signalValuePass;\n  };\n\n  // default mask 0xFFFFFFFFFFFFFFFF will be used.\n  TEST_WAIT64_NO_MASK testCasesNoMask64[] = {\n    {\n      // CHECK: hipStreamWaitValueGte,\n      hipStreamWaitValueGte,\n      0x7FFFFFFFFFFF0001,\n      0x7FFFFFFFFFFF0000,\n      0x7FFFFFFFFFFF0001\n    },\n    {\n      // CHECK: hipStreamWaitValueEq,\n      hipStreamWaitValueEq,\n      0x7FFFFFFFFFFFFFFF,\n      0x7FFFFFFF0FFF0000,\n      0x7FFFFFFFFFFFFFFF\n    },\n    {\n      // CHECK: hipStreamWaitValueAnd,\n      hipStreamWaitValueAnd,\n      0x70F0F0F0F0F0F0F0,\n      0x0F0F0F0F0F0F0F0F,\n      0X1F0F0F0F0F0F0F0F\n    },\n    {\n      // CHECK: hipStreamWaitValueNor,\n      hipStreamWaitValueNor,\n      0x4724724747247247,\n      static_cast<int64_t>(0xbddbddbdbddbddbd),\n      static_cast<int64_t>(0xbddbddbdbddbddb3)\n    }\n  };\n\n  // CHECK: hipStream_t stream;\n  hipStream_t stream;\n  // CHECK: hipStreamCreate(&stream);\n  hipStreamCreate(&stream);\n  // hipExtMallocWithFlags((void**)&signalPtr, 8, hipMallocSignalMemory);\n  int64_t* dataPtr64 = (int64_t*)malloc(sizeof(int64_t));\n  int32_t* dataPtr32 = (int32_t*)malloc(sizeof(int32_t));\n  // hipHostRegister(dataPtr64, sizeof(int64_t), 0);\n  hipHostRegister(dataPtr64, sizeof(int64_t), 0);\n  // CHECK: hipHostRegister(dataPtr32, sizeof(int32_t), 0);\n  hipHostRegister(dataPtr32, sizeof(int32_t), 0);\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto & tc : testCases) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr64 = DATA_INIT;\n      // CHECK: hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: if (hipStreamQuery(stream) != hipSuccess) {}\n      if (hipStreamQuery(stream) != hipSuccess) {}\n      // CHECK: hipStreamSynchronize(stream);\n      hipStreamSynchronize(stream);\n      if (*dataPtr64 != DATA_UPDATE) {}\n      // 32-bit API\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr32 = DATA_INIT;\n      // CHECK: hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = static_cast<int32_t>(tc.signalValuePass);\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      hipStreamSynchronize(stream);\n      if (*dataPtr32 != DATA_UPDATE) {}\n    }\n  }\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto& tc : testCasesNoMask32) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr32 = DATA_INIT;\n      // CHECK: hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      hipStreamWaitValue32(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      hipStreamWriteValue32(stream, hipDeviceptr_t(dataPtr32), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      hipStreamSynchronize(stream);\n      if (*dataPtr32 != DATA_UPDATE) {}\n    }\n  }\n  // Run-1: streamWait is blocking (wait conditions is false)\n  // Run-2: streamWait is non-blocking (wait condition is true)\n  for (int run = 0; run < 2; run++) {\n    bool isBlocking = run == 0;\n    for (const auto& tc : testCasesNoMask64) {\n      *signalPtr = isBlocking ? tc.signalValueFail : tc.signalValuePass;\n      *dataPtr64 = DATA_INIT;\n      // CHECK: hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      hipStreamWaitValue64(stream, hipDeviceptr_t(signalPtr), tc.waitValue, tc.compareOp);\n      // CHECK: hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      hipStreamWriteValue64(stream, hipDeviceptr_t(dataPtr64), DATA_UPDATE, writeFlag);\n      if (isBlocking) {\n        // Trigger an implict flush and verify stream has pending work.\n        // CHECK: if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        if (hipStreamQuery(stream) != hipErrorNotReady) {}\n        // update signal to unblock the wait.\n        *signalPtr = tc.signalValuePass;\n      }\n      // CHECK: hipStreamSynchronize(stream);\n      hipStreamSynchronize(stream);\n      if (*dataPtr64 != DATA_UPDATE) {}\n    }\n  }\n  // Cleanup\n  // CHECK: hipFree(signalPtr);\n  hipFree(signalPtr);\n  // CHECK: hipHostUnregister(dataPtr64);\n  hipHostUnregister(dataPtr64);\n  // CHECK: hipHostUnregister(dataPtr32);\n  hipHostUnregister(dataPtr32);\n  free(dataPtr64);\n  free(dataPtr32);\n  // CHECK: hipStreamDestroy(stream);\n  hipStreamDestroy(stream);\n}\n\nint main() {\n  // CHECK: hipFuncCache_t cacheConfig;\n  hipFuncCache_t cacheConfig;\n  void* func;\n  // CHECK: hipFuncSetCacheConfig(reinterpret_cast<const void*>(func), cacheConfig);\n  hipFuncSetCacheConfig(reinterpret_cast<const void*>(func), cacheConfig);\n  // CHECK: hipFuncAttributes attr{};\n  hipFuncAttributes attr{};\n  // CHECK: auto r = hipFuncGetAttributes(&attr, reinterpret_cast<const void*>(&fn));\n  auto r = hipFuncGetAttributes(&attr,reinterpret_cast<const void*>(&fn));\n  // CHECK: if (r != hipSuccess || attr.maxThreadsPerBlock == 0) {\n  if (r != hipSuccess || attr.maxThreadsPerBlock == 0) {\n    return 1;\n  }\n  testWrite();\n  testWait();\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/casts/reinterpret_cast.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n__global__ void test_0() {\n  int a = 10;\n}\n\nnamespace first {\n  __global__ void test_1() {\n    int b = 20;\n  }\n  namespace second {\n    __global__ void test_2() {\n      int c = 30;\n    }\n  }\n}\n\nint main() {\n  // CHECK: hipLaunchKernelGGL(::test_0, dim3(1), dim3(1), 0, 0);\n  ::test_0<<<1, 1>>>();\n  // CHECK: hipLaunchKernelGGL(first::test_1, dim3(1), dim3(1), 0, 0);\n  first::test_1<<<1, 1>>>();\n  // CHECK: hipLaunchKernelGGL(first::second::test_2, dim3(1), dim3(1), 0, 0);\n  first::second::test_2<<<1, 1>>>();\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n__global__ void test_0() {\n  int a = 10;\n}\n\nnamespace first {\n  __global__ void test_1() {\n    int b = 20;\n  }\n  namespace second {\n    __global__ void test_2() {\n      int c = 30;\n    }\n  }\n}\n\nint main() {\n  // CHECK: hipLaunchKernelGGL(::test_0, dim3(1), dim3(1), 0, 0);\n  ::test_0<<<1, 1>>>();\n  // CHECK: hipLaunchKernelGGL(first::test_1, dim3(1), dim3(1), 0, 0);\n  first::test_1<<<1, 1>>>();\n  // CHECK: hipLaunchKernelGGL(first::second::test_2, dim3(1), dim3(1), 0, 0);\n  first::second::test_2<<<1, 1>>>();\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/namespace/ns_kernel_launch.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#pragma once\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n#include <math.h>\n// CHECK: #include \"hip/hip_fp16.h\"\n#include \"cuda_fp16.h\"\n/**\n * Allocate GPU memory for `count` elements of type `T`.\n */\ntemplate<typename T>\nstatic T *gpuMalloc(size_t count) {\n    T *ret = nullptr;\n    // CHECK: hipMalloc(&ret, count * sizeof(T));\n    cudaMalloc(&ret, count * sizeof(T));\n    return ret;\n}\n\ntemplate<typename T>\n__global__ void add(int n, T *x, T *y) {\n  int index = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n  // CHECK: half2 tmp = __float2half2_rn(0.0f);\n  half2 tmp = __float2half2_rn(0.0f);\n  // CHECK: float max_val = fmax((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  float max_val = fmax((float)tmp.x, (float)tmp.y);\n  // CHECK: float min_val = fmin((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  float min_val = fmin((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  for (int i = index; i < n; i += stride)\n      y[i] = max_val - min_val + x[i] + y[i];\n}\n\nint main(int argc, char* argv[]) {\n    size_t numElements = 50;\n    float *A = gpuMalloc<float>(numElements);\n    float* B = gpuMalloc<float>(numElements);\n    for (int i = 0; i < numElements; ++i) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 512;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(add<float>), dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<float><<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; ++i)\n        maxError = fmax(maxError, fabs(B[i] - 3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if (maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#pragma once\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n// CHECK: #include \"hip/hip_fp16.h\"\n#include \"hip/hip_fp16.h\"\n/**\n * Allocate GPU memory for `count` elements of type `T`.\n */\ntemplate<typename T>\nstatic T *gpuMalloc(size_t count) {\n    T *ret = nullptr;\n    // CHECK: hipMalloc(&ret, count * sizeof(T));\n    hipMalloc(&ret, count * sizeof(T));\n    return ret;\n}\n\ntemplate<typename T>\n__global__ void add(int n, T *x, T *y) {\n  int index = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n  // CHECK: half2 tmp = __float2half2_rn(0.0f);\n  half2 tmp = __float2half2_rn(0.0f);\n  // CHECK: float max_val = fmax((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  float max_val = fmax((float)tmp.x, (float)tmp.y);\n  // CHECK: float min_val = fmin((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  float min_val = fmin((float)reinterpret_cast<half&>(tmp.x), (float)reinterpret_cast<half&>(tmp.y));\n  for (int i = index; i < n; i += stride)\n      y[i] = max_val - min_val + x[i] + y[i];\n}\n\nint main(int argc, char* argv[]) {\n    size_t numElements = 50;\n    float *A = gpuMalloc<float>(numElements);\n    float* B = gpuMalloc<float>(numElements);\n    for (int i = 0; i < numElements; ++i) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 512;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(add<float>), dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<float><<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; ++i)\n        maxError = fmax(maxError, fabs(B[i] - 3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if (maxError == 0.0f)\n        return 0;\n    return -1;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/half2_allocators.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n#include<cuda.h>\n#include<cuda_runtime.h>\n#include<iostream>\n#include<stdio.h>\n#include<malloc.h>\n\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n#define ITER 1024*1024\n\n// CHECK: if(status != hipSuccess) {\n#define check(msg, status){ \\\nif(status != cudaSuccess) { \\\n  printf(\"%s failed. \\n\", #msg); \\\n} \\\n}\n\n__global__ void Inc1(float *Ad, float *Bd){\n\tint tx = threadIdx.x + blockIdx.x * blockDim.x;\n\tif(tx < 1 ){\n\t\tfor(int i=0;i<ITER;i++){\n\t\t\tAd[tx] = Ad[tx] + 1.0f;\n\t\t\tfor(int j=0;j<256;j++){\n\t\t\t\tBd[tx] = Ad[tx];\n\t\t\t}\n\t\t}\n\t}\n}\n\n__global__ void Inc2(float *Ad, float *Bd){\n\tint tx = threadIdx.x + blockIdx.x * blockDim.x;\n\tif(tx < 1024){\n\t\tfor(int i=0;i<ITER;i++){\n\t\t\tAd[tx] = Ad[tx] + 1.0f;\n\t\t\tfor(int j=0;j<256;j++){\n\t\t\t\tBd[tx] = Ad[tx];\n\t\t\t}\n\t\t}\n\t}\n}\n\nint main(){\n\tfloat *A, *Ad, *Bd;\n\tA = new float[LEN];\n\tfor(int i=0;i<LEN;i++){\n\t\tA[i] = 0.0f;\n\t}\n\n\t// CHECK: hipError_t status;\n\tcudaError_t status;\n\n  // CHECK: status = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n\tstatus = cudaHostRegister(A, SIZE, cudaHostRegisterMapped);\n\tcheck(\"Registering A\",status);\n\n  // CHECK: hipHostGetDevicePointer(&Ad, A, 0);\n\tcudaHostGetDevicePointer(&Ad, A, 0);\n\n  // CHECK: hipMalloc((void**) &Bd, SIZE);\n\tcudaMalloc((void**) &Bd, SIZE);\n\tdim3 dimGrid(LEN/512,1,1);\n\tdim3 dimBlock(512,1,1);\n\n  // CHECK: hipLaunchKernelGGL(Inc1, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n\tInc1<<<dimGrid, dimBlock>>>(Ad, Bd);\n\tA[0] = -(ITER*1.0f);\n\tstd::cout<<\"Same cache line before completion: \\t\"<< A[0]<<std::endl;\n\n  // CHECK: hipDeviceSynchronize();\n\tcudaDeviceSynchronize();\n\tstd::cout<<\"Same cache line after completion: \\t\"<< A[0]<<std::endl;\n\n\tfor(int i=0;i<LEN;i++){\n\t\tA[i] = 0.0f;\n\t}\n\n  // CHECK: hipLaunchKernelGGL(Inc2, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n\tInc2<<<dimGrid, dimBlock>>>(Ad, Bd);\n\tA[0] = -(ITER*1.0f);\n\tstd::cout<<\"Diff cache line before completion: \\t\"<<A[0]<<std::endl;\n\n  // CHECK: hipDeviceSynchronize();\n\tcudaDeviceSynchronize();\n\tstd::cout<<\"Diff cache line after completion: \\t\"<<A[0]<<std::endl;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include<hip/hip_runtime.h>\n#include<hip/hip_runtime.h>\n#include<iostream>\n#include<stdio.h>\n#include<malloc.h>\n\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n#define ITER 1024*1024\n\n// CHECK: if(status != hipSuccess) {\n#define check(msg, status){ \\\nif(status != hipSuccess) { \\\n  printf(\"%s failed. \\n\", #msg); \\\n} \\\n}\n\n__global__ void Inc1(float *Ad, float *Bd){\n\tint tx = threadIdx.x + blockIdx.x * blockDim.x;\n\tif(tx < 1 ){\n\t\tfor(int i=0;i<ITER;i++){\n\t\t\tAd[tx] = Ad[tx] + 1.0f;\n\t\t\tfor(int j=0;j<256;j++){\n\t\t\t\tBd[tx] = Ad[tx];\n\t\t\t}\n\t\t}\n\t}\n}\n\n__global__ void Inc2(float *Ad, float *Bd){\n\tint tx = threadIdx.x + blockIdx.x * blockDim.x;\n\tif(tx < 1024){\n\t\tfor(int i=0;i<ITER;i++){\n\t\t\tAd[tx] = Ad[tx] + 1.0f;\n\t\t\tfor(int j=0;j<256;j++){\n\t\t\t\tBd[tx] = Ad[tx];\n\t\t\t}\n\t\t}\n\t}\n}\n\nint main(){\n\tfloat *A, *Ad, *Bd;\n\tA = new float[LEN];\n\tfor(int i=0;i<LEN;i++){\n\t\tA[i] = 0.0f;\n\t}\n\n\t// CHECK: hipError_t status;\n\thipError_t status;\n\n  // CHECK: status = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n\tstatus = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n\tcheck(\"Registering A\",status);\n\n  // CHECK: hipHostGetDevicePointer(&Ad, A, 0);\n\thipHostGetDevicePointer(&Ad, A, 0);\n\n  // CHECK: hipMalloc((void**) &Bd, SIZE);\n\thipMalloc((void**) &Bd, SIZE);\n\tdim3 dimGrid(LEN/512,1,1);\n\tdim3 dimBlock(512,1,1);\n\n  // CHECK: hipLaunchKernelGGL(Inc1, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n\tInc1<<<dimGrid, dimBlock>>>(Ad, Bd);\n\tA[0] = -(ITER*1.0f);\n\tstd::cout<<\"Same cache line before completion: \\t\"<< A[0]<<std::endl;\n\n  // CHECK: hipDeviceSynchronize();\n\thipDeviceSynchronize();\n\tstd::cout<<\"Same cache line after completion: \\t\"<< A[0]<<std::endl;\n\n\tfor(int i=0;i<LEN;i++){\n\t\tA[i] = 0.0f;\n\t}\n\n  // CHECK: hipLaunchKernelGGL(Inc2, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n\tInc2<<<dimGrid, dimBlock>>>(Ad, Bd);\n\tA[0] = -(ITER*1.0f);\n\tstd::cout<<\"Diff cache line before completion: \\t\"<<A[0]<<std::endl;\n\n  // CHECK: hipDeviceSynchronize();\n\thipDeviceSynchronize();\n\tstd::cout<<\"Diff cache line after completion: \\t\"<<A[0]<<std::endl;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/cudaRegister.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n#define K_THREADS 64\n#define K_INDEX() ((gridDim.x * blockIdx.y + blockIdx.x) * blockDim.x + threadIdx.x)\n#define RND() ((rand() & 0x7FFF) / float(0x8000))\n#define ERRORCHECK() cErrorCheck(__FILE__, __LINE__)\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  cudaEvent_t t##_start, t##_end;            \\\n  cudaEventCreate(&t##_start);               \\\n  cudaEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  cudaEventRecord(t##_start);                   \\\n  cudaEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  cudaEventRecord(t##_start);                                 \\\n  cudaEventSynchronize(t##_start);                            \\\n  cudaEventRecord(t##_end);                                   \\\n  cudaEventSynchronize(t##_end);                              \\\n  cudaEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess) {\n    printf(\"Error: %s\\n\", cudaGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  cudaGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  cudaFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  cudaMalloc(&gpu.r, N * sizeof(float4));\n  cudaMalloc(&gpu.v, N * sizeof(float4));\n  cudaMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  cudaMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  cudaMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  cudaFreeHost(cpu.r);\n  cudaFreeHost(cpu.v);\n  cudaFreeHost(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  cudaFree(gpu.r);\n  cudaFree(gpu.v);\n  cudaFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n#define K_THREADS 64\n#define K_INDEX() ((gridDim.x * blockIdx.y + blockIdx.x) * blockDim.x + threadIdx.x)\n#define RND() ((rand() & 0x7FFF) / float(0x8000))\n#define ERRORCHECK() cErrorCheck(__FILE__, __LINE__)\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  hipEvent_t t##_start, t##_end;            \\\n  hipEventCreate(&t##_start);               \\\n  hipEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  hipEventRecord(t##_start);                   \\\n  hipEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  hipEventRecord(t##_start);                                 \\\n  hipEventSynchronize(t##_start);                            \\\n  hipEventRecord(t##_end);                                   \\\n  hipEventSynchronize(t##_end);                              \\\n  hipEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  hipError_t err = hipGetLastError();\n  if (err != hipSuccess) {\n    printf(\"Error: %s\\n\", hipGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  hipGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  hipFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  hipMalloc(&gpu.r, N * sizeof(float4));\n  hipMalloc(&gpu.v, N * sizeof(float4));\n  hipMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  hipHostFree(cpu.r);\n  hipHostFree(cpu.v);\n  hipHostFree(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  hipFree(gpu.r);\n  hipFree(gpu.v);\n  hipFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/intro.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <iostream>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n#define TOKEN_PASTE(X, Y) X ## Y\n#define ARG_LIST_AS_MACRO a, device_x, device_y\n#define KERNEL_CALL_AS_MACRO axpy<float><<<1, kDataLen>>>\n#define KERNEL_NAME_MACRO axpy<float>\n\n// CHECK: #define COMPLETE_LAUNCH hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y)\n#define COMPLETE_LAUNCH axpy<<<1, kDataLen>>>(a, device_x, device_y)\n\n\ntemplate<typename T>\n__global__ void axpy(T a, T *x, T *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\n__global__ void empty() {\n}\n\nint main(int argc, char* argv[]) {\n  const int kDataLen = 4;\n\n  float a = 2.0f;\n  float host_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  float host_y[kDataLen];\n\n  // Copy input data to device.\n  float* device_x;\n  float* device_y;\n\n  // CHECK: hipMalloc(&device_x, kDataLen * sizeof(float));\n  cudaMalloc(&device_x, kDataLen * sizeof(float));\n\n#ifdef HERRING\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(float));\n  cudaMalloc(&device_y, kDataLen * sizeof(float));\n#else\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(double));\n  cudaMalloc(&device_y, kDataLen * sizeof(double));\n#endif\n\n  // CHECK: hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n  cudaMemcpy(device_x, host_x, kDataLen * sizeof(float), cudaMemcpyHostToDevice);\n\n  // Launch the kernel in numerous different strange ways to exercise the prerocessor.\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<<<1, kDataLen>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, TOKEN_PASTE(device, _x), device_y);\n  axpy<float><<<1, kDataLen>>>(a, TOKEN_PASTE(device, _x), device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  axpy<float><<<1, kDataLen>>>(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(KERNEL_NAME_MACRO), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  KERNEL_NAME_MACRO<<<1, kDataLen>>>(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  KERNEL_CALL_AS_MACRO(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen, 0>>>();\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen, 0, 0>>>();\n\n  // CHECK: COMPLETE_LAUNCH;\n  COMPLETE_LAUNCH;\n\n\n  // Copy output data to host.\n  // CHECK: hipDeviceSynchronize();\n  cudaDeviceSynchronize();\n\n  // CHECK: hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n  cudaMemcpy(host_y, device_y, kDataLen * sizeof(float), cudaMemcpyDeviceToHost);\n\n  // Print the results.\n  for (int i = 0; i < kDataLen; ++i) {\n    std::cout << \"y[\" << i << \"] = \" << host_y[i] << \"\\n\";\n  }\n\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <iostream>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n#define TOKEN_PASTE(X, Y) X ## Y\n#define ARG_LIST_AS_MACRO a, device_x, device_y\n#define KERNEL_CALL_AS_MACRO axpy<float><<<1, kDataLen>>>\n#define KERNEL_NAME_MACRO axpy<float>\n\n// CHECK: #define COMPLETE_LAUNCH hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y)\n#define COMPLETE_LAUNCH axpy<<<1, kDataLen>>>(a, device_x, device_y)\n\n\ntemplate<typename T>\n__global__ void axpy(T a, T *x, T *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\n__global__ void empty() {\n}\n\nint main(int argc, char* argv[]) {\n  const int kDataLen = 4;\n\n  float a = 2.0f;\n  float host_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  float host_y[kDataLen];\n\n  // Copy input data to device.\n  float* device_x;\n  float* device_y;\n\n  // CHECK: hipMalloc(&device_x, kDataLen * sizeof(float));\n  hipMalloc(&device_x, kDataLen * sizeof(float));\n\n#ifdef HERRING\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(float));\n  hipMalloc(&device_y, kDataLen * sizeof(float));\n#else\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(double));\n  hipMalloc(&device_y, kDataLen * sizeof(double));\n#endif\n\n  // CHECK: hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n  hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n\n  // Launch the kernel in numerous different strange ways to exercise the prerocessor.\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<<<1, kDataLen>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, TOKEN_PASTE(device, _x), device_y);\n  axpy<float><<<1, kDataLen>>>(a, TOKEN_PASTE(device, _x), device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  axpy<float><<<1, kDataLen>>>(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(KERNEL_NAME_MACRO), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  KERNEL_NAME_MACRO<<<1, kDataLen>>>(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, ARG_LIST_AS_MACRO);\n  KERNEL_CALL_AS_MACRO(ARG_LIST_AS_MACRO);\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen, 0>>>();\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen, 0, 0>>>();\n\n  // CHECK: COMPLETE_LAUNCH;\n  COMPLETE_LAUNCH;\n\n\n  // Copy output data to host.\n  // CHECK: hipDeviceSynchronize();\n  hipDeviceSynchronize();\n\n  // CHECK: hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n  hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n\n  // Print the results.\n  for (int i = 0; i < kDataLen; ++i) {\n    std::cout << \"y[\" << i << \"] = \" << host_y[i] << \"\\n\";\n  }\n\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/axpy.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define CHECK(cmd) \\\n{\\\n    cudaError_t error  = cmd;\\\n    if (error != cudaSuccess) { \\\n        fprintf(stderr, \"error: '%s'(%d) at %s:%d\\n\", cudaGetErrorString(error), error,__FILE__, __LINE__); \\\n        exit(EXIT_FAILURE);\\\n\t  }\\\n}\n\n\n/*\n * Square each element in the array A and write to array C.\n */\ntemplate <typename T>\n__global__ void\nvector_square(T *C_d, const T *A_d, size_t N)\n{\n    size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);\n    size_t stride = blockDim.x * gridDim.x;\n\n    for (size_t i=offset; i<N; i+=stride) {\n        C_d[i] = A_d[i] * A_d[i];\n    }\n}\n\n\nint main(int argc, char *argv[])\n{\n    float *A_d, *C_d;\n    float *A_h, *C_h;\n    size_t N = 1000000;\n    size_t Nbytes = N * sizeof(float);\n\n    // CHECK: hipDeviceProp_t props;\n    cudaDeviceProp props;\n\n    // CHECK: CHECK(hipGetDeviceProperties(&props, 0/*deviceID*/));\n    CHECK(cudaGetDeviceProperties(&props, 0/*deviceID*/));\n    printf (\"info: running on device %s\\n\", props.name);\n\n    printf (\"info: allocate host mem (%6.2f MB)\\n\", 2*Nbytes/1024.0/1024.0);\n    A_h = (float*)malloc(Nbytes);\n\n    // CHECK: CHECK(A_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    CHECK(A_h == 0 ? cudaErrorMemoryAllocation : cudaSuccess );\n    C_h = (float*)malloc(Nbytes);\n\n    // CHECK: CHECK(C_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    CHECK(C_h == 0 ? cudaErrorMemoryAllocation : cudaSuccess );\n    // Fill with Phi + i\n    for (size_t i=0; i<N; i++)\n    {\n        A_h[i] = 1.618f + i;\n    }\n\n    printf (\"info: allocate device mem (%6.2f MB)\\n\", 2*Nbytes/1024.0/1024.0);\n    // CHECK: CHECK(hipMalloc(&A_d, Nbytes));\n    // CHECK: CHECK(hipMalloc(&C_d, Nbytes));\n    CHECK(cudaMalloc(&A_d, Nbytes));\n    CHECK(cudaMalloc(&C_d, Nbytes));\n\n\n    printf (\"info: copy Host2Device\\n\");\n    // CHECK: CHECK ( hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice));\n    CHECK ( cudaMemcpy(A_d, A_h, Nbytes, cudaMemcpyHostToDevice));\n\n    const unsigned blocks = 512;\n    const unsigned threadsPerBlock = 256;\n\n    printf (\"info: launch 'vector_square' kernel\\n\");\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(vector_square), dim3(blocks), dim3(threadsPerBlock), 0, 0, C_d, A_d, N);\n    vector_square <<<blocks, threadsPerBlock>>> (C_d, A_d, N);\n\n    printf (\"info: copy Device2Host\\n\");\n    // CHECK: CHECK ( hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost));\n    CHECK ( cudaMemcpy(C_h, C_d, Nbytes, cudaMemcpyDeviceToHost));\n\n    printf (\"info: check result\\n\");\n    for (size_t i=0; i<N; i++)  {\n        if (C_h[i] != A_h[i] * A_h[i]) {\n            // CHECK: CHECK(hipErrorUnknown);\n            CHECK(cudaErrorUnknown);\n        }\n    }\n    printf (\"PASSED!\\n\");\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n/*\nCopyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define CHECK(cmd) \\\n{\\\n    hipError_t error  = cmd;\\\n    if (error != hipSuccess) { \\\n        fprintf(stderr, \"error: '%s'(%d) at %s:%d\\n\", hipGetErrorString(error), error,__FILE__, __LINE__); \\\n        exit(EXIT_FAILURE);\\\n\t  }\\\n}\n\n\n/*\n * Square each element in the array A and write to array C.\n */\ntemplate <typename T>\n__global__ void\nvector_square(T *C_d, const T *A_d, size_t N)\n{\n    size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);\n    size_t stride = blockDim.x * gridDim.x;\n\n    for (size_t i=offset; i<N; i+=stride) {\n        C_d[i] = A_d[i] * A_d[i];\n    }\n}\n\n\nint main(int argc, char *argv[])\n{\n    float *A_d, *C_d;\n    float *A_h, *C_h;\n    size_t N = 1000000;\n    size_t Nbytes = N * sizeof(float);\n\n    // CHECK: hipDeviceProp_t props;\n    hipDeviceProp_t props;\n\n    // CHECK: CHECK(hipGetDeviceProperties(&props, 0/*deviceID*/));\n    CHECK(hipGetDeviceProperties(&props, 0/*deviceID*/));\n    printf (\"info: running on device %s\\n\", props.name);\n\n    printf (\"info: allocate host mem (%6.2f MB)\\n\", 2*Nbytes/1024.0/1024.0);\n    A_h = (float*)malloc(Nbytes);\n\n    // CHECK: CHECK(A_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    CHECK(A_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    C_h = (float*)malloc(Nbytes);\n\n    // CHECK: CHECK(C_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    CHECK(C_h == 0 ? hipErrorOutOfMemory : hipSuccess );\n    // Fill with Phi + i\n    for (size_t i=0; i<N; i++)\n    {\n        A_h[i] = 1.618f + i;\n    }\n\n    printf (\"info: allocate device mem (%6.2f MB)\\n\", 2*Nbytes/1024.0/1024.0);\n    // CHECK: CHECK(hipMalloc(&A_d, Nbytes));\n    // CHECK: CHECK(hipMalloc(&C_d, Nbytes));\n    CHECK(hipMalloc(&A_d, Nbytes));\n    CHECK(hipMalloc(&C_d, Nbytes));\n\n\n    printf (\"info: copy Host2Device\\n\");\n    // CHECK: CHECK ( hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice));\n    CHECK ( hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice));\n\n    const unsigned blocks = 512;\n    const unsigned threadsPerBlock = 256;\n\n    printf (\"info: launch 'vector_square' kernel\\n\");\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(vector_square), dim3(blocks), dim3(threadsPerBlock), 0, 0, C_d, A_d, N);\n    vector_square <<<blocks, threadsPerBlock>>> (C_d, A_d, N);\n\n    printf (\"info: copy Device2Host\\n\");\n    // CHECK: CHECK ( hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost));\n    CHECK ( hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost));\n\n    printf (\"info: check result\\n\");\n    for (size_t i=0; i<N; i++)  {\n        if (C_h[i] != A_h[i] * A_h[i]) {\n            // CHECK: CHECK(hipErrorUnknown);\n            CHECK(hipErrorUnknown);\n        }\n    }\n    printf (\"PASSED!\\n\");\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/square.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// To measure effects of memory coalescing. Coalescing.cu\n// B. Wilkinson Jan 30, 2011\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n#define BlockSize 16  // Size of blocks, 32 x 32 threads, fixed, used globally\n\n__global__ void gpu_Comput (int *h, int N, int T) {\n\n// Array loaded with global thread ID that acesses that location\n\n\tint col = threadIdx.x + blockDim.x * blockIdx.x; \n\tint row = threadIdx.y + blockDim.y * blockIdx.y;\n\n\tint threadID = col + row * N;\n\tint index = row + col * N;  // sequentially down each row\n\n\tfor (int t = 0; t < T; t++)  // loop to repeat to reduce other time effects\n\t  h[index] = threadID;  // load array with flattened global thread ID\n}\n\nvoid printArray(int *h, int N) {\n\n\tprintf(\"Results of computation, every N/8 numbers, eight numbers\\n\");\n\n\tfor (int row = 0; row < N; row += N/8) {\n\t  for (int col = 0; col < N; col += N/8)\n\t  printf(\"%6d  \", h[col + row * N]);\n\t  printf(\"\\n\");\n\t}\n}\n\nint main(int argc, char *argv[])  {\n\n\tint T = 100;  // number of iterations, entered at keyboard\n\tint B = 1;  // number of blocks, entered at keyboard\n\tchar key;\n\n\tint *h, *dev_h;  // ptr to array holding numbers on host and device\n  // CHECK: hipEvent_t start, stop;\n\tcudaEvent_t start, stop;  // cuda events to measure time\n\tfloat elapsed_time_ms1;\n  // CHECK: hipEventCreate( &start );\n  // CHECK: hipEventCreate( &stop );\n\tcudaEventCreate( &start );\n\tcudaEventCreate( &stop );\n\n/* ------------------------- Keyboard input -----------------------------------*/\n\ndo {  // loop to repeat complete program\n\n\tprintf(\"Grid Structure 2-D grid, 2-D blocks\\n\");\n\tprintf(\"Blocks fixed at 16 x 16 threads, 512 threads, max for compute cap. 1.x\\n\");\n\tprintf(\"Enter number of blocks in grid, each dimension, currently %d\\n\",B);\n\tscanf(\"%d\",&B);\n\tprintf(\"Enter number of iterations, currently %d\\n\",T);\n\tscanf(\"%d\",&T);\n\n\tint N = B * BlockSize;  // size of data array, given input data\n\n\tprintf(\"Array size (and total grid-block size) %d x %d\\n\", N, N);\n\n\tdim3 Block(BlockSize, BlockSize);  //Block structure, 32 x 32 max\n\tdim3 Grid(B, B);  //Grid structure, B x B\n\n/* ------------------------- Allocate Memory-----------------------------------*/\n\n\tint size = N * N * sizeof(int);  // number of bytes in total in array\n\th = (int*) malloc(size);  // Array on host\n  // CHECK: hipMalloc((void**)&dev_h, size);\n\tcudaMalloc((void**)&dev_h, size);  // allocate device memory\n\n/* ------------------------- GPU Computation -----------------------------------*/\n\n  // CHECK: hipEventRecord( start, 0 );\n\tcudaEventRecord( start, 0 );\n  // CHECK: hipLaunchKernelGGL(gpu_Comput, dim3(Grid), dim3(Block), 0, 0, dev_h, N, T);\n\tgpu_Comput<<< Grid, Block >>>(dev_h, N, T);\n  // CHECK: hipEventRecord( stop, 0 );\n  // CHECK: hipEventSynchronize( stop );\n  // CHECK: hipEventElapsedTime( &elapsed_time_ms1, start, stop );\n\tcudaEventRecord( stop, 0 );  // instrument code to measue end time\n\tcudaEventSynchronize( stop );  // wait for all work done by threads\n\tcudaEventElapsedTime( &elapsed_time_ms1, start, stop );\n  // CHECK: hipMemcpy(h,dev_h, size ,hipMemcpyDeviceToHost);\n\tcudaMemcpy(h,dev_h, size ,cudaMemcpyDeviceToHost);  //Get results to check\n\n\tprintArray(h,N);\n\tprintf(\"\\nTime to calculate results on GPU: %f ms.\\n\", elapsed_time_ms1);\n\n/* -------------------------REPEAT PROGRAM INPUT-----------------------------------*/\n\n\tprintf(\"\\nEnter c to repeat, return to terminate\\n\");\n\n\tscanf(\"%c\",&key);\n\tscanf(\"%c\",&key);\n\n} while (key == 'c');  // loop of complete program\n\n/* --------------  clean up  ---------------------------------------*/\n\nfree(h);\n  // CHECK: hipFree(dev_h);\n\tcudaFree(dev_h);\n  // CHECK: hipEventDestroy(start);\n  // CHECK: hipEventDestroy(stop);\n\tcudaEventDestroy(start);\n\tcudaEventDestroy(stop);\n\n\treturn 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// To measure effects of memory coalescing. Coalescing.cu\n// B. Wilkinson Jan 30, 2011\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n#define BlockSize 16  // Size of blocks, 32 x 32 threads, fixed, used globally\n\n__global__ void gpu_Comput (int *h, int N, int T) {\n\n// Array loaded with global thread ID that acesses that location\n\n\tint col = threadIdx.x + blockDim.x * blockIdx.x; \n\tint row = threadIdx.y + blockDim.y * blockIdx.y;\n\n\tint threadID = col + row * N;\n\tint index = row + col * N;  // sequentially down each row\n\n\tfor (int t = 0; t < T; t++)  // loop to repeat to reduce other time effects\n\t  h[index] = threadID;  // load array with flattened global thread ID\n}\n\nvoid printArray(int *h, int N) {\n\n\tprintf(\"Results of computation, every N/8 numbers, eight numbers\\n\");\n\n\tfor (int row = 0; row < N; row += N/8) {\n\t  for (int col = 0; col < N; col += N/8)\n\t  printf(\"%6d  \", h[col + row * N]);\n\t  printf(\"\\n\");\n\t}\n}\n\nint main(int argc, char *argv[])  {\n\n\tint T = 100;  // number of iterations, entered at keyboard\n\tint B = 1;  // number of blocks, entered at keyboard\n\tchar key;\n\n\tint *h, *dev_h;  // ptr to array holding numbers on host and device\n  // CHECK: hipEvent_t start, stop;\n\thipEvent_t start, stop;  // cuda events to measure time\n\tfloat elapsed_time_ms1;\n  // CHECK: hipEventCreate( &start );\n  // CHECK: hipEventCreate( &stop );\n\thipEventCreate( &start );\n\thipEventCreate( &stop );\n\n/* ------------------------- Keyboard input -----------------------------------*/\n\ndo {  // loop to repeat complete program\n\n\tprintf(\"Grid Structure 2-D grid, 2-D blocks\\n\");\n\tprintf(\"Blocks fixed at 16 x 16 threads, 512 threads, max for compute cap. 1.x\\n\");\n\tprintf(\"Enter number of blocks in grid, each dimension, currently %d\\n\",B);\n\tscanf(\"%d\",&B);\n\tprintf(\"Enter number of iterations, currently %d\\n\",T);\n\tscanf(\"%d\",&T);\n\n\tint N = B * BlockSize;  // size of data array, given input data\n\n\tprintf(\"Array size (and total grid-block size) %d x %d\\n\", N, N);\n\n\tdim3 Block(BlockSize, BlockSize);  //Block structure, 32 x 32 max\n\tdim3 Grid(B, B);  //Grid structure, B x B\n\n/* ------------------------- Allocate Memory-----------------------------------*/\n\n\tint size = N * N * sizeof(int);  // number of bytes in total in array\n\th = (int*) malloc(size);  // Array on host\n  // CHECK: hipMalloc((void**)&dev_h, size);\n\thipMalloc((void**)&dev_h, size);  // allocate device memory\n\n/* ------------------------- GPU Computation -----------------------------------*/\n\n  // CHECK: hipEventRecord( start, 0 );\n\thipEventRecord( start, 0 );\n  // CHECK: hipLaunchKernelGGL(gpu_Comput, dim3(Grid), dim3(Block), 0, 0, dev_h, N, T);\n\tgpu_Comput<<< Grid, Block >>>(dev_h, N, T);\n  // CHECK: hipEventRecord( stop, 0 );\n  // CHECK: hipEventSynchronize( stop );\n  // CHECK: hipEventElapsedTime( &elapsed_time_ms1, start, stop );\n\thipEventRecord( stop, 0 );  // instrument code to measue end time\n\thipEventSynchronize( stop );  // wait for all work done by threads\n\thipEventElapsedTime( &elapsed_time_ms1, start, stop );\n  // CHECK: hipMemcpy(h,dev_h, size ,hipMemcpyDeviceToHost);\n\thipMemcpy(h,dev_h, size ,hipMemcpyDeviceToHost);  //Get results to check\n\n\tprintArray(h,N);\n\tprintf(\"\\nTime to calculate results on GPU: %f ms.\\n\", elapsed_time_ms1);\n\n/* -------------------------REPEAT PROGRAM INPUT-----------------------------------*/\n\n\tprintf(\"\\nEnter c to repeat, return to terminate\\n\");\n\n\tscanf(\"%c\",&key);\n\tscanf(\"%c\",&key);\n\n} while (key == 'c');  // loop of complete program\n\n/* --------------  clean up  ---------------------------------------*/\n\nfree(h);\n  // CHECK: hipFree(dev_h);\n\thipFree(dev_h);\n  // CHECK: hipEventDestroy(start);\n  // CHECK: hipEventDestroy(stop);\n\thipEventDestroy(start);\n\thipEventDestroy(stop);\n\n\treturn 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/coalescing.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\n#define K_THREADS 64\n#define K_INDEX() ((gridDim.x * blockIdx.y + blockIdx.x) * blockDim.x + threadIdx.x)\n#define RND() ((rand() & 0x7FFF) / float(0x8000))\n#define ERRORCHECK() cErrorCheck(__FILE__, __LINE__)\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  cudaEvent_t t##_start, t##_end;            \\\n  cudaEventCreate(&t##_start);               \\\n  cudaEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  cudaEventRecord(t##_start);                   \\\n  cudaEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  cudaEventRecord(t##_start);                                 \\\n  cudaEventSynchronize(t##_start);                            \\\n  cudaEventRecord(t##_end);                                   \\\n  cudaEventSynchronize(t##_end);                              \\\n  cudaEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipDeviceSynchronize();\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  cudaThreadSynchronize();\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess) {\n    printf(\"Error: %s\\n\", cudaGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  cudaGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  cudaFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  cudaMalloc(&gpu.r, N * sizeof(float4));\n  cudaMalloc(&gpu.v, N * sizeof(float4));\n  cudaMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  cudaMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n  cudaMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), cudaMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  cudaMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  cudaMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), cudaMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  cudaFreeHost(cpu.r);\n  cudaFreeHost(cpu.v);\n  cudaFreeHost(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  cudaFree(gpu.r);\n  cudaFree(gpu.v);\n  cudaFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\n#define K_THREADS 64\n#define K_INDEX() ((gridDim.x * blockIdx.y + blockIdx.x) * blockDim.x + threadIdx.x)\n#define RND() ((rand() & 0x7FFF) / float(0x8000))\n#define ERRORCHECK() cErrorCheck(__FILE__, __LINE__)\n\n// CHECK: hipEvent_t t##_start, t##_end;            \\\n// CHECK: hipEventCreate(&t##_start);               \\\n// CHECK: hipEventCreate(&t##_end);\n#define TIMER_CREATE(t)                      \\\n  hipEvent_t t##_start, t##_end;            \\\n  hipEventCreate(&t##_start);               \\\n  hipEventCreate(&t##_end);\n\n// CHECK: hipEventRecord(t##_start);                   \\\n// CHECK: hipEventSynchronize(t##_start);\n#define TIMER_START(t)                          \\\n  hipEventRecord(t##_start);                   \\\n  hipEventSynchronize(t##_start);              \\\n\n// CHECK: hipEventRecord(t##_start);                                 \\\n// CHECK: hipEventSynchronize(t##_start);                            \\\n// CHECK: hipEventRecord(t##_end);                                   \\\n// CHECK: hipEventSynchronize(t##_end);                              \\\n// CHECK: hipEventElapsedTime(&t, t##_start, t##_end);\n#define TIMER_END(t)                                          \\\n  hipEventRecord(t##_start);                                 \\\n  hipEventSynchronize(t##_start);                            \\\n  hipEventRecord(t##_end);                                   \\\n  hipEventSynchronize(t##_end);                              \\\n  hipEventElapsedTime(&t, t##_start, t##_end);               \n\n\ninline void cErrorCheck(const char *file, int line) {\n// CHECK: hipDeviceSynchronize();\n// CHECK: hipError_t err = hipGetLastError();\n// CHECK: if (err != hipSuccess) {\n// CHECK: printf(\"Error: %s\\n\", hipGetErrorString(err));\n  hipDeviceSynchronize();\n  hipError_t err = hipGetLastError();\n  if (err != hipSuccess) {\n    printf(\"Error: %s\\n\", hipGetErrorString(err));\n    printf(\" @ %s: %d\\n\", file, line);\n    exit(-1);\n  }\n}\n\ninline dim3 K_GRID(int n, int threads = K_THREADS) {\n  int blocks = (int)ceilf(sqrtf((float)n/threads));\n  dim3 grid(blocks, blocks);\n  return grid;\n}\n\ntypedef struct data  {\n  int n;\n  float4 *r, *v, *f;\n} data;\n\ndata cpu, gpu;\n\n#define N 20\n\n__global__ void repulsion(data gpu);\n__global__ void integration(data gpu);\n\n\nint main() {\n  printf(\"Cuda Test 1\\n\");\n\n  int count = 0;\n  // CHECK: hipGetDeviceCount(&count);\n  hipGetDeviceCount(&count);\n  printf(\" %d CUDA devices found\\n\", count);\n  if(!count) {\n    ::exit(EXIT_FAILURE);\n  }\n  // CHECK: hipFree(0);\n  hipFree(0);\n\n  cpu.n = N;\n\n  cpu.r = (float4*)malloc(N * sizeof(float4));\n  cpu.v = (float4*)malloc(N * sizeof(float4));\n  cpu.f = (float4*)malloc(N * sizeof(float4));\n\n  for(int i = 0; i < N; ++i) {\n    cpu.v[i] = make_float4(0,0,0,0);\n    cpu.r[i] = make_float4(RND(), RND(), RND(), 0);\n    cpu.f[i] = make_float4(0,0.01,0,0);\n  }\n\n  gpu = cpu;\n  // CHECK: hipMalloc(&gpu.r, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.v, N * sizeof(float4));\n  // CHECK: hipMalloc(&gpu.f, N * sizeof(float4));\n  hipMalloc(&gpu.r, N * sizeof(float4));\n  hipMalloc(&gpu.v, N * sizeof(float4));\n  hipMalloc(&gpu.f, N * sizeof(float4));\n  // CHECK: hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.r, cpu.r, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.v, cpu.v, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n  hipMemcpy(gpu.f, cpu.f, cpu.n * sizeof(float4), hipMemcpyHostToDevice);\n\n  ERRORCHECK();\n  float rep;\n  TIMER_CREATE(rep);\n  TIMER_START(rep);\n  // CHECK: hipLaunchKernelGGL(integration, dim3(K_GRID(cpu.n)), dim3(K_THREADS), 0, 0, gpu);\n  integration <<< K_GRID(cpu.n), K_THREADS >>>(gpu);\n\n  TIMER_END(rep);\n  printf(\"Took: %f ms\\n\", rep);\n  ERRORCHECK();\n  // CHECK: hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.r, gpu.r, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.v, gpu.v, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  hipMemcpy(cpu.f, gpu.f, cpu.n * sizeof(float4), hipMemcpyDeviceToHost);\n  // CHECK: hipHostFree(cpu.r);\n  // CHECK: hipHostFree(cpu.v);\n  // CHECK: hipHostFree(cpu.f);\n  hipHostFree(cpu.r);\n  hipHostFree(cpu.v);\n  hipHostFree(cpu.f);\n  // CHECK: hipFree(gpu.r);\n  // CHECK: hipFree(gpu.v);\n  // CHECK: hipFree(gpu.f);\n  hipFree(gpu.r);\n  hipFree(gpu.v);\n  hipFree(gpu.f);\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n\n  printf(\"Results: \\n\");\n  for(int i = 0; i < N; ++i) {\n    printf(\"%f, %f, %f \\n\", cpu.r[i].x, cpu.r[i].y, cpu.r[i].z);\n  }\n\n  printf(\"Ready...\\n\");\n  return 0;\n}\n\n__global__ void repulsion(data gpu) {\n  int idx = K_INDEX();\n  if(idx < N) {\n    gpu.r[idx].x = 1;\n    gpu.r[idx].y = 1;\n    gpu.r[idx].z = 1;\n  }\n}\n\n#define MULT4(v, s) v.x *= s; v.y *= s; v.z *= s; v.w *= s;\n#define ADD4(v1, v2) v1.x += v2.x; v1.y += v2.y; v1.z += v2.z; v1.w += v2.w;\n\n__global__ void integration(data gpu) {\n  int i = K_INDEX();\n  if(i < N) {\n    MULT4(gpu.f[i], 0.01);\n    MULT4(gpu.v[i], 0.01);\n    ADD4(gpu.v[i], gpu.f[i]);\n    ADD4(gpu.r[i], gpu.v[i]);\n    gpu.f[i] = make_float4(0,0,0,0);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/intro_before_13000.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// Kernel definition\n__global__ void  vecAdd(float* A, float* B, float* C)\n{\n  int i = threadIdx.x;\n  A[i] = 0;\n  B[i] = i;\n  C[i] = A[i] + B[i];\n}\n// CHECK: #include <hip/hip_runtime.h>\n#include <stdio.h>\n#define SIZE 10\n#define KERNELINVOKES  5000000\nint vecadd(int  gpudevice, int rank)\n{\n  int devcheck(int, int);\n  devcheck(gpudevice, rank);\n  float A[SIZE], B[SIZE], C[SIZE];\n  // Kernel invocation\n  float *devPtrA;\n  float *devPtrB;\n  float *devPtrC;\n  int memsize = SIZE * sizeof(float);\n  // CHECK: hipMalloc((void**)&devPtrA, memsize);\n  // CHECK: hipMalloc((void**)&devPtrB, memsize);\n  // CHECK: hipMalloc((void**)&devPtrC, memsize);\n  cudaMalloc((void**)&devPtrA, memsize);\n  cudaMalloc((void**)&devPtrB, memsize);\n  cudaMalloc((void**)&devPtrC, memsize);\n  // CHECK: hipMemcpy(devPtrA, A, memsize, hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(devPtrB, B, memsize, hipMemcpyHostToDevice);\n  cudaMemcpy(devPtrA, A, memsize, cudaMemcpyHostToDevice);\n  cudaMemcpy(devPtrB, B, memsize, cudaMemcpyHostToDevice);\n  for (int i = 0; i<KERNELINVOKES; i++)\n  {\n    // CHECK: hipLaunchKernelGGL(vecAdd, dim3(1), dim3(gpudevice), 0, 0, devPtrA, devPtrB, devPtrC);\n    vecAdd <<< 1, gpudevice >>>(devPtrA, devPtrB, devPtrC);\n  }\n  // CHECK: hipMemcpy(C, devPtrC, memsize, hipMemcpyDeviceToHost);\n  cudaMemcpy(C, devPtrC, memsize, cudaMemcpyDeviceToHost);\n  // calculate only up to gpudevice to show the unique output\n  // of each rank's kernel launch\n  for (int i = 0; i<gpudevice; i++)\n    printf(\"rank %d: C[%d]=%f\\n\", rank, i, C[i]);\n  // CHECK: hipFree(devPtrA);\n  // CHECK: hipFree(devPtrA);\n  // CHECK: hipFree(devPtrA);\n  cudaFree(devPtrA);\n  cudaFree(devPtrA);\n  cudaFree(devPtrA);\n}\nint devcheck(int  gpudevice, int rank)\n{\n  int device_count = 0;\n  int device;   // used with cudaGetDevice() to verify cudaSetDevice()\n  // CHECK: hipGetDeviceCount(&device_count);\n  cudaGetDeviceCount(&device_count);\n  if (gpudevice >= device_count)\n  {\n    printf(\"gpudevice >=  device_count ... exiting\\n\");\n    exit(1);\n  }\n  // CHECK: hipError_t cudareturn;\n  // CHECK: hipDeviceProp_t deviceProp;\n  // CHECK: hipGetDeviceProperties(&deviceProp, gpudevice);\n  cudaError_t cudareturn;\n  cudaDeviceProp deviceProp;\n  cudaGetDeviceProperties(&deviceProp, gpudevice);\n  if (deviceProp.warpSize <= 1)\n  {\n    printf(\"rank %d: warning, CUDA Device Emulation (CPU) detected, exiting\\n\", rank);\n    exit(1);\n  }\n  // CHECK: cudareturn = hipSetDevice(gpudevice);\n  cudareturn = cudaSetDevice(gpudevice);\n  // CHECK: if (cudareturn == hipErrorInvalidDevice)\n  if (cudareturn == cudaErrorInvalidDevice)\n  {\n    // CHECK: perror(\"hipSetDevice returned hipErrorInvalidDevice\");\n    perror(\"cudaSetDevice returned cudaErrorInvalidDevice\");\n  }\n  else\n  {\n    // CHECK: hipGetDevice(&device);\n    cudaGetDevice(&device);\n    printf(\"rank %d: cudaGetDevice()=%d\\n\", rank, device);\n  }\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// Kernel definition\n#include \"hip/hip_runtime.h\"\n__global__ void  vecAdd(float* A, float* B, float* C)\n{\n  int i = threadIdx.x;\n  A[i] = 0;\n  B[i] = i;\n  C[i] = A[i] + B[i];\n}\n// CHECK: #include <hip/hip_runtime.h>\n#include <stdio.h>\n#define SIZE 10\n#define KERNELINVOKES  5000000\nint vecadd(int  gpudevice, int rank)\n{\n  int devcheck(int, int);\n  devcheck(gpudevice, rank);\n  float A[SIZE], B[SIZE], C[SIZE];\n  // Kernel invocation\n  float *devPtrA;\n  float *devPtrB;\n  float *devPtrC;\n  int memsize = SIZE * sizeof(float);\n  // CHECK: hipMalloc((void**)&devPtrA, memsize);\n  // CHECK: hipMalloc((void**)&devPtrB, memsize);\n  // CHECK: hipMalloc((void**)&devPtrC, memsize);\n  hipMalloc((void**)&devPtrA, memsize);\n  hipMalloc((void**)&devPtrB, memsize);\n  hipMalloc((void**)&devPtrC, memsize);\n  // CHECK: hipMemcpy(devPtrA, A, memsize, hipMemcpyHostToDevice);\n  // CHECK: hipMemcpy(devPtrB, B, memsize, hipMemcpyHostToDevice);\n  hipMemcpy(devPtrA, A, memsize, hipMemcpyHostToDevice);\n  hipMemcpy(devPtrB, B, memsize, hipMemcpyHostToDevice);\n  for (int i = 0; i<KERNELINVOKES; i++)\n  {\n    // CHECK: hipLaunchKernelGGL(vecAdd, dim3(1), dim3(gpudevice), 0, 0, devPtrA, devPtrB, devPtrC);\n    vecAdd <<< 1, gpudevice >>>(devPtrA, devPtrB, devPtrC);\n  }\n  // CHECK: hipMemcpy(C, devPtrC, memsize, hipMemcpyDeviceToHost);\n  hipMemcpy(C, devPtrC, memsize, hipMemcpyDeviceToHost);\n  // calculate only up to gpudevice to show the unique output\n  // of each rank's kernel launch\n  for (int i = 0; i<gpudevice; i++)\n    printf(\"rank %d: C[%d]=%f\\n\", rank, i, C[i]);\n  // CHECK: hipFree(devPtrA);\n  // CHECK: hipFree(devPtrA);\n  // CHECK: hipFree(devPtrA);\n  hipFree(devPtrA);\n  hipFree(devPtrA);\n  hipFree(devPtrA);\n}\nint devcheck(int  gpudevice, int rank)\n{\n  int device_count = 0;\n  int device;   // used with cudaGetDevice() to verify cudaSetDevice()\n  // CHECK: hipGetDeviceCount(&device_count);\n  hipGetDeviceCount(&device_count);\n  if (gpudevice >= device_count)\n  {\n    printf(\"gpudevice >=  device_count ... exiting\\n\");\n    exit(1);\n  }\n  // CHECK: hipError_t cudareturn;\n  // CHECK: hipDeviceProp_t deviceProp;\n  // CHECK: hipGetDeviceProperties(&deviceProp, gpudevice);\n  hipError_t cudareturn;\n  hipDeviceProp_t deviceProp;\n  hipGetDeviceProperties(&deviceProp, gpudevice);\n  if (deviceProp.warpSize <= 1)\n  {\n    printf(\"rank %d: warning, CUDA Device Emulation (CPU) detected, exiting\\n\", rank);\n    exit(1);\n  }\n  // CHECK: cudareturn = hipSetDevice(gpudevice);\n  cudareturn = hipSetDevice(gpudevice);\n  // CHECK: if (cudareturn == hipErrorInvalidDevice)\n  if (cudareturn == hipErrorInvalidDevice)\n  {\n    // CHECK: perror(\"hipSetDevice returned hipErrorInvalidDevice\");\n    perror(\"hipSetDevice returned hipErrorInvalidDevice\");\n  }\n  else\n  {\n    // CHECK: hipGetDevice(&device);\n    hipGetDevice(&device);\n    printf(\"rank %d: hipGetDevice()=%d\\n\", rank, device);\n  }\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/vec_add.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n#include <cstdio>\n#include <iostream>\n#include <fstream>\n#include <vector>\n#include <chrono>\n#include <cfloat>\n#include <iomanip>\n#include <cmath>\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n\nvoid check_error(void) {\n  // CHECK: hipError_t err = hipGetLastError();\n  cudaError_t err = cudaGetLastError();\n  // CHECK: if (err != hipSuccess) {\n  if (err != cudaSuccess) {\n    std::cerr\n      << \"Error: \"\n      // CHECK: << hipGetErrorString(err)\n      << cudaGetErrorString(err)\n      << std::endl;\n      exit(err);\n  }\n}\n\n__global__ void atomic_reduction_kernel(int *in, int *out, int ARRAYSIZE) {\n  int sum = int(0);\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  for (int i = idx; i < ARRAYSIZE; i += blockDim.x * gridDim.x) {\n    sum += in[i];\n  }\n  // CHECK: atomicAdd(out, sum);\n  atomicAdd(out, sum);\n}\n\n__global__ void atomic_reduction_kernel2(int *in, int *out, int ARRAYSIZE) {\n  int sum = int(0);\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  for(int i = idx * 16; i < ARRAYSIZE; i += blockDim.x * gridDim.x * 16) {\n    sum += in[i] + in[i+1] + in[i+2] + in[i+3] + in[i+4] + in[i+5] + in[i+6] + in[i+7] + in[i+8] + in[i+9] + in[i+10]\n         + in[i+11] + in[i+12] + in[i+13] + in[i+14] + in[i+15];\n  }\n  // CHECK: atomicAdd(out, sum);\n  atomicAdd(out, sum);\n}\n\n__global__ void atomic_reduction_kernel3(int *in, int *out, int ARRAYSIZE) {\n    int sum = int(0);\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for(int i = idx * 4; i < ARRAYSIZE; i += blockDim.x * gridDim.x * 4) {\n      sum += in[i] + in[i+1] + in[i+2] + in[i+3];\n    }\n    // CHECK: atomicAdd(out, sum);\n    atomicAdd(out, sum);\n}\n\nint main(int argc, char **argv) {\n  unsigned int ARRAYSIZE = 52428800;\n  if (argc < 2) {\n    printf(\"Usage: ./reduction num_of_elems\\n\");\n    printf(\"using default value: %d\\n\", ARRAYSIZE);\n  } else\n    ARRAYSIZE = atoi(argv[1]);\n  int N = 10;\n  printf(\"ARRAYSIZE: %d\\n\", ARRAYSIZE);\n  std::cout << \"Array size: \" << ARRAYSIZE * sizeof(int) / 1024.0 / 1024.0 << \" MB\" << std::endl;\n  int *Array = (int*)malloc(ARRAYSIZE * sizeof(int));\n  int checksum = 0; \n  for (int i = 0; i < ARRAYSIZE; ++i) {\n    Array[i] = rand()%2;\n    checksum += Array[i];\n  }\n  int *in, *out;\n  std::chrono::high_resolution_clock::time_point t1, t2;\n  long long size = sizeof(int) * ARRAYSIZE;\n  // CHECK: hipMalloc(&in, size);\n  cudaMalloc(&in, size);\n  // CHECK: hipMalloc(&out, sizeof(int));\n  cudaMalloc(&out, sizeof(int));\n  check_error();\n  // CHECK: hipMemcpy(in, Array, ARRAYSIZE * sizeof(int), hipMemcpyHostToDevice);\n  cudaMemcpy(in, Array, ARRAYSIZE * sizeof(int), cudaMemcpyHostToDevice);\n  // CHECK: hipDeviceSynchronize();\n  cudaDeviceSynchronize();\n  check_error();\n  // CHECK: hipDeviceProp_t props;\n  cudaDeviceProp props;\n  // CHECK: hipGetDeviceProperties(&props, 0);\n  cudaGetDeviceProperties(&props, 0);\n  int threads = 256;\n  int blocks = std::min((ARRAYSIZE + threads - 1) / threads, 2048u);\n  t1 = std::chrono::high_resolution_clock::now();\n  for(int i = 0; i < N; ++i) {\n    // CHECK: hipMemsetAsync(out, 0, sizeof(int));\n    cudaMemsetAsync(out, 0, sizeof(int));\n    // CHECK: atomic_reduction_kernel<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    // CHECK: atomic_reduction_kernel2<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel2<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    // CHECK: atomic_reduction_kernel3<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel3<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    check_error();\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    check_error();\n  }\n  t2 = std::chrono::high_resolution_clock::now();\n  double times = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1).count();\n  float GB = (float)ARRAYSIZE * sizeof(int) * N;\n  std::cout << \"The average performance of reduction is \" << 1.0E-09 * GB / times << \" GBytes/sec\" << std::endl;\n  int sum;\n  // CHECK: hipMemcpy(&sum, out, sizeof(int), hipMemcpyDeviceToHost);\n  cudaMemcpy(&sum, out, sizeof(int), cudaMemcpyDeviceToHost);\n  check_error();\n  if(sum == checksum)\n    std::cout << \"VERIFICATION: result is CORRECT\" << std::endl << std::endl;\n  else\n    std::cout << \"VERIFICATION: result is INCORRECT!!\" << std::endl << std::endl;\n  // CHECK: hipFree(in);\n  cudaFree(in);\n  // CHECK: hipFree(out);\n  cudaFree(out);\n  check_error();\n  free(Array);\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args %clang_args\n/*\nCopyright (c) 2015-present Advanced Micro Devices, Inc. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\n#include <cstdio>\n#include <iostream>\n#include <fstream>\n#include <vector>\n#include <chrono>\n#include <cfloat>\n#include <iomanip>\n#include <cmath>\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\nvoid check_error(void) {\n  // CHECK: hipError_t err = hipGetLastError();\n  hipError_t err = hipGetLastError();\n  // CHECK: if (err != hipSuccess) {\n  if (err != hipSuccess) {\n    std::cerr\n      << \"Error: \"\n      // CHECK: << hipGetErrorString(err)\n      << hipGetErrorString(err)\n      << std::endl;\n      exit(err);\n  }\n}\n\n__global__ void atomic_reduction_kernel(int *in, int *out, int ARRAYSIZE) {\n  int sum = int(0);\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  for (int i = idx; i < ARRAYSIZE; i += blockDim.x * gridDim.x) {\n    sum += in[i];\n  }\n  // CHECK: atomicAdd(out, sum);\n  atomicAdd(out, sum);\n}\n\n__global__ void atomic_reduction_kernel2(int *in, int *out, int ARRAYSIZE) {\n  int sum = int(0);\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  for(int i = idx * 16; i < ARRAYSIZE; i += blockDim.x * gridDim.x * 16) {\n    sum += in[i] + in[i+1] + in[i+2] + in[i+3] + in[i+4] + in[i+5] + in[i+6] + in[i+7] + in[i+8] + in[i+9] + in[i+10]\n         + in[i+11] + in[i+12] + in[i+13] + in[i+14] + in[i+15];\n  }\n  // CHECK: atomicAdd(out, sum);\n  atomicAdd(out, sum);\n}\n\n__global__ void atomic_reduction_kernel3(int *in, int *out, int ARRAYSIZE) {\n    int sum = int(0);\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for(int i = idx * 4; i < ARRAYSIZE; i += blockDim.x * gridDim.x * 4) {\n      sum += in[i] + in[i+1] + in[i+2] + in[i+3];\n    }\n    // CHECK: atomicAdd(out, sum);\n    atomicAdd(out, sum);\n}\n\nint main(int argc, char **argv) {\n  unsigned int ARRAYSIZE = 52428800;\n  if (argc < 2) {\n    printf(\"Usage: ./reduction num_of_elems\\n\");\n    printf(\"using default value: %d\\n\", ARRAYSIZE);\n  } else\n    ARRAYSIZE = atoi(argv[1]);\n  int N = 10;\n  printf(\"ARRAYSIZE: %d\\n\", ARRAYSIZE);\n  std::cout << \"Array size: \" << ARRAYSIZE * sizeof(int) / 1024.0 / 1024.0 << \" MB\" << std::endl;\n  int *Array = (int*)malloc(ARRAYSIZE * sizeof(int));\n  int checksum = 0; \n  for (int i = 0; i < ARRAYSIZE; ++i) {\n    Array[i] = rand()%2;\n    checksum += Array[i];\n  }\n  int *in, *out;\n  std::chrono::high_resolution_clock::time_point t1, t2;\n  long long size = sizeof(int) * ARRAYSIZE;\n  // CHECK: hipMalloc(&in, size);\n  hipMalloc(&in, size);\n  // CHECK: hipMalloc(&out, sizeof(int));\n  hipMalloc(&out, sizeof(int));\n  check_error();\n  // CHECK: hipMemcpy(in, Array, ARRAYSIZE * sizeof(int), hipMemcpyHostToDevice);\n  hipMemcpy(in, Array, ARRAYSIZE * sizeof(int), hipMemcpyHostToDevice);\n  // CHECK: hipDeviceSynchronize();\n  hipDeviceSynchronize();\n  check_error();\n  // CHECK: hipDeviceProp_t props;\n  hipDeviceProp_t props;\n  // CHECK: hipGetDeviceProperties(&props, 0);\n  hipGetDeviceProperties(&props, 0);\n  int threads = 256;\n  int blocks = std::min((ARRAYSIZE + threads - 1) / threads, 2048u);\n  t1 = std::chrono::high_resolution_clock::now();\n  for(int i = 0; i < N; ++i) {\n    // CHECK: hipMemsetAsync(out, 0, sizeof(int));\n    hipMemsetAsync(out, 0, sizeof(int));\n    // CHECK: atomic_reduction_kernel<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    // CHECK: atomic_reduction_kernel2<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel2<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    // CHECK: atomic_reduction_kernel3<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    atomic_reduction_kernel3<<<blocks, threads>>>(in, out, ARRAYSIZE);\n    check_error();\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    check_error();\n  }\n  t2 = std::chrono::high_resolution_clock::now();\n  double times = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1).count();\n  float GB = (float)ARRAYSIZE * sizeof(int) * N;\n  std::cout << \"The average performance of reduction is \" << 1.0E-09 * GB / times << \" GBytes/sec\" << std::endl;\n  int sum;\n  // CHECK: hipMemcpy(&sum, out, sizeof(int), hipMemcpyDeviceToHost);\n  hipMemcpy(&sum, out, sizeof(int), hipMemcpyDeviceToHost);\n  check_error();\n  if(sum == checksum)\n    std::cout << \"VERIFICATION: result is CORRECT\" << std::endl << std::endl;\n  else\n    std::cout << \"VERIFICATION: result is INCORRECT!!\" << std::endl << std::endl;\n  // CHECK: hipFree(in);\n  hipFree(in);\n  // CHECK: hipFree(out);\n  hipFree(out);\n  check_error();\n  free(Array);\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/reduction.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#pragma once\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n/**\n * Allocate GPU memory for `count` elements of type `T`.\n */\ntemplate<typename T>\nstatic T* gpuMalloc(size_t count) {\n    T* ret = nullptr;\n    // CHECK: hipMalloc(&ret, count * sizeof(T));\n    cudaMalloc(&ret, count * sizeof(T));\n    return ret;\n}\n\ntemplate<typename T>\n__global__ void add(int n, T* x, T* y) {\n  int index = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n  for (int i = index; i < n; i += stride)\n    y[i] = x[i] + y[i];\n}\n\nint main(int argc, char* argv[]) {\n    size_t numElements = 50;\n    float *A = gpuMalloc<float>(numElements);\n    float* B = gpuMalloc<float>(numElements);\n    for (int i = 0; i < numElements; ++i) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 512;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(add<float>), dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<float><<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    cudaDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; ++i)\n      maxError = fmax(maxError, fabs(B[i] - 3.0f));\n    // CHECK: hipFree(A);\n    cudaFree(A);\n    // CHECK: hipFree(B);\n    cudaFree(B);\n    if (maxError == 0.0f)\n      return 0;\n    return -1;\n  }\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#pragma once\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n\n/**\n * Allocate GPU memory for `count` elements of type `T`.\n */\ntemplate<typename T>\nstatic T* gpuMalloc(size_t count) {\n    T* ret = nullptr;\n    // CHECK: hipMalloc(&ret, count * sizeof(T));\n    hipMalloc(&ret, count * sizeof(T));\n    return ret;\n}\n\ntemplate<typename T>\n__global__ void add(int n, T* x, T* y) {\n  int index = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n  for (int i = index; i < n; i += stride)\n    y[i] = x[i] + y[i];\n}\n\nint main(int argc, char* argv[]) {\n    size_t numElements = 50;\n    float *A = gpuMalloc<float>(numElements);\n    float* B = gpuMalloc<float>(numElements);\n    for (int i = 0; i < numElements; ++i) {\n        A[i] = 1.0f;\n        B[i] = 2.0f;\n    }\n    int blockSize = 512;\n    int numBlocks = (numElements + blockSize - 1) / blockSize;\n    dim3 dimGrid(numBlocks, 1, 1);\n    dim3 dimBlock(blockSize, 1, 1);\n    // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(add<float>), dim3(dimGrid), dim3(dimBlock), 0, 0, numElements, A, B);\n    add<float><<<dimGrid, dimBlock>>>(numElements, A, B);\n    // CHECK: hipDeviceSynchronize();\n    hipDeviceSynchronize();\n    float maxError = 0.0f;\n    for (int i = 0; i < numElements; ++i)\n      maxError = fmax(maxError, fabs(B[i] - 3.0f));\n    // CHECK: hipFree(A);\n    hipFree(A);\n    // CHECK: hipFree(B);\n    hipFree(B);\n    if (maxError == 0.0f)\n      return 0;\n    return -1;\n  }\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/samples/allocators.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <iostream>\n#include <algorithm>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda.h>\n\ntemplate<typename T>\n__global__ void axpy(T a, T *x, T *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\ntemplate<typename T1, typename T2>\n__global__ void axpy_2(T1 a, T2 *x, T2 *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\ntemplate<typename T>\n__global__ void axpy_empty() {\n}\n\n__global__ void empty() {\n}\n\n__global__ void nonempty(int x, int y, int z) {\n}\n\nint main(int argc, char* argv[]) {\n  const int kDataLen = 4;\n\n  float a = 2.0f;\n  float host_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  float host_y[kDataLen];\n\n  // Copy input data to device.\n  float* device_x;\n  float* device_y;\n\n  // CHECK: hipMalloc(&device_x, kDataLen * sizeof(float));\n  cudaMalloc(&device_x, kDataLen * sizeof(float));\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(float));\n  cudaMalloc(&device_y, kDataLen * sizeof(float));\n  // CHECK: hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n  cudaMemcpy(device_x, host_x, kDataLen * sizeof(float), cudaMemcpyHostToDevice);\n\n  int x = 1, y = 2, z = 3;\n  size_t N = 32;\n  // CHECK: hipStream_t stream = NULL;\n  cudaStream_t stream = NULL;\n  // CHECK: hipStreamCreate(&stream);\n  cudaStreamCreate(&stream);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen)>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen)>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen, N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen, N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen), N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen), N>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<1, kDataLen, N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen, N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen), N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen), N, stream>>>(a, device_x, device_y);\n\n  double h_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  double h_y[kDataLen];\n\n  // Copy input data to device.\n  double* d_x;\n  double* d_y;\n\n  // CHECK: hipMalloc(&d_x, kDataLen * sizeof(double));\n  cudaMalloc(&d_x, kDataLen * sizeof(double));\n  // CHECK: hipMalloc(&d_y, kDataLen * sizeof(double));\n  cudaMalloc(&d_y, kDataLen * sizeof(double));\n  // CHECK: hipMemcpy(d_x, h_x, kDataLen * sizeof(double), hipMemcpyHostToDevice);\n  cudaMemcpy(d_x, h_x, kDataLen * sizeof(double), cudaMemcpyHostToDevice);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<1, kDataLen*2+10, N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<dim3(1,1,1), kDataLen*2+10, N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<1, dim3(kDataLen*2+10), N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream>>>(a, d_x, d_y);\n\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<1, kDataLen>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<dim3(1), kDataLen>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<1, dim3(kDataLen)>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen)>>>();\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<1, kDataLen, N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<dim3(1), kDataLen, N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<1, dim3(kDataLen), N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen), N>>>();\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<1, kDataLen, N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<dim3(1), kDataLen, N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<1, dim3(kDataLen), N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen), N, stream>>>();\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<dim3(1), kDataLen>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, dim3(kDataLen)>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<dim3(1), dim3(kDataLen)>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<1, kDataLen, N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<dim3(1), kDataLen, N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<1, dim3(kDataLen), N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<dim3(1), dim3(kDataLen), N>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<1, kDataLen, N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<dim3(1), kDataLen, N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<1, dim3(kDataLen), N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<dim3(1), dim3(kDataLen), N, stream>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<1, kDataLen>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<dim3(1), kDataLen>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<1, dim3(kDataLen)>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen)>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<1, kDataLen, N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<dim3(1), kDataLen, N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<1, dim3(kDataLen), N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen), N>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<1, kDataLen, N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<dim3(1), kDataLen, N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<1, dim3(kDataLen), N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen), N, stream>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), std::min(x,y), stream, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x), std::min(x,y), stream>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), std::min(x,y), 0, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x), std::min(x,y)>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), 0, 0, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x)>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(y,z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(y,z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,y),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,y),z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N)),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N)),z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N+N -x/y + y*1)),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N+N -x/y + y*1)),z))>>>(x, y, z);\n\n  // Copy output data to host.\n  // CHECK: hipDeviceSynchronize();\n  cudaDeviceSynchronize();\n\n  // CHECK: hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n  cudaMemcpy(host_y, device_y, kDataLen * sizeof(float), cudaMemcpyDeviceToHost);\n\n  // Print the results.\n  for (int i = 0; i < kDataLen; ++i) {\n    std::cout << \"y[\" << i << \"] = \" << host_y[i] << \"\\n\";\n  }\n\n  // CHECK: hipDeviceReset();\n  cudaDeviceReset();\n  return 0;\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n#include <iostream>\n#include <algorithm>\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n\ntemplate<typename T>\n__global__ void axpy(T a, T *x, T *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\ntemplate<typename T1, typename T2>\n__global__ void axpy_2(T1 a, T2 *x, T2 *y) {\n  y[threadIdx.x] = a * x[threadIdx.x];\n}\n\ntemplate<typename T>\n__global__ void axpy_empty() {\n}\n\n__global__ void empty() {\n}\n\n__global__ void nonempty(int x, int y, int z) {\n}\n\nint main(int argc, char* argv[]) {\n  const int kDataLen = 4;\n\n  float a = 2.0f;\n  float host_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  float host_y[kDataLen];\n\n  // Copy input data to device.\n  float* device_x;\n  float* device_y;\n\n  // CHECK: hipMalloc(&device_x, kDataLen * sizeof(float));\n  hipMalloc(&device_x, kDataLen * sizeof(float));\n  // CHECK: hipMalloc(&device_y, kDataLen * sizeof(float));\n  hipMalloc(&device_y, kDataLen * sizeof(float));\n  // CHECK: hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n  hipMemcpy(device_x, host_x, kDataLen * sizeof(float), hipMemcpyHostToDevice);\n\n  int x = 1, y = 2, z = 3;\n  size_t N = 32;\n  // CHECK: hipStream_t stream = NULL;\n  hipStream_t stream = NULL;\n  // CHECK: hipStreamCreate(&stream);\n  hipStreamCreate(&stream);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen)>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), 0, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen)>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<1, kDataLen, N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen, N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen), N>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, 0, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen), N>>>(a, device_x, device_y);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<1, kDataLen, N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<dim3(1), kDataLen, N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<1, dim3(kDataLen), N, stream>>>(a, device_x, device_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy<float>), dim3(1), dim3(kDataLen), N, stream, a, device_x, device_y);\n  axpy<float><<<dim3(1), dim3(kDataLen), N, stream>>>(a, device_x, device_y);\n\n  double h_x[kDataLen] = {1.0f, 2.0f, 3.0f, 4.0f};\n  double h_y[kDataLen];\n\n  // Copy input data to device.\n  double* d_x;\n  double* d_y;\n\n  // CHECK: hipMalloc(&d_x, kDataLen * sizeof(double));\n  hipMalloc(&d_x, kDataLen * sizeof(double));\n  // CHECK: hipMalloc(&d_y, kDataLen * sizeof(double));\n  hipMalloc(&d_y, kDataLen * sizeof(double));\n  // CHECK: hipMemcpy(d_x, h_x, kDataLen * sizeof(double), hipMemcpyHostToDevice);\n  hipMemcpy(d_x, h_x, kDataLen * sizeof(double), hipMemcpyHostToDevice);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<1, kDataLen*2+10, N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<dim3(1,1,1), kDataLen*2+10, N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<1, dim3(kDataLen*2+10), N*N, stream>>>(a, d_x, d_y);\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream, a, d_x, d_y);\n  axpy_2<float,double><<<dim3(1,1,1), dim3(kDataLen*2+10), N*N, stream>>>(a, d_x, d_y);\n\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<1, kDataLen>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<dim3(1), kDataLen>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<1, dim3(kDataLen)>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), 0, 0);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen)>>>();\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<1, kDataLen, N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<dim3(1), kDataLen, N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<1, dim3(kDataLen), N>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, 0);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen), N>>>();\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<1, kDataLen, N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<dim3(1), kDataLen, N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<1, dim3(kDataLen), N, stream>>>();\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_empty<float>), dim3(1), dim3(kDataLen), N, stream);\n  axpy_empty<float><<<dim3(1), dim3(kDataLen), N, stream>>>();\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, kDataLen>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<dim3(1), kDataLen>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<1, dim3(kDataLen)>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), 0, 0);\n  empty<<<dim3(1), dim3(kDataLen)>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<1, kDataLen, N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<dim3(1), kDataLen, N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<1, dim3(kDataLen), N>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, 0);\n  empty<<<dim3(1), dim3(kDataLen), N>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<1, kDataLen, N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<dim3(1), kDataLen, N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<1, dim3(kDataLen), N, stream>>> ( );\n  // CHECK: hipLaunchKernelGGL(empty, dim3(1), dim3(kDataLen), N, stream);\n  empty<<<dim3(1), dim3(kDataLen), N, stream>>> ( );\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<1, kDataLen>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<dim3(1), kDataLen>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<1, dim3(kDataLen)>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), 0, 0, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen)>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<1, kDataLen, N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<dim3(1), kDataLen, N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<1, dim3(kDataLen), N>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, 0, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen), N>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<1, kDataLen, N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<dim3(1), kDataLen, N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<1, dim3(kDataLen), N, stream>>> (x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(1), dim3(kDataLen), N, stream, x, y, z);\n  nonempty<<<dim3(1), dim3(kDataLen), N, stream>>> (x, y, z);\n\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), std::min(x,y), stream, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x), std::min(x,y), stream>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), std::min(x,y), 0, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x), std::min(x,y)>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n  // CHECK: hipLaunchKernelGGL(HIP_KERNEL_NAME(axpy_2<float,double>), dim3(x,y,z), dim3(std::min(kDataLen*2+10,x)), 0, 0, a, std::min(d_x,d_y), std::max(d_x,d_y));\n  axpy_2<float,double><<<dim3(x,y,z), std::min(kDataLen*2+10,x)>>>(a, std::min(d_x,d_y), std::max(d_x,d_y));\n\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(y,z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(y,z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,y),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,y),z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N)),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N)),z))>>>(x, y, z);\n  // CHECK: hipLaunchKernelGGL(nonempty, dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N+N -x/y + y*1)),z)), 0, 0, x, y, z);\n  nonempty<<<dim3(x,y,z), dim3(x,y,std::min(std::max(x,int(N+N -x/y + y*1)),z))>>>(x, y, z);\n\n  // Copy output data to host.\n  // CHECK: hipDeviceSynchronize();\n  hipDeviceSynchronize();\n\n  // CHECK: hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n  hipMemcpy(host_y, device_y, kDataLen * sizeof(float), hipMemcpyDeviceToHost);\n\n  // Print the results.\n  for (int i = 0; i < kDataLen; ++i) {\n    std::cout << \"y[\" << i << \"] = \" << host_y[i] << \"\\n\";\n  }\n\n  // CHECK: hipDeviceReset();\n  hipDeviceReset();\n  return 0;\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/kernel_launch/kernel_launch_syntax.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// Synthetic test to warn only on device functions umin and umax as unsupported, but not on user defined ones.\n// ToDo: change lit testing in order to parse the output.\n\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n#define ITER 1024*1024\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <algorithm>\n\n#define CUDA_LAUNCH(cuda_call,dimGrid,dimBlock, ...) \\\n    cuda_call<<<dimGrid,dimBlock>>>(__VA_ARGS__);\n\n__global__ void Inc1(float *Ad, float *Bd) {\n  int tx = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tx < 1) {\n    for (int i = 0; i < ITER; ++i) {\n      Ad[tx] = Ad[tx] + 1.0f;\n      for (int j = 0; j < 256; ++j) {\n        Bd[tx] = Ad[tx];\n      }\n    }\n  }\n}\n\nint main() {\n  float *A, *Ad, *Bd;\n  A = new float[LEN];\n  for (int i = 0; i < LEN; ++i) {\n    A[i] = 0.0f;\n  }\n  // CHECK: hipError_t status;\n  cudaError_t status;\n  // CHECK: status = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n  status = cudaHostRegister(A, SIZE, cudaHostRegisterMapped);\n  // CHECK: hipHostGetDevicePointer(&Ad, A, 0);\n  cudaHostGetDevicePointer(&Ad, A, 0);\n  // CHECK: hipMalloc((void**)&Bd, SIZE);\n  cudaMalloc((void**)&Bd, SIZE);\n  dim3 dimGrid(LEN / 512, 1, 1);\n  dim3 dimBlock(512, 1, 1);\n\n  // CHECK: hipLaunchKernelGGL(Inc1, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n  CUDA_LAUNCH(Inc1, dimGrid, dimBlock, Ad, Bd);\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n// Synthetic test to warn only on device functions umin and umax as unsupported, but not on user defined ones.\n// ToDo: change lit testing in order to parse the output.\n\n#include \"hip/hip_runtime.h\"\n#define LEN 1024\n#define SIZE LEN * sizeof(float)\n#define ITER 1024*1024\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <algorithm>\n\n#define CUDA_LAUNCH(cuda_call,dimGrid,dimBlock, ...) \\\n    cuda_call<<<dimGrid,dimBlock>>>(__VA_ARGS__);\n\n__global__ void Inc1(float *Ad, float *Bd) {\n  int tx = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tx < 1) {\n    for (int i = 0; i < ITER; ++i) {\n      Ad[tx] = Ad[tx] + 1.0f;\n      for (int j = 0; j < 256; ++j) {\n        Bd[tx] = Ad[tx];\n      }\n    }\n  }\n}\n\nint main() {\n  float *A, *Ad, *Bd;\n  A = new float[LEN];\n  for (int i = 0; i < LEN; ++i) {\n    A[i] = 0.0f;\n  }\n  // CHECK: hipError_t status;\n  hipError_t status;\n  // CHECK: status = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n  status = hipHostRegister(A, SIZE, hipHostRegisterMapped);\n  // CHECK: hipHostGetDevicePointer(&Ad, A, 0);\n  hipHostGetDevicePointer(&Ad, A, 0);\n  // CHECK: hipMalloc((void**)&Bd, SIZE);\n  hipMalloc((void**)&Bd, SIZE);\n  dim3 dimGrid(LEN / 512, 1, 1);\n  dim3 dimBlock(512, 1, 1);\n\n  // CHECK: hipLaunchKernelGGL(Inc1, dim3(dimGrid), dim3(dimBlock), 0, 0, Ad, Bd);\n  CUDA_LAUNCH(Inc1, dimGrid, dimBlock, Ad, Bd);\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/kernel_launch/kernel_launch_01.cu"}
{"input": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <cuda_runtime.h>\n#include <algorithm>\n\n__global__ void my_kernel(int arg) {}\n\nvoid test_launches() {\n    struct { int x, y; } config = {1, 2};\n    dim3 grid_arr[2] = {dim3(1), dim3(2)};\n    int shared_mem = 0;\n\n    // CHECK: hipStream_t stream = 0;\n    cudaStream_t stream = 0;\n\n    // 1. Array indexing\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(grid_arr[0]), dim3(grid_arr[1]), 0, 0, 0);\n    my_kernel<<<grid_arr[0], grid_arr[1]>>>(0);\n\n    // 2. Member access\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(config.x), dim3(config.y), 0, 0, 0);\n    my_kernel<<<dim3(config.x), dim3(config.y)>>>(0);\n\n    // 3. Math, Logical, and Ternary operators\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(config.x == 1 ? 2 : 1), dim3(256), 0, 0, 0);\n    my_kernel<<<config.x == 1 ? 2 : 1, 256>>>(0);\n\n    // 4. Address-of operator\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(1), dim3(256), shared_mem, &stream, 0);\n    my_kernel<<<1, 256, shared_mem, &stream>>>(0);\n\n    // 5. Std calls\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(1), dim3(std::max(config.x, config.y)), shared_mem, 0, std::min(config.x, config.y));\n    my_kernel<<<1, std::max(config.x, config.y), shared_mem>>>(std::min(config.x, config.y));\n}\n", "output": "// RUN: %run_test hipify \"%s\" \"%t\" %hipify_args 1 --hip-kernel-execution-syntax %clang_args\n\n// CHECK: #include <hip/hip_runtime.h>\n#include <hip/hip_runtime.h>\n#include <algorithm>\n\n__global__ void my_kernel(int arg) {}\n\nvoid test_launches() {\n    struct { int x, y; } config = {1, 2};\n    dim3 grid_arr[2] = {dim3(1), dim3(2)};\n    int shared_mem = 0;\n\n    // CHECK: hipStream_t stream = 0;\n    hipStream_t stream = 0;\n\n    // 1. Array indexing\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(grid_arr[0]), dim3(grid_arr[1]), 0, 0, 0);\n    my_kernel<<<grid_arr[0], grid_arr[1]>>>(0);\n\n    // 2. Member access\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(config.x), dim3(config.y), 0, 0, 0);\n    my_kernel<<<dim3(config.x), dim3(config.y)>>>(0);\n\n    // 3. Math, Logical, and Ternary operators\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(config.x == 1 ? 2 : 1), dim3(256), 0, 0, 0);\n    my_kernel<<<config.x == 1 ? 2 : 1, 256>>>(0);\n\n    // 4. Address-of operator\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(1), dim3(256), shared_mem, &stream, 0);\n    my_kernel<<<1, 256, shared_mem, &stream>>>(0);\n\n    // 5. Std calls\n    // CHECK: hipLaunchKernelGGL(my_kernel, dim3(1), dim3(std::max(config.x, config.y)), shared_mem, 0, std::min(config.x, config.y));\n    my_kernel<<<1, std::max(config.x, config.y), shared_mem>>>(std::min(config.x, config.y));\n}\n", "source": "/kaggle/working/HIPIFY/tests/unit_tests/kernel_launch/complex_kernel_launches.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <chrono>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 4096  // Rows of A, Rows of C\n#define N 8192  // Columns of B, Columns of C\n#define K 1024  // Columns of A, Rows of B\n#define NGPUS 2 // Number of GPUs to use for computation\n\n// Kernel to perform matrix multiplication on a portion of the matrices\n__global__ void matMulKernelTP(int *A, int *B, int *C, int m, int n, int k, int col_start, int col_size) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int local_col = blockIdx.x * blockDim.x + threadIdx.x;\n    int col = col_start + local_col;\n    \n    if (row < m && local_col < col_size) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * col_size + local_col] = value;\n    }\n}\n\n// Host function for matrix multiplication\nvoid matMulHost(const int* A, const int* B, int* C, int m, int n, int k) {\n    for (int i = 0; i < m; i++) {\n        for (int j = 0; j < n; j++) {\n            int sum = 0;\n            for (int p = 0; p < k; p++) {\n                sum += A[i * k + p] * B[p * n + j];\n            }\n            C[i * n + j] = sum;\n        }\n    }\n}\n\nint main() {\n    auto total_start = std::chrono::high_resolution_clock::now();\n    // Host matrices\n    int *A = new int[M * K];\n    int *B = new int[K * N];\n    int *C = new int[M * N]();  // GPU result\n    int *C_host = new int[M * N]();  // CPU result\n\n    // Initialize matrices with sample values\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < K; j++) {\n            A[i * K + j] = (i + j) % 10;  // Simple pattern to avoid large numbers in multiplication\n        }\n    }\n    \n    for (int i = 0; i < K; i++) {\n        for (int j = 0; j < N; j++) {\n            B[i * N + j] = (i + j) % 10;\n        }\n    }\n\n    // Calculate split for tensor parallelism across N dimension\n    const int cols_per_gpu = N / NGPUS;\n    \n    // Arrays for each GPU\n    int *d_A[NGPUS], *d_B[NGPUS], *d_C[NGPUS];\n\n    // Create streams and events per GPU\n    cudaStream_t streams[NGPUS];\n    cudaEvent_t startEvents[NGPUS], stopEvents[NGPUS];\n\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        cudaCheckError(::cudaStreamCreate(&streams[gpu]));\n        cudaCheckError(::cudaEventCreate(&startEvents[gpu]));\n        cudaCheckError(::cudaEventCreate(&stopEvents[gpu]));\n    }\n\n    // Set up each GPU\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        \n        // Allocate memory on current GPU\n        cudaCheckError(::cudaMalloc(&d_A[gpu], M * K * sizeof(int)));\n        cudaCheckError(::cudaMalloc(&d_B[gpu], K * N * sizeof(int)));\n        cudaCheckError(::cudaMalloc(&d_C[gpu], M * cols_per_gpu * sizeof(int)));\n        \n        // Copy input matrices to current GPU\n        cudaCheckError(::cudaMemcpyAsync(d_A[gpu], A, M * K * sizeof(int), cudaMemcpyHostToDevice, streams[gpu]));\n        cudaCheckError(::cudaMemcpyAsync(d_B[gpu], B, K * N * sizeof(int), cudaMemcpyHostToDevice, streams[gpu]));\n        \n        // Set grid and block dimensions for this GPU's portion\n        const dim3 threadsPerBlock(16, 16);  // Using 16x16 thread blocks for better occupancy\n        const dim3 numBlocks(\n            (cols_per_gpu + threadsPerBlock.x - 1) / threadsPerBlock.x,\n            (M + threadsPerBlock.y - 1) / threadsPerBlock.y\n        );\n        \n        const int col_start = gpu * cols_per_gpu;\n\n        cudaCheckError(::cudaEventRecord(startEvents[gpu], streams[gpu]));\n\n        matMulKernelTP<<<numBlocks, threadsPerBlock, 0, streams[gpu]>>>(\n            d_A[gpu], d_B[gpu], d_C[gpu],\n            M, N, K, col_start, cols_per_gpu\n        );\n\n        // Record stop event for this GPU\n        cudaCheckError(::cudaEventRecord(stopEvents[gpu], streams[gpu]));\n    }\n    \n    // Copy results back from each GPU and combine\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        // Copy each row's portion separately to maintain correct layout\n        for (int row = 0; row < M; row++) {\n            cudaCheckError(::cudaMemcpyAsync(\n                &C[row * N + gpu * cols_per_gpu],    // Destination in host matrix\n                &d_C[gpu][row * cols_per_gpu],       // Source from GPU\n                cols_per_gpu * sizeof(int),          // Size of this GPU's portion\n                cudaMemcpyDeviceToHost,\n                streams[gpu]\n            ));\n        }\n        // Ensure all work in stream is completed before measuring timing.\n        cudaCheckError(::cudaStreamSynchronize(streams[gpu]));\n    }\n\n    // Elapsed time per GPU.\n    for (int gpu = 0; gpu < NGPUS; ++gpu) {\n        float gpu_time = 0;\n        cudaCheckError(::cudaEventElapsedTime(&gpu_time, startEvents[gpu], stopEvents[gpu]));\n        std::cout << \"GPU \" << gpu << \" execution time: \" << gpu_time << \"ms\" << std::endl;\n    }\n\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::cudaEventDestroy(startEvents[gpu]));\n        cudaCheckError(::cudaEventDestroy(stopEvents[gpu]));\n        cudaCheckError(::cudaStreamDestroy(streams[gpu]));\n    }\n\n    // Start CPU timing\n    auto cpu_start = std::chrono::high_resolution_clock::now();\n    \n    // Compute result on CPU for verification\n    std::cout << \"Computing on CPU for verification...\" << std::endl;\n    matMulHost(A, B, C_host, M, N, K);\n    \n    // Stop CPU timing\n    auto cpu_end = std::chrono::high_resolution_clock::now();\n    auto cpu_duration = std::chrono::duration_cast<std::chrono::milliseconds>(cpu_end - cpu_start);\n    \n    // Print timing results\n    std::cout << \"\\nTiming Results:\" << std::endl;\n    std::cout << \"CPU Time: \" << cpu_duration.count() << \" ms\" << std::endl;\n    \n    // Compare results\n    bool match = true;\n    int mismatch_count = 0;\n    for (int i = 0; i < M * N; i++) {\n        if (C[i] != C_host[i]) {\n            match = false;\n            mismatch_count++;\n        }\n    }\n    \n    // Output comparison results\n    std::cout << \"\\nResult comparison:\" << std::endl;\n    if (match) {\n        std::cout << \"GPU and CPU results match perfectly!\" << std::endl;\n    } else {\n        std::cout << \"Found \" << mismatch_count << \" mismatches between GPU and CPU results.\" << std::endl;\n    }\n    \n    // Function to print a 4x4 region of both matrices\n    auto print_region = [&](const char* region_name, int start_row, int start_col) {\n        std::cout << \"\\n\" << region_name << \" (4x4):\" << std::endl;\n        std::cout << \"GPU Result:\" << std::endl;\n        for (int i = 0; i < 4; i++) {\n            for (int j = 0; j < 4; j++) {\n                std::cout << C[(start_row + i) * N + (start_col + j)] << \" \";\n            }\n            std::cout << std::endl;\n        }\n        \n        std::cout << \"CPU Result:\" << std::endl;\n        for (int i = 0; i < 4; i++) {\n            for (int j = 0; j < 4; j++) {\n                std::cout << C_host[(start_row + i) * N + (start_col + j)] << \" \";\n            }\n            std::cout << std::endl;\n        }\n    };\n    \n    // Print different regions of the matrices\n    print_region(\"Top-left corner\", 0, 0);\n    print_region(\"Middle region\", M/2 - 2, N/2 - 2);\n    print_region(\"Bottom-right corner\", M - 4, N - 4);\n    \n    // Free device memory on each GPU\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        cudaCheckError(::cudaFree(d_A[gpu]));\n        cudaCheckError(::cudaFree(d_B[gpu]));\n        cudaCheckError(::cudaFree(d_C[gpu]));\n    }\n\n    // Free host memory\n    delete[] A;\n    delete[] B;\n    delete[] C;\n    delete[] C_host;\n    \n    auto total_end = std::chrono::high_resolution_clock::now();\n    auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(total_end - total_start);\n    \n    std::cout << \"Total Time: \" << cpu_duration.count() << \" ms\" << std::endl;\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <chrono>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 4096  // Rows of A, Rows of C\n#define N 8192  // Columns of B, Columns of C\n#define K 1024  // Columns of A, Rows of B\n#define NGPUS 2 // Number of GPUs to use for computation\n\n// Kernel to perform matrix multiplication on a portion of the matrices\n__global__ void matMulKernelTP(int *A, int *B, int *C, int m, int n, int k, int col_start, int col_size) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int local_col = blockIdx.x * blockDim.x + threadIdx.x;\n    int col = col_start + local_col;\n    \n    if (row < m && local_col < col_size) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * col_size + local_col] = value;\n    }\n}\n\n// Host function for matrix multiplication\nvoid matMulHost(const int* A, const int* B, int* C, int m, int n, int k) {\n    for (int i = 0; i < m; i++) {\n        for (int j = 0; j < n; j++) {\n            int sum = 0;\n            for (int p = 0; p < k; p++) {\n                sum += A[i * k + p] * B[p * n + j];\n            }\n            C[i * n + j] = sum;\n        }\n    }\n}\n\nint main() {\n    auto total_start = std::chrono::high_resolution_clock::now();\n    // Host matrices\n    int *A = new int[M * K];\n    int *B = new int[K * N];\n    int *C = new int[M * N]();  // GPU result\n    int *C_host = new int[M * N]();  // CPU result\n\n    // Initialize matrices with sample values\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < K; j++) {\n            A[i * K + j] = (i + j) % 10;  // Simple pattern to avoid large numbers in multiplication\n        }\n    }\n    \n    for (int i = 0; i < K; i++) {\n        for (int j = 0; j < N; j++) {\n            B[i * N + j] = (i + j) % 10;\n        }\n    }\n\n    // Calculate split for tensor parallelism across N dimension\n    const int cols_per_gpu = N / NGPUS;\n    \n    // Arrays for each GPU\n    int *d_A[NGPUS], *d_B[NGPUS], *d_C[NGPUS];\n\n    // Create streams and events per GPU\n    hipStream_t streams[NGPUS];\n    hipEvent_t startEvents[NGPUS], stopEvents[NGPUS];\n\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        cudaCheckError(::hipStreamCreate(&streams[gpu]));\n        cudaCheckError(::hipEventCreate(&startEvents[gpu]));\n        cudaCheckError(::hipEventCreate(&stopEvents[gpu]));\n    }\n\n    // Set up each GPU\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        \n        // Allocate memory on current GPU\n        cudaCheckError(::hipMalloc(&d_A[gpu], M * K * sizeof(int)));\n        cudaCheckError(::hipMalloc(&d_B[gpu], K * N * sizeof(int)));\n        cudaCheckError(::hipMalloc(&d_C[gpu], M * cols_per_gpu * sizeof(int)));\n        \n        // Copy input matrices to current GPU\n        cudaCheckError(::hipMemcpyAsync(d_A[gpu], A, M * K * sizeof(int), hipMemcpyHostToDevice, streams[gpu]));\n        cudaCheckError(::hipMemcpyAsync(d_B[gpu], B, K * N * sizeof(int), hipMemcpyHostToDevice, streams[gpu]));\n        \n        // Set grid and block dimensions for this GPU's portion\n        const dim3 threadsPerBlock(16, 16);  // Using 16x16 thread blocks for better occupancy\n        const dim3 numBlocks(\n            (cols_per_gpu + threadsPerBlock.x - 1) / threadsPerBlock.x,\n            (M + threadsPerBlock.y - 1) / threadsPerBlock.y\n        );\n        \n        const int col_start = gpu * cols_per_gpu;\n\n        cudaCheckError(::hipEventRecord(startEvents[gpu], streams[gpu]));\n\n        matMulKernelTP<<<numBlocks, threadsPerBlock, 0, streams[gpu]>>>(\n            d_A[gpu], d_B[gpu], d_C[gpu],\n            M, N, K, col_start, cols_per_gpu\n        );\n\n        // Record stop event for this GPU\n        cudaCheckError(::hipEventRecord(stopEvents[gpu], streams[gpu]));\n    }\n    \n    // Copy results back from each GPU and combine\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        // Copy each row's portion separately to maintain correct layout\n        for (int row = 0; row < M; row++) {\n            cudaCheckError(::hipMemcpyAsync(\n                &C[row * N + gpu * cols_per_gpu],    // Destination in host matrix\n                &d_C[gpu][row * cols_per_gpu],       // Source from GPU\n                cols_per_gpu * sizeof(int),          // Size of this GPU's portion\n                hipMemcpyDeviceToHost,\n                streams[gpu]\n            ));\n        }\n        // Ensure all work in stream is completed before measuring timing.\n        cudaCheckError(::hipStreamSynchronize(streams[gpu]));\n    }\n\n    // Elapsed time per GPU.\n    for (int gpu = 0; gpu < NGPUS; ++gpu) {\n        float gpu_time = 0;\n        cudaCheckError(::hipEventElapsedTime(&gpu_time, startEvents[gpu], stopEvents[gpu]));\n        std::cout << \"GPU \" << gpu << \" execution time: \" << gpu_time << \"ms\" << std::endl;\n    }\n\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::hipEventDestroy(startEvents[gpu]));\n        cudaCheckError(::hipEventDestroy(stopEvents[gpu]));\n        cudaCheckError(::hipStreamDestroy(streams[gpu]));\n    }\n\n    // Start CPU timing\n    auto cpu_start = std::chrono::high_resolution_clock::now();\n    \n    // Compute result on CPU for verification\n    std::cout << \"Computing on CPU for verification...\" << std::endl;\n    matMulHost(A, B, C_host, M, N, K);\n    \n    // Stop CPU timing\n    auto cpu_end = std::chrono::high_resolution_clock::now();\n    auto cpu_duration = std::chrono::duration_cast<std::chrono::milliseconds>(cpu_end - cpu_start);\n    \n    // Print timing results\n    std::cout << \"\\nTiming Results:\" << std::endl;\n    std::cout << \"CPU Time: \" << cpu_duration.count() << \" ms\" << std::endl;\n    \n    // Compare results\n    bool match = true;\n    int mismatch_count = 0;\n    for (int i = 0; i < M * N; i++) {\n        if (C[i] != C_host[i]) {\n            match = false;\n            mismatch_count++;\n        }\n    }\n    \n    // Output comparison results\n    std::cout << \"\\nResult comparison:\" << std::endl;\n    if (match) {\n        std::cout << \"GPU and CPU results match perfectly!\" << std::endl;\n    } else {\n        std::cout << \"Found \" << mismatch_count << \" mismatches between GPU and CPU results.\" << std::endl;\n    }\n    \n    // Function to print a 4x4 region of both matrices\n    auto print_region = [&](const char* region_name, int start_row, int start_col) {\n        std::cout << \"\\n\" << region_name << \" (4x4):\" << std::endl;\n        std::cout << \"GPU Result:\" << std::endl;\n        for (int i = 0; i < 4; i++) {\n            for (int j = 0; j < 4; j++) {\n                std::cout << C[(start_row + i) * N + (start_col + j)] << \" \";\n            }\n            std::cout << std::endl;\n        }\n        \n        std::cout << \"CPU Result:\" << std::endl;\n        for (int i = 0; i < 4; i++) {\n            for (int j = 0; j < 4; j++) {\n                std::cout << C_host[(start_row + i) * N + (start_col + j)] << \" \";\n            }\n            std::cout << std::endl;\n        }\n    };\n    \n    // Print different regions of the matrices\n    print_region(\"Top-left corner\", 0, 0);\n    print_region(\"Middle region\", M/2 - 2, N/2 - 2);\n    print_region(\"Bottom-right corner\", M - 4, N - 4);\n    \n    // Free device memory on each GPU\n    for (int gpu = 0; gpu < NGPUS; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        cudaCheckError(::hipFree(d_A[gpu]));\n        cudaCheckError(::hipFree(d_B[gpu]));\n        cudaCheckError(::hipFree(d_C[gpu]));\n    }\n\n    // Free host memory\n    delete[] A;\n    delete[] B;\n    delete[] C;\n    delete[] C_host;\n    \n    auto total_end = std::chrono::high_resolution_clock::now();\n    auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(total_end - total_start);\n    \n    std::cout << \"Total Time: \" << cpu_duration.count() << \" ms\" << std::endl;\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/TensorParallelFromScratch/02_matmul_tp/matmul_tp_big.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 2  // Rows of A, Rows of C\n#define N 2  // Columns of B, Columns of C\n#define K 3  // Columns of A, Rows of B\n\n// Kernel to perform matrix multiplication on a portion of the matrices\n__global__ void matMulKernelTP(int *A, int *B, int *C, int m, int n, int k, int col_start, int col_size) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int local_col = blockIdx.x * blockDim.x + threadIdx.x;\n    int col = col_start + local_col;\n    \n    if (row < m && local_col < col_size) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * col_size + local_col] = value;\n    }\n}\n\nint main() {\n    // Host matrices\n    int A[M][K] = { {1, 2, 3}, {4, 5, 6} };\n    int B[K][N] = { {100, 200}, {300, 400}, {500, 600} };\n    int C[M][N] = {0};\n\n    // Calculate split for tensor parallelism across N dimension\n    int cols_per_gpu = N / 2;\n    \n    // Arrays for each GPU\n    int *d_A[2], *d_B[2], *d_C[2];\n    \n    // Set up each GPU\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        \n        // Allocate memory on current GPU\n        cudaCheckError(::cudaMalloc(&d_A[gpu], M * K * sizeof(int)));\n        cudaCheckError(::cudaMalloc(&d_B[gpu], K * N * sizeof(int)));\n        cudaCheckError(::cudaMalloc(&d_C[gpu], M * cols_per_gpu * sizeof(int)));\n        \n        // Copy input matrices to current GPU\n        cudaCheckError(::cudaMemcpy(d_A[gpu], A, M * K * sizeof(int), cudaMemcpyHostToDevice));\n        cudaCheckError(::cudaMemcpy(d_B[gpu], B, K * N * sizeof(int), cudaMemcpyHostToDevice));\n        \n        // Set grid and block dimensions for this GPU's portion\n        dim3 threadsPerBlock(cols_per_gpu, M);\n        dim3 numBlocks(\n            (cols_per_gpu + threadsPerBlock.x - 1) / threadsPerBlock.x,\n            (M + threadsPerBlock.y - 1) / threadsPerBlock.y\n        );\n        \n        // Launch kernel for this GPU's portion\n        int col_start = gpu * cols_per_gpu;\n        matMulKernelTP<<<numBlocks, threadsPerBlock>>>(\n            d_A[gpu], d_B[gpu], d_C[gpu],\n            M, N, K, col_start, cols_per_gpu\n        );\n    }\n    \n    // Copy results back from each GPU and combine\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        // Copy each row's portion separately to maintain correct layout\n        for (int row = 0; row < M; row++) {\n            cudaCheckError(::cudaMemcpy(\n                &C[row][gpu * cols_per_gpu],         // Destination in host matrix\n                &d_C[gpu][row * cols_per_gpu],       // Source from GPU\n                cols_per_gpu * sizeof(int),          // Size of this GPU's portion\n                cudaMemcpyDeviceToHost\n            ));\n        }\n    }\n    \n    // Output the result matrix\n    std::cout << \"Result Matrix C:\" << std::endl;\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            std::cout << C[i][j] << \" \";\n        }\n        std::cout << std::endl;\n    }\n    \n    // Free device memory on each GPU\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::cudaSetDevice(gpu));\n        cudaCheckError(::cudaFree(d_A[gpu]));\n        cudaCheckError(::cudaFree(d_B[gpu]));\n        cudaCheckError(::cudaFree(d_C[gpu]));\n    }\n    \n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 2  // Rows of A, Rows of C\n#define N 2  // Columns of B, Columns of C\n#define K 3  // Columns of A, Rows of B\n\n// Kernel to perform matrix multiplication on a portion of the matrices\n__global__ void matMulKernelTP(int *A, int *B, int *C, int m, int n, int k, int col_start, int col_size) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int local_col = blockIdx.x * blockDim.x + threadIdx.x;\n    int col = col_start + local_col;\n    \n    if (row < m && local_col < col_size) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * col_size + local_col] = value;\n    }\n}\n\nint main() {\n    // Host matrices\n    int A[M][K] = { {1, 2, 3}, {4, 5, 6} };\n    int B[K][N] = { {100, 200}, {300, 400}, {500, 600} };\n    int C[M][N] = {0};\n\n    // Calculate split for tensor parallelism across N dimension\n    int cols_per_gpu = N / 2;\n    \n    // Arrays for each GPU\n    int *d_A[2], *d_B[2], *d_C[2];\n    \n    // Set up each GPU\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        \n        // Allocate memory on current GPU\n        cudaCheckError(::hipMalloc(&d_A[gpu], M * K * sizeof(int)));\n        cudaCheckError(::hipMalloc(&d_B[gpu], K * N * sizeof(int)));\n        cudaCheckError(::hipMalloc(&d_C[gpu], M * cols_per_gpu * sizeof(int)));\n        \n        // Copy input matrices to current GPU\n        cudaCheckError(::hipMemcpy(d_A[gpu], A, M * K * sizeof(int), hipMemcpyHostToDevice));\n        cudaCheckError(::hipMemcpy(d_B[gpu], B, K * N * sizeof(int), hipMemcpyHostToDevice));\n        \n        // Set grid and block dimensions for this GPU's portion\n        dim3 threadsPerBlock(cols_per_gpu, M);\n        dim3 numBlocks(\n            (cols_per_gpu + threadsPerBlock.x - 1) / threadsPerBlock.x,\n            (M + threadsPerBlock.y - 1) / threadsPerBlock.y\n        );\n        \n        // Launch kernel for this GPU's portion\n        int col_start = gpu * cols_per_gpu;\n        matMulKernelTP<<<numBlocks, threadsPerBlock>>>(\n            d_A[gpu], d_B[gpu], d_C[gpu],\n            M, N, K, col_start, cols_per_gpu\n        );\n    }\n    \n    // Copy results back from each GPU and combine\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        // Copy each row's portion separately to maintain correct layout\n        for (int row = 0; row < M; row++) {\n            cudaCheckError(::hipMemcpy(\n                &C[row][gpu * cols_per_gpu],         // Destination in host matrix\n                &d_C[gpu][row * cols_per_gpu],       // Source from GPU\n                cols_per_gpu * sizeof(int),          // Size of this GPU's portion\n                hipMemcpyDeviceToHost\n            ));\n        }\n    }\n    \n    // Output the result matrix\n    std::cout << \"Result Matrix C:\" << std::endl;\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            std::cout << C[i][j] << \" \";\n        }\n        std::cout << std::endl;\n    }\n    \n    // Free device memory on each GPU\n    for (int gpu = 0; gpu < 2; gpu++) {\n        cudaCheckError(::hipSetDevice(gpu));\n        cudaCheckError(::hipFree(d_A[gpu]));\n        cudaCheckError(::hipFree(d_B[gpu]));\n        cudaCheckError(::hipFree(d_C[gpu]));\n    }\n    \n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/TensorParallelFromScratch/02_matmul_tp/matmul_tp_small.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 2  // Rows of A, Rows of C\n#define N 2  // Columns of B, Columns of C\n#define K 3  // Columns of A, Rows of B\n\n// Kernel to perform our simple matrix multiplication\n__global__ void matMulKernel(int *A, int *B, int *C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    printf(\"row: %d, col: %d\\n\", row, col);\n    if (row < m && col < n) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * n + col] = value;\n    }\n}\n\nint main() {\n    // Host matrices\n    int A[M][K] = { {1, 2, 3}, {4, 5, 6} };\n    int B[K][N] = { {100, 200}, {300, 400}, {500, 600} };\n    int C[N][M] = {0};\n\n    int *d_A, *d_B, *d_C;\n\n    // Allocate device memory\n    cudaCheckError(::cudaMalloc(&d_A, M * K * sizeof(int)));\n    cudaCheckError(::cudaMalloc(&d_B, K * N * sizeof(int)));\n    cudaCheckError(::cudaMalloc(&d_C, M * N * sizeof(int)));\n\n    // Copy matrices from host to device\n    cudaCheckError(::cudaMemcpy(d_A, A, M * K * sizeof(int), cudaMemcpyHostToDevice));\n    cudaCheckError(::cudaMemcpy(d_B, B, K * N * sizeof(int), cudaMemcpyHostToDevice));\n\n    // Set our grid and block size\n    dim3 threadsPerBlock(M, N);\n    dim3 numBlocks((N + threadsPerBlock.x - 1) / threadsPerBlock.x, (M + threadsPerBlock.y - 1) / threadsPerBlock.y);\n\n    // Launch the kernel\n    matMulKernel<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, M, N, K);\n\n    // Copy result back to host\n    cudaCheckError(::cudaMemcpy(C, d_C, M * N * sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Output the result matrix\n    std::cout << \"Result Matrix C:\" << std::endl;\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            std::cout << C[i][j] << \" \";\n        }\n        std::cout << std::endl;\n    }\n\n    // Free device memory\n    cudaCheckError(::cudaFree(d_A));\n    cudaCheckError(::cudaFree(d_B));\n    cudaCheckError(::cudaFree(d_C));\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#include \"../../utils/utils.cuh\"\n\n#define M 2  // Rows of A, Rows of C\n#define N 2  // Columns of B, Columns of C\n#define K 3  // Columns of A, Rows of B\n\n// Kernel to perform our simple matrix multiplication\n__global__ void matMulKernel(int *A, int *B, int *C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    printf(\"row: %d, col: %d\\n\", row, col);\n    if (row < m && col < n) {\n        int value = 0;\n        for (int i = 0; i < k; i++) {\n            value += A[row * k + i] * B[i * n + col];\n        }\n        C[row * n + col] = value;\n    }\n}\n\nint main() {\n    // Host matrices\n    int A[M][K] = { {1, 2, 3}, {4, 5, 6} };\n    int B[K][N] = { {100, 200}, {300, 400}, {500, 600} };\n    int C[N][M] = {0};\n\n    int *d_A, *d_B, *d_C;\n\n    // Allocate device memory\n    cudaCheckError(::hipMalloc(&d_A, M * K * sizeof(int)));\n    cudaCheckError(::hipMalloc(&d_B, K * N * sizeof(int)));\n    cudaCheckError(::hipMalloc(&d_C, M * N * sizeof(int)));\n\n    // Copy matrices from host to device\n    cudaCheckError(::hipMemcpy(d_A, A, M * K * sizeof(int), hipMemcpyHostToDevice));\n    cudaCheckError(::hipMemcpy(d_B, B, K * N * sizeof(int), hipMemcpyHostToDevice));\n\n    // Set our grid and block size\n    dim3 threadsPerBlock(M, N);\n    dim3 numBlocks((N + threadsPerBlock.x - 1) / threadsPerBlock.x, (M + threadsPerBlock.y - 1) / threadsPerBlock.y);\n\n    // Launch the kernel\n    matMulKernel<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, M, N, K);\n\n    // Copy result back to host\n    cudaCheckError(::hipMemcpy(C, d_C, M * N * sizeof(int), hipMemcpyDeviceToHost));\n\n    // Output the result matrix\n    std::cout << \"Result Matrix C:\" << std::endl;\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            std::cout << C[i][j] << \" \";\n        }\n        std::cout << std::endl;\n    }\n\n    // Free device memory\n    cudaCheckError(::hipFree(d_A));\n    cudaCheckError(::hipFree(d_B));\n    cudaCheckError(::hipFree(d_C));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/TensorParallelFromScratch/01_simple_matmul_no_tp/matmul.cu"}
{"input": "#include <cstdio>\n#include <cuda_runtime.h>\n#include <nvToolsExt.h>\n#include \"../../utils/utils.cuh\"\n\n#define W 1024\n\n__global__ void matMul(float* out, const float* in1, const float* in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    float sum = 0.0f;\n    if (row < width && col < width) {\n        for (int i = 0; i < width; ++i) {\n            sum += in1[row * width + i] * in2[i * width + col];\n        }\n        out[row * width + col] = sum;\n    }\n}\n\nvoid doMatMul(float* out, const float* in1, const float* in2, int width) {\n    float *dIn1, *dIn2, *dOut;\n    const size_t size = width * width * sizeof(float);\n\n    printf(\"Allocating device memory...\\n\");\n    cudaCheckError(::cudaMalloc(&dIn1, size));\n    cudaCheckError(::cudaMalloc(&dIn2, size));\n    cudaCheckError(::cudaMalloc(&dOut, size));\n\n    printf(\"Copying data to device...\\n\");\n    cudaCheckError(::cudaMemcpy(dIn1, in1, size, cudaMemcpyHostToDevice));\n    cudaCheckError(::cudaMemcpy(dIn2, in2, size, cudaMemcpyHostToDevice));\n\n    dim3 threadsPerBlock(16, 16);\n    dim3 blocksPerGrid((width + threadsPerBlock.x - 1) / threadsPerBlock.x,\n                        (width + threadsPerBlock.y - 1) / threadsPerBlock.y);\n    printf(\"Launching kernel...\\n\");\n    for (int j = 0; j < 100; ++j)\n    {\n        nvtxRangePush(\"Matrix multiplication kernel\");\n        matMul<<<blocksPerGrid, threadsPerBlock>>>(dOut, dIn1, dIn2, width);\n        nvtxRangePop();\n    }\n    cudaCheckError(::cudaDeviceSynchronize());\n\n    printf(\"Copying result to host...\\n\");\n    cudaCheckError(::cudaMemcpy(out, dOut, size, cudaMemcpyHostToDevice));\n\n    printf(\"Freeing device memory...\\n\");\n    cudaCheckError(::cudaFree(dIn1));\n    cudaCheckError(::cudaFree(dIn2));\n    cudaCheckError(::cudaFree(dOut));\n}\n\nint main() {\n    float *in1, *in2, *out;\n    const size_t width = W * W * sizeof(float);\n    cudaCheckError(::cudaMallocHost(&in1, width));\n    cudaCheckError(::cudaMallocHost(&in2, width));\n    cudaCheckError(::cudaMallocHost(&out, width));\n    for (int i = 0; i < W * W; ++i) {\n        in1[i] = static_cast<float>(rand()) / RAND_MAX;\n        in2[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n    printf(\"Starting matrix multiplication...\\n\");\n    doMatMul(out, in1, in2, width);\n    printf(\"Matrix multiplication completed.\\n\");\n    printf(\"Freeing host memory...\\n\");\n    cudaCheckError(::cudaFreeHost(in1));\n    cudaCheckError(::cudaFreeHost(in2));\n    cudaCheckError(::cudaFreeHost(out));\n    return 0;\n}\n", "output": "#include <cstdio>\n#include <hip/hip_runtime.h>\n#include <nvToolsExt.h>\n#include \"../../utils/utils.cuh\"\n\n#define W 1024\n\n__global__ void matMul(float* out, const float* in1, const float* in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    float sum = 0.0f;\n    if (row < width && col < width) {\n        for (int i = 0; i < width; ++i) {\n            sum += in1[row * width + i] * in2[i * width + col];\n        }\n        out[row * width + col] = sum;\n    }\n}\n\nvoid doMatMul(float* out, const float* in1, const float* in2, int width) {\n    float *dIn1, *dIn2, *dOut;\n    const size_t size = width * width * sizeof(float);\n\n    printf(\"Allocating device memory...\\n\");\n    cudaCheckError(::hipMalloc(&dIn1, size));\n    cudaCheckError(::hipMalloc(&dIn2, size));\n    cudaCheckError(::hipMalloc(&dOut, size));\n\n    printf(\"Copying data to device...\\n\");\n    cudaCheckError(::hipMemcpy(dIn1, in1, size, hipMemcpyHostToDevice));\n    cudaCheckError(::hipMemcpy(dIn2, in2, size, hipMemcpyHostToDevice));\n\n    dim3 threadsPerBlock(16, 16);\n    dim3 blocksPerGrid((width + threadsPerBlock.x - 1) / threadsPerBlock.x,\n                        (width + threadsPerBlock.y - 1) / threadsPerBlock.y);\n    printf(\"Launching kernel...\\n\");\n    for (int j = 0; j < 100; ++j)\n    {\n        nvtxRangePush(\"Matrix multiplication kernel\");\n        matMul<<<blocksPerGrid, threadsPerBlock>>>(dOut, dIn1, dIn2, width);\n        nvtxRangePop();\n    }\n    cudaCheckError(::hipDeviceSynchronize());\n\n    printf(\"Copying result to host...\\n\");\n    cudaCheckError(::hipMemcpy(out, dOut, size, hipMemcpyHostToDevice));\n\n    printf(\"Freeing device memory...\\n\");\n    cudaCheckError(::hipFree(dIn1));\n    cudaCheckError(::hipFree(dIn2));\n    cudaCheckError(::hipFree(dOut));\n}\n\nint main() {\n    float *in1, *in2, *out;\n    const size_t width = W * W * sizeof(float);\n    cudaCheckError(::hipHostMalloc(&in1, width));\n    cudaCheckError(::hipHostMalloc(&in2, width));\n    cudaCheckError(::hipHostMalloc(&out, width));\n    for (int i = 0; i < W * W; ++i) {\n        in1[i] = static_cast<float>(rand()) / RAND_MAX;\n        in2[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n    printf(\"Starting matrix multiplication...\\n\");\n    doMatMul(out, in1, in2, width);\n    printf(\"Matrix multiplication completed.\\n\");\n    printf(\"Freeing host memory...\\n\");\n    cudaCheckError(::hipHostFree(in1));\n    cudaCheckError(::hipHostFree(in2));\n    cudaCheckError(::hipHostFree(out));\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/ProfilingExamples/nvtx/nvtx_example.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n#define N (1 << 20)  // 1M elements\n#define THREADS 1024  // Threads per block\n#define BLOCKS (N + THREADS - 1) / THREADS  // Number of blocks\n\n// Naive Reduction (with bank conflicts)\n__global__ void reduce_naive(float *input, float *output) {\n    __shared__ float shared_mem[THREADS];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load global memory into shared memory\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    // Reduction in shared memory\n    for (int stride = 1; stride < blockDim.x; stride *= 2) {\n        if (tid % (2 * stride) == 0) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// Optimized Reduction (Avoiding Bank Conflicts)\n__global__ void reduce_optimized(float *input, float *output) {\n    __shared__ float shared_mem[THREADS + (THREADS / 32)];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (coalesced)\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// CPU Reduction (Baseline for Benchmark)\nfloat reduce_cpu(float *input, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        sum += input[i];\n    }\n    return sum;\n}\n\n// Benchmarking Function\nvoid benchmark() {\n    float *h_input, *h_output;\n    float *d_input, *d_output;\n    cudaCheckError(::cudaMallocHost(&h_input, N * sizeof(float)));\n    cudaCheckError(::cudaMallocHost(&h_output, BLOCKS * sizeof(float)));\n    cudaCheckError(::cudaMalloc(&d_input, N * sizeof(float)));\n    cudaCheckError(::cudaMalloc(&d_output, BLOCKS * sizeof(float)));\n\n    // Initialize input\n    for (int i = 0; i < N; i++) {\n        h_input[i] = 1.0f;  // Simple case where sum = N\n    }\n\n    cudaCheckError(::cudaMemcpy(d_input, h_input, N * sizeof(float), cudaMemcpyHostToDevice));\n\n    cudaEvent_t start, stop;\n    cudaCheckError(::cudaEventCreate((&start)));\n    cudaCheckError(::cudaEventCreate(&stop));\n    // Benchmark Na\u00efve Kernel\n    cudaCheckError(::cudaEventRecord(start));\n    reduce_naive<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::cudaEventRecord(stop));\n    cudaCheckError(::cudaEventSynchronize(stop));\n    float time_naive;\n    cudaCheckError(::cudaEventElapsedTime(&time_naive, start, stop));\n    printf(\"Na\u00efve Kernel Time: %f ms\\n\", time_naive);\n    cudaCheckError(::cudaMemcpy(h_output, d_output, BLOCKS * sizeof(float), cudaMemcpyDeviceToHost));\n\n    // Final sum on CPU\n    float sum_naive = reduce_cpu(h_output, BLOCKS);\n\n    // Benchmark Optimized Kernel\n    cudaCheckError(::cudaEventRecord(start));\n    reduce_optimized<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::cudaEventRecord(stop));\n    cudaCheckError(::cudaEventSynchronize(stop));\n    float time_optimized;\n    cudaCheckError(::cudaEventElapsedTime(&time_optimized, start, stop));\n    printf(\"Optimized Kernel Time: %f ms\\n\", time_optimized);\n    cudaCheckError(::cudaMemcpy(h_output, d_output, BLOCKS * sizeof(float), cudaMemcpyDeviceToHost));\n    \n    float sum_optimized = reduce_cpu(h_output, BLOCKS);\n\n    // CPU Baseline\n    float sum_cpu = reduce_cpu(h_input, N);\n\n    printf(\"CPU Sum: %f\\n\", sum_cpu);\n    printf(\"Na\u00efve GPU Sum: %f\\n\", sum_naive);\n    printf(\"Optimized GPU Sum: %f\\n\", sum_optimized);\n\n    // Cleanup\n    cudaCheckError(::cudaFreeHost(h_input));\n    cudaCheckError(::cudaFreeHost(h_output));\n    cudaCheckError(::cudaFree(d_input));\n    cudaCheckError(::cudaFree(d_output));\n}\n\nint main() {\n    benchmark();\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n#define N (1 << 20)  // 1M elements\n#define THREADS 1024  // Threads per block\n#define BLOCKS (N + THREADS - 1) / THREADS  // Number of blocks\n\n// Naive Reduction (with bank conflicts)\n__global__ void reduce_naive(float *input, float *output) {\n    __shared__ float shared_mem[THREADS];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load global memory into shared memory\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    // Reduction in shared memory\n    for (int stride = 1; stride < blockDim.x; stride *= 2) {\n        if (tid % (2 * stride) == 0) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// Optimized Reduction (Avoiding Bank Conflicts)\n__global__ void reduce_optimized(float *input, float *output) {\n    __shared__ float shared_mem[THREADS + (THREADS / 32)];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (coalesced)\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// CPU Reduction (Baseline for Benchmark)\nfloat reduce_cpu(float *input, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        sum += input[i];\n    }\n    return sum;\n}\n\n// Benchmarking Function\nvoid benchmark() {\n    float *h_input, *h_output;\n    float *d_input, *d_output;\n    cudaCheckError(::hipHostMalloc(&h_input, N * sizeof(float)));\n    cudaCheckError(::hipHostMalloc(&h_output, BLOCKS * sizeof(float)));\n    cudaCheckError(::hipMalloc(&d_input, N * sizeof(float)));\n    cudaCheckError(::hipMalloc(&d_output, BLOCKS * sizeof(float)));\n\n    // Initialize input\n    for (int i = 0; i < N; i++) {\n        h_input[i] = 1.0f;  // Simple case where sum = N\n    }\n\n    cudaCheckError(::hipMemcpy(d_input, h_input, N * sizeof(float), hipMemcpyHostToDevice));\n\n    hipEvent_t start, stop;\n    cudaCheckError(::hipEventCreate((&start)));\n    cudaCheckError(::hipEventCreate(&stop));\n    // Benchmark Na\u00efve Kernel\n    cudaCheckError(::hipEventRecord(start));\n    reduce_naive<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::hipEventRecord(stop));\n    cudaCheckError(::hipEventSynchronize(stop));\n    float time_naive;\n    cudaCheckError(::hipEventElapsedTime(&time_naive, start, stop));\n    printf(\"Na\u00efve Kernel Time: %f ms\\n\", time_naive);\n    cudaCheckError(::hipMemcpy(h_output, d_output, BLOCKS * sizeof(float), hipMemcpyDeviceToHost));\n\n    // Final sum on CPU\n    float sum_naive = reduce_cpu(h_output, BLOCKS);\n\n    // Benchmark Optimized Kernel\n    cudaCheckError(::hipEventRecord(start));\n    reduce_optimized<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::hipEventRecord(stop));\n    cudaCheckError(::hipEventSynchronize(stop));\n    float time_optimized;\n    cudaCheckError(::hipEventElapsedTime(&time_optimized, start, stop));\n    printf(\"Optimized Kernel Time: %f ms\\n\", time_optimized);\n    cudaCheckError(::hipMemcpy(h_output, d_output, BLOCKS * sizeof(float), hipMemcpyDeviceToHost));\n    \n    float sum_optimized = reduce_cpu(h_output, BLOCKS);\n\n    // CPU Baseline\n    float sum_cpu = reduce_cpu(h_input, N);\n\n    printf(\"CPU Sum: %f\\n\", sum_cpu);\n    printf(\"Na\u00efve GPU Sum: %f\\n\", sum_naive);\n    printf(\"Optimized GPU Sum: %f\\n\", sum_optimized);\n\n    // Cleanup\n    cudaCheckError(::hipHostFree(h_input));\n    cudaCheckError(::hipHostFree(h_output));\n    cudaCheckError(::hipFree(d_input));\n    cudaCheckError(::hipFree(d_output));\n}\n\nint main() {\n    benchmark();\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/ProfilingExamples/memory_bank_conflict/bank_conflict.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n#define N (1 << 20)  // 1M elements\n#define THREADS 1024  // Threads per block\n#define BLOCKS (N + THREADS - 1) / THREADS  // Number of blocks\n\n__global__ void reduce_global(float *input, float *output) {\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x * 2 + tid; // Unroll by 2 for better memory efficiency\n\n    float sum = 0.0f;\n    if (global_idx < N) sum += input[global_idx];\n    if (global_idx + blockDim.x < N) sum += input[global_idx + blockDim.x];\n\n    // In-register reduction (warp-level)\n    for (int stride = warpSize / 2; stride > 0; stride >>= 1) {\n        sum += __shfl_down_sync(0xFFFFFFFF, sum, stride);\n    }\n\n    // Store only the first thread's result per warp\n    if (tid % warpSize == 0) {\n        atomicAdd(&output[blockIdx.x], sum);\n    }\n}\n\n\n\n// Optimized Reduction with Shared Memory\n__global__ void reduce_shared(float *input, float *output) {\n    __shared__ float shared_mem[THREADS + (THREADS / 32)];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (coalesced)\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// CPU Reduction (Baseline for Benchmark)\nfloat reduce_cpu(float *input, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        sum += input[i];\n    }\n    return sum;\n}\n\n// Benchmarking Function\nvoid benchmark() {\n    float *h_input, *h_output;\n    float *d_input, *d_output;\n    cudaCheckError(::cudaMallocHost(&h_input, N * sizeof(float)));\n    cudaCheckError(::cudaMallocHost(&h_output, BLOCKS * sizeof(float)));\n    cudaCheckError(::cudaMalloc(&d_input, N * sizeof(float)));\n    cudaCheckError(::cudaMalloc(&d_output, BLOCKS * sizeof(float)));\n\n    // Initialize input\n    for (int i = 0; i < N; i++) {\n        h_input[i] = 1.0f;  // Simple case where sum = N\n    }\n\n    cudaCheckError(::cudaMemcpy(d_input, h_input, N * sizeof(float), cudaMemcpyHostToDevice));\n\n    cudaEvent_t start, stop;\n    cudaCheckError(::cudaEventCreate((&start)));\n    cudaCheckError(::cudaEventCreate(&stop));\n    // Benchmark Global Kernel\n    cudaCheckError(::cudaEventRecord(start));\n    reduce_global<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::cudaEventRecord(stop));\n    cudaCheckError(::cudaEventSynchronize(stop));\n    float time_naive;\n    cudaCheckError(::cudaEventElapsedTime(&time_naive, start, stop));\n    printf(\"Global Kernel Time: %f ms\\n\", time_naive);\n    cudaCheckError(::cudaMemcpy(h_output, d_output, BLOCKS * sizeof(float), cudaMemcpyDeviceToHost));\n\n    // Final sum on CPU\n    float sum_naive = reduce_cpu(h_output, BLOCKS);\n\n    // Benchmark Optimized Kernel\n    cudaCheckError(::cudaEventRecord(start));\n    reduce_shared<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::cudaEventRecord(stop));\n    cudaCheckError(::cudaEventSynchronize(stop));\n    float time_optimized;\n    cudaCheckError(::cudaEventElapsedTime(&time_optimized, start, stop));\n    printf(\"Shmem Kernel Time: %f ms\\n\", time_optimized);\n    cudaCheckError(::cudaMemcpy(h_output, d_output, BLOCKS * sizeof(float), cudaMemcpyDeviceToHost));\n    \n    float sum_optimized = reduce_cpu(h_output, BLOCKS);\n\n    // CPU Baseline\n    float sum_cpu = reduce_cpu(h_input, N);\n\n    printf(\"CPU Sum: %f\\n\", sum_cpu);\n    printf(\"GPU Global memory Sum: %f\\n\", sum_naive);\n    printf(\"GPU Shared memory Sum: %f\\n\", sum_optimized);\n\n    // Cleanup\n    cudaCheckError(::cudaFreeHost(h_input));\n    cudaCheckError(::cudaFreeHost(h_output));\n    cudaCheckError(::cudaFree(d_input));\n    cudaCheckError(::cudaFree(d_output));\n}\n\nint main() {\n    benchmark();\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n#define N (1 << 20)  // 1M elements\n#define THREADS 1024  // Threads per block\n#define BLOCKS (N + THREADS - 1) / THREADS  // Number of blocks\n\n__global__ void reduce_global(float *input, float *output) {\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x * 2 + tid; // Unroll by 2 for better memory efficiency\n\n    float sum = 0.0f;\n    if (global_idx < N) sum += input[global_idx];\n    if (global_idx + blockDim.x < N) sum += input[global_idx + blockDim.x];\n\n    // In-register reduction (warp-level)\n    for (int stride = warpSize / 2; stride > 0; stride >>= 1) {\n        sum += __shfl_down_sync(0xFFFFFFFF, sum, stride);\n    }\n\n    // Store only the first thread's result per warp\n    if (tid % warpSize == 0) {\n        atomicAdd(&output[blockIdx.x], sum);\n    }\n}\n\n\n\n// Optimized Reduction with Shared Memory\n__global__ void reduce_shared(float *input, float *output) {\n    __shared__ float shared_mem[THREADS + (THREADS / 32)];\n\n    int tid = threadIdx.x;\n    int global_idx = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (coalesced)\n    shared_mem[tid] = (global_idx < N) ? input[global_idx] : 0.0f;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride) {\n            shared_mem[tid] += shared_mem[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Write block sum to output\n    if (tid == 0) output[blockIdx.x] = shared_mem[0];\n}\n\n// CPU Reduction (Baseline for Benchmark)\nfloat reduce_cpu(float *input, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        sum += input[i];\n    }\n    return sum;\n}\n\n// Benchmarking Function\nvoid benchmark() {\n    float *h_input, *h_output;\n    float *d_input, *d_output;\n    cudaCheckError(::hipHostMalloc(&h_input, N * sizeof(float)));\n    cudaCheckError(::hipHostMalloc(&h_output, BLOCKS * sizeof(float)));\n    cudaCheckError(::hipMalloc(&d_input, N * sizeof(float)));\n    cudaCheckError(::hipMalloc(&d_output, BLOCKS * sizeof(float)));\n\n    // Initialize input\n    for (int i = 0; i < N; i++) {\n        h_input[i] = 1.0f;  // Simple case where sum = N\n    }\n\n    cudaCheckError(::hipMemcpy(d_input, h_input, N * sizeof(float), hipMemcpyHostToDevice));\n\n    hipEvent_t start, stop;\n    cudaCheckError(::hipEventCreate((&start)));\n    cudaCheckError(::hipEventCreate(&stop));\n    // Benchmark Global Kernel\n    cudaCheckError(::hipEventRecord(start));\n    reduce_global<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::hipEventRecord(stop));\n    cudaCheckError(::hipEventSynchronize(stop));\n    float time_naive;\n    cudaCheckError(::hipEventElapsedTime(&time_naive, start, stop));\n    printf(\"Global Kernel Time: %f ms\\n\", time_naive);\n    cudaCheckError(::hipMemcpy(h_output, d_output, BLOCKS * sizeof(float), hipMemcpyDeviceToHost));\n\n    // Final sum on CPU\n    float sum_naive = reduce_cpu(h_output, BLOCKS);\n\n    // Benchmark Optimized Kernel\n    cudaCheckError(::hipEventRecord(start));\n    reduce_shared<<<BLOCKS, THREADS>>>(d_input, d_output);\n    cudaCheckError(::hipEventRecord(stop));\n    cudaCheckError(::hipEventSynchronize(stop));\n    float time_optimized;\n    cudaCheckError(::hipEventElapsedTime(&time_optimized, start, stop));\n    printf(\"Shmem Kernel Time: %f ms\\n\", time_optimized);\n    cudaCheckError(::hipMemcpy(h_output, d_output, BLOCKS * sizeof(float), hipMemcpyDeviceToHost));\n    \n    float sum_optimized = reduce_cpu(h_output, BLOCKS);\n\n    // CPU Baseline\n    float sum_cpu = reduce_cpu(h_input, N);\n\n    printf(\"CPU Sum: %f\\n\", sum_cpu);\n    printf(\"GPU Global memory Sum: %f\\n\", sum_naive);\n    printf(\"GPU Shared memory Sum: %f\\n\", sum_optimized);\n\n    // Cleanup\n    cudaCheckError(::hipHostFree(h_input));\n    cudaCheckError(::hipHostFree(h_output));\n    cudaCheckError(::hipFree(d_input));\n    cudaCheckError(::hipFree(d_output));\n}\n\nint main() {\n    benchmark();\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/ProfilingExamples/global_vs_shared_mem/global_vs_shared.cu"}
{"input": "#include <cassert>\n#include <iostream>\n#include <cuda_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n// Simple matmul kernel.\n__global__ void matMul(float *out, const float *in1, const float *in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    float sum = 0.0f;\n    if (row < width && col < width) {\n        for (int i = 0; i < width; ++i) {\n            sum += in1[row * width + i] * in2[i * width + col];\n        }\n        out[row * width + col] = sum;\n    }\n}\n\n// Add one kernel to emphasize serial execution within a stream.\n__global__ void addOne(float *out, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        int idx = row * width + col;\n        out[idx] += 1.0f;\n    }\n}\n\n// Minus one kernel to emphasize serial execution within a stream.\n__global__ void minusOne(float *out, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        int idx = row * width + col;\n        out[idx] -= 1.0f;\n    }\n}\n\n// Add matrices to a final result for comparison.\n__global__ void matAdd(float *out, const float *in1, const float *in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        out[row * width + col] = in1[row * width + col] + in2[row * width + col];\n    }\n}\n\n// Perform a matmul on separate streams for a million elements, then add them.\nint main() {\n    // Set dimensionality.\n    const int width = 1024;\n    const int size = width * width * sizeof(float);\n\n    // Create separate streams to run matmuls asynchronously.\n    cudaStream_t stream1, stream2;\n    cudaCheckError(::cudaStreamCreate(&stream1));\n    cudaCheckError(::cudaStreamCreate(&stream2));\n\n    // Allocations and initializations.\n    float *hA, *hB, *hC, *hD, *hResult;\n    float *dA, *dB, *dC, *dD, *dResultAB, *dResultCD, *dFinal;\n\n    // Host.\n    cudaCheckError(::cudaMallocHost(&hA, size));\n    cudaCheckError(::cudaMallocHost(&hB, size));\n    cudaCheckError(::cudaMallocHost(&hC, size));\n    cudaCheckError(::cudaMallocHost(&hD, size));\n    cudaCheckError(::cudaMallocHost(&hResult, size));\n\n    for (int i = 0; i < width * width; ++i) {\n        hA[i] = 1.0f;\n        hB[i] = 2.0f;\n        hC[i] = 3.0f;\n        hD[i] = 4.0f;\n    }\n\n    // Device.\n    cudaCheckError(::cudaMalloc(&dA, size));\n    cudaCheckError(::cudaMalloc(&dB, size));\n    cudaCheckError(::cudaMalloc(&dC, size));\n    cudaCheckError(::cudaMalloc(&dD, size));\n    cudaCheckError(::cudaMalloc(&dResultAB, size));\n    cudaCheckError(::cudaMalloc(&dResultCD, size));\n    cudaCheckError(::cudaMalloc(&dFinal, size));\n\n    // Copy data using separate streams. This is not necessary, per se, but it will\n    // allow for overlap of copy and execution in a batch mode case.\n    cudaCheckError(::cudaMemcpyAsync(dA, hA, size, cudaMemcpyHostToDevice, stream1));\n    cudaCheckError(::cudaMemcpyAsync(dB, hB, size, cudaMemcpyHostToDevice, stream1));\n    cudaCheckError(::cudaMemcpyAsync(dC, hC, size, cudaMemcpyHostToDevice, stream2));\n    cudaCheckError(::cudaMemcpyAsync(dD, hD, size, cudaMemcpyHostToDevice, stream2));\n    \n    // Launch matmul kernels on different streams.\n    dim3 threadsPerBlock(32, 32);\n    dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x, \n                   (width + threadsPerBlock.y - 1) / threadsPerBlock.y);\n    matMul<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, dA, dB, width);\n    matMul<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, dC, dD, width);\n\n    // Add 1.0f to each element in the result matrices\n    addOne<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, width);\n    addOne<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, width);\n\n    // Subtract 1.0f to each element in the result matrices\n    minusOne<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, width);\n    minusOne<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, width);\n    \n    // Synchronize work prior to computing on each stream's result array.\n    cudaCheckError(::cudaStreamSynchronize(stream1));\n    cudaCheckError(::cudaStreamSynchronize(stream2));\n\n    matAdd<<<numBlocks, threadsPerBlock, 0, stream1>>>(dFinal, dResultAB, dResultCD, width);\n    cudaCheckError(::cudaMemcpy(hResult, dFinal, size, cudaMemcpyDeviceToHost));\n\n    // Verify results.\n    float expected = (1.0f * 2.0f + 3.0f * 4.0f) * width;\n    for (int i = 0; i < width * width; ++i) {\n        assert(fabs(hResult[i] - expected) < 1e-5);\n    }\n\n    std::cout << \"Test passed!\" << std::endl;\n\n    // Clean up.\n    cudaCheckError(::cudaFree(dA));\n    cudaCheckError(::cudaFree(dB));\n    cudaCheckError(::cudaFree(dC));\n    cudaCheckError(::cudaFree(dD));\n    cudaCheckError(::cudaFree(dResultAB));\n    cudaCheckError(::cudaFree(dResultCD));\n    cudaCheckError(::cudaFree(dFinal));\n    cudaCheckError(::cudaStreamDestroy(stream1));\n    cudaCheckError(::cudaStreamDestroy(stream2));\n    cudaCheckError(::cudaFreeHost(hA));\n    cudaCheckError(::cudaFreeHost(hB));\n    cudaCheckError(::cudaFreeHost(hC));\n    cudaCheckError(::cudaFreeHost(hD));\n    cudaCheckError(::cudaFreeHost(hResult));\n\n    return 0;\n}\n\n", "output": "#include <cassert>\n#include <iostream>\n#include <hip/hip_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n// Simple matmul kernel.\n__global__ void matMul(float *out, const float *in1, const float *in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    float sum = 0.0f;\n    if (row < width && col < width) {\n        for (int i = 0; i < width; ++i) {\n            sum += in1[row * width + i] * in2[i * width + col];\n        }\n        out[row * width + col] = sum;\n    }\n}\n\n// Add one kernel to emphasize serial execution within a stream.\n__global__ void addOne(float *out, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        int idx = row * width + col;\n        out[idx] += 1.0f;\n    }\n}\n\n// Minus one kernel to emphasize serial execution within a stream.\n__global__ void minusOne(float *out, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        int idx = row * width + col;\n        out[idx] -= 1.0f;\n    }\n}\n\n// Add matrices to a final result for comparison.\n__global__ void matAdd(float *out, const float *in1, const float *in2, const int width) {\n    const int row = blockIdx.y * blockDim.y + threadIdx.y;\n    const int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < width && col < width) {\n        out[row * width + col] = in1[row * width + col] + in2[row * width + col];\n    }\n}\n\n// Perform a matmul on separate streams for a million elements, then add them.\nint main() {\n    // Set dimensionality.\n    const int width = 1024;\n    const int size = width * width * sizeof(float);\n\n    // Create separate streams to run matmuls asynchronously.\n    hipStream_t stream1, stream2;\n    cudaCheckError(::hipStreamCreate(&stream1));\n    cudaCheckError(::hipStreamCreate(&stream2));\n\n    // Allocations and initializations.\n    float *hA, *hB, *hC, *hD, *hResult;\n    float *dA, *dB, *dC, *dD, *dResultAB, *dResultCD, *dFinal;\n\n    // Host.\n    cudaCheckError(::hipHostMalloc(&hA, size));\n    cudaCheckError(::hipHostMalloc(&hB, size));\n    cudaCheckError(::hipHostMalloc(&hC, size));\n    cudaCheckError(::hipHostMalloc(&hD, size));\n    cudaCheckError(::hipHostMalloc(&hResult, size));\n\n    for (int i = 0; i < width * width; ++i) {\n        hA[i] = 1.0f;\n        hB[i] = 2.0f;\n        hC[i] = 3.0f;\n        hD[i] = 4.0f;\n    }\n\n    // Device.\n    cudaCheckError(::hipMalloc(&dA, size));\n    cudaCheckError(::hipMalloc(&dB, size));\n    cudaCheckError(::hipMalloc(&dC, size));\n    cudaCheckError(::hipMalloc(&dD, size));\n    cudaCheckError(::hipMalloc(&dResultAB, size));\n    cudaCheckError(::hipMalloc(&dResultCD, size));\n    cudaCheckError(::hipMalloc(&dFinal, size));\n\n    // Copy data using separate streams. This is not necessary, per se, but it will\n    // allow for overlap of copy and execution in a batch mode case.\n    cudaCheckError(::hipMemcpyAsync(dA, hA, size, hipMemcpyHostToDevice, stream1));\n    cudaCheckError(::hipMemcpyAsync(dB, hB, size, hipMemcpyHostToDevice, stream1));\n    cudaCheckError(::hipMemcpyAsync(dC, hC, size, hipMemcpyHostToDevice, stream2));\n    cudaCheckError(::hipMemcpyAsync(dD, hD, size, hipMemcpyHostToDevice, stream2));\n    \n    // Launch matmul kernels on different streams.\n    dim3 threadsPerBlock(32, 32);\n    dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x, \n                   (width + threadsPerBlock.y - 1) / threadsPerBlock.y);\n    matMul<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, dA, dB, width);\n    matMul<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, dC, dD, width);\n\n    // Add 1.0f to each element in the result matrices\n    addOne<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, width);\n    addOne<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, width);\n\n    // Subtract 1.0f to each element in the result matrices\n    minusOne<<<numBlocks, threadsPerBlock, 0, stream1>>>(dResultAB, width);\n    minusOne<<<numBlocks, threadsPerBlock, 0, stream2>>>(dResultCD, width);\n    \n    // Synchronize work prior to computing on each stream's result array.\n    cudaCheckError(::hipStreamSynchronize(stream1));\n    cudaCheckError(::hipStreamSynchronize(stream2));\n\n    matAdd<<<numBlocks, threadsPerBlock, 0, stream1>>>(dFinal, dResultAB, dResultCD, width);\n    cudaCheckError(::hipMemcpy(hResult, dFinal, size, hipMemcpyDeviceToHost));\n\n    // Verify results.\n    float expected = (1.0f * 2.0f + 3.0f * 4.0f) * width;\n    for (int i = 0; i < width * width; ++i) {\n        assert(fabs(hResult[i] - expected) < 1e-5);\n    }\n\n    std::cout << \"Test passed!\" << std::endl;\n\n    // Clean up.\n    cudaCheckError(::hipFree(dA));\n    cudaCheckError(::hipFree(dB));\n    cudaCheckError(::hipFree(dC));\n    cudaCheckError(::hipFree(dD));\n    cudaCheckError(::hipFree(dResultAB));\n    cudaCheckError(::hipFree(dResultCD));\n    cudaCheckError(::hipFree(dFinal));\n    cudaCheckError(::hipStreamDestroy(stream1));\n    cudaCheckError(::hipStreamDestroy(stream2));\n    cudaCheckError(::hipHostFree(hA));\n    cudaCheckError(::hipHostFree(hB));\n    cudaCheckError(::hipHostFree(hC));\n    cudaCheckError(::hipHostFree(hD));\n    cudaCheckError(::hipHostFree(hResult));\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cuda_examples/PerformanceChecklistExamples/cuda_streams/cuda_streams.cu"}
{"input": "#include <iostream>\n#include <cassert>\n#include \"../../utils/utils.cuh\"\n\n/**\n * @file vector_add.cu\n * \n * @brief We demonstrate adding 2 vectors of size 2048 using CUDA\n */\n\n/**\n * @brief CUDA kernel to perform vector addition\n * \n * @param d_v1 Device vector holding a input vector\n * @param d_v2 Device vector holding another input vector\n * @param d_v3 Device vector holding the sum of d_v1 and d_v2\n */\n__global__ void vector_add_kernel(float *d_v1, float *d_v2, float *d_v3, int n) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i < n) {\n        d_v3[i] = d_v1[i] + d_v2[i];\n    }\n}\n\nint main() {\n    int N = 2048;\n    size_t size = sizeof(float) * N;\n\n    // Allocate memory for the host vectors\n    float *v1, *v2, *v3;\n    cudaCheckError(::cudaMallocHost(&v1, size));\n    cudaCheckError(::cudaMallocHost(&v2, size));\n    cudaCheckError(::cudaMallocHost(&v3, size));\n\n    // Fill the host vectors with data\n    for (size_t i = 0; i < N; i++) {\n        v1[i] = 6.4;\n        v2[i] = 3.5;\n    }\n\n    // Create 3 device vectors for holding N float elements\n    float *d_v1, *d_v2, *d_v3;\n    cudaCheckError(::cudaMalloc(&d_v1, size));\n    cudaCheckError(::cudaMalloc(&d_v2, size));\n    cudaCheckError(::cudaMalloc(&d_v3, size));\n\n    // Copy the host vectors into the device vectors\n    cudaCheckError(::cudaMemcpy(d_v1, v1, size, cudaMemcpyHostToDevice));\n    cudaCheckError(::cudaMemcpy(d_v2, v2, size, cudaMemcpyHostToDevice));\n\n    // Launch the kernel using 8 thread blocks and 256 threads / block\n    vector_add_kernel<<<8, 256>>>(d_v1, d_v2, d_v3, N);\n\n    // Copy the result device vector into the result host vector\n    cudaCheckError(::cudaMemcpy(v3, d_v3, size, cudaMemcpyDeviceToHost));\n\n    cudaCheckError(::cudaDeviceSynchronize());\n\n    // Verify the result host vector\n    for (int i = 0; i < N; i++) {\n        assert(v3[i] == (float)9.9);\n    }\n\n    // Free the device vector\n    cudaCheckError(::cudaFree(d_v1));\n    cudaCheckError(::cudaFree(d_v2));\n    cudaCheckError(::cudaFree(d_v3));\n\n    // Free the host vectors\n    cudaCheckError(::cudaFreeHost(v1));\n    cudaCheckError(::cudaFreeHost(v2));\n    cudaCheckError(::cudaFreeHost(v3));\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cassert>\n#include \"../../utils/utils.cuh\"\n\n/**\n * @file vector_add.cu\n * \n * @brief We demonstrate adding 2 vectors of size 2048 using CUDA\n */\n\n/**\n * @brief CUDA kernel to perform vector addition\n * \n * @param d_v1 Device vector holding a input vector\n * @param d_v2 Device vector holding another input vector\n * @param d_v3 Device vector holding the sum of d_v1 and d_v2\n */\n__global__ void vector_add_kernel(float *d_v1, float *d_v2, float *d_v3, int n) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i < n) {\n        d_v3[i] = d_v1[i] + d_v2[i];\n    }\n}\n\nint main() {\n    int N = 2048;\n    size_t size = sizeof(float) * N;\n\n    // Allocate memory for the host vectors\n    float *v1, *v2, *v3;\n    cudaCheckError(::hipHostMalloc(&v1, size));\n    cudaCheckError(::hipHostMalloc(&v2, size));\n    cudaCheckError(::hipHostMalloc(&v3, size));\n\n    // Fill the host vectors with data\n    for (size_t i = 0; i < N; i++) {\n        v1[i] = 6.4;\n        v2[i] = 3.5;\n    }\n\n    // Create 3 device vectors for holding N float elements\n    float *d_v1, *d_v2, *d_v3;\n    cudaCheckError(::hipMalloc(&d_v1, size));\n    cudaCheckError(::hipMalloc(&d_v2, size));\n    cudaCheckError(::hipMalloc(&d_v3, size));\n\n    // Copy the host vectors into the device vectors\n    cudaCheckError(::hipMemcpy(d_v1, v1, size, hipMemcpyHostToDevice));\n    cudaCheckError(::hipMemcpy(d_v2, v2, size, hipMemcpyHostToDevice));\n\n    // Launch the kernel using 8 thread blocks and 256 threads / block\n    vector_add_kernel<<<8, 256>>>(d_v1, d_v2, d_v3, N);\n\n    // Copy the result device vector into the result host vector\n    cudaCheckError(::hipMemcpy(v3, d_v3, size, hipMemcpyDeviceToHost));\n\n    cudaCheckError(::hipDeviceSynchronize());\n\n    // Verify the result host vector\n    for (int i = 0; i < N; i++) {\n        assert(v3[i] == (float)9.9);\n    }\n\n    // Free the device vector\n    cudaCheckError(::hipFree(d_v1));\n    cudaCheckError(::hipFree(d_v2));\n    cudaCheckError(::hipFree(d_v3));\n\n    // Free the host vectors\n    cudaCheckError(::hipHostFree(v1));\n    cudaCheckError(::hipHostFree(v2));\n    cudaCheckError(::hipHostFree(v3));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/KernelAndLibExamples/vector_add/vector_add.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n#include <device_launch_parameters.h>\n#include \"../../utils/utils.cuh\"\n\n// Device (GPU) code to print thread index from the GPU.\n__global__ void helloFromGPU()\n{\n    int threadId = blockIdx.x * blockDim.x + threadIdx.x;\n    printf(\"Hello from GPU thread: %d!\\n\", threadId);\n}\n\n// Host code\nint main()\n{\n  int nDevices;\n  cudaGetDeviceCount(&nDevices);\n  for (int i = 0; i < nDevices; i++) {\n    cudaDeviceProp prop;\n    cudaGetDeviceProperties(&prop, i);\n    printf(\"Device Number: %d\\n\", i);\n    printf(\"  Device name: %s\\n\", prop.name);\n    printf(\"  major-minor: %d-%d\\n\", prop.major, prop.minor);\n    printf(\"  Concurrent kernels: %s\\n\", prop.concurrentKernels ? \"yes\" : \"no\");\n    printf(\"  Concurrent computation/communication: %s\\n\\n\",prop.deviceOverlap ? \"yes\" : \"no\");\n    printf(\"  Managed memory supported: %s\\n\", prop.managedMemory ? \"yes\" : \"no\");\n    printf(\"  Memory Clock Rate (MHz): %d\\n\",\n           prop.memoryClockRate/1024);\n    printf(\"  Memory Bus Width (bits): %d\\n\",\n           prop.memoryBusWidth);\n    printf(\"  Peak Memory Bandwidth (GB/s): %.1f\\n\",\n           2.0*prop.memoryClockRate*(prop.memoryBusWidth/8)/1.0e6);\n    printf(\"  Total global memory (Gbytes): %.1f\\n\",(float)(prop.totalGlobalMem)/1024.0/1024.0/1024.0);\n    printf(\"  Shared memory per block (Kbytes): %.1f\\n\",(float)(prop.sharedMemPerBlock)/1024.0);\n    printf(\"  L2 cache size (Kbytes): %.1f\\n\",(float)(prop.l2CacheSize)/1024);\n    printf(\"  Warp-size: %d\\n\", prop.warpSize);\n  }\n  printf(\"\\n\");\n  printf(\"Hello from CPU!\\n\");\n  helloFromGPU<<<1,4>>>();\n  cudaCheckError(::cudaDeviceSynchronize());\n  return 0;\n}\n\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <>\n#include \"../../utils/utils.cuh\"\n\n// Device (GPU) code to print thread index from the GPU.\n__global__ void helloFromGPU()\n{\n    int threadId = blockIdx.x * blockDim.x + threadIdx.x;\n    printf(\"Hello from GPU thread: %d!\\n\", threadId);\n}\n\n// Host code\nint main()\n{\n  int nDevices;\n  hipGetDeviceCount(&nDevices);\n  for (int i = 0; i < nDevices; i++) {\n    hipDeviceProp_t prop;\n    hipGetDeviceProperties(&prop, i);\n    printf(\"Device Number: %d\\n\", i);\n    printf(\"  Device name: %s\\n\", prop.name);\n    printf(\"  major-minor: %d-%d\\n\", prop.major, prop.minor);\n    printf(\"  Concurrent kernels: %s\\n\", prop.concurrentKernels ? \"yes\" : \"no\");\n    printf(\"  Concurrent computation/communication: %s\\n\\n\",prop.deviceOverlap ? \"yes\" : \"no\");\n    printf(\"  Managed memory supported: %s\\n\", prop.managedMemory ? \"yes\" : \"no\");\n    printf(\"  Memory Clock Rate (MHz): %d\\n\",\n           prop.memoryClockRate/1024);\n    printf(\"  Memory Bus Width (bits): %d\\n\",\n           prop.memoryBusWidth);\n    printf(\"  Peak Memory Bandwidth (GB/s): %.1f\\n\",\n           2.0*prop.memoryClockRate*(prop.memoryBusWidth/8)/1.0e6);\n    printf(\"  Total global memory (Gbytes): %.1f\\n\",(float)(prop.totalGlobalMem)/1024.0/1024.0/1024.0);\n    printf(\"  Shared memory per block (Kbytes): %.1f\\n\",(float)(prop.sharedMemPerBlock)/1024.0);\n    printf(\"  L2 cache size (Kbytes): %.1f\\n\",(float)(prop.l2CacheSize)/1024);\n    printf(\"  Warp-size: %d\\n\", prop.warpSize);\n  }\n  printf(\"\\n\");\n  printf(\"Hello from CPU!\\n\");\n  helloFromGPU<<<1,4>>>();\n  cudaCheckError(::hipDeviceSynchronize());\n  return 0;\n}\n\n", "source": "/kaggle/working/cuda_examples/SetupAndInitExamples/setup_check/hello_world.cu"}
{"input": "#include <chrono>\n#include <iostream>\n#include <vector>\n#include <cuda_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n// Simple kernel for use.\n__global__ void vector_add_kernel(float *d_v1, float *d_v2, float *d_v3, int n) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i < n) {\n        d_v3[i] = d_v1[i] + d_v2[i];\n    }\n}\n\n// Structure to pass around host memory pointers.\nstruct Memory {\n    float *v1;\n    float *v2;\n    float *v3;\n};\n\nvoid benchmark(int N, bool usePinnedMemory) {\n    size_t size = N * sizeof(float);\n    Memory mem;\n    \n    // Allocate host-pinned memory.\n    if (usePinnedMemory) {\n        cudaCheckError(::cudaMallocHost(&mem.v1, size));\n        cudaCheckError(::cudaMallocHost(&mem.v2, size));\n        cudaCheckError(::cudaMallocHost(&mem.v3, size));\n    } else {\n        // Allocate pageable memory.\n        mem.v1 = (float*)malloc(size);\n        mem.v2 = (float*)malloc(size);\n        mem.v3 = (float*)malloc(size);\n    }\n\n    // Initialize data\n    for (int i = 0; i < N; i++) {\n        mem.v1[i] = 1.0f;\n        mem.v2[i] = 2.0f;\n    }\n\n    // Allocate device memory\n    float *d_v1, *d_v2, *d_v3;\n    cudaCheckError(::cudaMalloc(&d_v1, size));\n    cudaCheckError(::cudaMalloc(&d_v2, size));\n    cudaCheckError(::cudaMalloc(&d_v3, size));\n\n    // Create CUDA stream\n    cudaStream_t stream;\n    cudaCheckError(::cudaStreamCreate(&stream));\n\n    // Create CUDA events for timing\n    cudaEvent_t start, stop;\n    cudaCheckError(::cudaEventCreate(&start));\n    cudaCheckError(::cudaEventCreate(&stop));\n\n    // Record start time\n    cudaCheckError(::cudaEventRecord(start, stream));\n\n    // Asynchronous memory transfers\n    cudaCheckError(::cudaMemcpyAsync(d_v1, mem.v1, size, cudaMemcpyHostToDevice, stream));\n    cudaCheckError(::cudaMemcpyAsync(d_v2, mem.v2, size, cudaMemcpyHostToDevice, stream));\n\n    // Launch kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n    vector_add_kernel<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(d_v1, d_v2, d_v3, N);\n\n    // Asynchronous memory transfer back to host\n    cudaCheckError(::cudaMemcpyAsync(mem.v3, d_v3, size, cudaMemcpyDeviceToHost, stream));\n\n    // Record stop time\n    cudaCheckError(::cudaEventRecord(stop, stream));\n    cudaCheckError(::cudaEventSynchronize(stop));\n\n    float milliseconds = 0;\n    cudaCheckError(::cudaEventElapsedTime(&milliseconds, start, stop));\n\n    // Calculate bandwidth\n    float bandwidth = 3 * size / (milliseconds / 1000) / 1e9; // GB/s\n\n    std::cout << (usePinnedMemory ? \"Pinned\" : \"Pageable\") << \" memory, N = \" << N \n              << \", Time: \" << milliseconds << \" ms, Bandwidth: \" << bandwidth << \" GB/s\" << std::endl;\n\n    // Clean up\n    cudaCheckError(::cudaFree(d_v1));\n    cudaCheckError(::cudaFree(d_v2));\n    cudaCheckError(::cudaFree(d_v3));\n    cudaCheckError(::cudaStreamDestroy(stream));\n    cudaCheckError(::cudaEventDestroy(start));\n    cudaCheckError(::cudaEventDestroy(stop));\n\n    if (usePinnedMemory) {\n        cudaCheckError(::cudaFreeHost(mem.v1));\n        cudaCheckError(::cudaFreeHost(mem.v2));\n        cudaCheckError(::cudaFreeHost(mem.v3));\n    } else {\n        free(mem.v1);\n        free(mem.v2);\n        free(mem.v3);\n    }\n}\n\nint main() {\n    // Small, to fairly large memory pushes:\n    // 1024 * 4 bytes = 4KB\n    // 1024 * 1024 * 4 bytes = 4MB\n    // 10 * 1024 * 1024 * 4 bytes = 40MB\n    std::vector<int> sizes = {1024, 1024 * 1024, 10 * 1024 * 1024};\n    \n    for (int N : sizes) {\n        benchmark(N, false); // Pageable memory\n        benchmark(N, true);  // Pinned memory\n        std::cout << std::endl;\n    }\n\n    return 0;\n}\n", "output": "#include <chrono>\n#include <iostream>\n#include <vector>\n#include <hip/hip_runtime.h>\n#include \"../../utils/utils.cuh\"\n\n// Simple kernel for use.\n__global__ void vector_add_kernel(float *d_v1, float *d_v2, float *d_v3, int n) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i < n) {\n        d_v3[i] = d_v1[i] + d_v2[i];\n    }\n}\n\n// Structure to pass around host memory pointers.\nstruct Memory {\n    float *v1;\n    float *v2;\n    float *v3;\n};\n\nvoid benchmark(int N, bool usePinnedMemory) {\n    size_t size = N * sizeof(float);\n    Memory mem;\n    \n    // Allocate host-pinned memory.\n    if (usePinnedMemory) {\n        cudaCheckError(::hipHostMalloc(&mem.v1, size));\n        cudaCheckError(::hipHostMalloc(&mem.v2, size));\n        cudaCheckError(::hipHostMalloc(&mem.v3, size));\n    } else {\n        // Allocate pageable memory.\n        mem.v1 = (float*)malloc(size);\n        mem.v2 = (float*)malloc(size);\n        mem.v3 = (float*)malloc(size);\n    }\n\n    // Initialize data\n    for (int i = 0; i < N; i++) {\n        mem.v1[i] = 1.0f;\n        mem.v2[i] = 2.0f;\n    }\n\n    // Allocate device memory\n    float *d_v1, *d_v2, *d_v3;\n    cudaCheckError(::hipMalloc(&d_v1, size));\n    cudaCheckError(::hipMalloc(&d_v2, size));\n    cudaCheckError(::hipMalloc(&d_v3, size));\n\n    // Create CUDA stream\n    hipStream_t stream;\n    cudaCheckError(::hipStreamCreate(&stream));\n\n    // Create CUDA events for timing\n    hipEvent_t start, stop;\n    cudaCheckError(::hipEventCreate(&start));\n    cudaCheckError(::hipEventCreate(&stop));\n\n    // Record start time\n    cudaCheckError(::hipEventRecord(start, stream));\n\n    // Asynchronous memory transfers\n    cudaCheckError(::hipMemcpyAsync(d_v1, mem.v1, size, hipMemcpyHostToDevice, stream));\n    cudaCheckError(::hipMemcpyAsync(d_v2, mem.v2, size, hipMemcpyHostToDevice, stream));\n\n    // Launch kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n    vector_add_kernel<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(d_v1, d_v2, d_v3, N);\n\n    // Asynchronous memory transfer back to host\n    cudaCheckError(::hipMemcpyAsync(mem.v3, d_v3, size, hipMemcpyDeviceToHost, stream));\n\n    // Record stop time\n    cudaCheckError(::hipEventRecord(stop, stream));\n    cudaCheckError(::hipEventSynchronize(stop));\n\n    float milliseconds = 0;\n    cudaCheckError(::hipEventElapsedTime(&milliseconds, start, stop));\n\n    // Calculate bandwidth\n    float bandwidth = 3 * size / (milliseconds / 1000) / 1e9; // GB/s\n\n    std::cout << (usePinnedMemory ? \"Pinned\" : \"Pageable\") << \" memory, N = \" << N \n              << \", Time: \" << milliseconds << \" ms, Bandwidth: \" << bandwidth << \" GB/s\" << std::endl;\n\n    // Clean up\n    cudaCheckError(::hipFree(d_v1));\n    cudaCheckError(::hipFree(d_v2));\n    cudaCheckError(::hipFree(d_v3));\n    cudaCheckError(::hipStreamDestroy(stream));\n    cudaCheckError(::hipEventDestroy(start));\n    cudaCheckError(::hipEventDestroy(stop));\n\n    if (usePinnedMemory) {\n        cudaCheckError(::hipHostFree(mem.v1));\n        cudaCheckError(::hipHostFree(mem.v2));\n        cudaCheckError(::hipHostFree(mem.v3));\n    } else {\n        free(mem.v1);\n        free(mem.v2);\n        free(mem.v3);\n    }\n}\n\nint main() {\n    // Small, to fairly large memory pushes:\n    // 1024 * 4 bytes = 4KB\n    // 1024 * 1024 * 4 bytes = 4MB\n    // 10 * 1024 * 1024 * 4 bytes = 40MB\n    std::vector<int> sizes = {1024, 1024 * 1024, 10 * 1024 * 1024};\n    \n    for (int N : sizes) {\n        benchmark(N, false); // Pageable memory\n        benchmark(N, true);  // Pinned memory\n        std::cout << std::endl;\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/MemoryAndStructureExamples/pinned_vs_pageable/pinned_vs_pageable.cu"}
{"input": "#include <cuda_runtime.h>\n#include <curand_kernel.h>\n#include <thrust/device_vector.h>\n#include <thrust/copy.h>\n#include <thrust/random.h>\n#include <iostream>\n#include \"../../utils/utils.cuh\"\n\n// Kernel to intialize weights with random values.\n__global__ void initializeWeightsKernel(float* weights, int size, unsigned long seed) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        curandStateXORWOW state;\n        curand_init(seed, idx, 0, &state);\n        weights[idx] = curand_normal(&state);\n    }\n}\n\n// Function to initialize weights using cudaMalloc\nvoid initializeWeightsCudaMalloc(int size) {\n\n    float* d_weights;\n    // Allocate memory for distribution, but don't initialize values until kernel\n    cudaCheckError(::cudaMalloc(&d_weights, size * sizeof(float)));\n\n    // Launch kernel to initialize weights\n    initializeWeightsKernel<<<(size + 255) / 256, 256>>>(d_weights, size, time(NULL));\n    cudaCheckError(::cudaDeviceSynchronize());\n\n    cudaCheckError(::cudaFree(d_weights));\n\n    std::cout << \"Initialized weights using cudaMalloc.\" << std::endl;\n}\n\n// Function to initialize weights using thrust::device_vector\nvoid initializeWeightsThrust(int size) {\n\n    // Create a device vector and initialize with zeros\n    thrust::device_vector<float> weights(size);\n\n    // Launch kernel to initialize weights\n    initializeWeightsKernel<<<(size + 255) / 256, 256>>>(thrust::raw_pointer_cast(weights.data()), size, time(NULL));\n\n    std::cout << \"Initialized weights using thrust::device_vector.\" << std::endl;\n}\n\nint main() {\n    const int size = 1000000;  // Large number of weights\n\n    // Initialize weights using thrust::device_vector\n    initializeWeightsThrust(size);\n\n    // Initialize weights using cudaMalloc\n    initializeWeightsCudaMalloc(size);\n\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <hiprand/hiprand_kernel.h>\n#include <thrust/device_vector.h>\n#include <thrust/copy.h>\n#include <thrust/random.h>\n#include <iostream>\n#include \"../../utils/utils.cuh\"\n\n// Kernel to intialize weights with random values.\n__global__ void initializeWeightsKernel(float* weights, int size, unsigned long seed) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        hiprandStateXORWOW state;\n        hiprand_init(seed, idx, 0, &state);\n        weights[idx] = hiprand_normal(&state);\n    }\n}\n\n// Function to initialize weights using cudaMalloc\nvoid initializeWeightsCudaMalloc(int size) {\n\n    float* d_weights;\n    // Allocate memory for distribution, but don't initialize values until kernel\n    cudaCheckError(::hipMalloc(&d_weights, size * sizeof(float)));\n\n    // Launch kernel to initialize weights\n    initializeWeightsKernel<<<(size + 255) / 256, 256>>>(d_weights, size, time(NULL));\n    cudaCheckError(::hipDeviceSynchronize());\n\n    cudaCheckError(::hipFree(d_weights));\n\n    std::cout << \"Initialized weights using hipMalloc.\" << std::endl;\n}\n\n// Function to initialize weights using thrust::device_vector\nvoid initializeWeightsThrust(int size) {\n\n    // Create a device vector and initialize with zeros\n    thrust::device_vector<float> weights(size);\n\n    // Launch kernel to initialize weights\n    initializeWeightsKernel<<<(size + 255) / 256, 256>>>(thrust::raw_pointer_cast(weights.data()), size, time(NULL));\n\n    std::cout << \"Initialized weights using thrust::device_vector.\" << std::endl;\n}\n\nint main() {\n    const int size = 1000000;  // Large number of weights\n\n    // Initialize weights using thrust::device_vector\n    initializeWeightsThrust(size);\n\n    // Initialize weights using cudaMalloc\n    initializeWeightsCudaMalloc(size);\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda_examples/MemoryAndStructureExamples/alloc_init_vs_alloc_uninit/alloc_init_vs_alloc_uninit.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockRadixSort\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_radix_sort.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n#include <algorithm>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_radix_sort.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n/// Uniform key samples\nbool g_uniform_keys;\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide sorting over integers\n */\ntemplate <\n    typename    Key,\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\n__launch_bounds__ (BLOCK_THREADS)\n__global__ void BlockSortKernel(\n    Key         *d_in,          // Tile of input\n    Key         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    enum { TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD };\n\n    // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockLoad<Key, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE> BlockLoadT;\n\n    // Specialize BlockRadixSort type for our thread block\n    typedef BlockRadixSort<Key, BLOCK_THREADS, ITEMS_PER_THREAD> BlockRadixSortT;\n\n    // Shared memory\n    __shared__ union TempStorage\n    {\n        typename BlockLoadT::TempStorage        load;\n        typename BlockRadixSortT::TempStorage   sort;\n    } temp_storage;\n\n    // Per-thread tile items\n    Key items[ITEMS_PER_THREAD];\n\n    // Our current block's offset\n    int block_offset = blockIdx.x * TILE_SIZE;\n\n    // Load items into a blocked arrangement\n    BlockLoadT(temp_storage.load).Load(d_in + block_offset, items);\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Sort keys\n    BlockRadixSortT(temp_storage.sort).SortBlockedToStriped(items);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store output in striped fashion\n    StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items);\n\n    // Store elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        d_elapsed[blockIdx.x] = (start > stop) ? start - stop : stop - start;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize sorting problem (and solution).\n */\ntemplate <typename Key>\nvoid Initialize(\n    Key *h_in,\n    Key *h_reference,\n    int num_items,\n    int tile_size)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        if (g_uniform_keys)\n        {\n            h_in[i] = 0;\n        }\n        else\n        {\n            RandomBits(h_in[i]);\n        }\n        h_reference[i] = h_in[i];\n    }\n\n    // Only sort the first tile\n    std::sort(h_reference, h_reference + tile_size);\n}\n\n\n/**\n * Test BlockScan\n */\ntemplate <\n    typename    Key,\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    Key *h_in               = new Key[TILE_SIZE * g_grid_size];\n    Key *h_reference        = new Key[TILE_SIZE * g_grid_size];\n    clock_t *h_elapsed      = new clock_t[g_grid_size];\n\n    // Initialize problem and reference output on host\n    Initialize(h_in, h_reference, TILE_SIZE * g_grid_size, TILE_SIZE);\n\n    // Initialize device arrays\n    Key *d_in       = NULL;\n    Key *d_out      = NULL;\n    clock_t *d_elapsed  = NULL;\n    CubDebugExit(cudaMalloc((void**)&d_in,          sizeof(Key) * TILE_SIZE * g_grid_size));\n    CubDebugExit(cudaMalloc((void**)&d_out,         sizeof(Key) * TILE_SIZE * g_grid_size));\n    CubDebugExit(cudaMalloc((void**)&d_elapsed,     sizeof(clock_t) * g_grid_size));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            std::cout << h_in[i] << \", \";\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD>, BLOCK_THREADS));\n\n    // Copy problem to device\n    CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(Key) * TILE_SIZE * g_grid_size, cudaMemcpyHostToDevice));\n\n    printf(\"BlockRadixSort %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        TILE_SIZE * g_grid_size, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n    fflush(stdout);\n\n    // Run kernel once to prime caches and check result\n    BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Check results\n    printf(\"\\tOutput items: \");\n    int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n    fflush(stdout);\n\n    // Run this several times and average the performance results\n    GpuTimer            timer;\n    float               elapsed_millis          = 0.0;\n    unsigned long long  elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        timer.Start();\n\n        // Run kernel\n        BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        CubDebugExit(cudaMemcpy(h_elapsed, d_elapsed, sizeof(clock_t) * g_grid_size, cudaMemcpyDeviceToHost));\n        for (int j = 0; j < g_grid_size; j++)\n        {\n            elapsed_clocks += h_elapsed[j];\n        }\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    double avg_clocks           = double(elapsed_clocks) / g_timing_iterations / g_grid_size;\n    double avg_clocks_per_item  = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockRadixSort::SortBlocked clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockRadixSort::SortBlocked clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n    fflush(stdout);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_elapsed) delete[] h_elapsed;\n    if (d_in) CubDebugExit(cudaFree(d_in));\n    if (d_out) CubDebugExit(cudaFree(d_out));\n    if (d_elapsed) CubDebugExit(cudaFree(d_elapsed));\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    g_uniform_keys = args.CheckCmdLineFlag(\"uniform\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations (default:%d)>]\"\n            \"[--grid-size=<grid size (default:%d)>]\"\n            \"[--v] \"\n            \"\\n\", argv[0], g_timing_iterations, g_grid_size);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    fflush(stdout);\n\n    // Run tests\n    printf(\"\\nuint32:\\n\"); fflush(stdout);\n    Test<unsigned int, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    printf(\"\\nfp32:\\n\"); fflush(stdout);\n    Test<float, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    printf(\"\\nuint8:\\n\"); fflush(stdout);\n    Test<unsigned char, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockRadixSort\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_radix_sort.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n#include <algorithm>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_radix_sort.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n/// Uniform key samples\nbool g_uniform_keys;\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide sorting over integers\n */\ntemplate <\n    typename    Key,\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\n__launch_bounds__ (BLOCK_THREADS)\n__global__ void BlockSortKernel(\n    Key         *d_in,          // Tile of input\n    Key         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    enum { TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD };\n\n    // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockLoad<Key, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE> BlockLoadT;\n\n    // Specialize BlockRadixSort type for our thread block\n    typedef BlockRadixSort<Key, BLOCK_THREADS, ITEMS_PER_THREAD> BlockRadixSortT;\n\n    // Shared memory\n    __shared__ union TempStorage\n    {\n        typename BlockLoadT::TempStorage        load;\n        typename BlockRadixSortT::TempStorage   sort;\n    } temp_storage;\n\n    // Per-thread tile items\n    Key items[ITEMS_PER_THREAD];\n\n    // Our current block's offset\n    int block_offset = blockIdx.x * TILE_SIZE;\n\n    // Load items into a blocked arrangement\n    BlockLoadT(temp_storage.load).Load(d_in + block_offset, items);\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Sort keys\n    BlockRadixSortT(temp_storage.sort).SortBlockedToStriped(items);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store output in striped fashion\n    StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items);\n\n    // Store elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        d_elapsed[blockIdx.x] = (start > stop) ? start - stop : stop - start;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize sorting problem (and solution).\n */\ntemplate <typename Key>\nvoid Initialize(\n    Key *h_in,\n    Key *h_reference,\n    int num_items,\n    int tile_size)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        if (g_uniform_keys)\n        {\n            h_in[i] = 0;\n        }\n        else\n        {\n            RandomBits(h_in[i]);\n        }\n        h_reference[i] = h_in[i];\n    }\n\n    // Only sort the first tile\n    std::sort(h_reference, h_reference + tile_size);\n}\n\n\n/**\n * Test BlockScan\n */\ntemplate <\n    typename    Key,\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    Key *h_in               = new Key[TILE_SIZE * g_grid_size];\n    Key *h_reference        = new Key[TILE_SIZE * g_grid_size];\n    clock_t *h_elapsed      = new clock_t[g_grid_size];\n\n    // Initialize problem and reference output on host\n    Initialize(h_in, h_reference, TILE_SIZE * g_grid_size, TILE_SIZE);\n\n    // Initialize device arrays\n    Key *d_in       = NULL;\n    Key *d_out      = NULL;\n    clock_t *d_elapsed  = NULL;\n    CubDebugExit(hipMalloc((void**)&d_in,          sizeof(Key) * TILE_SIZE * g_grid_size));\n    CubDebugExit(hipMalloc((void**)&d_out,         sizeof(Key) * TILE_SIZE * g_grid_size));\n    CubDebugExit(hipMalloc((void**)&d_elapsed,     sizeof(clock_t) * g_grid_size));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            std::cout << h_in[i] << \", \";\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD>, BLOCK_THREADS));\n\n    // Copy problem to device\n    CubDebugExit(hipMemcpy(d_in, h_in, sizeof(Key) * TILE_SIZE * g_grid_size, hipMemcpyHostToDevice));\n\n    printf(\"BlockRadixSort %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        TILE_SIZE * g_grid_size, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n    fflush(stdout);\n\n    // Run kernel once to prime caches and check result\n    BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Check results\n    printf(\"\\tOutput items: \");\n    int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n    fflush(stdout);\n\n    // Run this several times and average the performance results\n    GpuTimer            timer;\n    float               elapsed_millis          = 0.0;\n    unsigned long long  elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        timer.Start();\n\n        // Run kernel\n        BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        CubDebugExit(hipMemcpy(h_elapsed, d_elapsed, sizeof(clock_t) * g_grid_size, hipMemcpyDeviceToHost));\n        for (int j = 0; j < g_grid_size; j++)\n        {\n            elapsed_clocks += h_elapsed[j];\n        }\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    double avg_clocks           = double(elapsed_clocks) / g_timing_iterations / g_grid_size;\n    double avg_clocks_per_item  = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockRadixSort::SortBlocked clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockRadixSort::SortBlocked clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n    fflush(stdout);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_elapsed) delete[] h_elapsed;\n    if (d_in) CubDebugExit(hipFree(d_in));\n    if (d_out) CubDebugExit(hipFree(d_out));\n    if (d_elapsed) CubDebugExit(hipFree(d_elapsed));\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    g_uniform_keys = args.CheckCmdLineFlag(\"uniform\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations (default:%d)>]\"\n            \"[--grid-size=<grid size (default:%d)>]\"\n            \"[--v] \"\n            \"\\n\", argv[0], g_timing_iterations, g_grid_size);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    fflush(stdout);\n\n    // Run tests\n    printf(\"\\nuint32:\\n\"); fflush(stdout);\n    Test<unsigned int, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    printf(\"\\nfp32:\\n\"); fflush(stdout);\n    Test<float, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    printf(\"\\nuint8:\\n\"); fflush(stdout);\n    Test<unsigned char, 128, 13>();\n    printf(\"\\n\"); fflush(stdout);\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cub/examples/block/example_block_radix_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_reduce.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockReduceAlgorithm    ALGORITHM>\n__global__ void BlockReduceKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile aggregate\n    clock_t     *d_elapsed)     // Elapsed cycle count of block reduction\n{\n    // Specialize BlockReduce type for our thread block\n    typedef BlockReduce<int, BLOCK_THREADS, ALGORITHM> BlockReduceT;\n\n    // Shared memory\n    __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n    LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in, data);\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute sum\n    int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        *d_out = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int *h_in, int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block reduction\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockReduceAlgorithm    ALGORITHM>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    cudaMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    cudaMalloc((void**)&d_out,         sizeof(int) * 1);\n    cudaMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n    printf(\"BlockReduce algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        (ALGORITHM == BLOCK_REDUCE_RAKING) ? \"BLOCK_REDUCE_RAKING\" : \"BLOCK_REDUCE_WARP_REDUCTIONS\",\n        TILE_SIZE, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n\n    // Run kernel\n    BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    int compare = CompareDeviceResults(&h_aggregate, d_out, 1, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Run this several times and average the performance results\n    GpuTimer    timer;\n    float       elapsed_millis          = 0.0;\n    clock_t     elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Copy problem to device\n        cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n        timer.Start();\n\n        // Run kernel\n        BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        clock_t clocks;\n        CubDebugExit(cudaMemcpy(&clocks, d_elapsed, sizeof(clock_t), cudaMemcpyDeviceToHost));\n        elapsed_clocks += clocks;\n\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    float avg_clocks            = float(elapsed_clocks) / g_timing_iterations;\n    float avg_clocks_per_item   = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockReduce::Sum clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockReduce::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) cudaFree(d_in);\n    if (d_out) cudaFree(d_out);\n    if (d_elapsed) cudaFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations>] \"\n            \"[--grid-size=<grid size>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024, 1, BLOCK_REDUCE_RAKING>();\n    Test<512, 2, BLOCK_REDUCE_RAKING>();\n    Test<256, 4, BLOCK_REDUCE_RAKING>();\n    Test<128, 8, BLOCK_REDUCE_RAKING>();\n    Test<64, 16, BLOCK_REDUCE_RAKING>();\n    Test<32, 32, BLOCK_REDUCE_RAKING>();\n    Test<16, 64, BLOCK_REDUCE_RAKING>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<512, 2, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<256, 4, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<128, 8, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<64, 16, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<32, 32, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<16, 64, BLOCK_REDUCE_WARP_REDUCTIONS>();\n\n    return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_reduce.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockReduceAlgorithm    ALGORITHM>\n__global__ void BlockReduceKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile aggregate\n    clock_t     *d_elapsed)     // Elapsed cycle count of block reduction\n{\n    // Specialize BlockReduce type for our thread block\n    typedef BlockReduce<int, BLOCK_THREADS, ALGORITHM> BlockReduceT;\n\n    // Shared memory\n    __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n    LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in, data);\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute sum\n    int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        *d_out = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int *h_in, int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block reduction\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockReduceAlgorithm    ALGORITHM>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    hipMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    hipMalloc((void**)&d_out,         sizeof(int) * 1);\n    hipMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n    printf(\"BlockReduce algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        (ALGORITHM == BLOCK_REDUCE_RAKING) ? \"BLOCK_REDUCE_RAKING\" : \"BLOCK_REDUCE_WARP_REDUCTIONS\",\n        TILE_SIZE, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n\n    // Run kernel\n    BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    int compare = CompareDeviceResults(&h_aggregate, d_out, 1, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Run this several times and average the performance results\n    GpuTimer    timer;\n    float       elapsed_millis          = 0.0;\n    clock_t     elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Copy problem to device\n        hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n        timer.Start();\n\n        // Run kernel\n        BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        clock_t clocks;\n        CubDebugExit(hipMemcpy(&clocks, d_elapsed, sizeof(clock_t), hipMemcpyDeviceToHost));\n        elapsed_clocks += clocks;\n\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    float avg_clocks            = float(elapsed_clocks) / g_timing_iterations;\n    float avg_clocks_per_item   = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockReduce::Sum clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockReduce::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) hipFree(d_in);\n    if (d_out) hipFree(d_out);\n    if (d_elapsed) hipFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations>] \"\n            \"[--grid-size=<grid size>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024, 1, BLOCK_REDUCE_RAKING>();\n    Test<512, 2, BLOCK_REDUCE_RAKING>();\n    Test<256, 4, BLOCK_REDUCE_RAKING>();\n    Test<128, 8, BLOCK_REDUCE_RAKING>();\n    Test<64, 16, BLOCK_REDUCE_RAKING>();\n    Test<32, 32, BLOCK_REDUCE_RAKING>();\n    Test<16, 64, BLOCK_REDUCE_RAKING>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<512, 2, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<256, 4, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<128, 8, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<64, 16, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<32, 32, BLOCK_REDUCE_WARP_REDUCTIONS>();\n    Test<16, 64, BLOCK_REDUCE_WARP_REDUCTIONS>();\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cub/examples/block/example_block_reduce.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_scan.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_scan.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockScanAlgorithm      ALGORITHM>\n__global__ void BlockPrefixSumKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockLoad<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE> BlockLoadT;\n\n    // Specialize BlockStore type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockStore<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_STORE_WARP_TRANSPOSE> BlockStoreT;\n\n    // Specialize BlockScan type for our thread block\n    typedef BlockScan<int, BLOCK_THREADS, ALGORITHM> BlockScanT;\n\n    // Shared memory\n    __shared__ union TempStorage\n    {\n        typename BlockLoadT::TempStorage    load;\n        typename BlockStoreT::TempStorage   store;\n        typename BlockScanT::TempStorage    scan;\n    } temp_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n\n    // Load items into a blocked arrangement\n    BlockLoadT(temp_storage.load).Load(d_in, data);\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute exclusive prefix sum\n    int aggregate;\n    BlockScanT(temp_storage.scan).ExclusiveSum(data, data, aggregate);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Store items from a blocked arrangement\n    BlockStoreT(temp_storage.store).Store(d_out, data);\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(\n    int *h_in,\n    int *h_reference,\n    int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n\n        h_reference[i] = inclusive;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block scan\n */\ntemplate <\n    int                 BLOCK_THREADS,\n    int                 ITEMS_PER_THREAD,\n    BlockScanAlgorithm  ALGORITHM>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_reference    = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    cudaMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    cudaMalloc((void**)&d_out,         sizeof(int) * (TILE_SIZE + 1));\n    cudaMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n    printf(\"BlockScan algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        (ALGORITHM == BLOCK_SCAN_RAKING) ? \"BLOCK_SCAN_RAKING\" : (ALGORITHM == BLOCK_SCAN_RAKING_MEMOIZE) ? \"BLOCK_SCAN_RAKING_MEMOIZE\" : \"BLOCK_SCAN_WARP_SCANS\",\n        TILE_SIZE, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n\n    // Run aggregate/prefix kernel\n    BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check results\n    printf(\"\\tOutput items: \");\n    int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    compare = CompareDeviceResults(&h_aggregate, d_out + TILE_SIZE, 1, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Run this several times and average the performance results\n    GpuTimer    timer;\n    float       elapsed_millis          = 0.0;\n    clock_t     elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Copy problem to device\n        cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n        timer.Start();\n\n        // Run aggregate/prefix kernel\n        BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        clock_t clocks;\n        CubDebugExit(cudaMemcpy(&clocks, d_elapsed, sizeof(clock_t), cudaMemcpyDeviceToHost));\n        elapsed_clocks += clocks;\n\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    float avg_clocks            = float(elapsed_clocks) / g_timing_iterations;\n    float avg_clocks_per_item   = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockScan::Sum clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockScan::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) cudaFree(d_in);\n    if (d_out) cudaFree(d_out);\n    if (d_elapsed) cudaFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations (default:%d)>]\"\n            \"[--grid-size=<grid size (default:%d)>]\"\n            \"[--v] \"\n            \"\\n\", argv[0], g_timing_iterations, g_grid_size);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024, 1, BLOCK_SCAN_RAKING>();\n    Test<512, 2, BLOCK_SCAN_RAKING>();\n    Test<256, 4, BLOCK_SCAN_RAKING>();\n    Test<128, 8, BLOCK_SCAN_RAKING>();\n    Test<64, 16, BLOCK_SCAN_RAKING>();\n    Test<32, 32, BLOCK_SCAN_RAKING>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<512, 2, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<256, 4, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<128, 8, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<64, 16, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<32, 32, BLOCK_SCAN_RAKING_MEMOIZE>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_SCAN_WARP_SCANS>();\n    Test<512, 2, BLOCK_SCAN_WARP_SCANS>();\n    Test<256, 4, BLOCK_SCAN_WARP_SCANS>();\n    Test<128, 8, BLOCK_SCAN_WARP_SCANS>();\n    Test<64, 16, BLOCK_SCAN_WARP_SCANS>();\n    Test<32, 32, BLOCK_SCAN_WARP_SCANS>();\n\n\n    return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_scan.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_scan.cuh>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <\n    int                     BLOCK_THREADS,\n    int                     ITEMS_PER_THREAD,\n    BlockScanAlgorithm      ALGORITHM>\n__global__ void BlockPrefixSumKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockLoad<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE> BlockLoadT;\n\n    // Specialize BlockStore type for our thread block (uses warp-striped loads for coalescing, then transposes in shared memory to a blocked arrangement)\n    typedef BlockStore<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_STORE_WARP_TRANSPOSE> BlockStoreT;\n\n    // Specialize BlockScan type for our thread block\n    typedef BlockScan<int, BLOCK_THREADS, ALGORITHM> BlockScanT;\n\n    // Shared memory\n    __shared__ union TempStorage\n    {\n        typename BlockLoadT::TempStorage    load;\n        typename BlockStoreT::TempStorage   store;\n        typename BlockScanT::TempStorage    scan;\n    } temp_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n\n    // Load items into a blocked arrangement\n    BlockLoadT(temp_storage.load).Load(d_in, data);\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute exclusive prefix sum\n    int aggregate;\n    BlockScanT(temp_storage.scan).ExclusiveSum(data, data, aggregate);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Barrier for smem reuse\n    __syncthreads();\n\n    // Store items from a blocked arrangement\n    BlockStoreT(temp_storage.store).Store(d_out, data);\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(\n    int *h_in,\n    int *h_reference,\n    int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n\n        h_reference[i] = inclusive;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block scan\n */\ntemplate <\n    int                 BLOCK_THREADS,\n    int                 ITEMS_PER_THREAD,\n    BlockScanAlgorithm  ALGORITHM>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_reference    = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    hipMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    hipMalloc((void**)&d_out,         sizeof(int) * (TILE_SIZE + 1));\n    hipMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Kernel props\n    int max_sm_occupancy;\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n    printf(\"BlockScan algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n        (ALGORITHM == BLOCK_SCAN_RAKING) ? \"BLOCK_SCAN_RAKING\" : (ALGORITHM == BLOCK_SCAN_RAKING_MEMOIZE) ? \"BLOCK_SCAN_RAKING_MEMOIZE\" : \"BLOCK_SCAN_WARP_SCANS\",\n        TILE_SIZE, g_timing_iterations, g_grid_size, BLOCK_THREADS, ITEMS_PER_THREAD, max_sm_occupancy);\n\n    // Run aggregate/prefix kernel\n    BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n        d_in,\n        d_out,\n        d_elapsed);\n\n    // Check results\n    printf(\"\\tOutput items: \");\n    int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    compare = CompareDeviceResults(&h_aggregate, d_out + TILE_SIZE, 1, g_verbose, g_verbose);\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Run this several times and average the performance results\n    GpuTimer    timer;\n    float       elapsed_millis          = 0.0;\n    clock_t     elapsed_clocks          = 0;\n\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Copy problem to device\n        hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n        timer.Start();\n\n        // Run aggregate/prefix kernel\n        BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        timer.Stop();\n        elapsed_millis += timer.ElapsedMillis();\n\n        // Copy clocks from device\n        clock_t clocks;\n        CubDebugExit(hipMemcpy(&clocks, d_elapsed, sizeof(clock_t), hipMemcpyDeviceToHost));\n        elapsed_clocks += clocks;\n\n    }\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Display timing results\n    float avg_millis            = elapsed_millis / g_timing_iterations;\n    float avg_items_per_sec     = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n    float avg_clocks            = float(elapsed_clocks) / g_timing_iterations;\n    float avg_clocks_per_item   = avg_clocks / TILE_SIZE;\n\n    printf(\"\\tAverage BlockScan::Sum clocks: %.3f\\n\", avg_clocks);\n    printf(\"\\tAverage BlockScan::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n    printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n    printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) hipFree(d_in);\n    if (d_out) hipFree(d_out);\n    if (d_elapsed) hipFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--i=<timing iterations (default:%d)>]\"\n            \"[--grid-size=<grid size (default:%d)>]\"\n            \"[--v] \"\n            \"\\n\", argv[0], g_timing_iterations, g_grid_size);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024, 1, BLOCK_SCAN_RAKING>();\n    Test<512, 2, BLOCK_SCAN_RAKING>();\n    Test<256, 4, BLOCK_SCAN_RAKING>();\n    Test<128, 8, BLOCK_SCAN_RAKING>();\n    Test<64, 16, BLOCK_SCAN_RAKING>();\n    Test<32, 32, BLOCK_SCAN_RAKING>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<512, 2, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<256, 4, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<128, 8, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<64, 16, BLOCK_SCAN_RAKING_MEMOIZE>();\n    Test<32, 32, BLOCK_SCAN_RAKING_MEMOIZE>();\n\n    printf(\"-------------\\n\");\n\n    Test<1024, 1, BLOCK_SCAN_WARP_SCANS>();\n    Test<512, 2, BLOCK_SCAN_WARP_SCANS>();\n    Test<256, 4, BLOCK_SCAN_WARP_SCANS>();\n    Test<128, 8, BLOCK_SCAN_WARP_SCANS>();\n    Test<64, 16, BLOCK_SCAN_WARP_SCANS>();\n    Test<32, 32, BLOCK_SCAN_WARP_SCANS>();\n\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cub/examples/block/example_block_scan.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce with dynamic shared memory\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce_dyn_smem.cu -I../.. -lcudart -O3 -std=c++14\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <stdio.h>\n#include <algorithm>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_reduce.cuh>\n\n#include \"../../test/test_util.h\"\n\n// Some implementation details rely on c++14\n#if CUB_CPP_DIALECT >= 2014\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS>\n__global__ void BlockReduceKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out          // Tile aggregate\n    )\n{\n    // Specialize BlockReduce type for our thread block\n    using BlockReduceT = cub::BlockReduce<int, BLOCK_THREADS>;\n    using TempStorageT = typename BlockReduceT::TempStorage;\n\n    union ShmemLayout\n    {\n      TempStorageT reduce;\n      int aggregate;\n    };\n\n    // shared memory byte-array\n    extern __shared__ __align__(alignof(ShmemLayout)) char smem[];\n    \n    // cast to lvalue reference of expected type\n    auto& temp_storage = reinterpret_cast<TempStorageT&>(smem);\n\n    int data = d_in[threadIdx.x];\n\n    // Compute sum\n    int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n    // block-wide sync barrier necessary to re-use shared mem safely\n    __syncthreads();\n    int* smem_integers = reinterpret_cast<int*>(smem);\n    if (threadIdx.x == 0) smem_integers[0] = aggregate;\n\n    // sync to make new shared value available to all threads\n    __syncthreads();\n    aggregate = smem_integers[0];\n\n    // all threads write the aggregate to output\n    d_out[threadIdx.x] = aggregate;\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int *h_in, int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS>\nvoid Test()\n{\n    // Allocate host arrays\n    int *h_in = new int[BLOCK_THREADS];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, BLOCK_THREADS);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    cudaMalloc((void**)&d_in,          sizeof(int) * BLOCK_THREADS);\n    cudaMalloc((void**)&d_out,         sizeof(int) * BLOCK_THREADS);\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < BLOCK_THREADS; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * BLOCK_THREADS, cudaMemcpyHostToDevice);\n\n    // determine necessary storage size:\n    auto block_reduce_temp_bytes =\n      sizeof(typename cub::BlockReduce<int, BLOCK_THREADS>::TempStorage);\n    // finally, we need to make sure that we can hold at least one integer\n    // needed in the kernel to exchange data after reduction\n    auto smem_size = (std::max)(1 * sizeof(int), block_reduce_temp_bytes);\n\n    // use default stream\n    cudaStream_t stream = NULL;\n\n    // Run reduction kernel\n    BlockReduceKernel<BLOCK_THREADS>\n        <<<g_grid_size, BLOCK_THREADS, smem_size, stream>>>(\n            d_in,\n            d_out);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    int compare = 0;\n    for (int i = 0; i < BLOCK_THREADS; i++) {\n        compare = compare || CompareDeviceResults(\n            &h_aggregate, d_out + i, 1, g_verbose, g_verbose);\n    }\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (d_in) cudaFree(d_in);\n    if (d_out) cudaFree(d_out);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--grid-size=<grid size>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024>();\n    Test<512>();\n    Test<256>();\n    Test<128>();\n    Test<64>();\n    Test<32>();\n    Test<16>();\n\n    return 0;\n}\n\n#else // < C++14\n\nint main() {}\n\n#endif\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce with dynamic shared memory\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce_dyn_smem.cu -I../.. -lcudart -O3 -std=c++14\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n#include <algorithm>\n#include <iostream>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/block_reduce.cuh>\n\n#include \"../../test/test_util.h\"\n\n// Some implementation details rely on c++14\n#if CUB_CPP_DIALECT >= 2014\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS>\n__global__ void BlockReduceKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out          // Tile aggregate\n    )\n{\n    // Specialize BlockReduce type for our thread block\n    using BlockReduceT = hipcub::BlockReduce<int, BLOCK_THREADS>;\n    using TempStorageT = typename BlockReduceT::TempStorage;\n\n    union ShmemLayout\n    {\n      TempStorageT reduce;\n      int aggregate;\n    };\n\n    // shared memory byte-array\n    extern __shared__ __align__(alignof(ShmemLayout)) char smem[];\n    \n    // cast to lvalue reference of expected type\n    auto& temp_storage = reinterpret_cast<TempStorageT&>(smem);\n\n    int data = d_in[threadIdx.x];\n\n    // Compute sum\n    int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n    // block-wide sync barrier necessary to re-use shared mem safely\n    __syncthreads();\n    int* smem_integers = reinterpret_cast<int*>(smem);\n    if (threadIdx.x == 0) smem_integers[0] = aggregate;\n\n    // sync to make new shared value available to all threads\n    __syncthreads();\n    aggregate = smem_integers[0];\n\n    // all threads write the aggregate to output\n    d_out[threadIdx.x] = aggregate;\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int *h_in, int num_items)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_items; ++i)\n    {\n        h_in[i] = i % 17;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS>\nvoid Test()\n{\n    // Allocate host arrays\n    int *h_in = new int[BLOCK_THREADS];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, BLOCK_THREADS);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    hipMalloc((void**)&d_in,          sizeof(int) * BLOCK_THREADS);\n    hipMalloc((void**)&d_out,         sizeof(int) * BLOCK_THREADS);\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < BLOCK_THREADS; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * BLOCK_THREADS, hipMemcpyHostToDevice);\n\n    // determine necessary storage size:\n    auto block_reduce_temp_bytes =\n      sizeof(typename hipcub::BlockReduce<int, BLOCK_THREADS>::TempStorage);\n    // finally, we need to make sure that we can hold at least one integer\n    // needed in the kernel to exchange data after reduction\n    auto smem_size = (std::max)(1 * sizeof(int), block_reduce_temp_bytes);\n\n    // use default stream\n    hipStream_t stream = NULL;\n\n    // Run reduction kernel\n    BlockReduceKernel<BLOCK_THREADS>\n        <<<g_grid_size, BLOCK_THREADS, smem_size, stream>>>(\n            d_in,\n            d_out);\n\n    // Check total aggregate\n    printf(\"\\tAggregate: \");\n    int compare = 0;\n    for (int i = 0; i < BLOCK_THREADS; i++) {\n        compare = compare || CompareDeviceResults(\n            &h_aggregate, d_out + i, 1, g_verbose, g_verbose);\n    }\n    printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check for kernel errors and STDIO from the kernel, if any\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (d_in) hipFree(d_in);\n    if (d_out) hipFree(d_out);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--grid-size=<grid size>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Run tests\n    Test<1024>();\n    Test<512>();\n    Test<256>();\n    Test<128>();\n    Test<64>();\n    Test<32>();\n    Test<16>();\n\n    return 0;\n}\n\n#else // < C++14\n\nint main() {}\n\n#endif\n", "source": "/kaggle/working/cub/examples/block/example_block_reduce_dyn_smem.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <iostream>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(cub::ScanTileState<MessageT> tile_state)\n{\n  using scan_op_t         = cub::Sum;\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n  using tile_prefix_op    = cub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  const unsigned int threads_in_warp = 32;\n  const unsigned int tid             = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // Compute block aggregate\n  MessageT block_aggregate = blockIdx.x;\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, block_aggregate);\n      printf(\"tile %d: inclusive = %d\\n\", tile_idx, block_aggregate);\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(block_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, block_aggregate);\n        printf(\"tile %d: exclusive = %d inclusive = %d\\n\",\n               tile_idx,\n               exclusive_prefix,\n               inclusive_prefix);\n      }\n    }\n  }\n}\n\ntemplate <class MessageT>\nvoid decoupled_look_back_example(int blocks_in_grid)\n{\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(blocks_in_grid, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  tile_status.Init(blocks_in_grid, d_temp_storage, temp_storage_bytes);\n  const unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid   = cub::DivideAndRoundUp(blocks_in_grid,\n                                                                 threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, blocks_in_grid);\n\n  // Launch decoupled look-back\n  const unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<blocks_in_grid, threads_in_block>>>(tile_status);\n\n  // Wait for kernel to finish\n  cudaDeviceSynchronize();\n}\n\nint main() { decoupled_look_back_example<int>(14); }\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <iostream>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(hipcub::ScanTileState<MessageT> tile_state)\n{\n  using scan_op_t         = hipcub::Sum;\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n  using tile_prefix_op    = hipcub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  const unsigned int threads_in_warp = 32;\n  const unsigned int tid             = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // Compute block aggregate\n  MessageT block_aggregate = blockIdx.x;\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, block_aggregate);\n      printf(\"tile %d: inclusive = %d\\n\", tile_idx, block_aggregate);\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(block_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, block_aggregate);\n        printf(\"tile %d: exclusive = %d inclusive = %d\\n\",\n               tile_idx,\n               exclusive_prefix,\n               inclusive_prefix);\n      }\n    }\n  }\n}\n\ntemplate <class MessageT>\nvoid decoupled_look_back_example(int blocks_in_grid)\n{\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(blocks_in_grid, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  tile_status.Init(blocks_in_grid, d_temp_storage, temp_storage_bytes);\n  const unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid   = hipcub::DivideAndRoundUp(blocks_in_grid,\n                                                                 threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, blocks_in_grid);\n\n  // Launch decoupled look-back\n  const unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<blocks_in_grid, threads_in_block>>>(tile_status);\n\n  // Wait for kernel to finish\n  hipDeviceSynchronize();\n}\n\nint main() { decoupled_look_back_example<int>(14); }\n", "source": "/kaggle/working/cub/examples/device/example_device_decoupled_look_back.cu"}
{"input": "#include <thrust/distance.h>\n#include <thrust/binary_search.h>\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n#include <thrust/count.h>\n#include <thrust/for_each.h>\n#include <thrust/tabulate.h>\n\n#include <cub/device/device_copy.cuh>\n\n#include <cstdint>\n#include <random>\n\n#include \"thrust/scan.h\"\n#include <curand.h>\n#include <nvbench_helper.cuh>\n\nclass generator_t\n{\npublic:\n  generator_t();\n  ~generator_t();\n\n  template <typename T>\n  void operator()(seed_t seed,\n                  thrust::device_vector<T> &data,\n                  bit_entropy entropy,\n                  T min = std::numeric_limits<T>::lowest(),\n                  T max = std::numeric_limits<T>::max());\n\n  template <typename T>\n  thrust::device_vector<T> power_law_segment_offsets(seed_t seed,\n                                                     std::size_t total_elements,\n                                                     std::size_t total_segments);\n\n  double *distribution();\n  curandGenerator_t &gen() { return m_gen; }\n\n  double *prepare_random_generator(seed_t seed, std::size_t num_items);\n  double *prepare_lognormal_random_generator(seed_t seed, std::size_t num_items);\n\nprivate:\n  curandGenerator_t m_gen;\n  thrust::device_vector<double> m_distribution;\n};\n\ntemplate <typename T>\nstruct random_to_item_t\n{\n  double m_min;\n  double m_max;\n\n  __host__ __device__ random_to_item_t(T min, T max)\n      : m_min(static_cast<double>(min))\n      , m_max(static_cast<double>(max))\n  {}\n\n  __host__ __device__ T operator()(double random_value)\n  {\n    return static_cast<T>((m_max - m_min) * random_value + m_min);\n  }\n};\n\ngenerator_t::generator_t() { curandCreateGenerator(&m_gen, CURAND_RNG_PSEUDO_DEFAULT); }\n\ngenerator_t::~generator_t() { curandDestroyGenerator(m_gen); }\n\ndouble *generator_t::distribution() { return thrust::raw_pointer_cast(m_distribution.data()); }\n\ndouble *generator_t::prepare_random_generator(seed_t seed, std::size_t num_items)\n{\n  curandSetPseudoRandomGeneratorSeed(m_gen, seed.get());\n\n  m_distribution.resize(num_items);\n  curandGenerateUniformDouble(m_gen, this->distribution(), num_items);\n\n  return this->distribution();\n}\n\ndouble *generator_t::prepare_lognormal_random_generator(seed_t seed, std::size_t num_segments)\n{\n  curandSetPseudoRandomGeneratorSeed(m_gen, seed.get());\n\n  m_distribution.resize(num_segments);\n\n  const double mean = 3.0;\n  const double sigma = 1.2;\n  curandGenerateLogNormalDouble(m_gen, this->distribution(), num_segments, mean, sigma);\n\n  return this->distribution();\n}\n\ntemplate <class T>\n__global__ void and_kernel(T *d_in, T *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i] = d_in[i] & d_tmp[i];\n  }\n}\n\n__global__ void and_kernel(float *d_in, float *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    std::uint32_t result = reinterpret_cast<std::uint32_t &>(d_in[i]) &\n                           reinterpret_cast<std::uint32_t &>(d_tmp[i]);\n    d_in[i] = reinterpret_cast<float &>(result);\n  }\n}\n\n__global__ void and_kernel(double *d_in, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    std::uint64_t result = reinterpret_cast<std::uint64_t &>(d_in[i]) &\n                           reinterpret_cast<std::uint64_t &>(d_tmp[i]);\n    d_in[i] = reinterpret_cast<double &>(result);\n  }\n}\n\n__global__ void and_kernel(complex *d_in, complex *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    double in_real = d_in[i].real();\n    double in_imag = d_in[i].imag();\n\n    double tmp_real = d_tmp[i].real();\n    double tmp_imag = d_tmp[i].imag();\n\n    std::uint64_t result_real = reinterpret_cast<std::uint64_t &>(in_real) &\n                                reinterpret_cast<std::uint64_t &>(tmp_real);\n\n    std::uint64_t result_imag = reinterpret_cast<std::uint64_t &>(in_imag) &\n                                reinterpret_cast<std::uint64_t &>(tmp_imag);\n\n    d_in[i].real(reinterpret_cast<double &>(result_real));\n    d_in[i].imag(reinterpret_cast<double &>(result_imag));\n  }\n}\n\n__global__ void set_real_kernel(complex *d_in, complex min, complex max, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i].real(random_to_item_t<double>{min.real(), max.real()}(d_tmp[i]));\n  }\n}\n\n__global__ void set_imag_kernel(complex *d_in, complex min, complex max, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i].imag(random_to_item_t<double>{min.imag(), max.imag()}(d_tmp[i]));\n  }\n}\n\ntemplate <class T>\nstruct lognormal_transformer_t\n{\n  std::size_t total_elements;\n  double sum;\n\n  __device__ T operator()(double val)\n  {\n    return floor(val * total_elements / sum);\n  }\n};\n\ntemplate <class T>\n__global__ void lognormal_adjust_kernel(T *segment_sizes, std::size_t diff)\n{\n  const unsigned tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  if (tid < diff)\n  {\n    segment_sizes[tid]++;\n  }\n}\n\ntemplate <class T>\nthrust::device_vector<T> generator_t::power_law_segment_offsets(seed_t seed,\n                                                                std::size_t total_elements,\n                                                                std::size_t total_segments)\n{\n  thrust::device_vector<T> segment_sizes(total_segments + 1);\n  prepare_lognormal_random_generator(seed, total_segments);\n\n  if (thrust::count(m_distribution.begin(), m_distribution.end(), 0.0) == total_segments)\n  {\n    thrust::fill_n(m_distribution.begin(), total_segments, 1.0);\n  }\n\n  double sum = thrust::reduce(m_distribution.begin(), m_distribution.end());\n  thrust::transform(m_distribution.begin(),\n                    m_distribution.end(),\n                    segment_sizes.begin(),\n                    lognormal_transformer_t<T>{total_elements, sum});\n\n  const int diff = total_elements - thrust::reduce(segment_sizes.begin(), segment_sizes.end());\n  const int block_size = 256;\n  const int grid_size  = (std::abs(diff) + block_size - 1) / block_size;\n\n  T *d_segment_sizes = thrust::raw_pointer_cast(segment_sizes.data());\n  lognormal_adjust_kernel<T><<<grid_size, block_size>>>(d_segment_sizes, diff);\n\n  thrust::exclusive_scan(segment_sizes.begin(), segment_sizes.end(), segment_sizes.begin());\n\n  return segment_sizes;\n}\n\ntemplate <class T>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<T> &data,\n                             bit_entropy entropy,\n                             T min,\n                             T max)\n{\n  switch (entropy)\n  {\n    case bit_entropy::_1_000: {\n      prepare_random_generator(seed, data.size());\n\n      thrust::transform(m_distribution.begin(),\n                        m_distribution.end(),\n                        data.begin(),\n                        random_to_item_t<T>(min, max));\n      return;\n    }\n    case bit_entropy::_0_000: {\n      std::mt19937 rng;\n      rng.seed(static_cast<std::mt19937::result_type>(seed.get()));\n      std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n      T random_value = random_to_item_t<T>(min, max)(dist(rng));\n      thrust::fill(data.begin(), data.end(), random_value);\n      return;\n    }\n    default: {\n      prepare_random_generator(seed, data.size());\n\n      thrust::transform(m_distribution.begin(),\n                        m_distribution.end(),\n                        data.begin(),\n                        random_to_item_t<T>(min, max));\n\n      const int number_of_steps = static_cast<int>(entropy);\n      thrust::device_vector<T> tmp(data.size());\n      const int threads_in_block = 256;\n      const int blocks_in_grid   = (data.size() + threads_in_block - 1) / threads_in_block;\n\n      for (int i = 0; i < number_of_steps; i++, ++seed)\n      {\n        (*this)(seed, tmp, bit_entropy::_1_000, min, max);\n        and_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                         thrust::raw_pointer_cast(tmp.data()),\n                                                         data.size());\n        cudaStreamSynchronize(0);\n      }\n      return;\n    }\n  };\n}\n\ntemplate <>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<complex> &data,\n                             bit_entropy entropy,\n                             complex min,\n                             complex max)\n{\n  const int threads_in_block = 256;\n  const int blocks_in_grid   = (data.size() + threads_in_block - 1) / threads_in_block;\n\n  switch (entropy)\n  {\n    case bit_entropy::_1_000: {\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_real_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_imag_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      return;\n    }\n    case bit_entropy::_0_000: {\n      std::mt19937 rng;\n      rng.seed(static_cast<std::mt19937::result_type>(seed.get()));\n      std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n      double random_imag = random_to_item_t<double>(min.imag(), max.imag())(dist(rng));\n      double random_real = random_to_item_t<double>(min.imag(), max.imag())(dist(rng));\n      complex random_value(random_real, random_imag);\n      thrust::fill(data.begin(), data.end(), random_value);\n      return;\n    }\n    default: {\n      prepare_random_generator(seed, data.size());\n\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_real_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_imag_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n\n      const int number_of_steps = static_cast<int>(entropy);\n      thrust::device_vector<complex> tmp(data.size());\n\n      for (int i = 0; i < number_of_steps; i++, ++seed)\n      {\n        (*this)(seed, tmp, bit_entropy::_1_000, min, max);\n        and_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                         thrust::raw_pointer_cast(tmp.data()),\n                                                         data.size());\n        cudaStreamSynchronize(0);\n      }\n      return;\n    }\n  };\n}\n\nstruct random_to_probability_t\n{\n  double m_probability;\n\n  __host__ __device__ bool operator()(double random_value)\n  {\n    return random_value < m_probability;\n  }\n};\n\ntemplate <>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<bool> &data,\n                             bit_entropy entropy,\n                             bool /* min */,\n                             bool /* max */)\n{\n  if (entropy == bit_entropy::_0_000)\n  {\n    thrust::fill(data.begin(), data.end(), false);\n  }\n  else if (entropy == bit_entropy::_1_000)\n  {\n    thrust::fill(data.begin(), data.end(), true);\n  }\n  else\n  {\n    prepare_random_generator(seed, data.size());\n    thrust::transform(m_distribution.begin(),\n                      m_distribution.end(),\n                      data.begin(),\n                      random_to_probability_t{entropy_to_probability(entropy)});\n  }\n}\n\ntemplate <typename T>\nvoid gen(seed_t seed, thrust::device_vector<T> &data, bit_entropy entropy, T min, T max)\n{\n  generator_t{}(seed, data, entropy, min, max);\n}\n\n#define INSTANTIATE_RND(TYPE)                                                                      \\\n  template void gen<TYPE>(seed_t,                                                                  \\\n                          thrust::device_vector<TYPE> & data,                                      \\\n                          bit_entropy,                                                             \\\n                          TYPE min,                                                                \\\n                          TYPE max)\n\n#define INSTANTIATE(TYPE) INSTANTIATE_RND(TYPE);\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n\n#undef INSTANTIATE\n#undef INSTANTIATE_RND\n\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_offsets(seed_t seed,\n                                               std::size_t total_elements,\n                                               std::size_t total_segments)\n{\n  return generator_t{}.power_law_segment_offsets<T>(seed, total_elements, total_segments);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_power_law_offsets<TYPE>(seed_t, std::size_t, std::size_t)\n\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\n#undef INSTANTIATE\n\n\ntemplate <class T>\nstruct offset_to_iterator_t\n{\n  T *base_it;\n\n  __host__ __device__ __forceinline__ T*\n  operator()(std::size_t offset) const\n  {\n    return base_it + offset;\n  }\n};\n\ntemplate <class T>\nstruct repeat_index_t\n{\n  __host__ __device__ __forceinline__ thrust::constant_iterator<T> operator()(std::size_t i)\n  {\n    return thrust::constant_iterator<T>(static_cast<T>(i));\n  }\n};\n\nstruct offset_to_size_t\n{\n  std::size_t *offsets = nullptr;\n\n  __host__ __device__ __forceinline__ std::size_t operator()(std::size_t i)\n  {\n    return offsets[i + 1] - offsets[i];\n  }\n};\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_key_segments(seed_t seed,\n                                                    std::size_t total_elements,\n                                                    thrust::device_vector<std::size_t> &segment_offsets)\n{\n  std::size_t total_segments = segment_offsets.size() - 1;\n  thrust::device_vector<T> out(total_elements);\n  std::size_t *d_offsets = thrust::raw_pointer_cast(segment_offsets.data());\n  T *d_out = thrust::raw_pointer_cast(out.data());\n\n  thrust::counting_iterator<int> iota(0);\n  offset_to_iterator_t<T> dst_transform_op{d_out};\n\n  auto d_range_srcs = thrust::make_transform_iterator(iota, repeat_index_t<T>{});\n  auto d_range_dsts = thrust::make_transform_iterator(d_offsets, dst_transform_op);\n  auto d_range_sizes = thrust::make_transform_iterator(iota, offset_to_size_t{d_offsets});\n\n  std::uint8_t *d_temp_storage = nullptr;\n  std::size_t temp_storage_bytes = 0;\n  cub::DeviceCopy::Batched(d_temp_storage,\n                           temp_storage_bytes,\n                           d_range_srcs,\n                           d_range_dsts,\n                           d_range_sizes,\n                           total_segments);\n \n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  cub::DeviceCopy::Batched(d_temp_storage,\n                           temp_storage_bytes,\n                           d_range_srcs,\n                           d_range_dsts,\n                           d_range_sizes,\n                           total_segments);\n  cudaDeviceSynchronize();\n\n  return out;\n}\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_key_segments(seed_t seed,\n                                                    std::size_t total_elements,\n                                                    std::size_t total_segments)\n{\n  thrust::device_vector<std::size_t> segment_offsets =\n    gen_power_law_offsets<std::size_t>(seed, total_elements, total_segments);\n  return gen_power_law_key_segments<T>(seed, total_elements, segment_offsets);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_power_law_key_segments<TYPE>(seed_t,                    \\\n                                                                        std::size_t,               \\\n                                                                        std::size_t)\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n#undef INSTANTIATE\n\ntemplate <class T>\nstruct gt_t\n{\n  T val;\n\n  __device__ bool operator()(T x)\n  {\n    return x > val;\n  }\n};\n\ntemplate <typename T>\nthrust::device_vector<T> gen_uniform_offsets(seed_t seed,\n                                             T total_elements,\n                                             T min_segment_size,\n                                             T max_segment_size)\n{\n  thrust::device_vector<T> segment_offsets(total_elements + 2);\n  gen(seed, segment_offsets, bit_entropy::_1_000, min_segment_size, max_segment_size);\n  segment_offsets[total_elements] = total_elements + 1;\n  thrust::exclusive_scan(segment_offsets.begin(), segment_offsets.end(), segment_offsets.begin());\n  typename thrust::device_vector<T>::iterator iter =\n    thrust::find_if(segment_offsets.begin(), segment_offsets.end(), gt_t<T>{total_elements});\n  *iter = total_elements;\n  segment_offsets.erase(iter + 1, segment_offsets.end());\n  return segment_offsets;\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_uniform_offsets<TYPE>(seed_t, TYPE, TYPE, TYPE)\n\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\n\n#undef INSTANTIATE\n\n/**\n * @brief Generates a vector of random key segments.\n *\n * Not all parameter combinations can be satisfied. For instance, if the total\n * elements is less than the minimal segment size, the function will return a\n * vector with a single element that is outside of the requested range. \n * At most one segment can be out of the requested range.\n */\ntemplate <typename T>\nthrust::device_vector<T> gen_uniform_key_segments(seed_t seed,\n                                                  std::size_t total_elements,\n                                                  std::size_t min_segment_size,\n                                                  std::size_t max_segment_size)\n{\n  thrust::device_vector<std::size_t> segment_offsets =\n    gen_uniform_offsets(seed, total_elements, min_segment_size, max_segment_size);\n  return gen_power_law_key_segments<T>(seed, total_elements, segment_offsets);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_uniform_key_segments<TYPE>(seed_t,                      \\\n                                                                      std::size_t,                 \\\n                                                                      std::size_t,                 \\\n                                                                      std::size_t)\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n#undef INSTANTIATE\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/distance.h>\n#include <thrust/binary_search.h>\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n#include <thrust/count.h>\n#include <thrust/for_each.h>\n#include <thrust/tabulate.h>\n\n#include <cub/device/device_copy.cuh>\n\n#include <cstdint>\n#include <random>\n\n#include \"thrust/scan.h\"\n#include <hiprand/hiprand.h>\n#include <nvbench_helper.cuh>\n\nclass generator_t\n{\npublic:\n  generator_t();\n  ~generator_t();\n\n  template <typename T>\n  void operator()(seed_t seed,\n                  thrust::device_vector<T> &data,\n                  bit_entropy entropy,\n                  T min = std::numeric_limits<T>::lowest(),\n                  T max = std::numeric_limits<T>::max());\n\n  template <typename T>\n  thrust::device_vector<T> power_law_segment_offsets(seed_t seed,\n                                                     std::size_t total_elements,\n                                                     std::size_t total_segments);\n\n  double *distribution();\n  hiprandGenerator_t &gen() { return m_gen; }\n\n  double *prepare_random_generator(seed_t seed, std::size_t num_items);\n  double *prepare_lognormal_random_generator(seed_t seed, std::size_t num_items);\n\nprivate:\n  hiprandGenerator_t m_gen;\n  thrust::device_vector<double> m_distribution;\n};\n\ntemplate <typename T>\nstruct random_to_item_t\n{\n  double m_min;\n  double m_max;\n\n  __host__ __device__ random_to_item_t(T min, T max)\n      : m_min(static_cast<double>(min))\n      , m_max(static_cast<double>(max))\n  {}\n\n  __host__ __device__ T operator()(double random_value)\n  {\n    return static_cast<T>((m_max - m_min) * random_value + m_min);\n  }\n};\n\ngenerator_t::generator_t() { hiprandCreateGenerator(&m_gen, HIPRAND_RNG_PSEUDO_DEFAULT); }\n\ngenerator_t::~generator_t() { hiprandDestroyGenerator(m_gen); }\n\ndouble *generator_t::distribution() { return thrust::raw_pointer_cast(m_distribution.data()); }\n\ndouble *generator_t::prepare_random_generator(seed_t seed, std::size_t num_items)\n{\n  hiprandSetPseudoRandomGeneratorSeed(m_gen, seed.get());\n\n  m_distribution.resize(num_items);\n  hiprandGenerateUniformDouble(m_gen, this->distribution(), num_items);\n\n  return this->distribution();\n}\n\ndouble *generator_t::prepare_lognormal_random_generator(seed_t seed, std::size_t num_segments)\n{\n  hiprandSetPseudoRandomGeneratorSeed(m_gen, seed.get());\n\n  m_distribution.resize(num_segments);\n\n  const double mean = 3.0;\n  const double sigma = 1.2;\n  hiprandGenerateLogNormalDouble(m_gen, this->distribution(), num_segments, mean, sigma);\n\n  return this->distribution();\n}\n\ntemplate <class T>\n__global__ void and_kernel(T *d_in, T *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i] = d_in[i] & d_tmp[i];\n  }\n}\n\n__global__ void and_kernel(float *d_in, float *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    std::uint32_t result = reinterpret_cast<std::uint32_t &>(d_in[i]) &\n                           reinterpret_cast<std::uint32_t &>(d_tmp[i]);\n    d_in[i] = reinterpret_cast<float &>(result);\n  }\n}\n\n__global__ void and_kernel(double *d_in, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    std::uint64_t result = reinterpret_cast<std::uint64_t &>(d_in[i]) &\n                           reinterpret_cast<std::uint64_t &>(d_tmp[i]);\n    d_in[i] = reinterpret_cast<double &>(result);\n  }\n}\n\n__global__ void and_kernel(complex *d_in, complex *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    double in_real = d_in[i].real();\n    double in_imag = d_in[i].imag();\n\n    double tmp_real = d_tmp[i].real();\n    double tmp_imag = d_tmp[i].imag();\n\n    std::uint64_t result_real = reinterpret_cast<std::uint64_t &>(in_real) &\n                                reinterpret_cast<std::uint64_t &>(tmp_real);\n\n    std::uint64_t result_imag = reinterpret_cast<std::uint64_t &>(in_imag) &\n                                reinterpret_cast<std::uint64_t &>(tmp_imag);\n\n    d_in[i].real(reinterpret_cast<double &>(result_real));\n    d_in[i].imag(reinterpret_cast<double &>(result_imag));\n  }\n}\n\n__global__ void set_real_kernel(complex *d_in, complex min, complex max, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i].real(random_to_item_t<double>{min.real(), max.real()}(d_tmp[i]));\n  }\n}\n\n__global__ void set_imag_kernel(complex *d_in, complex min, complex max, double *d_tmp, std::size_t n)\n{\n  const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < n)\n  {\n    d_in[i].imag(random_to_item_t<double>{min.imag(), max.imag()}(d_tmp[i]));\n  }\n}\n\ntemplate <class T>\nstruct lognormal_transformer_t\n{\n  std::size_t total_elements;\n  double sum;\n\n  __device__ T operator()(double val)\n  {\n    return floor(val * total_elements / sum);\n  }\n};\n\ntemplate <class T>\n__global__ void lognormal_adjust_kernel(T *segment_sizes, std::size_t diff)\n{\n  const unsigned tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  if (tid < diff)\n  {\n    segment_sizes[tid]++;\n  }\n}\n\ntemplate <class T>\nthrust::device_vector<T> generator_t::power_law_segment_offsets(seed_t seed,\n                                                                std::size_t total_elements,\n                                                                std::size_t total_segments)\n{\n  thrust::device_vector<T> segment_sizes(total_segments + 1);\n  prepare_lognormal_random_generator(seed, total_segments);\n\n  if (thrust::count(m_distribution.begin(), m_distribution.end(), 0.0) == total_segments)\n  {\n    thrust::fill_n(m_distribution.begin(), total_segments, 1.0);\n  }\n\n  double sum = thrust::reduce(m_distribution.begin(), m_distribution.end());\n  thrust::transform(m_distribution.begin(),\n                    m_distribution.end(),\n                    segment_sizes.begin(),\n                    lognormal_transformer_t<T>{total_elements, sum});\n\n  const int diff = total_elements - thrust::reduce(segment_sizes.begin(), segment_sizes.end());\n  const int block_size = 256;\n  const int grid_size  = (std::abs(diff) + block_size - 1) / block_size;\n\n  T *d_segment_sizes = thrust::raw_pointer_cast(segment_sizes.data());\n  lognormal_adjust_kernel<T><<<grid_size, block_size>>>(d_segment_sizes, diff);\n\n  thrust::exclusive_scan(segment_sizes.begin(), segment_sizes.end(), segment_sizes.begin());\n\n  return segment_sizes;\n}\n\ntemplate <class T>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<T> &data,\n                             bit_entropy entropy,\n                             T min,\n                             T max)\n{\n  switch (entropy)\n  {\n    case bit_entropy::_1_000: {\n      prepare_random_generator(seed, data.size());\n\n      thrust::transform(m_distribution.begin(),\n                        m_distribution.end(),\n                        data.begin(),\n                        random_to_item_t<T>(min, max));\n      return;\n    }\n    case bit_entropy::_0_000: {\n      std::mt19937 rng;\n      rng.seed(static_cast<std::mt19937::result_type>(seed.get()));\n      std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n      T random_value = random_to_item_t<T>(min, max)(dist(rng));\n      thrust::fill(data.begin(), data.end(), random_value);\n      return;\n    }\n    default: {\n      prepare_random_generator(seed, data.size());\n\n      thrust::transform(m_distribution.begin(),\n                        m_distribution.end(),\n                        data.begin(),\n                        random_to_item_t<T>(min, max));\n\n      const int number_of_steps = static_cast<int>(entropy);\n      thrust::device_vector<T> tmp(data.size());\n      const int threads_in_block = 256;\n      const int blocks_in_grid   = (data.size() + threads_in_block - 1) / threads_in_block;\n\n      for (int i = 0; i < number_of_steps; i++, ++seed)\n      {\n        (*this)(seed, tmp, bit_entropy::_1_000, min, max);\n        and_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                         thrust::raw_pointer_cast(tmp.data()),\n                                                         data.size());\n        hipStreamSynchronize(0);\n      }\n      return;\n    }\n  };\n}\n\ntemplate <>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<complex> &data,\n                             bit_entropy entropy,\n                             complex min,\n                             complex max)\n{\n  const int threads_in_block = 256;\n  const int blocks_in_grid   = (data.size() + threads_in_block - 1) / threads_in_block;\n\n  switch (entropy)\n  {\n    case bit_entropy::_1_000: {\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_real_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_imag_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      return;\n    }\n    case bit_entropy::_0_000: {\n      std::mt19937 rng;\n      rng.seed(static_cast<std::mt19937::result_type>(seed.get()));\n      std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n      double random_imag = random_to_item_t<double>(min.imag(), max.imag())(dist(rng));\n      double random_real = random_to_item_t<double>(min.imag(), max.imag())(dist(rng));\n      complex random_value(random_real, random_imag);\n      thrust::fill(data.begin(), data.end(), random_value);\n      return;\n    }\n    default: {\n      prepare_random_generator(seed, data.size());\n\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_real_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n      prepare_random_generator(seed, data.size()); ++seed;\n      set_imag_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                            min,\n                                                            max,\n                                                            thrust::raw_pointer_cast(m_distribution.data()),\n                                                            data.size());\n\n      const int number_of_steps = static_cast<int>(entropy);\n      thrust::device_vector<complex> tmp(data.size());\n\n      for (int i = 0; i < number_of_steps; i++, ++seed)\n      {\n        (*this)(seed, tmp, bit_entropy::_1_000, min, max);\n        and_kernel<<<blocks_in_grid, threads_in_block>>>(thrust::raw_pointer_cast(data.data()),\n                                                         thrust::raw_pointer_cast(tmp.data()),\n                                                         data.size());\n        hipStreamSynchronize(0);\n      }\n      return;\n    }\n  };\n}\n\nstruct random_to_probability_t\n{\n  double m_probability;\n\n  __host__ __device__ bool operator()(double random_value)\n  {\n    return random_value < m_probability;\n  }\n};\n\ntemplate <>\nvoid generator_t::operator()(seed_t seed,\n                             thrust::device_vector<bool> &data,\n                             bit_entropy entropy,\n                             bool /* min */,\n                             bool /* max */)\n{\n  if (entropy == bit_entropy::_0_000)\n  {\n    thrust::fill(data.begin(), data.end(), false);\n  }\n  else if (entropy == bit_entropy::_1_000)\n  {\n    thrust::fill(data.begin(), data.end(), true);\n  }\n  else\n  {\n    prepare_random_generator(seed, data.size());\n    thrust::transform(m_distribution.begin(),\n                      m_distribution.end(),\n                      data.begin(),\n                      random_to_probability_t{entropy_to_probability(entropy)});\n  }\n}\n\ntemplate <typename T>\nvoid gen(seed_t seed, thrust::device_vector<T> &data, bit_entropy entropy, T min, T max)\n{\n  generator_t{}(seed, data, entropy, min, max);\n}\n\n#define INSTANTIATE_RND(TYPE)                                                                      \\\n  template void gen<TYPE>(seed_t,                                                                  \\\n                          thrust::device_vector<TYPE> & data,                                      \\\n                          bit_entropy,                                                             \\\n                          TYPE min,                                                                \\\n                          TYPE max)\n\n#define INSTANTIATE(TYPE) INSTANTIATE_RND(TYPE);\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n\n#undef INSTANTIATE\n#undef INSTANTIATE_RND\n\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_offsets(seed_t seed,\n                                               std::size_t total_elements,\n                                               std::size_t total_segments)\n{\n  return generator_t{}.power_law_segment_offsets<T>(seed, total_elements, total_segments);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_power_law_offsets<TYPE>(seed_t, std::size_t, std::size_t)\n\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\n#undef INSTANTIATE\n\n\ntemplate <class T>\nstruct offset_to_iterator_t\n{\n  T *base_it;\n\n  __host__ __device__ __forceinline__ T*\n  operator()(std::size_t offset) const\n  {\n    return base_it + offset;\n  }\n};\n\ntemplate <class T>\nstruct repeat_index_t\n{\n  __host__ __device__ __forceinline__ thrust::constant_iterator<T> operator()(std::size_t i)\n  {\n    return thrust::constant_iterator<T>(static_cast<T>(i));\n  }\n};\n\nstruct offset_to_size_t\n{\n  std::size_t *offsets = nullptr;\n\n  __host__ __device__ __forceinline__ std::size_t operator()(std::size_t i)\n  {\n    return offsets[i + 1] - offsets[i];\n  }\n};\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_key_segments(seed_t seed,\n                                                    std::size_t total_elements,\n                                                    thrust::device_vector<std::size_t> &segment_offsets)\n{\n  std::size_t total_segments = segment_offsets.size() - 1;\n  thrust::device_vector<T> out(total_elements);\n  std::size_t *d_offsets = thrust::raw_pointer_cast(segment_offsets.data());\n  T *d_out = thrust::raw_pointer_cast(out.data());\n\n  thrust::counting_iterator<int> iota(0);\n  offset_to_iterator_t<T> dst_transform_op{d_out};\n\n  auto d_range_srcs = thrust::make_transform_iterator(iota, repeat_index_t<T>{});\n  auto d_range_dsts = thrust::make_transform_iterator(d_offsets, dst_transform_op);\n  auto d_range_sizes = thrust::make_transform_iterator(iota, offset_to_size_t{d_offsets});\n\n  std::uint8_t *d_temp_storage = nullptr;\n  std::size_t temp_storage_bytes = 0;\n  hipcub::DeviceCopy::Batched(d_temp_storage,\n                           temp_storage_bytes,\n                           d_range_srcs,\n                           d_range_dsts,\n                           d_range_sizes,\n                           total_segments);\n \n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  hipcub::DeviceCopy::Batched(d_temp_storage,\n                           temp_storage_bytes,\n                           d_range_srcs,\n                           d_range_dsts,\n                           d_range_sizes,\n                           total_segments);\n  hipDeviceSynchronize();\n\n  return out;\n}\n\ntemplate <typename T>\nthrust::device_vector<T> gen_power_law_key_segments(seed_t seed,\n                                                    std::size_t total_elements,\n                                                    std::size_t total_segments)\n{\n  thrust::device_vector<std::size_t> segment_offsets =\n    gen_power_law_offsets<std::size_t>(seed, total_elements, total_segments);\n  return gen_power_law_key_segments<T>(seed, total_elements, segment_offsets);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_power_law_key_segments<TYPE>(seed_t,                    \\\n                                                                        std::size_t,               \\\n                                                                        std::size_t)\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n#undef INSTANTIATE\n\ntemplate <class T>\nstruct gt_t\n{\n  T val;\n\n  __device__ bool operator()(T x)\n  {\n    return x > val;\n  }\n};\n\ntemplate <typename T>\nthrust::device_vector<T> gen_uniform_offsets(seed_t seed,\n                                             T total_elements,\n                                             T min_segment_size,\n                                             T max_segment_size)\n{\n  thrust::device_vector<T> segment_offsets(total_elements + 2);\n  gen(seed, segment_offsets, bit_entropy::_1_000, min_segment_size, max_segment_size);\n  segment_offsets[total_elements] = total_elements + 1;\n  thrust::exclusive_scan(segment_offsets.begin(), segment_offsets.end(), segment_offsets.begin());\n  typename thrust::device_vector<T>::iterator iter =\n    thrust::find_if(segment_offsets.begin(), segment_offsets.end(), gt_t<T>{total_elements});\n  *iter = total_elements;\n  segment_offsets.erase(iter + 1, segment_offsets.end());\n  return segment_offsets;\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_uniform_offsets<TYPE>(seed_t, TYPE, TYPE, TYPE)\n\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\n\n#undef INSTANTIATE\n\n/**\n * @brief Generates a vector of random key segments.\n *\n * Not all parameter combinations can be satisfied. For instance, if the total\n * elements is less than the minimal segment size, the function will return a\n * vector with a single element that is outside of the requested range. \n * At most one segment can be out of the requested range.\n */\ntemplate <typename T>\nthrust::device_vector<T> gen_uniform_key_segments(seed_t seed,\n                                                  std::size_t total_elements,\n                                                  std::size_t min_segment_size,\n                                                  std::size_t max_segment_size)\n{\n  thrust::device_vector<std::size_t> segment_offsets =\n    gen_uniform_offsets(seed, total_elements, min_segment_size, max_segment_size);\n  return gen_power_law_key_segments<T>(seed, total_elements, segment_offsets);\n}\n\n#define INSTANTIATE(TYPE)                                                                          \\\n  template thrust::device_vector<TYPE> gen_uniform_key_segments<TYPE>(seed_t,                      \\\n                                                                      std::size_t,                 \\\n                                                                      std::size_t,                 \\\n                                                                      std::size_t)\n\nINSTANTIATE(bool);\n\nINSTANTIATE(uint8_t);\nINSTANTIATE(uint16_t);\nINSTANTIATE(uint32_t);\nINSTANTIATE(uint64_t);\nINSTANTIATE(uint128_t);\n\nINSTANTIATE(int8_t);\nINSTANTIATE(int16_t);\nINSTANTIATE(int32_t);\nINSTANTIATE(int64_t);\nINSTANTIATE(int128_t);\n\nINSTANTIATE(float);\nINSTANTIATE(double);\nINSTANTIATE(complex);\n#undef INSTANTIATE\n", "source": "/kaggle/working/cub/benchmarks/nvbench_helper/nvbench_helper.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::Unique utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to unique entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    cudaError_t                 */*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    InputIteratorT              d_in,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::Unique(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  OutputIteratorT      d_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          d_num_selected_out,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\ncudaError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     cudaError_t *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     InputIteratorT       d_in,\n                     OutputIteratorT      d_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            d_num_selected_out,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences for the current run\n        int repeat;\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve unique problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T>\nint Solve(\n    InputIteratorT  h_in,\n    T               *h_reference,\n    int             num_items)\n{\n    int num_selected = 0;\n    if (num_items > 0)\n    {\n        h_reference[num_selected] = h_in[0];\n        num_selected++;\n    }\n\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (h_in[i] != h_in[i - 1])\n        {\n            h_reference[num_selected] = h_in[i];\n            num_selected++;\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            T>\nvoid Test(\n    DeviceInputIteratorT d_in,\n    T                   *h_reference,\n    int                 num_selected,\n    int                 num_items)\n{\n    // Allocate device output array and num selected\n    T       *d_out = NULL;\n    int     *d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(T) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    CubDebugExit(cudaMemset(d_out, 0, sizeof(T) * num_items));\n    CubDebugExit(cudaMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = CompareDeviceResults(h_reference, d_out, num_selected, true, g_verbose);\n    printf(\"\\t Data %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis        = elapsed_millis / g_timing_iterations;\n        float giga_rate         = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth    = float((num_items + num_selected) * sizeof(T)) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment)\n{\n    // Allocate host arrays\n    T*  h_in        = new T[num_items];\n    T*  h_reference = new T[num_items];\n\n    // Initialize problem and solution\n    Initialize(entropy_reduction, h_in, num_items, max_segment);\n    int num_selected = Solve(h_in, h_reference, num_items);\n\n    printf(\"\\nPointer %s cub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(T).name(),\n        (int) sizeof(T),\n        entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    T *d_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(T) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(T) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_in, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test DeviceSelect on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestIterator(\n    int             num_items)\n{\n    // Use a counting iterator as the input\n    CountingInputIterator<T, int> h_in(0);\n\n    // Allocate host arrays\n    T*  h_reference = new T[num_items];\n\n    // Initialize problem and solution\n    int num_selected = Solve(h_in, h_reference, num_items);\n\n    printf(\"\\nIterator %s cub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(T).name(),\n        (int) sizeof(T));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND>(h_in, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid Test(\n    int             num_items)\n{\n    for (int max_segment = 1; ((max_segment > 0) && (max_segment < num_items)); max_segment *= 11)\n    {\n        TestPointer<BACKEND, T>(num_items, 0, max_segment);\n        TestPointer<BACKEND, T>(num_items, 2, max_segment);\n        TestPointer<BACKEND, T>(num_items, 7, max_segment);\n    }\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        T>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    Test<CUB, T>(num_items);\n#elif TEST_CDP == 1\n    Test<CDP, T>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <typename T>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<T>(0);\n        TestOp<T>(1);\n        TestOp<T>(100);\n        TestOp<T>(10000);\n        TestOp<T>(1000000);\n    }\n    else\n    {\n        TestOp<T>(num_items);\n    }\n}\n\ntemplate <typename T>\nvoid TestIteratorOp(int num_items)\n{\n  void *d_temp_storage{};\n  std::size_t temp_storage_size{};\n\n  thrust::device_vector<int> num_selected(1);\n\n  auto in = thrust::make_counting_iterator(static_cast<T>(0));\n  auto out = thrust::make_discard_iterator();\n\n  CubDebugExit(cub::DeviceSelect::Unique(d_temp_storage,\n                                         temp_storage_size,\n                                         in,\n                                         out,\n                                         num_selected.begin(),\n                                         num_items));\n\n  thrust::device_vector<char> temp_storage(temp_storage_size);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  CubDebugExit(cub::DeviceSelect::Unique(d_temp_storage,\n                                         temp_storage_size,\n                                         in,\n                                         out,\n                                         num_selected.begin(),\n                                         num_items));\n\n  AssertEquals(num_selected[0], num_items);\n}\n\ntemplate <typename T>\nvoid TestIterator(int num_items)\n{\n  if (num_items < 0)\n  {\n    TestIteratorOp<T>(0);\n    TestIteratorOp<T>(1);\n    TestIteratorOp<T>(100);\n    TestIteratorOp<T>(10000);\n    TestIteratorOp<T>(1000000);\n  }\n  else\n  {\n    TestIteratorOp<T>(num_items);\n  }\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    Test<unsigned char>(num_items);\n    Test<unsigned short>(num_items);\n    Test<unsigned int>(num_items);\n    Test<unsigned long long>(num_items);\n\n    Test<uchar2>(num_items);\n    Test<ushort2>(num_items);\n    Test<uint2>(num_items);\n    Test<ulonglong2>(num_items);\n\n    Test<uchar4>(num_items);\n    Test<ushort4>(num_items);\n    Test<uint4>(num_items);\n    Test<ulonglong4>(num_items);\n\n    Test<TestFoo>(num_items);\n    Test<TestBar>(num_items);\n\n    TestIterator<int>(num_items);\n\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::Unique utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to unique entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    hipError_t                 */*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    InputIteratorT              d_in,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::Unique(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  OutputIteratorT      d_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          d_num_selected_out,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\nhipError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     hipError_t *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     InputIteratorT       d_in,\n                     OutputIteratorT      d_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            d_num_selected_out,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences for the current run\n        int repeat;\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve unique problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T>\nint Solve(\n    InputIteratorT  h_in,\n    T               *h_reference,\n    int             num_items)\n{\n    int num_selected = 0;\n    if (num_items > 0)\n    {\n        h_reference[num_selected] = h_in[0];\n        num_selected++;\n    }\n\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (h_in[i] != h_in[i - 1])\n        {\n            h_reference[num_selected] = h_in[i];\n            num_selected++;\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            T>\nvoid Test(\n    DeviceInputIteratorT d_in,\n    T                   *h_reference,\n    int                 num_selected,\n    int                 num_items)\n{\n    // Allocate device output array and num selected\n    T       *d_out = NULL;\n    int     *d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(T) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    CubDebugExit(hipMemset(d_out, 0, sizeof(T) * num_items));\n    CubDebugExit(hipMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = CompareDeviceResults(h_reference, d_out, num_selected, true, g_verbose);\n    printf(\"\\t Data %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis        = elapsed_millis / g_timing_iterations;\n        float giga_rate         = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth    = float((num_items + num_selected) * sizeof(T)) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment)\n{\n    // Allocate host arrays\n    T*  h_in        = new T[num_items];\n    T*  h_reference = new T[num_items];\n\n    // Initialize problem and solution\n    Initialize(entropy_reduction, h_in, num_items, max_segment);\n    int num_selected = Solve(h_in, h_reference, num_items);\n\n    printf(\"\\nPointer %s hipcub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(T).name(),\n        (int) sizeof(T),\n        entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    T *d_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(T) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_in, h_in, sizeof(T) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_in, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test DeviceSelect on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestIterator(\n    int             num_items)\n{\n    // Use a counting iterator as the input\n    CountingInputIterator<T, int> h_in(0);\n\n    // Allocate host arrays\n    T*  h_reference = new T[num_items];\n\n    // Initialize problem and solution\n    int num_selected = Solve(h_in, h_reference, num_items);\n\n    printf(\"\\nIterator %s hipcub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(T).name(),\n        (int) sizeof(T));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND>(h_in, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid Test(\n    int             num_items)\n{\n    for (int max_segment = 1; ((max_segment > 0) && (max_segment < num_items)); max_segment *= 11)\n    {\n        TestPointer<BACKEND, T>(num_items, 0, max_segment);\n        TestPointer<BACKEND, T>(num_items, 2, max_segment);\n        TestPointer<BACKEND, T>(num_items, 7, max_segment);\n    }\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        T>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    Test<CUB, T>(num_items);\n#elif TEST_CDP == 1\n    Test<CDP, T>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <typename T>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<T>(0);\n        TestOp<T>(1);\n        TestOp<T>(100);\n        TestOp<T>(10000);\n        TestOp<T>(1000000);\n    }\n    else\n    {\n        TestOp<T>(num_items);\n    }\n}\n\ntemplate <typename T>\nvoid TestIteratorOp(int num_items)\n{\n  void *d_temp_storage{};\n  std::size_t temp_storage_size{};\n\n  thrust::device_vector<int> num_selected(1);\n\n  auto in = thrust::make_counting_iterator(static_cast<T>(0));\n  auto out = thrust::make_discard_iterator();\n\n  CubDebugExit(hipcub::DeviceSelect::Unique(d_temp_storage,\n                                         temp_storage_size,\n                                         in,\n                                         out,\n                                         num_selected.begin(),\n                                         num_items));\n\n  thrust::device_vector<char> temp_storage(temp_storage_size);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  CubDebugExit(hipcub::DeviceSelect::Unique(d_temp_storage,\n                                         temp_storage_size,\n                                         in,\n                                         out,\n                                         num_selected.begin(),\n                                         num_items));\n\n  AssertEquals(num_selected[0], num_items);\n}\n\ntemplate <typename T>\nvoid TestIterator(int num_items)\n{\n  if (num_items < 0)\n  {\n    TestIteratorOp<T>(0);\n    TestIteratorOp<T>(1);\n    TestIteratorOp<T>(100);\n    TestIteratorOp<T>(10000);\n    TestIteratorOp<T>(1000000);\n  }\n  else\n  {\n    TestIteratorOp<T>(num_items);\n  }\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    Test<unsigned char>(num_items);\n    Test<unsigned short>(num_items);\n    Test<unsigned int>(num_items);\n    Test<unsigned long long>(num_items);\n\n    Test<uchar2>(num_items);\n    Test<ushort2>(num_items);\n    Test<uint2>(num_items);\n    Test<ulonglong2>(num_items);\n\n    Test<uchar4>(num_items);\n    Test<ushort4>(num_items);\n    Test<uint4>(num_items);\n    Test<ulonglong4>(num_items);\n\n    Test<TestFoo>(num_items);\n    Test<TestBar>(num_items);\n\n    TestIterator<int>(num_items);\n\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_device_select_unique.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceReduce::ReduceByKey utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_reduce.cuh>\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce-by-key entrypoint\n */\ntemplate <\n    typename                    KeyInputIteratorT,\n    typename                    KeyOutputIteratorT,\n    typename                    ValueInputIteratorT,\n    typename                    ValueOutputIteratorT,\n    typename                    NumRunsIteratorT,\n    typename                    EqualityOpT,\n    typename                    ReductionOpT,\n    typename                    OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    cudaError_t                 */*d_cdp_error*/,\n\n    void                        *d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    KeyInputIteratorT           d_keys_in,\n    KeyOutputIteratorT          d_keys_out,\n    ValueInputIteratorT         d_values_in,\n    ValueOutputIteratorT        d_values_out,\n    NumRunsIteratorT            d_num_runs,\n    EqualityOpT                  /*equality_op*/,\n    ReductionOpT                 reduction_op,\n    OffsetT                     num_items)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceReduce::ReduceByKey(\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_keys_out,\n            d_values_in,\n            d_values_out,\n            d_num_runs,\n            reduction_op,\n            num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename KeyInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueInputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumRunsIteratorT,\n          typename EqualityOpT,\n          typename ReductionOpT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  KeyInputIteratorT    d_keys_in,\n                                  KeyOutputIteratorT   d_keys_out,\n                                  ValueInputIteratorT  d_values_in,\n                                  ValueOutputIteratorT d_values_out,\n                                  NumRunsIteratorT     d_num_runs,\n                                  EqualityOpT          equality_op,\n                                  ReductionOpT         reduction_op,\n                                  OffsetT              num_items)\n{\n\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_keys_out,\n                          d_values_in,\n                          d_values_out,\n                          d_num_runs,\n                          equality_op,\n                          reduction_op,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename KeyInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueInputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumRunsIteratorT,\n          typename EqualityOpT,\n          typename ReductionOpT,\n          typename OffsetT>\n__forceinline__ cudaError_t\nDispatch(Int2Type<CDP> /*dispatch_to*/,\n         int          timing_timing_iterations,\n         size_t      *d_temp_storage_bytes,\n         cudaError_t *d_cdp_error,\n\n         void                *d_temp_storage,\n         size_t              &temp_storage_bytes,\n         KeyInputIteratorT    d_keys_in,\n         KeyOutputIteratorT   d_keys_out,\n         ValueInputIteratorT  d_values_in,\n         ValueOutputIteratorT d_values_out,\n         NumRunsIteratorT     d_num_runs,\n         EqualityOpT          equality_op,\n         ReductionOpT         reduction_op,\n         OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              KeyInputIteratorT,\n                              KeyOutputIteratorT,\n                              ValueInputIteratorT,\n                              ValueOutputIteratorT,\n                              NumRunsIteratorT,\n                              EqualityOpT,\n                              ReductionOpT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_keys_out,\n            d_values_in,\n            d_values_out,\n            d_num_runs,\n            equality_op,\n            reduction_op,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences\n\n        int repeat;\n\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve problem.  Returns total number of segments identified\n */\ntemplate <\n    typename        KeyInputIteratorT,\n    typename        ValueInputIteratorT,\n    typename        KeyT,\n    typename        ValueT,\n    typename        EqualityOpT,\n    typename        ReductionOpT>\nint Solve(\n    KeyInputIteratorT       h_keys_in,\n    KeyT                    *h_keys_reference,\n    ValueInputIteratorT     h_values_in,\n    ValueT                  *h_values_reference,\n    EqualityOpT             equality_op,\n    ReductionOpT            reduction_op,\n    int                     num_items)\n{\n    using AccumT = cub::detail::accumulator_t<ReductionOpT, ValueT, ValueT>;\n\n    // First item\n    KeyT previous        = h_keys_in[0];\n    AccumT aggregate     = h_values_in[0];\n    int num_segments    = 0;\n\n    // Subsequent items\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (!equality_op(previous, h_keys_in[i]))\n        {\n            h_keys_reference[num_segments] = previous;\n            h_values_reference[num_segments] = static_cast<ValueT>(aggregate);\n            num_segments++;\n            aggregate = h_values_in[i];\n        }\n        else\n        {\n            aggregate = static_cast<ValueT>(reduction_op(aggregate, h_values_in[i]));\n        }\n        previous = h_keys_in[i];\n    }\n\n    h_keys_reference[num_segments] = previous;\n    h_values_reference[num_segments] = static_cast<ValueT>(aggregate);\n    num_segments++;\n\n    return num_segments;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceKeyInputIteratorT,\n    typename            DeviceValueInputIteratorT,\n    typename            KeyT,\n    typename            ValueT,\n    typename            EqualityOpT,\n    typename            ReductionOpT>\nvoid Test(\n    DeviceKeyInputIteratorT     d_keys_in,\n    DeviceValueInputIteratorT   d_values_in,\n    KeyT*                       h_keys_reference,\n    ValueT*                     h_values_reference,\n    EqualityOpT                 equality_op,\n    ReductionOpT                reduction_op,\n    int                         num_segments,\n    int                         num_items)\n{\n    // Allocate device output arrays and number of segments\n    KeyT*   d_keys_out             = NULL;\n    ValueT* d_values_out           = NULL;\n    int*    d_num_runs         = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_out, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_out, sizeof(ValueT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_runs, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output arrays\n    CubDebugExit(cudaMemset(d_keys_out, 0, sizeof(KeyT) * num_items));\n    CubDebugExit(cudaMemset(d_values_out, 0, sizeof(ValueT) * num_items));\n    CubDebugExit(cudaMemset(d_num_runs, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = CompareDeviceResults(h_keys_reference, d_keys_out, num_segments, true, g_verbose);\n    printf(\"\\t Keys %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(h_values_reference, d_values_out, num_segments, true, g_verbose);\n    printf(\"\\t Values %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    int compare3 = CompareDeviceResults(&num_segments, d_num_runs, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare3 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float   avg_millis  = elapsed_millis / g_timing_iterations;\n        float   giga_rate   = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        int     bytes_moved = ((num_items + num_segments) * sizeof(KeyT)) + ((num_items + num_segments) * sizeof(ValueT));\n        float   giga_bandwidth  = float(bytes_moved) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\", avg_millis, giga_rate, giga_bandwidth);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_keys_out) CubDebugExit(g_allocator.DeviceFree(d_keys_out));\n    if (d_values_out) CubDebugExit(g_allocator.DeviceFree(d_values_out));\n    if (d_num_runs) CubDebugExit(g_allocator.DeviceFree(d_num_runs));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2 | compare3);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment,\n    ReductionOpT    reduction_op)\n{\n    // Allocate host arrays\n    KeyT* h_keys_in        = new KeyT[num_items];\n    KeyT* h_keys_reference = new KeyT[num_items];\n\n    ValueT* h_values_in        = new ValueT[num_items];\n    ValueT* h_values_reference = new ValueT[num_items];\n\n    for (int i = 0; i < num_items; ++i)\n        InitValue(INTEGER_SEED, h_values_in[i], 1);\n\n    // Initialize problem and solution\n    Equality equality_op;\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    int num_segments = Solve(h_keys_in, h_keys_reference, h_values_in, h_values_reference, equality_op, reduction_op, num_items);\n\n    printf(\"\\nPointer %s cub::DeviceReduce::ReduceByKey %s reduction of %d items, %d segments (avg run length %.3f), {%s,%s} key value pairs, max_segment %d, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<ReductionOpT, Sum>::value) ? \"Sum\" : \"Max\",\n        num_items, num_segments, float(num_items) / num_segments,\n        typeid(KeyT).name(), typeid(ValueT).name(),\n        max_segment, entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT     *d_keys_in = NULL;\n    ValueT   *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(ValueT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemcpy(d_values_in, h_values_in, sizeof(ValueT) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, d_values_in, h_keys_reference, h_values_reference, equality_op, reduction_op, num_segments, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestIterator(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment,\n    ReductionOpT    reduction_op)\n{\n    // Allocate host arrays\n    KeyT* h_keys_in        = new KeyT[num_items];\n    KeyT* h_keys_reference = new KeyT[num_items];\n\n    ValueT one_val;\n    InitValue(INTEGER_SEED, one_val, 1);\n    ConstantInputIterator<ValueT, int> h_values_in(one_val);\n    ValueT* h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    Equality equality_op;\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    int num_segments = Solve(h_keys_in, h_keys_reference, h_values_in, h_values_reference, equality_op, reduction_op, num_items);\n\n    printf(\"\\nIterator %s cub::DeviceReduce::ReduceByKey %s reduction of %d items, %d segments (avg run length %.3f), {%s,%s} key value pairs, max_segment %d, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<ReductionOpT, Sum>::value) ? \"Sum\" : \"Max\",\n        num_items, num_segments, float(num_items) / num_segments,\n        typeid(KeyT).name(), typeid(ValueT).name(),\n        max_segment, entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT     *d_keys_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, h_values_in, h_keys_reference, h_values_reference, equality_op, reduction_op, num_segments, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid Test(\n    int             num_items,\n    ReductionOpT    reduction_op,\n    int             max_segment)\n{\n    // 0 key-bit entropy reduction rounds\n    TestPointer<BACKEND, KeyT, ValueT>(num_items, 0, max_segment, reduction_op);\n\n    if (max_segment > 1)\n    {\n        // 2 key-bit entropy reduction rounds\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 2, max_segment, reduction_op);\n\n        // 7 key-bit entropy reduction rounds\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 7, max_segment, reduction_op);\n    }\n}\n\n\n/**\n * Test different avg segment lengths modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid Test(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n    Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, -1);\n    Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, 1);\n\n    // Evaluate different max-segment lengths\n    for (int max_segment = 3; max_segment < CUB_MIN(num_items, (unsigned short) -1); max_segment *= 11)\n    {\n        Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, max_segment);\n    }\n}\n\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestDispatch(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, ValueT>(num_items, reduction_op);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, ValueT>(num_items, reduction_op);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestSize(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n    if (num_items < 0)\n    {\n        TestDispatch<KeyT, ValueT>(1,        reduction_op);\n        TestDispatch<KeyT, ValueT>(100,      reduction_op);\n        TestDispatch<KeyT, ValueT>(10000,    reduction_op);\n        TestDispatch<KeyT, ValueT>(1000000,  reduction_op);\n    }\n    else\n    {\n        TestDispatch<KeyT, ValueT>(num_items, reduction_op);\n    }\n\n}\n\n\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid TestOp(\n    int             num_items)\n{\n    TestSize<KeyT, ValueT>(num_items, cub::Sum());\n    TestSize<KeyT, ValueT>(num_items, cub::Max());\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    printf(\"\\n\");\n\n    // Get ptx version\n    int ptx_version = 0;\n    CubDebugExit(PtxVersion(ptx_version));\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    TestOp<int, signed char>(num_items);\n    TestOp<int, short>(num_items);\n    TestOp<int, int>(num_items);\n    TestOp<int, long>(num_items);\n    TestOp<int, long long>(num_items);\n    TestOp<int, float>(num_items);\n    TestOp<int, double>(num_items);\n\n    TestOp<int, uchar2>(num_items);\n    TestOp<int, uint2>(num_items);\n    TestOp<int, uint3>(num_items);\n    TestOp<int, uint4>(num_items);\n    TestOp<int, ulonglong4>(num_items);\n    TestOp<int, TestFoo>(num_items);\n    TestOp<int, TestBar>(num_items);\n\n    TestOp<signed char, int>(num_items);\n    TestOp<long long, int>(num_items);\n    TestOp<TestFoo, int>(num_items);\n    TestOp<TestBar, int>(num_items);\n\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceReduce::ReduceByKey utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_reduce.cuh>\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce-by-key entrypoint\n */\ntemplate <\n    typename                    KeyInputIteratorT,\n    typename                    KeyOutputIteratorT,\n    typename                    ValueInputIteratorT,\n    typename                    ValueOutputIteratorT,\n    typename                    NumRunsIteratorT,\n    typename                    EqualityOpT,\n    typename                    ReductionOpT,\n    typename                    OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    hipError_t                 */*d_cdp_error*/,\n\n    void                        *d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    KeyInputIteratorT           d_keys_in,\n    KeyOutputIteratorT          d_keys_out,\n    ValueInputIteratorT         d_values_in,\n    ValueOutputIteratorT        d_values_out,\n    NumRunsIteratorT            d_num_runs,\n    EqualityOpT                  /*equality_op*/,\n    ReductionOpT                 reduction_op,\n    OffsetT                     num_items)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceReduce::ReduceByKey(\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_keys_out,\n            d_values_in,\n            d_values_out,\n            d_num_runs,\n            reduction_op,\n            num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename KeyInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueInputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumRunsIteratorT,\n          typename EqualityOpT,\n          typename ReductionOpT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  KeyInputIteratorT    d_keys_in,\n                                  KeyOutputIteratorT   d_keys_out,\n                                  ValueInputIteratorT  d_values_in,\n                                  ValueOutputIteratorT d_values_out,\n                                  NumRunsIteratorT     d_num_runs,\n                                  EqualityOpT          equality_op,\n                                  ReductionOpT         reduction_op,\n                                  OffsetT              num_items)\n{\n\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_keys_out,\n                          d_values_in,\n                          d_values_out,\n                          d_num_runs,\n                          equality_op,\n                          reduction_op,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename KeyInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueInputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumRunsIteratorT,\n          typename EqualityOpT,\n          typename ReductionOpT,\n          typename OffsetT>\n__forceinline__ hipError_t\nDispatch(Int2Type<CDP> /*dispatch_to*/,\n         int          timing_timing_iterations,\n         size_t      *d_temp_storage_bytes,\n         hipError_t *d_cdp_error,\n\n         void                *d_temp_storage,\n         size_t              &temp_storage_bytes,\n         KeyInputIteratorT    d_keys_in,\n         KeyOutputIteratorT   d_keys_out,\n         ValueInputIteratorT  d_values_in,\n         ValueOutputIteratorT d_values_out,\n         NumRunsIteratorT     d_num_runs,\n         EqualityOpT          equality_op,\n         ReductionOpT         reduction_op,\n         OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              KeyInputIteratorT,\n                              KeyOutputIteratorT,\n                              ValueInputIteratorT,\n                              ValueOutputIteratorT,\n                              NumRunsIteratorT,\n                              EqualityOpT,\n                              ReductionOpT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_keys_out,\n            d_values_in,\n            d_values_out,\n            d_num_runs,\n            equality_op,\n            reduction_op,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences\n\n        int repeat;\n\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve problem.  Returns total number of segments identified\n */\ntemplate <\n    typename        KeyInputIteratorT,\n    typename        ValueInputIteratorT,\n    typename        KeyT,\n    typename        ValueT,\n    typename        EqualityOpT,\n    typename        ReductionOpT>\nint Solve(\n    KeyInputIteratorT       h_keys_in,\n    KeyT                    *h_keys_reference,\n    ValueInputIteratorT     h_values_in,\n    ValueT                  *h_values_reference,\n    EqualityOpT             equality_op,\n    ReductionOpT            reduction_op,\n    int                     num_items)\n{\n    using AccumT = hipcub::detail::accumulator_t<ReductionOpT, ValueT, ValueT>;\n\n    // First item\n    KeyT previous        = h_keys_in[0];\n    AccumT aggregate     = h_values_in[0];\n    int num_segments    = 0;\n\n    // Subsequent items\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (!equality_op(previous, h_keys_in[i]))\n        {\n            h_keys_reference[num_segments] = previous;\n            h_values_reference[num_segments] = static_cast<ValueT>(aggregate);\n            num_segments++;\n            aggregate = h_values_in[i];\n        }\n        else\n        {\n            aggregate = static_cast<ValueT>(reduction_op(aggregate, h_values_in[i]));\n        }\n        previous = h_keys_in[i];\n    }\n\n    h_keys_reference[num_segments] = previous;\n    h_values_reference[num_segments] = static_cast<ValueT>(aggregate);\n    num_segments++;\n\n    return num_segments;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceKeyInputIteratorT,\n    typename            DeviceValueInputIteratorT,\n    typename            KeyT,\n    typename            ValueT,\n    typename            EqualityOpT,\n    typename            ReductionOpT>\nvoid Test(\n    DeviceKeyInputIteratorT     d_keys_in,\n    DeviceValueInputIteratorT   d_values_in,\n    KeyT*                       h_keys_reference,\n    ValueT*                     h_values_reference,\n    EqualityOpT                 equality_op,\n    ReductionOpT                reduction_op,\n    int                         num_segments,\n    int                         num_items)\n{\n    // Allocate device output arrays and number of segments\n    KeyT*   d_keys_out             = NULL;\n    ValueT* d_values_out           = NULL;\n    int*    d_num_runs         = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_out, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_out, sizeof(ValueT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_runs, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output arrays\n    CubDebugExit(hipMemset(d_keys_out, 0, sizeof(KeyT) * num_items));\n    CubDebugExit(hipMemset(d_values_out, 0, sizeof(ValueT) * num_items));\n    CubDebugExit(hipMemset(d_num_runs, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = CompareDeviceResults(h_keys_reference, d_keys_out, num_segments, true, g_verbose);\n    printf(\"\\t Keys %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(h_values_reference, d_values_out, num_segments, true, g_verbose);\n    printf(\"\\t Values %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    int compare3 = CompareDeviceResults(&num_segments, d_num_runs, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare3 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, d_values_in, d_values_out, d_num_runs, equality_op, reduction_op, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float   avg_millis  = elapsed_millis / g_timing_iterations;\n        float   giga_rate   = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        int     bytes_moved = ((num_items + num_segments) * sizeof(KeyT)) + ((num_items + num_segments) * sizeof(ValueT));\n        float   giga_bandwidth  = float(bytes_moved) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\", avg_millis, giga_rate, giga_bandwidth);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_keys_out) CubDebugExit(g_allocator.DeviceFree(d_keys_out));\n    if (d_values_out) CubDebugExit(g_allocator.DeviceFree(d_values_out));\n    if (d_num_runs) CubDebugExit(g_allocator.DeviceFree(d_num_runs));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2 | compare3);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment,\n    ReductionOpT    reduction_op)\n{\n    // Allocate host arrays\n    KeyT* h_keys_in        = new KeyT[num_items];\n    KeyT* h_keys_reference = new KeyT[num_items];\n\n    ValueT* h_values_in        = new ValueT[num_items];\n    ValueT* h_values_reference = new ValueT[num_items];\n\n    for (int i = 0; i < num_items; ++i)\n        InitValue(INTEGER_SEED, h_values_in[i], 1);\n\n    // Initialize problem and solution\n    Equality equality_op;\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    int num_segments = Solve(h_keys_in, h_keys_reference, h_values_in, h_values_reference, equality_op, reduction_op, num_items);\n\n    printf(\"\\nPointer %s hipcub::DeviceReduce::ReduceByKey %s reduction of %d items, %d segments (avg run length %.3f), {%s,%s} key value pairs, max_segment %d, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<ReductionOpT, Sum>::value) ? \"Sum\" : \"Max\",\n        num_items, num_segments, float(num_items) / num_segments,\n        typeid(KeyT).name(), typeid(ValueT).name(),\n        max_segment, entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT     *d_keys_in = NULL;\n    ValueT   *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(ValueT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemcpy(d_values_in, h_values_in, sizeof(ValueT) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, d_values_in, h_keys_reference, h_values_reference, equality_op, reduction_op, num_segments, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestIterator(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment,\n    ReductionOpT    reduction_op)\n{\n    // Allocate host arrays\n    KeyT* h_keys_in        = new KeyT[num_items];\n    KeyT* h_keys_reference = new KeyT[num_items];\n\n    ValueT one_val;\n    InitValue(INTEGER_SEED, one_val, 1);\n    ConstantInputIterator<ValueT, int> h_values_in(one_val);\n    ValueT* h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    Equality equality_op;\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    int num_segments = Solve(h_keys_in, h_keys_reference, h_values_in, h_values_reference, equality_op, reduction_op, num_items);\n\n    printf(\"\\nIterator %s hipcub::DeviceReduce::ReduceByKey %s reduction of %d items, %d segments (avg run length %.3f), {%s,%s} key value pairs, max_segment %d, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<ReductionOpT, Sum>::value) ? \"Sum\" : \"Max\",\n        num_items, num_segments, float(num_items) / num_segments,\n        typeid(KeyT).name(), typeid(ValueT).name(),\n        max_segment, entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT     *d_keys_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, h_values_in, h_keys_reference, h_values_reference, equality_op, reduction_op, num_segments, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid Test(\n    int             num_items,\n    ReductionOpT    reduction_op,\n    int             max_segment)\n{\n    // 0 key-bit entropy reduction rounds\n    TestPointer<BACKEND, KeyT, ValueT>(num_items, 0, max_segment, reduction_op);\n\n    if (max_segment > 1)\n    {\n        // 2 key-bit entropy reduction rounds\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 2, max_segment, reduction_op);\n\n        // 7 key-bit entropy reduction rounds\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 7, max_segment, reduction_op);\n    }\n}\n\n\n/**\n * Test different avg segment lengths modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid Test(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n    Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, -1);\n    Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, 1);\n\n    // Evaluate different max-segment lengths\n    for (int max_segment = 3; max_segment < CUB_MIN(num_items, (unsigned short) -1); max_segment *= 11)\n    {\n        Test<BACKEND, KeyT, ValueT>(num_items, reduction_op, max_segment);\n    }\n}\n\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestDispatch(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, ValueT>(num_items, reduction_op);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, ValueT>(num_items, reduction_op);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT,\n    typename        ReductionOpT>\nvoid TestSize(\n    int             num_items,\n    ReductionOpT    reduction_op)\n{\n    if (num_items < 0)\n    {\n        TestDispatch<KeyT, ValueT>(1,        reduction_op);\n        TestDispatch<KeyT, ValueT>(100,      reduction_op);\n        TestDispatch<KeyT, ValueT>(10000,    reduction_op);\n        TestDispatch<KeyT, ValueT>(1000000,  reduction_op);\n    }\n    else\n    {\n        TestDispatch<KeyT, ValueT>(num_items, reduction_op);\n    }\n\n}\n\n\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid TestOp(\n    int             num_items)\n{\n    TestSize<KeyT, ValueT>(num_items, hipcub::Sum());\n    TestSize<KeyT, ValueT>(num_items, hipcub::Max());\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    printf(\"\\n\");\n\n    // Get ptx version\n    int ptx_version = 0;\n    CubDebugExit(PtxVersion(ptx_version));\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    TestOp<int, signed char>(num_items);\n    TestOp<int, short>(num_items);\n    TestOp<int, int>(num_items);\n    TestOp<int, long>(num_items);\n    TestOp<int, long long>(num_items);\n    TestOp<int, float>(num_items);\n    TestOp<int, double>(num_items);\n\n    TestOp<int, uchar2>(num_items);\n    TestOp<int, uint2>(num_items);\n    TestOp<int, uint3>(num_items);\n    TestOp<int, uint4>(num_items);\n    TestOp<int, ulonglong4>(num_items);\n    TestOp<int, TestFoo>(num_items);\n    TestOp<int, TestBar>(num_items);\n\n    TestOp<signed char, int>(num_items);\n    TestOp<long long, int>(num_items);\n    TestOp<TestFoo, int>(num_items);\n    TestOp<TestBar, int>(num_items);\n\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_device_reduce_by_key.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_scan.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_combine_scan_kernel(T *in, T *inclusive_out, T *exclusive_out, ActionT action)\n{\n  using warp_scan_t = cub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T inc_out, exc_out;\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data, inc_out, exc_out);\n\n  inclusive_out[tid] = inc_out;\n  exclusive_out[tid] = exc_out;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_combine_scan(thrust::device_vector<T> &in,\n                       thrust::device_vector<T> &inclusive_out,\n                       thrust::device_vector<T> &exclusive_out,\n                       ActionT action)\n{\n  warp_combine_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(inclusive_out.data()),\n                                                thrust::raw_pointer_cast(exclusive_out.data()),\n                                                action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_scan_kernel(T *in, T *out, ActionT action)\n{\n  using warp_scan_t = cub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_scan(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  warp_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <class WarpScanT, class T>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cub::Min{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cub::Min{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cub::Min{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cub::Min{}, warp_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    scan.ExclusiveScan(thread_data, thread_data, initial_value, cub::Min{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    scan.ExclusiveScan(thread_data, thread_data, initial_value, cub::Min{}, warp_aggregate);\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\nstruct min_scan_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void\n  operator()(WarpScanT &scan, T &thread_data, T &inclusive_output, T &exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, cub::Min{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_scan_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void\n  operator()(WarpScanT &scan, T &thread_data, T &inclusive_output, T &exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, initial_value, cub::Min{});\n  }\n};\n\ntemplate <class T, class ScanOpT>\nthrust::host_vector<T> compute_host_reference(scan_mode mode,\n                                              thrust::host_vector<T> &result,\n                                              int logical_warp_threads,\n                                              ScanOpT scan_op,\n                                              T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return thrust::host_vector<T>{};\n  }\n  // TODO : assert result.size() % logical_warp_threads == 0\n\n  int num_warps = CUB_QUOTIENT_CEILING(static_cast<int>(result.size()), logical_warp_threads);\n  thrust::host_vector<T> warp_accumulator(num_warps);\n  if (mode == scan_mode::exclusive)\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T *output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = initial_value;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        accumulator = static_cast<T>(scan_op(accumulator, output[i]));\n        T tmp       = output[i];\n        output[i]   = current;\n        current     = static_cast<T>(scan_op(current, tmp));\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n  else\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T *output = result.data() + w * logical_warp_threads;\n      T current = initial_value;\n      for (int i = 0; i < logical_warp_threads; i++)\n      {\n        current   = static_cast<T>(scan_op(current, output[i]));\n        output[i] = current;\n      }\n      warp_accumulator[w] = current;\n    }\n  }\n\n  return warp_accumulator;\n}\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 2>;\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive, scan_mode::inclusive>;\n\nusing vec_types = c2h::type_list<ulonglong4, uchar3, short2>;\n\nusing warp_combine_type = int;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr scan_mode mode           = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nCUB_TEST(\"Warp scan works with sum\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp scan works with vec_types\", \"[scan][warp]\", vec_types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Warp scan works with custom types\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Warp scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, params::mode>{target_thread_id,\n                                           thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates =\n    compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\n// TODO : Do we need all the types?\nCUB_TEST(\"Warp scan works with custom scan op\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               min_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp custom op scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_aggregate_op_t<type, params::mode>{target_thread_id,\n                                           thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nCUB_TEST(\"Warp custom op scan works with initial value\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         c2h::enum_type_list<scan_mode, scan_mode::exclusive>)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               min_init_value_op_t<type>{\n                                                                 initial_value});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp custom op scan with initial value returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         c2h::enum_type_list<scan_mode, scan_mode::exclusive>)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n  const type initial_value   = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type>{target_thread_id,\n                                        initial_value,\n                                        thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nCUB_TEST(\"Warp combination scan works with custom scan op\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  thrust::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in,\n                                                       d_inclusive_out,\n                                                       d_exclusive_out,\n                                                       min_scan_op_t{});\n\n  thrust::host_vector<type> h_exclusive_out = d_in;\n  thrust::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // According to WarpScan::Scan documentation -\n  // Because no initial value is supplied, the exclusive_output computed for warp-lane0 is\n  // undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  for (size_t i = 0; i < h_exclusive_out.size(); i += logical_warp_threads)\n  {\n    d_exclusive_out[i] = h_exclusive_out[i];\n  }\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n\nCUB_TEST(\"Warp combination custom scan works with initial value\",\n         \"[scan][warp]\",\n         logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  thrust::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const type initial_value = GENERATE_COPY(take(2, random(0, total_warps * logical_warp_threads)));\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in,\n                                                       d_inclusive_out,\n                                                       d_exclusive_out,\n                                                       min_init_value_scan_op_t<type>{\n                                                         initial_value});\n\n  thrust::host_vector<type> h_exclusive_out = d_in;\n  thrust::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_scan.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_combine_scan_kernel(T *in, T *inclusive_out, T *exclusive_out, ActionT action)\n{\n  using warp_scan_t = hipcub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T inc_out, exc_out;\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data, inc_out, exc_out);\n\n  inclusive_out[tid] = inc_out;\n  exclusive_out[tid] = exc_out;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_combine_scan(thrust::device_vector<T> &in,\n                       thrust::device_vector<T> &inclusive_out,\n                       thrust::device_vector<T> &exclusive_out,\n                       ActionT action)\n{\n  warp_combine_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(inclusive_out.data()),\n                                                thrust::raw_pointer_cast(exclusive_out.data()),\n                                                action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_scan_kernel(T *in, T *out, ActionT action)\n{\n  using warp_scan_t = hipcub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_scan(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  warp_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <class WarpScanT, class T>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, hipcub::Min{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, hipcub::Min{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, hipcub::Min{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, hipcub::Min{}, warp_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT &scan, T &thread_data) const\n  {\n    scan.ExclusiveScan(thread_data, thread_data, initial_value, hipcub::Min{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T *m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS> &scan, T &thread_data) const\n  {\n    T warp_aggregate{};\n\n    scan.ExclusiveScan(thread_data, thread_data, initial_value, hipcub::Min{}, warp_aggregate);\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\nstruct min_scan_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void\n  operator()(WarpScanT &scan, T &thread_data, T &inclusive_output, T &exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, hipcub::Min{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_scan_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void\n  operator()(WarpScanT &scan, T &thread_data, T &inclusive_output, T &exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, initial_value, hipcub::Min{});\n  }\n};\n\ntemplate <class T, class ScanOpT>\nthrust::host_vector<T> compute_host_reference(scan_mode mode,\n                                              thrust::host_vector<T> &result,\n                                              int logical_warp_threads,\n                                              ScanOpT scan_op,\n                                              T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return thrust::host_vector<T>{};\n  }\n  // TODO : assert result.size() % logical_warp_threads == 0\n\n  int num_warps = CUB_QUOTIENT_CEILING(static_cast<int>(result.size()), logical_warp_threads);\n  thrust::host_vector<T> warp_accumulator(num_warps);\n  if (mode == scan_mode::exclusive)\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T *output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = initial_value;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        accumulator = static_cast<T>(scan_op(accumulator, output[i]));\n        T tmp       = output[i];\n        output[i]   = current;\n        current     = static_cast<T>(scan_op(current, tmp));\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n  else\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T *output = result.data() + w * logical_warp_threads;\n      T current = initial_value;\n      for (int i = 0; i < logical_warp_threads; i++)\n      {\n        current   = static_cast<T>(scan_op(current, output[i]));\n        output[i] = current;\n      }\n      warp_accumulator[w] = current;\n    }\n  }\n\n  return warp_accumulator;\n}\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 2>;\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive, scan_mode::inclusive>;\n\nusing vec_types = c2h::type_list<ulonglong4, uchar3, short2>;\n\nusing warp_combine_type = int;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr scan_mode mode           = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nCUB_TEST(\"Warp scan works with sum\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp scan works with vec_types\", \"[scan][warp]\", vec_types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Warp scan works with custom types\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               sum_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Warp scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, params::mode>{target_thread_id,\n                                           thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates =\n    compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\n// TODO : Do we need all the types?\nCUB_TEST(\"Warp scan works with custom scan op\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               min_op_t<params::mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp custom op scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_aggregate_op_t<type, params::mode>{target_thread_id,\n                                           thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nCUB_TEST(\"Warp custom op scan works with initial value\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         c2h::enum_type_list<scan_mode, scan_mode::exclusive>)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in,\n                                                               d_out,\n                                                               min_init_value_op_t<type>{\n                                                                 initial_value});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Warp custom op scan with initial value returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         c2h::enum_type_list<scan_mode, scan_mode::exclusive>)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_warp_aggregates(params::total_warps);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n  const type initial_value   = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type>{target_thread_id,\n                                        initial_value,\n                                        thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nCUB_TEST(\"Warp combination scan works with custom scan op\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  thrust::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in,\n                                                       d_inclusive_out,\n                                                       d_exclusive_out,\n                                                       min_scan_op_t{});\n\n  thrust::host_vector<type> h_exclusive_out = d_in;\n  thrust::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    std::numeric_limits<type>::max());\n\n  // According to WarpScan::Scan documentation -\n  // Because no initial value is supplied, the exclusive_output computed for warp-lane0 is\n  // undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  for (size_t i = 0; i < h_exclusive_out.size(); i += logical_warp_threads)\n  {\n    d_exclusive_out[i] = h_exclusive_out[i];\n  }\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n\nCUB_TEST(\"Warp combination custom scan works with initial value\",\n         \"[scan][warp]\",\n         logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  thrust::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  thrust::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  const type initial_value = GENERATE_COPY(take(2, random(0, total_warps * logical_warp_threads)));\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in,\n                                                       d_inclusive_out,\n                                                       d_exclusive_out,\n                                                       min_init_value_scan_op_t<type>{\n                                                         initial_value});\n\n  thrust::host_vector<type> h_exclusive_out = d_in;\n  thrust::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) { return std::min(l, r); },\n    initial_value);\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_warp_scan.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <cub/block/block_merge_sort.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __device__ __host__ bool operator()(const DataType &lhs, const DataType &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class KeyT,\n          class ActionT>\n__global__ void block_merge_sort_kernel(\n    KeyT *data, \n    int valid_items,\n    KeyT oob_default,\n    ActionT action)\n{\n  using BlockMergeSort =\n    cub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_data[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    thread_data[item] = idx < valid_items ? data[idx] : KeyT();\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_data, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    data[idx] = thread_data[item];\n  }\n}\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class KeyT,\n          class ValueT,\n          class ActionT>\n__global__ void block_merge_sort_kernel(\n    KeyT *keys, \n    ValueT *vals, \n    int valid_items,\n    KeyT oob_default,\n    ActionT action)\n{\n  using BlockMergeSort =\n    cub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread, ValueT>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_keys[ItemsPerThread];\n  ValueT thread_vals[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    thread_keys[item] = idx < valid_items ? keys[idx] : KeyT{};\n    thread_vals[item] = idx < valid_items ? vals[idx] : ValueT{};\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_keys, thread_vals, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    keys[idx] = thread_keys[item];\n    vals[idx] = thread_vals[item];\n  }\n}\n\nstruct stable_sort_keys_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_data,\n                             int valid_items,\n                             DefaultT oob_default) const\n {\n   sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n }\n};\n\nstruct stable_sort_pairs_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             ValueT &thread_vals,\n                             int valid_items,\n                             DefaultT oob_default) const\n {\n   sort.StableSort(thread_keys,\n                   thread_vals,\n                   CustomLess{},\n                   valid_items,\n                   oob_default);\n }\n};\n\nstruct stable_sort_pairs_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             ValueT &thread_vals,\n                             int /* valid_items */,\n                             DefaultT /* oob_default */) const\n {\n   sort.StableSort(thread_keys, thread_vals, CustomLess());\n }\n};\n\nstruct stable_sort_keys_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             int /* valid_items */,\n                             DefaultT /* oob_default */) const\n {\n   sort.StableSort(thread_keys, CustomLess());\n }\n};\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          class KeyT,\n          class ActionT>\nvoid block_merge_sort(\n    thrust::device_vector<KeyT> &keys, \n    ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread>\n    <<<1, ThreadsInBlock>>>(\n        thrust::raw_pointer_cast(keys.data()),\n        static_cast<int>(keys.size()),\n        std::numeric_limits<KeyT>::max(),\n        action);\n\n  REQUIRE( cudaSuccess == cudaPeekAtLastError() );\n  REQUIRE( cudaSuccess == cudaDeviceSynchronize() );\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          class KeyT,\n          class ValueT,\n          class ActionT>\nvoid block_merge_sort(\n    thrust::device_vector<KeyT> &keys, \n    thrust::device_vector<ValueT> &vals, \n    ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread>\n    <<<1, ThreadsInBlock>>>(\n        thrust::raw_pointer_cast(keys.data()),\n        thrust::raw_pointer_cast(vals.data()),\n        static_cast<int>(keys.size()),\n        std::numeric_limits<KeyT>::max(),\n        action);\n\n  REQUIRE( cudaSuccess == cudaPeekAtLastError() );\n  REQUIRE( cudaSuccess == cudaDeviceSynchronize() );\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types = c2h::type_list<std::int32_t, std::int64_t>;\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\n\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n};\n\n\nCUB_TEST(\"Block merge sort can sort keys in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_keys(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    stable_sort_keys_partial_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort can sort keys in full tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_keys(params::tile_size);\n\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort can sort pairs in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n  using value_t = key_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  thrust::device_vector<key_t> d_keys(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_partial_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\nCUB_TEST(\"Block merge sort can sort pairs in full tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n  using value_t = key_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  thrust::device_vector<key_t> d_keys(params::tile_size);\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_full_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\n\nCUB_TEST(\"Block merge sort can sort pairs with mixed types\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = std::int32_t;\n  using value_t = std::int64_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_full_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\nCUB_TEST(\"Block merge sort can sort large tiles\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = std::uint16_t;\n\n  constexpr int items_per_thread = 2;\n\n  // Repurpose block sizes\n  constexpr int cmake_threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = cmake_threads_in_block < 256\n                                 ? 512\n                                 : 1024;\n\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort is stable\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::less_comparable_t,\n                                   c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <cub/block/block_merge_sort.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __device__ __host__ bool operator()(const DataType &lhs, const DataType &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class KeyT,\n          class ActionT>\n__global__ void block_merge_sort_kernel(\n    KeyT *data, \n    int valid_items,\n    KeyT oob_default,\n    ActionT action)\n{\n  using BlockMergeSort =\n    hipcub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_data[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    thread_data[item] = idx < valid_items ? data[idx] : KeyT();\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_data, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    data[idx] = thread_data[item];\n  }\n}\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class KeyT,\n          class ValueT,\n          class ActionT>\n__global__ void block_merge_sort_kernel(\n    KeyT *keys, \n    ValueT *vals, \n    int valid_items,\n    KeyT oob_default,\n    ActionT action)\n{\n  using BlockMergeSort =\n    hipcub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread, ValueT>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_keys[ItemsPerThread];\n  ValueT thread_vals[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    thread_keys[item] = idx < valid_items ? keys[idx] : KeyT{};\n    thread_vals[item] = idx < valid_items ? vals[idx] : ValueT{};\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_keys, thread_vals, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    keys[idx] = thread_keys[item];\n    vals[idx] = thread_vals[item];\n  }\n}\n\nstruct stable_sort_keys_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_data,\n                             int valid_items,\n                             DefaultT oob_default) const\n {\n   sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n }\n};\n\nstruct stable_sort_pairs_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             ValueT &thread_vals,\n                             int valid_items,\n                             DefaultT oob_default) const\n {\n   sort.StableSort(thread_keys,\n                   thread_vals,\n                   CustomLess{},\n                   valid_items,\n                   oob_default);\n }\n};\n\nstruct stable_sort_pairs_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             ValueT &thread_vals,\n                             int /* valid_items */,\n                             DefaultT /* oob_default */) const\n {\n   sort.StableSort(thread_keys, thread_vals, CustomLess());\n }\n};\n\nstruct stable_sort_keys_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT &sort,\n                             KeyT &thread_keys,\n                             int /* valid_items */,\n                             DefaultT /* oob_default */) const\n {\n   sort.StableSort(thread_keys, CustomLess());\n }\n};\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          class KeyT,\n          class ActionT>\nvoid block_merge_sort(\n    thrust::device_vector<KeyT> &keys, \n    ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread>\n    <<<1, ThreadsInBlock>>>(\n        thrust::raw_pointer_cast(keys.data()),\n        static_cast<int>(keys.size()),\n        std::numeric_limits<KeyT>::max(),\n        action);\n\n  REQUIRE( hipSuccess == hipPeekAtLastError() );\n  REQUIRE( hipSuccess == hipDeviceSynchronize() );\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          class KeyT,\n          class ValueT,\n          class ActionT>\nvoid block_merge_sort(\n    thrust::device_vector<KeyT> &keys, \n    thrust::device_vector<ValueT> &vals, \n    ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread>\n    <<<1, ThreadsInBlock>>>(\n        thrust::raw_pointer_cast(keys.data()),\n        thrust::raw_pointer_cast(vals.data()),\n        static_cast<int>(keys.size()),\n        std::numeric_limits<KeyT>::max(),\n        action);\n\n  REQUIRE( hipSuccess == hipPeekAtLastError() );\n  REQUIRE( hipSuccess == hipDeviceSynchronize() );\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types = c2h::type_list<std::int32_t, std::int64_t>;\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\n\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n};\n\n\nCUB_TEST(\"Block merge sort can sort keys in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_keys(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    stable_sort_keys_partial_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort can sort keys in full tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_keys(params::tile_size);\n\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort can sort pairs in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n  using value_t = key_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  thrust::device_vector<key_t> d_keys(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_partial_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\nCUB_TEST(\"Block merge sort can sort pairs in full tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n  using value_t = key_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  thrust::device_vector<key_t> d_keys(params::tile_size);\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_full_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\n\nCUB_TEST(\"Block merge sort can sort pairs with mixed types\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = std::int32_t;\n  using value_t = std::int64_t;\n  using pair_t = std::pair<key_t, value_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  thrust::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(CUB_SEED(5), d_keys);\n  c2h::gen(CUB_SEED(5), d_vals);\n\n  thrust::host_vector<key_t> h_keys = d_keys;\n  thrust::host_vector<value_t> h_vals = d_vals;\n\n  thrust::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_ref.data()), \n      thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(), \n      [](pair_t l, pair_t r) -> bool {\n        return l.first < r.first;\n      });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    d_vals,\n    stable_sort_pairs_full_tile_t{});\n\n  REQUIRE( h_keys == d_keys );\n  REQUIRE( h_vals == d_vals );\n}\n\nCUB_TEST(\"Block merge sort can sort large tiles\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = std::uint16_t;\n\n  constexpr int items_per_thread = 2;\n\n  // Repurpose block sizes\n  constexpr int cmake_threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = cmake_threads_in_block < 256\n                                 ? 512\n                                 : 1024;\n\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\nCUB_TEST(\"Block merge sort is stable\",\n         \"[merge sort][block]\",\n         threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::less_comparable_t,\n                                   c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  thrust::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(CUB_SEED(10), d_keys);\n\n  thrust::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(\n      thrust::raw_pointer_cast(h_reference.data()), \n      thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(), \n      CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(\n    d_keys,\n    stable_sort_keys_full_tile_t{});\n\n  REQUIRE( h_reference == d_keys );\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_merge_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/detail/cpp_compatibility.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/warp/warp_store.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\n__global__ void warp_store_kernel(OutputIteratorT output_iterator, ActionT action)\n{\n  using warp_store_t = cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t    = typename warp_store_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  T reg[ITEMS_PER_THREAD];\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    reg[item] = static_cast<T>(tid * ITEMS_PER_THREAD + item);\n  }\n\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  warp_store_t store(storage[warp_id]);\n\n  action(store, output_iterator + (warp_id * tile_size), reg);\n}\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\nvoid warp_store(OutputIteratorT output_iterator, ActionT action)\n{\n  warp_store_kernel<StoreAlgorithm,\n                    LOGICAL_WARP_THREADS,\n                    ITEMS_PER_THREAD,\n                    TOTAL_WARPS,\n                    T,\n                    OutputIteratorT,\n                    ActionT><<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(output_iterator, action);\n}\n\nstruct guarded_store_t\n{\n  int valid_items;\n  template <cub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void\n  operator()(cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n             OutputIteratorT output,\n             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg, valid_items);\n  }\n};\n\nstruct unguarded_store_t\n{\n  template <cub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void\n  operator()(cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n             OutputIteratorT output,\n             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg);\n  }\n};\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nthrust::device_vector<T> compute_reference(int valid_items)\n{\n  const int tile_size        = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  const int total_item_count = TOTAL_WARPS * tile_size;\n  thrust::device_vector<T> d_input(total_item_count);\n\n  CUB_IF_CONSTEXPR(StoreAlgorithm == cub::WarpStoreAlgorithm::WARP_STORE_STRIPED)\n  {\n    thrust::host_vector<T> input(total_item_count);\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD * TOTAL_WARPS>(\n      input.begin());\n    d_input = input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{d_input.size()}, d_input);\n  }\n  if (valid_items != total_item_count)\n  {\n    for (int warp_id = 0; warp_id < TOTAL_WARPS; warp_id++)\n    {\n      thrust::fill(d_input.begin() + warp_id * tile_size + valid_items,\n                   d_input.begin() + (warp_id + 1) * tile_size,\n                   T{});\n    }\n  }\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms           = c2h::enum_type_list<cub::WarpStoreAlgorithm,\n                                       cub::WarpStoreAlgorithm::WARP_STORE_DIRECT,\n                                       cub::WarpStoreAlgorithm::WARP_STORE_STRIPED,\n                                       cub::WarpStoreAlgorithm::WARP_STORE_TRANSPOSE,\n                                       cub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE>;\nusing algorithm =\n  c2h::enum_type_list<cub::WarpStoreAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_store_modifier = c2h::enum_type_list<cub::CacheStoreModifier,\n                                                 cub::CacheStoreModifier::STORE_DEFAULT,\n                                                 cub::CacheStoreModifier::STORE_WB,\n                                                 cub::CacheStoreModifier::STORE_CG,\n                                                 cub::CacheStoreModifier::STORE_CS,\n                                                 cub::CacheStoreModifier::STORE_WT,\n                                                 cub::CacheStoreModifier::STORE_VOLATILE>;\n\nconstexpr int guarded_store_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads          = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread              = c2h::get<2, TestType>::value;\n  static constexpr cub::WarpStoreAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                   = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                     = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count              = total_warps * tile_size;\n};\n\nCUB_TEST(\"Warp store guarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items =\n    GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, guarded_store_t{valid_items});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store guarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr cub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items =\n    GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out =\n    cub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, guarded_store_t{valid_items});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store unguarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = params::tile_size;\n  auto out              = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, unguarded_store_t{});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store unguarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr cub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = params::tile_size;\n  auto out =\n    cub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, unguarded_store_t{});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/detail/cpp_compatibility.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/warp/warp_store.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\n__global__ void warp_store_kernel(OutputIteratorT output_iterator, ActionT action)\n{\n  using warp_store_t = hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t    = typename warp_store_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  T reg[ITEMS_PER_THREAD];\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    reg[item] = static_cast<T>(tid * ITEMS_PER_THREAD + item);\n  }\n\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  warp_store_t store(storage[warp_id]);\n\n  action(store, output_iterator + (warp_id * tile_size), reg);\n}\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\nvoid warp_store(OutputIteratorT output_iterator, ActionT action)\n{\n  warp_store_kernel<StoreAlgorithm,\n                    LOGICAL_WARP_THREADS,\n                    ITEMS_PER_THREAD,\n                    TOTAL_WARPS,\n                    T,\n                    OutputIteratorT,\n                    ActionT><<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(output_iterator, action);\n}\n\nstruct guarded_store_t\n{\n  int valid_items;\n  template <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void\n  operator()(hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n             OutputIteratorT output,\n             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg, valid_items);\n  }\n};\n\nstruct unguarded_store_t\n{\n  template <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void\n  operator()(hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n             OutputIteratorT output,\n             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg);\n  }\n};\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nthrust::device_vector<T> compute_reference(int valid_items)\n{\n  const int tile_size        = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  const int total_item_count = TOTAL_WARPS * tile_size;\n  thrust::device_vector<T> d_input(total_item_count);\n\n  CUB_IF_CONSTEXPR(StoreAlgorithm == hipcub::WarpStoreAlgorithm::WARP_STORE_STRIPED)\n  {\n    thrust::host_vector<T> input(total_item_count);\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD * TOTAL_WARPS>(\n      input.begin());\n    d_input = input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{d_input.size()}, d_input);\n  }\n  if (valid_items != total_item_count)\n  {\n    for (int warp_id = 0; warp_id < TOTAL_WARPS; warp_id++)\n    {\n      thrust::fill(d_input.begin() + warp_id * tile_size + valid_items,\n                   d_input.begin() + (warp_id + 1) * tile_size,\n                   T{});\n    }\n  }\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms           = c2h::enum_type_list<hipcub::WarpStoreAlgorithm,\n                                       hipcub::WarpStoreAlgorithm::WARP_STORE_DIRECT,\n                                       hipcub::WarpStoreAlgorithm::WARP_STORE_STRIPED,\n                                       hipcub::WarpStoreAlgorithm::WARP_STORE_TRANSPOSE,\n                                       hipcub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE>;\nusing algorithm =\n  c2h::enum_type_list<hipcub::WarpStoreAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_store_modifier = c2h::enum_type_list<hipcub::CacheStoreModifier,\n                                                 hipcub::CacheStoreModifier::STORE_DEFAULT,\n                                                 hipcub::CacheStoreModifier::STORE_WB,\n                                                 hipcub::CacheStoreModifier::STORE_CG,\n                                                 hipcub::CacheStoreModifier::STORE_CS,\n                                                 hipcub::CacheStoreModifier::STORE_WT,\n                                                 hipcub::CacheStoreModifier::STORE_VOLATILE>;\n\nconstexpr int guarded_store_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads          = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread              = c2h::get<2, TestType>::value;\n  static constexpr hipcub::WarpStoreAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                   = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                     = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count              = total_warps * tile_size;\n};\n\nCUB_TEST(\"Warp store guarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items =\n    GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, guarded_store_t{valid_items});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store guarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr hipcub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items =\n    GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out =\n    hipcub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, guarded_store_t{valid_items});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store unguarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = params::tile_size;\n  auto out              = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, unguarded_store_t{});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nCUB_TEST(\"Warp store unguarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr hipcub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  thrust::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = params::tile_size;\n  auto out =\n    hipcub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm,\n             params::logical_warp_threads,\n             params::items_per_thread,\n             params::total_warps,\n             type>(out, unguarded_store_t{});\n  auto d_expected_output = compute_reference<params::algorithm,\n                                             params::logical_warp_threads,\n                                             params::items_per_thread,\n                                             params::total_warps,\n                                             type>(valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_warp_store.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#undef NDEBUG\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cassert>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(cub::ScanTileState<MessageT> tile_state,\n                                           MessageT *tile_data)\n{\n  using scan_op_t         = cub::Sum;\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n  using tile_prefix_op    = cub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  const unsigned int threads_in_warp = 32;\n  const unsigned int tid             = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // \"Compute\" tile aggregate\n  MessageT tile_aggregate = tile_data[tile_idx];\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, tile_aggregate);\n      tile_data[tile_idx] = tile_aggregate;\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(tile_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, tile_aggregate);\n        tile_data[tile_idx]       = inclusive_prefix;\n      }\n    }\n    __syncthreads();\n\n    assert(tile_data[tile_idx] == prefix.GetInclusivePrefix());\n    assert(tile_aggregate == prefix.GetBlockAggregate());\n  }\n}\n\nusing message_types = c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>;\n\ntemplate <class MessageT>\nthrust::host_vector<MessageT>\ncompute_reference(const thrust::device_vector<MessageT> &tile_aggregates)\n{\n  if (tile_aggregates.empty())\n  {\n    return {};\n  }\n\n  thrust::host_vector<MessageT> reference = tile_aggregates;\n  MessageT *h_reference = thrust::raw_pointer_cast(reference.data());\n\n  MessageT aggregate = h_reference[0];\n  for (std::size_t i = 1; i < reference.size(); i++)\n  {\n    aggregate += h_reference[i];\n    h_reference[i] = aggregate;\n  }\n\n  return reference;\n}\n\nCUB_TEST(\"Decoupled look-back works with various message types\",\n         \"[decoupled look-back][device]\",\n         message_types)\n{\n  using message_t         = typename c2h::get<0, TestType>;\n  using scan_tile_state_t = cub::ScanTileState<message_t>;\n\n  const int max_tiles = 1024 * 1024;\n  const int num_tiles = GENERATE_COPY(take(10, random(1, max_tiles)));\n\n  thrust::device_vector<message_t> tile_data(num_tiles);\n  message_t *d_tile_data = thrust::raw_pointer_cast(tile_data.data());\n\n  c2h::gen(CUB_SEED(2), tile_data);\n  thrust::host_vector<message_t> reference = compute_reference(tile_data);\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(num_tiles, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  cudaError_t status = tile_status.Init(num_tiles, d_temp_storage, temp_storage_bytes);\n  REQUIRE(status == cudaSuccess);\n\n  const unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid = cub::DivideAndRoundUp(num_tiles, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, num_tiles);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  // Launch decoupled look-back\n  const unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<num_tiles, threads_in_block>>>(tile_status, d_tile_data);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  REQUIRE(reference == tile_data);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#undef NDEBUG\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cassert>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(hipcub::ScanTileState<MessageT> tile_state,\n                                           MessageT *tile_data)\n{\n  using scan_op_t         = hipcub::Sum;\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n  using tile_prefix_op    = hipcub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  const unsigned int threads_in_warp = 32;\n  const unsigned int tid             = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // \"Compute\" tile aggregate\n  MessageT tile_aggregate = tile_data[tile_idx];\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, tile_aggregate);\n      tile_data[tile_idx] = tile_aggregate;\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(tile_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, tile_aggregate);\n        tile_data[tile_idx]       = inclusive_prefix;\n      }\n    }\n    __syncthreads();\n\n    assert(tile_data[tile_idx] == prefix.GetInclusivePrefix());\n    assert(tile_aggregate == prefix.GetBlockAggregate());\n  }\n}\n\nusing message_types = c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>;\n\ntemplate <class MessageT>\nthrust::host_vector<MessageT>\ncompute_reference(const thrust::device_vector<MessageT> &tile_aggregates)\n{\n  if (tile_aggregates.empty())\n  {\n    return {};\n  }\n\n  thrust::host_vector<MessageT> reference = tile_aggregates;\n  MessageT *h_reference = thrust::raw_pointer_cast(reference.data());\n\n  MessageT aggregate = h_reference[0];\n  for (std::size_t i = 1; i < reference.size(); i++)\n  {\n    aggregate += h_reference[i];\n    h_reference[i] = aggregate;\n  }\n\n  return reference;\n}\n\nCUB_TEST(\"Decoupled look-back works with various message types\",\n         \"[decoupled look-back][device]\",\n         message_types)\n{\n  using message_t         = typename c2h::get<0, TestType>;\n  using scan_tile_state_t = hipcub::ScanTileState<message_t>;\n\n  const int max_tiles = 1024 * 1024;\n  const int num_tiles = GENERATE_COPY(take(10, random(1, max_tiles)));\n\n  thrust::device_vector<message_t> tile_data(num_tiles);\n  message_t *d_tile_data = thrust::raw_pointer_cast(tile_data.data());\n\n  c2h::gen(CUB_SEED(2), tile_data);\n  thrust::host_vector<message_t> reference = compute_reference(tile_data);\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(num_tiles, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  hipError_t status = tile_status.Init(num_tiles, d_temp_storage, temp_storage_bytes);\n  REQUIRE(status == hipSuccess);\n\n  const unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid = hipcub::DivideAndRoundUp(num_tiles, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, num_tiles);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  // Launch decoupled look-back\n  const unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<num_tiles, threads_in_block>>>(tile_status, d_tile_data);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  REQUIRE(reference == tile_data);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_device_decoupled_look_back.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_reduce.cuh>\n\n#include <thrust/host_vector.h>\n\n#include <numeric>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <cub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_reduce_kernel(T *in, T *out, int valid_items, ActionT action)\n{\n  using block_reduce_t    = cub::BlockReduce<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t         = typename block_reduce_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid = static_cast<int>(cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? in[idx] : T();\n  }\n  __syncthreads();\n\n  block_reduce_t reduce(storage);\n\n  T aggregate = action(reduce, thread_data, valid_items);\n\n  if (tid == 0)\n  {\n    out[0] = aggregate;\n  }\n}\n\ntemplate <cub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_reduce(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_reduce_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()),\n                        thrust::raw_pointer_cast(out.data()),\n                        static_cast<int>(in.size()),\n                        action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nstruct sum_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int valid_items) const\n  {\n    return reduce.Sum(thread_data[0], valid_items);\n  }\n};\n\nstruct sum_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int /* valid_items */) const\n  {\n    return reduce.Sum(thread_data);\n  }\n};\n\nstruct max_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Reduce(thread_data[0], cub::Max{}, valid_items);\n  }\n};\n\nstruct max_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int /* valid_items */) const\n  {\n    return reduce.Reduce(thread_data, cub::Max{});\n  }\n};\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t, float, double>;\nusing vec_types = c2h::type_list<ulonglong4, uchar3, short2>;\n\n// %PARAM% TEST_DIM_X dimx 7:32:65:128\n// %PARAM% TEST_DIM_YZ dimyz 1:2\n\nusing block_dim_xs           = c2h::enum_type_list<int, TEST_DIM_X>;\nusing block_dim_yzs          = c2h::enum_type_list<int, TEST_DIM_YZ>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 4>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithm =\n  c2h::enum_type_list<cub::BlockReduceAlgorithm,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_WARP_REDUCTIONS>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int tile_size = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nCUB_TEST(\"Block reduce works with sum\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, sum_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with sum in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  std::vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom op\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(\n    1,\n    std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, max_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom op in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(\n    1,\n    std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, max_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom types\",\n         \"[reduce][block]\",\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using type = c2h::custom_type_t<c2h::accumulateable_t,\n                                  c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 1;\n  constexpr int block_dim_x = c2h::get<0, TestType>::value;\n  constexpr int block_dim_y = c2h::get<1, TestType>::value;\n  constexpr int block_dim_z = block_dim_y;\n  constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<2, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm,\n                   items_per_thread,\n                   block_dim_x,\n                   block_dim_y,\n                   block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\nCUB_TEST(\"Block reduce works with vec types\",\n         \"[reduce][block]\",\n         vec_types,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using type = c2h::get<0, TestType>;\n\n  constexpr int items_per_thread = 1;\n  constexpr int block_dim_x = c2h::get<1, TestType>::value;\n  constexpr int block_dim_y = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z = block_dim_y;\n  constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<3, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm,\n                   items_per_thread,\n                   block_dim_x,\n                   block_dim_y,\n                   block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_reduce.cuh>\n\n#include <thrust/host_vector.h>\n\n#include <numeric>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <hipcub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_reduce_kernel(T *in, T *out, int valid_items, ActionT action)\n{\n  using block_reduce_t    = hipcub::BlockReduce<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t         = typename block_reduce_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid = static_cast<int>(hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? in[idx] : T();\n  }\n  __syncthreads();\n\n  block_reduce_t reduce(storage);\n\n  T aggregate = action(reduce, thread_data, valid_items);\n\n  if (tid == 0)\n  {\n    out[0] = aggregate;\n  }\n}\n\ntemplate <hipcub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_reduce(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_reduce_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()),\n                        thrust::raw_pointer_cast(out.data()),\n                        static_cast<int>(in.size()),\n                        action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nstruct sum_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int valid_items) const\n  {\n    return reduce.Sum(thread_data[0], valid_items);\n  }\n};\n\nstruct sum_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int /* valid_items */) const\n  {\n    return reduce.Sum(thread_data);\n  }\n};\n\nstruct max_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Reduce(thread_data[0], hipcub::Max{}, valid_items);\n  }\n};\n\nstruct max_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT &reduce,\n                          T (&thread_data)[ItemsPerThread],\n                          int /* valid_items */) const\n  {\n    return reduce.Reduce(thread_data, hipcub::Max{});\n  }\n};\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t, float, double>;\nusing vec_types = c2h::type_list<ulonglong4, uchar3, short2>;\n\n// %PARAM% TEST_DIM_X dimx 7:32:65:128\n// %PARAM% TEST_DIM_YZ dimyz 1:2\n\nusing block_dim_xs           = c2h::enum_type_list<int, TEST_DIM_X>;\nusing block_dim_yzs          = c2h::enum_type_list<int, TEST_DIM_YZ>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 4>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithm =\n  c2h::enum_type_list<hipcub::BlockReduceAlgorithm,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_WARP_REDUCTIONS>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int tile_size = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nCUB_TEST(\"Block reduce works with sum\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, sum_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with sum in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  std::vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom op\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(\n    1,\n    std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, max_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom op in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(\n    1,\n    std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n                   params::items_per_thread,\n                   params::block_dim_x,\n                   params::block_dim_y,\n                   params::block_dim_z,\n                   type>(d_in, d_out, max_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nCUB_TEST(\"Block reduce works with custom types\",\n         \"[reduce][block]\",\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using type = c2h::custom_type_t<c2h::accumulateable_t,\n                                  c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 1;\n  constexpr int block_dim_x = c2h::get<0, TestType>::value;\n  constexpr int block_dim_y = c2h::get<1, TestType>::value;\n  constexpr int block_dim_z = block_dim_y;\n  constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<2, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm,\n                   items_per_thread,\n                   block_dim_x,\n                   block_dim_y,\n                   block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\nCUB_TEST(\"Block reduce works with vec types\",\n         \"[reduce][block]\",\n         vec_types,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using type = c2h::get<0, TestType>;\n\n  constexpr int items_per_thread = 1;\n  constexpr int block_dim_x = c2h::get<1, TestType>::value;\n  constexpr int block_dim_y = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z = block_dim_y;\n  constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<3, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  thrust::device_vector<type> d_out(1);\n  thrust::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  thrust::host_vector<type> h_in = d_in;\n  thrust::host_vector<type> h_reference(1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type &lhs, const type &rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm,\n                   items_per_thread,\n                   block_dim_x,\n                   block_dim_y,\n                   block_dim_z,\n                   type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_reduce.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <cub/block/block_shuffle.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          int ItemsPerThread,   \n          class T,  \n          class ActionT>\n__global__ void block_shuffle_kernel(T *data, ActionT action)\n{\n  using block_shuffle_t = cub::BlockShuffle<T, BlockDimX, BlockDimY, BlockDimZ>;\n  using temp_storage_t  = typename block_shuffle_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_data[ItemsPerThread];\n\n  data += cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ) * ItemsPerThread;\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_data[item] = data[item];\n  }\n  __syncthreads();\n\n  block_shuffle_t block_shuffle(temp_storage);\n  action(block_shuffle, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    data[item] = thread_data[item];\n  }\n}\n\nstruct up_op_t \n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Up(thread_data, thread_data);\n  }\n};\n\nstruct offset_op_t \n{\n  int m_distance;\n\n  __host__ offset_op_t(int distance)\n    : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Offset(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\nstruct rotate_op_t \n{\n  unsigned int m_distance;\n\n  __host__ rotate_op_t(unsigned int distance)\n    : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Rotate(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\ntemplate <class T>\nstruct up_with_suffix_op_t \n{\n  int m_target_thread_id;\n  T * m_d_suffix_ptr;\n\n  __host__ up_with_suffix_op_t(\n      int target_thread_id,\n      T *d_suffix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_suffix_ptr(d_suffix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    T suffix{};\n\n    block_shuffle.Up(thread_data, thread_data, suffix);\n\n    if (cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_suffix_ptr[0] = suffix;\n    }\n  }\n};\n\nstruct down_op_t \n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Down(thread_data, thread_data);\n  }\n};\n\ntemplate <class T>\nstruct down_with_prefix_op_t \n{\n  int m_target_thread_id;\n  T * m_d_prefix_ptr;\n\n  __host__ down_with_prefix_op_t(\n      int target_thread_id,\n      T *d_prefix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_prefix_ptr(d_prefix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    T prefix{};\n\n    block_shuffle.Down(thread_data, thread_data, prefix);\n\n    if (cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_prefix_ptr[0] = prefix;\n    }\n  }\n};\n\ntemplate <int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_shuffle(\n    thrust::device_vector<T> &data, \n    ActionT action)\n{\n  dim3 block(BlockDimX, BlockDimY, BlockDimZ);\n  block_shuffle_kernel<BlockDimX, BlockDimY, BlockDimZ, ItemsPerThread>\n    <<<1, block>>>(\n        thrust::raw_pointer_cast(data.data()),\n        action);\n\n  REQUIRE( cudaSuccess == cudaPeekAtLastError() );\n  REQUIRE( cudaSuccess == cudaDeviceSynchronize() );\n}\n\n// %PARAM% MULTI_DIM mdim 0:1\n// %PARAM% DIM_IDX dim_idx 0:1:2\n\n#if MULTI_DIM\nusing block_dim_xs  = c2h::enum_type_list<int, 7, 32, 64>;\nusing block_dim_yz  = c2h::enum_type_list<int, 2>;\n#else\nusing block_dim_xs  = c2h::enum_type_list<int, 64, 512, 1024>;\nusing block_dim_yz  = c2h::enum_type_list<int, 1>;\n#endif\n\nusing block_dim_x   = c2h::enum_type_list<int, c2h::get<DIM_IDX, block_dim_xs>::value>;\n\nusing types = c2h::type_list<std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 15>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int threads_in_block = block_dim_x * block_dim_y * block_dim_z;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n};\n\n\nCUB_TEST(\"Block shuffle offset works\",\n         \"[shuffle][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int distance = \n    GENERATE_COPY(take(4, random(1 - params::tile_size, \n                                 params::tile_size - 1)));\n\n  thrust::host_vector<type> h_data = d_data;\n  thrust::host_vector<type> h_ref(params::tile_size);\n\n  for (int i = 0; i < static_cast<int>(h_data.size()); i++)\n  {\n    const int source = i + distance;\n    h_ref[i] = (source >= 0) && (source < params::tile_size) \n             ? h_data[source]\n             : h_data[i];\n  }\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, offset_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle rotate works\",\n         \"[shuffle][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref = d_data;\n\n  const unsigned int distance = \n    GENERATE_COPY(take(4, random(0, params::tile_size - 1)));\n\n  thrust::host_vector<type> h_ref = d_data;\n  std::rotate(h_ref.begin(), h_ref.begin() + distance, h_ref.end());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, rotate_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle up works\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, up_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle up works when suffix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int target_thread_id = \n    GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  thrust::device_vector<type> d_suffix(1);\n  thrust::device_vector<type> d_suffix_ref(1);\n  thrust::copy(d_data.end() - 1, d_data.end(), d_suffix_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(\n    d_data,\n    up_with_suffix_op_t<type>{target_thread_id,\n                              thrust::raw_pointer_cast(d_suffix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_suffix_ref == d_suffix);\n}\n\nCUB_TEST(\"Block shuffle down works\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, down_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle down works when prefix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int target_thread_id = \n    GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  thrust::device_vector<type> d_prefix(1);\n  thrust::device_vector<type> d_prefix_ref(1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_prefix_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(\n    d_data,\n    down_with_prefix_op_t<type>{target_thread_id,\n                                thrust::raw_pointer_cast(d_prefix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_prefix_ref == d_prefix);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <cub/block/block_shuffle.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          int ItemsPerThread,   \n          class T,  \n          class ActionT>\n__global__ void block_shuffle_kernel(T *data, ActionT action)\n{\n  using block_shuffle_t = hipcub::BlockShuffle<T, BlockDimX, BlockDimY, BlockDimZ>;\n  using temp_storage_t  = typename block_shuffle_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_data[ItemsPerThread];\n\n  data += hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ) * ItemsPerThread;\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_data[item] = data[item];\n  }\n  __syncthreads();\n\n  block_shuffle_t block_shuffle(temp_storage);\n  action(block_shuffle, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    data[item] = thread_data[item];\n  }\n}\n\nstruct up_op_t \n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Up(thread_data, thread_data);\n  }\n};\n\nstruct offset_op_t \n{\n  int m_distance;\n\n  __host__ offset_op_t(int distance)\n    : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Offset(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\nstruct rotate_op_t \n{\n  unsigned int m_distance;\n\n  __host__ rotate_op_t(unsigned int distance)\n    : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Rotate(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\ntemplate <class T>\nstruct up_with_suffix_op_t \n{\n  int m_target_thread_id;\n  T * m_d_suffix_ptr;\n\n  __host__ up_with_suffix_op_t(\n      int target_thread_id,\n      T *d_suffix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_suffix_ptr(d_suffix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    T suffix{};\n\n    block_shuffle.Up(thread_data, thread_data, suffix);\n\n    if (hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_suffix_ptr[0] = suffix;\n    }\n  }\n};\n\nstruct down_op_t \n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Down(thread_data, thread_data);\n  }\n};\n\ntemplate <class T>\nstruct down_with_prefix_op_t \n{\n  int m_target_thread_id;\n  T * m_d_prefix_ptr;\n\n  __host__ down_with_prefix_op_t(\n      int target_thread_id,\n      T *d_prefix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_prefix_ptr(d_prefix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void\n  operator()(BlockShuffleT &block_shuffle,\n             T (&thread_data)[ItemsPerThread]) const\n  {\n    T prefix{};\n\n    block_shuffle.Down(thread_data, thread_data, prefix);\n\n    if (hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_prefix_ptr[0] = prefix;\n    }\n  }\n};\n\ntemplate <int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_shuffle(\n    thrust::device_vector<T> &data, \n    ActionT action)\n{\n  dim3 block(BlockDimX, BlockDimY, BlockDimZ);\n  block_shuffle_kernel<BlockDimX, BlockDimY, BlockDimZ, ItemsPerThread>\n    <<<1, block>>>(\n        thrust::raw_pointer_cast(data.data()),\n        action);\n\n  REQUIRE( hipSuccess == hipPeekAtLastError() );\n  REQUIRE( hipSuccess == hipDeviceSynchronize() );\n}\n\n// %PARAM% MULTI_DIM mdim 0:1\n// %PARAM% DIM_IDX dim_idx 0:1:2\n\n#if MULTI_DIM\nusing block_dim_xs  = c2h::enum_type_list<int, 7, 32, 64>;\nusing block_dim_yz  = c2h::enum_type_list<int, 2>;\n#else\nusing block_dim_xs  = c2h::enum_type_list<int, 64, 512, 1024>;\nusing block_dim_yz  = c2h::enum_type_list<int, 1>;\n#endif\n\nusing block_dim_x   = c2h::enum_type_list<int, c2h::get<DIM_IDX, block_dim_xs>::value>;\n\nusing types = c2h::type_list<std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 15>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int threads_in_block = block_dim_x * block_dim_y * block_dim_z;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n};\n\n\nCUB_TEST(\"Block shuffle offset works\",\n         \"[shuffle][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int distance = \n    GENERATE_COPY(take(4, random(1 - params::tile_size, \n                                 params::tile_size - 1)));\n\n  thrust::host_vector<type> h_data = d_data;\n  thrust::host_vector<type> h_ref(params::tile_size);\n\n  for (int i = 0; i < static_cast<int>(h_data.size()); i++)\n  {\n    const int source = i + distance;\n    h_ref[i] = (source >= 0) && (source < params::tile_size) \n             ? h_data[source]\n             : h_data[i];\n  }\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, offset_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle rotate works\",\n         \"[shuffle][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref = d_data;\n\n  const unsigned int distance = \n    GENERATE_COPY(take(4, random(0, params::tile_size - 1)));\n\n  thrust::host_vector<type> h_ref = d_data;\n  std::rotate(h_ref.begin(), h_ref.begin() + distance, h_ref.end());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, rotate_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle up works\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, up_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle up works when suffix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int target_thread_id = \n    GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  thrust::device_vector<type> d_suffix(1);\n  thrust::device_vector<type> d_suffix_ref(1);\n  thrust::copy(d_data.end() - 1, d_data.end(), d_suffix_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(\n    d_data,\n    up_with_suffix_op_t<type>{target_thread_id,\n                              thrust::raw_pointer_cast(d_suffix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_suffix_ref == d_suffix);\n}\n\nCUB_TEST(\"Block shuffle down works\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(d_data, down_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nCUB_TEST(\"Block shuffle down works when prefix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const int target_thread_id = \n    GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  thrust::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  thrust::device_vector<type> d_prefix(1);\n  thrust::device_vector<type> d_prefix_ref(1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_prefix_ref.begin());\n\n  block_shuffle<params::items_per_thread,\n                params::block_dim_x,\n                params::block_dim_y,\n                params::block_dim_z>(\n    d_data,\n    down_with_prefix_op_t<type>{target_thread_id,\n                                thrust::raw_pointer_cast(d_prefix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_prefix_ref == d_prefix);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_block_shuffle.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of iterator utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <iterator>\n#include <stdio.h>\n#include <typeinfo>\n\n#include <cub/iterator/arg_index_input_iterator.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/tex_obj_input_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n\n#include <cub/util_type.cuh>\n#include <cub/util_allocator.cuh>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose = false;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\ntemplate <typename T>\nstruct TransformOp\n{\n    // Increment transform\n    __host__ __device__ __forceinline__ T operator()(T input) const\n    {\n        T addend;\n        InitValue(INTEGER_SEED, addend, 1);\n        return input + addend;\n    }\n};\n\nstruct SelectOp\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ bool operator()(T input)\n    {\n        return true;\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Test random access input iterator\n */\ntemplate <\n    typename InputIteratorT,\n    typename T>\n__global__ void Kernel(\n    InputIteratorT    d_in,\n    T                 *d_out,\n    InputIteratorT    *d_itrs)\n{\n    d_out[0] = *d_in;               // Value at offset 0\n    d_out[1] = d_in[100];           // Value at offset 100\n    d_out[2] = *(d_in + 1000);      // Value at offset 1000\n    d_out[3] = *(d_in + 10000);     // Value at offset 10000\n\n    d_in++;\n    d_out[4] = d_in[0];             // Value at offset 1\n\n    d_in += 20;\n    d_out[5] = d_in[0];             // Value at offset 21\n    d_itrs[0] = d_in;               // Iterator at offset 21\n\n    d_in -= 10;\n    d_out[6] = d_in[0];             // Value at offset 11;\n\n    d_in -= 11;\n    d_out[7] = d_in[0];             // Value at offset 0\n    d_itrs[1] = d_in;               // Iterator at offset 0\n}\n\n\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n\n/**\n * Run iterator test on device\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T,\n    int             TEST_VALUES>\nvoid Test(\n    InputIteratorT  d_in,\n    T               (&h_reference)[TEST_VALUES])\n{\n    // Allocate device arrays\n    T                 *d_out    = NULL;\n    InputIteratorT    *d_itrs   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out,     sizeof(T) * TEST_VALUES));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_itrs,    sizeof(InputIteratorT) * 2));\n\n    int compare;\n\n    // Run unguarded kernel\n    Kernel<<<1, 1>>>(d_in, d_out, d_itrs);\n\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Check results\n    compare = CompareDeviceResults(h_reference, d_out, TEST_VALUES, g_verbose, g_verbose);\n    printf(\"\\tValues: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 21\n    InputIteratorT h_itr = d_in + 21;\n    compare = CompareDeviceResults(&h_itr, d_itrs, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 0\n    compare = CompareDeviceResults(&d_in, d_itrs + 1, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_itrs) CubDebugExit(g_allocator.DeviceFree(d_itrs));\n}\n\n\n/**\n * Test constant iterator\n */\ntemplate <typename T>\nvoid TestConstant(T base)\n{\n    printf(\"\\nTesting constant iterator on type %s (base: %lld)\\n\", typeid(T).name(), (unsigned long long) (base)); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    T h_reference[8] = {base, base, base, base, base, base, base, base};\n    ConstantInputIterator<T> d_itr(base);\n    Test(d_itr, h_reference);\n}\n\n\n/**\n * Test counting iterator\n */\ntemplate <typename T>\nvoid TestCounting(T base)\n{\n    printf(\"\\nTesting counting iterator on type %s (base: %d) \\n\", typeid(T).name(), int(base)); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = static_cast<T>(base + 0);          // Value at offset 0\n    h_reference[1] = static_cast<T>(base + 100);        // Value at offset 100\n    h_reference[2] = static_cast<T>(base + 1000);       // Value at offset 1000\n    h_reference[3] = static_cast<T>(base + 10000);      // Value at offset 10000\n    h_reference[4] = static_cast<T>(base + 1);          // Value at offset 1\n    h_reference[5] = static_cast<T>(base + 21);         // Value at offset 21\n    h_reference[6] = static_cast<T>(base + 11);         // Value at offset 11\n    h_reference[7] = static_cast<T>(base + 0);          // Value at offset 0;\n\n    CountingInputIterator<T> d_itr(base);\n    Test(d_itr, h_reference);\n}\n\n\n/**\n * Test modified iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestModified()\n{\n    printf(\"\\nTesting cache-modified iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, cudaMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    Test(CacheModifiedInputIterator<LOAD_DEFAULT, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CA, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CG, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CS, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CV, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_LDG, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_VOLATILE, T>((CastT*) d_data), h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n\n/**\n * Test transform iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTransform()\n{\n    printf(\"\\nTesting transform iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        InitValue(INTEGER_SEED, h_data[i], i);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, cudaMemcpyHostToDevice));\n\n    TransformOp<T> op;\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = op(h_data[0]);          // Value at offset 0\n    h_reference[1] = op(h_data[100]);        // Value at offset 100\n    h_reference[2] = op(h_data[1000]);       // Value at offset 1000\n    h_reference[3] = op(h_data[10000]);      // Value at offset 10000\n    h_reference[4] = op(h_data[1]);          // Value at offset 1\n    h_reference[5] = op(h_data[21]);         // Value at offset 21\n    h_reference[6] = op(h_data[11]);         // Value at offset 11\n    h_reference[7] = op(h_data[0]);          // Value at offset 0;\n\n    TransformInputIterator<T, TransformOp<T>, CastT*> d_itr((CastT*) d_data, op);\n    Test(d_itr, h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n\n/**\n * Test tex-obj texture iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexObj()\n{\n    printf(\"\\nTesting tex-obj iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    const unsigned int TEST_VALUES          = 11000;\n    const unsigned int DUMMY_OFFSET         = 500;\n    const unsigned int DUMMY_TEST_VALUES    = TEST_VALUES - DUMMY_OFFSET;\n\n    T *h_data = new T[TEST_VALUES];\n    for (unsigned int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data   = NULL;\n    T *d_dummy  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, cudaMemcpyHostToDevice));\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_dummy, h_data + DUMMY_OFFSET, sizeof(T) * DUMMY_TEST_VALUES, cudaMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    // Create and bind obj-based test iterator\n    TexObjInputIterator<T> d_obj_itr;\n    CubDebugExit(d_obj_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    Test(d_obj_itr, h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n    if (d_dummy) CubDebugExit(g_allocator.DeviceFree(d_dummy));\n}\n\n/**\n * Test texture transform iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexTransform()\n{\n    printf(\"\\nTesting tex-transform iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        InitValue(INTEGER_SEED, h_data[i], i);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, cudaMemcpyHostToDevice));\n\n    TransformOp<T> op;\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = op(h_data[0]);          // Value at offset 0\n    h_reference[1] = op(h_data[100]);        // Value at offset 100\n    h_reference[2] = op(h_data[1000]);       // Value at offset 1000\n    h_reference[3] = op(h_data[10000]);      // Value at offset 10000\n    h_reference[4] = op(h_data[1]);          // Value at offset 1\n    h_reference[5] = op(h_data[21]);         // Value at offset 21\n    h_reference[6] = op(h_data[11]);         // Value at offset 11\n    h_reference[7] = op(h_data[0]);          // Value at offset 0;\n\n    // Create and bind texture iterator\n    typedef TexObjInputIterator<T> TextureIterator;\n\n    TextureIterator d_tex_itr;\n    CubDebugExit(d_tex_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    // Create transform iterator\n    TransformInputIterator<T, TransformOp<T>, TextureIterator> xform_itr(d_tex_itr, op);\n\n    Test(xform_itr, h_reference);\n\n    CubDebugExit(d_tex_itr.UnbindTexture());\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n/**\n * Run non-integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test(Int2Type<false> /* is_integer */)\n{\n    TestModified<T, CastT>();\n    TestTransform<T, CastT>();\n    TestTexObj<T, CastT>();\n    TestTexTransform<T, CastT>();\n}\n\n/**\n * Run integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test(Int2Type<true> /* is_integer */)\n{\n    TestConstant<T>(0);\n    TestConstant<T>(99);\n\n    TestCounting<T>(0);\n    TestCounting<T>(99);\n\n    // Run non-integer tests\n    Test<T, CastT>(Int2Type<false>());\n}\n\n/**\n * Run tests\n */\ntemplate <typename T>\nvoid Test()\n{\n    enum {\n        IS_INTEGER = (Traits<T>::CATEGORY == SIGNED_INTEGER) || (Traits<T>::CATEGORY == UNSIGNED_INTEGER)\n    };\n\n    // Test non-const type\n    Test<T, T>(Int2Type<IS_INTEGER>());\n\n    // Test non-const type\n    Test<T, const T>(Int2Type<IS_INTEGER>());\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Get ptx version\n    int ptx_version = 0;\n    CubDebugExit(PtxVersion(ptx_version));\n\n    // Evaluate different data types\n    Test<signed char>();\n    Test<short>();\n    Test<int>();\n    Test<long>();\n    Test<long long>();\n    Test<float>();\n    Test<double>();\n\n    Test<char2>();\n    Test<short2>();\n    Test<int2>();\n    Test<long2>();\n    Test<longlong2>();\n    Test<float2>();\n    Test<double2>();\n\n    Test<char3>();\n    Test<short3>();\n    Test<int3>();\n    Test<long3>();\n    Test<longlong3>();\n    Test<float3>();\n    Test<double3>();\n\n    Test<char4>();\n    Test<short4>();\n    Test<int4>();\n    Test<long4>();\n    Test<longlong4>();\n    Test<float4>();\n    Test<double4>();\n\n    Test<TestFoo>();\n    Test<TestBar>();\n\n    printf(\"\\nTest complete\\n\"); fflush(stdout);\n\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of iterator utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <iterator>\n#include <stdio.h>\n#include <typeinfo>\n\n#include <cub/iterator/arg_index_input_iterator.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/tex_obj_input_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n\n#include <cub/util_type.cuh>\n#include <cub/util_allocator.cuh>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose = false;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\ntemplate <typename T>\nstruct TransformOp\n{\n    // Increment transform\n    __host__ __device__ __forceinline__ T operator()(T input) const\n    {\n        T addend;\n        InitValue(INTEGER_SEED, addend, 1);\n        return input + addend;\n    }\n};\n\nstruct SelectOp\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ bool operator()(T input)\n    {\n        return true;\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Test random access input iterator\n */\ntemplate <\n    typename InputIteratorT,\n    typename T>\n__global__ void Kernel(\n    InputIteratorT    d_in,\n    T                 *d_out,\n    InputIteratorT    *d_itrs)\n{\n    d_out[0] = *d_in;               // Value at offset 0\n    d_out[1] = d_in[100];           // Value at offset 100\n    d_out[2] = *(d_in + 1000);      // Value at offset 1000\n    d_out[3] = *(d_in + 10000);     // Value at offset 10000\n\n    d_in++;\n    d_out[4] = d_in[0];             // Value at offset 1\n\n    d_in += 20;\n    d_out[5] = d_in[0];             // Value at offset 21\n    d_itrs[0] = d_in;               // Iterator at offset 21\n\n    d_in -= 10;\n    d_out[6] = d_in[0];             // Value at offset 11;\n\n    d_in -= 11;\n    d_out[7] = d_in[0];             // Value at offset 0\n    d_itrs[1] = d_in;               // Iterator at offset 0\n}\n\n\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n\n/**\n * Run iterator test on device\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T,\n    int             TEST_VALUES>\nvoid Test(\n    InputIteratorT  d_in,\n    T               (&h_reference)[TEST_VALUES])\n{\n    // Allocate device arrays\n    T                 *d_out    = NULL;\n    InputIteratorT    *d_itrs   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out,     sizeof(T) * TEST_VALUES));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_itrs,    sizeof(InputIteratorT) * 2));\n\n    int compare;\n\n    // Run unguarded kernel\n    Kernel<<<1, 1>>>(d_in, d_out, d_itrs);\n\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Check results\n    compare = CompareDeviceResults(h_reference, d_out, TEST_VALUES, g_verbose, g_verbose);\n    printf(\"\\tValues: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 21\n    InputIteratorT h_itr = d_in + 21;\n    compare = CompareDeviceResults(&h_itr, d_itrs, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 0\n    compare = CompareDeviceResults(&d_in, d_itrs + 1, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_itrs) CubDebugExit(g_allocator.DeviceFree(d_itrs));\n}\n\n\n/**\n * Test constant iterator\n */\ntemplate <typename T>\nvoid TestConstant(T base)\n{\n    printf(\"\\nTesting constant iterator on type %s (base: %lld)\\n\", typeid(T).name(), (unsigned long long) (base)); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    T h_reference[8] = {base, base, base, base, base, base, base, base};\n    ConstantInputIterator<T> d_itr(base);\n    Test(d_itr, h_reference);\n}\n\n\n/**\n * Test counting iterator\n */\ntemplate <typename T>\nvoid TestCounting(T base)\n{\n    printf(\"\\nTesting counting iterator on type %s (base: %d) \\n\", typeid(T).name(), int(base)); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = static_cast<T>(base + 0);          // Value at offset 0\n    h_reference[1] = static_cast<T>(base + 100);        // Value at offset 100\n    h_reference[2] = static_cast<T>(base + 1000);       // Value at offset 1000\n    h_reference[3] = static_cast<T>(base + 10000);      // Value at offset 10000\n    h_reference[4] = static_cast<T>(base + 1);          // Value at offset 1\n    h_reference[5] = static_cast<T>(base + 21);         // Value at offset 21\n    h_reference[6] = static_cast<T>(base + 11);         // Value at offset 11\n    h_reference[7] = static_cast<T>(base + 0);          // Value at offset 0;\n\n    CountingInputIterator<T> d_itr(base);\n    Test(d_itr, h_reference);\n}\n\n\n/**\n * Test modified iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestModified()\n{\n    printf(\"\\nTesting cache-modified iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, hipMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    Test(CacheModifiedInputIterator<LOAD_DEFAULT, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CA, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CG, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CS, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_CV, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_LDG, T>((CastT*) d_data), h_reference);\n    Test(CacheModifiedInputIterator<LOAD_VOLATILE, T>((CastT*) d_data), h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n\n/**\n * Test transform iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTransform()\n{\n    printf(\"\\nTesting transform iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        InitValue(INTEGER_SEED, h_data[i], i);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, hipMemcpyHostToDevice));\n\n    TransformOp<T> op;\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = op(h_data[0]);          // Value at offset 0\n    h_reference[1] = op(h_data[100]);        // Value at offset 100\n    h_reference[2] = op(h_data[1000]);       // Value at offset 1000\n    h_reference[3] = op(h_data[10000]);      // Value at offset 10000\n    h_reference[4] = op(h_data[1]);          // Value at offset 1\n    h_reference[5] = op(h_data[21]);         // Value at offset 21\n    h_reference[6] = op(h_data[11]);         // Value at offset 11\n    h_reference[7] = op(h_data[0]);          // Value at offset 0;\n\n    TransformInputIterator<T, TransformOp<T>, CastT*> d_itr((CastT*) d_data, op);\n    Test(d_itr, h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n\n/**\n * Test tex-obj texture iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexObj()\n{\n    printf(\"\\nTesting tex-obj iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    const unsigned int TEST_VALUES          = 11000;\n    const unsigned int DUMMY_OFFSET         = 500;\n    const unsigned int DUMMY_TEST_VALUES    = TEST_VALUES - DUMMY_OFFSET;\n\n    T *h_data = new T[TEST_VALUES];\n    for (unsigned int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data   = NULL;\n    T *d_dummy  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, hipMemcpyHostToDevice));\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_dummy, h_data + DUMMY_OFFSET, sizeof(T) * DUMMY_TEST_VALUES, hipMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    // Create and bind obj-based test iterator\n    TexObjInputIterator<T> d_obj_itr;\n    CubDebugExit(d_obj_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    Test(d_obj_itr, h_reference);\n\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n    if (d_dummy) CubDebugExit(g_allocator.DeviceFree(d_dummy));\n}\n\n/**\n * Test texture transform iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexTransform()\n{\n    printf(\"\\nTesting tex-transform iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES = 11000;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        InitValue(INTEGER_SEED, h_data[i], i);\n    }\n\n    // Allocate device arrays\n    T *d_data = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, hipMemcpyHostToDevice));\n\n    TransformOp<T> op;\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = op(h_data[0]);          // Value at offset 0\n    h_reference[1] = op(h_data[100]);        // Value at offset 100\n    h_reference[2] = op(h_data[1000]);       // Value at offset 1000\n    h_reference[3] = op(h_data[10000]);      // Value at offset 10000\n    h_reference[4] = op(h_data[1]);          // Value at offset 1\n    h_reference[5] = op(h_data[21]);         // Value at offset 21\n    h_reference[6] = op(h_data[11]);         // Value at offset 11\n    h_reference[7] = op(h_data[0]);          // Value at offset 0;\n\n    // Create and bind texture iterator\n    typedef TexObjInputIterator<T> TextureIterator;\n\n    TextureIterator d_tex_itr;\n    CubDebugExit(d_tex_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    // Create transform iterator\n    TransformInputIterator<T, TransformOp<T>, TextureIterator> xform_itr(d_tex_itr, op);\n\n    Test(xform_itr, h_reference);\n\n    CubDebugExit(d_tex_itr.UnbindTexture());\n    if (h_data) delete[] h_data;\n    if (d_data) CubDebugExit(g_allocator.DeviceFree(d_data));\n}\n\n/**\n * Run non-integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test(Int2Type<false> /* is_integer */)\n{\n    TestModified<T, CastT>();\n    TestTransform<T, CastT>();\n    TestTexObj<T, CastT>();\n    TestTexTransform<T, CastT>();\n}\n\n/**\n * Run integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test(Int2Type<true> /* is_integer */)\n{\n    TestConstant<T>(0);\n    TestConstant<T>(99);\n\n    TestCounting<T>(0);\n    TestCounting<T>(99);\n\n    // Run non-integer tests\n    Test<T, CastT>(Int2Type<false>());\n}\n\n/**\n * Run tests\n */\ntemplate <typename T>\nvoid Test()\n{\n    enum {\n        IS_INTEGER = (Traits<T>::CATEGORY == SIGNED_INTEGER) || (Traits<T>::CATEGORY == UNSIGNED_INTEGER)\n    };\n\n    // Test non-const type\n    Test<T, T>(Int2Type<IS_INTEGER>());\n\n    // Test non-const type\n    Test<T, const T>(Int2Type<IS_INTEGER>());\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Get ptx version\n    int ptx_version = 0;\n    CubDebugExit(PtxVersion(ptx_version));\n\n    // Evaluate different data types\n    Test<signed char>();\n    Test<short>();\n    Test<int>();\n    Test<long>();\n    Test<long long>();\n    Test<float>();\n    Test<double>();\n\n    Test<char2>();\n    Test<short2>();\n    Test<int2>();\n    Test<long2>();\n    Test<longlong2>();\n    Test<float2>();\n    Test<double2>();\n\n    Test<char3>();\n    Test<short3>();\n    Test<int3>();\n    Test<long3>();\n    Test<longlong3>();\n    Test<float3>();\n    Test<double3>();\n\n    Test<char4>();\n    Test<short4>();\n    Test<int4>();\n    Test<long4>();\n    Test<longlong4>();\n    Test<float4>();\n    Test<double4>();\n\n    Test<TestFoo>();\n    Test<TestBar>();\n\n    printf(\"\\nTest complete\\n\"); fflush(stdout);\n\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_iterator.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceReduce utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_reduce.cuh>\n#include <cub/device/device_segmented_reduce.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_math.cuh>\n#include <cub/util_type.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include <cstdio>\n#include <limits>\n#include <type_traits>\n#include <typeinfo>\n\n#include \"test_util.h\"\n#include <nv/target>\n\n#define TEST_HALF_T !_NVHPC_CUDA\n\n#define TEST_BF_T !_NVHPC_CUDA\n\n#if TEST_HALF_T\n#include <cuda_fp16.h>\n\n// Half support is provided by SM53+. We currently test against a few older architectures. \n// The specializations below can be removed once we drop these architectures.  \nnamespace cub {\n\ntemplate <>\n__host__ __device__ __forceinline__ //\n__half Min::operator()(__half& a, __half& b) const \n{\n    NV_IF_TARGET(NV_PROVIDES_SM_53, \n                    (return CUB_MIN(a, b);),\n                    (return CUB_MIN(__half2float(a), __half2float(b));));\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\nKeyValuePair<int, __half> \nArgMin::operator()(const KeyValuePair<int, __half> &a, \n                   const KeyValuePair<int, __half> &b) const \n{\n    const float av = __half2float(a.value);\n    const float bv = __half2float(b.value);\n\n    if ((bv < av) || ((av == bv) && (b.key < a.key)))\n    {\n      return b;\n    }\n\n    return a;\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\n__half Max::operator()(__half& a, __half& b) const \n{\n    NV_IF_TARGET(NV_PROVIDES_SM_53, \n                    (return CUB_MAX(a, b);),\n                    (return CUB_MAX(__half2float(a), __half2float(b));));\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\nKeyValuePair<int, __half> \nArgMax::operator()(const KeyValuePair<int, __half> &a, \n                   const KeyValuePair<int, __half> &b) const \n{\n    const float av = __half2float(a.value);\n    const float bv = __half2float(b.value);\n\n    if ((bv > av) || ((av == bv) && (b.key < a.key)))\n    {\n      return b;\n    }\n\n    return a;\n}\n\n} // namespace cub\n#endif\n\n#if TEST_BF_T\n#include <cuda_bf16.h>\n#endif\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nint                     g_ptx_version;\nint                     g_sm_count;\ndouble                  g_device_giga_bandwidth;\nbool                    g_verbose           = false;\nbool                    g_verbose_input     = false;\nint                     g_timing_iterations = 0;\nCachingDeviceAllocator  g_allocator(true);\n\n\n// Dispatch types\nenum Backend\n{\n    CUB,            // CUB method\n    CUB_SEGMENTED,  // CUB segmented method\n    CDP,            // GPU-based (dynamic parallelism) dispatch to CUB method\n    CDP_SEGMENTED,  // GPU-based segmented method\n};\n\ninline const char* BackendToString(Backend b)\n{\n  switch (b)\n  {\n    case CUB:\n      return \"CUB\";\n    case CUB_SEGMENTED:\n      return \"CUB_SEGMENTED\";\n    case CDP:\n      return \"CDP\";\n    case CDP_SEGMENTED:\n      return \"CDP_SEGMENTED\";\n    default:\n      break;\n  }\n\n  return \"\";\n}\n\n// Custom max functor\nstruct CustomMax\n{\n    /// Boolean max operator, returns <tt>(a > b) ? a : b</tt>\n    template <typename T, typename C>\n    __host__ __device__ auto operator()(T&& a, C&& b) \n      -> cub::detail::accumulator_t<cub::Max, T, C>\n    {\n        return CUB_MAX(a, b);\n    }\n\n#if TEST_HALF_T\n    __host__ __device__ __half operator()(__half& a, __half& b) \n    {\n        return cub::Max{}(a, b);\n    }\n#endif\n};\n\n// Comparing results computed on CPU and GPU for extended floating point types is impossible. \n// For instance, when used with a constant iterator of two, the accumulator in sequential reference \n// computation (CPU) bumps into the 4096 limits, which will never change (`4096 + 2 = 4096`). \n// Meanwhile, per-thread aggregates (`2 * 16 = 32`) are accumulated within and among thread blocks, \n// yielding `inf` as a result. No reasonable epsilon can be selected to compare `inf` with `4096`. \n// To make `__half` and `__nv_bfloat16` arithmetic associative, the function object below raises \n// extended floating points to the area of unsigned short integers. This allows us to test large \n// inputs with few code-path differences in device algorithms. \nstruct ExtendedFloatSum\n{\n    template <class T>\n    __host__ __device__ T operator()(T a, T b) const\n    {\n        T result{};\n        result.__x = a.raw() + b.raw();\n        return result;\n    }\n\n#if TEST_HALF_T\n    __host__ __device__ __half operator()(__half a, __half b) const\n    {\n        uint16_t result = this->operator()(half_t{a}, half_t(b)).raw();\n        return reinterpret_cast<__half &>(result);\n    }\n#endif\n\n#if TEST_BF_T\n    __device__ __nv_bfloat16 operator()(__nv_bfloat16 a, __nv_bfloat16 b) const\n    {\n        uint16_t result = this->operator()(bfloat16_t{a}, bfloat16_t(b)).raw();\n        return reinterpret_cast<__nv_bfloat16 &>(result);\n    }\n#endif\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceReduce entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce entrypoint (custom-max)\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    ReductionOpT        reduction_op)\n{\n    using InputT = cub::detail::value_t<InputIteratorT>;\n\n    // The output value type\n    using OutputT = cub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    // Max-identity\n    OutputT identity = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, num_items, reduction_op, identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    int                     timing_iterations,\n    size_t *              /*d_temp_storage_bytes*/,\n    cudaError_t *         /*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    InputIteratorT          d_in,\n    OutputIteratorT         d_out,\n    int                     num_items,\n    int                   /*max_segments*/,\n    BeginOffsetIteratorT  /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT    /*d_segment_end_offsets*/,\n    cub::Sum              /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to extended fp sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION cudaError_t Dispatch(Int2Type<CUB> /*dispatch_to*/,\n                                          int timing_iterations,\n                                          size_t * /*d_temp_storage_bytes*/,\n                                          cudaError_t * /*d_cdp_error*/,\n\n                                          void *d_temp_storage,\n                                          size_t &temp_storage_bytes,\n                                          InputIteratorT d_in,\n                                          OutputIteratorT d_out,\n                                          int num_items,\n                                          int /*max_segments*/,\n                                          BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n                                          EndOffsetIteratorT /*d_segment_end_offsets*/,\n                                          ExtendedFloatSum reduction_op)\n{\n    using InputT  = cub::detail::value_t<InputIteratorT>;\n    using OutputT = cub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    OutputT identity{};\n\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Reduce(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items,\n                                     reduction_op,\n                                     identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to min entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    cub::Min            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to max entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    cub::Max            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to argmin entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    cub::ArgMin         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to argmax entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    cub::ArgMax         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSegmentedReduce entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce entrypoint (custom-max)\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    ReductionOpT        reduction_op)\n{\n    // The input value type\n    using InputT = cub::detail::value_t<InputIteratorT>;\n\n    // The output value type\n    using OutputT = cub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    // Max-identity\n    OutputT identity = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Reduce(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, identity);\n    }\n    return error;\n}\n\n/**\n * Dispatch to sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    cub::Sum            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to extended fp sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION cudaError_t Dispatch(Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n                                          int timing_iterations,\n                                          size_t * /*d_temp_storage_bytes*/,\n                                          cudaError_t * /*d_cdp_error*/,\n\n                                          void *d_temp_storage,\n                                          size_t &temp_storage_bytes,\n                                          InputIteratorT d_in,\n                                          OutputIteratorT d_out,\n                                          int /*num_items*/,\n                                          int max_segments,\n                                          BeginOffsetIteratorT d_segment_begin_offsets,\n                                          EndOffsetIteratorT d_segment_end_offsets,\n                                          ExtendedFloatSum reduction_op)\n{\n    using InputT  = cub::detail::value_t<InputIteratorT>;\n    using OutputT = cub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    OutputT identity{};\n\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Reduce(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              max_segments,\n                                              d_segment_begin_offsets,\n                                              d_segment_end_offsets,\n                                              reduction_op,\n                                              identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to min entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    cub::Min            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Min(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to max entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    cub::Max            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Max(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to argmin entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    cub::ArgMin         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::ArgMin(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to argmax entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    cudaError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    cub::ArgMax         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::ArgMax(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n\n//---------------------------------------------------------------------\n// CUDA nested-parallelism test kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceReduce\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  OutputIteratorT      d_out,\n                                  int                  num_items,\n                                  int                  max_segments,\n                                  BeginOffsetIteratorT d_segment_begin_offsets,\n                                  EndOffsetIteratorT   d_segment_end_offsets,\n                                  ReductionOpT         reduction_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          num_items,\n                          max_segments,\n                          d_segment_begin_offsets,\n                          d_segment_end_offsets,\n                          reduction_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Launch kernel and dispatch on device. Should only be called from host code.\n * The CubBackend should be one of the non-CDP CUB backends to invoke from the\n * device.\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\ncudaError_t LaunchCDPKernel(Int2Type<CubBackend> cub_backend,\n                            int                  timing_iterations,\n                            size_t              *d_temp_storage_bytes,\n                            cudaError_t         *d_cdp_error,\n\n                            void                *d_temp_storage,\n                            size_t              &temp_storage_bytes,\n                            InputIteratorT       d_in,\n                            OutputIteratorT      d_out,\n                            int                  num_items,\n                            int                  max_segments,\n                            BeginOffsetIteratorT d_segment_begin_offsets,\n                            EndOffsetIteratorT   d_segment_end_offsets,\n                            ReductionOpT         reduction_op)\n{\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CubBackend,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              BeginOffsetIteratorT,\n                              EndOffsetIteratorT,\n                              ReductionOpT>,\n            cub_backend,\n            timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            num_items,\n            max_segments,\n            d_segment_begin_offsets,\n            d_segment_end_offsets,\n            reduction_op);\n  CubDebugExit(retval);\n  CubDebugExit(cub::detail::device_synchronize());\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  return retval;\n}\n\n// Specializations of Dispatch that translate the CDP backend to the appropriate\n// CUB backend, and uses the CUB backend to launch the CDP kernel.\n#define DEFINE_CDP_DISPATCHER(CdpBackend, CubBackend)                          \\\n  template <typename InputIteratorT,                                           \\\n            typename OutputIteratorT,                                          \\\n            typename BeginOffsetIteratorT,                                     \\\n            typename EndOffsetIteratorT,                                       \\\n            typename ReductionOpT>                                             \\\n  cudaError_t Dispatch(Int2Type<CdpBackend>,                                   \\\n                       int          timing_iterations,                         \\\n                       size_t      *d_temp_storage_bytes,                      \\\n                       cudaError_t *d_cdp_error,                               \\\n                                                                               \\\n                       void                *d_temp_storage,                    \\\n                       size_t              &temp_storage_bytes,                \\\n                       InputIteratorT       d_in,                              \\\n                       OutputIteratorT      d_out,                             \\\n                       int                  num_items,                         \\\n                       int                  max_segments,                      \\\n                       BeginOffsetIteratorT d_segment_begin_offsets,           \\\n                       EndOffsetIteratorT   d_segment_end_offsets,             \\\n                       ReductionOpT         reduction_op)                      \\\n  {                                                                            \\\n    Int2Type<CubBackend> cub_backend{};                                        \\\n    return LaunchCDPKernel(cub_backend,                                        \\\n                           timing_iterations,                                  \\\n                           d_temp_storage_bytes,                               \\\n                           d_cdp_error,                                        \\\n                           d_temp_storage,                                     \\\n                           temp_storage_bytes,                                 \\\n                           d_in,                                               \\\n                           d_out,                                              \\\n                           num_items,                                          \\\n                           max_segments,                                       \\\n                           d_segment_begin_offsets,                            \\\n                           d_segment_end_offsets,                              \\\n                           reduction_op);                                      \\\n  }\n\nDEFINE_CDP_DISPATCHER(CDP, CUB)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED, CUB_SEGMENTED)\n\n#undef DEFINE_CDP_DISPATCHER\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n/// Initialize problem\ntemplate <typename InputT>\nvoid Initialize(\n    GenMode         gen_mode,\n    InputT          *h_in,\n    int             num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose_input)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/// Solve problem (max/custom-max functor)\ntemplate <typename ReductionOpT, typename InputT, typename _OutputT>\nstruct Solution\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = cub::detail::accumulator_t<ReductionOpT, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, OutputT(h_in[j]));\n            h_reference[i] = aggregate;\n        }\n    }\n};\n\n/// Solve problem (min functor)\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<cub::Min, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = cub::detail::accumulator_t<cub::Min, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, cub::Min reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate = Traits<InputT>::Max();    // replace with std::numeric_limits<OutputT>::max() when C++ support is more prevalent\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, OutputT(h_in[j]));\n            h_reference[i] = aggregate;\n        }\n    }\n};\n\n\n/// Solve problem (sum functor)\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<cub::Sum, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = cub::detail::accumulator_t<cub::Sum, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate{};\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, h_in[j]);\n            h_reference[i] = static_cast<OutputT>(aggregate);\n        }\n    }\n};\n\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<ExtendedFloatSum, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT   = OutputT;\n    using AccumT  = cub::detail::accumulator_t<cub::Sum, InitT, InputT>;\n\n    template <typename HostInputIteratorT,\n              typename OffsetT,\n              typename BeginOffsetIteratorT,\n              typename EndOffsetIteratorT,\n              typename ReductionOpT>\n    static void Solve(HostInputIteratorT h_in,\n                      OutputT *h_reference,\n                      OffsetT num_segments,\n                      BeginOffsetIteratorT h_segment_begin_offsets,\n                      EndOffsetIteratorT h_segment_end_offsets,\n                      ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate{};\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, h_in[j]);\n            h_reference[i] = static_cast<OutputT>(aggregate);\n        }\n    }\n};\n\n/// Solve problem (argmin functor)\ntemplate <typename InputValueT, typename OutputValueT>\nstruct Solution<cub::ArgMin, InputValueT, OutputValueT>\n{\n    typedef KeyValuePair<int, OutputValueT> OutputT;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, cub::ArgMin reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            const auto segment_begin = h_segment_begin_offsets[i];\n            const auto segment_end = h_segment_end_offsets[i];\n\n            if (segment_begin < segment_end) \n            {\n                OutputT aggregate(0, OutputValueT(h_in[segment_begin]));\n                for (int j = segment_begin + 1; j < segment_end; ++j)\n                {\n                    OutputT item(j - segment_begin, OutputValueT(h_in[j]));\n                    aggregate = reduction_op(aggregate, item);\n                }\n                h_reference[i] = aggregate;\n            }\n            else \n            {\n                // Guaranteed output for empty segments\n                OutputT aggregate(1, Traits<InputValueT>::Max()); // replace with std::numeric_limits<OutputT>::max() when C++ support is more prevalent\n                h_reference[i] = aggregate;\n            }\n        }\n    }\n};\n\n\n/// Solve problem (argmax functor)\ntemplate <typename InputValueT, typename OutputValueT>\nstruct Solution<cub::ArgMax, InputValueT, OutputValueT>\n{\n    typedef KeyValuePair<int, OutputValueT> OutputT;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, cub::ArgMax reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            const auto segment_begin = h_segment_begin_offsets[i];\n            const auto segment_end = h_segment_end_offsets[i];\n\n            if (segment_begin < segment_end) \n            {\n                OutputT aggregate(0, OutputValueT(h_in[segment_begin])); \n                for (int j = segment_begin + 1; j < segment_end; ++j)\n                {\n                    OutputT item(j - segment_begin, OutputValueT(h_in[j]));\n                    aggregate = reduction_op(aggregate, item);\n                }\n                h_reference[i] = aggregate;\n            }\n            else \n            {\n                OutputT aggregate(1, Traits<InputValueT>::Lowest());\n                h_reference[i] = aggregate;\n            }\n        }\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n\ntemplate <class It>\nIt unwrap_it(It it) {\n    return it;\n}\n\n#if TEST_HALF_T\n__half* unwrap_it(half_t *it) {\n    return reinterpret_cast<__half*>(it);\n}\n\ntemplate <class OffsetT>\nConstantInputIterator<__half, OffsetT> unwrap_it(ConstantInputIterator<half_t, OffsetT> it) {\n    half_t wrapped_val = *it;\n    __half val = wrapped_val.operator __half();\n    return ConstantInputIterator<__half, OffsetT>(val);\n}\n#endif\n\n#if TEST_BF_T\n__nv_bfloat16* unwrap_it(bfloat16_t* it) {\n    return reinterpret_cast<__nv_bfloat16*>(it);\n}\n\ntemplate <class OffsetT>\nConstantInputIterator<__nv_bfloat16, OffsetT> unwrap_it(ConstantInputIterator<bfloat16_t, OffsetT> it) {\n    bfloat16_t wrapped_val = *it;\n    __nv_bfloat16 val = wrapped_val.operator __nv_bfloat16();\n    return ConstantInputIterator<__nv_bfloat16, OffsetT>(val);\n}\n#endif\n\ntemplate <class WrappedItT, //\n          class ItT = decltype(unwrap_it(std::declval<WrappedItT>()))>\nstd::integral_constant<bool, !std::is_same<WrappedItT, ItT>::value> //\nreference_extended_fp(WrappedItT)\n{\n    return {};\n}\n\nExtendedFloatSum unwrap_op(std::true_type /* extended float */, cub::Sum) //\n{\n    return {};\n}\n\ntemplate <bool V, class OpT>\nOpT unwrap_op(std::integral_constant<bool, V> /* base case */, OpT op)\n{\n    return op;\n}\n\n/// Test DeviceReduce for a given problem input\ntemplate <\n    typename                BackendT,\n    typename                DeviceWrappedInputIteratorT,\n    typename                DeviceWrappedOutputIteratorT,\n    typename                HostReferenceIteratorT,\n    typename                OffsetT,\n    typename                BeginOffsetIteratorT,\n    typename                EndOffsetIteratorT,\n    typename                ReductionOpT>\nvoid Test(\n    BackendT                        backend,\n    DeviceWrappedInputIteratorT     d_wrapped_in,\n    DeviceWrappedOutputIteratorT    d_wrapped_out,\n    OffsetT                         num_items,\n    OffsetT                         num_segments,\n    BeginOffsetIteratorT            d_segment_begin_offsets,\n    EndOffsetIteratorT              d_segment_end_offsets,\n    ReductionOpT                    reduction_op,\n    HostReferenceIteratorT          h_reference)\n{\n    // Input data types\n    auto d_in = unwrap_it(d_wrapped_in);\n    auto d_out = unwrap_it(d_wrapped_out);\n\n    // Allocate CDP device arrays for temp storage size and error\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(cudaError_t) * 1));\n\n    // Inquire temp device storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(backend, 1,\n        d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n        d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        reduction_op));\n\n    // Allocate temp device storage\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(backend, 1,\n        d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n        d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        reduction_op));\n\n    // Check for correctness (and display results, if specified)\n    int compare = CompareDeviceResults(h_reference, d_wrapped_out, num_segments, g_verbose, g_verbose);\n\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    if (g_timing_iterations > 0)\n    {\n        GpuTimer gpu_timer;\n        gpu_timer.Start();\n\n        CubDebugExit(Dispatch(backend, g_timing_iterations,\n            d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n            d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n            reduction_op));\n\n        gpu_timer.Stop();\n        float elapsed_millis = gpu_timer.ElapsedMillis();\n\n        // Display performance\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = giga_rate * sizeof(h_reference[0]);\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\",\n            avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <Backend BACKEND,\n          typename OutputValueT,\n          typename HostInputIteratorT,\n          typename DeviceInputIteratorT,\n          typename OffsetT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\nvoid SolveAndTest(HostInputIteratorT h_in,\n                  DeviceInputIteratorT d_in,\n                  OffsetT num_items,\n                  OffsetT num_segments,\n                  BeginOffsetIteratorT h_segment_begin_offsets,\n                  EndOffsetIteratorT h_segment_end_offsets,\n                  BeginOffsetIteratorT d_segment_begin_offsets,\n                  EndOffsetIteratorT d_segment_end_offsets,\n                  ReductionOpT wrapped_reduction_op)\n{\n    auto reduction_op = unwrap_op(reference_extended_fp(d_in), wrapped_reduction_op);\n\n    using InputValueT = cub::detail::value_t<DeviceInputIteratorT>;\n    using SolutionT = Solution<decltype(reduction_op), InputValueT, OutputValueT>;\n    using OutputT = typename SolutionT::OutputT;\n\n    printf(\"\\n\\n%s cub::DeviceReduce<%s> %d items (%s), %d segments\\n\",\n           BackendToString(BACKEND),\n           typeid(ReductionOpT).name(),\n           num_items,\n           typeid(HostInputIteratorT).name(),\n           num_segments);\n    fflush(stdout);\n\n    // Allocate and solve solution\n    OutputT *h_reference = new OutputT[num_segments];\n    SolutionT::Solve(h_in, h_reference, num_segments, h_segment_begin_offsets, h_segment_end_offsets, reduction_op);\n\n    // Run with discard iterator\n    DiscardOutputIterator<OffsetT> discard_itr;\n    Test(Int2Type<BACKEND>(), d_in, discard_itr, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, h_reference);\n\n    // Run with output data\n    OutputT *d_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_segments));\n    CubDebugExit(cudaMemset(d_out, 0, sizeof(OutputT) * num_segments));\n    Test(Int2Type<BACKEND>(), d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, h_reference);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (h_reference) delete[] h_reference;\n}\n\n/// Test specific problem type\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        OffsetT,\n    typename        ReductionOpT>\nvoid TestProblem(\n    OffsetT         num_items,\n    OffsetT         num_segments,\n    GenMode         gen_mode,\n    ReductionOpT    reduction_op)\n{\n    printf(\"\\n\\nInitializing %d %s->%s (gen mode %d)... \", num_items, typeid(InputT).name(), typeid(OutputT).name(), gen_mode); fflush(stdout);\n    fflush(stdout);\n\n    // Initialize value data\n    InputT* h_in = new InputT[num_items];\n    Initialize(gen_mode, h_in, num_items);\n\n    // Initialize segment data\n    OffsetT *h_segment_offsets = new OffsetT[num_segments + 1];\n    InitializeSegments(num_items, num_segments, h_segment_offsets, g_verbose_input);\n\n    // Initialize device data\n    OffsetT *d_segment_offsets      = NULL;\n    InputT  *d_in                   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in,              sizeof(InputT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(OffsetT) * (num_segments + 1)));\n    CubDebugExit(cudaMemcpy(d_in,               h_in,                   sizeof(InputT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemcpy(d_segment_offsets,  h_segment_offsets,      sizeof(OffsetT) * (num_segments + 1), cudaMemcpyHostToDevice));\n\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_offsets, h_segment_offsets + 1, d_segment_offsets, d_segment_offsets + 1, reduction_op);\n\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_segment_offsets)  CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n    if (h_in)               delete[] h_in;\n    if (d_in)               CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/// Test different operators\ntemplate <\n    Backend             BACKEND,\n    typename            OutputT,\n    typename            HostInputIteratorT,\n    typename            DeviceInputIteratorT,\n    typename            OffsetT,\n    typename            BeginOffsetIteratorT,\n    typename            EndOffsetIteratorT>\nvoid TestByOp(\n    HostInputIteratorT      h_in,\n    DeviceInputIteratorT    d_in,\n    OffsetT                 num_items,\n    OffsetT                 num_segments,\n    BeginOffsetIteratorT    h_segment_begin_offsets,\n    EndOffsetIteratorT      h_segment_end_offsets,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets)\n{\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, CustomMax());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Sum());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Min());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, ArgMin());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Max());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, ArgMax());\n}\n\ntemplate<typename OffsetT>\nstruct TransformFunctor1\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\ntemplate<typename OffsetT>\nstruct TransformFunctor2\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\n/// Test different backends\ntemplate <\n    typename    InputT,\n    typename    OutputT,\n    typename    OffsetT>\nvoid TestByBackend(\n    OffsetT     num_items,\n    OffsetT     max_segments,\n    GenMode     gen_mode)\n{\n#if TEST_CDP == 0\n  constexpr auto NonSegmentedBackend   = CUB;\n  constexpr auto SegmentedBackend      = CUB_SEGMENTED;\n#else  // TEST_CDP\n  constexpr auto NonSegmentedBackend   = CDP;\n  constexpr auto SegmentedBackend      = CDP_SEGMENTED;\n#endif // TEST_CDP\n\n    // Initialize host data\n    printf(\"\\n\\nInitializing %d %s -> %s (gen mode %d)... \",\n        num_items, typeid(InputT).name(), typeid(OutputT).name(), gen_mode); fflush(stdout);\n\n    InputT  *h_in               = new InputT[num_items];\n    OffsetT *h_segment_offsets  = new OffsetT[max_segments + 1];\n    Initialize(gen_mode, h_in, num_items);\n\n    // Initialize device data\n    InputT  *d_in               = NULL;\n    OffsetT *d_segment_offsets  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(InputT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(OffsetT) * (max_segments + 1)));\n    CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(InputT) * num_items, cudaMemcpyHostToDevice));\n\n    //\n    // Test single-segment implementations\n    //\n\n    InitializeSegments(num_items, 1, h_segment_offsets, g_verbose_input);\n\n    // Page-aligned-input tests\n    TestByOp<NonSegmentedBackend, OutputT>(h_in, d_in, num_items, 1,\n        h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL);\n\n    // Non-page-aligned-input tests\n    if (num_items > 1)\n    {\n        InitializeSegments(num_items - 1, 1, h_segment_offsets, g_verbose_input);\n        TestByOp<NonSegmentedBackend, OutputT>(h_in + 1, d_in + 1, num_items - 1, 1,\n            h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL);\n    }\n\n    //\n    // Test segmented implementation\n    //\n\n    // Right now we assign a single thread block to each segment, so lets keep it to under 128K items per segment\n    int max_items_per_segment = 128000;\n\n    for (int num_segments = cub::DivideAndRoundUp(num_items, max_items_per_segment);\n        num_segments < max_segments;\n        num_segments = (num_segments * 32) + 1)\n    {\n        // Test with segment pointer\n        InitializeSegments(num_items, num_segments, h_segment_offsets, g_verbose_input);\n        CubDebugExit(cudaMemcpy(d_segment_offsets, h_segment_offsets, sizeof(OffsetT) * (num_segments + 1), cudaMemcpyHostToDevice));\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_offsets, h_segment_offsets + 1, d_segment_offsets, d_segment_offsets + 1);\n\n        // Test with segment iterator\n        typedef CastOp<OffsetT> IdentityOpT;\n        IdentityOpT identity_op;\n        TransformInputIterator<OffsetT, IdentityOpT, OffsetT*, OffsetT> h_segment_offsets_itr(\n            h_segment_offsets,\n            identity_op);\n        TransformInputIterator<OffsetT, IdentityOpT, OffsetT*, OffsetT> d_segment_offsets_itr(\n            d_segment_offsets,\n            identity_op);\n\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_offsets_itr, h_segment_offsets_itr + 1, d_segment_offsets_itr, d_segment_offsets_itr + 1);\n\n        // Test with transform iterators of different types\n\n        typedef TransformFunctor1<OffsetT> TransformFunctor1T;\n        typedef TransformFunctor2<OffsetT> TransformFunctor2T;\n\n        TransformInputIterator<OffsetT, TransformFunctor1T, OffsetT*, OffsetT> h_segment_begin_offsets_itr(h_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<OffsetT, TransformFunctor2T, OffsetT*, OffsetT> h_segment_end_offsets_itr(h_segment_offsets + 1, TransformFunctor2T());\n\n        TransformInputIterator<OffsetT, TransformFunctor1T, OffsetT*, OffsetT> d_segment_begin_offsets_itr(d_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<OffsetT, TransformFunctor2T, OffsetT*, OffsetT> d_segment_end_offsets_itr(d_segment_offsets + 1, TransformFunctor2T());\n\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_begin_offsets_itr, h_segment_end_offsets_itr,\n            d_segment_begin_offsets_itr, d_segment_end_offsets_itr);\n    }\n\n    if (h_in)               delete[] h_in;\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_in)               CubDebugExit(g_allocator.DeviceFree(d_in));\n    if (d_segment_offsets)  CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n\n/// Test different input-generation modes\ntemplate <\n    typename InputT,\n    typename OutputT,\n    typename OffsetT>\nvoid TestByGenMode(\n    OffsetT num_items,\n    OffsetT max_segments)\n{\n    //\n    // Test pointer support using different input-generation modes\n    //\n\n    TestByBackend<InputT, OutputT>(num_items, max_segments, UNIFORM);\n    TestByBackend<InputT, OutputT>(num_items, max_segments, INTEGER_SEED);\n    TestByBackend<InputT, OutputT>(num_items, max_segments, RANDOM);\n\n    //\n    // Test iterator support using a constant-iterator and SUM\n    //\n\n    InputT val;\n    InitValue(UNIFORM, val, 0);\n    ConstantInputIterator<InputT, OffsetT> in(val);\n\n    OffsetT *h_segment_offsets = new OffsetT[1 + 1];\n    InitializeSegments(num_items, 1, h_segment_offsets, g_verbose_input);\n\n#if TEST_CDP == 0\n    constexpr auto Backend   = CUB;\n#else  // TEST_CDP\n    constexpr auto Backend   = CDP;\n#endif // TEST_CDP\n\n    SolveAndTest<Backend, OutputT>(in, in, num_items, 1,\n        h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL, Sum());\n\n    if (h_segment_offsets) delete[] h_segment_offsets;\n}\n\n/// Test different problem sizes\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nvoid TestBySize(OffsetT max_items, OffsetT max_segments, OffsetT tile_size)\n{\n  // Test 0, 1, many\n  TestByGenMode<InputT, OutputT>(0, max_segments);\n  TestByGenMode<InputT, OutputT>(1, max_segments);\n  TestByGenMode<InputT, OutputT>(max_items, max_segments);\n\n  // Test random problem sizes from a log-distribution [8, max_items-ish)\n  int    num_iterations = 8;\n  double max_exp        = log(double(max_items)) / log(double(2.0));\n  for (int i = 0; i < num_iterations; ++i)\n  {\n    OffsetT num_items = (OffsetT)pow(2.0, RandomValue(max_exp - 3.0) + 3.0);\n    TestByGenMode<InputT, OutputT>(num_items, max_segments);\n  }\n\n  //\n  // White-box testing of single-segment problems around specific sizes\n  //\n\n#if TEST_CDP == 0\n  constexpr auto Backend   = CUB;\n#else  // TEST_CDP\n  constexpr auto Backend   = CDP;\n#endif // TEST_CDP\n\n  // Tile-boundaries: multiple blocks, one tile per block\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4 + 1, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4 - 1, 1, RANDOM, Sum());\n\n  // Tile-boundaries: multiple blocks, multiple tiles per block\n  OffsetT sm_occupancy = 32;\n  OffsetT occupancy    = tile_size * sm_occupancy * g_sm_count;\n  TestProblem<Backend, InputT, OutputT>(occupancy, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(occupancy + 1, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(occupancy - 1, 1, RANDOM, Sum());\n};\n\nclass CustomInputT\n{\n  char m_val{};\n\npublic:\n  __host__ __device__ explicit CustomInputT(char val)\n      : m_val(val)\n  {}\n\n  __host__ __device__ int get() const { return static_cast<int>(m_val); }\n};\n\nclass CustomAccumulatorT\n{\n  int m_val{0};\n  int m_magic_value{42};\n\n  __host__ __device__ CustomAccumulatorT(int val)\n      : m_val(val)\n  {}\n\npublic:\n  __host__ __device__ CustomAccumulatorT()\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomAccumulatorT &in)\n    : m_val(in.is_valid() * in.get())\n    , m_magic_value(in.is_valid() * 42)\n  {}\n\n  __host__ __device__ void operator=(const CustomInputT &in)\n  {\n    if (this->is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &in)\n  {\n    if (this->is_valid() && in.is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ CustomAccumulatorT \n  operator+(const CustomInputT &in) const\n  {\n    const int multiplier = this->is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ CustomAccumulatorT\n  operator+(const CustomAccumulatorT &in) const\n  {\n    const int multiplier = this->is_valid() && in.is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ int get() const { return m_val; }\n\n  __host__ __device__ bool is_valid() const { return m_magic_value == 42; }\n};\n\nclass CustomOutputT\n{\n  bool *m_d_flag{};\n  int m_expected{};\n\npublic:\n  __host__ __device__ CustomOutputT(bool *d_flag, int expected)\n      : m_d_flag(d_flag)\n      , m_expected(expected)\n  {}\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &accum) const\n  {\n    *m_d_flag = accum.is_valid() && (accum.get() == m_expected);\n  }\n};\n\n__global__ void InitializeTestAccumulatorTypes(int num_items,\n                                               int expected,\n                                               bool *d_flag,\n                                               CustomInputT *d_in,\n                                               CustomOutputT *d_out)\n{\n  const int idx = static_cast<int>(threadIdx.x + blockIdx.x * blockDim.x);\n\n  if (idx < num_items)\n  {\n    d_in[idx] = CustomInputT(1);\n  }\n\n  if (idx == 0)\n  {\n    *d_out = CustomOutputT{d_flag, expected};\n  }\n}\n\ntemplate <typename T, \n          typename OffsetT>\nvoid TestBigIndicesHelper(OffsetT num_items)\n{\n  thrust::constant_iterator<T> const_iter(T{1});\n  thrust::device_vector<std::size_t> out(1);\n  std::size_t* d_out = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(\n    cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, const_iter, d_out, num_items));\n\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  CubDebugExit(\n    cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, const_iter, d_out, num_items));\n  std::size_t result = out[0];\n\n  AssertEquals(result, num_items);\n}\n\ntemplate <typename T>\nvoid TestBigIndices()\n{\n  TestBigIndicesHelper<T, std::uint32_t>(1ull << 30);\n  TestBigIndicesHelper<T, std::uint32_t>(1ull << 31);\n  TestBigIndicesHelper<T, std::uint32_t>((1ull << 32) - 1);\n  TestBigIndicesHelper<T, std::uint64_t>(1ull << 33);\n}\n\n#if TEST_TYPES == 3\nvoid TestAccumulatorTypes()\n{\n  const int num_items  = 2 * 1024 * 1024;\n  const int expected   = num_items;\n  const int block_size = 256;\n  const int grid_size  = (num_items + block_size - 1) / block_size;\n\n  CustomInputT *d_in{};\n  CustomOutputT *d_out{};\n  CustomAccumulatorT init{};\n  bool *d_flag{};\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_out, sizeof(CustomOutputT)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_flag, sizeof(bool)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in,\n                                          sizeof(CustomInputT) * num_items));\n\n  InitializeTestAccumulatorTypes<<<grid_size, block_size>>>(num_items,\n                                                            expected,\n                                                            d_flag,\n                                                            d_in,\n                                                            d_out);\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(cub::DeviceReduce::Reduce(d_temp_storage,\n                                         temp_storage_bytes,\n                                         d_in,\n                                         d_out,\n                                         num_items,\n                                         cub::Sum{},\n                                         init));\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_temp_storage, temp_storage_bytes));\n  CubDebugExit(cudaMemset(d_temp_storage, 1,  temp_storage_bytes));\n\n  CubDebugExit(cub::DeviceReduce::Reduce(d_temp_storage,\n                                         temp_storage_bytes,\n                                         d_in,\n                                         d_out,\n                                         num_items,\n                                         cub::Sum{},\n                                         init));\n\n  bool ok{};\n  CubDebugExit(cudaMemcpy(&ok, d_flag, sizeof(bool), cudaMemcpyDeviceToHost));\n\n  AssertTrue(ok);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n/**\n * ArgMin should return max value for empty input. This interferes with\n * input data containing infinity values. This test checks that ArgMin\n * works correctly with infinity values.\n */\nvoid TestFloatInfInArgMin()\n{\n  using in_t     = float;\n  using offset_t = int;\n  using out_t    = cub::KeyValuePair<offset_t, in_t>;\n\n  const int n     = 10;\n  const float inf = ::cuda::std::numeric_limits<float>::infinity();\n\n  thrust::device_vector<in_t> in(n, inf);\n  thrust::device_vector<out_t> out(1);\n\n  const in_t *d_in = thrust::raw_pointer_cast(in.data());\n  out_t *d_out     = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(cub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n  CubDebugExit(cub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n\n  const out_t result = out[0];\n  AssertEquals(result.key, 0);\n  AssertEquals(result.value, inf);\n}\n\n/**\n * ArgMax should return lowest value for empty input. This interferes with\n * input data containing infinity values. This test checks that ArgMax\n * works correctly with infinity values.\n */\nvoid TestFloatInfInArgMax()\n{\n  using in_t = float;\n  using offset_t = int;\n  using out_t = cub::KeyValuePair<offset_t, in_t>;\n\n  const int n = 10;\n  const float inf = ::cuda::std::numeric_limits<float>::infinity();\n  \n  thrust::device_vector<in_t> in(n, -inf);\n  thrust::device_vector<out_t> out(1);\n\n  const in_t *d_in = thrust::raw_pointer_cast(in.data());\n  out_t *d_out = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};  \n\n  CubDebugExit(cub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n  CubDebugExit(cub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n\n  const out_t result = out[0];\n  AssertEquals(result.key, 0);\n  AssertEquals(result.value, -inf);\n}\n\nvoid TestFloatInfInArg()\n{\n  TestFloatInfInArgMin();\n  TestFloatInfInArgMax();\n}\n#endif\n\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nstruct GetTileSize\n{\n  OffsetT max_items{};\n  OffsetT max_segments{};\n  OffsetT tile_size{};\n\n  GetTileSize(OffsetT max_items, OffsetT max_segments)\n      : max_items(max_items)\n      , max_segments(max_segments)\n  {}\n\n  template <typename ActivePolicyT>\n  CUB_RUNTIME_FUNCTION cudaError_t Invoke()\n  {\n    this->tile_size = ActivePolicyT::ReducePolicy::BLOCK_THREADS *\n                      ActivePolicyT::ReducePolicy::ITEMS_PER_THREAD;\n    return cudaSuccess;\n  }\n};\n\n/// Test problem type\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nvoid TestType(OffsetT max_items, OffsetT max_segments)\n{\n  // Inspect the tuning policies to determine this arch's tile size:\n  using MaxPolicyT =\n    typename DeviceReducePolicy<InputT, OffsetT, cub::Sum>::MaxPolicy;\n  GetTileSize<InputT, OutputT, OffsetT> dispatch(max_items, max_segments);\n  CubDebugExit(MaxPolicyT::Invoke(g_ptx_version, dispatch));\n\n  TestBySize<InputT, OutputT>(max_items, max_segments, dispatch.tile_size);\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    typedef int OffsetT;\n\n    OffsetT max_items       = 27000000;\n    OffsetT max_segments    = 34000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    g_verbose_input = args.CheckCmdLineFlag(\"v2\");\n    args.GetCmdLineArgument(\"n\", max_items);\n    args.GetCmdLineArgument(\"s\", max_segments);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--s=<num segments> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n\n    // Get ptx version\n    CubDebugExit(PtxVersion(g_ptx_version));\n\n    // Get SM count\n    g_sm_count = args.deviceProp.multiProcessorCount;\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_TYPES types 0:1:2:3:4\n\n#if TEST_TYPES == 0\n    TestType<signed char, signed char>(max_items, max_segments);\n    TestType<unsigned char, unsigned char>(max_items, max_segments);\n    TestType<signed char, int>(max_items, max_segments);\n#elif TEST_TYPES == 1\n    TestType<short, short>(max_items, max_segments);\n    TestType<int, int>(max_items, max_segments);\n    TestType<long, long>(max_items, max_segments);\n    TestType<long long, long long>(max_items, max_segments);\n#elif TEST_TYPES == 2\n    TestType<uchar2, uchar2>(max_items, max_segments);\n    TestType<uint2, uint2>(max_items, max_segments);\n    TestType<ulonglong2, ulonglong2>(max_items, max_segments);\n    TestType<ulonglong4, ulonglong4>(max_items, max_segments);\n#elif TEST_TYPES == 3\n    TestType<TestFoo, TestFoo>(max_items, max_segments);\n    TestAccumulatorTypes();\n    TestFloatInfInArg();\n\n#if TEST_HALF_T\n    TestType<half_t, half_t>(max_items, max_segments);\n#endif\n#else // TEST_TYPES == 4\n    TestType<TestBar, TestBar>(max_items, max_segments);\n    TestBigIndices<std::size_t>();\n\n#if TEST_BF_T\n    TestType<bfloat16_t, bfloat16_t>(max_items, max_segments);\n#endif\n#endif\n    printf(\"\\n\");\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceReduce utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_reduce.cuh>\n#include <cub/device/device_segmented_reduce.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_math.cuh>\n#include <cub/util_type.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include <cstdio>\n#include <limits>\n#include <type_traits>\n#include <typeinfo>\n\n#include \"test_util.h\"\n#include <nv/target>\n\n#define TEST_HALF_T !_NVHPC_CUDA\n\n#define TEST_BF_T !_NVHPC_CUDA\n\n#if TEST_HALF_T\n#include <hip/hip_fp16.h>\n\n// Half support is provided by SM53+. We currently test against a few older architectures. \n// The specializations below can be removed once we drop these architectures.  \nnamespace cub {\n\ntemplate <>\n__host__ __device__ __forceinline__ //\n__half Min::operator()(__half& a, __half& b) const \n{\n    NV_IF_TARGET(NV_PROVIDES_SM_53, \n                    (return CUB_MIN(a, b);),\n                    (return CUB_MIN(__half2float(a), __half2float(b));));\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\nKeyValuePair<int, __half> \nArgMin::operator()(const KeyValuePair<int, __half> &a, \n                   const KeyValuePair<int, __half> &b) const \n{\n    const float av = __half2float(a.value);\n    const float bv = __half2float(b.value);\n\n    if ((bv < av) || ((av == bv) && (b.key < a.key)))\n    {\n      return b;\n    }\n\n    return a;\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\n__half Max::operator()(__half& a, __half& b) const \n{\n    NV_IF_TARGET(NV_PROVIDES_SM_53, \n                    (return CUB_MAX(a, b);),\n                    (return CUB_MAX(__half2float(a), __half2float(b));));\n}\n\ntemplate <>\n__host__ __device__ __forceinline__ //\nKeyValuePair<int, __half> \nArgMax::operator()(const KeyValuePair<int, __half> &a, \n                   const KeyValuePair<int, __half> &b) const \n{\n    const float av = __half2float(a.value);\n    const float bv = __half2float(b.value);\n\n    if ((bv > av) || ((av == bv) && (b.key < a.key)))\n    {\n      return b;\n    }\n\n    return a;\n}\n\n} // namespace cub\n#endif\n\n#if TEST_BF_T\n#include <hip/hip_bf16.h>\n#endif\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nint                     g_ptx_version;\nint                     g_sm_count;\ndouble                  g_device_giga_bandwidth;\nbool                    g_verbose           = false;\nbool                    g_verbose_input     = false;\nint                     g_timing_iterations = 0;\nCachingDeviceAllocator  g_allocator(true);\n\n\n// Dispatch types\nenum Backend\n{\n    CUB,            // CUB method\n    CUB_SEGMENTED,  // CUB segmented method\n    CDP,            // GPU-based (dynamic parallelism) dispatch to CUB method\n    CDP_SEGMENTED,  // GPU-based segmented method\n};\n\ninline const char* BackendToString(Backend b)\n{\n  switch (b)\n  {\n    case CUB:\n      return \"CUB\";\n    case CUB_SEGMENTED:\n      return \"CUB_SEGMENTED\";\n    case CDP:\n      return \"CDP\";\n    case CDP_SEGMENTED:\n      return \"CDP_SEGMENTED\";\n    default:\n      break;\n  }\n\n  return \"\";\n}\n\n// Custom max functor\nstruct CustomMax\n{\n    /// Boolean max operator, returns <tt>(a > b) ? a : b</tt>\n    template <typename T, typename C>\n    __host__ __device__ auto operator()(T&& a, C&& b) \n      -> hipcub::detail::accumulator_t<hipcub::Max, T, C>\n    {\n        return CUB_MAX(a, b);\n    }\n\n#if TEST_HALF_T\n    __host__ __device__ __half operator()(__half& a, __half& b) \n    {\n        return hipcub::Max{}(a, b);\n    }\n#endif\n};\n\n// Comparing results computed on CPU and GPU for extended floating point types is impossible. \n// For instance, when used with a constant iterator of two, the accumulator in sequential reference \n// computation (CPU) bumps into the 4096 limits, which will never change (`4096 + 2 = 4096`). \n// Meanwhile, per-thread aggregates (`2 * 16 = 32`) are accumulated within and among thread blocks, \n// yielding `inf` as a result. No reasonable epsilon can be selected to compare `inf` with `4096`. \n// To make `__half` and `__nv_bfloat16` arithmetic associative, the function object below raises \n// extended floating points to the area of unsigned short integers. This allows us to test large \n// inputs with few code-path differences in device algorithms. \nstruct ExtendedFloatSum\n{\n    template <class T>\n    __host__ __device__ T operator()(T a, T b) const\n    {\n        T result{};\n        result.__x = a.raw() + b.raw();\n        return result;\n    }\n\n#if TEST_HALF_T\n    __host__ __device__ __half operator()(__half a, __half b) const\n    {\n        uint16_t result = this->operator()(half_t{a}, half_t(b)).raw();\n        return reinterpret_cast<__half &>(result);\n    }\n#endif\n\n#if TEST_BF_T\n    __device__ __hip_bfloat16 operator()(__hip_bfloat16 a, __hip_bfloat16 b) const\n    {\n        uint16_t result = this->operator()(bfloat16_t{a}, bfloat16_t(b)).raw();\n        return reinterpret_cast<__hip_bfloat16 &>(result);\n    }\n#endif\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceReduce entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce entrypoint (custom-max)\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    ReductionOpT        reduction_op)\n{\n    using InputT = hipcub::detail::value_t<InputIteratorT>;\n\n    // The output value type\n    using OutputT = hipcub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    // Max-identity\n    OutputT identity = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, num_items, reduction_op, identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    int                     timing_iterations,\n    size_t *              /*d_temp_storage_bytes*/,\n    hipError_t *         /*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    InputIteratorT          d_in,\n    OutputIteratorT         d_out,\n    int                     num_items,\n    int                   /*max_segments*/,\n    BeginOffsetIteratorT  /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT    /*d_segment_end_offsets*/,\n    hipcub::Sum              /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to extended fp sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION hipError_t Dispatch(Int2Type<CUB> /*dispatch_to*/,\n                                          int timing_iterations,\n                                          size_t * /*d_temp_storage_bytes*/,\n                                          hipError_t * /*d_cdp_error*/,\n\n                                          void *d_temp_storage,\n                                          size_t &temp_storage_bytes,\n                                          InputIteratorT d_in,\n                                          OutputIteratorT d_out,\n                                          int num_items,\n                                          int /*max_segments*/,\n                                          BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n                                          EndOffsetIteratorT /*d_segment_end_offsets*/,\n                                          ExtendedFloatSum reduction_op)\n{\n    using InputT  = hipcub::detail::value_t<InputIteratorT>;\n    using OutputT = hipcub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    OutputT identity{};\n\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Reduce(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items,\n                                     reduction_op,\n                                     identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to min entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    hipcub::Min            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to max entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    hipcub::Max            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to argmin entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    hipcub::ArgMin         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to argmax entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 num_items,\n    int                 /*max_segments*/,\n    BeginOffsetIteratorT /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT  /*d_segment_end_offsets*/,\n    hipcub::ArgMax         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);\n    }\n\n    return error;\n}\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSegmentedReduce entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to reduce entrypoint (custom-max)\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    ReductionOpT        reduction_op)\n{\n    // The input value type\n    using InputT = hipcub::detail::value_t<InputIteratorT>;\n\n    // The output value type\n    using OutputT = hipcub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    // Max-identity\n    OutputT identity = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Reduce(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, identity);\n    }\n    return error;\n}\n\n/**\n * Dispatch to sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    hipcub::Sum            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to extended fp sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION hipError_t Dispatch(Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n                                          int timing_iterations,\n                                          size_t * /*d_temp_storage_bytes*/,\n                                          hipError_t * /*d_cdp_error*/,\n\n                                          void *d_temp_storage,\n                                          size_t &temp_storage_bytes,\n                                          InputIteratorT d_in,\n                                          OutputIteratorT d_out,\n                                          int /*num_items*/,\n                                          int max_segments,\n                                          BeginOffsetIteratorT d_segment_begin_offsets,\n                                          EndOffsetIteratorT d_segment_end_offsets,\n                                          ExtendedFloatSum reduction_op)\n{\n    using InputT  = hipcub::detail::value_t<InputIteratorT>;\n    using OutputT = hipcub::detail::non_void_value_t<OutputIteratorT, InputT>;\n\n    OutputT identity{};\n\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Reduce(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              max_segments,\n                                              d_segment_begin_offsets,\n                                              d_segment_end_offsets,\n                                              reduction_op,\n                                              identity);\n    }\n\n    return error;\n}\n\n/**\n * Dispatch to min entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    hipcub::Min            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Min(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to max entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    hipcub::Max            /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::Max(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to argmin entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    hipcub::ArgMin         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::ArgMin(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n/**\n * Dispatch to argmax entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<CUB_SEGMENTED>       /*dispatch_to*/,\n    int                 timing_iterations,\n    size_t              */*d_temp_storage_bytes*/,\n    hipError_t         */*d_cdp_error*/,\n\n    void*               d_temp_storage,\n    size_t&             temp_storage_bytes,\n    InputIteratorT      d_in,\n    OutputIteratorT     d_out,\n    int                 /*num_items*/,\n    int                 max_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT  d_segment_end_offsets,\n    hipcub::ArgMax         /*reduction_op*/)\n{\n    // Invoke kernel to device reduction directly\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_iterations; ++i)\n    {\n        error = DeviceSegmentedReduce::ArgMax(d_temp_storage, temp_storage_bytes,\n            d_in, d_out, max_segments, d_segment_begin_offsets, d_segment_end_offsets);\n    }\n    return error;\n}\n\n\n//---------------------------------------------------------------------\n// CUDA nested-parallelism test kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceReduce\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  OutputIteratorT      d_out,\n                                  int                  num_items,\n                                  int                  max_segments,\n                                  BeginOffsetIteratorT d_segment_begin_offsets,\n                                  EndOffsetIteratorT   d_segment_end_offsets,\n                                  ReductionOpT         reduction_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          num_items,\n                          max_segments,\n                          d_segment_begin_offsets,\n                          d_segment_end_offsets,\n                          reduction_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Launch kernel and dispatch on device. Should only be called from host code.\n * The CubBackend should be one of the non-CDP CUB backends to invoke from the\n * device.\n */\ntemplate <int CubBackend,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\nhipError_t LaunchCDPKernel(Int2Type<CubBackend> cub_backend,\n                            int                  timing_iterations,\n                            size_t              *d_temp_storage_bytes,\n                            hipError_t         *d_cdp_error,\n\n                            void                *d_temp_storage,\n                            size_t              &temp_storage_bytes,\n                            InputIteratorT       d_in,\n                            OutputIteratorT      d_out,\n                            int                  num_items,\n                            int                  max_segments,\n                            BeginOffsetIteratorT d_segment_begin_offsets,\n                            EndOffsetIteratorT   d_segment_end_offsets,\n                            ReductionOpT         reduction_op)\n{\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CubBackend,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              BeginOffsetIteratorT,\n                              EndOffsetIteratorT,\n                              ReductionOpT>,\n            cub_backend,\n            timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            num_items,\n            max_segments,\n            d_segment_begin_offsets,\n            d_segment_end_offsets,\n            reduction_op);\n  CubDebugExit(retval);\n  CubDebugExit(hipcub::detail::device_synchronize());\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  return retval;\n}\n\n// Specializations of Dispatch that translate the CDP backend to the appropriate\n// CUB backend, and uses the CUB backend to launch the CDP kernel.\n#define DEFINE_CDP_DISPATCHER(CdpBackend, CubBackend)                          \\\n  template <typename InputIteratorT,                                           \\\n            typename OutputIteratorT,                                          \\\n            typename BeginOffsetIteratorT,                                     \\\n            typename EndOffsetIteratorT,                                       \\\n            typename ReductionOpT>                                             \\\n  hipError_t Dispatch(Int2Type<CdpBackend>,                                   \\\n                       int          timing_iterations,                         \\\n                       size_t      *d_temp_storage_bytes,                      \\\n                       hipError_t *d_cdp_error,                               \\\n                                                                               \\\n                       void                *d_temp_storage,                    \\\n                       size_t              &temp_storage_bytes,                \\\n                       InputIteratorT       d_in,                              \\\n                       OutputIteratorT      d_out,                             \\\n                       int                  num_items,                         \\\n                       int                  max_segments,                      \\\n                       BeginOffsetIteratorT d_segment_begin_offsets,           \\\n                       EndOffsetIteratorT   d_segment_end_offsets,             \\\n                       ReductionOpT         reduction_op)                      \\\n  {                                                                            \\\n    Int2Type<CubBackend> cub_backend{};                                        \\\n    return LaunchCDPKernel(cub_backend,                                        \\\n                           timing_iterations,                                  \\\n                           d_temp_storage_bytes,                               \\\n                           d_cdp_error,                                        \\\n                           d_temp_storage,                                     \\\n                           temp_storage_bytes,                                 \\\n                           d_in,                                               \\\n                           d_out,                                              \\\n                           num_items,                                          \\\n                           max_segments,                                       \\\n                           d_segment_begin_offsets,                            \\\n                           d_segment_end_offsets,                              \\\n                           reduction_op);                                      \\\n  }\n\nDEFINE_CDP_DISPATCHER(CDP, CUB)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED, CUB_SEGMENTED)\n\n#undef DEFINE_CDP_DISPATCHER\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n/// Initialize problem\ntemplate <typename InputT>\nvoid Initialize(\n    GenMode         gen_mode,\n    InputT          *h_in,\n    int             num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose_input)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/// Solve problem (max/custom-max functor)\ntemplate <typename ReductionOpT, typename InputT, typename _OutputT>\nstruct Solution\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = hipcub::detail::accumulator_t<ReductionOpT, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate = Traits<InputT>::Lowest(); // replace with std::numeric_limits<OutputT>::lowest() when C++ support is more prevalent\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, OutputT(h_in[j]));\n            h_reference[i] = aggregate;\n        }\n    }\n};\n\n/// Solve problem (min functor)\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<hipcub::Min, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = hipcub::detail::accumulator_t<hipcub::Min, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, hipcub::Min reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate = Traits<InputT>::Max();    // replace with std::numeric_limits<OutputT>::max() when C++ support is more prevalent\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, OutputT(h_in[j]));\n            h_reference[i] = aggregate;\n        }\n    }\n};\n\n\n/// Solve problem (sum functor)\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<hipcub::Sum, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT = OutputT;\n    using AccumT = hipcub::detail::accumulator_t<hipcub::Sum, InitT, InputT>;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename ReductionOpT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate{};\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, h_in[j]);\n            h_reference[i] = static_cast<OutputT>(aggregate);\n        }\n    }\n};\n\ntemplate <typename InputT, typename _OutputT>\nstruct Solution<ExtendedFloatSum, InputT, _OutputT>\n{\n    using OutputT = _OutputT;\n    using InitT   = OutputT;\n    using AccumT  = hipcub::detail::accumulator_t<hipcub::Sum, InitT, InputT>;\n\n    template <typename HostInputIteratorT,\n              typename OffsetT,\n              typename BeginOffsetIteratorT,\n              typename EndOffsetIteratorT,\n              typename ReductionOpT>\n    static void Solve(HostInputIteratorT h_in,\n                      OutputT *h_reference,\n                      OffsetT num_segments,\n                      BeginOffsetIteratorT h_segment_begin_offsets,\n                      EndOffsetIteratorT h_segment_end_offsets,\n                      ReductionOpT reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            AccumT aggregate{};\n            for (int j = h_segment_begin_offsets[i]; j < h_segment_end_offsets[i]; ++j)\n                aggregate = reduction_op(aggregate, h_in[j]);\n            h_reference[i] = static_cast<OutputT>(aggregate);\n        }\n    }\n};\n\n/// Solve problem (argmin functor)\ntemplate <typename InputValueT, typename OutputValueT>\nstruct Solution<hipcub::ArgMin, InputValueT, OutputValueT>\n{\n    typedef KeyValuePair<int, OutputValueT> OutputT;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, hipcub::ArgMin reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            const auto segment_begin = h_segment_begin_offsets[i];\n            const auto segment_end = h_segment_end_offsets[i];\n\n            if (segment_begin < segment_end) \n            {\n                OutputT aggregate(0, OutputValueT(h_in[segment_begin]));\n                for (int j = segment_begin + 1; j < segment_end; ++j)\n                {\n                    OutputT item(j - segment_begin, OutputValueT(h_in[j]));\n                    aggregate = reduction_op(aggregate, item);\n                }\n                h_reference[i] = aggregate;\n            }\n            else \n            {\n                // Guaranteed output for empty segments\n                OutputT aggregate(1, Traits<InputValueT>::Max()); // replace with std::numeric_limits<OutputT>::max() when C++ support is more prevalent\n                h_reference[i] = aggregate;\n            }\n        }\n    }\n};\n\n\n/// Solve problem (argmax functor)\ntemplate <typename InputValueT, typename OutputValueT>\nstruct Solution<hipcub::ArgMax, InputValueT, OutputValueT>\n{\n    typedef KeyValuePair<int, OutputValueT> OutputT;\n\n    template <typename HostInputIteratorT, typename OffsetT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT>\n    static void Solve(HostInputIteratorT h_in, OutputT *h_reference, OffsetT num_segments,\n        BeginOffsetIteratorT h_segment_begin_offsets, EndOffsetIteratorT h_segment_end_offsets, hipcub::ArgMax reduction_op)\n    {\n        for (int i = 0; i < num_segments; ++i)\n        {\n            const auto segment_begin = h_segment_begin_offsets[i];\n            const auto segment_end = h_segment_end_offsets[i];\n\n            if (segment_begin < segment_end) \n            {\n                OutputT aggregate(0, OutputValueT(h_in[segment_begin])); \n                for (int j = segment_begin + 1; j < segment_end; ++j)\n                {\n                    OutputT item(j - segment_begin, OutputValueT(h_in[j]));\n                    aggregate = reduction_op(aggregate, item);\n                }\n                h_reference[i] = aggregate;\n            }\n            else \n            {\n                OutputT aggregate(1, Traits<InputValueT>::Lowest());\n                h_reference[i] = aggregate;\n            }\n        }\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n\ntemplate <class It>\nIt unwrap_it(It it) {\n    return it;\n}\n\n#if TEST_HALF_T\n__half* unwrap_it(half_t *it) {\n    return reinterpret_cast<__half*>(it);\n}\n\ntemplate <class OffsetT>\nConstantInputIterator<__half, OffsetT> unwrap_it(ConstantInputIterator<half_t, OffsetT> it) {\n    half_t wrapped_val = *it;\n    __half val = wrapped_val.operator __half();\n    return ConstantInputIterator<__half, OffsetT>(val);\n}\n#endif\n\n#if TEST_BF_T\n__hip_bfloat16* unwrap_it(bfloat16_t* it) {\n    return reinterpret_cast<__hip_bfloat16*>(it);\n}\n\ntemplate <class OffsetT>\nConstantInputIterator<__hip_bfloat16, OffsetT> unwrap_it(ConstantInputIterator<bfloat16_t, OffsetT> it) {\n    bfloat16_t wrapped_val = *it;\n    __hip_bfloat16 val = wrapped_val.operator __hip_bfloat16();\n    return ConstantInputIterator<__hip_bfloat16, OffsetT>(val);\n}\n#endif\n\ntemplate <class WrappedItT, //\n          class ItT = decltype(unwrap_it(std::declval<WrappedItT>()))>\nstd::integral_constant<bool, !std::is_same<WrappedItT, ItT>::value> //\nreference_extended_fp(WrappedItT)\n{\n    return {};\n}\n\nExtendedFloatSum unwrap_op(std::true_type /* extended float */, hipcub::Sum) //\n{\n    return {};\n}\n\ntemplate <bool V, class OpT>\nOpT unwrap_op(std::integral_constant<bool, V> /* base case */, OpT op)\n{\n    return op;\n}\n\n/// Test DeviceReduce for a given problem input\ntemplate <\n    typename                BackendT,\n    typename                DeviceWrappedInputIteratorT,\n    typename                DeviceWrappedOutputIteratorT,\n    typename                HostReferenceIteratorT,\n    typename                OffsetT,\n    typename                BeginOffsetIteratorT,\n    typename                EndOffsetIteratorT,\n    typename                ReductionOpT>\nvoid Test(\n    BackendT                        backend,\n    DeviceWrappedInputIteratorT     d_wrapped_in,\n    DeviceWrappedOutputIteratorT    d_wrapped_out,\n    OffsetT                         num_items,\n    OffsetT                         num_segments,\n    BeginOffsetIteratorT            d_segment_begin_offsets,\n    EndOffsetIteratorT              d_segment_end_offsets,\n    ReductionOpT                    reduction_op,\n    HostReferenceIteratorT          h_reference)\n{\n    // Input data types\n    auto d_in = unwrap_it(d_wrapped_in);\n    auto d_out = unwrap_it(d_wrapped_out);\n\n    // Allocate CDP device arrays for temp storage size and error\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(hipError_t) * 1));\n\n    // Inquire temp device storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(backend, 1,\n        d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n        d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        reduction_op));\n\n    // Allocate temp device storage\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(backend, 1,\n        d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n        d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        reduction_op));\n\n    // Check for correctness (and display results, if specified)\n    int compare = CompareDeviceResults(h_reference, d_wrapped_out, num_segments, g_verbose, g_verbose);\n\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    if (g_timing_iterations > 0)\n    {\n        GpuTimer gpu_timer;\n        gpu_timer.Start();\n\n        CubDebugExit(Dispatch(backend, g_timing_iterations,\n            d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes,\n            d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n            reduction_op));\n\n        gpu_timer.Stop();\n        float elapsed_millis = gpu_timer.ElapsedMillis();\n\n        // Display performance\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = giga_rate * sizeof(h_reference[0]);\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\",\n            avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <Backend BACKEND,\n          typename OutputValueT,\n          typename HostInputIteratorT,\n          typename DeviceInputIteratorT,\n          typename OffsetT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename ReductionOpT>\nvoid SolveAndTest(HostInputIteratorT h_in,\n                  DeviceInputIteratorT d_in,\n                  OffsetT num_items,\n                  OffsetT num_segments,\n                  BeginOffsetIteratorT h_segment_begin_offsets,\n                  EndOffsetIteratorT h_segment_end_offsets,\n                  BeginOffsetIteratorT d_segment_begin_offsets,\n                  EndOffsetIteratorT d_segment_end_offsets,\n                  ReductionOpT wrapped_reduction_op)\n{\n    auto reduction_op = unwrap_op(reference_extended_fp(d_in), wrapped_reduction_op);\n\n    using InputValueT = hipcub::detail::value_t<DeviceInputIteratorT>;\n    using SolutionT = Solution<decltype(reduction_op), InputValueT, OutputValueT>;\n    using OutputT = typename SolutionT::OutputT;\n\n    printf(\"\\n\\n%s hipcub::DeviceReduce<%s> %d items (%s), %d segments\\n\",\n           BackendToString(BACKEND),\n           typeid(ReductionOpT).name(),\n           num_items,\n           typeid(HostInputIteratorT).name(),\n           num_segments);\n    fflush(stdout);\n\n    // Allocate and solve solution\n    OutputT *h_reference = new OutputT[num_segments];\n    SolutionT::Solve(h_in, h_reference, num_segments, h_segment_begin_offsets, h_segment_end_offsets, reduction_op);\n\n    // Run with discard iterator\n    DiscardOutputIterator<OffsetT> discard_itr;\n    Test(Int2Type<BACKEND>(), d_in, discard_itr, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, h_reference);\n\n    // Run with output data\n    OutputT *d_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_segments));\n    CubDebugExit(hipMemset(d_out, 0, sizeof(OutputT) * num_segments));\n    Test(Int2Type<BACKEND>(), d_in, d_out, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, reduction_op, h_reference);\n\n    // Cleanup\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (h_reference) delete[] h_reference;\n}\n\n/// Test specific problem type\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        OffsetT,\n    typename        ReductionOpT>\nvoid TestProblem(\n    OffsetT         num_items,\n    OffsetT         num_segments,\n    GenMode         gen_mode,\n    ReductionOpT    reduction_op)\n{\n    printf(\"\\n\\nInitializing %d %s->%s (gen mode %d)... \", num_items, typeid(InputT).name(), typeid(OutputT).name(), gen_mode); fflush(stdout);\n    fflush(stdout);\n\n    // Initialize value data\n    InputT* h_in = new InputT[num_items];\n    Initialize(gen_mode, h_in, num_items);\n\n    // Initialize segment data\n    OffsetT *h_segment_offsets = new OffsetT[num_segments + 1];\n    InitializeSegments(num_items, num_segments, h_segment_offsets, g_verbose_input);\n\n    // Initialize device data\n    OffsetT *d_segment_offsets      = NULL;\n    InputT  *d_in                   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in,              sizeof(InputT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(OffsetT) * (num_segments + 1)));\n    CubDebugExit(hipMemcpy(d_in,               h_in,                   sizeof(InputT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemcpy(d_segment_offsets,  h_segment_offsets,      sizeof(OffsetT) * (num_segments + 1), hipMemcpyHostToDevice));\n\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_offsets, h_segment_offsets + 1, d_segment_offsets, d_segment_offsets + 1, reduction_op);\n\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_segment_offsets)  CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n    if (h_in)               delete[] h_in;\n    if (d_in)               CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/// Test different operators\ntemplate <\n    Backend             BACKEND,\n    typename            OutputT,\n    typename            HostInputIteratorT,\n    typename            DeviceInputIteratorT,\n    typename            OffsetT,\n    typename            BeginOffsetIteratorT,\n    typename            EndOffsetIteratorT>\nvoid TestByOp(\n    HostInputIteratorT      h_in,\n    DeviceInputIteratorT    d_in,\n    OffsetT                 num_items,\n    OffsetT                 num_segments,\n    BeginOffsetIteratorT    h_segment_begin_offsets,\n    EndOffsetIteratorT      h_segment_end_offsets,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets)\n{\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, CustomMax());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Sum());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Min());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, ArgMin());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, Max());\n    SolveAndTest<BACKEND, OutputT>(h_in, d_in, num_items, num_segments,\n        h_segment_begin_offsets, h_segment_end_offsets, d_segment_begin_offsets, d_segment_end_offsets, ArgMax());\n}\n\ntemplate<typename OffsetT>\nstruct TransformFunctor1\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\ntemplate<typename OffsetT>\nstruct TransformFunctor2\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\n/// Test different backends\ntemplate <\n    typename    InputT,\n    typename    OutputT,\n    typename    OffsetT>\nvoid TestByBackend(\n    OffsetT     num_items,\n    OffsetT     max_segments,\n    GenMode     gen_mode)\n{\n#if TEST_CDP == 0\n  constexpr auto NonSegmentedBackend   = CUB;\n  constexpr auto SegmentedBackend      = CUB_SEGMENTED;\n#else  // TEST_CDP\n  constexpr auto NonSegmentedBackend   = CDP;\n  constexpr auto SegmentedBackend      = CDP_SEGMENTED;\n#endif // TEST_CDP\n\n    // Initialize host data\n    printf(\"\\n\\nInitializing %d %s -> %s (gen mode %d)... \",\n        num_items, typeid(InputT).name(), typeid(OutputT).name(), gen_mode); fflush(stdout);\n\n    InputT  *h_in               = new InputT[num_items];\n    OffsetT *h_segment_offsets  = new OffsetT[max_segments + 1];\n    Initialize(gen_mode, h_in, num_items);\n\n    // Initialize device data\n    InputT  *d_in               = NULL;\n    OffsetT *d_segment_offsets  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(InputT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(OffsetT) * (max_segments + 1)));\n    CubDebugExit(hipMemcpy(d_in, h_in, sizeof(InputT) * num_items, hipMemcpyHostToDevice));\n\n    //\n    // Test single-segment implementations\n    //\n\n    InitializeSegments(num_items, 1, h_segment_offsets, g_verbose_input);\n\n    // Page-aligned-input tests\n    TestByOp<NonSegmentedBackend, OutputT>(h_in, d_in, num_items, 1,\n        h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL);\n\n    // Non-page-aligned-input tests\n    if (num_items > 1)\n    {\n        InitializeSegments(num_items - 1, 1, h_segment_offsets, g_verbose_input);\n        TestByOp<NonSegmentedBackend, OutputT>(h_in + 1, d_in + 1, num_items - 1, 1,\n            h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL);\n    }\n\n    //\n    // Test segmented implementation\n    //\n\n    // Right now we assign a single thread block to each segment, so lets keep it to under 128K items per segment\n    int max_items_per_segment = 128000;\n\n    for (int num_segments = hipcub::DivideAndRoundUp(num_items, max_items_per_segment);\n        num_segments < max_segments;\n        num_segments = (num_segments * 32) + 1)\n    {\n        // Test with segment pointer\n        InitializeSegments(num_items, num_segments, h_segment_offsets, g_verbose_input);\n        CubDebugExit(hipMemcpy(d_segment_offsets, h_segment_offsets, sizeof(OffsetT) * (num_segments + 1), hipMemcpyHostToDevice));\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_offsets, h_segment_offsets + 1, d_segment_offsets, d_segment_offsets + 1);\n\n        // Test with segment iterator\n        typedef CastOp<OffsetT> IdentityOpT;\n        IdentityOpT identity_op;\n        TransformInputIterator<OffsetT, IdentityOpT, OffsetT*, OffsetT> h_segment_offsets_itr(\n            h_segment_offsets,\n            identity_op);\n        TransformInputIterator<OffsetT, IdentityOpT, OffsetT*, OffsetT> d_segment_offsets_itr(\n            d_segment_offsets,\n            identity_op);\n\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_offsets_itr, h_segment_offsets_itr + 1, d_segment_offsets_itr, d_segment_offsets_itr + 1);\n\n        // Test with transform iterators of different types\n\n        typedef TransformFunctor1<OffsetT> TransformFunctor1T;\n        typedef TransformFunctor2<OffsetT> TransformFunctor2T;\n\n        TransformInputIterator<OffsetT, TransformFunctor1T, OffsetT*, OffsetT> h_segment_begin_offsets_itr(h_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<OffsetT, TransformFunctor2T, OffsetT*, OffsetT> h_segment_end_offsets_itr(h_segment_offsets + 1, TransformFunctor2T());\n\n        TransformInputIterator<OffsetT, TransformFunctor1T, OffsetT*, OffsetT> d_segment_begin_offsets_itr(d_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<OffsetT, TransformFunctor2T, OffsetT*, OffsetT> d_segment_end_offsets_itr(d_segment_offsets + 1, TransformFunctor2T());\n\n        TestByOp<SegmentedBackend, OutputT>(h_in, d_in, num_items, num_segments,\n            h_segment_begin_offsets_itr, h_segment_end_offsets_itr,\n            d_segment_begin_offsets_itr, d_segment_end_offsets_itr);\n    }\n\n    if (h_in)               delete[] h_in;\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_in)               CubDebugExit(g_allocator.DeviceFree(d_in));\n    if (d_segment_offsets)  CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n\n/// Test different input-generation modes\ntemplate <\n    typename InputT,\n    typename OutputT,\n    typename OffsetT>\nvoid TestByGenMode(\n    OffsetT num_items,\n    OffsetT max_segments)\n{\n    //\n    // Test pointer support using different input-generation modes\n    //\n\n    TestByBackend<InputT, OutputT>(num_items, max_segments, UNIFORM);\n    TestByBackend<InputT, OutputT>(num_items, max_segments, INTEGER_SEED);\n    TestByBackend<InputT, OutputT>(num_items, max_segments, RANDOM);\n\n    //\n    // Test iterator support using a constant-iterator and SUM\n    //\n\n    InputT val;\n    InitValue(UNIFORM, val, 0);\n    ConstantInputIterator<InputT, OffsetT> in(val);\n\n    OffsetT *h_segment_offsets = new OffsetT[1 + 1];\n    InitializeSegments(num_items, 1, h_segment_offsets, g_verbose_input);\n\n#if TEST_CDP == 0\n    constexpr auto Backend   = CUB;\n#else  // TEST_CDP\n    constexpr auto Backend   = CDP;\n#endif // TEST_CDP\n\n    SolveAndTest<Backend, OutputT>(in, in, num_items, 1,\n        h_segment_offsets, h_segment_offsets + 1, (OffsetT*) NULL, (OffsetT*)NULL, Sum());\n\n    if (h_segment_offsets) delete[] h_segment_offsets;\n}\n\n/// Test different problem sizes\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nvoid TestBySize(OffsetT max_items, OffsetT max_segments, OffsetT tile_size)\n{\n  // Test 0, 1, many\n  TestByGenMode<InputT, OutputT>(0, max_segments);\n  TestByGenMode<InputT, OutputT>(1, max_segments);\n  TestByGenMode<InputT, OutputT>(max_items, max_segments);\n\n  // Test random problem sizes from a log-distribution [8, max_items-ish)\n  int    num_iterations = 8;\n  double max_exp        = log(double(max_items)) / log(double(2.0));\n  for (int i = 0; i < num_iterations; ++i)\n  {\n    OffsetT num_items = (OffsetT)pow(2.0, RandomValue(max_exp - 3.0) + 3.0);\n    TestByGenMode<InputT, OutputT>(num_items, max_segments);\n  }\n\n  //\n  // White-box testing of single-segment problems around specific sizes\n  //\n\n#if TEST_CDP == 0\n  constexpr auto Backend   = CUB;\n#else  // TEST_CDP\n  constexpr auto Backend   = CDP;\n#endif // TEST_CDP\n\n  // Tile-boundaries: multiple blocks, one tile per block\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4 + 1, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(tile_size * 4 - 1, 1, RANDOM, Sum());\n\n  // Tile-boundaries: multiple blocks, multiple tiles per block\n  OffsetT sm_occupancy = 32;\n  OffsetT occupancy    = tile_size * sm_occupancy * g_sm_count;\n  TestProblem<Backend, InputT, OutputT>(occupancy, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(occupancy + 1, 1, RANDOM, Sum());\n  TestProblem<Backend, InputT, OutputT>(occupancy - 1, 1, RANDOM, Sum());\n};\n\nclass CustomInputT\n{\n  char m_val{};\n\npublic:\n  __host__ __device__ explicit CustomInputT(char val)\n      : m_val(val)\n  {}\n\n  __host__ __device__ int get() const { return static_cast<int>(m_val); }\n};\n\nclass CustomAccumulatorT\n{\n  int m_val{0};\n  int m_magic_value{42};\n\n  __host__ __device__ CustomAccumulatorT(int val)\n      : m_val(val)\n  {}\n\npublic:\n  __host__ __device__ CustomAccumulatorT()\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomAccumulatorT &in)\n    : m_val(in.is_valid() * in.get())\n    , m_magic_value(in.is_valid() * 42)\n  {}\n\n  __host__ __device__ void operator=(const CustomInputT &in)\n  {\n    if (this->is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &in)\n  {\n    if (this->is_valid() && in.is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ CustomAccumulatorT \n  operator+(const CustomInputT &in) const\n  {\n    const int multiplier = this->is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ CustomAccumulatorT\n  operator+(const CustomAccumulatorT &in) const\n  {\n    const int multiplier = this->is_valid() && in.is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ int get() const { return m_val; }\n\n  __host__ __device__ bool is_valid() const { return m_magic_value == 42; }\n};\n\nclass CustomOutputT\n{\n  bool *m_d_flag{};\n  int m_expected{};\n\npublic:\n  __host__ __device__ CustomOutputT(bool *d_flag, int expected)\n      : m_d_flag(d_flag)\n      , m_expected(expected)\n  {}\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &accum) const\n  {\n    *m_d_flag = accum.is_valid() && (accum.get() == m_expected);\n  }\n};\n\n__global__ void InitializeTestAccumulatorTypes(int num_items,\n                                               int expected,\n                                               bool *d_flag,\n                                               CustomInputT *d_in,\n                                               CustomOutputT *d_out)\n{\n  const int idx = static_cast<int>(threadIdx.x + blockIdx.x * blockDim.x);\n\n  if (idx < num_items)\n  {\n    d_in[idx] = CustomInputT(1);\n  }\n\n  if (idx == 0)\n  {\n    *d_out = CustomOutputT{d_flag, expected};\n  }\n}\n\ntemplate <typename T, \n          typename OffsetT>\nvoid TestBigIndicesHelper(OffsetT num_items)\n{\n  thrust::constant_iterator<T> const_iter(T{1});\n  thrust::device_vector<std::size_t> out(1);\n  std::size_t* d_out = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(\n    hipcub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, const_iter, d_out, num_items));\n\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  CubDebugExit(\n    hipcub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, const_iter, d_out, num_items));\n  std::size_t result = out[0];\n\n  AssertEquals(result, num_items);\n}\n\ntemplate <typename T>\nvoid TestBigIndices()\n{\n  TestBigIndicesHelper<T, std::uint32_t>(1ull << 30);\n  TestBigIndicesHelper<T, std::uint32_t>(1ull << 31);\n  TestBigIndicesHelper<T, std::uint32_t>((1ull << 32) - 1);\n  TestBigIndicesHelper<T, std::uint64_t>(1ull << 33);\n}\n\n#if TEST_TYPES == 3\nvoid TestAccumulatorTypes()\n{\n  const int num_items  = 2 * 1024 * 1024;\n  const int expected   = num_items;\n  const int block_size = 256;\n  const int grid_size  = (num_items + block_size - 1) / block_size;\n\n  CustomInputT *d_in{};\n  CustomOutputT *d_out{};\n  CustomAccumulatorT init{};\n  bool *d_flag{};\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_out, sizeof(CustomOutputT)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_flag, sizeof(bool)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in,\n                                          sizeof(CustomInputT) * num_items));\n\n  InitializeTestAccumulatorTypes<<<grid_size, block_size>>>(num_items,\n                                                            expected,\n                                                            d_flag,\n                                                            d_in,\n                                                            d_out);\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(hipcub::DeviceReduce::Reduce(d_temp_storage,\n                                         temp_storage_bytes,\n                                         d_in,\n                                         d_out,\n                                         num_items,\n                                         hipcub::Sum{},\n                                         init));\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_temp_storage, temp_storage_bytes));\n  CubDebugExit(hipMemset(d_temp_storage, 1,  temp_storage_bytes));\n\n  CubDebugExit(hipcub::DeviceReduce::Reduce(d_temp_storage,\n                                         temp_storage_bytes,\n                                         d_in,\n                                         d_out,\n                                         num_items,\n                                         hipcub::Sum{},\n                                         init));\n\n  bool ok{};\n  CubDebugExit(hipMemcpy(&ok, d_flag, sizeof(bool), hipMemcpyDeviceToHost));\n\n  AssertTrue(ok);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n/**\n * ArgMin should return max value for empty input. This interferes with\n * input data containing infinity values. This test checks that ArgMin\n * works correctly with infinity values.\n */\nvoid TestFloatInfInArgMin()\n{\n  using in_t     = float;\n  using offset_t = int;\n  using out_t    = hipcub::KeyValuePair<offset_t, in_t>;\n\n  const int n     = 10;\n  const float inf = ::cuda::std::numeric_limits<float>::infinity();\n\n  thrust::device_vector<in_t> in(n, inf);\n  thrust::device_vector<out_t> out(1);\n\n  const in_t *d_in = thrust::raw_pointer_cast(in.data());\n  out_t *d_out     = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(hipcub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n  CubDebugExit(hipcub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n\n  const out_t result = out[0];\n  AssertEquals(result.key, 0);\n  AssertEquals(result.value, inf);\n}\n\n/**\n * ArgMax should return lowest value for empty input. This interferes with\n * input data containing infinity values. This test checks that ArgMax\n * works correctly with infinity values.\n */\nvoid TestFloatInfInArgMax()\n{\n  using in_t = float;\n  using offset_t = int;\n  using out_t = hipcub::KeyValuePair<offset_t, in_t>;\n\n  const int n = 10;\n  const float inf = ::cuda::std::numeric_limits<float>::infinity();\n  \n  thrust::device_vector<in_t> in(n, -inf);\n  thrust::device_vector<out_t> out(1);\n\n  const in_t *d_in = thrust::raw_pointer_cast(in.data());\n  out_t *d_out = thrust::raw_pointer_cast(out.data());\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};  \n\n  CubDebugExit(hipcub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n  CubDebugExit(hipcub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out, n));\n\n  const out_t result = out[0];\n  AssertEquals(result.key, 0);\n  AssertEquals(result.value, -inf);\n}\n\nvoid TestFloatInfInArg()\n{\n  TestFloatInfInArgMin();\n  TestFloatInfInArgMax();\n}\n#endif\n\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nstruct GetTileSize\n{\n  OffsetT max_items{};\n  OffsetT max_segments{};\n  OffsetT tile_size{};\n\n  GetTileSize(OffsetT max_items, OffsetT max_segments)\n      : max_items(max_items)\n      , max_segments(max_segments)\n  {}\n\n  template <typename ActivePolicyT>\n  HIPCUB_RUNTIME_FUNCTION hipError_t Invoke()\n  {\n    this->tile_size = ActivePolicyT::ReducePolicy::BLOCK_THREADS *\n                      ActivePolicyT::ReducePolicy::ITEMS_PER_THREAD;\n    return hipSuccess;\n  }\n};\n\n/// Test problem type\ntemplate <typename InputT, typename OutputT, typename OffsetT>\nvoid TestType(OffsetT max_items, OffsetT max_segments)\n{\n  // Inspect the tuning policies to determine this arch's tile size:\n  using MaxPolicyT =\n    typename DeviceReducePolicy<InputT, OffsetT, hipcub::Sum>::MaxPolicy;\n  GetTileSize<InputT, OutputT, OffsetT> dispatch(max_items, max_segments);\n  CubDebugExit(MaxPolicyT::Invoke(g_ptx_version, dispatch));\n\n  TestBySize<InputT, OutputT>(max_items, max_segments, dispatch.tile_size);\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    typedef int OffsetT;\n\n    OffsetT max_items       = 27000000;\n    OffsetT max_segments    = 34000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    g_verbose_input = args.CheckCmdLineFlag(\"v2\");\n    args.GetCmdLineArgument(\"n\", max_items);\n    args.GetCmdLineArgument(\"s\", max_segments);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--s=<num segments> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n\n    // Get ptx version\n    CubDebugExit(PtxVersion(g_ptx_version));\n\n    // Get SM count\n    g_sm_count = args.deviceProp.multiProcessorCount;\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_TYPES types 0:1:2:3:4\n\n#if TEST_TYPES == 0\n    TestType<signed char, signed char>(max_items, max_segments);\n    TestType<unsigned char, unsigned char>(max_items, max_segments);\n    TestType<signed char, int>(max_items, max_segments);\n#elif TEST_TYPES == 1\n    TestType<short, short>(max_items, max_segments);\n    TestType<int, int>(max_items, max_segments);\n    TestType<long, long>(max_items, max_segments);\n    TestType<long long, long long>(max_items, max_segments);\n#elif TEST_TYPES == 2\n    TestType<uchar2, uchar2>(max_items, max_segments);\n    TestType<uint2, uint2>(max_items, max_segments);\n    TestType<ulonglong2, ulonglong2>(max_items, max_segments);\n    TestType<ulonglong4, ulonglong4>(max_items, max_segments);\n#elif TEST_TYPES == 3\n    TestType<TestFoo, TestFoo>(max_items, max_segments);\n    TestAccumulatorTypes();\n    TestFloatInfInArg();\n\n#if TEST_HALF_T\n    TestType<half_t, half_t>(max_items, max_segments);\n#endif\n#else // TEST_TYPES == 4\n    TestType<TestBar, TestBar>(max_items, max_segments);\n    TestBigIndices<std::size_t>();\n\n#if TEST_BF_T\n    TestType<bfloat16_t, bfloat16_t>(max_items, max_segments);\n#endif\n#endif\n    printf(\"\\n\");\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_device_reduce.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::If and DevicePartition::If utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/device/device_partition.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/count.h>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/random.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n// Selection functor type\ntemplate <typename T>\nstruct LessThan\n{\n    T compare;\n\n    __host__ __device__ __forceinline__\n    LessThan(T compare) : compare(compare) {}\n\n    __host__ __device__ __forceinline__\n    bool operator()(const T &a) const {\n        return (a < compare);\n    }\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to select if entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<false>             /*is_flagged*/,\n    Int2Type<false>             /*is_partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    cudaError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               /*d_flags*/,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   select_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to partition if entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<false>             /*is_flagged*/,\n    Int2Type<true>              /*is_partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    cudaError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               /*d_flags*/,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   select_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DevicePartition::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to select flagged entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<true>              /*is_flagged*/,\n    Int2Type<false>             /*partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    cudaError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               d_flags,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   /*select_op*/)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to partition flagged entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<true>              /*is_flagged*/,\n    Int2Type<true>              /*partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    cudaError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               d_flags,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   /*select_op*/)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DevicePartition::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename IsFlaggedTag,\n          typename IsPartitionTag,\n          typename InputIteratorT,\n          typename FlagIteratorT,\n          typename SelectOpT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  IsFlaggedTag         is_flagged,\n                                  IsPartitionTag       is_partition,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  FlagIteratorT        d_flags,\n                                  OutputIteratorT      d_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items,\n                                  SelectOpT            select_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          is_flagged,\n                          is_partition,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_flags,\n                          d_out,\n                          d_num_selected_out,\n                          num_items,\n                          select_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename IsFlaggedTag,\n          typename IsPartitionTag,\n          typename InputIteratorT,\n          typename FlagIteratorT,\n          typename SelectOpT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\ncudaError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     IsFlaggedTag   is_flagged,\n                     IsPartitionTag is_partition,\n                     int            timing_timing_iterations,\n                     size_t        *d_temp_storage_bytes,\n                     cudaError_t   *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     InputIteratorT       d_in,\n                     FlagIteratorT        d_flags,\n                     OutputIteratorT      d_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items,\n                     SelectOpT            select_op)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              IsFlaggedTag,\n                              IsPartitionTag,\n                              InputIteratorT,\n                              FlagIteratorT,\n                              SelectOpT,\n                              OutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            is_flagged,\n            is_partition,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_flags,\n            d_out,\n            d_num_selected_out,\n            num_items,\n            select_op);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    T*  h_in,\n    int num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        // Initialize each item to a randomly selected value from [0..126]\n        unsigned int value;\n        RandomBits(value, 0, 0, 7);\n        if (value == 127)\n            value = 126;\n        InitValue(INTEGER_SEED, h_in[i], value);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve selection problem (and set corresponding flags)\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        FlagIteratorT,\n    typename        SelectOpT,\n    typename        T>\nint Solve(\n    InputIteratorT  h_in,\n    SelectOpT       select_op,\n    T*              h_reference,\n    FlagIteratorT   h_flags,\n    int             num_items)\n{\n    int num_selected = 0;\n    for (int i = 0; i < num_items; ++i)\n    {\n        if ((h_flags[i] = select_op(h_in[i])))\n        {\n            h_reference[num_selected] = h_in[i];\n            num_selected++;\n        }\n        else\n        {\n            h_reference[num_items - (i - num_selected) - 1] = h_in[i];\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    bool                IS_FLAGGED,\n    bool                IS_PARTITION,\n    typename            DeviceInputIteratorT,\n    typename            FlagT,\n    typename            SelectOpT,\n    typename            T>\nvoid Test(\n    DeviceInputIteratorT    d_in,\n    FlagT*                  h_flags,\n    SelectOpT               select_op,\n    T*                      h_reference,\n    int                     num_selected,\n    int                     num_items)\n{\n    // Allocate device flags, output, and num-selected\n    FlagT*      d_flags = NULL;\n    T*          d_out = NULL;\n    int*        d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_flags, sizeof(FlagT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(T) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t*         d_temp_storage_bytes = NULL;\n    cudaError_t*    d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), 1, d_temp_storage_bytes, d_cdp_error,\n    d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Copy flags and clear device output array\n    CubDebugExit(cudaMemcpy(d_flags, h_flags, sizeof(FlagT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemset(d_out, 0, sizeof(T) * num_items));\n    CubDebugExit(cudaMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), 1, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = (IS_PARTITION) ?\n        CompareDeviceResults(h_reference, d_out, num_items, true, g_verbose) :\n        CompareDeviceResults(h_reference, d_out, num_selected, true, g_verbose);\n    printf(\"\\t Data %s\\n\", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s\\n\", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float   avg_millis          = elapsed_millis / g_timing_iterations;\n        float   giga_rate           = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        int     num_output_items    = (IS_PARTITION) ? num_items : num_selected;\n        int     num_flag_items      = (IS_FLAGGED) ? num_items : 0;\n        size_t  num_bytes           = sizeof(T) * (num_items + num_output_items) + sizeof(FlagT) * num_flag_items;\n        float   giga_bandwidth      = float(num_bytes) / avg_millis / 1000.0f / 1000.0f;\n\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_flags) CubDebugExit(g_allocator.DeviceFree(d_flags));\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid TestPointer(\n    int             num_items,\n    float           select_ratio)\n{\n    typedef char FlagT;\n\n    // Allocate host arrays\n    T*      h_in        = new T[num_items];\n    FlagT*  h_flags     = new FlagT[num_items];\n    T*      h_reference = new T[num_items];\n\n    // Initialize input\n    Initialize(h_in, num_items);\n\n    // Select a comparison value that is select_ratio through the space of [0,127]\n    T compare;\n    if (select_ratio <= 0.0)\n        InitValue(INTEGER_SEED, compare, 0);        // select none\n    else if (select_ratio >= 1.0)\n        InitValue(INTEGER_SEED, compare, 127);      // select all\n    else\n        InitValue(INTEGER_SEED, compare, int(double(double(127) * select_ratio)));\n\n    LessThan<T> select_op(compare);\n    int num_selected = Solve(h_in, select_op, h_reference, h_flags, num_items);\n\n    if (g_verbose) std::cout << \"\\nComparison item: \" << compare << \"\\n\";\n    printf(\"\\nPointer %s cub::%s::%s %d items, %d selected (select ratio %.3f), %s %d-byte elements\\n\",\n        (IS_PARTITION) ? \"DevicePartition\" : \"DeviceSelect\",\n        (IS_FLAGGED) ? \"Flagged\" : \"If\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_selected) / num_items, typeid(T).name(), (int) sizeof(T));\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    T *d_in = NULL;\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(T) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(T) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND, IS_FLAGGED, IS_PARTITION>(d_in, h_flags, select_op, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_flags) delete[] h_flags;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid TestIterator(\n    int             num_items,\n    float           select_ratio)\n{\n    typedef char FlagT;\n\n    // Allocate host arrays\n    T*      h_reference = new T[num_items];\n    FlagT*  h_flags = new FlagT[num_items];\n\n    // Use counting iterator as the input\n    CountingInputIterator<T, int> h_in(0);\n\n    // Select a comparison value that is select_ratio through the space of [0,127]\n    T compare;\n    if (select_ratio <= 0.0)\n        InitValue(INTEGER_SEED, compare, 0);        // select none\n    else if (select_ratio >= 1.0)\n        InitValue(INTEGER_SEED, compare, 127);      // select all\n    else\n        InitValue(INTEGER_SEED, compare, int(double(double(127) * select_ratio)));\n\n    LessThan<T> select_op(compare);\n    int num_selected = Solve(h_in, select_op, h_reference, h_flags, num_items);\n\n    if (g_verbose) std::cout << \"\\nComparison item: \" << compare << \"\\n\";\n    printf(\"\\nIterator %s cub::%s::%s %d items, %d selected (select ratio %.3f), %s %d-byte elements\\n\",\n        (IS_PARTITION) ? \"DevicePartition\" : \"DeviceSelect\",\n        (IS_FLAGGED) ? \"Flagged\" : \"If\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_selected) / num_items, typeid(T).name(), (int) sizeof(T));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND, IS_FLAGGED, IS_PARTITION>(h_in, h_flags, select_op, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n    if (h_flags) delete[] h_flags;\n}\n\n\n/**\n * Test different selection ratios\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid Test(\n    int             num_items)\n{\n    for (float select_ratio = 0.0f; select_ratio <= 1.0f; select_ratio += 0.2f)\n    {\n        TestPointer<BACKEND, IS_FLAGGED, IS_PARTITION, T>(num_items, select_ratio);\n    }\n}\n\n\n/**\n * Test (select vs. partition) and (flagged vs. functor)\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestMethod(\n    int             num_items)\n{\n    // Functor\n    Test<BACKEND, false, false, T>(num_items);\n    Test<BACKEND, false, true, T>(num_items);\n\n    // Flagged\n    Test<BACKEND, true, false, T>(num_items);\n    Test<BACKEND, true, true, T>(num_items);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        T>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    TestMethod<CUB, T>(num_items);\n#elif TEST_CDP == 1\n    TestMethod<CDP, T>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <typename T>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<T>(0);\n        TestOp<T>(1);\n        TestOp<T>(100);\n        TestOp<T>(10000);\n        TestOp<T>(1000000);\n    }\n    else\n    {\n        TestOp<T>(num_items);\n    }\n}\n\ntemplate<class T0, class T1>\nstruct pair_to_col_t\n{\n    __host__ __device__ T0 operator()(const thrust::tuple<T0, T1> &in)\n    {\n        return thrust::get<0>(in);\n    }\n};\n\ntemplate<class T0, class T1>\nstruct select_t {\n    __host__ __device__ bool operator()(const thrust::tuple<T0, T1> &in) {\n        return static_cast<T1>(thrust::get<0>(in)) > thrust::get<1>(in);\n    }\n};\n\ntemplate <typename T0, typename T1>\nvoid TestMixedOp(int num_items)\n{\n    const T0 target_value = static_cast<T0>(42);\n    thrust::device_vector<T0> col_a(num_items, target_value);\n    thrust::device_vector<T1> col_b(num_items, static_cast<T1>(4.2));\n\n    thrust::device_vector<T0> result(num_items);\n\n    auto in = thrust::make_zip_iterator(col_a.begin(), col_b.begin());\n    auto out = thrust::make_transform_output_iterator(result.begin(), pair_to_col_t<T0, T1>{});\n\n    void *d_tmp_storage {};\n    std::size_t tmp_storage_size{};\n    cub::DeviceSelect::If(\n            d_tmp_storage, tmp_storage_size,\n            in, out, thrust::make_discard_iterator(),\n            num_items, select_t<T0, T1>{});\n\n    thrust::device_vector<char> tmp_storage(tmp_storage_size);\n    d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n    cub::DeviceSelect::If(\n            d_tmp_storage, tmp_storage_size,\n            in, out, thrust::make_discard_iterator(),\n            num_items, select_t<T0, T1>{});\n\n    AssertEquals(num_items, thrust::count(result.begin(), result.end(), target_value));\n}\n\n/**\n * Test different input sizes\n */\ntemplate <typename T0, typename T1>\nvoid TestMixed(int num_items)\n{\n    if (num_items < 0)\n    {\n        TestMixedOp<T0, T1>(0);\n        TestMixedOp<T0, T1>(1);\n        TestMixedOp<T0, T1>(100);\n        TestMixedOp<T0, T1>(10000);\n        TestMixedOp<T0, T1>(1000000);\n    }\n    else\n    {\n        TestMixedOp<T0, T1>(num_items);\n    }\n}\n\nvoid TestFlagsNormalization()\n{\n  const int num_items = 1024 * 1024;\n  thrust::device_vector<int> result(num_items);\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n  CubDebugExit(\n    cub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               cub::CountingInputIterator<int>(0),      // in\n                               cub::CountingInputIterator<int>(1),      // flags\n                               thrust::raw_pointer_cast(result.data()), // out\n                               thrust::make_discard_iterator(), // num_out\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  CubDebugExit(\n    cub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               cub::CountingInputIterator<int>(0),      // in\n                               cub::CountingInputIterator<int>(1),      // flags\n                               thrust::raw_pointer_cast(result.data()), // out\n                               thrust::make_discard_iterator(), // num_out\n                               num_items));\n\n  AssertTrue(thrust::equal(result.begin(),\n                           result.end(),\n                           thrust::make_counting_iterator(0)));\n}\n\nvoid TestFlagsAliasingInPartition()\n{\n  int h_items[]{0, 1, 0, 2, 0, 3, 0, 4, 0, 5};\n  constexpr int num_items = sizeof(h_items) / sizeof(h_items[0]);\n\n  int *d_in{};\n  int *d_out{};\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in, sizeof(h_items)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_out, sizeof(h_items)));\n\n  CubDebugExit(\n    cudaMemcpy(d_in, h_items, sizeof(h_items), cudaMemcpyHostToDevice));\n\n  // alias flags and keys\n  int *d_flags = d_in;\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    cub::DevicePartition::Flagged(d_tmp_storage,\n                                  tmp_storage_size,\n                                  d_in,\n                                  d_flags,\n                                  d_out,\n                                  thrust::make_discard_iterator(), // num_out\n                                  num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  CubDebugExit(\n    cub::DevicePartition::Flagged(d_tmp_storage,\n                                  tmp_storage_size,\n                                  d_in,\n                                  d_flags,\n                                  d_out,\n                                  thrust::make_discard_iterator(), // num_out\n                                  num_items));\n\n  AssertTrue(thrust::equal(thrust::device,\n                           d_out,\n                           d_out + num_items / 2,\n                           thrust::make_counting_iterator(1)));\n\n  AssertEquals(\n    thrust::count(thrust::device, d_out + num_items / 2, d_out + num_items, 0),\n    num_items / 2);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\nstruct Odd\n{\n  __host__ __device__ bool operator()(int v) const { return v % 2; }\n};\n\nvoid TestIfInPlace()\n{\n  const int num_items = 4 * 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<int> reference(num_items);\n  thrust::device_vector<int> reference_out(1);\n\n  thrust::sequence(data.begin(), data.end());\n\n  Odd op{};\n\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  int *d_reference = thrust::raw_pointer_cast(reference.data());\n  int *d_reference_out = thrust::raw_pointer_cast(reference_out.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    cub::DeviceSelect::If(d_tmp_storage,\n                          tmp_storage_size,\n                          d_data,\n                          d_num_out,\n                          num_items,\n                          op));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    thrust::shuffle(data.begin(), data.end(), g);\n\n    CubDebugExit(\n      cub::DeviceSelect::If(d_tmp_storage,\n                            tmp_storage_size,\n                            d_data,\n                            d_reference,\n                            d_reference_out,\n                            num_items,\n                            op));\n\n    CubDebugExit(\n      cub::DeviceSelect::If(d_tmp_storage,\n                            tmp_storage_size,\n                            d_data,\n                            d_num_out,\n                            num_items,\n                            op));\n\n    AssertEquals(num_out, reference_out);\n    const int num_selected = num_out[0];\n\n    const bool match_reference = thrust::equal(reference.begin(),\n                                               reference.begin() + num_selected,\n                                               data.begin());\n    AssertTrue(match_reference);\n  }\n}\n\nvoid TestFlaggedInPlace()\n{\n  const int num_items = 4 * 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<bool> flags(num_items);\n\n  int h_num_out{};\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  bool *d_flags = thrust::raw_pointer_cast(flags.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    cub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               d_data,\n                               d_flags,\n                               d_num_out,\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    const int num_selected = RandomValue(num_items);\n\n    thrust::sequence(data.begin(), data.end());\n    thrust::fill(flags.begin(), flags.begin() + num_selected, true);\n    thrust::fill(flags.begin() + num_selected, flags.end(), false);\n    thrust::shuffle(flags.begin(), flags.end(), g);\n\n    CubDebugExit(\n      cub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,\n                                 d_flags,\n                                 d_num_out,\n                                 num_items));\n\n    cudaMemcpy(&h_num_out, d_num_out, sizeof(int), cudaMemcpyDeviceToHost);\n\n    AssertEquals(num_selected, h_num_out);\n\n    auto selection_perm_begin = thrust::make_permutation_iterator(flags.begin(),\n                                                                  data.begin());\n    auto selection_perm_end = selection_perm_begin + num_selected;\n\n    AssertEquals(num_selected,\n                 thrust::count(selection_perm_begin, selection_perm_end, true));\n  }\n}\n\nvoid TestFlaggedInPlaceWithAliasedFlags()\n{\n  const int num_items = 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<int> reference(num_items);\n  thrust::device_vector<int> flags(num_items);\n\n  int h_num_out{};\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  int *d_flags = d_data; // alias\n  int *d_allocated_flags = thrust::raw_pointer_cast(data.data());\n  int *d_reference = thrust::raw_pointer_cast(reference.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    cub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               d_data,\n                               d_flags,\n                               d_num_out,\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    const int num_selected = RandomValue(num_items);\n\n    thrust::sequence(data.begin(), data.begin() + num_selected, 1);\n    thrust::fill(data.begin() + num_selected, data.end(), 0);\n    thrust::shuffle(data.begin(), data.end(), g);\n\n    CubDebugExit(\n      cub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,      // in\n                                 d_allocated_flags,\n                                 d_reference, // out\n                                 d_num_out,\n                                 num_items));\n\n    CubDebugExit(\n      cub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,\n                                 d_flags,\n                                 d_num_out,\n                                 num_items));\n\n    cudaMemcpy(&h_num_out, d_num_out, sizeof(int), cudaMemcpyDeviceToHost);\n\n    AssertEquals(num_selected, h_num_out);\n\n    const bool match_reference = thrust::equal(reference.begin(),\n                                               reference.begin() + num_selected,\n                                               data.begin());\n    AssertTrue(match_reference);\n  }\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    float select_ratio      = 0.5;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"ratio\", select_ratio);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--ratio=<selection ratio, default 0.5>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    TestFlagsAliasingInPartition();\n\n    TestFlaggedInPlace();\n    TestFlaggedInPlaceWithAliasedFlags();\n    TestIfInPlace();\n\n    Test<unsigned char>(num_items);\n    Test<unsigned short>(num_items);\n    Test<unsigned int>(num_items);\n    Test<unsigned long long>(num_items);\n\n    Test<uchar2>(num_items);\n    Test<ushort2>(num_items);\n    Test<uint2>(num_items);\n    Test<ulonglong2>(num_items);\n\n    Test<uchar4>(num_items);\n    Test<ushort4>(num_items);\n    Test<uint4>(num_items);\n    Test<ulonglong4>(num_items);\n\n    Test<TestFoo>(num_items);\n    Test<TestBar>(num_items);\n\n    TestMixed<int, double>(num_items);\n    TestFlagsNormalization();\n\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::If and DevicePartition::If utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/device/device_partition.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/count.h>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/random.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n// Selection functor type\ntemplate <typename T>\nstruct LessThan\n{\n    T compare;\n\n    __host__ __device__ __forceinline__\n    LessThan(T compare) : compare(compare) {}\n\n    __host__ __device__ __forceinline__\n    bool operator()(const T &a) const {\n        return (a < compare);\n    }\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to select if entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<false>             /*is_flagged*/,\n    Int2Type<false>             /*is_partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    hipError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               /*d_flags*/,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   select_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to partition if entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<false>             /*is_flagged*/,\n    Int2Type<true>              /*is_partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    hipError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               /*d_flags*/,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   select_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DevicePartition::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to select flagged entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<true>              /*is_flagged*/,\n    Int2Type<false>             /*partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    hipError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               d_flags,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   /*select_op*/)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to partition flagged entrypoint\n */\ntemplate <typename InputIteratorT, typename FlagIteratorT, typename SelectOpT, typename OutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    Int2Type<true>              /*is_flagged*/,\n    Int2Type<true>              /*partition*/,\n    int                         timing_timing_iterations,\n    size_t*                     /*d_temp_storage_bytes*/,\n    hipError_t*                /*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t&                     temp_storage_bytes,\n    InputIteratorT              d_in,\n    FlagIteratorT               d_flags,\n    OutputIteratorT             d_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items,\n    SelectOpT                   /*select_op*/)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DevicePartition::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename IsFlaggedTag,\n          typename IsPartitionTag,\n          typename InputIteratorT,\n          typename FlagIteratorT,\n          typename SelectOpT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  IsFlaggedTag         is_flagged,\n                                  IsPartitionTag       is_partition,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  InputIteratorT       d_in,\n                                  FlagIteratorT        d_flags,\n                                  OutputIteratorT      d_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items,\n                                  SelectOpT            select_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          is_flagged,\n                          is_partition,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_flags,\n                          d_out,\n                          d_num_selected_out,\n                          num_items,\n                          select_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename IsFlaggedTag,\n          typename IsPartitionTag,\n          typename InputIteratorT,\n          typename FlagIteratorT,\n          typename SelectOpT,\n          typename OutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\nhipError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     IsFlaggedTag   is_flagged,\n                     IsPartitionTag is_partition,\n                     int            timing_timing_iterations,\n                     size_t        *d_temp_storage_bytes,\n                     hipError_t   *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     InputIteratorT       d_in,\n                     FlagIteratorT        d_flags,\n                     OutputIteratorT      d_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items,\n                     SelectOpT            select_op)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              IsFlaggedTag,\n                              IsPartitionTag,\n                              InputIteratorT,\n                              FlagIteratorT,\n                              SelectOpT,\n                              OutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            is_flagged,\n            is_partition,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_flags,\n            d_out,\n            d_num_selected_out,\n            num_items,\n            select_op);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    T*  h_in,\n    int num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        // Initialize each item to a randomly selected value from [0..126]\n        unsigned int value;\n        RandomBits(value, 0, 0, 7);\n        if (value == 127)\n            value = 126;\n        InitValue(INTEGER_SEED, h_in[i], value);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve selection problem (and set corresponding flags)\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        FlagIteratorT,\n    typename        SelectOpT,\n    typename        T>\nint Solve(\n    InputIteratorT  h_in,\n    SelectOpT       select_op,\n    T*              h_reference,\n    FlagIteratorT   h_flags,\n    int             num_items)\n{\n    int num_selected = 0;\n    for (int i = 0; i < num_items; ++i)\n    {\n        if ((h_flags[i] = select_op(h_in[i])))\n        {\n            h_reference[num_selected] = h_in[i];\n            num_selected++;\n        }\n        else\n        {\n            h_reference[num_items - (i - num_selected) - 1] = h_in[i];\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    bool                IS_FLAGGED,\n    bool                IS_PARTITION,\n    typename            DeviceInputIteratorT,\n    typename            FlagT,\n    typename            SelectOpT,\n    typename            T>\nvoid Test(\n    DeviceInputIteratorT    d_in,\n    FlagT*                  h_flags,\n    SelectOpT               select_op,\n    T*                      h_reference,\n    int                     num_selected,\n    int                     num_items)\n{\n    // Allocate device flags, output, and num-selected\n    FlagT*      d_flags = NULL;\n    T*          d_out = NULL;\n    int*        d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_flags, sizeof(FlagT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(T) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t*         d_temp_storage_bytes = NULL;\n    hipError_t*    d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), 1, d_temp_storage_bytes, d_cdp_error,\n    d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Copy flags and clear device output array\n    CubDebugExit(hipMemcpy(d_flags, h_flags, sizeof(FlagT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemset(d_out, 0, sizeof(T) * num_items));\n    CubDebugExit(hipMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), 1, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n\n    // Check for correctness (and display results, if specified)\n    int compare1 = (IS_PARTITION) ?\n        CompareDeviceResults(h_reference, d_out, num_items, true, g_verbose) :\n        CompareDeviceResults(h_reference, d_out, num_selected, true, g_verbose);\n    printf(\"\\t Data %s\\n\", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s\\n\", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), Int2Type<IS_FLAGGED>(), Int2Type<IS_PARTITION>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items, select_op));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float   avg_millis          = elapsed_millis / g_timing_iterations;\n        float   giga_rate           = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        int     num_output_items    = (IS_PARTITION) ? num_items : num_selected;\n        int     num_flag_items      = (IS_FLAGGED) ? num_items : 0;\n        size_t  num_bytes           = sizeof(T) * (num_items + num_output_items) + sizeof(FlagT) * num_flag_items;\n        float   giga_bandwidth      = float(num_bytes) / avg_millis / 1000.0f / 1000.0f;\n\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_flags) CubDebugExit(g_allocator.DeviceFree(d_flags));\n    if (d_out) CubDebugExit(g_allocator.DeviceFree(d_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid TestPointer(\n    int             num_items,\n    float           select_ratio)\n{\n    typedef char FlagT;\n\n    // Allocate host arrays\n    T*      h_in        = new T[num_items];\n    FlagT*  h_flags     = new FlagT[num_items];\n    T*      h_reference = new T[num_items];\n\n    // Initialize input\n    Initialize(h_in, num_items);\n\n    // Select a comparison value that is select_ratio through the space of [0,127]\n    T compare;\n    if (select_ratio <= 0.0)\n        InitValue(INTEGER_SEED, compare, 0);        // select none\n    else if (select_ratio >= 1.0)\n        InitValue(INTEGER_SEED, compare, 127);      // select all\n    else\n        InitValue(INTEGER_SEED, compare, int(double(double(127) * select_ratio)));\n\n    LessThan<T> select_op(compare);\n    int num_selected = Solve(h_in, select_op, h_reference, h_flags, num_items);\n\n    if (g_verbose) std::cout << \"\\nComparison item: \" << compare << \"\\n\";\n    printf(\"\\nPointer %s cub::%s::%s %d items, %d selected (select ratio %.3f), %s %d-byte elements\\n\",\n        (IS_PARTITION) ? \"DevicePartition\" : \"DeviceSelect\",\n        (IS_FLAGGED) ? \"Flagged\" : \"If\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_selected) / num_items, typeid(T).name(), (int) sizeof(T));\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    T *d_in = NULL;\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(T) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_in, h_in, sizeof(T) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND, IS_FLAGGED, IS_PARTITION>(d_in, h_flags, select_op, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_flags) delete[] h_flags;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid TestIterator(\n    int             num_items,\n    float           select_ratio)\n{\n    typedef char FlagT;\n\n    // Allocate host arrays\n    T*      h_reference = new T[num_items];\n    FlagT*  h_flags = new FlagT[num_items];\n\n    // Use counting iterator as the input\n    CountingInputIterator<T, int> h_in(0);\n\n    // Select a comparison value that is select_ratio through the space of [0,127]\n    T compare;\n    if (select_ratio <= 0.0)\n        InitValue(INTEGER_SEED, compare, 0);        // select none\n    else if (select_ratio >= 1.0)\n        InitValue(INTEGER_SEED, compare, 127);      // select all\n    else\n        InitValue(INTEGER_SEED, compare, int(double(double(127) * select_ratio)));\n\n    LessThan<T> select_op(compare);\n    int num_selected = Solve(h_in, select_op, h_reference, h_flags, num_items);\n\n    if (g_verbose) std::cout << \"\\nComparison item: \" << compare << \"\\n\";\n    printf(\"\\nIterator %s cub::%s::%s %d items, %d selected (select ratio %.3f), %s %d-byte elements\\n\",\n        (IS_PARTITION) ? \"DevicePartition\" : \"DeviceSelect\",\n        (IS_FLAGGED) ? \"Flagged\" : \"If\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_selected) / num_items, typeid(T).name(), (int) sizeof(T));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND, IS_FLAGGED, IS_PARTITION>(h_in, h_flags, select_op, h_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n    if (h_flags) delete[] h_flags;\n}\n\n\n/**\n * Test different selection ratios\n */\ntemplate <\n    Backend         BACKEND,\n    bool            IS_FLAGGED,\n    bool            IS_PARTITION,\n    typename        T>\nvoid Test(\n    int             num_items)\n{\n    for (float select_ratio = 0.0f; select_ratio <= 1.0f; select_ratio += 0.2f)\n    {\n        TestPointer<BACKEND, IS_FLAGGED, IS_PARTITION, T>(num_items, select_ratio);\n    }\n}\n\n\n/**\n * Test (select vs. partition) and (flagged vs. functor)\n */\ntemplate <\n    Backend         BACKEND,\n    typename        T>\nvoid TestMethod(\n    int             num_items)\n{\n    // Functor\n    Test<BACKEND, false, false, T>(num_items);\n    Test<BACKEND, false, true, T>(num_items);\n\n    // Flagged\n    Test<BACKEND, true, false, T>(num_items);\n    Test<BACKEND, true, true, T>(num_items);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        T>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    TestMethod<CUB, T>(num_items);\n#elif TEST_CDP == 1\n    TestMethod<CDP, T>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <typename T>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<T>(0);\n        TestOp<T>(1);\n        TestOp<T>(100);\n        TestOp<T>(10000);\n        TestOp<T>(1000000);\n    }\n    else\n    {\n        TestOp<T>(num_items);\n    }\n}\n\ntemplate<class T0, class T1>\nstruct pair_to_col_t\n{\n    __host__ __device__ T0 operator()(const thrust::tuple<T0, T1> &in)\n    {\n        return thrust::get<0>(in);\n    }\n};\n\ntemplate<class T0, class T1>\nstruct select_t {\n    __host__ __device__ bool operator()(const thrust::tuple<T0, T1> &in) {\n        return static_cast<T1>(thrust::get<0>(in)) > thrust::get<1>(in);\n    }\n};\n\ntemplate <typename T0, typename T1>\nvoid TestMixedOp(int num_items)\n{\n    const T0 target_value = static_cast<T0>(42);\n    thrust::device_vector<T0> col_a(num_items, target_value);\n    thrust::device_vector<T1> col_b(num_items, static_cast<T1>(4.2));\n\n    thrust::device_vector<T0> result(num_items);\n\n    auto in = thrust::make_zip_iterator(col_a.begin(), col_b.begin());\n    auto out = thrust::make_transform_output_iterator(result.begin(), pair_to_col_t<T0, T1>{});\n\n    void *d_tmp_storage {};\n    std::size_t tmp_storage_size{};\n    hipcub::DeviceSelect::If(\n            d_tmp_storage, tmp_storage_size,\n            in, out, thrust::make_discard_iterator(),\n            num_items, select_t<T0, T1>{});\n\n    thrust::device_vector<char> tmp_storage(tmp_storage_size);\n    d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n    hipcub::DeviceSelect::If(\n            d_tmp_storage, tmp_storage_size,\n            in, out, thrust::make_discard_iterator(),\n            num_items, select_t<T0, T1>{});\n\n    AssertEquals(num_items, thrust::count(result.begin(), result.end(), target_value));\n}\n\n/**\n * Test different input sizes\n */\ntemplate <typename T0, typename T1>\nvoid TestMixed(int num_items)\n{\n    if (num_items < 0)\n    {\n        TestMixedOp<T0, T1>(0);\n        TestMixedOp<T0, T1>(1);\n        TestMixedOp<T0, T1>(100);\n        TestMixedOp<T0, T1>(10000);\n        TestMixedOp<T0, T1>(1000000);\n    }\n    else\n    {\n        TestMixedOp<T0, T1>(num_items);\n    }\n}\n\nvoid TestFlagsNormalization()\n{\n  const int num_items = 1024 * 1024;\n  thrust::device_vector<int> result(num_items);\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n  CubDebugExit(\n    hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               hipcub::CountingInputIterator<int>(0),      // in\n                               hipcub::CountingInputIterator<int>(1),      // flags\n                               thrust::raw_pointer_cast(result.data()), // out\n                               thrust::make_discard_iterator(), // num_out\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  CubDebugExit(\n    hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               hipcub::CountingInputIterator<int>(0),      // in\n                               hipcub::CountingInputIterator<int>(1),      // flags\n                               thrust::raw_pointer_cast(result.data()), // out\n                               thrust::make_discard_iterator(), // num_out\n                               num_items));\n\n  AssertTrue(thrust::equal(result.begin(),\n                           result.end(),\n                           thrust::make_counting_iterator(0)));\n}\n\nvoid TestFlagsAliasingInPartition()\n{\n  int h_items[]{0, 1, 0, 2, 0, 3, 0, 4, 0, 5};\n  constexpr int num_items = sizeof(h_items) / sizeof(h_items[0]);\n\n  int *d_in{};\n  int *d_out{};\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in, sizeof(h_items)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_out, sizeof(h_items)));\n\n  CubDebugExit(\n    hipMemcpy(d_in, h_items, sizeof(h_items), hipMemcpyHostToDevice));\n\n  // alias flags and keys\n  int *d_flags = d_in;\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    hipcub::DevicePartition::Flagged(d_tmp_storage,\n                                  tmp_storage_size,\n                                  d_in,\n                                  d_flags,\n                                  d_out,\n                                  thrust::make_discard_iterator(), // num_out\n                                  num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  CubDebugExit(\n    hipcub::DevicePartition::Flagged(d_tmp_storage,\n                                  tmp_storage_size,\n                                  d_in,\n                                  d_flags,\n                                  d_out,\n                                  thrust::make_discard_iterator(), // num_out\n                                  num_items));\n\n  AssertTrue(thrust::equal(thrust::device,\n                           d_out,\n                           d_out + num_items / 2,\n                           thrust::make_counting_iterator(1)));\n\n  AssertEquals(\n    thrust::count(thrust::device, d_out + num_items / 2, d_out + num_items, 0),\n    num_items / 2);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\nstruct Odd\n{\n  __host__ __device__ bool operator()(int v) const { return v % 2; }\n};\n\nvoid TestIfInPlace()\n{\n  const int num_items = 4 * 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<int> reference(num_items);\n  thrust::device_vector<int> reference_out(1);\n\n  thrust::sequence(data.begin(), data.end());\n\n  Odd op{};\n\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  int *d_reference = thrust::raw_pointer_cast(reference.data());\n  int *d_reference_out = thrust::raw_pointer_cast(reference_out.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    hipcub::DeviceSelect::If(d_tmp_storage,\n                          tmp_storage_size,\n                          d_data,\n                          d_num_out,\n                          num_items,\n                          op));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    thrust::shuffle(data.begin(), data.end(), g);\n\n    CubDebugExit(\n      hipcub::DeviceSelect::If(d_tmp_storage,\n                            tmp_storage_size,\n                            d_data,\n                            d_reference,\n                            d_reference_out,\n                            num_items,\n                            op));\n\n    CubDebugExit(\n      hipcub::DeviceSelect::If(d_tmp_storage,\n                            tmp_storage_size,\n                            d_data,\n                            d_num_out,\n                            num_items,\n                            op));\n\n    AssertEquals(num_out, reference_out);\n    const int num_selected = num_out[0];\n\n    const bool match_reference = thrust::equal(reference.begin(),\n                                               reference.begin() + num_selected,\n                                               data.begin());\n    AssertTrue(match_reference);\n  }\n}\n\nvoid TestFlaggedInPlace()\n{\n  const int num_items = 4 * 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<bool> flags(num_items);\n\n  int h_num_out{};\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  bool *d_flags = thrust::raw_pointer_cast(flags.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               d_data,\n                               d_flags,\n                               d_num_out,\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    const int num_selected = RandomValue(num_items);\n\n    thrust::sequence(data.begin(), data.end());\n    thrust::fill(flags.begin(), flags.begin() + num_selected, true);\n    thrust::fill(flags.begin() + num_selected, flags.end(), false);\n    thrust::shuffle(flags.begin(), flags.end(), g);\n\n    CubDebugExit(\n      hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,\n                                 d_flags,\n                                 d_num_out,\n                                 num_items));\n\n    hipMemcpy(&h_num_out, d_num_out, sizeof(int), hipMemcpyDeviceToHost);\n\n    AssertEquals(num_selected, h_num_out);\n\n    auto selection_perm_begin = thrust::make_permutation_iterator(flags.begin(),\n                                                                  data.begin());\n    auto selection_perm_end = selection_perm_begin + num_selected;\n\n    AssertEquals(num_selected,\n                 thrust::count(selection_perm_begin, selection_perm_end, true));\n  }\n}\n\nvoid TestFlaggedInPlaceWithAliasedFlags()\n{\n  const int num_items = 1024 * 1024;\n  const int num_iters = 42;\n\n  thrust::device_vector<int> num_out(1);\n  thrust::device_vector<int> data(num_items);\n  thrust::device_vector<int> reference(num_items);\n  thrust::device_vector<int> flags(num_items);\n\n  int h_num_out{};\n  int *d_num_out = thrust::raw_pointer_cast(num_out.data());\n  int *d_data = thrust::raw_pointer_cast(data.data());\n  int *d_flags = d_data; // alias\n  int *d_allocated_flags = thrust::raw_pointer_cast(data.data());\n  int *d_reference = thrust::raw_pointer_cast(reference.data());\n\n  void *d_tmp_storage{};\n  std::size_t tmp_storage_size{};\n\n  CubDebugExit(\n    hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                               tmp_storage_size,\n                               d_data,\n                               d_flags,\n                               d_num_out,\n                               num_items));\n\n  thrust::device_vector<char> tmp_storage(tmp_storage_size);\n  d_tmp_storage = thrust::raw_pointer_cast(tmp_storage.data());\n\n  thrust::default_random_engine g{};\n\n  for (int iter = 0; iter < num_iters; iter++)\n  {\n    const int num_selected = RandomValue(num_items);\n\n    thrust::sequence(data.begin(), data.begin() + num_selected, 1);\n    thrust::fill(data.begin() + num_selected, data.end(), 0);\n    thrust::shuffle(data.begin(), data.end(), g);\n\n    CubDebugExit(\n      hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,      // in\n                                 d_allocated_flags,\n                                 d_reference, // out\n                                 d_num_out,\n                                 num_items));\n\n    CubDebugExit(\n      hipcub::DeviceSelect::Flagged(d_tmp_storage,\n                                 tmp_storage_size,\n                                 d_data,\n                                 d_flags,\n                                 d_num_out,\n                                 num_items));\n\n    hipMemcpy(&h_num_out, d_num_out, sizeof(int), hipMemcpyDeviceToHost);\n\n    AssertEquals(num_selected, h_num_out);\n\n    const bool match_reference = thrust::equal(reference.begin(),\n                                               reference.begin() + num_selected,\n                                               data.begin());\n    AssertTrue(match_reference);\n  }\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    float select_ratio      = 0.5;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"ratio\", select_ratio);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--ratio=<selection ratio, default 0.5>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    TestFlagsAliasingInPartition();\n\n    TestFlaggedInPlace();\n    TestFlaggedInPlaceWithAliasedFlags();\n    TestIfInPlace();\n\n    Test<unsigned char>(num_items);\n    Test<unsigned short>(num_items);\n    Test<unsigned int>(num_items);\n    Test<unsigned long long>(num_items);\n\n    Test<uchar2>(num_items);\n    Test<ushort2>(num_items);\n    Test<uint2>(num_items);\n    Test<ulonglong2>(num_items);\n\n    Test<uchar4>(num_items);\n    Test<ushort4>(num_items);\n    Test<uint4>(num_items);\n    Test<ulonglong4>(num_items);\n\n    Test<TestFoo>(num_items);\n    Test<TestBar>(num_items);\n\n    TestMixed<int, double>(num_items);\n    TestFlagsNormalization();\n\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_device_select_if.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2021 NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceScan utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <limits>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\ndouble                  g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\nenum AliasMode\n{\n  AliasNone,  // output is allocated\n  AliasKeys,  // output is an alias of input keys\n  AliasValues // output is an alias of input values\n};\n\n\n/**\n * \\brief WrapperFunctor (for precluding test-specialized dispatch to *Sum variants)\n */\ntemplate<typename OpT>\nstruct WrapperFunctor\n{\n    OpT op;\n\n    WrapperFunctor(OpT op) : op(op) {}\n\n    template <typename T, typename U>\n    __host__ __device__ __forceinline__ auto operator()(const T &a, const U &b) const\n      -> decltype(op(a, b))\n    {\n        return static_cast<T>(op(a, b));\n    }\n};\n\n/**\n * \\brief DivideByFiveFunctor (used by TestIterator)\n */\ntemplate<typename OutputT>\nstruct DivideByFiveFunctor\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ OutputT operator()(const T &a) const\n    {\n        return static_cast<OutputT>(a / 5);\n    }\n};\n\n/**\n * \\brief Mod2Equality (used for non-bool keys to make keys more likely to equal each other)\n */\nstruct Mod2Equality\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ T operator()(const T &a, const T &b) const\n    {\n        return (a % 2) == (b % 2);\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceScan entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to exclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT, typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename ScanOpT, typename InitialValueT, typename OffsetT, typename EqualityOpT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    IsPrimitiveT          /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    cudaError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    ScanOpT               scan_op,\n    InitialValueT         initial_value,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::ExclusiveScanByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, scan_op, initial_value, num_items, equality_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to exclusive sum entrypoint\n */\ntemplate <typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename InitialValueT, typename OffsetT, typename EqualityOpT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    Int2Type<true>        /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    cudaError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    Sum                   /*scan_op*/,\n    InitialValueT         /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::ExclusiveSumByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, num_items, equality_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to inclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT, typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename ScanOpT, typename OffsetT, typename EqualityOpT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    IsPrimitiveT          /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    cudaError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    ScanOpT               scan_op,\n    NullType              /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::InclusiveScanByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, scan_op, num_items, equality_op);\n    }\n    return error;\n}\n\n/**\n * Dispatch to inclusive sum entrypoint\n */\ntemplate <typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename OffsetT, typename EqualityOpT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    Int2Type<true>        /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    cudaError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    Sum                   /*scan_op*/,\n    NullType              /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::InclusiveSumByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, num_items, equality_op);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceScan\n */\ntemplate <int CubBackend,\n          typename IsPrimitiveT,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename ValuesOutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT,\n          typename EqualityOpT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  IsPrimitiveT         is_primitive,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                 *d_temp_storage,\n                                  size_t                temp_storage_bytes,\n                                  KeysInputIteratorT    d_keys_in,\n                                  ValuesInputIteratorT  d_values_in,\n                                  ValuesOutputIteratorT d_values_out,\n                                  ScanOpT               scan_op,\n                                  InitialValueT         initial_value,\n                                  OffsetT               num_items,\n                                  EqualityOpT           equality_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          is_primitive,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_values_in,\n                          d_values_out,\n                          scan_op,\n                          initial_value,\n                          num_items,\n                          equality_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename IsPrimitiveT,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename ValuesOutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT,\n          typename EqualityOpT>\ncudaError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     IsPrimitiveT is_primitive,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     cudaError_t *d_cdp_error,\n\n                     void                 *d_temp_storage,\n                     size_t               &temp_storage_bytes,\n                     KeysInputIteratorT    d_keys_in,\n                     ValuesInputIteratorT  d_values_in,\n                     ValuesOutputIteratorT d_values_out,\n                     ScanOpT               scan_op,\n                     InitialValueT         initial_value,\n                     OffsetT               num_items,\n                     EqualityOpT           equality_op)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              IsPrimitiveT,\n                              KeysInputIteratorT,\n                              ValuesInputIteratorT,\n                              ValuesOutputIteratorT,\n                              ScanOpT,\n                              InitialValueT,\n                              OffsetT,\n                              EqualityOpT>,\n            Int2Type<CUB>{},\n            is_primitive,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_values_in,\n            d_values_out,\n            scan_op,\n            initial_value,\n            num_items,\n            equality_op);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    GenMode      gen_mode,\n    T            *h_in,\n    int          num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n/**\n * Solve exclusive-scan problem\n */\ntemplate <\n    typename        KeysInputIteratorT,\n    typename        ValuesInputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Solve(\n    KeysInputIteratorT    h_keys_in,\n    ValuesInputIteratorT  h_values_in,\n    OutputT               *h_reference,\n    int                   num_items,\n    ScanOpT               scan_op,\n    InitialValueT         initial_value,\n    EqualityOpT           equality_op)\n{\n    using ValueT = cub::detail::value_t<ValuesInputIteratorT>;\n    using AccumT = cub::detail::accumulator_t<ScanOpT, InitialValueT, ValueT>;\n\n    if (num_items > 0)\n    {\n        for (int i = 0; i < num_items;) {\n            AccumT val         = static_cast<AccumT>(h_values_in[i]);\n            h_reference[i]     = initial_value;\n            AccumT inclusive   = static_cast<AccumT>(scan_op(initial_value, val));\n\n            ++i;\n\n            for (; i < num_items && equality_op(h_keys_in[i - 1], h_keys_in[i]); ++i)\n            {\n                val = static_cast<AccumT>(h_values_in[i]);\n                h_reference[i] = static_cast<OutputT>(inclusive);\n                inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n            }\n        }\n    }\n}\n\n\n/**\n * Solve inclusive-scan problem\n */\ntemplate <\n    typename        KeysInputIteratorT,\n    typename        ValuesInputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        EqualityOpT>\nvoid Solve(\n    KeysInputIteratorT    h_keys_in,\n    ValuesInputIteratorT  h_values_in,\n    OutputT               *h_reference,\n    int                   num_items,\n    ScanOpT               scan_op,\n    NullType              /*initial_value*/,\n    EqualityOpT           equality_op)\n{\n    using ValueT = cub::detail::value_t<ValuesInputIteratorT>;\n    using AccumT = cub::detail::accumulator_t<ScanOpT, ValueT, ValueT>;\n\n    if (num_items > 0)\n    {\n        for (int i = 0; i < num_items;) {\n            AccumT inclusive    = h_values_in[i];\n            h_reference[i]      = static_cast<OutputT>(inclusive);\n\n            ++i;\n\n            for (; i < num_items && equality_op(h_keys_in[i - 1], h_keys_in[i]); ++i)\n            {\n                AccumT val = h_values_in[i];\n                inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n                h_reference[i] = static_cast<OutputT>(inclusive);\n            }\n        }\n    }\n}\n\ntemplate<typename OutputT, typename DeviceInputIteratorT, bool InPlace>\nstruct AllocateOutput {\n    static void run(OutputT *&d_out, DeviceInputIteratorT, int num_items) {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_items));\n    }\n};\n\ntemplate<typename OutputT>\nstruct AllocateOutput<OutputT, OutputT *, true> {\n    static void run(OutputT *&d_out, OutputT *d_in, int /* num_items */) {\n        d_out = d_in;\n    }\n};\n\n/**\n * Test DeviceScan for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            KeysInputIteratorT,\n    typename            ValuesInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT,\n    typename            EqualityOpT,\n    AliasMode           Mode=AliasNone>\nvoid Test(\n    KeysInputIteratorT      d_keys_in,\n    ValuesInputIteratorT    d_values_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value,\n    EqualityOpT             equality_op)\n{\n    using KeyT = cub::detail::value_t<KeysInputIteratorT>;\n    using InputT = cub::detail::value_t<ValuesInputIteratorT>;\n\n    // Allocate device output array\n    OutputT *d_values_out = NULL;\n\n    if (Mode == AliasKeys)\n    {\n      AllocateOutput<OutputT, KeysInputIteratorT, Mode == AliasKeys>::run(\n        d_values_out,\n        d_keys_in,\n        num_items);\n    }\n    else\n    {\n      AllocateOutput<OutputT, ValuesInputIteratorT, Mode == AliasValues>::run(\n        d_values_out,\n        d_values_in,\n        num_items);\n    }\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,   sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_keys_in,\n        d_values_in,\n        d_values_out,\n        scan_op,\n        initial_value,\n        num_items,\n        equality_op));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    if (Mode == AliasNone)\n    {\n      CubDebugExit(cudaMemset(d_values_out, 0, sizeof(OutputT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_keys_in,\n        d_values_in,\n        d_values_out,\n        scan_op,\n        initial_value,\n        num_items,\n        equality_op));\n\n    // Check for correctness (and display results, if specified)\n    const int compare = CompareDeviceResults(h_reference,\n                                             d_values_out,\n                                             num_items,\n                                             true,\n                                             g_verbose);\n\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n      // Performance\n      GpuTimer gpu_timer;\n      gpu_timer.Start();\n      CubDebugExit(Dispatch(Int2Type<BACKEND>(),\n          Int2Type<Traits<OutputT>::PRIMITIVE>(),\n          g_timing_iterations,\n          d_temp_storage_bytes,\n          d_cdp_error,\n          d_temp_storage,\n          temp_storage_bytes,\n          d_keys_in,\n          d_values_in,\n          d_values_out,\n          scan_op,\n          initial_value,\n          num_items,\n          equality_op));\n\n      gpu_timer.Stop();\n      float elapsed_millis = gpu_timer.ElapsedMillis();\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = giga_rate * (sizeof(InputT) + sizeof(OutputT));\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\",\n            avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (Mode == AliasNone)\n    {\n      if (d_values_out) \n      {\n        CubDebugExit(g_allocator.DeviceFree(d_values_out));\n      }\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceValues(KeysInputIteratorT d_keys_in,\n                       OutputT *d_values_in,\n                       OutputT *h_reference,\n                       int num_items,\n                       ScanOpT scan_op,\n                       InitialValueT initial_value,\n                       EqualityOpT equality_op)\n{\n  Test<BACKEND,\n       KeysInputIteratorT,\n       OutputT *,\n       OutputT,\n       ScanOpT,\n       InitialValueT,\n       EqualityOpT,\n       AliasValues>(d_keys_in,\n                    d_values_in,\n                    h_reference,\n                    num_items,\n                    scan_op,\n                    initial_value,\n                    equality_op);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceValues(KeysInputIteratorT,\n                       ValuesInputIteratorT,\n                       OutputT *,\n                       int,\n                       ScanOpT,\n                       InitialValueT,\n                       EqualityOpT)\n{}\n\ntemplate <Backend BACKEND,\n          typename T,\n          typename ValuesInputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceKeys(T *d_keys_in,\n                     ValuesInputIteratorT d_values_in,\n                     T *h_reference,\n                     int num_items,\n                     ScanOpT scan_op,\n                     InitialValueT initial_value,\n                     EqualityOpT equality_op)\n{\n  Test<BACKEND,\n       T *,\n       ValuesInputIteratorT,\n       T,\n       ScanOpT,\n       InitialValueT,\n       EqualityOpT,\n       AliasKeys>(d_keys_in,\n                  d_values_in,\n                  h_reference,\n                  num_items,\n                  scan_op,\n                  initial_value,\n                  equality_op);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceKeys(KeysInputIteratorT,\n                     ValuesInputIteratorT,\n                     OutputT *,\n                     int,\n                     ScanOpT,\n                     InitialValueT,\n                     EqualityOpT)\n{}\n\n/**\n * Test DeviceScan on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid TestPointer(\n    int             num_items,\n    GenMode         gen_mode,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    printf(\"\\nPointer %s %s cub::DeviceScan::%s %d items, %s->%s (%d->%d bytes) , gen-mode %s\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT),\n        (gen_mode == RANDOM) ? \"RANDOM\" : (gen_mode == INTEGER_SEED) ? \"SEQUENTIAL\" : \"HOMOGENOUS\");\n    fflush(stdout);\n\n    // Allocate host arrays\n    KeyT*       h_keys_in   = new KeyT[num_items];\n    InputT*     h_values_in = new InputT[num_items];\n    OutputT*    h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Initialize(gen_mode, h_keys_in, num_items);\n    Initialize(gen_mode, h_values_in, num_items);\n\n    // If the output type is primitive and the operator is cub::Sum, the test\n    // dispatcher throws away scan_op and initial_value for exclusive scan.\n    // Without an initial_value arg, the accumulator switches to the input value\n    // type.\n    // Do the same thing here:\n    if (Traits<OutputT>::PRIMITIVE &&\n        std::is_same<ScanOpT, cub::Sum>::value &&\n        !std::is_same<InitialValueT, NullType>::value)\n    {\n      Solve(h_keys_in, h_values_in, h_reference, num_items, cub::Sum{}, InputT{}, equality_op);\n    }\n    else\n    {\n      Solve(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n    }\n\n    // Allocate problem device arrays\n    KeyT *d_keys_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    InputT *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(InputT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemcpy(d_values_in, h_values_in, sizeof(InputT) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in,\n                  d_values_in,\n                  h_reference,\n                  num_items,\n                  scan_op,\n                  initial_value,\n                  equality_op);\n\n    // Test in/out values aliasing\n    TestInplaceValues<BACKEND>(d_keys_in,\n                               d_values_in, \n                               h_reference,\n                               num_items,\n                               scan_op,\n                               initial_value,\n                               equality_op);\n\n    CubDebugExit(cudaMemcpy(d_values_in, h_values_in, sizeof(InputT) * num_items, cudaMemcpyHostToDevice));\n\n    // Test keys/values aliasing (should go last, changes keys)\n    TestInplaceKeys<BACKEND>(d_keys_in,\n                             d_values_in,\n                             h_reference,\n                             num_items,\n                             scan_op,\n                             initial_value,\n                             equality_op);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_reference) delete[] h_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test DeviceScan on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid TestIterator(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    printf(\"\\nIterator %s %s cub::DeviceScan::%s %d items, %s->%s (%d->%d bytes)\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT));\n    fflush(stdout);\n\n    // Use a counting iterator followed by div as the keys\n    using CountingIterT = CountingInputIterator<int, int>;\n    CountingIterT h_keys_in_helper(0);\n    TransformInputIterator<KeyT, DivideByFiveFunctor<KeyT>, CountingIterT> h_keys_in(h_keys_in_helper, DivideByFiveFunctor<KeyT>());\n\n    // Use a constant iterator as the input\n    InputT val = InputT();\n    ConstantInputIterator<InputT, int> h_values_in(val);\n\n    // Allocate host arrays\n    OutputT*  h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Solve(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n\n    // Run Test\n    Test<BACKEND>(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    TestPointer<BACKEND, KeyT, InputT, OutputT>(  num_items, UNIFORM, scan_op, initial_value, equality_op);\n    TestPointer<BACKEND, KeyT, InputT, OutputT>(  num_items, RANDOM,  scan_op, initial_value, equality_op);\n    TestIterator<BACKEND, KeyT, InputT, OutputT>( num_items, scan_op, initial_value, equality_op);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, InputT, OutputT>(num_items, scan_op, initial_value, equality_op);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, InputT, OutputT>(num_items, scan_op, initial_value, equality_op);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different operators\n */\ntemplate <typename KeyT, typename InputT, typename OutputT, typename EqualityOpT>\nvoid TestOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value,\n    EqualityOpT     equality_op)\n{\n    // Exclusive (use identity as initial value because it will dispatch to *Sum variants that don't take initial values)\n    Test<KeyT, InputT, OutputT>(num_items, cub::Sum(), identity, equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, cub::Max(), identity, equality_op);\n\n    // Exclusive (non-specialized, so we can test initial-value)\n    Test<KeyT, InputT, OutputT>(num_items, WrapperFunctor<cub::Sum>(cub::Sum()), initial_value, equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, WrapperFunctor<cub::Max>(cub::Max()), initial_value, equality_op);\n\n    // Inclusive (no initial value)\n    Test<KeyT, InputT, OutputT>(num_items, cub::Sum(), NullType(), equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, cub::Max(), NullType(), equality_op);\n}\n\n/**\n * Test different key type and equality operator\n */\ntemplate <typename InputT, typename OutputT>\nvoid TestKeyTAndEqualityOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value)\n{\n    TestOp<bool, InputT>(num_items, identity, initial_value, Equality());\n    TestOp<unsigned int, InputT>( num_items, identity, initial_value, Mod2Equality());\n}\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename InputT,\n    typename OutputT>\nvoid TestSize(\n    int         num_items,\n    OutputT     identity,\n    OutputT     initial_value)\n{\n    if (num_items < 0)\n    {\n        TestKeyTAndEqualityOp<InputT>(0,        identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(1,        identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(100,      identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(10000,    identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(1000000,  identity, initial_value);\n    }\n    else\n    {\n        TestKeyTAndEqualityOp<InputT>(num_items, identity, initial_value);\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_VALUE_TYPES types 0:1:2:3:4:5\n\n#if TEST_VALUE_TYPES == 0\n\n    // Test different input+output data types\n    TestSize<unsigned char>(num_items, (int)0, (int)99);\n\n    // Test same input+output data types\n    TestSize<unsigned char>(num_items, (unsigned char)0, (unsigned char)99);\n    TestSize<signed char>(num_items, (char)0, (char)99);\n\n#elif TEST_VALUE_TYPES == 1\n\n    TestSize<unsigned short>(num_items, (unsigned short)0, (unsigned short)99);\n    TestSize<unsigned int>(num_items, (unsigned int)0, (unsigned int)99);\n    TestSize<unsigned long long>(num_items,\n                                 (unsigned long long)0,\n                                 (unsigned long long)99);\n#elif TEST_VALUE_TYPES == 2\n\n    TestSize<uchar2>(num_items, make_uchar2(0, 0), make_uchar2(17, 21));\n    TestSize<char2>(num_items, make_char2(0, 0), make_char2(17, 21));\n    TestSize<ushort2>(num_items, make_ushort2(0, 0), make_ushort2(17, 21));\n\n#elif TEST_VALUE_TYPES == 3\n\n    TestSize<uint2>(num_items, make_uint2(0, 0), make_uint2(17, 21));\n    TestSize<ulonglong2>(num_items,\n                         make_ulonglong2(0, 0),\n                         make_ulonglong2(17, 21));\n    TestSize<uchar4>(num_items,\n                     make_uchar4(0, 0, 0, 0),\n                     make_uchar4(17, 21, 32, 85));\n\n#elif TEST_VALUE_TYPES == 4\n\n    TestSize<char4>(num_items,\n                    make_char4(0, 0, 0, 0),\n                    make_char4(17, 21, 32, 85));\n\n    TestSize<ushort4>(num_items,\n                      make_ushort4(0, 0, 0, 0),\n                      make_ushort4(17, 21, 32, 85));\n    TestSize<uint4>(num_items,\n                    make_uint4(0, 0, 0, 0),\n                    make_uint4(17, 21, 32, 85));\n\n#elif TEST_VALUE_TYPES == 5\n\n    TestSize<ulonglong4>(num_items,\n                         make_ulonglong4(0, 0, 0, 0),\n                         make_ulonglong4(17, 21, 32, 85));\n\n    TestSize<TestFoo>(num_items,\n                      TestFoo::MakeTestFoo(0, 0, 0, 0),\n                      TestFoo::MakeTestFoo(std::numeric_limits<TestFoo::x_t>::max(),\n                                           std::numeric_limits<TestFoo::y_t>::max(),\n                                           std::numeric_limits<TestFoo::z_t>::max(),\n                                           std::numeric_limits<TestFoo::w_t>::max()));\n\n    TestSize<TestBar>(num_items, \n                      TestBar(0, 0), \n                      TestBar(std::numeric_limits<long long>::max(), \n                              std::numeric_limits<int>::max()));\n\n#endif\n\n    return 0;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2021 NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceScan utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <limits>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\ndouble                  g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\nenum AliasMode\n{\n  AliasNone,  // output is allocated\n  AliasKeys,  // output is an alias of input keys\n  AliasValues // output is an alias of input values\n};\n\n\n/**\n * \\brief WrapperFunctor (for precluding test-specialized dispatch to *Sum variants)\n */\ntemplate<typename OpT>\nstruct WrapperFunctor\n{\n    OpT op;\n\n    WrapperFunctor(OpT op) : op(op) {}\n\n    template <typename T, typename U>\n    __host__ __device__ __forceinline__ auto operator()(const T &a, const U &b) const\n      -> decltype(op(a, b))\n    {\n        return static_cast<T>(op(a, b));\n    }\n};\n\n/**\n * \\brief DivideByFiveFunctor (used by TestIterator)\n */\ntemplate<typename OutputT>\nstruct DivideByFiveFunctor\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ OutputT operator()(const T &a) const\n    {\n        return static_cast<OutputT>(a / 5);\n    }\n};\n\n/**\n * \\brief Mod2Equality (used for non-bool keys to make keys more likely to equal each other)\n */\nstruct Mod2Equality\n{\n    template <typename T>\n    __host__ __device__ __forceinline__ T operator()(const T &a, const T &b) const\n    {\n        return (a % 2) == (b % 2);\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceScan entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to exclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT, typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename ScanOpT, typename InitialValueT, typename OffsetT, typename EqualityOpT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    IsPrimitiveT          /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    hipError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    ScanOpT               scan_op,\n    InitialValueT         initial_value,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::ExclusiveScanByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, scan_op, initial_value, num_items, equality_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to exclusive sum entrypoint\n */\ntemplate <typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename InitialValueT, typename OffsetT, typename EqualityOpT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    Int2Type<true>        /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    hipError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    Sum                   /*scan_op*/,\n    InitialValueT         /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::ExclusiveSumByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, num_items, equality_op);\n    }\n    return error;\n}\n\n\n/**\n * Dispatch to inclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT, typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename ScanOpT, typename OffsetT, typename EqualityOpT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    IsPrimitiveT          /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    hipError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    ScanOpT               scan_op,\n    NullType              /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::InclusiveScanByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, scan_op, num_items, equality_op);\n    }\n    return error;\n}\n\n/**\n * Dispatch to inclusive sum entrypoint\n */\ntemplate <typename KeysInputIteratorT, typename ValuesInputIteratorT, typename ValuesOutputIteratorT, typename OffsetT, typename EqualityOpT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>         /*dispatch_to*/,\n    Int2Type<true>        /*is_primitive*/,\n    int                   timing_timing_iterations,\n    size_t                */*d_temp_storage_bytes*/,\n    hipError_t           */*d_cdp_error*/,\n\n    void*                 d_temp_storage,\n    size_t&               temp_storage_bytes,\n    KeysInputIteratorT    d_keys_in,\n    ValuesInputIteratorT  d_values_in,\n    ValuesOutputIteratorT d_values_out,\n    Sum                   /*scan_op*/,\n    NullType              /*initial_value*/,\n    OffsetT               num_items,\n    EqualityOpT           equality_op)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceScan::InclusiveSumByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_values_out, num_items, equality_op);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceScan\n */\ntemplate <int CubBackend,\n          typename IsPrimitiveT,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename ValuesOutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT,\n          typename EqualityOpT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  IsPrimitiveT         is_primitive,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                 *d_temp_storage,\n                                  size_t                temp_storage_bytes,\n                                  KeysInputIteratorT    d_keys_in,\n                                  ValuesInputIteratorT  d_values_in,\n                                  ValuesOutputIteratorT d_values_out,\n                                  ScanOpT               scan_op,\n                                  InitialValueT         initial_value,\n                                  OffsetT               num_items,\n                                  EqualityOpT           equality_op)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          is_primitive,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_values_in,\n                          d_values_out,\n                          scan_op,\n                          initial_value,\n                          num_items,\n                          equality_op);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename IsPrimitiveT,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename ValuesOutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT,\n          typename EqualityOpT>\nhipError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     IsPrimitiveT is_primitive,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     hipError_t *d_cdp_error,\n\n                     void                 *d_temp_storage,\n                     size_t               &temp_storage_bytes,\n                     KeysInputIteratorT    d_keys_in,\n                     ValuesInputIteratorT  d_values_in,\n                     ValuesOutputIteratorT d_values_out,\n                     ScanOpT               scan_op,\n                     InitialValueT         initial_value,\n                     OffsetT               num_items,\n                     EqualityOpT           equality_op)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              IsPrimitiveT,\n                              KeysInputIteratorT,\n                              ValuesInputIteratorT,\n                              ValuesOutputIteratorT,\n                              ScanOpT,\n                              InitialValueT,\n                              OffsetT,\n                              EqualityOpT>,\n            Int2Type<CUB>{},\n            is_primitive,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_values_in,\n            d_values_out,\n            scan_op,\n            initial_value,\n            num_items,\n            equality_op);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    GenMode      gen_mode,\n    T            *h_in,\n    int          num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n/**\n * Solve exclusive-scan problem\n */\ntemplate <\n    typename        KeysInputIteratorT,\n    typename        ValuesInputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Solve(\n    KeysInputIteratorT    h_keys_in,\n    ValuesInputIteratorT  h_values_in,\n    OutputT               *h_reference,\n    int                   num_items,\n    ScanOpT               scan_op,\n    InitialValueT         initial_value,\n    EqualityOpT           equality_op)\n{\n    using ValueT = hipcub::detail::value_t<ValuesInputIteratorT>;\n    using AccumT = hipcub::detail::accumulator_t<ScanOpT, InitialValueT, ValueT>;\n\n    if (num_items > 0)\n    {\n        for (int i = 0; i < num_items;) {\n            AccumT val         = static_cast<AccumT>(h_values_in[i]);\n            h_reference[i]     = initial_value;\n            AccumT inclusive   = static_cast<AccumT>(scan_op(initial_value, val));\n\n            ++i;\n\n            for (; i < num_items && equality_op(h_keys_in[i - 1], h_keys_in[i]); ++i)\n            {\n                val = static_cast<AccumT>(h_values_in[i]);\n                h_reference[i] = static_cast<OutputT>(inclusive);\n                inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n            }\n        }\n    }\n}\n\n\n/**\n * Solve inclusive-scan problem\n */\ntemplate <\n    typename        KeysInputIteratorT,\n    typename        ValuesInputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        EqualityOpT>\nvoid Solve(\n    KeysInputIteratorT    h_keys_in,\n    ValuesInputIteratorT  h_values_in,\n    OutputT               *h_reference,\n    int                   num_items,\n    ScanOpT               scan_op,\n    NullType              /*initial_value*/,\n    EqualityOpT           equality_op)\n{\n    using ValueT = hipcub::detail::value_t<ValuesInputIteratorT>;\n    using AccumT = hipcub::detail::accumulator_t<ScanOpT, ValueT, ValueT>;\n\n    if (num_items > 0)\n    {\n        for (int i = 0; i < num_items;) {\n            AccumT inclusive    = h_values_in[i];\n            h_reference[i]      = static_cast<OutputT>(inclusive);\n\n            ++i;\n\n            for (; i < num_items && equality_op(h_keys_in[i - 1], h_keys_in[i]); ++i)\n            {\n                AccumT val = h_values_in[i];\n                inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n                h_reference[i] = static_cast<OutputT>(inclusive);\n            }\n        }\n    }\n}\n\ntemplate<typename OutputT, typename DeviceInputIteratorT, bool InPlace>\nstruct AllocateOutput {\n    static void run(OutputT *&d_out, DeviceInputIteratorT, int num_items) {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_items));\n    }\n};\n\ntemplate<typename OutputT>\nstruct AllocateOutput<OutputT, OutputT *, true> {\n    static void run(OutputT *&d_out, OutputT *d_in, int /* num_items */) {\n        d_out = d_in;\n    }\n};\n\n/**\n * Test DeviceScan for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            KeysInputIteratorT,\n    typename            ValuesInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT,\n    typename            EqualityOpT,\n    AliasMode           Mode=AliasNone>\nvoid Test(\n    KeysInputIteratorT      d_keys_in,\n    ValuesInputIteratorT    d_values_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value,\n    EqualityOpT             equality_op)\n{\n    using KeyT = hipcub::detail::value_t<KeysInputIteratorT>;\n    using InputT = hipcub::detail::value_t<ValuesInputIteratorT>;\n\n    // Allocate device output array\n    OutputT *d_values_out = NULL;\n\n    if (Mode == AliasKeys)\n    {\n      AllocateOutput<OutputT, KeysInputIteratorT, Mode == AliasKeys>::run(\n        d_values_out,\n        d_keys_in,\n        num_items);\n    }\n    else\n    {\n      AllocateOutput<OutputT, ValuesInputIteratorT, Mode == AliasValues>::run(\n        d_values_out,\n        d_values_in,\n        num_items);\n    }\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,   sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_keys_in,\n        d_values_in,\n        d_values_out,\n        scan_op,\n        initial_value,\n        num_items,\n        equality_op));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    if (Mode == AliasNone)\n    {\n      CubDebugExit(hipMemset(d_values_out, 0, sizeof(OutputT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_keys_in,\n        d_values_in,\n        d_values_out,\n        scan_op,\n        initial_value,\n        num_items,\n        equality_op));\n\n    // Check for correctness (and display results, if specified)\n    const int compare = CompareDeviceResults(h_reference,\n                                             d_values_out,\n                                             num_items,\n                                             true,\n                                             g_verbose);\n\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n      // Performance\n      GpuTimer gpu_timer;\n      gpu_timer.Start();\n      CubDebugExit(Dispatch(Int2Type<BACKEND>(),\n          Int2Type<Traits<OutputT>::PRIMITIVE>(),\n          g_timing_iterations,\n          d_temp_storage_bytes,\n          d_cdp_error,\n          d_temp_storage,\n          temp_storage_bytes,\n          d_keys_in,\n          d_values_in,\n          d_values_out,\n          scan_op,\n          initial_value,\n          num_items,\n          equality_op));\n\n      gpu_timer.Stop();\n      float elapsed_millis = gpu_timer.ElapsedMillis();\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = giga_rate * (sizeof(InputT) + sizeof(OutputT));\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\",\n            avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (Mode == AliasNone)\n    {\n      if (d_values_out) \n      {\n        CubDebugExit(g_allocator.DeviceFree(d_values_out));\n      }\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceValues(KeysInputIteratorT d_keys_in,\n                       OutputT *d_values_in,\n                       OutputT *h_reference,\n                       int num_items,\n                       ScanOpT scan_op,\n                       InitialValueT initial_value,\n                       EqualityOpT equality_op)\n{\n  Test<BACKEND,\n       KeysInputIteratorT,\n       OutputT *,\n       OutputT,\n       ScanOpT,\n       InitialValueT,\n       EqualityOpT,\n       AliasValues>(d_keys_in,\n                    d_values_in,\n                    h_reference,\n                    num_items,\n                    scan_op,\n                    initial_value,\n                    equality_op);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceValues(KeysInputIteratorT,\n                       ValuesInputIteratorT,\n                       OutputT *,\n                       int,\n                       ScanOpT,\n                       InitialValueT,\n                       EqualityOpT)\n{}\n\ntemplate <Backend BACKEND,\n          typename T,\n          typename ValuesInputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceKeys(T *d_keys_in,\n                     ValuesInputIteratorT d_values_in,\n                     T *h_reference,\n                     int num_items,\n                     ScanOpT scan_op,\n                     InitialValueT initial_value,\n                     EqualityOpT equality_op)\n{\n  Test<BACKEND,\n       T *,\n       ValuesInputIteratorT,\n       T,\n       ScanOpT,\n       InitialValueT,\n       EqualityOpT,\n       AliasKeys>(d_keys_in,\n                  d_values_in,\n                  h_reference,\n                  num_items,\n                  scan_op,\n                  initial_value,\n                  equality_op);\n}\n\ntemplate <Backend BACKEND,\n          typename KeysInputIteratorT,\n          typename ValuesInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename EqualityOpT>\nvoid TestInplaceKeys(KeysInputIteratorT,\n                     ValuesInputIteratorT,\n                     OutputT *,\n                     int,\n                     ScanOpT,\n                     InitialValueT,\n                     EqualityOpT)\n{}\n\n/**\n * Test DeviceScan on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid TestPointer(\n    int             num_items,\n    GenMode         gen_mode,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    printf(\"\\nPointer %s %s hipcub::DeviceScan::%s %d items, %s->%s (%d->%d bytes) , gen-mode %s\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT),\n        (gen_mode == RANDOM) ? \"RANDOM\" : (gen_mode == INTEGER_SEED) ? \"SEQUENTIAL\" : \"HOMOGENOUS\");\n    fflush(stdout);\n\n    // Allocate host arrays\n    KeyT*       h_keys_in   = new KeyT[num_items];\n    InputT*     h_values_in = new InputT[num_items];\n    OutputT*    h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Initialize(gen_mode, h_keys_in, num_items);\n    Initialize(gen_mode, h_values_in, num_items);\n\n    // If the output type is primitive and the operator is cub::Sum, the test\n    // dispatcher throws away scan_op and initial_value for exclusive scan.\n    // Without an initial_value arg, the accumulator switches to the input value\n    // type.\n    // Do the same thing here:\n    if (Traits<OutputT>::PRIMITIVE &&\n        std::is_same<ScanOpT, hipcub::Sum>::value &&\n        !std::is_same<InitialValueT, NullType>::value)\n    {\n      Solve(h_keys_in, h_values_in, h_reference, num_items, hipcub::Sum{}, InputT{}, equality_op);\n    }\n    else\n    {\n      Solve(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n    }\n\n    // Allocate problem device arrays\n    KeyT *d_keys_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    InputT *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(InputT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemcpy(d_values_in, h_values_in, sizeof(InputT) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in,\n                  d_values_in,\n                  h_reference,\n                  num_items,\n                  scan_op,\n                  initial_value,\n                  equality_op);\n\n    // Test in/out values aliasing\n    TestInplaceValues<BACKEND>(d_keys_in,\n                               d_values_in, \n                               h_reference,\n                               num_items,\n                               scan_op,\n                               initial_value,\n                               equality_op);\n\n    CubDebugExit(hipMemcpy(d_values_in, h_values_in, sizeof(InputT) * num_items, hipMemcpyHostToDevice));\n\n    // Test keys/values aliasing (should go last, changes keys)\n    TestInplaceKeys<BACKEND>(d_keys_in,\n                             d_values_in,\n                             h_reference,\n                             num_items,\n                             scan_op,\n                             initial_value,\n                             equality_op);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_reference) delete[] h_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test DeviceScan on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid TestIterator(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    printf(\"\\nIterator %s %s hipcub::DeviceScan::%s %d items, %s->%s (%d->%d bytes)\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT));\n    fflush(stdout);\n\n    // Use a counting iterator followed by div as the keys\n    using CountingIterT = CountingInputIterator<int, int>;\n    CountingIterT h_keys_in_helper(0);\n    TransformInputIterator<KeyT, DivideByFiveFunctor<KeyT>, CountingIterT> h_keys_in(h_keys_in_helper, DivideByFiveFunctor<KeyT>());\n\n    // Use a constant iterator as the input\n    InputT val = InputT();\n    ConstantInputIterator<InputT, int> h_values_in(val);\n\n    // Allocate host arrays\n    OutputT*  h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Solve(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n\n    // Run Test\n    Test<BACKEND>(h_keys_in, h_values_in, h_reference, num_items, scan_op, initial_value, equality_op);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n    TestPointer<BACKEND, KeyT, InputT, OutputT>(  num_items, UNIFORM, scan_op, initial_value, equality_op);\n    TestPointer<BACKEND, KeyT, InputT, OutputT>(  num_items, RANDOM,  scan_op, initial_value, equality_op);\n    TestIterator<BACKEND, KeyT, InputT, OutputT>( num_items, scan_op, initial_value, equality_op);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT,\n    typename        EqualityOpT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value,\n    EqualityOpT     equality_op)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, InputT, OutputT>(num_items, scan_op, initial_value, equality_op);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, InputT, OutputT>(num_items, scan_op, initial_value, equality_op);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different operators\n */\ntemplate <typename KeyT, typename InputT, typename OutputT, typename EqualityOpT>\nvoid TestOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value,\n    EqualityOpT     equality_op)\n{\n    // Exclusive (use identity as initial value because it will dispatch to *Sum variants that don't take initial values)\n    Test<KeyT, InputT, OutputT>(num_items, hipcub::Sum(), identity, equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, hipcub::Max(), identity, equality_op);\n\n    // Exclusive (non-specialized, so we can test initial-value)\n    Test<KeyT, InputT, OutputT>(num_items, WrapperFunctor<hipcub::Sum>(hipcub::Sum()), initial_value, equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, WrapperFunctor<hipcub::Max>(hipcub::Max()), initial_value, equality_op);\n\n    // Inclusive (no initial value)\n    Test<KeyT, InputT, OutputT>(num_items, hipcub::Sum(), NullType(), equality_op);\n    Test<KeyT, InputT, OutputT>(num_items, hipcub::Max(), NullType(), equality_op);\n}\n\n/**\n * Test different key type and equality operator\n */\ntemplate <typename InputT, typename OutputT>\nvoid TestKeyTAndEqualityOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value)\n{\n    TestOp<bool, InputT>(num_items, identity, initial_value, Equality());\n    TestOp<unsigned int, InputT>( num_items, identity, initial_value, Mod2Equality());\n}\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename InputT,\n    typename OutputT>\nvoid TestSize(\n    int         num_items,\n    OutputT     identity,\n    OutputT     initial_value)\n{\n    if (num_items < 0)\n    {\n        TestKeyTAndEqualityOp<InputT>(0,        identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(1,        identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(100,      identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(10000,    identity, initial_value);\n        TestKeyTAndEqualityOp<InputT>(1000000,  identity, initial_value);\n    }\n    else\n    {\n        TestKeyTAndEqualityOp<InputT>(num_items, identity, initial_value);\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_VALUE_TYPES types 0:1:2:3:4:5\n\n#if TEST_VALUE_TYPES == 0\n\n    // Test different input+output data types\n    TestSize<unsigned char>(num_items, (int)0, (int)99);\n\n    // Test same input+output data types\n    TestSize<unsigned char>(num_items, (unsigned char)0, (unsigned char)99);\n    TestSize<signed char>(num_items, (char)0, (char)99);\n\n#elif TEST_VALUE_TYPES == 1\n\n    TestSize<unsigned short>(num_items, (unsigned short)0, (unsigned short)99);\n    TestSize<unsigned int>(num_items, (unsigned int)0, (unsigned int)99);\n    TestSize<unsigned long long>(num_items,\n                                 (unsigned long long)0,\n                                 (unsigned long long)99);\n#elif TEST_VALUE_TYPES == 2\n\n    TestSize<uchar2>(num_items, make_uchar2(0, 0), make_uchar2(17, 21));\n    TestSize<char2>(num_items, make_char2(0, 0), make_char2(17, 21));\n    TestSize<ushort2>(num_items, make_ushort2(0, 0), make_ushort2(17, 21));\n\n#elif TEST_VALUE_TYPES == 3\n\n    TestSize<uint2>(num_items, make_uint2(0, 0), make_uint2(17, 21));\n    TestSize<ulonglong2>(num_items,\n                         make_ulonglong2(0, 0),\n                         make_ulonglong2(17, 21));\n    TestSize<uchar4>(num_items,\n                     make_uchar4(0, 0, 0, 0),\n                     make_uchar4(17, 21, 32, 85));\n\n#elif TEST_VALUE_TYPES == 4\n\n    TestSize<char4>(num_items,\n                    make_char4(0, 0, 0, 0),\n                    make_char4(17, 21, 32, 85));\n\n    TestSize<ushort4>(num_items,\n                      make_ushort4(0, 0, 0, 0),\n                      make_ushort4(17, 21, 32, 85));\n    TestSize<uint4>(num_items,\n                    make_uint4(0, 0, 0, 0),\n                    make_uint4(17, 21, 32, 85));\n\n#elif TEST_VALUE_TYPES == 5\n\n    TestSize<ulonglong4>(num_items,\n                         make_ulonglong4(0, 0, 0, 0),\n                         make_ulonglong4(17, 21, 32, 85));\n\n    TestSize<TestFoo>(num_items,\n                      TestFoo::MakeTestFoo(0, 0, 0, 0),\n                      TestFoo::MakeTestFoo(std::numeric_limits<TestFoo::x_t>::max(),\n                                           std::numeric_limits<TestFoo::y_t>::max(),\n                                           std::numeric_limits<TestFoo::z_t>::max(),\n                                           std::numeric_limits<TestFoo::w_t>::max()));\n\n    TestSize<TestBar>(num_items, \n                      TestBar(0, 0), \n                      TestBar(std::numeric_limits<long long>::max(), \n                              std::numeric_limits<int>::max()));\n\n#endif\n\n    return 0;\n}\n", "source": "/kaggle/working/cub/test/test_device_scan_by_key.cu"}
{"input": "/******************************************************************************\n* Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n*\n* Redistribution and use in source and binary forms, with or without\n* modification, are permitted provided that the following conditions are met:\n*     * Redistributions of source code must retain the above copyright\n*       notice, this list of conditions and the following disclaimer.\n*     * Redistributions in binary form must reproduce the above copyright\n*       notice, this list of conditions and the following disclaimer in the\n*       documentation and/or other materials provided with the distribution.\n*     * Neither the name of the NVIDIA CORPORATION nor the\n*       names of its contributors may be used to endorse or promote products\n*       derived from this software without specific prior written permission.\n*\n* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*\n******************************************************************************/\n\n/******************************************************************************\n * Test of BlockHistogram utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <limits>\n#include <string>\n\n#include <cub/block/block_histogram.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int BINS,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          cub::BlockHistogramAlgorithm ALGORITHM,\n          typename T,\n          typename HistoCounter>\n__global__ void block_histogram_kernel(T *d_samples, HistoCounter *d_histogram)\n{\n  // Parameterize BlockHistogram type for our thread block\n  using block_histogram_t =\n    cub::BlockHistogram<T, BLOCK_THREADS, ITEMS_PER_THREAD, BINS, ALGORITHM>;\n\n  // Allocate temp storage in shared memory\n  __shared__ typename block_histogram_t::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T data[ITEMS_PER_THREAD];\n  cub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_samples, data);\n\n  // Test histo (writing directly to histogram buffer in global)\n  block_histogram_t(temp_storage).Histogram(data, d_histogram);\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          int Bins,\n          cub::BlockHistogramAlgorithm Algorithm,\n          typename SampleT>\nvoid block_histogram(thrust::device_vector<SampleT> &d_samples,\n                     thrust::device_vector<int> &d_histogram)\n{\n  block_histogram_kernel<Bins, ThreadsInBlock, ItemsPerThread, Algorithm>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(d_samples.data()),\n                            thrust::raw_pointer_cast(d_histogram.data()));\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n// %PARAM% TEST_BINS bins 32:256:1024\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t>;\nusing threads_in_block = c2h::enum_type_list<int, 32, 96, 128>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 5>;\nusing bins             = c2h::enum_type_list<int, TEST_BINS>;\nusing algorithms = c2h::enum_type_list<cub::BlockHistogramAlgorithm,\n                                       cub::BLOCK_HISTO_SORT,\n                                       cub::BLOCK_HISTO_ATOMIC>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using sample_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int bins = c2h::get<3, TestType>::value;\n  static constexpr int num_samples = threads_in_block * items_per_thread;\n  static constexpr cub::BlockHistogramAlgorithm algorithm =\n    c2h::get<4, TestType>::value;\n};\n\nCUB_TEST(\"Block histogram can be computed with uniform input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  const sample_t uniform_value =\n    static_cast<sample_t>(GENERATE_COPY(take(10, random(0, params::bins - 1))));\n\n  thrust::host_vector<sample_t> h_samples(params::num_samples, uniform_value);\n  thrust::host_vector<int> h_reference(params::bins);\n  h_reference[static_cast<std::size_t>(uniform_value)] = params::num_samples;\n\n  // Allocate problem device arrays\n  thrust::device_vector<sample_t> d_samples = h_samples;\n  thrust::device_vector<int> d_histogram(params::bins);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\ntemplate <typename SampleT>\nthrust::host_vector<int>\ncompute_host_reference(int bins, const thrust::host_vector<SampleT> &h_samples)\n{\n  thrust::host_vector<int> h_reference(bins);\n  for (const SampleT &sample : h_samples)\n  {\n    h_reference[sample]++;\n  }\n\n  return h_reference;\n}\n\nCUB_TEST(\"Block histogram can be computed with modulo input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  thrust::device_vector<int> d_histogram(params::bins);\n  thrust::device_vector<sample_t> d_samples(params::num_samples);\n\n  c2h::gen(c2h::modulo_t{params::bins}, d_samples);\n\n  thrust::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\nCUB_TEST(\"Block histogram can be computed with random input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  thrust::device_vector<int> d_histogram(params::bins);\n  thrust::device_vector<sample_t> d_samples(params::num_samples);\n\n  const sample_t min_bin = static_cast<sample_t>(0);\n  const sample_t max_bin = \n    static_cast<sample_t>(\n      std::min(static_cast<std::int32_t>(std::numeric_limits<sample_t>::max()),\n               static_cast<std::int32_t>(params::bins - 1)));\n\n  c2h::gen(CUB_SEED(10), d_samples, min_bin, max_bin);\n\n  thrust::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\n", "output": "/******************************************************************************\n* Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n*\n* Redistribution and use in source and binary forms, with or without\n* modification, are permitted provided that the following conditions are met:\n*     * Redistributions of source code must retain the above copyright\n*       notice, this list of conditions and the following disclaimer.\n*     * Redistributions in binary form must reproduce the above copyright\n*       notice, this list of conditions and the following disclaimer in the\n*       documentation and/or other materials provided with the distribution.\n*     * Neither the name of the NVIDIA CORPORATION nor the\n*       names of its contributors may be used to endorse or promote products\n*       derived from this software without specific prior written permission.\n*\n* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*\n******************************************************************************/\n\n/******************************************************************************\n * Test of BlockHistogram utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <limits>\n#include <string>\n\n#include <cub/block/block_histogram.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int BINS,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          hipcub::BlockHistogramAlgorithm ALGORITHM,\n          typename T,\n          typename HistoCounter>\n__global__ void block_histogram_kernel(T *d_samples, HistoCounter *d_histogram)\n{\n  // Parameterize BlockHistogram type for our thread block\n  using block_histogram_t =\n    hipcub::BlockHistogram<T, BLOCK_THREADS, ITEMS_PER_THREAD, BINS, ALGORITHM>;\n\n  // Allocate temp storage in shared memory\n  __shared__ typename block_histogram_t::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T data[ITEMS_PER_THREAD];\n  hipcub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_samples, data);\n\n  // Test histo (writing directly to histogram buffer in global)\n  block_histogram_t(temp_storage).Histogram(data, d_histogram);\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          int Bins,\n          hipcub::BlockHistogramAlgorithm Algorithm,\n          typename SampleT>\nvoid block_histogram(thrust::device_vector<SampleT> &d_samples,\n                     thrust::device_vector<int> &d_histogram)\n{\n  block_histogram_kernel<Bins, ThreadsInBlock, ItemsPerThread, Algorithm>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(d_samples.data()),\n                            thrust::raw_pointer_cast(d_histogram.data()));\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n// %PARAM% TEST_BINS bins 32:256:1024\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t>;\nusing threads_in_block = c2h::enum_type_list<int, 32, 96, 128>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 5>;\nusing bins             = c2h::enum_type_list<int, TEST_BINS>;\nusing algorithms = c2h::enum_type_list<hipcub::BlockHistogramAlgorithm,\n                                       hipcub::BLOCK_HISTO_SORT,\n                                       hipcub::BLOCK_HISTO_ATOMIC>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using sample_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int bins = c2h::get<3, TestType>::value;\n  static constexpr int num_samples = threads_in_block * items_per_thread;\n  static constexpr hipcub::BlockHistogramAlgorithm algorithm =\n    c2h::get<4, TestType>::value;\n};\n\nCUB_TEST(\"Block histogram can be computed with uniform input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  const sample_t uniform_value =\n    static_cast<sample_t>(GENERATE_COPY(take(10, random(0, params::bins - 1))));\n\n  thrust::host_vector<sample_t> h_samples(params::num_samples, uniform_value);\n  thrust::host_vector<int> h_reference(params::bins);\n  h_reference[static_cast<std::size_t>(uniform_value)] = params::num_samples;\n\n  // Allocate problem device arrays\n  thrust::device_vector<sample_t> d_samples = h_samples;\n  thrust::device_vector<int> d_histogram(params::bins);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\ntemplate <typename SampleT>\nthrust::host_vector<int>\ncompute_host_reference(int bins, const thrust::host_vector<SampleT> &h_samples)\n{\n  thrust::host_vector<int> h_reference(bins);\n  for (const SampleT &sample : h_samples)\n  {\n    h_reference[sample]++;\n  }\n\n  return h_reference;\n}\n\nCUB_TEST(\"Block histogram can be computed with modulo input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  thrust::device_vector<int> d_histogram(params::bins);\n  thrust::device_vector<sample_t> d_samples(params::num_samples);\n\n  c2h::gen(c2h::modulo_t{params::bins}, d_samples);\n\n  thrust::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\nCUB_TEST(\"Block histogram can be computed with random input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  thrust::device_vector<int> d_histogram(params::bins);\n  thrust::device_vector<sample_t> d_samples(params::num_samples);\n\n  const sample_t min_bin = static_cast<sample_t>(0);\n  const sample_t max_bin = \n    static_cast<sample_t>(\n      std::min(static_cast<std::int32_t>(std::numeric_limits<sample_t>::max()),\n               static_cast<std::int32_t>(params::bins - 1)));\n\n  c2h::gen(CUB_SEED(10), d_samples, min_bin, max_bin);\n\n  thrust::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread,\n                  params::threads_in_block,\n                  params::bins,\n                  params::algorithm>(d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_histogram.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceRunLengthEncode utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include <cstdio>\n#include <typeinfo>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool g_verbose          = false;\nint g_timing_iterations = 0;\nCachingDeviceAllocator g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n  CUB, // CUB method\n  CDP, // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n// Operation types\nenum RleMethod\n{\n  RLE, // Run length encode\n  NON_TRIVIAL,\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to run-length encode entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t Dispatch(Int2Type<RLE> /*method*/,\n                                                          Int2Type<CUB> /*dispatch_to*/,\n                                                          int timing_timing_iterations,\n                                                          size_t * /*d_temp_storage_bytes*/,\n                                                          cudaError_t * /*d_cdp_error*/,\n\n                                                          void *d_temp_storage,\n                                                          size_t &temp_storage_bytes,\n                                                          InputIteratorT d_in,\n                                                          UniqueOutputIteratorT d_unique_out,\n                                                          OffsetsOutputIteratorT /*d_offsets_out*/,\n                                                          LengthsOutputIteratorT d_lengths_out,\n                                                          NumRunsIterator d_num_runs,\n                                                          cub::Equality /*equality_op*/,\n                                                          OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceRunLengthEncode::Encode(d_temp_storage,\n                                          temp_storage_bytes,\n                                          d_in,\n                                          d_unique_out,\n                                          d_lengths_out,\n                                          d_num_runs,\n                                          num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to non-trivial runs entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t Dispatch(Int2Type<NON_TRIVIAL> /*method*/,\n                                                          Int2Type<CUB> /*dispatch_to*/,\n                                                          int timing_timing_iterations,\n                                                          size_t * /*d_temp_storage_bytes*/,\n                                                          cudaError_t * /*d_cdp_error*/,\n\n                                                          void *d_temp_storage,\n                                                          size_t &temp_storage_bytes,\n                                                          InputIteratorT d_in,\n                                                          UniqueOutputIteratorT /*d_unique_out*/,\n                                                          OffsetsOutputIteratorT d_offsets_out,\n                                                          LengthsOutputIteratorT d_lengths_out,\n                                                          NumRunsIterator d_num_runs,\n                                                          cub::Equality /*equality_op*/,\n                                                          OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceRunLengthEncode::NonTrivialRuns(d_temp_storage,\n                                                  temp_storage_bytes,\n                                                  d_in,\n                                                  d_offsets_out,\n                                                  d_lengths_out,\n                                                  d_num_runs,\n                                                  num_items);\n  }\n  return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceRunLengthEncode\n */\ntemplate <int RLE_METHOD,\n          int CubBackend,\n          typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename EqualityOp,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<RLE_METHOD> method,\n                                  Int2Type<CubBackend> cub_backend,\n                                  int timing_timing_iterations,\n                                  size_t *d_temp_storage_bytes,\n                                  cudaError_t *d_cdp_error,\n\n                                  void *d_temp_storage,\n                                  size_t temp_storage_bytes,\n                                  InputIteratorT d_in,\n                                  UniqueOutputIteratorT d_unique_out,\n                                  OffsetsOutputIteratorT d_offsets_out,\n                                  LengthsOutputIteratorT d_lengths_out,\n                                  NumRunsIterator d_num_runs,\n                                  cub::Equality equality_op,\n                                  OffsetT num_items)\n{\n  *d_cdp_error = Dispatch(method,\n                          cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_unique_out,\n                          d_offsets_out,\n                          d_lengths_out,\n                          d_num_runs,\n                          equality_op,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <int RLE_METHOD,\n          typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename EqualityOp,\n          typename OffsetT>\n__forceinline__ cudaError_t Dispatch(Int2Type<RLE_METHOD> method,\n                                     Int2Type<CDP> /*dispatch_to*/,\n                                     int timing_timing_iterations,\n                                     size_t *d_temp_storage_bytes,\n                                     cudaError_t *d_cdp_error,\n\n                                     void *d_temp_storage,\n                                     size_t &temp_storage_bytes,\n                                     InputIteratorT d_in,\n                                     UniqueOutputIteratorT d_unique_out,\n                                     OffsetsOutputIteratorT d_offsets_out,\n                                     LengthsOutputIteratorT d_lengths_out,\n                                     NumRunsIterator d_num_runs,\n                                     EqualityOp equality_op,\n                                     OffsetT num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval = thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n                         .doit(CDPDispatchKernel<RLE_METHOD,\n                                                 CUB,\n                                                 InputIteratorT,\n                                                 UniqueOutputIteratorT,\n                                                 OffsetsOutputIteratorT,\n                                                 LengthsOutputIteratorT,\n                                                 NumRunsIterator,\n                                                 EqualityOp,\n                                                 OffsetT>,\n                               method,\n                               Int2Type<CUB>{},\n                               timing_timing_iterations,\n                               d_temp_storage_bytes,\n                               d_cdp_error,\n                               d_temp_storage,\n                               temp_storage_bytes,\n                               d_in,\n                               d_unique_out,\n                               d_offsets_out,\n                               d_lengths_out,\n                               d_num_runs,\n                               equality_op,\n                               num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval, d_cdp_error, sizeof(cudaError_t) * 1, cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(int entropy_reduction, T *h_in, int num_items, int max_segment)\n{\n  unsigned int max_int = (unsigned int)-1;\n\n  int key = 0;\n  int i   = 0;\n  while (i < num_items)\n  {\n    // Select number of repeating occurrences for the current run\n    int repeat;\n    if (max_segment < 0)\n    {\n      repeat = num_items;\n    }\n    else if (max_segment < 2)\n    {\n      repeat = 1;\n    }\n    else\n    {\n      RandomBits(repeat, entropy_reduction);\n      repeat = (int)((double(repeat) * double(max_segment)) / double(max_int));\n      repeat = CUB_MAX(1, repeat);\n    }\n\n    int j = i;\n    while (j < CUB_MIN(i + repeat, num_items))\n    {\n      InitValue(INTEGER_SEED, h_in[j], key);\n      j++;\n    }\n\n    i = j;\n    key++;\n  }\n\n  if (g_verbose)\n  {\n    printf(\"Input:\\n\");\n    DisplayResults(h_in, num_items);\n    printf(\"\\n\\n\");\n  }\n}\n\n/**\n * Solve problem.  Returns total number of segments identified\n */\ntemplate <RleMethod RLE_METHOD,\n          typename InputIteratorT,\n          typename T,\n          typename OffsetT,\n          typename LengthT,\n          typename EqualityOp>\nint Solve(InputIteratorT h_in,\n          T *h_unique_reference,\n          OffsetT *h_offsets_reference,\n          LengthT *h_lengths_reference,\n          EqualityOp equality_op,\n          int num_items)\n{\n  if (num_items == 0)\n    return 0;\n\n  // First item\n  T previous     = h_in[0];\n  LengthT length = 1;\n  int num_runs   = 0;\n  int run_begin  = 0;\n\n  // Subsequent items\n  for (int i = 1; i < num_items; ++i)\n  {\n    if (!equality_op(previous, h_in[i]))\n    {\n      if ((RLE_METHOD != NON_TRIVIAL) || (length > 1))\n      {\n        h_unique_reference[num_runs]  = previous;\n        h_offsets_reference[num_runs] = run_begin;\n        h_lengths_reference[num_runs] = length;\n        num_runs++;\n      }\n      length    = 1;\n      run_begin = i;\n    }\n    else\n    {\n      length++;\n    }\n    previous = h_in[i];\n  }\n\n  if ((RLE_METHOD != NON_TRIVIAL) || (length > 1))\n  {\n    h_unique_reference[num_runs]  = previous;\n    h_offsets_reference[num_runs] = run_begin;\n    h_lengths_reference[num_runs] = length;\n    num_runs++;\n  }\n\n  return num_runs;\n}\n\n/**\n * Test DeviceRunLengthEncode for a given problem input\n */\ntemplate <RleMethod RLE_METHOD,\n          Backend BACKEND,\n          typename DeviceInputIteratorT,\n          typename T,\n          typename OffsetT,\n          typename LengthT,\n          typename EqualityOp>\nvoid Test(DeviceInputIteratorT d_in,\n          T *h_unique_reference,\n          OffsetT *h_offsets_reference,\n          LengthT *h_lengths_reference,\n          EqualityOp equality_op,\n          int num_runs,\n          int num_items)\n{\n  // Allocate device output arrays and number of segments\n  T *d_unique_out        = NULL;\n  LengthT *d_offsets_out = NULL;\n  OffsetT *d_lengths_out = NULL;\n  int *d_num_runs        = NULL;\n\n  if (RLE_METHOD == RLE)\n    CubDebugExit(g_allocator.DeviceAllocate((void **)&d_unique_out, sizeof(T) * num_items));\n  if (RLE_METHOD == NON_TRIVIAL)\n    CubDebugExit(g_allocator.DeviceAllocate((void **)&d_offsets_out, sizeof(OffsetT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_lengths_out, sizeof(LengthT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_num_runs, sizeof(int)));\n\n  // Allocate CDP device arrays\n  size_t *d_temp_storage_bytes = NULL;\n  cudaError_t *d_cdp_error     = NULL;\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_temp_storage_bytes, sizeof(size_t) * 1));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_cdp_error, sizeof(cudaError_t) * 1));\n\n  // Allocate temporary storage\n  void *d_temp_storage      = NULL;\n  size_t temp_storage_bytes = 0;\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        1,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n  CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n  // Clear device output arrays\n  if (RLE_METHOD == RLE)\n    CubDebugExit(cudaMemset(d_unique_out, 0, sizeof(T) * num_items));\n  if (RLE_METHOD == NON_TRIVIAL)\n    CubDebugExit(cudaMemset(d_offsets_out, 0, sizeof(OffsetT) * num_items));\n  CubDebugExit(cudaMemset(d_lengths_out, 0, sizeof(LengthT) * num_items));\n  CubDebugExit(cudaMemset(d_num_runs, 0, sizeof(int)));\n\n  // Run warmup/correctness iteration\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        1,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n\n  // Check for correctness (and display results, if specified)\n  int compare0 = 0;\n  int compare1 = 0;\n  int compare2 = 0;\n  int compare3 = 0;\n\n  if (RLE_METHOD == RLE)\n  {\n    compare0 = CompareDeviceResults(h_unique_reference, d_unique_out, num_runs, true, g_verbose);\n    printf(\"\\t Keys %s\\n\", compare0 ? \"FAIL\" : \"PASS\");\n  }\n\n  if (RLE_METHOD != RLE)\n  {\n    compare1 = CompareDeviceResults(h_offsets_reference, d_offsets_out, num_runs, true, g_verbose);\n    printf(\"\\t Offsets %s\\n\", compare1 ? \"FAIL\" : \"PASS\");\n  }\n\n  compare2 = CompareDeviceResults(h_lengths_reference, d_lengths_out, num_runs, true, g_verbose);\n  printf(\"\\t Lengths %s\\n\", compare2 ? \"FAIL\" : \"PASS\");\n\n  compare3 = CompareDeviceResults(&num_runs, d_num_runs, 1, true, g_verbose);\n  printf(\"\\t Count %s\\n\", compare3 ? \"FAIL\" : \"PASS\");\n\n  // Flush any stdout/stderr\n  fflush(stdout);\n  fflush(stderr);\n\n  // Performance\n  GpuTimer gpu_timer;\n  gpu_timer.Start();\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        g_timing_iterations,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n  gpu_timer.Stop();\n  float elapsed_millis = gpu_timer.ElapsedMillis();\n\n  // Display performance\n  if (g_timing_iterations > 0)\n  {\n    float avg_millis = elapsed_millis / g_timing_iterations;\n    float giga_rate  = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n    int bytes_moved  = (num_items * sizeof(T)) + (num_runs * (sizeof(OffsetT) + sizeof(LengthT)));\n    float giga_bandwidth = float(bytes_moved) / avg_millis / 1000.0f / 1000.0f;\n    printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\",\n           avg_millis,\n           giga_rate,\n           giga_bandwidth);\n  }\n  printf(\"\\n\\n\");\n\n  // Flush any stdout/stderr\n  fflush(stdout);\n  fflush(stderr);\n\n  // Cleanup\n  if (d_unique_out)\n    CubDebugExit(g_allocator.DeviceFree(d_unique_out));\n  if (d_offsets_out)\n    CubDebugExit(g_allocator.DeviceFree(d_offsets_out));\n  if (d_lengths_out)\n    CubDebugExit(g_allocator.DeviceFree(d_lengths_out));\n  if (d_num_runs)\n    CubDebugExit(g_allocator.DeviceFree(d_num_runs));\n  if (d_temp_storage_bytes)\n    CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n  if (d_cdp_error)\n    CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n  if (d_temp_storage)\n    CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n  // Correctness asserts\n  AssertEquals(0, compare0 | compare1 | compare2 | compare3);\n}\n\n/**\n * Test DeviceRunLengthEncode on pointer type\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestPointer(int num_items, int entropy_reduction, int max_segment)\n{\n  // Allocate host arrays\n  T *h_in                      = new T[num_items];\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  for (int i = 0; i < num_items; ++i)\n    InitValue(INTEGER_SEED, h_offsets_reference[i], 1);\n\n  // Initialize problem and solution\n  Equality equality_op;\n  Initialize(entropy_reduction, h_in, num_items, max_segment);\n\n  int num_runs = Solve<RLE_METHOD>(h_in,\n                                   h_unique_reference,\n                                   h_offsets_reference,\n                                   h_lengths_reference,\n                                   equality_op,\n                                   num_items);\n\n  printf(\"\\nPointer %s cub::%s on %d items, %d segments (avg run length %.3f), {%s key, %s offset, \"\n         \"%s length}, max_segment %d, entropy_reduction %d\\n\",\n         (RLE_METHOD == RLE)           ? \"DeviceRunLengthEncode::Encode\"\n         : (RLE_METHOD == NON_TRIVIAL) ? \"DeviceRunLengthEncode::NonTrivialRuns\"\n                                       : \"Other\",\n         (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n         num_items,\n         num_runs,\n         float(num_items) / num_runs,\n         typeid(T).name(),\n         typeid(OffsetT).name(),\n         typeid(LengthT).name(),\n         max_segment,\n         entropy_reduction);\n  fflush(stdout);\n\n  // Allocate problem device arrays\n  T *d_in = NULL;\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in, sizeof(T) * num_items));\n\n  // Initialize device input\n  CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(T) * num_items, cudaMemcpyHostToDevice));\n\n  // Run Test\n  Test<RLE_METHOD, BACKEND>(d_in,\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_runs,\n                            num_items);\n\n  // Cleanup\n  if (h_in)\n    delete[] h_in;\n  if (h_unique_reference)\n    delete[] h_unique_reference;\n  if (h_offsets_reference)\n    delete[] h_offsets_reference;\n  if (h_lengths_reference)\n    delete[] h_lengths_reference;\n  if (d_in)\n    CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n/**\n * Test on iterator type\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestIterator(int num_items, Int2Type<true> /*is_primitive*/)\n{\n  // Allocate host arrays\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  T one_val;\n  InitValue(INTEGER_SEED, one_val, 1);\n  ConstantInputIterator<T, int> h_in(one_val);\n\n  // Initialize problem and solution\n  Equality equality_op;\n  int num_runs = Solve<RLE_METHOD>(h_in,\n                                   h_unique_reference,\n                                   h_offsets_reference,\n                                   h_lengths_reference,\n                                   equality_op,\n                                   num_items);\n\n  printf(\"\\nIterator %s cub::%s on %d items, %d segments (avg run length %.3f), {%s key, %s \"\n         \"offset, %s length}\\n\",\n         (RLE_METHOD == RLE)           ? \"DeviceRunLengthEncode::Encode\"\n         : (RLE_METHOD == NON_TRIVIAL) ? \"DeviceRunLengthEncode::NonTrivialRuns\"\n                                       : \"Other\",\n         (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n         num_items,\n         num_runs,\n         float(num_items) / num_runs,\n         typeid(T).name(),\n         typeid(OffsetT).name(),\n         typeid(LengthT).name());\n  fflush(stdout);\n\n  // Run Test\n  Test<RLE_METHOD, BACKEND>(h_in,\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_runs,\n                            num_items);\n\n  // Cleanup\n  if (h_unique_reference)\n    delete[] h_unique_reference;\n  if (h_offsets_reference)\n    delete[] h_offsets_reference;\n  if (h_lengths_reference)\n    delete[] h_lengths_reference;\n}\n\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestIterator(int /*num_items*/, Int2Type<false> /*is_primitive*/)\n{}\n\n/**\n * Test different gen modes\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid Test(int num_items)\n{\n  // Test iterator (one run)\n  TestIterator<RLE_METHOD, BACKEND, T, OffsetT, LengthT>(num_items,\n                                                         Int2Type<Traits<T>::PRIMITIVE>());\n\n  // Evaluate different run lengths / segment sizes\n  const int max_seg_limit = CUB_MIN(num_items, 1 << 16);\n  const int max_seg_inc   = 4;\n  for (int max_segment = 1, entropy_reduction = 0; max_segment <= max_seg_limit;\n       max_segment <<= max_seg_inc, entropy_reduction++)\n  {\n    const int max_seg = CUB_MAX(1, max_segment);\n    TestPointer<RLE_METHOD, BACKEND, T, OffsetT, LengthT>(num_items, entropy_reduction, max_seg);\n  }\n}\n\n/**\n * Test different dispatch\n */\ntemplate <typename T, typename OffsetT, typename LengthT>\nvoid TestDispatch(int num_items)\n{\n#if TEST_CDP == 0\n  Test<RLE, CUB, T, OffsetT, LengthT>(num_items);\n  Test<NON_TRIVIAL, CUB, T, OffsetT, LengthT>(num_items);\n#elif TEST_CDP == 1\n  Test<RLE, CDP, T, OffsetT, LengthT>(num_items);\n  Test<NON_TRIVIAL, CDP, T, OffsetT, LengthT>(num_items);\n#endif\n}\n\n/**\n * Test different input sizes\n */\ntemplate <typename T, typename OffsetT, typename LengthT>\nvoid TestSize(int num_items)\n{\n  if (num_items < 0)\n  {\n    TestDispatch<T, OffsetT, LengthT>(0);\n    TestDispatch<T, OffsetT, LengthT>(1);\n    TestDispatch<T, OffsetT, LengthT>(100);\n    TestDispatch<T, OffsetT, LengthT>(10000);\n    TestDispatch<T, OffsetT, LengthT>(1000000);\n  }\n  else\n  {\n    TestDispatch<T, OffsetT, LengthT>(num_items);\n  }\n}\n\n/**\n * @brief Test with NaNs as input to ensure we don't create an additional (invalid) run on the first\n * item when (item[0] == item[0]) is false.\n */\ntemplate <typename T>\nvoid TestNaNs()\n{\n  using OffsetT = int32_t;\n  using LengthT = int32_t;\n\n  const auto quiet_nan = std::numeric_limits<T>::quiet_NaN();\n\n  // Allocate host data\n  std::vector<T> h_in{quiet_nan, quiet_nan, quiet_nan};\n  const OffsetT num_items      = static_cast<OffsetT>(h_in.size());\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  // Initialize problem and solution\n  Equality equality_op;\n\n  // Get host-side data for verification\n  int num_runs = Solve<RLE>(h_in.data(),\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_items);\n\n  // Allocate problem device arrays\n  thrust::device_vector<T> d_in(num_items);\n  d_in = h_in;\n\n  // Run Test\n  Test<RLE, CUB>(thrust::raw_pointer_cast(d_in.data()),\n                 h_unique_reference,\n                 h_offsets_reference,\n                 h_lengths_reference,\n                 equality_op,\n                 num_runs,\n                 num_items);\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char **argv)\n{\n  int num_items = -1;\n\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"n\", num_items);\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--n=<input items> \"\n           \"[--i=<timing iterations> \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n  printf(\"\\n\");\n\n  // %PARAM% TEST_CDP cdp 0:1\n\n  // Run tests with leading/trailing NaNs, where (NaN == NaN) is false\n  TestNaNs<float>();\n  TestNaNs<double>();\n\n  // Test different input types\n  TestSize<signed char, int, int>(num_items);\n  TestSize<short, int, int>(num_items);\n  TestSize<int, int, int>(num_items);\n  TestSize<long, int, int>(num_items);\n  TestSize<long long, int, int>(num_items);\n  TestSize<float, int, int>(num_items);\n  TestSize<double, int, int>(num_items);\n\n  TestSize<uchar2, int, int>(num_items);\n  TestSize<uint2, int, int>(num_items);\n  TestSize<uint3, int, int>(num_items);\n  TestSize<uint4, int, int>(num_items);\n  TestSize<ulonglong4, int, int>(num_items);\n  TestSize<TestFoo, int, int>(num_items);\n  TestSize<TestBar, int, int>(num_items);\n\n  return 0;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceRunLengthEncode utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/thread/thread_operators.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include <cstdio>\n#include <typeinfo>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool g_verbose          = false;\nint g_timing_iterations = 0;\nCachingDeviceAllocator g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n  CUB, // CUB method\n  CDP, // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n// Operation types\nenum RleMethod\n{\n  RLE, // Run length encode\n  NON_TRIVIAL,\n};\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to run-length encode entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t Dispatch(Int2Type<RLE> /*method*/,\n                                                          Int2Type<CUB> /*dispatch_to*/,\n                                                          int timing_timing_iterations,\n                                                          size_t * /*d_temp_storage_bytes*/,\n                                                          hipError_t * /*d_cdp_error*/,\n\n                                                          void *d_temp_storage,\n                                                          size_t &temp_storage_bytes,\n                                                          InputIteratorT d_in,\n                                                          UniqueOutputIteratorT d_unique_out,\n                                                          OffsetsOutputIteratorT /*d_offsets_out*/,\n                                                          LengthsOutputIteratorT d_lengths_out,\n                                                          NumRunsIterator d_num_runs,\n                                                          hipcub::Equality /*equality_op*/,\n                                                          OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceRunLengthEncode::Encode(d_temp_storage,\n                                          temp_storage_bytes,\n                                          d_in,\n                                          d_unique_out,\n                                          d_lengths_out,\n                                          d_num_runs,\n                                          num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to non-trivial runs entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t Dispatch(Int2Type<NON_TRIVIAL> /*method*/,\n                                                          Int2Type<CUB> /*dispatch_to*/,\n                                                          int timing_timing_iterations,\n                                                          size_t * /*d_temp_storage_bytes*/,\n                                                          hipError_t * /*d_cdp_error*/,\n\n                                                          void *d_temp_storage,\n                                                          size_t &temp_storage_bytes,\n                                                          InputIteratorT d_in,\n                                                          UniqueOutputIteratorT /*d_unique_out*/,\n                                                          OffsetsOutputIteratorT d_offsets_out,\n                                                          LengthsOutputIteratorT d_lengths_out,\n                                                          NumRunsIterator d_num_runs,\n                                                          hipcub::Equality /*equality_op*/,\n                                                          OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceRunLengthEncode::NonTrivialRuns(d_temp_storage,\n                                                  temp_storage_bytes,\n                                                  d_in,\n                                                  d_offsets_out,\n                                                  d_lengths_out,\n                                                  d_num_runs,\n                                                  num_items);\n  }\n  return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceRunLengthEncode\n */\ntemplate <int RLE_METHOD,\n          int CubBackend,\n          typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename EqualityOp,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<RLE_METHOD> method,\n                                  Int2Type<CubBackend> cub_backend,\n                                  int timing_timing_iterations,\n                                  size_t *d_temp_storage_bytes,\n                                  hipError_t *d_cdp_error,\n\n                                  void *d_temp_storage,\n                                  size_t temp_storage_bytes,\n                                  InputIteratorT d_in,\n                                  UniqueOutputIteratorT d_unique_out,\n                                  OffsetsOutputIteratorT d_offsets_out,\n                                  LengthsOutputIteratorT d_lengths_out,\n                                  NumRunsIterator d_num_runs,\n                                  hipcub::Equality equality_op,\n                                  OffsetT num_items)\n{\n  *d_cdp_error = Dispatch(method,\n                          cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_unique_out,\n                          d_offsets_out,\n                          d_lengths_out,\n                          d_num_runs,\n                          equality_op,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <int RLE_METHOD,\n          typename InputIteratorT,\n          typename UniqueOutputIteratorT,\n          typename OffsetsOutputIteratorT,\n          typename LengthsOutputIteratorT,\n          typename NumRunsIterator,\n          typename EqualityOp,\n          typename OffsetT>\n__forceinline__ hipError_t Dispatch(Int2Type<RLE_METHOD> method,\n                                     Int2Type<CDP> /*dispatch_to*/,\n                                     int timing_timing_iterations,\n                                     size_t *d_temp_storage_bytes,\n                                     hipError_t *d_cdp_error,\n\n                                     void *d_temp_storage,\n                                     size_t &temp_storage_bytes,\n                                     InputIteratorT d_in,\n                                     UniqueOutputIteratorT d_unique_out,\n                                     OffsetsOutputIteratorT d_offsets_out,\n                                     LengthsOutputIteratorT d_lengths_out,\n                                     NumRunsIterator d_num_runs,\n                                     EqualityOp equality_op,\n                                     OffsetT num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval = thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n                         .doit(CDPDispatchKernel<RLE_METHOD,\n                                                 CUB,\n                                                 InputIteratorT,\n                                                 UniqueOutputIteratorT,\n                                                 OffsetsOutputIteratorT,\n                                                 LengthsOutputIteratorT,\n                                                 NumRunsIterator,\n                                                 EqualityOp,\n                                                 OffsetT>,\n                               method,\n                               Int2Type<CUB>{},\n                               timing_timing_iterations,\n                               d_temp_storage_bytes,\n                               d_cdp_error,\n                               d_temp_storage,\n                               temp_storage_bytes,\n                               d_in,\n                               d_unique_out,\n                               d_offsets_out,\n                               d_lengths_out,\n                               d_num_runs,\n                               equality_op,\n                               num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval, d_cdp_error, sizeof(hipError_t) * 1, hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(int entropy_reduction, T *h_in, int num_items, int max_segment)\n{\n  unsigned int max_int = (unsigned int)-1;\n\n  int key = 0;\n  int i   = 0;\n  while (i < num_items)\n  {\n    // Select number of repeating occurrences for the current run\n    int repeat;\n    if (max_segment < 0)\n    {\n      repeat = num_items;\n    }\n    else if (max_segment < 2)\n    {\n      repeat = 1;\n    }\n    else\n    {\n      RandomBits(repeat, entropy_reduction);\n      repeat = (int)((double(repeat) * double(max_segment)) / double(max_int));\n      repeat = CUB_MAX(1, repeat);\n    }\n\n    int j = i;\n    while (j < CUB_MIN(i + repeat, num_items))\n    {\n      InitValue(INTEGER_SEED, h_in[j], key);\n      j++;\n    }\n\n    i = j;\n    key++;\n  }\n\n  if (g_verbose)\n  {\n    printf(\"Input:\\n\");\n    DisplayResults(h_in, num_items);\n    printf(\"\\n\\n\");\n  }\n}\n\n/**\n * Solve problem.  Returns total number of segments identified\n */\ntemplate <RleMethod RLE_METHOD,\n          typename InputIteratorT,\n          typename T,\n          typename OffsetT,\n          typename LengthT,\n          typename EqualityOp>\nint Solve(InputIteratorT h_in,\n          T *h_unique_reference,\n          OffsetT *h_offsets_reference,\n          LengthT *h_lengths_reference,\n          EqualityOp equality_op,\n          int num_items)\n{\n  if (num_items == 0)\n    return 0;\n\n  // First item\n  T previous     = h_in[0];\n  LengthT length = 1;\n  int num_runs   = 0;\n  int run_begin  = 0;\n\n  // Subsequent items\n  for (int i = 1; i < num_items; ++i)\n  {\n    if (!equality_op(previous, h_in[i]))\n    {\n      if ((RLE_METHOD != NON_TRIVIAL) || (length > 1))\n      {\n        h_unique_reference[num_runs]  = previous;\n        h_offsets_reference[num_runs] = run_begin;\n        h_lengths_reference[num_runs] = length;\n        num_runs++;\n      }\n      length    = 1;\n      run_begin = i;\n    }\n    else\n    {\n      length++;\n    }\n    previous = h_in[i];\n  }\n\n  if ((RLE_METHOD != NON_TRIVIAL) || (length > 1))\n  {\n    h_unique_reference[num_runs]  = previous;\n    h_offsets_reference[num_runs] = run_begin;\n    h_lengths_reference[num_runs] = length;\n    num_runs++;\n  }\n\n  return num_runs;\n}\n\n/**\n * Test DeviceRunLengthEncode for a given problem input\n */\ntemplate <RleMethod RLE_METHOD,\n          Backend BACKEND,\n          typename DeviceInputIteratorT,\n          typename T,\n          typename OffsetT,\n          typename LengthT,\n          typename EqualityOp>\nvoid Test(DeviceInputIteratorT d_in,\n          T *h_unique_reference,\n          OffsetT *h_offsets_reference,\n          LengthT *h_lengths_reference,\n          EqualityOp equality_op,\n          int num_runs,\n          int num_items)\n{\n  // Allocate device output arrays and number of segments\n  T *d_unique_out        = NULL;\n  LengthT *d_offsets_out = NULL;\n  OffsetT *d_lengths_out = NULL;\n  int *d_num_runs        = NULL;\n\n  if (RLE_METHOD == RLE)\n    CubDebugExit(g_allocator.DeviceAllocate((void **)&d_unique_out, sizeof(T) * num_items));\n  if (RLE_METHOD == NON_TRIVIAL)\n    CubDebugExit(g_allocator.DeviceAllocate((void **)&d_offsets_out, sizeof(OffsetT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_lengths_out, sizeof(LengthT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_num_runs, sizeof(int)));\n\n  // Allocate CDP device arrays\n  size_t *d_temp_storage_bytes = NULL;\n  hipError_t *d_cdp_error     = NULL;\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_temp_storage_bytes, sizeof(size_t) * 1));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_cdp_error, sizeof(hipError_t) * 1));\n\n  // Allocate temporary storage\n  void *d_temp_storage      = NULL;\n  size_t temp_storage_bytes = 0;\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        1,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n  CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n  // Clear device output arrays\n  if (RLE_METHOD == RLE)\n    CubDebugExit(hipMemset(d_unique_out, 0, sizeof(T) * num_items));\n  if (RLE_METHOD == NON_TRIVIAL)\n    CubDebugExit(hipMemset(d_offsets_out, 0, sizeof(OffsetT) * num_items));\n  CubDebugExit(hipMemset(d_lengths_out, 0, sizeof(LengthT) * num_items));\n  CubDebugExit(hipMemset(d_num_runs, 0, sizeof(int)));\n\n  // Run warmup/correctness iteration\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        1,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n\n  // Check for correctness (and display results, if specified)\n  int compare0 = 0;\n  int compare1 = 0;\n  int compare2 = 0;\n  int compare3 = 0;\n\n  if (RLE_METHOD == RLE)\n  {\n    compare0 = CompareDeviceResults(h_unique_reference, d_unique_out, num_runs, true, g_verbose);\n    printf(\"\\t Keys %s\\n\", compare0 ? \"FAIL\" : \"PASS\");\n  }\n\n  if (RLE_METHOD != RLE)\n  {\n    compare1 = CompareDeviceResults(h_offsets_reference, d_offsets_out, num_runs, true, g_verbose);\n    printf(\"\\t Offsets %s\\n\", compare1 ? \"FAIL\" : \"PASS\");\n  }\n\n  compare2 = CompareDeviceResults(h_lengths_reference, d_lengths_out, num_runs, true, g_verbose);\n  printf(\"\\t Lengths %s\\n\", compare2 ? \"FAIL\" : \"PASS\");\n\n  compare3 = CompareDeviceResults(&num_runs, d_num_runs, 1, true, g_verbose);\n  printf(\"\\t Count %s\\n\", compare3 ? \"FAIL\" : \"PASS\");\n\n  // Flush any stdout/stderr\n  fflush(stdout);\n  fflush(stderr);\n\n  // Performance\n  GpuTimer gpu_timer;\n  gpu_timer.Start();\n  CubDebugExit(Dispatch(Int2Type<RLE_METHOD>(),\n                        Int2Type<BACKEND>(),\n                        g_timing_iterations,\n                        d_temp_storage_bytes,\n                        d_cdp_error,\n                        d_temp_storage,\n                        temp_storage_bytes,\n                        d_in,\n                        d_unique_out,\n                        d_offsets_out,\n                        d_lengths_out,\n                        d_num_runs,\n                        equality_op,\n                        num_items));\n  gpu_timer.Stop();\n  float elapsed_millis = gpu_timer.ElapsedMillis();\n\n  // Display performance\n  if (g_timing_iterations > 0)\n  {\n    float avg_millis = elapsed_millis / g_timing_iterations;\n    float giga_rate  = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n    int bytes_moved  = (num_items * sizeof(T)) + (num_runs * (sizeof(OffsetT) + sizeof(LengthT)));\n    float giga_bandwidth = float(bytes_moved) / avg_millis / 1000.0f / 1000.0f;\n    printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\",\n           avg_millis,\n           giga_rate,\n           giga_bandwidth);\n  }\n  printf(\"\\n\\n\");\n\n  // Flush any stdout/stderr\n  fflush(stdout);\n  fflush(stderr);\n\n  // Cleanup\n  if (d_unique_out)\n    CubDebugExit(g_allocator.DeviceFree(d_unique_out));\n  if (d_offsets_out)\n    CubDebugExit(g_allocator.DeviceFree(d_offsets_out));\n  if (d_lengths_out)\n    CubDebugExit(g_allocator.DeviceFree(d_lengths_out));\n  if (d_num_runs)\n    CubDebugExit(g_allocator.DeviceFree(d_num_runs));\n  if (d_temp_storage_bytes)\n    CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n  if (d_cdp_error)\n    CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n  if (d_temp_storage)\n    CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n  // Correctness asserts\n  AssertEquals(0, compare0 | compare1 | compare2 | compare3);\n}\n\n/**\n * Test DeviceRunLengthEncode on pointer type\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestPointer(int num_items, int entropy_reduction, int max_segment)\n{\n  // Allocate host arrays\n  T *h_in                      = new T[num_items];\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  for (int i = 0; i < num_items; ++i)\n    InitValue(INTEGER_SEED, h_offsets_reference[i], 1);\n\n  // Initialize problem and solution\n  Equality equality_op;\n  Initialize(entropy_reduction, h_in, num_items, max_segment);\n\n  int num_runs = Solve<RLE_METHOD>(h_in,\n                                   h_unique_reference,\n                                   h_offsets_reference,\n                                   h_lengths_reference,\n                                   equality_op,\n                                   num_items);\n\n  printf(\"\\nPointer %s cub::%s on %d items, %d segments (avg run length %.3f), {%s key, %s offset, \"\n         \"%s length}, max_segment %d, entropy_reduction %d\\n\",\n         (RLE_METHOD == RLE)           ? \"DeviceRunLengthEncode::Encode\"\n         : (RLE_METHOD == NON_TRIVIAL) ? \"DeviceRunLengthEncode::NonTrivialRuns\"\n                                       : \"Other\",\n         (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n         num_items,\n         num_runs,\n         float(num_items) / num_runs,\n         typeid(T).name(),\n         typeid(OffsetT).name(),\n         typeid(LengthT).name(),\n         max_segment,\n         entropy_reduction);\n  fflush(stdout);\n\n  // Allocate problem device arrays\n  T *d_in = NULL;\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in, sizeof(T) * num_items));\n\n  // Initialize device input\n  CubDebugExit(hipMemcpy(d_in, h_in, sizeof(T) * num_items, hipMemcpyHostToDevice));\n\n  // Run Test\n  Test<RLE_METHOD, BACKEND>(d_in,\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_runs,\n                            num_items);\n\n  // Cleanup\n  if (h_in)\n    delete[] h_in;\n  if (h_unique_reference)\n    delete[] h_unique_reference;\n  if (h_offsets_reference)\n    delete[] h_offsets_reference;\n  if (h_lengths_reference)\n    delete[] h_lengths_reference;\n  if (d_in)\n    CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n/**\n * Test on iterator type\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestIterator(int num_items, Int2Type<true> /*is_primitive*/)\n{\n  // Allocate host arrays\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  T one_val;\n  InitValue(INTEGER_SEED, one_val, 1);\n  ConstantInputIterator<T, int> h_in(one_val);\n\n  // Initialize problem and solution\n  Equality equality_op;\n  int num_runs = Solve<RLE_METHOD>(h_in,\n                                   h_unique_reference,\n                                   h_offsets_reference,\n                                   h_lengths_reference,\n                                   equality_op,\n                                   num_items);\n\n  printf(\"\\nIterator %s cub::%s on %d items, %d segments (avg run length %.3f), {%s key, %s \"\n         \"offset, %s length}\\n\",\n         (RLE_METHOD == RLE)           ? \"DeviceRunLengthEncode::Encode\"\n         : (RLE_METHOD == NON_TRIVIAL) ? \"DeviceRunLengthEncode::NonTrivialRuns\"\n                                       : \"Other\",\n         (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n         num_items,\n         num_runs,\n         float(num_items) / num_runs,\n         typeid(T).name(),\n         typeid(OffsetT).name(),\n         typeid(LengthT).name());\n  fflush(stdout);\n\n  // Run Test\n  Test<RLE_METHOD, BACKEND>(h_in,\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_runs,\n                            num_items);\n\n  // Cleanup\n  if (h_unique_reference)\n    delete[] h_unique_reference;\n  if (h_offsets_reference)\n    delete[] h_offsets_reference;\n  if (h_lengths_reference)\n    delete[] h_lengths_reference;\n}\n\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid TestIterator(int /*num_items*/, Int2Type<false> /*is_primitive*/)\n{}\n\n/**\n * Test different gen modes\n */\ntemplate <RleMethod RLE_METHOD, Backend BACKEND, typename T, typename OffsetT, typename LengthT>\nvoid Test(int num_items)\n{\n  // Test iterator (one run)\n  TestIterator<RLE_METHOD, BACKEND, T, OffsetT, LengthT>(num_items,\n                                                         Int2Type<Traits<T>::PRIMITIVE>());\n\n  // Evaluate different run lengths / segment sizes\n  const int max_seg_limit = CUB_MIN(num_items, 1 << 16);\n  const int max_seg_inc   = 4;\n  for (int max_segment = 1, entropy_reduction = 0; max_segment <= max_seg_limit;\n       max_segment <<= max_seg_inc, entropy_reduction++)\n  {\n    const int max_seg = CUB_MAX(1, max_segment);\n    TestPointer<RLE_METHOD, BACKEND, T, OffsetT, LengthT>(num_items, entropy_reduction, max_seg);\n  }\n}\n\n/**\n * Test different dispatch\n */\ntemplate <typename T, typename OffsetT, typename LengthT>\nvoid TestDispatch(int num_items)\n{\n#if TEST_CDP == 0\n  Test<RLE, CUB, T, OffsetT, LengthT>(num_items);\n  Test<NON_TRIVIAL, CUB, T, OffsetT, LengthT>(num_items);\n#elif TEST_CDP == 1\n  Test<RLE, CDP, T, OffsetT, LengthT>(num_items);\n  Test<NON_TRIVIAL, CDP, T, OffsetT, LengthT>(num_items);\n#endif\n}\n\n/**\n * Test different input sizes\n */\ntemplate <typename T, typename OffsetT, typename LengthT>\nvoid TestSize(int num_items)\n{\n  if (num_items < 0)\n  {\n    TestDispatch<T, OffsetT, LengthT>(0);\n    TestDispatch<T, OffsetT, LengthT>(1);\n    TestDispatch<T, OffsetT, LengthT>(100);\n    TestDispatch<T, OffsetT, LengthT>(10000);\n    TestDispatch<T, OffsetT, LengthT>(1000000);\n  }\n  else\n  {\n    TestDispatch<T, OffsetT, LengthT>(num_items);\n  }\n}\n\n/**\n * @brief Test with NaNs as input to ensure we don't create an additional (invalid) run on the first\n * item when (item[0] == item[0]) is false.\n */\ntemplate <typename T>\nvoid TestNaNs()\n{\n  using OffsetT = int32_t;\n  using LengthT = int32_t;\n\n  const auto quiet_nan = std::numeric_limits<T>::quiet_NaN();\n\n  // Allocate host data\n  std::vector<T> h_in{quiet_nan, quiet_nan, quiet_nan};\n  const OffsetT num_items      = static_cast<OffsetT>(h_in.size());\n  T *h_unique_reference        = new T[num_items];\n  OffsetT *h_offsets_reference = new OffsetT[num_items];\n  LengthT *h_lengths_reference = new LengthT[num_items];\n\n  // Initialize problem and solution\n  Equality equality_op;\n\n  // Get host-side data for verification\n  int num_runs = Solve<RLE>(h_in.data(),\n                            h_unique_reference,\n                            h_offsets_reference,\n                            h_lengths_reference,\n                            equality_op,\n                            num_items);\n\n  // Allocate problem device arrays\n  thrust::device_vector<T> d_in(num_items);\n  d_in = h_in;\n\n  // Run Test\n  Test<RLE, CUB>(thrust::raw_pointer_cast(d_in.data()),\n                 h_unique_reference,\n                 h_offsets_reference,\n                 h_lengths_reference,\n                 equality_op,\n                 num_runs,\n                 num_items);\n}\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char **argv)\n{\n  int num_items = -1;\n\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"n\", num_items);\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--n=<input items> \"\n           \"[--i=<timing iterations> \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n  printf(\"\\n\");\n\n  // %PARAM% TEST_CDP cdp 0:1\n\n  // Run tests with leading/trailing NaNs, where (NaN == NaN) is false\n  TestNaNs<float>();\n  TestNaNs<double>();\n\n  // Test different input types\n  TestSize<signed char, int, int>(num_items);\n  TestSize<short, int, int>(num_items);\n  TestSize<int, int, int>(num_items);\n  TestSize<long, int, int>(num_items);\n  TestSize<long long, int, int>(num_items);\n  TestSize<float, int, int>(num_items);\n  TestSize<double, int, int>(num_items);\n\n  TestSize<uchar2, int, int>(num_items);\n  TestSize<uint2, int, int>(num_items);\n  TestSize<uint3, int, int>(num_items);\n  TestSize<uint4, int, int>(num_items);\n  TestSize<ulonglong4, int, int>(num_items);\n  TestSize<TestFoo, int, int>(num_items);\n  TestSize<TestBar, int, int>(num_items);\n\n  return 0;\n}\n", "source": "/kaggle/working/cub/test/test_device_run_length_encode.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_segmented_sort.cuh>\n\n#include <nv/target>\n\n#include <test_util.h>\n\n#include <thrust/count.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n#include <thrust/reduce.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n\n#include <fstream>\n\n#define TEST_HALF_T !_NVHPC_CUDA\n\n#define TEST_BF_T !_NVHPC_CUDA\n\n#if TEST_HALF_T\n#include <cuda_fp16.h>\n#endif\n\n#if TEST_BF_T\n#include <cuda_bf16.h>\n#endif\n\nusing namespace cub;\n\ntemplate <typename T>\nstruct UnwrapHalfAndBfloat16\n{\n  using Type = T;\n};\n\n#if TEST_HALF_T\ntemplate <>\nstruct UnwrapHalfAndBfloat16<half_t>\n{\n  using Type = __half;\n};\n#endif\n\n#if TEST_BF_T\ntemplate <>\nstruct UnwrapHalfAndBfloat16<bfloat16_t>\n{\n  using Type = __nv_bfloat16;\n};\n#endif\n\nconstexpr static int MAX_ITERATIONS = 2;\n\n\nclass SizeGroupDescription\n{\npublic:\n  SizeGroupDescription(const int segments,\n                       const int segment_size)\n      : segments(segments)\n      , segment_size(segment_size)\n  {}\n\n  int segments {};\n  int segment_size {};\n};\n\ntemplate <typename KeyT>\nstruct SegmentChecker\n{\n  const KeyT *sorted_keys {};\n  const int *offsets {};\n\n  SegmentChecker(const KeyT *sorted_keys,\n                 const int *offsets)\n    : sorted_keys(sorted_keys)\n    , offsets(offsets)\n  {}\n\n  bool operator()(int segment_id)\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end = offsets[segment_id + 1];\n\n    int counter = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      if (sorted_keys[i] != static_cast<KeyT>(counter++))\n      {\n        return false;\n      }\n    }\n\n    return true;\n  }\n};\n\ntemplate <typename KeyT>\nstruct DescendingSegmentChecker\n{\n  const KeyT *sorted_keys{};\n  const int *offsets{};\n\n  DescendingSegmentChecker(const KeyT *sorted_keys,\n                           const int *offsets)\n      : sorted_keys(sorted_keys)\n      , offsets(offsets)\n  {}\n\n  bool operator()(int segment_id)\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end   = offsets[segment_id + 1];\n\n    int counter = 0;\n    for (int i = segment_end - 1; i >= segment_begin; i--)\n    {\n      if (sorted_keys[i] != static_cast<KeyT>(counter++))\n      {\n        return false;\n      }\n    }\n\n    return true;\n  }\n};\n\ntemplate <typename KeyT>\nstruct ReversedIota\n{\n  KeyT *data {};\n  const int *offsets {};\n\n  ReversedIota(KeyT *data,\n               const int *offsets)\n    : data(data)\n    , offsets(offsets)\n  {}\n\n  void operator()(int segment_id) const\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end = offsets[segment_id + 1];\n    const int segment_size = segment_end - segment_begin;\n\n    int count = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      data[i] = static_cast<KeyT>(segment_size - 1 - count++);\n    }\n  }\n};\n\n\ntemplate <typename KeyT>\nstruct Iota\n{\n  KeyT *data{};\n  const int *offsets{};\n\n  Iota(KeyT *data, const int *offsets)\n      : data(data)\n      , offsets(offsets)\n  {}\n\n  void operator()(int segment_id) const\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end   = offsets[segment_id + 1];\n\n    int count = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      data[i] = static_cast<KeyT>(count++);\n    }\n  }\n};\n\n\ntemplate <typename KeyT,\n          typename ValueT = cub::NullType>\nclass Input\n{\n  thrust::default_random_engine random_engine;\n  thrust::device_vector<int> d_segment_sizes;\n  thrust::device_vector<int> d_offsets;\n  thrust::host_vector<int> h_offsets;\n\n  using MaskedValueT = cub::detail::conditional_t<\n    std::is_same<ValueT, cub::NullType>::value, KeyT, ValueT>;\n\n  bool reverse {};\n  int num_items {};\n  thrust::device_vector<KeyT> d_keys;\n  thrust::device_vector<MaskedValueT> d_values;\n  thrust::host_vector<KeyT> h_keys;\n  thrust::host_vector<MaskedValueT> h_values;\n\npublic:\n  Input(bool reverse, const thrust::host_vector<int> &h_segment_sizes)\n      : d_segment_sizes(h_segment_sizes)\n      , d_offsets(d_segment_sizes.size() + 1)\n      , h_offsets(d_segment_sizes.size() + 1)\n      , reverse(reverse)\n      , num_items(static_cast<int>(\n          thrust::reduce(d_segment_sizes.begin(), d_segment_sizes.end())))\n      , d_keys(num_items)\n      , d_values(num_items)\n      , h_keys(num_items)\n      , h_values(num_items)\n  {\n    update();\n  }\n\n  Input(thrust::host_vector<int> &h_offsets)\n    : d_offsets(h_offsets)\n    , h_offsets(h_offsets)\n    , reverse(false)\n    , num_items(h_offsets.back())\n    , d_keys(num_items)\n    , d_values(num_items)\n  {\n  }\n\n  void shuffle()\n  {\n    thrust::shuffle(d_segment_sizes.begin(), d_segment_sizes.end(), random_engine);\n\n    update();\n  }\n\n  int get_num_items() const\n  {\n    return num_items;\n  }\n\n  int get_num_segments() const\n  {\n    return static_cast<unsigned int>(d_segment_sizes.size());\n  }\n\n  const KeyT *get_d_keys() const\n  {\n    return thrust::raw_pointer_cast(d_keys.data());\n  }\n\n  thrust::device_vector<KeyT> &get_d_keys_vec()\n  {\n    return d_keys;\n  }\n\n  thrust::device_vector<MaskedValueT> &get_d_values_vec()\n  {\n    return d_values;\n  }\n\n  KeyT *get_d_keys()\n  {\n    return thrust::raw_pointer_cast(d_keys.data());\n  }\n\n  const thrust::host_vector<int>& get_h_offsets()\n  {\n    return h_offsets;\n  }\n\n  MaskedValueT *get_d_values()\n  {\n    return thrust::raw_pointer_cast(d_values.data());\n  }\n\n  const int *get_d_offsets() const\n  {\n    return thrust::raw_pointer_cast(d_offsets.data());\n  }\n\n  template <typename T>\n  bool check_output_implementation(const T *keys_output)\n  {\n    const int *offsets = thrust::raw_pointer_cast(h_offsets.data());\n\n    if (reverse)\n    {\n      DescendingSegmentChecker<T> checker{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        if (!checker(i))\n        {\n          return false;\n        }\n      }\n    }\n    else\n    {\n      SegmentChecker<T> checker{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        if (!checker(i))\n        {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  bool check_output(const KeyT *d_keys_output,\n                    const MaskedValueT *d_values_output = nullptr)\n  {\n    KeyT *keys_output = thrust::raw_pointer_cast(h_keys.data());\n    MaskedValueT *values_output = thrust::raw_pointer_cast(h_values.data());\n\n    cudaMemcpy(keys_output,\n               d_keys_output,\n               sizeof(KeyT) * num_items,\n               cudaMemcpyDeviceToHost);\n\n    const bool keys_ok = check_output_implementation(keys_output);\n\n    if (std::is_same<ValueT, cub::NullType>::value || d_values_output == nullptr)\n    {\n      return keys_ok;\n    }\n\n    cudaMemcpy(values_output,\n               d_values_output,\n               sizeof(ValueT) * num_items,\n               cudaMemcpyDeviceToHost);\n\n    const bool values_ok = check_output_implementation(values_output);\n\n    return keys_ok && values_ok;\n  }\n\nprivate:\n  void update()\n  {\n    fill_offsets();\n    gen_keys();\n  }\n\n  void fill_offsets()\n  {\n    thrust::copy(d_segment_sizes.begin(), d_segment_sizes.end(), d_offsets.begin());\n    thrust::exclusive_scan(d_offsets.begin(), d_offsets.end(), d_offsets.begin(), 0u);\n    thrust::copy(d_offsets.begin(), d_offsets.end(), h_offsets.begin());\n  }\n\n  void gen_keys()\n  {\n    KeyT *keys_output = thrust::raw_pointer_cast(h_keys.data());\n    const int *offsets = thrust::raw_pointer_cast(h_offsets.data());\n\n    if (reverse)\n    {\n      Iota<KeyT> generator{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        generator(i);\n      }\n    }\n    else\n    {\n      ReversedIota<KeyT> generator{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        generator(i);\n      }\n    }\n\n    d_keys = h_keys;\n    d_values = d_keys;\n  }\n};\n\ntemplate <typename KeyT,\n          bool IsIntegralType = std::is_integral<KeyT>::value>\nclass InputDescription\n{\n  thrust::host_vector<int> segment_sizes;\n\npublic:\n  InputDescription& add(const SizeGroupDescription &group)\n  {\n    if (static_cast<std::size_t>(group.segment_size) <\n        static_cast<std::size_t>((std::numeric_limits<KeyT>::max)()))\n    {\n      for (int i = 0; i < group.segments; i++)\n      {\n        segment_sizes.push_back(group.segment_size);\n      }\n    }\n\n    return *this;\n  }\n\n  template <typename ValueT = cub::NullType>\n  Input<KeyT, ValueT> gen(bool reverse)\n  {\n    return Input<KeyT, ValueT>(reverse, segment_sizes);\n  }\n};\n\ntemplate <typename KeyT>\nclass InputDescription<KeyT, false>\n{\n  thrust::host_vector<int> segment_sizes;\n\npublic:\n  InputDescription& add(const SizeGroupDescription &group)\n  {\n    for (int i = 0; i < group.segments; i++)\n    {\n      segment_sizes.push_back(group.segment_size);\n    }\n\n    return *this;\n  }\n\n  template <typename ValueT = cub::NullType>\n  Input<KeyT, ValueT> gen(bool reverse)\n  {\n    return Input<KeyT, ValueT>(reverse, segment_sizes);\n  }\n};\n\n\ntemplate <typename WrappedKeyT,\n          typename ValueT>\nvoid Sort(bool pairs,\n          bool descending,\n          bool double_buffer,\n          bool stable_sort,\n\n          void *tmp_storage,\n          std::size_t &temp_storage_bytes,\n\n          WrappedKeyT *wrapped_input_keys,\n          WrappedKeyT *wrapped_output_keys,\n\n          ValueT *input_values,\n          ValueT *output_values,\n\n          int num_items,\n          int num_segments,\n          const int *d_offsets,\n\n          int *keys_selector = nullptr,\n          int *values_selector = nullptr)\n{\n  using KeyT = typename UnwrapHalfAndBfloat16<WrappedKeyT>::Type;\n\n  auto input_keys = reinterpret_cast<KeyT*>(wrapped_input_keys);\n  auto output_keys = reinterpret_cast<KeyT*>(wrapped_output_keys);\n\n  if (stable_sort)\n  {\n    if (pairs)\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          cub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(cub::DeviceSegmentedSort::StableSortPairsDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            keys_buffer,\n            values_buffer,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(cub::DeviceSegmentedSort::StableSortPairsDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            input_keys,\n            output_keys,\n            input_values,\n            output_values,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          cub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(\n            cub::DeviceSegmentedSort::StableSortPairs(tmp_storage,\n                                                      temp_storage_bytes,\n                                                      keys_buffer,\n                                                      values_buffer,\n                                                      num_items,\n                                                      num_segments,\n                                                      d_offsets,\n                                                      d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            cub::DeviceSegmentedSort::StableSortPairs(tmp_storage,\n                                                      temp_storage_bytes,\n                                                      input_keys,\n                                                      output_keys,\n                                                      input_values,\n                                                      output_values,\n                                                      num_items,\n                                                      num_segments,\n                                                      d_offsets,\n                                                      d_offsets + 1));\n        }\n      }\n    }\n    else\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(cub::DeviceSegmentedSort::StableSortKeysDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            keys_buffer,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(cub::DeviceSegmentedSort::StableSortKeysDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            input_keys,\n            output_keys,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(\n            cub::DeviceSegmentedSort::StableSortKeys(tmp_storage,\n                                                     temp_storage_bytes,\n                                                     keys_buffer,\n                                                     num_items,\n                                                     num_segments,\n                                                     d_offsets,\n                                                     d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            cub::DeviceSegmentedSort::StableSortKeys(tmp_storage,\n                                                     temp_storage_bytes,\n                                                     input_keys,\n                                                     output_keys,\n                                                     num_items,\n                                                     num_segments,\n                                                     d_offsets,\n                                                     d_offsets + 1));\n        }\n      }\n    }\n  }\n  else\n  {\n    if (pairs)\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          cub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(\n            cub::DeviceSegmentedSort::SortPairsDescending(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          keys_buffer,\n                                                          values_buffer,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            cub::DeviceSegmentedSort::SortPairsDescending(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          input_keys,\n                                                          output_keys,\n                                                          input_values,\n                                                          output_values,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          cub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(cub::DeviceSegmentedSort::SortPairs(tmp_storage,\n                                                           temp_storage_bytes,\n                                                           keys_buffer,\n                                                           values_buffer,\n                                                           num_items,\n                                                           num_segments,\n                                                           d_offsets,\n                                                           d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(cub::DeviceSegmentedSort::SortPairs(tmp_storage,\n                                                           temp_storage_bytes,\n                                                           input_keys,\n                                                           output_keys,\n                                                           input_values,\n                                                           output_values,\n                                                           num_items,\n                                                           num_segments,\n                                                           d_offsets,\n                                                           d_offsets + 1));\n        }\n      }\n    }\n    else\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(\n            cub::DeviceSegmentedSort::SortKeysDescending(tmp_storage,\n                                                         temp_storage_bytes,\n                                                         keys_buffer,\n                                                         num_items,\n                                                         num_segments,\n                                                         d_offsets,\n                                                         d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            cub::DeviceSegmentedSort::SortKeysDescending(tmp_storage,\n                                                         temp_storage_bytes,\n                                                         input_keys,\n                                                         output_keys,\n                                                         num_items,\n                                                         num_segments,\n                                                         d_offsets,\n                                                         d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(cub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          keys_buffer,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(cub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          input_keys,\n                                                          output_keys,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nstd::size_t Sort(bool pairs,\n                 bool descending,\n                 bool double_buffer,\n                 bool stable_sort,\n\n                 KeyT *input_keys,\n                 KeyT *output_keys,\n\n                 ValueT *input_values,\n                 ValueT *output_values,\n\n                 int num_items,\n                 int num_segments,\n                 const int *d_offsets,\n\n                 int *keys_selector   = nullptr,\n                 int *values_selector = nullptr)\n{\n  std::size_t temp_storage_bytes = 42ul;\n\n  Sort<KeyT, ValueT>(pairs,\n                     descending,\n                     double_buffer,\n                     stable_sort,\n                     nullptr,\n                     temp_storage_bytes,\n                     input_keys,\n                     output_keys,\n                     input_values,\n                     output_values,\n                     num_items,\n                     num_segments,\n                     d_offsets,\n                     keys_selector,\n                     values_selector);\n\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  Sort<KeyT, ValueT>(pairs,\n                     descending,\n                     double_buffer,\n                     stable_sort,\n                     d_temp_storage,\n                     temp_storage_bytes,\n                     input_keys,\n                     output_keys,\n                     input_values,\n                     output_values,\n                     num_items,\n                     num_segments,\n                     d_offsets,\n                     keys_selector,\n                     values_selector);\n\n  return temp_storage_bytes;\n}\n\n\nconstexpr bool keys_only = false;\nconstexpr bool pairs = true;\n\nconstexpr bool ascending = false;\nconstexpr bool descending = true;\n\nconstexpr bool pointers = false;\nconstexpr bool double_buffer = true;\n\nconstexpr bool unstable = false;\nconstexpr bool stable = true;\n\n\nvoid TestZeroSegments()\n{\n  // Type doesn't affect the escape logic, so it should be fine\n  // to test only one set of types here.\n\n  using KeyT = std::uint8_t;\n  using ValueT = std::uint64_t;\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffer: { pointers, double_buffer })\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          cub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffer,\n                             stable_sort,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             int{},\n                             int{},\n                             nullptr,\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          AssertEquals(keys_buffer.selector, 0);\n          AssertEquals(values_buffer.selector, 1);\n        }\n      }\n    }\n  }\n}\n\n\nvoid TestEmptySegments(int segments)\n{\n  // Type doesn't affect the escape logic, so it should be fine\n  // to test only one set of types here.\n\n  using KeyT = std::uint8_t;\n  using ValueT = std::uint64_t;\n\n  thrust::device_vector<int> offsets(segments + 1, int{});\n  const int *d_offsets = thrust::raw_pointer_cast(offsets.data());\n\n  for (bool sort_stable: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffer: { pointers, double_buffer })\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          cub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffer,\n                             sort_stable,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             int{},\n                             segments,\n                             d_offsets,\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          AssertEquals(keys_buffer.selector, 0);\n          AssertEquals(values_buffer.selector, 1);\n        }\n      }\n    }\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid TestSameSizeSegments(int segment_size,\n                          int segments,\n                          bool skip_values = false)\n{\n  const int num_items = segment_size * segments;\n\n  thrust::device_vector<int> offsets(segments + 1);\n  thrust::sequence(offsets.begin(),\n                   offsets.end(),\n                   int{},\n                   segment_size);\n\n  const int *d_offsets = thrust::raw_pointer_cast(offsets.data());\n\n  const KeyT target_key {1};\n  const ValueT target_value {42};\n\n  thrust::device_vector<KeyT> keys_input(num_items);\n  thrust::device_vector<KeyT> keys_output(num_items);\n\n  KeyT *d_keys_input  = thrust::raw_pointer_cast(keys_input.data());\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::device_vector<ValueT> values_input(num_items);\n  thrust::device_vector<ValueT> values_output(num_items);\n\n  thrust::host_vector<KeyT> host_keys(num_items);\n  thrust::host_vector<ValueT> host_values(num_items);\n\n  ValueT *d_values_input  = thrust::raw_pointer_cast(values_input.data());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      if (sort_pairs)\n      {\n        if (skip_values)\n        {\n          continue;\n        }\n      }\n\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffers: { pointers, double_buffer })\n        {\n          cub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          cub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          thrust::fill(keys_input.begin(), keys_input.end(), target_key);\n          thrust::fill(keys_output.begin(), keys_output.end(), KeyT{});\n\n          if (sort_pairs)\n          {\n            if (sort_buffers)\n            {\n              thrust::fill(values_input.begin(), values_input.end(), ValueT{});\n              thrust::fill(values_output.begin(), values_output.end(), target_value);\n            }\n            else\n            {\n              thrust::fill(values_input.begin(), values_input.end(), target_value);\n              thrust::fill(values_output.begin(), values_output.end(), ValueT{});\n            }\n          }\n\n          const std::size_t temp_storage_bytes =\n            Sort<KeyT, ValueT>(sort_pairs,\n                               sort_descending,\n                               sort_buffers,\n                               stable_sort,\n                               d_keys_input,\n                               d_keys_output,\n                               d_values_input,\n                               d_values_output,\n                               num_items,\n                               segments,\n                               d_offsets,\n                               &keys_buffer.selector,\n                               &values_buffer.selector);\n\n          // If temporary storage size is defined by extra keys storage\n          if (sort_buffers)\n          {\n            if (2 * segments * sizeof(unsigned int) < num_items * sizeof(KeyT))\n            {\n              std::size_t extra_temp_storage_bytes{};\n\n              Sort(sort_pairs,\n                   sort_descending,\n                   pointers,\n                   stable_sort,\n                   nullptr,\n                   extra_temp_storage_bytes,\n                   d_keys_input,\n                   d_keys_output,\n                   d_values_input,\n                   d_values_output,\n                   num_items,\n                   segments,\n                   d_offsets,\n                   &keys_buffer.selector,\n                   &values_buffer.selector);\n\n              AssertTrue(extra_temp_storage_bytes > temp_storage_bytes);\n            }\n          }\n\n          {\n            host_keys = keys_buffer.selector || !sort_buffers ? keys_output\n                                                              : keys_input;\n            const std::size_t items_selected =\n              thrust::count(host_keys.begin(), host_keys.end(), target_key);\n            AssertEquals(static_cast<int>(items_selected), num_items);\n          }\n\n          if (sort_pairs)\n          {\n            host_values = values_buffer.selector || !sort_buffers\n                            ? values_output\n                            : values_input;\n            const std::size_t items_selected =\n              thrust::count(host_values.begin(),\n                            host_values.end(),\n                            target_value);\n\n            AssertEquals(static_cast<int>(items_selected), num_items);\n          }\n        }\n      }\n    }\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid InputTest(bool sort_descending,\n               Input<KeyT, ValueT> &input)\n{\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs : { keys_only, pairs })\n    {\n      for (bool sort_buffers : {pointers, double_buffer})\n      {\n        for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n        {\n          thrust::fill(keys_output.begin(), keys_output.end(), KeyT{});\n          thrust::fill(values_output.begin(), values_output.end(), ValueT{});\n\n          cub::DoubleBuffer<KeyT> keys_buffer(input.get_d_keys(),\n                                              d_keys_output);\n          cub::DoubleBuffer<ValueT> values_buffer(input.get_d_values(),\n                                                  d_values_output);\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffers,\n                             stable_sort,\n                             input.get_d_keys(),\n                             d_keys_output,\n                             input.get_d_values(),\n                             d_values_output,\n                             input.get_num_items(),\n                             input.get_num_segments(),\n                             input.get_d_offsets(),\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          if (sort_buffers)\n          {\n            if (sort_pairs)\n            {\n              AssertTrue(input.check_output(keys_buffer.Current(),\n                                            values_buffer.Current()));\n            }\n            else\n            {\n              AssertTrue(input.check_output(keys_buffer.Current()));\n            }\n          }\n          else\n          {\n            if (sort_pairs)\n            {\n              AssertTrue(input.check_output(d_keys_output, d_values_output));\n            }\n            else\n            {\n              AssertTrue(input.check_output(d_keys_output));\n            }\n          }\n\n          input.shuffle();\n        }\n      }\n    }\n  }\n}\n\nstruct ComparisonPredicate\n{\n  template <typename T>\n  __host__ __device__ bool operator()(const T &lhs, const T &rhs) const\n  {\n    return lhs == rhs;\n  }\n\n  __host__ __device__ bool operator()(const half_t &lhs, const half_t &rhs) const\n  {\n    return lhs.raw() == rhs.raw();\n  }\n};\n\ntemplate <typename T>\nbool compare_two_outputs(const thrust::host_vector<int> &offsets,\n                         const thrust::host_vector<T> &lhs,\n                         const thrust::host_vector<T> &rhs)\n{\n  const auto num_segments = static_cast<unsigned int>(offsets.size() - 1);\n\n  for (std::size_t segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    auto lhs_begin = lhs.cbegin() + offsets[segment_id];\n    auto lhs_end = lhs.cbegin() + offsets[segment_id + 1];\n    auto rhs_begin = rhs.cbegin() + offsets[segment_id];\n\n    auto err = thrust::mismatch(lhs_begin, lhs_end, rhs_begin, ComparisonPredicate{});\n\n    if (err.first != lhs_end)\n    {\n      const auto idx = thrust::distance(lhs_begin, err.first);\n      const auto segment_size = std::distance(lhs_begin, lhs_end);\n\n      std::cerr << \"Mismatch in segment \" << segment_id\n                << \" at position \" << idx << \" / \" << segment_size\n                << \": \"\n                << static_cast<std::uint64_t>(lhs_begin[idx]) << \" vs \"\n                << static_cast<std::uint64_t>(rhs_begin[idx]) << \" (\"\n                << typeid(lhs_begin[idx]).name() << \")\" << std::endl;\n\n      return false;\n    }\n  }\n\n  return true;\n}\n\ntemplate <typename ValueT>\nvoid RandomizeInput(thrust::host_vector<bool> &h_keys,\n                    thrust::host_vector<ValueT> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<std::uint8_t>::max)()) > 128;\n    h_values[i] = RandomValue((std::numeric_limits<ValueT>::max)());\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid RandomizeInput(thrust::host_vector<KeyT> &h_keys,\n                    thrust::host_vector<ValueT> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<KeyT>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<ValueT>::max)());\n  }\n}\n\n#if TEST_HALF_T\nvoid RandomizeInput(thrust::host_vector<half_t> &h_keys,\n                    thrust::host_vector<std::uint32_t> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<int>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<std::uint32_t>::max)());\n  }\n}\n#endif\n\n#if TEST_BF_T\nvoid RandomizeInput(thrust::host_vector<bfloat16_t> &h_keys,\n                    thrust::host_vector<std::uint32_t> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<int>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<std::uint32_t>::max)());\n  }\n}\n#endif\n\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid HostReferenceSort(bool sort_pairs,\n                       bool sort_descending,\n                       unsigned int num_segments,\n                       const thrust::host_vector<int> &h_offsets,\n                       thrust::host_vector<KeyT> &h_keys,\n                       thrust::host_vector<ValueT> &h_values)\n{\n  for (unsigned int segment_i = 0;\n       segment_i < num_segments;\n       segment_i++)\n  {\n    const int segment_begin = h_offsets[segment_i];\n    const int segment_end   = h_offsets[segment_i + 1];\n\n    if (sort_pairs)\n    {\n      if (sort_descending)\n      {\n        thrust::stable_sort_by_key(h_keys.begin() + segment_begin,\n                                   h_keys.begin() + segment_end,\n                                   h_values.begin() + segment_begin,\n                                   thrust::greater<KeyT>{});\n      }\n      else\n      {\n        thrust::stable_sort_by_key(h_keys.begin() + segment_begin,\n                                   h_keys.begin() + segment_end,\n                                   h_values.begin() + segment_begin);\n      }\n    }\n    else\n    {\n      if (sort_descending)\n      {\n        thrust::stable_sort(h_keys.begin() + segment_begin,\n                            h_keys.begin() + segment_end,\n                            thrust::greater<KeyT>{});\n      }\n      else\n      {\n        thrust::stable_sort(h_keys.begin() + segment_begin,\n                            h_keys.begin() + segment_end);\n      }\n    }\n  }\n}\n\n\n#if STORE_ON_FAILURE\ntemplate <typename KeyT,\n          typename ValueT>\nvoid DumpInput(bool sort_pairs,\n               bool sort_descending,\n               bool sort_buffers,\n               Input<KeyT, ValueT> &input,\n               thrust::host_vector<KeyT> &h_keys,\n               thrust::host_vector<ValueT> &h_values)\n{\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  std::cout << \"sort pairs: \" << sort_pairs << \"\\n\";\n  std::cout << \"sort descending: \" << sort_descending << \"\\n\";\n  std::cout << \"sort buffers: \" << sort_buffers << \"\\n\";\n  std::cout << \"num_items: \" << input.get_num_items() << \"\\n\";\n  std::cout << \"num_segments: \" << input.get_num_segments() << \"\\n\";\n  std::cout << \"key type: \" << typeid(h_keys[0]).name() << \"\\n\";\n  std::cout << \"value type: \" << typeid(h_values[0]).name() << \"\\n\";\n  std::cout << \"offset type: \" << typeid(h_offsets[0]).name() << \"\\n\";\n\n  std::ofstream offsets_dump(\"offsets\", std::ios::binary);\n  offsets_dump.write(reinterpret_cast<const char *>(\n                       thrust::raw_pointer_cast(h_offsets.data())),\n                     sizeof(int) * h_offsets.size());\n\n  std::ofstream keys_dump(\"keys\", std::ios::binary);\n  keys_dump.write(reinterpret_cast<const char *>(\n                    thrust::raw_pointer_cast(h_keys.data())),\n                  sizeof(KeyT) * h_keys.size());\n\n  std::ofstream values_dump(\"values\", std::ios::binary);\n  values_dump.write(reinterpret_cast<const char *>(\n                      thrust::raw_pointer_cast(h_values.data())),\n                    sizeof(ValueT) * h_values.size());\n}\n#endif\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid InputTestRandom(Input<KeyT, ValueT> &input)\n{\n  thrust::host_vector<KeyT> h_keys_output(input.get_num_items());\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n\n  thrust::host_vector<ValueT> h_values_output(input.get_num_items());\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  thrust::host_vector<KeyT> h_keys(input.get_num_items());\n  thrust::host_vector<ValueT> h_values(input.get_num_items());\n\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffers: { pointers, double_buffer })\n        {\n          for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n          {\n            RandomizeInput(h_keys, h_values);\n\n#if STORE_ON_FAILURE\n            auto h_keys_backup = h_keys;\n            auto h_values_backup = h_values;\n#endif\n\n            input.get_d_keys_vec()   = h_keys;\n            input.get_d_values_vec() = h_values;\n\n            cub::DoubleBuffer<KeyT> keys_buffer(input.get_d_keys(), d_keys_output);\n            cub::DoubleBuffer<ValueT> values_buffer(input.get_d_values(), d_values_output);\n\n            Sort<KeyT, ValueT>(sort_pairs,\n                               sort_descending,\n                               sort_buffers,\n                               stable_sort,\n                               input.get_d_keys(),\n                               d_keys_output,\n                               input.get_d_values(),\n                               d_values_output,\n                               input.get_num_items(),\n                               input.get_num_segments(),\n                               input.get_d_offsets(),\n                               &keys_buffer.selector,\n                               &values_buffer.selector);\n\n            HostReferenceSort(sort_pairs,\n                              sort_descending,\n                              input.get_num_segments(),\n                              h_offsets,\n                              h_keys,\n                              h_values);\n\n            if (sort_buffers)\n            {\n              if (keys_buffer.selector)\n              {\n                h_keys_output = keys_output;\n              }\n              else\n              {\n                h_keys_output = input.get_d_keys_vec();\n              }\n\n              if (values_buffer.selector)\n              {\n                h_values_output = values_output;\n              }\n              else\n              {\n                h_values_output = input.get_d_values_vec();\n              }\n            }\n            else\n            {\n              h_keys_output = keys_output;\n              h_values_output = values_output;\n            }\n\n            const bool keys_ok =\n              compare_two_outputs(h_offsets, h_keys, h_keys_output);\n\n            const bool values_ok =\n              sort_pairs\n                ? compare_two_outputs(h_offsets, h_values, h_values_output)\n                : true;\n\n#if STORE_ON_FAILURE\n            if (!keys_ok || !values_ok)\n            {\n              DumpInput<KeyT, ValueT>(sort_pairs,\n                                      sort_descending,\n                                      sort_buffers,\n                                      input,\n                                      h_keys_backup,\n                                      h_values_backup);\n            }\n#endif\n\n            AssertTrue(keys_ok);\n            AssertTrue(values_ok);\n\n            input.shuffle();\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT,\n          bool IsSupportedType = std::is_integral<KeyT>::value>\nstruct EdgeTestDispatch\n{\n  // Edge cases that needs to be tested\n  const int empty_short_circuit_segment_size = 0;\n  const int copy_short_circuit_segment_size = 1;\n  const int swap_short_circuit_segment_size = 2;\n\n  const int a_few = 2;\n  const int a_bunch_of = 42;\n  const int a_lot_of = 420;\n\n  template <typename ActivePolicyT>\n  CUB_RUNTIME_FUNCTION __forceinline__ cudaError_t Invoke()\n  {\n    NV_IF_TARGET(NV_IS_HOST,\n      (using SmallAndMediumPolicyT =\n         typename ActivePolicyT::SmallAndMediumSegmentedSortPolicyT;\n       using LargeSegmentPolicyT = typename ActivePolicyT::LargeSegmentPolicy;\n\n       const int small_segment_max_segment_size =\n         SmallAndMediumPolicyT::SmallPolicyT::ITEMS_PER_TILE;\n\n       const int items_per_small_segment =\n         SmallAndMediumPolicyT::SmallPolicyT::ITEMS_PER_THREAD;\n\n       const int medium_segment_max_segment_size =\n         SmallAndMediumPolicyT::MediumPolicyT::ITEMS_PER_TILE;\n\n       const int single_thread_segment_size = items_per_small_segment;\n\n       const int large_cached_segment_max_segment_size =\n         LargeSegmentPolicyT::BLOCK_THREADS *\n         LargeSegmentPolicyT::ITEMS_PER_THREAD;\n\n       for (bool sort_descending : {ascending, descending}) {\n         Input<KeyT, ValueT> edge_cases =\n           InputDescription<KeyT>()\n             .add({a_lot_of, empty_short_circuit_segment_size})\n             .add({a_lot_of, copy_short_circuit_segment_size})\n             .add({a_lot_of, swap_short_circuit_segment_size})\n             .add({a_lot_of, swap_short_circuit_segment_size + 1})\n             .add({a_lot_of, swap_short_circuit_segment_size + 1})\n             .add({a_lot_of, single_thread_segment_size - 1})\n             .add({a_lot_of, single_thread_segment_size})\n             .add({a_lot_of, single_thread_segment_size + 1})\n             .add({a_lot_of, single_thread_segment_size * 2 - 1})\n             .add({a_lot_of, single_thread_segment_size * 2})\n             .add({a_lot_of, single_thread_segment_size * 2 + 1})\n             .add({a_bunch_of, small_segment_max_segment_size - 1})\n             .add({a_bunch_of, small_segment_max_segment_size})\n             .add({a_bunch_of, small_segment_max_segment_size + 1})\n             .add({a_bunch_of, medium_segment_max_segment_size - 1})\n             .add({a_bunch_of, medium_segment_max_segment_size})\n             .add({a_bunch_of, medium_segment_max_segment_size + 1})\n             .add({a_bunch_of, large_cached_segment_max_segment_size - 1})\n             .add({a_bunch_of, large_cached_segment_max_segment_size})\n             .add({a_bunch_of, large_cached_segment_max_segment_size + 1})\n             .add({a_few, large_cached_segment_max_segment_size * 2})\n             .add({a_few, large_cached_segment_max_segment_size * 3})\n             .add({a_few, large_cached_segment_max_segment_size * 5})\n             .template gen<ValueT>(sort_descending);\n\n         InputTest<KeyT, ValueT>(sort_descending, edge_cases);\n       }));\n\n    return cudaSuccess;\n  }\n};\n\ntemplate <typename KeyT,\n          typename ValueT>\nstruct EdgeTestDispatch<KeyT, ValueT, false>\n{\n  template <typename ActivePolicyT>\n  CUB_RUNTIME_FUNCTION __forceinline__ cudaError_t Invoke()\n  {\n    // Edge case test is using an optimized testing approach which is\n    // incompatible with duplicates. RandomTest is used for other types.\n    return cudaSuccess;\n  }\n};\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid EdgePatternsTest()\n{\n  int ptx_version = 0;\n  if (CubDebug(PtxVersion(ptx_version)))\n  {\n    return;\n  }\n\n  using MaxPolicyT =\n    typename cub::DeviceSegmentedSortPolicy<KeyT, ValueT>::MaxPolicy;\n  using EdgeTestDispatchT = EdgeTestDispatch<KeyT, ValueT>;\n  EdgeTestDispatchT dispatch;\n\n  MaxPolicyT::Invoke(ptx_version, dispatch);\n\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nInput<KeyT, ValueT> GenRandomInput(int max_items,\n                                   int min_segments,\n                                   int max_segments,\n                                   bool descending)\n{\n  int items_generated {};\n  const int segments_num = RandomValue(max_segments) + min_segments;\n\n  thrust::host_vector<int> segment_sizes;\n  segment_sizes.reserve(segments_num);\n\n  const int max_segment_size = 6000;\n\n  for (int segment_id = 0; segment_id < segments_num; segment_id++)\n  {\n    const int segment_size_raw = RandomValue(max_segment_size);\n    const int segment_size     = segment_size_raw > 0 ? segment_size_raw : 0;\n\n    if (segment_size + items_generated > max_items)\n    {\n      break;\n    }\n\n    items_generated += segment_size;\n    segment_sizes.push_back(segment_size);\n  }\n\n  return Input<KeyT, ValueT>{descending, segment_sizes};\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid RandomTest(int min_segments,\n                int max_segments)\n{\n  const int max_items = 10000000;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    Input<KeyT, ValueT> edge_cases = GenRandomInput<KeyT, ValueT>(max_items,\n                                                                  min_segments,\n                                                                  max_segments,\n                                                                  descending);\n\n    InputTestRandom(edge_cases);\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid Test()\n{\n  for (int segment_size: { 1, 1024, 24 * 1024 })\n  {\n    for (int segments: { 1, 1024 })\n    {\n      TestSameSizeSegments<KeyT, ValueT>(segment_size, segments);\n    }\n  }\n\n  RandomTest<KeyT, ValueT>(1 << 2, 1 << 8);\n  RandomTest<KeyT, ValueT>(1 << 9, 1 << 19);\n  EdgePatternsTest<KeyT, ValueT>();\n}\n\n\n#if TEST_CDP == 1\ntemplate <typename KeyT>\n__global__ void LauncherKernel(\n    void *tmp_storage,\n    std::size_t temp_storage_bytes,\n    const KeyT *in_keys,\n    KeyT *out_keys,\n    int num_items,\n    int num_segments,\n    const int *offsets)\n{\n  CubDebug(cub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                              temp_storage_bytes,\n                                              in_keys,\n                                              out_keys,\n                                              num_items,\n                                              num_segments,\n                                              offsets,\n                                              offsets + 1));\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid TestDeviceSideLaunch(Input<KeyT, ValueT> &input)\n{\n  thrust::host_vector<KeyT> h_keys_output(input.get_num_items());\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n\n  thrust::host_vector<ValueT> h_values_output(input.get_num_items());\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::host_vector<KeyT> h_keys(input.get_num_items());\n  thrust::host_vector<ValueT> h_values(input.get_num_items());\n\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    RandomizeInput(h_keys, h_values);\n\n    input.get_d_keys_vec()   = h_keys;\n    input.get_d_values_vec() = h_values;\n\n    const KeyT *d_input = input.get_d_keys();\n\n    std::size_t temp_storage_bytes{};\n    cub::DeviceSegmentedSort::SortKeys(nullptr,\n                                       temp_storage_bytes,\n                                       d_input,\n                                       d_keys_output,\n                                       input.get_num_items(),\n                                       input.get_num_segments(),\n                                       input.get_d_offsets(),\n                                       input.get_d_offsets() + 1);\n\n    thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n    std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n    LauncherKernel<KeyT><<<1, 1>>>(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_input,\n      d_keys_output,\n      input.get_num_items(),\n      input.get_num_segments(),\n      input.get_d_offsets());\n    CubDebugExit(cudaDeviceSynchronize());\n    CubDebugExit(cudaPeekAtLastError());\n\n    HostReferenceSort(false,\n                      false,\n                      input.get_num_segments(),\n                      h_offsets,\n                      h_keys,\n                      h_values);\n\n    h_keys_output = keys_output;\n\n    const bool keys_ok =\n      compare_two_outputs(h_offsets, h_keys, h_keys_output);\n\n    AssertTrue(keys_ok);\n\n    input.shuffle();\n  }\n}\n\ntemplate <typename KeyT>\nvoid TestDeviceSideLaunch(int min_segments, int max_segments)\n{\n  const int max_items = 10000000;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    Input<KeyT, KeyT> edge_cases =\n      GenRandomInput<KeyT, KeyT>(max_items,\n                                 min_segments,\n                                 max_segments,\n                                 descending);\n\n    TestDeviceSideLaunch(edge_cases);\n  }\n}\n\ntemplate <typename KeyT>\nvoid TestDeviceSideLaunch()\n{\n  TestDeviceSideLaunch<KeyT>(1 << 2, 1 << 8);\n  TestDeviceSideLaunch<KeyT>(1 << 9, 1 << 19);\n}\n\n#endif // TEST_CDP\n\nvoid TestUnspecifiedRanges()\n{\n  const std::size_t num_items = 1024 * 1024;\n  const std::size_t max_segments = 42;\n  const std::size_t avg_segment_size = num_items / max_segments;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    thrust::host_vector<int> h_offsets_begin;\n    thrust::host_vector<int> h_offsets_end;\n\n    h_offsets_begin.reserve(max_segments + 1);\n    h_offsets_end.reserve(max_segments + 1);\n\n    {\n      int offset = 0;\n\n      for (std::size_t sid = 0; sid < max_segments; sid++)\n      {\n        const int segment_size =\n          RandomValue(static_cast<int>(avg_segment_size));\n        const bool segment_is_utilized = RandomValue(100) > 60;\n\n        if (segment_is_utilized)\n        {\n          h_offsets_begin.push_back(offset);\n          h_offsets_end.push_back(offset + segment_size);\n        }\n\n        offset += segment_size;\n      }\n\n      if (h_offsets_begin.empty())\n      {\n        h_offsets_begin.push_back(avg_segment_size);\n        h_offsets_end.push_back(num_items);\n      }\n    }\n\n    thrust::device_vector<int> keys(num_items);\n    thrust::device_vector<int> values(num_items);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    thrust::device_vector<int> d_offsets_begin = h_offsets_begin;\n    thrust::device_vector<int> d_offsets_end = h_offsets_end;\n\n    thrust::device_vector<int> expected_keys = keys;\n    thrust::device_vector<int> expected_values = values;\n\n    const int num_segments = static_cast<int>(h_offsets_begin.size());\n\n    for (int sid = 0; sid < num_segments; sid++)\n    {\n      const int segment_begin = h_offsets_begin[sid];\n      const int segment_end = h_offsets_end[sid];\n\n      thrust::sort_by_key(expected_keys.begin() + segment_begin,\n                          expected_keys.begin() + segment_end,\n                          expected_values.begin() + segment_begin);\n    }\n\n    thrust::device_vector<int> result_keys = keys;\n    thrust::device_vector<int> result_values = values;\n\n    {\n      cub::DoubleBuffer<int> keys_buffer(\n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()));\n\n      cub::DoubleBuffer<int> values_buffer(\n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()));\n\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(cub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(cub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      for (int sid = 0; sid < num_segments; sid++)\n      {\n        const int segment_begin = h_offsets_begin[sid];\n        const int segment_end = h_offsets_end[sid];\n\n        if (keys_buffer.selector == 0)\n        {\n          thrust::copy(\n              keys.begin() + segment_begin,\n              keys.begin() + segment_end,\n              result_keys.begin() + segment_begin);\n        }\n                       \n        if (values_buffer.selector == 0)\n        {\n          thrust::copy(\n              values.begin() + segment_begin,\n              values.begin() + segment_end,\n              result_values.begin() + segment_begin);\n        }\n      }\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    result_keys = keys;\n    result_values = values;\n\n    {\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(cub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(cub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  CommandLineArgs args(argc, argv);\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // %PARAM% TEST_CDP cdp 0:1\n\n#if TEST_CDP == 0\n  TestZeroSegments();\n  TestEmptySegments(1 << 2);\n  TestEmptySegments(1 << 22);\n\n#if TEST_HALF_T\n  Test<half_t, std::uint32_t>();\n#endif\n\n#if TEST_BF_T\n  Test<bfloat16_t, std::uint32_t>();\n#endif\n\n  Test<bool, std::uint64_t>();\n  Test<std::uint8_t, std::uint64_t>();\n  Test<std::int64_t, std::uint32_t>();\n\n#elif TEST_CDP == 1\n  TestDeviceSideLaunch<int>();\n#endif // TEST_CDP\n\n  TestUnspecifiedRanges();\n\n  return 0;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_segmented_sort.cuh>\n\n#include <nv/target>\n\n#include <test_util.h>\n\n#include <thrust/count.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n#include <thrust/reduce.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n\n#include <fstream>\n\n#define TEST_HALF_T !_NVHPC_CUDA\n\n#define TEST_BF_T !_NVHPC_CUDA\n\n#if TEST_HALF_T\n#include <hip/hip_fp16.h>\n#endif\n\n#if TEST_BF_T\n#include <hip/hip_bf16.h>\n#endif\n\nusing namespace hipcub;\n\ntemplate <typename T>\nstruct UnwrapHalfAndBfloat16\n{\n  using Type = T;\n};\n\n#if TEST_HALF_T\ntemplate <>\nstruct UnwrapHalfAndBfloat16<half_t>\n{\n  using Type = __half;\n};\n#endif\n\n#if TEST_BF_T\ntemplate <>\nstruct UnwrapHalfAndBfloat16<bfloat16_t>\n{\n  using Type = __hip_bfloat16;\n};\n#endif\n\nconstexpr static int MAX_ITERATIONS = 2;\n\n\nclass SizeGroupDescription\n{\npublic:\n  SizeGroupDescription(const int segments,\n                       const int segment_size)\n      : segments(segments)\n      , segment_size(segment_size)\n  {}\n\n  int segments {};\n  int segment_size {};\n};\n\ntemplate <typename KeyT>\nstruct SegmentChecker\n{\n  const KeyT *sorted_keys {};\n  const int *offsets {};\n\n  SegmentChecker(const KeyT *sorted_keys,\n                 const int *offsets)\n    : sorted_keys(sorted_keys)\n    , offsets(offsets)\n  {}\n\n  bool operator()(int segment_id)\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end = offsets[segment_id + 1];\n\n    int counter = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      if (sorted_keys[i] != static_cast<KeyT>(counter++))\n      {\n        return false;\n      }\n    }\n\n    return true;\n  }\n};\n\ntemplate <typename KeyT>\nstruct DescendingSegmentChecker\n{\n  const KeyT *sorted_keys{};\n  const int *offsets{};\n\n  DescendingSegmentChecker(const KeyT *sorted_keys,\n                           const int *offsets)\n      : sorted_keys(sorted_keys)\n      , offsets(offsets)\n  {}\n\n  bool operator()(int segment_id)\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end   = offsets[segment_id + 1];\n\n    int counter = 0;\n    for (int i = segment_end - 1; i >= segment_begin; i--)\n    {\n      if (sorted_keys[i] != static_cast<KeyT>(counter++))\n      {\n        return false;\n      }\n    }\n\n    return true;\n  }\n};\n\ntemplate <typename KeyT>\nstruct ReversedIota\n{\n  KeyT *data {};\n  const int *offsets {};\n\n  ReversedIota(KeyT *data,\n               const int *offsets)\n    : data(data)\n    , offsets(offsets)\n  {}\n\n  void operator()(int segment_id) const\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end = offsets[segment_id + 1];\n    const int segment_size = segment_end - segment_begin;\n\n    int count = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      data[i] = static_cast<KeyT>(segment_size - 1 - count++);\n    }\n  }\n};\n\n\ntemplate <typename KeyT>\nstruct Iota\n{\n  KeyT *data{};\n  const int *offsets{};\n\n  Iota(KeyT *data, const int *offsets)\n      : data(data)\n      , offsets(offsets)\n  {}\n\n  void operator()(int segment_id) const\n  {\n    const int segment_begin = offsets[segment_id];\n    const int segment_end   = offsets[segment_id + 1];\n\n    int count = 0;\n    for (int i = segment_begin; i < segment_end; i++)\n    {\n      data[i] = static_cast<KeyT>(count++);\n    }\n  }\n};\n\n\ntemplate <typename KeyT,\n          typename ValueT = hipcub::NullType>\nclass Input\n{\n  thrust::default_random_engine random_engine;\n  thrust::device_vector<int> d_segment_sizes;\n  thrust::device_vector<int> d_offsets;\n  thrust::host_vector<int> h_offsets;\n\n  using MaskedValueT = hipcub::detail::conditional_t<\n    std::is_same<ValueT, hipcub::NullType>::value, KeyT, ValueT>;\n\n  bool reverse {};\n  int num_items {};\n  thrust::device_vector<KeyT> d_keys;\n  thrust::device_vector<MaskedValueT> d_values;\n  thrust::host_vector<KeyT> h_keys;\n  thrust::host_vector<MaskedValueT> h_values;\n\npublic:\n  Input(bool reverse, const thrust::host_vector<int> &h_segment_sizes)\n      : d_segment_sizes(h_segment_sizes)\n      , d_offsets(d_segment_sizes.size() + 1)\n      , h_offsets(d_segment_sizes.size() + 1)\n      , reverse(reverse)\n      , num_items(static_cast<int>(\n          thrust::reduce(d_segment_sizes.begin(), d_segment_sizes.end())))\n      , d_keys(num_items)\n      , d_values(num_items)\n      , h_keys(num_items)\n      , h_values(num_items)\n  {\n    update();\n  }\n\n  Input(thrust::host_vector<int> &h_offsets)\n    : d_offsets(h_offsets)\n    , h_offsets(h_offsets)\n    , reverse(false)\n    , num_items(h_offsets.back())\n    , d_keys(num_items)\n    , d_values(num_items)\n  {\n  }\n\n  void shuffle()\n  {\n    thrust::shuffle(d_segment_sizes.begin(), d_segment_sizes.end(), random_engine);\n\n    update();\n  }\n\n  int get_num_items() const\n  {\n    return num_items;\n  }\n\n  int get_num_segments() const\n  {\n    return static_cast<unsigned int>(d_segment_sizes.size());\n  }\n\n  const KeyT *get_d_keys() const\n  {\n    return thrust::raw_pointer_cast(d_keys.data());\n  }\n\n  thrust::device_vector<KeyT> &get_d_keys_vec()\n  {\n    return d_keys;\n  }\n\n  thrust::device_vector<MaskedValueT> &get_d_values_vec()\n  {\n    return d_values;\n  }\n\n  KeyT *get_d_keys()\n  {\n    return thrust::raw_pointer_cast(d_keys.data());\n  }\n\n  const thrust::host_vector<int>& get_h_offsets()\n  {\n    return h_offsets;\n  }\n\n  MaskedValueT *get_d_values()\n  {\n    return thrust::raw_pointer_cast(d_values.data());\n  }\n\n  const int *get_d_offsets() const\n  {\n    return thrust::raw_pointer_cast(d_offsets.data());\n  }\n\n  template <typename T>\n  bool check_output_implementation(const T *keys_output)\n  {\n    const int *offsets = thrust::raw_pointer_cast(h_offsets.data());\n\n    if (reverse)\n    {\n      DescendingSegmentChecker<T> checker{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        if (!checker(i))\n        {\n          return false;\n        }\n      }\n    }\n    else\n    {\n      SegmentChecker<T> checker{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        if (!checker(i))\n        {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  bool check_output(const KeyT *d_keys_output,\n                    const MaskedValueT *d_values_output = nullptr)\n  {\n    KeyT *keys_output = thrust::raw_pointer_cast(h_keys.data());\n    MaskedValueT *values_output = thrust::raw_pointer_cast(h_values.data());\n\n    hipMemcpy(keys_output,\n               d_keys_output,\n               sizeof(KeyT) * num_items,\n               hipMemcpyDeviceToHost);\n\n    const bool keys_ok = check_output_implementation(keys_output);\n\n    if (std::is_same<ValueT, hipcub::NullType>::value || d_values_output == nullptr)\n    {\n      return keys_ok;\n    }\n\n    hipMemcpy(values_output,\n               d_values_output,\n               sizeof(ValueT) * num_items,\n               hipMemcpyDeviceToHost);\n\n    const bool values_ok = check_output_implementation(values_output);\n\n    return keys_ok && values_ok;\n  }\n\nprivate:\n  void update()\n  {\n    fill_offsets();\n    gen_keys();\n  }\n\n  void fill_offsets()\n  {\n    thrust::copy(d_segment_sizes.begin(), d_segment_sizes.end(), d_offsets.begin());\n    thrust::exclusive_scan(d_offsets.begin(), d_offsets.end(), d_offsets.begin(), 0u);\n    thrust::copy(d_offsets.begin(), d_offsets.end(), h_offsets.begin());\n  }\n\n  void gen_keys()\n  {\n    KeyT *keys_output = thrust::raw_pointer_cast(h_keys.data());\n    const int *offsets = thrust::raw_pointer_cast(h_offsets.data());\n\n    if (reverse)\n    {\n      Iota<KeyT> generator{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        generator(i);\n      }\n    }\n    else\n    {\n      ReversedIota<KeyT> generator{keys_output, offsets};\n\n      for (int i = 0; i < get_num_segments(); i++)\n      {\n        generator(i);\n      }\n    }\n\n    d_keys = h_keys;\n    d_values = d_keys;\n  }\n};\n\ntemplate <typename KeyT,\n          bool IsIntegralType = std::is_integral<KeyT>::value>\nclass InputDescription\n{\n  thrust::host_vector<int> segment_sizes;\n\npublic:\n  InputDescription& add(const SizeGroupDescription &group)\n  {\n    if (static_cast<std::size_t>(group.segment_size) <\n        static_cast<std::size_t>((std::numeric_limits<KeyT>::max)()))\n    {\n      for (int i = 0; i < group.segments; i++)\n      {\n        segment_sizes.push_back(group.segment_size);\n      }\n    }\n\n    return *this;\n  }\n\n  template <typename ValueT = hipcub::NullType>\n  Input<KeyT, ValueT> gen(bool reverse)\n  {\n    return Input<KeyT, ValueT>(reverse, segment_sizes);\n  }\n};\n\ntemplate <typename KeyT>\nclass InputDescription<KeyT, false>\n{\n  thrust::host_vector<int> segment_sizes;\n\npublic:\n  InputDescription& add(const SizeGroupDescription &group)\n  {\n    for (int i = 0; i < group.segments; i++)\n    {\n      segment_sizes.push_back(group.segment_size);\n    }\n\n    return *this;\n  }\n\n  template <typename ValueT = hipcub::NullType>\n  Input<KeyT, ValueT> gen(bool reverse)\n  {\n    return Input<KeyT, ValueT>(reverse, segment_sizes);\n  }\n};\n\n\ntemplate <typename WrappedKeyT,\n          typename ValueT>\nvoid Sort(bool pairs,\n          bool descending,\n          bool double_buffer,\n          bool stable_sort,\n\n          void *tmp_storage,\n          std::size_t &temp_storage_bytes,\n\n          WrappedKeyT *wrapped_input_keys,\n          WrappedKeyT *wrapped_output_keys,\n\n          ValueT *input_values,\n          ValueT *output_values,\n\n          int num_items,\n          int num_segments,\n          const int *d_offsets,\n\n          int *keys_selector = nullptr,\n          int *values_selector = nullptr)\n{\n  using KeyT = typename UnwrapHalfAndBfloat16<WrappedKeyT>::Type;\n\n  auto input_keys = reinterpret_cast<KeyT*>(wrapped_input_keys);\n  auto output_keys = reinterpret_cast<KeyT*>(wrapped_output_keys);\n\n  if (stable_sort)\n  {\n    if (pairs)\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          hipcub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(hipcub::DeviceSegmentedSort::StableSortPairsDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            keys_buffer,\n            values_buffer,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(hipcub::DeviceSegmentedSort::StableSortPairsDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            input_keys,\n            output_keys,\n            input_values,\n            output_values,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          hipcub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::StableSortPairs(tmp_storage,\n                                                      temp_storage_bytes,\n                                                      keys_buffer,\n                                                      values_buffer,\n                                                      num_items,\n                                                      num_segments,\n                                                      d_offsets,\n                                                      d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::StableSortPairs(tmp_storage,\n                                                      temp_storage_bytes,\n                                                      input_keys,\n                                                      output_keys,\n                                                      input_values,\n                                                      output_values,\n                                                      num_items,\n                                                      num_segments,\n                                                      d_offsets,\n                                                      d_offsets + 1));\n        }\n      }\n    }\n    else\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(hipcub::DeviceSegmentedSort::StableSortKeysDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            keys_buffer,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(hipcub::DeviceSegmentedSort::StableSortKeysDescending(\n            tmp_storage,\n            temp_storage_bytes,\n            input_keys,\n            output_keys,\n            num_items,\n            num_segments,\n            d_offsets,\n            d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::StableSortKeys(tmp_storage,\n                                                     temp_storage_bytes,\n                                                     keys_buffer,\n                                                     num_items,\n                                                     num_segments,\n                                                     d_offsets,\n                                                     d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::StableSortKeys(tmp_storage,\n                                                     temp_storage_bytes,\n                                                     input_keys,\n                                                     output_keys,\n                                                     num_items,\n                                                     num_segments,\n                                                     d_offsets,\n                                                     d_offsets + 1));\n        }\n      }\n    }\n  }\n  else\n  {\n    if (pairs)\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          hipcub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::SortPairsDescending(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          keys_buffer,\n                                                          values_buffer,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::SortPairsDescending(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          input_keys,\n                                                          output_keys,\n                                                          input_values,\n                                                          output_values,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          hipcub::DoubleBuffer<ValueT> values_buffer(input_values, output_values);\n          values_buffer.selector = *values_selector;\n\n          CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(tmp_storage,\n                                                           temp_storage_bytes,\n                                                           keys_buffer,\n                                                           values_buffer,\n                                                           num_items,\n                                                           num_segments,\n                                                           d_offsets,\n                                                           d_offsets + 1));\n\n          *keys_selector   = keys_buffer.selector;\n          *values_selector = values_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(tmp_storage,\n                                                           temp_storage_bytes,\n                                                           input_keys,\n                                                           output_keys,\n                                                           input_values,\n                                                           output_values,\n                                                           num_items,\n                                                           num_segments,\n                                                           d_offsets,\n                                                           d_offsets + 1));\n        }\n      }\n    }\n    else\n    {\n      if (descending)\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::SortKeysDescending(tmp_storage,\n                                                         temp_storage_bytes,\n                                                         keys_buffer,\n                                                         num_items,\n                                                         num_segments,\n                                                         d_offsets,\n                                                         d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(\n            hipcub::DeviceSegmentedSort::SortKeysDescending(tmp_storage,\n                                                         temp_storage_bytes,\n                                                         input_keys,\n                                                         output_keys,\n                                                         num_items,\n                                                         num_segments,\n                                                         d_offsets,\n                                                         d_offsets + 1));\n        }\n      }\n      else\n      {\n        if (double_buffer)\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input_keys, output_keys);\n          keys_buffer.selector = *keys_selector;\n\n          CubDebugExit(hipcub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          keys_buffer,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n\n          *keys_selector = keys_buffer.selector;\n        }\n        else\n        {\n          CubDebugExit(hipcub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                                          temp_storage_bytes,\n                                                          input_keys,\n                                                          output_keys,\n                                                          num_items,\n                                                          num_segments,\n                                                          d_offsets,\n                                                          d_offsets + 1));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nstd::size_t Sort(bool pairs,\n                 bool descending,\n                 bool double_buffer,\n                 bool stable_sort,\n\n                 KeyT *input_keys,\n                 KeyT *output_keys,\n\n                 ValueT *input_values,\n                 ValueT *output_values,\n\n                 int num_items,\n                 int num_segments,\n                 const int *d_offsets,\n\n                 int *keys_selector   = nullptr,\n                 int *values_selector = nullptr)\n{\n  std::size_t temp_storage_bytes = 42ul;\n\n  Sort<KeyT, ValueT>(pairs,\n                     descending,\n                     double_buffer,\n                     stable_sort,\n                     nullptr,\n                     temp_storage_bytes,\n                     input_keys,\n                     output_keys,\n                     input_values,\n                     output_values,\n                     num_items,\n                     num_segments,\n                     d_offsets,\n                     keys_selector,\n                     values_selector);\n\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  Sort<KeyT, ValueT>(pairs,\n                     descending,\n                     double_buffer,\n                     stable_sort,\n                     d_temp_storage,\n                     temp_storage_bytes,\n                     input_keys,\n                     output_keys,\n                     input_values,\n                     output_values,\n                     num_items,\n                     num_segments,\n                     d_offsets,\n                     keys_selector,\n                     values_selector);\n\n  return temp_storage_bytes;\n}\n\n\nconstexpr bool keys_only = false;\nconstexpr bool pairs = true;\n\nconstexpr bool ascending = false;\nconstexpr bool descending = true;\n\nconstexpr bool pointers = false;\nconstexpr bool double_buffer = true;\n\nconstexpr bool unstable = false;\nconstexpr bool stable = true;\n\n\nvoid TestZeroSegments()\n{\n  // Type doesn't affect the escape logic, so it should be fine\n  // to test only one set of types here.\n\n  using KeyT = std::uint8_t;\n  using ValueT = std::uint64_t;\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffer: { pointers, double_buffer })\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          hipcub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffer,\n                             stable_sort,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             int{},\n                             int{},\n                             nullptr,\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          AssertEquals(keys_buffer.selector, 0);\n          AssertEquals(values_buffer.selector, 1);\n        }\n      }\n    }\n  }\n}\n\n\nvoid TestEmptySegments(int segments)\n{\n  // Type doesn't affect the escape logic, so it should be fine\n  // to test only one set of types here.\n\n  using KeyT = std::uint8_t;\n  using ValueT = std::uint64_t;\n\n  thrust::device_vector<int> offsets(segments + 1, int{});\n  const int *d_offsets = thrust::raw_pointer_cast(offsets.data());\n\n  for (bool sort_stable: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffer: { pointers, double_buffer })\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          hipcub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffer,\n                             sort_stable,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             nullptr,\n                             int{},\n                             segments,\n                             d_offsets,\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          AssertEquals(keys_buffer.selector, 0);\n          AssertEquals(values_buffer.selector, 1);\n        }\n      }\n    }\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid TestSameSizeSegments(int segment_size,\n                          int segments,\n                          bool skip_values = false)\n{\n  const int num_items = segment_size * segments;\n\n  thrust::device_vector<int> offsets(segments + 1);\n  thrust::sequence(offsets.begin(),\n                   offsets.end(),\n                   int{},\n                   segment_size);\n\n  const int *d_offsets = thrust::raw_pointer_cast(offsets.data());\n\n  const KeyT target_key {1};\n  const ValueT target_value {42};\n\n  thrust::device_vector<KeyT> keys_input(num_items);\n  thrust::device_vector<KeyT> keys_output(num_items);\n\n  KeyT *d_keys_input  = thrust::raw_pointer_cast(keys_input.data());\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::device_vector<ValueT> values_input(num_items);\n  thrust::device_vector<ValueT> values_output(num_items);\n\n  thrust::host_vector<KeyT> host_keys(num_items);\n  thrust::host_vector<ValueT> host_values(num_items);\n\n  ValueT *d_values_input  = thrust::raw_pointer_cast(values_input.data());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      if (sort_pairs)\n      {\n        if (skip_values)\n        {\n          continue;\n        }\n      }\n\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffers: { pointers, double_buffer })\n        {\n          hipcub::DoubleBuffer<KeyT> keys_buffer(nullptr, nullptr);\n          hipcub::DoubleBuffer<ValueT> values_buffer(nullptr, nullptr);\n          values_buffer.selector = 1;\n\n          thrust::fill(keys_input.begin(), keys_input.end(), target_key);\n          thrust::fill(keys_output.begin(), keys_output.end(), KeyT{});\n\n          if (sort_pairs)\n          {\n            if (sort_buffers)\n            {\n              thrust::fill(values_input.begin(), values_input.end(), ValueT{});\n              thrust::fill(values_output.begin(), values_output.end(), target_value);\n            }\n            else\n            {\n              thrust::fill(values_input.begin(), values_input.end(), target_value);\n              thrust::fill(values_output.begin(), values_output.end(), ValueT{});\n            }\n          }\n\n          const std::size_t temp_storage_bytes =\n            Sort<KeyT, ValueT>(sort_pairs,\n                               sort_descending,\n                               sort_buffers,\n                               stable_sort,\n                               d_keys_input,\n                               d_keys_output,\n                               d_values_input,\n                               d_values_output,\n                               num_items,\n                               segments,\n                               d_offsets,\n                               &keys_buffer.selector,\n                               &values_buffer.selector);\n\n          // If temporary storage size is defined by extra keys storage\n          if (sort_buffers)\n          {\n            if (2 * segments * sizeof(unsigned int) < num_items * sizeof(KeyT))\n            {\n              std::size_t extra_temp_storage_bytes{};\n\n              Sort(sort_pairs,\n                   sort_descending,\n                   pointers,\n                   stable_sort,\n                   nullptr,\n                   extra_temp_storage_bytes,\n                   d_keys_input,\n                   d_keys_output,\n                   d_values_input,\n                   d_values_output,\n                   num_items,\n                   segments,\n                   d_offsets,\n                   &keys_buffer.selector,\n                   &values_buffer.selector);\n\n              AssertTrue(extra_temp_storage_bytes > temp_storage_bytes);\n            }\n          }\n\n          {\n            host_keys = keys_buffer.selector || !sort_buffers ? keys_output\n                                                              : keys_input;\n            const std::size_t items_selected =\n              thrust::count(host_keys.begin(), host_keys.end(), target_key);\n            AssertEquals(static_cast<int>(items_selected), num_items);\n          }\n\n          if (sort_pairs)\n          {\n            host_values = values_buffer.selector || !sort_buffers\n                            ? values_output\n                            : values_input;\n            const std::size_t items_selected =\n              thrust::count(host_values.begin(),\n                            host_values.end(),\n                            target_value);\n\n            AssertEquals(static_cast<int>(items_selected), num_items);\n          }\n        }\n      }\n    }\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid InputTest(bool sort_descending,\n               Input<KeyT, ValueT> &input)\n{\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs : { keys_only, pairs })\n    {\n      for (bool sort_buffers : {pointers, double_buffer})\n      {\n        for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n        {\n          thrust::fill(keys_output.begin(), keys_output.end(), KeyT{});\n          thrust::fill(values_output.begin(), values_output.end(), ValueT{});\n\n          hipcub::DoubleBuffer<KeyT> keys_buffer(input.get_d_keys(),\n                                              d_keys_output);\n          hipcub::DoubleBuffer<ValueT> values_buffer(input.get_d_values(),\n                                                  d_values_output);\n\n          Sort<KeyT, ValueT>(sort_pairs,\n                             sort_descending,\n                             sort_buffers,\n                             stable_sort,\n                             input.get_d_keys(),\n                             d_keys_output,\n                             input.get_d_values(),\n                             d_values_output,\n                             input.get_num_items(),\n                             input.get_num_segments(),\n                             input.get_d_offsets(),\n                             &keys_buffer.selector,\n                             &values_buffer.selector);\n\n          if (sort_buffers)\n          {\n            if (sort_pairs)\n            {\n              AssertTrue(input.check_output(keys_buffer.Current(),\n                                            values_buffer.Current()));\n            }\n            else\n            {\n              AssertTrue(input.check_output(keys_buffer.Current()));\n            }\n          }\n          else\n          {\n            if (sort_pairs)\n            {\n              AssertTrue(input.check_output(d_keys_output, d_values_output));\n            }\n            else\n            {\n              AssertTrue(input.check_output(d_keys_output));\n            }\n          }\n\n          input.shuffle();\n        }\n      }\n    }\n  }\n}\n\nstruct ComparisonPredicate\n{\n  template <typename T>\n  __host__ __device__ bool operator()(const T &lhs, const T &rhs) const\n  {\n    return lhs == rhs;\n  }\n\n  __host__ __device__ bool operator()(const half_t &lhs, const half_t &rhs) const\n  {\n    return lhs.raw() == rhs.raw();\n  }\n};\n\ntemplate <typename T>\nbool compare_two_outputs(const thrust::host_vector<int> &offsets,\n                         const thrust::host_vector<T> &lhs,\n                         const thrust::host_vector<T> &rhs)\n{\n  const auto num_segments = static_cast<unsigned int>(offsets.size() - 1);\n\n  for (std::size_t segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    auto lhs_begin = lhs.cbegin() + offsets[segment_id];\n    auto lhs_end = lhs.cbegin() + offsets[segment_id + 1];\n    auto rhs_begin = rhs.cbegin() + offsets[segment_id];\n\n    auto err = thrust::mismatch(lhs_begin, lhs_end, rhs_begin, ComparisonPredicate{});\n\n    if (err.first != lhs_end)\n    {\n      const auto idx = thrust::distance(lhs_begin, err.first);\n      const auto segment_size = std::distance(lhs_begin, lhs_end);\n\n      std::cerr << \"Mismatch in segment \" << segment_id\n                << \" at position \" << idx << \" / \" << segment_size\n                << \": \"\n                << static_cast<std::uint64_t>(lhs_begin[idx]) << \" vs \"\n                << static_cast<std::uint64_t>(rhs_begin[idx]) << \" (\"\n                << typeid(lhs_begin[idx]).name() << \")\" << std::endl;\n\n      return false;\n    }\n  }\n\n  return true;\n}\n\ntemplate <typename ValueT>\nvoid RandomizeInput(thrust::host_vector<bool> &h_keys,\n                    thrust::host_vector<ValueT> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<std::uint8_t>::max)()) > 128;\n    h_values[i] = RandomValue((std::numeric_limits<ValueT>::max)());\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid RandomizeInput(thrust::host_vector<KeyT> &h_keys,\n                    thrust::host_vector<ValueT> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<KeyT>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<ValueT>::max)());\n  }\n}\n\n#if TEST_HALF_T\nvoid RandomizeInput(thrust::host_vector<half_t> &h_keys,\n                    thrust::host_vector<std::uint32_t> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<int>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<std::uint32_t>::max)());\n  }\n}\n#endif\n\n#if TEST_BF_T\nvoid RandomizeInput(thrust::host_vector<bfloat16_t> &h_keys,\n                    thrust::host_vector<std::uint32_t> &h_values)\n{\n  for (std::size_t i = 0; i < h_keys.size(); i++)\n  {\n    h_keys[i] = RandomValue((std::numeric_limits<int>::max)());\n    h_values[i] = RandomValue((std::numeric_limits<std::uint32_t>::max)());\n  }\n}\n#endif\n\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid HostReferenceSort(bool sort_pairs,\n                       bool sort_descending,\n                       unsigned int num_segments,\n                       const thrust::host_vector<int> &h_offsets,\n                       thrust::host_vector<KeyT> &h_keys,\n                       thrust::host_vector<ValueT> &h_values)\n{\n  for (unsigned int segment_i = 0;\n       segment_i < num_segments;\n       segment_i++)\n  {\n    const int segment_begin = h_offsets[segment_i];\n    const int segment_end   = h_offsets[segment_i + 1];\n\n    if (sort_pairs)\n    {\n      if (sort_descending)\n      {\n        thrust::stable_sort_by_key(h_keys.begin() + segment_begin,\n                                   h_keys.begin() + segment_end,\n                                   h_values.begin() + segment_begin,\n                                   thrust::greater<KeyT>{});\n      }\n      else\n      {\n        thrust::stable_sort_by_key(h_keys.begin() + segment_begin,\n                                   h_keys.begin() + segment_end,\n                                   h_values.begin() + segment_begin);\n      }\n    }\n    else\n    {\n      if (sort_descending)\n      {\n        thrust::stable_sort(h_keys.begin() + segment_begin,\n                            h_keys.begin() + segment_end,\n                            thrust::greater<KeyT>{});\n      }\n      else\n      {\n        thrust::stable_sort(h_keys.begin() + segment_begin,\n                            h_keys.begin() + segment_end);\n      }\n    }\n  }\n}\n\n\n#if STORE_ON_FAILURE\ntemplate <typename KeyT,\n          typename ValueT>\nvoid DumpInput(bool sort_pairs,\n               bool sort_descending,\n               bool sort_buffers,\n               Input<KeyT, ValueT> &input,\n               thrust::host_vector<KeyT> &h_keys,\n               thrust::host_vector<ValueT> &h_values)\n{\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  std::cout << \"sort pairs: \" << sort_pairs << \"\\n\";\n  std::cout << \"sort descending: \" << sort_descending << \"\\n\";\n  std::cout << \"sort buffers: \" << sort_buffers << \"\\n\";\n  std::cout << \"num_items: \" << input.get_num_items() << \"\\n\";\n  std::cout << \"num_segments: \" << input.get_num_segments() << \"\\n\";\n  std::cout << \"key type: \" << typeid(h_keys[0]).name() << \"\\n\";\n  std::cout << \"value type: \" << typeid(h_values[0]).name() << \"\\n\";\n  std::cout << \"offset type: \" << typeid(h_offsets[0]).name() << \"\\n\";\n\n  std::ofstream offsets_dump(\"offsets\", std::ios::binary);\n  offsets_dump.write(reinterpret_cast<const char *>(\n                       thrust::raw_pointer_cast(h_offsets.data())),\n                     sizeof(int) * h_offsets.size());\n\n  std::ofstream keys_dump(\"keys\", std::ios::binary);\n  keys_dump.write(reinterpret_cast<const char *>(\n                    thrust::raw_pointer_cast(h_keys.data())),\n                  sizeof(KeyT) * h_keys.size());\n\n  std::ofstream values_dump(\"values\", std::ios::binary);\n  values_dump.write(reinterpret_cast<const char *>(\n                      thrust::raw_pointer_cast(h_values.data())),\n                    sizeof(ValueT) * h_values.size());\n}\n#endif\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid InputTestRandom(Input<KeyT, ValueT> &input)\n{\n  thrust::host_vector<KeyT> h_keys_output(input.get_num_items());\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n\n  thrust::host_vector<ValueT> h_values_output(input.get_num_items());\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n  ValueT *d_values_output = thrust::raw_pointer_cast(values_output.data());\n\n  thrust::host_vector<KeyT> h_keys(input.get_num_items());\n  thrust::host_vector<ValueT> h_values(input.get_num_items());\n\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  for (bool stable_sort: { unstable, stable })\n  {\n    for (bool sort_pairs: { keys_only, pairs })\n    {\n      for (bool sort_descending: { ascending, descending })\n      {\n        for (bool sort_buffers: { pointers, double_buffer })\n        {\n          for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n          {\n            RandomizeInput(h_keys, h_values);\n\n#if STORE_ON_FAILURE\n            auto h_keys_backup = h_keys;\n            auto h_values_backup = h_values;\n#endif\n\n            input.get_d_keys_vec()   = h_keys;\n            input.get_d_values_vec() = h_values;\n\n            hipcub::DoubleBuffer<KeyT> keys_buffer(input.get_d_keys(), d_keys_output);\n            hipcub::DoubleBuffer<ValueT> values_buffer(input.get_d_values(), d_values_output);\n\n            Sort<KeyT, ValueT>(sort_pairs,\n                               sort_descending,\n                               sort_buffers,\n                               stable_sort,\n                               input.get_d_keys(),\n                               d_keys_output,\n                               input.get_d_values(),\n                               d_values_output,\n                               input.get_num_items(),\n                               input.get_num_segments(),\n                               input.get_d_offsets(),\n                               &keys_buffer.selector,\n                               &values_buffer.selector);\n\n            HostReferenceSort(sort_pairs,\n                              sort_descending,\n                              input.get_num_segments(),\n                              h_offsets,\n                              h_keys,\n                              h_values);\n\n            if (sort_buffers)\n            {\n              if (keys_buffer.selector)\n              {\n                h_keys_output = keys_output;\n              }\n              else\n              {\n                h_keys_output = input.get_d_keys_vec();\n              }\n\n              if (values_buffer.selector)\n              {\n                h_values_output = values_output;\n              }\n              else\n              {\n                h_values_output = input.get_d_values_vec();\n              }\n            }\n            else\n            {\n              h_keys_output = keys_output;\n              h_values_output = values_output;\n            }\n\n            const bool keys_ok =\n              compare_two_outputs(h_offsets, h_keys, h_keys_output);\n\n            const bool values_ok =\n              sort_pairs\n                ? compare_two_outputs(h_offsets, h_values, h_values_output)\n                : true;\n\n#if STORE_ON_FAILURE\n            if (!keys_ok || !values_ok)\n            {\n              DumpInput<KeyT, ValueT>(sort_pairs,\n                                      sort_descending,\n                                      sort_buffers,\n                                      input,\n                                      h_keys_backup,\n                                      h_values_backup);\n            }\n#endif\n\n            AssertTrue(keys_ok);\n            AssertTrue(values_ok);\n\n            input.shuffle();\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename KeyT,\n          typename ValueT,\n          bool IsSupportedType = std::is_integral<KeyT>::value>\nstruct EdgeTestDispatch\n{\n  // Edge cases that needs to be tested\n  const int empty_short_circuit_segment_size = 0;\n  const int copy_short_circuit_segment_size = 1;\n  const int swap_short_circuit_segment_size = 2;\n\n  const int a_few = 2;\n  const int a_bunch_of = 42;\n  const int a_lot_of = 420;\n\n  template <typename ActivePolicyT>\n  HIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t Invoke()\n  {\n    NV_IF_TARGET(NV_IS_HOST,\n      (using SmallAndMediumPolicyT =\n         typename ActivePolicyT::SmallAndMediumSegmentedSortPolicyT;\n       using LargeSegmentPolicyT = typename ActivePolicyT::LargeSegmentPolicy;\n\n       const int small_segment_max_segment_size =\n         SmallAndMediumPolicyT::SmallPolicyT::ITEMS_PER_TILE;\n\n       const int items_per_small_segment =\n         SmallAndMediumPolicyT::SmallPolicyT::ITEMS_PER_THREAD;\n\n       const int medium_segment_max_segment_size =\n         SmallAndMediumPolicyT::MediumPolicyT::ITEMS_PER_TILE;\n\n       const int single_thread_segment_size = items_per_small_segment;\n\n       const int large_cached_segment_max_segment_size =\n         LargeSegmentPolicyT::BLOCK_THREADS *\n         LargeSegmentPolicyT::ITEMS_PER_THREAD;\n\n       for (bool sort_descending : {ascending, descending}) {\n         Input<KeyT, ValueT> edge_cases =\n           InputDescription<KeyT>()\n             .add({a_lot_of, empty_short_circuit_segment_size})\n             .add({a_lot_of, copy_short_circuit_segment_size})\n             .add({a_lot_of, swap_short_circuit_segment_size})\n             .add({a_lot_of, swap_short_circuit_segment_size + 1})\n             .add({a_lot_of, swap_short_circuit_segment_size + 1})\n             .add({a_lot_of, single_thread_segment_size - 1})\n             .add({a_lot_of, single_thread_segment_size})\n             .add({a_lot_of, single_thread_segment_size + 1})\n             .add({a_lot_of, single_thread_segment_size * 2 - 1})\n             .add({a_lot_of, single_thread_segment_size * 2})\n             .add({a_lot_of, single_thread_segment_size * 2 + 1})\n             .add({a_bunch_of, small_segment_max_segment_size - 1})\n             .add({a_bunch_of, small_segment_max_segment_size})\n             .add({a_bunch_of, small_segment_max_segment_size + 1})\n             .add({a_bunch_of, medium_segment_max_segment_size - 1})\n             .add({a_bunch_of, medium_segment_max_segment_size})\n             .add({a_bunch_of, medium_segment_max_segment_size + 1})\n             .add({a_bunch_of, large_cached_segment_max_segment_size - 1})\n             .add({a_bunch_of, large_cached_segment_max_segment_size})\n             .add({a_bunch_of, large_cached_segment_max_segment_size + 1})\n             .add({a_few, large_cached_segment_max_segment_size * 2})\n             .add({a_few, large_cached_segment_max_segment_size * 3})\n             .add({a_few, large_cached_segment_max_segment_size * 5})\n             .template gen<ValueT>(sort_descending);\n\n         InputTest<KeyT, ValueT>(sort_descending, edge_cases);\n       }));\n\n    return hipSuccess;\n  }\n};\n\ntemplate <typename KeyT,\n          typename ValueT>\nstruct EdgeTestDispatch<KeyT, ValueT, false>\n{\n  template <typename ActivePolicyT>\n  HIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t Invoke()\n  {\n    // Edge case test is using an optimized testing approach which is\n    // incompatible with duplicates. RandomTest is used for other types.\n    return hipSuccess;\n  }\n};\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid EdgePatternsTest()\n{\n  int ptx_version = 0;\n  if (HipcubDebug(PtxVersion(ptx_version)))\n  {\n    return;\n  }\n\n  using MaxPolicyT =\n    typename hipcub::DeviceSegmentedSortPolicy<KeyT, ValueT>::MaxPolicy;\n  using EdgeTestDispatchT = EdgeTestDispatch<KeyT, ValueT>;\n  EdgeTestDispatchT dispatch;\n\n  MaxPolicyT::Invoke(ptx_version, dispatch);\n\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nInput<KeyT, ValueT> GenRandomInput(int max_items,\n                                   int min_segments,\n                                   int max_segments,\n                                   bool descending)\n{\n  int items_generated {};\n  const int segments_num = RandomValue(max_segments) + min_segments;\n\n  thrust::host_vector<int> segment_sizes;\n  segment_sizes.reserve(segments_num);\n\n  const int max_segment_size = 6000;\n\n  for (int segment_id = 0; segment_id < segments_num; segment_id++)\n  {\n    const int segment_size_raw = RandomValue(max_segment_size);\n    const int segment_size     = segment_size_raw > 0 ? segment_size_raw : 0;\n\n    if (segment_size + items_generated > max_items)\n    {\n      break;\n    }\n\n    items_generated += segment_size;\n    segment_sizes.push_back(segment_size);\n  }\n\n  return Input<KeyT, ValueT>{descending, segment_sizes};\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid RandomTest(int min_segments,\n                int max_segments)\n{\n  const int max_items = 10000000;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    Input<KeyT, ValueT> edge_cases = GenRandomInput<KeyT, ValueT>(max_items,\n                                                                  min_segments,\n                                                                  max_segments,\n                                                                  descending);\n\n    InputTestRandom(edge_cases);\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid Test()\n{\n  for (int segment_size: { 1, 1024, 24 * 1024 })\n  {\n    for (int segments: { 1, 1024 })\n    {\n      TestSameSizeSegments<KeyT, ValueT>(segment_size, segments);\n    }\n  }\n\n  RandomTest<KeyT, ValueT>(1 << 2, 1 << 8);\n  RandomTest<KeyT, ValueT>(1 << 9, 1 << 19);\n  EdgePatternsTest<KeyT, ValueT>();\n}\n\n\n#if TEST_CDP == 1\ntemplate <typename KeyT>\n__global__ void LauncherKernel(\n    void *tmp_storage,\n    std::size_t temp_storage_bytes,\n    const KeyT *in_keys,\n    KeyT *out_keys,\n    int num_items,\n    int num_segments,\n    const int *offsets)\n{\n  HipcubDebug(hipcub::DeviceSegmentedSort::SortKeys(tmp_storage,\n                                              temp_storage_bytes,\n                                              in_keys,\n                                              out_keys,\n                                              num_items,\n                                              num_segments,\n                                              offsets,\n                                              offsets + 1));\n}\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid TestDeviceSideLaunch(Input<KeyT, ValueT> &input)\n{\n  thrust::host_vector<KeyT> h_keys_output(input.get_num_items());\n  thrust::device_vector<KeyT> keys_output(input.get_num_items());\n\n  thrust::host_vector<ValueT> h_values_output(input.get_num_items());\n  thrust::device_vector<ValueT> values_output(input.get_num_items());\n\n  KeyT *d_keys_output = thrust::raw_pointer_cast(keys_output.data());\n\n  thrust::host_vector<KeyT> h_keys(input.get_num_items());\n  thrust::host_vector<ValueT> h_values(input.get_num_items());\n\n  const thrust::host_vector<int> &h_offsets = input.get_h_offsets();\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    RandomizeInput(h_keys, h_values);\n\n    input.get_d_keys_vec()   = h_keys;\n    input.get_d_values_vec() = h_values;\n\n    const KeyT *d_input = input.get_d_keys();\n\n    std::size_t temp_storage_bytes{};\n    hipcub::DeviceSegmentedSort::SortKeys(nullptr,\n                                       temp_storage_bytes,\n                                       d_input,\n                                       d_keys_output,\n                                       input.get_num_items(),\n                                       input.get_num_segments(),\n                                       input.get_d_offsets(),\n                                       input.get_d_offsets() + 1);\n\n    thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n    std::uint8_t *d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n    LauncherKernel<KeyT><<<1, 1>>>(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_input,\n      d_keys_output,\n      input.get_num_items(),\n      input.get_num_segments(),\n      input.get_d_offsets());\n    CubDebugExit(hipDeviceSynchronize());\n    CubDebugExit(hipPeekAtLastError());\n\n    HostReferenceSort(false,\n                      false,\n                      input.get_num_segments(),\n                      h_offsets,\n                      h_keys,\n                      h_values);\n\n    h_keys_output = keys_output;\n\n    const bool keys_ok =\n      compare_two_outputs(h_offsets, h_keys, h_keys_output);\n\n    AssertTrue(keys_ok);\n\n    input.shuffle();\n  }\n}\n\ntemplate <typename KeyT>\nvoid TestDeviceSideLaunch(int min_segments, int max_segments)\n{\n  const int max_items = 10000000;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    Input<KeyT, KeyT> edge_cases =\n      GenRandomInput<KeyT, KeyT>(max_items,\n                                 min_segments,\n                                 max_segments,\n                                 descending);\n\n    TestDeviceSideLaunch(edge_cases);\n  }\n}\n\ntemplate <typename KeyT>\nvoid TestDeviceSideLaunch()\n{\n  TestDeviceSideLaunch<KeyT>(1 << 2, 1 << 8);\n  TestDeviceSideLaunch<KeyT>(1 << 9, 1 << 19);\n}\n\n#endif // TEST_CDP\n\nvoid TestUnspecifiedRanges()\n{\n  const std::size_t num_items = 1024 * 1024;\n  const std::size_t max_segments = 42;\n  const std::size_t avg_segment_size = num_items / max_segments;\n\n  for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++)\n  {\n    thrust::host_vector<int> h_offsets_begin;\n    thrust::host_vector<int> h_offsets_end;\n\n    h_offsets_begin.reserve(max_segments + 1);\n    h_offsets_end.reserve(max_segments + 1);\n\n    {\n      int offset = 0;\n\n      for (std::size_t sid = 0; sid < max_segments; sid++)\n      {\n        const int segment_size =\n          RandomValue(static_cast<int>(avg_segment_size));\n        const bool segment_is_utilized = RandomValue(100) > 60;\n\n        if (segment_is_utilized)\n        {\n          h_offsets_begin.push_back(offset);\n          h_offsets_end.push_back(offset + segment_size);\n        }\n\n        offset += segment_size;\n      }\n\n      if (h_offsets_begin.empty())\n      {\n        h_offsets_begin.push_back(avg_segment_size);\n        h_offsets_end.push_back(num_items);\n      }\n    }\n\n    thrust::device_vector<int> keys(num_items);\n    thrust::device_vector<int> values(num_items);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    thrust::device_vector<int> d_offsets_begin = h_offsets_begin;\n    thrust::device_vector<int> d_offsets_end = h_offsets_end;\n\n    thrust::device_vector<int> expected_keys = keys;\n    thrust::device_vector<int> expected_values = values;\n\n    const int num_segments = static_cast<int>(h_offsets_begin.size());\n\n    for (int sid = 0; sid < num_segments; sid++)\n    {\n      const int segment_begin = h_offsets_begin[sid];\n      const int segment_end = h_offsets_end[sid];\n\n      thrust::sort_by_key(expected_keys.begin() + segment_begin,\n                          expected_keys.begin() + segment_end,\n                          expected_values.begin() + segment_begin);\n    }\n\n    thrust::device_vector<int> result_keys = keys;\n    thrust::device_vector<int> result_values = values;\n\n    {\n      hipcub::DoubleBuffer<int> keys_buffer(\n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()));\n\n      hipcub::DoubleBuffer<int> values_buffer(\n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()));\n\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      for (int sid = 0; sid < num_segments; sid++)\n      {\n        const int segment_begin = h_offsets_begin[sid];\n        const int segment_end = h_offsets_end[sid];\n\n        if (keys_buffer.selector == 0)\n        {\n          thrust::copy(\n              keys.begin() + segment_begin,\n              keys.begin() + segment_end,\n              result_keys.begin() + segment_begin);\n        }\n                       \n        if (values_buffer.selector == 0)\n        {\n          thrust::copy(\n              values.begin() + segment_begin,\n              values.begin() + segment_end,\n              result_values.begin() + segment_begin);\n        }\n      }\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    result_keys = keys;\n    result_values = values;\n\n    {\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(hipcub::DeviceSegmentedSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data())));\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  CommandLineArgs args(argc, argv);\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // %PARAM% TEST_CDP cdp 0:1\n\n#if TEST_CDP == 0\n  TestZeroSegments();\n  TestEmptySegments(1 << 2);\n  TestEmptySegments(1 << 22);\n\n#if TEST_HALF_T\n  Test<half_t, std::uint32_t>();\n#endif\n\n#if TEST_BF_T\n  Test<bfloat16_t, std::uint32_t>();\n#endif\n\n  Test<bool, std::uint64_t>();\n  Test<std::uint8_t, std::uint64_t>();\n  Test<std::int64_t, std::uint32_t>();\n\n#elif TEST_CDP == 1\n  TestDeviceSideLaunch<int>();\n#endif // TEST_CDP\n\n  TestUnspecifiedRanges();\n\n  return 0;\n}\n", "source": "/kaggle/working/cub/test/test_device_segmented_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#undef NDEBUG\n#include <cassert>\n\n#include \"catch2_test_block_radix_sort.cuh\"\n#include \"cub/block/radix_rank_sort_operations.cuh\"\n\n#include <algorithm>\n#include <type_traits>\n#include <utility>\n\n// example-begin custom-type\nstruct custom_t\n{\n  float f;\n  int unused;\n  long long int lli;\n\n  custom_t() = default;\n  __device__ custom_t(float f, long long int lli)\n      : f(f)\n      , unused(42)\n      , lli(lli)\n  {}\n};\n\nstatic __device__ bool operator==(const custom_t &lhs, const custom_t &rhs)\n{\n  return lhs.f == rhs.f && lhs.lli == rhs.lli;\n}\n\nstruct decomposer_t\n{\n  __device__ ::cuda::std::tuple<float &, long long int &> //\n  operator()(custom_t &key) const\n  {\n    return {key.f, key.lli};\n  }\n};\n// example-end custom-type\n\n__global__ void sort_keys()\n{\n  // example-begin keys\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2}  //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5}  //\n     }};\n  // example-end keys\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_bits()\n{\n  // example-begin keys-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n  // example-end keys-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_descending()\n{\n  // example-begin keys-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5}  //\n     }};\n  // example-end keys-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_bits()\n{\n  // example-begin keys-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60}  // thread 1 expected keys\n     }};\n  // example-end keys-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs()\n{\n  // example-begin pairs\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3},             // thread 0 values\n     {1, 2, 5}};            // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2}  //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2},  // thread 0 expected values\n     {3, 4, 5}}; // thread 1 expected values\n  // example-end pairs\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_bits()\n{\n  // example-begin pairs-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1},  // thread 0 values\n     {0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_keys[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0},  // thread 0 values\n     {1}}; // thread 1 values\n  // example-end pairs-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs_descending()\n{\n  // example-begin pairs-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4},  // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2},  // thread 0 expected values\n     {4, 3, 5}}; // thread 1 expected values\n  // example-end pairs-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_bits()\n{\n  // example-begin pairs-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1},  // thread 0 values\n     {0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x],\n                    thread_values[threadIdx.x],\n                    decomposer_t{},\n                    begin_bit,\n                    end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60}  // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0},  // thread 0 expected values\n     {1}}; // thread 1 expected values\n  // example-end pairs-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_blocked_to_striped()\n{\n  // example-begin keys-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4}  //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5}  //\n     }};\n  // example-end keys-striped\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 59}, \n       {42.4, 1ll << 61}\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 60},\n       {24.2, 1ll << 62} \n     }};\n  // example-end keys-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_blocked_to_striped()\n{\n  // example-begin pairs-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3},  // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4}  //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 4},  // thread 0 values\n     {1, 3, 5}}; // thread 1 values\n  // example-end pairs-striped\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  int thread_values[2][2] = //\n    {{3, 2},  // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x],\n                          thread_values[threadIdx.x],\n                          decomposer_t{},\n                          begin_bit,\n                          end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 59}, \n       {42.4, 1ll << 61}\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 60},\n       {24.2, 1ll << 62} \n     }};\n\n  int expected_values[2][2] = //\n    {{0, 2},  // thread 0 values\n     {1, 3}}; // thread 1 values\n  // example-end pairs-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped()\n{\n  // example-begin keys-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3}  //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5}  //\n     }};\n  // example-end keys-striped-descending\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60}  //\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59}  //\n     }};\n  // example-end keys-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped()\n{\n  // example-begin pairs-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4},  // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x],\n                                    thread_values[threadIdx.x],\n                                    decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3}  //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 3},  // thread 0 values\n     {1, 4, 5}}; // thread 1 values\n  // example-end pairs-striped-descending\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  int thread_values[2][2] = //\n    {{3, 2},  // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x],\n                                    thread_values[threadIdx.x],\n                                    decomposer_t{},\n                                    begin_bit,\n                                    end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60}  //\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59}  //\n     }};\n\n  int expected_values[2][2] = //\n    {{3, 1},  // thread 0 values\n     {2, 0}}; // thread 1 values\n  // example-end pairs-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\nTEST_CASE(\"Block radix sort works in some corner cases\", \"[radix][sort][block]\")\n{\n  sort_keys<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#undef NDEBUG\n#include <cassert>\n\n#include \"catch2_test_block_radix_sort.cuh\"\n#include \"cub/block/radix_rank_sort_operations.cuh\"\n\n#include <algorithm>\n#include <type_traits>\n#include <utility>\n\n// example-begin custom-type\nstruct custom_t\n{\n  float f;\n  int unused;\n  long long int lli;\n\n  custom_t() = default;\n  __device__ custom_t(float f, long long int lli)\n      : f(f)\n      , unused(42)\n      , lli(lli)\n  {}\n};\n\nstatic __device__ bool operator==(const custom_t &lhs, const custom_t &rhs)\n{\n  return lhs.f == rhs.f && lhs.lli == rhs.lli;\n}\n\nstruct decomposer_t\n{\n  __device__ ::cuda::std::tuple<float &, long long int &> //\n  operator()(custom_t &key) const\n  {\n    return {key.f, key.lli};\n  }\n};\n// example-end custom-type\n\n__global__ void sort_keys()\n{\n  // example-begin keys\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2}  //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5}  //\n     }};\n  // example-end keys\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_bits()\n{\n  // example-begin keys-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n  // example-end keys-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_descending()\n{\n  // example-begin keys-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5}  //\n     }};\n  // example-end keys-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_bits()\n{\n  // example-begin keys-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60}  // thread 1 expected keys\n     }};\n  // example-end keys-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs()\n{\n  // example-begin pairs\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3},             // thread 0 values\n     {1, 2, 5}};            // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2}  //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2},  // thread 0 expected values\n     {3, 4, 5}}; // thread 1 expected values\n  // example-end pairs\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_bits()\n{\n  // example-begin pairs-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1},  // thread 0 values\n     {0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_keys[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0},  // thread 0 values\n     {1}}; // thread 1 values\n  // example-end pairs-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs_descending()\n{\n  // example-begin pairs-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4},  // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2},  // thread 0 expected values\n     {4, 3, 5}}; // thread 1 expected values\n  // example-end pairs-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_bits()\n{\n  // example-begin pairs-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1},  // thread 0 values\n     {0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x],\n                    thread_values[threadIdx.x],\n                    decomposer_t{},\n                    begin_bit,\n                    end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60}  // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0},  // thread 0 expected values\n     {1}}; // thread 1 expected values\n  // example-end pairs-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_blocked_to_striped()\n{\n  // example-begin keys-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4}  //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5}  //\n     }};\n  // example-end keys-striped\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 59}, \n       {42.4, 1ll << 61}\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 60},\n       {24.2, 1ll << 62} \n     }};\n  // example-end keys-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_blocked_to_striped()\n{\n  // example-begin pairs-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3},  // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4}  //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 4},  // thread 0 values\n     {1, 3, 5}}; // thread 1 values\n  // example-end pairs-striped\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  int thread_values[2][2] = //\n    {{3, 2},  // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x],\n                          thread_values[threadIdx.x],\n                          decomposer_t{},\n                          begin_bit,\n                          end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 59}, \n       {42.4, 1ll << 61}\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 60},\n       {24.2, 1ll << 62} \n     }};\n\n  int expected_values[2][2] = //\n    {{0, 2},  // thread 0 values\n     {1, 3}}; // thread 1 values\n  // example-end pairs-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped()\n{\n  // example-begin keys-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3}  //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5}  //\n     }};\n  // example-end keys-striped-descending\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60}  //\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59}  //\n     }};\n  // example-end keys-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped()\n{\n  // example-begin pairs-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0}  //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4},  // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x],\n                                    thread_values[threadIdx.x],\n                                    decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3}  //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5}  //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 3},  // thread 0 values\n     {1, 4, 5}}; // thread 1 values\n  // example-end pairs-striped-descending\n  \n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{ // thread 0 keys\n       {24.2, 1ll << 62}, \n       {42.4, 1ll << 61} \n     },\n     { // thread 1 keys\n       {42.4, 1ll << 60}, \n       {24.2, 1ll << 59}  \n     }};\n\n  int thread_values[2][2] = //\n    {{3, 2},  // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  const int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  const int end_bit = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  -----------> \n  //\n  // The bit subrange `[60, 68)` specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  -----------> \n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x],\n                                    thread_values[threadIdx.x],\n                                    decomposer_t{},\n                                    begin_bit,\n                                    end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{ // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60}  //\n     },\n     { // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59}  //\n     }};\n\n  int expected_values[2][2] = //\n    {{3, 1},  // thread 0 values\n     {2, 0}}; // thread 1 values\n  // example-end pairs-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\nTEST_CASE(\"Block radix sort works in some corner cases\", \"[radix][sort][block]\")\n{\n  sort_keys<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n", "source": "/kaggle/working/cub/test/catch2_test_block_radix_sort_custom.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/util_macro.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/warp/warp_merge_sort.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n\n#include <algorithm>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"c2h/custom_type.cuh\"\n#include \"catch2_test_helper.h\"\n\nstruct CustomLess\n{\n  template <typename T>\n  __device__ __host__ bool operator()(const T &lhs, const T &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting keys-only.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void\nwarp_merge_sort_kernel(T *in, T *out, SegmentSizeItT segment_sizes, T oob_default, ActionT action)\n{\n  using warp_merge_sort_t = cub::WarpMergeSort<T, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = threadIdx.x;\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  T thread_data[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  cub::WARP_SYNC(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, thread_data, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = (idx - warp_offset) >= valid_items ? oob_default : thread_data[item];\n  }\n}\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting key-value\n * pairs.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(KeyT *keys_in,\n                                       KeyT *keys_out,\n                                       ValueT *values_in,\n                                       ValueT *values_out,\n                                       SegmentSizeItT segment_sizes,\n                                       KeyT oob_default,\n                                       ActionT action)\n{\n  using warp_merge_sort_t =\n    cub::WarpMergeSort<KeyT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ValueT>;\n  using storage_t = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  KeyT keys[ITEMS_PER_THREAD];\n  ValueT values[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    keys[item]    = keys_in[idx];\n    values[item]  = values_in[idx];\n  }\n  cub::WARP_SYNC(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, keys, values, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx   = thread_offset + item;\n    keys_out[idx]   = (idx - warp_offset) >= valid_items ? oob_default : keys[item];\n    values_out[idx] = (idx - warp_offset) >= valid_items ? ValueT{} : values[item];\n  }\n}\n\n// -----------------------------------------------------------\n// Dimensions being instantiated:\n// {full,partial} x {stable, 'unstable'} x {keys, kv-pairs}\n// -----------------------------------------------------------\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on keys-only\n */\nstruct warp_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             T /*oob_default*/) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             T oob_default) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on keys-only\n */\nstruct warp_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             T /*oob_default*/) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             T oob_default) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on key-value pairs\n */\nstruct warp_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             KeyT /*oob_default*/) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             KeyT oob_default) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on key-value pairs\n */\nstruct warp_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             KeyT /*oob_default*/) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             KeyT oob_default) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Dispatch helper function for sorting keys\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(thrust::device_vector<T> &in,\n                     thrust::device_vector<T> &out,\n                     SegmentSizesItT segment_sizes,\n                     T oob_default,\n                     ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                segment_sizes,\n                                                oob_default,\n                                                action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Dispatch helper function for sorting key-value pairs\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(thrust::device_vector<KeyT> &keys_in,\n                     thrust::device_vector<KeyT> &keys_out,\n                     thrust::device_vector<ValueT> &values_in,\n                     thrust::device_vector<ValueT> &values_out,\n                     SegmentSizesItT segment_sizes,\n                     KeyT oob_default,\n                     ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(keys_in.data()),\n                                                thrust::raw_pointer_cast(keys_out.data()),\n                                                thrust::raw_pointer_cast(values_in.data()),\n                                                thrust::raw_pointer_cast(values_out.data()),\n                                                segment_sizes,\n                                                oob_default,\n                                                action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Performs a stable sort on per-warp segments of data and assigns oob_default to items that\n * are out-of-bounds.\n */\ntemplate <typename RandomItT, typename SegmentSizeItT, typename T>\nvoid compute_host_reference(RandomItT h_data,\n                            SegmentSizeItT segment_sizes,\n                            unsigned int num_segments,\n                            T oob_default,\n                            int logical_warp_items)\n{\n  for (unsigned int segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    unsigned int segment_size = segment_sizes[segment_id];\n    std::stable_sort(h_data, h_data + segment_size);\n    std::fill(h_data + segment_size, h_data + logical_warp_items, oob_default);\n    h_data += logical_warp_items;\n  }\n}\n\n/**\n * @brief Stability requirement of the sorting algorithm\n */\nenum class stability\n{\n  stable,\n  unstable\n};\n\n// List of key types to test\nusing custom_t =\n  c2h::custom_type_t<c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing key_types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t, custom_t>;\n\n// List of value types\nusing value_types = c2h::type_list<std::int32_t, custom_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 4>;\n\n// Number of items per thread to test\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 4, 7>;\n\n// Whether the sort is required to be stable or not\nusing stability_list = c2h::enum_type_list<stability, stability::stable, stability::unstable>;\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int logical_warp_items   = logical_warp_threads * items_per_thread;\n  static constexpr int total_warps          = 2;\n  static constexpr int tile_size            = items_per_thread * total_warps * logical_warp_threads;\n  static constexpr bool is_stable           = c2h::get<3, TestType>::value == stability::stable;\n};\n\nCUB_TEST(\"Warp sort on keys-only works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n  using warp_sort_delegate =\n    cub::detail::conditional_t<params::is_stable, warp_stable_sort_keys_t, warp_sort_keys_t>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  auto segment_sizes     = thrust::make_constant_iterator(params::logical_warp_items);\n  const auto oob_default = std::numeric_limits<type>::max();\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    segment_sizes,\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in_out = d_in;\n  compute_host_reference(h_in_out.begin(),\n                         segment_sizes,\n                         params::total_warps,\n                         oob_default,\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nCUB_TEST(\"Warp sort keys-only on partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  using warp_sort_delegate = cub::detail::\n    conditional_t<params::is_stable, warp_partial_stable_sort_keys_t, warp_partial_sort_keys_t>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = std::numeric_limits<type>::max();\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    d_segment_sizes.cbegin(),\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in_out     = d_in;\n  thrust::host_vector<int> segment_sizes = d_segment_sizes;\n  compute_host_reference(h_in_out.begin(),\n                         segment_sizes,\n                         params::total_warps,\n                         oob_default,\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nCUB_TEST(\"Warp sort on keys-value pairs works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params     = params_t<TestType>;\n  using key_type   = typename params::type;\n  using value_type = typename c2h::get<4, TestType>;\n  using warp_sort_delegate =\n    cub::detail::conditional_t<params::is_stable, warp_stable_sort_pairs_t, warp_sort_pairs_t>;\n\n  // Prepare test data\n  thrust::device_vector<key_type> d_keys_in(params::tile_size);\n  thrust::device_vector<key_type> d_keys_out(params::tile_size);\n  thrust::device_vector<value_type> d_values_in(params::tile_size);\n  thrust::device_vector<value_type> d_values_out(params::tile_size);\n  auto segment_sizes     = thrust::make_constant_iterator(params::logical_warp_items);\n  const auto oob_default = std::numeric_limits<key_type>::max();\n  c2h::gen(CUB_SEED(10), d_keys_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in,\n    d_keys_out,\n    d_values_in,\n    d_values_out,\n    segment_sizes,\n    oob_default,\n    warp_stable_sort_pairs_t{});\n\n  // Prepare verification data\n  thrust::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  thrust::host_vector<value_type> h_values_in_out = d_values_in;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(cpu_kv_pairs,\n                         segment_sizes,\n                         params::total_warps,\n                         thrust::make_tuple(oob_default, value_type{}),\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n\nCUB_TEST(\"Warp sort on key-value pairs of a partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params             = params_t<TestType>;\n  using key_type           = typename params::type;\n  using value_type         = typename c2h::get<4, TestType>;\n  using warp_sort_delegate = cub::detail::\n    conditional_t<params::is_stable, warp_partial_stable_sort_pairs_t, warp_partial_sort_pairs_t>;\n\n  // Prepare test data\n  thrust::device_vector<key_type> d_keys_in(params::tile_size);\n  thrust::device_vector<key_type> d_keys_out(params::tile_size);\n  thrust::device_vector<value_type> d_values_in(params::tile_size);\n  thrust::device_vector<value_type> d_values_out(params::tile_size);\n  thrust::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = std::numeric_limits<key_type>::max();\n  c2h::gen(CUB_SEED(5), d_keys_in);\n  c2h::gen(CUB_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in,\n    d_keys_out,\n    d_values_in,\n    d_values_out,\n    d_segment_sizes.cbegin(),\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  thrust::host_vector<value_type> h_values_in_out = d_values_in;\n  thrust::host_vector<int> segment_sizes          = d_segment_sizes;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(cpu_kv_pairs,\n                         segment_sizes,\n                         params::total_warps,\n                         thrust::make_tuple(oob_default, value_type{}),\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/warp/warp_merge_sort.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n\n#include <algorithm>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"c2h/custom_type.cuh\"\n#include \"catch2_test_helper.h\"\n\nstruct CustomLess\n{\n  template <typename T>\n  __device__ __host__ bool operator()(const T &lhs, const T &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting keys-only.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void\nwarp_merge_sort_kernel(T *in, T *out, SegmentSizeItT segment_sizes, T oob_default, ActionT action)\n{\n  using warp_merge_sort_t = hipcub::WarpMergeSort<T, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = threadIdx.x;\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  T thread_data[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  hipcub::WARP_SYNC(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, thread_data, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = (idx - warp_offset) >= valid_items ? oob_default : thread_data[item];\n  }\n}\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting key-value\n * pairs.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(KeyT *keys_in,\n                                       KeyT *keys_out,\n                                       ValueT *values_in,\n                                       ValueT *values_out,\n                                       SegmentSizeItT segment_sizes,\n                                       KeyT oob_default,\n                                       ActionT action)\n{\n  using warp_merge_sort_t =\n    hipcub::WarpMergeSort<KeyT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ValueT>;\n  using storage_t = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  KeyT keys[ITEMS_PER_THREAD];\n  ValueT values[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    keys[item]    = keys_in[idx];\n    values[item]  = values_in[idx];\n  }\n  hipcub::WARP_SYNC(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, keys, values, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx   = thread_offset + item;\n    keys_out[idx]   = (idx - warp_offset) >= valid_items ? oob_default : keys[item];\n    values_out[idx] = (idx - warp_offset) >= valid_items ? ValueT{} : values[item];\n  }\n}\n\n// -----------------------------------------------------------\n// Dimensions being instantiated:\n// {full,partial} x {stable, 'unstable'} x {keys, kv-pairs}\n// -----------------------------------------------------------\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on keys-only\n */\nstruct warp_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             T /*oob_default*/) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             T oob_default) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on keys-only\n */\nstruct warp_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             T /*oob_default*/) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             T (&thread_data)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             T oob_default) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on key-value pairs\n */\nstruct warp_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             KeyT /*oob_default*/) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             KeyT oob_default) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on key-value pairs\n */\nstruct warp_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int /*valid_items*/,\n                             KeyT /*oob_default*/) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(WarpSortT &warp_sort,\n                             KeyT (&keys)[ITEMS_PER_THREAD],\n                             ValueT (&values)[ITEMS_PER_THREAD],\n                             int valid_items,\n                             KeyT oob_default) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Dispatch helper function for sorting keys\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(thrust::device_vector<T> &in,\n                     thrust::device_vector<T> &out,\n                     SegmentSizesItT segment_sizes,\n                     T oob_default,\n                     ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                segment_sizes,\n                                                oob_default,\n                                                action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Dispatch helper function for sorting key-value pairs\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(thrust::device_vector<KeyT> &keys_in,\n                     thrust::device_vector<KeyT> &keys_out,\n                     thrust::device_vector<ValueT> &values_in,\n                     thrust::device_vector<ValueT> &values_out,\n                     SegmentSizesItT segment_sizes,\n                     KeyT oob_default,\n                     ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(keys_in.data()),\n                                                thrust::raw_pointer_cast(keys_out.data()),\n                                                thrust::raw_pointer_cast(values_in.data()),\n                                                thrust::raw_pointer_cast(values_out.data()),\n                                                segment_sizes,\n                                                oob_default,\n                                                action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Performs a stable sort on per-warp segments of data and assigns oob_default to items that\n * are out-of-bounds.\n */\ntemplate <typename RandomItT, typename SegmentSizeItT, typename T>\nvoid compute_host_reference(RandomItT h_data,\n                            SegmentSizeItT segment_sizes,\n                            unsigned int num_segments,\n                            T oob_default,\n                            int logical_warp_items)\n{\n  for (unsigned int segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    unsigned int segment_size = segment_sizes[segment_id];\n    std::stable_sort(h_data, h_data + segment_size);\n    std::fill(h_data + segment_size, h_data + logical_warp_items, oob_default);\n    h_data += logical_warp_items;\n  }\n}\n\n/**\n * @brief Stability requirement of the sorting algorithm\n */\nenum class stability\n{\n  stable,\n  unstable\n};\n\n// List of key types to test\nusing custom_t =\n  c2h::custom_type_t<c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing key_types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t, custom_t>;\n\n// List of value types\nusing value_types = c2h::type_list<std::int32_t, custom_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 4>;\n\n// Number of items per thread to test\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 4, 7>;\n\n// Whether the sort is required to be stable or not\nusing stability_list = c2h::enum_type_list<stability, stability::stable, stability::unstable>;\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int logical_warp_items   = logical_warp_threads * items_per_thread;\n  static constexpr int total_warps          = 2;\n  static constexpr int tile_size            = items_per_thread * total_warps * logical_warp_threads;\n  static constexpr bool is_stable           = c2h::get<3, TestType>::value == stability::stable;\n};\n\nCUB_TEST(\"Warp sort on keys-only works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n  using warp_sort_delegate =\n    hipcub::detail::conditional_t<params::is_stable, warp_stable_sort_keys_t, warp_sort_keys_t>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  auto segment_sizes     = thrust::make_constant_iterator(params::logical_warp_items);\n  const auto oob_default = std::numeric_limits<type>::max();\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    segment_sizes,\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in_out = d_in;\n  compute_host_reference(h_in_out.begin(),\n                         segment_sizes,\n                         params::total_warps,\n                         oob_default,\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nCUB_TEST(\"Warp sort keys-only on partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  using warp_sort_delegate = hipcub::detail::\n    conditional_t<params::is_stable, warp_partial_stable_sort_keys_t, warp_partial_sort_keys_t>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = std::numeric_limits<type>::max();\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    d_segment_sizes.cbegin(),\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in_out     = d_in;\n  thrust::host_vector<int> segment_sizes = d_segment_sizes;\n  compute_host_reference(h_in_out.begin(),\n                         segment_sizes,\n                         params::total_warps,\n                         oob_default,\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nCUB_TEST(\"Warp sort on keys-value pairs works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params     = params_t<TestType>;\n  using key_type   = typename params::type;\n  using value_type = typename c2h::get<4, TestType>;\n  using warp_sort_delegate =\n    hipcub::detail::conditional_t<params::is_stable, warp_stable_sort_pairs_t, warp_sort_pairs_t>;\n\n  // Prepare test data\n  thrust::device_vector<key_type> d_keys_in(params::tile_size);\n  thrust::device_vector<key_type> d_keys_out(params::tile_size);\n  thrust::device_vector<value_type> d_values_in(params::tile_size);\n  thrust::device_vector<value_type> d_values_out(params::tile_size);\n  auto segment_sizes     = thrust::make_constant_iterator(params::logical_warp_items);\n  const auto oob_default = std::numeric_limits<key_type>::max();\n  c2h::gen(CUB_SEED(10), d_keys_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in,\n    d_keys_out,\n    d_values_in,\n    d_values_out,\n    segment_sizes,\n    oob_default,\n    warp_stable_sort_pairs_t{});\n\n  // Prepare verification data\n  thrust::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  thrust::host_vector<value_type> h_values_in_out = d_values_in;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(cpu_kv_pairs,\n                         segment_sizes,\n                         params::total_warps,\n                         thrust::make_tuple(oob_default, value_type{}),\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n\nCUB_TEST(\"Warp sort on key-value pairs of a partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params             = params_t<TestType>;\n  using key_type           = typename params::type;\n  using value_type         = typename c2h::get<4, TestType>;\n  using warp_sort_delegate = hipcub::detail::\n    conditional_t<params::is_stable, warp_partial_stable_sort_pairs_t, warp_partial_sort_pairs_t>;\n\n  // Prepare test data\n  thrust::device_vector<key_type> d_keys_in(params::tile_size);\n  thrust::device_vector<key_type> d_keys_out(params::tile_size);\n  thrust::device_vector<value_type> d_values_in(params::tile_size);\n  thrust::device_vector<value_type> d_values_out(params::tile_size);\n  thrust::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = std::numeric_limits<key_type>::max();\n  c2h::gen(CUB_SEED(5), d_keys_in);\n  c2h::gen(CUB_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in,\n    d_keys_out,\n    d_values_in,\n    d_values_out,\n    d_segment_sizes.cbegin(),\n    oob_default,\n    warp_sort_delegate{});\n\n  // Prepare verification data\n  thrust::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  thrust::host_vector<value_type> h_values_in_out = d_values_in;\n  thrust::host_vector<int> segment_sizes          = d_segment_sizes;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(cpu_kv_pairs,\n                         segment_sizes,\n                         params::total_warps,\n                         thrust::make_tuple(oob_default, value_type{}),\n                         params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_warp_merge_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/detail/cpp_compatibility.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/warp/warp_load.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\n__global__ void warp_load_kernel(InputIteratorT input_iterator, ActionT action, int *error_counter)\n{\n  using warp_load_t = cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_load_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int linear_tid = threadIdx.x;\n\n  const int warp_id = linear_tid / LOGICAL_WARP_THREADS;\n  warp_load_t load(storage[warp_id]);\n\n  // Test WarpLoad specialization\n  T reg[ITEMS_PER_THREAD];\n  action.load(load, input_iterator + (warp_id * tile_size), reg);\n\n  // Verify data was loaded as expected\n  action.verify(reg, error_counter);\n}\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\nvoid warp_load(InputIteratorT input_iterator, ActionT action, int *error_counter)\n{\n  warp_load_kernel<LoadAlgorithm,\n                   LOGICAL_WARP_THREADS,\n                   ITEMS_PER_THREAD,\n                   TOTAL_WARPS,\n                   T,\n                   InputIteratorT,\n                   ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(input_iterator, action, error_counter);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief WarpLoad test specialisation for guarded loads\n */\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, typename T>\nstruct guarded_load_t\n{\n  int valid_items;\n  T oob_default;\n\n  template <int ITEMS_PER_THREAD, typename InputIteratorT>\n  __device__ void load(cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg, valid_items, oob_default);\n  }\n\n  template <int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int *error_counter)\n  {\n    const auto linear_tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n    const auto lane_id    = linear_tid % LOGICAL_WARP_THREADS;\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(linear_tid * ITEMS_PER_THREAD + item);\n\n      const bool is_oob = LoadAlgorithm == cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED\n                            ? item * LOGICAL_WARP_THREADS + lane_id >= valid_items\n                            : lane_id * ITEMS_PER_THREAD + item >= valid_items;\n\n      if (is_oob)\n      {\n        if (reg[item] != oob_default)\n        {\n          atomicAdd(error_counter, 1);\n        }\n      }\n      else if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\n/**\n * @brief WarpLoad test specialisation for unguarded loads\n */\nstruct unguarded_load_t\n{\n  template <cub::WarpLoadAlgorithm LoadAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename InputIteratorT>\n  __device__ void load(cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg);\n  }\n\n  template <typename T, int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int *error_counter)\n  {\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(threadIdx.x * ITEMS_PER_THREAD + item);\n\n      if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nthrust::device_vector<T> generate_input()\n{\n  const int tile_size = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  const int num_items = TOTAL_WARPS * tile_size;\n\n  thrust::device_vector<T> d_input(num_items);\n\n  if (LoadAlgorithm == cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED)\n  {\n    thrust::host_vector<T> h_input(num_items);\n\n    // In this case we need different stripe pattern, so the\n    // items/threads parameters are swapped\n\n    constexpr int FAKE_BLOCK_SIZE = ITEMS_PER_THREAD * TOTAL_WARPS;\n\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, FAKE_BLOCK_SIZE>(h_input.begin());\n    d_input = h_input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{num_items}, d_input);\n  }\n\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms           = c2h::enum_type_list<cub::WarpLoadAlgorithm,\n                                       cub::WarpLoadAlgorithm::WARP_LOAD_DIRECT,\n                                       cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED,\n                                       cub::WarpLoadAlgorithm::WARP_LOAD_TRANSPOSE,\n                                       cub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE>;\nusing algorithm =\n  c2h::enum_type_list<cub::WarpLoadAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_load_modifier = c2h::enum_type_list<cub::CacheLoadModifier,\n                                                cub::CacheLoadModifier::LOAD_DEFAULT,\n                                                cub::CacheLoadModifier::LOAD_CA,\n                                                cub::CacheLoadModifier::LOAD_CG,\n                                                cub::CacheLoadModifier::LOAD_CS,\n                                                cub::CacheLoadModifier::LOAD_CV,\n                                                cub::CacheLoadModifier::LOAD_LDG,\n                                                cub::CacheLoadModifier::LOAD_VOLATILE>;\n\nconstexpr int guarded_load_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads         = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread             = c2h::get<2, TestType>::value;\n  static constexpr cub::WarpLoadAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                  = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                    = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count             = total_warps * tile_size;\n};\n\nCUB_TEST(\"Warp load guarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n\n  const int valid_items =\n    GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(thrust::raw_pointer_cast(d_in.data()),\n                  delegate_t{valid_items, oob_default},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const int num_errors           = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load guarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n  constexpr cub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  const int valid_items =\n    GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  auto in_it =\n    cub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(in_it,\n                  delegate_t{valid_items, oob_default},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load unguarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = unguarded_load_t;\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(thrust::raw_pointer_cast(d_in.data()),\n                  delegate_t{},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load unguarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params                                   = params_t<TestType>;\n  using type                                     = typename params::type;\n  using delegate_t                               = unguarded_load_t;\n  constexpr cub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  auto in_it =\n    cub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(in_it, delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/detail/cpp_compatibility.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/warp/warp_load.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\n__global__ void warp_load_kernel(InputIteratorT input_iterator, ActionT action, int *error_counter)\n{\n  using warp_load_t = hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_load_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int linear_tid = threadIdx.x;\n\n  const int warp_id = linear_tid / LOGICAL_WARP_THREADS;\n  warp_load_t load(storage[warp_id]);\n\n  // Test WarpLoad specialization\n  T reg[ITEMS_PER_THREAD];\n  action.load(load, input_iterator + (warp_id * tile_size), reg);\n\n  // Verify data was loaded as expected\n  action.verify(reg, error_counter);\n}\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\nvoid warp_load(InputIteratorT input_iterator, ActionT action, int *error_counter)\n{\n  warp_load_kernel<LoadAlgorithm,\n                   LOGICAL_WARP_THREADS,\n                   ITEMS_PER_THREAD,\n                   TOTAL_WARPS,\n                   T,\n                   InputIteratorT,\n                   ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(input_iterator, action, error_counter);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief WarpLoad test specialisation for guarded loads\n */\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, typename T>\nstruct guarded_load_t\n{\n  int valid_items;\n  T oob_default;\n\n  template <int ITEMS_PER_THREAD, typename InputIteratorT>\n  __device__ void load(hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg, valid_items, oob_default);\n  }\n\n  template <int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int *error_counter)\n  {\n    const auto linear_tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n    const auto lane_id    = linear_tid % LOGICAL_WARP_THREADS;\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(linear_tid * ITEMS_PER_THREAD + item);\n\n      const bool is_oob = LoadAlgorithm == hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED\n                            ? item * LOGICAL_WARP_THREADS + lane_id >= valid_items\n                            : lane_id * ITEMS_PER_THREAD + item >= valid_items;\n\n      if (is_oob)\n      {\n        if (reg[item] != oob_default)\n        {\n          atomicAdd(error_counter, 1);\n        }\n      }\n      else if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\n/**\n * @brief WarpLoad test specialisation for unguarded loads\n */\nstruct unguarded_load_t\n{\n  template <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename InputIteratorT>\n  __device__ void load(hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg);\n  }\n\n  template <typename T, int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int *error_counter)\n  {\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(threadIdx.x * ITEMS_PER_THREAD + item);\n\n      if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nthrust::device_vector<T> generate_input()\n{\n  const int tile_size = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  const int num_items = TOTAL_WARPS * tile_size;\n\n  thrust::device_vector<T> d_input(num_items);\n\n  if (LoadAlgorithm == hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED)\n  {\n    thrust::host_vector<T> h_input(num_items);\n\n    // In this case we need different stripe pattern, so the\n    // items/threads parameters are swapped\n\n    constexpr int FAKE_BLOCK_SIZE = ITEMS_PER_THREAD * TOTAL_WARPS;\n\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, FAKE_BLOCK_SIZE>(h_input.begin());\n    d_input = h_input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{num_items}, d_input);\n  }\n\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms           = c2h::enum_type_list<hipcub::WarpLoadAlgorithm,\n                                       hipcub::WarpLoadAlgorithm::WARP_LOAD_DIRECT,\n                                       hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED,\n                                       hipcub::WarpLoadAlgorithm::WARP_LOAD_TRANSPOSE,\n                                       hipcub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE>;\nusing algorithm =\n  c2h::enum_type_list<hipcub::WarpLoadAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_load_modifier = c2h::enum_type_list<hipcub::CacheLoadModifier,\n                                                hipcub::CacheLoadModifier::LOAD_DEFAULT,\n                                                hipcub::CacheLoadModifier::LOAD_CA,\n                                                hipcub::CacheLoadModifier::LOAD_CG,\n                                                hipcub::CacheLoadModifier::LOAD_CS,\n                                                hipcub::CacheLoadModifier::LOAD_CV,\n                                                hipcub::CacheLoadModifier::LOAD_LDG,\n                                                hipcub::CacheLoadModifier::LOAD_VOLATILE>;\n\nconstexpr int guarded_load_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads         = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread             = c2h::get<2, TestType>::value;\n  static constexpr hipcub::WarpLoadAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                  = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                    = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count             = total_warps * tile_size;\n};\n\nCUB_TEST(\"Warp load guarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n\n  const int valid_items =\n    GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(thrust::raw_pointer_cast(d_in.data()),\n                  delegate_t{valid_items, oob_default},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const int num_errors           = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load guarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n  constexpr hipcub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  const int valid_items =\n    GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  auto in_it =\n    hipcub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(in_it,\n                  delegate_t{valid_items, oob_default},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load unguarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = unguarded_load_t;\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(thrust::raw_pointer_cast(d_in.data()),\n                  delegate_t{},\n                  thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nCUB_TEST(\"Warp load unguarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params                                   = params_t<TestType>;\n  using type                                     = typename params::type;\n  using delegate_t                               = unguarded_load_t;\n  constexpr hipcub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  auto d_in = generate_input<params::algorithm,\n                             params::logical_warp_threads,\n                             params::items_per_thread,\n                             params::total_warps,\n                             type>();\n  auto in_it =\n    hipcub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  thrust::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type>(in_it, delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors          = d_error_counter[0];\n  const int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_warp_load.cu"}
{"input": "/*******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"test_util.h\"\n#include \"cub/thread/thread_sort.cuh\"\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n#include <thrust/random.h>\n\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __host__ __device__ bool operator()(DataType &lhs, DataType &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n\ntemplate <typename KeyT,\n          typename ValueT,\n          int ItemsPerThread>\n__global__ void kernel(const KeyT *keys_in,\n                       KeyT *keys_out,\n                       const ValueT *values_in,\n                       ValueT *values_out)\n{\n  KeyT thread_keys[ItemsPerThread];\n  KeyT thread_values[ItemsPerThread];\n\n  const auto thread_offset = ItemsPerThread * threadIdx.x;\n  keys_in += thread_offset;\n  keys_out += thread_offset;\n  values_in += thread_offset;\n  values_out += thread_offset;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_keys[item] = keys_in[item];\n    thread_values[item] = values_in[item];\n  }\n\n  cub::StableOddEvenSort(thread_keys, thread_values, CustomLess{});\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    keys_out[item] = thread_keys[item];\n    values_out[item] = thread_values[item];\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT,\n          int ItemsPerThread>\nvoid Test()\n{\n  const unsigned int threads_in_block = 1024;\n  const unsigned int elements = threads_in_block * ItemsPerThread;\n\n  thrust::default_random_engine re;\n  thrust::device_vector<std::uint8_t> data_source(elements);\n\n  for (int iteration = 0; iteration < 10; iteration++)\n  {\n    thrust::sequence(data_source.begin(), data_source.end());\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    thrust::device_vector<KeyT> in_keys(data_source);\n    thrust::device_vector<KeyT> out_keys(elements);\n\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    thrust::device_vector<ValueT> in_values(data_source);\n    thrust::device_vector<ValueT> out_values(elements);\n\n    thrust::host_vector<KeyT> host_keys(in_keys);\n    thrust::host_vector<ValueT> host_values(in_values);\n\n    kernel<KeyT, ValueT, ItemsPerThread><<<1, threads_in_block>>>(\n      thrust::raw_pointer_cast(in_keys.data()),\n      thrust::raw_pointer_cast(out_keys.data()),\n      thrust::raw_pointer_cast(in_values.data()),\n      thrust::raw_pointer_cast(out_values.data()));\n\n    for (unsigned int tid = 0; tid < threads_in_block; tid++)\n    {\n      const auto thread_begin = tid * ItemsPerThread;\n      const auto thread_end = thread_begin + ItemsPerThread;\n\n      thrust::sort_by_key(host_keys.begin() + thread_begin,\n                          host_keys.begin() + thread_end,\n                          host_values.begin() + thread_begin,\n                          CustomLess{});\n    }\n\n    AssertEquals(host_keys, out_keys);\n    AssertEquals(host_values, out_values);\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid Test()\n{\n  Test<KeyT, ValueT, 2>();\n  Test<KeyT, ValueT, 3>();\n  Test<KeyT, ValueT, 4>();\n  Test<KeyT, ValueT, 5>();\n  Test<KeyT, ValueT, 7>();\n  Test<KeyT, ValueT, 8>();\n  Test<KeyT, ValueT, 9>();\n  Test<KeyT, ValueT, 11>();\n}\n\nint main()\n{\n  Test<std::uint32_t, std::uint32_t>();\n  Test<std::uint32_t, std::uint64_t>();\n\n  return 0;\n}\n", "output": "/*******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"test_util.h\"\n#include \"cub/thread/thread_sort.cuh\"\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n#include <thrust/random.h>\n\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __host__ __device__ bool operator()(DataType &lhs, DataType &rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n\ntemplate <typename KeyT,\n          typename ValueT,\n          int ItemsPerThread>\n__global__ void kernel(const KeyT *keys_in,\n                       KeyT *keys_out,\n                       const ValueT *values_in,\n                       ValueT *values_out)\n{\n  KeyT thread_keys[ItemsPerThread];\n  KeyT thread_values[ItemsPerThread];\n\n  const auto thread_offset = ItemsPerThread * threadIdx.x;\n  keys_in += thread_offset;\n  keys_out += thread_offset;\n  values_in += thread_offset;\n  values_out += thread_offset;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_keys[item] = keys_in[item];\n    thread_values[item] = values_in[item];\n  }\n\n  hipcub::StableOddEvenSort(thread_keys, thread_values, CustomLess{});\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    keys_out[item] = thread_keys[item];\n    values_out[item] = thread_values[item];\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT,\n          int ItemsPerThread>\nvoid Test()\n{\n  const unsigned int threads_in_block = 1024;\n  const unsigned int elements = threads_in_block * ItemsPerThread;\n\n  thrust::default_random_engine re;\n  thrust::device_vector<std::uint8_t> data_source(elements);\n\n  for (int iteration = 0; iteration < 10; iteration++)\n  {\n    thrust::sequence(data_source.begin(), data_source.end());\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    thrust::device_vector<KeyT> in_keys(data_source);\n    thrust::device_vector<KeyT> out_keys(elements);\n\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    thrust::device_vector<ValueT> in_values(data_source);\n    thrust::device_vector<ValueT> out_values(elements);\n\n    thrust::host_vector<KeyT> host_keys(in_keys);\n    thrust::host_vector<ValueT> host_values(in_values);\n\n    kernel<KeyT, ValueT, ItemsPerThread><<<1, threads_in_block>>>(\n      thrust::raw_pointer_cast(in_keys.data()),\n      thrust::raw_pointer_cast(out_keys.data()),\n      thrust::raw_pointer_cast(in_values.data()),\n      thrust::raw_pointer_cast(out_values.data()));\n\n    for (unsigned int tid = 0; tid < threads_in_block; tid++)\n    {\n      const auto thread_begin = tid * ItemsPerThread;\n      const auto thread_end = thread_begin + ItemsPerThread;\n\n      thrust::sort_by_key(host_keys.begin() + thread_begin,\n                          host_keys.begin() + thread_end,\n                          host_values.begin() + thread_begin,\n                          CustomLess{});\n    }\n\n    AssertEquals(host_keys, out_keys);\n    AssertEquals(host_values, out_values);\n  }\n}\n\n\ntemplate <typename KeyT,\n          typename ValueT>\nvoid Test()\n{\n  Test<KeyT, ValueT, 2>();\n  Test<KeyT, ValueT, 3>();\n  Test<KeyT, ValueT, 4>();\n  Test<KeyT, ValueT, 5>();\n  Test<KeyT, ValueT, 7>();\n  Test<KeyT, ValueT, 8>();\n  Test<KeyT, ValueT, 9>();\n  Test<KeyT, ValueT, 11>();\n}\n\nint main()\n{\n  Test<std::uint32_t, std::uint32_t>();\n  Test<std::uint32_t, std::uint64_t>();\n\n  return 0;\n}\n", "source": "/kaggle/working/cub/test/test_thread_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_load.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm LoadAlgorithm>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread,\n                  ThreadsInBlock,\n                  cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm LoadAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  using input_t      = cub::detail::value_t<InputIteratorT>;\n  using block_load_t = cub::BlockLoad<input_t,\n                                      ThreadsInBlock,\n                                      ItemsPerThread,\n                                      LoadAlgorithm>;\n  using storage_t =\n    typename block_load_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load_t block_load(storage);\n\n  input_t data[ItemsPerThread];\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_load.Load(input, data);\n  }\n  else\n  {\n    block_load.Load(input, data, num_items);\n  }\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread, ThreadsInBlock, LoadAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = data[i];\n    }\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm /* LoadAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread,\n                 ThreadsInBlock,\n                 cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm LoadAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_load(\n    InputIteratorT input,\n    OutputIteratorT output,\n    int num_items)\n{\n  using input_t = cub::detail::value_t<InputIteratorT>;\n  using block_load_t = cub::BlockLoad<input_t,\n                                      ThreadsInBlock,\n                                      ItemsPerThread,\n                                      LoadAlgorithm>;\n  using storage_t = typename block_load_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= 1024 * 48;\n\n  kernel<InputIteratorT,\n         OutputIteratorT,\n         ItemsPerThread,\n         ThreadsInBlock,\n         LoadAlgorithm><<<1, ThreadsInBlock>>>(\n             std::integral_constant<bool, sufficient_resources>{},\n             input, output, num_items);\n\n  REQUIRE( cudaSuccess == cudaPeekAtLastError() );\n  REQUIRE( cudaSuccess == cudaDeviceSynchronize() );\n}\n\n\n// %PARAM% IPT it 1:11\n\nusing types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing load_algorithm   = c2h::enum_type_list<\n  cub::BlockLoadAlgorithm,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE,             \n  cub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_load_algorithm = c2h::enum_type_list<\n  cub::BlockLoadAlgorithm,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n  cub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block load works with even block sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with even odd sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with even vector types\",\n         \"[load][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with custom types\",\n         \"[load][block]\",\n         items_per_thread,\n         load_algorithm)\n{\n  using type = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<items_per_thread,\n             threads_in_block,\n             load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                             thrust::raw_pointer_cast(d_output.data()),\n                             static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with caching iterators\",\n         \"[load][block]\",\n         items_per_thread,\n         load_algorithm)\n{\n  using type = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  cub::CacheModifiedInputIterator<cub::CacheLoadModifier::LOAD_DEFAULT, type> in(\n    thrust::raw_pointer_cast(d_input.data()));\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<items_per_thread,\n             threads_in_block,\n             load_algorithm>(in,\n                             thrust::raw_pointer_cast(d_output.data()),\n                             static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_load.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm LoadAlgorithm>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread,\n                  ThreadsInBlock,\n                  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm LoadAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  using input_t      = hipcub::detail::value_t<InputIteratorT>;\n  using block_load_t = hipcub::BlockLoad<input_t,\n                                      ThreadsInBlock,\n                                      ItemsPerThread,\n                                      LoadAlgorithm>;\n  using storage_t =\n    typename block_load_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load_t block_load(storage);\n\n  input_t data[ItemsPerThread];\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_load.Load(input, data);\n  }\n  else\n  {\n    block_load.Load(input, data, num_items);\n  }\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread, ThreadsInBlock, LoadAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = data[i];\n    }\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm /* LoadAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread,\n                 ThreadsInBlock,\n                 hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm LoadAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_load(\n    InputIteratorT input,\n    OutputIteratorT output,\n    int num_items)\n{\n  using input_t = hipcub::detail::value_t<InputIteratorT>;\n  using block_load_t = hipcub::BlockLoad<input_t,\n                                      ThreadsInBlock,\n                                      ItemsPerThread,\n                                      LoadAlgorithm>;\n  using storage_t = typename block_load_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= 1024 * 48;\n\n  kernel<InputIteratorT,\n         OutputIteratorT,\n         ItemsPerThread,\n         ThreadsInBlock,\n         LoadAlgorithm><<<1, ThreadsInBlock>>>(\n             std::integral_constant<bool, sufficient_resources>{},\n             input, output, num_items);\n\n  REQUIRE( hipSuccess == hipPeekAtLastError() );\n  REQUIRE( hipSuccess == hipDeviceSynchronize() );\n}\n\n\n// %PARAM% IPT it 1:11\n\nusing types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing load_algorithm   = c2h::enum_type_list<\n  hipcub::BlockLoadAlgorithm,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE,             \n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_load_algorithm = c2h::enum_type_list<\n  hipcub::BlockLoadAlgorithm,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n  hipcub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block load works with even block sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with even odd sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with even vector types\",\n         \"[load][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<params::items_per_thread,\n             params::threads_in_block,\n             params::load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                     thrust::raw_pointer_cast(d_output.data()),\n                                     static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with custom types\",\n         \"[load][block]\",\n         items_per_thread,\n         load_algorithm)\n{\n  using type = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<items_per_thread,\n             threads_in_block,\n             load_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                             thrust::raw_pointer_cast(d_output.data()),\n                             static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block load works with caching iterators\",\n         \"[load][block]\",\n         items_per_thread,\n         load_algorithm)\n{\n  using type = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = \n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  hipcub::CacheModifiedInputIterator<hipcub::CacheLoadModifier::LOAD_DEFAULT, type> in(\n    thrust::raw_pointer_cast(d_input.data()));\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_load<items_per_thread,\n             threads_in_block,\n             load_algorithm>(in,\n                             thrust::raw_pointer_cast(d_output.data()),\n                             static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_load.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_adjacent_difference.cuh>\n\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class T,\n          class ActionT>\n__global__ void block_adj_diff_kernel(T *data, ActionT action, bool in_place)\n{\n  using block_adjacent_differencet_t =\n    cub::BlockAdjacentDifference<T, ThreadsInBlock>;\n  using temp_storage_t = typename block_adjacent_differencet_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_in[ItemsPerThread];\n  T thread_out[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_in[item] = data[thread_offset + item];\n  }\n  __syncthreads();\n\n  block_adjacent_differencet_t adj_diff(temp_storage);\n\n  if (in_place)\n  {\n    action(adj_diff, thread_in, thread_in);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_in[item];\n    }\n  }\n  else\n  {\n    action(adj_diff, thread_in, thread_out);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_out[item];\n    }\n  }\n}\n\ntemplate <class T>\nstruct custom_difference_t\n{\n  __host__ __device__ T operator()(const T &lhs, const T &rhs)\n  {\n    return lhs - rhs;\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct base_op_t\n{\n  template <int ItemsPerThread, typename T, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ItemsPerThread],\n                             T (&output)[ItemsPerThread]) const\n  {\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, custom_difference_t<T>{});\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, custom_difference_t<T>{});\n    }\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct last_tile_op_t\n{\n  int m_valid_items{};\n\n  __host__ last_tile_op_t(int valid_items)\n    : m_valid_items(valid_items)\n  {}\n\n  template <int ITEMS_PER_THREAD,\n            typename T,\n            typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeftPartialTile(input,\n                                             output,\n                                             diff,\n                                             m_valid_items);\n    }\n    else\n    {\n      block_adj_diff.SubtractRightPartialTile(input,\n                                              output,\n                                              diff,\n                                              m_valid_items);\n    }\n  }\n};\n\ntemplate <class T, \n          bool ReadLeft>\nstruct middle_tile_op_t\n{\n  T m_neighbour_tile_value;\n\n  __host__ middle_tile_op_t(T neighbour_tile_value)\n    : m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input,\n                                  output,\n                                  diff,\n                                  m_neighbour_tile_value);\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input,\n                                   output,\n                                   diff,\n                                   m_neighbour_tile_value);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct last_tile_with_pred_op_t\n{\n  int m_valid_items;\n  T m_neighbour_tile_value;\n\n  __host__ last_tile_with_pred_op_t(\n      int valid_items,\n      T neighbour_tile_value)\n    : m_valid_items(valid_items)\n    , m_neighbour_tile_value(neighbour_tile_value)\n  {\n  }\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff, \n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n    block_adj_diff.SubtractLeftPartialTile(input,\n                                           output,\n                                           diff,\n                                           m_valid_items,\n                                           m_neighbour_tile_value);\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class T, class ActionT>\nvoid block_adj_diff(thrust::device_vector<T> &data,\n                    bool in_place,\n                    ActionT action)\n{\n  block_adj_diff_kernel<ThreadsInBlock, ItemsPerThread, T, ActionT>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(data.data()),\n                            action,\n                            in_place);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <bool ReadLeft, \n          class T>\nvoid host_adj_diff(thrust::host_vector<T> &h_data, int valid_items)\n{\n  custom_difference_t<T> diff{};\n\n  if (ReadLeft)\n  {\n    for (int i = valid_items - 1; i > 0; i--)\n    {\n      h_data[i] = diff(h_data[i], h_data[i - 1]);\n    }\n  }\n  else\n  {\n    for (int i = 0; i < valid_items - 1; i++)\n    {\n      h_data[i] = diff(h_data[i], h_data[i + 1]);\n    }\n  }\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(thrust::host_vector<T> &h_data,\n                   int valid_items,\n                   T neighbour_value)\n{\n  custom_difference_t<T> diff{};\n\n  host_adj_diff<ReadLeft>(h_data, valid_items);\n\n  if (valid_items == 0)\n  {\n    return;\n  }\n\n  if (ReadLeft)\n  {\n    h_data[0] = diff(h_data[0], neighbour_value);\n  }\n  else\n  {\n    h_data[valid_items - 1] = diff(h_data[valid_items - 1], neighbour_value);\n  }\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types =\n  c2h::type_list<std::uint16_t, std::int32_t, std::int64_t>;\n\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\nusing directions = c2h::enum_type_list<bool, false, true>;\nusing left_only = c2h::enum_type_list<bool, true>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr bool read_left = c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block adjacent difference works with full tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, params::tile_size);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    base_op_t<params::read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with last tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, valid_items);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    last_tile_op_t<params::read_left>{valid_items});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with single tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         left_only)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n  constexpr bool read_left = true;\n\n  thrust::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value = h_data[h_data.size() / 2];\n\n  host_adj_diff<read_left>(h_data, valid_items, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    last_tile_with_pred_op_t<key_t>{valid_items, neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with middle tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value = h_data[h_data.size() / 2]; \n\n  host_adj_diff<params::read_left>(h_data, params::tile_size, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    middle_tile_op_t<key_t, params::read_left>{neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference supports custom types\",\n         \"[adjacent difference][block]\",\n         threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::equal_comparable_t,\n                                   c2h::subtractable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = threads_in_block * items_per_thread;\n  constexpr bool read_left = true;\n  const bool in_place = true;\n\n  thrust::device_vector<key_t> d_data(tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<read_left>(h_data, tile_size);\n\n  block_adj_diff<items_per_thread, threads_in_block>(d_data,\n                                                     in_place,\n                                                     base_op_t<read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\n// TODO Test different input/output types\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_adjacent_difference.cuh>\n\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ThreadsInBlock,\n          int ItemsPerThread,\n          class T,\n          class ActionT>\n__global__ void block_adj_diff_kernel(T *data, ActionT action, bool in_place)\n{\n  using block_adjacent_differencet_t =\n    hipcub::BlockAdjacentDifference<T, ThreadsInBlock>;\n  using temp_storage_t = typename block_adjacent_differencet_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_in[ItemsPerThread];\n  T thread_out[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_in[item] = data[thread_offset + item];\n  }\n  __syncthreads();\n\n  block_adjacent_differencet_t adj_diff(temp_storage);\n\n  if (in_place)\n  {\n    action(adj_diff, thread_in, thread_in);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_in[item];\n    }\n  }\n  else\n  {\n    action(adj_diff, thread_in, thread_out);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_out[item];\n    }\n  }\n}\n\ntemplate <class T>\nstruct custom_difference_t\n{\n  __host__ __device__ T operator()(const T &lhs, const T &rhs)\n  {\n    return lhs - rhs;\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct base_op_t\n{\n  template <int ItemsPerThread, typename T, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ItemsPerThread],\n                             T (&output)[ItemsPerThread]) const\n  {\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, custom_difference_t<T>{});\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, custom_difference_t<T>{});\n    }\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct last_tile_op_t\n{\n  int m_valid_items{};\n\n  __host__ last_tile_op_t(int valid_items)\n    : m_valid_items(valid_items)\n  {}\n\n  template <int ITEMS_PER_THREAD,\n            typename T,\n            typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeftPartialTile(input,\n                                             output,\n                                             diff,\n                                             m_valid_items);\n    }\n    else\n    {\n      block_adj_diff.SubtractRightPartialTile(input,\n                                              output,\n                                              diff,\n                                              m_valid_items);\n    }\n  }\n};\n\ntemplate <class T, \n          bool ReadLeft>\nstruct middle_tile_op_t\n{\n  T m_neighbour_tile_value;\n\n  __host__ middle_tile_op_t(T neighbour_tile_value)\n    : m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff,\n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input,\n                                  output,\n                                  diff,\n                                  m_neighbour_tile_value);\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input,\n                                   output,\n                                   diff,\n                                   m_neighbour_tile_value);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct last_tile_with_pred_op_t\n{\n  int m_valid_items;\n  T m_neighbour_tile_value;\n\n  __host__ last_tile_with_pred_op_t(\n      int valid_items,\n      T neighbour_tile_value)\n    : m_valid_items(valid_items)\n    , m_neighbour_tile_value(neighbour_tile_value)\n  {\n  }\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff &block_adj_diff, \n                             T (&input)[ITEMS_PER_THREAD],\n                             T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n    block_adj_diff.SubtractLeftPartialTile(input,\n                                           output,\n                                           diff,\n                                           m_valid_items,\n                                           m_neighbour_tile_value);\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class T, class ActionT>\nvoid block_adj_diff(thrust::device_vector<T> &data,\n                    bool in_place,\n                    ActionT action)\n{\n  block_adj_diff_kernel<ThreadsInBlock, ItemsPerThread, T, ActionT>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(data.data()),\n                            action,\n                            in_place);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <bool ReadLeft, \n          class T>\nvoid host_adj_diff(thrust::host_vector<T> &h_data, int valid_items)\n{\n  custom_difference_t<T> diff{};\n\n  if (ReadLeft)\n  {\n    for (int i = valid_items - 1; i > 0; i--)\n    {\n      h_data[i] = diff(h_data[i], h_data[i - 1]);\n    }\n  }\n  else\n  {\n    for (int i = 0; i < valid_items - 1; i++)\n    {\n      h_data[i] = diff(h_data[i], h_data[i + 1]);\n    }\n  }\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(thrust::host_vector<T> &h_data,\n                   int valid_items,\n                   T neighbour_value)\n{\n  custom_difference_t<T> diff{};\n\n  host_adj_diff<ReadLeft>(h_data, valid_items);\n\n  if (valid_items == 0)\n  {\n    return;\n  }\n\n  if (ReadLeft)\n  {\n    h_data[0] = diff(h_data[0], neighbour_value);\n  }\n  else\n  {\n    h_data[valid_items - 1] = diff(h_data[valid_items - 1], neighbour_value);\n  }\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types =\n  c2h::type_list<std::uint16_t, std::int32_t, std::int64_t>;\n\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\nusing directions = c2h::enum_type_list<bool, false, true>;\nusing left_only = c2h::enum_type_list<bool, true>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr bool read_left = c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block adjacent difference works with full tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, params::tile_size);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    base_op_t<params::read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with last tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, valid_items);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    last_tile_op_t<params::read_left>{valid_items});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with single tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         left_only)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n  constexpr bool read_left = true;\n\n  thrust::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value = h_data[h_data.size() / 2];\n\n  host_adj_diff<read_left>(h_data, valid_items, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    last_tile_with_pred_op_t<key_t>{valid_items, neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference works with middle tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t = typename params::key_t;\n\n  thrust::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value = h_data[h_data.size() / 2]; \n\n  host_adj_diff<params::read_left>(h_data, params::tile_size, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data,\n    in_place,\n    middle_tile_op_t<key_t, params::read_left>{neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nCUB_TEST(\"Block adjacent difference supports custom types\",\n         \"[adjacent difference][block]\",\n         threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::equal_comparable_t,\n                                   c2h::subtractable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size = threads_in_block * items_per_thread;\n  constexpr bool read_left = true;\n  const bool in_place = true;\n\n  thrust::device_vector<key_t> d_data(tile_size);\n  c2h::gen(CUB_SEED(10), d_data);\n\n  thrust::host_vector<key_t> h_data = d_data;\n  host_adj_diff<read_left>(h_data, tile_size);\n\n  block_adj_diff<items_per_thread, threads_in_block>(d_data,\n                                                     in_place,\n                                                     base_op_t<read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\n// TODO Test different input/output types\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_adjacent_difference.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceRadixSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <algorithm>\n#include <climits>\n#include <cstdio>\n#include <limits>\n#include <memory>\n#include <random>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#if !_NVHPC_CUDA\n    #include <cuda_fp16.h>\n#endif\n\n#if !_NVHPC_CUDA\n    #include <cuda_bf16.h>\n#endif\n\n#include <cub/detail/device_synchronize.cuh>\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/device/device_segmented_radix_sort.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_math.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/host_vector.h>\n#include <thrust/mismatch.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose                       = false;\nint                     g_timing_iterations             = 0;\nstd::size_t             g_smallest_pre_sorted_num_items = (std::size_t(1) << 32) - 42;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,                        // CUB method (allows overwriting of input)\n    CUB_NO_OVERWRITE,           // CUB method (disallows overwriting of input)\n\n    CUB_SEGMENTED,              // CUB method (allows overwriting of input)\n    CUB_SEGMENTED_NO_OVERWRITE, // CUB method (disallows overwriting of input)\n\n    // Same as above, but launches kernels from device using CDP.\n    CDP,\n    CDP_NO_OVERWRITE,\n    CDP_SEGMENTED,\n    CDP_SEGMENTED_NO_OVERWRITE,\n};\n\nstatic const char* BackendToString(Backend b)\n{\n  switch (b)\n  {\n    case CUB:\n      return \"CUB\";\n    case CUB_NO_OVERWRITE:\n      return \"CUB_NO_OVERWRITE\";\n    case CUB_SEGMENTED:\n      return \"CUB_SEGMENTED\";\n    case CUB_SEGMENTED_NO_OVERWRITE:\n      return \"CUB_SEGMENTED_NO_OVERWRITE\";\n    case CDP:\n      return \"CDP\";\n    case CDP_NO_OVERWRITE:\n      return \"CDP_NO_OVERWRITE\";\n    case CDP_SEGMENTED:\n      return \"CDP_SEGMENTED\";\n    case CDP_SEGMENTED_NO_OVERWRITE:\n      return \"CDP_SEGMENTED_NO_OVERWRITE\";\n    default:\n      break;\n  }\n\n  return \"\";\n}\n\n//---------------------------------------------------------------------\n// Dispatch to different DeviceRadixSort entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to CUB sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<false>         /*is_descending*/,\n    Int2Type<CUB>           /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    cudaError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    return DeviceRadixSort::SortPairs(\n        d_temp_storage, temp_storage_bytes,\n        d_keys, d_values,\n        num_items, begin_bit, end_bit);\n}\n\n/**\n * Dispatch to CUB_NO_OVERWRITE sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<false>             /*is_descending*/,\n    Int2Type<CUB_NO_OVERWRITE>  /*dispatch_to*/,\n    int                         */*d_selector*/,\n    size_t                      */*d_temp_storage_bytes*/,\n    cudaError_t                 */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    KeyT      const *const_keys_itr     = d_keys.Current();\n    ValueT    const *const_values_itr   = d_values.Current();\n\n    cudaError_t retval = DeviceRadixSort::SortPairs(\n        d_temp_storage, temp_storage_bytes,\n        const_keys_itr, d_keys.Alternate(), const_values_itr, d_values.Alternate(),\n        num_items, begin_bit, end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n}\n\n/**\n * Dispatch to CUB sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<true>          /*is_descending*/,\n    Int2Type<CUB>           /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    cudaError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    return DeviceRadixSort::SortPairsDescending(\n        d_temp_storage, temp_storage_bytes,\n        d_keys, d_values, num_items,\n        begin_bit, end_bit);\n}\n\n\n/**\n * Dispatch to CUB_NO_OVERWRITE sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<true>              /*is_descending*/,\n    Int2Type<CUB_NO_OVERWRITE>  /*dispatch_to*/,\n    int                         */*d_selector*/,\n    size_t                      */*d_temp_storage_bytes*/,\n    cudaError_t                 */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    KeyT      const *const_keys_itr     = d_keys.Current();\n    ValueT    const *const_values_itr   = d_values.Current();\n\n    cudaError_t retval = DeviceRadixSort::SortPairsDescending(\n        d_temp_storage, temp_storage_bytes,\n        const_keys_itr, d_keys.Alternate(), const_values_itr, d_values.Alternate(),\n        num_items, begin_bit, end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n}\n\n//---------------------------------------------------------------------\n// Dispatch to different DeviceRadixSort entrypoints\n//---------------------------------------------------------------------\n\n// Validates that `num_items` fits into `int`\n// TODO(canonizer): remove this check once num_items is templated for segmented sort.\ntemplate <typename NumItemsT>\n__host__ __device__ bool ValidateNumItemsForSegmentedSort(NumItemsT num_items)\n{\n  if (static_cast<long long int>(num_items) <\n      static_cast<long long int>(INT_MAX))\n  {\n    return true;\n  }\n  else\n  {\n    printf(\"cub::DeviceSegmentedRadixSort is currently limited by %d items but \"\n           \"%lld were provided\\n\",\n           INT_MAX,\n           static_cast<long long int>(num_items));\n  }\n\n  return false;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<false>         /*is_descending*/,\n    Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    cudaError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    return DeviceSegmentedRadixSort::SortPairs(d_temp_storage,\n                                               temp_storage_bytes,\n                                               d_keys,\n                                               d_values,\n                                               static_cast<int>(num_items),\n                                               num_segments,\n                                               d_segment_begin_offsets,\n                                               d_segment_end_offsets,\n                                               begin_bit,\n                                               end_bit);\n  }\n\n  return cudaErrorInvalidValue;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED_NO_OVERWRITE sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<false>                         /*is_descending*/,\n    Int2Type<CUB_SEGMENTED_NO_OVERWRITE>    /*dispatch_to*/,\n    int                                     */*d_selector*/,\n    size_t                                  */*d_temp_storage_bytes*/,\n    cudaError_t                             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    KeyT const *const_keys_itr     = d_keys.Current();\n    ValueT const *const_values_itr = d_values.Current();\n\n    cudaError_t retval =\n      DeviceSegmentedRadixSort::SortPairs(d_temp_storage,\n                                          temp_storage_bytes,\n                                          const_keys_itr,\n                                          d_keys.Alternate(),\n                                          const_values_itr,\n                                          d_values.Alternate(),\n                                          static_cast<int>(num_items),\n                                          num_segments,\n                                          d_segment_begin_offsets,\n                                          d_segment_end_offsets,\n                                          begin_bit,\n                                          end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n  }\n\n  return cudaErrorInvalidValue;\n}\n\n\n/**\n * Dispatch to CUB_SEGMENTED sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<true>          /*is_descending*/,\n    Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    cudaError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    return DeviceSegmentedRadixSort::SortPairsDescending(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_keys,\n      d_values,\n      static_cast<int>(num_items),\n      num_segments,\n      d_segment_begin_offsets,\n      d_segment_end_offsets,\n      begin_bit,\n      end_bit);\n  }\n\n  return cudaErrorInvalidValue;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED_NO_OVERWRITE sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nCUB_RUNTIME_FUNCTION\ncudaError_t Dispatch(\n    Int2Type<true>                          /*is_descending*/,\n    Int2Type<CUB_SEGMENTED_NO_OVERWRITE>    /*dispatch_to*/,\n    int                                     */*d_selector*/,\n    size_t                                  */*d_temp_storage_bytes*/,\n    cudaError_t                             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    KeyT const *const_keys_itr     = d_keys.Current();\n    ValueT const *const_values_itr = d_values.Current();\n\n    cudaError_t retval =\n      DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage,\n                                                    temp_storage_bytes,\n                                                    const_keys_itr,\n                                                    d_keys.Alternate(),\n                                                    const_values_itr,\n                                                    d_values.Alternate(),\n                                                    static_cast<int>(num_items),\n                                                    num_segments,\n                                                    d_segment_begin_offsets,\n                                                    d_segment_end_offsets,\n                                                    begin_bit,\n                                                    end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n  }\n\n  return cudaErrorInvalidValue;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceRadixSort\n */\ntemplate <int IsDescending,\n          int CubBackend,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\n__global__ void CDPDispatchKernel(Int2Type<IsDescending> is_descending,\n                                  Int2Type<CubBackend>   cub_backend,\n                                  int                   *d_selector,\n                                  size_t                *d_temp_storage_bytes,\n                                  cudaError_t           *d_cdp_error,\n                                  void                  *d_temp_storage,\n                                  size_t                 temp_storage_bytes,\n                                  DoubleBuffer<KeyT>     d_keys,\n                                  DoubleBuffer<ValueT>   d_values,\n                                  NumItemsT              num_items,\n                                  int                    num_segments,\n                                  BeginOffsetIteratorT d_segment_begin_offsets,\n                                  EndOffsetIteratorT   d_segment_end_offsets,\n                                  int                  begin_bit,\n                                  int                  end_bit)\n{\n  *d_cdp_error = Dispatch(is_descending,\n                          cub_backend,\n                          d_selector,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys,\n                          d_values,\n                          num_items,\n                          num_segments,\n                          d_segment_begin_offsets,\n                          d_segment_end_offsets,\n                          begin_bit,\n                          end_bit);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n  *d_selector           = d_keys.selector;\n}\n\n/**\n * Launch kernel and dispatch on device. Should only be called from host code.\n * The CubBackend should be one of the non-CDP CUB backends to invoke from the\n * device.\n */\ntemplate <int IsDescending,\n          int CubBackend,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\ncudaError_t LaunchCDPKernel(Int2Type<IsDescending> is_descending,\n                            Int2Type<CubBackend>   cub_backend,\n                            int                   *d_selector,\n                            size_t                *d_temp_storage_bytes,\n                            cudaError_t           *d_cdp_error,\n\n                            void                 *d_temp_storage,\n                            size_t               &temp_storage_bytes,\n                            DoubleBuffer<KeyT>   &d_keys,\n                            DoubleBuffer<ValueT> &d_values,\n                            NumItemsT             num_items,\n                            int                   num_segments,\n                            BeginOffsetIteratorT  d_segment_begin_offsets,\n                            EndOffsetIteratorT    d_segment_end_offsets,\n                            int                   begin_bit,\n                            int                   end_bit)\n{\n  // Invoke kernel to invoke device-side dispatch:\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<IsDescending,\n                              CubBackend,\n                              KeyT,\n                              ValueT,\n                              BeginOffsetIteratorT,\n                              EndOffsetIteratorT,\n                              NumItemsT>,\n            is_descending,\n            cub_backend,\n            d_selector,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys,\n            d_values,\n            num_items,\n            num_segments,\n            d_segment_begin_offsets,\n            d_segment_end_offsets,\n            begin_bit,\n            end_bit);\n  CubDebugExit(retval);\n  CubDebugExit(cub::detail::device_synchronize());\n\n  // Copy out selector\n  CubDebugExit(cudaMemcpy(&d_keys.selector,\n                          d_selector,\n                          sizeof(int) * 1,\n                          cudaMemcpyDeviceToHost));\n  d_values.selector = d_keys.selector;\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  return retval;\n}\n\n// Specializations of Dispatch that translate the CDP backend to the appropriate\n// CUB backend, and uses the CUB backend to launch the CDP kernel.\n#define DEFINE_CDP_DISPATCHER(CdpBackend, CubBackend)                          \\\n  template <int IsDescending,                                                  \\\n            typename KeyT,                                                     \\\n            typename ValueT,                                                   \\\n            typename BeginOffsetIteratorT,                                     \\\n            typename EndOffsetIteratorT,                                       \\\n            typename NumItemsT>                                                \\\n  cudaError_t Dispatch(Int2Type<IsDescending> is_descending,                   \\\n                       Int2Type<CdpBackend> /*dispatch_to*/,                   \\\n                       int         *d_selector,                                \\\n                       size_t      *d_temp_storage_bytes,                      \\\n                       cudaError_t *d_cdp_error,                               \\\n                                                                               \\\n                       void                 *d_temp_storage,                   \\\n                       size_t               &temp_storage_bytes,               \\\n                       DoubleBuffer<KeyT>   &d_keys,                           \\\n                       DoubleBuffer<ValueT> &d_values,                         \\\n                       NumItemsT             num_items,                        \\\n                       int                   num_segments,                     \\\n                       BeginOffsetIteratorT  d_segment_begin_offsets,          \\\n                       EndOffsetIteratorT    d_segment_end_offsets,            \\\n                       int                   begin_bit,                        \\\n                       int                   end_bit)                          \\\n  {                                                                            \\\n    Int2Type<CubBackend> cub_backend{};                                        \\\n    return LaunchCDPKernel(is_descending,                                      \\\n                           cub_backend,                                        \\\n                           d_selector,                                         \\\n                           d_temp_storage_bytes,                               \\\n                           d_cdp_error,                                        \\\n                           d_temp_storage,                                     \\\n                           temp_storage_bytes,                                 \\\n                           d_keys,                                             \\\n                           d_values,                                           \\\n                           num_items,                                          \\\n                           num_segments,                                       \\\n                           d_segment_begin_offsets,                            \\\n                           d_segment_end_offsets,                              \\\n                           begin_bit,                                          \\\n                           end_bit);                                           \\\n  }\n\nDEFINE_CDP_DISPATCHER(CDP, CUB)\nDEFINE_CDP_DISPATCHER(CDP_NO_OVERWRITE, CUB_NO_OVERWRITE)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED, CUB_SEGMENTED)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED_NO_OVERWRITE, CUB_SEGMENTED_NO_OVERWRITE)\n\n#undef DEFINE_CDP_DISPATCHER\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n\n\n/**\n * Simple key-value pairing\n */\ntemplate <\n    typename KeyT,\n    typename ValueT>\nstruct Pair\n{\n    KeyT     key;\n    ValueT   value;\n\n    bool operator<(const Pair &b) const\n    {\n        return (key < b.key);\n    }\n};\n\n\n/**\n * Simple key-value pairing (specialized for bool types)\n */\ntemplate <typename ValueT>\nstruct Pair<bool, ValueT>\n{\n    bool     key;\n    ValueT   value;\n\n    bool operator<(const Pair &b) const\n    {\n        return (!key && b.key);\n    }\n};\n\n\n/**\n * Initialize key data\n */\ntemplate <typename KeyT, typename NumItemsT>\nvoid InitializeKeyBits(\n    GenMode         gen_mode,\n    KeyT            *h_keys,\n    NumItemsT       num_items,\n    int             /*entropy_reduction*/)\n{\n    for (NumItemsT i = 0; i < num_items; ++i)\n        InitValue(gen_mode, h_keys[i], i);\n}\n\ntemplate <typename KeyT,\n          typename UnsignedBits = typename cub::Traits<KeyT>::UnsignedBits>\nUnsignedBits KeyBits(KeyT key)\n{\n    UnsignedBits bits;\n    memcpy(&bits, &key, sizeof(KeyT));\n    return bits;\n}\n\n/** Initialize the reference array monotonically. */\ntemplate <typename KeyT, typename NumItemsT>\nvoid InitializeKeysSorted(\n    KeyT            *h_keys,\n    NumItemsT       num_items)\n{\n    using TraitsT = cub::Traits<KeyT>;\n    using UnsignedBits = typename TraitsT::UnsignedBits;\n\n    // Numbers to generate random runs.\n    UnsignedBits max_inc = 1 << (sizeof(UnsignedBits) < 4 ? 3 :\n                                 (sizeof(UnsignedBits) < 8 ? 14 : 24));\n    UnsignedBits min_bits = TraitsT::TwiddleIn(KeyBits(TraitsT::Lowest()));\n    UnsignedBits max_bits = TraitsT::TwiddleIn(KeyBits(TraitsT::Max()));\n    NumItemsT max_run = std::max(\n        NumItemsT(double(num_items) * (max_inc + 1) / max_bits),\n        NumItemsT(1 << 14));\n\n    UnsignedBits *h_key_bits = reinterpret_cast<UnsignedBits*>(h_keys);\n    NumItemsT i = 0;\n    // Start with the minimum twiddled key.\n    UnsignedBits twiddled_key = min_bits;\n    while (i < num_items)\n    {\n        // Generate random increment (avoid overflow).\n        UnsignedBits inc_bits = 0;\n        RandomBits(inc_bits);\n        // twiddled_key < max_bits at this point.\n        UnsignedBits inc = static_cast<UnsignedBits>(std::min(1 + inc_bits % max_inc, max_bits - twiddled_key));\n        twiddled_key += inc;\n\n        // Generate random run length (ensure there are enough values to fill the rest).\n        NumItemsT run_bits = 0;\n        RandomBits(run_bits);\n        NumItemsT run_length = std::min(1 + run_bits % max_run, num_items - i);\n        if (twiddled_key == max_bits) run_length = num_items - i;\n        NumItemsT run_end = i + run_length;\n\n        // Fill the array.\n        UnsignedBits key = TraitsT::TwiddleOut(twiddled_key);\n        // Avoid -0.0 for floating-point keys.\n        UnsignedBits negative_zero = UnsignedBits(1) << UnsignedBits(sizeof(UnsignedBits) * 8 - 1);\n        if (TraitsT::CATEGORY == cub::FLOATING_POINT && key == negative_zero)\n        {\n            key = 0;\n        }\n\n        for (; i < run_end; ++i)\n        {\n            h_key_bits[i] = key;\n        }\n    }\n}\n\n\n/**\n * Initialize solution\n */\ntemplate <bool IS_DESCENDING, bool WANT_RANKS, typename KeyT, typename NumItemsT>\nvoid InitializeSolution(\n    KeyT       *h_keys,\n    NumItemsT  num_items,\n    int        num_segments,\n    bool       pre_sorted,\n    NumItemsT  *h_segment_offsets,\n    int        begin_bit,\n    int        end_bit,\n    NumItemsT  *&h_reference_ranks,\n    KeyT       *&h_reference_keys)\n{\n    if (num_items == 0)\n    {\n        h_reference_ranks = nullptr;\n        h_reference_keys = nullptr;\n        return;\n    }\n\n    if (pre_sorted)\n    {\n        printf(\"Shuffling reference solution on CPU\\n\");\n        // Note: begin_bit and end_bit are ignored here, and assumed to have the\n        // default values (begin_bit == 0, end_bit == 8 * sizeof(KeyT)).\n        // Otherwise, pre-sorting won't work, as it doesn't necessarily\n        // correspond to the order of keys sorted by a subrange of bits.\n        // num_segments is also ignored as assumed to be 1, as pre-sorted tests\n        // are currently not supported for multiple segments.\n        //\n        // Pre-sorted tests with non-default begin_bit, end_bit or num_segments\n        // != 1 are skipped in TestBits() and TestSegments(), respectively.\n        AssertEquals(begin_bit, 0);\n        AssertEquals(end_bit, static_cast<int>(8 * sizeof(KeyT)));\n        AssertEquals(num_segments, 1);\n\n        // Copy to the reference solution.\n        h_reference_keys = new KeyT[num_items];\n        if (IS_DESCENDING)\n        {\n            // Copy in reverse.\n            for (NumItemsT i = 0; i < num_items; ++i)\n            {\n                h_reference_keys[i] = h_keys[num_items - 1 - i];\n            }\n            // Copy back.\n            memcpy(h_keys, h_reference_keys, num_items * sizeof(KeyT));\n        }\n        else\n        {\n            memcpy(h_reference_keys, h_keys, num_items * sizeof(KeyT));\n        }\n\n        // Summarize the pre-sorted array (element, 1st position, count).\n        struct Element\n        {\n            KeyT key;\n            NumItemsT num;\n            NumItemsT index;\n        };\n\n        std::vector<Element> summary;\n        KeyT cur_key = h_reference_keys[0];\n        summary.push_back(Element{cur_key, 1, 0});\n        for (NumItemsT i = 1; i < num_items; ++i)\n        {\n            KeyT key = h_reference_keys[i];\n            if (key == cur_key)\n            {\n                // Same key.\n                summary.back().num++;\n                continue;\n            }\n\n            // Different key.\n            cur_key = key;\n            summary.push_back(Element{cur_key, 1, i});\n        }\n\n        // Generate a random permutation from the summary. Such a complicated\n        // approach is used to permute the array and compute ranks in a\n        // cache-friendly way and in a short time.\n        if (WANT_RANKS)\n        {\n            h_reference_ranks = new NumItemsT[num_items];\n        }\n        NumItemsT max_run = 32;\n        NumItemsT run = 0;\n        NumItemsT i = 0;\n        while (summary.size() > 0)\n        {\n            // Pick up a random element and a run.\n            NumItemsT bits = 0;\n            RandomBits(bits);\n            NumItemsT summary_id = bits % summary.size();\n            Element& element = summary[summary_id];\n            run = std::min(1 + bits % (max_run - 1), element.num);\n            for (NumItemsT j = 0; j < run; ++j)\n            {\n                h_keys[i + j] = element.key;\n                if (WANT_RANKS)\n                {\n                    h_reference_ranks[element.index + j] = i + j;\n                }\n            }\n            i += run;\n            element.index += run;\n            element.num -= run;\n            if (element.num == 0)\n            {\n                // Remove the empty entry.\n                std::swap(summary[summary_id], summary.back());\n                summary.pop_back();\n            }\n        }\n        printf(\" Done.\\n\");\n    }\n    else\n    {\n        typedef Pair<KeyT, NumItemsT> PairT;\n\n        PairT *h_pairs = new PairT[num_items];\n\n        int num_bits = end_bit - begin_bit;\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n\n            // Mask off unwanted portions\n            if (num_bits < static_cast<int>(sizeof(KeyT) * 8))\n            {\n                using UnsignedBits = typename cub::Traits<KeyT>::UnsignedBits;\n\n                UnsignedBits base = 0;\n                memcpy(&base, &h_keys[i], sizeof(KeyT));\n                base &= ((UnsignedBits{1} << num_bits) - 1) << begin_bit;\n                memcpy(&h_pairs[i].key, &base, sizeof(KeyT));\n            }\n            else\n            {\n                h_pairs[i].key = h_keys[i];\n            }\n\n            h_pairs[i].value = i;\n        }\n\n        printf(\"\\nSorting reference solution on CPU \"\n               \"(%zd items, %d segments, %zd items/seg)...\",\n               static_cast<std::size_t>(num_items),\n               num_segments,\n               static_cast<std::size_t>(num_items / num_segments));\n        fflush(stdout);\n\n        for (int i = 0; i < num_segments; ++i)\n        {\n            if (IS_DESCENDING) std::reverse(h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n            std::stable_sort(               h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n            if (IS_DESCENDING) std::reverse(h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n        }\n\n        printf(\" Done.\\n\"); fflush(stdout);\n\n        if (WANT_RANKS)\n        {\n            h_reference_ranks  = new NumItemsT[num_items];\n        }\n        h_reference_keys   = new KeyT[num_items];\n\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n            if (WANT_RANKS)\n            {\n                h_reference_ranks[i]    = h_pairs[i].value;\n            }\n            h_reference_keys[i]     = h_keys[h_pairs[i].value];\n        }\n\n        if (h_pairs) delete[] h_pairs;\n    }\n}\n\ntemplate <bool IS_DESCENDING, typename KeyT, typename NumItemsT>\nvoid ResetKeys(KeyT *h_keys, NumItemsT num_items, bool pre_sorted, KeyT *reference_keys)\n{\n    if (!pre_sorted) return;\n\n    // Copy the reference keys back.\n    if (IS_DESCENDING)\n    {\n        // Keys need to be copied in reverse.\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n            h_keys[i] = reference_keys[num_items - 1 - i];\n        }\n    }\n    else\n    {\n        memcpy(h_keys, reference_keys, num_items * sizeof(KeyT));\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\ntemplate <typename T>\nstruct UnwrapHalfAndBfloat16 {\n    using Type = T;\n};\n\n#if !_NVHPC_CUDA\ntemplate <>\nstruct UnwrapHalfAndBfloat16<half_t> {\n    using Type = __half;\n};\n#endif\n\n#if !_NVHPC_CUDA\ntemplate <>\nstruct UnwrapHalfAndBfloat16<bfloat16_t> {\n    using Type = __nv_bfloat16;\n};\n#endif\n\ntemplate <class T, class NumItemsT>\nint compare_device_arrays(T* host_reference, T* d_tmp_buffer, T* d_data, NumItemsT num_items)\n{\n    CubDebugExit(cudaMemcpy(d_tmp_buffer, host_reference, sizeof(T) * num_items, cudaMemcpyHostToDevice));\n\n    auto d_reference_begin = d_tmp_buffer;\n    auto d_reference_end = d_tmp_buffer + num_items;\n    auto err = thrust::mismatch(thrust::device, d_reference_begin, d_reference_end, d_data);\n\n    if (err.first != d_reference_end)\n    {\n        const auto index = thrust::distance(d_reference_begin, err.first);\n        return CompareDeviceResults(host_reference + index, d_data + index, 1, true, g_verbose);\n    }\n\n    return 0;\n}\n\ntemplate <class NumItemsT>\nint compare_device_arrays(\n    CUB_NS_QUALIFIER::NullType */* h_reference */,\n    CUB_NS_QUALIFIER::NullType */* d_tmp_buffer */,\n    CUB_NS_QUALIFIER::NullType */* d_data */,\n    NumItemsT /* num_items */)\n{\n    return 0;\n}\n\n/**\n * Test DeviceRadixSort\n */\ntemplate <\n    Backend     BACKEND,\n    bool        IS_DESCENDING,\n    typename    KeyT,\n    typename    ValueT,\n    typename    BeginOffsetIteratorT,\n    typename    EndOffsetIteratorT,\n    typename    NumItemsT>\nvoid Test(\n    KeyT                 *h_keys,\n    ValueT               *h_values,\n    NumItemsT            num_items,\n    int                  num_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit,\n    KeyT                 *h_reference_keys,\n    ValueT               *h_reference_values)\n{\n    // Key alias type\n    using KeyAliasT = typename UnwrapHalfAndBfloat16<KeyT>::Type;\n\n    const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n    printf(\"%s %s cub::DeviceRadixSort %zd items, %d segments, \"\n           \"%d-byte keys (%s) %d-byte values (%s), %d-byte num_items (%s), \"\n           \"descending %d, begin_bit %d, end_bit %d\\n\",\n           BackendToString(BACKEND),\n           (KEYS_ONLY) ? \"keys-only\" : \"key-value\",\n           static_cast<std::size_t>(num_items),\n           num_segments,\n           static_cast<int>(sizeof(KeyT)),\n           typeid(KeyT).name(),\n           (KEYS_ONLY) ? 0 : static_cast<int>(sizeof(ValueT)),\n           typeid(ValueT).name(),\n           static_cast<int>(sizeof(NumItemsT)),\n           typeid(NumItemsT).name(),\n           IS_DESCENDING,\n           begin_bit,\n           end_bit);\n\n    if (g_verbose)\n    {\n        printf(\"Input keys:\\n\");\n        DisplayResults(h_keys, num_items);\n        printf(\"\\n\\n\");\n    }\n\n    // Allocate device arrays\n    DoubleBuffer<KeyAliasT> d_keys;\n    DoubleBuffer<ValueT>    d_values;\n    int                     *d_selector;\n    size_t                  *d_temp_storage_bytes;\n    cudaError_t             *d_cdp_error;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys.d_buffers[0], sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys.d_buffers[1], sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_selector, sizeof(int) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes, sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error, sizeof(cudaError_t) * 1));\n    if (!KEYS_ONLY)\n    {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values.d_buffers[0], sizeof(ValueT) * num_items));\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values.d_buffers[1], sizeof(ValueT) * num_items));\n    }\n\n    // Allocate temporary storage (and make it un-aligned)\n    size_t  temp_storage_bytes  = 0;\n    void    *d_temp_storage     = NULL;\n    CubDebugExit(Dispatch(\n        Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_keys, d_values,\n        num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        begin_bit, end_bit));\n\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes + 1));\n    void* mis_aligned_temp = static_cast<char*>(d_temp_storage) + 1;\n\n    // Initialize/clear device arrays\n    d_keys.selector = 0;\n    CubDebugExit(cudaMemcpy(d_keys.d_buffers[0], h_keys, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemset(d_keys.d_buffers[1], 0, sizeof(KeyT) * num_items));\n    if (!KEYS_ONLY)\n    {\n        d_values.selector = 0;\n        CubDebugExit(cudaMemcpy(d_values.d_buffers[0], h_values, sizeof(ValueT) * num_items, cudaMemcpyHostToDevice));\n        CubDebugExit(cudaMemset(d_values.d_buffers[1], 0, sizeof(ValueT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n        mis_aligned_temp, temp_storage_bytes, d_keys, d_values,\n        num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        begin_bit, end_bit));\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Check for correctness (and display results, if specified)\n    printf(\"Warmup done.  Checking results:\\n\"); fflush(stdout);\n\n    int compare = 0;\n\n    // If in/out API is used, we are not allowed to overwrite the input. \n    // Let's check that the input buffer is not overwritten by the algorithm.\n    if (BACKEND == CUB_NO_OVERWRITE)\n    {\n        KeyT *d_input_keys = reinterpret_cast<KeyT*>(d_keys.d_buffers[0]);\n\n        if (temp_storage_bytes < sizeof(KeyT) * num_items)\n        {\n            // For small input sizes, temporary storage is not large enough to fit keys.\n            compare = CompareDeviceResults(h_keys, d_input_keys, num_items, true, g_verbose);\n        }\n        else \n        {\n            // If overwrite is not allowed, temporary storage is large enough to fit keys.\n            KeyT* temp_keys = reinterpret_cast<KeyT*>(d_temp_storage);\n\n            compare = compare_device_arrays(h_keys, temp_keys, d_input_keys, num_items);\n            printf(\"\\t Compare input keys: %s \", compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n        }\n    }\n\n    // After the previous check is done, we can safely reuse alternative buffer to store \n    // the reference results and compare current output.\n    compare |= compare_device_arrays(h_reference_keys,\n                                     reinterpret_cast<KeyT *>(d_keys.Alternate()),\n                                     reinterpret_cast<KeyT *>(d_keys.Current()),\n                                     num_items);\n\n    printf(\"\\t Compare keys (selector %d): %s \", d_keys.selector, compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n\n    if (!KEYS_ONLY)\n    {\n        int values_compare = compare_device_arrays(h_reference_values,\n                                                   d_values.Alternate(),\n                                                   d_values.Current(),\n                                                   num_items);\n\n        compare |= values_compare;\n        printf(\"\\t Compare values (selector %d): %s \", d_values.selector, values_compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n    }\n\n    // Performance\n    if (g_timing_iterations)\n        printf(\"\\nPerforming timing iterations:\\n\"); fflush(stdout);\n\n    GpuTimer gpu_timer;\n    float elapsed_millis = 0.0f;\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Initialize/clear device arrays\n        CubDebugExit(cudaMemcpy(d_keys.d_buffers[d_keys.selector], h_keys, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n        CubDebugExit(cudaMemset(d_keys.d_buffers[d_keys.selector ^ 1], 0, sizeof(KeyT) * num_items));\n        if (!KEYS_ONLY)\n        {\n            CubDebugExit(cudaMemcpy(d_values.d_buffers[d_values.selector], h_values, sizeof(ValueT) * num_items, cudaMemcpyHostToDevice));\n            CubDebugExit(cudaMemset(d_values.d_buffers[d_values.selector ^ 1], 0, sizeof(ValueT) * num_items));\n        }\n\n        gpu_timer.Start();\n        CubDebugExit(Dispatch(\n            Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n            mis_aligned_temp, temp_storage_bytes, d_keys, d_values,\n            num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n            begin_bit, end_bit));\n        gpu_timer.Stop();\n        elapsed_millis += gpu_timer.ElapsedMillis();\n    }\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = (KEYS_ONLY) ?\n            giga_rate * sizeof(KeyT) * 2 :\n            giga_rate * (sizeof(KeyT) + sizeof(ValueT)) * 2;\n        printf(\"\\n%.3f elapsed ms, %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\", elapsed_millis, avg_millis, giga_rate, giga_bandwidth);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (d_keys.d_buffers[0]) CubDebugExit(g_allocator.DeviceFree(d_keys.d_buffers[0]));\n    if (d_keys.d_buffers[1]) CubDebugExit(g_allocator.DeviceFree(d_keys.d_buffers[1]));\n    if (d_values.d_buffers[0]) CubDebugExit(g_allocator.DeviceFree(d_values.d_buffers[0]));\n    if (d_values.d_buffers[1]) CubDebugExit(g_allocator.DeviceFree(d_values.d_buffers[1]));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_selector) CubDebugExit(g_allocator.DeviceFree(d_selector));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\n// Returns whether there is enough memory for the test.\ntemplate <typename KeyT, typename ValueT>\nbool HasEnoughMemory(std::size_t num_items, bool overwrite)\n{\n    std::size_t total_mem = TotalGlobalMem();\n    std::size_t value_size = std::is_same<ValueT, NullType>::value\n                           ? 0\n                           : sizeof(ValueT);\n    // A conservative estimate of the amount of memory required.\n    double factor = overwrite ? 2.25 : 3.25;\n    std::size_t test_mem = static_cast<std::size_t>\n      (num_items * (sizeof(KeyT) + value_size) * factor);\n    return test_mem < total_mem;\n}\n\n\n/**\n * Test backend\n */\ntemplate <bool IS_DESCENDING,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestBackend(KeyT                *h_keys,\n                 NumItemsT            num_items,\n                 int                  num_segments,\n                 BeginOffsetIteratorT d_segment_begin_offsets,\n                 EndOffsetIteratorT   d_segment_end_offsets,\n                 int                  begin_bit,\n                 int                  end_bit,\n                 KeyT                *h_reference_keys,\n                 NumItemsT           *h_reference_ranks)\n{\n#if TEST_CDP == 0\n  constexpr auto NonSegmentedOverwrite   = CUB;\n  constexpr auto NonSegmentedNoOverwrite = CUB_NO_OVERWRITE;\n  constexpr auto SegmentedOverwrite      = CUB_SEGMENTED;\n  constexpr auto SegmentedNoOverwrite    = CUB_SEGMENTED_NO_OVERWRITE;\n#else  // TEST_CDP\n  constexpr auto NonSegmentedOverwrite   = CDP;\n  constexpr auto NonSegmentedNoOverwrite = CDP_NO_OVERWRITE;\n  constexpr auto SegmentedOverwrite      = CDP_SEGMENTED;\n  constexpr auto SegmentedNoOverwrite    = CDP_SEGMENTED_NO_OVERWRITE;\n#endif // TEST_CDP\n\n  const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n  // A conservative check assuming overwrite is allowed.\n  if (!HasEnoughMemory<KeyT, ValueT>(static_cast<std::size_t>(num_items), true))\n  {\n    printf(\"Skipping the test due to insufficient device memory\\n\");\n    return;\n  }\n\n  std::unique_ptr<ValueT[]> h_value_data{};\n\n  ValueT *h_values           = nullptr;\n  ValueT *h_reference_values = nullptr;\n\n  if (!KEYS_ONLY)\n  {\n    h_value_data.reset(new ValueT[2 * static_cast<std::size_t>(num_items)]);\n    h_values           = h_value_data.get();\n    h_reference_values = h_value_data.get() + num_items;\n\n    for (NumItemsT i = 0; i < num_items; ++i)\n    {\n      InitValue(INTEGER_SEED, h_values[i], i);\n      InitValue(INTEGER_SEED, h_reference_values[i], h_reference_ranks[i]);\n    }\n  }\n\n  // Skip segmented sort if num_items isn't int.\n  // TODO(64bit-seg-sort): re-enable these tests once num_items is templated for\n  // segmented sort.\n  if (std::is_same<NumItemsT, int>::value)\n  {\n    printf(\"Testing segmented sort with overwrite\\n\");\n    Test<SegmentedOverwrite, IS_DESCENDING>(h_keys,\n                                            h_values,\n                                            num_items,\n                                            num_segments,\n                                            d_segment_begin_offsets,\n                                            d_segment_end_offsets,\n                                            begin_bit,\n                                            end_bit,\n                                            h_reference_keys,\n                                            h_reference_values);\n    printf(\"Testing segmented sort with no overwrite\\n\");\n    Test<SegmentedNoOverwrite, IS_DESCENDING>(h_keys,\n                                              h_values,\n                                              num_items,\n                                              num_segments,\n                                              d_segment_begin_offsets,\n                                              d_segment_end_offsets,\n                                              begin_bit,\n                                              end_bit,\n                                              h_reference_keys,\n                                              h_reference_values);\n  }\n  else\n  {\n    printf(\"Skipping segmented sort tests (NumItemsT != int)\\n\");\n  }\n\n  if (num_segments == 1)\n  {\n    printf(\"Testing non-segmented sort with overwrite\\n\");\n    Test<NonSegmentedOverwrite, IS_DESCENDING>(h_keys,\n                                               h_values,\n                                               num_items,\n                                               num_segments,\n                                               d_segment_begin_offsets,\n                                               d_segment_end_offsets,\n                                               begin_bit,\n                                               end_bit,\n                                               h_reference_keys,\n                                               h_reference_values);\n    if (HasEnoughMemory<KeyT, ValueT>(static_cast<std::size_t>(num_items),\n                                      false))\n    {\n      printf(\"Testing non-segmented sort with no overwrite\\n\");\n      Test<NonSegmentedNoOverwrite, IS_DESCENDING>(h_keys,\n                                                   h_values,\n                                                   num_items,\n                                                   num_segments,\n                                                   d_segment_begin_offsets,\n                                                   d_segment_end_offsets,\n                                                   begin_bit,\n                                                   end_bit,\n                                                   h_reference_keys,\n                                                   h_reference_values);\n    }\n    else\n    {\n      printf(\"Skipping no-overwrite tests with %zd items due to \"\n             \"insufficient memory\\n\",\n             static_cast<std::size_t>(num_items));\n    }\n  }\n}\n\n\n// Smallest value type for TEST_VALUE_TYPE.\n// Unless TEST_VALUE_TYPE == 3, this is the only value type tested.\n#if TEST_VALUE_TYPE == 0\n// Test keys-only\nusing SmallestValueT = NullType;\n#elif TEST_VALUE_TYPE == 1\n// Test with 8b value\nusing SmallestValueT = unsigned char;\n#elif TEST_VALUE_TYPE == 2\n// Test with 32b value\nusing SmallestValueT = unsigned int;\n// Test with 64b value\n#elif TEST_VALUE_TYPE == 3\nusing SmallestValueT = unsigned long long;\n#endif\n\n\n/**\n * Test value type\n */\ntemplate <bool IS_DESCENDING, typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename NumItemsT>\nvoid TestValueTypes(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit)\n{\n    // Initialize the solution\n    NumItemsT *h_reference_ranks = NULL;\n    KeyT *h_reference_keys = NULL;\n    // If TEST_VALUE_TYPE == 0, no values are sorted, only keys.\n    // Since ranks are only necessary when checking for values,\n    // they are not computed in this case.\n    InitializeSolution<IS_DESCENDING, TEST_VALUE_TYPE != 0>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, begin_bit, end_bit, h_reference_ranks, h_reference_keys);\n\n    TestBackend<IS_DESCENDING, KeyT, SmallestValueT>          (h_keys, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit, h_reference_keys, h_reference_ranks);\n\n#if TEST_VALUE_TYPE == 3\n    // Test with non-trivially-constructable value\n    // These are cheap to build, so lump them in with the 64b value tests.\n    TestBackend<IS_DESCENDING, KeyT, TestBar>           (h_keys, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit, h_reference_keys, h_reference_ranks);\n#endif\n\n    // Cleanup\n    ResetKeys<IS_DESCENDING>(h_keys, num_items, pre_sorted, h_reference_keys);\n    if (h_reference_ranks) delete[] h_reference_ranks;\n    if (h_reference_keys) delete[] h_reference_keys;\n}\n\n\n\n/**\n * Test ascending/descending\n */\ntemplate <typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestDirection(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit)\n{\n    TestValueTypes<true>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n    TestValueTypes<false>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n}\n\n\n/**\n * Test different bit ranges\n */\ntemplate <typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestBits(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets)\n{\n    // Don't test partial-word sorting for boolean, fp, or signed types (the bit-flipping techniques get in the way) or pre-sorted keys\n    if ((Traits<KeyT>::CATEGORY == UNSIGNED_INTEGER)\n        && (!std::is_same<KeyT, bool>::value)\n        && !pre_sorted)\n    {\n        // Partial bits\n        int begin_bit = 1;\n        int end_bit = (sizeof(KeyT) * 8) - 1;\n        printf(\"Testing key bits [%d,%d)\\n\", begin_bit, end_bit); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n\n        // Equal bits\n        begin_bit = end_bit = 0;\n        printf(\"Testing key bits [%d,%d)\\n\", begin_bit, end_bit); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n\n        // Across subword boundaries\n        int mid_bit = sizeof(KeyT) * 4;\n        printf(\"Testing key bits [%d,%d)\\n\", mid_bit - 1, mid_bit + 1); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, mid_bit - 1, mid_bit + 1);\n    }\n\n    printf(\"Testing key bits [%d,%d)\\n\", 0, int(sizeof(KeyT)) * 8); fflush(stdout);\n    TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, 0, sizeof(KeyT) * 8);\n}\n\n\ntemplate<typename OffsetT>\nstruct TransformFunctor1\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\ntemplate<typename OffsetT>\nstruct TransformFunctor2\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\n\n/**\n* Test different segment iterators\n*/\ntemplate <typename KeyT, typename NumItemsT>\nvoid TestSegmentIterators(\n    KeyT           *h_keys,\n    NumItemsT      num_items,\n    int            num_segments,\n    bool           pre_sorted,\n    NumItemsT     *h_segment_offsets,\n    NumItemsT     *d_segment_offsets)\n{\n    InitializeSegments(num_items, num_segments, h_segment_offsets);\n    CubDebugExit(cudaMemcpy(d_segment_offsets, h_segment_offsets, sizeof(NumItemsT) * (num_segments + 1), cudaMemcpyHostToDevice));\n\n    // Test with segment pointer.\n    // This is also used to test non-segmented sort.\n    TestBits(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_offsets, d_segment_offsets + 1);\n\n    if (num_segments > 1)\n    {\n        // Test with transform iterators of different types\n        typedef TransformFunctor1<NumItemsT> TransformFunctor1T;\n        typedef TransformFunctor2<NumItemsT> TransformFunctor2T;\n\n        TransformInputIterator<NumItemsT, TransformFunctor1T, NumItemsT*, NumItemsT> d_segment_begin_offsets_itr(d_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<NumItemsT, TransformFunctor2T, NumItemsT*, NumItemsT> d_segment_end_offsets_itr(d_segment_offsets + 1, TransformFunctor2T());\n\n        TestBits(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets_itr, d_segment_end_offsets_itr);\n    }\n}\n\n\n/**\n * Test different segment compositions\n */\ntemplate <typename KeyT, typename NumItemsT>\nvoid TestSegments(\n    KeyT         *h_keys,\n    NumItemsT    num_items,\n    int          max_segments,\n    bool         pre_sorted)\n{\n    max_segments = static_cast<int>(CUB_MIN(num_items, static_cast<NumItemsT>(max_segments)));\n    NumItemsT *h_segment_offsets = new NumItemsT[max_segments + 1];\n\n    NumItemsT *d_segment_offsets = nullptr;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(NumItemsT) * (max_segments + 1)));\n\n    for (int num_segments = max_segments; num_segments > 1; num_segments = cub::DivideAndRoundUp(num_segments, 64))\n    {\n        // Pre-sorted tests are not supported for segmented sort\n        if (num_items / num_segments < 128 * 1000 && !pre_sorted) {\n            // Right now we assign a single thread block to each segment, so lets keep it to under 128K items per segment\n            TestSegmentIterators(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_offsets);\n        }\n    }\n\n    // Test single segment\n    if (num_items > 0)\n    {\n      if (num_items < 128 * 1000 || pre_sorted)\n      {\n        // Right now we assign a single thread block to each segment, so lets\n        // keep it to under 128K items per segment\n        TestSegmentIterators(h_keys,\n                             num_items,\n                             1,\n                             pre_sorted,\n                             h_segment_offsets,\n                             d_segment_offsets);\n      }\n    }\n\n    if (h_segment_offsets) delete[] h_segment_offsets;\n    if (d_segment_offsets) CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n/** \n * Test different NumItemsT, i.e. types of num_items \n */\ntemplate <typename KeyT>\nvoid TestNumItems(KeyT         *h_keys,\n                  std::size_t  num_items,\n                  int          max_segments,\n                  bool         pre_sorted)\n{\n    if (!pre_sorted && num_items <= std::size_t(std::numeric_limits<int>::max()))\n    {\n        TestSegments<KeyT, int>(h_keys, static_cast<int>(num_items), max_segments, pre_sorted);\n    }\n    if (pre_sorted && num_items <= std::size_t(std::numeric_limits<std::uint32_t>::max()))\n    {\n        TestSegments<KeyT, std::uint32_t>(h_keys, static_cast<std::uint32_t>(num_items), max_segments, pre_sorted);\n    }\n    TestSegments<KeyT, std::size_t>(h_keys, num_items, max_segments, pre_sorted);\n}\n\n\n/**\n * Test different (sub)lengths and number of segments\n */\ntemplate <typename KeyT>\nvoid TestSizes(KeyT* h_keys,\n               std::size_t max_items,\n               int max_segments,\n               bool pre_sorted)\n{\n    if (pre_sorted)\n    {\n        // run a specific list of sizes, up to max_items\n        std::size_t sizes[] = {g_smallest_pre_sorted_num_items, 4350000007ull};\n        for (std::size_t num_items : sizes)\n        {\n            if (num_items > max_items) break;\n            TestNumItems(h_keys, num_items, max_segments, pre_sorted);\n        }\n    }\n    else\n    {\n        for (std::size_t num_items = max_items;\n             num_items > 1;\n             num_items = cub::DivideAndRoundUp(num_items, 64))\n        {\n            TestNumItems(h_keys, num_items, max_segments, pre_sorted);\n        }\n    }\n}\n\n/**\n * Test key sampling distributions\n */\ntemplate <typename KeyT, bool WITH_PRE_SORTED>\nvoid TestGen(\n    std::size_t     max_items,\n    int             max_segments)\n{\n    if (max_items == ~std::size_t(0))\n    {\n        max_items = 8000003;\n    }\n\n    if (max_segments < 0)\n    {\n        max_segments = 5003;\n    }\n\n    std::unique_ptr<KeyT[]> h_keys(new KeyT[max_items]);\n\n    // Test trivial problems sizes\n    h_keys[0] = static_cast<KeyT>(42);\n    TestNumItems(h_keys.get(), 0, 0, false);\n    TestNumItems(h_keys.get(), 1, 1, false);\n\n    for (int entropy_reduction = 0; entropy_reduction <= 6; entropy_reduction += 6)\n    {\n        printf(\"\\nTesting random %s keys with entropy reduction factor %d\\n\", typeid(KeyT).name(), entropy_reduction); fflush(stdout);\n        InitializeKeyBits(RANDOM, h_keys.get(), max_items, entropy_reduction);\n        TestSizes(h_keys.get(), max_items, max_segments, false);\n    }\n\n    if (cub::Traits<KeyT>::CATEGORY == cub::FLOATING_POINT)\n    {\n        printf(\"\\nTesting random %s keys with some replaced with -0.0 or +0.0 \\n\", typeid(KeyT).name());\n        fflush(stdout);\n        InitializeKeyBits(RANDOM_MINUS_PLUS_ZERO, h_keys.get(), max_items, 0);\n        // This just tests +/- 0 handling -- don't need to test multiple sizes\n        TestNumItems(h_keys.get(), max_items, max_segments, false);\n    }\n\n    printf(\"\\nTesting uniform %s keys\\n\", typeid(KeyT).name()); fflush(stdout);\n    InitializeKeyBits(UNIFORM, h_keys.get(), max_items, 0);\n    TestSizes(h_keys.get(), max_items, max_segments, false);\n\n    printf(\"\\nTesting natural number %s keys\\n\", typeid(KeyT).name()); fflush(stdout);\n    InitializeKeyBits(INTEGER_SEED, h_keys.get(), max_items, 0);\n    TestSizes(h_keys.get(), max_items, max_segments, false);\n\n    if (WITH_PRE_SORTED)\n    {\n        // Presorting is only used for testing large input arrays.\n        const std::size_t large_num_items = std::size_t(4350000007ull);\n\n        // A conservative check for memory, as we don't know ValueT or whether\n        // the overwrite is allowed until later.\n        // For ValueT, the check is actually exact unless TEST_VALUE_TYPE == 3.\n        if (!HasEnoughMemory<KeyT, SmallestValueT>(large_num_items, true))\n        {\n            printf(\"Skipping the permutation-based test due to insufficient device memory\\n\");\n            return;\n        }\n\n        h_keys.reset(nullptr); // Explicitly free old buffer before allocating.\n        h_keys.reset(new KeyT[large_num_items]);\n\n        printf(\"\\nTesting pre-sorted and randomly permuted %s keys\\n\", typeid(KeyT).name());\n        fflush(stdout);\n        InitializeKeysSorted(h_keys.get(), large_num_items);\n        fflush(stdout);\n        TestSizes(h_keys.get(), large_num_items, max_segments, true);\n        fflush(stdout);\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Simple test\n//---------------------------------------------------------------------\n\ntemplate <\n    Backend     BACKEND,\n    typename    KeyT,\n    typename    ValueT,\n    bool        IS_DESCENDING>\nvoid Test(\n    std::size_t num_items,\n    int         num_segments,\n    GenMode     gen_mode,\n    int         entropy_reduction,\n    int         begin_bit,\n    int         end_bit)\n{\n    const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n    KeyT         *h_keys             = new KeyT[num_items];\n    std::size_t  *h_reference_ranks  = NULL;\n    KeyT         *h_reference_keys   = NULL;\n    ValueT       *h_values           = NULL;\n    ValueT       *h_reference_values = NULL;\n    size_t       *h_segment_offsets  = new std::size_t[num_segments + 1];\n\n    std::size_t* d_segment_offsets = nullptr;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(std::size_t) * (num_segments + 1)));\n\n    if (end_bit < 0)\n        end_bit = sizeof(KeyT) * 8;\n\n    InitializeKeyBits(gen_mode, h_keys, num_items, entropy_reduction);\n    InitializeSegments(num_items, num_segments, h_segment_offsets);\n    CubDebugExit(cudaMemcpy(d_segment_offsets, h_segment_offsets, sizeof(std::size_t) * (num_segments + 1), cudaMemcpyHostToDevice));\n    InitializeSolution<IS_DESCENDING, !KEYS_ONLY>(\n        h_keys, num_items, num_segments, false, h_segment_offsets,\n        begin_bit, end_bit, h_reference_ranks, h_reference_keys);\n\n    if (!KEYS_ONLY)\n    {\n        h_values            = new ValueT[num_items];\n        h_reference_values  = new ValueT[num_items];\n\n        for (std::size_t i = 0; i < num_items; ++i)\n        {\n            InitValue(INTEGER_SEED, h_values[i], i);\n            InitValue(INTEGER_SEED, h_reference_values[i], h_reference_ranks[i]);\n        }\n    }\n    if (h_reference_ranks) delete[] h_reference_ranks;\n\n    printf(\"\\nTesting bits [%d,%d) of %s keys with gen-mode %d\\n\", begin_bit, end_bit, typeid(KeyT).name(), gen_mode); fflush(stdout);\n    Test<BACKEND, IS_DESCENDING>(\n        h_keys, h_values,\n        num_items, num_segments, d_segment_offsets, d_segment_offsets + 1,\n        begin_bit, end_bit, h_reference_keys, h_reference_values);\n\n    if (h_keys)             delete[] h_keys;\n    if (h_reference_keys)   delete[] h_reference_keys;\n    if (h_values)           delete[] h_values;\n    if (h_reference_values) delete[] h_reference_values;\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_segment_offsets) CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n#if TEST_VALUE_TYPE == 0\nvoid TestUnspecifiedRanges()\n{\n  const std::size_t num_items = 1024 * 1024;\n  const std::size_t max_segments = 42;\n  const std::size_t avg_segment_size = num_items / max_segments;\n\n  for (int iteration = 0; iteration < 4; iteration++)\n  {\n    thrust::host_vector<int> h_offsets_begin;\n    thrust::host_vector<int> h_offsets_end;\n\n    h_offsets_begin.reserve(max_segments + 1);\n    h_offsets_end.reserve(max_segments + 1);\n\n    {\n      int offset = 0;\n\n      for (std::size_t sid = 0; sid < max_segments; sid++)\n      {\n        const int segment_size = \n          static_cast<int>(RandomValue(avg_segment_size));\n\n        const bool segment_is_utilized = segment_size > 0 \n                                       && RandomValue(100) > 60;\n\n        if (segment_is_utilized)\n        {\n          h_offsets_begin.push_back(offset);\n          h_offsets_end.push_back(offset + segment_size);\n        }\n\n        offset += segment_size;\n      }\n\n      if (h_offsets_begin.empty())\n      {\n        h_offsets_begin.push_back(avg_segment_size);\n        h_offsets_end.push_back(num_items);\n      }\n    }\n\n    thrust::device_vector<int> keys(num_items);\n    thrust::device_vector<int> values(num_items);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    thrust::device_vector<int> d_offsets_begin = h_offsets_begin;\n    thrust::device_vector<int> d_offsets_end = h_offsets_end;\n\n    thrust::device_vector<int> expected_keys = keys;\n    thrust::device_vector<int> expected_values = values;\n\n    const int num_segments = static_cast<int>(h_offsets_begin.size());\n\n    thrust::device_vector<int> result_keys = keys;\n    thrust::device_vector<int> result_values = values;\n\n    for (int sid = 0; sid < num_segments; sid++)\n    {\n      const int segment_begin = h_offsets_begin[sid];\n      const int segment_end = h_offsets_end[sid];\n\n      thrust::sort_by_key(expected_keys.begin() + segment_begin,\n                          expected_keys.begin() + segment_end,\n                          expected_values.begin() + segment_begin);\n    }\n\n    {\n      cub::DoubleBuffer<int> keys_buffer(\n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()));\n\n      cub::DoubleBuffer<int> values_buffer(\n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()));\n\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{nullptr};\n\n      CubDebugExit(cub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(cub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      for (int sid = 0; sid < num_segments; sid++)\n      {\n        const int segment_begin = h_offsets_begin[sid];\n        const int segment_end = h_offsets_end[sid];\n\n        if (keys_buffer.selector == 0)\n        {\n          thrust::copy(\n              keys.begin() + segment_begin,\n              keys.begin() + segment_end,\n              result_keys.begin() + segment_begin);\n        }\n                       \n        if (values_buffer.selector == 0)\n        {\n          thrust::copy(\n              values.begin() + segment_begin,\n              values.begin() + segment_end,\n              result_values.begin() + segment_begin);\n        }\n      }\n    }\n\n    AssertEquals(result_keys, expected_keys); \n    AssertEquals(result_values, expected_values);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    result_keys = keys;\n    result_values = values;\n\n    {\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(cub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(cub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n  }\n}\n#endif\n\n#if TEST_KEY_BYTES == 4\n// Following tests check that new decomposer API doesn't break old API. \n// It's disabled because some compilers don't like implicit conversions, which\n// is required for the test. Once we figure out how to temporarily enable conversion, we can\n// re-enable the test.\n#define ENABLING_CONVERSION_IS_FIGURED_OUT 0\n#if ENABLING_CONVERSION_IS_FIGURED_OUT \nstruct bit_selector\n{\n  int bit;\n\n  operator int() const\n  {\n    return bit;\n  }\n};\n\ntemplate <class BeginBitT, class EndBitT, class... Ts>\nvoid device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(BeginBitT begin_bit, EndBitT end_bit, Ts... args)\n{\n  const int num_items = 0;\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortKeys(d_temp_storage,\n                                   temp_storage_bytes,\n                                   args...,\n                                   num_items,\n                                   begin_bit,\n                                   end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortKeys(d_temp_storage,\n                                   temp_storage_bytes,\n                                   args...,\n                                   num_items,\n                                   begin_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortKeysDescending(d_temp_storage,\n                                             temp_storage_bytes,\n                                             args...,\n                                             num_items,\n                                             begin_bit,\n                                             end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortKeysDescending(d_temp_storage,\n                                             temp_storage_bytes,\n                                             args...,\n                                             num_items,\n                                             begin_bit);\n  }\n}\n\ntemplate <class BeginBitT, class EndBitT, class... Ts>\nvoid device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(BeginBitT begin_bit,\n                                                                         EndBitT end_bit,\n                                                                         Ts... args)\n{\n  const int num_items = 0;\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortPairs(d_temp_storage,\n                                    temp_storage_bytes,\n                                    args...,\n                                    num_items,\n                                    begin_bit,\n                                    end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortPairs(d_temp_storage,\n                                    temp_storage_bytes,\n                                    args...,\n                                    num_items,\n                                    begin_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortPairsDescending(d_temp_storage,\n                                              temp_storage_bytes,\n                                              args...,\n                                              num_items,\n                                              begin_bit,\n                                              end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    cub::DeviceRadixSort::SortPairsDescending(d_temp_storage,\n                                              temp_storage_bytes,\n                                              args...,\n                                              num_items,\n                                              begin_bit);\n  }\n}\n\ntemplate <class BeginBitT, class EndBitT>\nvoid device_radix_sort_allows_implicit_conversions_for_bits(BeginBitT begin_bit, EndBitT end_bit)\n{\n  int *d_i_ptr = nullptr;\n\n  device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                     end_bit,\n                                                                     d_i_ptr,\n                                                                     d_i_ptr);\n\n  cub::DoubleBuffer<int> keys(d_i_ptr, d_i_ptr);\n  device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(begin_bit, end_bit, keys);\n\n  #if TEST_VALUE_TYPE == 2\n  unsigned int *d_u_ptr = nullptr;\n\n  device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                      end_bit,\n                                                                      d_i_ptr,\n                                                                      d_i_ptr,\n                                                                      d_u_ptr,\n                                                                      d_u_ptr);\n\n  cub::DoubleBuffer<unsigned int> pairs(d_u_ptr, d_u_ptr);\n  device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                      end_bit,\n                                                                      keys,\n                                                                      pairs);\n  #endif\n}\n\nvoid device_radix_sort_allows_implicit_conversions_for_bits()\n{\n  int begin_i = 0;\n  long long int begin_lli = 0;\n  bit_selector begin_bs{0};\n\n  int end_i = 2;\n  long long int end_lli = 2;\n  bit_selector end_bs{2};\n\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_bs);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_bs);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_bs);\n}\n#endif // ENABLING_CONVERSION_IS_FIGURED_OUT \n#endif // TEST_KEY_BYTES == 4\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    std::size_t num_items = ~std::size_t(0);\n    int num_segments = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"s\", num_segments);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--s=<num segments> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_KEY_BYTES bytes 1:2:4:8:16\n    // %PARAM% TEST_VALUE_TYPE pairs 0:1:2:3\n    //   0->Keys only\n    //   1->uchar\n    //   2->uint\n    //   3->[ull,TestBar] (TestBar is cheap to build, included here to\n    //                     reduce total number of targets)\n\n    // To reduce testing time, some key types are only tested when not\n    // testing pairs:\n#if TEST_VALUE_TYPE == 0\n#define TEST_EXTENDED_KEY_TYPES\n#endif\n\n    // Compile/run thorough tests\n#if TEST_KEY_BYTES == 1\n\n    TestGen<signed char, true>        (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<bool, false>              (num_items, num_segments);\n    TestGen<signed char, false>       (num_items, num_segments);\n    TestGen<unsigned char, false>     (num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 2\n    TestGen<unsigned short, true>     (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<short, false>             (num_items, num_segments);\n\n#if !_NVHPC_CUDA\n    TestGen<half_t, false>            (num_items, num_segments);\n#endif // CTK >= 9\n\n#if !_NVHPC_CUDA\n#if !defined(__ICC)\n    // Fails with `-0 != 0` with ICC for unknown reasons. See #333.\n    TestGen<bfloat16_t, false>        (num_items, num_segments);\n#endif // !ICC\n#endif // CTK >= 11\n\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 4\n\n    TestGen<int, true>                (num_items, num_segments);\n\n#if TEST_VALUE_TYPE == 0\n    TestUnspecifiedRanges();\n#endif\n\n#if ENABLING_CONVERSION_IS_FIGURED_OUT \n    device_radix_sort_allows_implicit_conversions_for_bits();\n#endif\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<float, false>             (num_items, num_segments);\n    TestGen<unsigned int, false>      (num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 8\n\n    TestGen<double, true>             (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<long long, false>         (num_items, num_segments);\n    TestGen<unsigned long long, false>(num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 16\n\n#if CUB_IS_INT128_ENABLED \n    TestGen<__int128_t,  false>(num_items, num_segments);\n    TestGen<__uint128_t, false>(num_items, num_segments);\n#else\n    // Fix unused static function for MSVC\n    BackendToString(CUB);\n#endif\n\n#endif // TEST_KEY_BYTES switch\n\n    return 0;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceRadixSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <algorithm>\n#include <climits>\n#include <cstdio>\n#include <limits>\n#include <memory>\n#include <random>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#if !_NVHPC_CUDA\n    #include <hip/hip_fp16.h>\n#endif\n\n#if !_NVHPC_CUDA\n    #include <hip/hip_bf16.h>\n#endif\n\n#include <cub/detail/device_synchronize.cuh>\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/device/device_segmented_radix_sort.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_math.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/host_vector.h>\n#include <thrust/mismatch.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose                       = false;\nint                     g_timing_iterations             = 0;\nstd::size_t             g_smallest_pre_sorted_num_items = (std::size_t(1) << 32) - 42;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,                        // CUB method (allows overwriting of input)\n    CUB_NO_OVERWRITE,           // CUB method (disallows overwriting of input)\n\n    CUB_SEGMENTED,              // CUB method (allows overwriting of input)\n    CUB_SEGMENTED_NO_OVERWRITE, // CUB method (disallows overwriting of input)\n\n    // Same as above, but launches kernels from device using CDP.\n    CDP,\n    CDP_NO_OVERWRITE,\n    CDP_SEGMENTED,\n    CDP_SEGMENTED_NO_OVERWRITE,\n};\n\nstatic const char* BackendToString(Backend b)\n{\n  switch (b)\n  {\n    case CUB:\n      return \"CUB\";\n    case CUB_NO_OVERWRITE:\n      return \"CUB_NO_OVERWRITE\";\n    case CUB_SEGMENTED:\n      return \"CUB_SEGMENTED\";\n    case CUB_SEGMENTED_NO_OVERWRITE:\n      return \"CUB_SEGMENTED_NO_OVERWRITE\";\n    case CDP:\n      return \"CDP\";\n    case CDP_NO_OVERWRITE:\n      return \"CDP_NO_OVERWRITE\";\n    case CDP_SEGMENTED:\n      return \"CDP_SEGMENTED\";\n    case CDP_SEGMENTED_NO_OVERWRITE:\n      return \"CDP_SEGMENTED_NO_OVERWRITE\";\n    default:\n      break;\n  }\n\n  return \"\";\n}\n\n//---------------------------------------------------------------------\n// Dispatch to different DeviceRadixSort entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to CUB sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<false>         /*is_descending*/,\n    Int2Type<CUB>           /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    hipError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    return DeviceRadixSort::SortPairs(\n        d_temp_storage, temp_storage_bytes,\n        d_keys, d_values,\n        num_items, begin_bit, end_bit);\n}\n\n/**\n * Dispatch to CUB_NO_OVERWRITE sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<false>             /*is_descending*/,\n    Int2Type<CUB_NO_OVERWRITE>  /*dispatch_to*/,\n    int                         */*d_selector*/,\n    size_t                      */*d_temp_storage_bytes*/,\n    hipError_t                 */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    KeyT      const *const_keys_itr     = d_keys.Current();\n    ValueT    const *const_values_itr   = d_values.Current();\n\n    hipError_t retval = DeviceRadixSort::SortPairs(\n        d_temp_storage, temp_storage_bytes,\n        const_keys_itr, d_keys.Alternate(), const_values_itr, d_values.Alternate(),\n        num_items, begin_bit, end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n}\n\n/**\n * Dispatch to CUB sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<true>          /*is_descending*/,\n    Int2Type<CUB>           /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    hipError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    return DeviceRadixSort::SortPairsDescending(\n        d_temp_storage, temp_storage_bytes,\n        d_keys, d_values, num_items,\n        begin_bit, end_bit);\n}\n\n\n/**\n * Dispatch to CUB_NO_OVERWRITE sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<true>              /*is_descending*/,\n    Int2Type<CUB_NO_OVERWRITE>  /*dispatch_to*/,\n    int                         */*d_selector*/,\n    size_t                      */*d_temp_storage_bytes*/,\n    hipError_t                 */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     /*num_segments*/,\n    BeginOffsetIteratorT    /*d_segment_begin_offsets*/,\n    EndOffsetIteratorT      /*d_segment_end_offsets*/,\n    int                     begin_bit,\n    int                     end_bit)\n{\n    KeyT      const *const_keys_itr     = d_keys.Current();\n    ValueT    const *const_values_itr   = d_values.Current();\n\n    hipError_t retval = DeviceRadixSort::SortPairsDescending(\n        d_temp_storage, temp_storage_bytes,\n        const_keys_itr, d_keys.Alternate(), const_values_itr, d_values.Alternate(),\n        num_items, begin_bit, end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n}\n\n//---------------------------------------------------------------------\n// Dispatch to different DeviceRadixSort entrypoints\n//---------------------------------------------------------------------\n\n// Validates that `num_items` fits into `int`\n// TODO(canonizer): remove this check once num_items is templated for segmented sort.\ntemplate <typename NumItemsT>\n__host__ __device__ bool ValidateNumItemsForSegmentedSort(NumItemsT num_items)\n{\n  if (static_cast<long long int>(num_items) <\n      static_cast<long long int>(INT_MAX))\n  {\n    return true;\n  }\n  else\n  {\n    printf(\"hipcub::DeviceSegmentedRadixSort is currently limited by %d items but \"\n           \"%lld were provided\\n\",\n           INT_MAX,\n           static_cast<long long int>(num_items));\n  }\n\n  return false;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<false>         /*is_descending*/,\n    Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    hipError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    return DeviceSegmentedRadixSort::SortPairs(d_temp_storage,\n                                               temp_storage_bytes,\n                                               d_keys,\n                                               d_values,\n                                               static_cast<int>(num_items),\n                                               num_segments,\n                                               d_segment_begin_offsets,\n                                               d_segment_end_offsets,\n                                               begin_bit,\n                                               end_bit);\n  }\n\n  return hipErrorInvalidValue;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED_NO_OVERWRITE sorting entrypoint (specialized for ascending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<false>                         /*is_descending*/,\n    Int2Type<CUB_SEGMENTED_NO_OVERWRITE>    /*dispatch_to*/,\n    int                                     */*d_selector*/,\n    size_t                                  */*d_temp_storage_bytes*/,\n    hipError_t                             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    KeyT const *const_keys_itr     = d_keys.Current();\n    ValueT const *const_values_itr = d_values.Current();\n\n    hipError_t retval =\n      DeviceSegmentedRadixSort::SortPairs(d_temp_storage,\n                                          temp_storage_bytes,\n                                          const_keys_itr,\n                                          d_keys.Alternate(),\n                                          const_values_itr,\n                                          d_values.Alternate(),\n                                          static_cast<int>(num_items),\n                                          num_segments,\n                                          d_segment_begin_offsets,\n                                          d_segment_end_offsets,\n                                          begin_bit,\n                                          end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n  }\n\n  return hipErrorInvalidValue;\n}\n\n\n/**\n * Dispatch to CUB_SEGMENTED sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<true>          /*is_descending*/,\n    Int2Type<CUB_SEGMENTED> /*dispatch_to*/,\n    int                     */*d_selector*/,\n    size_t                  */*d_temp_storage_bytes*/,\n    hipError_t             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    return DeviceSegmentedRadixSort::SortPairsDescending(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_keys,\n      d_values,\n      static_cast<int>(num_items),\n      num_segments,\n      d_segment_begin_offsets,\n      d_segment_end_offsets,\n      begin_bit,\n      end_bit);\n  }\n\n  return hipErrorInvalidValue;\n}\n\n/**\n * Dispatch to CUB_SEGMENTED_NO_OVERWRITE sorting entrypoint (specialized for descending)\n */\ntemplate <typename KeyT, typename ValueT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nHIPCUB_RUNTIME_FUNCTION\nhipError_t Dispatch(\n    Int2Type<true>                          /*is_descending*/,\n    Int2Type<CUB_SEGMENTED_NO_OVERWRITE>    /*dispatch_to*/,\n    int                                     */*d_selector*/,\n    size_t                                  */*d_temp_storage_bytes*/,\n    hipError_t                             */*d_cdp_error*/,\n\n    void*                   d_temp_storage,\n    size_t&                 temp_storage_bytes,\n    DoubleBuffer<KeyT>      &d_keys,\n    DoubleBuffer<ValueT>    &d_values,\n    NumItemsT               num_items,\n    int                     num_segments,\n    BeginOffsetIteratorT    d_segment_begin_offsets,\n    EndOffsetIteratorT      d_segment_end_offsets,\n    int                     begin_bit,\n    int                     end_bit)\n{\n  if (ValidateNumItemsForSegmentedSort(num_items))\n  {\n    KeyT const *const_keys_itr     = d_keys.Current();\n    ValueT const *const_values_itr = d_values.Current();\n\n    hipError_t retval =\n      DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage,\n                                                    temp_storage_bytes,\n                                                    const_keys_itr,\n                                                    d_keys.Alternate(),\n                                                    const_values_itr,\n                                                    d_values.Alternate(),\n                                                    static_cast<int>(num_items),\n                                                    num_segments,\n                                                    d_segment_begin_offsets,\n                                                    d_segment_end_offsets,\n                                                    begin_bit,\n                                                    end_bit);\n\n    d_keys.selector ^= 1;\n    d_values.selector ^= 1;\n    return retval;\n  }\n\n  return hipErrorInvalidValue;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceRadixSort\n */\ntemplate <int IsDescending,\n          int CubBackend,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\n__global__ void CDPDispatchKernel(Int2Type<IsDescending> is_descending,\n                                  Int2Type<CubBackend>   cub_backend,\n                                  int                   *d_selector,\n                                  size_t                *d_temp_storage_bytes,\n                                  hipError_t           *d_cdp_error,\n                                  void                  *d_temp_storage,\n                                  size_t                 temp_storage_bytes,\n                                  DoubleBuffer<KeyT>     d_keys,\n                                  DoubleBuffer<ValueT>   d_values,\n                                  NumItemsT              num_items,\n                                  int                    num_segments,\n                                  BeginOffsetIteratorT d_segment_begin_offsets,\n                                  EndOffsetIteratorT   d_segment_end_offsets,\n                                  int                  begin_bit,\n                                  int                  end_bit)\n{\n  *d_cdp_error = Dispatch(is_descending,\n                          cub_backend,\n                          d_selector,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys,\n                          d_values,\n                          num_items,\n                          num_segments,\n                          d_segment_begin_offsets,\n                          d_segment_end_offsets,\n                          begin_bit,\n                          end_bit);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n  *d_selector           = d_keys.selector;\n}\n\n/**\n * Launch kernel and dispatch on device. Should only be called from host code.\n * The CubBackend should be one of the non-CDP CUB backends to invoke from the\n * device.\n */\ntemplate <int IsDescending,\n          int CubBackend,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\nhipError_t LaunchCDPKernel(Int2Type<IsDescending> is_descending,\n                            Int2Type<CubBackend>   cub_backend,\n                            int                   *d_selector,\n                            size_t                *d_temp_storage_bytes,\n                            hipError_t           *d_cdp_error,\n\n                            void                 *d_temp_storage,\n                            size_t               &temp_storage_bytes,\n                            DoubleBuffer<KeyT>   &d_keys,\n                            DoubleBuffer<ValueT> &d_values,\n                            NumItemsT             num_items,\n                            int                   num_segments,\n                            BeginOffsetIteratorT  d_segment_begin_offsets,\n                            EndOffsetIteratorT    d_segment_end_offsets,\n                            int                   begin_bit,\n                            int                   end_bit)\n{\n  // Invoke kernel to invoke device-side dispatch:\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<IsDescending,\n                              CubBackend,\n                              KeyT,\n                              ValueT,\n                              BeginOffsetIteratorT,\n                              EndOffsetIteratorT,\n                              NumItemsT>,\n            is_descending,\n            cub_backend,\n            d_selector,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys,\n            d_values,\n            num_items,\n            num_segments,\n            d_segment_begin_offsets,\n            d_segment_end_offsets,\n            begin_bit,\n            end_bit);\n  CubDebugExit(retval);\n  CubDebugExit(hipcub::detail::device_synchronize());\n\n  // Copy out selector\n  CubDebugExit(hipMemcpy(&d_keys.selector,\n                          d_selector,\n                          sizeof(int) * 1,\n                          hipMemcpyDeviceToHost));\n  d_values.selector = d_keys.selector;\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  return retval;\n}\n\n// Specializations of Dispatch that translate the CDP backend to the appropriate\n// CUB backend, and uses the CUB backend to launch the CDP kernel.\n#define DEFINE_CDP_DISPATCHER(CdpBackend, CubBackend)                          \\\n  template <int IsDescending,                                                  \\\n            typename KeyT,                                                     \\\n            typename ValueT,                                                   \\\n            typename BeginOffsetIteratorT,                                     \\\n            typename EndOffsetIteratorT,                                       \\\n            typename NumItemsT>                                                \\\n  hipError_t Dispatch(Int2Type<IsDescending> is_descending,                   \\\n                       Int2Type<CdpBackend> /*dispatch_to*/,                   \\\n                       int         *d_selector,                                \\\n                       size_t      *d_temp_storage_bytes,                      \\\n                       hipError_t *d_cdp_error,                               \\\n                                                                               \\\n                       void                 *d_temp_storage,                   \\\n                       size_t               &temp_storage_bytes,               \\\n                       DoubleBuffer<KeyT>   &d_keys,                           \\\n                       DoubleBuffer<ValueT> &d_values,                         \\\n                       NumItemsT             num_items,                        \\\n                       int                   num_segments,                     \\\n                       BeginOffsetIteratorT  d_segment_begin_offsets,          \\\n                       EndOffsetIteratorT    d_segment_end_offsets,            \\\n                       int                   begin_bit,                        \\\n                       int                   end_bit)                          \\\n  {                                                                            \\\n    Int2Type<CubBackend> cub_backend{};                                        \\\n    return LaunchCDPKernel(is_descending,                                      \\\n                           cub_backend,                                        \\\n                           d_selector,                                         \\\n                           d_temp_storage_bytes,                               \\\n                           d_cdp_error,                                        \\\n                           d_temp_storage,                                     \\\n                           temp_storage_bytes,                                 \\\n                           d_keys,                                             \\\n                           d_values,                                           \\\n                           num_items,                                          \\\n                           num_segments,                                       \\\n                           d_segment_begin_offsets,                            \\\n                           d_segment_end_offsets,                              \\\n                           begin_bit,                                          \\\n                           end_bit);                                           \\\n  }\n\nDEFINE_CDP_DISPATCHER(CDP, CUB)\nDEFINE_CDP_DISPATCHER(CDP_NO_OVERWRITE, CUB_NO_OVERWRITE)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED, CUB_SEGMENTED)\nDEFINE_CDP_DISPATCHER(CDP_SEGMENTED_NO_OVERWRITE, CUB_SEGMENTED_NO_OVERWRITE)\n\n#undef DEFINE_CDP_DISPATCHER\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Problem generation\n//---------------------------------------------------------------------\n\n\n/**\n * Simple key-value pairing\n */\ntemplate <\n    typename KeyT,\n    typename ValueT>\nstruct Pair\n{\n    KeyT     key;\n    ValueT   value;\n\n    bool operator<(const Pair &b) const\n    {\n        return (key < b.key);\n    }\n};\n\n\n/**\n * Simple key-value pairing (specialized for bool types)\n */\ntemplate <typename ValueT>\nstruct Pair<bool, ValueT>\n{\n    bool     key;\n    ValueT   value;\n\n    bool operator<(const Pair &b) const\n    {\n        return (!key && b.key);\n    }\n};\n\n\n/**\n * Initialize key data\n */\ntemplate <typename KeyT, typename NumItemsT>\nvoid InitializeKeyBits(\n    GenMode         gen_mode,\n    KeyT            *h_keys,\n    NumItemsT       num_items,\n    int             /*entropy_reduction*/)\n{\n    for (NumItemsT i = 0; i < num_items; ++i)\n        InitValue(gen_mode, h_keys[i], i);\n}\n\ntemplate <typename KeyT,\n          typename UnsignedBits = typename hipcub::Traits<KeyT>::UnsignedBits>\nUnsignedBits KeyBits(KeyT key)\n{\n    UnsignedBits bits;\n    memcpy(&bits, &key, sizeof(KeyT));\n    return bits;\n}\n\n/** Initialize the reference array monotonically. */\ntemplate <typename KeyT, typename NumItemsT>\nvoid InitializeKeysSorted(\n    KeyT            *h_keys,\n    NumItemsT       num_items)\n{\n    using TraitsT = hipcub::Traits<KeyT>;\n    using UnsignedBits = typename TraitsT::UnsignedBits;\n\n    // Numbers to generate random runs.\n    UnsignedBits max_inc = 1 << (sizeof(UnsignedBits) < 4 ? 3 :\n                                 (sizeof(UnsignedBits) < 8 ? 14 : 24));\n    UnsignedBits min_bits = TraitsT::TwiddleIn(KeyBits(TraitsT::Lowest()));\n    UnsignedBits max_bits = TraitsT::TwiddleIn(KeyBits(TraitsT::Max()));\n    NumItemsT max_run = std::max(\n        NumItemsT(double(num_items) * (max_inc + 1) / max_bits),\n        NumItemsT(1 << 14));\n\n    UnsignedBits *h_key_bits = reinterpret_cast<UnsignedBits*>(h_keys);\n    NumItemsT i = 0;\n    // Start with the minimum twiddled key.\n    UnsignedBits twiddled_key = min_bits;\n    while (i < num_items)\n    {\n        // Generate random increment (avoid overflow).\n        UnsignedBits inc_bits = 0;\n        RandomBits(inc_bits);\n        // twiddled_key < max_bits at this point.\n        UnsignedBits inc = static_cast<UnsignedBits>(std::min(1 + inc_bits % max_inc, max_bits - twiddled_key));\n        twiddled_key += inc;\n\n        // Generate random run length (ensure there are enough values to fill the rest).\n        NumItemsT run_bits = 0;\n        RandomBits(run_bits);\n        NumItemsT run_length = std::min(1 + run_bits % max_run, num_items - i);\n        if (twiddled_key == max_bits) run_length = num_items - i;\n        NumItemsT run_end = i + run_length;\n\n        // Fill the array.\n        UnsignedBits key = TraitsT::TwiddleOut(twiddled_key);\n        // Avoid -0.0 for floating-point keys.\n        UnsignedBits negative_zero = UnsignedBits(1) << UnsignedBits(sizeof(UnsignedBits) * 8 - 1);\n        if (TraitsT::CATEGORY == hipcub::FLOATING_POINT && key == negative_zero)\n        {\n            key = 0;\n        }\n\n        for (; i < run_end; ++i)\n        {\n            h_key_bits[i] = key;\n        }\n    }\n}\n\n\n/**\n * Initialize solution\n */\ntemplate <bool IS_DESCENDING, bool WANT_RANKS, typename KeyT, typename NumItemsT>\nvoid InitializeSolution(\n    KeyT       *h_keys,\n    NumItemsT  num_items,\n    int        num_segments,\n    bool       pre_sorted,\n    NumItemsT  *h_segment_offsets,\n    int        begin_bit,\n    int        end_bit,\n    NumItemsT  *&h_reference_ranks,\n    KeyT       *&h_reference_keys)\n{\n    if (num_items == 0)\n    {\n        h_reference_ranks = nullptr;\n        h_reference_keys = nullptr;\n        return;\n    }\n\n    if (pre_sorted)\n    {\n        printf(\"Shuffling reference solution on CPU\\n\");\n        // Note: begin_bit and end_bit are ignored here, and assumed to have the\n        // default values (begin_bit == 0, end_bit == 8 * sizeof(KeyT)).\n        // Otherwise, pre-sorting won't work, as it doesn't necessarily\n        // correspond to the order of keys sorted by a subrange of bits.\n        // num_segments is also ignored as assumed to be 1, as pre-sorted tests\n        // are currently not supported for multiple segments.\n        //\n        // Pre-sorted tests with non-default begin_bit, end_bit or num_segments\n        // != 1 are skipped in TestBits() and TestSegments(), respectively.\n        AssertEquals(begin_bit, 0);\n        AssertEquals(end_bit, static_cast<int>(8 * sizeof(KeyT)));\n        AssertEquals(num_segments, 1);\n\n        // Copy to the reference solution.\n        h_reference_keys = new KeyT[num_items];\n        if (IS_DESCENDING)\n        {\n            // Copy in reverse.\n            for (NumItemsT i = 0; i < num_items; ++i)\n            {\n                h_reference_keys[i] = h_keys[num_items - 1 - i];\n            }\n            // Copy back.\n            memcpy(h_keys, h_reference_keys, num_items * sizeof(KeyT));\n        }\n        else\n        {\n            memcpy(h_reference_keys, h_keys, num_items * sizeof(KeyT));\n        }\n\n        // Summarize the pre-sorted array (element, 1st position, count).\n        struct Element\n        {\n            KeyT key;\n            NumItemsT num;\n            NumItemsT index;\n        };\n\n        std::vector<Element> summary;\n        KeyT cur_key = h_reference_keys[0];\n        summary.push_back(Element{cur_key, 1, 0});\n        for (NumItemsT i = 1; i < num_items; ++i)\n        {\n            KeyT key = h_reference_keys[i];\n            if (key == cur_key)\n            {\n                // Same key.\n                summary.back().num++;\n                continue;\n            }\n\n            // Different key.\n            cur_key = key;\n            summary.push_back(Element{cur_key, 1, i});\n        }\n\n        // Generate a random permutation from the summary. Such a complicated\n        // approach is used to permute the array and compute ranks in a\n        // cache-friendly way and in a short time.\n        if (WANT_RANKS)\n        {\n            h_reference_ranks = new NumItemsT[num_items];\n        }\n        NumItemsT max_run = 32;\n        NumItemsT run = 0;\n        NumItemsT i = 0;\n        while (summary.size() > 0)\n        {\n            // Pick up a random element and a run.\n            NumItemsT bits = 0;\n            RandomBits(bits);\n            NumItemsT summary_id = bits % summary.size();\n            Element& element = summary[summary_id];\n            run = std::min(1 + bits % (max_run - 1), element.num);\n            for (NumItemsT j = 0; j < run; ++j)\n            {\n                h_keys[i + j] = element.key;\n                if (WANT_RANKS)\n                {\n                    h_reference_ranks[element.index + j] = i + j;\n                }\n            }\n            i += run;\n            element.index += run;\n            element.num -= run;\n            if (element.num == 0)\n            {\n                // Remove the empty entry.\n                std::swap(summary[summary_id], summary.back());\n                summary.pop_back();\n            }\n        }\n        printf(\" Done.\\n\");\n    }\n    else\n    {\n        typedef Pair<KeyT, NumItemsT> PairT;\n\n        PairT *h_pairs = new PairT[num_items];\n\n        int num_bits = end_bit - begin_bit;\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n\n            // Mask off unwanted portions\n            if (num_bits < static_cast<int>(sizeof(KeyT) * 8))\n            {\n                using UnsignedBits = typename hipcub::Traits<KeyT>::UnsignedBits;\n\n                UnsignedBits base = 0;\n                memcpy(&base, &h_keys[i], sizeof(KeyT));\n                base &= ((UnsignedBits{1} << num_bits) - 1) << begin_bit;\n                memcpy(&h_pairs[i].key, &base, sizeof(KeyT));\n            }\n            else\n            {\n                h_pairs[i].key = h_keys[i];\n            }\n\n            h_pairs[i].value = i;\n        }\n\n        printf(\"\\nSorting reference solution on CPU \"\n               \"(%zd items, %d segments, %zd items/seg)...\",\n               static_cast<std::size_t>(num_items),\n               num_segments,\n               static_cast<std::size_t>(num_items / num_segments));\n        fflush(stdout);\n\n        for (int i = 0; i < num_segments; ++i)\n        {\n            if (IS_DESCENDING) std::reverse(h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n            std::stable_sort(               h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n            if (IS_DESCENDING) std::reverse(h_pairs + h_segment_offsets[i], h_pairs + h_segment_offsets[i + 1]);\n        }\n\n        printf(\" Done.\\n\"); fflush(stdout);\n\n        if (WANT_RANKS)\n        {\n            h_reference_ranks  = new NumItemsT[num_items];\n        }\n        h_reference_keys   = new KeyT[num_items];\n\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n            if (WANT_RANKS)\n            {\n                h_reference_ranks[i]    = h_pairs[i].value;\n            }\n            h_reference_keys[i]     = h_keys[h_pairs[i].value];\n        }\n\n        if (h_pairs) delete[] h_pairs;\n    }\n}\n\ntemplate <bool IS_DESCENDING, typename KeyT, typename NumItemsT>\nvoid ResetKeys(KeyT *h_keys, NumItemsT num_items, bool pre_sorted, KeyT *reference_keys)\n{\n    if (!pre_sorted) return;\n\n    // Copy the reference keys back.\n    if (IS_DESCENDING)\n    {\n        // Keys need to be copied in reverse.\n        for (NumItemsT i = 0; i < num_items; ++i)\n        {\n            h_keys[i] = reference_keys[num_items - 1 - i];\n        }\n    }\n    else\n    {\n        memcpy(h_keys, reference_keys, num_items * sizeof(KeyT));\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\ntemplate <typename T>\nstruct UnwrapHalfAndBfloat16 {\n    using Type = T;\n};\n\n#if !_NVHPC_CUDA\ntemplate <>\nstruct UnwrapHalfAndBfloat16<half_t> {\n    using Type = __half;\n};\n#endif\n\n#if !_NVHPC_CUDA\ntemplate <>\nstruct UnwrapHalfAndBfloat16<bfloat16_t> {\n    using Type = __hip_bfloat16;\n};\n#endif\n\ntemplate <class T, class NumItemsT>\nint compare_device_arrays(T* host_reference, T* d_tmp_buffer, T* d_data, NumItemsT num_items)\n{\n    CubDebugExit(hipMemcpy(d_tmp_buffer, host_reference, sizeof(T) * num_items, hipMemcpyHostToDevice));\n\n    auto d_reference_begin = d_tmp_buffer;\n    auto d_reference_end = d_tmp_buffer + num_items;\n    auto err = thrust::mismatch(thrust::device, d_reference_begin, d_reference_end, d_data);\n\n    if (err.first != d_reference_end)\n    {\n        const auto index = thrust::distance(d_reference_begin, err.first);\n        return CompareDeviceResults(host_reference + index, d_data + index, 1, true, g_verbose);\n    }\n\n    return 0;\n}\n\ntemplate <class NumItemsT>\nint compare_device_arrays(\n    CUB_NS_QUALIFIER::NullType */* h_reference */,\n    CUB_NS_QUALIFIER::NullType */* d_tmp_buffer */,\n    CUB_NS_QUALIFIER::NullType */* d_data */,\n    NumItemsT /* num_items */)\n{\n    return 0;\n}\n\n/**\n * Test DeviceRadixSort\n */\ntemplate <\n    Backend     BACKEND,\n    bool        IS_DESCENDING,\n    typename    KeyT,\n    typename    ValueT,\n    typename    BeginOffsetIteratorT,\n    typename    EndOffsetIteratorT,\n    typename    NumItemsT>\nvoid Test(\n    KeyT                 *h_keys,\n    ValueT               *h_values,\n    NumItemsT            num_items,\n    int                  num_segments,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit,\n    KeyT                 *h_reference_keys,\n    ValueT               *h_reference_values)\n{\n    // Key alias type\n    using KeyAliasT = typename UnwrapHalfAndBfloat16<KeyT>::Type;\n\n    const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n    printf(\"%s %s hipcub::DeviceRadixSort %zd items, %d segments, \"\n           \"%d-byte keys (%s) %d-byte values (%s), %d-byte num_items (%s), \"\n           \"descending %d, begin_bit %d, end_bit %d\\n\",\n           BackendToString(BACKEND),\n           (KEYS_ONLY) ? \"keys-only\" : \"key-value\",\n           static_cast<std::size_t>(num_items),\n           num_segments,\n           static_cast<int>(sizeof(KeyT)),\n           typeid(KeyT).name(),\n           (KEYS_ONLY) ? 0 : static_cast<int>(sizeof(ValueT)),\n           typeid(ValueT).name(),\n           static_cast<int>(sizeof(NumItemsT)),\n           typeid(NumItemsT).name(),\n           IS_DESCENDING,\n           begin_bit,\n           end_bit);\n\n    if (g_verbose)\n    {\n        printf(\"Input keys:\\n\");\n        DisplayResults(h_keys, num_items);\n        printf(\"\\n\\n\");\n    }\n\n    // Allocate device arrays\n    DoubleBuffer<KeyAliasT> d_keys;\n    DoubleBuffer<ValueT>    d_values;\n    int                     *d_selector;\n    size_t                  *d_temp_storage_bytes;\n    hipError_t             *d_cdp_error;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys.d_buffers[0], sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys.d_buffers[1], sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_selector, sizeof(int) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes, sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error, sizeof(hipError_t) * 1));\n    if (!KEYS_ONLY)\n    {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values.d_buffers[0], sizeof(ValueT) * num_items));\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values.d_buffers[1], sizeof(ValueT) * num_items));\n    }\n\n    // Allocate temporary storage (and make it un-aligned)\n    size_t  temp_storage_bytes  = 0;\n    void    *d_temp_storage     = NULL;\n    CubDebugExit(Dispatch(\n        Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n        d_temp_storage, temp_storage_bytes, d_keys, d_values,\n        num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        begin_bit, end_bit));\n\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes + 1));\n    void* mis_aligned_temp = static_cast<char*>(d_temp_storage) + 1;\n\n    // Initialize/clear device arrays\n    d_keys.selector = 0;\n    CubDebugExit(hipMemcpy(d_keys.d_buffers[0], h_keys, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemset(d_keys.d_buffers[1], 0, sizeof(KeyT) * num_items));\n    if (!KEYS_ONLY)\n    {\n        d_values.selector = 0;\n        CubDebugExit(hipMemcpy(d_values.d_buffers[0], h_values, sizeof(ValueT) * num_items, hipMemcpyHostToDevice));\n        CubDebugExit(hipMemset(d_values.d_buffers[1], 0, sizeof(ValueT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n        mis_aligned_temp, temp_storage_bytes, d_keys, d_values,\n        num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n        begin_bit, end_bit));\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Check for correctness (and display results, if specified)\n    printf(\"Warmup done.  Checking results:\\n\"); fflush(stdout);\n\n    int compare = 0;\n\n    // If in/out API is used, we are not allowed to overwrite the input. \n    // Let's check that the input buffer is not overwritten by the algorithm.\n    if (BACKEND == CUB_NO_OVERWRITE)\n    {\n        KeyT *d_input_keys = reinterpret_cast<KeyT*>(d_keys.d_buffers[0]);\n\n        if (temp_storage_bytes < sizeof(KeyT) * num_items)\n        {\n            // For small input sizes, temporary storage is not large enough to fit keys.\n            compare = CompareDeviceResults(h_keys, d_input_keys, num_items, true, g_verbose);\n        }\n        else \n        {\n            // If overwrite is not allowed, temporary storage is large enough to fit keys.\n            KeyT* temp_keys = reinterpret_cast<KeyT*>(d_temp_storage);\n\n            compare = compare_device_arrays(h_keys, temp_keys, d_input_keys, num_items);\n            printf(\"\\t Compare input keys: %s \", compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n        }\n    }\n\n    // After the previous check is done, we can safely reuse alternative buffer to store \n    // the reference results and compare current output.\n    compare |= compare_device_arrays(h_reference_keys,\n                                     reinterpret_cast<KeyT *>(d_keys.Alternate()),\n                                     reinterpret_cast<KeyT *>(d_keys.Current()),\n                                     num_items);\n\n    printf(\"\\t Compare keys (selector %d): %s \", d_keys.selector, compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n\n    if (!KEYS_ONLY)\n    {\n        int values_compare = compare_device_arrays(h_reference_values,\n                                                   d_values.Alternate(),\n                                                   d_values.Current(),\n                                                   num_items);\n\n        compare |= values_compare;\n        printf(\"\\t Compare values (selector %d): %s \", d_values.selector, values_compare ? \"FAIL\" : \"PASS\"); fflush(stdout);\n    }\n\n    // Performance\n    if (g_timing_iterations)\n        printf(\"\\nPerforming timing iterations:\\n\"); fflush(stdout);\n\n    GpuTimer gpu_timer;\n    float elapsed_millis = 0.0f;\n    for (int i = 0; i < g_timing_iterations; ++i)\n    {\n        // Initialize/clear device arrays\n        CubDebugExit(hipMemcpy(d_keys.d_buffers[d_keys.selector], h_keys, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n        CubDebugExit(hipMemset(d_keys.d_buffers[d_keys.selector ^ 1], 0, sizeof(KeyT) * num_items));\n        if (!KEYS_ONLY)\n        {\n            CubDebugExit(hipMemcpy(d_values.d_buffers[d_values.selector], h_values, sizeof(ValueT) * num_items, hipMemcpyHostToDevice));\n            CubDebugExit(hipMemset(d_values.d_buffers[d_values.selector ^ 1], 0, sizeof(ValueT) * num_items));\n        }\n\n        gpu_timer.Start();\n        CubDebugExit(Dispatch(\n            Int2Type<IS_DESCENDING>(), Int2Type<BACKEND>(), d_selector, d_temp_storage_bytes, d_cdp_error,\n            mis_aligned_temp, temp_storage_bytes, d_keys, d_values,\n            num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets,\n            begin_bit, end_bit));\n        gpu_timer.Stop();\n        elapsed_millis += gpu_timer.ElapsedMillis();\n    }\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis = elapsed_millis / g_timing_iterations;\n        float giga_rate = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth = (KEYS_ONLY) ?\n            giga_rate * sizeof(KeyT) * 2 :\n            giga_rate * (sizeof(KeyT) + sizeof(ValueT)) * 2;\n        printf(\"\\n%.3f elapsed ms, %.3f avg ms, %.3f billion items/s, %.3f logical GB/s\", elapsed_millis, avg_millis, giga_rate, giga_bandwidth);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (d_keys.d_buffers[0]) CubDebugExit(g_allocator.DeviceFree(d_keys.d_buffers[0]));\n    if (d_keys.d_buffers[1]) CubDebugExit(g_allocator.DeviceFree(d_keys.d_buffers[1]));\n    if (d_values.d_buffers[0]) CubDebugExit(g_allocator.DeviceFree(d_values.d_buffers[0]));\n    if (d_values.d_buffers[1]) CubDebugExit(g_allocator.DeviceFree(d_values.d_buffers[1]));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_selector) CubDebugExit(g_allocator.DeviceFree(d_selector));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\n// Returns whether there is enough memory for the test.\ntemplate <typename KeyT, typename ValueT>\nbool HasEnoughMemory(std::size_t num_items, bool overwrite)\n{\n    std::size_t total_mem = TotalGlobalMem();\n    std::size_t value_size = std::is_same<ValueT, NullType>::value\n                           ? 0\n                           : sizeof(ValueT);\n    // A conservative estimate of the amount of memory required.\n    double factor = overwrite ? 2.25 : 3.25;\n    std::size_t test_mem = static_cast<std::size_t>\n      (num_items * (sizeof(KeyT) + value_size) * factor);\n    return test_mem < total_mem;\n}\n\n\n/**\n * Test backend\n */\ntemplate <bool IS_DESCENDING,\n          typename KeyT,\n          typename ValueT,\n          typename BeginOffsetIteratorT,\n          typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestBackend(KeyT                *h_keys,\n                 NumItemsT            num_items,\n                 int                  num_segments,\n                 BeginOffsetIteratorT d_segment_begin_offsets,\n                 EndOffsetIteratorT   d_segment_end_offsets,\n                 int                  begin_bit,\n                 int                  end_bit,\n                 KeyT                *h_reference_keys,\n                 NumItemsT           *h_reference_ranks)\n{\n#if TEST_CDP == 0\n  constexpr auto NonSegmentedOverwrite   = CUB;\n  constexpr auto NonSegmentedNoOverwrite = CUB_NO_OVERWRITE;\n  constexpr auto SegmentedOverwrite      = CUB_SEGMENTED;\n  constexpr auto SegmentedNoOverwrite    = CUB_SEGMENTED_NO_OVERWRITE;\n#else  // TEST_CDP\n  constexpr auto NonSegmentedOverwrite   = CDP;\n  constexpr auto NonSegmentedNoOverwrite = CDP_NO_OVERWRITE;\n  constexpr auto SegmentedOverwrite      = CDP_SEGMENTED;\n  constexpr auto SegmentedNoOverwrite    = CDP_SEGMENTED_NO_OVERWRITE;\n#endif // TEST_CDP\n\n  const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n  // A conservative check assuming overwrite is allowed.\n  if (!HasEnoughMemory<KeyT, ValueT>(static_cast<std::size_t>(num_items), true))\n  {\n    printf(\"Skipping the test due to insufficient device memory\\n\");\n    return;\n  }\n\n  std::unique_ptr<ValueT[]> h_value_data{};\n\n  ValueT *h_values           = nullptr;\n  ValueT *h_reference_values = nullptr;\n\n  if (!KEYS_ONLY)\n  {\n    h_value_data.reset(new ValueT[2 * static_cast<std::size_t>(num_items)]);\n    h_values           = h_value_data.get();\n    h_reference_values = h_value_data.get() + num_items;\n\n    for (NumItemsT i = 0; i < num_items; ++i)\n    {\n      InitValue(INTEGER_SEED, h_values[i], i);\n      InitValue(INTEGER_SEED, h_reference_values[i], h_reference_ranks[i]);\n    }\n  }\n\n  // Skip segmented sort if num_items isn't int.\n  // TODO(64bit-seg-sort): re-enable these tests once num_items is templated for\n  // segmented sort.\n  if (std::is_same<NumItemsT, int>::value)\n  {\n    printf(\"Testing segmented sort with overwrite\\n\");\n    Test<SegmentedOverwrite, IS_DESCENDING>(h_keys,\n                                            h_values,\n                                            num_items,\n                                            num_segments,\n                                            d_segment_begin_offsets,\n                                            d_segment_end_offsets,\n                                            begin_bit,\n                                            end_bit,\n                                            h_reference_keys,\n                                            h_reference_values);\n    printf(\"Testing segmented sort with no overwrite\\n\");\n    Test<SegmentedNoOverwrite, IS_DESCENDING>(h_keys,\n                                              h_values,\n                                              num_items,\n                                              num_segments,\n                                              d_segment_begin_offsets,\n                                              d_segment_end_offsets,\n                                              begin_bit,\n                                              end_bit,\n                                              h_reference_keys,\n                                              h_reference_values);\n  }\n  else\n  {\n    printf(\"Skipping segmented sort tests (NumItemsT != int)\\n\");\n  }\n\n  if (num_segments == 1)\n  {\n    printf(\"Testing non-segmented sort with overwrite\\n\");\n    Test<NonSegmentedOverwrite, IS_DESCENDING>(h_keys,\n                                               h_values,\n                                               num_items,\n                                               num_segments,\n                                               d_segment_begin_offsets,\n                                               d_segment_end_offsets,\n                                               begin_bit,\n                                               end_bit,\n                                               h_reference_keys,\n                                               h_reference_values);\n    if (HasEnoughMemory<KeyT, ValueT>(static_cast<std::size_t>(num_items),\n                                      false))\n    {\n      printf(\"Testing non-segmented sort with no overwrite\\n\");\n      Test<NonSegmentedNoOverwrite, IS_DESCENDING>(h_keys,\n                                                   h_values,\n                                                   num_items,\n                                                   num_segments,\n                                                   d_segment_begin_offsets,\n                                                   d_segment_end_offsets,\n                                                   begin_bit,\n                                                   end_bit,\n                                                   h_reference_keys,\n                                                   h_reference_values);\n    }\n    else\n    {\n      printf(\"Skipping no-overwrite tests with %zd items due to \"\n             \"insufficient memory\\n\",\n             static_cast<std::size_t>(num_items));\n    }\n  }\n}\n\n\n// Smallest value type for TEST_VALUE_TYPE.\n// Unless TEST_VALUE_TYPE == 3, this is the only value type tested.\n#if TEST_VALUE_TYPE == 0\n// Test keys-only\nusing SmallestValueT = NullType;\n#elif TEST_VALUE_TYPE == 1\n// Test with 8b value\nusing SmallestValueT = unsigned char;\n#elif TEST_VALUE_TYPE == 2\n// Test with 32b value\nusing SmallestValueT = unsigned int;\n// Test with 64b value\n#elif TEST_VALUE_TYPE == 3\nusing SmallestValueT = unsigned long long;\n#endif\n\n\n/**\n * Test value type\n */\ntemplate <bool IS_DESCENDING, typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT, typename NumItemsT>\nvoid TestValueTypes(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit)\n{\n    // Initialize the solution\n    NumItemsT *h_reference_ranks = NULL;\n    KeyT *h_reference_keys = NULL;\n    // If TEST_VALUE_TYPE == 0, no values are sorted, only keys.\n    // Since ranks are only necessary when checking for values,\n    // they are not computed in this case.\n    InitializeSolution<IS_DESCENDING, TEST_VALUE_TYPE != 0>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, begin_bit, end_bit, h_reference_ranks, h_reference_keys);\n\n    TestBackend<IS_DESCENDING, KeyT, SmallestValueT>          (h_keys, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit, h_reference_keys, h_reference_ranks);\n\n#if TEST_VALUE_TYPE == 3\n    // Test with non-trivially-constructable value\n    // These are cheap to build, so lump them in with the 64b value tests.\n    TestBackend<IS_DESCENDING, KeyT, TestBar>           (h_keys, num_items, num_segments, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit, h_reference_keys, h_reference_ranks);\n#endif\n\n    // Cleanup\n    ResetKeys<IS_DESCENDING>(h_keys, num_items, pre_sorted, h_reference_keys);\n    if (h_reference_ranks) delete[] h_reference_ranks;\n    if (h_reference_keys) delete[] h_reference_keys;\n}\n\n\n\n/**\n * Test ascending/descending\n */\ntemplate <typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestDirection(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets,\n    int                  begin_bit,\n    int                  end_bit)\n{\n    TestValueTypes<true>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n    TestValueTypes<false>(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n}\n\n\n/**\n * Test different bit ranges\n */\ntemplate <typename KeyT, typename BeginOffsetIteratorT, typename EndOffsetIteratorT,\n          typename NumItemsT>\nvoid TestBits(\n    KeyT                 *h_keys,\n    NumItemsT            num_items,\n    int                  num_segments,\n    bool                 pre_sorted,\n    NumItemsT            *h_segment_offsets,\n    BeginOffsetIteratorT d_segment_begin_offsets,\n    EndOffsetIteratorT   d_segment_end_offsets)\n{\n    // Don't test partial-word sorting for boolean, fp, or signed types (the bit-flipping techniques get in the way) or pre-sorted keys\n    if ((Traits<KeyT>::CATEGORY == UNSIGNED_INTEGER)\n        && (!std::is_same<KeyT, bool>::value)\n        && !pre_sorted)\n    {\n        // Partial bits\n        int begin_bit = 1;\n        int end_bit = (sizeof(KeyT) * 8) - 1;\n        printf(\"Testing key bits [%d,%d)\\n\", begin_bit, end_bit); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n\n        // Equal bits\n        begin_bit = end_bit = 0;\n        printf(\"Testing key bits [%d,%d)\\n\", begin_bit, end_bit); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, begin_bit, end_bit);\n\n        // Across subword boundaries\n        int mid_bit = sizeof(KeyT) * 4;\n        printf(\"Testing key bits [%d,%d)\\n\", mid_bit - 1, mid_bit + 1); fflush(stdout);\n        TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, mid_bit - 1, mid_bit + 1);\n    }\n\n    printf(\"Testing key bits [%d,%d)\\n\", 0, int(sizeof(KeyT)) * 8); fflush(stdout);\n    TestDirection(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets, d_segment_end_offsets, 0, sizeof(KeyT) * 8);\n}\n\n\ntemplate<typename OffsetT>\nstruct TransformFunctor1\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\ntemplate<typename OffsetT>\nstruct TransformFunctor2\n{\n    __host__ __device__ __forceinline__ OffsetT operator()(OffsetT offset) const\n    {\n        return offset;\n    }\n};\n\n\n/**\n* Test different segment iterators\n*/\ntemplate <typename KeyT, typename NumItemsT>\nvoid TestSegmentIterators(\n    KeyT           *h_keys,\n    NumItemsT      num_items,\n    int            num_segments,\n    bool           pre_sorted,\n    NumItemsT     *h_segment_offsets,\n    NumItemsT     *d_segment_offsets)\n{\n    InitializeSegments(num_items, num_segments, h_segment_offsets);\n    CubDebugExit(hipMemcpy(d_segment_offsets, h_segment_offsets, sizeof(NumItemsT) * (num_segments + 1), hipMemcpyHostToDevice));\n\n    // Test with segment pointer.\n    // This is also used to test non-segmented sort.\n    TestBits(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_offsets, d_segment_offsets + 1);\n\n    if (num_segments > 1)\n    {\n        // Test with transform iterators of different types\n        typedef TransformFunctor1<NumItemsT> TransformFunctor1T;\n        typedef TransformFunctor2<NumItemsT> TransformFunctor2T;\n\n        TransformInputIterator<NumItemsT, TransformFunctor1T, NumItemsT*, NumItemsT> d_segment_begin_offsets_itr(d_segment_offsets, TransformFunctor1T());\n        TransformInputIterator<NumItemsT, TransformFunctor2T, NumItemsT*, NumItemsT> d_segment_end_offsets_itr(d_segment_offsets + 1, TransformFunctor2T());\n\n        TestBits(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_begin_offsets_itr, d_segment_end_offsets_itr);\n    }\n}\n\n\n/**\n * Test different segment compositions\n */\ntemplate <typename KeyT, typename NumItemsT>\nvoid TestSegments(\n    KeyT         *h_keys,\n    NumItemsT    num_items,\n    int          max_segments,\n    bool         pre_sorted)\n{\n    max_segments = static_cast<int>(CUB_MIN(num_items, static_cast<NumItemsT>(max_segments)));\n    NumItemsT *h_segment_offsets = new NumItemsT[max_segments + 1];\n\n    NumItemsT *d_segment_offsets = nullptr;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(NumItemsT) * (max_segments + 1)));\n\n    for (int num_segments = max_segments; num_segments > 1; num_segments = hipcub::DivideAndRoundUp(num_segments, 64))\n    {\n        // Pre-sorted tests are not supported for segmented sort\n        if (num_items / num_segments < 128 * 1000 && !pre_sorted) {\n            // Right now we assign a single thread block to each segment, so lets keep it to under 128K items per segment\n            TestSegmentIterators(h_keys, num_items, num_segments, pre_sorted, h_segment_offsets, d_segment_offsets);\n        }\n    }\n\n    // Test single segment\n    if (num_items > 0)\n    {\n      if (num_items < 128 * 1000 || pre_sorted)\n      {\n        // Right now we assign a single thread block to each segment, so lets\n        // keep it to under 128K items per segment\n        TestSegmentIterators(h_keys,\n                             num_items,\n                             1,\n                             pre_sorted,\n                             h_segment_offsets,\n                             d_segment_offsets);\n      }\n    }\n\n    if (h_segment_offsets) delete[] h_segment_offsets;\n    if (d_segment_offsets) CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n/** \n * Test different NumItemsT, i.e. types of num_items \n */\ntemplate <typename KeyT>\nvoid TestNumItems(KeyT         *h_keys,\n                  std::size_t  num_items,\n                  int          max_segments,\n                  bool         pre_sorted)\n{\n    if (!pre_sorted && num_items <= std::size_t(std::numeric_limits<int>::max()))\n    {\n        TestSegments<KeyT, int>(h_keys, static_cast<int>(num_items), max_segments, pre_sorted);\n    }\n    if (pre_sorted && num_items <= std::size_t(std::numeric_limits<std::uint32_t>::max()))\n    {\n        TestSegments<KeyT, std::uint32_t>(h_keys, static_cast<std::uint32_t>(num_items), max_segments, pre_sorted);\n    }\n    TestSegments<KeyT, std::size_t>(h_keys, num_items, max_segments, pre_sorted);\n}\n\n\n/**\n * Test different (sub)lengths and number of segments\n */\ntemplate <typename KeyT>\nvoid TestSizes(KeyT* h_keys,\n               std::size_t max_items,\n               int max_segments,\n               bool pre_sorted)\n{\n    if (pre_sorted)\n    {\n        // run a specific list of sizes, up to max_items\n        std::size_t sizes[] = {g_smallest_pre_sorted_num_items, 4350000007ull};\n        for (std::size_t num_items : sizes)\n        {\n            if (num_items > max_items) break;\n            TestNumItems(h_keys, num_items, max_segments, pre_sorted);\n        }\n    }\n    else\n    {\n        for (std::size_t num_items = max_items;\n             num_items > 1;\n             num_items = hipcub::DivideAndRoundUp(num_items, 64))\n        {\n            TestNumItems(h_keys, num_items, max_segments, pre_sorted);\n        }\n    }\n}\n\n/**\n * Test key sampling distributions\n */\ntemplate <typename KeyT, bool WITH_PRE_SORTED>\nvoid TestGen(\n    std::size_t     max_items,\n    int             max_segments)\n{\n    if (max_items == ~std::size_t(0))\n    {\n        max_items = 8000003;\n    }\n\n    if (max_segments < 0)\n    {\n        max_segments = 5003;\n    }\n\n    std::unique_ptr<KeyT[]> h_keys(new KeyT[max_items]);\n\n    // Test trivial problems sizes\n    h_keys[0] = static_cast<KeyT>(42);\n    TestNumItems(h_keys.get(), 0, 0, false);\n    TestNumItems(h_keys.get(), 1, 1, false);\n\n    for (int entropy_reduction = 0; entropy_reduction <= 6; entropy_reduction += 6)\n    {\n        printf(\"\\nTesting random %s keys with entropy reduction factor %d\\n\", typeid(KeyT).name(), entropy_reduction); fflush(stdout);\n        InitializeKeyBits(RANDOM, h_keys.get(), max_items, entropy_reduction);\n        TestSizes(h_keys.get(), max_items, max_segments, false);\n    }\n\n    if (hipcub::Traits<KeyT>::CATEGORY == hipcub::FLOATING_POINT)\n    {\n        printf(\"\\nTesting random %s keys with some replaced with -0.0 or +0.0 \\n\", typeid(KeyT).name());\n        fflush(stdout);\n        InitializeKeyBits(RANDOM_MINUS_PLUS_ZERO, h_keys.get(), max_items, 0);\n        // This just tests +/- 0 handling -- don't need to test multiple sizes\n        TestNumItems(h_keys.get(), max_items, max_segments, false);\n    }\n\n    printf(\"\\nTesting uniform %s keys\\n\", typeid(KeyT).name()); fflush(stdout);\n    InitializeKeyBits(UNIFORM, h_keys.get(), max_items, 0);\n    TestSizes(h_keys.get(), max_items, max_segments, false);\n\n    printf(\"\\nTesting natural number %s keys\\n\", typeid(KeyT).name()); fflush(stdout);\n    InitializeKeyBits(INTEGER_SEED, h_keys.get(), max_items, 0);\n    TestSizes(h_keys.get(), max_items, max_segments, false);\n\n    if (WITH_PRE_SORTED)\n    {\n        // Presorting is only used for testing large input arrays.\n        const std::size_t large_num_items = std::size_t(4350000007ull);\n\n        // A conservative check for memory, as we don't know ValueT or whether\n        // the overwrite is allowed until later.\n        // For ValueT, the check is actually exact unless TEST_VALUE_TYPE == 3.\n        if (!HasEnoughMemory<KeyT, SmallestValueT>(large_num_items, true))\n        {\n            printf(\"Skipping the permutation-based test due to insufficient device memory\\n\");\n            return;\n        }\n\n        h_keys.reset(nullptr); // Explicitly free old buffer before allocating.\n        h_keys.reset(new KeyT[large_num_items]);\n\n        printf(\"\\nTesting pre-sorted and randomly permuted %s keys\\n\", typeid(KeyT).name());\n        fflush(stdout);\n        InitializeKeysSorted(h_keys.get(), large_num_items);\n        fflush(stdout);\n        TestSizes(h_keys.get(), large_num_items, max_segments, true);\n        fflush(stdout);\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Simple test\n//---------------------------------------------------------------------\n\ntemplate <\n    Backend     BACKEND,\n    typename    KeyT,\n    typename    ValueT,\n    bool        IS_DESCENDING>\nvoid Test(\n    std::size_t num_items,\n    int         num_segments,\n    GenMode     gen_mode,\n    int         entropy_reduction,\n    int         begin_bit,\n    int         end_bit)\n{\n    const bool KEYS_ONLY = std::is_same<ValueT, NullType>::value;\n\n    KeyT         *h_keys             = new KeyT[num_items];\n    std::size_t  *h_reference_ranks  = NULL;\n    KeyT         *h_reference_keys   = NULL;\n    ValueT       *h_values           = NULL;\n    ValueT       *h_reference_values = NULL;\n    size_t       *h_segment_offsets  = new std::size_t[num_segments + 1];\n\n    std::size_t* d_segment_offsets = nullptr;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_segment_offsets, sizeof(std::size_t) * (num_segments + 1)));\n\n    if (end_bit < 0)\n        end_bit = sizeof(KeyT) * 8;\n\n    InitializeKeyBits(gen_mode, h_keys, num_items, entropy_reduction);\n    InitializeSegments(num_items, num_segments, h_segment_offsets);\n    CubDebugExit(hipMemcpy(d_segment_offsets, h_segment_offsets, sizeof(std::size_t) * (num_segments + 1), hipMemcpyHostToDevice));\n    InitializeSolution<IS_DESCENDING, !KEYS_ONLY>(\n        h_keys, num_items, num_segments, false, h_segment_offsets,\n        begin_bit, end_bit, h_reference_ranks, h_reference_keys);\n\n    if (!KEYS_ONLY)\n    {\n        h_values            = new ValueT[num_items];\n        h_reference_values  = new ValueT[num_items];\n\n        for (std::size_t i = 0; i < num_items; ++i)\n        {\n            InitValue(INTEGER_SEED, h_values[i], i);\n            InitValue(INTEGER_SEED, h_reference_values[i], h_reference_ranks[i]);\n        }\n    }\n    if (h_reference_ranks) delete[] h_reference_ranks;\n\n    printf(\"\\nTesting bits [%d,%d) of %s keys with gen-mode %d\\n\", begin_bit, end_bit, typeid(KeyT).name(), gen_mode); fflush(stdout);\n    Test<BACKEND, IS_DESCENDING>(\n        h_keys, h_values,\n        num_items, num_segments, d_segment_offsets, d_segment_offsets + 1,\n        begin_bit, end_bit, h_reference_keys, h_reference_values);\n\n    if (h_keys)             delete[] h_keys;\n    if (h_reference_keys)   delete[] h_reference_keys;\n    if (h_values)           delete[] h_values;\n    if (h_reference_values) delete[] h_reference_values;\n    if (h_segment_offsets)  delete[] h_segment_offsets;\n    if (d_segment_offsets) CubDebugExit(g_allocator.DeviceFree(d_segment_offsets));\n}\n\n#if TEST_VALUE_TYPE == 0\nvoid TestUnspecifiedRanges()\n{\n  const std::size_t num_items = 1024 * 1024;\n  const std::size_t max_segments = 42;\n  const std::size_t avg_segment_size = num_items / max_segments;\n\n  for (int iteration = 0; iteration < 4; iteration++)\n  {\n    thrust::host_vector<int> h_offsets_begin;\n    thrust::host_vector<int> h_offsets_end;\n\n    h_offsets_begin.reserve(max_segments + 1);\n    h_offsets_end.reserve(max_segments + 1);\n\n    {\n      int offset = 0;\n\n      for (std::size_t sid = 0; sid < max_segments; sid++)\n      {\n        const int segment_size = \n          static_cast<int>(RandomValue(avg_segment_size));\n\n        const bool segment_is_utilized = segment_size > 0 \n                                       && RandomValue(100) > 60;\n\n        if (segment_is_utilized)\n        {\n          h_offsets_begin.push_back(offset);\n          h_offsets_end.push_back(offset + segment_size);\n        }\n\n        offset += segment_size;\n      }\n\n      if (h_offsets_begin.empty())\n      {\n        h_offsets_begin.push_back(avg_segment_size);\n        h_offsets_end.push_back(num_items);\n      }\n    }\n\n    thrust::device_vector<int> keys(num_items);\n    thrust::device_vector<int> values(num_items);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    thrust::device_vector<int> d_offsets_begin = h_offsets_begin;\n    thrust::device_vector<int> d_offsets_end = h_offsets_end;\n\n    thrust::device_vector<int> expected_keys = keys;\n    thrust::device_vector<int> expected_values = values;\n\n    const int num_segments = static_cast<int>(h_offsets_begin.size());\n\n    thrust::device_vector<int> result_keys = keys;\n    thrust::device_vector<int> result_values = values;\n\n    for (int sid = 0; sid < num_segments; sid++)\n    {\n      const int segment_begin = h_offsets_begin[sid];\n      const int segment_end = h_offsets_end[sid];\n\n      thrust::sort_by_key(expected_keys.begin() + segment_begin,\n                          expected_keys.begin() + segment_end,\n                          expected_values.begin() + segment_begin);\n    }\n\n    {\n      hipcub::DoubleBuffer<int> keys_buffer(\n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()));\n\n      hipcub::DoubleBuffer<int> values_buffer(\n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()));\n\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{nullptr};\n\n      CubDebugExit(hipcub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(hipcub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          keys_buffer, values_buffer, \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      for (int sid = 0; sid < num_segments; sid++)\n      {\n        const int segment_begin = h_offsets_begin[sid];\n        const int segment_end = h_offsets_end[sid];\n\n        if (keys_buffer.selector == 0)\n        {\n          thrust::copy(\n              keys.begin() + segment_begin,\n              keys.begin() + segment_end,\n              result_keys.begin() + segment_begin);\n        }\n                       \n        if (values_buffer.selector == 0)\n        {\n          thrust::copy(\n              values.begin() + segment_begin,\n              values.begin() + segment_end,\n              result_values.begin() + segment_begin);\n        }\n      }\n    }\n\n    AssertEquals(result_keys, expected_keys); \n    AssertEquals(result_values, expected_values);\n\n    thrust::sequence(keys.rbegin(), keys.rend());\n    thrust::sequence(values.rbegin(), values.rend());\n\n    result_keys = keys;\n    result_values = values;\n\n    {\n      std::size_t temp_storage_bytes{};\n      std::uint8_t *d_temp_storage{};\n\n      CubDebugExit(hipcub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n\n      thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n      d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n      CubDebugExit(hipcub::DeviceSegmentedRadixSort::SortPairs(\n          d_temp_storage, temp_storage_bytes, \n          thrust::raw_pointer_cast(keys.data()), \n          thrust::raw_pointer_cast(result_keys.data()), \n          thrust::raw_pointer_cast(values.data()), \n          thrust::raw_pointer_cast(result_values.data()), \n          num_items, num_segments, \n          thrust::raw_pointer_cast(d_offsets_begin.data()),\n          thrust::raw_pointer_cast(d_offsets_end.data()),\n          0, sizeof(int) * 8));\n    }\n\n    AssertEquals(result_values, expected_values);\n    AssertEquals(result_keys, expected_keys);\n  }\n}\n#endif\n\n#if TEST_KEY_BYTES == 4\n// Following tests check that new decomposer API doesn't break old API. \n// It's disabled because some compilers don't like implicit conversions, which\n// is required for the test. Once we figure out how to temporarily enable conversion, we can\n// re-enable the test.\n#define ENABLING_CONVERSION_IS_FIGURED_OUT 0\n#if ENABLING_CONVERSION_IS_FIGURED_OUT \nstruct bit_selector\n{\n  int bit;\n\n  operator int() const\n  {\n    return bit;\n  }\n};\n\ntemplate <class BeginBitT, class EndBitT, class... Ts>\nvoid device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(BeginBitT begin_bit, EndBitT end_bit, Ts... args)\n{\n  const int num_items = 0;\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortKeys(d_temp_storage,\n                                   temp_storage_bytes,\n                                   args...,\n                                   num_items,\n                                   begin_bit,\n                                   end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortKeys(d_temp_storage,\n                                   temp_storage_bytes,\n                                   args...,\n                                   num_items,\n                                   begin_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortKeysDescending(d_temp_storage,\n                                             temp_storage_bytes,\n                                             args...,\n                                             num_items,\n                                             begin_bit,\n                                             end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortKeysDescending(d_temp_storage,\n                                             temp_storage_bytes,\n                                             args...,\n                                             num_items,\n                                             begin_bit);\n  }\n}\n\ntemplate <class BeginBitT, class EndBitT, class... Ts>\nvoid device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(BeginBitT begin_bit,\n                                                                         EndBitT end_bit,\n                                                                         Ts... args)\n{\n  const int num_items = 0;\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortPairs(d_temp_storage,\n                                    temp_storage_bytes,\n                                    args...,\n                                    num_items,\n                                    begin_bit,\n                                    end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortPairs(d_temp_storage,\n                                    temp_storage_bytes,\n                                    args...,\n                                    num_items,\n                                    begin_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortPairsDescending(d_temp_storage,\n                                              temp_storage_bytes,\n                                              args...,\n                                              num_items,\n                                              begin_bit,\n                                              end_bit);\n  }\n\n  {\n    std::size_t temp_storage_bytes = 0;\n    std::uint8_t *d_temp_storage   = nullptr;\n\n    hipcub::DeviceRadixSort::SortPairsDescending(d_temp_storage,\n                                              temp_storage_bytes,\n                                              args...,\n                                              num_items,\n                                              begin_bit);\n  }\n}\n\ntemplate <class BeginBitT, class EndBitT>\nvoid device_radix_sort_allows_implicit_conversions_for_bits(BeginBitT begin_bit, EndBitT end_bit)\n{\n  int *d_i_ptr = nullptr;\n\n  device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                     end_bit,\n                                                                     d_i_ptr,\n                                                                     d_i_ptr);\n\n  hipcub::DoubleBuffer<int> keys(d_i_ptr, d_i_ptr);\n  device_radix_sort_keys_allows_implicit_conversions_for_bits_helper(begin_bit, end_bit, keys);\n\n  #if TEST_VALUE_TYPE == 2\n  unsigned int *d_u_ptr = nullptr;\n\n  device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                      end_bit,\n                                                                      d_i_ptr,\n                                                                      d_i_ptr,\n                                                                      d_u_ptr,\n                                                                      d_u_ptr);\n\n  hipcub::DoubleBuffer<unsigned int> pairs(d_u_ptr, d_u_ptr);\n  device_radix_sort_pairs_allows_implicit_conversions_for_bits_helper(begin_bit,\n                                                                      end_bit,\n                                                                      keys,\n                                                                      pairs);\n  #endif\n}\n\nvoid device_radix_sort_allows_implicit_conversions_for_bits()\n{\n  int begin_i = 0;\n  long long int begin_lli = 0;\n  bit_selector begin_bs{0};\n\n  int end_i = 2;\n  long long int end_lli = 2;\n  bit_selector end_bs{2};\n\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_i, end_bs);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_lli, end_bs);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_i);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_lli);\n  device_radix_sort_allows_implicit_conversions_for_bits(begin_bs, end_bs);\n}\n#endif // ENABLING_CONVERSION_IS_FIGURED_OUT \n#endif // TEST_KEY_BYTES == 4\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    std::size_t num_items = ~std::size_t(0);\n    int num_segments = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"s\", num_segments);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--s=<num segments> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_KEY_BYTES bytes 1:2:4:8:16\n    // %PARAM% TEST_VALUE_TYPE pairs 0:1:2:3\n    //   0->Keys only\n    //   1->uchar\n    //   2->uint\n    //   3->[ull,TestBar] (TestBar is cheap to build, included here to\n    //                     reduce total number of targets)\n\n    // To reduce testing time, some key types are only tested when not\n    // testing pairs:\n#if TEST_VALUE_TYPE == 0\n#define TEST_EXTENDED_KEY_TYPES\n#endif\n\n    // Compile/run thorough tests\n#if TEST_KEY_BYTES == 1\n\n    TestGen<signed char, true>        (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<bool, false>              (num_items, num_segments);\n    TestGen<signed char, false>       (num_items, num_segments);\n    TestGen<unsigned char, false>     (num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 2\n    TestGen<unsigned short, true>     (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<short, false>             (num_items, num_segments);\n\n#if !_NVHPC_CUDA\n    TestGen<half_t, false>            (num_items, num_segments);\n#endif // CTK >= 9\n\n#if !_NVHPC_CUDA\n#if !defined(__ICC)\n    // Fails with `-0 != 0` with ICC for unknown reasons. See #333.\n    TestGen<bfloat16_t, false>        (num_items, num_segments);\n#endif // !ICC\n#endif // CTK >= 11\n\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 4\n\n    TestGen<int, true>                (num_items, num_segments);\n\n#if TEST_VALUE_TYPE == 0\n    TestUnspecifiedRanges();\n#endif\n\n#if ENABLING_CONVERSION_IS_FIGURED_OUT \n    device_radix_sort_allows_implicit_conversions_for_bits();\n#endif\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<float, false>             (num_items, num_segments);\n    TestGen<unsigned int, false>      (num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 8\n\n    TestGen<double, true>             (num_items, num_segments);\n\n#ifdef TEST_EXTENDED_KEY_TYPES\n    TestGen<long long, false>         (num_items, num_segments);\n    TestGen<unsigned long long, false>(num_items, num_segments);\n#endif // TEST_EXTENDED_KEY_TYPES\n\n#elif TEST_KEY_BYTES == 16\n\n#if CUB_IS_INT128_ENABLED \n    TestGen<__int128_t,  false>(num_items, num_segments);\n    TestGen<__uint128_t, false>(num_items, num_segments);\n#else\n    // Fix unused static function for MSVC\n    BackendToString(CUB);\n#endif\n\n#endif // TEST_KEY_BYTES switch\n\n    return 0;\n}\n", "source": "/kaggle/working/cub/test/test_device_radix_sort.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_run_length_decode.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\n/******************************************************************************\n * HELPER CLASS FOR RUN-LENGTH DECODING TESTS\n ******************************************************************************/\n\n/**\n * \\brief Class template to facilitate testing the BlockRunLengthDecode algorithm for all its\n * template parameter specialisations.\n *\n * \\tparam ItemItT The item type being run-length decoded\n * \\tparam RunLengthsItT Iterator type providing the runs' lengths\n * \\tparam RUNS_PER_THREAD The number of runs that each thread is getting assigned to\n * \\tparam DECODED_ITEMS_PER_THREAD The number of run-length decoded items that each thread is \n *         decoding \\tparam TEST_RELATIVE_OFFSETS_ Whether to also retrieve each decoded item's \n *         relative offset within its run \\tparam TEST_RUN_OFFSETS_ Whether to pass in each run's \n *         offset instead of each run's length \\tparam BLOCK_DIM_X The thread block length in \n *         threads along the X dimension\n * \\tparam BLOCK_DIM_Y The thread block length in threads along the Y dimension\n * \\tparam BLOCK_DIM_Z The thread block length in threads along the Z dimension\n */\ntemplate <typename ItemItT,\n          typename RunLengthsItT,\n          int RUNS_PER_THREAD,\n          int DECODED_ITEMS_PER_THREAD,\n          bool TEST_RELATIVE_OFFSETS_,\n          bool TEST_RUN_OFFSETS_,\n          int BLOCK_DIM_X,\n          int BLOCK_DIM_Y = 1,\n          int BLOCK_DIM_Z = 1>\nclass AgentTestBlockRunLengthDecode\n{\npublic:\n  constexpr static uint32_t BLOCK_THREADS     = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr static uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * BLOCK_THREADS;\n  constexpr static bool TEST_RELATIVE_OFFSETS = TEST_RELATIVE_OFFSETS_;\n\nprivate:\n  using RunItemT   = cub::detail::value_t<ItemItT>;\n  using RunLengthT = cub::detail::value_t<RunLengthsItT>;\n\n  using BlockRunOffsetScanT =\n    cub::BlockScan<RunLengthT, BLOCK_DIM_X, cub::BLOCK_SCAN_RAKING, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockRunLengthDecodeT =\n    cub::BlockRunLengthDecode<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, DECODED_ITEMS_PER_THREAD>;\n\n  using BlockLoadRunItemT = cub::BlockLoad<RunItemT,\n                                           BLOCK_DIM_X,\n                                           RUNS_PER_THREAD,\n                                           cub::BLOCK_LOAD_WARP_TRANSPOSE,\n                                           BLOCK_DIM_Y,\n                                           BLOCK_DIM_Z>;\n\n  using BlockLoadRunLengthsT = cub::BlockLoad<RunLengthT,\n                                              BLOCK_DIM_X,\n                                              RUNS_PER_THREAD,\n                                              cub::BLOCK_LOAD_WARP_TRANSPOSE,\n                                              BLOCK_DIM_Y,\n                                              BLOCK_DIM_Z>;\n\n  using BlockStoreDecodedItemT = cub::BlockStore<RunItemT,\n                                                 BLOCK_DIM_X,\n                                                 DECODED_ITEMS_PER_THREAD,\n                                                 cub::BLOCK_STORE_WARP_TRANSPOSE,\n                                                 BLOCK_DIM_Y,\n                                                 BLOCK_DIM_Z>;\n\n  using BlockStoreRelativeOffsetT = cub::BlockStore<RunLengthT,\n                                                    BLOCK_DIM_X,\n                                                    DECODED_ITEMS_PER_THREAD,\n                                                    cub::BLOCK_STORE_WARP_TRANSPOSE,\n                                                    BLOCK_DIM_Y,\n                                                    BLOCK_DIM_Z>;\n\n  __device__ __forceinline__ BlockRunLengthDecodeT\n  InitBlockRunLengthDecode(RunItemT (&unique_items)[RUNS_PER_THREAD],\n                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                           RunLengthT &decoded_size,\n                           cub::Int2Type<true> /*test_run_offsets*/)\n  {\n    RunLengthT run_offsets[RUNS_PER_THREAD];\n    BlockRunOffsetScanT(temp_storage.run_offsets_scan_storage)\n      .ExclusiveSum(run_lengths, run_offsets, decoded_size);\n\n    // Ensure temporary shared memory can be repurposed\n    cub::CTA_SYNC();\n\n    // Construct BlockRunLengthDecode and initialize with the run offsets\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage,\n                                 unique_items,\n                                 run_offsets);\n  }\n\n  __device__ __forceinline__ BlockRunLengthDecodeT\n  InitBlockRunLengthDecode(RunItemT (&unique_items)[RUNS_PER_THREAD],\n                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                           RunLengthT &decoded_size,\n                           cub::Int2Type<false> /*test_run_offsets*/)\n  {\n    // Construct BlockRunLengthDecode and initialize with the run lengths\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage,\n                                 unique_items,\n                                 run_lengths,\n                                 decoded_size);\n  }\n\n  __device__ __forceinline__ void LoadRuns(ItemItT d_block_unique_items,\n                                           RunLengthsItT d_block_run_lengths,\n                                           RunItemT (&unique_items)[RUNS_PER_THREAD],\n                                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                                           size_t num_valid_items)\n  {\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage)\n        .Load(d_block_unique_items, unique_items, num_valid_items);\n    }\n    else\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items);\n    }\n\n    // Ensure BlockLoad's temporary shared memory can be repurposed\n    cub::CTA_SYNC();\n\n    // Load this block's tile of run lengths\n    if (num_valid_items < RUNS_PER_BLOCK)\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths, num_valid_items, static_cast<RunLengthT>(0));\n    else\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths);\n\n    // Ensure temporary shared memory can be repurposed\n    cub::CTA_SYNC();\n  }\n\npublic:\n  union TempStorage\n  {\n    typename BlockLoadRunItemT::TempStorage load_uniques_storage;\n    typename BlockLoadRunLengthsT::TempStorage load_run_lengths_storage;\n    cub::detail::\n      conditional_t<TEST_RUN_OFFSETS_, typename BlockRunOffsetScanT::TempStorage, cub::NullType>\n        run_offsets_scan_storage;\n    struct\n    {\n      typename BlockRunLengthDecodeT::TempStorage run_length_decode_storage;\n      typename BlockStoreDecodedItemT::TempStorage store_decoded_runs_storage;\n      typename BlockStoreRelativeOffsetT::TempStorage store_relative_offsets;\n    } decode;\n  };\n\n  TempStorage &temp_storage;\n\n  __device__ __forceinline__ AgentTestBlockRunLengthDecode(TempStorage &temp_storage)\n      : temp_storage(temp_storage)\n  {}\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, and computes their \"decompressed\" (run-length\n   * decoded) size.\n   */\n  __device__ __forceinline__ uint32_t GetDecodedSize(ItemItT d_block_unique_items,\n                                                     RunLengthsItT d_block_run_lengths,\n                                                     size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items,\n                               run_lengths,\n                               decoded_size,\n                               cub::Int2Type<TEST_RUN_OFFSETS_>());\n    return decoded_size;\n  }\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, run-length decodes them, and writes the results\n   * to \\p d_block_decoded_out.\n   */\n  template <typename UniqueItemOutItT, typename RelativeOffsetOutItT>\n  __device__ __forceinline__ uint32_t WriteDecodedRuns(ItemItT d_block_unique_items,\n                                                       RunLengthsItT d_block_run_lengths,\n                                                       UniqueItemOutItT d_block_decoded_out,\n                                                       RelativeOffsetOutItT d_block_rel_out,\n                                                       size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items,\n                               run_lengths,\n                               decoded_size,\n                               cub::Int2Type<TEST_RUN_OFFSETS_>());\n\n    // Run-length decode (\"decompress\") the runs into a window buffer of limited size. This is\n    // repeated until all runs have been decoded.\n    uint32_t decoded_window_offset = 0U;\n    while (decoded_window_offset < decoded_size)\n    {\n      RunLengthT relative_offsets[DECODED_ITEMS_PER_THREAD];\n      RunItemT decoded_items[DECODED_ITEMS_PER_THREAD];\n\n      // The number of decoded items that are valid within this window (aka pass) of run-length\n      // decoding\n      uint32_t num_valid_items = decoded_size - decoded_window_offset;\n      run_length_decode.RunLengthDecode(decoded_items, relative_offsets, decoded_window_offset);\n      BlockStoreDecodedItemT(temp_storage.decode.store_decoded_runs_storage)\n        .Store(d_block_decoded_out + decoded_window_offset, decoded_items, num_valid_items);\n\n      if (TEST_RELATIVE_OFFSETS)\n      {\n        BlockStoreRelativeOffsetT(temp_storage.decode.store_relative_offsets)\n          .Store(d_block_rel_out + decoded_window_offset, relative_offsets, num_valid_items);\n      }\n\n      decoded_window_offset += DECODED_ITEMS_PER_THREAD * BLOCK_THREADS;\n    }\n    return decoded_size;\n  }\n};\n\n/******************************************************************************\n * [STAGE 1] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename OffsetT,\n          typename DecodedSizesOutT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__\n  void BlockRunLengthDecodeGetSizeKernel(const ItemItT d_unique_items,\n                                         const RunLengthsItT d_run_lengths,\n                                         const OffsetT num_runs,\n                                         DecodedSizesOutT d_decoded_sizes)\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset)\n                                                                       : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  uint64_t num_decoded_items = run_length_decode_agent.GetDecodedSize(d_unique_items + block_offset,\n                                                                      d_run_lengths + block_offset,\n                                                                      num_valid_runs);\n\n  d_decoded_sizes[blockIdx.x] = num_decoded_items;\n}\n\n/******************************************************************************\n * [STAGE 2] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename DecodedSizesOutT,\n          typename OffsetT,\n          typename DecodedItemsOutItT,\n          typename RelativeOffsetOutItT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__\n  void BlockRunLengthDecodeTestKernel(const ItemItT d_unique_items,\n                                      const RunLengthsItT d_run_lengths,\n                                      const DecodedSizesOutT d_decoded_offsets,\n                                      const OffsetT num_runs,\n                                      DecodedItemsOutItT d_decoded_items,\n                                      RelativeOffsetOutItT d_relative_offsets)\n\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset)\n                                                                       : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  run_length_decode_agent.WriteDecodedRuns(d_unique_items + block_offset,\n                                           d_run_lengths + block_offset,\n                                           d_decoded_items + d_decoded_offsets[blockIdx.x],\n                                           d_relative_offsets + d_decoded_offsets[blockIdx.x],\n                                           num_valid_runs);\n}\n\nstruct ModOp\n{\n  using T = uint32_t;\n  __host__ __device__ __forceinline__ T operator()(const T &x) const { return 1 + (x % 100); }\n};\n\ntemplate <uint32_t RUNS_PER_THREAD,\n          uint32_t DECODED_ITEMS_PER_THREAD,\n          uint32_t BLOCK_DIM_X,\n          uint32_t BLOCK_DIM_Y,\n          uint32_t BLOCK_DIM_Z,\n          bool TEST_RUN_OFFSETS,\n          bool TEST_RELATIVE_OFFSETS>\nvoid TestAlgorithmSpecialisation()\n{\n  constexpr uint32_t THREADS_PER_BLOCK = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * THREADS_PER_BLOCK;\n\n  using RunItemT   = float;\n  using RunLengthT = uint32_t;\n  using ItemItT    = cub::CountingInputIterator<RunItemT>;\n  using RunLengthsItT =\n    cub::TransformInputIterator<RunLengthT, ModOp, cub::CountingInputIterator<RunLengthT>>;\n\n  ItemItT d_unique_items(1000U);\n  RunLengthsItT d_run_lengths(cub::CountingInputIterator<RunLengthT>(0), ModOp{});\n\n  constexpr uint32_t num_runs   = 10000;\n  constexpr uint32_t num_blocks = (num_runs + (RUNS_PER_BLOCK - 1U)) / RUNS_PER_BLOCK;\n\n  size_t temp_storage_bytes      = 0ULL;\n  void *temp_storage             = nullptr;\n  uint32_t *h_num_decoded_total  = nullptr;\n  uint32_t *d_decoded_sizes      = nullptr;\n  uint32_t *d_decoded_offsets    = nullptr;\n  RunItemT *d_decoded_out        = nullptr;\n  RunLengthT *d_relative_offsets = nullptr;\n  RunItemT *h_decoded_out        = nullptr;\n  RunLengthT *h_relative_offsets = nullptr;\n\n  using AgentTestBlockRunLengthDecodeT = AgentTestBlockRunLengthDecode<ItemItT,\n                                                                       RunLengthsItT,\n                                                                       RUNS_PER_THREAD,\n                                                                       DECODED_ITEMS_PER_THREAD,\n                                                                       TEST_RELATIVE_OFFSETS,\n                                                                       TEST_RUN_OFFSETS,\n                                                                       THREADS_PER_BLOCK,\n                                                                       1,\n                                                                       1>;\n\n  enum : uint32_t\n  {\n    TIMER_SIZE_BEGIN = 0,\n    TIMER_SIZE_END,\n    TIMER_DECODE_BEGIN,\n    TIMER_DECODE_END,\n    NUM_TIMERS,\n  };\n\n  // Get temporary storage requirements for the scan (for computing offsets for the per-block\n  // run-length decoded items)\n  cub::DeviceScan::InclusiveSum(nullptr,\n                                temp_storage_bytes,\n                                d_decoded_sizes,\n                                d_decoded_offsets,\n                                num_blocks);\n\n  // Allocate device memory\n  CubDebugExit(cudaMalloc(&temp_storage, temp_storage_bytes));\n  CubDebugExit(cudaMalloc(&d_decoded_sizes, num_blocks * sizeof(*d_decoded_sizes)));\n  // Allocate for the exclusive sum PLUS the overall aggregate\n  CubDebugExit(cudaMalloc(&d_decoded_offsets, (num_blocks + 1) * sizeof(*d_decoded_offsets)));\n  CubDebugExit(cudaMallocHost(&h_num_decoded_total, sizeof(*h_num_decoded_total)));\n\n  // Get the per-block number of items being decoded (i-th thread block writing size to\n  // d_decoded_sizes[i])\n  BlockRunLengthDecodeGetSizeKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items,\n                                            d_run_lengths,\n                                            num_runs,\n                                            d_decoded_sizes);\n\n  // Compute offsets for the runs decoded by each block (exclusive sum + aggregate)\n  CubDebugExit(cudaMemsetAsync(d_decoded_offsets, 0, sizeof(d_decoded_offsets[0])));\n  CubDebugExit(cub::DeviceScan::InclusiveSum(temp_storage,\n                                             temp_storage_bytes,\n                                             d_decoded_sizes,\n                                             &d_decoded_offsets[1],\n                                             num_blocks));\n\n  // Copy the total decoded size to CPU in order to allocate just the right amount of device memory\n  CubDebugExit(cudaMemcpy(h_num_decoded_total,\n                          &d_decoded_offsets[num_blocks],\n                          sizeof(*h_num_decoded_total),\n                          cudaMemcpyDeviceToHost));\n\n  // Allocate device memory for the run-length decoded output\n  CubDebugExit(cudaMallocHost(&h_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  CubDebugExit(cudaMalloc(&d_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(cudaMalloc(&d_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n    CubDebugExit(cudaMallocHost(&h_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n  }\n\n  // Perform the block-wise run-length decoding (each block taking its offset from\n  // d_decoded_offsets)\n  BlockRunLengthDecodeTestKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items,\n                                            d_run_lengths,\n                                            d_decoded_offsets,\n                                            num_runs,\n                                            d_decoded_out,\n                                            d_relative_offsets);\n\n  // Copy back results for verification\n  CubDebugExit(cudaMemcpy(h_decoded_out,\n                          d_decoded_out,\n                          (*h_num_decoded_total) * sizeof(*h_decoded_out),\n                          cudaMemcpyDeviceToHost));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    // Copy back the relative offsets\n    CubDebugExit(cudaMemcpy(h_relative_offsets,\n                            d_relative_offsets,\n                            (*h_num_decoded_total) * sizeof(*h_relative_offsets),\n                            cudaMemcpyDeviceToHost));\n  }\n\n  // Generate host-side run-length decoded data for verification\n  std::vector<std::pair<RunItemT, RunLengthT>> host_golden;\n  host_golden.reserve(*h_num_decoded_total);\n  for (uint32_t run = 0; run < num_runs; run++)\n  {\n    for (RunLengthT i = 0; i < d_run_lengths[run]; i++)\n    {\n      host_golden.push_back({d_unique_items[run], i});\n    }\n  }\n\n  // Verify the total run-length decoded size is correct\n  REQUIRE(host_golden.size() == h_num_decoded_total[0]);\n\n  // Verify the run-length decoded data is correct\n  bool cmp_eq = true;\n  for (uint32_t i = 0; i < host_golden.size(); i++)\n  {\n    if (host_golden[i].first != h_decoded_out[i])\n    {\n      FAIL(\"Mismatch at #\" << i << \": CPU item: \" << host_golden[i].first\n           << \", GPU: \" << h_decoded_out[i] << \"\\n\");\n      cmp_eq = false;\n    }\n    if (TEST_RELATIVE_OFFSETS)\n    {\n      if (host_golden[i].second != h_relative_offsets[i])\n      {\n        FAIL(\"Mismatch of relative offset at #\" << i\n             << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i]\n             << \"; relative offsets: CPU: \" << host_golden[i].second\n             << \", GPU: \" << h_relative_offsets[i] << \"\\n\");\n        cmp_eq = false;\n        break;\n      }\n    }\n  }\n\n  REQUIRE(cmp_eq == true);\n\n  // Clean up memory allocations\n  CubDebugExit(cudaFree(temp_storage));\n  CubDebugExit(cudaFree(d_decoded_sizes));\n  CubDebugExit(cudaFree(d_decoded_offsets));\n  CubDebugExit(cudaFree(d_decoded_out));\n  CubDebugExit(cudaFreeHost(h_num_decoded_total));\n  CubDebugExit(cudaFreeHost(h_decoded_out));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(cudaFree(d_relative_offsets));\n    CubDebugExit(cudaFreeHost(h_relative_offsets));\n  }\n}\n\nconstexpr bool DO_TEST_RELATIVE_OFFSETS     = true;\nconstexpr bool DO_NOT_TEST_RELATIVE_OFFSETS = false;\n\nconstexpr bool TEST_WITH_RUN_OFFSETS = true;\nconstexpr bool TEST_WITH_RUN_LENGTHS = false;\n\ntemplate <int RunsPerThread,\n          int DecodedItemsPerThread,\n          int BlockDimX,\n          int BlockDimY = 1,\n          int BlockDimZ = 1>\nstruct params_t\n{\n  static constexpr int runs_per_thread          = RunsPerThread;\n  static constexpr int decoded_items_per_thread = DecodedItemsPerThread;\n  static constexpr int block_dim_x              = BlockDimX;\n  static constexpr int block_dim_y              = BlockDimY;\n  static constexpr int block_dim_z              = BlockDimZ;\n};\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run lengths and offsets relative to each run\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run lengths and performs normal run-length \"\n              \"decoding\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run offsets and generates offsets relative to \"\n              \"each run\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run offsets and performs normal run-length \"\n              \"decoding\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_run_length_decode.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\n/******************************************************************************\n * HELPER CLASS FOR RUN-LENGTH DECODING TESTS\n ******************************************************************************/\n\n/**\n * \\brief Class template to facilitate testing the BlockRunLengthDecode algorithm for all its\n * template parameter specialisations.\n *\n * \\tparam ItemItT The item type being run-length decoded\n * \\tparam RunLengthsItT Iterator type providing the runs' lengths\n * \\tparam RUNS_PER_THREAD The number of runs that each thread is getting assigned to\n * \\tparam DECODED_ITEMS_PER_THREAD The number of run-length decoded items that each thread is \n *         decoding \\tparam TEST_RELATIVE_OFFSETS_ Whether to also retrieve each decoded item's \n *         relative offset within its run \\tparam TEST_RUN_OFFSETS_ Whether to pass in each run's \n *         offset instead of each run's length \\tparam BLOCK_DIM_X The thread block length in \n *         threads along the X dimension\n * \\tparam BLOCK_DIM_Y The thread block length in threads along the Y dimension\n * \\tparam BLOCK_DIM_Z The thread block length in threads along the Z dimension\n */\ntemplate <typename ItemItT,\n          typename RunLengthsItT,\n          int RUNS_PER_THREAD,\n          int DECODED_ITEMS_PER_THREAD,\n          bool TEST_RELATIVE_OFFSETS_,\n          bool TEST_RUN_OFFSETS_,\n          int BLOCK_DIM_X,\n          int BLOCK_DIM_Y = 1,\n          int BLOCK_DIM_Z = 1>\nclass AgentTestBlockRunLengthDecode\n{\npublic:\n  constexpr static uint32_t BLOCK_THREADS     = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr static uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * BLOCK_THREADS;\n  constexpr static bool TEST_RELATIVE_OFFSETS = TEST_RELATIVE_OFFSETS_;\n\nprivate:\n  using RunItemT   = hipcub::detail::value_t<ItemItT>;\n  using RunLengthT = hipcub::detail::value_t<RunLengthsItT>;\n\n  using BlockRunOffsetScanT =\n    hipcub::BlockScan<RunLengthT, BLOCK_DIM_X, hipcub::BLOCK_SCAN_RAKING, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockRunLengthDecodeT =\n    hipcub::BlockRunLengthDecode<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, DECODED_ITEMS_PER_THREAD>;\n\n  using BlockLoadRunItemT = hipcub::BlockLoad<RunItemT,\n                                           BLOCK_DIM_X,\n                                           RUNS_PER_THREAD,\n                                           hipcub::BLOCK_LOAD_WARP_TRANSPOSE,\n                                           BLOCK_DIM_Y,\n                                           BLOCK_DIM_Z>;\n\n  using BlockLoadRunLengthsT = hipcub::BlockLoad<RunLengthT,\n                                              BLOCK_DIM_X,\n                                              RUNS_PER_THREAD,\n                                              hipcub::BLOCK_LOAD_WARP_TRANSPOSE,\n                                              BLOCK_DIM_Y,\n                                              BLOCK_DIM_Z>;\n\n  using BlockStoreDecodedItemT = hipcub::BlockStore<RunItemT,\n                                                 BLOCK_DIM_X,\n                                                 DECODED_ITEMS_PER_THREAD,\n                                                 hipcub::BLOCK_STORE_WARP_TRANSPOSE,\n                                                 BLOCK_DIM_Y,\n                                                 BLOCK_DIM_Z>;\n\n  using BlockStoreRelativeOffsetT = hipcub::BlockStore<RunLengthT,\n                                                    BLOCK_DIM_X,\n                                                    DECODED_ITEMS_PER_THREAD,\n                                                    hipcub::BLOCK_STORE_WARP_TRANSPOSE,\n                                                    BLOCK_DIM_Y,\n                                                    BLOCK_DIM_Z>;\n\n  __device__ __forceinline__ BlockRunLengthDecodeT\n  InitBlockRunLengthDecode(RunItemT (&unique_items)[RUNS_PER_THREAD],\n                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                           RunLengthT &decoded_size,\n                           hipcub::Int2Type<true> /*test_run_offsets*/)\n  {\n    RunLengthT run_offsets[RUNS_PER_THREAD];\n    BlockRunOffsetScanT(temp_storage.run_offsets_scan_storage)\n      .ExclusiveSum(run_lengths, run_offsets, decoded_size);\n\n    // Ensure temporary shared memory can be repurposed\n    hipcub::CTA_SYNC();\n\n    // Construct BlockRunLengthDecode and initialize with the run offsets\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage,\n                                 unique_items,\n                                 run_offsets);\n  }\n\n  __device__ __forceinline__ BlockRunLengthDecodeT\n  InitBlockRunLengthDecode(RunItemT (&unique_items)[RUNS_PER_THREAD],\n                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                           RunLengthT &decoded_size,\n                           hipcub::Int2Type<false> /*test_run_offsets*/)\n  {\n    // Construct BlockRunLengthDecode and initialize with the run lengths\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage,\n                                 unique_items,\n                                 run_lengths,\n                                 decoded_size);\n  }\n\n  __device__ __forceinline__ void LoadRuns(ItemItT d_block_unique_items,\n                                           RunLengthsItT d_block_run_lengths,\n                                           RunItemT (&unique_items)[RUNS_PER_THREAD],\n                                           RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n                                           size_t num_valid_items)\n  {\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage)\n        .Load(d_block_unique_items, unique_items, num_valid_items);\n    }\n    else\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items);\n    }\n\n    // Ensure BlockLoad's temporary shared memory can be repurposed\n    hipcub::CTA_SYNC();\n\n    // Load this block's tile of run lengths\n    if (num_valid_items < RUNS_PER_BLOCK)\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths, num_valid_items, static_cast<RunLengthT>(0));\n    else\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths);\n\n    // Ensure temporary shared memory can be repurposed\n    hipcub::CTA_SYNC();\n  }\n\npublic:\n  union TempStorage\n  {\n    typename BlockLoadRunItemT::TempStorage load_uniques_storage;\n    typename BlockLoadRunLengthsT::TempStorage load_run_lengths_storage;\n    hipcub::detail::\n      conditional_t<TEST_RUN_OFFSETS_, typename BlockRunOffsetScanT::TempStorage, hipcub::NullType>\n        run_offsets_scan_storage;\n    struct\n    {\n      typename BlockRunLengthDecodeT::TempStorage run_length_decode_storage;\n      typename BlockStoreDecodedItemT::TempStorage store_decoded_runs_storage;\n      typename BlockStoreRelativeOffsetT::TempStorage store_relative_offsets;\n    } decode;\n  };\n\n  TempStorage &temp_storage;\n\n  __device__ __forceinline__ AgentTestBlockRunLengthDecode(TempStorage &temp_storage)\n      : temp_storage(temp_storage)\n  {}\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, and computes their \"decompressed\" (run-length\n   * decoded) size.\n   */\n  __device__ __forceinline__ uint32_t GetDecodedSize(ItemItT d_block_unique_items,\n                                                     RunLengthsItT d_block_run_lengths,\n                                                     size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items,\n                               run_lengths,\n                               decoded_size,\n                               hipcub::Int2Type<TEST_RUN_OFFSETS_>());\n    return decoded_size;\n  }\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, run-length decodes them, and writes the results\n   * to \\p d_block_decoded_out.\n   */\n  template <typename UniqueItemOutItT, typename RelativeOffsetOutItT>\n  __device__ __forceinline__ uint32_t WriteDecodedRuns(ItemItT d_block_unique_items,\n                                                       RunLengthsItT d_block_run_lengths,\n                                                       UniqueItemOutItT d_block_decoded_out,\n                                                       RelativeOffsetOutItT d_block_rel_out,\n                                                       size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items,\n                               run_lengths,\n                               decoded_size,\n                               hipcub::Int2Type<TEST_RUN_OFFSETS_>());\n\n    // Run-length decode (\"decompress\") the runs into a window buffer of limited size. This is\n    // repeated until all runs have been decoded.\n    uint32_t decoded_window_offset = 0U;\n    while (decoded_window_offset < decoded_size)\n    {\n      RunLengthT relative_offsets[DECODED_ITEMS_PER_THREAD];\n      RunItemT decoded_items[DECODED_ITEMS_PER_THREAD];\n\n      // The number of decoded items that are valid within this window (aka pass) of run-length\n      // decoding\n      uint32_t num_valid_items = decoded_size - decoded_window_offset;\n      run_length_decode.RunLengthDecode(decoded_items, relative_offsets, decoded_window_offset);\n      BlockStoreDecodedItemT(temp_storage.decode.store_decoded_runs_storage)\n        .Store(d_block_decoded_out + decoded_window_offset, decoded_items, num_valid_items);\n\n      if (TEST_RELATIVE_OFFSETS)\n      {\n        BlockStoreRelativeOffsetT(temp_storage.decode.store_relative_offsets)\n          .Store(d_block_rel_out + decoded_window_offset, relative_offsets, num_valid_items);\n      }\n\n      decoded_window_offset += DECODED_ITEMS_PER_THREAD * BLOCK_THREADS;\n    }\n    return decoded_size;\n  }\n};\n\n/******************************************************************************\n * [STAGE 1] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename OffsetT,\n          typename DecodedSizesOutT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__\n  void BlockRunLengthDecodeGetSizeKernel(const ItemItT d_unique_items,\n                                         const RunLengthsItT d_run_lengths,\n                                         const OffsetT num_runs,\n                                         DecodedSizesOutT d_decoded_sizes)\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset)\n                                                                       : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  uint64_t num_decoded_items = run_length_decode_agent.GetDecodedSize(d_unique_items + block_offset,\n                                                                      d_run_lengths + block_offset,\n                                                                      num_valid_runs);\n\n  d_decoded_sizes[blockIdx.x] = num_decoded_items;\n}\n\n/******************************************************************************\n * [STAGE 2] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename DecodedSizesOutT,\n          typename OffsetT,\n          typename DecodedItemsOutItT,\n          typename RelativeOffsetOutItT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__\n  void BlockRunLengthDecodeTestKernel(const ItemItT d_unique_items,\n                                      const RunLengthsItT d_run_lengths,\n                                      const DecodedSizesOutT d_decoded_offsets,\n                                      const OffsetT num_runs,\n                                      DecodedItemsOutItT d_decoded_items,\n                                      RelativeOffsetOutItT d_relative_offsets)\n\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset)\n                                                                       : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  run_length_decode_agent.WriteDecodedRuns(d_unique_items + block_offset,\n                                           d_run_lengths + block_offset,\n                                           d_decoded_items + d_decoded_offsets[blockIdx.x],\n                                           d_relative_offsets + d_decoded_offsets[blockIdx.x],\n                                           num_valid_runs);\n}\n\nstruct ModOp\n{\n  using T = uint32_t;\n  __host__ __device__ __forceinline__ T operator()(const T &x) const { return 1 + (x % 100); }\n};\n\ntemplate <uint32_t RUNS_PER_THREAD,\n          uint32_t DECODED_ITEMS_PER_THREAD,\n          uint32_t BLOCK_DIM_X,\n          uint32_t BLOCK_DIM_Y,\n          uint32_t BLOCK_DIM_Z,\n          bool TEST_RUN_OFFSETS,\n          bool TEST_RELATIVE_OFFSETS>\nvoid TestAlgorithmSpecialisation()\n{\n  constexpr uint32_t THREADS_PER_BLOCK = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * THREADS_PER_BLOCK;\n\n  using RunItemT   = float;\n  using RunLengthT = uint32_t;\n  using ItemItT    = hipcub::CountingInputIterator<RunItemT>;\n  using RunLengthsItT =\n    hipcub::TransformInputIterator<RunLengthT, ModOp, hipcub::CountingInputIterator<RunLengthT>>;\n\n  ItemItT d_unique_items(1000U);\n  RunLengthsItT d_run_lengths(hipcub::CountingInputIterator<RunLengthT>(0), ModOp{});\n\n  constexpr uint32_t num_runs   = 10000;\n  constexpr uint32_t num_blocks = (num_runs + (RUNS_PER_BLOCK - 1U)) / RUNS_PER_BLOCK;\n\n  size_t temp_storage_bytes      = 0ULL;\n  void *temp_storage             = nullptr;\n  uint32_t *h_num_decoded_total  = nullptr;\n  uint32_t *d_decoded_sizes      = nullptr;\n  uint32_t *d_decoded_offsets    = nullptr;\n  RunItemT *d_decoded_out        = nullptr;\n  RunLengthT *d_relative_offsets = nullptr;\n  RunItemT *h_decoded_out        = nullptr;\n  RunLengthT *h_relative_offsets = nullptr;\n\n  using AgentTestBlockRunLengthDecodeT = AgentTestBlockRunLengthDecode<ItemItT,\n                                                                       RunLengthsItT,\n                                                                       RUNS_PER_THREAD,\n                                                                       DECODED_ITEMS_PER_THREAD,\n                                                                       TEST_RELATIVE_OFFSETS,\n                                                                       TEST_RUN_OFFSETS,\n                                                                       THREADS_PER_BLOCK,\n                                                                       1,\n                                                                       1>;\n\n  enum : uint32_t\n  {\n    TIMER_SIZE_BEGIN = 0,\n    TIMER_SIZE_END,\n    TIMER_DECODE_BEGIN,\n    TIMER_DECODE_END,\n    NUM_TIMERS,\n  };\n\n  // Get temporary storage requirements for the scan (for computing offsets for the per-block\n  // run-length decoded items)\n  hipcub::DeviceScan::InclusiveSum(nullptr,\n                                temp_storage_bytes,\n                                d_decoded_sizes,\n                                d_decoded_offsets,\n                                num_blocks);\n\n  // Allocate device memory\n  CubDebugExit(hipMalloc(&temp_storage, temp_storage_bytes));\n  CubDebugExit(hipMalloc(&d_decoded_sizes, num_blocks * sizeof(*d_decoded_sizes)));\n  // Allocate for the exclusive sum PLUS the overall aggregate\n  CubDebugExit(hipMalloc(&d_decoded_offsets, (num_blocks + 1) * sizeof(*d_decoded_offsets)));\n  CubDebugExit(hipHostMalloc(&h_num_decoded_total, sizeof(*h_num_decoded_total)));\n\n  // Get the per-block number of items being decoded (i-th thread block writing size to\n  // d_decoded_sizes[i])\n  BlockRunLengthDecodeGetSizeKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items,\n                                            d_run_lengths,\n                                            num_runs,\n                                            d_decoded_sizes);\n\n  // Compute offsets for the runs decoded by each block (exclusive sum + aggregate)\n  CubDebugExit(hipMemsetAsync(d_decoded_offsets, 0, sizeof(d_decoded_offsets[0])));\n  CubDebugExit(hipcub::DeviceScan::InclusiveSum(temp_storage,\n                                             temp_storage_bytes,\n                                             d_decoded_sizes,\n                                             &d_decoded_offsets[1],\n                                             num_blocks));\n\n  // Copy the total decoded size to CPU in order to allocate just the right amount of device memory\n  CubDebugExit(hipMemcpy(h_num_decoded_total,\n                          &d_decoded_offsets[num_blocks],\n                          sizeof(*h_num_decoded_total),\n                          hipMemcpyDeviceToHost));\n\n  // Allocate device memory for the run-length decoded output\n  CubDebugExit(hipHostMalloc(&h_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  CubDebugExit(hipMalloc(&d_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(hipMalloc(&d_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n    CubDebugExit(hipHostMalloc(&h_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n  }\n\n  // Perform the block-wise run-length decoding (each block taking its offset from\n  // d_decoded_offsets)\n  BlockRunLengthDecodeTestKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items,\n                                            d_run_lengths,\n                                            d_decoded_offsets,\n                                            num_runs,\n                                            d_decoded_out,\n                                            d_relative_offsets);\n\n  // Copy back results for verification\n  CubDebugExit(hipMemcpy(h_decoded_out,\n                          d_decoded_out,\n                          (*h_num_decoded_total) * sizeof(*h_decoded_out),\n                          hipMemcpyDeviceToHost));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    // Copy back the relative offsets\n    CubDebugExit(hipMemcpy(h_relative_offsets,\n                            d_relative_offsets,\n                            (*h_num_decoded_total) * sizeof(*h_relative_offsets),\n                            hipMemcpyDeviceToHost));\n  }\n\n  // Generate host-side run-length decoded data for verification\n  std::vector<std::pair<RunItemT, RunLengthT>> host_golden;\n  host_golden.reserve(*h_num_decoded_total);\n  for (uint32_t run = 0; run < num_runs; run++)\n  {\n    for (RunLengthT i = 0; i < d_run_lengths[run]; i++)\n    {\n      host_golden.push_back({d_unique_items[run], i});\n    }\n  }\n\n  // Verify the total run-length decoded size is correct\n  REQUIRE(host_golden.size() == h_num_decoded_total[0]);\n\n  // Verify the run-length decoded data is correct\n  bool cmp_eq = true;\n  for (uint32_t i = 0; i < host_golden.size(); i++)\n  {\n    if (host_golden[i].first != h_decoded_out[i])\n    {\n      FAIL(\"Mismatch at #\" << i << \": CPU item: \" << host_golden[i].first\n           << \", GPU: \" << h_decoded_out[i] << \"\\n\");\n      cmp_eq = false;\n    }\n    if (TEST_RELATIVE_OFFSETS)\n    {\n      if (host_golden[i].second != h_relative_offsets[i])\n      {\n        FAIL(\"Mismatch of relative offset at #\" << i\n             << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i]\n             << \"; relative offsets: CPU: \" << host_golden[i].second\n             << \", GPU: \" << h_relative_offsets[i] << \"\\n\");\n        cmp_eq = false;\n        break;\n      }\n    }\n  }\n\n  REQUIRE(cmp_eq == true);\n\n  // Clean up memory allocations\n  CubDebugExit(hipFree(temp_storage));\n  CubDebugExit(hipFree(d_decoded_sizes));\n  CubDebugExit(hipFree(d_decoded_offsets));\n  CubDebugExit(hipFree(d_decoded_out));\n  CubDebugExit(hipHostFree(h_num_decoded_total));\n  CubDebugExit(hipHostFree(h_decoded_out));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(hipFree(d_relative_offsets));\n    CubDebugExit(hipHostFree(h_relative_offsets));\n  }\n}\n\nconstexpr bool DO_TEST_RELATIVE_OFFSETS     = true;\nconstexpr bool DO_NOT_TEST_RELATIVE_OFFSETS = false;\n\nconstexpr bool TEST_WITH_RUN_OFFSETS = true;\nconstexpr bool TEST_WITH_RUN_LENGTHS = false;\n\ntemplate <int RunsPerThread,\n          int DecodedItemsPerThread,\n          int BlockDimX,\n          int BlockDimY = 1,\n          int BlockDimZ = 1>\nstruct params_t\n{\n  static constexpr int runs_per_thread          = RunsPerThread;\n  static constexpr int decoded_items_per_thread = DecodedItemsPerThread;\n  static constexpr int block_dim_x              = BlockDimX;\n  static constexpr int block_dim_y              = BlockDimY;\n  static constexpr int block_dim_z              = BlockDimZ;\n};\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run lengths and offsets relative to each run\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run lengths and performs normal run-length \"\n              \"decoding\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run offsets and generates offsets relative to \"\n              \"each run\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nCUB_TEST_LIST(\"Block Run Length Decode works with run offsets and performs normal run-length \"\n              \"decoding\",\n              \"[rld][block]\",\n              params_t<1, 1, 64>,\n              params_t<1, 3, 32, 2, 3>,\n              params_t<1, 1, 128>,\n              params_t<1, 8, 128>,\n              params_t<3, 1, 256>,\n              params_t<1, 8, 256>,\n              params_t<8, 1, 256>,\n              params_t<1, 1, 256>,\n              params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_run_length_decode.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_scan.cuh>\n\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <cub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_scan_kernel(T *in, T *out, ActionT action)\n{\n  using block_scan_t      = cub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t         = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncthreads();\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = thread_data[item];\n  }\n}\n\ntemplate <cub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_scan(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()),\n                        thrust::raw_pointer_cast(out.data()),\n                        action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <int ItemsPerThread, class BlockScanT, class T>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, int (&thread_data)[ItemsPerThread]) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cub::Min{});\n    }\n    else \n    {\n      scan.InclusiveScan(thread_data, thread_data, cub::Min{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_prefix_op_t\n{\n  T m_prefix;\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : T{};\n      prefix   = prefix + block_aggregate;\n      return retval;\n    }\n  };\n\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, prefix_op);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_prefix_op_t\n{\n  T m_prefix;\n  static constexpr T min_identity = std::numeric_limits<T>::max();\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : min_identity;\n      prefix   = cub::Min{}(prefix, block_aggregate);\n      return retval;\n    }\n  };\n\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cub::Min{}, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cub::Min{}, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, class ScanOpT>\nT host_scan(scan_mode mode, thrust::host_vector<T> &result, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty()) \n  {\n    return {};\n  }\n\n  T accumulator = static_cast<T>(scan_op(initial_value, result[0]));\n  T block_accumulator = result[0];\n\n  if (mode == scan_mode::exclusive)\n  {\n    result[0] = initial_value;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      T tmp       = result[i];\n      result[i]   = accumulator;\n      accumulator = static_cast<T>(scan_op(accumulator, tmp));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, tmp));\n    }\n  }\n  else\n  {\n    result[0] = accumulator;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      accumulator = static_cast<T>(scan_op(accumulator, result[i]));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, result[i]));\n      result[i] = accumulator;\n    }\n  }\n\n  return block_accumulator;\n}\n\n// %PARAM% ALGO_TYPE alg 0:1:2\n// %PARAM% TEST_MODE mode 0:1\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing vec_types        = c2h::type_list<ulonglong4, uchar3, short2>;\nusing block_dim_x      = c2h::enum_type_list<int, 17, 32, 65, 96>;\nusing block_dim_yz     = c2h::enum_type_list<int, 1, 2>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 9>;\nusing algorithms       = c2h::enum_type_list<cub::BlockScanAlgorithm,\n                                       cub::BlockScanAlgorithm::BLOCK_SCAN_RAKING,\n                                       cub::BlockScanAlgorithm::BLOCK_SCAN_WARP_SCANS,\n                                       cub::BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE>;\nusing algorithm = c2h::enum_type_list<cub::BlockScanAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\n#if TEST_MODE == 0\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::inclusive>;\n#else\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive>;\n#endif\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int block_dim_x      = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int items_per_thread = c2h::get<3, TestType>::value;\n  static constexpr int tile_size = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr cub::BlockScanAlgorithm algorithm = c2h::get<4, TestType>::value;\n  static constexpr scan_mode mode                    = c2h::get<5, TestType>::value;\n};\n\nCUB_TEST(\"Block scan works with sum\",\n         \"[scan][block]\",\n         types,\n         block_dim_x,\n         block_dim_yz,\n         items_per_thread,\n         algorithm,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<params::algorithm,\n             params::items_per_thread,\n             params::block_dim_x,\n             params::block_dim_y,\n             params::block_dim_z>(d_in, d_out, sum_op_t<params::mode>{});\n\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Block scan works with vec types\", \"[scan][block]\", vec_types, algorithm, modes)\n{\n  constexpr int items_per_thread = 3;\n  constexpr int block_dim_x      = 256;\n  constexpr int block_dim_y      = 1;\n  constexpr int block_dim_z      = 1;\n  constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<1, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<2, TestType>::value;\n\n  using type = typename c2h::get<0, TestType>;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in,\n                                                                                 d_out,\n                                                                                 sum_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan works with custom types\", \"[scan][block]\", algorithm, modes)\n{\n  constexpr int items_per_thread = 3;\n  constexpr int block_dim_x      = 256;\n  constexpr int block_dim_y      = 1;\n  constexpr int block_dim_z      = 1;\n  constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in,\n                                                                                 d_out,\n                                                                                 sum_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan returns valid block aggregate\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  thrust::device_vector<type> d_block_aggregate(1);\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, mode>{target_thread_id,\n                                   thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n  type block_aggregate = host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(block_aggregate == d_block_aggregate[0]);\n}\n\nCUB_TEST(\"Block scan supports prefix op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    sum_prefix_op_t<type, mode>{prefix});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{}, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan supports custom scan op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n  d_in[0] = INT_MIN;\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, [](type l, type r) { return std::min(l, r); }, INT_MIN );\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan supports prefix op and custom scan op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_prefix_op_t<type, mode>{prefix});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, [] (type a, type b) { return std::min(a, b); }, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_scan.cuh>\n\n#include <thrust/host_vector.h>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_scan_kernel(T *in, T *out, ActionT action)\n{\n  using block_scan_t      = hipcub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t         = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncthreads();\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = thread_data[item];\n  }\n}\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_scan(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()),\n                        thrust::raw_pointer_cast(out.data()),\n                        action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <int ItemsPerThread, class BlockScanT, class T>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, int (&thread_data)[ItemsPerThread]) const\n  {\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, hipcub::Min{});\n    }\n    else \n    {\n      scan.InclusiveScan(thread_data, thread_data, hipcub::Min{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T *m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_prefix_op_t\n{\n  T m_prefix;\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : T{};\n      prefix   = prefix + block_aggregate;\n      return retval;\n    }\n  };\n\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, prefix_op);\n    }\n    else \n    {\n      scan.InclusiveSum(thread_data, thread_data, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_prefix_op_t\n{\n  T m_prefix;\n  static constexpr T min_identity = std::numeric_limits<T>::max();\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : min_identity;\n      prefix   = hipcub::Min{}(prefix, block_aggregate);\n      return retval;\n    }\n  };\n\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT &scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, hipcub::Min{}, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, hipcub::Min{}, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, class ScanOpT>\nT host_scan(scan_mode mode, thrust::host_vector<T> &result, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty()) \n  {\n    return {};\n  }\n\n  T accumulator = static_cast<T>(scan_op(initial_value, result[0]));\n  T block_accumulator = result[0];\n\n  if (mode == scan_mode::exclusive)\n  {\n    result[0] = initial_value;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      T tmp       = result[i];\n      result[i]   = accumulator;\n      accumulator = static_cast<T>(scan_op(accumulator, tmp));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, tmp));\n    }\n  }\n  else\n  {\n    result[0] = accumulator;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      accumulator = static_cast<T>(scan_op(accumulator, result[i]));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, result[i]));\n      result[i] = accumulator;\n    }\n  }\n\n  return block_accumulator;\n}\n\n// %PARAM% ALGO_TYPE alg 0:1:2\n// %PARAM% TEST_MODE mode 0:1\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing vec_types        = c2h::type_list<ulonglong4, uchar3, short2>;\nusing block_dim_x      = c2h::enum_type_list<int, 17, 32, 65, 96>;\nusing block_dim_yz     = c2h::enum_type_list<int, 1, 2>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 9>;\nusing algorithms       = c2h::enum_type_list<hipcub::BlockScanAlgorithm,\n                                       hipcub::BlockScanAlgorithm::BLOCK_SCAN_RAKING,\n                                       hipcub::BlockScanAlgorithm::BLOCK_SCAN_WARP_SCANS,\n                                       hipcub::BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE>;\nusing algorithm = c2h::enum_type_list<hipcub::BlockScanAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\n#if TEST_MODE == 0\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::inclusive>;\n#else\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive>;\n#endif\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int block_dim_x      = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int items_per_thread = c2h::get<3, TestType>::value;\n  static constexpr int tile_size = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<4, TestType>::value;\n  static constexpr scan_mode mode                    = c2h::get<5, TestType>::value;\n};\n\nCUB_TEST(\"Block scan works with sum\",\n         \"[scan][block]\",\n         types,\n         block_dim_x,\n         block_dim_yz,\n         items_per_thread,\n         algorithm,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  thrust::device_vector<type> d_out(params::tile_size);\n  thrust::device_vector<type> d_in(params::tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<params::algorithm,\n             params::items_per_thread,\n             params::block_dim_x,\n             params::block_dim_y,\n             params::block_dim_z>(d_in, d_out, sum_op_t<params::mode>{});\n\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nCUB_TEST(\"Block scan works with vec types\", \"[scan][block]\", vec_types, algorithm, modes)\n{\n  constexpr int items_per_thread = 3;\n  constexpr int block_dim_x      = 256;\n  constexpr int block_dim_y      = 1;\n  constexpr int block_dim_z      = 1;\n  constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<1, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<2, TestType>::value;\n\n  using type = typename c2h::get<0, TestType>;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in,\n                                                                                 d_out,\n                                                                                 sum_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan works with custom types\", \"[scan][block]\", algorithm, modes)\n{\n  constexpr int items_per_thread = 3;\n  constexpr int block_dim_x      = 256;\n  constexpr int block_dim_y      = 1;\n  constexpr int block_dim_z      = 1;\n  constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in,\n                                                                                 d_out,\n                                                                                 sum_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan returns valid block aggregate\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  thrust::device_vector<type> d_block_aggregate(1);\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, mode>{target_thread_id,\n                                   thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  thrust::host_vector<type> h_out = d_in;\n  type block_aggregate = host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(block_aggregate == d_block_aggregate[0]);\n}\n\nCUB_TEST(\"Block scan supports prefix op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    sum_prefix_op_t<type, mode>{prefix});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{}, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan supports custom scan op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n  d_in[0] = INT_MIN;\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_op_t<mode>{});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, [](type l, type r) { return std::min(l, r); }, INT_MIN );\n\n  REQUIRE(h_out == d_out);\n}\n\nCUB_TEST(\"Block scan supports prefix op and custom scan op\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  thrust::device_vector<type> d_out(tile_size);\n  thrust::device_vector<type> d_in(tile_size);\n  c2h::gen(CUB_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_prefix_op_t<type, mode>{prefix});\n\n  thrust::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, [] (type a, type b) { return std::min(a, b); }, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_scan.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"cub/util_type.cuh\"\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_rank.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/radix_rank_sort_operations.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <algorithm>\n#include <iostream>\n#include <memory>\n\n#include \"test_util.h\"\n#include <stdio.h>\n\nbool g_verbose = false;\ncub::CachingDeviceAllocator g_allocator(true);\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\n__launch_bounds__(BlockThreads, 1) __global__ void kernel(Key *d_keys, int *d_ranks)\n{\n  using block_radix_rank = cub::detail::\n    block_radix_rank_t<RankAlgorithm, BlockThreads, RadixBits, Descending, ScanAlgorithm>;\n\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ storage_t temp_storage;\n\n  // Items per thread\n  Key keys[ItemsPerThread];\n  int ranks[ItemsPerThread];\n\n  constexpr bool uses_warp_striped_arrangement =\n    RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH ||\n    RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ANY ||\n    RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR;\n\n  if (uses_warp_striped_arrangement)\n  {\n    cub::LoadDirectWarpStriped(threadIdx.x, d_keys, keys);\n  }\n  else\n  {\n    cub::LoadDirectBlocked(threadIdx.x, d_keys, keys);\n  }\n\n  cub::BFEDigitExtractor<Key> extractor(0, RadixBits);\n  block_radix_rank(temp_storage).RankKeys(keys, ranks, extractor);\n\n  if (uses_warp_striped_arrangement)\n  {\n    cub::StoreDirectWarpStriped(threadIdx.x, d_ranks, ranks);\n  }\n  else\n  {\n    cub::StoreDirectBlocked(threadIdx.x, d_ranks, ranks);\n  }\n}\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n/**\n * Simple key-value pairing\n */\ntemplate <typename Key>\nstruct pair_t\n{\n  Key key;\n  int value;\n\n  bool operator<(const pair_t &b) const { return (key < b.key); }\n};\n\ntemplate <bool DESCENDING, typename Key>\nvoid Initialize(GenMode gen_mode, Key *h_keys, int *h_reference_ranks, int num_items, int num_bits)\n{\n  std::unique_ptr<pair_t<Key>[]> h_pairs_storage(new pair_t<Key>[num_items]);\n  pair_t<Key> *h_pairs = h_pairs_storage.get();\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    InitValue(gen_mode, h_keys[i], i);\n\n    // Mask off unwanted portions\n    std::uint64_t base = 0;\n    memcpy(&base, &h_keys[i], sizeof(Key));\n    base &= (1ull << num_bits) - 1;\n    memcpy(&h_keys[i], &base, sizeof(Key));\n\n    h_pairs[i].key   = h_keys[i];\n    h_pairs[i].value = i;\n  }\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  std::stable_sort(h_pairs, h_pairs + num_items);\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_reference_ranks[h_pairs[i].value] = i;\n  }\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestDriver(GenMode gen_mode)\n{\n  constexpr int tile_size = BlockThreads * ItemsPerThread;\n\n  // Allocate host arrays\n  std::unique_ptr<Key[]> h_keys(new Key[tile_size]);\n  std::unique_ptr<int[]> h_ranks(new int[tile_size]);\n  std::unique_ptr<int[]> h_reference_ranks(new int[tile_size]);\n\n  // Allocate device arrays\n  Key *d_keys  = nullptr;\n  int *d_ranks = nullptr;\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_keys, sizeof(Key) * tile_size));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_ranks, sizeof(int) * tile_size));\n\n  // Initialize problem and solution on host\n  Initialize<Descending>(gen_mode, h_keys.get(), h_reference_ranks.get(), tile_size, RadixBits);\n\n  // Copy problem to device\n  CubDebugExit(cudaMemcpy(d_keys, h_keys.get(), sizeof(Key) * tile_size, cudaMemcpyHostToDevice));\n\n  // Run kernel\n  kernel<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>\n    <<<1, BlockThreads>>>(d_keys, d_ranks);\n\n  // Flush kernel output / errors\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Check keys results\n  const bool compare =\n    CompareDeviceResults(h_reference_ranks.get(), d_ranks, tile_size, g_verbose, g_verbose);\n  AssertEquals(0, compare);\n\n  if (d_keys)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_keys));\n  }\n\n  if (d_ranks)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_ranks));\n  }\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cub::Int2Type<true> /*fits_smem_capacity*/)\n{\n  TestDriver<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    UNIFORM);\n\n  TestDriver<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    INTEGER_SEED);\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cub::Int2Type<false> fits_smem_capacity)\n{}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          bool Descending,\n          typename Key>\nvoid Test()\n{\n  // Check size of smem storage for the target arch to make sure it will fit\n  using block_radix_rank = cub::detail::\n    block_radix_rank_t<RankAlgorithm, BlockThreads, RadixBits, Descending, ScanAlgorithm>;\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  cub::Int2Type<(sizeof(storage_t) <= 48 * 1024)> fits_smem_capacity;\n\n  TestValid<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    fits_smem_capacity);\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          typename Key>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, true, Key>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, false, Key>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint8_t>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint16_t>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int BlockThreads, int ItemsPerThread, int RadixBits>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, cub::BLOCK_SCAN_RAKING>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, cub::BLOCK_SCAN_WARP_SCANS>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int BlockThreads, int ItemsPerThread>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, 1>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, 5>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int BlockThreads>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, 1>();\n  Test<RankAlgorithm, BlockThreads, 4>();\n}\n\ntemplate <int BlockThreads>\nvoid Test(cub::Int2Type<true> /* multiple of hw warp */)\n{\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_MATCH, BlockThreads>();\n\n  // TODO(senior-zero):\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR\n}\n\ntemplate <int BlockThreads>\nvoid Test(cub::Int2Type<false> /* multiple of hw warp */)\n{}\n\ntemplate <int BlockThreads>\nvoid Test()\n{\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_BASIC, BlockThreads>();\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_MEMOIZE, BlockThreads>();\n\n  Test<BlockThreads>(cub::Int2Type<(BlockThreads % 32) == 0>{});\n}\n\nint main(int argc, char **argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  Test<16>();\n  Test<32>();\n  Test<128>();\n  Test<130>();\n\n  return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#include \"cub/util_type.cuh\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_rank.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/radix_rank_sort_operations.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <algorithm>\n#include <iostream>\n#include <memory>\n\n#include \"test_util.h\"\n#include <stdio.h>\n\nbool g_verbose = false;\nhipcub::CachingDeviceAllocator g_allocator(true);\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\n__launch_bounds__(BlockThreads, 1) __global__ void kernel(Key *d_keys, int *d_ranks)\n{\n  using block_radix_rank = hipcub::detail::\n    block_radix_rank_t<RankAlgorithm, BlockThreads, RadixBits, Descending, ScanAlgorithm>;\n\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ storage_t temp_storage;\n\n  // Items per thread\n  Key keys[ItemsPerThread];\n  int ranks[ItemsPerThread];\n\n  constexpr bool uses_warp_striped_arrangement =\n    RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH ||\n    RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ANY ||\n    RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR;\n\n  if (uses_warp_striped_arrangement)\n  {\n    hipcub::LoadDirectWarpStriped(threadIdx.x, d_keys, keys);\n  }\n  else\n  {\n    hipcub::LoadDirectBlocked(threadIdx.x, d_keys, keys);\n  }\n\n  hipcub::BFEDigitExtractor<Key> extractor(0, RadixBits);\n  block_radix_rank(temp_storage).RankKeys(keys, ranks, extractor);\n\n  if (uses_warp_striped_arrangement)\n  {\n    hipcub::StoreDirectWarpStriped(threadIdx.x, d_ranks, ranks);\n  }\n  else\n  {\n    hipcub::StoreDirectBlocked(threadIdx.x, d_ranks, ranks);\n  }\n}\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n/**\n * Simple key-value pairing\n */\ntemplate <typename Key>\nstruct pair_t\n{\n  Key key;\n  int value;\n\n  bool operator<(const pair_t &b) const { return (key < b.key); }\n};\n\ntemplate <bool DESCENDING, typename Key>\nvoid Initialize(GenMode gen_mode, Key *h_keys, int *h_reference_ranks, int num_items, int num_bits)\n{\n  std::unique_ptr<pair_t<Key>[]> h_pairs_storage(new pair_t<Key>[num_items]);\n  pair_t<Key> *h_pairs = h_pairs_storage.get();\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    InitValue(gen_mode, h_keys[i], i);\n\n    // Mask off unwanted portions\n    std::uint64_t base = 0;\n    memcpy(&base, &h_keys[i], sizeof(Key));\n    base &= (1ull << num_bits) - 1;\n    memcpy(&h_keys[i], &base, sizeof(Key));\n\n    h_pairs[i].key   = h_keys[i];\n    h_pairs[i].value = i;\n  }\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  std::stable_sort(h_pairs, h_pairs + num_items);\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_reference_ranks[h_pairs[i].value] = i;\n  }\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestDriver(GenMode gen_mode)\n{\n  constexpr int tile_size = BlockThreads * ItemsPerThread;\n\n  // Allocate host arrays\n  std::unique_ptr<Key[]> h_keys(new Key[tile_size]);\n  std::unique_ptr<int[]> h_ranks(new int[tile_size]);\n  std::unique_ptr<int[]> h_reference_ranks(new int[tile_size]);\n\n  // Allocate device arrays\n  Key *d_keys  = nullptr;\n  int *d_ranks = nullptr;\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_keys, sizeof(Key) * tile_size));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_ranks, sizeof(int) * tile_size));\n\n  // Initialize problem and solution on host\n  Initialize<Descending>(gen_mode, h_keys.get(), h_reference_ranks.get(), tile_size, RadixBits);\n\n  // Copy problem to device\n  CubDebugExit(hipMemcpy(d_keys, h_keys.get(), sizeof(Key) * tile_size, hipMemcpyHostToDevice));\n\n  // Run kernel\n  kernel<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>\n    <<<1, BlockThreads>>>(d_keys, d_ranks);\n\n  // Flush kernel output / errors\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Check keys results\n  const bool compare =\n    CompareDeviceResults(h_reference_ranks.get(), d_ranks, tile_size, g_verbose, g_verbose);\n  AssertEquals(0, compare);\n\n  if (d_keys)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_keys));\n  }\n\n  if (d_ranks)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_ranks));\n  }\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(hipcub::Int2Type<true> /*fits_smem_capacity*/)\n{\n  TestDriver<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    UNIFORM);\n\n  TestDriver<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    INTEGER_SEED);\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(hipcub::Int2Type<false> fits_smem_capacity)\n{}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          bool Descending,\n          typename Key>\nvoid Test()\n{\n  // Check size of smem storage for the target arch to make sure it will fit\n  using block_radix_rank = hipcub::detail::\n    block_radix_rank_t<RankAlgorithm, BlockThreads, RadixBits, Descending, ScanAlgorithm>;\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  hipcub::Int2Type<(sizeof(storage_t) <= 48 * 1024)> fits_smem_capacity;\n\n  TestValid<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    fits_smem_capacity);\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          typename Key>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, true, Key>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, false, Key>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int BlockThreads,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint8_t>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint16_t>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int BlockThreads, int ItemsPerThread, int RadixBits>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, hipcub::BLOCK_SCAN_RAKING>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, RadixBits, hipcub::BLOCK_SCAN_WARP_SCANS>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int BlockThreads, int ItemsPerThread>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, 1>();\n  Test<RankAlgorithm, BlockThreads, ItemsPerThread, 5>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int BlockThreads>\nvoid Test()\n{\n  Test<RankAlgorithm, BlockThreads, 1>();\n  Test<RankAlgorithm, BlockThreads, 4>();\n}\n\ntemplate <int BlockThreads>\nvoid Test(hipcub::Int2Type<true> /* multiple of hw warp */)\n{\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH, BlockThreads>();\n\n  // TODO(senior-zero):\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR\n}\n\ntemplate <int BlockThreads>\nvoid Test(hipcub::Int2Type<false> /* multiple of hw warp */)\n{}\n\ntemplate <int BlockThreads>\nvoid Test()\n{\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_BASIC, BlockThreads>();\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_MEMOIZE, BlockThreads>();\n\n  Test<BlockThreads>(hipcub::Int2Type<(BlockThreads % 32) == 0>{});\n}\n\nint main(int argc, char **argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  Test<16>();\n  Test<32>();\n  Test<128>();\n  Test<130>();\n\n  return 0;\n}\n\n", "source": "/kaggle/working/cub/test/test_block_radix_rank.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <thrust/count.h>\n\n#include <cuda/std/tuple>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_cdp_helper.h\"\n#include \"catch2_test_helper.h\"\n\n// %PARAM% TEST_CDP cdp 0:1\n\ntemplate <class T>\n__global__ void cub_api_example_x2_0_kernel(const T *d_in, T *d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] * T{2};\n  }\n}\n\ntemplate <class T>\n__global__ void cub_api_example_x0_5_kernel(const T *d_in, T *d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] / T{2};\n  }\n}\n\nstruct cub_api_example_t\n{\n  static constexpr int threads_in_block = 256;\n\n  template <class T, class KernelT>\n  CUB_RUNTIME_FUNCTION static cudaError_t invoke(std::uint8_t *d_temp_storage,\n                                                 std::size_t &temp_storage_bytes,\n                                                 KernelT kernel,\n                                                 const T *d_in,\n                                                 T *d_out,\n                                                 int num_items,\n                                                 bool should_be_invoked_on_device)\n  {\n    NV_IF_TARGET(NV_IS_HOST,\n                 (if (should_be_invoked_on_device) { return cudaErrorLaunchFailure; }),\n                 (if (!should_be_invoked_on_device) { return cudaErrorLaunchFailure; }));\n\n    if (d_temp_storage == nullptr)\n    {\n      temp_storage_bytes = static_cast<std::size_t>(num_items);\n      return cudaSuccess;\n    }\n\n    if (temp_storage_bytes != static_cast<std::size_t>(num_items))\n    {\n      return cudaErrorInvalidValue;\n    }\n\n    const int blocks_in_grid = (num_items + threads_in_block - 1) / threads_in_block;\n\n    return thrust::cuda_cub::launcher::triple_chevron(blocks_in_grid, threads_in_block, 0, 0)\n      .doit(kernel, d_in, d_out, num_items);\n  }\n\n  template <class T>\n  CUB_RUNTIME_FUNCTION static cudaError_t x2_0(std::uint8_t *d_temp_storage,\n                                               std::size_t &temp_storage_bytes,\n                                               const T *d_in,\n                                               T *d_out,\n                                               int num_items,\n                                               bool should_be_invoked_on_device)\n  {\n    return invoke(d_temp_storage,\n                  temp_storage_bytes,\n                  cub_api_example_x2_0_kernel<T>,\n                  d_in,\n                  d_out,\n                  num_items,\n                  should_be_invoked_on_device);\n  }\n\n  template <class T>\n  CUB_RUNTIME_FUNCTION static cudaError_t x0_5(std::uint8_t *d_temp_storage,\n                                               std::size_t &temp_storage_bytes,\n                                               const T *d_in,\n                                               T *d_out,\n                                               int num_items,\n                                               bool should_be_invoked_on_device)\n  {\n    return invoke(d_temp_storage,\n                  temp_storage_bytes,\n                  cub_api_example_x0_5_kernel<T>,\n                  d_in,\n                  d_out,\n                  num_items,\n                  should_be_invoked_on_device);\n  }\n};\n\nDECLARE_CDP_WRAPPER(cub_api_example_t::x2_0, x2_0);\nDECLARE_CDP_WRAPPER(cub_api_example_t::x0_5, x0_5);\n\nCUB_TEST(\"CDP wrapper works with predefined invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  thrust::device_vector<int> in(n, 21);\n  thrust::device_vector<int> out(n);\n\n  int *d_in  = thrust::raw_pointer_cast(in.data());\n  int *d_out = thrust::raw_pointer_cast(out.data());\n\n  constexpr bool should_be_invoked_on_device = TEST_CDP;\n\n  {\n    x2_0(d_in, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    x0_5(d_out, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n\nstruct custom_x2_0_invocable\n{\n  template <class T>\n  CUB_RUNTIME_FUNCTION cudaError_t operator()(std::uint8_t *d_temp_storage,\n                                              std::size_t &temp_storage_bytes,\n                                              const T *d_in,\n                                              T *d_out,\n                                              int num_items,\n                                              bool should_be_invoked_on_device)\n  {\n    return cub_api_example_t::x2_0(d_temp_storage,\n                                   temp_storage_bytes,\n                                   d_in,\n                                   d_out,\n                                   num_items,\n                                   should_be_invoked_on_device);\n  }\n};\n\nstruct custom_x0_5_invocable\n{\n  template <class T>\n  CUB_RUNTIME_FUNCTION cudaError_t operator()(std::uint8_t *d_temp_storage,\n                                              std::size_t &temp_storage_bytes,\n                                              const T *d_in,\n                                              T *d_out,\n                                              int num_items,\n                                              bool should_be_invoked_on_device)\n  {\n    return cub_api_example_t::x0_5(d_temp_storage,\n                                   temp_storage_bytes,\n                                   d_in,\n                                   d_out,\n                                   num_items,\n                                   should_be_invoked_on_device);\n  }\n};\n\nCUB_TEST(\"CDP wrapper works with custom invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  thrust::device_vector<int> in(n, 21);\n  thrust::device_vector<int> out(n);\n\n  int *d_in  = thrust::raw_pointer_cast(in.data());\n  int *d_out = thrust::raw_pointer_cast(out.data());\n\n  constexpr bool should_be_invoked_on_device = TEST_CDP;\n\n  {\n    cdp_launch(custom_x2_0_invocable{}, d_in, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    cdp_launch(custom_x0_5_invocable{}, d_out, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n\n#include <cuda/std/tuple>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_cdp_helper.h\"\n#include \"catch2_test_helper.h\"\n\n// %PARAM% TEST_CDP cdp 0:1\n\ntemplate <class T>\n__global__ void cub_api_example_x2_0_kernel(const T *d_in, T *d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] * T{2};\n  }\n}\n\ntemplate <class T>\n__global__ void cub_api_example_x0_5_kernel(const T *d_in, T *d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] / T{2};\n  }\n}\n\nstruct cub_api_example_t\n{\n  static constexpr int threads_in_block = 256;\n\n  template <class T, class KernelT>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t invoke(std::uint8_t *d_temp_storage,\n                                                 std::size_t &temp_storage_bytes,\n                                                 KernelT kernel,\n                                                 const T *d_in,\n                                                 T *d_out,\n                                                 int num_items,\n                                                 bool should_be_invoked_on_device)\n  {\n    NV_IF_TARGET(NV_IS_HOST,\n                 (if (should_be_invoked_on_device) { return hipErrorLaunchFailure; }),\n                 (if (!should_be_invoked_on_device) { return hipErrorLaunchFailure; }));\n\n    if (d_temp_storage == nullptr)\n    {\n      temp_storage_bytes = static_cast<std::size_t>(num_items);\n      return hipSuccess;\n    }\n\n    if (temp_storage_bytes != static_cast<std::size_t>(num_items))\n    {\n      return hipErrorInvalidValue;\n    }\n\n    const int blocks_in_grid = (num_items + threads_in_block - 1) / threads_in_block;\n\n    return thrust::cuda_cub::launcher::triple_chevron(blocks_in_grid, threads_in_block, 0, 0)\n      .doit(kernel, d_in, d_out, num_items);\n  }\n\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t x2_0(std::uint8_t *d_temp_storage,\n                                               std::size_t &temp_storage_bytes,\n                                               const T *d_in,\n                                               T *d_out,\n                                               int num_items,\n                                               bool should_be_invoked_on_device)\n  {\n    return invoke(d_temp_storage,\n                  temp_storage_bytes,\n                  cub_api_example_x2_0_kernel<T>,\n                  d_in,\n                  d_out,\n                  num_items,\n                  should_be_invoked_on_device);\n  }\n\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t x0_5(std::uint8_t *d_temp_storage,\n                                               std::size_t &temp_storage_bytes,\n                                               const T *d_in,\n                                               T *d_out,\n                                               int num_items,\n                                               bool should_be_invoked_on_device)\n  {\n    return invoke(d_temp_storage,\n                  temp_storage_bytes,\n                  cub_api_example_x0_5_kernel<T>,\n                  d_in,\n                  d_out,\n                  num_items,\n                  should_be_invoked_on_device);\n  }\n};\n\nDECLARE_CDP_WRAPPER(cub_api_example_t::x2_0, x2_0);\nDECLARE_CDP_WRAPPER(cub_api_example_t::x0_5, x0_5);\n\nCUB_TEST(\"CDP wrapper works with predefined invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  thrust::device_vector<int> in(n, 21);\n  thrust::device_vector<int> out(n);\n\n  int *d_in  = thrust::raw_pointer_cast(in.data());\n  int *d_out = thrust::raw_pointer_cast(out.data());\n\n  constexpr bool should_be_invoked_on_device = TEST_CDP;\n\n  {\n    x2_0(d_in, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    x0_5(d_out, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n\nstruct custom_x2_0_invocable\n{\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION hipError_t operator()(std::uint8_t *d_temp_storage,\n                                              std::size_t &temp_storage_bytes,\n                                              const T *d_in,\n                                              T *d_out,\n                                              int num_items,\n                                              bool should_be_invoked_on_device)\n  {\n    return cub_api_example_t::x2_0(d_temp_storage,\n                                   temp_storage_bytes,\n                                   d_in,\n                                   d_out,\n                                   num_items,\n                                   should_be_invoked_on_device);\n  }\n};\n\nstruct custom_x0_5_invocable\n{\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION hipError_t operator()(std::uint8_t *d_temp_storage,\n                                              std::size_t &temp_storage_bytes,\n                                              const T *d_in,\n                                              T *d_out,\n                                              int num_items,\n                                              bool should_be_invoked_on_device)\n  {\n    return cub_api_example_t::x0_5(d_temp_storage,\n                                   temp_storage_bytes,\n                                   d_in,\n                                   d_out,\n                                   num_items,\n                                   should_be_invoked_on_device);\n  }\n};\n\nCUB_TEST(\"CDP wrapper works with custom invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  thrust::device_vector<int> in(n, 21);\n  thrust::device_vector<int> out(n);\n\n  int *d_in  = thrust::raw_pointer_cast(in.data());\n  int *d_out = thrust::raw_pointer_cast(out.data());\n\n  constexpr bool should_be_invoked_on_device = TEST_CDP;\n\n  {\n    cdp_launch(custom_x2_0_invocable{}, d_in, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    cdp_launch(custom_x0_5_invocable{}, d_out, d_out, n, should_be_invoked_on_device);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n", "source": "/kaggle/working/cub/test/catch2_test_cdp_wrapper.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"c2h/custom_type.cuh\"\n#include \"catch2_test_helper.h\"\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\n__global__ void warp_reduce_kernel(T *in, T *out, ActionT action)\n{\n  using warp_reduce_t = cub::WarpReduce<T, LOGICAL_WARP_THREADS>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = threadIdx.x;\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Load data\n  T thread_data = in[tid];\n  // Instantiate and run warp reduction\n  warp_reduce_t warp_reduce(storage[warp_id]);\n  auto result = action(tid, warp_reduce, thread_data);\n\n  // Write warp aggregate\n  out[tid] = result;\n}\n\n/**\n * @brief Delegate wrapper for WarpReduce::Sum\n */\ntemplate <typename T>\nstruct warp_sum_t\n{\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    auto result = warp_reduce.Sum(thread_data);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpReduce::Sum\n */\ntemplate <typename T>\nstruct warp_sum_partial_t\n{\n  int num_valid;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ __forceinline__ T operator()(int linear_tid,\n                                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                                          T &thread_data) const\n  {\n    auto result = warp_reduce.Sum(thread_data, num_valid);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::Reduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_reduce_t\n{\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ __forceinline__ T operator()(int linear_tid,\n                                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                                          T &thread_data) const\n  {\n    auto result = warp_reduce.Reduce(thread_data, reduction_op);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpReduce::Reduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_reduce_partial_t\n{\n  int num_valid;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    auto result = warp_reduce.Reduce(thread_data, reduction_op, num_valid);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_tail_t\n{\n  uint8_t *d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) ||\n                         ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result = warp_reduce.TailSegmentedSum(thread_data, d_flags[linear_tid]);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_head_t\n{\n  uint8_t *d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedSum(thread_data, d_flags[linear_tid]);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_tail_t\n{\n  uint8_t *d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) ||\n                         ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result = warp_reduce.TailSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_head_t\n{\n  uint8_t *d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          cub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result = warp_reduce.HeadSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Dispatch helper function\n */\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\nvoid warp_reduce(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  warp_reduce_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T,\n          typename ::cuda::std::enable_if<::cuda::std::is_floating_point<T>::value, int>::type = 0>\nvoid verify_results(const thrust::host_vector<T> &expected_data,\n                    const thrust::device_vector<T> &test_results)\n{\n  REQUIRE_APPROX_EQ(expected_data, test_results);\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T,\n          typename ::cuda::std::enable_if<!::cuda::std::is_floating_point<T>::value, int>::type = 0>\nvoid verify_results(const thrust::host_vector<T> &expected_data,\n                    const thrust::device_vector<T> &test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\nenum class reduce_mode\n{\n  all,\n  partial,\n  head_flags,\n  tail_flags,\n};\n\ntemplate <typename InputItT, typename FlagInputItT, typename ReductionOp, typename ResultOutItT>\nvoid compute_host_reference(reduce_mode mode,\n                            InputItT h_in,\n                            FlagInputItT h_flags,\n                            int warps,\n                            int logical_warp_threads,\n                            int valid_warp_threads,\n                            ReductionOp reduction_op,\n                            ResultOutItT h_data_out)\n{\n  // Accumulate segments (lane 0 of each warp is implicitly a segment head)\n  for (int warp = 0; warp < warps; ++warp)\n  {\n    int warp_offset = warp * logical_warp_threads;\n    int item_offset = warp_offset + valid_warp_threads - 1;\n\n    // Last item in warp\n    auto head_aggregate = h_in[item_offset];\n    auto tail_aggregate = h_in[item_offset];\n\n    if (mode != reduce_mode::tail_flags && h_flags[item_offset])\n    {\n      h_data_out[item_offset] = head_aggregate;\n    }\n    item_offset--;\n\n    // Work backwards\n    while (item_offset >= warp_offset)\n    {\n      if (h_flags[item_offset + 1])\n      {\n        head_aggregate = h_in[item_offset];\n      }\n      else\n      {\n        head_aggregate = reduction_op(head_aggregate, h_in[item_offset]);\n      }\n\n      if (h_flags[item_offset])\n      {\n        if (mode == reduce_mode::head_flags)\n        {\n          h_data_out[item_offset] = head_aggregate;\n        }\n        else if (mode == reduce_mode::tail_flags)\n        {\n          h_data_out[item_offset + 1] = tail_aggregate;\n          tail_aggregate              = h_in[item_offset];\n        }\n      }\n      else\n      {\n        tail_aggregate = reduction_op(tail_aggregate, h_in[item_offset]);\n      }\n\n      item_offset--;\n    }\n\n    // Record last segment aggregate\n    if (mode == reduce_mode::tail_flags)\n    {\n      h_data_out[warp_offset] = tail_aggregate;\n    }\n    else\n    {\n      h_data_out[warp_offset] = head_aggregate;\n    }\n  }\n}\n\n// List of types to test\nusing custom_t       = c2h::custom_type_t<c2h::accumulateable_t,\n                                    c2h::equal_comparable_t,\n                                    c2h::lexicographical_less_comparable_t>;\nusing full_type_list = c2h::type_list<std::uint8_t,\n                                      std::uint16_t,\n                                      std::int32_t,\n                                      std::int64_t,\n                                      custom_t,\n                                      ulonglong4,\n                                      uchar3,\n                                      short2>;\n\nusing builtin_type_list = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 7, 1>;\n\nusing segmented_modes =\n  c2h::enum_type_list<reduce_mode, reduce_mode::head_flags, reduce_mode::tail_flags>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nCUB_TEST(\"Warp sum works\", \"[reduce][warp]\", full_type_list, logical_warp_threads)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in, d_out, warp_sum_t<type>{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp reduce works\", \"[reduce][warp]\", builtin_type_list, logical_warp_threads)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = cub::Min;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in,\n                                                                 d_out,\n                                                                 warp_reduce_t<type, red_op_t>{\n                                                                   red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp sum on partial warp works\", \"[reduce][warp]\", full_type_list, logical_warp_threads)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const int valid_items = GENERATE_COPY(take(2, random(1, params::logical_warp_threads)));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in,\n                                                                 d_out,\n                                                                 warp_sum_partial_t<type>{\n                                                                   valid_items});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp reduce on partial warp works\",\n         \"[reduce][warp]\",\n         builtin_type_list,\n         logical_warp_threads)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = cub::Min;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const int valid_items = GENERATE_COPY(take(2, random(1, params::logical_warp_threads)));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_reduce_partial_t<type, red_op_t>{valid_items, red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp segmented sum works\",\n         \"[reduce][warp]\",\n         full_type_list,\n         logical_warp_threads,\n         segmented_modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags ||\n                  segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_sum_t = cub::detail::conditional_t<(segmented_mod == reduce_mode::tail_flags),\n                                                    warp_seg_sum_tail_t<type>,\n                                                    warp_seg_sum_head_t<type>>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<uint8_t> d_flags(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min  = 0;\n  constexpr uint8_t max  = 2;\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_seg_sum_t{thrust::raw_pointer_cast(d_flags.data())});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in       = d_in;\n  thrust::host_vector<uint8_t> h_flags = d_flags;\n  thrust::host_vector<type> h_out      = h_in;\n  compute_host_reference(segmented_mod,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp segmented reduction works\",\n         \"[reduce][warp]\",\n         builtin_type_list,\n         logical_warp_threads,\n         segmented_modes)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = cub::Min;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags ||\n                  segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_reduction_t =\n    cub::detail::conditional_t<(segmented_mod == reduce_mode::tail_flags),\n                               warp_seg_reduce_tail_t<type, red_op_t>,\n                               warp_seg_reduce_head_t<type, red_op_t>>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<uint8_t> d_flags(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min  = 0;\n  constexpr uint8_t max  = 2;\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_seg_reduction_t{thrust::raw_pointer_cast(d_flags.data()), red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in       = d_in;\n  thrust::host_vector<uint8_t> h_flags = d_flags;\n  thrust::host_vector<type> h_out      = h_in;\n  compute_host_reference(segmented_mod,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/constant_iterator.h>\n\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"c2h/custom_type.cuh\"\n#include \"catch2_test_helper.h\"\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\n__global__ void warp_reduce_kernel(T *in, T *out, ActionT action)\n{\n  using warp_reduce_t = hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = threadIdx.x;\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Load data\n  T thread_data = in[tid];\n  // Instantiate and run warp reduction\n  warp_reduce_t warp_reduce(storage[warp_id]);\n  auto result = action(tid, warp_reduce, thread_data);\n\n  // Write warp aggregate\n  out[tid] = result;\n}\n\n/**\n * @brief Delegate wrapper for WarpReduce::Sum\n */\ntemplate <typename T>\nstruct warp_sum_t\n{\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    auto result = warp_reduce.Sum(thread_data);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpReduce::Sum\n */\ntemplate <typename T>\nstruct warp_sum_partial_t\n{\n  int num_valid;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ __forceinline__ T operator()(int linear_tid,\n                                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                                          T &thread_data) const\n  {\n    auto result = warp_reduce.Sum(thread_data, num_valid);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::Reduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_reduce_t\n{\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ __forceinline__ T operator()(int linear_tid,\n                                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                                          T &thread_data) const\n  {\n    auto result = warp_reduce.Reduce(thread_data, reduction_op);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpReduce::Reduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_reduce_partial_t\n{\n  int num_valid;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    auto result = warp_reduce.Reduce(thread_data, reduction_op, num_valid);\n    return ((linear_tid % LOGICAL_WARP_THREADS) == 0) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_tail_t\n{\n  uint8_t *d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) ||\n                         ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result = warp_reduce.TailSegmentedSum(thread_data, d_flags[linear_tid]);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_head_t\n{\n  uint8_t *d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedSum(thread_data, d_flags[linear_tid]);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_tail_t\n{\n  uint8_t *d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) ||\n                         ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result = warp_reduce.TailSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_head_t\n{\n  uint8_t *d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid,\n                          hipcub::WarpReduce<T, LOGICAL_WARP_THREADS> &warp_reduce,\n                          T &thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result = warp_reduce.HeadSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Dispatch helper function\n */\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\nvoid warp_reduce(thrust::device_vector<T> &in, thrust::device_vector<T> &out, ActionT action)\n{\n  warp_reduce_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T,\n          typename ::cuda::std::enable_if<::cuda::std::is_floating_point<T>::value, int>::type = 0>\nvoid verify_results(const thrust::host_vector<T> &expected_data,\n                    const thrust::device_vector<T> &test_results)\n{\n  REQUIRE_APPROX_EQ(expected_data, test_results);\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T,\n          typename ::cuda::std::enable_if<!::cuda::std::is_floating_point<T>::value, int>::type = 0>\nvoid verify_results(const thrust::host_vector<T> &expected_data,\n                    const thrust::device_vector<T> &test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\nenum class reduce_mode\n{\n  all,\n  partial,\n  head_flags,\n  tail_flags,\n};\n\ntemplate <typename InputItT, typename FlagInputItT, typename ReductionOp, typename ResultOutItT>\nvoid compute_host_reference(reduce_mode mode,\n                            InputItT h_in,\n                            FlagInputItT h_flags,\n                            int warps,\n                            int logical_warp_threads,\n                            int valid_warp_threads,\n                            ReductionOp reduction_op,\n                            ResultOutItT h_data_out)\n{\n  // Accumulate segments (lane 0 of each warp is implicitly a segment head)\n  for (int warp = 0; warp < warps; ++warp)\n  {\n    int warp_offset = warp * logical_warp_threads;\n    int item_offset = warp_offset + valid_warp_threads - 1;\n\n    // Last item in warp\n    auto head_aggregate = h_in[item_offset];\n    auto tail_aggregate = h_in[item_offset];\n\n    if (mode != reduce_mode::tail_flags && h_flags[item_offset])\n    {\n      h_data_out[item_offset] = head_aggregate;\n    }\n    item_offset--;\n\n    // Work backwards\n    while (item_offset >= warp_offset)\n    {\n      if (h_flags[item_offset + 1])\n      {\n        head_aggregate = h_in[item_offset];\n      }\n      else\n      {\n        head_aggregate = reduction_op(head_aggregate, h_in[item_offset]);\n      }\n\n      if (h_flags[item_offset])\n      {\n        if (mode == reduce_mode::head_flags)\n        {\n          h_data_out[item_offset] = head_aggregate;\n        }\n        else if (mode == reduce_mode::tail_flags)\n        {\n          h_data_out[item_offset + 1] = tail_aggregate;\n          tail_aggregate              = h_in[item_offset];\n        }\n      }\n      else\n      {\n        tail_aggregate = reduction_op(tail_aggregate, h_in[item_offset]);\n      }\n\n      item_offset--;\n    }\n\n    // Record last segment aggregate\n    if (mode == reduce_mode::tail_flags)\n    {\n      h_data_out[warp_offset] = tail_aggregate;\n    }\n    else\n    {\n      h_data_out[warp_offset] = head_aggregate;\n    }\n  }\n}\n\n// List of types to test\nusing custom_t       = c2h::custom_type_t<c2h::accumulateable_t,\n                                    c2h::equal_comparable_t,\n                                    c2h::lexicographical_less_comparable_t>;\nusing full_type_list = c2h::type_list<std::uint8_t,\n                                      std::uint16_t,\n                                      std::int32_t,\n                                      std::int64_t,\n                                      custom_t,\n                                      ulonglong4,\n                                      uchar3,\n                                      short2>;\n\nusing builtin_type_list = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 7, 1>;\n\nusing segmented_modes =\n  c2h::enum_type_list<reduce_mode, reduce_mode::head_flags, reduce_mode::tail_flags>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nCUB_TEST(\"Warp sum works\", \"[reduce][warp]\", full_type_list, logical_warp_threads)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in, d_out, warp_sum_t<type>{});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp reduce works\", \"[reduce][warp]\", builtin_type_list, logical_warp_threads)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = hipcub::Min;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in,\n                                                                 d_out,\n                                                                 warp_reduce_t<type, red_op_t>{\n                                                                   red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp sum on partial warp works\", \"[reduce][warp]\", full_type_list, logical_warp_threads)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const int valid_items = GENERATE_COPY(take(2, random(1, params::logical_warp_threads)));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(d_in,\n                                                                 d_out,\n                                                                 warp_sum_partial_t<type>{\n                                                                   valid_items});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp reduce on partial warp works\",\n         \"[reduce][warp]\",\n         builtin_type_list,\n         logical_warp_threads)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = hipcub::Min;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const int valid_items = GENERATE_COPY(take(2, random(1, params::logical_warp_threads)));\n  c2h::gen(CUB_SEED(10), d_in);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_reduce_partial_t<type, red_op_t>{valid_items, red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in  = d_in;\n  thrust::host_vector<type> h_out = h_in;\n  auto h_flags                    = thrust::make_constant_iterator(false);\n  compute_host_reference(reduce_mode::all,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp segmented sum works\",\n         \"[reduce][warp]\",\n         full_type_list,\n         logical_warp_threads,\n         segmented_modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags ||\n                  segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_sum_t = hipcub::detail::conditional_t<(segmented_mod == reduce_mode::tail_flags),\n                                                    warp_seg_sum_tail_t<type>,\n                                                    warp_seg_sum_head_t<type>>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<uint8_t> d_flags(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min  = 0;\n  constexpr uint8_t max  = 2;\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_seg_sum_t{thrust::raw_pointer_cast(d_flags.data())});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in       = d_in;\n  thrust::host_vector<uint8_t> h_flags = d_flags;\n  thrust::host_vector<type> h_out      = h_in;\n  compute_host_reference(segmented_mod,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         ::cuda::std::plus<type>{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nCUB_TEST(\"Warp segmented reduction works\",\n         \"[reduce][warp]\",\n         builtin_type_list,\n         logical_warp_threads,\n         segmented_modes)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = hipcub::Min;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags ||\n                  segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_reduction_t =\n    hipcub::detail::conditional_t<(segmented_mod == reduce_mode::tail_flags),\n                               warp_seg_reduce_tail_t<type, red_op_t>,\n                               warp_seg_reduce_head_t<type, red_op_t>>;\n\n  // Prepare test data\n  thrust::device_vector<type> d_in(params::tile_size);\n  thrust::device_vector<uint8_t> d_flags(params::tile_size);\n  thrust::device_vector<type> d_out(params::tile_size);\n  const auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min  = 0;\n  constexpr uint8_t max  = 2;\n  c2h::gen(CUB_SEED(5), d_in);\n  c2h::gen(CUB_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    warp_seg_reduction_t{thrust::raw_pointer_cast(d_flags.data()), red_op_t{}});\n\n  // Prepare verification data\n  thrust::host_vector<type> h_in       = d_in;\n  thrust::host_vector<uint8_t> h_flags = d_flags;\n  thrust::host_vector<type> h_out      = h_in;\n  compute_host_reference(segmented_mod,\n                         h_in,\n                         h_flags,\n                         params::total_warps,\n                         params::logical_warp_threads,\n                         valid_items,\n                         red_op_t{},\n                         h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}", "source": "/kaggle/working/cub/test/catch2_test_warp_reduce.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_exchange.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/reverse.h>\n#include <thrust/sequence.h>\n\n#include <type_traits>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread, typename = void>\nstruct exchange_data_t;\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread>\nstruct exchange_data_t<InputT,\n                       OutputT,\n                       ItemsPerThread,\n                       typename std::enable_if<std::is_same<InputT, OutputT>::value>::type>\n{\n  InputT input[ItemsPerThread];\n  OutputT (&output)[ItemsPerThread] = input;\n\n  template <int LogicalWarpThreads>\n  inline __device__ void\n  scatter(cub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange,\n          int (&ranks)[ItemsPerThread])\n  {\n    exchange.ScatterToStriped(input, ranks);\n  }\n};\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread>\nstruct exchange_data_t<InputT,\n                       OutputT,\n                       ItemsPerThread,\n                       typename std::enable_if<!std::is_same<InputT, OutputT>::value>::type>\n{\n  InputT input[ItemsPerThread];\n  OutputT output[ItemsPerThread];\n\n  template <int LogicalWarpThreads>\n  inline __device__ void\n  scatter(cub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange,\n          int (&ranks)[ItemsPerThread])\n  {\n    exchange.ScatterToStriped(input, output, ranks);\n  }\n};\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT>\n__global__ void scatter_kernel(const InputT *input_data, OutputT *output_data)\n{\n  using warp_exchange_t = cub::WarpExchange<InputT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t       = typename warp_exchange_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t temp_storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  const int lane_id = tid % LOGICAL_WARP_THREADS;\n\n  warp_exchange_t exchange(temp_storage[warp_id]);\n\n  exchange_data_t<InputT, OutputT, ITEMS_PER_THREAD> exchange_data;\n\n  // Reverse data\n  int ranks[ITEMS_PER_THREAD];\n\n  input_data += warp_id * tile_size;\n  output_data += warp_id * tile_size;\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const auto item_idx       = lane_id * ITEMS_PER_THREAD + item;\n    exchange_data.input[item] = input_data[item_idx];\n    ranks[item]               = tile_size - 1 - item_idx;\n  }\n\n  exchange_data.scatter(exchange, ranks);\n\n  // Striped to blocked\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    output_data[item * LOGICAL_WARP_THREADS + lane_id] = exchange_data.output[item];\n  }\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT>\nvoid warp_scatter_strided(thrust::device_vector<InputT> &in, thrust::device_vector<OutputT> &out)\n{\n  scatter_kernel<LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, InputT, OutputT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT,\n          typename ActionT>\n__global__ void kernel(const InputT *input_data, OutputT *output_data, ActionT action)\n{\n  using warp_exchange_t = cub::WarpExchange<InputT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t       = typename warp_exchange_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t temp_storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  const int lane_id = tid % LOGICAL_WARP_THREADS;\n\n  warp_exchange_t exchange(temp_storage[warp_id]);\n\n  exchange_data_t<InputT, OutputT, ITEMS_PER_THREAD> exchange_data;\n\n  input_data += warp_id * tile_size;\n  output_data += warp_id * tile_size;\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    exchange_data.input[item] = input_data[lane_id * ITEMS_PER_THREAD + item];\n  }\n\n  action(exchange_data.input, exchange_data.output, exchange);\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    output_data[lane_id * ITEMS_PER_THREAD + item] = exchange_data.output[item];\n  }\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT,\n          typename ActionT>\nvoid warp_exchange(thrust::device_vector<InputT> &in,\n                   thrust::device_vector<OutputT> &out,\n                   ActionT action)\n{\n  kernel<LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, InputT, OutputT, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nstruct blocked_to_striped\n{\n  template <typename InputT,\n            typename OutputT,\n            int LogicalWarpThreads,\n            int ItemsPerThread,\n            int ITEMS_PER_THREAD>\n  __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD],\n                             OutputT (&output)[ITEMS_PER_THREAD],\n                             cub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange)\n  {\n    exchange.BlockedToStriped(input, output);\n  }\n};\n\nstruct striped_to_blocked\n{\n  template <typename InputT,\n            typename OutputT,\n            int LogicalWarpThreads,\n            int ItemsPerThread,\n            int ITEMS_PER_THREAD>\n  __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD],\n                             OutputT (&output)[ITEMS_PER_THREAD],\n                             cub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange)\n  {\n    exchange.StripedToBlocked(input, output);\n  }\n};\n\ntemplate <typename T>\nthrust::host_vector<T> compute_host_reference(const thrust::device_vector<T> &d_input,\n                                              int tile_size)\n{\n  thrust::host_vector<T> input = d_input;\n\n  int num_warps = CUB_QUOTIENT_CEILING(static_cast<int>(d_input.size()), tile_size);\n  for (int warp_id = 0; warp_id < num_warps; warp_id++)\n  {\n    const int warp_data_begin = tile_size * warp_id;\n    const int warp_data_end   = warp_data_begin + tile_size;\n    thrust::reverse(input.begin() + warp_data_begin, input.begin() + warp_data_end);\n  }\n  return input;\n}\n\nusing inout_types = c2h::type_list<c2h::pair<std::uint16_t, std::int64_t>,\n                                   c2h::pair<std::uint16_t, std::uint32_t>,\n                                   c2h::pair<std::int32_t, std::int32_t>,\n                                   c2h::pair<std::int64_t, std::int64_t>,\n                                   c2h::pair<uchar3, uchar3>,\n                                   c2h::pair<ulonglong4, ulonglong4>>;\n\nusing logical_warp_threads = c2h::enum_type_list<int, 4, 16, 32>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using in_type  = typename c2h::first<c2h::get<0, TestType>>;\n  using out_type = typename c2h::second<c2h::get<0, TestType>>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count     = total_warps * tile_size;\n};\n\nCUB_TEST(\"Scatter to striped works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count);\n  thrust::device_vector<in_type> d_in(params::total_item_count);\n\n  c2h::gen(c2h::modulo_t{d_in.size()}, d_in);\n\n  warp_scatter_strided<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out);\n\n  auto h_expected_output = compute_host_reference(d_in, params::tile_size);\n  REQUIRE(h_expected_output == d_out);\n}\n\nCUB_TEST(\"Blocked to striped works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count, out_type{});\n  thrust::device_vector<in_type> d_in(params::total_item_count);\n\n  c2h::gen(c2h::modulo_t{d_in.size()}, d_in);\n\n  warp_exchange<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out,\n    blocked_to_striped{});\n  thrust::host_vector<out_type> h_expected_output(d_out.size());\n  fill_striped<params::logical_warp_threads,\n               params::items_per_thread,\n               params::logical_warp_threads * params::total_warps>(h_expected_output.begin());\n\n  REQUIRE(h_expected_output == d_out);\n}\n\nCUB_TEST(\"Striped to blocked works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count, out_type{});\n\n  thrust::host_vector<in_type> h_in(params::total_item_count);\n  fill_striped<params::logical_warp_threads,\n               params::items_per_thread,\n               params::logical_warp_threads * params::total_warps>(h_in.begin());\n  thrust::device_vector<in_type> d_in = h_in;\n\n  warp_exchange<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out,\n    striped_to_blocked{});\n  thrust::device_vector<out_type> d_expected_output(d_out.size());\n  c2h::gen(c2h::modulo_t{d_out.size()}, d_expected_output);\n\n  REQUIRE(d_expected_output == d_out);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_exchange.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/reverse.h>\n#include <thrust/sequence.h>\n\n#include <type_traits>\n\n#include \"fill_striped.cuh\"\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread, typename = void>\nstruct exchange_data_t;\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread>\nstruct exchange_data_t<InputT,\n                       OutputT,\n                       ItemsPerThread,\n                       typename std::enable_if<std::is_same<InputT, OutputT>::value>::type>\n{\n  InputT input[ItemsPerThread];\n  OutputT (&output)[ItemsPerThread] = input;\n\n  template <int LogicalWarpThreads>\n  inline __device__ void\n  scatter(hipcub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange,\n          int (&ranks)[ItemsPerThread])\n  {\n    exchange.ScatterToStriped(input, ranks);\n  }\n};\n\ntemplate <typename InputT, typename OutputT, int ItemsPerThread>\nstruct exchange_data_t<InputT,\n                       OutputT,\n                       ItemsPerThread,\n                       typename std::enable_if<!std::is_same<InputT, OutputT>::value>::type>\n{\n  InputT input[ItemsPerThread];\n  OutputT output[ItemsPerThread];\n\n  template <int LogicalWarpThreads>\n  inline __device__ void\n  scatter(hipcub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange,\n          int (&ranks)[ItemsPerThread])\n  {\n    exchange.ScatterToStriped(input, output, ranks);\n  }\n};\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT>\n__global__ void scatter_kernel(const InputT *input_data, OutputT *output_data)\n{\n  using warp_exchange_t = hipcub::WarpExchange<InputT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t       = typename warp_exchange_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t temp_storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  const int lane_id = tid % LOGICAL_WARP_THREADS;\n\n  warp_exchange_t exchange(temp_storage[warp_id]);\n\n  exchange_data_t<InputT, OutputT, ITEMS_PER_THREAD> exchange_data;\n\n  // Reverse data\n  int ranks[ITEMS_PER_THREAD];\n\n  input_data += warp_id * tile_size;\n  output_data += warp_id * tile_size;\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const auto item_idx       = lane_id * ITEMS_PER_THREAD + item;\n    exchange_data.input[item] = input_data[item_idx];\n    ranks[item]               = tile_size - 1 - item_idx;\n  }\n\n  exchange_data.scatter(exchange, ranks);\n\n  // Striped to blocked\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    output_data[item * LOGICAL_WARP_THREADS + lane_id] = exchange_data.output[item];\n  }\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT>\nvoid warp_scatter_strided(thrust::device_vector<InputT> &in, thrust::device_vector<OutputT> &out)\n{\n  scatter_kernel<LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, InputT, OutputT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT,\n          typename ActionT>\n__global__ void kernel(const InputT *input_data, OutputT *output_data, ActionT action)\n{\n  using warp_exchange_t = hipcub::WarpExchange<InputT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t       = typename warp_exchange_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t temp_storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  const int lane_id = tid % LOGICAL_WARP_THREADS;\n\n  warp_exchange_t exchange(temp_storage[warp_id]);\n\n  exchange_data_t<InputT, OutputT, ITEMS_PER_THREAD> exchange_data;\n\n  input_data += warp_id * tile_size;\n  output_data += warp_id * tile_size;\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    exchange_data.input[item] = input_data[lane_id * ITEMS_PER_THREAD + item];\n  }\n\n  action(exchange_data.input, exchange_data.output, exchange);\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    output_data[lane_id * ITEMS_PER_THREAD + item] = exchange_data.output[item];\n  }\n}\n\ntemplate <int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename InputT,\n          typename OutputT,\n          typename ActionT>\nvoid warp_exchange(thrust::device_vector<InputT> &in,\n                   thrust::device_vector<OutputT> &out,\n                   ActionT action)\n{\n  kernel<LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, InputT, OutputT, ActionT>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(thrust::raw_pointer_cast(in.data()),\n                                                thrust::raw_pointer_cast(out.data()),\n                                                action);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nstruct blocked_to_striped\n{\n  template <typename InputT,\n            typename OutputT,\n            int LogicalWarpThreads,\n            int ItemsPerThread,\n            int ITEMS_PER_THREAD>\n  __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD],\n                             OutputT (&output)[ITEMS_PER_THREAD],\n                             hipcub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange)\n  {\n    exchange.BlockedToStriped(input, output);\n  }\n};\n\nstruct striped_to_blocked\n{\n  template <typename InputT,\n            typename OutputT,\n            int LogicalWarpThreads,\n            int ItemsPerThread,\n            int ITEMS_PER_THREAD>\n  __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD],\n                             OutputT (&output)[ITEMS_PER_THREAD],\n                             hipcub::WarpExchange<InputT, ItemsPerThread, LogicalWarpThreads> &exchange)\n  {\n    exchange.StripedToBlocked(input, output);\n  }\n};\n\ntemplate <typename T>\nthrust::host_vector<T> compute_host_reference(const thrust::device_vector<T> &d_input,\n                                              int tile_size)\n{\n  thrust::host_vector<T> input = d_input;\n\n  int num_warps = CUB_QUOTIENT_CEILING(static_cast<int>(d_input.size()), tile_size);\n  for (int warp_id = 0; warp_id < num_warps; warp_id++)\n  {\n    const int warp_data_begin = tile_size * warp_id;\n    const int warp_data_end   = warp_data_begin + tile_size;\n    thrust::reverse(input.begin() + warp_data_begin, input.begin() + warp_data_end);\n  }\n  return input;\n}\n\nusing inout_types = c2h::type_list<c2h::pair<std::uint16_t, std::int64_t>,\n                                   c2h::pair<std::uint16_t, std::uint32_t>,\n                                   c2h::pair<std::int32_t, std::int32_t>,\n                                   c2h::pair<std::int64_t, std::int64_t>,\n                                   c2h::pair<uchar3, uchar3>,\n                                   c2h::pair<ulonglong4, ulonglong4>>;\n\nusing logical_warp_threads = c2h::enum_type_list<int, 4, 16, 32>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == CUB_WARP_THREADS(0));\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value() { return total_warps; }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using in_type  = typename c2h::first<c2h::get<0, TestType>>;\n  using out_type = typename c2h::second<c2h::get<0, TestType>>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count     = total_warps * tile_size;\n};\n\nCUB_TEST(\"Scatter to striped works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count);\n  thrust::device_vector<in_type> d_in(params::total_item_count);\n\n  c2h::gen(c2h::modulo_t{d_in.size()}, d_in);\n\n  warp_scatter_strided<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out);\n\n  auto h_expected_output = compute_host_reference(d_in, params::tile_size);\n  REQUIRE(h_expected_output == d_out);\n}\n\nCUB_TEST(\"Blocked to striped works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count, out_type{});\n  thrust::device_vector<in_type> d_in(params::total_item_count);\n\n  c2h::gen(c2h::modulo_t{d_in.size()}, d_in);\n\n  warp_exchange<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out,\n    blocked_to_striped{});\n  thrust::host_vector<out_type> h_expected_output(d_out.size());\n  fill_striped<params::logical_warp_threads,\n               params::items_per_thread,\n               params::logical_warp_threads * params::total_warps>(h_expected_output.begin());\n\n  REQUIRE(h_expected_output == d_out);\n}\n\nCUB_TEST(\"Striped to blocked works\",\n         \"[exchange][warp]\",\n         inout_types,\n         logical_warp_threads,\n         items_per_thread)\n{\n  using params   = params_t<TestType>;\n  using in_type  = typename params::in_type;\n  using out_type = typename params::out_type;\n  thrust::device_vector<out_type> d_out(params::total_item_count, out_type{});\n\n  thrust::host_vector<in_type> h_in(params::total_item_count);\n  fill_striped<params::logical_warp_threads,\n               params::items_per_thread,\n               params::logical_warp_threads * params::total_warps>(h_in.begin());\n  thrust::device_vector<in_type> d_in = h_in;\n\n  warp_exchange<params::logical_warp_threads, params::items_per_thread, params::total_warps>(\n    d_in,\n    d_out,\n    striped_to_blocked{});\n  thrust::device_vector<out_type> d_expected_output(d_out.size());\n  c2h::gen(c2h::modulo_t{d_out.size()}, d_expected_output);\n\n  REQUIRE(d_expected_output == d_out);\n}\n", "source": "/kaggle/working/cub/test/catch2_test_warp_exchange.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/block/block_store.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm /* StoreAlgorithm */>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread,\n                  ThreadsInBlock,\n                  cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm StoreAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  using input_t      = cub::detail::value_t<InputIteratorT>;\n  using block_store_t = cub::BlockStore<input_t,\n                                        ThreadsInBlock,\n                                        ItemsPerThread,\n                                        StoreAlgorithm>;\n  using storage_t = typename block_store_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_store_t block_store(storage);\n\n  input_t data[ItemsPerThread];\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread, ThreadsInBlock, StoreAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      data[i] = input[idx];\n    }\n  }\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_store.Store(output, data);\n  }\n  else\n  {\n    block_store.Store(output, data, num_items);\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm /* StoreAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread,\n                 ThreadsInBlock,\n                 cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm StoreAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_store(\n    InputIteratorT input,\n    OutputIteratorT output,\n    int num_items)\n{\n  using input_t = cub::detail::value_t<InputIteratorT>;\n  using block_store_t = cub::BlockStore<input_t,\n                                        ThreadsInBlock,\n                                        ItemsPerThread,\n                                        StoreAlgorithm>;\n  using storage_t = typename block_store_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= 1024 * 48;\n\n  kernel<InputIteratorT,\n         OutputIteratorT,\n         ItemsPerThread,\n         ThreadsInBlock,\n         StoreAlgorithm><<<1, ThreadsInBlock>>>(\n    std::integral_constant<bool, sufficient_resources>{},\n    input,\n    output,\n    num_items);\n\n  REQUIRE( cudaSuccess == cudaPeekAtLastError() );\n  REQUIRE( cudaSuccess == cudaDeviceSynchronize() );\n}\n\n\n// %PARAM% IPT it 1:11\n\nusing types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing store_algorithm  = c2h::enum_type_list<\n  cub::BlockStoreAlgorithm,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE,             \n  cub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_store_algorithm = c2h::enum_type_list<\n  cub::BlockStoreAlgorithm,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n  cub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm = \n    c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block store works with even block sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                       thrust::raw_pointer_cast(d_output.data()),\n                                       static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with even odd sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                       thrust::raw_pointer_cast(\n                                         d_output.data()),\n                                       static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with even vector types\",\n         \"[store][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with custom types\",\n         \"[store][block]\",\n         items_per_thread,\n         store_algorithm)\n{\n  using type                     = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm =\n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nCUB_TEST(\"Block store works with caching iterators\",\n         \"[store][block]\",\n         items_per_thread,\n         store_algorithm)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm =\n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n  cub::CacheModifiedOutputIterator<cub::CacheStoreModifier::STORE_DEFAULT, type> out(\n    thrust::raw_pointer_cast(d_output.data()));\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    out,\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_store.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n// Has to go after all cub headers. Otherwise, this test won't catch unused\n// variables in cub kernels.\n#include \"catch2_test_helper.h\"\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm /* StoreAlgorithm */>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread,\n                  ThreadsInBlock,\n                  hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm StoreAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  using input_t      = hipcub::detail::value_t<InputIteratorT>;\n  using block_store_t = hipcub::BlockStore<input_t,\n                                        ThreadsInBlock,\n                                        ItemsPerThread,\n                                        StoreAlgorithm>;\n  using storage_t = typename block_store_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_store_t block_store(storage);\n\n  input_t data[ItemsPerThread];\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread, ThreadsInBlock, StoreAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      data[i] = input[idx];\n    }\n  }\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_store.Store(output, data);\n  }\n  else\n  {\n    block_store.Store(output, data, num_items);\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm /* StoreAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>,\n                       InputIteratorT input,\n                       OutputIteratorT output,\n                       int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx =\n      output_idx<ItemsPerThread,\n                 ThreadsInBlock,\n                 hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm StoreAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_store(\n    InputIteratorT input,\n    OutputIteratorT output,\n    int num_items)\n{\n  using input_t = hipcub::detail::value_t<InputIteratorT>;\n  using block_store_t = hipcub::BlockStore<input_t,\n                                        ThreadsInBlock,\n                                        ItemsPerThread,\n                                        StoreAlgorithm>;\n  using storage_t = typename block_store_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= 1024 * 48;\n\n  kernel<InputIteratorT,\n         OutputIteratorT,\n         ItemsPerThread,\n         ThreadsInBlock,\n         StoreAlgorithm><<<1, ThreadsInBlock>>>(\n    std::integral_constant<bool, sufficient_resources>{},\n    input,\n    output,\n    num_items);\n\n  REQUIRE( hipSuccess == hipPeekAtLastError() );\n  REQUIRE( hipSuccess == hipDeviceSynchronize() );\n}\n\n\n// %PARAM% IPT it 1:11\n\nusing types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing store_algorithm  = c2h::enum_type_list<\n  hipcub::BlockStoreAlgorithm,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE,             \n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_store_algorithm = c2h::enum_type_list<\n  hipcub::BlockStoreAlgorithm,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n  hipcub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm = \n    c2h::get<3, TestType>::value;\n};\n\nCUB_TEST(\"Block store works with even block sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                       thrust::raw_pointer_cast(d_output.data()),\n                                       static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with even odd sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(thrust::raw_pointer_cast(d_input.data()),\n                                       thrust::raw_pointer_cast(\n                                         d_output.data()),\n                                       static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with even vector types\",\n         \"[store][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type = typename params::type;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread,\n              params::threads_in_block,\n              params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE( d_input == d_output );\n}\n\nCUB_TEST(\"Block store works with custom types\",\n         \"[store][block]\",\n         items_per_thread,\n         store_algorithm)\n{\n  using type                     = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm =\n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nCUB_TEST(\"Block store works with caching iterators\",\n         \"[store][block]\",\n         items_per_thread,\n         store_algorithm)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm =\n    c2h::get<1, TestType>::value;\n\n  thrust::device_vector<type> d_input(\n    GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(CUB_SEED(10), d_input);\n\n  thrust::device_vector<type> d_output(d_input.size());\n  hipcub::CacheModifiedOutputIterator<hipcub::CacheStoreModifier::STORE_DEFAULT, type> out(\n    thrust::raw_pointer_cast(d_output.data()));\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    out,\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n", "source": "/kaggle/working/cub/test/catch2_test_block_store.cu"}
{"input": "/******************************************************************************\n * Copyright (c) NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::Unique utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nint                     g_repeat                = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to unique entrypoint\n */\ntemplate <typename KeyInputIteratorT, typename ValueInputIteratorT, typename KeyOutputIteratorT, typename ValueOutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__\ncudaError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    cudaError_t                 */*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    KeyInputIteratorT           d_keys_in,\n    ValueInputIteratorT         d_values_in,\n    KeyOutputIteratorT          d_keys_out,\n    ValueOutputIteratorT        d_values_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items)\n{\n    cudaError_t error = cudaSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::UniqueByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename KeyInputIteratorT,\n          typename ValueInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  cudaError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  KeyInputIteratorT    d_keys_in,\n                                  ValueInputIteratorT  d_values_in,\n                                  KeyOutputIteratorT   d_keys_out,\n                                  ValueOutputIteratorT d_values_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_values_in,\n                          d_keys_out,\n                          d_values_out,\n                          d_num_selected_out,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename KeyInputIteratorT,\n          typename ValueInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\ncudaError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     cudaError_t *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     KeyInputIteratorT    d_keys_in,\n                     ValueInputIteratorT  d_values_in,\n                     KeyOutputIteratorT   d_keys_out,\n                     ValueOutputIteratorT d_values_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  cudaError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              KeyInputIteratorT,\n                              ValueInputIteratorT,\n                              KeyOutputIteratorT,\n                              ValueOutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_values_in,\n            d_keys_out,\n            d_values_out,\n            d_num_selected_out,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences for the current run\n        int repeat;\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve unique problem\n */\ntemplate <\n    typename        KeyInputIteratorT,\n    typename        ValueInputIteratorT,\n    typename        KeyT,\n    typename        ValueT>\nint Solve(\n    KeyInputIteratorT    h_keys_in,\n    ValueInputIteratorT  h_values_in,\n    KeyT                 *h_keys_reference,\n    ValueT               *h_values_reference,\n    int                  num_items)\n{\n    int num_selected = 0;\n    if (num_items > 0)\n    {\n        h_keys_reference[num_selected] = h_keys_in[0];\n        h_values_reference[num_selected] = h_values_in[0];\n        num_selected++;\n    }\n\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (h_keys_in[i] != h_keys_in[i - 1])\n        {\n            h_keys_reference[num_selected] = h_keys_in[i];\n            h_values_reference[num_selected] = h_values_in[i];\n            num_selected++;\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            KeyInputIteratorT,\n    typename            ValueInputIteratorT,\n    typename            KeyT,\n    typename            ValueT>\nvoid Test(\n    KeyInputIteratorT    d_keys_in,\n    ValueInputIteratorT  d_values_in,\n    KeyT                 *h_keys_reference,\n    ValueT               *h_values_reference,\n    int                  num_selected,\n    int                  num_items)\n{\n    // Allocate device output array and num selected\n    KeyT    *d_keys_out = NULL;\n    ValueT  *d_values_out = NULL;\n    int     *d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_out, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_out, sizeof(ValueT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    CubDebugExit(cudaMemset(d_keys_out, 0, sizeof(KeyT) * num_items));\n    CubDebugExit(cudaMemset(d_values_out, 0, sizeof(ValueT) * num_items));\n    CubDebugExit(cudaMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare11 = CompareDeviceResults(h_keys_reference, d_keys_out, num_selected, true, g_verbose);\n    int compare12 = CompareDeviceResults(h_values_reference, d_values_out, num_selected, true, g_verbose);\n    int compare1 = compare11 && compare12;\n    printf(\"\\t Data %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis        = elapsed_millis / g_timing_iterations;\n        float giga_rate         = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth    = float((num_items + num_selected) * (sizeof(KeyT) + sizeof(ValueT))) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_keys_out) CubDebugExit(g_allocator.DeviceFree(d_keys_out));\n    if (d_values_out) CubDebugExit(g_allocator.DeviceFree(d_values_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment)\n{\n    // Allocate host arrays\n    KeyT*    h_keys_in        = new KeyT[num_items];\n    ValueT*  h_values_in      = new ValueT[num_items];\n    KeyT*    h_keys_reference = new KeyT[num_items];\n    ValueT*  h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    Initialize(entropy_reduction, h_values_in, num_items, max_segment);\n    int num_selected = Solve(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_items);\n\n    printf(\"\\nPointer %s cub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(KeyT).name(),\n        (int) sizeof(KeyT),\n        entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT *d_keys_in = NULL;\n    ValueT *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(ValueT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, cudaMemcpyHostToDevice));\n    CubDebugExit(cudaMemcpy(d_values_in, h_values_in, sizeof(ValueT) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, d_values_in, h_keys_reference, h_values_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test DeviceSelect on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid TestIterator(\n    int             num_items)\n{\n    // Use a counting iterator as the input\n    CountingInputIterator<KeyT, int> h_keys_in(0);\n    CountingInputIterator<ValueT, int> h_values_in(0);\n\n    // Allocate host arrays\n    KeyT*    h_keys_reference   = new KeyT[num_items];\n    ValueT*  h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    int num_selected = Solve(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_items);\n\n    printf(\"\\nIterator %s cub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(KeyT).name(),\n        (int) sizeof(ValueT));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND>(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid Test(\n    int             num_items)\n{\n    for (int max_segment = 1; ((max_segment > 0) && (max_segment < num_items)); max_segment *= 11)\n    {\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 0, max_segment);\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 2, max_segment);\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 7, max_segment);\n    }\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, ValueT>(num_items);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, ValueT>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<KeyT, ValueT>(0);\n        TestOp<KeyT, ValueT>(1);\n        TestOp<KeyT, ValueT>(100);\n        TestOp<KeyT, ValueT>(10000);\n        TestOp<KeyT, ValueT>(1000000);\n    }\n    else\n    {\n        TestOp<KeyT, ValueT>(num_items);\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"repeat\", g_repeat);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    Test<unsigned char, int>(num_items);\n    Test<unsigned char, long>(num_items);\n    Test<unsigned short, int>(num_items);\n    Test<unsigned short, long>(num_items);\n    Test<unsigned int, int>(num_items);\n    Test<unsigned int, long>(num_items);\n    Test<unsigned long long, int>(num_items);\n    Test<unsigned long long, long>(num_items);\n\n    Test<uchar2, uint2>(num_items);\n    Test<uchar2, ulonglong2>(num_items);\n    Test<ushort2, uint2>(num_items);\n    Test<ushort2, ulonglong2>(num_items);\n    Test<uint2, uint2>(num_items);\n    Test<uint2, ulonglong2>(num_items);\n    Test<ulonglong2, uint2>(num_items);\n    Test<ulonglong2, ulonglong2>(num_items);\n\n    Test<uchar4, uint4>(num_items);\n    Test<uchar4, ulonglong4>(num_items);\n    Test<ushort4, uint4>(num_items);\n    Test<ushort4, ulonglong4>(num_items);\n    Test<uint4, uint4>(num_items);\n    Test<uint4, ulonglong4>(num_items);\n    Test<ulonglong4, uint4>(num_items);\n    Test<ulonglong4, ulonglong4>(num_items);\n\n    Test<TestFoo, TestFoo>(num_items);\n    Test<TestFoo, TestBar>(num_items);\n    Test<TestFoo, int>(num_items);\n    Test<TestBar, TestFoo>(num_items);\n    Test<TestBar, TestBar>(num_items);\n    Test<TestBar, int>(num_items);\n\n    return 0;\n}\n\n\n\n", "output": "/******************************************************************************\n * Copyright (c) NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceSelect::Unique utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_select.cuh>\n#include <cub/iterator/counting_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose               = false;\nint                     g_timing_iterations     = 0;\nint                     g_repeat                = 0;\nfloat                   g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceSelect entrypoints\n//---------------------------------------------------------------------\n\n\n/**\n * Dispatch to unique entrypoint\n */\ntemplate <typename KeyInputIteratorT, typename ValueInputIteratorT, typename KeyOutputIteratorT, typename ValueOutputIteratorT, typename NumSelectedIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__\nhipError_t Dispatch(\n    Int2Type<CUB>               /*dispatch_to*/,\n    int                         timing_timing_iterations,\n    size_t                      */*d_temp_storage_bytes*/,\n    hipError_t                 */*d_cdp_error*/,\n\n    void*                       d_temp_storage,\n    size_t                      &temp_storage_bytes,\n    KeyInputIteratorT           d_keys_in,\n    ValueInputIteratorT         d_values_in,\n    KeyOutputIteratorT          d_keys_out,\n    ValueOutputIteratorT        d_values_out,\n    NumSelectedIteratorT        d_num_selected_out,\n    OffsetT                     num_items)\n{\n    hipError_t error = hipSuccess;\n    for (int i = 0; i < timing_timing_iterations; ++i)\n    {\n        error = DeviceSelect::UniqueByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items);\n    }\n    return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceSelect\n */\ntemplate <int CubBackend,\n          typename KeyInputIteratorT,\n          typename ValueInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(Int2Type<CubBackend> cub_backend,\n                                  int                  timing_timing_iterations,\n                                  size_t              *d_temp_storage_bytes,\n                                  hipError_t         *d_cdp_error,\n\n                                  void                *d_temp_storage,\n                                  size_t               temp_storage_bytes,\n                                  KeyInputIteratorT    d_keys_in,\n                                  ValueInputIteratorT  d_values_in,\n                                  KeyOutputIteratorT   d_keys_out,\n                                  ValueOutputIteratorT d_values_out,\n                                  NumSelectedIteratorT d_num_selected_out,\n                                  OffsetT              num_items)\n{\n  *d_cdp_error = Dispatch(cub_backend,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_keys_in,\n                          d_values_in,\n                          d_keys_out,\n                          d_values_out,\n                          d_num_selected_out,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename KeyInputIteratorT,\n          typename ValueInputIteratorT,\n          typename KeyOutputIteratorT,\n          typename ValueOutputIteratorT,\n          typename NumSelectedIteratorT,\n          typename OffsetT>\nhipError_t Dispatch(Int2Type<CDP> /*dispatch_to*/,\n                     int          timing_timing_iterations,\n                     size_t      *d_temp_storage_bytes,\n                     hipError_t *d_cdp_error,\n\n                     void                *d_temp_storage,\n                     size_t              &temp_storage_bytes,\n                     KeyInputIteratorT    d_keys_in,\n                     ValueInputIteratorT  d_values_in,\n                     KeyOutputIteratorT   d_keys_out,\n                     ValueOutputIteratorT d_values_out,\n                     NumSelectedIteratorT d_num_selected_out,\n                     OffsetT              num_items)\n{\n  // Invoke kernel to invoke device-side dispatch\n  hipError_t retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<CUB,\n                              KeyInputIteratorT,\n                              ValueInputIteratorT,\n                              KeyOutputIteratorT,\n                              ValueOutputIteratorT,\n                              NumSelectedIteratorT,\n                              OffsetT>,\n            Int2Type<CUB>{},\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_keys_in,\n            d_values_in,\n            d_keys_out,\n            d_values_out,\n            d_num_selected_out,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    int         entropy_reduction,\n    T           *h_in,\n    int         num_items,\n    int         max_segment)\n{\n    unsigned int max_int = (unsigned int) -1;\n\n    int key = 0;\n    int i = 0;\n    while (i < num_items)\n    {\n        // Select number of repeating occurrences for the current run\n        int repeat;\n        if (max_segment < 0)\n        {\n            repeat = num_items;\n        }\n        else if (max_segment < 2)\n        {\n            repeat = 1;\n        }\n        else\n        {\n            RandomBits(repeat, entropy_reduction);\n            repeat = (int) ((double(repeat) * double(max_segment)) / double(max_int));\n            repeat = CUB_MAX(1, repeat);\n        }\n\n        int j = i;\n        while (j < CUB_MIN(i + repeat, num_items))\n        {\n            InitValue(INTEGER_SEED, h_in[j], key);\n            j++;\n        }\n\n        i = j;\n        key++;\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n\n/**\n * Solve unique problem\n */\ntemplate <\n    typename        KeyInputIteratorT,\n    typename        ValueInputIteratorT,\n    typename        KeyT,\n    typename        ValueT>\nint Solve(\n    KeyInputIteratorT    h_keys_in,\n    ValueInputIteratorT  h_values_in,\n    KeyT                 *h_keys_reference,\n    ValueT               *h_values_reference,\n    int                  num_items)\n{\n    int num_selected = 0;\n    if (num_items > 0)\n    {\n        h_keys_reference[num_selected] = h_keys_in[0];\n        h_values_reference[num_selected] = h_values_in[0];\n        num_selected++;\n    }\n\n    for (int i = 1; i < num_items; ++i)\n    {\n        if (h_keys_in[i] != h_keys_in[i - 1])\n        {\n            h_keys_reference[num_selected] = h_keys_in[i];\n            h_values_reference[num_selected] = h_values_in[i];\n            num_selected++;\n        }\n    }\n\n    return num_selected;\n}\n\n\n\n/**\n * Test DeviceSelect for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            KeyInputIteratorT,\n    typename            ValueInputIteratorT,\n    typename            KeyT,\n    typename            ValueT>\nvoid Test(\n    KeyInputIteratorT    d_keys_in,\n    ValueInputIteratorT  d_values_in,\n    KeyT                 *h_keys_reference,\n    ValueT               *h_values_reference,\n    int                  num_selected,\n    int                  num_items)\n{\n    // Allocate device output array and num selected\n    KeyT    *d_keys_out = NULL;\n    ValueT  *d_values_out = NULL;\n    int     *d_num_selected_out = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_out, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_out, sizeof(ValueT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_num_selected_out, sizeof(int)));\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,           sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    // Clear device output array\n    CubDebugExit(hipMemset(d_keys_out, 0, sizeof(KeyT) * num_items));\n    CubDebugExit(hipMemset(d_values_out, 0, sizeof(ValueT) * num_items));\n    CubDebugExit(hipMemset(d_num_selected_out, 0, sizeof(int)));\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), 1, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare11 = CompareDeviceResults(h_keys_reference, d_keys_out, num_selected, true, g_verbose);\n    int compare12 = CompareDeviceResults(h_values_reference, d_values_out, num_selected, true, g_verbose);\n    int compare1 = compare11 && compare12;\n    printf(\"\\t Data %s \", compare1 ? \"FAIL\" : \"PASS\");\n\n    int compare2 = CompareDeviceResults(&num_selected, d_num_selected_out, 1, true, g_verbose);\n    printf(\"\\t Count %s \", compare2 ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    CubDebugExit(Dispatch(Int2Type<BACKEND>(), g_timing_iterations, d_temp_storage_bytes, d_cdp_error, d_temp_storage, temp_storage_bytes, d_keys_in, d_values_in, d_keys_out, d_values_out, d_num_selected_out, num_items));\n    gpu_timer.Stop();\n    float elapsed_millis = gpu_timer.ElapsedMillis();\n\n    // Display performance\n    if (g_timing_iterations > 0)\n    {\n        float avg_millis        = elapsed_millis / g_timing_iterations;\n        float giga_rate         = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n        float giga_bandwidth    = float((num_items + num_selected) * (sizeof(KeyT) + sizeof(ValueT))) / avg_millis / 1000.0f / 1000.0f;\n        printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% peak\", avg_millis, giga_rate, giga_bandwidth, giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n    printf(\"\\n\\n\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Cleanup\n    if (d_keys_out) CubDebugExit(g_allocator.DeviceFree(d_keys_out));\n    if (d_values_out) CubDebugExit(g_allocator.DeviceFree(d_values_out));\n    if (d_num_selected_out) CubDebugExit(g_allocator.DeviceFree(d_num_selected_out));\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare1 | compare2);\n}\n\n\n/**\n * Test DeviceSelect on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid TestPointer(\n    int             num_items,\n    int             entropy_reduction,\n    int             max_segment)\n{\n    // Allocate host arrays\n    KeyT*    h_keys_in        = new KeyT[num_items];\n    ValueT*  h_values_in      = new ValueT[num_items];\n    KeyT*    h_keys_reference = new KeyT[num_items];\n    ValueT*  h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    Initialize(entropy_reduction, h_keys_in, num_items, max_segment);\n    Initialize(entropy_reduction, h_values_in, num_items, max_segment);\n    int num_selected = Solve(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_items);\n\n    printf(\"\\nPointer %s hipcub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements, entropy_reduction %d\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(KeyT).name(),\n        (int) sizeof(KeyT),\n        entropy_reduction);\n    fflush(stdout);\n\n    // Allocate problem device arrays\n    KeyT *d_keys_in = NULL;\n    ValueT *d_values_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_keys_in, sizeof(KeyT) * num_items));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_values_in, sizeof(ValueT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_keys_in, h_keys_in, sizeof(KeyT) * num_items, hipMemcpyHostToDevice));\n    CubDebugExit(hipMemcpy(d_values_in, h_values_in, sizeof(ValueT) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_keys_in, d_values_in, h_keys_reference, h_values_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_keys_in) delete[] h_keys_in;\n    if (h_values_in) delete[] h_values_in;\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n    if (d_keys_in) CubDebugExit(g_allocator.DeviceFree(d_keys_in));\n    if (d_values_in) CubDebugExit(g_allocator.DeviceFree(d_values_in));\n}\n\n\n/**\n * Test DeviceSelect on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid TestIterator(\n    int             num_items)\n{\n    // Use a counting iterator as the input\n    CountingInputIterator<KeyT, int> h_keys_in(0);\n    CountingInputIterator<ValueT, int> h_values_in(0);\n\n    // Allocate host arrays\n    KeyT*    h_keys_reference   = new KeyT[num_items];\n    ValueT*  h_values_reference = new ValueT[num_items];\n\n    // Initialize problem and solution\n    int num_selected = Solve(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_items);\n\n    printf(\"\\nIterator %s hipcub::DeviceSelect::Unique %d items, %d selected (avg run length %.3f), %s %d-byte elements\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        num_items, num_selected, float(num_items) / num_selected,\n        typeid(KeyT).name(),\n        (int) sizeof(ValueT));\n    fflush(stdout);\n\n    // Run Test\n    Test<BACKEND>(h_keys_in, h_values_in, h_keys_reference, h_values_reference, num_selected, num_items);\n\n    // Cleanup\n    if (h_keys_reference) delete[] h_keys_reference;\n    if (h_values_reference) delete[] h_values_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        KeyT,\n    typename        ValueT>\nvoid Test(\n    int             num_items)\n{\n    for (int max_segment = 1; ((max_segment > 0) && (max_segment < num_items)); max_segment *= 11)\n    {\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 0, max_segment);\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 2, max_segment);\n        TestPointer<BACKEND, KeyT, ValueT>(num_items, 7, max_segment);\n    }\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid TestOp(\n    int             num_items)\n{\n#if TEST_CDP == 0\n    Test<CUB, KeyT, ValueT>(num_items);\n#elif TEST_CDP == 1\n    Test<CDP, KeyT, ValueT>(num_items);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename        KeyT,\n    typename        ValueT>\nvoid Test(\n    int             num_items)\n{\n    if (num_items < 0)\n    {\n        TestOp<KeyT, ValueT>(0);\n        TestOp<KeyT, ValueT>(1);\n        TestOp<KeyT, ValueT>(100);\n        TestOp<KeyT, ValueT>(10000);\n        TestOp<KeyT, ValueT>(1000000);\n    }\n    else\n    {\n        TestOp<KeyT, ValueT>(num_items);\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items           = -1;\n    int entropy_reduction   = 0;\n    int maxseg              = 1000;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n    args.GetCmdLineArgument(\"repeat\", g_repeat);\n    args.GetCmdLineArgument(\"maxseg\", maxseg);\n    args.GetCmdLineArgument(\"entropy\", entropy_reduction);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--maxseg=<max segment length>]\"\n            \"[--entropy=<segment length bit entropy reduction rounds>]\"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n\n    // Test different input types\n    Test<unsigned char, int>(num_items);\n    Test<unsigned char, long>(num_items);\n    Test<unsigned short, int>(num_items);\n    Test<unsigned short, long>(num_items);\n    Test<unsigned int, int>(num_items);\n    Test<unsigned int, long>(num_items);\n    Test<unsigned long long, int>(num_items);\n    Test<unsigned long long, long>(num_items);\n\n    Test<uchar2, uint2>(num_items);\n    Test<uchar2, ulonglong2>(num_items);\n    Test<ushort2, uint2>(num_items);\n    Test<ushort2, ulonglong2>(num_items);\n    Test<uint2, uint2>(num_items);\n    Test<uint2, ulonglong2>(num_items);\n    Test<ulonglong2, uint2>(num_items);\n    Test<ulonglong2, ulonglong2>(num_items);\n\n    Test<uchar4, uint4>(num_items);\n    Test<uchar4, ulonglong4>(num_items);\n    Test<ushort4, uint4>(num_items);\n    Test<ushort4, ulonglong4>(num_items);\n    Test<uint4, uint4>(num_items);\n    Test<uint4, ulonglong4>(num_items);\n    Test<ulonglong4, uint4>(num_items);\n    Test<ulonglong4, ulonglong4>(num_items);\n\n    Test<TestFoo, TestFoo>(num_items);\n    Test<TestFoo, TestBar>(num_items);\n    Test<TestFoo, int>(num_items);\n    Test<TestBar, TestFoo>(num_items);\n    Test<TestBar, TestBar>(num_items);\n    Test<TestBar, int>(num_items);\n\n    return 0;\n}\n\n\n\n", "source": "/kaggle/working/cub/test/test_device_select_unique_by_key.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <cub/device/device_memcpy.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_ptx.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/fill.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/logical.h>\n#include <thrust/sequence.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <limits>\n#include <numeric>\n#include <random>\n#include <type_traits>\n#include <vector>\n\n#include \"test_util.h\"\n\n/**\n * @brief Host-side random data generation\n */\ntemplate <typename T>\nvoid GenerateRandomData(\n  T *rand_out,\n  const std::size_t num_items,\n  const T min_rand_val          = std::numeric_limits<T>::min(),\n  const T max_rand_val          = std::numeric_limits<T>::max(),\n  const std::uint_fast32_t seed = 320981U,\n  typename std::enable_if<std::is_integral<T>::value && (sizeof(T) >= 2)>::type * = nullptr)\n{\n  // initialize random number generator\n  std::mt19937 rng(seed);\n  std::uniform_int_distribution<T> uni_dist(min_rand_val, max_rand_val);\n\n  // generate random numbers\n  for (std::size_t i = 0; i < num_items; ++i)\n  {\n    rand_out[i] = uni_dist(rng);\n  }\n}\n\ntemplate <typename InputBufferIt,\n          typename OutputBufferIt,\n          typename BufferSizeIteratorT,\n          typename BufferOffsetT>\nvoid __global__ BaselineBatchMemCpyKernel(InputBufferIt input_buffer_it,\n                                          OutputBufferIt output_buffer_it,\n                                          BufferSizeIteratorT buffer_sizes,\n                                          BufferOffsetT num_buffers)\n{\n  BufferOffsetT gtid = blockDim.x * blockIdx.x + threadIdx.x;\n  if (gtid >= num_buffers)\n  {\n    return;\n  }\n  for (BufferOffsetT i = 0; i < buffer_sizes[gtid]; i++)\n  {\n    reinterpret_cast<uint8_t *>(output_buffer_it[gtid])[i] =\n      reinterpret_cast<uint8_t *>(input_buffer_it[gtid])[i];\n  }\n}\n\ntemplate <typename InputBufferIt, typename OutputBufferIt, typename BufferSizeIteratorT>\nvoid InvokeBaselineBatchMemcpy(InputBufferIt input_buffer_it,\n                               OutputBufferIt output_buffer_it,\n                               BufferSizeIteratorT buffer_sizes,\n                               uint32_t num_buffers)\n{\n  constexpr uint32_t block_threads = 128U;\n  uint32_t num_blocks              = (num_buffers + block_threads - 1) / block_threads;\n  BaselineBatchMemCpyKernel<<<num_blocks, block_threads>>>(input_buffer_it,\n                                                           output_buffer_it,\n                                                           buffer_sizes,\n                                                           num_buffers);\n}\n\ntemplate <typename InputBufferIt,\n          typename OutputBufferIt,\n          typename BufferSizeIteratorT,\n          typename BufferOffsetT>\nvoid __global__ BaselineBatchMemCpyPerBlockKernel(InputBufferIt input_buffer_it,\n                                                  OutputBufferIt output_buffer_it,\n                                                  BufferSizeIteratorT buffer_sizes,\n                                                  BufferOffsetT num_buffers)\n{\n  BufferOffsetT gbid = blockIdx.x;\n  if (gbid >= num_buffers)\n  {\n    return;\n  }\n  for (BufferOffsetT i = threadIdx.x; i < buffer_sizes[gbid] / 8; i += blockDim.x)\n  {\n    reinterpret_cast<uint64_t *>(output_buffer_it[gbid])[i] =\n      reinterpret_cast<uint64_t *>(input_buffer_it[gbid])[i];\n  }\n}\n\n/**\n * @brief Used for generating a shuffled but cohesive sequence of output-buffer offsets for the\n * sequence of input-buffers.\n */\ntemplate <typename BufferOffsetT, typename ByteOffsetT, typename BufferSizeT>\nstd::vector<ByteOffsetT> GetShuffledBufferOffsets(const std::vector<BufferSizeT> &buffer_sizes,\n                                                  const std::uint_fast32_t seed = 320981U)\n{\n  BufferOffsetT num_buffers = static_cast<BufferOffsetT>(buffer_sizes.size());\n\n  // We're remapping the i-th buffer to pmt_idxs[i]\n  std::mt19937 rng(seed);\n  std::vector<BufferOffsetT> pmt_idxs(num_buffers);\n  std::iota(pmt_idxs.begin(), pmt_idxs.end(), static_cast<BufferOffsetT>(0));\n  std::shuffle(std::begin(pmt_idxs), std::end(pmt_idxs), rng);\n\n  // Compute the offsets using the new mapping\n  ByteOffsetT running_offset = {};\n  std::vector<ByteOffsetT> permuted_offsets;\n  permuted_offsets.reserve(num_buffers);\n  for (auto permuted_buffer_idx : pmt_idxs)\n  {\n    permuted_offsets.emplace_back(running_offset);\n    running_offset += buffer_sizes[permuted_buffer_idx];\n  }\n\n  // Generate the scatter indexes that identify where each buffer was mapped to\n  std::vector<BufferOffsetT> scatter_idxs(num_buffers);\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    scatter_idxs[pmt_idxs[i]] = i;\n  }\n\n  std::vector<ByteOffsetT> new_offsets(num_buffers);\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    new_offsets[i] = permuted_offsets[scatter_idxs[i]];\n  }\n\n  return new_offsets;\n}\n\n/**\n * @brief Function object class template that takes an offset and returns an iterator at the given\n * offset relative to a fixed base iterator.\n *\n * @tparam IteratorT The random-access iterator type to be returned\n */\ntemplate <typename IteratorT>\nstruct OffsetToPtrOp\n{\n  template <typename T>\n  __host__ __device__ __forceinline__ IteratorT operator()(T offset) const\n  {\n    return base_it + offset;\n  }\n  IteratorT base_it;\n};\n\nenum class TestDataGen\n{\n  // Random offsets into a data segment\n  RANDOM,\n\n  // Buffers cohesively reside next to each other\n  CONSECUTIVE\n};\n\n/**\n * @brief\n *\n * @tparam AtomicT The most granular type being copied. All source and destination pointers will be\n * aligned based on this type, the number of bytes being copied will be an integer multiple of this\n * type's size\n * @tparam BufferOffsetT Type used for indexing into the array of buffers\n * @tparam BufferSizeT Type used for indexing into individual bytes of a buffer (large enough to\n * cover the max buffer size)\n * @tparam ByteOffsetT Type used for indexing into bytes over *all* the buffers' sizes\n */\ntemplate <typename AtomicT, typename BufferOffsetT, typename BufferSizeT, typename ByteOffsetT>\nvoid RunTest(BufferOffsetT num_buffers,\n             BufferSizeT min_buffer_size,\n             BufferSizeT max_buffer_size,\n             TestDataGen input_gen,\n             TestDataGen output_gen)\n{\n  using SrcPtrT = uint8_t *;\n\n  // Buffer segment data (their offsets and sizes)\n  std::vector<BufferSizeT> h_buffer_sizes(num_buffers);\n  std::vector<ByteOffsetT> h_buffer_src_offsets(num_buffers);\n  std::vector<ByteOffsetT> h_buffer_dst_offsets(num_buffers);\n\n  // Device-side resources\n  void *d_in                        = nullptr;\n  void *d_out                       = nullptr;\n  ByteOffsetT *d_buffer_src_offsets = nullptr;\n  ByteOffsetT *d_buffer_dst_offsets = nullptr;\n  BufferSizeT *d_buffer_sizes       = nullptr;\n  void *d_temp_storage              = nullptr;\n  size_t temp_storage_bytes         = 0;\n\n  // Generate the buffer sizes\n  GenerateRandomData(h_buffer_sizes.data(), h_buffer_sizes.size(), min_buffer_size, max_buffer_size);\n\n  // Make sure buffer sizes are a multiple of the most granular unit (one AtomicT) being copied\n  // (round down)\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    h_buffer_sizes[i] = (h_buffer_sizes[i] / sizeof(AtomicT)) * sizeof(AtomicT);\n  }\n\n  // Compute the total bytes to be copied\n  ByteOffsetT num_total_bytes = 0;\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    if (input_gen == TestDataGen::CONSECUTIVE)\n    {\n      h_buffer_src_offsets[i] = num_total_bytes;\n    }\n    if (output_gen == TestDataGen::CONSECUTIVE)\n    {\n      h_buffer_dst_offsets[i] = num_total_bytes;\n    }\n    num_total_bytes += h_buffer_sizes[i];\n  }\n\n  // Shuffle input buffer source-offsets\n  std::uint_fast32_t shuffle_seed = 320981U;\n  if (input_gen == TestDataGen::RANDOM)\n  {\n    h_buffer_src_offsets = GetShuffledBufferOffsets<BufferOffsetT, ByteOffsetT>(h_buffer_sizes,\n                                                                                shuffle_seed);\n    shuffle_seed += 42;\n  }\n\n  // Shuffle input buffer source-offsets\n  if (output_gen == TestDataGen::RANDOM)\n  {\n    h_buffer_dst_offsets = GetShuffledBufferOffsets<BufferOffsetT, ByteOffsetT>(h_buffer_sizes,\n                                                                                shuffle_seed);\n  }\n\n  // Get temporary storage requirements\n  CubDebugExit(cub::DeviceMemcpy::Batched(d_temp_storage,\n                                          temp_storage_bytes,\n                                          static_cast<SrcPtrT *>(nullptr),\n                                          static_cast<SrcPtrT *>(nullptr),\n                                          d_buffer_sizes,\n                                          num_buffers));\n\n  // Check if there's sufficient device memory to run this test\n  std::size_t total_required_mem = num_total_bytes +                                 //\n                                   num_total_bytes +                                 //\n                                   (num_buffers * sizeof(d_buffer_src_offsets[0])) + //\n                                   (num_buffers * sizeof(d_buffer_dst_offsets[0])) + //\n                                   (num_buffers * sizeof(d_buffer_sizes[0])) +       //\n                                   temp_storage_bytes;                               //\n  if (TotalGlobalMem() < total_required_mem)\n  {\n    std::cout\n      << \"Skipping the test due to insufficient device memory\\n\"                                  //\n      << \" - Required: \" << total_required_mem << \" B, available: \" << TotalGlobalMem() << \" B\\n\" //\n      << \" - Skipped test instance: \"                                                             //\n      << \" -> Min. buffer size: \" << min_buffer_size << \", max. buffer size: \" << max_buffer_size //\n      << \", num_buffers: \" << num_buffers                                                         //\n      << \", in_gen: \" << ((input_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")            //\n      << \", out_gen: \" << ((output_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\");\n    return;\n  }\n\n  cudaEvent_t events[2];\n  cudaEventCreate(&events[0]);\n  cudaEventCreate(&events[1]);\n\n  cudaStream_t stream;\n  cudaStreamCreate(&stream);\n\n  // Allocate device memory\n  CubDebugExit(cudaMalloc(&d_in, num_total_bytes));\n  CubDebugExit(cudaMalloc(&d_out, num_total_bytes));\n  CubDebugExit(cudaMalloc(&d_buffer_src_offsets, num_buffers * sizeof(d_buffer_src_offsets[0])));\n  CubDebugExit(cudaMalloc(&d_buffer_dst_offsets, num_buffers * sizeof(d_buffer_dst_offsets[0])));\n  CubDebugExit(cudaMalloc(&d_buffer_sizes, num_buffers * sizeof(d_buffer_sizes[0])));\n  CubDebugExit(cudaMalloc(&d_temp_storage, temp_storage_bytes));\n\n  // Populate the data source with random data\n  using RandomInitAliasT         = uint16_t;\n  std::size_t num_aliased_factor = sizeof(RandomInitAliasT) / sizeof(uint8_t);\n  std::size_t num_aliased_units  = CUB_QUOTIENT_CEILING(num_total_bytes, num_aliased_factor);\n  std::unique_ptr<uint8_t[]> h_in(new uint8_t[num_aliased_units * num_aliased_factor]);\n  std::unique_ptr<uint8_t[]> h_out(new uint8_t[num_total_bytes]);\n  std::unique_ptr<uint8_t[]> h_gpu_results(new uint8_t[num_total_bytes]);\n\n  // Generate random offsets into the random-bits data buffer\n  GenerateRandomData(reinterpret_cast<RandomInitAliasT *>(h_in.get()), num_aliased_units);\n\n  // Prepare d_buffer_srcs\n  OffsetToPtrOp<SrcPtrT> src_transform_op{static_cast<SrcPtrT>(d_in)};\n  cub::TransformInputIterator<SrcPtrT, OffsetToPtrOp<SrcPtrT>, ByteOffsetT *> d_buffer_srcs(\n    d_buffer_src_offsets,\n    src_transform_op);\n\n  // Prepare d_buffer_dsts\n  OffsetToPtrOp<SrcPtrT> dst_transform_op{static_cast<SrcPtrT>(d_out)};\n  cub::TransformInputIterator<SrcPtrT, OffsetToPtrOp<SrcPtrT>, ByteOffsetT *> d_buffer_dsts(\n    d_buffer_dst_offsets,\n    dst_transform_op);\n\n  // Prepare random data segment (which serves for the buffer sources)\n  CubDebugExit(cudaMemcpyAsync(d_in, h_in.get(), num_total_bytes, cudaMemcpyHostToDevice, stream));\n\n  // Prepare d_buffer_src_offsets\n  CubDebugExit(cudaMemcpyAsync(d_buffer_src_offsets,\n                               h_buffer_src_offsets.data(),\n                               h_buffer_src_offsets.size() * sizeof(h_buffer_src_offsets[0]),\n                               cudaMemcpyHostToDevice,\n                               stream));\n\n  // Prepare d_buffer_dst_offsets\n  CubDebugExit(cudaMemcpyAsync(d_buffer_dst_offsets,\n                               h_buffer_dst_offsets.data(),\n                               h_buffer_dst_offsets.size() * sizeof(h_buffer_dst_offsets[0]),\n                               cudaMemcpyHostToDevice,\n                               stream));\n\n  // Prepare d_buffer_sizes\n  CubDebugExit(cudaMemcpyAsync(d_buffer_sizes,\n                               h_buffer_sizes.data(),\n                               h_buffer_sizes.size() * sizeof(h_buffer_sizes[0]),\n                               cudaMemcpyHostToDevice,\n                               stream));\n\n  // Record event before algorithm\n  cudaEventRecord(events[0], stream);\n\n  // Invoke device-side algorithm being under test\n  CubDebugExit(cub::DeviceMemcpy::Batched(d_temp_storage,\n                                          temp_storage_bytes,\n                                          d_buffer_srcs,\n                                          d_buffer_dsts,\n                                          d_buffer_sizes,\n                                          num_buffers,\n                                          stream));\n\n  // Record event after algorithm\n  cudaEventRecord(events[1], stream);\n\n  // Copy back the output buffer\n  CubDebugExit(\n    cudaMemcpyAsync(h_gpu_results.get(), d_out, num_total_bytes, cudaMemcpyDeviceToHost, stream));\n\n  // Make sure results have been copied back to the host\n  CubDebugExit(cudaStreamSynchronize(stream));\n\n  // CPU-side result generation for verification\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    std::memcpy(h_out.get() + h_buffer_dst_offsets[i],\n                h_in.get() + h_buffer_src_offsets[i],\n                h_buffer_sizes[i]);\n  }\n\n  float duration = 0;\n  cudaEventElapsedTime(&duration, events[0], events[1]);\n\n#ifdef CUB_TEST_BENCHMARK\n  size_t stats_src_offsets = sizeof(ByteOffsetT) * num_buffers;\n  size_t stats_dst_offsets = sizeof(ByteOffsetT) * num_buffers;\n  size_t stats_sizes       = sizeof(BufferSizeT) * num_buffers;\n  size_t stats_data_copied = 2 * num_total_bytes;\n\n  std::cout\n    << \"Min. buffer size: \" << min_buffer_size << \", max. buffer size: \" << max_buffer_size     //\n    << \", num_buffers: \" << num_buffers                                                         //\n    << \", in_gen: \" << ((input_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")            //\n    << \", out_gen: \" << ((output_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")          //\n    << \", src size: \" << stats_src_offsets << \", dst size: \" << stats_dst_offsets               //\n    << \", sizes size: \" << stats_sizes << \", cpy_data_size: \" << stats_data_copied              //\n    << \", total: \" << (stats_src_offsets + stats_dst_offsets + stats_sizes + stats_data_copied) //\n    << \", duration: \" << duration                                                               //\n    << \", BW: \"\n    << ((double)(stats_src_offsets + stats_dst_offsets + stats_sizes + stats_data_copied) /\n        1000000000.0) /\n         (duration / 1000.0)\n    << \"GB/s \\n\";\n#endif\n\n  for (ByteOffsetT i = 0; i < num_total_bytes; i++)\n  {\n    if (h_gpu_results.get()[i] != h_out.get()[i])\n    {\n      std::cout << \"Mismatch at index \" << i\n                << \", CPU vs. GPU: \" << static_cast<uint16_t>(h_gpu_results.get()[i]) << \", \"\n                << static_cast<uint16_t>(h_out.get()[i]) << \"\\n\";\n    }\n    AssertEquals(h_out.get()[i], h_gpu_results.get()[i]);\n  }\n\n  CubDebugExit(cudaFree(d_in));\n  CubDebugExit(cudaFree(d_out));\n  CubDebugExit(cudaFree(d_buffer_src_offsets));\n  CubDebugExit(cudaFree(d_buffer_dst_offsets));\n  CubDebugExit(cudaFree(d_buffer_sizes));\n  CubDebugExit(cudaFree(d_temp_storage));\n}\n\ntemplate <int LOGICAL_WARP_SIZE, typename VectorT, typename ByteOffsetT>\n__global__ void TestVectorizedCopyKernel(const void *d_in, void *d_out, ByteOffsetT copy_size)\n{\n  cub::detail::VectorizedCopy<LOGICAL_WARP_SIZE, VectorT>(threadIdx.x, d_out, copy_size, d_in);\n}\n\nstruct TupleMemberEqualityOp\n{\n  template <typename T>\n  __host__ __device__ __forceinline__ bool operator()(T tuple)\n  {\n    return thrust::get<0>(tuple) == thrust::get<1>(tuple);\n  }\n};\n\n/**\n * @brief Tests the VectorizedCopy for various aligned and misaligned input and output pointers.\n * @tparam VectorT The vector type used for vectorized stores (i.e., one of uint4, uint2, uint32_t)\n */\ntemplate <typename VectorT>\nvoid TestVectorizedCopy()\n{\n\n  constexpr uint32_t threads_per_block = 8;\n\n  std::vector<std::size_t> in_offsets{0, 1, sizeof(uint32_t) - 1};\n  std::vector<std::size_t> out_offsets{0, 1, sizeof(VectorT) - 1};\n  std::vector<std::size_t> copy_sizes{0,\n                                      1,\n                                      sizeof(uint32_t),\n                                      sizeof(VectorT),\n                                      2 * threads_per_block * sizeof(VectorT)};\n  for (auto copy_sizes_it = std::begin(copy_sizes); copy_sizes_it < std::end(copy_sizes);\n       copy_sizes_it++)\n  {\n    for (auto in_offsets_it = std::begin(in_offsets); in_offsets_it < std::end(in_offsets);\n         in_offsets_it++)\n    {\n      for (auto out_offsets_it = std::begin(out_offsets); out_offsets_it < std::end(out_offsets);\n           out_offsets_it++)\n      {\n        std::size_t in_offset  = *in_offsets_it;\n        std::size_t out_offset = *out_offsets_it;\n        std::size_t copy_size  = *copy_sizes_it;\n\n        // Prepare data\n        const std::size_t alloc_size_in  = in_offset + copy_size;\n        const std::size_t alloc_size_out = out_offset + copy_size;\n        thrust::device_vector<char> data_in(alloc_size_in);\n        thrust::device_vector<char> data_out(alloc_size_out);\n        thrust::sequence(data_in.begin(), data_in.end(), static_cast<char>(0));\n        thrust::fill_n(data_out.begin(), alloc_size_out, static_cast<char>(0x42));\n\n        auto d_in  = thrust::raw_pointer_cast(data_in.data());\n        auto d_out = thrust::raw_pointer_cast(data_out.data());\n\n        TestVectorizedCopyKernel<threads_per_block, VectorT>\n          <<<1, threads_per_block>>>(d_in + in_offset,\n                                     d_out + out_offset,\n                                     static_cast<int>(copy_size));\n        auto zip_it = thrust::make_zip_iterator(data_in.begin() + in_offset,\n                                                data_out.begin() + out_offset);\n\n        bool success = thrust::all_of(zip_it, zip_it + copy_size, TupleMemberEqualityOp{});\n        AssertTrue(success);\n      }\n    }\n  }\n}\n\ntemplate <uint32_t NUM_ITEMS, uint32_t MAX_ITEM_VALUE, bool PREFER_POW2_BITS>\n__global__ void TestBitPackedCounterKernel(uint32_t *bins,\n                                           uint32_t *increments,\n                                           uint32_t *counts_out,\n                                           uint32_t num_items)\n{\n  using BitPackedCounterT =\n    cub::detail::BitPackedCounter<NUM_ITEMS, MAX_ITEM_VALUE, PREFER_POW2_BITS>;\n  BitPackedCounterT counter{};\n  for (uint32_t i = 0; i < num_items; i++)\n  {\n    counter.Add(bins[i], increments[i]);\n  }\n\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    counts_out[i] = counter.Get(i);\n  }\n}\n\n/**\n * @brief Tests BitPackedCounter that's used for computing the histogram of buffer sizes (i.e.,\n * small, medium, large).\n */\ntemplate <uint32_t NUM_ITEMS, uint32_t MAX_ITEM_VALUE>\nvoid TestBitPackedCounter(const std::uint_fast32_t seed = 320981U)\n{\n\n  constexpr uint32_t min_increment = 0;\n  constexpr uint32_t max_increment = 4;\n  constexpr double avg_increment   = static_cast<double>(min_increment) +\n                                   (static_cast<double>(max_increment - min_increment) / 2.0);\n  std::uint32_t num_increments = \n      static_cast<uint32_t>(static_cast<double>(MAX_ITEM_VALUE * NUM_ITEMS) / avg_increment);\n\n  // Test input data\n  std::array<uint64_t, NUM_ITEMS> reference_counters{};\n  thrust::host_vector<uint32_t> h_bins(num_increments);\n  thrust::host_vector<uint32_t> h_increments(num_increments);\n\n  // Generate random test input data\n  GenerateRandomData(thrust::raw_pointer_cast(h_bins.data()),\n                     num_increments,\n                     0U,\n                     NUM_ITEMS - 1U,\n                     seed);\n  GenerateRandomData(thrust::raw_pointer_cast(h_increments.data()),\n                     num_increments,\n                     min_increment,\n                     max_increment,\n                     (seed + 17));\n\n  // Make sure test data does not overflow any of the counters\n  for (std::size_t i = 0; i < num_increments; i++)\n  {\n    // New increment for this bin would overflow => zero this increment\n    if (reference_counters[h_bins[i]] + h_increments[i] >= MAX_ITEM_VALUE)\n    {\n      h_increments[i] = 0;\n    }\n    else\n    {\n      reference_counters[h_bins[i]] += h_increments[i];\n    }\n  }\n\n  // Device memory\n  thrust::device_vector<uint32_t> bins_in(num_increments);\n  thrust::device_vector<uint32_t> increments_in(num_increments);\n  thrust::device_vector<uint32_t> counts_out(NUM_ITEMS);\n\n  // Initialize device-side test data\n  bins_in       = h_bins;\n  increments_in = h_increments;\n\n  // Memory for GPU-generated results\n  thrust::host_vector<uint32_t> host_counts(num_increments);\n\n  // Reset counters to arbitrary random value\n  thrust::fill(counts_out.begin(), counts_out.end(), 814920U);\n\n  // Run tests with densely bit-packed counters\n  TestBitPackedCounterKernel<NUM_ITEMS, MAX_ITEM_VALUE, false>\n    <<<1, 1>>>(thrust::raw_pointer_cast(bins_in.data()),\n               thrust::raw_pointer_cast(increments_in.data()),\n               thrust::raw_pointer_cast(counts_out.data()),\n               num_increments);\n\n  // Result verification\n  host_counts = counts_out;\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    AssertEquals(reference_counters[i], host_counts[i]);\n  }\n\n  // Reset counters to arbitrary random value\n  thrust::fill(counts_out.begin(), counts_out.end(), 814920U);\n\n  // Run tests with bit-packed counters, where bit-count is a power-of-two\n  TestBitPackedCounterKernel<NUM_ITEMS, MAX_ITEM_VALUE, true>\n    <<<1, 1>>>(thrust::raw_pointer_cast(bins_in.data()),\n               thrust::raw_pointer_cast(increments_in.data()),\n               thrust::raw_pointer_cast(counts_out.data()),\n               num_increments);\n\n  // Result verification\n  host_counts = counts_out;\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    AssertEquals(reference_counters[i], host_counts[i]);\n  }\n}\n\nint main(int argc, char **argv)\n{\n  CommandLineArgs args(argc, argv);\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  //---------------------------------------------------------------------\n  // VectorizedCopy tests\n  //---------------------------------------------------------------------\n  TestVectorizedCopy<uint32_t>();\n  TestVectorizedCopy<uint4>();\n\n  //---------------------------------------------------------------------\n  // BitPackedCounter tests\n  //---------------------------------------------------------------------\n  TestBitPackedCounter<1, 1>();\n  TestBitPackedCounter<1, (0x01U << 16)>();\n  TestBitPackedCounter<4, 1>();\n  TestBitPackedCounter<4, 2>();\n  TestBitPackedCounter<4, 255>();\n  TestBitPackedCounter<4, 256>();\n  TestBitPackedCounter<8, 1024>();\n  TestBitPackedCounter<32, 1>();\n  TestBitPackedCounter<32, 256>();\n\n  //---------------------------------------------------------------------\n  // DeviceMemcpy::Batched tests\n  //---------------------------------------------------------------------\n  // The most granular type being copied. Buffer's will be aligned and their size be an integer\n  // multiple of this type\n  using AtomicCopyT = uint8_t;\n\n  // Type used for indexing into the array of buffers\n  using BufferOffsetT = uint32_t;\n\n  // Type used for indexing into individual bytes of a buffer (large enough to cover the max buffer\n  using BufferSizeT = uint32_t;\n\n  // Type used for indexing into bytes over *all* the buffers' sizes\n  using ByteOffsetT = uint32_t;\n\n  // Total number of bytes that are targeted to be copied on each run\n  const BufferOffsetT target_copy_size = 64U << 20;\n\n  // The number of randomly\n  constexpr std::size_t num_rnd_buffer_range_tests = 32;\n\n  // Each buffer's size will be random within this interval\n  std::vector<std::pair<std::size_t, std::size_t>> buffer_size_ranges = {{0, 1},\n                                                                         {1, 2},\n                                                                         {0, 16},\n                                                                         {1, 32},\n                                                                         {1, 1024},\n                                                                         {1, 32 * 1024},\n                                                                         {128 * 1024, 256 * 1024},\n                                                                         {target_copy_size,\n                                                                          target_copy_size}};\n\n  std::mt19937 rng(0);\n  std::uniform_int_distribution<std::size_t> size_dist(1, 1000000);\n  for (std::size_t i = 0; i < num_rnd_buffer_range_tests; i++)\n  {\n    auto range_begin = size_dist(rng);\n    auto range_end   = size_dist(rng);\n    if (range_begin > range_end)\n    {\n      std::swap(range_begin, range_end);\n    }\n    buffer_size_ranges.push_back({range_begin, range_end});\n  }\n\n  for (const auto &buffer_size_range : buffer_size_ranges)\n  {\n    BufferSizeT min_buffer_size =\n      static_cast<BufferSizeT>(CUB_ROUND_UP_NEAREST(buffer_size_range.first, sizeof(AtomicCopyT)));\n    BufferSizeT max_buffer_size =\n      static_cast<BufferSizeT>(CUB_ROUND_UP_NEAREST(buffer_size_range.second,\n                                                    static_cast<BufferSizeT>(sizeof(AtomicCopyT))));\n    double average_buffer_size = (min_buffer_size + max_buffer_size) / 2.0;\n    BufferOffsetT target_num_buffers =\n      static_cast<BufferOffsetT>(target_copy_size / average_buffer_size);\n\n    // Run tests with input buffer being consecutive and output buffers being consecutive\n    RunTest<AtomicCopyT, BufferOffsetT, BufferSizeT, ByteOffsetT>(target_num_buffers,\n                                                                  min_buffer_size,\n                                                                  max_buffer_size,\n                                                                  TestDataGen::CONSECUTIVE,\n                                                                  TestDataGen::CONSECUTIVE);\n\n    // Run tests with input buffer being randomly shuffled and output buffers being randomly\n    // shuffled\n    RunTest<AtomicCopyT, BufferOffsetT, BufferSizeT, ByteOffsetT>(target_num_buffers,\n                                                                  min_buffer_size,\n                                                                  max_buffer_size,\n                                                                  TestDataGen::RANDOM,\n                                                                  TestDataGen::RANDOM);\n  }\n\n  //---------------------------------------------------------------------\n  // DeviceMemcpy::Batched test with 64-bit offsets\n  //---------------------------------------------------------------------\n  using ByteOffset64T = uint64_t;\n  using BufferSize64T = uint64_t;\n  ByteOffset64T large_target_copy_size =\n    static_cast<ByteOffset64T>(std::numeric_limits<uint32_t>::max()) + (128ULL * 1024ULL * 1024ULL);\n  // Make sure min_buffer_size is in fact smaller than max buffer size\n  constexpr BufferOffsetT single_buffer = 1;\n\n  // Run tests with input buffer being consecutive and output buffers being consecutive\n  RunTest<AtomicCopyT, BufferOffsetT, BufferSize64T, ByteOffset64T>(single_buffer,\n                                                                    large_target_copy_size,\n                                                                    large_target_copy_size,\n                                                                    TestDataGen::CONSECUTIVE,\n                                                                    TestDataGen::CONSECUTIVE);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/device/device_memcpy.cuh>\n#include <cub/iterator/transform_input_iterator.cuh>\n#include <cub/util_ptx.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/fill.h>\n#include <thrust/host_vector.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/logical.h>\n#include <thrust/sequence.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <limits>\n#include <numeric>\n#include <random>\n#include <type_traits>\n#include <vector>\n\n#include \"test_util.h\"\n\n/**\n * @brief Host-side random data generation\n */\ntemplate <typename T>\nvoid GenerateRandomData(\n  T *rand_out,\n  const std::size_t num_items,\n  const T min_rand_val          = std::numeric_limits<T>::min(),\n  const T max_rand_val          = std::numeric_limits<T>::max(),\n  const std::uint_fast32_t seed = 320981U,\n  typename std::enable_if<std::is_integral<T>::value && (sizeof(T) >= 2)>::type * = nullptr)\n{\n  // initialize random number generator\n  std::mt19937 rng(seed);\n  std::uniform_int_distribution<T> uni_dist(min_rand_val, max_rand_val);\n\n  // generate random numbers\n  for (std::size_t i = 0; i < num_items; ++i)\n  {\n    rand_out[i] = uni_dist(rng);\n  }\n}\n\ntemplate <typename InputBufferIt,\n          typename OutputBufferIt,\n          typename BufferSizeIteratorT,\n          typename BufferOffsetT>\nvoid __global__ BaselineBatchMemCpyKernel(InputBufferIt input_buffer_it,\n                                          OutputBufferIt output_buffer_it,\n                                          BufferSizeIteratorT buffer_sizes,\n                                          BufferOffsetT num_buffers)\n{\n  BufferOffsetT gtid = blockDim.x * blockIdx.x + threadIdx.x;\n  if (gtid >= num_buffers)\n  {\n    return;\n  }\n  for (BufferOffsetT i = 0; i < buffer_sizes[gtid]; i++)\n  {\n    reinterpret_cast<uint8_t *>(output_buffer_it[gtid])[i] =\n      reinterpret_cast<uint8_t *>(input_buffer_it[gtid])[i];\n  }\n}\n\ntemplate <typename InputBufferIt, typename OutputBufferIt, typename BufferSizeIteratorT>\nvoid InvokeBaselineBatchMemcpy(InputBufferIt input_buffer_it,\n                               OutputBufferIt output_buffer_it,\n                               BufferSizeIteratorT buffer_sizes,\n                               uint32_t num_buffers)\n{\n  constexpr uint32_t block_threads = 128U;\n  uint32_t num_blocks              = (num_buffers + block_threads - 1) / block_threads;\n  BaselineBatchMemCpyKernel<<<num_blocks, block_threads>>>(input_buffer_it,\n                                                           output_buffer_it,\n                                                           buffer_sizes,\n                                                           num_buffers);\n}\n\ntemplate <typename InputBufferIt,\n          typename OutputBufferIt,\n          typename BufferSizeIteratorT,\n          typename BufferOffsetT>\nvoid __global__ BaselineBatchMemCpyPerBlockKernel(InputBufferIt input_buffer_it,\n                                                  OutputBufferIt output_buffer_it,\n                                                  BufferSizeIteratorT buffer_sizes,\n                                                  BufferOffsetT num_buffers)\n{\n  BufferOffsetT gbid = blockIdx.x;\n  if (gbid >= num_buffers)\n  {\n    return;\n  }\n  for (BufferOffsetT i = threadIdx.x; i < buffer_sizes[gbid] / 8; i += blockDim.x)\n  {\n    reinterpret_cast<uint64_t *>(output_buffer_it[gbid])[i] =\n      reinterpret_cast<uint64_t *>(input_buffer_it[gbid])[i];\n  }\n}\n\n/**\n * @brief Used for generating a shuffled but cohesive sequence of output-buffer offsets for the\n * sequence of input-buffers.\n */\ntemplate <typename BufferOffsetT, typename ByteOffsetT, typename BufferSizeT>\nstd::vector<ByteOffsetT> GetShuffledBufferOffsets(const std::vector<BufferSizeT> &buffer_sizes,\n                                                  const std::uint_fast32_t seed = 320981U)\n{\n  BufferOffsetT num_buffers = static_cast<BufferOffsetT>(buffer_sizes.size());\n\n  // We're remapping the i-th buffer to pmt_idxs[i]\n  std::mt19937 rng(seed);\n  std::vector<BufferOffsetT> pmt_idxs(num_buffers);\n  std::iota(pmt_idxs.begin(), pmt_idxs.end(), static_cast<BufferOffsetT>(0));\n  std::shuffle(std::begin(pmt_idxs), std::end(pmt_idxs), rng);\n\n  // Compute the offsets using the new mapping\n  ByteOffsetT running_offset = {};\n  std::vector<ByteOffsetT> permuted_offsets;\n  permuted_offsets.reserve(num_buffers);\n  for (auto permuted_buffer_idx : pmt_idxs)\n  {\n    permuted_offsets.emplace_back(running_offset);\n    running_offset += buffer_sizes[permuted_buffer_idx];\n  }\n\n  // Generate the scatter indexes that identify where each buffer was mapped to\n  std::vector<BufferOffsetT> scatter_idxs(num_buffers);\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    scatter_idxs[pmt_idxs[i]] = i;\n  }\n\n  std::vector<ByteOffsetT> new_offsets(num_buffers);\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    new_offsets[i] = permuted_offsets[scatter_idxs[i]];\n  }\n\n  return new_offsets;\n}\n\n/**\n * @brief Function object class template that takes an offset and returns an iterator at the given\n * offset relative to a fixed base iterator.\n *\n * @tparam IteratorT The random-access iterator type to be returned\n */\ntemplate <typename IteratorT>\nstruct OffsetToPtrOp\n{\n  template <typename T>\n  __host__ __device__ __forceinline__ IteratorT operator()(T offset) const\n  {\n    return base_it + offset;\n  }\n  IteratorT base_it;\n};\n\nenum class TestDataGen\n{\n  // Random offsets into a data segment\n  RANDOM,\n\n  // Buffers cohesively reside next to each other\n  CONSECUTIVE\n};\n\n/**\n * @brief\n *\n * @tparam AtomicT The most granular type being copied. All source and destination pointers will be\n * aligned based on this type, the number of bytes being copied will be an integer multiple of this\n * type's size\n * @tparam BufferOffsetT Type used for indexing into the array of buffers\n * @tparam BufferSizeT Type used for indexing into individual bytes of a buffer (large enough to\n * cover the max buffer size)\n * @tparam ByteOffsetT Type used for indexing into bytes over *all* the buffers' sizes\n */\ntemplate <typename AtomicT, typename BufferOffsetT, typename BufferSizeT, typename ByteOffsetT>\nvoid RunTest(BufferOffsetT num_buffers,\n             BufferSizeT min_buffer_size,\n             BufferSizeT max_buffer_size,\n             TestDataGen input_gen,\n             TestDataGen output_gen)\n{\n  using SrcPtrT = uint8_t *;\n\n  // Buffer segment data (their offsets and sizes)\n  std::vector<BufferSizeT> h_buffer_sizes(num_buffers);\n  std::vector<ByteOffsetT> h_buffer_src_offsets(num_buffers);\n  std::vector<ByteOffsetT> h_buffer_dst_offsets(num_buffers);\n\n  // Device-side resources\n  void *d_in                        = nullptr;\n  void *d_out                       = nullptr;\n  ByteOffsetT *d_buffer_src_offsets = nullptr;\n  ByteOffsetT *d_buffer_dst_offsets = nullptr;\n  BufferSizeT *d_buffer_sizes       = nullptr;\n  void *d_temp_storage              = nullptr;\n  size_t temp_storage_bytes         = 0;\n\n  // Generate the buffer sizes\n  GenerateRandomData(h_buffer_sizes.data(), h_buffer_sizes.size(), min_buffer_size, max_buffer_size);\n\n  // Make sure buffer sizes are a multiple of the most granular unit (one AtomicT) being copied\n  // (round down)\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    h_buffer_sizes[i] = (h_buffer_sizes[i] / sizeof(AtomicT)) * sizeof(AtomicT);\n  }\n\n  // Compute the total bytes to be copied\n  ByteOffsetT num_total_bytes = 0;\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    if (input_gen == TestDataGen::CONSECUTIVE)\n    {\n      h_buffer_src_offsets[i] = num_total_bytes;\n    }\n    if (output_gen == TestDataGen::CONSECUTIVE)\n    {\n      h_buffer_dst_offsets[i] = num_total_bytes;\n    }\n    num_total_bytes += h_buffer_sizes[i];\n  }\n\n  // Shuffle input buffer source-offsets\n  std::uint_fast32_t shuffle_seed = 320981U;\n  if (input_gen == TestDataGen::RANDOM)\n  {\n    h_buffer_src_offsets = GetShuffledBufferOffsets<BufferOffsetT, ByteOffsetT>(h_buffer_sizes,\n                                                                                shuffle_seed);\n    shuffle_seed += 42;\n  }\n\n  // Shuffle input buffer source-offsets\n  if (output_gen == TestDataGen::RANDOM)\n  {\n    h_buffer_dst_offsets = GetShuffledBufferOffsets<BufferOffsetT, ByteOffsetT>(h_buffer_sizes,\n                                                                                shuffle_seed);\n  }\n\n  // Get temporary storage requirements\n  CubDebugExit(hipcub::DeviceMemcpy::Batched(d_temp_storage,\n                                          temp_storage_bytes,\n                                          static_cast<SrcPtrT *>(nullptr),\n                                          static_cast<SrcPtrT *>(nullptr),\n                                          d_buffer_sizes,\n                                          num_buffers));\n\n  // Check if there's sufficient device memory to run this test\n  std::size_t total_required_mem = num_total_bytes +                                 //\n                                   num_total_bytes +                                 //\n                                   (num_buffers * sizeof(d_buffer_src_offsets[0])) + //\n                                   (num_buffers * sizeof(d_buffer_dst_offsets[0])) + //\n                                   (num_buffers * sizeof(d_buffer_sizes[0])) +       //\n                                   temp_storage_bytes;                               //\n  if (TotalGlobalMem() < total_required_mem)\n  {\n    std::cout\n      << \"Skipping the test due to insufficient device memory\\n\"                                  //\n      << \" - Required: \" << total_required_mem << \" B, available: \" << TotalGlobalMem() << \" B\\n\" //\n      << \" - Skipped test instance: \"                                                             //\n      << \" -> Min. buffer size: \" << min_buffer_size << \", max. buffer size: \" << max_buffer_size //\n      << \", num_buffers: \" << num_buffers                                                         //\n      << \", in_gen: \" << ((input_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")            //\n      << \", out_gen: \" << ((output_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\");\n    return;\n  }\n\n  hipEvent_t events[2];\n  hipEventCreate(&events[0]);\n  hipEventCreate(&events[1]);\n\n  hipStream_t stream;\n  hipStreamCreate(&stream);\n\n  // Allocate device memory\n  CubDebugExit(hipMalloc(&d_in, num_total_bytes));\n  CubDebugExit(hipMalloc(&d_out, num_total_bytes));\n  CubDebugExit(hipMalloc(&d_buffer_src_offsets, num_buffers * sizeof(d_buffer_src_offsets[0])));\n  CubDebugExit(hipMalloc(&d_buffer_dst_offsets, num_buffers * sizeof(d_buffer_dst_offsets[0])));\n  CubDebugExit(hipMalloc(&d_buffer_sizes, num_buffers * sizeof(d_buffer_sizes[0])));\n  CubDebugExit(hipMalloc(&d_temp_storage, temp_storage_bytes));\n\n  // Populate the data source with random data\n  using RandomInitAliasT         = uint16_t;\n  std::size_t num_aliased_factor = sizeof(RandomInitAliasT) / sizeof(uint8_t);\n  std::size_t num_aliased_units  = CUB_QUOTIENT_CEILING(num_total_bytes, num_aliased_factor);\n  std::unique_ptr<uint8_t[]> h_in(new uint8_t[num_aliased_units * num_aliased_factor]);\n  std::unique_ptr<uint8_t[]> h_out(new uint8_t[num_total_bytes]);\n  std::unique_ptr<uint8_t[]> h_gpu_results(new uint8_t[num_total_bytes]);\n\n  // Generate random offsets into the random-bits data buffer\n  GenerateRandomData(reinterpret_cast<RandomInitAliasT *>(h_in.get()), num_aliased_units);\n\n  // Prepare d_buffer_srcs\n  OffsetToPtrOp<SrcPtrT> src_transform_op{static_cast<SrcPtrT>(d_in)};\n  hipcub::TransformInputIterator<SrcPtrT, OffsetToPtrOp<SrcPtrT>, ByteOffsetT *> d_buffer_srcs(\n    d_buffer_src_offsets,\n    src_transform_op);\n\n  // Prepare d_buffer_dsts\n  OffsetToPtrOp<SrcPtrT> dst_transform_op{static_cast<SrcPtrT>(d_out)};\n  hipcub::TransformInputIterator<SrcPtrT, OffsetToPtrOp<SrcPtrT>, ByteOffsetT *> d_buffer_dsts(\n    d_buffer_dst_offsets,\n    dst_transform_op);\n\n  // Prepare random data segment (which serves for the buffer sources)\n  CubDebugExit(hipMemcpyAsync(d_in, h_in.get(), num_total_bytes, hipMemcpyHostToDevice, stream));\n\n  // Prepare d_buffer_src_offsets\n  CubDebugExit(hipMemcpyAsync(d_buffer_src_offsets,\n                               h_buffer_src_offsets.data(),\n                               h_buffer_src_offsets.size() * sizeof(h_buffer_src_offsets[0]),\n                               hipMemcpyHostToDevice,\n                               stream));\n\n  // Prepare d_buffer_dst_offsets\n  CubDebugExit(hipMemcpyAsync(d_buffer_dst_offsets,\n                               h_buffer_dst_offsets.data(),\n                               h_buffer_dst_offsets.size() * sizeof(h_buffer_dst_offsets[0]),\n                               hipMemcpyHostToDevice,\n                               stream));\n\n  // Prepare d_buffer_sizes\n  CubDebugExit(hipMemcpyAsync(d_buffer_sizes,\n                               h_buffer_sizes.data(),\n                               h_buffer_sizes.size() * sizeof(h_buffer_sizes[0]),\n                               hipMemcpyHostToDevice,\n                               stream));\n\n  // Record event before algorithm\n  hipEventRecord(events[0], stream);\n\n  // Invoke device-side algorithm being under test\n  CubDebugExit(hipcub::DeviceMemcpy::Batched(d_temp_storage,\n                                          temp_storage_bytes,\n                                          d_buffer_srcs,\n                                          d_buffer_dsts,\n                                          d_buffer_sizes,\n                                          num_buffers,\n                                          stream));\n\n  // Record event after algorithm\n  hipEventRecord(events[1], stream);\n\n  // Copy back the output buffer\n  CubDebugExit(\n    hipMemcpyAsync(h_gpu_results.get(), d_out, num_total_bytes, hipMemcpyDeviceToHost, stream));\n\n  // Make sure results have been copied back to the host\n  CubDebugExit(hipStreamSynchronize(stream));\n\n  // CPU-side result generation for verification\n  for (BufferOffsetT i = 0; i < num_buffers; i++)\n  {\n    std::memcpy(h_out.get() + h_buffer_dst_offsets[i],\n                h_in.get() + h_buffer_src_offsets[i],\n                h_buffer_sizes[i]);\n  }\n\n  float duration = 0;\n  hipEventElapsedTime(&duration, events[0], events[1]);\n\n#ifdef CUB_TEST_BENCHMARK\n  size_t stats_src_offsets = sizeof(ByteOffsetT) * num_buffers;\n  size_t stats_dst_offsets = sizeof(ByteOffsetT) * num_buffers;\n  size_t stats_sizes       = sizeof(BufferSizeT) * num_buffers;\n  size_t stats_data_copied = 2 * num_total_bytes;\n\n  std::cout\n    << \"Min. buffer size: \" << min_buffer_size << \", max. buffer size: \" << max_buffer_size     //\n    << \", num_buffers: \" << num_buffers                                                         //\n    << \", in_gen: \" << ((input_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")            //\n    << \", out_gen: \" << ((output_gen == TestDataGen::RANDOM) ? \"SHFL\" : \"CONSECUTIVE\")          //\n    << \", src size: \" << stats_src_offsets << \", dst size: \" << stats_dst_offsets               //\n    << \", sizes size: \" << stats_sizes << \", cpy_data_size: \" << stats_data_copied              //\n    << \", total: \" << (stats_src_offsets + stats_dst_offsets + stats_sizes + stats_data_copied) //\n    << \", duration: \" << duration                                                               //\n    << \", BW: \"\n    << ((double)(stats_src_offsets + stats_dst_offsets + stats_sizes + stats_data_copied) /\n        1000000000.0) /\n         (duration / 1000.0)\n    << \"GB/s \\n\";\n#endif\n\n  for (ByteOffsetT i = 0; i < num_total_bytes; i++)\n  {\n    if (h_gpu_results.get()[i] != h_out.get()[i])\n    {\n      std::cout << \"Mismatch at index \" << i\n                << \", CPU vs. GPU: \" << static_cast<uint16_t>(h_gpu_results.get()[i]) << \", \"\n                << static_cast<uint16_t>(h_out.get()[i]) << \"\\n\";\n    }\n    AssertEquals(h_out.get()[i], h_gpu_results.get()[i]);\n  }\n\n  CubDebugExit(hipFree(d_in));\n  CubDebugExit(hipFree(d_out));\n  CubDebugExit(hipFree(d_buffer_src_offsets));\n  CubDebugExit(hipFree(d_buffer_dst_offsets));\n  CubDebugExit(hipFree(d_buffer_sizes));\n  CubDebugExit(hipFree(d_temp_storage));\n}\n\ntemplate <int LOGICAL_WARP_SIZE, typename VectorT, typename ByteOffsetT>\n__global__ void TestVectorizedCopyKernel(const void *d_in, void *d_out, ByteOffsetT copy_size)\n{\n  hipcub::detail::VectorizedCopy<LOGICAL_WARP_SIZE, VectorT>(threadIdx.x, d_out, copy_size, d_in);\n}\n\nstruct TupleMemberEqualityOp\n{\n  template <typename T>\n  __host__ __device__ __forceinline__ bool operator()(T tuple)\n  {\n    return thrust::get<0>(tuple) == thrust::get<1>(tuple);\n  }\n};\n\n/**\n * @brief Tests the VectorizedCopy for various aligned and misaligned input and output pointers.\n * @tparam VectorT The vector type used for vectorized stores (i.e., one of uint4, uint2, uint32_t)\n */\ntemplate <typename VectorT>\nvoid TestVectorizedCopy()\n{\n\n  constexpr uint32_t threads_per_block = 8;\n\n  std::vector<std::size_t> in_offsets{0, 1, sizeof(uint32_t) - 1};\n  std::vector<std::size_t> out_offsets{0, 1, sizeof(VectorT) - 1};\n  std::vector<std::size_t> copy_sizes{0,\n                                      1,\n                                      sizeof(uint32_t),\n                                      sizeof(VectorT),\n                                      2 * threads_per_block * sizeof(VectorT)};\n  for (auto copy_sizes_it = std::begin(copy_sizes); copy_sizes_it < std::end(copy_sizes);\n       copy_sizes_it++)\n  {\n    for (auto in_offsets_it = std::begin(in_offsets); in_offsets_it < std::end(in_offsets);\n         in_offsets_it++)\n    {\n      for (auto out_offsets_it = std::begin(out_offsets); out_offsets_it < std::end(out_offsets);\n           out_offsets_it++)\n      {\n        std::size_t in_offset  = *in_offsets_it;\n        std::size_t out_offset = *out_offsets_it;\n        std::size_t copy_size  = *copy_sizes_it;\n\n        // Prepare data\n        const std::size_t alloc_size_in  = in_offset + copy_size;\n        const std::size_t alloc_size_out = out_offset + copy_size;\n        thrust::device_vector<char> data_in(alloc_size_in);\n        thrust::device_vector<char> data_out(alloc_size_out);\n        thrust::sequence(data_in.begin(), data_in.end(), static_cast<char>(0));\n        thrust::fill_n(data_out.begin(), alloc_size_out, static_cast<char>(0x42));\n\n        auto d_in  = thrust::raw_pointer_cast(data_in.data());\n        auto d_out = thrust::raw_pointer_cast(data_out.data());\n\n        TestVectorizedCopyKernel<threads_per_block, VectorT>\n          <<<1, threads_per_block>>>(d_in + in_offset,\n                                     d_out + out_offset,\n                                     static_cast<int>(copy_size));\n        auto zip_it = thrust::make_zip_iterator(data_in.begin() + in_offset,\n                                                data_out.begin() + out_offset);\n\n        bool success = thrust::all_of(zip_it, zip_it + copy_size, TupleMemberEqualityOp{});\n        AssertTrue(success);\n      }\n    }\n  }\n}\n\ntemplate <uint32_t NUM_ITEMS, uint32_t MAX_ITEM_VALUE, bool PREFER_POW2_BITS>\n__global__ void TestBitPackedCounterKernel(uint32_t *bins,\n                                           uint32_t *increments,\n                                           uint32_t *counts_out,\n                                           uint32_t num_items)\n{\n  using BitPackedCounterT =\n    hipcub::detail::BitPackedCounter<NUM_ITEMS, MAX_ITEM_VALUE, PREFER_POW2_BITS>;\n  BitPackedCounterT counter{};\n  for (uint32_t i = 0; i < num_items; i++)\n  {\n    counter.Add(bins[i], increments[i]);\n  }\n\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    counts_out[i] = counter.Get(i);\n  }\n}\n\n/**\n * @brief Tests BitPackedCounter that's used for computing the histogram of buffer sizes (i.e.,\n * small, medium, large).\n */\ntemplate <uint32_t NUM_ITEMS, uint32_t MAX_ITEM_VALUE>\nvoid TestBitPackedCounter(const std::uint_fast32_t seed = 320981U)\n{\n\n  constexpr uint32_t min_increment = 0;\n  constexpr uint32_t max_increment = 4;\n  constexpr double avg_increment   = static_cast<double>(min_increment) +\n                                   (static_cast<double>(max_increment - min_increment) / 2.0);\n  std::uint32_t num_increments = \n      static_cast<uint32_t>(static_cast<double>(MAX_ITEM_VALUE * NUM_ITEMS) / avg_increment);\n\n  // Test input data\n  std::array<uint64_t, NUM_ITEMS> reference_counters{};\n  thrust::host_vector<uint32_t> h_bins(num_increments);\n  thrust::host_vector<uint32_t> h_increments(num_increments);\n\n  // Generate random test input data\n  GenerateRandomData(thrust::raw_pointer_cast(h_bins.data()),\n                     num_increments,\n                     0U,\n                     NUM_ITEMS - 1U,\n                     seed);\n  GenerateRandomData(thrust::raw_pointer_cast(h_increments.data()),\n                     num_increments,\n                     min_increment,\n                     max_increment,\n                     (seed + 17));\n\n  // Make sure test data does not overflow any of the counters\n  for (std::size_t i = 0; i < num_increments; i++)\n  {\n    // New increment for this bin would overflow => zero this increment\n    if (reference_counters[h_bins[i]] + h_increments[i] >= MAX_ITEM_VALUE)\n    {\n      h_increments[i] = 0;\n    }\n    else\n    {\n      reference_counters[h_bins[i]] += h_increments[i];\n    }\n  }\n\n  // Device memory\n  thrust::device_vector<uint32_t> bins_in(num_increments);\n  thrust::device_vector<uint32_t> increments_in(num_increments);\n  thrust::device_vector<uint32_t> counts_out(NUM_ITEMS);\n\n  // Initialize device-side test data\n  bins_in       = h_bins;\n  increments_in = h_increments;\n\n  // Memory for GPU-generated results\n  thrust::host_vector<uint32_t> host_counts(num_increments);\n\n  // Reset counters to arbitrary random value\n  thrust::fill(counts_out.begin(), counts_out.end(), 814920U);\n\n  // Run tests with densely bit-packed counters\n  TestBitPackedCounterKernel<NUM_ITEMS, MAX_ITEM_VALUE, false>\n    <<<1, 1>>>(thrust::raw_pointer_cast(bins_in.data()),\n               thrust::raw_pointer_cast(increments_in.data()),\n               thrust::raw_pointer_cast(counts_out.data()),\n               num_increments);\n\n  // Result verification\n  host_counts = counts_out;\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    AssertEquals(reference_counters[i], host_counts[i]);\n  }\n\n  // Reset counters to arbitrary random value\n  thrust::fill(counts_out.begin(), counts_out.end(), 814920U);\n\n  // Run tests with bit-packed counters, where bit-count is a power-of-two\n  TestBitPackedCounterKernel<NUM_ITEMS, MAX_ITEM_VALUE, true>\n    <<<1, 1>>>(thrust::raw_pointer_cast(bins_in.data()),\n               thrust::raw_pointer_cast(increments_in.data()),\n               thrust::raw_pointer_cast(counts_out.data()),\n               num_increments);\n\n  // Result verification\n  host_counts = counts_out;\n  for (uint32_t i = 0; i < NUM_ITEMS; i++)\n  {\n    AssertEquals(reference_counters[i], host_counts[i]);\n  }\n}\n\nint main(int argc, char **argv)\n{\n  CommandLineArgs args(argc, argv);\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  //---------------------------------------------------------------------\n  // VectorizedCopy tests\n  //---------------------------------------------------------------------\n  TestVectorizedCopy<uint32_t>();\n  TestVectorizedCopy<uint4>();\n\n  //---------------------------------------------------------------------\n  // BitPackedCounter tests\n  //---------------------------------------------------------------------\n  TestBitPackedCounter<1, 1>();\n  TestBitPackedCounter<1, (0x01U << 16)>();\n  TestBitPackedCounter<4, 1>();\n  TestBitPackedCounter<4, 2>();\n  TestBitPackedCounter<4, 255>();\n  TestBitPackedCounter<4, 256>();\n  TestBitPackedCounter<8, 1024>();\n  TestBitPackedCounter<32, 1>();\n  TestBitPackedCounter<32, 256>();\n\n  //---------------------------------------------------------------------\n  // DeviceMemcpy::Batched tests\n  //---------------------------------------------------------------------\n  // The most granular type being copied. Buffer's will be aligned and their size be an integer\n  // multiple of this type\n  using AtomicCopyT = uint8_t;\n\n  // Type used for indexing into the array of buffers\n  using BufferOffsetT = uint32_t;\n\n  // Type used for indexing into individual bytes of a buffer (large enough to cover the max buffer\n  using BufferSizeT = uint32_t;\n\n  // Type used for indexing into bytes over *all* the buffers' sizes\n  using ByteOffsetT = uint32_t;\n\n  // Total number of bytes that are targeted to be copied on each run\n  const BufferOffsetT target_copy_size = 64U << 20;\n\n  // The number of randomly\n  constexpr std::size_t num_rnd_buffer_range_tests = 32;\n\n  // Each buffer's size will be random within this interval\n  std::vector<std::pair<std::size_t, std::size_t>> buffer_size_ranges = {{0, 1},\n                                                                         {1, 2},\n                                                                         {0, 16},\n                                                                         {1, 32},\n                                                                         {1, 1024},\n                                                                         {1, 32 * 1024},\n                                                                         {128 * 1024, 256 * 1024},\n                                                                         {target_copy_size,\n                                                                          target_copy_size}};\n\n  std::mt19937 rng(0);\n  std::uniform_int_distribution<std::size_t> size_dist(1, 1000000);\n  for (std::size_t i = 0; i < num_rnd_buffer_range_tests; i++)\n  {\n    auto range_begin = size_dist(rng);\n    auto range_end   = size_dist(rng);\n    if (range_begin > range_end)\n    {\n      std::swap(range_begin, range_end);\n    }\n    buffer_size_ranges.push_back({range_begin, range_end});\n  }\n\n  for (const auto &buffer_size_range : buffer_size_ranges)\n  {\n    BufferSizeT min_buffer_size =\n      static_cast<BufferSizeT>(CUB_ROUND_UP_NEAREST(buffer_size_range.first, sizeof(AtomicCopyT)));\n    BufferSizeT max_buffer_size =\n      static_cast<BufferSizeT>(CUB_ROUND_UP_NEAREST(buffer_size_range.second,\n                                                    static_cast<BufferSizeT>(sizeof(AtomicCopyT))));\n    double average_buffer_size = (min_buffer_size + max_buffer_size) / 2.0;\n    BufferOffsetT target_num_buffers =\n      static_cast<BufferOffsetT>(target_copy_size / average_buffer_size);\n\n    // Run tests with input buffer being consecutive and output buffers being consecutive\n    RunTest<AtomicCopyT, BufferOffsetT, BufferSizeT, ByteOffsetT>(target_num_buffers,\n                                                                  min_buffer_size,\n                                                                  max_buffer_size,\n                                                                  TestDataGen::CONSECUTIVE,\n                                                                  TestDataGen::CONSECUTIVE);\n\n    // Run tests with input buffer being randomly shuffled and output buffers being randomly\n    // shuffled\n    RunTest<AtomicCopyT, BufferOffsetT, BufferSizeT, ByteOffsetT>(target_num_buffers,\n                                                                  min_buffer_size,\n                                                                  max_buffer_size,\n                                                                  TestDataGen::RANDOM,\n                                                                  TestDataGen::RANDOM);\n  }\n\n  //---------------------------------------------------------------------\n  // DeviceMemcpy::Batched test with 64-bit offsets\n  //---------------------------------------------------------------------\n  using ByteOffset64T = uint64_t;\n  using BufferSize64T = uint64_t;\n  ByteOffset64T large_target_copy_size =\n    static_cast<ByteOffset64T>(std::numeric_limits<uint32_t>::max()) + (128ULL * 1024ULL * 1024ULL);\n  // Make sure min_buffer_size is in fact smaller than max buffer size\n  constexpr BufferOffsetT single_buffer = 1;\n\n  // Run tests with input buffer being consecutive and output buffers being consecutive\n  RunTest<AtomicCopyT, BufferOffsetT, BufferSize64T, ByteOffset64T>(single_buffer,\n                                                                    large_target_copy_size,\n                                                                    large_target_copy_size,\n                                                                    TestDataGen::CONSECUTIVE,\n                                                                    TestDataGen::CONSECUTIVE);\n}\n", "source": "/kaggle/working/cub/test/test_device_batch_memcpy.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of iterator utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n// This file tests deprecated CUB APIs. Silence deprecation warnings:\n#define CUB_IGNORE_DEPRECATED_API\n\n#include <cub/iterator/tex_ref_input_iterator.cuh>\n#include <cub/util_type.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <iterator>\n#include <cstdio>\n#include <typeinfo>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose = false;\nCachingDeviceAllocator  g_allocator(true);\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Test random access input iterator\n */\ntemplate <\n    typename InputIteratorT,\n    typename T>\n__global__ void Kernel(\n    InputIteratorT    d_in,\n    T                 *d_out,\n    InputIteratorT    *d_itrs)\n{\n    d_out[0] = *d_in;               // Value at offset 0\n    d_out[1] = d_in[100];           // Value at offset 100\n    d_out[2] = *(d_in + 1000);      // Value at offset 1000\n    d_out[3] = *(d_in + 10000);     // Value at offset 10000\n\n    d_in++;\n    d_out[4] = d_in[0];             // Value at offset 1\n\n    d_in += 20;\n    d_out[5] = d_in[0];             // Value at offset 21\n    d_itrs[0] = d_in;               // Iterator at offset 21\n\n    d_in -= 10;\n    d_out[6] = d_in[0];             // Value at offset 11;\n\n    d_in -= 11;\n    d_out[7] = d_in[0];             // Value at offset 0\n    d_itrs[1] = d_in;               // Iterator at offset 0\n}\n\n\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n\n/**\n * Run iterator test on device\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T,\n    int             TEST_VALUES>\nvoid Test(\n    InputIteratorT  d_in,\n    T               (&h_reference)[TEST_VALUES])\n{\n    // Allocate device arrays\n    T                 *d_out    = NULL;\n    InputIteratorT    *d_itrs   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out,     sizeof(T) * TEST_VALUES));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_itrs,    sizeof(InputIteratorT) * 2));\n\n    int compare;\n\n    // Run unguarded kernel\n    Kernel<<<1, 1>>>(d_in, d_out, d_itrs);\n\n    CubDebugExit(cudaPeekAtLastError());\n    CubDebugExit(cudaDeviceSynchronize());\n\n    // Check results\n    compare = CompareDeviceResults(h_reference, d_out, TEST_VALUES, g_verbose, g_verbose);\n    printf(\"\\tValues: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 21\n    InputIteratorT h_itr = d_in + 21;\n    compare = CompareDeviceResults(&h_itr, d_itrs, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 0\n    compare = CompareDeviceResults(&d_in, d_itrs + 1, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Cleanup\n    if (d_out)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_out));\n    }\n    if (d_itrs)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_itrs));\n    }\n}\n\n/**\n * Test tex-ref texture iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexRef()\n{\n    printf(\"\\nTesting tex-ref iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES                   = 11000;\n    constexpr unsigned int DUMMY_OFFSET         = 500;\n    constexpr unsigned int DUMMY_TEST_VALUES    = TEST_VALUES - DUMMY_OFFSET;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data   = NULL;\n    T *d_dummy  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, cudaMemcpyHostToDevice));\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n    CubDebugExit(cudaMemcpy(d_dummy, h_data + DUMMY_OFFSET, sizeof(T) * DUMMY_TEST_VALUES, cudaMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    // Create and bind ref-based test iterator\n    TexRefInputIterator<T, __LINE__> d_ref_itr;\n    CubDebugExit(d_ref_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    // Create and bind dummy iterator of same type to check with interferance\n    TexRefInputIterator<T, __LINE__> d_ref_itr2;\n    CubDebugExit(d_ref_itr2.BindTexture((CastT*) d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n\n    Test(d_ref_itr, h_reference);\n\n    CubDebugExit(d_ref_itr.UnbindTexture());\n    CubDebugExit(d_ref_itr2.UnbindTexture());\n\n    if (h_data)\n    {\n        delete[] h_data;\n    }\n    if (d_data)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_data));\n    }\n    if (d_dummy)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_dummy));\n    }\n}\n\n/**\n * Run non-integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test()\n{\n    TestTexRef<T, CastT>();\n}\n\n/**\n * Run tests\n */\ntemplate <typename T>\nvoid Test()\n{\n    // Test non-const type\n    Test<T, T>();\n\n    // Test non-const type\n    Test<T, const T>();\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Evaluate different data types\n    Test<signed char>();\n    Test<short>();\n    Test<int>();\n    Test<long>();\n    Test<long long>();\n    Test<float>();\n    Test<double>();\n\n    Test<char2>();\n    Test<short2>();\n    Test<int2>();\n    Test<long2>();\n    Test<longlong2>();\n    Test<float2>();\n    Test<double2>();\n\n    Test<char3>();\n    Test<short3>();\n    Test<int3>();\n    Test<long3>();\n    Test<longlong3>();\n    Test<float3>();\n    Test<double3>();\n\n    Test<char4>();\n    Test<short4>();\n    Test<int4>();\n    Test<long4>();\n    Test<longlong4>();\n    Test<float4>();\n    Test<double4>();\n\n    Test<TestFoo>();\n    Test<TestBar>();\n\n    printf(\"\\nTest complete\\n\");\n    fflush(stdout);\n\n    return 0;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of iterator utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n// This file tests deprecated CUB APIs. Silence deprecation warnings:\n#define CUB_IGNORE_DEPRECATED_API\n\n#include <cub/iterator/tex_ref_input_iterator.cuh>\n#include <cub/util_type.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <iterator>\n#include <cstdio>\n#include <typeinfo>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose = false;\nCachingDeviceAllocator  g_allocator(true);\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Test random access input iterator\n */\ntemplate <\n    typename InputIteratorT,\n    typename T>\n__global__ void Kernel(\n    InputIteratorT    d_in,\n    T                 *d_out,\n    InputIteratorT    *d_itrs)\n{\n    d_out[0] = *d_in;               // Value at offset 0\n    d_out[1] = d_in[100];           // Value at offset 100\n    d_out[2] = *(d_in + 1000);      // Value at offset 1000\n    d_out[3] = *(d_in + 10000);     // Value at offset 10000\n\n    d_in++;\n    d_out[4] = d_in[0];             // Value at offset 1\n\n    d_in += 20;\n    d_out[5] = d_in[0];             // Value at offset 21\n    d_itrs[0] = d_in;               // Iterator at offset 21\n\n    d_in -= 10;\n    d_out[6] = d_in[0];             // Value at offset 11;\n\n    d_in -= 11;\n    d_out[7] = d_in[0];             // Value at offset 0\n    d_itrs[1] = d_in;               // Iterator at offset 0\n}\n\n\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n\n/**\n * Run iterator test on device\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        T,\n    int             TEST_VALUES>\nvoid Test(\n    InputIteratorT  d_in,\n    T               (&h_reference)[TEST_VALUES])\n{\n    // Allocate device arrays\n    T                 *d_out    = NULL;\n    InputIteratorT    *d_itrs   = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out,     sizeof(T) * TEST_VALUES));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_itrs,    sizeof(InputIteratorT) * 2));\n\n    int compare;\n\n    // Run unguarded kernel\n    Kernel<<<1, 1>>>(d_in, d_out, d_itrs);\n\n    CubDebugExit(hipPeekAtLastError());\n    CubDebugExit(hipDeviceSynchronize());\n\n    // Check results\n    compare = CompareDeviceResults(h_reference, d_out, TEST_VALUES, g_verbose, g_verbose);\n    printf(\"\\tValues: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 21\n    InputIteratorT h_itr = d_in + 21;\n    compare = CompareDeviceResults(&h_itr, d_itrs, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Check iterator at offset 0\n    compare = CompareDeviceResults(&d_in, d_itrs + 1, 1, g_verbose, g_verbose);\n    printf(\"\\tIterators: %s\\n\", (compare) ? \"FAIL\" : \"PASS\");\n    AssertEquals(0, compare);\n\n    // Cleanup\n    if (d_out)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_out));\n    }\n    if (d_itrs)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_itrs));\n    }\n}\n\n/**\n * Test tex-ref texture iterator\n */\ntemplate <typename T, typename CastT>\nvoid TestTexRef()\n{\n    printf(\"\\nTesting tex-ref iterator on type %s\\n\", typeid(T).name()); fflush(stdout);\n\n    //\n    // Test iterator manipulation in kernel\n    //\n\n    constexpr int TEST_VALUES                   = 11000;\n    constexpr unsigned int DUMMY_OFFSET         = 500;\n    constexpr unsigned int DUMMY_TEST_VALUES    = TEST_VALUES - DUMMY_OFFSET;\n\n    T *h_data = new T[TEST_VALUES];\n    for (int i = 0; i < TEST_VALUES; ++i)\n    {\n        RandomBits(h_data[i]);\n    }\n\n    // Allocate device arrays\n    T *d_data   = NULL;\n    T *d_dummy  = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_data, sizeof(T) * TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_data, h_data, sizeof(T) * TEST_VALUES, hipMemcpyHostToDevice));\n\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n    CubDebugExit(hipMemcpy(d_dummy, h_data + DUMMY_OFFSET, sizeof(T) * DUMMY_TEST_VALUES, hipMemcpyHostToDevice));\n\n    // Initialize reference data\n    T h_reference[8];\n    h_reference[0] = h_data[0];          // Value at offset 0\n    h_reference[1] = h_data[100];        // Value at offset 100\n    h_reference[2] = h_data[1000];       // Value at offset 1000\n    h_reference[3] = h_data[10000];      // Value at offset 10000\n    h_reference[4] = h_data[1];          // Value at offset 1\n    h_reference[5] = h_data[21];         // Value at offset 21\n    h_reference[6] = h_data[11];         // Value at offset 11\n    h_reference[7] = h_data[0];          // Value at offset 0;\n\n    // Create and bind ref-based test iterator\n    TexRefInputIterator<T, __LINE__> d_ref_itr;\n    CubDebugExit(d_ref_itr.BindTexture((CastT*) d_data, sizeof(T) * TEST_VALUES));\n\n    // Create and bind dummy iterator of same type to check with interferance\n    TexRefInputIterator<T, __LINE__> d_ref_itr2;\n    CubDebugExit(d_ref_itr2.BindTexture((CastT*) d_dummy, sizeof(T) * DUMMY_TEST_VALUES));\n\n    Test(d_ref_itr, h_reference);\n\n    CubDebugExit(d_ref_itr.UnbindTexture());\n    CubDebugExit(d_ref_itr2.UnbindTexture());\n\n    if (h_data)\n    {\n        delete[] h_data;\n    }\n    if (d_data)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_data));\n    }\n    if (d_dummy)\n    {\n        CubDebugExit(g_allocator.DeviceFree(d_dummy));\n    }\n}\n\n/**\n * Run non-integer tests\n */\ntemplate <typename T, typename CastT>\nvoid Test()\n{\n    TestTexRef<T, CastT>();\n}\n\n/**\n * Run tests\n */\ntemplate <typename T>\nvoid Test()\n{\n    // Test non-const type\n    Test<T, T>();\n\n    // Test non-const type\n    Test<T, const T>();\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Evaluate different data types\n    Test<signed char>();\n    Test<short>();\n    Test<int>();\n    Test<long>();\n    Test<long long>();\n    Test<float>();\n    Test<double>();\n\n    Test<char2>();\n    Test<short2>();\n    Test<int2>();\n    Test<long2>();\n    Test<longlong2>();\n    Test<float2>();\n    Test<double2>();\n\n    Test<char3>();\n    Test<short3>();\n    Test<int3>();\n    Test<long3>();\n    Test<longlong3>();\n    Test<float3>();\n    Test<double3>();\n\n    Test<char4>();\n    Test<short4>();\n    Test<int4>();\n    Test<long4>();\n    Test<longlong4>();\n    Test<float4>();\n    Test<double4>();\n\n    Test<TestFoo>();\n    Test<TestBar>();\n\n    printf(\"\\nTest complete\\n\");\n    fflush(stdout);\n\n    return 0;\n}\n", "source": "/kaggle/working/cub/test/test_iterator_deprecated.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test evaluation for software global barrier throughput\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <stdio.h>\n\n#include <cub/grid/grid_barrier.cuh>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Kernel that iterates through the specified number of software global barriers\n */\n__global__ void Kernel(\n    GridBarrier global_barrier,\n    int iterations)\n{\n    for (int i = 0; i < iterations; i++)\n    {\n        global_barrier.Sync();\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    cudaError_t retval = cudaSuccess;\n\n    // Defaults\n    int iterations = 10000;\n    int block_size = 128;\n    int grid_size = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n\n    // Get args\n    args.GetCmdLineArgument(\"i\", iterations);\n    args.GetCmdLineArgument(\"grid-size\", grid_size);\n    args.GetCmdLineArgument(\"block-size\", block_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>]\"\n            \"[--i=<iterations>]\"\n            \"[--grid-size<grid-size>]\"\n            \"[--block-size<block-size>]\"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Get device ordinal\n    int device_ordinal;\n    CubDebugExit(cudaGetDevice(&device_ordinal));\n\n    // Get device SM version\n    int sm_version = 0;\n    CubDebugExit(SmVersion(sm_version, device_ordinal));\n\n    // Get SM properties\n    int sm_count, max_block_threads, max_sm_occupancy;\n    CubDebugExit(cudaDeviceGetAttribute(&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal));\n    CubDebugExit(cudaDeviceGetAttribute(&max_block_threads, cudaDevAttrMaxThreadsPerBlock, device_ordinal));\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, EmptyKernel<void>, 32));\n\n    // Compute grid size and occupancy\n    int occupancy = CUB_MIN((max_block_threads / block_size), max_sm_occupancy);\n\n    if (grid_size == -1)\n    {\n        grid_size = occupancy * sm_count;\n    }\n    else\n    {\n        occupancy = grid_size / sm_count;\n    }\n\n    printf(\"Initializing software global barrier for Kernel<<<%d,%d>>> with %d occupancy\\n\",\n        grid_size, block_size, occupancy);\n    fflush(stdout);\n\n    // Init global barrier\n    GridBarrierLifetime global_barrier;\n    global_barrier.Setup(grid_size);\n\n    // Time kernel\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    Kernel<<<grid_size, block_size>>>(global_barrier, iterations);\n    gpu_timer.Stop();\n\n    retval = CubDebug(cudaDeviceSynchronize());\n\n    // Output timing results\n    float avg_elapsed = gpu_timer.ElapsedMillis() / float(iterations);\n    printf(\"%d iterations, %f total elapsed millis, %f avg elapsed millis\\n\",\n        iterations,\n        gpu_timer.ElapsedMillis(),\n        avg_elapsed);\n\n    return retval;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test evaluation for software global barrier throughput\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n\n#include <cub/grid/grid_barrier.cuh>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Test kernels\n//---------------------------------------------------------------------\n\n/**\n * Kernel that iterates through the specified number of software global barriers\n */\n__global__ void Kernel(\n    GridBarrier global_barrier,\n    int iterations)\n{\n    for (int i = 0; i < iterations; i++)\n    {\n        global_barrier.Sync();\n    }\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    hipError_t retval = hipSuccess;\n\n    // Defaults\n    int iterations = 10000;\n    int block_size = 128;\n    int grid_size = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n\n    // Get args\n    args.GetCmdLineArgument(\"i\", iterations);\n    args.GetCmdLineArgument(\"grid-size\", grid_size);\n    args.GetCmdLineArgument(\"block-size\", block_size);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--device=<device-id>]\"\n            \"[--i=<iterations>]\"\n            \"[--grid-size<grid-size>]\"\n            \"[--block-size<block-size>]\"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n\n    // Get device ordinal\n    int device_ordinal;\n    CubDebugExit(hipGetDevice(&device_ordinal));\n\n    // Get device SM version\n    int sm_version = 0;\n    CubDebugExit(SmVersion(sm_version, device_ordinal));\n\n    // Get SM properties\n    int sm_count, max_block_threads, max_sm_occupancy;\n    CubDebugExit(hipDeviceGetAttribute(&sm_count, hipDeviceAttributeMultiprocessorCount, device_ordinal));\n    CubDebugExit(hipDeviceGetAttribute(&max_block_threads, hipDeviceAttributeMaxThreadsPerBlock, device_ordinal));\n    CubDebugExit(MaxSmOccupancy(max_sm_occupancy, EmptyKernel<void>, 32));\n\n    // Compute grid size and occupancy\n    int occupancy = CUB_MIN((max_block_threads / block_size), max_sm_occupancy);\n\n    if (grid_size == -1)\n    {\n        grid_size = occupancy * sm_count;\n    }\n    else\n    {\n        occupancy = grid_size / sm_count;\n    }\n\n    printf(\"Initializing software global barrier for Kernel<<<%d,%d>>> with %d occupancy\\n\",\n        grid_size, block_size, occupancy);\n    fflush(stdout);\n\n    // Init global barrier\n    GridBarrierLifetime global_barrier;\n    global_barrier.Setup(grid_size);\n\n    // Time kernel\n    GpuTimer gpu_timer;\n    gpu_timer.Start();\n    Kernel<<<grid_size, block_size>>>(global_barrier, iterations);\n    gpu_timer.Stop();\n\n    retval = HipcubDebug(hipDeviceSynchronize());\n\n    // Output timing results\n    float avg_elapsed = gpu_timer.ElapsedMillis() / float(iterations);\n    printf(\"%d iterations, %f total elapsed millis, %f avg elapsed millis\\n\",\n        iterations,\n        gpu_timer.ElapsedMillis(),\n        avg_elapsed);\n\n    return retval;\n}\n", "source": "/kaggle/working/cub/test/test_grid_barrier.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceScan utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_type.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace cub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\ndouble                  g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n/**\n * \\brief WrapperFunctor (for precluding test-specialized dispatch to *Sum variants)\n */\ntemplate<typename OpT>\nstruct WrapperFunctor\n{\n    OpT op;\n\n    WrapperFunctor(OpT op) : op(op) {}\n\n    template <typename T, typename U>\n    __host__ __device__ __forceinline__ auto operator()(const T &a, const U &b) const\n      -> decltype(op(a, b))\n    {\n        return static_cast<T>(op(a, b));\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceScan entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to exclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /* d_temp_storage_bytes */,\n         cudaError_t * /* d_cdp_error */,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         ScanOpT scan_op,\n         InitialValueT initial_value,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      scan_op,\n                                      initial_value,\n                                      num_items);\n  }\n  return error;\n}\n\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         ScanOpT scan_op,\n         InitialValueT initial_value,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      d_out,\n                                      scan_op,\n                                      initial_value,\n                                      num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to exclusive sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename InitialValueT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         Sum /*scan_op*/,\n         InitialValueT /*initial_value*/,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     num_items);\n  }\n  return error;\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename InitialValueT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         Sum /*scan_op*/,\n         InitialValueT /*initial_value*/,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to inclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         ScanOpT scan_op,\n         NullType /* initial_value */,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      scan_op,\n                                      num_items);\n  }\n  return error;\n}\n\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         ScanOpT scan_op,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      d_out,\n                                      scan_op,\n                                      num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to inclusive sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         Sum /*scan_op*/,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     num_items);\n  }\n  return error;\n}\n\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION __forceinline__ cudaError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         cudaError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         Sum /*scan_op*/,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  cudaError_t error = cudaSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items);\n  }\n  return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceScan\n */\ntemplate <typename InPlaceT,\n          typename CubBackendT,\n          typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(InPlaceT     in_place,\n                                  CubBackendT  cub_backend,\n                                  IsPrimitiveT is_primitive,\n                                  int          timing_timing_iterations,\n                                  size_t      *d_temp_storage_bytes,\n                                  cudaError_t *d_cdp_error,\n\n                                  void           *d_temp_storage,\n                                  size_t          temp_storage_bytes,\n                                  InputIteratorT  d_in,\n                                  OutputIteratorT d_out,\n                                  ScanOpT         scan_op,\n                                  InitialValueT   initial_value,\n                                  OffsetT         num_items)\n{\n  *d_cdp_error = Dispatch(in_place,\n                          cub_backend,\n                          is_primitive,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          scan_op,\n                          initial_value,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename InPlaceT,\n          typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\ncudaError_t Dispatch(InPlaceT      in_place,\n                     Int2Type<CDP> dispatch_to,\n                     IsPrimitiveT  is_primitive,\n                     int           timing_timing_iterations,\n                     size_t       *d_temp_storage_bytes,\n                     cudaError_t  *d_cdp_error,\n\n                     void           *d_temp_storage,\n                     size_t         &temp_storage_bytes,\n                     InputIteratorT  d_in,\n                     OutputIteratorT d_out,\n                     ScanOpT         scan_op,\n                     InitialValueT   initial_value,\n                     OffsetT         num_items)\n{\n  // Invoke kernel to invoke device-side dispatch to CUB backend:\n  (void)dispatch_to;\n  using CubBackendT = Int2Type<CUB>;\n  CubBackendT cub_backend;\n  cudaError_t   retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<InPlaceT,\n                              CubBackendT,\n                              IsPrimitiveT,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              ScanOpT,\n                              InitialValueT,\n                              OffsetT>,\n            in_place,\n            cub_backend,\n            is_primitive,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            scan_op,\n            initial_value,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(cudaMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          cudaMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(cudaMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(cudaError_t) * 1,\n                          cudaMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    GenMode      gen_mode,\n    T            *h_in,\n    int          num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n/**\n * Solve exclusive-scan problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Solve(\n    InputIteratorT  h_in,\n    OutputT         *h_reference,\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    using AccumT = \n      cub::detail::accumulator_t<\n        ScanOpT, \n        InitialValueT, \n        cub::detail::value_t<InputIteratorT>>;\n\n    if (num_items > 0)\n    {\n        AccumT val         = static_cast<AccumT>(h_in[0]);\n        h_reference[0]     = initial_value;\n        AccumT inclusive   = static_cast<AccumT>(scan_op(initial_value, val));\n\n        for (int i = 1; i < num_items; ++i)\n        {\n            val = static_cast<AccumT>(h_in[i]);\n            h_reference[i] = static_cast<OutputT>(inclusive);\n            inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n        }\n    }\n}\n\n\n/**\n * Solve inclusive-scan problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT>\nvoid Solve(\n    InputIteratorT  h_in,\n    OutputT         *h_reference,\n    int             num_items,\n    ScanOpT         scan_op,\n    NullType)\n{\n    using AccumT = \n      cub::detail::accumulator_t<\n        ScanOpT, \n        cub::detail::value_t<InputIteratorT>, \n        cub::detail::value_t<InputIteratorT>>;\n\n    if (num_items > 0)\n    {\n        AccumT inclusive    = h_in[0];\n        h_reference[0]      = static_cast<OutputT>(inclusive);\n\n        for (int i = 1; i < num_items; ++i)\n        {\n            AccumT val = h_in[i];\n            inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n            h_reference[i] = static_cast<OutputT>(inclusive);\n        }\n    }\n}\n\ntemplate<typename OutputT, typename DeviceInputIteratorT, bool InPlace>\nstruct AllocateOutput {\n    static void run(OutputT *&d_out, DeviceInputIteratorT, int num_items) {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_items));\n    }\n};\n\ntemplate<typename OutputT>\nstruct AllocateOutput<OutputT, OutputT *, true> {\n    static void run(OutputT *&d_out, OutputT *d_in, int /* num_items */) {\n        d_out = d_in;\n    }\n};\n\n/**\n * Test DeviceScan for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT,\n    bool                InPlace=false>\nvoid Test(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    using InputT = cub::detail::value_t<DeviceInputIteratorT>;\n\n    // Allocate device output array\n    OutputT *d_out = NULL;\n    AllocateOutput<OutputT, DeviceInputIteratorT, InPlace>::run(d_out, d_in, num_items);\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    cudaError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,   sizeof(cudaError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(\n        Int2Type<InPlace>(),\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_in,\n        d_out,\n        scan_op,\n        initial_value,\n        num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    if (!InPlace)\n    {\n      // Clear device output array\n      CubDebugExit(cudaMemset(d_out, 0, sizeof(OutputT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<InPlace>(),\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_in,\n        d_out,\n        scan_op,\n        initial_value,\n        num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare = CompareDeviceResults(h_reference, d_out, num_items, true, g_verbose);\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    if (g_timing_iterations > 0)\n    {\n      GpuTimer gpu_timer;\n      gpu_timer.Start();\n      CubDebugExit(Dispatch(Int2Type<InPlace>(),\n                            Int2Type<BACKEND>(),\n                            Int2Type<Traits<OutputT>::PRIMITIVE>(),\n                            g_timing_iterations,\n                            d_temp_storage_bytes,\n                            d_cdp_error,\n                            d_temp_storage,\n                            temp_storage_bytes,\n                            d_in,\n                            d_out,\n                            scan_op,\n                            initial_value,\n                            num_items));\n      gpu_timer.Stop();\n      float elapsed_millis = gpu_timer.ElapsedMillis();\n\n      // Display performance\n      float avg_millis     = elapsed_millis / g_timing_iterations;\n      float giga_rate      = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n      float giga_bandwidth = giga_rate * (sizeof(InputT) + sizeof(OutputT));\n      printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% \"\n             \"peak\",\n             avg_millis,\n             giga_rate,\n             giga_bandwidth,\n             giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (!InPlace)\n    {\n      if (d_out)\n      {\n        CubDebugExit(g_allocator.DeviceFree(d_out));\n      }\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <typename InitialValueT>\n__global__ void FillInitValue(InitialValueT *ptr, InitialValueT initial_value) {\n    *ptr = initial_value;\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<!std::is_same<InitialValueT, cub::NullType>::value>::type\nTestFutureInitValue(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    // Allocate device initial_value\n    InitialValueT *d_initial_value = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_initial_value, sizeof(InitialValueT)));\n    FillInitValue<<<1, 1>>>(d_initial_value, initial_value);\n\n    // Run test\n    auto future_init_value = cub::FutureValue<InitialValueT>(d_initial_value);\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, future_init_value);\n\n    // Cleanup\n    if (d_initial_value) CubDebugExit(g_allocator.DeviceFree(d_initial_value));\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<std::is_same<InitialValueT, cub::NullType>::value>::type\nTestFutureInitValue(\n    DeviceInputIteratorT,\n    OutputT *,\n    int,\n    ScanOpT,\n    InitialValueT)\n{\n    // cub::NullType does not have device pointer, so nothing to do here\n}\n\ntemplate <\n  Backend             BACKEND,\n  typename            DeviceInputIteratorT,\n  typename            OutputT,\n  typename            ScanOpT,\n  typename            InitialValueT>\ntypename std::enable_if<!std::is_same<InitialValueT, cub::NullType>::value>::type\nTestFutureInitValueIter(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    using IterT = cub::ConstantInputIterator<InitialValueT>;\n    IterT iter(initial_value);\n    auto future_init_value = cub::FutureValue<InitialValueT, IterT>(iter);\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, future_init_value);\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<std::is_same<InitialValueT, cub::NullType>::value>::type\nTestFutureInitValueIter(\n    DeviceInputIteratorT,\n    OutputT *,\n    int,\n    ScanOpT,\n    InitialValueT)\n{\n    // cub::NullType does not have device pointer, so nothing to do here\n}\n\ntemplate <Backend BACKEND,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT>\nvoid TestInplace(OutputT *d_in,\n                 OutputT *h_reference,\n                 int num_items,\n                 ScanOpT scan_op,\n                 InitialValueT initial_value)\n{\n  Test<BACKEND, OutputT *, OutputT, ScanOpT, InitialValueT, true>(d_in,\n                                                                  h_reference,\n                                                                  num_items,\n                                                                  scan_op,\n                                                                  initial_value);\n}\n\ntemplate <Backend BACKEND,\n          typename DeviceInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT>\nvoid TestInplace(DeviceInputIteratorT, OutputT *, int, ScanOpT, InitialValueT)\n{}\n\n/**\n * Test DeviceScan on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid TestPointer(\n    int             num_items,\n    GenMode         gen_mode,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    printf(\"\\nPointer %s %s cub::DeviceScan::%s %d items, %s->%s (%d->%d bytes) , gen-mode %s\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT),\n        (gen_mode == RANDOM) ? \"RANDOM\" : (gen_mode == INTEGER_SEED) ? \"SEQUENTIAL\" : \"HOMOGENOUS\");\n    fflush(stdout);\n\n    // Allocate host arrays\n    InputT*     h_in        = new InputT[num_items];\n    OutputT*    h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Initialize(gen_mode, h_in, num_items);\n\n    // If the output type is primitive and the operator is cub::Sum, the test\n    // dispatcher throws away scan_op and initial_value for exclusive scan.\n    // Without an initial_value arg, the accumulator switches to the input value\n    // type.\n    // Do the same thing here:\n    if (Traits<OutputT>::PRIMITIVE &&\n        std::is_same<ScanOpT, cub::Sum>::value &&\n        !std::is_same<InitialValueT, NullType>::value)\n    {\n      Solve(h_in, h_reference, num_items, cub::Sum{}, InputT{});\n    }\n    else\n    {\n      Solve(h_in, h_reference, num_items, scan_op, initial_value);\n    }\n\n    // Allocate problem device arrays\n    InputT *d_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(InputT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(InputT) * num_items, cudaMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n    TestFutureInitValue<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n    TestInplace<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test DeviceScan on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid TestIterator(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    printf(\"\\nIterator %s %s cub::DeviceScan::%s %d items, %s->%s (%d->%d bytes)\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT));\n    fflush(stdout);\n\n    // Use a constant iterator as the input\n    InputT val = InputT();\n    ConstantInputIterator<InputT, int> h_in(val);\n\n    // Allocate host arrays\n    OutputT*  h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Solve(h_in, h_reference, num_items, scan_op, initial_value);\n\n    // Run Test\n    Test<BACKEND>(h_in, h_reference, num_items, scan_op, initial_value);\n    TestFutureInitValueIter<BACKEND>(h_in, h_reference, num_items, scan_op, initial_value);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    TestPointer<BACKEND, InputT, OutputT>(  num_items, UNIFORM, scan_op, initial_value);\n    TestPointer<BACKEND, InputT, OutputT>(  num_items, RANDOM,  scan_op, initial_value);\n    TestIterator<BACKEND, InputT, OutputT>( num_items, scan_op, initial_value);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n#if TEST_CDP == 0\n    Test<CUB, InputT, OutputT>(num_items, scan_op, initial_value);\n#elif TEST_CDP == 1\n    Test<CDP, InputT, OutputT>(num_items, scan_op, initial_value);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different operators\n */\ntemplate <typename InputT, typename OutputT>\nvoid TestOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value)\n{\n    // Exclusive (use identity as initial value because it will dispatch to *Sum variants that don't take initial values)\n    Test<InputT, OutputT>(num_items, cub::Sum(), identity);\n    Test<InputT, OutputT>(num_items, cub::Max(), identity);\n\n    // Exclusive (non-specialized, so we can test initial-value)\n    Test<InputT, OutputT>(num_items, WrapperFunctor<cub::Sum>(cub::Sum()), initial_value);\n    Test<InputT, OutputT>(num_items, WrapperFunctor<cub::Max>(cub::Max()), initial_value);\n\n    // Inclusive (no initial value)\n    Test<InputT, OutputT>(num_items, cub::Sum(), NullType());\n    Test<InputT, OutputT>(num_items, cub::Max(), NullType());\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename InputT,\n    typename OutputT>\nvoid TestSize(\n    int     num_items,\n    OutputT identity,\n    OutputT initial_value)\n{\n    if (num_items < 0)\n    {\n        TestOp<InputT>(0,        identity, initial_value);\n        TestOp<InputT>(1,        identity, initial_value);\n        TestOp<InputT>(100,      identity, initial_value);\n        TestOp<InputT>(10000,    identity, initial_value);\n        TestOp<InputT>(1000000,  identity, initial_value);\n    }\n    else\n    {\n        TestOp<InputT>(num_items, identity, initial_value);\n    }\n}\n\nclass CustomInputT\n{\n  char m_val{};\n\npublic:\n  __host__ __device__ explicit CustomInputT(char val)\n      : m_val(val)\n  {}\n\n  __host__ __device__ int get() const { return static_cast<int>(m_val); }\n};\n\nclass CustomAccumulatorT\n{\n  int m_val{0};\n  int m_magic_value{42};\n\n  __host__ __device__ CustomAccumulatorT(int val)\n      : m_val(val)\n  {}\n\npublic:\n  __host__ __device__ CustomAccumulatorT()\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomAccumulatorT &in)\n    : m_val(in.is_valid() * in.get())\n    , m_magic_value(in.is_valid() * 42)\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomInputT &in)\n    : m_val(in.get())\n    , m_magic_value(42)\n  {}\n\n  __host__ __device__ void operator=(const CustomInputT &in)\n  {\n    if (this->is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &in)\n  {\n    if (this->is_valid() && in.is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ CustomAccumulatorT \n  operator+(const CustomInputT &in) const\n  {\n    const int multiplier = this->is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ CustomAccumulatorT\n  operator+(const CustomAccumulatorT &in) const\n  {\n    const int multiplier = this->is_valid() && in.is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ int get() const { return m_val; }\n\n  __host__ __device__ bool is_valid() const { return m_magic_value == 42; }\n};\n\nclass CustomOutputT\n{\n  int *m_d_ok_count{};\n  int m_expected{};\n\npublic:\n  __host__ __device__ CustomOutputT(int *d_ok_count, int expected)\n      : m_d_ok_count(d_ok_count)\n      , m_expected(expected)\n  {}\n\n  __device__ void operator=(const CustomAccumulatorT &accum) const\n  {\n    const int ok = accum.is_valid() && (accum.get() == m_expected);\n    atomicAdd(m_d_ok_count, ok);\n  }\n};\n\n__global__ void InitializeTestAccumulatorTypes(int num_items,\n                                               int *d_ok_count,\n                                               CustomInputT *d_in,\n                                               CustomOutputT *d_out)\n{\n  const int idx = static_cast<int>(threadIdx.x + blockIdx.x * blockDim.x);\n\n  if (idx < num_items)\n  {\n    d_in[idx] = CustomInputT(1);\n    d_out[idx] = CustomOutputT{d_ok_count, idx};\n  }\n}\n\nvoid TestAccumulatorTypes()\n{\n  const int num_items  = 2 * 1024 * 1024;\n  const int block_size = 256;\n  const int grid_size  = (num_items + block_size - 1) / block_size;\n\n  CustomInputT *d_in{};\n  CustomOutputT *d_out{};\n  CustomAccumulatorT init{};\n  int *d_ok_count{};\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_ok_count, sizeof(int)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_out,\n                                          sizeof(CustomOutputT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in,\n                                          sizeof(CustomInputT) * num_items));\n\n  InitializeTestAccumulatorTypes<<<grid_size, block_size>>>(num_items,\n                                                            d_ok_count,\n                                                            d_in,\n                                                            d_out);\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(cub::DeviceScan::ExclusiveScan(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              cub::Sum{},\n                                              init,\n                                              num_items));\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_temp_storage, temp_storage_bytes));\n  CubDebugExit(cudaMemset(d_temp_storage, 1, temp_storage_bytes));\n\n  CubDebugExit(cub::DeviceScan::ExclusiveScan(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              cub::Sum{},\n                                              init,\n                                              num_items));\n\n  int ok{};\n  CubDebugExit(cudaMemcpy(&ok, d_ok_count, sizeof(int), cudaMemcpyDeviceToHost));\n\n  AssertEquals(ok, num_items);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n  CubDebugExit(g_allocator.DeviceFree(d_ok_count));\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_VALUE_TYPES types 0:1:2\n\n#if TEST_VALUE_TYPES == 0\n\n    // Test different input+output data types\n    TestSize<unsigned char>(num_items, (int)0, (int)99);\n\n    // Test same input+output data types\n    TestSize<unsigned char>(num_items, (unsigned char)0, (unsigned char)99);\n    TestSize<signed char>(num_items, (char)0, (char)99);\n    TestSize<unsigned short>(num_items, (unsigned short)0, (unsigned short)99);\n    TestSize<unsigned int>(num_items, (unsigned int)0, (unsigned int)99);\n    TestSize<unsigned long long>(num_items,\n                                 (unsigned long long)0,\n                                 (unsigned long long)99);\n\n#elif TEST_VALUE_TYPES == 1\n\n    TestSize<uchar2>(num_items, make_uchar2(0, 0), make_uchar2(17, 21));\n    TestSize<char2>(num_items, make_char2(0, 0), make_char2(17, 21));\n    TestSize<ushort2>(num_items, make_ushort2(0, 0), make_ushort2(17, 21));\n    TestSize<uint2>(num_items, make_uint2(0, 0), make_uint2(17, 21));\n    TestSize<ulonglong2>(num_items,\n                         make_ulonglong2(0, 0),\n                         make_ulonglong2(17, 21));\n    TestSize<uchar4>(num_items,\n                     make_uchar4(0, 0, 0, 0),\n                     make_uchar4(17, 21, 32, 85));\n#elif TEST_VALUE_TYPES == 2\n    TestSize<char4>(num_items,\n                    make_char4(0, 0, 0, 0),\n                    make_char4(17, 21, 32, 85));\n\n    TestSize<ushort4>(num_items,\n                      make_ushort4(0, 0, 0, 0),\n                      make_ushort4(17, 21, 32, 85));\n    TestSize<uint4>(num_items,\n                    make_uint4(0, 0, 0, 0),\n                    make_uint4(17, 21, 32, 85));\n    TestSize<ulonglong4>(num_items,\n                         make_ulonglong4(0, 0, 0, 0),\n                         make_ulonglong4(17, 21, 32, 85));\n\n    TestSize<TestFoo>(num_items,\n                      TestFoo::MakeTestFoo(0, 0, 0, 0),\n                      TestFoo::MakeTestFoo(std::numeric_limits<TestFoo::x_t>::max(),\n                                           std::numeric_limits<TestFoo::y_t>::max(),\n                                           std::numeric_limits<TestFoo::z_t>::max(),\n                                           std::numeric_limits<TestFoo::w_t>::max()));\n\n    TestSize<TestBar>(num_items, \n                      TestBar(0, 0), \n                      TestBar(std::numeric_limits<long long>::max(), \n                              std::numeric_limits<int>::max()));\n\n    TestAccumulatorTypes();\n#endif\n\n    return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2018, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Test of DeviceScan utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/device/device_scan.cuh>\n#include <cub/iterator/constant_input_iterator.cuh>\n#include <cub/iterator/discard_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_type.cuh>\n\n#include <thrust/system/cuda/detail/core/triple_chevron_launch.h>\n\n#include \"test_util.h\"\n\n#include <cstdio>\n#include <typeinfo>\n\nusing namespace hipcub;\n\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool                    g_verbose           = false;\nint                     g_timing_iterations = 0;\ndouble                  g_device_giga_bandwidth;\nCachingDeviceAllocator  g_allocator(true);\n\n// Dispatch types\nenum Backend\n{\n    CUB,        // CUB method\n    CDP,        // GPU-based (dynamic parallelism) dispatch to CUB method\n};\n\n\n/**\n * \\brief WrapperFunctor (for precluding test-specialized dispatch to *Sum variants)\n */\ntemplate<typename OpT>\nstruct WrapperFunctor\n{\n    OpT op;\n\n    WrapperFunctor(OpT op) : op(op) {}\n\n    template <typename T, typename U>\n    __host__ __device__ __forceinline__ auto operator()(const T &a, const U &b) const\n      -> decltype(op(a, b))\n    {\n        return static_cast<T>(op(a, b));\n    }\n};\n\n\n//---------------------------------------------------------------------\n// Dispatch to different CUB DeviceScan entrypoints\n//---------------------------------------------------------------------\n\n/**\n * Dispatch to exclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /* d_temp_storage_bytes */,\n         hipError_t * /* d_cdp_error */,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         ScanOpT scan_op,\n         InitialValueT initial_value,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      scan_op,\n                                      initial_value,\n                                      num_items);\n  }\n  return error;\n}\n\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         ScanOpT scan_op,\n         InitialValueT initial_value,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      d_out,\n                                      scan_op,\n                                      initial_value,\n                                      num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to exclusive sum entrypoint\n */\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename InitialValueT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         Sum /*scan_op*/,\n         InitialValueT /*initial_value*/,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     num_items);\n  }\n  return error;\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename InitialValueT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         Sum /*scan_op*/,\n         InitialValueT /*initial_value*/,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::ExclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to inclusive scan entrypoint\n */\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         ScanOpT scan_op,\n         NullType /* initial_value */,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      scan_op,\n                                      num_items);\n  }\n  return error;\n}\n\ntemplate <typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         IsPrimitiveT /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         ScanOpT scan_op,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveScan(d_temp_storage,\n                                      temp_storage_bytes,\n                                      d_in,\n                                      d_out,\n                                      scan_op,\n                                      num_items);\n  }\n  return error;\n}\n\n/**\n * Dispatch to inclusive sum entrypoint\n */\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<true> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT /* d_out */,\n         Sum /*scan_op*/,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     num_items);\n  }\n  return error;\n}\n\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t\nDispatch(Int2Type<false> /*in_place*/,\n         Int2Type<CUB> /*dispatch_to*/,\n         Int2Type<true> /*is_primitive*/,\n         int timing_timing_iterations,\n         size_t * /*d_temp_storage_bytes*/,\n         hipError_t * /*d_cdp_error*/,\n         void *d_temp_storage,\n         size_t &temp_storage_bytes,\n         InputIteratorT d_in,\n         OutputIteratorT d_out,\n         Sum /*scan_op*/,\n         NullType /*initial_value*/,\n         OffsetT num_items)\n{\n  hipError_t error = hipSuccess;\n  for (int i = 0; i < timing_timing_iterations; ++i)\n  {\n    error = DeviceScan::InclusiveSum(d_temp_storage,\n                                     temp_storage_bytes,\n                                     d_in,\n                                     d_out,\n                                     num_items);\n  }\n  return error;\n}\n\n//---------------------------------------------------------------------\n// CUDA Nested Parallelism Test Kernel\n//---------------------------------------------------------------------\n\n#if TEST_CDP == 1\n\n/**\n * Simple wrapper kernel to invoke DeviceScan\n */\ntemplate <typename InPlaceT,\n          typename CubBackendT,\n          typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\n__global__ void CDPDispatchKernel(InPlaceT     in_place,\n                                  CubBackendT  cub_backend,\n                                  IsPrimitiveT is_primitive,\n                                  int          timing_timing_iterations,\n                                  size_t      *d_temp_storage_bytes,\n                                  hipError_t *d_cdp_error,\n\n                                  void           *d_temp_storage,\n                                  size_t          temp_storage_bytes,\n                                  InputIteratorT  d_in,\n                                  OutputIteratorT d_out,\n                                  ScanOpT         scan_op,\n                                  InitialValueT   initial_value,\n                                  OffsetT         num_items)\n{\n  *d_cdp_error = Dispatch(in_place,\n                          cub_backend,\n                          is_primitive,\n                          timing_timing_iterations,\n                          d_temp_storage_bytes,\n                          d_cdp_error,\n                          d_temp_storage,\n                          temp_storage_bytes,\n                          d_in,\n                          d_out,\n                          scan_op,\n                          initial_value,\n                          num_items);\n\n  *d_temp_storage_bytes = temp_storage_bytes;\n}\n\n/**\n * Dispatch to CDP kernel\n */\ntemplate <typename InPlaceT,\n          typename IsPrimitiveT,\n          typename InputIteratorT,\n          typename OutputIteratorT,\n          typename ScanOpT,\n          typename InitialValueT,\n          typename OffsetT>\nhipError_t Dispatch(InPlaceT      in_place,\n                     Int2Type<CDP> dispatch_to,\n                     IsPrimitiveT  is_primitive,\n                     int           timing_timing_iterations,\n                     size_t       *d_temp_storage_bytes,\n                     hipError_t  *d_cdp_error,\n\n                     void           *d_temp_storage,\n                     size_t         &temp_storage_bytes,\n                     InputIteratorT  d_in,\n                     OutputIteratorT d_out,\n                     ScanOpT         scan_op,\n                     InitialValueT   initial_value,\n                     OffsetT         num_items)\n{\n  // Invoke kernel to invoke device-side dispatch to CUB backend:\n  (void)dispatch_to;\n  using CubBackendT = Int2Type<CUB>;\n  CubBackendT cub_backend;\n  hipError_t   retval =\n    thrust::cuda_cub::launcher::triple_chevron(1, 1, 0, 0)\n      .doit(CDPDispatchKernel<InPlaceT,\n                              CubBackendT,\n                              IsPrimitiveT,\n                              InputIteratorT,\n                              OutputIteratorT,\n                              ScanOpT,\n                              InitialValueT,\n                              OffsetT>,\n            in_place,\n            cub_backend,\n            is_primitive,\n            timing_timing_iterations,\n            d_temp_storage_bytes,\n            d_cdp_error,\n            d_temp_storage,\n            temp_storage_bytes,\n            d_in,\n            d_out,\n            scan_op,\n            initial_value,\n            num_items);\n  CubDebugExit(retval);\n\n  // Copy out temp_storage_bytes\n  CubDebugExit(hipMemcpy(&temp_storage_bytes,\n                          d_temp_storage_bytes,\n                          sizeof(size_t) * 1,\n                          hipMemcpyDeviceToHost));\n\n  // Copy out error\n  CubDebugExit(hipMemcpy(&retval,\n                          d_cdp_error,\n                          sizeof(hipError_t) * 1,\n                          hipMemcpyDeviceToHost));\n  return retval;\n}\n\n#endif // TEST_CDP\n\n//---------------------------------------------------------------------\n// Test generation\n//---------------------------------------------------------------------\n\n\n/**\n * Initialize problem\n */\ntemplate <typename T>\nvoid Initialize(\n    GenMode      gen_mode,\n    T            *h_in,\n    int          num_items)\n{\n    for (int i = 0; i < num_items; ++i)\n    {\n        InitValue(gen_mode, h_in[i], i);\n    }\n\n    if (g_verbose)\n    {\n        printf(\"Input:\\n\");\n        DisplayResults(h_in, num_items);\n        printf(\"\\n\\n\");\n    }\n}\n\n/**\n * Solve exclusive-scan problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Solve(\n    InputIteratorT  h_in,\n    OutputT         *h_reference,\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    using AccumT = \n      hipcub::detail::accumulator_t<\n        ScanOpT, \n        InitialValueT, \n        hipcub::detail::value_t<InputIteratorT>>;\n\n    if (num_items > 0)\n    {\n        AccumT val         = static_cast<AccumT>(h_in[0]);\n        h_reference[0]     = initial_value;\n        AccumT inclusive   = static_cast<AccumT>(scan_op(initial_value, val));\n\n        for (int i = 1; i < num_items; ++i)\n        {\n            val = static_cast<AccumT>(h_in[i]);\n            h_reference[i] = static_cast<OutputT>(inclusive);\n            inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n        }\n    }\n}\n\n\n/**\n * Solve inclusive-scan problem\n */\ntemplate <\n    typename        InputIteratorT,\n    typename        OutputT,\n    typename        ScanOpT>\nvoid Solve(\n    InputIteratorT  h_in,\n    OutputT         *h_reference,\n    int             num_items,\n    ScanOpT         scan_op,\n    NullType)\n{\n    using AccumT = \n      hipcub::detail::accumulator_t<\n        ScanOpT, \n        hipcub::detail::value_t<InputIteratorT>, \n        hipcub::detail::value_t<InputIteratorT>>;\n\n    if (num_items > 0)\n    {\n        AccumT inclusive    = h_in[0];\n        h_reference[0]      = static_cast<OutputT>(inclusive);\n\n        for (int i = 1; i < num_items; ++i)\n        {\n            AccumT val = h_in[i];\n            inclusive = static_cast<AccumT>(scan_op(inclusive, val));\n            h_reference[i] = static_cast<OutputT>(inclusive);\n        }\n    }\n}\n\ntemplate<typename OutputT, typename DeviceInputIteratorT, bool InPlace>\nstruct AllocateOutput {\n    static void run(OutputT *&d_out, DeviceInputIteratorT, int num_items) {\n        CubDebugExit(g_allocator.DeviceAllocate((void**)&d_out, sizeof(OutputT) * num_items));\n    }\n};\n\ntemplate<typename OutputT>\nstruct AllocateOutput<OutputT, OutputT *, true> {\n    static void run(OutputT *&d_out, OutputT *d_in, int /* num_items */) {\n        d_out = d_in;\n    }\n};\n\n/**\n * Test DeviceScan for a given problem input\n */\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT,\n    bool                InPlace=false>\nvoid Test(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    using InputT = hipcub::detail::value_t<DeviceInputIteratorT>;\n\n    // Allocate device output array\n    OutputT *d_out = NULL;\n    AllocateOutput<OutputT, DeviceInputIteratorT, InPlace>::run(d_out, d_in, num_items);\n\n    // Allocate CDP device arrays\n    size_t          *d_temp_storage_bytes = NULL;\n    hipError_t     *d_cdp_error = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_temp_storage_bytes,  sizeof(size_t) * 1));\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_cdp_error,   sizeof(hipError_t) * 1));\n\n    // Allocate temporary storage\n    void            *d_temp_storage = NULL;\n    size_t          temp_storage_bytes = 0;\n    CubDebugExit(Dispatch(\n        Int2Type<InPlace>(),\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_in,\n        d_out,\n        scan_op,\n        initial_value,\n        num_items));\n    CubDebugExit(g_allocator.DeviceAllocate(&d_temp_storage, temp_storage_bytes));\n\n    if (!InPlace)\n    {\n      // Clear device output array\n      CubDebugExit(hipMemset(d_out, 0, sizeof(OutputT) * num_items));\n    }\n\n    // Run warmup/correctness iteration\n    CubDebugExit(Dispatch(\n        Int2Type<InPlace>(),\n        Int2Type<BACKEND>(),\n        Int2Type<Traits<OutputT>::PRIMITIVE>(),\n        1,\n        d_temp_storage_bytes,\n        d_cdp_error,\n        d_temp_storage,\n        temp_storage_bytes,\n        d_in,\n        d_out,\n        scan_op,\n        initial_value,\n        num_items));\n\n    // Check for correctness (and display results, if specified)\n    int compare = CompareDeviceResults(h_reference, d_out, num_items, true, g_verbose);\n    printf(\"\\t%s\", compare ? \"FAIL\" : \"PASS\");\n\n    // Flush any stdout/stderr\n    fflush(stdout);\n    fflush(stderr);\n\n    // Performance\n    if (g_timing_iterations > 0)\n    {\n      GpuTimer gpu_timer;\n      gpu_timer.Start();\n      CubDebugExit(Dispatch(Int2Type<InPlace>(),\n                            Int2Type<BACKEND>(),\n                            Int2Type<Traits<OutputT>::PRIMITIVE>(),\n                            g_timing_iterations,\n                            d_temp_storage_bytes,\n                            d_cdp_error,\n                            d_temp_storage,\n                            temp_storage_bytes,\n                            d_in,\n                            d_out,\n                            scan_op,\n                            initial_value,\n                            num_items));\n      gpu_timer.Stop();\n      float elapsed_millis = gpu_timer.ElapsedMillis();\n\n      // Display performance\n      float avg_millis     = elapsed_millis / g_timing_iterations;\n      float giga_rate      = float(num_items) / avg_millis / 1000.0f / 1000.0f;\n      float giga_bandwidth = giga_rate * (sizeof(InputT) + sizeof(OutputT));\n      printf(\", %.3f avg ms, %.3f billion items/s, %.3f logical GB/s, %.1f%% \"\n             \"peak\",\n             avg_millis,\n             giga_rate,\n             giga_bandwidth,\n             giga_bandwidth / g_device_giga_bandwidth * 100.0);\n    }\n\n    printf(\"\\n\\n\");\n\n    // Cleanup\n    if (!InPlace)\n    {\n      if (d_out)\n      {\n        CubDebugExit(g_allocator.DeviceFree(d_out));\n      }\n    }\n\n    if (d_temp_storage_bytes) CubDebugExit(g_allocator.DeviceFree(d_temp_storage_bytes));\n    if (d_cdp_error) CubDebugExit(g_allocator.DeviceFree(d_cdp_error));\n    if (d_temp_storage) CubDebugExit(g_allocator.DeviceFree(d_temp_storage));\n\n    // Correctness asserts\n    AssertEquals(0, compare);\n}\n\ntemplate <typename InitialValueT>\n__global__ void FillInitValue(InitialValueT *ptr, InitialValueT initial_value) {\n    *ptr = initial_value;\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<!std::is_same<InitialValueT, hipcub::NullType>::value>::type\nTestFutureInitValue(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    // Allocate device initial_value\n    InitialValueT *d_initial_value = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_initial_value, sizeof(InitialValueT)));\n    FillInitValue<<<1, 1>>>(d_initial_value, initial_value);\n\n    // Run test\n    auto future_init_value = hipcub::FutureValue<InitialValueT>(d_initial_value);\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, future_init_value);\n\n    // Cleanup\n    if (d_initial_value) CubDebugExit(g_allocator.DeviceFree(d_initial_value));\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<std::is_same<InitialValueT, hipcub::NullType>::value>::type\nTestFutureInitValue(\n    DeviceInputIteratorT,\n    OutputT *,\n    int,\n    ScanOpT,\n    InitialValueT)\n{\n    // cub::NullType does not have device pointer, so nothing to do here\n}\n\ntemplate <\n  Backend             BACKEND,\n  typename            DeviceInputIteratorT,\n  typename            OutputT,\n  typename            ScanOpT,\n  typename            InitialValueT>\ntypename std::enable_if<!std::is_same<InitialValueT, hipcub::NullType>::value>::type\nTestFutureInitValueIter(\n    DeviceInputIteratorT    d_in,\n    OutputT                 *h_reference,\n    int                     num_items,\n    ScanOpT                 scan_op,\n    InitialValueT           initial_value)\n{\n    using IterT = hipcub::ConstantInputIterator<InitialValueT>;\n    IterT iter(initial_value);\n    auto future_init_value = hipcub::FutureValue<InitialValueT, IterT>(iter);\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, future_init_value);\n}\n\ntemplate <\n    Backend             BACKEND,\n    typename            DeviceInputIteratorT,\n    typename            OutputT,\n    typename            ScanOpT,\n    typename            InitialValueT>\ntypename std::enable_if<std::is_same<InitialValueT, hipcub::NullType>::value>::type\nTestFutureInitValueIter(\n    DeviceInputIteratorT,\n    OutputT *,\n    int,\n    ScanOpT,\n    InitialValueT)\n{\n    // cub::NullType does not have device pointer, so nothing to do here\n}\n\ntemplate <Backend BACKEND,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT>\nvoid TestInplace(OutputT *d_in,\n                 OutputT *h_reference,\n                 int num_items,\n                 ScanOpT scan_op,\n                 InitialValueT initial_value)\n{\n  Test<BACKEND, OutputT *, OutputT, ScanOpT, InitialValueT, true>(d_in,\n                                                                  h_reference,\n                                                                  num_items,\n                                                                  scan_op,\n                                                                  initial_value);\n}\n\ntemplate <Backend BACKEND,\n          typename DeviceInputIteratorT,\n          typename OutputT,\n          typename ScanOpT,\n          typename InitialValueT>\nvoid TestInplace(DeviceInputIteratorT, OutputT *, int, ScanOpT, InitialValueT)\n{}\n\n/**\n * Test DeviceScan on pointer type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid TestPointer(\n    int             num_items,\n    GenMode         gen_mode,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    printf(\"\\nPointer %s %s hipcub::DeviceScan::%s %d items, %s->%s (%d->%d bytes) , gen-mode %s\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT),\n        (gen_mode == RANDOM) ? \"RANDOM\" : (gen_mode == INTEGER_SEED) ? \"SEQUENTIAL\" : \"HOMOGENOUS\");\n    fflush(stdout);\n\n    // Allocate host arrays\n    InputT*     h_in        = new InputT[num_items];\n    OutputT*    h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Initialize(gen_mode, h_in, num_items);\n\n    // If the output type is primitive and the operator is cub::Sum, the test\n    // dispatcher throws away scan_op and initial_value for exclusive scan.\n    // Without an initial_value arg, the accumulator switches to the input value\n    // type.\n    // Do the same thing here:\n    if (Traits<OutputT>::PRIMITIVE &&\n        std::is_same<ScanOpT, hipcub::Sum>::value &&\n        !std::is_same<InitialValueT, NullType>::value)\n    {\n      Solve(h_in, h_reference, num_items, hipcub::Sum{}, InputT{});\n    }\n    else\n    {\n      Solve(h_in, h_reference, num_items, scan_op, initial_value);\n    }\n\n    // Allocate problem device arrays\n    InputT *d_in = NULL;\n    CubDebugExit(g_allocator.DeviceAllocate((void**)&d_in, sizeof(InputT) * num_items));\n\n    // Initialize device input\n    CubDebugExit(hipMemcpy(d_in, h_in, sizeof(InputT) * num_items, hipMemcpyHostToDevice));\n\n    // Run Test\n    Test<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n    TestFutureInitValue<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n    TestInplace<BACKEND>(d_in, h_reference, num_items, scan_op, initial_value);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (d_in) CubDebugExit(g_allocator.DeviceFree(d_in));\n}\n\n\n/**\n * Test DeviceScan on iterator type\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid TestIterator(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    printf(\"\\nIterator %s %s hipcub::DeviceScan::%s %d items, %s->%s (%d->%d bytes)\\n\",\n        (BACKEND == CDP) ? \"CDP CUB\" : \"CUB\",\n        (std::is_same<InitialValueT, NullType>::value) ? \"Inclusive\" : \"Exclusive\",\n        (std::is_same<ScanOpT, Sum>::value) ? \"Sum\" : \"Scan\",\n        num_items,\n        typeid(InputT).name(), typeid(OutputT).name(), (int) sizeof(InputT), (int) sizeof(OutputT));\n    fflush(stdout);\n\n    // Use a constant iterator as the input\n    InputT val = InputT();\n    ConstantInputIterator<InputT, int> h_in(val);\n\n    // Allocate host arrays\n    OutputT*  h_reference = new OutputT[num_items];\n\n    // Initialize problem and solution\n    Solve(h_in, h_reference, num_items, scan_op, initial_value);\n\n    // Run Test\n    Test<BACKEND>(h_in, h_reference, num_items, scan_op, initial_value);\n    TestFutureInitValueIter<BACKEND>(h_in, h_reference, num_items, scan_op, initial_value);\n\n    // Cleanup\n    if (h_reference) delete[] h_reference;\n}\n\n\n/**\n * Test different gen modes\n */\ntemplate <\n    Backend         BACKEND,\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n    TestPointer<BACKEND, InputT, OutputT>(  num_items, UNIFORM, scan_op, initial_value);\n    TestPointer<BACKEND, InputT, OutputT>(  num_items, RANDOM,  scan_op, initial_value);\n    TestIterator<BACKEND, InputT, OutputT>( num_items, scan_op, initial_value);\n}\n\n\n/**\n * Test different dispatch\n */\ntemplate <\n    typename        InputT,\n    typename        OutputT,\n    typename        ScanOpT,\n    typename        InitialValueT>\nvoid Test(\n    int             num_items,\n    ScanOpT         scan_op,\n    InitialValueT   initial_value)\n{\n#if TEST_CDP == 0\n    Test<CUB, InputT, OutputT>(num_items, scan_op, initial_value);\n#elif TEST_CDP == 1\n    Test<CDP, InputT, OutputT>(num_items, scan_op, initial_value);\n#endif // TEST_CDP\n}\n\n\n/**\n * Test different operators\n */\ntemplate <typename InputT, typename OutputT>\nvoid TestOp(\n    int             num_items,\n    OutputT         identity,\n    OutputT         initial_value)\n{\n    // Exclusive (use identity as initial value because it will dispatch to *Sum variants that don't take initial values)\n    Test<InputT, OutputT>(num_items, hipcub::Sum(), identity);\n    Test<InputT, OutputT>(num_items, hipcub::Max(), identity);\n\n    // Exclusive (non-specialized, so we can test initial-value)\n    Test<InputT, OutputT>(num_items, WrapperFunctor<hipcub::Sum>(hipcub::Sum()), initial_value);\n    Test<InputT, OutputT>(num_items, WrapperFunctor<hipcub::Max>(hipcub::Max()), initial_value);\n\n    // Inclusive (no initial value)\n    Test<InputT, OutputT>(num_items, hipcub::Sum(), NullType());\n    Test<InputT, OutputT>(num_items, hipcub::Max(), NullType());\n}\n\n\n/**\n * Test different input sizes\n */\ntemplate <\n    typename InputT,\n    typename OutputT>\nvoid TestSize(\n    int     num_items,\n    OutputT identity,\n    OutputT initial_value)\n{\n    if (num_items < 0)\n    {\n        TestOp<InputT>(0,        identity, initial_value);\n        TestOp<InputT>(1,        identity, initial_value);\n        TestOp<InputT>(100,      identity, initial_value);\n        TestOp<InputT>(10000,    identity, initial_value);\n        TestOp<InputT>(1000000,  identity, initial_value);\n    }\n    else\n    {\n        TestOp<InputT>(num_items, identity, initial_value);\n    }\n}\n\nclass CustomInputT\n{\n  char m_val{};\n\npublic:\n  __host__ __device__ explicit CustomInputT(char val)\n      : m_val(val)\n  {}\n\n  __host__ __device__ int get() const { return static_cast<int>(m_val); }\n};\n\nclass CustomAccumulatorT\n{\n  int m_val{0};\n  int m_magic_value{42};\n\n  __host__ __device__ CustomAccumulatorT(int val)\n      : m_val(val)\n  {}\n\npublic:\n  __host__ __device__ CustomAccumulatorT()\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomAccumulatorT &in)\n    : m_val(in.is_valid() * in.get())\n    , m_magic_value(in.is_valid() * 42)\n  {}\n\n  __host__ __device__ CustomAccumulatorT(const CustomInputT &in)\n    : m_val(in.get())\n    , m_magic_value(42)\n  {}\n\n  __host__ __device__ void operator=(const CustomInputT &in)\n  {\n    if (this->is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ void operator=(const CustomAccumulatorT &in)\n  {\n    if (this->is_valid() && in.is_valid())\n    {\n      m_val = in.get();\n    }\n  }\n\n  __host__ __device__ CustomAccumulatorT \n  operator+(const CustomInputT &in) const\n  {\n    const int multiplier = this->is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ CustomAccumulatorT\n  operator+(const CustomAccumulatorT &in) const\n  {\n    const int multiplier = this->is_valid() && in.is_valid();\n    return {(m_val + in.get()) * multiplier};\n  }\n\n  __host__ __device__ int get() const { return m_val; }\n\n  __host__ __device__ bool is_valid() const { return m_magic_value == 42; }\n};\n\nclass CustomOutputT\n{\n  int *m_d_ok_count{};\n  int m_expected{};\n\npublic:\n  __host__ __device__ CustomOutputT(int *d_ok_count, int expected)\n      : m_d_ok_count(d_ok_count)\n      , m_expected(expected)\n  {}\n\n  __device__ void operator=(const CustomAccumulatorT &accum) const\n  {\n    const int ok = accum.is_valid() && (accum.get() == m_expected);\n    atomicAdd(m_d_ok_count, ok);\n  }\n};\n\n__global__ void InitializeTestAccumulatorTypes(int num_items,\n                                               int *d_ok_count,\n                                               CustomInputT *d_in,\n                                               CustomOutputT *d_out)\n{\n  const int idx = static_cast<int>(threadIdx.x + blockIdx.x * blockDim.x);\n\n  if (idx < num_items)\n  {\n    d_in[idx] = CustomInputT(1);\n    d_out[idx] = CustomOutputT{d_ok_count, idx};\n  }\n}\n\nvoid TestAccumulatorTypes()\n{\n  const int num_items  = 2 * 1024 * 1024;\n  const int block_size = 256;\n  const int grid_size  = (num_items + block_size - 1) / block_size;\n\n  CustomInputT *d_in{};\n  CustomOutputT *d_out{};\n  CustomAccumulatorT init{};\n  int *d_ok_count{};\n\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_ok_count, sizeof(int)));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_out,\n                                          sizeof(CustomOutputT) * num_items));\n  CubDebugExit(g_allocator.DeviceAllocate((void **)&d_in,\n                                          sizeof(CustomInputT) * num_items));\n\n  InitializeTestAccumulatorTypes<<<grid_size, block_size>>>(num_items,\n                                                            d_ok_count,\n                                                            d_in,\n                                                            d_out);\n\n  std::uint8_t *d_temp_storage{};\n  std::size_t temp_storage_bytes{};\n\n  CubDebugExit(hipcub::DeviceScan::ExclusiveScan(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              hipcub::Sum{},\n                                              init,\n                                              num_items));\n\n  CubDebugExit(\n    g_allocator.DeviceAllocate((void **)&d_temp_storage, temp_storage_bytes));\n  CubDebugExit(hipMemset(d_temp_storage, 1, temp_storage_bytes));\n\n  CubDebugExit(hipcub::DeviceScan::ExclusiveScan(d_temp_storage,\n                                              temp_storage_bytes,\n                                              d_in,\n                                              d_out,\n                                              hipcub::Sum{},\n                                              init,\n                                              num_items));\n\n  int ok{};\n  CubDebugExit(hipMemcpy(&ok, d_ok_count, sizeof(int), hipMemcpyDeviceToHost));\n\n  AssertEquals(ok, num_items);\n\n  CubDebugExit(g_allocator.DeviceFree(d_out));\n  CubDebugExit(g_allocator.DeviceFree(d_in));\n  CubDebugExit(g_allocator.DeviceFree(d_ok_count));\n}\n\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    int num_items = -1;\n\n    // Initialize command line\n    CommandLineArgs args(argc, argv);\n    g_verbose = args.CheckCmdLineFlag(\"v\");\n    args.GetCmdLineArgument(\"n\", num_items);\n    args.GetCmdLineArgument(\"i\", g_timing_iterations);\n\n    // Print usage\n    if (args.CheckCmdLineFlag(\"help\"))\n    {\n        printf(\"%s \"\n            \"[--n=<input items> \"\n            \"[--i=<timing iterations> \"\n            \"[--device=<device-id>] \"\n            \"[--v] \"\n            \"\\n\", argv[0]);\n        exit(0);\n    }\n\n    // Initialize device\n    CubDebugExit(args.DeviceInit());\n    g_device_giga_bandwidth = args.device_giga_bandwidth;\n    printf(\"\\n\");\n\n    // %PARAM% TEST_CDP cdp 0:1\n    // %PARAM% TEST_VALUE_TYPES types 0:1:2\n\n#if TEST_VALUE_TYPES == 0\n\n    // Test different input+output data types\n    TestSize<unsigned char>(num_items, (int)0, (int)99);\n\n    // Test same input+output data types\n    TestSize<unsigned char>(num_items, (unsigned char)0, (unsigned char)99);\n    TestSize<signed char>(num_items, (char)0, (char)99);\n    TestSize<unsigned short>(num_items, (unsigned short)0, (unsigned short)99);\n    TestSize<unsigned int>(num_items, (unsigned int)0, (unsigned int)99);\n    TestSize<unsigned long long>(num_items,\n                                 (unsigned long long)0,\n                                 (unsigned long long)99);\n\n#elif TEST_VALUE_TYPES == 1\n\n    TestSize<uchar2>(num_items, make_uchar2(0, 0), make_uchar2(17, 21));\n    TestSize<char2>(num_items, make_char2(0, 0), make_char2(17, 21));\n    TestSize<ushort2>(num_items, make_ushort2(0, 0), make_ushort2(17, 21));\n    TestSize<uint2>(num_items, make_uint2(0, 0), make_uint2(17, 21));\n    TestSize<ulonglong2>(num_items,\n                         make_ulonglong2(0, 0),\n                         make_ulonglong2(17, 21));\n    TestSize<uchar4>(num_items,\n                     make_uchar4(0, 0, 0, 0),\n                     make_uchar4(17, 21, 32, 85));\n#elif TEST_VALUE_TYPES == 2\n    TestSize<char4>(num_items,\n                    make_char4(0, 0, 0, 0),\n                    make_char4(17, 21, 32, 85));\n\n    TestSize<ushort4>(num_items,\n                      make_ushort4(0, 0, 0, 0),\n                      make_ushort4(17, 21, 32, 85));\n    TestSize<uint4>(num_items,\n                    make_uint4(0, 0, 0, 0),\n                    make_uint4(17, 21, 32, 85));\n    TestSize<ulonglong4>(num_items,\n                         make_ulonglong4(0, 0, 0, 0),\n                         make_ulonglong4(17, 21, 32, 85));\n\n    TestSize<TestFoo>(num_items,\n                      TestFoo::MakeTestFoo(0, 0, 0, 0),\n                      TestFoo::MakeTestFoo(std::numeric_limits<TestFoo::x_t>::max(),\n                                           std::numeric_limits<TestFoo::y_t>::max(),\n                                           std::numeric_limits<TestFoo::z_t>::max(),\n                                           std::numeric_limits<TestFoo::w_t>::max()));\n\n    TestSize<TestBar>(num_items, \n                      TestBar(0, 0), \n                      TestBar(std::numeric_limits<long long>::max(), \n                              std::numeric_limits<int>::max()));\n\n    TestAccumulatorTypes();\n#endif\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cub/test/test_device_scan.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cstdio>\n#include <cstdlib>\n#include <ctime>\n\n#include \"cuda_fp16.h\"\n#include \"helper_cuda.h\"\n\n#define NUM_OF_BLOCKS  128\n#define NUM_OF_THREADS 128\n\n__forceinline__ __device__ void reduceInShared_intrinsics(half2 *const v)\n{\n    if (threadIdx.x < 64)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 64]);\n    __syncthreads();\n    if (threadIdx.x < 32)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 32]);\n    __syncthreads();\n    if (threadIdx.x < 16)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 16]);\n    __syncthreads();\n    if (threadIdx.x < 8)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 8]);\n    __syncthreads();\n    if (threadIdx.x < 4)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 4]);\n    __syncthreads();\n    if (threadIdx.x < 2)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 2]);\n    __syncthreads();\n    if (threadIdx.x < 1)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 1]);\n    __syncthreads();\n}\n\n__forceinline__ __device__ void reduceInShared_native(half2 *const v)\n{\n    if (threadIdx.x < 64)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 64];\n    __syncthreads();\n    if (threadIdx.x < 32)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 32];\n    __syncthreads();\n    if (threadIdx.x < 16)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 16];\n    __syncthreads();\n    if (threadIdx.x < 8)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 8];\n    __syncthreads();\n    if (threadIdx.x < 4)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 4];\n    __syncthreads();\n    if (threadIdx.x < 2)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 2];\n    __syncthreads();\n    if (threadIdx.x < 1)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 1];\n    __syncthreads();\n}\n\n__global__ void\nscalarProductKernel_intrinsics(half2 const *const a, half2 const *const b, float *const results, size_t const size)\n{\n    const int        stride = gridDim.x * blockDim.x;\n    __shared__ half2 shArray[NUM_OF_THREADS];\n\n    shArray[threadIdx.x] = __float2half2_rn(0.f);\n    half2 value          = __float2half2_rn(0.f);\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < size; i += stride) {\n        value = __hfma2(a[i], b[i], value);\n    }\n\n    shArray[threadIdx.x] = value;\n    __syncthreads();\n    reduceInShared_intrinsics(shArray);\n\n    if (threadIdx.x == 0) {\n        half2 result        = shArray[0];\n        float f_result      = __low2float(result) + __high2float(result);\n        results[blockIdx.x] = f_result;\n    }\n}\n\n__global__ void\nscalarProductKernel_native(half2 const *const a, half2 const *const b, float *const results, size_t const size)\n{\n    const int        stride = gridDim.x * blockDim.x;\n    __shared__ half2 shArray[NUM_OF_THREADS];\n\n    half2 value(0.f, 0.f);\n    shArray[threadIdx.x] = value;\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < size; i += stride) {\n        value = a[i] * b[i] + value;\n    }\n\n    shArray[threadIdx.x] = value;\n    __syncthreads();\n    reduceInShared_native(shArray);\n\n    if (threadIdx.x == 0) {\n        half2 result        = shArray[0];\n        float f_result      = (float)result.y + (float)result.x;\n        results[blockIdx.x] = f_result;\n    }\n}\n\nvoid generateInput(half2 *a, size_t size)\n{\n    for (size_t i = 0; i < size; ++i) {\n        half2 temp;\n        temp.x = static_cast<float>(rand() % 4);\n        temp.y = static_cast<float>(rand() % 2);\n        a[i]   = temp;\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    srand((unsigned int)time(NULL));\n    size_t size = NUM_OF_BLOCKS * NUM_OF_THREADS * 16;\n\n    half2 *vec[2];\n    half2 *devVec[2];\n\n    float *results;\n    float *devResults;\n\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp devProp;\n    checkCudaErrors(cudaGetDeviceProperties(&devProp, devID));\n\n    if (devProp.major < 5 || (devProp.major == 5 && devProp.minor < 3)) {\n        printf(\"ERROR: fp16ScalarProduct requires GPU devices with compute SM 5.3 or \"\n               \"higher.\\n\");\n        return EXIT_WAIVED;\n    }\n\n    for (int i = 0; i < 2; ++i) {\n        checkCudaErrors(cudaMallocHost((void **)&vec[i], size * sizeof *vec[i]));\n        checkCudaErrors(cudaMalloc((void **)&devVec[i], size * sizeof *devVec[i]));\n    }\n\n    checkCudaErrors(cudaMallocHost((void **)&results, NUM_OF_BLOCKS * sizeof *results));\n    checkCudaErrors(cudaMalloc((void **)&devResults, NUM_OF_BLOCKS * sizeof *devResults));\n\n    for (int i = 0; i < 2; ++i) {\n        generateInput(vec[i], size);\n        checkCudaErrors(cudaMemcpy(devVec[i], vec[i], size * sizeof *vec[i], cudaMemcpyHostToDevice));\n    }\n\n    scalarProductKernel_native<<<NUM_OF_BLOCKS, NUM_OF_THREADS>>>(devVec[0], devVec[1], devResults, size);\n\n    checkCudaErrors(cudaMemcpy(results, devResults, NUM_OF_BLOCKS * sizeof *results, cudaMemcpyDeviceToHost));\n\n    float result_native = 0;\n    for (int i = 0; i < NUM_OF_BLOCKS; ++i) {\n        result_native += results[i];\n    }\n    printf(\"Result native operators\\t: %f \\n\", result_native);\n\n    scalarProductKernel_intrinsics<<<NUM_OF_BLOCKS, NUM_OF_THREADS>>>(devVec[0], devVec[1], devResults, size);\n\n    checkCudaErrors(cudaMemcpy(results, devResults, NUM_OF_BLOCKS * sizeof *results, cudaMemcpyDeviceToHost));\n\n    float result_intrinsics = 0;\n    for (int i = 0; i < NUM_OF_BLOCKS; ++i) {\n        result_intrinsics += results[i];\n    }\n    printf(\"Result intrinsics\\t: %f \\n\", result_intrinsics);\n\n    printf(\"&&&& fp16ScalarProduct %s\\n\", (fabs(result_intrinsics - result_native) < 0.00001) ? \"PASSED\" : \"FAILED\");\n\n    for (int i = 0; i < 2; ++i) {\n        checkCudaErrors(cudaFree(devVec[i]));\n        checkCudaErrors(cudaFreeHost(vec[i]));\n    }\n\n    checkCudaErrors(cudaFree(devResults));\n    checkCudaErrors(cudaFreeHost(results));\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cstdio>\n#include <cstdlib>\n#include <ctime>\n\n#include \"hip/hip_fp16.h\"\n#include \"hip/hip_runtime_api.h\"\n\n#define NUM_OF_BLOCKS  128\n#define NUM_OF_THREADS 128\n\n__forceinline__ __device__ void reduceInShared_intrinsics(half2 *const v)\n{\n    if (threadIdx.x < 64)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 64]);\n    __syncthreads();\n    if (threadIdx.x < 32)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 32]);\n    __syncthreads();\n    if (threadIdx.x < 16)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 16]);\n    __syncthreads();\n    if (threadIdx.x < 8)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 8]);\n    __syncthreads();\n    if (threadIdx.x < 4)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 4]);\n    __syncthreads();\n    if (threadIdx.x < 2)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 2]);\n    __syncthreads();\n    if (threadIdx.x < 1)\n        v[threadIdx.x] = __hadd2(v[threadIdx.x], v[threadIdx.x + 1]);\n    __syncthreads();\n}\n\n__forceinline__ __device__ void reduceInShared_native(half2 *const v)\n{\n    if (threadIdx.x < 64)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 64];\n    __syncthreads();\n    if (threadIdx.x < 32)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 32];\n    __syncthreads();\n    if (threadIdx.x < 16)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 16];\n    __syncthreads();\n    if (threadIdx.x < 8)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 8];\n    __syncthreads();\n    if (threadIdx.x < 4)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 4];\n    __syncthreads();\n    if (threadIdx.x < 2)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 2];\n    __syncthreads();\n    if (threadIdx.x < 1)\n        v[threadIdx.x] = v[threadIdx.x] + v[threadIdx.x + 1];\n    __syncthreads();\n}\n\n__global__ void\nscalarProductKernel_intrinsics(half2 const *const a, half2 const *const b, float *const results, size_t const size)\n{\n    const int        stride = gridDim.x * blockDim.x;\n    __shared__ half2 shArray[NUM_OF_THREADS];\n\n    shArray[threadIdx.x] = __float2half2_rn(0.f);\n    half2 value          = __float2half2_rn(0.f);\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < size; i += stride) {\n        value = __hfma2(a[i], b[i], value);\n    }\n\n    shArray[threadIdx.x] = value;\n    __syncthreads();\n    reduceInShared_intrinsics(shArray);\n\n    if (threadIdx.x == 0) {\n        half2 result        = shArray[0];\n        float f_result      = __low2float(result) + __high2float(result);\n        results[blockIdx.x] = f_result;\n    }\n}\n\n__global__ void\nscalarProductKernel_native(half2 const *const a, half2 const *const b, float *const results, size_t const size)\n{\n    const int        stride = gridDim.x * blockDim.x;\n    __shared__ half2 shArray[NUM_OF_THREADS];\n\n    half2 value(0.f, 0.f);\n    shArray[threadIdx.x] = value;\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < size; i += stride) {\n        value = a[i] * b[i] + value;\n    }\n\n    shArray[threadIdx.x] = value;\n    __syncthreads();\n    reduceInShared_native(shArray);\n\n    if (threadIdx.x == 0) {\n        half2 result        = shArray[0];\n        float f_result      = (float)result.y + (float)result.x;\n        results[blockIdx.x] = f_result;\n    }\n}\n\nvoid generateInput(half2 *a, size_t size)\n{\n    for (size_t i = 0; i < size; ++i) {\n        half2 temp;\n        temp.x = static_cast<float>(rand() % 4);\n        temp.y = static_cast<float>(rand() % 2);\n        a[i]   = temp;\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    srand((unsigned int)time(NULL));\n    size_t size = NUM_OF_BLOCKS * NUM_OF_THREADS * 16;\n\n    half2 *vec[2];\n    half2 *devVec[2];\n\n    float *results;\n    float *devResults;\n\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t devProp;\n    checkCudaErrors(hipGetDeviceProperties(&devProp, devID));\n\n    if (devProp.major < 5 || (devProp.major == 5 && devProp.minor < 3)) {\n        printf(\"ERROR: fp16ScalarProduct requires GPU devices with compute SM 5.3 or \"\n               \"higher.\\n\");\n        return EXIT_WAIVED;\n    }\n\n    for (int i = 0; i < 2; ++i) {\n        checkCudaErrors(hipHostMalloc((void **)&vec[i], size * sizeof *vec[i]));\n        checkCudaErrors(hipMalloc((void **)&devVec[i], size * sizeof *devVec[i]));\n    }\n\n    checkCudaErrors(hipHostMalloc((void **)&results, NUM_OF_BLOCKS * sizeof *results));\n    checkCudaErrors(hipMalloc((void **)&devResults, NUM_OF_BLOCKS * sizeof *devResults));\n\n    for (int i = 0; i < 2; ++i) {\n        generateInput(vec[i], size);\n        checkCudaErrors(hipMemcpy(devVec[i], vec[i], size * sizeof *vec[i], hipMemcpyHostToDevice));\n    }\n\n    scalarProductKernel_native<<<NUM_OF_BLOCKS, NUM_OF_THREADS>>>(devVec[0], devVec[1], devResults, size);\n\n    checkCudaErrors(hipMemcpy(results, devResults, NUM_OF_BLOCKS * sizeof *results, hipMemcpyDeviceToHost));\n\n    float result_native = 0;\n    for (int i = 0; i < NUM_OF_BLOCKS; ++i) {\n        result_native += results[i];\n    }\n    printf(\"Result native operators\\t: %f \\n\", result_native);\n\n    scalarProductKernel_intrinsics<<<NUM_OF_BLOCKS, NUM_OF_THREADS>>>(devVec[0], devVec[1], devResults, size);\n\n    checkCudaErrors(hipMemcpy(results, devResults, NUM_OF_BLOCKS * sizeof *results, hipMemcpyDeviceToHost));\n\n    float result_intrinsics = 0;\n    for (int i = 0; i < NUM_OF_BLOCKS; ++i) {\n        result_intrinsics += results[i];\n    }\n    printf(\"Result intrinsics\\t: %f \\n\", result_intrinsics);\n\n    printf(\"&&&& fp16ScalarProduct %s\\n\", (fabs(result_intrinsics - result_native) < 0.00001) ? \"PASSED\" : \"FAILED\");\n\n    for (int i = 0; i < 2; ++i) {\n        checkCudaErrors(hipFree(devVec[i]));\n        checkCudaErrors(hipHostFree(vec[i]));\n    }\n\n    checkCudaErrors(hipFree(devResults));\n    checkCudaErrors(hipHostFree(results));\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/fp16ScalarProduct/fp16ScalarProduct.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n * Host code.\n *\n * This sample implements matrix multiplication which makes use of shared memory\n * to ensure data reuse, the matrix multiplication is done using tiling approach.\n * It has been written for clarity of exposition to illustrate various CUDA programming\n * principles, not with the goal of providing the most performant generic kernel for matrix multiplication.\n * See also:\n * V. Volkov and J. Demmel, \"Benchmarking GPUs to tune dense linear algebra,\"\n * in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),\n * Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_profiler_api.h>\n#include <cuda_runtime.h>\n\n// Helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulCUDA(float *C, float *A, float *B, int wA, int wB)\n{\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nvoid ConstantInit(float *data, int size, float val)\n{\n    for (int i = 0; i < size; ++i) {\n        data[i] = val;\n    }\n}\n\n/**\n * Run a simple test of matrix multiplication using CUDA\n */\nint MatrixMultiply(int argc, char **argv, int block_size, const dim3 &dimsA, const dim3 &dimsB)\n{\n    // Allocate host memory for matrices A and B\n    unsigned int size_A     = dimsA.x * dimsA.y;\n    unsigned int mem_size_A = sizeof(float) * size_A;\n    float       *h_A;\n    checkCudaErrors(cudaMallocHost(&h_A, mem_size_A));\n    unsigned int size_B     = dimsB.x * dimsB.y;\n    unsigned int mem_size_B = sizeof(float) * size_B;\n    float       *h_B;\n    checkCudaErrors(cudaMallocHost(&h_B, mem_size_B));\n    cudaStream_t stream;\n\n    // Initialize host memory\n    const float valB = 0.01f;\n    ConstantInit(h_A, size_A, 1.0f);\n    ConstantInit(h_B, size_B, valB);\n\n    // Allocate device memory\n    float *d_A, *d_B, *d_C;\n\n    // Allocate host matrix C\n    dim3         dimsC(dimsB.x, dimsA.y, 1);\n    unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);\n    float       *h_C;\n    checkCudaErrors(cudaMallocHost(&h_C, mem_size_C));\n\n    if (h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host matrix C!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_A), mem_size_A));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_B), mem_size_B));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_C), mem_size_C));\n    // Allocate CUDA events that we'll use for timing\n    cudaEvent_t start, stop;\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // copy host memory to device\n    checkCudaErrors(cudaMemcpyAsync(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemcpyAsync(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice, stream));\n\n    // Setup execution parameters\n    dim3 threads(block_size, block_size);\n    dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);\n\n    // Create and start timer\n    printf(\"Computing result using CUDA Kernel...\\n\");\n\n    // Performs warmup operation using matrixMul CUDA kernel\n    if (block_size == 16) {\n        MatrixMulCUDA<16><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n    }\n    else {\n        MatrixMulCUDA<32><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n    }\n\n    printf(\"done\\n\");\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    // Record the start event\n    checkCudaErrors(cudaEventRecord(start, stream));\n\n    // Execute the kernel\n    int nIter = 300;\n\n    for (int j = 0; j < nIter; j++) {\n        if (block_size == 16) {\n            MatrixMulCUDA<16><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        }\n        else {\n            MatrixMulCUDA<32><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        }\n    }\n\n    // Record the stop event\n    checkCudaErrors(cudaEventRecord(stop, stream));\n\n    // Wait for the stop event to complete\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(cudaEventElapsedTime(&msecTotal, start, stop));\n\n    // Compute and print the performance\n    float  msecPerMatrixMul = msecTotal / nIter;\n    double flopsPerMatrixMul =\n        2.0 * static_cast<double>(dimsA.x) * static_cast<double>(dimsA.y) * static_cast<double>(dimsB.x);\n    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);\n    printf(\"Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops,\"\n           \" WorkgroupSize= %u threads/block\\n\",\n           gigaFlops,\n           msecPerMatrixMul,\n           flopsPerMatrixMul,\n           threads.x * threads.y);\n\n    // Copy result from device to host\n    checkCudaErrors(cudaMemcpyAsync(h_C, d_C, mem_size_C, cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    printf(\"Checking computed result for correctness: \");\n    bool correct = true;\n\n    // test relative error by the formula\n    //     |<x, y>_cpu - <x,y>_gpu|/<|x|, |y|>  < eps\n    double eps = 1.e-6; // machine zero\n\n    for (int i = 0; i < static_cast<int>(dimsC.x * dimsC.y); i++) {\n        double abs_err    = fabs(h_C[i] - (dimsA.x * valB));\n        double dot_length = dimsA.x;\n        double abs_val    = fabs(h_C[i]);\n        double rel_err    = abs_err / abs_val / dot_length;\n\n        if (rel_err > eps) {\n            printf(\"Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\\n\", i, h_C[i], dimsA.x * valB, eps);\n            correct = false;\n        }\n    }\n\n    printf(\"%s\\n\", correct ? \"Result = PASS\" : \"Result = FAIL\");\n\n    // Clean up memory\n    checkCudaErrors(cudaFreeHost(h_A));\n    checkCudaErrors(cudaFreeHost(h_B));\n    checkCudaErrors(cudaFreeHost(h_C));\n    checkCudaErrors(cudaFree(d_A));\n    checkCudaErrors(cudaFree(d_B));\n    checkCudaErrors(cudaFree(d_C));\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance \"\n           \"measurements. Results may vary when GPU Boost is enabled.\\n\");\n\n    if (correct) {\n        return EXIT_SUCCESS;\n    }\n    else {\n        return EXIT_FAILURE;\n    }\n}\n\n\n/**\n * Program main\n */\nint main(int argc, char **argv)\n{\n    printf(\"[Matrix Multiply Using CUDA] - Starting...\\n\");\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        printf(\"      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\\n\");\n        printf(\"      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\\n\");\n        printf(\"  Note: Outer matrix dimensions of A & B matrices\"\n               \" must be equal.\\n\");\n\n        exit(EXIT_SUCCESS);\n    }\n\n    // This will pick the best possible CUDA capable device, otherwise\n    // override the device ID based on input provided at the command line\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int block_size = 32;\n\n    dim3 dimsA(5 * 2 * block_size, 5 * 2 * block_size, 1);\n    dim3 dimsB(5 * 4 * block_size, 5 * 2 * block_size, 1);\n\n    // width of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wA\")) {\n        dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wA\");\n    }\n\n    // height of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hA\")) {\n        dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hA\");\n    }\n\n    // width of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wB\")) {\n        dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wB\");\n    }\n\n    // height of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hB\")) {\n        dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hB\");\n    }\n\n    if (dimsA.x != dimsB.y) {\n        printf(\"Error: outer matrix dimensions must be equal. (%d != %d)\\n\", dimsA.x, dimsB.y);\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"MatrixA(%d,%d), MatrixB(%d,%d)\\n\", dimsA.x, dimsA.y, dimsB.x, dimsB.y);\n\n    checkCudaErrors(cudaProfilerStart());\n    int matrix_result = MatrixMultiply(argc, argv, block_size, dimsA, dimsB);\n    checkCudaErrors(cudaProfilerStop());\n\n    exit(matrix_result);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n * Host code.\n *\n * This sample implements matrix multiplication which makes use of shared memory\n * to ensure data reuse, the matrix multiplication is done using tiling approach.\n * It has been written for clarity of exposition to illustrate various CUDA programming\n * principles, not with the goal of providing the most performant generic kernel for matrix multiplication.\n * See also:\n * V. Volkov and J. Demmel, \"Benchmarking GPUs to tune dense linear algebra,\"\n * in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),\n * Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_runtime.h>\n\n// Helper functions and utilities to work with CUDA\n#include <helper_functions.h>\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulCUDA(float *C, float *A, float *B, int wA, int wB)\n{\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nvoid ConstantInit(float *data, int size, float val)\n{\n    for (int i = 0; i < size; ++i) {\n        data[i] = val;\n    }\n}\n\n/**\n * Run a simple test of matrix multiplication using CUDA\n */\nint MatrixMultiply(int argc, char **argv, int block_size, const dim3 &dimsA, const dim3 &dimsB)\n{\n    // Allocate host memory for matrices A and B\n    unsigned int size_A     = dimsA.x * dimsA.y;\n    unsigned int mem_size_A = sizeof(float) * size_A;\n    float       *h_A;\n    checkCudaErrors(hipHostMalloc(&h_A, mem_size_A));\n    unsigned int size_B     = dimsB.x * dimsB.y;\n    unsigned int mem_size_B = sizeof(float) * size_B;\n    float       *h_B;\n    checkCudaErrors(hipHostMalloc(&h_B, mem_size_B));\n    hipStream_t stream;\n\n    // Initialize host memory\n    const float valB = 0.01f;\n    ConstantInit(h_A, size_A, 1.0f);\n    ConstantInit(h_B, size_B, valB);\n\n    // Allocate device memory\n    float *d_A, *d_B, *d_C;\n\n    // Allocate host matrix C\n    dim3         dimsC(dimsB.x, dimsA.y, 1);\n    unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);\n    float       *h_C;\n    checkCudaErrors(hipHostMalloc(&h_C, mem_size_C));\n\n    if (h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host matrix C!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_A), mem_size_A));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_B), mem_size_B));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_C), mem_size_C));\n    // Allocate CUDA events that we'll use for timing\n    hipEvent_t start, stop;\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // copy host memory to device\n    checkCudaErrors(hipMemcpyAsync(d_A, h_A, mem_size_A, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemcpyAsync(d_B, h_B, mem_size_B, hipMemcpyHostToDevice, stream));\n\n    // Setup execution parameters\n    dim3 threads(block_size, block_size);\n    dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);\n\n    // Create and start timer\n    printf(\"Computing result using CUDA Kernel...\\n\");\n\n    // Performs warmup operation using matrixMul CUDA kernel\n    if (block_size == 16) {\n        MatrixMulCUDA<16><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n    }\n    else {\n        MatrixMulCUDA<32><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n    }\n\n    printf(\"done\\n\");\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    // Record the start event\n    checkCudaErrors(hipEventRecord(start, stream));\n\n    // Execute the kernel\n    int nIter = 300;\n\n    for (int j = 0; j < nIter; j++) {\n        if (block_size == 16) {\n            MatrixMulCUDA<16><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        }\n        else {\n            MatrixMulCUDA<32><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        }\n    }\n\n    // Record the stop event\n    checkCudaErrors(hipEventRecord(stop, stream));\n\n    // Wait for the stop event to complete\n    checkCudaErrors(hipEventSynchronize(stop));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(hipEventElapsedTime(&msecTotal, start, stop));\n\n    // Compute and print the performance\n    float  msecPerMatrixMul = msecTotal / nIter;\n    double flopsPerMatrixMul =\n        2.0 * static_cast<double>(dimsA.x) * static_cast<double>(dimsA.y) * static_cast<double>(dimsB.x);\n    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);\n    printf(\"Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops,\"\n           \" WorkgroupSize= %u threads/block\\n\",\n           gigaFlops,\n           msecPerMatrixMul,\n           flopsPerMatrixMul,\n           threads.x * threads.y);\n\n    // Copy result from device to host\n    checkCudaErrors(hipMemcpyAsync(h_C, d_C, mem_size_C, hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    printf(\"Checking computed result for correctness: \");\n    bool correct = true;\n\n    // test relative error by the formula\n    //     |<x, y>_cpu - <x,y>_gpu|/<|x|, |y|>  < eps\n    double eps = 1.e-6; // machine zero\n\n    for (int i = 0; i < static_cast<int>(dimsC.x * dimsC.y); i++) {\n        double abs_err    = fabs(h_C[i] - (dimsA.x * valB));\n        double dot_length = dimsA.x;\n        double abs_val    = fabs(h_C[i]);\n        double rel_err    = abs_err / abs_val / dot_length;\n\n        if (rel_err > eps) {\n            printf(\"Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\\n\", i, h_C[i], dimsA.x * valB, eps);\n            correct = false;\n        }\n    }\n\n    printf(\"%s\\n\", correct ? \"Result = PASS\" : \"Result = FAIL\");\n\n    // Clean up memory\n    checkCudaErrors(hipHostFree(h_A));\n    checkCudaErrors(hipHostFree(h_B));\n    checkCudaErrors(hipHostFree(h_C));\n    checkCudaErrors(hipFree(d_A));\n    checkCudaErrors(hipFree(d_B));\n    checkCudaErrors(hipFree(d_C));\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance \"\n           \"measurements. Results may vary when GPU Boost is enabled.\\n\");\n\n    if (correct) {\n        return EXIT_SUCCESS;\n    }\n    else {\n        return EXIT_FAILURE;\n    }\n}\n\n\n/**\n * Program main\n */\nint main(int argc, char **argv)\n{\n    printf(\"[Matrix Multiply Using CUDA] - Starting...\\n\");\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        printf(\"      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\\n\");\n        printf(\"      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\\n\");\n        printf(\"  Note: Outer matrix dimensions of A & B matrices\"\n               \" must be equal.\\n\");\n\n        exit(EXIT_SUCCESS);\n    }\n\n    // This will pick the best possible CUDA capable device, otherwise\n    // override the device ID based on input provided at the command line\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int block_size = 32;\n\n    dim3 dimsA(5 * 2 * block_size, 5 * 2 * block_size, 1);\n    dim3 dimsB(5 * 4 * block_size, 5 * 2 * block_size, 1);\n\n    // width of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wA\")) {\n        dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wA\");\n    }\n\n    // height of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hA\")) {\n        dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hA\");\n    }\n\n    // width of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wB\")) {\n        dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wB\");\n    }\n\n    // height of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hB\")) {\n        dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hB\");\n    }\n\n    if (dimsA.x != dimsB.y) {\n        printf(\"Error: outer matrix dimensions must be equal. (%d != %d)\\n\", dimsA.x, dimsB.y);\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"MatrixA(%d,%d), MatrixB(%d,%d)\\n\", dimsA.x, dimsA.y, dimsB.x, dimsB.y);\n\n    checkCudaErrors(hipProfilerStart());\n    int matrix_result = MatrixMultiply(argc, argv, block_size, dimsA, dimsB);\n    checkCudaErrors(hipProfilerStop());\n\n    exit(matrix_result);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/matrixMul/matrixMul.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* pitchLinearTexture\n *\n * This example demonstrates how to use textures bound to pitch linear memory.\n * It performs a shift of matrix elements using wrap addressing mode (aka\n * periodic boundary conditions) on two arrays, a pitch linear and a CUDA array,\n * in order to highlight the differences in using each.\n *\n * Textures binding to pitch linear memory is a new feature in CUDA 2.2,\n * and allows use of texture features such as wrap addressing mode and\n * filtering which are not possible with textures bound to regular linear memory\n */\n\n// includes, system\n#include <stdio.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n\n#define NUM_REPS 100 // number of repetitions performed\n#define TILE_DIM 16  // tile/block size\n\nconst char *sSDKsample = \"simplePitchLinearTexture\";\n\n// Auto-Verification Code\nbool bTestResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n// NB: (1) The second argument \"pitch\" is in elements, not bytes\n//     (2) normalized coordinates are used (required for wrap address mode)\n////////////////////////////////////////////////////////////////////////////////\n//! Shifts matrix elements using pitch linear array\n//! @param odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nshiftPitchLinear(float *odata, int pitch, int width, int height, int shiftX, int shiftY, cudaTextureObject_t texRefPL)\n{\n    int xid = blockIdx.x * blockDim.x + threadIdx.x;\n    int yid = blockIdx.y * blockDim.y + threadIdx.y;\n\n    odata[yid * pitch + xid] = tex2D<float>(texRefPL, (xid + shiftX) / (float)width, (yid + shiftY) / (float)height);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Shifts matrix elements using regular array\n//! @param odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nshiftArray(float *odata, int pitch, int width, int height, int shiftX, int shiftY, cudaTextureObject_t texRefArray)\n{\n    int xid = blockIdx.x * blockDim.x + threadIdx.x;\n    int yid = blockIdx.y * blockDim.y + threadIdx.y;\n\n    odata[yid * pitch + xid] = tex2D<float>(texRefArray, (xid + shiftX) / (float)width, (yid + shiftY) / (float)height);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\\n\", sSDKsample);\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, bTestResult ? \"OK\" : \"ERROR!\");\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    // Set array size\n    const int nx = 2048;\n    const int ny = 2048;\n\n    // Setup shifts applied to x and y data\n    const int x_shift = 5;\n    const int y_shift = 7;\n\n    if ((nx % TILE_DIM != 0) || (ny % TILE_DIM != 0)) {\n        printf(\"nx and ny must be multiples of TILE_DIM\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Setup execution configuration parameters\n    dim3 dimGrid(nx / TILE_DIM, ny / TILE_DIM), dimBlock(TILE_DIM, TILE_DIM);\n\n    // This will pick the best possible CUDA capable device\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // CUDA events for timing\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    // Host allocation and initialization\n    float *h_idata = (float *)malloc(sizeof(float) * nx * ny);\n    float *h_odata = (float *)malloc(sizeof(float) * nx * ny);\n    float *gold    = (float *)malloc(sizeof(float) * nx * ny);\n\n    for (int i = 0; i < nx * ny; ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // Device memory allocation\n    // Pitch linear input data\n    float *d_idataPL;\n    size_t d_pitchBytes;\n\n    checkCudaErrors(cudaMallocPitch((void **)&d_idataPL, &d_pitchBytes, nx * sizeof(float), ny));\n\n    // Array input data\n    cudaArray            *d_idataArray;\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();\n\n    checkCudaErrors(cudaMallocArray(&d_idataArray, &channelDesc, nx, ny));\n\n    // Pitch linear output data\n    float *d_odata;\n    checkCudaErrors(cudaMallocPitch((void **)&d_odata, &d_pitchBytes, nx * sizeof(float), ny));\n\n    // Copy host data to device\n    // Pitch linear\n    size_t h_pitchBytes = nx * sizeof(float);\n\n    checkCudaErrors(\n        cudaMemcpy2D(d_idataPL, d_pitchBytes, h_idata, h_pitchBytes, nx * sizeof(float), ny, cudaMemcpyHostToDevice));\n\n    // Array\n    checkCudaErrors(cudaMemcpyToArray(d_idataArray, 0, 0, h_idata, nx * ny * sizeof(float), cudaMemcpyHostToDevice));\n\n    cudaTextureObject_t texRefPL;\n    cudaTextureObject_t texRefArray;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                  = cudaResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = d_idataPL;\n    texRes.res.pitch2D.desc         = channelDesc;\n    texRes.res.pitch2D.width        = nx;\n    texRes.res.pitch2D.height       = ny;\n    texRes.res.pitch2D.pitchInBytes = h_pitchBytes;\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&texRefPL, &texRes, &texDescr, NULL));\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n    texRes.resType            = cudaResourceTypeArray;\n    texRes.res.array.array    = d_idataArray;\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n    checkCudaErrors(cudaCreateTextureObject(&texRefArray, &texRes, &texDescr, NULL));\n\n    // Reference calculation\n    for (int j = 0; j < ny; ++j) {\n        int jshift = (j + y_shift) % ny;\n\n        for (int i = 0; i < nx; ++i) {\n            int ishift       = (i + x_shift) % nx;\n            gold[j * nx + i] = h_idata[jshift * nx + ishift];\n        }\n    }\n\n    // Run ShiftPitchLinear kernel\n    checkCudaErrors(cudaMemset2D(d_odata, d_pitchBytes, 0, nx * sizeof(float), ny));\n\n    checkCudaErrors(cudaEventRecord(start, 0));\n\n    for (int i = 0; i < NUM_REPS; ++i) {\n        shiftPitchLinear<<<dimGrid, dimBlock>>>(\n            d_odata, (int)(d_pitchBytes / sizeof(float)), nx, ny, x_shift, y_shift, texRefPL);\n    }\n\n    checkCudaErrors(cudaEventRecord(stop, 0));\n    checkCudaErrors(cudaEventSynchronize(stop));\n    float timePL;\n    checkCudaErrors(cudaEventElapsedTime(&timePL, start, stop));\n\n    // Check results\n    checkCudaErrors(\n        cudaMemcpy2D(h_odata, h_pitchBytes, d_odata, d_pitchBytes, nx * sizeof(float), ny, cudaMemcpyDeviceToHost));\n\n    bool res = compareData(gold, h_odata, nx * ny, 0.0f, 0.15f);\n\n    bTestResult = true;\n\n    if (res == false) {\n        printf(\"*** shiftPitchLinear failed ***\\n\");\n        bTestResult = false;\n    }\n\n    // Run ShiftArray kernel\n    checkCudaErrors(cudaMemset2D(d_odata, d_pitchBytes, 0, nx * sizeof(float), ny));\n    checkCudaErrors(cudaEventRecord(start, 0));\n\n    for (int i = 0; i < NUM_REPS; ++i) {\n        shiftArray<<<dimGrid, dimBlock>>>(\n            d_odata, (int)(d_pitchBytes / sizeof(float)), nx, ny, x_shift, y_shift, texRefArray);\n    }\n\n    checkCudaErrors(cudaEventRecord(stop, 0));\n    checkCudaErrors(cudaEventSynchronize(stop));\n    float timeArray;\n    checkCudaErrors(cudaEventElapsedTime(&timeArray, start, stop));\n\n    // Check results\n    checkCudaErrors(\n        cudaMemcpy2D(h_odata, h_pitchBytes, d_odata, d_pitchBytes, nx * sizeof(float), ny, cudaMemcpyDeviceToHost));\n    res = compareData(gold, h_odata, nx * ny, 0.0f, 0.15f);\n\n    if (res == false) {\n        printf(\"*** shiftArray failed ***\\n\");\n        bTestResult = false;\n    }\n\n    float bandwidthPL    = 2.f * 1000.f * nx * ny * sizeof(float) / (1.e+9f) / (timePL / NUM_REPS);\n    float bandwidthArray = 2.f * 1000.f * nx * ny * sizeof(float) / (1.e+9f) / (timeArray / NUM_REPS);\n\n    printf(\"\\nBandwidth (GB/s) for pitch linear: %.2e; for array: %.2e\\n\", bandwidthPL, bandwidthArray);\n\n    float fetchRatePL    = nx * ny / 1.e+6f / (timePL / (1000.0f * NUM_REPS));\n    float fetchRateArray = nx * ny / 1.e+6f / (timeArray / (1000.0f * NUM_REPS));\n\n    printf(\"\\nTexture fetch rate (Mpix/s) for pitch linear: \"\n           \"%.2e; for array: %.2e\\n\\n\",\n           fetchRatePL,\n           fetchRateArray);\n\n    // Cleanup\n    free(h_idata);\n    free(h_odata);\n    free(gold);\n\n    checkCudaErrors(cudaDestroyTextureObject(texRefPL));\n    checkCudaErrors(cudaDestroyTextureObject(texRefArray));\n    checkCudaErrors(cudaFree(d_idataPL));\n    checkCudaErrors(cudaFreeArray(d_idataArray));\n    checkCudaErrors(cudaFree(d_odata));\n\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* pitchLinearTexture\n *\n * This example demonstrates how to use textures bound to pitch linear memory.\n * It performs a shift of matrix elements using wrap addressing mode (aka\n * periodic boundary conditions) on two arrays, a pitch linear and a CUDA array,\n * in order to highlight the differences in using each.\n *\n * Textures binding to pitch linear memory is a new feature in CUDA 2.2,\n * and allows use of texture features such as wrap addressing mode and\n * filtering which are not possible with textures bound to regular linear memory\n */\n\n// includes, system\n#include <stdio.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n\n#define NUM_REPS 100 // number of repetitions performed\n#define TILE_DIM 16  // tile/block size\n\nconst char *sSDKsample = \"simplePitchLinearTexture\";\n\n// Auto-Verification Code\nbool bTestResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n// NB: (1) The second argument \"pitch\" is in elements, not bytes\n//     (2) normalized coordinates are used (required for wrap address mode)\n////////////////////////////////////////////////////////////////////////////////\n//! Shifts matrix elements using pitch linear array\n//! @param odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nshiftPitchLinear(float *odata, int pitch, int width, int height, int shiftX, int shiftY, hipTextureObject_t texRefPL)\n{\n    int xid = blockIdx.x * blockDim.x + threadIdx.x;\n    int yid = blockIdx.y * blockDim.y + threadIdx.y;\n\n    odata[yid * pitch + xid] = tex2D<float>(texRefPL, (xid + shiftX) / (float)width, (yid + shiftY) / (float)height);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Shifts matrix elements using regular array\n//! @param odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nshiftArray(float *odata, int pitch, int width, int height, int shiftX, int shiftY, hipTextureObject_t texRefArray)\n{\n    int xid = blockIdx.x * blockDim.x + threadIdx.x;\n    int yid = blockIdx.y * blockDim.y + threadIdx.y;\n\n    odata[yid * pitch + xid] = tex2D<float>(texRefArray, (xid + shiftX) / (float)width, (yid + shiftY) / (float)height);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\\n\", sSDKsample);\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, bTestResult ? \"OK\" : \"ERROR!\");\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    // Set array size\n    const int nx = 2048;\n    const int ny = 2048;\n\n    // Setup shifts applied to x and y data\n    const int x_shift = 5;\n    const int y_shift = 7;\n\n    if ((nx % TILE_DIM != 0) || (ny % TILE_DIM != 0)) {\n        printf(\"nx and ny must be multiples of TILE_DIM\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Setup execution configuration parameters\n    dim3 dimGrid(nx / TILE_DIM, ny / TILE_DIM), dimBlock(TILE_DIM, TILE_DIM);\n\n    // This will pick the best possible CUDA capable device\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // CUDA events for timing\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    // Host allocation and initialization\n    float *h_idata = (float *)malloc(sizeof(float) * nx * ny);\n    float *h_odata = (float *)malloc(sizeof(float) * nx * ny);\n    float *gold    = (float *)malloc(sizeof(float) * nx * ny);\n\n    for (int i = 0; i < nx * ny; ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // Device memory allocation\n    // Pitch linear input data\n    float *d_idataPL;\n    size_t d_pitchBytes;\n\n    checkCudaErrors(hipMallocPitch((void **)&d_idataPL, &d_pitchBytes, nx * sizeof(float), ny));\n\n    // Array input data\n    hipArray            *d_idataArray;\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc<float>();\n\n    checkCudaErrors(hipMallocArray(&d_idataArray, &channelDesc, nx, ny));\n\n    // Pitch linear output data\n    float *d_odata;\n    checkCudaErrors(hipMallocPitch((void **)&d_odata, &d_pitchBytes, nx * sizeof(float), ny));\n\n    // Copy host data to device\n    // Pitch linear\n    size_t h_pitchBytes = nx * sizeof(float);\n\n    checkCudaErrors(\n        hipMemcpy2D(d_idataPL, d_pitchBytes, h_idata, h_pitchBytes, nx * sizeof(float), ny, hipMemcpyHostToDevice));\n\n    // Array\n    checkCudaErrors(hipMemcpyToArray(d_idataArray, 0, 0, h_idata, nx * ny * sizeof(float), hipMemcpyHostToDevice));\n\n    hipTextureObject_t texRefPL;\n    hipTextureObject_t texRefArray;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                  = hipResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = d_idataPL;\n    texRes.res.pitch2D.desc         = channelDesc;\n    texRes.res.pitch2D.width        = nx;\n    texRes.res.pitch2D.height       = ny;\n    texRes.res.pitch2D.pitchInBytes = h_pitchBytes;\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&texRefPL, &texRes, &texDescr, NULL));\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n    texRes.resType            = hipResourceTypeArray;\n    texRes.res.array.array    = d_idataArray;\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n    checkCudaErrors(hipCreateTextureObject(&texRefArray, &texRes, &texDescr, NULL));\n\n    // Reference calculation\n    for (int j = 0; j < ny; ++j) {\n        int jshift = (j + y_shift) % ny;\n\n        for (int i = 0; i < nx; ++i) {\n            int ishift       = (i + x_shift) % nx;\n            gold[j * nx + i] = h_idata[jshift * nx + ishift];\n        }\n    }\n\n    // Run ShiftPitchLinear kernel\n    checkCudaErrors(hipMemset2D(d_odata, d_pitchBytes, 0, nx * sizeof(float), ny));\n\n    checkCudaErrors(hipEventRecord(start, 0));\n\n    for (int i = 0; i < NUM_REPS; ++i) {\n        shiftPitchLinear<<<dimGrid, dimBlock>>>(\n            d_odata, (int)(d_pitchBytes / sizeof(float)), nx, ny, x_shift, y_shift, texRefPL);\n    }\n\n    checkCudaErrors(hipEventRecord(stop, 0));\n    checkCudaErrors(hipEventSynchronize(stop));\n    float timePL;\n    checkCudaErrors(hipEventElapsedTime(&timePL, start, stop));\n\n    // Check results\n    checkCudaErrors(\n        hipMemcpy2D(h_odata, h_pitchBytes, d_odata, d_pitchBytes, nx * sizeof(float), ny, hipMemcpyDeviceToHost));\n\n    bool res = compareData(gold, h_odata, nx * ny, 0.0f, 0.15f);\n\n    bTestResult = true;\n\n    if (res == false) {\n        printf(\"*** shiftPitchLinear failed ***\\n\");\n        bTestResult = false;\n    }\n\n    // Run ShiftArray kernel\n    checkCudaErrors(hipMemset2D(d_odata, d_pitchBytes, 0, nx * sizeof(float), ny));\n    checkCudaErrors(hipEventRecord(start, 0));\n\n    for (int i = 0; i < NUM_REPS; ++i) {\n        shiftArray<<<dimGrid, dimBlock>>>(\n            d_odata, (int)(d_pitchBytes / sizeof(float)), nx, ny, x_shift, y_shift, texRefArray);\n    }\n\n    checkCudaErrors(hipEventRecord(stop, 0));\n    checkCudaErrors(hipEventSynchronize(stop));\n    float timeArray;\n    checkCudaErrors(hipEventElapsedTime(&timeArray, start, stop));\n\n    // Check results\n    checkCudaErrors(\n        hipMemcpy2D(h_odata, h_pitchBytes, d_odata, d_pitchBytes, nx * sizeof(float), ny, hipMemcpyDeviceToHost));\n    res = compareData(gold, h_odata, nx * ny, 0.0f, 0.15f);\n\n    if (res == false) {\n        printf(\"*** shiftArray failed ***\\n\");\n        bTestResult = false;\n    }\n\n    float bandwidthPL    = 2.f * 1000.f * nx * ny * sizeof(float) / (1.e+9f) / (timePL / NUM_REPS);\n    float bandwidthArray = 2.f * 1000.f * nx * ny * sizeof(float) / (1.e+9f) / (timeArray / NUM_REPS);\n\n    printf(\"\\nBandwidth (GB/s) for pitch linear: %.2e; for array: %.2e\\n\", bandwidthPL, bandwidthArray);\n\n    float fetchRatePL    = nx * ny / 1.e+6f / (timePL / (1000.0f * NUM_REPS));\n    float fetchRateArray = nx * ny / 1.e+6f / (timeArray / (1000.0f * NUM_REPS));\n\n    printf(\"\\nTexture fetch rate (Mpix/s) for pitch linear: \"\n           \"%.2e; for array: %.2e\\n\\n\",\n           fetchRatePL,\n           fetchRateArray);\n\n    // Cleanup\n    free(h_idata);\n    free(h_odata);\n    free(gold);\n\n    checkCudaErrors(hipDestroyTextureObject(texRefPL));\n    checkCudaErrors(hipDestroyTextureObject(texRefArray));\n    checkCudaErrors(hipFree(d_idataPL));\n    checkCudaErrors(hipFreeArray(d_idataArray));\n    checkCudaErrors(hipFree(d_odata));\n\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simplePitchLinearTexture/simplePitchLinearTexture.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/vectorAddDrv/vectorAdd_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n/* Add two vectors on the GPU */\n__global__ void vectorAddGPU(float *a, float *b, float *c, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        c[idx] = a[idx] + b[idx];\n    }\n}\n\n// Allocate generic memory with malloc() and pin it laster instead of using\n// cudaHostAlloc()\nbool bPinGenericMemory = false;\n\n// Macro to aligned up to the memory size in question\n#define MEMORY_ALIGNMENT  4096\n#define ALIGN_UP(x, size) (((size_t)x + (size - 1)) & (~(size - 1)))\n\nint main(int argc, char **argv)\n{\n    int            n, nelem, deviceCount;\n    int            idev   = 0; // use default device 0\n    char          *device = NULL;\n    unsigned int   flags;\n    size_t         bytes;\n    float         *a, *b, *c;          // Pinned memory allocated on the CPU\n    float         *a_UA, *b_UA, *c_UA; // Non-4K Aligned Pinned memory on the CPU\n    float         *d_a, *d_b, *d_c;    // Device pointers for mapped memory\n    float          errorNorm, refNorm, ref, diff;\n    cudaDeviceProp deviceProp;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printf(\"Usage:  simpleZeroCopy [OPTION]\\n\\n\");\n        printf(\"Options:\\n\");\n        printf(\"  --device=[device #]  Specify the device to be used\\n\");\n        printf(\"  --use_generic_memory (optional) use generic page-aligned for system \"\n               \"memory\\n\");\n        return EXIT_SUCCESS;\n    }\n\n    /* Get the device selected by the user or default to 0, and then set it. */\n    if (getCmdLineArgumentString(argc, (const char **)argv, \"device\", &device)) {\n        cudaGetDeviceCount(&deviceCount);\n        idev = atoi(device);\n\n        if (idev >= deviceCount || idev < 0) {\n            fprintf(stderr, \"Device number %d is invalid, will use default CUDA device 0.\\n\", idev);\n            idev = 0;\n        }\n    }\n\n    // if GPU found supports SM 1.2, then continue, otherwise we exit\n    if (!checkCudaCapabilities(1, 2)) {\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_generic_memory\")) {\n#if defined(__APPLE__) || defined(MACOSX)\n        bPinGenericMemory = false; // Generic Pinning of System Paged memory is not\n                                   // currently supported on Mac OSX\n#else\n        bPinGenericMemory = true;\n#endif\n    }\n\n    if (bPinGenericMemory) {\n        printf(\"> Using Generic System Paged Memory (malloc)\\n\");\n    }\n    else {\n        printf(\"> Using CUDA Host Allocated (cudaHostAlloc)\\n\");\n    }\n\n    checkCudaErrors(cudaSetDevice(idev));\n\n    /* Verify the selected device supports mapped memory and set the device\n       flags for mapping host memory. */\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, idev));\n\n#if CUDART_VERSION >= 2020\n\n    if (!deviceProp.canMapHostMemory) {\n        fprintf(stderr, \"Device %d does not support mapping CPU host memory!\\n\", idev);\n\n        exit(EXIT_SUCCESS);\n    }\n\n    checkCudaErrors(cudaSetDeviceFlags(cudaDeviceMapHost));\n#else\n    fprintf(stderr,\n            \"CUDART version %d.%d does not support \"\n            \"<cudaDeviceProp.canMapHostMemory> field\\n\",\n            ,\n            CUDART_VERSION / 1000,\n            (CUDART_VERSION % 100) / 10);\n\n    exit(EXIT_SUCCESS);\n#endif\n\n#if CUDART_VERSION < 4000\n\n    if (bPinGenericMemory) {\n        fprintf(stderr,\n                \"CUDART version %d.%d does not support <cudaHostRegister> function\\n\",\n                CUDART_VERSION / 1000,\n                (CUDART_VERSION % 100) / 10);\n\n        exit(EXIT_SUCCESS);\n    }\n\n#endif\n\n    /* Allocate mapped CPU memory. */\n\n    nelem = 1048576;\n    bytes = nelem * sizeof(float);\n\n    if (bPinGenericMemory) {\n#if CUDART_VERSION >= 4000\n        a_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n        b_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n        c_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n\n        // We need to ensure memory is aligned to 4K (so we will need to padd memory\n        // accordingly)\n        a = (float *)ALIGN_UP(a_UA, MEMORY_ALIGNMENT);\n        b = (float *)ALIGN_UP(b_UA, MEMORY_ALIGNMENT);\n        c = (float *)ALIGN_UP(c_UA, MEMORY_ALIGNMENT);\n\n        checkCudaErrors(cudaHostRegister(a, bytes, cudaHostRegisterMapped));\n        checkCudaErrors(cudaHostRegister(b, bytes, cudaHostRegisterMapped));\n        checkCudaErrors(cudaHostRegister(c, bytes, cudaHostRegisterMapped));\n#endif\n    }\n    else {\n#if CUDART_VERSION >= 2020\n        flags = cudaHostAllocMapped;\n        checkCudaErrors(cudaHostAlloc((void **)&a, bytes, flags));\n        checkCudaErrors(cudaHostAlloc((void **)&b, bytes, flags));\n        checkCudaErrors(cudaHostAlloc((void **)&c, bytes, flags));\n#endif\n    }\n\n    /* Initialize the vectors. */\n\n    for (n = 0; n < nelem; n++) {\n        a[n] = rand() / (float)RAND_MAX;\n        b[n] = rand() / (float)RAND_MAX;\n    }\n\n    /* Get the device pointers for the pinned CPU memory mapped into the GPU\n       memory space. */\n\n#if CUDART_VERSION >= 2020\n    checkCudaErrors(cudaHostGetDevicePointer((void **)&d_a, (void *)a, 0));\n    checkCudaErrors(cudaHostGetDevicePointer((void **)&d_b, (void *)b, 0));\n    checkCudaErrors(cudaHostGetDevicePointer((void **)&d_c, (void *)c, 0));\n#endif\n\n    /* Call the GPU kernel using the CPU pointers residing in CPU mapped memory.\n     */\n    printf(\"> vectorAddGPU kernel will add vectors using mapped CPU memory...\\n\");\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n    vectorAddGPU<<<grid, block>>>(d_a, d_b, d_c, nelem);\n    checkCudaErrors(cudaDeviceSynchronize());\n    getLastCudaError(\"vectorAddGPU() execution failed\");\n\n    /* Compare the results */\n\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n\n    /* Memory clean up */\n\n    printf(\"> Releasing CPU memory...\\n\");\n\n    if (bPinGenericMemory) {\n#if CUDART_VERSION >= 4000\n        checkCudaErrors(cudaHostUnregister(a));\n        checkCudaErrors(cudaHostUnregister(b));\n        checkCudaErrors(cudaHostUnregister(c));\n        free(a_UA);\n        free(b_UA);\n        free(c_UA);\n#endif\n    }\n    else {\n#if CUDART_VERSION >= 2020\n        checkCudaErrors(cudaFreeHost(a));\n        checkCudaErrors(cudaFreeHost(b));\n        checkCudaErrors(cudaFreeHost(c));\n#endif\n    }\n\n    exit(errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n/* Add two vectors on the GPU */\n__global__ void vectorAddGPU(float *a, float *b, float *c, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        c[idx] = a[idx] + b[idx];\n    }\n}\n\n// Allocate generic memory with malloc() and pin it laster instead of using\n// cudaHostAlloc()\nbool bPinGenericMemory = false;\n\n// Macro to aligned up to the memory size in question\n#define MEMORY_ALIGNMENT  4096\n#define ALIGN_UP(x, size) (((size_t)x + (size - 1)) & (~(size - 1)))\n\nint main(int argc, char **argv)\n{\n    int            n, nelem, deviceCount;\n    int            idev   = 0; // use default device 0\n    char          *device = NULL;\n    unsigned int   flags;\n    size_t         bytes;\n    float         *a, *b, *c;          // Pinned memory allocated on the CPU\n    float         *a_UA, *b_UA, *c_UA; // Non-4K Aligned Pinned memory on the CPU\n    float         *d_a, *d_b, *d_c;    // Device pointers for mapped memory\n    float          errorNorm, refNorm, ref, diff;\n    hipDeviceProp_t deviceProp;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printf(\"Usage:  simpleZeroCopy [OPTION]\\n\\n\");\n        printf(\"Options:\\n\");\n        printf(\"  --device=[device #]  Specify the device to be used\\n\");\n        printf(\"  --use_generic_memory (optional) use generic page-aligned for system \"\n               \"memory\\n\");\n        return EXIT_SUCCESS;\n    }\n\n    /* Get the device selected by the user or default to 0, and then set it. */\n    if (getCmdLineArgumentString(argc, (const char **)argv, \"device\", &device)) {\n        hipGetDeviceCount(&deviceCount);\n        idev = atoi(device);\n\n        if (idev >= deviceCount || idev < 0) {\n            fprintf(stderr, \"Device number %d is invalid, will use default CUDA device 0.\\n\", idev);\n            idev = 0;\n        }\n    }\n\n    // if GPU found supports SM 1.2, then continue, otherwise we exit\n    if (!checkCudaCapabilities(1, 2)) {\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_generic_memory\")) {\n#if defined(__APPLE__) || defined(MACOSX)\n        bPinGenericMemory = false; // Generic Pinning of System Paged memory is not\n                                   // currently supported on Mac OSX\n#else\n        bPinGenericMemory = true;\n#endif\n    }\n\n    if (bPinGenericMemory) {\n        printf(\"> Using Generic System Paged Memory (malloc)\\n\");\n    }\n    else {\n        printf(\"> Using CUDA Host Allocated (hipHostAlloc)\\n\");\n    }\n\n    checkCudaErrors(hipSetDevice(idev));\n\n    /* Verify the selected device supports mapped memory and set the device\n       flags for mapping host memory. */\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, idev));\n\n#if CUDART_VERSION >= 2020\n\n    if (!deviceProp.canMapHostMemory) {\n        fprintf(stderr, \"Device %d does not support mapping CPU host memory!\\n\", idev);\n\n        exit(EXIT_SUCCESS);\n    }\n\n    checkCudaErrors(hipSetDeviceFlags(hipDeviceMapHost));\n#else\n    fprintf(stderr,\n            \"CUDART version %d.%d does not support \"\n            \"<hipDeviceProp_t.canMapHostMemory> field\\n\",\n            ,\n            CUDART_VERSION / 1000,\n            (CUDART_VERSION % 100) / 10);\n\n    exit(EXIT_SUCCESS);\n#endif\n\n#if CUDART_VERSION < 4000\n\n    if (bPinGenericMemory) {\n        fprintf(stderr,\n                \"CUDART version %d.%d does not support <hipHostRegister> function\\n\",\n                CUDART_VERSION / 1000,\n                (CUDART_VERSION % 100) / 10);\n\n        exit(EXIT_SUCCESS);\n    }\n\n#endif\n\n    /* Allocate mapped CPU memory. */\n\n    nelem = 1048576;\n    bytes = nelem * sizeof(float);\n\n    if (bPinGenericMemory) {\n#if CUDART_VERSION >= 4000\n        a_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n        b_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n        c_UA = (float *)malloc(bytes + MEMORY_ALIGNMENT);\n\n        // We need to ensure memory is aligned to 4K (so we will need to padd memory\n        // accordingly)\n        a = (float *)ALIGN_UP(a_UA, MEMORY_ALIGNMENT);\n        b = (float *)ALIGN_UP(b_UA, MEMORY_ALIGNMENT);\n        c = (float *)ALIGN_UP(c_UA, MEMORY_ALIGNMENT);\n\n        checkCudaErrors(hipHostRegister(a, bytes, hipHostRegisterMapped));\n        checkCudaErrors(hipHostRegister(b, bytes, hipHostRegisterMapped));\n        checkCudaErrors(hipHostRegister(c, bytes, hipHostRegisterMapped));\n#endif\n    }\n    else {\n#if CUDART_VERSION >= 2020\n        flags = hipHostMallocMapped;\n        checkCudaErrors(hipHostAlloc((void **)&a, bytes, flags));\n        checkCudaErrors(hipHostAlloc((void **)&b, bytes, flags));\n        checkCudaErrors(hipHostAlloc((void **)&c, bytes, flags));\n#endif\n    }\n\n    /* Initialize the vectors. */\n\n    for (n = 0; n < nelem; n++) {\n        a[n] = rand() / (float)RAND_MAX;\n        b[n] = rand() / (float)RAND_MAX;\n    }\n\n    /* Get the device pointers for the pinned CPU memory mapped into the GPU\n       memory space. */\n\n#if CUDART_VERSION >= 2020\n    checkCudaErrors(hipHostGetDevicePointer((void **)&d_a, (void *)a, 0));\n    checkCudaErrors(hipHostGetDevicePointer((void **)&d_b, (void *)b, 0));\n    checkCudaErrors(hipHostGetDevicePointer((void **)&d_c, (void *)c, 0));\n#endif\n\n    /* Call the GPU kernel using the CPU pointers residing in CPU mapped memory.\n     */\n    printf(\"> vectorAddGPU kernel will add vectors using mapped CPU memory...\\n\");\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n    vectorAddGPU<<<grid, block>>>(d_a, d_b, d_c, nelem);\n    checkCudaErrors(hipDeviceSynchronize());\n    getLastCudaError(\"vectorAddGPU() execution failed\");\n\n    /* Compare the results */\n\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n\n    /* Memory clean up */\n\n    printf(\"> Releasing CPU memory...\\n\");\n\n    if (bPinGenericMemory) {\n#if CUDART_VERSION >= 4000\n        checkCudaErrors(hipHostUnregister(a));\n        checkCudaErrors(hipHostUnregister(b));\n        checkCudaErrors(hipHostUnregister(c));\n        free(a_UA);\n        free(b_UA);\n        free(c_UA);\n#endif\n    }\n    else {\n#if CUDART_VERSION >= 2020\n        checkCudaErrors(hipHostFree(a));\n        checkCudaErrors(hipHostFree(b));\n        checkCudaErrors(hipHostFree(c));\n#endif\n    }\n\n    exit(errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleZeroCopy/simpleZeroCopy.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Multi-GPU sample using OpenMP for threading on the CPU side\n * needs a compiler that supports OpenMP 2.0\n */\n\n#include <helper_cuda.h>\n#include <omp.h>\n#include <stdio.h> // stdio functions are used since C++ streams aren't necessarily thread safe\n\nusing namespace std;\n\n// a simple kernel that simply increments each array element by b\n__global__ void kernelAddConstant(int *g_a, const int b)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    g_a[idx] += b;\n}\n\n// a predicate that checks whether each array element is set to its index plus b\nint correctResult(int *data, const int n, const int b)\n{\n    for (int i = 0; i < n; i++)\n        if (data[i] != i + b)\n            return 0;\n\n    return 1;\n}\n\nint main(int argc, char *argv[])\n{\n    int num_gpus = 0; // number of CUDA GPUs\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    /////////////////////////////////////////////////////////////////\n    // determine the number of CUDA capable GPUs\n    //\n    cudaGetDeviceCount(&num_gpus);\n\n    if (num_gpus < 1) {\n        printf(\"no CUDA capable devices were detected\\n\");\n        return 1;\n    }\n\n    /////////////////////////////////////////////////////////////////\n    // display CPU and GPU configuration\n    //\n    printf(\"number of host CPUs:\\t%d\\n\", omp_get_num_procs());\n    printf(\"number of CUDA devices:\\t%d\\n\", num_gpus);\n\n    for (int i = 0; i < num_gpus; i++) {\n        cudaDeviceProp dprop;\n        cudaGetDeviceProperties(&dprop, i);\n        printf(\"   %d: %s\\n\", i, dprop.name);\n    }\n\n    printf(\"---------------------------\\n\");\n\n    /////////////////////////////////////////////////////////////////\n    // initialize data\n    //\n    unsigned int n      = num_gpus * 8192;\n    unsigned int nbytes = n * sizeof(int);\n    int         *a      = 0; // pointer to data on the CPU\n    int          b      = 3; // value by which the array is incremented\n    a                   = (int *)malloc(nbytes);\n\n    if (0 == a) {\n        printf(\"couldn't allocate CPU memory\\n\");\n        return 1;\n    }\n\n    for (unsigned int i = 0; i < n; i++)\n        a[i] = i;\n\n    ////////////////////////////////////////////////////////////////\n    // run as many CPU threads as there are CUDA devices\n    //   each CPU thread controls a different device, processing its\n    //   portion of the data.  It's possible to use more CPU threads\n    //   than there are CUDA devices, in which case several CPU\n    //   threads will be allocating resources and launching kernels\n    //   on the same device.  For example, try omp_set_num_threads(2*num_gpus);\n    //   Recall that all variables declared inside an \"omp parallel\" scope are\n    //   local to each CPU thread\n    //\n    omp_set_num_threads(num_gpus); // create as many CPU threads as there are CUDA devices\n// omp_set_num_threads(2*num_gpus);// create twice as many CPU threads as there\n// are CUDA devices\n#pragma omp parallel\n    {\n        unsigned int cpu_thread_id   = omp_get_thread_num();\n        unsigned int num_cpu_threads = omp_get_num_threads();\n\n        // set and check the CUDA device for this CPU thread\n        int gpu_id = -1;\n        checkCudaErrors(\n            cudaSetDevice(cpu_thread_id % num_gpus)); // \"% num_gpus\" allows more CPU threads than GPU devices\n        checkCudaErrors(cudaGetDevice(&gpu_id));\n        printf(\"CPU thread %d (of %d) uses CUDA device %d\\n\", cpu_thread_id, num_cpu_threads, gpu_id);\n\n        int         *d_a   = 0; // pointer to memory on the device associated with this CPU thread\n        int         *sub_a = a + cpu_thread_id * n / num_cpu_threads; // pointer to this CPU thread's portion of data\n        unsigned int nbytes_per_kernel = nbytes / num_cpu_threads;\n        dim3         gpu_threads(128); // 128 threads per block\n        dim3         gpu_blocks(n / (gpu_threads.x * num_cpu_threads));\n\n        checkCudaErrors(cudaMalloc((void **)&d_a, nbytes_per_kernel));\n        checkCudaErrors(cudaMemset(d_a, 0, nbytes_per_kernel));\n        checkCudaErrors(cudaMemcpy(d_a, sub_a, nbytes_per_kernel, cudaMemcpyHostToDevice));\n        kernelAddConstant<<<gpu_blocks, gpu_threads>>>(d_a, b);\n\n        checkCudaErrors(cudaMemcpy(sub_a, d_a, nbytes_per_kernel, cudaMemcpyDeviceToHost));\n        checkCudaErrors(cudaFree(d_a));\n    }\n    printf(\"---------------------------\\n\");\n\n    if (cudaSuccess != cudaGetLastError())\n        printf(\"%s\\n\", cudaGetErrorString(cudaGetLastError()));\n\n    ////////////////////////////////////////////////////////////////\n    // check the result\n    //\n    bool bResult = correctResult(a, n, b);\n\n    if (a)\n        free(a); // free CPU memory\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Multi-GPU sample using OpenMP for threading on the CPU side\n * needs a compiler that supports OpenMP 2.0\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <omp.h>\n#include <stdio.h> // stdio functions are used since C++ streams aren't necessarily thread safe\n\nusing namespace std;\n\n// a simple kernel that simply increments each array element by b\n__global__ void kernelAddConstant(int *g_a, const int b)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    g_a[idx] += b;\n}\n\n// a predicate that checks whether each array element is set to its index plus b\nint correctResult(int *data, const int n, const int b)\n{\n    for (int i = 0; i < n; i++)\n        if (data[i] != i + b)\n            return 0;\n\n    return 1;\n}\n\nint main(int argc, char *argv[])\n{\n    int num_gpus = 0; // number of CUDA GPUs\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    /////////////////////////////////////////////////////////////////\n    // determine the number of CUDA capable GPUs\n    //\n    hipGetDeviceCount(&num_gpus);\n\n    if (num_gpus < 1) {\n        printf(\"no CUDA capable devices were detected\\n\");\n        return 1;\n    }\n\n    /////////////////////////////////////////////////////////////////\n    // display CPU and GPU configuration\n    //\n    printf(\"number of host CPUs:\\t%d\\n\", omp_get_num_procs());\n    printf(\"number of CUDA devices:\\t%d\\n\", num_gpus);\n\n    for (int i = 0; i < num_gpus; i++) {\n        hipDeviceProp_t dprop;\n        hipGetDeviceProperties(&dprop, i);\n        printf(\"   %d: %s\\n\", i, dprop.name);\n    }\n\n    printf(\"---------------------------\\n\");\n\n    /////////////////////////////////////////////////////////////////\n    // initialize data\n    //\n    unsigned int n      = num_gpus * 8192;\n    unsigned int nbytes = n * sizeof(int);\n    int         *a      = 0; // pointer to data on the CPU\n    int          b      = 3; // value by which the array is incremented\n    a                   = (int *)malloc(nbytes);\n\n    if (0 == a) {\n        printf(\"couldn't allocate CPU memory\\n\");\n        return 1;\n    }\n\n    for (unsigned int i = 0; i < n; i++)\n        a[i] = i;\n\n    ////////////////////////////////////////////////////////////////\n    // run as many CPU threads as there are CUDA devices\n    //   each CPU thread controls a different device, processing its\n    //   portion of the data.  It's possible to use more CPU threads\n    //   than there are CUDA devices, in which case several CPU\n    //   threads will be allocating resources and launching kernels\n    //   on the same device.  For example, try omp_set_num_threads(2*num_gpus);\n    //   Recall that all variables declared inside an \"omp parallel\" scope are\n    //   local to each CPU thread\n    //\n    omp_set_num_threads(num_gpus); // create as many CPU threads as there are CUDA devices\n// omp_set_num_threads(2*num_gpus);// create twice as many CPU threads as there\n// are CUDA devices\n#pragma omp parallel\n    {\n        unsigned int cpu_thread_id   = omp_get_thread_num();\n        unsigned int num_cpu_threads = omp_get_num_threads();\n\n        // set and check the CUDA device for this CPU thread\n        int gpu_id = -1;\n        checkCudaErrors(\n            hipSetDevice(cpu_thread_id % num_gpus)); // \"% num_gpus\" allows more CPU threads than GPU devices\n        checkCudaErrors(hipGetDevice(&gpu_id));\n        printf(\"CPU thread %d (of %d) uses CUDA device %d\\n\", cpu_thread_id, num_cpu_threads, gpu_id);\n\n        int         *d_a   = 0; // pointer to memory on the device associated with this CPU thread\n        int         *sub_a = a + cpu_thread_id * n / num_cpu_threads; // pointer to this CPU thread's portion of data\n        unsigned int nbytes_per_kernel = nbytes / num_cpu_threads;\n        dim3         gpu_threads(128); // 128 threads per block\n        dim3         gpu_blocks(n / (gpu_threads.x * num_cpu_threads));\n\n        checkCudaErrors(hipMalloc((void **)&d_a, nbytes_per_kernel));\n        checkCudaErrors(hipMemset(d_a, 0, nbytes_per_kernel));\n        checkCudaErrors(hipMemcpy(d_a, sub_a, nbytes_per_kernel, hipMemcpyHostToDevice));\n        kernelAddConstant<<<gpu_blocks, gpu_threads>>>(d_a, b);\n\n        checkCudaErrors(hipMemcpy(sub_a, d_a, nbytes_per_kernel, hipMemcpyDeviceToHost));\n        checkCudaErrors(hipFree(d_a));\n    }\n    printf(\"---------------------------\\n\");\n\n    if (hipSuccess != hipGetLastError())\n        printf(\"%s\\n\", hipGetErrorString(hipGetLastError()));\n\n    ////////////////////////////////////////////////////////////////\n    // check the result\n    //\n    bool bResult = correctResult(a, n, b);\n\n    if (a)\n        free(a); // free CPU memory\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/cudaOpenMP/cudaOpenMP.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates Inter Process Communication\n * using one process per GPU for computation.\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <vector>\n\n#include \"helper_cuda.h\"\n#include \"helper_multiprocess.h\"\nstatic const char shmName[] = \"simpleIPCshm\";\n// For direct NVLINK and PCI-E peers, at max 8 simultaneous peers are allowed\n// For NVSWITCH connected peers like DGX-2, simultaneous peers are not limited\n// in the same way.\n#define MAX_DEVICES (32)\n#define DATA_SIZE   (64ULL << 20ULL) // 64MB\n\n#if defined(__linux__)\n#define cpu_atomic_add32(a, x) __sync_add_and_fetch(a, x)\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n#define cpu_atomic_add32(a, x) InterlockedAdd((volatile LONG *)a, x)\n#else\n#error Unsupported system\n#endif\n\ntypedef struct shmStruct_st\n{\n    size_t               nprocesses;\n    int                  barrier;\n    int                  sense;\n    int                  devices[MAX_DEVICES];\n    cudaIpcMemHandle_t   memHandle[MAX_DEVICES];\n    cudaIpcEventHandle_t eventHandle[MAX_DEVICES];\n} shmStruct;\n\n__global__ void simpleKernel(char *ptr, int sz, char val)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n\nstatic void barrierWait(volatile int *barrier, volatile int *sense, unsigned int n)\n{\n    int count;\n\n    // Check-in\n    count = cpu_atomic_add32(barrier, 1);\n    if (count == n) // Last one in\n        *sense = 1;\n    while (!*sense)\n        ;\n\n    // Check-out\n    count = cpu_atomic_add32(barrier, -1);\n    if (count == 0) // Last one out\n        *sense = 0;\n    while (*sense)\n        ;\n}\n\nstatic void childProcess(int id)\n{\n    volatile shmStruct      *shm = NULL;\n    cudaStream_t             stream;\n    sharedMemoryInfo         info;\n    size_t                   procCount, i;\n    int                      blocks  = 0;\n    int                      threads = 128;\n    cudaDeviceProp           prop;\n    std::vector<void *>      ptrs;\n    std::vector<cudaEvent_t> events;\n    std::vector<char>        verification_buffer(DATA_SIZE);\n    char                     pidString[20] = {0};\n    char                     lshmName[40]  = {0};\n\n    // Use parent process ID to create a unique shared memory name for Linux multi-process\n#ifdef __linux__\n    pid_t pid;\n    pid = getppid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n#endif\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"CP: lshmName = %s\\n\", lshmName);\n\n    if (sharedMemoryOpen(lshmName, sizeof(shmStruct), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm       = (volatile shmStruct *)info.addr;\n    procCount = shm->nprocesses;\n\n    printf(\"Process %d: Starting on device %d...\\n\", id, shm->devices[id]);\n\n    checkCudaErrors(cudaSetDevice(shm->devices[id]));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, shm->devices[id]));\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, simpleKernel, threads, 0));\n    blocks *= prop.multiProcessorCount;\n\n    // Open and track all the allocations and events created in the master\n    // process for use later\n    for (i = 0; i < procCount; i++) {\n        void       *ptr = NULL;\n        cudaEvent_t event;\n\n        // Notice, we don't need to explicitly enable peer access for\n        // allocations on other devices.\n        checkCudaErrors(\n            cudaIpcOpenMemHandle(&ptr, *(cudaIpcMemHandle_t *)&shm->memHandle[i], cudaIpcMemLazyEnablePeerAccess));\n        checkCudaErrors(cudaIpcOpenEventHandle(&event, *(cudaIpcEventHandle_t *)&shm->eventHandle[i]));\n\n        ptrs.push_back(ptr);\n        events.push_back(event);\n    }\n\n    // At each iteration of the loop, each sibling process will push work on\n    // their respective devices accessing the next peer mapped buffer allocated\n    // by the master process (these can come from other sibling processes as\n    // well). To coordinate each process' access, we force the stream to wait for\n    // the work already accessing this buffer asynchronously through IPC events,\n    // allowing the CPU processes to continue to queue more work.\n    for (i = 0; i < procCount; i++) {\n        size_t bufferId = (i + id) % procCount;\n        // Wait for the buffer to be accessed to be ready\n        checkCudaErrors(cudaStreamWaitEvent(stream, events[bufferId], 0));\n        // Push a simple kernel on it\n        simpleKernel<<<blocks, threads, 0, stream>>>((char *)ptrs[bufferId], DATA_SIZE, id);\n        checkCudaErrors(cudaGetLastError());\n        // Signal that this buffer is ready for the next consumer\n        checkCudaErrors(cudaEventRecord(events[bufferId], stream));\n        // Wait for all my sibling processes to push this stage of their work\n        // before proceeding to the next. This prevents siblings from racing\n        // ahead and clobbering the recorded event or waiting on the wrong\n        // recorded event.\n        barrierWait(&shm->barrier, &shm->sense, (unsigned int)procCount);\n        if (id == 0) {\n            printf(\"Step %lld done\\n\", (unsigned long long)i);\n        }\n    }\n\n    // Now wait for my buffer to be ready so I can copy it locally and verify it\n    checkCudaErrors(cudaStreamWaitEvent(stream, events[id], 0));\n    checkCudaErrors(cudaMemcpyAsync(&verification_buffer[0], ptrs[id], DATA_SIZE, cudaMemcpyDeviceToHost, stream));\n    // And wait for all the queued up work to complete\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    printf(\"Process %d: verifying...\\n\", id);\n\n    // The contents should have the id of the sibling just after me\n    char compareId = (char)((id + 1) % procCount);\n    for (unsigned long long j = 0; j < DATA_SIZE; j++) {\n        if (verification_buffer[j] != compareId) {\n            printf(\"Process %d: Verification mismatch at %lld: %d != %d\\n\",\n                   id,\n                   j,\n                   (int)verification_buffer[j],\n                   (int)compareId);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < procCount; i++) {\n        checkCudaErrors(cudaIpcCloseMemHandle(ptrs[i]));\n        checkCudaErrors(cudaEventDestroy(events[i]));\n    }\n\n    checkCudaErrors(cudaStreamDestroy(stream));\n\n    printf(\"Process %d complete!\\n\", id);\n}\n\nstatic void parentProcess(char *app)\n{\n    sharedMemoryInfo         info;\n    int                      devCount, i;\n    volatile shmStruct      *shm = NULL;\n    std::vector<void *>      ptrs;\n    std::vector<cudaEvent_t> events;\n    std::vector<Process>     processes;\n    char                     pidString[20] = {0};\n    char                     lshmName[40]  = {0};\n\n    // Use current process ID to create a unique shared memory name for Linux multi-process\n#ifdef __linux__\n    pid_t pid;\n    pid = getpid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n#endif\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"PP: lshmName = %s\\n\", lshmName);\n\n    checkCudaErrors(cudaGetDeviceCount(&devCount));\n\n    if (sharedMemoryCreate(lshmName, sizeof(*shm), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm = (volatile shmStruct *)info.addr;\n    memset((void *)shm, 0, sizeof(*shm));\n\n    // Pick all the devices that can access each other's memory for this test\n    // Keep in mind that CUDA has minimal support for fork() without a\n    // corresponding exec() in the child process, but in this case our\n    // spawnProcess will always exec, so no need to worry.\n    for (i = 0; i < devCount; i++) {\n        bool           allPeers = true;\n        cudaDeviceProp prop;\n        checkCudaErrors(cudaGetDeviceProperties(&prop, i));\n\n        // CUDA IPC is only supported on devices with unified addressing\n        if (!prop.unifiedAddressing) {\n            printf(\"Device %d does not support unified addressing, skipping...\\n\", i);\n            continue;\n        }\n        // This sample requires two processes accessing each device, so we need\n        // to ensure exclusive or prohibited mode is not set\n        int computeMode;\n        checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, i));\n        if (computeMode != cudaComputeModeDefault) {\n            printf(\"Device %d is in an unsupported compute mode for this sample\\n\", i);\n            continue;\n        }\n\n        for (int j = 0; j < shm->nprocesses; j++) {\n            int canAccessPeerIJ, canAccessPeerJI;\n            checkCudaErrors(cudaDeviceCanAccessPeer(&canAccessPeerJI, shm->devices[j], i));\n            checkCudaErrors(cudaDeviceCanAccessPeer(&canAccessPeerIJ, i, shm->devices[j]));\n            if (!canAccessPeerIJ || !canAccessPeerJI) {\n                allPeers = false;\n                break;\n            }\n        }\n        if (allPeers) {\n            // Enable peers here.  This isn't necessary for IPC, but it will\n            // setup the peers for the device.  For systems that only allow 8\n            // peers per GPU at a time, this acts to remove devices from CanAccessPeer\n            for (int j = 0; j < shm->nprocesses; j++) {\n                checkCudaErrors(cudaSetDevice(i));\n                checkCudaErrors(cudaDeviceEnablePeerAccess(shm->devices[j], 0));\n                checkCudaErrors(cudaSetDevice(shm->devices[j]));\n                checkCudaErrors(cudaDeviceEnablePeerAccess(i, 0));\n            }\n            shm->devices[shm->nprocesses++] = i;\n            if (shm->nprocesses >= MAX_DEVICES)\n                break;\n        }\n        else {\n            printf(\"Device %d is not peer capable with some other selected peers, \"\n                   \"skipping\\n\",\n                   i);\n        }\n    }\n\n    if (shm->nprocesses == 0) {\n        printf(\"No CUDA devices support IPC\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Now allocate memory and an event for each process and fill the shared\n    // memory buffer with the IPC handles to communicate\n    for (i = 0; i < shm->nprocesses; i++) {\n        void       *ptr = NULL;\n        cudaEvent_t event;\n\n        checkCudaErrors(cudaSetDevice(shm->devices[i]));\n        checkCudaErrors(cudaMalloc(&ptr, DATA_SIZE));\n        checkCudaErrors(cudaIpcGetMemHandle((cudaIpcMemHandle_t *)&shm->memHandle[i], ptr));\n        checkCudaErrors(cudaEventCreate(&event, cudaEventDisableTiming | cudaEventInterprocess));\n        checkCudaErrors(cudaIpcGetEventHandle((cudaIpcEventHandle_t *)&shm->eventHandle[i], event));\n\n        ptrs.push_back(ptr);\n        events.push_back(event);\n    }\n\n    // Launch the child processes!\n    for (i = 0; i < shm->nprocesses; i++) {\n        char        devIdx[12]; // Increased size to ensure enough space for formatted integer\n        char *const args[] = {app, devIdx, NULL};\n        Process     process;\n\n        snprintf(devIdx, sizeof(devIdx), \"%d\", i);\n\n        if (spawnProcess(&process, app, args)) {\n            printf(\"Failed to create process\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        processes.push_back(process);\n    }\n\n    // And wait for them to finish\n    for (i = 0; i < processes.size(); i++) {\n        if (waitProcess(&processes[i]) != EXIT_SUCCESS) {\n            printf(\"Process %d failed!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < shm->nprocesses; i++) {\n        checkCudaErrors(cudaSetDevice(shm->devices[i]));\n        checkCudaErrors(cudaEventSynchronize(events[i]));\n        checkCudaErrors(cudaEventDestroy(events[i]));\n        checkCudaErrors(cudaFree(ptrs[i]));\n    }\n\n    sharedMemoryClose(&info);\n}\n\nint main(int argc, char **argv)\n{\n#if defined(__arm__) || defined(__aarch64__)\n    printf(\"Not supported on ARM\\n\");\n    return EXIT_WAIVED;\n#else\n    if (argc == 1) {\n        parentProcess(argv[0]);\n    }\n    else {\n        childProcess(atoi(argv[1]));\n    }\n    return EXIT_SUCCESS;\n#endif\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates Inter Process Communication\n * using one process per GPU for computation.\n */\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <vector>\n\n#include \"hip/hip_runtime_api.h\"\n#include \"helper_multiprocess.h\"\nstatic const char shmName[] = \"simpleIPCshm\";\n// For direct NVLINK and PCI-E peers, at max 8 simultaneous peers are allowed\n// For NVSWITCH connected peers like DGX-2, simultaneous peers are not limited\n// in the same way.\n#define MAX_DEVICES (32)\n#define DATA_SIZE   (64ULL << 20ULL) // 64MB\n\n#if defined(__linux__)\n#define cpu_atomic_add32(a, x) __sync_add_and_fetch(a, x)\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n#define cpu_atomic_add32(a, x) InterlockedAdd((volatile LONG *)a, x)\n#else\n#error Unsupported system\n#endif\n\ntypedef struct shmStruct_st\n{\n    size_t               nprocesses;\n    int                  barrier;\n    int                  sense;\n    int                  devices[MAX_DEVICES];\n    hipIpcMemHandle_t   memHandle[MAX_DEVICES];\n    hipIpcEventHandle_t eventHandle[MAX_DEVICES];\n} shmStruct;\n\n__global__ void simpleKernel(char *ptr, int sz, char val)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n\nstatic void barrierWait(volatile int *barrier, volatile int *sense, unsigned int n)\n{\n    int count;\n\n    // Check-in\n    count = cpu_atomic_add32(barrier, 1);\n    if (count == n) // Last one in\n        *sense = 1;\n    while (!*sense)\n        ;\n\n    // Check-out\n    count = cpu_atomic_add32(barrier, -1);\n    if (count == 0) // Last one out\n        *sense = 0;\n    while (*sense)\n        ;\n}\n\nstatic void childProcess(int id)\n{\n    volatile shmStruct      *shm = NULL;\n    hipStream_t             stream;\n    sharedMemoryInfo         info;\n    size_t                   procCount, i;\n    int                      blocks  = 0;\n    int                      threads = 128;\n    hipDeviceProp_t           prop;\n    std::vector<void *>      ptrs;\n    std::vector<hipEvent_t> events;\n    std::vector<char>        verification_buffer(DATA_SIZE);\n    char                     pidString[20] = {0};\n    char                     lshmName[40]  = {0};\n\n    // Use parent process ID to create a unique shared memory name for Linux multi-process\n#ifdef __linux__\n    pid_t pid;\n    pid = getppid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n#endif\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"CP: lshmName = %s\\n\", lshmName);\n\n    if (sharedMemoryOpen(lshmName, sizeof(shmStruct), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm       = (volatile shmStruct *)info.addr;\n    procCount = shm->nprocesses;\n\n    printf(\"Process %d: Starting on device %d...\\n\", id, shm->devices[id]);\n\n    checkCudaErrors(hipSetDevice(shm->devices[id]));\n    checkCudaErrors(hipGetDeviceProperties(&prop, shm->devices[id]));\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, simpleKernel, threads, 0));\n    blocks *= prop.multiProcessorCount;\n\n    // Open and track all the allocations and events created in the master\n    // process for use later\n    for (i = 0; i < procCount; i++) {\n        void       *ptr = NULL;\n        hipEvent_t event;\n\n        // Notice, we don't need to explicitly enable peer access for\n        // allocations on other devices.\n        checkCudaErrors(\n            hipIpcOpenMemHandle(&ptr, *(hipIpcMemHandle_t *)&shm->memHandle[i], hipIpcMemLazyEnablePeerAccess));\n        checkCudaErrors(hipIpcOpenEventHandle(&event, *(hipIpcEventHandle_t *)&shm->eventHandle[i]));\n\n        ptrs.push_back(ptr);\n        events.push_back(event);\n    }\n\n    // At each iteration of the loop, each sibling process will push work on\n    // their respective devices accessing the next peer mapped buffer allocated\n    // by the master process (these can come from other sibling processes as\n    // well). To coordinate each process' access, we force the stream to wait for\n    // the work already accessing this buffer asynchronously through IPC events,\n    // allowing the CPU processes to continue to queue more work.\n    for (i = 0; i < procCount; i++) {\n        size_t bufferId = (i + id) % procCount;\n        // Wait for the buffer to be accessed to be ready\n        checkCudaErrors(hipStreamWaitEvent(stream, events[bufferId], 0));\n        // Push a simple kernel on it\n        simpleKernel<<<blocks, threads, 0, stream>>>((char *)ptrs[bufferId], DATA_SIZE, id);\n        checkCudaErrors(hipGetLastError());\n        // Signal that this buffer is ready for the next consumer\n        checkCudaErrors(hipEventRecord(events[bufferId], stream));\n        // Wait for all my sibling processes to push this stage of their work\n        // before proceeding to the next. This prevents siblings from racing\n        // ahead and clobbering the recorded event or waiting on the wrong\n        // recorded event.\n        barrierWait(&shm->barrier, &shm->sense, (unsigned int)procCount);\n        if (id == 0) {\n            printf(\"Step %lld done\\n\", (unsigned long long)i);\n        }\n    }\n\n    // Now wait for my buffer to be ready so I can copy it locally and verify it\n    checkCudaErrors(hipStreamWaitEvent(stream, events[id], 0));\n    checkCudaErrors(hipMemcpyAsync(&verification_buffer[0], ptrs[id], DATA_SIZE, hipMemcpyDeviceToHost, stream));\n    // And wait for all the queued up work to complete\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    printf(\"Process %d: verifying...\\n\", id);\n\n    // The contents should have the id of the sibling just after me\n    char compareId = (char)((id + 1) % procCount);\n    for (unsigned long long j = 0; j < DATA_SIZE; j++) {\n        if (verification_buffer[j] != compareId) {\n            printf(\"Process %d: Verification mismatch at %lld: %d != %d\\n\",\n                   id,\n                   j,\n                   (int)verification_buffer[j],\n                   (int)compareId);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < procCount; i++) {\n        checkCudaErrors(hipIpcCloseMemHandle(ptrs[i]));\n        checkCudaErrors(hipEventDestroy(events[i]));\n    }\n\n    checkCudaErrors(hipStreamDestroy(stream));\n\n    printf(\"Process %d complete!\\n\", id);\n}\n\nstatic void parentProcess(char *app)\n{\n    sharedMemoryInfo         info;\n    int                      devCount, i;\n    volatile shmStruct      *shm = NULL;\n    std::vector<void *>      ptrs;\n    std::vector<hipEvent_t> events;\n    std::vector<Process>     processes;\n    char                     pidString[20] = {0};\n    char                     lshmName[40]  = {0};\n\n    // Use current process ID to create a unique shared memory name for Linux multi-process\n#ifdef __linux__\n    pid_t pid;\n    pid = getpid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n#endif\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"PP: lshmName = %s\\n\", lshmName);\n\n    checkCudaErrors(hipGetDeviceCount(&devCount));\n\n    if (sharedMemoryCreate(lshmName, sizeof(*shm), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm = (volatile shmStruct *)info.addr;\n    memset((void *)shm, 0, sizeof(*shm));\n\n    // Pick all the devices that can access each other's memory for this test\n    // Keep in mind that CUDA has minimal support for fork() without a\n    // corresponding exec() in the child process, but in this case our\n    // spawnProcess will always exec, so no need to worry.\n    for (i = 0; i < devCount; i++) {\n        bool           allPeers = true;\n        hipDeviceProp_t prop;\n        checkCudaErrors(hipGetDeviceProperties(&prop, i));\n\n        // CUDA IPC is only supported on devices with unified addressing\n        if (!prop.unifiedAddressing) {\n            printf(\"Device %d does not support unified addressing, skipping...\\n\", i);\n            continue;\n        }\n        // This sample requires two processes accessing each device, so we need\n        // to ensure exclusive or prohibited mode is not set\n        int computeMode;\n        checkCudaErrors(hipDeviceGetAttribute(&computeMode, hipDeviceAttributeComputeMode, i));\n        if (computeMode != hipComputeModeDefault) {\n            printf(\"Device %d is in an unsupported compute mode for this sample\\n\", i);\n            continue;\n        }\n\n        for (int j = 0; j < shm->nprocesses; j++) {\n            int canAccessPeerIJ, canAccessPeerJI;\n            checkCudaErrors(hipDeviceCanAccessPeer(&canAccessPeerJI, shm->devices[j], i));\n            checkCudaErrors(hipDeviceCanAccessPeer(&canAccessPeerIJ, i, shm->devices[j]));\n            if (!canAccessPeerIJ || !canAccessPeerJI) {\n                allPeers = false;\n                break;\n            }\n        }\n        if (allPeers) {\n            // Enable peers here.  This isn't necessary for IPC, but it will\n            // setup the peers for the device.  For systems that only allow 8\n            // peers per GPU at a time, this acts to remove devices from CanAccessPeer\n            for (int j = 0; j < shm->nprocesses; j++) {\n                checkCudaErrors(hipSetDevice(i));\n                checkCudaErrors(hipDeviceEnablePeerAccess(shm->devices[j], 0));\n                checkCudaErrors(hipSetDevice(shm->devices[j]));\n                checkCudaErrors(hipDeviceEnablePeerAccess(i, 0));\n            }\n            shm->devices[shm->nprocesses++] = i;\n            if (shm->nprocesses >= MAX_DEVICES)\n                break;\n        }\n        else {\n            printf(\"Device %d is not peer capable with some other selected peers, \"\n                   \"skipping\\n\",\n                   i);\n        }\n    }\n\n    if (shm->nprocesses == 0) {\n        printf(\"No CUDA devices support IPC\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Now allocate memory and an event for each process and fill the shared\n    // memory buffer with the IPC handles to communicate\n    for (i = 0; i < shm->nprocesses; i++) {\n        void       *ptr = NULL;\n        hipEvent_t event;\n\n        checkCudaErrors(hipSetDevice(shm->devices[i]));\n        checkCudaErrors(hipMalloc(&ptr, DATA_SIZE));\n        checkCudaErrors(hipIpcGetMemHandle((hipIpcMemHandle_t *)&shm->memHandle[i], ptr));\n        checkCudaErrors(hipEventCreate(&event, hipEventDisableTiming | hipEventInterprocess));\n        checkCudaErrors(hipIpcGetEventHandle((hipIpcEventHandle_t *)&shm->eventHandle[i], event));\n\n        ptrs.push_back(ptr);\n        events.push_back(event);\n    }\n\n    // Launch the child processes!\n    for (i = 0; i < shm->nprocesses; i++) {\n        char        devIdx[12]; // Increased size to ensure enough space for formatted integer\n        char *const args[] = {app, devIdx, NULL};\n        Process     process;\n\n        snprintf(devIdx, sizeof(devIdx), \"%d\", i);\n\n        if (spawnProcess(&process, app, args)) {\n            printf(\"Failed to create process\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        processes.push_back(process);\n    }\n\n    // And wait for them to finish\n    for (i = 0; i < processes.size(); i++) {\n        if (waitProcess(&processes[i]) != EXIT_SUCCESS) {\n            printf(\"Process %d failed!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < shm->nprocesses; i++) {\n        checkCudaErrors(hipSetDevice(shm->devices[i]));\n        checkCudaErrors(hipEventSynchronize(events[i]));\n        checkCudaErrors(hipEventDestroy(events[i]));\n        checkCudaErrors(hipFree(ptrs[i]));\n    }\n\n    sharedMemoryClose(&info);\n}\n\nint main(int argc, char **argv)\n{\n#if defined(__arm__) || defined(__aarch64__)\n    printf(\"Not supported on ARM\\n\");\n    return EXIT_WAIVED;\n#else\n    if (argc == 1) {\n        parentProcess(argv[0]);\n    }\n    else {\n        childProcess(atoi(argv[1]));\n    }\n    return EXIT_SUCCESS;\n#endif\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleIPC/simpleIPC.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Template project which demonstrates the basics on how to setup a project\n * example application.\n * Host code.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper functions for SDK examples\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\nextern \"C\" void computeGold(float *reference, float *idata, const unsigned int len);\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param g_idata  input data in global memory\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void testKernel(float *g_idata, float *g_odata)\n{\n    // shared memory\n    // the size is determined by the host application\n    extern __shared__ float sdata[];\n\n    // access thread id\n    const unsigned int tid = threadIdx.x;\n    // access number of threads in this block\n    const unsigned int num_threads = blockDim.x;\n\n    // read in input data from global memory\n    sdata[tid] = g_idata[tid];\n    __syncthreads();\n\n    // perform some computations\n    sdata[tid] = (float)num_threads * sdata[tid];\n    __syncthreads();\n\n    // write data to global memory\n    g_odata[tid] = sdata[tid];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    bool bTestResult = true;\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    unsigned int num_threads = 32;\n    unsigned int mem_size    = sizeof(float) * num_threads;\n\n    // allocate host memory\n    float *h_idata = (float *)malloc(mem_size);\n\n    // initalize the memory\n    for (unsigned int i = 0; i < num_threads; ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // allocate device memory\n    float *d_idata;\n    checkCudaErrors(cudaMalloc((void **)&d_idata, mem_size));\n    // copy host memory to device\n    checkCudaErrors(cudaMemcpy(d_idata, h_idata, mem_size, cudaMemcpyHostToDevice));\n\n    // allocate device memory for result\n    float *d_odata;\n    checkCudaErrors(cudaMalloc((void **)&d_odata, mem_size));\n\n    // setup execution parameters\n    dim3 grid(1, 1, 1);\n    dim3 threads(num_threads, 1, 1);\n\n    // execute the kernel\n    testKernel<<<grid, threads, mem_size>>>(d_idata, d_odata);\n\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(mem_size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(h_odata, d_odata, sizeof(float) * num_threads, cudaMemcpyDeviceToHost));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // compute reference solution\n    float *reference = (float *)malloc(mem_size);\n    computeGold(reference, h_idata, num_threads);\n\n    // check result\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile(\"./data/regression.dat\", h_odata, num_threads, 0.0f, false);\n    }\n    else {\n        // custom output handling when no regression test running\n        // in this case check if the result is equivalent to the expected solution\n        bTestResult = compareData(reference, h_odata, num_threads, 0.0f, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_idata);\n    free(h_odata);\n    free(reference);\n    checkCudaErrors(cudaFree(d_idata));\n    checkCudaErrors(cudaFree(d_odata));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Template project which demonstrates the basics on how to setup a project\n * example application.\n * Host code.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h> // helper functions for SDK examples\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\nextern \"C\" void computeGold(float *reference, float *idata, const unsigned int len);\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param g_idata  input data in global memory\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void testKernel(float *g_idata, float *g_odata)\n{\n    // shared memory\n    // the size is determined by the host application\n    extern __shared__ float sdata[];\n\n    // access thread id\n    const unsigned int tid = threadIdx.x;\n    // access number of threads in this block\n    const unsigned int num_threads = blockDim.x;\n\n    // read in input data from global memory\n    sdata[tid] = g_idata[tid];\n    __syncthreads();\n\n    // perform some computations\n    sdata[tid] = (float)num_threads * sdata[tid];\n    __syncthreads();\n\n    // write data to global memory\n    g_odata[tid] = sdata[tid];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    bool bTestResult = true;\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    unsigned int num_threads = 32;\n    unsigned int mem_size    = sizeof(float) * num_threads;\n\n    // allocate host memory\n    float *h_idata = (float *)malloc(mem_size);\n\n    // initalize the memory\n    for (unsigned int i = 0; i < num_threads; ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // allocate device memory\n    float *d_idata;\n    checkCudaErrors(hipMalloc((void **)&d_idata, mem_size));\n    // copy host memory to device\n    checkCudaErrors(hipMemcpy(d_idata, h_idata, mem_size, hipMemcpyHostToDevice));\n\n    // allocate device memory for result\n    float *d_odata;\n    checkCudaErrors(hipMalloc((void **)&d_odata, mem_size));\n\n    // setup execution parameters\n    dim3 grid(1, 1, 1);\n    dim3 threads(num_threads, 1, 1);\n\n    // execute the kernel\n    testKernel<<<grid, threads, mem_size>>>(d_idata, d_odata);\n\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(mem_size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(h_odata, d_odata, sizeof(float) * num_threads, hipMemcpyDeviceToHost));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // compute reference solution\n    float *reference = (float *)malloc(mem_size);\n    computeGold(reference, h_idata, num_threads);\n\n    // check result\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile(\"./data/regression.dat\", h_odata, num_threads, 0.0f, false);\n    }\n    else {\n        // custom output handling when no regression test running\n        // in this case check if the result is equivalent to the expected solution\n        bTestResult = compareData(reference, h_odata, num_threads, 0.0f, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_idata);\n    free(h_odata);\n    free(reference);\n    checkCudaErrors(hipFree(d_idata));\n    checkCudaErrors(hipFree(d_odata));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/template/template.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Tests assert function.\n//! Thread whose id > N will print assertion failed error message.\n////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" __global__ void testKernel(int N)\n{\n    int gtid = blockIdx.x * blockDim.x + threadIdx.x;\n    assert(gtid < N);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Tests assert function.\n//! Thread whose id > N will print assertion failed error message.\n////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void testKernel(int N)\n{\n    int gtid = blockIdx.x * blockDim.x + threadIdx.x;\n    assert(gtid < N);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleAssert_nvrtc/simpleAssert_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper functions for SDK examples\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\ncudaAccessPolicyWindow initAccessPolicyWindow(void)\n{\n    cudaAccessPolicyWindow accessPolicyWindow = {0};\n    accessPolicyWindow.base_ptr               = (void *)0;\n    accessPolicyWindow.num_bytes              = 0;\n    accessPolicyWindow.hitRatio               = 0.f;\n    accessPolicyWindow.hitProp                = cudaAccessPropertyNormal;\n    accessPolicyWindow.missProp               = cudaAccessPropertyStreaming;\n    return accessPolicyWindow;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param data  input data in global memory\n//! @param dataSize  input data size\n//! @param bigData  input bigData in global memory\n//! @param bigDataSize  input bigData size\n//! @param hitcount how many data access are done within block\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void kernCacheSegmentTest(int *data, int dataSize, int *trash, int bigDataSize, int hitCount)\n{\n    __shared__ unsigned int hit;\n    int                     row    = blockIdx.y * blockDim.y + threadIdx.y;\n    int                     col    = blockIdx.x * blockDim.x + threadIdx.x;\n    int                     tID    = row * blockDim.y + col;\n    uint32_t                psRand = tID;\n\n    atomicExch(&hit, 0);\n    __syncthreads();\n    while (hit < hitCount) {\n        psRand ^= psRand << 13;\n        psRand ^= psRand >> 17;\n        psRand ^= psRand << 5;\n\n        int idx = tID - psRand;\n        if (idx < 0) {\n            idx = -idx;\n        }\n\n        if ((tID % 2) == 0) {\n            data[psRand % dataSize] = data[psRand % dataSize] + data[idx % dataSize];\n        }\n        else {\n            trash[psRand % bigDataSize] = trash[psRand % bigDataSize] + trash[idx % bigDataSize];\n        }\n\n        atomicAdd(&hit, 1);\n    }\n}\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    bool                   bTestResult = true;\n    cudaAccessPolicyWindow accessPolicyWindow;\n    cudaDeviceProp         deviceProp;\n    cudaStreamAttrValue    streamAttrValue;\n    cudaStream_t           stream;\n    cudaStreamAttrID       streamAttrID;\n    dim3                   threads(32, 32);\n    int                   *dataDevicePointer;\n    int                   *dataHostPointer;\n    int                    dataSize;\n    int                   *bigDataDevicePointer;\n    int                   *bigDataHostPointer;\n    int                    bigDataSize;\n    StopWatchInterface    *timer = 0;\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n    // Get device properties\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n    dim3 blocks(deviceProp.maxGridSize[1], 1);\n\n    // Make sure device the l2 optimization\n    if (deviceProp.persistingL2CacheMaxSize == 0) {\n        printf(\"Waiving execution as device %d does not support persisting L2 \"\n               \"Caching\\n\",\n               devID);\n        exit(EXIT_WAIVED);\n    }\n\n    // Create stream to assiocate with window\n    checkCudaErrors(cudaStreamCreate(&stream));\n\n    // Set the amount of l2 cache that will be persisting to maximum the device\n    // can support\n    checkCudaErrors(cudaDeviceSetLimit(cudaLimitPersistingL2CacheSize, deviceProp.persistingL2CacheMaxSize));\n\n    // Stream attribute to set\n    streamAttrID = cudaStreamAttributeAccessPolicyWindow;\n\n    // Default window\n    streamAttrValue.accessPolicyWindow = initAccessPolicyWindow();\n    accessPolicyWindow                 = initAccessPolicyWindow();\n\n    // Allocate size of both buffers\n    bigDataSize = (deviceProp.l2CacheSize * 4) / sizeof(int);\n    dataSize    = (deviceProp.l2CacheSize / 4) / sizeof(int);\n\n    // Allocate data\n    checkCudaErrors(cudaMallocHost(&dataHostPointer, dataSize * sizeof(int)));\n    checkCudaErrors(cudaMallocHost(&bigDataHostPointer, bigDataSize * sizeof(int)));\n\n    for (int i = 0; i < bigDataSize; ++i) {\n        if (i < dataSize) {\n            dataHostPointer[i] = i;\n        }\n\n        bigDataHostPointer[bigDataSize - i - 1] = i;\n    }\n\n    checkCudaErrors(cudaMalloc((void **)&dataDevicePointer, dataSize * sizeof(int)));\n    checkCudaErrors(cudaMalloc((void **)&bigDataDevicePointer, bigDataSize * sizeof(int)));\n    checkCudaErrors(\n        cudaMemcpyAsync(dataDevicePointer, dataHostPointer, dataSize * sizeof(int), cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemcpyAsync(\n        bigDataDevicePointer, bigDataHostPointer, bigDataSize * sizeof(int), cudaMemcpyHostToDevice, stream));\n\n    // Make a window for the buffer of interest\n    accessPolicyWindow.base_ptr        = (void *)dataDevicePointer;\n    accessPolicyWindow.num_bytes       = dataSize * sizeof(int);\n    accessPolicyWindow.hitRatio        = 1.f;\n    accessPolicyWindow.hitProp         = cudaAccessPropertyPersisting;\n    accessPolicyWindow.missProp        = cudaAccessPropertyNormal;\n    streamAttrValue.accessPolicyWindow = accessPolicyWindow;\n\n    // Assign window to stream\n    checkCudaErrors(cudaStreamSetAttribute(stream, streamAttrID, &streamAttrValue));\n\n    // Demote any previous persisting lines\n    checkCudaErrors(cudaCtxResetPersistingL2Cache());\n\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    kernCacheSegmentTest<<<blocks, threads, 0, stream>>>(\n        dataDevicePointer, dataSize, bigDataDevicePointer, bigDataSize, 0xAFFFF);\n\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // Free memory\n    checkCudaErrors(cudaFreeHost(dataHostPointer));\n    checkCudaErrors(cudaFreeHost(bigDataHostPointer));\n    checkCudaErrors(cudaFree(dataDevicePointer));\n    checkCudaErrors(cudaFree(bigDataDevicePointer));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h> // helper functions for SDK examples\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\nhipAccessPolicyWindow initAccessPolicyWindow(void)\n{\n    hipAccessPolicyWindow accessPolicyWindow = {0};\n    accessPolicyWindow.base_ptr               = (void *)0;\n    accessPolicyWindow.num_bytes              = 0;\n    accessPolicyWindow.hitRatio               = 0.f;\n    accessPolicyWindow.hitProp                = hipAccessPropertyNormal;\n    accessPolicyWindow.missProp               = hipAccessPropertyStreaming;\n    return accessPolicyWindow;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param data  input data in global memory\n//! @param dataSize  input data size\n//! @param bigData  input bigData in global memory\n//! @param bigDataSize  input bigData size\n//! @param hitcount how many data access are done within block\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void kernCacheSegmentTest(int *data, int dataSize, int *trash, int bigDataSize, int hitCount)\n{\n    __shared__ unsigned int hit;\n    int                     row    = blockIdx.y * blockDim.y + threadIdx.y;\n    int                     col    = blockIdx.x * blockDim.x + threadIdx.x;\n    int                     tID    = row * blockDim.y + col;\n    uint32_t                psRand = tID;\n\n    atomicExch(&hit, 0);\n    __syncthreads();\n    while (hit < hitCount) {\n        psRand ^= psRand << 13;\n        psRand ^= psRand >> 17;\n        psRand ^= psRand << 5;\n\n        int idx = tID - psRand;\n        if (idx < 0) {\n            idx = -idx;\n        }\n\n        if ((tID % 2) == 0) {\n            data[psRand % dataSize] = data[psRand % dataSize] + data[idx % dataSize];\n        }\n        else {\n            trash[psRand % bigDataSize] = trash[psRand % bigDataSize] + trash[idx % bigDataSize];\n        }\n\n        atomicAdd(&hit, 1);\n    }\n}\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    bool                   bTestResult = true;\n    hipAccessPolicyWindow accessPolicyWindow;\n    hipDeviceProp_t         deviceProp;\n    hipLaunchAttributeValue    streamAttrValue;\n    hipStream_t           stream;\n    hipLaunchAttributeID       streamAttrID;\n    dim3                   threads(32, 32);\n    int                   *dataDevicePointer;\n    int                   *dataHostPointer;\n    int                    dataSize;\n    int                   *bigDataDevicePointer;\n    int                   *bigDataHostPointer;\n    int                    bigDataSize;\n    StopWatchInterface    *timer = 0;\n\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n    // Get device properties\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n    dim3 blocks(deviceProp.maxGridSize[1], 1);\n\n    // Make sure device the l2 optimization\n    if (deviceProp.persistingL2CacheMaxSize == 0) {\n        printf(\"Waiving execution as device %d does not support persisting L2 \"\n               \"Caching\\n\",\n               devID);\n        exit(EXIT_WAIVED);\n    }\n\n    // Create stream to assiocate with window\n    checkCudaErrors(hipStreamCreate(&stream));\n\n    // Set the amount of l2 cache that will be persisting to maximum the device\n    // can support\n    checkCudaErrors(hipDeviceSetLimit(cudaLimitPersistingL2CacheSize, deviceProp.persistingL2CacheMaxSize));\n\n    // Stream attribute to set\n    streamAttrID = hipLaunchAttributeAccessPolicyWindow;\n\n    // Default window\n    streamAttrValue.accessPolicyWindow = initAccessPolicyWindow();\n    accessPolicyWindow                 = initAccessPolicyWindow();\n\n    // Allocate size of both buffers\n    bigDataSize = (deviceProp.l2CacheSize * 4) / sizeof(int);\n    dataSize    = (deviceProp.l2CacheSize / 4) / sizeof(int);\n\n    // Allocate data\n    checkCudaErrors(hipHostMalloc(&dataHostPointer, dataSize * sizeof(int)));\n    checkCudaErrors(hipHostMalloc(&bigDataHostPointer, bigDataSize * sizeof(int)));\n\n    for (int i = 0; i < bigDataSize; ++i) {\n        if (i < dataSize) {\n            dataHostPointer[i] = i;\n        }\n\n        bigDataHostPointer[bigDataSize - i - 1] = i;\n    }\n\n    checkCudaErrors(hipMalloc((void **)&dataDevicePointer, dataSize * sizeof(int)));\n    checkCudaErrors(hipMalloc((void **)&bigDataDevicePointer, bigDataSize * sizeof(int)));\n    checkCudaErrors(\n        hipMemcpyAsync(dataDevicePointer, dataHostPointer, dataSize * sizeof(int), hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemcpyAsync(\n        bigDataDevicePointer, bigDataHostPointer, bigDataSize * sizeof(int), hipMemcpyHostToDevice, stream));\n\n    // Make a window for the buffer of interest\n    accessPolicyWindow.base_ptr        = (void *)dataDevicePointer;\n    accessPolicyWindow.num_bytes       = dataSize * sizeof(int);\n    accessPolicyWindow.hitRatio        = 1.f;\n    accessPolicyWindow.hitProp         = hipAccessPropertyPersisting;\n    accessPolicyWindow.missProp        = hipAccessPropertyNormal;\n    streamAttrValue.accessPolicyWindow = accessPolicyWindow;\n\n    // Assign window to stream\n    checkCudaErrors(hipStreamSetAttribute(stream, streamAttrID, &streamAttrValue));\n\n    // Demote any previous persisting lines\n    checkCudaErrors(cudaCtxResetPersistingL2Cache());\n\n    checkCudaErrors(hipStreamSynchronize(stream));\n    kernCacheSegmentTest<<<blocks, threads, 0, stream>>>(\n        dataDevicePointer, dataSize, bigDataDevicePointer, bigDataSize, 0xAFFFF);\n\n    checkCudaErrors(hipStreamSynchronize(stream));\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // Free memory\n    checkCudaErrors(hipHostFree(dataHostPointer));\n    checkCudaErrors(hipHostFree(bigDataHostPointer));\n    checkCudaErrors(hipFree(dataDevicePointer));\n    checkCudaErrors(hipFree(bigDataDevicePointer));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleAttributes/simpleAttributes.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <assert.h>\n#include <helper_cuda.h>\n\n#include \"mergeSort_common.h\"\n\ninline __device__ void Comparator(uint &keyA, uint &valA, uint &keyB, uint &valB, uint arrowDir)\n{\n    uint t;\n\n    if ((keyA > keyB) == arrowDir) {\n        t    = keyA;\n        keyA = keyB;\n        keyB = t;\n        t    = valA;\n        valA = valB;\n        valB = t;\n    }\n}\n\n__global__ void\nbitonicSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint sortDir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more short vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < arrayLength; size <<= 1) {\n        // Bitonic merge\n        uint dir = (threadIdx.x & (size / 2)) != 0;\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n        }\n    }\n\n    // ddd == sortDir for the last bitonic merge step\n    {\n        for (uint stride = arrayLength / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], sortDir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n// Helper function (also used by odd-even merge sort)\nextern \"C\" uint factorRadix2(uint *log2L, uint L)\n{\n    if (!L) {\n        *log2L = 0;\n        return 0;\n    }\n    else {\n        for (*log2L = 0; (L & 1) == 0; L >>= 1, *log2L++)\n            ;\n\n        return L;\n    }\n}\n\nextern \"C\" void bitonicSortShared(uint *d_DstKey,\n                                  uint *d_DstVal,\n                                  uint *d_SrcKey,\n                                  uint *d_SrcVal,\n                                  uint  batchSize,\n                                  uint  arrayLength,\n                                  uint  sortDir)\n{\n    // Nothing to sort\n    if (arrayLength < 2) {\n        return;\n    }\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    assert(arrayLength <= SHARED_SIZE_LIMIT);\n    assert((batchSize * arrayLength) % SHARED_SIZE_LIMIT == 0);\n\n    bitonicSortSharedKernel<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, sortDir);\n    getLastCudaError(\"bitonicSortSharedKernel<<<>>> failed!\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 3: merge elementary intervals\n////////////////////////////////////////////////////////////////////////////////\nstatic inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }\n\nstatic inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }\n\ntemplate <uint sortDir>\nstatic inline __device__ void\nComparatorExtended(uint &keyA, uint &valA, uint &flagA, uint &keyB, uint &valB, uint &flagB, uint arrowDir)\n{\n    uint t;\n\n    if ((!(flagA || flagB) && ((keyA > keyB) == arrowDir)) || ((arrowDir == sortDir) && (flagA == 1))\n        || ((arrowDir != sortDir) && (flagB == 1))) {\n        t     = keyA;\n        keyA  = keyB;\n        keyB  = t;\n        t     = valA;\n        valA  = valB;\n        valB  = t;\n        t     = flagA;\n        flagA = flagB;\n        flagB = t;\n    }\n}\n\ntemplate <uint sortDir>\n__global__ void bitonicMergeElementaryIntervalsKernel(uint *d_DstKey,\n                                                      uint *d_DstVal,\n                                                      uint *d_SrcKey,\n                                                      uint *d_SrcVal,\n                                                      uint *d_LimitsA,\n                                                      uint *d_LimitsB,\n                                                      uint  stride,\n                                                      uint  N)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_val[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_inf[2 * SAMPLE_STRIDE];\n\n    const uint intervalI   = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);\n    const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;\n    d_SrcKey += segmentBase;\n    d_SrcVal += segmentBase;\n    d_DstKey += segmentBase;\n    d_DstVal += segmentBase;\n\n    // Set up threadblock-wide parameters\n    __shared__ uint startSrcA, lenSrcA, startSrcB, lenSrcB, startDst;\n\n    if (threadIdx.x == 0) {\n        uint segmentElementsA = stride;\n        uint segmentElementsB = umin(stride, N - segmentBase - stride);\n        uint segmentSamplesA  = stride / SAMPLE_STRIDE;\n        uint segmentSamplesB  = getSampleCount(segmentElementsB);\n        uint segmentSamples   = segmentSamplesA + segmentSamplesB;\n\n        startSrcA = d_LimitsA[blockIdx.x];\n        startSrcB = d_LimitsB[blockIdx.x];\n        startDst  = startSrcA + startSrcB;\n\n        uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;\n        uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;\n        lenSrcA      = endSrcA - startSrcA;\n        lenSrcB      = endSrcB - startSrcB;\n    }\n\n    s_inf[threadIdx.x + 0]             = 1;\n    s_inf[threadIdx.x + SAMPLE_STRIDE] = 1;\n\n    // Load input data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        s_key[threadIdx.x] = d_SrcKey[0 + startSrcA + threadIdx.x];\n        s_val[threadIdx.x] = d_SrcVal[0 + startSrcA + threadIdx.x];\n        s_inf[threadIdx.x] = 0;\n    }\n\n    // Prepare for bitonic merge by inversing the ordering\n    if (threadIdx.x < lenSrcB) {\n        s_key[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcKey[stride + startSrcB + threadIdx.x];\n        s_val[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcVal[stride + startSrcB + threadIdx.x];\n        s_inf[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = 0;\n    }\n\n    //\"Extended\" bitonic merge\n    for (uint stride = SAMPLE_STRIDE; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n        uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n        ComparatorExtended<sortDir>(s_key[pos + 0],\n                                    s_val[pos + 0],\n                                    s_inf[pos + 0],\n                                    s_key[pos + stride],\n                                    s_val[pos + stride],\n                                    s_inf[pos + stride],\n                                    sortDir);\n    }\n\n    // Store sorted data\n    cg::sync(cta);\n    d_DstKey += startDst;\n    d_DstVal += startDst;\n\n    if (threadIdx.x < lenSrcA) {\n        d_DstKey[threadIdx.x] = s_key[threadIdx.x];\n        d_DstVal[threadIdx.x] = s_val[threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        d_DstKey[lenSrcA + threadIdx.x] = s_key[lenSrcA + threadIdx.x];\n        d_DstVal[lenSrcA + threadIdx.x] = s_val[lenSrcA + threadIdx.x];\n    }\n}\n\nextern \"C\" void bitonicMergeElementaryIntervals(uint *d_DstKey,\n                                                uint *d_DstVal,\n                                                uint *d_SrcKey,\n                                                uint *d_SrcVal,\n                                                uint *d_LimitsA,\n                                                uint *d_LimitsB,\n                                                uint  stride,\n                                                uint  N,\n                                                uint  sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n\n    uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;\n\n    if (sortDir) {\n        bitonicMergeElementaryIntervalsKernel<1U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<1> failed\\n\");\n    }\n    else {\n        bitonicMergeElementaryIntervalsKernel<0U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<0> failed\\n\");\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <assert.h>\n#include <hip/hip_runtime_api.h>\n\n#include \"mergeSort_common.h\"\n\ninline __device__ void Comparator(uint &keyA, uint &valA, uint &keyB, uint &valB, uint arrowDir)\n{\n    uint t;\n\n    if ((keyA > keyB) == arrowDir) {\n        t    = keyA;\n        keyA = keyB;\n        keyB = t;\n        t    = valA;\n        valA = valB;\n        valB = t;\n    }\n}\n\n__global__ void\nbitonicSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint sortDir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more short vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < arrayLength; size <<= 1) {\n        // Bitonic merge\n        uint dir = (threadIdx.x & (size / 2)) != 0;\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n        }\n    }\n\n    // ddd == sortDir for the last bitonic merge step\n    {\n        for (uint stride = arrayLength / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], sortDir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n// Helper function (also used by odd-even merge sort)\nextern \"C\" uint factorRadix2(uint *log2L, uint L)\n{\n    if (!L) {\n        *log2L = 0;\n        return 0;\n    }\n    else {\n        for (*log2L = 0; (L & 1) == 0; L >>= 1, *log2L++)\n            ;\n\n        return L;\n    }\n}\n\nextern \"C\" void bitonicSortShared(uint *d_DstKey,\n                                  uint *d_DstVal,\n                                  uint *d_SrcKey,\n                                  uint *d_SrcVal,\n                                  uint  batchSize,\n                                  uint  arrayLength,\n                                  uint  sortDir)\n{\n    // Nothing to sort\n    if (arrayLength < 2) {\n        return;\n    }\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    assert(arrayLength <= SHARED_SIZE_LIMIT);\n    assert((batchSize * arrayLength) % SHARED_SIZE_LIMIT == 0);\n\n    bitonicSortSharedKernel<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, sortDir);\n    getLastCudaError(\"bitonicSortSharedKernel<<<>>> failed!\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 3: merge elementary intervals\n////////////////////////////////////////////////////////////////////////////////\nstatic inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }\n\nstatic inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }\n\ntemplate <uint sortDir>\nstatic inline __device__ void\nComparatorExtended(uint &keyA, uint &valA, uint &flagA, uint &keyB, uint &valB, uint &flagB, uint arrowDir)\n{\n    uint t;\n\n    if ((!(flagA || flagB) && ((keyA > keyB) == arrowDir)) || ((arrowDir == sortDir) && (flagA == 1))\n        || ((arrowDir != sortDir) && (flagB == 1))) {\n        t     = keyA;\n        keyA  = keyB;\n        keyB  = t;\n        t     = valA;\n        valA  = valB;\n        valB  = t;\n        t     = flagA;\n        flagA = flagB;\n        flagB = t;\n    }\n}\n\ntemplate <uint sortDir>\n__global__ void bitonicMergeElementaryIntervalsKernel(uint *d_DstKey,\n                                                      uint *d_DstVal,\n                                                      uint *d_SrcKey,\n                                                      uint *d_SrcVal,\n                                                      uint *d_LimitsA,\n                                                      uint *d_LimitsB,\n                                                      uint  stride,\n                                                      uint  N)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_val[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_inf[2 * SAMPLE_STRIDE];\n\n    const uint intervalI   = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);\n    const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;\n    d_SrcKey += segmentBase;\n    d_SrcVal += segmentBase;\n    d_DstKey += segmentBase;\n    d_DstVal += segmentBase;\n\n    // Set up threadblock-wide parameters\n    __shared__ uint startSrcA, lenSrcA, startSrcB, lenSrcB, startDst;\n\n    if (threadIdx.x == 0) {\n        uint segmentElementsA = stride;\n        uint segmentElementsB = umin(stride, N - segmentBase - stride);\n        uint segmentSamplesA  = stride / SAMPLE_STRIDE;\n        uint segmentSamplesB  = getSampleCount(segmentElementsB);\n        uint segmentSamples   = segmentSamplesA + segmentSamplesB;\n\n        startSrcA = d_LimitsA[blockIdx.x];\n        startSrcB = d_LimitsB[blockIdx.x];\n        startDst  = startSrcA + startSrcB;\n\n        uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;\n        uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;\n        lenSrcA      = endSrcA - startSrcA;\n        lenSrcB      = endSrcB - startSrcB;\n    }\n\n    s_inf[threadIdx.x + 0]             = 1;\n    s_inf[threadIdx.x + SAMPLE_STRIDE] = 1;\n\n    // Load input data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        s_key[threadIdx.x] = d_SrcKey[0 + startSrcA + threadIdx.x];\n        s_val[threadIdx.x] = d_SrcVal[0 + startSrcA + threadIdx.x];\n        s_inf[threadIdx.x] = 0;\n    }\n\n    // Prepare for bitonic merge by inversing the ordering\n    if (threadIdx.x < lenSrcB) {\n        s_key[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcKey[stride + startSrcB + threadIdx.x];\n        s_val[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = d_SrcVal[stride + startSrcB + threadIdx.x];\n        s_inf[2 * SAMPLE_STRIDE - 1 - threadIdx.x] = 0;\n    }\n\n    //\"Extended\" bitonic merge\n    for (uint stride = SAMPLE_STRIDE; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n        uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n        ComparatorExtended<sortDir>(s_key[pos + 0],\n                                    s_val[pos + 0],\n                                    s_inf[pos + 0],\n                                    s_key[pos + stride],\n                                    s_val[pos + stride],\n                                    s_inf[pos + stride],\n                                    sortDir);\n    }\n\n    // Store sorted data\n    cg::sync(cta);\n    d_DstKey += startDst;\n    d_DstVal += startDst;\n\n    if (threadIdx.x < lenSrcA) {\n        d_DstKey[threadIdx.x] = s_key[threadIdx.x];\n        d_DstVal[threadIdx.x] = s_val[threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        d_DstKey[lenSrcA + threadIdx.x] = s_key[lenSrcA + threadIdx.x];\n        d_DstVal[lenSrcA + threadIdx.x] = s_val[lenSrcA + threadIdx.x];\n    }\n}\n\nextern \"C\" void bitonicMergeElementaryIntervals(uint *d_DstKey,\n                                                uint *d_DstVal,\n                                                uint *d_SrcKey,\n                                                uint *d_SrcVal,\n                                                uint *d_LimitsA,\n                                                uint *d_LimitsB,\n                                                uint  stride,\n                                                uint  N,\n                                                uint  sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n\n    uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;\n\n    if (sortDir) {\n        bitonicMergeElementaryIntervalsKernel<1U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<1> failed\\n\");\n    }\n    else {\n        bitonicMergeElementaryIntervalsKernel<0U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<0> failed\\n\");\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/mergeSort/bitonic.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Based on \"Designing efficient sorting algorithms for manycore GPUs\"\n * by Nadathur Satish, Mark Harris, and Michael Garland\n * http://mgarland.org/files/papers/gpusort-ipdps09.pdf\n *\n * Victor Podlozhnyuk 09/24/2009\n */\n\n#include <assert.h>\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n\n#include \"mergeSort_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Helper functions\n////////////////////////////////////////////////////////////////////////////////\nstatic inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }\n\nstatic inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }\n\n#define W (sizeof(uint) * 8)\nstatic inline __device__ uint nextPowerOfTwo(uint x)\n{\n    /*\n        --x;\n        x |= x >> 1;\n        x |= x >> 2;\n        x |= x >> 4;\n        x |= x >> 8;\n        x |= x >> 16;\n        return ++x;\n    */\n    return 1U << (W - __clz(x - 1));\n}\n\ntemplate <uint sortDir> static inline __device__ uint binarySearchInclusive(uint val, uint *data, uint L, uint stride)\n{\n    if (L == 0) {\n        return 0;\n    }\n\n    uint pos = 0;\n\n    for (; stride > 0; stride >>= 1) {\n        uint newPos = umin(pos + stride, L);\n\n        if ((sortDir && (data[newPos - 1] <= val)) || (!sortDir && (data[newPos - 1] >= val))) {\n            pos = newPos;\n        }\n    }\n\n    return pos;\n}\n\ntemplate <uint sortDir> static inline __device__ uint binarySearchExclusive(uint val, uint *data, uint L, uint stride)\n{\n    if (L == 0) {\n        return 0;\n    }\n\n    uint pos = 0;\n\n    for (; stride > 0; stride >>= 1) {\n        uint newPos = umin(pos + stride, L);\n\n        if ((sortDir && (data[newPos - 1] < val)) || (!sortDir && (data[newPos - 1] > val))) {\n            pos = newPos;\n        }\n    }\n\n    return pos;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Bottom-level merge sort (binary search-based)\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\n__global__ void mergeSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint  s_val[SHARED_SIZE_LIMIT];\n\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint stride = 1; stride < arrayLength; stride <<= 1) {\n        uint  lPos    = threadIdx.x & (stride - 1);\n        uint *baseKey = s_key + 2 * (threadIdx.x - lPos);\n        uint *baseVal = s_val + 2 * (threadIdx.x - lPos);\n\n        cg::sync(cta);\n        uint keyA = baseKey[lPos + 0];\n        uint valA = baseVal[lPos + 0];\n        uint keyB = baseKey[lPos + stride];\n        uint valB = baseVal[lPos + stride];\n        uint posA = binarySearchExclusive<sortDir>(keyA, baseKey + stride, stride, stride) + lPos;\n        uint posB = binarySearchInclusive<sortDir>(keyB, baseKey + 0, stride, stride) + lPos;\n\n        cg::sync(cta);\n        baseKey[posA] = keyA;\n        baseVal[posA] = valA;\n        baseKey[posB] = keyB;\n        baseVal[posB] = valB;\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\nstatic void mergeSortShared(uint *d_DstKey,\n                            uint *d_DstVal,\n                            uint *d_SrcKey,\n                            uint *d_SrcVal,\n                            uint  batchSize,\n                            uint  arrayLength,\n                            uint  sortDir)\n{\n    if (arrayLength < 2) {\n        return;\n    }\n\n    assert(SHARED_SIZE_LIMIT % arrayLength == 0);\n    assert(((batchSize * arrayLength) % SHARED_SIZE_LIMIT) == 0);\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (sortDir) {\n        mergeSortSharedKernel<1U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);\n        getLastCudaError(\"mergeSortShared<1><<<>>> failed\\n\");\n    }\n    else {\n        mergeSortSharedKernel<0U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);\n        getLastCudaError(\"mergeSortShared<0><<<>>> failed\\n\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 1: generate sample ranks\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\n__global__ void\ngenerateSampleRanksKernel(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint threadCount)\n{\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (pos >= threadCount) {\n        return;\n    }\n\n    const uint i           = pos & ((stride / SAMPLE_STRIDE) - 1);\n    const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);\n    d_SrcKey += segmentBase;\n    d_RanksA += segmentBase / SAMPLE_STRIDE;\n    d_RanksB += segmentBase / SAMPLE_STRIDE;\n\n    const uint segmentElementsA = stride;\n    const uint segmentElementsB = umin(stride, N - segmentBase - stride);\n    const uint segmentSamplesA  = getSampleCount(segmentElementsA);\n    const uint segmentSamplesB  = getSampleCount(segmentElementsB);\n\n    if (i < segmentSamplesA) {\n        d_RanksA[i] = i * SAMPLE_STRIDE;\n        d_RanksB[i] = binarySearchExclusive<sortDir>(\n            d_SrcKey[i * SAMPLE_STRIDE], d_SrcKey + stride, segmentElementsB, nextPowerOfTwo(segmentElementsB));\n    }\n\n    if (i < segmentSamplesB) {\n        d_RanksB[(stride / SAMPLE_STRIDE) + i] = i * SAMPLE_STRIDE;\n        d_RanksA[(stride / SAMPLE_STRIDE) + i] = binarySearchInclusive<sortDir>(\n            d_SrcKey[stride + i * SAMPLE_STRIDE], d_SrcKey + 0, segmentElementsA, nextPowerOfTwo(segmentElementsA));\n    }\n}\n\nstatic void generateSampleRanks(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)\n                                                      : (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);\n\n    if (sortDir) {\n        generateSampleRanksKernel<1U>\n            <<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);\n        getLastCudaError(\"generateSampleRanksKernel<1U><<<>>> failed\\n\");\n    }\n    else {\n        generateSampleRanksKernel<0U>\n            <<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);\n        getLastCudaError(\"generateSampleRanksKernel<0U><<<>>> failed\\n\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 2: generate sample ranks and indices\n////////////////////////////////////////////////////////////////////////////////\n__global__ void mergeRanksAndIndicesKernel(uint *d_Limits, uint *d_Ranks, uint stride, uint N, uint threadCount)\n{\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (pos >= threadCount) {\n        return;\n    }\n\n    const uint i           = pos & ((stride / SAMPLE_STRIDE) - 1);\n    const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);\n    d_Ranks += (pos - i) * 2;\n    d_Limits += (pos - i) * 2;\n\n    const uint segmentElementsA = stride;\n    const uint segmentElementsB = umin(stride, N - segmentBase - stride);\n    const uint segmentSamplesA  = getSampleCount(segmentElementsA);\n    const uint segmentSamplesB  = getSampleCount(segmentElementsB);\n\n    if (i < segmentSamplesA) {\n        uint dstPos = binarySearchExclusive<1U>(\n                          d_Ranks[i], d_Ranks + segmentSamplesA, segmentSamplesB, nextPowerOfTwo(segmentSamplesB))\n                    + i;\n        d_Limits[dstPos] = d_Ranks[i];\n    }\n\n    if (i < segmentSamplesB) {\n        uint dstPos = binarySearchInclusive<1U>(\n                          d_Ranks[segmentSamplesA + i], d_Ranks, segmentSamplesA, nextPowerOfTwo(segmentSamplesA))\n                    + i;\n        d_Limits[dstPos] = d_Ranks[segmentSamplesA + i];\n    }\n}\n\nstatic void mergeRanksAndIndices(uint *d_LimitsA, uint *d_LimitsB, uint *d_RanksA, uint *d_RanksB, uint stride, uint N)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)\n                                                      : (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);\n\n    mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsA, d_RanksA, stride, N, threadCount);\n    getLastCudaError(\"mergeRanksAndIndicesKernel(A)<<<>>> failed\\n\");\n\n    mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsB, d_RanksB, stride, N, threadCount);\n    getLastCudaError(\"mergeRanksAndIndicesKernel(B)<<<>>> failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 3: merge elementary intervals\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\ninline __device__ void merge(uint            *dstKey,\n                             uint            *dstVal,\n                             uint            *srcAKey,\n                             uint            *srcAVal,\n                             uint            *srcBKey,\n                             uint            *srcBVal,\n                             uint             lenA,\n                             uint             nPowTwoLenA,\n                             uint             lenB,\n                             uint             nPowTwoLenB,\n                             cg::thread_block cta)\n{\n    uint keyA, valA, keyB, valB, dstPosA, dstPosB;\n\n    if (threadIdx.x < lenA) {\n        keyA    = srcAKey[threadIdx.x];\n        valA    = srcAVal[threadIdx.x];\n        dstPosA = binarySearchExclusive<sortDir>(keyA, srcBKey, lenB, nPowTwoLenB) + threadIdx.x;\n    }\n\n    if (threadIdx.x < lenB) {\n        keyB    = srcBKey[threadIdx.x];\n        valB    = srcBVal[threadIdx.x];\n        dstPosB = binarySearchInclusive<sortDir>(keyB, srcAKey, lenA, nPowTwoLenA) + threadIdx.x;\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < lenA) {\n        dstKey[dstPosA] = keyA;\n        dstVal[dstPosA] = valA;\n    }\n\n    if (threadIdx.x < lenB) {\n        dstKey[dstPosB] = keyB;\n        dstVal[dstPosB] = valB;\n    }\n}\n\ntemplate <uint sortDir>\n__global__ void mergeElementaryIntervalsKernel(uint *d_DstKey,\n                                               uint *d_DstVal,\n                                               uint *d_SrcKey,\n                                               uint *d_SrcVal,\n                                               uint *d_LimitsA,\n                                               uint *d_LimitsB,\n                                               uint  stride,\n                                               uint  N)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_val[2 * SAMPLE_STRIDE];\n\n    const uint intervalI   = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);\n    const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;\n    d_SrcKey += segmentBase;\n    d_SrcVal += segmentBase;\n    d_DstKey += segmentBase;\n    d_DstVal += segmentBase;\n\n    // Set up threadblock-wide parameters\n    __shared__ uint startSrcA, startSrcB, lenSrcA, lenSrcB, startDstA, startDstB;\n\n    if (threadIdx.x == 0) {\n        uint segmentElementsA = stride;\n        uint segmentElementsB = umin(stride, N - segmentBase - stride);\n        uint segmentSamplesA  = getSampleCount(segmentElementsA);\n        uint segmentSamplesB  = getSampleCount(segmentElementsB);\n        uint segmentSamples   = segmentSamplesA + segmentSamplesB;\n\n        startSrcA    = d_LimitsA[blockIdx.x];\n        startSrcB    = d_LimitsB[blockIdx.x];\n        uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;\n        uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;\n        lenSrcA      = endSrcA - startSrcA;\n        lenSrcB      = endSrcB - startSrcB;\n        startDstA    = startSrcA + startSrcB;\n        startDstB    = startDstA + lenSrcA;\n    }\n\n    // Load main input data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        s_key[threadIdx.x + 0] = d_SrcKey[0 + startSrcA + threadIdx.x];\n        s_val[threadIdx.x + 0] = d_SrcVal[0 + startSrcA + threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        s_key[threadIdx.x + SAMPLE_STRIDE] = d_SrcKey[stride + startSrcB + threadIdx.x];\n        s_val[threadIdx.x + SAMPLE_STRIDE] = d_SrcVal[stride + startSrcB + threadIdx.x];\n    }\n\n    // Merge data in shared memory\n    cg::sync(cta);\n    merge<sortDir>(s_key,\n                   s_val,\n                   s_key + 0,\n                   s_val + 0,\n                   s_key + SAMPLE_STRIDE,\n                   s_val + SAMPLE_STRIDE,\n                   lenSrcA,\n                   SAMPLE_STRIDE,\n                   lenSrcB,\n                   SAMPLE_STRIDE,\n                   cta);\n\n    // Store merged data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        d_DstKey[startDstA + threadIdx.x] = s_key[threadIdx.x];\n        d_DstVal[startDstA + threadIdx.x] = s_val[threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        d_DstKey[startDstB + threadIdx.x] = s_key[lenSrcA + threadIdx.x];\n        d_DstVal[startDstB + threadIdx.x] = s_val[lenSrcA + threadIdx.x];\n    }\n}\n\nstatic void mergeElementaryIntervals(uint *d_DstKey,\n                                     uint *d_DstVal,\n                                     uint *d_SrcKey,\n                                     uint *d_SrcVal,\n                                     uint *d_LimitsA,\n                                     uint *d_LimitsB,\n                                     uint  stride,\n                                     uint  N,\n                                     uint  sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;\n\n    if (sortDir) {\n        mergeElementaryIntervalsKernel<1U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<1> failed\\n\");\n    }\n    else {\n        mergeElementaryIntervalsKernel<0U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<0> failed\\n\");\n    }\n}\n\nextern \"C\" void bitonicSortShared(uint *d_DstKey,\n                                  uint *d_DstVal,\n                                  uint *d_SrcKey,\n                                  uint *d_SrcVal,\n                                  uint  batchSize,\n                                  uint  arrayLength,\n                                  uint  sortDir);\n\nextern \"C\" void bitonicMergeElementaryIntervals(uint *d_DstKey,\n                                                uint *d_DstVal,\n                                                uint *d_SrcKey,\n                                                uint *d_SrcVal,\n                                                uint *d_LimitsA,\n                                                uint *d_LimitsB,\n                                                uint  stride,\n                                                uint  N,\n                                                uint  sortDir);\n\nstatic uint      *d_RanksA, *d_RanksB, *d_LimitsA, *d_LimitsB;\nstatic const uint MAX_SAMPLE_COUNT = 32768;\n\nextern \"C\" void initMergeSort(void)\n{\n    checkCudaErrors(cudaMalloc((void **)&d_RanksA, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(cudaMalloc((void **)&d_RanksB, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(cudaMalloc((void **)&d_LimitsA, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(cudaMalloc((void **)&d_LimitsB, MAX_SAMPLE_COUNT * sizeof(uint)));\n}\n\nextern \"C\" void closeMergeSort(void)\n{\n    checkCudaErrors(cudaFree(d_RanksA));\n    checkCudaErrors(cudaFree(d_RanksB));\n    checkCudaErrors(cudaFree(d_LimitsB));\n    checkCudaErrors(cudaFree(d_LimitsA));\n}\n\nextern \"C\" void mergeSort(uint *d_DstKey,\n                          uint *d_DstVal,\n                          uint *d_BufKey,\n                          uint *d_BufVal,\n                          uint *d_SrcKey,\n                          uint *d_SrcVal,\n                          uint  N,\n                          uint  sortDir)\n{\n    uint stageCount = 0;\n\n    for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1, stageCount++)\n        ;\n\n    uint *ikey, *ival, *okey, *oval;\n\n    if (stageCount & 1) {\n        ikey = d_BufKey;\n        ival = d_BufVal;\n        okey = d_DstKey;\n        oval = d_DstVal;\n    }\n    else {\n        ikey = d_DstKey;\n        ival = d_DstVal;\n        okey = d_BufKey;\n        oval = d_BufVal;\n    }\n\n    assert(N <= (SAMPLE_STRIDE * MAX_SAMPLE_COUNT));\n    assert(N % SHARED_SIZE_LIMIT == 0);\n    mergeSortShared(ikey, ival, d_SrcKey, d_SrcVal, N / SHARED_SIZE_LIMIT, SHARED_SIZE_LIMIT, sortDir);\n\n    for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1) {\n        uint lastSegmentElements = N % (2 * stride);\n\n        // Find sample ranks and prepare for limiters merge\n        generateSampleRanks(d_RanksA, d_RanksB, ikey, stride, N, sortDir);\n\n        // Merge ranks and indices\n        mergeRanksAndIndices(d_LimitsA, d_LimitsB, d_RanksA, d_RanksB, stride, N);\n\n        // Merge elementary intervals\n        mergeElementaryIntervals(okey, oval, ikey, ival, d_LimitsA, d_LimitsB, stride, N, sortDir);\n\n        if (lastSegmentElements <= stride) {\n            // Last merge segment consists of a single array which just needs to be\n            // passed through\n            checkCudaErrors(cudaMemcpy(okey + (N - lastSegmentElements),\n                                       ikey + (N - lastSegmentElements),\n                                       lastSegmentElements * sizeof(uint),\n                                       cudaMemcpyDeviceToDevice));\n            checkCudaErrors(cudaMemcpy(oval + (N - lastSegmentElements),\n                                       ival + (N - lastSegmentElements),\n                                       lastSegmentElements * sizeof(uint),\n                                       cudaMemcpyDeviceToDevice));\n        }\n\n        uint *t;\n        t    = ikey;\n        ikey = okey;\n        okey = t;\n        t    = ival;\n        ival = oval;\n        oval = t;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Based on \"Designing efficient sorting algorithms for manycore GPUs\"\n * by Nadathur Satish, Mark Harris, and Michael Garland\n * http://mgarland.org/files/papers/gpusort-ipdps09.pdf\n *\n * Victor Podlozhnyuk 09/24/2009\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n\n#include \"mergeSort_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Helper functions\n////////////////////////////////////////////////////////////////////////////////\nstatic inline __host__ __device__ uint iDivUp(uint a, uint b) { return ((a % b) == 0) ? (a / b) : (a / b + 1); }\n\nstatic inline __host__ __device__ uint getSampleCount(uint dividend) { return iDivUp(dividend, SAMPLE_STRIDE); }\n\n#define W (sizeof(uint) * 8)\nstatic inline __device__ uint nextPowerOfTwo(uint x)\n{\n    /*\n        --x;\n        x |= x >> 1;\n        x |= x >> 2;\n        x |= x >> 4;\n        x |= x >> 8;\n        x |= x >> 16;\n        return ++x;\n    */\n    return 1U << (W - __clz(x - 1));\n}\n\ntemplate <uint sortDir> static inline __device__ uint binarySearchInclusive(uint val, uint *data, uint L, uint stride)\n{\n    if (L == 0) {\n        return 0;\n    }\n\n    uint pos = 0;\n\n    for (; stride > 0; stride >>= 1) {\n        uint newPos = umin(pos + stride, L);\n\n        if ((sortDir && (data[newPos - 1] <= val)) || (!sortDir && (data[newPos - 1] >= val))) {\n            pos = newPos;\n        }\n    }\n\n    return pos;\n}\n\ntemplate <uint sortDir> static inline __device__ uint binarySearchExclusive(uint val, uint *data, uint L, uint stride)\n{\n    if (L == 0) {\n        return 0;\n    }\n\n    uint pos = 0;\n\n    for (; stride > 0; stride >>= 1) {\n        uint newPos = umin(pos + stride, L);\n\n        if ((sortDir && (data[newPos - 1] < val)) || (!sortDir && (data[newPos - 1] > val))) {\n            pos = newPos;\n        }\n    }\n\n    return pos;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Bottom-level merge sort (binary search-based)\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\n__global__ void mergeSortSharedKernel(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint  s_val[SHARED_SIZE_LIMIT];\n\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint stride = 1; stride < arrayLength; stride <<= 1) {\n        uint  lPos    = threadIdx.x & (stride - 1);\n        uint *baseKey = s_key + 2 * (threadIdx.x - lPos);\n        uint *baseVal = s_val + 2 * (threadIdx.x - lPos);\n\n        cg::sync(cta);\n        uint keyA = baseKey[lPos + 0];\n        uint valA = baseVal[lPos + 0];\n        uint keyB = baseKey[lPos + stride];\n        uint valB = baseVal[lPos + stride];\n        uint posA = binarySearchExclusive<sortDir>(keyA, baseKey + stride, stride, stride) + lPos;\n        uint posB = binarySearchInclusive<sortDir>(keyB, baseKey + 0, stride, stride) + lPos;\n\n        cg::sync(cta);\n        baseKey[posA] = keyA;\n        baseVal[posA] = valA;\n        baseKey[posB] = keyB;\n        baseVal[posB] = valB;\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\nstatic void mergeSortShared(uint *d_DstKey,\n                            uint *d_DstVal,\n                            uint *d_SrcKey,\n                            uint *d_SrcVal,\n                            uint  batchSize,\n                            uint  arrayLength,\n                            uint  sortDir)\n{\n    if (arrayLength < 2) {\n        return;\n    }\n\n    assert(SHARED_SIZE_LIMIT % arrayLength == 0);\n    assert(((batchSize * arrayLength) % SHARED_SIZE_LIMIT) == 0);\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (sortDir) {\n        mergeSortSharedKernel<1U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);\n        getLastCudaError(\"mergeSortShared<1><<<>>> failed\\n\");\n    }\n    else {\n        mergeSortSharedKernel<0U><<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength);\n        getLastCudaError(\"mergeSortShared<0><<<>>> failed\\n\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 1: generate sample ranks\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\n__global__ void\ngenerateSampleRanksKernel(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint threadCount)\n{\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (pos >= threadCount) {\n        return;\n    }\n\n    const uint i           = pos & ((stride / SAMPLE_STRIDE) - 1);\n    const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);\n    d_SrcKey += segmentBase;\n    d_RanksA += segmentBase / SAMPLE_STRIDE;\n    d_RanksB += segmentBase / SAMPLE_STRIDE;\n\n    const uint segmentElementsA = stride;\n    const uint segmentElementsB = umin(stride, N - segmentBase - stride);\n    const uint segmentSamplesA  = getSampleCount(segmentElementsA);\n    const uint segmentSamplesB  = getSampleCount(segmentElementsB);\n\n    if (i < segmentSamplesA) {\n        d_RanksA[i] = i * SAMPLE_STRIDE;\n        d_RanksB[i] = binarySearchExclusive<sortDir>(\n            d_SrcKey[i * SAMPLE_STRIDE], d_SrcKey + stride, segmentElementsB, nextPowerOfTwo(segmentElementsB));\n    }\n\n    if (i < segmentSamplesB) {\n        d_RanksB[(stride / SAMPLE_STRIDE) + i] = i * SAMPLE_STRIDE;\n        d_RanksA[(stride / SAMPLE_STRIDE) + i] = binarySearchInclusive<sortDir>(\n            d_SrcKey[stride + i * SAMPLE_STRIDE], d_SrcKey + 0, segmentElementsA, nextPowerOfTwo(segmentElementsA));\n    }\n}\n\nstatic void generateSampleRanks(uint *d_RanksA, uint *d_RanksB, uint *d_SrcKey, uint stride, uint N, uint sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)\n                                                      : (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);\n\n    if (sortDir) {\n        generateSampleRanksKernel<1U>\n            <<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);\n        getLastCudaError(\"generateSampleRanksKernel<1U><<<>>> failed\\n\");\n    }\n    else {\n        generateSampleRanksKernel<0U>\n            <<<iDivUp(threadCount, 256), 256>>>(d_RanksA, d_RanksB, d_SrcKey, stride, N, threadCount);\n        getLastCudaError(\"generateSampleRanksKernel<0U><<<>>> failed\\n\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 2: generate sample ranks and indices\n////////////////////////////////////////////////////////////////////////////////\n__global__ void mergeRanksAndIndicesKernel(uint *d_Limits, uint *d_Ranks, uint stride, uint N, uint threadCount)\n{\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (pos >= threadCount) {\n        return;\n    }\n\n    const uint i           = pos & ((stride / SAMPLE_STRIDE) - 1);\n    const uint segmentBase = (pos - i) * (2 * SAMPLE_STRIDE);\n    d_Ranks += (pos - i) * 2;\n    d_Limits += (pos - i) * 2;\n\n    const uint segmentElementsA = stride;\n    const uint segmentElementsB = umin(stride, N - segmentBase - stride);\n    const uint segmentSamplesA  = getSampleCount(segmentElementsA);\n    const uint segmentSamplesB  = getSampleCount(segmentElementsB);\n\n    if (i < segmentSamplesA) {\n        uint dstPos = binarySearchExclusive<1U>(\n                          d_Ranks[i], d_Ranks + segmentSamplesA, segmentSamplesB, nextPowerOfTwo(segmentSamplesB))\n                    + i;\n        d_Limits[dstPos] = d_Ranks[i];\n    }\n\n    if (i < segmentSamplesB) {\n        uint dstPos = binarySearchInclusive<1U>(\n                          d_Ranks[segmentSamplesA + i], d_Ranks, segmentSamplesA, nextPowerOfTwo(segmentSamplesA))\n                    + i;\n        d_Limits[dstPos] = d_Ranks[segmentSamplesA + i];\n    }\n}\n\nstatic void mergeRanksAndIndices(uint *d_LimitsA, uint *d_LimitsB, uint *d_RanksA, uint *d_RanksB, uint stride, uint N)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint threadCount = (lastSegmentElements > stride) ? (N + 2 * stride - lastSegmentElements) / (2 * SAMPLE_STRIDE)\n                                                      : (N - lastSegmentElements) / (2 * SAMPLE_STRIDE);\n\n    mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsA, d_RanksA, stride, N, threadCount);\n    getLastCudaError(\"mergeRanksAndIndicesKernel(A)<<<>>> failed\\n\");\n\n    mergeRanksAndIndicesKernel<<<iDivUp(threadCount, 256), 256>>>(d_LimitsB, d_RanksB, stride, N, threadCount);\n    getLastCudaError(\"mergeRanksAndIndicesKernel(B)<<<>>> failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge step 3: merge elementary intervals\n////////////////////////////////////////////////////////////////////////////////\ntemplate <uint sortDir>\ninline __device__ void merge(uint            *dstKey,\n                             uint            *dstVal,\n                             uint            *srcAKey,\n                             uint            *srcAVal,\n                             uint            *srcBKey,\n                             uint            *srcBVal,\n                             uint             lenA,\n                             uint             nPowTwoLenA,\n                             uint             lenB,\n                             uint             nPowTwoLenB,\n                             cg::thread_block cta)\n{\n    uint keyA, valA, keyB, valB, dstPosA, dstPosB;\n\n    if (threadIdx.x < lenA) {\n        keyA    = srcAKey[threadIdx.x];\n        valA    = srcAVal[threadIdx.x];\n        dstPosA = binarySearchExclusive<sortDir>(keyA, srcBKey, lenB, nPowTwoLenB) + threadIdx.x;\n    }\n\n    if (threadIdx.x < lenB) {\n        keyB    = srcBKey[threadIdx.x];\n        valB    = srcBVal[threadIdx.x];\n        dstPosB = binarySearchInclusive<sortDir>(keyB, srcAKey, lenA, nPowTwoLenA) + threadIdx.x;\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < lenA) {\n        dstKey[dstPosA] = keyA;\n        dstVal[dstPosA] = valA;\n    }\n\n    if (threadIdx.x < lenB) {\n        dstKey[dstPosB] = keyB;\n        dstVal[dstPosB] = valB;\n    }\n}\n\ntemplate <uint sortDir>\n__global__ void mergeElementaryIntervalsKernel(uint *d_DstKey,\n                                               uint *d_DstVal,\n                                               uint *d_SrcKey,\n                                               uint *d_SrcVal,\n                                               uint *d_LimitsA,\n                                               uint *d_LimitsB,\n                                               uint  stride,\n                                               uint  N)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_key[2 * SAMPLE_STRIDE];\n    __shared__ uint  s_val[2 * SAMPLE_STRIDE];\n\n    const uint intervalI   = blockIdx.x & ((2 * stride) / SAMPLE_STRIDE - 1);\n    const uint segmentBase = (blockIdx.x - intervalI) * SAMPLE_STRIDE;\n    d_SrcKey += segmentBase;\n    d_SrcVal += segmentBase;\n    d_DstKey += segmentBase;\n    d_DstVal += segmentBase;\n\n    // Set up threadblock-wide parameters\n    __shared__ uint startSrcA, startSrcB, lenSrcA, lenSrcB, startDstA, startDstB;\n\n    if (threadIdx.x == 0) {\n        uint segmentElementsA = stride;\n        uint segmentElementsB = umin(stride, N - segmentBase - stride);\n        uint segmentSamplesA  = getSampleCount(segmentElementsA);\n        uint segmentSamplesB  = getSampleCount(segmentElementsB);\n        uint segmentSamples   = segmentSamplesA + segmentSamplesB;\n\n        startSrcA    = d_LimitsA[blockIdx.x];\n        startSrcB    = d_LimitsB[blockIdx.x];\n        uint endSrcA = (intervalI + 1 < segmentSamples) ? d_LimitsA[blockIdx.x + 1] : segmentElementsA;\n        uint endSrcB = (intervalI + 1 < segmentSamples) ? d_LimitsB[blockIdx.x + 1] : segmentElementsB;\n        lenSrcA      = endSrcA - startSrcA;\n        lenSrcB      = endSrcB - startSrcB;\n        startDstA    = startSrcA + startSrcB;\n        startDstB    = startDstA + lenSrcA;\n    }\n\n    // Load main input data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        s_key[threadIdx.x + 0] = d_SrcKey[0 + startSrcA + threadIdx.x];\n        s_val[threadIdx.x + 0] = d_SrcVal[0 + startSrcA + threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        s_key[threadIdx.x + SAMPLE_STRIDE] = d_SrcKey[stride + startSrcB + threadIdx.x];\n        s_val[threadIdx.x + SAMPLE_STRIDE] = d_SrcVal[stride + startSrcB + threadIdx.x];\n    }\n\n    // Merge data in shared memory\n    cg::sync(cta);\n    merge<sortDir>(s_key,\n                   s_val,\n                   s_key + 0,\n                   s_val + 0,\n                   s_key + SAMPLE_STRIDE,\n                   s_val + SAMPLE_STRIDE,\n                   lenSrcA,\n                   SAMPLE_STRIDE,\n                   lenSrcB,\n                   SAMPLE_STRIDE,\n                   cta);\n\n    // Store merged data\n    cg::sync(cta);\n\n    if (threadIdx.x < lenSrcA) {\n        d_DstKey[startDstA + threadIdx.x] = s_key[threadIdx.x];\n        d_DstVal[startDstA + threadIdx.x] = s_val[threadIdx.x];\n    }\n\n    if (threadIdx.x < lenSrcB) {\n        d_DstKey[startDstB + threadIdx.x] = s_key[lenSrcA + threadIdx.x];\n        d_DstVal[startDstB + threadIdx.x] = s_val[lenSrcA + threadIdx.x];\n    }\n}\n\nstatic void mergeElementaryIntervals(uint *d_DstKey,\n                                     uint *d_DstVal,\n                                     uint *d_SrcKey,\n                                     uint *d_SrcVal,\n                                     uint *d_LimitsA,\n                                     uint *d_LimitsB,\n                                     uint  stride,\n                                     uint  N,\n                                     uint  sortDir)\n{\n    uint lastSegmentElements = N % (2 * stride);\n    uint mergePairs = (lastSegmentElements > stride) ? getSampleCount(N) : (N - lastSegmentElements) / SAMPLE_STRIDE;\n\n    if (sortDir) {\n        mergeElementaryIntervalsKernel<1U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<1> failed\\n\");\n    }\n    else {\n        mergeElementaryIntervalsKernel<0U>\n            <<<mergePairs, SAMPLE_STRIDE>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, d_LimitsA, d_LimitsB, stride, N);\n        getLastCudaError(\"mergeElementaryIntervalsKernel<0> failed\\n\");\n    }\n}\n\nextern \"C\" void bitonicSortShared(uint *d_DstKey,\n                                  uint *d_DstVal,\n                                  uint *d_SrcKey,\n                                  uint *d_SrcVal,\n                                  uint  batchSize,\n                                  uint  arrayLength,\n                                  uint  sortDir);\n\nextern \"C\" void bitonicMergeElementaryIntervals(uint *d_DstKey,\n                                                uint *d_DstVal,\n                                                uint *d_SrcKey,\n                                                uint *d_SrcVal,\n                                                uint *d_LimitsA,\n                                                uint *d_LimitsB,\n                                                uint  stride,\n                                                uint  N,\n                                                uint  sortDir);\n\nstatic uint      *d_RanksA, *d_RanksB, *d_LimitsA, *d_LimitsB;\nstatic const uint MAX_SAMPLE_COUNT = 32768;\n\nextern \"C\" void initMergeSort(void)\n{\n    checkCudaErrors(hipMalloc((void **)&d_RanksA, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(hipMalloc((void **)&d_RanksB, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(hipMalloc((void **)&d_LimitsA, MAX_SAMPLE_COUNT * sizeof(uint)));\n    checkCudaErrors(hipMalloc((void **)&d_LimitsB, MAX_SAMPLE_COUNT * sizeof(uint)));\n}\n\nextern \"C\" void closeMergeSort(void)\n{\n    checkCudaErrors(hipFree(d_RanksA));\n    checkCudaErrors(hipFree(d_RanksB));\n    checkCudaErrors(hipFree(d_LimitsB));\n    checkCudaErrors(hipFree(d_LimitsA));\n}\n\nextern \"C\" void mergeSort(uint *d_DstKey,\n                          uint *d_DstVal,\n                          uint *d_BufKey,\n                          uint *d_BufVal,\n                          uint *d_SrcKey,\n                          uint *d_SrcVal,\n                          uint  N,\n                          uint  sortDir)\n{\n    uint stageCount = 0;\n\n    for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1, stageCount++)\n        ;\n\n    uint *ikey, *ival, *okey, *oval;\n\n    if (stageCount & 1) {\n        ikey = d_BufKey;\n        ival = d_BufVal;\n        okey = d_DstKey;\n        oval = d_DstVal;\n    }\n    else {\n        ikey = d_DstKey;\n        ival = d_DstVal;\n        okey = d_BufKey;\n        oval = d_BufVal;\n    }\n\n    assert(N <= (SAMPLE_STRIDE * MAX_SAMPLE_COUNT));\n    assert(N % SHARED_SIZE_LIMIT == 0);\n    mergeSortShared(ikey, ival, d_SrcKey, d_SrcVal, N / SHARED_SIZE_LIMIT, SHARED_SIZE_LIMIT, sortDir);\n\n    for (uint stride = SHARED_SIZE_LIMIT; stride < N; stride <<= 1) {\n        uint lastSegmentElements = N % (2 * stride);\n\n        // Find sample ranks and prepare for limiters merge\n        generateSampleRanks(d_RanksA, d_RanksB, ikey, stride, N, sortDir);\n\n        // Merge ranks and indices\n        mergeRanksAndIndices(d_LimitsA, d_LimitsB, d_RanksA, d_RanksB, stride, N);\n\n        // Merge elementary intervals\n        mergeElementaryIntervals(okey, oval, ikey, ival, d_LimitsA, d_LimitsB, stride, N, sortDir);\n\n        if (lastSegmentElements <= stride) {\n            // Last merge segment consists of a single array which just needs to be\n            // passed through\n            checkCudaErrors(hipMemcpy(okey + (N - lastSegmentElements),\n                                       ikey + (N - lastSegmentElements),\n                                       lastSegmentElements * sizeof(uint),\n                                       hipMemcpyDeviceToDevice));\n            checkCudaErrors(hipMemcpy(oval + (N - lastSegmentElements),\n                                       ival + (N - lastSegmentElements),\n                                       lastSegmentElements * sizeof(uint),\n                                       hipMemcpyDeviceToDevice));\n        }\n\n        uint *t;\n        t    = ikey;\n        ikey = okey;\n        okey = t;\n        t    = ival;\n        ival = oval;\n        oval = t;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/mergeSort/mergeSort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates the usage of CUDA streams for overlapping\n * kernel execution with device/host memcopies.  The kernel is used to\n * initialize an array to a specific value, after which the array is\n * copied to the host (CPU) memory.  To increase performance, multiple\n * kernel/memcopy pairs are launched asynchronously, each pair in its\n * own stream.  Devices with Compute Capability 1.1 can overlap a kernel\n * and a memcopy as long as they are issued in different streams.  Kernels\n * are serialized.  Thus, if n pairs are launched, streamed approach\n * can reduce the memcopy cost to the (1/n)th of a single copy of the entire\n * data set.\n *\n * Additionally, this sample uses CUDA events to measure elapsed time for\n * CUDA calls.  Events are a part of CUDA API and provide a system independent\n * way to measure execution times on CUDA devices with approximately 0.5\n * microsecond precision.\n *\n * Elapsed times are averaged over nreps repetitions (10 by default).\n *\n */\n\nconst char *sSDKsample = \"simpleStreams\";\n\nconst char *sEventSyncMethod[] = {\"cudaEventDefault\", \"cudaEventBlockingSync\", \"cudaEventDisableTiming\", NULL};\n\nconst char *sDeviceSyncMethod[] = {\"cudaDeviceScheduleAuto\",\n                                   \"cudaDeviceScheduleSpin\",\n                                   \"cudaDeviceScheduleYield\",\n                                   \"INVALID\",\n                                   \"cudaDeviceScheduleBlockingSync\",\n                                   NULL};\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#ifndef WIN32\n#include <sys/mman.h> // for mmap() / munmap()\n#endif\n\n// Macro to aligned up to the memory size in question\n#define MEMORY_ALIGNMENT  4096\n#define ALIGN_UP(x, size) (((size_t)x + (size - 1)) & (~(size - 1)))\n\n__global__ void init_array(int *g_data, int *factor, int num_iterations)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = 0; i < num_iterations; i++) {\n        g_data[idx] += *factor; // non-coalesced on purpose, to burn time\n    }\n}\n\nbool correct_data(int *a, const int n, const int c)\n{\n    for (int i = 0; i < n; i++) {\n        if (a[i] != c) {\n            printf(\"%d: %d %d\\n\", i, a[i], c);\n            return false;\n        }\n    }\n\n    return true;\n}\n\ninline void AllocateHostMemory(bool bPinGenericMemory, int **pp_a, int **ppAligned_a, int nbytes)\n{\n#if CUDART_VERSION >= 4000\n#if !defined(__arm__) && !defined(__aarch64__)\n    if (bPinGenericMemory) {\n// allocate a generic page-aligned chunk of system memory\n#ifdef WIN32\n        printf(\"> VirtualAlloc() allocating %4.2f Mbytes of (generic page-aligned \"\n               \"system memory)\\n\",\n               (float)nbytes / 1048576.0f);\n        *pp_a = (int *)VirtualAlloc(NULL, (nbytes + MEMORY_ALIGNMENT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\n#else\n        printf(\"> mmap() allocating %4.2f Mbytes (generic page-aligned system \"\n               \"memory)\\n\",\n               (float)nbytes / 1048576.0f);\n        *pp_a = (int *)mmap(NULL, (nbytes + MEMORY_ALIGNMENT), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);\n#endif\n\n        *ppAligned_a = (int *)ALIGN_UP(*pp_a, MEMORY_ALIGNMENT);\n\n        printf(\"> cudaHostRegister() registering %4.2f Mbytes of generic allocated \"\n               \"system memory\\n\",\n               (float)nbytes / 1048576.0f);\n        // pin allocate memory\n        checkCudaErrors(cudaHostRegister(*ppAligned_a, nbytes, cudaHostRegisterMapped));\n    }\n    else\n#endif\n#endif\n    {\n        printf(\"> cudaMallocHost() allocating %4.2f Mbytes of system memory\\n\", (float)nbytes / 1048576.0f);\n        // allocate host memory (pinned is required for achieve asynchronicity)\n        checkCudaErrors(cudaMallocHost((void **)pp_a, nbytes));\n        *ppAligned_a = *pp_a;\n    }\n}\n\ninline void FreeHostMemory(bool bPinGenericMemory, int **pp_a, int **ppAligned_a, int nbytes)\n{\n#if CUDART_VERSION >= 4000\n#if !defined(__arm__) && !defined(__aarch64__)\n    // CUDA 4.0 support pinning of generic host memory\n    if (bPinGenericMemory) {\n        // unpin and delete host memory\n        checkCudaErrors(cudaHostUnregister(*ppAligned_a));\n#ifdef WIN32\n        VirtualFree(*pp_a, 0, MEM_RELEASE);\n#else\n        munmap(*pp_a, nbytes);\n#endif\n    }\n    else\n#endif\n#endif\n    {\n        cudaFreeHost(*pp_a);\n    }\n}\n\nstatic const char *sSyncMethod[] = {\"0 (Automatic Blocking)\",\n                                    \"1 (Spin Blocking)\",\n                                    \"2 (Yield Blocking)\",\n                                    \"3 (Undefined Blocking Method)\",\n                                    \"4 (Blocking Sync Event) = low CPU utilization\",\n                                    NULL};\n\nvoid printHelp()\n{\n    printf(\"Usage: %s [options below]\\n\", sSDKsample);\n    printf(\"\\t--sync_method=n for CPU/GPU synchronization\\n\");\n    printf(\"\\t             n=%s\\n\", sSyncMethod[0]);\n    printf(\"\\t             n=%s\\n\", sSyncMethod[1]);\n    printf(\"\\t             n=%s\\n\", sSyncMethod[2]);\n    printf(\"\\t   <Default> n=%s\\n\", sSyncMethod[4]);\n    printf(\"\\t--use_generic_memory (default) use generic page-aligned for system \"\n           \"memory\\n\");\n    printf(\"\\t--use_cuda_malloc_host (optional) use cudaMallocHost to allocate \"\n           \"system memory\\n\");\n}\n\n#if defined(__APPLE__) || defined(MACOSX)\n#define DEFAULT_PINNED_GENERIC_MEMORY false\n#else\n#define DEFAULT_PINNED_GENERIC_MEMORY true\n#endif\n\nint main(int argc, char **argv)\n{\n    int   cuda_device = 0;\n    int   nstreams    = 4;                        // number of streams for CUDA calls\n    int   nreps       = 10;                       // number of times each experiment is repeated\n    int   n           = 16 * 1024 * 1024;         // number of ints in the data set\n    int   nbytes      = n * sizeof(int);          // number of data bytes\n    dim3  threads, blocks;                        // kernel launch configuration\n    float elapsed_time, time_memcpy, time_kernel; // timing variables\n    float scale_factor = 1.0f;\n\n    // allocate generic memory and pin it laster instead of using cudaHostAlloc()\n\n    bool bPinGenericMemory  = DEFAULT_PINNED_GENERIC_MEMORY; // we want this to be the default behavior\n    int  device_sync_method = cudaDeviceBlockingSync;        // by default we use BlockingSync\n\n    int niterations; // number of iterations for the loop inside the kernel\n\n    printf(\"[ %s ]\\n\\n\", sSDKsample);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printHelp();\n        return EXIT_SUCCESS;\n    }\n\n    if ((device_sync_method = getCmdLineArgumentInt(argc, (const char **)argv, \"sync_method\")) >= 0) {\n        if (device_sync_method == 0 || device_sync_method == 1 || device_sync_method == 2 || device_sync_method == 4) {\n            printf(\"Device synchronization method set to = %s\\n\", sSyncMethod[device_sync_method]);\n            printf(\"Setting reps to 100 to demonstrate steady state\\n\");\n            nreps = 100;\n        }\n        else {\n            printf(\"Invalid command line option sync_method=\\\"%d\\\"\\n\", device_sync_method);\n            return EXIT_FAILURE;\n        }\n    }\n    else {\n        printHelp();\n        return EXIT_SUCCESS;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_generic_memory\")) {\n#if defined(__APPLE__) || defined(MACOSX)\n        bPinGenericMemory = false; // Generic Pinning of System Paged memory not\n                                   // currently supported on Mac OSX\n#else\n        bPinGenericMemory = true;\n#endif\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_cuda_malloc_host\")) {\n        bPinGenericMemory = false;\n    }\n\n    printf(\"\\n> \");\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    // check the compute capability of the device\n    int num_devices = 0;\n    checkCudaErrors(cudaGetDeviceCount(&num_devices));\n\n    if (0 == num_devices) {\n        printf(\"your system does not have a CUDA capable device, waiving test...\\n\");\n        return EXIT_WAIVED;\n    }\n\n    // check if the command-line chosen device ID is within range, exit if not\n    if (cuda_device >= num_devices) {\n        printf(\"cuda_device=%d is invalid, must choose device ID between 0 and %d\\n\", cuda_device, num_devices - 1);\n        return EXIT_FAILURE;\n    }\n\n    checkCudaErrors(cudaSetDevice(cuda_device));\n\n    // Checking for compute capabilities\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n\n    niterations = 5;\n\n    // Check if GPU can map host memory (Generic Method), if not then we override\n    // bPinGenericMemory to be false\n    if (bPinGenericMemory) {\n        printf(\"Device: <%s> canMapHostMemory: %s\\n\", deviceProp.name, deviceProp.canMapHostMemory ? \"Yes\" : \"No\");\n\n        if (deviceProp.canMapHostMemory == 0) {\n            printf(\"Using cudaMallocHost, CUDA device does not support mapping of \"\n                   \"generic host memory\\n\");\n            bPinGenericMemory = false;\n        }\n    }\n\n    // Anything that is less than 32 Cores will have scaled down workload\n    scale_factor =\n        max((32.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n            1.0f);\n    n = (int)rint((float)n / scale_factor);\n\n    printf(\"> CUDA Capable: SM %d.%d hardware\\n\", deviceProp.major, deviceProp.minor);\n    printf(\"> %d Multiprocessor(s) x %d (Cores/Multiprocessor) = %d (Cores)\\n\",\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    printf(\"> scale_factor = %1.4f\\n\", 1.0f / scale_factor);\n    printf(\"> array_size   = %d\\n\\n\", n);\n\n    // enable use of blocking sync, to reduce CPU usage\n    printf(\"> Using CPU/GPU Device Synchronization method (%s)\\n\", sDeviceSyncMethod[device_sync_method]);\n    checkCudaErrors(cudaSetDeviceFlags(device_sync_method | (bPinGenericMemory ? cudaDeviceMapHost : 0)));\n\n    // allocate host memory\n    int  c          = 5; // value to which the array will be initialized\n    int *h_a        = 0; // pointer to the array data in host memory\n    int *hAligned_a = 0; // pointer to the array data in host memory (aligned to\n                         // MEMORY_ALIGNMENT)\n\n    // Allocate Host memory (could be using cudaMallocHost or VirtualAlloc/mmap if\n    // using the new CUDA 4.0 features\n    AllocateHostMemory(bPinGenericMemory, &h_a, &hAligned_a, nbytes);\n\n    // allocate device memory\n    int *d_a = 0,\n        *d_c = 0; // pointers to data and init value in the device memory\n    checkCudaErrors(cudaMalloc((void **)&d_a, nbytes));\n    checkCudaErrors(cudaMemset(d_a, 0x0, nbytes));\n    checkCudaErrors(cudaMalloc((void **)&d_c, sizeof(int)));\n    checkCudaErrors(cudaMemcpy(d_c, &c, sizeof(int), cudaMemcpyHostToDevice));\n\n    printf(\"\\nStarting Test\\n\");\n\n    // allocate and initialize an array of stream handles\n    cudaStream_t *streams = (cudaStream_t *)malloc(nstreams * sizeof(cudaStream_t));\n\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(cudaStreamCreate(&(streams[i])));\n    }\n\n    // create CUDA event handles\n    // use blocking sync\n    cudaEvent_t start_event, stop_event;\n    int eventflags = ((device_sync_method == cudaDeviceBlockingSync) ? cudaEventBlockingSync : cudaEventDefault);\n\n    checkCudaErrors(cudaEventCreateWithFlags(&start_event, eventflags));\n    checkCudaErrors(cudaEventCreateWithFlags(&stop_event, eventflags));\n\n    // time memcopy from device\n    checkCudaErrors(cudaEventRecord(start_event, 0)); // record in stream-0, to\n                                                      // ensure that all previous\n                                                      // CUDA calls have\n                                                      // completed\n    checkCudaErrors(cudaMemcpyAsync(hAligned_a, d_a, nbytes, cudaMemcpyDeviceToHost, streams[0]));\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n    checkCudaErrors(cudaEventSynchronize(stop_event)); // block until the event is actually recorded\n    checkCudaErrors(cudaEventElapsedTime(&time_memcpy, start_event, stop_event));\n    printf(\"memcopy:\\t%.2f\\n\", time_memcpy);\n\n    // time kernel\n    threads = dim3(512, 1);\n    blocks  = dim3(n / threads.x, 1);\n    checkCudaErrors(cudaEventRecord(start_event, 0));\n    init_array<<<blocks, threads, 0, streams[0]>>>(d_a, d_c, niterations);\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n    checkCudaErrors(cudaEventSynchronize(stop_event));\n    checkCudaErrors(cudaEventElapsedTime(&time_kernel, start_event, stop_event));\n    printf(\"kernel:\\t\\t%.2f\\n\", time_kernel);\n\n    //////////////////////////////////////////////////////////////////////\n    // time non-streamed execution for reference\n    threads = dim3(512, 1);\n    blocks  = dim3(n / threads.x, 1);\n    checkCudaErrors(cudaEventRecord(start_event, 0));\n\n    for (int k = 0; k < nreps; k++) {\n        init_array<<<blocks, threads>>>(d_a, d_c, niterations);\n        checkCudaErrors(cudaMemcpy(hAligned_a, d_a, nbytes, cudaMemcpyDeviceToHost));\n    }\n\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n    checkCudaErrors(cudaEventSynchronize(stop_event));\n    checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start_event, stop_event));\n    printf(\"non-streamed:\\t%.2f\\n\", elapsed_time / nreps);\n\n    //////////////////////////////////////////////////////////////////////\n    // time execution with nstreams streams\n    threads = dim3(512, 1);\n    blocks  = dim3(n / (nstreams * threads.x), 1);\n    memset(hAligned_a, 255,\n           nbytes);                              // set host memory bits to all 1s, for testing correctness\n    checkCudaErrors(cudaMemset(d_a, 0, nbytes)); // set device memory to all 0s, for testing correctness\n    checkCudaErrors(cudaEventRecord(start_event, 0));\n\n    for (int k = 0; k < nreps; k++) {\n        // asynchronously launch nstreams kernels, each operating on its own portion\n        // of data\n        for (int i = 0; i < nstreams; i++) {\n            init_array<<<blocks, threads, 0, streams[i]>>>(d_a + i * n / nstreams, d_c, niterations);\n        }\n\n        // asynchronously launch nstreams memcopies.  Note that memcopy in stream x\n        // will only\n        //   commence executing when all previous CUDA calls in stream x have\n        //   completed\n        for (int i = 0; i < nstreams; i++) {\n            checkCudaErrors(cudaMemcpyAsync(hAligned_a + i * n / nstreams,\n                                            d_a + i * n / nstreams,\n                                            nbytes / nstreams,\n                                            cudaMemcpyDeviceToHost,\n                                            streams[i]));\n        }\n    }\n\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n    checkCudaErrors(cudaEventSynchronize(stop_event));\n    checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start_event, stop_event));\n    printf(\"%d streams:\\t%.2f\\n\", nstreams, elapsed_time / nreps);\n\n    // check whether the output is correct\n    printf(\"-------------------------------\\n\");\n    bool bResults = correct_data(hAligned_a, n, c * nreps * niterations);\n\n    // release resources\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(cudaStreamDestroy(streams[i]));\n    }\n\n    checkCudaErrors(cudaEventDestroy(start_event));\n    checkCudaErrors(cudaEventDestroy(stop_event));\n\n    // Free cudaMallocHost or Generic Host allocated memory (from CUDA 4.0)\n    FreeHostMemory(bPinGenericMemory, &h_a, &hAligned_a, nbytes);\n\n    checkCudaErrors(cudaFree(d_a));\n    checkCudaErrors(cudaFree(d_c));\n\n    return bResults ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates the usage of CUDA streams for overlapping\n * kernel execution with device/host memcopies.  The kernel is used to\n * initialize an array to a specific value, after which the array is\n * copied to the host (CPU) memory.  To increase performance, multiple\n * kernel/memcopy pairs are launched asynchronously, each pair in its\n * own stream.  Devices with Compute Capability 1.1 can overlap a kernel\n * and a memcopy as long as they are issued in different streams.  Kernels\n * are serialized.  Thus, if n pairs are launched, streamed approach\n * can reduce the memcopy cost to the (1/n)th of a single copy of the entire\n * data set.\n *\n * Additionally, this sample uses CUDA events to measure elapsed time for\n * CUDA calls.  Events are a part of CUDA API and provide a system independent\n * way to measure execution times on CUDA devices with approximately 0.5\n * microsecond precision.\n *\n * Elapsed times are averaged over nreps repetitions (10 by default).\n *\n */\n\nconst char *sSDKsample = \"simpleStreams\";\n\nconst char *sEventSyncMethod[] = {\"hipEventDefault\", \"hipEventBlockingSync\", \"hipEventDisableTiming\", NULL};\n\nconst char *sDeviceSyncMethod[] = {\"hipDeviceScheduleAuto\",\n                                   \"hipDeviceScheduleSpin\",\n                                   \"hipDeviceScheduleYield\",\n                                   \"INVALID\",\n                                   \"hipDeviceScheduleBlockingSync\",\n                                   NULL};\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#ifndef WIN32\n#include <sys/mman.h> // for mmap() / munmap()\n#endif\n\n// Macro to aligned up to the memory size in question\n#define MEMORY_ALIGNMENT  4096\n#define ALIGN_UP(x, size) (((size_t)x + (size - 1)) & (~(size - 1)))\n\n__global__ void init_array(int *g_data, int *factor, int num_iterations)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = 0; i < num_iterations; i++) {\n        g_data[idx] += *factor; // non-coalesced on purpose, to burn time\n    }\n}\n\nbool correct_data(int *a, const int n, const int c)\n{\n    for (int i = 0; i < n; i++) {\n        if (a[i] != c) {\n            printf(\"%d: %d %d\\n\", i, a[i], c);\n            return false;\n        }\n    }\n\n    return true;\n}\n\ninline void AllocateHostMemory(bool bPinGenericMemory, int **pp_a, int **ppAligned_a, int nbytes)\n{\n#if CUDART_VERSION >= 4000\n#if !defined(__arm__) && !defined(__aarch64__)\n    if (bPinGenericMemory) {\n// allocate a generic page-aligned chunk of system memory\n#ifdef WIN32\n        printf(\"> VirtualAlloc() allocating %4.2f Mbytes of (generic page-aligned \"\n               \"system memory)\\n\",\n               (float)nbytes / 1048576.0f);\n        *pp_a = (int *)VirtualAlloc(NULL, (nbytes + MEMORY_ALIGNMENT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\n#else\n        printf(\"> mmap() allocating %4.2f Mbytes (generic page-aligned system \"\n               \"memory)\\n\",\n               (float)nbytes / 1048576.0f);\n        *pp_a = (int *)mmap(NULL, (nbytes + MEMORY_ALIGNMENT), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);\n#endif\n\n        *ppAligned_a = (int *)ALIGN_UP(*pp_a, MEMORY_ALIGNMENT);\n\n        printf(\"> hipHostRegister() registering %4.2f Mbytes of generic allocated \"\n               \"system memory\\n\",\n               (float)nbytes / 1048576.0f);\n        // pin allocate memory\n        checkCudaErrors(hipHostRegister(*ppAligned_a, nbytes, hipHostRegisterMapped));\n    }\n    else\n#endif\n#endif\n    {\n        printf(\"> hipHostMalloc() allocating %4.2f Mbytes of system memory\\n\", (float)nbytes / 1048576.0f);\n        // allocate host memory (pinned is required for achieve asynchronicity)\n        checkCudaErrors(hipHostMalloc((void **)pp_a, nbytes));\n        *ppAligned_a = *pp_a;\n    }\n}\n\ninline void FreeHostMemory(bool bPinGenericMemory, int **pp_a, int **ppAligned_a, int nbytes)\n{\n#if CUDART_VERSION >= 4000\n#if !defined(__arm__) && !defined(__aarch64__)\n    // CUDA 4.0 support pinning of generic host memory\n    if (bPinGenericMemory) {\n        // unpin and delete host memory\n        checkCudaErrors(hipHostUnregister(*ppAligned_a));\n#ifdef WIN32\n        VirtualFree(*pp_a, 0, MEM_RELEASE);\n#else\n        munmap(*pp_a, nbytes);\n#endif\n    }\n    else\n#endif\n#endif\n    {\n        hipHostFree(*pp_a);\n    }\n}\n\nstatic const char *sSyncMethod[] = {\"0 (Automatic Blocking)\",\n                                    \"1 (Spin Blocking)\",\n                                    \"2 (Yield Blocking)\",\n                                    \"3 (Undefined Blocking Method)\",\n                                    \"4 (Blocking Sync Event) = low CPU utilization\",\n                                    NULL};\n\nvoid printHelp()\n{\n    printf(\"Usage: %s [options below]\\n\", sSDKsample);\n    printf(\"\\t--sync_method=n for CPU/GPU synchronization\\n\");\n    printf(\"\\t             n=%s\\n\", sSyncMethod[0]);\n    printf(\"\\t             n=%s\\n\", sSyncMethod[1]);\n    printf(\"\\t             n=%s\\n\", sSyncMethod[2]);\n    printf(\"\\t   <Default> n=%s\\n\", sSyncMethod[4]);\n    printf(\"\\t--use_generic_memory (default) use generic page-aligned for system \"\n           \"memory\\n\");\n    printf(\"\\t--use_cuda_malloc_host (optional) use hipHostMalloc to allocate \"\n           \"system memory\\n\");\n}\n\n#if defined(__APPLE__) || defined(MACOSX)\n#define DEFAULT_PINNED_GENERIC_MEMORY false\n#else\n#define DEFAULT_PINNED_GENERIC_MEMORY true\n#endif\n\nint main(int argc, char **argv)\n{\n    int   cuda_device = 0;\n    int   nstreams    = 4;                        // number of streams for CUDA calls\n    int   nreps       = 10;                       // number of times each experiment is repeated\n    int   n           = 16 * 1024 * 1024;         // number of ints in the data set\n    int   nbytes      = n * sizeof(int);          // number of data bytes\n    dim3  threads, blocks;                        // kernel launch configuration\n    float elapsed_time, time_memcpy, time_kernel; // timing variables\n    float scale_factor = 1.0f;\n\n    // allocate generic memory and pin it laster instead of using cudaHostAlloc()\n\n    bool bPinGenericMemory  = DEFAULT_PINNED_GENERIC_MEMORY; // we want this to be the default behavior\n    int  device_sync_method = hipDeviceScheduleBlockingSync;        // by default we use BlockingSync\n\n    int niterations; // number of iterations for the loop inside the kernel\n\n    printf(\"[ %s ]\\n\\n\", sSDKsample);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printHelp();\n        return EXIT_SUCCESS;\n    }\n\n    if ((device_sync_method = getCmdLineArgumentInt(argc, (const char **)argv, \"sync_method\")) >= 0) {\n        if (device_sync_method == 0 || device_sync_method == 1 || device_sync_method == 2 || device_sync_method == 4) {\n            printf(\"Device synchronization method set to = %s\\n\", sSyncMethod[device_sync_method]);\n            printf(\"Setting reps to 100 to demonstrate steady state\\n\");\n            nreps = 100;\n        }\n        else {\n            printf(\"Invalid command line option sync_method=\\\"%d\\\"\\n\", device_sync_method);\n            return EXIT_FAILURE;\n        }\n    }\n    else {\n        printHelp();\n        return EXIT_SUCCESS;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_generic_memory\")) {\n#if defined(__APPLE__) || defined(MACOSX)\n        bPinGenericMemory = false; // Generic Pinning of System Paged memory not\n                                   // currently supported on Mac OSX\n#else\n        bPinGenericMemory = true;\n#endif\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"use_cuda_malloc_host\")) {\n        bPinGenericMemory = false;\n    }\n\n    printf(\"\\n> \");\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    // check the compute capability of the device\n    int num_devices = 0;\n    checkCudaErrors(hipGetDeviceCount(&num_devices));\n\n    if (0 == num_devices) {\n        printf(\"your system does not have a CUDA capable device, waiving test...\\n\");\n        return EXIT_WAIVED;\n    }\n\n    // check if the command-line chosen device ID is within range, exit if not\n    if (cuda_device >= num_devices) {\n        printf(\"cuda_device=%d is invalid, must choose device ID between 0 and %d\\n\", cuda_device, num_devices - 1);\n        return EXIT_FAILURE;\n    }\n\n    checkCudaErrors(hipSetDevice(cuda_device));\n\n    // Checking for compute capabilities\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n\n    niterations = 5;\n\n    // Check if GPU can map host memory (Generic Method), if not then we override\n    // bPinGenericMemory to be false\n    if (bPinGenericMemory) {\n        printf(\"Device: <%s> canMapHostMemory: %s\\n\", deviceProp.name, deviceProp.canMapHostMemory ? \"Yes\" : \"No\");\n\n        if (deviceProp.canMapHostMemory == 0) {\n            printf(\"Using hipHostMalloc, CUDA device does not support mapping of \"\n                   \"generic host memory\\n\");\n            bPinGenericMemory = false;\n        }\n    }\n\n    // Anything that is less than 32 Cores will have scaled down workload\n    scale_factor =\n        max((32.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n            1.0f);\n    n = (int)rint((float)n / scale_factor);\n\n    printf(\"> CUDA Capable: SM %d.%d hardware\\n\", deviceProp.major, deviceProp.minor);\n    printf(\"> %d Multiprocessor(s) x %d (Cores/Multiprocessor) = %d (Cores)\\n\",\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    printf(\"> scale_factor = %1.4f\\n\", 1.0f / scale_factor);\n    printf(\"> array_size   = %d\\n\\n\", n);\n\n    // enable use of blocking sync, to reduce CPU usage\n    printf(\"> Using CPU/GPU Device Synchronization method (%s)\\n\", sDeviceSyncMethod[device_sync_method]);\n    checkCudaErrors(hipSetDeviceFlags(device_sync_method | (bPinGenericMemory ? hipDeviceMapHost : 0)));\n\n    // allocate host memory\n    int  c          = 5; // value to which the array will be initialized\n    int *h_a        = 0; // pointer to the array data in host memory\n    int *hAligned_a = 0; // pointer to the array data in host memory (aligned to\n                         // MEMORY_ALIGNMENT)\n\n    // Allocate Host memory (could be using cudaMallocHost or VirtualAlloc/mmap if\n    // using the new CUDA 4.0 features\n    AllocateHostMemory(bPinGenericMemory, &h_a, &hAligned_a, nbytes);\n\n    // allocate device memory\n    int *d_a = 0,\n        *d_c = 0; // pointers to data and init value in the device memory\n    checkCudaErrors(hipMalloc((void **)&d_a, nbytes));\n    checkCudaErrors(hipMemset(d_a, 0x0, nbytes));\n    checkCudaErrors(hipMalloc((void **)&d_c, sizeof(int)));\n    checkCudaErrors(hipMemcpy(d_c, &c, sizeof(int), hipMemcpyHostToDevice));\n\n    printf(\"\\nStarting Test\\n\");\n\n    // allocate and initialize an array of stream handles\n    hipStream_t *streams = (hipStream_t *)malloc(nstreams * sizeof(hipStream_t));\n\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(hipStreamCreate(&(streams[i])));\n    }\n\n    // create CUDA event handles\n    // use blocking sync\n    hipEvent_t start_event, stop_event;\n    int eventflags = ((device_sync_method == hipDeviceScheduleBlockingSync) ? hipEventBlockingSync : hipEventDefault);\n\n    checkCudaErrors(hipEventCreateWithFlags(&start_event, eventflags));\n    checkCudaErrors(hipEventCreateWithFlags(&stop_event, eventflags));\n\n    // time memcopy from device\n    checkCudaErrors(hipEventRecord(start_event, 0)); // record in stream-0, to\n                                                      // ensure that all previous\n                                                      // CUDA calls have\n                                                      // completed\n    checkCudaErrors(hipMemcpyAsync(hAligned_a, d_a, nbytes, hipMemcpyDeviceToHost, streams[0]));\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n    checkCudaErrors(hipEventSynchronize(stop_event)); // block until the event is actually recorded\n    checkCudaErrors(hipEventElapsedTime(&time_memcpy, start_event, stop_event));\n    printf(\"memcopy:\\t%.2f\\n\", time_memcpy);\n\n    // time kernel\n    threads = dim3(512, 1);\n    blocks  = dim3(n / threads.x, 1);\n    checkCudaErrors(hipEventRecord(start_event, 0));\n    init_array<<<blocks, threads, 0, streams[0]>>>(d_a, d_c, niterations);\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n    checkCudaErrors(hipEventSynchronize(stop_event));\n    checkCudaErrors(hipEventElapsedTime(&time_kernel, start_event, stop_event));\n    printf(\"kernel:\\t\\t%.2f\\n\", time_kernel);\n\n    //////////////////////////////////////////////////////////////////////\n    // time non-streamed execution for reference\n    threads = dim3(512, 1);\n    blocks  = dim3(n / threads.x, 1);\n    checkCudaErrors(hipEventRecord(start_event, 0));\n\n    for (int k = 0; k < nreps; k++) {\n        init_array<<<blocks, threads>>>(d_a, d_c, niterations);\n        checkCudaErrors(hipMemcpy(hAligned_a, d_a, nbytes, hipMemcpyDeviceToHost));\n    }\n\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n    checkCudaErrors(hipEventSynchronize(stop_event));\n    checkCudaErrors(hipEventElapsedTime(&elapsed_time, start_event, stop_event));\n    printf(\"non-streamed:\\t%.2f\\n\", elapsed_time / nreps);\n\n    //////////////////////////////////////////////////////////////////////\n    // time execution with nstreams streams\n    threads = dim3(512, 1);\n    blocks  = dim3(n / (nstreams * threads.x), 1);\n    memset(hAligned_a, 255,\n           nbytes);                              // set host memory bits to all 1s, for testing correctness\n    checkCudaErrors(hipMemset(d_a, 0, nbytes)); // set device memory to all 0s, for testing correctness\n    checkCudaErrors(hipEventRecord(start_event, 0));\n\n    for (int k = 0; k < nreps; k++) {\n        // asynchronously launch nstreams kernels, each operating on its own portion\n        // of data\n        for (int i = 0; i < nstreams; i++) {\n            init_array<<<blocks, threads, 0, streams[i]>>>(d_a + i * n / nstreams, d_c, niterations);\n        }\n\n        // asynchronously launch nstreams memcopies.  Note that memcopy in stream x\n        // will only\n        //   commence executing when all previous CUDA calls in stream x have\n        //   completed\n        for (int i = 0; i < nstreams; i++) {\n            checkCudaErrors(hipMemcpyAsync(hAligned_a + i * n / nstreams,\n                                            d_a + i * n / nstreams,\n                                            nbytes / nstreams,\n                                            hipMemcpyDeviceToHost,\n                                            streams[i]));\n        }\n    }\n\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n    checkCudaErrors(hipEventSynchronize(stop_event));\n    checkCudaErrors(hipEventElapsedTime(&elapsed_time, start_event, stop_event));\n    printf(\"%d streams:\\t%.2f\\n\", nstreams, elapsed_time / nreps);\n\n    // check whether the output is correct\n    printf(\"-------------------------------\\n\");\n    bool bResults = correct_data(hAligned_a, n, c * nreps * niterations);\n\n    // release resources\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(hipStreamDestroy(streams[i]));\n    }\n\n    checkCudaErrors(hipEventDestroy(start_event));\n    checkCudaErrors(hipEventDestroy(stop_event));\n\n    // Free cudaMallocHost or Generic Host allocated memory (from CUDA 4.0)\n    FreeHostMemory(bPinGenericMemory, &h_a, &hAligned_a, nbytes);\n\n    checkCudaErrors(hipFree(d_a));\n    checkCudaErrors(hipFree(d_c));\n\n    return bResults ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleStreams/simpleStreams.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Quadro and Tesla GPUs with compute capability >= 2.0 can overlap two\n * memcopies with kernel execution. This sample illustrates the usage of CUDA\n * streams to achieve overlapping of kernel execution with copying data to and\n * from the device.\n *\n * Additionally, this sample uses CUDA events to measure elapsed time for\n * CUDA calls.  Events are a part of CUDA API and provide a system independent\n * way to measure execution times on CUDA devices with approximately 0.5\n * microsecond precision.\n *\n * Elapsed times are averaged over nreps repetitions (10 by default).\n *\n */\n\nconst char *sSDKname = \"simpleMultiCopy\";\n\n// includes, system\n#include <stdio.h>\n\n// include CUDA\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\n// includes, kernels\n// Declare the CUDA kernels here and main() code that is needed to launch\n// Compute workload on the system\n__global__ void incKernel(int *g_out, int *g_in, int N, int inner_reps)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        for (int i = 0; i < inner_reps; ++i) {\n            g_out[idx] = g_in[idx] + 1;\n        }\n    }\n}\n\n#define STREAM_COUNT 4\n\n// Uncomment to simulate data source/sink IO times\n// #define SIMULATE_IO\n\nint *h_data_source;\nint *h_data_sink;\n\nint *h_data_in[STREAM_COUNT];\nint *d_data_in[STREAM_COUNT];\n\nint *h_data_out[STREAM_COUNT];\nint *d_data_out[STREAM_COUNT];\n\ncudaEvent_t  cycleDone[STREAM_COUNT];\ncudaStream_t stream[STREAM_COUNT];\n\ncudaEvent_t start, stop;\n\nint N          = 1 << 22;\nint nreps      = 10; // number of times each experiment is repeated\nint inner_reps = 5;\n\nint memsize;\n\ndim3 block(512);\ndim3 grid;\n\nint thread_blocks;\n\nfloat processWithStreams(int streams_used);\nvoid  init();\nbool  test();\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char *argv[])\n{\n    int            cuda_device = 0;\n    float          scale_factor;\n    cudaDeviceProp deviceProp;\n\n    printf(\"[%s] - Starting...\\n\", sSDKname);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"device\")) {\n        cuda_device = getCmdLineArgumentInt(argc, (const char **)argv, \"device=\");\n\n        if (cuda_device < 0) {\n            printf(\"Invalid command line parameters\\n\");\n            exit(EXIT_FAILURE);\n        }\n        else {\n            printf(\"cuda_device = %d\\n\", cuda_device);\n            cuda_device = gpuDeviceInit(cuda_device);\n\n            if (cuda_device < 0) {\n                printf(\"No CUDA Capable devices found, exiting...\\n\");\n                exit(EXIT_SUCCESS);\n            }\n        }\n    }\n    else {\n        // Otherwise pick the device with the highest Gflops/s\n        cuda_device = gpuGetMaxGflopsDeviceId();\n        checkCudaErrors(cudaSetDevice(cuda_device));\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n        printf(\"> Using CUDA device [%d]: %s\\n\", cuda_device, deviceProp.name);\n    }\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n    printf(\"[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    // Anything that is less than 32 Cores will have scaled down workload\n    scale_factor =\n        max((32.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n            1.0f);\n    N = (int)((float)N / scale_factor);\n\n    printf(\"> Device name: %s\\n\", deviceProp.name);\n    printf(\"> CUDA Capability %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n    printf(\"> scale_factor = %.2f\\n\", 1.0f / scale_factor);\n    printf(\"> array_size   = %d\\n\\n\", N);\n\n    memsize = N * sizeof(int);\n\n    thread_blocks = N / block.x;\n\n    grid.x = thread_blocks % 65535;\n    grid.y = (thread_blocks / 65535 + 1);\n\n    // Allocate resources\n\n    h_data_source = (int *)malloc(memsize);\n    h_data_sink   = (int *)malloc(memsize);\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        checkCudaErrors(cudaHostAlloc(&h_data_in[i], memsize, cudaHostAllocDefault));\n        checkCudaErrors(cudaMalloc(&d_data_in[i], memsize));\n        checkCudaErrors(cudaMemset(d_data_in[i], 0, memsize));\n\n        checkCudaErrors(cudaHostAlloc(&h_data_out[i], memsize, cudaHostAllocDefault));\n        checkCudaErrors(cudaMalloc(&d_data_out[i], memsize));\n\n        checkCudaErrors(cudaStreamCreate(&stream[i]));\n        checkCudaErrors(cudaEventCreate(&cycleDone[i]));\n\n        cudaEventRecord(cycleDone[i], stream[i]);\n    }\n\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    init();\n\n    // Kernel warmup\n    incKernel<<<grid, block>>>(d_data_out[0], d_data_in[0], N, inner_reps);\n\n    // Time copies and kernel\n    cudaEventRecord(start, 0);\n    checkCudaErrors(cudaMemcpyAsync(d_data_in[0], h_data_in[0], memsize, cudaMemcpyHostToDevice, 0));\n    cudaEventRecord(stop, 0);\n    cudaEventSynchronize(stop);\n\n    float memcpy_h2d_time;\n    cudaEventElapsedTime(&memcpy_h2d_time, start, stop);\n\n    cudaEventRecord(start, 0);\n    checkCudaErrors(cudaMemcpyAsync(h_data_out[0], d_data_out[0], memsize, cudaMemcpyDeviceToHost, 0));\n    cudaEventRecord(stop, 0);\n    cudaEventSynchronize(stop);\n\n    float memcpy_d2h_time;\n    cudaEventElapsedTime(&memcpy_d2h_time, start, stop);\n\n    cudaEventRecord(start, 0);\n    incKernel<<<grid, block, 0, 0>>>(d_data_out[0], d_data_in[0], N, inner_reps);\n    cudaEventRecord(stop, 0);\n    cudaEventSynchronize(stop);\n\n    float kernel_time;\n    cudaEventElapsedTime(&kernel_time, start, stop);\n\n    printf(\"\\n\");\n    printf(\"Relevant properties of this CUDA device\\n\");\n    int canOverlap;\n    checkCudaErrors(cudaDeviceGetAttribute(&canOverlap, cudaDevAttrGpuOverlap, cuda_device));\n    printf(\"(%s) Can overlap one CPU<>GPU data transfer with GPU kernel execution \"\n           \"(device property \\\"cudaDevAttrGpuOverlap\\\")\\n\",\n           canOverlap ? \"X\" : \" \");\n    // printf(\"(%s) Can execute several GPU kernels simultaneously (compute\n    // capability >= 2.0)\\n\", deviceProp.major >= 2 ? \"X\": \" \");\n    printf(\"(%s) Can overlap two CPU<>GPU data transfers with GPU kernel execution\\n\"\n           \"    (Compute Capability >= 2.0 AND (Tesla product OR Quadro \"\n           \"4000/5000/6000/K5000)\\n\",\n           (deviceProp.major >= 2 && deviceProp.asyncEngineCount > 1) ? \"X\" : \" \");\n\n    printf(\"\\n\");\n    printf(\"Measured timings (throughput):\\n\");\n    printf(\" Memcpy host to device\\t: %f ms (%f GB/s)\\n\", memcpy_h2d_time, (memsize * 1e-6) / memcpy_h2d_time);\n    printf(\" Memcpy device to host\\t: %f ms (%f GB/s)\\n\", memcpy_d2h_time, (memsize * 1e-6) / memcpy_d2h_time);\n    printf(\" Kernel\\t\\t\\t: %f ms (%f GB/s)\\n\", kernel_time, (inner_reps * memsize * 2e-6) / kernel_time);\n\n    printf(\"\\n\");\n    printf(\"Theoretical limits for speedup gained from overlapped data \"\n           \"transfers:\\n\");\n    printf(\"No overlap at all (transfer-kernel-transfer): %f ms \\n\", memcpy_h2d_time + memcpy_d2h_time + kernel_time);\n    printf(\"Compute can overlap with one transfer: %f ms\\n\", max((memcpy_h2d_time + memcpy_d2h_time), kernel_time));\n    printf(\"Compute can overlap with both data transfers: %f ms\\n\",\n           max(max(memcpy_h2d_time, memcpy_d2h_time), kernel_time));\n\n    // Process pipelined work\n    float serial_time  = processWithStreams(1);\n    float overlap_time = processWithStreams(STREAM_COUNT);\n\n    printf(\"\\nAverage measured timings over %d repetitions:\\n\", nreps);\n    printf(\" Avg. time when execution fully serialized\\t: %f ms\\n\", serial_time / nreps);\n    printf(\" Avg. time when overlapped using %d streams\\t: %f ms\\n\", STREAM_COUNT, overlap_time / nreps);\n    printf(\" Avg. speedup gained (serialized - overlapped)\\t: %f ms\\n\", (serial_time - overlap_time) / nreps);\n\n    printf(\"\\nMeasured throughput:\\n\");\n    printf(\" Fully serialized execution\\t\\t: %f GB/s\\n\", (nreps * (memsize * 2e-6)) / serial_time);\n    printf(\" Overlapped using %d streams\\t\\t: %f GB/s\\n\", STREAM_COUNT, (nreps * (memsize * 2e-6)) / overlap_time);\n\n    // Verify the results, we will use the results for final output\n    bool bResults = test();\n\n    // Free resources\n\n    free(h_data_source);\n    free(h_data_sink);\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        cudaFreeHost(h_data_in[i]);\n        cudaFree(d_data_in[i]);\n\n        cudaFreeHost(h_data_out[i]);\n        cudaFree(d_data_out[i]);\n\n        cudaStreamDestroy(stream[i]);\n        cudaEventDestroy(cycleDone[i]);\n    }\n\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n\n    // Test result\n    exit(bResults ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nfloat processWithStreams(int streams_used)\n{\n    int current_stream = 0;\n\n    float time;\n\n    // Do processing in a loop\n    //\n    // Note: All memory commands are processed in the order  they are issued,\n    // independent of the stream they are enqueued in. Hence the pattern by\n    // which the copy and kernel commands are enqueued in the stream\n    // has an influence on the achieved overlap.\n\n    cudaEventRecord(start, 0);\n\n    for (int i = 0; i < nreps; ++i) {\n        int next_stream = (current_stream + 1) % streams_used;\n\n#ifdef SIMULATE_IO\n        // Store the result\n        memcpy(h_data_sink, h_data_out[current_stream], memsize);\n\n        // Read new input\n        memcpy(h_data_in[next_stream], h_data_source, memsize);\n#endif\n\n        // Ensure that processing and copying of the last cycle has finished\n        cudaEventSynchronize(cycleDone[next_stream]);\n\n        // Process current frame\n        incKernel<<<grid, block, 0, stream[current_stream]>>>(\n            d_data_out[current_stream], d_data_in[current_stream], N, inner_reps);\n\n        // Upload next frame\n        checkCudaErrors(cudaMemcpyAsync(\n            d_data_in[next_stream], h_data_in[next_stream], memsize, cudaMemcpyHostToDevice, stream[next_stream]));\n\n        // Download current frame\n        checkCudaErrors(cudaMemcpyAsync(h_data_out[current_stream],\n                                        d_data_out[current_stream],\n                                        memsize,\n                                        cudaMemcpyDeviceToHost,\n                                        stream[current_stream]));\n\n        checkCudaErrors(cudaEventRecord(cycleDone[current_stream], stream[current_stream]));\n\n        current_stream = next_stream;\n    }\n\n    cudaEventRecord(stop, 0);\n\n    cudaDeviceSynchronize();\n\n    cudaEventElapsedTime(&time, start, stop);\n\n    return time;\n}\n\nvoid init()\n{\n    for (int i = 0; i < N; ++i) {\n        h_data_source[i] = 0;\n    }\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        memcpy(h_data_in[i], h_data_source, memsize);\n    }\n}\n\nbool test()\n{\n    bool passed = true;\n\n    for (int j = 0; j < STREAM_COUNT; ++j) {\n        for (int i = 0; i < N; ++i) {\n            passed &= (h_data_out[j][i] == 1);\n        }\n    }\n\n    return passed;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Quadro and Tesla GPUs with compute capability >= 2.0 can overlap two\n * memcopies with kernel execution. This sample illustrates the usage of CUDA\n * streams to achieve overlapping of kernel execution with copying data to and\n * from the device.\n *\n * Additionally, this sample uses CUDA events to measure elapsed time for\n * CUDA calls.  Events are a part of CUDA API and provide a system independent\n * way to measure execution times on CUDA devices with approximately 0.5\n * microsecond precision.\n *\n * Elapsed times are averaged over nreps repetitions (10 by default).\n *\n */\n\nconst char *sSDKname = \"simpleMultiCopy\";\n\n// includes, system\n#include <stdio.h>\n\n// include CUDA\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\n// includes, kernels\n// Declare the CUDA kernels here and main() code that is needed to launch\n// Compute workload on the system\n__global__ void incKernel(int *g_out, int *g_in, int N, int inner_reps)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        for (int i = 0; i < inner_reps; ++i) {\n            g_out[idx] = g_in[idx] + 1;\n        }\n    }\n}\n\n#define STREAM_COUNT 4\n\n// Uncomment to simulate data source/sink IO times\n// #define SIMULATE_IO\n\nint *h_data_source;\nint *h_data_sink;\n\nint *h_data_in[STREAM_COUNT];\nint *d_data_in[STREAM_COUNT];\n\nint *h_data_out[STREAM_COUNT];\nint *d_data_out[STREAM_COUNT];\n\nhipEvent_t  cycleDone[STREAM_COUNT];\nhipStream_t stream[STREAM_COUNT];\n\nhipEvent_t start, stop;\n\nint N          = 1 << 22;\nint nreps      = 10; // number of times each experiment is repeated\nint inner_reps = 5;\n\nint memsize;\n\ndim3 block(512);\ndim3 grid;\n\nint thread_blocks;\n\nfloat processWithStreams(int streams_used);\nvoid  init();\nbool  test();\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char *argv[])\n{\n    int            cuda_device = 0;\n    float          scale_factor;\n    hipDeviceProp_t deviceProp;\n\n    printf(\"[%s] - Starting...\\n\", sSDKname);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"device\")) {\n        cuda_device = getCmdLineArgumentInt(argc, (const char **)argv, \"device=\");\n\n        if (cuda_device < 0) {\n            printf(\"Invalid command line parameters\\n\");\n            exit(EXIT_FAILURE);\n        }\n        else {\n            printf(\"cuda_device = %d\\n\", cuda_device);\n            cuda_device = gpuDeviceInit(cuda_device);\n\n            if (cuda_device < 0) {\n                printf(\"No CUDA Capable devices found, exiting...\\n\");\n                exit(EXIT_SUCCESS);\n            }\n        }\n    }\n    else {\n        // Otherwise pick the device with the highest Gflops/s\n        cuda_device = gpuGetMaxGflopsDeviceId();\n        checkCudaErrors(hipSetDevice(cuda_device));\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n        printf(\"> Using CUDA device [%d]: %s\\n\", cuda_device, deviceProp.name);\n    }\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n    printf(\"[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    // Anything that is less than 32 Cores will have scaled down workload\n    scale_factor =\n        max((32.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n            1.0f);\n    N = (int)((float)N / scale_factor);\n\n    printf(\"> Device name: %s\\n\", deviceProp.name);\n    printf(\"> CUDA Capability %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n    printf(\"> scale_factor = %.2f\\n\", 1.0f / scale_factor);\n    printf(\"> array_size   = %d\\n\\n\", N);\n\n    memsize = N * sizeof(int);\n\n    thread_blocks = N / block.x;\n\n    grid.x = thread_blocks % 65535;\n    grid.y = (thread_blocks / 65535 + 1);\n\n    // Allocate resources\n\n    h_data_source = (int *)malloc(memsize);\n    h_data_sink   = (int *)malloc(memsize);\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        checkCudaErrors(hipHostAlloc(&h_data_in[i], memsize, hipHostMallocDefault));\n        checkCudaErrors(hipMalloc(&d_data_in[i], memsize));\n        checkCudaErrors(hipMemset(d_data_in[i], 0, memsize));\n\n        checkCudaErrors(hipHostAlloc(&h_data_out[i], memsize, hipHostMallocDefault));\n        checkCudaErrors(hipMalloc(&d_data_out[i], memsize));\n\n        checkCudaErrors(hipStreamCreate(&stream[i]));\n        checkCudaErrors(hipEventCreate(&cycleDone[i]));\n\n        hipEventRecord(cycleDone[i], stream[i]);\n    }\n\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    init();\n\n    // Kernel warmup\n    incKernel<<<grid, block>>>(d_data_out[0], d_data_in[0], N, inner_reps);\n\n    // Time copies and kernel\n    hipEventRecord(start, 0);\n    checkCudaErrors(hipMemcpyAsync(d_data_in[0], h_data_in[0], memsize, hipMemcpyHostToDevice, 0));\n    hipEventRecord(stop, 0);\n    hipEventSynchronize(stop);\n\n    float memcpy_h2d_time;\n    hipEventElapsedTime(&memcpy_h2d_time, start, stop);\n\n    hipEventRecord(start, 0);\n    checkCudaErrors(hipMemcpyAsync(h_data_out[0], d_data_out[0], memsize, hipMemcpyDeviceToHost, 0));\n    hipEventRecord(stop, 0);\n    hipEventSynchronize(stop);\n\n    float memcpy_d2h_time;\n    hipEventElapsedTime(&memcpy_d2h_time, start, stop);\n\n    hipEventRecord(start, 0);\n    incKernel<<<grid, block, 0, 0>>>(d_data_out[0], d_data_in[0], N, inner_reps);\n    hipEventRecord(stop, 0);\n    hipEventSynchronize(stop);\n\n    float kernel_time;\n    hipEventElapsedTime(&kernel_time, start, stop);\n\n    printf(\"\\n\");\n    printf(\"Relevant properties of this CUDA device\\n\");\n    int canOverlap;\n    checkCudaErrors(hipDeviceGetAttribute(&canOverlap, hipDeviceAttributeAsyncEngineCount, cuda_device));\n    printf(\"(%s) Can overlap one CPU<>GPU data transfer with GPU kernel execution \"\n           \"(device property \\\"hipDeviceAttributeAsyncEngineCount\\\")\\n\",\n           canOverlap ? \"X\" : \" \");\n    // printf(\"(%s) Can execute several GPU kernels simultaneously (compute\n    // capability >= 2.0)\\n\", deviceProp.major >= 2 ? \"X\": \" \");\n    printf(\"(%s) Can overlap two CPU<>GPU data transfers with GPU kernel execution\\n\"\n           \"    (Compute Capability >= 2.0 AND (Tesla product OR Quadro \"\n           \"4000/5000/6000/K5000)\\n\",\n           (deviceProp.major >= 2 && deviceProp.asyncEngineCount > 1) ? \"X\" : \" \");\n\n    printf(\"\\n\");\n    printf(\"Measured timings (throughput):\\n\");\n    printf(\" Memcpy host to device\\t: %f ms (%f GB/s)\\n\", memcpy_h2d_time, (memsize * 1e-6) / memcpy_h2d_time);\n    printf(\" Memcpy device to host\\t: %f ms (%f GB/s)\\n\", memcpy_d2h_time, (memsize * 1e-6) / memcpy_d2h_time);\n    printf(\" Kernel\\t\\t\\t: %f ms (%f GB/s)\\n\", kernel_time, (inner_reps * memsize * 2e-6) / kernel_time);\n\n    printf(\"\\n\");\n    printf(\"Theoretical limits for speedup gained from overlapped data \"\n           \"transfers:\\n\");\n    printf(\"No overlap at all (transfer-kernel-transfer): %f ms \\n\", memcpy_h2d_time + memcpy_d2h_time + kernel_time);\n    printf(\"Compute can overlap with one transfer: %f ms\\n\", max((memcpy_h2d_time + memcpy_d2h_time), kernel_time));\n    printf(\"Compute can overlap with both data transfers: %f ms\\n\",\n           max(max(memcpy_h2d_time, memcpy_d2h_time), kernel_time));\n\n    // Process pipelined work\n    float serial_time  = processWithStreams(1);\n    float overlap_time = processWithStreams(STREAM_COUNT);\n\n    printf(\"\\nAverage measured timings over %d repetitions:\\n\", nreps);\n    printf(\" Avg. time when execution fully serialized\\t: %f ms\\n\", serial_time / nreps);\n    printf(\" Avg. time when overlapped using %d streams\\t: %f ms\\n\", STREAM_COUNT, overlap_time / nreps);\n    printf(\" Avg. speedup gained (serialized - overlapped)\\t: %f ms\\n\", (serial_time - overlap_time) / nreps);\n\n    printf(\"\\nMeasured throughput:\\n\");\n    printf(\" Fully serialized execution\\t\\t: %f GB/s\\n\", (nreps * (memsize * 2e-6)) / serial_time);\n    printf(\" Overlapped using %d streams\\t\\t: %f GB/s\\n\", STREAM_COUNT, (nreps * (memsize * 2e-6)) / overlap_time);\n\n    // Verify the results, we will use the results for final output\n    bool bResults = test();\n\n    // Free resources\n\n    free(h_data_source);\n    free(h_data_sink);\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        hipHostFree(h_data_in[i]);\n        hipFree(d_data_in[i]);\n\n        hipHostFree(h_data_out[i]);\n        hipFree(d_data_out[i]);\n\n        hipStreamDestroy(stream[i]);\n        hipEventDestroy(cycleDone[i]);\n    }\n\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n\n    // Test result\n    exit(bResults ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nfloat processWithStreams(int streams_used)\n{\n    int current_stream = 0;\n\n    float time;\n\n    // Do processing in a loop\n    //\n    // Note: All memory commands are processed in the order  they are issued,\n    // independent of the stream they are enqueued in. Hence the pattern by\n    // which the copy and kernel commands are enqueued in the stream\n    // has an influence on the achieved overlap.\n\n    hipEventRecord(start, 0);\n\n    for (int i = 0; i < nreps; ++i) {\n        int next_stream = (current_stream + 1) % streams_used;\n\n#ifdef SIMULATE_IO\n        // Store the result\n        memcpy(h_data_sink, h_data_out[current_stream], memsize);\n\n        // Read new input\n        memcpy(h_data_in[next_stream], h_data_source, memsize);\n#endif\n\n        // Ensure that processing and copying of the last cycle has finished\n        hipEventSynchronize(cycleDone[next_stream]);\n\n        // Process current frame\n        incKernel<<<grid, block, 0, stream[current_stream]>>>(\n            d_data_out[current_stream], d_data_in[current_stream], N, inner_reps);\n\n        // Upload next frame\n        checkCudaErrors(hipMemcpyAsync(\n            d_data_in[next_stream], h_data_in[next_stream], memsize, hipMemcpyHostToDevice, stream[next_stream]));\n\n        // Download current frame\n        checkCudaErrors(hipMemcpyAsync(h_data_out[current_stream],\n                                        d_data_out[current_stream],\n                                        memsize,\n                                        hipMemcpyDeviceToHost,\n                                        stream[current_stream]));\n\n        checkCudaErrors(hipEventRecord(cycleDone[current_stream], stream[current_stream]));\n\n        current_stream = next_stream;\n    }\n\n    hipEventRecord(stop, 0);\n\n    hipDeviceSynchronize();\n\n    hipEventElapsedTime(&time, start, stop);\n\n    return time;\n}\n\nvoid init()\n{\n    for (int i = 0; i < N; ++i) {\n        h_data_source[i] = 0;\n    }\n\n    for (int i = 0; i < STREAM_COUNT; ++i) {\n        memcpy(h_data_in[i], h_data_source, memsize);\n    }\n}\n\nbool test()\n{\n    bool passed = true;\n\n    for (int j = 0; j < STREAM_COUNT; ++j) {\n        for (int i = 0; i < N; ++i) {\n            passed &= (h_data_out[j][i] == 1);\n        }\n    }\n\n    return passed;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleMultiCopy/simpleMultiCopy.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleDrvRuntime/vectorAdd_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how use texture fetches in CUDA\n *\n * This sample takes an input PGM image (imageFilename) and generates\n * an output PGM image (imageFilename_out).  This CUDA kernel performs\n * a simple 2D transform (rotation) on the texture coordinates (u,v).\n */\n\n// Includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n\n#define MIN_EPSILON_ERROR 5e-3f\n\n////////////////////////////////////////////////////////////////////////////////\n// Define the files that are to be save and the reference images for validation\nconst char *imageFilename = \"teapot512.pgm\";\nconst char *refFilename   = \"ref_rotated.pgm\";\nfloat       angle         = 0.5f; // angle to rotate image by (in radians)\n\n// Auto-Verification Code\nbool testResult = true;\n\nstatic const char *sampleName = \"simpleSurfaceWrite\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Write to a cuArray (texture data source) using surface writes\n//! @param gIData input data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void surfaceWriteKernel(float *gIData, int width, int height, cudaSurfaceObject_t outputSurface)\n{\n    // calculate surface coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // read from global memory and write to cuarray (via surface reference)\n    surf2Dwrite(gIData[y * width + x], outputSurface, x * 4, y, cudaBoundaryModeTrap);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param gOData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *gOData, int width, int height, float theta, cudaTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u = x / (float)width;\n    float v = y / (float)height;\n\n    // transform coordinates\n    u -= 0.5f;\n    v -= 0.5f;\n    float tu = u * cosf(theta) - v * sinf(theta) + 0.5f;\n    float tv = v * cosf(theta) + u * sinf(theta) + 0.5f;\n\n    // read from texture and write to global memory\n    gOData[y * width + x] = tex2D<float>(tex, tu, tv);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\nextern \"C\" void computeGold(float *reference, float *idata, const unsigned int len);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    // Process command-line arguments\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"input\")) {\n            getCmdLineArgumentString(argc, (const char **)argv, \"input\", (char **)&imageFilename);\n\n            if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n                getCmdLineArgumentString(argc, (const char **)argv, \"reference\", (char **)&refFilename);\n            }\n            else {\n                printf(\"-input flag should be used with -reference flag\");\n                exit(EXIT_FAILURE);\n            }\n        }\n        else if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n            printf(\"-reference flag should be used with -input flag\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    // Use command-line specified CUDA device,\n    // otherwise use device with highest Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // Get number of SMs on this GPU\n    cudaDeviceProp deviceProps;\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors, SM %d.%d\\n\",\n           deviceProps.name,\n           deviceProps.multiProcessorCount,\n           deviceProps.major,\n           deviceProps.minor);\n\n    // Load image from disk\n    float       *hData = NULL;\n    unsigned int width, height;\n    char        *imagePath = sdkFindFilePath(imageFilename, argv[0]);\n\n    if (imagePath == NULL) {\n        printf(\"Unable to source image input file: %s\\n\", imageFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(imagePath, &hData, &width, &height);\n\n    unsigned int size = width * height * sizeof(float);\n    printf(\"Loaded '%s', %d x %d pixels\\n\", imageFilename, width, height);\n\n    // Load reference image from image (output)\n    float *hDataRef = (float *)malloc(size);\n    char  *refPath  = sdkFindFilePath(refFilename, argv[0]);\n\n    if (refPath == NULL) {\n        printf(\"Unable to find reference image file: %s\\n\", refFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(refPath, &hDataRef, &width, &height);\n\n    // Allocate device memory for result\n    float *dData = NULL;\n    checkCudaErrors(cudaMalloc((void **)&dData, size));\n\n    // Allocate array and copy image data\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    cudaArray            *cuArray;\n    checkCudaErrors(cudaMallocArray(&cuArray, &channelDesc, width, height, cudaArraySurfaceLoadStore));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    cudaSurfaceObject_t outputSurface;\n    cudaResourceDesc    surfRes;\n    memset(&surfRes, 0, sizeof(cudaResourceDesc));\n    surfRes.resType         = cudaResourceTypeArray;\n    surfRes.res.array.array = cuArray;\n\n    checkCudaErrors(cudaCreateSurfaceObject(&outputSurface, &surfRes));\n#if 1\n    checkCudaErrors(cudaMemcpy(dData, hData, size, cudaMemcpyHostToDevice));\n    surfaceWriteKernel<<<dimGrid, dimBlock>>>(dData, width, height, outputSurface);\n#else // This is what differs from the example simpleTexture\n    checkCudaErrors(cudaMemcpyToArray(cuArray, 0, 0, hData, size, cudaMemcpyHostToDevice));\n#endif\n\n    cudaTextureObject_t tex;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = cuArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    // Warmup\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // Execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    // Check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    cudaDeviceSynchronize();\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mpixels/sec\\n\", (width * height / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);\n    sdkDeleteTimer(&timer);\n\n    // Allocate mem for the result on host side\n    float *hOData = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(hOData, dData, size, cudaMemcpyDeviceToHost));\n\n    // Write result to file\n    char outputFilename[1024];\n    strcpy(outputFilename, \"output.pgm\");\n    sdkSavePGM(\"output.pgm\", hOData, width, height);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    // Write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // Write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", hOData, width * height, 0.0f, false);\n    }\n    else {\n        // We need to reload the data from disk,\n        // because it is inverted upon output\n        sdkLoadPGM(outputFilename, &hOData, &width, &height);\n\n        printf(\"Comparing files\\n\");\n        printf(\"\\toutput:    <%s>\\n\", outputFilename);\n        printf(\"\\treference: <%s>\\n\", refPath);\n        testResult = compareData(hOData, hDataRef, width * height, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    checkCudaErrors(cudaDestroySurfaceObject(outputSurface));\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n    checkCudaErrors(cudaFree(dData));\n    checkCudaErrors(cudaFreeArray(cuArray));\n    free(imagePath);\n    free(refPath);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how use texture fetches in CUDA\n *\n * This sample takes an input PGM image (imageFilename) and generates\n * an output PGM image (imageFilename_out).  This CUDA kernel performs\n * a simple 2D transform (rotation) on the texture coordinates (u,v).\n */\n\n// Includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n\n#define MIN_EPSILON_ERROR 5e-3f\n\n////////////////////////////////////////////////////////////////////////////////\n// Define the files that are to be save and the reference images for validation\nconst char *imageFilename = \"teapot512.pgm\";\nconst char *refFilename   = \"ref_rotated.pgm\";\nfloat       angle         = 0.5f; // angle to rotate image by (in radians)\n\n// Auto-Verification Code\nbool testResult = true;\n\nstatic const char *sampleName = \"simpleSurfaceWrite\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Write to a cuArray (texture data source) using surface writes\n//! @param gIData input data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void surfaceWriteKernel(float *gIData, int width, int height, hipSurfaceObject_t outputSurface)\n{\n    // calculate surface coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // read from global memory and write to cuarray (via surface reference)\n    surf2Dwrite(gIData[y * width + x], outputSurface, x * 4, y, hipBoundaryModeTrap);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param gOData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *gOData, int width, int height, float theta, hipTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u = x / (float)width;\n    float v = y / (float)height;\n\n    // transform coordinates\n    u -= 0.5f;\n    v -= 0.5f;\n    float tu = u * cosf(theta) - v * sinf(theta) + 0.5f;\n    float tv = v * cosf(theta) + u * sinf(theta) + 0.5f;\n\n    // read from texture and write to global memory\n    gOData[y * width + x] = tex2D<float>(tex, tu, tv);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\nextern \"C\" void computeGold(float *reference, float *idata, const unsigned int len);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    // Process command-line arguments\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"input\")) {\n            getCmdLineArgumentString(argc, (const char **)argv, \"input\", (char **)&imageFilename);\n\n            if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n                getCmdLineArgumentString(argc, (const char **)argv, \"reference\", (char **)&refFilename);\n            }\n            else {\n                printf(\"-input flag should be used with -reference flag\");\n                exit(EXIT_FAILURE);\n            }\n        }\n        else if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n            printf(\"-reference flag should be used with -input flag\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    // Use command-line specified CUDA device,\n    // otherwise use device with highest Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // Get number of SMs on this GPU\n    hipDeviceProp_t deviceProps;\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors, SM %d.%d\\n\",\n           deviceProps.name,\n           deviceProps.multiProcessorCount,\n           deviceProps.major,\n           deviceProps.minor);\n\n    // Load image from disk\n    float       *hData = NULL;\n    unsigned int width, height;\n    char        *imagePath = sdkFindFilePath(imageFilename, argv[0]);\n\n    if (imagePath == NULL) {\n        printf(\"Unable to source image input file: %s\\n\", imageFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(imagePath, &hData, &width, &height);\n\n    unsigned int size = width * height * sizeof(float);\n    printf(\"Loaded '%s', %d x %d pixels\\n\", imageFilename, width, height);\n\n    // Load reference image from image (output)\n    float *hDataRef = (float *)malloc(size);\n    char  *refPath  = sdkFindFilePath(refFilename, argv[0]);\n\n    if (refPath == NULL) {\n        printf(\"Unable to find reference image file: %s\\n\", refFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(refPath, &hDataRef, &width, &height);\n\n    // Allocate device memory for result\n    float *dData = NULL;\n    checkCudaErrors(hipMalloc((void **)&dData, size));\n\n    // Allocate array and copy image data\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    hipArray            *cuArray;\n    checkCudaErrors(hipMallocArray(&cuArray, &channelDesc, width, height, hipArraySurfaceLoadStore));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    hipSurfaceObject_t outputSurface;\n    hipResourceDesc    surfRes;\n    memset(&surfRes, 0, sizeof(hipResourceDesc));\n    surfRes.resType         = hipResourceTypeArray;\n    surfRes.res.array.array = cuArray;\n\n    checkCudaErrors(hipCreateSurfaceObject(&outputSurface, &surfRes));\n#if 1\n    checkCudaErrors(hipMemcpy(dData, hData, size, hipMemcpyHostToDevice));\n    surfaceWriteKernel<<<dimGrid, dimBlock>>>(dData, width, height, outputSurface);\n#else // This is what differs from the example simpleTexture\n    checkCudaErrors(hipMemcpyToArray(cuArray, 0, 0, hData, size, hipMemcpyHostToDevice));\n#endif\n\n    hipTextureObject_t tex;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = cuArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    // Warmup\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // Execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    // Check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    hipDeviceSynchronize();\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mpixels/sec\\n\", (width * height / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);\n    sdkDeleteTimer(&timer);\n\n    // Allocate mem for the result on host side\n    float *hOData = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(hOData, dData, size, hipMemcpyDeviceToHost));\n\n    // Write result to file\n    char outputFilename[1024];\n    strcpy(outputFilename, \"output.pgm\");\n    sdkSavePGM(\"output.pgm\", hOData, width, height);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    // Write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // Write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", hOData, width * height, 0.0f, false);\n    }\n    else {\n        // We need to reload the data from disk,\n        // because it is inverted upon output\n        sdkLoadPGM(outputFilename, &hOData, &width, &height);\n\n        printf(\"Comparing files\\n\");\n        printf(\"\\toutput:    <%s>\\n\", outputFilename);\n        printf(\"\\treference: <%s>\\n\", refPath);\n        testResult = compareData(hOData, hDataRef, width * height, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    checkCudaErrors(hipDestroySurfaceObject(outputSurface));\n    checkCudaErrors(hipDestroyTextureObject(tex));\n    checkCudaErrors(hipFree(dData));\n    checkCudaErrors(hipFreeArray(cuArray));\n    free(imagePath);\n    free(refPath);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleSurfaceWrite/simpleSurfaceWrite.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// This sample demonstrates how HyperQ allows supporting devices to avoid false\n// dependencies between kernels in different streams.\n//\n// - Devices without HyperQ will run a maximum of two kernels at a time (one\n//   kernel_A and one kernel_B).\n// - Devices with HyperQ will run up to 32 kernels simultaneously.\n\n#include <cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\nconst char *sSDKsample = \"hyperQ\";\n\n// This subroutine does no real work but runs for at least the specified number\n// of clock ticks.\n__device__ void clock_block(clock_t *d_o, clock_t clock_count)\n{\n    unsigned int start_clock = (unsigned int)clock();\n\n    clock_t clock_offset = 0;\n\n    while (clock_offset < clock_count) {\n        unsigned int end_clock = (unsigned int)clock();\n\n        // The code below should work like\n        // this (thanks to modular arithmetics):\n        //\n        // clock_offset = (clock_t) (end_clock > start_clock ?\n        //                           end_clock - start_clock :\n        //                           end_clock + (0xffffffffu - start_clock));\n        //\n        // Indeed, let m = 2^32 then\n        // end - start = end + m - start (mod m).\n\n        clock_offset = (clock_t)(end_clock - start_clock);\n    }\n\n    d_o[0] = clock_offset;\n}\n\n// We create two identical kernels calling clock_block(), we create two so that\n// we can identify dependencies in the profile timeline (\"kernel_B\" is always\n// dependent on \"kernel_A\" in the same stream).\n__global__ void kernel_A(clock_t *d_o, clock_t clock_count) { clock_block(d_o, clock_count); }\n__global__ void kernel_B(clock_t *d_o, clock_t clock_count) { clock_block(d_o, clock_count); }\n\n// Single-warp reduction kernel (note: this is not optimized for simplicity)\n__global__ void sum(clock_t *d_clocks, int N)\n{\n    // Handle to thread block group\n    cg::thread_block   cta = cg::this_thread_block();\n    __shared__ clock_t s_clocks[32];\n\n    clock_t my_sum = 0;\n\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        my_sum += d_clocks[i];\n    }\n\n    s_clocks[threadIdx.x] = my_sum;\n    cg::sync(cta);\n\n    for (int i = warpSize / 2; i > 0; i /= 2) {\n        if (threadIdx.x < i) {\n            s_clocks[threadIdx.x] += s_clocks[threadIdx.x + i];\n        }\n\n        cg::sync(cta);\n    }\n\n    if (threadIdx.x == 0) {\n        d_clocks[0] = s_clocks[0];\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int   nstreams    = 32; // One stream for each pair of kernels\n    float kernel_time = 10; // Time each kernel should run in ms\n    float elapsed_time;\n    int   cuda_device = 0;\n\n    printf(\"starting %s...\\n\", sSDKsample);\n\n    // Get number of streams (if overridden on the command line)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"nstreams\")) {\n        nstreams = getCmdLineArgumentInt(argc, (const char **)argv, \"nstreams\");\n    }\n\n    // Use command-line specified CUDA device, otherwise use device with\n    // highest Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    // Get device properties\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDevice(&cuda_device));\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n\n    // Get device clock rate\n    int clockRate;\n    checkCudaErrors(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, cuda_device));\n\n    // HyperQ is available in devices of Compute Capability 3.5 and higher\n    if (deviceProp.major < 3 || (deviceProp.major == 3 && deviceProp.minor < 5)) {\n        if (deviceProp.concurrentKernels == 0) {\n            printf(\"> GPU does not support concurrent kernel execution (SM 3.5 or \"\n                   \"higher required)\\n\");\n            printf(\"  CUDA kernel runs will be serialized\\n\");\n        }\n        else {\n            printf(\"> GPU does not support HyperQ\\n\");\n            printf(\"  CUDA kernel runs will have limited concurrency\\n\");\n        }\n    }\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // Allocate host memory for the output (reduced to a single value)\n    clock_t *a = 0;\n    checkCudaErrors(cudaMallocHost((void **)&a, sizeof(clock_t)));\n\n    // Allocate device memory for the output (one value for each kernel)\n    clock_t *d_a = 0;\n    checkCudaErrors(cudaMalloc((void **)&d_a, 2 * nstreams * sizeof(clock_t)));\n\n    // Allocate and initialize an array of stream handles\n    cudaStream_t *streams = (cudaStream_t *)malloc(nstreams * sizeof(cudaStream_t));\n\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(cudaStreamCreate(&(streams[i])));\n    }\n\n    // Create CUDA event handles\n    cudaEvent_t start_event, stop_event;\n    checkCudaErrors(cudaEventCreate(&start_event));\n    checkCudaErrors(cudaEventCreate(&stop_event));\n\n    // Target time per kernel is kernel_time ms, clockRate is in KHz\n    // Target number of clocks = target time * clock frequency\n#if defined(__arm__) || defined(__aarch64__)\n    // the kernel takes more time than the channel reset time on arm archs, so to\n    // prevent hangs reduce time_clocks.\n    clock_t time_clocks = (clock_t)(kernel_time * (clockRate / 100));\n#else\n    clock_t time_clocks = (clock_t)(kernel_time * clockRate);\n#endif\n    clock_t total_clocks = 0;\n\n    // Start the clock\n    checkCudaErrors(cudaEventRecord(start_event, 0));\n\n    // Queue pairs of {kernel_A, kernel_B} in separate streams\n    for (int i = 0; i < nstreams; ++i) {\n        kernel_A<<<1, 1, 0, streams[i]>>>(&d_a[2 * i], time_clocks);\n        total_clocks += time_clocks;\n        kernel_B<<<1, 1, 0, streams[i]>>>(&d_a[2 * i + 1], time_clocks);\n        total_clocks += time_clocks;\n    }\n\n    // Stop the clock in stream 0 (i.e. all previous kernels will be complete)\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n\n    // At this point the CPU has dispatched all work for the GPU and can\n    // continue processing other tasks in parallel. In this sample we just want\n    // to wait until all work is done so we use a blocking cudaMemcpy below.\n\n    // Run the sum kernel and copy the result back to host\n    sum<<<1, 32>>>(d_a, 2 * nstreams);\n    checkCudaErrors(cudaMemcpy(a, d_a, sizeof(clock_t), cudaMemcpyDeviceToHost));\n\n    // stop_event will have been recorded but including the synchronize here to\n    // prevent copy/paste errors!\n    checkCudaErrors(cudaEventSynchronize(stop_event));\n    checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start_event, stop_event));\n\n    printf(\"Expected time for serial execution of %d sets of kernels is between \"\n           \"approx. %.3fs and %.3fs\\n\",\n           nstreams,\n           (nstreams + 1) * kernel_time / 1000.0f,\n           2 * nstreams * kernel_time / 1000.0f);\n    printf(\"Expected time for fully concurrent execution of %d sets of kernels is \"\n           \"approx. %.3fs\\n\",\n           nstreams,\n           2 * kernel_time / 1000.0f);\n    printf(\"Measured time for sample = %.3fs\\n\", elapsed_time / 1000.0f);\n\n    bool bTestResult = (a[0] >= total_clocks);\n\n    // Release resources\n    for (int i = 0; i < nstreams; i++) {\n        cudaStreamDestroy(streams[i]);\n    }\n\n    free(streams);\n    cudaEventDestroy(start_event);\n    cudaEventDestroy(stop_event);\n    cudaFreeHost(a);\n    cudaFree(d_a);\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// This sample demonstrates how HyperQ allows supporting devices to avoid false\n// dependencies between kernels in different streams.\n//\n// - Devices without HyperQ will run a maximum of two kernels at a time (one\n//   kernel_A and one kernel_B).\n// - Devices with HyperQ will run up to 32 kernels simultaneously.\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\nconst char *sSDKsample = \"hyperQ\";\n\n// This subroutine does no real work but runs for at least the specified number\n// of clock ticks.\n__device__ void clock_block(clock_t *d_o, clock_t clock_count)\n{\n    unsigned int start_clock = (unsigned int)clock();\n\n    clock_t clock_offset = 0;\n\n    while (clock_offset < clock_count) {\n        unsigned int end_clock = (unsigned int)clock();\n\n        // The code below should work like\n        // this (thanks to modular arithmetics):\n        //\n        // clock_offset = (clock_t) (end_clock > start_clock ?\n        //                           end_clock - start_clock :\n        //                           end_clock + (0xffffffffu - start_clock));\n        //\n        // Indeed, let m = 2^32 then\n        // end - start = end + m - start (mod m).\n\n        clock_offset = (clock_t)(end_clock - start_clock);\n    }\n\n    d_o[0] = clock_offset;\n}\n\n// We create two identical kernels calling clock_block(), we create two so that\n// we can identify dependencies in the profile timeline (\"kernel_B\" is always\n// dependent on \"kernel_A\" in the same stream).\n__global__ void kernel_A(clock_t *d_o, clock_t clock_count) { clock_block(d_o, clock_count); }\n__global__ void kernel_B(clock_t *d_o, clock_t clock_count) { clock_block(d_o, clock_count); }\n\n// Single-warp reduction kernel (note: this is not optimized for simplicity)\n__global__ void sum(clock_t *d_clocks, int N)\n{\n    // Handle to thread block group\n    cg::thread_block   cta = cg::this_thread_block();\n    __shared__ clock_t s_clocks[32];\n\n    clock_t my_sum = 0;\n\n    for (int i = threadIdx.x; i < N; i += blockDim.x) {\n        my_sum += d_clocks[i];\n    }\n\n    s_clocks[threadIdx.x] = my_sum;\n    cg::sync(cta);\n\n    for (int i = warpSize / 2; i > 0; i /= 2) {\n        if (threadIdx.x < i) {\n            s_clocks[threadIdx.x] += s_clocks[threadIdx.x + i];\n        }\n\n        cg::sync(cta);\n    }\n\n    if (threadIdx.x == 0) {\n        d_clocks[0] = s_clocks[0];\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int   nstreams    = 32; // One stream for each pair of kernels\n    float kernel_time = 10; // Time each kernel should run in ms\n    float elapsed_time;\n    int   cuda_device = 0;\n\n    printf(\"starting %s...\\n\", sSDKsample);\n\n    // Get number of streams (if overridden on the command line)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"nstreams\")) {\n        nstreams = getCmdLineArgumentInt(argc, (const char **)argv, \"nstreams\");\n    }\n\n    // Use command-line specified CUDA device, otherwise use device with\n    // highest Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    // Get device properties\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDevice(&cuda_device));\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n\n    // Get device clock rate\n    int clockRate;\n    checkCudaErrors(hipDeviceGetAttribute(&clockRate, hipDeviceAttributeClockRate, cuda_device));\n\n    // HyperQ is available in devices of Compute Capability 3.5 and higher\n    if (deviceProp.major < 3 || (deviceProp.major == 3 && deviceProp.minor < 5)) {\n        if (deviceProp.concurrentKernels == 0) {\n            printf(\"> GPU does not support concurrent kernel execution (SM 3.5 or \"\n                   \"higher required)\\n\");\n            printf(\"  CUDA kernel runs will be serialized\\n\");\n        }\n        else {\n            printf(\"> GPU does not support HyperQ\\n\");\n            printf(\"  CUDA kernel runs will have limited concurrency\\n\");\n        }\n    }\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // Allocate host memory for the output (reduced to a single value)\n    clock_t *a = 0;\n    checkCudaErrors(hipHostMalloc((void **)&a, sizeof(clock_t)));\n\n    // Allocate device memory for the output (one value for each kernel)\n    clock_t *d_a = 0;\n    checkCudaErrors(hipMalloc((void **)&d_a, 2 * nstreams * sizeof(clock_t)));\n\n    // Allocate and initialize an array of stream handles\n    hipStream_t *streams = (hipStream_t *)malloc(nstreams * sizeof(hipStream_t));\n\n    for (int i = 0; i < nstreams; i++) {\n        checkCudaErrors(hipStreamCreate(&(streams[i])));\n    }\n\n    // Create CUDA event handles\n    hipEvent_t start_event, stop_event;\n    checkCudaErrors(hipEventCreate(&start_event));\n    checkCudaErrors(hipEventCreate(&stop_event));\n\n    // Target time per kernel is kernel_time ms, clockRate is in KHz\n    // Target number of clocks = target time * clock frequency\n#if defined(__arm__) || defined(__aarch64__)\n    // the kernel takes more time than the channel reset time on arm archs, so to\n    // prevent hangs reduce time_clocks.\n    clock_t time_clocks = (clock_t)(kernel_time * (clockRate / 100));\n#else\n    clock_t time_clocks = (clock_t)(kernel_time * clockRate);\n#endif\n    clock_t total_clocks = 0;\n\n    // Start the clock\n    checkCudaErrors(hipEventRecord(start_event, 0));\n\n    // Queue pairs of {kernel_A, kernel_B} in separate streams\n    for (int i = 0; i < nstreams; ++i) {\n        kernel_A<<<1, 1, 0, streams[i]>>>(&d_a[2 * i], time_clocks);\n        total_clocks += time_clocks;\n        kernel_B<<<1, 1, 0, streams[i]>>>(&d_a[2 * i + 1], time_clocks);\n        total_clocks += time_clocks;\n    }\n\n    // Stop the clock in stream 0 (i.e. all previous kernels will be complete)\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n\n    // At this point the CPU has dispatched all work for the GPU and can\n    // continue processing other tasks in parallel. In this sample we just want\n    // to wait until all work is done so we use a blocking cudaMemcpy below.\n\n    // Run the sum kernel and copy the result back to host\n    sum<<<1, 32>>>(d_a, 2 * nstreams);\n    checkCudaErrors(hipMemcpy(a, d_a, sizeof(clock_t), hipMemcpyDeviceToHost));\n\n    // stop_event will have been recorded but including the synchronize here to\n    // prevent copy/paste errors!\n    checkCudaErrors(hipEventSynchronize(stop_event));\n    checkCudaErrors(hipEventElapsedTime(&elapsed_time, start_event, stop_event));\n\n    printf(\"Expected time for serial execution of %d sets of kernels is between \"\n           \"approx. %.3fs and %.3fs\\n\",\n           nstreams,\n           (nstreams + 1) * kernel_time / 1000.0f,\n           2 * nstreams * kernel_time / 1000.0f);\n    printf(\"Expected time for fully concurrent execution of %d sets of kernels is \"\n           \"approx. %.3fs\\n\",\n           nstreams,\n           2 * kernel_time / 1000.0f);\n    printf(\"Measured time for sample = %.3fs\\n\", elapsed_time / 1000.0f);\n\n    bool bTestResult = (a[0] >= total_clocks);\n\n    // Release resources\n    for (int i = 0; i < nstreams; i++) {\n        hipStreamDestroy(streams[i]);\n    }\n\n    free(streams);\n    hipEventDestroy(start_event);\n    hipEventDestroy(stop_event);\n    hipHostFree(a);\n    hipFree(d_a);\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleHyperQ/simpleHyperQ.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how use texture fetches in CUDA\n *\n * This sample takes an input PGM image (image_filename) and generates\n * an output PGM image (image_filename_out).  This CUDA kernel performs\n * a simple 2D transform (rotation) on the texture coordinates (u,v).\n */\n\n// Includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n\n#define MAX_EPSILON_ERROR 5e-3f\n\n// Define the files that are to be save and the reference images for validation\nconst char *imageFilename = \"teapot512.pgm\";\nconst char *refFilename   = \"ref_rotated.pgm\";\n\nconst char *sampleName = \"simpleTexture\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Constants\nconst float angle = 0.5f; // angle to rotate image by (in radians)\n\n// Auto-Verification Code\nbool testResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param outputData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *outputData, int width, int height, float theta, cudaTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    outputData[y * width + x] = tex2D<float>(tex, tu + 0.5f, tv + 0.5f);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    // Process command-line arguments\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"input\")) {\n            getCmdLineArgumentString(argc, (const char **)argv, \"input\", (char **)&imageFilename);\n\n            if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n                getCmdLineArgumentString(argc, (const char **)argv, \"reference\", (char **)&refFilename);\n            }\n            else {\n                printf(\"-input flag should be used with -reference flag\");\n                exit(EXIT_FAILURE);\n            }\n        }\n        else if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n            printf(\"-reference flag should be used with -input flag\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // load image from disk\n    float       *hData = NULL;\n    unsigned int width, height;\n    char        *imagePath = sdkFindFilePath(imageFilename, argv[0]);\n\n    if (imagePath == NULL) {\n        printf(\"Unable to source image file: %s\\n\", imageFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(imagePath, &hData, &width, &height);\n\n    unsigned int size = width * height * sizeof(float);\n    printf(\"Loaded '%s', %d x %d pixels\\n\", imageFilename, width, height);\n\n    // Load reference image from image (output)\n    float *hDataRef = (float *)malloc(size);\n    char  *refPath  = sdkFindFilePath(refFilename, argv[0]);\n\n    if (refPath == NULL) {\n        printf(\"Unable to find reference image file: %s\\n\", refFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(refPath, &hDataRef, &width, &height);\n\n    // Allocate device memory for result\n    float *dData = NULL;\n    checkCudaErrors(cudaMalloc((void **)&dData, size));\n\n    // Allocate array and copy image data\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    cudaArray            *cuArray;\n    checkCudaErrors(cudaMallocArray(&cuArray, &channelDesc, width, height));\n    checkCudaErrors(cudaMemcpyToArray(cuArray, 0, 0, hData, size, cudaMemcpyHostToDevice));\n\n    cudaTextureObject_t tex;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = cuArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    // Warmup\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // Execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    // Check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mpixels/sec\\n\", (width * height / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);\n    sdkDeleteTimer(&timer);\n\n    // Allocate mem for the result on host side\n    float *hOutputData = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(hOutputData, dData, size, cudaMemcpyDeviceToHost));\n\n    // Write result to file\n    char outputFilename[1024];\n    strcpy(outputFilename, imagePath);\n    strcpy(outputFilename + strlen(imagePath) - 4, \"_out.pgm\");\n    sdkSavePGM(outputFilename, hOutputData, width, height);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    // Write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // Write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", hOutputData, width * height, 0.0f, false);\n    }\n    else {\n        // We need to reload the data from disk,\n        // because it is inverted upon output\n        sdkLoadPGM(outputFilename, &hOutputData, &width, &height);\n\n        printf(\"Comparing files\\n\");\n        printf(\"\\toutput:    <%s>\\n\", outputFilename);\n        printf(\"\\treference: <%s>\\n\", refPath);\n\n        testResult = compareData(hOutputData, hDataRef, width * height, MAX_EPSILON_ERROR, 0.15f);\n    }\n\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n    checkCudaErrors(cudaFree(dData));\n    checkCudaErrors(cudaFreeArray(cuArray));\n    free(imagePath);\n    free(refPath);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how use texture fetches in CUDA\n *\n * This sample takes an input PGM image (image_filename) and generates\n * an output PGM image (image_filename_out).  This CUDA kernel performs\n * a simple 2D transform (rotation) on the texture coordinates (u,v).\n */\n\n// Includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// Includes CUDA\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n\n#define MAX_EPSILON_ERROR 5e-3f\n\n// Define the files that are to be save and the reference images for validation\nconst char *imageFilename = \"teapot512.pgm\";\nconst char *refFilename   = \"ref_rotated.pgm\";\n\nconst char *sampleName = \"simpleTexture\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Constants\nconst float angle = 0.5f; // angle to rotate image by (in radians)\n\n// Auto-Verification Code\nbool testResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param outputData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *outputData, int width, int height, float theta, hipTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    outputData[y * width + x] = tex2D<float>(tex, tu + 0.5f, tv + 0.5f);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    // Process command-line arguments\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"input\")) {\n            getCmdLineArgumentString(argc, (const char **)argv, \"input\", (char **)&imageFilename);\n\n            if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n                getCmdLineArgumentString(argc, (const char **)argv, \"reference\", (char **)&refFilename);\n            }\n            else {\n                printf(\"-input flag should be used with -reference flag\");\n                exit(EXIT_FAILURE);\n            }\n        }\n        else if (checkCmdLineFlag(argc, (const char **)argv, \"reference\")) {\n            printf(\"-reference flag should be used with -input flag\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // load image from disk\n    float       *hData = NULL;\n    unsigned int width, height;\n    char        *imagePath = sdkFindFilePath(imageFilename, argv[0]);\n\n    if (imagePath == NULL) {\n        printf(\"Unable to source image file: %s\\n\", imageFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(imagePath, &hData, &width, &height);\n\n    unsigned int size = width * height * sizeof(float);\n    printf(\"Loaded '%s', %d x %d pixels\\n\", imageFilename, width, height);\n\n    // Load reference image from image (output)\n    float *hDataRef = (float *)malloc(size);\n    char  *refPath  = sdkFindFilePath(refFilename, argv[0]);\n\n    if (refPath == NULL) {\n        printf(\"Unable to find reference image file: %s\\n\", refFilename);\n        exit(EXIT_FAILURE);\n    }\n\n    sdkLoadPGM(refPath, &hDataRef, &width, &height);\n\n    // Allocate device memory for result\n    float *dData = NULL;\n    checkCudaErrors(hipMalloc((void **)&dData, size));\n\n    // Allocate array and copy image data\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    hipArray            *cuArray;\n    checkCudaErrors(hipMallocArray(&cuArray, &channelDesc, width, height));\n    checkCudaErrors(hipMemcpyToArray(cuArray, 0, 0, hData, size, hipMemcpyHostToDevice));\n\n    hipTextureObject_t tex;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = cuArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    // Warmup\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    checkCudaErrors(hipDeviceSynchronize());\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // Execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(dData, width, height, angle, tex);\n\n    // Check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mpixels/sec\\n\", (width * height / (sdkGetTimerValue(&timer) / 1000.0f)) / 1e6);\n    sdkDeleteTimer(&timer);\n\n    // Allocate mem for the result on host side\n    float *hOutputData = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(hOutputData, dData, size, hipMemcpyDeviceToHost));\n\n    // Write result to file\n    char outputFilename[1024];\n    strcpy(outputFilename, imagePath);\n    strcpy(outputFilename + strlen(imagePath) - 4, \"_out.pgm\");\n    sdkSavePGM(outputFilename, hOutputData, width, height);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    // Write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // Write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", hOutputData, width * height, 0.0f, false);\n    }\n    else {\n        // We need to reload the data from disk,\n        // because it is inverted upon output\n        sdkLoadPGM(outputFilename, &hOutputData, &width, &height);\n\n        printf(\"Comparing files\\n\");\n        printf(\"\\toutput:    <%s>\\n\", outputFilename);\n        printf(\"\\treference: <%s>\\n\", refPath);\n\n        testResult = compareData(hOutputData, hDataRef, width * height, MAX_EPSILON_ERROR, 0.15f);\n    }\n\n    checkCudaErrors(hipDestroyTextureObject(tex));\n    checkCudaErrors(hipFree(dData));\n    checkCudaErrors(hipFreeArray(cuArray));\n    free(imagePath);\n    free(refPath);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleTexture/simpleTexture.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _SIMPLETEXTURE_KERNEL_H_\n#define _SIMPLETEXTURE_KERNEL_H_\n#include <cuda.h>\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" __global__ void transformKernel(float *g_odata, int width, int height, float theta, CUtexObject tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    g_odata[y * width + x] = tex2D<float>(tex, tu + 0.5f, tv + 0.5f);\n}\n\n#endif // #ifndef _SIMPLETEXTURE_KERNEL_H_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _SIMPLETEXTURE_KERNEL_H_\n#define _SIMPLETEXTURE_KERNEL_H_\n#include <hip/hip_runtime.h>\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform an image using texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" __global__ void transformKernel(float *g_odata, int width, int height, float theta, hipTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    g_odata[y * width + x] = tex2D<float>(tex, tu + 0.5f, tv + 0.5f);\n}\n\n#endif // #ifndef _SIMPLETEXTURE_KERNEL_H_\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleTextureDrv/simpleTexture_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cstdint>\n#include <helper_cuda.h> // helper functions for CUDA error check\n#include <iostream>\n\nconst int manualBlockSize = 32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Test kernel\n//\n// This kernel squares each array element. Each thread addresses\n// himself with threadIdx and blockIdx, so that it can handle any\n// execution configuration, including anything the launch configurator\n// API suggests.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void square(uint32_t *array, int arrayCount)\n{\n    extern __shared__ int dynamicSmem[];\n    int                   idx = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (idx < arrayCount) {\n        array[idx] *= array[idx];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Potential occupancy calculator\n//\n// The potential occupancy is calculated according to the kernel and\n// execution configuration the user desires. Occupancy is defined in\n// terms of active blocks per multiprocessor, and the user can convert\n// it to other metrics.\n//\n// This wrapper routine computes the occupancy of kernel, and reports\n// it in terms of active warps / maximum warps per SM.\n////////////////////////////////////////////////////////////////////////////////\nstatic double reportPotentialOccupancy(void *kernel, int blockSize, size_t dynamicSMem)\n{\n    int            device;\n    cudaDeviceProp prop;\n\n    int numBlocks;\n    int activeWarps;\n    int maxWarps;\n\n    double occupancy;\n\n    checkCudaErrors(cudaGetDevice(&device));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, device));\n\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&numBlocks, kernel, blockSize, dynamicSMem));\n\n    activeWarps = numBlocks * blockSize / prop.warpSize;\n    maxWarps    = prop.maxThreadsPerMultiProcessor / prop.warpSize;\n\n    occupancy = (double)activeWarps / maxWarps;\n\n    return occupancy;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Occupancy-based launch configurator\n//\n// The launch configurator, cudaOccupancyMaxPotentialBlockSize and\n// cudaOccupancyMaxPotentialBlockSizeVariableSMem, suggests a block\n// size that achieves the best theoretical occupancy. It also returns\n// the minimum number of blocks needed to achieve the occupancy on the\n// whole device.\n//\n// This launch configurator is purely occupancy-based. It doesn't\n// translate directly to performance, but the suggestion should\n// nevertheless be a good starting point for further optimizations.\n//\n// This function configures the launch based on the \"automatic\"\n// argument, records the runtime, and reports occupancy and runtime.\n////////////////////////////////////////////////////////////////////////////////\nstatic int launchConfig(uint32_t *array, int arrayCount, bool automatic)\n{\n    int    blockSize;\n    int    minGridSize;\n    int    gridSize;\n    size_t dynamicSMemUsage = 0;\n\n    cudaEvent_t start;\n    cudaEvent_t end;\n\n    float elapsedTime;\n\n    double potentialOccupancy;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&end));\n\n    if (automatic) {\n        checkCudaErrors(\n            cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)square, dynamicSMemUsage, arrayCount));\n\n        std::cout << \"Suggested block size: \" << blockSize << std::endl\n                  << \"Minimum grid size for maximum occupancy: \" << minGridSize << std::endl;\n    }\n    else {\n        // This block size is too small. Given limited number of\n        // active blocks per multiprocessor, the number of active\n        // threads will be limited, and thus unable to achieve maximum\n        // occupancy.\n        //\n        blockSize = manualBlockSize;\n    }\n\n    // Round up\n    //\n    gridSize = (arrayCount + blockSize - 1) / blockSize;\n\n    // Launch and profile\n    //\n    checkCudaErrors(cudaEventRecord(start));\n    square<<<gridSize, blockSize, dynamicSMemUsage>>>(array, arrayCount);\n    checkCudaErrors(cudaEventRecord(end));\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Calculate occupancy\n    //\n    potentialOccupancy = reportPotentialOccupancy((void *)square, blockSize, dynamicSMemUsage);\n\n    std::cout << \"Potential occupancy: \" << potentialOccupancy * 100 << \"%\" << std::endl;\n\n    // Report elapsed time\n    //\n    checkCudaErrors(cudaEventElapsedTime(&elapsedTime, start, end));\n    std::cout << \"Elapsed time: \" << elapsedTime << \"ms\" << std::endl;\n\n    return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The test\n//\n// The test generates an array and squares it with a CUDA kernel, then\n// verifies the result.\n////////////////////////////////////////////////////////////////////////////////\nstatic int test(bool automaticLaunchConfig, const int count = 1000000)\n{\n    uint32_t *array;\n    uint32_t *dArray;\n    int       size = count * sizeof(uint32_t);\n\n    array = new uint32_t[count];\n\n    for (uint32_t i = 0; i < count; i += 1) {\n        array[i] = i;\n    }\n\n    checkCudaErrors(cudaMalloc(&dArray, size));\n    checkCudaErrors(cudaMemcpy(dArray, array, size, cudaMemcpyHostToDevice));\n\n    for (uint32_t i = 0; i < count; i += 1) {\n        array[i] = 0;\n    }\n\n    launchConfig(dArray, count, automaticLaunchConfig);\n\n    checkCudaErrors(cudaMemcpy(array, dArray, size, cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaFree(dArray));\n\n    // Verify the return data\n    // Both GPU and CPU use uint32_t * uint32_t, which has well-defined overflow behavior (modulo 2^32)\n    //\n    for (uint32_t i = 0; i < count; i += 1) {\n        if (array[i] != i * i) {\n            std::cout << \"element \" << i << \" expected \" << i * i << \" actual \" << array[i] << std::endl;\n            return 1;\n        }\n    }\n    delete[] array;\n\n    return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Sample Main\n//\n// The sample runs the test with manually configured launch and\n// automatically configured launch, and reports the occupancy and\n// performance.\n////////////////////////////////////////////////////////////////////////////////\nint main()\n{\n    int status;\n\n    std::cout << \"starting Simple Occupancy\" << std::endl << std::endl;\n\n    std::cout << \"[ Manual configuration with \" << manualBlockSize << \" threads per block ]\" << std::endl;\n\n    status = test(false);\n    if (status) {\n        std::cerr << \"Test failed\\n\" << std::endl;\n        return -1;\n    }\n\n    std::cout << std::endl;\n\n    std::cout << \"[ Automatic, occupancy-based configuration ]\" << std::endl;\n    status = test(true);\n    if (status) {\n        std::cerr << \"Test failed\\n\" << std::endl;\n        return -1;\n    }\n\n    std::cout << std::endl;\n    std::cout << \"Test PASSED\\n\" << std::endl;\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cstdint>\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n#include <iostream>\n\nconst int manualBlockSize = 32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Test kernel\n//\n// This kernel squares each array element. Each thread addresses\n// himself with threadIdx and blockIdx, so that it can handle any\n// execution configuration, including anything the launch configurator\n// API suggests.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void square(uint32_t *array, int arrayCount)\n{\n    extern __shared__ int dynamicSmem[];\n    int                   idx = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (idx < arrayCount) {\n        array[idx] *= array[idx];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Potential occupancy calculator\n//\n// The potential occupancy is calculated according to the kernel and\n// execution configuration the user desires. Occupancy is defined in\n// terms of active blocks per multiprocessor, and the user can convert\n// it to other metrics.\n//\n// This wrapper routine computes the occupancy of kernel, and reports\n// it in terms of active warps / maximum warps per SM.\n////////////////////////////////////////////////////////////////////////////////\nstatic double reportPotentialOccupancy(void *kernel, int blockSize, size_t dynamicSMem)\n{\n    int            device;\n    hipDeviceProp_t prop;\n\n    int numBlocks;\n    int activeWarps;\n    int maxWarps;\n\n    double occupancy;\n\n    checkCudaErrors(hipGetDevice(&device));\n    checkCudaErrors(hipGetDeviceProperties(&prop, device));\n\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(&numBlocks, kernel, blockSize, dynamicSMem));\n\n    activeWarps = numBlocks * blockSize / prop.warpSize;\n    maxWarps    = prop.maxThreadsPerMultiProcessor / prop.warpSize;\n\n    occupancy = (double)activeWarps / maxWarps;\n\n    return occupancy;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Occupancy-based launch configurator\n//\n// The launch configurator, cudaOccupancyMaxPotentialBlockSize and\n// cudaOccupancyMaxPotentialBlockSizeVariableSMem, suggests a block\n// size that achieves the best theoretical occupancy. It also returns\n// the minimum number of blocks needed to achieve the occupancy on the\n// whole device.\n//\n// This launch configurator is purely occupancy-based. It doesn't\n// translate directly to performance, but the suggestion should\n// nevertheless be a good starting point for further optimizations.\n//\n// This function configures the launch based on the \"automatic\"\n// argument, records the runtime, and reports occupancy and runtime.\n////////////////////////////////////////////////////////////////////////////////\nstatic int launchConfig(uint32_t *array, int arrayCount, bool automatic)\n{\n    int    blockSize;\n    int    minGridSize;\n    int    gridSize;\n    size_t dynamicSMemUsage = 0;\n\n    hipEvent_t start;\n    hipEvent_t end;\n\n    float elapsedTime;\n\n    double potentialOccupancy;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&end));\n\n    if (automatic) {\n        checkCudaErrors(\n            hipOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)square, dynamicSMemUsage, arrayCount));\n\n        std::cout << \"Suggested block size: \" << blockSize << std::endl\n                  << \"Minimum grid size for maximum occupancy: \" << minGridSize << std::endl;\n    }\n    else {\n        // This block size is too small. Given limited number of\n        // active blocks per multiprocessor, the number of active\n        // threads will be limited, and thus unable to achieve maximum\n        // occupancy.\n        //\n        blockSize = manualBlockSize;\n    }\n\n    // Round up\n    //\n    gridSize = (arrayCount + blockSize - 1) / blockSize;\n\n    // Launch and profile\n    //\n    checkCudaErrors(hipEventRecord(start));\n    square<<<gridSize, blockSize, dynamicSMemUsage>>>(array, arrayCount);\n    checkCudaErrors(hipEventRecord(end));\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Calculate occupancy\n    //\n    potentialOccupancy = reportPotentialOccupancy((void *)square, blockSize, dynamicSMemUsage);\n\n    std::cout << \"Potential occupancy: \" << potentialOccupancy * 100 << \"%\" << std::endl;\n\n    // Report elapsed time\n    //\n    checkCudaErrors(hipEventElapsedTime(&elapsedTime, start, end));\n    std::cout << \"Elapsed time: \" << elapsedTime << \"ms\" << std::endl;\n\n    return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The test\n//\n// The test generates an array and squares it with a CUDA kernel, then\n// verifies the result.\n////////////////////////////////////////////////////////////////////////////////\nstatic int test(bool automaticLaunchConfig, const int count = 1000000)\n{\n    uint32_t *array;\n    uint32_t *dArray;\n    int       size = count * sizeof(uint32_t);\n\n    array = new uint32_t[count];\n\n    for (uint32_t i = 0; i < count; i += 1) {\n        array[i] = i;\n    }\n\n    checkCudaErrors(hipMalloc(&dArray, size));\n    checkCudaErrors(hipMemcpy(dArray, array, size, hipMemcpyHostToDevice));\n\n    for (uint32_t i = 0; i < count; i += 1) {\n        array[i] = 0;\n    }\n\n    launchConfig(dArray, count, automaticLaunchConfig);\n\n    checkCudaErrors(hipMemcpy(array, dArray, size, hipMemcpyDeviceToHost));\n    checkCudaErrors(hipFree(dArray));\n\n    // Verify the return data\n    // Both GPU and CPU use uint32_t * uint32_t, which has well-defined overflow behavior (modulo 2^32)\n    //\n    for (uint32_t i = 0; i < count; i += 1) {\n        if (array[i] != i * i) {\n            std::cout << \"element \" << i << \" expected \" << i * i << \" actual \" << array[i] << std::endl;\n            return 1;\n        }\n    }\n    delete[] array;\n\n    return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Sample Main\n//\n// The sample runs the test with manually configured launch and\n// automatically configured launch, and reports the occupancy and\n// performance.\n////////////////////////////////////////////////////////////////////////////////\nint main()\n{\n    int status;\n\n    std::cout << \"starting Simple Occupancy\" << std::endl << std::endl;\n\n    std::cout << \"[ Manual configuration with \" << manualBlockSize << \" threads per block ]\" << std::endl;\n\n    status = test(false);\n    if (status) {\n        std::cerr << \"Test failed\\n\" << std::endl;\n        return -1;\n    }\n\n    std::cout << std::endl;\n\n    std::cout << \"[ Automatic, occupancy-based configuration ]\" << std::endl;\n    status = test(true);\n    if (status) {\n        std::cerr << \"Test failed\\n\" << std::endl;\n        return -1;\n    }\n\n    std::cout << std::endl;\n    std::cout << \"Test PASSED\\n\" << std::endl;\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleOccupancy/simpleOccupancy.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* A program demonstrating trivial use of system-wide atomics on migratable\n * memory.\n */\n\n#include <cstdio>\n#include <ctime>\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <math.h>\n#include <stdint.h>\n\n#define min(a, b) (a) < (b) ? (a) : (b)\n#define max(a, b) (a) > (b) ? (a) : (b)\n\n#define LOOP_NUM 50\n\n__global__ void atomicKernel(int *atom_arr)\n{\n    unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < LOOP_NUM; i++) {\n        // Atomic addition\n        atomicAdd_system(&atom_arr[0], 10);\n\n        // Atomic exchange\n        atomicExch_system(&atom_arr[1], tid);\n\n        // Atomic maximum\n        atomicMax_system(&atom_arr[2], tid);\n\n        // Atomic minimum\n        atomicMin_system(&atom_arr[3], tid);\n\n        // Atomic increment (modulo 17+1)\n        atomicInc_system((unsigned int *)&atom_arr[4], 17);\n\n        // Atomic decrement\n        atomicDec_system((unsigned int *)&atom_arr[5], 137);\n\n        // Atomic compare-and-swap\n        atomicCAS_system(&atom_arr[6], tid - 1, tid);\n\n        // Bitwise atomic instructions\n\n        // Atomic AND\n        atomicAnd_system(&atom_arr[7], 2 * tid + 7);\n\n        // Atomic OR\n        atomicOr_system(&atom_arr[8], 1 << tid);\n\n        // Atomic XOR\n        atomicXor_system(&atom_arr[9], tid);\n    }\n}\n\nvoid atomicKernel_CPU(int *atom_arr, int no_of_threads)\n{\n    for (int i = no_of_threads; i < 2 * no_of_threads; i++) {\n        for (int j = 0; j < LOOP_NUM; j++) {\n            // Atomic addition\n            __sync_fetch_and_add(&atom_arr[0], 10);\n\n            // Atomic exchange\n            __sync_lock_test_and_set(&atom_arr[1], i);\n\n            // Atomic maximum\n            int old, expected;\n            do {\n                expected = atom_arr[2];\n                old      = __sync_val_compare_and_swap(&atom_arr[2], expected, max(expected, i));\n            } while (old != expected);\n\n            // Atomic minimum\n            do {\n                expected = atom_arr[3];\n                old      = __sync_val_compare_and_swap(&atom_arr[3], expected, min(expected, i));\n            } while (old != expected);\n\n            // Atomic increment (modulo 17+1)\n            int limit = 17;\n            do {\n                expected = atom_arr[4];\n                old      = __sync_val_compare_and_swap(&atom_arr[4], expected, (expected >= limit) ? 0 : expected + 1);\n            } while (old != expected);\n\n            // Atomic decrement\n            limit = 137;\n            do {\n                expected = atom_arr[5];\n                old      = __sync_val_compare_and_swap(\n                    &atom_arr[5], expected, ((expected == 0) || (expected > limit)) ? limit : expected - 1);\n            } while (old != expected);\n\n            // Atomic compare-and-swap\n            __sync_val_compare_and_swap(&atom_arr[6], i - 1, i);\n\n            // Bitwise atomic instructions\n\n            // Atomic AND\n            __sync_fetch_and_and(&atom_arr[7], 2 * i + 7);\n\n            // Atomic OR\n            __sync_fetch_and_or(&atom_arr[8], 1 << i);\n\n            // Atomic XOR\n            // 11th element should be 0xff\n            __sync_fetch_and_xor(&atom_arr[9], i);\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute reference data set\n//! Each element is multiplied with the number of threads / array length\n//! @param reference  reference data, computed but preallocated\n//! @param idata      input data as provided to device\n//! @param len        number of elements in reference / idata\n////////////////////////////////////////////////////////////////////////////////\nint verify(int *testData, const int len)\n{\n    int val = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val += 10;\n    }\n\n    if (val != testData[0]) {\n        printf(\"atomicAdd failed val = %d testData = %d\\n\", val, testData[0]);\n        return false;\n    }\n\n    val = 0;\n\n    bool found = false;\n\n    for (int i = 0; i < len; ++i) {\n        // second element should be a member of [0, len)\n        if (i == testData[1]) {\n            found = true;\n            break;\n        }\n    }\n\n    if (!found) {\n        printf(\"atomicExch failed\\n\");\n        return false;\n    }\n\n    val = -(1 << 8);\n\n    for (int i = 0; i < len; ++i) {\n        // third element should be len-1\n        val = max(val, i);\n    }\n\n    if (val != testData[2]) {\n        printf(\"atomicMax failed\\n\");\n        return false;\n    }\n\n    val = 1 << 8;\n\n    for (int i = 0; i < len; ++i) {\n        val = min(val, i);\n    }\n\n    if (val != testData[3]) {\n        printf(\"atomicMin failed\\n\");\n        return false;\n    }\n\n    int limit = 17;\n    val       = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val = (val >= limit) ? 0 : val + 1;\n    }\n\n    if (val != testData[4]) {\n        printf(\"atomicInc failed\\n\");\n        return false;\n    }\n\n    limit = 137;\n    val   = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val = ((val == 0) || (val > limit)) ? limit : val - 1;\n    }\n\n    if (val != testData[5]) {\n        printf(\"atomicDec failed\\n\");\n        return false;\n    }\n\n    found = false;\n\n    for (int i = 0; i < len; ++i) {\n        // seventh element should be a member of [0, len)\n        if (i == testData[6]) {\n            found = true;\n            break;\n        }\n    }\n\n    if (!found) {\n        printf(\"atomicCAS failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n\n    for (int i = 0; i < len; ++i) {\n        // 8th element should be 1\n        val &= (2 * i + 7);\n    }\n\n    if (val != testData[7]) {\n        printf(\"atomicAnd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n\n    for (int i = 0; i < len; ++i) {\n        // 9th element should be 0xff\n        val |= (1 << i);\n    }\n\n    if (val != testData[8]) {\n        printf(\"atomicOr failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n\n    for (int i = 0; i < len; ++i) {\n        // 11th element should be 0xff\n        val ^= i;\n    }\n\n    if (val != testData[9]) {\n        printf(\"atomicXor failed\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nint main(int argc, char **argv)\n{\n    // set device\n    cudaDeviceProp device_prop;\n    int            dev_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&device_prop, dev_id));\n\n    if (!device_prop.managedMemory) {\n        // This samples requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int computeMode;\n    checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, dev_id));\n    if (computeMode == cudaComputeModeProhibited) {\n        // This sample requires being run with a default or process exclusive mode\n        fprintf(stderr,\n                \"This sample requires a device in either default or process \"\n                \"exclusive mode\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int concurrentManagedAccess;\n    checkCudaErrors(cudaDeviceGetAttribute(&concurrentManagedAccess, cudaDevAttrConcurrentManagedAccess, dev_id));\n\n    if (device_prop.major < 6) {\n        printf(\"%s: requires a minimum CUDA compute 6.0 capability, waiving \"\n               \"testing.\\n\",\n               argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    unsigned int numThreads = 256;\n    unsigned int numBlocks  = 64;\n    unsigned int numData    = 10;\n\n    int *atom_arr;\n\n    if (device_prop.pageableMemoryAccess) {\n        printf(\"CAN access pageable memory\\n\");\n        atom_arr = (int *)malloc(sizeof(int) * numData);\n    }\n    else {\n        printf(\"CANNOT access pageable memory\\n\");\n        checkCudaErrors(cudaMallocManaged(&atom_arr, sizeof(int) * numData));\n    }\n\n    for (unsigned int i = 0; i < numData; i++)\n        atom_arr[i] = 0;\n\n    // To make the AND and XOR tests generate something other than 0...\n    atom_arr[7] = atom_arr[9] = 0xff;\n\n    atomicKernel<<<numBlocks, numThreads>>>(atom_arr);\n\n    // Do device synchronize for devices that don't support concurrent managed access such as WSL.\n    if (!concurrentManagedAccess) {\n        checkCudaErrors(cudaDeviceSynchronize());\n    }\n\n    atomicKernel_CPU(atom_arr, numBlocks * numThreads);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Compute & verify reference solution\n    int testResult = verify(atom_arr, 2 * numThreads * numBlocks);\n\n    if (device_prop.pageableMemoryAccess) {\n        free(atom_arr);\n    }\n    else {\n        cudaFree(atom_arr);\n    }\n\n    printf(\"systemWideAtomics completed, returned %s \\n\", testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* A program demonstrating trivial use of system-wide atomics on migratable\n * memory.\n */\n\n#include <cstdio>\n#include <ctime>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <math.h>\n#include <stdint.h>\n\n#define min(a, b) (a) < (b) ? (a) : (b)\n#define max(a, b) (a) > (b) ? (a) : (b)\n\n#define LOOP_NUM 50\n\n__global__ void atomicKernel(int *atom_arr)\n{\n    unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < LOOP_NUM; i++) {\n        // Atomic addition\n        atomicAdd_system(&atom_arr[0], 10);\n\n        // Atomic exchange\n        atomicExch_system(&atom_arr[1], tid);\n\n        // Atomic maximum\n        atomicMax_system(&atom_arr[2], tid);\n\n        // Atomic minimum\n        atomicMin_system(&atom_arr[3], tid);\n\n        // Atomic increment (modulo 17+1)\n        atomicInc_system((unsigned int *)&atom_arr[4], 17);\n\n        // Atomic decrement\n        atomicDec_system((unsigned int *)&atom_arr[5], 137);\n\n        // Atomic compare-and-swap\n        atomicCAS_system(&atom_arr[6], tid - 1, tid);\n\n        // Bitwise atomic instructions\n\n        // Atomic AND\n        atomicAnd_system(&atom_arr[7], 2 * tid + 7);\n\n        // Atomic OR\n        atomicOr_system(&atom_arr[8], 1 << tid);\n\n        // Atomic XOR\n        atomicXor_system(&atom_arr[9], tid);\n    }\n}\n\nvoid atomicKernel_CPU(int *atom_arr, int no_of_threads)\n{\n    for (int i = no_of_threads; i < 2 * no_of_threads; i++) {\n        for (int j = 0; j < LOOP_NUM; j++) {\n            // Atomic addition\n            __sync_fetch_and_add(&atom_arr[0], 10);\n\n            // Atomic exchange\n            __sync_lock_test_and_set(&atom_arr[1], i);\n\n            // Atomic maximum\n            int old, expected;\n            do {\n                expected = atom_arr[2];\n                old      = __sync_val_compare_and_swap(&atom_arr[2], expected, max(expected, i));\n            } while (old != expected);\n\n            // Atomic minimum\n            do {\n                expected = atom_arr[3];\n                old      = __sync_val_compare_and_swap(&atom_arr[3], expected, min(expected, i));\n            } while (old != expected);\n\n            // Atomic increment (modulo 17+1)\n            int limit = 17;\n            do {\n                expected = atom_arr[4];\n                old      = __sync_val_compare_and_swap(&atom_arr[4], expected, (expected >= limit) ? 0 : expected + 1);\n            } while (old != expected);\n\n            // Atomic decrement\n            limit = 137;\n            do {\n                expected = atom_arr[5];\n                old      = __sync_val_compare_and_swap(\n                    &atom_arr[5], expected, ((expected == 0) || (expected > limit)) ? limit : expected - 1);\n            } while (old != expected);\n\n            // Atomic compare-and-swap\n            __sync_val_compare_and_swap(&atom_arr[6], i - 1, i);\n\n            // Bitwise atomic instructions\n\n            // Atomic AND\n            __sync_fetch_and_and(&atom_arr[7], 2 * i + 7);\n\n            // Atomic OR\n            __sync_fetch_and_or(&atom_arr[8], 1 << i);\n\n            // Atomic XOR\n            // 11th element should be 0xff\n            __sync_fetch_and_xor(&atom_arr[9], i);\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute reference data set\n//! Each element is multiplied with the number of threads / array length\n//! @param reference  reference data, computed but preallocated\n//! @param idata      input data as provided to device\n//! @param len        number of elements in reference / idata\n////////////////////////////////////////////////////////////////////////////////\nint verify(int *testData, const int len)\n{\n    int val = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val += 10;\n    }\n\n    if (val != testData[0]) {\n        printf(\"atomicAdd failed val = %d testData = %d\\n\", val, testData[0]);\n        return false;\n    }\n\n    val = 0;\n\n    bool found = false;\n\n    for (int i = 0; i < len; ++i) {\n        // second element should be a member of [0, len)\n        if (i == testData[1]) {\n            found = true;\n            break;\n        }\n    }\n\n    if (!found) {\n        printf(\"atomicExch failed\\n\");\n        return false;\n    }\n\n    val = -(1 << 8);\n\n    for (int i = 0; i < len; ++i) {\n        // third element should be len-1\n        val = max(val, i);\n    }\n\n    if (val != testData[2]) {\n        printf(\"atomicMax failed\\n\");\n        return false;\n    }\n\n    val = 1 << 8;\n\n    for (int i = 0; i < len; ++i) {\n        val = min(val, i);\n    }\n\n    if (val != testData[3]) {\n        printf(\"atomicMin failed\\n\");\n        return false;\n    }\n\n    int limit = 17;\n    val       = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val = (val >= limit) ? 0 : val + 1;\n    }\n\n    if (val != testData[4]) {\n        printf(\"atomicInc failed\\n\");\n        return false;\n    }\n\n    limit = 137;\n    val   = 0;\n\n    for (int i = 0; i < len * LOOP_NUM; ++i) {\n        val = ((val == 0) || (val > limit)) ? limit : val - 1;\n    }\n\n    if (val != testData[5]) {\n        printf(\"atomicDec failed\\n\");\n        return false;\n    }\n\n    found = false;\n\n    for (int i = 0; i < len; ++i) {\n        // seventh element should be a member of [0, len)\n        if (i == testData[6]) {\n            found = true;\n            break;\n        }\n    }\n\n    if (!found) {\n        printf(\"atomicCAS failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n\n    for (int i = 0; i < len; ++i) {\n        // 8th element should be 1\n        val &= (2 * i + 7);\n    }\n\n    if (val != testData[7]) {\n        printf(\"atomicAnd failed\\n\");\n        return false;\n    }\n\n    val = 0;\n\n    for (int i = 0; i < len; ++i) {\n        // 9th element should be 0xff\n        val |= (1 << i);\n    }\n\n    if (val != testData[8]) {\n        printf(\"atomicOr failed\\n\");\n        return false;\n    }\n\n    val = 0xff;\n\n    for (int i = 0; i < len; ++i) {\n        // 11th element should be 0xff\n        val ^= i;\n    }\n\n    if (val != testData[9]) {\n        printf(\"atomicXor failed\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nint main(int argc, char **argv)\n{\n    // set device\n    hipDeviceProp_t device_prop;\n    int            dev_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&device_prop, dev_id));\n\n    if (!device_prop.managedMemory) {\n        // This samples requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int computeMode;\n    checkCudaErrors(hipDeviceGetAttribute(&computeMode, hipDeviceAttributeComputeMode, dev_id));\n    if (computeMode == hipComputeModeProhibited) {\n        // This sample requires being run with a default or process exclusive mode\n        fprintf(stderr,\n                \"This sample requires a device in either default or process \"\n                \"exclusive mode\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int concurrentManagedAccess;\n    checkCudaErrors(hipDeviceGetAttribute(&concurrentManagedAccess, hipDeviceAttributeConcurrentManagedAccess, dev_id));\n\n    if (device_prop.major < 6) {\n        printf(\"%s: requires a minimum CUDA compute 6.0 capability, waiving \"\n               \"testing.\\n\",\n               argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    unsigned int numThreads = 256;\n    unsigned int numBlocks  = 64;\n    unsigned int numData    = 10;\n\n    int *atom_arr;\n\n    if (device_prop.pageableMemoryAccess) {\n        printf(\"CAN access pageable memory\\n\");\n        atom_arr = (int *)malloc(sizeof(int) * numData);\n    }\n    else {\n        printf(\"CANNOT access pageable memory\\n\");\n        checkCudaErrors(hipMallocManaged(&atom_arr, sizeof(int) * numData));\n    }\n\n    for (unsigned int i = 0; i < numData; i++)\n        atom_arr[i] = 0;\n\n    // To make the AND and XOR tests generate something other than 0...\n    atom_arr[7] = atom_arr[9] = 0xff;\n\n    atomicKernel<<<numBlocks, numThreads>>>(atom_arr);\n\n    // Do device synchronize for devices that don't support concurrent managed access such as WSL.\n    if (!concurrentManagedAccess) {\n        checkCudaErrors(hipDeviceSynchronize());\n    }\n\n    atomicKernel_CPU(atom_arr, numBlocks * numThreads);\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Compute & verify reference solution\n    int testResult = verify(atom_arr, 2 * numThreads * numBlocks);\n\n    if (device_prop.pageableMemoryAccess) {\n        free(atom_arr);\n    }\n    else {\n        hipFree(atom_arr);\n    }\n\n    printf(\"systemWideAtomics completed, returned %s \\n\", testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/systemWideAtomics/systemWideAtomics.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n * Host code.\n *\n * This sample implements matrix multiplication as described in Chapter 3\n * of the programming guide.\n * It has been written for clarity of exposition to illustrate various CUDA\n * programming principles, not with the goal of providing the most\n * performant generic kernel for matrix multiplication.\n *\n * See also:\n * V. Volkov and J. Demmel, \"Benchmarking GPUs to tune dense linear algebra,\"\n * in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),\n * Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.\n */\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\n\n#include <cooperative_groups.h>\n\ntemplate <int BLOCK_SIZE> __device__ void matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)\n{\n    // Handle to thread block group\n    cooperative_groups::thread_block cta = cooperative_groups::this_thread_block();\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        cooperative_groups::sync(cta);\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        cooperative_groups::sync(cta);\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nextern \"C\" __global__ void matrixMulCUDA_block16(float *C, float *A, float *B, int wA, int wB)\n{\n    matrixMulCUDA<16>(C, A, B, wA, wB);\n}\n\nextern \"C\" __global__ void matrixMulCUDA_block32(float *C, float *A, float *B, int wA, int wB)\n{\n    matrixMulCUDA<32>(C, A, B, wA, wB);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n * Host code.\n *\n * This sample implements matrix multiplication as described in Chapter 3\n * of the programming guide.\n * It has been written for clarity of exposition to illustrate various CUDA\n * programming principles, not with the goal of providing the most\n * performant generic kernel for matrix multiplication.\n *\n * See also:\n * V. Volkov and J. Demmel, \"Benchmarking GPUs to tune dense linear algebra,\"\n * in Proc. 2008 ACM/IEEE Conf. on Supercomputing (SC '08),\n * Piscataway, NJ: IEEE Press, 2008, pp. Art. 31:1-11.\n */\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\ntemplate <int BLOCK_SIZE> __device__ void matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)\n{\n    // Handle to thread block group\n    cooperative_groups::thread_block cta = cooperative_groups::this_thread_block();\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        cooperative_groups::sync(cta);\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        cooperative_groups::sync(cta);\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nextern \"C\" __global__ void matrixMulCUDA_block16(float *C, float *A, float *B, int wA, int wB)\n{\n    matrixMulCUDA<16>(C, A, B, wA, wB);\n}\n\nextern \"C\" __global__ void matrixMulCUDA_block32(float *C, float *A, float *B, int wA, int wB)\n{\n    matrixMulCUDA<32>(C, A, B, wA, wB);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/matrixMul_nvrtc/matrixMul_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This example shows how to use the clock function to measure the performance\n * of block of threads of a kernel accurately. Blocks are executed in parallel\n * and out of order. Since there's no synchronization mechanism between blocks,\n * we measure the clock once for each block. The clock samples are written to\n * device memory.\n */\n\n// System includes\n#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// This kernel computes a standard parallel reduction and evaluates the\n// time it takes to do that for each block. The timing results are stored\n// in device memory.\n__global__ static void timedReduction(const float *input, float *output, clock_t *timer)\n{\n    // __shared__ float shared[2 * blockDim.x];\n    extern __shared__ float shared[];\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    if (tid == 0)\n        timer[bid] = clock();\n\n    // Copy input.\n    shared[tid]              = input[tid];\n    shared[tid + blockDim.x] = input[tid + blockDim.x];\n\n    // Perform reduction to find minimum.\n    for (int d = blockDim.x; d > 0; d /= 2) {\n        __syncthreads();\n\n        if (tid < d) {\n            float f0 = shared[tid];\n            float f1 = shared[tid + d];\n\n            if (f1 < f0) {\n                shared[tid] = f1;\n            }\n        }\n    }\n\n    // Write result.\n    if (tid == 0)\n        output[bid] = shared[0];\n\n    __syncthreads();\n\n    if (tid == 0)\n        timer[bid + gridDim.x] = clock();\n}\n\n#define NUM_BLOCKS  64\n#define NUM_THREADS 256\n\n// It's interesting to change the number of blocks and the number of threads to\n// understand how to keep the hardware busy.\n//\n// Here are some numbers I get on my G80:\n//    blocks - clocks\n//    1 - 3096\n//    8 - 3232\n//    16 - 3364\n//    32 - 4615\n//    64 - 9981\n//\n// With less than 16 blocks some of the multiprocessors of the device are idle.\n// With more than 16 you are using all the multiprocessors, but there's only one\n// block per multiprocessor and that doesn't allow you to hide the latency of\n// the memory. With more than 32 the speed scales linearly.\n\n// Start the main CUDA Sample here\nint main(int argc, char **argv)\n{\n    printf(\"CUDA Clock sample\\n\");\n\n    // This will pick the best possible CUDA capable device\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    float   *dinput  = NULL;\n    float   *doutput = NULL;\n    clock_t *dtimer  = NULL;\n\n    clock_t timer[NUM_BLOCKS * 2];\n    float   input[NUM_THREADS * 2];\n\n    for (int i = 0; i < NUM_THREADS * 2; i++) {\n        input[i] = (float)i;\n    }\n\n    checkCudaErrors(cudaMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2));\n    checkCudaErrors(cudaMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS));\n    checkCudaErrors(cudaMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));\n\n    checkCudaErrors(cudaMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2, cudaMemcpyHostToDevice));\n\n    timedReduction<<<NUM_BLOCKS, NUM_THREADS, sizeof(float) * 2 * NUM_THREADS>>>(dinput, doutput, dtimer);\n\n    checkCudaErrors(cudaMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2, cudaMemcpyDeviceToHost));\n\n    checkCudaErrors(cudaFree(dinput));\n    checkCudaErrors(cudaFree(doutput));\n    checkCudaErrors(cudaFree(dtimer));\n\n    long double avgElapsedClocks = 0;\n\n    for (int i = 0; i < NUM_BLOCKS; i++) {\n        avgElapsedClocks += (long double)(timer[i + NUM_BLOCKS] - timer[i]);\n    }\n\n    avgElapsedClocks = avgElapsedClocks / NUM_BLOCKS;\n    printf(\"Average clocks/block = %Lf\\n\", avgElapsedClocks);\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This example shows how to use the clock function to measure the performance\n * of block of threads of a kernel accurately. Blocks are executed in parallel\n * and out of order. Since there's no synchronization mechanism between blocks,\n * we measure the clock once for each block. The clock samples are written to\n * device memory.\n */\n\n// System includes\n#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// This kernel computes a standard parallel reduction and evaluates the\n// time it takes to do that for each block. The timing results are stored\n// in device memory.\n__global__ static void timedReduction(const float *input, float *output, clock_t *timer)\n{\n    // __shared__ float shared[2 * blockDim.x];\n    extern __shared__ float shared[];\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    if (tid == 0)\n        timer[bid] = clock();\n\n    // Copy input.\n    shared[tid]              = input[tid];\n    shared[tid + blockDim.x] = input[tid + blockDim.x];\n\n    // Perform reduction to find minimum.\n    for (int d = blockDim.x; d > 0; d /= 2) {\n        __syncthreads();\n\n        if (tid < d) {\n            float f0 = shared[tid];\n            float f1 = shared[tid + d];\n\n            if (f1 < f0) {\n                shared[tid] = f1;\n            }\n        }\n    }\n\n    // Write result.\n    if (tid == 0)\n        output[bid] = shared[0];\n\n    __syncthreads();\n\n    if (tid == 0)\n        timer[bid + gridDim.x] = clock();\n}\n\n#define NUM_BLOCKS  64\n#define NUM_THREADS 256\n\n// It's interesting to change the number of blocks and the number of threads to\n// understand how to keep the hardware busy.\n//\n// Here are some numbers I get on my G80:\n//    blocks - clocks\n//    1 - 3096\n//    8 - 3232\n//    16 - 3364\n//    32 - 4615\n//    64 - 9981\n//\n// With less than 16 blocks some of the multiprocessors of the device are idle.\n// With more than 16 you are using all the multiprocessors, but there's only one\n// block per multiprocessor and that doesn't allow you to hide the latency of\n// the memory. With more than 32 the speed scales linearly.\n\n// Start the main CUDA Sample here\nint main(int argc, char **argv)\n{\n    printf(\"CUDA Clock sample\\n\");\n\n    // This will pick the best possible CUDA capable device\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    float   *dinput  = NULL;\n    float   *doutput = NULL;\n    clock_t *dtimer  = NULL;\n\n    clock_t timer[NUM_BLOCKS * 2];\n    float   input[NUM_THREADS * 2];\n\n    for (int i = 0; i < NUM_THREADS * 2; i++) {\n        input[i] = (float)i;\n    }\n\n    checkCudaErrors(hipMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2));\n    checkCudaErrors(hipMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS));\n    checkCudaErrors(hipMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));\n\n    checkCudaErrors(hipMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2, hipMemcpyHostToDevice));\n\n    timedReduction<<<NUM_BLOCKS, NUM_THREADS, sizeof(float) * 2 * NUM_THREADS>>>(dinput, doutput, dtimer);\n\n    checkCudaErrors(hipMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2, hipMemcpyDeviceToHost));\n\n    checkCudaErrors(hipFree(dinput));\n    checkCudaErrors(hipFree(doutput));\n    checkCudaErrors(hipFree(dtimer));\n\n    long double avgElapsedClocks = 0;\n\n    for (int i = 0; i < NUM_BLOCKS; i++) {\n        avgElapsedClocks += (long double)(timer[i + NUM_BLOCKS] - timer[i]);\n    }\n\n    avgElapsedClocks = avgElapsedClocks / NUM_BLOCKS;\n    printf(\"Average clocks/block = %Lf\\n\", avgElapsedClocks);\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/clock/clock.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#else\n#include <sys/utsname.h>\n#endif\n\n// Includes, system\n#include <cassert>\n#include <stdio.h>\n\n// Includes CUDA\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n\nconst char *sampleName = \"simpleAssert\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Auto-Verification Code\nbool testResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Tests assert function.\n//! Thread whose id > N will print assertion failed error message.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void testKernel(int N)\n{\n    int gtid = blockIdx.x * blockDim.x + threadIdx.x;\n    assert(gtid < N);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nvoid runTest(int argc, char **argv)\n{\n    int         Nblocks  = 2;\n    int         Nthreads = 32;\n    cudaError_t error;\n\n#ifndef _WIN32\n    utsname OS_System_Type;\n    uname(&OS_System_Type);\n\n    printf(\"OS_System_Type.release = %s\\n\", OS_System_Type.release);\n\n    if (!strcasecmp(OS_System_Type.sysname, \"Darwin\")) {\n        printf(\"simpleAssert is not current supported on Mac OSX\\n\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    else {\n        printf(\"OS Info: <%s>\\n\\n\", OS_System_Type.version);\n    }\n\n#endif\n\n    // This will pick the best possible CUDA capable device\n    findCudaDevice(argc, (const char **)argv);\n\n    // Kernel configuration, where a one-dimensional\n    // grid and one-dimensional blocks are configured.\n    dim3 dimGrid(Nblocks);\n    dim3 dimBlock(Nthreads);\n\n    printf(\"Launch kernel to generate assertion failures\\n\");\n    testKernel<<<dimGrid, dimBlock>>>(60);\n\n    // Synchronize (flushes assert output).\n    printf(\"\\n-- Begin assert output\\n\\n\");\n    error = cudaDeviceSynchronize();\n    printf(\"\\n-- End assert output\\n\\n\");\n\n    // Check for errors and failed asserts in asynchronous kernel launch.\n    if (error == cudaErrorAssert) {\n        printf(\"Device assert failed as expected, \"\n               \"CUDA error message is: %s\\n\\n\",\n               cudaGetErrorString(error));\n    }\n\n    testResult = error == cudaErrorAssert;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#else\n#include <sys/utsname.h>\n#endif\n\n// Includes, system\n#include <cassert>\n#include <stdio.h>\n\n// Includes CUDA\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n\nconst char *sampleName = \"simpleAssert\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Auto-Verification Code\nbool testResult = true;\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernels\n////////////////////////////////////////////////////////////////////////////////\n//! Tests assert function.\n//! Thread whose id > N will print assertion failed error message.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void testKernel(int N)\n{\n    int gtid = blockIdx.x * blockDim.x + threadIdx.x;\n    assert(gtid < N);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Declaration, forward\nvoid runTest(int argc, char **argv);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", sampleName);\n\n    runTest(argc, argv);\n\n    printf(\"%s completed, returned %s\\n\", sampleName, testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nvoid runTest(int argc, char **argv)\n{\n    int         Nblocks  = 2;\n    int         Nthreads = 32;\n    hipError_t error;\n\n#ifndef _WIN32\n    utsname OS_System_Type;\n    uname(&OS_System_Type);\n\n    printf(\"OS_System_Type.release = %s\\n\", OS_System_Type.release);\n\n    if (!strcasecmp(OS_System_Type.sysname, \"Darwin\")) {\n        printf(\"simpleAssert is not current supported on Mac OSX\\n\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    else {\n        printf(\"OS Info: <%s>\\n\\n\", OS_System_Type.version);\n    }\n\n#endif\n\n    // This will pick the best possible CUDA capable device\n    findCudaDevice(argc, (const char **)argv);\n\n    // Kernel configuration, where a one-dimensional\n    // grid and one-dimensional blocks are configured.\n    dim3 dimGrid(Nblocks);\n    dim3 dimBlock(Nthreads);\n\n    printf(\"Launch kernel to generate assertion failures\\n\");\n    testKernel<<<dimGrid, dimBlock>>>(60);\n\n    // Synchronize (flushes assert output).\n    printf(\"\\n-- Begin assert output\\n\\n\");\n    error = hipDeviceSynchronize();\n    printf(\"\\n-- End assert output\\n\\n\");\n\n    // Check for errors and failed asserts in asynchronous kernel launch.\n    if (error == hipErrorAssert) {\n        printf(\"Device assert failed as expected, \"\n               \"CUDA error message is: %s\\n\\n\",\n               hipGetErrorString(error));\n    }\n\n    testResult = error == hipErrorAssert;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleAssert/simpleAssert.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements multi-threaded heterogeneous computing workloads with\n * the new CPU callbacks for CUDA streams and events introduced with CUDA 5.0.\n * Together with the thread safety of the CUDA API implementing heterogeneous\n * workloads that float between CPU threads and GPUs has become simple and\n * efficient.\n *\n * The workloads in the sample follow the form CPU preprocess -> GPU process ->\n * CPU postprocess.\n * Each CPU processing step is handled by its own dedicated thread. GPU\n * workloads are sent to all available GPUs in the system.\n *\n */\n\n// System includes\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#include \"multithreading.h\"\n\nconst int N_workloads             = 8;\nconst int N_elements_per_workload = 100000;\n\nCUTBarrier thread_barrier;\n\nvoid CUDART_CB myStreamCallback(cudaStream_t event, cudaError_t status, void *data);\n\nstruct heterogeneous_workload\n{\n    int id;\n    int cudaDeviceID;\n\n    int         *h_data;\n    int         *d_data;\n    cudaStream_t stream;\n\n    bool success;\n};\n\n__global__ void incKernel(int *data, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n        data[i]++;\n}\n\nCUT_THREADPROC launch(void *void_arg)\n{\n    heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;\n\n    // Select GPU for this CPU thread\n    checkCudaErrors(cudaSetDevice(workload->cudaDeviceID));\n\n    // Allocate Resources\n    checkCudaErrors(cudaStreamCreate(&workload->stream));\n    checkCudaErrors(cudaMalloc(&workload->d_data, N_elements_per_workload * sizeof(int)));\n    checkCudaErrors(cudaHostAlloc(&workload->h_data, N_elements_per_workload * sizeof(int), cudaHostAllocPortable));\n\n    // CPU thread generates data\n    for (int i = 0; i < N_elements_per_workload; ++i) {\n        workload->h_data[i] = workload->id + i;\n    }\n\n    // Schedule work for GPU in CUDA stream without blocking the CPU thread\n    // Note: Dedicated streams enable concurrent execution of workloads on the GPU\n    dim3 block(512);\n    dim3 grid((N_elements_per_workload + block.x - 1) / block.x);\n\n    checkCudaErrors(cudaMemcpyAsync(workload->d_data,\n                                    workload->h_data,\n                                    N_elements_per_workload * sizeof(int),\n                                    cudaMemcpyHostToDevice,\n                                    workload->stream));\n    incKernel<<<grid, block, 0, workload->stream>>>(workload->d_data, N_elements_per_workload);\n    checkCudaErrors(cudaMemcpyAsync(workload->h_data,\n                                    workload->d_data,\n                                    N_elements_per_workload * sizeof(int),\n                                    cudaMemcpyDeviceToHost,\n                                    workload->stream));\n\n    // New in CUDA 5.0: Add a CPU callback which is called once all currently\n    // pending operations in the CUDA stream have finished\n    checkCudaErrors(cudaStreamAddCallback(workload->stream, myStreamCallback, workload, 0));\n\n    CUT_THREADEND;\n    // CPU thread end of life, GPU continues to process data...\n}\n\nCUT_THREADPROC postprocess(void *void_arg)\n{\n    heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;\n    // ... GPU is done with processing, continue on new CPU thread...\n\n    // Select GPU for this CPU thread\n    checkCudaErrors(cudaSetDevice(workload->cudaDeviceID));\n\n    // CPU thread consumes results from GPU\n    workload->success = true;\n\n    for (int i = 0; i < N_workloads; ++i) {\n        workload->success &= workload->h_data[i] == i + workload->id + 1;\n    }\n\n    // Free Resources\n    checkCudaErrors(cudaFree(workload->d_data));\n    checkCudaErrors(cudaFreeHost(workload->h_data));\n    checkCudaErrors(cudaStreamDestroy(workload->stream));\n\n    // Signal the end of the heterogeneous workload to main thread\n    cutIncrementBarrier(&thread_barrier);\n\n    CUT_THREADEND;\n}\n\nvoid CUDART_CB myStreamCallback(cudaStream_t stream, cudaError_t status, void *data)\n{\n    // Check status of GPU after stream operations are done\n    checkCudaErrors(status);\n\n    // Spawn new CPU worker thread and continue processing on the CPU\n    cutStartThread(postprocess, data);\n}\n\nint main(int argc, char **argv)\n{\n    int N_gpus, max_gpus = 0;\n    int gpuInfo[32]; // assume a maximum of 32 GPUs in a system configuration\n\n    printf(\"Starting simpleCallback\\n\");\n\n    checkCudaErrors(cudaGetDeviceCount(&N_gpus));\n    printf(\"Found %d CUDA capable GPUs\\n\", N_gpus);\n\n    if (N_gpus > 32) {\n        printf(\"simpleCallback only supports 32 GPU(s)\\n\");\n    }\n\n    for (int devid = 0; devid < N_gpus; devid++) {\n        int            SMversion;\n        cudaDeviceProp deviceProp;\n        cudaSetDevice(devid);\n        cudaGetDeviceProperties(&deviceProp, devid);\n        SMversion = deviceProp.major << 4 + deviceProp.minor;\n        printf(\"GPU[%d] %s supports SM %d.%d\", devid, deviceProp.name, deviceProp.major, deviceProp.minor);\n        printf(\", %s GPU Callback Functions\\n\", (SMversion >= 0x11) ? \"capable\" : \"NOT capable\");\n\n        if (SMversion >= 0x11) {\n            gpuInfo[max_gpus++] = devid;\n        }\n    }\n\n    printf(\"%d GPUs available to run Callback Functions\\n\", max_gpus);\n\n    heterogeneous_workload *workloads;\n    workloads = (heterogeneous_workload *)malloc(N_workloads * sizeof(heterogeneous_workload));\n    ;\n    thread_barrier = cutCreateBarrier(N_workloads);\n\n    // Main thread spawns a CPU worker thread for each heterogeneous workload\n    printf(\"Starting %d heterogeneous computing workloads\\n\", N_workloads);\n\n    for (int i = 0; i < N_workloads; ++i) {\n        workloads[i].id           = i;\n        workloads[i].cudaDeviceID = gpuInfo[i % max_gpus]; // i % N_gpus;\n\n        cutStartThread(launch, &workloads[i]);\n    }\n\n    // Sleep until all workloads have finished\n    cutWaitForBarrier(&thread_barrier);\n    printf(\"Total of %d workloads finished:\\n\", N_workloads);\n\n    bool success = true;\n\n    for (int i = 0; i < N_workloads; ++i) {\n        success &= workloads[i].success;\n    }\n\n    printf(\"%s\\n\", success ? \"Success\" : \"Failure\");\n\n    free(workloads);\n\n    exit(success ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements multi-threaded heterogeneous computing workloads with\n * the new CPU callbacks for CUDA streams and events introduced with CUDA 5.0.\n * Together with the thread safety of the CUDA API implementing heterogeneous\n * workloads that float between CPU threads and GPUs has become simple and\n * efficient.\n *\n * The workloads in the sample follow the form CPU preprocess -> GPU process ->\n * CPU postprocess.\n * Each CPU processing step is handled by its own dedicated thread. GPU\n * workloads are sent to all available GPUs in the system.\n *\n */\n\n// System includes\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#include \"multithreading.h\"\n\nconst int N_workloads             = 8;\nconst int N_elements_per_workload = 100000;\n\nCUTBarrier thread_barrier;\n\nvoid CUDART_CB myStreamCallback(hipStream_t event, hipError_t status, void *data);\n\nstruct heterogeneous_workload\n{\n    int id;\n    int cudaDeviceID;\n\n    int         *h_data;\n    int         *d_data;\n    hipStream_t stream;\n\n    bool success;\n};\n\n__global__ void incKernel(int *data, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n        data[i]++;\n}\n\nCUT_THREADPROC launch(void *void_arg)\n{\n    heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;\n\n    // Select GPU for this CPU thread\n    checkCudaErrors(hipSetDevice(workload->cudaDeviceID));\n\n    // Allocate Resources\n    checkCudaErrors(hipStreamCreate(&workload->stream));\n    checkCudaErrors(hipMalloc(&workload->d_data, N_elements_per_workload * sizeof(int)));\n    checkCudaErrors(hipHostAlloc(&workload->h_data, N_elements_per_workload * sizeof(int), hipHostMallocPortable));\n\n    // CPU thread generates data\n    for (int i = 0; i < N_elements_per_workload; ++i) {\n        workload->h_data[i] = workload->id + i;\n    }\n\n    // Schedule work for GPU in CUDA stream without blocking the CPU thread\n    // Note: Dedicated streams enable concurrent execution of workloads on the GPU\n    dim3 block(512);\n    dim3 grid((N_elements_per_workload + block.x - 1) / block.x);\n\n    checkCudaErrors(hipMemcpyAsync(workload->d_data,\n                                    workload->h_data,\n                                    N_elements_per_workload * sizeof(int),\n                                    hipMemcpyHostToDevice,\n                                    workload->stream));\n    incKernel<<<grid, block, 0, workload->stream>>>(workload->d_data, N_elements_per_workload);\n    checkCudaErrors(hipMemcpyAsync(workload->h_data,\n                                    workload->d_data,\n                                    N_elements_per_workload * sizeof(int),\n                                    hipMemcpyDeviceToHost,\n                                    workload->stream));\n\n    // New in CUDA 5.0: Add a CPU callback which is called once all currently\n    // pending operations in the CUDA stream have finished\n    checkCudaErrors(hipStreamAddCallback(workload->stream, myStreamCallback, workload, 0));\n\n    CUT_THREADEND;\n    // CPU thread end of life, GPU continues to process data...\n}\n\nCUT_THREADPROC postprocess(void *void_arg)\n{\n    heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;\n    // ... GPU is done with processing, continue on new CPU thread...\n\n    // Select GPU for this CPU thread\n    checkCudaErrors(hipSetDevice(workload->cudaDeviceID));\n\n    // CPU thread consumes results from GPU\n    workload->success = true;\n\n    for (int i = 0; i < N_workloads; ++i) {\n        workload->success &= workload->h_data[i] == i + workload->id + 1;\n    }\n\n    // Free Resources\n    checkCudaErrors(hipFree(workload->d_data));\n    checkCudaErrors(hipHostFree(workload->h_data));\n    checkCudaErrors(hipStreamDestroy(workload->stream));\n\n    // Signal the end of the heterogeneous workload to main thread\n    cutIncrementBarrier(&thread_barrier);\n\n    CUT_THREADEND;\n}\n\nvoid CUDART_CB myStreamCallback(hipStream_t stream, hipError_t status, void *data)\n{\n    // Check status of GPU after stream operations are done\n    checkCudaErrors(status);\n\n    // Spawn new CPU worker thread and continue processing on the CPU\n    cutStartThread(postprocess, data);\n}\n\nint main(int argc, char **argv)\n{\n    int N_gpus, max_gpus = 0;\n    int gpuInfo[32]; // assume a maximum of 32 GPUs in a system configuration\n\n    printf(\"Starting simpleCallback\\n\");\n\n    checkCudaErrors(hipGetDeviceCount(&N_gpus));\n    printf(\"Found %d CUDA capable GPUs\\n\", N_gpus);\n\n    if (N_gpus > 32) {\n        printf(\"simpleCallback only supports 32 GPU(s)\\n\");\n    }\n\n    for (int devid = 0; devid < N_gpus; devid++) {\n        int            SMversion;\n        hipDeviceProp_t deviceProp;\n        hipSetDevice(devid);\n        hipGetDeviceProperties(&deviceProp, devid);\n        SMversion = deviceProp.major << 4 + deviceProp.minor;\n        printf(\"GPU[%d] %s supports SM %d.%d\", devid, deviceProp.name, deviceProp.major, deviceProp.minor);\n        printf(\", %s GPU Callback Functions\\n\", (SMversion >= 0x11) ? \"capable\" : \"NOT capable\");\n\n        if (SMversion >= 0x11) {\n            gpuInfo[max_gpus++] = devid;\n        }\n    }\n\n    printf(\"%d GPUs available to run Callback Functions\\n\", max_gpus);\n\n    heterogeneous_workload *workloads;\n    workloads = (heterogeneous_workload *)malloc(N_workloads * sizeof(heterogeneous_workload));\n    ;\n    thread_barrier = cutCreateBarrier(N_workloads);\n\n    // Main thread spawns a CPU worker thread for each heterogeneous workload\n    printf(\"Starting %d heterogeneous computing workloads\\n\", N_workloads);\n\n    for (int i = 0; i < N_workloads; ++i) {\n        workloads[i].id           = i;\n        workloads[i].cudaDeviceID = gpuInfo[i % max_gpus]; // i % N_gpus;\n\n        cutStartThread(launch, &workloads[i]);\n    }\n\n    // Sleep until all workloads have finished\n    cutWaitForBarrier(&thread_barrier);\n    printf(\"Total of %d workloads finished:\\n\", N_workloads);\n\n    bool success = true;\n\n    for (int i = 0; i < N_workloads; ++i) {\n        success &= workloads[i].success;\n    }\n\n    printf(\"%s\\n\", success ? \"Success\" : \"Failure\");\n\n    free(workloads);\n\n    exit(success ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleCallback/simpleCallback.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Includes, system\n#include <stdio.h>\n\n// Includes CUDA\n#include <cooperative_groups.h>\n#include <cuda/barrier>\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n\nnamespace cg = cooperative_groups;\n\n#if __CUDA_ARCH__ >= 700\ntemplate <bool writeSquareRoot>\n__device__ void reduceBlockData(cuda::barrier<cuda::thread_scope_block> &barrier,\n                                cg::thread_block_tile<32>               &tile32,\n                                double                                  &threadSum,\n                                double                                  *result)\n{\n    extern __shared__ double tmp[];\n\n#pragma unroll\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n        threadSum += tile32.shfl_down(threadSum, offset);\n    }\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = threadSum;\n    }\n\n    auto token = barrier.arrive();\n\n    barrier.wait(std::move(token));\n\n    // The warp 0 will perform last round of reduction\n    if (tile32.meta_group_rank() == 0) {\n        double beta = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n\n#pragma unroll\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            beta += tile32.shfl_down(beta, offset);\n        }\n\n        if (tile32.thread_rank() == 0) {\n            if (writeSquareRoot)\n                *result = sqrt(beta);\n            else\n                *result = beta;\n        }\n    }\n}\n#endif\n\n__global__ void normVecByDotProductAWBarrier(float *vecA, float *vecB, double *partialResults, int size)\n{\n#if __CUDA_ARCH__ >= 700\n#pragma diag_suppress static_var_with_dynamic_init\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n    ;\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    __shared__ cuda::barrier<cuda::thread_scope_block> barrier;\n\n    if (threadIdx.x == 0) {\n        init(&barrier, blockDim.x);\n    }\n\n    cg::sync(cta);\n\n    double threadSum = 0.0;\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        threadSum += (double)(vecA[i] * vecB[i]);\n    }\n\n    // Each thread block performs reduction of partial dotProducts and writes to\n    // global mem.\n    reduceBlockData<false>(barrier, tile32, threadSum, &partialResults[blockIdx.x]);\n\n    cg::sync(grid);\n\n    // One block performs the final summation of partial dot products\n    // of all the thread blocks and writes the sqrt of final dot product.\n    if (blockIdx.x == 0) {\n        threadSum = 0.0;\n        for (int i = cta.thread_rank(); i < gridDim.x; i += cta.size()) {\n            threadSum += partialResults[i];\n        }\n        reduceBlockData<true>(barrier, tile32, threadSum, &partialResults[0]);\n    }\n\n    cg::sync(grid);\n\n    const double finalValue = partialResults[0];\n\n    // Perform normalization of vecA & vecB.\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        vecA[i] = (float)vecA[i] / finalValue;\n        vecB[i] = (float)vecB[i] / finalValue;\n    }\n#endif\n}\n\nint runNormVecByDotProductAWBarrier(int argc, char **argv, int deviceId);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", argv[0]);\n\n    // This will pick the best possible CUDA capable device\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int major = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev));\n\n    // Arrive-Wait Barrier require a GPU of Volta (SM7X) architecture or higher.\n    if (major < 7) {\n        printf(\"simpleAWBarrier requires SM 7.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int supportsCooperativeLaunch = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&supportsCooperativeLaunch, cudaDevAttrCooperativeLaunch, dev));\n\n    if (!supportsCooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               dev);\n        exit(EXIT_WAIVED);\n    }\n\n    int testResult = runNormVecByDotProductAWBarrier(argc, argv, dev);\n\n    printf(\"%s completed, returned %s\\n\", argv[0], testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nint runNormVecByDotProductAWBarrier(int argc, char **argv, int deviceId)\n{\n    float  *vecA, *d_vecA;\n    float  *vecB, *d_vecB;\n    double *d_partialResults;\n    int     size = 10000000;\n\n    checkCudaErrors(cudaMallocHost(&vecA, sizeof(float) * size));\n    checkCudaErrors(cudaMallocHost(&vecB, sizeof(float) * size));\n\n    checkCudaErrors(cudaMalloc(&d_vecA, sizeof(float) * size));\n    checkCudaErrors(cudaMalloc(&d_vecB, sizeof(float) * size));\n\n    float baseVal = 2.0;\n    for (int i = 0; i < size; i++) {\n        vecA[i] = vecB[i] = baseVal;\n    }\n\n    cudaStream_t stream;\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaMemcpyAsync(d_vecA, vecA, sizeof(float) * size, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemcpyAsync(d_vecB, vecB, sizeof(float) * size, cudaMemcpyHostToDevice, stream));\n\n    // Kernel configuration, where a one-dimensional\n    // grid and one-dimensional blocks are configured.\n    int minGridSize = 0, blockSize = 0;\n    checkCudaErrors(\n        cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)normVecByDotProductAWBarrier, 0, size));\n\n    int smemSize = ((blockSize / 32) + 1) * sizeof(double);\n\n    int numBlocksPerSm = 0;\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n        &numBlocksPerSm, normVecByDotProductAWBarrier, blockSize, smemSize));\n\n    int multiProcessorCount = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&multiProcessorCount, cudaDevAttrMultiProcessorCount, deviceId));\n\n    minGridSize = multiProcessorCount * numBlocksPerSm;\n    checkCudaErrors(cudaMalloc(&d_partialResults, minGridSize * sizeof(double)));\n\n    printf(\"Launching normVecByDotProductAWBarrier kernel with numBlocks = %d \"\n           \"blockSize = %d\\n\",\n           minGridSize,\n           blockSize);\n\n    dim3 dimGrid(minGridSize, 1, 1), dimBlock(blockSize, 1, 1);\n\n    void *kernelArgs[] = {(void *)&d_vecA, (void *)&d_vecB, (void *)&d_partialResults, (void *)&size};\n\n    checkCudaErrors(cudaLaunchCooperativeKernel(\n        (void *)normVecByDotProductAWBarrier, dimGrid, dimBlock, kernelArgs, smemSize, stream));\n\n    checkCudaErrors(cudaMemcpyAsync(vecA, d_vecA, sizeof(float) * size, cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    float        expectedResult = (baseVal / sqrt(size * baseVal * baseVal));\n    unsigned int matches        = 0;\n    for (int i = 0; i < size; i++) {\n        if ((vecA[i] - expectedResult) > 0.00001) {\n            printf(\"mismatch at i = %d\\n\", i);\n            break;\n        }\n        else {\n            matches++;\n        }\n    }\n\n    printf(\"Result = %s\\n\", matches == size ? \"PASSED\" : \"FAILED\");\n    checkCudaErrors(cudaFree(d_vecA));\n    checkCudaErrors(cudaFree(d_vecB));\n    checkCudaErrors(cudaFree(d_partialResults));\n\n    checkCudaErrors(cudaFreeHost(vecA));\n    checkCudaErrors(cudaFreeHost(vecB));\n    return matches == size;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Includes, system\n#include <stdio.h>\n\n// Includes CUDA\n#include <hip/hip_cooperative_groups.h>\n#include <cuda/barrier>\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n\nnamespace cg = cooperative_groups;\n\n#if __CUDA_ARCH__ >= 700\ntemplate <bool writeSquareRoot>\n__device__ void reduceBlockData(cuda::barrier<cuda::thread_scope_block> &barrier,\n                                cg::thread_block_tile<32>               &tile32,\n                                double                                  &threadSum,\n                                double                                  *result)\n{\n    extern __shared__ double tmp[];\n\n#pragma unroll\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n        threadSum += tile32.shfl_down(threadSum, offset);\n    }\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = threadSum;\n    }\n\n    auto token = barrier.arrive();\n\n    barrier.wait(std::move(token));\n\n    // The warp 0 will perform last round of reduction\n    if (tile32.meta_group_rank() == 0) {\n        double beta = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n\n#pragma unroll\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            beta += tile32.shfl_down(beta, offset);\n        }\n\n        if (tile32.thread_rank() == 0) {\n            if (writeSquareRoot)\n                *result = sqrt(beta);\n            else\n                *result = beta;\n        }\n    }\n}\n#endif\n\n__global__ void normVecByDotProductAWBarrier(float *vecA, float *vecB, double *partialResults, int size)\n{\n#if __CUDA_ARCH__ >= 700\n#pragma diag_suppress static_var_with_dynamic_init\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n    ;\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    __shared__ cuda::barrier<cuda::thread_scope_block> barrier;\n\n    if (threadIdx.x == 0) {\n        init(&barrier, blockDim.x);\n    }\n\n    cg::sync(cta);\n\n    double threadSum = 0.0;\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        threadSum += (double)(vecA[i] * vecB[i]);\n    }\n\n    // Each thread block performs reduction of partial dotProducts and writes to\n    // global mem.\n    reduceBlockData<false>(barrier, tile32, threadSum, &partialResults[blockIdx.x]);\n\n    cg::sync(grid);\n\n    // One block performs the final summation of partial dot products\n    // of all the thread blocks and writes the sqrt of final dot product.\n    if (blockIdx.x == 0) {\n        threadSum = 0.0;\n        for (int i = cta.thread_rank(); i < gridDim.x; i += cta.size()) {\n            threadSum += partialResults[i];\n        }\n        reduceBlockData<true>(barrier, tile32, threadSum, &partialResults[0]);\n    }\n\n    cg::sync(grid);\n\n    const double finalValue = partialResults[0];\n\n    // Perform normalization of vecA & vecB.\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        vecA[i] = (float)vecA[i] / finalValue;\n        vecB[i] = (float)vecB[i] / finalValue;\n    }\n#endif\n}\n\nint runNormVecByDotProductAWBarrier(int argc, char **argv, int deviceId);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s starting...\\n\", argv[0]);\n\n    // This will pick the best possible CUDA capable device\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int major = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, dev));\n\n    // Arrive-Wait Barrier require a GPU of Volta (SM7X) architecture or higher.\n    if (major < 7) {\n        printf(\"simpleAWBarrier requires SM 7.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int supportsCooperativeLaunch = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&supportsCooperativeLaunch, hipDeviceAttributeCooperativeLaunch, dev));\n\n    if (!supportsCooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               dev);\n        exit(EXIT_WAIVED);\n    }\n\n    int testResult = runNormVecByDotProductAWBarrier(argc, argv, dev);\n\n    printf(\"%s completed, returned %s\\n\", argv[0], testResult ? \"OK\" : \"ERROR!\");\n    exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nint runNormVecByDotProductAWBarrier(int argc, char **argv, int deviceId)\n{\n    float  *vecA, *d_vecA;\n    float  *vecB, *d_vecB;\n    double *d_partialResults;\n    int     size = 10000000;\n\n    checkCudaErrors(hipHostMalloc(&vecA, sizeof(float) * size));\n    checkCudaErrors(hipHostMalloc(&vecB, sizeof(float) * size));\n\n    checkCudaErrors(hipMalloc(&d_vecA, sizeof(float) * size));\n    checkCudaErrors(hipMalloc(&d_vecB, sizeof(float) * size));\n\n    float baseVal = 2.0;\n    for (int i = 0; i < size; i++) {\n        vecA[i] = vecB[i] = baseVal;\n    }\n\n    hipStream_t stream;\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    checkCudaErrors(hipMemcpyAsync(d_vecA, vecA, sizeof(float) * size, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemcpyAsync(d_vecB, vecB, sizeof(float) * size, hipMemcpyHostToDevice, stream));\n\n    // Kernel configuration, where a one-dimensional\n    // grid and one-dimensional blocks are configured.\n    int minGridSize = 0, blockSize = 0;\n    checkCudaErrors(\n        hipOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)normVecByDotProductAWBarrier, 0, size));\n\n    int smemSize = ((blockSize / 32) + 1) * sizeof(double);\n\n    int numBlocksPerSm = 0;\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(\n        &numBlocksPerSm, normVecByDotProductAWBarrier, blockSize, smemSize));\n\n    int multiProcessorCount = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&multiProcessorCount, hipDeviceAttributeMultiprocessorCount, deviceId));\n\n    minGridSize = multiProcessorCount * numBlocksPerSm;\n    checkCudaErrors(hipMalloc(&d_partialResults, minGridSize * sizeof(double)));\n\n    printf(\"Launching normVecByDotProductAWBarrier kernel with numBlocks = %d \"\n           \"blockSize = %d\\n\",\n           minGridSize,\n           blockSize);\n\n    dim3 dimGrid(minGridSize, 1, 1), dimBlock(blockSize, 1, 1);\n\n    void *kernelArgs[] = {(void *)&d_vecA, (void *)&d_vecB, (void *)&d_partialResults, (void *)&size};\n\n    checkCudaErrors(hipLaunchCooperativeKernel(reinterpret_cast<const void*>((void *)normVecByDotProductAWBarrier), dimGrid, dimBlock, kernelArgs, smemSize, stream));\n\n    checkCudaErrors(hipMemcpyAsync(vecA, d_vecA, sizeof(float) * size, hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    float        expectedResult = (baseVal / sqrt(size * baseVal * baseVal));\n    unsigned int matches        = 0;\n    for (int i = 0; i < size; i++) {\n        if ((vecA[i] - expectedResult) > 0.00001) {\n            printf(\"mismatch at i = %d\\n\", i);\n            break;\n        }\n        else {\n            matches++;\n        }\n    }\n\n    printf(\"Result = %s\\n\", matches == size ? \"PASSED\" : \"FAILED\");\n    checkCudaErrors(hipFree(d_vecA));\n    checkCudaErrors(hipFree(d_vecB));\n    checkCudaErrors(hipFree(d_partialResults));\n\n    checkCudaErrors(hipHostFree(vecA));\n    checkCudaErrors(hipHostFree(vecB));\n    return matches == size;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleAWBarrier/simpleAWBarrier.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 3\n * of the programming guide with some additions like error checking.\n *\n */\n\n// Device code\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void VecAdd_kernel(const float *A, const float *B, float *C, int N)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < N)\n        C[i] = A[i] + B[i];\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/vectorAddMMAP/vectorAdd_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This example shows how to use the clock function to measure the performance\n * of block of threads of a kernel accurately. Blocks are executed in parallel\n * and out of order. Since there's no synchronization mechanism between blocks,\n * we measure the clock once for each block. The clock samples are written to\n * device memory.\n */\n\n// This kernel computes a standard parallel reduction and evaluates the\n// time it takes to do that for each block. The timing results are stored\n// in device memory.\n\nextern \"C\" __global__ void timedReduction(const float *input, float *output, clock_t *timer)\n{\n    // __shared__ float shared[2 * blockDim.x];\n    extern __shared__ float shared[];\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    if (tid == 0)\n        timer[bid] = clock();\n\n    // Copy input.\n    shared[tid]              = input[tid];\n    shared[tid + blockDim.x] = input[tid + blockDim.x];\n\n    // Perform reduction to find minimum.\n    for (int d = blockDim.x; d > 0; d /= 2) {\n        __syncthreads();\n\n        if (tid < d) {\n            float f0 = shared[tid];\n            float f1 = shared[tid + d];\n\n            if (f1 < f0) {\n                shared[tid] = f1;\n            }\n        }\n    }\n\n    // Write result.\n    if (tid == 0)\n        output[bid] = shared[0];\n\n    __syncthreads();\n\n    if (tid == 0)\n        timer[bid + gridDim.x] = clock();\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This example shows how to use the clock function to measure the performance\n * of block of threads of a kernel accurately. Blocks are executed in parallel\n * and out of order. Since there's no synchronization mechanism between blocks,\n * we measure the clock once for each block. The clock samples are written to\n * device memory.\n */\n\n// This kernel computes a standard parallel reduction and evaluates the\n// time it takes to do that for each block. The timing results are stored\n// in device memory.\n\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void timedReduction(const float *input, float *output, clock_t *timer)\n{\n    // __shared__ float shared[2 * blockDim.x];\n    extern __shared__ float shared[];\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    if (tid == 0)\n        timer[bid] = clock();\n\n    // Copy input.\n    shared[tid]              = input[tid];\n    shared[tid + blockDim.x] = input[tid + blockDim.x];\n\n    // Perform reduction to find minimum.\n    for (int d = blockDim.x; d > 0; d /= 2) {\n        __syncthreads();\n\n        if (tid < d) {\n            float f0 = shared[tid];\n            float f1 = shared[tid + d];\n\n            if (f1 < f0) {\n                shared[tid] = f1;\n            }\n        }\n    }\n\n    // Write result.\n    if (tid == 0)\n        output[bid] = shared[0];\n\n    __syncthreads();\n\n    if (tid == 0)\n        timer[bid + gridDim.x] = clock();\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/clock_nvrtc/clock_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n\nextern \"C\" __global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numElements) {\n        C[i] = A[i] + B[i];\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numElements) {\n        C[i] = A[i] + B[i];\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/vectorAdd_nvrtc/vectorAdd_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how to use texture fetches from layered 2D textures\n * in CUDA C\n *\n * This sample first generates a 3D input data array for the layered texture\n * and the expected output. Then it starts CUDA C kernels, one for each layer,\n * which fetch their layer's texture data (using normalized texture coordinates)\n * transform it to the expected output, and write it to a 3D output data array.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, kernels\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\nstatic const char *sSDKname = \"simpleLayeredTexture\";\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform a layer of a layered 2D texture using texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *g_odata, int width, int height, int layer, cudaTextureObject_t tex)\n{\n    // calculate this thread's data point\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // 0.5f offset and division are necessary to access the original data points\n    // in the texture (such that bilinear interpolation will not be activated).\n    // For details, see also CUDA Programming Guide, Appendix D\n    float u = (x + 0.5f) / (float)width;\n    float v = (y + 0.5f) / (float)height;\n\n    // read from texture, do expected transformation and write to global memory\n    g_odata[layer * width * height + y * width + x] = -tex2DLayered<float>(tex, u, v, layer) + layer;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"[%s] - Starting...\\n\", sSDKname);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    bool bResult = true;\n\n    // get number of SMs on this GPU\n    cudaDeviceProp deviceProps;\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors \", deviceProps.name, deviceProps.multiProcessorCount);\n    printf(\"SM %d.%d\\n\", deviceProps.major, deviceProps.minor);\n\n    // generate input data for layered texture\n    unsigned int width = 512, height = 512, num_layers = 5;\n    unsigned int size   = width * height * num_layers * sizeof(float);\n    float       *h_data = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        for (int i = 0; i < (int)(width * height); i++) {\n            h_data[layer * width * height + i] = (float)i;\n        }\n\n    // this is the expected transformation of the input data (the expected output)\n    float *h_data_ref = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        for (int i = 0; i < (int)(width * height); i++) {\n            h_data_ref[layer * width * height + i] = -h_data[layer * width * height + i] + layer;\n        }\n\n    // allocate device memory for result\n    float *d_data = NULL;\n    checkCudaErrors(cudaMalloc((void **)&d_data, size));\n\n    // allocate array and copy image data\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    cudaArray            *cu_3darray;\n    checkCudaErrors(\n        cudaMalloc3DArray(&cu_3darray, &channelDesc, make_cudaExtent(width, height, num_layers), cudaArrayLayered));\n    cudaMemcpy3DParms myparms = {0};\n    myparms.srcPos            = make_cudaPos(0, 0, 0);\n    myparms.dstPos            = make_cudaPos(0, 0, 0);\n    myparms.srcPtr            = make_cudaPitchedPtr(h_data, width * sizeof(float), width, height);\n    myparms.dstArray          = cu_3darray;\n    myparms.extent            = make_cudaExtent(width, height, num_layers);\n    myparms.kind              = cudaMemcpyHostToDevice;\n    checkCudaErrors(cudaMemcpy3D(&myparms));\n\n    cudaTextureObject_t tex;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = cu_3darray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    printf(\"Covering 2D data array of %d x %d: Grid size is %d x %d, each block has \"\n           \"8 x 8 threads\\n\",\n           width,\n           height,\n           dimGrid.x,\n           dimGrid.y);\n\n    transformKernel<<<dimGrid, dimBlock>>>(d_data, width, height, 0,\n                                           tex); // warmup (for better timing)\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"warmup Kernel execution failed\");\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // execute the kernel\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        transformKernel<<<dimGrid, dimBlock, 0>>>(d_data, width, height, layer, tex);\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %.3f msec\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mtexlookups/sec\\n\", (width * height * num_layers / (sdkGetTimerValue(&timer) / 1000.0f) / 1e6));\n    sdkDeleteTimer(&timer);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(h_odata, d_data, size, cudaMemcpyDeviceToHost));\n\n    // write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", h_odata, width * height, 0.0f, false);\n    }\n    else {\n        printf(\"Comparing kernel output to expected data\\n\");\n\n#define MIN_EPSILON_ERROR 5e-3f\n        bResult = compareData(h_odata, h_data_ref, width * height * num_layers, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_data);\n    free(h_data_ref);\n    free(h_odata);\n\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n    checkCudaErrors(cudaFree(d_data));\n    checkCudaErrors(cudaFreeArray(cu_3darray));\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how to use texture fetches from layered 2D textures\n * in CUDA C\n *\n * This sample first generates a 3D input data array for the layered texture\n * and the expected output. Then it starts CUDA C kernels, one for each layer,\n * which fetch their layer's texture data (using normalized texture coordinates)\n * transform it to the expected output, and write it to a 3D output data array.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, kernels\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\nstatic const char *sSDKname = \"simpleLayeredTexture\";\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform a layer of a layered 2D texture using texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *g_odata, int width, int height, int layer, hipTextureObject_t tex)\n{\n    // calculate this thread's data point\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // 0.5f offset and division are necessary to access the original data points\n    // in the texture (such that bilinear interpolation will not be activated).\n    // For details, see also CUDA Programming Guide, Appendix D\n    float u = (x + 0.5f) / (float)width;\n    float v = (y + 0.5f) / (float)height;\n\n    // read from texture, do expected transformation and write to global memory\n    g_odata[layer * width * height + y * width + x] = -tex2DLayered<float>(tex, u, v, layer) + layer;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"[%s] - Starting...\\n\", sSDKname);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    bool bResult = true;\n\n    // get number of SMs on this GPU\n    hipDeviceProp_t deviceProps;\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors \", deviceProps.name, deviceProps.multiProcessorCount);\n    printf(\"SM %d.%d\\n\", deviceProps.major, deviceProps.minor);\n\n    // generate input data for layered texture\n    unsigned int width = 512, height = 512, num_layers = 5;\n    unsigned int size   = width * height * num_layers * sizeof(float);\n    float       *h_data = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        for (int i = 0; i < (int)(width * height); i++) {\n            h_data[layer * width * height + i] = (float)i;\n        }\n\n    // this is the expected transformation of the input data (the expected output)\n    float *h_data_ref = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        for (int i = 0; i < (int)(width * height); i++) {\n            h_data_ref[layer * width * height + i] = -h_data[layer * width * height + i] + layer;\n        }\n\n    // allocate device memory for result\n    float *d_data = NULL;\n    checkCudaErrors(hipMalloc((void **)&d_data, size));\n\n    // allocate array and copy image data\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    hipArray            *cu_3darray;\n    checkCudaErrors(\n        hipMalloc3DArray(&cu_3darray, &channelDesc, make_hipExtent(width, height, num_layers), hipArrayLayered));\n    hipMemcpy3DParms myparms = {0};\n    myparms.srcPos            = make_hipPos(0, 0, 0);\n    myparms.dstPos            = make_hipPos(0, 0, 0);\n    myparms.srcPtr            = make_hipPitchedPtr(h_data, width * sizeof(float), width, height);\n    myparms.dstArray          = cu_3darray;\n    myparms.extent            = make_hipExtent(width, height, num_layers);\n    myparms.kind              = hipMemcpyHostToDevice;\n    checkCudaErrors(hipMemcpy3D(&myparms));\n\n    hipTextureObject_t tex;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = cu_3darray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);\n\n    printf(\"Covering 2D data array of %d x %d: Grid size is %d x %d, each block has \"\n           \"8 x 8 threads\\n\",\n           width,\n           height,\n           dimGrid.x,\n           dimGrid.y);\n\n    transformKernel<<<dimGrid, dimBlock>>>(d_data, width, height, 0,\n                                           tex); // warmup (for better timing)\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"warmup Kernel execution failed\");\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // execute the kernel\n    for (unsigned int layer = 0; layer < num_layers; layer++)\n        transformKernel<<<dimGrid, dimBlock, 0>>>(d_data, width, height, layer, tex);\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %.3f msec\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mtexlookups/sec\\n\", (width * height * num_layers / (sdkGetTimerValue(&timer) / 1000.0f) / 1e6));\n    sdkDeleteTimer(&timer);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(h_odata, d_data, size, hipMemcpyDeviceToHost));\n\n    // write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", h_odata, width * height, 0.0f, false);\n    }\n    else {\n        printf(\"Comparing kernel output to expected data\\n\");\n\n#define MIN_EPSILON_ERROR 5e-3f\n        bResult = compareData(h_odata, h_data_ref, width * height * num_layers, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_data);\n    free(h_data_ref);\n    free(h_odata);\n\n    checkCudaErrors(hipDestroyTextureObject(tex));\n    checkCudaErrors(hipFree(d_data));\n    checkCudaErrors(hipFreeArray(cu_3darray));\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleLayeredTexture/simpleLayeredTexture.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n *\n * This sample is a simple code that illustrates basic usage of\n * cooperative groups within the thread block. The code launches a single\n * thread block, creates a cooperative group of all threads in the block,\n * and a set of tiled partition cooperative groups. For each, it uses a\n * generic reduction function to calculate the sum of all the ranks in\n * that group. In each case the result is printed, together with the\n * expected answer (which is calculated using the analytical formula\n * (n-1)*n)/2, noting that the ranks start at zero).\n *\n */\n\n#include <cooperative_groups.h>\n#include <stdio.h>\n\nusing namespace cooperative_groups;\n\n/**\n * CUDA device function\n *\n * calculates the sum of val across the group g. The workspace array, x,\n * must be large enough to contain g.size() integers.\n */\n__device__ int sumReduction(thread_group g, int *x, int val)\n{\n    // rank of this thread in the group\n    int lane = g.thread_rank();\n\n    // for each iteration of this loop, the number of threads active in the\n    // reduction, i, is halved, and each active thread (with index [lane])\n    // performs a single summation of it's own value with that\n    // of a \"partner\" (with index [lane+i]).\n    for (int i = g.size() / 2; i > 0; i /= 2) {\n        // store value for this thread in temporary array\n        x[lane] = val;\n\n        // synchronize all threads in group\n        g.sync();\n\n        if (lane < i)\n            // active threads perform summation of their value with\n            // their partner's value\n            val += x[lane + i];\n\n        // synchronize all threads in group\n        g.sync();\n    }\n\n    // master thread in group returns result, and others return -1.\n    if (g.thread_rank() == 0)\n        return val;\n    else\n        return -1;\n}\n\n/**\n * CUDA kernel device code\n *\n * Creates cooperative groups and performs reductions\n */\n__global__ void cgkernel()\n{\n    // threadBlockGroup includes all threads in the block\n    thread_block threadBlockGroup     = this_thread_block();\n    int          threadBlockGroupSize = threadBlockGroup.size();\n\n    // workspace array in shared memory required for reduction\n    extern __shared__ int workspace[];\n\n    int input, output, expectedOutput;\n\n    // input to reduction, for each thread, is its' rank in the group\n    input = threadBlockGroup.thread_rank();\n\n    // expected output from analytical formula (n-1)(n)/2\n    // (noting that indexing starts at 0 rather than 1)\n    expectedOutput = (threadBlockGroupSize - 1) * threadBlockGroupSize / 2;\n\n    // perform reduction\n    output = sumReduction(threadBlockGroup, workspace, input);\n\n    // master thread in group prints out result\n    if (threadBlockGroup.thread_rank() == 0) {\n        printf(\" Sum of all ranks 0..%d in threadBlockGroup is %d (expected %d)\\n\\n\",\n               (int)threadBlockGroup.size() - 1,\n               output,\n               expectedOutput);\n\n        printf(\" Now creating %d groups, each of size 16 threads:\\n\\n\", (int)threadBlockGroup.size() / 16);\n    }\n\n    threadBlockGroup.sync();\n\n    // each tiledPartition16 group includes 16 threads\n    thread_block_tile<16> tiledPartition16 = tiled_partition<16>(threadBlockGroup);\n\n    // This offset allows each group to have its own unique area in the workspace\n    // array\n    int workspaceOffset = threadBlockGroup.thread_rank() - tiledPartition16.thread_rank();\n\n    // input to reduction, for each thread, is its' rank in the group\n    input = tiledPartition16.thread_rank();\n\n    // expected output from analytical formula (n-1)(n)/2\n    // (noting that indexing starts at 0 rather than 1)\n    expectedOutput = 15 * 16 / 2;\n\n    // Perform reduction\n    output = sumReduction(tiledPartition16, workspace + workspaceOffset, input);\n\n    // each master thread prints out result\n    if (tiledPartition16.thread_rank() == 0)\n        printf(\"   Sum of all ranks 0..15 in this tiledPartition16 group is %d \"\n               \"(expected %d)\\n\",\n               output,\n               expectedOutput);\n\n    return;\n}\n\n/**\n * Host main routine\n */\nint main()\n{\n    // Error code to check return values for CUDA calls\n    cudaError_t err;\n\n    // Launch the kernel\n\n    int blocksPerGrid   = 1;\n    int threadsPerBlock = 64;\n\n    printf(\"\\nLaunching a single block with %d threads...\\n\\n\", threadsPerBlock);\n\n    // we use the optional third argument to specify the size\n    // of shared memory required in the kernel\n    cgkernel<<<blocksPerGrid, threadsPerBlock, threadsPerBlock * sizeof(int)>>>();\n    err = cudaDeviceSynchronize();\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to launch kernel (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"\\n...Done.\\n\\n\");\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n *\n * This sample is a simple code that illustrates basic usage of\n * cooperative groups within the thread block. The code launches a single\n * thread block, creates a cooperative group of all threads in the block,\n * and a set of tiled partition cooperative groups. For each, it uses a\n * generic reduction function to calculate the sum of all the ranks in\n * that group. In each case the result is printed, together with the\n * expected answer (which is calculated using the analytical formula\n * (n-1)*n)/2, noting that the ranks start at zero).\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n\nusing namespace cooperative_groups;\n\n/**\n * CUDA device function\n *\n * calculates the sum of val across the group g. The workspace array, x,\n * must be large enough to contain g.size() integers.\n */\n__device__ int sumReduction(thread_group g, int *x, int val)\n{\n    // rank of this thread in the group\n    int lane = g.thread_rank();\n\n    // for each iteration of this loop, the number of threads active in the\n    // reduction, i, is halved, and each active thread (with index [lane])\n    // performs a single summation of it's own value with that\n    // of a \"partner\" (with index [lane+i]).\n    for (int i = g.size() / 2; i > 0; i /= 2) {\n        // store value for this thread in temporary array\n        x[lane] = val;\n\n        // synchronize all threads in group\n        g.sync();\n\n        if (lane < i)\n            // active threads perform summation of their value with\n            // their partner's value\n            val += x[lane + i];\n\n        // synchronize all threads in group\n        g.sync();\n    }\n\n    // master thread in group returns result, and others return -1.\n    if (g.thread_rank() == 0)\n        return val;\n    else\n        return -1;\n}\n\n/**\n * CUDA kernel device code\n *\n * Creates cooperative groups and performs reductions\n */\n__global__ void cgkernel()\n{\n    // threadBlockGroup includes all threads in the block\n    thread_block threadBlockGroup     = this_thread_block();\n    int          threadBlockGroupSize = threadBlockGroup.size();\n\n    // workspace array in shared memory required for reduction\n    extern __shared__ int workspace[];\n\n    int input, output, expectedOutput;\n\n    // input to reduction, for each thread, is its' rank in the group\n    input = threadBlockGroup.thread_rank();\n\n    // expected output from analytical formula (n-1)(n)/2\n    // (noting that indexing starts at 0 rather than 1)\n    expectedOutput = (threadBlockGroupSize - 1) * threadBlockGroupSize / 2;\n\n    // perform reduction\n    output = sumReduction(threadBlockGroup, workspace, input);\n\n    // master thread in group prints out result\n    if (threadBlockGroup.thread_rank() == 0) {\n        printf(\" Sum of all ranks 0..%d in threadBlockGroup is %d (expected %d)\\n\\n\",\n               (int)threadBlockGroup.size() - 1,\n               output,\n               expectedOutput);\n\n        printf(\" Now creating %d groups, each of size 16 threads:\\n\\n\", (int)threadBlockGroup.size() / 16);\n    }\n\n    threadBlockGroup.sync();\n\n    // each tiledPartition16 group includes 16 threads\n    thread_block_tile<16> tiledPartition16 = tiled_partition<16>(threadBlockGroup);\n\n    // This offset allows each group to have its own unique area in the workspace\n    // array\n    int workspaceOffset = threadBlockGroup.thread_rank() - tiledPartition16.thread_rank();\n\n    // input to reduction, for each thread, is its' rank in the group\n    input = tiledPartition16.thread_rank();\n\n    // expected output from analytical formula (n-1)(n)/2\n    // (noting that indexing starts at 0 rather than 1)\n    expectedOutput = 15 * 16 / 2;\n\n    // Perform reduction\n    output = sumReduction(tiledPartition16, workspace + workspaceOffset, input);\n\n    // each master thread prints out result\n    if (tiledPartition16.thread_rank() == 0)\n        printf(\"   Sum of all ranks 0..15 in this tiledPartition16 group is %d \"\n               \"(expected %d)\\n\",\n               output,\n               expectedOutput);\n\n    return;\n}\n\n/**\n * Host main routine\n */\nint main()\n{\n    // Error code to check return values for CUDA calls\n    hipError_t err;\n\n    // Launch the kernel\n\n    int blocksPerGrid   = 1;\n    int threadsPerBlock = 64;\n\n    printf(\"\\nLaunching a single block with %d threads...\\n\\n\", threadsPerBlock);\n\n    // we use the optional third argument to specify the size\n    // of shared memory required in the kernel\n    cgkernel<<<blocksPerGrid, threadsPerBlock, threadsPerBlock * sizeof(int)>>>();\n    err = hipDeviceSynchronize();\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to launch kernel (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"\\n...Done.\\n\\n\");\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleCooperativeGroups/simpleCooperativeGroups.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Simple example demonstrating how to use MPI with CUDA\n *\n *  Generate some random numbers on one node.\n *  Dispatch them to all nodes.\n *  Compute their square root on each node's GPU.\n *  Compute the average of the results using MPI.\n *\n *  simpleMPI.cu: GPU part, compiled with nvcc\n */\n\n#include <iostream>\nusing std::cerr;\nusing std::endl;\n\n#include \"simpleMPI.h\"\n\n// Error handling macro\n#define CUDA_CHECK(call)                                                     \\\n    if ((call) != cudaSuccess) {                                             \\\n        cudaError_t err = cudaGetLastError();                                \\\n        cerr << \"CUDA error calling \\\"\" #call \"\\\", code is \" << err << endl; \\\n        my_abort(err);                                                       \\\n    }\n\n// Device code\n// Very simple GPU Kernel that computes square roots of input numbers\n__global__ void simpleMPIKernel(float *input, float *output)\n{\n    int tid     = blockIdx.x * blockDim.x + threadIdx.x;\n    output[tid] = sqrt(input[tid]);\n}\n\n// Initialize an array with random data (between 0 and 1)\nvoid initData(float *data, int dataSize)\n{\n    for (int i = 0; i < dataSize; i++) {\n        data[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// CUDA computation on each node\n// No MPI here, only CUDA\nvoid computeGPU(float *hostData, int blockSize, int gridSize)\n{\n    int dataSize = blockSize * gridSize;\n\n    // Allocate data on GPU memory\n    float *deviceInputData = NULL;\n    CUDA_CHECK(cudaMalloc((void **)&deviceInputData, dataSize * sizeof(float)));\n\n    float *deviceOutputData = NULL;\n    CUDA_CHECK(cudaMalloc((void **)&deviceOutputData, dataSize * sizeof(float)));\n\n    // Copy to GPU memory\n    CUDA_CHECK(cudaMemcpy(deviceInputData, hostData, dataSize * sizeof(float), cudaMemcpyHostToDevice));\n\n    // Run kernel\n    simpleMPIKernel<<<gridSize, blockSize>>>(deviceInputData, deviceOutputData);\n\n    // Copy data back to CPU memory\n    CUDA_CHECK(cudaMemcpy(hostData, deviceOutputData, dataSize * sizeof(float), cudaMemcpyDeviceToHost));\n\n    // Free GPU memory\n    CUDA_CHECK(cudaFree(deviceInputData));\n    CUDA_CHECK(cudaFree(deviceOutputData));\n}\n\nfloat sum(float *data, int size)\n{\n    float accum = 0.f;\n\n    for (int i = 0; i < size; i++) {\n        accum += data[i];\n    }\n\n    return accum;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Simple example demonstrating how to use MPI with CUDA\n *\n *  Generate some random numbers on one node.\n *  Dispatch them to all nodes.\n *  Compute their square root on each node's GPU.\n *  Compute the average of the results using MPI.\n *\n *  simpleMPI.cu: GPU part, compiled with nvcc\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\nusing std::cerr;\nusing std::endl;\n\n#include \"simpleMPI.h\"\n\n// Error handling macro\n#define CUDA_CHECK(call)                                                     \\\n    if ((call) != hipSuccess) {                                             \\\n        hipError_t err = hipGetLastError();                                \\\n        cerr << \"CUDA error calling \\\"\" #call \"\\\", code is \" << err << endl; \\\n        my_abort(err);                                                       \\\n    }\n\n// Device code\n// Very simple GPU Kernel that computes square roots of input numbers\n__global__ void simpleMPIKernel(float *input, float *output)\n{\n    int tid     = blockIdx.x * blockDim.x + threadIdx.x;\n    output[tid] = sqrt(input[tid]);\n}\n\n// Initialize an array with random data (between 0 and 1)\nvoid initData(float *data, int dataSize)\n{\n    for (int i = 0; i < dataSize; i++) {\n        data[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// CUDA computation on each node\n// No MPI here, only CUDA\nvoid computeGPU(float *hostData, int blockSize, int gridSize)\n{\n    int dataSize = blockSize * gridSize;\n\n    // Allocate data on GPU memory\n    float *deviceInputData = NULL;\n    CUDA_CHECK(hipMalloc((void **)&deviceInputData, dataSize * sizeof(float)));\n\n    float *deviceOutputData = NULL;\n    CUDA_CHECK(hipMalloc((void **)&deviceOutputData, dataSize * sizeof(float)));\n\n    // Copy to GPU memory\n    CUDA_CHECK(hipMemcpy(deviceInputData, hostData, dataSize * sizeof(float), hipMemcpyHostToDevice));\n\n    // Run kernel\n    simpleMPIKernel<<<gridSize, blockSize>>>(deviceInputData, deviceOutputData);\n\n    // Copy data back to CPU memory\n    CUDA_CHECK(hipMemcpy(hostData, deviceOutputData, dataSize * sizeof(float), hipMemcpyDeviceToHost));\n\n    // Free GPU memory\n    CUDA_CHECK(hipFree(deviceInputData));\n    CUDA_CHECK(hipFree(deviceOutputData));\n}\n\nfloat sum(float *data, int size)\n{\n    float accum = 0.f;\n\n    for (int i = 0; i < size; i++) {\n        accum += data[i];\n    }\n\n    return accum;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleMPI/simpleMPI.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n#define _SIMPLETEXTURE3D_KERNEL_CU_\n\n#include <helper_cuda.h>\n#include <helper_math.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\ncudaArray          *d_volumeArray = 0;\ncudaTextureObject_t tex; // 3D texture\n\n__global__ void d_render(uint *d_output, uint imageW, uint imageH, float w, cudaTextureObject_t texObj)\n{\n    uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;\n    uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;\n\n    float u = x / (float)imageW;\n    float v = y / (float)imageH;\n    // read from 3D texture\n    float voxel = tex3D<float>(texObj, u, v, w);\n\n    if ((x < imageW) && (y < imageH)) {\n        // write output color\n        uint i      = __umul24(y, imageW) + x;\n        d_output[i] = voxel * 255;\n    }\n}\n\nextern \"C\" void setTextureFilterMode(bool bLinearFilter)\n{\n    if (tex) {\n        checkCudaErrors(cudaDestroyTextureObject(tex));\n    }\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? cudaFilterModeLinear : cudaFilterModePoint;\n    ;\n    texDescr.addressMode[0] = cudaAddressModeWrap;\n    texDescr.addressMode[1] = cudaAddressModeWrap;\n    texDescr.addressMode[2] = cudaAddressModeWrap;\n    texDescr.readMode       = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void initCuda(const uchar *h_volume, cudaExtent volumeSize)\n{\n    // create 3D array\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<uchar>();\n    checkCudaErrors(cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize));\n\n    // copy data to 3D array\n    cudaMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr =\n        make_cudaPitchedPtr((void *)h_volume, volumeSize.width * sizeof(uchar), volumeSize.width, volumeSize.height);\n    copyParams.dstArray = d_volumeArray;\n    copyParams.extent   = volumeSize;\n    copyParams.kind     = cudaMemcpyHostToDevice;\n    checkCudaErrors(cudaMemcpy3D(&copyParams));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    // access with normalized texture coordinates\n    texDescr.normalizedCoords = true;\n    // linear interpolation\n    texDescr.filterMode = cudaFilterModeLinear;\n    // wrap texture coordinates\n    texDescr.addressMode[0] = cudaAddressModeWrap;\n    texDescr.addressMode[1] = cudaAddressModeWrap;\n    texDescr.addressMode[2] = cudaAddressModeWrap;\n    texDescr.readMode       = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void render_kernel(dim3 gridSize, dim3 blockSize, uint *d_output, uint imageW, uint imageH, float w)\n{\n    d_render<<<gridSize, blockSize>>>(d_output, imageW, imageH, w, tex);\n}\n\nvoid cleanupCuda()\n{\n    if (tex) {\n        checkCudaErrors(cudaDestroyTextureObject(tex));\n    }\n    if (d_volumeArray) {\n        checkCudaErrors(cudaFreeArray(d_volumeArray));\n    }\n}\n\n#endif // #ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n#define _SIMPLETEXTURE3D_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\nhipArray          *d_volumeArray = 0;\nhipTextureObject_t tex; // 3D texture\n\n__global__ void d_render(uint *d_output, uint imageW, uint imageH, float w, hipTextureObject_t texObj)\n{\n    uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;\n    uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;\n\n    float u = x / (float)imageW;\n    float v = y / (float)imageH;\n    // read from 3D texture\n    float voxel = tex3D<float>(texObj, u, v, w);\n\n    if ((x < imageW) && (y < imageH)) {\n        // write output color\n        uint i      = __umul24(y, imageW) + x;\n        d_output[i] = voxel * 255;\n    }\n}\n\nextern \"C\" void setTextureFilterMode(bool bLinearFilter)\n{\n    if (tex) {\n        checkCudaErrors(hipDestroyTextureObject(tex));\n    }\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? hipFilterModeLinear : hipFilterModePoint;\n    ;\n    texDescr.addressMode[0] = hipAddressModeWrap;\n    texDescr.addressMode[1] = hipAddressModeWrap;\n    texDescr.addressMode[2] = hipAddressModeWrap;\n    texDescr.readMode       = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void initCuda(const uchar *h_volume, hipExtent volumeSize)\n{\n    // create 3D array\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc<uchar>();\n    checkCudaErrors(hipMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize));\n\n    // copy data to 3D array\n    hipMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr =\n        make_hipPitchedPtr((void *)h_volume, volumeSize.width * sizeof(uchar), volumeSize.width, volumeSize.height);\n    copyParams.dstArray = d_volumeArray;\n    copyParams.extent   = volumeSize;\n    copyParams.kind     = hipMemcpyHostToDevice;\n    checkCudaErrors(hipMemcpy3D(&copyParams));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    // access with normalized texture coordinates\n    texDescr.normalizedCoords = true;\n    // linear interpolation\n    texDescr.filterMode = hipFilterModeLinear;\n    // wrap texture coordinates\n    texDescr.addressMode[0] = hipAddressModeWrap;\n    texDescr.addressMode[1] = hipAddressModeWrap;\n    texDescr.addressMode[2] = hipAddressModeWrap;\n    texDescr.readMode       = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void render_kernel(dim3 gridSize, dim3 blockSize, uint *d_output, uint imageW, uint imageH, float w)\n{\n    d_render<<<gridSize, blockSize>>>(d_output, imageW, imageH, w, tex);\n}\n\nvoid cleanupCuda()\n{\n    if (tex) {\n        checkCudaErrors(hipDestroyTextureObject(tex));\n    }\n    if (d_volumeArray) {\n        checkCudaErrors(hipFreeArray(d_volumeArray));\n    }\n}\n\n#endif // #ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleTexture3D/simpleTexture3D_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates a combination of Peer-to-Peer (P2P) and\n * Unified Virtual Address Space (UVA) features new to SDK 4.0\n */\n\n// includes, system\n#include <stdio.h>\n#include <stdlib.h>\n\n// CUDA includes\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\n__global__ void SimpleKernel(float *src, float *dst)\n{\n    // Just a dummy kernel, doing enough for us to verify that everything\n    // worked\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    dst[idx]      = src[idx] * 2.0f;\n}\n\ninline bool IsAppBuiltAs64() { return sizeof(void *) == 8; }\n\nint main(int argc, char **argv)\n{\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    if (!IsAppBuiltAs64()) {\n        printf(\"%s is only supported with on 64-bit OSs and the application must be \"\n               \"built as a 64-bit target.  Test is being waived.\\n\",\n               argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    // Number of GPUs\n    printf(\"Checking for multiple GPUs...\\n\");\n    int gpu_n;\n    checkCudaErrors(cudaGetDeviceCount(&gpu_n));\n    printf(\"CUDA-capable device count: %i\\n\", gpu_n);\n\n    if (gpu_n < 2) {\n        printf(\"Two or more GPUs with Peer-to-Peer access capability are required for \"\n               \"%s.\\n\",\n               argv[0]);\n        printf(\"Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Query device properties\n    cudaDeviceProp prop[64];\n    int            gpuid[2]; // we want to find the first two GPU's that can support P2P\n\n    for (int i = 0; i < gpu_n; i++) {\n        checkCudaErrors(cudaGetDeviceProperties(&prop[i], i));\n    }\n    // Check possibility for peer access\n    printf(\"\\nChecking GPU(s) for support of peer to peer memory access...\\n\");\n\n    int can_access_peer;\n    int p2pCapableGPUs[2]; // We take only 1 pair of P2P capable GPUs\n    p2pCapableGPUs[0] = p2pCapableGPUs[1] = -1;\n\n    // Show all the combinations of supported P2P GPUs\n    for (int i = 0; i < gpu_n; i++) {\n        for (int j = 0; j < gpu_n; j++) {\n            if (i == j) {\n                continue;\n            }\n            checkCudaErrors(cudaDeviceCanAccessPeer(&can_access_peer, i, j));\n            printf(\"> Peer access from %s (GPU%d) -> %s (GPU%d) : %s\\n\",\n                   prop[i].name,\n                   i,\n                   prop[j].name,\n                   j,\n                   can_access_peer ? \"Yes\" : \"No\");\n            if (can_access_peer && p2pCapableGPUs[0] == -1) {\n                p2pCapableGPUs[0] = i;\n                p2pCapableGPUs[1] = j;\n            }\n        }\n    }\n\n    if (p2pCapableGPUs[0] == -1 || p2pCapableGPUs[1] == -1) {\n        printf(\"Two or more GPUs with Peer-to-Peer access capability are required for \"\n               \"%s.\\n\",\n               argv[0]);\n        printf(\"Peer to Peer access is not available amongst GPUs in the system, \"\n               \"waiving test.\\n\");\n\n        exit(EXIT_WAIVED);\n    }\n\n    // Use first pair of p2p capable GPUs detected.\n    gpuid[0] = p2pCapableGPUs[0];\n    gpuid[1] = p2pCapableGPUs[1];\n\n    // Enable peer access\n    printf(\"Enabling peer access between GPU%d and GPU%d...\\n\", gpuid[0], gpuid[1]);\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    checkCudaErrors(cudaDeviceEnablePeerAccess(gpuid[1], 0));\n    checkCudaErrors(cudaSetDevice(gpuid[1]));\n    checkCudaErrors(cudaDeviceEnablePeerAccess(gpuid[0], 0));\n\n    // Allocate buffers\n    const size_t buf_size = 1024 * 1024 * 16 * sizeof(float);\n    printf(\n        \"Allocating buffers (%iMB on GPU%d, GPU%d and CPU Host)...\\n\", int(buf_size / 1024 / 1024), gpuid[0], gpuid[1]);\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    float *g0;\n    checkCudaErrors(cudaMalloc(&g0, buf_size));\n    checkCudaErrors(cudaSetDevice(gpuid[1]));\n    float *g1;\n    checkCudaErrors(cudaMalloc(&g1, buf_size));\n    float *h0;\n    checkCudaErrors(cudaMallocHost(&h0, buf_size)); // Automatically portable with UVA\n\n    // Create CUDA event handles\n    printf(\"Creating event handles...\\n\");\n    cudaEvent_t start_event, stop_event;\n    float       time_memcpy;\n    int         eventflags = cudaEventBlockingSync;\n    checkCudaErrors(cudaEventCreateWithFlags(&start_event, eventflags));\n    checkCudaErrors(cudaEventCreateWithFlags(&stop_event, eventflags));\n\n    // P2P memcopy() benchmark\n    checkCudaErrors(cudaEventRecord(start_event, 0));\n\n    for (int i = 0; i < 100; i++) {\n        // With UVA we don't need to specify source and target devices, the\n        // runtime figures this out by itself from the pointers\n        // Ping-pong copy between GPUs\n        if (i % 2 == 0) {\n            checkCudaErrors(cudaMemcpy(g1, g0, buf_size, cudaMemcpyDefault));\n        }\n        else {\n            checkCudaErrors(cudaMemcpy(g0, g1, buf_size, cudaMemcpyDefault));\n        }\n    }\n\n    checkCudaErrors(cudaEventRecord(stop_event, 0));\n    checkCudaErrors(cudaEventSynchronize(stop_event));\n    checkCudaErrors(cudaEventElapsedTime(&time_memcpy, start_event, stop_event));\n    printf(\"cudaMemcpyPeer / cudaMemcpy between GPU%d and GPU%d: %.2fGB/s\\n\",\n           gpuid[0],\n           gpuid[1],\n           (1.0f / (time_memcpy / 1000.0f)) * ((100.0f * buf_size)) / 1024.0f / 1024.0f / 1024.0f);\n\n    // Prepare host buffer and copy to GPU 0\n    printf(\"Preparing host buffer and memcpy to GPU%d...\\n\", gpuid[0]);\n\n    for (int i = 0; i < buf_size / sizeof(float); i++) {\n        h0[i] = float(i % 4096);\n    }\n\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    checkCudaErrors(cudaMemcpy(g0, h0, buf_size, cudaMemcpyDefault));\n\n    // Kernel launch configuration\n    const dim3 threads(512, 1);\n    const dim3 blocks((buf_size / sizeof(float)) / threads.x, 1);\n\n    // Run kernel on GPU 1, reading input from the GPU 0 buffer, writing\n    // output to the GPU 1 buffer\n    printf(\"Run kernel on GPU%d, taking source data from GPU%d and writing to \"\n           \"GPU%d...\\n\",\n           gpuid[1],\n           gpuid[0],\n           gpuid[1]);\n    checkCudaErrors(cudaSetDevice(gpuid[1]));\n    SimpleKernel<<<blocks, threads>>>(g0, g1);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Run kernel on GPU 0, reading input from the GPU 1 buffer, writing\n    // output to the GPU 0 buffer\n    printf(\"Run kernel on GPU%d, taking source data from GPU%d and writing to \"\n           \"GPU%d...\\n\",\n           gpuid[0],\n           gpuid[1],\n           gpuid[0]);\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    SimpleKernel<<<blocks, threads>>>(g1, g0);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Copy data back to host and verify\n    printf(\"Copy data back to host from GPU%d and verify results...\\n\", gpuid[0]);\n    checkCudaErrors(cudaMemcpy(h0, g0, buf_size, cudaMemcpyDefault));\n\n    int error_count = 0;\n\n    for (int i = 0; i < buf_size / sizeof(float); i++) {\n        // Re-generate input data and apply 2x '* 2.0f' computation of both\n        // kernel runs\n        if (h0[i] != float(i % 4096) * 2.0f * 2.0f) {\n            printf(\"Verification error @ element %i: val = %f, ref = %f\\n\", i, h0[i], (float(i % 4096) * 2.0f * 2.0f));\n\n            if (error_count++ > 10) {\n                break;\n            }\n        }\n    }\n\n    // Disable peer access (also unregisters memory for non-UVA cases)\n    printf(\"Disabling peer access...\\n\");\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    checkCudaErrors(cudaDeviceDisablePeerAccess(gpuid[1]));\n    checkCudaErrors(cudaSetDevice(gpuid[1]));\n    checkCudaErrors(cudaDeviceDisablePeerAccess(gpuid[0]));\n\n    // Cleanup and shutdown\n    printf(\"Shutting down...\\n\");\n    checkCudaErrors(cudaEventDestroy(start_event));\n    checkCudaErrors(cudaEventDestroy(stop_event));\n    checkCudaErrors(cudaSetDevice(gpuid[0]));\n    checkCudaErrors(cudaFree(g0));\n    checkCudaErrors(cudaSetDevice(gpuid[1]));\n    checkCudaErrors(cudaFree(g1));\n    checkCudaErrors(cudaFreeHost(h0));\n\n    for (int i = 0; i < gpu_n; i++) {\n        checkCudaErrors(cudaSetDevice(i));\n    }\n\n    if (error_count != 0) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n    else {\n        printf(\"Test passed\\n\");\n        exit(EXIT_SUCCESS);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates a combination of Peer-to-Peer (P2P) and\n * Unified Virtual Address Space (UVA) features new to SDK 4.0\n */\n\n// includes, system\n#include <stdio.h>\n#include <stdlib.h>\n\n// CUDA includes\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h> // helper for shared that are common to CUDA Samples\n\n__global__ void SimpleKernel(float *src, float *dst)\n{\n    // Just a dummy kernel, doing enough for us to verify that everything\n    // worked\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    dst[idx]      = src[idx] * 2.0f;\n}\n\ninline bool IsAppBuiltAs64() { return sizeof(void *) == 8; }\n\nint main(int argc, char **argv)\n{\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    if (!IsAppBuiltAs64()) {\n        printf(\"%s is only supported with on 64-bit OSs and the application must be \"\n               \"built as a 64-bit target.  Test is being waived.\\n\",\n               argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    // Number of GPUs\n    printf(\"Checking for multiple GPUs...\\n\");\n    int gpu_n;\n    checkCudaErrors(hipGetDeviceCount(&gpu_n));\n    printf(\"CUDA-capable device count: %i\\n\", gpu_n);\n\n    if (gpu_n < 2) {\n        printf(\"Two or more GPUs with Peer-to-Peer access capability are required for \"\n               \"%s.\\n\",\n               argv[0]);\n        printf(\"Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Query device properties\n    hipDeviceProp_t prop[64];\n    int            gpuid[2]; // we want to find the first two GPU's that can support P2P\n\n    for (int i = 0; i < gpu_n; i++) {\n        checkCudaErrors(hipGetDeviceProperties(&prop[i], i));\n    }\n    // Check possibility for peer access\n    printf(\"\\nChecking GPU(s) for support of peer to peer memory access...\\n\");\n\n    int can_access_peer;\n    int p2pCapableGPUs[2]; // We take only 1 pair of P2P capable GPUs\n    p2pCapableGPUs[0] = p2pCapableGPUs[1] = -1;\n\n    // Show all the combinations of supported P2P GPUs\n    for (int i = 0; i < gpu_n; i++) {\n        for (int j = 0; j < gpu_n; j++) {\n            if (i == j) {\n                continue;\n            }\n            checkCudaErrors(hipDeviceCanAccessPeer(&can_access_peer, i, j));\n            printf(\"> Peer access from %s (GPU%d) -> %s (GPU%d) : %s\\n\",\n                   prop[i].name,\n                   i,\n                   prop[j].name,\n                   j,\n                   can_access_peer ? \"Yes\" : \"No\");\n            if (can_access_peer && p2pCapableGPUs[0] == -1) {\n                p2pCapableGPUs[0] = i;\n                p2pCapableGPUs[1] = j;\n            }\n        }\n    }\n\n    if (p2pCapableGPUs[0] == -1 || p2pCapableGPUs[1] == -1) {\n        printf(\"Two or more GPUs with Peer-to-Peer access capability are required for \"\n               \"%s.\\n\",\n               argv[0]);\n        printf(\"Peer to Peer access is not available amongst GPUs in the system, \"\n               \"waiving test.\\n\");\n\n        exit(EXIT_WAIVED);\n    }\n\n    // Use first pair of p2p capable GPUs detected.\n    gpuid[0] = p2pCapableGPUs[0];\n    gpuid[1] = p2pCapableGPUs[1];\n\n    // Enable peer access\n    printf(\"Enabling peer access between GPU%d and GPU%d...\\n\", gpuid[0], gpuid[1]);\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    checkCudaErrors(hipDeviceEnablePeerAccess(gpuid[1], 0));\n    checkCudaErrors(hipSetDevice(gpuid[1]));\n    checkCudaErrors(hipDeviceEnablePeerAccess(gpuid[0], 0));\n\n    // Allocate buffers\n    const size_t buf_size = 1024 * 1024 * 16 * sizeof(float);\n    printf(\n        \"Allocating buffers (%iMB on GPU%d, GPU%d and CPU Host)...\\n\", int(buf_size / 1024 / 1024), gpuid[0], gpuid[1]);\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    float *g0;\n    checkCudaErrors(hipMalloc(&g0, buf_size));\n    checkCudaErrors(hipSetDevice(gpuid[1]));\n    float *g1;\n    checkCudaErrors(hipMalloc(&g1, buf_size));\n    float *h0;\n    checkCudaErrors(hipHostMalloc(&h0, buf_size)); // Automatically portable with UVA\n\n    // Create CUDA event handles\n    printf(\"Creating event handles...\\n\");\n    hipEvent_t start_event, stop_event;\n    float       time_memcpy;\n    int         eventflags = hipEventBlockingSync;\n    checkCudaErrors(hipEventCreateWithFlags(&start_event, eventflags));\n    checkCudaErrors(hipEventCreateWithFlags(&stop_event, eventflags));\n\n    // P2P memcopy() benchmark\n    checkCudaErrors(hipEventRecord(start_event, 0));\n\n    for (int i = 0; i < 100; i++) {\n        // With UVA we don't need to specify source and target devices, the\n        // runtime figures this out by itself from the pointers\n        // Ping-pong copy between GPUs\n        if (i % 2 == 0) {\n            checkCudaErrors(hipMemcpy(g1, g0, buf_size, hipMemcpyDefault));\n        }\n        else {\n            checkCudaErrors(hipMemcpy(g0, g1, buf_size, hipMemcpyDefault));\n        }\n    }\n\n    checkCudaErrors(hipEventRecord(stop_event, 0));\n    checkCudaErrors(hipEventSynchronize(stop_event));\n    checkCudaErrors(hipEventElapsedTime(&time_memcpy, start_event, stop_event));\n    printf(\"hipMemcpyPeer / hipMemcpy between GPU%d and GPU%d: %.2fGB/s\\n\",\n           gpuid[0],\n           gpuid[1],\n           (1.0f / (time_memcpy / 1000.0f)) * ((100.0f * buf_size)) / 1024.0f / 1024.0f / 1024.0f);\n\n    // Prepare host buffer and copy to GPU 0\n    printf(\"Preparing host buffer and memcpy to GPU%d...\\n\", gpuid[0]);\n\n    for (int i = 0; i < buf_size / sizeof(float); i++) {\n        h0[i] = float(i % 4096);\n    }\n\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    checkCudaErrors(hipMemcpy(g0, h0, buf_size, hipMemcpyDefault));\n\n    // Kernel launch configuration\n    const dim3 threads(512, 1);\n    const dim3 blocks((buf_size / sizeof(float)) / threads.x, 1);\n\n    // Run kernel on GPU 1, reading input from the GPU 0 buffer, writing\n    // output to the GPU 1 buffer\n    printf(\"Run kernel on GPU%d, taking source data from GPU%d and writing to \"\n           \"GPU%d...\\n\",\n           gpuid[1],\n           gpuid[0],\n           gpuid[1]);\n    checkCudaErrors(hipSetDevice(gpuid[1]));\n    SimpleKernel<<<blocks, threads>>>(g0, g1);\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Run kernel on GPU 0, reading input from the GPU 1 buffer, writing\n    // output to the GPU 0 buffer\n    printf(\"Run kernel on GPU%d, taking source data from GPU%d and writing to \"\n           \"GPU%d...\\n\",\n           gpuid[0],\n           gpuid[1],\n           gpuid[0]);\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    SimpleKernel<<<blocks, threads>>>(g1, g0);\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Copy data back to host and verify\n    printf(\"Copy data back to host from GPU%d and verify results...\\n\", gpuid[0]);\n    checkCudaErrors(hipMemcpy(h0, g0, buf_size, hipMemcpyDefault));\n\n    int error_count = 0;\n\n    for (int i = 0; i < buf_size / sizeof(float); i++) {\n        // Re-generate input data and apply 2x '* 2.0f' computation of both\n        // kernel runs\n        if (h0[i] != float(i % 4096) * 2.0f * 2.0f) {\n            printf(\"Verification error @ element %i: val = %f, ref = %f\\n\", i, h0[i], (float(i % 4096) * 2.0f * 2.0f));\n\n            if (error_count++ > 10) {\n                break;\n            }\n        }\n    }\n\n    // Disable peer access (also unregisters memory for non-UVA cases)\n    printf(\"Disabling peer access...\\n\");\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    checkCudaErrors(hipDeviceDisablePeerAccess(gpuid[1]));\n    checkCudaErrors(hipSetDevice(gpuid[1]));\n    checkCudaErrors(hipDeviceDisablePeerAccess(gpuid[0]));\n\n    // Cleanup and shutdown\n    printf(\"Shutting down...\\n\");\n    checkCudaErrors(hipEventDestroy(start_event));\n    checkCudaErrors(hipEventDestroy(stop_event));\n    checkCudaErrors(hipSetDevice(gpuid[0]));\n    checkCudaErrors(hipFree(g0));\n    checkCudaErrors(hipSetDevice(gpuid[1]));\n    checkCudaErrors(hipFree(g1));\n    checkCudaErrors(hipHostFree(h0));\n\n    for (int i = 0; i < gpu_n; i++) {\n        checkCudaErrors(hipSetDevice(i));\n    }\n\n    if (error_count != 0) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n    else {\n        printf(\"Test passed\\n\");\n        exit(EXIT_SUCCESS);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleP2P/simpleP2P.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Matrix multiplication: C = A * B.\n * Device code.\n */\n\n#ifndef _MATRIXMUL_KERNEL_H_\n#define _MATRIXMUL_KERNEL_H_\n\n#include <stdio.h>\n\n#define AS(i, j) As[i][j]\n#define BS(i, j) Bs[i][j]\n\n////////////////////////////////////////////////////////////////////////////////\n//! Matrix multiplication on the device: C = A * B\n//! wA is A's width and wB is B's width\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int block_size, typename size_type>\n__device__ void matrixMul(float *C, float *A, float *B, size_type wA, size_type wB)\n{\n    // Block index\n    size_type bx = blockIdx.x;\n    size_type by = blockIdx.y;\n\n    // Thread index\n    size_type tx = threadIdx.x;\n    size_type ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    size_type aBegin = wA * block_size * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    size_type aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    size_type aStep = block_size;\n\n    // Index of the first sub-matrix of B processed by the block\n    size_type bBegin = block_size * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    size_type bStep = block_size * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (size_type a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[block_size][block_size];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[block_size][block_size];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        AS(ty, tx) = A[a + wA * ty + tx];\n        BS(ty, tx) = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (size_type k = 0; k < block_size; ++k)\n            Csub += AS(ty, k) * BS(k, tx);\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    size_type c         = wB * block_size * by + block_size * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\n// C wrappers around our template kernel\nextern \"C\" __global__ void matrixMul_bs8_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<8, size_t>(C, A, B, wA, wB);\n}\nextern \"C\" __global__ void matrixMul_bs16_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<16, size_t>(C, A, B, wA, wB);\n}\nextern \"C\" __global__ void matrixMul_bs32_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<32, size_t>(C, A, B, wA, wB);\n}\n\n#endif // #ifndef _MATRIXMUL_KERNEL_H_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Matrix multiplication: C = A * B.\n * Device code.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef _MATRIXMUL_KERNEL_H_\n#define _MATRIXMUL_KERNEL_H_\n\n#include <stdio.h>\n\n#define AS(i, j) As[i][j]\n#define BS(i, j) Bs[i][j]\n\n////////////////////////////////////////////////////////////////////////////////\n//! Matrix multiplication on the device: C = A * B\n//! wA is A's width and wB is B's width\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int block_size, typename size_type>\n__device__ void matrixMul(float *C, float *A, float *B, size_type wA, size_type wB)\n{\n    // Block index\n    size_type bx = blockIdx.x;\n    size_type by = blockIdx.y;\n\n    // Thread index\n    size_type tx = threadIdx.x;\n    size_type ty = threadIdx.y;\n\n    // Index of the first sub-matrix of A processed by the block\n    size_type aBegin = wA * block_size * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    size_type aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    size_type aStep = block_size;\n\n    // Index of the first sub-matrix of B processed by the block\n    size_type bBegin = block_size * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    size_type bStep = block_size * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (size_type a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[block_size][block_size];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[block_size][block_size];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        AS(ty, tx) = A[a + wA * ty + tx];\n        BS(ty, tx) = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (size_type k = 0; k < block_size; ++k)\n            Csub += AS(ty, k) * BS(k, tx);\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    size_type c         = wB * block_size * by + block_size * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\n// C wrappers around our template kernel\nextern \"C\" __global__ void matrixMul_bs8_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<8, size_t>(C, A, B, wA, wB);\n}\nextern \"C\" __global__ void matrixMul_bs16_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<16, size_t>(C, A, B, wA, wB);\n}\nextern \"C\" __global__ void matrixMul_bs32_64bit(float *C, float *A, float *B, size_t wA, size_t wB)\n{\n    matrixMul<32, size_t>(C, A, B, wA, wB);\n}\n\n#endif // #ifndef _MATRIXMUL_KERNEL_H_\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/matrixMulDrv/matrixMul_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This application demonstrates how to use the CUDA API to use multiple GPUs,\n * with an emphasis on simple illustration of the techniques (not on\n * performance).\n *\n * Note that in order to detect multiple GPUs in your system you have to disable\n * SLI in the nvidia control panel. Otherwise only one GPU is visible to the\n * application. On the other side, you can still extend your desktop to screens\n * attached to both GPUs.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n#include \"simpleMultiGPU.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n////////////////////////////////////////////////////////////////////////////////\nconst int MAX_GPU_COUNT = 32;\nconst int DATA_N        = 1048576 * 32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Simple reduction kernel.\n// Refer to the 'reduction' CUDA Sample describing\n// reduction optimization strategies\n////////////////////////////////////////////////////////////////////////////////\n__global__ static void reduceKernel(float *d_Result, float *d_Input, int N)\n{\n    const int tid     = blockIdx.x * blockDim.x + threadIdx.x;\n    const int threadN = gridDim.x * blockDim.x;\n    float     sum     = 0;\n\n    for (int pos = tid; pos < N; pos += threadN)\n        sum += d_Input[pos];\n\n    d_Result[tid] = sum;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // Solver config\n    TGPUplan plan[MAX_GPU_COUNT];\n\n    // GPU reduction results\n    float h_SumGPU[MAX_GPU_COUNT];\n\n    float  sumGPU;\n    double sumCPU, diff;\n\n    int i, j, gpuBase, GPU_N;\n\n    const int BLOCK_N  = 32;\n    const int THREAD_N = 256;\n    const int ACCUM_N  = BLOCK_N * THREAD_N;\n\n    printf(\"Starting simpleMultiGPU\\n\");\n    checkCudaErrors(cudaGetDeviceCount(&GPU_N));\n\n    if (GPU_N > MAX_GPU_COUNT) {\n        GPU_N = MAX_GPU_COUNT;\n    }\n\n    printf(\"CUDA-capable device count: %i\\n\", GPU_N);\n\n    printf(\"Generating input data...\\n\\n\");\n\n    // Subdividing input data across GPUs\n    // Get data sizes for each GPU\n    for (i = 0; i < GPU_N; i++) {\n        plan[i].dataN = DATA_N / GPU_N;\n    }\n\n    // Take into account \"odd\" data sizes\n    for (i = 0; i < DATA_N % GPU_N; i++) {\n        plan[i].dataN++;\n    }\n\n    // Assign data ranges to GPUs\n    gpuBase = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        plan[i].h_Sum = h_SumGPU + i;\n        gpuBase += plan[i].dataN;\n    }\n\n    // Create streams for issuing GPU command asynchronously and allocate memory\n    // (GPU and System page-locked)\n    for (i = 0; i < GPU_N; i++) {\n        checkCudaErrors(cudaSetDevice(i));\n        checkCudaErrors(cudaStreamCreate(&plan[i].stream));\n        // Allocate memory\n        checkCudaErrors(cudaMalloc((void **)&plan[i].d_Data, plan[i].dataN * sizeof(float)));\n        checkCudaErrors(cudaMalloc((void **)&plan[i].d_Sum, ACCUM_N * sizeof(float)));\n        checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Sum_from_device, ACCUM_N * sizeof(float)));\n        checkCudaErrors(cudaMallocHost((void **)&plan[i].h_Data, plan[i].dataN * sizeof(float)));\n\n        for (j = 0; j < plan[i].dataN; j++) {\n            plan[i].h_Data[j] = (float)rand() / (float)RAND_MAX;\n        }\n    }\n\n    // Start timing and compute on GPU(s)\n    printf(\"Computing with %d GPUs...\\n\", GPU_N);\n    // create and start timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // start the timer\n    sdkStartTimer(&timer);\n\n    // Copy data to GPU, launch the kernel and copy data back. All asynchronously\n    for (i = 0; i < GPU_N; i++) {\n        // Set device\n        checkCudaErrors(cudaSetDevice(i));\n\n        // Copy input data from CPU\n        checkCudaErrors(cudaMemcpyAsync(\n            plan[i].d_Data, plan[i].h_Data, plan[i].dataN * sizeof(float), cudaMemcpyHostToDevice, plan[i].stream));\n\n        // Perform GPU computations\n        reduceKernel<<<BLOCK_N, THREAD_N, 0, plan[i].stream>>>(plan[i].d_Sum, plan[i].d_Data, plan[i].dataN);\n        getLastCudaError(\"reduceKernel() execution failed.\\n\");\n\n        // Read back GPU results\n        checkCudaErrors(cudaMemcpyAsync(\n            plan[i].h_Sum_from_device, plan[i].d_Sum, ACCUM_N * sizeof(float), cudaMemcpyDeviceToHost, plan[i].stream));\n    }\n\n    // Process GPU results\n    for (i = 0; i < GPU_N; i++) {\n        float sum;\n\n        // Set device\n        checkCudaErrors(cudaSetDevice(i));\n\n        // Wait for all operations to finish\n        cudaStreamSynchronize(plan[i].stream);\n\n        // Finalize GPU reduction for current subvector\n        sum = 0;\n\n        for (j = 0; j < ACCUM_N; j++) {\n            sum += plan[i].h_Sum_from_device[j];\n        }\n\n        *(plan[i].h_Sum) = (float)sum;\n\n        // Shut down this GPU\n        checkCudaErrors(cudaFreeHost(plan[i].h_Sum_from_device));\n        checkCudaErrors(cudaFree(plan[i].d_Sum));\n        checkCudaErrors(cudaFree(plan[i].d_Data));\n        checkCudaErrors(cudaStreamDestroy(plan[i].stream));\n    }\n\n    sumGPU = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        sumGPU += h_SumGPU[i];\n    }\n\n    sdkStopTimer(&timer);\n    printf(\"  GPU Processing time: %f (ms)\\n\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // Compute on Host CPU\n    printf(\"Computing with Host CPU...\\n\\n\");\n\n    sumCPU = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        for (j = 0; j < plan[i].dataN; j++) {\n            sumCPU += plan[i].h_Data[j];\n        }\n    }\n\n    // Compare GPU and CPU results\n    printf(\"Comparing GPU and Host CPU results...\\n\");\n    diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);\n    printf(\"  GPU sum: %f\\n  CPU sum: %f\\n\", sumGPU, sumCPU);\n    printf(\"  Relative difference: %E \\n\\n\", diff);\n\n    // Cleanup and shutdown\n    for (i = 0; i < GPU_N; i++) {\n        checkCudaErrors(cudaSetDevice(i));\n        checkCudaErrors(cudaFreeHost(plan[i].h_Data));\n    }\n\n    exit((diff < 1e-5) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This application demonstrates how to use the CUDA API to use multiple GPUs,\n * with an emphasis on simple illustration of the techniques (not on\n * performance).\n *\n * Note that in order to detect multiple GPUs in your system you have to disable\n * SLI in the nvidia control panel. Otherwise only one GPU is visible to the\n * application. On the other side, you can still extend your desktop to screens\n * attached to both GPUs.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n#include \"simpleMultiGPU.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n////////////////////////////////////////////////////////////////////////////////\nconst int MAX_GPU_COUNT = 32;\nconst int DATA_N        = 1048576 * 32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Simple reduction kernel.\n// Refer to the 'reduction' CUDA Sample describing\n// reduction optimization strategies\n////////////////////////////////////////////////////////////////////////////////\n__global__ static void reduceKernel(float *d_Result, float *d_Input, int N)\n{\n    const int tid     = blockIdx.x * blockDim.x + threadIdx.x;\n    const int threadN = gridDim.x * blockDim.x;\n    float     sum     = 0;\n\n    for (int pos = tid; pos < N; pos += threadN)\n        sum += d_Input[pos];\n\n    d_Result[tid] = sum;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // Solver config\n    TGPUplan plan[MAX_GPU_COUNT];\n\n    // GPU reduction results\n    float h_SumGPU[MAX_GPU_COUNT];\n\n    float  sumGPU;\n    double sumCPU, diff;\n\n    int i, j, gpuBase, GPU_N;\n\n    const int BLOCK_N  = 32;\n    const int THREAD_N = 256;\n    const int ACCUM_N  = BLOCK_N * THREAD_N;\n\n    printf(\"Starting simpleMultiGPU\\n\");\n    checkCudaErrors(hipGetDeviceCount(&GPU_N));\n\n    if (GPU_N > MAX_GPU_COUNT) {\n        GPU_N = MAX_GPU_COUNT;\n    }\n\n    printf(\"CUDA-capable device count: %i\\n\", GPU_N);\n\n    printf(\"Generating input data...\\n\\n\");\n\n    // Subdividing input data across GPUs\n    // Get data sizes for each GPU\n    for (i = 0; i < GPU_N; i++) {\n        plan[i].dataN = DATA_N / GPU_N;\n    }\n\n    // Take into account \"odd\" data sizes\n    for (i = 0; i < DATA_N % GPU_N; i++) {\n        plan[i].dataN++;\n    }\n\n    // Assign data ranges to GPUs\n    gpuBase = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        plan[i].h_Sum = h_SumGPU + i;\n        gpuBase += plan[i].dataN;\n    }\n\n    // Create streams for issuing GPU command asynchronously and allocate memory\n    // (GPU and System page-locked)\n    for (i = 0; i < GPU_N; i++) {\n        checkCudaErrors(hipSetDevice(i));\n        checkCudaErrors(hipStreamCreate(&plan[i].stream));\n        // Allocate memory\n        checkCudaErrors(hipMalloc((void **)&plan[i].d_Data, plan[i].dataN * sizeof(float)));\n        checkCudaErrors(hipMalloc((void **)&plan[i].d_Sum, ACCUM_N * sizeof(float)));\n        checkCudaErrors(hipHostMalloc((void **)&plan[i].h_Sum_from_device, ACCUM_N * sizeof(float)));\n        checkCudaErrors(hipHostMalloc((void **)&plan[i].h_Data, plan[i].dataN * sizeof(float)));\n\n        for (j = 0; j < plan[i].dataN; j++) {\n            plan[i].h_Data[j] = (float)rand() / (float)RAND_MAX;\n        }\n    }\n\n    // Start timing and compute on GPU(s)\n    printf(\"Computing with %d GPUs...\\n\", GPU_N);\n    // create and start timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // start the timer\n    sdkStartTimer(&timer);\n\n    // Copy data to GPU, launch the kernel and copy data back. All asynchronously\n    for (i = 0; i < GPU_N; i++) {\n        // Set device\n        checkCudaErrors(hipSetDevice(i));\n\n        // Copy input data from CPU\n        checkCudaErrors(hipMemcpyAsync(\n            plan[i].d_Data, plan[i].h_Data, plan[i].dataN * sizeof(float), hipMemcpyHostToDevice, plan[i].stream));\n\n        // Perform GPU computations\n        reduceKernel<<<BLOCK_N, THREAD_N, 0, plan[i].stream>>>(plan[i].d_Sum, plan[i].d_Data, plan[i].dataN);\n        getLastCudaError(\"reduceKernel() execution failed.\\n\");\n\n        // Read back GPU results\n        checkCudaErrors(hipMemcpyAsync(\n            plan[i].h_Sum_from_device, plan[i].d_Sum, ACCUM_N * sizeof(float), hipMemcpyDeviceToHost, plan[i].stream));\n    }\n\n    // Process GPU results\n    for (i = 0; i < GPU_N; i++) {\n        float sum;\n\n        // Set device\n        checkCudaErrors(hipSetDevice(i));\n\n        // Wait for all operations to finish\n        hipStreamSynchronize(plan[i].stream);\n\n        // Finalize GPU reduction for current subvector\n        sum = 0;\n\n        for (j = 0; j < ACCUM_N; j++) {\n            sum += plan[i].h_Sum_from_device[j];\n        }\n\n        *(plan[i].h_Sum) = (float)sum;\n\n        // Shut down this GPU\n        checkCudaErrors(hipHostFree(plan[i].h_Sum_from_device));\n        checkCudaErrors(hipFree(plan[i].d_Sum));\n        checkCudaErrors(hipFree(plan[i].d_Data));\n        checkCudaErrors(hipStreamDestroy(plan[i].stream));\n    }\n\n    sumGPU = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        sumGPU += h_SumGPU[i];\n    }\n\n    sdkStopTimer(&timer);\n    printf(\"  GPU Processing time: %f (ms)\\n\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // Compute on Host CPU\n    printf(\"Computing with Host CPU...\\n\\n\");\n\n    sumCPU = 0;\n\n    for (i = 0; i < GPU_N; i++) {\n        for (j = 0; j < plan[i].dataN; j++) {\n            sumCPU += plan[i].h_Data[j];\n        }\n    }\n\n    // Compare GPU and CPU results\n    printf(\"Comparing GPU and Host CPU results...\\n\");\n    diff = fabs(sumCPU - sumGPU) / fabs(sumCPU);\n    printf(\"  GPU sum: %f\\n  CPU sum: %f\\n\", sumGPU, sumCPU);\n    printf(\"  Relative difference: %E \\n\\n\", diff);\n\n    // Cleanup and shutdown\n    for (i = 0; i < GPU_N; i++) {\n        checkCudaErrors(hipSetDevice(i));\n        checkCudaErrors(hipHostFree(plan[i].h_Data));\n    }\n\n    exit((diff < 1e-5) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleMultiGPU/simpleMultiGPU.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n__global__ void testKernel(int val)\n{\n    printf(\"[%d, %d]:\\t\\tValue is:%d\\n\",\n           blockIdx.y * gridDim.x + blockIdx.x,\n           threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x,\n           val);\n}\n\nint main(int argc, char **argv)\n{\n    int            devID;\n    cudaDeviceProp props;\n\n    // This will pick the best possible CUDA capable device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // Get GPU information\n    checkCudaErrors(cudaGetDevice(&devID));\n    checkCudaErrors(cudaGetDeviceProperties(&props, devID));\n    printf(\"Device %d: \\\"%s\\\" with Compute capability %d.%d\\n\", devID, props.name, props.major, props.minor);\n\n    printf(\"printf() is called. Output:\\n\\n\");\n\n    // Kernel configuration, where a two-dimensional grid and\n    // three-dimensional blocks are configured.\n    dim3 dimGrid(2, 2);\n    dim3 dimBlock(2, 2, 2);\n    testKernel<<<dimGrid, dimBlock>>>(10);\n    cudaDeviceSynchronize();\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n__global__ void testKernel(int val)\n{\n    printf(\"[%d, %d]:\\t\\tValue is:%d\\n\",\n           blockIdx.y * gridDim.x + blockIdx.x,\n           threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x,\n           val);\n}\n\nint main(int argc, char **argv)\n{\n    int            devID;\n    hipDeviceProp_t props;\n\n    // This will pick the best possible CUDA capable device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // Get GPU information\n    checkCudaErrors(hipGetDevice(&devID));\n    checkCudaErrors(hipGetDeviceProperties(&props, devID));\n    printf(\"Device %d: \\\"%s\\\" with Compute capability %d.%d\\n\", devID, props.name, props.major, props.minor);\n\n    printf(\"printf() is called. Output:\\n\\n\");\n\n    // Kernel configuration, where a two-dimensional grid and\n    // three-dimensional blocks are configured.\n    dim3 dimGrid(2, 2);\n    dim3 dimBlock(2, 2, 2);\n    testKernel<<<dimGrid, dimBlock>>>(10);\n    hipDeviceSynchronize();\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simplePrintf/simplePrintf.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how to use texture fetches from layered 2D textures\n * in CUDA C\n *\n * This sample first generates a 3D input data array for the layered texture\n * and the expected output. Then it starts CUDA C kernels, one for each layer,\n * which fetch their layer's texture data (using normalized texture coordinates)\n * transform it to the expected output, and write it to a 3D output data array.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\nstatic const char *sSDKname = \"simpleCubemapTexture\";\n\n// includes, kernels\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform a cubemap face of a linear buffe using cubemap texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *g_odata, int width, cudaTextureObject_t tex)\n{\n    // calculate this thread's data point\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // 0.5f offset and division are necessary to access the original data points\n    // in the texture (such that bilinear interpolation will not be activated).\n    // For details, see also CUDA Programming Guide, Appendix D\n\n    float u = ((x + 0.5f) / (float)width) * 2.f - 1.f;\n    float v = ((y + 0.5f) / (float)width) * 2.f - 1.f;\n\n    float cx, cy, cz;\n\n    for (unsigned int face = 0; face < 6; face++) {\n        // Layer 0 is positive X face\n        if (face == 0) {\n            cx = 1;\n            cy = -v;\n            cz = -u;\n        }\n        // Layer 1 is negative X face\n        else if (face == 1) {\n            cx = -1;\n            cy = -v;\n            cz = u;\n        }\n        // Layer 2 is positive Y face\n        else if (face == 2) {\n            cx = u;\n            cy = 1;\n            cz = v;\n        }\n        // Layer 3 is negative Y face\n        else if (face == 3) {\n            cx = u;\n            cy = -1;\n            cz = -v;\n        }\n        // Layer 4 is positive Z face\n        else if (face == 4) {\n            cx = u;\n            cy = -v;\n            cz = 1;\n        }\n        // Layer 4 is negative Z face\n        else if (face == 5) {\n            cx = -u;\n            cy = -v;\n            cz = -1;\n        }\n\n        // read from texture, do expected transformation and write to global memory\n        g_odata[face * width * width + y * width + x] = -texCubemap<float>(tex, cx, cy, cz);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    bool bResult = true;\n\n    // get number of SMs on this GPU\n    cudaDeviceProp deviceProps;\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors \", deviceProps.name, deviceProps.multiProcessorCount);\n    printf(\"SM %d.%d\\n\", deviceProps.major, deviceProps.minor);\n\n    if (deviceProps.major < 2) {\n        printf(\"%s requires SM 2.0 or higher for support of Texture Arrays.  Test \"\n               \"will exit... \\n\",\n               sSDKname);\n\n        exit(EXIT_WAIVED);\n    }\n\n    // generate input data for layered texture\n    unsigned int width = 64, num_faces = 6, num_layers = 1;\n    unsigned int cubemap_size = width * width * num_faces;\n    unsigned int size         = cubemap_size * num_layers * sizeof(float);\n    float       *h_data       = (float *)malloc(size);\n\n    for (int i = 0; i < (int)(cubemap_size * num_layers); i++) {\n        h_data[i] = (float)i;\n    }\n\n    // this is the expected transformation of the input data (the expected output)\n    float *h_data_ref = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++) {\n        for (int i = 0; i < (int)(cubemap_size); i++) {\n            h_data_ref[layer * cubemap_size + i] = -h_data[layer * cubemap_size + i] + layer;\n        }\n    }\n\n    // allocate device memory for result\n    float *d_data = NULL;\n    checkCudaErrors(cudaMalloc((void **)&d_data, size));\n\n    // allocate array and copy image data\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    cudaArray            *cu_3darray;\n    //    checkCudaErrors(cudaMalloc3DArray( &cu_3darray, &channelDesc,\n    //    make_cudaExtent(width, height, num_layers), cudaArrayLayered ));\n    checkCudaErrors(\n        cudaMalloc3DArray(&cu_3darray, &channelDesc, make_cudaExtent(width, width, num_faces), cudaArrayCubemap));\n    cudaMemcpy3DParms myparms = {0};\n    myparms.srcPos            = make_cudaPos(0, 0, 0);\n    myparms.dstPos            = make_cudaPos(0, 0, 0);\n    myparms.srcPtr            = make_cudaPitchedPtr(h_data, width * sizeof(float), width, width);\n    myparms.dstArray          = cu_3darray;\n    myparms.extent            = make_cudaExtent(width, width, num_faces);\n    myparms.kind              = cudaMemcpyHostToDevice;\n    checkCudaErrors(cudaMemcpy3D(&myparms));\n\n    cudaTextureObject_t tex;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = cu_3darray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.addressMode[2]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, width / dimBlock.y, 1);\n\n    printf(\"Covering Cubemap data array of %d~3 x %d: Grid size is %d x %d, each \"\n           \"block has 8 x 8 threads\\n\",\n           width,\n           num_layers,\n           dimGrid.x,\n           dimGrid.y);\n\n    transformKernel<<<dimGrid, dimBlock>>>(d_data, width,\n                                           tex); // warmup (for better timing)\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"warmup Kernel execution failed\");\n\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(d_data, width, tex);\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %.3f msec\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mtexlookups/sec\\n\", (cubemap_size / (sdkGetTimerValue(&timer) / 1000.0f) / 1e6));\n    sdkDeleteTimer(&timer);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(h_odata, d_data, size, cudaMemcpyDeviceToHost));\n\n    // write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", h_odata, width * width, 0.0f, false);\n    }\n    else {\n        printf(\"Comparing kernel output to expected data\\n\");\n\n#define MIN_EPSILON_ERROR 5e-3f\n        bResult = compareData(h_odata, h_data_ref, cubemap_size, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_data);\n    free(h_data_ref);\n    free(h_odata);\n\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n    checkCudaErrors(cudaFree(d_data));\n    checkCudaErrors(cudaFreeArray(cu_3darray));\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates how to use texture fetches from layered 2D textures\n * in CUDA C\n *\n * This sample first generates a 3D input data array for the layered texture\n * and the expected output. Then it starts CUDA C kernels, one for each layer,\n * which fetch their layer's texture data (using normalized texture coordinates)\n * transform it to the expected output, and write it to a 3D output data array.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes CUDA\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\nstatic const char *sSDKname = \"simpleCubemapTexture\";\n\n// includes, kernels\n\n////////////////////////////////////////////////////////////////////////////////\n//! Transform a cubemap face of a linear buffe using cubemap texture lookups\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void transformKernel(float *g_odata, int width, hipTextureObject_t tex)\n{\n    // calculate this thread's data point\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // 0.5f offset and division are necessary to access the original data points\n    // in the texture (such that bilinear interpolation will not be activated).\n    // For details, see also CUDA Programming Guide, Appendix D\n\n    float u = ((x + 0.5f) / (float)width) * 2.f - 1.f;\n    float v = ((y + 0.5f) / (float)width) * 2.f - 1.f;\n\n    float cx, cy, cz;\n\n    for (unsigned int face = 0; face < 6; face++) {\n        // Layer 0 is positive X face\n        if (face == 0) {\n            cx = 1;\n            cy = -v;\n            cz = -u;\n        }\n        // Layer 1 is negative X face\n        else if (face == 1) {\n            cx = -1;\n            cy = -v;\n            cz = u;\n        }\n        // Layer 2 is positive Y face\n        else if (face == 2) {\n            cx = u;\n            cy = 1;\n            cz = v;\n        }\n        // Layer 3 is negative Y face\n        else if (face == 3) {\n            cx = u;\n            cy = -1;\n            cz = -v;\n        }\n        // Layer 4 is positive Z face\n        else if (face == 4) {\n            cx = u;\n            cy = -v;\n            cz = 1;\n        }\n        // Layer 4 is negative Z face\n        else if (face == 5) {\n            cx = -u;\n            cy = -v;\n            cz = -1;\n        }\n\n        // read from texture, do expected transformation and write to global memory\n        g_odata[face * width * width + y * width + x] = -texCubemap<float>(tex, cx, cy, cz);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    bool bResult = true;\n\n    // get number of SMs on this GPU\n    hipDeviceProp_t deviceProps;\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors \", deviceProps.name, deviceProps.multiProcessorCount);\n    printf(\"SM %d.%d\\n\", deviceProps.major, deviceProps.minor);\n\n    if (deviceProps.major < 2) {\n        printf(\"%s requires SM 2.0 or higher for support of Texture Arrays.  Test \"\n               \"will exit... \\n\",\n               sSDKname);\n\n        exit(EXIT_WAIVED);\n    }\n\n    // generate input data for layered texture\n    unsigned int width = 64, num_faces = 6, num_layers = 1;\n    unsigned int cubemap_size = width * width * num_faces;\n    unsigned int size         = cubemap_size * num_layers * sizeof(float);\n    float       *h_data       = (float *)malloc(size);\n\n    for (int i = 0; i < (int)(cubemap_size * num_layers); i++) {\n        h_data[i] = (float)i;\n    }\n\n    // this is the expected transformation of the input data (the expected output)\n    float *h_data_ref = (float *)malloc(size);\n\n    for (unsigned int layer = 0; layer < num_layers; layer++) {\n        for (int i = 0; i < (int)(cubemap_size); i++) {\n            h_data_ref[layer * cubemap_size + i] = -h_data[layer * cubemap_size + i] + layer;\n        }\n    }\n\n    // allocate device memory for result\n    float *d_data = NULL;\n    checkCudaErrors(hipMalloc((void **)&d_data, size));\n\n    // allocate array and copy image data\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    hipArray            *cu_3darray;\n    //    checkCudaErrors(cudaMalloc3DArray( &cu_3darray, &channelDesc,\n    //    make_cudaExtent(width, height, num_layers), cudaArrayLayered ));\n    checkCudaErrors(\n        hipMalloc3DArray(&cu_3darray, &channelDesc, make_hipExtent(width, width, num_faces), hipArrayCubemap));\n    hipMemcpy3DParms myparms = {0};\n    myparms.srcPos            = make_hipPos(0, 0, 0);\n    myparms.dstPos            = make_hipPos(0, 0, 0);\n    myparms.srcPtr            = make_hipPitchedPtr(h_data, width * sizeof(float), width, width);\n    myparms.dstArray          = cu_3darray;\n    myparms.extent            = make_hipExtent(width, width, num_faces);\n    myparms.kind              = hipMemcpyHostToDevice;\n    checkCudaErrors(hipMemcpy3D(&myparms));\n\n    hipTextureObject_t tex;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = cu_3darray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.addressMode[2]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(width / dimBlock.x, width / dimBlock.y, 1);\n\n    printf(\"Covering Cubemap data array of %d~3 x %d: Grid size is %d x %d, each \"\n           \"block has 8 x 8 threads\\n\",\n           width,\n           num_layers,\n           dimGrid.x,\n           dimGrid.y);\n\n    transformKernel<<<dimGrid, dimBlock>>>(d_data, width,\n                                           tex); // warmup (for better timing)\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"warmup Kernel execution failed\");\n\n    checkCudaErrors(hipDeviceSynchronize());\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkStartTimer(&timer);\n\n    // execute the kernel\n    transformKernel<<<dimGrid, dimBlock, 0>>>(d_data, width, tex);\n\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %.3f msec\\n\", sdkGetTimerValue(&timer));\n    printf(\"%.2f Mtexlookups/sec\\n\", (cubemap_size / (sdkGetTimerValue(&timer) / 1000.0f) / 1e6));\n    sdkDeleteTimer(&timer);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(h_odata, d_data, size, hipMemcpyDeviceToHost));\n\n    // write regression file if necessary\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        sdkWriteFile<float>(\"./data/regression.dat\", h_odata, width * width, 0.0f, false);\n    }\n    else {\n        printf(\"Comparing kernel output to expected data\\n\");\n\n#define MIN_EPSILON_ERROR 5e-3f\n        bResult = compareData(h_odata, h_data_ref, cubemap_size, MIN_EPSILON_ERROR, 0.0f);\n    }\n\n    // cleanup memory\n    free(h_data);\n    free(h_data_ref);\n    free(h_odata);\n\n    checkCudaErrors(hipDestroyTextureObject(tex));\n    checkCudaErrors(hipFree(d_data));\n    checkCudaErrors(hipFreeArray(cu_3darray));\n\n    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleCubemapTexture/simpleCubemapTexture.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n\n#include <helper_cuda.h>\n\n// clamp x to range [a, b]\n__device__ float clamp(float x, float a, float b) { return max(a, min(b, x)); }\n\n__device__ int clamp(int x, int a, int b) { return max(a, min(b, x)); }\n\n// convert floating point rgb color to 8-bit integer\n__device__ int rgbToInt(float r, float g, float b)\n{\n    r = clamp(r, 0.0f, 255.0f);\n    g = clamp(g, 0.0f, 255.0f);\n    b = clamp(b, 0.0f, 255.0f);\n    return (int(b) << 16) | (int(g) << 8) | int(r);\n}\n\n__global__ void cudaProcess(unsigned int *g_odata, int imgw)\n{\n    extern __shared__ uchar4 sdata[];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bw = blockDim.x;\n    int bh = blockDim.y;\n    int x  = blockIdx.x * bw + tx;\n    int y  = blockIdx.y * bh + ty;\n\n    uchar4 c4             = make_uchar4((x & 0x20) ? 100 : 0, 0, (y & 0x20) ? 100 : 0, 0);\n    g_odata[y * imgw + x] = rgbToInt(c4.z, c4.y, c4.x);\n}\n\nextern \"C\" void launch_cudaProcess(dim3 grid, dim3 block, int sbytes, unsigned int *g_odata, int imgw)\n{\n    cudaProcess<<<grid, block, sbytes>>>(g_odata, imgw);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n\n// clamp x to range [a, b]\n__device__ float clamp(float x, float a, float b) { return max(a, min(b, x)); }\n\n__device__ int clamp(int x, int a, int b) { return max(a, min(b, x)); }\n\n// convert floating point rgb color to 8-bit integer\n__device__ int rgbToInt(float r, float g, float b)\n{\n    r = clamp(r, 0.0f, 255.0f);\n    g = clamp(g, 0.0f, 255.0f);\n    b = clamp(b, 0.0f, 255.0f);\n    return (int(b) << 16) | (int(g) << 8) | int(r);\n}\n\n__global__ void cudaProcess(unsigned int *g_odata, int imgw)\n{\n    extern __shared__ uchar4 sdata[];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bw = blockDim.x;\n    int bh = blockDim.y;\n    int x  = blockIdx.x * bw + tx;\n    int y  = blockIdx.y * bh + ty;\n\n    uchar4 c4             = make_uchar4((x & 0x20) ? 100 : 0, 0, (y & 0x20) ? 100 : 0, 0);\n    g_odata[y * imgw + x] = rgbToInt(c4.z, c4.y, c4.x);\n}\n\nextern \"C\" void launch_cudaProcess(dim3 grid, dim3 block, int sbytes, unsigned int *g_odata, int imgw)\n{\n    cudaProcess<<<grid, block, sbytes>>>(g_odata, imgw);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleCUDA2GL/simpleCUDA2GL.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* This sample is a templatized version of the template project.\n * It also shows how to correctly templatize dynamically allocated shared\n * memory arrays.\n * Host code.\n */\n\n// System includes\n#include <assert.h>\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n// includes, kernels\n#include \"sharedmem.cuh\"\n\nint g_TotalFailures = 0;\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param g_idata  input data in global memory\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> __global__ void testKernel(T *g_idata, T *g_odata)\n{\n    // Shared mem size is determined by the host app at run time\n    SharedMemory<T> smem;\n    T              *sdata = smem.getPointer();\n\n    // access thread id\n    const unsigned int tid = threadIdx.x;\n    // access number of threads in this block\n    const unsigned int num_threads = blockDim.x;\n\n    // read in input data from global memory\n    sdata[tid] = g_idata[tid];\n    __syncthreads();\n\n    // perform some computations\n    sdata[tid] = (T)num_threads * sdata[tid];\n    __syncthreads();\n\n    // write data to global memory\n    g_odata[tid] = sdata[tid];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\ntemplate <class T> void runTest(int argc, char **argv, int len);\n\ntemplate <class T> void computeGold(T *reference, T *idata, const unsigned int len)\n{\n    const T T_len = static_cast<T>(len);\n\n    for (unsigned int i = 0; i < len; ++i) {\n        reference[i] = idata[i] * T_len;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"> runTest<float,32>\\n\");\n    runTest<float>(argc, argv, 32);\n    printf(\"> runTest<int,64>\\n\");\n    runTest<int>(argc, argv, 64);\n\n    printf(\"\\n[simpleTemplates] -> Test Results: %d Failures\\n\", g_TotalFailures);\n\n    exit(g_TotalFailures == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n// To completely templatize runTest (below) with cutil, we need to use\n// template specialization to wrap up CUTIL's array comparison and file writing\n// functions for different types.\n\n// Here's the generic wrapper for cutCompare*\ntemplate <class T> class ArrayComparator\n{\npublic:\n    bool compare(const T *reference, T *data, unsigned int len)\n    {\n        fprintf(stderr, \"Error: no comparison function implemented for this type\\n\");\n        return false;\n    }\n};\n\n// Here's the specialization for ints:\ntemplate <> class ArrayComparator<int>\n{\npublic:\n    bool compare(const int *reference, int *data, unsigned int len)\n    {\n        return compareData(reference, data, len, 0.15f, 0.0f);\n    }\n};\n\n// Here's the specialization for floats:\ntemplate <> class ArrayComparator<float>\n{\npublic:\n    bool compare(const float *reference, float *data, unsigned int len)\n    {\n        return compareData(reference, data, len, 0.15f, 0.15f);\n    }\n};\n\n// Here's the generic wrapper for cutWriteFile*\ntemplate <class T> class ArrayFileWriter\n{\npublic:\n    bool write(const char *filename, T *data, unsigned int len, float epsilon)\n    {\n        fprintf(stderr, \"Error: no file write function implemented for this type\\n\");\n        return false;\n    }\n};\n\n// Here's the specialization for ints:\ntemplate <> class ArrayFileWriter<int>\n{\npublic:\n    bool write(const char *filename, int *data, unsigned int len, float epsilon)\n    {\n        return sdkWriteFile(filename, data, len, epsilon, false);\n    }\n};\n\n// Here's the specialization for floats:\ntemplate <> class ArrayFileWriter<float>\n{\npublic:\n    bool write(const char *filename, float *data, unsigned int len, float epsilon)\n    {\n        return sdkWriteFile(filename, data, len, epsilon, false);\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> void runTest(int argc, char **argv, int len)\n{\n    int            devID;\n    cudaDeviceProp deviceProps;\n\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get number of SMs on this GPU\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors\\n\", deviceProps.name, deviceProps.multiProcessorCount);\n\n    // create and start timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // start the timer\n    sdkStartTimer(&timer);\n\n    unsigned int num_threads = len;\n    unsigned int mem_size    = sizeof(float) * num_threads;\n\n    // allocate host memory\n    T *h_idata = (T *)malloc(mem_size);\n\n    // initialize the memory\n    for (unsigned int i = 0; i < num_threads; ++i) {\n        h_idata[i] = (T)i;\n    }\n\n    // allocate device memory\n    T *d_idata;\n    checkCudaErrors(cudaMalloc((void **)&d_idata, mem_size));\n    // copy host memory to device\n    checkCudaErrors(cudaMemcpy(d_idata, h_idata, mem_size, cudaMemcpyHostToDevice));\n\n    // allocate device memory for result\n    T *d_odata;\n    checkCudaErrors(cudaMalloc((void **)&d_odata, mem_size));\n\n    // setup execution parameters\n    dim3 grid(1, 1, 1);\n    dim3 threads(num_threads, 1, 1);\n\n    // execute the kernel\n    testKernel<T><<<grid, threads, mem_size>>>(d_idata, d_odata);\n\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // allocate mem for the result on host side\n    T *h_odata = (T *)malloc(mem_size);\n    // copy result from device to host\n    checkCudaErrors(cudaMemcpy(h_odata, d_odata, sizeof(T) * num_threads, cudaMemcpyDeviceToHost));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // compute reference solution\n    T *reference = (T *)malloc(mem_size);\n    computeGold<T>(reference, h_idata, num_threads);\n\n    ArrayComparator<T> comparator;\n    ArrayFileWriter<T> writer;\n\n    // check result\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        writer.write(\"./data/regression.dat\", h_odata, num_threads, 0.0f);\n    }\n    else {\n        // custom output handling when no regression test running\n        // in this case check if the result is equivalent to the expected solution\n        bool res = comparator.compare(reference, h_odata, num_threads);\n        printf(\"Compare %s\\n\\n\", (1 == res) ? \"OK\" : \"MISMATCH\");\n        g_TotalFailures += (1 != res);\n    }\n\n    // cleanup memory\n    free(h_idata);\n    free(h_odata);\n    free(reference);\n    checkCudaErrors(cudaFree(d_idata));\n    checkCudaErrors(cudaFree(d_odata));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* This sample is a templatized version of the template project.\n * It also shows how to correctly templatize dynamically allocated shared\n * memory arrays.\n * Host code.\n */\n\n// System includes\n#include <assert.h>\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#ifndef MAX\n#define MAX(a, b) (a > b ? a : b)\n#endif\n\n// includes, kernels\n#include \"sharedmem.cuh\"\n\nint g_TotalFailures = 0;\n\n////////////////////////////////////////////////////////////////////////////////\n//! Simple test kernel for device functionality\n//! @param g_idata  input data in global memory\n//! @param g_odata  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> __global__ void testKernel(T *g_idata, T *g_odata)\n{\n    // Shared mem size is determined by the host app at run time\n    SharedMemory<T> smem;\n    T              *sdata = smem.getPointer();\n\n    // access thread id\n    const unsigned int tid = threadIdx.x;\n    // access number of threads in this block\n    const unsigned int num_threads = blockDim.x;\n\n    // read in input data from global memory\n    sdata[tid] = g_idata[tid];\n    __syncthreads();\n\n    // perform some computations\n    sdata[tid] = (T)num_threads * sdata[tid];\n    __syncthreads();\n\n    // write data to global memory\n    g_odata[tid] = sdata[tid];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\ntemplate <class T> void runTest(int argc, char **argv, int len);\n\ntemplate <class T> void computeGold(T *reference, T *idata, const unsigned int len)\n{\n    const T T_len = static_cast<T>(len);\n\n    for (unsigned int i = 0; i < len; ++i) {\n        reference[i] = idata[i] * T_len;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"> runTest<float,32>\\n\");\n    runTest<float>(argc, argv, 32);\n    printf(\"> runTest<int,64>\\n\");\n    runTest<int>(argc, argv, 64);\n\n    printf(\"\\n[simpleTemplates] -> Test Results: %d Failures\\n\", g_TotalFailures);\n\n    exit(g_TotalFailures == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n// To completely templatize runTest (below) with cutil, we need to use\n// template specialization to wrap up CUTIL's array comparison and file writing\n// functions for different types.\n\n// Here's the generic wrapper for cutCompare*\ntemplate <class T> class ArrayComparator\n{\npublic:\n    bool compare(const T *reference, T *data, unsigned int len)\n    {\n        fprintf(stderr, \"Error: no comparison function implemented for this type\\n\");\n        return false;\n    }\n};\n\n// Here's the specialization for ints:\ntemplate <> class ArrayComparator<int>\n{\npublic:\n    bool compare(const int *reference, int *data, unsigned int len)\n    {\n        return compareData(reference, data, len, 0.15f, 0.0f);\n    }\n};\n\n// Here's the specialization for floats:\ntemplate <> class ArrayComparator<float>\n{\npublic:\n    bool compare(const float *reference, float *data, unsigned int len)\n    {\n        return compareData(reference, data, len, 0.15f, 0.15f);\n    }\n};\n\n// Here's the generic wrapper for cutWriteFile*\ntemplate <class T> class ArrayFileWriter\n{\npublic:\n    bool write(const char *filename, T *data, unsigned int len, float epsilon)\n    {\n        fprintf(stderr, \"Error: no file write function implemented for this type\\n\");\n        return false;\n    }\n};\n\n// Here's the specialization for ints:\ntemplate <> class ArrayFileWriter<int>\n{\npublic:\n    bool write(const char *filename, int *data, unsigned int len, float epsilon)\n    {\n        return sdkWriteFile(filename, data, len, epsilon, false);\n    }\n};\n\n// Here's the specialization for floats:\ntemplate <> class ArrayFileWriter<float>\n{\npublic:\n    bool write(const char *filename, float *data, unsigned int len, float epsilon)\n    {\n        return sdkWriteFile(filename, data, len, epsilon, false);\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> void runTest(int argc, char **argv, int len)\n{\n    int            devID;\n    hipDeviceProp_t deviceProps;\n\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get number of SMs on this GPU\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s] has %d Multi-Processors\\n\", deviceProps.name, deviceProps.multiProcessorCount);\n\n    // create and start timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // start the timer\n    sdkStartTimer(&timer);\n\n    unsigned int num_threads = len;\n    unsigned int mem_size    = sizeof(float) * num_threads;\n\n    // allocate host memory\n    T *h_idata = (T *)malloc(mem_size);\n\n    // initialize the memory\n    for (unsigned int i = 0; i < num_threads; ++i) {\n        h_idata[i] = (T)i;\n    }\n\n    // allocate device memory\n    T *d_idata;\n    checkCudaErrors(hipMalloc((void **)&d_idata, mem_size));\n    // copy host memory to device\n    checkCudaErrors(hipMemcpy(d_idata, h_idata, mem_size, hipMemcpyHostToDevice));\n\n    // allocate device memory for result\n    T *d_odata;\n    checkCudaErrors(hipMalloc((void **)&d_odata, mem_size));\n\n    // setup execution parameters\n    dim3 grid(1, 1, 1);\n    dim3 threads(num_threads, 1, 1);\n\n    // execute the kernel\n    testKernel<T><<<grid, threads, mem_size>>>(d_idata, d_odata);\n\n    // check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed\");\n\n    // allocate mem for the result on host side\n    T *h_odata = (T *)malloc(mem_size);\n    // copy result from device to host\n    checkCudaErrors(hipMemcpy(h_odata, d_odata, sizeof(T) * num_threads, hipMemcpyDeviceToHost));\n\n    sdkStopTimer(&timer);\n    printf(\"Processing time: %f (ms)\\n\", sdkGetTimerValue(&timer));\n    sdkDeleteTimer(&timer);\n\n    // compute reference solution\n    T *reference = (T *)malloc(mem_size);\n    computeGold<T>(reference, h_idata, num_threads);\n\n    ArrayComparator<T> comparator;\n    ArrayFileWriter<T> writer;\n\n    // check result\n    if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n        // write file for regression test\n        writer.write(\"./data/regression.dat\", h_odata, num_threads, 0.0f);\n    }\n    else {\n        // custom output handling when no regression test running\n        // in this case check if the result is equivalent to the expected solution\n        bool res = comparator.compare(reference, h_odata, num_threads);\n        printf(\"Compare %s\\n\\n\", (1 == res) ? \"OK\" : \"MISMATCH\");\n        g_TotalFailures += (1 != res);\n    }\n\n    // cleanup memory\n    free(h_idata);\n    free(h_odata);\n    free(reference);\n    checkCudaErrors(hipFree(d_idata));\n    checkCudaErrors(hipFree(d_odata));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/simpleTemplates/simpleTemplates.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <stdio.h>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numElements) {\n        C[i] = A[i] + B[i] + 0.0f;\n    }\n}\n\n/**\n * Host main routine\n */\nint main(void)\n{\n    // Error code to check return values for CUDA calls\n    cudaError_t err = cudaSuccess;\n\n    // Print the vector length to be used, and compute its size\n    int    numElements = 50000;\n    size_t size        = numElements * sizeof(float);\n    printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n    // Allocate the host input vector A\n    float *h_A = (float *)malloc(size);\n\n    // Allocate the host input vector B\n    float *h_B = (float *)malloc(size);\n\n    // Allocate the host output vector C\n    float *h_C = (float *)malloc(size);\n\n    // Verify that allocations succeeded\n    if (h_A == NULL || h_B == NULL || h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host vectors!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Initialize the host input vectors\n    for (int i = 0; i < numElements; ++i) {\n        h_A[i] = rand() / (float)RAND_MAX;\n        h_B[i] = rand() / (float)RAND_MAX;\n    }\n\n    // Allocate the device input vector A\n    float *d_A = NULL;\n    err        = cudaMalloc((void **)&d_A, size);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector A (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate the device input vector B\n    float *d_B = NULL;\n    err        = cudaMalloc((void **)&d_B, size);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector B (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate the device output vector C\n    float *d_C = NULL;\n    err        = cudaMalloc((void **)&d_C, size);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector C (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Copy the host input vectors A and B in host memory to the device input\n    // vectors in\n    // device memory\n    printf(\"Copy input data from the host memory to the CUDA device\\n\");\n    err = cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to copy vector A from host to device (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to copy vector B from host to device (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Launch the Vector Add CUDA Kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid   = (numElements + threadsPerBlock - 1) / threadsPerBlock;\n    printf(\"CUDA kernel launch with %d blocks of %d threads\\n\", blocksPerGrid, threadsPerBlock);\n    vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);\n    err = cudaGetLastError();\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to launch vectorAdd kernel (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Copy the device result vector in device memory to the host result vector\n    // in host memory.\n    printf(\"Copy output data from the CUDA device to the host memory\\n\");\n    err = cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to copy vector C from device to host (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Verify that the result vector is correct\n    for (int i = 0; i < numElements; ++i) {\n        if (fabs(h_A[i] + h_B[i] - h_C[i]) > 1e-5) {\n            fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    printf(\"Test PASSED\\n\");\n\n    // Free device global memory\n    err = cudaFree(d_A);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to free device vector A (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = cudaFree(d_B);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to free device vector B (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = cudaFree(d_C);\n\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"Failed to free device vector C (error code %s)!\\n\", cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Free host memory\n    free(h_A);\n    free(h_B);\n    free(h_C);\n\n    printf(\"Done\\n\");\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <stdio.h>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)\n{\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numElements) {\n        C[i] = A[i] + B[i] + 0.0f;\n    }\n}\n\n/**\n * Host main routine\n */\nint main(void)\n{\n    // Error code to check return values for CUDA calls\n    hipError_t err = hipSuccess;\n\n    // Print the vector length to be used, and compute its size\n    int    numElements = 50000;\n    size_t size        = numElements * sizeof(float);\n    printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n    // Allocate the host input vector A\n    float *h_A = (float *)malloc(size);\n\n    // Allocate the host input vector B\n    float *h_B = (float *)malloc(size);\n\n    // Allocate the host output vector C\n    float *h_C = (float *)malloc(size);\n\n    // Verify that allocations succeeded\n    if (h_A == NULL || h_B == NULL || h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host vectors!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Initialize the host input vectors\n    for (int i = 0; i < numElements; ++i) {\n        h_A[i] = rand() / (float)RAND_MAX;\n        h_B[i] = rand() / (float)RAND_MAX;\n    }\n\n    // Allocate the device input vector A\n    float *d_A = NULL;\n    err        = hipMalloc((void **)&d_A, size);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector A (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate the device input vector B\n    float *d_B = NULL;\n    err        = hipMalloc((void **)&d_B, size);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector B (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate the device output vector C\n    float *d_C = NULL;\n    err        = hipMalloc((void **)&d_C, size);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to allocate device vector C (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Copy the host input vectors A and B in host memory to the device input\n    // vectors in\n    // device memory\n    printf(\"Copy input data from the host memory to the CUDA device\\n\");\n    err = hipMemcpy(d_A, h_A, size, hipMemcpyHostToDevice);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to copy vector A from host to device (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = hipMemcpy(d_B, h_B, size, hipMemcpyHostToDevice);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to copy vector B from host to device (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Launch the Vector Add CUDA Kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid   = (numElements + threadsPerBlock - 1) / threadsPerBlock;\n    printf(\"CUDA kernel launch with %d blocks of %d threads\\n\", blocksPerGrid, threadsPerBlock);\n    vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);\n    err = hipGetLastError();\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to launch vectorAdd kernel (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Copy the device result vector in device memory to the host result vector\n    // in host memory.\n    printf(\"Copy output data from the CUDA device to the host memory\\n\");\n    err = hipMemcpy(h_C, d_C, size, hipMemcpyDeviceToHost);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to copy vector C from device to host (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Verify that the result vector is correct\n    for (int i = 0; i < numElements; ++i) {\n        if (fabs(h_A[i] + h_B[i] - h_C[i]) > 1e-5) {\n            fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    printf(\"Test PASSED\\n\");\n\n    // Free device global memory\n    err = hipFree(d_A);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to free device vector A (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = hipFree(d_B);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to free device vector B (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    err = hipFree(d_C);\n\n    if (err != hipSuccess) {\n        fprintf(stderr, \"Failed to free device vector C (error code %s)!\\n\", hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n\n    // Free host memory\n    free(h_A);\n    free(h_B);\n    free(h_C);\n\n    printf(\"Done\\n\");\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/vectorAdd/vectorAdd.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates the usage of CUDA events for both GPU timing and\n * overlapping CPU and GPU execution.  Events are inserted into a stream\n * of CUDA calls.  Since CUDA stream calls are asynchronous, the CPU can\n * perform computations while GPU is executing (including DMA memcopies\n * between the host and device).  CPU can query CUDA events to determine\n * whether GPU has completed tasks.\n */\n\n// includes, system\n#include <stdio.h>\n\n// includes CUDA Runtime\n#include <cuda_profiler_api.h>\n#include <cuda_runtime.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h> // helper utility functions\n\n__global__ void increment_kernel(int *g_data, int inc_value)\n{\n    int idx     = blockIdx.x * blockDim.x + threadIdx.x;\n    g_data[idx] = g_data[idx] + inc_value;\n}\n\nbool correct_output(int *data, const int n, const int x)\n{\n    for (int i = 0; i < n; i++)\n        if (data[i] != x) {\n            printf(\"Error! data[%d] = %d, ref = %d\\n\", i, data[i], x);\n            return false;\n        }\n\n    return true;\n}\n\nint main(int argc, char *argv[])\n{\n    int            devID;\n    cudaDeviceProp deviceProps;\n\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // This will pick the best possible CUDA capable device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get device name\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s]\\n\", deviceProps.name);\n\n    int n      = 16 * 1024 * 1024;\n    int nbytes = n * sizeof(int);\n    int value  = 26;\n\n    // allocate host memory\n    int *a = 0;\n    checkCudaErrors(cudaMallocHost((void **)&a, nbytes));\n    memset(a, 0, nbytes);\n\n    // allocate device memory\n    int *d_a = 0;\n    checkCudaErrors(cudaMalloc((void **)&d_a, nbytes));\n    checkCudaErrors(cudaMemset(d_a, 255, nbytes));\n\n    // set kernel launch configuration\n    dim3 threads = dim3(512, 1);\n    dim3 blocks  = dim3(n / threads.x, 1);\n\n    // create cuda event handles\n    cudaEvent_t start, stop;\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkResetTimer(&timer);\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    float gpu_time = 0.0f;\n\n    // asynchronously issue work to the GPU (all to stream 0)\n    checkCudaErrors(cudaProfilerStart());\n    sdkStartTimer(&timer);\n    cudaEventRecord(start, 0);\n    cudaMemcpyAsync(d_a, a, nbytes, cudaMemcpyHostToDevice, 0);\n    increment_kernel<<<blocks, threads, 0, 0>>>(d_a, value);\n    cudaMemcpyAsync(a, d_a, nbytes, cudaMemcpyDeviceToHost, 0);\n    cudaEventRecord(stop, 0);\n    sdkStopTimer(&timer);\n    checkCudaErrors(cudaProfilerStop());\n\n    // have CPU do some work while waiting for stage 1 to finish\n    unsigned long int counter = 0;\n\n    while (cudaEventQuery(stop) == cudaErrorNotReady) {\n        counter++;\n    }\n\n    checkCudaErrors(cudaEventElapsedTime(&gpu_time, start, stop));\n\n    // print the cpu and gpu times\n    printf(\"time spent executing by the GPU: %.2f\\n\", gpu_time);\n    printf(\"time spent by CPU in CUDA calls: %.2f\\n\", sdkGetTimerValue(&timer));\n    printf(\"CPU executed %lu iterations while waiting for GPU to finish\\n\", counter);\n\n    // check the output for correctness\n    bool bFinalResults = correct_output(a, n, value);\n\n    // release resources\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n    checkCudaErrors(cudaFreeHost(a));\n    checkCudaErrors(cudaFree(d_a));\n\n    exit(bFinalResults ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates the usage of CUDA events for both GPU timing and\n * overlapping CPU and GPU execution.  Events are inserted into a stream\n * of CUDA calls.  Since CUDA stream calls are asynchronous, the CPU can\n * perform computations while GPU is executing (including DMA memcopies\n * between the host and device).  CPU can query CUDA events to determine\n * whether GPU has completed tasks.\n */\n\n// includes, system\n#include <stdio.h>\n\n// includes CUDA Runtime\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_runtime.h>\n\n// includes, project\n#include <helper_functions.h> // helper utility functions\n\n__global__ void increment_kernel(int *g_data, int inc_value)\n{\n    int idx     = blockIdx.x * blockDim.x + threadIdx.x;\n    g_data[idx] = g_data[idx] + inc_value;\n}\n\nbool correct_output(int *data, const int n, const int x)\n{\n    for (int i = 0; i < n; i++)\n        if (data[i] != x) {\n            printf(\"Error! data[%d] = %d, ref = %d\\n\", i, data[i], x);\n            return false;\n        }\n\n    return true;\n}\n\nint main(int argc, char *argv[])\n{\n    int            devID;\n    hipDeviceProp_t deviceProps;\n\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // This will pick the best possible CUDA capable device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get device name\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, devID));\n    printf(\"CUDA device [%s]\\n\", deviceProps.name);\n\n    int n      = 16 * 1024 * 1024;\n    int nbytes = n * sizeof(int);\n    int value  = 26;\n\n    // allocate host memory\n    int *a = 0;\n    checkCudaErrors(hipHostMalloc((void **)&a, nbytes));\n    memset(a, 0, nbytes);\n\n    // allocate device memory\n    int *d_a = 0;\n    checkCudaErrors(hipMalloc((void **)&d_a, nbytes));\n    checkCudaErrors(hipMemset(d_a, 255, nbytes));\n\n    // set kernel launch configuration\n    dim3 threads = dim3(512, 1);\n    dim3 blocks  = dim3(n / threads.x, 1);\n\n    // create cuda event handles\n    hipEvent_t start, stop;\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n    sdkResetTimer(&timer);\n\n    checkCudaErrors(hipDeviceSynchronize());\n    float gpu_time = 0.0f;\n\n    // asynchronously issue work to the GPU (all to stream 0)\n    checkCudaErrors(hipProfilerStart());\n    sdkStartTimer(&timer);\n    hipEventRecord(start, 0);\n    hipMemcpyAsync(d_a, a, nbytes, hipMemcpyHostToDevice, 0);\n    increment_kernel<<<blocks, threads, 0, 0>>>(d_a, value);\n    hipMemcpyAsync(a, d_a, nbytes, hipMemcpyDeviceToHost, 0);\n    hipEventRecord(stop, 0);\n    sdkStopTimer(&timer);\n    checkCudaErrors(hipProfilerStop());\n\n    // have CPU do some work while waiting for stage 1 to finish\n    unsigned long int counter = 0;\n\n    while (hipEventQuery(stop) == hipErrorNotReady) {\n        counter++;\n    }\n\n    checkCudaErrors(hipEventElapsedTime(&gpu_time, start, stop));\n\n    // print the cpu and gpu times\n    printf(\"time spent executing by the GPU: %.2f\\n\", gpu_time);\n    printf(\"time spent by CPU in CUDA calls: %.2f\\n\", sdkGetTimerValue(&timer));\n    printf(\"CPU executed %lu iterations while waiting for GPU to finish\\n\", counter);\n\n    // check the output for correctness\n    bool bFinalResults = correct_output(a, n, value);\n\n    // release resources\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n    checkCudaErrors(hipHostFree(a));\n    checkCudaErrors(hipFree(d_a));\n\n    exit(bFinalResults ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/0_Introduction/asyncAPI/asyncAPI.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef QUASIRANDOMGENERATOR_KERNEL_CUH\n#define QUASIRANDOMGENERATOR_KERNEL_CUH\n\n#include <helper_cuda.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"quasirandomGenerator_common.h\"\n\n// Fast integer multiplication\n#define MUL(a, b) __umul24(a, b)\n\n////////////////////////////////////////////////////////////////////////////////\n// Niederreiter quasirandom number generation kernel\n////////////////////////////////////////////////////////////////////////////////\nstatic __constant__ unsigned int c_Table[QRNG_DIMENSIONS][QRNG_RESOLUTION];\n\nstatic __global__ void quasirandomGeneratorKernel(float *d_Output, unsigned int seed, unsigned int N)\n{\n    unsigned int *dimBase = &c_Table[threadIdx.y][0];\n    unsigned int  tid     = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int  threadN = MUL(blockDim.x, gridDim.x);\n\n    for (unsigned int pos = tid; pos < N; pos += threadN) {\n        unsigned int result = 0;\n        unsigned int data   = seed + pos;\n\n        for (int bit = 0; bit < QRNG_RESOLUTION; bit++, data >>= 1)\n            if (data & 1) {\n                result ^= dimBase[bit];\n            }\n\n        d_Output[MUL(threadIdx.y, N) + pos] = (float)(result + 1) * INT_SCALE;\n    }\n}\n\n// Table initialization routine\nextern \"C\" void initTableGPU(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION])\n{\n    checkCudaErrors(cudaMemcpyToSymbol(c_Table, tableCPU, QRNG_DIMENSIONS * QRNG_RESOLUTION * sizeof(unsigned int)));\n}\n\n// Host-side interface\nextern \"C\" void quasirandomGeneratorGPU(float *d_Output, unsigned int seed, unsigned int N)\n{\n    dim3 threads(128, QRNG_DIMENSIONS);\n    quasirandomGeneratorKernel<<<128, threads>>>(d_Output, seed, N);\n    getLastCudaError(\"quasirandomGeneratorKernel() execution failed.\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Moro's Inverse Cumulative Normal Distribution function approximation\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float MoroInvCNDgpu(unsigned int x)\n{\n    const float a1 = 2.50662823884f;\n    const float a2 = -18.61500062529f;\n    const float a3 = 41.39119773534f;\n    const float a4 = -25.44106049637f;\n    const float b1 = -8.4735109309f;\n    const float b2 = 23.08336743743f;\n    const float b3 = -21.06224101826f;\n    const float b4 = 3.13082909833f;\n    const float c1 = 0.337475482272615f;\n    const float c2 = 0.976169019091719f;\n    const float c3 = 0.160797971491821f;\n    const float c4 = 2.76438810333863E-02f;\n    const float c5 = 3.8405729373609E-03f;\n    const float c6 = 3.951896511919E-04f;\n    const float c7 = 3.21767881768E-05f;\n    const float c8 = 2.888167364E-07f;\n    const float c9 = 3.960315187E-07f;\n\n    float z;\n\n    bool negate = false;\n\n    // Ensure the conversion to floating point will give a value in the\n    // range (0,0.5] by restricting the input to the bottom half of the\n    // input domain. We will later reflect the result if the input was\n    // originally in the top half of the input domain\n    if (x >= 0x80000000UL) {\n        x      = 0xffffffffUL - x;\n        negate = true;\n    }\n\n    // x is now in the range [0,0x80000000) (i.e. [0,0x7fffffff])\n    // Convert to floating point in (0,0.5]\n    const float x1 = 1.0f / static_cast<float>(0xffffffffUL);\n    const float x2 = x1 / 2.0f;\n    float       p1 = x * x1 + x2;\n    // Convert to floating point in (-0.5,0]\n    float p2 = p1 - 0.5f;\n\n    // The input to the Moro inversion is p2 which is in the range\n    // (-0.5,0]. This means that our output will be the negative side\n    // of the bell curve (which we will reflect if \"negate\" is true).\n\n    // Main body of the bell curve for |p| < 0.42\n    if (p2 > -0.42f) {\n        z = p2 * p2;\n        z = p2 * (((a4 * z + a3) * z + a2) * z + a1) / ((((b4 * z + b3) * z + b2) * z + b1) * z + 1.0f);\n    }\n    // Special case (Chebychev) for tail\n    else {\n        z = __logf(-__logf(p1));\n        z = -(c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * (c8 + z * c9))))))));\n    }\n\n    // If the original input (x) was in the top half of the range, reflect\n    // to get the positive side of the bell curve\n    return negate ? -z : z;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main kernel. Choose between transforming\n// input sequence and uniform ascending (0, 1) sequence\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void inverseCNDKernel(float *d_Output, unsigned int *d_Input, unsigned int pathN)\n{\n    unsigned int distance = ((unsigned int)-1) / (pathN + 1);\n    unsigned int tid      = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int threadN  = MUL(blockDim.x, gridDim.x);\n\n    // Transform input number sequence if it's supplied\n    if (d_Input) {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = d_Input[pos];\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n    // Else generate input uniformly placed samples on the fly\n    // and write to destination\n    else {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = (pos + 1) * distance;\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n}\n\nextern \"C\" void inverseCNDgpu(float *d_Output, unsigned int *d_Input, unsigned int N)\n{\n    inverseCNDKernel<<<128, 128>>>(d_Output, d_Input, N);\n    getLastCudaError(\"inverseCNDKernel() execution failed.\\n\");\n}\n\n#endif\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef QUASIRANDOMGENERATOR_KERNEL_CUH\n#define QUASIRANDOMGENERATOR_KERNEL_CUH\n\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"quasirandomGenerator_common.h\"\n\n// Fast integer multiplication\n#define MUL(a, b) __umul24(a, b)\n\n////////////////////////////////////////////////////////////////////////////////\n// Niederreiter quasirandom number generation kernel\n////////////////////////////////////////////////////////////////////////////////\nstatic __constant__ unsigned int c_Table[QRNG_DIMENSIONS][QRNG_RESOLUTION];\n\nstatic __global__ void quasirandomGeneratorKernel(float *d_Output, unsigned int seed, unsigned int N)\n{\n    unsigned int *dimBase = &c_Table[threadIdx.y][0];\n    unsigned int  tid     = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int  threadN = MUL(blockDim.x, gridDim.x);\n\n    for (unsigned int pos = tid; pos < N; pos += threadN) {\n        unsigned int result = 0;\n        unsigned int data   = seed + pos;\n\n        for (int bit = 0; bit < QRNG_RESOLUTION; bit++, data >>= 1)\n            if (data & 1) {\n                result ^= dimBase[bit];\n            }\n\n        d_Output[MUL(threadIdx.y, N) + pos] = (float)(result + 1) * INT_SCALE;\n    }\n}\n\n// Table initialization routine\nextern \"C\" void initTableGPU(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION])\n{\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(c_Table), tableCPU, QRNG_DIMENSIONS * QRNG_RESOLUTION * sizeof(unsigned int)));\n}\n\n// Host-side interface\nextern \"C\" void quasirandomGeneratorGPU(float *d_Output, unsigned int seed, unsigned int N)\n{\n    dim3 threads(128, QRNG_DIMENSIONS);\n    quasirandomGeneratorKernel<<<128, threads>>>(d_Output, seed, N);\n    getLastCudaError(\"quasirandomGeneratorKernel() execution failed.\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Moro's Inverse Cumulative Normal Distribution function approximation\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float MoroInvCNDgpu(unsigned int x)\n{\n    const float a1 = 2.50662823884f;\n    const float a2 = -18.61500062529f;\n    const float a3 = 41.39119773534f;\n    const float a4 = -25.44106049637f;\n    const float b1 = -8.4735109309f;\n    const float b2 = 23.08336743743f;\n    const float b3 = -21.06224101826f;\n    const float b4 = 3.13082909833f;\n    const float c1 = 0.337475482272615f;\n    const float c2 = 0.976169019091719f;\n    const float c3 = 0.160797971491821f;\n    const float c4 = 2.76438810333863E-02f;\n    const float c5 = 3.8405729373609E-03f;\n    const float c6 = 3.951896511919E-04f;\n    const float c7 = 3.21767881768E-05f;\n    const float c8 = 2.888167364E-07f;\n    const float c9 = 3.960315187E-07f;\n\n    float z;\n\n    bool negate = false;\n\n    // Ensure the conversion to floating point will give a value in the\n    // range (0,0.5] by restricting the input to the bottom half of the\n    // input domain. We will later reflect the result if the input was\n    // originally in the top half of the input domain\n    if (x >= 0x80000000UL) {\n        x      = 0xffffffffUL - x;\n        negate = true;\n    }\n\n    // x is now in the range [0,0x80000000) (i.e. [0,0x7fffffff])\n    // Convert to floating point in (0,0.5]\n    const float x1 = 1.0f / static_cast<float>(0xffffffffUL);\n    const float x2 = x1 / 2.0f;\n    float       p1 = x * x1 + x2;\n    // Convert to floating point in (-0.5,0]\n    float p2 = p1 - 0.5f;\n\n    // The input to the Moro inversion is p2 which is in the range\n    // (-0.5,0]. This means that our output will be the negative side\n    // of the bell curve (which we will reflect if \"negate\" is true).\n\n    // Main body of the bell curve for |p| < 0.42\n    if (p2 > -0.42f) {\n        z = p2 * p2;\n        z = p2 * (((a4 * z + a3) * z + a2) * z + a1) / ((((b4 * z + b3) * z + b2) * z + b1) * z + 1.0f);\n    }\n    // Special case (Chebychev) for tail\n    else {\n        z = __logf(-__logf(p1));\n        z = -(c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * (c8 + z * c9))))))));\n    }\n\n    // If the original input (x) was in the top half of the range, reflect\n    // to get the positive side of the bell curve\n    return negate ? -z : z;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main kernel. Choose between transforming\n// input sequence and uniform ascending (0, 1) sequence\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void inverseCNDKernel(float *d_Output, unsigned int *d_Input, unsigned int pathN)\n{\n    unsigned int distance = ((unsigned int)-1) / (pathN + 1);\n    unsigned int tid      = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int threadN  = MUL(blockDim.x, gridDim.x);\n\n    // Transform input number sequence if it's supplied\n    if (d_Input) {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = d_Input[pos];\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n    // Else generate input uniformly placed samples on the fly\n    // and write to destination\n    else {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = (pos + 1) * distance;\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n}\n\nextern \"C\" void inverseCNDgpu(float *d_Output, unsigned int *d_Input, unsigned int N)\n{\n    inverseCNDKernel<<<128, 128>>>(d_Output, d_Input, N);\n    getLastCudaError(\"inverseCNDKernel() execution failed.\\n\");\n}\n\n#endif\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/quasirandomGenerator/quasirandomGenerator_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types and parameters\n////////////////////////////////////////////////////////////////////////////////\n#include <cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n\n#include \"binomialOptions_common.h\"\n#include \"realtype.h\"\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real vDt;\n    real puByDf;\n    real pdByDf;\n} __TOptionData;\nstatic __constant__ __TOptionData d_OptionData[MAX_OPTIONS];\nstatic __device__ real            d_CallValue[MAX_OPTIONS];\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n#ifndef DOUBLE_PRECISION\n__device__ inline float expiryCallValue(float S, float X, float vDt, int i)\n{\n    float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;\n    return (d > 0.0F) ? d : 0.0F;\n}\n#else\n__device__ inline double expiryCallValue(double S, double X, double vDt, int i)\n{\n    double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;\n    return (d > 0.0) ? d : 0.0;\n}\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU kernel\n////////////////////////////////////////////////////////////////////////////////\n#define THREADBLOCK_SIZE 128\n#define ELEMS_PER_THREAD (NUM_STEPS / THREADBLOCK_SIZE)\n#if NUM_STEPS % THREADBLOCK_SIZE\n#error Bad constants\n#endif\n\n__global__ void binomialOptionsKernel()\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ real  call_exchange[THREADBLOCK_SIZE + 1];\n\n    const int  tid    = threadIdx.x;\n    const real S      = d_OptionData[blockIdx.x].S;\n    const real X      = d_OptionData[blockIdx.x].X;\n    const real vDt    = d_OptionData[blockIdx.x].vDt;\n    const real puByDf = d_OptionData[blockIdx.x].puByDf;\n    const real pdByDf = d_OptionData[blockIdx.x].pdByDf;\n\n    real call[ELEMS_PER_THREAD + 1];\n#pragma unroll\n    for (int i = 0; i < ELEMS_PER_THREAD; ++i)\n        call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);\n\n    if (tid == 0)\n        call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);\n\n    int final_it = max(0, tid * ELEMS_PER_THREAD - 1);\n\n#pragma unroll 16\n    for (int i = NUM_STEPS; i > 0; --i) {\n        call_exchange[tid] = call[0];\n        cg::sync(cta);\n        call[ELEMS_PER_THREAD] = call_exchange[tid + 1];\n        cg::sync(cta);\n\n        if (i > final_it) {\n#pragma unroll\n            for (int j = 0; j < ELEMS_PER_THREAD; ++j)\n                call[j] = puByDf * call[j + 1] + pdByDf * call[j];\n        }\n    }\n\n    if (tid == 0) {\n        d_CallValue[blockIdx.x] = call[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU binomialOptions\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN)\n{\n    __TOptionData h_OptionData[MAX_OPTIONS];\n\n    for (int i = 0; i < optN; i++) {\n        const real T = optionData[i].T;\n        const real R = optionData[i].R;\n        const real V = optionData[i].V;\n\n        const real dt  = T / (real)NUM_STEPS;\n        const real vDt = V * sqrt(dt);\n        const real rDt = R * dt;\n        // Per-step interest and discount factors\n        const real If = exp(rDt);\n        const real Df = exp(-rDt);\n        // Values and pseudoprobabilities of upward and downward moves\n        const real u      = exp(vDt);\n        const real d      = exp(-vDt);\n        const real pu     = (If - d) / (u - d);\n        const real pd     = (real)1.0 - pu;\n        const real puByDf = pu * Df;\n        const real pdByDf = pd * Df;\n\n        h_OptionData[i].S      = (real)optionData[i].S;\n        h_OptionData[i].X      = (real)optionData[i].X;\n        h_OptionData[i].vDt    = (real)vDt;\n        h_OptionData[i].puByDf = (real)puByDf;\n        h_OptionData[i].pdByDf = (real)pdByDf;\n    }\n\n    checkCudaErrors(cudaMemcpyToSymbol(d_OptionData, h_OptionData, optN * sizeof(__TOptionData)));\n    binomialOptionsKernel<<<optN, THREADBLOCK_SIZE>>>();\n    getLastCudaError(\"binomialOptionsKernel() execution failed.\\n\");\n    checkCudaErrors(cudaMemcpyFromSymbol(callValue, d_CallValue, optN * sizeof(real)));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types and parameters\n////////////////////////////////////////////////////////////////////////////////\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n\n#include \"binomialOptions_common.h\"\n#include \"realtype.h\"\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real vDt;\n    real puByDf;\n    real pdByDf;\n} __TOptionData;\nstatic __constant__ __TOptionData d_OptionData[MAX_OPTIONS];\nstatic __device__ real            d_CallValue[MAX_OPTIONS];\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n#ifndef DOUBLE_PRECISION\n__device__ inline float expiryCallValue(float S, float X, float vDt, int i)\n{\n    float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;\n    return (d > 0.0F) ? d : 0.0F;\n}\n#else\n__device__ inline double expiryCallValue(double S, double X, double vDt, int i)\n{\n    double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;\n    return (d > 0.0) ? d : 0.0;\n}\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU kernel\n////////////////////////////////////////////////////////////////////////////////\n#define THREADBLOCK_SIZE 128\n#define ELEMS_PER_THREAD (NUM_STEPS / THREADBLOCK_SIZE)\n#if NUM_STEPS % THREADBLOCK_SIZE\n#error Bad constants\n#endif\n\n__global__ void binomialOptionsKernel()\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ real  call_exchange[THREADBLOCK_SIZE + 1];\n\n    const int  tid    = threadIdx.x;\n    const real S      = d_OptionData[blockIdx.x].S;\n    const real X      = d_OptionData[blockIdx.x].X;\n    const real vDt    = d_OptionData[blockIdx.x].vDt;\n    const real puByDf = d_OptionData[blockIdx.x].puByDf;\n    const real pdByDf = d_OptionData[blockIdx.x].pdByDf;\n\n    real call[ELEMS_PER_THREAD + 1];\n#pragma unroll\n    for (int i = 0; i < ELEMS_PER_THREAD; ++i)\n        call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);\n\n    if (tid == 0)\n        call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);\n\n    int final_it = max(0, tid * ELEMS_PER_THREAD - 1);\n\n#pragma unroll 16\n    for (int i = NUM_STEPS; i > 0; --i) {\n        call_exchange[tid] = call[0];\n        cg::sync(cta);\n        call[ELEMS_PER_THREAD] = call_exchange[tid + 1];\n        cg::sync(cta);\n\n        if (i > final_it) {\n#pragma unroll\n            for (int j = 0; j < ELEMS_PER_THREAD; ++j)\n                call[j] = puByDf * call[j + 1] + pdByDf * call[j];\n        }\n    }\n\n    if (tid == 0) {\n        d_CallValue[blockIdx.x] = call[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU binomialOptions\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" void binomialOptionsGPU(real *callValue, TOptionData *optionData, int optN)\n{\n    __TOptionData h_OptionData[MAX_OPTIONS];\n\n    for (int i = 0; i < optN; i++) {\n        const real T = optionData[i].T;\n        const real R = optionData[i].R;\n        const real V = optionData[i].V;\n\n        const real dt  = T / (real)NUM_STEPS;\n        const real vDt = V * sqrt(dt);\n        const real rDt = R * dt;\n        // Per-step interest and discount factors\n        const real If = exp(rDt);\n        const real Df = exp(-rDt);\n        // Values and pseudoprobabilities of upward and downward moves\n        const real u      = exp(vDt);\n        const real d      = exp(-vDt);\n        const real pu     = (If - d) / (u - d);\n        const real pd     = (real)1.0 - pu;\n        const real puByDf = pu * Df;\n        const real pdByDf = pd * Df;\n\n        h_OptionData[i].S      = (real)optionData[i].S;\n        h_OptionData[i].X      = (real)optionData[i].X;\n        h_OptionData[i].vDt    = (real)vDt;\n        h_OptionData[i].puByDf = (real)puByDf;\n        h_OptionData[i].pdByDf = (real)pdByDf;\n    }\n\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(d_OptionData), h_OptionData, optN * sizeof(__TOptionData)));\n    binomialOptionsKernel<<<optN, THREADBLOCK_SIZE>>>();\n    getLastCudaError(\"binomialOptionsKernel() execution failed.\\n\");\n    checkCudaErrors(hipMemcpyFromSymbol(callValue,HIP_SYMBOL(d_CallValue), optN * sizeof(real)));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/binomialOptions/binomialOptions_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the Cuda OpenGL bindings to\n    dynamically modify a vertex buffer using a Cuda kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with Cuda\n    3. Map the VBO for writing from Cuda\n    4. Run Cuda kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL\n\n    Host code\n*/\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// OpenGL Graphics includes\n#include <helper_gl.h>\n#if defined(__APPLE__) || defined(MACOSX)\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n#include <GLUT/glut.h>\n#ifndef glutCloseFunc\n#define glutCloseFunc glutWMCloseFunc\n#endif\n#else\n#include <GL/freeglut.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n#include <vector_types.h>\n\n#define MAX_EPSILON_ERROR 10.0f\n#define THRESHOLD         0.30f\n#define REFRESH_DELAY     10 // ms\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// vbo variables\nGLuint                       vbo;\nstruct cudaGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Auto-Verification Code\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\nbool         g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nbool runTest(int argc, char **argv, char *ref_file);\nvoid cleanup();\n\n// GL functionality\nbool initGL(int *argc, char **argv);\nvoid createVBO(GLuint *vbo, struct cudaGraphicsResource **vbo_res, unsigned int vbo_res_flags);\nvoid deleteVBO(GLuint *vbo, struct cudaGraphicsResource *vbo_res);\n\n// rendering callbacks\nvoid display();\nvoid keyboard(unsigned char key, int x, int y);\nvoid mouse(int button, int state, int x, int y);\nvoid motion(int x, int y);\nvoid timerEvent(int value);\n\n// Cuda functionality\nvoid runCuda(struct cudaGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGL (VBO)\";\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we are running non-OpenGL and doing a compare of the VBO was generated correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda GL Interop (VBO): %3.1f fps (Max 100Hz)\", avgFPS);\n    glutSetWindowTitle(fps);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Initialize GL\n////////////////////////////////////////////////////////////////////////////////\nbool initGL(int *argc, char **argv)\n{\n    glutInit(argc, argv);\n    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);\n    glutInitWindowSize(window_width, window_height);\n    glutCreateWindow(\"Cuda GL Interop (VBO)\");\n    glutDisplayFunc(display);\n    glutKeyboardFunc(keyboard);\n    glutMotionFunc(motion);\n    glutTimerFunc(REFRESH_DELAY, timerEvent, 0);\n\n    // initialize necessary OpenGL extensions\n    if (!isGLVersionSupported(2, 0)) {\n        fprintf(stderr, \"ERROR: Support for necessary OpenGL extensions missing.\");\n        fflush(stderr);\n        return false;\n    }\n\n    // default initialization\n    glClearColor(0.0, 0.0, 0.0, 1.0);\n    glDisable(GL_DEPTH_TEST);\n\n    // viewport\n    glViewport(0, 0, window_width, window_height);\n\n    // projection\n    glMatrixMode(GL_PROJECTION);\n    glLoadIdentity();\n    gluPerspective(60.0, (GLfloat)window_width / (GLfloat)window_height, 0.1, 10.0);\n\n    SDK_CHECK_ERROR_GL();\n\n    return true;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    // use command-line specified CUDA device, otherwise use device with highest Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(cudaMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, vbo);\n\n        cudaFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // First initialize OpenGL context, so we can properly set the GL for CUDA.\n        // This is necessary in order to achieve optimal performance with OpenGL/CUDA interop.\n        if (false == initGL(&argc, argv)) {\n            return false;\n        }\n\n        // register callbacks\n        glutDisplayFunc(display);\n        glutKeyboardFunc(keyboard);\n        glutMouseFunc(mouse);\n        glutMotionFunc(motion);\n#if defined(__APPLE__) || defined(MACOSX)\n        atexit(cleanup);\n#else\n        glutCloseFunc(cleanup);\n#endif\n\n        // create VBO\n        createVBO(&vbo, &cuda_vbo_resource, cudaGraphicsMapFlagsWriteDiscard);\n\n        // run the cuda part\n        runCuda(&cuda_vbo_resource);\n\n        // start rendering mainloop\n        glutMainLoop();\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct cudaGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    checkCudaErrors(cudaGraphicsMapResources(1, vbo_resource, 0));\n    size_t num_bytes;\n    checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource));\n    // printf(\"CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    checkCudaErrors(cudaGraphicsUnmapResources(1, vbo_resource, 0));\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    cudaDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    checkCudaErrors(\n        cudaMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), cudaMemcpyDeviceToHost));\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Create VBO\n////////////////////////////////////////////////////////////////////////////////\nvoid createVBO(GLuint *vbo, struct cudaGraphicsResource **vbo_res, unsigned int vbo_res_flags)\n{\n    assert(vbo);\n\n    // create buffer object\n    glGenBuffers(1, vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, *vbo);\n\n    // initialize buffer object\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);\n\n    glBindBuffer(GL_ARRAY_BUFFER, 0);\n\n    // register this buffer object with CUDA\n    checkCudaErrors(cudaGraphicsGLRegisterBuffer(vbo_res, *vbo, vbo_res_flags));\n\n    SDK_CHECK_ERROR_GL();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Delete VBO\n////////////////////////////////////////////////////////////////////////////////\nvoid deleteVBO(GLuint *vbo, struct cudaGraphicsResource *vbo_res)\n{\n\n    // unregister this buffer object with CUDA\n    checkCudaErrors(cudaGraphicsUnregisterResource(vbo_res));\n\n    glBindBuffer(1, *vbo);\n    glDeleteBuffers(1, vbo);\n\n    *vbo = 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display()\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n    // set view matrix\n    glMatrixMode(GL_MODELVIEW);\n    glLoadIdentity();\n    glTranslatef(0.0, 0.0, translate_z);\n    glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    // render from the vbo\n    glBindBuffer(GL_ARRAY_BUFFER, vbo);\n    glVertexPointer(4, GL_FLOAT, 0, 0);\n\n    glEnableClientState(GL_VERTEX_ARRAY);\n    glColor3f(1.0, 0.0, 0.0);\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n    glDisableClientState(GL_VERTEX_ARRAY);\n\n    glutSwapBuffers();\n\n    g_fAnim += 0.01f;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\nvoid timerEvent(int value)\n{\n    if (glutGetWindow()) {\n        glutPostRedisplay();\n        glutTimerFunc(REFRESH_DELAY, timerEvent, 0);\n    }\n}\n\nvoid cleanup()\n{\n    sdkDeleteTimer(&timer);\n\n    if (vbo) {\n        deleteVBO(&vbo, cuda_vbo_resource);\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n//! Keyboard events handler\n////////////////////////////////////////////////////////////////////////////////\nvoid keyboard(unsigned char key, int /*x*/, int /*y*/)\n{\n    switch (key) {\n    case (27):\n#if defined(__APPLE__) || defined(MACOSX)\n        exit(EXIT_SUCCESS);\n#else\n        glutDestroyWindow(glutGetWindow());\n        return;\n#endif\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Mouse event handlers\n////////////////////////////////////////////////////////////////////////////////\nvoid mouse(int button, int state, int x, int y)\n{\n    if (state == GLUT_DOWN) {\n        mouse_buttons |= 1 << button;\n    }\n    else if (state == GLUT_UP) {\n        mouse_buttons = 0;\n    }\n\n    mouse_old_x = x;\n    mouse_old_y = y;\n}\n\nvoid motion(int x, int y)\n{\n    float dx, dy;\n    dx = (float)(x - mouse_old_x);\n    dy = (float)(y - mouse_old_y);\n\n    if (mouse_buttons & 1) {\n        rotate_x += dy * 0.2f;\n        rotate_y += dx * 0.2f;\n    }\n    else if (mouse_buttons & 4) {\n        translate_z += dy * 0.01f;\n    }\n\n    mouse_old_x = x;\n    mouse_old_y = y;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        checkCudaErrors(cudaGraphicsUnregisterResource(cuda_vbo_resource));\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        SDK_CHECK_ERROR_GL();\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the Cuda OpenGL bindings to\n    dynamically modify a vertex buffer using a Cuda kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with Cuda\n    3. Map the VBO for writing from Cuda\n    4. Run Cuda kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL\n\n    Host code\n*/\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// OpenGL Graphics includes\n#include <helper_gl.h>\n#if defined(__APPLE__) || defined(MACOSX)\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n#include <GLUT/glut.h>\n#ifndef glutCloseFunc\n#define glutCloseFunc glutWMCloseFunc\n#endif\n#else\n#include <GL/freeglut.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n#include <hip/hip_vector_types.h>\n\n#define MAX_EPSILON_ERROR 10.0f\n#define THRESHOLD         0.30f\n#define REFRESH_DELAY     10 // ms\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// vbo variables\nGLuint                       vbo;\nstruct hipGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Auto-Verification Code\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\nbool         g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nbool runTest(int argc, char **argv, char *ref_file);\nvoid cleanup();\n\n// GL functionality\nbool initGL(int *argc, char **argv);\nvoid createVBO(GLuint *vbo, struct hipGraphicsResource **vbo_res, unsigned int vbo_res_flags);\nvoid deleteVBO(GLuint *vbo, struct hipGraphicsResource *vbo_res);\n\n// rendering callbacks\nvoid display();\nvoid keyboard(unsigned char key, int x, int y);\nvoid mouse(int button, int state, int x, int y);\nvoid motion(int x, int y);\nvoid timerEvent(int value);\n\n// Cuda functionality\nvoid runCuda(struct hipGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGL (VBO)\";\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we are running non-OpenGL and doing a compare of the VBO was generated correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda GL Interop (VBO): %3.1f fps (Max 100Hz)\", avgFPS);\n    glutSetWindowTitle(fps);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Initialize GL\n////////////////////////////////////////////////////////////////////////////////\nbool initGL(int *argc, char **argv)\n{\n    glutInit(argc, argv);\n    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);\n    glutInitWindowSize(window_width, window_height);\n    glutCreateWindow(\"Cuda GL Interop (VBO)\");\n    glutDisplayFunc(display);\n    glutKeyboardFunc(keyboard);\n    glutMotionFunc(motion);\n    glutTimerFunc(REFRESH_DELAY, timerEvent, 0);\n\n    // initialize necessary OpenGL extensions\n    if (!isGLVersionSupported(2, 0)) {\n        fprintf(stderr, \"ERROR: Support for necessary OpenGL extensions missing.\");\n        fflush(stderr);\n        return false;\n    }\n\n    // default initialization\n    glClearColor(0.0, 0.0, 0.0, 1.0);\n    glDisable(GL_DEPTH_TEST);\n\n    // viewport\n    glViewport(0, 0, window_width, window_height);\n\n    // projection\n    glMatrixMode(GL_PROJECTION);\n    glLoadIdentity();\n    gluPerspective(60.0, (GLfloat)window_width / (GLfloat)window_height, 0.1, 10.0);\n\n    SDK_CHECK_ERROR_GL();\n\n    return true;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    // use command-line specified CUDA device, otherwise use device with highest Gflops/s\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(hipMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, vbo);\n\n        hipFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // First initialize OpenGL context, so we can properly set the GL for CUDA.\n        // This is necessary in order to achieve optimal performance with OpenGL/CUDA interop.\n        if (false == initGL(&argc, argv)) {\n            return false;\n        }\n\n        // register callbacks\n        glutDisplayFunc(display);\n        glutKeyboardFunc(keyboard);\n        glutMouseFunc(mouse);\n        glutMotionFunc(motion);\n#if defined(__APPLE__) || defined(MACOSX)\n        atexit(cleanup);\n#else\n        glutCloseFunc(cleanup);\n#endif\n\n        // create VBO\n        createVBO(&vbo, &cuda_vbo_resource, cudaGraphicsMapFlagsWriteDiscard);\n\n        // run the cuda part\n        runCuda(&cuda_vbo_resource);\n\n        // start rendering mainloop\n        glutMainLoop();\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct hipGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    checkCudaErrors(hipGraphicsMapResources(1, vbo_resource, 0));\n    size_t num_bytes;\n    checkCudaErrors(hipGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource));\n    // printf(\"CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    checkCudaErrors(hipGraphicsUnmapResources(1, vbo_resource, 0));\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    hipDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    checkCudaErrors(\n        hipMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), hipMemcpyDeviceToHost));\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Create VBO\n////////////////////////////////////////////////////////////////////////////////\nvoid createVBO(GLuint *vbo, struct hipGraphicsResource **vbo_res, unsigned int vbo_res_flags)\n{\n    assert(vbo);\n\n    // create buffer object\n    glGenBuffers(1, vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, *vbo);\n\n    // initialize buffer object\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);\n\n    glBindBuffer(GL_ARRAY_BUFFER, 0);\n\n    // register this buffer object with CUDA\n    checkCudaErrors(hipGraphicsGLRegisterBuffer(vbo_res, *vbo, vbo_res_flags));\n\n    SDK_CHECK_ERROR_GL();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Delete VBO\n////////////////////////////////////////////////////////////////////////////////\nvoid deleteVBO(GLuint *vbo, struct hipGraphicsResource *vbo_res)\n{\n\n    // unregister this buffer object with CUDA\n    checkCudaErrors(hipGraphicsUnregisterResource(vbo_res));\n\n    glBindBuffer(1, *vbo);\n    glDeleteBuffers(1, vbo);\n\n    *vbo = 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display()\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n    // set view matrix\n    glMatrixMode(GL_MODELVIEW);\n    glLoadIdentity();\n    glTranslatef(0.0, 0.0, translate_z);\n    glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    // render from the vbo\n    glBindBuffer(GL_ARRAY_BUFFER, vbo);\n    glVertexPointer(4, GL_FLOAT, 0, 0);\n\n    glEnableClientState(GL_VERTEX_ARRAY);\n    glColor3f(1.0, 0.0, 0.0);\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n    glDisableClientState(GL_VERTEX_ARRAY);\n\n    glutSwapBuffers();\n\n    g_fAnim += 0.01f;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\nvoid timerEvent(int value)\n{\n    if (glutGetWindow()) {\n        glutPostRedisplay();\n        glutTimerFunc(REFRESH_DELAY, timerEvent, 0);\n    }\n}\n\nvoid cleanup()\n{\n    sdkDeleteTimer(&timer);\n\n    if (vbo) {\n        deleteVBO(&vbo, cuda_vbo_resource);\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n//! Keyboard events handler\n////////////////////////////////////////////////////////////////////////////////\nvoid keyboard(unsigned char key, int /*x*/, int /*y*/)\n{\n    switch (key) {\n    case (27):\n#if defined(__APPLE__) || defined(MACOSX)\n        exit(EXIT_SUCCESS);\n#else\n        glutDestroyWindow(glutGetWindow());\n        return;\n#endif\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Mouse event handlers\n////////////////////////////////////////////////////////////////////////////////\nvoid mouse(int button, int state, int x, int y)\n{\n    if (state == GLUT_DOWN) {\n        mouse_buttons |= 1 << button;\n    }\n    else if (state == GLUT_UP) {\n        mouse_buttons = 0;\n    }\n\n    mouse_old_x = x;\n    mouse_old_y = y;\n}\n\nvoid motion(int x, int y)\n{\n    float dx, dy;\n    dx = (float)(x - mouse_old_x);\n    dy = (float)(y - mouse_old_y);\n\n    if (mouse_buttons & 1) {\n        rotate_x += dy * 0.2f;\n        rotate_y += dx * 0.2f;\n    }\n    else if (mouse_buttons & 4) {\n        translate_z += dy * 0.01f;\n    }\n\n    mouse_old_x = x;\n    mouse_old_y = y;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        checkCudaErrors(hipGraphicsUnregisterResource(cuda_vbo_resource));\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(hipGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        SDK_CHECK_ERROR_GL();\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleGL/simpleGL.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple 3D volume renderer\n\n#ifndef _VOLUMERENDER_KERNEL_CU_\n#define _VOLUMERENDER_KERNEL_CU_\n\n#include <helper_cuda.h>\n#include <helper_math.h>\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\ncudaArray *d_volumeArray = 0;\ncudaArray *d_transferFuncArray;\n\ntypedef unsigned char VolumeType;\n// typedef unsigned short VolumeType;\n\ncudaTextureObject_t texObject;   // For 3D texture\ncudaTextureObject_t transferTex; // For 1D transfer function texture\n\ntypedef struct\n{\n    float4 m[3];\n} float3x4;\n\n__constant__ float3x4 c_invViewMatrix; // inverse view matrix\n\nstruct Ray\n{\n    float3 o; // origin\n    float3 d; // direction\n};\n\n// intersect ray with a box\n// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm\n\n__device__ int intersectBox(Ray r, float3 boxmin, float3 boxmax, float *tnear, float *tfar)\n{\n    // compute intersection of ray with all six bbox planes\n    float3 invR = make_float3(1.0f) / r.d;\n    float3 tbot = invR * (boxmin - r.o);\n    float3 ttop = invR * (boxmax - r.o);\n\n    // re-order intersections to find smallest and largest on each axis\n    float3 tmin = fminf(ttop, tbot);\n    float3 tmax = fmaxf(ttop, tbot);\n\n    // find the largest tmin and the smallest tmax\n    float largest_tmin  = fmaxf(fmaxf(tmin.x, tmin.y), fmaxf(tmin.x, tmin.z));\n    float smallest_tmax = fminf(fminf(tmax.x, tmax.y), fminf(tmax.x, tmax.z));\n\n    *tnear = largest_tmin;\n    *tfar  = smallest_tmax;\n\n    return smallest_tmax > largest_tmin;\n}\n\n// transform vector by matrix (no translation)\n__device__ float3 mul(const float3x4 &M, const float3 &v)\n{\n    float3 r;\n    r.x = dot(v, make_float3(M.m[0]));\n    r.y = dot(v, make_float3(M.m[1]));\n    r.z = dot(v, make_float3(M.m[2]));\n    return r;\n}\n\n// transform vector by matrix with translation\n__device__ float4 mul(const float3x4 &M, const float4 &v)\n{\n    float4 r;\n    r.x = dot(v, M.m[0]);\n    r.y = dot(v, M.m[1]);\n    r.z = dot(v, M.m[2]);\n    r.w = 1.0f;\n    return r;\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return (uint(rgba.w * 255) << 24) | (uint(rgba.z * 255) << 16) | (uint(rgba.y * 255) << 8) | uint(rgba.x * 255);\n}\n\n__global__ void d_render(uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         cudaTextureObject_t tex,\n                         cudaTextureObject_t transferTex)\n{\n    const int    maxSteps         = 500;\n    const float  tstep            = 0.01f;\n    const float  opacityThreshold = 0.95f;\n    const float3 boxMin           = make_float3(-1.0f, -1.0f, -1.0f);\n    const float3 boxMax           = make_float3(1.0f, 1.0f, 1.0f);\n\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if ((x >= imageW) || (y >= imageH))\n        return;\n\n    float u = (x / (float)imageW) * 2.0f - 1.0f;\n    float v = (y / (float)imageH) * 2.0f - 1.0f;\n\n    // calculate eye ray in world space\n    Ray eyeRay;\n    eyeRay.o = make_float3(mul(c_invViewMatrix, make_float4(0.0f, 0.0f, 0.0f, 1.0f)));\n    eyeRay.d = normalize(make_float3(u, v, -2.0f));\n    eyeRay.d = mul(c_invViewMatrix, eyeRay.d);\n\n    // find intersection with box\n    float tnear, tfar;\n    int   hit = intersectBox(eyeRay, boxMin, boxMax, &tnear, &tfar);\n\n    if (!hit)\n        return;\n\n    if (tnear < 0.0f)\n        tnear = 0.0f; // clamp to near plane\n\n    // march along ray from front to back, accumulating color\n    float4 sum  = make_float4(0.0f);\n    float  t    = tnear;\n    float3 pos  = eyeRay.o + eyeRay.d * tnear;\n    float3 step = eyeRay.d * tstep;\n\n    for (int i = 0; i < maxSteps; i++) {\n        // read from 3D texture\n        // remap position to [0, 1] coordinates\n        float sample = tex3D<float>(tex, pos.x * 0.5f + 0.5f, pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f);\n        // sample *= 64.0f;    // scale for 10-bit data\n\n        // lookup in transfer function texture\n        float4 col = tex1D<float4>(transferTex, (sample - transferOffset) * transferScale);\n        col.w *= density;\n\n        // \"under\" operator for back-to-front blending\n        // sum = lerp(sum, col, col.w);\n\n        // pre-multiply alpha\n        col.x *= col.w;\n        col.y *= col.w;\n        col.z *= col.w;\n        // \"over\" operator for front-to-back blending\n        sum = sum + col * (1.0f - sum.w);\n\n        // exit early if opaque\n        if (sum.w > opacityThreshold)\n            break;\n\n        t += tstep;\n\n        if (t > tfar)\n            break;\n\n        pos += step;\n    }\n\n    sum *= brightness;\n\n    // write output color\n    d_output[y * imageW + x] = rgbaFloatToInt(sum);\n}\n\nextern \"C\" void setTextureFilterMode(bool bLinearFilter)\n{\n    if (texObject) {\n        checkCudaErrors(cudaDestroyTextureObject(texObject));\n    }\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? cudaFilterModeLinear : cudaFilterModePoint;\n\n    texDescr.addressMode[0] = cudaAddressModeWrap;\n    texDescr.addressMode[1] = cudaAddressModeWrap;\n    texDescr.addressMode[2] = cudaAddressModeWrap;\n\n    texDescr.readMode = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void initCuda(void *h_volume, cudaExtent volumeSize)\n{\n    // create 3D array\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<VolumeType>();\n    checkCudaErrors(cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize));\n\n    // copy data to 3D array\n    cudaMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr =\n        make_cudaPitchedPtr(h_volume, volumeSize.width * sizeof(VolumeType), volumeSize.width, volumeSize.height);\n    copyParams.dstArray = d_volumeArray;\n    copyParams.extent   = volumeSize;\n    copyParams.kind     = cudaMemcpyHostToDevice;\n    checkCudaErrors(cudaMemcpy3D(&copyParams));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;                 // access with normalized texture coordinates\n    texDescr.filterMode       = cudaFilterModeLinear; // linear interpolation\n\n    texDescr.addressMode[0] = cudaAddressModeClamp; // clamp texture coordinates\n    texDescr.addressMode[1] = cudaAddressModeClamp;\n    texDescr.addressMode[2] = cudaAddressModeClamp;\n\n    texDescr.readMode = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n\n    // create transfer function texture\n    float4 transferFunc[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            1.0,\n            0.0,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.5,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    cudaChannelFormatDesc channelDesc2 = cudaCreateChannelDesc<float4>();\n    cudaArray            *d_transferFuncArray;\n    checkCudaErrors(cudaMallocArray(&d_transferFuncArray, &channelDesc2, sizeof(transferFunc) / sizeof(float4), 1));\n    checkCudaErrors(cudaMemcpy2DToArray(\n        d_transferFuncArray, 0, 0, transferFunc, 0, sizeof(transferFunc), 1, cudaMemcpyHostToDevice));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_transferFuncArray;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true; // access with normalized texture coordinates\n    texDescr.filterMode       = cudaFilterModeLinear;\n\n    texDescr.addressMode[0] = cudaAddressModeClamp; // wrap texture coordinates\n\n    texDescr.readMode = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&transferTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeCudaBuffers()\n{\n    checkCudaErrors(cudaDestroyTextureObject(texObject));\n    checkCudaErrors(cudaDestroyTextureObject(transferTex));\n    checkCudaErrors(cudaFreeArray(d_volumeArray));\n    checkCudaErrors(cudaFreeArray(d_transferFuncArray));\n}\n\nextern \"C\" void render_kernel(dim3  gridSize,\n                              dim3  blockSize,\n                              uint *d_output,\n                              uint  imageW,\n                              uint  imageH,\n                              float density,\n                              float brightness,\n                              float transferOffset,\n                              float transferScale)\n{\n    d_render<<<gridSize, blockSize>>>(\n        d_output, imageW, imageH, density, brightness, transferOffset, transferScale, texObject, transferTex);\n}\n\nextern \"C\" void copyInvViewMatrix(float *invViewMatrix, size_t sizeofMatrix)\n{\n    checkCudaErrors(cudaMemcpyToSymbol(c_invViewMatrix, invViewMatrix, sizeofMatrix));\n}\n\n#endif // #ifndef _VOLUMERENDER_KERNEL_CU_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple 3D volume renderer\n\n#include \"hip/hip_runtime.h\"\n#ifndef _VOLUMERENDER_KERNEL_CU_\n#define _VOLUMERENDER_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\nhipArray *d_volumeArray = 0;\nhipArray *d_transferFuncArray;\n\ntypedef unsigned char VolumeType;\n// typedef unsigned short VolumeType;\n\nhipTextureObject_t texObject;   // For 3D texture\nhipTextureObject_t transferTex; // For 1D transfer function texture\n\ntypedef struct\n{\n    float4 m[3];\n} float3x4;\n\n__constant__ float3x4 c_invViewMatrix; // inverse view matrix\n\nstruct Ray\n{\n    float3 o; // origin\n    float3 d; // direction\n};\n\n// intersect ray with a box\n// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm\n\n__device__ int intersectBox(Ray r, float3 boxmin, float3 boxmax, float *tnear, float *tfar)\n{\n    // compute intersection of ray with all six bbox planes\n    float3 invR = make_float3(1.0f) / r.d;\n    float3 tbot = invR * (boxmin - r.o);\n    float3 ttop = invR * (boxmax - r.o);\n\n    // re-order intersections to find smallest and largest on each axis\n    float3 tmin = fminf(ttop, tbot);\n    float3 tmax = fmaxf(ttop, tbot);\n\n    // find the largest tmin and the smallest tmax\n    float largest_tmin  = fmaxf(fmaxf(tmin.x, tmin.y), fmaxf(tmin.x, tmin.z));\n    float smallest_tmax = fminf(fminf(tmax.x, tmax.y), fminf(tmax.x, tmax.z));\n\n    *tnear = largest_tmin;\n    *tfar  = smallest_tmax;\n\n    return smallest_tmax > largest_tmin;\n}\n\n// transform vector by matrix (no translation)\n__device__ float3 mul(const float3x4 &M, const float3 &v)\n{\n    float3 r;\n    r.x = dot(v, make_float3(M.m[0]));\n    r.y = dot(v, make_float3(M.m[1]));\n    r.z = dot(v, make_float3(M.m[2]));\n    return r;\n}\n\n// transform vector by matrix with translation\n__device__ float4 mul(const float3x4 &M, const float4 &v)\n{\n    float4 r;\n    r.x = dot(v, M.m[0]);\n    r.y = dot(v, M.m[1]);\n    r.z = dot(v, M.m[2]);\n    r.w = 1.0f;\n    return r;\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return (uint(rgba.w * 255) << 24) | (uint(rgba.z * 255) << 16) | (uint(rgba.y * 255) << 8) | uint(rgba.x * 255);\n}\n\n__global__ void d_render(uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         hipTextureObject_t tex,\n                         hipTextureObject_t transferTex)\n{\n    const int    maxSteps         = 500;\n    const float  tstep            = 0.01f;\n    const float  opacityThreshold = 0.95f;\n    const float3 boxMin           = make_float3(-1.0f, -1.0f, -1.0f);\n    const float3 boxMax           = make_float3(1.0f, 1.0f, 1.0f);\n\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if ((x >= imageW) || (y >= imageH))\n        return;\n\n    float u = (x / (float)imageW) * 2.0f - 1.0f;\n    float v = (y / (float)imageH) * 2.0f - 1.0f;\n\n    // calculate eye ray in world space\n    Ray eyeRay;\n    eyeRay.o = make_float3(mul(c_invViewMatrix, make_float4(0.0f, 0.0f, 0.0f, 1.0f)));\n    eyeRay.d = normalize(make_float3(u, v, -2.0f));\n    eyeRay.d = mul(c_invViewMatrix, eyeRay.d);\n\n    // find intersection with box\n    float tnear, tfar;\n    int   hit = intersectBox(eyeRay, boxMin, boxMax, &tnear, &tfar);\n\n    if (!hit)\n        return;\n\n    if (tnear < 0.0f)\n        tnear = 0.0f; // clamp to near plane\n\n    // march along ray from front to back, accumulating color\n    float4 sum  = make_float4(0.0f);\n    float  t    = tnear;\n    float3 pos  = eyeRay.o + eyeRay.d * tnear;\n    float3 step = eyeRay.d * tstep;\n\n    for (int i = 0; i < maxSteps; i++) {\n        // read from 3D texture\n        // remap position to [0, 1] coordinates\n        float sample = tex3D<float>(tex, pos.x * 0.5f + 0.5f, pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f);\n        // sample *= 64.0f;    // scale for 10-bit data\n\n        // lookup in transfer function texture\n        float4 col = tex1D<float4>(transferTex, (sample - transferOffset) * transferScale);\n        col.w *= density;\n\n        // \"under\" operator for back-to-front blending\n        // sum = lerp(sum, col, col.w);\n\n        // pre-multiply alpha\n        col.x *= col.w;\n        col.y *= col.w;\n        col.z *= col.w;\n        // \"over\" operator for front-to-back blending\n        sum = sum + col * (1.0f - sum.w);\n\n        // exit early if opaque\n        if (sum.w > opacityThreshold)\n            break;\n\n        t += tstep;\n\n        if (t > tfar)\n            break;\n\n        pos += step;\n    }\n\n    sum *= brightness;\n\n    // write output color\n    d_output[y * imageW + x] = rgbaFloatToInt(sum);\n}\n\nextern \"C\" void setTextureFilterMode(bool bLinearFilter)\n{\n    if (texObject) {\n        checkCudaErrors(hipDestroyTextureObject(texObject));\n    }\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? hipFilterModeLinear : hipFilterModePoint;\n\n    texDescr.addressMode[0] = hipAddressModeWrap;\n    texDescr.addressMode[1] = hipAddressModeWrap;\n    texDescr.addressMode[2] = hipAddressModeWrap;\n\n    texDescr.readMode = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void initCuda(void *h_volume, hipExtent volumeSize)\n{\n    // create 3D array\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc<VolumeType>();\n    checkCudaErrors(hipMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize));\n\n    // copy data to 3D array\n    hipMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr =\n        make_hipPitchedPtr(h_volume, volumeSize.width * sizeof(VolumeType), volumeSize.width, volumeSize.height);\n    copyParams.dstArray = d_volumeArray;\n    copyParams.extent   = volumeSize;\n    copyParams.kind     = hipMemcpyHostToDevice;\n    checkCudaErrors(hipMemcpy3D(&copyParams));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_volumeArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;                 // access with normalized texture coordinates\n    texDescr.filterMode       = hipFilterModeLinear; // linear interpolation\n\n    texDescr.addressMode[0] = hipAddressModeClamp; // clamp texture coordinates\n    texDescr.addressMode[1] = hipAddressModeClamp;\n    texDescr.addressMode[2] = hipAddressModeClamp;\n\n    texDescr.readMode = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n\n    // create transfer function texture\n    float4 transferFunc[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            1.0,\n            0.0,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.5,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    hipChannelFormatDesc channelDesc2 = hipCreateChannelDesc<float4>();\n    hipArray            *d_transferFuncArray;\n    checkCudaErrors(hipMallocArray(&d_transferFuncArray, &channelDesc2, sizeof(transferFunc) / sizeof(float4), 1));\n    checkCudaErrors(hipMemcpy2DToArray(\n        d_transferFuncArray, 0, 0, transferFunc, 0, sizeof(transferFunc), 1, hipMemcpyHostToDevice));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_transferFuncArray;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true; // access with normalized texture coordinates\n    texDescr.filterMode       = hipFilterModeLinear;\n\n    texDescr.addressMode[0] = hipAddressModeClamp; // wrap texture coordinates\n\n    texDescr.readMode = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&transferTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeCudaBuffers()\n{\n    checkCudaErrors(hipDestroyTextureObject(texObject));\n    checkCudaErrors(hipDestroyTextureObject(transferTex));\n    checkCudaErrors(hipFreeArray(d_volumeArray));\n    checkCudaErrors(hipFreeArray(d_transferFuncArray));\n}\n\nextern \"C\" void render_kernel(dim3  gridSize,\n                              dim3  blockSize,\n                              uint *d_output,\n                              uint  imageW,\n                              uint  imageH,\n                              float density,\n                              float brightness,\n                              float transferOffset,\n                              float transferScale)\n{\n    d_render<<<gridSize, blockSize>>>(\n        d_output, imageW, imageH, density, brightness, transferOffset, transferScale, texObject, transferTex);\n}\n\nextern \"C\" void copyInvViewMatrix(float *invViewMatrix, size_t sizeofMatrix)\n{\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(c_invViewMatrix), invViewMatrix, sizeofMatrix));\n}\n\n#endif // #ifndef _VOLUMERENDER_KERNEL_CU_\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/volumeRender/volumeRender_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#define GLFW_INCLUDE_VULKAN\n#ifdef _WIN64\n// Add windows.h to the include path firstly as dependency for other Windows headers\n#include <windows.h>\n// Add other Windows headers\n#include <VersionHelpers.h>\n#include <aclapi.h>\n#include <dxgi1_2.h>\n#define _USE_MATH_DEFINES\n#endif\n\n#include <GLFW/glfw3.h>\n#include <vulkan/vulkan.h>\n#ifdef _WIN64\n#include <vulkan/vulkan_win32.h>\n#endif\n\n#include <algorithm>\n#include <array>\n#include <chrono>\n#include <cstdlib>\n#include <cstring>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <fstream>\n#include <helper_cuda.h>\n#include <helper_image.h>\n#include <helper_math.h>\n#include <iostream>\n#include <set>\n#include <stdexcept>\n#include <thread>\n#include <vector>\n\n#include \"linmath.h\"\n\n#define WIDTH  800\n#define HEIGHT 600\n\nconst int MAX_FRAMES = 4;\n\nconst std::vector<const char *> validationLayers = {\"VK_LAYER_KHRONOS_validation\"};\n\n#ifdef NDEBUG\nconst bool enableValidationLayers = false;\n#else\nconst bool enableValidationLayers = true;\n#endif\n\nstd::string execution_path;\n\nVkResult CreateDebugUtilsMessengerEXT(VkInstance                                instance,\n                                      const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,\n                                      const VkAllocationCallbacks              *pAllocator,\n                                      VkDebugUtilsMessengerEXT                 *pDebugMessenger)\n{\n    auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, \"vkCreateDebugUtilsMessengerEXT\");\n    if (func != nullptr) {\n        return func(instance, pCreateInfo, pAllocator, pDebugMessenger);\n    }\n    else {\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n};\n\nconst std::vector<const char *> deviceExtensions = {\n    VK_KHR_SWAPCHAIN_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,\n#ifdef _WIN64\n    VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,\n#else\n    VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,\n#endif\n};\n\n#ifdef _WIN64\nclass WindowsSecurityAttributes\n{\nprotected:\n    SECURITY_ATTRIBUTES  m_winSecurityAttributes;\n    PSECURITY_DESCRIPTOR m_winPSecurityDescriptor;\n\npublic:\n    WindowsSecurityAttributes();\n    SECURITY_ATTRIBUTES *operator&();\n    ~WindowsSecurityAttributes();\n};\n\nWindowsSecurityAttributes::WindowsSecurityAttributes()\n{\n    m_winPSecurityDescriptor = (PSECURITY_DESCRIPTOR)calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof(void **));\n\n    PSID *ppSID = (PSID *)((PBYTE)m_winPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);\n    PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *));\n\n    InitializeSecurityDescriptor(m_winPSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);\n\n    SID_IDENTIFIER_AUTHORITY sidIdentifierAuthority = SECURITY_WORLD_SID_AUTHORITY;\n    AllocateAndInitializeSid(&sidIdentifierAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, ppSID);\n\n    EXPLICIT_ACCESS explicitAccess;\n    ZeroMemory(&explicitAccess, sizeof(EXPLICIT_ACCESS));\n    explicitAccess.grfAccessPermissions = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;\n    explicitAccess.grfAccessMode        = SET_ACCESS;\n    explicitAccess.grfInheritance       = INHERIT_ONLY;\n    explicitAccess.Trustee.TrusteeForm  = TRUSTEE_IS_SID;\n    explicitAccess.Trustee.TrusteeType  = TRUSTEE_IS_WELL_KNOWN_GROUP;\n    explicitAccess.Trustee.ptstrName    = (LPTSTR)*ppSID;\n\n    SetEntriesInAcl(1, &explicitAccess, NULL, ppACL);\n\n    SetSecurityDescriptorDacl(m_winPSecurityDescriptor, TRUE, *ppACL, FALSE);\n\n    m_winSecurityAttributes.nLength              = sizeof(m_winSecurityAttributes);\n    m_winSecurityAttributes.lpSecurityDescriptor = m_winPSecurityDescriptor;\n    m_winSecurityAttributes.bInheritHandle       = TRUE;\n}\n\nSECURITY_ATTRIBUTES *WindowsSecurityAttributes::operator&() { return &m_winSecurityAttributes; }\n\nWindowsSecurityAttributes::~WindowsSecurityAttributes()\n{\n    PSID *ppSID = (PSID *)((PBYTE)m_winPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);\n    PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *));\n\n    if (*ppSID) {\n        FreeSid(*ppSID);\n    }\n    if (*ppACL) {\n        LocalFree(*ppACL);\n    }\n    free(m_winPSecurityDescriptor);\n}\n#endif\n\nvoid DestroyDebugUtilsMessengerEXT(VkInstance                   instance,\n                                   VkDebugUtilsMessengerEXT     debugMessenger,\n                                   const VkAllocationCallbacks *pAllocator)\n{\n    auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, \"vkDestroyDebugUtilsMessengerEXT\");\n    if (func != nullptr) {\n        func(instance, debugMessenger, pAllocator);\n    }\n}\n\nstruct QueueFamilyIndices\n{\n    int graphicsFamily = -1;\n    int presentFamily  = -1;\n\n    bool isComplete() { return graphicsFamily >= 0 && presentFamily >= 0; }\n};\n\nstruct SwapChainSupportDetails\n{\n    VkSurfaceCapabilitiesKHR        capabilities;\n    std::vector<VkSurfaceFormatKHR> formats;\n    std::vector<VkPresentModeKHR>   presentModes;\n};\n\ntypedef float vec2[2];\n\nstruct Vertex\n{\n    vec4 pos;\n    vec3 color;\n    vec2 texCoord;\n\n    static VkVertexInputBindingDescription getBindingDescription()\n    {\n        VkVertexInputBindingDescription bindingDescription = {};\n        bindingDescription.binding                         = 0;\n        bindingDescription.stride                          = sizeof(Vertex);\n        bindingDescription.inputRate                       = VK_VERTEX_INPUT_RATE_VERTEX;\n\n        return bindingDescription;\n    }\n\n    static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions()\n    {\n        std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {};\n\n        attributeDescriptions[0].binding  = 0;\n        attributeDescriptions[0].location = 0;\n        attributeDescriptions[0].format   = VK_FORMAT_R32G32B32A32_SFLOAT;\n        attributeDescriptions[0].offset   = offsetof(Vertex, pos);\n\n        attributeDescriptions[1].binding  = 0;\n        attributeDescriptions[1].location = 1;\n        attributeDescriptions[1].format   = VK_FORMAT_R32G32B32_SFLOAT;\n        attributeDescriptions[1].offset   = offsetof(Vertex, color);\n\n        attributeDescriptions[2].binding  = 0;\n        attributeDescriptions[2].location = 2;\n        attributeDescriptions[2].format   = VK_FORMAT_R32G32_SFLOAT;\n        attributeDescriptions[2].offset   = offsetof(Vertex, texCoord);\n\n        return attributeDescriptions;\n    }\n};\n\nstruct UniformBufferObject\n{\n    alignas(16) mat4x4 model;\n    alignas(16) mat4x4 view;\n    alignas(16) mat4x4 proj;\n};\n\nconst std::vector<Vertex> vertices = {{{-1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},\n                                      {{1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},\n                                      {{1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},\n                                      {{-1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};\n\nconst std::vector<uint16_t> indices = {0, 1, 2, 2, 3, 0};\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n__device__ float4 rgbaIntToFloat(unsigned int c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\nint filter_radius = 14;\nint g_nFilterSign = 1;\n\n// This varies the filter radius, so we can see automatic animation\nvoid varySigma()\n{\n    filter_radius += g_nFilterSign;\n\n    if (filter_radius > 64) {\n        filter_radius = 64; // clamp to 64 and then negate sign\n        g_nFilterSign = -1;\n    }\n    else if (filter_radius < 0) {\n        filter_radius = 0;\n        g_nFilterSign = 1;\n    }\n}\n\n// row pass using texture lookups\n__global__ void d_boxfilter_rgba_x(cudaSurfaceObject_t *dstSurfMipMapArray,\n                                   cudaTextureObject_t  textureMipMapInput,\n                                   size_t               baseWidth,\n                                   size_t               baseHeight,\n                                   size_t               mipLevels,\n                                   int                  filter_radius)\n{\n    float        scale = 1.0f / (float)((filter_radius << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (y < baseHeight) {\n        for (uint32_t mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n            uint32_t width  = (baseWidth >> mipLevelIdx) ? (baseWidth >> mipLevelIdx) : 1;\n            uint32_t height = (baseHeight >> mipLevelIdx) ? (baseHeight >> mipLevelIdx) : 1;\n            if (y < height && filter_radius < width) {\n                float  px = 1.0 / width;\n                float  py = 1.0 / height;\n                float4 t  = make_float4(0.0f);\n                for (int x = -filter_radius; x <= filter_radius; x++) {\n                    t += tex2DLod<float4>(textureMipMapInput, x * px, y * py, (float)mipLevelIdx);\n                }\n\n                unsigned int dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], 0, y);\n\n                for (int x = 1; x < width; x++) {\n                    t += tex2DLod<float4>(textureMipMapInput, (x + filter_radius) * px, y * py, (float)mipLevelIdx);\n                    t -= tex2DLod<float4>(textureMipMapInput, (x - filter_radius - 1) * px, y * py, (float)mipLevelIdx);\n                    unsigned int dataB = rgbaFloatToInt(t * scale);\n                    surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], x * sizeof(uchar4), y);\n                }\n            }\n        }\n    }\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_boxfilter_rgba_y(cudaSurfaceObject_t *dstSurfMipMapArray,\n                                   cudaSurfaceObject_t *srcSurfMipMapArray,\n                                   size_t               baseWidth,\n                                   size_t               baseHeight,\n                                   size_t               mipLevels,\n                                   int                  filter_radius)\n{\n    unsigned int x     = blockIdx.x * blockDim.x + threadIdx.x;\n    float        scale = 1.0f / (float)((filter_radius << 1) + 1);\n\n    for (uint32_t mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n        uint32_t width  = (baseWidth >> mipLevelIdx) ? (baseWidth >> mipLevelIdx) : 1;\n        uint32_t height = (baseHeight >> mipLevelIdx) ? (baseHeight >> mipLevelIdx) : 1;\n\n        if (x < width && height > filter_radius) {\n            float4 t;\n            // do left edge\n            int          colInBytes = x * sizeof(uchar4);\n            unsigned int pixFirst   = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, 0);\n            t                       = rgbaIntToFloat(pixFirst) * filter_radius;\n\n            for (int y = 0; (y < (filter_radius + 1)) && (y < height); y++) {\n                unsigned int pix = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y);\n                t += rgbaIntToFloat(pix);\n            }\n\n            unsigned int dataB = rgbaFloatToInt(t * scale);\n            surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, 0);\n\n            for (int y = 1; (y < filter_radius + 1) && ((y + filter_radius) < height); y++) {\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y + filter_radius);\n                t += rgbaIntToFloat(pix);\n                t -= rgbaIntToFloat(pixFirst);\n\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n\n            // main loop\n            for (int y = (filter_radius + 1); y < (height - filter_radius); y++) {\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y + filter_radius);\n                t += rgbaIntToFloat(pix);\n\n                pix = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y - filter_radius - 1);\n                t -= rgbaIntToFloat(pix);\n\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n\n            // do right edge\n            unsigned int pixLast = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, height - 1);\n            for (int y = height - filter_radius; (y < height) && ((y - filter_radius - 1) > 1); y++) {\n                t += rgbaIntToFloat(pixLast);\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y - filter_radius - 1);\n                t -= rgbaIntToFloat(pix);\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n        }\n    }\n}\n\nclass vulkanImageCUDA\n{\npublic:\n    void loadImageData(const std::string &filename)\n    {\n        // load image (needed so we can get the width and height before we create\n        // the window\n        char *image_path = sdkFindFilePath(filename.c_str(), execution_path.c_str());\n\n        if (image_path == 0) {\n            printf(\"Error finding image file '%s'\\n\", filename.c_str());\n            exit(EXIT_FAILURE);\n        }\n\n        sdkLoadPPM4(image_path, (unsigned char **)&image_data, &imageWidth, &imageHeight);\n\n        if (!image_data) {\n            printf(\"Error opening file '%s'\\n\", image_path);\n            exit(EXIT_FAILURE);\n        }\n\n        printf(\"Loaded '%s', %d x %d pixels\\n\", image_path, imageWidth, imageHeight);\n    }\n\n    void run()\n    {\n        initWindow();\n        initVulkan();\n        initCuda();\n        mainLoop();\n        cleanup();\n    }\n\nprivate:\n    GLFWwindow *window;\n\n    VkInstance               instance;\n    VkDebugUtilsMessengerEXT debugMessenger;\n    VkSurfaceKHR             surface;\n\n    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;\n    VkDevice         device;\n    uint8_t          vkDeviceUUID[VK_UUID_SIZE];\n\n    VkQueue graphicsQueue;\n    VkQueue presentQueue;\n\n    VkSwapchainKHR             swapChain;\n    std::vector<VkImage>       swapChainImages;\n    VkFormat                   swapChainImageFormat;\n    VkExtent2D                 swapChainExtent;\n    std::vector<VkImageView>   swapChainImageViews;\n    std::vector<VkFramebuffer> swapChainFramebuffers;\n\n    VkRenderPass          renderPass;\n    VkDescriptorSetLayout descriptorSetLayout;\n    VkPipelineLayout      pipelineLayout;\n    VkPipeline            graphicsPipeline;\n\n    VkCommandPool commandPool;\n\n    VkImage        textureImage;\n    VkDeviceMemory textureImageMemory;\n    VkImageView    textureImageView;\n    VkSampler      textureSampler;\n\n    VkBuffer       vertexBuffer;\n    VkDeviceMemory vertexBufferMemory;\n    VkBuffer       indexBuffer;\n    VkDeviceMemory indexBufferMemory;\n\n    std::vector<VkBuffer>       uniformBuffers;\n    std::vector<VkDeviceMemory> uniformBuffersMemory;\n\n    VkDescriptorPool             descriptorPool;\n    std::vector<VkDescriptorSet> descriptorSets;\n\n    std::vector<VkCommandBuffer> commandBuffers;\n\n    std::vector<VkSemaphore> imageAvailableSemaphores;\n    std::vector<VkSemaphore> renderFinishedSemaphores;\n    VkSemaphore              cudaUpdateVkSemaphore, vkUpdateCudaSemaphore;\n    std::vector<VkFence>     inFlightFences;\n\n    size_t currentFrame = 0;\n\n    bool framebufferResized = false;\n\n#ifdef _WIN64\n    PFN_vkGetMemoryWin32HandleKHR    fpGetMemoryWin32HandleKHR;\n    PFN_vkGetSemaphoreWin32HandleKHR fpGetSemaphoreWin32HandleKHR;\n#else\n    PFN_vkGetMemoryFdKHR    fpGetMemoryFdKHR    = NULL;\n    PFN_vkGetSemaphoreFdKHR fpGetSemaphoreFdKHR = NULL;\n#endif\n\n    PFN_vkGetPhysicalDeviceProperties2 fpGetPhysicalDeviceProperties2;\n\n    unsigned int *image_data = NULL;\n    unsigned int  imageWidth, imageHeight;\n    unsigned int  mipLevels = 1;\n    size_t        totalImageMemSize;\n\n    // CUDA objects\n    cudaExternalMemory_t             cudaExtMemImageBuffer;\n    cudaMipmappedArray_t             cudaMipmappedImageArray, cudaMipmappedImageArrayTemp, cudaMipmappedImageArrayOrig;\n    std::vector<cudaSurfaceObject_t> surfaceObjectList, surfaceObjectListTemp;\n    cudaSurfaceObject_t             *d_surfaceObjectList, *d_surfaceObjectListTemp;\n    cudaTextureObject_t              textureObjMipMapInput;\n\n    cudaExternalSemaphore_t cudaExtCudaUpdateVkSemaphore;\n    cudaExternalSemaphore_t cudaExtVkUpdateCudaSemaphore;\n    cudaStream_t            streamToRun;\n\n    void initWindow()\n    {\n        glfwInit();\n\n        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);\n\n        window = glfwCreateWindow(WIDTH, HEIGHT, \"Vulkan Image CUDA Box Filter\", nullptr, nullptr);\n        glfwSetWindowUserPointer(window, this);\n        glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);\n    }\n\n    static void framebufferResizeCallback(GLFWwindow *window, int width, int height)\n    {\n        auto app                = reinterpret_cast<vulkanImageCUDA *>(glfwGetWindowUserPointer(window));\n        app->framebufferResized = true;\n    }\n\n    void initVulkan()\n    {\n        createInstance();\n        setupDebugMessenger();\n        createSurface();\n        pickPhysicalDevice();\n        createLogicalDevice();\n        getKhrExtensionsFn();\n        createSwapChain();\n        createImageViews();\n        createRenderPass();\n        createDescriptorSetLayout();\n        createGraphicsPipeline();\n        createFramebuffers();\n        createCommandPool();\n        createTextureImage();\n        createTextureImageView();\n        createTextureSampler();\n        createVertexBuffer();\n        createIndexBuffer();\n        createUniformBuffers();\n        createDescriptorPool();\n        createDescriptorSets();\n        createCommandBuffers();\n        createSyncObjects();\n        createSyncObjectsExt();\n    }\n\n    void initCuda()\n    {\n        setCudaVkDevice();\n        checkCudaErrors(cudaStreamCreate(&streamToRun));\n        cudaVkImportImageMem();\n        cudaVkImportSemaphore();\n    }\n\n    void mainLoop()\n    {\n        updateUniformBuffer();\n        while (!glfwWindowShouldClose(window)) {\n            glfwPollEvents();\n            drawFrame();\n        }\n\n        vkDeviceWaitIdle(device);\n    }\n\n    void cleanupSwapChain()\n    {\n        for (auto framebuffer : swapChainFramebuffers) {\n            vkDestroyFramebuffer(device, framebuffer, nullptr);\n        }\n\n        vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());\n\n        vkDestroyPipeline(device, graphicsPipeline, nullptr);\n        vkDestroyPipelineLayout(device, pipelineLayout, nullptr);\n        vkDestroyRenderPass(device, renderPass, nullptr);\n\n        for (auto imageView : swapChainImageViews) {\n            vkDestroyImageView(device, imageView, nullptr);\n        }\n\n        vkDestroySwapchainKHR(device, swapChain, nullptr);\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            vkDestroyBuffer(device, uniformBuffers[i], nullptr);\n            vkFreeMemory(device, uniformBuffersMemory[i], nullptr);\n        }\n\n        vkDestroyDescriptorPool(device, descriptorPool, nullptr);\n    }\n\n    void cleanup()\n    {\n        cleanupSwapChain();\n\n        vkDestroySampler(device, textureSampler, nullptr);\n        vkDestroyImageView(device, textureImageView, nullptr);\n\n        for (int i = 0; i < mipLevels; i++) {\n            checkCudaErrors(cudaDestroySurfaceObject(surfaceObjectList[i]));\n            checkCudaErrors(cudaDestroySurfaceObject(surfaceObjectListTemp[i]));\n        }\n\n        checkCudaErrors(cudaFree(d_surfaceObjectList));\n        checkCudaErrors(cudaFree(d_surfaceObjectListTemp));\n        checkCudaErrors(cudaFreeMipmappedArray(cudaMipmappedImageArrayTemp));\n        checkCudaErrors(cudaFreeMipmappedArray(cudaMipmappedImageArrayOrig));\n        checkCudaErrors(cudaFreeMipmappedArray(cudaMipmappedImageArray));\n        checkCudaErrors(cudaDestroyTextureObject(textureObjMipMapInput));\n        checkCudaErrors(cudaDestroyExternalMemory(cudaExtMemImageBuffer));\n        checkCudaErrors(cudaDestroyExternalSemaphore(cudaExtCudaUpdateVkSemaphore));\n        checkCudaErrors(cudaDestroyExternalSemaphore(cudaExtVkUpdateCudaSemaphore));\n\n        vkDestroyImage(device, textureImage, nullptr);\n        vkFreeMemory(device, textureImageMemory, nullptr);\n\n        vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);\n\n        vkDestroyBuffer(device, indexBuffer, nullptr);\n        vkFreeMemory(device, indexBufferMemory, nullptr);\n\n        vkDestroyBuffer(device, vertexBuffer, nullptr);\n        vkFreeMemory(device, vertexBufferMemory, nullptr);\n\n        vkDestroySemaphore(device, cudaUpdateVkSemaphore, nullptr);\n        vkDestroySemaphore(device, vkUpdateCudaSemaphore, nullptr);\n\n        for (size_t i = 0; i < MAX_FRAMES; i++) {\n            vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);\n            vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);\n            vkDestroyFence(device, inFlightFences[i], nullptr);\n        }\n\n        vkDestroyCommandPool(device, commandPool, nullptr);\n\n        vkDestroyDevice(device, nullptr);\n\n        if (enableValidationLayers) {\n            DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);\n        }\n\n        vkDestroySurfaceKHR(instance, surface, nullptr);\n        vkDestroyInstance(instance, nullptr);\n\n        glfwDestroyWindow(window);\n\n        glfwTerminate();\n    }\n\n    void recreateSwapChain()\n    {\n        int width = 0, height = 0;\n        while (width == 0 || height == 0) {\n            glfwGetFramebufferSize(window, &width, &height);\n            glfwWaitEvents();\n        }\n\n        vkDeviceWaitIdle(device);\n\n        cleanupSwapChain();\n\n        createSwapChain();\n        createImageViews();\n        createRenderPass();\n        createGraphicsPipeline();\n        createFramebuffers();\n        createUniformBuffers();\n        createDescriptorPool();\n        createDescriptorSets();\n        createCommandBuffers();\n    }\n\n    void createInstance()\n    {\n        if (enableValidationLayers && !checkValidationLayerSupport()) {\n            throw std::runtime_error(\"validation layers requested, but not available!\");\n        }\n\n        VkApplicationInfo appInfo  = {};\n        appInfo.sType              = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n        appInfo.pApplicationName   = \"Vulkan Image CUDA Interop\";\n        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);\n        appInfo.pEngineName        = \"No Engine\";\n        appInfo.engineVersion      = VK_MAKE_VERSION(1, 0, 0);\n        appInfo.apiVersion         = VK_API_VERSION_1_1;\n\n        VkInstanceCreateInfo createInfo = {};\n        createInfo.sType                = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n        createInfo.pApplicationInfo     = &appInfo;\n\n        auto extensions                    = getRequiredExtensions();\n        createInfo.enabledExtensionCount   = static_cast<uint32_t>(extensions.size());\n        createInfo.ppEnabledExtensionNames = extensions.data();\n\n        VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;\n        if (enableValidationLayers) {\n            createInfo.enabledLayerCount   = static_cast<uint32_t>(validationLayers.size());\n            createInfo.ppEnabledLayerNames = validationLayers.data();\n\n            populateDebugMessengerCreateInfo(debugCreateInfo);\n            createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo;\n        }\n        else {\n            createInfo.enabledLayerCount = 0;\n\n            createInfo.pNext = nullptr;\n        }\n\n        if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create instance!\");\n        }\n\n        fpGetPhysicalDeviceProperties2 =\n            (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(instance, \"vkGetPhysicalDeviceProperties2\");\n        if (fpGetPhysicalDeviceProperties2 == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetPhysicalDeviceProperties2KHR\\\" not \"\n                                     \"found.\\n\");\n        }\n\n#ifdef _WIN64\n        fpGetMemoryWin32HandleKHR =\n            (PFN_vkGetMemoryWin32HandleKHR)vkGetInstanceProcAddr(instance, \"vkGetMemoryWin32HandleKHR\");\n        if (fpGetMemoryWin32HandleKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetMemoryWin32HandleKHR\\\" not \"\n                                     \"found.\\n\");\n        }\n#else\n        fpGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)vkGetInstanceProcAddr(instance, \"vkGetMemoryFdKHR\");\n        if (fpGetMemoryFdKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetMemoryFdKHR\\\" not found.\\n\");\n        }\n        else {\n            std::cout << \"Vulkan proc address for vkGetMemoryFdKHR - \" << fpGetMemoryFdKHR << std::endl;\n        }\n#endif\n    }\n\n    void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo)\n    {\n        createInfo                 = {};\n        createInfo.sType           = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;\n        createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT\n                                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT\n                                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;\n        createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT\n                               | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT\n                               | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;\n        createInfo.pfnUserCallback = debugCallback;\n    }\n\n    void setupDebugMessenger()\n    {\n        if (!enableValidationLayers)\n            return;\n\n        VkDebugUtilsMessengerCreateInfoEXT createInfo;\n        populateDebugMessengerCreateInfo(createInfo);\n\n        if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to set up debug messenger!\");\n        }\n    }\n\n    void createSurface()\n    {\n        if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create window surface!\");\n        }\n    }\n\n    void pickPhysicalDevice()\n    {\n        uint32_t deviceCount = 0;\n        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);\n\n        if (deviceCount == 0) {\n            throw std::runtime_error(\"failed to find GPUs with Vulkan support!\");\n        }\n\n        std::vector<VkPhysicalDevice> devices(deviceCount);\n        vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());\n\n        for (const auto &device : devices) {\n            if (isDeviceSuitable(device)) {\n                physicalDevice = device;\n                break;\n            }\n        }\n\n        if (physicalDevice == VK_NULL_HANDLE) {\n            throw std::runtime_error(\"failed to find a suitable GPU!\");\n        }\n\n        std::cout << \"Selected physical device = \" << physicalDevice << std::endl;\n\n        VkPhysicalDeviceIDProperties vkPhysicalDeviceIDProperties = {};\n        vkPhysicalDeviceIDProperties.sType                        = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;\n        vkPhysicalDeviceIDProperties.pNext                        = NULL;\n\n        VkPhysicalDeviceProperties2 vkPhysicalDeviceProperties2 = {};\n        vkPhysicalDeviceProperties2.sType                       = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;\n        vkPhysicalDeviceProperties2.pNext                       = &vkPhysicalDeviceIDProperties;\n\n        fpGetPhysicalDeviceProperties2(physicalDevice, &vkPhysicalDeviceProperties2);\n\n        memcpy(vkDeviceUUID, vkPhysicalDeviceIDProperties.deviceUUID, sizeof(vkDeviceUUID));\n    }\n\n    void getKhrExtensionsFn()\n    {\n#ifdef _WIN64\n\n        fpGetSemaphoreWin32HandleKHR =\n            (PFN_vkGetSemaphoreWin32HandleKHR)vkGetDeviceProcAddr(device, \"vkGetSemaphoreWin32HandleKHR\");\n        if (fpGetSemaphoreWin32HandleKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetSemaphoreWin32HandleKHR\\\" not \"\n                                     \"found.\\n\");\n        }\n#else\n        fpGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR)vkGetDeviceProcAddr(device, \"vkGetSemaphoreFdKHR\");\n        if (fpGetSemaphoreFdKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetSemaphoreFdKHR\\\" not found.\\n\");\n        }\n#endif\n    }\n\n    int setCudaVkDevice()\n    {\n        int current_device     = 0;\n        int device_count       = 0;\n        int devices_prohibited = 0;\n\n        cudaDeviceProp deviceProp;\n        int            computeMode;\n        checkCudaErrors(cudaGetDeviceCount(&device_count));\n\n        if (device_count == 0) {\n            fprintf(stderr, \"CUDA error: no devices supporting CUDA.\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        // Find the GPU which is selected by Vulkan\n        while (current_device < device_count) {\n            cudaGetDeviceProperties(&deviceProp, current_device);\n            checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, current_device));\n            if ((computeMode != cudaComputeModeProhibited)) {\n                // Compare the cuda device UUID with vulkan UUID\n                int ret = memcmp(&deviceProp.uuid, &vkDeviceUUID, VK_UUID_SIZE);\n                if (ret == 0) {\n                    checkCudaErrors(cudaSetDevice(current_device));\n                    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));\n                    printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\\n\",\n                           current_device,\n                           deviceProp.name,\n                           deviceProp.major,\n                           deviceProp.minor);\n\n                    return current_device;\n                }\n            }\n            else {\n                devices_prohibited++;\n            }\n\n            current_device++;\n        }\n\n        if (devices_prohibited == device_count) {\n            fprintf(stderr,\n                    \"CUDA error:\"\n                    \" No Vulkan-CUDA Interop capable GPU found.\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        return -1;\n    }\n\n    void createLogicalDevice()\n    {\n        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);\n\n        std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;\n        std::set<int>                        uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};\n\n        float queuePriority = 1.0f;\n        for (int queueFamily : uniqueQueueFamilies) {\n            VkDeviceQueueCreateInfo queueCreateInfo = {};\n            queueCreateInfo.sType                   = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n            queueCreateInfo.queueFamilyIndex        = queueFamily;\n            queueCreateInfo.queueCount              = 1;\n            queueCreateInfo.pQueuePriorities        = &queuePriority;\n            queueCreateInfos.push_back(queueCreateInfo);\n        }\n\n        VkPhysicalDeviceFeatures deviceFeatures = {};\n        deviceFeatures.samplerAnisotropy        = VK_TRUE;\n\n        VkDeviceCreateInfo createInfo = {};\n        createInfo.sType              = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;\n\n        createInfo.pQueueCreateInfos    = queueCreateInfos.data();\n        createInfo.queueCreateInfoCount = queueCreateInfos.size();\n\n        createInfo.pEnabledFeatures = &deviceFeatures;\n        std::vector<const char *> enabledExtensionNameList;\n\n        for (int i = 0; i < deviceExtensions.size(); i++) {\n            enabledExtensionNameList.push_back(deviceExtensions[i]);\n        }\n        if (enableValidationLayers) {\n            createInfo.enabledLayerCount   = static_cast<uint32_t>(validationLayers.size());\n            createInfo.ppEnabledLayerNames = validationLayers.data();\n        }\n        else {\n            createInfo.enabledLayerCount = 0;\n        }\n        createInfo.enabledExtensionCount   = static_cast<uint32_t>(enabledExtensionNameList.size());\n        createInfo.ppEnabledExtensionNames = enabledExtensionNameList.data();\n\n        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create logical device!\");\n        }\n        vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);\n        vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);\n    }\n\n    void createSwapChain()\n    {\n        SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);\n\n        VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);\n        VkPresentModeKHR   presentMode   = chooseSwapPresentMode(swapChainSupport.presentModes);\n        VkExtent2D         extent        = chooseSwapExtent(swapChainSupport.capabilities);\n\n        uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;\n        if (swapChainSupport.capabilities.maxImageCount > 0\n            && imageCount > swapChainSupport.capabilities.maxImageCount) {\n            imageCount = swapChainSupport.capabilities.maxImageCount;\n        }\n\n        VkSwapchainCreateInfoKHR createInfo = {};\n        createInfo.sType                    = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;\n        createInfo.surface                  = surface;\n\n        createInfo.minImageCount    = imageCount;\n        createInfo.imageFormat      = surfaceFormat.format;\n        createInfo.imageColorSpace  = surfaceFormat.colorSpace;\n        createInfo.imageExtent      = extent;\n        createInfo.imageArrayLayers = 1;\n        createInfo.imageUsage       = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n        QueueFamilyIndices indices              = findQueueFamilies(physicalDevice);\n        uint32_t           queueFamilyIndices[] = {(uint32_t)indices.graphicsFamily, (uint32_t)indices.presentFamily};\n\n        if (indices.graphicsFamily != indices.presentFamily) {\n            createInfo.imageSharingMode      = VK_SHARING_MODE_CONCURRENT;\n            createInfo.queueFamilyIndexCount = 2;\n            createInfo.pQueueFamilyIndices   = queueFamilyIndices;\n        }\n        else {\n            createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;\n        }\n\n        createInfo.preTransform   = swapChainSupport.capabilities.currentTransform;\n        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;\n        createInfo.presentMode    = presentMode;\n        createInfo.clipped        = VK_TRUE;\n\n        if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create swap chain!\");\n        }\n\n        vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);\n        swapChainImages.resize(imageCount);\n        vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());\n\n        swapChainImageFormat = surfaceFormat.format;\n        swapChainExtent      = extent;\n    }\n\n    void createImageViews()\n    {\n        swapChainImageViews.resize(swapChainImages.size());\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);\n        }\n    }\n\n    void createRenderPass()\n    {\n        VkAttachmentDescription colorAttachment = {};\n        colorAttachment.format                  = swapChainImageFormat;\n        colorAttachment.samples                 = VK_SAMPLE_COUNT_1_BIT;\n        colorAttachment.loadOp                  = VK_ATTACHMENT_LOAD_OP_CLEAR;\n        colorAttachment.storeOp                 = VK_ATTACHMENT_STORE_OP_STORE;\n        colorAttachment.stencilLoadOp           = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n        colorAttachment.stencilStoreOp          = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n        colorAttachment.initialLayout           = VK_IMAGE_LAYOUT_UNDEFINED;\n        colorAttachment.finalLayout             = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n\n        VkAttachmentReference colorAttachmentRef = {};\n        colorAttachmentRef.attachment            = 0;\n        colorAttachmentRef.layout                = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n        VkSubpassDescription subpass = {};\n        subpass.pipelineBindPoint    = VK_PIPELINE_BIND_POINT_GRAPHICS;\n        subpass.colorAttachmentCount = 1;\n        subpass.pColorAttachments    = &colorAttachmentRef;\n\n        VkSubpassDependency dependency = {};\n        dependency.srcSubpass          = VK_SUBPASS_EXTERNAL;\n        dependency.dstSubpass          = 0;\n        dependency.srcStageMask        = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        dependency.srcAccessMask       = 0;\n        dependency.dstStageMask        = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        dependency.dstAccessMask       = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n        VkRenderPassCreateInfo renderPassInfo = {};\n        renderPassInfo.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;\n        renderPassInfo.attachmentCount        = 1;\n        renderPassInfo.pAttachments           = &colorAttachment;\n        renderPassInfo.subpassCount           = 1;\n        renderPassInfo.pSubpasses             = &subpass;\n        renderPassInfo.dependencyCount        = 1;\n        renderPassInfo.pDependencies          = &dependency;\n\n        if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create render pass!\");\n        }\n    }\n\n    void createDescriptorSetLayout()\n    {\n        VkDescriptorSetLayoutBinding uboLayoutBinding = {};\n        uboLayoutBinding.binding                      = 0;\n        uboLayoutBinding.descriptorCount              = 1;\n        uboLayoutBinding.descriptorType               = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n        uboLayoutBinding.pImmutableSamplers           = nullptr;\n        uboLayoutBinding.stageFlags                   = VK_SHADER_STAGE_VERTEX_BIT;\n\n        VkDescriptorSetLayoutBinding samplerLayoutBinding = {};\n        samplerLayoutBinding.binding                      = 1;\n        samplerLayoutBinding.descriptorCount              = 1;\n        samplerLayoutBinding.descriptorType               = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        samplerLayoutBinding.pImmutableSamplers           = nullptr;\n        samplerLayoutBinding.stageFlags                   = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n        std::array<VkDescriptorSetLayoutBinding, 2> bindings   = {uboLayoutBinding, samplerLayoutBinding};\n        VkDescriptorSetLayoutCreateInfo             layoutInfo = {};\n        layoutInfo.sType                                       = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n        layoutInfo.bindingCount                                = static_cast<uint32_t>(bindings.size());\n        layoutInfo.pBindings                                   = bindings.data();\n\n        if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create descriptor set layout!\");\n        }\n    }\n\n    void createGraphicsPipeline()\n    {\n        auto vertShaderCode = readFile(\"vert.spv\");\n        auto fragShaderCode = readFile(\"frag.spv\");\n\n        VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);\n        VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);\n\n        VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};\n        vertShaderStageInfo.sType                           = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n        vertShaderStageInfo.stage                           = VK_SHADER_STAGE_VERTEX_BIT;\n        vertShaderStageInfo.module                          = vertShaderModule;\n        vertShaderStageInfo.pName                           = \"main\";\n\n        VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};\n        fragShaderStageInfo.sType                           = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n        fragShaderStageInfo.stage                           = VK_SHADER_STAGE_FRAGMENT_BIT;\n        fragShaderStageInfo.module                          = fragShaderModule;\n        fragShaderStageInfo.pName                           = \"main\";\n\n        VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};\n\n        VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};\n        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;\n\n        auto bindingDescription    = Vertex::getBindingDescription();\n        auto attributeDescriptions = Vertex::getAttributeDescriptions();\n\n        vertexInputInfo.vertexBindingDescriptionCount   = 1;\n        vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());\n        vertexInputInfo.pVertexBindingDescriptions      = &bindingDescription;\n        vertexInputInfo.pVertexAttributeDescriptions    = attributeDescriptions.data();\n\n        VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};\n        inputAssembly.sType                  = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;\n        inputAssembly.topology               = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n        inputAssembly.primitiveRestartEnable = VK_FALSE;\n\n        VkViewport viewport = {};\n        viewport.x          = 0.0f;\n        viewport.y          = 0.0f;\n        viewport.width      = (float)swapChainExtent.width;\n        viewport.height     = (float)swapChainExtent.height;\n        viewport.minDepth   = 0.0f;\n        viewport.maxDepth   = 1.0f;\n\n        VkRect2D scissor = {};\n        scissor.offset   = {0, 0};\n        scissor.extent   = swapChainExtent;\n\n        VkPipelineViewportStateCreateInfo viewportState = {};\n        viewportState.sType                             = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n        viewportState.viewportCount                     = 1;\n        viewportState.pViewports                        = &viewport;\n        viewportState.scissorCount                      = 1;\n        viewportState.pScissors                         = &scissor;\n\n        VkPipelineRasterizationStateCreateInfo rasterizer = {};\n        rasterizer.sType                                  = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;\n        rasterizer.depthClampEnable                       = VK_FALSE;\n        rasterizer.rasterizerDiscardEnable                = VK_FALSE;\n        rasterizer.polygonMode                            = VK_POLYGON_MODE_FILL;\n        rasterizer.lineWidth                              = 1.0f;\n        rasterizer.cullMode                               = VK_CULL_MODE_BACK_BIT;\n        rasterizer.frontFace                              = VK_FRONT_FACE_COUNTER_CLOCKWISE;\n        rasterizer.depthBiasEnable                        = VK_FALSE;\n\n        VkPipelineMultisampleStateCreateInfo multisampling = {};\n        multisampling.sType                                = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;\n        multisampling.sampleShadingEnable                  = VK_FALSE;\n        multisampling.rasterizationSamples                 = VK_SAMPLE_COUNT_1_BIT;\n\n        VkPipelineColorBlendAttachmentState colorBlendAttachment = {};\n        colorBlendAttachment.colorWriteMask =\n            VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n        colorBlendAttachment.blendEnable = VK_FALSE;\n\n        VkPipelineColorBlendStateCreateInfo colorBlending = {};\n        colorBlending.sType                               = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;\n        colorBlending.logicOpEnable                       = VK_FALSE;\n        colorBlending.logicOp                             = VK_LOGIC_OP_COPY;\n        colorBlending.attachmentCount                     = 1;\n        colorBlending.pAttachments                        = &colorBlendAttachment;\n        colorBlending.blendConstants[0]                   = 0.0f;\n        colorBlending.blendConstants[1]                   = 0.0f;\n        colorBlending.blendConstants[2]                   = 0.0f;\n        colorBlending.blendConstants[3]                   = 0.0f;\n\n        VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};\n        pipelineLayoutInfo.sType                      = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n        pipelineLayoutInfo.setLayoutCount             = 1;\n        pipelineLayoutInfo.pSetLayouts                = &descriptorSetLayout;\n\n        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create pipeline layout!\");\n        }\n\n        VkGraphicsPipelineCreateInfo pipelineInfo = {};\n        pipelineInfo.sType                        = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n        pipelineInfo.stageCount                   = 2;\n        pipelineInfo.pStages                      = shaderStages;\n        pipelineInfo.pVertexInputState            = &vertexInputInfo;\n        pipelineInfo.pInputAssemblyState          = &inputAssembly;\n        pipelineInfo.pViewportState               = &viewportState;\n        pipelineInfo.pRasterizationState          = &rasterizer;\n        pipelineInfo.pMultisampleState            = &multisampling;\n        pipelineInfo.pColorBlendState             = &colorBlending;\n        pipelineInfo.layout                       = pipelineLayout;\n        pipelineInfo.renderPass                   = renderPass;\n        pipelineInfo.subpass                      = 0;\n        pipelineInfo.basePipelineHandle           = VK_NULL_HANDLE;\n\n        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline)\n            != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create graphics pipeline!\");\n        }\n\n        vkDestroyShaderModule(device, fragShaderModule, nullptr);\n        vkDestroyShaderModule(device, vertShaderModule, nullptr);\n    }\n\n    void createFramebuffers()\n    {\n        swapChainFramebuffers.resize(swapChainImageViews.size());\n\n        for (size_t i = 0; i < swapChainImageViews.size(); i++) {\n            VkImageView attachments[] = {swapChainImageViews[i]};\n\n            VkFramebufferCreateInfo framebufferInfo = {};\n            framebufferInfo.sType                   = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;\n            framebufferInfo.renderPass              = renderPass;\n            framebufferInfo.attachmentCount         = 1;\n            framebufferInfo.pAttachments            = attachments;\n            framebufferInfo.width                   = swapChainExtent.width;\n            framebufferInfo.height                  = swapChainExtent.height;\n            framebufferInfo.layers                  = 1;\n\n            if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to create framebuffer!\");\n            }\n        }\n    }\n\n    void createCommandPool()\n    {\n        QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);\n\n        VkCommandPoolCreateInfo poolInfo = {};\n        poolInfo.sType                   = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n        poolInfo.queueFamilyIndex        = queueFamilyIndices.graphicsFamily;\n\n        if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create graphics command pool!\");\n        }\n    }\n\n    void createTextureImage()\n    {\n        VkDeviceSize imageSize = imageWidth * imageHeight * 4;\n        mipLevels              = static_cast<uint32_t>(std::floor(std::log2(std::max(imageWidth, imageHeight)))) + 1;\n        printf(\"mipLevels = %d\\n\", mipLevels);\n\n        if (!image_data) {\n            throw std::runtime_error(\"failed to load texture image!\");\n        }\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(imageSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);\n        memcpy(data, image_data, static_cast<size_t>(imageSize));\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        // VK_FORMAT_R8G8B8A8_UNORM changed to VK_FORMAT_R8G8B8A8_UINT\n        createImage(imageWidth,\n                    imageHeight,\n                    VK_FORMAT_R8G8B8A8_UNORM,\n                    VK_IMAGE_TILING_OPTIMAL,\n                    VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                        | VK_IMAGE_USAGE_SAMPLED_BIT,\n                    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                    textureImage,\n                    textureImageMemory);\n\n        transitionImageLayout(\n            textureImage, VK_FORMAT_R8G8B8A8_UINT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n        copyBufferToImage(\n            stagingBuffer, textureImage, static_cast<uint32_t>(imageWidth), static_cast<uint32_t>(imageHeight));\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n\n        generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_UNORM);\n    }\n\n    void generateMipmaps(VkImage image, VkFormat imageFormat)\n    {\n        VkFormatProperties formatProperties;\n        vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);\n\n        if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {\n            throw std::runtime_error(\"texture image format does not support linear blitting!\");\n        }\n\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkImageMemoryBarrier barrier            = {};\n        barrier.sType                           = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        barrier.image                           = image;\n        barrier.srcQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.dstQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        barrier.subresourceRange.baseArrayLayer = 0;\n        barrier.subresourceRange.layerCount     = 1;\n        barrier.subresourceRange.levelCount     = 1;\n\n        int32_t mipWidth  = imageWidth;\n        int32_t mipHeight = imageHeight;\n\n        for (uint32_t i = 1; i < mipLevels; i++) {\n            barrier.subresourceRange.baseMipLevel = i - 1;\n            barrier.oldLayout                     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n            barrier.newLayout                     = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;\n            barrier.srcAccessMask                 = VK_ACCESS_TRANSFER_WRITE_BIT;\n            barrier.dstAccessMask                 = VK_ACCESS_TRANSFER_READ_BIT;\n\n            vkCmdPipelineBarrier(commandBuffer,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 0,\n                                 0,\n                                 nullptr,\n                                 0,\n                                 nullptr,\n                                 1,\n                                 &barrier);\n\n            VkImageBlit blit                   = {};\n            blit.srcOffsets[0]                 = {0, 0, 0};\n            blit.srcOffsets[1]                 = {mipWidth, mipHeight, 1};\n            blit.srcSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n            blit.srcSubresource.mipLevel       = i - 1;\n            blit.srcSubresource.baseArrayLayer = 0;\n            blit.srcSubresource.layerCount     = 1;\n            blit.dstOffsets[0]                 = {0, 0, 0};\n            blit.dstOffsets[1]             = {mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1};\n            blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n            blit.dstSubresource.mipLevel   = i;\n            blit.dstSubresource.baseArrayLayer = 0;\n            blit.dstSubresource.layerCount     = 1;\n\n            vkCmdBlitImage(commandBuffer,\n                           image,\n                           VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n                           image,\n                           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n                           1,\n                           &blit,\n                           VK_FILTER_LINEAR);\n\n            barrier.oldLayout     = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;\n            barrier.newLayout     = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n            barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n            barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n\n            vkCmdPipelineBarrier(commandBuffer,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n                                 0,\n                                 0,\n                                 nullptr,\n                                 0,\n                                 nullptr,\n                                 1,\n                                 &barrier);\n\n            if (mipWidth > 1)\n                mipWidth /= 2;\n            if (mipHeight > 1)\n                mipHeight /= 2;\n        }\n\n        barrier.subresourceRange.baseMipLevel = mipLevels - 1;\n        barrier.oldLayout                     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n        barrier.newLayout                     = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n        barrier.srcAccessMask                 = VK_ACCESS_TRANSFER_WRITE_BIT;\n        barrier.dstAccessMask                 = VK_ACCESS_SHADER_READ_BIT;\n\n        vkCmdPipelineBarrier(commandBuffer,\n                             VK_PIPELINE_STAGE_TRANSFER_BIT,\n                             VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n                             0,\n                             0,\n                             nullptr,\n                             0,\n                             nullptr,\n                             1,\n                             &barrier);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n#ifdef _WIN64 // For windows\n    HANDLE getVkImageMemHandle(VkExternalMemoryHandleTypeFlagsKHR externalMemoryHandleType)\n    {\n        HANDLE handle;\n\n        VkMemoryGetWin32HandleInfoKHR vkMemoryGetWin32HandleInfoKHR = {};\n        vkMemoryGetWin32HandleInfoKHR.sType      = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;\n        vkMemoryGetWin32HandleInfoKHR.pNext      = NULL;\n        vkMemoryGetWin32HandleInfoKHR.memory     = textureImageMemory;\n        vkMemoryGetWin32HandleInfoKHR.handleType = (VkExternalMemoryHandleTypeFlagBitsKHR)externalMemoryHandleType;\n\n        fpGetMemoryWin32HandleKHR(device, &vkMemoryGetWin32HandleInfoKHR, &handle);\n        return handle;\n    }\n    HANDLE getVkSemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBitsKHR externalSemaphoreHandleType,\n                                VkSemaphore                             &semVkCuda)\n    {\n        HANDLE handle;\n\n        VkSemaphoreGetWin32HandleInfoKHR vulkanSemaphoreGetWin32HandleInfoKHR = {};\n        vulkanSemaphoreGetWin32HandleInfoKHR.sType      = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR;\n        vulkanSemaphoreGetWin32HandleInfoKHR.pNext      = NULL;\n        vulkanSemaphoreGetWin32HandleInfoKHR.semaphore  = semVkCuda;\n        vulkanSemaphoreGetWin32HandleInfoKHR.handleType = externalSemaphoreHandleType;\n\n        fpGetSemaphoreWin32HandleKHR(device, &vulkanSemaphoreGetWin32HandleInfoKHR, &handle);\n\n        return handle;\n    }\n#else\n    int getVkImageMemHandle(VkExternalMemoryHandleTypeFlagsKHR externalMemoryHandleType)\n    {\n        if (externalMemoryHandleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR) {\n            int fd;\n\n            VkMemoryGetFdInfoKHR vkMemoryGetFdInfoKHR = {};\n            vkMemoryGetFdInfoKHR.sType                = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;\n            vkMemoryGetFdInfoKHR.pNext                = NULL;\n            vkMemoryGetFdInfoKHR.memory               = textureImageMemory;\n            vkMemoryGetFdInfoKHR.handleType           = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n\n            fpGetMemoryFdKHR(device, &vkMemoryGetFdInfoKHR, &fd);\n\n            return fd;\n        }\n        return -1;\n    }\n\n    int getVkSemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBitsKHR externalSemaphoreHandleType,\n                             VkSemaphore                             &semVkCuda)\n    {\n        if (externalSemaphoreHandleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {\n            int fd;\n\n            VkSemaphoreGetFdInfoKHR vulkanSemaphoreGetFdInfoKHR = {};\n            vulkanSemaphoreGetFdInfoKHR.sType                   = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;\n            vulkanSemaphoreGetFdInfoKHR.pNext                   = NULL;\n            vulkanSemaphoreGetFdInfoKHR.semaphore               = semVkCuda;\n            vulkanSemaphoreGetFdInfoKHR.handleType              = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n\n            fpGetSemaphoreFdKHR(device, &vulkanSemaphoreGetFdInfoKHR, &fd);\n\n            return fd;\n        }\n        return -1;\n    }\n#endif\n\n    void createTextureImageView() { textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); }\n\n    void createTextureSampler()\n    {\n        VkSamplerCreateInfo samplerInfo     = {};\n        samplerInfo.sType                   = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n        samplerInfo.magFilter               = VK_FILTER_LINEAR;\n        samplerInfo.minFilter               = VK_FILTER_LINEAR;\n        samplerInfo.addressModeU            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.addressModeV            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.addressModeW            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.anisotropyEnable        = VK_TRUE;\n        samplerInfo.maxAnisotropy           = 16;\n        samplerInfo.borderColor             = VK_BORDER_COLOR_INT_OPAQUE_BLACK;\n        samplerInfo.unnormalizedCoordinates = VK_FALSE;\n        samplerInfo.compareEnable           = VK_FALSE;\n        samplerInfo.compareOp               = VK_COMPARE_OP_ALWAYS;\n        samplerInfo.mipmapMode              = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n        samplerInfo.minLod                  = 0; // Optional\n        samplerInfo.maxLod                  = static_cast<float>(mipLevels);\n        samplerInfo.mipLodBias              = 0; // Optional\n\n        if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create texture sampler!\");\n        }\n    }\n\n    VkImageView createImageView(VkImage image, VkFormat format)\n    {\n        VkImageViewCreateInfo viewInfo           = {};\n        viewInfo.sType                           = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n        viewInfo.image                           = image;\n        viewInfo.viewType                        = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.format                          = format;\n        viewInfo.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        viewInfo.subresourceRange.baseMipLevel   = 0;\n        viewInfo.subresourceRange.levelCount     = mipLevels;\n        viewInfo.subresourceRange.baseArrayLayer = 0;\n        viewInfo.subresourceRange.layerCount     = 1;\n\n        VkImageView imageView;\n        if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create texture image view!\");\n        }\n\n        return imageView;\n    }\n\n    void createImage(uint32_t              width,\n                     uint32_t              height,\n                     VkFormat              format,\n                     VkImageTiling         tiling,\n                     VkImageUsageFlags     usage,\n                     VkMemoryPropertyFlags properties,\n                     VkImage              &image,\n                     VkDeviceMemory       &imageMemory)\n    {\n        VkImageCreateInfo imageInfo = {};\n        imageInfo.sType             = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n        imageInfo.imageType         = VK_IMAGE_TYPE_2D;\n        imageInfo.extent.width      = width;\n        imageInfo.extent.height     = height;\n        imageInfo.extent.depth      = 1;\n        imageInfo.mipLevels         = mipLevels;\n        imageInfo.arrayLayers       = 1;\n        imageInfo.format            = format;\n        imageInfo.tiling            = tiling;\n        imageInfo.initialLayout     = VK_IMAGE_LAYOUT_UNDEFINED;\n        imageInfo.usage             = usage;\n        imageInfo.samples           = VK_SAMPLE_COUNT_1_BIT;\n        imageInfo.sharingMode       = VK_SHARING_MODE_EXCLUSIVE;\n\n        VkExternalMemoryImageCreateInfo vkExternalMemImageCreateInfo = {};\n        vkExternalMemImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n        vkExternalMemImageCreateInfo.pNext = NULL;\n#ifdef _WIN64\n        vkExternalMemImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;\n#else\n        vkExternalMemImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n#endif\n\n        imageInfo.pNext = &vkExternalMemImageCreateInfo;\n\n        if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create image!\");\n        }\n\n        VkMemoryRequirements memRequirements;\n        vkGetImageMemoryRequirements(device, image, &memRequirements);\n\n#ifdef _WIN64\n        WindowsSecurityAttributes winSecurityAttributes;\n\n        VkExportMemoryWin32HandleInfoKHR vulkanExportMemoryWin32HandleInfoKHR = {};\n        vulkanExportMemoryWin32HandleInfoKHR.sType       = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR;\n        vulkanExportMemoryWin32HandleInfoKHR.pNext       = NULL;\n        vulkanExportMemoryWin32HandleInfoKHR.pAttributes = &winSecurityAttributes;\n        vulkanExportMemoryWin32HandleInfoKHR.dwAccess    = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;\n        vulkanExportMemoryWin32HandleInfoKHR.name        = (LPCWSTR)NULL;\n#endif\n        VkExportMemoryAllocateInfoKHR vulkanExportMemoryAllocateInfoKHR = {};\n        vulkanExportMemoryAllocateInfoKHR.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;\n#ifdef _WIN64\n        vulkanExportMemoryAllocateInfoKHR.pNext = IsWindows8OrGreater() ? &vulkanExportMemoryWin32HandleInfoKHR : NULL;\n        vulkanExportMemoryAllocateInfoKHR.handleTypes = IsWindows8OrGreater()\n                                                          ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                          : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n#else\n        vulkanExportMemoryAllocateInfoKHR.pNext       = NULL;\n        vulkanExportMemoryAllocateInfoKHR.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n#endif\n\n        VkMemoryAllocateInfo allocInfo = {};\n        allocInfo.sType                = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        allocInfo.allocationSize       = memRequirements.size;\n        allocInfo.pNext                = &vulkanExportMemoryAllocateInfoKHR;\n        allocInfo.memoryTypeIndex      = findMemoryType(memRequirements.memoryTypeBits, properties);\n\n        VkMemoryRequirements vkMemoryRequirements = {};\n        vkGetImageMemoryRequirements(device, image, &vkMemoryRequirements);\n        totalImageMemSize = vkMemoryRequirements.size;\n\n        if (vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate image memory!\");\n        }\n\n        vkBindImageMemory(device, image, textureImageMemory, 0);\n    }\n\n    void cudaVkImportSemaphore()\n    {\n        cudaExternalSemaphoreHandleDesc externalSemaphoreHandleDesc;\n        memset(&externalSemaphoreHandleDesc, 0, sizeof(externalSemaphoreHandleDesc));\n#ifdef _WIN64\n        externalSemaphoreHandleDesc.type = IsWindows8OrGreater() ? cudaExternalSemaphoreHandleTypeOpaqueWin32\n                                                                 : cudaExternalSemaphoreHandleTypeOpaqueWin32Kmt;\n        externalSemaphoreHandleDesc.handle.win32.handle =\n            getVkSemaphoreHandle(IsWindows8OrGreater() ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                       : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,\n                                 cudaUpdateVkSemaphore);\n#else\n        externalSemaphoreHandleDesc.type = cudaExternalSemaphoreHandleTypeOpaqueFd;\n        externalSemaphoreHandleDesc.handle.fd =\n            getVkSemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, cudaUpdateVkSemaphore);\n#endif\n        externalSemaphoreHandleDesc.flags = 0;\n\n        checkCudaErrors(cudaImportExternalSemaphore(&cudaExtCudaUpdateVkSemaphore, &externalSemaphoreHandleDesc));\n\n        memset(&externalSemaphoreHandleDesc, 0, sizeof(externalSemaphoreHandleDesc));\n#ifdef _WIN64\n        externalSemaphoreHandleDesc.type = IsWindows8OrGreater() ? cudaExternalSemaphoreHandleTypeOpaqueWin32\n                                                                 : cudaExternalSemaphoreHandleTypeOpaqueWin32Kmt;\n        ;\n        externalSemaphoreHandleDesc.handle.win32.handle =\n            getVkSemaphoreHandle(IsWindows8OrGreater() ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                       : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,\n                                 vkUpdateCudaSemaphore);\n#else\n        externalSemaphoreHandleDesc.type = cudaExternalSemaphoreHandleTypeOpaqueFd;\n        externalSemaphoreHandleDesc.handle.fd =\n            getVkSemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, vkUpdateCudaSemaphore);\n#endif\n        externalSemaphoreHandleDesc.flags = 0;\n        checkCudaErrors(cudaImportExternalSemaphore(&cudaExtVkUpdateCudaSemaphore, &externalSemaphoreHandleDesc));\n        printf(\"CUDA Imported Vulkan semaphore\\n\");\n    }\n\n    void cudaVkImportImageMem()\n    {\n        cudaExternalMemoryHandleDesc cudaExtMemHandleDesc;\n        memset(&cudaExtMemHandleDesc, 0, sizeof(cudaExtMemHandleDesc));\n#ifdef _WIN64\n        cudaExtMemHandleDesc.type = IsWindows8OrGreater() ? cudaExternalMemoryHandleTypeOpaqueWin32\n                                                          : cudaExternalMemoryHandleTypeOpaqueWin32Kmt;\n        cudaExtMemHandleDesc.handle.win32.handle =\n            getVkImageMemHandle(IsWindows8OrGreater() ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                      : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT);\n#else\n        cudaExtMemHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueFd;\n\n        cudaExtMemHandleDesc.handle.fd = getVkImageMemHandle(VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR);\n#endif\n        cudaExtMemHandleDesc.size = totalImageMemSize;\n\n        checkCudaErrors(cudaImportExternalMemory(&cudaExtMemImageBuffer, &cudaExtMemHandleDesc));\n\n        cudaExternalMemoryMipmappedArrayDesc externalMemoryMipmappedArrayDesc;\n\n        memset(&externalMemoryMipmappedArrayDesc, 0, sizeof(externalMemoryMipmappedArrayDesc));\n\n        cudaExtent            extent = make_cudaExtent(imageWidth, imageHeight, 0);\n        cudaChannelFormatDesc formatDesc;\n        formatDesc.x = 8;\n        formatDesc.y = 8;\n        formatDesc.z = 8;\n        formatDesc.w = 8;\n        formatDesc.f = cudaChannelFormatKindUnsigned;\n\n        externalMemoryMipmappedArrayDesc.offset     = 0;\n        externalMemoryMipmappedArrayDesc.formatDesc = formatDesc;\n        externalMemoryMipmappedArrayDesc.extent     = extent;\n        externalMemoryMipmappedArrayDesc.flags      = 0;\n        externalMemoryMipmappedArrayDesc.numLevels  = mipLevels;\n\n        checkCudaErrors(cudaExternalMemoryGetMappedMipmappedArray(\n            &cudaMipmappedImageArray, cudaExtMemImageBuffer, &externalMemoryMipmappedArrayDesc));\n\n        checkCudaErrors(cudaMallocMipmappedArray(&cudaMipmappedImageArrayTemp, &formatDesc, extent, mipLevels));\n        checkCudaErrors(cudaMallocMipmappedArray(&cudaMipmappedImageArrayOrig, &formatDesc, extent, mipLevels));\n\n        for (int mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n            cudaArray_t      cudaMipLevelArray, cudaMipLevelArrayTemp, cudaMipLevelArrayOrig;\n            cudaResourceDesc resourceDesc;\n\n            checkCudaErrors(cudaGetMipmappedArrayLevel(&cudaMipLevelArray, cudaMipmappedImageArray, mipLevelIdx));\n            checkCudaErrors(\n                cudaGetMipmappedArrayLevel(&cudaMipLevelArrayTemp, cudaMipmappedImageArrayTemp, mipLevelIdx));\n            checkCudaErrors(\n                cudaGetMipmappedArrayLevel(&cudaMipLevelArrayOrig, cudaMipmappedImageArrayOrig, mipLevelIdx));\n\n            uint32_t width  = (imageWidth >> mipLevelIdx) ? (imageWidth >> mipLevelIdx) : 1;\n            uint32_t height = (imageHeight >> mipLevelIdx) ? (imageHeight >> mipLevelIdx) : 1;\n            checkCudaErrors(cudaMemcpy2DArrayToArray(cudaMipLevelArrayOrig,\n                                                     0,\n                                                     0,\n                                                     cudaMipLevelArray,\n                                                     0,\n                                                     0,\n                                                     width * sizeof(uchar4),\n                                                     height,\n                                                     cudaMemcpyDeviceToDevice));\n\n            memset(&resourceDesc, 0, sizeof(resourceDesc));\n            resourceDesc.resType         = cudaResourceTypeArray;\n            resourceDesc.res.array.array = cudaMipLevelArray;\n\n            cudaSurfaceObject_t surfaceObject;\n            checkCudaErrors(cudaCreateSurfaceObject(&surfaceObject, &resourceDesc));\n\n            surfaceObjectList.push_back(surfaceObject);\n\n            memset(&resourceDesc, 0, sizeof(resourceDesc));\n            resourceDesc.resType         = cudaResourceTypeArray;\n            resourceDesc.res.array.array = cudaMipLevelArrayTemp;\n\n            cudaSurfaceObject_t surfaceObjectTemp;\n            checkCudaErrors(cudaCreateSurfaceObject(&surfaceObjectTemp, &resourceDesc));\n            surfaceObjectListTemp.push_back(surfaceObjectTemp);\n        }\n\n        cudaResourceDesc resDescr;\n        memset(&resDescr, 0, sizeof(cudaResourceDesc));\n\n        resDescr.resType           = cudaResourceTypeMipmappedArray;\n        resDescr.res.mipmap.mipmap = cudaMipmappedImageArrayOrig;\n\n        cudaTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n        texDescr.normalizedCoords = true;\n        texDescr.filterMode       = cudaFilterModeLinear;\n        texDescr.mipmapFilterMode = cudaFilterModeLinear;\n\n        texDescr.addressMode[0] = cudaAddressModeWrap;\n        texDescr.addressMode[1] = cudaAddressModeWrap;\n\n        texDescr.maxMipmapLevelClamp = float(mipLevels - 1);\n\n        texDescr.readMode = cudaReadModeNormalizedFloat;\n\n        checkCudaErrors(cudaCreateTextureObject(&textureObjMipMapInput, &resDescr, &texDescr, NULL));\n\n        checkCudaErrors(cudaMalloc((void **)&d_surfaceObjectList, sizeof(cudaSurfaceObject_t) * mipLevels));\n        checkCudaErrors(cudaMalloc((void **)&d_surfaceObjectListTemp, sizeof(cudaSurfaceObject_t) * mipLevels));\n\n        checkCudaErrors(cudaMemcpy(d_surfaceObjectList,\n                                   surfaceObjectList.data(),\n                                   sizeof(cudaSurfaceObject_t) * mipLevels,\n                                   cudaMemcpyHostToDevice));\n        checkCudaErrors(cudaMemcpy(d_surfaceObjectListTemp,\n                                   surfaceObjectListTemp.data(),\n                                   sizeof(cudaSurfaceObject_t) * mipLevels,\n                                   cudaMemcpyHostToDevice));\n\n        printf(\"CUDA Kernel Vulkan image buffer\\n\");\n    }\n\n    void cudaUpdateVkImage()\n    {\n        cudaVkSemaphoreWait(cudaExtVkUpdateCudaSemaphore);\n\n        int nthreads = 128;\n\n        /*Perform 2D box filter on image using CUDA */\n        d_boxfilter_rgba_x<<<imageHeight / nthreads, nthreads, 0, streamToRun>>>(\n            d_surfaceObjectListTemp, textureObjMipMapInput, imageWidth, imageHeight, mipLevels, filter_radius);\n\n        d_boxfilter_rgba_y<<<imageWidth / nthreads, nthreads, 0, streamToRun>>>(\n            d_surfaceObjectList, d_surfaceObjectListTemp, imageWidth, imageHeight, mipLevels, filter_radius);\n\n        varySigma();\n\n        cudaVkSemaphoreSignal(cudaExtCudaUpdateVkSemaphore);\n    }\n\n    void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkImageMemoryBarrier barrier            = {};\n        barrier.sType                           = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        barrier.oldLayout                       = oldLayout;\n        barrier.newLayout                       = newLayout;\n        barrier.srcQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.dstQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.image                           = image;\n        barrier.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        barrier.subresourceRange.baseMipLevel   = 0;\n        barrier.subresourceRange.levelCount     = mipLevels;\n        barrier.subresourceRange.baseArrayLayer = 0;\n        barrier.subresourceRange.layerCount     = 1;\n\n        VkPipelineStageFlags sourceStage;\n        VkPipelineStageFlags destinationStage;\n\n        if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {\n            barrier.srcAccessMask = 0;\n            barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\n            sourceStage      = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;\n            destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;\n        }\n        else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n                 && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {\n            barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n            barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n\n            sourceStage      = VK_PIPELINE_STAGE_TRANSFER_BIT;\n            destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n        }\n        else {\n            throw std::invalid_argument(\"unsupported layout transition!\");\n        }\n\n        vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkBufferImageCopy region               = {};\n        region.bufferOffset                    = 0;\n        region.bufferRowLength                 = 0;\n        region.bufferImageHeight               = 0;\n        region.imageSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        region.imageSubresource.mipLevel       = 0;\n        region.imageSubresource.baseArrayLayer = 0;\n        region.imageSubresource.layerCount     = 1;\n        region.imageOffset                     = {0, 0, 0};\n        region.imageExtent                     = {width, height, 1};\n\n        vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    void createVertexBuffer()\n    {\n        VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);\n        memcpy(data, vertices.data(), (size_t)bufferSize);\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,\n                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                     vertexBuffer,\n                     vertexBufferMemory);\n\n        copyBuffer(stagingBuffer, vertexBuffer, bufferSize);\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n    }\n\n    void createIndexBuffer()\n    {\n        VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);\n        memcpy(data, indices.data(), (size_t)bufferSize);\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,\n                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                     indexBuffer,\n                     indexBufferMemory);\n\n        copyBuffer(stagingBuffer, indexBuffer, bufferSize);\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n    }\n\n    void createUniformBuffers()\n    {\n        VkDeviceSize bufferSize = sizeof(UniformBufferObject);\n\n        uniformBuffers.resize(swapChainImages.size());\n        uniformBuffersMemory.resize(swapChainImages.size());\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            createBuffer(bufferSize,\n                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,\n                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                         uniformBuffers[i],\n                         uniformBuffersMemory[i]);\n        }\n    }\n\n    void createDescriptorPool()\n    {\n        std::array<VkDescriptorPoolSize, 2> poolSizes = {};\n        poolSizes[0].type                             = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n        poolSizes[0].descriptorCount                  = static_cast<uint32_t>(swapChainImages.size());\n        poolSizes[1].type                             = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        poolSizes[1].descriptorCount                  = static_cast<uint32_t>(swapChainImages.size());\n\n        VkDescriptorPoolCreateInfo poolInfo = {};\n        poolInfo.sType                      = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;\n        poolInfo.poolSizeCount              = static_cast<uint32_t>(poolSizes.size());\n        poolInfo.pPoolSizes                 = poolSizes.data();\n        poolInfo.maxSets                    = static_cast<uint32_t>(swapChainImages.size());\n\n        if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create descriptor pool!\");\n        }\n    }\n\n    void createDescriptorSets()\n    {\n        std::vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), descriptorSetLayout);\n        VkDescriptorSetAllocateInfo        allocInfo = {};\n        allocInfo.sType                              = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n        allocInfo.descriptorPool                     = descriptorPool;\n        allocInfo.descriptorSetCount                 = static_cast<uint32_t>(swapChainImages.size());\n        allocInfo.pSetLayouts                        = layouts.data();\n\n        descriptorSets.resize(swapChainImages.size());\n        if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate descriptor sets!\");\n        }\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            VkDescriptorBufferInfo bufferInfo = {};\n            bufferInfo.buffer                 = uniformBuffers[i];\n            bufferInfo.offset                 = 0;\n            bufferInfo.range                  = sizeof(UniformBufferObject);\n\n            VkDescriptorImageInfo imageInfo = {};\n            imageInfo.imageLayout           = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n            imageInfo.imageView             = textureImageView;\n            imageInfo.sampler               = textureSampler;\n\n            std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};\n\n            descriptorWrites[0].sType           = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n            descriptorWrites[0].dstSet          = descriptorSets[i];\n            descriptorWrites[0].dstBinding      = 0;\n            descriptorWrites[0].dstArrayElement = 0;\n            descriptorWrites[0].descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n            descriptorWrites[0].descriptorCount = 1;\n            descriptorWrites[0].pBufferInfo     = &bufferInfo;\n\n            descriptorWrites[1].sType           = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n            descriptorWrites[1].dstSet          = descriptorSets[i];\n            descriptorWrites[1].dstBinding      = 1;\n            descriptorWrites[1].dstArrayElement = 0;\n            descriptorWrites[1].descriptorType  = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n            descriptorWrites[1].descriptorCount = 1;\n            descriptorWrites[1].pImageInfo      = &imageInfo;\n\n            vkUpdateDescriptorSets(\n                device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);\n        }\n    }\n\n    void createBuffer(VkDeviceSize          size,\n                      VkBufferUsageFlags    usage,\n                      VkMemoryPropertyFlags properties,\n                      VkBuffer             &buffer,\n                      VkDeviceMemory       &bufferMemory)\n    {\n        VkBufferCreateInfo bufferInfo = {};\n        bufferInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n        bufferInfo.size               = size;\n        bufferInfo.usage              = usage;\n        bufferInfo.sharingMode        = VK_SHARING_MODE_EXCLUSIVE;\n\n        if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create buffer!\");\n        }\n\n        VkMemoryRequirements memRequirements;\n        vkGetBufferMemoryRequirements(device, buffer, &memRequirements);\n\n        VkMemoryAllocateInfo allocInfo = {};\n        allocInfo.sType                = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        allocInfo.allocationSize       = memRequirements.size;\n        allocInfo.memoryTypeIndex      = findMemoryType(memRequirements.memoryTypeBits, properties);\n\n        if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate buffer memory!\");\n        }\n\n        vkBindBufferMemory(device, buffer, bufferMemory, 0);\n    }\n\n    VkCommandBuffer beginSingleTimeCommands()\n    {\n        VkCommandBufferAllocateInfo allocInfo = {};\n        allocInfo.sType                       = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n        allocInfo.level                       = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n        allocInfo.commandPool                 = commandPool;\n        allocInfo.commandBufferCount          = 1;\n\n        VkCommandBuffer commandBuffer;\n        vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);\n\n        VkCommandBufferBeginInfo beginInfo = {};\n        beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n        beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n        vkBeginCommandBuffer(commandBuffer, &beginInfo);\n\n        return commandBuffer;\n    }\n\n    void endSingleTimeCommands(VkCommandBuffer commandBuffer)\n    {\n        vkEndCommandBuffer(commandBuffer);\n\n        VkSubmitInfo submitInfo       = {};\n        submitInfo.sType              = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n        submitInfo.commandBufferCount = 1;\n        submitInfo.pCommandBuffers    = &commandBuffer;\n\n        vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);\n        vkQueueWaitIdle(graphicsQueue);\n\n        vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);\n    }\n\n    void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkBufferCopy copyRegion = {};\n        copyRegion.size         = size;\n        vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)\n    {\n        VkPhysicalDeviceMemoryProperties memProperties;\n        vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);\n\n        for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {\n            if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {\n                return i;\n            }\n        }\n\n        throw std::runtime_error(\"failed to find suitable memory type!\");\n    }\n\n    void createCommandBuffers()\n    {\n        commandBuffers.resize(swapChainFramebuffers.size());\n\n        VkCommandBufferAllocateInfo allocInfo = {};\n        allocInfo.sType                       = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n        allocInfo.commandPool                 = commandPool;\n        allocInfo.level                       = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n        allocInfo.commandBufferCount          = (uint32_t)commandBuffers.size();\n\n        if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate command buffers!\");\n        }\n\n        for (size_t i = 0; i < commandBuffers.size(); i++) {\n            VkCommandBufferBeginInfo beginInfo = {};\n            beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n            beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;\n\n            if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to begin recording command buffer!\");\n            }\n\n            VkRenderPassBeginInfo renderPassInfo = {};\n            renderPassInfo.sType                 = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n            renderPassInfo.renderPass            = renderPass;\n            renderPassInfo.framebuffer           = swapChainFramebuffers[i];\n            renderPassInfo.renderArea.offset     = {0, 0};\n            renderPassInfo.renderArea.extent     = swapChainExtent;\n\n            VkClearValue clearColor        = {0.0f, 0.0f, 0.0f, 1.0f};\n            renderPassInfo.clearValueCount = 1;\n            renderPassInfo.pClearValues    = &clearColor;\n\n            vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);\n\n            vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);\n\n            VkBuffer     vertexBuffers[] = {vertexBuffer};\n            VkDeviceSize offsets[]       = {0};\n            vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);\n\n            vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);\n\n            vkCmdBindDescriptorSets(commandBuffers[i],\n                                    VK_PIPELINE_BIND_POINT_GRAPHICS,\n                                    pipelineLayout,\n                                    0,\n                                    1,\n                                    &descriptorSets[i],\n                                    0,\n                                    nullptr);\n\n            vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);\n            // vkCmdDraw(commandBuffers[i], static_cast<uint32_t>(vertices.size()), 1,\n            // 0, 0);\n\n            vkCmdEndRenderPass(commandBuffers[i]);\n\n            if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to record command buffer!\");\n            }\n        }\n    }\n\n    void createSyncObjects()\n    {\n        imageAvailableSemaphores.resize(MAX_FRAMES);\n        renderFinishedSemaphores.resize(MAX_FRAMES);\n        inFlightFences.resize(MAX_FRAMES);\n\n        VkSemaphoreCreateInfo semaphoreInfo = {};\n        semaphoreInfo.sType                 = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n        VkFenceCreateInfo fenceInfo = {};\n        fenceInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;\n        fenceInfo.flags             = VK_FENCE_CREATE_SIGNALED_BIT;\n\n        for (size_t i = 0; i < MAX_FRAMES; i++) {\n            if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS\n                || vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS\n                || vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to create synchronization objects for a frame!\");\n            }\n        }\n    }\n\n    void createSyncObjectsExt()\n    {\n        VkSemaphoreCreateInfo semaphoreInfo = {};\n        semaphoreInfo.sType                 = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n        memset(&semaphoreInfo, 0, sizeof(semaphoreInfo));\n        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n#ifdef _WIN64\n        WindowsSecurityAttributes winSecurityAttributes;\n\n        VkExportSemaphoreWin32HandleInfoKHR vulkanExportSemaphoreWin32HandleInfoKHR = {};\n        vulkanExportSemaphoreWin32HandleInfoKHR.sType       = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR;\n        vulkanExportSemaphoreWin32HandleInfoKHR.pNext       = NULL;\n        vulkanExportSemaphoreWin32HandleInfoKHR.pAttributes = &winSecurityAttributes;\n        vulkanExportSemaphoreWin32HandleInfoKHR.dwAccess    = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;\n        vulkanExportSemaphoreWin32HandleInfoKHR.name        = (LPCWSTR)NULL;\n#endif\n        VkExportSemaphoreCreateInfoKHR vulkanExportSemaphoreCreateInfo = {};\n        vulkanExportSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR;\n#ifdef _WIN64\n        vulkanExportSemaphoreCreateInfo.pNext = IsWindows8OrGreater() ? &vulkanExportSemaphoreWin32HandleInfoKHR : NULL;\n        vulkanExportSemaphoreCreateInfo.handleTypes = IsWindows8OrGreater()\n                                                        ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                        : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n#else\n        vulkanExportSemaphoreCreateInfo.pNext       = NULL;\n        vulkanExportSemaphoreCreateInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n#endif\n        semaphoreInfo.pNext = &vulkanExportSemaphoreCreateInfo;\n\n        if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &cudaUpdateVkSemaphore) != VK_SUCCESS\n            || vkCreateSemaphore(device, &semaphoreInfo, nullptr, &vkUpdateCudaSemaphore) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create synchronization objects for a CUDA-Vulkan!\");\n        }\n    }\n\n    void updateUniformBuffer()\n    {\n        UniformBufferObject ubo = {};\n\n        mat4x4_identity(ubo.model);\n        mat4x4 Model;\n        mat4x4_dup(Model, ubo.model);\n        mat4x4_rotate(ubo.model, Model, 0.0f, 0.0f, 1.0f, degreesToRadians(135.0f));\n\n        vec3 eye    = {2.0f, 2.0f, 2.0f};\n        vec3 center = {0.0f, 0.0f, 0.0f};\n        vec3 up     = {0.0f, 0.0f, 1.0f};\n        mat4x4_look_at(ubo.view, eye, center, up);\n\n        mat4x4_perspective(\n            ubo.proj, degreesToRadians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f);\n        ubo.proj[1][1] *= -1;\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            void *data;\n            vkMapMemory(device, uniformBuffersMemory[i], 0, sizeof(ubo), 0, &data);\n            memcpy(data, &ubo, sizeof(ubo));\n            vkUnmapMemory(device, uniformBuffersMemory[i]);\n        }\n    }\n\n    void drawFrame()\n    {\n        static int startSubmit = 0;\n\n        vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());\n\n        uint32_t imageIndex;\n        VkResult result = vkAcquireNextImageKHR(device,\n                                                swapChain,\n                                                std::numeric_limits<uint64_t>::max(),\n                                                imageAvailableSemaphores[currentFrame],\n                                                VK_NULL_HANDLE,\n                                                &imageIndex);\n\n        if (result == VK_ERROR_OUT_OF_DATE_KHR) {\n            recreateSwapChain();\n            return;\n        }\n        else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {\n            throw std::runtime_error(\"failed to acquire swap chain image!\");\n        }\n\n        vkResetFences(device, 1, &inFlightFences[currentFrame]);\n\n        if (!startSubmit) {\n            submitVulkan(imageIndex);\n            startSubmit = 1;\n        }\n        else {\n            submitVulkanCuda(imageIndex);\n        }\n\n        VkPresentInfoKHR presentInfo = {};\n        presentInfo.sType            = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};\n\n        presentInfo.waitSemaphoreCount = 1;\n        presentInfo.pWaitSemaphores    = signalSemaphores;\n\n        VkSwapchainKHR swapChains[] = {swapChain};\n        presentInfo.swapchainCount  = 1;\n        presentInfo.pSwapchains     = swapChains;\n        presentInfo.pImageIndices   = &imageIndex;\n        presentInfo.pResults        = nullptr; // Optional\n\n        result = vkQueuePresentKHR(presentQueue, &presentInfo);\n\n        if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {\n            framebufferResized = false;\n            recreateSwapChain();\n        }\n        else if (result != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to present swap chain image!\");\n        }\n\n        cudaUpdateVkImage();\n\n        currentFrame = (currentFrame + 1) % MAX_FRAMES;\n        // Added sleep of 10 millisecs so that CPU does not submit too much work to\n        // GPU\n        std::this_thread::sleep_for(std::chrono::microseconds(10000));\n        char title[256];\n        sprintf(title, \"Vulkan Image CUDA Box Filter (radius=%d)\", filter_radius);\n        glfwSetWindowTitle(window, title);\n    }\n\n    void cudaVkSemaphoreSignal(cudaExternalSemaphore_t &extSemaphore)\n    {\n        cudaExternalSemaphoreSignalParams extSemaphoreSignalParams;\n        memset(&extSemaphoreSignalParams, 0, sizeof(extSemaphoreSignalParams));\n\n        extSemaphoreSignalParams.params.fence.value = 0;\n        extSemaphoreSignalParams.flags              = 0;\n        checkCudaErrors(cudaSignalExternalSemaphoresAsync(&extSemaphore, &extSemaphoreSignalParams, 1, streamToRun));\n    }\n\n    void cudaVkSemaphoreWait(cudaExternalSemaphore_t &extSemaphore)\n    {\n        cudaExternalSemaphoreWaitParams extSemaphoreWaitParams;\n\n        memset(&extSemaphoreWaitParams, 0, sizeof(extSemaphoreWaitParams));\n\n        extSemaphoreWaitParams.params.fence.value = 0;\n        extSemaphoreWaitParams.flags              = 0;\n\n        checkCudaErrors(cudaWaitExternalSemaphoresAsync(&extSemaphore, &extSemaphoreWaitParams, 1, streamToRun));\n    }\n\n    void submitVulkan(uint32_t imageIndex)\n    {\n        VkSubmitInfo submitInfo = {};\n        submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\n        VkSemaphore          waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};\n        VkPipelineStageFlags waitStages[]     = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};\n        submitInfo.waitSemaphoreCount         = 1;\n        submitInfo.pWaitSemaphores            = waitSemaphores;\n        submitInfo.pWaitDstStageMask          = waitStages;\n        submitInfo.commandBufferCount         = 1;\n        submitInfo.pCommandBuffers            = &commandBuffers[imageIndex];\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame], vkUpdateCudaSemaphore};\n\n        submitInfo.signalSemaphoreCount = 2;\n        submitInfo.pSignalSemaphores    = signalSemaphores;\n\n        if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to submit draw command buffer!\");\n        }\n    }\n\n    void submitVulkanCuda(uint32_t imageIndex)\n    {\n        VkSubmitInfo submitInfo = {};\n        submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\n        VkSemaphore          waitSemaphores[] = {imageAvailableSemaphores[currentFrame], cudaUpdateVkSemaphore};\n        VkPipelineStageFlags waitStages[]     = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n                                                 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};\n        submitInfo.waitSemaphoreCount         = 2;\n        submitInfo.pWaitSemaphores            = waitSemaphores;\n        submitInfo.pWaitDstStageMask          = waitStages;\n        submitInfo.commandBufferCount         = 1;\n        submitInfo.pCommandBuffers            = &commandBuffers[imageIndex];\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame], vkUpdateCudaSemaphore};\n\n        submitInfo.signalSemaphoreCount = 2;\n        submitInfo.pSignalSemaphores    = signalSemaphores;\n\n        if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to submit draw command buffer!\");\n        }\n    }\n\n    VkShaderModule createShaderModule(const std::vector<char> &code)\n    {\n        VkShaderModuleCreateInfo createInfo = {};\n        createInfo.sType                    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n        createInfo.codeSize                 = code.size();\n        createInfo.pCode                    = reinterpret_cast<const uint32_t *>(code.data());\n\n        VkShaderModule shaderModule;\n        if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create shader module!\");\n        }\n\n        return shaderModule;\n    }\n\n    VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats)\n    {\n        if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {\n            return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};\n        }\n\n        for (const auto &availableFormat : availableFormats) {\n            if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM\n                && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {\n                return availableFormat;\n            }\n        }\n\n        return availableFormats[0];\n    }\n\n    VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes)\n    {\n        VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;\n\n        for (const auto &availablePresentMode : availablePresentModes) {\n            if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {\n                return availablePresentMode;\n            }\n            else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {\n                bestMode = availablePresentMode;\n            }\n        }\n\n        return bestMode;\n    }\n\n    VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities)\n    {\n        if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {\n            return capabilities.currentExtent;\n        }\n        else {\n            int width, height;\n            glfwGetFramebufferSize(window, &width, &height);\n\n            VkExtent2D actualExtent = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};\n\n            actualExtent.width  = std::max(capabilities.minImageExtent.width,\n                                          std::min(capabilities.maxImageExtent.width, actualExtent.width));\n            actualExtent.height = std::max(capabilities.minImageExtent.height,\n                                           std::min(capabilities.maxImageExtent.height, actualExtent.height));\n\n            return actualExtent;\n        }\n    }\n\n    SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device)\n    {\n        SwapChainSupportDetails details;\n\n        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);\n\n        uint32_t formatCount;\n        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);\n\n        if (formatCount != 0) {\n            details.formats.resize(formatCount);\n            vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());\n        }\n\n        uint32_t presentModeCount;\n        vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);\n\n        if (presentModeCount != 0) {\n            details.presentModes.resize(presentModeCount);\n            vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());\n        }\n\n        return details;\n    }\n\n    bool isDeviceSuitable(VkPhysicalDevice device)\n    {\n        QueueFamilyIndices indices = findQueueFamilies(device);\n\n        bool extensionsSupported = checkDeviceExtensionSupport(device);\n\n        bool swapChainAdequate = false;\n        if (extensionsSupported) {\n            SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);\n            swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();\n        }\n\n        VkPhysicalDeviceFeatures supportedFeatures;\n        vkGetPhysicalDeviceFeatures(device, &supportedFeatures);\n\n        return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;\n    }\n\n    bool checkDeviceExtensionSupport(VkPhysicalDevice device)\n    {\n        uint32_t extensionCount;\n        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);\n\n        std::vector<VkExtensionProperties> availableExtensions(extensionCount);\n        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());\n\n        std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());\n\n        for (const auto &extension : availableExtensions) {\n            requiredExtensions.erase(extension.extensionName);\n        }\n\n        return requiredExtensions.empty();\n    }\n\n    QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device)\n    {\n        QueueFamilyIndices indices;\n\n        uint32_t queueFamilyCount = 0;\n        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);\n\n        std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);\n        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());\n\n        int i = 0;\n        for (const auto &queueFamily : queueFamilies) {\n            if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {\n                indices.graphicsFamily = i;\n            }\n\n            VkBool32 presentSupport = false;\n            vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);\n\n            if (queueFamily.queueCount > 0 && presentSupport) {\n                indices.presentFamily = i;\n            }\n\n            if (indices.isComplete()) {\n                break;\n            }\n\n            i++;\n        }\n\n        return indices;\n    }\n\n    std::vector<const char *> getRequiredExtensions()\n    {\n        uint32_t     glfwExtensionCount = 0;\n        const char **glfwExtensions;\n        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);\n\n        std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);\n\n        if (enableValidationLayers) {\n            extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);\n        }\n\n        return extensions;\n    }\n\n    bool checkValidationLayerSupport()\n    {\n        uint32_t layerCount;\n        vkEnumerateInstanceLayerProperties(&layerCount, nullptr);\n\n        std::vector<VkLayerProperties> availableLayers(layerCount);\n        vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());\n\n        for (const char *layerName : validationLayers) {\n            bool layerFound = false;\n\n            for (const auto &layerProperties : availableLayers) {\n                if (strcmp(layerName, layerProperties.layerName) == 0) {\n                    layerFound = true;\n                    break;\n                }\n            }\n\n            if (!layerFound) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    static std::vector<char> readFile(const std::string &filename)\n    {\n        char         *file_path = sdkFindFilePath(filename.c_str(), execution_path.c_str());\n        std::ifstream file(file_path, std::ios::ate | std::ios::binary);\n\n        if (!file.is_open()) {\n            throw std::runtime_error(\"failed to open file!\");\n        }\n\n        size_t            fileSize = (size_t)file.tellg();\n        std::vector<char> buffer(fileSize);\n\n        file.seekg(0);\n        file.read(buffer.data(), fileSize);\n\n        file.close();\n\n        return buffer;\n    }\n\n    static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,\n                                                        VkDebugUtilsMessageTypeFlagsEXT             messageType,\n                                                        const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,\n                                                        void                                       *pUserData)\n    {\n        std::cerr << \"validation layer: \" << pCallbackData->pMessage << std::endl;\n\n        return VK_FALSE;\n    }\n};\n\nint main(int argc, char **argv)\n{\n    execution_path             = argv[0];\n    std::string image_filename = \"teapot1024.ppm\";\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n        getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&image_filename);\n    }\n\n    vulkanImageCUDA app;\n\n    try {\n        // This app only works on ppm images\n        app.loadImageData(image_filename);\n        app.run();\n    }\n    catch (const std::exception &e) {\n        std::cerr << e.what() << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#define GLFW_INCLUDE_VULKAN\n#ifdef _WIN64\n// Add windows.h to the include path firstly as dependency for other Windows headers\n#include <windows.h>\n// Add other Windows headers\n#include <VersionHelpers.h>\n#include <aclapi.h>\n#include <dxgi1_2.h>\n#define _USE_MATH_DEFINES\n#endif\n\n#include <GLFW/glfw3.h>\n#include <vulkan/vulkan.h>\n#ifdef _WIN64\n#include <vulkan/vulkan_win32.h>\n#endif\n\n#include <algorithm>\n#include <array>\n#include <chrono>\n#include <cstdlib>\n#include <cstring>\n#include <hip/hip_runtime.h>\n#include <fstream>\n#include <hip/hip_runtime_api.h>\n#include <helper_image.h>\n#include <hip/hip_vector_types.h>\n#include <iostream>\n#include <set>\n#include <stdexcept>\n#include <thread>\n#include <vector>\n\n#include \"linmath.h\"\n\n#define WIDTH  800\n#define HEIGHT 600\n\nconst int MAX_FRAMES = 4;\n\nconst std::vector<const char *> validationLayers = {\"VK_LAYER_KHRONOS_validation\"};\n\n#ifdef NDEBUG\nconst bool enableValidationLayers = false;\n#else\nconst bool enableValidationLayers = true;\n#endif\n\nstd::string execution_path;\n\nVkResult CreateDebugUtilsMessengerEXT(VkInstance                                instance,\n                                      const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,\n                                      const VkAllocationCallbacks              *pAllocator,\n                                      VkDebugUtilsMessengerEXT                 *pDebugMessenger)\n{\n    auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, \"vkCreateDebugUtilsMessengerEXT\");\n    if (func != nullptr) {\n        return func(instance, pCreateInfo, pAllocator, pDebugMessenger);\n    }\n    else {\n        return VK_ERROR_EXTENSION_NOT_PRESENT;\n    }\n};\n\nconst std::vector<const char *> deviceExtensions = {\n    VK_KHR_SWAPCHAIN_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,\n#ifdef _WIN64\n    VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,\n#else\n    VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,\n    VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,\n#endif\n};\n\n#ifdef _WIN64\nclass WindowsSecurityAttributes\n{\nprotected:\n    SECURITY_ATTRIBUTES  m_winSecurityAttributes;\n    PSECURITY_DESCRIPTOR m_winPSecurityDescriptor;\n\npublic:\n    WindowsSecurityAttributes();\n    SECURITY_ATTRIBUTES *operator&();\n    ~WindowsSecurityAttributes();\n};\n\nWindowsSecurityAttributes::WindowsSecurityAttributes()\n{\n    m_winPSecurityDescriptor = (PSECURITY_DESCRIPTOR)calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH + 2 * sizeof(void **));\n\n    PSID *ppSID = (PSID *)((PBYTE)m_winPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);\n    PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *));\n\n    InitializeSecurityDescriptor(m_winPSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);\n\n    SID_IDENTIFIER_AUTHORITY sidIdentifierAuthority = SECURITY_WORLD_SID_AUTHORITY;\n    AllocateAndInitializeSid(&sidIdentifierAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, ppSID);\n\n    EXPLICIT_ACCESS explicitAccess;\n    ZeroMemory(&explicitAccess, sizeof(EXPLICIT_ACCESS));\n    explicitAccess.grfAccessPermissions = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;\n    explicitAccess.grfAccessMode        = SET_ACCESS;\n    explicitAccess.grfInheritance       = INHERIT_ONLY;\n    explicitAccess.Trustee.TrusteeForm  = TRUSTEE_IS_SID;\n    explicitAccess.Trustee.TrusteeType  = TRUSTEE_IS_WELL_KNOWN_GROUP;\n    explicitAccess.Trustee.ptstrName    = (LPTSTR)*ppSID;\n\n    SetEntriesInAcl(1, &explicitAccess, NULL, ppACL);\n\n    SetSecurityDescriptorDacl(m_winPSecurityDescriptor, TRUE, *ppACL, FALSE);\n\n    m_winSecurityAttributes.nLength              = sizeof(m_winSecurityAttributes);\n    m_winSecurityAttributes.lpSecurityDescriptor = m_winPSecurityDescriptor;\n    m_winSecurityAttributes.bInheritHandle       = TRUE;\n}\n\nSECURITY_ATTRIBUTES *WindowsSecurityAttributes::operator&() { return &m_winSecurityAttributes; }\n\nWindowsSecurityAttributes::~WindowsSecurityAttributes()\n{\n    PSID *ppSID = (PSID *)((PBYTE)m_winPSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);\n    PACL *ppACL = (PACL *)((PBYTE)ppSID + sizeof(PSID *));\n\n    if (*ppSID) {\n        FreeSid(*ppSID);\n    }\n    if (*ppACL) {\n        LocalFree(*ppACL);\n    }\n    free(m_winPSecurityDescriptor);\n}\n#endif\n\nvoid DestroyDebugUtilsMessengerEXT(VkInstance                   instance,\n                                   VkDebugUtilsMessengerEXT     debugMessenger,\n                                   const VkAllocationCallbacks *pAllocator)\n{\n    auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, \"vkDestroyDebugUtilsMessengerEXT\");\n    if (func != nullptr) {\n        func(instance, debugMessenger, pAllocator);\n    }\n}\n\nstruct QueueFamilyIndices\n{\n    int graphicsFamily = -1;\n    int presentFamily  = -1;\n\n    bool isComplete() { return graphicsFamily >= 0 && presentFamily >= 0; }\n};\n\nstruct SwapChainSupportDetails\n{\n    VkSurfaceCapabilitiesKHR        capabilities;\n    std::vector<VkSurfaceFormatKHR> formats;\n    std::vector<VkPresentModeKHR>   presentModes;\n};\n\ntypedef float vec2[2];\n\nstruct Vertex\n{\n    vec4 pos;\n    vec3 color;\n    vec2 texCoord;\n\n    static VkVertexInputBindingDescription getBindingDescription()\n    {\n        VkVertexInputBindingDescription bindingDescription = {};\n        bindingDescription.binding                         = 0;\n        bindingDescription.stride                          = sizeof(Vertex);\n        bindingDescription.inputRate                       = VK_VERTEX_INPUT_RATE_VERTEX;\n\n        return bindingDescription;\n    }\n\n    static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions()\n    {\n        std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {};\n\n        attributeDescriptions[0].binding  = 0;\n        attributeDescriptions[0].location = 0;\n        attributeDescriptions[0].format   = VK_FORMAT_R32G32B32A32_SFLOAT;\n        attributeDescriptions[0].offset   = offsetof(Vertex, pos);\n\n        attributeDescriptions[1].binding  = 0;\n        attributeDescriptions[1].location = 1;\n        attributeDescriptions[1].format   = VK_FORMAT_R32G32B32_SFLOAT;\n        attributeDescriptions[1].offset   = offsetof(Vertex, color);\n\n        attributeDescriptions[2].binding  = 0;\n        attributeDescriptions[2].location = 2;\n        attributeDescriptions[2].format   = VK_FORMAT_R32G32_SFLOAT;\n        attributeDescriptions[2].offset   = offsetof(Vertex, texCoord);\n\n        return attributeDescriptions;\n    }\n};\n\nstruct UniformBufferObject\n{\n    alignas(16) mat4x4 model;\n    alignas(16) mat4x4 view;\n    alignas(16) mat4x4 proj;\n};\n\nconst std::vector<Vertex> vertices = {{{-1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},\n                                      {{1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},\n                                      {{1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},\n                                      {{-1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};\n\nconst std::vector<uint16_t> indices = {0, 1, 2, 2, 3, 0};\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n__device__ float4 rgbaIntToFloat(unsigned int c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\nint filter_radius = 14;\nint g_nFilterSign = 1;\n\n// This varies the filter radius, so we can see automatic animation\nvoid varySigma()\n{\n    filter_radius += g_nFilterSign;\n\n    if (filter_radius > 64) {\n        filter_radius = 64; // clamp to 64 and then negate sign\n        g_nFilterSign = -1;\n    }\n    else if (filter_radius < 0) {\n        filter_radius = 0;\n        g_nFilterSign = 1;\n    }\n}\n\n// row pass using texture lookups\n__global__ void d_boxfilter_rgba_x(hipSurfaceObject_t *dstSurfMipMapArray,\n                                   hipTextureObject_t  textureMipMapInput,\n                                   size_t               baseWidth,\n                                   size_t               baseHeight,\n                                   size_t               mipLevels,\n                                   int                  filter_radius)\n{\n    float        scale = 1.0f / (float)((filter_radius << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (y < baseHeight) {\n        for (uint32_t mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n            uint32_t width  = (baseWidth >> mipLevelIdx) ? (baseWidth >> mipLevelIdx) : 1;\n            uint32_t height = (baseHeight >> mipLevelIdx) ? (baseHeight >> mipLevelIdx) : 1;\n            if (y < height && filter_radius < width) {\n                float  px = 1.0 / width;\n                float  py = 1.0 / height;\n                float4 t  = make_float4(0.0f);\n                for (int x = -filter_radius; x <= filter_radius; x++) {\n                    t += tex2DLod<float4>(textureMipMapInput, x * px, y * py, (float)mipLevelIdx);\n                }\n\n                unsigned int dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], 0, y);\n\n                for (int x = 1; x < width; x++) {\n                    t += tex2DLod<float4>(textureMipMapInput, (x + filter_radius) * px, y * py, (float)mipLevelIdx);\n                    t -= tex2DLod<float4>(textureMipMapInput, (x - filter_radius - 1) * px, y * py, (float)mipLevelIdx);\n                    unsigned int dataB = rgbaFloatToInt(t * scale);\n                    surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], x * sizeof(uchar4), y);\n                }\n            }\n        }\n    }\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_boxfilter_rgba_y(hipSurfaceObject_t *dstSurfMipMapArray,\n                                   hipSurfaceObject_t *srcSurfMipMapArray,\n                                   size_t               baseWidth,\n                                   size_t               baseHeight,\n                                   size_t               mipLevels,\n                                   int                  filter_radius)\n{\n    unsigned int x     = blockIdx.x * blockDim.x + threadIdx.x;\n    float        scale = 1.0f / (float)((filter_radius << 1) + 1);\n\n    for (uint32_t mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n        uint32_t width  = (baseWidth >> mipLevelIdx) ? (baseWidth >> mipLevelIdx) : 1;\n        uint32_t height = (baseHeight >> mipLevelIdx) ? (baseHeight >> mipLevelIdx) : 1;\n\n        if (x < width && height > filter_radius) {\n            float4 t;\n            // do left edge\n            int          colInBytes = x * sizeof(uchar4);\n            unsigned int pixFirst   = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, 0);\n            t                       = rgbaIntToFloat(pixFirst) * filter_radius;\n\n            for (int y = 0; (y < (filter_radius + 1)) && (y < height); y++) {\n                unsigned int pix = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y);\n                t += rgbaIntToFloat(pix);\n            }\n\n            unsigned int dataB = rgbaFloatToInt(t * scale);\n            surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, 0);\n\n            for (int y = 1; (y < filter_radius + 1) && ((y + filter_radius) < height); y++) {\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y + filter_radius);\n                t += rgbaIntToFloat(pix);\n                t -= rgbaIntToFloat(pixFirst);\n\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n\n            // main loop\n            for (int y = (filter_radius + 1); y < (height - filter_radius); y++) {\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y + filter_radius);\n                t += rgbaIntToFloat(pix);\n\n                pix = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y - filter_radius - 1);\n                t -= rgbaIntToFloat(pix);\n\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n\n            // do right edge\n            unsigned int pixLast = surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, height - 1);\n            for (int y = height - filter_radius; (y < height) && ((y - filter_radius - 1) > 1); y++) {\n                t += rgbaIntToFloat(pixLast);\n                unsigned int pix =\n                    surf2Dread<unsigned int>(srcSurfMipMapArray[mipLevelIdx], colInBytes, y - filter_radius - 1);\n                t -= rgbaIntToFloat(pix);\n                dataB = rgbaFloatToInt(t * scale);\n                surf2Dwrite(dataB, dstSurfMipMapArray[mipLevelIdx], colInBytes, y);\n            }\n        }\n    }\n}\n\nclass vulkanImageCUDA\n{\npublic:\n    void loadImageData(const std::string &filename)\n    {\n        // load image (needed so we can get the width and height before we create\n        // the window\n        char *image_path = sdkFindFilePath(filename.c_str(), execution_path.c_str());\n\n        if (image_path == 0) {\n            printf(\"Error finding image file '%s'\\n\", filename.c_str());\n            exit(EXIT_FAILURE);\n        }\n\n        sdkLoadPPM4(image_path, (unsigned char **)&image_data, &imageWidth, &imageHeight);\n\n        if (!image_data) {\n            printf(\"Error opening file '%s'\\n\", image_path);\n            exit(EXIT_FAILURE);\n        }\n\n        printf(\"Loaded '%s', %d x %d pixels\\n\", image_path, imageWidth, imageHeight);\n    }\n\n    void run()\n    {\n        initWindow();\n        initVulkan();\n        initCuda();\n        mainLoop();\n        cleanup();\n    }\n\nprivate:\n    GLFWwindow *window;\n\n    VkInstance               instance;\n    VkDebugUtilsMessengerEXT debugMessenger;\n    VkSurfaceKHR             surface;\n\n    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;\n    VkDevice         device;\n    uint8_t          vkDeviceUUID[VK_UUID_SIZE];\n\n    VkQueue graphicsQueue;\n    VkQueue presentQueue;\n\n    VkSwapchainKHR             swapChain;\n    std::vector<VkImage>       swapChainImages;\n    VkFormat                   swapChainImageFormat;\n    VkExtent2D                 swapChainExtent;\n    std::vector<VkImageView>   swapChainImageViews;\n    std::vector<VkFramebuffer> swapChainFramebuffers;\n\n    VkRenderPass          renderPass;\n    VkDescriptorSetLayout descriptorSetLayout;\n    VkPipelineLayout      pipelineLayout;\n    VkPipeline            graphicsPipeline;\n\n    VkCommandPool commandPool;\n\n    VkImage        textureImage;\n    VkDeviceMemory textureImageMemory;\n    VkImageView    textureImageView;\n    VkSampler      textureSampler;\n\n    VkBuffer       vertexBuffer;\n    VkDeviceMemory vertexBufferMemory;\n    VkBuffer       indexBuffer;\n    VkDeviceMemory indexBufferMemory;\n\n    std::vector<VkBuffer>       uniformBuffers;\n    std::vector<VkDeviceMemory> uniformBuffersMemory;\n\n    VkDescriptorPool             descriptorPool;\n    std::vector<VkDescriptorSet> descriptorSets;\n\n    std::vector<VkCommandBuffer> commandBuffers;\n\n    std::vector<VkSemaphore> imageAvailableSemaphores;\n    std::vector<VkSemaphore> renderFinishedSemaphores;\n    VkSemaphore              cudaUpdateVkSemaphore, vkUpdateCudaSemaphore;\n    std::vector<VkFence>     inFlightFences;\n\n    size_t currentFrame = 0;\n\n    bool framebufferResized = false;\n\n#ifdef _WIN64\n    PFN_vkGetMemoryWin32HandleKHR    fpGetMemoryWin32HandleKHR;\n    PFN_vkGetSemaphoreWin32HandleKHR fpGetSemaphoreWin32HandleKHR;\n#else\n    PFN_vkGetMemoryFdKHR    fpGetMemoryFdKHR    = NULL;\n    PFN_vkGetSemaphoreFdKHR fpGetSemaphoreFdKHR = NULL;\n#endif\n\n    PFN_vkGetPhysicalDeviceProperties2 fpGetPhysicalDeviceProperties2;\n\n    unsigned int *image_data = NULL;\n    unsigned int  imageWidth, imageHeight;\n    unsigned int  mipLevels = 1;\n    size_t        totalImageMemSize;\n\n    // CUDA objects\n    hipExternalMemory_t             cudaExtMemImageBuffer;\n    hipMipmappedArray_t             cudaMipmappedImageArray, cudaMipmappedImageArrayTemp, cudaMipmappedImageArrayOrig;\n    std::vector<hipSurfaceObject_t> surfaceObjectList, surfaceObjectListTemp;\n    hipSurfaceObject_t             *d_surfaceObjectList, *d_surfaceObjectListTemp;\n    hipTextureObject_t              textureObjMipMapInput;\n\n    hipExternalSemaphore_t cudaExtCudaUpdateVkSemaphore;\n    hipExternalSemaphore_t cudaExtVkUpdateCudaSemaphore;\n    hipStream_t            streamToRun;\n\n    void initWindow()\n    {\n        glfwInit();\n\n        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);\n\n        window = glfwCreateWindow(WIDTH, HEIGHT, \"Vulkan Image CUDA Box Filter\", nullptr, nullptr);\n        glfwSetWindowUserPointer(window, this);\n        glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);\n    }\n\n    static void framebufferResizeCallback(GLFWwindow *window, int width, int height)\n    {\n        auto app                = reinterpret_cast<vulkanImageCUDA *>(glfwGetWindowUserPointer(window));\n        app->framebufferResized = true;\n    }\n\n    void initVulkan()\n    {\n        createInstance();\n        setupDebugMessenger();\n        createSurface();\n        pickPhysicalDevice();\n        createLogicalDevice();\n        getKhrExtensionsFn();\n        createSwapChain();\n        createImageViews();\n        createRenderPass();\n        createDescriptorSetLayout();\n        createGraphicsPipeline();\n        createFramebuffers();\n        createCommandPool();\n        createTextureImage();\n        createTextureImageView();\n        createTextureSampler();\n        createVertexBuffer();\n        createIndexBuffer();\n        createUniformBuffers();\n        createDescriptorPool();\n        createDescriptorSets();\n        createCommandBuffers();\n        createSyncObjects();\n        createSyncObjectsExt();\n    }\n\n    void initCuda()\n    {\n        setCudaVkDevice();\n        checkCudaErrors(hipStreamCreate(&streamToRun));\n        cudaVkImportImageMem();\n        cudaVkImportSemaphore();\n    }\n\n    void mainLoop()\n    {\n        updateUniformBuffer();\n        while (!glfwWindowShouldClose(window)) {\n            glfwPollEvents();\n            drawFrame();\n        }\n\n        vkDeviceWaitIdle(device);\n    }\n\n    void cleanupSwapChain()\n    {\n        for (auto framebuffer : swapChainFramebuffers) {\n            vkDestroyFramebuffer(device, framebuffer, nullptr);\n        }\n\n        vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());\n\n        vkDestroyPipeline(device, graphicsPipeline, nullptr);\n        vkDestroyPipelineLayout(device, pipelineLayout, nullptr);\n        vkDestroyRenderPass(device, renderPass, nullptr);\n\n        for (auto imageView : swapChainImageViews) {\n            vkDestroyImageView(device, imageView, nullptr);\n        }\n\n        vkDestroySwapchainKHR(device, swapChain, nullptr);\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            vkDestroyBuffer(device, uniformBuffers[i], nullptr);\n            vkFreeMemory(device, uniformBuffersMemory[i], nullptr);\n        }\n\n        vkDestroyDescriptorPool(device, descriptorPool, nullptr);\n    }\n\n    void cleanup()\n    {\n        cleanupSwapChain();\n\n        vkDestroySampler(device, textureSampler, nullptr);\n        vkDestroyImageView(device, textureImageView, nullptr);\n\n        for (int i = 0; i < mipLevels; i++) {\n            checkCudaErrors(hipDestroySurfaceObject(surfaceObjectList[i]));\n            checkCudaErrors(hipDestroySurfaceObject(surfaceObjectListTemp[i]));\n        }\n\n        checkCudaErrors(hipFree(d_surfaceObjectList));\n        checkCudaErrors(hipFree(d_surfaceObjectListTemp));\n        checkCudaErrors(hipFreeMipmappedArray(cudaMipmappedImageArrayTemp));\n        checkCudaErrors(hipFreeMipmappedArray(cudaMipmappedImageArrayOrig));\n        checkCudaErrors(hipFreeMipmappedArray(cudaMipmappedImageArray));\n        checkCudaErrors(hipDestroyTextureObject(textureObjMipMapInput));\n        checkCudaErrors(hipDestroyExternalMemory(cudaExtMemImageBuffer));\n        checkCudaErrors(hipDestroyExternalSemaphore(cudaExtCudaUpdateVkSemaphore));\n        checkCudaErrors(hipDestroyExternalSemaphore(cudaExtVkUpdateCudaSemaphore));\n\n        vkDestroyImage(device, textureImage, nullptr);\n        vkFreeMemory(device, textureImageMemory, nullptr);\n\n        vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);\n\n        vkDestroyBuffer(device, indexBuffer, nullptr);\n        vkFreeMemory(device, indexBufferMemory, nullptr);\n\n        vkDestroyBuffer(device, vertexBuffer, nullptr);\n        vkFreeMemory(device, vertexBufferMemory, nullptr);\n\n        vkDestroySemaphore(device, cudaUpdateVkSemaphore, nullptr);\n        vkDestroySemaphore(device, vkUpdateCudaSemaphore, nullptr);\n\n        for (size_t i = 0; i < MAX_FRAMES; i++) {\n            vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);\n            vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);\n            vkDestroyFence(device, inFlightFences[i], nullptr);\n        }\n\n        vkDestroyCommandPool(device, commandPool, nullptr);\n\n        vkDestroyDevice(device, nullptr);\n\n        if (enableValidationLayers) {\n            DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);\n        }\n\n        vkDestroySurfaceKHR(instance, surface, nullptr);\n        vkDestroyInstance(instance, nullptr);\n\n        glfwDestroyWindow(window);\n\n        glfwTerminate();\n    }\n\n    void recreateSwapChain()\n    {\n        int width = 0, height = 0;\n        while (width == 0 || height == 0) {\n            glfwGetFramebufferSize(window, &width, &height);\n            glfwWaitEvents();\n        }\n\n        vkDeviceWaitIdle(device);\n\n        cleanupSwapChain();\n\n        createSwapChain();\n        createImageViews();\n        createRenderPass();\n        createGraphicsPipeline();\n        createFramebuffers();\n        createUniformBuffers();\n        createDescriptorPool();\n        createDescriptorSets();\n        createCommandBuffers();\n    }\n\n    void createInstance()\n    {\n        if (enableValidationLayers && !checkValidationLayerSupport()) {\n            throw std::runtime_error(\"validation layers requested, but not available!\");\n        }\n\n        VkApplicationInfo appInfo  = {};\n        appInfo.sType              = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n        appInfo.pApplicationName   = \"Vulkan Image CUDA Interop\";\n        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);\n        appInfo.pEngineName        = \"No Engine\";\n        appInfo.engineVersion      = VK_MAKE_VERSION(1, 0, 0);\n        appInfo.apiVersion         = VK_API_VERSION_1_1;\n\n        VkInstanceCreateInfo createInfo = {};\n        createInfo.sType                = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n        createInfo.pApplicationInfo     = &appInfo;\n\n        auto extensions                    = getRequiredExtensions();\n        createInfo.enabledExtensionCount   = static_cast<uint32_t>(extensions.size());\n        createInfo.ppEnabledExtensionNames = extensions.data();\n\n        VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;\n        if (enableValidationLayers) {\n            createInfo.enabledLayerCount   = static_cast<uint32_t>(validationLayers.size());\n            createInfo.ppEnabledLayerNames = validationLayers.data();\n\n            populateDebugMessengerCreateInfo(debugCreateInfo);\n            createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo;\n        }\n        else {\n            createInfo.enabledLayerCount = 0;\n\n            createInfo.pNext = nullptr;\n        }\n\n        if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create instance!\");\n        }\n\n        fpGetPhysicalDeviceProperties2 =\n            (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(instance, \"vkGetPhysicalDeviceProperties2\");\n        if (fpGetPhysicalDeviceProperties2 == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetPhysicalDeviceProperties2KHR\\\" not \"\n                                     \"found.\\n\");\n        }\n\n#ifdef _WIN64\n        fpGetMemoryWin32HandleKHR =\n            (PFN_vkGetMemoryWin32HandleKHR)vkGetInstanceProcAddr(instance, \"vkGetMemoryWin32HandleKHR\");\n        if (fpGetMemoryWin32HandleKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetMemoryWin32HandleKHR\\\" not \"\n                                     \"found.\\n\");\n        }\n#else\n        fpGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)vkGetInstanceProcAddr(instance, \"vkGetMemoryFdKHR\");\n        if (fpGetMemoryFdKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetMemoryFdKHR\\\" not found.\\n\");\n        }\n        else {\n            std::cout << \"Vulkan proc address for vkGetMemoryFdKHR - \" << fpGetMemoryFdKHR << std::endl;\n        }\n#endif\n    }\n\n    void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo)\n    {\n        createInfo                 = {};\n        createInfo.sType           = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;\n        createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT\n                                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT\n                                   | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;\n        createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT\n                               | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT\n                               | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;\n        createInfo.pfnUserCallback = debugCallback;\n    }\n\n    void setupDebugMessenger()\n    {\n        if (!enableValidationLayers)\n            return;\n\n        VkDebugUtilsMessengerCreateInfoEXT createInfo;\n        populateDebugMessengerCreateInfo(createInfo);\n\n        if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to set up debug messenger!\");\n        }\n    }\n\n    void createSurface()\n    {\n        if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create window surface!\");\n        }\n    }\n\n    void pickPhysicalDevice()\n    {\n        uint32_t deviceCount = 0;\n        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);\n\n        if (deviceCount == 0) {\n            throw std::runtime_error(\"failed to find GPUs with Vulkan support!\");\n        }\n\n        std::vector<VkPhysicalDevice> devices(deviceCount);\n        vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());\n\n        for (const auto &device : devices) {\n            if (isDeviceSuitable(device)) {\n                physicalDevice = device;\n                break;\n            }\n        }\n\n        if (physicalDevice == VK_NULL_HANDLE) {\n            throw std::runtime_error(\"failed to find a suitable GPU!\");\n        }\n\n        std::cout << \"Selected physical device = \" << physicalDevice << std::endl;\n\n        VkPhysicalDeviceIDProperties vkPhysicalDeviceIDProperties = {};\n        vkPhysicalDeviceIDProperties.sType                        = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;\n        vkPhysicalDeviceIDProperties.pNext                        = NULL;\n\n        VkPhysicalDeviceProperties2 vkPhysicalDeviceProperties2 = {};\n        vkPhysicalDeviceProperties2.sType                       = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;\n        vkPhysicalDeviceProperties2.pNext                       = &vkPhysicalDeviceIDProperties;\n\n        fpGetPhysicalDeviceProperties2(physicalDevice, &vkPhysicalDeviceProperties2);\n\n        memcpy(vkDeviceUUID, vkPhysicalDeviceIDProperties.deviceUUID, sizeof(vkDeviceUUID));\n    }\n\n    void getKhrExtensionsFn()\n    {\n#ifdef _WIN64\n\n        fpGetSemaphoreWin32HandleKHR =\n            (PFN_vkGetSemaphoreWin32HandleKHR)vkGetDeviceProcAddr(device, \"vkGetSemaphoreWin32HandleKHR\");\n        if (fpGetSemaphoreWin32HandleKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetSemaphoreWin32HandleKHR\\\" not \"\n                                     \"found.\\n\");\n        }\n#else\n        fpGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR)vkGetDeviceProcAddr(device, \"vkGetSemaphoreFdKHR\");\n        if (fpGetSemaphoreFdKHR == NULL) {\n            throw std::runtime_error(\"Vulkan: Proc address for \\\"vkGetSemaphoreFdKHR\\\" not found.\\n\");\n        }\n#endif\n    }\n\n    int setCudaVkDevice()\n    {\n        int current_device     = 0;\n        int device_count       = 0;\n        int devices_prohibited = 0;\n\n        hipDeviceProp_t deviceProp;\n        int            computeMode;\n        checkCudaErrors(hipGetDeviceCount(&device_count));\n\n        if (device_count == 0) {\n            fprintf(stderr, \"CUDA error: no devices supporting CUDA.\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        // Find the GPU which is selected by Vulkan\n        while (current_device < device_count) {\n            hipGetDeviceProperties(&deviceProp, current_device);\n            checkCudaErrors(hipDeviceGetAttribute(&computeMode, hipDeviceAttributeComputeMode, current_device));\n            if ((computeMode != hipComputeModeProhibited)) {\n                // Compare the cuda device UUID with vulkan UUID\n                int ret = memcmp(&deviceProp.uuid, &vkDeviceUUID, VK_UUID_SIZE);\n                if (ret == 0) {\n                    checkCudaErrors(hipSetDevice(current_device));\n                    checkCudaErrors(hipGetDeviceProperties(&deviceProp, current_device));\n                    printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\\n\",\n                           current_device,\n                           deviceProp.name,\n                           deviceProp.major,\n                           deviceProp.minor);\n\n                    return current_device;\n                }\n            }\n            else {\n                devices_prohibited++;\n            }\n\n            current_device++;\n        }\n\n        if (devices_prohibited == device_count) {\n            fprintf(stderr,\n                    \"CUDA error:\"\n                    \" No Vulkan-CUDA Interop capable GPU found.\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        return -1;\n    }\n\n    void createLogicalDevice()\n    {\n        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);\n\n        std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;\n        std::set<int>                        uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};\n\n        float queuePriority = 1.0f;\n        for (int queueFamily : uniqueQueueFamilies) {\n            VkDeviceQueueCreateInfo queueCreateInfo = {};\n            queueCreateInfo.sType                   = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n            queueCreateInfo.queueFamilyIndex        = queueFamily;\n            queueCreateInfo.queueCount              = 1;\n            queueCreateInfo.pQueuePriorities        = &queuePriority;\n            queueCreateInfos.push_back(queueCreateInfo);\n        }\n\n        VkPhysicalDeviceFeatures deviceFeatures = {};\n        deviceFeatures.samplerAnisotropy        = VK_TRUE;\n\n        VkDeviceCreateInfo createInfo = {};\n        createInfo.sType              = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;\n\n        createInfo.pQueueCreateInfos    = queueCreateInfos.data();\n        createInfo.queueCreateInfoCount = queueCreateInfos.size();\n\n        createInfo.pEnabledFeatures = &deviceFeatures;\n        std::vector<const char *> enabledExtensionNameList;\n\n        for (int i = 0; i < deviceExtensions.size(); i++) {\n            enabledExtensionNameList.push_back(deviceExtensions[i]);\n        }\n        if (enableValidationLayers) {\n            createInfo.enabledLayerCount   = static_cast<uint32_t>(validationLayers.size());\n            createInfo.ppEnabledLayerNames = validationLayers.data();\n        }\n        else {\n            createInfo.enabledLayerCount = 0;\n        }\n        createInfo.enabledExtensionCount   = static_cast<uint32_t>(enabledExtensionNameList.size());\n        createInfo.ppEnabledExtensionNames = enabledExtensionNameList.data();\n\n        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create logical device!\");\n        }\n        vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);\n        vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);\n    }\n\n    void createSwapChain()\n    {\n        SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);\n\n        VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);\n        VkPresentModeKHR   presentMode   = chooseSwapPresentMode(swapChainSupport.presentModes);\n        VkExtent2D         extent        = chooseSwapExtent(swapChainSupport.capabilities);\n\n        uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;\n        if (swapChainSupport.capabilities.maxImageCount > 0\n            && imageCount > swapChainSupport.capabilities.maxImageCount) {\n            imageCount = swapChainSupport.capabilities.maxImageCount;\n        }\n\n        VkSwapchainCreateInfoKHR createInfo = {};\n        createInfo.sType                    = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;\n        createInfo.surface                  = surface;\n\n        createInfo.minImageCount    = imageCount;\n        createInfo.imageFormat      = surfaceFormat.format;\n        createInfo.imageColorSpace  = surfaceFormat.colorSpace;\n        createInfo.imageExtent      = extent;\n        createInfo.imageArrayLayers = 1;\n        createInfo.imageUsage       = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n        QueueFamilyIndices indices              = findQueueFamilies(physicalDevice);\n        uint32_t           queueFamilyIndices[] = {(uint32_t)indices.graphicsFamily, (uint32_t)indices.presentFamily};\n\n        if (indices.graphicsFamily != indices.presentFamily) {\n            createInfo.imageSharingMode      = VK_SHARING_MODE_CONCURRENT;\n            createInfo.queueFamilyIndexCount = 2;\n            createInfo.pQueueFamilyIndices   = queueFamilyIndices;\n        }\n        else {\n            createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;\n        }\n\n        createInfo.preTransform   = swapChainSupport.capabilities.currentTransform;\n        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;\n        createInfo.presentMode    = presentMode;\n        createInfo.clipped        = VK_TRUE;\n\n        if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create swap chain!\");\n        }\n\n        vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);\n        swapChainImages.resize(imageCount);\n        vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());\n\n        swapChainImageFormat = surfaceFormat.format;\n        swapChainExtent      = extent;\n    }\n\n    void createImageViews()\n    {\n        swapChainImageViews.resize(swapChainImages.size());\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);\n        }\n    }\n\n    void createRenderPass()\n    {\n        VkAttachmentDescription colorAttachment = {};\n        colorAttachment.format                  = swapChainImageFormat;\n        colorAttachment.samples                 = VK_SAMPLE_COUNT_1_BIT;\n        colorAttachment.loadOp                  = VK_ATTACHMENT_LOAD_OP_CLEAR;\n        colorAttachment.storeOp                 = VK_ATTACHMENT_STORE_OP_STORE;\n        colorAttachment.stencilLoadOp           = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n        colorAttachment.stencilStoreOp          = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n        colorAttachment.initialLayout           = VK_IMAGE_LAYOUT_UNDEFINED;\n        colorAttachment.finalLayout             = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n\n        VkAttachmentReference colorAttachmentRef = {};\n        colorAttachmentRef.attachment            = 0;\n        colorAttachmentRef.layout                = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n        VkSubpassDescription subpass = {};\n        subpass.pipelineBindPoint    = VK_PIPELINE_BIND_POINT_GRAPHICS;\n        subpass.colorAttachmentCount = 1;\n        subpass.pColorAttachments    = &colorAttachmentRef;\n\n        VkSubpassDependency dependency = {};\n        dependency.srcSubpass          = VK_SUBPASS_EXTERNAL;\n        dependency.dstSubpass          = 0;\n        dependency.srcStageMask        = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        dependency.srcAccessMask       = 0;\n        dependency.dstStageMask        = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        dependency.dstAccessMask       = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n        VkRenderPassCreateInfo renderPassInfo = {};\n        renderPassInfo.sType                  = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;\n        renderPassInfo.attachmentCount        = 1;\n        renderPassInfo.pAttachments           = &colorAttachment;\n        renderPassInfo.subpassCount           = 1;\n        renderPassInfo.pSubpasses             = &subpass;\n        renderPassInfo.dependencyCount        = 1;\n        renderPassInfo.pDependencies          = &dependency;\n\n        if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create render pass!\");\n        }\n    }\n\n    void createDescriptorSetLayout()\n    {\n        VkDescriptorSetLayoutBinding uboLayoutBinding = {};\n        uboLayoutBinding.binding                      = 0;\n        uboLayoutBinding.descriptorCount              = 1;\n        uboLayoutBinding.descriptorType               = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n        uboLayoutBinding.pImmutableSamplers           = nullptr;\n        uboLayoutBinding.stageFlags                   = VK_SHADER_STAGE_VERTEX_BIT;\n\n        VkDescriptorSetLayoutBinding samplerLayoutBinding = {};\n        samplerLayoutBinding.binding                      = 1;\n        samplerLayoutBinding.descriptorCount              = 1;\n        samplerLayoutBinding.descriptorType               = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        samplerLayoutBinding.pImmutableSamplers           = nullptr;\n        samplerLayoutBinding.stageFlags                   = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n        std::array<VkDescriptorSetLayoutBinding, 2> bindings   = {uboLayoutBinding, samplerLayoutBinding};\n        VkDescriptorSetLayoutCreateInfo             layoutInfo = {};\n        layoutInfo.sType                                       = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n        layoutInfo.bindingCount                                = static_cast<uint32_t>(bindings.size());\n        layoutInfo.pBindings                                   = bindings.data();\n\n        if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create descriptor set layout!\");\n        }\n    }\n\n    void createGraphicsPipeline()\n    {\n        auto vertShaderCode = readFile(\"vert.spv\");\n        auto fragShaderCode = readFile(\"frag.spv\");\n\n        VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);\n        VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);\n\n        VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};\n        vertShaderStageInfo.sType                           = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n        vertShaderStageInfo.stage                           = VK_SHADER_STAGE_VERTEX_BIT;\n        vertShaderStageInfo.module                          = vertShaderModule;\n        vertShaderStageInfo.pName                           = \"main\";\n\n        VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};\n        fragShaderStageInfo.sType                           = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n        fragShaderStageInfo.stage                           = VK_SHADER_STAGE_FRAGMENT_BIT;\n        fragShaderStageInfo.module                          = fragShaderModule;\n        fragShaderStageInfo.pName                           = \"main\";\n\n        VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};\n\n        VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};\n        vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;\n\n        auto bindingDescription    = Vertex::getBindingDescription();\n        auto attributeDescriptions = Vertex::getAttributeDescriptions();\n\n        vertexInputInfo.vertexBindingDescriptionCount   = 1;\n        vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());\n        vertexInputInfo.pVertexBindingDescriptions      = &bindingDescription;\n        vertexInputInfo.pVertexAttributeDescriptions    = attributeDescriptions.data();\n\n        VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};\n        inputAssembly.sType                  = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;\n        inputAssembly.topology               = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n        inputAssembly.primitiveRestartEnable = VK_FALSE;\n\n        VkViewport viewport = {};\n        viewport.x          = 0.0f;\n        viewport.y          = 0.0f;\n        viewport.width      = (float)swapChainExtent.width;\n        viewport.height     = (float)swapChainExtent.height;\n        viewport.minDepth   = 0.0f;\n        viewport.maxDepth   = 1.0f;\n\n        VkRect2D scissor = {};\n        scissor.offset   = {0, 0};\n        scissor.extent   = swapChainExtent;\n\n        VkPipelineViewportStateCreateInfo viewportState = {};\n        viewportState.sType                             = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n        viewportState.viewportCount                     = 1;\n        viewportState.pViewports                        = &viewport;\n        viewportState.scissorCount                      = 1;\n        viewportState.pScissors                         = &scissor;\n\n        VkPipelineRasterizationStateCreateInfo rasterizer = {};\n        rasterizer.sType                                  = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;\n        rasterizer.depthClampEnable                       = VK_FALSE;\n        rasterizer.rasterizerDiscardEnable                = VK_FALSE;\n        rasterizer.polygonMode                            = VK_POLYGON_MODE_FILL;\n        rasterizer.lineWidth                              = 1.0f;\n        rasterizer.cullMode                               = VK_CULL_MODE_BACK_BIT;\n        rasterizer.frontFace                              = VK_FRONT_FACE_COUNTER_CLOCKWISE;\n        rasterizer.depthBiasEnable                        = VK_FALSE;\n\n        VkPipelineMultisampleStateCreateInfo multisampling = {};\n        multisampling.sType                                = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;\n        multisampling.sampleShadingEnable                  = VK_FALSE;\n        multisampling.rasterizationSamples                 = VK_SAMPLE_COUNT_1_BIT;\n\n        VkPipelineColorBlendAttachmentState colorBlendAttachment = {};\n        colorBlendAttachment.colorWriteMask =\n            VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n        colorBlendAttachment.blendEnable = VK_FALSE;\n\n        VkPipelineColorBlendStateCreateInfo colorBlending = {};\n        colorBlending.sType                               = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;\n        colorBlending.logicOpEnable                       = VK_FALSE;\n        colorBlending.logicOp                             = VK_LOGIC_OP_COPY;\n        colorBlending.attachmentCount                     = 1;\n        colorBlending.pAttachments                        = &colorBlendAttachment;\n        colorBlending.blendConstants[0]                   = 0.0f;\n        colorBlending.blendConstants[1]                   = 0.0f;\n        colorBlending.blendConstants[2]                   = 0.0f;\n        colorBlending.blendConstants[3]                   = 0.0f;\n\n        VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};\n        pipelineLayoutInfo.sType                      = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n        pipelineLayoutInfo.setLayoutCount             = 1;\n        pipelineLayoutInfo.pSetLayouts                = &descriptorSetLayout;\n\n        if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create pipeline layout!\");\n        }\n\n        VkGraphicsPipelineCreateInfo pipelineInfo = {};\n        pipelineInfo.sType                        = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n        pipelineInfo.stageCount                   = 2;\n        pipelineInfo.pStages                      = shaderStages;\n        pipelineInfo.pVertexInputState            = &vertexInputInfo;\n        pipelineInfo.pInputAssemblyState          = &inputAssembly;\n        pipelineInfo.pViewportState               = &viewportState;\n        pipelineInfo.pRasterizationState          = &rasterizer;\n        pipelineInfo.pMultisampleState            = &multisampling;\n        pipelineInfo.pColorBlendState             = &colorBlending;\n        pipelineInfo.layout                       = pipelineLayout;\n        pipelineInfo.renderPass                   = renderPass;\n        pipelineInfo.subpass                      = 0;\n        pipelineInfo.basePipelineHandle           = VK_NULL_HANDLE;\n\n        if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline)\n            != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create graphics pipeline!\");\n        }\n\n        vkDestroyShaderModule(device, fragShaderModule, nullptr);\n        vkDestroyShaderModule(device, vertShaderModule, nullptr);\n    }\n\n    void createFramebuffers()\n    {\n        swapChainFramebuffers.resize(swapChainImageViews.size());\n\n        for (size_t i = 0; i < swapChainImageViews.size(); i++) {\n            VkImageView attachments[] = {swapChainImageViews[i]};\n\n            VkFramebufferCreateInfo framebufferInfo = {};\n            framebufferInfo.sType                   = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;\n            framebufferInfo.renderPass              = renderPass;\n            framebufferInfo.attachmentCount         = 1;\n            framebufferInfo.pAttachments            = attachments;\n            framebufferInfo.width                   = swapChainExtent.width;\n            framebufferInfo.height                  = swapChainExtent.height;\n            framebufferInfo.layers                  = 1;\n\n            if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to create framebuffer!\");\n            }\n        }\n    }\n\n    void createCommandPool()\n    {\n        QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);\n\n        VkCommandPoolCreateInfo poolInfo = {};\n        poolInfo.sType                   = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n        poolInfo.queueFamilyIndex        = queueFamilyIndices.graphicsFamily;\n\n        if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create graphics command pool!\");\n        }\n    }\n\n    void createTextureImage()\n    {\n        VkDeviceSize imageSize = imageWidth * imageHeight * 4;\n        mipLevels              = static_cast<uint32_t>(std::floor(std::log2(std::max(imageWidth, imageHeight)))) + 1;\n        printf(\"mipLevels = %d\\n\", mipLevels);\n\n        if (!image_data) {\n            throw std::runtime_error(\"failed to load texture image!\");\n        }\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(imageSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);\n        memcpy(data, image_data, static_cast<size_t>(imageSize));\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        // VK_FORMAT_R8G8B8A8_UNORM changed to VK_FORMAT_R8G8B8A8_UINT\n        createImage(imageWidth,\n                    imageHeight,\n                    VK_FORMAT_R8G8B8A8_UNORM,\n                    VK_IMAGE_TILING_OPTIMAL,\n                    VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                        | VK_IMAGE_USAGE_SAMPLED_BIT,\n                    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                    textureImage,\n                    textureImageMemory);\n\n        transitionImageLayout(\n            textureImage, VK_FORMAT_R8G8B8A8_UINT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n        copyBufferToImage(\n            stagingBuffer, textureImage, static_cast<uint32_t>(imageWidth), static_cast<uint32_t>(imageHeight));\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n\n        generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_UNORM);\n    }\n\n    void generateMipmaps(VkImage image, VkFormat imageFormat)\n    {\n        VkFormatProperties formatProperties;\n        vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);\n\n        if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {\n            throw std::runtime_error(\"texture image format does not support linear blitting!\");\n        }\n\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkImageMemoryBarrier barrier            = {};\n        barrier.sType                           = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        barrier.image                           = image;\n        barrier.srcQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.dstQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        barrier.subresourceRange.baseArrayLayer = 0;\n        barrier.subresourceRange.layerCount     = 1;\n        barrier.subresourceRange.levelCount     = 1;\n\n        int32_t mipWidth  = imageWidth;\n        int32_t mipHeight = imageHeight;\n\n        for (uint32_t i = 1; i < mipLevels; i++) {\n            barrier.subresourceRange.baseMipLevel = i - 1;\n            barrier.oldLayout                     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n            barrier.newLayout                     = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;\n            barrier.srcAccessMask                 = VK_ACCESS_TRANSFER_WRITE_BIT;\n            barrier.dstAccessMask                 = VK_ACCESS_TRANSFER_READ_BIT;\n\n            vkCmdPipelineBarrier(commandBuffer,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 0,\n                                 0,\n                                 nullptr,\n                                 0,\n                                 nullptr,\n                                 1,\n                                 &barrier);\n\n            VkImageBlit blit                   = {};\n            blit.srcOffsets[0]                 = {0, 0, 0};\n            blit.srcOffsets[1]                 = {mipWidth, mipHeight, 1};\n            blit.srcSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n            blit.srcSubresource.mipLevel       = i - 1;\n            blit.srcSubresource.baseArrayLayer = 0;\n            blit.srcSubresource.layerCount     = 1;\n            blit.dstOffsets[0]                 = {0, 0, 0};\n            blit.dstOffsets[1]             = {mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1};\n            blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n            blit.dstSubresource.mipLevel   = i;\n            blit.dstSubresource.baseArrayLayer = 0;\n            blit.dstSubresource.layerCount     = 1;\n\n            vkCmdBlitImage(commandBuffer,\n                           image,\n                           VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n                           image,\n                           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n                           1,\n                           &blit,\n                           VK_FILTER_LINEAR);\n\n            barrier.oldLayout     = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;\n            barrier.newLayout     = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n            barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n            barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n\n            vkCmdPipelineBarrier(commandBuffer,\n                                 VK_PIPELINE_STAGE_TRANSFER_BIT,\n                                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n                                 0,\n                                 0,\n                                 nullptr,\n                                 0,\n                                 nullptr,\n                                 1,\n                                 &barrier);\n\n            if (mipWidth > 1)\n                mipWidth /= 2;\n            if (mipHeight > 1)\n                mipHeight /= 2;\n        }\n\n        barrier.subresourceRange.baseMipLevel = mipLevels - 1;\n        barrier.oldLayout                     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n        barrier.newLayout                     = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n        barrier.srcAccessMask                 = VK_ACCESS_TRANSFER_WRITE_BIT;\n        barrier.dstAccessMask                 = VK_ACCESS_SHADER_READ_BIT;\n\n        vkCmdPipelineBarrier(commandBuffer,\n                             VK_PIPELINE_STAGE_TRANSFER_BIT,\n                             VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n                             0,\n                             0,\n                             nullptr,\n                             0,\n                             nullptr,\n                             1,\n                             &barrier);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n#ifdef _WIN64 // For windows\n    HANDLE getVkImageMemHandle(VkExternalMemoryHandleTypeFlagsKHR externalMemoryHandleType)\n    {\n        HANDLE handle;\n\n        VkMemoryGetWin32HandleInfoKHR vkMemoryGetWin32HandleInfoKHR = {};\n        vkMemoryGetWin32HandleInfoKHR.sType      = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;\n        vkMemoryGetWin32HandleInfoKHR.pNext      = NULL;\n        vkMemoryGetWin32HandleInfoKHR.memory     = textureImageMemory;\n        vkMemoryGetWin32HandleInfoKHR.handleType = (VkExternalMemoryHandleTypeFlagBitsKHR)externalMemoryHandleType;\n\n        fpGetMemoryWin32HandleKHR(device, &vkMemoryGetWin32HandleInfoKHR, &handle);\n        return handle;\n    }\n    HANDLE getVkSemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBitsKHR externalSemaphoreHandleType,\n                                VkSemaphore                             &semVkCuda)\n    {\n        HANDLE handle;\n\n        VkSemaphoreGetWin32HandleInfoKHR vulkanSemaphoreGetWin32HandleInfoKHR = {};\n        vulkanSemaphoreGetWin32HandleInfoKHR.sType      = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR;\n        vulkanSemaphoreGetWin32HandleInfoKHR.pNext      = NULL;\n        vulkanSemaphoreGetWin32HandleInfoKHR.semaphore  = semVkCuda;\n        vulkanSemaphoreGetWin32HandleInfoKHR.handleType = externalSemaphoreHandleType;\n\n        fpGetSemaphoreWin32HandleKHR(device, &vulkanSemaphoreGetWin32HandleInfoKHR, &handle);\n\n        return handle;\n    }\n#else\n    int getVkImageMemHandle(VkExternalMemoryHandleTypeFlagsKHR externalMemoryHandleType)\n    {\n        if (externalMemoryHandleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR) {\n            int fd;\n\n            VkMemoryGetFdInfoKHR vkMemoryGetFdInfoKHR = {};\n            vkMemoryGetFdInfoKHR.sType                = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;\n            vkMemoryGetFdInfoKHR.pNext                = NULL;\n            vkMemoryGetFdInfoKHR.memory               = textureImageMemory;\n            vkMemoryGetFdInfoKHR.handleType           = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n\n            fpGetMemoryFdKHR(device, &vkMemoryGetFdInfoKHR, &fd);\n\n            return fd;\n        }\n        return -1;\n    }\n\n    int getVkSemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBitsKHR externalSemaphoreHandleType,\n                             VkSemaphore                             &semVkCuda)\n    {\n        if (externalSemaphoreHandleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {\n            int fd;\n\n            VkSemaphoreGetFdInfoKHR vulkanSemaphoreGetFdInfoKHR = {};\n            vulkanSemaphoreGetFdInfoKHR.sType                   = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;\n            vulkanSemaphoreGetFdInfoKHR.pNext                   = NULL;\n            vulkanSemaphoreGetFdInfoKHR.semaphore               = semVkCuda;\n            vulkanSemaphoreGetFdInfoKHR.handleType              = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n\n            fpGetSemaphoreFdKHR(device, &vulkanSemaphoreGetFdInfoKHR, &fd);\n\n            return fd;\n        }\n        return -1;\n    }\n#endif\n\n    void createTextureImageView() { textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); }\n\n    void createTextureSampler()\n    {\n        VkSamplerCreateInfo samplerInfo     = {};\n        samplerInfo.sType                   = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n        samplerInfo.magFilter               = VK_FILTER_LINEAR;\n        samplerInfo.minFilter               = VK_FILTER_LINEAR;\n        samplerInfo.addressModeU            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.addressModeV            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.addressModeW            = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        samplerInfo.anisotropyEnable        = VK_TRUE;\n        samplerInfo.maxAnisotropy           = 16;\n        samplerInfo.borderColor             = VK_BORDER_COLOR_INT_OPAQUE_BLACK;\n        samplerInfo.unnormalizedCoordinates = VK_FALSE;\n        samplerInfo.compareEnable           = VK_FALSE;\n        samplerInfo.compareOp               = VK_COMPARE_OP_ALWAYS;\n        samplerInfo.mipmapMode              = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n        samplerInfo.minLod                  = 0; // Optional\n        samplerInfo.maxLod                  = static_cast<float>(mipLevels);\n        samplerInfo.mipLodBias              = 0; // Optional\n\n        if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create texture sampler!\");\n        }\n    }\n\n    VkImageView createImageView(VkImage image, VkFormat format)\n    {\n        VkImageViewCreateInfo viewInfo           = {};\n        viewInfo.sType                           = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n        viewInfo.image                           = image;\n        viewInfo.viewType                        = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.format                          = format;\n        viewInfo.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        viewInfo.subresourceRange.baseMipLevel   = 0;\n        viewInfo.subresourceRange.levelCount     = mipLevels;\n        viewInfo.subresourceRange.baseArrayLayer = 0;\n        viewInfo.subresourceRange.layerCount     = 1;\n\n        VkImageView imageView;\n        if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create texture image view!\");\n        }\n\n        return imageView;\n    }\n\n    void createImage(uint32_t              width,\n                     uint32_t              height,\n                     VkFormat              format,\n                     VkImageTiling         tiling,\n                     VkImageUsageFlags     usage,\n                     VkMemoryPropertyFlags properties,\n                     VkImage              &image,\n                     VkDeviceMemory       &imageMemory)\n    {\n        VkImageCreateInfo imageInfo = {};\n        imageInfo.sType             = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n        imageInfo.imageType         = VK_IMAGE_TYPE_2D;\n        imageInfo.extent.width      = width;\n        imageInfo.extent.height     = height;\n        imageInfo.extent.depth      = 1;\n        imageInfo.mipLevels         = mipLevels;\n        imageInfo.arrayLayers       = 1;\n        imageInfo.format            = format;\n        imageInfo.tiling            = tiling;\n        imageInfo.initialLayout     = VK_IMAGE_LAYOUT_UNDEFINED;\n        imageInfo.usage             = usage;\n        imageInfo.samples           = VK_SAMPLE_COUNT_1_BIT;\n        imageInfo.sharingMode       = VK_SHARING_MODE_EXCLUSIVE;\n\n        VkExternalMemoryImageCreateInfo vkExternalMemImageCreateInfo = {};\n        vkExternalMemImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n        vkExternalMemImageCreateInfo.pNext = NULL;\n#ifdef _WIN64\n        vkExternalMemImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;\n#else\n        vkExternalMemImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n#endif\n\n        imageInfo.pNext = &vkExternalMemImageCreateInfo;\n\n        if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create image!\");\n        }\n\n        VkMemoryRequirements memRequirements;\n        vkGetImageMemoryRequirements(device, image, &memRequirements);\n\n#ifdef _WIN64\n        WindowsSecurityAttributes winSecurityAttributes;\n\n        VkExportMemoryWin32HandleInfoKHR vulkanExportMemoryWin32HandleInfoKHR = {};\n        vulkanExportMemoryWin32HandleInfoKHR.sType       = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR;\n        vulkanExportMemoryWin32HandleInfoKHR.pNext       = NULL;\n        vulkanExportMemoryWin32HandleInfoKHR.pAttributes = &winSecurityAttributes;\n        vulkanExportMemoryWin32HandleInfoKHR.dwAccess    = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;\n        vulkanExportMemoryWin32HandleInfoKHR.name        = (LPCWSTR)NULL;\n#endif\n        VkExportMemoryAllocateInfoKHR vulkanExportMemoryAllocateInfoKHR = {};\n        vulkanExportMemoryAllocateInfoKHR.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;\n#ifdef _WIN64\n        vulkanExportMemoryAllocateInfoKHR.pNext = IsWindows8OrGreater() ? &vulkanExportMemoryWin32HandleInfoKHR : NULL;\n        vulkanExportMemoryAllocateInfoKHR.handleTypes = IsWindows8OrGreater()\n                                                          ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                          : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n#else\n        vulkanExportMemoryAllocateInfoKHR.pNext       = NULL;\n        vulkanExportMemoryAllocateInfoKHR.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;\n#endif\n\n        VkMemoryAllocateInfo allocInfo = {};\n        allocInfo.sType                = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        allocInfo.allocationSize       = memRequirements.size;\n        allocInfo.pNext                = &vulkanExportMemoryAllocateInfoKHR;\n        allocInfo.memoryTypeIndex      = findMemoryType(memRequirements.memoryTypeBits, properties);\n\n        VkMemoryRequirements vkMemoryRequirements = {};\n        vkGetImageMemoryRequirements(device, image, &vkMemoryRequirements);\n        totalImageMemSize = vkMemoryRequirements.size;\n\n        if (vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate image memory!\");\n        }\n\n        vkBindImageMemory(device, image, textureImageMemory, 0);\n    }\n\n    void cudaVkImportSemaphore()\n    {\n        hipExternalSemaphoreHandleDesc externalSemaphoreHandleDesc;\n        memset(&externalSemaphoreHandleDesc, 0, sizeof(externalSemaphoreHandleDesc));\n#ifdef _WIN64\n        externalSemaphoreHandleDesc.type = IsWindows8OrGreater() ? hipExternalSemaphoreHandleTypeOpaqueWin32\n                                                                 : hipExternalSemaphoreHandleTypeOpaqueWin32Kmt;\n        externalSemaphoreHandleDesc.handle.win32.handle =\n            getVkSemaphoreHandle(IsWindows8OrGreater() ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                       : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,\n                                 cudaUpdateVkSemaphore);\n#else\n        externalSemaphoreHandleDesc.type = hipExternalSemaphoreHandleTypeOpaqueFd;\n        externalSemaphoreHandleDesc.handle.fd =\n            getVkSemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, cudaUpdateVkSemaphore);\n#endif\n        externalSemaphoreHandleDesc.flags = 0;\n\n        checkCudaErrors(hipImportExternalSemaphore(&cudaExtCudaUpdateVkSemaphore, &externalSemaphoreHandleDesc));\n\n        memset(&externalSemaphoreHandleDesc, 0, sizeof(externalSemaphoreHandleDesc));\n#ifdef _WIN64\n        externalSemaphoreHandleDesc.type = IsWindows8OrGreater() ? hipExternalSemaphoreHandleTypeOpaqueWin32\n                                                                 : hipExternalSemaphoreHandleTypeOpaqueWin32Kmt;\n        ;\n        externalSemaphoreHandleDesc.handle.win32.handle =\n            getVkSemaphoreHandle(IsWindows8OrGreater() ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                       : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,\n                                 vkUpdateCudaSemaphore);\n#else\n        externalSemaphoreHandleDesc.type = hipExternalSemaphoreHandleTypeOpaqueFd;\n        externalSemaphoreHandleDesc.handle.fd =\n            getVkSemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, vkUpdateCudaSemaphore);\n#endif\n        externalSemaphoreHandleDesc.flags = 0;\n        checkCudaErrors(hipImportExternalSemaphore(&cudaExtVkUpdateCudaSemaphore, &externalSemaphoreHandleDesc));\n        printf(\"CUDA Imported Vulkan semaphore\\n\");\n    }\n\n    void cudaVkImportImageMem()\n    {\n        hipExternalMemoryHandleDesc cudaExtMemHandleDesc;\n        memset(&cudaExtMemHandleDesc, 0, sizeof(cudaExtMemHandleDesc));\n#ifdef _WIN64\n        cudaExtMemHandleDesc.type = IsWindows8OrGreater() ? hipExternalMemoryHandleTypeOpaqueWin32\n                                                          : hipExternalMemoryHandleTypeOpaqueWin32Kmt;\n        cudaExtMemHandleDesc.handle.win32.handle =\n            getVkImageMemHandle(IsWindows8OrGreater() ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                      : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT);\n#else\n        cudaExtMemHandleDesc.type = hipExternalMemoryHandleTypeOpaqueFd;\n\n        cudaExtMemHandleDesc.handle.fd = getVkImageMemHandle(VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR);\n#endif\n        cudaExtMemHandleDesc.size = totalImageMemSize;\n\n        checkCudaErrors(hipImportExternalMemory(&cudaExtMemImageBuffer, &cudaExtMemHandleDesc));\n\n        cudaExternalMemoryMipmappedArrayDesc externalMemoryMipmappedArrayDesc;\n\n        memset(&externalMemoryMipmappedArrayDesc, 0, sizeof(externalMemoryMipmappedArrayDesc));\n\n        hipExtent            extent = make_hipExtent(imageWidth, imageHeight, 0);\n        hipChannelFormatDesc formatDesc;\n        formatDesc.x = 8;\n        formatDesc.y = 8;\n        formatDesc.z = 8;\n        formatDesc.w = 8;\n        formatDesc.f = hipChannelFormatKindUnsigned;\n\n        externalMemoryMipmappedArrayDesc.offset     = 0;\n        externalMemoryMipmappedArrayDesc.formatDesc = formatDesc;\n        externalMemoryMipmappedArrayDesc.extent     = extent;\n        externalMemoryMipmappedArrayDesc.flags      = 0;\n        externalMemoryMipmappedArrayDesc.numLevels  = mipLevels;\n\n        checkCudaErrors(cudaExternalMemoryGetMappedMipmappedArray(\n            &cudaMipmappedImageArray, cudaExtMemImageBuffer, &externalMemoryMipmappedArrayDesc));\n\n        checkCudaErrors(hipMallocMipmappedArray(&cudaMipmappedImageArrayTemp, &formatDesc, extent, mipLevels));\n        checkCudaErrors(hipMallocMipmappedArray(&cudaMipmappedImageArrayOrig, &formatDesc, extent, mipLevels));\n\n        for (int mipLevelIdx = 0; mipLevelIdx < mipLevels; mipLevelIdx++) {\n            hipArray_t      cudaMipLevelArray, cudaMipLevelArrayTemp, cudaMipLevelArrayOrig;\n            hipResourceDesc resourceDesc;\n\n            checkCudaErrors(hipGetMipmappedArrayLevel(&cudaMipLevelArray, cudaMipmappedImageArray, mipLevelIdx));\n            checkCudaErrors(\n                hipGetMipmappedArrayLevel(&cudaMipLevelArrayTemp, cudaMipmappedImageArrayTemp, mipLevelIdx));\n            checkCudaErrors(\n                hipGetMipmappedArrayLevel(&cudaMipLevelArrayOrig, cudaMipmappedImageArrayOrig, mipLevelIdx));\n\n            uint32_t width  = (imageWidth >> mipLevelIdx) ? (imageWidth >> mipLevelIdx) : 1;\n            uint32_t height = (imageHeight >> mipLevelIdx) ? (imageHeight >> mipLevelIdx) : 1;\n            checkCudaErrors(hipMemcpy2DArrayToArray(cudaMipLevelArrayOrig,\n                                                     0,\n                                                     0,\n                                                     cudaMipLevelArray,\n                                                     0,\n                                                     0,\n                                                     width * sizeof(uchar4),\n                                                     height,\n                                                     hipMemcpyDeviceToDevice));\n\n            memset(&resourceDesc, 0, sizeof(resourceDesc));\n            resourceDesc.resType         = hipResourceTypeArray;\n            resourceDesc.res.array.array = cudaMipLevelArray;\n\n            hipSurfaceObject_t surfaceObject;\n            checkCudaErrors(hipCreateSurfaceObject(&surfaceObject, &resourceDesc));\n\n            surfaceObjectList.push_back(surfaceObject);\n\n            memset(&resourceDesc, 0, sizeof(resourceDesc));\n            resourceDesc.resType         = hipResourceTypeArray;\n            resourceDesc.res.array.array = cudaMipLevelArrayTemp;\n\n            hipSurfaceObject_t surfaceObjectTemp;\n            checkCudaErrors(hipCreateSurfaceObject(&surfaceObjectTemp, &resourceDesc));\n            surfaceObjectListTemp.push_back(surfaceObjectTemp);\n        }\n\n        hipResourceDesc resDescr;\n        memset(&resDescr, 0, sizeof(hipResourceDesc));\n\n        resDescr.resType           = hipResourceTypeMipmappedArray;\n        resDescr.res.mipmap.mipmap = cudaMipmappedImageArrayOrig;\n\n        hipTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n        texDescr.normalizedCoords = true;\n        texDescr.filterMode       = hipFilterModeLinear;\n        texDescr.mipmapFilterMode = hipFilterModeLinear;\n\n        texDescr.addressMode[0] = hipAddressModeWrap;\n        texDescr.addressMode[1] = hipAddressModeWrap;\n\n        texDescr.maxMipmapLevelClamp = float(mipLevels - 1);\n\n        texDescr.readMode = hipReadModeNormalizedFloat;\n\n        checkCudaErrors(hipCreateTextureObject(&textureObjMipMapInput, &resDescr, &texDescr, NULL));\n\n        checkCudaErrors(hipMalloc((void **)&d_surfaceObjectList, sizeof(hipSurfaceObject_t) * mipLevels));\n        checkCudaErrors(hipMalloc((void **)&d_surfaceObjectListTemp, sizeof(hipSurfaceObject_t) * mipLevels));\n\n        checkCudaErrors(hipMemcpy(d_surfaceObjectList,\n                                   surfaceObjectList.data(),\n                                   sizeof(hipSurfaceObject_t) * mipLevels,\n                                   hipMemcpyHostToDevice));\n        checkCudaErrors(hipMemcpy(d_surfaceObjectListTemp,\n                                   surfaceObjectListTemp.data(),\n                                   sizeof(hipSurfaceObject_t) * mipLevels,\n                                   hipMemcpyHostToDevice));\n\n        printf(\"CUDA Kernel Vulkan image buffer\\n\");\n    }\n\n    void cudaUpdateVkImage()\n    {\n        cudaVkSemaphoreWait(cudaExtVkUpdateCudaSemaphore);\n\n        int nthreads = 128;\n\n        /*Perform 2D box filter on image using CUDA */\n        d_boxfilter_rgba_x<<<imageHeight / nthreads, nthreads, 0, streamToRun>>>(\n            d_surfaceObjectListTemp, textureObjMipMapInput, imageWidth, imageHeight, mipLevels, filter_radius);\n\n        d_boxfilter_rgba_y<<<imageWidth / nthreads, nthreads, 0, streamToRun>>>(\n            d_surfaceObjectList, d_surfaceObjectListTemp, imageWidth, imageHeight, mipLevels, filter_radius);\n\n        varySigma();\n\n        cudaVkSemaphoreSignal(cudaExtCudaUpdateVkSemaphore);\n    }\n\n    void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkImageMemoryBarrier barrier            = {};\n        barrier.sType                           = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        barrier.oldLayout                       = oldLayout;\n        barrier.newLayout                       = newLayout;\n        barrier.srcQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.dstQueueFamilyIndex             = VK_QUEUE_FAMILY_IGNORED;\n        barrier.image                           = image;\n        barrier.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        barrier.subresourceRange.baseMipLevel   = 0;\n        barrier.subresourceRange.levelCount     = mipLevels;\n        barrier.subresourceRange.baseArrayLayer = 0;\n        barrier.subresourceRange.layerCount     = 1;\n\n        VkPipelineStageFlags sourceStage;\n        VkPipelineStageFlags destinationStage;\n\n        if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {\n            barrier.srcAccessMask = 0;\n            barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\n            sourceStage      = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;\n            destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;\n        }\n        else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n                 && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {\n            barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n            barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n\n            sourceStage      = VK_PIPELINE_STAGE_TRANSFER_BIT;\n            destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n        }\n        else {\n            throw std::invalid_argument(\"unsupported layout transition!\");\n        }\n\n        vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkBufferImageCopy region               = {};\n        region.bufferOffset                    = 0;\n        region.bufferRowLength                 = 0;\n        region.bufferImageHeight               = 0;\n        region.imageSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n        region.imageSubresource.mipLevel       = 0;\n        region.imageSubresource.baseArrayLayer = 0;\n        region.imageSubresource.layerCount     = 1;\n        region.imageOffset                     = {0, 0, 0};\n        region.imageExtent                     = {width, height, 1};\n\n        vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    void createVertexBuffer()\n    {\n        VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);\n        memcpy(data, vertices.data(), (size_t)bufferSize);\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,\n                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                     vertexBuffer,\n                     vertexBufferMemory);\n\n        copyBuffer(stagingBuffer, vertexBuffer, bufferSize);\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n    }\n\n    void createIndexBuffer()\n    {\n        VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();\n\n        VkBuffer       stagingBuffer;\n        VkDeviceMemory stagingBufferMemory;\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                     stagingBuffer,\n                     stagingBufferMemory);\n\n        void *data;\n        vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);\n        memcpy(data, indices.data(), (size_t)bufferSize);\n        vkUnmapMemory(device, stagingBufferMemory);\n\n        createBuffer(bufferSize,\n                     VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,\n                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n                     indexBuffer,\n                     indexBufferMemory);\n\n        copyBuffer(stagingBuffer, indexBuffer, bufferSize);\n\n        vkDestroyBuffer(device, stagingBuffer, nullptr);\n        vkFreeMemory(device, stagingBufferMemory, nullptr);\n    }\n\n    void createUniformBuffers()\n    {\n        VkDeviceSize bufferSize = sizeof(UniformBufferObject);\n\n        uniformBuffers.resize(swapChainImages.size());\n        uniformBuffersMemory.resize(swapChainImages.size());\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            createBuffer(bufferSize,\n                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,\n                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n                         uniformBuffers[i],\n                         uniformBuffersMemory[i]);\n        }\n    }\n\n    void createDescriptorPool()\n    {\n        std::array<VkDescriptorPoolSize, 2> poolSizes = {};\n        poolSizes[0].type                             = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n        poolSizes[0].descriptorCount                  = static_cast<uint32_t>(swapChainImages.size());\n        poolSizes[1].type                             = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        poolSizes[1].descriptorCount                  = static_cast<uint32_t>(swapChainImages.size());\n\n        VkDescriptorPoolCreateInfo poolInfo = {};\n        poolInfo.sType                      = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;\n        poolInfo.poolSizeCount              = static_cast<uint32_t>(poolSizes.size());\n        poolInfo.pPoolSizes                 = poolSizes.data();\n        poolInfo.maxSets                    = static_cast<uint32_t>(swapChainImages.size());\n\n        if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create descriptor pool!\");\n        }\n    }\n\n    void createDescriptorSets()\n    {\n        std::vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), descriptorSetLayout);\n        VkDescriptorSetAllocateInfo        allocInfo = {};\n        allocInfo.sType                              = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n        allocInfo.descriptorPool                     = descriptorPool;\n        allocInfo.descriptorSetCount                 = static_cast<uint32_t>(swapChainImages.size());\n        allocInfo.pSetLayouts                        = layouts.data();\n\n        descriptorSets.resize(swapChainImages.size());\n        if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate descriptor sets!\");\n        }\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            VkDescriptorBufferInfo bufferInfo = {};\n            bufferInfo.buffer                 = uniformBuffers[i];\n            bufferInfo.offset                 = 0;\n            bufferInfo.range                  = sizeof(UniformBufferObject);\n\n            VkDescriptorImageInfo imageInfo = {};\n            imageInfo.imageLayout           = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n            imageInfo.imageView             = textureImageView;\n            imageInfo.sampler               = textureSampler;\n\n            std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};\n\n            descriptorWrites[0].sType           = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n            descriptorWrites[0].dstSet          = descriptorSets[i];\n            descriptorWrites[0].dstBinding      = 0;\n            descriptorWrites[0].dstArrayElement = 0;\n            descriptorWrites[0].descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n            descriptorWrites[0].descriptorCount = 1;\n            descriptorWrites[0].pBufferInfo     = &bufferInfo;\n\n            descriptorWrites[1].sType           = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n            descriptorWrites[1].dstSet          = descriptorSets[i];\n            descriptorWrites[1].dstBinding      = 1;\n            descriptorWrites[1].dstArrayElement = 0;\n            descriptorWrites[1].descriptorType  = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n            descriptorWrites[1].descriptorCount = 1;\n            descriptorWrites[1].pImageInfo      = &imageInfo;\n\n            vkUpdateDescriptorSets(\n                device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);\n        }\n    }\n\n    void createBuffer(VkDeviceSize          size,\n                      VkBufferUsageFlags    usage,\n                      VkMemoryPropertyFlags properties,\n                      VkBuffer             &buffer,\n                      VkDeviceMemory       &bufferMemory)\n    {\n        VkBufferCreateInfo bufferInfo = {};\n        bufferInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n        bufferInfo.size               = size;\n        bufferInfo.usage              = usage;\n        bufferInfo.sharingMode        = VK_SHARING_MODE_EXCLUSIVE;\n\n        if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create buffer!\");\n        }\n\n        VkMemoryRequirements memRequirements;\n        vkGetBufferMemoryRequirements(device, buffer, &memRequirements);\n\n        VkMemoryAllocateInfo allocInfo = {};\n        allocInfo.sType                = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        allocInfo.allocationSize       = memRequirements.size;\n        allocInfo.memoryTypeIndex      = findMemoryType(memRequirements.memoryTypeBits, properties);\n\n        if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate buffer memory!\");\n        }\n\n        vkBindBufferMemory(device, buffer, bufferMemory, 0);\n    }\n\n    VkCommandBuffer beginSingleTimeCommands()\n    {\n        VkCommandBufferAllocateInfo allocInfo = {};\n        allocInfo.sType                       = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n        allocInfo.level                       = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n        allocInfo.commandPool                 = commandPool;\n        allocInfo.commandBufferCount          = 1;\n\n        VkCommandBuffer commandBuffer;\n        vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);\n\n        VkCommandBufferBeginInfo beginInfo = {};\n        beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n        beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n        vkBeginCommandBuffer(commandBuffer, &beginInfo);\n\n        return commandBuffer;\n    }\n\n    void endSingleTimeCommands(VkCommandBuffer commandBuffer)\n    {\n        vkEndCommandBuffer(commandBuffer);\n\n        VkSubmitInfo submitInfo       = {};\n        submitInfo.sType              = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n        submitInfo.commandBufferCount = 1;\n        submitInfo.pCommandBuffers    = &commandBuffer;\n\n        vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);\n        vkQueueWaitIdle(graphicsQueue);\n\n        vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);\n    }\n\n    void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)\n    {\n        VkCommandBuffer commandBuffer = beginSingleTimeCommands();\n\n        VkBufferCopy copyRegion = {};\n        copyRegion.size         = size;\n        vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);\n\n        endSingleTimeCommands(commandBuffer);\n    }\n\n    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)\n    {\n        VkPhysicalDeviceMemoryProperties memProperties;\n        vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);\n\n        for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {\n            if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {\n                return i;\n            }\n        }\n\n        throw std::runtime_error(\"failed to find suitable memory type!\");\n    }\n\n    void createCommandBuffers()\n    {\n        commandBuffers.resize(swapChainFramebuffers.size());\n\n        VkCommandBufferAllocateInfo allocInfo = {};\n        allocInfo.sType                       = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n        allocInfo.commandPool                 = commandPool;\n        allocInfo.level                       = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n        allocInfo.commandBufferCount          = (uint32_t)commandBuffers.size();\n\n        if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to allocate command buffers!\");\n        }\n\n        for (size_t i = 0; i < commandBuffers.size(); i++) {\n            VkCommandBufferBeginInfo beginInfo = {};\n            beginInfo.sType                    = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n            beginInfo.flags                    = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;\n\n            if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to begin recording command buffer!\");\n            }\n\n            VkRenderPassBeginInfo renderPassInfo = {};\n            renderPassInfo.sType                 = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n            renderPassInfo.renderPass            = renderPass;\n            renderPassInfo.framebuffer           = swapChainFramebuffers[i];\n            renderPassInfo.renderArea.offset     = {0, 0};\n            renderPassInfo.renderArea.extent     = swapChainExtent;\n\n            VkClearValue clearColor        = {0.0f, 0.0f, 0.0f, 1.0f};\n            renderPassInfo.clearValueCount = 1;\n            renderPassInfo.pClearValues    = &clearColor;\n\n            vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);\n\n            vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);\n\n            VkBuffer     vertexBuffers[] = {vertexBuffer};\n            VkDeviceSize offsets[]       = {0};\n            vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);\n\n            vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);\n\n            vkCmdBindDescriptorSets(commandBuffers[i],\n                                    VK_PIPELINE_BIND_POINT_GRAPHICS,\n                                    pipelineLayout,\n                                    0,\n                                    1,\n                                    &descriptorSets[i],\n                                    0,\n                                    nullptr);\n\n            vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);\n            // vkCmdDraw(commandBuffers[i], static_cast<uint32_t>(vertices.size()), 1,\n            // 0, 0);\n\n            vkCmdEndRenderPass(commandBuffers[i]);\n\n            if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to record command buffer!\");\n            }\n        }\n    }\n\n    void createSyncObjects()\n    {\n        imageAvailableSemaphores.resize(MAX_FRAMES);\n        renderFinishedSemaphores.resize(MAX_FRAMES);\n        inFlightFences.resize(MAX_FRAMES);\n\n        VkSemaphoreCreateInfo semaphoreInfo = {};\n        semaphoreInfo.sType                 = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n        VkFenceCreateInfo fenceInfo = {};\n        fenceInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;\n        fenceInfo.flags             = VK_FENCE_CREATE_SIGNALED_BIT;\n\n        for (size_t i = 0; i < MAX_FRAMES; i++) {\n            if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS\n                || vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS\n                || vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {\n                throw std::runtime_error(\"failed to create synchronization objects for a frame!\");\n            }\n        }\n    }\n\n    void createSyncObjectsExt()\n    {\n        VkSemaphoreCreateInfo semaphoreInfo = {};\n        semaphoreInfo.sType                 = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n        memset(&semaphoreInfo, 0, sizeof(semaphoreInfo));\n        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\n#ifdef _WIN64\n        WindowsSecurityAttributes winSecurityAttributes;\n\n        VkExportSemaphoreWin32HandleInfoKHR vulkanExportSemaphoreWin32HandleInfoKHR = {};\n        vulkanExportSemaphoreWin32HandleInfoKHR.sType       = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR;\n        vulkanExportSemaphoreWin32HandleInfoKHR.pNext       = NULL;\n        vulkanExportSemaphoreWin32HandleInfoKHR.pAttributes = &winSecurityAttributes;\n        vulkanExportSemaphoreWin32HandleInfoKHR.dwAccess    = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;\n        vulkanExportSemaphoreWin32HandleInfoKHR.name        = (LPCWSTR)NULL;\n#endif\n        VkExportSemaphoreCreateInfoKHR vulkanExportSemaphoreCreateInfo = {};\n        vulkanExportSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR;\n#ifdef _WIN64\n        vulkanExportSemaphoreCreateInfo.pNext = IsWindows8OrGreater() ? &vulkanExportSemaphoreWin32HandleInfoKHR : NULL;\n        vulkanExportSemaphoreCreateInfo.handleTypes = IsWindows8OrGreater()\n                                                        ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n                                                        : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n#else\n        vulkanExportSemaphoreCreateInfo.pNext       = NULL;\n        vulkanExportSemaphoreCreateInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n#endif\n        semaphoreInfo.pNext = &vulkanExportSemaphoreCreateInfo;\n\n        if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &cudaUpdateVkSemaphore) != VK_SUCCESS\n            || vkCreateSemaphore(device, &semaphoreInfo, nullptr, &vkUpdateCudaSemaphore) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create synchronization objects for a CUDA-Vulkan!\");\n        }\n    }\n\n    void updateUniformBuffer()\n    {\n        UniformBufferObject ubo = {};\n\n        mat4x4_identity(ubo.model);\n        mat4x4 Model;\n        mat4x4_dup(Model, ubo.model);\n        mat4x4_rotate(ubo.model, Model, 0.0f, 0.0f, 1.0f, degreesToRadians(135.0f));\n\n        vec3 eye    = {2.0f, 2.0f, 2.0f};\n        vec3 center = {0.0f, 0.0f, 0.0f};\n        vec3 up     = {0.0f, 0.0f, 1.0f};\n        mat4x4_look_at(ubo.view, eye, center, up);\n\n        mat4x4_perspective(\n            ubo.proj, degreesToRadians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f);\n        ubo.proj[1][1] *= -1;\n\n        for (size_t i = 0; i < swapChainImages.size(); i++) {\n            void *data;\n            vkMapMemory(device, uniformBuffersMemory[i], 0, sizeof(ubo), 0, &data);\n            memcpy(data, &ubo, sizeof(ubo));\n            vkUnmapMemory(device, uniformBuffersMemory[i]);\n        }\n    }\n\n    void drawFrame()\n    {\n        static int startSubmit = 0;\n\n        vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());\n\n        uint32_t imageIndex;\n        VkResult result = vkAcquireNextImageKHR(device,\n                                                swapChain,\n                                                std::numeric_limits<uint64_t>::max(),\n                                                imageAvailableSemaphores[currentFrame],\n                                                VK_NULL_HANDLE,\n                                                &imageIndex);\n\n        if (result == VK_ERROR_OUT_OF_DATE_KHR) {\n            recreateSwapChain();\n            return;\n        }\n        else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {\n            throw std::runtime_error(\"failed to acquire swap chain image!\");\n        }\n\n        vkResetFences(device, 1, &inFlightFences[currentFrame]);\n\n        if (!startSubmit) {\n            submitVulkan(imageIndex);\n            startSubmit = 1;\n        }\n        else {\n            submitVulkanCuda(imageIndex);\n        }\n\n        VkPresentInfoKHR presentInfo = {};\n        presentInfo.sType            = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};\n\n        presentInfo.waitSemaphoreCount = 1;\n        presentInfo.pWaitSemaphores    = signalSemaphores;\n\n        VkSwapchainKHR swapChains[] = {swapChain};\n        presentInfo.swapchainCount  = 1;\n        presentInfo.pSwapchains     = swapChains;\n        presentInfo.pImageIndices   = &imageIndex;\n        presentInfo.pResults        = nullptr; // Optional\n\n        result = vkQueuePresentKHR(presentQueue, &presentInfo);\n\n        if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {\n            framebufferResized = false;\n            recreateSwapChain();\n        }\n        else if (result != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to present swap chain image!\");\n        }\n\n        cudaUpdateVkImage();\n\n        currentFrame = (currentFrame + 1) % MAX_FRAMES;\n        // Added sleep of 10 millisecs so that CPU does not submit too much work to\n        // GPU\n        std::this_thread::sleep_for(std::chrono::microseconds(10000));\n        char title[256];\n        sprintf(title, \"Vulkan Image CUDA Box Filter (radius=%d)\", filter_radius);\n        glfwSetWindowTitle(window, title);\n    }\n\n    void cudaVkSemaphoreSignal(hipExternalSemaphore_t &extSemaphore)\n    {\n        hipExternalSemaphoreSignalParams extSemaphoreSignalParams;\n        memset(&extSemaphoreSignalParams, 0, sizeof(extSemaphoreSignalParams));\n\n        extSemaphoreSignalParams.params.fence.value = 0;\n        extSemaphoreSignalParams.flags              = 0;\n        checkCudaErrors(hipSignalExternalSemaphoresAsync(&extSemaphore, &extSemaphoreSignalParams, 1, streamToRun));\n    }\n\n    void cudaVkSemaphoreWait(hipExternalSemaphore_t &extSemaphore)\n    {\n        hipExternalSemaphoreWaitParams extSemaphoreWaitParams;\n\n        memset(&extSemaphoreWaitParams, 0, sizeof(extSemaphoreWaitParams));\n\n        extSemaphoreWaitParams.params.fence.value = 0;\n        extSemaphoreWaitParams.flags              = 0;\n\n        checkCudaErrors(hipWaitExternalSemaphoresAsync(&extSemaphore, &extSemaphoreWaitParams, 1, streamToRun));\n    }\n\n    void submitVulkan(uint32_t imageIndex)\n    {\n        VkSubmitInfo submitInfo = {};\n        submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\n        VkSemaphore          waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};\n        VkPipelineStageFlags waitStages[]     = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};\n        submitInfo.waitSemaphoreCount         = 1;\n        submitInfo.pWaitSemaphores            = waitSemaphores;\n        submitInfo.pWaitDstStageMask          = waitStages;\n        submitInfo.commandBufferCount         = 1;\n        submitInfo.pCommandBuffers            = &commandBuffers[imageIndex];\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame], vkUpdateCudaSemaphore};\n\n        submitInfo.signalSemaphoreCount = 2;\n        submitInfo.pSignalSemaphores    = signalSemaphores;\n\n        if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to submit draw command buffer!\");\n        }\n    }\n\n    void submitVulkanCuda(uint32_t imageIndex)\n    {\n        VkSubmitInfo submitInfo = {};\n        submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\n        VkSemaphore          waitSemaphores[] = {imageAvailableSemaphores[currentFrame], cudaUpdateVkSemaphore};\n        VkPipelineStageFlags waitStages[]     = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n                                                 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};\n        submitInfo.waitSemaphoreCount         = 2;\n        submitInfo.pWaitSemaphores            = waitSemaphores;\n        submitInfo.pWaitDstStageMask          = waitStages;\n        submitInfo.commandBufferCount         = 1;\n        submitInfo.pCommandBuffers            = &commandBuffers[imageIndex];\n\n        VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame], vkUpdateCudaSemaphore};\n\n        submitInfo.signalSemaphoreCount = 2;\n        submitInfo.pSignalSemaphores    = signalSemaphores;\n\n        if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to submit draw command buffer!\");\n        }\n    }\n\n    VkShaderModule createShaderModule(const std::vector<char> &code)\n    {\n        VkShaderModuleCreateInfo createInfo = {};\n        createInfo.sType                    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n        createInfo.codeSize                 = code.size();\n        createInfo.pCode                    = reinterpret_cast<const uint32_t *>(code.data());\n\n        VkShaderModule shaderModule;\n        if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {\n            throw std::runtime_error(\"failed to create shader module!\");\n        }\n\n        return shaderModule;\n    }\n\n    VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats)\n    {\n        if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {\n            return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};\n        }\n\n        for (const auto &availableFormat : availableFormats) {\n            if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM\n                && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {\n                return availableFormat;\n            }\n        }\n\n        return availableFormats[0];\n    }\n\n    VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes)\n    {\n        VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;\n\n        for (const auto &availablePresentMode : availablePresentModes) {\n            if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {\n                return availablePresentMode;\n            }\n            else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {\n                bestMode = availablePresentMode;\n            }\n        }\n\n        return bestMode;\n    }\n\n    VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities)\n    {\n        if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {\n            return capabilities.currentExtent;\n        }\n        else {\n            int width, height;\n            glfwGetFramebufferSize(window, &width, &height);\n\n            VkExtent2D actualExtent = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};\n\n            actualExtent.width  = std::max(capabilities.minImageExtent.width,\n                                          std::min(capabilities.maxImageExtent.width, actualExtent.width));\n            actualExtent.height = std::max(capabilities.minImageExtent.height,\n                                           std::min(capabilities.maxImageExtent.height, actualExtent.height));\n\n            return actualExtent;\n        }\n    }\n\n    SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device)\n    {\n        SwapChainSupportDetails details;\n\n        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);\n\n        uint32_t formatCount;\n        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);\n\n        if (formatCount != 0) {\n            details.formats.resize(formatCount);\n            vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());\n        }\n\n        uint32_t presentModeCount;\n        vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);\n\n        if (presentModeCount != 0) {\n            details.presentModes.resize(presentModeCount);\n            vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());\n        }\n\n        return details;\n    }\n\n    bool isDeviceSuitable(VkPhysicalDevice device)\n    {\n        QueueFamilyIndices indices = findQueueFamilies(device);\n\n        bool extensionsSupported = checkDeviceExtensionSupport(device);\n\n        bool swapChainAdequate = false;\n        if (extensionsSupported) {\n            SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);\n            swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();\n        }\n\n        VkPhysicalDeviceFeatures supportedFeatures;\n        vkGetPhysicalDeviceFeatures(device, &supportedFeatures);\n\n        return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;\n    }\n\n    bool checkDeviceExtensionSupport(VkPhysicalDevice device)\n    {\n        uint32_t extensionCount;\n        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);\n\n        std::vector<VkExtensionProperties> availableExtensions(extensionCount);\n        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());\n\n        std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());\n\n        for (const auto &extension : availableExtensions) {\n            requiredExtensions.erase(extension.extensionName);\n        }\n\n        return requiredExtensions.empty();\n    }\n\n    QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device)\n    {\n        QueueFamilyIndices indices;\n\n        uint32_t queueFamilyCount = 0;\n        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);\n\n        std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);\n        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());\n\n        int i = 0;\n        for (const auto &queueFamily : queueFamilies) {\n            if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {\n                indices.graphicsFamily = i;\n            }\n\n            VkBool32 presentSupport = false;\n            vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);\n\n            if (queueFamily.queueCount > 0 && presentSupport) {\n                indices.presentFamily = i;\n            }\n\n            if (indices.isComplete()) {\n                break;\n            }\n\n            i++;\n        }\n\n        return indices;\n    }\n\n    std::vector<const char *> getRequiredExtensions()\n    {\n        uint32_t     glfwExtensionCount = 0;\n        const char **glfwExtensions;\n        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);\n\n        std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);\n\n        if (enableValidationLayers) {\n            extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);\n        }\n\n        return extensions;\n    }\n\n    bool checkValidationLayerSupport()\n    {\n        uint32_t layerCount;\n        vkEnumerateInstanceLayerProperties(&layerCount, nullptr);\n\n        std::vector<VkLayerProperties> availableLayers(layerCount);\n        vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());\n\n        for (const char *layerName : validationLayers) {\n            bool layerFound = false;\n\n            for (const auto &layerProperties : availableLayers) {\n                if (strcmp(layerName, layerProperties.layerName) == 0) {\n                    layerFound = true;\n                    break;\n                }\n            }\n\n            if (!layerFound) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    static std::vector<char> readFile(const std::string &filename)\n    {\n        char         *file_path = sdkFindFilePath(filename.c_str(), execution_path.c_str());\n        std::ifstream file(file_path, std::ios::ate | std::ios::binary);\n\n        if (!file.is_open()) {\n            throw std::runtime_error(\"failed to open file!\");\n        }\n\n        size_t            fileSize = (size_t)file.tellg();\n        std::vector<char> buffer(fileSize);\n\n        file.seekg(0);\n        file.read(buffer.data(), fileSize);\n\n        file.close();\n\n        return buffer;\n    }\n\n    static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,\n                                                        VkDebugUtilsMessageTypeFlagsEXT             messageType,\n                                                        const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,\n                                                        void                                       *pUserData)\n    {\n        std::cerr << \"validation layer: \" << pCallbackData->pMessage << std::endl;\n\n        return VK_FALSE;\n    }\n};\n\nint main(int argc, char **argv)\n{\n    execution_path             = argv[0];\n    std::string image_filename = \"teapot1024.ppm\";\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n        getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&image_filename);\n    }\n\n    vulkanImageCUDA app;\n\n    try {\n        // This app only works on ppm images\n        app.loadImageData(image_filename);\n        app.run();\n    }\n    catch (const std::exception &e) {\n        std::cerr << e.what() << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/vulkanImageCUDA/vulkanImageCUDA.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cstdio>\n#include <helper_cuda.h>\n#include <helper_timer.h>\n#include <vector>\n\nusing namespace std;\n\nconst char *sSampleName = \"P2P (Peer-to-Peer) GPU Bandwidth Latency Test\";\n\ntypedef enum {\n    P2P_WRITE = 0,\n    P2P_READ  = 1,\n} P2PDataTransfer;\n\ntypedef enum {\n    CE = 0,\n    SM = 1,\n} P2PEngine;\n\nP2PEngine p2p_mechanism = CE; // By default use Copy Engine\n\n// Macro for checking cuda errors following a cuda launch or api call\n#define cudaCheckError()                                                                     \\\n    {                                                                                        \\\n        cudaError_t e = cudaGetLastError();                                                  \\\n        if (e != cudaSuccess) {                                                              \\\n            printf(\"Cuda failure %s:%d: '%s'\\n\", __FILE__, __LINE__, cudaGetErrorString(e)); \\\n            exit(EXIT_FAILURE);                                                              \\\n        }                                                                                    \\\n    }\n__global__ void delay(volatile int *flag, unsigned long long timeout_clocks = 10000000)\n{\n    // Wait until the application notifies us that it has completed queuing up the\n    // experiment, or timeout and exit, allowing the application to make progress\n    long long int start_clock, sample_clock;\n    start_clock = clock64();\n\n    while (!*flag) {\n        sample_clock = clock64();\n\n        if (sample_clock - start_clock > timeout_clocks) {\n            break;\n        }\n    }\n}\n\n// This kernel is for demonstration purposes only, not a performant kernel for\n// p2p transfers.\n__global__ void copyp2p(int4 *__restrict__ dest, int4 const *__restrict__ src, size_t num_elems)\n{\n    size_t globalId = blockIdx.x * blockDim.x + threadIdx.x;\n    size_t gridSize = blockDim.x * gridDim.x;\n\n#pragma unroll(5)\n    for (size_t i = globalId; i < num_elems; i += gridSize) {\n        dest[i] = src[i];\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Print help screen\n///////////////////////////////////////////////////////////////////////////\nvoid printHelp(void)\n{\n    printf(\"Usage:  p2pBandwidthLatencyTest [OPTION]...\\n\");\n    printf(\"Tests bandwidth/latency of GPU pairs using P2P and without P2P\\n\");\n    printf(\"\\n\");\n\n    printf(\"Options:\\n\");\n    printf(\"--help\\t\\tDisplay this help menu\\n\");\n    printf(\"--p2p_read\\tUse P2P reads for data transfers between GPU pairs and show \"\n           \"corresponding results.\\n \\t\\tDefault used is P2P write operation.\\n\");\n    printf(\"--sm_copy                      Use SM intiated p2p transfers instead of Copy Engine\\n\");\n    printf(\"--numElems=<NUM_OF_INT_ELEMS>  Number of integer elements to be used in p2p copy.\\n\");\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Detect if cudaMemcpyPeerAsync will automatically fall back to\n// host-staged copies when P2P is disabled.\n//\n// We probe a single representative pair (device 0 -> device 1).\n// On a given system, confidential-computing (CC) and security\n// policies are uniform across GPUs, so if this pair is blocked\n// with cudaErrorNotSupported in P2P-off mode, it is reasonable\n// to assume all cross-GPU pairs behave the same.\n//\n// For a production application that must handle heterogeneous\n// environments, users may want to probe all device pairs.\n///////////////////////////////////////////////////////////////////////////\nbool detectFallback(int numGPUs)\n{\n    if (numGPUs <= 1)\n        return false;\n\n    cudaSetDevice(0);\n    int         *tmp0 = nullptr, *tmp1 = nullptr;\n    cudaStream_t s;\n    cudaStreamCreateWithFlags(&s, cudaStreamNonBlocking);\n\n    size_t testElems = 1;\n    cudaMalloc(&tmp0, testElems * sizeof(int));\n    cudaSetDevice(1);\n    cudaMalloc(&tmp1, testElems * sizeof(int));\n    cudaCheckError();\n\n    // Explicitly ensure P2P is disabled for this test\n    // (Clear any pre-existing P2P access if it happens to be enabled)\n    cudaSetDevice(0);\n    cudaDeviceDisablePeerAccess(1);\n    cudaGetLastError(); // Clear error if peer access was not enabled\n\n    cudaSetDevice(1);\n    cudaDeviceDisablePeerAccess(0);\n    cudaGetLastError(); // Clear error if peer access was not enabled\n\n    bool        needsFallback = false;\n    cudaError_t testErr       = cudaMemcpyPeerAsync(tmp1, 1, tmp0, 0, testElems * sizeof(int), s);\n\n    if (testErr == cudaErrorPeerAccessNotEnabled || testErr == cudaErrorNotSupported) {\n        needsFallback = true;\n        printf(\"Note: cudaMemcpyPeerAsync reported '%s' - will use host-mediated copy when P2P is disabled\\n\",\n               cudaGetErrorString(testErr));\n        cudaGetLastError();\n    }\n\n    cudaStreamSynchronize(s);\n    cudaFree(tmp0);\n    cudaFree(tmp1);\n    cudaStreamDestroy(s);\n    cudaCheckError();\n\n    return needsFallback;\n}\n\nvoid checkP2Paccess(int numGPUs)\n{\n    for (int i = 0; i < numGPUs; i++) {\n        cudaSetDevice(i);\n        cudaCheckError();\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access;\n            if (i != j) {\n                cudaDeviceCanAccessPeer(&access, i, j);\n                cudaCheckError();\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", i, access ? \"CAN\" : \"CANNOT\", j);\n            }\n        }\n    }\n    printf(\"\\n***NOTE: In case a device doesn't have P2P access to other one, it \"\n           \"falls back to normal memcopy procedure.\\nSo you can see lesser \"\n           \"Bandwidth (GB/s) and unstable Latency (us) in those cases.\\n\\n\");\n}\n\nvoid performP2PCopy(int         *dest,\n                    int          destDevice,\n                    int         *src,\n                    int          srcDevice,\n                    int          num_elems,\n                    int          repeat,\n                    bool         p2paccess,\n                    cudaStream_t streamToRun,\n                    bool         useFallback,\n                    int         *hostBuffer)\n{\n    int blockSize = 0;\n    int numBlocks = 0;\n\n    cudaOccupancyMaxPotentialBlockSize(&numBlocks, &blockSize, copyp2p);\n    cudaCheckError();\n\n    if (p2p_mechanism == SM && p2paccess) {\n        for (int r = 0; r < repeat; r++) {\n            copyp2p<<<numBlocks, blockSize, 0, streamToRun>>>((int4 *)dest, (int4 *)src, num_elems / 4);\n        }\n        cudaCheckError();\n    }\n    else if (useFallback && srcDevice != destDevice) {\n        // Use host-mediated copy for cross-GPU transfers when cudaMemcpyPeerAsync is not supported\n        for (int r = 0; r < repeat; r++) {\n            cudaMemcpyAsync(hostBuffer, src, sizeof(int) * num_elems, cudaMemcpyDeviceToHost, streamToRun);\n            cudaMemcpyAsync(dest, hostBuffer, sizeof(int) * num_elems, cudaMemcpyHostToDevice, streamToRun);\n        }\n        cudaCheckError();\n    }\n    else if (useFallback && srcDevice == destDevice) {\n        // Same device copy\n        for (int r = 0; r < repeat; r++) {\n            cudaMemcpyAsync(dest, src, sizeof(int) * num_elems, cudaMemcpyDeviceToDevice, streamToRun);\n        }\n        cudaCheckError();\n    }\n    else {\n        // Use cudaMemcpyPeerAsync\n        for (int r = 0; r < repeat; r++) {\n            cudaMemcpyPeerAsync(dest, destDevice, src, srcDevice, sizeof(int) * num_elems, streamToRun);\n        }\n        cudaCheckError();\n    }\n}\n\nvoid outputBandwidthMatrix(int numElems, int numGPUs, bool p2p, P2PDataTransfer p2p_method, bool needsFallback)\n{\n    int                  repeat = 5;\n    volatile int        *flag   = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs); // buffer for D2D, that is, intra-GPU copy\n    vector<cudaEvent_t>  start(numGPUs);\n    vector<cudaEvent_t>  stop(numGPUs);\n    vector<cudaStream_t> stream(numGPUs);\n\n    cudaHostAlloc((void **)&flag, sizeof(*flag), cudaHostAllocPortable);\n    cudaCheckError();\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaStreamCreateWithFlags(&stream[d], cudaStreamNonBlocking);\n        cudaMalloc(&buffers[d], numElems * sizeof(int));\n        cudaCheckError();\n        cudaMemset(buffers[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        cudaMalloc(&buffersD2D[d], numElems * sizeof(int));\n        cudaCheckError();\n        cudaMemset(buffersD2D[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        cudaEventCreate(&start[d]);\n        cudaCheckError();\n        cudaEventCreate(&stop[d]);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        cudaHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, cudaHostAllocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> bandwidthMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        cudaSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                cudaDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    cudaDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    cudaSetDevice(j);\n                    cudaCheckError();\n                    cudaDeviceEnablePeerAccess(i, 0);\n                    cudaCheckError();\n                    cudaSetDevice(i);\n                    cudaCheckError();\n                }\n            }\n\n            cudaStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            delay<<<1, 1, 0, stream[i]>>>(flag);\n            cudaCheckError();\n            cudaEventRecord(start[i], stream[i]);\n            cudaCheckError();\n\n            if (i == j) {\n                // Perform intra-GPU, D2D copies\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n            }\n            else {\n                if (p2p_method == P2P_WRITE) {\n                    performP2PCopy(\n                        buffers[j], j, buffers[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n                else {\n                    performP2PCopy(\n                        buffers[i], i, buffers[j], j, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n            }\n            cudaCheckError();\n\n            cudaEventRecord(stop[i], stream[i]);\n            cudaCheckError();\n\n            // Release the queued events\n            *flag = 1;\n            cudaStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            float time_ms;\n            cudaEventElapsedTime(&time_ms, start[i], stop[i]);\n            double time_s = time_ms / 1e3;\n\n            double gb = numElems * sizeof(int) * repeat / (double)1e9;\n            if (i == j) {\n                gb *= 2; // must count both the read and the write here\n            }\n            bandwidthMatrix[i * numGPUs + j] = gb / time_s;\n            if (p2p && access) {\n                cudaDeviceDisablePeerAccess(j);\n                cudaSetDevice(j);\n                cudaDeviceDisablePeerAccess(i);\n                cudaSetDevice(i);\n                cudaCheckError();\n            }\n        }\n    }\n\n    printf(\"   D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", bandwidthMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaFree(buffers[d]);\n        cudaFree(buffersD2D[d]);\n        cudaCheckError();\n        cudaEventDestroy(start[d]);\n        cudaCheckError();\n        cudaEventDestroy(stop[d]);\n        cudaCheckError();\n        cudaStreamDestroy(stream[d]);\n        cudaCheckError();\n    }\n\n    if (hostBuffer) {\n        cudaFreeHost(hostBuffer);\n        cudaCheckError();\n    }\n\n    cudaFreeHost((void *)flag);\n    cudaCheckError();\n}\n\nvoid outputBidirectionalBandwidthMatrix(int numElems, int numGPUs, bool p2p, bool needsFallback)\n{\n    int                  repeat = 5;\n    volatile int        *flag   = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs);\n    vector<cudaEvent_t>  start(numGPUs);\n    vector<cudaEvent_t>  stop(numGPUs);\n    vector<cudaStream_t> stream0(numGPUs);\n    vector<cudaStream_t> stream1(numGPUs);\n\n    cudaHostAlloc((void **)&flag, sizeof(*flag), cudaHostAllocPortable);\n    cudaCheckError();\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaMalloc(&buffers[d], numElems * sizeof(int));\n        cudaMemset(buffers[d], 0, numElems * sizeof(int));\n        cudaMalloc(&buffersD2D[d], numElems * sizeof(int));\n        cudaMemset(buffersD2D[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        cudaEventCreate(&start[d]);\n        cudaCheckError();\n        cudaEventCreate(&stop[d]);\n        cudaCheckError();\n        cudaStreamCreateWithFlags(&stream0[d], cudaStreamNonBlocking);\n        cudaCheckError();\n        cudaStreamCreateWithFlags(&stream1[d], cudaStreamNonBlocking);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        cudaHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, cudaHostAllocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> bandwidthMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        cudaSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                cudaDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    cudaSetDevice(i);\n                    cudaDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    cudaSetDevice(j);\n                    cudaDeviceEnablePeerAccess(i, 0);\n                    cudaCheckError();\n                }\n            }\n\n            cudaSetDevice(i);\n            cudaStreamSynchronize(stream0[i]);\n            cudaStreamSynchronize(stream1[j]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            cudaSetDevice(i);\n            // No need to block stream1 since it'll be blocked on stream0's event\n            delay<<<1, 1, 0, stream0[i]>>>(flag);\n            cudaCheckError();\n\n            // Force stream1 not to start until stream0 does, in order to ensure\n            // the events on stream0 fully encompass the time needed for all\n            // operations\n            cudaEventRecord(start[i], stream0[i]);\n            cudaStreamWaitEvent(stream1[j], start[i], 0);\n\n            if (i == j) {\n                // For intra-GPU perform 2 memcopies buffersD2D <-> buffers\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream0[i], useFallback, hostBuffer);\n                performP2PCopy(\n                    buffersD2D[i], i, buffers[i], i, numElems, repeat, access, stream1[i], useFallback, hostBuffer);\n            }\n            else {\n                if (access && p2p_mechanism == SM) {\n                    cudaSetDevice(j);\n                }\n                performP2PCopy(\n                    buffers[i], i, buffers[j], j, numElems, repeat, access, stream1[j], useFallback, hostBuffer);\n                if (access && p2p_mechanism == SM) {\n                    cudaSetDevice(i);\n                }\n                performP2PCopy(\n                    buffers[j], j, buffers[i], i, numElems, repeat, access, stream0[i], useFallback, hostBuffer);\n            }\n\n            // Notify stream0 that stream1 is complete and record the time of\n            // the total transaction\n            cudaEventRecord(stop[j], stream1[j]);\n            cudaStreamWaitEvent(stream0[i], stop[j], 0);\n            cudaEventRecord(stop[i], stream0[i]);\n\n            // Release the queued operations\n            *flag = 1;\n            cudaStreamSynchronize(stream0[i]);\n            cudaStreamSynchronize(stream1[j]);\n            cudaCheckError();\n\n            float time_ms;\n            cudaEventElapsedTime(&time_ms, start[i], stop[i]);\n            double time_s = time_ms / 1e3;\n\n            double gb = 2.0 * numElems * sizeof(int) * repeat / (double)1e9;\n            if (i == j) {\n                gb *= 2; // must count both the read and the write here\n            }\n            bandwidthMatrix[i * numGPUs + j] = gb / time_s;\n            if (p2p && access) {\n                cudaSetDevice(i);\n                cudaDeviceDisablePeerAccess(j);\n                cudaSetDevice(j);\n                cudaDeviceDisablePeerAccess(i);\n            }\n        }\n    }\n\n    printf(\"   D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", bandwidthMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaFree(buffers[d]);\n        cudaFree(buffersD2D[d]);\n        cudaCheckError();\n        cudaEventDestroy(start[d]);\n        cudaCheckError();\n        cudaEventDestroy(stop[d]);\n        cudaCheckError();\n        cudaStreamDestroy(stream0[d]);\n        cudaCheckError();\n        cudaStreamDestroy(stream1[d]);\n        cudaCheckError();\n    }\n\n    if (hostBuffer) {\n        cudaFreeHost(hostBuffer);\n        cudaCheckError();\n    }\n\n    cudaFreeHost((void *)flag);\n    cudaCheckError();\n}\n\nvoid outputLatencyMatrix(int numGPUs, bool p2p, P2PDataTransfer p2p_method, bool needsFallback)\n{\n    int                  repeat    = 100;\n    int                  numElems  = 4; // perform 1-int4 transfer.\n    volatile int        *flag      = NULL;\n    StopWatchInterface  *stopWatch = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs); // buffer for D2D, that is, intra-GPU copy\n    vector<cudaStream_t> stream(numGPUs);\n    vector<cudaEvent_t>  start(numGPUs);\n    vector<cudaEvent_t>  stop(numGPUs);\n\n    cudaHostAlloc((void **)&flag, sizeof(*flag), cudaHostAllocPortable);\n    cudaCheckError();\n\n    if (!sdkCreateTimer(&stopWatch)) {\n        printf(\"Failed to create stop watch\\n\");\n        exit(EXIT_FAILURE);\n    }\n    sdkStartTimer(&stopWatch);\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaStreamCreateWithFlags(&stream[d], cudaStreamNonBlocking);\n        cudaMalloc(&buffers[d], sizeof(int) * numElems);\n        cudaMemset(buffers[d], 0, sizeof(int) * numElems);\n        cudaMalloc(&buffersD2D[d], sizeof(int) * numElems);\n        cudaMemset(buffersD2D[d], 0, sizeof(int) * numElems);\n        cudaCheckError();\n        cudaEventCreate(&start[d]);\n        cudaCheckError();\n        cudaEventCreate(&stop[d]);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        cudaHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, cudaHostAllocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> gpuLatencyMatrix(numGPUs * numGPUs);\n    vector<double> cpuLatencyMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        cudaSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                cudaDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    cudaDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    cudaSetDevice(j);\n                    cudaDeviceEnablePeerAccess(i, 0);\n                    cudaSetDevice(i);\n                    cudaCheckError();\n                }\n            }\n            cudaStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            delay<<<1, 1, 0, stream[i]>>>(flag);\n            cudaCheckError();\n            cudaEventRecord(start[i], stream[i]);\n\n            sdkResetTimer(&stopWatch);\n            if (i == j) {\n                // Perform intra-GPU, D2D copies\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n            }\n            else {\n                if (p2p_method == P2P_WRITE) {\n                    performP2PCopy(\n                        buffers[j], j, buffers[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n                else {\n                    performP2PCopy(\n                        buffers[i], i, buffers[j], j, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n            }\n            float cpu_time_ms = sdkGetTimerValue(&stopWatch);\n\n            cudaEventRecord(stop[i], stream[i]);\n            // Now that the work has been queued up, release the stream\n            *flag = 1;\n            cudaStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            float gpu_time_ms;\n            cudaEventElapsedTime(&gpu_time_ms, start[i], stop[i]);\n\n            gpuLatencyMatrix[i * numGPUs + j] = gpu_time_ms * 1e3 / repeat;\n            cpuLatencyMatrix[i * numGPUs + j] = cpu_time_ms * 1e3 / repeat;\n            if (p2p && access) {\n                cudaDeviceDisablePeerAccess(j);\n                cudaSetDevice(j);\n                cudaDeviceDisablePeerAccess(i);\n                cudaSetDevice(i);\n                cudaCheckError();\n            }\n        }\n    }\n\n    printf(\"   GPU\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", gpuLatencyMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    printf(\"\\n   CPU\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", cpuLatencyMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        cudaSetDevice(d);\n        cudaFree(buffers[d]);\n        cudaFree(buffersD2D[d]);\n        cudaCheckError();\n        cudaEventDestroy(start[d]);\n        cudaCheckError();\n        cudaEventDestroy(stop[d]);\n        cudaCheckError();\n        cudaStreamDestroy(stream[d]);\n        cudaCheckError();\n    }\n\n    sdkDeleteTimer(&stopWatch);\n\n    if (hostBuffer) {\n        cudaFreeHost(hostBuffer);\n        cudaCheckError();\n    }\n\n    cudaFreeHost((void *)flag);\n    cudaCheckError();\n}\n\nint main(int argc, char **argv)\n{\n    int             numGPUs, numElems = 40000000;\n    P2PDataTransfer p2p_method = P2P_WRITE;\n\n    cudaGetDeviceCount(&numGPUs);\n    cudaCheckError();\n\n    // process command line args\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printHelp();\n        return 0;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"p2p_read\")) {\n        p2p_method = P2P_READ;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"sm_copy\")) {\n        p2p_mechanism = SM;\n    }\n\n    // number of elements of int to be used in copy.\n    if (checkCmdLineFlag(argc, (const char **)argv, \"numElems\")) {\n        numElems = getCmdLineArgumentInt(argc, (const char **)argv, \"numElems\");\n    }\n\n    printf(\"[%s]\\n\", sSampleName);\n\n    // output devices\n    for (int i = 0; i < numGPUs; i++) {\n        cudaDeviceProp prop;\n        cudaGetDeviceProperties(&prop, i);\n        cudaCheckError();\n        printf(\"Device: %d, %s, pciBusID: %x, pciDeviceID: %x, pciDomainID:%x\\n\",\n               i,\n               prop.name,\n               prop.pciBusID,\n               prop.pciDeviceID,\n               prop.pciDomainID);\n    }\n\n    checkP2Paccess(numGPUs);\n\n    // Environment detection: One-time check if cudaMemcpyPeerAsync is supported when P2P is disabled\n    bool needsFallback = detectFallback(numGPUs);\n\n    // Check peer-to-peer connectivity\n    printf(\"P2P Connectivity Matrix\\n\");\n    printf(\"     D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d\", j);\n    }\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d\\t\", i);\n        for (int j = 0; j < numGPUs; j++) {\n            if (i != j) {\n                int access;\n                cudaDeviceCanAccessPeer(&access, i, j);\n                cudaCheckError();\n                printf(\"%6d\", (access) ? 1 : 0);\n            }\n            else {\n                printf(\"%6d\", 1);\n            }\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Unidirectional P2P=Disabled Bandwidth Matrix (GB/s)\\n\");\n    outputBandwidthMatrix(numElems, numGPUs, false, P2P_WRITE, needsFallback);\n    printf(\"Unidirectional P2P=Enabled Bandwidth (P2P Writes) Matrix (GB/s)\\n\");\n    outputBandwidthMatrix(numElems, numGPUs, true, P2P_WRITE, needsFallback);\n    if (p2p_method == P2P_READ) {\n        printf(\"Unidirectional P2P=Enabled Bandwidth (P2P Reads) Matrix (GB/s)\\n\");\n        outputBandwidthMatrix(numElems, numGPUs, true, p2p_method, needsFallback);\n    }\n    printf(\"Bidirectional P2P=Disabled Bandwidth Matrix (GB/s)\\n\");\n    outputBidirectionalBandwidthMatrix(numElems, numGPUs, false, needsFallback);\n    printf(\"Bidirectional P2P=Enabled Bandwidth Matrix (GB/s)\\n\");\n    outputBidirectionalBandwidthMatrix(numElems, numGPUs, true, needsFallback);\n\n    printf(\"P2P=Disabled Latency Matrix (us)\\n\");\n    outputLatencyMatrix(numGPUs, false, P2P_WRITE, needsFallback);\n    printf(\"P2P=Enabled Latency (P2P Writes) Matrix (us)\\n\");\n    outputLatencyMatrix(numGPUs, true, P2P_WRITE, needsFallback);\n    if (p2p_method == P2P_READ) {\n        printf(\"P2P=Enabled Latency (P2P Reads) Matrix (us)\\n\");\n        outputLatencyMatrix(numGPUs, true, p2p_method, needsFallback);\n    }\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cstdio>\n#include <hip/hip_runtime_api.h>\n#include <helper_timer.h>\n#include <vector>\n\nusing namespace std;\n\nconst char *sSampleName = \"P2P (Peer-to-Peer) GPU Bandwidth Latency Test\";\n\ntypedef enum {\n    P2P_WRITE = 0,\n    P2P_READ  = 1,\n} P2PDataTransfer;\n\ntypedef enum {\n    CE = 0,\n    SM = 1,\n} P2PEngine;\n\nP2PEngine p2p_mechanism = CE; // By default use Copy Engine\n\n// Macro for checking cuda errors following a cuda launch or api call\n#define cudaCheckError()                                                                     \\\n    {                                                                                        \\\n        hipError_t e = hipGetLastError();                                                  \\\n        if (e != hipSuccess) {                                                              \\\n            printf(\"Cuda failure %s:%d: '%s'\\n\", __FILE__, __LINE__, hipGetErrorString(e)); \\\n            exit(EXIT_FAILURE);                                                              \\\n        }                                                                                    \\\n    }\n__global__ void delay(volatile int *flag, unsigned long long timeout_clocks = 10000000)\n{\n    // Wait until the application notifies us that it has completed queuing up the\n    // experiment, or timeout and exit, allowing the application to make progress\n    long long int start_clock, sample_clock;\n    start_clock = clock64();\n\n    while (!*flag) {\n        sample_clock = clock64();\n\n        if (sample_clock - start_clock > timeout_clocks) {\n            break;\n        }\n    }\n}\n\n// This kernel is for demonstration purposes only, not a performant kernel for\n// p2p transfers.\n__global__ void copyp2p(int4 *__restrict__ dest, int4 const *__restrict__ src, size_t num_elems)\n{\n    size_t globalId = blockIdx.x * blockDim.x + threadIdx.x;\n    size_t gridSize = blockDim.x * gridDim.x;\n\n#pragma unroll(5)\n    for (size_t i = globalId; i < num_elems; i += gridSize) {\n        dest[i] = src[i];\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Print help screen\n///////////////////////////////////////////////////////////////////////////\nvoid printHelp(void)\n{\n    printf(\"Usage:  p2pBandwidthLatencyTest [OPTION]...\\n\");\n    printf(\"Tests bandwidth/latency of GPU pairs using P2P and without P2P\\n\");\n    printf(\"\\n\");\n\n    printf(\"Options:\\n\");\n    printf(\"--help\\t\\tDisplay this help menu\\n\");\n    printf(\"--p2p_read\\tUse P2P reads for data transfers between GPU pairs and show \"\n           \"corresponding results.\\n \\t\\tDefault used is P2P write operation.\\n\");\n    printf(\"--sm_copy                      Use SM intiated p2p transfers instead of Copy Engine\\n\");\n    printf(\"--numElems=<NUM_OF_INT_ELEMS>  Number of integer elements to be used in p2p copy.\\n\");\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Detect if cudaMemcpyPeerAsync will automatically fall back to\n// host-staged copies when P2P is disabled.\n//\n// We probe a single representative pair (device 0 -> device 1).\n// On a given system, confidential-computing (CC) and security\n// policies are uniform across GPUs, so if this pair is blocked\n// with cudaErrorNotSupported in P2P-off mode, it is reasonable\n// to assume all cross-GPU pairs behave the same.\n//\n// For a production application that must handle heterogeneous\n// environments, users may want to probe all device pairs.\n///////////////////////////////////////////////////////////////////////////\nbool detectFallback(int numGPUs)\n{\n    if (numGPUs <= 1)\n        return false;\n\n    hipSetDevice(0);\n    int         *tmp0 = nullptr, *tmp1 = nullptr;\n    hipStream_t s;\n    hipStreamCreateWithFlags(&s, hipStreamNonBlocking);\n\n    size_t testElems = 1;\n    hipMalloc(&tmp0, testElems * sizeof(int));\n    hipSetDevice(1);\n    hipMalloc(&tmp1, testElems * sizeof(int));\n    cudaCheckError();\n\n    // Explicitly ensure P2P is disabled for this test\n    // (Clear any pre-existing P2P access if it happens to be enabled)\n    hipSetDevice(0);\n    hipDeviceDisablePeerAccess(1);\n    hipGetLastError(); // Clear error if peer access was not enabled\n\n    hipSetDevice(1);\n    hipDeviceDisablePeerAccess(0);\n    hipGetLastError(); // Clear error if peer access was not enabled\n\n    bool        needsFallback = false;\n    hipError_t testErr       = hipMemcpyPeerAsync(tmp1, 1, tmp0, 0, testElems * sizeof(int), s);\n\n    if (testErr == hipErrorPeerAccessNotEnabled || testErr == hipErrorNotSupported) {\n        needsFallback = true;\n        printf(\"Note: hipMemcpyPeerAsync reported '%s' - will use host-mediated copy when P2P is disabled\\n\",\n               hipGetErrorString(testErr));\n        hipGetLastError();\n    }\n\n    hipStreamSynchronize(s);\n    hipFree(tmp0);\n    hipFree(tmp1);\n    hipStreamDestroy(s);\n    cudaCheckError();\n\n    return needsFallback;\n}\n\nvoid checkP2Paccess(int numGPUs)\n{\n    for (int i = 0; i < numGPUs; i++) {\n        hipSetDevice(i);\n        cudaCheckError();\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access;\n            if (i != j) {\n                hipDeviceCanAccessPeer(&access, i, j);\n                cudaCheckError();\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", i, access ? \"CAN\" : \"CANNOT\", j);\n            }\n        }\n    }\n    printf(\"\\n***NOTE: In case a device doesn't have P2P access to other one, it \"\n           \"falls back to normal memcopy procedure.\\nSo you can see lesser \"\n           \"Bandwidth (GB/s) and unstable Latency (us) in those cases.\\n\\n\");\n}\n\nvoid performP2PCopy(int         *dest,\n                    int          destDevice,\n                    int         *src,\n                    int          srcDevice,\n                    int          num_elems,\n                    int          repeat,\n                    bool         p2paccess,\n                    hipStream_t streamToRun,\n                    bool         useFallback,\n                    int         *hostBuffer)\n{\n    int blockSize = 0;\n    int numBlocks = 0;\n\n    hipOccupancyMaxPotentialBlockSize(&numBlocks, &blockSize, copyp2p);\n    cudaCheckError();\n\n    if (p2p_mechanism == SM && p2paccess) {\n        for (int r = 0; r < repeat; r++) {\n            copyp2p<<<numBlocks, blockSize, 0, streamToRun>>>((int4 *)dest, (int4 *)src, num_elems / 4);\n        }\n        cudaCheckError();\n    }\n    else if (useFallback && srcDevice != destDevice) {\n        // Use host-mediated copy for cross-GPU transfers when cudaMemcpyPeerAsync is not supported\n        for (int r = 0; r < repeat; r++) {\n            hipMemcpyAsync(hostBuffer, src, sizeof(int) * num_elems, hipMemcpyDeviceToHost, streamToRun);\n            hipMemcpyAsync(dest, hostBuffer, sizeof(int) * num_elems, hipMemcpyHostToDevice, streamToRun);\n        }\n        cudaCheckError();\n    }\n    else if (useFallback && srcDevice == destDevice) {\n        // Same device copy\n        for (int r = 0; r < repeat; r++) {\n            hipMemcpyAsync(dest, src, sizeof(int) * num_elems, hipMemcpyDeviceToDevice, streamToRun);\n        }\n        cudaCheckError();\n    }\n    else {\n        // Use cudaMemcpyPeerAsync\n        for (int r = 0; r < repeat; r++) {\n            hipMemcpyPeerAsync(dest, destDevice, src, srcDevice, sizeof(int) * num_elems, streamToRun);\n        }\n        cudaCheckError();\n    }\n}\n\nvoid outputBandwidthMatrix(int numElems, int numGPUs, bool p2p, P2PDataTransfer p2p_method, bool needsFallback)\n{\n    int                  repeat = 5;\n    volatile int        *flag   = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs); // buffer for D2D, that is, intra-GPU copy\n    vector<hipEvent_t>  start(numGPUs);\n    vector<hipEvent_t>  stop(numGPUs);\n    vector<hipStream_t> stream(numGPUs);\n\n    hipHostAlloc((void **)&flag, sizeof(*flag), hipHostMallocPortable);\n    cudaCheckError();\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipStreamCreateWithFlags(&stream[d], hipStreamNonBlocking);\n        hipMalloc(&buffers[d], numElems * sizeof(int));\n        cudaCheckError();\n        hipMemset(buffers[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        hipMalloc(&buffersD2D[d], numElems * sizeof(int));\n        cudaCheckError();\n        hipMemset(buffersD2D[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        hipEventCreate(&start[d]);\n        cudaCheckError();\n        hipEventCreate(&stop[d]);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        hipHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, hipHostMallocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> bandwidthMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        hipSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                hipDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    hipDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    hipSetDevice(j);\n                    cudaCheckError();\n                    hipDeviceEnablePeerAccess(i, 0);\n                    cudaCheckError();\n                    hipSetDevice(i);\n                    cudaCheckError();\n                }\n            }\n\n            hipStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            delay<<<1, 1, 0, stream[i]>>>(flag);\n            cudaCheckError();\n            hipEventRecord(start[i], stream[i]);\n            cudaCheckError();\n\n            if (i == j) {\n                // Perform intra-GPU, D2D copies\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n            }\n            else {\n                if (p2p_method == P2P_WRITE) {\n                    performP2PCopy(\n                        buffers[j], j, buffers[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n                else {\n                    performP2PCopy(\n                        buffers[i], i, buffers[j], j, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n            }\n            cudaCheckError();\n\n            hipEventRecord(stop[i], stream[i]);\n            cudaCheckError();\n\n            // Release the queued events\n            *flag = 1;\n            hipStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            float time_ms;\n            hipEventElapsedTime(&time_ms, start[i], stop[i]);\n            double time_s = time_ms / 1e3;\n\n            double gb = numElems * sizeof(int) * repeat / (double)1e9;\n            if (i == j) {\n                gb *= 2; // must count both the read and the write here\n            }\n            bandwidthMatrix[i * numGPUs + j] = gb / time_s;\n            if (p2p && access) {\n                hipDeviceDisablePeerAccess(j);\n                hipSetDevice(j);\n                hipDeviceDisablePeerAccess(i);\n                hipSetDevice(i);\n                cudaCheckError();\n            }\n        }\n    }\n\n    printf(\"   D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", bandwidthMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipFree(buffers[d]);\n        hipFree(buffersD2D[d]);\n        cudaCheckError();\n        hipEventDestroy(start[d]);\n        cudaCheckError();\n        hipEventDestroy(stop[d]);\n        cudaCheckError();\n        hipStreamDestroy(stream[d]);\n        cudaCheckError();\n    }\n\n    if (hostBuffer) {\n        hipHostFree(hostBuffer);\n        cudaCheckError();\n    }\n\n    hipHostFree((void *)flag);\n    cudaCheckError();\n}\n\nvoid outputBidirectionalBandwidthMatrix(int numElems, int numGPUs, bool p2p, bool needsFallback)\n{\n    int                  repeat = 5;\n    volatile int        *flag   = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs);\n    vector<hipEvent_t>  start(numGPUs);\n    vector<hipEvent_t>  stop(numGPUs);\n    vector<hipStream_t> stream0(numGPUs);\n    vector<hipStream_t> stream1(numGPUs);\n\n    hipHostAlloc((void **)&flag, sizeof(*flag), hipHostMallocPortable);\n    cudaCheckError();\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipMalloc(&buffers[d], numElems * sizeof(int));\n        hipMemset(buffers[d], 0, numElems * sizeof(int));\n        hipMalloc(&buffersD2D[d], numElems * sizeof(int));\n        hipMemset(buffersD2D[d], 0, numElems * sizeof(int));\n        cudaCheckError();\n        hipEventCreate(&start[d]);\n        cudaCheckError();\n        hipEventCreate(&stop[d]);\n        cudaCheckError();\n        hipStreamCreateWithFlags(&stream0[d], hipStreamNonBlocking);\n        cudaCheckError();\n        hipStreamCreateWithFlags(&stream1[d], hipStreamNonBlocking);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        hipHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, hipHostMallocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> bandwidthMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        hipSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                hipDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    hipSetDevice(i);\n                    hipDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    hipSetDevice(j);\n                    hipDeviceEnablePeerAccess(i, 0);\n                    cudaCheckError();\n                }\n            }\n\n            hipSetDevice(i);\n            hipStreamSynchronize(stream0[i]);\n            hipStreamSynchronize(stream1[j]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            hipSetDevice(i);\n            // No need to block stream1 since it'll be blocked on stream0's event\n            delay<<<1, 1, 0, stream0[i]>>>(flag);\n            cudaCheckError();\n\n            // Force stream1 not to start until stream0 does, in order to ensure\n            // the events on stream0 fully encompass the time needed for all\n            // operations\n            hipEventRecord(start[i], stream0[i]);\n            hipStreamWaitEvent(stream1[j], start[i], 0);\n\n            if (i == j) {\n                // For intra-GPU perform 2 memcopies buffersD2D <-> buffers\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream0[i], useFallback, hostBuffer);\n                performP2PCopy(\n                    buffersD2D[i], i, buffers[i], i, numElems, repeat, access, stream1[i], useFallback, hostBuffer);\n            }\n            else {\n                if (access && p2p_mechanism == SM) {\n                    hipSetDevice(j);\n                }\n                performP2PCopy(\n                    buffers[i], i, buffers[j], j, numElems, repeat, access, stream1[j], useFallback, hostBuffer);\n                if (access && p2p_mechanism == SM) {\n                    hipSetDevice(i);\n                }\n                performP2PCopy(\n                    buffers[j], j, buffers[i], i, numElems, repeat, access, stream0[i], useFallback, hostBuffer);\n            }\n\n            // Notify stream0 that stream1 is complete and record the time of\n            // the total transaction\n            hipEventRecord(stop[j], stream1[j]);\n            hipStreamWaitEvent(stream0[i], stop[j], 0);\n            hipEventRecord(stop[i], stream0[i]);\n\n            // Release the queued operations\n            *flag = 1;\n            hipStreamSynchronize(stream0[i]);\n            hipStreamSynchronize(stream1[j]);\n            cudaCheckError();\n\n            float time_ms;\n            hipEventElapsedTime(&time_ms, start[i], stop[i]);\n            double time_s = time_ms / 1e3;\n\n            double gb = 2.0 * numElems * sizeof(int) * repeat / (double)1e9;\n            if (i == j) {\n                gb *= 2; // must count both the read and the write here\n            }\n            bandwidthMatrix[i * numGPUs + j] = gb / time_s;\n            if (p2p && access) {\n                hipSetDevice(i);\n                hipDeviceDisablePeerAccess(j);\n                hipSetDevice(j);\n                hipDeviceDisablePeerAccess(i);\n            }\n        }\n    }\n\n    printf(\"   D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", bandwidthMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipFree(buffers[d]);\n        hipFree(buffersD2D[d]);\n        cudaCheckError();\n        hipEventDestroy(start[d]);\n        cudaCheckError();\n        hipEventDestroy(stop[d]);\n        cudaCheckError();\n        hipStreamDestroy(stream0[d]);\n        cudaCheckError();\n        hipStreamDestroy(stream1[d]);\n        cudaCheckError();\n    }\n\n    if (hostBuffer) {\n        hipHostFree(hostBuffer);\n        cudaCheckError();\n    }\n\n    hipHostFree((void *)flag);\n    cudaCheckError();\n}\n\nvoid outputLatencyMatrix(int numGPUs, bool p2p, P2PDataTransfer p2p_method, bool needsFallback)\n{\n    int                  repeat    = 100;\n    int                  numElems  = 4; // perform 1-int4 transfer.\n    volatile int        *flag      = NULL;\n    StopWatchInterface  *stopWatch = NULL;\n    vector<int *>        buffers(numGPUs);\n    vector<int *>        buffersD2D(numGPUs); // buffer for D2D, that is, intra-GPU copy\n    vector<hipStream_t> stream(numGPUs);\n    vector<hipEvent_t>  start(numGPUs);\n    vector<hipEvent_t>  stop(numGPUs);\n\n    hipHostAlloc((void **)&flag, sizeof(*flag), hipHostMallocPortable);\n    cudaCheckError();\n\n    if (!sdkCreateTimer(&stopWatch)) {\n        printf(\"Failed to create stop watch\\n\");\n        exit(EXIT_FAILURE);\n    }\n    sdkStartTimer(&stopWatch);\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipStreamCreateWithFlags(&stream[d], hipStreamNonBlocking);\n        hipMalloc(&buffers[d], sizeof(int) * numElems);\n        hipMemset(buffers[d], 0, sizeof(int) * numElems);\n        hipMalloc(&buffersD2D[d], sizeof(int) * numElems);\n        hipMemset(buffersD2D[d], 0, sizeof(int) * numElems);\n        cudaCheckError();\n        hipEventCreate(&start[d]);\n        cudaCheckError();\n        hipEventCreate(&stop[d]);\n        cudaCheckError();\n    }\n\n    // Decide if fallback is needed based on global detection result and current test scenario\n    bool useFallback = (!p2p && needsFallback);\n    int *hostBuffer  = nullptr;\n\n    if (useFallback) {\n        // Allocate hostBuffer for this function's numElems\n        hipHostAlloc((void **)&hostBuffer, sizeof(int) * numElems, hipHostMallocDefault);\n        cudaCheckError();\n    }\n\n    vector<double> gpuLatencyMatrix(numGPUs * numGPUs);\n    vector<double> cpuLatencyMatrix(numGPUs * numGPUs);\n\n    for (int i = 0; i < numGPUs; i++) {\n        hipSetDevice(i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            int access = 0;\n            if (p2p) {\n                hipDeviceCanAccessPeer(&access, i, j);\n                if (access) {\n                    hipDeviceEnablePeerAccess(j, 0);\n                    cudaCheckError();\n                    hipSetDevice(j);\n                    hipDeviceEnablePeerAccess(i, 0);\n                    hipSetDevice(i);\n                    cudaCheckError();\n                }\n            }\n            hipStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            // Block the stream until all the work is queued up\n            // DANGER! - cudaMemcpy*Async may infinitely block waiting for\n            // room to push the operation, so keep the number of repeatitions\n            // relatively low.  Higher repeatitions will cause the delay kernel\n            // to timeout and lead to unstable results.\n            *flag = 0;\n            delay<<<1, 1, 0, stream[i]>>>(flag);\n            cudaCheckError();\n            hipEventRecord(start[i], stream[i]);\n\n            sdkResetTimer(&stopWatch);\n            if (i == j) {\n                // Perform intra-GPU, D2D copies\n                performP2PCopy(\n                    buffers[i], i, buffersD2D[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n            }\n            else {\n                if (p2p_method == P2P_WRITE) {\n                    performP2PCopy(\n                        buffers[j], j, buffers[i], i, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n                else {\n                    performP2PCopy(\n                        buffers[i], i, buffers[j], j, numElems, repeat, access, stream[i], useFallback, hostBuffer);\n                }\n            }\n            float cpu_time_ms = sdkGetTimerValue(&stopWatch);\n\n            hipEventRecord(stop[i], stream[i]);\n            // Now that the work has been queued up, release the stream\n            *flag = 1;\n            hipStreamSynchronize(stream[i]);\n            cudaCheckError();\n\n            float gpu_time_ms;\n            hipEventElapsedTime(&gpu_time_ms, start[i], stop[i]);\n\n            gpuLatencyMatrix[i * numGPUs + j] = gpu_time_ms * 1e3 / repeat;\n            cpuLatencyMatrix[i * numGPUs + j] = cpu_time_ms * 1e3 / repeat;\n            if (p2p && access) {\n                hipDeviceDisablePeerAccess(j);\n                hipSetDevice(j);\n                hipDeviceDisablePeerAccess(i);\n                hipSetDevice(i);\n                cudaCheckError();\n            }\n        }\n    }\n\n    printf(\"   GPU\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", gpuLatencyMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    printf(\"\\n   CPU\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d \", j);\n    }\n\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d \", i);\n\n        for (int j = 0; j < numGPUs; j++) {\n            printf(\"%6.02f \", cpuLatencyMatrix[i * numGPUs + j]);\n        }\n\n        printf(\"\\n\");\n    }\n\n    for (int d = 0; d < numGPUs; d++) {\n        hipSetDevice(d);\n        hipFree(buffers[d]);\n        hipFree(buffersD2D[d]);\n        cudaCheckError();\n        hipEventDestroy(start[d]);\n        cudaCheckError();\n        hipEventDestroy(stop[d]);\n        cudaCheckError();\n        hipStreamDestroy(stream[d]);\n        cudaCheckError();\n    }\n\n    sdkDeleteTimer(&stopWatch);\n\n    if (hostBuffer) {\n        hipHostFree(hostBuffer);\n        cudaCheckError();\n    }\n\n    hipHostFree((void *)flag);\n    cudaCheckError();\n}\n\nint main(int argc, char **argv)\n{\n    int             numGPUs, numElems = 40000000;\n    P2PDataTransfer p2p_method = P2P_WRITE;\n\n    hipGetDeviceCount(&numGPUs);\n    cudaCheckError();\n\n    // process command line args\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printHelp();\n        return 0;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"p2p_read\")) {\n        p2p_method = P2P_READ;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"sm_copy\")) {\n        p2p_mechanism = SM;\n    }\n\n    // number of elements of int to be used in copy.\n    if (checkCmdLineFlag(argc, (const char **)argv, \"numElems\")) {\n        numElems = getCmdLineArgumentInt(argc, (const char **)argv, \"numElems\");\n    }\n\n    printf(\"[%s]\\n\", sSampleName);\n\n    // output devices\n    for (int i = 0; i < numGPUs; i++) {\n        hipDeviceProp_t prop;\n        hipGetDeviceProperties(&prop, i);\n        cudaCheckError();\n        printf(\"Device: %d, %s, pciBusID: %x, pciDeviceID: %x, pciDomainID:%x\\n\",\n               i,\n               prop.name,\n               prop.pciBusID,\n               prop.pciDeviceID,\n               prop.pciDomainID);\n    }\n\n    checkP2Paccess(numGPUs);\n\n    // Environment detection: One-time check if cudaMemcpyPeerAsync is supported when P2P is disabled\n    bool needsFallback = detectFallback(numGPUs);\n\n    // Check peer-to-peer connectivity\n    printf(\"P2P Connectivity Matrix\\n\");\n    printf(\"     D\\\\D\");\n\n    for (int j = 0; j < numGPUs; j++) {\n        printf(\"%6d\", j);\n    }\n    printf(\"\\n\");\n\n    for (int i = 0; i < numGPUs; i++) {\n        printf(\"%6d\\t\", i);\n        for (int j = 0; j < numGPUs; j++) {\n            if (i != j) {\n                int access;\n                hipDeviceCanAccessPeer(&access, i, j);\n                cudaCheckError();\n                printf(\"%6d\", (access) ? 1 : 0);\n            }\n            else {\n                printf(\"%6d\", 1);\n            }\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"Unidirectional P2P=Disabled Bandwidth Matrix (GB/s)\\n\");\n    outputBandwidthMatrix(numElems, numGPUs, false, P2P_WRITE, needsFallback);\n    printf(\"Unidirectional P2P=Enabled Bandwidth (P2P Writes) Matrix (GB/s)\\n\");\n    outputBandwidthMatrix(numElems, numGPUs, true, P2P_WRITE, needsFallback);\n    if (p2p_method == P2P_READ) {\n        printf(\"Unidirectional P2P=Enabled Bandwidth (P2P Reads) Matrix (GB/s)\\n\");\n        outputBandwidthMatrix(numElems, numGPUs, true, p2p_method, needsFallback);\n    }\n    printf(\"Bidirectional P2P=Disabled Bandwidth Matrix (GB/s)\\n\");\n    outputBidirectionalBandwidthMatrix(numElems, numGPUs, false, needsFallback);\n    printf(\"Bidirectional P2P=Enabled Bandwidth Matrix (GB/s)\\n\");\n    outputBidirectionalBandwidthMatrix(numElems, numGPUs, true, needsFallback);\n\n    printf(\"P2P=Disabled Latency Matrix (us)\\n\");\n    outputLatencyMatrix(numGPUs, false, P2P_WRITE, needsFallback);\n    printf(\"P2P=Enabled Latency (P2P Writes) Matrix (us)\\n\");\n    outputLatencyMatrix(numGPUs, true, P2P_WRITE, needsFallback);\n    if (p2p_method == P2P_READ) {\n        printf(\"P2P=Enabled Latency (P2P Reads) Matrix (us)\\n\");\n        outputLatencyMatrix(numGPUs, true, p2p_method, needsFallback);\n    }\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/p2pBandwidthLatencyTest/p2pBandwidthLatencyTest.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types\n////////////////////////////////////////////////////////////////////////////////\n#include <cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n#include <curand_kernel.h>\n#include <helper_cuda.h>\n\n#include \"MonteCarlo_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Helper reduction template\n// Please see the \"reduction\" CUDA Sample for more information\n////////////////////////////////////////////////////////////////////////////////\n#include \"MonteCarlo_reduction.cuh\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Internal GPU-side data structures\n////////////////////////////////////////////////////////////////////////////////\n#define MAX_OPTIONS (1024 * 1024)\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real MuByT;\n    real VBySqrtT;\n} __TOptionData;\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut payoff functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)\n{\n    float callValue = S * __expf(MuByT + VBySqrtT * r) - X;\n    return (callValue > 0.0F) ? callValue : 0.0F;\n}\n\n__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)\n{\n    double callValue = S * exp(MuByT + VBySqrtT * r) - X;\n    return (callValue > 0.0) ? callValue : 0.0;\n}\n\n#define THREAD_N 256\n\n////////////////////////////////////////////////////////////////////////////////\n// This kernel computes the integral over all paths using a single thread block\n// per option. It is fastest when the number of thread blocks times the work per\n// block is high enough to keep the GPU busy.\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void MonteCarloOneBlockPerOption(curandState *__restrict rngStates,\n                                                   const __TOptionData *__restrict d_OptionData,\n                                                   __TOptionValue *__restrict d_CallValue,\n                                                   int pathN,\n                                                   int optionN)\n{\n    // Handle to thread block group\n    cg::thread_block          cta    = cg::this_thread_block();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    const int       SUM_N = THREAD_N;\n    __shared__ real s_SumCall[SUM_N];\n    __shared__ real s_Sum2Call[SUM_N];\n\n    // determine global thread id\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n    // Copy random number state to local memory for efficiency\n    curandState localState = rngStates[tid];\n    for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x) {\n        const real S        = d_OptionData[optionIndex].S;\n        const real X        = d_OptionData[optionIndex].X;\n        const real MuByT    = d_OptionData[optionIndex].MuByT;\n        const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;\n\n        // Cycle through the entire samples array:\n        // derive end stock price for each path\n        // accumulate partial integrals into intermediate shared memory buffer\n        for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {\n            __TOptionValue sumCall = {0, 0};\n\n#pragma unroll 8\n            for (int i = iSum; i < pathN; i += SUM_N) {\n                real r         = curand_normal(&localState);\n                real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);\n                sumCall.Expected += callValue;\n                sumCall.Confidence += callValue * callValue;\n            }\n\n            s_SumCall[iSum]  = sumCall.Expected;\n            s_Sum2Call[iSum] = sumCall.Confidence;\n        }\n\n        // Reduce shared memory accumulators\n        // and write final result to global memory\n        cg::sync(cta);\n        sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);\n    }\n}\n\nstatic __global__ void rngSetupStates(curandState *rngState, int device_id)\n{\n    // determine global thread id\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    // Each threadblock gets different seed,\n    // Threads within a threadblock get different sequence numbers\n    curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU Monte Carlo\n////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" void initMonteCarloGPU(TOptionPlan *plan)\n{\n    checkCudaErrors(cudaMalloc(&plan->d_OptionData, sizeof(__TOptionData) * (plan->optionCount)));\n    checkCudaErrors(cudaMalloc(&plan->d_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));\n    checkCudaErrors(cudaMallocHost(&plan->h_OptionData, sizeof(__TOptionData) * (plan->optionCount)));\n    // Allocate internal device memory\n    checkCudaErrors(cudaMallocHost(&plan->h_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));\n    // Allocate states for pseudo random number generators\n    checkCudaErrors(cudaMalloc((void **)&plan->rngStates, plan->gridSize * THREAD_N * sizeof(curandState)));\n    checkCudaErrors(cudaMemset(plan->rngStates, 0, plan->gridSize * THREAD_N * sizeof(curandState)));\n\n    // place each device pathN random numbers apart on the random number sequence\n    rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);\n    getLastCudaError(\"rngSetupStates kernel failed.\\n\");\n}\n\n// Compute statistics and deallocate internal device memory\nextern \"C\" void closeMonteCarloGPU(TOptionPlan *plan)\n{\n    for (int i = 0; i < plan->optionCount; i++) {\n        const double RT    = plan->optionData[i].R * plan->optionData[i].T;\n        const double sum   = plan->h_CallValue[i].Expected;\n        const double sum2  = plan->h_CallValue[i].Confidence;\n        const double pathN = plan->pathN;\n        // Derive average from the total sum and discount by riskfree rate\n        plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);\n        // Standard deviation\n        double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));\n        // Confidence width; in 95% of all cases theoretical value lies within these\n        // borders\n        plan->callValue[i].Confidence = (float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));\n    }\n\n    checkCudaErrors(cudaFree(plan->rngStates));\n    checkCudaErrors(cudaFreeHost(plan->h_CallValue));\n    checkCudaErrors(cudaFreeHost(plan->h_OptionData));\n    checkCudaErrors(cudaFree(plan->d_CallValue));\n    checkCudaErrors(cudaFree(plan->d_OptionData));\n}\n\n// Main computations\nextern \"C\" void MonteCarloGPU(TOptionPlan *plan, cudaStream_t stream)\n{\n    __TOptionValue *h_CallValue = plan->h_CallValue;\n\n    if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {\n        printf(\"MonteCarloGPU(): bad option count.\\n\");\n        return;\n    }\n\n    __TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;\n\n    for (int i = 0; i < plan->optionCount; i++) {\n        const double T           = plan->optionData[i].T;\n        const double R           = plan->optionData[i].R;\n        const double V           = plan->optionData[i].V;\n        const double MuByT       = (R - 0.5 * V * V) * T;\n        const double VBySqrtT    = V * sqrt(T);\n        h_OptionData[i].S        = (real)plan->optionData[i].S;\n        h_OptionData[i].X        = (real)plan->optionData[i].X;\n        h_OptionData[i].MuByT    = (real)MuByT;\n        h_OptionData[i].VBySqrtT = (real)VBySqrtT;\n    }\n\n    checkCudaErrors(cudaMemcpyAsync(\n        plan->d_OptionData, h_OptionData, plan->optionCount * sizeof(__TOptionData), cudaMemcpyHostToDevice, stream));\n\n    MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(plan->rngStates,\n                                                                         (__TOptionData *)(plan->d_OptionData),\n                                                                         (__TOptionValue *)(plan->d_CallValue),\n                                                                         plan->pathN,\n                                                                         plan->optionCount);\n    getLastCudaError(\"MonteCarloOneBlockPerOption() execution failed\\n\");\n\n    checkCudaErrors(cudaMemcpyAsync(\n        h_CallValue, plan->d_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaMemcpyDeviceToHost, stream));\n\n    // cudaDeviceSynchronize();\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types\n////////////////////////////////////////////////////////////////////////////////\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand_kernel.h>\n#include <hip/hip_runtime_api.h>\n\n#include \"MonteCarlo_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Helper reduction template\n// Please see the \"reduction\" CUDA Sample for more information\n////////////////////////////////////////////////////////////////////////////////\n#include \"MonteCarlo_reduction.cuh\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Internal GPU-side data structures\n////////////////////////////////////////////////////////////////////////////////\n#define MAX_OPTIONS (1024 * 1024)\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real MuByT;\n    real VBySqrtT;\n} __TOptionData;\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut payoff functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)\n{\n    float callValue = S * __expf(MuByT + VBySqrtT * r) - X;\n    return (callValue > 0.0F) ? callValue : 0.0F;\n}\n\n__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)\n{\n    double callValue = S * exp(MuByT + VBySqrtT * r) - X;\n    return (callValue > 0.0) ? callValue : 0.0;\n}\n\n#define THREAD_N 256\n\n////////////////////////////////////////////////////////////////////////////////\n// This kernel computes the integral over all paths using a single thread block\n// per option. It is fastest when the number of thread blocks times the work per\n// block is high enough to keep the GPU busy.\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void MonteCarloOneBlockPerOption(hiprandState *__restrict rngStates,\n                                                   const __TOptionData *__restrict d_OptionData,\n                                                   __TOptionValue *__restrict d_CallValue,\n                                                   int pathN,\n                                                   int optionN)\n{\n    // Handle to thread block group\n    cg::thread_block          cta    = cg::this_thread_block();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    const int       SUM_N = THREAD_N;\n    __shared__ real s_SumCall[SUM_N];\n    __shared__ real s_Sum2Call[SUM_N];\n\n    // determine global thread id\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n    // Copy random number state to local memory for efficiency\n    hiprandState localState = rngStates[tid];\n    for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x) {\n        const real S        = d_OptionData[optionIndex].S;\n        const real X        = d_OptionData[optionIndex].X;\n        const real MuByT    = d_OptionData[optionIndex].MuByT;\n        const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;\n\n        // Cycle through the entire samples array:\n        // derive end stock price for each path\n        // accumulate partial integrals into intermediate shared memory buffer\n        for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x) {\n            __TOptionValue sumCall = {0, 0};\n\n#pragma unroll 8\n            for (int i = iSum; i < pathN; i += SUM_N) {\n                real r         = hiprand_normal(&localState);\n                real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);\n                sumCall.Expected += callValue;\n                sumCall.Confidence += callValue * callValue;\n            }\n\n            s_SumCall[iSum]  = sumCall.Expected;\n            s_Sum2Call[iSum] = sumCall.Confidence;\n        }\n\n        // Reduce shared memory accumulators\n        // and write final result to global memory\n        cg::sync(cta);\n        sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);\n    }\n}\n\nstatic __global__ void rngSetupStates(hiprandState *rngState, int device_id)\n{\n    // determine global thread id\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    // Each threadblock gets different seed,\n    // Threads within a threadblock get different sequence numbers\n    hiprand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU Monte Carlo\n////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" void initMonteCarloGPU(TOptionPlan *plan)\n{\n    checkCudaErrors(hipMalloc(&plan->d_OptionData, sizeof(__TOptionData) * (plan->optionCount)));\n    checkCudaErrors(hipMalloc(&plan->d_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));\n    checkCudaErrors(hipHostMalloc(&plan->h_OptionData, sizeof(__TOptionData) * (plan->optionCount)));\n    // Allocate internal device memory\n    checkCudaErrors(hipHostMalloc(&plan->h_CallValue, sizeof(__TOptionValue) * (plan->optionCount)));\n    // Allocate states for pseudo random number generators\n    checkCudaErrors(hipMalloc((void **)&plan->rngStates, plan->gridSize * THREAD_N * sizeof(hiprandState)));\n    checkCudaErrors(hipMemset(plan->rngStates, 0, plan->gridSize * THREAD_N * sizeof(hiprandState)));\n\n    // place each device pathN random numbers apart on the random number sequence\n    rngSetupStates<<<plan->gridSize, THREAD_N>>>(plan->rngStates, plan->device);\n    getLastCudaError(\"rngSetupStates kernel failed.\\n\");\n}\n\n// Compute statistics and deallocate internal device memory\nextern \"C\" void closeMonteCarloGPU(TOptionPlan *plan)\n{\n    for (int i = 0; i < plan->optionCount; i++) {\n        const double RT    = plan->optionData[i].R * plan->optionData[i].T;\n        const double sum   = plan->h_CallValue[i].Expected;\n        const double sum2  = plan->h_CallValue[i].Confidence;\n        const double pathN = plan->pathN;\n        // Derive average from the total sum and discount by riskfree rate\n        plan->callValue[i].Expected = (float)(exp(-RT) * sum / pathN);\n        // Standard deviation\n        double stdDev = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));\n        // Confidence width; in 95% of all cases theoretical value lies within these\n        // borders\n        plan->callValue[i].Confidence = (float)(exp(-RT) * 1.96 * stdDev / sqrt(pathN));\n    }\n\n    checkCudaErrors(hipFree(plan->rngStates));\n    checkCudaErrors(hipHostFree(plan->h_CallValue));\n    checkCudaErrors(hipHostFree(plan->h_OptionData));\n    checkCudaErrors(hipFree(plan->d_CallValue));\n    checkCudaErrors(hipFree(plan->d_OptionData));\n}\n\n// Main computations\nextern \"C\" void MonteCarloGPU(TOptionPlan *plan, hipStream_t stream)\n{\n    __TOptionValue *h_CallValue = plan->h_CallValue;\n\n    if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS) {\n        printf(\"MonteCarloGPU(): bad option count.\\n\");\n        return;\n    }\n\n    __TOptionData *h_OptionData = (__TOptionData *)plan->h_OptionData;\n\n    for (int i = 0; i < plan->optionCount; i++) {\n        const double T           = plan->optionData[i].T;\n        const double R           = plan->optionData[i].R;\n        const double V           = plan->optionData[i].V;\n        const double MuByT       = (R - 0.5 * V * V) * T;\n        const double VBySqrtT    = V * sqrt(T);\n        h_OptionData[i].S        = (real)plan->optionData[i].S;\n        h_OptionData[i].X        = (real)plan->optionData[i].X;\n        h_OptionData[i].MuByT    = (real)MuByT;\n        h_OptionData[i].VBySqrtT = (real)VBySqrtT;\n    }\n\n    checkCudaErrors(hipMemcpyAsync(\n        plan->d_OptionData, h_OptionData, plan->optionCount * sizeof(__TOptionData), hipMemcpyHostToDevice, stream));\n\n    MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(plan->rngStates,\n                                                                         (__TOptionData *)(plan->d_OptionData),\n                                                                         (__TOptionValue *)(plan->d_CallValue),\n                                                                         plan->pathN,\n                                                                         plan->optionCount);\n    getLastCudaError(\"MonteCarloOneBlockPerOption() execution failed\\n\");\n\n    checkCudaErrors(hipMemcpyAsync(\n        h_CallValue, plan->d_CallValue, plan->optionCount * sizeof(__TOptionValue), hipMemcpyDeviceToHost, stream));\n\n    // cudaDeviceSynchronize();\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/MonteCarloMultiGPU/MonteCarlo_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <float.h> // for FLT_MAX\n#include <helper_cuda.h>\n#include <helper_functions.h>\n#include <helper_math.h>\n\n#include \"CudaMath.h\"\n#include \"dds.h\"\n#include \"permutations.h\"\n\n// Definitions\n#define INPUT_IMAGE     \"teapot512_std.ppm\"\n#define REFERENCE_IMAGE \"teapot512_ref.dds\"\n\n#define ERROR_THRESHOLD 0.02f\n\n#define NUM_THREADS 64 // Number of threads per block.\n\n#define __debugsync()\n\ntemplate <class T> __device__ inline void swap(T &a, T &b)\n{\n    T tmp = a;\n    a     = b;\n    b     = tmp;\n}\n\n//__constant__ float3 kColorMetric = { 0.2126f, 0.7152f, 0.0722f };\n__constant__ float3 kColorMetric = {1.0f, 1.0f, 1.0f};\n\n////////////////////////////////////////////////////////////////////////////////\n// Sort colors\n////////////////////////////////////////////////////////////////////////////////\n__device__ void sortColors(const float *values, int *ranks, cg::thread_group tile)\n{\n    const int tid = threadIdx.x;\n\n    int rank = 0;\n\n#pragma unroll\n\n    for (int i = 0; i < 16; i++) {\n        rank += (values[i] < values[tid]);\n    }\n\n    ranks[tid] = rank;\n\n    cg::sync(tile);\n\n    // Resolve elements with the same index.\n    for (int i = 0; i < 15; i++) {\n        if (tid > i && ranks[tid] == ranks[i]) {\n            ++ranks[tid];\n        }\n        cg::sync(tile);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Load color block to shared mem\n////////////////////////////////////////////////////////////////////////////////\n__device__ void loadColorBlock(const uint      *image,\n                               float3           colors[16],\n                               float3           sums[16],\n                               int              xrefs[16],\n                               int              blockOffset,\n                               cg::thread_block cta)\n{\n    const int bid = blockIdx.x + blockOffset;\n    const int idx = threadIdx.x;\n\n    __shared__ float dps[16];\n\n    float3 tmp;\n\n    cg::thread_group tile = cg::tiled_partition(cta, 16);\n\n    if (idx < 16) {\n        // Read color and copy to shared mem.\n        uint c = image[(bid) * 16 + idx];\n\n        colors[idx].x = ((c >> 0) & 0xFF) * (1.0f / 255.0f);\n        colors[idx].y = ((c >> 8) & 0xFF) * (1.0f / 255.0f);\n        colors[idx].z = ((c >> 16) & 0xFF) * (1.0f / 255.0f);\n\n        cg::sync(tile);\n        // Sort colors along the best fit line.\n        colorSums(colors, sums, tile);\n\n        cg::sync(tile);\n\n        float3 axis = bestFitLine(colors, sums[0], tile);\n\n        cg::sync(tile);\n\n        dps[idx] = dot(colors[idx], axis);\n\n        cg::sync(tile);\n\n        sortColors(dps, xrefs, tile);\n\n        cg::sync(tile);\n\n        tmp = colors[idx];\n\n        cg::sync(tile);\n\n        colors[xrefs[idx]] = tmp;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Round color to RGB565 and expand\n////////////////////////////////////////////////////////////////////////////////\ninline __device__ float3 roundAndExpand(float3 v, ushort *w)\n{\n    v.x = rintf(__saturatef(v.x) * 31.0f);\n    v.y = rintf(__saturatef(v.y) * 63.0f);\n    v.z = rintf(__saturatef(v.z) * 31.0f);\n\n    *w = ((ushort)v.x << 11) | ((ushort)v.y << 5) | (ushort)v.z;\n    v.x *= 0.03227752766457f; // approximate integer bit expansion.\n    v.y *= 0.01583151765563f;\n    v.z *= 0.03227752766457f;\n    return v;\n}\n\n__constant__ float     alphaTable4[4] = {9.0f, 0.0f, 6.0f, 3.0f};\n__constant__ float     alphaTable3[4] = {4.0f, 0.0f, 2.0f, 2.0f};\n__constant__ const int prods4[4]      = {0x090000, 0x000900, 0x040102, 0x010402};\n__constant__ const int prods3[4]      = {0x040000, 0x000400, 0x040101, 0x010401};\n\n#define USE_TABLES 1\n\n////////////////////////////////////////////////////////////////////////////////\n// Evaluate permutations\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ float\nevalPermutation4(const float3 *colors, uint permutation, ushort *start, ushort *end, float3 color_sum)\n{\n// Compute endpoints using least squares.\n#if USE_TABLES\n    float3 alphax_sum = make_float3(0.0f, 0.0f, 0.0f);\n\n    int akku = 0;\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        alphax_sum += alphaTable4[bits & 3] * colors[i];\n        akku += prods4[bits & 3];\n    }\n\n    float  alpha2_sum    = float(akku >> 16);\n    float  beta2_sum     = float((akku >> 8) & 0xff);\n    float  alphabeta_sum = float((akku >> 0) & 0xff);\n    float3 betax_sum     = (9.0f * color_sum) - alphax_sum;\n#else\n    float  alpha2_sum    = 0.0f;\n    float  beta2_sum     = 0.0f;\n    float  alphabeta_sum = 0.0f;\n    float3 alphax_sum    = make_float3(0.0f, 0.0f, 0.0f);\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        float beta = (bits & 1);\n\n        if (bits & 2) {\n            beta = (1 + beta) * (1.0f / 3.0f);\n        }\n\n        float alpha = 1.0f - beta;\n\n        alpha2_sum += alpha * alpha;\n        beta2_sum += beta * beta;\n        alphabeta_sum += alpha * beta;\n        alphax_sum += alpha * colors[i];\n    }\n\n    float3 betax_sum = color_sum - alphax_sum;\n#endif\n\n    // alpha2, beta2, alphabeta and factor could be precomputed for each\n    // permutation, but it's faster to recompute them.\n    const float factor = 1.0f / (alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum);\n\n    float3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;\n    float3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;\n\n    // Round a, b to the closest 5-6-5 color and expand...\n    a = roundAndExpand(a, start);\n    b = roundAndExpand(b, end);\n\n    // compute the error\n    float3 e = a * a * alpha2_sum + b * b * beta2_sum + 2.0f * (a * b * alphabeta_sum - a * alphax_sum - b * betax_sum);\n\n    return (0.111111111111f) * dot(e, kColorMetric);\n}\n\nstatic __device__ float\nevalPermutation3(const float3 *colors, uint permutation, ushort *start, ushort *end, float3 color_sum)\n{\n// Compute endpoints using least squares.\n#if USE_TABLES\n    float3 alphax_sum = make_float3(0.0f, 0.0f, 0.0f);\n\n    int akku = 0;\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        alphax_sum += alphaTable3[bits & 3] * colors[i];\n        akku += prods3[bits & 3];\n    }\n\n    float  alpha2_sum    = float(akku >> 16);\n    float  beta2_sum     = float((akku >> 8) & 0xff);\n    float  alphabeta_sum = float((akku >> 0) & 0xff);\n    float3 betax_sum     = (4.0f * color_sum) - alphax_sum;\n#else\n    float  alpha2_sum    = 0.0f;\n    float  beta2_sum     = 0.0f;\n    float  alphabeta_sum = 0.0f;\n    float3 alphax_sum    = make_float3(0.0f, 0.0f, 0.0f);\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        float beta = (bits & 1);\n\n        if (bits & 2) {\n            beta = 0.5f;\n        }\n\n        float alpha = 1.0f - beta;\n\n        alpha2_sum += alpha * alpha;\n        beta2_sum += beta * beta;\n        alphabeta_sum += alpha * beta;\n        alphax_sum += alpha * colors[i];\n    }\n\n    float3 betax_sum = color_sum - alphax_sum;\n#endif\n\n    const float factor = 1.0f / (alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum);\n\n    float3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;\n    float3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;\n\n    // Round a, b to the closest 5-6-5 color and expand...\n    a = roundAndExpand(a, start);\n    b = roundAndExpand(b, end);\n\n    // compute the error\n    float3 e = a * a * alpha2_sum + b * b * beta2_sum + 2.0f * (a * b * alphabeta_sum - a * alphax_sum - b * betax_sum);\n\n    return (0.25f) * dot(e, kColorMetric);\n}\n\n__device__ void evalAllPermutations(const float3    *colors,\n                                    const uint      *permutations,\n                                    ushort          &bestStart,\n                                    ushort          &bestEnd,\n                                    uint            &bestPermutation,\n                                    float           *errors,\n                                    float3           color_sum,\n                                    cg::thread_block cta)\n{\n    const int idx = threadIdx.x;\n\n    float bestError = FLT_MAX;\n\n    __shared__ uint s_permutations[160];\n\n    for (int i = 0; i < 16; i++) {\n        int pidx = idx + NUM_THREADS * i;\n\n        if (pidx >= 992) {\n            break;\n        }\n\n        ushort start, end;\n        uint   permutation = permutations[pidx];\n\n        if (pidx < 160) {\n            s_permutations[pidx] = permutation;\n        }\n\n        float error = evalPermutation4(colors, permutation, &start, &end, color_sum);\n\n        if (error < bestError) {\n            bestError       = error;\n            bestPermutation = permutation;\n            bestStart       = start;\n            bestEnd         = end;\n        }\n    }\n\n    if (bestStart < bestEnd) {\n        swap(bestEnd, bestStart);\n        bestPermutation ^= 0x55555555; // Flip indices.\n    }\n\n    cg::sync(cta); // Sync here to ensure s_permutations is valid going forward\n\n    for (int i = 0; i < 3; i++) {\n        int pidx = idx + NUM_THREADS * i;\n\n        if (pidx >= 160) {\n            break;\n        }\n\n        ushort start, end;\n        uint   permutation = s_permutations[pidx];\n        float  error       = evalPermutation3(colors, permutation, &start, &end, color_sum);\n\n        if (error < bestError) {\n            bestError       = error;\n            bestPermutation = permutation;\n            bestStart       = start;\n            bestEnd         = end;\n\n            if (bestStart > bestEnd) {\n                swap(bestEnd, bestStart);\n                bestPermutation ^= (~bestPermutation >> 1) & 0x55555555; // Flip indices.\n            }\n        }\n    }\n\n    errors[idx] = bestError;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Find index with minimum error\n////////////////////////////////////////////////////////////////////////////////\n__device__ int findMinError(float *errors, cg::thread_block cta)\n{\n    const int      idx = threadIdx.x;\n    __shared__ int indices[NUM_THREADS];\n    indices[idx] = idx;\n\n    cg::sync(cta);\n\n    for (int d = NUM_THREADS / 2; d > 0; d >>= 1) {\n        float err0   = errors[idx];\n        float err1   = (idx + d) < NUM_THREADS ? errors[idx + d] : FLT_MAX;\n        int   index1 = (idx + d) < NUM_THREADS ? indices[idx + d] : 0;\n\n        cg::sync(cta);\n\n        if (err1 < err0) {\n            errors[idx]  = err1;\n            indices[idx] = index1;\n        }\n\n        cg::sync(cta);\n    }\n\n    return indices[0];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Save DXT block\n////////////////////////////////////////////////////////////////////////////////\n__device__ void saveBlockDXT1(ushort start, ushort end, uint permutation, int xrefs[16], uint2 *result, int blockOffset)\n{\n    const int bid = blockIdx.x + blockOffset;\n\n    if (start == end) {\n        permutation = 0;\n    }\n\n    // Reorder permutation.\n    uint indices = 0;\n\n    for (int i = 0; i < 16; i++) {\n        int ref = xrefs[i];\n        indices |= ((permutation >> (2 * ref)) & 3) << (2 * i);\n    }\n\n    // Write endpoints.\n    result[bid].x = (end << 16) | start;\n\n    // Write palette indices.\n    result[bid].y = indices;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Compress color block\n////////////////////////////////////////////////////////////////////////////////\n__global__ void compress(const uint *permutations, const uint *image, uint2 *result, int blockOffset)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n\n    const int idx = threadIdx.x;\n\n    __shared__ float3 colors[16];\n    __shared__ float3 sums[16];\n    __shared__ int    xrefs[16];\n\n    loadColorBlock(image, colors, sums, xrefs, blockOffset, cta);\n\n    cg::sync(cta);\n\n    ushort bestStart, bestEnd;\n    uint   bestPermutation;\n\n    __shared__ float errors[NUM_THREADS];\n\n    evalAllPermutations(colors, permutations, bestStart, bestEnd, bestPermutation, errors, sums[0], cta);\n\n    // Use a parallel reduction to find minimum error.\n    const int minIdx = findMinError(errors, cta);\n\n    cg::sync(cta);\n\n    // Only write the result of the winner thread.\n    if (idx == minIdx) {\n        saveBlockDXT1(bestStart, bestEnd, bestPermutation, xrefs, result, blockOffset);\n    }\n}\n\n// Helper structs and functions to validate the output of the compressor.\n// We cannot simply do a bitwise compare, because different compilers produce\n// different\n// results for different targets due to floating point arithmetic.\n\nunion Color32\n{\n    struct\n    {\n        unsigned char b, g, r, a;\n    };\n    unsigned int u;\n};\n\nunion Color16\n{\n    struct\n    {\n        unsigned short b : 5;\n        unsigned short g : 6;\n        unsigned short r : 5;\n    };\n    unsigned short u;\n};\n\nstruct BlockDXT1\n{\n    Color16 col0;\n    Color16 col1;\n    union\n    {\n        unsigned char row[4];\n        unsigned int  indices;\n    };\n\n    void decompress(Color32 colors[16]) const;\n};\n\nvoid BlockDXT1::decompress(Color32 *colors) const\n{\n    Color32 palette[4];\n\n    // Does bit expansion before interpolation.\n    palette[0].b = (col0.b << 3) | (col0.b >> 2);\n    palette[0].g = (col0.g << 2) | (col0.g >> 4);\n    palette[0].r = (col0.r << 3) | (col0.r >> 2);\n    palette[0].a = 0xFF;\n\n    palette[1].r = (col1.r << 3) | (col1.r >> 2);\n    palette[1].g = (col1.g << 2) | (col1.g >> 4);\n    palette[1].b = (col1.b << 3) | (col1.b >> 2);\n    palette[1].a = 0xFF;\n\n    if (col0.u > col1.u) {\n        // Four-color block: derive the other two colors.\n        palette[2].r = (2 * palette[0].r + palette[1].r) / 3;\n        palette[2].g = (2 * palette[0].g + palette[1].g) / 3;\n        palette[2].b = (2 * palette[0].b + palette[1].b) / 3;\n        palette[2].a = 0xFF;\n\n        palette[3].r = (2 * palette[1].r + palette[0].r) / 3;\n        palette[3].g = (2 * palette[1].g + palette[0].g) / 3;\n        palette[3].b = (2 * palette[1].b + palette[0].b) / 3;\n        palette[3].a = 0xFF;\n    }\n    else {\n        // Three-color block: derive the other color.\n        palette[2].r = (palette[0].r + palette[1].r) / 2;\n        palette[2].g = (palette[0].g + palette[1].g) / 2;\n        palette[2].b = (palette[0].b + palette[1].b) / 2;\n        palette[2].a = 0xFF;\n\n        palette[3].r = 0x00;\n        palette[3].g = 0x00;\n        palette[3].b = 0x00;\n        palette[3].a = 0x00;\n    }\n\n    for (int i = 0; i < 16; i++) {\n        colors[i] = palette[(indices >> (2 * i)) & 0x3];\n    }\n}\n\nstatic int compareColors(const Color32 *b0, const Color32 *b1)\n{\n    int sum = 0;\n\n    for (int i = 0; i < 16; i++) {\n        int r = (b0[i].r - b1[i].r);\n        int g = (b0[i].g - b1[i].g);\n        int b = (b0[i].b - b1[i].b);\n        sum += r * r + g * g + b * b;\n    }\n\n    return sum;\n}\n\nstatic int compareBlock(const BlockDXT1 *b0, const BlockDXT1 *b1)\n{\n    Color32 colors0[16];\n    Color32 colors1[16];\n\n    if (memcmp(b0, b1, sizeof(BlockDXT1)) == 0) {\n        return 0;\n    }\n    else {\n        b0->decompress(colors0);\n        b1->decompress(colors1);\n\n        return compareColors(colors0, colors1);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    // Load input image.\n    unsigned char *data = NULL;\n    uint           W, H;\n\n    char *image_path = sdkFindFilePath(INPUT_IMAGE, argv[0]);\n\n    if (image_path == 0) {\n        printf(\"Error, unable to find source image  <%s>\\n\", image_path);\n        exit(EXIT_FAILURE);\n    }\n\n    if (!sdkLoadPPM4ub(image_path, &data, &W, &H)) {\n        printf(\"Error, unable to open source image file <%s>\\n\", image_path);\n\n        exit(EXIT_FAILURE);\n    }\n\n    uint w = W, h = H;\n\n    printf(\"Image Loaded '%s', %d x %d pixels\\n\\n\", image_path, w, h);\n\n    // Allocate input image.\n    const uint memSize = w * h * 4;\n    assert(0 != memSize);\n    uint *block_image = (uint *)malloc(memSize);\n\n    // Convert linear image to block linear.\n    for (uint by = 0; by < h / 4; by++) {\n        for (uint bx = 0; bx < w / 4; bx++) {\n            for (int i = 0; i < 16; i++) {\n                const int x                             = i & 3;\n                const int y                             = i / 4;\n                block_image[(by * w / 4 + bx) * 16 + i] = ((uint *)data)[(by * 4 + y) * 4 * (W / 4) + bx * 4 + x];\n            }\n        }\n    }\n\n    // copy into global mem\n    uint *d_data = NULL;\n    checkCudaErrors(cudaMalloc((void **)&d_data, memSize));\n\n    // Result\n    uint      *d_result       = NULL;\n    const uint compressedSize = (w / 4) * (h / 4) * 8;\n    checkCudaErrors(cudaMalloc((void **)&d_result, compressedSize));\n    uint *h_result = (uint *)malloc(compressedSize);\n\n    // Compute permutations.\n    uint permutations[1024];\n    computePermutations(permutations);\n\n    // Copy permutations host to devie.\n    uint *d_permutations = NULL;\n    checkCudaErrors(cudaMalloc((void **)&d_permutations, 1024 * sizeof(uint)));\n    checkCudaErrors(cudaMemcpy(d_permutations, permutations, 1024 * sizeof(uint), cudaMemcpyHostToDevice));\n\n    // create a timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // Copy image from host to device\n    checkCudaErrors(cudaMemcpy(d_data, block_image, memSize, cudaMemcpyHostToDevice));\n\n    // Determine launch configuration and run timed computation numIterations\n    // times\n    uint blocks = ((w + 3) / 4) * ((h + 3) / 4); // rounds up by 1 block in each dim if %4 != 0\n\n    int            devID;\n    cudaDeviceProp deviceProp;\n\n    // get number of SMs on this GPU\n    checkCudaErrors(cudaGetDevice(&devID));\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n\n    // Restrict the numbers of blocks to launch on low end GPUs to avoid kernel\n    // timeout\n    int blocksPerLaunch = min(blocks, 768 * deviceProp.multiProcessorCount);\n\n    printf(\"Running DXT Compression on %u x %u image...\\n\", w, h);\n    printf(\"\\n%u Blocks, %u Threads per Block, %u Threads in Grid...\\n\\n\", blocks, NUM_THREADS, blocks * NUM_THREADS);\n    int numIterations = 1;\n\n    for (int i = -1; i < numIterations; ++i) {\n        if (i == 0) {\n            checkCudaErrors(cudaDeviceSynchronize());\n            sdkStartTimer(&timer);\n        }\n\n        for (int j = 0; j < (int)blocks; j += blocksPerLaunch) {\n            compress<<<min(blocksPerLaunch, blocks - j), NUM_THREADS>>>(d_permutations, d_data, (uint2 *)d_result, j);\n        }\n    }\n\n    getLastCudaError(\"compress\");\n\n    // sync to host, stop timer, record perf\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkStopTimer(&timer);\n    double dAvgTime = 1.0e-3 * sdkGetTimerValue(&timer) / (double)numIterations;\n    printf(\"dxtc, Throughput = %.4f MPixels/s, Time = %.5f s, Size = %u Pixels, \"\n           \"NumDevsUsed = %i, Workgroup = %d\\n\",\n           (1.0e-6 * (double)(W * H) / dAvgTime),\n           dAvgTime,\n           (W * H),\n           1,\n           NUM_THREADS);\n\n    // copy result data from device to host\n    checkCudaErrors(cudaMemcpy(h_result, d_result, compressedSize, cudaMemcpyDeviceToHost));\n\n    // Write out result data to DDS file\n    char output_filename[1024];\n    strcpy(output_filename, image_path);\n    strcpy(output_filename + strlen(image_path) - 3, \"dds\");\n    FILE *fp = fopen(output_filename, \"wb\");\n\n    if (fp == 0) {\n        printf(\"Error, unable to open output image <%s>\\n\", output_filename);\n        exit(EXIT_FAILURE);\n    }\n\n    DDSHeader header;\n    header.fourcc      = FOURCC_DDS;\n    header.size        = 124;\n    header.flags       = (DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_LINEARSIZE);\n    header.height      = h;\n    header.width       = w;\n    header.pitch       = compressedSize;\n    header.depth       = 0;\n    header.mipmapcount = 0;\n    memset(header.reserved, 0, sizeof(header.reserved));\n    header.pf.size     = 32;\n    header.pf.flags    = DDPF_FOURCC;\n    header.pf.fourcc   = FOURCC_DXT1;\n    header.pf.bitcount = 0;\n    header.pf.rmask    = 0;\n    header.pf.gmask    = 0;\n    header.pf.bmask    = 0;\n    header.pf.amask    = 0;\n    header.caps.caps1  = DDSCAPS_TEXTURE;\n    header.caps.caps2  = 0;\n    header.caps.caps3  = 0;\n    header.caps.caps4  = 0;\n    header.notused     = 0;\n    fwrite(&header, sizeof(DDSHeader), 1, fp);\n    fwrite(h_result, compressedSize, 1, fp);\n    fclose(fp);\n\n    // Make sure the generated image is correct.\n    const char *reference_image_path = sdkFindFilePath(REFERENCE_IMAGE, argv[0]);\n\n    if (reference_image_path == 0) {\n        printf(\"Error, unable to find reference image\\n\");\n\n        exit(EXIT_FAILURE);\n    }\n\n    fp = fopen(reference_image_path, \"rb\");\n\n    if (fp == 0) {\n        printf(\"Error, unable to open reference image\\n\");\n\n        exit(EXIT_FAILURE);\n    }\n\n    fseek(fp, sizeof(DDSHeader), SEEK_SET);\n    uint  referenceSize = (W / 4) * (H / 4) * 8;\n    uint *reference     = (uint *)malloc(referenceSize);\n    fread(reference, referenceSize, 1, fp);\n    fclose(fp);\n\n    printf(\"\\nChecking accuracy...\\n\");\n    float rms = 0;\n\n    for (uint y = 0; y < h; y += 4) {\n        for (uint x = 0; x < w; x += 4) {\n            uint referenceBlockIdx = ((y / 4) * (W / 4) + (x / 4));\n            uint resultBlockIdx    = ((y / 4) * (w / 4) + (x / 4));\n\n            int cmp =\n                compareBlock(((BlockDXT1 *)h_result) + resultBlockIdx, ((BlockDXT1 *)reference) + referenceBlockIdx);\n\n            if (cmp != 0.0f) {\n                printf(\"Deviation at (%4d,%4d):\\t%f rms\\n\", x / 4, y / 4, float(cmp) / 16 / 3);\n            }\n\n            rms += cmp;\n        }\n    }\n\n    rms /= w * h * 3;\n\n    // Free allocated resources and exit\n    checkCudaErrors(cudaFree(d_permutations));\n    checkCudaErrors(cudaFree(d_data));\n    checkCudaErrors(cudaFree(d_result));\n    free(image_path);\n    free(data);\n    free(block_image);\n    free(h_result);\n    free(reference);\n    sdkDeleteTimer(&timer);\n\n    printf(\"RMS(reference, result) = %f\\n\\n\", rms);\n    printf(rms <= ERROR_THRESHOLD ? \"Test passed\\n\" : \"Test failed!\\n\");\n    /* Return zero if test passed, one otherwise */\n    return rms > ERROR_THRESHOLD;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <float.h> // for FLT_MAX\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n#include <hip/hip_vector_types.h>\n\n#include \"CudaMath.h\"\n#include \"dds.h\"\n#include \"permutations.h\"\n\n// Definitions\n#define INPUT_IMAGE     \"teapot512_std.ppm\"\n#define REFERENCE_IMAGE \"teapot512_ref.dds\"\n\n#define ERROR_THRESHOLD 0.02f\n\n#define NUM_THREADS 64 // Number of threads per block.\n\n#define __debugsync()\n\ntemplate <class T> __device__ inline void swap(T &a, T &b)\n{\n    T tmp = a;\n    a     = b;\n    b     = tmp;\n}\n\n//__constant__ float3 kColorMetric = { 0.2126f, 0.7152f, 0.0722f };\n__constant__ float3 kColorMetric = {1.0f, 1.0f, 1.0f};\n\n////////////////////////////////////////////////////////////////////////////////\n// Sort colors\n////////////////////////////////////////////////////////////////////////////////\n__device__ void sortColors(const float *values, int *ranks, cg::thread_group tile)\n{\n    const int tid = threadIdx.x;\n\n    int rank = 0;\n\n#pragma unroll\n\n    for (int i = 0; i < 16; i++) {\n        rank += (values[i] < values[tid]);\n    }\n\n    ranks[tid] = rank;\n\n    cg::sync(tile);\n\n    // Resolve elements with the same index.\n    for (int i = 0; i < 15; i++) {\n        if (tid > i && ranks[tid] == ranks[i]) {\n            ++ranks[tid];\n        }\n        cg::sync(tile);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Load color block to shared mem\n////////////////////////////////////////////////////////////////////////////////\n__device__ void loadColorBlock(const uint      *image,\n                               float3           colors[16],\n                               float3           sums[16],\n                               int              xrefs[16],\n                               int              blockOffset,\n                               cg::thread_block cta)\n{\n    const int bid = blockIdx.x + blockOffset;\n    const int idx = threadIdx.x;\n\n    __shared__ float dps[16];\n\n    float3 tmp;\n\n    cg::thread_group tile = cg::tiled_partition(cta, 16);\n\n    if (idx < 16) {\n        // Read color and copy to shared mem.\n        uint c = image[(bid) * 16 + idx];\n\n        colors[idx].x = ((c >> 0) & 0xFF) * (1.0f / 255.0f);\n        colors[idx].y = ((c >> 8) & 0xFF) * (1.0f / 255.0f);\n        colors[idx].z = ((c >> 16) & 0xFF) * (1.0f / 255.0f);\n\n        cg::sync(tile);\n        // Sort colors along the best fit line.\n        colorSums(colors, sums, tile);\n\n        cg::sync(tile);\n\n        float3 axis = bestFitLine(colors, sums[0], tile);\n\n        cg::sync(tile);\n\n        dps[idx] = dot(colors[idx], axis);\n\n        cg::sync(tile);\n\n        sortColors(dps, xrefs, tile);\n\n        cg::sync(tile);\n\n        tmp = colors[idx];\n\n        cg::sync(tile);\n\n        colors[xrefs[idx]] = tmp;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Round color to RGB565 and expand\n////////////////////////////////////////////////////////////////////////////////\ninline __device__ float3 roundAndExpand(float3 v, ushort *w)\n{\n    v.x = rintf(__saturatef(v.x) * 31.0f);\n    v.y = rintf(__saturatef(v.y) * 63.0f);\n    v.z = rintf(__saturatef(v.z) * 31.0f);\n\n    *w = ((ushort)v.x << 11) | ((ushort)v.y << 5) | (ushort)v.z;\n    v.x *= 0.03227752766457f; // approximate integer bit expansion.\n    v.y *= 0.01583151765563f;\n    v.z *= 0.03227752766457f;\n    return v;\n}\n\n__constant__ float     alphaTable4[4] = {9.0f, 0.0f, 6.0f, 3.0f};\n__constant__ float     alphaTable3[4] = {4.0f, 0.0f, 2.0f, 2.0f};\n__constant__ const int prods4[4]      = {0x090000, 0x000900, 0x040102, 0x010402};\n__constant__ const int prods3[4]      = {0x040000, 0x000400, 0x040101, 0x010401};\n\n#define USE_TABLES 1\n\n////////////////////////////////////////////////////////////////////////////////\n// Evaluate permutations\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ float\nevalPermutation4(const float3 *colors, uint permutation, ushort *start, ushort *end, float3 color_sum)\n{\n// Compute endpoints using least squares.\n#if USE_TABLES\n    float3 alphax_sum = make_float3(0.0f, 0.0f, 0.0f);\n\n    int akku = 0;\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        alphax_sum += alphaTable4[bits & 3] * colors[i];\n        akku += prods4[bits & 3];\n    }\n\n    float  alpha2_sum    = float(akku >> 16);\n    float  beta2_sum     = float((akku >> 8) & 0xff);\n    float  alphabeta_sum = float((akku >> 0) & 0xff);\n    float3 betax_sum     = (9.0f * color_sum) - alphax_sum;\n#else\n    float  alpha2_sum    = 0.0f;\n    float  beta2_sum     = 0.0f;\n    float  alphabeta_sum = 0.0f;\n    float3 alphax_sum    = make_float3(0.0f, 0.0f, 0.0f);\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        float beta = (bits & 1);\n\n        if (bits & 2) {\n            beta = (1 + beta) * (1.0f / 3.0f);\n        }\n\n        float alpha = 1.0f - beta;\n\n        alpha2_sum += alpha * alpha;\n        beta2_sum += beta * beta;\n        alphabeta_sum += alpha * beta;\n        alphax_sum += alpha * colors[i];\n    }\n\n    float3 betax_sum = color_sum - alphax_sum;\n#endif\n\n    // alpha2, beta2, alphabeta and factor could be precomputed for each\n    // permutation, but it's faster to recompute them.\n    const float factor = 1.0f / (alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum);\n\n    float3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;\n    float3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;\n\n    // Round a, b to the closest 5-6-5 color and expand...\n    a = roundAndExpand(a, start);\n    b = roundAndExpand(b, end);\n\n    // compute the error\n    float3 e = a * a * alpha2_sum + b * b * beta2_sum + 2.0f * (a * b * alphabeta_sum - a * alphax_sum - b * betax_sum);\n\n    return (0.111111111111f) * dot(e, kColorMetric);\n}\n\nstatic __device__ float\nevalPermutation3(const float3 *colors, uint permutation, ushort *start, ushort *end, float3 color_sum)\n{\n// Compute endpoints using least squares.\n#if USE_TABLES\n    float3 alphax_sum = make_float3(0.0f, 0.0f, 0.0f);\n\n    int akku = 0;\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        alphax_sum += alphaTable3[bits & 3] * colors[i];\n        akku += prods3[bits & 3];\n    }\n\n    float  alpha2_sum    = float(akku >> 16);\n    float  beta2_sum     = float((akku >> 8) & 0xff);\n    float  alphabeta_sum = float((akku >> 0) & 0xff);\n    float3 betax_sum     = (4.0f * color_sum) - alphax_sum;\n#else\n    float  alpha2_sum    = 0.0f;\n    float  beta2_sum     = 0.0f;\n    float  alphabeta_sum = 0.0f;\n    float3 alphax_sum    = make_float3(0.0f, 0.0f, 0.0f);\n\n    // Compute alpha & beta for this permutation.\n    for (int i = 0; i < 16; i++) {\n        const uint bits = permutation >> (2 * i);\n\n        float beta = (bits & 1);\n\n        if (bits & 2) {\n            beta = 0.5f;\n        }\n\n        float alpha = 1.0f - beta;\n\n        alpha2_sum += alpha * alpha;\n        beta2_sum += beta * beta;\n        alphabeta_sum += alpha * beta;\n        alphax_sum += alpha * colors[i];\n    }\n\n    float3 betax_sum = color_sum - alphax_sum;\n#endif\n\n    const float factor = 1.0f / (alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum);\n\n    float3 a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;\n    float3 b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;\n\n    // Round a, b to the closest 5-6-5 color and expand...\n    a = roundAndExpand(a, start);\n    b = roundAndExpand(b, end);\n\n    // compute the error\n    float3 e = a * a * alpha2_sum + b * b * beta2_sum + 2.0f * (a * b * alphabeta_sum - a * alphax_sum - b * betax_sum);\n\n    return (0.25f) * dot(e, kColorMetric);\n}\n\n__device__ void evalAllPermutations(const float3    *colors,\n                                    const uint      *permutations,\n                                    ushort          &bestStart,\n                                    ushort          &bestEnd,\n                                    uint            &bestPermutation,\n                                    float           *errors,\n                                    float3           color_sum,\n                                    cg::thread_block cta)\n{\n    const int idx = threadIdx.x;\n\n    float bestError = FLT_MAX;\n\n    __shared__ uint s_permutations[160];\n\n    for (int i = 0; i < 16; i++) {\n        int pidx = idx + NUM_THREADS * i;\n\n        if (pidx >= 992) {\n            break;\n        }\n\n        ushort start, end;\n        uint   permutation = permutations[pidx];\n\n        if (pidx < 160) {\n            s_permutations[pidx] = permutation;\n        }\n\n        float error = evalPermutation4(colors, permutation, &start, &end, color_sum);\n\n        if (error < bestError) {\n            bestError       = error;\n            bestPermutation = permutation;\n            bestStart       = start;\n            bestEnd         = end;\n        }\n    }\n\n    if (bestStart < bestEnd) {\n        swap(bestEnd, bestStart);\n        bestPermutation ^= 0x55555555; // Flip indices.\n    }\n\n    cg::sync(cta); // Sync here to ensure s_permutations is valid going forward\n\n    for (int i = 0; i < 3; i++) {\n        int pidx = idx + NUM_THREADS * i;\n\n        if (pidx >= 160) {\n            break;\n        }\n\n        ushort start, end;\n        uint   permutation = s_permutations[pidx];\n        float  error       = evalPermutation3(colors, permutation, &start, &end, color_sum);\n\n        if (error < bestError) {\n            bestError       = error;\n            bestPermutation = permutation;\n            bestStart       = start;\n            bestEnd         = end;\n\n            if (bestStart > bestEnd) {\n                swap(bestEnd, bestStart);\n                bestPermutation ^= (~bestPermutation >> 1) & 0x55555555; // Flip indices.\n            }\n        }\n    }\n\n    errors[idx] = bestError;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Find index with minimum error\n////////////////////////////////////////////////////////////////////////////////\n__device__ int findMinError(float *errors, cg::thread_block cta)\n{\n    const int      idx = threadIdx.x;\n    __shared__ int indices[NUM_THREADS];\n    indices[idx] = idx;\n\n    cg::sync(cta);\n\n    for (int d = NUM_THREADS / 2; d > 0; d >>= 1) {\n        float err0   = errors[idx];\n        float err1   = (idx + d) < NUM_THREADS ? errors[idx + d] : FLT_MAX;\n        int   index1 = (idx + d) < NUM_THREADS ? indices[idx + d] : 0;\n\n        cg::sync(cta);\n\n        if (err1 < err0) {\n            errors[idx]  = err1;\n            indices[idx] = index1;\n        }\n\n        cg::sync(cta);\n    }\n\n    return indices[0];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Save DXT block\n////////////////////////////////////////////////////////////////////////////////\n__device__ void saveBlockDXT1(ushort start, ushort end, uint permutation, int xrefs[16], uint2 *result, int blockOffset)\n{\n    const int bid = blockIdx.x + blockOffset;\n\n    if (start == end) {\n        permutation = 0;\n    }\n\n    // Reorder permutation.\n    uint indices = 0;\n\n    for (int i = 0; i < 16; i++) {\n        int ref = xrefs[i];\n        indices |= ((permutation >> (2 * ref)) & 3) << (2 * i);\n    }\n\n    // Write endpoints.\n    result[bid].x = (end << 16) | start;\n\n    // Write palette indices.\n    result[bid].y = indices;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Compress color block\n////////////////////////////////////////////////////////////////////////////////\n__global__ void compress(const uint *permutations, const uint *image, uint2 *result, int blockOffset)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n\n    const int idx = threadIdx.x;\n\n    __shared__ float3 colors[16];\n    __shared__ float3 sums[16];\n    __shared__ int    xrefs[16];\n\n    loadColorBlock(image, colors, sums, xrefs, blockOffset, cta);\n\n    cg::sync(cta);\n\n    ushort bestStart, bestEnd;\n    uint   bestPermutation;\n\n    __shared__ float errors[NUM_THREADS];\n\n    evalAllPermutations(colors, permutations, bestStart, bestEnd, bestPermutation, errors, sums[0], cta);\n\n    // Use a parallel reduction to find minimum error.\n    const int minIdx = findMinError(errors, cta);\n\n    cg::sync(cta);\n\n    // Only write the result of the winner thread.\n    if (idx == minIdx) {\n        saveBlockDXT1(bestStart, bestEnd, bestPermutation, xrefs, result, blockOffset);\n    }\n}\n\n// Helper structs and functions to validate the output of the compressor.\n// We cannot simply do a bitwise compare, because different compilers produce\n// different\n// results for different targets due to floating point arithmetic.\n\nunion Color32\n{\n    struct\n    {\n        unsigned char b, g, r, a;\n    };\n    unsigned int u;\n};\n\nunion Color16\n{\n    struct\n    {\n        unsigned short b : 5;\n        unsigned short g : 6;\n        unsigned short r : 5;\n    };\n    unsigned short u;\n};\n\nstruct BlockDXT1\n{\n    Color16 col0;\n    Color16 col1;\n    union\n    {\n        unsigned char row[4];\n        unsigned int  indices;\n    };\n\n    void decompress(Color32 colors[16]) const;\n};\n\nvoid BlockDXT1::decompress(Color32 *colors) const\n{\n    Color32 palette[4];\n\n    // Does bit expansion before interpolation.\n    palette[0].b = (col0.b << 3) | (col0.b >> 2);\n    palette[0].g = (col0.g << 2) | (col0.g >> 4);\n    palette[0].r = (col0.r << 3) | (col0.r >> 2);\n    palette[0].a = 0xFF;\n\n    palette[1].r = (col1.r << 3) | (col1.r >> 2);\n    palette[1].g = (col1.g << 2) | (col1.g >> 4);\n    palette[1].b = (col1.b << 3) | (col1.b >> 2);\n    palette[1].a = 0xFF;\n\n    if (col0.u > col1.u) {\n        // Four-color block: derive the other two colors.\n        palette[2].r = (2 * palette[0].r + palette[1].r) / 3;\n        palette[2].g = (2 * palette[0].g + palette[1].g) / 3;\n        palette[2].b = (2 * palette[0].b + palette[1].b) / 3;\n        palette[2].a = 0xFF;\n\n        palette[3].r = (2 * palette[1].r + palette[0].r) / 3;\n        palette[3].g = (2 * palette[1].g + palette[0].g) / 3;\n        palette[3].b = (2 * palette[1].b + palette[0].b) / 3;\n        palette[3].a = 0xFF;\n    }\n    else {\n        // Three-color block: derive the other color.\n        palette[2].r = (palette[0].r + palette[1].r) / 2;\n        palette[2].g = (palette[0].g + palette[1].g) / 2;\n        palette[2].b = (palette[0].b + palette[1].b) / 2;\n        palette[2].a = 0xFF;\n\n        palette[3].r = 0x00;\n        palette[3].g = 0x00;\n        palette[3].b = 0x00;\n        palette[3].a = 0x00;\n    }\n\n    for (int i = 0; i < 16; i++) {\n        colors[i] = palette[(indices >> (2 * i)) & 0x3];\n    }\n}\n\nstatic int compareColors(const Color32 *b0, const Color32 *b1)\n{\n    int sum = 0;\n\n    for (int i = 0; i < 16; i++) {\n        int r = (b0[i].r - b1[i].r);\n        int g = (b0[i].g - b1[i].g);\n        int b = (b0[i].b - b1[i].b);\n        sum += r * r + g * g + b * b;\n    }\n\n    return sum;\n}\n\nstatic int compareBlock(const BlockDXT1 *b0, const BlockDXT1 *b1)\n{\n    Color32 colors0[16];\n    Color32 colors1[16];\n\n    if (memcmp(b0, b1, sizeof(BlockDXT1)) == 0) {\n        return 0;\n    }\n    else {\n        b0->decompress(colors0);\n        b1->decompress(colors1);\n\n        return compareColors(colors0, colors1);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"%s Starting...\\n\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    // Load input image.\n    unsigned char *data = NULL;\n    uint           W, H;\n\n    char *image_path = sdkFindFilePath(INPUT_IMAGE, argv[0]);\n\n    if (image_path == 0) {\n        printf(\"Error, unable to find source image  <%s>\\n\", image_path);\n        exit(EXIT_FAILURE);\n    }\n\n    if (!sdkLoadPPM4ub(image_path, &data, &W, &H)) {\n        printf(\"Error, unable to open source image file <%s>\\n\", image_path);\n\n        exit(EXIT_FAILURE);\n    }\n\n    uint w = W, h = H;\n\n    printf(\"Image Loaded '%s', %d x %d pixels\\n\\n\", image_path, w, h);\n\n    // Allocate input image.\n    const uint memSize = w * h * 4;\n    assert(0 != memSize);\n    uint *block_image = (uint *)malloc(memSize);\n\n    // Convert linear image to block linear.\n    for (uint by = 0; by < h / 4; by++) {\n        for (uint bx = 0; bx < w / 4; bx++) {\n            for (int i = 0; i < 16; i++) {\n                const int x                             = i & 3;\n                const int y                             = i / 4;\n                block_image[(by * w / 4 + bx) * 16 + i] = ((uint *)data)[(by * 4 + y) * 4 * (W / 4) + bx * 4 + x];\n            }\n        }\n    }\n\n    // copy into global mem\n    uint *d_data = NULL;\n    checkCudaErrors(hipMalloc((void **)&d_data, memSize));\n\n    // Result\n    uint      *d_result       = NULL;\n    const uint compressedSize = (w / 4) * (h / 4) * 8;\n    checkCudaErrors(hipMalloc((void **)&d_result, compressedSize));\n    uint *h_result = (uint *)malloc(compressedSize);\n\n    // Compute permutations.\n    uint permutations[1024];\n    computePermutations(permutations);\n\n    // Copy permutations host to devie.\n    uint *d_permutations = NULL;\n    checkCudaErrors(hipMalloc((void **)&d_permutations, 1024 * sizeof(uint)));\n    checkCudaErrors(hipMemcpy(d_permutations, permutations, 1024 * sizeof(uint), hipMemcpyHostToDevice));\n\n    // create a timer\n    StopWatchInterface *timer = NULL;\n    sdkCreateTimer(&timer);\n\n    // Copy image from host to device\n    checkCudaErrors(hipMemcpy(d_data, block_image, memSize, hipMemcpyHostToDevice));\n\n    // Determine launch configuration and run timed computation numIterations\n    // times\n    uint blocks = ((w + 3) / 4) * ((h + 3) / 4); // rounds up by 1 block in each dim if %4 != 0\n\n    int            devID;\n    hipDeviceProp_t deviceProp;\n\n    // get number of SMs on this GPU\n    checkCudaErrors(hipGetDevice(&devID));\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n\n    // Restrict the numbers of blocks to launch on low end GPUs to avoid kernel\n    // timeout\n    int blocksPerLaunch = min(blocks, 768 * deviceProp.multiProcessorCount);\n\n    printf(\"Running DXT Compression on %u x %u image...\\n\", w, h);\n    printf(\"\\n%u Blocks, %u Threads per Block, %u Threads in Grid...\\n\\n\", blocks, NUM_THREADS, blocks * NUM_THREADS);\n    int numIterations = 1;\n\n    for (int i = -1; i < numIterations; ++i) {\n        if (i == 0) {\n            checkCudaErrors(hipDeviceSynchronize());\n            sdkStartTimer(&timer);\n        }\n\n        for (int j = 0; j < (int)blocks; j += blocksPerLaunch) {\n            compress<<<min(blocksPerLaunch, blocks - j), NUM_THREADS>>>(d_permutations, d_data, (uint2 *)d_result, j);\n        }\n    }\n\n    getLastCudaError(\"compress\");\n\n    // sync to host, stop timer, record perf\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkStopTimer(&timer);\n    double dAvgTime = 1.0e-3 * sdkGetTimerValue(&timer) / (double)numIterations;\n    printf(\"dxtc, Throughput = %.4f MPixels/s, Time = %.5f s, Size = %u Pixels, \"\n           \"NumDevsUsed = %i, Workgroup = %d\\n\",\n           (1.0e-6 * (double)(W * H) / dAvgTime),\n           dAvgTime,\n           (W * H),\n           1,\n           NUM_THREADS);\n\n    // copy result data from device to host\n    checkCudaErrors(hipMemcpy(h_result, d_result, compressedSize, hipMemcpyDeviceToHost));\n\n    // Write out result data to DDS file\n    char output_filename[1024];\n    strcpy(output_filename, image_path);\n    strcpy(output_filename + strlen(image_path) - 3, \"dds\");\n    FILE *fp = fopen(output_filename, \"wb\");\n\n    if (fp == 0) {\n        printf(\"Error, unable to open output image <%s>\\n\", output_filename);\n        exit(EXIT_FAILURE);\n    }\n\n    DDSHeader header;\n    header.fourcc      = FOURCC_DDS;\n    header.size        = 124;\n    header.flags       = (DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_LINEARSIZE);\n    header.height      = h;\n    header.width       = w;\n    header.pitch       = compressedSize;\n    header.depth       = 0;\n    header.mipmapcount = 0;\n    memset(header.reserved, 0, sizeof(header.reserved));\n    header.pf.size     = 32;\n    header.pf.flags    = DDPF_FOURCC;\n    header.pf.fourcc   = FOURCC_DXT1;\n    header.pf.bitcount = 0;\n    header.pf.rmask    = 0;\n    header.pf.gmask    = 0;\n    header.pf.bmask    = 0;\n    header.pf.amask    = 0;\n    header.caps.caps1  = DDSCAPS_TEXTURE;\n    header.caps.caps2  = 0;\n    header.caps.caps3  = 0;\n    header.caps.caps4  = 0;\n    header.notused     = 0;\n    fwrite(&header, sizeof(DDSHeader), 1, fp);\n    fwrite(h_result, compressedSize, 1, fp);\n    fclose(fp);\n\n    // Make sure the generated image is correct.\n    const char *reference_image_path = sdkFindFilePath(REFERENCE_IMAGE, argv[0]);\n\n    if (reference_image_path == 0) {\n        printf(\"Error, unable to find reference image\\n\");\n\n        exit(EXIT_FAILURE);\n    }\n\n    fp = fopen(reference_image_path, \"rb\");\n\n    if (fp == 0) {\n        printf(\"Error, unable to open reference image\\n\");\n\n        exit(EXIT_FAILURE);\n    }\n\n    fseek(fp, sizeof(DDSHeader), SEEK_SET);\n    uint  referenceSize = (W / 4) * (H / 4) * 8;\n    uint *reference     = (uint *)malloc(referenceSize);\n    fread(reference, referenceSize, 1, fp);\n    fclose(fp);\n\n    printf(\"\\nChecking accuracy...\\n\");\n    float rms = 0;\n\n    for (uint y = 0; y < h; y += 4) {\n        for (uint x = 0; x < w; x += 4) {\n            uint referenceBlockIdx = ((y / 4) * (W / 4) + (x / 4));\n            uint resultBlockIdx    = ((y / 4) * (w / 4) + (x / 4));\n\n            int cmp =\n                compareBlock(((BlockDXT1 *)h_result) + resultBlockIdx, ((BlockDXT1 *)reference) + referenceBlockIdx);\n\n            if (cmp != 0.0f) {\n                printf(\"Deviation at (%4d,%4d):\\t%f rms\\n\", x / 4, y / 4, float(cmp) / 16 / 3);\n            }\n\n            rms += cmp;\n        }\n    }\n\n    rms /= w * h * 3;\n\n    // Free allocated resources and exit\n    checkCudaErrors(hipFree(d_permutations));\n    checkCudaErrors(hipFree(d_data));\n    checkCudaErrors(hipFree(d_result));\n    free(image_path);\n    free(data);\n    free(block_image);\n    free(h_result);\n    free(reference);\n    sdkDeleteTimer(&timer);\n\n    printf(\"RMS(reference, result) = %f\\n\\n\", rms);\n    printf(rms <= ERROR_THRESHOLD ? \"Test passed\\n\" : \"Test failed!\\n\");\n    /* Return zero if test passed, one otherwise */\n    return rms > ERROR_THRESHOLD;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/dxtc/dxtc.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"binomialOptions_common.h\"\n#include \"common_gpu_header.h\"\n#include \"realtype.h\"\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real vDt;\n    real puByDf;\n    real pdByDf;\n} __TOptionData;\nstatic __constant__ __TOptionData d_OptionData[MAX_OPTIONS];\n__device__ real                   d_CallValue[MAX_OPTIONS];\n\n#define THREADBLOCK_SIZE 128\n#define ELEMS_PER_THREAD (NUM_STEPS / THREADBLOCK_SIZE)\n#if NUM_STEPS % THREADBLOCK_SIZE\n#error Bad constants\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n\n#ifndef DOUBLE_PRECISION\n__device__ inline float expiryCallValue(float S, float X, float vDt, int i)\n{\n    float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;\n    return (d > 0.0F) ? d : 0.0F;\n}\n\n#else\n__device__ inline double expiryCallValue(double S, double X, double vDt, int i)\n{\n    double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;\n    return (d > 0.0) ? d : 0.0;\n}\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU kernel\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" __global__ void binomialOptionsKernel()\n{\n    __shared__ real call_exchange[THREADBLOCK_SIZE + 1];\n\n    const int  tid    = threadIdx.x;\n    const real S      = d_OptionData[blockIdx.x].S;\n    const real X      = d_OptionData[blockIdx.x].X;\n    const real vDt    = d_OptionData[blockIdx.x].vDt;\n    const real puByDf = d_OptionData[blockIdx.x].puByDf;\n    const real pdByDf = d_OptionData[blockIdx.x].pdByDf;\n\n    real call[ELEMS_PER_THREAD + 1];\n#pragma unroll\n    for (int i = 0; i < ELEMS_PER_THREAD; ++i)\n        call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);\n\n    if (tid == 0)\n        call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);\n\n    int final_it = max(0, tid * ELEMS_PER_THREAD - 1);\n\n#pragma unroll 16\n    for (int i = NUM_STEPS; i > 0; --i) {\n        call_exchange[tid] = call[0];\n        __syncthreads();\n        call[ELEMS_PER_THREAD] = call_exchange[tid + 1];\n        __syncthreads();\n\n        if (i > final_it) {\n#pragma unroll\n            for (int j = 0; j < ELEMS_PER_THREAD; ++j)\n                call[j] = puByDf * call[j + 1] + pdByDf * call[j];\n        }\n    }\n\n    if (tid == 0) {\n        d_CallValue[blockIdx.x] = call[0];\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"binomialOptions_common.h\"\n#include \"common_gpu_header.h\"\n#include \"realtype.h\"\n\n// Preprocessed input option data\ntypedef struct\n{\n    real S;\n    real X;\n    real vDt;\n    real puByDf;\n    real pdByDf;\n} __TOptionData;\nstatic __constant__ __TOptionData d_OptionData[MAX_OPTIONS];\n__device__ real                   d_CallValue[MAX_OPTIONS];\n\n#define THREADBLOCK_SIZE 128\n#define ELEMS_PER_THREAD (NUM_STEPS / THREADBLOCK_SIZE)\n#if NUM_STEPS % THREADBLOCK_SIZE\n#error Bad constants\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n\n#ifndef DOUBLE_PRECISION\n__device__ inline float expiryCallValue(float S, float X, float vDt, int i)\n{\n    float d = S * __expf(vDt * (2.0f * i - NUM_STEPS)) - X;\n    return (d > 0.0F) ? d : 0.0F;\n}\n\n#else\n__device__ inline double expiryCallValue(double S, double X, double vDt, int i)\n{\n    double d = S * exp(vDt * (2.0 * i - NUM_STEPS)) - X;\n    return (d > 0.0) ? d : 0.0;\n}\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU kernel\n////////////////////////////////////////////////////////////////////////////////\nextern \"C\" __global__ void binomialOptionsKernel()\n{\n    __shared__ real call_exchange[THREADBLOCK_SIZE + 1];\n\n    const int  tid    = threadIdx.x;\n    const real S      = d_OptionData[blockIdx.x].S;\n    const real X      = d_OptionData[blockIdx.x].X;\n    const real vDt    = d_OptionData[blockIdx.x].vDt;\n    const real puByDf = d_OptionData[blockIdx.x].puByDf;\n    const real pdByDf = d_OptionData[blockIdx.x].pdByDf;\n\n    real call[ELEMS_PER_THREAD + 1];\n#pragma unroll\n    for (int i = 0; i < ELEMS_PER_THREAD; ++i)\n        call[i] = expiryCallValue(S, X, vDt, tid * ELEMS_PER_THREAD + i);\n\n    if (tid == 0)\n        call_exchange[THREADBLOCK_SIZE] = expiryCallValue(S, X, vDt, NUM_STEPS);\n\n    int final_it = max(0, tid * ELEMS_PER_THREAD - 1);\n\n#pragma unroll 16\n    for (int i = NUM_STEPS; i > 0; --i) {\n        call_exchange[tid] = call[0];\n        __syncthreads();\n        call[ELEMS_PER_THREAD] = call_exchange[tid + 1];\n        __syncthreads();\n\n        if (i > final_it) {\n#pragma unroll\n            for (int j = 0; j < ELEMS_PER_THREAD; ++j)\n                call[j] = puByDf * call[j + 1] + pdByDf * call[j];\n        }\n    }\n\n    if (tid == 0) {\n        d_CallValue[blockIdx.x] = call[0];\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/binomialOptions_nvrtc/binomialOptions_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_string.h>\n\n#include \"SobelFilter_kernels.h\"\n\n// Texture object for reading image\ncudaTextureObject_t             texObject;\nextern __shared__ unsigned char LocalBlock[];\nstatic cudaArray               *array = NULL;\n\n#define RADIUS 1\n\n#ifdef FIXED_BLOCKWIDTH\n#define BlockWidth  80\n#define SharedPitch 384\n#endif\n\n// This will output the proper CUDA error strings in the event that a CUDA host\n// call returns an error\n#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)\n\ninline void __checkCudaErrors(cudaError err, const char *file, const int line)\n{\n    if (cudaSuccess != err) {\n        fprintf(stderr, \"%s(%i) : CUDA Runtime API error %d: %s.\\n\", file, line, (int)err, cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n}\n\n__device__ unsigned char ComputeSobel(unsigned char ul, // upper left\n                                      unsigned char um, // upper middle\n                                      unsigned char ur, // upper right\n                                      unsigned char ml, // middle left\n                                      unsigned char mm, // middle (unused)\n                                      unsigned char mr, // middle right\n                                      unsigned char ll, // lower left\n                                      unsigned char lm, // lower middle\n                                      unsigned char lr, // lower right\n                                      float         fScale)\n{\n    short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;\n    short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;\n    short Sum  = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));\n\n    if (Sum < 0) {\n        return 0;\n    }\n    else if (Sum > 0xff) {\n        return 0xff;\n    }\n\n    return (unsigned char)Sum;\n}\n\n__global__ void SobelShared(uchar4        *pSobelOriginal,\n                            unsigned short SobelPitch,\n#ifndef FIXED_BLOCKWIDTH\n                            short BlockWidth,\n                            short SharedPitch,\n#endif\n                            short               w,\n                            short               h,\n                            float               fScale,\n                            cudaTextureObject_t tex)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    short            u   = 4 * blockIdx.x * BlockWidth;\n    short            v   = blockIdx.y * blockDim.y + threadIdx.y;\n    short            ib;\n\n    int SharedIdx = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n        LocalBlock[SharedIdx + 4 * ib + 0] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 1] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 2] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 3] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));\n    }\n\n    if (threadIdx.y < RADIUS * 2) {\n        //\n        // copy trailing RADIUS*2 rows of pixels into shared\n        //\n        SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;\n\n        for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n            LocalBlock[SharedIdx + 4 * ib + 0] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 1] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 2] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 3] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v + blockDim.y - RADIUS));\n        }\n    }\n\n    cg::sync(cta);\n\n    u >>= 2; // index as uchar4 from here\n    uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);\n    SharedIdx      = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {\n        unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];\n        unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];\n        unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];\n        unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];\n        unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];\n        unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];\n        unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];\n        unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];\n        unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];\n\n        uchar4 out;\n\n        out.x = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];\n        pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];\n        pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];\n        out.y = ComputeSobel(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22, pix20, fScale);\n\n        pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];\n        pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];\n        pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];\n        out.z = ComputeSobel(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20, pix21, fScale);\n\n        pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];\n        pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];\n        pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];\n        out.w = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (u + ib < w / 4 && v < h) {\n            pSobel[u + ib] = out;\n        }\n    }\n\n    cg::sync(cta);\n}\n\n__global__ void\nSobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fscale, cudaTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        pSobel[i] = min(max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale), 0.f), 255.f);\n    }\n}\n\n__global__ void SobelTex(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fScale, cudaTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        unsigned char pix00 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);\n        unsigned char pix01 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);\n        unsigned char pix02 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);\n        unsigned char pix10 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);\n        unsigned char pix11 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);\n        unsigned char pix12 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);\n        unsigned char pix20 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);\n        unsigned char pix21 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);\n        unsigned char pix22 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);\n        pSobel[i]           = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n    }\n}\n\nextern \"C\" void setupTexture(int iw, int ih, Pixel *data, int Bpp)\n{\n    cudaChannelFormatDesc desc;\n\n    if (Bpp == 1) {\n        desc = cudaCreateChannelDesc<unsigned char>();\n    }\n    else {\n        desc = cudaCreateChannelDesc<uchar4>();\n    }\n\n    checkCudaErrors(cudaMallocArray(&array, &desc, iw, ih));\n    checkCudaErrors(cudaMemcpy2DToArray(\n        array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih, cudaMemcpyHostToDevice));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void deleteTexture(void)\n{\n    checkCudaErrors(cudaFreeArray(array));\n    checkCudaErrors(cudaDestroyTextureObject(texObject));\n}\n\n// Wrapper for the __global__ call that sets up the texture and threads\nextern \"C\" void sobelFilter(Pixel *odata, int iw, int ih, enum SobelDisplayMode mode, float fScale)\n{\n    switch (mode) {\n    case SOBELDISPLAY_IMAGE:\n        SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);\n        break;\n\n    case SOBELDISPLAY_SOBELTEX:\n        SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);\n        break;\n\n    case SOBELDISPLAY_SOBELSHARED: {\n        dim3 threads(16, 4);\n#ifndef FIXED_BLOCKWIDTH\n        int BlockWidth = 80; // must be divisible by 16 for coalescing\n#endif\n        dim3 blocks =\n            dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)), ih / threads.y + (0 != ih % threads.y));\n        int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);\n        int sharedMem   = SharedPitch * (threads.y + 2 * RADIUS);\n\n        // for the shared kernel, width must be divisible by 4\n        iw &= ~3;\n\n        SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata,\n                                                    iw,\n#ifndef FIXED_BLOCKWIDTH\n                                                    BlockWidth,\n                                                    SharedPitch,\n#endif\n                                                    iw,\n                                                    ih,\n                                                    fScale,\n                                                    texObject);\n    } break;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_string.h>\n\n#include \"SobelFilter_kernels.h\"\n\n// Texture object for reading image\nhipTextureObject_t             texObject;\nextern __shared__ unsigned char LocalBlock[];\nstatic hipArray               *array = NULL;\n\n#define RADIUS 1\n\n#ifdef FIXED_BLOCKWIDTH\n#define BlockWidth  80\n#define SharedPitch 384\n#endif\n\n// This will output the proper CUDA error strings in the event that a CUDA host\n// call returns an error\n#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)\n\ninline void __checkCudaErrors(hipError_t err, const char *file, const int line)\n{\n    if (hipSuccess != err) {\n        fprintf(stderr, \"%s(%i) : CUDA Runtime API error %d: %s.\\n\", file, line, (int)err, hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n}\n\n__device__ unsigned char ComputeSobel(unsigned char ul, // upper left\n                                      unsigned char um, // upper middle\n                                      unsigned char ur, // upper right\n                                      unsigned char ml, // middle left\n                                      unsigned char mm, // middle (unused)\n                                      unsigned char mr, // middle right\n                                      unsigned char ll, // lower left\n                                      unsigned char lm, // lower middle\n                                      unsigned char lr, // lower right\n                                      float         fScale)\n{\n    short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;\n    short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;\n    short Sum  = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));\n\n    if (Sum < 0) {\n        return 0;\n    }\n    else if (Sum > 0xff) {\n        return 0xff;\n    }\n\n    return (unsigned char)Sum;\n}\n\n__global__ void SobelShared(uchar4        *pSobelOriginal,\n                            unsigned short SobelPitch,\n#ifndef FIXED_BLOCKWIDTH\n                            short BlockWidth,\n                            short SharedPitch,\n#endif\n                            short               w,\n                            short               h,\n                            float               fScale,\n                            hipTextureObject_t tex)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    short            u   = 4 * blockIdx.x * BlockWidth;\n    short            v   = blockIdx.y * blockDim.y + threadIdx.y;\n    short            ib;\n\n    int SharedIdx = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n        LocalBlock[SharedIdx + 4 * ib + 0] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 1] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 2] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 3] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));\n    }\n\n    if (threadIdx.y < RADIUS * 2) {\n        //\n        // copy trailing RADIUS*2 rows of pixels into shared\n        //\n        SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;\n\n        for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n            LocalBlock[SharedIdx + 4 * ib + 0] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 1] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 2] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 3] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v + blockDim.y - RADIUS));\n        }\n    }\n\n    cg::sync(cta);\n\n    u >>= 2; // index as uchar4 from here\n    uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);\n    SharedIdx      = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {\n        unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];\n        unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];\n        unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];\n        unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];\n        unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];\n        unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];\n        unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];\n        unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];\n        unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];\n\n        uchar4 out;\n\n        out.x = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];\n        pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];\n        pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];\n        out.y = ComputeSobel(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22, pix20, fScale);\n\n        pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];\n        pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];\n        pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];\n        out.z = ComputeSobel(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20, pix21, fScale);\n\n        pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];\n        pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];\n        pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];\n        out.w = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (u + ib < w / 4 && v < h) {\n            pSobel[u + ib] = out;\n        }\n    }\n\n    cg::sync(cta);\n}\n\n__global__ void\nSobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fscale, hipTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        pSobel[i] = min(max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale), 0.f), 255.f);\n    }\n}\n\n__global__ void SobelTex(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fScale, hipTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        unsigned char pix00 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);\n        unsigned char pix01 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);\n        unsigned char pix02 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);\n        unsigned char pix10 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);\n        unsigned char pix11 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);\n        unsigned char pix12 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);\n        unsigned char pix20 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);\n        unsigned char pix21 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);\n        unsigned char pix22 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);\n        pSobel[i]           = ComputeSobel(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n    }\n}\n\nextern \"C\" void setupTexture(int iw, int ih, Pixel *data, int Bpp)\n{\n    hipChannelFormatDesc desc;\n\n    if (Bpp == 1) {\n        desc = hipCreateChannelDesc<unsigned char>();\n    }\n    else {\n        desc = hipCreateChannelDesc<uchar4>();\n    }\n\n    checkCudaErrors(hipMallocArray(&array, &desc, iw, ih));\n    checkCudaErrors(hipMemcpy2DToArray(\n        array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih, hipMemcpyHostToDevice));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&texObject, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void deleteTexture(void)\n{\n    checkCudaErrors(hipFreeArray(array));\n    checkCudaErrors(hipDestroyTextureObject(texObject));\n}\n\n// Wrapper for the __global__ call that sets up the texture and threads\nextern \"C\" void sobelFilter(Pixel *odata, int iw, int ih, enum SobelDisplayMode mode, float fScale)\n{\n    switch (mode) {\n    case SOBELDISPLAY_IMAGE:\n        SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);\n        break;\n\n    case SOBELDISPLAY_SOBELTEX:\n        SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, texObject);\n        break;\n\n    case SOBELDISPLAY_SOBELSHARED: {\n        dim3 threads(16, 4);\n#ifndef FIXED_BLOCKWIDTH\n        int BlockWidth = 80; // must be divisible by 16 for coalescing\n#endif\n        dim3 blocks =\n            dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)), ih / threads.y + (0 != ih % threads.y));\n        int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);\n        int sharedMem   = SharedPitch * (threads.y + 2 * RADIUS);\n\n        // for the shared kernel, width must be divisible by 4\n        iw &= ~3;\n\n        SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata,\n                                                    iw,\n#ifndef FIXED_BLOCKWIDTH\n                                                    BlockWidth,\n                                                    SharedPitch,\n#endif\n                                                    iw,\n                                                    ih,\n                                                    fScale,\n                                                    texObject);\n    } break;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/SobelFilter/SobelFilter_kernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _VOLUMEFILTER_KERNEL_CU_\n#define _VOLUMEFILTER_KERNEL_CU_\n\n#include <helper_cuda.h>\n#include <helper_math.h>\n\n#include \"volumeFilter.h\"\n\ntypedef unsigned int   uint;\ntypedef unsigned char  uchar;\ntypedef unsigned short ushort;\n\n__constant__ float4 c_filterData[VOLUMEFILTER_MAXWEIGHTS];\n\n__global__ void d_filter_surface3d(int                 filterSize,\n                                   float               filter_offset,\n                                   cudaExtent          volumeSize,\n                                   cudaTextureObject_t volumeTexIn,\n                                   cudaSurfaceObject_t volumeTexOut)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n    int z = blockIdx.z * blockDim.z + threadIdx.z;\n\n    if (x >= volumeSize.width || y >= volumeSize.height || z >= volumeSize.depth) {\n        return;\n    }\n\n    float  filtered  = 0;\n    float4 basecoord = make_float4(x, y, z, 0);\n\n    for (int i = 0; i < filterSize; i++) {\n        float4 coord = basecoord + c_filterData[i];\n        filtered += tex3D<float>(volumeTexIn, coord.x, coord.y, coord.z) * c_filterData[i].w;\n    }\n\n    filtered += filter_offset;\n\n    VolumeType output = VolumeTypeInfo<VolumeType>::convert(filtered);\n\n    // surface writes need byte offsets for x!\n    surf3Dwrite(output, volumeTexOut, x * sizeof(VolumeType), y, z);\n}\n\nstatic unsigned int iDivUp(size_t a, size_t b)\n{\n    size_t val = (a % b != 0) ? (a / b + 1) : (a / b);\n    if (val > UINT_MAX) {\n        fprintf(stderr, \"\\nUINT_MAX limit exceeded in iDivUp() exiting.....\\n\");\n        exit(EXIT_FAILURE); // val exceeds limit\n    }\n\n    return static_cast<unsigned int>(val);\n}\n\nextern \"C\" Volume *VolumeFilter_runFilter(Volume *input,\n                                          Volume *output0,\n                                          Volume *output1,\n                                          int     iterations,\n                                          int     numWeights,\n                                          float4 *weights,\n                                          float   postWeightOffset)\n{\n    Volume      *swap = 0;\n    cudaExtent   size = input->size;\n    unsigned int dim  = 32 / sizeof(VolumeType);\n    dim3         blockSize(dim, dim, 1);\n    dim3 gridSize(iDivUp(size.width, blockSize.x), iDivUp(size.height, blockSize.y), iDivUp(size.depth, blockSize.z));\n\n    // set weights\n    checkCudaErrors(cudaMemcpyToSymbol(c_filterData, weights, sizeof(float4) * numWeights));\n\n    for (int i = 0; i < iterations; i++) {\n        d_filter_surface3d<<<gridSize, blockSize>>>(\n            numWeights, postWeightOffset, size, input->volumeTex, output0->volumeSurf);\n\n        getLastCudaError(\"filter kernel failed\");\n\n        swap    = input;\n        input   = output0;\n        output0 = swap;\n\n        if (i == 0) {\n            output0 = output1;\n        }\n    }\n\n    return input;\n}\n#endif\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef _VOLUMEFILTER_KERNEL_CU_\n#define _VOLUMEFILTER_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n\n#include \"volumeFilter.h\"\n\ntypedef unsigned int   uint;\ntypedef unsigned char  uchar;\ntypedef unsigned short ushort;\n\n__constant__ float4 c_filterData[VOLUMEFILTER_MAXWEIGHTS];\n\n__global__ void d_filter_surface3d(int                 filterSize,\n                                   float               filter_offset,\n                                   hipExtent          volumeSize,\n                                   hipTextureObject_t volumeTexIn,\n                                   hipSurfaceObject_t volumeTexOut)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n    int z = blockIdx.z * blockDim.z + threadIdx.z;\n\n    if (x >= volumeSize.width || y >= volumeSize.height || z >= volumeSize.depth) {\n        return;\n    }\n\n    float  filtered  = 0;\n    float4 basecoord = make_float4(x, y, z, 0);\n\n    for (int i = 0; i < filterSize; i++) {\n        float4 coord = basecoord + c_filterData[i];\n        filtered += tex3D<float>(volumeTexIn, coord.x, coord.y, coord.z) * c_filterData[i].w;\n    }\n\n    filtered += filter_offset;\n\n    VolumeType output = VolumeTypeInfo<VolumeType>::convert(filtered);\n\n    // surface writes need byte offsets for x!\n    surf3Dwrite(output, volumeTexOut, x * sizeof(VolumeType), y, z);\n}\n\nstatic unsigned int iDivUp(size_t a, size_t b)\n{\n    size_t val = (a % b != 0) ? (a / b + 1) : (a / b);\n    if (val > UINT_MAX) {\n        fprintf(stderr, \"\\nUINT_MAX limit exceeded in iDivUp() exiting.....\\n\");\n        exit(EXIT_FAILURE); // val exceeds limit\n    }\n\n    return static_cast<unsigned int>(val);\n}\n\nextern \"C\" Volume *VolumeFilter_runFilter(Volume *input,\n                                          Volume *output0,\n                                          Volume *output1,\n                                          int     iterations,\n                                          int     numWeights,\n                                          float4 *weights,\n                                          float   postWeightOffset)\n{\n    Volume      *swap = 0;\n    hipExtent   size = input->size;\n    unsigned int dim  = 32 / sizeof(VolumeType);\n    dim3         blockSize(dim, dim, 1);\n    dim3 gridSize(iDivUp(size.width, blockSize.x), iDivUp(size.height, blockSize.y), iDivUp(size.depth, blockSize.z));\n\n    // set weights\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(c_filterData), weights, sizeof(float4) * numWeights));\n\n    for (int i = 0; i < iterations; i++) {\n        d_filter_surface3d<<<gridSize, blockSize>>>(\n            numWeights, postWeightOffset, size, input->volumeTex, output0->volumeSurf);\n\n        getLastCudaError(\"filter kernel failed\");\n\n        swap    = input;\n        input   = output0;\n        output0 = swap;\n\n        if (i == 0) {\n            output0 = output1;\n        }\n    }\n\n    return input;\n}\n#endif\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/volumeFiltering/volumeFilter_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple 3D volume renderer\n\n#ifndef _VOLUMERENDER_KERNEL_CU_\n#define _VOLUMERENDER_KERNEL_CU_\n\n#include <helper_cuda.h>\n#include <helper_math.h>\n\n#include \"volumeRender.h\"\n\n#define VOLUMERENDER_TFS            2\n#define VOLUMERENDER_TF_PREINTSIZE  1024\n#define VOLUMERENDER_TF_PREINTSTEPS 1024\n#define VOLUMERENDER_TF_PREINTRAY   4\n\nenum TFMode {\n    TF_SINGLE_1D         = 0, // single 1D TF for everything\n    TF_LAYERED_2D_PREINT = 1, // layered 2D TF uses pre-integration\n    TF_LAYERED_2D        = 2, // layered 2D TF without pre-integration behavior\n};\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\nstatic bool       usePreInt           = true;\nstatic cudaArray *d_transferIntegrate = 0;\nstatic cudaArray *d_transferFunc      = 0;\nstatic cudaArray *d_transferArray     = 0;\n\n// 1D transfer function texture\ncudaTextureObject_t transferTex;\n// 1D transfer integration texture\ncudaTextureObject_t transferIntegrateTex;\ncudaSurfaceObject_t transferIntegrateSurf;\n// 2D layered preintegrated transfer function texture\ncudaTextureObject_t transferLayerPreintTex;\ncudaSurfaceObject_t transferLayerPreintSurf;\n\ntypedef struct\n{\n    float4 m[3];\n} float3x4;\n\n__constant__ float3x4 c_invViewMatrix; // inverse view matrix\n\nstruct Ray\n{\n    float3 o; // origin\n    float3 d; // direction\n};\n\n// intersect ray with a box\n// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm\n\n__device__ int intersectBox(Ray r, float3 boxmin, float3 boxmax, float *tnear, float *tfar)\n{\n    // compute intersection of ray with all six bbox planes\n    float3 invR = make_float3(1.0f) / r.d;\n    float3 tbot = invR * (boxmin - r.o);\n    float3 ttop = invR * (boxmax - r.o);\n\n    // re-order intersections to find smallest and largest on each axis\n    float3 tmin = fminf(ttop, tbot);\n    float3 tmax = fmaxf(ttop, tbot);\n\n    // find the largest tmin and the smallest tmax\n    float largest_tmin  = fmaxf(fmaxf(tmin.x, tmin.y), fmaxf(tmin.x, tmin.z));\n    float smallest_tmax = fminf(fminf(tmax.x, tmax.y), fminf(tmax.x, tmax.z));\n\n    *tnear = largest_tmin;\n    *tfar  = smallest_tmax;\n\n    return smallest_tmax > largest_tmin;\n}\n\n// transform vector by matrix (no translation)\n__device__ float3 mul(const float3x4 &M, const float3 &v)\n{\n    float3 r;\n    r.x = dot(v, make_float3(M.m[0]));\n    r.y = dot(v, make_float3(M.m[1]));\n    r.z = dot(v, make_float3(M.m[2]));\n    return r;\n}\n\n// transform vector by matrix with translation\n__device__ float4 mul(const float3x4 &M, const float4 &v)\n{\n    float4 r;\n    r.x = dot(v, M.m[0]);\n    r.y = dot(v, M.m[1]);\n    r.z = dot(v, M.m[2]);\n    r.w = 1.0f;\n    return r;\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return (uint(rgba.w * 255) << 24) | (uint(rgba.z * 255) << 16) | (uint(rgba.y * 255) << 8) | uint(rgba.x * 255);\n}\n\ntemplate <int TFMODE>\n__device__ void d_render(uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         cudaTextureObject_t volumeTex,\n                         cudaTextureObject_t transferTex,\n                         cudaTextureObject_t transferLayerPreintTex,\n                         float               transferWeight = 0.0f)\n{\n    const float  rayscale         = float(TFMODE != TF_SINGLE_1D ? VOLUMERENDER_TF_PREINTRAY : 1);\n    const int    maxSteps         = 512;\n    const float  tstep            = 0.01f * rayscale;\n    const float  opacityThreshold = 0.95f;\n    const float3 boxMin           = make_float3(-1.0f, -1.0f, -1.0f);\n    const float3 boxMax           = make_float3(1.0f, 1.0f, 1.0f);\n\n    density *= rayscale;\n\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if ((x >= imageW) || (y >= imageH))\n        return;\n\n    float u = (x / (float)imageW) * 2.0f - 1.0f;\n    float v = (y / (float)imageH) * 2.0f - 1.0f;\n\n    // calculate eye ray in world space\n    Ray eyeRay;\n    eyeRay.o = make_float3(mul(c_invViewMatrix, make_float4(0.0f, 0.0f, 0.0f, 1.0f)));\n    eyeRay.d = normalize(make_float3(u, v, -2.0f));\n    eyeRay.d = mul(c_invViewMatrix, eyeRay.d);\n\n    // find intersection with box\n    float tnear, tfar;\n    int   hit = intersectBox(eyeRay, boxMin, boxMax, &tnear, &tfar);\n\n    if (!hit)\n        return;\n\n    if (tnear < 0.0f)\n        tnear = 0.0f; // clamp to near plane\n\n    // march along ray from front to back, accumulating color\n    float4 sum  = make_float4(0.0f);\n    float  t    = tnear;\n    float3 pos  = eyeRay.o + eyeRay.d * tnear;\n    float3 step = eyeRay.d * tstep;\n\n    float lastsample = 0;\n\n    // lastsample = (lastsample-transferOffset)*transferScale;\n    for (int i = 0; i < maxSteps; i++) {\n        // read from 3D texture\n        // remap position to [0, 1] coordinates\n        float3 coord  = make_float3(pos.x * 0.5f + 0.5f, pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f);\n        float  sample = tex3D<float>(volumeTex, coord.x, coord.y, coord.z);\n        // sample = (sample-transferOffset)*transferScale;\n        // sample *= 64.0f;    // scale for 10-bit data\n\n        // lookup in transfer function texture\n        float4 col;\n        int    tfid = (pos.x < 0);\n\n        if (TFMODE != TF_SINGLE_1D) {\n            col = tex2DLayered<float4>(\n                transferLayerPreintTex, sample, TFMODE == TF_LAYERED_2D ? sample : lastsample, tfid);\n            col.w *= density;\n            lastsample = sample;\n        }\n        else {\n            col = tex1D<float4>(transferTex, sample);\n            col.w *= 0;\n        }\n\n        // \"under\" operator for back-to-front blending\n        // sum = lerp(sum, col, col.w);\n\n        // pre-multiply alpha\n        col.x *= col.w;\n        col.y *= col.w;\n        col.z *= col.w;\n        // \"over\" operator for front-to-back blending\n        sum = sum + col * (1.0f - sum.w);\n\n        // exit early if opaque\n        if (sum.w > opacityThreshold)\n            break;\n\n        t += tstep;\n\n        if (t > tfar)\n            break;\n\n        pos += step;\n    }\n\n    sum *= brightness;\n\n    // write output color\n    d_output[y * imageW + x] = rgbaFloatToInt(sum);\n}\n\n__global__ void d_render_regular(uint               *d_output,\n                                 uint                imageW,\n                                 uint                imageH,\n                                 float               density,\n                                 float               brightness,\n                                 float               transferOffset,\n                                 float               transferScale,\n                                 cudaTextureObject_t volumeTex,\n                                 cudaTextureObject_t transferTex,\n                                 cudaTextureObject_t transferLayerPreintTex,\n                                 float               transferWeight = 0.0f)\n{\n    d_render<TF_SINGLE_1D>(d_output,\n                           imageW,\n                           imageH,\n                           density,\n                           brightness,\n                           transferOffset,\n                           transferScale,\n                           volumeTex,\n                           transferTex,\n                           transferLayerPreintTex,\n                           transferWeight);\n}\n\n__global__ void d_render_preint(uint               *d_output,\n                                uint                imageW,\n                                uint                imageH,\n                                float               density,\n                                float               brightness,\n                                float               transferOffset,\n                                float               transferScale,\n                                cudaTextureObject_t volumeTex,\n                                cudaTextureObject_t transferTex,\n                                cudaTextureObject_t transferLayerPreintTex,\n                                float               transferWeight = 0.0f)\n{\n    d_render<TF_LAYERED_2D_PREINT>(d_output,\n                                   imageW,\n                                   imageH,\n                                   density,\n                                   brightness,\n                                   transferOffset,\n                                   transferScale,\n                                   volumeTex,\n                                   transferTex,\n                                   transferLayerPreintTex,\n                                   transferWeight);\n}\n\n__global__ void d_render_preint_off(uint               *d_output,\n                                    uint                imageW,\n                                    uint                imageH,\n                                    float               density,\n                                    float               brightness,\n                                    float               transferOffset,\n                                    float               transferScale,\n                                    cudaTextureObject_t volumeTex,\n                                    cudaTextureObject_t transferTex,\n                                    cudaTextureObject_t transferLayerPreintTex,\n                                    float               transferWeight = 0.0f)\n{\n    d_render<TF_LAYERED_2D>(d_output,\n                            imageW,\n                            imageH,\n                            density,\n                            brightness,\n                            transferOffset,\n                            transferScale,\n                            volumeTex,\n                            transferTex,\n                            transferLayerPreintTex,\n                            transferWeight);\n}\n\n//////////////////////////////////////////////////////////////////////////\n\n__global__ void\nd_integrate_trapezoidal(cudaExtent extent, cudaTextureObject_t transferTex, cudaSurfaceObject_t transferIntegrateSurf)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // for higher speed could use hierarchical approach for sum\n    if (x >= extent.width) {\n        return;\n    }\n\n    float stepsize = 1.0 / float(extent.width - 1);\n    float to       = float(x) * stepsize;\n\n    float4 outclr = make_float4(0, 0, 0, 0);\n    float  incr   = stepsize;\n\n    float4 lastval = tex1D<float4>(transferTex, 0);\n\n    float cur = incr;\n\n    while (cur < to + incr * 0.5) {\n        float4 val       = tex1D<float4>(transferTex, cur);\n        float4 trapezoid = (lastval + val) / 2.0f;\n        lastval          = val;\n\n        outclr += trapezoid;\n        cur += incr;\n    }\n\n    // surface writes need byte offsets for x!\n    surf1Dwrite(outclr, transferIntegrateSurf, x * sizeof(float4));\n}\n\n__global__ void d_preintegrate(int                 layer,\n                               float               steps,\n                               cudaExtent          extent,\n                               cudaTextureObject_t transferTex,\n                               cudaTextureObject_t transferIntegrateTex,\n                               cudaSurfaceObject_t transferLayerPreintSurf)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (x >= extent.width || y >= extent.height) {\n        return;\n    }\n\n    float sx = float(x) / float(extent.width);\n    float sy = float(y) / float(extent.height);\n\n    float smax = max(sx, sy);\n    float smin = min(sx, sy);\n\n    float4 iv;\n\n    if (x != y) {\n        // assumes square textures!\n        float fracc = smax - smin;\n        fracc       = 1.0 / (fracc * steps);\n\n        float4 intmax = tex1D<float4>(transferIntegrateTex, smax);\n        float4 intmin = tex1D<float4>(transferIntegrateTex, smin);\n        iv.x          = (intmax.x - intmin.x) * fracc;\n        iv.y          = (intmax.y - intmin.y) * fracc;\n        iv.z          = (intmax.z - intmin.z) * fracc;\n        // iv.w = (intmax.w - intmin.w)*fracc;\n        iv.w = (1.0 - exp(-(intmax.w - intmin.w) * fracc));\n    }\n    else {\n        float4 sample = tex1D<float4>(transferTex, smin);\n        iv.x          = sample.x;\n        iv.y          = sample.y;\n        iv.z          = sample.z;\n        // iv.w = sample.w;\n        iv.w = (1.0 - exp(-sample.w));\n    }\n\n    iv.x = __saturatef(iv.x);\n    iv.y = __saturatef(iv.y);\n    iv.z = __saturatef(iv.z);\n    iv.w = __saturatef(iv.w);\n\n    // surface writes need byte offsets for x!\n    surf2DLayeredwrite(iv, transferLayerPreintSurf, x * sizeof(float4), y, layer);\n}\n\n//////////////////////////////////////////////////////////////////////////\n\nvoid VolumeRender_setTextureFilterMode(bool bLinearFilter, Volume *vol)\n{\n    if (vol->volumeTex) {\n        checkCudaErrors(cudaDestroyTextureObject(vol->volumeTex));\n    }\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = vol->content;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? cudaFilterModeLinear : cudaFilterModePoint;\n\n    texDescr.addressMode[0] = cudaAddressModeWrap;\n    texDescr.addressMode[1] = cudaAddressModeWrap;\n    texDescr.addressMode[2] = cudaAddressModeWrap;\n\n    texDescr.readMode = VolumeTypeInfo<VolumeType>::readMode;\n\n    checkCudaErrors(cudaCreateTextureObject(&vol->volumeTex, &texRes, &texDescr, NULL));\n}\n\nstatic unsigned int iDivUp(size_t a, size_t b)\n{\n    size_t val = (a % b != 0) ? (a / b + 1) : (a / b);\n    if (val > UINT_MAX) {\n        fprintf(stderr, \"\\nUINT_MAX limit exceeded in iDivUp() exiting.....\\n\");\n        exit(EXIT_FAILURE); // val exceeds limit\n    }\n\n    return static_cast<unsigned int>(val);\n}\n\nvoid VolumeRender_updateTF(int tfIdx, int numColors, float4 *colors)\n{\n    if (d_transferFunc) {\n        checkCudaErrors(cudaFreeArray(d_transferFunc));\n        d_transferFunc = 0;\n    }\n\n    cudaChannelFormatDesc channelFloat4 = cudaCreateChannelDesc<float4>();\n    checkCudaErrors(cudaMallocArray(&d_transferFunc, &channelFloat4, numColors, 1));\n    checkCudaErrors(\n        cudaMemcpy2DToArray(d_transferFunc, 0, 0, colors, 0, sizeof(float4) * numColors, 1, cudaMemcpyHostToDevice));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_transferFunc;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&transferTex, &texRes, &texDescr, NULL));\n\n    if (tfIdx < 0 || tfIdx >= VOLUMERENDER_TFS) {\n        return;\n    }\n\n    {\n        cudaExtent extent = {VOLUMERENDER_TF_PREINTSTEPS, 0, 0};\n        dim3       blockSize(32, 1, 1);\n        dim3       gridSize(iDivUp(extent.width, blockSize.x), 1, 1);\n        d_integrate_trapezoidal<<<gridSize, blockSize>>>(extent, transferTex, transferIntegrateSurf);\n    }\n\n    {\n        cudaExtent extent = {VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TFS};\n        dim3       blockSize(16, 16, 1);\n        dim3       gridSize(iDivUp(extent.width, blockSize.x), iDivUp(extent.height, blockSize.y), 1);\n        d_preintegrate<<<gridSize, blockSize>>>(tfIdx,\n                                                float(VOLUMERENDER_TF_PREINTSTEPS),\n                                                extent,\n                                                transferTex,\n                                                transferIntegrateTex,\n                                                transferLayerPreintSurf);\n    }\n}\n\nvoid VolumeRender_init()\n{\n    cudaResourceDesc texRes;\n    cudaTextureDesc  texDescr;\n\n    cudaChannelFormatDesc channelFloat4 = cudaCreateChannelDesc<float4>();\n    cudaExtent            extent        = {VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TFS};\n    checkCudaErrors(\n        cudaMalloc3DArray(&d_transferArray, &channelFloat4, extent, cudaArrayLayered | cudaArraySurfaceLoadStore));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_transferArray;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.addressMode[1]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&transferLayerPreintTex, &texRes, &texDescr, NULL));\n\n    cudaResourceDesc surfRes;\n    memset(&surfRes, 0, sizeof(cudaResourceDesc));\n    surfRes.resType         = cudaResourceTypeArray;\n    surfRes.res.array.array = d_transferArray;\n\n    checkCudaErrors(cudaCreateSurfaceObject(&transferLayerPreintSurf, &surfRes));\n\n    checkCudaErrors(cudaMallocArray(\n        &d_transferIntegrate, &channelFloat4, VOLUMERENDER_TF_PREINTSTEPS, 0, cudaArraySurfaceLoadStore));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_transferIntegrate;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModeLinear;\n\n    texDescr.addressMode[0] = cudaAddressModeClamp;\n    texDescr.addressMode[1] = cudaAddressModeClamp;\n    texDescr.addressMode[2] = cudaAddressModeClamp;\n\n    texDescr.readMode = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&transferIntegrateTex, &texRes, &texDescr, NULL));\n\n    memset(&surfRes, 0, sizeof(cudaResourceDesc));\n    surfRes.resType         = cudaResourceTypeArray;\n    surfRes.res.array.array = d_transferIntegrate;\n\n    checkCudaErrors(cudaCreateSurfaceObject(&transferIntegrateSurf, &surfRes));\n\n    // create transfer function texture\n    float4 transferFunc0[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            1.0,\n            0.0,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.5,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    float4 transferFunc1[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            0.125,\n        },\n        {\n            0.0,\n            0.5,\n            1.0,\n            0.125,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            0.125,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            0.125,\n        },\n        {\n            0.25,\n            0.75,\n            0.0,\n            1.0,\n        },\n        {\n            0.75,\n            0.25,\n            0.0,\n            0.125,\n        },\n        {\n            1.0,\n            0.75,\n            0.0,\n            0.125,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    VolumeRender_updateTF(1, sizeof(transferFunc1) / sizeof(float4), transferFunc1);\n    VolumeRender_updateTF(0, sizeof(transferFunc0) / sizeof(float4), transferFunc0);\n}\n\nvoid VolumeRender_deinit()\n{\n    checkCudaErrors(cudaDestroyTextureObject(transferTex));\n    checkCudaErrors(cudaDestroyTextureObject(transferIntegrateTex));\n    checkCudaErrors(cudaDestroySurfaceObject(transferIntegrateSurf));\n    checkCudaErrors(cudaDestroyTextureObject(transferLayerPreintTex));\n    checkCudaErrors(cudaDestroySurfaceObject(transferLayerPreintSurf));\n    checkCudaErrors(cudaFreeArray(d_transferFunc));\n    checkCudaErrors(cudaFreeArray(d_transferArray));\n    checkCudaErrors(cudaFreeArray(d_transferIntegrate));\n    d_transferArray     = 0;\n    d_transferFunc      = 0;\n    d_transferIntegrate = 0;\n}\n\nvoid VolumeRender_setPreIntegrated(int state) { usePreInt = !!state; }\n\nvoid VolumeRender_render(dim3                gridSize,\n                         dim3                blockSize,\n                         uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         cudaTextureObject_t volumeTex)\n{\n    if (usePreInt) {\n        d_render_preint<<<gridSize, blockSize>>>(d_output,\n                                                 imageW,\n                                                 imageH,\n                                                 density,\n                                                 brightness,\n                                                 transferOffset,\n                                                 transferScale,\n                                                 volumeTex,\n                                                 transferTex,\n                                                 transferLayerPreintTex);\n    }\n    else {\n        d_render_preint_off<<<gridSize, blockSize>>>(d_output,\n                                                     imageW,\n                                                     imageH,\n                                                     density,\n                                                     brightness,\n                                                     transferOffset,\n                                                     transferScale,\n                                                     volumeTex,\n                                                     transferTex,\n                                                     transferLayerPreintTex);\n    }\n}\n\nvoid VolumeRender_copyInvViewMatrix(float *invViewMatrix, size_t sizeofMatrix)\n{\n    checkCudaErrors(cudaMemcpyToSymbol(c_invViewMatrix, invViewMatrix, sizeofMatrix));\n}\n\n#endif // #ifndef _VOLUMERENDER_KERNEL_CU_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple 3D volume renderer\n\n#include \"hip/hip_runtime.h\"\n#ifndef _VOLUMERENDER_KERNEL_CU_\n#define _VOLUMERENDER_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n\n#include \"volumeRender.h\"\n\n#define VOLUMERENDER_TFS            2\n#define VOLUMERENDER_TF_PREINTSIZE  1024\n#define VOLUMERENDER_TF_PREINTSTEPS 1024\n#define VOLUMERENDER_TF_PREINTRAY   4\n\nenum TFMode {\n    TF_SINGLE_1D         = 0, // single 1D TF for everything\n    TF_LAYERED_2D_PREINT = 1, // layered 2D TF uses pre-integration\n    TF_LAYERED_2D        = 2, // layered 2D TF without pre-integration behavior\n};\n\ntypedef unsigned int  uint;\ntypedef unsigned char uchar;\n\nstatic bool       usePreInt           = true;\nstatic hipArray *d_transferIntegrate = 0;\nstatic hipArray *d_transferFunc      = 0;\nstatic hipArray *d_transferArray     = 0;\n\n// 1D transfer function texture\nhipTextureObject_t transferTex;\n// 1D transfer integration texture\nhipTextureObject_t transferIntegrateTex;\nhipSurfaceObject_t transferIntegrateSurf;\n// 2D layered preintegrated transfer function texture\nhipTextureObject_t transferLayerPreintTex;\nhipSurfaceObject_t transferLayerPreintSurf;\n\ntypedef struct\n{\n    float4 m[3];\n} float3x4;\n\n__constant__ float3x4 c_invViewMatrix; // inverse view matrix\n\nstruct Ray\n{\n    float3 o; // origin\n    float3 d; // direction\n};\n\n// intersect ray with a box\n// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm\n\n__device__ int intersectBox(Ray r, float3 boxmin, float3 boxmax, float *tnear, float *tfar)\n{\n    // compute intersection of ray with all six bbox planes\n    float3 invR = make_float3(1.0f) / r.d;\n    float3 tbot = invR * (boxmin - r.o);\n    float3 ttop = invR * (boxmax - r.o);\n\n    // re-order intersections to find smallest and largest on each axis\n    float3 tmin = fminf(ttop, tbot);\n    float3 tmax = fmaxf(ttop, tbot);\n\n    // find the largest tmin and the smallest tmax\n    float largest_tmin  = fmaxf(fmaxf(tmin.x, tmin.y), fmaxf(tmin.x, tmin.z));\n    float smallest_tmax = fminf(fminf(tmax.x, tmax.y), fminf(tmax.x, tmax.z));\n\n    *tnear = largest_tmin;\n    *tfar  = smallest_tmax;\n\n    return smallest_tmax > largest_tmin;\n}\n\n// transform vector by matrix (no translation)\n__device__ float3 mul(const float3x4 &M, const float3 &v)\n{\n    float3 r;\n    r.x = dot(v, make_float3(M.m[0]));\n    r.y = dot(v, make_float3(M.m[1]));\n    r.z = dot(v, make_float3(M.m[2]));\n    return r;\n}\n\n// transform vector by matrix with translation\n__device__ float4 mul(const float3x4 &M, const float4 &v)\n{\n    float4 r;\n    r.x = dot(v, M.m[0]);\n    r.y = dot(v, M.m[1]);\n    r.z = dot(v, M.m[2]);\n    r.w = 1.0f;\n    return r;\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return (uint(rgba.w * 255) << 24) | (uint(rgba.z * 255) << 16) | (uint(rgba.y * 255) << 8) | uint(rgba.x * 255);\n}\n\ntemplate <int TFMODE>\n__device__ void d_render(uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         hipTextureObject_t volumeTex,\n                         hipTextureObject_t transferTex,\n                         hipTextureObject_t transferLayerPreintTex,\n                         float               transferWeight = 0.0f)\n{\n    const float  rayscale         = float(TFMODE != TF_SINGLE_1D ? VOLUMERENDER_TF_PREINTRAY : 1);\n    const int    maxSteps         = 512;\n    const float  tstep            = 0.01f * rayscale;\n    const float  opacityThreshold = 0.95f;\n    const float3 boxMin           = make_float3(-1.0f, -1.0f, -1.0f);\n    const float3 boxMax           = make_float3(1.0f, 1.0f, 1.0f);\n\n    density *= rayscale;\n\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if ((x >= imageW) || (y >= imageH))\n        return;\n\n    float u = (x / (float)imageW) * 2.0f - 1.0f;\n    float v = (y / (float)imageH) * 2.0f - 1.0f;\n\n    // calculate eye ray in world space\n    Ray eyeRay;\n    eyeRay.o = make_float3(mul(c_invViewMatrix, make_float4(0.0f, 0.0f, 0.0f, 1.0f)));\n    eyeRay.d = normalize(make_float3(u, v, -2.0f));\n    eyeRay.d = mul(c_invViewMatrix, eyeRay.d);\n\n    // find intersection with box\n    float tnear, tfar;\n    int   hit = intersectBox(eyeRay, boxMin, boxMax, &tnear, &tfar);\n\n    if (!hit)\n        return;\n\n    if (tnear < 0.0f)\n        tnear = 0.0f; // clamp to near plane\n\n    // march along ray from front to back, accumulating color\n    float4 sum  = make_float4(0.0f);\n    float  t    = tnear;\n    float3 pos  = eyeRay.o + eyeRay.d * tnear;\n    float3 step = eyeRay.d * tstep;\n\n    float lastsample = 0;\n\n    // lastsample = (lastsample-transferOffset)*transferScale;\n    for (int i = 0; i < maxSteps; i++) {\n        // read from 3D texture\n        // remap position to [0, 1] coordinates\n        float3 coord  = make_float3(pos.x * 0.5f + 0.5f, pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f);\n        float  sample = tex3D<float>(volumeTex, coord.x, coord.y, coord.z);\n        // sample = (sample-transferOffset)*transferScale;\n        // sample *= 64.0f;    // scale for 10-bit data\n\n        // lookup in transfer function texture\n        float4 col;\n        int    tfid = (pos.x < 0);\n\n        if (TFMODE != TF_SINGLE_1D) {\n            col = tex2DLayered<float4>(\n                transferLayerPreintTex, sample, TFMODE == TF_LAYERED_2D ? sample : lastsample, tfid);\n            col.w *= density;\n            lastsample = sample;\n        }\n        else {\n            col = tex1D<float4>(transferTex, sample);\n            col.w *= 0;\n        }\n\n        // \"under\" operator for back-to-front blending\n        // sum = lerp(sum, col, col.w);\n\n        // pre-multiply alpha\n        col.x *= col.w;\n        col.y *= col.w;\n        col.z *= col.w;\n        // \"over\" operator for front-to-back blending\n        sum = sum + col * (1.0f - sum.w);\n\n        // exit early if opaque\n        if (sum.w > opacityThreshold)\n            break;\n\n        t += tstep;\n\n        if (t > tfar)\n            break;\n\n        pos += step;\n    }\n\n    sum *= brightness;\n\n    // write output color\n    d_output[y * imageW + x] = rgbaFloatToInt(sum);\n}\n\n__global__ void d_render_regular(uint               *d_output,\n                                 uint                imageW,\n                                 uint                imageH,\n                                 float               density,\n                                 float               brightness,\n                                 float               transferOffset,\n                                 float               transferScale,\n                                 hipTextureObject_t volumeTex,\n                                 hipTextureObject_t transferTex,\n                                 hipTextureObject_t transferLayerPreintTex,\n                                 float               transferWeight = 0.0f)\n{\n    d_render<TF_SINGLE_1D>(d_output,\n                           imageW,\n                           imageH,\n                           density,\n                           brightness,\n                           transferOffset,\n                           transferScale,\n                           volumeTex,\n                           transferTex,\n                           transferLayerPreintTex,\n                           transferWeight);\n}\n\n__global__ void d_render_preint(uint               *d_output,\n                                uint                imageW,\n                                uint                imageH,\n                                float               density,\n                                float               brightness,\n                                float               transferOffset,\n                                float               transferScale,\n                                hipTextureObject_t volumeTex,\n                                hipTextureObject_t transferTex,\n                                hipTextureObject_t transferLayerPreintTex,\n                                float               transferWeight = 0.0f)\n{\n    d_render<TF_LAYERED_2D_PREINT>(d_output,\n                                   imageW,\n                                   imageH,\n                                   density,\n                                   brightness,\n                                   transferOffset,\n                                   transferScale,\n                                   volumeTex,\n                                   transferTex,\n                                   transferLayerPreintTex,\n                                   transferWeight);\n}\n\n__global__ void d_render_preint_off(uint               *d_output,\n                                    uint                imageW,\n                                    uint                imageH,\n                                    float               density,\n                                    float               brightness,\n                                    float               transferOffset,\n                                    float               transferScale,\n                                    hipTextureObject_t volumeTex,\n                                    hipTextureObject_t transferTex,\n                                    hipTextureObject_t transferLayerPreintTex,\n                                    float               transferWeight = 0.0f)\n{\n    d_render<TF_LAYERED_2D>(d_output,\n                            imageW,\n                            imageH,\n                            density,\n                            brightness,\n                            transferOffset,\n                            transferScale,\n                            volumeTex,\n                            transferTex,\n                            transferLayerPreintTex,\n                            transferWeight);\n}\n\n//////////////////////////////////////////////////////////////////////////\n\n__global__ void\nd_integrate_trapezoidal(hipExtent extent, hipTextureObject_t transferTex, hipSurfaceObject_t transferIntegrateSurf)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // for higher speed could use hierarchical approach for sum\n    if (x >= extent.width) {\n        return;\n    }\n\n    float stepsize = 1.0 / float(extent.width - 1);\n    float to       = float(x) * stepsize;\n\n    float4 outclr = make_float4(0, 0, 0, 0);\n    float  incr   = stepsize;\n\n    float4 lastval = tex1D<float4>(transferTex, 0);\n\n    float cur = incr;\n\n    while (cur < to + incr * 0.5) {\n        float4 val       = tex1D<float4>(transferTex, cur);\n        float4 trapezoid = (lastval + val) / 2.0f;\n        lastval          = val;\n\n        outclr += trapezoid;\n        cur += incr;\n    }\n\n    // surface writes need byte offsets for x!\n    surf1Dwrite(outclr, transferIntegrateSurf, x * sizeof(float4));\n}\n\n__global__ void d_preintegrate(int                 layer,\n                               float               steps,\n                               hipExtent          extent,\n                               hipTextureObject_t transferTex,\n                               hipTextureObject_t transferIntegrateTex,\n                               hipSurfaceObject_t transferLayerPreintSurf)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (x >= extent.width || y >= extent.height) {\n        return;\n    }\n\n    float sx = float(x) / float(extent.width);\n    float sy = float(y) / float(extent.height);\n\n    float smax = max(sx, sy);\n    float smin = min(sx, sy);\n\n    float4 iv;\n\n    if (x != y) {\n        // assumes square textures!\n        float fracc = smax - smin;\n        fracc       = 1.0 / (fracc * steps);\n\n        float4 intmax = tex1D<float4>(transferIntegrateTex, smax);\n        float4 intmin = tex1D<float4>(transferIntegrateTex, smin);\n        iv.x          = (intmax.x - intmin.x) * fracc;\n        iv.y          = (intmax.y - intmin.y) * fracc;\n        iv.z          = (intmax.z - intmin.z) * fracc;\n        // iv.w = (intmax.w - intmin.w)*fracc;\n        iv.w = (1.0 - exp(-(intmax.w - intmin.w) * fracc));\n    }\n    else {\n        float4 sample = tex1D<float4>(transferTex, smin);\n        iv.x          = sample.x;\n        iv.y          = sample.y;\n        iv.z          = sample.z;\n        // iv.w = sample.w;\n        iv.w = (1.0 - exp(-sample.w));\n    }\n\n    iv.x = __saturatef(iv.x);\n    iv.y = __saturatef(iv.y);\n    iv.z = __saturatef(iv.z);\n    iv.w = __saturatef(iv.w);\n\n    // surface writes need byte offsets for x!\n    surf2DLayeredwrite(iv, transferLayerPreintSurf, x * sizeof(float4), y, layer);\n}\n\n//////////////////////////////////////////////////////////////////////////\n\nvoid VolumeRender_setTextureFilterMode(bool bLinearFilter, Volume *vol)\n{\n    if (vol->volumeTex) {\n        checkCudaErrors(hipDestroyTextureObject(vol->volumeTex));\n    }\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = vol->content;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = bLinearFilter ? hipFilterModeLinear : hipFilterModePoint;\n\n    texDescr.addressMode[0] = hipAddressModeWrap;\n    texDescr.addressMode[1] = hipAddressModeWrap;\n    texDescr.addressMode[2] = hipAddressModeWrap;\n\n    texDescr.readMode = VolumeTypeInfo<VolumeType>::readMode;\n\n    checkCudaErrors(hipCreateTextureObject(&vol->volumeTex, &texRes, &texDescr, NULL));\n}\n\nstatic unsigned int iDivUp(size_t a, size_t b)\n{\n    size_t val = (a % b != 0) ? (a / b + 1) : (a / b);\n    if (val > UINT_MAX) {\n        fprintf(stderr, \"\\nUINT_MAX limit exceeded in iDivUp() exiting.....\\n\");\n        exit(EXIT_FAILURE); // val exceeds limit\n    }\n\n    return static_cast<unsigned int>(val);\n}\n\nvoid VolumeRender_updateTF(int tfIdx, int numColors, float4 *colors)\n{\n    if (d_transferFunc) {\n        checkCudaErrors(hipFreeArray(d_transferFunc));\n        d_transferFunc = 0;\n    }\n\n    hipChannelFormatDesc channelFloat4 = hipCreateChannelDesc<float4>();\n    checkCudaErrors(hipMallocArray(&d_transferFunc, &channelFloat4, numColors, 1));\n    checkCudaErrors(\n        hipMemcpy2DToArray(d_transferFunc, 0, 0, colors, 0, sizeof(float4) * numColors, 1, hipMemcpyHostToDevice));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_transferFunc;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&transferTex, &texRes, &texDescr, NULL));\n\n    if (tfIdx < 0 || tfIdx >= VOLUMERENDER_TFS) {\n        return;\n    }\n\n    {\n        hipExtent extent = {VOLUMERENDER_TF_PREINTSTEPS, 0, 0};\n        dim3       blockSize(32, 1, 1);\n        dim3       gridSize(iDivUp(extent.width, blockSize.x), 1, 1);\n        d_integrate_trapezoidal<<<gridSize, blockSize>>>(extent, transferTex, transferIntegrateSurf);\n    }\n\n    {\n        hipExtent extent = {VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TFS};\n        dim3       blockSize(16, 16, 1);\n        dim3       gridSize(iDivUp(extent.width, blockSize.x), iDivUp(extent.height, blockSize.y), 1);\n        d_preintegrate<<<gridSize, blockSize>>>(tfIdx,\n                                                float(VOLUMERENDER_TF_PREINTSTEPS),\n                                                extent,\n                                                transferTex,\n                                                transferIntegrateTex,\n                                                transferLayerPreintSurf);\n    }\n}\n\nvoid VolumeRender_init()\n{\n    hipResourceDesc texRes;\n    hipTextureDesc  texDescr;\n\n    hipChannelFormatDesc channelFloat4 = hipCreateChannelDesc<float4>();\n    hipExtent            extent        = {VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TF_PREINTSIZE, VOLUMERENDER_TFS};\n    checkCudaErrors(\n        hipMalloc3DArray(&d_transferArray, &channelFloat4, extent, hipArrayLayered | hipArraySurfaceLoadStore));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_transferArray;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.addressMode[1]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&transferLayerPreintTex, &texRes, &texDescr, NULL));\n\n    hipResourceDesc surfRes;\n    memset(&surfRes, 0, sizeof(hipResourceDesc));\n    surfRes.resType         = hipResourceTypeArray;\n    surfRes.res.array.array = d_transferArray;\n\n    checkCudaErrors(hipCreateSurfaceObject(&transferLayerPreintSurf, &surfRes));\n\n    checkCudaErrors(hipMallocArray(\n        &d_transferIntegrate, &channelFloat4, VOLUMERENDER_TF_PREINTSTEPS, 0, hipArraySurfaceLoadStore));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_transferIntegrate;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModeLinear;\n\n    texDescr.addressMode[0] = hipAddressModeClamp;\n    texDescr.addressMode[1] = hipAddressModeClamp;\n    texDescr.addressMode[2] = hipAddressModeClamp;\n\n    texDescr.readMode = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&transferIntegrateTex, &texRes, &texDescr, NULL));\n\n    memset(&surfRes, 0, sizeof(hipResourceDesc));\n    surfRes.resType         = hipResourceTypeArray;\n    surfRes.res.array.array = d_transferIntegrate;\n\n    checkCudaErrors(hipCreateSurfaceObject(&transferIntegrateSurf, &surfRes));\n\n    // create transfer function texture\n    float4 transferFunc0[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            1.0,\n            0.0,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.5,\n            0.0,\n            1.0,\n        },\n        {\n            1.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            1.0,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            1.0,\n            0.0,\n            1.0,\n            1.0,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    float4 transferFunc1[] = {\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            0.125,\n        },\n        {\n            0.0,\n            0.5,\n            1.0,\n            0.125,\n        },\n        {\n            0.0,\n            1.0,\n            1.0,\n            0.125,\n        },\n        {\n            0.0,\n            1.0,\n            0.0,\n            0.125,\n        },\n        {\n            0.25,\n            0.75,\n            0.0,\n            1.0,\n        },\n        {\n            0.75,\n            0.25,\n            0.0,\n            0.125,\n        },\n        {\n            1.0,\n            0.75,\n            0.0,\n            0.125,\n        },\n        {\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        },\n    };\n\n    VolumeRender_updateTF(1, sizeof(transferFunc1) / sizeof(float4), transferFunc1);\n    VolumeRender_updateTF(0, sizeof(transferFunc0) / sizeof(float4), transferFunc0);\n}\n\nvoid VolumeRender_deinit()\n{\n    checkCudaErrors(hipDestroyTextureObject(transferTex));\n    checkCudaErrors(hipDestroyTextureObject(transferIntegrateTex));\n    checkCudaErrors(hipDestroySurfaceObject(transferIntegrateSurf));\n    checkCudaErrors(hipDestroyTextureObject(transferLayerPreintTex));\n    checkCudaErrors(hipDestroySurfaceObject(transferLayerPreintSurf));\n    checkCudaErrors(hipFreeArray(d_transferFunc));\n    checkCudaErrors(hipFreeArray(d_transferArray));\n    checkCudaErrors(hipFreeArray(d_transferIntegrate));\n    d_transferArray     = 0;\n    d_transferFunc      = 0;\n    d_transferIntegrate = 0;\n}\n\nvoid VolumeRender_setPreIntegrated(int state) { usePreInt = !!state; }\n\nvoid VolumeRender_render(dim3                gridSize,\n                         dim3                blockSize,\n                         uint               *d_output,\n                         uint                imageW,\n                         uint                imageH,\n                         float               density,\n                         float               brightness,\n                         float               transferOffset,\n                         float               transferScale,\n                         hipTextureObject_t volumeTex)\n{\n    if (usePreInt) {\n        d_render_preint<<<gridSize, blockSize>>>(d_output,\n                                                 imageW,\n                                                 imageH,\n                                                 density,\n                                                 brightness,\n                                                 transferOffset,\n                                                 transferScale,\n                                                 volumeTex,\n                                                 transferTex,\n                                                 transferLayerPreintTex);\n    }\n    else {\n        d_render_preint_off<<<gridSize, blockSize>>>(d_output,\n                                                     imageW,\n                                                     imageH,\n                                                     density,\n                                                     brightness,\n                                                     transferOffset,\n                                                     transferScale,\n                                                     volumeTex,\n                                                     transferTex,\n                                                     transferLayerPreintTex);\n    }\n}\n\nvoid VolumeRender_copyInvViewMatrix(float *invViewMatrix, size_t sizeofMatrix)\n{\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(c_invViewMatrix), invViewMatrix, sizeofMatrix));\n}\n\n#endif // #ifndef _VOLUMERENDER_KERNEL_CU_\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/volumeFiltering/volumeRender_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n\n#include \"ShaderStructs.h\"\n#include \"helper_cuda.h\"\n#include \"sinewave_cuda.h\"\n\n__global__ void sinewave_gen_kernel(Vertex *vertices, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    if (y < height && x < width) {\n        // write output vertex\n        vertices[y * width + x].position.x = u;\n        vertices[y * width + x].position.y = w;\n        vertices[y * width + x].position.z = v;\n        vertices[y * width + x].color.x    = 1.0f;\n        vertices[y * width + x].color.y    = 0.0f;\n        vertices[y * width + x].color.z    = 0.0f;\n        vertices[y * width + x].color.w    = 0.0f;\n    }\n}\n\nVertex *cudaImportVertexBuffer(void *sharedHandle, cudaExternalMemory_t &externalMemory, int meshWidth, int meshHeight)\n{\n    cudaExternalMemoryHandleDesc externalMemoryHandleDesc;\n    memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc));\n\n    externalMemoryHandleDesc.type                = cudaExternalMemoryHandleTypeD3D11ResourceKmt;\n    externalMemoryHandleDesc.size                = sizeof(Vertex) * meshHeight * meshWidth;\n    externalMemoryHandleDesc.flags               = cudaExternalMemoryDedicated;\n    externalMemoryHandleDesc.handle.win32.handle = sharedHandle;\n\n    checkCudaErrors(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc));\n\n    cudaExternalMemoryBufferDesc externalMemoryBufferDesc;\n    memset(&externalMemoryBufferDesc, 0, sizeof(externalMemoryBufferDesc));\n    externalMemoryBufferDesc.offset = 0;\n    externalMemoryBufferDesc.size   = sizeof(Vertex) * meshHeight * meshWidth;\n    externalMemoryBufferDesc.flags  = 0;\n\n    Vertex *cudaDevVertptr = NULL;\n    checkCudaErrors(\n        cudaExternalMemoryGetMappedBuffer((void **)&cudaDevVertptr, externalMemory, &externalMemoryBufferDesc));\n\n    return cudaDevVertptr;\n}\n\nvoid cudaImportKeyedMutex(void *sharedHandle, cudaExternalSemaphore_t &extSemaphore)\n{\n    cudaExternalSemaphoreHandleDesc extSemaDesc;\n    memset(&extSemaDesc, 0, sizeof(extSemaDesc));\n    extSemaDesc.type                = cudaExternalSemaphoreHandleTypeKeyedMutexKmt;\n    extSemaDesc.handle.win32.handle = sharedHandle;\n    extSemaDesc.flags               = 0;\n\n    checkCudaErrors(cudaImportExternalSemaphore(&extSemaphore, &extSemaDesc));\n}\n\nvoid cudaAcquireSync(cudaExternalSemaphore_t &extSemaphore,\n                     uint64_t                 key,\n                     unsigned int             timeoutMs,\n                     cudaStream_t             streamToRun)\n{\n    cudaExternalSemaphoreWaitParams extSemWaitParams;\n    memset(&extSemWaitParams, 0, sizeof(extSemWaitParams));\n    extSemWaitParams.params.keyedMutex.key       = key;\n    extSemWaitParams.params.keyedMutex.timeoutMs = timeoutMs;\n\n    checkCudaErrors(cudaWaitExternalSemaphoresAsync(&extSemaphore, &extSemWaitParams, 1, streamToRun));\n}\n\nvoid cudaReleaseSync(cudaExternalSemaphore_t &extSemaphore, uint64_t key, cudaStream_t streamToRun)\n{\n    cudaExternalSemaphoreSignalParams extSemSigParams;\n    memset(&extSemSigParams, 0, sizeof(extSemSigParams));\n    extSemSigParams.params.keyedMutex.key = key;\n\n    checkCudaErrors(cudaSignalExternalSemaphoresAsync(&extSemaphore, &extSemSigParams, 1, streamToRun));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid RunSineWaveKernel(cudaExternalSemaphore_t &extSemaphore,\n                       uint64_t                &key,\n                       unsigned int             timeoutMs,\n                       size_t                   mesh_width,\n                       size_t                   mesh_height,\n                       Vertex                  *cudaDevVertptr,\n                       cudaStream_t             streamToRun)\n{\n    static float t = 0.0f;\n    cudaAcquireSync(extSemaphore, key++, timeoutMs, streamToRun);\n\n    dim3 block(16, 16, 1);\n    dim3 grid(mesh_width / 16, mesh_height / 16, 1);\n    sinewave_gen_kernel<<<grid, block, 0, streamToRun>>>(cudaDevVertptr, mesh_width, mesh_height, t);\n    getLastCudaError(\"sinewave_gen_kernel execution failed.\\n\");\n\n    cudaReleaseSync(extSemaphore, key, streamToRun);\n    t += 0.01f;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#include \"ShaderStructs.h\"\n#include \"hip/hip_runtime_api.h\"\n#include \"sinewave_cuda.h\"\n\n__global__ void sinewave_gen_kernel(Vertex *vertices, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    if (y < height && x < width) {\n        // write output vertex\n        vertices[y * width + x].position.x = u;\n        vertices[y * width + x].position.y = w;\n        vertices[y * width + x].position.z = v;\n        vertices[y * width + x].color.x    = 1.0f;\n        vertices[y * width + x].color.y    = 0.0f;\n        vertices[y * width + x].color.z    = 0.0f;\n        vertices[y * width + x].color.w    = 0.0f;\n    }\n}\n\nVertex *cudaImportVertexBuffer(void *sharedHandle, hipExternalMemory_t &externalMemory, int meshWidth, int meshHeight)\n{\n    hipExternalMemoryHandleDesc externalMemoryHandleDesc;\n    memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc));\n\n    externalMemoryHandleDesc.type                = hipExternalMemoryHandleTypeD3D11ResourceKmt;\n    externalMemoryHandleDesc.size                = sizeof(Vertex) * meshHeight * meshWidth;\n    externalMemoryHandleDesc.flags               = hipExternalMemoryDedicated;\n    externalMemoryHandleDesc.handle.win32.handle = sharedHandle;\n\n    checkCudaErrors(hipImportExternalMemory(&externalMemory, &externalMemoryHandleDesc));\n\n    hipExternalMemoryBufferDesc externalMemoryBufferDesc;\n    memset(&externalMemoryBufferDesc, 0, sizeof(externalMemoryBufferDesc));\n    externalMemoryBufferDesc.offset = 0;\n    externalMemoryBufferDesc.size   = sizeof(Vertex) * meshHeight * meshWidth;\n    externalMemoryBufferDesc.flags  = 0;\n\n    Vertex *cudaDevVertptr = NULL;\n    checkCudaErrors(\n        hipExternalMemoryGetMappedBuffer((void **)&cudaDevVertptr, externalMemory, &externalMemoryBufferDesc));\n\n    return cudaDevVertptr;\n}\n\nvoid cudaImportKeyedMutex(void *sharedHandle, hipExternalSemaphore_t &extSemaphore)\n{\n    hipExternalSemaphoreHandleDesc extSemaDesc;\n    memset(&extSemaDesc, 0, sizeof(extSemaDesc));\n    extSemaDesc.type                = cudaExternalSemaphoreHandleTypeKeyedMutexKmt;\n    extSemaDesc.handle.win32.handle = sharedHandle;\n    extSemaDesc.flags               = 0;\n\n    checkCudaErrors(hipImportExternalSemaphore(&extSemaphore, &extSemaDesc));\n}\n\nvoid cudaAcquireSync(hipExternalSemaphore_t &extSemaphore,\n                     uint64_t                 key,\n                     unsigned int             timeoutMs,\n                     hipStream_t             streamToRun)\n{\n    hipExternalSemaphoreWaitParams extSemWaitParams;\n    memset(&extSemWaitParams, 0, sizeof(extSemWaitParams));\n    extSemWaitParams.params.keyedMutex.key       = key;\n    extSemWaitParams.params.keyedMutex.timeoutMs = timeoutMs;\n\n    checkCudaErrors(hipWaitExternalSemaphoresAsync(&extSemaphore, &extSemWaitParams, 1, streamToRun));\n}\n\nvoid cudaReleaseSync(hipExternalSemaphore_t &extSemaphore, uint64_t key, hipStream_t streamToRun)\n{\n    hipExternalSemaphoreSignalParams extSemSigParams;\n    memset(&extSemSigParams, 0, sizeof(extSemSigParams));\n    extSemSigParams.params.keyedMutex.key = key;\n\n    checkCudaErrors(hipSignalExternalSemaphoresAsync(&extSemaphore, &extSemSigParams, 1, streamToRun));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid RunSineWaveKernel(hipExternalSemaphore_t &extSemaphore,\n                       uint64_t                &key,\n                       unsigned int             timeoutMs,\n                       size_t                   mesh_width,\n                       size_t                   mesh_height,\n                       Vertex                  *cudaDevVertptr,\n                       hipStream_t             streamToRun)\n{\n    static float t = 0.0f;\n    cudaAcquireSync(extSemaphore, key++, timeoutMs, streamToRun);\n\n    dim3 block(16, 16, 1);\n    dim3 grid(mesh_width / 16, mesh_height / 16, 1);\n    sinewave_gen_kernel<<<grid, block, 0, streamToRun>>>(cudaDevVertptr, mesh_width, mesh_height, t);\n    getLastCudaError(\"sinewave_gen_kernel execution failed.\\n\");\n\n    cudaReleaseSync(extSemaphore, key, streamToRun);\n    t += 0.01f;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleD3D11/sinewave_cuda.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * See: https://www.piday.org/million/\n */\n\n#include <algorithm>\n\n#include \"MonteCarloPi.h\"\n#define CUDA_DRIVER_API\n#include <helper_cuda.h>\n#include <iostream>\n\n#define ROUND_UP_TO_GRANULARITY(x, n) (((x + n - 1) / n) * n)\n\n// `ipcHandleTypeFlag` specifies the platform specific handle type this sample\n// uses for importing and exporting memory allocation. On Linux this sample\n// specifies the type as CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR meaning that\n// file descriptors will be used. On Windows this sample specifies the type as\n// CU_MEM_HANDLE_TYPE_WIN32 meaning that NT HANDLEs will be used. The\n// ipcHandleTypeFlag variable is a convenience variable and is passed by value\n// to individual requests.\n#if defined(__linux__)\nCUmemAllocationHandleType ipcHandleTypeFlag = CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR;\n#else\nCUmemAllocationHandleType ipcHandleTypeFlag = CU_MEM_HANDLE_TYPE_WIN32;\n#endif\n\n// Windows-specific LPSECURITYATTRIBUTES\nvoid getDefaultSecurityDescriptor(CUmemAllocationProp *prop)\n{\n#if defined(__linux__)\n    return;\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n    static const char        sddl[] = \"D:P(OA;;GARCSDWDWOCCDCLCSWLODTWPRPCRFA;;;WD)\";\n    static OBJECT_ATTRIBUTES objAttributes;\n    static bool              objAttributesConfigured = false;\n\n    if (!objAttributesConfigured) {\n        PSECURITY_DESCRIPTOR secDesc;\n        BOOL result = ConvertStringSecurityDescriptorToSecurityDescriptorA(sddl, SDDL_REVISION_1, &secDesc, NULL);\n        if (result == 0) {\n            printf(\"IPC failure: getDefaultSecurityDescriptor Failed! (%d)\\n\", GetLastError());\n        }\n\n        InitializeObjectAttributes(&objAttributes, NULL, 0, NULL, secDesc);\n\n        objAttributesConfigured = true;\n    }\n\n    prop->win32HandleMetaData = &objAttributes;\n    return;\n#endif\n}\n\n__global__ void monte_carlo_kernel(vec2        *xyVector,\n                                   float       *pointsInsideCircle,\n                                   float       *numPointsInCircle,\n                                   unsigned int numPoints,\n                                   float        time)\n{\n    const size_t stride = gridDim.x * blockDim.x;\n    size_t       tid    = blockIdx.x * blockDim.x + threadIdx.x;\n    float        count  = 0.0f;\n\n    curandState rgnState;\n    curand_init((unsigned long long)time, tid, 0, &rgnState);\n\n    for (; tid < numPoints; tid += stride) {\n        float x          = curand_uniform(&rgnState);\n        float y          = curand_uniform(&rgnState);\n        x                = (2.0f * x) - 1.0f;\n        y                = (2.0f * y) - 1.0f;\n        xyVector[tid][0] = x;\n        xyVector[tid][1] = y;\n\n        // Compute the distance of this point form the center(0, 0)\n        float dist = sqrtf((x * x) + (y * y));\n\n        // If distance is less than the radius of the unit circle, the point lies in\n        // the circle.\n        pointsInsideCircle[tid] = (dist <= 1.0f);\n        count += (dist <= 1.0f);\n    }\n    atomicAdd(numPointsInCircle, count);\n}\n\nMonteCarloPiSimulation::MonteCarloPiSimulation(size_t num_points)\n    : m_xyVector(nullptr)\n    , m_pointsInsideCircle(nullptr)\n    , m_totalPointsInsideCircle(0)\n    , m_totalPointsSimulated(0)\n    , m_numPoints(num_points)\n{\n}\n\nMonteCarloPiSimulation::~MonteCarloPiSimulation()\n{\n    if (m_numPointsInCircle) {\n        checkCudaErrors(cudaFree(m_numPointsInCircle));\n        m_numPointsInCircle = nullptr;\n    }\n    if (m_hostNumPointsInCircle) {\n        checkCudaErrors(cudaFreeHost(m_hostNumPointsInCircle));\n        m_hostNumPointsInCircle = nullptr;\n    }\n\n    cleanupSimulationAllocations();\n}\n\nvoid MonteCarloPiSimulation::initSimulation(int cudaDevice, cudaStream_t stream)\n{\n    m_cudaDevice = cudaDevice;\n    getIdealExecutionConfiguration();\n\n    // Allocate a position buffer that contains random location of the points in\n    // XY cartesian plane.\n    // Allocate a bitmap buffer which holds information of whether a point in the\n    // position buffer is inside the unit circle or not.\n    setupSimulationAllocations();\n\n    checkCudaErrors(cudaMalloc((float **)&m_numPointsInCircle, sizeof(*m_numPointsInCircle)));\n    checkCudaErrors(cudaMallocHost((float **)&m_hostNumPointsInCircle, sizeof(*m_hostNumPointsInCircle)));\n}\n\nvoid MonteCarloPiSimulation::stepSimulation(float time, cudaStream_t stream)\n{\n    checkCudaErrors(cudaMemsetAsync(m_numPointsInCircle, 0, sizeof(*m_numPointsInCircle), stream));\n\n    monte_carlo_kernel<<<m_blocks, m_threads, 0, stream>>>(\n        m_xyVector, m_pointsInsideCircle, m_numPointsInCircle, m_numPoints, time);\n    getLastCudaError(\"Failed to launch CUDA simulation\");\n\n    checkCudaErrors(cudaMemcpyAsync(\n        m_hostNumPointsInCircle, m_numPointsInCircle, sizeof(*m_numPointsInCircle), cudaMemcpyDeviceToHost, stream));\n\n    // Queue up a stream callback to compute and print the PI value.\n    checkCudaErrors(cudaLaunchHostFunc(stream, this->computePiCallback, (void *)this));\n}\n\nvoid MonteCarloPiSimulation::computePiCallback(void *args)\n{\n    MonteCarloPiSimulation *cbData = (MonteCarloPiSimulation *)args;\n    cbData->m_totalPointsInsideCircle += *(cbData->m_hostNumPointsInCircle);\n    cbData->m_totalPointsSimulated += cbData->m_numPoints;\n    double piValue = 4.0 * ((double)cbData->m_totalPointsInsideCircle / (double)cbData->m_totalPointsSimulated);\n    printf(\"Approximate Pi value for %zd data points: %lf \\n\", cbData->m_totalPointsSimulated, piValue);\n}\n\nvoid MonteCarloPiSimulation::getIdealExecutionConfiguration()\n{\n    int warpSize            = 0;\n    int multiProcessorCount = 0;\n\n    checkCudaErrors(cudaSetDevice(m_cudaDevice));\n    checkCudaErrors(cudaDeviceGetAttribute(&warpSize, cudaDevAttrWarpSize, m_cudaDevice));\n\n    // We don't need large block sizes, since there's not much inter-thread\n    // communication\n    m_threads = warpSize;\n\n    // Use the occupancy calculator and fill the gpu as best as we can\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&m_blocks, monte_carlo_kernel, warpSize, 0));\n\n    checkCudaErrors(cudaDeviceGetAttribute(&multiProcessorCount, cudaDevAttrMultiProcessorCount, m_cudaDevice));\n    m_blocks *= multiProcessorCount;\n\n    // Go ahead and the clamp the blocks to the minimum needed for this\n    // height/width\n    m_blocks = std::min(m_blocks, (int)((m_numPoints + m_threads - 1) / m_threads));\n}\n\nvoid MonteCarloPiSimulation::setupSimulationAllocations()\n{\n    CUdeviceptr                  d_ptr       = 0U;\n    size_t                       granularity = 0;\n    CUmemGenericAllocationHandle cudaPositionHandle, cudaInCircleHandle;\n\n    CUmemAllocationProp allocProp  = {};\n    allocProp.type                 = CU_MEM_ALLOCATION_TYPE_PINNED;\n    allocProp.location.type        = CU_MEM_LOCATION_TYPE_DEVICE;\n    allocProp.location.id          = m_cudaDevice;\n    allocProp.win32HandleMetaData  = NULL;\n    allocProp.requestedHandleTypes = ipcHandleTypeFlag;\n\n    // Windows-specific LPSECURITYATTRIBUTES is required when\n    // CU_MEM_HANDLE_TYPE_WIN32 is used. The security attribute defines the scope\n    // of which exported allocations may be tranferred to other processes. For all\n    // other handle types, pass NULL.\n    getDefaultSecurityDescriptor(&allocProp);\n\n    // Get the recommended granularity for m_cudaDevice.\n    checkCudaErrors(cuMemGetAllocationGranularity(&granularity, &allocProp, CU_MEM_ALLOC_GRANULARITY_RECOMMENDED));\n\n    size_t xyPositionVecSize = m_numPoints * sizeof(*m_xyVector);\n    size_t inCircleVecSize   = m_numPoints * sizeof(*m_pointsInsideCircle);\n\n    size_t xyPositionSize = ROUND_UP_TO_GRANULARITY(xyPositionVecSize, granularity);\n    size_t inCircleSize   = ROUND_UP_TO_GRANULARITY(inCircleVecSize, granularity);\n    m_totalAllocationSize = (xyPositionSize + inCircleSize);\n\n    // Reserve the required contiguous VA space for the allocations\n    checkCudaErrors(cuMemAddressReserve(&d_ptr, m_totalAllocationSize, granularity, 0U, 0));\n\n    // Create the allocations as a pinned allocation on this device.\n    // Create an allocation to store all the positions of points on the xy plane\n    // and a second allocation which stores information if the corresponding\n    // position is inside the unit circle or not.\n    checkCudaErrors(cuMemCreate(&cudaPositionHandle, xyPositionSize, &allocProp, 0));\n    checkCudaErrors(cuMemCreate(&cudaInCircleHandle, inCircleSize, &allocProp, 0));\n\n    // Export the allocation to a platform-specific handle. The type of handle\n    // requested here must match the requestedHandleTypes field in the prop\n    // structure passed to cuMemCreate. The handle obtained here will be passed to\n    // vulkan to import the allocation.\n    checkCudaErrors(\n        cuMemExportToShareableHandle((void *)&m_posShareableHandle, cudaPositionHandle, ipcHandleTypeFlag, 0));\n    checkCudaErrors(\n        cuMemExportToShareableHandle((void *)&m_inCircleShareableHandle, cudaInCircleHandle, ipcHandleTypeFlag, 0));\n\n    CUdeviceptr va_position = d_ptr;\n    CUdeviceptr va_InCircle = va_position + xyPositionSize;\n    m_pointsInsideCircle    = (float *)va_InCircle;\n    m_xyVector              = (vec2 *)va_position;\n\n    // Assign the chunk to the appropriate VA range\n    checkCudaErrors(cuMemMap(va_position, xyPositionSize, 0, cudaPositionHandle, 0));\n    checkCudaErrors(cuMemMap(va_InCircle, inCircleSize, 0, cudaInCircleHandle, 0));\n\n    // Release the handles for the allocation. Since the allocation is currently\n    // mapped to a VA range with a previous call to cuMemMap the actual freeing of\n    // memory allocation will happen on an eventual call to cuMemUnmap. Thus the\n    // allocation will be kept live until it is unmapped.\n    checkCudaErrors(cuMemRelease(cudaPositionHandle));\n    checkCudaErrors(cuMemRelease(cudaInCircleHandle));\n\n    CUmemAccessDesc accessDescriptor = {};\n    accessDescriptor.location.id     = m_cudaDevice;\n    accessDescriptor.location.type   = CU_MEM_LOCATION_TYPE_DEVICE;\n    accessDescriptor.flags           = CU_MEM_ACCESS_FLAGS_PROT_READWRITE;\n\n    // Apply the access descriptor to the whole VA range. Essentially enables\n    // Read-Write access to the range.\n    checkCudaErrors(cuMemSetAccess(d_ptr, m_totalAllocationSize, &accessDescriptor, 1));\n}\n\nvoid MonteCarloPiSimulation::cleanupSimulationAllocations()\n{\n    if (m_xyVector && m_pointsInsideCircle) {\n        // Unmap the mapped virtual memory region\n        // Since the handles to the mapped backing stores have already been released\n        // by cuMemRelease, and these are the only/last mappings referencing them,\n        // The backing stores will be freed.\n        checkCudaErrors(cuMemUnmap((CUdeviceptr)m_xyVector, m_totalAllocationSize));\n\n        checkIpcErrors(ipcCloseShareableHandle(m_posShareableHandle));\n        checkIpcErrors(ipcCloseShareableHandle(m_inCircleShareableHandle));\n\n        // Free the virtual address region.\n        checkCudaErrors(cuMemAddressFree((CUdeviceptr)m_xyVector, m_totalAllocationSize));\n\n        m_xyVector           = nullptr;\n        m_pointsInsideCircle = nullptr;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * See: https://www.piday.org/million/\n */\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n\n#include \"MonteCarloPi.h\"\n#define CUDA_DRIVER_API\n#include <hip/hip_runtime_api.h>\n#include <iostream>\n\n#define ROUND_UP_TO_GRANULARITY(x, n) (((x + n - 1) / n) * n)\n\n// `ipcHandleTypeFlag` specifies the platform specific handle type this sample\n// uses for importing and exporting memory allocation. On Linux this sample\n// specifies the type as CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR meaning that\n// file descriptors will be used. On Windows this sample specifies the type as\n// CU_MEM_HANDLE_TYPE_WIN32 meaning that NT HANDLEs will be used. The\n// ipcHandleTypeFlag variable is a convenience variable and is passed by value\n// to individual requests.\n#if defined(__linux__)\nhipMemAllocationHandleType ipcHandleTypeFlag = hipMemHandleTypePosixFileDescriptor;\n#else\nhipMemAllocationHandleType ipcHandleTypeFlag = hipMemHandleTypeWin32;\n#endif\n\n// Windows-specific LPSECURITYATTRIBUTES\nvoid getDefaultSecurityDescriptor(hipMemAllocationProp *prop)\n{\n#if defined(__linux__)\n    return;\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n    static const char        sddl[] = \"D:P(OA;;GARCSDWDWOCCDCLCSWLODTWPRPCRFA;;;WD)\";\n    static OBJECT_ATTRIBUTES objAttributes;\n    static bool              objAttributesConfigured = false;\n\n    if (!objAttributesConfigured) {\n        PSECURITY_DESCRIPTOR secDesc;\n        BOOL result = ConvertStringSecurityDescriptorToSecurityDescriptorA(sddl, SDDL_REVISION_1, &secDesc, NULL);\n        if (result == 0) {\n            printf(\"IPC failure: getDefaultSecurityDescriptor Failed! (%d)\\n\", GetLastError());\n        }\n\n        InitializeObjectAttributes(&objAttributes, NULL, 0, NULL, secDesc);\n\n        objAttributesConfigured = true;\n    }\n\n    prop->win32HandleMetaData = &objAttributes;\n    return;\n#endif\n}\n\n__global__ void monte_carlo_kernel(vec2        *xyVector,\n                                   float       *pointsInsideCircle,\n                                   float       *numPointsInCircle,\n                                   unsigned int numPoints,\n                                   float        time)\n{\n    const size_t stride = gridDim.x * blockDim.x;\n    size_t       tid    = blockIdx.x * blockDim.x + threadIdx.x;\n    float        count  = 0.0f;\n\n    hiprandState rgnState;\n    hiprand_init((unsigned long long)time, tid, 0, &rgnState);\n\n    for (; tid < numPoints; tid += stride) {\n        float x          = hiprand_uniform(&rgnState);\n        float y          = hiprand_uniform(&rgnState);\n        x                = (2.0f * x) - 1.0f;\n        y                = (2.0f * y) - 1.0f;\n        xyVector[tid][0] = x;\n        xyVector[tid][1] = y;\n\n        // Compute the distance of this point form the center(0, 0)\n        float dist = sqrtf((x * x) + (y * y));\n\n        // If distance is less than the radius of the unit circle, the point lies in\n        // the circle.\n        pointsInsideCircle[tid] = (dist <= 1.0f);\n        count += (dist <= 1.0f);\n    }\n    atomicAdd(numPointsInCircle, count);\n}\n\nMonteCarloPiSimulation::MonteCarloPiSimulation(size_t num_points)\n    : m_xyVector(nullptr)\n    , m_pointsInsideCircle(nullptr)\n    , m_totalPointsInsideCircle(0)\n    , m_totalPointsSimulated(0)\n    , m_numPoints(num_points)\n{\n}\n\nMonteCarloPiSimulation::~MonteCarloPiSimulation()\n{\n    if (m_numPointsInCircle) {\n        checkCudaErrors(hipFree(m_numPointsInCircle));\n        m_numPointsInCircle = nullptr;\n    }\n    if (m_hostNumPointsInCircle) {\n        checkCudaErrors(hipHostFree(m_hostNumPointsInCircle));\n        m_hostNumPointsInCircle = nullptr;\n    }\n\n    cleanupSimulationAllocations();\n}\n\nvoid MonteCarloPiSimulation::initSimulation(int cudaDevice, hipStream_t stream)\n{\n    m_cudaDevice = cudaDevice;\n    getIdealExecutionConfiguration();\n\n    // Allocate a position buffer that contains random location of the points in\n    // XY cartesian plane.\n    // Allocate a bitmap buffer which holds information of whether a point in the\n    // position buffer is inside the unit circle or not.\n    setupSimulationAllocations();\n\n    checkCudaErrors(hipMalloc((float **)&m_numPointsInCircle, sizeof(*m_numPointsInCircle)));\n    checkCudaErrors(hipHostMalloc((float **)&m_hostNumPointsInCircle, sizeof(*m_hostNumPointsInCircle)));\n}\n\nvoid MonteCarloPiSimulation::stepSimulation(float time, hipStream_t stream)\n{\n    checkCudaErrors(hipMemsetAsync(m_numPointsInCircle, 0, sizeof(*m_numPointsInCircle), stream));\n\n    monte_carlo_kernel<<<m_blocks, m_threads, 0, stream>>>(\n        m_xyVector, m_pointsInsideCircle, m_numPointsInCircle, m_numPoints, time);\n    getLastCudaError(\"Failed to launch CUDA simulation\");\n\n    checkCudaErrors(hipMemcpyAsync(\n        m_hostNumPointsInCircle, m_numPointsInCircle, sizeof(*m_numPointsInCircle), hipMemcpyDeviceToHost, stream));\n\n    // Queue up a stream callback to compute and print the PI value.\n    checkCudaErrors(hipLaunchHostFunc(stream, this->computePiCallback, (void *)this));\n}\n\nvoid MonteCarloPiSimulation::computePiCallback(void *args)\n{\n    MonteCarloPiSimulation *cbData = (MonteCarloPiSimulation *)args;\n    cbData->m_totalPointsInsideCircle += *(cbData->m_hostNumPointsInCircle);\n    cbData->m_totalPointsSimulated += cbData->m_numPoints;\n    double piValue = 4.0 * ((double)cbData->m_totalPointsInsideCircle / (double)cbData->m_totalPointsSimulated);\n    printf(\"Approximate Pi value for %zd data points: %lf \\n\", cbData->m_totalPointsSimulated, piValue);\n}\n\nvoid MonteCarloPiSimulation::getIdealExecutionConfiguration()\n{\n    int warpSize            = 0;\n    int multiProcessorCount = 0;\n\n    checkCudaErrors(hipSetDevice(m_cudaDevice));\n    checkCudaErrors(hipDeviceGetAttribute(&warpSize, hipDeviceAttributeWarpSize, m_cudaDevice));\n\n    // We don't need large block sizes, since there's not much inter-thread\n    // communication\n    m_threads = warpSize;\n\n    // Use the occupancy calculator and fill the gpu as best as we can\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(&m_blocks, monte_carlo_kernel, warpSize, 0));\n\n    checkCudaErrors(hipDeviceGetAttribute(&multiProcessorCount, hipDeviceAttributeMultiprocessorCount, m_cudaDevice));\n    m_blocks *= multiProcessorCount;\n\n    // Go ahead and the clamp the blocks to the minimum needed for this\n    // height/width\n    m_blocks = std::min(m_blocks, (int)((m_numPoints + m_threads - 1) / m_threads));\n}\n\nvoid MonteCarloPiSimulation::setupSimulationAllocations()\n{\n    hipDeviceptr_t                  d_ptr       = 0U;\n    size_t                       granularity = 0;\n    hipMemGenericAllocationHandle_t cudaPositionHandle, cudaInCircleHandle;\n\n    hipMemAllocationProp allocProp  = {};\n    allocProp.type                 = hipMemAllocationTypePinned;\n    allocProp.location.type        = hipMemLocationTypeDevice;\n    allocProp.location.id          = m_cudaDevice;\n    allocProp.win32HandleMetaData  = NULL;\n    allocProp.requestedHandleTypes = ipcHandleTypeFlag;\n\n    // Windows-specific LPSECURITYATTRIBUTES is required when\n    // CU_MEM_HANDLE_TYPE_WIN32 is used. The security attribute defines the scope\n    // of which exported allocations may be tranferred to other processes. For all\n    // other handle types, pass NULL.\n    getDefaultSecurityDescriptor(&allocProp);\n\n    // Get the recommended granularity for m_cudaDevice.\n    checkCudaErrors(hipMemGetAllocationGranularity(&granularity, &allocProp, hipMemAllocationGranularityRecommended));\n\n    size_t xyPositionVecSize = m_numPoints * sizeof(*m_xyVector);\n    size_t inCircleVecSize   = m_numPoints * sizeof(*m_pointsInsideCircle);\n\n    size_t xyPositionSize = ROUND_UP_TO_GRANULARITY(xyPositionVecSize, granularity);\n    size_t inCircleSize   = ROUND_UP_TO_GRANULARITY(inCircleVecSize, granularity);\n    m_totalAllocationSize = (xyPositionSize + inCircleSize);\n\n    // Reserve the required contiguous VA space for the allocations\n    checkCudaErrors(hipMemAddressReserve(&d_ptr, m_totalAllocationSize, granularity, 0U, 0));\n\n    // Create the allocations as a pinned allocation on this device.\n    // Create an allocation to store all the positions of points on the xy plane\n    // and a second allocation which stores information if the corresponding\n    // position is inside the unit circle or not.\n    checkCudaErrors(hipMemCreate(&cudaPositionHandle, xyPositionSize, &allocProp, 0));\n    checkCudaErrors(hipMemCreate(&cudaInCircleHandle, inCircleSize, &allocProp, 0));\n\n    // Export the allocation to a platform-specific handle. The type of handle\n    // requested here must match the requestedHandleTypes field in the prop\n    // structure passed to cuMemCreate. The handle obtained here will be passed to\n    // vulkan to import the allocation.\n    checkCudaErrors(\n        hipMemExportToShareableHandle((void *)&m_posShareableHandle, cudaPositionHandle, ipcHandleTypeFlag, 0));\n    checkCudaErrors(\n        hipMemExportToShareableHandle((void *)&m_inCircleShareableHandle, cudaInCircleHandle, ipcHandleTypeFlag, 0));\n\n    hipDeviceptr_t va_position = d_ptr;\n    hipDeviceptr_t va_InCircle = va_position + xyPositionSize;\n    m_pointsInsideCircle    = (float *)va_InCircle;\n    m_xyVector              = (vec2 *)va_position;\n\n    // Assign the chunk to the appropriate VA range\n    checkCudaErrors(hipMemMap(va_position, xyPositionSize, 0, cudaPositionHandle, 0));\n    checkCudaErrors(hipMemMap(va_InCircle, inCircleSize, 0, cudaInCircleHandle, 0));\n\n    // Release the handles for the allocation. Since the allocation is currently\n    // mapped to a VA range with a previous call to cuMemMap the actual freeing of\n    // memory allocation will happen on an eventual call to cuMemUnmap. Thus the\n    // allocation will be kept live until it is unmapped.\n    checkCudaErrors(hipMemRelease(cudaPositionHandle));\n    checkCudaErrors(hipMemRelease(cudaInCircleHandle));\n\n    hipMemAccessDesc accessDescriptor = {};\n    accessDescriptor.location.id     = m_cudaDevice;\n    accessDescriptor.location.type   = hipMemLocationTypeDevice;\n    accessDescriptor.flags           = hipMemAccessFlagsProtReadWrite;\n\n    // Apply the access descriptor to the whole VA range. Essentially enables\n    // Read-Write access to the range.\n    checkCudaErrors(hipMemSetAccess(d_ptr, m_totalAllocationSize, &accessDescriptor, 1));\n}\n\nvoid MonteCarloPiSimulation::cleanupSimulationAllocations()\n{\n    if (m_xyVector && m_pointsInsideCircle) {\n        // Unmap the mapped virtual memory region\n        // Since the handles to the mapped backing stores have already been released\n        // by cuMemRelease, and these are the only/last mappings referencing them,\n        // The backing stores will be freed.\n        checkCudaErrors(hipMemUnmap((hipDeviceptr_t)m_xyVector, m_totalAllocationSize));\n\n        checkIpcErrors(ipcCloseShareableHandle(m_posShareableHandle));\n        checkIpcErrors(ipcCloseShareableHandle(m_inCircleShareableHandle));\n\n        // Free the virtual address region.\n        checkCudaErrors(hipMemAddressFree((hipDeviceptr_t)m_xyVector, m_totalAllocationSize));\n\n        m_xyVector           = nullptr;\n        m_pointsInsideCircle = nullptr;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleVulkanMMAP/MonteCarloPi.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <helper_cuda.h> // CUDA device initialization helper functions\n#include <helper_functions.h>\n#include <helper_math.h>\n\n__constant__ float cGaussian[64]; // gaussian array in device side\n\ncudaTextureObject_t rgbaTexdImage;\ncudaTextureObject_t rgbaTexdTemp;\n\nuint  *dImage = NULL; // original image\nuint  *dTemp  = NULL; // temp array for iterations\nsize_t pitch;\n\n/*\n    Perform a simple bilateral filter.\n\n    Bilateral filter is a nonlinear filter that is a mixture of range\n    filter and domain filter, the previous one preserves crisp edges and\n    the latter one filters noise. The intensity value at each pixel in\n    an image is replaced by a weighted average of intensity values from\n    nearby pixels.\n\n    The weight factor is calculated by the product of domain filter\n    component(using the gaussian distribution as a spatial distance) as\n    well as range filter component(Euclidean distance between center pixel\n    and the current neighbor pixel). Because this process is nonlinear,\n    the sample just uses a simple pixel by pixel step.\n\n    Texture fetches automatically clamp to edge of image. 1D gaussian array\n    is mapped to a 1D texture instead of using shared memory, which may\n    cause severe bank conflict.\n\n    Threads are y-pass(column-pass), because the output is coalesced.\n\n    Parameters\n    od - pointer to output data in global memory\n    d_f - pointer to the 1D gaussian array\n    e_d - euclidean delta\n    w  - image width\n    h  - image height\n    r  - filter radius\n*/\n\n// Euclidean Distance (x, y, d) = exp((|x - y| / d)^2 / 2)\n__device__ float euclideanLen(float4 a, float4 b, float d)\n{\n    float mod = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z);\n\n    return __expf(-mod / (2.f * d * d));\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(fabs(rgba.x)); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(fabs(rgba.y));\n    rgba.z = __saturatef(fabs(rgba.z));\n    rgba.w = __saturatef(fabs(rgba.w));\n    return (uint(rgba.w * 255.0f) << 24) | (uint(rgba.z * 255.0f) << 16) | (uint(rgba.y * 255.0f) << 8)\n         | uint(rgba.x * 255.0f);\n}\n\n__device__ float4 rgbaIntToFloat(uint c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_bilateral_filter(uint *od, int w, int h, float e_d, int r, cudaTextureObject_t rgbaTex)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (x >= w || y >= h) {\n        return;\n    }\n\n    float  sum = 0.0f;\n    float  factor;\n    float4 t      = {0.f, 0.f, 0.f, 0.f};\n    float4 center = tex2D<float4>(rgbaTex, x, y);\n\n    for (int i = -r; i <= r; i++) {\n        for (int j = -r; j <= r; j++) {\n            float4 curPix = tex2D<float4>(rgbaTex, x + j, y + i);\n            factor        = cGaussian[i + r] * cGaussian[j + r] * // domain factor\n                     euclideanLen(curPix, center, e_d);           // range factor\n\n            t += factor * curPix;\n            sum += factor;\n        }\n    }\n\n    od[y * w + x] = rgbaFloatToInt(t / sum);\n}\n\nextern \"C\" void initTexture(int width, int height, uint *hImage)\n{\n    // copy image data to array\n    checkCudaErrors(cudaMallocPitch(&dImage, &pitch, sizeof(uint) * width, height));\n    checkCudaErrors(cudaMallocPitch(&dTemp, &pitch, sizeof(uint) * width, height));\n    checkCudaErrors(cudaMemcpy2D(\n        dImage, pitch, hImage, sizeof(uint) * width, sizeof(uint) * width, height, cudaMemcpyHostToDevice));\n\n    // texture<uchar4, 2, cudaReadModeNormalizedFloat> rgbaTex;\n    cudaChannelFormatDesc desc = cudaCreateChannelDesc<uchar4>();\n    cudaResourceDesc      texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                  = cudaResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = dImage;\n    texRes.res.pitch2D.desc         = desc;\n    texRes.res.pitch2D.width        = width;\n    texRes.res.pitch2D.height       = height;\n    texRes.res.pitch2D.pitchInBytes = pitch;\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&rgbaTexdImage, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                  = cudaResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = dTemp;\n    texRes.res.pitch2D.desc         = desc;\n    texRes.res.pitch2D.width        = width;\n    texRes.res.pitch2D.height       = height;\n    texRes.res.pitch2D.pitchInBytes = pitch;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&rgbaTexdTemp, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeTextures()\n{\n    checkCudaErrors(cudaDestroyTextureObject(rgbaTexdImage));\n    checkCudaErrors(cudaDestroyTextureObject(rgbaTexdTemp));\n    checkCudaErrors(cudaFree(dImage));\n    checkCudaErrors(cudaFree(dTemp));\n}\n\n/*\n    Because a 2D gaussian mask is symmetry in row and column,\n    here only generate a 1D mask, and use the product by row\n    and column index later.\n\n    1D gaussian distribution :\n        g(x, d) -- C * exp(-x^2/d^2), C is a constant amplifier\n\n    parameters:\n    og - output gaussian array in global memory\n    delta - the 2nd parameter 'd' in the above function\n    radius - half of the filter size\n             (total filter size = 2 * radius + 1)\n*/\nextern \"C\" void updateGaussian(float delta, int radius)\n{\n    float fGaussian[64];\n\n    for (int i = 0; i < 2 * radius + 1; ++i) {\n        float x      = (float)(i - radius);\n        fGaussian[i] = expf(-(x * x) / (2 * delta * delta));\n    }\n\n    checkCudaErrors(cudaMemcpyToSymbol(cGaussian, fGaussian, sizeof(float) * (2 * radius + 1)));\n}\n\n/*\n    Perform 2D bilateral filter on image using CUDA\n\n    Parameters:\n    d_dest - pointer to destination image in device memory\n    width  - image width\n    height - image height\n    e_d    - euclidean delta\n    radius - filter radius\n    iterations - number of iterations\n*/\n\n// RGBA version\nextern \"C\" double bilateralFilterRGBA(uint               *dDest,\n                                      int                 width,\n                                      int                 height,\n                                      float               e_d,\n                                      int                 radius,\n                                      int                 iterations,\n                                      StopWatchInterface *timer)\n{\n    // var for kernel computation timing\n    double dKernelTime;\n\n    for (int i = 0; i < iterations; i++) {\n        // sync host and start kernel computation timer\n        dKernelTime = 0.0;\n        checkCudaErrors(cudaDeviceSynchronize());\n        sdkResetTimer(&timer);\n\n        dim3 gridSize((width + 16 - 1) / 16, (height + 16 - 1) / 16);\n        dim3 blockSize(16, 16);\n\n        if (iterations > 1) {\n            d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdTemp);\n        }\n        else {\n            d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdImage);\n        }\n\n        // sync host and stop computation timer\n        checkCudaErrors(cudaDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(cudaMemcpy2D(\n                dTemp, pitch, dDest, sizeof(int) * width, sizeof(int) * width, height, cudaMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h> // CUDA device initialization helper functions\n#include <helper_functions.h>\n#include <hip/hip_vector_types.h>\n\n__constant__ float cGaussian[64]; // gaussian array in device side\n\nhipTextureObject_t rgbaTexdImage;\nhipTextureObject_t rgbaTexdTemp;\n\nuint  *dImage = NULL; // original image\nuint  *dTemp  = NULL; // temp array for iterations\nsize_t pitch;\n\n/*\n    Perform a simple bilateral filter.\n\n    Bilateral filter is a nonlinear filter that is a mixture of range\n    filter and domain filter, the previous one preserves crisp edges and\n    the latter one filters noise. The intensity value at each pixel in\n    an image is replaced by a weighted average of intensity values from\n    nearby pixels.\n\n    The weight factor is calculated by the product of domain filter\n    component(using the gaussian distribution as a spatial distance) as\n    well as range filter component(Euclidean distance between center pixel\n    and the current neighbor pixel). Because this process is nonlinear,\n    the sample just uses a simple pixel by pixel step.\n\n    Texture fetches automatically clamp to edge of image. 1D gaussian array\n    is mapped to a 1D texture instead of using shared memory, which may\n    cause severe bank conflict.\n\n    Threads are y-pass(column-pass), because the output is coalesced.\n\n    Parameters\n    od - pointer to output data in global memory\n    d_f - pointer to the 1D gaussian array\n    e_d - euclidean delta\n    w  - image width\n    h  - image height\n    r  - filter radius\n*/\n\n// Euclidean Distance (x, y, d) = exp((|x - y| / d)^2 / 2)\n__device__ float euclideanLen(float4 a, float4 b, float d)\n{\n    float mod = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) + (b.z - a.z) * (b.z - a.z);\n\n    return __expf(-mod / (2.f * d * d));\n}\n\n__device__ uint rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(fabs(rgba.x)); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(fabs(rgba.y));\n    rgba.z = __saturatef(fabs(rgba.z));\n    rgba.w = __saturatef(fabs(rgba.w));\n    return (uint(rgba.w * 255.0f) << 24) | (uint(rgba.z * 255.0f) << 16) | (uint(rgba.y * 255.0f) << 8)\n         | uint(rgba.x * 255.0f);\n}\n\n__device__ float4 rgbaIntToFloat(uint c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_bilateral_filter(uint *od, int w, int h, float e_d, int r, hipTextureObject_t rgbaTex)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (x >= w || y >= h) {\n        return;\n    }\n\n    float  sum = 0.0f;\n    float  factor;\n    float4 t      = {0.f, 0.f, 0.f, 0.f};\n    float4 center = tex2D<float4>(rgbaTex, x, y);\n\n    for (int i = -r; i <= r; i++) {\n        for (int j = -r; j <= r; j++) {\n            float4 curPix = tex2D<float4>(rgbaTex, x + j, y + i);\n            factor        = cGaussian[i + r] * cGaussian[j + r] * // domain factor\n                     euclideanLen(curPix, center, e_d);           // range factor\n\n            t += factor * curPix;\n            sum += factor;\n        }\n    }\n\n    od[y * w + x] = rgbaFloatToInt(t / sum);\n}\n\nextern \"C\" void initTexture(int width, int height, uint *hImage)\n{\n    // copy image data to array\n    checkCudaErrors(hipMallocPitch(&dImage, &pitch, sizeof(uint) * width, height));\n    checkCudaErrors(hipMallocPitch(&dTemp, &pitch, sizeof(uint) * width, height));\n    checkCudaErrors(hipMemcpy2D(\n        dImage, pitch, hImage, sizeof(uint) * width, sizeof(uint) * width, height, hipMemcpyHostToDevice));\n\n    // texture<uchar4, 2, cudaReadModeNormalizedFloat> rgbaTex;\n    hipChannelFormatDesc desc = hipCreateChannelDesc<uchar4>();\n    hipResourceDesc      texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                  = hipResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = dImage;\n    texRes.res.pitch2D.desc         = desc;\n    texRes.res.pitch2D.width        = width;\n    texRes.res.pitch2D.height       = height;\n    texRes.res.pitch2D.pitchInBytes = pitch;\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&rgbaTexdImage, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                  = hipResourceTypePitch2D;\n    texRes.res.pitch2D.devPtr       = dTemp;\n    texRes.res.pitch2D.desc         = desc;\n    texRes.res.pitch2D.width        = width;\n    texRes.res.pitch2D.height       = height;\n    texRes.res.pitch2D.pitchInBytes = pitch;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&rgbaTexdTemp, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeTextures()\n{\n    checkCudaErrors(hipDestroyTextureObject(rgbaTexdImage));\n    checkCudaErrors(hipDestroyTextureObject(rgbaTexdTemp));\n    checkCudaErrors(hipFree(dImage));\n    checkCudaErrors(hipFree(dTemp));\n}\n\n/*\n    Because a 2D gaussian mask is symmetry in row and column,\n    here only generate a 1D mask, and use the product by row\n    and column index later.\n\n    1D gaussian distribution :\n        g(x, d) -- C * exp(-x^2/d^2), C is a constant amplifier\n\n    parameters:\n    og - output gaussian array in global memory\n    delta - the 2nd parameter 'd' in the above function\n    radius - half of the filter size\n             (total filter size = 2 * radius + 1)\n*/\nextern \"C\" void updateGaussian(float delta, int radius)\n{\n    float fGaussian[64];\n\n    for (int i = 0; i < 2 * radius + 1; ++i) {\n        float x      = (float)(i - radius);\n        fGaussian[i] = expf(-(x * x) / (2 * delta * delta));\n    }\n\n    checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(cGaussian), fGaussian, sizeof(float) * (2 * radius + 1)));\n}\n\n/*\n    Perform 2D bilateral filter on image using CUDA\n\n    Parameters:\n    d_dest - pointer to destination image in device memory\n    width  - image width\n    height - image height\n    e_d    - euclidean delta\n    radius - filter radius\n    iterations - number of iterations\n*/\n\n// RGBA version\nextern \"C\" double bilateralFilterRGBA(uint               *dDest,\n                                      int                 width,\n                                      int                 height,\n                                      float               e_d,\n                                      int                 radius,\n                                      int                 iterations,\n                                      StopWatchInterface *timer)\n{\n    // var for kernel computation timing\n    double dKernelTime;\n\n    for (int i = 0; i < iterations; i++) {\n        // sync host and start kernel computation timer\n        dKernelTime = 0.0;\n        checkCudaErrors(hipDeviceSynchronize());\n        sdkResetTimer(&timer);\n\n        dim3 gridSize((width + 16 - 1) / 16, (height + 16 - 1) / 16);\n        dim3 blockSize(16, 16);\n\n        if (iterations > 1) {\n            d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdTemp);\n        }\n        else {\n            d_bilateral_filter<<<gridSize, blockSize>>>(dDest, width, height, e_d, radius, rgbaTexdImage);\n        }\n\n        // sync host and stop computation timer\n        checkCudaErrors(hipDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(hipMemcpy2D(\n                dTemp, pitch, dDest, sizeof(int) * width, sizeof(int) * width, height, hipMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/bilateralFilter/bilateral_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n\n#include \"Mandelbrot_kernel.cuh\"\n#include \"Mandelbrot_kernel.h\"\n#include \"helper_cuda.h\"\n\n// The Mandelbrot CUDA GPU thread function\n\ntemplate <class T>\n__global__ void Mandelbrot0(uchar4      *dst,\n                            const int    imageW,\n                            const int    imageH,\n                            const int    crunch,\n                            const T      xOff,\n                            const T      yOff,\n                            const T      xJP,\n                            const T      yJP,\n                            const T      scale,\n                            const uchar4 colors,\n                            const int    frame,\n                            const int    animationFrame,\n                            const int    gridWidth,\n                            const int    numBlocks,\n                            const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Calculate the location\n            const T xPos = (T)ix * scale + xOff;\n            const T yPos = (T)iy * scale + yOff;\n\n            // Calculate the Mandelbrot index for the current location\n            int m = CalcMandelbrot<T>(xPos, yPos, xJP, yJP, crunch, isJ);\n            //            int m = blockIdx.x;         // uncomment to see scheduling\n            //            order\n            m = m > 0 ? crunch - m : 0;\n\n            // Convert the Mandelbrot index into a color\n            uchar4 color;\n\n            if (m) {\n                m += animationFrame;\n                color.x = m * colors.x;\n                color.y = m * colors.y;\n                color.z = m * colors.z;\n            }\n            else {\n                color.x = 0;\n                color.y = 0;\n                color.z = 0;\n            }\n\n            // Output the pixel\n            int pixel = imageW * iy + ix;\n\n            if (frame == 0) {\n                color.w    = 0;\n                dst[pixel] = color;\n            }\n            else {\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // Mandelbrot0\n\n// The Mandelbrot CUDA GPU thread function (double single version)\n__global__ void MandelbrotDS0(uchar4      *dst,\n                              const int    imageW,\n                              const int    imageH,\n                              const int    crunch,\n                              const float  xOff0,\n                              const float  xOff1,\n                              const float  yOff0,\n                              const float  yOff1,\n                              const float  xJP,\n                              const float  yJP,\n                              const float  scale,\n                              const uchar4 colors,\n                              const int    frame,\n                              const int    animationFrame,\n                              const int    gridWidth,\n                              const int    numBlocks,\n                              const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Calculate the location\n            float xPos0 = (float)ix * scale;\n            float xPos1 = 0.0f;\n            float yPos0 = (float)iy * scale;\n            float yPos1 = 0.0f;\n            dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);\n            dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);\n\n            // Calculate the Mandelbrot index for the current location\n            int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);\n            m     = m > 0 ? crunch - m : 0;\n\n            // Convert the Mandelbrot index into a color\n            uchar4 color;\n\n            if (m) {\n                m += animationFrame;\n                color.x = m * colors.x;\n                color.y = m * colors.y;\n                color.z = m * colors.z;\n            }\n            else {\n                color.x = 0;\n                color.y = 0;\n                color.z = 0;\n            }\n\n            // Output the pixel\n            int pixel = imageW * iy + ix;\n\n            if (frame == 0) {\n                color.w    = 0;\n                dst[pixel] = color;\n            }\n            else {\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n} // MandelbrotDS0\n\n// The Mandelbrot secondary AA pass CUDA GPU thread function\ntemplate <class T>\n__global__ void Mandelbrot1(uchar4      *dst,\n                            const int    imageW,\n                            const int    imageH,\n                            const int    crunch,\n                            const T      xOff,\n                            const T      yOff,\n                            const T      xJP,\n                            const T      yJP,\n                            const T      scale,\n                            const uchar4 colors,\n                            const int    frame,\n                            const int    animationFrame,\n                            const int    gridWidth,\n                            const int    numBlocks,\n                            const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Get the current pixel color\n            int    pixel      = imageW * iy + ix;\n            uchar4 pixelColor = dst[pixel];\n            int    count      = 0;\n\n            // Search for pixels out of tolerance surrounding the current pixel\n            if (ix > 0) {\n                count += CheckColors(pixelColor, dst[pixel - 1]);\n            }\n\n            if (ix + 1 < imageW) {\n                count += CheckColors(pixelColor, dst[pixel + 1]);\n            }\n\n            if (iy > 0) {\n                count += CheckColors(pixelColor, dst[pixel - imageW]);\n            }\n\n            if (iy + 1 < imageH) {\n                count += CheckColors(pixelColor, dst[pixel + imageW]);\n            }\n\n            if (count) {\n                // Calculate the location\n                const T xPos = (T)ix * scale + xOff;\n                const T yPos = (T)iy * scale + yOff;\n\n                // Calculate the Mandelbrot index for the current location\n                int m = CalcMandelbrot(xPos, yPos, xJP, yJP, crunch, isJ);\n                m     = m > 0 ? crunch - m : 0;\n\n                // Convert the Mandelbrot index into a color\n                uchar4 color;\n\n                if (m) {\n                    m += animationFrame;\n                    color.x = m * colors.x;\n                    color.y = m * colors.y;\n                    color.z = m * colors.z;\n                }\n                else {\n                    color.x = 0;\n                    color.y = 0;\n                    color.z = 0;\n                }\n\n                // Output the pixel\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // Mandelbrot1\n\n// The Mandelbrot secondary AA pass CUDA GPU thread function (double single\n// version)\n__global__ void MandelbrotDS1(uchar4      *dst,\n                              const int    imageW,\n                              const int    imageH,\n                              const int    crunch,\n                              const float  xOff0,\n                              const float  xOff1,\n                              const float  yOff0,\n                              const float  yOff1,\n                              const float  xJP,\n                              const float  yJP,\n                              const float  scale,\n                              const uchar4 colors,\n                              const int    frame,\n                              const int    animationFrame,\n                              const int    gridWidth,\n                              const int    numBlocks,\n                              const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Get the current pixel color\n            int    pixel      = imageW * iy + ix;\n            uchar4 pixelColor = dst[pixel];\n            int    count      = 0;\n\n            // Search for pixels out of tolerance surrounding the current pixel\n            if (ix > 0) {\n                count += CheckColors(pixelColor, dst[pixel - 1]);\n            }\n\n            if (ix + 1 < imageW) {\n                count += CheckColors(pixelColor, dst[pixel + 1]);\n            }\n\n            if (iy > 0) {\n                count += CheckColors(pixelColor, dst[pixel - imageW]);\n            }\n\n            if (iy + 1 < imageH) {\n                count += CheckColors(pixelColor, dst[pixel + imageW]);\n            }\n\n            if (count) {\n                // Calculate the location\n                float xPos0 = (float)ix * scale;\n                float xPos1 = 0.0f;\n                float yPos0 = (float)iy * scale;\n                float yPos1 = 0.0f;\n                dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);\n                dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);\n\n                // Calculate the Mandelbrot index for the current location\n                int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);\n                m     = m > 0 ? crunch - m : 0;\n\n                // Convert the Mandelbrot index into a color\n                uchar4 color;\n\n                if (m) {\n                    m += animationFrame;\n                    color.x = m * colors.x;\n                    color.y = m * colors.y;\n                    color.z = m * colors.z;\n                }\n                else {\n                    color.x = 0;\n                    color.y = 0;\n                    color.z = 0;\n                }\n\n                // Output the pixel\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // MandelbrotDS1\n\n// The host CPU Mandelbrot thread spawner\nvoid RunMandelbrot0(uchar4      *dst,\n                    const int    imageW,\n                    const int    imageH,\n                    const int    crunch,\n                    const double xOff,\n                    const double yOff,\n                    const double xjp,\n                    const double yjp,\n                    const double scale,\n                    const uchar4 colors,\n                    const int    frame,\n                    const int    animationFrame,\n                    const int    mode,\n                    const int    numSMs,\n                    const bool   isJ,\n                    int          version)\n{\n    dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);\n    dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));\n\n    int numWorkerBlocks = numSMs;\n\n    switch (mode) {\n    default:\n    case 0:\n        Mandelbrot0<float><<<numWorkerBlocks, threads>>>(dst,\n                                                         imageW,\n                                                         imageH,\n                                                         crunch,\n                                                         (float)xOff,\n                                                         (float)yOff,\n                                                         (float)xjp,\n                                                         (float)yjp,\n                                                         (float)scale,\n                                                         colors,\n                                                         frame,\n                                                         animationFrame,\n                                                         grid.x,\n                                                         grid.x * grid.y,\n                                                         isJ);\n        break;\n    case 1:\n        float x0, x1, y0, y1;\n        dsdeq(x0, x1, xOff);\n        dsdeq(y0, y1, yOff);\n        MandelbrotDS0<<<numWorkerBlocks, threads>>>(dst,\n                                                    imageW,\n                                                    imageH,\n                                                    crunch,\n                                                    x0,\n                                                    x1,\n                                                    y0,\n                                                    y1,\n                                                    (float)xjp,\n                                                    (float)yjp,\n                                                    (float)scale,\n                                                    colors,\n                                                    frame,\n                                                    animationFrame,\n                                                    grid.x,\n                                                    grid.x * grid.y,\n                                                    isJ);\n        break;\n    case 2:\n        Mandelbrot0<double><<<numWorkerBlocks, threads>>>(dst,\n                                                          imageW,\n                                                          imageH,\n                                                          crunch,\n                                                          xOff,\n                                                          yOff,\n                                                          xjp,\n                                                          yjp,\n                                                          scale,\n                                                          colors,\n                                                          frame,\n                                                          animationFrame,\n                                                          grid.x,\n                                                          grid.x * grid.y,\n                                                          isJ);\n        break;\n    }\n\n    getLastCudaError(\"Mandelbrot0 kernel execution failed.\\n\");\n} // RunMandelbrot0\n\n// The host CPU Mandelbrot thread spawner\nvoid RunMandelbrot1(uchar4      *dst,\n                    const int    imageW,\n                    const int    imageH,\n                    const int    crunch,\n                    const double xOff,\n                    const double yOff,\n                    const double xjp,\n                    const double yjp,\n                    const double scale,\n                    const uchar4 colors,\n                    const int    frame,\n                    const int    animationFrame,\n                    const int    mode,\n                    const int    numSMs,\n                    const bool   isJ,\n                    int          version)\n{\n    dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);\n    dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));\n\n    int numWorkerBlocks = numSMs;\n\n    switch (mode) {\n    default:\n    case 0:\n        Mandelbrot1<float><<<numWorkerBlocks, threads>>>(dst,\n                                                         imageW,\n                                                         imageH,\n                                                         crunch,\n                                                         (float)xOff,\n                                                         (float)yOff,\n                                                         (float)xjp,\n                                                         (float)yjp,\n                                                         (float)scale,\n                                                         colors,\n                                                         frame,\n                                                         animationFrame,\n                                                         grid.x,\n                                                         grid.x * grid.y,\n                                                         isJ);\n        break;\n    case 1:\n        float x0, x1, y0, y1;\n        dsdeq(x0, x1, xOff);\n        dsdeq(y0, y1, yOff);\n        MandelbrotDS1<<<numWorkerBlocks, threads>>>(dst,\n                                                    imageW,\n                                                    imageH,\n                                                    crunch,\n                                                    x0,\n                                                    x1,\n                                                    y0,\n                                                    y1,\n                                                    (float)xjp,\n                                                    (float)yjp,\n                                                    (float)scale,\n                                                    colors,\n                                                    frame,\n                                                    animationFrame,\n                                                    grid.x,\n                                                    grid.x * grid.y,\n                                                    isJ);\n        break;\n    case 2:\n        Mandelbrot1<double><<<numWorkerBlocks, threads>>>(dst,\n                                                          imageW,\n                                                          imageH,\n                                                          crunch,\n                                                          xOff,\n                                                          yOff,\n                                                          xjp,\n                                                          yjp,\n                                                          scale,\n                                                          colors,\n                                                          frame,\n                                                          animationFrame,\n                                                          grid.x,\n                                                          grid.x * grid.y,\n                                                          isJ);\n        break;\n    }\n\n    getLastCudaError(\"Mandelbrot1 kernel execution failed.\\n\");\n} // RunMandelbrot1\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#include \"Mandelbrot_kernel.cuh\"\n#include \"Mandelbrot_kernel.h\"\n#include \"hip/hip_runtime_api.h\"\n\n// The Mandelbrot CUDA GPU thread function\n\ntemplate <class T>\n__global__ void Mandelbrot0(uchar4      *dst,\n                            const int    imageW,\n                            const int    imageH,\n                            const int    crunch,\n                            const T      xOff,\n                            const T      yOff,\n                            const T      xJP,\n                            const T      yJP,\n                            const T      scale,\n                            const uchar4 colors,\n                            const int    frame,\n                            const int    animationFrame,\n                            const int    gridWidth,\n                            const int    numBlocks,\n                            const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Calculate the location\n            const T xPos = (T)ix * scale + xOff;\n            const T yPos = (T)iy * scale + yOff;\n\n            // Calculate the Mandelbrot index for the current location\n            int m = CalcMandelbrot<T>(xPos, yPos, xJP, yJP, crunch, isJ);\n            //            int m = blockIdx.x;         // uncomment to see scheduling\n            //            order\n            m = m > 0 ? crunch - m : 0;\n\n            // Convert the Mandelbrot index into a color\n            uchar4 color;\n\n            if (m) {\n                m += animationFrame;\n                color.x = m * colors.x;\n                color.y = m * colors.y;\n                color.z = m * colors.z;\n            }\n            else {\n                color.x = 0;\n                color.y = 0;\n                color.z = 0;\n            }\n\n            // Output the pixel\n            int pixel = imageW * iy + ix;\n\n            if (frame == 0) {\n                color.w    = 0;\n                dst[pixel] = color;\n            }\n            else {\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // Mandelbrot0\n\n// The Mandelbrot CUDA GPU thread function (double single version)\n__global__ void MandelbrotDS0(uchar4      *dst,\n                              const int    imageW,\n                              const int    imageH,\n                              const int    crunch,\n                              const float  xOff0,\n                              const float  xOff1,\n                              const float  yOff0,\n                              const float  yOff1,\n                              const float  xJP,\n                              const float  yJP,\n                              const float  scale,\n                              const uchar4 colors,\n                              const int    frame,\n                              const int    animationFrame,\n                              const int    gridWidth,\n                              const int    numBlocks,\n                              const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Calculate the location\n            float xPos0 = (float)ix * scale;\n            float xPos1 = 0.0f;\n            float yPos0 = (float)iy * scale;\n            float yPos1 = 0.0f;\n            dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);\n            dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);\n\n            // Calculate the Mandelbrot index for the current location\n            int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);\n            m     = m > 0 ? crunch - m : 0;\n\n            // Convert the Mandelbrot index into a color\n            uchar4 color;\n\n            if (m) {\n                m += animationFrame;\n                color.x = m * colors.x;\n                color.y = m * colors.y;\n                color.z = m * colors.z;\n            }\n            else {\n                color.x = 0;\n                color.y = 0;\n                color.z = 0;\n            }\n\n            // Output the pixel\n            int pixel = imageW * iy + ix;\n\n            if (frame == 0) {\n                color.w    = 0;\n                dst[pixel] = color;\n            }\n            else {\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (dst[pixel].x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (dst[pixel].y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (dst[pixel].z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n} // MandelbrotDS0\n\n// The Mandelbrot secondary AA pass CUDA GPU thread function\ntemplate <class T>\n__global__ void Mandelbrot1(uchar4      *dst,\n                            const int    imageW,\n                            const int    imageH,\n                            const int    crunch,\n                            const T      xOff,\n                            const T      yOff,\n                            const T      xJP,\n                            const T      yJP,\n                            const T      scale,\n                            const uchar4 colors,\n                            const int    frame,\n                            const int    animationFrame,\n                            const int    gridWidth,\n                            const int    numBlocks,\n                            const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Get the current pixel color\n            int    pixel      = imageW * iy + ix;\n            uchar4 pixelColor = dst[pixel];\n            int    count      = 0;\n\n            // Search for pixels out of tolerance surrounding the current pixel\n            if (ix > 0) {\n                count += CheckColors(pixelColor, dst[pixel - 1]);\n            }\n\n            if (ix + 1 < imageW) {\n                count += CheckColors(pixelColor, dst[pixel + 1]);\n            }\n\n            if (iy > 0) {\n                count += CheckColors(pixelColor, dst[pixel - imageW]);\n            }\n\n            if (iy + 1 < imageH) {\n                count += CheckColors(pixelColor, dst[pixel + imageW]);\n            }\n\n            if (count) {\n                // Calculate the location\n                const T xPos = (T)ix * scale + xOff;\n                const T yPos = (T)iy * scale + yOff;\n\n                // Calculate the Mandelbrot index for the current location\n                int m = CalcMandelbrot(xPos, yPos, xJP, yJP, crunch, isJ);\n                m     = m > 0 ? crunch - m : 0;\n\n                // Convert the Mandelbrot index into a color\n                uchar4 color;\n\n                if (m) {\n                    m += animationFrame;\n                    color.x = m * colors.x;\n                    color.y = m * colors.y;\n                    color.z = m * colors.z;\n                }\n                else {\n                    color.x = 0;\n                    color.y = 0;\n                    color.z = 0;\n                }\n\n                // Output the pixel\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // Mandelbrot1\n\n// The Mandelbrot secondary AA pass CUDA GPU thread function (double single\n// version)\n__global__ void MandelbrotDS1(uchar4      *dst,\n                              const int    imageW,\n                              const int    imageH,\n                              const int    crunch,\n                              const float  xOff0,\n                              const float  xOff1,\n                              const float  yOff0,\n                              const float  yOff1,\n                              const float  xJP,\n                              const float  yJP,\n                              const float  scale,\n                              const uchar4 colors,\n                              const int    frame,\n                              const int    animationFrame,\n                              const int    gridWidth,\n                              const int    numBlocks,\n                              const bool   isJ)\n{\n    // loop until all blocks completed\n    for (unsigned int blockIndex = blockIdx.x; blockIndex < numBlocks; blockIndex += gridDim.x) {\n        unsigned int blockX = blockIndex % gridWidth;\n        unsigned int blockY = blockIndex / gridWidth;\n\n        // process this block\n        const int ix = blockDim.x * blockX + threadIdx.x;\n        const int iy = blockDim.y * blockY + threadIdx.y;\n\n        if ((ix < imageW) && (iy < imageH)) {\n            // Get the current pixel color\n            int    pixel      = imageW * iy + ix;\n            uchar4 pixelColor = dst[pixel];\n            int    count      = 0;\n\n            // Search for pixels out of tolerance surrounding the current pixel\n            if (ix > 0) {\n                count += CheckColors(pixelColor, dst[pixel - 1]);\n            }\n\n            if (ix + 1 < imageW) {\n                count += CheckColors(pixelColor, dst[pixel + 1]);\n            }\n\n            if (iy > 0) {\n                count += CheckColors(pixelColor, dst[pixel - imageW]);\n            }\n\n            if (iy + 1 < imageH) {\n                count += CheckColors(pixelColor, dst[pixel + imageW]);\n            }\n\n            if (count) {\n                // Calculate the location\n                float xPos0 = (float)ix * scale;\n                float xPos1 = 0.0f;\n                float yPos0 = (float)iy * scale;\n                float yPos1 = 0.0f;\n                dsadd(xPos0, xPos1, xPos0, xPos1, xOff0, xOff1);\n                dsadd(yPos0, yPos1, yPos0, yPos1, yOff0, yOff1);\n\n                // Calculate the Mandelbrot index for the current location\n                int m = CalcMandelbrotDS(xPos0, xPos1, yPos0, yPos1, xJP, yJP, crunch, isJ);\n                m     = m > 0 ? crunch - m : 0;\n\n                // Convert the Mandelbrot index into a color\n                uchar4 color;\n\n                if (m) {\n                    m += animationFrame;\n                    color.x = m * colors.x;\n                    color.y = m * colors.y;\n                    color.z = m * colors.z;\n                }\n                else {\n                    color.x = 0;\n                    color.y = 0;\n                    color.z = 0;\n                }\n\n                // Output the pixel\n                int frame1   = frame + 1;\n                int frame2   = frame1 / 2;\n                dst[pixel].x = (pixelColor.x * frame + color.x + frame2) / frame1;\n                dst[pixel].y = (pixelColor.y * frame + color.y + frame2) / frame1;\n                dst[pixel].z = (pixelColor.z * frame + color.z + frame2) / frame1;\n            }\n        }\n    }\n\n} // MandelbrotDS1\n\n// The host CPU Mandelbrot thread spawner\nvoid RunMandelbrot0(uchar4      *dst,\n                    const int    imageW,\n                    const int    imageH,\n                    const int    crunch,\n                    const double xOff,\n                    const double yOff,\n                    const double xjp,\n                    const double yjp,\n                    const double scale,\n                    const uchar4 colors,\n                    const int    frame,\n                    const int    animationFrame,\n                    const int    mode,\n                    const int    numSMs,\n                    const bool   isJ,\n                    int          version)\n{\n    dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);\n    dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));\n\n    int numWorkerBlocks = numSMs;\n\n    switch (mode) {\n    default:\n    case 0:\n        Mandelbrot0<float><<<numWorkerBlocks, threads>>>(dst,\n                                                         imageW,\n                                                         imageH,\n                                                         crunch,\n                                                         (float)xOff,\n                                                         (float)yOff,\n                                                         (float)xjp,\n                                                         (float)yjp,\n                                                         (float)scale,\n                                                         colors,\n                                                         frame,\n                                                         animationFrame,\n                                                         grid.x,\n                                                         grid.x * grid.y,\n                                                         isJ);\n        break;\n    case 1:\n        float x0, x1, y0, y1;\n        dsdeq(x0, x1, xOff);\n        dsdeq(y0, y1, yOff);\n        MandelbrotDS0<<<numWorkerBlocks, threads>>>(dst,\n                                                    imageW,\n                                                    imageH,\n                                                    crunch,\n                                                    x0,\n                                                    x1,\n                                                    y0,\n                                                    y1,\n                                                    (float)xjp,\n                                                    (float)yjp,\n                                                    (float)scale,\n                                                    colors,\n                                                    frame,\n                                                    animationFrame,\n                                                    grid.x,\n                                                    grid.x * grid.y,\n                                                    isJ);\n        break;\n    case 2:\n        Mandelbrot0<double><<<numWorkerBlocks, threads>>>(dst,\n                                                          imageW,\n                                                          imageH,\n                                                          crunch,\n                                                          xOff,\n                                                          yOff,\n                                                          xjp,\n                                                          yjp,\n                                                          scale,\n                                                          colors,\n                                                          frame,\n                                                          animationFrame,\n                                                          grid.x,\n                                                          grid.x * grid.y,\n                                                          isJ);\n        break;\n    }\n\n    getLastCudaError(\"Mandelbrot0 kernel execution failed.\\n\");\n} // RunMandelbrot0\n\n// The host CPU Mandelbrot thread spawner\nvoid RunMandelbrot1(uchar4      *dst,\n                    const int    imageW,\n                    const int    imageH,\n                    const int    crunch,\n                    const double xOff,\n                    const double yOff,\n                    const double xjp,\n                    const double yjp,\n                    const double scale,\n                    const uchar4 colors,\n                    const int    frame,\n                    const int    animationFrame,\n                    const int    mode,\n                    const int    numSMs,\n                    const bool   isJ,\n                    int          version)\n{\n    dim3 threads(BLOCKDIM_X, BLOCKDIM_Y);\n    dim3 grid(iDivUp(imageW, BLOCKDIM_X), iDivUp(imageH, BLOCKDIM_Y));\n\n    int numWorkerBlocks = numSMs;\n\n    switch (mode) {\n    default:\n    case 0:\n        Mandelbrot1<float><<<numWorkerBlocks, threads>>>(dst,\n                                                         imageW,\n                                                         imageH,\n                                                         crunch,\n                                                         (float)xOff,\n                                                         (float)yOff,\n                                                         (float)xjp,\n                                                         (float)yjp,\n                                                         (float)scale,\n                                                         colors,\n                                                         frame,\n                                                         animationFrame,\n                                                         grid.x,\n                                                         grid.x * grid.y,\n                                                         isJ);\n        break;\n    case 1:\n        float x0, x1, y0, y1;\n        dsdeq(x0, x1, xOff);\n        dsdeq(y0, y1, yOff);\n        MandelbrotDS1<<<numWorkerBlocks, threads>>>(dst,\n                                                    imageW,\n                                                    imageH,\n                                                    crunch,\n                                                    x0,\n                                                    x1,\n                                                    y0,\n                                                    y1,\n                                                    (float)xjp,\n                                                    (float)yjp,\n                                                    (float)scale,\n                                                    colors,\n                                                    frame,\n                                                    animationFrame,\n                                                    grid.x,\n                                                    grid.x * grid.y,\n                                                    isJ);\n        break;\n    case 2:\n        Mandelbrot1<double><<<numWorkerBlocks, threads>>>(dst,\n                                                          imageW,\n                                                          imageH,\n                                                          crunch,\n                                                          xOff,\n                                                          yOff,\n                                                          xjp,\n                                                          yjp,\n                                                          scale,\n                                                          colors,\n                                                          frame,\n                                                          animationFrame,\n                                                          grid.x,\n                                                          grid.x * grid.y,\n                                                          isJ);\n        break;\n    }\n\n    getLastCudaError(\"Mandelbrot1 kernel execution failed.\\n\");\n} // RunMandelbrot1\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/Mandelbrot/Mandelbrot_cuda.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define PI 3.1415926536f\n\n/*\n * Paint a 2D surface with a moving bulls-eye pattern.  The \"face\" parameter\n * selects\n * between 6 different colors to use.  We will use a different color on each\n * face of a\n * cube map.\n */\n__global__ void cuda_kernel_texture_cube(char *surface, int width, int height, size_t pitch, int face, float t)\n{\n    int            x = blockIdx.x * blockDim.x + threadIdx.x;\n    int            y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned char *pixel;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // get a pointer to this pixel\n    pixel = (unsigned char *)(surface + y * pitch) + 4 * x;\n\n    // populate it\n    float         theta_x = (2.0f * x) / width - 1.0f;\n    float         theta_y = (2.0f * y) / height - 1.0f;\n    float         theta   = 2.0f * PI * sqrt(theta_x * theta_x + theta_y * theta_y);\n    unsigned char value   = 255 * (0.6f + 0.4f * cos(theta + t));\n\n    pixel[3] = 255; // alpha\n\n    if (face % 2) {\n        pixel[0]        =      // blue\n            pixel[1]    =      // green\n            pixel[2]    = 0.5; // red\n        pixel[face / 2] = value;\n    }\n    else {\n        pixel[0]        =        // blue\n            pixel[1]    =        // green\n            pixel[2]    = value; // red\n        pixel[face / 2] = 0.5;\n    }\n}\n\nextern \"C\" void cuda_texture_cube(void *surface, int width, int height, size_t pitch, int face, float t)\n{\n    cudaError_t error = cudaSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_cube<<<Dg, Db>>>((char *)surface, width, height, pitch, face, t);\n\n    error = cudaGetLastError();\n\n    if (error != cudaSuccess) {\n        printf(\"cuda_kernel_texture_cube() failed to launch error = %d\\n\", error);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define PI 3.1415926536f\n\n/*\n * Paint a 2D surface with a moving bulls-eye pattern.  The \"face\" parameter\n * selects\n * between 6 different colors to use.  We will use a different color on each\n * face of a\n * cube map.\n */\n__global__ void cuda_kernel_texture_cube(char *surface, int width, int height, size_t pitch, int face, float t)\n{\n    int            x = blockIdx.x * blockDim.x + threadIdx.x;\n    int            y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned char *pixel;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // get a pointer to this pixel\n    pixel = (unsigned char *)(surface + y * pitch) + 4 * x;\n\n    // populate it\n    float         theta_x = (2.0f * x) / width - 1.0f;\n    float         theta_y = (2.0f * y) / height - 1.0f;\n    float         theta   = 2.0f * PI * sqrt(theta_x * theta_x + theta_y * theta_y);\n    unsigned char value   = 255 * (0.6f + 0.4f * cos(theta + t));\n\n    pixel[3] = 255; // alpha\n\n    if (face % 2) {\n        pixel[0]        =      // blue\n            pixel[1]    =      // green\n            pixel[2]    = 0.5; // red\n        pixel[face / 2] = value;\n    }\n    else {\n        pixel[0]        =        // blue\n            pixel[1]    =        // green\n            pixel[2]    = value; // red\n        pixel[face / 2] = 0.5;\n    }\n}\n\nextern \"C\" void cuda_texture_cube(void *surface, int width, int height, size_t pitch, int face, float t)\n{\n    hipError_t error = hipSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_cube<<<Dg, Db>>>((char *)surface, width, height, pitch, face, t);\n\n    error = hipGetLastError();\n\n    if (error != hipSuccess) {\n        printf(\"cuda_kernel_texture_cube() failed to launch error = %d\\n\", error);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleD3D11Texture/texture_cube.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/*\n * Paint a 3D texture with a gradient in X (blue) and Z (green), and have every\n * other Z slice have full red.\n */\n__global__ void cuda_kernel_texture_3d(unsigned char *surface,\n                                       int            width,\n                                       int            height,\n                                       int            depth,\n                                       size_t         pitch,\n                                       size_t         pitchSlice,\n                                       float          t)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // walk across the Z slices of this texture.  it should be noted that\n    // this is far from optimal data access.\n    for (int z = 0; z < depth; ++z) {\n        // get a pointer to this pixel\n        unsigned char *pixel = surface + z * pitchSlice + y * pitch + 4 * x;\n        pixel[0] = (unsigned char)(255.f * (0.5f + 0.5f * cos(t + (x * x + y * y + z * z) * 0.0001f * 3.14f))); // red\n        pixel[1] = (unsigned char)(255.f * (0.5f + 0.5f * sin(t + (x * x + y * y + z * z) * 0.0001f * 3.14f))); // green\n        pixel[2] = (unsigned char)0;                                                                            // blue\n        pixel[3] = 255;                                                                                         // alpha\n    }\n}\n\nextern \"C\" void\ncuda_texture_3d(void *surface, int width, int height, int depth, size_t pitch, size_t pitchSlice, float t)\n{\n    cudaError_t error = cudaSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_3d<<<Dg, Db>>>((unsigned char *)surface, width, height, depth, pitch, pitchSlice, t);\n\n    error = cudaGetLastError();\n\n    if (error != cudaSuccess) {\n        printf(\"cuda_kernel_texture_3d() failed to launch error = %d\\n\", error);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/*\n * Paint a 3D texture with a gradient in X (blue) and Z (green), and have every\n * other Z slice have full red.\n */\n__global__ void cuda_kernel_texture_3d(unsigned char *surface,\n                                       int            width,\n                                       int            height,\n                                       int            depth,\n                                       size_t         pitch,\n                                       size_t         pitchSlice,\n                                       float          t)\n{\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // walk across the Z slices of this texture.  it should be noted that\n    // this is far from optimal data access.\n    for (int z = 0; z < depth; ++z) {\n        // get a pointer to this pixel\n        unsigned char *pixel = surface + z * pitchSlice + y * pitch + 4 * x;\n        pixel[0] = (unsigned char)(255.f * (0.5f + 0.5f * cos(t + (x * x + y * y + z * z) * 0.0001f * 3.14f))); // red\n        pixel[1] = (unsigned char)(255.f * (0.5f + 0.5f * sin(t + (x * x + y * y + z * z) * 0.0001f * 3.14f))); // green\n        pixel[2] = (unsigned char)0;                                                                            // blue\n        pixel[3] = 255;                                                                                         // alpha\n    }\n}\n\nextern \"C\" void\ncuda_texture_3d(void *surface, int width, int height, int depth, size_t pitch, size_t pitchSlice, float t)\n{\n    hipError_t error = hipSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_3d<<<Dg, Db>>>((unsigned char *)surface, width, height, depth, pitch, pitchSlice, t);\n\n    error = hipGetLastError();\n\n    if (error != hipSuccess) {\n        printf(\"cuda_kernel_texture_3d() failed to launch error = %d\\n\", error);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleD3D11Texture/texture_3d.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define PI 3.1415926536f\n\n/*\n * Paint a 2D texture with a moving red/green hatch pattern on a\n * strobing blue background.  Note that this kernel reads to and\n * writes from the texture, hence why this texture was not mapped\n * as WriteDiscard.\n */\n__global__ void cuda_kernel_texture_2d(unsigned char *surface, int width, int height, size_t pitch, float t)\n{\n    int    x = blockIdx.x * blockDim.x + threadIdx.x;\n    int    y = blockIdx.y * blockDim.y + threadIdx.y;\n    float *pixel;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // get a pointer to the pixel at (x,y)\n    pixel = (float *)(surface + y * pitch) + 4 * x;\n\n    // populate it\n    float value_x = 0.5f + 0.5f * cos(t + 10.0f * ((2.0f * x) / width - 1.0f));\n    float value_y = 0.5f + 0.5f * cos(t + 10.0f * ((2.0f * y) / height - 1.0f));\n    pixel[0]      = 0.5 * pixel[0] + 0.5 * pow(value_x, 3.0f); // red\n    pixel[1]      = 0.5 * pixel[1] + 0.5 * pow(value_y, 3.0f); // green\n    pixel[2]      = 0.5f + 0.5f * cos(t);                      // blue\n    pixel[3]      = 1;                                         // alpha\n}\n\nextern \"C\" void cuda_texture_2d(void *surface, int width, int height, size_t pitch, float t)\n{\n    cudaError_t error = cudaSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_2d<<<Dg, Db>>>((unsigned char *)surface, width, height, pitch, t);\n\n    error = cudaGetLastError();\n\n    if (error != cudaSuccess) {\n        printf(\"cuda_kernel_texture_2d() failed to launch error = %d\\n\", error);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define PI 3.1415926536f\n\n/*\n * Paint a 2D texture with a moving red/green hatch pattern on a\n * strobing blue background.  Note that this kernel reads to and\n * writes from the texture, hence why this texture was not mapped\n * as WriteDiscard.\n */\n__global__ void cuda_kernel_texture_2d(unsigned char *surface, int width, int height, size_t pitch, float t)\n{\n    int    x = blockIdx.x * blockDim.x + threadIdx.x;\n    int    y = blockIdx.y * blockDim.y + threadIdx.y;\n    float *pixel;\n\n    // in the case where, due to quantization into grids, we have\n    // more threads than pixels, skip the threads which don't\n    // correspond to valid pixels\n    if (x >= width || y >= height)\n        return;\n\n    // get a pointer to the pixel at (x,y)\n    pixel = (float *)(surface + y * pitch) + 4 * x;\n\n    // populate it\n    float value_x = 0.5f + 0.5f * cos(t + 10.0f * ((2.0f * x) / width - 1.0f));\n    float value_y = 0.5f + 0.5f * cos(t + 10.0f * ((2.0f * y) / height - 1.0f));\n    pixel[0]      = 0.5 * pixel[0] + 0.5 * pow(value_x, 3.0f); // red\n    pixel[1]      = 0.5 * pixel[1] + 0.5 * pow(value_y, 3.0f); // green\n    pixel[2]      = 0.5f + 0.5f * cos(t);                      // blue\n    pixel[3]      = 1;                                         // alpha\n}\n\nextern \"C\" void cuda_texture_2d(void *surface, int width, int height, size_t pitch, float t)\n{\n    hipError_t error = hipSuccess;\n\n    dim3 Db = dim3(16, 16); // block dimensions are fixed to be 256 threads\n    dim3 Dg = dim3((width + Db.x - 1) / Db.x, (height + Db.y - 1) / Db.y);\n\n    cuda_kernel_texture_2d<<<Dg, Db>>>((unsigned char *)surface, width, height, pitch, t);\n\n    error = hipGetLastError();\n\n    if (error != hipSuccess) {\n        printf(\"cuda_kernel_texture_2d() failed to launch error = %d\\n\", error);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleD3D11Texture/texture_2d.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <helper_cuda.h>\n#include <math.h>\n\n#if defined(__APPLE__) || defined(MACOSX)\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n#include <GLUT/glut.h>\n#else\n#include <GL/freeglut.h>\n#endif\n\n// CUDA standard includes\n#include <cooperative_groups.h>\n#include <cuda_gl_interop.h>\n#include <cuda_runtime.h>\n\nnamespace cg = cooperative_groups;\n\n#include \"bodysystem.h\"\n\n__constant__ float  softeningSquared;\n__constant__ double softeningSquared_fp64;\n\ncudaError_t setSofteningSquared(float softeningSq)\n{\n    return cudaMemcpyToSymbol(softeningSquared, &softeningSq, sizeof(float), 0, cudaMemcpyHostToDevice);\n}\n\ncudaError_t setSofteningSquared(double softeningSq)\n{\n    return cudaMemcpyToSymbol(softeningSquared_fp64, &softeningSq, sizeof(double), 0, cudaMemcpyHostToDevice);\n}\n\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\ntemplate <typename T> __device__ T rsqrt_T(T x) { return rsqrt(x); }\n\ntemplate <> __device__ float rsqrt_T<float>(float x) { return rsqrtf(x); }\n\ntemplate <> __device__ double rsqrt_T<double>(double x) { return rsqrt(x); }\n\n// Macros to simplify shared memory addressing\n#define SX(i) sharedPos[i + blockDim.x * threadIdx.y]\n// This macro is only used when multithreadBodies is true (below)\n#define SX_SUM(i, j) sharedPos[i + blockDim.x * j]\n\ntemplate <typename T> __device__ T getSofteningSquared() { return softeningSquared; }\ntemplate <> __device__ double      getSofteningSquared<double>() { return softeningSquared_fp64; }\n\ntemplate <typename T> struct DeviceData\n{\n    T           *dPos[2]; // mapped host pointers\n    T           *dVel;\n    cudaEvent_t  event;\n    unsigned int offset;\n    unsigned int numBodies;\n};\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\nbodyBodyInteraction(typename vec3<T>::Type ai, typename vec4<T>::Type bi, typename vec4<T>::Type bj)\n{\n    typename vec3<T>::Type r;\n\n    // r_ij  [3 FLOPS]\n    r.x = bj.x - bi.x;\n    r.y = bj.y - bi.y;\n    r.z = bj.z - bi.z;\n\n    // distSqr = dot(r_ij, r_ij) + EPS^2  [6 FLOPS]\n    T distSqr = r.x * r.x + r.y * r.y + r.z * r.z;\n    distSqr += getSofteningSquared<T>();\n\n    // invDistCube =1/distSqr^(3/2)  [4 FLOPS (2 mul, 1 sqrt, 1 inv)]\n    T invDist     = rsqrt_T(distSqr);\n    T invDistCube = invDist * invDist * invDist;\n\n    // s = m_j * invDistCube [1 FLOP]\n    T s = bj.w * invDistCube;\n\n    // a_i =  a_i + s * r_ij [6 FLOPS]\n    ai.x += r.x * s;\n    ai.y += r.y * s;\n    ai.z += r.z * s;\n\n    return ai;\n}\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\ncomputeBodyAccel(typename vec4<T>::Type bodyPos, typename vec4<T>::Type *positions, int numTiles, cg::thread_block cta)\n{\n    typename vec4<T>::Type *sharedPos = SharedMemory<typename vec4<T>::Type>();\n\n    typename vec3<T>::Type acc = {0.0f, 0.0f, 0.0f};\n\n    for (int tile = 0; tile < numTiles; tile++) {\n        sharedPos[threadIdx.x] = positions[tile * blockDim.x + threadIdx.x];\n\n        cg::sync(cta);\n\n// This is the \"tile_calculation\" from the GPUG3 article.\n#pragma unroll 128\n\n        for (unsigned int counter = 0; counter < blockDim.x; counter++) {\n            acc = bodyBodyInteraction<T>(acc, bodyPos, sharedPos[counter]);\n        }\n\n        cg::sync(cta);\n    }\n\n    return acc;\n}\n\ntemplate <typename T>\n__global__ void integrateBodies(typename vec4<T>::Type *__restrict__ newPos,\n                                typename vec4<T>::Type *__restrict__ oldPos,\n                                typename vec4<T>::Type *vel,\n                                unsigned int            deviceOffset,\n                                unsigned int            deviceNumBodies,\n                                float                   deltaTime,\n                                float                   damping,\n                                int                     numTiles)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    int              index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (index >= deviceNumBodies) {\n        return;\n    }\n\n    typename vec4<T>::Type position = oldPos[deviceOffset + index];\n\n    typename vec3<T>::Type accel = computeBodyAccel<T>(position, oldPos, numTiles, cta);\n\n    // acceleration = force / mass;\n    // new velocity = old velocity + acceleration * deltaTime\n    // note we factor out the body's mass from the equation, here and in\n    // bodyBodyInteraction\n    // (because they cancel out).  Thus here force == acceleration\n    typename vec4<T>::Type velocity = vel[deviceOffset + index];\n\n    velocity.x += accel.x * deltaTime;\n    velocity.y += accel.y * deltaTime;\n    velocity.z += accel.z * deltaTime;\n\n    velocity.x *= damping;\n    velocity.y *= damping;\n    velocity.z *= damping;\n\n    // new position = old position + velocity * deltaTime\n    position.x += velocity.x * deltaTime;\n    position.y += velocity.y * deltaTime;\n    position.z += velocity.z * deltaTime;\n\n    // store new position and velocity\n    newPos[deviceOffset + index] = position;\n    vel[deviceOffset + index]    = velocity;\n}\n\ntemplate <typename T>\nvoid integrateNbodySystem(DeviceData<T>         *deviceData,\n                          cudaGraphicsResource **pgres,\n                          unsigned int           currentRead,\n                          float                  deltaTime,\n                          float                  damping,\n                          unsigned int           numBodies,\n                          unsigned int           numDevices,\n                          int                    blockSize,\n                          bool                   bUsePBO)\n{\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[currentRead], cudaGraphicsMapFlagsReadOnly));\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[1 - currentRead], cudaGraphicsMapFlagsWriteDiscard));\n        checkCudaErrors(cudaGraphicsMapResources(2, pgres, 0));\n        size_t bytes;\n        checkCudaErrors(cudaGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[currentRead]), &bytes, pgres[currentRead]));\n        checkCudaErrors(cudaGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[1 - currentRead]), &bytes, pgres[1 - currentRead]));\n    }\n\n    for (unsigned int dev = 0; dev != numDevices; dev++) {\n        if (numDevices > 1) {\n            cudaSetDevice(dev);\n        }\n\n        int numBlocks     = (deviceData[dev].numBodies + blockSize - 1) / blockSize;\n        int numTiles      = (numBodies + blockSize - 1) / blockSize;\n        int sharedMemSize = blockSize * 4 * sizeof(T); // 4 floats for pos\n\n        integrateBodies<T>\n            <<<numBlocks, blockSize, sharedMemSize>>>((typename vec4<T>::Type *)deviceData[dev].dPos[1 - currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dPos[currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dVel,\n                                                      deviceData[dev].offset,\n                                                      deviceData[dev].numBodies,\n                                                      deltaTime,\n                                                      damping,\n                                                      numTiles);\n\n        if (numDevices > 1) {\n            checkCudaErrors(cudaEventRecord(deviceData[dev].event));\n            // MJH: Hack on older driver versions to force kernel launches to flush!\n            cudaStreamQuery(0);\n        }\n\n        // check if kernel invocation generated an error\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    if (numDevices > 1) {\n        for (unsigned int dev = 0; dev < numDevices; dev++) {\n            checkCudaErrors(cudaEventSynchronize(deviceData[dev].event));\n        }\n    }\n\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsUnmapResources(2, pgres, 0));\n    }\n}\n\n// Explicit specializations needed to generate code\ntemplate void integrateNbodySystem<float>(DeviceData<float>     *deviceData,\n                                          cudaGraphicsResource **pgres,\n                                          unsigned int           currentRead,\n                                          float                  deltaTime,\n                                          float                  damping,\n                                          unsigned int           numBodies,\n                                          unsigned int           numDevices,\n                                          int                    blockSize,\n                                          bool                   bUsePBO);\n\ntemplate void integrateNbodySystem<double>(DeviceData<double>    *deviceData,\n                                           cudaGraphicsResource **pgres,\n                                           unsigned int           currentRead,\n                                           float                  deltaTime,\n                                           float                  damping,\n                                           unsigned int           numBodies,\n                                           unsigned int           numDevices,\n                                           int                    blockSize,\n                                           bool                   bUsePBO);\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime_api.h>\n#include <math.h>\n\n#if defined(__APPLE__) || defined(MACOSX)\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n#include <GLUT/glut.h>\n#else\n#include <GL/freeglut.h>\n#endif\n\n// CUDA standard includes\n#include <hip/hip_cooperative_groups.h>\n#include <cuda_gl_interop.h>\n#include <hip/hip_runtime.h>\n\nnamespace cg = cooperative_groups;\n\n#include \"bodysystem.h\"\n\n__constant__ float  softeningSquared;\n__constant__ double softeningSquared_fp64;\n\nhipError_t setSofteningSquared(float softeningSq)\n{\n    return hipMemcpyToSymbol(HIP_SYMBOL(softeningSquared), &softeningSq, sizeof(float), 0, hipMemcpyHostToDevice);\n}\n\nhipError_t setSofteningSquared(double softeningSq)\n{\n    return hipMemcpyToSymbol(HIP_SYMBOL(softeningSquared_fp64), &softeningSq, sizeof(double), 0, hipMemcpyHostToDevice);\n}\n\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\ntemplate <typename T> __device__ T rsqrt_T(T x) { return rsqrt(x); }\n\ntemplate <> __device__ float rsqrt_T<float>(float x) { return rsqrtf(x); }\n\ntemplate <> __device__ double rsqrt_T<double>(double x) { return rsqrt(x); }\n\n// Macros to simplify shared memory addressing\n#define SX(i) sharedPos[i + blockDim.x * threadIdx.y]\n// This macro is only used when multithreadBodies is true (below)\n#define SX_SUM(i, j) sharedPos[i + blockDim.x * j]\n\ntemplate <typename T> __device__ T getSofteningSquared() { return softeningSquared; }\ntemplate <> __device__ double      getSofteningSquared<double>() { return softeningSquared_fp64; }\n\ntemplate <typename T> struct DeviceData\n{\n    T           *dPos[2]; // mapped host pointers\n    T           *dVel;\n    hipEvent_t  event;\n    unsigned int offset;\n    unsigned int numBodies;\n};\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\nbodyBodyInteraction(typename vec3<T>::Type ai, typename vec4<T>::Type bi, typename vec4<T>::Type bj)\n{\n    typename vec3<T>::Type r;\n\n    // r_ij  [3 FLOPS]\n    r.x = bj.x - bi.x;\n    r.y = bj.y - bi.y;\n    r.z = bj.z - bi.z;\n\n    // distSqr = dot(r_ij, r_ij) + EPS^2  [6 FLOPS]\n    T distSqr = r.x * r.x + r.y * r.y + r.z * r.z;\n    distSqr += getSofteningSquared<T>();\n\n    // invDistCube =1/distSqr^(3/2)  [4 FLOPS (2 mul, 1 sqrt, 1 inv)]\n    T invDist     = rsqrt_T(distSqr);\n    T invDistCube = invDist * invDist * invDist;\n\n    // s = m_j * invDistCube [1 FLOP]\n    T s = bj.w * invDistCube;\n\n    // a_i =  a_i + s * r_ij [6 FLOPS]\n    ai.x += r.x * s;\n    ai.y += r.y * s;\n    ai.z += r.z * s;\n\n    return ai;\n}\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\ncomputeBodyAccel(typename vec4<T>::Type bodyPos, typename vec4<T>::Type *positions, int numTiles, cg::thread_block cta)\n{\n    typename vec4<T>::Type *sharedPos = SharedMemory<typename vec4<T>::Type>();\n\n    typename vec3<T>::Type acc = {0.0f, 0.0f, 0.0f};\n\n    for (int tile = 0; tile < numTiles; tile++) {\n        sharedPos[threadIdx.x] = positions[tile * blockDim.x + threadIdx.x];\n\n        cg::sync(cta);\n\n// This is the \"tile_calculation\" from the GPUG3 article.\n#pragma unroll 128\n\n        for (unsigned int counter = 0; counter < blockDim.x; counter++) {\n            acc = bodyBodyInteraction<T>(acc, bodyPos, sharedPos[counter]);\n        }\n\n        cg::sync(cta);\n    }\n\n    return acc;\n}\n\ntemplate <typename T>\n__global__ void integrateBodies(typename vec4<T>::Type *__restrict__ newPos,\n                                typename vec4<T>::Type *__restrict__ oldPos,\n                                typename vec4<T>::Type *vel,\n                                unsigned int            deviceOffset,\n                                unsigned int            deviceNumBodies,\n                                float                   deltaTime,\n                                float                   damping,\n                                int                     numTiles)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    int              index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (index >= deviceNumBodies) {\n        return;\n    }\n\n    typename vec4<T>::Type position = oldPos[deviceOffset + index];\n\n    typename vec3<T>::Type accel = computeBodyAccel<T>(position, oldPos, numTiles, cta);\n\n    // acceleration = force / mass;\n    // new velocity = old velocity + acceleration * deltaTime\n    // note we factor out the body's mass from the equation, here and in\n    // bodyBodyInteraction\n    // (because they cancel out).  Thus here force == acceleration\n    typename vec4<T>::Type velocity = vel[deviceOffset + index];\n\n    velocity.x += accel.x * deltaTime;\n    velocity.y += accel.y * deltaTime;\n    velocity.z += accel.z * deltaTime;\n\n    velocity.x *= damping;\n    velocity.y *= damping;\n    velocity.z *= damping;\n\n    // new position = old position + velocity * deltaTime\n    position.x += velocity.x * deltaTime;\n    position.y += velocity.y * deltaTime;\n    position.z += velocity.z * deltaTime;\n\n    // store new position and velocity\n    newPos[deviceOffset + index] = position;\n    vel[deviceOffset + index]    = velocity;\n}\n\ntemplate <typename T>\nvoid integrateNbodySystem(DeviceData<T>         *deviceData,\n                          hipGraphicsResource **pgres,\n                          unsigned int           currentRead,\n                          float                  deltaTime,\n                          float                  damping,\n                          unsigned int           numBodies,\n                          unsigned int           numDevices,\n                          int                    blockSize,\n                          bool                   bUsePBO)\n{\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[currentRead], cudaGraphicsMapFlagsReadOnly));\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[1 - currentRead], cudaGraphicsMapFlagsWriteDiscard));\n        checkCudaErrors(hipGraphicsMapResources(2, pgres, 0));\n        size_t bytes;\n        checkCudaErrors(hipGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[currentRead]), &bytes, pgres[currentRead]));\n        checkCudaErrors(hipGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[1 - currentRead]), &bytes, pgres[1 - currentRead]));\n    }\n\n    for (unsigned int dev = 0; dev != numDevices; dev++) {\n        if (numDevices > 1) {\n            hipSetDevice(dev);\n        }\n\n        int numBlocks     = (deviceData[dev].numBodies + blockSize - 1) / blockSize;\n        int numTiles      = (numBodies + blockSize - 1) / blockSize;\n        int sharedMemSize = blockSize * 4 * sizeof(T); // 4 floats for pos\n\n        integrateBodies<T>\n            <<<numBlocks, blockSize, sharedMemSize>>>((typename vec4<T>::Type *)deviceData[dev].dPos[1 - currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dPos[currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dVel,\n                                                      deviceData[dev].offset,\n                                                      deviceData[dev].numBodies,\n                                                      deltaTime,\n                                                      damping,\n                                                      numTiles);\n\n        if (numDevices > 1) {\n            checkCudaErrors(hipEventRecord(deviceData[dev].event));\n            // MJH: Hack on older driver versions to force kernel launches to flush!\n            hipStreamQuery(0);\n        }\n\n        // check if kernel invocation generated an error\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    if (numDevices > 1) {\n        for (unsigned int dev = 0; dev < numDevices; dev++) {\n            checkCudaErrors(hipEventSynchronize(deviceData[dev].event));\n        }\n    }\n\n    if (bUsePBO) {\n        checkCudaErrors(hipGraphicsUnmapResources(2, pgres, 0));\n    }\n}\n\n// Explicit specializations needed to generate code\ntemplate void integrateNbodySystem<float>(DeviceData<float>     *deviceData,\n                                          hipGraphicsResource **pgres,\n                                          unsigned int           currentRead,\n                                          float                  deltaTime,\n                                          float                  damping,\n                                          unsigned int           numBodies,\n                                          unsigned int           numDevices,\n                                          int                    blockSize,\n                                          bool                   bUsePBO);\n\ntemplate void integrateNbodySystem<double>(DeviceData<double>    *deviceData,\n                                           hipGraphicsResource **pgres,\n                                           unsigned int           currentRead,\n                                           float                  deltaTime,\n                                           float                  damping,\n                                           unsigned int           numBodies,\n                                           unsigned int           numDevices,\n                                           int                    blockSize,\n                                           bool                   bUsePBO);\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/nbody/bodysystemcuda.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda_runtime.h>\n#include <cufft.h>       // CUDA FFT Libraries\n#include <helper_cuda.h> // Helper functions for CUDA Error handling\n#include <stdio.h>\n#include <stdlib.h>\n\n// OpenGL Graphics includes\n#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION\n#include <helper_gl.h>\n\n// FluidsGL CUDA kernel definitions\n#include \"fluidsGL_kernels.cuh\"\n\n// Texture object for reading velocity field\ncudaTextureObject_t texObj;\nstatic cudaArray   *array = NULL;\n\n// Particle data\nextern GLuint                       vbo;               // OpenGL vertex buffer object\nextern struct cudaGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange\n\n// Texture pitch\nextern size_t      tPitch;\nextern cufftHandle planr2c;\nextern cufftHandle planc2r;\ncData             *vxfield = NULL;\ncData             *vyfield = NULL;\n\nvoid setupTexture(int x, int y)\n{\n    cudaChannelFormatDesc desc = cudaCreateChannelDesc<float2>();\n\n    cudaMallocArray(&array, &desc, y, x);\n    getLastCudaError(\"cudaMalloc failed\");\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&texObj, &texRes, &texDescr, NULL));\n}\n\nvoid updateTexture(cData *data, size_t wib, size_t h, size_t pitch)\n{\n    checkCudaErrors(cudaMemcpy2DToArray(array, 0, 0, data, pitch, wib, h, cudaMemcpyDeviceToDevice));\n}\n\nvoid deleteTexture(void)\n{\n    checkCudaErrors(cudaDestroyTextureObject(texObj));\n    checkCudaErrors(cudaFreeArray(array));\n}\n\n// Note that these kernels are designed to work with arbitrary\n// domain sizes, not just domains that are multiples of the tile\n// size. Therefore, we have extra code that checks to make sure\n// a given thread location falls within the domain boundaries in\n// both X and Y. Also, the domain is covered by looping over\n// multiple elements in the Y direction, while there is a one-to-one\n// mapping between threads in X and the tile size in X.\n// Nolan Goodnight 9/22/06\n\n// This method adds constant force vectors to the velocity field\n// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.\n__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch)\n{\n    int    tx = threadIdx.x;\n    int    ty = threadIdx.y;\n    cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;\n\n    cData vterm = *fj;\n    tx -= r;\n    ty -= r;\n    float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);\n    vterm.x += s * fx;\n    vterm.y += s * fy;\n    *fj = vterm;\n}\n\n// This method performs the velocity advection step, where we\n// trace velocity vectors back in time to update each grid cell.\n// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear\n// interpolation in the velocity space.\n__global__ void advectVelocity_k(cData              *v,\n                                 float              *vx,\n                                 float              *vy,\n                                 int                 dx,\n                                 int                 pdx,\n                                 int                 dy,\n                                 float               dt,\n                                 int                 lb,\n                                 cudaTextureObject_t texObject)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData vterm, ploc;\n    float vxterm, vyterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * pdx + gtidx;\n                vterm  = tex2D<cData>(texObject, (float)gtidx, (float)fi);\n                ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);\n                ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);\n                vterm  = tex2D<cData>(texObject, ploc.x, ploc.y);\n                vxterm = vterm.x;\n                vyterm = vterm.y;\n                vx[fj] = vxterm;\n                vy[fj] = vyterm;\n            }\n        }\n    }\n}\n\n// This method performs velocity diffusion and forces mass conservation\n// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued\n// arrays holding the Fourier coefficients of the velocity field in\n// X and Y. Diffusion in this space takes a simple form described as:\n// v(k,t) = v(k,t) / (1 + visc * dt * k^2), where visc is the viscosity,\n// and k is the wavenumber. The projection step forces the Fourier\n// velocity vectors to be orthogonal to the vectors for each\n// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.\n__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData xterm, yterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                xterm  = vx[fj];\n                yterm  = vy[fj];\n\n                // Compute the index of the wavenumber based on the\n                // data order produced by a standard NN FFT.\n                int iix = gtidx;\n                int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;\n\n                // Velocity diffusion\n                float kk   = (float)(iix * iix + iiy * iiy); // k^2\n                float diff = 1.f / (1.f + visc * dt * kk);\n                xterm.x *= diff;\n                xterm.y *= diff;\n                yterm.x *= diff;\n                yterm.y *= diff;\n\n                // Velocity projection\n                if (kk > 0.f) {\n                    float rkk = 1.f / kk;\n                    // Real portion of velocity projection\n                    float rkp = (iix * xterm.x + iiy * yterm.x);\n                    // Imaginary portion of velocity projection\n                    float ikp = (iix * xterm.y + iiy * yterm.y);\n                    xterm.x -= rkk * rkp * iix;\n                    xterm.y -= rkk * ikp * iix;\n                    yterm.x -= rkk * rkp * iiy;\n                    yterm.y -= rkk * ikp * iiy;\n                }\n\n                vx[fj] = xterm;\n                vy[fj] = yterm;\n            }\n        }\n    }\n}\n\n// This method updates the velocity field 'v' using the two complex\n// arrays from the previous step: 'vx' and 'vy'. Here we scale the\n// real components by 1/(dx*dy) to account for an unnormalized FFT.\n__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    float vxterm, vyterm;\n    cData nvterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fjr = fi * pdx + gtidx;\n                vxterm  = vx[fjr];\n                vyterm  = vy[fjr];\n\n                // Normalize the result of the inverse FFT\n                float scale = 1.f / (dx * dy);\n                nvterm.x    = vxterm * scale;\n                nvterm.y    = vyterm * scale;\n\n                cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;\n                *fj       = nvterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// This method updates the particles by moving particle positions\n// according to the velocity field and time step. That is, for each\n// particle: p(t+1) = p(t) + dt * v(p(t)).\n__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    // gtidx is the domain location in x for this thread\n    cData pterm, vterm;\n\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                pterm  = part[fj];\n\n                int xvi = ((int)(pterm.x * dx));\n                int yvi = ((int)(pterm.y * dy));\n                vterm   = *((cData *)((char *)v + yvi * pitch) + xvi);\n\n                pterm.x += dt * vterm.x;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.x += 1.f;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.y += dt * vterm.y;\n                pterm.y = pterm.y - (int)pterm.y;\n                pterm.y += 1.f;\n                pterm.y = pterm.y - (int)pterm.y;\n\n                part[fj] = pterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// These are the external function calls necessary for launching fluid\n// simulation\nextern \"C\" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r)\n{\n    dim3 tids(2 * r + 1, 2 * r + 1);\n\n    addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);\n    getLastCudaError(\"addForces_k failed.\");\n}\n\nextern \"C\" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n\n    dim3 tids(TIDSX, TIDSY);\n\n    updateTexture(v, DIM * sizeof(cData), DIM, tPitch);\n    advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY, texObj);\n\n    getLastCudaError(\"advectVelocity_k failed.\");\n}\n\nextern \"C\" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc)\n{\n    // Forward FFT\n    checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vx, (cufftComplex *)vx));\n    checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vy, (cufftComplex *)vy));\n\n    uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);\n    uint3 tids = make_uint3(TIDSX, TIDSY, 1);\n\n    diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);\n    getLastCudaError(\"diffuseProject_k failed.\");\n\n    // Inverse FFT\n    checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vx, (cufftReal *)vx));\n    checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vy, (cufftReal *)vy));\n}\n\nextern \"C\" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"updateVelocity_k failed.\");\n}\n\nextern \"C\" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    cData *p;\n    cudaGraphicsMapResources(1, &cuda_vbo_resource, 0);\n    getLastCudaError(\"cudaGraphicsMapResources failed\");\n\n    size_t num_bytes;\n    cudaGraphicsResourceGetMappedPointer((void **)&p, &num_bytes, cuda_vbo_resource);\n    getLastCudaError(\"cudaGraphicsResourceGetMappedPointer failed\");\n\n    advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"advectParticles_k failed.\");\n\n    cudaGraphicsUnmapResources(1, &cuda_vbo_resource, 0);\n    getLastCudaError(\"cudaGraphicsUnmapResources failed\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime.h>\n#include <hipfft/hipfft.h>       // CUDA FFT Libraries\n#include <hip/hip_runtime_api.h> // Helper functions for CUDA Error handling\n#include <stdio.h>\n#include <stdlib.h>\n\n// OpenGL Graphics includes\n#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION\n#include <helper_gl.h>\n\n// FluidsGL CUDA kernel definitions\n#include \"fluidsGL_kernels.cuh\"\n\n// Texture object for reading velocity field\nhipTextureObject_t texObj;\nstatic hipArray   *array = NULL;\n\n// Particle data\nextern GLuint                       vbo;               // OpenGL vertex buffer object\nextern struct hipGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange\n\n// Texture pitch\nextern size_t      tPitch;\nextern hipfftHandle planr2c;\nextern hipfftHandle planc2r;\ncData             *vxfield = NULL;\ncData             *vyfield = NULL;\n\nvoid setupTexture(int x, int y)\n{\n    hipChannelFormatDesc desc = hipCreateChannelDesc<float2>();\n\n    hipMallocArray(&array, &desc, y, x);\n    getLastCudaError(\"hipMalloc failed\");\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&texObj, &texRes, &texDescr, NULL));\n}\n\nvoid updateTexture(cData *data, size_t wib, size_t h, size_t pitch)\n{\n    checkCudaErrors(hipMemcpy2DToArray(array, 0, 0, data, pitch, wib, h, hipMemcpyDeviceToDevice));\n}\n\nvoid deleteTexture(void)\n{\n    checkCudaErrors(hipDestroyTextureObject(texObj));\n    checkCudaErrors(hipFreeArray(array));\n}\n\n// Note that these kernels are designed to work with arbitrary\n// domain sizes, not just domains that are multiples of the tile\n// size. Therefore, we have extra code that checks to make sure\n// a given thread location falls within the domain boundaries in\n// both X and Y. Also, the domain is covered by looping over\n// multiple elements in the Y direction, while there is a one-to-one\n// mapping between threads in X and the tile size in X.\n// Nolan Goodnight 9/22/06\n\n// This method adds constant force vectors to the velocity field\n// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.\n__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch)\n{\n    int    tx = threadIdx.x;\n    int    ty = threadIdx.y;\n    cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;\n\n    cData vterm = *fj;\n    tx -= r;\n    ty -= r;\n    float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);\n    vterm.x += s * fx;\n    vterm.y += s * fy;\n    *fj = vterm;\n}\n\n// This method performs the velocity advection step, where we\n// trace velocity vectors back in time to update each grid cell.\n// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear\n// interpolation in the velocity space.\n__global__ void advectVelocity_k(cData              *v,\n                                 float              *vx,\n                                 float              *vy,\n                                 int                 dx,\n                                 int                 pdx,\n                                 int                 dy,\n                                 float               dt,\n                                 int                 lb,\n                                 hipTextureObject_t texObject)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData vterm, ploc;\n    float vxterm, vyterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * pdx + gtidx;\n                vterm  = tex2D<cData>(texObject, (float)gtidx, (float)fi);\n                ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);\n                ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);\n                vterm  = tex2D<cData>(texObject, ploc.x, ploc.y);\n                vxterm = vterm.x;\n                vyterm = vterm.y;\n                vx[fj] = vxterm;\n                vy[fj] = vyterm;\n            }\n        }\n    }\n}\n\n// This method performs velocity diffusion and forces mass conservation\n// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued\n// arrays holding the Fourier coefficients of the velocity field in\n// X and Y. Diffusion in this space takes a simple form described as:\n// v(k,t) = v(k,t) / (1 + visc * dt * k^2), where visc is the viscosity,\n// and k is the wavenumber. The projection step forces the Fourier\n// velocity vectors to be orthogonal to the vectors for each\n// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.\n__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData xterm, yterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                xterm  = vx[fj];\n                yterm  = vy[fj];\n\n                // Compute the index of the wavenumber based on the\n                // data order produced by a standard NN FFT.\n                int iix = gtidx;\n                int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;\n\n                // Velocity diffusion\n                float kk   = (float)(iix * iix + iiy * iiy); // k^2\n                float diff = 1.f / (1.f + visc * dt * kk);\n                xterm.x *= diff;\n                xterm.y *= diff;\n                yterm.x *= diff;\n                yterm.y *= diff;\n\n                // Velocity projection\n                if (kk > 0.f) {\n                    float rkk = 1.f / kk;\n                    // Real portion of velocity projection\n                    float rkp = (iix * xterm.x + iiy * yterm.x);\n                    // Imaginary portion of velocity projection\n                    float ikp = (iix * xterm.y + iiy * yterm.y);\n                    xterm.x -= rkk * rkp * iix;\n                    xterm.y -= rkk * ikp * iix;\n                    yterm.x -= rkk * rkp * iiy;\n                    yterm.y -= rkk * ikp * iiy;\n                }\n\n                vx[fj] = xterm;\n                vy[fj] = yterm;\n            }\n        }\n    }\n}\n\n// This method updates the velocity field 'v' using the two complex\n// arrays from the previous step: 'vx' and 'vy'. Here we scale the\n// real components by 1/(dx*dy) to account for an unnormalized FFT.\n__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    float vxterm, vyterm;\n    cData nvterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fjr = fi * pdx + gtidx;\n                vxterm  = vx[fjr];\n                vyterm  = vy[fjr];\n\n                // Normalize the result of the inverse FFT\n                float scale = 1.f / (dx * dy);\n                nvterm.x    = vxterm * scale;\n                nvterm.y    = vyterm * scale;\n\n                cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;\n                *fj       = nvterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// This method updates the particles by moving particle positions\n// according to the velocity field and time step. That is, for each\n// particle: p(t+1) = p(t) + dt * v(p(t)).\n__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    // gtidx is the domain location in x for this thread\n    cData pterm, vterm;\n\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                pterm  = part[fj];\n\n                int xvi = ((int)(pterm.x * dx));\n                int yvi = ((int)(pterm.y * dy));\n                vterm   = *((cData *)((char *)v + yvi * pitch) + xvi);\n\n                pterm.x += dt * vterm.x;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.x += 1.f;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.y += dt * vterm.y;\n                pterm.y = pterm.y - (int)pterm.y;\n                pterm.y += 1.f;\n                pterm.y = pterm.y - (int)pterm.y;\n\n                part[fj] = pterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// These are the external function calls necessary for launching fluid\n// simulation\nextern \"C\" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r)\n{\n    dim3 tids(2 * r + 1, 2 * r + 1);\n\n    addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);\n    getLastCudaError(\"addForces_k failed.\");\n}\n\nextern \"C\" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n\n    dim3 tids(TIDSX, TIDSY);\n\n    updateTexture(v, DIM * sizeof(cData), DIM, tPitch);\n    advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY, texObj);\n\n    getLastCudaError(\"advectVelocity_k failed.\");\n}\n\nextern \"C\" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc)\n{\n    // Forward FFT\n    checkCudaErrors(hipfftExecR2C(planr2c, (hipfftReal *)vx, (hipfftComplex *)vx));\n    checkCudaErrors(hipfftExecR2C(planr2c, (hipfftReal *)vy, (hipfftComplex *)vy));\n\n    uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);\n    uint3 tids = make_uint3(TIDSX, TIDSY, 1);\n\n    diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);\n    getLastCudaError(\"diffuseProject_k failed.\");\n\n    // Inverse FFT\n    checkCudaErrors(hipfftExecC2R(planc2r, (hipfftComplex *)vx, (hipfftReal *)vx));\n    checkCudaErrors(hipfftExecC2R(planc2r, (hipfftComplex *)vy, (hipfftReal *)vy));\n}\n\nextern \"C\" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"updateVelocity_k failed.\");\n}\n\nextern \"C\" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    cData *p;\n    hipGraphicsMapResources(1, &cuda_vbo_resource, 0);\n    getLastCudaError(\"hipGraphicsMapResources failed\");\n\n    size_t num_bytes;\n    hipGraphicsResourceGetMappedPointer((void **)&p, &num_bytes, cuda_vbo_resource);\n    getLastCudaError(\"hipGraphicsResourceGetMappedPointer failed\");\n\n    advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"advectParticles_k failed.\");\n\n    hipGraphicsUnmapResources(1, &cuda_vbo_resource, 0);\n    getLastCudaError(\"hipGraphicsUnmapResources failed\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/fluidsGL/fluidsGL_kernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n\ncudaTextureObject_t inTexObject;\n\n// clamp x to range [a, b]\n__device__ float clamp(float x, float a, float b) { return max(a, min(b, x)); }\n\n__device__ int clamp(int x, int a, int b) { return max(a, min(b, x)); }\n\n// convert floating point rgb color to 8-bit integer\n__device__ int rgbToInt(float r, float g, float b)\n{\n    r = clamp(r, 0.0f, 255.0f);\n    g = clamp(g, 0.0f, 255.0f);\n    b = clamp(b, 0.0f, 255.0f);\n    return (int(b) << 16) | (int(g) << 8) | int(r);\n}\n\n// get pixel from 2D image, with clamping to border\n__device__ uchar4 getPixel(int x, int y, cudaTextureObject_t inTex)\n{\n#ifndef USE_TEXTURE_RGBA8UI\n    float4 res   = tex2D<float4>(inTex, x, y);\n    uchar4 ucres = make_uchar4(res.x * 255.0f, res.y * 255.0f, res.z * 255.0f, res.w * 255.0f);\n#else\n    uchar4 ucres = tex2D<uchar4>(inTex, x, y);\n#endif\n    return ucres;\n}\n\n// macros to make indexing shared memory easier\n#define SMEM(X, Y) sdata[(Y) * tilew + (X)]\n\n/*\n    2D convolution using shared memory\n    - operates on 8-bit RGB data stored in 32-bit int\n    - assumes kernel radius is less than or equal to block size\n    - not optimized for performance\n     _____________\n    |   :     :   |\n    |_ _:_____:_ _|\n    |   |     |   |\n    |   |     |   |\n    |_ _|_____|_ _|\n  r |   :     :   |\n    |___:_____:___|\n      r    bw   r\n    <----tilew---->\n*/\n\n__global__ void cudaProcess(unsigned int       *g_odata,\n                            int                 imgw,\n                            int                 imgh,\n                            int                 tilew,\n                            int                 r,\n                            float               threshold,\n                            float               highlight,\n                            cudaTextureObject_t inTex)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    extern __shared__ uchar4 sdata[];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bw = blockDim.x;\n    int bh = blockDim.y;\n    int x  = blockIdx.x * bw + tx;\n    int y  = blockIdx.y * bh + ty;\n\n#if 0\n    uchar4 c4 = getPixel(x, y);\n    g_odata[y*imgw+x] = rgbToInt(c4.z, c4.y, c4.x);\n#else\n    // copy tile to shared memory\n    // center region\n    SMEM(r + tx, r + ty) = getPixel(x, y, inTex);\n\n    // borders\n    if (threadIdx.x < r) {\n        // left\n        SMEM(tx, r + ty) = getPixel(x - r, y, inTex);\n        // right\n        SMEM(r + bw + tx, r + ty) = getPixel(x + bw, y, inTex);\n    }\n\n    if (threadIdx.y < r) {\n        // top\n        SMEM(r + tx, ty) = getPixel(x, y - r, inTex);\n        // bottom\n        SMEM(r + tx, r + bh + ty) = getPixel(x, y + bh, inTex);\n    }\n\n    // load corners\n    if ((threadIdx.x < r) && (threadIdx.y < r)) {\n        // tl\n        SMEM(tx, ty) = getPixel(x - r, y - r, inTex);\n        // bl\n        SMEM(tx, r + bh + ty) = getPixel(x - r, y + bh, inTex);\n        // tr\n        SMEM(r + bw + tx, ty) = getPixel(x + bh, y - r, inTex);\n        // br\n        SMEM(r + bw + tx, r + bh + ty) = getPixel(x + bw, y + bh, inTex);\n    }\n\n    // wait for loads to complete\n    cg::sync(cta);\n\n    // perform convolution\n    float rsum    = 0.0f;\n    float gsum    = 0.0f;\n    float bsum    = 0.0f;\n    float samples = 0.0f;\n\n    for (int dy = -r; dy <= r; dy++) {\n        for (int dx = -r; dx <= r; dx++) {\n#if 0\n            // try this to see the benefit of using shared memory\n            uchar4 pixel = getPixel(x+dx, y+dy);\n#else\n            uchar4 pixel = SMEM(r + tx + dx, r + ty + dy);\n#endif\n\n            // only sum pixels within disc-shaped kernel\n            float l = dx * dx + dy * dy;\n\n            if (l <= r * r) {\n                float r = float(pixel.x);\n                float g = float(pixel.y);\n                float b = float(pixel.z);\n#if 1\n                // brighten highlights\n                float lum = (r + g + b) / (255 * 3);\n\n                if (lum > threshold) {\n                    r *= highlight;\n                    g *= highlight;\n                    b *= highlight;\n                }\n\n#endif\n                rsum += r;\n                gsum += g;\n                bsum += b;\n                samples += 1.0f;\n            }\n        }\n    }\n\n    rsum /= samples;\n    gsum /= samples;\n    bsum /= samples;\n    // ABGR\n    g_odata[y * imgw + x] = rgbToInt(rsum, gsum, bsum);\n// g_odata[y*imgw+x] = rgbToInt(x,y,0);\n#endif\n}\n\nextern \"C\" void launch_cudaProcess(dim3          grid,\n                                   dim3          block,\n                                   int           sbytes,\n                                   cudaArray    *g_data_array,\n                                   unsigned int *g_odata,\n                                   int           imgw,\n                                   int           imgh,\n                                   int           tilew,\n                                   int           radius,\n                                   float         threshold,\n                                   float         highlight)\n{\n    struct cudaChannelFormatDesc desc;\n    checkCudaErrors(cudaGetChannelDesc(&desc, g_data_array));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = g_data_array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&inTexObject, &texRes, &texDescr, NULL));\n\n#if 0\n    printf(\"CUDA Array channel descriptor, bits per component:\\n\");\n    printf(\"X %d Y %d Z %d W %d, kind %d\\n\",\n           desc.x,desc.y,desc.z,desc.w,desc.f);\n\n    printf(\"Possible values for channel format kind: i %d, u%d, f%d:\\n\",\n           cudaChannelFormatKindSigned, cudaChannelFormatKindUnsigned,\n           cudaChannelFormatKindFloat);\n#endif\n\n// printf(\"\\n\");\n#ifdef GPU_PROFILING\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n\n    int nIter = 30;\n\n    for (int i = -1; i < nIter; ++i) {\n        if (i == 0) {\n            sdkStartTimer(&timer);\n        }\n\n#endif\n\n        cudaProcess<<<grid, block, sbytes>>>(\n            g_odata, imgw, imgh, block.x + (2 * radius), radius, 0.8f, 4.0f, inTexObject);\n\n#ifdef GPU_PROFILING\n    }\n\n    cudaDeviceSynchronize();\n    sdkStopTimer(&timer);\n    double dSeconds   = sdkGetTimerValue(&timer) / ((double)nIter * 1000.0);\n    double dNumTexels = (double)imgw * (double)imgh;\n    double mtexps     = 1.0e-6 * dNumTexels / dSeconds;\n\n    if (radius == 4) {\n        printf(\"\\n\");\n        printf(\"postprocessGL, Throughput = %.4f MTexels/s, Time = %.5f s, Size = \"\n               \"%.0f Texels, NumDevsUsed = %d, Workgroup = %u\\n\",\n               mtexps,\n               dSeconds,\n               dNumTexels,\n               1,\n               block.x * block.y);\n    }\n\n#endif\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Utilities and system includes\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n\nhipTextureObject_t inTexObject;\n\n// clamp x to range [a, b]\n__device__ float clamp(float x, float a, float b) { return max(a, min(b, x)); }\n\n__device__ int clamp(int x, int a, int b) { return max(a, min(b, x)); }\n\n// convert floating point rgb color to 8-bit integer\n__device__ int rgbToInt(float r, float g, float b)\n{\n    r = clamp(r, 0.0f, 255.0f);\n    g = clamp(g, 0.0f, 255.0f);\n    b = clamp(b, 0.0f, 255.0f);\n    return (int(b) << 16) | (int(g) << 8) | int(r);\n}\n\n// get pixel from 2D image, with clamping to border\n__device__ uchar4 getPixel(int x, int y, hipTextureObject_t inTex)\n{\n#ifndef USE_TEXTURE_RGBA8UI\n    float4 res   = tex2D<float4>(inTex, x, y);\n    uchar4 ucres = make_uchar4(res.x * 255.0f, res.y * 255.0f, res.z * 255.0f, res.w * 255.0f);\n#else\n    uchar4 ucres = tex2D<uchar4>(inTex, x, y);\n#endif\n    return ucres;\n}\n\n// macros to make indexing shared memory easier\n#define SMEM(X, Y) sdata[(Y) * tilew + (X)]\n\n/*\n    2D convolution using shared memory\n    - operates on 8-bit RGB data stored in 32-bit int\n    - assumes kernel radius is less than or equal to block size\n    - not optimized for performance\n     _____________\n    |   :     :   |\n    |_ _:_____:_ _|\n    |   |     |   |\n    |   |     |   |\n    |_ _|_____|_ _|\n  r |   :     :   |\n    |___:_____:___|\n      r    bw   r\n    <----tilew---->\n*/\n\n__global__ void cudaProcess(unsigned int       *g_odata,\n                            int                 imgw,\n                            int                 imgh,\n                            int                 tilew,\n                            int                 r,\n                            float               threshold,\n                            float               highlight,\n                            hipTextureObject_t inTex)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    extern __shared__ uchar4 sdata[];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bw = blockDim.x;\n    int bh = blockDim.y;\n    int x  = blockIdx.x * bw + tx;\n    int y  = blockIdx.y * bh + ty;\n\n#if 0\n    uchar4 c4 = getPixel(x, y);\n    g_odata[y*imgw+x] = rgbToInt(c4.z, c4.y, c4.x);\n#else\n    // copy tile to shared memory\n    // center region\n    SMEM(r + tx, r + ty) = getPixel(x, y, inTex);\n\n    // borders\n    if (threadIdx.x < r) {\n        // left\n        SMEM(tx, r + ty) = getPixel(x - r, y, inTex);\n        // right\n        SMEM(r + bw + tx, r + ty) = getPixel(x + bw, y, inTex);\n    }\n\n    if (threadIdx.y < r) {\n        // top\n        SMEM(r + tx, ty) = getPixel(x, y - r, inTex);\n        // bottom\n        SMEM(r + tx, r + bh + ty) = getPixel(x, y + bh, inTex);\n    }\n\n    // load corners\n    if ((threadIdx.x < r) && (threadIdx.y < r)) {\n        // tl\n        SMEM(tx, ty) = getPixel(x - r, y - r, inTex);\n        // bl\n        SMEM(tx, r + bh + ty) = getPixel(x - r, y + bh, inTex);\n        // tr\n        SMEM(r + bw + tx, ty) = getPixel(x + bh, y - r, inTex);\n        // br\n        SMEM(r + bw + tx, r + bh + ty) = getPixel(x + bw, y + bh, inTex);\n    }\n\n    // wait for loads to complete\n    cg::sync(cta);\n\n    // perform convolution\n    float rsum    = 0.0f;\n    float gsum    = 0.0f;\n    float bsum    = 0.0f;\n    float samples = 0.0f;\n\n    for (int dy = -r; dy <= r; dy++) {\n        for (int dx = -r; dx <= r; dx++) {\n#if 0\n            // try this to see the benefit of using shared memory\n            uchar4 pixel = getPixel(x+dx, y+dy);\n#else\n            uchar4 pixel = SMEM(r + tx + dx, r + ty + dy);\n#endif\n\n            // only sum pixels within disc-shaped kernel\n            float l = dx * dx + dy * dy;\n\n            if (l <= r * r) {\n                float r = float(pixel.x);\n                float g = float(pixel.y);\n                float b = float(pixel.z);\n#if 1\n                // brighten highlights\n                float lum = (r + g + b) / (255 * 3);\n\n                if (lum > threshold) {\n                    r *= highlight;\n                    g *= highlight;\n                    b *= highlight;\n                }\n\n#endif\n                rsum += r;\n                gsum += g;\n                bsum += b;\n                samples += 1.0f;\n            }\n        }\n    }\n\n    rsum /= samples;\n    gsum /= samples;\n    bsum /= samples;\n    // ABGR\n    g_odata[y * imgw + x] = rgbToInt(rsum, gsum, bsum);\n// g_odata[y*imgw+x] = rgbToInt(x,y,0);\n#endif\n}\n\nextern \"C\" void launch_cudaProcess(dim3          grid,\n                                   dim3          block,\n                                   int           sbytes,\n                                   hipArray    *g_data_array,\n                                   unsigned int *g_odata,\n                                   int           imgw,\n                                   int           imgh,\n                                   int           tilew,\n                                   int           radius,\n                                   float         threshold,\n                                   float         highlight)\n{\n    struct hipChannelFormatDesc desc;\n    checkCudaErrors(hipGetChannelDesc(&desc, g_data_array));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = g_data_array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&inTexObject, &texRes, &texDescr, NULL));\n\n#if 0\n    printf(\"CUDA Array channel descriptor, bits per component:\\n\");\n    printf(\"X %d Y %d Z %d W %d, kind %d\\n\",\n           desc.x,desc.y,desc.z,desc.w,desc.f);\n\n    printf(\"Possible values for channel format kind: i %d, u%d, f%d:\\n\",\n           hipChannelFormatKindSigned, hipChannelFormatKindUnsigned,\n           hipChannelFormatKindFloat);\n#endif\n\n// printf(\"\\n\");\n#ifdef GPU_PROFILING\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n\n    int nIter = 30;\n\n    for (int i = -1; i < nIter; ++i) {\n        if (i == 0) {\n            sdkStartTimer(&timer);\n        }\n\n#endif\n\n        cudaProcess<<<grid, block, sbytes>>>(\n            g_odata, imgw, imgh, block.x + (2 * radius), radius, 0.8f, 4.0f, inTexObject);\n\n#ifdef GPU_PROFILING\n    }\n\n    hipDeviceSynchronize();\n    sdkStopTimer(&timer);\n    double dSeconds   = sdkGetTimerValue(&timer) / ((double)nIter * 1000.0);\n    double dNumTexels = (double)imgw * (double)imgh;\n    double mtexps     = 1.0e-6 * dNumTexels / dSeconds;\n\n    if (radius == 4) {\n        printf(\"\\n\");\n        printf(\"postprocessGL, Throughput = %.4f MTexels/s, Time = %.5f s, Size = \"\n               \"%.0f Texels, NumDevsUsed = %d, Workgroup = %u\\n\",\n               mtexps,\n               dSeconds,\n               dNumTexels,\n               1,\n               block.x * block.y);\n    }\n\n#endif\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/postProcessGL/postProcessGL.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <algorithm>\n#include <helper_cuda.h>\n\n#include \"SineWaveSimulation.h\"\n\n__global__ void sinewave(float *heightMap, unsigned int width, unsigned int height, float time)\n{\n    const float  freq   = 4.0f;\n    const size_t stride = gridDim.x * blockDim.x;\n\n    // Iterate through the entire array in a way that is\n    // independent of the grid configuration\n    for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < width * height; tid += stride) {\n        // Calculate the x, y coordinates\n        const size_t y = tid / width;\n        const size_t x = tid - y * width;\n        // Normalize x, y to [0,1]\n        const float u = ((2.0f * x) / width) - 1.0f;\n        const float v = ((2.0f * y) / height) - 1.0f;\n        // Calculate the new height value\n        const float w = 0.5f * sinf(u * freq + time) * cosf(v * freq + time);\n        // Store this new height value\n        heightMap[tid] = w;\n    }\n}\n\nSineWaveSimulation::SineWaveSimulation(size_t width, size_t height)\n    : m_heightMap(nullptr)\n    , m_width(width)\n    , m_height(height)\n{\n}\n\nvoid SineWaveSimulation::initCudaLaunchConfig(int device)\n{\n    cudaDeviceProp prop = {};\n    checkCudaErrors(cudaSetDevice(device));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, device));\n\n    // We don't need large block sizes, since there's not much inter-thread\n    // communication\n    m_threads = prop.warpSize;\n\n    // Use the occupancy calculator and fill the gpu as best as we can\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&m_blocks, sinewave, prop.warpSize, 0));\n    m_blocks *= prop.multiProcessorCount;\n\n    // Go ahead and the clamp the blocks to the minimum needed for this\n    // height/width\n    m_blocks = std::min(m_blocks, (int)((m_width * m_height + m_threads - 1) / m_threads));\n}\n\nint SineWaveSimulation::initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE)\n{\n    int current_device     = 0;\n    int device_count       = 0;\n    int devices_prohibited = 0;\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceCount(&device_count));\n\n    if (device_count == 0) {\n        fprintf(stderr, \"CUDA error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Find the GPU which is selected by Vulkan\n    while (current_device < device_count) {\n        cudaGetDeviceProperties(&deviceProp, current_device);\n        int computeMode;\n        checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, current_device));\n\n        if ((computeMode != cudaComputeModeProhibited)) {\n            // Compare the cuda device UUID with vulkan UUID\n            int ret = memcmp((void *)&deviceProp.uuid, vkDeviceUUID, UUID_SIZE);\n            if (ret == 0) {\n                checkCudaErrors(cudaSetDevice(current_device));\n                checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));\n                printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\\n\",\n                       current_device,\n                       deviceProp.name,\n                       deviceProp.major,\n                       deviceProp.minor);\n\n                return current_device;\n            }\n        }\n        else {\n            devices_prohibited++;\n        }\n\n        current_device++;\n    }\n\n    if (devices_prohibited == device_count) {\n        fprintf(stderr,\n                \"CUDA error:\"\n                \" No Vulkan-CUDA Interop capable GPU found.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    return -1;\n}\n\nSineWaveSimulation::~SineWaveSimulation() { m_heightMap = NULL; }\n\nvoid SineWaveSimulation::initSimulation(float *heights) { m_heightMap = heights; }\n\nvoid SineWaveSimulation::stepSimulation(float time, cudaStream_t stream)\n{\n    sinewave<<<m_blocks, m_threads, 0, stream>>>(m_heightMap, m_width, m_height, time);\n    getLastCudaError(\"Failed to launch CUDA simulation\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n#include <hip/hip_runtime_api.h>\n\n#include \"SineWaveSimulation.h\"\n\n__global__ void sinewave(float *heightMap, unsigned int width, unsigned int height, float time)\n{\n    const float  freq   = 4.0f;\n    const size_t stride = gridDim.x * blockDim.x;\n\n    // Iterate through the entire array in a way that is\n    // independent of the grid configuration\n    for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < width * height; tid += stride) {\n        // Calculate the x, y coordinates\n        const size_t y = tid / width;\n        const size_t x = tid - y * width;\n        // Normalize x, y to [0,1]\n        const float u = ((2.0f * x) / width) - 1.0f;\n        const float v = ((2.0f * y) / height) - 1.0f;\n        // Calculate the new height value\n        const float w = 0.5f * sinf(u * freq + time) * cosf(v * freq + time);\n        // Store this new height value\n        heightMap[tid] = w;\n    }\n}\n\nSineWaveSimulation::SineWaveSimulation(size_t width, size_t height)\n    : m_heightMap(nullptr)\n    , m_width(width)\n    , m_height(height)\n{\n}\n\nvoid SineWaveSimulation::initCudaLaunchConfig(int device)\n{\n    hipDeviceProp_t prop = {};\n    checkCudaErrors(hipSetDevice(device));\n    checkCudaErrors(hipGetDeviceProperties(&prop, device));\n\n    // We don't need large block sizes, since there's not much inter-thread\n    // communication\n    m_threads = prop.warpSize;\n\n    // Use the occupancy calculator and fill the gpu as best as we can\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(&m_blocks, sinewave, prop.warpSize, 0));\n    m_blocks *= prop.multiProcessorCount;\n\n    // Go ahead and the clamp the blocks to the minimum needed for this\n    // height/width\n    m_blocks = std::min(m_blocks, (int)((m_width * m_height + m_threads - 1) / m_threads));\n}\n\nint SineWaveSimulation::initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE)\n{\n    int current_device     = 0;\n    int device_count       = 0;\n    int devices_prohibited = 0;\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceCount(&device_count));\n\n    if (device_count == 0) {\n        fprintf(stderr, \"CUDA error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Find the GPU which is selected by Vulkan\n    while (current_device < device_count) {\n        hipGetDeviceProperties(&deviceProp, current_device);\n        int computeMode;\n        checkCudaErrors(hipDeviceGetAttribute(&computeMode, hipDeviceAttributeComputeMode, current_device));\n\n        if ((computeMode != hipComputeModeProhibited)) {\n            // Compare the cuda device UUID with vulkan UUID\n            int ret = memcmp((void *)&deviceProp.uuid, vkDeviceUUID, UUID_SIZE);\n            if (ret == 0) {\n                checkCudaErrors(hipSetDevice(current_device));\n                checkCudaErrors(hipGetDeviceProperties(&deviceProp, current_device));\n                printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\\n\",\n                       current_device,\n                       deviceProp.name,\n                       deviceProp.major,\n                       deviceProp.minor);\n\n                return current_device;\n            }\n        }\n        else {\n            devices_prohibited++;\n        }\n\n        current_device++;\n    }\n\n    if (devices_prohibited == device_count) {\n        fprintf(stderr,\n                \"CUDA error:\"\n                \" No Vulkan-CUDA Interop capable GPU found.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    return -1;\n}\n\nSineWaveSimulation::~SineWaveSimulation() { m_heightMap = NULL; }\n\nvoid SineWaveSimulation::initSimulation(float *heights) { m_heightMap = heights; }\n\nvoid SineWaveSimulation::stepSimulation(float time, hipStream_t stream)\n{\n    sinewave<<<m_blocks, m_threads, 0, stream>>>(m_heightMap, m_width, m_height, time);\n    getLastCudaError(\"Failed to launch CUDA simulation\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleVulkan/SineWaveSimulation.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"ShaderStructs.h\"\n\n__global__ void sinewave_gen_kernel(Vertex *vertices, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    if (y < height && x < width) {\n        // write output vertex\n        vertices[y * width + x].position.x = u;\n        vertices[y * width + x].position.y = w;\n        vertices[y * width + x].position.z = v;\n        // vertices[y*width+x].position[3] = 1.0f;\n        vertices[y * width + x].color.x = 1.0f;\n        vertices[y * width + x].color.y = 0.0f;\n        vertices[y * width + x].color.z = 0.0f;\n        vertices[y * width + x].color.w = 0.0f;\n    }\n}\n\n// The host CPU Sinewave thread spawner\nvoid RunSineWaveKernel(size_t       mesh_width,\n                       size_t       mesh_height,\n                       Vertex      *cudaDevVertptr,\n                       cudaStream_t streamToRun,\n                       float        AnimTime)\n{\n    dim3    block(16, 16, 1);\n    dim3    grid(mesh_width / 16, mesh_height / 16, 1);\n    Vertex *vertices = (Vertex *)cudaDevVertptr;\n    sinewave_gen_kernel<<<grid, block, 0, streamToRun>>>(vertices, mesh_width, mesh_height, AnimTime);\n\n    getLastCudaError(\"sinewave_gen_kernel execution failed.\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"ShaderStructs.h\"\n\n__global__ void sinewave_gen_kernel(Vertex *vertices, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    if (y < height && x < width) {\n        // write output vertex\n        vertices[y * width + x].position.x = u;\n        vertices[y * width + x].position.y = w;\n        vertices[y * width + x].position.z = v;\n        // vertices[y*width+x].position[3] = 1.0f;\n        vertices[y * width + x].color.x = 1.0f;\n        vertices[y * width + x].color.y = 0.0f;\n        vertices[y * width + x].color.z = 0.0f;\n        vertices[y * width + x].color.w = 0.0f;\n    }\n}\n\n// The host CPU Sinewave thread spawner\nvoid RunSineWaveKernel(size_t       mesh_width,\n                       size_t       mesh_height,\n                       Vertex      *cudaDevVertptr,\n                       hipStream_t streamToRun,\n                       float        AnimTime)\n{\n    dim3    block(16, 16, 1);\n    dim3    grid(mesh_width / 16, mesh_height / 16, 1);\n    Vertex *vertices = (Vertex *)cudaDevVertptr;\n    sinewave_gen_kernel<<<grid, block, 0, streamToRun>>>(vertices, mesh_width, mesh_height, AnimTime);\n\n    getLastCudaError(\"sinewave_gen_kernel execution failed.\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/simpleD3D12/sinewave_cuda.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Portions Copyright (c) 2009 Mike Giles, Oxford University.  All rights\n * reserved.\n * Portions Copyright (c) 2008 Frances Y. Kuo and Stephen Joe.  All rights\n * reserved.\n *\n * Sobol Quasi-random Number Generator example\n *\n * Based on CUDA code submitted by Mike Giles, Oxford University, United Kingdom\n * http://people.maths.ox.ac.uk/~gilesm/\n *\n * and C code developed by Stephen Joe, University of Waikato, New Zealand\n * and Frances Kuo, University of New South Wales, Australia\n * http://web.maths.unsw.edu.au/~fkuo/sobol/\n *\n * For theoretical background see:\n *\n * P. Bratley and B.L. Fox.\n * Implementing Sobol's quasirandom sequence generator\n * http://portal.acm.org/citation.cfm?id=42288\n * ACM Trans. on Math. Software, 14(1):88-100, 1988\n *\n * S. Joe and F. Kuo.\n * Remark on algorithm 659: implementing Sobol's quasirandom sequence generator.\n * http://portal.acm.org/citation.cfm?id=641879\n * ACM Trans. on Math. Software, 29(1):49-57, 2003\n *\n */\n\n#include <cooperative_groups.h>\n\n#include \"sobol.h\"\n#include \"sobol_gpu.h\"\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n#define k_2powneg32 2.3283064E-10F\n\n__global__ void sobolGPU_kernel(unsigned n_vectors, unsigned n_dimensions, unsigned *d_directions, float *d_output)\n{\n    // Handle to thread block group\n    cg::thread_block        cta = cg::this_thread_block();\n    __shared__ unsigned int v[n_directions];\n\n    // Offset into the correct dimension as specified by the\n    // block y coordinate\n    d_directions = d_directions + n_directions * blockIdx.y;\n    d_output     = d_output + n_vectors * blockIdx.y;\n\n    // Copy the direction numbers for this dimension into shared\n    // memory - there are only 32 direction numbers so only the\n    // first 32 (n_directions) threads need participate.\n    if (threadIdx.x < n_directions) {\n        v[threadIdx.x] = d_directions[threadIdx.x];\n    }\n\n    cg::sync(cta);\n\n    // Set initial index (i.e. which vector this thread is\n    // computing first) and stride (i.e. step to the next vector\n    // for this thread)\n    int i0     = threadIdx.x + blockIdx.x * blockDim.x;\n    int stride = gridDim.x * blockDim.x;\n\n    // Get the gray code of the index\n    // c.f. Numerical Recipes in C, chapter 20\n    // http://www.nrbook.com/a/bookcpdf/c20-2.pdf\n    unsigned int g = i0 ^ (i0 >> 1);\n\n    // Initialisation for first point x[i0]\n    // In the Bratley and Fox paper this is equation (*), where\n    // we are computing the value for x[n] without knowing the\n    // value of x[n-1].\n    unsigned int X = 0;\n    unsigned int mask;\n\n    for (unsigned int k = 0; k < __ffs(stride) - 1; k++) {\n        // We want X ^= g_k * v[k], where g_k is one or zero.\n        // We do this by setting a mask with all bits equal to\n        // g_k. In reality we keep shifting g so that g_k is the\n        // LSB of g. This way we avoid multiplication.\n        mask = -(g & 1);\n        X ^= mask & v[k];\n        g = g >> 1;\n    }\n\n    if (i0 < n_vectors) {\n        d_output[i0] = (float)X * k_2powneg32;\n    }\n\n    // Now do rest of points, using the stride\n    // Here we want to generate x[i] from x[i-stride] where we\n    // don't have any of the x in between, therefore we have to\n    // revisit the equation (**), this is easiest with an example\n    // so assume stride is 16.\n    // From x[n] to x[n+16] there will be:\n    //   8 changes in the first bit\n    //   4 changes in the second bit\n    //   2 changes in the third bit\n    //   1 change in the fourth\n    //   1 change in one of the remaining bits\n    //\n    // What this means is that in the equation:\n    //   x[n+1] = x[n] ^ v[p]\n    //   x[n+2] = x[n+1] ^ v[q] = x[n] ^ v[p] ^ v[q]\n    //   ...\n    // We will apply xor with v[1] eight times, v[2] four times,\n    // v[3] twice, v[4] once and one other direction number once.\n    // Since two xors cancel out, we can skip even applications\n    // and just apply xor with v[4] (i.e. log2(16)) and with\n    // the current applicable direction number.\n    // Note that all these indices count from 1, so we need to\n    // subtract 1 from them all to account for C arrays counting\n    // from zero.\n    unsigned int v_log2stridem1 = v[__ffs(stride) - 2];\n    unsigned int v_stridemask   = stride - 1;\n\n    for (unsigned int i = i0 + stride; i < n_vectors; i += stride) {\n        // x[i] = x[i-stride] ^ v[b] ^ v[c]\n        //  where b is log2(stride) minus 1 for C array indexing\n        //  where c is the index of the rightmost zero bit in i,\n        //  not including the bottom log2(stride) bits, minus 1\n        //  for C array indexing\n        // In the Bratley and Fox paper this is equation (**)\n        X ^= v_log2stridem1 ^ v[__ffs(~((i - stride) | v_stridemask)) - 1];\n        d_output[i] = (float)X * k_2powneg32;\n    }\n}\n\nextern \"C\" void sobolGPU(int n_vectors, int n_dimensions, unsigned int *d_directions, float *d_output)\n{\n    const int threadsperblock = 64;\n\n    // Set up the execution configuration\n    dim3 dimGrid;\n    dim3 dimBlock;\n\n    int            device;\n    cudaDeviceProp prop;\n    checkCudaErrors(cudaGetDevice(&device));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, device));\n\n    // This implementation of the generator outputs all the draws for\n    // one dimension in a contiguous region of memory, followed by the\n    // next dimension and so on.\n    // Therefore all threads within a block will be processing different\n    // vectors from the same dimension. As a result we want the total\n    // number of blocks to be a multiple of the number of dimensions.\n    dimGrid.y = n_dimensions;\n\n    // If the number of dimensions is large then we will set the number\n    // of blocks to equal the number of dimensions (i.e. dimGrid.x = 1)\n    // but if the number of dimensions is small (e.g. less than four per\n    // multiprocessor) then we'll partition the vectors across blocks\n    // (as well as threads).\n    if (n_dimensions < (4 * prop.multiProcessorCount)) {\n        dimGrid.x = 4 * prop.multiProcessorCount;\n    }\n    else {\n        dimGrid.x = 1;\n    }\n\n    // Cap the dimGrid.x if the number of vectors is small\n    if (dimGrid.x > (unsigned int)(n_vectors / threadsperblock)) {\n        dimGrid.x = (n_vectors + threadsperblock - 1) / threadsperblock;\n    }\n\n    // Round up to a power of two, required for the algorithm so that\n    // stride is a power of two.\n    unsigned int targetDimGridX = dimGrid.x;\n\n    for (dimGrid.x = 1; dimGrid.x < targetDimGridX; dimGrid.x *= 2)\n        ;\n\n    // Fix the number of threads\n    dimBlock.x = threadsperblock;\n\n    // Execute GPU kernel\n    sobolGPU_kernel<<<dimGrid, dimBlock>>>(n_vectors, n_dimensions, d_directions, d_output);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Portions Copyright (c) 2009 Mike Giles, Oxford University.  All rights\n * reserved.\n * Portions Copyright (c) 2008 Frances Y. Kuo and Stephen Joe.  All rights\n * reserved.\n *\n * Sobol Quasi-random Number Generator example\n *\n * Based on CUDA code submitted by Mike Giles, Oxford University, United Kingdom\n * http://people.maths.ox.ac.uk/~gilesm/\n *\n * and C code developed by Stephen Joe, University of Waikato, New Zealand\n * and Frances Kuo, University of New South Wales, Australia\n * http://web.maths.unsw.edu.au/~fkuo/sobol/\n *\n * For theoretical background see:\n *\n * P. Bratley and B.L. Fox.\n * Implementing Sobol's quasirandom sequence generator\n * http://portal.acm.org/citation.cfm?id=42288\n * ACM Trans. on Math. Software, 14(1):88-100, 1988\n *\n * S. Joe and F. Kuo.\n * Remark on algorithm 659: implementing Sobol's quasirandom sequence generator.\n * http://portal.acm.org/citation.cfm?id=641879\n * ACM Trans. on Math. Software, 29(1):49-57, 2003\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\n#include \"sobol.h\"\n#include \"sobol_gpu.h\"\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n#define k_2powneg32 2.3283064E-10F\n\n__global__ void sobolGPU_kernel(unsigned n_vectors, unsigned n_dimensions, unsigned *d_directions, float *d_output)\n{\n    // Handle to thread block group\n    cg::thread_block        cta = cg::this_thread_block();\n    __shared__ unsigned int v[n_directions];\n\n    // Offset into the correct dimension as specified by the\n    // block y coordinate\n    d_directions = d_directions + n_directions * blockIdx.y;\n    d_output     = d_output + n_vectors * blockIdx.y;\n\n    // Copy the direction numbers for this dimension into shared\n    // memory - there are only 32 direction numbers so only the\n    // first 32 (n_directions) threads need participate.\n    if (threadIdx.x < n_directions) {\n        v[threadIdx.x] = d_directions[threadIdx.x];\n    }\n\n    cg::sync(cta);\n\n    // Set initial index (i.e. which vector this thread is\n    // computing first) and stride (i.e. step to the next vector\n    // for this thread)\n    int i0     = threadIdx.x + blockIdx.x * blockDim.x;\n    int stride = gridDim.x * blockDim.x;\n\n    // Get the gray code of the index\n    // c.f. Numerical Recipes in C, chapter 20\n    // http://www.nrbook.com/a/bookcpdf/c20-2.pdf\n    unsigned int g = i0 ^ (i0 >> 1);\n\n    // Initialisation for first point x[i0]\n    // In the Bratley and Fox paper this is equation (*), where\n    // we are computing the value for x[n] without knowing the\n    // value of x[n-1].\n    unsigned int X = 0;\n    unsigned int mask;\n\n    for (unsigned int k = 0; k < __ffs(stride) - 1; k++) {\n        // We want X ^= g_k * v[k], where g_k is one or zero.\n        // We do this by setting a mask with all bits equal to\n        // g_k. In reality we keep shifting g so that g_k is the\n        // LSB of g. This way we avoid multiplication.\n        mask = -(g & 1);\n        X ^= mask & v[k];\n        g = g >> 1;\n    }\n\n    if (i0 < n_vectors) {\n        d_output[i0] = (float)X * k_2powneg32;\n    }\n\n    // Now do rest of points, using the stride\n    // Here we want to generate x[i] from x[i-stride] where we\n    // don't have any of the x in between, therefore we have to\n    // revisit the equation (**), this is easiest with an example\n    // so assume stride is 16.\n    // From x[n] to x[n+16] there will be:\n    //   8 changes in the first bit\n    //   4 changes in the second bit\n    //   2 changes in the third bit\n    //   1 change in the fourth\n    //   1 change in one of the remaining bits\n    //\n    // What this means is that in the equation:\n    //   x[n+1] = x[n] ^ v[p]\n    //   x[n+2] = x[n+1] ^ v[q] = x[n] ^ v[p] ^ v[q]\n    //   ...\n    // We will apply xor with v[1] eight times, v[2] four times,\n    // v[3] twice, v[4] once and one other direction number once.\n    // Since two xors cancel out, we can skip even applications\n    // and just apply xor with v[4] (i.e. log2(16)) and with\n    // the current applicable direction number.\n    // Note that all these indices count from 1, so we need to\n    // subtract 1 from them all to account for C arrays counting\n    // from zero.\n    unsigned int v_log2stridem1 = v[__ffs(stride) - 2];\n    unsigned int v_stridemask   = stride - 1;\n\n    for (unsigned int i = i0 + stride; i < n_vectors; i += stride) {\n        // x[i] = x[i-stride] ^ v[b] ^ v[c]\n        //  where b is log2(stride) minus 1 for C array indexing\n        //  where c is the index of the rightmost zero bit in i,\n        //  not including the bottom log2(stride) bits, minus 1\n        //  for C array indexing\n        // In the Bratley and Fox paper this is equation (**)\n        X ^= v_log2stridem1 ^ v[__ffs(~((i - stride) | v_stridemask)) - 1];\n        d_output[i] = (float)X * k_2powneg32;\n    }\n}\n\nextern \"C\" void sobolGPU(int n_vectors, int n_dimensions, unsigned int *d_directions, float *d_output)\n{\n    const int threadsperblock = 64;\n\n    // Set up the execution configuration\n    dim3 dimGrid;\n    dim3 dimBlock;\n\n    int            device;\n    hipDeviceProp_t prop;\n    checkCudaErrors(hipGetDevice(&device));\n    checkCudaErrors(hipGetDeviceProperties(&prop, device));\n\n    // This implementation of the generator outputs all the draws for\n    // one dimension in a contiguous region of memory, followed by the\n    // next dimension and so on.\n    // Therefore all threads within a block will be processing different\n    // vectors from the same dimension. As a result we want the total\n    // number of blocks to be a multiple of the number of dimensions.\n    dimGrid.y = n_dimensions;\n\n    // If the number of dimensions is large then we will set the number\n    // of blocks to equal the number of dimensions (i.e. dimGrid.x = 1)\n    // but if the number of dimensions is small (e.g. less than four per\n    // multiprocessor) then we'll partition the vectors across blocks\n    // (as well as threads).\n    if (n_dimensions < (4 * prop.multiProcessorCount)) {\n        dimGrid.x = 4 * prop.multiProcessorCount;\n    }\n    else {\n        dimGrid.x = 1;\n    }\n\n    // Cap the dimGrid.x if the number of vectors is small\n    if (dimGrid.x > (unsigned int)(n_vectors / threadsperblock)) {\n        dimGrid.x = (n_vectors + threadsperblock - 1) / threadsperblock;\n    }\n\n    // Round up to a power of two, required for the algorithm so that\n    // stride is a power of two.\n    unsigned int targetDimGridX = dimGrid.x;\n\n    for (dimGrid.x = 1; dimGrid.x < targetDimGridX; dimGrid.x *= 2)\n        ;\n\n    // Fix the number of threads\n    dimBlock.x = threadsperblock;\n\n    // Execute GPU kernel\n    sobolGPU_kernel<<<dimGrid, dimBlock>>>(n_vectors, n_dimensions, d_directions, d_output);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/SobolQRNG/sobol_gpu.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _MARCHING_CUBES_KERNEL_CU_\n#define _MARCHING_CUBES_KERNEL_CU_\n\n#include <cuda_runtime_api.h>\n#include <helper_cuda.h> // includes for helper CUDA functions\n#include <helper_math.h>\n#include <stdio.h>\n#include <string.h>\n#include <thrust/device_vector.h>\n#include <thrust/scan.h>\n\n#include \"defines.h\"\n#include \"tables.h\"\n\n// textures containing look-up tables\ncudaTextureObject_t triTex;\ncudaTextureObject_t numVertsTex;\n\n// volume data\ncudaTextureObject_t volumeTex;\n\nextern \"C\" void allocateTextures(uint **d_edgeTable, uint **d_triTable, uint **d_numVertsTable)\n{\n    checkCudaErrors(cudaMalloc((void **)d_edgeTable, 256 * sizeof(uint)));\n    checkCudaErrors(cudaMemcpy((void *)*d_edgeTable, (void *)edgeTable, 256 * sizeof(uint), cudaMemcpyHostToDevice));\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindUnsigned);\n\n    checkCudaErrors(cudaMalloc((void **)d_triTable, 256 * 16 * sizeof(uint)));\n    checkCudaErrors(cudaMemcpy((void *)*d_triTable, (void *)triTable, 256 * 16 * sizeof(uint), cudaMemcpyHostToDevice));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                = cudaResourceTypeLinear;\n    texRes.res.linear.devPtr      = *d_triTable;\n    texRes.res.linear.sizeInBytes = 256 * 16 * sizeof(uint);\n    texRes.res.linear.desc        = channelDesc;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&triTex, &texRes, &texDescr, NULL));\n\n    checkCudaErrors(cudaMalloc((void **)d_numVertsTable, 256 * sizeof(uint)));\n    checkCudaErrors(\n        cudaMemcpy((void *)*d_numVertsTable, (void *)numVertsTable, 256 * sizeof(uint), cudaMemcpyHostToDevice));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                = cudaResourceTypeLinear;\n    texRes.res.linear.devPtr      = *d_numVertsTable;\n    texRes.res.linear.sizeInBytes = 256 * sizeof(uint);\n    texRes.res.linear.desc        = channelDesc;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&numVertsTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void createVolumeTexture(uchar *d_volume, size_t buffSize)\n{\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType                = cudaResourceTypeLinear;\n    texRes.res.linear.devPtr      = d_volume;\n    texRes.res.linear.sizeInBytes = buffSize;\n    texRes.res.linear.desc        = cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&volumeTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void destroyAllTextureObjects()\n{\n    checkCudaErrors(cudaDestroyTextureObject(triTex));\n    checkCudaErrors(cudaDestroyTextureObject(numVertsTex));\n    checkCudaErrors(cudaDestroyTextureObject(volumeTex));\n}\n\n// an interesting field function\n__device__ float tangle(float x, float y, float z)\n{\n    x *= 3.0f;\n    y *= 3.0f;\n    z *= 3.0f;\n    return (x * x * x * x - 5.0f * x * x + y * y * y * y - 5.0f * y * y + z * z * z * z - 5.0f * z * z + 11.8f) * 0.2f\n         + 0.5f;\n}\n\n// evaluate field function at point\n__device__ float fieldFunc(float3 p) { return tangle(p.x, p.y, p.z); }\n\n// evaluate field function at a point\n// returns value and gradient in float4\n__device__ float4 fieldFunc4(float3 p)\n{\n    float       v  = tangle(p.x, p.y, p.z);\n    const float d  = 0.001f;\n    float       dx = tangle(p.x + d, p.y, p.z) - v;\n    float       dy = tangle(p.x, p.y + d, p.z) - v;\n    float       dz = tangle(p.x, p.y, p.z + d) - v;\n    return make_float4(dx, dy, dz, v);\n}\n\n// sample volume data set at a point\n__device__ float sampleVolume(cudaTextureObject_t volumeTex, uchar *data, uint3 p, uint3 gridSize)\n{\n    p.x    = min(p.x, gridSize.x - 1);\n    p.y    = min(p.y, gridSize.y - 1);\n    p.z    = min(p.z, gridSize.z - 1);\n    uint i = (p.z * gridSize.x * gridSize.y) + (p.y * gridSize.x) + p.x;\n    //    return (float) data[i] / 255.0f;\n    return tex1Dfetch<float>(volumeTex, i);\n}\n\n// compute position in 3d grid from 1d index\n// only works for power of 2 sizes\n__device__ uint3 calcGridPos(uint i, uint3 gridSizeShift, uint3 gridSizeMask)\n{\n    uint3 gridPos;\n    gridPos.x = i & gridSizeMask.x;\n    gridPos.y = (i >> gridSizeShift.y) & gridSizeMask.y;\n    gridPos.z = (i >> gridSizeShift.z) & gridSizeMask.z;\n    return gridPos;\n}\n\n// classify voxel based on number of vertices it will generate\n// one thread per voxel\n__global__ void classifyVoxel(uint               *voxelVerts,\n                              uint               *voxelOccupied,\n                              uchar              *volume,\n                              uint3               gridSize,\n                              uint3               gridSizeShift,\n                              uint3               gridSizeMask,\n                              uint                numVoxels,\n                              float3              voxelSize,\n                              float               isoValue,\n                              cudaTextureObject_t numVertsTex,\n                              cudaTextureObject_t volumeTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    uint3 gridPos = calcGridPos(i, gridSizeShift, gridSizeMask);\n\n// read field values at neighbouring grid vertices\n#if SAMPLE_VOLUME\n    float field[8];\n    field[0] = sampleVolume(volumeTex, volume, gridPos, gridSize);\n    field[1] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 0), gridSize);\n    field[2] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 0), gridSize);\n    field[3] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 0), gridSize);\n    field[4] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 0, 1), gridSize);\n    field[5] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 1), gridSize);\n    field[6] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 1), gridSize);\n    field[7] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 1), gridSize);\n#else\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    float field[8];\n    field[0] = fieldFunc(p);\n    field[1] = fieldFunc(p + make_float3(voxelSize.x, 0, 0));\n    field[2] = fieldFunc(p + make_float3(voxelSize.x, voxelSize.y, 0));\n    field[3] = fieldFunc(p + make_float3(0, voxelSize.y, 0));\n    field[4] = fieldFunc(p + make_float3(0, 0, voxelSize.z));\n    field[5] = fieldFunc(p + make_float3(voxelSize.x, 0, voxelSize.z));\n    field[6] = fieldFunc(p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z));\n    field[7] = fieldFunc(p + make_float3(0, voxelSize.y, voxelSize.z));\n#endif\n\n    // calculate flag indicating if each vertex is inside or outside isosurface\n    uint cubeindex;\n    cubeindex = uint(field[0] < isoValue);\n    cubeindex += uint(field[1] < isoValue) * 2;\n    cubeindex += uint(field[2] < isoValue) * 4;\n    cubeindex += uint(field[3] < isoValue) * 8;\n    cubeindex += uint(field[4] < isoValue) * 16;\n    cubeindex += uint(field[5] < isoValue) * 32;\n    cubeindex += uint(field[6] < isoValue) * 64;\n    cubeindex += uint(field[7] < isoValue) * 128;\n\n    // read number of vertices from texture\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    if (i < numVoxels) {\n        voxelVerts[i]    = numVerts;\n        voxelOccupied[i] = (numVerts > 0);\n    }\n}\n\nextern \"C\" void launch_classifyVoxel(dim3   grid,\n                                     dim3   threads,\n                                     uint  *voxelVerts,\n                                     uint  *voxelOccupied,\n                                     uchar *volume,\n                                     uint3  gridSize,\n                                     uint3  gridSizeShift,\n                                     uint3  gridSizeMask,\n                                     uint   numVoxels,\n                                     float3 voxelSize,\n                                     float  isoValue)\n{\n    // calculate number of vertices need per voxel\n    classifyVoxel<<<grid, threads>>>(voxelVerts,\n                                     voxelOccupied,\n                                     volume,\n                                     gridSize,\n                                     gridSizeShift,\n                                     gridSizeMask,\n                                     numVoxels,\n                                     voxelSize,\n                                     isoValue,\n                                     numVertsTex,\n                                     volumeTex);\n    getLastCudaError(\"classifyVoxel failed\");\n}\n\n// compact voxel array\n__global__ void compactVoxels(uint *compactedVoxelArray, uint *voxelOccupied, uint *voxelOccupiedScan, uint numVoxels)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (voxelOccupied[i] && (i < numVoxels)) {\n        compactedVoxelArray[voxelOccupiedScan[i]] = i;\n    }\n}\n\nextern \"C\" void launch_compactVoxels(dim3  grid,\n                                     dim3  threads,\n                                     uint *compactedVoxelArray,\n                                     uint *voxelOccupied,\n                                     uint *voxelOccupiedScan,\n                                     uint  numVoxels)\n{\n    compactVoxels<<<grid, threads>>>(compactedVoxelArray, voxelOccupied, voxelOccupiedScan, numVoxels);\n    getLastCudaError(\"compactVoxels failed\");\n}\n\n// compute interpolated vertex along an edge\n__device__ float3 vertexInterp(float isolevel, float3 p0, float3 p1, float f0, float f1)\n{\n    float t = (isolevel - f0) / (f1 - f0);\n    return lerp(p0, p1, t);\n}\n\n// compute interpolated vertex position and normal along an edge\n__device__ void vertexInterp2(float isolevel, float3 p0, float3 p1, float4 f0, float4 f1, float3 &p, float3 &n)\n{\n    float t = (isolevel - f0.w) / (f1.w - f0.w);\n    p       = lerp(p0, p1, t);\n    n.x     = lerp(f0.x, f1.x, t);\n    n.y     = lerp(f0.y, f1.y, t);\n    n.z     = lerp(f0.z, f1.z, t);\n    //    n = normalize(n);\n}\n\n// generate triangles for each voxel using marching cubes\n// interpolates normals from field function\n__global__ void generateTriangles(float4             *pos,\n                                  float4             *norm,\n                                  uint               *compactedVoxelArray,\n                                  uint               *numVertsScanned,\n                                  uint3               gridSize,\n                                  uint3               gridSizeShift,\n                                  uint3               gridSizeMask,\n                                  float3              voxelSize,\n                                  float               isoValue,\n                                  uint                activeVoxels,\n                                  uint                maxVerts,\n                                  cudaTextureObject_t triTex,\n                                  cudaTextureObject_t numVertsTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (i > activeVoxels - 1) {\n        // can't return here because of syncthreads()\n        i = activeVoxels - 1;\n    }\n\n#if SKIP_EMPTY_VOXELS\n    uint voxel = compactedVoxelArray[i];\n#else\n    uint voxel = i;\n#endif\n\n    // compute position in 3d grid\n    uint3 gridPos = calcGridPos(voxel, gridSizeShift, gridSizeMask);\n\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    // calculate cell vertex positions\n    float3 v[8];\n    v[0] = p;\n    v[1] = p + make_float3(voxelSize.x, 0, 0);\n    v[2] = p + make_float3(voxelSize.x, voxelSize.y, 0);\n    v[3] = p + make_float3(0, voxelSize.y, 0);\n    v[4] = p + make_float3(0, 0, voxelSize.z);\n    v[5] = p + make_float3(voxelSize.x, 0, voxelSize.z);\n    v[6] = p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z);\n    v[7] = p + make_float3(0, voxelSize.y, voxelSize.z);\n\n    // evaluate field values\n    float4 field[8];\n    field[0] = fieldFunc4(v[0]);\n    field[1] = fieldFunc4(v[1]);\n    field[2] = fieldFunc4(v[2]);\n    field[3] = fieldFunc4(v[3]);\n    field[4] = fieldFunc4(v[4]);\n    field[5] = fieldFunc4(v[5]);\n    field[6] = fieldFunc4(v[6]);\n    field[7] = fieldFunc4(v[7]);\n\n    // recalculate flag\n    // (this is faster than storing it in global memory)\n    uint cubeindex;\n    cubeindex = uint(field[0].w < isoValue);\n    cubeindex += uint(field[1].w < isoValue) * 2;\n    cubeindex += uint(field[2].w < isoValue) * 4;\n    cubeindex += uint(field[3].w < isoValue) * 8;\n    cubeindex += uint(field[4].w < isoValue) * 16;\n    cubeindex += uint(field[5].w < isoValue) * 32;\n    cubeindex += uint(field[6].w < isoValue) * 64;\n    cubeindex += uint(field[7].w < isoValue) * 128;\n\n    // find the vertices where the surface intersects the cube\n\n#if USE_SHARED\n    // use partioned shared memory to avoid using local memory\n    __shared__ float3 vertlist[12 * NTHREADS];\n    __shared__ float3 normlist[12 * NTHREADS];\n\n    vertexInterp2(isoValue, v[0], v[1], field[0], field[1], vertlist[threadIdx.x], normlist[threadIdx.x]);\n    vertexInterp2(\n        isoValue, v[1], v[2], field[1], field[2], vertlist[threadIdx.x + NTHREADS], normlist[threadIdx.x + NTHREADS]);\n    vertexInterp2(isoValue,\n                  v[2],\n                  v[3],\n                  field[2],\n                  field[3],\n                  vertlist[threadIdx.x + (NTHREADS * 2)],\n                  normlist[threadIdx.x + (NTHREADS * 2)]);\n    vertexInterp2(isoValue,\n                  v[3],\n                  v[0],\n                  field[3],\n                  field[0],\n                  vertlist[threadIdx.x + (NTHREADS * 3)],\n                  normlist[threadIdx.x + (NTHREADS * 3)]);\n    vertexInterp2(isoValue,\n                  v[4],\n                  v[5],\n                  field[4],\n                  field[5],\n                  vertlist[threadIdx.x + (NTHREADS * 4)],\n                  normlist[threadIdx.x + (NTHREADS * 4)]);\n    vertexInterp2(isoValue,\n                  v[5],\n                  v[6],\n                  field[5],\n                  field[6],\n                  vertlist[threadIdx.x + (NTHREADS * 5)],\n                  normlist[threadIdx.x + (NTHREADS * 5)]);\n    vertexInterp2(isoValue,\n                  v[6],\n                  v[7],\n                  field[6],\n                  field[7],\n                  vertlist[threadIdx.x + (NTHREADS * 6)],\n                  normlist[threadIdx.x + (NTHREADS * 6)]);\n    vertexInterp2(isoValue,\n                  v[7],\n                  v[4],\n                  field[7],\n                  field[4],\n                  vertlist[threadIdx.x + (NTHREADS * 7)],\n                  normlist[threadIdx.x + (NTHREADS * 7)]);\n    vertexInterp2(isoValue,\n                  v[0],\n                  v[4],\n                  field[0],\n                  field[4],\n                  vertlist[threadIdx.x + (NTHREADS * 8)],\n                  normlist[threadIdx.x + (NTHREADS * 8)]);\n    vertexInterp2(isoValue,\n                  v[1],\n                  v[5],\n                  field[1],\n                  field[5],\n                  vertlist[threadIdx.x + (NTHREADS * 9)],\n                  normlist[threadIdx.x + (NTHREADS * 9)]);\n    vertexInterp2(isoValue,\n                  v[2],\n                  v[6],\n                  field[2],\n                  field[6],\n                  vertlist[threadIdx.x + (NTHREADS * 10)],\n                  normlist[threadIdx.x + (NTHREADS * 10)]);\n    vertexInterp2(isoValue,\n                  v[3],\n                  v[7],\n                  field[3],\n                  field[7],\n                  vertlist[threadIdx.x + (NTHREADS * 11)],\n                  normlist[threadIdx.x + (NTHREADS * 11)]);\n    __syncthreads();\n\n#else\n    float3 vertlist[12];\n    float3 normlist[12];\n\n    vertexInterp2(isoValue, v[0], v[1], field[0], field[1], vertlist[0], normlist[0]);\n    vertexInterp2(isoValue, v[1], v[2], field[1], field[2], vertlist[1], normlist[1]);\n    vertexInterp2(isoValue, v[2], v[3], field[2], field[3], vertlist[2], normlist[2]);\n    vertexInterp2(isoValue, v[3], v[0], field[3], field[0], vertlist[3], normlist[3]);\n\n    vertexInterp2(isoValue, v[4], v[5], field[4], field[5], vertlist[4], normlist[4]);\n    vertexInterp2(isoValue, v[5], v[6], field[5], field[6], vertlist[5], normlist[5]);\n    vertexInterp2(isoValue, v[6], v[7], field[6], field[7], vertlist[6], normlist[6]);\n    vertexInterp2(isoValue, v[7], v[4], field[7], field[4], vertlist[7], normlist[7]);\n\n    vertexInterp2(isoValue, v[0], v[4], field[0], field[4], vertlist[8], normlist[8]);\n    vertexInterp2(isoValue, v[1], v[5], field[1], field[5], vertlist[9], normlist[9]);\n    vertexInterp2(isoValue, v[2], v[6], field[2], field[6], vertlist[10], normlist[10]);\n    vertexInterp2(isoValue, v[3], v[7], field[3], field[7], vertlist[11], normlist[11]);\n#endif\n\n    // output triangle vertices\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    for (int i = 0; i < numVerts; i++) {\n        uint edge = tex1Dfetch<uint>(triTex, cubeindex * 16 + i);\n\n        uint index = numVertsScanned[voxel] + i;\n\n        if (index < maxVerts) {\n#if USE_SHARED\n            pos[index]  = make_float4(vertlist[(edge * NTHREADS) + threadIdx.x], 1.0f);\n            norm[index] = make_float4(normlist[(edge * NTHREADS) + threadIdx.x], 0.0f);\n#else\n            pos[index]  = make_float4(vertlist[edge], 1.0f);\n            norm[index] = make_float4(normlist[edge], 0.0f);\n#endif\n        }\n    }\n}\n\nextern \"C\" void launch_generateTriangles(dim3    grid,\n                                         dim3    threads,\n                                         float4 *pos,\n                                         float4 *norm,\n                                         uint   *compactedVoxelArray,\n                                         uint   *numVertsScanned,\n                                         uint3   gridSize,\n                                         uint3   gridSizeShift,\n                                         uint3   gridSizeMask,\n                                         float3  voxelSize,\n                                         float   isoValue,\n                                         uint    activeVoxels,\n                                         uint    maxVerts)\n{\n    generateTriangles<<<grid, NTHREADS>>>(pos,\n                                          norm,\n                                          compactedVoxelArray,\n                                          numVertsScanned,\n                                          gridSize,\n                                          gridSizeShift,\n                                          gridSizeMask,\n                                          voxelSize,\n                                          isoValue,\n                                          activeVoxels,\n                                          maxVerts,\n                                          triTex,\n                                          numVertsTex);\n    getLastCudaError(\"generateTriangles failed\");\n}\n\n// calculate triangle normal\n__device__ float3 calcNormal(float3 *v0, float3 *v1, float3 *v2)\n{\n    float3 edge0 = *v1 - *v0;\n    float3 edge1 = *v2 - *v0;\n    // note - it's faster to perform normalization in vertex shader rather than\n    // here\n    return cross(edge0, edge1);\n}\n\n// version that calculates flat surface normal for each triangle\n__global__ void generateTriangles2(float4             *pos,\n                                   float4             *norm,\n                                   uint               *compactedVoxelArray,\n                                   uint               *numVertsScanned,\n                                   uchar              *volume,\n                                   uint3               gridSize,\n                                   uint3               gridSizeShift,\n                                   uint3               gridSizeMask,\n                                   float3              voxelSize,\n                                   float               isoValue,\n                                   uint                activeVoxels,\n                                   uint                maxVerts,\n                                   cudaTextureObject_t triTex,\n                                   cudaTextureObject_t numVertsTex,\n                                   cudaTextureObject_t volumeTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (i > activeVoxels - 1) {\n        i = activeVoxels - 1;\n    }\n\n#if SKIP_EMPTY_VOXELS\n    uint voxel = compactedVoxelArray[i];\n#else\n    uint voxel = i;\n#endif\n\n    // compute position in 3d grid\n    uint3 gridPos = calcGridPos(voxel, gridSizeShift, gridSizeMask);\n\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    // calculate cell vertex positions\n    float3 v[8];\n    v[0] = p;\n    v[1] = p + make_float3(voxelSize.x, 0, 0);\n    v[2] = p + make_float3(voxelSize.x, voxelSize.y, 0);\n    v[3] = p + make_float3(0, voxelSize.y, 0);\n    v[4] = p + make_float3(0, 0, voxelSize.z);\n    v[5] = p + make_float3(voxelSize.x, 0, voxelSize.z);\n    v[6] = p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z);\n    v[7] = p + make_float3(0, voxelSize.y, voxelSize.z);\n\n#if SAMPLE_VOLUME\n    float field[8];\n    field[0] = sampleVolume(volumeTex, volume, gridPos, gridSize);\n    field[1] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 0), gridSize);\n    field[2] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 0), gridSize);\n    field[3] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 0), gridSize);\n    field[4] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 0, 1), gridSize);\n    field[5] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 1), gridSize);\n    field[6] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 1), gridSize);\n    field[7] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 1), gridSize);\n#else\n    // evaluate field values\n    float field[8];\n    field[0] = fieldFunc(v[0]);\n    field[1] = fieldFunc(v[1]);\n    field[2] = fieldFunc(v[2]);\n    field[3] = fieldFunc(v[3]);\n    field[4] = fieldFunc(v[4]);\n    field[5] = fieldFunc(v[5]);\n    field[6] = fieldFunc(v[6]);\n    field[7] = fieldFunc(v[7]);\n#endif\n\n    // recalculate flag\n    uint cubeindex;\n    cubeindex = uint(field[0] < isoValue);\n    cubeindex += uint(field[1] < isoValue) * 2;\n    cubeindex += uint(field[2] < isoValue) * 4;\n    cubeindex += uint(field[3] < isoValue) * 8;\n    cubeindex += uint(field[4] < isoValue) * 16;\n    cubeindex += uint(field[5] < isoValue) * 32;\n    cubeindex += uint(field[6] < isoValue) * 64;\n    cubeindex += uint(field[7] < isoValue) * 128;\n\n    // find the vertices where the surface intersects the cube\n\n#if USE_SHARED\n    // use shared memory to avoid using local\n    __shared__ float3 vertlist[12 * NTHREADS];\n\n    vertlist[threadIdx.x]                   = vertexInterp(isoValue, v[0], v[1], field[0], field[1]);\n    vertlist[NTHREADS + threadIdx.x]        = vertexInterp(isoValue, v[1], v[2], field[1], field[2]);\n    vertlist[(NTHREADS * 2) + threadIdx.x]  = vertexInterp(isoValue, v[2], v[3], field[2], field[3]);\n    vertlist[(NTHREADS * 3) + threadIdx.x]  = vertexInterp(isoValue, v[3], v[0], field[3], field[0]);\n    vertlist[(NTHREADS * 4) + threadIdx.x]  = vertexInterp(isoValue, v[4], v[5], field[4], field[5]);\n    vertlist[(NTHREADS * 5) + threadIdx.x]  = vertexInterp(isoValue, v[5], v[6], field[5], field[6]);\n    vertlist[(NTHREADS * 6) + threadIdx.x]  = vertexInterp(isoValue, v[6], v[7], field[6], field[7]);\n    vertlist[(NTHREADS * 7) + threadIdx.x]  = vertexInterp(isoValue, v[7], v[4], field[7], field[4]);\n    vertlist[(NTHREADS * 8) + threadIdx.x]  = vertexInterp(isoValue, v[0], v[4], field[0], field[4]);\n    vertlist[(NTHREADS * 9) + threadIdx.x]  = vertexInterp(isoValue, v[1], v[5], field[1], field[5]);\n    vertlist[(NTHREADS * 10) + threadIdx.x] = vertexInterp(isoValue, v[2], v[6], field[2], field[6]);\n    vertlist[(NTHREADS * 11) + threadIdx.x] = vertexInterp(isoValue, v[3], v[7], field[3], field[7]);\n    __syncthreads();\n#else\n\n    float3 vertlist[12];\n\n    vertlist[0] = vertexInterp(isoValue, v[0], v[1], field[0], field[1]);\n    vertlist[1] = vertexInterp(isoValue, v[1], v[2], field[1], field[2]);\n    vertlist[2] = vertexInterp(isoValue, v[2], v[3], field[2], field[3]);\n    vertlist[3] = vertexInterp(isoValue, v[3], v[0], field[3], field[0]);\n\n    vertlist[4] = vertexInterp(isoValue, v[4], v[5], field[4], field[5]);\n    vertlist[5] = vertexInterp(isoValue, v[5], v[6], field[5], field[6]);\n    vertlist[6] = vertexInterp(isoValue, v[6], v[7], field[6], field[7]);\n    vertlist[7] = vertexInterp(isoValue, v[7], v[4], field[7], field[4]);\n\n    vertlist[8]  = vertexInterp(isoValue, v[0], v[4], field[0], field[4]);\n    vertlist[9]  = vertexInterp(isoValue, v[1], v[5], field[1], field[5]);\n    vertlist[10] = vertexInterp(isoValue, v[2], v[6], field[2], field[6]);\n    vertlist[11] = vertexInterp(isoValue, v[3], v[7], field[3], field[7]);\n#endif\n\n    // output triangle vertices\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    for (int i = 0; i < numVerts; i += 3) {\n        uint index = numVertsScanned[voxel] + i;\n\n        float3 *v[3];\n        uint    edge;\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i);\n#if USE_SHARED\n        v[0] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[0] = &vertlist[edge];\n#endif\n\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i + 1);\n#if USE_SHARED\n        v[1] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[1] = &vertlist[edge];\n#endif\n\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i + 2);\n#if USE_SHARED\n        v[2] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[2] = &vertlist[edge];\n#endif\n\n        // calculate triangle surface normal\n        float3 n = calcNormal(v[0], v[1], v[2]);\n\n        if (index < (maxVerts - 3)) {\n            pos[index]  = make_float4(*v[0], 1.0f);\n            norm[index] = make_float4(n, 0.0f);\n\n            pos[index + 1]  = make_float4(*v[1], 1.0f);\n            norm[index + 1] = make_float4(n, 0.0f);\n\n            pos[index + 2]  = make_float4(*v[2], 1.0f);\n            norm[index + 2] = make_float4(n, 0.0f);\n        }\n    }\n}\n\nextern \"C\" void launch_generateTriangles2(dim3    grid,\n                                          dim3    threads,\n                                          float4 *pos,\n                                          float4 *norm,\n                                          uint   *compactedVoxelArray,\n                                          uint   *numVertsScanned,\n                                          uchar  *volume,\n                                          uint3   gridSize,\n                                          uint3   gridSizeShift,\n                                          uint3   gridSizeMask,\n                                          float3  voxelSize,\n                                          float   isoValue,\n                                          uint    activeVoxels,\n                                          uint    maxVerts)\n{\n    generateTriangles2<<<grid, NTHREADS>>>(pos,\n                                           norm,\n                                           compactedVoxelArray,\n                                           numVertsScanned,\n                                           volume,\n                                           gridSize,\n                                           gridSizeShift,\n                                           gridSizeMask,\n                                           voxelSize,\n                                           isoValue,\n                                           activeVoxels,\n                                           maxVerts,\n                                           triTex,\n                                           numVertsTex,\n                                           volumeTex);\n    getLastCudaError(\"generateTriangles2 failed\");\n}\n\nextern \"C\" void ThrustScanWrapper(unsigned int *output, unsigned int *input, unsigned int numElements)\n{\n    thrust::exclusive_scan(thrust::device_ptr<unsigned int>(input),\n                           thrust::device_ptr<unsigned int>(input + numElements),\n                           thrust::device_ptr<unsigned int>(output));\n}\n\n#endif\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef _MARCHING_CUBES_KERNEL_CU_\n#define _MARCHING_CUBES_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n#include <stdio.h>\n#include <string.h>\n#include <thrust/device_vector.h>\n#include <thrust/scan.h>\n\n#include \"defines.h\"\n#include \"tables.h\"\n\n// textures containing look-up tables\nhipTextureObject_t triTex;\nhipTextureObject_t numVertsTex;\n\n// volume data\nhipTextureObject_t volumeTex;\n\nextern \"C\" void allocateTextures(uint **d_edgeTable, uint **d_triTable, uint **d_numVertsTable)\n{\n    checkCudaErrors(hipMalloc((void **)d_edgeTable, 256 * sizeof(uint)));\n    checkCudaErrors(hipMemcpy((void *)*d_edgeTable, (void *)edgeTable, 256 * sizeof(uint), hipMemcpyHostToDevice));\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindUnsigned);\n\n    checkCudaErrors(hipMalloc((void **)d_triTable, 256 * 16 * sizeof(uint)));\n    checkCudaErrors(hipMemcpy((void *)*d_triTable, (void *)triTable, 256 * 16 * sizeof(uint), hipMemcpyHostToDevice));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                = hipResourceTypeLinear;\n    texRes.res.linear.devPtr      = *d_triTable;\n    texRes.res.linear.sizeInBytes = 256 * 16 * sizeof(uint);\n    texRes.res.linear.desc        = channelDesc;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&triTex, &texRes, &texDescr, NULL));\n\n    checkCudaErrors(hipMalloc((void **)d_numVertsTable, 256 * sizeof(uint)));\n    checkCudaErrors(\n        hipMemcpy((void *)*d_numVertsTable, (void *)numVertsTable, 256 * sizeof(uint), hipMemcpyHostToDevice));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                = hipResourceTypeLinear;\n    texRes.res.linear.devPtr      = *d_numVertsTable;\n    texRes.res.linear.sizeInBytes = 256 * sizeof(uint);\n    texRes.res.linear.desc        = channelDesc;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&numVertsTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void createVolumeTexture(uchar *d_volume, size_t buffSize)\n{\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType                = hipResourceTypeLinear;\n    texRes.res.linear.devPtr      = d_volume;\n    texRes.res.linear.sizeInBytes = buffSize;\n    texRes.res.linear.desc        = hipCreateChannelDesc(8, 0, 0, 0, hipChannelFormatKindUnsigned);\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&volumeTex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void destroyAllTextureObjects()\n{\n    checkCudaErrors(hipDestroyTextureObject(triTex));\n    checkCudaErrors(hipDestroyTextureObject(numVertsTex));\n    checkCudaErrors(hipDestroyTextureObject(volumeTex));\n}\n\n// an interesting field function\n__device__ float tangle(float x, float y, float z)\n{\n    x *= 3.0f;\n    y *= 3.0f;\n    z *= 3.0f;\n    return (x * x * x * x - 5.0f * x * x + y * y * y * y - 5.0f * y * y + z * z * z * z - 5.0f * z * z + 11.8f) * 0.2f\n         + 0.5f;\n}\n\n// evaluate field function at point\n__device__ float fieldFunc(float3 p) { return tangle(p.x, p.y, p.z); }\n\n// evaluate field function at a point\n// returns value and gradient in float4\n__device__ float4 fieldFunc4(float3 p)\n{\n    float       v  = tangle(p.x, p.y, p.z);\n    const float d  = 0.001f;\n    float       dx = tangle(p.x + d, p.y, p.z) - v;\n    float       dy = tangle(p.x, p.y + d, p.z) - v;\n    float       dz = tangle(p.x, p.y, p.z + d) - v;\n    return make_float4(dx, dy, dz, v);\n}\n\n// sample volume data set at a point\n__device__ float sampleVolume(hipTextureObject_t volumeTex, uchar *data, uint3 p, uint3 gridSize)\n{\n    p.x    = min(p.x, gridSize.x - 1);\n    p.y    = min(p.y, gridSize.y - 1);\n    p.z    = min(p.z, gridSize.z - 1);\n    uint i = (p.z * gridSize.x * gridSize.y) + (p.y * gridSize.x) + p.x;\n    //    return (float) data[i] / 255.0f;\n    return tex1Dfetch<float>(volumeTex, i);\n}\n\n// compute position in 3d grid from 1d index\n// only works for power of 2 sizes\n__device__ uint3 calcGridPos(uint i, uint3 gridSizeShift, uint3 gridSizeMask)\n{\n    uint3 gridPos;\n    gridPos.x = i & gridSizeMask.x;\n    gridPos.y = (i >> gridSizeShift.y) & gridSizeMask.y;\n    gridPos.z = (i >> gridSizeShift.z) & gridSizeMask.z;\n    return gridPos;\n}\n\n// classify voxel based on number of vertices it will generate\n// one thread per voxel\n__global__ void classifyVoxel(uint               *voxelVerts,\n                              uint               *voxelOccupied,\n                              uchar              *volume,\n                              uint3               gridSize,\n                              uint3               gridSizeShift,\n                              uint3               gridSizeMask,\n                              uint                numVoxels,\n                              float3              voxelSize,\n                              float               isoValue,\n                              hipTextureObject_t numVertsTex,\n                              hipTextureObject_t volumeTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    uint3 gridPos = calcGridPos(i, gridSizeShift, gridSizeMask);\n\n// read field values at neighbouring grid vertices\n#if SAMPLE_VOLUME\n    float field[8];\n    field[0] = sampleVolume(volumeTex, volume, gridPos, gridSize);\n    field[1] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 0), gridSize);\n    field[2] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 0), gridSize);\n    field[3] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 0), gridSize);\n    field[4] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 0, 1), gridSize);\n    field[5] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 1), gridSize);\n    field[6] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 1), gridSize);\n    field[7] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 1), gridSize);\n#else\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    float field[8];\n    field[0] = fieldFunc(p);\n    field[1] = fieldFunc(p + make_float3(voxelSize.x, 0, 0));\n    field[2] = fieldFunc(p + make_float3(voxelSize.x, voxelSize.y, 0));\n    field[3] = fieldFunc(p + make_float3(0, voxelSize.y, 0));\n    field[4] = fieldFunc(p + make_float3(0, 0, voxelSize.z));\n    field[5] = fieldFunc(p + make_float3(voxelSize.x, 0, voxelSize.z));\n    field[6] = fieldFunc(p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z));\n    field[7] = fieldFunc(p + make_float3(0, voxelSize.y, voxelSize.z));\n#endif\n\n    // calculate flag indicating if each vertex is inside or outside isosurface\n    uint cubeindex;\n    cubeindex = uint(field[0] < isoValue);\n    cubeindex += uint(field[1] < isoValue) * 2;\n    cubeindex += uint(field[2] < isoValue) * 4;\n    cubeindex += uint(field[3] < isoValue) * 8;\n    cubeindex += uint(field[4] < isoValue) * 16;\n    cubeindex += uint(field[5] < isoValue) * 32;\n    cubeindex += uint(field[6] < isoValue) * 64;\n    cubeindex += uint(field[7] < isoValue) * 128;\n\n    // read number of vertices from texture\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    if (i < numVoxels) {\n        voxelVerts[i]    = numVerts;\n        voxelOccupied[i] = (numVerts > 0);\n    }\n}\n\nextern \"C\" void launch_classifyVoxel(dim3   grid,\n                                     dim3   threads,\n                                     uint  *voxelVerts,\n                                     uint  *voxelOccupied,\n                                     uchar *volume,\n                                     uint3  gridSize,\n                                     uint3  gridSizeShift,\n                                     uint3  gridSizeMask,\n                                     uint   numVoxels,\n                                     float3 voxelSize,\n                                     float  isoValue)\n{\n    // calculate number of vertices need per voxel\n    classifyVoxel<<<grid, threads>>>(voxelVerts,\n                                     voxelOccupied,\n                                     volume,\n                                     gridSize,\n                                     gridSizeShift,\n                                     gridSizeMask,\n                                     numVoxels,\n                                     voxelSize,\n                                     isoValue,\n                                     numVertsTex,\n                                     volumeTex);\n    getLastCudaError(\"classifyVoxel failed\");\n}\n\n// compact voxel array\n__global__ void compactVoxels(uint *compactedVoxelArray, uint *voxelOccupied, uint *voxelOccupiedScan, uint numVoxels)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (voxelOccupied[i] && (i < numVoxels)) {\n        compactedVoxelArray[voxelOccupiedScan[i]] = i;\n    }\n}\n\nextern \"C\" void launch_compactVoxels(dim3  grid,\n                                     dim3  threads,\n                                     uint *compactedVoxelArray,\n                                     uint *voxelOccupied,\n                                     uint *voxelOccupiedScan,\n                                     uint  numVoxels)\n{\n    compactVoxels<<<grid, threads>>>(compactedVoxelArray, voxelOccupied, voxelOccupiedScan, numVoxels);\n    getLastCudaError(\"compactVoxels failed\");\n}\n\n// compute interpolated vertex along an edge\n__device__ float3 vertexInterp(float isolevel, float3 p0, float3 p1, float f0, float f1)\n{\n    float t = (isolevel - f0) / (f1 - f0);\n    return lerp(p0, p1, t);\n}\n\n// compute interpolated vertex position and normal along an edge\n__device__ void vertexInterp2(float isolevel, float3 p0, float3 p1, float4 f0, float4 f1, float3 &p, float3 &n)\n{\n    float t = (isolevel - f0.w) / (f1.w - f0.w);\n    p       = lerp(p0, p1, t);\n    n.x     = lerp(f0.x, f1.x, t);\n    n.y     = lerp(f0.y, f1.y, t);\n    n.z     = lerp(f0.z, f1.z, t);\n    //    n = normalize(n);\n}\n\n// generate triangles for each voxel using marching cubes\n// interpolates normals from field function\n__global__ void generateTriangles(float4             *pos,\n                                  float4             *norm,\n                                  uint               *compactedVoxelArray,\n                                  uint               *numVertsScanned,\n                                  uint3               gridSize,\n                                  uint3               gridSizeShift,\n                                  uint3               gridSizeMask,\n                                  float3              voxelSize,\n                                  float               isoValue,\n                                  uint                activeVoxels,\n                                  uint                maxVerts,\n                                  hipTextureObject_t triTex,\n                                  hipTextureObject_t numVertsTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (i > activeVoxels - 1) {\n        // can't return here because of syncthreads()\n        i = activeVoxels - 1;\n    }\n\n#if SKIP_EMPTY_VOXELS\n    uint voxel = compactedVoxelArray[i];\n#else\n    uint voxel = i;\n#endif\n\n    // compute position in 3d grid\n    uint3 gridPos = calcGridPos(voxel, gridSizeShift, gridSizeMask);\n\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    // calculate cell vertex positions\n    float3 v[8];\n    v[0] = p;\n    v[1] = p + make_float3(voxelSize.x, 0, 0);\n    v[2] = p + make_float3(voxelSize.x, voxelSize.y, 0);\n    v[3] = p + make_float3(0, voxelSize.y, 0);\n    v[4] = p + make_float3(0, 0, voxelSize.z);\n    v[5] = p + make_float3(voxelSize.x, 0, voxelSize.z);\n    v[6] = p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z);\n    v[7] = p + make_float3(0, voxelSize.y, voxelSize.z);\n\n    // evaluate field values\n    float4 field[8];\n    field[0] = fieldFunc4(v[0]);\n    field[1] = fieldFunc4(v[1]);\n    field[2] = fieldFunc4(v[2]);\n    field[3] = fieldFunc4(v[3]);\n    field[4] = fieldFunc4(v[4]);\n    field[5] = fieldFunc4(v[5]);\n    field[6] = fieldFunc4(v[6]);\n    field[7] = fieldFunc4(v[7]);\n\n    // recalculate flag\n    // (this is faster than storing it in global memory)\n    uint cubeindex;\n    cubeindex = uint(field[0].w < isoValue);\n    cubeindex += uint(field[1].w < isoValue) * 2;\n    cubeindex += uint(field[2].w < isoValue) * 4;\n    cubeindex += uint(field[3].w < isoValue) * 8;\n    cubeindex += uint(field[4].w < isoValue) * 16;\n    cubeindex += uint(field[5].w < isoValue) * 32;\n    cubeindex += uint(field[6].w < isoValue) * 64;\n    cubeindex += uint(field[7].w < isoValue) * 128;\n\n    // find the vertices where the surface intersects the cube\n\n#if USE_SHARED\n    // use partioned shared memory to avoid using local memory\n    __shared__ float3 vertlist[12 * NTHREADS];\n    __shared__ float3 normlist[12 * NTHREADS];\n\n    vertexInterp2(isoValue, v[0], v[1], field[0], field[1], vertlist[threadIdx.x], normlist[threadIdx.x]);\n    vertexInterp2(\n        isoValue, v[1], v[2], field[1], field[2], vertlist[threadIdx.x + NTHREADS], normlist[threadIdx.x + NTHREADS]);\n    vertexInterp2(isoValue,\n                  v[2],\n                  v[3],\n                  field[2],\n                  field[3],\n                  vertlist[threadIdx.x + (NTHREADS * 2)],\n                  normlist[threadIdx.x + (NTHREADS * 2)]);\n    vertexInterp2(isoValue,\n                  v[3],\n                  v[0],\n                  field[3],\n                  field[0],\n                  vertlist[threadIdx.x + (NTHREADS * 3)],\n                  normlist[threadIdx.x + (NTHREADS * 3)]);\n    vertexInterp2(isoValue,\n                  v[4],\n                  v[5],\n                  field[4],\n                  field[5],\n                  vertlist[threadIdx.x + (NTHREADS * 4)],\n                  normlist[threadIdx.x + (NTHREADS * 4)]);\n    vertexInterp2(isoValue,\n                  v[5],\n                  v[6],\n                  field[5],\n                  field[6],\n                  vertlist[threadIdx.x + (NTHREADS * 5)],\n                  normlist[threadIdx.x + (NTHREADS * 5)]);\n    vertexInterp2(isoValue,\n                  v[6],\n                  v[7],\n                  field[6],\n                  field[7],\n                  vertlist[threadIdx.x + (NTHREADS * 6)],\n                  normlist[threadIdx.x + (NTHREADS * 6)]);\n    vertexInterp2(isoValue,\n                  v[7],\n                  v[4],\n                  field[7],\n                  field[4],\n                  vertlist[threadIdx.x + (NTHREADS * 7)],\n                  normlist[threadIdx.x + (NTHREADS * 7)]);\n    vertexInterp2(isoValue,\n                  v[0],\n                  v[4],\n                  field[0],\n                  field[4],\n                  vertlist[threadIdx.x + (NTHREADS * 8)],\n                  normlist[threadIdx.x + (NTHREADS * 8)]);\n    vertexInterp2(isoValue,\n                  v[1],\n                  v[5],\n                  field[1],\n                  field[5],\n                  vertlist[threadIdx.x + (NTHREADS * 9)],\n                  normlist[threadIdx.x + (NTHREADS * 9)]);\n    vertexInterp2(isoValue,\n                  v[2],\n                  v[6],\n                  field[2],\n                  field[6],\n                  vertlist[threadIdx.x + (NTHREADS * 10)],\n                  normlist[threadIdx.x + (NTHREADS * 10)]);\n    vertexInterp2(isoValue,\n                  v[3],\n                  v[7],\n                  field[3],\n                  field[7],\n                  vertlist[threadIdx.x + (NTHREADS * 11)],\n                  normlist[threadIdx.x + (NTHREADS * 11)]);\n    __syncthreads();\n\n#else\n    float3 vertlist[12];\n    float3 normlist[12];\n\n    vertexInterp2(isoValue, v[0], v[1], field[0], field[1], vertlist[0], normlist[0]);\n    vertexInterp2(isoValue, v[1], v[2], field[1], field[2], vertlist[1], normlist[1]);\n    vertexInterp2(isoValue, v[2], v[3], field[2], field[3], vertlist[2], normlist[2]);\n    vertexInterp2(isoValue, v[3], v[0], field[3], field[0], vertlist[3], normlist[3]);\n\n    vertexInterp2(isoValue, v[4], v[5], field[4], field[5], vertlist[4], normlist[4]);\n    vertexInterp2(isoValue, v[5], v[6], field[5], field[6], vertlist[5], normlist[5]);\n    vertexInterp2(isoValue, v[6], v[7], field[6], field[7], vertlist[6], normlist[6]);\n    vertexInterp2(isoValue, v[7], v[4], field[7], field[4], vertlist[7], normlist[7]);\n\n    vertexInterp2(isoValue, v[0], v[4], field[0], field[4], vertlist[8], normlist[8]);\n    vertexInterp2(isoValue, v[1], v[5], field[1], field[5], vertlist[9], normlist[9]);\n    vertexInterp2(isoValue, v[2], v[6], field[2], field[6], vertlist[10], normlist[10]);\n    vertexInterp2(isoValue, v[3], v[7], field[3], field[7], vertlist[11], normlist[11]);\n#endif\n\n    // output triangle vertices\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    for (int i = 0; i < numVerts; i++) {\n        uint edge = tex1Dfetch<uint>(triTex, cubeindex * 16 + i);\n\n        uint index = numVertsScanned[voxel] + i;\n\n        if (index < maxVerts) {\n#if USE_SHARED\n            pos[index]  = make_float4(vertlist[(edge * NTHREADS) + threadIdx.x], 1.0f);\n            norm[index] = make_float4(normlist[(edge * NTHREADS) + threadIdx.x], 0.0f);\n#else\n            pos[index]  = make_float4(vertlist[edge], 1.0f);\n            norm[index] = make_float4(normlist[edge], 0.0f);\n#endif\n        }\n    }\n}\n\nextern \"C\" void launch_generateTriangles(dim3    grid,\n                                         dim3    threads,\n                                         float4 *pos,\n                                         float4 *norm,\n                                         uint   *compactedVoxelArray,\n                                         uint   *numVertsScanned,\n                                         uint3   gridSize,\n                                         uint3   gridSizeShift,\n                                         uint3   gridSizeMask,\n                                         float3  voxelSize,\n                                         float   isoValue,\n                                         uint    activeVoxels,\n                                         uint    maxVerts)\n{\n    generateTriangles<<<grid, NTHREADS>>>(pos,\n                                          norm,\n                                          compactedVoxelArray,\n                                          numVertsScanned,\n                                          gridSize,\n                                          gridSizeShift,\n                                          gridSizeMask,\n                                          voxelSize,\n                                          isoValue,\n                                          activeVoxels,\n                                          maxVerts,\n                                          triTex,\n                                          numVertsTex);\n    getLastCudaError(\"generateTriangles failed\");\n}\n\n// calculate triangle normal\n__device__ float3 calcNormal(float3 *v0, float3 *v1, float3 *v2)\n{\n    float3 edge0 = *v1 - *v0;\n    float3 edge1 = *v2 - *v0;\n    // note - it's faster to perform normalization in vertex shader rather than\n    // here\n    return cross(edge0, edge1);\n}\n\n// version that calculates flat surface normal for each triangle\n__global__ void generateTriangles2(float4             *pos,\n                                   float4             *norm,\n                                   uint               *compactedVoxelArray,\n                                   uint               *numVertsScanned,\n                                   uchar              *volume,\n                                   uint3               gridSize,\n                                   uint3               gridSizeShift,\n                                   uint3               gridSizeMask,\n                                   float3              voxelSize,\n                                   float               isoValue,\n                                   uint                activeVoxels,\n                                   uint                maxVerts,\n                                   hipTextureObject_t triTex,\n                                   hipTextureObject_t numVertsTex,\n                                   hipTextureObject_t volumeTex)\n{\n    uint blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;\n    uint i       = __mul24(blockId, blockDim.x) + threadIdx.x;\n\n    if (i > activeVoxels - 1) {\n        i = activeVoxels - 1;\n    }\n\n#if SKIP_EMPTY_VOXELS\n    uint voxel = compactedVoxelArray[i];\n#else\n    uint voxel = i;\n#endif\n\n    // compute position in 3d grid\n    uint3 gridPos = calcGridPos(voxel, gridSizeShift, gridSizeMask);\n\n    float3 p;\n    p.x = -1.0f + (gridPos.x * voxelSize.x);\n    p.y = -1.0f + (gridPos.y * voxelSize.y);\n    p.z = -1.0f + (gridPos.z * voxelSize.z);\n\n    // calculate cell vertex positions\n    float3 v[8];\n    v[0] = p;\n    v[1] = p + make_float3(voxelSize.x, 0, 0);\n    v[2] = p + make_float3(voxelSize.x, voxelSize.y, 0);\n    v[3] = p + make_float3(0, voxelSize.y, 0);\n    v[4] = p + make_float3(0, 0, voxelSize.z);\n    v[5] = p + make_float3(voxelSize.x, 0, voxelSize.z);\n    v[6] = p + make_float3(voxelSize.x, voxelSize.y, voxelSize.z);\n    v[7] = p + make_float3(0, voxelSize.y, voxelSize.z);\n\n#if SAMPLE_VOLUME\n    float field[8];\n    field[0] = sampleVolume(volumeTex, volume, gridPos, gridSize);\n    field[1] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 0), gridSize);\n    field[2] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 0), gridSize);\n    field[3] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 0), gridSize);\n    field[4] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 0, 1), gridSize);\n    field[5] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 0, 1), gridSize);\n    field[6] = sampleVolume(volumeTex, volume, gridPos + make_uint3(1, 1, 1), gridSize);\n    field[7] = sampleVolume(volumeTex, volume, gridPos + make_uint3(0, 1, 1), gridSize);\n#else\n    // evaluate field values\n    float field[8];\n    field[0] = fieldFunc(v[0]);\n    field[1] = fieldFunc(v[1]);\n    field[2] = fieldFunc(v[2]);\n    field[3] = fieldFunc(v[3]);\n    field[4] = fieldFunc(v[4]);\n    field[5] = fieldFunc(v[5]);\n    field[6] = fieldFunc(v[6]);\n    field[7] = fieldFunc(v[7]);\n#endif\n\n    // recalculate flag\n    uint cubeindex;\n    cubeindex = uint(field[0] < isoValue);\n    cubeindex += uint(field[1] < isoValue) * 2;\n    cubeindex += uint(field[2] < isoValue) * 4;\n    cubeindex += uint(field[3] < isoValue) * 8;\n    cubeindex += uint(field[4] < isoValue) * 16;\n    cubeindex += uint(field[5] < isoValue) * 32;\n    cubeindex += uint(field[6] < isoValue) * 64;\n    cubeindex += uint(field[7] < isoValue) * 128;\n\n    // find the vertices where the surface intersects the cube\n\n#if USE_SHARED\n    // use shared memory to avoid using local\n    __shared__ float3 vertlist[12 * NTHREADS];\n\n    vertlist[threadIdx.x]                   = vertexInterp(isoValue, v[0], v[1], field[0], field[1]);\n    vertlist[NTHREADS + threadIdx.x]        = vertexInterp(isoValue, v[1], v[2], field[1], field[2]);\n    vertlist[(NTHREADS * 2) + threadIdx.x]  = vertexInterp(isoValue, v[2], v[3], field[2], field[3]);\n    vertlist[(NTHREADS * 3) + threadIdx.x]  = vertexInterp(isoValue, v[3], v[0], field[3], field[0]);\n    vertlist[(NTHREADS * 4) + threadIdx.x]  = vertexInterp(isoValue, v[4], v[5], field[4], field[5]);\n    vertlist[(NTHREADS * 5) + threadIdx.x]  = vertexInterp(isoValue, v[5], v[6], field[5], field[6]);\n    vertlist[(NTHREADS * 6) + threadIdx.x]  = vertexInterp(isoValue, v[6], v[7], field[6], field[7]);\n    vertlist[(NTHREADS * 7) + threadIdx.x]  = vertexInterp(isoValue, v[7], v[4], field[7], field[4]);\n    vertlist[(NTHREADS * 8) + threadIdx.x]  = vertexInterp(isoValue, v[0], v[4], field[0], field[4]);\n    vertlist[(NTHREADS * 9) + threadIdx.x]  = vertexInterp(isoValue, v[1], v[5], field[1], field[5]);\n    vertlist[(NTHREADS * 10) + threadIdx.x] = vertexInterp(isoValue, v[2], v[6], field[2], field[6]);\n    vertlist[(NTHREADS * 11) + threadIdx.x] = vertexInterp(isoValue, v[3], v[7], field[3], field[7]);\n    __syncthreads();\n#else\n\n    float3 vertlist[12];\n\n    vertlist[0] = vertexInterp(isoValue, v[0], v[1], field[0], field[1]);\n    vertlist[1] = vertexInterp(isoValue, v[1], v[2], field[1], field[2]);\n    vertlist[2] = vertexInterp(isoValue, v[2], v[3], field[2], field[3]);\n    vertlist[3] = vertexInterp(isoValue, v[3], v[0], field[3], field[0]);\n\n    vertlist[4] = vertexInterp(isoValue, v[4], v[5], field[4], field[5]);\n    vertlist[5] = vertexInterp(isoValue, v[5], v[6], field[5], field[6]);\n    vertlist[6] = vertexInterp(isoValue, v[6], v[7], field[6], field[7]);\n    vertlist[7] = vertexInterp(isoValue, v[7], v[4], field[7], field[4]);\n\n    vertlist[8]  = vertexInterp(isoValue, v[0], v[4], field[0], field[4]);\n    vertlist[9]  = vertexInterp(isoValue, v[1], v[5], field[1], field[5]);\n    vertlist[10] = vertexInterp(isoValue, v[2], v[6], field[2], field[6]);\n    vertlist[11] = vertexInterp(isoValue, v[3], v[7], field[3], field[7]);\n#endif\n\n    // output triangle vertices\n    uint numVerts = tex1Dfetch<uint>(numVertsTex, cubeindex);\n\n    for (int i = 0; i < numVerts; i += 3) {\n        uint index = numVertsScanned[voxel] + i;\n\n        float3 *v[3];\n        uint    edge;\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i);\n#if USE_SHARED\n        v[0] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[0] = &vertlist[edge];\n#endif\n\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i + 1);\n#if USE_SHARED\n        v[1] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[1] = &vertlist[edge];\n#endif\n\n        edge = tex1Dfetch<uint>(triTex, (cubeindex * 16) + i + 2);\n#if USE_SHARED\n        v[2] = &vertlist[(edge * NTHREADS) + threadIdx.x];\n#else\n        v[2] = &vertlist[edge];\n#endif\n\n        // calculate triangle surface normal\n        float3 n = calcNormal(v[0], v[1], v[2]);\n\n        if (index < (maxVerts - 3)) {\n            pos[index]  = make_float4(*v[0], 1.0f);\n            norm[index] = make_float4(n, 0.0f);\n\n            pos[index + 1]  = make_float4(*v[1], 1.0f);\n            norm[index + 1] = make_float4(n, 0.0f);\n\n            pos[index + 2]  = make_float4(*v[2], 1.0f);\n            norm[index + 2] = make_float4(n, 0.0f);\n        }\n    }\n}\n\nextern \"C\" void launch_generateTriangles2(dim3    grid,\n                                          dim3    threads,\n                                          float4 *pos,\n                                          float4 *norm,\n                                          uint   *compactedVoxelArray,\n                                          uint   *numVertsScanned,\n                                          uchar  *volume,\n                                          uint3   gridSize,\n                                          uint3   gridSizeShift,\n                                          uint3   gridSizeMask,\n                                          float3  voxelSize,\n                                          float   isoValue,\n                                          uint    activeVoxels,\n                                          uint    maxVerts)\n{\n    generateTriangles2<<<grid, NTHREADS>>>(pos,\n                                           norm,\n                                           compactedVoxelArray,\n                                           numVertsScanned,\n                                           volume,\n                                           gridSize,\n                                           gridSizeShift,\n                                           gridSizeMask,\n                                           voxelSize,\n                                           isoValue,\n                                           activeVoxels,\n                                           maxVerts,\n                                           triTex,\n                                           numVertsTex,\n                                           volumeTex);\n    getLastCudaError(\"generateTriangles2 failed\");\n}\n\nextern \"C\" void ThrustScanWrapper(unsigned int *output, unsigned int *input, unsigned int numElements)\n{\n    thrust::exclusive_scan(thrust::device_ptr<unsigned int>(input),\n                           thrust::device_ptr<unsigned int>(input + numElements),\n                           thrust::device_ptr<unsigned int>(output));\n}\n\n#endif\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/marchingCubes/marchingCubes_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Implements NV12 to BGR batch conversion\n\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#include \"resize_convert.h\"\n\n#define CONV_THREADS_X 64\n#define CONV_THREADS_Y 10\n\n__forceinline__ __device__ static float clampF(float x, float lower, float upper)\n{\n    return x < lower ? lower : (x > upper ? upper : x);\n}\n\n__global__ static void nv12ToBGRplanarBatchKernel(const uint8_t *pNv12,\n                                                  int            nNv12Pitch,\n                                                  float         *pBgr,\n                                                  int            nRgbPitch,\n                                                  int            nWidth,\n                                                  int            nHeight,\n                                                  int            nBatchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    if ((x << 2) + 1 > nWidth || (y << 1) + 1 > nHeight)\n        return;\n\n    const uint8_t *__restrict__ pSrc = pNv12;\n\n    for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {\n        pSrc = pNv12 + i * ((nHeight * nNv12Pitch * 3) >> 1) + (x << 2) + (y << 1) * nNv12Pitch;\n        uchar4 luma2x01, luma2x23, uv2;\n        *(uint32_t *)&luma2x01 = *(uint32_t *)pSrc;\n        *(uint32_t *)&luma2x23 = *(uint32_t *)(pSrc + nNv12Pitch);\n        *(uint32_t *)&uv2      = *(uint32_t *)(pSrc + (nHeight - y) * nNv12Pitch);\n\n        float *pDstBlock = (pBgr + i * ((nHeight * nRgbPitch * 3) >> 2) + ((blockIdx.x * blockDim.x) << 2)\n                            + ((blockIdx.y * blockDim.y) << 1) * (nRgbPitch >> 2));\n\n        float2 add1;\n        float2 add2;\n        float2 add3;\n        float2 add00, add01, add02, add03;\n        float2 d, e;\n\n        add00.x = 1.1644f * luma2x01.x;\n        add01.x = 1.1644f * luma2x01.y;\n        add00.y = 1.1644f * luma2x01.z;\n        add01.y = 1.1644f * luma2x01.w;\n\n        add02.x = 1.1644f * luma2x23.x;\n        add03.x = 1.1644f * luma2x23.y;\n        add02.y = 1.1644f * luma2x23.z;\n        add03.y = 1.1644f * luma2x23.w;\n\n        d.x = uv2.x - 128.0f;\n        e.x = uv2.y - 128.0f;\n        d.y = uv2.z - 128.0f;\n        e.y = uv2.w - 128.0f;\n\n        add1.x = 2.0172f * d.x;\n        add1.y = 2.0172f * d.y;\n\n        add2.x = (-0.3918f) * d.x + (-0.8130f) * e.x;\n        add2.y = (-0.3918f) * d.y + (-0.8130f) * e.y;\n\n        add3.x = 1.5960f * e.x;\n        add3.y = 1.5960f * e.y;\n\n        int rowStride     = (threadIdx.y << 1) * (nRgbPitch >> 2);\n        int nextRowStride = ((threadIdx.y << 1) + 1) * (nRgbPitch >> 2);\n        // B\n        *((float4 *)&pDstBlock[rowStride + (threadIdx.x << 2)]) = make_float4(clampF(add00.x + add1.x, 0.0f, 255.0f),\n                                                                              clampF(add01.x + add1.x, 0.0f, 255.0f),\n                                                                              clampF(add00.y + add1.y, 0.0f, 255.0f),\n                                                                              clampF(add01.y + add1.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add1.x, 0.0f, 255.0f),\n                        clampF(add03.x + add1.x, 0.0f, 255.0f),\n                        clampF(add02.y + add1.y, 0.0f, 255.0f),\n                        clampF(add03.y + add1.y, 0.0f, 255.0f));\n\n        int planeStride = nHeight * nRgbPitch >> 2;\n        // G\n        *((float4 *)&pDstBlock[planeStride + rowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add00.x + add2.x, 0.0f, 255.0f),\n                        clampF(add01.x + add2.x, 0.0f, 255.0f),\n                        clampF(add00.y + add2.y, 0.0f, 255.0f),\n                        clampF(add01.y + add2.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[planeStride + nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add2.x, 0.0f, 255.0f),\n                        clampF(add03.x + add2.x, 0.0f, 255.0f),\n                        clampF(add02.y + add2.y, 0.0f, 255.0f),\n                        clampF(add03.y + add2.y, 0.0f, 255.0f));\n\n        // R\n        *((float4 *)&pDstBlock[(planeStride << 1) + rowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add00.x + add3.x, 0.0f, 255.0f),\n                        clampF(add01.x + add3.x, 0.0f, 255.0f),\n                        clampF(add00.y + add3.y, 0.0f, 255.0f),\n                        clampF(add01.y + add3.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[(planeStride << 1) + nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add3.x, 0.0f, 255.0f),\n                        clampF(add03.x + add3.x, 0.0f, 255.0f),\n                        clampF(add02.y + add3.y, 0.0f, 255.0f),\n                        clampF(add03.y + add3.y, 0.0f, 255.0f));\n    }\n}\n\nvoid nv12ToBGRplanarBatch(uint8_t     *pNv12,\n                          int          nNv12Pitch,\n                          float       *pBgr,\n                          int          nRgbPitch,\n                          int          nWidth,\n                          int          nHeight,\n                          int          nBatchSize,\n                          cudaStream_t stream)\n{\n    dim3 threads(CONV_THREADS_X, CONV_THREADS_Y);\n\n    size_t blockDimZ = nBatchSize;\n\n    // Restricting blocks in Z-dim till 32 to not launch too many blocks\n    blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n\n    dim3 blocks((nWidth / 4 - 1) / threads.x + 1, (nHeight / 2 - 1) / threads.y + 1, blockDimZ);\n    nv12ToBGRplanarBatchKernel<<<blocks, threads, 0, stream>>>(\n        pNv12, nNv12Pitch, pBgr, nRgbPitch, nWidth, nHeight, nBatchSize);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Implements NV12 to BGR batch conversion\n\n#include <hip/hip_runtime.h>\n\n#include \"resize_convert.h\"\n\n#define CONV_THREADS_X 64\n#define CONV_THREADS_Y 10\n\n__forceinline__ __device__ static float clampF(float x, float lower, float upper)\n{\n    return x < lower ? lower : (x > upper ? upper : x);\n}\n\n__global__ static void nv12ToBGRplanarBatchKernel(const uint8_t *pNv12,\n                                                  int            nNv12Pitch,\n                                                  float         *pBgr,\n                                                  int            nRgbPitch,\n                                                  int            nWidth,\n                                                  int            nHeight,\n                                                  int            nBatchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    if ((x << 2) + 1 > nWidth || (y << 1) + 1 > nHeight)\n        return;\n\n    const uint8_t *__restrict__ pSrc = pNv12;\n\n    for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {\n        pSrc = pNv12 + i * ((nHeight * nNv12Pitch * 3) >> 1) + (x << 2) + (y << 1) * nNv12Pitch;\n        uchar4 luma2x01, luma2x23, uv2;\n        *(uint32_t *)&luma2x01 = *(uint32_t *)pSrc;\n        *(uint32_t *)&luma2x23 = *(uint32_t *)(pSrc + nNv12Pitch);\n        *(uint32_t *)&uv2      = *(uint32_t *)(pSrc + (nHeight - y) * nNv12Pitch);\n\n        float *pDstBlock = (pBgr + i * ((nHeight * nRgbPitch * 3) >> 2) + ((blockIdx.x * blockDim.x) << 2)\n                            + ((blockIdx.y * blockDim.y) << 1) * (nRgbPitch >> 2));\n\n        float2 add1;\n        float2 add2;\n        float2 add3;\n        float2 add00, add01, add02, add03;\n        float2 d, e;\n\n        add00.x = 1.1644f * luma2x01.x;\n        add01.x = 1.1644f * luma2x01.y;\n        add00.y = 1.1644f * luma2x01.z;\n        add01.y = 1.1644f * luma2x01.w;\n\n        add02.x = 1.1644f * luma2x23.x;\n        add03.x = 1.1644f * luma2x23.y;\n        add02.y = 1.1644f * luma2x23.z;\n        add03.y = 1.1644f * luma2x23.w;\n\n        d.x = uv2.x - 128.0f;\n        e.x = uv2.y - 128.0f;\n        d.y = uv2.z - 128.0f;\n        e.y = uv2.w - 128.0f;\n\n        add1.x = 2.0172f * d.x;\n        add1.y = 2.0172f * d.y;\n\n        add2.x = (-0.3918f) * d.x + (-0.8130f) * e.x;\n        add2.y = (-0.3918f) * d.y + (-0.8130f) * e.y;\n\n        add3.x = 1.5960f * e.x;\n        add3.y = 1.5960f * e.y;\n\n        int rowStride     = (threadIdx.y << 1) * (nRgbPitch >> 2);\n        int nextRowStride = ((threadIdx.y << 1) + 1) * (nRgbPitch >> 2);\n        // B\n        *((float4 *)&pDstBlock[rowStride + (threadIdx.x << 2)]) = make_float4(clampF(add00.x + add1.x, 0.0f, 255.0f),\n                                                                              clampF(add01.x + add1.x, 0.0f, 255.0f),\n                                                                              clampF(add00.y + add1.y, 0.0f, 255.0f),\n                                                                              clampF(add01.y + add1.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add1.x, 0.0f, 255.0f),\n                        clampF(add03.x + add1.x, 0.0f, 255.0f),\n                        clampF(add02.y + add1.y, 0.0f, 255.0f),\n                        clampF(add03.y + add1.y, 0.0f, 255.0f));\n\n        int planeStride = nHeight * nRgbPitch >> 2;\n        // G\n        *((float4 *)&pDstBlock[planeStride + rowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add00.x + add2.x, 0.0f, 255.0f),\n                        clampF(add01.x + add2.x, 0.0f, 255.0f),\n                        clampF(add00.y + add2.y, 0.0f, 255.0f),\n                        clampF(add01.y + add2.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[planeStride + nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add2.x, 0.0f, 255.0f),\n                        clampF(add03.x + add2.x, 0.0f, 255.0f),\n                        clampF(add02.y + add2.y, 0.0f, 255.0f),\n                        clampF(add03.y + add2.y, 0.0f, 255.0f));\n\n        // R\n        *((float4 *)&pDstBlock[(planeStride << 1) + rowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add00.x + add3.x, 0.0f, 255.0f),\n                        clampF(add01.x + add3.x, 0.0f, 255.0f),\n                        clampF(add00.y + add3.y, 0.0f, 255.0f),\n                        clampF(add01.y + add3.y, 0.0f, 255.0f));\n        *((float4 *)&pDstBlock[(planeStride << 1) + nextRowStride + (threadIdx.x << 2)]) =\n            make_float4(clampF(add02.x + add3.x, 0.0f, 255.0f),\n                        clampF(add03.x + add3.x, 0.0f, 255.0f),\n                        clampF(add02.y + add3.y, 0.0f, 255.0f),\n                        clampF(add03.y + add3.y, 0.0f, 255.0f));\n    }\n}\n\nvoid nv12ToBGRplanarBatch(uint8_t     *pNv12,\n                          int          nNv12Pitch,\n                          float       *pBgr,\n                          int          nRgbPitch,\n                          int          nWidth,\n                          int          nHeight,\n                          int          nBatchSize,\n                          hipStream_t stream)\n{\n    dim3 threads(CONV_THREADS_X, CONV_THREADS_Y);\n\n    size_t blockDimZ = nBatchSize;\n\n    // Restricting blocks in Z-dim till 32 to not launch too many blocks\n    blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n\n    dim3 blocks((nWidth / 4 - 1) / threads.x + 1, (nHeight / 2 - 1) / threads.y + 1, blockDimZ);\n    nv12ToBGRplanarBatchKernel<<<blocks, threads, 0, stream>>>(\n        pNv12, nNv12Pitch, pBgr, nRgbPitch, nWidth, nHeight, nBatchSize);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/NV12toBGRandResize/nv12_to_bgr_planar.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <fstream>\n#include <iostream>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include \"resize_convert.h\"\n#include \"utils.h\"\n\n__global__ void floatToChar(float *src, unsigned char *dst, int height, int width, int batchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (x >= height * width)\n        return;\n\n    int offset = height * width * 3;\n\n    for (int j = 0; j < batchSize; j++) {\n        // b\n        *(dst + j * offset + x * 3 + 0) = (unsigned char)*(src + j * offset + height * width * 0 + x);\n        // g\n        *(dst + j * offset + x * 3 + 1) = (unsigned char)*(src + j * offset + height * width * 1 + x);\n        // r\n        *(dst + j * offset + x * 3 + 2) = (unsigned char)*(src + j * offset + height * width * 2 + x);\n    }\n}\n\nvoid floatPlanarToChar(float *src, unsigned char *dst, int height, int width, int batchSize)\n{\n    floatToChar<<<(height * width - 1) / 1024 + 1, 1024, 0, NULL>>>(src, dst, height, width, batchSize);\n}\n\nvoid dumpRawBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)\n{\n    float *bgr, *d_bgr;\n    int    frameSize;\n    char   directory[120];\n    char   mkdir_cmd[256];\n#if !defined(_WIN32)\n    sprintf(directory, \"output/%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir -p %s 2> /dev/null\", directory);\n#else\n    sprintf(directory, \"output\\\\%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir %s 2> nul\", directory);\n#endif\n\n    int ret = system(mkdir_cmd);\n\n    frameSize = width * height * 3 * sizeof(float);\n    bgr       = (float *)malloc(frameSize);\n    if (bgr == NULL) {\n        std::cerr << \"Failed malloc for bgr\\n\";\n        return;\n    }\n\n    d_bgr = d_srcBGR;\n    for (int i = 0; i < batchSize; i++) {\n        char           filename[256];\n        std::ofstream *outputFile;\n\n        checkCudaErrors(cudaMemcpy((void *)bgr, (void *)d_bgr, frameSize, cudaMemcpyDeviceToHost));\n        snprintf(filename, sizeof(filename), \"%s/%s_%d.raw\", directory, tag, (i + 1));\n\n        outputFile = new std::ofstream(filename);\n        if (outputFile) {\n            outputFile->write((char *)bgr, frameSize);\n            delete outputFile;\n        }\n\n        d_bgr += pitch * height * 3;\n    }\n\n    free(bgr);\n}\n\nvoid dumpBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)\n{\n    dumpRawBGR(d_srcBGR, pitch, width, height, batchSize, folder, tag);\n}\n\nvoid dumpYUV(unsigned char *d_nv12, int size, char *folder, char *tag)\n{\n    unsigned char *nv12Data;\n    std::ofstream *nv12File;\n    char           filename[256];\n    char           directory[60];\n    char           mkdir_cmd[256];\n#if !defined(_WIN32)\n    sprintf(directory, \"output/%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir -p %s 2> /dev/null\", directory);\n#else\n    sprintf(directory, \"output\\\\%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir %s 2> nul\", directory);\n#endif\n\n    int ret = system(mkdir_cmd);\n\n    snprintf(filename, sizeof(filename), \"%s/%s.nv12\", directory, tag);\n\n    nv12File = new std::ofstream(filename);\n    if (nv12File == NULL) {\n        std::cerr << \"Failed to new \" << filename;\n        return;\n    }\n\n    nv12Data = (unsigned char *)malloc(size * (sizeof(char)));\n    if (nv12Data == NULL) {\n        std::cerr << \"Failed to allcoate memory\\n\";\n        return;\n    }\n\n    cudaMemcpy((void *)nv12Data, (void *)d_nv12, size, cudaMemcpyDeviceToHost);\n\n    nv12File->write((const char *)nv12Data, size);\n\n    free(nv12Data);\n    delete nv12File;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime.h>\n#include <fstream>\n#include <iostream>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include \"resize_convert.h\"\n#include \"utils.h\"\n\n__global__ void floatToChar(float *src, unsigned char *dst, int height, int width, int batchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (x >= height * width)\n        return;\n\n    int offset = height * width * 3;\n\n    for (int j = 0; j < batchSize; j++) {\n        // b\n        *(dst + j * offset + x * 3 + 0) = (unsigned char)*(src + j * offset + height * width * 0 + x);\n        // g\n        *(dst + j * offset + x * 3 + 1) = (unsigned char)*(src + j * offset + height * width * 1 + x);\n        // r\n        *(dst + j * offset + x * 3 + 2) = (unsigned char)*(src + j * offset + height * width * 2 + x);\n    }\n}\n\nvoid floatPlanarToChar(float *src, unsigned char *dst, int height, int width, int batchSize)\n{\n    floatToChar<<<(height * width - 1) / 1024 + 1, 1024, 0, NULL>>>(src, dst, height, width, batchSize);\n}\n\nvoid dumpRawBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)\n{\n    float *bgr, *d_bgr;\n    int    frameSize;\n    char   directory[120];\n    char   mkdir_cmd[256];\n#if !defined(_WIN32)\n    sprintf(directory, \"output/%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir -p %s 2> /dev/null\", directory);\n#else\n    sprintf(directory, \"output\\\\%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir %s 2> nul\", directory);\n#endif\n\n    int ret = system(mkdir_cmd);\n\n    frameSize = width * height * 3 * sizeof(float);\n    bgr       = (float *)malloc(frameSize);\n    if (bgr == NULL) {\n        std::cerr << \"Failed malloc for bgr\\n\";\n        return;\n    }\n\n    d_bgr = d_srcBGR;\n    for (int i = 0; i < batchSize; i++) {\n        char           filename[256];\n        std::ofstream *outputFile;\n\n        checkCudaErrors(hipMemcpy((void *)bgr, (void *)d_bgr, frameSize, hipMemcpyDeviceToHost));\n        snprintf(filename, sizeof(filename), \"%s/%s_%d.raw\", directory, tag, (i + 1));\n\n        outputFile = new std::ofstream(filename);\n        if (outputFile) {\n            outputFile->write((char *)bgr, frameSize);\n            delete outputFile;\n        }\n\n        d_bgr += pitch * height * 3;\n    }\n\n    free(bgr);\n}\n\nvoid dumpBGR(float *d_srcBGR, int pitch, int width, int height, int batchSize, char *folder, char *tag)\n{\n    dumpRawBGR(d_srcBGR, pitch, width, height, batchSize, folder, tag);\n}\n\nvoid dumpYUV(unsigned char *d_nv12, int size, char *folder, char *tag)\n{\n    unsigned char *nv12Data;\n    std::ofstream *nv12File;\n    char           filename[256];\n    char           directory[60];\n    char           mkdir_cmd[256];\n#if !defined(_WIN32)\n    sprintf(directory, \"output/%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir -p %s 2> /dev/null\", directory);\n#else\n    sprintf(directory, \"output\\\\%s\", folder);\n    sprintf(mkdir_cmd, \"mkdir %s 2> nul\", directory);\n#endif\n\n    int ret = system(mkdir_cmd);\n\n    snprintf(filename, sizeof(filename), \"%s/%s.nv12\", directory, tag);\n\n    nv12File = new std::ofstream(filename);\n    if (nv12File == NULL) {\n        std::cerr << \"Failed to new \" << filename;\n        return;\n    }\n\n    nv12Data = (unsigned char *)malloc(size * (sizeof(char)));\n    if (nv12Data == NULL) {\n        std::cerr << \"Failed to allcoate memory\\n\";\n        return;\n    }\n\n    hipMemcpy((void *)nv12Data, (void *)d_nv12, size, hipMemcpyDeviceToHost);\n\n    nv12File->write((const char *)nv12Data, size);\n\n    free(nv12Data);\n    delete nv12File;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/NV12toBGRandResize/utils.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Implements interlace NV12 frames batch resize\n\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#include \"resize_convert.h\"\n\n__global__ static void resizeNV12BatchKernel(cudaTextureObject_t texSrcLuma,\n                                             cudaTextureObject_t texSrcChroma,\n                                             uint8_t            *pDstNv12,\n                                             int                 nSrcWidth,\n                                             int                 nSrcHeight,\n                                             int                 nDstPitch,\n                                             int                 nDstWidth,\n                                             int                 nDstHeight,\n                                             int                 nBatchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    int px = x * 2, py = y * 2;\n\n    if ((px + 1) >= nDstWidth || (py + 1) >= nDstHeight)\n        return;\n\n    float fxScale = 1.0f * nSrcWidth / nDstWidth;\n    float fyScale = 1.0f * nSrcHeight / nDstHeight;\n\n    uint8_t *p            = pDstNv12 + px + py * nDstPitch;\n    int      hh           = nDstHeight * 3 / 2;\n    int      nByte        = nDstPitch * hh;\n    int      px_fxScale   = px * fxScale;\n    int      px_fxScale_1 = (px + 1) * fxScale;\n    int      py_fyScale   = py * fyScale;\n    int      py_fyScale_1 = (py + 1) * fyScale;\n\n    for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {\n        *(uchar2 *)p               = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale),\n                                   tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale));\n        *(uchar2 *)(p + nDstPitch) = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale_1),\n                                                 tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale_1));\n        *(uchar2 *)(p + (nDstHeight - y) * nDstPitch) =\n            tex2D<uchar2>(texSrcChroma, x * fxScale, (hh * i + nDstHeight + y) * fyScale);\n        p += nByte;\n        py += hh;\n    }\n}\n\nvoid resizeNV12Batch(uint8_t     *dpSrc,\n                     int          nSrcPitch,\n                     int          nSrcWidth,\n                     int          nSrcHeight,\n                     uint8_t     *dpDst,\n                     int          nDstPitch,\n                     int          nDstWidth,\n                     int          nDstHeight,\n                     int          nBatchSize,\n                     cudaStream_t stream)\n{\n    int              hhSrc           = ceilf(nSrcHeight * 3.0f / 2.0f);\n    cudaResourceDesc resDesc         = {};\n    resDesc.resType                  = cudaResourceTypePitch2D;\n    resDesc.res.pitch2D.devPtr       = dpSrc;\n    resDesc.res.pitch2D.desc         = cudaCreateChannelDesc<uint8_t>();\n    resDesc.res.pitch2D.width        = nSrcWidth;\n    resDesc.res.pitch2D.height       = hhSrc * nBatchSize;\n    resDesc.res.pitch2D.pitchInBytes = nSrcPitch;\n\n    cudaTextureDesc texDesc = {};\n    texDesc.filterMode      = cudaFilterModePoint;\n    texDesc.readMode        = cudaReadModeElementType;\n\n    cudaTextureObject_t texLuma = 0;\n    checkCudaErrors(cudaCreateTextureObject(&texLuma, &resDesc, &texDesc, NULL));\n\n    resDesc.res.pitch2D.desc = cudaCreateChannelDesc<uchar2>();\n    resDesc.res.pitch2D.width /= 2;\n\n    cudaTextureObject_t texChroma = 0;\n    checkCudaErrors(cudaCreateTextureObject(&texChroma, &resDesc, &texDesc, NULL));\n\n    dim3 block(32, 32, 1);\n\n    size_t blockDimZ = nBatchSize;\n\n    // Restricting blocks in Z-dim till 32 to not launch too many blocks\n    blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n\n    dim3 grid((nDstWidth / 2 + block.x) / block.x, (nDstHeight / 2 + block.y) / block.y, blockDimZ);\n    resizeNV12BatchKernel<<<grid, block, 0, stream>>>(\n        texLuma, texChroma, dpDst, nSrcWidth, nSrcHeight, nDstPitch, nDstWidth, nDstHeight, nBatchSize);\n\n    checkCudaErrors(cudaDestroyTextureObject(texLuma));\n    checkCudaErrors(cudaDestroyTextureObject(texChroma));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Implements interlace NV12 frames batch resize\n\n#include <hip/hip_runtime.h>\n\n#include \"resize_convert.h\"\n\n__global__ static void resizeNV12BatchKernel(hipTextureObject_t texSrcLuma,\n                                             hipTextureObject_t texSrcChroma,\n                                             uint8_t            *pDstNv12,\n                                             int                 nSrcWidth,\n                                             int                 nSrcHeight,\n                                             int                 nDstPitch,\n                                             int                 nDstWidth,\n                                             int                 nDstHeight,\n                                             int                 nBatchSize)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    int px = x * 2, py = y * 2;\n\n    if ((px + 1) >= nDstWidth || (py + 1) >= nDstHeight)\n        return;\n\n    float fxScale = 1.0f * nSrcWidth / nDstWidth;\n    float fyScale = 1.0f * nSrcHeight / nDstHeight;\n\n    uint8_t *p            = pDstNv12 + px + py * nDstPitch;\n    int      hh           = nDstHeight * 3 / 2;\n    int      nByte        = nDstPitch * hh;\n    int      px_fxScale   = px * fxScale;\n    int      px_fxScale_1 = (px + 1) * fxScale;\n    int      py_fyScale   = py * fyScale;\n    int      py_fyScale_1 = (py + 1) * fyScale;\n\n    for (int i = blockIdx.z; i < nBatchSize; i += gridDim.z) {\n        *(uchar2 *)p               = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale),\n                                   tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale));\n        *(uchar2 *)(p + nDstPitch) = make_uchar2(tex2D<uint8_t>(texSrcLuma, px_fxScale, py_fyScale_1),\n                                                 tex2D<uint8_t>(texSrcLuma, px_fxScale_1, py_fyScale_1));\n        *(uchar2 *)(p + (nDstHeight - y) * nDstPitch) =\n            tex2D<uchar2>(texSrcChroma, x * fxScale, (hh * i + nDstHeight + y) * fyScale);\n        p += nByte;\n        py += hh;\n    }\n}\n\nvoid resizeNV12Batch(uint8_t     *dpSrc,\n                     int          nSrcPitch,\n                     int          nSrcWidth,\n                     int          nSrcHeight,\n                     uint8_t     *dpDst,\n                     int          nDstPitch,\n                     int          nDstWidth,\n                     int          nDstHeight,\n                     int          nBatchSize,\n                     hipStream_t stream)\n{\n    int              hhSrc           = ceilf(nSrcHeight * 3.0f / 2.0f);\n    hipResourceDesc resDesc         = {};\n    resDesc.resType                  = hipResourceTypePitch2D;\n    resDesc.res.pitch2D.devPtr       = dpSrc;\n    resDesc.res.pitch2D.desc         = hipCreateChannelDesc<uint8_t>();\n    resDesc.res.pitch2D.width        = nSrcWidth;\n    resDesc.res.pitch2D.height       = hhSrc * nBatchSize;\n    resDesc.res.pitch2D.pitchInBytes = nSrcPitch;\n\n    hipTextureDesc texDesc = {};\n    texDesc.filterMode      = hipFilterModePoint;\n    texDesc.readMode        = hipReadModeElementType;\n\n    hipTextureObject_t texLuma = 0;\n    checkCudaErrors(hipCreateTextureObject(&texLuma, &resDesc, &texDesc, NULL));\n\n    resDesc.res.pitch2D.desc = hipCreateChannelDesc<uchar2>();\n    resDesc.res.pitch2D.width /= 2;\n\n    hipTextureObject_t texChroma = 0;\n    checkCudaErrors(hipCreateTextureObject(&texChroma, &resDesc, &texDesc, NULL));\n\n    dim3 block(32, 32, 1);\n\n    size_t blockDimZ = nBatchSize;\n\n    // Restricting blocks in Z-dim till 32 to not launch too many blocks\n    blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n\n    dim3 grid((nDstWidth / 2 + block.x) / block.x, (nDstHeight / 2 + block.y) / block.y, blockDimZ);\n    resizeNV12BatchKernel<<<grid, block, 0, stream>>>(\n        texLuma, texChroma, dpDst, nSrcWidth, nSrcHeight, nDstPitch, nDstWidth, nDstHeight, nBatchSize);\n\n    checkCudaErrors(hipDestroyTextureObject(texLuma));\n    checkCudaErrors(hipDestroyTextureObject(texChroma));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/NV12toBGRandResize/nv12_resize.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Implements BGR 3 progressive planars frames batch resize\n\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#include \"resize_convert.h\"\n\n__global__ void resizeBGRplanarBatchKernel(cudaTextureObject_t texSrc,\n                                           float              *pDst,\n                                           int                 nDstPitch,\n                                           int                 nDstHeight,\n                                           int                 nSrcHeight,\n                                           int                 batch,\n                                           float               scaleX,\n                                           float               scaleY,\n                                           int                 cropX,\n                                           int                 cropY,\n                                           int                 cropW,\n                                           int                 cropH)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    if (x >= (int)(cropW / scaleX) || y >= (int)(cropH / scaleY))\n        return;\n\n    int    frameSize = nDstPitch * nDstHeight;\n    float *p         = NULL;\n    for (int i = blockIdx.z; i < batch; i += gridDim.z) {\n#pragma unroll\n        for (int channel = 0; channel < 3; channel++) {\n            p  = pDst + i * 3 * frameSize + y * nDstPitch + x + channel * frameSize;\n            *p = tex2D<float>(texSrc, x * scaleX + cropX, ((3 * i + channel) * nSrcHeight + y * scaleY + cropY));\n        }\n    }\n}\n\n\nstatic void resizeBGRplanarBatchCore(float       *dpSrc,\n                                     int          nSrcPitch,\n                                     int          nSrcWidth,\n                                     int          nSrcHeight,\n                                     float       *dpDst,\n                                     int          nDstPitch,\n                                     int          nDstWidth,\n                                     int          nDstHeight,\n                                     int          nBatchSize,\n                                     cudaStream_t stream,\n                                     bool         whSameResizeRatio,\n                                     int          cropX,\n                                     int          cropY,\n                                     int          cropW,\n                                     int          cropH)\n{\n    cudaTextureObject_t texSrc[2];\n    int                 nTiles = 1, h, iTile;\n\n    h = nSrcHeight * 3 * nBatchSize;\n    while ((h + nTiles - 1) / nTiles > 65536)\n        nTiles++;\n\n    if (nTiles > 2)\n        return;\n\n    int batchTile     = nBatchSize / nTiles;\n    int batchTileLast = nBatchSize - batchTile * (nTiles - 1);\n\n    for (iTile = 0; iTile < nTiles; ++iTile) {\n        int    bs       = (iTile == nTiles - 1) ? batchTileLast : batchTile;\n        float *dpSrcNew = dpSrc + iTile * (batchTile * 3 * nSrcHeight * nSrcPitch);\n\n        cudaResourceDesc resDesc         = {};\n        resDesc.resType                  = cudaResourceTypePitch2D;\n        resDesc.res.pitch2D.devPtr       = dpSrcNew;\n        resDesc.res.pitch2D.desc         = cudaCreateChannelDesc<float>();\n        resDesc.res.pitch2D.width        = nSrcWidth;\n        resDesc.res.pitch2D.height       = bs * 3 * nSrcHeight;\n        resDesc.res.pitch2D.pitchInBytes = nSrcPitch * sizeof(float);\n        cudaTextureDesc texDesc          = {};\n        texDesc.filterMode               = cudaFilterModeLinear;\n        texDesc.readMode                 = cudaReadModeElementType;\n\n        checkCudaErrors(cudaCreateTextureObject(&texSrc[iTile], &resDesc, &texDesc, NULL));\n        float *dpDstNew = dpDst + iTile * (batchTile * 3 * nDstHeight * nDstPitch);\n\n        if (cropW == 0 || cropH == 0) {\n            cropX = 0;\n            cropY = 0;\n            cropW = nSrcWidth;\n            cropH = nSrcHeight;\n        }\n\n        float scaleX = (cropW * 1.0f / nDstWidth);\n        float scaleY = (cropH * 1.0f / nDstHeight);\n\n        if (whSameResizeRatio == true)\n            scaleX = scaleY = scaleX > scaleY ? scaleX : scaleY;\n        dim3 block(32, 32, 1);\n\n        size_t blockDimZ = bs;\n        // Restricting blocks in Z-dim till 32 to not launch too many blocks\n        blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n        dim3 grid((cropW * 1.0f / scaleX + block.x - 1) / block.x,\n                  (cropH * 1.0f / scaleY + block.y - 1) / block.y,\n                  blockDimZ);\n\n        resizeBGRplanarBatchKernel<<<grid, block, 0, stream>>>(\n            texSrc[iTile], dpDstNew, nDstPitch, nDstHeight, nSrcHeight, bs, scaleX, scaleY, cropX, cropY, cropW, cropH);\n    }\n\n    for (iTile = 0; iTile < nTiles; ++iTile)\n        checkCudaErrors(cudaDestroyTextureObject(texSrc[iTile]));\n}\n\nvoid resizeBGRplanarBatch(float       *dpSrc,\n                          int          nSrcPitch,\n                          int          nSrcWidth,\n                          int          nSrcHeight,\n                          float       *dpDst,\n                          int          nDstPitch,\n                          int          nDstWidth,\n                          int          nDstHeight,\n                          int          nBatchSize,\n                          cudaStream_t stream,\n                          int          cropX,\n                          int          cropY,\n                          int          cropW,\n                          int          cropH,\n                          bool         whSameResizeRatio)\n{\n    resizeBGRplanarBatchCore(dpSrc,\n                             nSrcPitch,\n                             nSrcWidth,\n                             nSrcHeight,\n                             dpDst,\n                             nDstPitch,\n                             nDstWidth,\n                             nDstHeight,\n                             nBatchSize,\n                             stream,\n                             whSameResizeRatio,\n                             cropX,\n                             cropY,\n                             cropW,\n                             cropH);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Implements BGR 3 progressive planars frames batch resize\n\n#include <hip/hip_runtime.h>\n\n#include \"resize_convert.h\"\n\n__global__ void resizeBGRplanarBatchKernel(hipTextureObject_t texSrc,\n                                           float              *pDst,\n                                           int                 nDstPitch,\n                                           int                 nDstHeight,\n                                           int                 nSrcHeight,\n                                           int                 batch,\n                                           float               scaleX,\n                                           float               scaleY,\n                                           int                 cropX,\n                                           int                 cropY,\n                                           int                 cropW,\n                                           int                 cropH)\n{\n    int x = threadIdx.x + blockIdx.x * blockDim.x;\n    int y = threadIdx.y + blockIdx.y * blockDim.y;\n\n    if (x >= (int)(cropW / scaleX) || y >= (int)(cropH / scaleY))\n        return;\n\n    int    frameSize = nDstPitch * nDstHeight;\n    float *p         = NULL;\n    for (int i = blockIdx.z; i < batch; i += gridDim.z) {\n#pragma unroll\n        for (int channel = 0; channel < 3; channel++) {\n            p  = pDst + i * 3 * frameSize + y * nDstPitch + x + channel * frameSize;\n            *p = tex2D<float>(texSrc, x * scaleX + cropX, ((3 * i + channel) * nSrcHeight + y * scaleY + cropY));\n        }\n    }\n}\n\n\nstatic void resizeBGRplanarBatchCore(float       *dpSrc,\n                                     int          nSrcPitch,\n                                     int          nSrcWidth,\n                                     int          nSrcHeight,\n                                     float       *dpDst,\n                                     int          nDstPitch,\n                                     int          nDstWidth,\n                                     int          nDstHeight,\n                                     int          nBatchSize,\n                                     hipStream_t stream,\n                                     bool         whSameResizeRatio,\n                                     int          cropX,\n                                     int          cropY,\n                                     int          cropW,\n                                     int          cropH)\n{\n    hipTextureObject_t texSrc[2];\n    int                 nTiles = 1, h, iTile;\n\n    h = nSrcHeight * 3 * nBatchSize;\n    while ((h + nTiles - 1) / nTiles > 65536)\n        nTiles++;\n\n    if (nTiles > 2)\n        return;\n\n    int batchTile     = nBatchSize / nTiles;\n    int batchTileLast = nBatchSize - batchTile * (nTiles - 1);\n\n    for (iTile = 0; iTile < nTiles; ++iTile) {\n        int    bs       = (iTile == nTiles - 1) ? batchTileLast : batchTile;\n        float *dpSrcNew = dpSrc + iTile * (batchTile * 3 * nSrcHeight * nSrcPitch);\n\n        hipResourceDesc resDesc         = {};\n        resDesc.resType                  = hipResourceTypePitch2D;\n        resDesc.res.pitch2D.devPtr       = dpSrcNew;\n        resDesc.res.pitch2D.desc         = hipCreateChannelDesc<float>();\n        resDesc.res.pitch2D.width        = nSrcWidth;\n        resDesc.res.pitch2D.height       = bs * 3 * nSrcHeight;\n        resDesc.res.pitch2D.pitchInBytes = nSrcPitch * sizeof(float);\n        hipTextureDesc texDesc          = {};\n        texDesc.filterMode               = hipFilterModeLinear;\n        texDesc.readMode                 = hipReadModeElementType;\n\n        checkCudaErrors(hipCreateTextureObject(&texSrc[iTile], &resDesc, &texDesc, NULL));\n        float *dpDstNew = dpDst + iTile * (batchTile * 3 * nDstHeight * nDstPitch);\n\n        if (cropW == 0 || cropH == 0) {\n            cropX = 0;\n            cropY = 0;\n            cropW = nSrcWidth;\n            cropH = nSrcHeight;\n        }\n\n        float scaleX = (cropW * 1.0f / nDstWidth);\n        float scaleY = (cropH * 1.0f / nDstHeight);\n\n        if (whSameResizeRatio == true)\n            scaleX = scaleY = scaleX > scaleY ? scaleX : scaleY;\n        dim3 block(32, 32, 1);\n\n        size_t blockDimZ = bs;\n        // Restricting blocks in Z-dim till 32 to not launch too many blocks\n        blockDimZ = (blockDimZ > 32) ? 32 : blockDimZ;\n        dim3 grid((cropW * 1.0f / scaleX + block.x - 1) / block.x,\n                  (cropH * 1.0f / scaleY + block.y - 1) / block.y,\n                  blockDimZ);\n\n        resizeBGRplanarBatchKernel<<<grid, block, 0, stream>>>(\n            texSrc[iTile], dpDstNew, nDstPitch, nDstHeight, nSrcHeight, bs, scaleX, scaleY, cropX, cropY, cropW, cropH);\n    }\n\n    for (iTile = 0; iTile < nTiles; ++iTile)\n        checkCudaErrors(hipDestroyTextureObject(texSrc[iTile]));\n}\n\nvoid resizeBGRplanarBatch(float       *dpSrc,\n                          int          nSrcPitch,\n                          int          nSrcWidth,\n                          int          nSrcHeight,\n                          float       *dpDst,\n                          int          nDstPitch,\n                          int          nDstWidth,\n                          int          nDstHeight,\n                          int          nBatchSize,\n                          hipStream_t stream,\n                          int          cropX,\n                          int          cropY,\n                          int          cropW,\n                          int          cropH,\n                          bool         whSameResizeRatio)\n{\n    resizeBGRplanarBatchCore(dpSrc,\n                             nSrcPitch,\n                             nSrcWidth,\n                             nSrcHeight,\n                             dpDst,\n                             nDstPitch,\n                             nDstWidth,\n                             nDstHeight,\n                             nBatchSize,\n                             stream,\n                             whSameResizeRatio,\n                             cropX,\n                             cropY,\n                             cropW,\n                             cropH);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/NV12toBGRandResize/bgr_resize.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef QUASIRANDOMGENERATOR_KERNEL_CUH\n#define QUASIRANDOMGENERATOR_KERNEL_CUH\n\n#include \"quasirandomGenerator_common.h\"\n\n// Fast integer multiplication\n#define MUL(a, b) __umul24(a, b)\n\n////////////////////////////////////////////////////////////////////////////////\n// Niederreiter quasirandom number generation kernel\n////////////////////////////////////////////////////////////////////////////////\n__constant__ unsigned int c_Table[QRNG_DIMENSIONS][QRNG_RESOLUTION];\n\nextern \"C\" __global__ void quasirandomGeneratorKernel(float *d_Output, unsigned int seed, unsigned int N)\n{\n    unsigned int *dimBase = &c_Table[threadIdx.y][0];\n    unsigned int  tid     = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int  threadN = MUL(blockDim.x, gridDim.x);\n\n    for (unsigned int pos = tid; pos < N; pos += threadN) {\n        unsigned int result = 0;\n        unsigned int data   = seed + pos;\n\n        for (int bit = 0; bit < QRNG_RESOLUTION; bit++, data >>= 1)\n            if (data & 1) {\n                result ^= dimBase[bit];\n            }\n\n        d_Output[MUL(threadIdx.y, N) + pos] = (float)(result + 1) * INT_SCALE;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Moro's Inverse Cumulative Normal Distribution function approximation\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float MoroInvCNDgpu(unsigned int x)\n{\n    const float a1 = 2.50662823884f;\n    const float a2 = -18.61500062529f;\n    const float a3 = 41.39119773534f;\n    const float a4 = -25.44106049637f;\n    const float b1 = -8.4735109309f;\n    const float b2 = 23.08336743743f;\n    const float b3 = -21.06224101826f;\n    const float b4 = 3.13082909833f;\n    const float c1 = 0.337475482272615f;\n    const float c2 = 0.976169019091719f;\n    const float c3 = 0.160797971491821f;\n    const float c4 = 2.76438810333863E-02f;\n    const float c5 = 3.8405729373609E-03f;\n    const float c6 = 3.951896511919E-04f;\n    const float c7 = 3.21767881768E-05f;\n    const float c8 = 2.888167364E-07f;\n    const float c9 = 3.960315187E-07f;\n\n    float z;\n\n    bool negate = false;\n\n    // Ensure the conversion to floating point will give a value in the\n    // range (0,0.5] by restricting the input to the bottom half of the\n    // input domain. We will later reflect the result if the input was\n    // originally in the top half of the input domain\n    if (x >= 0x80000000UL) {\n        x      = 0xffffffffUL - x;\n        negate = true;\n    }\n\n    // x is now in the range [0,0x80000000) (i.e. [0,0x7fffffff])\n    // Convert to floating point in (0,0.5]\n    const float x1 = 1.0f / static_cast<float>(0xffffffffUL);\n    const float x2 = x1 / 2.0f;\n    float       p1 = x * x1 + x2;\n    // Convert to floating point in (-0.5,0]\n    float p2 = p1 - 0.5f;\n\n    // The input to the Moro inversion is p2 which is in the range\n    // (-0.5,0]. This means that our output will be the negative side\n    // of the bell curve (which we will reflect if \"negate\" is true).\n\n    // Main body of the bell curve for |p| < 0.42\n    if (p2 > -0.42f) {\n        z = p2 * p2;\n        z = p2 * (((a4 * z + a3) * z + a2) * z + a1) / ((((b4 * z + b3) * z + b2) * z + b1) * z + 1.0f);\n    }\n    // Special case (Chebychev) for tail\n    else {\n        z = __logf(-__logf(p1));\n        z = -(c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * (c8 + z * c9))))))));\n    }\n\n    // If the original input (x) was in the top half of the range, reflect\n    // to get the positive side of the bell curve\n\n    return negate ? -z : z;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main kernel. Choose between transforming\n// input sequence and uniform ascending (0, 1) sequence\n////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" __global__ void inverseCNDKernel(float *d_Output, unsigned int pathN)\n{\n    unsigned int distance = ((unsigned int)-1) / (pathN + 1);\n    unsigned int tid      = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int threadN  = MUL(blockDim.x, gridDim.x);\n\n    // Transform input number sequence if it's supplied\n    if (0) // d_Input)\n    {\n        /*\n          for (unsigned int pos = tid; pos < pathN; pos += threadN)\n          {\n              unsigned int d = d_Input[pos];\n              d_Output[pos] = (float)MoroInvCNDgpu(d);\n          }\n          */\n    }\n    // Else generate input uniformly placed samples on the fly\n    // and write to destination\n    else {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = (pos + 1) * distance;\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n}\n\n#endif\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef QUASIRANDOMGENERATOR_KERNEL_CUH\n#define QUASIRANDOMGENERATOR_KERNEL_CUH\n\n#include \"quasirandomGenerator_common.h\"\n\n// Fast integer multiplication\n#define MUL(a, b) __umul24(a, b)\n\n////////////////////////////////////////////////////////////////////////////////\n// Niederreiter quasirandom number generation kernel\n////////////////////////////////////////////////////////////////////////////////\n__constant__ unsigned int c_Table[QRNG_DIMENSIONS][QRNG_RESOLUTION];\n\nextern \"C\" __global__ void quasirandomGeneratorKernel(float *d_Output, unsigned int seed, unsigned int N)\n{\n    unsigned int *dimBase = &c_Table[threadIdx.y][0];\n    unsigned int  tid     = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int  threadN = MUL(blockDim.x, gridDim.x);\n\n    for (unsigned int pos = tid; pos < N; pos += threadN) {\n        unsigned int result = 0;\n        unsigned int data   = seed + pos;\n\n        for (int bit = 0; bit < QRNG_RESOLUTION; bit++, data >>= 1)\n            if (data & 1) {\n                result ^= dimBase[bit];\n            }\n\n        d_Output[MUL(threadIdx.y, N) + pos] = (float)(result + 1) * INT_SCALE;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Moro's Inverse Cumulative Normal Distribution function approximation\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float MoroInvCNDgpu(unsigned int x)\n{\n    const float a1 = 2.50662823884f;\n    const float a2 = -18.61500062529f;\n    const float a3 = 41.39119773534f;\n    const float a4 = -25.44106049637f;\n    const float b1 = -8.4735109309f;\n    const float b2 = 23.08336743743f;\n    const float b3 = -21.06224101826f;\n    const float b4 = 3.13082909833f;\n    const float c1 = 0.337475482272615f;\n    const float c2 = 0.976169019091719f;\n    const float c3 = 0.160797971491821f;\n    const float c4 = 2.76438810333863E-02f;\n    const float c5 = 3.8405729373609E-03f;\n    const float c6 = 3.951896511919E-04f;\n    const float c7 = 3.21767881768E-05f;\n    const float c8 = 2.888167364E-07f;\n    const float c9 = 3.960315187E-07f;\n\n    float z;\n\n    bool negate = false;\n\n    // Ensure the conversion to floating point will give a value in the\n    // range (0,0.5] by restricting the input to the bottom half of the\n    // input domain. We will later reflect the result if the input was\n    // originally in the top half of the input domain\n    if (x >= 0x80000000UL) {\n        x      = 0xffffffffUL - x;\n        negate = true;\n    }\n\n    // x is now in the range [0,0x80000000) (i.e. [0,0x7fffffff])\n    // Convert to floating point in (0,0.5]\n    const float x1 = 1.0f / static_cast<float>(0xffffffffUL);\n    const float x2 = x1 / 2.0f;\n    float       p1 = x * x1 + x2;\n    // Convert to floating point in (-0.5,0]\n    float p2 = p1 - 0.5f;\n\n    // The input to the Moro inversion is p2 which is in the range\n    // (-0.5,0]. This means that our output will be the negative side\n    // of the bell curve (which we will reflect if \"negate\" is true).\n\n    // Main body of the bell curve for |p| < 0.42\n    if (p2 > -0.42f) {\n        z = p2 * p2;\n        z = p2 * (((a4 * z + a3) * z + a2) * z + a1) / ((((b4 * z + b3) * z + b2) * z + b1) * z + 1.0f);\n    }\n    // Special case (Chebychev) for tail\n    else {\n        z = __logf(-__logf(p1));\n        z = -(c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * (c8 + z * c9))))))));\n    }\n\n    // If the original input (x) was in the top half of the range, reflect\n    // to get the positive side of the bell curve\n\n    return negate ? -z : z;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main kernel. Choose between transforming\n// input sequence and uniform ascending (0, 1) sequence\n////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" __global__ void inverseCNDKernel(float *d_Output, unsigned int pathN)\n{\n    unsigned int distance = ((unsigned int)-1) / (pathN + 1);\n    unsigned int tid      = MUL(blockDim.x, blockIdx.x) + threadIdx.x;\n    unsigned int threadN  = MUL(blockDim.x, gridDim.x);\n\n    // Transform input number sequence if it's supplied\n    if (0) // d_Input)\n    {\n        /*\n          for (unsigned int pos = tid; pos < pathN; pos += threadN)\n          {\n              unsigned int d = d_Input[pos];\n              d_Output[pos] = (float)MoroInvCNDgpu(d);\n          }\n          */\n    }\n    // Else generate input uniformly placed samples on the fly\n    // and write to destination\n    else {\n        for (unsigned int pos = tid; pos < pathN; pos += threadN) {\n            unsigned int d = (pos + 1) * distance;\n            d_Output[pos]  = (float)MoroInvCNDgpu(d);\n        }\n    }\n}\n\n#endif\n", "source": "/kaggle/working/cuda-samples/Samples/5_Domain_Specific/quasirandomGenerator_nvrtc/quasirandomGenerator_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple interop app demonstrating EGLImage + EGLSync interop with CUDA.\n// Using EGLSync - CUDA Event interop one can achieve synchronization on GPU\n// itself for GL-EGL-CUDA operations instead of blocking CPU for\n// synchronization. This app requires GLES 3.2 or higher\n\n//---------------------------INCLUDES---------------------------------//\n#include <EGL/egl.h>\n#include <EGL/eglext.h>\n#include <GLES3/gl32.h>\n#include <cuda.h>\n#include <cudaEGL.h>\n#include <helper_cuda_drvapi.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include \"egl_common.h\"\n#include \"graphics_interface.h\"\n\n//---------------------------DEFINES---------------------------------//\n#define MAX_ITR 100\n\n#define FAILURE 0\n#define SUCCESS 1\n#define WAIVED  2\n\n#define BLOCK_SIZE 16\n\n#define GL_READ  0\n#define GL_WRITE 1\n//---------------------------MACROS---------------------------------//\n\n// Error-checking wrapper around GL calls\n#define GL_SAFE_CALL(call)                                                    \\\n    {                                                                         \\\n        GLenum err;                                                           \\\n        call;                                                                 \\\n        err = glGetError();                                                   \\\n        if (err != GL_NO_ERROR) {                                             \\\n            fprintf(stderr, \"%s:%d GL error: %d\\n\", __FILE__, __LINE__, err); \\\n            cleanup(FAILURE);                                                 \\\n        }                                                                     \\\n    }\n\n#define GL_SAFE_CALL_NO_CLEANUP(call, err)                                       \\\n    {                                                                            \\\n        GLenum status;                                                           \\\n        call;                                                                    \\\n        status = glGetError();                                                   \\\n        if (status != GL_NO_ERROR) {                                             \\\n            fprintf(stderr, \"%s:%d GL error: %d\\n\", __FILE__, __LINE__, status); \\\n            err = status;                                                        \\\n        }                                                                        \\\n    }\n\n// Error-checking wrapper around CUDA calls (taken from cutil.h)\n#define CUDA_SAFE_CALL(call)                                                                                        \\\n    do {                                                                                                            \\\n        cudaError err = call;                                                                                       \\\n        if (cudaSuccess != err) {                                                                                   \\\n            fprintf(                                                                                                \\\n                stderr, \"Cuda error in file '%s' in line %i : %s.\\n\", __FILE__, __LINE__, cudaGetErrorString(err)); \\\n            cleanup(FAILURE);                                                                                       \\\n        }                                                                                                           \\\n    } while (0)\n\n#define CUDA_SAFE_CALL_NO_CLEANUP(call, err)                                                                           \\\n    do {                                                                                                               \\\n        cudaError status = call;                                                                                       \\\n        if (cudaSuccess != status) {                                                                                   \\\n            fprintf(                                                                                                   \\\n                stderr, \"Cuda error in file '%s' in line %i : %s.\\n\", __FILE__, __LINE__, cudaGetErrorString(status)); \\\n            err = status;                                                                                              \\\n        }                                                                                                              \\\n    } while (0)\n\n#if defined(EXTENSION_LIST)\nEXTENSION_LIST(EXTLST_DECL)\ntypedef void (*extlst_fnptr_t)(void);\nstatic struct\n{\n    extlst_fnptr_t *fnptr;\n    char const     *name;\n} extensionList[] = {EXTENSION_LIST(EXTLST_ENTRY)};\n\nint eglSetupExtensions(void)\n{\n    unsigned int i;\n\n    for (i = 0; i < (sizeof(extensionList) / sizeof(*extensionList)); i++) {\n        *extensionList[i].fnptr = eglGetProcAddress(extensionList[i].name);\n        if (*extensionList[i].fnptr == NULL) {\n            printf(\"Couldn't get address of %s()\\n\", extensionList[i].name);\n            return 0;\n        }\n    }\n\n    return 1;\n}\n#endif\n\n#if defined(EXTENSION_LIST)\nEXTENSION_LIST(EXTLST_EXTERN)\n#endif\n\n//------------------------GLOBAL VARIABLES--------------------------//\n\n// GL texture\nGLuint tex[2] = {0};\n\n// Used to catch unexpected termination from GLUT\nint cleanExit = 0;\n\n// Use CPU Sync or GPU sync; Default GPU\nint useGpu = 1;\n\n// CUDA Resource\nCUgraphicsResource writeResource = NULL;\nCUgraphicsResource readResource  = NULL;\nCUarray            writeArray, readArray;\nCUdevice           device;\nCUcontext          context;\n\n// Which device to run on\nunsigned int dev = 0;\n\n// Default width, height, and iterations value\nint width  = 2048;\nint height = 2048;\nint itr    = MAX_ITR;\n\n// Error check variable\n__device__ static unsigned int numErrors = 0;\n\n//-----------------------FUNCTION PROTOTYPES------------------------//\n\nvoid checkSync(int argc, char **argv);\nint  parseCmdLine(int argc, char **argv);\nvoid printUsage(void);\nvoid cleanup(int status);\nvoid exitHandler(void);\nvoid printStatus(int status);\nvoid checkSyncOnCPU(void);\nvoid checkSyncOnGPU(EGLDisplay dpy);\n\n__global__ void\nverify_and_update_kernel(CUsurfObject write, CUsurfObject read, char expected, char newval, int width, int height);\nextern \"C\" cudaError_t cudaGetValueMismatch();\n\n//-----------------------FUNCTION DEFINITIONS------------------------//\n\nint main(int argc, char *argv[])\n{\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    parseCmdLine(argc, argv);\n    atexit(exitHandler);\n\n    checkSync(argc, argv);\n    return 0;\n}\n\nint parseCmdLine(int argc, char **argv)\n{\n    int i;\n    for (i = 1; i < argc; i++) {\n        if (strcmp(argv[i], \"-cpu\") == 0) {\n            useGpu = 0;\n        }\n\n        if (strcmp(argv[i], \"-h\") == 0) {\n            printUsage();\n            cleanup(SUCCESS);\n        }\n\n        if (strcmp(argv[i], \"-width\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"width option must be followed by value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &width) != 1) {\n                printf(\"Error: invalid width value\\n\");\n                return FAILURE;\n            }\n        }\n\n        if (strcmp(argv[i], \"-height\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"height option must be followed by value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &height) != 1) {\n                printf(\"Error: invalid height value\\n\");\n                return FAILURE;\n            }\n        }\n        if (strcmp(argv[i], \"-itr\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"itr option must be followed by iteration value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &itr) != 1) {\n                printf(\"Error: invalid iteration value\\n\");\n                return FAILURE;\n            }\n        }\n    }\n\n    return SUCCESS;\n}\n\nvoid printUsage(void)\n{\n    printf(\"Usage:\\n\");\n    printf(\"\\t-h\\tPrint command line options\\n\");\n    printf(\"\\t-cpu\\tSync on the CPU instead of the GPU\\n\");\n    printf(\"\\t-width w\\tSet the width to w\\n\");\n    printf(\"\\t-height h\\tSet the height to h\\n\");\n    printf(\"\\t-itr i\\tSet number of iterations to i\\n\");\n}\n\nvoid checkSync(int argc, char **argv)\n{\n    int            x, y;\n    int            bufferSize = width * height * 4;\n    unsigned char *pSurf_read = NULL, *pSurf_write = NULL;\n    int            integrated;\n\n    CUresult          status          = CUDA_SUCCESS;\n    CUctxCreateParams ctxCreateParams = {};\n\n    // Init values for variables\n    x = y = 0;\n\n    if (CUDA_SUCCESS != (status = cuInit(0))) {\n        printf(\"Failed to initialize CUDA\\n\");\n    }\n    device = findCudaDeviceDRV(argc, (const char **)argv);\n\n    if (CUDA_SUCCESS != (status = cuCtxCreate(&context, &ctxCreateParams, 0, device))) {\n        printf(\"failed to create CUDA context\\n\");\n    }\n    cuCtxPushCurrent(context);\n\n    status = cuDeviceGetAttribute(&integrated, CU_DEVICE_ATTRIBUTE_INTEGRATED, device);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Failed to get device attribute CU_DEVICE_ATTRIBUTE_INTEGRATED\\n\");\n        cleanup(FAILURE);\n    }\n\n    if (integrated != 1) {\n        printf(\"EGLSync_CUDAEvent_Interop does not support dGPU. Waiving sample.\\n\");\n        cleanup(WAIVED);\n    }\n\n#if (defined(__arm__) || defined(__aarch64__)) && defined(__linux__)\n    graphics_setup_window(0, 0, width, height, \"EGLSync_CUDA_Interop\");\n#endif\n\n    pSurf_read  = (unsigned char *)malloc(bufferSize);\n    pSurf_write = (unsigned char *)malloc(bufferSize);\n    if (pSurf_read == NULL || pSurf_write == NULL) {\n        printf(\"malloc failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    for (x = 0; x < width; x++) {\n        for (y = 0; y < height; y++) {\n            pSurf_read[(y * width + x) * 4]      = 1;\n            pSurf_read[(y * width + x) * 4 + 1]  = 1;\n            pSurf_read[(y * width + x) * 4 + 2]  = 1;\n            pSurf_read[(y * width + x) * 4 + 3]  = 1;\n            pSurf_write[(y * width + x) * 4]     = 0;\n            pSurf_write[(y * width + x) * 4 + 1] = 0;\n            pSurf_write[(y * width + x) * 4 + 2] = 0;\n            pSurf_write[(y * width + x) * 4 + 3] = 0;\n        }\n    }\n\n    // NOP call to error-check the above glut calls\n    GL_SAFE_CALL({});\n\n    // Init texture\n    GL_SAFE_CALL(glGenTextures(2, tex));\n\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, tex[GL_READ]));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pSurf_read));\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, 0));\n\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, tex[GL_WRITE]));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pSurf_write));\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, 0));\n\n    glFinish();\n\n    EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();\n    if (eglDisplayHandle == EGL_NO_DISPLAY) {\n        printf(\"eglDisplayHandle failed \\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"eglDisplay Handle created \\n\");\n    }\n\n    if (!eglSetupExtensions()) {\n        printf(\"SetupExtentions failed \\n\");\n        cleanup(FAILURE);\n    }\n\n    EGLContext eglCtx = eglGetCurrentContext();\n    if (eglCtx == EGL_NO_CONTEXT) {\n        printf(\"Context1 create failed with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n\n    // Create the EGL_Image\n    EGLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE};\n\n    EGLImageKHR eglImage1 = eglCreateImageKHR(\n        eglDisplayHandle, eglCtx, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(intptr_t)tex[GL_READ], eglImgAttrs);\n    if (eglImage1 == EGL_NO_IMAGE_KHR) {\n        printf(\"EGLImage create failed for read texture with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"EGLImage1 created \\n\");\n    }\n\n    EGLImageKHR eglImage2 = eglCreateImageKHR(\n        eglDisplayHandle, eglCtx, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(intptr_t)tex[GL_WRITE], eglImgAttrs);\n    if (eglImage2 == EGL_NO_IMAGE_KHR) {\n        printf(\"EGLImage create failed for write texture with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"EGLImage2 created \\n\");\n    }\n\n    glFinish();\n\n    status = cuGraphicsEGLRegisterImage(&writeResource, eglImage1, CU_GRAPHICS_REGISTER_FLAGS_NONE);\n    if (status != CUDA_SUCCESS) {\n        printf(\"cuGraphicsEGLRegisterImage failed with Texture 1\\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"cuGraphicsEGLRegisterImage Passed, writeResource created with texture \"\n               \"1\\n\");\n    }\n\n    status = cuGraphicsSubResourceGetMappedArray(&writeArray, writeResource, 0, 0);\n    if (status != CUDA_SUCCESS) {\n        printf(\"cuGraphicsSubResourceGetMappedArray failed for writeResource with \"\n               \"texture 1\\n\");\n        cleanup(FAILURE);\n    }\n\n    status = cuGraphicsEGLRegisterImage(&readResource, eglImage2, CU_GRAPHICS_REGISTER_FLAGS_NONE);\n    if (status != CUDA_SUCCESS) {\n        printf(\"cuGraphicsEGLRegisterImage failed for readResource with Texture 2\\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"cuGraphicsEGLRegisterImage Passed, readResource created with texture \"\n               \"2\\n\");\n    }\n\n    status = cuGraphicsSubResourceGetMappedArray(&readArray, readResource, 0, 0);\n    if (status != CUDA_SUCCESS) {\n        printf(\"cuGraphicsSubResourceGetMappedArray failed for texture 2\\n\");\n        cleanup(FAILURE);\n    }\n\n    if (useGpu) {\n        printf(\"Using GPU Sync path\\n\");\n        checkSyncOnGPU(eglDisplayHandle);\n    }\n    else {\n        printf(\"Using CPU Sync path\\n\");\n        checkSyncOnCPU();\n    }\n\n    free(pSurf_read);\n    free(pSurf_write);\n    cleanup(SUCCESS);\n}\n\nvoid checkSyncOnCPU(void)\n{\n    int                z = 0;\n    unsigned char      expectedData, newData;\n    CUresult           status = CUDA_SUCCESS;\n    CUDA_RESOURCE_DESC wdsc, rdsc;\n    memset(&wdsc, 0, sizeof(wdsc));\n    memset(&rdsc, 0, sizeof(rdsc));\n\n    expectedData = 0;\n    newData      = 1;\n\n    wdsc.resType          = CU_RESOURCE_TYPE_ARRAY;\n    wdsc.res.array.hArray = writeArray;\n    CUsurfObject writeSurface;\n    rdsc.resType          = CU_RESOURCE_TYPE_ARRAY;\n    rdsc.res.array.hArray = readArray;\n    CUsurfObject readSurface;\n\n    status = cuSurfObjectCreate(&writeSurface, &wdsc);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Surface bounding failed with status %d\\n\", status);\n        cleanup(FAILURE);\n    }\n    status = cuSurfObjectCreate(&readSurface, &rdsc);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Surface bounding failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    for (z = 0; z < itr; z++) {\n        // GL call to copy from read texture to write texture\n        GL_SAFE_CALL(glCopyImageSubData(\n            tex[GL_READ], GL_TEXTURE_2D, 0, 0, 0, 0, tex[GL_WRITE], GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1));\n\n        glFinish();\n\n        newData++;\n        expectedData++;\n\n        verify_and_update_kernel<<<(width * height) / 256, 256>>>(\n            writeSurface, readSurface, expectedData, newData, width, height);\n\n        status = cuCtxSynchronize();\n        if (status != CUDA_SUCCESS) {\n            printf(\"cuCtxSynchronize failed \\n\");\n        }\n    }\n\n    cudaError_t err = cudaGetValueMismatch();\n    if (err != cudaSuccess) {\n        printf(\"Value mismatch seen when using CPU sync\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Clean up CUDA writeResource\n    status = cuGraphicsUnregisterResource(writeResource);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered writeResource. \\n\");\n    }\n\n    // Clean up CUDA readResource\n    status = cuGraphicsUnregisterResource(readResource);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered readResource. \\n\");\n    }\n}\n\n/*\n    Performs same function as checkSyncOnCPU\n    Here instead of glFinish() and cuCtxSynchronize like in checkSyncOnCPU,\n    we make use of EGLSync, CUDA Event and cuStreamWaitEvent, eglWaitSyncKHR to\n   achieve the synchronization due to this CPU is not blocked for any\n   synchronization needed between GL-EGL & CUDA operations all synchronizations\n   happens on the GPU only.\n*/\nvoid checkSyncOnGPU(EGLDisplay dpy)\n{\n    int                z = 0;\n    unsigned char      expectedData, newData;\n    cudaError_t        err;\n    CUresult           status = CUDA_SUCCESS;\n    CUstream           stream;\n    CUevent            timingDisabledEvent;\n    CUDA_RESOURCE_DESC wdsc, rdsc;\n    memset(&wdsc, 0, sizeof(wdsc));\n    memset(&rdsc, 0, sizeof(rdsc));\n\n    expectedData = 0;\n    newData      = 1;\n\n    wdsc.resType          = CU_RESOURCE_TYPE_ARRAY;\n    wdsc.res.array.hArray = writeArray;\n    CUsurfObject writeSurface;\n    rdsc.resType          = CU_RESOURCE_TYPE_ARRAY;\n    rdsc.res.array.hArray = readArray;\n    CUsurfObject readSurface;\n\n    status = cuSurfObjectCreate(&writeSurface, &wdsc);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Surface bounding failed with status %d\\n\", status);\n        cleanup(FAILURE);\n    }\n    status = cuSurfObjectCreate(&readSurface, &rdsc);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Surface bounding failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    status = cuStreamCreate(&stream, CU_STREAM_DEFAULT);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Stream creation failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Creates timing disabled event which uses non-blocking synchronization\n    status = cuEventCreate(&timingDisabledEvent, CU_EVENT_DISABLE_TIMING);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Default event creation failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    /*\n        1. We perform texture-to-texture copy in GLES which is async function\n        2. Followed by creating EGLSync and a CUDA Event from that EGLSync object\n        3. Using cuStreamWaitEvent() we wait in GPU for the GLES to finish texture\n       copy.\n        4. CUDA kernel verfiy_and_update_kernel verifies if the copied data by\n       GLES is correct, and it updates the buffer with new values.\n        5. This is followed by eglWaitSyncKHR() which waits for the cuda kernel to\n       finish, so that in the next iteration GLES can perform the copying of the\n       updated buffer to write texture,\n    */\n    for (z = 0; z < itr; z++) {\n        // GL call to copy from read texture to write texture\n        GL_SAFE_CALL(glCopyImageSubData(\n            tex[GL_READ], GL_TEXTURE_2D, 0, 0, 0, 0, tex[GL_WRITE], GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1));\n\n        EGLSyncKHR   eglSyncForGL, eglSyncForCuda;\n        EGLBoolean   egl_status  = EGL_TRUE;\n        EGLAttribKHR eglattrib[] = {EGL_CUDA_EVENT_HANDLE_NV, (EGLAttrib)timingDisabledEvent, EGL_NONE};\n\n        CUevent cudaEGLSyncEvent;\n\n        eglSyncForGL = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);\n\n        if (eglSyncForGL == EGL_NO_SYNC_KHR) {\n            printf(\" EGL Sync creation failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        status = cuEventCreateFromEGLSync(&cudaEGLSyncEvent, eglSyncForGL, CU_EVENT_DEFAULT);\n        if (status != CUDA_SUCCESS) {\n            printf(\"CUDA event creation from EGLSync failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        // We wait from CUDA in GPU for GL-EGL operation completion\n        status = cuStreamWaitEvent(stream, cudaEGLSyncEvent, 0);\n        if (status != CUDA_SUCCESS) {\n            printf(\"Stream wait for event created from EGLSync failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        egl_status = eglDestroySyncKHR(dpy, eglSyncForGL);\n        if (egl_status != EGL_TRUE) {\n            printf(\"EGL sync object destruction failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        newData++;\n        expectedData++;\n\n        // Verifies the values in readSurface which is copied by\n        // glCopyImageSubData() And writes value of newData into writeSurface\n        verify_and_update_kernel<<<(width * height) / 256, 256, 0, stream>>>(\n            writeSurface, readSurface, expectedData, newData, width, height);\n\n        status = cuEventDestroy(cudaEGLSyncEvent);\n        if (status != CUDA_SUCCESS) {\n            printf(\"Event Destroy failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        status = cuEventRecord(timingDisabledEvent, stream);\n        if (status != CUDA_SUCCESS) {\n            printf(\"Event Record failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        // creating an EGL sync object linked to a CUDA event object\n        eglSyncForCuda = eglCreateSync64KHR(dpy, EGL_SYNC_CUDA_EVENT_NV, eglattrib);\n\n        // We wait from EGL for CUDA operation completion\n        egl_status = eglWaitSyncKHR(dpy, eglSyncForCuda, 0);\n        if (egl_status != EGL_TRUE) {\n            printf(\"eglWaitSyncKHR failed\\n\");\n            cleanup(FAILURE);\n        }\n        egl_status = eglDestroySyncKHR(dpy, eglSyncForCuda);\n        if (egl_status != EGL_TRUE) {\n            printf(\"EGL sync object destruction failed\\n\");\n            cleanup(FAILURE);\n        }\n    }\n\n    err = cudaGetValueMismatch();\n    if (err != cudaSuccess) {\n        printf(\"Value mismatch seen when using GPU sync\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Clean up CUDA writeResource\n    status = cuGraphicsUnregisterResource(writeResource);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered writeResource. \\n\");\n    }\n\n    // Clean up CUDA readResource\n    status = cuGraphicsUnregisterResource(readResource);\n    if (status != CUDA_SUCCESS) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered readResource. \\n\");\n    }\n}\n\n// Verifies the values in readSurface whether they are expected ones\n// And writes value of newData into writeSurface\n__global__ void\nverify_and_update_kernel(CUsurfObject write, CUsurfObject read, char expected, char newval, int width, int height)\n{\n    unsigned int x = blockDim.x * blockIdx.x + threadIdx.x;\n    unsigned int y = blockDim.y * blockIdx.y + threadIdx.y;\n\n    if (x < width && y < height) {\n        uchar4 check;\n        surf2Dread(&check, read, x * 4, y);\n        if (check.x != expected || check.y != expected || check.z != expected || check.w != expected) {\n            printf(\"Mismatch found in values read[0]= %u read[1]= %u read[2]= %u \"\n                   \"read[3]= %u expected is %u\\n\",\n                   check.x,\n                   check.y,\n                   check.z,\n                   check.w,\n                   expected);\n            numErrors++;\n            return;\n        }\n        uchar4 data = make_uchar4(newval, newval, newval, newval);\n        surf2Dwrite(data, write, x * 4, y);\n    }\n}\n\n__global__ void getNumErrors(int *numErr) { *numErr = numErrors; }\n\nextern \"C\" cudaError_t cudaGetValueMismatch()\n{\n    int         numErr_h;\n    int        *numErr_d = NULL;\n    cudaError_t err      = cudaSuccess;\n\n    err = cudaMalloc(&numErr_d, sizeof(int));\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaMemcpy failed with %s\\n\", cudaGetErrorString(err));\n        cudaFree(numErr_d);\n        return err;\n    }\n\n    getNumErrors<<<1, 1>>>(numErr_d);\n    err = cudaDeviceSynchronize();\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaDeviceSynchronize failed with %s\\n\", cudaGetErrorString(err));\n    }\n    err = cudaMemcpy(&numErr_h, numErr_d, sizeof(int), cudaMemcpyDeviceToHost);\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaMemcpy failed with %s\\n\", cudaGetErrorString(err));\n        cudaFree(numErr_d);\n        return err;\n    }\n    err = cudaFree(numErr_d);\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaFree failed with %s\\n\", cudaGetErrorString(err));\n        return err;\n    }\n    if (numErr_h > 0) {\n        return cudaErrorUnknown;\n    }\n    return cudaSuccess;\n}\n\n// Clean up state and exit. If status is SUCCESS, regression success is printed\n// to stdout. This will happen if the glut timer is triggered. If status is\n// anything else, the regression failure message is printed.\nvoid cleanup(int status)\n{\n    GLenum    glErr      = GL_NO_ERROR;\n    cudaError cudaErr    = cudaSuccess;\n    int       exitStatus = status;\n\n    // Clean up GL\n    if (*tex) {\n        GL_SAFE_CALL_NO_CLEANUP(glDeleteTextures(2, tex), glErr);\n    }\n\n    // Print test status and exit\n    if (glErr != GL_NO_ERROR || cudaErr != cudaSuccess)\n        exitStatus = FAILURE;\n\n    printStatus(exitStatus);\n\n    cleanExit = 1;\n\n    graphics_close_window();\n\n    if (exitStatus == FAILURE)\n        exit(EXIT_FAILURE);\n\n    if (exitStatus == WAIVED)\n        exit(EXIT_WAIVED);\n\n    exit(0);\n}\n\nvoid exitHandler(void)\n{\n    if (!cleanExit) {\n        printf(\"&&&& EGLSync_CUDAEvent_Interop unexpected failure \\n\");\n        printStatus(FAILURE);\n    }\n}\n\n// Print test success or fail for regression testing\nvoid printStatus(int status)\n{\n    switch (status) {\n    case SUCCESS:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop PASSED\\n\");\n        break;\n    case WAIVED:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop WAIVED\\n\");\n        break;\n    default:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop FAILED\\n\");\n        break;\n    }\n    fflush(stdout);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Simple interop app demonstrating EGLImage + EGLSync interop with CUDA.\n// Using EGLSync - CUDA Event interop one can achieve synchronization on GPU\n// itself for GL-EGL-CUDA operations instead of blocking CPU for\n// synchronization. This app requires GLES 3.2 or higher\n\n//---------------------------INCLUDES---------------------------------//\n#include <EGL/egl.h>\n#include <EGL/eglext.h>\n#include <GLES3/gl32.h>\n#include <hip/hip_runtime.h>\n#include <cudaEGL.h>\n#include <helper_cuda_drvapi.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include \"egl_common.h\"\n#include \"graphics_interface.h\"\n\n//---------------------------DEFINES---------------------------------//\n#define MAX_ITR 100\n\n#define FAILURE 0\n#define SUCCESS 1\n#define WAIVED  2\n\n#define BLOCK_SIZE 16\n\n#define GL_READ  0\n#define GL_WRITE 1\n//---------------------------MACROS---------------------------------//\n\n// Error-checking wrapper around GL calls\n#define GL_SAFE_CALL(call)                                                    \\\n    {                                                                         \\\n        GLenum err;                                                           \\\n        call;                                                                 \\\n        err = glGetError();                                                   \\\n        if (err != GL_NO_ERROR) {                                             \\\n            fprintf(stderr, \"%s:%d GL error: %d\\n\", __FILE__, __LINE__, err); \\\n            cleanup(FAILURE);                                                 \\\n        }                                                                     \\\n    }\n\n#define GL_SAFE_CALL_NO_CLEANUP(call, err)                                       \\\n    {                                                                            \\\n        GLenum status;                                                           \\\n        call;                                                                    \\\n        status = glGetError();                                                   \\\n        if (status != GL_NO_ERROR) {                                             \\\n            fprintf(stderr, \"%s:%d GL error: %d\\n\", __FILE__, __LINE__, status); \\\n            err = status;                                                        \\\n        }                                                                        \\\n    }\n\n// Error-checking wrapper around CUDA calls (taken from cutil.h)\n#define CUDA_SAFE_CALL(call)                                                                                        \\\n    do {                                                                                                            \\\n        hipError_t err = call;                                                                                       \\\n        if (hipSuccess != err) {                                                                                   \\\n            fprintf(                                                                                                \\\n                stderr, \"Cuda error in file '%s' in line %i : %s.\\n\", __FILE__, __LINE__, hipGetErrorString(err)); \\\n            cleanup(FAILURE);                                                                                       \\\n        }                                                                                                           \\\n    } while (0)\n\n#define CUDA_SAFE_CALL_NO_CLEANUP(call, err)                                                                           \\\n    do {                                                                                                               \\\n        hipError_t status = call;                                                                                       \\\n        if (hipSuccess != status) {                                                                                   \\\n            fprintf(                                                                                                   \\\n                stderr, \"Cuda error in file '%s' in line %i : %s.\\n\", __FILE__, __LINE__, hipGetErrorString(status)); \\\n            err = status;                                                                                              \\\n        }                                                                                                              \\\n    } while (0)\n\n#if defined(EXTENSION_LIST)\nEXTENSION_LIST(EXTLST_DECL)\ntypedef void (*extlst_fnptr_t)(void);\nstatic struct\n{\n    extlst_fnptr_t *fnptr;\n    char const     *name;\n} extensionList[] = {EXTENSION_LIST(EXTLST_ENTRY)};\n\nint eglSetupExtensions(void)\n{\n    unsigned int i;\n\n    for (i = 0; i < (sizeof(extensionList) / sizeof(*extensionList)); i++) {\n        *extensionList[i].fnptr = eglGetProcAddress(extensionList[i].name);\n        if (*extensionList[i].fnptr == NULL) {\n            printf(\"Couldn't get address of %s()\\n\", extensionList[i].name);\n            return 0;\n        }\n    }\n\n    return 1;\n}\n#endif\n\n#if defined(EXTENSION_LIST)\nEXTENSION_LIST(EXTLST_EXTERN)\n#endif\n\n//------------------------GLOBAL VARIABLES--------------------------//\n\n// GL texture\nGLuint tex[2] = {0};\n\n// Used to catch unexpected termination from GLUT\nint cleanExit = 0;\n\n// Use CPU Sync or GPU sync; Default GPU\nint useGpu = 1;\n\n// CUDA Resource\nhipGraphicsResource_t writeResource = NULL;\nhipGraphicsResource_t readResource  = NULL;\nhipArray_t            writeArray, readArray;\nhipDevice_t           device;\nhipCtx_t          context;\n\n// Which device to run on\nunsigned int dev = 0;\n\n// Default width, height, and iterations value\nint width  = 2048;\nint height = 2048;\nint itr    = MAX_ITR;\n\n// Error check variable\n__device__ static unsigned int numErrors = 0;\n\n//-----------------------FUNCTION PROTOTYPES------------------------//\n\nvoid checkSync(int argc, char **argv);\nint  parseCmdLine(int argc, char **argv);\nvoid printUsage(void);\nvoid cleanup(int status);\nvoid exitHandler(void);\nvoid printStatus(int status);\nvoid checkSyncOnCPU(void);\nvoid checkSyncOnGPU(EGLDisplay dpy);\n\n__global__ void\nverify_and_update_kernel(hipSurfaceObject_t write, hipSurfaceObject_t read, char expected, char newval, int width, int height);\nextern \"C\" hipError_t cudaGetValueMismatch();\n\n//-----------------------FUNCTION DEFINITIONS------------------------//\n\nint main(int argc, char *argv[])\n{\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    parseCmdLine(argc, argv);\n    atexit(exitHandler);\n\n    checkSync(argc, argv);\n    return 0;\n}\n\nint parseCmdLine(int argc, char **argv)\n{\n    int i;\n    for (i = 1; i < argc; i++) {\n        if (strcmp(argv[i], \"-cpu\") == 0) {\n            useGpu = 0;\n        }\n\n        if (strcmp(argv[i], \"-h\") == 0) {\n            printUsage();\n            cleanup(SUCCESS);\n        }\n\n        if (strcmp(argv[i], \"-width\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"width option must be followed by value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &width) != 1) {\n                printf(\"Error: invalid width value\\n\");\n                return FAILURE;\n            }\n        }\n\n        if (strcmp(argv[i], \"-height\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"height option must be followed by value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &height) != 1) {\n                printf(\"Error: invalid height value\\n\");\n                return FAILURE;\n            }\n        }\n        if (strcmp(argv[i], \"-itr\") == 0) {\n            ++i;\n            if (i == argc) {\n                printf(\"itr option must be followed by iteration value\\n\");\n                return FAILURE;\n            }\n            if (sscanf(argv[i], \"%d\", &itr) != 1) {\n                printf(\"Error: invalid iteration value\\n\");\n                return FAILURE;\n            }\n        }\n    }\n\n    return SUCCESS;\n}\n\nvoid printUsage(void)\n{\n    printf(\"Usage:\\n\");\n    printf(\"\\t-h\\tPrint command line options\\n\");\n    printf(\"\\t-cpu\\tSync on the CPU instead of the GPU\\n\");\n    printf(\"\\t-width w\\tSet the width to w\\n\");\n    printf(\"\\t-height h\\tSet the height to h\\n\");\n    printf(\"\\t-itr i\\tSet number of iterations to i\\n\");\n}\n\nvoid checkSync(int argc, char **argv)\n{\n    int            x, y;\n    int            bufferSize = width * height * 4;\n    unsigned char *pSurf_read = NULL, *pSurf_write = NULL;\n    int            integrated;\n\n    hipError_t          status          = hipSuccess;\n    CUctxCreateParams ctxCreateParams = {};\n\n    // Init values for variables\n    x = y = 0;\n\n    if (hipSuccess != (status = hipInit(0))) {\n        printf(\"Failed to initialize CUDA\\n\");\n    }\n    device = findCudaDeviceDRV(argc, (const char **)argv);\n\n    if (hipSuccess != (status = hipCtxCreate(&context, &ctxCreateParams, 0, device))) {\n        printf(\"failed to create CUDA context\\n\");\n    }\n    hipCtxPushCurrent(context);\n\n    status = hipDeviceGetAttribute(&integrated, hipDeviceAttributeIntegrated, device);\n    if (status != hipSuccess) {\n        printf(\"Failed to get device attribute hipDeviceAttributeIntegrated\\n\");\n        cleanup(FAILURE);\n    }\n\n    if (integrated != 1) {\n        printf(\"EGLSync_CUDAEvent_Interop does not support dGPU. Waiving sample.\\n\");\n        cleanup(WAIVED);\n    }\n\n#if (defined(__arm__) || defined(__aarch64__)) && defined(__linux__)\n    graphics_setup_window(0, 0, width, height, \"EGLSync_CUDA_Interop\");\n#endif\n\n    pSurf_read  = (unsigned char *)malloc(bufferSize);\n    pSurf_write = (unsigned char *)malloc(bufferSize);\n    if (pSurf_read == NULL || pSurf_write == NULL) {\n        printf(\"malloc failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    for (x = 0; x < width; x++) {\n        for (y = 0; y < height; y++) {\n            pSurf_read[(y * width + x) * 4]      = 1;\n            pSurf_read[(y * width + x) * 4 + 1]  = 1;\n            pSurf_read[(y * width + x) * 4 + 2]  = 1;\n            pSurf_read[(y * width + x) * 4 + 3]  = 1;\n            pSurf_write[(y * width + x) * 4]     = 0;\n            pSurf_write[(y * width + x) * 4 + 1] = 0;\n            pSurf_write[(y * width + x) * 4 + 2] = 0;\n            pSurf_write[(y * width + x) * 4 + 3] = 0;\n        }\n    }\n\n    // NOP call to error-check the above glut calls\n    GL_SAFE_CALL({});\n\n    // Init texture\n    GL_SAFE_CALL(glGenTextures(2, tex));\n\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, tex[GL_READ]));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pSurf_read));\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, 0));\n\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, tex[GL_WRITE]));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));\n    GL_SAFE_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pSurf_write));\n    GL_SAFE_CALL(glBindTexture(GL_TEXTURE_2D, 0));\n\n    glFinish();\n\n    EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();\n    if (eglDisplayHandle == EGL_NO_DISPLAY) {\n        printf(\"eglDisplayHandle failed \\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"eglDisplay Handle created \\n\");\n    }\n\n    if (!eglSetupExtensions()) {\n        printf(\"SetupExtentions failed \\n\");\n        cleanup(FAILURE);\n    }\n\n    EGLContext eglCtx = eglGetCurrentContext();\n    if (eglCtx == EGL_NO_CONTEXT) {\n        printf(\"Context1 create failed with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n\n    // Create the EGL_Image\n    EGLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE};\n\n    EGLImageKHR eglImage1 = eglCreateImageKHR(\n        eglDisplayHandle, eglCtx, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(intptr_t)tex[GL_READ], eglImgAttrs);\n    if (eglImage1 == EGL_NO_IMAGE_KHR) {\n        printf(\"EGLImage create failed for read texture with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"EGLImage1 created \\n\");\n    }\n\n    EGLImageKHR eglImage2 = eglCreateImageKHR(\n        eglDisplayHandle, eglCtx, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(intptr_t)tex[GL_WRITE], eglImgAttrs);\n    if (eglImage2 == EGL_NO_IMAGE_KHR) {\n        printf(\"EGLImage create failed for write texture with error %d\\n\", eglGetError());\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"EGLImage2 created \\n\");\n    }\n\n    glFinish();\n\n    status = cuGraphicsEGLRegisterImage(&writeResource, eglImage1, hipGraphicsRegisterFlagsNone);\n    if (status != hipSuccess) {\n        printf(\"cuGraphicsEGLRegisterImage failed with Texture 1\\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"cuGraphicsEGLRegisterImage Passed, writeResource created with texture \"\n               \"1\\n\");\n    }\n\n    status = hipGraphicsSubResourceGetMappedArray(&writeArray, writeResource, 0, 0);\n    if (status != hipSuccess) {\n        printf(\"hipGraphicsSubResourceGetMappedArray failed for writeResource with \"\n               \"texture 1\\n\");\n        cleanup(FAILURE);\n    }\n\n    status = cuGraphicsEGLRegisterImage(&readResource, eglImage2, hipGraphicsRegisterFlagsNone);\n    if (status != hipSuccess) {\n        printf(\"cuGraphicsEGLRegisterImage failed for readResource with Texture 2\\n\");\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"cuGraphicsEGLRegisterImage Passed, readResource created with texture \"\n               \"2\\n\");\n    }\n\n    status = hipGraphicsSubResourceGetMappedArray(&readArray, readResource, 0, 0);\n    if (status != hipSuccess) {\n        printf(\"hipGraphicsSubResourceGetMappedArray failed for texture 2\\n\");\n        cleanup(FAILURE);\n    }\n\n    if (useGpu) {\n        printf(\"Using GPU Sync path\\n\");\n        checkSyncOnGPU(eglDisplayHandle);\n    }\n    else {\n        printf(\"Using CPU Sync path\\n\");\n        checkSyncOnCPU();\n    }\n\n    free(pSurf_read);\n    free(pSurf_write);\n    cleanup(SUCCESS);\n}\n\nvoid checkSyncOnCPU(void)\n{\n    int                z = 0;\n    unsigned char      expectedData, newData;\n    hipError_t           status = hipSuccess;\n    HIP_RESOURCE_DESC wdsc, rdsc;\n    memset(&wdsc, 0, sizeof(wdsc));\n    memset(&rdsc, 0, sizeof(rdsc));\n\n    expectedData = 0;\n    newData      = 1;\n\n    wdsc.resType          = HIP_RESOURCE_TYPE_ARRAY;\n    wdsc.res.array.hArray = writeArray;\n    hipSurfaceObject_t writeSurface;\n    rdsc.resType          = HIP_RESOURCE_TYPE_ARRAY;\n    rdsc.res.array.hArray = readArray;\n    hipSurfaceObject_t readSurface;\n\n    status = cuSurfObjectCreate(&writeSurface, &wdsc);\n    if (status != hipSuccess) {\n        printf(\"Surface bounding failed with status %d\\n\", status);\n        cleanup(FAILURE);\n    }\n    status = cuSurfObjectCreate(&readSurface, &rdsc);\n    if (status != hipSuccess) {\n        printf(\"Surface bounding failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    for (z = 0; z < itr; z++) {\n        // GL call to copy from read texture to write texture\n        GL_SAFE_CALL(glCopyImageSubData(\n            tex[GL_READ], GL_TEXTURE_2D, 0, 0, 0, 0, tex[GL_WRITE], GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1));\n\n        glFinish();\n\n        newData++;\n        expectedData++;\n\n        verify_and_update_kernel<<<(width * height) / 256, 256>>>(\n            writeSurface, readSurface, expectedData, newData, width, height);\n\n        status = hipCtxSynchronize();\n        if (status != hipSuccess) {\n            printf(\"hipCtxSynchronize failed \\n\");\n        }\n    }\n\n    hipError_t err = cudaGetValueMismatch();\n    if (err != hipSuccess) {\n        printf(\"Value mismatch seen when using CPU sync\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Clean up CUDA writeResource\n    status = hipGraphicsUnregisterResource(writeResource);\n    if (status != hipSuccess) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered writeResource. \\n\");\n    }\n\n    // Clean up CUDA readResource\n    status = hipGraphicsUnregisterResource(readResource);\n    if (status != hipSuccess) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered readResource. \\n\");\n    }\n}\n\n/*\n    Performs same function as checkSyncOnCPU\n    Here instead of glFinish() and cuCtxSynchronize like in checkSyncOnCPU,\n    we make use of EGLSync, CUDA Event and cuStreamWaitEvent, eglWaitSyncKHR to\n   achieve the synchronization due to this CPU is not blocked for any\n   synchronization needed between GL-EGL & CUDA operations all synchronizations\n   happens on the GPU only.\n*/\nvoid checkSyncOnGPU(EGLDisplay dpy)\n{\n    int                z = 0;\n    unsigned char      expectedData, newData;\n    hipError_t        err;\n    hipError_t           status = hipSuccess;\n    hipStream_t           stream;\n    hipEvent_t            timingDisabledEvent;\n    HIP_RESOURCE_DESC wdsc, rdsc;\n    memset(&wdsc, 0, sizeof(wdsc));\n    memset(&rdsc, 0, sizeof(rdsc));\n\n    expectedData = 0;\n    newData      = 1;\n\n    wdsc.resType          = HIP_RESOURCE_TYPE_ARRAY;\n    wdsc.res.array.hArray = writeArray;\n    hipSurfaceObject_t writeSurface;\n    rdsc.resType          = HIP_RESOURCE_TYPE_ARRAY;\n    rdsc.res.array.hArray = readArray;\n    hipSurfaceObject_t readSurface;\n\n    status = cuSurfObjectCreate(&writeSurface, &wdsc);\n    if (status != hipSuccess) {\n        printf(\"Surface bounding failed with status %d\\n\", status);\n        cleanup(FAILURE);\n    }\n    status = cuSurfObjectCreate(&readSurface, &rdsc);\n    if (status != hipSuccess) {\n        printf(\"Surface bounding failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    status = hipStreamCreateWithFlags(&stream, hipStreamDefault);\n    if (status != hipSuccess) {\n        printf(\"Stream creation failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Creates timing disabled event which uses non-blocking synchronization\n    status = hipEventCreateWithFlags(&timingDisabledEvent, hipEventDisableTiming);\n    if (status != hipSuccess) {\n        printf(\"Default event creation failed\\n\");\n        cleanup(FAILURE);\n    }\n\n    /*\n        1. We perform texture-to-texture copy in GLES which is async function\n        2. Followed by creating EGLSync and a CUDA Event from that EGLSync object\n        3. Using cuStreamWaitEvent() we wait in GPU for the GLES to finish texture\n       copy.\n        4. CUDA kernel verfiy_and_update_kernel verifies if the copied data by\n       GLES is correct, and it updates the buffer with new values.\n        5. This is followed by eglWaitSyncKHR() which waits for the cuda kernel to\n       finish, so that in the next iteration GLES can perform the copying of the\n       updated buffer to write texture,\n    */\n    for (z = 0; z < itr; z++) {\n        // GL call to copy from read texture to write texture\n        GL_SAFE_CALL(glCopyImageSubData(\n            tex[GL_READ], GL_TEXTURE_2D, 0, 0, 0, 0, tex[GL_WRITE], GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1));\n\n        EGLSyncKHR   eglSyncForGL, eglSyncForCuda;\n        EGLBoolean   egl_status  = EGL_TRUE;\n        EGLAttribKHR eglattrib[] = {EGL_CUDA_EVENT_HANDLE_NV, (EGLAttrib)timingDisabledEvent, EGL_NONE};\n\n        hipEvent_t cudaEGLSyncEvent;\n\n        eglSyncForGL = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);\n\n        if (eglSyncForGL == EGL_NO_SYNC_KHR) {\n            printf(\" EGL Sync creation failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        status = cuEventCreateFromEGLSync(&cudaEGLSyncEvent, eglSyncForGL, hipEventDefault);\n        if (status != hipSuccess) {\n            printf(\"CUDA event creation from EGLSync failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        // We wait from CUDA in GPU for GL-EGL operation completion\n        status = hipStreamWaitEvent(stream, cudaEGLSyncEvent, 0);\n        if (status != hipSuccess) {\n            printf(\"Stream wait for event created from EGLSync failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        egl_status = eglDestroySyncKHR(dpy, eglSyncForGL);\n        if (egl_status != EGL_TRUE) {\n            printf(\"EGL sync object destruction failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        newData++;\n        expectedData++;\n\n        // Verifies the values in readSurface which is copied by\n        // glCopyImageSubData() And writes value of newData into writeSurface\n        verify_and_update_kernel<<<(width * height) / 256, 256, 0, stream>>>(\n            writeSurface, readSurface, expectedData, newData, width, height);\n\n        status = hipEventDestroy(cudaEGLSyncEvent);\n        if (status != hipSuccess) {\n            printf(\"Event Destroy failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        status = hipEventRecord(timingDisabledEvent, stream);\n        if (status != hipSuccess) {\n            printf(\"Event Record failed\\n\");\n            cleanup(FAILURE);\n        }\n\n        // creating an EGL sync object linked to a CUDA event object\n        eglSyncForCuda = eglCreateSync64KHR(dpy, EGL_SYNC_CUDA_EVENT_NV, eglattrib);\n\n        // We wait from EGL for CUDA operation completion\n        egl_status = eglWaitSyncKHR(dpy, eglSyncForCuda, 0);\n        if (egl_status != EGL_TRUE) {\n            printf(\"eglWaitSyncKHR failed\\n\");\n            cleanup(FAILURE);\n        }\n        egl_status = eglDestroySyncKHR(dpy, eglSyncForCuda);\n        if (egl_status != EGL_TRUE) {\n            printf(\"EGL sync object destruction failed\\n\");\n            cleanup(FAILURE);\n        }\n    }\n\n    err = cudaGetValueMismatch();\n    if (err != hipSuccess) {\n        printf(\"Value mismatch seen when using GPU sync\\n\");\n        cleanup(FAILURE);\n    }\n\n    // Clean up CUDA writeResource\n    status = hipGraphicsUnregisterResource(writeResource);\n    if (status != hipSuccess) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered writeResource. \\n\");\n    }\n\n    // Clean up CUDA readResource\n    status = hipGraphicsUnregisterResource(readResource);\n    if (status != hipSuccess) {\n        printf(\"Failed to unregister %d\", status);\n        cleanup(FAILURE);\n    }\n    else {\n        printf(\"Unregistered readResource. \\n\");\n    }\n}\n\n// Verifies the values in readSurface whether they are expected ones\n// And writes value of newData into writeSurface\n__global__ void\nverify_and_update_kernel(hipSurfaceObject_t write, hipSurfaceObject_t read, char expected, char newval, int width, int height)\n{\n    unsigned int x = blockDim.x * blockIdx.x + threadIdx.x;\n    unsigned int y = blockDim.y * blockIdx.y + threadIdx.y;\n\n    if (x < width && y < height) {\n        uchar4 check;\n        surf2Dread(&check, read, x * 4, y);\n        if (check.x != expected || check.y != expected || check.z != expected || check.w != expected) {\n            printf(\"Mismatch found in values read[0]= %u read[1]= %u read[2]= %u \"\n                   \"read[3]= %u expected is %u\\n\",\n                   check.x,\n                   check.y,\n                   check.z,\n                   check.w,\n                   expected);\n            numErrors++;\n            return;\n        }\n        uchar4 data = make_uchar4(newval, newval, newval, newval);\n        surf2Dwrite(data, write, x * 4, y);\n    }\n}\n\n__global__ void getNumErrors(int *numErr) { *numErr = numErrors; }\n\nextern \"C\" hipError_t cudaGetValueMismatch()\n{\n    int         numErr_h;\n    int        *numErr_d = NULL;\n    hipError_t err      = hipSuccess;\n\n    err = hipMalloc(&numErr_d, sizeof(int));\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipMemcpy failed with %s\\n\", hipGetErrorString(err));\n        hipFree(numErr_d);\n        return err;\n    }\n\n    getNumErrors<<<1, 1>>>(numErr_d);\n    err = hipDeviceSynchronize();\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipDeviceSynchronize failed with %s\\n\", hipGetErrorString(err));\n    }\n    err = hipMemcpy(&numErr_h, numErr_d, sizeof(int), hipMemcpyDeviceToHost);\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipMemcpy failed with %s\\n\", hipGetErrorString(err));\n        hipFree(numErr_d);\n        return err;\n    }\n    err = hipFree(numErr_d);\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipFree failed with %s\\n\", hipGetErrorString(err));\n        return err;\n    }\n    if (numErr_h > 0) {\n        return hipErrorUnknown;\n    }\n    return hipSuccess;\n}\n\n// Clean up state and exit. If status is SUCCESS, regression success is printed\n// to stdout. This will happen if the glut timer is triggered. If status is\n// anything else, the regression failure message is printed.\nvoid cleanup(int status)\n{\n    GLenum    glErr      = GL_NO_ERROR;\n    hipError_t cudaErr    = hipSuccess;\n    int       exitStatus = status;\n\n    // Clean up GL\n    if (*tex) {\n        GL_SAFE_CALL_NO_CLEANUP(glDeleteTextures(2, tex), glErr);\n    }\n\n    // Print test status and exit\n    if (glErr != GL_NO_ERROR || cudaErr != hipSuccess)\n        exitStatus = FAILURE;\n\n    printStatus(exitStatus);\n\n    cleanExit = 1;\n\n    graphics_close_window();\n\n    if (exitStatus == FAILURE)\n        exit(EXIT_FAILURE);\n\n    if (exitStatus == WAIVED)\n        exit(EXIT_WAIVED);\n\n    exit(0);\n}\n\nvoid exitHandler(void)\n{\n    if (!cleanExit) {\n        printf(\"&&&& EGLSync_CUDAEvent_Interop unexpected failure \\n\");\n        printStatus(FAILURE);\n    }\n}\n\n// Print test success or fail for regression testing\nvoid printStatus(int status)\n{\n    switch (status) {\n    case SUCCESS:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop PASSED\\n\");\n        break;\n    case WAIVED:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop WAIVED\\n\");\n        break;\n    default:\n        printf(\"&&&& EGLSync_CUDAEvent_Interop FAILED\\n\");\n        break;\n    }\n    fflush(stdout);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/EGLSync_CUDAEvent_Interop/EGLSync_CUDAEvent_Interop.cu"}
{"input": "/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n#include <cuda.h>\n#include <helper_cuda.h>\n\nstatic __global__ void flipSurfaceBits(cudaSurfaceObject_t surfObj, int width, int height)\n{\n    char         data;\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    if (x < width && y < height) {\n        // Read from input surface\n        surf2Dread(&data, surfObj, x, y);\n        // Write to output surface\n        data = ~data;\n        surf2Dwrite(data, surfObj, x, y);\n    }\n}\n\n// Copy cudaArray to surface memory and launch the CUDA kernel\nvoid launchFlipSurfaceBitsKernel(cudaArray_t *levelArray,\n                                 int32_t     *multiPlanarWidth,\n                                 int32_t     *multiPlanarHeight,\n                                 int          numPlanes)\n{\n\n    cudaSurfaceObject_t surfObject[numPlanes] = {0};\n    cudaResourceDesc    resDesc;\n\n    for (int i = 0; i < numPlanes; i++) {\n        memset(&resDesc, 0, sizeof(resDesc));\n        resDesc.resType         = cudaResourceTypeArray;\n        resDesc.res.array.array = levelArray[i];\n        checkCudaErrors(cudaCreateSurfaceObject(&surfObject[i], &resDesc));\n        dim3 threadsperBlock(16, 16);\n        dim3 numBlocks((multiPlanarWidth[i] + threadsperBlock.x - 1) / threadsperBlock.x,\n                       (multiPlanarHeight[i] + threadsperBlock.y - 1) / threadsperBlock.y);\n        flipSurfaceBits<<<numBlocks, threadsperBlock>>>(surfObject[i], multiPlanarWidth[i], multiPlanarHeight[i]);\n    }\n}\n", "output": "/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\nstatic __global__ void flipSurfaceBits(hipSurfaceObject_t surfObj, int width, int height)\n{\n    char         data;\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    if (x < width && y < height) {\n        // Read from input surface\n        surf2Dread(&data, surfObj, x, y);\n        // Write to output surface\n        data = ~data;\n        surf2Dwrite(data, surfObj, x, y);\n    }\n}\n\n// Copy cudaArray to surface memory and launch the CUDA kernel\nvoid launchFlipSurfaceBitsKernel(hipArray_t *levelArray,\n                                 int32_t     *multiPlanarWidth,\n                                 int32_t     *multiPlanarHeight,\n                                 int          numPlanes)\n{\n\n    hipSurfaceObject_t surfObject[numPlanes] = {0};\n    hipResourceDesc    resDesc;\n\n    for (int i = 0; i < numPlanes; i++) {\n        memset(&resDesc, 0, sizeof(resDesc));\n        resDesc.resType         = hipResourceTypeArray;\n        resDesc.res.array.array = levelArray[i];\n        checkCudaErrors(hipCreateSurfaceObject(&surfObject[i], &resDesc));\n        dim3 threadsperBlock(16, 16);\n        dim3 numBlocks((multiPlanarWidth[i] + threadsperBlock.x - 1) / threadsperBlock.x,\n                       (multiPlanarHeight[i] + threadsperBlock.y - 1) / threadsperBlock.y);\n        flipSurfaceBits<<<numBlocks, threadsperBlock>>>(surfObject[i], multiPlanarWidth[i], multiPlanarHeight[i]);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/cudaNvSciBufMultiplanar/imageKernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the CUDA C bindings to OpenGL ES to\n    dynamically modify a vertex buffer using a CUDA C kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with CUDA C\n    3. Map the VBO for writing from CUDA C\n    4. Run CUDA C kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL ES\n\n    Host code\n*/\n\n// includes, system\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid error_exit(const char *format, ...)\n{\n    va_list args;\n    va_start(args, format);\n    vfprintf(stderr, format, args);\n    va_end(args);\n    exit(1);\n}\n\n#include \"graphics_interface.c\"\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n// #include <helper_cuda_gl.h>      // helper functions for CUDA/GL interop\n\n#include <vector_types.h>\n\n#define MAX_EPSILON_ERROR 0.0f\n#define THRESHOLD         0.0f\n#define REFRESH_DELAY     1 // ms\n\n#define GUI_IDLE      0x100\n#define GUI_ROTATE    0x101\n#define GUI_TRANSLATE 0x102\n\nint gui_mode;\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// OpenGL ES variables and interop with CUDA C\nGLuint                       mesh_vao, mesh_vbo;\nstruct cudaGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// UI / mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Frame statistics\nint          frame;\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\n\n// Auto-Verification Code\nbool g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\n\n// CUDA functionality\nvoid runCuda(struct cudaGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGLES (VBO)\";\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda/OpenGL ES Interop (VBO): %3.1f fps (Max 1000 fps)\", avgFPS);\n    graphics_set_windowtitle(fps);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct cudaGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    cudaGraphicsMapResources(1, vbo_resource, 0);\n    size_t num_bytes;\n    cudaGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource);\n    // printf(\"Sample CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    cudaGraphicsUnmapResources(1, vbo_resource, 0);\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    cudaDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    cudaMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), cudaMemcpyDeviceToHost);\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display_thisframe(float time_delta)\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    // GET_GLERROR(0);\n\n    // set view matrix: broken, it doesn't work in OpenGL ES! Must put into shader\n    // glMatrixMode(GL_MODELVIEW);\n    // glLoadIdentity();\n    // glTranslatef(0.0, 0.0, translate_z);\n    // glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    // glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n\n    // GET_GLERROR(0);\n    glFinish();\n    // GET_GLERROR(0);\n\n    g_fAnim += time_delta;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        printf(\"%s: Mapping result buffer from OpenGL ES\\n\", __FUNCTION__);\n\n        cudaGraphicsUnregisterResource(cuda_vbo_resource);\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data =\n            (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, mesh_width * mesh_height * 4 * sizeof(float), GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        GET_GLERROR(0);\n    }\n}\n\nGLuint mesh_shader = 0;\n\nvoid readAndCompileShaderFromGLSLFile(GLuint new_shaderprogram, const char *filename, GLenum shaderType)\n{\n    FILE *file = fopen(filename, \"rb\"); // open shader text file\n    if (!file)\n        error_exit(\"Filename %s does not exist\\n\", filename);\n\n    /* get the size of the file and read it */\n    fseek(file, 0, SEEK_END);\n    GLint size = ftell(file);\n    char *data = (char *)malloc(sizeof(char) * (size + 1));\n    memset(data, 0, sizeof(char) * (size + 1));\n    fseek(file, 0, SEEK_SET);\n    size_t res = fread(data, 1, size, file);\n    fclose(file);\n\n    GLuint shader = glCreateShader(shaderType);\n    glShaderSource(shader, 1, (const GLchar **)&data, &size);\n    glCompileShader(shader);\n\n    GET_GLERROR(0);\n    GLint compile_success = 0;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_success);\n    GET_GLERROR(0);\n\n    if (compile_success == GL_FALSE) {\n        printf(\"Compilation of %s failed!\\n Reason:\\n\", filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        glDeleteShader(shader);\n        exit(1);\n    }\n\n    glAttachShader(new_shaderprogram, shader);\n    glDeleteShader(shader); // good to do?\n\n    free(data);\n}\n\nGLuint ShaderCreate(const char *vshader_filename, const char *fshader_filename)\n{\n    printf(\"Loading GLSL shaders %s %s\\n\", vshader_filename, fshader_filename);\n\n    GLuint new_shaderprogram = glCreateProgram();\n\n    GET_GLERROR(0);\n    if (vshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, vshader_filename, GL_VERTEX_SHADER);\n\n    GET_GLERROR(0);\n    if (fshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, fshader_filename, GL_FRAGMENT_SHADER);\n\n    GET_GLERROR(0);\n\n    glLinkProgram(new_shaderprogram);\n\n    GET_GLERROR(0);\n    GLint link_success;\n    glGetProgramiv(new_shaderprogram, GL_LINK_STATUS, &link_success);\n\n    if (link_success == GL_FALSE) {\n        printf(\"Linking of %s with %s failed!\\n Reason:\\n\", vshader_filename, fshader_filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(new_shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(new_shaderprogram, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        exit(EXIT_FAILURE);\n    }\n\n    return new_shaderprogram;\n}\n\n//===========================================================================\n// InitGraphicsState() - initialize OpenGL\n//===========================================================================\nstatic void InitGraphicsState(void)\n{\n    char *GL_version  = (char *)glGetString(GL_VERSION);\n    char *GL_vendor   = (char *)glGetString(GL_VENDOR);\n    char *GL_renderer = (char *)glGetString(GL_RENDERER);\n\n    printf(\"Version: %s\\n\", GL_version);\n    printf(\"Vendor: %s\\n\", GL_vendor);\n    printf(\"Renderer: %s\\n\", GL_renderer);\n\n    // RENDERING SETUP (OpenGL ES or OpenGL Core Profile!)\n    glGenVertexArrays(1, &mesh_vao); // Features' Vertex Array Object allocation\n    glBindVertexArray(mesh_vao);     // bind VAO\n\n    // initialize buffer object\n    glGenBuffers(1, &mesh_vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo);\n\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);\n    glVertexAttribPointer((GLuint)0, 4, GL_FLOAT, GL_FALSE, 0, 0);\n    glEnableVertexAttribArray(0);\n\n    checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, mesh_vbo, cudaGraphicsMapFlagsNone));\n    // glBindVertexArray(0); // keep above Vertex Array Object bound (it's the\n    // only one throughout)\n\n    // GLSL stuff\n    char *vertex_shader_path   = sdkFindFilePath(\"mesh.vert.glsl\", pArgv[0]);\n    char *fragment_shader_path = sdkFindFilePath(\"mesh.frag.glsl\", pArgv[0]);\n\n    if (vertex_shader_path == NULL || fragment_shader_path == NULL) {\n        printf(\"Error finding shader file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    mesh_shader = ShaderCreate(vertex_shader_path, fragment_shader_path);\n    GET_GLERROR(0);\n\n    free(vertex_shader_path);\n    free(fragment_shader_path);\n\n    glUseProgram(mesh_shader);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    int devID = 0;\n#if defined(__aarch64__) || defined(__arm__)\n    // find iGPU on the system which is compute capable which will perform\n    // GLES-CUDA interop\n    devID = findIntegratedGPU();\n#else\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    devID = findCudaDevice(argc, (const char **)argv);\n#endif\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(cudaMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, mesh_vbo);\n\n        cudaFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // this would use command-line specified CUDA device, note that CUDA\n        // defaults to highest Gflops/s device\n        if (checkCmdLineFlag(argc, (const char **)argv, \"device\"))\n            error_exit(\"Device setting not yet implemented!\\n\");\n\n        // create X11 window and set up associated OpenGL ES context\n        graphics_setup_window(0, 0, window_width, window_height, sSDKsample);\n\n        InitGraphicsState(); // set up GLES stuff\n\n        glClearColor(0, 0.5, 1, 1); // blue-ish background\n        glClear(GL_COLOR_BUFFER_BIT);\n\n        // printf(\"WP%d\\n\", __LINE__);\n        graphics_swap_buffers();\n\n        XEvent event;\n        KeySym key;\n        char   text[255];\n\n        int frame = 0;\n\n        while (frame < 100000) {\n            if (XPending(display)) {\n                XNextEvent(display, &event);\n\n                if (event.type == Expose && event.xexpose.count == 0) {\n                    printf(\"Redraw requested!\\n\");\n                }\n                if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1) {\n                    if (text[0] == 27)\n                        goto label_stop_x;\n\n                    printf(\"You pressed the %c key!\\n\", text[0]);\n                }\n\n                if (event.type == ButtonPress) {\n                    printf(\n                        \"Mouse button %d press at (%d,%d)\\n\", event.xbutton.button, event.xbutton.x, event.xbutton.y);\n\n                    if (event.xbutton.button == Button1)\n                        gui_mode = GUI_TRANSLATE;\n                    if (event.xbutton.button == Button3)\n                        gui_mode = GUI_ROTATE;\n                    mouse_old_x = event.xbutton.x;\n                    mouse_old_y = event.xbutton.y;\n                }\n\n                if (event.type == ButtonRelease) {\n                    printf(\"Mouse button %d released at (%d,%d)\\n\",\n                           event.xbutton.button,\n                           event.xbutton.x,\n                           event.xbutton.y);\n\n                    gui_mode    = GUI_IDLE;\n                    mouse_old_x = event.xbutton.x;\n                    mouse_old_y = event.xbutton.y;\n                }\n\n                if (event.type == MotionNotify) {\n                    // printf(\"Mouse motion towards %d %d, GUI mode is 0x%x\\n\",\n                    //\t   event.xmotion.x, event.xmotion.y, gui_mode);\n                    float dx, dy;\n                    dx = (float)(event.xmotion.x - mouse_old_x);\n                    dy = (float)(event.xmotion.y - mouse_old_y);\n\n                    if (gui_mode == GUI_ROTATE) {\n                        rotate_x += dy * 0.2f;\n                        rotate_y += dx * 0.2f;\n                        printf(\"rot x %f y %f\\n\", rotate_x, rotate_y);\n                    }\n                    if (gui_mode == GUI_TRANSLATE) {\n                        translate_z += dy * 0.01f;\n                        printf(\"translate z %f\\n\", translate_z);\n                    }\n\n                    mouse_old_x = event.xmotion.x;\n                    mouse_old_y = event.xmotion.y;\n                }\n            }\n\n            display_thisframe(0.010);\n            usleep(1000); // need not take full CPU and GPU\n\n            graphics_swap_buffers();\n            // printf(\"frame %d\\n\",frame++);\n        }\n\n    label_stop_x:\n        // NOTE: Before destroying OpenGL ES context, must unregister all shared\n        // resources from CUDA !\n        cudaGraphicsUnregisterResource(cuda_vbo_resource);\n\n        graphics_close_window(); // close window and destroy OpenGL ES context\n\n        sdkDeleteTimer(&timer);\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we run without OpenGL and see if VBO is generated\n            // correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the CUDA C bindings to OpenGL ES to\n    dynamically modify a vertex buffer using a CUDA C kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with CUDA C\n    3. Map the VBO for writing from CUDA C\n    4. Run CUDA C kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL ES\n\n    Host code\n*/\n\n// includes, system\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid error_exit(const char *format, ...)\n{\n    va_list args;\n    va_start(args, format);\n    vfprintf(stderr, format, args);\n    va_end(args);\n    exit(1);\n}\n\n#include \"graphics_interface.c\"\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n// #include <helper_cuda_gl.h>      // helper functions for CUDA/GL interop\n\n#include <hip/hip_vector_types.h>\n\n#define MAX_EPSILON_ERROR 0.0f\n#define THRESHOLD         0.0f\n#define REFRESH_DELAY     1 // ms\n\n#define GUI_IDLE      0x100\n#define GUI_ROTATE    0x101\n#define GUI_TRANSLATE 0x102\n\nint gui_mode;\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// OpenGL ES variables and interop with CUDA C\nGLuint                       mesh_vao, mesh_vbo;\nstruct hipGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// UI / mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Frame statistics\nint          frame;\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\n\n// Auto-Verification Code\nbool g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\n\n// CUDA functionality\nvoid runCuda(struct hipGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGLES (VBO)\";\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda/OpenGL ES Interop (VBO): %3.1f fps (Max 1000 fps)\", avgFPS);\n    graphics_set_windowtitle(fps);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct hipGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    hipGraphicsMapResources(1, vbo_resource, 0);\n    size_t num_bytes;\n    hipGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource);\n    // printf(\"Sample CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    hipGraphicsUnmapResources(1, vbo_resource, 0);\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    hipDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    hipMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), hipMemcpyDeviceToHost);\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display_thisframe(float time_delta)\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    // GET_GLERROR(0);\n\n    // set view matrix: broken, it doesn't work in OpenGL ES! Must put into shader\n    // glMatrixMode(GL_MODELVIEW);\n    // glLoadIdentity();\n    // glTranslatef(0.0, 0.0, translate_z);\n    // glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    // glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n\n    // GET_GLERROR(0);\n    glFinish();\n    // GET_GLERROR(0);\n\n    g_fAnim += time_delta;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        printf(\"%s: Mapping result buffer from OpenGL ES\\n\", __FUNCTION__);\n\n        hipGraphicsUnregisterResource(cuda_vbo_resource);\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data =\n            (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, mesh_width * mesh_height * 4 * sizeof(float), GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(hipGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        GET_GLERROR(0);\n    }\n}\n\nGLuint mesh_shader = 0;\n\nvoid readAndCompileShaderFromGLSLFile(GLuint new_shaderprogram, const char *filename, GLenum shaderType)\n{\n    FILE *file = fopen(filename, \"rb\"); // open shader text file\n    if (!file)\n        error_exit(\"Filename %s does not exist\\n\", filename);\n\n    /* get the size of the file and read it */\n    fseek(file, 0, SEEK_END);\n    GLint size = ftell(file);\n    char *data = (char *)malloc(sizeof(char) * (size + 1));\n    memset(data, 0, sizeof(char) * (size + 1));\n    fseek(file, 0, SEEK_SET);\n    size_t res = fread(data, 1, size, file);\n    fclose(file);\n\n    GLuint shader = glCreateShader(shaderType);\n    glShaderSource(shader, 1, (const GLchar **)&data, &size);\n    glCompileShader(shader);\n\n    GET_GLERROR(0);\n    GLint compile_success = 0;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_success);\n    GET_GLERROR(0);\n\n    if (compile_success == GL_FALSE) {\n        printf(\"Compilation of %s failed!\\n Reason:\\n\", filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        glDeleteShader(shader);\n        exit(1);\n    }\n\n    glAttachShader(new_shaderprogram, shader);\n    glDeleteShader(shader); // good to do?\n\n    free(data);\n}\n\nGLuint ShaderCreate(const char *vshader_filename, const char *fshader_filename)\n{\n    printf(\"Loading GLSL shaders %s %s\\n\", vshader_filename, fshader_filename);\n\n    GLuint new_shaderprogram = glCreateProgram();\n\n    GET_GLERROR(0);\n    if (vshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, vshader_filename, GL_VERTEX_SHADER);\n\n    GET_GLERROR(0);\n    if (fshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, fshader_filename, GL_FRAGMENT_SHADER);\n\n    GET_GLERROR(0);\n\n    glLinkProgram(new_shaderprogram);\n\n    GET_GLERROR(0);\n    GLint link_success;\n    glGetProgramiv(new_shaderprogram, GL_LINK_STATUS, &link_success);\n\n    if (link_success == GL_FALSE) {\n        printf(\"Linking of %s with %s failed!\\n Reason:\\n\", vshader_filename, fshader_filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(new_shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(new_shaderprogram, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        exit(EXIT_FAILURE);\n    }\n\n    return new_shaderprogram;\n}\n\n//===========================================================================\n// InitGraphicsState() - initialize OpenGL\n//===========================================================================\nstatic void InitGraphicsState(void)\n{\n    char *GL_version  = (char *)glGetString(GL_VERSION);\n    char *GL_vendor   = (char *)glGetString(GL_VENDOR);\n    char *GL_renderer = (char *)glGetString(GL_RENDERER);\n\n    printf(\"Version: %s\\n\", GL_version);\n    printf(\"Vendor: %s\\n\", GL_vendor);\n    printf(\"Renderer: %s\\n\", GL_renderer);\n\n    // RENDERING SETUP (OpenGL ES or OpenGL Core Profile!)\n    glGenVertexArrays(1, &mesh_vao); // Features' Vertex Array Object allocation\n    glBindVertexArray(mesh_vao);     // bind VAO\n\n    // initialize buffer object\n    glGenBuffers(1, &mesh_vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo);\n\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);\n    glVertexAttribPointer((GLuint)0, 4, GL_FLOAT, GL_FALSE, 0, 0);\n    glEnableVertexAttribArray(0);\n\n    checkCudaErrors(hipGraphicsGLRegisterBuffer(&cuda_vbo_resource, mesh_vbo, cudaGraphicsMapFlagsNone));\n    // glBindVertexArray(0); // keep above Vertex Array Object bound (it's the\n    // only one throughout)\n\n    // GLSL stuff\n    char *vertex_shader_path   = sdkFindFilePath(\"mesh.vert.glsl\", pArgv[0]);\n    char *fragment_shader_path = sdkFindFilePath(\"mesh.frag.glsl\", pArgv[0]);\n\n    if (vertex_shader_path == NULL || fragment_shader_path == NULL) {\n        printf(\"Error finding shader file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    mesh_shader = ShaderCreate(vertex_shader_path, fragment_shader_path);\n    GET_GLERROR(0);\n\n    free(vertex_shader_path);\n    free(fragment_shader_path);\n\n    glUseProgram(mesh_shader);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    int devID = 0;\n#if defined(__aarch64__) || defined(__arm__)\n    // find iGPU on the system which is compute capable which will perform\n    // GLES-CUDA interop\n    devID = findIntegratedGPU();\n#else\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    devID = findCudaDevice(argc, (const char **)argv);\n#endif\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(hipMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, mesh_vbo);\n\n        hipFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // this would use command-line specified CUDA device, note that CUDA\n        // defaults to highest Gflops/s device\n        if (checkCmdLineFlag(argc, (const char **)argv, \"device\"))\n            error_exit(\"Device setting not yet implemented!\\n\");\n\n        // create X11 window and set up associated OpenGL ES context\n        graphics_setup_window(0, 0, window_width, window_height, sSDKsample);\n\n        InitGraphicsState(); // set up GLES stuff\n\n        glClearColor(0, 0.5, 1, 1); // blue-ish background\n        glClear(GL_COLOR_BUFFER_BIT);\n\n        // printf(\"WP%d\\n\", __LINE__);\n        graphics_swap_buffers();\n\n        XEvent event;\n        KeySym key;\n        char   text[255];\n\n        int frame = 0;\n\n        while (frame < 100000) {\n            if (XPending(display)) {\n                XNextEvent(display, &event);\n\n                if (event.type == Expose && event.xexpose.count == 0) {\n                    printf(\"Redraw requested!\\n\");\n                }\n                if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1) {\n                    if (text[0] == 27)\n                        goto label_stop_x;\n\n                    printf(\"You pressed the %c key!\\n\", text[0]);\n                }\n\n                if (event.type == ButtonPress) {\n                    printf(\n                        \"Mouse button %d press at (%d,%d)\\n\", event.xbutton.button, event.xbutton.x, event.xbutton.y);\n\n                    if (event.xbutton.button == Button1)\n                        gui_mode = GUI_TRANSLATE;\n                    if (event.xbutton.button == Button3)\n                        gui_mode = GUI_ROTATE;\n                    mouse_old_x = event.xbutton.x;\n                    mouse_old_y = event.xbutton.y;\n                }\n\n                if (event.type == ButtonRelease) {\n                    printf(\"Mouse button %d released at (%d,%d)\\n\",\n                           event.xbutton.button,\n                           event.xbutton.x,\n                           event.xbutton.y);\n\n                    gui_mode    = GUI_IDLE;\n                    mouse_old_x = event.xbutton.x;\n                    mouse_old_y = event.xbutton.y;\n                }\n\n                if (event.type == MotionNotify) {\n                    // printf(\"Mouse motion towards %d %d, GUI mode is 0x%x\\n\",\n                    //\t   event.xmotion.x, event.xmotion.y, gui_mode);\n                    float dx, dy;\n                    dx = (float)(event.xmotion.x - mouse_old_x);\n                    dy = (float)(event.xmotion.y - mouse_old_y);\n\n                    if (gui_mode == GUI_ROTATE) {\n                        rotate_x += dy * 0.2f;\n                        rotate_y += dx * 0.2f;\n                        printf(\"rot x %f y %f\\n\", rotate_x, rotate_y);\n                    }\n                    if (gui_mode == GUI_TRANSLATE) {\n                        translate_z += dy * 0.01f;\n                        printf(\"translate z %f\\n\", translate_z);\n                    }\n\n                    mouse_old_x = event.xmotion.x;\n                    mouse_old_y = event.xmotion.y;\n                }\n            }\n\n            display_thisframe(0.010);\n            usleep(1000); // need not take full CPU and GPU\n\n            graphics_swap_buffers();\n            // printf(\"frame %d\\n\",frame++);\n        }\n\n    label_stop_x:\n        // NOTE: Before destroying OpenGL ES context, must unregister all shared\n        // resources from CUDA !\n        hipGraphicsUnregisterResource(cuda_vbo_resource);\n\n        graphics_close_window(); // close window and destroy OpenGL ES context\n\n        sdkDeleteTimer(&timer);\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we run without OpenGL and see if VBO is generated\n            // correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/simpleGLES/simpleGLES.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda_runtime.h>\n#include <cufft.h>       // CUDA FFT Libraries\n#include <helper_cuda.h> // Helper functions for CUDA Error handling\n#include <stdio.h>\n#include <stdlib.h>\n\n// OpenGL Graphics includes\n#include <GLES3/gl31.h>\n\n// FluidsGLES CUDA kernel definitions\n#include \"fluidsGLES_kernels.cuh\"\n\n// Texture object for reading velocity field\ncudaTextureObject_t texObj;\nstatic cudaArray   *array = NULL;\n\n// Particle data\nextern GLuint                       vbo;               // OpenGL vertex buffer object\nextern struct cudaGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange\n\n// Texture pitch\nextern size_t      tPitch;\nextern cufftHandle planr2c;\nextern cufftHandle planc2r;\ncData             *vxfield = NULL;\ncData             *vyfield = NULL;\n\nvoid setupTexture(int x, int y)\n{\n    cudaChannelFormatDesc desc = cudaCreateChannelDesc<float2>();\n\n    cudaMallocArray(&array, &desc, y, x);\n    getLastCudaError(\"cudaMalloc failed\");\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&texObj, &texRes, &texDescr, NULL));\n}\n\nvoid updateTexture(cData *data, size_t wib, size_t h, size_t pitch)\n{\n    checkCudaErrors(cudaMemcpy2DToArray(array, 0, 0, data, pitch, wib, h, cudaMemcpyDeviceToDevice));\n}\n\nvoid deleteTexture(void)\n{\n    checkCudaErrors(cudaDestroyTextureObject(texObj));\n    checkCudaErrors(cudaFreeArray(array));\n}\n\n// Note that these kernels are designed to work with arbitrary\n// domain sizes, not just domains that are multiples of the tile\n// size. Therefore, we have extra code that checks to make sure\n// a given thread location falls within the domain boundaries in\n// both X and Y. Also, the domain is covered by looping over\n// multiple elements in the Y direction, while there is a one-to-one\n// mapping between threads in X and the tile size in X.\n// Nolan Goodnight 9/22/06\n\n// This method adds constant force vectors to the velocity field\n// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.\n__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch)\n{\n    int    tx = threadIdx.x;\n    int    ty = threadIdx.y;\n    cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;\n\n    cData vterm = *fj;\n    tx -= r;\n    ty -= r;\n    float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);\n    vterm.x += s * fx;\n    vterm.y += s * fy;\n    *fj = vterm;\n}\n\n// This method performs the velocity advection step, where we\n// trace velocity vectors back in time to update each grid cell.\n// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear\n// interpolation in the velocity space.\n__global__ void advectVelocity_k(cData              *v,\n                                 float              *vx,\n                                 float              *vy,\n                                 int                 dx,\n                                 int                 pdx,\n                                 int                 dy,\n                                 float               dt,\n                                 int                 lb,\n                                 cudaTextureObject_t texObject)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData vterm, ploc;\n    float vxterm, vyterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * pdx + gtidx;\n                vterm  = tex2D<cData>(texObject, (float)gtidx, (float)fi);\n                ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);\n                ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);\n                vterm  = tex2D<cData>(texObject, ploc.x, ploc.y);\n                vxterm = vterm.x;\n                vyterm = vterm.y;\n                vx[fj] = vxterm;\n                vy[fj] = vyterm;\n            }\n        }\n    }\n}\n\n// This method performs velocity diffusion and forces mass conservation\n// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued\n// arrays holding the Fourier coefficients of the velocity field in\n// X and Y. Diffusion in this space takes a simple form described as:\n// v(k,t) = v(k,t) / (1 + visc * dt * k^2), where visc is the viscosity,\n// and k is the wavenumber. The projection step forces the Fourier\n// velocity vectors to be orthogonal to the vectors for each\n// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.\n__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData xterm, yterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                xterm  = vx[fj];\n                yterm  = vy[fj];\n\n                // Compute the index of the wavenumber based on the\n                // data order produced by a standard NN FFT.\n                int iix = gtidx;\n                int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;\n\n                // Velocity diffusion\n                float kk   = (float)(iix * iix + iiy * iiy); // k^2\n                float diff = 1.f / (1.f + visc * dt * kk);\n                xterm.x *= diff;\n                xterm.y *= diff;\n                yterm.x *= diff;\n                yterm.y *= diff;\n\n                // Velocity projection\n                if (kk > 0.f) {\n                    float rkk = 1.f / kk;\n                    // Real portion of velocity projection\n                    float rkp = (iix * xterm.x + iiy * yterm.x);\n                    // Imaginary portion of velocity projection\n                    float ikp = (iix * xterm.y + iiy * yterm.y);\n                    xterm.x -= rkk * rkp * iix;\n                    xterm.y -= rkk * ikp * iix;\n                    yterm.x -= rkk * rkp * iiy;\n                    yterm.y -= rkk * ikp * iiy;\n                }\n\n                vx[fj] = xterm;\n                vy[fj] = yterm;\n            }\n        }\n    }\n}\n\n// This method updates the velocity field 'v' using the two complex\n// arrays from the previous step: 'vx' and 'vy'. Here we scale the\n// real components by 1/(dx*dy) to account for an unnormalized FFT.\n__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    float vxterm, vyterm;\n    cData nvterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fjr = fi * pdx + gtidx;\n                vxterm  = vx[fjr];\n                vyterm  = vy[fjr];\n\n                // Normalize the result of the inverse FFT\n                float scale = 1.f / (dx * dy);\n                nvterm.x    = vxterm * scale;\n                nvterm.y    = vyterm * scale;\n\n                cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;\n                *fj       = nvterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// This method updates the particles by moving particle positions\n// according to the velocity field and time step. That is, for each\n// particle: p(t+1) = p(t) + dt * v(p(t)).\n__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    // gtidx is the domain location in x for this thread\n    cData pterm, vterm;\n\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                pterm  = part[fj];\n\n                int xvi = ((int)(pterm.x * dx));\n                int yvi = ((int)(pterm.y * dy));\n                vterm   = *((cData *)((char *)v + yvi * pitch) + xvi);\n\n                pterm.x += dt * vterm.x;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.x += 1.f;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.y += dt * vterm.y;\n                pterm.y = pterm.y - (int)pterm.y;\n                pterm.y += 1.f;\n                pterm.y = pterm.y - (int)pterm.y;\n\n                part[fj] = pterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// These are the external function calls necessary for launching fluid simuation\nextern \"C\" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r)\n{\n    dim3 tids(2 * r + 1, 2 * r + 1);\n\n    addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);\n    getLastCudaError(\"addForces_k failed.\");\n}\n\nextern \"C\" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n\n    dim3 tids(TIDSX, TIDSY);\n\n    updateTexture(v, DIM * sizeof(cData), DIM, tPitch);\n    advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY, texObj);\n    getLastCudaError(\"advectVelocity_k failed.\");\n}\n\nextern \"C\" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc)\n{\n    // Forward FFT\n    checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vx, (cufftComplex *)vx));\n    checkCudaErrors(cufftExecR2C(planr2c, (cufftReal *)vy, (cufftComplex *)vy));\n\n    uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);\n    uint3 tids = make_uint3(TIDSX, TIDSY, 1);\n\n    diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);\n    getLastCudaError(\"diffuseProject_k failed.\");\n\n    // Inverse FFT\n    checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vx, (cufftReal *)vx));\n    checkCudaErrors(cufftExecC2R(planc2r, (cufftComplex *)vy, (cufftReal *)vy));\n}\n\nextern \"C\" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"updateVelocity_k failed.\");\n}\n\nextern \"C\" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    cData *p;\n    checkCudaErrors(cudaGraphicsMapResources(1, &cuda_vbo_resource, 0));\n    getLastCudaError(\"cudaGraphicsMapResources failed\");\n\n    size_t num_bytes;\n    checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&p, &num_bytes, cuda_vbo_resource));\n    getLastCudaError(\"cudaGraphicsResourceGetMappedPointer failed\");\n\n    advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"advectParticles_k failed.\");\n\n    checkCudaErrors(cudaGraphicsUnmapResources(1, &cuda_vbo_resource, 0));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime.h>\n#include <hipfft/hipfft.h>       // CUDA FFT Libraries\n#include <hip/hip_runtime_api.h> // Helper functions for CUDA Error handling\n#include <stdio.h>\n#include <stdlib.h>\n\n// OpenGL Graphics includes\n#include <GLES3/gl31.h>\n\n// FluidsGLES CUDA kernel definitions\n#include \"fluidsGLES_kernels.cuh\"\n\n// Texture object for reading velocity field\nhipTextureObject_t texObj;\nstatic hipArray   *array = NULL;\n\n// Particle data\nextern GLuint                       vbo;               // OpenGL vertex buffer object\nextern struct hipGraphicsResource *cuda_vbo_resource; // handles OpenGL-CUDA exchange\n\n// Texture pitch\nextern size_t      tPitch;\nextern hipfftHandle planr2c;\nextern hipfftHandle planc2r;\ncData             *vxfield = NULL;\ncData             *vyfield = NULL;\n\nvoid setupTexture(int x, int y)\n{\n    hipChannelFormatDesc desc = hipCreateChannelDesc<float2>();\n\n    hipMallocArray(&array, &desc, y, x);\n    getLastCudaError(\"hipMalloc failed\");\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&texObj, &texRes, &texDescr, NULL));\n}\n\nvoid updateTexture(cData *data, size_t wib, size_t h, size_t pitch)\n{\n    checkCudaErrors(hipMemcpy2DToArray(array, 0, 0, data, pitch, wib, h, hipMemcpyDeviceToDevice));\n}\n\nvoid deleteTexture(void)\n{\n    checkCudaErrors(hipDestroyTextureObject(texObj));\n    checkCudaErrors(hipFreeArray(array));\n}\n\n// Note that these kernels are designed to work with arbitrary\n// domain sizes, not just domains that are multiples of the tile\n// size. Therefore, we have extra code that checks to make sure\n// a given thread location falls within the domain boundaries in\n// both X and Y. Also, the domain is covered by looping over\n// multiple elements in the Y direction, while there is a one-to-one\n// mapping between threads in X and the tile size in X.\n// Nolan Goodnight 9/22/06\n\n// This method adds constant force vectors to the velocity field\n// stored in 'v' according to v(x,t+1) = v(x,t) + dt * f.\n__global__ void addForces_k(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r, size_t pitch)\n{\n    int    tx = threadIdx.x;\n    int    ty = threadIdx.y;\n    cData *fj = (cData *)((char *)v + (ty + spy) * pitch) + tx + spx;\n\n    cData vterm = *fj;\n    tx -= r;\n    ty -= r;\n    float s = 1.f / (1.f + tx * tx * tx * tx + ty * ty * ty * ty);\n    vterm.x += s * fx;\n    vterm.y += s * fy;\n    *fj = vterm;\n}\n\n// This method performs the velocity advection step, where we\n// trace velocity vectors back in time to update each grid cell.\n// That is, v(x,t+1) = v(p(x,-dt),t). Here we perform bilinear\n// interpolation in the velocity space.\n__global__ void advectVelocity_k(cData              *v,\n                                 float              *vx,\n                                 float              *vy,\n                                 int                 dx,\n                                 int                 pdx,\n                                 int                 dy,\n                                 float               dt,\n                                 int                 lb,\n                                 hipTextureObject_t texObject)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData vterm, ploc;\n    float vxterm, vyterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * pdx + gtidx;\n                vterm  = tex2D<cData>(texObject, (float)gtidx, (float)fi);\n                ploc.x = (gtidx + 0.5f) - (dt * vterm.x * dx);\n                ploc.y = (fi + 0.5f) - (dt * vterm.y * dy);\n                vterm  = tex2D<cData>(texObject, ploc.x, ploc.y);\n                vxterm = vterm.x;\n                vyterm = vterm.y;\n                vx[fj] = vxterm;\n                vy[fj] = vyterm;\n            }\n        }\n    }\n}\n\n// This method performs velocity diffusion and forces mass conservation\n// in the frequency domain. The inputs 'vx' and 'vy' are complex-valued\n// arrays holding the Fourier coefficients of the velocity field in\n// X and Y. Diffusion in this space takes a simple form described as:\n// v(k,t) = v(k,t) / (1 + visc * dt * k^2), where visc is the viscosity,\n// and k is the wavenumber. The projection step forces the Fourier\n// velocity vectors to be orthogonal to the vectors for each\n// wavenumber: v(k,t) = v(k,t) - ((k dot v(k,t) * k) / k^2.\n__global__ void diffuseProject_k(cData *vx, cData *vy, int dx, int dy, float dt, float visc, int lb)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    cData xterm, yterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                xterm  = vx[fj];\n                yterm  = vy[fj];\n\n                // Compute the index of the wavenumber based on the\n                // data order produced by a standard NN FFT.\n                int iix = gtidx;\n                int iiy = (fi > dy / 2) ? (fi - (dy)) : fi;\n\n                // Velocity diffusion\n                float kk   = (float)(iix * iix + iiy * iiy); // k^2\n                float diff = 1.f / (1.f + visc * dt * kk);\n                xterm.x *= diff;\n                xterm.y *= diff;\n                yterm.x *= diff;\n                yterm.y *= diff;\n\n                // Velocity projection\n                if (kk > 0.f) {\n                    float rkk = 1.f / kk;\n                    // Real portion of velocity projection\n                    float rkp = (iix * xterm.x + iiy * yterm.x);\n                    // Imaginary portion of velocity projection\n                    float ikp = (iix * xterm.y + iiy * yterm.y);\n                    xterm.x -= rkk * rkp * iix;\n                    xterm.y -= rkk * ikp * iix;\n                    yterm.x -= rkk * rkp * iiy;\n                    yterm.y -= rkk * ikp * iiy;\n                }\n\n                vx[fj] = xterm;\n                vy[fj] = yterm;\n            }\n        }\n    }\n}\n\n// This method updates the velocity field 'v' using the two complex\n// arrays from the previous step: 'vx' and 'vy'. Here we scale the\n// real components by 1/(dx*dy) to account for an unnormalized FFT.\n__global__ void updateVelocity_k(cData *v, float *vx, float *vy, int dx, int pdx, int dy, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    float vxterm, vyterm;\n    cData nvterm;\n\n    // gtidx is the domain location in x for this thread\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fjr = fi * pdx + gtidx;\n                vxterm  = vx[fjr];\n                vyterm  = vy[fjr];\n\n                // Normalize the result of the inverse FFT\n                float scale = 1.f / (dx * dy);\n                nvterm.x    = vxterm * scale;\n                nvterm.y    = vyterm * scale;\n\n                cData *fj = (cData *)((char *)v + fi * pitch) + gtidx;\n                *fj       = nvterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// This method updates the particles by moving particle positions\n// according to the velocity field and time step. That is, for each\n// particle: p(t+1) = p(t) + dt * v(p(t)).\n__global__ void advectParticles_k(cData *part, cData *v, int dx, int dy, float dt, int lb, size_t pitch)\n{\n    int gtidx = blockIdx.x * blockDim.x + threadIdx.x;\n    int gtidy = blockIdx.y * (lb * blockDim.y) + threadIdx.y * lb;\n    int p;\n\n    // gtidx is the domain location in x for this thread\n    cData pterm, vterm;\n\n    if (gtidx < dx) {\n        for (p = 0; p < lb; p++) {\n            // fi is the domain location in y for this thread\n            int fi = gtidy + p;\n\n            if (fi < dy) {\n                int fj = fi * dx + gtidx;\n                pterm  = part[fj];\n\n                int xvi = ((int)(pterm.x * dx));\n                int yvi = ((int)(pterm.y * dy));\n                vterm   = *((cData *)((char *)v + yvi * pitch) + xvi);\n\n                pterm.x += dt * vterm.x;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.x += 1.f;\n                pterm.x = pterm.x - (int)pterm.x;\n                pterm.y += dt * vterm.y;\n                pterm.y = pterm.y - (int)pterm.y;\n                pterm.y += 1.f;\n                pterm.y = pterm.y - (int)pterm.y;\n\n                part[fj] = pterm;\n            }\n        } // If this thread is inside the domain in Y\n    } // If this thread is inside the domain in X\n}\n\n// These are the external function calls necessary for launching fluid simuation\nextern \"C\" void addForces(cData *v, int dx, int dy, int spx, int spy, float fx, float fy, int r)\n{\n    dim3 tids(2 * r + 1, 2 * r + 1);\n\n    addForces_k<<<1, tids>>>(v, dx, dy, spx, spy, fx, fy, r, tPitch);\n    getLastCudaError(\"addForces_k failed.\");\n}\n\nextern \"C\" void advectVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n\n    dim3 tids(TIDSX, TIDSY);\n\n    updateTexture(v, DIM * sizeof(cData), DIM, tPitch);\n    advectVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, dt, TILEY / TIDSY, texObj);\n    getLastCudaError(\"advectVelocity_k failed.\");\n}\n\nextern \"C\" void diffuseProject(cData *vx, cData *vy, int dx, int dy, float dt, float visc)\n{\n    // Forward FFT\n    checkCudaErrors(hipfftExecR2C(planr2c, (hipfftReal *)vx, (hipfftComplex *)vx));\n    checkCudaErrors(hipfftExecR2C(planr2c, (hipfftReal *)vy, (hipfftComplex *)vy));\n\n    uint3 grid = make_uint3((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1), 1);\n    uint3 tids = make_uint3(TIDSX, TIDSY, 1);\n\n    diffuseProject_k<<<grid, tids>>>(vx, vy, dx, dy, dt, visc, TILEY / TIDSY);\n    getLastCudaError(\"diffuseProject_k failed.\");\n\n    // Inverse FFT\n    checkCudaErrors(hipfftExecC2R(planc2r, (hipfftComplex *)vx, (hipfftReal *)vx));\n    checkCudaErrors(hipfftExecC2R(planc2r, (hipfftComplex *)vy, (hipfftReal *)vy));\n}\n\nextern \"C\" void updateVelocity(cData *v, float *vx, float *vy, int dx, int pdx, int dy)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    updateVelocity_k<<<grid, tids>>>(v, vx, vy, dx, pdx, dy, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"updateVelocity_k failed.\");\n}\n\nextern \"C\" void advectParticles(GLuint vbo, cData *v, int dx, int dy, float dt)\n{\n    dim3 grid((dx / TILEX) + (!(dx % TILEX) ? 0 : 1), (dy / TILEY) + (!(dy % TILEY) ? 0 : 1));\n    dim3 tids(TIDSX, TIDSY);\n\n    cData *p;\n    checkCudaErrors(hipGraphicsMapResources(1, &cuda_vbo_resource, 0));\n    getLastCudaError(\"hipGraphicsMapResources failed\");\n\n    size_t num_bytes;\n    checkCudaErrors(hipGraphicsResourceGetMappedPointer((void **)&p, &num_bytes, cuda_vbo_resource));\n    getLastCudaError(\"hipGraphicsResourceGetMappedPointer failed\");\n\n    advectParticles_k<<<grid, tids>>>(p, v, dx, dy, dt, TILEY / TIDSY, tPitch);\n    getLastCudaError(\"advectParticles_k failed.\");\n\n    checkCudaErrors(hipGraphicsUnmapResources(1, &cuda_vbo_resource, 0));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/fluidsGLES/fluidsGLES_kernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the CUDA C bindings to OpenGL ES to\n    dynamically modify a vertex buffer using a CUDA C kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with CUDA C\n    3. Map the VBO for writing from CUDA C\n    4. Run CUDA C kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL ES\n\n    Host code\n*/\n\n// includes, system\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid error_exit(const char *format, ...)\n{\n    va_list args;\n    va_start(args, format);\n    vfprintf(stderr, format, args);\n    va_end(args);\n    exit(1);\n}\n\n#if 0\n#include \"graphics_interface.c\"\n#else\n#include \"graphics_interface_egloutput_via_egl.c\"\n#endif\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <cuda_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <helper_cuda.h> // helper functions for CUDA error check\n                         // #include <helper_cuda_gl.h>      // helper functions for CUDA/GL interop\n\n#include <vector_types.h>\n\n#define MAX_EPSILON_ERROR 0.0f\n#define THRESHOLD         0.0f\n#define REFRESH_DELAY     1 // ms\n\n#define GUI_IDLE      0x100\n#define GUI_ROTATE    0x101\n#define GUI_TRANSLATE 0x102\n\nint gui_mode;\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// OpenGL ES variables and interop with CUDA C\nGLuint                       mesh_vao, mesh_vbo;\nstruct cudaGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// UI / mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Frame statistics\nint          frame;\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\n\n// Auto-Verification Code\nbool g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\n\n// CUDA functionality\nvoid runCuda(struct cudaGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGLES (VBO)\";\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda/OpenGL ES Interop (VBO): %3.1f fps (Max 1000 fps)\", avgFPS);\n    graphics_set_windowtitle(fps);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct cudaGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    cudaGraphicsMapResources(1, vbo_resource, 0);\n    size_t num_bytes;\n    cudaGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource);\n    // printf(\"Sample CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    cudaGraphicsUnmapResources(1, vbo_resource, 0);\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    cudaDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    cudaMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), cudaMemcpyDeviceToHost);\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display_thisframe(float time_delta)\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    // GET_GLERROR(0);\n\n    // set view matrix: broken, it doesn't work in OpenGL ES! Must put into shader\n    // glMatrixMode(GL_MODELVIEW);\n    // glLoadIdentity();\n    // glTranslatef(0.0, 0.0, translate_z);\n    // glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    // glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n\n    // GET_GLERROR(0);\n    glFinish();\n    // GET_GLERROR(0);\n\n    g_fAnim += time_delta;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        printf(\"%s: Mapping result buffer from OpenGL ES\\n\", __FUNCTION__);\n\n        cudaGraphicsUnregisterResource(cuda_vbo_resource);\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data =\n            (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, mesh_width * mesh_height * 4 * sizeof(float), GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        GET_GLERROR(0);\n    }\n}\n\nGLuint mesh_shader = 0;\n\nvoid readAndCompileShaderFromGLSLFile(GLuint new_shaderprogram, const char *filename, GLenum shaderType)\n{\n    FILE *file = fopen(filename, \"rb\"); // open shader text file\n    if (!file)\n        error_exit(\"Filename %s does not exist\\n\", filename);\n\n    /* get the size of the file and read it */\n    fseek(file, 0, SEEK_END);\n    GLint size = ftell(file);\n    char *data = (char *)malloc(sizeof(char) * (size + 1));\n    memset(data, 0, sizeof(char) * (size + 1));\n    fseek(file, 0, SEEK_SET);\n    size_t res = fread(data, 1, size, file);\n    fclose(file);\n\n    GLuint shader = glCreateShader(shaderType);\n    glShaderSource(shader, 1, (const GLchar **)&data, &size);\n    glCompileShader(shader);\n\n    GET_GLERROR(0);\n    GLint compile_success = 0;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_success);\n    GET_GLERROR(0);\n\n    if (compile_success == GL_FALSE) {\n        printf(\"Compilation of %s failed!\\n Reason:\\n\", filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        glDeleteShader(shader);\n        exit(1);\n    }\n\n    glAttachShader(new_shaderprogram, shader);\n    glDeleteShader(shader); // good to do?\n\n    free(data);\n}\n\nGLuint ShaderCreate(const char *vshader_filename, const char *fshader_filename)\n{\n    printf(\"Loading GLSL shaders %s %s\\n\", vshader_filename, fshader_filename);\n\n    GLuint new_shaderprogram = glCreateProgram();\n\n    GET_GLERROR(0);\n    if (vshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, vshader_filename, GL_VERTEX_SHADER);\n\n    GET_GLERROR(0);\n    if (fshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, fshader_filename, GL_FRAGMENT_SHADER);\n\n    GET_GLERROR(0);\n\n    glLinkProgram(new_shaderprogram);\n\n    GET_GLERROR(0);\n    GLint link_success;\n    glGetProgramiv(new_shaderprogram, GL_LINK_STATUS, &link_success);\n\n    if (link_success == GL_FALSE) {\n        printf(\"Linking of %s with %s failed!\\n Reason:\\n\", vshader_filename, fshader_filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(new_shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(new_shaderprogram, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        exit(EXIT_FAILURE);\n    }\n\n    return new_shaderprogram;\n}\n\n//===========================================================================\n// InitGraphicsState() - initialize OpenGL\n//===========================================================================\nstatic void InitGraphicsState(char **argv)\n{\n    char *GL_version  = (char *)glGetString(GL_VERSION);\n    char *GL_vendor   = (char *)glGetString(GL_VENDOR);\n    char *GL_renderer = (char *)glGetString(GL_RENDERER);\n\n    printf(\"Version: %s\\n\", GL_version);\n    printf(\"Vendor: %s\\n\", GL_vendor);\n    printf(\"Renderer: %s\\n\", GL_renderer);\n\n    // RENDERING SETUP (OpenGL ES or OpenGL Core Profile!)\n    glGenVertexArrays(1, &mesh_vao); // Features' Vertex Array Object allocation\n    glBindVertexArray(mesh_vao);     // bind VAO\n\n    // initialize buffer object\n    glGenBuffers(1, &mesh_vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo);\n\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);\n    glVertexAttribPointer((GLuint)0, 4, GL_FLOAT, GL_FALSE, 0, 0);\n    glEnableVertexAttribArray(0);\n\n    checkCudaErrors(cudaGraphicsGLRegisterBuffer(&cuda_vbo_resource, mesh_vbo, cudaGraphicsMapFlagsNone));\n\n    // glBindVertexArray(0); // keep above Vertex Array Object bound (it's the\n    // only one throughout)\n\n    // GLSL stuff\n    char *vertex_shader_path   = sdkFindFilePath(\"mesh.vert.glsl\", argv[0]);\n    char *fragment_shader_path = sdkFindFilePath(\"mesh.frag.glsl\", argv[0]);\n\n    if (vertex_shader_path == NULL || fragment_shader_path == NULL) {\n        printf(\"Error finding shader file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    mesh_shader = ShaderCreate(vertex_shader_path, fragment_shader_path);\n    GET_GLERROR(0);\n\n    free(vertex_shader_path);\n    free(fragment_shader_path);\n\n    glUseProgram(mesh_shader);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    int devID = 0;\n#if defined(__aarch64__) || defined(__arm__)\n    // find iGPU on the system which is compute capable which will perform\n    // GLES-CUDA interop\n    devID = findIntegratedGPU();\n#else\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    devID = findCudaDevice(argc, (const char **)argv);\n#endif\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(cudaMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, mesh_vbo);\n\n        cudaFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // this would use command-line specified CUDA device, note that CUDA\n        // defaults to highest Gflops/s device\n        if (checkCmdLineFlag(argc, (const char **)argv, \"device\"))\n            error_exit(\"Device setting not yet implemented!\\n\");\n\n        // create X11 window and set up associated OpenGL ES context\n        graphics_setup_window(0, 0, window_width, window_height, sSDKsample);\n\n        InitGraphicsState(argv); // set up GLES stuff\n\n        glClearColor(0, 0.5, 1, 1); // blue-ish background\n        glClear(GL_COLOR_BUFFER_BIT);\n\n        // printf(\"WP%d\\n\", __LINE__);\n        graphics_swap_buffers();\n\n        int frame = 0;\n\n        while (frame < 1000) {\n            display_thisframe(0.010);\n            usleep(1000); // need not take full CPU and GPU\n\n            graphics_swap_buffers();\n            // printf(\"frame %d\\n\",frame++);\n        }\n\n        // NOTE: Before destroying OpenGL ES context, must unregister all shared\n        // resources from CUDA !\n        cudaGraphicsUnregisterResource(cuda_vbo_resource);\n\n        graphics_close_window(); // close window and destroy OpenGL ES context\n\n        sdkDeleteTimer(&timer);\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we run without OpenGL and see if VBO is generated\n            // correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    This example demonstrates how to use the CUDA C bindings to OpenGL ES to\n    dynamically modify a vertex buffer using a CUDA C kernel.\n\n    The steps are:\n    1. Create an empty vertex buffer object (VBO)\n    2. Register the VBO with CUDA C\n    3. Map the VBO for writing from CUDA C\n    4. Run CUDA C kernel to modify the vertex positions\n    5. Unmap the VBO\n    6. Render the results using OpenGL ES\n\n    Host code\n*/\n\n// includes, system\n#include <math.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\nvoid error_exit(const char *format, ...)\n{\n    va_list args;\n    va_start(args, format);\n    vfprintf(stderr, format, args);\n    va_end(args);\n    exit(1);\n}\n\n#if 0\n#include \"graphics_interface.c\"\n#else\n#include \"graphics_interface_egloutput_via_egl.c\"\n#endif\n\n#ifdef _WIN32\n#define WINDOWS_LEAN_AND_MEAN\n#define NOMINMAX\n#include <windows.h>\n#endif\n\n// includes, cuda\n#include <cuda_gl_interop.h>\n#include <hip/hip_runtime.h>\n\n// Utilities and timing functions\n#include <helper_functions.h> // includes cuda.h and cuda_runtime_api.h\n\n// CUDA helper functions\n#include <hip/hip_runtime_api.h> // helper functions for CUDA error check\n                         // #include <helper_cuda_gl.h>      // helper functions for CUDA/GL interop\n\n#include <hip/hip_vector_types.h>\n\n#define MAX_EPSILON_ERROR 0.0f\n#define THRESHOLD         0.0f\n#define REFRESH_DELAY     1 // ms\n\n#define GUI_IDLE      0x100\n#define GUI_ROTATE    0x101\n#define GUI_TRANSLATE 0x102\n\nint gui_mode;\n\n////////////////////////////////////////////////////////////////////////////////\n// constants\nconst unsigned int window_width  = 512;\nconst unsigned int window_height = 512;\n\nconst unsigned int mesh_width  = 256;\nconst unsigned int mesh_height = 256;\n\n// OpenGL ES variables and interop with CUDA C\nGLuint                       mesh_vao, mesh_vbo;\nstruct hipGraphicsResource *cuda_vbo_resource;\nvoid                        *d_vbo_buffer = NULL;\n\nfloat g_fAnim = 0.0;\n\n// UI / mouse controls\nint   mouse_old_x, mouse_old_y;\nint   mouse_buttons = 0;\nfloat rotate_x = 0.0, rotate_y = 0.0;\nfloat translate_z = -3.0;\n\nStopWatchInterface *timer = NULL;\n\n// Frame statistics\nint          frame;\nint          fpsCount      = 0; // FPS count for averaging\nint          fpsLimit      = 1; // FPS limit for sampling\nint          g_Index       = 0;\nfloat        avgFPS        = 0.0f;\nunsigned int frameCount    = 0;\nunsigned int g_TotalErrors = 0;\n\n// Auto-Verification Code\nbool g_bQAReadback = false;\n\nint   *pArgc = NULL;\nchar **pArgv = NULL;\n\n#define MAX(a, b) ((a > b) ? a : b)\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\n\n// CUDA functionality\nvoid runCuda(struct hipGraphicsResource **vbo_resource);\nvoid runAutoTest(int devID, char **argv, char *ref_file);\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo);\n\nconst char *sSDKsample = \"simpleGLES (VBO)\";\n\nvoid computeFPS()\n{\n    frameCount++;\n    fpsCount++;\n\n    if (fpsCount == fpsLimit) {\n        avgFPS   = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);\n        fpsCount = 0;\n        fpsLimit = (int)MAX(avgFPS, 1.f);\n\n        sdkResetTimer(&timer);\n    }\n\n    char fps[256];\n    sprintf(fps, \"Cuda/OpenGL ES Interop (VBO): %3.1f fps (Max 1000 fps)\", avgFPS);\n    graphics_set_windowtitle(fps);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//! Simple kernel to modify vertex positions in sine wave pattern\n//! @param data  data in global memory\n///////////////////////////////////////////////////////////////////////////////\n__global__ void simple_vbo_kernel(float4 *pos, unsigned int width, unsigned int height, float time)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    // calculate uv coordinates\n    float u = x / (float)width;\n    float v = y / (float)height;\n    u       = u * 2.0f - 1.0f;\n    v       = v * 2.0f - 1.0f;\n\n    // calculate simple sine wave pattern\n    float freq = 4.0f;\n    float w    = sinf(u * freq + time) * cosf(v * freq + time) * 0.5f;\n\n    // write output vertex\n    pos[y * width + x] = make_float4(u, w, v, 1.0f);\n}\n\nvoid launch_kernel(float4 *pos, unsigned int mesh_width, unsigned int mesh_height, float time)\n{\n    // execute the kernel\n    dim3 block(8, 8, 1);\n    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    simple_vbo_kernel<<<grid, block>>>(pos, mesh_width, mesh_height, time);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runCuda(struct hipGraphicsResource **vbo_resource)\n{\n    // map OpenGL buffer object for writing from CUDA\n    float4 *dptr;\n    hipGraphicsMapResources(1, vbo_resource, 0);\n    size_t num_bytes;\n    hipGraphicsResourceGetMappedPointer((void **)&dptr, &num_bytes, *vbo_resource);\n    // printf(\"Sample CUDA mapped VBO: May access %ld bytes\\n\", num_bytes);\n\n    // execute the kernel\n    //    dim3 block(8, 8, 1);\n    //    dim3 grid(mesh_width / block.x, mesh_height / block.y, 1);\n    //    kernel<<< grid, block>>>(dptr, mesh_width, mesh_height, g_fAnim);\n\n    launch_kernel(dptr, mesh_width, mesh_height, g_fAnim);\n\n    // unmap buffer object\n    hipGraphicsUnmapResources(1, vbo_resource, 0);\n}\n\n#ifdef _WIN32\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)\n#endif\n#else\n#ifndef FOPEN\n#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))\n#endif\n#endif\n\nvoid sdkDumpBin2(void *data, unsigned int bytes, const char *filename)\n{\n    printf(\"sdkDumpBin: <%s>\\n\", filename);\n    FILE *fp;\n    FOPEN(fp, filename, \"wb\");\n    fwrite(data, bytes, 1, fp);\n    fflush(fp);\n    fclose(fp);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run the Cuda part of the computation\n////////////////////////////////////////////////////////////////////////////////\nvoid runAutoTest(int devID, char **argv, char *ref_file)\n{\n    char *reference_file = NULL;\n    void *imageData      = malloc(mesh_width * mesh_height * sizeof(float));\n\n    // execute the kernel\n    launch_kernel((float4 *)d_vbo_buffer, mesh_width, mesh_height, g_fAnim);\n\n    hipDeviceSynchronize();\n    getLastCudaError(\"launch_kernel failed\");\n\n    hipMemcpy(imageData, d_vbo_buffer, mesh_width * mesh_height * sizeof(float), hipMemcpyDeviceToHost);\n\n    sdkDumpBin2(imageData, mesh_width * mesh_height * sizeof(float), \"simpleGL.bin\");\n    reference_file = sdkFindFilePath(ref_file, argv[0]);\n\n    if (reference_file\n        && !sdkCompareBin2BinFloat(\"simpleGL.bin\",\n                                   reference_file,\n                                   mesh_width * mesh_height * sizeof(float),\n                                   MAX_EPSILON_ERROR,\n                                   THRESHOLD,\n                                   pArgv[0])) {\n        g_TotalErrors++;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Display callback\n////////////////////////////////////////////////////////////////////////////////\nvoid display_thisframe(float time_delta)\n{\n    sdkStartTimer(&timer);\n\n    // run CUDA kernel to generate vertex positions\n    runCuda(&cuda_vbo_resource);\n\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    // GET_GLERROR(0);\n\n    // set view matrix: broken, it doesn't work in OpenGL ES! Must put into shader\n    // glMatrixMode(GL_MODELVIEW);\n    // glLoadIdentity();\n    // glTranslatef(0.0, 0.0, translate_z);\n    // glRotatef(rotate_x, 1.0, 0.0, 0.0);\n    // glRotatef(rotate_y, 0.0, 1.0, 0.0);\n\n    glDrawArrays(GL_POINTS, 0, mesh_width * mesh_height);\n\n    // GET_GLERROR(0);\n    glFinish();\n    // GET_GLERROR(0);\n\n    g_fAnim += time_delta;\n\n    sdkStopTimer(&timer);\n    computeFPS();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Check if the result is correct or write data to file for external\n//! regression testing\n////////////////////////////////////////////////////////////////////////////////\nvoid checkResultCuda(int argc, char **argv, const GLuint &vbo)\n{\n    if (!d_vbo_buffer) {\n        printf(\"%s: Mapping result buffer from OpenGL ES\\n\", __FUNCTION__);\n\n        hipGraphicsUnregisterResource(cuda_vbo_resource);\n\n        // map buffer object\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        float *data =\n            (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, mesh_width * mesh_height * 4 * sizeof(float), GL_READ_ONLY);\n\n        // check result\n        if (checkCmdLineFlag(argc, (const char **)argv, \"regression\")) {\n            // write file for regression test\n            sdkWriteFile<float>(\"./data/regression.dat\", data, mesh_width * mesh_height * 3, 0.0, false);\n        }\n\n        // unmap GL buffer object\n        if (!glUnmapBuffer(GL_ARRAY_BUFFER)) {\n            fprintf(stderr, \"Unmap buffer failed.\\n\");\n            fflush(stderr);\n        }\n\n        checkCudaErrors(hipGraphicsGLRegisterBuffer(&cuda_vbo_resource, vbo, cudaGraphicsMapFlagsWriteDiscard));\n\n        GET_GLERROR(0);\n    }\n}\n\nGLuint mesh_shader = 0;\n\nvoid readAndCompileShaderFromGLSLFile(GLuint new_shaderprogram, const char *filename, GLenum shaderType)\n{\n    FILE *file = fopen(filename, \"rb\"); // open shader text file\n    if (!file)\n        error_exit(\"Filename %s does not exist\\n\", filename);\n\n    /* get the size of the file and read it */\n    fseek(file, 0, SEEK_END);\n    GLint size = ftell(file);\n    char *data = (char *)malloc(sizeof(char) * (size + 1));\n    memset(data, 0, sizeof(char) * (size + 1));\n    fseek(file, 0, SEEK_SET);\n    size_t res = fread(data, 1, size, file);\n    fclose(file);\n\n    GLuint shader = glCreateShader(shaderType);\n    glShaderSource(shader, 1, (const GLchar **)&data, &size);\n    glCompileShader(shader);\n\n    GET_GLERROR(0);\n    GLint compile_success = 0;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_success);\n    GET_GLERROR(0);\n\n    if (compile_success == GL_FALSE) {\n        printf(\"Compilation of %s failed!\\n Reason:\\n\", filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        glDeleteShader(shader);\n        exit(1);\n    }\n\n    glAttachShader(new_shaderprogram, shader);\n    glDeleteShader(shader); // good to do?\n\n    free(data);\n}\n\nGLuint ShaderCreate(const char *vshader_filename, const char *fshader_filename)\n{\n    printf(\"Loading GLSL shaders %s %s\\n\", vshader_filename, fshader_filename);\n\n    GLuint new_shaderprogram = glCreateProgram();\n\n    GET_GLERROR(0);\n    if (vshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, vshader_filename, GL_VERTEX_SHADER);\n\n    GET_GLERROR(0);\n    if (fshader_filename)\n        readAndCompileShaderFromGLSLFile(new_shaderprogram, fshader_filename, GL_FRAGMENT_SHADER);\n\n    GET_GLERROR(0);\n\n    glLinkProgram(new_shaderprogram);\n\n    GET_GLERROR(0);\n    GLint link_success;\n    glGetProgramiv(new_shaderprogram, GL_LINK_STATUS, &link_success);\n\n    if (link_success == GL_FALSE) {\n        printf(\"Linking of %s with %s failed!\\n Reason:\\n\", vshader_filename, fshader_filename);\n\n        GLint maxLength = 0;\n        glGetShaderiv(new_shaderprogram, GL_INFO_LOG_LENGTH, &maxLength);\n\n        char errorLog[maxLength];\n        glGetShaderInfoLog(new_shaderprogram, maxLength, &maxLength, &errorLog[0]);\n\n        printf(\"%s\", errorLog);\n\n        exit(EXIT_FAILURE);\n    }\n\n    return new_shaderprogram;\n}\n\n//===========================================================================\n// InitGraphicsState() - initialize OpenGL\n//===========================================================================\nstatic void InitGraphicsState(char **argv)\n{\n    char *GL_version  = (char *)glGetString(GL_VERSION);\n    char *GL_vendor   = (char *)glGetString(GL_VENDOR);\n    char *GL_renderer = (char *)glGetString(GL_RENDERER);\n\n    printf(\"Version: %s\\n\", GL_version);\n    printf(\"Vendor: %s\\n\", GL_vendor);\n    printf(\"Renderer: %s\\n\", GL_renderer);\n\n    // RENDERING SETUP (OpenGL ES or OpenGL Core Profile!)\n    glGenVertexArrays(1, &mesh_vao); // Features' Vertex Array Object allocation\n    glBindVertexArray(mesh_vao);     // bind VAO\n\n    // initialize buffer object\n    glGenBuffers(1, &mesh_vbo);\n    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo);\n\n    unsigned int size = mesh_width * mesh_height * 4 * sizeof(float);\n    glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);\n    glVertexAttribPointer((GLuint)0, 4, GL_FLOAT, GL_FALSE, 0, 0);\n    glEnableVertexAttribArray(0);\n\n    checkCudaErrors(hipGraphicsGLRegisterBuffer(&cuda_vbo_resource, mesh_vbo, cudaGraphicsMapFlagsNone));\n\n    // glBindVertexArray(0); // keep above Vertex Array Object bound (it's the\n    // only one throughout)\n\n    // GLSL stuff\n    char *vertex_shader_path   = sdkFindFilePath(\"mesh.vert.glsl\", argv[0]);\n    char *fragment_shader_path = sdkFindFilePath(\"mesh.frag.glsl\", argv[0]);\n\n    if (vertex_shader_path == NULL || fragment_shader_path == NULL) {\n        printf(\"Error finding shader file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    mesh_shader = ShaderCreate(vertex_shader_path, fragment_shader_path);\n    GET_GLERROR(0);\n\n    free(vertex_shader_path);\n    free(fragment_shader_path);\n\n    glUseProgram(mesh_shader);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, char *ref_file)\n{\n    // Create the CUTIL timer\n    sdkCreateTimer(&timer);\n\n    int devID = 0;\n#if defined(__aarch64__) || defined(__arm__)\n    // find iGPU on the system which is compute capable which will perform\n    // GLES-CUDA interop\n    devID = findIntegratedGPU();\n#else\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    devID = findCudaDevice(argc, (const char **)argv);\n#endif\n\n    // command line mode only\n    if (ref_file != NULL) {\n        // create VBO\n        checkCudaErrors(hipMalloc((void **)&d_vbo_buffer, mesh_width * mesh_height * 4 * sizeof(float)));\n\n        // run the cuda part\n        runAutoTest(devID, argv, ref_file);\n\n        // check result of Cuda step\n        checkResultCuda(argc, argv, mesh_vbo);\n\n        hipFree(d_vbo_buffer);\n        d_vbo_buffer = NULL;\n    }\n    else {\n        // this would use command-line specified CUDA device, note that CUDA\n        // defaults to highest Gflops/s device\n        if (checkCmdLineFlag(argc, (const char **)argv, \"device\"))\n            error_exit(\"Device setting not yet implemented!\\n\");\n\n        // create X11 window and set up associated OpenGL ES context\n        graphics_setup_window(0, 0, window_width, window_height, sSDKsample);\n\n        InitGraphicsState(argv); // set up GLES stuff\n\n        glClearColor(0, 0.5, 1, 1); // blue-ish background\n        glClear(GL_COLOR_BUFFER_BIT);\n\n        // printf(\"WP%d\\n\", __LINE__);\n        graphics_swap_buffers();\n\n        int frame = 0;\n\n        while (frame < 1000) {\n            display_thisframe(0.010);\n            usleep(1000); // need not take full CPU and GPU\n\n            graphics_swap_buffers();\n            // printf(\"frame %d\\n\",frame++);\n        }\n\n        // NOTE: Before destroying OpenGL ES context, must unregister all shared\n        // resources from CUDA !\n        hipGraphicsUnregisterResource(cuda_vbo_resource);\n\n        graphics_close_window(); // close window and destroy OpenGL ES context\n\n        sdkDeleteTimer(&timer);\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    char *ref_file = NULL;\n\n    pArgc = &argc;\n    pArgv = argv;\n\n#if defined(__linux__)\n    setenv(\"DISPLAY\", \":0\", 0);\n#endif\n\n    printf(\"%s starting...\\n\", sSDKsample);\n\n    if (argc > 1) {\n        if (checkCmdLineFlag(argc, (const char **)argv, \"file\")) {\n            // In this mode, we run without OpenGL and see if VBO is generated\n            // correctly\n            getCmdLineArgumentString(argc, (const char **)argv, \"file\", (char **)&ref_file);\n        }\n    }\n\n    printf(\"\\n\");\n\n    runTest(argc, argv, ref_file);\n\n    printf(\"%s completed, returned %s\\n\", sSDKsample, (g_TotalErrors == 0) ? \"OK\" : \"ERROR!\");\n\n    exit(g_TotalErrors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/simpleGLES_EGLOutput/simpleGLES_EGLOutput.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <helper_cuda.h>\n#include <math.h>\n\n// #include <GL/glew.h>\n// #include <GL/freeglut.h>\n\n// CUDA standard includes\n#include <cuda_runtime.h>\n// #include <cuda_gl_interop.h>\n\n#include \"bodysystem.h\"\n\n__constant__ float  softeningSquared;\n__constant__ double softeningSquared_fp64;\n\ncudaError_t setSofteningSquared(float softeningSq)\n{\n    return cudaMemcpyToSymbol(softeningSquared, &softeningSq, sizeof(float), 0, cudaMemcpyHostToDevice);\n}\n\ncudaError_t setSofteningSquared(double softeningSq)\n{\n    return cudaMemcpyToSymbol(softeningSquared_fp64, &softeningSq, sizeof(double), 0, cudaMemcpyHostToDevice);\n}\n\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\ntemplate <typename T> __device__ T rsqrt_T(T x) { return rsqrt(x); }\n\ntemplate <> __device__ float rsqrt_T<float>(float x) { return rsqrtf(x); }\n\ntemplate <> __device__ double rsqrt_T<double>(double x) { return rsqrt(x); }\n\n// Macros to simplify shared memory addressing\n#define SX(i) sharedPos[i + blockDim.x * threadIdx.y]\n// This macro is only used when multithreadBodies is true (below)\n#define SX_SUM(i, j) sharedPos[i + blockDim.x * j]\n\ntemplate <typename T> __device__ T getSofteningSquared() { return softeningSquared; }\ntemplate <> __device__ double      getSofteningSquared<double>() { return softeningSquared_fp64; }\n\ntemplate <typename T> struct DeviceData\n{\n    T           *dPos[2]; // mapped host pointers\n    T           *dVel;\n    cudaEvent_t  event;\n    unsigned int offset;\n    unsigned int numBodies;\n};\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\nbodyBodyInteraction(typename vec3<T>::Type ai, typename vec4<T>::Type bi, typename vec4<T>::Type bj)\n{\n    typename vec3<T>::Type r;\n\n    // r_ij  [3 FLOPS]\n    r.x = bj.x - bi.x;\n    r.y = bj.y - bi.y;\n    r.z = bj.z - bi.z;\n\n    // distSqr = dot(r_ij, r_ij) + EPS^2  [6 FLOPS]\n    T distSqr = r.x * r.x + r.y * r.y + r.z * r.z;\n    distSqr += getSofteningSquared<T>();\n\n    // invDistCube =1/distSqr^(3/2)  [4 FLOPS (2 mul, 1 sqrt, 1 inv)]\n    T invDist     = rsqrt_T(distSqr);\n    T invDistCube = invDist * invDist * invDist;\n\n    // s = m_j * invDistCube [1 FLOP]\n    T s = bj.w * invDistCube;\n\n    // a_i =  a_i + s * r_ij [6 FLOPS]\n    ai.x += r.x * s;\n    ai.y += r.y * s;\n    ai.z += r.z * s;\n\n    return ai;\n}\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\ncomputeBodyAccel(typename vec4<T>::Type bodyPos, typename vec4<T>::Type *positions, int numTiles)\n{\n    typename vec4<T>::Type *sharedPos = SharedMemory<typename vec4<T>::Type>();\n\n    typename vec3<T>::Type acc = {0.0f, 0.0f, 0.0f};\n\n    for (int tile = 0; tile < numTiles; tile++) {\n        sharedPos[threadIdx.x] = positions[tile * blockDim.x + threadIdx.x];\n\n        __syncthreads();\n\n        // This is the \"tile_calculation\" from the GPUG3 article.\n#pragma unroll 128\n\n        for (unsigned int counter = 0; counter < blockDim.x; counter++) {\n            acc = bodyBodyInteraction<T>(acc, bodyPos, sharedPos[counter]);\n        }\n\n        __syncthreads();\n    }\n\n    return acc;\n}\n\ntemplate <typename T>\n__global__ void integrateBodies(typename vec4<T>::Type *__restrict__ newPos,\n                                typename vec4<T>::Type *__restrict__ oldPos,\n                                typename vec4<T>::Type *vel,\n                                unsigned int            deviceOffset,\n                                unsigned int            deviceNumBodies,\n                                float                   deltaTime,\n                                float                   damping,\n                                int                     numTiles)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (index >= deviceNumBodies) {\n        return;\n    }\n\n    typename vec4<T>::Type position = oldPos[deviceOffset + index];\n\n    typename vec3<T>::Type accel = computeBodyAccel<T>(position, oldPos, numTiles);\n\n    // acceleration = force / mass;\n    // new velocity = old velocity + acceleration * deltaTime\n    // note we factor out the body's mass from the equation, here and in\n    // bodyBodyInteraction (because they cancel out).  Thus here force ==\n    // acceleration\n    typename vec4<T>::Type velocity = vel[deviceOffset + index];\n\n    velocity.x += accel.x * deltaTime;\n    velocity.y += accel.y * deltaTime;\n    velocity.z += accel.z * deltaTime;\n\n    velocity.x *= damping;\n    velocity.y *= damping;\n    velocity.z *= damping;\n\n    // new position = old position + velocity * deltaTime\n    position.x += velocity.x * deltaTime;\n    position.y += velocity.y * deltaTime;\n    position.z += velocity.z * deltaTime;\n\n    // store new position and velocity\n    newPos[deviceOffset + index] = position;\n    vel[deviceOffset + index]    = velocity;\n}\n\ntemplate <typename T>\nvoid integrateNbodySystem(DeviceData<T>         *deviceData,\n                          cudaGraphicsResource **pgres,\n                          unsigned int           currentRead,\n                          float                  deltaTime,\n                          float                  damping,\n                          unsigned int           numBodies,\n                          unsigned int           numDevices,\n                          int                    blockSize,\n                          bool                   bUsePBO)\n{\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[currentRead], cudaGraphicsMapFlagsReadOnly));\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[1 - currentRead], cudaGraphicsMapFlagsWriteDiscard));\n        checkCudaErrors(cudaGraphicsMapResources(2, pgres, 0));\n        size_t bytes;\n        checkCudaErrors(cudaGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[currentRead]), &bytes, pgres[currentRead]));\n        checkCudaErrors(cudaGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[1 - currentRead]), &bytes, pgres[1 - currentRead]));\n    }\n\n    for (unsigned int dev = 0; dev != numDevices; dev++) {\n        if (numDevices > 1) {\n            cudaSetDevice(dev);\n        }\n\n        int numBlocks     = (deviceData[dev].numBodies + blockSize - 1) / blockSize;\n        int numTiles      = (numBodies + blockSize - 1) / blockSize;\n        int sharedMemSize = blockSize * 4 * sizeof(T); // 4 floats for pos\n\n        integrateBodies<T>\n            <<<numBlocks, blockSize, sharedMemSize>>>((typename vec4<T>::Type *)deviceData[dev].dPos[1 - currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dPos[currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dVel,\n                                                      deviceData[dev].offset,\n                                                      deviceData[dev].numBodies,\n                                                      deltaTime,\n                                                      damping,\n                                                      numTiles);\n\n        if (numDevices > 1) {\n            checkCudaErrors(cudaEventRecord(deviceData[dev].event));\n            // MJH: Hack on older driver versions to force kernel launches to flush!\n            cudaStreamQuery(0);\n        }\n\n        // check if kernel invocation generated an error\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    if (numDevices > 1) {\n        for (unsigned int dev = 0; dev < numDevices; dev++) {\n            checkCudaErrors(cudaEventSynchronize(deviceData[dev].event));\n        }\n    }\n\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsUnmapResources(2, pgres, 0));\n    }\n}\n\n// Explicit specializations needed to generate code\ntemplate void integrateNbodySystem<float>(DeviceData<float>     *deviceData,\n                                          cudaGraphicsResource **pgres,\n                                          unsigned int           currentRead,\n                                          float                  deltaTime,\n                                          float                  damping,\n                                          unsigned int           numBodies,\n                                          unsigned int           numDevices,\n                                          int                    blockSize,\n                                          bool                   bUsePBO);\n\ntemplate void integrateNbodySystem<double>(DeviceData<double>    *deviceData,\n                                           cudaGraphicsResource **pgres,\n                                           unsigned int           currentRead,\n                                           float                  deltaTime,\n                                           float                  damping,\n                                           unsigned int           numBodies,\n                                           unsigned int           numDevices,\n                                           int                    blockSize,\n                                           bool                   bUsePBO);\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime_api.h>\n#include <math.h>\n\n// #include <GL/glew.h>\n// #include <GL/freeglut.h>\n\n// CUDA standard includes\n#include <hip/hip_runtime.h>\n// #include <cuda_gl_interop.h>\n\n#include \"bodysystem.h\"\n\n__constant__ float  softeningSquared;\n__constant__ double softeningSquared_fp64;\n\nhipError_t setSofteningSquared(float softeningSq)\n{\n    return hipMemcpyToSymbol(HIP_SYMBOL(softeningSquared), &softeningSq, sizeof(float), 0, hipMemcpyHostToDevice);\n}\n\nhipError_t setSofteningSquared(double softeningSq)\n{\n    return hipMemcpyToSymbol(HIP_SYMBOL(softeningSquared_fp64), &softeningSq, sizeof(double), 0, hipMemcpyHostToDevice);\n}\n\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\ntemplate <typename T> __device__ T rsqrt_T(T x) { return rsqrt(x); }\n\ntemplate <> __device__ float rsqrt_T<float>(float x) { return rsqrtf(x); }\n\ntemplate <> __device__ double rsqrt_T<double>(double x) { return rsqrt(x); }\n\n// Macros to simplify shared memory addressing\n#define SX(i) sharedPos[i + blockDim.x * threadIdx.y]\n// This macro is only used when multithreadBodies is true (below)\n#define SX_SUM(i, j) sharedPos[i + blockDim.x * j]\n\ntemplate <typename T> __device__ T getSofteningSquared() { return softeningSquared; }\ntemplate <> __device__ double      getSofteningSquared<double>() { return softeningSquared_fp64; }\n\ntemplate <typename T> struct DeviceData\n{\n    T           *dPos[2]; // mapped host pointers\n    T           *dVel;\n    hipEvent_t  event;\n    unsigned int offset;\n    unsigned int numBodies;\n};\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\nbodyBodyInteraction(typename vec3<T>::Type ai, typename vec4<T>::Type bi, typename vec4<T>::Type bj)\n{\n    typename vec3<T>::Type r;\n\n    // r_ij  [3 FLOPS]\n    r.x = bj.x - bi.x;\n    r.y = bj.y - bi.y;\n    r.z = bj.z - bi.z;\n\n    // distSqr = dot(r_ij, r_ij) + EPS^2  [6 FLOPS]\n    T distSqr = r.x * r.x + r.y * r.y + r.z * r.z;\n    distSqr += getSofteningSquared<T>();\n\n    // invDistCube =1/distSqr^(3/2)  [4 FLOPS (2 mul, 1 sqrt, 1 inv)]\n    T invDist     = rsqrt_T(distSqr);\n    T invDistCube = invDist * invDist * invDist;\n\n    // s = m_j * invDistCube [1 FLOP]\n    T s = bj.w * invDistCube;\n\n    // a_i =  a_i + s * r_ij [6 FLOPS]\n    ai.x += r.x * s;\n    ai.y += r.y * s;\n    ai.z += r.z * s;\n\n    return ai;\n}\n\ntemplate <typename T>\n__device__ typename vec3<T>::Type\ncomputeBodyAccel(typename vec4<T>::Type bodyPos, typename vec4<T>::Type *positions, int numTiles)\n{\n    typename vec4<T>::Type *sharedPos = SharedMemory<typename vec4<T>::Type>();\n\n    typename vec3<T>::Type acc = {0.0f, 0.0f, 0.0f};\n\n    for (int tile = 0; tile < numTiles; tile++) {\n        sharedPos[threadIdx.x] = positions[tile * blockDim.x + threadIdx.x];\n\n        __syncthreads();\n\n        // This is the \"tile_calculation\" from the GPUG3 article.\n#pragma unroll 128\n\n        for (unsigned int counter = 0; counter < blockDim.x; counter++) {\n            acc = bodyBodyInteraction<T>(acc, bodyPos, sharedPos[counter]);\n        }\n\n        __syncthreads();\n    }\n\n    return acc;\n}\n\ntemplate <typename T>\n__global__ void integrateBodies(typename vec4<T>::Type *__restrict__ newPos,\n                                typename vec4<T>::Type *__restrict__ oldPos,\n                                typename vec4<T>::Type *vel,\n                                unsigned int            deviceOffset,\n                                unsigned int            deviceNumBodies,\n                                float                   deltaTime,\n                                float                   damping,\n                                int                     numTiles)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (index >= deviceNumBodies) {\n        return;\n    }\n\n    typename vec4<T>::Type position = oldPos[deviceOffset + index];\n\n    typename vec3<T>::Type accel = computeBodyAccel<T>(position, oldPos, numTiles);\n\n    // acceleration = force / mass;\n    // new velocity = old velocity + acceleration * deltaTime\n    // note we factor out the body's mass from the equation, here and in\n    // bodyBodyInteraction (because they cancel out).  Thus here force ==\n    // acceleration\n    typename vec4<T>::Type velocity = vel[deviceOffset + index];\n\n    velocity.x += accel.x * deltaTime;\n    velocity.y += accel.y * deltaTime;\n    velocity.z += accel.z * deltaTime;\n\n    velocity.x *= damping;\n    velocity.y *= damping;\n    velocity.z *= damping;\n\n    // new position = old position + velocity * deltaTime\n    position.x += velocity.x * deltaTime;\n    position.y += velocity.y * deltaTime;\n    position.z += velocity.z * deltaTime;\n\n    // store new position and velocity\n    newPos[deviceOffset + index] = position;\n    vel[deviceOffset + index]    = velocity;\n}\n\ntemplate <typename T>\nvoid integrateNbodySystem(DeviceData<T>         *deviceData,\n                          hipGraphicsResource **pgres,\n                          unsigned int           currentRead,\n                          float                  deltaTime,\n                          float                  damping,\n                          unsigned int           numBodies,\n                          unsigned int           numDevices,\n                          int                    blockSize,\n                          bool                   bUsePBO)\n{\n    if (bUsePBO) {\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[currentRead], cudaGraphicsMapFlagsReadOnly));\n        checkCudaErrors(cudaGraphicsResourceSetMapFlags(pgres[1 - currentRead], cudaGraphicsMapFlagsWriteDiscard));\n        checkCudaErrors(hipGraphicsMapResources(2, pgres, 0));\n        size_t bytes;\n        checkCudaErrors(hipGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[currentRead]), &bytes, pgres[currentRead]));\n        checkCudaErrors(hipGraphicsResourceGetMappedPointer(\n            (void **)&(deviceData[0].dPos[1 - currentRead]), &bytes, pgres[1 - currentRead]));\n    }\n\n    for (unsigned int dev = 0; dev != numDevices; dev++) {\n        if (numDevices > 1) {\n            hipSetDevice(dev);\n        }\n\n        int numBlocks     = (deviceData[dev].numBodies + blockSize - 1) / blockSize;\n        int numTiles      = (numBodies + blockSize - 1) / blockSize;\n        int sharedMemSize = blockSize * 4 * sizeof(T); // 4 floats for pos\n\n        integrateBodies<T>\n            <<<numBlocks, blockSize, sharedMemSize>>>((typename vec4<T>::Type *)deviceData[dev].dPos[1 - currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dPos[currentRead],\n                                                      (typename vec4<T>::Type *)deviceData[dev].dVel,\n                                                      deviceData[dev].offset,\n                                                      deviceData[dev].numBodies,\n                                                      deltaTime,\n                                                      damping,\n                                                      numTiles);\n\n        if (numDevices > 1) {\n            checkCudaErrors(hipEventRecord(deviceData[dev].event));\n            // MJH: Hack on older driver versions to force kernel launches to flush!\n            hipStreamQuery(0);\n        }\n\n        // check if kernel invocation generated an error\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    if (numDevices > 1) {\n        for (unsigned int dev = 0; dev < numDevices; dev++) {\n            checkCudaErrors(hipEventSynchronize(deviceData[dev].event));\n        }\n    }\n\n    if (bUsePBO) {\n        checkCudaErrors(hipGraphicsUnmapResources(2, pgres, 0));\n    }\n}\n\n// Explicit specializations needed to generate code\ntemplate void integrateNbodySystem<float>(DeviceData<float>     *deviceData,\n                                          hipGraphicsResource **pgres,\n                                          unsigned int           currentRead,\n                                          float                  deltaTime,\n                                          float                  damping,\n                                          unsigned int           numBodies,\n                                          unsigned int           numDevices,\n                                          int                    blockSize,\n                                          bool                   bUsePBO);\n\ntemplate void integrateNbodySystem<double>(DeviceData<double>    *deviceData,\n                                           hipGraphicsResource **pgres,\n                                           unsigned int           currentRead,\n                                           float                  deltaTime,\n                                           float                  damping,\n                                           unsigned int           numBodies,\n                                           unsigned int           numDevices,\n                                           int                    blockSize,\n                                           bool                   bUsePBO);\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/nbody_opengles/bodysystemcuda.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda_runtime.h>\n#include <helper_image.h>\n#include <iostream>\n\n#include \"cuda_consumer.h\"\n#include \"nvmedia_image_nvscibuf.h\"\n#include \"nvmedia_utils/cmdline.h\"\n\n// Enable this to 1 if require cuda processed output to ppm file.\n#define WRITE_OUTPUT_IMAGE 0\n\n#define checkNvSciErrors(call)                                   \\\n    do {                                                         \\\n        NvSciError _status = call;                               \\\n        if (NvSciError_Success != _status) {                     \\\n            printf(\"NVSCI call in file '%s' in line %i returned\" \\\n                   \" %d, expected %d\\n\",                         \\\n                   __FILE__,                                     \\\n                   __LINE__,                                     \\\n                   _status,                                      \\\n                   NvSciError_Success);                          \\\n            fflush(stdout);                                      \\\n            exit(EXIT_FAILURE);                                  \\\n        }                                                        \\\n    } while (0)\n\n__global__ static void\nyuvToGrayscale(cudaSurfaceObject_t surfaceObject, unsigned int *dstImage, int32_t imageWidth, int32_t imageHeight)\n{\n    size_t x = blockIdx.x * blockDim.x + threadIdx.x;\n    size_t y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    uchar4 *dstImageUchar4 = (uchar4 *)dstImage;\n    for (; x < imageWidth && y < imageHeight; x += gridDim.x * blockDim.x, y += gridDim.y * blockDim.y) {\n        int           colInBytes   = x * sizeof(unsigned char);\n        unsigned char luma         = surf2Dread<unsigned char>(surfaceObject, colInBytes, y);\n        uchar4        grayscalePix = make_uchar4(luma, luma, luma, 0);\n\n        dstImageUchar4[y * imageWidth + x] = grayscalePix;\n    }\n}\n\nstatic void cudaImportNvSciSync(cudaExternalSemaphore_t &extSem, NvSciSyncObj &syncObj)\n{\n    cudaExternalSemaphoreHandleDesc extSemDesc;\n    memset(&extSemDesc, 0, sizeof(extSemDesc));\n    extSemDesc.type                = cudaExternalSemaphoreHandleTypeNvSciSync;\n    extSemDesc.handle.nvSciSyncObj = (void *)syncObj;\n\n    checkCudaErrors(cudaImportExternalSemaphore(&extSem, &extSemDesc));\n}\n\nstatic void waitExternalSemaphore(cudaExternalSemaphore_t &waitSem, NvSciSyncFence *fence, cudaStream_t stream)\n{\n    cudaExternalSemaphoreWaitParams waitParams;\n    memset(&waitParams, 0, sizeof(waitParams));\n    // For cross-process signaler-waiter applications need to use NvSciIpc\n    // and NvSciSync[Export|Import] utilities to share the NvSciSyncFence\n    // across process. This step is optional in single-process.\n    waitParams.params.nvSciSync.fence = (void *)fence;\n    waitParams.flags                  = 0;\n\n    checkCudaErrors(cudaWaitExternalSemaphoresAsync(&waitSem, &waitParams, 1, stream));\n}\n\nstatic void signalExternalSemaphore(cudaExternalSemaphore_t &signalSem, NvSciSyncFence *fence, cudaStream_t stream)\n{\n    cudaExternalSemaphoreSignalParams signalParams;\n    memset(&signalParams, 0, sizeof(signalParams));\n    // For cross-process signaler-waiter applications need to use NvSciIpc\n    // and NvSciSync[Export|Import] utilities to share the NvSciSyncFence\n    // across process. This step is optional in single-process.\n    signalParams.params.nvSciSync.fence = (void *)fence;\n    signalParams.flags                  = 0;\n\n    checkCudaErrors(cudaSignalExternalSemaphoresAsync(&signalSem, &signalParams, 1, stream));\n}\n\nstatic void yuvToGrayscaleCudaKernel(cudaExternalResInterop &cudaExtResObj, int32_t imageWidth, int32_t imageHeight)\n{\n#if WRITE_OUTPUT_IMAGE\n    unsigned int *h_dstImage;\n    checkCudaErrors(cudaMallocHost(&h_dstImage, sizeof(unsigned int) * imageHeight * imageWidth));\n#endif\n    dim3 block(16, 16, 1);\n    dim3 grid((imageWidth / block.x) + 1, (imageHeight / block.y) + 1, 1);\n\n    yuvToGrayscale<<<grid, block, 0, cudaExtResObj.stream>>>(\n        cudaExtResObj.cudaSurfaceNvmediaBuf[0], cudaExtResObj.d_outputImage, imageWidth, imageHeight);\n\n#if WRITE_OUTPUT_IMAGE\n    checkCudaErrors(cudaMemcpyAsync(h_dstImage,\n                                    cudaExtResObj.d_outputImage,\n                                    sizeof(unsigned int) * imageHeight * imageWidth,\n                                    cudaMemcpyDeviceToHost,\n                                    cudaExtResObj.stream));\n    checkCudaErrors(cudaStreamSynchronize(cudaExtResObj.stream));\n    char        outputFilename[1024];\n    std::string image_filename = \"Grayscale\";\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length(), \"_nvsci_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)h_dstImage, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n    checkCudaErrors(cudaFreeHost(h_dstImage));\n#endif\n}\n\nstatic void cudaImportNvSciImage(cudaExternalResInterop &cudaExtResObj, NvSciBufObj &inputBufObj)\n{\n    NvSciBufModule           module   = NULL;\n    NvSciBufAttrList         attrlist = NULL;\n    NvSciBufAttrKeyValuePair pairArrayOut[10];\n\n    checkNvSciErrors(NvSciBufModuleOpen(&module));\n    checkNvSciErrors(NvSciBufAttrListCreate(module, &attrlist));\n    checkNvSciErrors(NvSciBufObjGetAttrList(inputBufObj, &attrlist));\n\n    memset(pairArrayOut, 0, sizeof(NvSciBufAttrKeyValuePair) * 10);\n\n    int numAttrs                 = 0;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_Size;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneChannelCount;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneCount;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneWidth;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneHeight;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_Layout;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneBitsPerPixel;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneOffset;\n\n    checkNvSciErrors(NvSciBufAttrListGetAttrs(attrlist, pairArrayOut, numAttrs));\n\n    uint64_t size             = *(uint64_t *)pairArrayOut[0].value;\n    uint8_t  channelCount     = *(uint8_t *)pairArrayOut[1].value;\n    cudaExtResObj.planeCount  = *(int32_t *)pairArrayOut[2].value;\n    cudaExtResObj.imageWidth  = (int32_t *)malloc(sizeof(int32_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.imageHeight = (int32_t *)malloc(sizeof(int32_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.planeOffset = (uint64_t *)malloc(sizeof(uint64_t) * cudaExtResObj.planeCount);\n\n    memcpy(cudaExtResObj.imageWidth, (int32_t *)pairArrayOut[3].value, cudaExtResObj.planeCount * sizeof(int32_t));\n    memcpy(cudaExtResObj.imageHeight, (int32_t *)pairArrayOut[4].value, cudaExtResObj.planeCount * sizeof(int32_t));\n    memcpy(cudaExtResObj.planeOffset, (uint64_t *)pairArrayOut[7].value, cudaExtResObj.planeCount * sizeof(uint64_t));\n\n    NvSciBufAttrValImageLayoutType layout       = *(NvSciBufAttrValImageLayoutType *)pairArrayOut[5].value;\n    uint32_t                       bitsPerPixel = *(uint32_t *)pairArrayOut[6].value;\n\n    if (layout != NvSciBufImage_BlockLinearType) {\n        printf(\"Image layout is not block linear.. waiving execution\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    cudaExternalMemoryHandleDesc memHandleDesc;\n    memset(&memHandleDesc, 0, sizeof(memHandleDesc));\n    memHandleDesc.type                  = cudaExternalMemoryHandleTypeNvSciBuf;\n    memHandleDesc.handle.nvSciBufObject = inputBufObj;\n    memHandleDesc.size                  = size;\n    checkCudaErrors(cudaImportExternalMemory(&cudaExtResObj.extMemImageBuf, &memHandleDesc));\n\n    cudaExtResObj.d_mipmapArray =\n        (cudaMipmappedArray_t *)malloc(sizeof(cudaMipmappedArray_t) * cudaExtResObj.planeCount);\n\n    for (int i = 0; i < cudaExtResObj.planeCount; i++) {\n        cudaExtent extent = {};\n        memset(&extent, 0, sizeof(extent));\n        extent.width  = cudaExtResObj.imageWidth[i];\n        extent.height = cudaExtResObj.imageHeight[i];\n        extent.depth  = 0;\n        cudaChannelFormatDesc desc;\n        switch (channelCount) {\n        case 1:\n        default:\n            desc = cudaCreateChannelDesc(bitsPerPixel, 0, 0, 0, cudaChannelFormatKindUnsigned);\n            break;\n        case 2:\n            desc = cudaCreateChannelDesc(bitsPerPixel, bitsPerPixel, 0, 0, cudaChannelFormatKindUnsigned);\n            break;\n        case 3:\n            desc = cudaCreateChannelDesc(bitsPerPixel, bitsPerPixel, bitsPerPixel, 0, cudaChannelFormatKindUnsigned);\n            break;\n        case 4:\n            desc = cudaCreateChannelDesc(\n                bitsPerPixel, bitsPerPixel, bitsPerPixel, bitsPerPixel, cudaChannelFormatKindUnsigned);\n            break;\n        }\n\n        cudaExternalMemoryMipmappedArrayDesc mipmapDesc = {0};\n        mipmapDesc.offset                               = cudaExtResObj.planeOffset[i];\n        mipmapDesc.formatDesc                           = desc;\n        mipmapDesc.extent                               = extent;\n        mipmapDesc.flags                                = 0;\n        mipmapDesc.numLevels                            = 1;\n        checkCudaErrors(cudaExternalMemoryGetMappedMipmappedArray(\n            &cudaExtResObj.d_mipmapArray[i], cudaExtResObj.extMemImageBuf, &mipmapDesc));\n    }\n}\n\nstatic cudaSurfaceObject_t createCudaSurface(cudaArray_t &d_mipLevelArray)\n{\n    cudaResourceDesc resourceDesc;\n    memset(&resourceDesc, 0, sizeof(resourceDesc));\n    resourceDesc.resType         = cudaResourceTypeArray;\n    resourceDesc.res.array.array = d_mipLevelArray;\n\n    cudaSurfaceObject_t surfaceObject;\n    checkCudaErrors(cudaCreateSurfaceObject(&surfaceObject, &resourceDesc));\n    return surfaceObject;\n}\n\nstatic cudaStream_t createCudaStream(int deviceId)\n{\n    checkCudaErrors(cudaSetDevice(deviceId));\n    cudaStream_t stream;\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    return stream;\n}\n\n// CUDA setup buffers/synchronization objects for interop via NvSci API.\nvoid setupCuda(cudaExternalResInterop &cudaExtResObj,\n               NvSciBufObj            &inputBufObj,\n               NvSciSyncObj           &syncObj,\n               NvSciSyncObj           &cudaSignalerSyncObj,\n               int                     deviceId)\n{\n    checkCudaErrors(cudaSetDevice(deviceId));\n    cudaImportNvSciSync(cudaExtResObj.waitSem, syncObj);\n    cudaImportNvSciSync(cudaExtResObj.signalSem, cudaSignalerSyncObj);\n\n    cudaImportNvSciImage(cudaExtResObj, inputBufObj);\n    cudaExtResObj.d_mipLevelArray = (cudaArray_t *)malloc(sizeof(cudaArray_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.cudaSurfaceNvmediaBuf =\n        (cudaSurfaceObject_t *)malloc(sizeof(cudaSurfaceObject_t) * cudaExtResObj.planeCount);\n\n    for (int i = 0; i < cudaExtResObj.planeCount; ++i) {\n        uint32_t mipLevelId = 0;\n        checkCudaErrors(\n            cudaGetMipmappedArrayLevel(&cudaExtResObj.d_mipLevelArray[i], cudaExtResObj.d_mipmapArray[i], mipLevelId));\n        cudaExtResObj.cudaSurfaceNvmediaBuf[i] = createCudaSurface(cudaExtResObj.d_mipLevelArray[i]);\n    }\n\n    cudaExtResObj.stream = createCudaStream(deviceId);\n    checkCudaErrors(cudaMalloc(&cudaExtResObj.d_outputImage,\n                               sizeof(unsigned int) * cudaExtResObj.imageWidth[0] * cudaExtResObj.imageHeight[0]));\n}\n\n// CUDA clean up buffers used **with** NvSci API.\nvoid cleanupCuda(cudaExternalResInterop &cudaExtResObj)\n{\n    for (int i = 0; i < cudaExtResObj.planeCount; i++) {\n        checkCudaErrors(cudaDestroySurfaceObject(cudaExtResObj.cudaSurfaceNvmediaBuf[i]));\n        checkCudaErrors(cudaFreeMipmappedArray(cudaExtResObj.d_mipmapArray[i]));\n    }\n    free(cudaExtResObj.d_mipmapArray);\n    free(cudaExtResObj.d_mipLevelArray);\n    free(cudaExtResObj.cudaSurfaceNvmediaBuf);\n    free(cudaExtResObj.imageWidth);\n    free(cudaExtResObj.imageHeight);\n    checkCudaErrors(cudaDestroyExternalSemaphore(cudaExtResObj.waitSem));\n    checkCudaErrors(cudaDestroyExternalSemaphore(cudaExtResObj.signalSem));\n    checkCudaErrors(cudaDestroyExternalMemory(cudaExtResObj.extMemImageBuf));\n    checkCudaErrors(cudaStreamDestroy(cudaExtResObj.stream));\n    checkCudaErrors(cudaFree(cudaExtResObj.d_outputImage));\n}\n\nvoid runCudaOperation(cudaExternalResInterop &cudaExtResObj,\n                      NvSciSyncFence         *cudaWaitFence,\n                      NvSciSyncFence         *cudaSignalFence,\n                      int                     deviceId,\n                      int                     iterations)\n{\n    checkCudaErrors(cudaSetDevice(deviceId));\n    static int64_t launch = 0;\n\n    waitExternalSemaphore(cudaExtResObj.waitSem, cudaWaitFence, cudaExtResObj.stream);\n\n    // run cuda kernel over surface object of the LUMA surface part to extract\n    // grayscale.\n    yuvToGrayscaleCudaKernel(cudaExtResObj, cudaExtResObj.imageWidth[0], cudaExtResObj.imageHeight[0]);\n\n    // signal fence till the second last iterations for NvMedia2DBlit to wait for\n    // cuda signal and for final iteration as there is no corresponding NvMedia\n    // operation pending therefore we end with cudaStreamSynchronize()\n    if (launch < iterations - 1) {\n        signalExternalSemaphore(cudaExtResObj.signalSem, cudaSignalFence, cudaExtResObj.stream);\n    }\n    else {\n        checkCudaErrors(cudaStreamSynchronize(cudaExtResObj.stream));\n    }\n    launch++;\n}\n\n// CUDA imports and operates on NvSci buffer/synchronization objects\nvoid setupCuda(Blit2DTest *ctx, cudaResources &cudaResObj, int deviceId)\n{\n    checkCudaErrors(cudaSetDevice(deviceId));\n    cudaResObj.d_yuvArray            = (cudaArray_t *)malloc(sizeof(cudaArray_t) * ctx->numSurfaces);\n    cudaResObj.cudaSurfaceNvmediaBuf = (cudaSurfaceObject_t *)malloc(sizeof(cudaSurfaceObject_t) * ctx->numSurfaces);\n    cudaChannelFormatDesc channelDesc;\n    switch (ctx->bytesPerPixel) {\n    case 1:\n    default:\n        channelDesc = cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);\n        break;\n    }\n\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(cudaMallocArray(&cudaResObj.d_yuvArray[k],\n                                        &channelDesc,\n                                        ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                        ctx->heightSurface * ctx->yScalePtr[k]));\n        cudaResObj.cudaSurfaceNvmediaBuf[k] = createCudaSurface(cudaResObj.d_yuvArray[k]);\n    }\n    checkCudaErrors(\n        cudaMalloc(&cudaResObj.d_outputImage, sizeof(unsigned int) * ctx->widthSurface * ctx->heightSurface));\n\n    cudaResObj.stream = createCudaStream(deviceId);\n}\n\n// CUDA clean up buffers used **without** NvSci API.\nvoid cleanupCuda(Blit2DTest *ctx, cudaResources &cudaResObj)\n{\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(cudaDestroySurfaceObject(cudaResObj.cudaSurfaceNvmediaBuf[k]));\n        checkCudaErrors(cudaFreeArray(cudaResObj.d_yuvArray[k]));\n    }\n\n    free(cudaResObj.cudaSurfaceNvmediaBuf);\n\n    checkCudaErrors(cudaStreamDestroy(cudaResObj.stream));\n    checkCudaErrors(cudaFree(cudaResObj.d_outputImage));\n}\n\nstatic void\nyuvToGrayscaleCudaKernelNonNvSci(cudaResources &cudaResObj, int deviceId, int32_t imageWidth, int32_t imageHeight)\n{\n#if WRITE_OUTPUT_IMAGE\n    unsigned int *h_dstImage;\n    checkCudaErrors(cudaMallocHost(&h_dstImage, sizeof(unsigned int) * imageHeight * imageWidth));\n#endif\n    dim3 block(16, 16, 1);\n    dim3 grid((imageWidth / block.x) + 1, (imageHeight / block.y) + 1, 1);\n\n    yuvToGrayscale<<<grid, block, 0, cudaResObj.stream>>>(\n        cudaResObj.cudaSurfaceNvmediaBuf[0], cudaResObj.d_outputImage, imageWidth, imageHeight);\n\n#if WRITE_OUTPUT_IMAGE\n    checkCudaErrors(cudaMemcpyAsync(h_dstImage,\n                                    cudaResObj.d_outputImage,\n                                    sizeof(unsigned int) * imageHeight * imageWidth,\n                                    cudaMemcpyDeviceToHost,\n                                    cudaResObj.stream));\n    checkCudaErrors(cudaStreamSynchronize(cudaResObj.stream));\n    char        outputFilename[1024];\n    std::string image_filename = \"Grayscale\";\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length(), \"_non-nvsci_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)h_dstImage, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n    checkCudaErrors(cudaFreeHost(h_dstImage));\n#else\n    checkCudaErrors(cudaStreamSynchronize(cudaResObj.stream));\n#endif\n}\n\n// CUDA operates **without** NvSci APIs buffer/synchronization objects.\nvoid runCudaOperation(Blit2DTest *ctx, cudaResources &cudaResObj, int deviceId)\n{\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(cudaMemcpy2DToArray(cudaResObj.d_yuvArray[k],\n                                            0,\n                                            0,\n                                            ctx->dstBuff[k],\n                                            ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                            ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                            ctx->heightSurface * ctx->yScalePtr[k],\n                                            cudaMemcpyHostToDevice));\n    }\n    // run cuda kernel over surface object of the LUMA surface part to extract\n    // grayscale.\n    yuvToGrayscaleCudaKernelNonNvSci(cudaResObj, deviceId, ctx->widthSurface, ctx->heightSurface);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime.h>\n#include <helper_image.h>\n#include <iostream>\n\n#include \"cuda_consumer.h\"\n#include \"nvmedia_image_nvscibuf.h\"\n#include \"nvmedia_utils/cmdline.h\"\n\n// Enable this to 1 if require cuda processed output to ppm file.\n#define WRITE_OUTPUT_IMAGE 0\n\n#define checkNvSciErrors(call)                                   \\\n    do {                                                         \\\n        NvSciError _status = call;                               \\\n        if (NvSciError_Success != _status) {                     \\\n            printf(\"NVSCI call in file '%s' in line %i returned\" \\\n                   \" %d, expected %d\\n\",                         \\\n                   __FILE__,                                     \\\n                   __LINE__,                                     \\\n                   _status,                                      \\\n                   NvSciError_Success);                          \\\n            fflush(stdout);                                      \\\n            exit(EXIT_FAILURE);                                  \\\n        }                                                        \\\n    } while (0)\n\n__global__ static void\nyuvToGrayscale(hipSurfaceObject_t surfaceObject, unsigned int *dstImage, int32_t imageWidth, int32_t imageHeight)\n{\n    size_t x = blockIdx.x * blockDim.x + threadIdx.x;\n    size_t y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    uchar4 *dstImageUchar4 = (uchar4 *)dstImage;\n    for (; x < imageWidth && y < imageHeight; x += gridDim.x * blockDim.x, y += gridDim.y * blockDim.y) {\n        int           colInBytes   = x * sizeof(unsigned char);\n        unsigned char luma         = surf2Dread<unsigned char>(surfaceObject, colInBytes, y);\n        uchar4        grayscalePix = make_uchar4(luma, luma, luma, 0);\n\n        dstImageUchar4[y * imageWidth + x] = grayscalePix;\n    }\n}\n\nstatic void cudaImportNvSciSync(hipExternalSemaphore_t &extSem, NvSciSyncObj &syncObj)\n{\n    hipExternalSemaphoreHandleDesc extSemDesc;\n    memset(&extSemDesc, 0, sizeof(extSemDesc));\n    extSemDesc.type                = cudaExternalSemaphoreHandleTypeNvSciSync;\n    extSemDesc.handle.nvSciSyncObj = (void *)syncObj;\n\n    checkCudaErrors(hipImportExternalSemaphore(&extSem, &extSemDesc));\n}\n\nstatic void waitExternalSemaphore(hipExternalSemaphore_t &waitSem, NvSciSyncFence *fence, hipStream_t stream)\n{\n    hipExternalSemaphoreWaitParams waitParams;\n    memset(&waitParams, 0, sizeof(waitParams));\n    // For cross-process signaler-waiter applications need to use NvSciIpc\n    // and NvSciSync[Export|Import] utilities to share the NvSciSyncFence\n    // across process. This step is optional in single-process.\n    waitParams.params.nvSciSync.fence = (void *)fence;\n    waitParams.flags                  = 0;\n\n    checkCudaErrors(hipWaitExternalSemaphoresAsync(&waitSem, &waitParams, 1, stream));\n}\n\nstatic void signalExternalSemaphore(hipExternalSemaphore_t &signalSem, NvSciSyncFence *fence, hipStream_t stream)\n{\n    hipExternalSemaphoreSignalParams signalParams;\n    memset(&signalParams, 0, sizeof(signalParams));\n    // For cross-process signaler-waiter applications need to use NvSciIpc\n    // and NvSciSync[Export|Import] utilities to share the NvSciSyncFence\n    // across process. This step is optional in single-process.\n    signalParams.params.nvSciSync.fence = (void *)fence;\n    signalParams.flags                  = 0;\n\n    checkCudaErrors(hipSignalExternalSemaphoresAsync(&signalSem, &signalParams, 1, stream));\n}\n\nstatic void yuvToGrayscaleCudaKernel(cudaExternalResInterop &cudaExtResObj, int32_t imageWidth, int32_t imageHeight)\n{\n#if WRITE_OUTPUT_IMAGE\n    unsigned int *h_dstImage;\n    checkCudaErrors(hipHostMalloc(&h_dstImage, sizeof(unsigned int) * imageHeight * imageWidth));\n#endif\n    dim3 block(16, 16, 1);\n    dim3 grid((imageWidth / block.x) + 1, (imageHeight / block.y) + 1, 1);\n\n    yuvToGrayscale<<<grid, block, 0, cudaExtResObj.stream>>>(\n        cudaExtResObj.cudaSurfaceNvmediaBuf[0], cudaExtResObj.d_outputImage, imageWidth, imageHeight);\n\n#if WRITE_OUTPUT_IMAGE\n    checkCudaErrors(hipMemcpyAsync(h_dstImage,\n                                    cudaExtResObj.d_outputImage,\n                                    sizeof(unsigned int) * imageHeight * imageWidth,\n                                    hipMemcpyDeviceToHost,\n                                    cudaExtResObj.stream));\n    checkCudaErrors(hipStreamSynchronize(cudaExtResObj.stream));\n    char        outputFilename[1024];\n    std::string image_filename = \"Grayscale\";\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length(), \"_nvsci_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)h_dstImage, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n    checkCudaErrors(hipHostFree(h_dstImage));\n#endif\n}\n\nstatic void cudaImportNvSciImage(cudaExternalResInterop &cudaExtResObj, NvSciBufObj &inputBufObj)\n{\n    NvSciBufModule           module   = NULL;\n    NvSciBufAttrList         attrlist = NULL;\n    NvSciBufAttrKeyValuePair pairArrayOut[10];\n\n    checkNvSciErrors(NvSciBufModuleOpen(&module));\n    checkNvSciErrors(NvSciBufAttrListCreate(module, &attrlist));\n    checkNvSciErrors(NvSciBufObjGetAttrList(inputBufObj, &attrlist));\n\n    memset(pairArrayOut, 0, sizeof(NvSciBufAttrKeyValuePair) * 10);\n\n    int numAttrs                 = 0;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_Size;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneChannelCount;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneCount;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneWidth;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneHeight;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_Layout;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneBitsPerPixel;\n    pairArrayOut[numAttrs++].key = NvSciBufImageAttrKey_PlaneOffset;\n\n    checkNvSciErrors(NvSciBufAttrListGetAttrs(attrlist, pairArrayOut, numAttrs));\n\n    uint64_t size             = *(uint64_t *)pairArrayOut[0].value;\n    uint8_t  channelCount     = *(uint8_t *)pairArrayOut[1].value;\n    cudaExtResObj.planeCount  = *(int32_t *)pairArrayOut[2].value;\n    cudaExtResObj.imageWidth  = (int32_t *)malloc(sizeof(int32_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.imageHeight = (int32_t *)malloc(sizeof(int32_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.planeOffset = (uint64_t *)malloc(sizeof(uint64_t) * cudaExtResObj.planeCount);\n\n    memcpy(cudaExtResObj.imageWidth, (int32_t *)pairArrayOut[3].value, cudaExtResObj.planeCount * sizeof(int32_t));\n    memcpy(cudaExtResObj.imageHeight, (int32_t *)pairArrayOut[4].value, cudaExtResObj.planeCount * sizeof(int32_t));\n    memcpy(cudaExtResObj.planeOffset, (uint64_t *)pairArrayOut[7].value, cudaExtResObj.planeCount * sizeof(uint64_t));\n\n    NvSciBufAttrValImageLayoutType layout       = *(NvSciBufAttrValImageLayoutType *)pairArrayOut[5].value;\n    uint32_t                       bitsPerPixel = *(uint32_t *)pairArrayOut[6].value;\n\n    if (layout != NvSciBufImage_BlockLinearType) {\n        printf(\"Image layout is not block linear.. waiving execution\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    hipExternalMemoryHandleDesc memHandleDesc;\n    memset(&memHandleDesc, 0, sizeof(memHandleDesc));\n    memHandleDesc.type                  = cudaExternalMemoryHandleTypeNvSciBuf;\n    memHandleDesc.handle.nvSciBufObject = inputBufObj;\n    memHandleDesc.size                  = size;\n    checkCudaErrors(hipImportExternalMemory(&cudaExtResObj.extMemImageBuf, &memHandleDesc));\n\n    cudaExtResObj.d_mipmapArray =\n        (hipMipmappedArray_t *)malloc(sizeof(hipMipmappedArray_t) * cudaExtResObj.planeCount);\n\n    for (int i = 0; i < cudaExtResObj.planeCount; i++) {\n        hipExtent extent = {};\n        memset(&extent, 0, sizeof(extent));\n        extent.width  = cudaExtResObj.imageWidth[i];\n        extent.height = cudaExtResObj.imageHeight[i];\n        extent.depth  = 0;\n        hipChannelFormatDesc desc;\n        switch (channelCount) {\n        case 1:\n        default:\n            desc = hipCreateChannelDesc(bitsPerPixel, 0, 0, 0, hipChannelFormatKindUnsigned);\n            break;\n        case 2:\n            desc = hipCreateChannelDesc(bitsPerPixel, bitsPerPixel, 0, 0, hipChannelFormatKindUnsigned);\n            break;\n        case 3:\n            desc = hipCreateChannelDesc(bitsPerPixel, bitsPerPixel, bitsPerPixel, 0, hipChannelFormatKindUnsigned);\n            break;\n        case 4:\n            desc = hipCreateChannelDesc(\n                bitsPerPixel, bitsPerPixel, bitsPerPixel, bitsPerPixel, hipChannelFormatKindUnsigned);\n            break;\n        }\n\n        cudaExternalMemoryMipmappedArrayDesc mipmapDesc = {0};\n        mipmapDesc.offset                               = cudaExtResObj.planeOffset[i];\n        mipmapDesc.formatDesc                           = desc;\n        mipmapDesc.extent                               = extent;\n        mipmapDesc.flags                                = 0;\n        mipmapDesc.numLevels                            = 1;\n        checkCudaErrors(cudaExternalMemoryGetMappedMipmappedArray(\n            &cudaExtResObj.d_mipmapArray[i], cudaExtResObj.extMemImageBuf, &mipmapDesc));\n    }\n}\n\nstatic hipSurfaceObject_t createCudaSurface(hipArray_t &d_mipLevelArray)\n{\n    hipResourceDesc resourceDesc;\n    memset(&resourceDesc, 0, sizeof(resourceDesc));\n    resourceDesc.resType         = hipResourceTypeArray;\n    resourceDesc.res.array.array = d_mipLevelArray;\n\n    hipSurfaceObject_t surfaceObject;\n    checkCudaErrors(hipCreateSurfaceObject(&surfaceObject, &resourceDesc));\n    return surfaceObject;\n}\n\nstatic hipStream_t createCudaStream(int deviceId)\n{\n    checkCudaErrors(hipSetDevice(deviceId));\n    hipStream_t stream;\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    return stream;\n}\n\n// CUDA setup buffers/synchronization objects for interop via NvSci API.\nvoid setupCuda(cudaExternalResInterop &cudaExtResObj,\n               NvSciBufObj            &inputBufObj,\n               NvSciSyncObj           &syncObj,\n               NvSciSyncObj           &cudaSignalerSyncObj,\n               int                     deviceId)\n{\n    checkCudaErrors(hipSetDevice(deviceId));\n    cudaImportNvSciSync(cudaExtResObj.waitSem, syncObj);\n    cudaImportNvSciSync(cudaExtResObj.signalSem, cudaSignalerSyncObj);\n\n    cudaImportNvSciImage(cudaExtResObj, inputBufObj);\n    cudaExtResObj.d_mipLevelArray = (hipArray_t *)malloc(sizeof(hipArray_t) * cudaExtResObj.planeCount);\n    cudaExtResObj.cudaSurfaceNvmediaBuf =\n        (hipSurfaceObject_t *)malloc(sizeof(hipSurfaceObject_t) * cudaExtResObj.planeCount);\n\n    for (int i = 0; i < cudaExtResObj.planeCount; ++i) {\n        uint32_t mipLevelId = 0;\n        checkCudaErrors(\n            hipGetMipmappedArrayLevel(&cudaExtResObj.d_mipLevelArray[i], cudaExtResObj.d_mipmapArray[i], mipLevelId));\n        cudaExtResObj.cudaSurfaceNvmediaBuf[i] = createCudaSurface(cudaExtResObj.d_mipLevelArray[i]);\n    }\n\n    cudaExtResObj.stream = createCudaStream(deviceId);\n    checkCudaErrors(hipMalloc(&cudaExtResObj.d_outputImage,\n                               sizeof(unsigned int) * cudaExtResObj.imageWidth[0] * cudaExtResObj.imageHeight[0]));\n}\n\n// CUDA clean up buffers used **with** NvSci API.\nvoid cleanupCuda(cudaExternalResInterop &cudaExtResObj)\n{\n    for (int i = 0; i < cudaExtResObj.planeCount; i++) {\n        checkCudaErrors(hipDestroySurfaceObject(cudaExtResObj.cudaSurfaceNvmediaBuf[i]));\n        checkCudaErrors(hipFreeMipmappedArray(cudaExtResObj.d_mipmapArray[i]));\n    }\n    free(cudaExtResObj.d_mipmapArray);\n    free(cudaExtResObj.d_mipLevelArray);\n    free(cudaExtResObj.cudaSurfaceNvmediaBuf);\n    free(cudaExtResObj.imageWidth);\n    free(cudaExtResObj.imageHeight);\n    checkCudaErrors(hipDestroyExternalSemaphore(cudaExtResObj.waitSem));\n    checkCudaErrors(hipDestroyExternalSemaphore(cudaExtResObj.signalSem));\n    checkCudaErrors(hipDestroyExternalMemory(cudaExtResObj.extMemImageBuf));\n    checkCudaErrors(hipStreamDestroy(cudaExtResObj.stream));\n    checkCudaErrors(hipFree(cudaExtResObj.d_outputImage));\n}\n\nvoid runCudaOperation(cudaExternalResInterop &cudaExtResObj,\n                      NvSciSyncFence         *cudaWaitFence,\n                      NvSciSyncFence         *cudaSignalFence,\n                      int                     deviceId,\n                      int                     iterations)\n{\n    checkCudaErrors(hipSetDevice(deviceId));\n    static int64_t launch = 0;\n\n    waitExternalSemaphore(cudaExtResObj.waitSem, cudaWaitFence, cudaExtResObj.stream);\n\n    // run cuda kernel over surface object of the LUMA surface part to extract\n    // grayscale.\n    yuvToGrayscaleCudaKernel(cudaExtResObj, cudaExtResObj.imageWidth[0], cudaExtResObj.imageHeight[0]);\n\n    // signal fence till the second last iterations for NvMedia2DBlit to wait for\n    // cuda signal and for final iteration as there is no corresponding NvMedia\n    // operation pending therefore we end with cudaStreamSynchronize()\n    if (launch < iterations - 1) {\n        signalExternalSemaphore(cudaExtResObj.signalSem, cudaSignalFence, cudaExtResObj.stream);\n    }\n    else {\n        checkCudaErrors(hipStreamSynchronize(cudaExtResObj.stream));\n    }\n    launch++;\n}\n\n// CUDA imports and operates on NvSci buffer/synchronization objects\nvoid setupCuda(Blit2DTest *ctx, cudaResources &cudaResObj, int deviceId)\n{\n    checkCudaErrors(hipSetDevice(deviceId));\n    cudaResObj.d_yuvArray            = (hipArray_t *)malloc(sizeof(hipArray_t) * ctx->numSurfaces);\n    cudaResObj.cudaSurfaceNvmediaBuf = (hipSurfaceObject_t *)malloc(sizeof(hipSurfaceObject_t) * ctx->numSurfaces);\n    hipChannelFormatDesc channelDesc;\n    switch (ctx->bytesPerPixel) {\n    case 1:\n    default:\n        channelDesc = hipCreateChannelDesc(8, 0, 0, 0, hipChannelFormatKindUnsigned);\n        break;\n    }\n\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(hipMallocArray(&cudaResObj.d_yuvArray[k],\n                                        &channelDesc,\n                                        ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                        ctx->heightSurface * ctx->yScalePtr[k]));\n        cudaResObj.cudaSurfaceNvmediaBuf[k] = createCudaSurface(cudaResObj.d_yuvArray[k]);\n    }\n    checkCudaErrors(\n        hipMalloc(&cudaResObj.d_outputImage, sizeof(unsigned int) * ctx->widthSurface * ctx->heightSurface));\n\n    cudaResObj.stream = createCudaStream(deviceId);\n}\n\n// CUDA clean up buffers used **without** NvSci API.\nvoid cleanupCuda(Blit2DTest *ctx, cudaResources &cudaResObj)\n{\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(hipDestroySurfaceObject(cudaResObj.cudaSurfaceNvmediaBuf[k]));\n        checkCudaErrors(hipFreeArray(cudaResObj.d_yuvArray[k]));\n    }\n\n    free(cudaResObj.cudaSurfaceNvmediaBuf);\n\n    checkCudaErrors(hipStreamDestroy(cudaResObj.stream));\n    checkCudaErrors(hipFree(cudaResObj.d_outputImage));\n}\n\nstatic void\nyuvToGrayscaleCudaKernelNonNvSci(cudaResources &cudaResObj, int deviceId, int32_t imageWidth, int32_t imageHeight)\n{\n#if WRITE_OUTPUT_IMAGE\n    unsigned int *h_dstImage;\n    checkCudaErrors(hipHostMalloc(&h_dstImage, sizeof(unsigned int) * imageHeight * imageWidth));\n#endif\n    dim3 block(16, 16, 1);\n    dim3 grid((imageWidth / block.x) + 1, (imageHeight / block.y) + 1, 1);\n\n    yuvToGrayscale<<<grid, block, 0, cudaResObj.stream>>>(\n        cudaResObj.cudaSurfaceNvmediaBuf[0], cudaResObj.d_outputImage, imageWidth, imageHeight);\n\n#if WRITE_OUTPUT_IMAGE\n    checkCudaErrors(hipMemcpyAsync(h_dstImage,\n                                    cudaResObj.d_outputImage,\n                                    sizeof(unsigned int) * imageHeight * imageWidth,\n                                    hipMemcpyDeviceToHost,\n                                    cudaResObj.stream));\n    checkCudaErrors(hipStreamSynchronize(cudaResObj.stream));\n    char        outputFilename[1024];\n    std::string image_filename = \"Grayscale\";\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length(), \"_non-nvsci_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)h_dstImage, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n    checkCudaErrors(hipHostFree(h_dstImage));\n#else\n    checkCudaErrors(hipStreamSynchronize(cudaResObj.stream));\n#endif\n}\n\n// CUDA operates **without** NvSci APIs buffer/synchronization objects.\nvoid runCudaOperation(Blit2DTest *ctx, cudaResources &cudaResObj, int deviceId)\n{\n    for (int k = 0; k < ctx->numSurfaces; k++) {\n        checkCudaErrors(hipMemcpy2DToArray(cudaResObj.d_yuvArray[k],\n                                            0,\n                                            0,\n                                            ctx->dstBuff[k],\n                                            ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                            ctx->widthSurface * ctx->xScalePtr[k] * ctx->bytesPerPixel,\n                                            ctx->heightSurface * ctx->yScalePtr[k],\n                                            hipMemcpyHostToDevice));\n    }\n    // run cuda kernel over surface object of the LUMA surface part to extract\n    // grayscale.\n    yuvToGrayscaleCudaKernelNonNvSci(cudaResObj, deviceId, ctx->widthSurface, ctx->heightSurface);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/8_Platform_Specific/Tegra/cudaNvSciNvMedia/cuda_consumer.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// std::system includes\n#include <cstdio>\n\n// CUDA-C includes\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n\n#define TOTAL_SIZE 256 * 1024 * 1024\n#define EACH_SIZE  128 * 1024 * 1024\n\n// # threadblocks\n#define TBLOCKS 1024\n#define THREADS 512\n\n// throw error on equality\n#define ERR_EQ(X, Y)                                                                 \\\n    do {                                                                             \\\n        if ((X) == (Y)) {                                                            \\\n            fprintf(stderr, \"Error in %s at %s:%d\\n\", __func__, __FILE__, __LINE__); \\\n            exit(-1);                                                                \\\n        }                                                                            \\\n    } while (0)\n\n// throw error on difference\n#define ERR_NE(X, Y)                                                                 \\\n    do {                                                                             \\\n        if ((X) != (Y)) {                                                            \\\n            fprintf(stderr, \"Error in %s at %s:%d\\n\", __func__, __FILE__, __LINE__); \\\n            exit(-1);                                                                \\\n        }                                                                            \\\n    } while (0)\n\n// copy from source -> destination arrays\n__global__ void memcpy_kernel(int *dst, int *src, size_t n)\n{\n    int num = gridDim.x * blockDim.x;\n    int id  = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = id; i < n / sizeof(int); i += num) {\n        dst[i] = src[i];\n    }\n}\n\n// initialise memory\nvoid mem_init(int *buf, size_t n)\n{\n    for (int i = 0; i < n / sizeof(int); i++) {\n        buf[i] = i;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    cudaDeviceProp device_prop;\n    int            dev_id;\n\n    printf(\"Starting [%s]...\\n\", argv[0]);\n\n    // set device\n    dev_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&device_prop, dev_id));\n\n    if ((device_prop.major << 4) + device_prop.minor < 0x35) {\n        fprintf(stderr,\n                \"%s requires Compute Capability of SM 3.5 or higher to \"\n                \"run.\\nexiting...\\n\",\n                argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    // get the range of priorities available\n    // [ greatest_priority, lowest_priority ]\n    int priority_low;\n    int priority_hi;\n    checkCudaErrors(cudaDeviceGetStreamPriorityRange(&priority_low, &priority_hi));\n\n    printf(\"CUDA stream priority range: LOW: %d to HIGH: %d\\n\", priority_low, priority_hi);\n\n    // create streams with highest and lowest available priorities\n    cudaStream_t st_low;\n    cudaStream_t st_hi;\n    checkCudaErrors(cudaStreamCreateWithPriority(&st_low, cudaStreamNonBlocking, priority_low));\n    checkCudaErrors(cudaStreamCreateWithPriority(&st_hi, cudaStreamNonBlocking, priority_hi));\n\n    size_t size;\n    size = TOTAL_SIZE;\n\n    // initialise host data\n    int *h_src_low;\n    int *h_src_hi;\n    ERR_EQ(h_src_low = (int *)malloc(size), NULL);\n    ERR_EQ(h_src_hi = (int *)malloc(size), NULL);\n    mem_init(h_src_low, size);\n    mem_init(h_src_hi, size);\n\n    // initialise device data\n    int *h_dst_low;\n    int *h_dst_hi;\n    ERR_EQ(h_dst_low = (int *)malloc(size), NULL);\n    ERR_EQ(h_dst_hi = (int *)malloc(size), NULL);\n    memset(h_dst_low, 0, size);\n    memset(h_dst_hi, 0, size);\n\n    // copy source data -> device\n    int *d_src_low;\n    int *d_src_hi;\n    checkCudaErrors(cudaMalloc(&d_src_low, size));\n    checkCudaErrors(cudaMalloc(&d_src_hi, size));\n    checkCudaErrors(cudaMemcpy(d_src_low, h_src_low, size, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(d_src_hi, h_src_hi, size, cudaMemcpyHostToDevice));\n\n    // allocate memory for memcopy destination\n    int *d_dst_low;\n    int *d_dst_hi;\n    checkCudaErrors(cudaMalloc(&d_dst_low, size));\n    checkCudaErrors(cudaMalloc(&d_dst_hi, size));\n\n    // create some events\n    cudaEvent_t ev_start_low;\n    cudaEvent_t ev_start_hi;\n    cudaEvent_t ev_end_low;\n    cudaEvent_t ev_end_hi;\n    checkCudaErrors(cudaEventCreate(&ev_start_low));\n    checkCudaErrors(cudaEventCreate(&ev_start_hi));\n    checkCudaErrors(cudaEventCreate(&ev_end_low));\n    checkCudaErrors(cudaEventCreate(&ev_end_hi));\n\n    /* */\n\n    // call pair of kernels repeatedly (with different priority streams)\n    checkCudaErrors(cudaEventRecord(ev_start_low, st_low));\n    checkCudaErrors(cudaEventRecord(ev_start_hi, st_hi));\n\n    for (int i = 0; i < TOTAL_SIZE; i += EACH_SIZE) {\n        int j = i / sizeof(int);\n        memcpy_kernel<<<TBLOCKS, THREADS, 0, st_low>>>(d_dst_low + j, d_src_low + j, EACH_SIZE);\n        memcpy_kernel<<<TBLOCKS, THREADS, 0, st_hi>>>(d_dst_hi + j, d_src_hi + j, EACH_SIZE);\n    }\n\n    checkCudaErrors(cudaEventRecord(ev_end_low, st_low));\n    checkCudaErrors(cudaEventRecord(ev_end_hi, st_hi));\n\n    checkCudaErrors(cudaEventSynchronize(ev_end_low));\n    checkCudaErrors(cudaEventSynchronize(ev_end_hi));\n\n    /* */\n\n    size = TOTAL_SIZE;\n    checkCudaErrors(cudaMemcpy(h_dst_low, d_dst_low, size, cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaMemcpy(h_dst_hi, d_dst_hi, size, cudaMemcpyDeviceToHost));\n\n    // check results of kernels\n    ERR_NE(memcmp(h_dst_low, h_src_low, size), 0);\n    ERR_NE(memcmp(h_dst_hi, h_src_hi, size), 0);\n\n    // check timings\n    float ms_low;\n    float ms_hi;\n    checkCudaErrors(cudaEventElapsedTime(&ms_low, ev_start_low, ev_end_low));\n    checkCudaErrors(cudaEventElapsedTime(&ms_hi, ev_start_hi, ev_end_hi));\n\n    printf(\"elapsed time of kernels launched to LOW priority stream: %.3lf ms\\n\", ms_low);\n    printf(\"elapsed time of kernels launched to HI  priority stream: %.3lf ms\\n\", ms_hi);\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// std::system includes\n#include <cstdio>\n\n// CUDA-C includes\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\n#define TOTAL_SIZE 256 * 1024 * 1024\n#define EACH_SIZE  128 * 1024 * 1024\n\n// # threadblocks\n#define TBLOCKS 1024\n#define THREADS 512\n\n// throw error on equality\n#define ERR_EQ(X, Y)                                                                 \\\n    do {                                                                             \\\n        if ((X) == (Y)) {                                                            \\\n            fprintf(stderr, \"Error in %s at %s:%d\\n\", __func__, __FILE__, __LINE__); \\\n            exit(-1);                                                                \\\n        }                                                                            \\\n    } while (0)\n\n// throw error on difference\n#define ERR_NE(X, Y)                                                                 \\\n    do {                                                                             \\\n        if ((X) != (Y)) {                                                            \\\n            fprintf(stderr, \"Error in %s at %s:%d\\n\", __func__, __FILE__, __LINE__); \\\n            exit(-1);                                                                \\\n        }                                                                            \\\n    } while (0)\n\n// copy from source -> destination arrays\n__global__ void memcpy_kernel(int *dst, int *src, size_t n)\n{\n    int num = gridDim.x * blockDim.x;\n    int id  = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = id; i < n / sizeof(int); i += num) {\n        dst[i] = src[i];\n    }\n}\n\n// initialise memory\nvoid mem_init(int *buf, size_t n)\n{\n    for (int i = 0; i < n / sizeof(int); i++) {\n        buf[i] = i;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    hipDeviceProp_t device_prop;\n    int            dev_id;\n\n    printf(\"Starting [%s]...\\n\", argv[0]);\n\n    // set device\n    dev_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&device_prop, dev_id));\n\n    if ((device_prop.major << 4) + device_prop.minor < 0x35) {\n        fprintf(stderr,\n                \"%s requires Compute Capability of SM 3.5 or higher to \"\n                \"run.\\nexiting...\\n\",\n                argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    // get the range of priorities available\n    // [ greatest_priority, lowest_priority ]\n    int priority_low;\n    int priority_hi;\n    checkCudaErrors(hipDeviceGetStreamPriorityRange(&priority_low, &priority_hi));\n\n    printf(\"CUDA stream priority range: LOW: %d to HIGH: %d\\n\", priority_low, priority_hi);\n\n    // create streams with highest and lowest available priorities\n    hipStream_t st_low;\n    hipStream_t st_hi;\n    checkCudaErrors(hipStreamCreateWithPriority(&st_low, hipStreamNonBlocking, priority_low));\n    checkCudaErrors(hipStreamCreateWithPriority(&st_hi, hipStreamNonBlocking, priority_hi));\n\n    size_t size;\n    size = TOTAL_SIZE;\n\n    // initialise host data\n    int *h_src_low;\n    int *h_src_hi;\n    ERR_EQ(h_src_low = (int *)malloc(size), NULL);\n    ERR_EQ(h_src_hi = (int *)malloc(size), NULL);\n    mem_init(h_src_low, size);\n    mem_init(h_src_hi, size);\n\n    // initialise device data\n    int *h_dst_low;\n    int *h_dst_hi;\n    ERR_EQ(h_dst_low = (int *)malloc(size), NULL);\n    ERR_EQ(h_dst_hi = (int *)malloc(size), NULL);\n    memset(h_dst_low, 0, size);\n    memset(h_dst_hi, 0, size);\n\n    // copy source data -> device\n    int *d_src_low;\n    int *d_src_hi;\n    checkCudaErrors(hipMalloc(&d_src_low, size));\n    checkCudaErrors(hipMalloc(&d_src_hi, size));\n    checkCudaErrors(hipMemcpy(d_src_low, h_src_low, size, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(d_src_hi, h_src_hi, size, hipMemcpyHostToDevice));\n\n    // allocate memory for memcopy destination\n    int *d_dst_low;\n    int *d_dst_hi;\n    checkCudaErrors(hipMalloc(&d_dst_low, size));\n    checkCudaErrors(hipMalloc(&d_dst_hi, size));\n\n    // create some events\n    hipEvent_t ev_start_low;\n    hipEvent_t ev_start_hi;\n    hipEvent_t ev_end_low;\n    hipEvent_t ev_end_hi;\n    checkCudaErrors(hipEventCreate(&ev_start_low));\n    checkCudaErrors(hipEventCreate(&ev_start_hi));\n    checkCudaErrors(hipEventCreate(&ev_end_low));\n    checkCudaErrors(hipEventCreate(&ev_end_hi));\n\n    /* */\n\n    // call pair of kernels repeatedly (with different priority streams)\n    checkCudaErrors(hipEventRecord(ev_start_low, st_low));\n    checkCudaErrors(hipEventRecord(ev_start_hi, st_hi));\n\n    for (int i = 0; i < TOTAL_SIZE; i += EACH_SIZE) {\n        int j = i / sizeof(int);\n        memcpy_kernel<<<TBLOCKS, THREADS, 0, st_low>>>(d_dst_low + j, d_src_low + j, EACH_SIZE);\n        memcpy_kernel<<<TBLOCKS, THREADS, 0, st_hi>>>(d_dst_hi + j, d_src_hi + j, EACH_SIZE);\n    }\n\n    checkCudaErrors(hipEventRecord(ev_end_low, st_low));\n    checkCudaErrors(hipEventRecord(ev_end_hi, st_hi));\n\n    checkCudaErrors(hipEventSynchronize(ev_end_low));\n    checkCudaErrors(hipEventSynchronize(ev_end_hi));\n\n    /* */\n\n    size = TOTAL_SIZE;\n    checkCudaErrors(hipMemcpy(h_dst_low, d_dst_low, size, hipMemcpyDeviceToHost));\n    checkCudaErrors(hipMemcpy(h_dst_hi, d_dst_hi, size, hipMemcpyDeviceToHost));\n\n    // check results of kernels\n    ERR_NE(memcmp(h_dst_low, h_src_low, size), 0);\n    ERR_NE(memcmp(h_dst_hi, h_src_hi, size), 0);\n\n    // check timings\n    float ms_low;\n    float ms_hi;\n    checkCudaErrors(hipEventElapsedTime(&ms_low, ev_start_low, ev_end_low));\n    checkCudaErrors(hipEventElapsedTime(&ms_hi, ev_start_hi, ev_end_hi));\n\n    printf(\"elapsed time of kernels launched to LOW priority stream: %.3lf ms\\n\", ms_low);\n    printf(\"elapsed time of kernels launched to HI  priority stream: %.3lf ms\\n\", ms_hi);\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/StreamPriorities/StreamPriorities.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a integer GEMM computation using the Warp Matrix\n// Multiply and Accumulate API.\n\n// In this program, the compute_gemm kernel computes the result of a matrix\n// multiplication and addition: D = alpha * A * B + beta * C. The dimensions of\n// both C and D matrices are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x\n// K_GLOBAL (row-major), the B matrix is K_GLOBAL x N_GLOBAL (column-major). In\n// that kernel, each CTA computes one 128 x 128 tile of the resulting matrix per\n// iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes\n// eight 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array. Warps\n// compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and\n// accumulating the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments\n//   from shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B\n// matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the\n//   A and B data from shared memory, thus reducing the number of data copies\n//   from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an\n// additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_HALF macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each\n// warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory\n//   contents to global memory, again avoiding redundant random global memory\n//   accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register\n// utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <cuda.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 1\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n#define WMMA_M 16\n#define WMMA_N 16\n#define WMMA_K 16\n\n// GEMM configuration.\n\n#define M_TILES 256\n#define N_TILES 256\n#define K_TILES 256\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that are 16 * 16 * 8 * 8 * 2 = 32 Kb each\n// (i.e. two 8x8 arrays of tiles of 16x16 uint8_t-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the\n// performance would be severely impacted. So we choose to reduce the chunk size\n// in half, i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 8\n#else\n#define CHUNK_K 16\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(uint8_t))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B\n// matrix in shared memory to minimize possible bank conflicts. Before\n// performing the nvcuda::wmma::mma_sync operation, the warp must load the\n// matrix data using the nvcuda::wmma::load_matrix_sync operation. Although the\n// memory access pattern is not specified for that function, each lane in the\n// warp can read one or multiple matrix elements from different matrix rows or\n// columns. For shared memory, such access can result in bank conflicts if\n// different rows / columns of the matrix map to the same bank. By shifting each\n// row and column by a few bytes, we make sure that they map to different banks,\n// thus reducing the number of possible bank conflicts. The number of 32\n// one-byte \"uint8_t\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by\n// nvcuda::wmma::load_matrix_sync.\n#define SKEW_UINT8 32\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        cudaError_t __err = cudaGetLastError();                                               \\\n        if (__err != cudaSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, cudaGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(uint8_t *a, uint8_t *b, int *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (uint8_t)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (uint8_t)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (rand() % 3);\n    }\n}\n\n__global__ void compute_gemm_imma(const uint8_t *A, const uint8_t *B, const int *C, int *D, int alpha, int beta)\n{\n    extern __shared__ uint8_t shmem[][CHUNK_K * K + SKEW_UINT8];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    int *shmem_warp_tile_ptr = (int *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * K * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and\n    // from shared memory.\n    int *shmem_warp_stream_ptr = (int *)&shmem[0][0] + warpId * SHMEM_STRIDE * K;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may\n    // result in a loss of precision). Zero still needs to be specially handled\n    // though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the\n    // matrix to the right and down, and selects the next tile to compute. Once\n    // there's no such tile, all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared\n        // memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const int   *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            typedef int4 copy_t;\n\n            *((copy_t *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((copy_t *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment\n        // multiplications along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, int> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const int *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const uint8_t *warp_ptr = (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % 4) * 2)\n                                               : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % 4) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy\n            // the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            int4 *lane_ptr = (int4 *)(warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL)\n                           + (laneId % CHUNK_COPY_LINE_LANES);\n\n            // Shift the second half of the warp to the next row / column in the\n            // shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) = *lane_ptr;\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = (int4 *)((uint8_t *)lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP);\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, uint8_t, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, uint8_t, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t         shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const uint8_t *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_UINT8);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be\n                            // reused against the other A matrix fragments.\n                            size_t shmem_idx_b      = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const uint8_t *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_UINT8);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL\n                // threads in the warp are well-defined even though element indices\n                // within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                int *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global\n        // memory.\n        int *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n}\n\n// Performs an MxNxK GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16.\n//  3) Neither A nor B are transposed.\n// Note: This is a less performant version of the compute_gemm_imma kernel. It\n// is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without\n//       relying on availability of the shared memory.\n__global__ void simple_wmma_gemm_imma(const uint8_t *a,\n                                      const uint8_t *b,\n                                      const int     *c,\n                                      int           *d,\n                                      int            m_ld,\n                                      int            n_ld,\n                                      int            k_ld,\n                                      int            alpha,\n                                      int            beta)\n{\n    // Leading dimensions. Packed with no transpositions.\n    int lda = m_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, uint8_t, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, uint8_t, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, int>                   acc_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, int>                   c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += WMMA_K) {\n        int aCol = i;\n        int aRow = warpM * WMMA_M;\n\n        int bCol = i;\n        int bRow = warpN * WMMA_N;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bCol + bRow * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result\n    // scaled by alpha\n    int cCol = warpN * WMMA_N;\n    int cRow = warpM * WMMA_M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n}\n\n__host__ void matMultiplyOnHost(uint8_t *A,\n                                uint8_t *B,\n                                int     *C,\n                                int      alpha,\n                                int      beta,\n                                int      numARows,\n                                int      numAColumns,\n                                int      numBRows,\n                                int      numBColumns,\n                                int      numCRows,\n                                int      numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            int temp = 0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM72) architecture or higher.\n    if (deviceProp.major < 7 || (deviceProp.major <= 7 && deviceProp.minor < 2)) {\n        printf(\"immaTensorCoreGemm requires SM 7.2 or higher to use Tensor Cores.  \"\n               \"Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    uint8_t *A_h = NULL;\n    uint8_t *B_h = NULL;\n    int     *C_h = NULL;\n#if CPU_DEBUG\n    int *result_hD   = NULL;\n    int *result_host = NULL;\n#endif\n\n    A_h = (uint8_t *)malloc(sizeof(uint8_t) * M_GLOBAL * K_GLOBAL);\n    B_h = (uint8_t *)malloc(sizeof(uint8_t) * K_GLOBAL * N_GLOBAL);\n    C_h = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n    result_host = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    uint8_t *A = NULL;\n    uint8_t *B = NULL;\n    int     *C = NULL;\n    int     *D = NULL;\n\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&A), sizeof(uint8_t) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&B), sizeof(uint8_t) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&C), sizeof(int) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&D), sizeof(int) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    checkCudaErrors(cudaMemcpy(A, A_h, sizeof(uint8_t) * M_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(B, B_h, sizeof(uint8_t) * N_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(C, C_h, sizeof(int) * M_GLOBAL * N_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(D, 0, sizeof(int) * M_GLOBAL * N_GLOBAL));\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache\n        // per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the\n        // maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(uint8_t) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_UINT8) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(int))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    int alpha = 1;\n    int beta  = 1;\n\n    cudaEvent_t start, stop;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if (deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) {\n        printf(\"Computing... using high performance kernel compute_gemm_imma \\n\");\n\n        checkCudaErrors(cudaFuncSetAttribute(compute_gemm_imma, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n        checkKernelErrors((compute_gemm_imma<<<deviceProp.multiProcessorCount, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n            A, B, C, D, alpha, beta)));\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(int) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multiple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (WMMA_M * blockDim.x / 32 - 1)) / (WMMA_M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + WMMA_N * blockDim.y - 1) / (WMMA_N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm_imma kernel\\n\");\n        simple_wmma_gemm_imma<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(int) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(int) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (abs(result_hD[i] - result_host[i]) > 0) {\n            printf(\"mismatch i=%d result_hD=%d result_host=%d\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_host);\n    free(result_hD);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(A)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(B)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(C)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(D)));\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a integer GEMM computation using the Warp Matrix\n// Multiply and Accumulate API.\n\n// In this program, the compute_gemm kernel computes the result of a matrix\n// multiplication and addition: D = alpha * A * B + beta * C. The dimensions of\n// both C and D matrices are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x\n// K_GLOBAL (row-major), the B matrix is K_GLOBAL x N_GLOBAL (column-major). In\n// that kernel, each CTA computes one 128 x 128 tile of the resulting matrix per\n// iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes\n// eight 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array. Warps\n// compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and\n// accumulating the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments\n//   from shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B\n// matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the\n//   A and B data from shared memory, thus reducing the number of data copies\n//   from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an\n// additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_HALF macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each\n// warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory\n//   contents to global memory, again avoiding redundant random global memory\n//   accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register\n// utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 1\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n#define WMMA_M 16\n#define WMMA_N 16\n#define WMMA_K 16\n\n// GEMM configuration.\n\n#define M_TILES 256\n#define N_TILES 256\n#define K_TILES 256\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that are 16 * 16 * 8 * 8 * 2 = 32 Kb each\n// (i.e. two 8x8 arrays of tiles of 16x16 uint8_t-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the\n// performance would be severely impacted. So we choose to reduce the chunk size\n// in half, i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 8\n#else\n#define CHUNK_K 16\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(uint8_t))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B\n// matrix in shared memory to minimize possible bank conflicts. Before\n// performing the nvcuda::wmma::mma_sync operation, the warp must load the\n// matrix data using the nvcuda::wmma::load_matrix_sync operation. Although the\n// memory access pattern is not specified for that function, each lane in the\n// warp can read one or multiple matrix elements from different matrix rows or\n// columns. For shared memory, such access can result in bank conflicts if\n// different rows / columns of the matrix map to the same bank. By shifting each\n// row and column by a few bytes, we make sure that they map to different banks,\n// thus reducing the number of possible bank conflicts. The number of 32\n// one-byte \"uint8_t\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by\n// nvcuda::wmma::load_matrix_sync.\n#define SKEW_UINT8 32\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        hipError_t __err = hipGetLastError();                                               \\\n        if (__err != hipSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, hipGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(uint8_t *a, uint8_t *b, int *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (uint8_t)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (uint8_t)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (rand() % 3);\n    }\n}\n\n__global__ void compute_gemm_imma(const uint8_t *A, const uint8_t *B, const int *C, int *D, int alpha, int beta)\n{\n    extern __shared__ uint8_t shmem[][CHUNK_K * K + SKEW_UINT8];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    int *shmem_warp_tile_ptr = (int *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * K * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and\n    // from shared memory.\n    int *shmem_warp_stream_ptr = (int *)&shmem[0][0] + warpId * SHMEM_STRIDE * K;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may\n    // result in a loss of precision). Zero still needs to be specially handled\n    // though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the\n    // matrix to the right and down, and selects the next tile to compute. Once\n    // there's no such tile, all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared\n        // memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const int   *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            typedef int4 copy_t;\n\n            *((copy_t *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((copy_t *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment\n        // multiplications along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, int> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const int *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const uint8_t *warp_ptr = (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % 4) * 2)\n                                               : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % 4) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy\n            // the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            int4 *lane_ptr = (int4 *)(warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL)\n                           + (laneId % CHUNK_COPY_LINE_LANES);\n\n            // Shift the second half of the warp to the next row / column in the\n            // shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) = *lane_ptr;\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = (int4 *)((uint8_t *)lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP);\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, uint8_t, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, uint8_t, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t         shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const uint8_t *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_UINT8);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be\n                            // reused against the other A matrix fragments.\n                            size_t shmem_idx_b      = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const uint8_t *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_UINT8);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL\n                // threads in the warp are well-defined even though element indices\n                // within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                int *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global\n        // memory.\n        int *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n}\n\n// Performs an MxNxK GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16.\n//  3) Neither A nor B are transposed.\n// Note: This is a less performant version of the compute_gemm_imma kernel. It\n// is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without\n//       relying on availability of the shared memory.\n__global__ void simple_wmma_gemm_imma(const uint8_t *a,\n                                      const uint8_t *b,\n                                      const int     *c,\n                                      int           *d,\n                                      int            m_ld,\n                                      int            n_ld,\n                                      int            k_ld,\n                                      int            alpha,\n                                      int            beta)\n{\n    // Leading dimensions. Packed with no transpositions.\n    int lda = m_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, uint8_t, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, uint8_t, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, int>                   acc_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, int>                   c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += WMMA_K) {\n        int aCol = i;\n        int aRow = warpM * WMMA_M;\n\n        int bCol = i;\n        int bRow = warpN * WMMA_N;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bCol + bRow * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result\n    // scaled by alpha\n    int cCol = warpN * WMMA_N;\n    int cRow = warpM * WMMA_M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n}\n\n__host__ void matMultiplyOnHost(uint8_t *A,\n                                uint8_t *B,\n                                int     *C,\n                                int      alpha,\n                                int      beta,\n                                int      numARows,\n                                int      numAColumns,\n                                int      numBRows,\n                                int      numBColumns,\n                                int      numCRows,\n                                int      numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            int temp = 0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM72) architecture or higher.\n    if (deviceProp.major < 7 || (deviceProp.major <= 7 && deviceProp.minor < 2)) {\n        printf(\"immaTensorCoreGemm requires SM 7.2 or higher to use Tensor Cores.  \"\n               \"Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    uint8_t *A_h = NULL;\n    uint8_t *B_h = NULL;\n    int     *C_h = NULL;\n#if CPU_DEBUG\n    int *result_hD   = NULL;\n    int *result_host = NULL;\n#endif\n\n    A_h = (uint8_t *)malloc(sizeof(uint8_t) * M_GLOBAL * K_GLOBAL);\n    B_h = (uint8_t *)malloc(sizeof(uint8_t) * K_GLOBAL * N_GLOBAL);\n    C_h = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n    result_host = (int *)malloc(sizeof(int) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    uint8_t *A = NULL;\n    uint8_t *B = NULL;\n    int     *C = NULL;\n    int     *D = NULL;\n\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&A), sizeof(uint8_t) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&B), sizeof(uint8_t) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&C), sizeof(int) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&D), sizeof(int) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    checkCudaErrors(hipMemcpy(A, A_h, sizeof(uint8_t) * M_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(B, B_h, sizeof(uint8_t) * N_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(C, C_h, sizeof(int) * M_GLOBAL * N_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(D, 0, sizeof(int) * M_GLOBAL * N_GLOBAL));\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache\n        // per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the\n        // maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(uint8_t) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_UINT8) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(int))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    int alpha = 1;\n    int beta  = 1;\n\n    hipEvent_t start, stop;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if (deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) {\n        printf(\"Computing... using high performance kernel compute_gemm_imma \\n\");\n\n        checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_gemm_imma), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n        checkKernelErrors((compute_gemm_imma<<<deviceProp.multiProcessorCount, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n            A, B, C, D, alpha, beta)));\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(int) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multiple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (WMMA_M * blockDim.x / 32 - 1)) / (WMMA_M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + WMMA_N * blockDim.y - 1) / (WMMA_N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm_imma kernel\\n\");\n        simple_wmma_gemm_imma<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(int) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(int) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (abs(result_hD[i] - result_host[i]) > 0) {\n            printf(\"mismatch i=%d result_hD=%d result_host=%d\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_host);\n    free(result_hD);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(hipEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(A)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(B)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(C)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(D)));\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/immaTensorCoreGemm/immaTensorCoreGemm.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample demonstrates dynamic global memory allocation through device C++\n// new and delete operators and virtual function declarations available with\n// CUDA 4.0.\n\n#include <cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n#include <algorithm>\n#include <helper_cuda.h>\n#include <stdlib.h>\n#include <vector>\n\nconst char *sSDKsample = \"newdelete\";\n\n#include \"container.hpp\"\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to allocate and instantiate Container objects on the device heap\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void vectorCreate(Container<int> **g_container, int max_size)\n{\n    // The Vector object and the data storage are allocated in device heap memory.\n    // This makes it persistent for the lifetime of the CUDA context.\n    // The grid has only one thread as only a single object instance is needed.\n\n    *g_container = new Vector<int>(max_size);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to fill and consume shared Container objects.\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void containerFill(Container<int> **g_container)\n{\n    // All threads of the grid cooperatively populate the shared Container object\n    // with data.\n    if (threadIdx.x == 0) {\n        (*g_container)->push(blockIdx.x);\n    }\n}\n\n__global__ void containerConsume(Container<int> **g_container, int *d_result)\n{\n    // All threads of the grid cooperatively consume the data from the shared\n    // Container object.\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int v;\n\n    if ((*g_container)->pop(v)) {\n        d_result[idx] = v;\n    }\n    else {\n        d_result[idx] = -1;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernel to delete shared Container objects.\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void containerDelete(Container<int> **g_container) { delete *g_container; }\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to using of placement new to put shared Vector objects and data in\n// shared memory\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void placementNew(int *d_result)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    __shared__ unsigned char __align__(8) s_buffer[sizeof(Vector<int>)];\n    __shared__ int           __align__(8) s_data[1024];\n    __shared__ Vector<int> *s_vector;\n\n    // The first thread of the block initializes the shared Vector object.\n    // The placement new operator enables the Vector object and the data array top\n    // be placed in shared memory.\n    if (threadIdx.x == 0) {\n        s_vector = new (s_buffer) Vector<int>(1024, s_data);\n    }\n\n    cg::sync(cta);\n\n    if ((threadIdx.x & 1) == 0) {\n        s_vector->push(threadIdx.x >> 1);\n    }\n\n    // Need to sync as the vector implementation does not support concurrent\n    // push/pop operations.\n    cg::sync(cta);\n\n    int v;\n\n    if (s_vector->pop(v)) {\n        d_result[threadIdx.x] = v;\n    }\n    else {\n        d_result[threadIdx.x] = -1;\n    }\n\n    // Note: deleting objects placed in shared memory is not necessary (lifetime\n    // of shared memory is that of the block)\n}\n\nstruct ComplexType_t\n{\n    int   a;\n    int   b;\n    float c;\n    float d;\n};\n\n__global__ void complexVector(int *d_result)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    __shared__ unsigned char __align__(8) s_buffer[sizeof(Vector<ComplexType_t>)];\n    __shared__ ComplexType_t __align__(8) s_data[1024];\n    __shared__ Vector<ComplexType_t> *s_vector;\n\n    // The first thread of the block initializes the shared Vector object.\n    // The placement new operator enables the Vector object and the data array top\n    // be placed in shared memory.\n    if (threadIdx.x == 0) {\n        s_vector = new (s_buffer) Vector<ComplexType_t>(1024, s_data);\n    }\n\n    cg::sync(cta);\n\n    if ((threadIdx.x & 1) == 0) {\n        ComplexType_t data;\n        data.a = threadIdx.x >> 1;\n        data.b = blockIdx.x;\n        data.c = threadIdx.x / (float)(blockDim.x);\n        data.d = blockIdx.x / (float)(gridDim.x);\n\n        s_vector->push(data);\n    }\n\n    cg::sync(cta);\n\n    ComplexType_t v;\n\n    if (s_vector->pop(v)) {\n        d_result[threadIdx.x] = v.a;\n    }\n    else {\n        d_result[threadIdx.x] = -1;\n    }\n\n    // Note: deleting objects placed in shared memory is not necessary (lifetime\n    // of shared memory is that of the block)\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Host code\n//\n////////////////////////////////////////////////////////////////////////////////\n\nbool checkResult(int *d_result, int N)\n{\n    std::vector<int> h_result;\n    h_result.resize(N);\n\n    checkCudaErrors(cudaMemcpy(&h_result[0], d_result, N * sizeof(int), cudaMemcpyDeviceToHost));\n    std::sort(h_result.begin(), h_result.end());\n\n    bool success = true;\n    bool test    = false;\n\n    int value = 0;\n\n    for (int i = 0; i < N; ++i) {\n        if (h_result[i] != -1) {\n            test = true;\n        }\n\n        if (test && (value++) != h_result[i]) {\n            success = false;\n        }\n    }\n\n    return success;\n}\n\nbool testContainer(Container<int> **d_container, int blocks, int threads)\n{\n    int *d_result;\n    cudaMalloc(&d_result, blocks * threads * sizeof(int));\n\n    containerFill<<<blocks, threads>>>(d_container);\n    containerConsume<<<blocks, threads>>>(d_container, d_result);\n    containerDelete<<<1, 1>>>(d_container);\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    bool success = checkResult(d_result, blocks * threads);\n\n    cudaFree(d_result);\n\n    return success;\n}\n\nbool testPlacementNew(int threads)\n{\n    int *d_result;\n    cudaMalloc(&d_result, threads * sizeof(int));\n\n    placementNew<<<1, threads>>>(d_result);\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    bool success = checkResult(d_result, threads);\n\n    cudaFree(d_result);\n\n    return success;\n}\n\nbool testComplexType(int threads)\n{\n    int *d_result;\n    cudaMalloc(&d_result, threads * sizeof(int));\n\n    complexVector<<<1, threads>>>(d_result);\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    bool success = checkResult(d_result, threads);\n\n    cudaFree(d_result);\n\n    return success;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// MAIN\n//\n////////////////////////////////////////////////////////////////////////////////\n\nint main(int argc, char **argv)\n{\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    // set the heap size for device size new/delete to 128 MB\n    checkCudaErrors(cudaDeviceSetLimit(cudaLimitMallocHeapSize, 128 * (1 << 20)));\n\n    Container<int> **d_container;\n    checkCudaErrors(cudaMalloc(&d_container, sizeof(Container<int> **)));\n\n    bool bTest       = false;\n    int  test_passed = 0;\n\n    printf(\" > Container = Vector test \");\n    vectorCreate<<<1, 1>>>(d_container, 128 * 128);\n    bTest = testContainer(d_container, 128, 128);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    checkCudaErrors(cudaFree(d_container));\n\n    printf(\" > Container = Vector, using placement new on SMEM buffer test \");\n    bTest = testPlacementNew(1024);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    printf(\" > Container = Vector, with user defined datatype test \");\n    bTest = testComplexType(1024);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    printf(\"Test Summary: %d/3 succesfully run\\n\", test_passed);\n\n    exit(test_passed == 3 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample demonstrates dynamic global memory allocation through device C++\n// new and delete operators and virtual function declarations available with\n// CUDA 4.0.\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n#include <algorithm>\n#include <hip/hip_runtime_api.h>\n#include <stdlib.h>\n#include <vector>\n\nconst char *sSDKsample = \"newdelete\";\n\n#include \"container.hpp\"\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to allocate and instantiate Container objects on the device heap\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void vectorCreate(Container<int> **g_container, int max_size)\n{\n    // The Vector object and the data storage are allocated in device heap memory.\n    // This makes it persistent for the lifetime of the CUDA context.\n    // The grid has only one thread as only a single object instance is needed.\n\n    *g_container = new Vector<int>(max_size);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to fill and consume shared Container objects.\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void containerFill(Container<int> **g_container)\n{\n    // All threads of the grid cooperatively populate the shared Container object\n    // with data.\n    if (threadIdx.x == 0) {\n        (*g_container)->push(blockIdx.x);\n    }\n}\n\n__global__ void containerConsume(Container<int> **g_container, int *d_result)\n{\n    // All threads of the grid cooperatively consume the data from the shared\n    // Container object.\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int v;\n\n    if ((*g_container)->pop(v)) {\n        d_result[idx] = v;\n    }\n    else {\n        d_result[idx] = -1;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernel to delete shared Container objects.\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void containerDelete(Container<int> **g_container) { delete *g_container; }\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Kernels to using of placement new to put shared Vector objects and data in\n// shared memory\n//\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void placementNew(int *d_result)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    __shared__ unsigned char __align__(8) s_buffer[sizeof(Vector<int>)];\n    __shared__ int           __align__(8) s_data[1024];\n    __shared__ Vector<int> *s_vector;\n\n    // The first thread of the block initializes the shared Vector object.\n    // The placement new operator enables the Vector object and the data array top\n    // be placed in shared memory.\n    if (threadIdx.x == 0) {\n        s_vector = new (s_buffer) Vector<int>(1024, s_data);\n    }\n\n    cg::sync(cta);\n\n    if ((threadIdx.x & 1) == 0) {\n        s_vector->push(threadIdx.x >> 1);\n    }\n\n    // Need to sync as the vector implementation does not support concurrent\n    // push/pop operations.\n    cg::sync(cta);\n\n    int v;\n\n    if (s_vector->pop(v)) {\n        d_result[threadIdx.x] = v;\n    }\n    else {\n        d_result[threadIdx.x] = -1;\n    }\n\n    // Note: deleting objects placed in shared memory is not necessary (lifetime\n    // of shared memory is that of the block)\n}\n\nstruct ComplexType_t\n{\n    int   a;\n    int   b;\n    float c;\n    float d;\n};\n\n__global__ void complexVector(int *d_result)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    __shared__ unsigned char __align__(8) s_buffer[sizeof(Vector<ComplexType_t>)];\n    __shared__ ComplexType_t __align__(8) s_data[1024];\n    __shared__ Vector<ComplexType_t> *s_vector;\n\n    // The first thread of the block initializes the shared Vector object.\n    // The placement new operator enables the Vector object and the data array top\n    // be placed in shared memory.\n    if (threadIdx.x == 0) {\n        s_vector = new (s_buffer) Vector<ComplexType_t>(1024, s_data);\n    }\n\n    cg::sync(cta);\n\n    if ((threadIdx.x & 1) == 0) {\n        ComplexType_t data;\n        data.a = threadIdx.x >> 1;\n        data.b = blockIdx.x;\n        data.c = threadIdx.x / (float)(blockDim.x);\n        data.d = blockIdx.x / (float)(gridDim.x);\n\n        s_vector->push(data);\n    }\n\n    cg::sync(cta);\n\n    ComplexType_t v;\n\n    if (s_vector->pop(v)) {\n        d_result[threadIdx.x] = v.a;\n    }\n    else {\n        d_result[threadIdx.x] = -1;\n    }\n\n    // Note: deleting objects placed in shared memory is not necessary (lifetime\n    // of shared memory is that of the block)\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// Host code\n//\n////////////////////////////////////////////////////////////////////////////////\n\nbool checkResult(int *d_result, int N)\n{\n    std::vector<int> h_result;\n    h_result.resize(N);\n\n    checkCudaErrors(hipMemcpy(&h_result[0], d_result, N * sizeof(int), hipMemcpyDeviceToHost));\n    std::sort(h_result.begin(), h_result.end());\n\n    bool success = true;\n    bool test    = false;\n\n    int value = 0;\n\n    for (int i = 0; i < N; ++i) {\n        if (h_result[i] != -1) {\n            test = true;\n        }\n\n        if (test && (value++) != h_result[i]) {\n            success = false;\n        }\n    }\n\n    return success;\n}\n\nbool testContainer(Container<int> **d_container, int blocks, int threads)\n{\n    int *d_result;\n    hipMalloc(&d_result, blocks * threads * sizeof(int));\n\n    containerFill<<<blocks, threads>>>(d_container);\n    containerConsume<<<blocks, threads>>>(d_container, d_result);\n    containerDelete<<<1, 1>>>(d_container);\n    checkCudaErrors(hipDeviceSynchronize());\n\n    bool success = checkResult(d_result, blocks * threads);\n\n    hipFree(d_result);\n\n    return success;\n}\n\nbool testPlacementNew(int threads)\n{\n    int *d_result;\n    hipMalloc(&d_result, threads * sizeof(int));\n\n    placementNew<<<1, threads>>>(d_result);\n    checkCudaErrors(hipDeviceSynchronize());\n\n    bool success = checkResult(d_result, threads);\n\n    hipFree(d_result);\n\n    return success;\n}\n\nbool testComplexType(int threads)\n{\n    int *d_result;\n    hipMalloc(&d_result, threads * sizeof(int));\n\n    complexVector<<<1, threads>>>(d_result);\n    checkCudaErrors(hipDeviceSynchronize());\n\n    bool success = checkResult(d_result, threads);\n\n    hipFree(d_result);\n\n    return success;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n// MAIN\n//\n////////////////////////////////////////////////////////////////////////////////\n\nint main(int argc, char **argv)\n{\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    // set the heap size for device size new/delete to 128 MB\n    checkCudaErrors(hipDeviceSetLimit(hipLimitMallocHeapSize, 128 * (1 << 20)));\n\n    Container<int> **d_container;\n    checkCudaErrors(hipMalloc(&d_container, sizeof(Container<int> **)));\n\n    bool bTest       = false;\n    int  test_passed = 0;\n\n    printf(\" > Container = Vector test \");\n    vectorCreate<<<1, 1>>>(d_container, 128 * 128);\n    bTest = testContainer(d_container, 128, 128);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    checkCudaErrors(hipFree(d_container));\n\n    printf(\" > Container = Vector, using placement new on SMEM buffer test \");\n    bTest = testPlacementNew(1024);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    printf(\" > Container = Vector, with user defined datatype test \");\n    bTest = testComplexType(1024);\n    printf(bTest ? \"OK\\n\\n\" : \"NOT OK\\n\\n\");\n    test_passed += (bTest ? 1 : 0);\n\n    printf(\"Test Summary: %d/3 succesfully run\\n\", test_passed);\n\n    exit(test_passed == 3 ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/newdelete/newdelete.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Device code\nextern \"C\" __global__ void memMapIpc_kernel(char *ptr, int sz, char val)\n{\n    // Dummy kernel\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// Device code\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void memMapIpc_kernel(char *ptr, int sz, char val)\n{\n    // Dummy kernel\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/memMapIPCDrv/memMapIpc_kernel.cu"}
{"input": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This file demonstrates the usage of conditional graph nodes with\n * a series of *simple* example graphs.\n *\n * For more information on conditional nodes, see the programming guide:\n *\n *   https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#conditional-graph-nodes\n *\n */\n\n// System includes\n#include <cassert>\n#include <cstdio>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional IF node.\n * The kernel sets the condition variable to true if a device memory location\n * contains an odd number. Otherwise the condition variable is set to false.\n * There is a single kernel, C, within the conditional body which prints a message.\n *\n * A -> B [ C ]\n *\n */\n\n__global__ void ifGraphKernelA(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    // In this example, condition is set if *dPtr is odd\n    unsigned int value = *dPtr & 0x01;\n    cudaGraphSetConditional(handle, value);\n    printf(\"GPU: Handle set to %d\\n\", value);\n}\n\n// This kernel will only be executed if the condition is true\n__global__ void ifGraphKernelC(void) { printf(\"GPU: Hello from the GPU! The condition was true.\\n\"); }\n\n// Setup and launch the graph\nvoid simpleIfGraph(void)\n{\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec;\n    cudaGraphNode_t kernelNode;\n    cudaGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(cudaMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleIfGraph: Building graph...\\n\");\n    cudaGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    cudaGraphNodeParams params = {cudaGraphNodeTypeKernel};\n    params.kernel.func         = (void *)ifGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(cudaGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    cudaGraphNodeParams cParams = {cudaGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeIf;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(cudaGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Populate the body of the conditional node\n    cudaGraphNode_t bodyNode;\n    params.kernel.func         = (void *)ifGraphKernelC;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(cudaGraphAddNode(&bodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with device memory set to 0\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 1, 1)); // Set dPtr to 1\n    printf(\"Host: Launching graph with device memory set to 1\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaFree(dPtr));\n\n    printf(\"simpleIfGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a single conditional while node.\n * The default value of the conditional variable is set to true, so this\n * effectively becomes a do-while loop as the conditional body will always\n * execute at least once. The body of the conditional contains 3 kernel nodes:\n * A [ B -> C -> D ]\n * Nodes B and C are just dummy nodes for demonstrative purposes. Node D\n * will decrement a device memory location and set the condition value to false\n * when the value reaches zero, terminating the loop.\n * In this example, stream capture is used to populate the conditional body.\n */\n\n// This kernel will only be executed if the condition is true\n__global__ void doWhileEmptyKernel(void)\n{\n    printf(\"GPU: doWhileEmptyKernel()\\n\");\n    return;\n}\n\n__global__ void doWhileLoopKernel(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    if (--(*dPtr) == 0) {\n        cudaGraphSetConditional(handle, 0);\n    }\n    printf(\"GPU: counter = %d\\n\", *dPtr);\n}\n\nvoid simpleDoWhileGraph(void)\n{\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec;\n    cudaGraphNode_t conditionalNode;\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(cudaMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleDoWhileGraph: Building graph...\\n\");\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n\n    cudaGraphConditionalHandle handle;\n    checkCudaErrors(cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault));\n\n    cudaGraphNodeParams cParams = {cudaGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeWhile;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(cudaGraphAddNode(&conditionalNode, graph, NULL, NULL, 0, &cParams));\n\n    cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    cudaStream_t captureStream;\n    checkCudaErrors(cudaStreamCreate(&captureStream));\n\n    checkCudaErrors(\n        cudaStreamBeginCaptureToGraph(captureStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));\n    doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n    doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n    doWhileLoopKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n    checkCudaErrors(cudaStreamEndCapture(captureStream, nullptr));\n    checkCudaErrors(cudaStreamDestroy(captureStream));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10\n    printf(\"Host: Launching graph with loop counter set to 10\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaFree(dPtr));\n\n    printf(\"simpleDoWhileGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a conditional while loop using stream capture.\n * This demonstrates how to insert a conditional node into a stream which is\n * being captured. The graph consists of a kernel node, A, followed by a\n * conditional while node, B, followed by a kernel node, D. The conditional\n * body is populated by a single kernel node, C:\n *\n * A -> B [ C ] -> D\n *\n * The same kernel will be used for both nodes A and C. This kernel will test\n * a device memory location and set the condition when the location is non-zero.\n * We must run the kernel before the loop as well as inside the loop in order\n * to behave like a while loop as opposed to a do-while loop. We need to evaluate\n * the device memory location before the conditional node is evaluated in order\n * to set the condition variable properly. Because we're using a kernel upstream\n * of the conditional node, there is no need to use the handle default value to\n * initialize the conditional value.\n */\n\n__global__ void capturedWhileKernel(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    printf(\"GPU: counter = %d\\n\", *dPtr);\n    if (*dPtr) {\n        (*dPtr)--;\n    }\n    cudaGraphSetConditional(handle, *dPtr);\n}\n\n__global__ void capturedWhileEmptyKernel(void)\n{\n    printf(\"GPU: capturedWhileEmptyKernel()\\n\");\n    return;\n}\n\nvoid capturedWhileGraph(void)\n{\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec;\n\n    cudaStreamCaptureStatus status;\n    const cudaGraphNode_t  *dependencies;\n    size_t                  numDependencies;\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(cudaMalloc((void **)&dPtr, 1));\n\n    printf(\"capturedWhileGraph: Building graph...\\n\");\n    cudaStream_t captureStream;\n    checkCudaErrors(cudaStreamCreate(&captureStream));\n\n    checkCudaErrors(cudaStreamBeginCapture(captureStream, cudaStreamCaptureModeGlobal));\n\n    // Obtain the handle of the graph\n    checkCudaErrors(\n        cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n\n    // Create the conditional handle\n    cudaGraphConditionalHandle handle;\n    checkCudaErrors(cudaGraphConditionalHandleCreate(&handle, graph));\n\n    // Insert kernel node A\n    capturedWhileKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n\n    // Obtain the handle for node A\n    checkCudaErrors(\n        cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n\n    // Insert conditional node B\n    cudaGraphNode_t     conditionalNode;\n    cudaGraphNodeParams cParams = {cudaGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeWhile;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(cudaGraphAddNode(&conditionalNode, graph, dependencies, NULL, numDependencies, &cParams));\n\n    cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Update stream capture dependencies to account for the node we manually added\n    checkCudaErrors(cudaStreamUpdateCaptureDependencies(\n        captureStream, &conditionalNode, NULL, 1, cudaStreamSetCaptureDependencies));\n\n    // Insert kernel node D\n    capturedWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n\n    checkCudaErrors(cudaStreamEndCapture(captureStream, &graph));\n    checkCudaErrors(cudaStreamDestroy(captureStream));\n\n    // Populate conditional body graph using stream capture\n    cudaStream_t bodyStream;\n    checkCudaErrors(cudaStreamCreate(&bodyStream));\n\n    checkCudaErrors(\n        cudaStreamBeginCaptureToGraph(bodyStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));\n\n    // Insert kernel node C\n    capturedWhileKernel<<<1, 1, 0, bodyStream>>>(dPtr, handle);\n    checkCudaErrors(cudaStreamEndCapture(bodyStream, nullptr));\n    checkCudaErrors(cudaStreamDestroy(bodyStream));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    // Device memory is zero, so the conditional node will not execute\n    checkCudaErrors(cudaMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with loop counter set to 0\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10\n    printf(\"Host: Launching graph with loop counter set to 10\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaFree(dPtr));\n\n    printf(\"capturedWhileGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional IF node containing two graphs.\n * The first graph within the conditional will be executed when the condition is true, while the second\n * graph will be executed when the conditional is false.\n * The kernel sets the condition variable to true if a device memory location\n * contains an odd number. Otherwise the condition variable is set to false.\n * There is a single kernel(C & D) within each conditional body which prints a message.\n *\n * A -> B [ C | D ]\n *\n * This example requires CUDA >= 12.8.\n */\n\n// This kernel will only be executed if the condition is false\n__global__ void ifGraphKernelD(void) { printf(\"GPU: Hello from the GPU! The condition was false.\\n\"); }\n\n// Setup and launch the graph\nvoid simpleIfElseGraph(void)\n{\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec;\n    cudaGraphNode_t kernelNode;\n    cudaGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(cudaMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleIfElseGraph: Building graph...\\n\");\n    cudaGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    cudaGraphNodeParams params = {cudaGraphNodeTypeKernel};\n    params.kernel.func         = (void *)ifGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(cudaGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    cudaGraphNodeParams cParams = {cudaGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeIf;\n    cParams.conditional.size    = 2; // Set size to 2 to indicate an ELSE graph will be used\n    checkCudaErrors(cudaGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Populate the body of the first graph in the conditional node, executed if the condition is true\n    cudaGraphNode_t trueBodyNode;\n    params.kernel.func         = (void *)ifGraphKernelC;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(cudaGraphAddNode(&trueBodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    // Populate the body of the second graph in the conditional node, executed if the condition is false\n    bodyGraph = cParams.conditional.phGraph_out[1];\n\n    cudaGraphNode_t falseBodyNode;\n    params.kernel.func         = (void *)ifGraphKernelD;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(cudaGraphAddNode(&falseBodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with device memory set to 0\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(cudaMemset(dPtr, 1, 1)); // Set dPtr to 1\n    printf(\"Host: Launching graph with device memory set to 1\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaFree(dPtr));\n\n    printf(\"simpleIfElseGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional SWITCH node containing four graphs.\n * The nth graph within the conditional will be executed when the condition is n. If conditional >= n,\n * no graph will be executed.\n * Kernel A sets the condition variable to the value stored in a device memory location.\n * This device location is updated from the host with each launch to demonstrate the behavior.\n * There is a single kernel(nodes C, D, E and F) within each conditional body which prints a message.\n *\n * A -> B [ C | D | E | F ]\n *\n * This example requires CUDA >= 12.8.\n */\n\n__global__ void switchGraphKernelA(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    unsigned int value = *dPtr;\n    cudaGraphSetConditional(handle, value);\n    printf(\"GPU: Handle set to %d\\n\", value);\n}\n\n__global__ void switchGraphKernelC(void) { printf(\"GPU: Hello from switchGraphKernelC(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelD(void) { printf(\"GPU: Hello from switchGraphKernelD(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelE(void) { printf(\"GPU: Hello from switchGraphKernelE(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelF(void) { printf(\"GPU: Hello from switchGraphKernelF(), running on the GPU!\\n\"); }\n\n// Setup and launch the graph\nvoid simpleSwitchGraph(void)\n{\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec;\n    cudaGraphNode_t kernelNode;\n    cudaGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(cudaMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleSwitchGraph: Building graph...\\n\");\n    cudaGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    cudaGraphNodeParams params = {cudaGraphNodeTypeKernel};\n    params.kernel.func         = (void *)switchGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(cudaGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    cudaGraphNodeParams cParams = {cudaGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeSwitch;\n    cParams.conditional.size    = 4;\n    checkCudaErrors(cudaGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    // Populate the four graph bodies within the SWITCH conditional graph\n    cudaGraphNode_t bodyNode;\n    params.kernel.kernelParams = nullptr;\n    params.kernel.func         = (void *)switchGraphKernelC;\n    checkCudaErrors(cudaGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[0], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelD;\n    checkCudaErrors(cudaGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[1], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelE;\n    checkCudaErrors(cudaGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[2], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelF;\n    checkCudaErrors(cudaGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[3], NULL, NULL, 0, &params));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    for (char i = 0; i < 5; i++) {\n        // Initialize device memory and launch the graph\n        checkCudaErrors(cudaMemset(dPtr, i, 1));\n        printf(\"Host: Launching graph with device memory set to %d\\n\", i);\n        checkCudaErrors(cudaGraphLaunch(graphExec, 0));\n        checkCudaErrors(cudaDeviceSynchronize());\n    }\n\n    // Cleanup\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaFree(dPtr));\n\n    printf(\"simpleSwitchGraph: Complete\\n\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    int device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion = 0;\n\n    cudaDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 12030) {\n        printf(\"Skipping execution as driver does not support Graph Conditional Nodes\\n\");\n        return 0;\n    }\n\n    simpleIfGraph();\n    simpleDoWhileGraph();\n    capturedWhileGraph();\n\n    if (driverVersion < 12080) {\n        printf(\"Skipping execution as driver does not support if/else and switch type Graph Conditional Nodes\\n\");\n        return 0;\n    }\n\n    simpleIfElseGraph();\n    simpleSwitchGraph();\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This file demonstrates the usage of conditional graph nodes with\n * a series of *simple* example graphs.\n *\n * For more information on conditional nodes, see the programming guide:\n *\n *   https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#conditional-graph-nodes\n *\n */\n\n// System includes\n#include <cassert>\n#include <cstdio>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional IF node.\n * The kernel sets the condition variable to true if a device memory location\n * contains an odd number. Otherwise the condition variable is set to false.\n * There is a single kernel, C, within the conditional body which prints a message.\n *\n * A -> B [ C ]\n *\n */\n\n__global__ void ifGraphKernelA(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    // In this example, condition is set if *dPtr is odd\n    unsigned int value = *dPtr & 0x01;\n    cudaGraphSetConditional(handle, value);\n    printf(\"GPU: Handle set to %d\\n\", value);\n}\n\n// This kernel will only be executed if the condition is true\n__global__ void ifGraphKernelC(void) { printf(\"GPU: Hello from the GPU! The condition was true.\\n\"); }\n\n// Setup and launch the graph\nvoid simpleIfGraph(void)\n{\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec;\n    hipGraphNode_t kernelNode;\n    hipGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(hipMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleIfGraph: Building graph...\\n\");\n    hipGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    hipGraphNodeParams params = {hipGraphNodeTypeKernel};\n    params.kernel.func         = (void *)ifGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(hipGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    hipGraphNodeParams cParams = {hipGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeIf;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(hipGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Populate the body of the conditional node\n    hipGraphNode_t bodyNode;\n    params.kernel.func         = (void *)ifGraphKernelC;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(hipGraphAddNode(&bodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with device memory set to 0\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 1, 1)); // Set dPtr to 1\n    printf(\"Host: Launching graph with device memory set to 1\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipFree(dPtr));\n\n    printf(\"simpleIfGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a single conditional while node.\n * The default value of the conditional variable is set to true, so this\n * effectively becomes a do-while loop as the conditional body will always\n * execute at least once. The body of the conditional contains 3 kernel nodes:\n * A [ B -> C -> D ]\n * Nodes B and C are just dummy nodes for demonstrative purposes. Node D\n * will decrement a device memory location and set the condition value to false\n * when the value reaches zero, terminating the loop.\n * In this example, stream capture is used to populate the conditional body.\n */\n\n// This kernel will only be executed if the condition is true\n__global__ void doWhileEmptyKernel(void)\n{\n    printf(\"GPU: doWhileEmptyKernel()\\n\");\n    return;\n}\n\n__global__ void doWhileLoopKernel(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    if (--(*dPtr) == 0) {\n        cudaGraphSetConditional(handle, 0);\n    }\n    printf(\"GPU: counter = %d\\n\", *dPtr);\n}\n\nvoid simpleDoWhileGraph(void)\n{\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec;\n    hipGraphNode_t conditionalNode;\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(hipMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleDoWhileGraph: Building graph...\\n\");\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n\n    cudaGraphConditionalHandle handle;\n    checkCudaErrors(cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault));\n\n    hipGraphNodeParams cParams = {hipGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeWhile;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(hipGraphAddNode(&conditionalNode, graph, NULL, NULL, 0, &cParams));\n\n    hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    hipStream_t captureStream;\n    checkCudaErrors(hipStreamCreate(&captureStream));\n\n    checkCudaErrors(\n        hipStreamBeginCaptureToGraph(captureStream, bodyGraph, nullptr, nullptr, 0, hipStreamCaptureModeGlobal));\n    doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n    doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n    doWhileLoopKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n    checkCudaErrors(hipStreamEndCapture(captureStream, nullptr));\n    checkCudaErrors(hipStreamDestroy(captureStream));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 10, 1)); // Set dPtr to 10\n    printf(\"Host: Launching graph with loop counter set to 10\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipFree(dPtr));\n\n    printf(\"simpleDoWhileGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a conditional while loop using stream capture.\n * This demonstrates how to insert a conditional node into a stream which is\n * being captured. The graph consists of a kernel node, A, followed by a\n * conditional while node, B, followed by a kernel node, D. The conditional\n * body is populated by a single kernel node, C:\n *\n * A -> B [ C ] -> D\n *\n * The same kernel will be used for both nodes A and C. This kernel will test\n * a device memory location and set the condition when the location is non-zero.\n * We must run the kernel before the loop as well as inside the loop in order\n * to behave like a while loop as opposed to a do-while loop. We need to evaluate\n * the device memory location before the conditional node is evaluated in order\n * to set the condition variable properly. Because we're using a kernel upstream\n * of the conditional node, there is no need to use the handle default value to\n * initialize the conditional value.\n */\n\n__global__ void capturedWhileKernel(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    printf(\"GPU: counter = %d\\n\", *dPtr);\n    if (*dPtr) {\n        (*dPtr)--;\n    }\n    cudaGraphSetConditional(handle, *dPtr);\n}\n\n__global__ void capturedWhileEmptyKernel(void)\n{\n    printf(\"GPU: capturedWhileEmptyKernel()\\n\");\n    return;\n}\n\nvoid capturedWhileGraph(void)\n{\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec;\n\n    hipStreamCaptureStatus status;\n    const hipGraphNode_t  *dependencies;\n    size_t                  numDependencies;\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(hipMalloc((void **)&dPtr, 1));\n\n    printf(\"capturedWhileGraph: Building graph...\\n\");\n    hipStream_t captureStream;\n    checkCudaErrors(hipStreamCreate(&captureStream));\n\n    checkCudaErrors(hipStreamBeginCapture(captureStream, hipStreamCaptureModeGlobal));\n\n    // Obtain the handle of the graph\n    checkCudaErrors(\n        hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n\n    // Create the conditional handle\n    cudaGraphConditionalHandle handle;\n    checkCudaErrors(cudaGraphConditionalHandleCreate(&handle, graph));\n\n    // Insert kernel node A\n    capturedWhileKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n\n    // Obtain the handle for node A\n    checkCudaErrors(\n        hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n\n    // Insert conditional node B\n    hipGraphNode_t     conditionalNode;\n    hipGraphNodeParams cParams = {hipGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeWhile;\n    cParams.conditional.size    = 1;\n    checkCudaErrors(hipGraphAddNode(&conditionalNode, graph, dependencies, NULL, numDependencies, &cParams));\n\n    hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Update stream capture dependencies to account for the node we manually added\n    checkCudaErrors(hipStreamUpdateCaptureDependencies(\n        captureStream, &conditionalNode, NULL, 1, hipStreamSetCaptureDependencies));\n\n    // Insert kernel node D\n    capturedWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n\n    checkCudaErrors(hipStreamEndCapture(captureStream, &graph));\n    checkCudaErrors(hipStreamDestroy(captureStream));\n\n    // Populate conditional body graph using stream capture\n    hipStream_t bodyStream;\n    checkCudaErrors(hipStreamCreate(&bodyStream));\n\n    checkCudaErrors(\n        hipStreamBeginCaptureToGraph(bodyStream, bodyGraph, nullptr, nullptr, 0, hipStreamCaptureModeGlobal));\n\n    // Insert kernel node C\n    capturedWhileKernel<<<1, 1, 0, bodyStream>>>(dPtr, handle);\n    checkCudaErrors(hipStreamEndCapture(bodyStream, nullptr));\n    checkCudaErrors(hipStreamDestroy(bodyStream));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    // Device memory is zero, so the conditional node will not execute\n    checkCudaErrors(hipMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with loop counter set to 0\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 10, 1)); // Set dPtr to 10\n    printf(\"Host: Launching graph with loop counter set to 10\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipFree(dPtr));\n\n    printf(\"capturedWhileGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional IF node containing two graphs.\n * The first graph within the conditional will be executed when the condition is true, while the second\n * graph will be executed when the conditional is false.\n * The kernel sets the condition variable to true if a device memory location\n * contains an odd number. Otherwise the condition variable is set to false.\n * There is a single kernel(C & D) within each conditional body which prints a message.\n *\n * A -> B [ C | D ]\n *\n * This example requires CUDA >= 12.8.\n */\n\n// This kernel will only be executed if the condition is false\n__global__ void ifGraphKernelD(void) { printf(\"GPU: Hello from the GPU! The condition was false.\\n\"); }\n\n// Setup and launch the graph\nvoid simpleIfElseGraph(void)\n{\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec;\n    hipGraphNode_t kernelNode;\n    hipGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(hipMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleIfElseGraph: Building graph...\\n\");\n    hipGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    hipGraphNodeParams params = {hipGraphNodeTypeKernel};\n    params.kernel.func         = (void *)ifGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(hipGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    hipGraphNodeParams cParams = {hipGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeIf;\n    cParams.conditional.size    = 2; // Set size to 2 to indicate an ELSE graph will be used\n    checkCudaErrors(hipGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n    // Populate the body of the first graph in the conditional node, executed if the condition is true\n    hipGraphNode_t trueBodyNode;\n    params.kernel.func         = (void *)ifGraphKernelC;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(hipGraphAddNode(&trueBodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    // Populate the body of the second graph in the conditional node, executed if the condition is false\n    bodyGraph = cParams.conditional.phGraph_out[1];\n\n    hipGraphNode_t falseBodyNode;\n    params.kernel.func         = (void *)ifGraphKernelD;\n    params.kernel.kernelParams = nullptr;\n    checkCudaErrors(hipGraphAddNode(&falseBodyNode, bodyGraph, NULL, NULL, 0, &params));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 0, 1)); // Set dPtr to 0\n    printf(\"Host: Launching graph with device memory set to 0\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Initialize device memory and launch the graph\n    checkCudaErrors(hipMemset(dPtr, 1, 1)); // Set dPtr to 1\n    printf(\"Host: Launching graph with device memory set to 1\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    // Cleanup\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipFree(dPtr));\n\n    printf(\"simpleIfElseGraph: Complete\\n\\n\");\n}\n\n/*\n * Create a graph containing two nodes.\n * The first node, A, is a kernel and the second node, B, is a conditional SWITCH node containing four graphs.\n * The nth graph within the conditional will be executed when the condition is n. If conditional >= n,\n * no graph will be executed.\n * Kernel A sets the condition variable to the value stored in a device memory location.\n * This device location is updated from the host with each launch to demonstrate the behavior.\n * There is a single kernel(nodes C, D, E and F) within each conditional body which prints a message.\n *\n * A -> B [ C | D | E | F ]\n *\n * This example requires CUDA >= 12.8.\n */\n\n__global__ void switchGraphKernelA(char *dPtr, cudaGraphConditionalHandle handle)\n{\n    unsigned int value = *dPtr;\n    cudaGraphSetConditional(handle, value);\n    printf(\"GPU: Handle set to %d\\n\", value);\n}\n\n__global__ void switchGraphKernelC(void) { printf(\"GPU: Hello from switchGraphKernelC(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelD(void) { printf(\"GPU: Hello from switchGraphKernelD(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelE(void) { printf(\"GPU: Hello from switchGraphKernelE(), running on the GPU!\\n\"); }\n\n__global__ void switchGraphKernelF(void) { printf(\"GPU: Hello from switchGraphKernelF(), running on the GPU!\\n\"); }\n\n// Setup and launch the graph\nvoid simpleSwitchGraph(void)\n{\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec;\n    hipGraphNode_t kernelNode;\n    hipGraphNode_t conditionalNode;\n\n    void *kernelArgs[2];\n\n    // Allocate a byte of device memory to use as input\n    char *dPtr;\n    checkCudaErrors(hipMalloc((void **)&dPtr, 1));\n\n    printf(\"simpleSwitchGraph: Building graph...\\n\");\n    hipGraphCreate(&graph, 0);\n\n    // Create conditional handle.\n    cudaGraphConditionalHandle handle;\n    cudaGraphConditionalHandleCreate(&handle, graph);\n\n    // Use a kernel upstream of the conditional to set the handle value\n    hipGraphNodeParams params = {hipGraphNodeTypeKernel};\n    params.kernel.func         = (void *)switchGraphKernelA;\n    params.kernel.blockDim.x = params.kernel.blockDim.y = params.kernel.blockDim.z = 1;\n    params.kernel.gridDim.x = params.kernel.gridDim.y = params.kernel.gridDim.z = 1;\n    params.kernel.kernelParams                                                  = kernelArgs;\n    kernelArgs[0]                                                               = &dPtr;\n    kernelArgs[1]                                                               = &handle;\n    checkCudaErrors(hipGraphAddNode(&kernelNode, graph, NULL, NULL, 0, &params));\n\n    hipGraphNodeParams cParams = {hipGraphNodeTypeConditional};\n    cParams.conditional.handle  = handle;\n    cParams.conditional.type    = cudaGraphCondTypeSwitch;\n    cParams.conditional.size    = 4;\n    checkCudaErrors(hipGraphAddNode(&conditionalNode, graph, &kernelNode, NULL, 0, &cParams));\n\n    // Populate the four graph bodies within the SWITCH conditional graph\n    hipGraphNode_t bodyNode;\n    params.kernel.kernelParams = nullptr;\n    params.kernel.func         = (void *)switchGraphKernelC;\n    checkCudaErrors(hipGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[0], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelD;\n    checkCudaErrors(hipGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[1], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelE;\n    checkCudaErrors(hipGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[2], NULL, NULL, 0, &params));\n    params.kernel.func = (void *)switchGraphKernelF;\n    checkCudaErrors(hipGraphAddNode(&bodyNode, cParams.conditional.phGraph_out[3], NULL, NULL, 0, &params));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    for (char i = 0; i < 5; i++) {\n        // Initialize device memory and launch the graph\n        checkCudaErrors(hipMemset(dPtr, i, 1));\n        printf(\"Host: Launching graph with device memory set to %d\\n\", i);\n        checkCudaErrors(hipGraphLaunch(graphExec, 0));\n        checkCudaErrors(hipDeviceSynchronize());\n    }\n\n    // Cleanup\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipFree(dPtr));\n\n    printf(\"simpleSwitchGraph: Complete\\n\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    int device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion = 0;\n\n    hipDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 12030) {\n        printf(\"Skipping execution as driver does not support Graph Conditional Nodes\\n\");\n        return 0;\n    }\n\n    simpleIfGraph();\n    simpleDoWhileGraph();\n    capturedWhileGraph();\n\n    if (driverVersion < 12080) {\n        printf(\"Skipping execution as driver does not support if/else and switch type Graph Conditional Nodes\\n\");\n        return 0;\n    }\n\n    simpleIfElseGraph();\n    simpleSwitchGraph();\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/graphConditionalNodes/graphConditionalNodes.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a __nv_bfloat16 (E8M7) GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_gemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 128 x 128 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes eight\n// 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_BF16 macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda/pipeline>\n#include <cuda_bf16.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n// GEMM configuration.\n\n#define M_TILES 512\n#define N_TILES 512\n#define K_TILES 512\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that is (M = 16) * (K = 16) * 8 * (CHUNK_K = 8)\n// * sizeof(__nv_bfloat16) = 32 Kb each.\n// (i.e. two 8x8 arrays of tiles of 16x16 __nv_bfloat16-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(__nv_bfloat16))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 16 two-byte \"__nv_bfloat16\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_BF16 16\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        cudaError_t __err = cudaGetLastError();                                               \\\n        if (__err != cudaSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, cudaGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    bf16mma_shmem_gemm_async_copy = 0, // __nv_bfloat16 MMA shmem using kernel with async_copy\n    bf16mma_shmem_gemm            = 1, // __nv_bfloat16 MMA shmem using kernel normal copy (without async_copy).\n    simple_bf16mma_gemm           = 2  // __nv_bfloat16 MMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_bf16gemm_async_copy\", \"compute_bf16gemm\", \"simple_wmma_bf16gemm\"};\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(__nv_bfloat16 *a, __nv_bfloat16 *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (__nv_bfloat16)(float)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (__nv_bfloat16)(float)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (float)(rand() % 3);\n    }\n}\n\n__global__ void\ncompute_bf16gemm(const __nv_bfloat16 *A, const __nv_bfloat16 *B, const float *C, float *D, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ __nv_bfloat16 shmem[][CHUNK_K * K + SKEW_BF16];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const __nv_bfloat16 *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const __nv_bfloat16 *lane_ptr = (warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, __nv_bfloat16, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, __nv_bfloat16, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t               shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const __nv_bfloat16 *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_BF16);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const __nv_bfloat16 *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_BF16);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((float4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((float4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void compute_bf16gemm_async_copy(const __nv_bfloat16 *A,\n                                            const __nv_bfloat16 *B,\n                                            const float         *C,\n                                            float               *D,\n                                            float                alpha,\n                                            float                beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ __nv_bfloat16 shmem[][CHUNK_K * K + SKEW_BF16];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe       = cuda::make_pipeline();\n    const auto                                shape4     = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    constexpr int                             loadStride = 2; // load 4 floats, left-shift by 2.\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape4,\n                               pipe);\n\n            pipe.producer_commit();\n        }\n\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        pipe.consumer_release();\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const __nv_bfloat16 *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2;\n        constexpr int loadStrideBfloat8 = 3; // load 8 bfloats, left-shift by 3.\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStrideBfloat8;\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for bf16 MMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + ((warpId / (WARPS_PER_BLOCK / 2)) * shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const __nv_bfloat16 *lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape4, pipe);\n                pipe.producer_commit();\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, __nv_bfloat16, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, __nv_bfloat16, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t               shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const __nv_bfloat16 *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_BF16);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const __nv_bfloat16 *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_BF16);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n// Performs an MxNxK bf16 GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16, 16 and 16 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_bf16gemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void simple_wmma_bf16gemm(__nv_bfloat16 *a,\n                                     __nv_bfloat16 *b,\n                                     float         *c,\n                                     float         *d,\n                                     int            m_ld,\n                                     int            n_ld,\n                                     int            k_ld,\n                                     float          alpha,\n                                     float          beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, __nv_bfloat16, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, __nv_bfloat16, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                       acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                       c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        int bCol = i;\n        int bRow = warpN * N;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(__nv_bfloat16 *A,\n                                __nv_bfloat16 *B,\n                                float         *C,\n                                float          alpha,\n                                float          beta,\n                                int            numARows,\n                                int            numAColumns,\n                                int            numBRows,\n                                int            numBColumns,\n                                int            numCRows,\n                                int            numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += (float)A[i * numAColumns + k] * (float)B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"bf16TensorCoreGemm requires requires SM 8.0 or higher to use Tensor Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    __nv_bfloat16 *A_h = NULL;\n    __nv_bfloat16 *B_h = NULL;\n    float         *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (__nv_bfloat16 *)malloc(sizeof(__nv_bfloat16) * M_GLOBAL * K_GLOBAL);\n    B_h = (__nv_bfloat16 *)malloc(sizeof(__nv_bfloat16) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    __nv_bfloat16 *A = NULL;\n    __nv_bfloat16 *B = NULL;\n    float         *C = NULL;\n    float         *D = NULL;\n\n    checkCudaErrors(cudaMalloc((void **)&A, sizeof(__nv_bfloat16) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&B, sizeof(__nv_bfloat16) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&C, sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&D, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(cudaMemcpy(A, A_h, sizeof(__nv_bfloat16) * M_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(B, B_h, sizeof(__nv_bfloat16) * N_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(__nv_bfloat16) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_BF16) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    cudaEvent_t start, stop;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n\n    // kernel to run - default (b16mma_shmem_gemm_async_copy == 0)\n    kernels selected_kernel = bf16mma_shmem_gemm_async_copy;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 3) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 2, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_bf16mma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case bf16mma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(cudaFuncSetAttribute(\n                compute_bf16gemm_async_copy, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_bf16gemm_async_copy<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case bf16mma_shmem_gemm:\n            checkCudaErrors(\n                cudaFuncSetAttribute(compute_bf16gemm, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_bf16gemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_bf16gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(cudaFree((void *)A));\n    checkCudaErrors(cudaFree((void *)B));\n    checkCudaErrors(cudaFree((void *)C));\n    checkCudaErrors(cudaFree((void *)D));\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a __nv_bfloat16 (E8M7) GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_gemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 128 x 128 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes eight\n// 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_BF16 macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <cuda/pipeline>\n#include <hip/hip_bf16.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n// GEMM configuration.\n\n#define M_TILES 512\n#define N_TILES 512\n#define K_TILES 512\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that is (M = 16) * (K = 16) * 8 * (CHUNK_K = 8)\n// * sizeof(__nv_bfloat16) = 32 Kb each.\n// (i.e. two 8x8 arrays of tiles of 16x16 __nv_bfloat16-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(__hip_bfloat16))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 16 two-byte \"__nv_bfloat16\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_BF16 16\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        hipError_t __err = hipGetLastError();                                               \\\n        if (__err != hipSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, hipGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    bf16mma_shmem_gemm_async_copy = 0, // __nv_bfloat16 MMA shmem using kernel with async_copy\n    bf16mma_shmem_gemm            = 1, // __nv_bfloat16 MMA shmem using kernel normal copy (without async_copy).\n    simple_bf16mma_gemm           = 2  // __nv_bfloat16 MMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_bf16gemm_async_copy\", \"compute_bf16gemm\", \"simple_wmma_bf16gemm\"};\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(__hip_bfloat16 *a, __hip_bfloat16 *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (__hip_bfloat16)(float)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (__hip_bfloat16)(float)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (float)(rand() % 3);\n    }\n}\n\n__global__ void\ncompute_bf16gemm(const __hip_bfloat16 *A, const __hip_bfloat16 *B, const float *C, float *D, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ __hip_bfloat16 shmem[][CHUNK_K * K + SKEW_BF16];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const __hip_bfloat16 *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const __hip_bfloat16 *lane_ptr = (warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, __hip_bfloat16, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, __hip_bfloat16, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t               shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const __hip_bfloat16 *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_BF16);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const __hip_bfloat16 *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_BF16);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((float4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((float4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void compute_bf16gemm_async_copy(const __hip_bfloat16 *A,\n                                            const __hip_bfloat16 *B,\n                                            const float         *C,\n                                            float               *D,\n                                            float                alpha,\n                                            float                beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ __hip_bfloat16 shmem[][CHUNK_K * K + SKEW_BF16];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe       = cuda::make_pipeline();\n    const auto                                shape4     = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    constexpr int                             loadStride = 2; // load 4 floats, left-shift by 2.\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape4,\n                               pipe);\n\n            pipe.producer_commit();\n        }\n\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        pipe.consumer_release();\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const __hip_bfloat16 *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2;\n        constexpr int loadStrideBfloat8 = 3; // load 8 bfloats, left-shift by 3.\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStrideBfloat8;\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for bf16 MMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + ((warpId / (WARPS_PER_BLOCK / 2)) * shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const __hip_bfloat16 *lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape4, pipe);\n                pipe.producer_commit();\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, __hip_bfloat16, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, __hip_bfloat16, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t               shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const __hip_bfloat16 *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_BF16);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const __hip_bfloat16 *tile_ptr = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_BF16);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n// Performs an MxNxK bf16 GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16, 16 and 16 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_bf16gemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void simple_wmma_bf16gemm(__hip_bfloat16 *a,\n                                     __hip_bfloat16 *b,\n                                     float         *c,\n                                     float         *d,\n                                     int            m_ld,\n                                     int            n_ld,\n                                     int            k_ld,\n                                     float          alpha,\n                                     float          beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, __hip_bfloat16, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, __hip_bfloat16, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                       acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                       c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        int bCol = i;\n        int bRow = warpN * N;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(__hip_bfloat16 *A,\n                                __hip_bfloat16 *B,\n                                float         *C,\n                                float          alpha,\n                                float          beta,\n                                int            numARows,\n                                int            numAColumns,\n                                int            numBRows,\n                                int            numBColumns,\n                                int            numCRows,\n                                int            numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += (float)A[i * numAColumns + k] * (float)B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"bf16TensorCoreGemm requires requires SM 8.0 or higher to use Tensor Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    __hip_bfloat16 *A_h = NULL;\n    __hip_bfloat16 *B_h = NULL;\n    float         *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (__hip_bfloat16 *)malloc(sizeof(__hip_bfloat16) * M_GLOBAL * K_GLOBAL);\n    B_h = (__hip_bfloat16 *)malloc(sizeof(__hip_bfloat16) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    __hip_bfloat16 *A = NULL;\n    __hip_bfloat16 *B = NULL;\n    float         *C = NULL;\n    float         *D = NULL;\n\n    checkCudaErrors(hipMalloc((void **)&A, sizeof(__hip_bfloat16) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&B, sizeof(__hip_bfloat16) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&C, sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&D, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(hipMemcpy(A, A_h, sizeof(__hip_bfloat16) * M_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(B, B_h, sizeof(__hip_bfloat16) * N_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(__hip_bfloat16) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_BF16) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    hipEvent_t start, stop;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n\n    // kernel to run - default (b16mma_shmem_gemm_async_copy == 0)\n    kernels selected_kernel = bf16mma_shmem_gemm_async_copy;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 3) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 2, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_bf16mma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case bf16mma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_bf16gemm_async_copy), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_bf16gemm_async_copy<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case bf16mma_shmem_gemm:\n            checkCudaErrors(\n                hipFuncSetAttribute(reinterpret_cast<const void*>(compute_bf16gemm), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_bf16gemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_bf16gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(hipEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(hipFree((void *)A));\n    checkCudaErrors(hipFree((void *)B));\n    checkCudaErrors(hipFree((void *)C));\n    checkCudaErrors(hipFree((void *)D));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/bf16TensorCoreGemm/bf16TensorCoreGemm.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <vector>\n\n#include \"jacobi.h\"\n\nnamespace cg = cooperative_groups;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600\n#else\n__device__ double atomicAdd(double *address, double val)\n{\n    unsigned long long int *address_as_ull = (unsigned long long int *)address;\n    unsigned long long int  old            = *address_as_ull, assumed;\n\n    do {\n        assumed = old;\n        old     = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));\n\n        // Note: uses integer comparison to avoid hang in case of NaN (since NaN !=\n        // NaN)\n    } while (assumed != old);\n\n    return __longlong_as_double(old);\n}\n#endif\n\nstatic __global__ void\nJacobiMethod(const float *A, const double *b, const float conv_threshold, double *x, double *x_new, double *sum)\n{\n    // Handle to thread block group\n    cg::thread_block  cta = cg::this_thread_block();\n    __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n    __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n    for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x) {\n        x_shared[i] = x[i];\n    }\n\n    if (threadIdx.x < ROWS_PER_CTA) {\n        int k = threadIdx.x;\n        for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n             k += ROWS_PER_CTA, i += ROWS_PER_CTA) {\n            b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n        }\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++) {\n        double rowThreadSum = 0.0;\n        for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x) {\n            rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n        }\n\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n        }\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n        }\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < ROWS_PER_CTA) {\n        cg::thread_block_tile<ROWS_PER_CTA> tile8    = cg::tiled_partition<ROWS_PER_CTA>(cta);\n        double                              temp_sum = 0.0;\n\n        int k = threadIdx.x;\n\n        for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n             k += ROWS_PER_CTA, i += ROWS_PER_CTA) {\n            double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n            dx /= A[i * N_ROWS + i];\n\n            x_new[i] = (x_shared[i] + dx);\n            temp_sum += fabs(dx);\n        }\n\n        for (int offset = tile8.size() / 2; offset > 0; offset /= 2) {\n            temp_sum += tile8.shfl_down(temp_sum, offset);\n        }\n\n        if (tile8.thread_rank() == 0) {\n            atomicAdd(sum, temp_sum);\n        }\n    }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(double *x, double *g_sum)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    extern __shared__ double warpSum[];\n    double                   sum = 0.0;\n\n    int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x) {\n        double d = x[i] - 1.0;\n        sum += fabs(d);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n        sum += tile32.shfl_down(sum, offset);\n    }\n\n    if (tile32.thread_rank() == 0) {\n        warpSum[threadIdx.x / warpSize] = sum;\n    }\n\n    cg::sync(cta);\n\n    double blockSum = 0.0;\n    if (threadIdx.x < (blockDim.x / warpSize)) {\n        blockSum = warpSum[threadIdx.x];\n    }\n\n    if (threadIdx.x < 32) {\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            blockSum += tile32.shfl_down(blockSum, offset);\n        }\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(g_sum, blockSum);\n        }\n    }\n}\n\ndouble JacobiMethodGpuCudaGraphExecKernelSetParams(const float  *A,\n                                                   const double *b,\n                                                   const float   conv_threshold,\n                                                   const int     max_iter,\n                                                   double       *x,\n                                                   double       *x_new,\n                                                   cudaStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3            nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec = NULL;\n\n    double  sum   = 0.0;\n    double *d_sum = NULL;\n    checkCudaErrors(cudaMalloc(&d_sum, sizeof(double)));\n\n    std::vector<cudaGraphNode_t> nodeDependencies;\n    cudaGraphNode_t              memcpyNode, jacobiKernelNode, memsetNode;\n    cudaMemcpy3DParms            memcpyParams = {0};\n    cudaMemsetParams             memsetParams = {0};\n\n    memsetParams.dst   = (void *)d_sum;\n    memsetParams.value = 0;\n    memsetParams.pitch = 0;\n    // elementSize can be max 4 bytes, so we take sizeof(float) and width=2\n    memsetParams.elementSize = sizeof(float);\n    memsetParams.width       = 2;\n    memsetParams.height      = 1;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n    checkCudaErrors(cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n    nodeDependencies.push_back(memsetNode);\n\n    cudaKernelNodeParams NodeParams0, NodeParams1;\n    NodeParams0.func           = (void *)JacobiMethod;\n    NodeParams0.gridDim        = nblocks;\n    NodeParams0.blockDim       = nthreads;\n    NodeParams0.sharedMemBytes = 0;\n    void *kernelArgs0[6]       = {\n        (void *)&A, (void *)&b, (void *)&conv_threshold, (void *)&x, (void *)&x_new, (void *)&d_sum};\n    NodeParams0.kernelParams = kernelArgs0;\n    NodeParams0.extra        = NULL;\n\n    checkCudaErrors(cudaGraphAddKernelNode(\n        &jacobiKernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &NodeParams0));\n\n    nodeDependencies.clear();\n    nodeDependencies.push_back(jacobiKernelNode);\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_cudaPitchedPtr(d_sum, sizeof(double), 1, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_cudaPitchedPtr(&sum, sizeof(double), 1, 1);\n    memcpyParams.extent   = make_cudaExtent(sizeof(double), 1, 1);\n    memcpyParams.kind     = cudaMemcpyDeviceToHost;\n\n    checkCudaErrors(\n        cudaGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    NodeParams1.func           = (void *)JacobiMethod;\n    NodeParams1.gridDim        = nblocks;\n    NodeParams1.blockDim       = nthreads;\n    NodeParams1.sharedMemBytes = 0;\n    void *kernelArgs1[6]       = {\n        (void *)&A, (void *)&b, (void *)&conv_threshold, (void *)&x_new, (void *)&x, (void *)&d_sum};\n    NodeParams1.kernelParams = kernelArgs1;\n    NodeParams1.extra        = NULL;\n\n    int k = 0;\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(cudaGraphExecKernelNodeSetParams(\n            graphExec, jacobiKernelNode, ((k & 1) == 0) ? &NodeParams0 : &NodeParams1));\n        checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n        checkCudaErrors(cudaStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n            checkCudaErrors(cudaStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(cudaFree(d_sum));\n    return sum;\n}\n\ndouble JacobiMethodGpuCudaGraphExecUpdate(const float  *A,\n                                          const double *b,\n                                          const float   conv_threshold,\n                                          const int     max_iter,\n                                          double       *x,\n                                          double       *x_new,\n                                          cudaStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3            nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n    cudaGraph_t     graph;\n    cudaGraphExec_t graphExec = NULL;\n\n    double  sum = 0.0;\n    double *d_sum;\n    checkCudaErrors(cudaMalloc(&d_sum, sizeof(double)));\n\n    int k = 0;\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal));\n        checkCudaErrors(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n        if ((k & 1) == 0) {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n        }\n        else {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n        }\n        checkCudaErrors(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n        checkCudaErrors(cudaStreamEndCapture(stream, &graph));\n\n        if (graphExec == NULL) {\n            checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n        }\n        else {\n            cudaGraphExecUpdateResult updateResult_out;\n            checkCudaErrors(cudaGraphExecUpdate(graphExec, graph, NULL, &updateResult_out));\n            if (updateResult_out != cudaGraphExecUpdateSuccess) {\n                if (graphExec != NULL) {\n                    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n                }\n                printf(\"k = %d graph update failed with error - %d\\n\", k, updateResult_out);\n                checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n            }\n        }\n        checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n        checkCudaErrors(cudaStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n            checkCudaErrors(cudaStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(cudaFree(d_sum));\n    return sum;\n}\n\ndouble JacobiMethodGpu(const float  *A,\n                       const double *b,\n                       const float   conv_threshold,\n                       const int     max_iter,\n                       double       *x,\n                       double       *x_new,\n                       cudaStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n    double  sum = 0.0;\n    double *d_sum;\n    checkCudaErrors(cudaMalloc(&d_sum, sizeof(double)));\n    int k = 0;\n\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n        if ((k & 1) == 0) {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n        }\n        else {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n        }\n        checkCudaErrors(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n        checkCudaErrors(cudaStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n            checkCudaErrors(cudaStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(cudaFree(d_sum));\n    return sum;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <vector>\n\n#include \"jacobi.h\"\n\nnamespace cg = cooperative_groups;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600\n#else\n__device__ double atomicAdd(double *address, double val)\n{\n    unsigned long long int *address_as_ull = (unsigned long long int *)address;\n    unsigned long long int  old            = *address_as_ull, assumed;\n\n    do {\n        assumed = old;\n        old     = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));\n\n        // Note: uses integer comparison to avoid hang in case of NaN (since NaN !=\n        // NaN)\n    } while (assumed != old);\n\n    return __longlong_as_double(old);\n}\n#endif\n\nstatic __global__ void\nJacobiMethod(const float *A, const double *b, const float conv_threshold, double *x, double *x_new, double *sum)\n{\n    // Handle to thread block group\n    cg::thread_block  cta = cg::this_thread_block();\n    __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n    __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n    for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x) {\n        x_shared[i] = x[i];\n    }\n\n    if (threadIdx.x < ROWS_PER_CTA) {\n        int k = threadIdx.x;\n        for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n             k += ROWS_PER_CTA, i += ROWS_PER_CTA) {\n            b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n        }\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++) {\n        double rowThreadSum = 0.0;\n        for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x) {\n            rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n        }\n\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n        }\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n        }\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < ROWS_PER_CTA) {\n        cg::thread_block_tile<ROWS_PER_CTA> tile8    = cg::tiled_partition<ROWS_PER_CTA>(cta);\n        double                              temp_sum = 0.0;\n\n        int k = threadIdx.x;\n\n        for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n             k += ROWS_PER_CTA, i += ROWS_PER_CTA) {\n            double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n            dx /= A[i * N_ROWS + i];\n\n            x_new[i] = (x_shared[i] + dx);\n            temp_sum += fabs(dx);\n        }\n\n        for (int offset = tile8.size() / 2; offset > 0; offset /= 2) {\n            temp_sum += tile8.shfl_down(temp_sum, offset);\n        }\n\n        if (tile8.thread_rank() == 0) {\n            atomicAdd(sum, temp_sum);\n        }\n    }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(double *x, double *g_sum)\n{\n    // Handle to thread block group\n    cg::thread_block         cta = cg::this_thread_block();\n    extern __shared__ double warpSum[];\n    double                   sum = 0.0;\n\n    int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x) {\n        double d = x[i] - 1.0;\n        sum += fabs(d);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n        sum += tile32.shfl_down(sum, offset);\n    }\n\n    if (tile32.thread_rank() == 0) {\n        warpSum[threadIdx.x / warpSize] = sum;\n    }\n\n    cg::sync(cta);\n\n    double blockSum = 0.0;\n    if (threadIdx.x < (blockDim.x / warpSize)) {\n        blockSum = warpSum[threadIdx.x];\n    }\n\n    if (threadIdx.x < 32) {\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            blockSum += tile32.shfl_down(blockSum, offset);\n        }\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(g_sum, blockSum);\n        }\n    }\n}\n\ndouble JacobiMethodGpuCudaGraphExecKernelSetParams(const float  *A,\n                                                   const double *b,\n                                                   const float   conv_threshold,\n                                                   const int     max_iter,\n                                                   double       *x,\n                                                   double       *x_new,\n                                                   hipStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3            nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec = NULL;\n\n    double  sum   = 0.0;\n    double *d_sum = NULL;\n    checkCudaErrors(hipMalloc(&d_sum, sizeof(double)));\n\n    std::vector<hipGraphNode_t> nodeDependencies;\n    hipGraphNode_t              memcpyNode, jacobiKernelNode, memsetNode;\n    hipMemcpy3DParms            memcpyParams = {0};\n    hipMemsetParams             memsetParams = {0};\n\n    memsetParams.dst   = (void *)d_sum;\n    memsetParams.value = 0;\n    memsetParams.pitch = 0;\n    // elementSize can be max 4 bytes, so we take sizeof(float) and width=2\n    memsetParams.elementSize = sizeof(float);\n    memsetParams.width       = 2;\n    memsetParams.height      = 1;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n    checkCudaErrors(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n    nodeDependencies.push_back(memsetNode);\n\n    hipKernelNodeParams NodeParams0, NodeParams1;\n    NodeParams0.func           = (void *)JacobiMethod;\n    NodeParams0.gridDim        = nblocks;\n    NodeParams0.blockDim       = nthreads;\n    NodeParams0.sharedMemBytes = 0;\n    void *kernelArgs0[6]       = {\n        (void *)&A, (void *)&b, (void *)&conv_threshold, (void *)&x, (void *)&x_new, (void *)&d_sum};\n    NodeParams0.kernelParams = kernelArgs0;\n    NodeParams0.extra        = NULL;\n\n    checkCudaErrors(hipGraphAddKernelNode(\n        &jacobiKernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &NodeParams0));\n\n    nodeDependencies.clear();\n    nodeDependencies.push_back(jacobiKernelNode);\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_hipPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_hipPitchedPtr(d_sum, sizeof(double), 1, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_hipPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_hipPitchedPtr(&sum, sizeof(double), 1, 1);\n    memcpyParams.extent   = make_hipExtent(sizeof(double), 1, 1);\n    memcpyParams.kind     = hipMemcpyDeviceToHost;\n\n    checkCudaErrors(\n        hipGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    NodeParams1.func           = (void *)JacobiMethod;\n    NodeParams1.gridDim        = nblocks;\n    NodeParams1.blockDim       = nthreads;\n    NodeParams1.sharedMemBytes = 0;\n    void *kernelArgs1[6]       = {\n        (void *)&A, (void *)&b, (void *)&conv_threshold, (void *)&x_new, (void *)&x, (void *)&d_sum};\n    NodeParams1.kernelParams = kernelArgs1;\n    NodeParams1.extra        = NULL;\n\n    int k = 0;\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(hipGraphExecKernelNodeSetParams(\n            graphExec, jacobiKernelNode, ((k & 1) == 0) ? &NodeParams0 : &NodeParams1));\n        checkCudaErrors(hipGraphLaunch(graphExec, stream));\n        checkCudaErrors(hipStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n            checkCudaErrors(hipStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(hipFree(d_sum));\n    return sum;\n}\n\ndouble JacobiMethodGpuCudaGraphExecUpdate(const float  *A,\n                                          const double *b,\n                                          const float   conv_threshold,\n                                          const int     max_iter,\n                                          double       *x,\n                                          double       *x_new,\n                                          hipStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3            nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n    hipGraph_t     graph;\n    hipGraphExec_t graphExec = NULL;\n\n    double  sum = 0.0;\n    double *d_sum;\n    checkCudaErrors(hipMalloc(&d_sum, sizeof(double)));\n\n    int k = 0;\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal));\n        checkCudaErrors(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n        if ((k & 1) == 0) {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n        }\n        else {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n        }\n        checkCudaErrors(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n        checkCudaErrors(hipStreamEndCapture(stream, &graph));\n\n        if (graphExec == NULL) {\n            checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n        }\n        else {\n            hipGraphExecUpdateResult updateResult_out;\n            checkCudaErrors(hipGraphExecUpdate(graphExec, graph, NULL, &updateResult_out));\n            if (updateResult_out != hipGraphExecUpdateSuccess) {\n                if (graphExec != NULL) {\n                    checkCudaErrors(hipGraphExecDestroy(graphExec));\n                }\n                printf(\"k = %d graph update failed with error - %d\\n\", k, updateResult_out);\n                checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n            }\n        }\n        checkCudaErrors(hipGraphLaunch(graphExec, stream));\n        checkCudaErrors(hipStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n            checkCudaErrors(hipStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(hipFree(d_sum));\n    return sum;\n}\n\ndouble JacobiMethodGpu(const float  *A,\n                       const double *b,\n                       const float   conv_threshold,\n                       const int     max_iter,\n                       double       *x,\n                       double       *x_new,\n                       hipStream_t  stream)\n{\n    // CTA size\n    dim3 nthreads(256, 1, 1);\n    // grid size\n    dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n    double  sum = 0.0;\n    double *d_sum;\n    checkCudaErrors(hipMalloc(&d_sum, sizeof(double)));\n    int k = 0;\n\n    for (k = 0; k < max_iter; k++) {\n        checkCudaErrors(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n        if ((k & 1) == 0) {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n        }\n        else {\n            JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n        }\n        checkCudaErrors(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n        checkCudaErrors(hipStreamSynchronize(stream));\n\n        if (sum <= conv_threshold) {\n            checkCudaErrors(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            if ((k & 1) == 0) {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n            }\n            else {\n                finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n            }\n\n            checkCudaErrors(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n            checkCudaErrors(hipStreamSynchronize(stream));\n            printf(\"GPU iterations : %d\\n\", k + 1);\n            printf(\"GPU error : %.3e\\n\", sum);\n            break;\n        }\n    }\n\n    checkCudaErrors(hipFree(d_sum));\n    return sum;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/jacobiCudaGraphs/jacobi.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cstdio>\n#include <cstdlib>\n#include <helper_cuda.h>\n#include <helper_string.h>\n#include <iostream>\n\n////////////////////////////////////////////////////////////////////////////////\n// Variable on the GPU used to generate unique identifiers of blocks.\n////////////////////////////////////////////////////////////////////////////////\n__device__ int g_uids = 0;\n\n////////////////////////////////////////////////////////////////////////////////\n// Print a simple message to signal the block which is currently executing.\n////////////////////////////////////////////////////////////////////////////////\n__device__ void print_info(int depth, int thread, int uid, int parent_uid)\n{\n    if (threadIdx.x == 0) {\n        if (depth == 0)\n            printf(\"BLOCK %d launched by the host\\n\", uid);\n        else {\n            char buffer[32];\n\n            for (int i = 0; i < depth; ++i) {\n                buffer[3 * i + 0] = '|';\n                buffer[3 * i + 1] = ' ';\n                buffer[3 * i + 2] = ' ';\n            }\n\n            buffer[3 * depth] = '\\0';\n            printf(\"%sBLOCK %d launched by thread %d of block %d\\n\", buffer, uid, thread, parent_uid);\n        }\n    }\n\n    __syncthreads();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The kernel using CUDA dynamic parallelism.\n//\n// It generates a unique identifier for each block. Prints the information\n// about that block. Finally, if the 'max_depth' has not been reached, the\n// block launches new blocks directly from the GPU.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void cdp_kernel(int max_depth, int depth, int thread, int parent_uid)\n{\n    // We create a unique ID per block. Thread 0 does that and shares the value\n    // with the other threads.\n    __shared__ int s_uid;\n\n    if (threadIdx.x == 0) {\n        s_uid = atomicAdd(&g_uids, 1);\n    }\n\n    __syncthreads();\n\n    // We print the ID of the block and information about its parent.\n    print_info(depth, thread, s_uid, parent_uid);\n\n    // We launch new blocks if we haven't reached the max_depth yet.\n    if (++depth >= max_depth) {\n        return;\n    }\n\n    cdp_kernel<<<gridDim.x, blockDim.x>>>(max_depth, depth, threadIdx.x, s_uid);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"starting Simple Print (CUDA Dynamic Parallelism)\\n\");\n\n    // Parse a few command-line arguments.\n    int max_depth = 2;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        printf(\"Usage: %s depth=<max_depth>\\t(where max_depth is a value between 1 \"\n               \"and 8).\\n\",\n               argv[0]);\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"depth\")) {\n        max_depth = getCmdLineArgumentInt(argc, (const char **)argv, \"depth\");\n\n        if (max_depth < 1 || max_depth > 8) {\n            printf(\"depth parameter has to be between 1 and 8\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Find/set the device.\n    int            device = -1;\n    cudaDeviceProp deviceProp;\n    device = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, device));\n\n    if (!(deviceProp.major > 3 || (deviceProp.major == 3 && deviceProp.minor >= 5))) {\n        printf(\"GPU %d - %s  does not support CUDA Dynamic Parallelism\\n Exiting.\", device, deviceProp.name);\n        exit(EXIT_WAIVED);\n    }\n\n    // Print a message describing what the sample does.\n    printf(\"*********************************************************************\"\n           \"******\\n\");\n    printf(\"The CPU launches 2 blocks of 2 threads each. On the device each thread \"\n           \"will\\n\");\n    printf(\"launch 2 blocks of 2 threads each. The GPU we will do that \"\n           \"recursively\\n\");\n    printf(\"until it reaches max_depth=%d\\n\\n\", max_depth);\n    printf(\"In total 2\");\n    int num_blocks = 2, sum = 2;\n\n    for (int i = 1; i < max_depth; ++i) {\n        num_blocks *= 4;\n        printf(\"+%d\", num_blocks);\n        sum += num_blocks;\n    }\n\n    printf(\"=%d blocks are launched!!! (%d from the GPU)\\n\", sum, sum - 2);\n    printf(\"************************************************************************\"\n           \"***\\n\\n\");\n\n    // Launch the kernel from the CPU.\n    printf(\"Launching cdp_kernel() with CUDA Dynamic Parallelism:\\n\\n\");\n    cdp_kernel<<<2, 2>>>(max_depth, 0, 0, -1);\n    checkCudaErrors(cudaGetLastError());\n\n    // Finalize.\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cstdio>\n#include <cstdlib>\n#include <hip/hip_runtime_api.h>\n#include <helper_string.h>\n#include <iostream>\n\n////////////////////////////////////////////////////////////////////////////////\n// Variable on the GPU used to generate unique identifiers of blocks.\n////////////////////////////////////////////////////////////////////////////////\n__device__ int g_uids = 0;\n\n////////////////////////////////////////////////////////////////////////////////\n// Print a simple message to signal the block which is currently executing.\n////////////////////////////////////////////////////////////////////////////////\n__device__ void print_info(int depth, int thread, int uid, int parent_uid)\n{\n    if (threadIdx.x == 0) {\n        if (depth == 0)\n            printf(\"BLOCK %d launched by the host\\n\", uid);\n        else {\n            char buffer[32];\n\n            for (int i = 0; i < depth; ++i) {\n                buffer[3 * i + 0] = '|';\n                buffer[3 * i + 1] = ' ';\n                buffer[3 * i + 2] = ' ';\n            }\n\n            buffer[3 * depth] = '\\0';\n            printf(\"%sBLOCK %d launched by thread %d of block %d\\n\", buffer, uid, thread, parent_uid);\n        }\n    }\n\n    __syncthreads();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The kernel using CUDA dynamic parallelism.\n//\n// It generates a unique identifier for each block. Prints the information\n// about that block. Finally, if the 'max_depth' has not been reached, the\n// block launches new blocks directly from the GPU.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void cdp_kernel(int max_depth, int depth, int thread, int parent_uid)\n{\n    // We create a unique ID per block. Thread 0 does that and shares the value\n    // with the other threads.\n    __shared__ int s_uid;\n\n    if (threadIdx.x == 0) {\n        s_uid = atomicAdd(&g_uids, 1);\n    }\n\n    __syncthreads();\n\n    // We print the ID of the block and information about its parent.\n    print_info(depth, thread, s_uid, parent_uid);\n\n    // We launch new blocks if we haven't reached the max_depth yet.\n    if (++depth >= max_depth) {\n        return;\n    }\n\n    cdp_kernel<<<gridDim.x, blockDim.x>>>(max_depth, depth, threadIdx.x, s_uid);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"starting Simple Print (CUDA Dynamic Parallelism)\\n\");\n\n    // Parse a few command-line arguments.\n    int max_depth = 2;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        printf(\"Usage: %s depth=<max_depth>\\t(where max_depth is a value between 1 \"\n               \"and 8).\\n\",\n               argv[0]);\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"depth\")) {\n        max_depth = getCmdLineArgumentInt(argc, (const char **)argv, \"depth\");\n\n        if (max_depth < 1 || max_depth > 8) {\n            printf(\"depth parameter has to be between 1 and 8\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Find/set the device.\n    int            device = -1;\n    hipDeviceProp_t deviceProp;\n    device = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, device));\n\n    if (!(deviceProp.major > 3 || (deviceProp.major == 3 && deviceProp.minor >= 5))) {\n        printf(\"GPU %d - %s  does not support CUDA Dynamic Parallelism\\n Exiting.\", device, deviceProp.name);\n        exit(EXIT_WAIVED);\n    }\n\n    // Print a message describing what the sample does.\n    printf(\"*********************************************************************\"\n           \"******\\n\");\n    printf(\"The CPU launches 2 blocks of 2 threads each. On the device each thread \"\n           \"will\\n\");\n    printf(\"launch 2 blocks of 2 threads each. The GPU we will do that \"\n           \"recursively\\n\");\n    printf(\"until it reaches max_depth=%d\\n\\n\", max_depth);\n    printf(\"In total 2\");\n    int num_blocks = 2, sum = 2;\n\n    for (int i = 1; i < max_depth; ++i) {\n        num_blocks *= 4;\n        printf(\"+%d\", num_blocks);\n        sum += num_blocks;\n    }\n\n    printf(\"=%d blocks are launched!!! (%d from the GPU)\\n\", sum, sum - 2);\n    printf(\"************************************************************************\"\n           \"***\\n\\n\");\n\n    // Launch the kernel from the CPU.\n    printf(\"Launching cdp_kernel() with CUDA Dynamic Parallelism:\\n\\n\");\n    cdp_kernel<<<2, 2>>>(max_depth, 0, 0, -1);\n    checkCudaErrors(hipGetLastError());\n\n    // Finalize.\n    checkCudaErrors(hipDeviceSynchronize());\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpSimplePrint/cdpSimplePrint.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#define NUM_GRAPHS        8\n#define THREADS_PER_BLOCK 512\n\nvoid printMemoryFootprint(int device)\n{\n    size_t footprint;\n    checkCudaErrors(cudaDeviceGetGraphMemAttribute(device, (cudaGraphMemAttributeType)0, &footprint));\n    printf(\"    FOOTPRINT: %lu bytes\\n\", footprint);\n}\n\nvoid prepareAllocParams(cudaMemAllocNodeParams *allocParams, size_t bytes, int device)\n{\n    memset(allocParams, 0, sizeof(*allocParams));\n\n    allocParams->bytesize                = bytes;\n    allocParams->poolProps.allocType     = cudaMemAllocationTypePinned;\n    allocParams->poolProps.location.id   = device;\n    allocParams->poolProps.location.type = cudaMemLocationTypeDevice;\n}\n\nvoid createVirtAddrReuseGraph(cudaGraphExec_t *graphExec, size_t bytes, int device)\n{\n    cudaGraph_t            graph;\n    cudaGraphNode_t        allocNodeA, allocNodeB, freeNodeA, freeNodeB;\n    cudaMemAllocNodeParams allocParams;\n    float                 *d_a, *d_b;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    d_a = (float *)allocParams.dptr;\n    checkCudaErrors(cudaGraphAddMemFreeNode(&freeNodeA, graph, &allocNodeA, 1, (void *)d_a));\n\n    // The dependency between the allocation of d_b and the free of d_a allows d_b\n    // to reuse the same VA.\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeB, graph, &freeNodeA, 1, &allocParams));\n    d_b = (float *)allocParams.dptr;\n\n    if (d_a == d_b) {\n        printf(\"Check confirms that d_a and d_b share a virtual address.\\n\");\n    }\n    else {\n        printf(\"Check shows that d_a and d_b DO NOT share a virtual address.\\n\");\n    }\n\n    checkCudaErrors(cudaGraphAddMemFreeNode(&freeNodeB, graph, &allocNodeB, 1, (void *)d_b));\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\nvoid virtualAddressReuseSingleGraph(size_t bytes, int device)\n{\n    cudaStream_t    stream;\n    cudaGraphExec_t graphExec;\n\n    printf(\"================================\\n\");\n    printf(\"Running virtual address reuse example.\\n\");\n    printf(\"Sequential allocations & frees within a single graph enable CUDA to \"\n           \"reuse virtual addresses.\\n\\n\");\n\n    createVirtAddrReuseGraph(&graphExec, bytes, device);\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printMemoryFootprint(device);\n\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaStreamDestroy(stream));\n}\n\n// This is a kernel that does no real work but runs at least for a specified\n// number of clocks\n__global__ void clockBlock(clock_t clock_count)\n{\n    unsigned int start_clock = (unsigned int)clock();\n\n    clock_t clock_offset = 0;\n\n    while (clock_offset < clock_count) {\n        unsigned int end_clock = (unsigned int)clock();\n\n        // The code below should work like\n        // this (thanks to modular arithmetics):\n        //\n        // clock_offset = (clock_t) (end_clock > start_clock ?\n        //                           end_clock - start_clock :\n        //                           end_clock + (0xffffffffu - start_clock));\n        //\n        // Indeed, let m = 2^32 then\n        // end - start = end + m - start (mod m).\n\n        clock_offset = (clock_t)(end_clock - start_clock);\n    }\n}\n\n// A pointer to the allocated device buffer is returned in dPtr so the caller\n// can compare virtual addresses. The kernel node is added to increase the\n// graph's runtime.\nvoid createSimpleAllocFreeGraph(cudaGraphExec_t *graphExec, float **dPtr, size_t bytes, int device)\n{\n    cudaGraph_t            graph;\n    cudaGraphNode_t        allocNodeA, freeNodeA, blockDeviceNode;\n    cudaMemAllocNodeParams allocParams;\n    cudaKernelNodeParams   blockDeviceNodeParams = {0};\n    int                    numElements           = bytes / sizeof(float);\n    float                  kernelTime            = 5; // time for each thread to run in microseconds\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    *dPtr = (float *)allocParams.dptr;\n\n    int clockRate;\n    checkCudaErrors(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, device));\n    clock_t time_clocks = (clock_t)((kernelTime / 1000.0) * clockRate);\n\n    void *blockDeviceArgs[1] = {(void *)&time_clocks};\n\n    size_t numBlocks                     = numElements / (size_t)THREADS_PER_BLOCK;\n    blockDeviceNodeParams.gridDim        = dim3(numBlocks, 1, 1);\n    blockDeviceNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    blockDeviceNodeParams.sharedMemBytes = 0;\n    blockDeviceNodeParams.extra          = NULL;\n    blockDeviceNodeParams.func           = (void *)clockBlock;\n    blockDeviceNodeParams.kernelParams   = (void **)blockDeviceArgs;\n    checkCudaErrors(cudaGraphAddKernelNode(&blockDeviceNode, graph, &allocNodeA, 1, &blockDeviceNodeParams));\n\n    checkCudaErrors(cudaGraphAddMemFreeNode(&freeNodeA, graph, &blockDeviceNode, 1, (void *)*dPtr));\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\nvoid physicalMemoryReuseSingleStream(size_t bytes, int device)\n{\n    cudaStream_t    stream;\n    cudaGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n    bool            virtualAddrDiffer = true;\n\n    printf(\"================================\\n\");\n    printf(\"Running physical memory reuse example.\\n\");\n    printf(\"CUDA reuses the same physical memory for allocations from separate \"\n           \"graphs when the allocation lifetimes don't overlap.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n    }\n\n    printf(\"Creating the graph execs does not reserve any physical memory.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaGraphLaunch(graphExecs[0], stream));\n    printf(\"\\nThe first graph launched reserves the memory it needs.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(cudaGraphLaunch(graphExecs[0], stream));\n    printf(\"A subsequent launch of the same graph in the same stream reuses the \"\n           \"same physical memory. \");\n    printf(\"Thus the memory footprint does not grow here.\\n\");\n    printMemoryFootprint(device);\n\n    printf(\"\\nSubsequent launches of other graphs in the same stream also reuse the \"\n           \"physical memory. \");\n    printf(\"Thus the memory footprint does not grow here.\\n\");\n    for (int i = 1; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaGraphLaunch(graphExecs[i], stream));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        for (int j = i + 1; j < NUM_GRAPHS; j++) {\n            if (dPtrs[i] == dPtrs[j]) {\n                virtualAddrDiffer = false;\n                printf(\"Error: Graph exec %d and %d have the same virtual address!\\n\", i - 1, i);\n            }\n        }\n        checkCudaErrors(cudaGraphExecDestroy(graphExecs[i]));\n    }\n    if (virtualAddrDiffer) {\n        printf(\"\\nCheck confirms all graphs use a different virtual address.\\n\");\n    }\n    else {\n        printf(\"\\nAll graphs do NOT use different virtual addresses. Exiting test.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(cudaStreamDestroy(stream));\n}\n\nvoid simultaneousStreams(size_t bytes, int device)\n{\n    cudaStream_t    streams[NUM_GRAPHS];\n    cudaGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n\n    printf(\"================================\\n\");\n    printf(\"Running simultaneous streams example.\\n\");\n    printf(\"Graphs that can run concurrently need separate physical memory. \");\n    printf(\"In this example, each graph launched in a separate stream increases the \"\n           \"total memory footprint.\\n\\n\");\n\n    printf(\"When launching a new graph, CUDA may reuse physical memory from a graph \"\n           \"whose execution has already \");\n    printf(\"finished -- even if the new graph is being launched in a different \"\n           \"stream from the completed graph. \");\n    printf(\"Therefore, a kernel node is added to the graphs to increase \"\n           \"runtime.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n        checkCudaErrors(cudaStreamCreateWithFlags(&streams[i], cudaStreamNonBlocking));\n    }\n\n    printf(\"Initial footprint:\\n\");\n    printMemoryFootprint(device);\n\n    printf(\"\\nEach graph launch in a seperate stream grows the memory footprint:\\n\");\n    for (int i = 1; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaGraphLaunch(graphExecs[i], streams[i]));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaStreamSynchronize(streams[i]));\n        checkCudaErrors(cudaGraphExecDestroy(graphExecs[i]));\n        checkCudaErrors(cudaStreamDestroy(streams[i]));\n    }\n}\n\nvoid createSimpleAllocNoFreeGraph(cudaGraphExec_t *graphExec, float **dPtr, size_t bytes, int device)\n{\n    cudaGraph_t            graph;\n    cudaGraphNode_t        allocNodeA;\n    cudaMemAllocNodeParams allocParams;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    *dPtr = (float *)allocParams.dptr;\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\nvoid unfreedAllocations(size_t bytes, int device)\n{\n    cudaStream_t    stream;\n    cudaGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n\n    printf(\"================================\\n\");\n    printf(\"Running unfreed streams example.\\n\");\n    printf(\"CUDA cannot reuse phyiscal memory from graphs which do not free their \"\n           \"allocations.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocNoFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n    }\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    printf(\"Despite being launched in the same stream, each graph launch grows the \"\n           \"memory footprint. \");\n    printf(\"Since the allocation is not freed, CUDA keeps the memory valid for \"\n           \"use.\\n\");\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaGraphLaunch(graphExecs[i], stream));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    checkCudaErrors(cudaDeviceGraphMemTrim(device));\n    printf(\"\\nTrimming does not impact the memory footprint since the un-freed \"\n           \"allocations are still holding onto the memory.\\n\");\n    printMemoryFootprint(device);\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaFree(dPtrs[i]));\n    }\n    printf(\"\\nFreeing the allocations does not shrink the footprint.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(cudaDeviceGraphMemTrim(device));\n    printf(\"\\nSince the allocations are now freed, trimming does reduce the \"\n           \"footprint even when the graph execs are not yet destroyed.\\n\");\n    printMemoryFootprint(device);\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(cudaGraphExecDestroy(graphExecs[i]));\n    }\n    checkCudaErrors(cudaStreamDestroy(stream));\n}\n\nvoid cleanupMemory(int device)\n{\n    checkCudaErrors(cudaDeviceGraphMemTrim(device));\n    printf(\"\\nCleaning up example by trimming device memory.\\n\");\n    printMemoryFootprint(device);\n    printf(\"\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    size_t bytes  = 64 * 1024 * 1024;\n    int    device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion             = 0;\n    int deviceSupportsMemoryPools = 0;\n\n    cudaDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 11040) {\n        printf(\"Waiving execution as driver does not support Graph Memory Nodes\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    cudaDeviceGetAttribute(&deviceSupportsMemoryPools, cudaDevAttrMemoryPoolsSupported, device);\n    if (!deviceSupportsMemoryPools) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else {\n        printf(\"Running sample.\\n\");\n    }\n\n    virtualAddressReuseSingleGraph(bytes, device);\n    cleanupMemory(device);\n\n    physicalMemoryReuseSingleStream(bytes, device);\n    cleanupMemory(device);\n\n    simultaneousStreams(bytes, device);\n    cleanupMemory(device);\n\n    unfreedAllocations(bytes, device);\n    cleanupMemory(device);\n\n    printf(\"================================\\n\");\n    printf(\"Sample complete.\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#define NUM_GRAPHS        8\n#define THREADS_PER_BLOCK 512\n\nvoid printMemoryFootprint(int device)\n{\n    size_t footprint;\n    checkCudaErrors(hipDeviceGetGraphMemAttribute(device, (hipGraphMemAttributeType)0, &footprint));\n    printf(\"    FOOTPRINT: %lu bytes\\n\", footprint);\n}\n\nvoid prepareAllocParams(hipMemAllocNodeParams *allocParams, size_t bytes, int device)\n{\n    memset(allocParams, 0, sizeof(*allocParams));\n\n    allocParams->bytesize                = bytes;\n    allocParams->poolProps.allocType     = hipMemAllocationTypePinned;\n    allocParams->poolProps.location.id   = device;\n    allocParams->poolProps.location.type = hipMemLocationTypeDevice;\n}\n\nvoid createVirtAddrReuseGraph(hipGraphExec_t *graphExec, size_t bytes, int device)\n{\n    hipGraph_t            graph;\n    hipGraphNode_t        allocNodeA, allocNodeB, freeNodeA, freeNodeB;\n    hipMemAllocNodeParams allocParams;\n    float                 *d_a, *d_b;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    d_a = (float *)allocParams.dptr;\n    checkCudaErrors(hipGraphAddMemFreeNode(&freeNodeA, graph, &allocNodeA, 1, (void *)d_a));\n\n    // The dependency between the allocation of d_b and the free of d_a allows d_b\n    // to reuse the same VA.\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeB, graph, &freeNodeA, 1, &allocParams));\n    d_b = (float *)allocParams.dptr;\n\n    if (d_a == d_b) {\n        printf(\"Check confirms that d_a and d_b share a virtual address.\\n\");\n    }\n    else {\n        printf(\"Check shows that d_a and d_b DO NOT share a virtual address.\\n\");\n    }\n\n    checkCudaErrors(hipGraphAddMemFreeNode(&freeNodeB, graph, &allocNodeB, 1, (void *)d_b));\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\nvoid virtualAddressReuseSingleGraph(size_t bytes, int device)\n{\n    hipStream_t    stream;\n    hipGraphExec_t graphExec;\n\n    printf(\"================================\\n\");\n    printf(\"Running virtual address reuse example.\\n\");\n    printf(\"Sequential allocations & frees within a single graph enable CUDA to \"\n           \"reuse virtual addresses.\\n\\n\");\n\n    createVirtAddrReuseGraph(&graphExec, bytes, device);\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printMemoryFootprint(device);\n\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipStreamDestroy(stream));\n}\n\n// This is a kernel that does no real work but runs at least for a specified\n// number of clocks\n__global__ void clockBlock(clock_t clock_count)\n{\n    unsigned int start_clock = (unsigned int)clock();\n\n    clock_t clock_offset = 0;\n\n    while (clock_offset < clock_count) {\n        unsigned int end_clock = (unsigned int)clock();\n\n        // The code below should work like\n        // this (thanks to modular arithmetics):\n        //\n        // clock_offset = (clock_t) (end_clock > start_clock ?\n        //                           end_clock - start_clock :\n        //                           end_clock + (0xffffffffu - start_clock));\n        //\n        // Indeed, let m = 2^32 then\n        // end - start = end + m - start (mod m).\n\n        clock_offset = (clock_t)(end_clock - start_clock);\n    }\n}\n\n// A pointer to the allocated device buffer is returned in dPtr so the caller\n// can compare virtual addresses. The kernel node is added to increase the\n// graph's runtime.\nvoid createSimpleAllocFreeGraph(hipGraphExec_t *graphExec, float **dPtr, size_t bytes, int device)\n{\n    hipGraph_t            graph;\n    hipGraphNode_t        allocNodeA, freeNodeA, blockDeviceNode;\n    hipMemAllocNodeParams allocParams;\n    hipKernelNodeParams   blockDeviceNodeParams = {0};\n    int                    numElements           = bytes / sizeof(float);\n    float                  kernelTime            = 5; // time for each thread to run in microseconds\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    *dPtr = (float *)allocParams.dptr;\n\n    int clockRate;\n    checkCudaErrors(hipDeviceGetAttribute(&clockRate, hipDeviceAttributeClockRate, device));\n    clock_t time_clocks = (clock_t)((kernelTime / 1000.0) * clockRate);\n\n    void *blockDeviceArgs[1] = {(void *)&time_clocks};\n\n    size_t numBlocks                     = numElements / (size_t)THREADS_PER_BLOCK;\n    blockDeviceNodeParams.gridDim        = dim3(numBlocks, 1, 1);\n    blockDeviceNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    blockDeviceNodeParams.sharedMemBytes = 0;\n    blockDeviceNodeParams.extra          = NULL;\n    blockDeviceNodeParams.func           = (void *)clockBlock;\n    blockDeviceNodeParams.kernelParams   = (void **)blockDeviceArgs;\n    checkCudaErrors(hipGraphAddKernelNode(&blockDeviceNode, graph, &allocNodeA, 1, &blockDeviceNodeParams));\n\n    checkCudaErrors(hipGraphAddMemFreeNode(&freeNodeA, graph, &blockDeviceNode, 1, (void *)*dPtr));\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\nvoid physicalMemoryReuseSingleStream(size_t bytes, int device)\n{\n    hipStream_t    stream;\n    hipGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n    bool            virtualAddrDiffer = true;\n\n    printf(\"================================\\n\");\n    printf(\"Running physical memory reuse example.\\n\");\n    printf(\"CUDA reuses the same physical memory for allocations from separate \"\n           \"graphs when the allocation lifetimes don't overlap.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n    }\n\n    printf(\"Creating the graph execs does not reserve any physical memory.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    checkCudaErrors(hipGraphLaunch(graphExecs[0], stream));\n    printf(\"\\nThe first graph launched reserves the memory it needs.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(hipGraphLaunch(graphExecs[0], stream));\n    printf(\"A subsequent launch of the same graph in the same stream reuses the \"\n           \"same physical memory. \");\n    printf(\"Thus the memory footprint does not grow here.\\n\");\n    printMemoryFootprint(device);\n\n    printf(\"\\nSubsequent launches of other graphs in the same stream also reuse the \"\n           \"physical memory. \");\n    printf(\"Thus the memory footprint does not grow here.\\n\");\n    for (int i = 1; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipGraphLaunch(graphExecs[i], stream));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        for (int j = i + 1; j < NUM_GRAPHS; j++) {\n            if (dPtrs[i] == dPtrs[j]) {\n                virtualAddrDiffer = false;\n                printf(\"Error: Graph exec %d and %d have the same virtual address!\\n\", i - 1, i);\n            }\n        }\n        checkCudaErrors(hipGraphExecDestroy(graphExecs[i]));\n    }\n    if (virtualAddrDiffer) {\n        printf(\"\\nCheck confirms all graphs use a different virtual address.\\n\");\n    }\n    else {\n        printf(\"\\nAll graphs do NOT use different virtual addresses. Exiting test.\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(hipStreamDestroy(stream));\n}\n\nvoid simultaneousStreams(size_t bytes, int device)\n{\n    hipStream_t    streams[NUM_GRAPHS];\n    hipGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n\n    printf(\"================================\\n\");\n    printf(\"Running simultaneous streams example.\\n\");\n    printf(\"Graphs that can run concurrently need separate physical memory. \");\n    printf(\"In this example, each graph launched in a separate stream increases the \"\n           \"total memory footprint.\\n\\n\");\n\n    printf(\"When launching a new graph, CUDA may reuse physical memory from a graph \"\n           \"whose execution has already \");\n    printf(\"finished -- even if the new graph is being launched in a different \"\n           \"stream from the completed graph. \");\n    printf(\"Therefore, a kernel node is added to the graphs to increase \"\n           \"runtime.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n        checkCudaErrors(hipStreamCreateWithFlags(&streams[i], hipStreamNonBlocking));\n    }\n\n    printf(\"Initial footprint:\\n\");\n    printMemoryFootprint(device);\n\n    printf(\"\\nEach graph launch in a seperate stream grows the memory footprint:\\n\");\n    for (int i = 1; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipGraphLaunch(graphExecs[i], streams[i]));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipStreamSynchronize(streams[i]));\n        checkCudaErrors(hipGraphExecDestroy(graphExecs[i]));\n        checkCudaErrors(hipStreamDestroy(streams[i]));\n    }\n}\n\nvoid createSimpleAllocNoFreeGraph(hipGraphExec_t *graphExec, float **dPtr, size_t bytes, int device)\n{\n    hipGraph_t            graph;\n    hipGraphNode_t        allocNodeA;\n    hipMemAllocNodeParams allocParams;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n    prepareAllocParams(&allocParams, bytes, device);\n\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeA, graph, NULL, 0, &allocParams));\n    *dPtr = (float *)allocParams.dptr;\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\nvoid unfreedAllocations(size_t bytes, int device)\n{\n    hipStream_t    stream;\n    hipGraphExec_t graphExecs[NUM_GRAPHS];\n    float          *dPtrs[NUM_GRAPHS];\n\n    printf(\"================================\\n\");\n    printf(\"Running unfreed streams example.\\n\");\n    printf(\"CUDA cannot reuse phyiscal memory from graphs which do not free their \"\n           \"allocations.\\n\\n\");\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        createSimpleAllocNoFreeGraph(&graphExecs[i], &dPtrs[i], bytes, device);\n    }\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    printf(\"Despite being launched in the same stream, each graph launch grows the \"\n           \"memory footprint. \");\n    printf(\"Since the allocation is not freed, CUDA keeps the memory valid for \"\n           \"use.\\n\");\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipGraphLaunch(graphExecs[i], stream));\n        printf(\"%02d: \", i);\n        printMemoryFootprint(device);\n    }\n\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    checkCudaErrors(hipDeviceGraphMemTrim(device));\n    printf(\"\\nTrimming does not impact the memory footprint since the un-freed \"\n           \"allocations are still holding onto the memory.\\n\");\n    printMemoryFootprint(device);\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipFree(dPtrs[i]));\n    }\n    printf(\"\\nFreeing the allocations does not shrink the footprint.\\n\");\n    printMemoryFootprint(device);\n\n    checkCudaErrors(hipDeviceGraphMemTrim(device));\n    printf(\"\\nSince the allocations are now freed, trimming does reduce the \"\n           \"footprint even when the graph execs are not yet destroyed.\\n\");\n    printMemoryFootprint(device);\n\n    for (int i = 0; i < NUM_GRAPHS; i++) {\n        checkCudaErrors(hipGraphExecDestroy(graphExecs[i]));\n    }\n    checkCudaErrors(hipStreamDestroy(stream));\n}\n\nvoid cleanupMemory(int device)\n{\n    checkCudaErrors(hipDeviceGraphMemTrim(device));\n    printf(\"\\nCleaning up example by trimming device memory.\\n\");\n    printMemoryFootprint(device);\n    printf(\"\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    size_t bytes  = 64 * 1024 * 1024;\n    int    device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion             = 0;\n    int deviceSupportsMemoryPools = 0;\n\n    hipDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 11040) {\n        printf(\"Waiving execution as driver does not support Graph Memory Nodes\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    hipDeviceGetAttribute(&deviceSupportsMemoryPools, hipDeviceAttributeMemoryPoolsSupported, device);\n    if (!deviceSupportsMemoryPools) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else {\n        printf(\"Running sample.\\n\");\n    }\n\n    virtualAddressReuseSingleGraph(bytes, device);\n    cleanupMemory(device);\n\n    physicalMemoryReuseSingleStream(bytes, device);\n    cleanupMemory(device);\n\n    simultaneousStreams(bytes, device);\n    cleanupMemory(device);\n\n    unfreedAllocations(bytes, device);\n    cleanupMemory(device);\n\n    printf(\"================================\\n\");\n    printf(\"Sample complete.\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/graphMemoryFootprint/graphMemoryFootprint.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a tf32 (E8M10) GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_gemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 128 x 128 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes eight\n// 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_FLOAT macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda/pipeline>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 8\n\n// GEMM configuration.\n\n#define M_TILES 512\n#define N_TILES 512\n#define K_TILES 512\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that is (M = 16) * (K = 8) * 8 * (CHUNK_K = 8)\n// * sizeof(float) = 32 Kb each.\n// (i.e. two 8x8 arrays of tiles of 16x8 float-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(float))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 8 four-byte \"float\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_FLOAT 8\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        cudaError_t __err = cudaGetLastError();                                               \\\n        if (__err != cudaSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, cudaGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    tf32mma_shmem_gemm_async_copy = 0, // tf32 MMA shmem using kernel with async_copy\n    tf32mma_shmem_gemm            = 1, // tf32 MMA shmem using kernel normal copy (without async_copy).\n    simple_tf32mma_gemm           = 2  // tf32 MMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_tf32gemm_async_copy\", \"compute_tf32gemm\", \"simple_wmma_tf32gemm\"};\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(float *a, float *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (float)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (float)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (float)(rand() % 3);\n    }\n}\n\n__global__ void compute_tf32gemm(const float *A, const float *B, const float *C, float *D, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ float shmem[][CHUNK_K * K + SKEW_FLOAT];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const float *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const float *lane_ptr = (warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t       shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const float *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                    for (int t = 0; t < a[i].num_elements; t++) {\n                        a[i].x[t] = wmma::__float_to_tf32(a[i].x[t]);\n                    }\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t       shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const float *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                            for (int t = 0; t < b[j].num_elements; t++) {\n                                b[j].x[t] = wmma::__float_to_tf32(b[j].x[t]);\n                            }\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_tf32gemm_async_copy(const float *A, const float *B, const float *C, float *D, const float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ float shmem[][CHUNK_K * K + SKEW_FLOAT];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe       = cuda::make_pipeline();\n    const auto                                shape4     = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    constexpr int                             loadStride = 2; // load 4 floats, so left-shift by 2.\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape4,\n                               pipe);\n            pipe.producer_commit();\n        }\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n        pipe.consumer_release();\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const float *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2;\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStride;\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for tf32 MMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + ((warpId / (WARPS_PER_BLOCK / 2)) * shmem_idx_b_off);\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const float *lane_ptr = (warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape4, pipe);\n                pipe.producer_commit();\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t       shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const float *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n\n#pragma unroll\n                    for (int t = 0; t < a[i].num_elements; t++) {\n                        a[i].x[t] = wmma::__float_to_tf32(a[i].x[t]);\n                    }\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t       shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const float *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                            for (int t = 0; t < b[j].num_elements; t++) {\n                                b[j].x[t] = wmma::__float_to_tf32(b[j].x[t]);\n                            }\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((float4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((float4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n// Performs an MxNxK tf32 GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16, 16 and 8 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_tf32gemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void\nsimple_wmma_tf32gemm(float *a, float *b, float *c, float *d, int m_ld, int n_ld, int k_ld, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                               acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                               c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        // int bCol = i;\n        // int bRow = warpN * N;\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n#pragma unroll\n            for (int t = 0; t < a_frag.num_elements; t++) {\n                a_frag.x[t] = wmma::__float_to_tf32(a_frag.x[t]);\n            }\n\n#pragma unroll\n            for (int t = 0; t < b_frag.num_elements; t++) {\n                b_frag.x[t] = wmma::__float_to_tf32(b_frag.x[t]);\n            }\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(float *A,\n                                float *B,\n                                float *C,\n                                float  alpha,\n                                float  beta,\n                                int    numARows,\n                                int    numAColumns,\n                                int    numBRows,\n                                int    numBColumns,\n                                int    numCRows,\n                                int    numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"tf32TensorCoreGemm requires requires SM 8.0 or higher to use Tensor Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    float *A_h = NULL;\n    float *B_h = NULL;\n    float *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (float *)malloc(sizeof(float) * M_GLOBAL * K_GLOBAL);\n    B_h = (float *)malloc(sizeof(float) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    float *A = NULL;\n    float *B = NULL;\n    float *C = NULL;\n    float *D = NULL;\n\n    checkCudaErrors(cudaMalloc((void **)&A, sizeof(float) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&B, sizeof(float) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&C, sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&D, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(cudaMemcpy(A, A_h, sizeof(float) * M_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(B, B_h, sizeof(float) * N_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(float) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_FLOAT) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    cudaEvent_t start, stop;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n\n    // kernel to run - default (tf32mma_shmem_gemm_async_copy == 0)\n    kernels selected_kernel = tf32mma_shmem_gemm_async_copy;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 3) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 2, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_tf32mma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case tf32mma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(cudaFuncSetAttribute(\n                compute_tf32gemm_async_copy, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_tf32gemm_async_copy<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case tf32mma_shmem_gemm:\n            checkCudaErrors(\n                cudaFuncSetAttribute(compute_tf32gemm, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_tf32gemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_tf32gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(cudaFree((void *)A));\n    checkCudaErrors(cudaFree((void *)B));\n    checkCudaErrors(cudaFree((void *)C));\n    checkCudaErrors(cudaFree((void *)D));\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a tf32 (E8M10) GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_gemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 128 x 128 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes eight\n// 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_FLOAT macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <cuda/pipeline>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 8\n\n// GEMM configuration.\n\n#define M_TILES 512\n#define N_TILES 512\n#define K_TILES 512\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that is (M = 16) * (K = 8) * 8 * (CHUNK_K = 8)\n// * sizeof(float) = 32 Kb each.\n// (i.e. two 8x8 arrays of tiles of 16x8 float-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(float))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 8 four-byte \"float\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_FLOAT 8\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        hipError_t __err = hipGetLastError();                                               \\\n        if (__err != hipSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, hipGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    tf32mma_shmem_gemm_async_copy = 0, // tf32 MMA shmem using kernel with async_copy\n    tf32mma_shmem_gemm            = 1, // tf32 MMA shmem using kernel normal copy (without async_copy).\n    simple_tf32mma_gemm           = 2  // tf32 MMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_tf32gemm_async_copy\", \"compute_tf32gemm\", \"simple_wmma_tf32gemm\"};\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(float *a, float *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (float)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (float)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (float)(rand() % 3);\n    }\n}\n\n__global__ void compute_tf32gemm(const float *A, const float *B, const float *C, float *D, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ float shmem[][CHUNK_K * K + SKEW_FLOAT];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const float *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const float *lane_ptr = (warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t       shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const float *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                    for (int t = 0; t < a[i].num_elements; t++) {\n                        a[i].x[t] = wmma::__float_to_tf32(a[i].x[t]);\n                    }\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t       shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const float *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                            for (int t = 0; t < b[j].num_elements; t++) {\n                                b[j].x[t] = wmma::__float_to_tf32(b[j].x[t]);\n                            }\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_tf32gemm_async_copy(const float *A, const float *B, const float *C, float *D, const float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ float shmem[][CHUNK_K * K + SKEW_FLOAT];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr = (float *)&shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                               + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe       = cuda::make_pipeline();\n    const auto                                shape4     = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    constexpr int                             loadStride = 2; // load 4 floats, so left-shift by 2.\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape4,\n                               pipe);\n            pipe.producer_commit();\n        }\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n        pipe.consumer_release();\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const float *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2;\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStride;\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for tf32 MMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + ((warpId / (WARPS_PER_BLOCK / 2)) * shmem_idx_b_off);\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const float *lane_ptr = (warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem);\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape4, pipe);\n                pipe.producer_commit();\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t       shmem_idx_a = (warpId / BLOCK_ROW_WARPS) * M * BLOCK_ROW_WARPS + (i * M);\n                    const float *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n\n#pragma unroll\n                    for (int t = 0; t < a[i].num_elements; t++) {\n                        a[i].x[t] = wmma::__float_to_tf32(a[i].x[t]);\n                    }\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t       shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const float *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_FLOAT);\n#pragma unroll\n                            for (int t = 0; t < b[j].num_elements; t++) {\n                                b[j].x[t] = wmma::__float_to_tf32(b[j].x[t]);\n                            }\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((float4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((float4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n// Performs an MxNxK tf32 GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16, 16 and 8 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_tf32gemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void\nsimple_wmma_tf32gemm(float *a, float *b, float *c, float *d, int m_ld, int n_ld, int k_ld, float alpha, float beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, wmma::precision::tf32, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, wmma::precision::tf32, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                               acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, float>                               c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        // int bCol = i;\n        // int bRow = warpN * N;\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n#pragma unroll\n            for (int t = 0; t < a_frag.num_elements; t++) {\n                a_frag.x[t] = wmma::__float_to_tf32(a_frag.x[t]);\n            }\n\n#pragma unroll\n            for (int t = 0; t < b_frag.num_elements; t++) {\n                b_frag.x[t] = wmma::__float_to_tf32(b_frag.x[t]);\n            }\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(float *A,\n                                float *B,\n                                float *C,\n                                float  alpha,\n                                float  beta,\n                                int    numARows,\n                                int    numAColumns,\n                                int    numBRows,\n                                int    numBColumns,\n                                int    numCRows,\n                                int    numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"tf32TensorCoreGemm requires requires SM 8.0 or higher to use Tensor Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    float *A_h = NULL;\n    float *B_h = NULL;\n    float *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (float *)malloc(sizeof(float) * M_GLOBAL * K_GLOBAL);\n    B_h = (float *)malloc(sizeof(float) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    float *A = NULL;\n    float *B = NULL;\n    float *C = NULL;\n    float *D = NULL;\n\n    checkCudaErrors(hipMalloc((void **)&A, sizeof(float) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&B, sizeof(float) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&C, sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&D, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(hipMemcpy(A, A_h, sizeof(float) * M_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(B, B_h, sizeof(float) * N_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(float) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_FLOAT) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    hipEvent_t start, stop;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n\n    // kernel to run - default (tf32mma_shmem_gemm_async_copy == 0)\n    kernels selected_kernel = tf32mma_shmem_gemm_async_copy;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 3) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 2, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_tf32mma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case tf32mma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_tf32gemm_async_copy), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_tf32gemm_async_copy<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case tf32mma_shmem_gemm:\n            checkCudaErrors(\n                hipFuncSetAttribute(reinterpret_cast<const void*>(compute_tf32gemm), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_tf32gemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_tf32gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n        }\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(hipEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(hipFree((void *)A));\n    checkCudaErrors(hipFree((void *)B));\n    checkCudaErrors(hipFree((void *)C));\n    checkCudaErrors(hipFree((void *)D));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/tf32TensorCoreGemm/tf32TensorCoreGemm.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Simple kernel for ptxjit demonstration.\n *\n */\nextern \"C\" __global__ void myKernel(int *data)\n{\n    int tid   = blockIdx.x * blockDim.x + threadIdx.x;\n    data[tid] = tid;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Simple kernel for ptxjit demonstration.\n *\n */\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void myKernel(int *data)\n{\n    int tid   = blockIdx.x * blockDim.x + threadIdx.x;\n    data[tid] = tid;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/ptxjit/ptxjit_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// This sample uses the compressible memory allocation if device supports it\n// and performs saxpy on it.\n// Compressible memory may give better performance if the data is amenable to\n// compression.\n\n#include <cuda.h>\n#include <stdio.h>\n#define CUDA_DRIVER_API\n#include \"compMalloc.h\"\n#include \"helper_cuda.h\"\n\n__global__ void saxpy(const float a, const float4 *x, const float4 *y, float4 *z, const size_t n)\n{\n    for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += gridDim.x * blockDim.x) {\n        const float4 x4 = x[i];\n        const float4 y4 = y[i];\n        z[i]            = make_float4(a * x4.x + y4.x, a * x4.y + y4.y, a * x4.z + y4.z, a * x4.w + y4.w);\n    }\n}\n\n__global__ void init(float4 *x, float4 *y, const float val, const size_t n)\n{\n    const float4 val4 = make_float4(val, val, val, val);\n    for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += gridDim.x * blockDim.x) {\n        x[i] = y[i] = val4;\n    }\n}\n\nvoid launchSaxpy(const float  a,\n                 float4      *x,\n                 float4      *y,\n                 float4      *z,\n                 const size_t n,\n                 const float  init_val,\n                 const bool   compressibleZbuf)\n{\n    cudaEvent_t start, stop;\n    float       ms;\n    int         blockSize;\n    int         minGridSize;\n    dim3        threads, blocks;\n\n    if (!compressibleZbuf) {\n        // We are on config where compressible buffer can only be initialized through cudaMemcpy\n        // hence, x & y buffers are allocated as compressible and initialized via cudaMemcpy\n        // whereas z buffer is allocated as non-compressible.\n        float4 *h_x = (float4 *)malloc(sizeof(float4) * n);\n        float4 *h_y = (float4 *)malloc(sizeof(float4) * n);\n        for (int i = 0; i < n; i++) {\n            h_x[i].x = h_x[i].y = h_x[i].z = h_x[i].w = init_val;\n            h_y[i].x = h_y[i].y = h_y[i].z = h_y[i].w = init_val;\n        }\n        checkCudaErrors(cudaMemcpy(x, h_x, sizeof(float4) * n, cudaMemcpyHostToDevice));\n        checkCudaErrors(cudaMemcpy(y, h_y, sizeof(float4) * n, cudaMemcpyHostToDevice));\n        free(h_x);\n        free(h_y);\n    }\n    else {\n        checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)init));\n        threads = dim3(blockSize, 1, 1);\n        blocks  = dim3(minGridSize, 1, 1);\n        init<<<blocks, threads>>>(x, y, init_val, n);\n    }\n\n    checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)saxpy));\n    threads = dim3(blockSize, 1, 1);\n    blocks  = dim3(minGridSize, 1, 1);\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n    saxpy<<<blocks, threads>>>(a, x, y, z, n);\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n    checkCudaErrors(cudaEventElapsedTime(&ms, start, stop));\n\n    const size_t size = n * sizeof(float4);\n    printf(\"Running saxpy with %d blocks x %d threads = %.3f ms %.3f TB/s\\n\",\n           blocks.x,\n           threads.x,\n           ms,\n           (size * 3) / ms / 1e9);\n}\n\nint main(int argc, char **argv)\n{\n    const size_t n = 10485760;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        exit(EXIT_SUCCESS);\n    }\n\n    findCudaDevice(argc, (const char **)argv);\n    CUdevice currentDevice;\n    checkCudaErrors(cuCtxGetDevice(&currentDevice));\n\n    // Check that the selected device supports virtual memory management\n    int vmm_supported = -1;\n    checkCudaErrors(\n        cuDeviceGetAttribute(&vmm_supported, CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED, currentDevice));\n    if (vmm_supported == 0) {\n        printf(\"Device %d doesn't support Virtual Memory Management, waiving the execution.\\n\", currentDevice);\n        exit(EXIT_WAIVED);\n    }\n\n    int isCompressionAvailable;\n    checkCudaErrors(cuDeviceGetAttribute(\n        &isCompressionAvailable, CU_DEVICE_ATTRIBUTE_GENERIC_COMPRESSION_SUPPORTED, currentDevice));\n    if (isCompressionAvailable == 0) {\n        printf(\"Device %d doesn't support Generic memory compression, waiving the execution.\\n\", currentDevice);\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"Generic memory compression support is available\\n\");\n\n    int major, minor;\n    checkCudaErrors(cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, currentDevice));\n    checkCudaErrors(cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, currentDevice));\n    float4      *x, *y, *z;\n    const size_t size = n * sizeof(float4);\n\n    // Allocating compressible memory\n    checkCudaErrors(allocateCompressible((void **)&x, size, true));\n    checkCudaErrors(allocateCompressible((void **)&y, size, true));\n    bool compressibleZbuf = 0;\n    if ((major == 8 && minor == 0) || (major == 8 && minor == 6)) {\n        // On SM 8.0 and 8.6 GPUs compressible buffer can only be initialized\n        // through cudaMemcpy.\n        printf(\"allocating non-compressible Z buffer\\n\");\n        checkCudaErrors(allocateCompressible((void **)&z, size, false));\n        compressibleZbuf = 0;\n    }\n    else {\n        checkCudaErrors(allocateCompressible((void **)&z, size, true));\n        compressibleZbuf = 1;\n    }\n\n    printf(\"Running saxpy on %zu bytes of Compressible memory\\n\", size);\n\n    const float a        = 1.0f;\n    const float init_val = 1.0f;\n    launchSaxpy(a, x, y, z, n, init_val, compressibleZbuf);\n\n    checkCudaErrors(freeCompressible(x, size, true));\n    checkCudaErrors(freeCompressible(y, size, true));\n    checkCudaErrors(freeCompressible(z, size, true));\n\n    printf(\"Running saxpy on %zu bytes of Non-Compressible memory\\n\", size);\n    // Allocating non-compressible memory\n    checkCudaErrors(allocateCompressible((void **)&x, size, false));\n    checkCudaErrors(allocateCompressible((void **)&y, size, false));\n    checkCudaErrors(allocateCompressible((void **)&z, size, false));\n\n    launchSaxpy(a, x, y, z, n, init_val, compressibleZbuf);\n\n    checkCudaErrors(freeCompressible(x, size, false));\n    checkCudaErrors(freeCompressible(y, size, false));\n    checkCudaErrors(freeCompressible(z, size, false));\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// This sample uses the compressible memory allocation if device supports it\n// and performs saxpy on it.\n// Compressible memory may give better performance if the data is amenable to\n// compression.\n\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#define CUDA_DRIVER_API\n#include \"compMalloc.h\"\n#include \"hip/hip_runtime_api.h\"\n\n__global__ void saxpy(const float a, const float4 *x, const float4 *y, float4 *z, const size_t n)\n{\n    for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += gridDim.x * blockDim.x) {\n        const float4 x4 = x[i];\n        const float4 y4 = y[i];\n        z[i]            = make_float4(a * x4.x + y4.x, a * x4.y + y4.y, a * x4.z + y4.z, a * x4.w + y4.w);\n    }\n}\n\n__global__ void init(float4 *x, float4 *y, const float val, const size_t n)\n{\n    const float4 val4 = make_float4(val, val, val, val);\n    for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += gridDim.x * blockDim.x) {\n        x[i] = y[i] = val4;\n    }\n}\n\nvoid launchSaxpy(const float  a,\n                 float4      *x,\n                 float4      *y,\n                 float4      *z,\n                 const size_t n,\n                 const float  init_val,\n                 const bool   compressibleZbuf)\n{\n    hipEvent_t start, stop;\n    float       ms;\n    int         blockSize;\n    int         minGridSize;\n    dim3        threads, blocks;\n\n    if (!compressibleZbuf) {\n        // We are on config where compressible buffer can only be initialized through cudaMemcpy\n        // hence, x & y buffers are allocated as compressible and initialized via cudaMemcpy\n        // whereas z buffer is allocated as non-compressible.\n        float4 *h_x = (float4 *)malloc(sizeof(float4) * n);\n        float4 *h_y = (float4 *)malloc(sizeof(float4) * n);\n        for (int i = 0; i < n; i++) {\n            h_x[i].x = h_x[i].y = h_x[i].z = h_x[i].w = init_val;\n            h_y[i].x = h_y[i].y = h_y[i].z = h_y[i].w = init_val;\n        }\n        checkCudaErrors(hipMemcpy(x, h_x, sizeof(float4) * n, hipMemcpyHostToDevice));\n        checkCudaErrors(hipMemcpy(y, h_y, sizeof(float4) * n, hipMemcpyHostToDevice));\n        free(h_x);\n        free(h_y);\n    }\n    else {\n        checkCudaErrors(hipOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)init));\n        threads = dim3(blockSize, 1, 1);\n        blocks  = dim3(minGridSize, 1, 1);\n        init<<<blocks, threads>>>(x, y, init_val, n);\n    }\n\n    checkCudaErrors(hipOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, (void *)saxpy));\n    threads = dim3(blockSize, 1, 1);\n    blocks  = dim3(minGridSize, 1, 1);\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n    saxpy<<<blocks, threads>>>(a, x, y, z, n);\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n    checkCudaErrors(hipEventElapsedTime(&ms, start, stop));\n\n    const size_t size = n * sizeof(float4);\n    printf(\"Running saxpy with %d blocks x %d threads = %.3f ms %.3f TB/s\\n\",\n           blocks.x,\n           threads.x,\n           ms,\n           (size * 3) / ms / 1e9);\n}\n\nint main(int argc, char **argv)\n{\n    const size_t n = 10485760;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        exit(EXIT_SUCCESS);\n    }\n\n    findCudaDevice(argc, (const char **)argv);\n    hipDevice_t currentDevice;\n    checkCudaErrors(hipCtxGetDevice(&currentDevice));\n\n    // Check that the selected device supports virtual memory management\n    int vmm_supported = -1;\n    checkCudaErrors(\n        hipDeviceGetAttribute(&vmm_supported, CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED, currentDevice));\n    if (vmm_supported == 0) {\n        printf(\"Device %d doesn't support Virtual Memory Management, waiving the execution.\\n\", currentDevice);\n        exit(EXIT_WAIVED);\n    }\n\n    int isCompressionAvailable;\n    checkCudaErrors(hipDeviceGetAttribute(\n        &isCompressionAvailable, CU_DEVICE_ATTRIBUTE_GENERIC_COMPRESSION_SUPPORTED, currentDevice));\n    if (isCompressionAvailable == 0) {\n        printf(\"Device %d doesn't support Generic memory compression, waiving the execution.\\n\", currentDevice);\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"Generic memory compression support is available\\n\");\n\n    int major, minor;\n    checkCudaErrors(hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, currentDevice));\n    checkCudaErrors(hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, currentDevice));\n    float4      *x, *y, *z;\n    const size_t size = n * sizeof(float4);\n\n    // Allocating compressible memory\n    checkCudaErrors(allocateCompressible((void **)&x, size, true));\n    checkCudaErrors(allocateCompressible((void **)&y, size, true));\n    bool compressibleZbuf = 0;\n    if ((major == 8 && minor == 0) || (major == 8 && minor == 6)) {\n        // On SM 8.0 and 8.6 GPUs compressible buffer can only be initialized\n        // through cudaMemcpy.\n        printf(\"allocating non-compressible Z buffer\\n\");\n        checkCudaErrors(allocateCompressible((void **)&z, size, false));\n        compressibleZbuf = 0;\n    }\n    else {\n        checkCudaErrors(allocateCompressible((void **)&z, size, true));\n        compressibleZbuf = 1;\n    }\n\n    printf(\"Running saxpy on %zu bytes of Compressible memory\\n\", size);\n\n    const float a        = 1.0f;\n    const float init_val = 1.0f;\n    launchSaxpy(a, x, y, z, n, init_val, compressibleZbuf);\n\n    checkCudaErrors(freeCompressible(x, size, true));\n    checkCudaErrors(freeCompressible(y, size, true));\n    checkCudaErrors(freeCompressible(z, size, true));\n\n    printf(\"Running saxpy on %zu bytes of Non-Compressible memory\\n\", size);\n    // Allocating non-compressible memory\n    checkCudaErrors(allocateCompressible((void **)&x, size, false));\n    checkCudaErrors(allocateCompressible((void **)&y, size, false));\n    checkCudaErrors(allocateCompressible((void **)&z, size, false));\n\n    launchSaxpy(a, x, y, z, n, init_val, compressibleZbuf);\n\n    checkCudaErrors(freeCompressible(x, size, false));\n    checkCudaErrors(freeCompressible(y, size, false));\n    checkCudaErrors(freeCompressible(z, size, false));\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cudaCompressibleMemory/saxpy.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 9.\n\n// In this program, the compute_gemm kernel computes the result of a matrix\n// multiplication and addition: D = alpha * A * B + beta * C. The dimensions of\n// both C and D matrices are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x\n// K_GLOBAL (row-major), the B matrix is K_GLOBAL x N_GLOBAL (column-major). In\n// that kernel, each CTA computes one 128 x 128 tile of the resulting matrix per\n// iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes\n// eight 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array. Warps\n// compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and\n// accumulating the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments\n//   from shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B\n//   matrices from global memory to shared memory. After that, all warps in the\n//   CTA reuse the A and B data from shared memory, thus reducing the number of\n//   data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an\n//   additional padding (skew) to reduce the number of shared memory access bank\n//   conflicts.\n//   (See a detailed explanation near the SKEW_HALF macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each\n//   warp stores its subtiles to shared memory. The CTA then copies the shared\n//   memory contents to global memory, again avoiding redundant random global\n//   memory  accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register\n//   utilization, but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <cuda.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 1\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n#define WMMA_M 16\n#define WMMA_N 16\n#define WMMA_K 16\n\n// GEMM configuration.\n\n#define M_TILES 256\n#define N_TILES 256\n#define K_TILES 256\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that are 16 * 16 * 8 * 8 * 2 = 32 Kb each\n// (i.e. two 8x8 arrays of tiles of 16x16 half-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the\n// performance would be severely impacted. So we choose to reduce the chunk size\n// in half, i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(half))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 16 two-byte \"half\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_HALF 16\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        cudaError_t __err = cudaGetLastError();                                               \\\n        if (__err != cudaSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, cudaGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(half *a, half *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (half)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (half)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = static_cast<float>(rand() % 3);\n    }\n}\n\n__global__ void compute_gemm(const half *A, const half *B, const float *C, float *D, float alpha, float beta)\n{\n    extern __shared__ half shmem[][CHUNK_K * K + SKEW_HALF];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr =\n        (float *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * K * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and\n    // from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * K;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may\n    // result in a loss of precision). Zero still needs to be specially handled\n    // though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the\n    // matrix to the right and down, and selects the next tile to compute. Once\n    // there's no such tile, all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_ROW_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared\n        // memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            typedef int4 copy_t;\n\n            *((copy_t *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((copy_t *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment\n        // multiplications along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const half *warp_ptr = (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % 4) * 2)\n                                            : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % 4) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy\n            // the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            int4 *lane_ptr = (int4 *)(warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL)\n                           + (laneId % CHUNK_COPY_LINE_LANES);\n\n            // Shift the second half of the warp to the next row / column in the\n            // shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) = *lane_ptr;\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = (int4 *)((half *)lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP);\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, half, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, half, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t      shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const half *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_HALF);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be\n                            // reused against the other A matrix fragments.\n                            size_t      shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const half *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_HALF);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL\n                // threads in the warp are well-defined even though element indices\n                // within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global\n        // memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n}\n\n// Performs an MxNxK GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16.\n//  3) Neither A nor B are transposed.\n// Note: This is a less performant version of the compute_gemm kernel. It is\n// designed for\n//       demonstration purposes only to show the CUDA WMMA API use without\n//       relying on availability of the shared memory.\n__global__ void\nsimple_wmma_gemm(half *a, half *b, float *c, float *d, int m_ld, int n_ld, int k_ld, float alpha, float beta)\n{\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float>              acc_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float>              c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += WMMA_K) {\n        int aCol = i;\n        int aRow = warpM * WMMA_M;\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result\n    // scaled by alpha\n    int cCol = warpN * WMMA_N;\n    int cRow = warpM * WMMA_M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n}\n\n__host__ void matMultiplyOnHost(half  *A,\n                                half  *B,\n                                float *C,\n                                float  alpha,\n                                float  beta,\n                                int    numARows,\n                                int    numAColumns,\n                                int    numBRows,\n                                int    numBColumns,\n                                int    numCRows,\n                                int    numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += (float)A[i * numAColumns + k] * (float)B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM7X) architecture or higher.\n    if (deviceProp.major < 7) {\n        printf(\"cudaTensorCoreGemm requires SM 7.0 or higher to use Tensor \"\n               \"Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    half  *A_h = NULL;\n    half  *B_h = NULL;\n    float *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (half *)malloc(sizeof(half) * M_GLOBAL * K_GLOBAL);\n    B_h = (half *)malloc(sizeof(half) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    half  *A = NULL;\n    half  *B = NULL;\n    float *C = NULL;\n    float *D = NULL;\n\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&A), sizeof(half) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&B), sizeof(half) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&C), sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&D), sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(cudaMemcpy(A, A_h, sizeof(half) * M_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(B, B_h, sizeof(half) * N_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache\n        // per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the\n        // maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(half) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_HALF) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    cudaEvent_t start, stop;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if (deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) {\n        printf(\"Computing... using high performance kernel compute_gemm \\n\");\n\n        checkCudaErrors(cudaFuncSetAttribute(compute_gemm, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n        checkKernelErrors(\n            (compute_gemm<<<deviceProp.multiProcessorCount, THREADS_PER_BLOCK, SHMEM_SZ>>>(A, B, C, D, alpha, beta)));\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (WMMA_M * blockDim.x / 32 - 1)) / (WMMA_M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + WMMA_N * blockDim.y - 1) / (WMMA_N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f)\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\",\n           static_cast<double>((static_cast<double>(M_GLOBAL) * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.))\n               / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(A)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(B)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(C)));\n    checkCudaErrors(cudaFree(reinterpret_cast<void *>(D)));\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// CUDA sample demonstrating a GEMM computation using the Warp Matrix Multiply\n// and Accumulate API introduced in CUDA 9.\n\n// In this program, the compute_gemm kernel computes the result of a matrix\n// multiplication and addition: D = alpha * A * B + beta * C. The dimensions of\n// both C and D matrices are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x\n// K_GLOBAL (row-major), the B matrix is K_GLOBAL x N_GLOBAL (column-major). In\n// that kernel, each CTA computes one 128 x 128 tile of the resulting matrix per\n// iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 128 x 128 tile to compute.\n// Each CTA consists of eight warps. For the 128 x 128 tile, each warp computes\n// eight 16 x 16 subtiles, organized in a 2 x 4 two-dimensional array. Warps\n// compute the 16 x 16 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and\n// accumulating the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 128 x 128 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments\n//   from shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B\n//   matrices from global memory to shared memory. After that, all warps in the\n//   CTA reuse the A and B data from shared memory, thus reducing the number of\n//   data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an\n//   additional padding (skew) to reduce the number of shared memory access bank\n//   conflicts.\n//   (See a detailed explanation near the SKEW_HALF macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each\n//   warp stores its subtiles to shared memory. The CTA then copies the shared\n//   memory contents to global memory, again avoiding redundant random global\n//   memory  accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register\n//   utilization, but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 1\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 16\n#define N 16\n#define K 16\n\n#define WMMA_M 16\n#define WMMA_N 16\n#define WMMA_K 16\n\n// GEMM configuration.\n\n#define M_TILES 256\n#define N_TILES 256\n#define K_TILES 256\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit two 8-tile chunks of\n// the A and B matrix data, that are 16 * 16 * 8 * 8 * 2 = 32 Kb each\n// (i.e. two 8x8 arrays of tiles of 16x16 half-typed elements per CTA).\n// But we cannot account the 8 Kb total skew overhead, without which the\n// performance would be severely impacted. So we choose to reduce the chunk size\n// in half, i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 4\n#else\n#define CHUNK_K 8\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(half))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 16 two-byte \"half\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_HALF 16\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        hipError_t __err = hipGetLastError();                                               \\\n        if (__err != hipSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, hipGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nusing namespace nvcuda;\n\n__host__ void init_host_matrices(half *a, half *b, float *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (half)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (half)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = static_cast<float>(rand() % 3);\n    }\n}\n\n__global__ void compute_gemm(const half *A, const half *B, const float *C, float *D, float alpha, float beta)\n{\n    extern __shared__ half shmem[][CHUNK_K * K + SKEW_HALF];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    float *shmem_warp_tile_ptr =\n        (float *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * K * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and\n    // from shared memory.\n    float *shmem_warp_stream_ptr = (float *)&shmem[0][0] + warpId * SHMEM_STRIDE * K;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may\n    // result in a loss of precision). Zero still needs to be specially handled\n    // though.\n    beta /= alpha;\n\n    // Each CTA slides along the 128 x 128 tiles from the top left corner of the\n    // matrix to the right and down, and selects the next tile to compute. Once\n    // there's no such tile, all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_ROW_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared\n        // memory.\n        const size_t gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const float *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            typedef int4 copy_t;\n\n            *((copy_t *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((copy_t *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment\n        // multiplications along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, float> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const half *warp_ptr = (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % 4) * 2)\n                                            : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % 4) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy\n            // the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            int4 *lane_ptr = (int4 *)(warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL)\n                           + (laneId % CHUNK_COPY_LINE_LANES);\n\n            // Shift the second half of the warp to the next row / column in the\n            // shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP) * 2; i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) = *lane_ptr;\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = (int4 *)((half *)lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP);\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, half, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, half, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t      shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const half *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_HALF);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be\n                            // reused against the other A matrix fragments.\n                            size_t      shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const half *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_HALF);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                // Uniform, point-wise transformations of ALL fragment elements by ALL\n                // threads in the warp are well-defined even though element indices\n                // within fragment storage are not defined.\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                float *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * K + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global\n        // memory.\n        float *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < K; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n}\n\n// Performs an MxNxK GEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 16.\n//  3) Neither A nor B are transposed.\n// Note: This is a less performant version of the compute_gemm kernel. It is\n// designed for\n//       demonstration purposes only to show the CUDA WMMA API use without\n//       relying on availability of the shared memory.\n__global__ void\nsimple_wmma_gemm(half *a, half *b, float *c, float *d, int m_ld, int n_ld, int k_ld, float alpha, float beta)\n{\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float>              acc_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float>              c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += WMMA_K) {\n        int aCol = i;\n        int aRow = warpM * WMMA_M;\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result\n    // scaled by alpha\n    int cCol = warpN * WMMA_N;\n    int cRow = warpM * WMMA_M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n}\n\n__host__ void matMultiplyOnHost(half  *A,\n                                half  *B,\n                                float *C,\n                                float  alpha,\n                                float  beta,\n                                int    numARows,\n                                int    numAColumns,\n                                int    numBRows,\n                                int    numBColumns,\n                                int    numCRows,\n                                int    numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            float temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                temp += (float)A[i * numAColumns + k] * (float)B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n\n    // Tensor cores require a GPU of Volta (SM7X) architecture or higher.\n    if (deviceProp.major < 7) {\n        printf(\"cudaTensorCoreGemm requires SM 7.0 or higher to use Tensor \"\n               \"Cores.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    half  *A_h = NULL;\n    half  *B_h = NULL;\n    float *C_h = NULL;\n#if CPU_DEBUG\n    float *result_hD   = NULL;\n    float *result_host = NULL;\n#endif\n\n    A_h = (half *)malloc(sizeof(half) * M_GLOBAL * K_GLOBAL);\n    B_h = (half *)malloc(sizeof(half) * K_GLOBAL * N_GLOBAL);\n    C_h = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n    result_host = (float *)malloc(sizeof(float) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    half  *A = NULL;\n    half  *B = NULL;\n    float *C = NULL;\n    float *D = NULL;\n\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&A), sizeof(half) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&B), sizeof(half) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&C), sizeof(float) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&D), sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(hipMemcpy(A, A_h, sizeof(half) * M_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(B, B_h, sizeof(half) * N_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(C, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(D, 0, sizeof(float) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache\n        // per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the\n        // maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(half) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_HALF) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(float))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const float alpha = 1.1f;\n    const float beta  = 1.2f;\n\n    hipEvent_t start, stop;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if (deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) {\n        printf(\"Computing... using high performance kernel compute_gemm \\n\");\n\n        checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_gemm), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n        checkKernelErrors(\n            (compute_gemm<<<deviceProp.multiProcessorCount, THREADS_PER_BLOCK, SHMEM_SZ>>>(A, B, C, D, alpha, beta)));\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (WMMA_M * blockDim.x / 32 - 1)) / (WMMA_M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + WMMA_N * blockDim.y - 1) / (WMMA_N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(float) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(float) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f)\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n    }\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(hipEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"TFLOPS: %.2f\\n\",\n           static_cast<double>((static_cast<double>(M_GLOBAL) * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.))\n               / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(A)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(B)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(C)));\n    checkCudaErrors(hipFree(reinterpret_cast<void *>(D)));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cudaTensorCoreGemm/cudaTensorCoreGemm.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// CUDA sample demonstrating a Double precision GEMM computation using the Warp\n//  Matrix Multiply and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_dgemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 64 x 64 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 64 x 64 tile to compute.\n// Each CTA consists of eight warps. For the 64 x 64 tile, each warp computes eight\n// 8 x 8 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 8 x 8 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 64 x 64 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_DOUBLE macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <cooperative_groups/memcpy_async.h>\n#include <cuda.h>\n#include <cuda/barrier>\n#include <cuda/pipeline>\n#include <cuda/std/type_traits>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 8\n#define N 8\n#define K 4\n\n// GEMM configuration.\n\n#define M_TILES 1024\n#define N_TILES 1024\n#define K_TILES 1024\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit 8x16-tile chunks of each\n// the A and B matrix data, that are (M = 8) * (K = 4) * 8 * (CHUNK_K = 16) * sizeof(double) = 32 Kb each\n// But we cannot account the 4 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 8\n#else\n#define CHUNK_K 16\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(double))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 4 eight-byte \"double\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_DOUBLE 4\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        cudaError_t __err = cudaGetLastError();                                               \\\n        if (__err != cudaSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, cudaGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    dmma_shmem_gemm_async_copy    = 0, // DMMA shmem using kernel with async_copy\n    dmma_shmem_gemm_cg_async_copy = 1, // DMMA shmem using kernel with cooperative groups async_copy\n    dmma_shmem_gemm               = 2, // DMMA shmem using kernel normal copy (without async_copy).\n    simple_dmma_gemm              = 3  // DMMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_dgemm_async_copy\",\n                             \"compute_dgemm_cg_async_copy\",\n                             \"compute_dgemm\",\n                             \"simple_wmma_gemm\"};\n\nusing namespace nvcuda;\nnamespace cg = cooperative_groups;\n\n__host__ void init_host_matrices(double *a, double *b, double *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (double)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (double)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (double)(rand() % 3);\n    }\n}\n\n__global__ void compute_dgemm(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr = (double *)&shmem[0][0]\n                                + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                                + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = (double *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const double *lane_ptr = warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP); i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_dgemm_async_copy(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr = &shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                                + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = &shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe = cuda::make_pipeline();\n\n    const auto    shape2     = cuda::aligned_size_t<alignof(double2)>(sizeof(double2));\n    constexpr int loadStride = 1; // load 2 double, left-shift by 1.\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape2,\n                               pipe);\n\n            pipe.producer_commit();\n        }\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        pipe.consumer_release();\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP);\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStride;\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for DMMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + (shmem_idx_b_off * (warpId / (WARPS_PER_BLOCK / 2)));\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const double *lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape2, pipe);\n\n                pipe.producer_commit();\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_dgemm_cg_async_copy(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n    auto                     cta    = cg::this_thread_block();\n    auto                     tile32 = cg::tiled_partition<32>(cta);\n\n    constexpr int tileChunkCopySize = WARP_SIZE / CHUNK_COPY_LINES_PER_WARP;\n    auto          tileChunkCopy     = cg::tiled_partition<tileChunkCopySize>(cta);\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr =\n        (double *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * N * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = (double *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            auto dst_ptr = &shmem_warp_stream_ptr[(SHMEM_STRIDE * i)];\n            auto src_ptr = &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i)];\n            cg::memcpy_async(\n                tile32, dst_ptr, src_ptr, cuda::aligned_size_t<alignof(double2)>{tile32.size() * sizeof(double2)});\n        }\n\n        cg::wait(cta);\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        cg::wait(cta);\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                         : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP);\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for DMMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + (shmem_idx_b_off * (warpId / (WARPS_PER_BLOCK / 2)));\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            auto lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                auto dst_ptr = &shmem[shmem_idx][0];\n                auto src_ptr = lane_ptr;\n\n                cg::memcpy_async(tileChunkCopy,\n                                 dst_ptr,\n                                 src_ptr,\n                                 cuda::aligned_size_t<alignof(double2)>{tileChunkCopySize * sizeof(double2)});\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n            cg::wait(cta);\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            cg::sync(cta);\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        cg::sync(cta);\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n        cg::sync(cta);\n    }\n#endif\n}\n\n// Performs an MxNxK DGEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 8, 8 and 4 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_dgemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void\nsimple_wmma_gemm(double *a, double *b, double *c, double *d, int m_ld, int n_ld, int k_ld, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, double>               acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, double>               c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(double *A,\n                                double *B,\n                                double *C,\n                                float   alpha,\n                                float   beta,\n                                int     numARows,\n                                int     numAColumns,\n                                int     numBRows,\n                                int     numBColumns,\n                                int     numCRows,\n                                int     numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            double temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                // B matrix is column major. A matrix is row major.\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n\n    // Double precision Tensor cores require a GPU of Ampere (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"dmmaTensorCoreGemm requires SM 8.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    double *A_h = NULL;\n    double *B_h = NULL;\n    double *C_h = NULL;\n#if CPU_DEBUG\n    double *result_hD   = NULL;\n    double *result_host = NULL;\n#endif\n\n    A_h = (double *)malloc(sizeof(double) * M_GLOBAL * K_GLOBAL);\n    B_h = (double *)malloc(sizeof(double) * K_GLOBAL * N_GLOBAL);\n    C_h = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n    result_host = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    double *A = NULL;\n    double *B = NULL;\n    double *C = NULL;\n    double *D = NULL;\n\n    checkCudaErrors(cudaMalloc((void **)&A, sizeof(double) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&B, sizeof(double) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&C, sizeof(double) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(cudaMalloc((void **)&D, sizeof(double) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(cudaMemcpy(A, A_h, sizeof(double) * M_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(B, B_h, sizeof(double) * N_GLOBAL * K_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(C, C_h, sizeof(double) * M_GLOBAL * N_GLOBAL, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(D, 0, sizeof(double) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(double) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_DOUBLE) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(double))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const double alpha = 1.1f;\n    const double beta  = 1.2f;\n\n    cudaEvent_t start, stop;\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    checkCudaErrors(cudaEventRecord(start));\n\n    kernels selected_kernel = dmma_shmem_gemm_async_copy;\n\n    // kernel to run - default (dmma_shmem_gemm_async_copy == 0)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 4) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 3, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_dmma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case dmma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(\n                cudaFuncSetAttribute(compute_dgemm_async_copy, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_dgemm_async_copy<<<deviceProp.multiProcessorCount * 3, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case dmma_shmem_gemm_cg_async_copy:\n            checkCudaErrors(cudaFuncSetAttribute(\n                compute_dgemm_cg_async_copy, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_dgemm_cg_async_copy<<<deviceProp.multiProcessorCount * 3, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case dmma_shmem_gemm:\n            checkCudaErrors(cudaFuncSetAttribute(compute_dgemm, cudaFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_dgemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(double) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(cudaMemcpy(result_hD, D, sizeof(double) * M_GLOBAL * N_GLOBAL, cudaMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(cudaEventRecord(stop));\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(double) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    size_t number_of_matches = 0;\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n            break;\n        }\n        else {\n            number_of_matches++;\n        }\n    }\n    printf(\"number_of_matches = %zu out of = %d \\n\", number_of_matches, N_GLOBAL * M_GLOBAL);\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"FP64 TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(cudaFree((void *)A));\n    checkCudaErrors(cudaFree((void *)B));\n    checkCudaErrors(cudaFree((void *)C));\n    checkCudaErrors(cudaFree((void *)D));\n\n    return 0;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n// CUDA sample demonstrating a Double precision GEMM computation using the Warp\n//  Matrix Multiply and Accumulate API introduced in CUDA 11.0.\n\n// In this program, the compute_dgemm kernel computes the result of a matrix multiplication\n// and addition: D = alpha * A * B + beta * C. The dimensions of both C and D matrices\n// are M_GLOBAL x N_GLOBAL. The A matrix is M_GLOBAL x K_GLOBAL (row-major), the B matrix\n// is K_GLOBAL x N_GLOBAL (column-major).\n// In that kernel, each CTA computes one 64 x 64 tile of the resulting matrix\n// per iteration. When the tile is computed, the CTA stores it to the global memory\n// and begins a new iteration, selecting a new 64 x 64 tile to compute.\n// Each CTA consists of eight warps. For the 64 x 64 tile, each warp computes eight\n// 8 x 8 subtiles, organized in a 2 x 4 two-dimensional array.\n// Warps compute the 8 x 8 subtiles using nvcuda::wmma::mma_sync operations by\n// moving through the K_GLOBAL dimension of the A and B matrices and accumulating\n// the intermediate result in the local thread state.\n\n// There are a number of simple optimizations used in the algorithm:\n// - The CTA copies the 64 x 64 tile of the C matrix from the global memory to\n//   shared memory. After that is done, each warp loads the C matrix fragments from\n//   shared memory, thus avoiding a random global memory access.\n// - On each internal iteration, the CTA copies a portion of the A and B matrices from\n//   global memory to shared memory. After that, all warps in the CTA reuse the A and B\n//   data from shared memory, thus reducing the number of data copies from global memory.\n// - The portions of the A and B matrices are stored in shared memory with an additional\n//   padding (skew) to reduce the number of shared memory access bank conflicts.\n//   (See a detailed explanation near the SKEW_DOUBLE macro definition.)\n// - When the CTA finishes computing the tiles of the resulting matrix, each warp stores\n//   its subtiles to shared memory. The CTA then copies the shared memory contents to\n//   global memory, again avoiding redundant random global memory accesses.\n// - Note that the CTA tile size is chosen to maximize the GPU register utilization,\n//   but carefully enough to avoid local memory use.\n\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/memcpy_async.h>\n#include <hip/hip_runtime.h>\n#include <cuda/barrier>\n#include <cuda/pipeline>\n#include <cuda/std/type_traits>\n#include <mma.h>\n#include <stdio.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Externally configurable parameters.\n\n#ifndef CPU_DEBUG\n// Set this to 1 to verify the correctness of the GPU-computed matrix.\n#define CPU_DEBUG 0\n#endif\n\n#ifndef SHARED_MEMORY_LIMIT_64K\n// Set this to 0 to use more than 64 Kb of shared memory to cache data, to\n// improve the performance of the computations on GPU.\n// Note that you need a GPU that can have more than 64 Kb of shared memory\n// per multiprocessor.\n#define SHARED_MEMORY_LIMIT_64K 0\n#endif\n\n// GPU configuration.\n\n#define WARP_SIZE 32\n\n// MMA matrix tile dimensions.\n\n#define M 8\n#define N 8\n#define K 4\n\n// GEMM configuration.\n\n#define M_TILES 1024\n#define N_TILES 1024\n#define K_TILES 1024\n\n#define M_GLOBAL (M * M_TILES)\n#define N_GLOBAL (N * N_TILES)\n#define K_GLOBAL (K * K_TILES)\n\n#define C_LAYOUT wmma::mem_row_major\n\n// Implementation constants.\n\n#define WARPS_PER_BLOCK   8\n#define THREADS_PER_BLOCK (WARP_SIZE * WARPS_PER_BLOCK)\n\n#if SHARED_MEMORY_LIMIT_64K\n// With only 64 Kb shared memory available, we can fit 8x16-tile chunks of each\n// the A and B matrix data, that are (M = 8) * (K = 4) * 8 * (CHUNK_K = 16) * sizeof(double) = 32 Kb each\n// But we cannot account the 4 Kb total skew overhead, without which the performance\n// would be severely impacted. So we choose to reduce the chunk size in half,\n// i.e. the amount of A and B matrix data we cache in shared memory.\n// Accordingly, this doubles the number of outer iterations across the global K\n// dimension, which only slightly impacts the performance.\n#define CHUNK_K 8\n#else\n#define CHUNK_K 16\n#endif\n\n#define CHUNK_LINE_BYTES          (CHUNK_K * K * sizeof(double))\n#define WARP_COPY_BYTES           (WARP_SIZE * sizeof(int4))\n#define CHUNK_COPY_LINES_PER_WARP (WARP_COPY_BYTES / CHUNK_LINE_BYTES)\n#define CHUNK_COPY_LINE_LANES     (WARP_SIZE / CHUNK_COPY_LINES_PER_WARP)\n\n#define BLOCK_ROW_WARPS 2\n#define BLOCK_COL_WARPS 4\n\n#define WARP_ROW_TILES 4\n#define WARP_COL_TILES 2\n\n#define BLOCK_ROW_TILES (WARP_ROW_TILES * BLOCK_ROW_WARPS)\n#define BLOCK_COL_TILES (WARP_COL_TILES * BLOCK_COL_WARPS)\n\n#define GLOBAL_MEM_STRIDE N_GLOBAL\n\n#define SHMEM_STRIDE (N * BLOCK_ROW_TILES)\n#define SHMEM_OFFSET (N * WARP_ROW_TILES)\n\n// The macro below is used to shift rows of the A matrix and columns of the B matrix\n// in shared memory to minimize possible bank conflicts.\n// Before performing the nvcuda::wmma::mma_sync operation, the warp must load the matrix\n// data using the nvcuda::wmma::load_matrix_sync operation. Although the memory access pattern\n// is not specified for that function, each lane in the warp can read one or multiple matrix\n// elements from different matrix rows or columns.\n// For shared memory, such access can result in bank conflicts if different rows / columns\n// of the matrix map to the same bank. By shifting each row and column by a few bytes, we\n// make sure that they map to different banks, thus reducing the number of possible bank\n// conflicts.\n// The number of 4 eight-byte \"double\" elements is chosen as the minimum possible shift because\n// we must keep each row and column 256-bit aligned, as required by nvcuda::wmma::load_matrix_sync.\n#define SKEW_DOUBLE 4\n\n#define checkKernelErrors(expr)                                                               \\\n    do {                                                                                      \\\n        expr;                                                                                 \\\n                                                                                              \\\n        hipError_t __err = hipGetLastError();                                               \\\n        if (__err != hipSuccess) {                                                           \\\n            printf(\"Line %d: '%s' failed: %s\\n\", __LINE__, #expr, hipGetErrorString(__err)); \\\n            abort();                                                                          \\\n        }                                                                                     \\\n    } while (0)\n\nenum kernels {\n    dmma_shmem_gemm_async_copy    = 0, // DMMA shmem using kernel with async_copy\n    dmma_shmem_gemm_cg_async_copy = 1, // DMMA shmem using kernel with cooperative groups async_copy\n    dmma_shmem_gemm               = 2, // DMMA shmem using kernel normal copy (without async_copy).\n    simple_dmma_gemm              = 3  // DMMA non-shmem using simple kernel.\n};\n\nconst char *kernelNames[] = {\"compute_dgemm_async_copy\",\n                             \"compute_dgemm_cg_async_copy\",\n                             \"compute_dgemm\",\n                             \"simple_wmma_gemm\"};\n\nusing namespace nvcuda;\nnamespace cg = cooperative_groups;\n\n__host__ void init_host_matrices(double *a, double *b, double *c)\n{\n    for (int i = 0; i < M_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            a[i * K_GLOBAL + j] = (double)(rand() % 3);\n        }\n    }\n\n    for (int i = 0; i < N_GLOBAL; i++) {\n        for (int j = 0; j < K_GLOBAL; j++) {\n            b[i * K_GLOBAL + j] = (double)(rand() % 3);\n        }\n    }\n\n    for (int t = 0; t < M_GLOBAL * N_GLOBAL; t++) {\n        c[t] = (double)(rand() % 3);\n    }\n}\n\n__global__ void compute_dgemm(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    const size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr = (double *)&shmem[0][0]\n                                + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                                + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = (double *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId) =\n                *((int4 *)(src_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            size_t shmem_idx = warpId < (WARPS_PER_BLOCK / 2)\n                                 ? (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                                 : (N * (warpId % (WARPS_PER_BLOCK / 2)) * 2 + shmem_idx_b_off);\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const double *lane_ptr = warp_ptr + tile_k * K + (laneId / CHUNK_COPY_LINE_LANES) * K_GLOBAL;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += laneId / CHUNK_COPY_LINE_LANES;\n\n#pragma unroll\n            for (int i = 0; i < ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP); i++) {\n                // Copy 16 bytes at once in each lane.\n                *((int4 *)&shmem[shmem_idx][0] + (laneId % CHUNK_COPY_LINE_LANES)) =\n                    *((int4 *)lane_ptr + (laneId % CHUNK_COPY_LINE_LANES));\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_dgemm_async_copy(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr = &shmem[0][0] + (warpId / BLOCK_ROW_WARPS) * SHMEM_STRIDE * N * BLOCK_ROW_WARPS\n                                + (warpId % BLOCK_ROW_WARPS) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = &shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe = cuda::make_pipeline();\n\n    const auto    shape2     = cuda::aligned_size_t<alignof(double2)>(sizeof(double2));\n    constexpr int loadStride = 1; // load 2 double, left-shift by 1.\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            pipe.producer_acquire();\n            cuda::memcpy_async(&shmem_warp_stream_ptr[(SHMEM_STRIDE * i) + (laneId << loadStride)],\n                               &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i) + (laneId << loadStride)],\n                               shape2,\n                               pipe);\n\n            pipe.producer_commit();\n        }\n        // Now wait for all the above issued 8 batches to complete.\n        cuda::pipeline_consumer_wait_prior<0>(pipe);\n        __syncthreads();\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n                // Scale the C matrix.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        pipe.consumer_release();\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        __syncthreads();\n\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < (WARPS_PER_BLOCK / 2))\n                ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP);\n        const int     laneLoadElem      = (laneId % CHUNK_COPY_LINE_LANES) << loadStride;\n\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for DMMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + (shmem_idx_b_off * (warpId / (WARPS_PER_BLOCK / 2)));\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            const double *lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL + laneLoadElem;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                pipe.producer_acquire();\n\n                cuda::memcpy_async(&shmem[shmem_idx][laneLoadElem], lane_ptr, shape2, pipe);\n\n                pipe.producer_commit();\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n\n            cuda::pipeline_consumer_wait_prior<0>(pipe);\n            __syncthreads();\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            pipe.consumer_release();\n            __syncthreads();\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        __syncthreads();\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n\n        __syncthreads();\n    }\n#endif\n}\n\n__global__ void\ncompute_dgemm_cg_async_copy(const double *A, const double *B, const double *C, double *D, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    extern __shared__ double shmem[][CHUNK_K * K + SKEW_DOUBLE];\n    auto                     cta    = cg::this_thread_block();\n    auto                     tile32 = cg::tiled_partition<32>(cta);\n\n    constexpr int tileChunkCopySize = WARP_SIZE / CHUNK_COPY_LINES_PER_WARP;\n    auto          tileChunkCopy     = cg::tiled_partition<tileChunkCopySize>(cta);\n\n    // Warp and lane identification.\n    const unsigned int warpId = threadIdx.x / WARP_SIZE;\n    const unsigned int laneId = threadIdx.x % WARP_SIZE;\n\n    // Offset in shared memory from which the B matrix is stored.\n    constexpr size_t shmem_idx_b_off = BLOCK_COL_TILES * M;\n\n    // This pointer is used to access the C and D matrix tiles this warp computes.\n    double *shmem_warp_tile_ptr =\n        (double *)&shmem[0][0] + (warpId / 2) * SHMEM_STRIDE * N * 2 + (warpId % 2) * SHMEM_OFFSET;\n\n    // This pointer is used to stream the C and D matrices block-wide tile to and from shared memory.\n    double *shmem_warp_stream_ptr = (double *)&shmem[0][0] + warpId * SHMEM_STRIDE * N;\n\n    // Adjust the beta scaler, as it'll be multiplied by alpha at the end of\n    // each tile computation. Technically this is not generally correct (may result\n    // in a loss of precision). Zero still needs to be specially handled though.\n    beta /= alpha;\n\n    // Each CTA slides along the 64 x 64 tiles from the top left corner of the matrix to the\n    // right and down, and selects the next tile to compute. Once there's no such tile,\n    // all warps in this CTA exit.\n    for (unsigned int block_pos = blockIdx.x;; block_pos += gridDim.x) {\n        const unsigned int block_tile_i = ((block_pos * BLOCK_ROW_TILES) / N_TILES) * (BLOCK_COL_TILES);\n        const unsigned int block_tile_j = (block_pos * BLOCK_COL_TILES) % N_TILES;\n\n        // Stop when there are no more D matrix tiles to compute in this CTA.\n        if (block_tile_i >= M_TILES) {\n            break;\n        }\n\n        // This warp's pointer to the C matrix data to copy memory from to shared memory.\n        const size_t  gmem_idx                 = (block_tile_i + warpId) * M * GLOBAL_MEM_STRIDE + block_tile_j * N;\n        const double *src_gmem_warp_stream_ptr = &C[gmem_idx];\n\n        // Stream multiple C tiles to shared memory.\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            auto dst_ptr = &shmem_warp_stream_ptr[(SHMEM_STRIDE * i)];\n            auto src_ptr = &src_gmem_warp_stream_ptr[(GLOBAL_MEM_STRIDE * i)];\n            cg::memcpy_async(\n                tile32, dst_ptr, src_ptr, cuda::aligned_size_t<alignof(double2)>{tile32.size() * sizeof(double2)});\n        }\n\n        cg::wait(cta);\n\n        // These fragments will accumulate the result of A and B matrix fragment multiplications\n        // along the K_GLOBAL dimension.\n        wmma::fragment<wmma::accumulator, M, N, K, double> c[WARP_COL_TILES][WARP_ROW_TILES];\n\n        // Load the C matrix tiles into fragments from shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                const double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n                wmma::load_matrix_sync(c[i][j], tile_ptr, SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        // Scale the C matrix.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++) {\n                    c[i][j].x[t] *= beta;\n                }\n            }\n        }\n\n        // sync here so that shared memory can then be used for loading A & B matrices.\n        cg::wait(cta);\n        // Select what warp copies what matrix to shared memory.\n        // Warps 0-3 copy the A matrix, warps 4-7 copy the B matrix.\n        const double *warp_ptr =\n            (warpId < 4) ? (&A[block_tile_i * M * K_GLOBAL] + M * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2)\n                         : (&B[block_tile_j * N * K_GLOBAL] + N * K_GLOBAL * (warpId % (WARPS_PER_BLOCK / 2)) * 2);\n\n        const int     stridePerLaneCopy = (laneId / CHUNK_COPY_LINE_LANES);\n        constexpr int chunksPerLane     = ((WARP_SIZE / 2) / CHUNK_COPY_LINES_PER_WARP);\n        // Go through the global K dimension by a fixed step at a time.\n#pragma unroll\n        for (int tile_k = 0; tile_k < K_TILES; tile_k += CHUNK_K) {\n            // Copy slices of the A and B matrices to shared memory.\n            // The first half of the warps in the CTA copy the A matrix, the rest copy the B matrix.\n            // As for DMMA  M == N we use M for warp 4-7 + shmem_idx_b_off.\n            size_t shmem_idx =\n                (M * (warpId % (WARPS_PER_BLOCK / 2)) * 2) + (shmem_idx_b_off * (warpId / (WARPS_PER_BLOCK / 2)));\n\n            // First half of the warp copies the first row / column of the matrix,\n            // the second half of the warp copies the next.\n            auto lane_ptr = warp_ptr + tile_k * K + stridePerLaneCopy * K_GLOBAL;\n\n            // Shift the second half of the warp to the next row / column in the shared memory.\n            shmem_idx += stridePerLaneCopy;\n\n#pragma unroll\n            for (int i = 0; i < chunksPerLane; i++) {\n                // Copy 16 bytes at once in each lane.\n                auto dst_ptr = &shmem[shmem_idx][0];\n                auto src_ptr = lane_ptr;\n\n                cg::memcpy_async(tileChunkCopy,\n                                 dst_ptr,\n                                 src_ptr,\n                                 cuda::aligned_size_t<alignof(double2)>{tileChunkCopySize * sizeof(double2)});\n\n                // Advance the global memory pointer and the shared memory index.\n                lane_ptr = lane_ptr + K_GLOBAL * CHUNK_COPY_LINES_PER_WARP;\n                shmem_idx += CHUNK_COPY_LINES_PER_WARP;\n            }\n            cg::wait(cta);\n\n            // Compute a grid of C matrix tiles in each warp.\n#pragma unroll\n            for (int k_step = 0; k_step < CHUNK_K; k_step++) {\n                wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a[WARP_COL_TILES];\n                wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b[WARP_ROW_TILES];\n\n#pragma unroll\n                for (int i = 0; i < WARP_COL_TILES; i++) {\n                    size_t        shmem_idx_a = (warpId / 2) * M * 2 + (i * M);\n                    const double *tile_ptr    = &shmem[shmem_idx_a][k_step * K];\n\n                    wmma::load_matrix_sync(a[i], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n\n#pragma unroll\n                    for (int j = 0; j < WARP_ROW_TILES; j++) {\n                        if (i == 0) {\n                            // Load the B matrix fragment once, because it is going to be reused\n                            // against the other A matrix fragments.\n                            size_t        shmem_idx_b = shmem_idx_b_off + (WARP_ROW_TILES * N) * (warpId % 2) + (j * N);\n                            const double *tile_ptr    = &shmem[shmem_idx_b][k_step * K];\n\n                            wmma::load_matrix_sync(b[j], tile_ptr, K * CHUNK_K + SKEW_DOUBLE);\n                        }\n\n                        wmma::mma_sync(c[i][j], a[i], b[j], c[i][j]);\n                    }\n                }\n            }\n            cg::sync(cta);\n        }\n\n        // Store the D fragments to shared memory.\n#pragma unroll\n        for (int i = 0; i < WARP_COL_TILES; i++) {\n#pragma unroll\n            for (int j = 0; j < WARP_ROW_TILES; j++) {\n                // Uniform, point-wise transformations of ALL fragment elements by ALL threads in the\n                // warp are well-defined even though element indices within fragment storage are not defined.\n#pragma unroll\n                for (int t = 0; t < c[i][j].num_elements; t++)\n                    c[i][j].x[t] *= alpha;\n\n                double *tile_ptr = shmem_warp_tile_ptr + i * SHMEM_STRIDE * N + j * N;\n\n                wmma::store_matrix_sync(tile_ptr, c[i][j], SHMEM_STRIDE, C_LAYOUT);\n            }\n        }\n\n        cg::sync(cta);\n\n        // Now that shared memory contains all the D tiles, stream them to global memory.\n        double *dst_gmem_warp_stream_ptr = &D[gmem_idx];\n\n#pragma unroll\n        for (int i = 0; i < N; i++) {\n            *((int4 *)(dst_gmem_warp_stream_ptr + GLOBAL_MEM_STRIDE * i) + laneId) =\n                *((int4 *)(shmem_warp_stream_ptr + SHMEM_STRIDE * i) + laneId);\n        }\n        cg::sync(cta);\n    }\n#endif\n}\n\n// Performs an MxNxK DGEMM (C=alpha*A*B + beta*C) assuming:\n//  1) Matrices are packed in memory.\n//  2) M, N and K are multiples of 8, 8 and 4 respectively.\n//  3) A is row major, B is column major matrix.\n// Note: This is a less performant version of the compute_dgemm kernel. It is designed for\n//       demonstration purposes only to show the CUDA WMMA API use without relying on\n//       availability of the shared memory.\n__global__ void\nsimple_wmma_gemm(double *a, double *b, double *c, double *d, int m_ld, int n_ld, int k_ld, double alpha, double beta)\n{\n#if __CUDA_ARCH__ >= 800\n    // Leading dimensions. Packed with no transpositions.\n    int lda = k_ld;\n    int ldb = k_ld;\n    int ldc = n_ld;\n\n    // Tile using a 2D grid\n    int warpM = (blockIdx.x * blockDim.x + threadIdx.x) / warpSize;\n    int warpN = (blockIdx.y * blockDim.y + threadIdx.y);\n\n    // Declare the fragments\n    wmma::fragment<wmma::matrix_a, M, N, K, double, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N, K, double, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, double>               acc_frag;\n    wmma::fragment<wmma::accumulator, M, N, K, double>               c_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    // Loop over k\n    for (int i = 0; i < k_ld; i += K) {\n        int aCol = i;\n        int aRow = warpM * M;\n\n        int bCol = warpN * N;\n        int bRow = i;\n\n        // Bounds checking\n        if (aRow < m_ld && aCol < k_ld && bRow < k_ld && bCol < n_ld) {\n            // Load the inputs\n            wmma::load_matrix_sync(a_frag, a + aCol + aRow * lda, lda);\n            wmma::load_matrix_sync(b_frag, b + bRow + bCol * ldb, ldb);\n\n            // Perform the matrix multiplication\n            wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n        }\n    }\n\n    // Load in the current value of c, scale it by beta, and add this our result scaled by alpha\n    int cCol = warpN * N;\n    int cRow = warpM * M;\n\n    if (cRow < m_ld && cCol < n_ld) {\n        wmma::load_matrix_sync(c_frag, c + cCol + cRow * ldc, ldc, wmma::mem_row_major);\n\n        for (int i = 0; i < c_frag.num_elements; i++) {\n            c_frag.x[i] = alpha * acc_frag.x[i] + beta * c_frag.x[i];\n        }\n\n        // Store the output\n        wmma::store_matrix_sync(d + cCol + cRow * ldc, c_frag, ldc, wmma::mem_row_major);\n    }\n#endif\n}\n\n__host__ void matMultiplyOnHost(double *A,\n                                double *B,\n                                double *C,\n                                float   alpha,\n                                float   beta,\n                                int     numARows,\n                                int     numAColumns,\n                                int     numBRows,\n                                int     numBColumns,\n                                int     numCRows,\n                                int     numCColumns)\n{\n    for (int i = 0; i < numCRows; i++) {\n        for (int j = 0; j < numCColumns; j++) {\n            double temp = 0.0;\n\n            for (int k = 0; k < numAColumns; k++) {\n                // B matrix is column major. A matrix is row major.\n                temp += A[i * numAColumns + k] * B[j * numBRows + k];\n            }\n\n            C[i * numCColumns + j] = temp * alpha + beta * C[i * numCColumns + j];\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"Initializing...\\n\");\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n\n    // Double precision Tensor cores require a GPU of Ampere (SM8X) architecture or higher.\n    if (deviceProp.major < 8) {\n        printf(\"dmmaTensorCoreGemm requires SM 8.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"M: %d (%d x %d)\\n\", M_GLOBAL, M, M_TILES);\n    printf(\"N: %d (%d x %d)\\n\", N_GLOBAL, N, N_TILES);\n    printf(\"K: %d (%d x %d)\\n\", K_GLOBAL, K, K_TILES);\n\n    double *A_h = NULL;\n    double *B_h = NULL;\n    double *C_h = NULL;\n#if CPU_DEBUG\n    double *result_hD   = NULL;\n    double *result_host = NULL;\n#endif\n\n    A_h = (double *)malloc(sizeof(double) * M_GLOBAL * K_GLOBAL);\n    B_h = (double *)malloc(sizeof(double) * K_GLOBAL * N_GLOBAL);\n    C_h = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n#if CPU_DEBUG\n    result_hD   = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n    result_host = (double *)malloc(sizeof(double) * M_GLOBAL * N_GLOBAL);\n#endif\n\n    double *A = NULL;\n    double *B = NULL;\n    double *C = NULL;\n    double *D = NULL;\n\n    checkCudaErrors(hipMalloc((void **)&A, sizeof(double) * M_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&B, sizeof(double) * N_GLOBAL * K_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&C, sizeof(double) * M_GLOBAL * N_GLOBAL));\n    checkCudaErrors(hipMalloc((void **)&D, sizeof(double) * M_GLOBAL * N_GLOBAL));\n\n    assert(((unsigned long long)A) % 128 == 0);\n    assert(((unsigned long long)B) % 128 == 0);\n    assert(((unsigned long long)C) % 128 == 0);\n    assert(((unsigned long long)D) % 128 == 0);\n\n    init_host_matrices(A_h, B_h, C_h);\n\n    printf(\"Preparing data for GPU...\\n\");\n\n    checkCudaErrors(hipMemcpy(A, A_h, sizeof(double) * M_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(B, B_h, sizeof(double) * N_GLOBAL * K_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(C, C_h, sizeof(double) * M_GLOBAL * N_GLOBAL, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(D, 0, sizeof(double) * M_GLOBAL * N_GLOBAL));\n\n    enum {\n        // Compute the right amount of shared memory to request.\n        // We need shared memory to hold per-CTA C and D matrix tiles, and to cache per-CTA chunks\n        // of the A and B matrices. Therefore, the right amount to request is the maximum of those\n        // two numbers.\n        SHMEM_SZ = MAX(sizeof(double) * (BLOCK_COL_TILES * M) * (CHUNK_K * K + SKEW_DOUBLE) * 2,\n                       M * (BLOCK_ROW_WARPS * WARP_ROW_TILES) * N * (BLOCK_COL_WARPS * WARP_COL_TILES) * sizeof(double))\n    };\n\n    printf(\"Required shared memory size: %lu Kb\\n\", SHMEM_SZ / 1024UL);\n\n    const double alpha = 1.1f;\n    const double beta  = 1.2f;\n\n    hipEvent_t start, stop;\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    checkCudaErrors(hipEventRecord(start));\n\n    kernels selected_kernel = dmma_shmem_gemm_async_copy;\n\n    // kernel to run - default (dmma_shmem_gemm_async_copy == 0)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 4) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 3, you have entered %d\\n\", kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // If enough shared memory available on the GPU use high performant kernel\n    if ((deviceProp.sharedMemPerMultiprocessor >= SHMEM_SZ) && (selected_kernel != simple_dmma_gemm)) {\n        printf(\"Computing using high performance kernel = %d - %s\\n\", selected_kernel, kernelNames[selected_kernel]);\n\n        switch (selected_kernel) {\n        case dmma_shmem_gemm_async_copy:\n        default:\n            checkCudaErrors(\n                hipFuncSetAttribute(reinterpret_cast<const void*>(compute_dgemm_async_copy), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_dgemm_async_copy<<<deviceProp.multiProcessorCount * 3, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case dmma_shmem_gemm_cg_async_copy:\n            checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_dgemm_cg_async_copy), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors(\n                (compute_dgemm_cg_async_copy<<<deviceProp.multiProcessorCount * 3, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                    A, B, C, D, alpha, beta)));\n            break;\n        case dmma_shmem_gemm:\n            checkCudaErrors(hipFuncSetAttribute(reinterpret_cast<const void*>(compute_dgemm), hipFuncAttributeMaxDynamicSharedMemorySize, SHMEM_SZ));\n            checkKernelErrors((compute_dgemm<<<deviceProp.multiProcessorCount * 2, THREADS_PER_BLOCK, SHMEM_SZ>>>(\n                A, B, C, D, alpha, beta)));\n            break;\n        }\n\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(double) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n    else {\n        dim3 gridDim;\n        dim3 blockDim;\n\n        // blockDim.x must be a multple of warpSize\n        // 128x4 means we have 16 warps and a block computes a 64x64 output tile\n        blockDim.x = 128;\n        blockDim.y = 4;\n\n        gridDim.x = (M_GLOBAL + (M * blockDim.x / 32 - 1)) / (M * blockDim.x / 32);\n        gridDim.y = (N_GLOBAL + N * blockDim.y - 1) / (N * blockDim.y);\n\n        printf(\"Computing... using simple_wmma_gemm kernel\\n\");\n        simple_wmma_gemm<<<gridDim, blockDim>>>(A, B, C, D, M_GLOBAL, N_GLOBAL, K_GLOBAL, alpha, beta);\n#if CPU_DEBUG\n        checkCudaErrors(hipMemcpy(result_hD, D, sizeof(double) * M_GLOBAL * N_GLOBAL, hipMemcpyDeviceToHost));\n#endif\n    }\n\n    checkCudaErrors(hipEventRecord(stop));\n    checkCudaErrors(hipEventSynchronize(stop));\n\n#if CPU_DEBUG\n    printf(\"Verifying correctness of the computations...\\n\");\n\n    memcpy(result_host, C_h, sizeof(double) * M_GLOBAL * N_GLOBAL);\n\n    matMultiplyOnHost(A_h, B_h, result_host, alpha, beta, M_GLOBAL, K_GLOBAL, K_GLOBAL, N_GLOBAL, M_GLOBAL, N_GLOBAL);\n\n    size_t number_of_matches = 0;\n    for (int i = 0; i < N_GLOBAL * M_GLOBAL; i++) {\n        if (fabs(result_hD[i] - result_host[i]) > 0.1f) {\n            printf(\"mismatch i=%d result_hD=%f result_host=%f\\n\", i, result_hD[i], result_host[i]);\n            break;\n        }\n        else {\n            number_of_matches++;\n        }\n    }\n    printf(\"number_of_matches = %zu out of = %d \\n\", number_of_matches, N_GLOBAL * M_GLOBAL);\n    free(result_hD);\n    free(result_host);\n#endif\n\n    float milliseconds = 0;\n\n    checkCudaErrors(hipEventElapsedTime(&milliseconds, start, stop));\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"FP64 TFLOPS: %.2f\\n\", (((double)M_GLOBAL * N_GLOBAL * K_GLOBAL * 2) / (milliseconds / 1000.)) / 1e12);\n\n    free(A_h);\n    free(B_h);\n    free(C_h);\n    checkCudaErrors(hipFree((void *)A));\n    checkCudaErrors(hipFree((void *)B));\n    checkCudaErrors(hipFree((void *)C));\n    checkCudaErrors(hipFree((void *)D));\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/dmmaTensorCoreGemm/dmmaTensorCoreGemm.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cstdio>\n#include <helper_cuda.h>\n#include <helper_string.h>\n#include <iostream>\n\n#define MAX_DEPTH      16\n#define INSERTION_SORT 32\n\n////////////////////////////////////////////////////////////////////////////////\n// Selection sort used when depth gets too big or the number of elements drops\n// below a threshold.\n////////////////////////////////////////////////////////////////////////////////\n__device__ void selection_sort(unsigned int *data, int left, int right)\n{\n    for (int i = left; i <= right; ++i) {\n        unsigned min_val = data[i];\n        int      min_idx = i;\n\n        // Find the smallest value in the range [left, right].\n        for (int j = i + 1; j <= right; ++j) {\n            unsigned val_j = data[j];\n\n            if (val_j < min_val) {\n                min_idx = j;\n                min_val = val_j;\n            }\n        }\n\n        // Swap the values.\n        if (i != min_idx) {\n            data[min_idx] = data[i];\n            data[i]       = min_val;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Very basic quicksort algorithm, recursively launching the next level.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void cdp_simple_quicksort(unsigned int *data, int left, int right, int depth)\n{\n    // If we're too deep or there are few elements left, we use an insertion\n    // sort...\n    if (depth >= MAX_DEPTH || right - left <= INSERTION_SORT) {\n        selection_sort(data, left, right);\n        return;\n    }\n\n    unsigned int *lptr  = data + left;\n    unsigned int *rptr  = data + right;\n    unsigned int  pivot = data[(left + right) / 2];\n\n    // Do the partitioning.\n    while (lptr <= rptr) {\n        // Find the next left- and right-hand values to swap\n        unsigned int lval = *lptr;\n        unsigned int rval = *rptr;\n\n        // Move the left pointer as long as the pointed element is smaller than the\n        // pivot.\n        while (lval < pivot) {\n            lptr++;\n            lval = *lptr;\n        }\n\n        // Move the right pointer as long as the pointed element is larger than the\n        // pivot.\n        while (rval > pivot) {\n            rptr--;\n            rval = *rptr;\n        }\n\n        // If the swap points are valid, do the swap!\n        if (lptr <= rptr) {\n            *lptr++ = rval;\n            *rptr-- = lval;\n        }\n    }\n\n    // Now the recursive part\n    int nright = rptr - data;\n    int nleft  = lptr - data;\n\n    // Launch a new block to sort the left part.\n    if (left < (rptr - data)) {\n        cudaStream_t s;\n        cudaStreamCreateWithFlags(&s, cudaStreamNonBlocking);\n        cdp_simple_quicksort<<<1, 1, 0, s>>>(data, left, nright, depth + 1);\n        cudaStreamDestroy(s);\n    }\n\n    // Launch a new block to sort the right part.\n    if ((lptr - data) < right) {\n        cudaStream_t s1;\n        cudaStreamCreateWithFlags(&s1, cudaStreamNonBlocking);\n        cdp_simple_quicksort<<<1, 1, 0, s1>>>(data, nleft, right, depth + 1);\n        cudaStreamDestroy(s1);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Call the quicksort kernel from the host.\n////////////////////////////////////////////////////////////////////////////////\nvoid run_qsort(unsigned int *data, unsigned int nitems)\n{\n    // Prepare CDP for the max depth 'MAX_DEPTH'.\n\n    // Launch on device\n    int left  = 0;\n    int right = nitems - 1;\n    std::cout << \"Launching kernel on the GPU\" << std::endl;\n    cdp_simple_quicksort<<<1, 1>>>(data, left, right, 0);\n    checkCudaErrors(cudaDeviceSynchronize());\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Initialize data on the host.\n////////////////////////////////////////////////////////////////////////////////\nvoid initialize_data(unsigned int *dst, unsigned int nitems)\n{\n    // Fixed seed for illustration\n    srand(2047);\n\n    // Fill dst with random values\n    for (unsigned i = 0; i < nitems; i++)\n        dst[i] = rand() % nitems;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Verify the results.\n////////////////////////////////////////////////////////////////////////////////\nvoid check_results(int n, unsigned int *results_d)\n{\n    unsigned int *results_h = new unsigned[n];\n    checkCudaErrors(cudaMemcpy(results_h, results_d, n * sizeof(unsigned), cudaMemcpyDeviceToHost));\n\n    for (int i = 1; i < n; ++i)\n        if (results_h[i - 1] > results_h[i]) {\n            std::cout << \"Invalid item[\" << i - 1 << \"]: \" << results_h[i - 1] << \" greater than \" << results_h[i]\n                      << std::endl;\n            exit(EXIT_FAILURE);\n        }\n\n    std::cout << \"OK\" << std::endl;\n    delete[] results_h;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    int  num_items = 128;\n    bool verbose   = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        std::cerr << \"Usage: \" << argv[0]\n                  << \" num_items=<num_items>\\twhere num_items is the number of \"\n                     \"items to sort\"\n                  << std::endl;\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"v\")) {\n        verbose = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"num_items\")) {\n        num_items = getCmdLineArgumentInt(argc, (const char **)argv, \"num_items\");\n\n        if (num_items < 1) {\n            std::cerr << \"ERROR: num_items has to be greater than 1\" << std::endl;\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Find/set device and get device properties\n    int            device = -1;\n    cudaDeviceProp deviceProp;\n    device = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, device));\n\n    if (!(deviceProp.major > 3 || (deviceProp.major == 3 && deviceProp.minor >= 5))) {\n        printf(\"GPU %d - %s  does not support CUDA Dynamic Parallelism\\n Exiting.\", device, deviceProp.name);\n        exit(EXIT_WAIVED);\n    }\n\n    // Create input data\n    unsigned int *h_data = 0;\n    unsigned int *d_data = 0;\n\n    // Allocate CPU memory and initialize data.\n    std::cout << \"Initializing data:\" << std::endl;\n    h_data = (unsigned int *)malloc(num_items * sizeof(unsigned int));\n    initialize_data(h_data, num_items);\n\n    if (verbose) {\n        for (int i = 0; i < num_items; i++)\n            std::cout << \"Data [\" << i << \"]: \" << h_data[i] << std::endl;\n    }\n\n    // Allocate GPU memory.\n    checkCudaErrors(cudaMalloc((void **)&d_data, num_items * sizeof(unsigned int)));\n    checkCudaErrors(cudaMemcpy(d_data, h_data, num_items * sizeof(unsigned int), cudaMemcpyHostToDevice));\n\n    // Execute\n    std::cout << \"Running quicksort on \" << num_items << \" elements\" << std::endl;\n    run_qsort(d_data, num_items);\n\n    // Check result\n    std::cout << \"Validating results: \";\n    check_results(num_items, d_data);\n\n    free(h_data);\n    checkCudaErrors(cudaFree(d_data));\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cstdio>\n#include <hip/hip_runtime_api.h>\n#include <helper_string.h>\n#include <iostream>\n\n#define MAX_DEPTH      16\n#define INSERTION_SORT 32\n\n////////////////////////////////////////////////////////////////////////////////\n// Selection sort used when depth gets too big or the number of elements drops\n// below a threshold.\n////////////////////////////////////////////////////////////////////////////////\n__device__ void selection_sort(unsigned int *data, int left, int right)\n{\n    for (int i = left; i <= right; ++i) {\n        unsigned min_val = data[i];\n        int      min_idx = i;\n\n        // Find the smallest value in the range [left, right].\n        for (int j = i + 1; j <= right; ++j) {\n            unsigned val_j = data[j];\n\n            if (val_j < min_val) {\n                min_idx = j;\n                min_val = val_j;\n            }\n        }\n\n        // Swap the values.\n        if (i != min_idx) {\n            data[min_idx] = data[i];\n            data[i]       = min_val;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Very basic quicksort algorithm, recursively launching the next level.\n////////////////////////////////////////////////////////////////////////////////\n__global__ void cdp_simple_quicksort(unsigned int *data, int left, int right, int depth)\n{\n    // If we're too deep or there are few elements left, we use an insertion\n    // sort...\n    if (depth >= MAX_DEPTH || right - left <= INSERTION_SORT) {\n        selection_sort(data, left, right);\n        return;\n    }\n\n    unsigned int *lptr  = data + left;\n    unsigned int *rptr  = data + right;\n    unsigned int  pivot = data[(left + right) / 2];\n\n    // Do the partitioning.\n    while (lptr <= rptr) {\n        // Find the next left- and right-hand values to swap\n        unsigned int lval = *lptr;\n        unsigned int rval = *rptr;\n\n        // Move the left pointer as long as the pointed element is smaller than the\n        // pivot.\n        while (lval < pivot) {\n            lptr++;\n            lval = *lptr;\n        }\n\n        // Move the right pointer as long as the pointed element is larger than the\n        // pivot.\n        while (rval > pivot) {\n            rptr--;\n            rval = *rptr;\n        }\n\n        // If the swap points are valid, do the swap!\n        if (lptr <= rptr) {\n            *lptr++ = rval;\n            *rptr-- = lval;\n        }\n    }\n\n    // Now the recursive part\n    int nright = rptr - data;\n    int nleft  = lptr - data;\n\n    // Launch a new block to sort the left part.\n    if (left < (rptr - data)) {\n        hipStream_t s;\n        hipStreamCreateWithFlags(&s, hipStreamNonBlocking);\n        cdp_simple_quicksort<<<1, 1, 0, s>>>(data, left, nright, depth + 1);\n        hipStreamDestroy(s);\n    }\n\n    // Launch a new block to sort the right part.\n    if ((lptr - data) < right) {\n        hipStream_t s1;\n        hipStreamCreateWithFlags(&s1, hipStreamNonBlocking);\n        cdp_simple_quicksort<<<1, 1, 0, s1>>>(data, nleft, right, depth + 1);\n        hipStreamDestroy(s1);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Call the quicksort kernel from the host.\n////////////////////////////////////////////////////////////////////////////////\nvoid run_qsort(unsigned int *data, unsigned int nitems)\n{\n    // Prepare CDP for the max depth 'MAX_DEPTH'.\n\n    // Launch on device\n    int left  = 0;\n    int right = nitems - 1;\n    std::cout << \"Launching kernel on the GPU\" << std::endl;\n    cdp_simple_quicksort<<<1, 1>>>(data, left, right, 0);\n    checkCudaErrors(hipDeviceSynchronize());\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Initialize data on the host.\n////////////////////////////////////////////////////////////////////////////////\nvoid initialize_data(unsigned int *dst, unsigned int nitems)\n{\n    // Fixed seed for illustration\n    srand(2047);\n\n    // Fill dst with random values\n    for (unsigned i = 0; i < nitems; i++)\n        dst[i] = rand() % nitems;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Verify the results.\n////////////////////////////////////////////////////////////////////////////////\nvoid check_results(int n, unsigned int *results_d)\n{\n    unsigned int *results_h = new unsigned[n];\n    checkCudaErrors(hipMemcpy(results_h, results_d, n * sizeof(unsigned), hipMemcpyDeviceToHost));\n\n    for (int i = 1; i < n; ++i)\n        if (results_h[i - 1] > results_h[i]) {\n            std::cout << \"Invalid item[\" << i - 1 << \"]: \" << results_h[i - 1] << \" greater than \" << results_h[i]\n                      << std::endl;\n            exit(EXIT_FAILURE);\n        }\n\n    std::cout << \"OK\" << std::endl;\n    delete[] results_h;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    int  num_items = 128;\n    bool verbose   = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        std::cerr << \"Usage: \" << argv[0]\n                  << \" num_items=<num_items>\\twhere num_items is the number of \"\n                     \"items to sort\"\n                  << std::endl;\n        exit(EXIT_SUCCESS);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"v\")) {\n        verbose = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"num_items\")) {\n        num_items = getCmdLineArgumentInt(argc, (const char **)argv, \"num_items\");\n\n        if (num_items < 1) {\n            std::cerr << \"ERROR: num_items has to be greater than 1\" << std::endl;\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Find/set device and get device properties\n    int            device = -1;\n    hipDeviceProp_t deviceProp;\n    device = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, device));\n\n    if (!(deviceProp.major > 3 || (deviceProp.major == 3 && deviceProp.minor >= 5))) {\n        printf(\"GPU %d - %s  does not support CUDA Dynamic Parallelism\\n Exiting.\", device, deviceProp.name);\n        exit(EXIT_WAIVED);\n    }\n\n    // Create input data\n    unsigned int *h_data = 0;\n    unsigned int *d_data = 0;\n\n    // Allocate CPU memory and initialize data.\n    std::cout << \"Initializing data:\" << std::endl;\n    h_data = (unsigned int *)malloc(num_items * sizeof(unsigned int));\n    initialize_data(h_data, num_items);\n\n    if (verbose) {\n        for (int i = 0; i < num_items; i++)\n            std::cout << \"Data [\" << i << \"]: \" << h_data[i] << std::endl;\n    }\n\n    // Allocate GPU memory.\n    checkCudaErrors(hipMalloc((void **)&d_data, num_items * sizeof(unsigned int)));\n    checkCudaErrors(hipMemcpy(d_data, h_data, num_items * sizeof(unsigned int), hipMemcpyHostToDevice));\n\n    // Execute\n    std::cout << \"Running quicksort on \" << num_items << \" elements\" << std::endl;\n    run_qsort(d_data, num_items);\n\n    // Check result\n    std::cout << \"Validating results: \";\n    check_results(num_items, d_data);\n\n    free(h_data);\n    checkCudaErrors(hipFree(d_data));\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpSimpleQuicksort/cdpSimpleQuicksort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This is a basic, recursive bitonic sort taken from\n// http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm\n//\n// The parallel code is based on:\n// http://www.tools-of-computing.com/tc/CS/Sorts/bitonic_sort.htm\n//\n// The multithread code is from me.\n\n#include <cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\n#include \"cdpQuicksort.h\"\n\n// Inline PTX call to return index of highest non-zero bit in a word\nstatic __device__ __forceinline__ unsigned int __btflo(unsigned int word)\n{\n    unsigned int ret;\n    asm volatile(\"bfind.u32 %0, %1;\" : \"=r\"(ret) : \"r\"(word));\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  qcompare\n//\n//  Comparison function. Note difference from libc standard in\n//  that we take by reference, not by pointer. I can't figure\n//  out how to get a template-to-pointer specialisation working.\n//  Perhaps it requires a class?\n//\n////////////////////////////////////////////////////////////////////////////////\n__device__ __forceinline__ int qcompare(unsigned &val1, unsigned &val2)\n{\n    return (val1 > val2) ? 1 : (val1 == val2) ? 0 : -1;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  Basic any-N bitonic sort. We sort \"len\" elements of \"indata\", starting\n//  from the \"offset\" elements into the input data array. Note that \"outdata\"\n//  can safely be the same as \"indata\" for an in-place sort (we stage through\n//  shared memory).\n//\n//  We handle non-power-of-2 sizes by padding out to the next largest power of\n//  2.\n//  This is the fully-generic version, for sorting arbitrary data which does not\n//  have a clear \"maximum\" value. We track \"invalid\" entries in a separate array\n//  to make sure that they always sorts as \"max value\" elements. A template\n//  parameter \"OOR\" allows specialisation to optimise away the invalid tracking.\n//\n//  We can do a more specialised version for int/longlong/flat/double, in which\n//  we pad out the array with max-value-of-type elements. That's another\n//  function.\n//\n//  The last step copies from indata -> outdata... the rest is done in-place.\n//  We use shared memory as temporary storage, which puts an upper limit on\n//  how much data we can sort per block.\n//\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ void\nbitonicsort_kernel(unsigned *indata, unsigned *outdata, unsigned int offset, unsigned int len, cg::thread_block cta)\n{\n    __shared__ unsigned sortbuf[1024]; // Max of 1024 elements - TODO: make this dynamic\n\n    // First copy data into shared memory.\n    unsigned int inside  = (threadIdx.x < len);\n    sortbuf[threadIdx.x] = inside ? indata[threadIdx.x + offset] : 0xffffffffu;\n    cg::sync(cta);\n\n    // Now the sort loops\n    // Here, \"k\" is the sort level (remember bitonic does a multi-level butterfly\n    // style sort)\n    // and \"j\" is the partner element in the butterfly.\n    // Two threads each work on one butterfly, because the read/write needs to\n    // happen\n    // simultaneously\n    for (unsigned int k = 2; k <= blockDim.x; k *= 2) // Butterfly stride increments in powers of 2\n    {\n        for (unsigned int j = k >> 1; j > 0; j >>= 1) // Strides also in powers of to, up to <k\n        {\n            unsigned int swap_idx  = threadIdx.x ^ j; // Index of element we're compare-and-swapping with\n            unsigned     my_elem   = sortbuf[threadIdx.x];\n            unsigned     swap_elem = sortbuf[swap_idx];\n\n            cg::sync(cta);\n\n            // The k'th bit of my threadid (and hence my sort item ID)\n            // determines if we sort ascending or descending.\n            // However, since threads are reading from the top AND the bottom of\n            // the butterfly, if my ID is > swap_idx, then ascending means mine<swap.\n            // Finally, if either my_elem or swap_elem is out of range, then it\n            // ALWAYS acts like it's the largest number.\n            // Confusing? It saves us two writes though.\n            unsigned int ascend  = k * (swap_idx < threadIdx.x);\n            unsigned int descend = k * (swap_idx > threadIdx.x);\n            bool         swap    = false;\n\n            if ((threadIdx.x & k) == ascend) {\n                if (my_elem > swap_elem)\n                    swap = true;\n            }\n\n            if ((threadIdx.x & k) == descend) {\n                if (my_elem < swap_elem)\n                    swap = true;\n            }\n\n            // If we had to swap, then write my data to the other element's position.\n            // Don't forget to track out-of-range status too!\n            if (swap) {\n                sortbuf[swap_idx] = my_elem;\n            }\n\n            cg::sync(cta);\n        }\n    }\n\n    // Copy the sorted data from shared memory back to the output buffer\n    if (threadIdx.x < len)\n        outdata[threadIdx.x + offset] = sortbuf[threadIdx.x];\n}\n\n//////////////////////////////////////////////////////////////////////////////////\n//  This is an emergency-CTA sort, which sorts an arbitrary sized chunk\n//  using a single block. Useful for if qsort runs out of nesting depth.\n//\n//  Note that bitonic sort needs enough storage to pad up to the nearest\n//  power of 2. This means that the double-buffer is always large enough\n//  (when combined with the main buffer), but we do not get enough space\n//  to keep OOR information.\n//\n//  This in turn means that this sort does not work with a generic data\n//  type. It must be a directly-comparable (i.e. with max value) type.\n//\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ void big_bitonicsort_kernel(unsigned        *indata,\n                                                              unsigned        *outdata,\n                                                              unsigned        *backbuf,\n                                                              unsigned int     offset,\n                                                              unsigned int     len,\n                                                              cg::thread_block cta)\n{\n    unsigned int len2 = 1 << (__btflo(len - 1U) + 1); // Round up len to nearest power-of-2\n\n    if (threadIdx.x >= len2)\n        return; // Early out for case where more threads launched than there is\n                // data\n\n    // First, set up our unused values to be the max data type.\n    for (unsigned int i = len; i < len2; i += blockDim.x) {\n        unsigned int index = i + threadIdx.x;\n\n        if (index < len2) {\n            // Must split our index between two buffers\n            if (index < len)\n                indata[index + offset] = 0xffffffffu;\n            else\n                backbuf[index + offset - len] = 0xffffffffu;\n        }\n    }\n\n    cg::sync(cta);\n\n    // Now the sort loops\n    // Here, \"k\" is the sort level (remember bitonic does a multi-level butterfly\n    // style sort)\n    // and \"j\" is the partner element in the butterfly.\n    // Two threads each work on one butterfly, because the read/write needs to\n    // happen\n    // simultaneously\n    for (unsigned int k = 2; k <= len2; k *= 2) // Butterfly stride increments in powers of 2\n    {\n        for (unsigned int j = k >> 1; j > 0; j >>= 1) // Strides also in powers of to, up to <k\n        {\n            for (unsigned int i = 0; i < len2; i += blockDim.x) {\n                unsigned int index    = threadIdx.x + i;\n                unsigned int swap_idx = index ^ j; // Index of element we're compare-and-swapping with\n\n                // Only do the swap for index<swap_idx (avoids collision between other\n                // threads)\n                if (swap_idx > index) {\n                    unsigned my_elem, swap_elem;\n\n                    if (index < len)\n                        my_elem = indata[index + offset];\n                    else\n                        my_elem = backbuf[index + offset - len];\n\n                    if (swap_idx < len)\n                        swap_elem = indata[swap_idx + offset];\n                    else\n                        swap_elem = backbuf[swap_idx + offset - len];\n\n                    // The k'th bit of my index (and hence my sort item ID)\n                    // determines if we sort ascending or descending.\n                    // Also, if either my_elem or swap_elem is out of range, then it\n                    // ALWAYS acts like it's the largest number.\n                    bool swap = false;\n\n                    if ((index & k) == 0) {\n                        if (my_elem > swap_elem)\n                            swap = true;\n                    }\n\n                    if ((index & k) == k) {\n                        if (my_elem < swap_elem)\n                            swap = true;\n                    }\n\n                    // If we had to swap, then write my data to the other element's\n                    // position.\n                    if (swap) {\n                        if (swap_idx < len)\n                            indata[swap_idx + offset] = my_elem;\n                        else\n                            backbuf[swap_idx + offset - len] = my_elem;\n\n                        if (index < len)\n                            indata[index + offset] = swap_elem;\n                        else\n                            backbuf[index + offset - len] = swap_elem;\n                    }\n                }\n            }\n\n            cg::sync(cta); // Only need to sync for each \"j\" pass\n        }\n    }\n\n    // Copy the sorted data from the input to the output buffer, because we sort\n    // in-place\n    if (outdata != indata) {\n        for (unsigned int i = 0; i < len; i += blockDim.x) {\n            unsigned int index = i + threadIdx.x;\n\n            if (index < len)\n                outdata[index + offset] = indata[index + offset];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// KERNELS\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void bitonicsort(unsigned *indata, unsigned *outdata, unsigned int offset, unsigned int len)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    bitonicsort_kernel(indata, outdata, offset, len, cta);\n}\n\n__global__ void\nbig_bitonicsort(unsigned *indata, unsigned *outdata, unsigned *backbuf, unsigned int offset, unsigned int len)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    big_bitonicsort_kernel(indata, outdata, backbuf, offset, len, cta);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This is a basic, recursive bitonic sort taken from\n// http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm\n//\n// The parallel code is based on:\n// http://www.tools-of-computing.com/tc/CS/Sorts/bitonic_sort.htm\n//\n// The multithread code is from me.\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\n#include \"cdpQuicksort.h\"\n\n// Inline PTX call to return index of highest non-zero bit in a word\nstatic __device__ __forceinline__ unsigned int __btflo(unsigned int word)\n{\n    unsigned int ret;\n    asm volatile(\"bfind.u32 %0, %1;\" : \"=r\"(ret) : \"r\"(word));\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  qcompare\n//\n//  Comparison function. Note difference from libc standard in\n//  that we take by reference, not by pointer. I can't figure\n//  out how to get a template-to-pointer specialisation working.\n//  Perhaps it requires a class?\n//\n////////////////////////////////////////////////////////////////////////////////\n__device__ __forceinline__ int qcompare(unsigned &val1, unsigned &val2)\n{\n    return (val1 > val2) ? 1 : (val1 == val2) ? 0 : -1;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  Basic any-N bitonic sort. We sort \"len\" elements of \"indata\", starting\n//  from the \"offset\" elements into the input data array. Note that \"outdata\"\n//  can safely be the same as \"indata\" for an in-place sort (we stage through\n//  shared memory).\n//\n//  We handle non-power-of-2 sizes by padding out to the next largest power of\n//  2.\n//  This is the fully-generic version, for sorting arbitrary data which does not\n//  have a clear \"maximum\" value. We track \"invalid\" entries in a separate array\n//  to make sure that they always sorts as \"max value\" elements. A template\n//  parameter \"OOR\" allows specialisation to optimise away the invalid tracking.\n//\n//  We can do a more specialised version for int/longlong/flat/double, in which\n//  we pad out the array with max-value-of-type elements. That's another\n//  function.\n//\n//  The last step copies from indata -> outdata... the rest is done in-place.\n//  We use shared memory as temporary storage, which puts an upper limit on\n//  how much data we can sort per block.\n//\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ void\nbitonicsort_kernel(unsigned *indata, unsigned *outdata, unsigned int offset, unsigned int len, cg::thread_block cta)\n{\n    __shared__ unsigned sortbuf[1024]; // Max of 1024 elements - TODO: make this dynamic\n\n    // First copy data into shared memory.\n    unsigned int inside  = (threadIdx.x < len);\n    sortbuf[threadIdx.x] = inside ? indata[threadIdx.x + offset] : 0xffffffffu;\n    cg::sync(cta);\n\n    // Now the sort loops\n    // Here, \"k\" is the sort level (remember bitonic does a multi-level butterfly\n    // style sort)\n    // and \"j\" is the partner element in the butterfly.\n    // Two threads each work on one butterfly, because the read/write needs to\n    // happen\n    // simultaneously\n    for (unsigned int k = 2; k <= blockDim.x; k *= 2) // Butterfly stride increments in powers of 2\n    {\n        for (unsigned int j = k >> 1; j > 0; j >>= 1) // Strides also in powers of to, up to <k\n        {\n            unsigned int swap_idx  = threadIdx.x ^ j; // Index of element we're compare-and-swapping with\n            unsigned     my_elem   = sortbuf[threadIdx.x];\n            unsigned     swap_elem = sortbuf[swap_idx];\n\n            cg::sync(cta);\n\n            // The k'th bit of my threadid (and hence my sort item ID)\n            // determines if we sort ascending or descending.\n            // However, since threads are reading from the top AND the bottom of\n            // the butterfly, if my ID is > swap_idx, then ascending means mine<swap.\n            // Finally, if either my_elem or swap_elem is out of range, then it\n            // ALWAYS acts like it's the largest number.\n            // Confusing? It saves us two writes though.\n            unsigned int ascend  = k * (swap_idx < threadIdx.x);\n            unsigned int descend = k * (swap_idx > threadIdx.x);\n            bool         swap    = false;\n\n            if ((threadIdx.x & k) == ascend) {\n                if (my_elem > swap_elem)\n                    swap = true;\n            }\n\n            if ((threadIdx.x & k) == descend) {\n                if (my_elem < swap_elem)\n                    swap = true;\n            }\n\n            // If we had to swap, then write my data to the other element's position.\n            // Don't forget to track out-of-range status too!\n            if (swap) {\n                sortbuf[swap_idx] = my_elem;\n            }\n\n            cg::sync(cta);\n        }\n    }\n\n    // Copy the sorted data from shared memory back to the output buffer\n    if (threadIdx.x < len)\n        outdata[threadIdx.x + offset] = sortbuf[threadIdx.x];\n}\n\n//////////////////////////////////////////////////////////////////////////////////\n//  This is an emergency-CTA sort, which sorts an arbitrary sized chunk\n//  using a single block. Useful for if qsort runs out of nesting depth.\n//\n//  Note that bitonic sort needs enough storage to pad up to the nearest\n//  power of 2. This means that the double-buffer is always large enough\n//  (when combined with the main buffer), but we do not get enough space\n//  to keep OOR information.\n//\n//  This in turn means that this sort does not work with a generic data\n//  type. It must be a directly-comparable (i.e. with max value) type.\n//\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ void big_bitonicsort_kernel(unsigned        *indata,\n                                                              unsigned        *outdata,\n                                                              unsigned        *backbuf,\n                                                              unsigned int     offset,\n                                                              unsigned int     len,\n                                                              cg::thread_block cta)\n{\n    unsigned int len2 = 1 << (__btflo(len - 1U) + 1); // Round up len to nearest power-of-2\n\n    if (threadIdx.x >= len2)\n        return; // Early out for case where more threads launched than there is\n                // data\n\n    // First, set up our unused values to be the max data type.\n    for (unsigned int i = len; i < len2; i += blockDim.x) {\n        unsigned int index = i + threadIdx.x;\n\n        if (index < len2) {\n            // Must split our index between two buffers\n            if (index < len)\n                indata[index + offset] = 0xffffffffu;\n            else\n                backbuf[index + offset - len] = 0xffffffffu;\n        }\n    }\n\n    cg::sync(cta);\n\n    // Now the sort loops\n    // Here, \"k\" is the sort level (remember bitonic does a multi-level butterfly\n    // style sort)\n    // and \"j\" is the partner element in the butterfly.\n    // Two threads each work on one butterfly, because the read/write needs to\n    // happen\n    // simultaneously\n    for (unsigned int k = 2; k <= len2; k *= 2) // Butterfly stride increments in powers of 2\n    {\n        for (unsigned int j = k >> 1; j > 0; j >>= 1) // Strides also in powers of to, up to <k\n        {\n            for (unsigned int i = 0; i < len2; i += blockDim.x) {\n                unsigned int index    = threadIdx.x + i;\n                unsigned int swap_idx = index ^ j; // Index of element we're compare-and-swapping with\n\n                // Only do the swap for index<swap_idx (avoids collision between other\n                // threads)\n                if (swap_idx > index) {\n                    unsigned my_elem, swap_elem;\n\n                    if (index < len)\n                        my_elem = indata[index + offset];\n                    else\n                        my_elem = backbuf[index + offset - len];\n\n                    if (swap_idx < len)\n                        swap_elem = indata[swap_idx + offset];\n                    else\n                        swap_elem = backbuf[swap_idx + offset - len];\n\n                    // The k'th bit of my index (and hence my sort item ID)\n                    // determines if we sort ascending or descending.\n                    // Also, if either my_elem or swap_elem is out of range, then it\n                    // ALWAYS acts like it's the largest number.\n                    bool swap = false;\n\n                    if ((index & k) == 0) {\n                        if (my_elem > swap_elem)\n                            swap = true;\n                    }\n\n                    if ((index & k) == k) {\n                        if (my_elem < swap_elem)\n                            swap = true;\n                    }\n\n                    // If we had to swap, then write my data to the other element's\n                    // position.\n                    if (swap) {\n                        if (swap_idx < len)\n                            indata[swap_idx + offset] = my_elem;\n                        else\n                            backbuf[swap_idx + offset - len] = my_elem;\n\n                        if (index < len)\n                            indata[index + offset] = swap_elem;\n                        else\n                            backbuf[index + offset - len] = swap_elem;\n                    }\n                }\n            }\n\n            cg::sync(cta); // Only need to sync for each \"j\" pass\n        }\n    }\n\n    // Copy the sorted data from the input to the output buffer, because we sort\n    // in-place\n    if (outdata != indata) {\n        for (unsigned int i = 0; i < len; i += blockDim.x) {\n            unsigned int index = i + threadIdx.x;\n\n            if (index < len)\n                outdata[index + offset] = indata[index + offset];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// KERNELS\n////////////////////////////////////////////////////////////////////////////////\n\n__global__ void bitonicsort(unsigned *indata, unsigned *outdata, unsigned int offset, unsigned int len)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    bitonicsort_kernel(indata, outdata, offset, len, cta);\n}\n\n__global__ void\nbig_bitonicsort(unsigned *indata, unsigned *outdata, unsigned *backbuf, unsigned int offset, unsigned int len)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    big_bitonicsort_kernel(indata, outdata, backbuf, offset, len, cta);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpAdvancedQuicksort/cdpBitonicSort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  cdpAdvancedQuicksort.cu\n//\n//  Implementation of a parallel quicksort in CUDA. It comes in\n//  several parts:\n//\n//  1. A small-set insertion sort. We do this on any set with <=32 elements\n//  2. A partitioning kernel, which - given a pivot - separates an input\n//     array into elements <=pivot, and >pivot. Two quicksorts will then\n//     be launched to resolve each of these.\n//  3. A quicksort co-ordinator, which figures out what kernels to launch\n//     and when.\n//\n////////////////////////////////////////////////////////////////////////////////\n#include <cooperative_groups.h>\n#include <thrust/device_vector.h>\n#include <thrust/random.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n#include <helper_string.h>\n\n#include \"cdpQuicksort.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Inline PTX call to return index of highest non-zero bit in a word\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ unsigned int __qsflo(unsigned int word)\n{\n    unsigned int ret;\n    asm volatile(\"bfind.u32 %0, %1;\" : \"=r\"(ret) : \"r\"(word));\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  ringbufAlloc\n//\n//  Allocates from a ringbuffer. Allows for not failing when we run out\n//  of stack for tracking the offset counts for each sort subsection.\n//\n//  We use the atomicMax trick to allow out-of-order retirement. If we\n//  hit the size limit on the ringbuffer, then we spin-wait for people\n//  to complete.\n//\n////////////////////////////////////////////////////////////////////////////////\ntemplate <typename T> static __device__ T *ringbufAlloc(qsortRingbuf *ringbuf)\n{\n    // Wait for there to be space in the ring buffer. We'll retry only a fixed\n    // number of times and then fail, to avoid an out-of-memory deadlock.\n    unsigned int loop = 10000;\n\n    while (((ringbuf->head - ringbuf->tail) >= ringbuf->stacksize) && (loop-- > 0))\n        ;\n\n    if (loop == 0)\n        return NULL;\n\n    // Note that the element includes a little index book-keeping, for freeing\n    // later.\n    unsigned int index = atomicAdd((unsigned int *)&ringbuf->head, 1);\n    T           *ret   = (T *)(ringbuf->stackbase) + (index & (ringbuf->stacksize - 1));\n    ret->index         = index;\n\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  ringBufFree\n//\n//  Releases an element from the ring buffer. If every element is released\n//  up to and including this one, we can advance the tail to indicate that\n//  space is now available.\n//\n////////////////////////////////////////////////////////////////////////////////\ntemplate <typename T> static __device__ void ringbufFree(qsortRingbuf *ringbuf, T *data)\n{\n    unsigned int index = data->index; // Non-wrapped index to free\n    unsigned int count = atomicAdd((unsigned int *)&(ringbuf->count), 1) + 1;\n    unsigned int max   = atomicMax((unsigned int *)&(ringbuf->max), index + 1);\n\n    // Update the tail if need be. Note we update \"max\" to be the new value in\n    // ringbuf->max\n    if (max < (index + 1))\n        max = index + 1;\n\n    if (max == count)\n        atomicMax((unsigned int *)&(ringbuf->tail), count);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  qsort_warp\n//\n//  Simplest possible implementation, does a per-warp quicksort with no\n//  inter-warp\n//  communication. This has a high atomic issue rate, but the rest should\n//  actually\n//  be fairly quick because of low work per thread.\n//\n//  A warp finds its section of the data, then writes all data <pivot to one\n//  buffer and all data >pivot to the other. Atomics are used to get a unique\n//  section of the buffer.\n//\n//  Obvious optimisation: do multiple chunks per warp, to increase in-flight\n//  loads\n//  and cover the instruction overhead.\n//\n////////////////////////////////////////////////////////////////////////////////\n__global__ void qsort_warp(unsigned        *indata,\n                           unsigned        *outdata,\n                           unsigned int     offset,\n                           unsigned int     len,\n                           qsortAtomicData *atomicData,\n                           qsortRingbuf    *atomicDataStack,\n                           unsigned int     source_is_indata,\n                           unsigned int     depth)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Find my data offset, based on warp ID\n    unsigned int thread_id = threadIdx.x + (blockIdx.x << QSORT_BLOCKSIZE_SHIFT);\n    // unsigned int warp_id = threadIdx.x >> 5;   // Used for debug only\n    unsigned int lane_id = threadIdx.x & (warpSize - 1);\n\n    // Exit if I'm outside the range of sort to be done\n    if (thread_id >= len)\n        return;\n\n    //\n    // First part of the algorithm. Each warp counts the number of elements that\n    // are\n    // greater/less than the pivot.\n    //\n    // When a warp knows its count, it updates an atomic counter.\n    //\n\n    // Read in the data and the pivot. Arbitrary pivot selection for now.\n    unsigned pivot = indata[offset + len / 2];\n    unsigned data  = indata[offset + thread_id];\n\n    // Count how many are <= and how many are > pivot.\n    // If all are <= pivot then we adjust the comparison\n    // because otherwise the sort will move nothing and\n    // we'll iterate forever.\n    cg::coalesced_group active  = cg::coalesced_threads();\n    unsigned int        greater = (data > pivot);\n    unsigned int        gt_mask = active.ballot(greater);\n\n    if (gt_mask == 0) {\n        greater = (data >= pivot);\n        gt_mask = active.ballot(greater); // Must re-ballot for adjusted comparator\n    }\n\n    unsigned int lt_mask  = active.ballot(!greater);\n    unsigned int gt_count = __popc(gt_mask);\n    unsigned int lt_count = __popc(lt_mask);\n\n    // Atomically adjust the lt_ and gt_offsets by this amount. Only one thread\n    // need do this. Share the result using shfl\n    unsigned int lt_offset, gt_offset;\n\n    if (lane_id == 0) {\n        if (lt_count > 0)\n            lt_offset = atomicAdd((unsigned int *)&atomicData->lt_offset, lt_count);\n\n        if (gt_count > 0)\n            gt_offset = len - (atomicAdd((unsigned int *)&atomicData->gt_offset, gt_count) + gt_count);\n    }\n\n    lt_offset = active.shfl((int)lt_offset, 0); // Everyone pulls the offsets from lane 0\n    gt_offset = active.shfl((int)gt_offset, 0);\n\n    // Now compute my own personal offset within this. I need to know how many\n    // threads with a lane ID less than mine are going to write to the same buffer\n    // as me. We can use popc to implement a single-operation warp scan in this\n    // case.\n    unsigned lane_mask_lt;\n    asm(\"mov.u32 %0, %%lanemask_lt;\" : \"=r\"(lane_mask_lt));\n    unsigned int my_mask   = greater ? gt_mask : lt_mask;\n    unsigned int my_offset = __popc(my_mask & lane_mask_lt);\n\n    // Move data.\n    my_offset += greater ? gt_offset : lt_offset;\n    outdata[offset + my_offset] = data;\n\n    // Count up if we're the last warp in. If so, then Kepler will launch the next\n    // set of sorts directly from here.\n    if (lane_id == 0) {\n        // Count \"elements written\". If I wrote the last one, then trigger the next\n        // qsorts\n        unsigned int mycount = lt_count + gt_count;\n\n        if (atomicAdd((unsigned int *)&atomicData->sorted_count, mycount) + mycount == len) {\n            // We're the last warp to do any sorting. Therefore it's up to us to\n            // launch the next stage.\n            unsigned int lt_len = atomicData->lt_offset;\n            unsigned int gt_len = atomicData->gt_offset;\n\n            cudaStream_t lstream, rstream;\n            cudaStreamCreateWithFlags(&lstream, cudaStreamNonBlocking);\n            cudaStreamCreateWithFlags(&rstream, cudaStreamNonBlocking);\n\n            // Begin by freeing our atomicData storage. It's better for the ringbuffer\n            // algorithm\n            // if we free when we're done, rather than re-using (makes for less\n            // fragmentation).\n            ringbufFree<qsortAtomicData>(atomicDataStack, atomicData);\n\n            // Exceptional case: if \"lt_len\" is zero, then all values in the batch\n            // are equal. We are then done (may need to copy into correct buffer,\n            // though)\n            if (lt_len == 0) {\n                if (source_is_indata)\n                    cudaMemcpyAsync(indata + offset,\n                                    outdata + offset,\n                                    gt_len * sizeof(unsigned),\n                                    cudaMemcpyDeviceToDevice,\n                                    lstream);\n\n                return;\n            }\n\n            // Start with lower half first\n            if (lt_len > BITONICSORT_LEN) {\n                // If we've exceeded maximum depth, fall through to backup\n                // big_bitonicsort\n                if (depth >= QSORT_MAXDEPTH) {\n                    // The final bitonic stage sorts in-place in \"outdata\". We therefore\n                    // re-use \"indata\" as the out-of-range tracking buffer. For (2^n)+1\n                    // elements we need (2^(n+1)) bytes of oor buffer. The backup qsort\n                    // buffer is at least this large when sizeof(QTYPE) >= 2.\n                    big_bitonicsort<<<1, BITONICSORT_LEN, 0, lstream>>>(\n                        outdata, source_is_indata ? indata : outdata, indata, offset, lt_len);\n                }\n                else {\n                    // Launch another quicksort. We need to allocate more storage for the\n                    // atomic data.\n                    if ((atomicData = ringbufAlloc<qsortAtomicData>(atomicDataStack)) == NULL)\n                        printf(\"Stack-allocation error. Failing left child launch.\\n\");\n                    else {\n                        atomicData->lt_offset = atomicData->gt_offset = atomicData->sorted_count = 0;\n                        unsigned int numblocks =\n                            (unsigned int)(lt_len + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n                        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, lstream>>>(\n                            outdata, indata, offset, lt_len, atomicData, atomicDataStack, !source_is_indata, depth + 1);\n                    }\n                }\n            }\n            else if (lt_len > 1) {\n                // Final stage uses a bitonic sort instead. It's important to\n                // make sure the final stage ends up in the correct (original) buffer.\n                // We launch the smallest power-of-2 number of threads that we can.\n                unsigned int bitonic_len = 1 << (__qsflo(lt_len - 1U) + 1);\n                bitonicsort<<<1, bitonic_len, 0, lstream>>>(\n                    outdata, source_is_indata ? indata : outdata, offset, lt_len);\n            }\n            // Finally, if we sorted just one single element, we must still make\n            // sure that it winds up in the correct place.\n            else if (source_is_indata && (lt_len == 1))\n                indata[offset] = outdata[offset];\n\n            if (cudaPeekAtLastError() != cudaSuccess)\n                printf(\"Left-side launch fail: %s\\n\", cudaGetErrorString(cudaGetLastError()));\n\n            // Now the upper half.\n            if (gt_len > BITONICSORT_LEN) {\n                // If we've exceeded maximum depth, fall through to backup\n                // big_bitonicsort\n                if (depth >= QSORT_MAXDEPTH)\n                    big_bitonicsort<<<1, BITONICSORT_LEN, 0, rstream>>>(\n                        outdata, source_is_indata ? indata : outdata, indata, offset + lt_len, gt_len);\n                else {\n                    // Allocate new atomic storage for this launch\n                    if ((atomicData = ringbufAlloc<qsortAtomicData>(atomicDataStack)) == NULL)\n                        printf(\"Stack allocation error! Failing right-side launch.\\n\");\n                    else {\n                        atomicData->lt_offset = atomicData->gt_offset = atomicData->sorted_count = 0;\n                        unsigned int numblocks =\n                            (unsigned int)(gt_len + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n                        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, rstream>>>(outdata,\n                                                                               indata,\n                                                                               offset + lt_len,\n                                                                               gt_len,\n                                                                               atomicData,\n                                                                               atomicDataStack,\n                                                                               !source_is_indata,\n                                                                               depth + 1);\n                    }\n                }\n            }\n            else if (gt_len > 1) {\n                unsigned int bitonic_len = 1 << (__qsflo(gt_len - 1U) + 1);\n                bitonicsort<<<1, bitonic_len, 0, rstream>>>(\n                    outdata, source_is_indata ? indata : outdata, offset + lt_len, gt_len);\n            }\n            else if (source_is_indata && (gt_len == 1))\n                indata[offset + lt_len] = outdata[offset + lt_len];\n\n            if (cudaPeekAtLastError() != cudaSuccess)\n                printf(\"Right-side launch fail: %s\\n\", cudaGetErrorString(cudaGetLastError()));\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  run_quicksort\n//\n//  Host-side code to run the Kepler version of quicksort. It's pretty\n//  simple, because all launch control is handled on the device via CDP.\n//\n//  All parallel quicksorts require an equal-sized scratch buffer. This\n//  must be passed in ahead of time.\n//\n//  Returns the time elapsed for the sort.\n//\n////////////////////////////////////////////////////////////////////////////////\nfloat run_quicksort_cdp(unsigned *gpudata, unsigned *scratchdata, unsigned int count, cudaStream_t stream)\n{\n    unsigned int stacksize = QSORT_STACK_ELEMS;\n\n    // This is the stack, for atomic tracking of each sort's status\n    qsortAtomicData *gpustack;\n    checkCudaErrors(cudaMalloc((void **)&gpustack, stacksize * sizeof(qsortAtomicData)));\n    checkCudaErrors(cudaMemset(gpustack, 0, sizeof(qsortAtomicData))); // Only need set first entry to 0\n\n    // Create the memory ringbuffer used for handling the stack.\n    // Initialise everything to where it needs to be.\n    qsortRingbuf  buf;\n    qsortRingbuf *ringbuf;\n    checkCudaErrors(cudaMalloc((void **)&ringbuf, sizeof(qsortRingbuf)));\n    buf.head      = 1; // We start with one allocation\n    buf.tail      = 0;\n    buf.count     = 0;\n    buf.max       = 0;\n    buf.stacksize = stacksize;\n    buf.stackbase = gpustack;\n    checkCudaErrors(cudaMemcpy(ringbuf, &buf, sizeof(buf), cudaMemcpyHostToDevice));\n\n    // Timing events...\n    cudaEvent_t ev1, ev2;\n    checkCudaErrors(cudaEventCreate(&ev1));\n    checkCudaErrors(cudaEventCreate(&ev2));\n    checkCudaErrors(cudaEventRecord(ev1));\n\n    // Now we trivially launch the qsort kernel\n    if (count > BITONICSORT_LEN) {\n        unsigned int numblocks = (unsigned int)(count + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, stream>>>(\n            gpudata, scratchdata, 0U, count, gpustack, ringbuf, true, 0);\n    }\n    else {\n        bitonicsort<<<1, BITONICSORT_LEN>>>(gpudata, gpudata, 0, count);\n    }\n\n    checkCudaErrors(cudaGetLastError());\n    checkCudaErrors(cudaEventRecord(ev2));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    float elapse = 0.0f;\n\n    if (cudaPeekAtLastError() != cudaSuccess)\n        printf(\"Launch failure: %s\\n\", cudaGetErrorString(cudaGetLastError()));\n    else\n        checkCudaErrors(cudaEventElapsedTime(&elapse, ev1, ev2));\n\n    // Sanity check that the stack allocator is doing the right thing\n    checkCudaErrors(cudaMemcpy(&buf, ringbuf, sizeof(*ringbuf), cudaMemcpyDeviceToHost));\n\n    if (count > BITONICSORT_LEN && buf.head != buf.tail) {\n        printf(\"Stack allocation error!\\nRingbuf:\\n\");\n        printf(\"\\t head = %u\\n\", buf.head);\n        printf(\"\\t tail = %u\\n\", buf.tail);\n        printf(\"\\tcount = %u\\n\", buf.count);\n        printf(\"\\t  max = %u\\n\", buf.max);\n    }\n\n    // Release our stack data once we're done\n    checkCudaErrors(cudaFree(ringbuf));\n    checkCudaErrors(cudaFree(gpustack));\n\n    return elapse;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////////\nint run_qsort(unsigned int size, int seed, int debug, int loop, int verbose)\n{\n    if (seed > 0)\n        srand(seed);\n\n    // Create and set up our test\n    unsigned *gpudata, *scratchdata;\n    checkCudaErrors(cudaMalloc((void **)&gpudata, size * sizeof(unsigned)));\n    checkCudaErrors(cudaMalloc((void **)&scratchdata, size * sizeof(unsigned)));\n\n    // Create CPU data.\n    unsigned    *data = new unsigned[size];\n    unsigned int min  = loop ? loop : size;\n    unsigned int max  = size;\n    loop              = (loop == 0) ? 1 : loop;\n\n    for (size = min; size <= max; size += loop) {\n        if (verbose)\n            printf(\" Input: \");\n\n        for (unsigned int i = 0; i < size; i++) {\n            // Build data 8 bits at a time\n            data[i]   = 0;\n            char *ptr = (char *)&(data[i]);\n\n            for (unsigned j = 0; j < sizeof(unsigned); j++) {\n                // Easy-to-read data in debug mode\n                if (debug) {\n                    *ptr++ = (char)(rand() % 10);\n                    break;\n                }\n\n                *ptr++ = (char)(rand() & 255);\n            }\n\n            if (verbose) {\n                if (i && !(i % 32))\n                    printf(\"\\n        \");\n\n                printf(\"%u \", data[i]);\n            }\n        }\n\n        if (verbose)\n            printf(\"\\n\");\n\n        checkCudaErrors(cudaMemcpy(gpudata, data, size * sizeof(unsigned), cudaMemcpyHostToDevice));\n\n        // So we're now populated and ready to go! We size our launch as\n        // blocks of up to BLOCKSIZE threads, and appropriate grid size.\n        // One thread is launched per element.\n        float elapse;\n        elapse = run_quicksort_cdp(gpudata, scratchdata, size, NULL);\n\n        // run_bitonicsort<SORTTYPE>(gpudata, scratchdata, size, verbose);\n        checkCudaErrors(cudaDeviceSynchronize());\n\n        // Copy back the data and verify correct sort\n        checkCudaErrors(cudaMemcpy(data, gpudata, size * sizeof(unsigned), cudaMemcpyDeviceToHost));\n\n        if (verbose) {\n            printf(\"Output: \");\n\n            for (unsigned int i = 0; i < size; i++) {\n                if (i && !(i % 32))\n                    printf(\"\\n        \");\n\n                printf(\"%u \", data[i]);\n            }\n\n            printf(\"\\n\");\n        }\n\n        unsigned int check;\n\n        for (check = 1; check < size; check++) {\n            if (data[check] < data[check - 1]) {\n                printf(\"FAILED at element: %d\\n\", check);\n                break;\n            }\n        }\n\n        if (check != size) {\n            printf(\"    cdpAdvancedQuicksort FAILED\\n\");\n            exit(EXIT_FAILURE);\n        }\n        else\n            printf(\"    cdpAdvancedQuicksort PASSED\\n\");\n\n        // Display the time between event recordings\n        printf(\"Sorted %u elems in %.3f ms (%.3f Melems/sec)\\n\", size, elapse, (float)size / (elapse * 1000.0f));\n        fflush(stdout);\n    }\n\n    // Release everything and we're done\n    checkCudaErrors(cudaFree(scratchdata));\n    checkCudaErrors(cudaFree(gpudata));\n    delete (data);\n    return 0;\n}\n\nstatic void usage()\n{\n    printf(\"Syntax: cdpAdvancedQuicksort [-size=<num>] [-seed=<num>] [-debug] \"\n           \"[-loop-step=<num>] [-verbose]\\n\");\n    printf(\"If loop_step is non-zero, will run from 1->array_len in steps of \"\n           \"loop_step\\n\");\n}\n\n// Host side entry\nint main(int argc, char *argv[])\n{\n    int          size    = 1000000;\n    unsigned int seed    = 0;\n    int          debug   = 0;\n    int          loop    = 0;\n    int          verbose = 0;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        usage();\n        printf(\"&&&& cdpAdvancedQuicksort WAIVED\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"size\")) {\n        size = getCmdLineArgumentInt(argc, (const char **)argv, \"size\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"seed\")) {\n        seed = getCmdLineArgumentInt(argc, (const char **)argv, \"seed\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"loop-step\")) {\n        loop = getCmdLineArgumentInt(argc, (const char **)argv, \"loop-step\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"debug\")) {\n        debug = 1;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"verbose\")) {\n        verbose = 1;\n    }\n\n    // Get device properties\n    int            cuda_device = findCudaDevice(argc, (const char **)argv);\n    cudaDeviceProp properties;\n    checkCudaErrors(cudaGetDeviceProperties(&properties, cuda_device));\n    int cdpCapable = (properties.major == 3 && properties.minor >= 5) || properties.major >= 4;\n\n    printf(\"GPU device %s has compute capabilities (SM %d.%d)\\n\", properties.name, properties.major, properties.minor);\n\n    if (!cdpCapable) {\n        printf(\"cdpAdvancedQuicksort requires SM 3.5 or higher to use CUDA Dynamic \"\n               \"Parallelism.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"Running qsort on %d elements with seed %d, on %s\\n\", size, seed, properties.name);\n\n    // Increase the pending kernel launch limit\n    cudaDeviceSetLimit(cudaLimitDevRuntimePendingLaunchCount, 4096);\n\n    run_qsort(size, seed, debug, loop, verbose);\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  cdpAdvancedQuicksort.cu\n//\n//  Implementation of a parallel quicksort in CUDA. It comes in\n//  several parts:\n//\n//  1. A small-set insertion sort. We do this on any set with <=32 elements\n//  2. A partitioning kernel, which - given a pivot - separates an input\n//     array into elements <=pivot, and >pivot. Two quicksorts will then\n//     be launched to resolve each of these.\n//  3. A quicksort co-ordinator, which figures out what kernels to launch\n//     and when.\n//\n////////////////////////////////////////////////////////////////////////////////\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <thrust/device_vector.h>\n#include <thrust/random.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n#include <helper_string.h>\n\n#include \"cdpQuicksort.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Inline PTX call to return index of highest non-zero bit in a word\n////////////////////////////////////////////////////////////////////////////////\nstatic __device__ __forceinline__ unsigned int __qsflo(unsigned int word)\n{\n    unsigned int ret;\n    asm volatile(\"bfind.u32 %0, %1;\" : \"=r\"(ret) : \"r\"(word));\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  ringbufAlloc\n//\n//  Allocates from a ringbuffer. Allows for not failing when we run out\n//  of stack for tracking the offset counts for each sort subsection.\n//\n//  We use the atomicMax trick to allow out-of-order retirement. If we\n//  hit the size limit on the ringbuffer, then we spin-wait for people\n//  to complete.\n//\n////////////////////////////////////////////////////////////////////////////////\ntemplate <typename T> static __device__ T *ringbufAlloc(qsortRingbuf *ringbuf)\n{\n    // Wait for there to be space in the ring buffer. We'll retry only a fixed\n    // number of times and then fail, to avoid an out-of-memory deadlock.\n    unsigned int loop = 10000;\n\n    while (((ringbuf->head - ringbuf->tail) >= ringbuf->stacksize) && (loop-- > 0))\n        ;\n\n    if (loop == 0)\n        return NULL;\n\n    // Note that the element includes a little index book-keeping, for freeing\n    // later.\n    unsigned int index = atomicAdd((unsigned int *)&ringbuf->head, 1);\n    T           *ret   = (T *)(ringbuf->stackbase) + (index & (ringbuf->stacksize - 1));\n    ret->index         = index;\n\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  ringBufFree\n//\n//  Releases an element from the ring buffer. If every element is released\n//  up to and including this one, we can advance the tail to indicate that\n//  space is now available.\n//\n////////////////////////////////////////////////////////////////////////////////\ntemplate <typename T> static __device__ void ringbufFree(qsortRingbuf *ringbuf, T *data)\n{\n    unsigned int index = data->index; // Non-wrapped index to free\n    unsigned int count = atomicAdd((unsigned int *)&(ringbuf->count), 1) + 1;\n    unsigned int max   = atomicMax((unsigned int *)&(ringbuf->max), index + 1);\n\n    // Update the tail if need be. Note we update \"max\" to be the new value in\n    // ringbuf->max\n    if (max < (index + 1))\n        max = index + 1;\n\n    if (max == count)\n        atomicMax((unsigned int *)&(ringbuf->tail), count);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  qsort_warp\n//\n//  Simplest possible implementation, does a per-warp quicksort with no\n//  inter-warp\n//  communication. This has a high atomic issue rate, but the rest should\n//  actually\n//  be fairly quick because of low work per thread.\n//\n//  A warp finds its section of the data, then writes all data <pivot to one\n//  buffer and all data >pivot to the other. Atomics are used to get a unique\n//  section of the buffer.\n//\n//  Obvious optimisation: do multiple chunks per warp, to increase in-flight\n//  loads\n//  and cover the instruction overhead.\n//\n////////////////////////////////////////////////////////////////////////////////\n__global__ void qsort_warp(unsigned        *indata,\n                           unsigned        *outdata,\n                           unsigned int     offset,\n                           unsigned int     len,\n                           qsortAtomicData *atomicData,\n                           qsortRingbuf    *atomicDataStack,\n                           unsigned int     source_is_indata,\n                           unsigned int     depth)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Find my data offset, based on warp ID\n    unsigned int thread_id = threadIdx.x + (blockIdx.x << QSORT_BLOCKSIZE_SHIFT);\n    // unsigned int warp_id = threadIdx.x >> 5;   // Used for debug only\n    unsigned int lane_id = threadIdx.x & (warpSize - 1);\n\n    // Exit if I'm outside the range of sort to be done\n    if (thread_id >= len)\n        return;\n\n    //\n    // First part of the algorithm. Each warp counts the number of elements that\n    // are\n    // greater/less than the pivot.\n    //\n    // When a warp knows its count, it updates an atomic counter.\n    //\n\n    // Read in the data and the pivot. Arbitrary pivot selection for now.\n    unsigned pivot = indata[offset + len / 2];\n    unsigned data  = indata[offset + thread_id];\n\n    // Count how many are <= and how many are > pivot.\n    // If all are <= pivot then we adjust the comparison\n    // because otherwise the sort will move nothing and\n    // we'll iterate forever.\n    cg::coalesced_group active  = cg::coalesced_threads();\n    unsigned int        greater = (data > pivot);\n    unsigned int        gt_mask = active.ballot(greater);\n\n    if (gt_mask == 0) {\n        greater = (data >= pivot);\n        gt_mask = active.ballot(greater); // Must re-ballot for adjusted comparator\n    }\n\n    unsigned int lt_mask  = active.ballot(!greater);\n    unsigned int gt_count = __popc(gt_mask);\n    unsigned int lt_count = __popc(lt_mask);\n\n    // Atomically adjust the lt_ and gt_offsets by this amount. Only one thread\n    // need do this. Share the result using shfl\n    unsigned int lt_offset, gt_offset;\n\n    if (lane_id == 0) {\n        if (lt_count > 0)\n            lt_offset = atomicAdd((unsigned int *)&atomicData->lt_offset, lt_count);\n\n        if (gt_count > 0)\n            gt_offset = len - (atomicAdd((unsigned int *)&atomicData->gt_offset, gt_count) + gt_count);\n    }\n\n    lt_offset = active.shfl((int)lt_offset, 0); // Everyone pulls the offsets from lane 0\n    gt_offset = active.shfl((int)gt_offset, 0);\n\n    // Now compute my own personal offset within this. I need to know how many\n    // threads with a lane ID less than mine are going to write to the same buffer\n    // as me. We can use popc to implement a single-operation warp scan in this\n    // case.\n    unsigned lane_mask_lt;\n    asm(\"mov.u32 %0, %%lanemask_lt;\" : \"=r\"(lane_mask_lt));\n    unsigned int my_mask   = greater ? gt_mask : lt_mask;\n    unsigned int my_offset = __popc(my_mask & lane_mask_lt);\n\n    // Move data.\n    my_offset += greater ? gt_offset : lt_offset;\n    outdata[offset + my_offset] = data;\n\n    // Count up if we're the last warp in. If so, then Kepler will launch the next\n    // set of sorts directly from here.\n    if (lane_id == 0) {\n        // Count \"elements written\". If I wrote the last one, then trigger the next\n        // qsorts\n        unsigned int mycount = lt_count + gt_count;\n\n        if (atomicAdd((unsigned int *)&atomicData->sorted_count, mycount) + mycount == len) {\n            // We're the last warp to do any sorting. Therefore it's up to us to\n            // launch the next stage.\n            unsigned int lt_len = atomicData->lt_offset;\n            unsigned int gt_len = atomicData->gt_offset;\n\n            hipStream_t lstream, rstream;\n            hipStreamCreateWithFlags(&lstream, hipStreamNonBlocking);\n            hipStreamCreateWithFlags(&rstream, hipStreamNonBlocking);\n\n            // Begin by freeing our atomicData storage. It's better for the ringbuffer\n            // algorithm\n            // if we free when we're done, rather than re-using (makes for less\n            // fragmentation).\n            ringbufFree<qsortAtomicData>(atomicDataStack, atomicData);\n\n            // Exceptional case: if \"lt_len\" is zero, then all values in the batch\n            // are equal. We are then done (may need to copy into correct buffer,\n            // though)\n            if (lt_len == 0) {\n                if (source_is_indata)\n                    hipMemcpyAsync(indata + offset,\n                                    outdata + offset,\n                                    gt_len * sizeof(unsigned),\n                                    hipMemcpyDeviceToDevice,\n                                    lstream);\n\n                return;\n            }\n\n            // Start with lower half first\n            if (lt_len > BITONICSORT_LEN) {\n                // If we've exceeded maximum depth, fall through to backup\n                // big_bitonicsort\n                if (depth >= QSORT_MAXDEPTH) {\n                    // The final bitonic stage sorts in-place in \"outdata\". We therefore\n                    // re-use \"indata\" as the out-of-range tracking buffer. For (2^n)+1\n                    // elements we need (2^(n+1)) bytes of oor buffer. The backup qsort\n                    // buffer is at least this large when sizeof(QTYPE) >= 2.\n                    big_bitonicsort<<<1, BITONICSORT_LEN, 0, lstream>>>(\n                        outdata, source_is_indata ? indata : outdata, indata, offset, lt_len);\n                }\n                else {\n                    // Launch another quicksort. We need to allocate more storage for the\n                    // atomic data.\n                    if ((atomicData = ringbufAlloc<qsortAtomicData>(atomicDataStack)) == NULL)\n                        printf(\"Stack-allocation error. Failing left child launch.\\n\");\n                    else {\n                        atomicData->lt_offset = atomicData->gt_offset = atomicData->sorted_count = 0;\n                        unsigned int numblocks =\n                            (unsigned int)(lt_len + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n                        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, lstream>>>(\n                            outdata, indata, offset, lt_len, atomicData, atomicDataStack, !source_is_indata, depth + 1);\n                    }\n                }\n            }\n            else if (lt_len > 1) {\n                // Final stage uses a bitonic sort instead. It's important to\n                // make sure the final stage ends up in the correct (original) buffer.\n                // We launch the smallest power-of-2 number of threads that we can.\n                unsigned int bitonic_len = 1 << (__qsflo(lt_len - 1U) + 1);\n                bitonicsort<<<1, bitonic_len, 0, lstream>>>(\n                    outdata, source_is_indata ? indata : outdata, offset, lt_len);\n            }\n            // Finally, if we sorted just one single element, we must still make\n            // sure that it winds up in the correct place.\n            else if (source_is_indata && (lt_len == 1))\n                indata[offset] = outdata[offset];\n\n            if (hipPeekAtLastError() != hipSuccess)\n                printf(\"Left-side launch fail: %s\\n\", hipGetErrorString(hipGetLastError()));\n\n            // Now the upper half.\n            if (gt_len > BITONICSORT_LEN) {\n                // If we've exceeded maximum depth, fall through to backup\n                // big_bitonicsort\n                if (depth >= QSORT_MAXDEPTH)\n                    big_bitonicsort<<<1, BITONICSORT_LEN, 0, rstream>>>(\n                        outdata, source_is_indata ? indata : outdata, indata, offset + lt_len, gt_len);\n                else {\n                    // Allocate new atomic storage for this launch\n                    if ((atomicData = ringbufAlloc<qsortAtomicData>(atomicDataStack)) == NULL)\n                        printf(\"Stack allocation error! Failing right-side launch.\\n\");\n                    else {\n                        atomicData->lt_offset = atomicData->gt_offset = atomicData->sorted_count = 0;\n                        unsigned int numblocks =\n                            (unsigned int)(gt_len + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n                        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, rstream>>>(outdata,\n                                                                               indata,\n                                                                               offset + lt_len,\n                                                                               gt_len,\n                                                                               atomicData,\n                                                                               atomicDataStack,\n                                                                               !source_is_indata,\n                                                                               depth + 1);\n                    }\n                }\n            }\n            else if (gt_len > 1) {\n                unsigned int bitonic_len = 1 << (__qsflo(gt_len - 1U) + 1);\n                bitonicsort<<<1, bitonic_len, 0, rstream>>>(\n                    outdata, source_is_indata ? indata : outdata, offset + lt_len, gt_len);\n            }\n            else if (source_is_indata && (gt_len == 1))\n                indata[offset + lt_len] = outdata[offset + lt_len];\n\n            if (hipPeekAtLastError() != hipSuccess)\n                printf(\"Right-side launch fail: %s\\n\", hipGetErrorString(hipGetLastError()));\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  run_quicksort\n//\n//  Host-side code to run the Kepler version of quicksort. It's pretty\n//  simple, because all launch control is handled on the device via CDP.\n//\n//  All parallel quicksorts require an equal-sized scratch buffer. This\n//  must be passed in ahead of time.\n//\n//  Returns the time elapsed for the sort.\n//\n////////////////////////////////////////////////////////////////////////////////\nfloat run_quicksort_cdp(unsigned *gpudata, unsigned *scratchdata, unsigned int count, hipStream_t stream)\n{\n    unsigned int stacksize = QSORT_STACK_ELEMS;\n\n    // This is the stack, for atomic tracking of each sort's status\n    qsortAtomicData *gpustack;\n    checkCudaErrors(hipMalloc((void **)&gpustack, stacksize * sizeof(qsortAtomicData)));\n    checkCudaErrors(hipMemset(gpustack, 0, sizeof(qsortAtomicData))); // Only need set first entry to 0\n\n    // Create the memory ringbuffer used for handling the stack.\n    // Initialise everything to where it needs to be.\n    qsortRingbuf  buf;\n    qsortRingbuf *ringbuf;\n    checkCudaErrors(hipMalloc((void **)&ringbuf, sizeof(qsortRingbuf)));\n    buf.head      = 1; // We start with one allocation\n    buf.tail      = 0;\n    buf.count     = 0;\n    buf.max       = 0;\n    buf.stacksize = stacksize;\n    buf.stackbase = gpustack;\n    checkCudaErrors(hipMemcpy(ringbuf, &buf, sizeof(buf), hipMemcpyHostToDevice));\n\n    // Timing events...\n    hipEvent_t ev1, ev2;\n    checkCudaErrors(hipEventCreate(&ev1));\n    checkCudaErrors(hipEventCreate(&ev2));\n    checkCudaErrors(hipEventRecord(ev1));\n\n    // Now we trivially launch the qsort kernel\n    if (count > BITONICSORT_LEN) {\n        unsigned int numblocks = (unsigned int)(count + (QSORT_BLOCKSIZE - 1)) >> QSORT_BLOCKSIZE_SHIFT;\n        qsort_warp<<<numblocks, QSORT_BLOCKSIZE, 0, stream>>>(\n            gpudata, scratchdata, 0U, count, gpustack, ringbuf, true, 0);\n    }\n    else {\n        bitonicsort<<<1, BITONICSORT_LEN>>>(gpudata, gpudata, 0, count);\n    }\n\n    checkCudaErrors(hipGetLastError());\n    checkCudaErrors(hipEventRecord(ev2));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    float elapse = 0.0f;\n\n    if (hipPeekAtLastError() != hipSuccess)\n        printf(\"Launch failure: %s\\n\", hipGetErrorString(hipGetLastError()));\n    else\n        checkCudaErrors(hipEventElapsedTime(&elapse, ev1, ev2));\n\n    // Sanity check that the stack allocator is doing the right thing\n    checkCudaErrors(hipMemcpy(&buf, ringbuf, sizeof(*ringbuf), hipMemcpyDeviceToHost));\n\n    if (count > BITONICSORT_LEN && buf.head != buf.tail) {\n        printf(\"Stack allocation error!\\nRingbuf:\\n\");\n        printf(\"\\t head = %u\\n\", buf.head);\n        printf(\"\\t tail = %u\\n\", buf.tail);\n        printf(\"\\tcount = %u\\n\", buf.count);\n        printf(\"\\t  max = %u\\n\", buf.max);\n    }\n\n    // Release our stack data once we're done\n    checkCudaErrors(hipFree(ringbuf));\n    checkCudaErrors(hipFree(gpustack));\n\n    return elapse;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////////\nint run_qsort(unsigned int size, int seed, int debug, int loop, int verbose)\n{\n    if (seed > 0)\n        srand(seed);\n\n    // Create and set up our test\n    unsigned *gpudata, *scratchdata;\n    checkCudaErrors(hipMalloc((void **)&gpudata, size * sizeof(unsigned)));\n    checkCudaErrors(hipMalloc((void **)&scratchdata, size * sizeof(unsigned)));\n\n    // Create CPU data.\n    unsigned    *data = new unsigned[size];\n    unsigned int min  = loop ? loop : size;\n    unsigned int max  = size;\n    loop              = (loop == 0) ? 1 : loop;\n\n    for (size = min; size <= max; size += loop) {\n        if (verbose)\n            printf(\" Input: \");\n\n        for (unsigned int i = 0; i < size; i++) {\n            // Build data 8 bits at a time\n            data[i]   = 0;\n            char *ptr = (char *)&(data[i]);\n\n            for (unsigned j = 0; j < sizeof(unsigned); j++) {\n                // Easy-to-read data in debug mode\n                if (debug) {\n                    *ptr++ = (char)(rand() % 10);\n                    break;\n                }\n\n                *ptr++ = (char)(rand() & 255);\n            }\n\n            if (verbose) {\n                if (i && !(i % 32))\n                    printf(\"\\n        \");\n\n                printf(\"%u \", data[i]);\n            }\n        }\n\n        if (verbose)\n            printf(\"\\n\");\n\n        checkCudaErrors(hipMemcpy(gpudata, data, size * sizeof(unsigned), hipMemcpyHostToDevice));\n\n        // So we're now populated and ready to go! We size our launch as\n        // blocks of up to BLOCKSIZE threads, and appropriate grid size.\n        // One thread is launched per element.\n        float elapse;\n        elapse = run_quicksort_cdp(gpudata, scratchdata, size, NULL);\n\n        // run_bitonicsort<SORTTYPE>(gpudata, scratchdata, size, verbose);\n        checkCudaErrors(hipDeviceSynchronize());\n\n        // Copy back the data and verify correct sort\n        checkCudaErrors(hipMemcpy(data, gpudata, size * sizeof(unsigned), hipMemcpyDeviceToHost));\n\n        if (verbose) {\n            printf(\"Output: \");\n\n            for (unsigned int i = 0; i < size; i++) {\n                if (i && !(i % 32))\n                    printf(\"\\n        \");\n\n                printf(\"%u \", data[i]);\n            }\n\n            printf(\"\\n\");\n        }\n\n        unsigned int check;\n\n        for (check = 1; check < size; check++) {\n            if (data[check] < data[check - 1]) {\n                printf(\"FAILED at element: %d\\n\", check);\n                break;\n            }\n        }\n\n        if (check != size) {\n            printf(\"    cdpAdvancedQuicksort FAILED\\n\");\n            exit(EXIT_FAILURE);\n        }\n        else\n            printf(\"    cdpAdvancedQuicksort PASSED\\n\");\n\n        // Display the time between event recordings\n        printf(\"Sorted %u elems in %.3f ms (%.3f Melems/sec)\\n\", size, elapse, (float)size / (elapse * 1000.0f));\n        fflush(stdout);\n    }\n\n    // Release everything and we're done\n    checkCudaErrors(hipFree(scratchdata));\n    checkCudaErrors(hipFree(gpudata));\n    delete (data);\n    return 0;\n}\n\nstatic void usage()\n{\n    printf(\"Syntax: cdpAdvancedQuicksort [-size=<num>] [-seed=<num>] [-debug] \"\n           \"[-loop-step=<num>] [-verbose]\\n\");\n    printf(\"If loop_step is non-zero, will run from 1->array_len in steps of \"\n           \"loop_step\\n\");\n}\n\n// Host side entry\nint main(int argc, char *argv[])\n{\n    int          size    = 1000000;\n    unsigned int seed    = 0;\n    int          debug   = 0;\n    int          loop    = 0;\n    int          verbose = 0;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        usage();\n        printf(\"&&&& cdpAdvancedQuicksort WAIVED\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"size\")) {\n        size = getCmdLineArgumentInt(argc, (const char **)argv, \"size\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"seed\")) {\n        seed = getCmdLineArgumentInt(argc, (const char **)argv, \"seed\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"loop-step\")) {\n        loop = getCmdLineArgumentInt(argc, (const char **)argv, \"loop-step\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"debug\")) {\n        debug = 1;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"verbose\")) {\n        verbose = 1;\n    }\n\n    // Get device properties\n    int            cuda_device = findCudaDevice(argc, (const char **)argv);\n    hipDeviceProp_t properties;\n    checkCudaErrors(hipGetDeviceProperties(&properties, cuda_device));\n    int cdpCapable = (properties.major == 3 && properties.minor >= 5) || properties.major >= 4;\n\n    printf(\"GPU device %s has compute capabilities (SM %d.%d)\\n\", properties.name, properties.major, properties.minor);\n\n    if (!cdpCapable) {\n        printf(\"cdpAdvancedQuicksort requires SM 3.5 or higher to use CUDA Dynamic \"\n               \"Parallelism.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"Running qsort on %d elements with seed %d, on %s\\n\", size, seed, properties.name);\n\n    // Increase the pending kernel launch limit\n    hipDeviceSetLimit(cudaLimitDevRuntimePendingLaunchCount, 4096);\n\n    run_qsort(size, seed, debug, loop, verbose);\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpAdvancedQuicksort/cdpAdvancedQuicksort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n *\n * This sample demonstrates implements matrix multiplication which makes use of\n * shared memory to ensure data reuse, the matrix multiplication is done using\n * tiling approach.\n * With compute capability 8.0 or higher the CUDA kernels involved uses\n * asynchronously copy data from global to shared memory; a.k.a., async-copy.\n * This sample has been written for clarity of exposition to illustrate various\n * CUDA programming principles, not with the goal of providing the most\n * performant generic kernel for matrix multiplication.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda/pipeline>\n#include <cuda_runtime.h>\n\n#if __CUDA_ARCH__ >= 700\n#include <cuda/barrier>\n#endif\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n// Helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\nenum kernels {\n    AsyncCopyMultiStageLargeChunk  = 0,\n    AsyncCopyLargeChunk            = 1,\n    AsyncCopyLargeChunkAWBarrier   = 2,\n    AsyncCopyMultiStageSharedState = 3,\n    AsyncCopyMultiStage            = 4,\n    AsyncCopySingleStage           = 5,\n    Naive                          = 6,\n    NaiveLargeChunk                = 7\n};\n\nconst char *kernelNames[] = {\"AsyncCopyMultiStageLargeChunk\",\n                             \"AsyncCopyLargeChunk\",\n                             \"AsyncCopyLargeChunkAWBarrier\",\n                             \"AsyncCopyMultiStageSharedState\",\n                             \"AsyncCopyMultiStage\",\n                             \"AsyncCopySingleStage\",\n                             \"Naive\",\n                             \"NaiveLargeChunk\"};\n\nconstexpr int blockSize = 16;\n\n// Multi Stage memcpy_async pipeline with large chunk copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyMultiStageLargeChunk(float *__restrict__ C,\n                                                       const float *__restrict__ A,\n                                                       const float *__restrict__ B,\n                                                       int wA,\n                                                       int wB)\n{\n    // Requires BLOCK_SIZE % 4 == 0\n\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ alignas(alignof(float4)) float As[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ alignas(alignof(float4)) float Bs[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * (BLOCK_SIZE)*blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    const int  t4x    = threadIdx.x * 4;\n    const auto shape4 = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe = cuda::make_pipeline();\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n        for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n            pipe.producer_acquire();\n            if (aStage <= aEnd && t4x < BLOCK_SIZE) {\n                // Rotating buffer\n                const int j = iStage % maxPipelineStages;\n                cuda::memcpy_async(&As[j][threadIdx.y][t4x], &A[aStage + wA * threadIdx.y + t4x], shape4, pipe);\n                cuda::memcpy_async(&Bs[j][threadIdx.y][t4x], &B[aStage + wA * threadIdx.y + t4x], shape4, pipe);\n            }\n            pipe.producer_commit();\n        }\n\n        pipe.consumer_wait();\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Rotating buffer\n        const int j = i % maxPipelineStages;\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n        }\n        pipe.consumer_release();\n\n        // Don't have to synchronize because maxPipelineStages is greater than one\n        // therefore next iteration is loading to a different buffer.\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Single Stage memcpy_async pipeline with Large copy chunk (float4)\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyLargeChunk(float *__restrict__ C,\n                                             const float *__restrict__ A,\n                                             const float *__restrict__ B,\n                                             int wA,\n                                             int wB)\n{\n    // Requires BLOCK_SIZE % 4 == 0\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Single-stage pipeline version\n    float Csub = 0.0;\n\n    const int                                 t4x    = threadIdx.x * 4;\n    const auto                                shape4 = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory;\n        // a subset of threads loads a contiguous chunk of elements.\n\n        // Previously, per-thread:\n        // As[ty][tx] = A[a + wA * ty + tx];\n        // Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Now, one fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&As[threadIdx.y][t4x], &A[a + wA * threadIdx.y + t4x], shape4, pipe);\n            cuda::memcpy_async(&Bs[threadIdx.y][t4x], &B[a + wA * threadIdx.y + t4x], shape4, pipe);\n\n            pipe.producer_commit();\n            pipe.consumer_wait();\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        pipe.consumer_release();\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Single Stage memcpy_async pipeline with Large copy chunk (float4) using\n// arrive-wait barrier\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyLargeChunkAWBarrier(float *__restrict__ C,\n                                                      const float *__restrict__ A,\n                                                      const float *__restrict__ B,\n                                                      int wA,\n                                                      int wB)\n{\n#if __CUDA_ARCH__ >= 700\n#pragma diag_suppress static_var_with_dynamic_init\n    // Requires BLOCK_SIZE % 4 == 0\n\n    __shared__ cuda::barrier<cuda::thread_scope_block> bar;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    if (threadIdx.x == 0) {\n        init(&bar, blockDim.x * blockDim.y);\n    }\n    __syncthreads();\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    float Csub = 0.0;\n\n    const int t4x = threadIdx.x * 4;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory;\n        // a subset of threads loads a contiguous chunk of elements.\n\n        // Now, one fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            float4 *const       A4s = reinterpret_cast<float4 *>(&As[threadIdx.y][t4x]);\n            float4 *const       B4s = reinterpret_cast<float4 *>(&Bs[threadIdx.y][t4x]);\n            const float4 *const A4  = reinterpret_cast<const float4 *>(&A[a + wA * threadIdx.y + t4x]);\n            const float4 *const B4  = reinterpret_cast<const float4 *>(&B[a + wA * threadIdx.y + t4x]);\n\n            cuda::memcpy_async(A4s, A4, sizeof(float4), bar);\n            cuda::memcpy_async(B4s, B4, sizeof(float4), bar);\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        bar.arrive_and_wait();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        bar.arrive_and_wait();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n#endif\n}\n\n// Single Stage memcpy_async pipeline with float copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopySingleStage(float *C, const float *A, const float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Single-stage pipeline version\n    float Csub = 0.0;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n    const auto                                shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n        {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&As[threadIdx.y][threadIdx.x], &A[a + wA * threadIdx.y + threadIdx.x], shape1, pipe);\n            cuda::memcpy_async(&Bs[threadIdx.y][threadIdx.x], &B[b + wB * threadIdx.y + threadIdx.x], shape1, pipe);\n\n            pipe.producer_commit();\n        }\n\n        pipe.consumer_wait();\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Multi Stage memcpy_async thread_scope_thread pipeline with single-element\n// async-copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyMultiStage(float *__restrict__ C,\n                                             const float *__restrict__ A,\n                                             const float *__restrict__ B,\n                                             int wA,\n                                             int wB)\n{\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ float As[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ float Bs[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n    const auto                                shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n\n        for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n            if (aStage <= aEnd) {\n                // Rotating buffer\n                const int j = iStage % maxPipelineStages;\n\n                pipe.producer_acquire();\n\n                cuda::memcpy_async(\n                    &As[j][threadIdx.y][threadIdx.x], &A[aStage + wA * threadIdx.y + threadIdx.x], shape1, pipe);\n                cuda::memcpy_async(\n                    &Bs[j][threadIdx.y][threadIdx.x], &B[bStage + wB * threadIdx.y + threadIdx.x], shape1, pipe);\n\n                pipe.producer_commit();\n            }\n        }\n        pipe.consumer_wait();\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        const int j = i % maxPipelineStages;\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n        }\n\n        pipe.consumer_release();\n        // Don't have to synchronize because maxPipelineStages is greater than one\n        // therefore next iteration is loading to a different buffer.\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Multi Stage shared state memcpy_async pipeline thread_scope_block\n// with parititioned producer & consumer, here we've 1 warp as producer\n// group which issues memcpy_async operations and rest all warps are part of\n// consumer group which perform gemm computation on the loaded matrices by\n// producer.\ntemplate <int BLOCK_SIZE_X>\n__global__ void MatrixMulAsyncCopyMultiStageSharedState(float *__restrict__ C,\n                                                        const float *__restrict__ A,\n                                                        const float *__restrict__ B,\n                                                        int wA,\n                                                        int wB)\n{\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ float As[maxPipelineStages][BLOCK_SIZE_X][BLOCK_SIZE_X];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ float Bs[maxPipelineStages][BLOCK_SIZE_X][BLOCK_SIZE_X];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * BLOCK_SIZE_X * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    constexpr int aStep = BLOCK_SIZE_X;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE_X * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE_X * wB;\n\n    auto cta = cg::this_thread_block();\n\n    const auto shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n    __shared__ cuda::pipeline_shared_state<cuda::thread_scope_block, maxPipelineStages> shared_state;\n    constexpr int consumer_row_count = BLOCK_SIZE_X;\n\n    const auto thread_role =\n        (cta.thread_index().y < consumer_row_count) ? cuda::pipeline_role::consumer : cuda::pipeline_role::producer;\n    auto pipe = cuda::make_pipeline(cta, &shared_state, thread_role);\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        if (threadIdx.y >= consumer_row_count) {\n            // this is a whole producer warp because threadIdx.y >= 16 where 16 ==\n            // consumer_row_count,\n            // which loads the matrices from device memory to shared memory;\n            for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n                if (aStage <= aEnd) {\n                    // Rotating buffer\n                    const int j          = iStage % maxPipelineStages;\n                    const int strideRows = (blockDim.y - consumer_row_count);\n                    pipe.producer_acquire();\n                    for (int rowId = threadIdx.y - consumer_row_count; rowId < BLOCK_SIZE_X; rowId += strideRows) {\n                        cuda::memcpy_async(\n                            &As[j][rowId][threadIdx.x], &A[aStage + wA * rowId + threadIdx.x], shape1, pipe);\n                        cuda::memcpy_async(\n                            &Bs[j][rowId][threadIdx.x], &B[bStage + wB * rowId + threadIdx.x], shape1, pipe);\n                    }\n                    pipe.producer_commit();\n                }\n            }\n        }\n        else {\n            // this is a whole set of consumer group because threadIdx.y <\n            // consumer_row_count where consumer_row_count == 16,\n            // which computes gemm operation on matrices loaded in shared memory by\n            // producer warp.\n            const int j = i % maxPipelineStages;\n            // Synchronize consumer group to make sure the matrices are loaded by\n            // producer group.\n            pipe.consumer_wait();\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n            for (int k = 0; k < BLOCK_SIZE_X; ++k) {\n                Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n            }\n            pipe.consumer_release();\n        }\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    if (threadIdx.y < consumer_row_count) {\n        const int c                           = wB * BLOCK_SIZE_X * blockIdx.y + BLOCK_SIZE_X * blockIdx.x;\n        C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n    }\n}\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulNaive(float *C, float *A, float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[threadIdx.y][threadIdx.x] = A[a + wA * threadIdx.y + threadIdx.x];\n        Bs[threadIdx.y][threadIdx.x] = B[b + wB * threadIdx.y + threadIdx.x];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulNaiveLargeChunk(float *C, float *A, float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    int t4x = threadIdx.x * 4;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory\n        // to shared memory;\n\n        // One fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            float4 *const       A4s = reinterpret_cast<float4 *>(&As[threadIdx.y][t4x]);\n            float4 *const       B4s = reinterpret_cast<float4 *>(&Bs[threadIdx.y][t4x]);\n            const float4 *const A4  = reinterpret_cast<float4 *>(&A[a + wA * threadIdx.y + t4x]);\n            const float4 *const B4  = reinterpret_cast<float4 *>(&B[a + wA * threadIdx.y + t4x]);\n            *A4s                    = *A4;\n            *B4s                    = *B4;\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\nvoid ConstantInit(float *data, int size, float val)\n{\n    for (int i = 0; i < size; ++i) {\n        data[i] = val;\n    }\n}\n\n/**\n * Run matrix multiplication using CUDA\n */\nint MatrixMultiply(int argc, char **argv, const dim3 &dimsA, const dim3 &dimsB, kernels kernel_number)\n{\n    // Allocate host memory for matrices A and B\n    unsigned int size_A     = dimsA.x * dimsA.y;\n    unsigned int mem_size_A = sizeof(float) * size_A;\n    float       *h_A;\n    checkCudaErrors(cudaMallocHost(&h_A, mem_size_A));\n    unsigned int size_B     = dimsB.x * dimsB.y;\n    unsigned int mem_size_B = sizeof(float) * size_B;\n    float       *h_B;\n    checkCudaErrors(cudaMallocHost(&h_B, mem_size_B));\n    cudaStream_t stream;\n\n    // Initialize host memory\n    const float valB = 2.10f;\n    ConstantInit(h_A, size_A, 1.0f);\n    ConstantInit(h_B, size_B, valB);\n\n    // Allocate device memory\n    float *d_A, *d_B, *d_C;\n\n    // Allocate host matrix C\n    dim3         dimsC(dimsB.x, dimsA.y, 1);\n    unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);\n    float       *h_C;\n    checkCudaErrors(cudaMallocHost(&h_C, mem_size_C));\n\n    if (h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host matrix C!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_A), mem_size_A));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_B), mem_size_B));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_C), mem_size_C));\n    // Allocate CUDA events that we'll use for timing\n    cudaEvent_t start, stop;\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // copy host memory to device\n    checkCudaErrors(cudaMemcpyAsync(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemcpyAsync(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemsetAsync(d_C, 0, mem_size_C, stream));\n\n    // Setup execution parameters\n    dim3 threads(blockSize, blockSize);\n    dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);\n\n    // Here the block size is 16x18, where first 16 rows are consumer thread group\n    // and last 2 rows (1 warp) is producer thread group\n    dim3 threadsSharedStateKernel(blockSize, blockSize + 2, 1);\n    dim3 gridSharedStateKernel(dimsB.x / threadsSharedStateKernel.x, dimsA.y / threadsSharedStateKernel.x);\n\n    printf(\"Running kernel = %d - %s\\n\", kernel_number, kernelNames[kernel_number]);\n    // Create and start timer\n    printf(\"Computing result using CUDA Kernel...\\n\");\n\n    // Performs warmup operation using matrixMul CUDA kernel\n    switch (kernel_number) {\n    case AsyncCopyMultiStageLargeChunk:\n    default:\n        MatrixMulAsyncCopyMultiStageLargeChunk<blockSize>\n            <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyLargeChunk:\n        MatrixMulAsyncCopyLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyLargeChunkAWBarrier:\n        MatrixMulAsyncCopyLargeChunkAWBarrier<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyMultiStageSharedState:\n        MatrixMulAsyncCopyMultiStageSharedState<blockSize>\n            <<<gridSharedStateKernel, threadsSharedStateKernel, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyMultiStage:\n        MatrixMulAsyncCopyMultiStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopySingleStage:\n        MatrixMulAsyncCopySingleStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case Naive:\n        MatrixMulNaive<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case NaiveLargeChunk:\n        MatrixMulNaiveLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    }\n\n    printf(\"done\\n\");\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    // Execute the kernel\n    int nIter = 100;\n\n    // Record the start event\n    checkCudaErrors(cudaEventRecord(start, stream));\n\n    for (int j = 0; j < nIter; j++) {\n        switch (kernel_number) {\n        case AsyncCopyMultiStageLargeChunk:\n        default:\n            MatrixMulAsyncCopyMultiStageLargeChunk<blockSize>\n                <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyLargeChunk:\n            MatrixMulAsyncCopyLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyLargeChunkAWBarrier:\n            MatrixMulAsyncCopyLargeChunkAWBarrier<blockSize>\n                <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyMultiStageSharedState:\n            MatrixMulAsyncCopyMultiStageSharedState<blockSize>\n                <<<gridSharedStateKernel, threadsSharedStateKernel, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyMultiStage:\n            MatrixMulAsyncCopyMultiStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopySingleStage:\n            MatrixMulAsyncCopySingleStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case Naive:\n            MatrixMulNaive<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case NaiveLargeChunk:\n            MatrixMulNaiveLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        }\n    }\n\n    // Record the stop event\n    checkCudaErrors(cudaEventRecord(stop, stream));\n\n    // Wait for the stop event to complete\n    checkCudaErrors(cudaEventSynchronize(stop));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(cudaEventElapsedTime(&msecTotal, start, stop));\n\n    // Compute and print the performance\n    float  msecPerMatrixMul = msecTotal / nIter;\n    double flopsPerMatrixMul =\n        2.0 * static_cast<double>(dimsA.x) * static_cast<double>(dimsA.y) * static_cast<double>(dimsB.x);\n    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);\n    printf(\"Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops,\"\n           \" WorkgroupSize= %u threads/block\\n\",\n           gigaFlops,\n           msecPerMatrixMul,\n           flopsPerMatrixMul,\n           threads.x * threads.y);\n\n    // Copy result from device to host\n    checkCudaErrors(cudaMemcpyAsync(h_C, d_C, mem_size_C, cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    printf(\"Checking computed result for correctness: \");\n    bool correct = true;\n\n    // test relative error by the formula\n    // |<x, y>_cpu - <x,y>_gpu|/<|x|, |y|>  < eps\n    double eps = 1.e-6; // machine zero\n\n    for (int i = 0; i < static_cast<int>(dimsC.x * dimsC.y); i++) {\n        double abs_err    = fabs(h_C[i] - (dimsA.x * valB));\n        double dot_length = dimsA.x;\n        double abs_val    = fabs(h_C[i]);\n        double rel_err    = abs_err / abs_val / dot_length;\n\n        if (rel_err > eps) {\n            printf(\"Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\\n\", i, h_C[i], dimsA.x * valB, eps);\n            correct = false;\n        }\n    }\n\n    printf(\"%s\\n\", correct ? \"Result = PASS\" : \"Result = FAIL\");\n\n    // Clean up memory\n    checkCudaErrors(cudaFreeHost(h_A));\n    checkCudaErrors(cudaFreeHost(h_B));\n    checkCudaErrors(cudaFreeHost(h_C));\n    checkCudaErrors(cudaFree(d_A));\n    checkCudaErrors(cudaFree(d_B));\n    checkCudaErrors(cudaFree(d_C));\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance \"\n           \"measurements. Results may vary when GPU Boost is enabled.\\n\");\n\n    if (correct) {\n        return EXIT_SUCCESS;\n    }\n    else {\n        return EXIT_FAILURE;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"[globalToShmemAsyncCopy] - Starting...\\n\");\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        printf(\"      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\\n\");\n        printf(\"      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\\n\");\n        printf(\"      -kernel=kernel_number (0 - AsyncCopyMultiStageLargeChunk; 1 - \"\n               \"AsyncCopyLargeChunk)\\n\");\n        printf(\"                            (2 - AsyncCopyLargeChunkAWBarrier; 3 - \"\n               \"AsyncCopyMultiStageSharedState)\\n\");\n        printf(\"                            (4 - AsyncCopyMultiStage; 5 - \"\n               \"AsyncCopySingleStage; 6 - Naive without memcpy_async)\\n\");\n        printf(\"                            (7 - NaiveLargeChunk without \"\n               \"memcpy_async)\\n\");\n        printf(\"  Note: Outer matrix dimensions of A & B matrices must be equal.\\n\");\n\n        exit(EXIT_SUCCESS);\n    }\n\n    // This will pick the best possible CUDA capable device, otherwise\n    // override the device ID based on input provided at the command line\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int  matrixBlock = 32;\n    dim3 dimsA(10 * 4 * matrixBlock, 10 * 4 * matrixBlock, 1);\n    dim3 dimsB(10 * 4 * matrixBlock, 10 * 4 * matrixBlock, 1);\n\n    // width of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wA\")) {\n        dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wA\");\n    }\n\n    // height of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hA\")) {\n        dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hA\");\n    }\n\n    // width of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wB\")) {\n        dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wB\");\n    }\n\n    // height of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hB\")) {\n        dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hB\");\n    }\n\n    if (dimsA.x != dimsB.y) {\n        printf(\"Error: outer matrix dimensions must be equal. (%d != %d)\\n\", dimsA.x, dimsB.y);\n        exit(EXIT_FAILURE);\n    }\n\n    kernels selected_kernel = AsyncCopyMultiStageLargeChunk;\n\n    // kernel to run - default (AsyncCopyMultiStageLargeChunk == 0)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 8) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 6, you have entered \"\n                   \"%d\\n\",\n                   kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    int major = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev));\n    if (major < 7) {\n        printf(\"globalToShmemAsyncCopy requires SM 7.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"MatrixA(%d,%d), MatrixB(%d,%d)\\n\", dimsA.x, dimsA.y, dimsB.x, dimsB.y);\n\n    int matrix_result = MatrixMultiply(argc, argv, dimsA, dimsB, selected_kernel);\n\n    exit(matrix_result);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Matrix multiplication: C = A * B.\n *\n * This sample demonstrates implements matrix multiplication which makes use of\n * shared memory to ensure data reuse, the matrix multiplication is done using\n * tiling approach.\n * With compute capability 8.0 or higher the CUDA kernels involved uses\n * asynchronously copy data from global to shared memory; a.k.a., async-copy.\n * This sample has been written for clarity of exposition to illustrate various\n * CUDA programming principles, not with the goal of providing the most\n * performant generic kernel for matrix multiplication.\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda/pipeline>\n#include <hip/hip_runtime.h>\n\n#if __CUDA_ARCH__ >= 700\n#include <cuda/barrier>\n#endif\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n// Helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\nenum kernels {\n    AsyncCopyMultiStageLargeChunk  = 0,\n    AsyncCopyLargeChunk            = 1,\n    AsyncCopyLargeChunkAWBarrier   = 2,\n    AsyncCopyMultiStageSharedState = 3,\n    AsyncCopyMultiStage            = 4,\n    AsyncCopySingleStage           = 5,\n    Naive                          = 6,\n    NaiveLargeChunk                = 7\n};\n\nconst char *kernelNames[] = {\"AsyncCopyMultiStageLargeChunk\",\n                             \"AsyncCopyLargeChunk\",\n                             \"AsyncCopyLargeChunkAWBarrier\",\n                             \"AsyncCopyMultiStageSharedState\",\n                             \"AsyncCopyMultiStage\",\n                             \"AsyncCopySingleStage\",\n                             \"Naive\",\n                             \"NaiveLargeChunk\"};\n\nconstexpr int blockSize = 16;\n\n// Multi Stage memcpy_async pipeline with large chunk copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyMultiStageLargeChunk(float *__restrict__ C,\n                                                       const float *__restrict__ A,\n                                                       const float *__restrict__ B,\n                                                       int wA,\n                                                       int wB)\n{\n    // Requires BLOCK_SIZE % 4 == 0\n\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ alignas(alignof(float4)) float As[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ alignas(alignof(float4)) float Bs[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * (BLOCK_SIZE)*blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    const int  t4x    = threadIdx.x * 4;\n    const auto shape4 = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe = cuda::make_pipeline();\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n        for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n            pipe.producer_acquire();\n            if (aStage <= aEnd && t4x < BLOCK_SIZE) {\n                // Rotating buffer\n                const int j = iStage % maxPipelineStages;\n                cuda::memcpy_async(&As[j][threadIdx.y][t4x], &A[aStage + wA * threadIdx.y + t4x], shape4, pipe);\n                cuda::memcpy_async(&Bs[j][threadIdx.y][t4x], &B[aStage + wA * threadIdx.y + t4x], shape4, pipe);\n            }\n            pipe.producer_commit();\n        }\n\n        pipe.consumer_wait();\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Rotating buffer\n        const int j = i % maxPipelineStages;\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n        }\n        pipe.consumer_release();\n\n        // Don't have to synchronize because maxPipelineStages is greater than one\n        // therefore next iteration is loading to a different buffer.\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Single Stage memcpy_async pipeline with Large copy chunk (float4)\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyLargeChunk(float *__restrict__ C,\n                                             const float *__restrict__ A,\n                                             const float *__restrict__ B,\n                                             int wA,\n                                             int wB)\n{\n    // Requires BLOCK_SIZE % 4 == 0\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Single-stage pipeline version\n    float Csub = 0.0;\n\n    const int                                 t4x    = threadIdx.x * 4;\n    const auto                                shape4 = cuda::aligned_size_t<alignof(float4)>(sizeof(float4));\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory;\n        // a subset of threads loads a contiguous chunk of elements.\n\n        // Previously, per-thread:\n        // As[ty][tx] = A[a + wA * ty + tx];\n        // Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Now, one fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&As[threadIdx.y][t4x], &A[a + wA * threadIdx.y + t4x], shape4, pipe);\n            cuda::memcpy_async(&Bs[threadIdx.y][t4x], &B[a + wA * threadIdx.y + t4x], shape4, pipe);\n\n            pipe.producer_commit();\n            pipe.consumer_wait();\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        pipe.consumer_release();\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Single Stage memcpy_async pipeline with Large copy chunk (float4) using\n// arrive-wait barrier\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyLargeChunkAWBarrier(float *__restrict__ C,\n                                                      const float *__restrict__ A,\n                                                      const float *__restrict__ B,\n                                                      int wA,\n                                                      int wB)\n{\n#if __CUDA_ARCH__ >= 700\n#pragma diag_suppress static_var_with_dynamic_init\n    // Requires BLOCK_SIZE % 4 == 0\n\n    __shared__ cuda::barrier<cuda::thread_scope_block> bar;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    if (threadIdx.x == 0) {\n        init(&bar, blockDim.x * blockDim.y);\n    }\n    __syncthreads();\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    float Csub = 0.0;\n\n    const int t4x = threadIdx.x * 4;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory;\n        // a subset of threads loads a contiguous chunk of elements.\n\n        // Now, one fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            float4 *const       A4s = reinterpret_cast<float4 *>(&As[threadIdx.y][t4x]);\n            float4 *const       B4s = reinterpret_cast<float4 *>(&Bs[threadIdx.y][t4x]);\n            const float4 *const A4  = reinterpret_cast<const float4 *>(&A[a + wA * threadIdx.y + t4x]);\n            const float4 *const B4  = reinterpret_cast<const float4 *>(&B[a + wA * threadIdx.y + t4x]);\n\n            cuda::memcpy_async(A4s, A4, sizeof(float4), bar);\n            cuda::memcpy_async(B4s, B4, sizeof(float4), bar);\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        bar.arrive_and_wait();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        bar.arrive_and_wait();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n#endif\n}\n\n// Single Stage memcpy_async pipeline with float copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopySingleStage(float *C, const float *A, const float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Single-stage pipeline version\n    float Csub = 0.0;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n    const auto                                shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n        {\n            pipe.producer_acquire();\n\n            cuda::memcpy_async(&As[threadIdx.y][threadIdx.x], &A[a + wA * threadIdx.y + threadIdx.x], shape1, pipe);\n            cuda::memcpy_async(&Bs[threadIdx.y][threadIdx.x], &B[b + wB * threadIdx.y + threadIdx.x], shape1, pipe);\n\n            pipe.producer_commit();\n        }\n\n        pipe.consumer_wait();\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before overwriting the\n        // shared memory sub-matrix buffers As and Bs in the next iteration.\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Multi Stage memcpy_async thread_scope_thread pipeline with single-element\n// async-copy\ntemplate <int BLOCK_SIZE>\n__global__ void MatrixMulAsyncCopyMultiStage(float *__restrict__ C,\n                                             const float *__restrict__ A,\n                                             const float *__restrict__ B,\n                                             int wA,\n                                             int wB)\n{\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ float As[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ float Bs[maxPipelineStages][BLOCK_SIZE][BLOCK_SIZE];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    cuda::pipeline<cuda::thread_scope_thread> pipe   = cuda::make_pipeline();\n    const auto                                shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        // Load the matrices from device memory to shared memory; each thread loads\n        // one element of each matrix\n\n        for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n            if (aStage <= aEnd) {\n                // Rotating buffer\n                const int j = iStage % maxPipelineStages;\n\n                pipe.producer_acquire();\n\n                cuda::memcpy_async(\n                    &As[j][threadIdx.y][threadIdx.x], &A[aStage + wA * threadIdx.y + threadIdx.x], shape1, pipe);\n                cuda::memcpy_async(\n                    &Bs[j][threadIdx.y][threadIdx.x], &B[bStage + wB * threadIdx.y + threadIdx.x], shape1, pipe);\n\n                pipe.producer_commit();\n            }\n        }\n        pipe.consumer_wait();\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        const int j = i % maxPipelineStages;\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n        }\n\n        pipe.consumer_release();\n        // Don't have to synchronize because maxPipelineStages is greater than one\n        // therefore next iteration is loading to a different buffer.\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\n// Multi Stage shared state memcpy_async pipeline thread_scope_block\n// with parititioned producer & consumer, here we've 1 warp as producer\n// group which issues memcpy_async operations and rest all warps are part of\n// consumer group which perform gemm computation on the loaded matrices by\n// producer.\ntemplate <int BLOCK_SIZE_X>\n__global__ void MatrixMulAsyncCopyMultiStageSharedState(float *__restrict__ C,\n                                                        const float *__restrict__ A,\n                                                        const float *__restrict__ B,\n                                                        int wA,\n                                                        int wB)\n{\n    // Multi-stage pipeline version\n    constexpr size_t maxPipelineStages = 4;\n\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A for each stage\n    __shared__ float As[maxPipelineStages][BLOCK_SIZE_X][BLOCK_SIZE_X];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B for each stage\n    __shared__ float Bs[maxPipelineStages][BLOCK_SIZE_X][BLOCK_SIZE_X];\n\n    float Csub = 0.0;\n\n    // Index of the first sub-matrix of A processed by the block\n    const int aBegin = wA * BLOCK_SIZE_X * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    const int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    constexpr int aStep = BLOCK_SIZE_X;\n\n    // Index of the first sub-matrix of B processed by the block\n    const int bBegin = BLOCK_SIZE_X * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE_X * wB;\n\n    auto cta = cg::this_thread_block();\n\n    const auto shape1 = cuda::aligned_size_t<alignof(float)>(sizeof(float));\n    __shared__ cuda::pipeline_shared_state<cuda::thread_scope_block, maxPipelineStages> shared_state;\n    constexpr int consumer_row_count = BLOCK_SIZE_X;\n\n    const auto thread_role =\n        (cta.thread_index().y < consumer_row_count) ? cuda::pipeline_role::consumer : cuda::pipeline_role::producer;\n    auto pipe = cuda::make_pipeline(cta, &shared_state, thread_role);\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin, i = 0, aStage = aBegin, bStage = bBegin, iStage = 0; a <= aEnd;\n         a += aStep, b += bStep, ++i) {\n        if (threadIdx.y >= consumer_row_count) {\n            // this is a whole producer warp because threadIdx.y >= 16 where 16 ==\n            // consumer_row_count,\n            // which loads the matrices from device memory to shared memory;\n            for (; aStage <= a + aStep * maxPipelineStages; aStage += aStep, bStage += bStep, ++iStage) {\n                if (aStage <= aEnd) {\n                    // Rotating buffer\n                    const int j          = iStage % maxPipelineStages;\n                    const int strideRows = (blockDim.y - consumer_row_count);\n                    pipe.producer_acquire();\n                    for (int rowId = threadIdx.y - consumer_row_count; rowId < BLOCK_SIZE_X; rowId += strideRows) {\n                        cuda::memcpy_async(\n                            &As[j][rowId][threadIdx.x], &A[aStage + wA * rowId + threadIdx.x], shape1, pipe);\n                        cuda::memcpy_async(\n                            &Bs[j][rowId][threadIdx.x], &B[bStage + wB * rowId + threadIdx.x], shape1, pipe);\n                    }\n                    pipe.producer_commit();\n                }\n            }\n        }\n        else {\n            // this is a whole set of consumer group because threadIdx.y <\n            // consumer_row_count where consumer_row_count == 16,\n            // which computes gemm operation on matrices loaded in shared memory by\n            // producer warp.\n            const int j = i % maxPipelineStages;\n            // Synchronize consumer group to make sure the matrices are loaded by\n            // producer group.\n            pipe.consumer_wait();\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n            for (int k = 0; k < BLOCK_SIZE_X; ++k) {\n                Csub += As[j][threadIdx.y][k] * Bs[j][k][threadIdx.x];\n            }\n            pipe.consumer_release();\n        }\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes four element\n    if (threadIdx.y < consumer_row_count) {\n        const int c                           = wB * BLOCK_SIZE_X * blockIdx.y + BLOCK_SIZE_X * blockIdx.x;\n        C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n    }\n}\n\n/**\n * Matrix multiplication (CUDA Kernel) on the device: C = A * B\n * wA is A's width and wB is B's width\n */\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulNaive(float *C, float *A, float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[threadIdx.y][threadIdx.x] = A[a + wA * threadIdx.y + threadIdx.x];\n        Bs[threadIdx.y][threadIdx.x] = B[b + wB * threadIdx.y + threadIdx.x];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\ntemplate <int BLOCK_SIZE> __global__ void MatrixMulNaiveLargeChunk(float *C, float *A, float *B, int wA, int wB)\n{\n    // Declaration of the shared memory array As used to\n    // store the sub-matrix of A\n    __shared__ alignas(alignof(float4)) float As[BLOCK_SIZE][BLOCK_SIZE];\n\n    // Declaration of the shared memory array Bs used to\n    // store the sub-matrix of B\n    __shared__ alignas(alignof(float4)) float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n    int t4x = threadIdx.x * 4;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = wA * BLOCK_SIZE * blockIdx.y;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * blockIdx.x;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Load the matrices from device memory\n        // to shared memory;\n\n        // One fourth of the threads load four elements of each matrix\n        if (t4x < BLOCK_SIZE) {\n            float4 *const       A4s = reinterpret_cast<float4 *>(&As[threadIdx.y][t4x]);\n            float4 *const       B4s = reinterpret_cast<float4 *>(&Bs[threadIdx.y][t4x]);\n            const float4 *const A4  = reinterpret_cast<float4 *>(&A[a + wA * threadIdx.y + t4x]);\n            const float4 *const B4  = reinterpret_cast<float4 *>(&B[a + wA * threadIdx.y + t4x]);\n            *A4s                    = *A4;\n            *B4s                    = *B4;\n        }\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n// Multiply the two matrices together;\n// each thread computes one element\n// of the block sub-matrix\n#pragma unroll\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[threadIdx.y][k] * Bs[k][threadIdx.x];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c                                 = wB * BLOCK_SIZE * blockIdx.y + BLOCK_SIZE * blockIdx.x;\n    C[c + wB * threadIdx.y + threadIdx.x] = Csub;\n}\n\nvoid ConstantInit(float *data, int size, float val)\n{\n    for (int i = 0; i < size; ++i) {\n        data[i] = val;\n    }\n}\n\n/**\n * Run matrix multiplication using CUDA\n */\nint MatrixMultiply(int argc, char **argv, const dim3 &dimsA, const dim3 &dimsB, kernels kernel_number)\n{\n    // Allocate host memory for matrices A and B\n    unsigned int size_A     = dimsA.x * dimsA.y;\n    unsigned int mem_size_A = sizeof(float) * size_A;\n    float       *h_A;\n    checkCudaErrors(hipHostMalloc(&h_A, mem_size_A));\n    unsigned int size_B     = dimsB.x * dimsB.y;\n    unsigned int mem_size_B = sizeof(float) * size_B;\n    float       *h_B;\n    checkCudaErrors(hipHostMalloc(&h_B, mem_size_B));\n    hipStream_t stream;\n\n    // Initialize host memory\n    const float valB = 2.10f;\n    ConstantInit(h_A, size_A, 1.0f);\n    ConstantInit(h_B, size_B, valB);\n\n    // Allocate device memory\n    float *d_A, *d_B, *d_C;\n\n    // Allocate host matrix C\n    dim3         dimsC(dimsB.x, dimsA.y, 1);\n    unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);\n    float       *h_C;\n    checkCudaErrors(hipHostMalloc(&h_C, mem_size_C));\n\n    if (h_C == NULL) {\n        fprintf(stderr, \"Failed to allocate host matrix C!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_A), mem_size_A));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_B), mem_size_B));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_C), mem_size_C));\n    // Allocate CUDA events that we'll use for timing\n    hipEvent_t start, stop;\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // copy host memory to device\n    checkCudaErrors(hipMemcpyAsync(d_A, h_A, mem_size_A, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemcpyAsync(d_B, h_B, mem_size_B, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemsetAsync(d_C, 0, mem_size_C, stream));\n\n    // Setup execution parameters\n    dim3 threads(blockSize, blockSize);\n    dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);\n\n    // Here the block size is 16x18, where first 16 rows are consumer thread group\n    // and last 2 rows (1 warp) is producer thread group\n    dim3 threadsSharedStateKernel(blockSize, blockSize + 2, 1);\n    dim3 gridSharedStateKernel(dimsB.x / threadsSharedStateKernel.x, dimsA.y / threadsSharedStateKernel.x);\n\n    printf(\"Running kernel = %d - %s\\n\", kernel_number, kernelNames[kernel_number]);\n    // Create and start timer\n    printf(\"Computing result using CUDA Kernel...\\n\");\n\n    // Performs warmup operation using matrixMul CUDA kernel\n    switch (kernel_number) {\n    case AsyncCopyMultiStageLargeChunk:\n    default:\n        MatrixMulAsyncCopyMultiStageLargeChunk<blockSize>\n            <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyLargeChunk:\n        MatrixMulAsyncCopyLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyLargeChunkAWBarrier:\n        MatrixMulAsyncCopyLargeChunkAWBarrier<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyMultiStageSharedState:\n        MatrixMulAsyncCopyMultiStageSharedState<blockSize>\n            <<<gridSharedStateKernel, threadsSharedStateKernel, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopyMultiStage:\n        MatrixMulAsyncCopyMultiStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case AsyncCopySingleStage:\n        MatrixMulAsyncCopySingleStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case Naive:\n        MatrixMulNaive<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    case NaiveLargeChunk:\n        MatrixMulNaiveLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n        break;\n    }\n\n    printf(\"done\\n\");\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    // Execute the kernel\n    int nIter = 100;\n\n    // Record the start event\n    checkCudaErrors(hipEventRecord(start, stream));\n\n    for (int j = 0; j < nIter; j++) {\n        switch (kernel_number) {\n        case AsyncCopyMultiStageLargeChunk:\n        default:\n            MatrixMulAsyncCopyMultiStageLargeChunk<blockSize>\n                <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyLargeChunk:\n            MatrixMulAsyncCopyLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyLargeChunkAWBarrier:\n            MatrixMulAsyncCopyLargeChunkAWBarrier<blockSize>\n                <<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyMultiStageSharedState:\n            MatrixMulAsyncCopyMultiStageSharedState<blockSize>\n                <<<gridSharedStateKernel, threadsSharedStateKernel, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopyMultiStage:\n            MatrixMulAsyncCopyMultiStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case AsyncCopySingleStage:\n            MatrixMulAsyncCopySingleStage<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case Naive:\n            MatrixMulNaive<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        case NaiveLargeChunk:\n            MatrixMulNaiveLargeChunk<blockSize><<<grid, threads, 0, stream>>>(d_C, d_A, d_B, dimsA.x, dimsB.x);\n            break;\n        }\n    }\n\n    // Record the stop event\n    checkCudaErrors(hipEventRecord(stop, stream));\n\n    // Wait for the stop event to complete\n    checkCudaErrors(hipEventSynchronize(stop));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(hipEventElapsedTime(&msecTotal, start, stop));\n\n    // Compute and print the performance\n    float  msecPerMatrixMul = msecTotal / nIter;\n    double flopsPerMatrixMul =\n        2.0 * static_cast<double>(dimsA.x) * static_cast<double>(dimsA.y) * static_cast<double>(dimsB.x);\n    double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);\n    printf(\"Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops,\"\n           \" WorkgroupSize= %u threads/block\\n\",\n           gigaFlops,\n           msecPerMatrixMul,\n           flopsPerMatrixMul,\n           threads.x * threads.y);\n\n    // Copy result from device to host\n    checkCudaErrors(hipMemcpyAsync(h_C, d_C, mem_size_C, hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    printf(\"Checking computed result for correctness: \");\n    bool correct = true;\n\n    // test relative error by the formula\n    // |<x, y>_cpu - <x,y>_gpu|/<|x|, |y|>  < eps\n    double eps = 1.e-6; // machine zero\n\n    for (int i = 0; i < static_cast<int>(dimsC.x * dimsC.y); i++) {\n        double abs_err    = fabs(h_C[i] - (dimsA.x * valB));\n        double dot_length = dimsA.x;\n        double abs_val    = fabs(h_C[i]);\n        double rel_err    = abs_err / abs_val / dot_length;\n\n        if (rel_err > eps) {\n            printf(\"Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\\n\", i, h_C[i], dimsA.x * valB, eps);\n            correct = false;\n        }\n    }\n\n    printf(\"%s\\n\", correct ? \"Result = PASS\" : \"Result = FAIL\");\n\n    // Clean up memory\n    checkCudaErrors(hipHostFree(h_A));\n    checkCudaErrors(hipHostFree(h_B));\n    checkCudaErrors(hipHostFree(h_C));\n    checkCudaErrors(hipFree(d_A));\n    checkCudaErrors(hipFree(d_B));\n    checkCudaErrors(hipFree(d_C));\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance \"\n           \"measurements. Results may vary when GPU Boost is enabled.\\n\");\n\n    if (correct) {\n        return EXIT_SUCCESS;\n    }\n    else {\n        return EXIT_FAILURE;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"[globalToShmemAsyncCopy] - Starting...\\n\");\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"?\")) {\n        printf(\"Usage -device=n (n >= 0 for deviceID)\\n\");\n        printf(\"      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\\n\");\n        printf(\"      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\\n\");\n        printf(\"      -kernel=kernel_number (0 - AsyncCopyMultiStageLargeChunk; 1 - \"\n               \"AsyncCopyLargeChunk)\\n\");\n        printf(\"                            (2 - AsyncCopyLargeChunkAWBarrier; 3 - \"\n               \"AsyncCopyMultiStageSharedState)\\n\");\n        printf(\"                            (4 - AsyncCopyMultiStage; 5 - \"\n               \"AsyncCopySingleStage; 6 - Naive without memcpy_async)\\n\");\n        printf(\"                            (7 - NaiveLargeChunk without \"\n               \"memcpy_async)\\n\");\n        printf(\"  Note: Outer matrix dimensions of A & B matrices must be equal.\\n\");\n\n        exit(EXIT_SUCCESS);\n    }\n\n    // This will pick the best possible CUDA capable device, otherwise\n    // override the device ID based on input provided at the command line\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    int  matrixBlock = 32;\n    dim3 dimsA(10 * 4 * matrixBlock, 10 * 4 * matrixBlock, 1);\n    dim3 dimsB(10 * 4 * matrixBlock, 10 * 4 * matrixBlock, 1);\n\n    // width of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wA\")) {\n        dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wA\");\n    }\n\n    // height of Matrix A\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hA\")) {\n        dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hA\");\n    }\n\n    // width of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"wB\")) {\n        dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, \"wB\");\n    }\n\n    // height of Matrix B\n    if (checkCmdLineFlag(argc, (const char **)argv, \"hB\")) {\n        dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, \"hB\");\n    }\n\n    if (dimsA.x != dimsB.y) {\n        printf(\"Error: outer matrix dimensions must be equal. (%d != %d)\\n\", dimsA.x, dimsB.y);\n        exit(EXIT_FAILURE);\n    }\n\n    kernels selected_kernel = AsyncCopyMultiStageLargeChunk;\n\n    // kernel to run - default (AsyncCopyMultiStageLargeChunk == 0)\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel\")) {\n        int kernel_number = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel\");\n        if (kernel_number < 8) {\n            selected_kernel = (kernels)kernel_number;\n        }\n        else {\n            printf(\"Error: kernel number should be between 0 to 6, you have entered \"\n                   \"%d\\n\",\n                   kernel_number);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    int major = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, dev));\n    if (major < 7) {\n        printf(\"globalToShmemAsyncCopy requires SM 7.0 or higher.  Exiting...\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    printf(\"MatrixA(%d,%d), MatrixB(%d,%d)\\n\", dimsA.x, dimsA.y, dimsB.x, dimsB.y);\n\n    int matrix_result = MatrixMultiply(argc, argv, dimsA, dimsB, selected_kernel);\n\n    exit(matrix_result);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/globalToShmemAsyncCopy/globalToShmemAsyncCopy.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n// includes, project\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\nnamespace cg = cooperative_groups;\n\n#define NUM_ELEMS             10000000\n#define NUM_THREADS_PER_BLOCK 512\n\n// warp-aggregated atomic increment\n__device__ int atomicAggInc(int *counter)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n\n    // leader does the update\n    int res = 0;\n    if (active.thread_rank() == 0) {\n        res = atomicAdd(counter, active.size());\n    }\n\n    // broadcast result\n    res = active.shfl(res, 0);\n\n    // each thread computes its own value\n    return res + active.thread_rank();\n}\n\n__global__ void filter_arr(int *dst, int *nres, const int *src, int n)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n\n    for (int i = id; i < n; i += gridDim.x * blockDim.x) {\n        if (src[i] > 0)\n            dst[atomicAggInc(nres)] = src[i];\n    }\n}\n\n// warp-aggregated atomic multi bucket increment\n#if __CUDA_ARCH__ >= 700\n__device__ int atomicAggIncMulti(const int bucket, int *counter)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n    // group all threads with same bucket value.\n    auto labeledGroup = cg::labeled_partition(active, bucket);\n\n    int res = 0;\n    if (labeledGroup.thread_rank() == 0) {\n        res = atomicAdd(&counter[bucket], labeledGroup.size());\n    }\n\n    // broadcast result\n    res = labeledGroup.shfl(res, 0);\n\n    // each thread computes its own value\n    return res + labeledGroup.thread_rank();\n}\n#endif\n\n// Places individual value indices into its corresponding buckets.\n__global__ void\nmapToBuckets(const int *srcArr, int *indicesBuckets, int *bucketCounters, const int srcSize, const int numOfBuckets)\n{\n#if __CUDA_ARCH__ >= 700\n    cg::grid_group grid = cg::this_grid();\n\n    for (int i = grid.thread_rank(); i < srcSize; i += grid.size()) {\n        const int bucket = srcArr[i];\n        if (bucket < numOfBuckets) {\n            indicesBuckets[atomicAggIncMulti(bucket, bucketCounters)] = i;\n        }\n    }\n#endif\n}\n\nint mapIndicesToBuckets(int *h_srcArr, int *d_srcArr, int numOfBuckets)\n{\n    int *d_indicesBuckets, *d_bucketCounters;\n    int *cpuBucketCounters = new int[numOfBuckets];\n    int *h_bucketCounters  = new int[numOfBuckets];\n\n    memset(cpuBucketCounters, 0, sizeof(int) * numOfBuckets);\n    // Initialize each bucket counters.\n    for (int i = 0; i < numOfBuckets; i++) {\n        h_bucketCounters[i] = i * NUM_ELEMS;\n    }\n\n    checkCudaErrors(cudaMalloc(&d_indicesBuckets, sizeof(int) * NUM_ELEMS * numOfBuckets));\n    checkCudaErrors(cudaMalloc(&d_bucketCounters, sizeof(int) * numOfBuckets));\n\n    checkCudaErrors(cudaMemcpy(d_bucketCounters, h_bucketCounters, sizeof(int) * numOfBuckets, cudaMemcpyHostToDevice));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK), 1, 1);\n\n    mapToBuckets<<<dimGrid, dimBlock>>>(d_srcArr, d_indicesBuckets, d_bucketCounters, NUM_ELEMS, numOfBuckets);\n\n    checkCudaErrors(cudaMemcpy(h_bucketCounters, d_bucketCounters, sizeof(int) * numOfBuckets, cudaMemcpyDeviceToHost));\n\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        cpuBucketCounters[h_srcArr[i]]++;\n    }\n\n    bool allMatch   = true;\n    int  finalElems = 0;\n    for (int i = 0; i < numOfBuckets; i++) {\n        finalElems += (h_bucketCounters[i] - i * NUM_ELEMS);\n        if (cpuBucketCounters[i] != (h_bucketCounters[i] - i * NUM_ELEMS)) {\n            allMatch = false;\n            break;\n        }\n    }\n\n    if (!allMatch && finalElems != NUM_ELEMS) {\n        return EXIT_FAILURE;\n    }\n    return EXIT_SUCCESS;\n}\n\n// Warp-aggregated atomic Max in multi bucket\n#if __CUDA_ARCH__ >= 700\n__device__ void atomicAggMaxMulti(const int bucket, int *counter, const int valueForMax)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n    // group all threads with same bucket value.\n    auto labeledGroup = cg::labeled_partition(active, bucket);\n\n    const int maxValueInGroup = cg::reduce(labeledGroup, valueForMax, cg::greater<int>());\n\n    if (labeledGroup.thread_rank() == 0) {\n        atomicMax(&counter[bucket], maxValueInGroup);\n    }\n}\n#endif\n\n// Performs max calculation in each buckets.\n__global__ void calculateMaxInEachBuckets(const int *srcArr,\n                                          const int *valueInBuckets,\n                                          int       *bucketsMax,\n                                          const int  srcSize,\n                                          const int  numOfBuckets)\n{\n#if __CUDA_ARCH__ >= 700\n    cg::grid_group grid = cg::this_grid();\n\n    for (int i = grid.thread_rank(); i < srcSize; i += grid.size()) {\n        const int bucket = srcArr[i];\n        if (bucket < numOfBuckets) {\n            atomicAggMaxMulti(bucket, bucketsMax, valueInBuckets[i]);\n        }\n    }\n#endif\n}\n\nint calculateMaxInBuckets(int *h_srcArr, int *d_srcArr, int numOfBuckets)\n{\n    int *d_valueInBuckets, *d_bucketsMax;\n    int *h_valueInBuckets = new int[NUM_ELEMS];\n    int *cpuBucketsMax    = new int[numOfBuckets];\n    int *h_bucketsMax     = new int[numOfBuckets];\n\n    memset(cpuBucketsMax, 0, sizeof(int) * numOfBuckets);\n\n    // Here we create values which is assumed to correspond to each\n    // buckets of srcArr at same array index.\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        h_valueInBuckets[i] = rand();\n    }\n\n    checkCudaErrors(cudaMalloc(&d_valueInBuckets, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(cudaMalloc(&d_bucketsMax, sizeof(int) * numOfBuckets));\n\n    checkCudaErrors(cudaMemset(d_bucketsMax, 0, sizeof(int) * numOfBuckets));\n    checkCudaErrors(cudaMemcpy(d_valueInBuckets, h_valueInBuckets, sizeof(int) * NUM_ELEMS, cudaMemcpyHostToDevice));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK), 1, 1);\n\n    calculateMaxInEachBuckets<<<dimGrid, dimBlock>>>(d_srcArr, d_valueInBuckets, d_bucketsMax, NUM_ELEMS, numOfBuckets);\n\n    checkCudaErrors(cudaMemcpy(h_bucketsMax, d_bucketsMax, sizeof(int) * numOfBuckets, cudaMemcpyDeviceToHost));\n\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        if (cpuBucketsMax[h_srcArr[i]] < h_valueInBuckets[i]) {\n            cpuBucketsMax[h_srcArr[i]] = h_valueInBuckets[i];\n        }\n    }\n\n    bool allMatch   = true;\n    int  finalElems = 0;\n    for (int i = 0; i < numOfBuckets; i++) {\n        if (cpuBucketsMax[i] != h_bucketsMax[i]) {\n            allMatch = false;\n            printf(\"CPU i=%d  max = %d mismatches GPU max = %d\\n\", i, cpuBucketsMax[i], h_bucketsMax[i]);\n            break;\n        }\n    }\n    if (allMatch) {\n        printf(\"CPU max matches GPU max\\n\");\n    }\n\n    delete[] h_valueInBuckets;\n    delete[] cpuBucketsMax;\n    delete[] h_bucketsMax;\n    checkCudaErrors(cudaFree(d_valueInBuckets));\n    checkCudaErrors(cudaFree(d_bucketsMax));\n\n    if (!allMatch && finalElems != NUM_ELEMS) {\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\nint main(int argc, char **argv)\n{\n    int *data_to_filter, *filtered_data, nres = 0;\n    int *d_data_to_filter, *d_filtered_data, *d_nres;\n\n    int numOfBuckets = 5;\n\n    data_to_filter = reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));\n\n    // Generate input data.\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        data_to_filter[i] = rand() % numOfBuckets;\n    }\n\n    int devId = findCudaDevice(argc, (const char **)argv);\n\n    checkCudaErrors(cudaMalloc(&d_data_to_filter, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(cudaMalloc(&d_filtered_data, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(cudaMalloc(&d_nres, sizeof(int)));\n\n    checkCudaErrors(cudaMemcpy(d_data_to_filter, data_to_filter, sizeof(int) * NUM_ELEMS, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemset(d_nres, 0, sizeof(int)));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK) + 1, 1, 1);\n\n    filter_arr<<<dimGrid, dimBlock>>>(d_filtered_data, d_nres, d_data_to_filter, NUM_ELEMS);\n\n    checkCudaErrors(cudaMemcpy(&nres, d_nres, sizeof(int), cudaMemcpyDeviceToHost));\n\n    filtered_data = reinterpret_cast<int *>(malloc(sizeof(int) * nres));\n\n    checkCudaErrors(cudaMemcpy(filtered_data, d_filtered_data, sizeof(int) * nres, cudaMemcpyDeviceToHost));\n\n    int *host_filtered_data = reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));\n\n    // Generate host output with host filtering code.\n    int host_flt_count = 0;\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        if (data_to_filter[i] > 0) {\n            host_filtered_data[host_flt_count++] = data_to_filter[i];\n        }\n    }\n\n    int major = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, devId));\n\n    int mapIndicesToBucketsStatus   = EXIT_SUCCESS;\n    int calculateMaxInBucketsStatus = EXIT_SUCCESS;\n    // atomicAggIncMulti & atomicAggMaxMulti require a GPU of Volta (SM7X) architecture or higher,\n    // so that it can take advantage of the new MATCH capability of Volta hardware\n    if (major >= 7) {\n        mapIndicesToBucketsStatus   = mapIndicesToBuckets(data_to_filter, d_data_to_filter, numOfBuckets);\n        calculateMaxInBucketsStatus = calculateMaxInBuckets(data_to_filter, d_data_to_filter, numOfBuckets);\n    }\n\n    printf(\"\\nWarp Aggregated Atomics %s \\n\",\n           (host_flt_count == nres) && (mapIndicesToBucketsStatus == EXIT_SUCCESS)\n                   && (calculateMaxInBucketsStatus == EXIT_SUCCESS)\n               ? \"PASSED\"\n               : \"FAILED\");\n\n    checkCudaErrors(cudaFree(d_data_to_filter));\n    checkCudaErrors(cudaFree(d_filtered_data));\n    checkCudaErrors(cudaFree(d_nres));\n    free(data_to_filter);\n    free(filtered_data);\n    free(host_filtered_data);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n// includes, project\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\nnamespace cg = cooperative_groups;\n\n#define NUM_ELEMS             10000000\n#define NUM_THREADS_PER_BLOCK 512\n\n// warp-aggregated atomic increment\n__device__ int atomicAggInc(int *counter)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n\n    // leader does the update\n    int res = 0;\n    if (active.thread_rank() == 0) {\n        res = atomicAdd(counter, active.size());\n    }\n\n    // broadcast result\n    res = active.shfl(res, 0);\n\n    // each thread computes its own value\n    return res + active.thread_rank();\n}\n\n__global__ void filter_arr(int *dst, int *nres, const int *src, int n)\n{\n    int id = threadIdx.x + blockIdx.x * blockDim.x;\n\n    for (int i = id; i < n; i += gridDim.x * blockDim.x) {\n        if (src[i] > 0)\n            dst[atomicAggInc(nres)] = src[i];\n    }\n}\n\n// warp-aggregated atomic multi bucket increment\n#if __CUDA_ARCH__ >= 700\n__device__ int atomicAggIncMulti(const int bucket, int *counter)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n    // group all threads with same bucket value.\n    auto labeledGroup = cg::labeled_partition(active, bucket);\n\n    int res = 0;\n    if (labeledGroup.thread_rank() == 0) {\n        res = atomicAdd(&counter[bucket], labeledGroup.size());\n    }\n\n    // broadcast result\n    res = labeledGroup.shfl(res, 0);\n\n    // each thread computes its own value\n    return res + labeledGroup.thread_rank();\n}\n#endif\n\n// Places individual value indices into its corresponding buckets.\n__global__ void\nmapToBuckets(const int *srcArr, int *indicesBuckets, int *bucketCounters, const int srcSize, const int numOfBuckets)\n{\n#if __CUDA_ARCH__ >= 700\n    cg::grid_group grid = cg::this_grid();\n\n    for (int i = grid.thread_rank(); i < srcSize; i += grid.size()) {\n        const int bucket = srcArr[i];\n        if (bucket < numOfBuckets) {\n            indicesBuckets[atomicAggIncMulti(bucket, bucketCounters)] = i;\n        }\n    }\n#endif\n}\n\nint mapIndicesToBuckets(int *h_srcArr, int *d_srcArr, int numOfBuckets)\n{\n    int *d_indicesBuckets, *d_bucketCounters;\n    int *cpuBucketCounters = new int[numOfBuckets];\n    int *h_bucketCounters  = new int[numOfBuckets];\n\n    memset(cpuBucketCounters, 0, sizeof(int) * numOfBuckets);\n    // Initialize each bucket counters.\n    for (int i = 0; i < numOfBuckets; i++) {\n        h_bucketCounters[i] = i * NUM_ELEMS;\n    }\n\n    checkCudaErrors(hipMalloc(&d_indicesBuckets, sizeof(int) * NUM_ELEMS * numOfBuckets));\n    checkCudaErrors(hipMalloc(&d_bucketCounters, sizeof(int) * numOfBuckets));\n\n    checkCudaErrors(hipMemcpy(d_bucketCounters, h_bucketCounters, sizeof(int) * numOfBuckets, hipMemcpyHostToDevice));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK), 1, 1);\n\n    mapToBuckets<<<dimGrid, dimBlock>>>(d_srcArr, d_indicesBuckets, d_bucketCounters, NUM_ELEMS, numOfBuckets);\n\n    checkCudaErrors(hipMemcpy(h_bucketCounters, d_bucketCounters, sizeof(int) * numOfBuckets, hipMemcpyDeviceToHost));\n\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        cpuBucketCounters[h_srcArr[i]]++;\n    }\n\n    bool allMatch   = true;\n    int  finalElems = 0;\n    for (int i = 0; i < numOfBuckets; i++) {\n        finalElems += (h_bucketCounters[i] - i * NUM_ELEMS);\n        if (cpuBucketCounters[i] != (h_bucketCounters[i] - i * NUM_ELEMS)) {\n            allMatch = false;\n            break;\n        }\n    }\n\n    if (!allMatch && finalElems != NUM_ELEMS) {\n        return EXIT_FAILURE;\n    }\n    return EXIT_SUCCESS;\n}\n\n// Warp-aggregated atomic Max in multi bucket\n#if __CUDA_ARCH__ >= 700\n__device__ void atomicAggMaxMulti(const int bucket, int *counter, const int valueForMax)\n{\n    cg::coalesced_group active = cg::coalesced_threads();\n    // group all threads with same bucket value.\n    auto labeledGroup = cg::labeled_partition(active, bucket);\n\n    const int maxValueInGroup = cg::reduce(labeledGroup, valueForMax, cg::greater<int>());\n\n    if (labeledGroup.thread_rank() == 0) {\n        atomicMax(&counter[bucket], maxValueInGroup);\n    }\n}\n#endif\n\n// Performs max calculation in each buckets.\n__global__ void calculateMaxInEachBuckets(const int *srcArr,\n                                          const int *valueInBuckets,\n                                          int       *bucketsMax,\n                                          const int  srcSize,\n                                          const int  numOfBuckets)\n{\n#if __CUDA_ARCH__ >= 700\n    cg::grid_group grid = cg::this_grid();\n\n    for (int i = grid.thread_rank(); i < srcSize; i += grid.size()) {\n        const int bucket = srcArr[i];\n        if (bucket < numOfBuckets) {\n            atomicAggMaxMulti(bucket, bucketsMax, valueInBuckets[i]);\n        }\n    }\n#endif\n}\n\nint calculateMaxInBuckets(int *h_srcArr, int *d_srcArr, int numOfBuckets)\n{\n    int *d_valueInBuckets, *d_bucketsMax;\n    int *h_valueInBuckets = new int[NUM_ELEMS];\n    int *cpuBucketsMax    = new int[numOfBuckets];\n    int *h_bucketsMax     = new int[numOfBuckets];\n\n    memset(cpuBucketsMax, 0, sizeof(int) * numOfBuckets);\n\n    // Here we create values which is assumed to correspond to each\n    // buckets of srcArr at same array index.\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        h_valueInBuckets[i] = rand();\n    }\n\n    checkCudaErrors(hipMalloc(&d_valueInBuckets, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(hipMalloc(&d_bucketsMax, sizeof(int) * numOfBuckets));\n\n    checkCudaErrors(hipMemset(d_bucketsMax, 0, sizeof(int) * numOfBuckets));\n    checkCudaErrors(hipMemcpy(d_valueInBuckets, h_valueInBuckets, sizeof(int) * NUM_ELEMS, hipMemcpyHostToDevice));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK), 1, 1);\n\n    calculateMaxInEachBuckets<<<dimGrid, dimBlock>>>(d_srcArr, d_valueInBuckets, d_bucketsMax, NUM_ELEMS, numOfBuckets);\n\n    checkCudaErrors(hipMemcpy(h_bucketsMax, d_bucketsMax, sizeof(int) * numOfBuckets, hipMemcpyDeviceToHost));\n\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        if (cpuBucketsMax[h_srcArr[i]] < h_valueInBuckets[i]) {\n            cpuBucketsMax[h_srcArr[i]] = h_valueInBuckets[i];\n        }\n    }\n\n    bool allMatch   = true;\n    int  finalElems = 0;\n    for (int i = 0; i < numOfBuckets; i++) {\n        if (cpuBucketsMax[i] != h_bucketsMax[i]) {\n            allMatch = false;\n            printf(\"CPU i=%d  max = %d mismatches GPU max = %d\\n\", i, cpuBucketsMax[i], h_bucketsMax[i]);\n            break;\n        }\n    }\n    if (allMatch) {\n        printf(\"CPU max matches GPU max\\n\");\n    }\n\n    delete[] h_valueInBuckets;\n    delete[] cpuBucketsMax;\n    delete[] h_bucketsMax;\n    checkCudaErrors(hipFree(d_valueInBuckets));\n    checkCudaErrors(hipFree(d_bucketsMax));\n\n    if (!allMatch && finalElems != NUM_ELEMS) {\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n\nint main(int argc, char **argv)\n{\n    int *data_to_filter, *filtered_data, nres = 0;\n    int *d_data_to_filter, *d_filtered_data, *d_nres;\n\n    int numOfBuckets = 5;\n\n    data_to_filter = reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));\n\n    // Generate input data.\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        data_to_filter[i] = rand() % numOfBuckets;\n    }\n\n    int devId = findCudaDevice(argc, (const char **)argv);\n\n    checkCudaErrors(hipMalloc(&d_data_to_filter, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(hipMalloc(&d_filtered_data, sizeof(int) * NUM_ELEMS));\n    checkCudaErrors(hipMalloc(&d_nres, sizeof(int)));\n\n    checkCudaErrors(hipMemcpy(d_data_to_filter, data_to_filter, sizeof(int) * NUM_ELEMS, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemset(d_nres, 0, sizeof(int)));\n\n    dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);\n    dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK) + 1, 1, 1);\n\n    filter_arr<<<dimGrid, dimBlock>>>(d_filtered_data, d_nres, d_data_to_filter, NUM_ELEMS);\n\n    checkCudaErrors(hipMemcpy(&nres, d_nres, sizeof(int), hipMemcpyDeviceToHost));\n\n    filtered_data = reinterpret_cast<int *>(malloc(sizeof(int) * nres));\n\n    checkCudaErrors(hipMemcpy(filtered_data, d_filtered_data, sizeof(int) * nres, hipMemcpyDeviceToHost));\n\n    int *host_filtered_data = reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));\n\n    // Generate host output with host filtering code.\n    int host_flt_count = 0;\n    for (int i = 0; i < NUM_ELEMS; i++) {\n        if (data_to_filter[i] > 0) {\n            host_filtered_data[host_flt_count++] = data_to_filter[i];\n        }\n    }\n\n    int major = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, devId));\n\n    int mapIndicesToBucketsStatus   = EXIT_SUCCESS;\n    int calculateMaxInBucketsStatus = EXIT_SUCCESS;\n    // atomicAggIncMulti & atomicAggMaxMulti require a GPU of Volta (SM7X) architecture or higher,\n    // so that it can take advantage of the new MATCH capability of Volta hardware\n    if (major >= 7) {\n        mapIndicesToBucketsStatus   = mapIndicesToBuckets(data_to_filter, d_data_to_filter, numOfBuckets);\n        calculateMaxInBucketsStatus = calculateMaxInBuckets(data_to_filter, d_data_to_filter, numOfBuckets);\n    }\n\n    printf(\"\\nWarp Aggregated Atomics %s \\n\",\n           (host_flt_count == nres) && (mapIndicesToBucketsStatus == EXIT_SUCCESS)\n                   && (calculateMaxInBucketsStatus == EXIT_SUCCESS)\n               ? \"PASSED\"\n               : \"FAILED\");\n\n    checkCudaErrors(hipFree(d_data_to_filter));\n    checkCudaErrors(hipFree(d_filtered_data));\n    checkCudaErrors(hipFree(d_nres));\n    free(data_to_filter);\n    free(filtered_data);\n    free(host_filtered_data);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/warpAggregatedAtomicsCG/warpAggregatedAtomicsCG.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates basic usage of binary partition cooperative groups\n * within the thread block tile when divergent path exists.\n * 1.) Each thread loads a value from random array.\n * 2.) then checks if it is odd or even.\n * 3.) create binary partition group based on the above predicate\n * 4.) we count the number of odd/even in the group based on size of the binary\n       groups\n * 5.) write it global counter of odd.\n * 6.) sum the values loaded by individual threads(using reduce) and write it to\n       global even & odd elements sum.\n *\n * **NOTE** :\n *    binary_partition results in splitting warp into divergent thread groups\n *    this is not good from performance perspective, but in cases where warp\n *    divergence is inevitable one can use binary_partition group.\n*/\n\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <helper_cuda.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\nvoid initOddEvenArr(int *inputArr, unsigned int size)\n{\n    for (int i = 0; i < size; i++) {\n        inputArr[i] = rand() % 50;\n    }\n}\n\n/**\n * CUDA kernel device code\n *\n * Creates cooperative groups and performs odd/even counting & summation.\n */\n__global__ void oddEvenCountAndSumCG(int *inputArr, int *numOfOdds, int *sumOfOddAndEvens, unsigned int size)\n{\n    cg::thread_block          cta    = cg::this_thread_block();\n    cg::grid_group            grid   = cg::this_grid();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        int  elem    = inputArr[i];\n        auto subTile = cg::binary_partition(tile32, elem & 1);\n        if (elem & 1) // Odd numbers group\n        {\n            int oddGroupSum = cg::reduce(subTile, elem, cg::plus<int>());\n\n            if (subTile.thread_rank() == 0) {\n                // Add number of odds present in this group of Odds.\n                atomicAdd(numOfOdds, subTile.size());\n\n                // Add local reduction of odds present in this group of Odds.\n                atomicAdd(&sumOfOddAndEvens[0], oddGroupSum);\n            }\n        }\n        else // Even numbers group\n        {\n            int evenGroupSum = cg::reduce(subTile, elem, cg::plus<int>());\n\n            if (subTile.thread_rank() == 0) {\n                // Add local reduction of even present in this group of evens.\n                atomicAdd(&sumOfOddAndEvens[1], evenGroupSum);\n            }\n        }\n        // reconverge warp so for next loop iteration we ensure convergence of\n        // above diverged threads to perform coalesced loads of inputArr.\n        cg::sync(tile32);\n    }\n}\n\n/**\n * Host main routine\n */\nint main(int argc, const char **argv)\n{\n    int          deviceId = findCudaDevice(argc, argv);\n    int         *h_inputArr, *d_inputArr;\n    int         *h_numOfOdds, *d_numOfOdds;\n    int         *h_sumOfOddEvenElems, *d_sumOfOddEvenElems;\n    unsigned int arrSize = 1024 * 100;\n\n    checkCudaErrors(cudaMallocHost(&h_inputArr, sizeof(int) * arrSize));\n    checkCudaErrors(cudaMallocHost(&h_numOfOdds, sizeof(int)));\n    checkCudaErrors(cudaMallocHost(&h_sumOfOddEvenElems, sizeof(int) * 2));\n    initOddEvenArr(h_inputArr, arrSize);\n\n    cudaStream_t stream;\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    checkCudaErrors(cudaMalloc(&d_inputArr, sizeof(int) * arrSize));\n    checkCudaErrors(cudaMalloc(&d_numOfOdds, sizeof(int)));\n    checkCudaErrors(cudaMalloc(&d_sumOfOddEvenElems, sizeof(int) * 2));\n\n    checkCudaErrors(cudaMemcpyAsync(d_inputArr, h_inputArr, sizeof(int) * arrSize, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemsetAsync(d_numOfOdds, 0, sizeof(int), stream));\n    checkCudaErrors(cudaMemsetAsync(d_sumOfOddEvenElems, 0, 2 * sizeof(int), stream));\n\n    // Launch the kernel\n    int threadsPerBlock = 0;\n    int blocksPerGrid   = 0;\n    checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&blocksPerGrid, &threadsPerBlock, oddEvenCountAndSumCG, 0, 0));\n\n    printf(\"\\nLaunching %d blocks with %d threads...\\n\\n\", blocksPerGrid, threadsPerBlock);\n\n    oddEvenCountAndSumCG<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(\n        d_inputArr, d_numOfOdds, d_sumOfOddEvenElems, arrSize);\n\n    checkCudaErrors(cudaMemcpyAsync(h_numOfOdds, d_numOfOdds, sizeof(int), cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(\n        cudaMemcpyAsync(h_sumOfOddEvenElems, d_sumOfOddEvenElems, 2 * sizeof(int), cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    printf(\"Array size = %d Num of Odds = %d Sum of Odds = %d Sum of Evens %d\\n\",\n           arrSize,\n           h_numOfOdds[0],\n           h_sumOfOddEvenElems[0],\n           h_sumOfOddEvenElems[1]);\n    printf(\"\\n...Done.\\n\\n\");\n\n    checkCudaErrors(cudaFreeHost(h_inputArr));\n    checkCudaErrors(cudaFreeHost(h_numOfOdds));\n    checkCudaErrors(cudaFreeHost(h_sumOfOddEvenElems));\n\n    checkCudaErrors(cudaFree(d_inputArr));\n    checkCudaErrors(cudaFree(d_numOfOdds));\n    checkCudaErrors(cudaFree(d_sumOfOddEvenElems));\n\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample illustrates basic usage of binary partition cooperative groups\n * within the thread block tile when divergent path exists.\n * 1.) Each thread loads a value from random array.\n * 2.) then checks if it is odd or even.\n * 3.) create binary partition group based on the above predicate\n * 4.) we count the number of odd/even in the group based on size of the binary\n       groups\n * 5.) write it global counter of odd.\n * 6.) sum the values loaded by individual threads(using reduce) and write it to\n       global even & odd elements sum.\n *\n * **NOTE** :\n *    binary_partition results in splitting warp into divergent thread groups\n *    this is not good from performance perspective, but in cases where warp\n *    divergence is inevitable one can use binary_partition group.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\nvoid initOddEvenArr(int *inputArr, unsigned int size)\n{\n    for (int i = 0; i < size; i++) {\n        inputArr[i] = rand() % 50;\n    }\n}\n\n/**\n * CUDA kernel device code\n *\n * Creates cooperative groups and performs odd/even counting & summation.\n */\n__global__ void oddEvenCountAndSumCG(int *inputArr, int *numOfOdds, int *sumOfOddAndEvens, unsigned int size)\n{\n    cg::thread_block          cta    = cg::this_thread_block();\n    cg::grid_group            grid   = cg::this_grid();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        int  elem    = inputArr[i];\n        auto subTile = cg::binary_partition(tile32, elem & 1);\n        if (elem & 1) // Odd numbers group\n        {\n            int oddGroupSum = cg::reduce(subTile, elem, cg::plus<int>());\n\n            if (subTile.thread_rank() == 0) {\n                // Add number of odds present in this group of Odds.\n                atomicAdd(numOfOdds, subTile.size());\n\n                // Add local reduction of odds present in this group of Odds.\n                atomicAdd(&sumOfOddAndEvens[0], oddGroupSum);\n            }\n        }\n        else // Even numbers group\n        {\n            int evenGroupSum = cg::reduce(subTile, elem, cg::plus<int>());\n\n            if (subTile.thread_rank() == 0) {\n                // Add local reduction of even present in this group of evens.\n                atomicAdd(&sumOfOddAndEvens[1], evenGroupSum);\n            }\n        }\n        // reconverge warp so for next loop iteration we ensure convergence of\n        // above diverged threads to perform coalesced loads of inputArr.\n        cg::sync(tile32);\n    }\n}\n\n/**\n * Host main routine\n */\nint main(int argc, const char **argv)\n{\n    int          deviceId = findCudaDevice(argc, argv);\n    int         *h_inputArr, *d_inputArr;\n    int         *h_numOfOdds, *d_numOfOdds;\n    int         *h_sumOfOddEvenElems, *d_sumOfOddEvenElems;\n    unsigned int arrSize = 1024 * 100;\n\n    checkCudaErrors(hipHostMalloc(&h_inputArr, sizeof(int) * arrSize));\n    checkCudaErrors(hipHostMalloc(&h_numOfOdds, sizeof(int)));\n    checkCudaErrors(hipHostMalloc(&h_sumOfOddEvenElems, sizeof(int) * 2));\n    initOddEvenArr(h_inputArr, arrSize);\n\n    hipStream_t stream;\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    checkCudaErrors(hipMalloc(&d_inputArr, sizeof(int) * arrSize));\n    checkCudaErrors(hipMalloc(&d_numOfOdds, sizeof(int)));\n    checkCudaErrors(hipMalloc(&d_sumOfOddEvenElems, sizeof(int) * 2));\n\n    checkCudaErrors(hipMemcpyAsync(d_inputArr, h_inputArr, sizeof(int) * arrSize, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemsetAsync(d_numOfOdds, 0, sizeof(int), stream));\n    checkCudaErrors(hipMemsetAsync(d_sumOfOddEvenElems, 0, 2 * sizeof(int), stream));\n\n    // Launch the kernel\n    int threadsPerBlock = 0;\n    int blocksPerGrid   = 0;\n    checkCudaErrors(hipOccupancyMaxPotentialBlockSize(&blocksPerGrid, &threadsPerBlock, oddEvenCountAndSumCG, 0, 0));\n\n    printf(\"\\nLaunching %d blocks with %d threads...\\n\\n\", blocksPerGrid, threadsPerBlock);\n\n    oddEvenCountAndSumCG<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(\n        d_inputArr, d_numOfOdds, d_sumOfOddEvenElems, arrSize);\n\n    checkCudaErrors(hipMemcpyAsync(h_numOfOdds, d_numOfOdds, sizeof(int), hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(\n        hipMemcpyAsync(h_sumOfOddEvenElems, d_sumOfOddEvenElems, 2 * sizeof(int), hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    printf(\"Array size = %d Num of Odds = %d Sum of Odds = %d Sum of Evens %d\\n\",\n           arrSize,\n           h_numOfOdds[0],\n           h_sumOfOddEvenElems[0],\n           h_sumOfOddEvenElems[1]);\n    printf(\"\\n...Done.\\n\\n\");\n\n    checkCudaErrors(hipHostFree(h_inputArr));\n    checkCudaErrors(hipHostFree(h_numOfOdds));\n    checkCudaErrors(hipHostFree(h_sumOfOddEvenElems));\n\n    checkCudaErrors(hipFree(d_inputArr));\n    checkCudaErrors(hipFree(d_numOfOdds));\n    checkCudaErrors(hipFree(d_sumOfOddEvenElems));\n\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/binaryPartitionCG/binaryPartitionCG.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n\n// for cuda::std::tuple\n#include <cuda/std/tuple>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n////////////////////////////////////////////////////////////////////////////////\n// A structure of 2D points (structure of arrays).\n////////////////////////////////////////////////////////////////////////////////\nclass Points\n{\n    float *m_x;\n    float *m_y;\n\npublic:\n    // Constructor.\n    __host__ __device__ Points()\n        : m_x(NULL)\n        , m_y(NULL)\n    {\n    }\n\n    // Constructor.\n    __host__ __device__ Points(float *x, float *y)\n        : m_x(x)\n        , m_y(y)\n    {\n    }\n\n    // Get a point.\n    __host__ __device__ __forceinline__ float2 get_point(int idx) const { return make_float2(m_x[idx], m_y[idx]); }\n\n    // Set a point.\n    __host__ __device__ __forceinline__ void set_point(int idx, const float2 &p)\n    {\n        m_x[idx] = p.x;\n        m_y[idx] = p.y;\n    }\n\n    // Set the pointers.\n    __host__ __device__ __forceinline__ void set(float *x, float *y)\n    {\n        m_x = x;\n        m_y = y;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// A 2D bounding box\n////////////////////////////////////////////////////////////////////////////////\nclass Bounding_box\n{\n    // Extreme points of the bounding box.\n    float2 m_p_min;\n    float2 m_p_max;\n\npublic:\n    // Constructor. Create a unit box.\n    __host__ __device__ Bounding_box()\n    {\n        m_p_min = make_float2(0.0f, 0.0f);\n        m_p_max = make_float2(1.0f, 1.0f);\n    }\n\n    // Compute the center of the bounding-box.\n    __host__ __device__ void compute_center(float2 &center) const\n    {\n        center.x = 0.5f * (m_p_min.x + m_p_max.x);\n        center.y = 0.5f * (m_p_min.y + m_p_max.y);\n    }\n\n    // The points of the box.\n    __host__ __device__ __forceinline__ const float2 &get_max() const { return m_p_max; }\n\n    __host__ __device__ __forceinline__ const float2 &get_min() const { return m_p_min; }\n\n    // Does a box contain a point.\n    __host__ __device__ bool contains(const float2 &p) const\n    {\n        return p.x >= m_p_min.x && p.x < m_p_max.x && p.y >= m_p_min.y && p.y < m_p_max.y;\n    }\n\n    // Define the bounding box.\n    __host__ __device__ void set(float min_x, float min_y, float max_x, float max_y)\n    {\n        m_p_min.x = min_x;\n        m_p_min.y = min_y;\n        m_p_max.x = max_x;\n        m_p_max.y = max_y;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// A node of a quadree.\n////////////////////////////////////////////////////////////////////////////////\nclass Quadtree_node\n{\n    // The identifier of the node.\n    int m_id;\n    // The bounding box of the tree.\n    Bounding_box m_bounding_box;\n    // The range of points.\n    int m_begin, m_end;\n\npublic:\n    // Constructor.\n    __host__ __device__ Quadtree_node()\n        : m_id(0)\n        , m_begin(0)\n        , m_end(0)\n    {\n    }\n\n    // The ID of a node at its level.\n    __host__ __device__ int id() const { return m_id; }\n\n    // The ID of a node at its level.\n    __host__ __device__ void set_id(int new_id) { m_id = new_id; }\n\n    // The bounding box.\n    __host__ __device__ __forceinline__ const Bounding_box &bounding_box() const { return m_bounding_box; }\n\n    // Set the bounding box.\n    __host__ __device__ __forceinline__ void set_bounding_box(float min_x, float min_y, float max_x, float max_y)\n    {\n        m_bounding_box.set(min_x, min_y, max_x, max_y);\n    }\n\n    // The number of points in the tree.\n    __host__ __device__ __forceinline__ int num_points() const { return m_end - m_begin; }\n\n    // The range of points in the tree.\n    __host__ __device__ __forceinline__ int points_begin() const { return m_begin; }\n\n    __host__ __device__ __forceinline__ int points_end() const { return m_end; }\n\n    // Define the range for that node.\n    __host__ __device__ __forceinline__ void set_range(int begin, int end)\n    {\n        m_begin = begin;\n        m_end   = end;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Algorithm parameters.\n////////////////////////////////////////////////////////////////////////////////\nstruct Parameters\n{\n    // Choose the right set of points to use as in/out.\n    int point_selector;\n    // The number of nodes at a given level (2^k for level k).\n    int num_nodes_at_this_level;\n    // The recursion depth.\n    int depth;\n    // The max value for depth.\n    const int max_depth;\n    // The minimum number of points in a node to stop recursion.\n    const int min_points_per_node;\n\n    // Constructor set to default values.\n    __host__ __device__ Parameters(int max_depth, int min_points_per_node)\n        : point_selector(0)\n        , num_nodes_at_this_level(1)\n        , depth(0)\n        , max_depth(max_depth)\n        , min_points_per_node(min_points_per_node)\n    {\n    }\n\n    // Copy constructor. Changes the values for next iteration.\n    __host__ __device__ Parameters(const Parameters &params, bool)\n        : point_selector((params.point_selector + 1) % 2)\n        , num_nodes_at_this_level(4 * params.num_nodes_at_this_level)\n        , depth(params.depth + 1)\n        , max_depth(params.max_depth)\n        , min_points_per_node(params.min_points_per_node)\n    {\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Build a quadtree on the GPU. Use CUDA Dynamic Parallelism.\n//\n// The algorithm works as follows. The host (CPU) launches one block of\n// NUM_THREADS_PER_BLOCK threads. That block will do the following steps:\n//\n// 1- Check the number of points and its depth.\n//\n// We impose a maximum depth to the tree and a minimum number of points per\n// node. If the maximum depth is exceeded or the minimum number of points is\n// reached. The threads in the block exit.\n//\n// Before exiting, they perform a buffer swap if it is needed. Indeed, the\n// algorithm uses two buffers to permute the points and make sure they are\n// properly distributed in the quadtree. By design we want all points to be\n// in the first buffer of points at the end of the algorithm. It is the reason\n// why we may have to swap the buffer before leavin (if the points are in the\n// 2nd buffer).\n//\n// 2- Count the number of points in each child.\n//\n// If the depth is not too high and the number of points is sufficient, the\n// block has to dispatch the points into four geometrical buckets: Its\n// children. For that purpose, we compute the center of the bounding box and\n// count the number of points in each quadrant.\n//\n// The set of points is divided into sections. Each section is given to a\n// warp of threads (32 threads). Warps use __ballot and __popc intrinsics\n// to count the points. See the Programming Guide for more information about\n// those functions.\n//\n// 3- Scan the warps' results to know the \"global\" numbers.\n//\n// Warps work independently from each other. At the end, each warp knows the\n// number of points in its section. To know the numbers for the block, the\n// block has to run a scan/reduce at the block level. It's a traditional\n// approach. The implementation in that sample is not as optimized as what\n// could be found in fast radix sorts, for example, but it relies on the same\n// idea.\n//\n// 4- Move points.\n//\n// Now that the block knows how many points go in each of its 4 children, it\n// remains to dispatch the points. It is straightforward.\n//\n// 5- Launch new blocks.\n//\n// The block launches four new blocks: One per children. Each of the four blocks\n// will apply the same algorithm.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int NUM_THREADS_PER_BLOCK>\n__global__ void build_quadtree_kernel(Quadtree_node *nodes, Points *points, Parameters params)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // The number of warps in a block.\n    const int NUM_WARPS_PER_BLOCK = NUM_THREADS_PER_BLOCK / warpSize;\n\n    // Shared memory to store the number of points.\n    extern __shared__ int smem[];\n\n    // s_num_pts[4][NUM_WARPS_PER_BLOCK];\n    // Addresses of shared memory.\n    volatile int *s_num_pts[4];\n\n    for (int i = 0; i < 4; ++i)\n        s_num_pts[i] = (volatile int *)&smem[i * NUM_WARPS_PER_BLOCK];\n\n    // Compute the coordinates of the threads in the block.\n    const int warp_id = threadIdx.x / warpSize;\n    const int lane_id = threadIdx.x % warpSize;\n\n    // Mask for compaction.\n    // Same as: asm( \"mov.u32 %0, %%lanemask_lt;\" : \"=r\"(lane_mask_lt) );\n    int lane_mask_lt = (1 << lane_id) - 1;\n\n    // The current node.\n    Quadtree_node &node = nodes[blockIdx.x];\n\n    // The number of points in the node.\n    int num_points = node.num_points();\n\n    float2 center;\n    int    range_begin, range_end;\n    int    warp_cnts[4] = {0, 0, 0, 0};\n    //\n    // 1- Check the number of points and its depth.\n    //\n\n    // Stop the recursion here. Make sure points[0] contains all the points.\n    if (params.depth >= params.max_depth || num_points <= params.min_points_per_node) {\n        if (params.point_selector == 1) {\n            int it = node.points_begin(), end = node.points_end();\n\n            for (it += threadIdx.x; it < end; it += NUM_THREADS_PER_BLOCK)\n                if (it < end)\n                    points[0].set_point(it, points[1].get_point(it));\n        }\n\n        return;\n    }\n\n    // Compute the center of the bounding box of the points.\n    const Bounding_box &bbox = node.bounding_box();\n\n    bbox.compute_center(center);\n\n    // Find how many points to give to each warp.\n    int num_points_per_warp = max(warpSize, (num_points + NUM_WARPS_PER_BLOCK - 1) / NUM_WARPS_PER_BLOCK);\n\n    // Each warp of threads will compute the number of points to move to each\n    // quadrant.\n    range_begin = node.points_begin() + warp_id * num_points_per_warp;\n    range_end   = min(range_begin + num_points_per_warp, node.points_end());\n\n    //\n    // 2- Count the number of points in each child.\n    //\n\n    // Input points.\n    const Points &in_points = points[params.point_selector];\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n    // Compute the number of points.\n    for (int range_it = range_begin + tile32.thread_rank(); tile32.any(range_it < range_end); range_it += warpSize) {\n        // Is it still an active thread?\n        bool is_active = range_it < range_end;\n\n        // Load the coordinates of the point.\n        float2 p = is_active ? in_points.get_point(range_it) : make_float2(0.0f, 0.0f);\n\n        // Count top-left points.\n        int num_pts = __popc(tile32.ballot(is_active && p.x < center.x && p.y >= center.y));\n        warp_cnts[0] += tile32.shfl(num_pts, 0);\n\n        // Count top-right points.\n        num_pts = __popc(tile32.ballot(is_active && p.x >= center.x && p.y >= center.y));\n        warp_cnts[1] += tile32.shfl(num_pts, 0);\n\n        // Count bottom-left points.\n        num_pts = __popc(tile32.ballot(is_active && p.x < center.x && p.y < center.y));\n        warp_cnts[2] += tile32.shfl(num_pts, 0);\n\n        // Count bottom-right points.\n        num_pts = __popc(tile32.ballot(is_active && p.x >= center.x && p.y < center.y));\n        warp_cnts[3] += tile32.shfl(num_pts, 0);\n    }\n\n    if (tile32.thread_rank() == 0) {\n        s_num_pts[0][warp_id] = warp_cnts[0];\n        s_num_pts[1][warp_id] = warp_cnts[1];\n        s_num_pts[2][warp_id] = warp_cnts[2];\n        s_num_pts[3][warp_id] = warp_cnts[3];\n    }\n\n    // Make sure warps have finished counting.\n    cg::sync(cta);\n\n    //\n    // 3- Scan the warps' results to know the \"global\" numbers.\n    //\n\n    // First 4 warps scan the numbers of points per child (inclusive scan).\n    if (warp_id < 4) {\n        int num_pts = tile32.thread_rank() < NUM_WARPS_PER_BLOCK ? s_num_pts[warp_id][tile32.thread_rank()] : 0;\n#pragma unroll\n\n        for (int offset = 1; offset < NUM_WARPS_PER_BLOCK; offset *= 2) {\n            int n = tile32.shfl_up(num_pts, offset);\n\n            if (tile32.thread_rank() >= offset)\n                num_pts += n;\n        }\n\n        if (tile32.thread_rank() < NUM_WARPS_PER_BLOCK)\n            s_num_pts[warp_id][tile32.thread_rank()] = num_pts;\n    }\n\n    cg::sync(cta);\n\n    // Compute global offsets.\n    if (warp_id == 0) {\n        int sum = s_num_pts[0][NUM_WARPS_PER_BLOCK - 1];\n\n        for (int row = 1; row < 4; ++row) {\n            int tmp = s_num_pts[row][NUM_WARPS_PER_BLOCK - 1];\n            cg::sync(tile32);\n\n            if (tile32.thread_rank() < NUM_WARPS_PER_BLOCK)\n                s_num_pts[row][tile32.thread_rank()] += sum;\n\n            cg::sync(tile32);\n            sum += tmp;\n        }\n    }\n\n    cg::sync(cta);\n\n    // Make the scan exclusive.\n    int val = 0;\n    if (threadIdx.x < 4 * NUM_WARPS_PER_BLOCK) {\n        val = threadIdx.x == 0 ? 0 : smem[threadIdx.x - 1];\n        val += node.points_begin();\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < 4 * NUM_WARPS_PER_BLOCK) {\n        smem[threadIdx.x] = val;\n    }\n\n    cg::sync(cta);\n\n    //\n    // 4- Move points.\n    //\n    if (!(params.depth >= params.max_depth || num_points <= params.min_points_per_node)) {\n        // Output points.\n        Points &out_points = points[(params.point_selector + 1) % 2];\n\n        warp_cnts[0] = s_num_pts[0][warp_id];\n        warp_cnts[1] = s_num_pts[1][warp_id];\n        warp_cnts[2] = s_num_pts[2][warp_id];\n        warp_cnts[3] = s_num_pts[3][warp_id];\n\n        const Points &in_points = points[params.point_selector];\n        // Reorder points.\n        for (int range_it = range_begin + tile32.thread_rank(); tile32.any(range_it < range_end);\n             range_it += warpSize) {\n            // Is it still an active thread?\n            bool is_active = range_it < range_end;\n\n            // Load the coordinates of the point.\n            float2 p = is_active ? in_points.get_point(range_it) : make_float2(0.0f, 0.0f);\n\n            // Count top-left points.\n            bool pred = is_active && p.x < center.x && p.y >= center.y;\n            int  vote = tile32.ballot(pred);\n            int  dest = warp_cnts[0] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[0] += tile32.shfl(__popc(vote), 0);\n\n            // Count top-right points.\n            pred = is_active && p.x >= center.x && p.y >= center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[1] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[1] += tile32.shfl(__popc(vote), 0);\n\n            // Count bottom-left points.\n            pred = is_active && p.x < center.x && p.y < center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[2] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[2] += tile32.shfl(__popc(vote), 0);\n\n            // Count bottom-right points.\n            pred = is_active && p.x >= center.x && p.y < center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[3] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[3] += tile32.shfl(__popc(vote), 0);\n        }\n    }\n\n    cg::sync(cta);\n\n    if (tile32.thread_rank() == 0) {\n        s_num_pts[0][warp_id] = warp_cnts[0];\n        s_num_pts[1][warp_id] = warp_cnts[1];\n        s_num_pts[2][warp_id] = warp_cnts[2];\n        s_num_pts[3][warp_id] = warp_cnts[3];\n    }\n\n    cg::sync(cta);\n\n    //\n    // 5- Launch new blocks.\n    //\n    if (!(params.depth >= params.max_depth || num_points <= params.min_points_per_node)) {\n        // The last thread launches new blocks.\n        if (threadIdx.x == NUM_THREADS_PER_BLOCK - 1) {\n            // The children.\n            Quadtree_node *children = &nodes[params.num_nodes_at_this_level - (node.id() & ~3)];\n\n            // The offsets of the children at their level.\n            int child_offset = 4 * node.id();\n\n            // Set IDs.\n            children[child_offset + 0].set_id(4 * node.id() + 0);\n            children[child_offset + 1].set_id(4 * node.id() + 1);\n            children[child_offset + 2].set_id(4 * node.id() + 2);\n            children[child_offset + 3].set_id(4 * node.id() + 3);\n\n            const Bounding_box &bbox = node.bounding_box();\n            // Points of the bounding-box.\n            const float2 &p_min = bbox.get_min();\n            const float2 &p_max = bbox.get_max();\n\n            // Set the bounding boxes of the children.\n            children[child_offset + 0].set_bounding_box(p_min.x, center.y, center.x,\n                                                        p_max.y); // Top-left.\n            children[child_offset + 1].set_bounding_box(center.x, center.y, p_max.x,\n                                                        p_max.y); // Top-right.\n            children[child_offset + 2].set_bounding_box(p_min.x, p_min.y, center.x,\n                                                        center.y); // Bottom-left.\n            children[child_offset + 3].set_bounding_box(center.x, p_min.y, p_max.x,\n                                                        center.y); // Bottom-right.\n\n            // Set the ranges of the children.\n\n            children[child_offset + 0].set_range(node.points_begin(), s_num_pts[0][warp_id]);\n            children[child_offset + 1].set_range(s_num_pts[0][warp_id], s_num_pts[1][warp_id]);\n            children[child_offset + 2].set_range(s_num_pts[1][warp_id], s_num_pts[2][warp_id]);\n            children[child_offset + 3].set_range(s_num_pts[2][warp_id], s_num_pts[3][warp_id]);\n\n            // Launch 4 children.\n            build_quadtree_kernel<NUM_THREADS_PER_BLOCK>\n                <<<4, NUM_THREADS_PER_BLOCK, 4 * NUM_WARPS_PER_BLOCK * sizeof(int)>>>(\n                    &children[child_offset], points, Parameters(params, true));\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Make sure a Quadtree is properly defined.\n////////////////////////////////////////////////////////////////////////////////\nbool check_quadtree(const Quadtree_node *nodes, int idx, int num_pts, Points *pts, Parameters params)\n{\n    const Quadtree_node &node       = nodes[idx];\n    int                  num_points = node.num_points();\n\n    if (!(params.depth == params.max_depth || num_points <= params.min_points_per_node)) {\n        int num_points_in_children = 0;\n\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 0].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 1].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 2].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 3].num_points();\n\n        if (num_points_in_children != node.num_points())\n            return false;\n\n        return check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 0, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 1, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 2, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 3, num_pts, pts, Parameters(params, true));\n    }\n\n    const Bounding_box &bbox = node.bounding_box();\n\n    for (int it = node.points_begin(); it < node.points_end(); ++it) {\n        if (it >= num_pts)\n            return false;\n\n        float2 p = pts->get_point(it);\n\n        if (!bbox.contains(p))\n            return false;\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Parallel random number generator.\n////////////////////////////////////////////////////////////////////////////////\nstruct Random_generator\n{\n    int count;\n\n    __host__ __device__ Random_generator()\n        : count(0)\n    {\n    }\n    __host__ __device__ unsigned int hash(unsigned int a)\n    {\n        a = (a + 0x7ed55d16) + (a << 12);\n        a = (a ^ 0xc761c23c) ^ (a >> 19);\n        a = (a + 0x165667b1) + (a << 5);\n        a = (a + 0xd3a2646c) ^ (a << 9);\n        a = (a + 0xfd7046c5) + (a << 3);\n        a = (a ^ 0xb55a4f09) ^ (a >> 16);\n        return a;\n    }\n\n    __host__ __device__ __forceinline__ cuda::std::tuple<float, float> operator()()\n    {\n#ifdef __CUDA_ARCH__\n        unsigned seed = hash(blockIdx.x * blockDim.x + threadIdx.x + count);\n        // thrust::generate may call operator() more than once per thread.\n        // Hence, increment count by grid size to ensure uniqueness of seed\n        count += blockDim.x * gridDim.x;\n#else\n        unsigned seed = hash(0);\n#endif\n        thrust::default_random_engine                    rng(seed);\n        thrust::random::uniform_real_distribution<float> distrib;\n        return cuda::std::make_tuple(distrib(rng), distrib(rng));\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Allocate GPU structs, launch kernel and clean up\n////////////////////////////////////////////////////////////////////////////////\nbool cdpQuadtree(int warp_size)\n{\n    // Constants to control the algorithm.\n    const int num_points          = 1024;\n    const int max_depth           = 8;\n    const int min_points_per_node = 16;\n\n    // Allocate memory for points.\n    thrust::device_vector<float> x_d0(num_points);\n    thrust::device_vector<float> x_d1(num_points);\n    thrust::device_vector<float> y_d0(num_points);\n    thrust::device_vector<float> y_d1(num_points);\n\n    // Generate random points.\n    Random_generator rnd;\n    thrust::generate(\n        thrust::make_zip_iterator(x_d0.begin(), y_d0.begin()), thrust::make_zip_iterator(x_d0.end(), y_d0.end()), rnd);\n\n    // Host structures to analyze the device ones.\n    Points points_init[2];\n    points_init[0].set(thrust::raw_pointer_cast(&x_d0[0]), thrust::raw_pointer_cast(&y_d0[0]));\n    points_init[1].set(thrust::raw_pointer_cast(&x_d1[0]), thrust::raw_pointer_cast(&y_d1[0]));\n\n    // Allocate memory to store points.\n    Points *points;\n    checkCudaErrors(cudaMalloc((void **)&points, 2 * sizeof(Points)));\n    checkCudaErrors(cudaMemcpy(points, points_init, 2 * sizeof(Points), cudaMemcpyHostToDevice));\n\n    // We could use a close form...\n    int max_nodes = 0;\n\n    for (int i = 0, num_nodes_at_level = 1; i < max_depth; ++i, num_nodes_at_level *= 4)\n        max_nodes += num_nodes_at_level;\n\n    // Allocate memory to store the tree.\n    Quadtree_node root;\n    root.set_range(0, num_points);\n    Quadtree_node *nodes;\n    checkCudaErrors(cudaMalloc((void **)&nodes, max_nodes * sizeof(Quadtree_node)));\n    checkCudaErrors(cudaMemcpy(nodes, &root, sizeof(Quadtree_node), cudaMemcpyHostToDevice));\n\n    // Build the quadtree.\n    Parameters params(max_depth, min_points_per_node);\n    std::cout << \"Launching CDP kernel to build the quadtree\" << std::endl;\n    const int    NUM_THREADS_PER_BLOCK = 128; // Do not use less than 128 threads.\n    const int    NUM_WARPS_PER_BLOCK   = NUM_THREADS_PER_BLOCK / warp_size;\n    const size_t smem_size             = 4 * NUM_WARPS_PER_BLOCK * sizeof(int);\n    build_quadtree_kernel<NUM_THREADS_PER_BLOCK><<<1, NUM_THREADS_PER_BLOCK, smem_size>>>(nodes, points, params);\n    checkCudaErrors(cudaGetLastError());\n\n    // Copy points to CPU.\n    thrust::host_vector<float> x_h(x_d0);\n    thrust::host_vector<float> y_h(y_d0);\n    Points                     host_points;\n    host_points.set(thrust::raw_pointer_cast(&x_h[0]), thrust::raw_pointer_cast(&y_h[0]));\n\n    // Copy nodes to CPU.\n    Quadtree_node *host_nodes = new Quadtree_node[max_nodes];\n    checkCudaErrors(cudaMemcpy(host_nodes, nodes, max_nodes * sizeof(Quadtree_node), cudaMemcpyDeviceToHost));\n\n    // Validate the results.\n    bool ok = check_quadtree(host_nodes, 0, num_points, &host_points, params);\n    std::cout << \"Results: \" << (ok ? \"OK\" : \"FAILED\") << std::endl;\n\n    // Free CPU memory.\n    delete[] host_nodes;\n\n    // Free memory.\n    checkCudaErrors(cudaFree(nodes));\n    checkCudaErrors(cudaFree(points));\n\n    return ok;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // Find/set the device.\n    // The test requires an architecture SM35 or greater (CDP capable).\n    int            cuda_device = findCudaDevice(argc, (const char **)argv);\n    cudaDeviceProp deviceProps;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProps, cuda_device));\n    int cdpCapable = (deviceProps.major == 3 && deviceProps.minor >= 5) || deviceProps.major >= 4;\n\n    printf(\n        \"GPU device %s has compute capabilities (SM %d.%d)\\n\", deviceProps.name, deviceProps.major, deviceProps.minor);\n\n    if (!cdpCapable) {\n        std::cerr << \"cdpQuadTree requires SM 3.5 or higher to use CUDA Dynamic \"\n                     \"Parallelism.  Exiting...\\n\"\n                  << std::endl;\n        exit(EXIT_WAIVED);\n    }\n\n    bool ok = cdpQuadtree(deviceProps.warpSize);\n\n    return (ok ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n\n// for cuda::std::tuple\n#include <cuda/std/tuple>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n////////////////////////////////////////////////////////////////////////////////\n// A structure of 2D points (structure of arrays).\n////////////////////////////////////////////////////////////////////////////////\nclass Points\n{\n    float *m_x;\n    float *m_y;\n\npublic:\n    // Constructor.\n    __host__ __device__ Points()\n        : m_x(NULL)\n        , m_y(NULL)\n    {\n    }\n\n    // Constructor.\n    __host__ __device__ Points(float *x, float *y)\n        : m_x(x)\n        , m_y(y)\n    {\n    }\n\n    // Get a point.\n    __host__ __device__ __forceinline__ float2 get_point(int idx) const { return make_float2(m_x[idx], m_y[idx]); }\n\n    // Set a point.\n    __host__ __device__ __forceinline__ void set_point(int idx, const float2 &p)\n    {\n        m_x[idx] = p.x;\n        m_y[idx] = p.y;\n    }\n\n    // Set the pointers.\n    __host__ __device__ __forceinline__ void set(float *x, float *y)\n    {\n        m_x = x;\n        m_y = y;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// A 2D bounding box\n////////////////////////////////////////////////////////////////////////////////\nclass Bounding_box\n{\n    // Extreme points of the bounding box.\n    float2 m_p_min;\n    float2 m_p_max;\n\npublic:\n    // Constructor. Create a unit box.\n    __host__ __device__ Bounding_box()\n    {\n        m_p_min = make_float2(0.0f, 0.0f);\n        m_p_max = make_float2(1.0f, 1.0f);\n    }\n\n    // Compute the center of the bounding-box.\n    __host__ __device__ void compute_center(float2 &center) const\n    {\n        center.x = 0.5f * (m_p_min.x + m_p_max.x);\n        center.y = 0.5f * (m_p_min.y + m_p_max.y);\n    }\n\n    // The points of the box.\n    __host__ __device__ __forceinline__ const float2 &get_max() const { return m_p_max; }\n\n    __host__ __device__ __forceinline__ const float2 &get_min() const { return m_p_min; }\n\n    // Does a box contain a point.\n    __host__ __device__ bool contains(const float2 &p) const\n    {\n        return p.x >= m_p_min.x && p.x < m_p_max.x && p.y >= m_p_min.y && p.y < m_p_max.y;\n    }\n\n    // Define the bounding box.\n    __host__ __device__ void set(float min_x, float min_y, float max_x, float max_y)\n    {\n        m_p_min.x = min_x;\n        m_p_min.y = min_y;\n        m_p_max.x = max_x;\n        m_p_max.y = max_y;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// A node of a quadree.\n////////////////////////////////////////////////////////////////////////////////\nclass Quadtree_node\n{\n    // The identifier of the node.\n    int m_id;\n    // The bounding box of the tree.\n    Bounding_box m_bounding_box;\n    // The range of points.\n    int m_begin, m_end;\n\npublic:\n    // Constructor.\n    __host__ __device__ Quadtree_node()\n        : m_id(0)\n        , m_begin(0)\n        , m_end(0)\n    {\n    }\n\n    // The ID of a node at its level.\n    __host__ __device__ int id() const { return m_id; }\n\n    // The ID of a node at its level.\n    __host__ __device__ void set_id(int new_id) { m_id = new_id; }\n\n    // The bounding box.\n    __host__ __device__ __forceinline__ const Bounding_box &bounding_box() const { return m_bounding_box; }\n\n    // Set the bounding box.\n    __host__ __device__ __forceinline__ void set_bounding_box(float min_x, float min_y, float max_x, float max_y)\n    {\n        m_bounding_box.set(min_x, min_y, max_x, max_y);\n    }\n\n    // The number of points in the tree.\n    __host__ __device__ __forceinline__ int num_points() const { return m_end - m_begin; }\n\n    // The range of points in the tree.\n    __host__ __device__ __forceinline__ int points_begin() const { return m_begin; }\n\n    __host__ __device__ __forceinline__ int points_end() const { return m_end; }\n\n    // Define the range for that node.\n    __host__ __device__ __forceinline__ void set_range(int begin, int end)\n    {\n        m_begin = begin;\n        m_end   = end;\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Algorithm parameters.\n////////////////////////////////////////////////////////////////////////////////\nstruct Parameters\n{\n    // Choose the right set of points to use as in/out.\n    int point_selector;\n    // The number of nodes at a given level (2^k for level k).\n    int num_nodes_at_this_level;\n    // The recursion depth.\n    int depth;\n    // The max value for depth.\n    const int max_depth;\n    // The minimum number of points in a node to stop recursion.\n    const int min_points_per_node;\n\n    // Constructor set to default values.\n    __host__ __device__ Parameters(int max_depth, int min_points_per_node)\n        : point_selector(0)\n        , num_nodes_at_this_level(1)\n        , depth(0)\n        , max_depth(max_depth)\n        , min_points_per_node(min_points_per_node)\n    {\n    }\n\n    // Copy constructor. Changes the values for next iteration.\n    __host__ __device__ Parameters(const Parameters &params, bool)\n        : point_selector((params.point_selector + 1) % 2)\n        , num_nodes_at_this_level(4 * params.num_nodes_at_this_level)\n        , depth(params.depth + 1)\n        , max_depth(params.max_depth)\n        , min_points_per_node(params.min_points_per_node)\n    {\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Build a quadtree on the GPU. Use CUDA Dynamic Parallelism.\n//\n// The algorithm works as follows. The host (CPU) launches one block of\n// NUM_THREADS_PER_BLOCK threads. That block will do the following steps:\n//\n// 1- Check the number of points and its depth.\n//\n// We impose a maximum depth to the tree and a minimum number of points per\n// node. If the maximum depth is exceeded or the minimum number of points is\n// reached. The threads in the block exit.\n//\n// Before exiting, they perform a buffer swap if it is needed. Indeed, the\n// algorithm uses two buffers to permute the points and make sure they are\n// properly distributed in the quadtree. By design we want all points to be\n// in the first buffer of points at the end of the algorithm. It is the reason\n// why we may have to swap the buffer before leavin (if the points are in the\n// 2nd buffer).\n//\n// 2- Count the number of points in each child.\n//\n// If the depth is not too high and the number of points is sufficient, the\n// block has to dispatch the points into four geometrical buckets: Its\n// children. For that purpose, we compute the center of the bounding box and\n// count the number of points in each quadrant.\n//\n// The set of points is divided into sections. Each section is given to a\n// warp of threads (32 threads). Warps use __ballot and __popc intrinsics\n// to count the points. See the Programming Guide for more information about\n// those functions.\n//\n// 3- Scan the warps' results to know the \"global\" numbers.\n//\n// Warps work independently from each other. At the end, each warp knows the\n// number of points in its section. To know the numbers for the block, the\n// block has to run a scan/reduce at the block level. It's a traditional\n// approach. The implementation in that sample is not as optimized as what\n// could be found in fast radix sorts, for example, but it relies on the same\n// idea.\n//\n// 4- Move points.\n//\n// Now that the block knows how many points go in each of its 4 children, it\n// remains to dispatch the points. It is straightforward.\n//\n// 5- Launch new blocks.\n//\n// The block launches four new blocks: One per children. Each of the four blocks\n// will apply the same algorithm.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int NUM_THREADS_PER_BLOCK>\n__global__ void build_quadtree_kernel(Quadtree_node *nodes, Points *points, Parameters params)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // The number of warps in a block.\n    const int NUM_WARPS_PER_BLOCK = NUM_THREADS_PER_BLOCK / warpSize;\n\n    // Shared memory to store the number of points.\n    extern __shared__ int smem[];\n\n    // s_num_pts[4][NUM_WARPS_PER_BLOCK];\n    // Addresses of shared memory.\n    volatile int *s_num_pts[4];\n\n    for (int i = 0; i < 4; ++i)\n        s_num_pts[i] = (volatile int *)&smem[i * NUM_WARPS_PER_BLOCK];\n\n    // Compute the coordinates of the threads in the block.\n    const int warp_id = threadIdx.x / warpSize;\n    const int lane_id = threadIdx.x % warpSize;\n\n    // Mask for compaction.\n    // Same as: asm( \"mov.u32 %0, %%lanemask_lt;\" : \"=r\"(lane_mask_lt) );\n    int lane_mask_lt = (1 << lane_id) - 1;\n\n    // The current node.\n    Quadtree_node &node = nodes[blockIdx.x];\n\n    // The number of points in the node.\n    int num_points = node.num_points();\n\n    float2 center;\n    int    range_begin, range_end;\n    int    warp_cnts[4] = {0, 0, 0, 0};\n    //\n    // 1- Check the number of points and its depth.\n    //\n\n    // Stop the recursion here. Make sure points[0] contains all the points.\n    if (params.depth >= params.max_depth || num_points <= params.min_points_per_node) {\n        if (params.point_selector == 1) {\n            int it = node.points_begin(), end = node.points_end();\n\n            for (it += threadIdx.x; it < end; it += NUM_THREADS_PER_BLOCK)\n                if (it < end)\n                    points[0].set_point(it, points[1].get_point(it));\n        }\n\n        return;\n    }\n\n    // Compute the center of the bounding box of the points.\n    const Bounding_box &bbox = node.bounding_box();\n\n    bbox.compute_center(center);\n\n    // Find how many points to give to each warp.\n    int num_points_per_warp = max(warpSize, (num_points + NUM_WARPS_PER_BLOCK - 1) / NUM_WARPS_PER_BLOCK);\n\n    // Each warp of threads will compute the number of points to move to each\n    // quadrant.\n    range_begin = node.points_begin() + warp_id * num_points_per_warp;\n    range_end   = min(range_begin + num_points_per_warp, node.points_end());\n\n    //\n    // 2- Count the number of points in each child.\n    //\n\n    // Input points.\n    const Points &in_points = points[params.point_selector];\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n    // Compute the number of points.\n    for (int range_it = range_begin + tile32.thread_rank(); tile32.any(range_it < range_end); range_it += warpSize) {\n        // Is it still an active thread?\n        bool is_active = range_it < range_end;\n\n        // Load the coordinates of the point.\n        float2 p = is_active ? in_points.get_point(range_it) : make_float2(0.0f, 0.0f);\n\n        // Count top-left points.\n        int num_pts = __popc(tile32.ballot(is_active && p.x < center.x && p.y >= center.y));\n        warp_cnts[0] += tile32.shfl(num_pts, 0);\n\n        // Count top-right points.\n        num_pts = __popc(tile32.ballot(is_active && p.x >= center.x && p.y >= center.y));\n        warp_cnts[1] += tile32.shfl(num_pts, 0);\n\n        // Count bottom-left points.\n        num_pts = __popc(tile32.ballot(is_active && p.x < center.x && p.y < center.y));\n        warp_cnts[2] += tile32.shfl(num_pts, 0);\n\n        // Count bottom-right points.\n        num_pts = __popc(tile32.ballot(is_active && p.x >= center.x && p.y < center.y));\n        warp_cnts[3] += tile32.shfl(num_pts, 0);\n    }\n\n    if (tile32.thread_rank() == 0) {\n        s_num_pts[0][warp_id] = warp_cnts[0];\n        s_num_pts[1][warp_id] = warp_cnts[1];\n        s_num_pts[2][warp_id] = warp_cnts[2];\n        s_num_pts[3][warp_id] = warp_cnts[3];\n    }\n\n    // Make sure warps have finished counting.\n    cg::sync(cta);\n\n    //\n    // 3- Scan the warps' results to know the \"global\" numbers.\n    //\n\n    // First 4 warps scan the numbers of points per child (inclusive scan).\n    if (warp_id < 4) {\n        int num_pts = tile32.thread_rank() < NUM_WARPS_PER_BLOCK ? s_num_pts[warp_id][tile32.thread_rank()] : 0;\n#pragma unroll\n\n        for (int offset = 1; offset < NUM_WARPS_PER_BLOCK; offset *= 2) {\n            int n = tile32.shfl_up(num_pts, offset);\n\n            if (tile32.thread_rank() >= offset)\n                num_pts += n;\n        }\n\n        if (tile32.thread_rank() < NUM_WARPS_PER_BLOCK)\n            s_num_pts[warp_id][tile32.thread_rank()] = num_pts;\n    }\n\n    cg::sync(cta);\n\n    // Compute global offsets.\n    if (warp_id == 0) {\n        int sum = s_num_pts[0][NUM_WARPS_PER_BLOCK - 1];\n\n        for (int row = 1; row < 4; ++row) {\n            int tmp = s_num_pts[row][NUM_WARPS_PER_BLOCK - 1];\n            cg::sync(tile32);\n\n            if (tile32.thread_rank() < NUM_WARPS_PER_BLOCK)\n                s_num_pts[row][tile32.thread_rank()] += sum;\n\n            cg::sync(tile32);\n            sum += tmp;\n        }\n    }\n\n    cg::sync(cta);\n\n    // Make the scan exclusive.\n    int val = 0;\n    if (threadIdx.x < 4 * NUM_WARPS_PER_BLOCK) {\n        val = threadIdx.x == 0 ? 0 : smem[threadIdx.x - 1];\n        val += node.points_begin();\n    }\n\n    cg::sync(cta);\n\n    if (threadIdx.x < 4 * NUM_WARPS_PER_BLOCK) {\n        smem[threadIdx.x] = val;\n    }\n\n    cg::sync(cta);\n\n    //\n    // 4- Move points.\n    //\n    if (!(params.depth >= params.max_depth || num_points <= params.min_points_per_node)) {\n        // Output points.\n        Points &out_points = points[(params.point_selector + 1) % 2];\n\n        warp_cnts[0] = s_num_pts[0][warp_id];\n        warp_cnts[1] = s_num_pts[1][warp_id];\n        warp_cnts[2] = s_num_pts[2][warp_id];\n        warp_cnts[3] = s_num_pts[3][warp_id];\n\n        const Points &in_points = points[params.point_selector];\n        // Reorder points.\n        for (int range_it = range_begin + tile32.thread_rank(); tile32.any(range_it < range_end);\n             range_it += warpSize) {\n            // Is it still an active thread?\n            bool is_active = range_it < range_end;\n\n            // Load the coordinates of the point.\n            float2 p = is_active ? in_points.get_point(range_it) : make_float2(0.0f, 0.0f);\n\n            // Count top-left points.\n            bool pred = is_active && p.x < center.x && p.y >= center.y;\n            int  vote = tile32.ballot(pred);\n            int  dest = warp_cnts[0] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[0] += tile32.shfl(__popc(vote), 0);\n\n            // Count top-right points.\n            pred = is_active && p.x >= center.x && p.y >= center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[1] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[1] += tile32.shfl(__popc(vote), 0);\n\n            // Count bottom-left points.\n            pred = is_active && p.x < center.x && p.y < center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[2] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[2] += tile32.shfl(__popc(vote), 0);\n\n            // Count bottom-right points.\n            pred = is_active && p.x >= center.x && p.y < center.y;\n            vote = tile32.ballot(pred);\n            dest = warp_cnts[3] + __popc(vote & lane_mask_lt);\n\n            if (pred)\n                out_points.set_point(dest, p);\n\n            warp_cnts[3] += tile32.shfl(__popc(vote), 0);\n        }\n    }\n\n    cg::sync(cta);\n\n    if (tile32.thread_rank() == 0) {\n        s_num_pts[0][warp_id] = warp_cnts[0];\n        s_num_pts[1][warp_id] = warp_cnts[1];\n        s_num_pts[2][warp_id] = warp_cnts[2];\n        s_num_pts[3][warp_id] = warp_cnts[3];\n    }\n\n    cg::sync(cta);\n\n    //\n    // 5- Launch new blocks.\n    //\n    if (!(params.depth >= params.max_depth || num_points <= params.min_points_per_node)) {\n        // The last thread launches new blocks.\n        if (threadIdx.x == NUM_THREADS_PER_BLOCK - 1) {\n            // The children.\n            Quadtree_node *children = &nodes[params.num_nodes_at_this_level - (node.id() & ~3)];\n\n            // The offsets of the children at their level.\n            int child_offset = 4 * node.id();\n\n            // Set IDs.\n            children[child_offset + 0].set_id(4 * node.id() + 0);\n            children[child_offset + 1].set_id(4 * node.id() + 1);\n            children[child_offset + 2].set_id(4 * node.id() + 2);\n            children[child_offset + 3].set_id(4 * node.id() + 3);\n\n            const Bounding_box &bbox = node.bounding_box();\n            // Points of the bounding-box.\n            const float2 &p_min = bbox.get_min();\n            const float2 &p_max = bbox.get_max();\n\n            // Set the bounding boxes of the children.\n            children[child_offset + 0].set_bounding_box(p_min.x, center.y, center.x,\n                                                        p_max.y); // Top-left.\n            children[child_offset + 1].set_bounding_box(center.x, center.y, p_max.x,\n                                                        p_max.y); // Top-right.\n            children[child_offset + 2].set_bounding_box(p_min.x, p_min.y, center.x,\n                                                        center.y); // Bottom-left.\n            children[child_offset + 3].set_bounding_box(center.x, p_min.y, p_max.x,\n                                                        center.y); // Bottom-right.\n\n            // Set the ranges of the children.\n\n            children[child_offset + 0].set_range(node.points_begin(), s_num_pts[0][warp_id]);\n            children[child_offset + 1].set_range(s_num_pts[0][warp_id], s_num_pts[1][warp_id]);\n            children[child_offset + 2].set_range(s_num_pts[1][warp_id], s_num_pts[2][warp_id]);\n            children[child_offset + 3].set_range(s_num_pts[2][warp_id], s_num_pts[3][warp_id]);\n\n            // Launch 4 children.\n            build_quadtree_kernel<NUM_THREADS_PER_BLOCK>\n                <<<4, NUM_THREADS_PER_BLOCK, 4 * NUM_WARPS_PER_BLOCK * sizeof(int)>>>(\n                    &children[child_offset], points, Parameters(params, true));\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Make sure a Quadtree is properly defined.\n////////////////////////////////////////////////////////////////////////////////\nbool check_quadtree(const Quadtree_node *nodes, int idx, int num_pts, Points *pts, Parameters params)\n{\n    const Quadtree_node &node       = nodes[idx];\n    int                  num_points = node.num_points();\n\n    if (!(params.depth == params.max_depth || num_points <= params.min_points_per_node)) {\n        int num_points_in_children = 0;\n\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 0].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 1].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 2].num_points();\n        num_points_in_children += nodes[params.num_nodes_at_this_level + 4 * idx + 3].num_points();\n\n        if (num_points_in_children != node.num_points())\n            return false;\n\n        return check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 0, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 1, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 2, num_pts, pts, Parameters(params, true))\n            && check_quadtree(\n                   &nodes[params.num_nodes_at_this_level], 4 * idx + 3, num_pts, pts, Parameters(params, true));\n    }\n\n    const Bounding_box &bbox = node.bounding_box();\n\n    for (int it = node.points_begin(); it < node.points_end(); ++it) {\n        if (it >= num_pts)\n            return false;\n\n        float2 p = pts->get_point(it);\n\n        if (!bbox.contains(p))\n            return false;\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Parallel random number generator.\n////////////////////////////////////////////////////////////////////////////////\nstruct Random_generator\n{\n    int count;\n\n    __host__ __device__ Random_generator()\n        : count(0)\n    {\n    }\n    __host__ __device__ unsigned int hash(unsigned int a)\n    {\n        a = (a + 0x7ed55d16) + (a << 12);\n        a = (a ^ 0xc761c23c) ^ (a >> 19);\n        a = (a + 0x165667b1) + (a << 5);\n        a = (a + 0xd3a2646c) ^ (a << 9);\n        a = (a + 0xfd7046c5) + (a << 3);\n        a = (a ^ 0xb55a4f09) ^ (a >> 16);\n        return a;\n    }\n\n    __host__ __device__ __forceinline__ cuda::std::tuple<float, float> operator()()\n    {\n#ifdef __CUDA_ARCH__\n        unsigned seed = hash(blockIdx.x * blockDim.x + threadIdx.x + count);\n        // thrust::generate may call operator() more than once per thread.\n        // Hence, increment count by grid size to ensure uniqueness of seed\n        count += blockDim.x * gridDim.x;\n#else\n        unsigned seed = hash(0);\n#endif\n        thrust::default_random_engine                    rng(seed);\n        thrust::random::uniform_real_distribution<float> distrib;\n        return cuda::std::make_tuple(distrib(rng), distrib(rng));\n    }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Allocate GPU structs, launch kernel and clean up\n////////////////////////////////////////////////////////////////////////////////\nbool cdpQuadtree(int warp_size)\n{\n    // Constants to control the algorithm.\n    const int num_points          = 1024;\n    const int max_depth           = 8;\n    const int min_points_per_node = 16;\n\n    // Allocate memory for points.\n    thrust::device_vector<float> x_d0(num_points);\n    thrust::device_vector<float> x_d1(num_points);\n    thrust::device_vector<float> y_d0(num_points);\n    thrust::device_vector<float> y_d1(num_points);\n\n    // Generate random points.\n    Random_generator rnd;\n    thrust::generate(\n        thrust::make_zip_iterator(x_d0.begin(), y_d0.begin()), thrust::make_zip_iterator(x_d0.end(), y_d0.end()), rnd);\n\n    // Host structures to analyze the device ones.\n    Points points_init[2];\n    points_init[0].set(thrust::raw_pointer_cast(&x_d0[0]), thrust::raw_pointer_cast(&y_d0[0]));\n    points_init[1].set(thrust::raw_pointer_cast(&x_d1[0]), thrust::raw_pointer_cast(&y_d1[0]));\n\n    // Allocate memory to store points.\n    Points *points;\n    checkCudaErrors(hipMalloc((void **)&points, 2 * sizeof(Points)));\n    checkCudaErrors(hipMemcpy(points, points_init, 2 * sizeof(Points), hipMemcpyHostToDevice));\n\n    // We could use a close form...\n    int max_nodes = 0;\n\n    for (int i = 0, num_nodes_at_level = 1; i < max_depth; ++i, num_nodes_at_level *= 4)\n        max_nodes += num_nodes_at_level;\n\n    // Allocate memory to store the tree.\n    Quadtree_node root;\n    root.set_range(0, num_points);\n    Quadtree_node *nodes;\n    checkCudaErrors(hipMalloc((void **)&nodes, max_nodes * sizeof(Quadtree_node)));\n    checkCudaErrors(hipMemcpy(nodes, &root, sizeof(Quadtree_node), hipMemcpyHostToDevice));\n\n    // Build the quadtree.\n    Parameters params(max_depth, min_points_per_node);\n    std::cout << \"Launching CDP kernel to build the quadtree\" << std::endl;\n    const int    NUM_THREADS_PER_BLOCK = 128; // Do not use less than 128 threads.\n    const int    NUM_WARPS_PER_BLOCK   = NUM_THREADS_PER_BLOCK / warp_size;\n    const size_t smem_size             = 4 * NUM_WARPS_PER_BLOCK * sizeof(int);\n    build_quadtree_kernel<NUM_THREADS_PER_BLOCK><<<1, NUM_THREADS_PER_BLOCK, smem_size>>>(nodes, points, params);\n    checkCudaErrors(hipGetLastError());\n\n    // Copy points to CPU.\n    thrust::host_vector<float> x_h(x_d0);\n    thrust::host_vector<float> y_h(y_d0);\n    Points                     host_points;\n    host_points.set(thrust::raw_pointer_cast(&x_h[0]), thrust::raw_pointer_cast(&y_h[0]));\n\n    // Copy nodes to CPU.\n    Quadtree_node *host_nodes = new Quadtree_node[max_nodes];\n    checkCudaErrors(hipMemcpy(host_nodes, nodes, max_nodes * sizeof(Quadtree_node), hipMemcpyDeviceToHost));\n\n    // Validate the results.\n    bool ok = check_quadtree(host_nodes, 0, num_points, &host_points, params);\n    std::cout << \"Results: \" << (ok ? \"OK\" : \"FAILED\") << std::endl;\n\n    // Free CPU memory.\n    delete[] host_nodes;\n\n    // Free memory.\n    checkCudaErrors(hipFree(nodes));\n    checkCudaErrors(hipFree(points));\n\n    return ok;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entry point.\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    // Find/set the device.\n    // The test requires an architecture SM35 or greater (CDP capable).\n    int            cuda_device = findCudaDevice(argc, (const char **)argv);\n    hipDeviceProp_t deviceProps;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProps, cuda_device));\n    int cdpCapable = (deviceProps.major == 3 && deviceProps.minor >= 5) || deviceProps.major >= 4;\n\n    printf(\n        \"GPU device %s has compute capabilities (SM %d.%d)\\n\", deviceProps.name, deviceProps.major, deviceProps.minor);\n\n    if (!cdpCapable) {\n        std::cerr << \"cdpQuadTree requires SM 3.5 or higher to use CUDA Dynamic \"\n                     \"Parallelism.  Exiting...\\n\"\n                  << std::endl;\n        exit(EXIT_WAIVED);\n    }\n\n    bool ok = cdpQuadtree(deviceProps.warpSize);\n\n    return (ok ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpQuadtree/cdpQuadtree.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <climits>\n#include <stdio.h>\n#include <vector>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#define THREADS_PER_BLOCK  512\n#define ALLOWABLE_VARIANCE 1.e-6f\n#define NUM_ELEMENTS       8000000\n\n// Stores the square of each input element in output array\n__global__ void squareArray(const float *input, float *output, int numElements)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < numElements) {\n        output[idx] = input[idx] * input[idx];\n    }\n}\n\n// Stores the negative of each input element in output array\n__global__ void negateArray(const float *input, float *output, int numElements)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < numElements) {\n        output[idx] = input[idx] * -1;\n    }\n}\n\nstruct negSquareArrays\n{\n    float *input;\n    float *square;\n    float *negSquare;\n    int    numElements;\n    size_t bytes;\n    size_t numBlocks;\n};\n\nvoid fillRandomly(float *array, int numElements)\n{\n    for (int n = 0; n < numElements; n++) {\n        array[n] = rand() / (float)RAND_MAX;\n    }\n}\n\nvoid resetOutputArrays(negSquareArrays *hostArrays)\n{\n    fillRandomly(hostArrays->square, hostArrays->numElements);\n    fillRandomly(hostArrays->negSquare, hostArrays->numElements);\n}\n\nvoid prepareHostArrays(negSquareArrays *hostArrays)\n{\n    hostArrays->numElements = NUM_ELEMENTS;\n    size_t bytes            = hostArrays->numElements * sizeof(float);\n\n    size_t numBlocks = hostArrays->numElements / (size_t)THREADS_PER_BLOCK;\n    if ((numBlocks % (size_t)THREADS_PER_BLOCK) != 0) {\n        numBlocks++;\n    }\n\n    hostArrays->input     = (float *)malloc(bytes);\n    hostArrays->square    = (float *)malloc(bytes);\n    hostArrays->negSquare = (float *)malloc(bytes);\n    hostArrays->bytes     = bytes;\n    hostArrays->numBlocks = numBlocks;\n\n    fillRandomly(hostArrays->input, hostArrays->numElements);\n    fillRandomly(hostArrays->square, hostArrays->numElements);\n    fillRandomly(hostArrays->negSquare, hostArrays->numElements);\n}\n\nvoid createFreeGraph(cudaGraphExec_t *graphExec, float *dPtr)\n{\n    cudaGraph_t     graph;\n    cudaGraphNode_t freeNode;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n\n    checkCudaErrors(cudaGraphAddMemFreeNode(&freeNode, graph, NULL, 0, (void *)dPtr));\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\n/**\n * Demonstrates explicitly creating a CUDA graph including memory nodes.\n * createNegateSquaresGraphWithStreamCapture constructs an equivalent graph\n * using stream capture.\n *\n * If d_negSquare_out is non null, then:\n * 1) d_negSquare will not be freed;\n * 2) the value of d_negSquare_out will be set to d_negSquare.\n *\n * Diagram of the graph constructed by createNegateSquaresGraphExplicitly:\n *\n * alloc d_input\n *       |\n * alloc d_square\n *       |\n * Memcpy a to device\n *       |\n * launch kernel squareArray ------->---- Memcpy d_square to host\n *       |                                      |\n * free d_input                                 |\n *       |                                      |\n * allocate d_negSquare                         |\n *       |                                      |\n * launch kernel negateArray -------->--- free d_square\n *       |\n * Memcpy d_negSquare to host\n *       |\n * free d_negSquare\n */\nvoid createNegateSquaresGraphExplicitly(cudaGraphExec_t *graphExec,\n                                        int              device,\n                                        negSquareArrays *hostArrays,\n                                        float          **d_negSquare_out = NULL)\n{\n    // Array buffers on device\n    float *d_input, *d_square, *d_negSquare;\n\n    // Memory allocation parameters\n    cudaMemAllocNodeParams allocParams;\n    memset(&allocParams, 0, sizeof(allocParams));\n    allocParams.bytesize                = hostArrays->bytes;\n    allocParams.poolProps.allocType     = cudaMemAllocationTypePinned;\n    allocParams.poolProps.location.id   = device;\n    allocParams.poolProps.location.type = cudaMemLocationTypeDevice;\n\n    // Kernel launch parameters\n    cudaKernelNodeParams kernelNodeParams = {0};\n    kernelNodeParams.gridDim              = dim3(hostArrays->numBlocks, 1, 1);\n    kernelNodeParams.blockDim             = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes       = 0;\n    kernelNodeParams.extra                = NULL;\n\n    cudaGraph_t     graph;\n    cudaGraphNode_t allocNodeInput, allocNodeSquare, allocNodeNegSquare;\n    cudaGraphNode_t copyNodeInput, copyNodeSquare, copyNodeNegSquare;\n    cudaGraphNode_t squareKernelNode, negateKernelNode;\n    cudaGraphNode_t freeNodeInput, freeNodeSquare;\n\n    // Buffer for storing graph node dependencies\n    std::vector<cudaGraphNode_t> nodeDependencies;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeInput, graph, NULL, 0, &allocParams));\n    d_input = (float *)allocParams.dptr;\n\n    // To keep the graph structure simple (fewer branching dependencies),\n    // allocNodeSquare should depend on allocNodeInput\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeSquare, graph, &allocNodeInput, 1, &allocParams));\n    d_square = (float *)allocParams.dptr;\n\n    // copyNodeInput needs to depend on allocNodeInput because copyNodeInput\n    // writes to d_input. It does so here indirectly through allocNodeSquare.\n    checkCudaErrors(cudaGraphAddMemcpyNode1D(&copyNodeInput,\n                                             graph,\n                                             &allocNodeSquare,\n                                             1,\n                                             d_input,\n                                             hostArrays->input,\n                                             hostArrays->bytes,\n                                             cudaMemcpyHostToDevice));\n\n    void *squareKernelArgs[3]     = {(void *)&d_input, (void *)&d_square, (void *)&(hostArrays->numElements)};\n    kernelNodeParams.func         = (void *)squareArray;\n    kernelNodeParams.kernelParams = (void **)squareKernelArgs;\n\n    // Square kernel depends on copyNodeInput to ensure all data is on the device\n    // before kernel launch.\n    checkCudaErrors(cudaGraphAddKernelNode(&squareKernelNode, graph, &copyNodeInput, 1, &kernelNodeParams));\n\n    checkCudaErrors(cudaGraphAddMemcpyNode1D(&copyNodeSquare,\n                                             graph,\n                                             &squareKernelNode,\n                                             1,\n                                             hostArrays->square,\n                                             d_square,\n                                             hostArrays->bytes,\n                                             cudaMemcpyDeviceToHost));\n\n    // Free of d_input depends on the square kernel to ensure that d_input is not\n    // freed while being read by the kernel. It also depends on the alloc of\n    // d_input via squareKernelNode > copyNodeInput > allocNodeSquare >\n    // allocNodeInput.\n    checkCudaErrors(cudaGraphAddMemFreeNode(&freeNodeInput, graph, &squareKernelNode, 1, d_input));\n\n    // Allocation of C depends on free of A so CUDA can reuse the virtual address.\n    checkCudaErrors(cudaGraphAddMemAllocNode(&allocNodeNegSquare, graph, &freeNodeInput, 1, &allocParams));\n    d_negSquare = (float *)allocParams.dptr;\n\n    if (d_negSquare == d_input) {\n        printf(\"Check verified that d_negSquare and d_input share a virtual \"\n               \"address.\\n\");\n    }\n\n    void *negateKernelArgs[3]     = {(void *)&d_square, (void *)&d_negSquare, (void *)&(hostArrays->numElements)};\n    kernelNodeParams.func         = (void *)negateArray;\n    kernelNodeParams.kernelParams = (void **)negateKernelArgs;\n\n    checkCudaErrors(cudaGraphAddKernelNode(&negateKernelNode, graph, &allocNodeNegSquare, 1, &kernelNodeParams));\n\n    nodeDependencies.push_back(copyNodeSquare);\n    nodeDependencies.push_back(negateKernelNode);\n    checkCudaErrors(\n        cudaGraphAddMemFreeNode(&freeNodeSquare, graph, nodeDependencies.data(), nodeDependencies.size(), d_square));\n    nodeDependencies.clear();\n\n    checkCudaErrors(cudaGraphAddMemcpyNode1D(&copyNodeNegSquare,\n                                             graph,\n                                             &negateKernelNode,\n                                             1,\n                                             hostArrays->negSquare,\n                                             d_negSquare,\n                                             hostArrays->bytes,\n                                             cudaMemcpyDeviceToHost));\n\n    if (d_negSquare_out == NULL) {\n        cudaGraphNode_t freeNodeNegSquare;\n        checkCudaErrors(cudaGraphAddMemFreeNode(&freeNodeNegSquare, graph, &copyNodeNegSquare, 1, d_negSquare));\n    }\n    else {\n        *d_negSquare_out = d_negSquare;\n    }\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\n/**\n * Adds work to a CUDA stream which negates the square of values in the input\n * array.\n *\n * If d_negSquare_out is non null, then:\n * 1) d_negSquare will not be freed;\n * 2) the value of d_negSquare_out will be set to d_negSquare.\n *\n * Diagram of the stream operations in doNegateSquaresInStream\n * ---------------------------------------------------------------------\n * | STREAM                             | STREAM2                      |\n * ---------------------------------------------------------------------\n *\n * alloc d_input\n *       |\n * alloc d_square\n *       |\n * Memcpy a to device\n *       |\n * launch kernel squareArray\n *       |\n * record squareKernelCompleteEvent -->-- wait squareKernelCompleteEvent\n *       |                                      |\n * free d_input                                 |\n *       |                                      |\n * allocate d_negSquare                   Memcpy d_square to host\n *       |                                      |\n * launch kernel negateArray                    |\n *       |                                      |\n * record negateKernelCompleteEvent -->-- wait negateKernelCompleteEvent\n *       |                                      |\n * Memcpy d_negSquare to host                   |\n *       |                                free d_square\n * free d_negSquare                             |\n *       |                                      |\n * wait squareFreeEvent --------------<---- record squareFreeEvent\n */\nvoid doNegateSquaresInStream(cudaStream_t stream1, negSquareArrays *hostArrays, float **d_negSquare_out = NULL)\n{\n    float       *d_input, *d_square, *d_negSquare;\n    cudaStream_t stream2;\n    cudaEvent_t  squareKernelCompleteEvent, negateKernelCompleteEvent, squareFreeEvent;\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream2, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaEventCreate(&squareKernelCompleteEvent));\n    checkCudaErrors(cudaEventCreate(&negateKernelCompleteEvent));\n    checkCudaErrors(cudaEventCreate(&squareFreeEvent));\n\n    // Virtual addresses are assigned synchronously when cudaMallocAsync is\n    // called, thus there is no performace benefit gained by separating the\n    // allocations into two streams.\n    checkCudaErrors(cudaMallocAsync(&d_input, hostArrays->bytes, stream1));\n    checkCudaErrors(cudaMallocAsync(&d_square, hostArrays->bytes, stream1));\n\n    checkCudaErrors(cudaMemcpyAsync(d_input, hostArrays->input, hostArrays->bytes, cudaMemcpyHostToDevice, stream1));\n    squareArray<<<hostArrays->numBlocks, THREADS_PER_BLOCK, 0, stream1>>>(d_input, d_square, hostArrays->numElements);\n    checkCudaErrors(cudaEventRecord(squareKernelCompleteEvent, stream1));\n\n    checkCudaErrors(cudaStreamWaitEvent(stream2, squareKernelCompleteEvent, 0));\n    checkCudaErrors(cudaMemcpyAsync(hostArrays->square, d_square, hostArrays->bytes, cudaMemcpyDeviceToHost, stream2));\n\n    checkCudaErrors(cudaFreeAsync(d_input, stream1));\n    checkCudaErrors(cudaMallocAsync(&d_negSquare, hostArrays->bytes, stream1));\n    negateArray<<<hostArrays->numBlocks, THREADS_PER_BLOCK, 0, stream1>>>(\n        d_square, d_negSquare, hostArrays->numElements);\n    checkCudaErrors(cudaEventRecord(negateKernelCompleteEvent, stream1));\n    checkCudaErrors(\n        cudaMemcpyAsync(hostArrays->negSquare, d_negSquare, hostArrays->bytes, cudaMemcpyDeviceToHost, stream1));\n    if (d_negSquare_out == NULL) {\n        checkCudaErrors(cudaFreeAsync(d_negSquare, stream1));\n    }\n    else {\n        *d_negSquare_out = d_negSquare;\n    }\n\n    checkCudaErrors(cudaStreamWaitEvent(stream2, negateKernelCompleteEvent, 0));\n    checkCudaErrors(cudaFreeAsync(d_square, stream2));\n    checkCudaErrors(cudaEventRecord(squareFreeEvent, stream2));\n\n    checkCudaErrors(cudaStreamWaitEvent(stream1, squareFreeEvent, 0));\n\n    checkCudaErrors(cudaStreamDestroy(stream2));\n    checkCudaErrors(cudaEventDestroy(squareKernelCompleteEvent));\n    checkCudaErrors(cudaEventDestroy(negateKernelCompleteEvent));\n    checkCudaErrors(cudaEventDestroy(squareFreeEvent));\n}\n\n/**\n * Demonstrates creating a CUDA graph including memory nodes using stream\n * capture. createNegateSquaresGraphExplicitly constructs an equivalent graph\n * without stream capture.\n */\nvoid createNegateSquaresGraphWithStreamCapture(cudaGraphExec_t *graphExec,\n                                               negSquareArrays *hostArrays,\n                                               float          **d_negSquare_out = NULL)\n{\n    cudaGraph_t  graph;\n    cudaStream_t stream;\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal));\n    doNegateSquaresInStream(stream, hostArrays, d_negSquare_out);\n    checkCudaErrors(cudaStreamEndCapture(stream, &graph));\n\n    checkCudaErrors(cudaGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(cudaStreamDestroy(stream));\n    checkCudaErrors(cudaGraphDestroy(graph));\n}\n\nvoid prepareRefArrays(negSquareArrays *hostArrays, negSquareArrays *deviceRefArrays, bool **foundValidationFailure)\n{\n    deviceRefArrays->bytes       = hostArrays->bytes;\n    deviceRefArrays->numElements = hostArrays->numElements;\n\n    for (int i = 0; i < hostArrays->numElements; i++) {\n        hostArrays->square[i]    = hostArrays->input[i] * hostArrays->input[i];\n        hostArrays->negSquare[i] = hostArrays->square[i] * -1;\n    }\n\n    checkCudaErrors(cudaMalloc((void **)&deviceRefArrays->negSquare, deviceRefArrays->bytes));\n    checkCudaErrors(\n        cudaMemcpy(deviceRefArrays->negSquare, hostArrays->negSquare, hostArrays->bytes, cudaMemcpyHostToDevice));\n\n    checkCudaErrors(cudaMallocManaged((void **)foundValidationFailure, sizeof(bool)));\n}\n\nint checkValidationFailure(bool *foundValidationFailure)\n{\n    if (*foundValidationFailure) {\n        printf(\"Validation FAILURE!\\n\\n\");\n        *foundValidationFailure = false;\n        return EXIT_FAILURE;\n    }\n    else {\n        printf(\"Validation PASSED!\\n\\n\");\n        return EXIT_SUCCESS;\n    }\n}\n\n__global__ void validateGPU(float *d_negSquare, negSquareArrays devRefArrays, bool *foundValidationFailure)\n{\n    int   idx = blockIdx.x * blockDim.x + threadIdx.x;\n    float ref, diff;\n\n    if (idx < devRefArrays.numElements) {\n        ref  = devRefArrays.negSquare[idx];\n        diff = d_negSquare[idx] - ref;\n        diff *= diff;\n        ref *= ref;\n        if (diff / ref > ALLOWABLE_VARIANCE) {\n            *foundValidationFailure = true;\n        }\n    }\n}\n\nvoid validateHost(negSquareArrays *hostArrays, bool *foundValidationFailure)\n{\n    float ref, diff;\n\n    for (int i = 0; i < hostArrays->numElements; i++) {\n        ref  = hostArrays->input[i] * hostArrays->input[i] * -1;\n        diff = hostArrays->negSquare[i] - ref;\n        diff *= diff;\n        ref *= ref;\n        if (diff / ref > ALLOWABLE_VARIANCE) {\n            *foundValidationFailure = true;\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    negSquareArrays hostArrays, deviceRefArrays;\n    cudaStream_t    stream;\n    cudaGraphExec_t graphExec, graphExecFreeC;\n\n    // Declare pointers for GPU buffers\n    float *d_negSquare            = NULL;\n    bool  *foundValidationFailure = NULL;\n\n    srand(time(0));\n    int device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion             = 0;\n    int deviceSupportsMemoryPools = 0;\n\n    cudaDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 11040) {\n        printf(\"Waiving execution as driver does not support Graph Memory Nodes\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    cudaDeviceGetAttribute(&deviceSupportsMemoryPools, cudaDevAttrMemoryPoolsSupported, device);\n    if (!deviceSupportsMemoryPools) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else {\n        printf(\"Setting up sample.\\n\");\n    }\n\n    prepareHostArrays(&hostArrays);\n    prepareRefArrays(&hostArrays, &deviceRefArrays, &foundValidationFailure);\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    printf(\"Setup complete.\\n\\n\");\n\n    printf(\"Running negateSquares in a stream.\\n\");\n    doNegateSquaresInStream(stream, &hostArrays);\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printf(\"Validating negateSquares in a stream...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares in a stream-captured graph.\\n\");\n    createNegateSquaresGraphWithStreamCapture(&graphExec, &hostArrays);\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printf(\"Validating negateSquares in a stream-captured graph...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares in an explicitly constructed graph.\\n\");\n    createNegateSquaresGraphExplicitly(&graphExec, device, &hostArrays);\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printf(\"Validating negateSquares in an explicitly constructed graph...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    // Each of the three examples below free d_negSquare outside the graph. As\n    // demonstrated by validateGPU, d_negSquare can be accessed by outside the\n    // graph before d_negSquare is freed.\n\n    printf(\"Running negateSquares with d_negSquare freed outside the stream.\\n\");\n    createNegateSquaresGraphExplicitly(&graphExec, device, &hostArrays, &d_negSquare);\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    // Since cudaFree is synchronous, the stream must synchronize before freeing\n    // d_negSquare to ensure d_negSquare no longer being accessed.\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    checkCudaErrors(cudaFree(d_negSquare));\n    printf(\"Validating negateSquares with d_negSquare freed outside the \"\n           \"stream...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares with d_negSquare freed outside the graph.\\n\");\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    checkCudaErrors(cudaFreeAsync(d_negSquare, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printf(\"Validating negateSquares with d_negSquare freed outside the graph...\\n\");\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares with d_negSquare freed in a different graph.\\n\");\n    createFreeGraph(&graphExecFreeC, d_negSquare);\n    checkCudaErrors(cudaGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    checkCudaErrors(cudaGraphLaunch(graphExecFreeC, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    printf(\"Validating negateSquares with d_negSquare freed in a different \"\n           \"graph...\\n\");\n    checkValidationFailure(foundValidationFailure);\n\n    printf(\"Cleaning up sample.\\n\");\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphExecDestroy(graphExecFreeC));\n    checkCudaErrors(cudaStreamDestroy(stream));\n    checkCudaErrors(cudaFree(foundValidationFailure));\n    checkCudaErrors(cudaFree(deviceRefArrays.negSquare));\n    free(hostArrays.input);\n    free(hostArrays.square);\n    free(hostArrays.negSquare);\n    printf(\"Cleanup complete. Exiting sample.\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// System includes\n#include <assert.h>\n#include <climits>\n#include <stdio.h>\n#include <vector>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#define THREADS_PER_BLOCK  512\n#define ALLOWABLE_VARIANCE 1.e-6f\n#define NUM_ELEMENTS       8000000\n\n// Stores the square of each input element in output array\n__global__ void squareArray(const float *input, float *output, int numElements)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < numElements) {\n        output[idx] = input[idx] * input[idx];\n    }\n}\n\n// Stores the negative of each input element in output array\n__global__ void negateArray(const float *input, float *output, int numElements)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < numElements) {\n        output[idx] = input[idx] * -1;\n    }\n}\n\nstruct negSquareArrays\n{\n    float *input;\n    float *square;\n    float *negSquare;\n    int    numElements;\n    size_t bytes;\n    size_t numBlocks;\n};\n\nvoid fillRandomly(float *array, int numElements)\n{\n    for (int n = 0; n < numElements; n++) {\n        array[n] = rand() / (float)RAND_MAX;\n    }\n}\n\nvoid resetOutputArrays(negSquareArrays *hostArrays)\n{\n    fillRandomly(hostArrays->square, hostArrays->numElements);\n    fillRandomly(hostArrays->negSquare, hostArrays->numElements);\n}\n\nvoid prepareHostArrays(negSquareArrays *hostArrays)\n{\n    hostArrays->numElements = NUM_ELEMENTS;\n    size_t bytes            = hostArrays->numElements * sizeof(float);\n\n    size_t numBlocks = hostArrays->numElements / (size_t)THREADS_PER_BLOCK;\n    if ((numBlocks % (size_t)THREADS_PER_BLOCK) != 0) {\n        numBlocks++;\n    }\n\n    hostArrays->input     = (float *)malloc(bytes);\n    hostArrays->square    = (float *)malloc(bytes);\n    hostArrays->negSquare = (float *)malloc(bytes);\n    hostArrays->bytes     = bytes;\n    hostArrays->numBlocks = numBlocks;\n\n    fillRandomly(hostArrays->input, hostArrays->numElements);\n    fillRandomly(hostArrays->square, hostArrays->numElements);\n    fillRandomly(hostArrays->negSquare, hostArrays->numElements);\n}\n\nvoid createFreeGraph(hipGraphExec_t *graphExec, float *dPtr)\n{\n    hipGraph_t     graph;\n    hipGraphNode_t freeNode;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n\n    checkCudaErrors(hipGraphAddMemFreeNode(&freeNode, graph, NULL, 0, (void *)dPtr));\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\n/**\n * Demonstrates explicitly creating a CUDA graph including memory nodes.\n * createNegateSquaresGraphWithStreamCapture constructs an equivalent graph\n * using stream capture.\n *\n * If d_negSquare_out is non null, then:\n * 1) d_negSquare will not be freed;\n * 2) the value of d_negSquare_out will be set to d_negSquare.\n *\n * Diagram of the graph constructed by createNegateSquaresGraphExplicitly:\n *\n * alloc d_input\n *       |\n * alloc d_square\n *       |\n * Memcpy a to device\n *       |\n * launch kernel squareArray ------->---- Memcpy d_square to host\n *       |                                      |\n * free d_input                                 |\n *       |                                      |\n * allocate d_negSquare                         |\n *       |                                      |\n * launch kernel negateArray -------->--- free d_square\n *       |\n * Memcpy d_negSquare to host\n *       |\n * free d_negSquare\n */\nvoid createNegateSquaresGraphExplicitly(hipGraphExec_t *graphExec,\n                                        int              device,\n                                        negSquareArrays *hostArrays,\n                                        float          **d_negSquare_out = NULL)\n{\n    // Array buffers on device\n    float *d_input, *d_square, *d_negSquare;\n\n    // Memory allocation parameters\n    hipMemAllocNodeParams allocParams;\n    memset(&allocParams, 0, sizeof(allocParams));\n    allocParams.bytesize                = hostArrays->bytes;\n    allocParams.poolProps.allocType     = hipMemAllocationTypePinned;\n    allocParams.poolProps.location.id   = device;\n    allocParams.poolProps.location.type = hipMemLocationTypeDevice;\n\n    // Kernel launch parameters\n    hipKernelNodeParams kernelNodeParams = {0};\n    kernelNodeParams.gridDim              = dim3(hostArrays->numBlocks, 1, 1);\n    kernelNodeParams.blockDim             = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes       = 0;\n    kernelNodeParams.extra                = NULL;\n\n    hipGraph_t     graph;\n    hipGraphNode_t allocNodeInput, allocNodeSquare, allocNodeNegSquare;\n    hipGraphNode_t copyNodeInput, copyNodeSquare, copyNodeNegSquare;\n    hipGraphNode_t squareKernelNode, negateKernelNode;\n    hipGraphNode_t freeNodeInput, freeNodeSquare;\n\n    // Buffer for storing graph node dependencies\n    std::vector<hipGraphNode_t> nodeDependencies;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeInput, graph, NULL, 0, &allocParams));\n    d_input = (float *)allocParams.dptr;\n\n    // To keep the graph structure simple (fewer branching dependencies),\n    // allocNodeSquare should depend on allocNodeInput\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeSquare, graph, &allocNodeInput, 1, &allocParams));\n    d_square = (float *)allocParams.dptr;\n\n    // copyNodeInput needs to depend on allocNodeInput because copyNodeInput\n    // writes to d_input. It does so here indirectly through allocNodeSquare.\n    checkCudaErrors(hipGraphAddMemcpyNode1D(&copyNodeInput,\n                                             graph,\n                                             &allocNodeSquare,\n                                             1,\n                                             d_input,\n                                             hostArrays->input,\n                                             hostArrays->bytes,\n                                             hipMemcpyHostToDevice));\n\n    void *squareKernelArgs[3]     = {(void *)&d_input, (void *)&d_square, (void *)&(hostArrays->numElements)};\n    kernelNodeParams.func         = (void *)squareArray;\n    kernelNodeParams.kernelParams = (void **)squareKernelArgs;\n\n    // Square kernel depends on copyNodeInput to ensure all data is on the device\n    // before kernel launch.\n    checkCudaErrors(hipGraphAddKernelNode(&squareKernelNode, graph, &copyNodeInput, 1, &kernelNodeParams));\n\n    checkCudaErrors(hipGraphAddMemcpyNode1D(&copyNodeSquare,\n                                             graph,\n                                             &squareKernelNode,\n                                             1,\n                                             hostArrays->square,\n                                             d_square,\n                                             hostArrays->bytes,\n                                             hipMemcpyDeviceToHost));\n\n    // Free of d_input depends on the square kernel to ensure that d_input is not\n    // freed while being read by the kernel. It also depends on the alloc of\n    // d_input via squareKernelNode > copyNodeInput > allocNodeSquare >\n    // allocNodeInput.\n    checkCudaErrors(hipGraphAddMemFreeNode(&freeNodeInput, graph, &squareKernelNode, 1, d_input));\n\n    // Allocation of C depends on free of A so CUDA can reuse the virtual address.\n    checkCudaErrors(hipGraphAddMemAllocNode(&allocNodeNegSquare, graph, &freeNodeInput, 1, &allocParams));\n    d_negSquare = (float *)allocParams.dptr;\n\n    if (d_negSquare == d_input) {\n        printf(\"Check verified that d_negSquare and d_input share a virtual \"\n               \"address.\\n\");\n    }\n\n    void *negateKernelArgs[3]     = {(void *)&d_square, (void *)&d_negSquare, (void *)&(hostArrays->numElements)};\n    kernelNodeParams.func         = (void *)negateArray;\n    kernelNodeParams.kernelParams = (void **)negateKernelArgs;\n\n    checkCudaErrors(hipGraphAddKernelNode(&negateKernelNode, graph, &allocNodeNegSquare, 1, &kernelNodeParams));\n\n    nodeDependencies.push_back(copyNodeSquare);\n    nodeDependencies.push_back(negateKernelNode);\n    checkCudaErrors(\n        hipGraphAddMemFreeNode(&freeNodeSquare, graph, nodeDependencies.data(), nodeDependencies.size(), d_square));\n    nodeDependencies.clear();\n\n    checkCudaErrors(hipGraphAddMemcpyNode1D(&copyNodeNegSquare,\n                                             graph,\n                                             &negateKernelNode,\n                                             1,\n                                             hostArrays->negSquare,\n                                             d_negSquare,\n                                             hostArrays->bytes,\n                                             hipMemcpyDeviceToHost));\n\n    if (d_negSquare_out == NULL) {\n        hipGraphNode_t freeNodeNegSquare;\n        checkCudaErrors(hipGraphAddMemFreeNode(&freeNodeNegSquare, graph, &copyNodeNegSquare, 1, d_negSquare));\n    }\n    else {\n        *d_negSquare_out = d_negSquare;\n    }\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\n/**\n * Adds work to a CUDA stream which negates the square of values in the input\n * array.\n *\n * If d_negSquare_out is non null, then:\n * 1) d_negSquare will not be freed;\n * 2) the value of d_negSquare_out will be set to d_negSquare.\n *\n * Diagram of the stream operations in doNegateSquaresInStream\n * ---------------------------------------------------------------------\n * | STREAM                             | STREAM2                      |\n * ---------------------------------------------------------------------\n *\n * alloc d_input\n *       |\n * alloc d_square\n *       |\n * Memcpy a to device\n *       |\n * launch kernel squareArray\n *       |\n * record squareKernelCompleteEvent -->-- wait squareKernelCompleteEvent\n *       |                                      |\n * free d_input                                 |\n *       |                                      |\n * allocate d_negSquare                   Memcpy d_square to host\n *       |                                      |\n * launch kernel negateArray                    |\n *       |                                      |\n * record negateKernelCompleteEvent -->-- wait negateKernelCompleteEvent\n *       |                                      |\n * Memcpy d_negSquare to host                   |\n *       |                                free d_square\n * free d_negSquare                             |\n *       |                                      |\n * wait squareFreeEvent --------------<---- record squareFreeEvent\n */\nvoid doNegateSquaresInStream(hipStream_t stream1, negSquareArrays *hostArrays, float **d_negSquare_out = NULL)\n{\n    float       *d_input, *d_square, *d_negSquare;\n    hipStream_t stream2;\n    hipEvent_t  squareKernelCompleteEvent, negateKernelCompleteEvent, squareFreeEvent;\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking));\n\n    checkCudaErrors(hipEventCreate(&squareKernelCompleteEvent));\n    checkCudaErrors(hipEventCreate(&negateKernelCompleteEvent));\n    checkCudaErrors(hipEventCreate(&squareFreeEvent));\n\n    // Virtual addresses are assigned synchronously when cudaMallocAsync is\n    // called, thus there is no performace benefit gained by separating the\n    // allocations into two streams.\n    checkCudaErrors(hipMallocAsync(&d_input, hostArrays->bytes, stream1));\n    checkCudaErrors(hipMallocAsync(&d_square, hostArrays->bytes, stream1));\n\n    checkCudaErrors(hipMemcpyAsync(d_input, hostArrays->input, hostArrays->bytes, hipMemcpyHostToDevice, stream1));\n    squareArray<<<hostArrays->numBlocks, THREADS_PER_BLOCK, 0, stream1>>>(d_input, d_square, hostArrays->numElements);\n    checkCudaErrors(hipEventRecord(squareKernelCompleteEvent, stream1));\n\n    checkCudaErrors(hipStreamWaitEvent(stream2, squareKernelCompleteEvent, 0));\n    checkCudaErrors(hipMemcpyAsync(hostArrays->square, d_square, hostArrays->bytes, hipMemcpyDeviceToHost, stream2));\n\n    checkCudaErrors(hipFreeAsync(d_input, stream1));\n    checkCudaErrors(hipMallocAsync(&d_negSquare, hostArrays->bytes, stream1));\n    negateArray<<<hostArrays->numBlocks, THREADS_PER_BLOCK, 0, stream1>>>(\n        d_square, d_negSquare, hostArrays->numElements);\n    checkCudaErrors(hipEventRecord(negateKernelCompleteEvent, stream1));\n    checkCudaErrors(\n        hipMemcpyAsync(hostArrays->negSquare, d_negSquare, hostArrays->bytes, hipMemcpyDeviceToHost, stream1));\n    if (d_negSquare_out == NULL) {\n        checkCudaErrors(hipFreeAsync(d_negSquare, stream1));\n    }\n    else {\n        *d_negSquare_out = d_negSquare;\n    }\n\n    checkCudaErrors(hipStreamWaitEvent(stream2, negateKernelCompleteEvent, 0));\n    checkCudaErrors(hipFreeAsync(d_square, stream2));\n    checkCudaErrors(hipEventRecord(squareFreeEvent, stream2));\n\n    checkCudaErrors(hipStreamWaitEvent(stream1, squareFreeEvent, 0));\n\n    checkCudaErrors(hipStreamDestroy(stream2));\n    checkCudaErrors(hipEventDestroy(squareKernelCompleteEvent));\n    checkCudaErrors(hipEventDestroy(negateKernelCompleteEvent));\n    checkCudaErrors(hipEventDestroy(squareFreeEvent));\n}\n\n/**\n * Demonstrates creating a CUDA graph including memory nodes using stream\n * capture. createNegateSquaresGraphExplicitly constructs an equivalent graph\n * without stream capture.\n */\nvoid createNegateSquaresGraphWithStreamCapture(hipGraphExec_t *graphExec,\n                                               negSquareArrays *hostArrays,\n                                               float          **d_negSquare_out = NULL)\n{\n    hipGraph_t  graph;\n    hipStream_t stream;\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    checkCudaErrors(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal));\n    doNegateSquaresInStream(stream, hostArrays, d_negSquare_out);\n    checkCudaErrors(hipStreamEndCapture(stream, &graph));\n\n    checkCudaErrors(hipGraphInstantiate(graphExec, graph, NULL, NULL, 0));\n    checkCudaErrors(hipStreamDestroy(stream));\n    checkCudaErrors(hipGraphDestroy(graph));\n}\n\nvoid prepareRefArrays(negSquareArrays *hostArrays, negSquareArrays *deviceRefArrays, bool **foundValidationFailure)\n{\n    deviceRefArrays->bytes       = hostArrays->bytes;\n    deviceRefArrays->numElements = hostArrays->numElements;\n\n    for (int i = 0; i < hostArrays->numElements; i++) {\n        hostArrays->square[i]    = hostArrays->input[i] * hostArrays->input[i];\n        hostArrays->negSquare[i] = hostArrays->square[i] * -1;\n    }\n\n    checkCudaErrors(hipMalloc((void **)&deviceRefArrays->negSquare, deviceRefArrays->bytes));\n    checkCudaErrors(\n        hipMemcpy(deviceRefArrays->negSquare, hostArrays->negSquare, hostArrays->bytes, hipMemcpyHostToDevice));\n\n    checkCudaErrors(hipMallocManaged((void **)foundValidationFailure, sizeof(bool)));\n}\n\nint checkValidationFailure(bool *foundValidationFailure)\n{\n    if (*foundValidationFailure) {\n        printf(\"Validation FAILURE!\\n\\n\");\n        *foundValidationFailure = false;\n        return EXIT_FAILURE;\n    }\n    else {\n        printf(\"Validation PASSED!\\n\\n\");\n        return EXIT_SUCCESS;\n    }\n}\n\n__global__ void validateGPU(float *d_negSquare, negSquareArrays devRefArrays, bool *foundValidationFailure)\n{\n    int   idx = blockIdx.x * blockDim.x + threadIdx.x;\n    float ref, diff;\n\n    if (idx < devRefArrays.numElements) {\n        ref  = devRefArrays.negSquare[idx];\n        diff = d_negSquare[idx] - ref;\n        diff *= diff;\n        ref *= ref;\n        if (diff / ref > ALLOWABLE_VARIANCE) {\n            *foundValidationFailure = true;\n        }\n    }\n}\n\nvoid validateHost(negSquareArrays *hostArrays, bool *foundValidationFailure)\n{\n    float ref, diff;\n\n    for (int i = 0; i < hostArrays->numElements; i++) {\n        ref  = hostArrays->input[i] * hostArrays->input[i] * -1;\n        diff = hostArrays->negSquare[i] - ref;\n        diff *= diff;\n        ref *= ref;\n        if (diff / ref > ALLOWABLE_VARIANCE) {\n            *foundValidationFailure = true;\n        }\n    }\n}\n\nint main(int argc, char **argv)\n{\n    negSquareArrays hostArrays, deviceRefArrays;\n    hipStream_t    stream;\n    hipGraphExec_t graphExec, graphExecFreeC;\n\n    // Declare pointers for GPU buffers\n    float *d_negSquare            = NULL;\n    bool  *foundValidationFailure = NULL;\n\n    srand(time(0));\n    int device = findCudaDevice(argc, (const char **)argv);\n\n    int driverVersion             = 0;\n    int deviceSupportsMemoryPools = 0;\n\n    hipDriverGetVersion(&driverVersion);\n    printf(\"Driver version is: %d.%d\\n\", driverVersion / 1000, (driverVersion % 100) / 10);\n\n    if (driverVersion < 11040) {\n        printf(\"Waiving execution as driver does not support Graph Memory Nodes\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    hipDeviceGetAttribute(&deviceSupportsMemoryPools, hipDeviceAttributeMemoryPoolsSupported, device);\n    if (!deviceSupportsMemoryPools) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else {\n        printf(\"Setting up sample.\\n\");\n    }\n\n    prepareHostArrays(&hostArrays);\n    prepareRefArrays(&hostArrays, &deviceRefArrays, &foundValidationFailure);\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    printf(\"Setup complete.\\n\\n\");\n\n    printf(\"Running negateSquares in a stream.\\n\");\n    doNegateSquaresInStream(stream, &hostArrays);\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printf(\"Validating negateSquares in a stream...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares in a stream-captured graph.\\n\");\n    createNegateSquaresGraphWithStreamCapture(&graphExec, &hostArrays);\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printf(\"Validating negateSquares in a stream-captured graph...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares in an explicitly constructed graph.\\n\");\n    createNegateSquaresGraphExplicitly(&graphExec, device, &hostArrays);\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printf(\"Validating negateSquares in an explicitly constructed graph...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    // Each of the three examples below free d_negSquare outside the graph. As\n    // demonstrated by validateGPU, d_negSquare can be accessed by outside the\n    // graph before d_negSquare is freed.\n\n    printf(\"Running negateSquares with d_negSquare freed outside the stream.\\n\");\n    createNegateSquaresGraphExplicitly(&graphExec, device, &hostArrays, &d_negSquare);\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    // Since cudaFree is synchronous, the stream must synchronize before freeing\n    // d_negSquare to ensure d_negSquare no longer being accessed.\n    checkCudaErrors(hipStreamSynchronize(stream));\n    checkCudaErrors(hipFree(d_negSquare));\n    printf(\"Validating negateSquares with d_negSquare freed outside the \"\n           \"stream...\\n\");\n    validateHost(&hostArrays, foundValidationFailure);\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares with d_negSquare freed outside the graph.\\n\");\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    checkCudaErrors(hipFreeAsync(d_negSquare, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printf(\"Validating negateSquares with d_negSquare freed outside the graph...\\n\");\n    checkValidationFailure(foundValidationFailure);\n    resetOutputArrays(&hostArrays);\n\n    printf(\"Running negateSquares with d_negSquare freed in a different graph.\\n\");\n    createFreeGraph(&graphExecFreeC, d_negSquare);\n    checkCudaErrors(hipGraphLaunch(graphExec, stream));\n    validateGPU<<<hostArrays.numBlocks, THREADS_PER_BLOCK, 0, stream>>>(\n        d_negSquare, deviceRefArrays, foundValidationFailure);\n    checkCudaErrors(hipGraphLaunch(graphExecFreeC, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n    printf(\"Validating negateSquares with d_negSquare freed in a different \"\n           \"graph...\\n\");\n    checkValidationFailure(foundValidationFailure);\n\n    printf(\"Cleaning up sample.\\n\");\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphExecDestroy(graphExecFreeC));\n    checkCudaErrors(hipStreamDestroy(stream));\n    checkCudaErrors(hipFree(foundValidationFailure));\n    checkCudaErrors(hipFree(deviceRefArrays.negSquare));\n    free(hostArrays.input);\n    free(hostArrays.square);\n    free(hostArrays.negSquare);\n    printf(\"Cleanup complete. Exiting sample.\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/graphMemoryNodes/graphMemoryNodes.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n  This sample has two kernels, one doing the rendering every frame, and\n  another one used to generate the mip map levels at startup.\n\n  For rendering we use a \"virtual\" texturing approach, where one 2d texture\n  stores pointers to the actual textures used. This can be achieved by the\n  new cudaTextureObject introduced in CUDA 5.0 and requiring sm3+ hardware.\n\n  The mipmap generation kernel uses cudaSurfaceObject and cudaTextureObject\n  passed as kernel arguments to compute the higher mip map level based on\n  the lower.\n*/\n\n#ifndef _BINDLESSTEXTURE_KERNEL_CU_\n#define _BINDLESSTEXTURE_KERNEL_CU_\n\n#include <helper_cuda.h>\n#include <helper_math.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <vector>\n\n#include \"bindlessTexture.h\"\n\n// set this to just see the mipmap chain of first image\n// #define SHOW_MIPMAPS\n\n// local references to resources\n\nImage              atlasImage;\nstd::vector<Image> contentImages;\nfloat              highestLod = 1.0f;\n\n#ifndef MAX\n#define MAX(a, b) ((a > b) ? a : b)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n\n__host__ __device__ __inline__ uint2 encodeTextureObject(cudaTextureObject_t obj)\n{\n    return make_uint2((uint)(obj & 0xFFFFFFFF), (uint)(obj >> 32));\n}\n\n__host__ __device__ __inline__ cudaTextureObject_t decodeTextureObject(uint2 obj)\n{\n    return (((cudaTextureObject_t)obj.x) | ((cudaTextureObject_t)obj.y) << 32);\n}\n\n__device__ __inline__ float4 to_float4(uchar4 vec) { return make_float4(vec.x, vec.y, vec.z, vec.w); }\n\n__device__ __inline__ uchar4 to_uchar4(float4 vec)\n{\n    return make_uchar4((uchar)vec.x, (uchar)vec.y, (uchar)vec.z, (uchar)vec.w);\n}\n\n//////////////////////////////////////////////////////////////////////////\n// Rendering\n\n// the atlas texture stores the 64 bit cudaTextureObjects\n// we use it for \"virtual\" texturing\n\n__global__ void d_render(uchar4 *d_output, uint imageW, uint imageH, float lod, cudaTextureObject_t atlasTexture)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u = x / (float)imageW;\n    float v = y / (float)imageH;\n\n    if ((x < imageW) && (y < imageH)) {\n        // read from 2D atlas texture and decode texture object\n        uint2               texCoded = tex2D<uint2>(atlasTexture, u, v);\n        cudaTextureObject_t tex      = decodeTextureObject(texCoded);\n\n        // read from cuda texture object, use template to specify what data will be\n        // returned. tex2DLod allows us to pass the lod (mip map level) directly.\n        // There is other functions with CUDA 5, e.g. tex2DGrad, that allow you\n        // to pass derivatives to perform automatic mipmap/anisotropic filtering.\n        float4 color = tex2DLod<float4>(tex, u, 1 - v, lod);\n        // In our sample tex is always valid, but for something like your own\n        // sparse texturing you would need to make sure to handle the zero case.\n\n        // write output color\n        uint i      = y * imageW + x;\n        d_output[i] = to_uchar4(color * 255.0);\n    }\n}\n\nextern \"C\" void renderAtlasImage(dim3 gridSize, dim3 blockSize, uchar4 *d_output, uint imageW, uint imageH, float lod)\n{\n    // psuedo animate lod\n    lod = fmodf(lod, highestLod * 2);\n    lod = highestLod - fabs(lod - highestLod);\n\n#ifdef SHOW_MIPMAPS\n    lod = 0.0f;\n#endif\n\n    d_render<<<gridSize, blockSize>>>(d_output, imageW, imageH, lod, atlasImage.textureObject);\n\n    checkCudaErrors(cudaGetLastError());\n}\n\n//////////////////////////////////////////////////////////////////////////\n// MipMap Generation\n\n//  A key benefit of using the new surface objects is that we don't need any\n//  global binding points anymore. We can directly pass them as function\n//  arguments.\n\n__global__ void d_mipmap(cudaSurfaceObject_t mipOutput, cudaTextureObject_t mipInput, uint imageW, uint imageH)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float px = 1.0 / float(imageW);\n    float py = 1.0 / float(imageH);\n\n    if ((x < imageW) && (y < imageH)) {\n        // take the average of 4 samples\n\n        // we are using the normalized access to make sure non-power-of-two textures\n        // behave well when downsized.\n        float4 color = (tex2D<float4>(mipInput, (x + 0) * px, (y + 0) * py))\n                     + (tex2D<float4>(mipInput, (x + 1) * px, (y + 0) * py))\n                     + (tex2D<float4>(mipInput, (x + 1) * px, (y + 1) * py))\n                     + (tex2D<float4>(mipInput, (x + 0) * px, (y + 1) * py));\n\n        color /= 4.0;\n        color *= 255.0;\n        color = fminf(color, make_float4(255.0));\n\n        surf2Dwrite(to_uchar4(color), mipOutput, x * sizeof(uchar4), y);\n    }\n}\n\nvoid generateMipMaps(cudaMipmappedArray_t mipmapArray, cudaExtent size)\n{\n    size_t width  = size.width;\n    size_t height = size.height;\n\n#ifdef SHOW_MIPMAPS\n    cudaArray_t levelFirst;\n    checkCudaErrors(cudaGetMipmappedArrayLevel(&levelFirst, mipmapArray, 0));\n#endif\n\n    uint level = 0;\n\n    while (width != 1 || height != 1) {\n        width /= 2;\n        width = MAX((size_t)1, width);\n        height /= 2;\n        height = MAX((size_t)1, height);\n\n        cudaArray_t levelFrom;\n        checkCudaErrors(cudaGetMipmappedArrayLevel(&levelFrom, mipmapArray, level));\n        cudaArray_t levelTo;\n        checkCudaErrors(cudaGetMipmappedArrayLevel(&levelTo, mipmapArray, level + 1));\n\n        cudaExtent levelToSize;\n        checkCudaErrors(cudaArrayGetInfo(NULL, &levelToSize, NULL, levelTo));\n        checkHost(levelToSize.width == width);\n        checkHost(levelToSize.height == height);\n        checkHost(levelToSize.depth == 0);\n\n        // generate texture object for reading\n        cudaTextureObject_t texInput;\n        cudaResourceDesc    texRes;\n        memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n        texRes.resType         = cudaResourceTypeArray;\n        texRes.res.array.array = levelFrom;\n\n        cudaTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n        texDescr.normalizedCoords = 1;\n        texDescr.filterMode       = cudaFilterModeLinear;\n\n        texDescr.addressMode[0] = cudaAddressModeClamp;\n        texDescr.addressMode[1] = cudaAddressModeClamp;\n        texDescr.addressMode[2] = cudaAddressModeClamp;\n\n        texDescr.readMode = cudaReadModeNormalizedFloat;\n\n        checkCudaErrors(cudaCreateTextureObject(&texInput, &texRes, &texDescr, NULL));\n\n        // generate surface object for writing\n\n        cudaSurfaceObject_t surfOutput;\n        cudaResourceDesc    surfRes;\n        memset(&surfRes, 0, sizeof(cudaResourceDesc));\n        surfRes.resType         = cudaResourceTypeArray;\n        surfRes.res.array.array = levelTo;\n\n        checkCudaErrors(cudaCreateSurfaceObject(&surfOutput, &surfRes));\n\n        // run mipmap kernel\n        dim3 blockSize(16, 16, 1);\n        dim3 gridSize(((uint)width + blockSize.x - 1) / blockSize.x, ((uint)height + blockSize.y - 1) / blockSize.y, 1);\n\n        d_mipmap<<<gridSize, blockSize>>>(surfOutput, texInput, (uint)width, (uint)height);\n\n        checkCudaErrors(cudaDeviceSynchronize());\n        checkCudaErrors(cudaGetLastError());\n\n        checkCudaErrors(cudaDestroySurfaceObject(surfOutput));\n\n        checkCudaErrors(cudaDestroyTextureObject(texInput));\n\n#ifdef SHOW_MIPMAPS\n        // we blit the current mipmap back into first level\n        cudaMemcpy3DParms copyParams = {0};\n        copyParams.dstArray          = levelFirst;\n        copyParams.srcArray          = levelTo;\n        copyParams.extent            = make_cudaExtent(width, height, 1);\n        copyParams.kind              = cudaMemcpyDeviceToDevice;\n        checkCudaErrors(cudaMemcpy3D(&copyParams));\n#endif\n\n        level++;\n    }\n}\n\nuint getMipMapLevels(cudaExtent size)\n{\n    size_t sz = MAX(MAX(size.width, size.height), size.depth);\n\n    uint levels = 0;\n\n    while (sz) {\n        sz /= 2;\n        levels++;\n    }\n\n    return levels;\n}\n\n//////////////////////////////////////////////////////////////////////////\n// Initalization\n\nextern \"C\" void randomizeAtlas()\n{\n    uint2 *h_data = (uint2 *)atlasImage.h_data;\n\n    // assign random texture object handles to our atlas image tiles\n    for (size_t i = 0; i < atlasImage.size.width * atlasImage.size.height; i++) {\n#ifdef SHOW_MIPMAPS\n        h_data[i] = encodeTextureObject(contentImages[0].textureObject);\n#else\n        h_data[i] = encodeTextureObject(contentImages[rand() % contentImages.size()].textureObject);\n#endif\n    }\n\n    // copy data to atlas array\n    cudaMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr            = make_cudaPitchedPtr(\n        atlasImage.h_data, atlasImage.size.width * sizeof(uint2), atlasImage.size.width, atlasImage.size.height);\n    copyParams.dstArray     = atlasImage.dataArray;\n    copyParams.extent       = atlasImage.size;\n    copyParams.extent.depth = 1;\n    copyParams.kind         = cudaMemcpyHostToDevice;\n    checkCudaErrors(cudaMemcpy3D(&copyParams));\n};\n\nextern \"C\" void deinitAtlasAndImages()\n{\n    for (size_t i = 0; i < contentImages.size(); i++) {\n        Image &image = contentImages[i];\n\n        if (image.h_data) {\n            free(image.h_data);\n        }\n\n        if (image.textureObject) {\n            checkCudaErrors(cudaDestroyTextureObject(image.textureObject));\n        }\n\n        if (image.mipmapArray) {\n            checkCudaErrors(cudaFreeMipmappedArray(image.mipmapArray));\n        }\n    }\n\n    if (atlasImage.h_data) {\n        free(atlasImage.h_data);\n    }\n\n    if (atlasImage.textureObject) {\n        checkCudaErrors(cudaDestroyTextureObject(atlasImage.textureObject));\n    }\n\n    if (atlasImage.dataArray) {\n        checkCudaErrors(cudaFreeArray(atlasImage.dataArray));\n    }\n}\n\nextern \"C\" void initAtlasAndImages(const Image *images, size_t numImages, cudaExtent atlasSize)\n{\n    // create individual textures\n    contentImages.resize(numImages);\n\n    for (size_t i = 0; i < numImages; i++) {\n        Image &image     = contentImages[i];\n        image.size       = images[i].size;\n        image.size.depth = 0;\n        image.type       = cudaResourceTypeMipmappedArray;\n\n        // how many mipmaps we need\n        uint levels = getMipMapLevels(image.size);\n        highestLod  = MAX(highestLod, (float)levels - 1);\n\n        cudaChannelFormatDesc desc = cudaCreateChannelDesc<uchar4>();\n        checkCudaErrors(cudaMallocMipmappedArray(&image.mipmapArray, &desc, image.size, levels));\n\n        // upload level 0\n        cudaArray_t level0;\n        checkCudaErrors(cudaGetMipmappedArrayLevel(&level0, image.mipmapArray, 0));\n\n        cudaMemcpy3DParms copyParams = {0};\n        copyParams.srcPtr            = make_cudaPitchedPtr(\n            images[i].h_data, image.size.width * sizeof(uchar4), image.size.width, image.size.height);\n        copyParams.dstArray     = level0;\n        copyParams.extent       = image.size;\n        copyParams.extent.depth = 1;\n        copyParams.kind         = cudaMemcpyHostToDevice;\n        checkCudaErrors(cudaMemcpy3D(&copyParams));\n\n        // compute rest of mipmaps based on level 0\n        generateMipMaps(image.mipmapArray, image.size);\n\n        // generate bindless texture object\n\n        cudaResourceDesc resDescr;\n        memset(&resDescr, 0, sizeof(cudaResourceDesc));\n\n        resDescr.resType           = cudaResourceTypeMipmappedArray;\n        resDescr.res.mipmap.mipmap = image.mipmapArray;\n\n        cudaTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n        texDescr.normalizedCoords = 1;\n        texDescr.filterMode       = cudaFilterModeLinear;\n        texDescr.mipmapFilterMode = cudaFilterModeLinear;\n\n        texDescr.addressMode[0] = cudaAddressModeClamp;\n        texDescr.addressMode[1] = cudaAddressModeClamp;\n        texDescr.addressMode[2] = cudaAddressModeClamp;\n\n        texDescr.maxMipmapLevelClamp = float(levels - 1);\n\n        texDescr.readMode = cudaReadModeNormalizedFloat;\n\n        checkCudaErrors(cudaCreateTextureObject(&image.textureObject, &resDescr, &texDescr, NULL));\n    }\n\n    // create atlas array\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<uint2>();\n    checkCudaErrors(cudaMallocArray(&atlasImage.dataArray, &channelDesc, atlasSize.width, atlasSize.height));\n    atlasImage.h_data = malloc(atlasSize.width * atlasSize.height * sizeof(uint2));\n    atlasImage.type   = cudaResourceTypeArray;\n    atlasImage.size   = atlasSize;\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = atlasImage.dataArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.addressMode[1]   = cudaAddressModeClamp;\n    texDescr.addressMode[1]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&atlasImage.textureObject, &texRes, &texDescr, NULL));\n\n    randomizeAtlas();\n}\n\n#endif // #ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n  This sample has two kernels, one doing the rendering every frame, and\n  another one used to generate the mip map levels at startup.\n\n  For rendering we use a \"virtual\" texturing approach, where one 2d texture\n  stores pointers to the actual textures used. This can be achieved by the\n  new cudaTextureObject introduced in CUDA 5.0 and requiring sm3+ hardware.\n\n  The mipmap generation kernel uses cudaSurfaceObject and cudaTextureObject\n  passed as kernel arguments to compute the higher mip map level based on\n  the lower.\n*/\n\n#include \"hip/hip_runtime.h\"\n#ifndef _BINDLESSTEXTURE_KERNEL_CU_\n#define _BINDLESSTEXTURE_KERNEL_CU_\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_vector_types.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <vector>\n\n#include \"bindlessTexture.h\"\n\n// set this to just see the mipmap chain of first image\n// #define SHOW_MIPMAPS\n\n// local references to resources\n\nImage              atlasImage;\nstd::vector<Image> contentImages;\nfloat              highestLod = 1.0f;\n\n#ifndef MAX\n#define MAX(a, b) ((a > b) ? a : b)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n\n__host__ __device__ __inline__ uint2 encodeTextureObject(hipTextureObject_t obj)\n{\n    return make_uint2((uint)(obj & 0xFFFFFFFF), (uint)(obj >> 32));\n}\n\n__host__ __device__ __inline__ hipTextureObject_t decodeTextureObject(uint2 obj)\n{\n    return (((hipTextureObject_t)obj.x) | ((hipTextureObject_t)obj.y) << 32);\n}\n\n__device__ __inline__ float4 to_float4(uchar4 vec) { return make_float4(vec.x, vec.y, vec.z, vec.w); }\n\n__device__ __inline__ uchar4 to_uchar4(float4 vec)\n{\n    return make_uchar4((uchar)vec.x, (uchar)vec.y, (uchar)vec.z, (uchar)vec.w);\n}\n\n//////////////////////////////////////////////////////////////////////////\n// Rendering\n\n// the atlas texture stores the 64 bit cudaTextureObjects\n// we use it for \"virtual\" texturing\n\n__global__ void d_render(uchar4 *d_output, uint imageW, uint imageH, float lod, hipTextureObject_t atlasTexture)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u = x / (float)imageW;\n    float v = y / (float)imageH;\n\n    if ((x < imageW) && (y < imageH)) {\n        // read from 2D atlas texture and decode texture object\n        uint2               texCoded = tex2D<uint2>(atlasTexture, u, v);\n        hipTextureObject_t tex      = decodeTextureObject(texCoded);\n\n        // read from cuda texture object, use template to specify what data will be\n        // returned. tex2DLod allows us to pass the lod (mip map level) directly.\n        // There is other functions with CUDA 5, e.g. tex2DGrad, that allow you\n        // to pass derivatives to perform automatic mipmap/anisotropic filtering.\n        float4 color = tex2DLod<float4>(tex, u, 1 - v, lod);\n        // In our sample tex is always valid, but for something like your own\n        // sparse texturing you would need to make sure to handle the zero case.\n\n        // write output color\n        uint i      = y * imageW + x;\n        d_output[i] = to_uchar4(color * 255.0);\n    }\n}\n\nextern \"C\" void renderAtlasImage(dim3 gridSize, dim3 blockSize, uchar4 *d_output, uint imageW, uint imageH, float lod)\n{\n    // psuedo animate lod\n    lod = fmodf(lod, highestLod * 2);\n    lod = highestLod - fabs(lod - highestLod);\n\n#ifdef SHOW_MIPMAPS\n    lod = 0.0f;\n#endif\n\n    d_render<<<gridSize, blockSize>>>(d_output, imageW, imageH, lod, atlasImage.textureObject);\n\n    checkCudaErrors(hipGetLastError());\n}\n\n//////////////////////////////////////////////////////////////////////////\n// MipMap Generation\n\n//  A key benefit of using the new surface objects is that we don't need any\n//  global binding points anymore. We can directly pass them as function\n//  arguments.\n\n__global__ void d_mipmap(hipSurfaceObject_t mipOutput, hipTextureObject_t mipInput, uint imageW, uint imageH)\n{\n    uint x = blockIdx.x * blockDim.x + threadIdx.x;\n    uint y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float px = 1.0 / float(imageW);\n    float py = 1.0 / float(imageH);\n\n    if ((x < imageW) && (y < imageH)) {\n        // take the average of 4 samples\n\n        // we are using the normalized access to make sure non-power-of-two textures\n        // behave well when downsized.\n        float4 color = (tex2D<float4>(mipInput, (x + 0) * px, (y + 0) * py))\n                     + (tex2D<float4>(mipInput, (x + 1) * px, (y + 0) * py))\n                     + (tex2D<float4>(mipInput, (x + 1) * px, (y + 1) * py))\n                     + (tex2D<float4>(mipInput, (x + 0) * px, (y + 1) * py));\n\n        color /= 4.0;\n        color *= 255.0;\n        color = fminf(color, make_float4(255.0));\n\n        surf2Dwrite(to_uchar4(color), mipOutput, x * sizeof(uchar4), y);\n    }\n}\n\nvoid generateMipMaps(hipMipmappedArray_t mipmapArray, hipExtent size)\n{\n    size_t width  = size.width;\n    size_t height = size.height;\n\n#ifdef SHOW_MIPMAPS\n    hipArray_t levelFirst;\n    checkCudaErrors(hipGetMipmappedArrayLevel(&levelFirst, mipmapArray, 0));\n#endif\n\n    uint level = 0;\n\n    while (width != 1 || height != 1) {\n        width /= 2;\n        width = MAX((size_t)1, width);\n        height /= 2;\n        height = MAX((size_t)1, height);\n\n        hipArray_t levelFrom;\n        checkCudaErrors(hipGetMipmappedArrayLevel(&levelFrom, mipmapArray, level));\n        hipArray_t levelTo;\n        checkCudaErrors(hipGetMipmappedArrayLevel(&levelTo, mipmapArray, level + 1));\n\n        hipExtent levelToSize;\n        checkCudaErrors(hipArrayGetInfo(NULL, &levelToSize, NULL, levelTo));\n        checkHost(levelToSize.width == width);\n        checkHost(levelToSize.height == height);\n        checkHost(levelToSize.depth == 0);\n\n        // generate texture object for reading\n        hipTextureObject_t texInput;\n        hipResourceDesc    texRes;\n        memset(&texRes, 0, sizeof(hipResourceDesc));\n\n        texRes.resType         = hipResourceTypeArray;\n        texRes.res.array.array = levelFrom;\n\n        hipTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n        texDescr.normalizedCoords = 1;\n        texDescr.filterMode       = hipFilterModeLinear;\n\n        texDescr.addressMode[0] = hipAddressModeClamp;\n        texDescr.addressMode[1] = hipAddressModeClamp;\n        texDescr.addressMode[2] = hipAddressModeClamp;\n\n        texDescr.readMode = hipReadModeNormalizedFloat;\n\n        checkCudaErrors(hipCreateTextureObject(&texInput, &texRes, &texDescr, NULL));\n\n        // generate surface object for writing\n\n        hipSurfaceObject_t surfOutput;\n        hipResourceDesc    surfRes;\n        memset(&surfRes, 0, sizeof(hipResourceDesc));\n        surfRes.resType         = hipResourceTypeArray;\n        surfRes.res.array.array = levelTo;\n\n        checkCudaErrors(hipCreateSurfaceObject(&surfOutput, &surfRes));\n\n        // run mipmap kernel\n        dim3 blockSize(16, 16, 1);\n        dim3 gridSize(((uint)width + blockSize.x - 1) / blockSize.x, ((uint)height + blockSize.y - 1) / blockSize.y, 1);\n\n        d_mipmap<<<gridSize, blockSize>>>(surfOutput, texInput, (uint)width, (uint)height);\n\n        checkCudaErrors(hipDeviceSynchronize());\n        checkCudaErrors(hipGetLastError());\n\n        checkCudaErrors(hipDestroySurfaceObject(surfOutput));\n\n        checkCudaErrors(hipDestroyTextureObject(texInput));\n\n#ifdef SHOW_MIPMAPS\n        // we blit the current mipmap back into first level\n        hipMemcpy3DParms copyParams = {0};\n        copyParams.dstArray          = levelFirst;\n        copyParams.srcArray          = levelTo;\n        copyParams.extent            = make_hipExtent(width, height, 1);\n        copyParams.kind              = hipMemcpyDeviceToDevice;\n        checkCudaErrors(hipMemcpy3D(&copyParams));\n#endif\n\n        level++;\n    }\n}\n\nuint getMipMapLevels(hipExtent size)\n{\n    size_t sz = MAX(MAX(size.width, size.height), size.depth);\n\n    uint levels = 0;\n\n    while (sz) {\n        sz /= 2;\n        levels++;\n    }\n\n    return levels;\n}\n\n//////////////////////////////////////////////////////////////////////////\n// Initalization\n\nextern \"C\" void randomizeAtlas()\n{\n    uint2 *h_data = (uint2 *)atlasImage.h_data;\n\n    // assign random texture object handles to our atlas image tiles\n    for (size_t i = 0; i < atlasImage.size.width * atlasImage.size.height; i++) {\n#ifdef SHOW_MIPMAPS\n        h_data[i] = encodeTextureObject(contentImages[0].textureObject);\n#else\n        h_data[i] = encodeTextureObject(contentImages[rand() % contentImages.size()].textureObject);\n#endif\n    }\n\n    // copy data to atlas array\n    hipMemcpy3DParms copyParams = {0};\n    copyParams.srcPtr            = make_hipPitchedPtr(\n        atlasImage.h_data, atlasImage.size.width * sizeof(uint2), atlasImage.size.width, atlasImage.size.height);\n    copyParams.dstArray     = atlasImage.dataArray;\n    copyParams.extent       = atlasImage.size;\n    copyParams.extent.depth = 1;\n    copyParams.kind         = hipMemcpyHostToDevice;\n    checkCudaErrors(hipMemcpy3D(&copyParams));\n};\n\nextern \"C\" void deinitAtlasAndImages()\n{\n    for (size_t i = 0; i < contentImages.size(); i++) {\n        Image &image = contentImages[i];\n\n        if (image.h_data) {\n            free(image.h_data);\n        }\n\n        if (image.textureObject) {\n            checkCudaErrors(hipDestroyTextureObject(image.textureObject));\n        }\n\n        if (image.mipmapArray) {\n            checkCudaErrors(hipFreeMipmappedArray(image.mipmapArray));\n        }\n    }\n\n    if (atlasImage.h_data) {\n        free(atlasImage.h_data);\n    }\n\n    if (atlasImage.textureObject) {\n        checkCudaErrors(hipDestroyTextureObject(atlasImage.textureObject));\n    }\n\n    if (atlasImage.dataArray) {\n        checkCudaErrors(hipFreeArray(atlasImage.dataArray));\n    }\n}\n\nextern \"C\" void initAtlasAndImages(const Image *images, size_t numImages, hipExtent atlasSize)\n{\n    // create individual textures\n    contentImages.resize(numImages);\n\n    for (size_t i = 0; i < numImages; i++) {\n        Image &image     = contentImages[i];\n        image.size       = images[i].size;\n        image.size.depth = 0;\n        image.type       = hipResourceTypeMipmappedArray;\n\n        // how many mipmaps we need\n        uint levels = getMipMapLevels(image.size);\n        highestLod  = MAX(highestLod, (float)levels - 1);\n\n        hipChannelFormatDesc desc = hipCreateChannelDesc<uchar4>();\n        checkCudaErrors(hipMallocMipmappedArray(&image.mipmapArray, &desc, image.size, levels));\n\n        // upload level 0\n        hipArray_t level0;\n        checkCudaErrors(hipGetMipmappedArrayLevel(&level0, image.mipmapArray, 0));\n\n        hipMemcpy3DParms copyParams = {0};\n        copyParams.srcPtr            = make_hipPitchedPtr(\n            images[i].h_data, image.size.width * sizeof(uchar4), image.size.width, image.size.height);\n        copyParams.dstArray     = level0;\n        copyParams.extent       = image.size;\n        copyParams.extent.depth = 1;\n        copyParams.kind         = hipMemcpyHostToDevice;\n        checkCudaErrors(hipMemcpy3D(&copyParams));\n\n        // compute rest of mipmaps based on level 0\n        generateMipMaps(image.mipmapArray, image.size);\n\n        // generate bindless texture object\n\n        hipResourceDesc resDescr;\n        memset(&resDescr, 0, sizeof(hipResourceDesc));\n\n        resDescr.resType           = hipResourceTypeMipmappedArray;\n        resDescr.res.mipmap.mipmap = image.mipmapArray;\n\n        hipTextureDesc texDescr;\n        memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n        texDescr.normalizedCoords = 1;\n        texDescr.filterMode       = hipFilterModeLinear;\n        texDescr.mipmapFilterMode = hipFilterModeLinear;\n\n        texDescr.addressMode[0] = hipAddressModeClamp;\n        texDescr.addressMode[1] = hipAddressModeClamp;\n        texDescr.addressMode[2] = hipAddressModeClamp;\n\n        texDescr.maxMipmapLevelClamp = float(levels - 1);\n\n        texDescr.readMode = hipReadModeNormalizedFloat;\n\n        checkCudaErrors(hipCreateTextureObject(&image.textureObject, &resDescr, &texDescr, NULL));\n    }\n\n    // create atlas array\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc<uint2>();\n    checkCudaErrors(hipMallocArray(&atlasImage.dataArray, &channelDesc, atlasSize.width, atlasSize.height));\n    atlasImage.h_data = malloc(atlasSize.width * atlasSize.height * sizeof(uint2));\n    atlasImage.type   = hipResourceTypeArray;\n    atlasImage.size   = atlasSize;\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = atlasImage.dataArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.addressMode[1]   = hipAddressModeClamp;\n    texDescr.addressMode[1]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&atlasImage.textureObject, &texRes, &texDescr, NULL));\n\n    randomizeAtlas();\n}\n\n#endif // #ifndef _SIMPLETEXTURE3D_KERNEL_CU_\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/bindlessTexture/bindlessTexture_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda_runtime_api.h>\n#include <helper_cuda.h>\n#include <stdio.h>\n#include <string.h>\n\n__forceinline__ __device__ float2 operator+(float2 a, float2 b)\n{\n    float2 c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n__forceinline__ __device__ float2 operator-(float2 a, float2 b)\n{\n    float2 c;\n    c.x = a.x - b.x;\n    c.y = a.y - b.y;\n    return c;\n}\n\n__forceinline__ __device__ float2 operator*(float a, float2 b)\n{\n    float2 c;\n    c.x = a * b.x;\n    c.y = a * b.y;\n    return c;\n}\n\n__forceinline__ __device__ float length(float2 a) { return sqrtf(a.x * a.x + a.y * a.y); }\n\n#define MAX_TESSELLATION 32\nstruct BezierLine\n{\n    float2  CP[3];\n    float2 *vertexPos;\n    int     nVertices;\n};\n\n__global__ void computeBezierLinePositions(int lidx, BezierLine *bLines, int nTessPoints)\n{\n    int idx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (idx < nTessPoints) {\n        float u   = (float)idx / (float)(nTessPoints - 1);\n        float omu = 1.0f - u;\n\n        float B3u[3];\n\n        B3u[0] = omu * omu;\n        B3u[1] = 2.0f * u * omu;\n        B3u[2] = u * u;\n\n        float2 position = {0, 0};\n\n        for (int i = 0; i < 3; i++) {\n            position = position + B3u[i] * bLines[lidx].CP[i];\n        }\n\n        bLines[lidx].vertexPos[idx] = position;\n    }\n}\n\n__global__ void computeBezierLinesCDP(BezierLine *bLines, int nLines)\n{\n    int lidx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (lidx < nLines) {\n        float curvature = length(bLines[lidx].CP[1] - 0.5f * (bLines[lidx].CP[0] + bLines[lidx].CP[2]))\n                        / length(bLines[lidx].CP[2] - bLines[lidx].CP[0]);\n        int nTessPoints = min(max((int)(curvature * 16.0f), 4), MAX_TESSELLATION);\n\n        if (bLines[lidx].vertexPos == NULL) {\n            bLines[lidx].nVertices = nTessPoints;\n            cudaMalloc((void **)&bLines[lidx].vertexPos, nTessPoints * sizeof(float2));\n        }\n\n        computeBezierLinePositions<<<ceilf((float)bLines[lidx].nVertices / 32.0f), 32>>>(\n            lidx, bLines, bLines[lidx].nVertices);\n    }\n}\n\n__global__ void freeVertexMem(BezierLine *bLines, int nLines)\n{\n    int lidx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (lidx < nLines)\n        cudaFree(bLines[lidx].vertexPos);\n}\n\nunsigned int checkCapableSM35Device(int argc, char **argv)\n{\n    // Get device properties\n    cudaDeviceProp properties;\n    int            device_count = 0, device = -1;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"device\")) {\n        device = getCmdLineArgumentInt(argc, (const char **)argv, \"device\");\n\n        cudaDeviceProp properties;\n        checkCudaErrors(cudaGetDeviceProperties(&properties, device));\n\n        if (properties.major > 3 || (properties.major == 3 && properties.minor >= 5)) {\n            printf(\"Running on GPU  %d (%s)\\n\", device, properties.name);\n        }\n        else {\n            printf(\"cdpBezierTessellation requires GPU devices with compute SM 3.5 or \"\n                   \"higher.\");\n            printf(\"Current GPU device has compute SM %d.%d. Exiting...\\n\", properties.major, properties.minor);\n            return EXIT_FAILURE;\n        }\n    }\n    else {\n        checkCudaErrors(cudaGetDeviceCount(&device_count));\n\n        for (int i = 0; i < device_count; ++i) {\n            checkCudaErrors(cudaGetDeviceProperties(&properties, i));\n\n            if (properties.major > 3 || (properties.major == 3 && properties.minor >= 5)) {\n                device = i;\n                printf(\"Running on GPU %d (%s)\\n\", i, properties.name);\n                break;\n            }\n\n            printf(\"GPU %d %s does not support CUDA Dynamic Parallelism\\n\", i, properties.name);\n        }\n    }\n    if (device == -1) {\n        fprintf(stderr,\n                \"cdpBezierTessellation requires GPU devices with compute SM 3.5 or \"\n                \"higher.  Exiting...\\n\");\n        return EXIT_WAIVED;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n#define N_LINES   256\n#define BLOCK_DIM 64\nint main(int argc, char **argv)\n{\n    BezierLine *bLines_h = new BezierLine[N_LINES];\n\n    float2 last = {0, 0};\n\n    for (int i = 0; i < N_LINES; i++) {\n        bLines_h[i].CP[0] = last;\n\n        for (int j = 1; j < 3; j++) {\n            bLines_h[i].CP[j].x = (float)rand() / (float)RAND_MAX;\n            bLines_h[i].CP[j].y = (float)rand() / (float)RAND_MAX;\n        }\n\n        last                  = bLines_h[i].CP[2];\n        bLines_h[i].vertexPos = NULL;\n        bLines_h[i].nVertices = 0;\n    }\n\n    unsigned int sm35Ret = checkCapableSM35Device(argc, argv);\n    if (sm35Ret != EXIT_SUCCESS) {\n        exit(sm35Ret);\n    }\n\n    BezierLine *bLines_d;\n    checkCudaErrors(cudaMalloc((void **)&bLines_d, N_LINES * sizeof(BezierLine)));\n    checkCudaErrors(cudaMemcpy(bLines_d, bLines_h, N_LINES * sizeof(BezierLine), cudaMemcpyHostToDevice));\n    printf(\"Computing Bezier Lines (CUDA Dynamic Parallelism Version) ... \");\n    computeBezierLinesCDP<<<(unsigned int)ceil((float)N_LINES / (float)BLOCK_DIM), BLOCK_DIM>>>(bLines_d, N_LINES);\n    printf(\"Done!\\n\");\n\n    // Do something to draw the lines here\n\n    freeVertexMem<<<(unsigned int)ceil((float)N_LINES / (float)BLOCK_DIM), BLOCK_DIM>>>(bLines_d, N_LINES);\n    checkCudaErrors(cudaFree(bLines_d));\n    delete[] bLines_h;\n\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n#include <string.h>\n\n__forceinline__ __device__ float2 operator+(float2 a, float2 b)\n{\n    float2 c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n__forceinline__ __device__ float2 operator-(float2 a, float2 b)\n{\n    float2 c;\n    c.x = a.x - b.x;\n    c.y = a.y - b.y;\n    return c;\n}\n\n__forceinline__ __device__ float2 operator*(float a, float2 b)\n{\n    float2 c;\n    c.x = a * b.x;\n    c.y = a * b.y;\n    return c;\n}\n\n__forceinline__ __device__ float length(float2 a) { return sqrtf(a.x * a.x + a.y * a.y); }\n\n#define MAX_TESSELLATION 32\nstruct BezierLine\n{\n    float2  CP[3];\n    float2 *vertexPos;\n    int     nVertices;\n};\n\n__global__ void computeBezierLinePositions(int lidx, BezierLine *bLines, int nTessPoints)\n{\n    int idx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (idx < nTessPoints) {\n        float u   = (float)idx / (float)(nTessPoints - 1);\n        float omu = 1.0f - u;\n\n        float B3u[3];\n\n        B3u[0] = omu * omu;\n        B3u[1] = 2.0f * u * omu;\n        B3u[2] = u * u;\n\n        float2 position = {0, 0};\n\n        for (int i = 0; i < 3; i++) {\n            position = position + B3u[i] * bLines[lidx].CP[i];\n        }\n\n        bLines[lidx].vertexPos[idx] = position;\n    }\n}\n\n__global__ void computeBezierLinesCDP(BezierLine *bLines, int nLines)\n{\n    int lidx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (lidx < nLines) {\n        float curvature = length(bLines[lidx].CP[1] - 0.5f * (bLines[lidx].CP[0] + bLines[lidx].CP[2]))\n                        / length(bLines[lidx].CP[2] - bLines[lidx].CP[0]);\n        int nTessPoints = min(max((int)(curvature * 16.0f), 4), MAX_TESSELLATION);\n\n        if (bLines[lidx].vertexPos == NULL) {\n            bLines[lidx].nVertices = nTessPoints;\n            hipMalloc((void **)&bLines[lidx].vertexPos, nTessPoints * sizeof(float2));\n        }\n\n        computeBezierLinePositions<<<ceilf((float)bLines[lidx].nVertices / 32.0f), 32>>>(\n            lidx, bLines, bLines[lidx].nVertices);\n    }\n}\n\n__global__ void freeVertexMem(BezierLine *bLines, int nLines)\n{\n    int lidx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if (lidx < nLines)\n        hipFree(bLines[lidx].vertexPos);\n}\n\nunsigned int checkCapableSM35Device(int argc, char **argv)\n{\n    // Get device properties\n    hipDeviceProp_t properties;\n    int            device_count = 0, device = -1;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"device\")) {\n        device = getCmdLineArgumentInt(argc, (const char **)argv, \"device\");\n\n        hipDeviceProp_t properties;\n        checkCudaErrors(hipGetDeviceProperties(&properties, device));\n\n        if (properties.major > 3 || (properties.major == 3 && properties.minor >= 5)) {\n            printf(\"Running on GPU  %d (%s)\\n\", device, properties.name);\n        }\n        else {\n            printf(\"cdpBezierTessellation requires GPU devices with compute SM 3.5 or \"\n                   \"higher.\");\n            printf(\"Current GPU device has compute SM %d.%d. Exiting...\\n\", properties.major, properties.minor);\n            return EXIT_FAILURE;\n        }\n    }\n    else {\n        checkCudaErrors(hipGetDeviceCount(&device_count));\n\n        for (int i = 0; i < device_count; ++i) {\n            checkCudaErrors(hipGetDeviceProperties(&properties, i));\n\n            if (properties.major > 3 || (properties.major == 3 && properties.minor >= 5)) {\n                device = i;\n                printf(\"Running on GPU %d (%s)\\n\", i, properties.name);\n                break;\n            }\n\n            printf(\"GPU %d %s does not support CUDA Dynamic Parallelism\\n\", i, properties.name);\n        }\n    }\n    if (device == -1) {\n        fprintf(stderr,\n                \"cdpBezierTessellation requires GPU devices with compute SM 3.5 or \"\n                \"higher.  Exiting...\\n\");\n        return EXIT_WAIVED;\n    }\n\n    return EXIT_SUCCESS;\n}\n\n#define N_LINES   256\n#define BLOCK_DIM 64\nint main(int argc, char **argv)\n{\n    BezierLine *bLines_h = new BezierLine[N_LINES];\n\n    float2 last = {0, 0};\n\n    for (int i = 0; i < N_LINES; i++) {\n        bLines_h[i].CP[0] = last;\n\n        for (int j = 1; j < 3; j++) {\n            bLines_h[i].CP[j].x = (float)rand() / (float)RAND_MAX;\n            bLines_h[i].CP[j].y = (float)rand() / (float)RAND_MAX;\n        }\n\n        last                  = bLines_h[i].CP[2];\n        bLines_h[i].vertexPos = NULL;\n        bLines_h[i].nVertices = 0;\n    }\n\n    unsigned int sm35Ret = checkCapableSM35Device(argc, argv);\n    if (sm35Ret != EXIT_SUCCESS) {\n        exit(sm35Ret);\n    }\n\n    BezierLine *bLines_d;\n    checkCudaErrors(hipMalloc((void **)&bLines_d, N_LINES * sizeof(BezierLine)));\n    checkCudaErrors(hipMemcpy(bLines_d, bLines_h, N_LINES * sizeof(BezierLine), hipMemcpyHostToDevice));\n    printf(\"Computing Bezier Lines (CUDA Dynamic Parallelism Version) ... \");\n    computeBezierLinesCDP<<<(unsigned int)ceil((float)N_LINES / (float)BLOCK_DIM), BLOCK_DIM>>>(bLines_d, N_LINES);\n    printf(\"Done!\\n\");\n\n    // Do something to draw the lines here\n\n    freeVertexMem<<<(unsigned int)ceil((float)N_LINES / (float)BLOCK_DIM), BLOCK_DIM>>>(bLines_d, N_LINES);\n    checkCudaErrors(hipFree(bLines_d));\n    delete[] bLines_h;\n\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/cdpBezierTessellation/BezierLineCDP.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <vector>\n\nnamespace cg = cooperative_groups;\n\n#define THREADS_PER_BLOCK       512\n#define GRAPH_LAUNCH_ITERATIONS 3\n\ntypedef struct callBackData\n{\n    const char *fn_name;\n    double     *data;\n} callBackData_t;\n\n__global__ void reduce(float *inputVec, double *outputVec, size_t inputSize, size_t outputSize)\n{\n    __shared__ double tmp[THREADS_PER_BLOCK];\n\n    cg::thread_block cta       = cg::this_thread_block();\n    size_t           globaltid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    double temp_sum = 0.0;\n    for (int i = globaltid; i < inputSize; i += gridDim.x * blockDim.x) {\n        temp_sum += (double)inputVec[i];\n    }\n    tmp[cta.thread_rank()] = temp_sum;\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    double beta = temp_sum;\n    double temp;\n\n    for (int i = tile32.size() / 2; i > 0; i >>= 1) {\n        if (tile32.thread_rank() < i) {\n            temp = tmp[cta.thread_rank() + i];\n            beta += temp;\n            tmp[cta.thread_rank()] = beta;\n        }\n        cg::sync(tile32);\n    }\n    cg::sync(cta);\n\n    if (cta.thread_rank() == 0 && blockIdx.x < outputSize) {\n        beta = 0.0;\n        for (int i = 0; i < cta.size(); i += tile32.size()) {\n            beta += tmp[i];\n        }\n        outputVec[blockIdx.x] = beta;\n    }\n}\n\n__global__ void reduceFinal(double *inputVec, double *result, size_t inputSize)\n{\n    __shared__ double tmp[THREADS_PER_BLOCK];\n\n    cg::thread_block cta       = cg::this_thread_block();\n    size_t           globaltid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    double temp_sum = 0.0;\n    for (int i = globaltid; i < inputSize; i += gridDim.x * blockDim.x) {\n        temp_sum += (double)inputVec[i];\n    }\n    tmp[cta.thread_rank()] = temp_sum;\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    // do reduction in shared mem\n    if ((blockDim.x >= 512) && (cta.thread_rank() < 256)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockDim.x >= 256) && (cta.thread_rank() < 128)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockDim.x >= 128) && (cta.thread_rank() < 64)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 64];\n    }\n\n    cg::sync(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockDim.x >= 64)\n            temp_sum += tmp[cta.thread_rank() + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            temp_sum += tile32.shfl_down(temp_sum, offset);\n        }\n    }\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        result[0] = temp_sum;\n}\n\nvoid init_input(float *a, size_t size)\n{\n    for (size_t i = 0; i < size; i++)\n        a[i] = (rand() & 0xFF) / (float)RAND_MAX;\n}\n\nvoid CUDART_CB myHostNodeCallback(void *data)\n{\n    // Check status of GPU after stream operations are done\n    callBackData_t *tmp = (callBackData_t *)(data);\n    // checkCudaErrors(tmp->status);\n\n    double *result   = (double *)(tmp->data);\n    char   *function = (char *)(tmp->fn_name);\n    printf(\"[%s] Host callback final reduced sum = %lf\\n\", function, *result);\n    *result = 0.0; // reset the result\n}\n\nvoid cudaGraphsManual(float  *inputVec_h,\n                      float  *inputVec_d,\n                      double *outputVec_d,\n                      double *result_d,\n                      size_t  inputSize,\n                      size_t  numOfBlocks)\n{\n    cudaStream_t                 streamForGraph;\n    cudaGraph_t                  graph;\n    std::vector<cudaGraphNode_t> nodeDependencies;\n    cudaGraphNode_t              memcpyNode, kernelNode, memsetNode;\n    double                       result_h = 0.0;\n\n    checkCudaErrors(cudaStreamCreate(&streamForGraph));\n\n    cudaKernelNodeParams kernelNodeParams = {0};\n    cudaMemcpy3DParms    memcpyParams     = {0};\n    cudaMemsetParams     memsetParams     = {0};\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_cudaPitchedPtr(inputVec_h, sizeof(float) * inputSize, inputSize, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_cudaPitchedPtr(inputVec_d, sizeof(float) * inputSize, inputSize, 1);\n    memcpyParams.extent   = make_cudaExtent(sizeof(float) * inputSize, 1, 1);\n    memcpyParams.kind     = cudaMemcpyHostToDevice;\n\n    memsetParams.dst         = (void *)outputVec_d;\n    memsetParams.value       = 0;\n    memsetParams.pitch       = 0;\n    memsetParams.elementSize = sizeof(float); // elementSize can be max 4 bytes\n    memsetParams.width       = numOfBlocks * 2;\n    memsetParams.height      = 1;\n\n    checkCudaErrors(cudaGraphCreate(&graph, 0));\n    checkCudaErrors(cudaGraphAddMemcpyNode(&memcpyNode, graph, NULL, 0, &memcpyParams));\n    checkCudaErrors(cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n\n    nodeDependencies.push_back(memsetNode);\n    nodeDependencies.push_back(memcpyNode);\n\n    void *kernelArgs[4] = {(void *)&inputVec_d, (void *)&outputVec_d, &inputSize, &numOfBlocks};\n\n    kernelNodeParams.func           = (void *)reduce;\n    kernelNodeParams.gridDim        = dim3(numOfBlocks, 1, 1);\n    kernelNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes = 0;\n    kernelNodeParams.kernelParams   = (void **)kernelArgs;\n    kernelNodeParams.extra          = NULL;\n\n    checkCudaErrors(cudaGraphAddKernelNode(\n        &kernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &kernelNodeParams));\n\n    nodeDependencies.clear();\n    nodeDependencies.push_back(kernelNode);\n\n    memset(&memsetParams, 0, sizeof(memsetParams));\n    memsetParams.dst         = result_d;\n    memsetParams.value       = 0;\n    memsetParams.elementSize = sizeof(float);\n    memsetParams.width       = 2;\n    memsetParams.height      = 1;\n    checkCudaErrors(cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n\n    nodeDependencies.push_back(memsetNode);\n\n    memset(&kernelNodeParams, 0, sizeof(kernelNodeParams));\n    kernelNodeParams.func           = (void *)reduceFinal;\n    kernelNodeParams.gridDim        = dim3(1, 1, 1);\n    kernelNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes = 0;\n    void *kernelArgs2[3]            = {(void *)&outputVec_d, (void *)&result_d, &numOfBlocks};\n    kernelNodeParams.kernelParams   = kernelArgs2;\n    kernelNodeParams.extra          = NULL;\n\n    checkCudaErrors(cudaGraphAddKernelNode(\n        &kernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &kernelNodeParams));\n    nodeDependencies.clear();\n    nodeDependencies.push_back(kernelNode);\n\n    memset(&memcpyParams, 0, sizeof(memcpyParams));\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_cudaPitchedPtr(result_d, sizeof(double), 1, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_cudaPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_cudaPitchedPtr(&result_h, sizeof(double), 1, 1);\n    memcpyParams.extent   = make_cudaExtent(sizeof(double), 1, 1);\n    memcpyParams.kind     = cudaMemcpyDeviceToHost;\n    checkCudaErrors(\n        cudaGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n    nodeDependencies.clear();\n    nodeDependencies.push_back(memcpyNode);\n\n    cudaGraphNode_t    hostNode;\n    cudaHostNodeParams hostParams = {0};\n    hostParams.fn                 = myHostNodeCallback;\n    callBackData_t hostFnData;\n    hostFnData.data     = &result_h;\n    hostFnData.fn_name  = \"cudaGraphsManual\";\n    hostParams.userData = &hostFnData;\n\n    checkCudaErrors(\n        cudaGraphAddHostNode(&hostNode, graph, nodeDependencies.data(), nodeDependencies.size(), &hostParams));\n\n    cudaGraphNode_t *nodes    = NULL;\n    size_t           numNodes = 0;\n    checkCudaErrors(cudaGraphGetNodes(graph, nodes, &numNodes));\n    printf(\"\\nNum of nodes in the graph created manually = %zu\\n\", numNodes);\n\n    cudaGraphExec_t graphExec;\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    cudaGraph_t     clonedGraph;\n    cudaGraphExec_t clonedGraphExec;\n    checkCudaErrors(cudaGraphClone(&clonedGraph, graph));\n    checkCudaErrors(cudaGraphInstantiate(&clonedGraphExec, clonedGraph, NULL, NULL, 0));\n\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(cudaGraphLaunch(graphExec, streamForGraph));\n    }\n\n    checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n\n    printf(\"Cloned Graph Output.. \\n\");\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(cudaGraphLaunch(clonedGraphExec, streamForGraph));\n    }\n    checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphExecDestroy(clonedGraphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaGraphDestroy(clonedGraph));\n    checkCudaErrors(cudaStreamDestroy(streamForGraph));\n}\n\nvoid cudaGraphsUsingStreamCapture(float  *inputVec_h,\n                                  float  *inputVec_d,\n                                  double *outputVec_d,\n                                  double *result_d,\n                                  size_t  inputSize,\n                                  size_t  numOfBlocks)\n{\n    cudaStream_t stream1, stream2, stream3, streamForGraph;\n    cudaEvent_t  forkStreamEvent, memsetEvent1, memsetEvent2;\n    cudaGraph_t  graph;\n    double       result_h = 0.0;\n\n    checkCudaErrors(cudaStreamCreate(&stream1));\n    checkCudaErrors(cudaStreamCreate(&stream2));\n    checkCudaErrors(cudaStreamCreate(&stream3));\n    checkCudaErrors(cudaStreamCreate(&streamForGraph));\n\n    checkCudaErrors(cudaEventCreate(&forkStreamEvent));\n    checkCudaErrors(cudaEventCreate(&memsetEvent1));\n    checkCudaErrors(cudaEventCreate(&memsetEvent2));\n\n    checkCudaErrors(cudaStreamBeginCapture(stream1, cudaStreamCaptureModeGlobal));\n\n    checkCudaErrors(cudaEventRecord(forkStreamEvent, stream1));\n    checkCudaErrors(cudaStreamWaitEvent(stream2, forkStreamEvent, 0));\n    checkCudaErrors(cudaStreamWaitEvent(stream3, forkStreamEvent, 0));\n\n    checkCudaErrors(cudaMemcpyAsync(inputVec_d, inputVec_h, sizeof(float) * inputSize, cudaMemcpyDefault, stream1));\n\n    checkCudaErrors(cudaMemsetAsync(outputVec_d, 0, sizeof(double) * numOfBlocks, stream2));\n\n    checkCudaErrors(cudaEventRecord(memsetEvent1, stream2));\n\n    checkCudaErrors(cudaMemsetAsync(result_d, 0, sizeof(double), stream3));\n    checkCudaErrors(cudaEventRecord(memsetEvent2, stream3));\n\n    checkCudaErrors(cudaStreamWaitEvent(stream1, memsetEvent1, 0));\n\n    reduce<<<numOfBlocks, THREADS_PER_BLOCK, 0, stream1>>>(inputVec_d, outputVec_d, inputSize, numOfBlocks);\n\n    checkCudaErrors(cudaStreamWaitEvent(stream1, memsetEvent2, 0));\n\n    reduceFinal<<<1, THREADS_PER_BLOCK, 0, stream1>>>(outputVec_d, result_d, numOfBlocks);\n    checkCudaErrors(cudaMemcpyAsync(&result_h, result_d, sizeof(double), cudaMemcpyDefault, stream1));\n\n    callBackData_t hostFnData = {0};\n    hostFnData.data           = &result_h;\n    hostFnData.fn_name        = \"cudaGraphsUsingStreamCapture\";\n    cudaHostFn_t fn           = myHostNodeCallback;\n    checkCudaErrors(cudaLaunchHostFunc(stream1, fn, &hostFnData));\n    checkCudaErrors(cudaStreamEndCapture(stream1, &graph));\n\n    cudaGraphNode_t *nodes    = NULL;\n    size_t           numNodes = 0;\n    checkCudaErrors(cudaGraphGetNodes(graph, nodes, &numNodes));\n    printf(\"\\nNum of nodes in the graph created using stream capture API = %zu\\n\", numNodes);\n\n    cudaGraphExec_t graphExec;\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    cudaGraph_t     clonedGraph;\n    cudaGraphExec_t clonedGraphExec;\n    checkCudaErrors(cudaGraphClone(&clonedGraph, graph));\n    checkCudaErrors(cudaGraphInstantiate(&clonedGraphExec, clonedGraph, NULL, NULL, 0));\n\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(cudaGraphLaunch(graphExec, streamForGraph));\n    }\n\n    checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n\n    printf(\"Cloned Graph Output.. \\n\");\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(cudaGraphLaunch(clonedGraphExec, streamForGraph));\n    }\n\n    checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphExecDestroy(clonedGraphExec));\n    checkCudaErrors(cudaGraphDestroy(graph));\n    checkCudaErrors(cudaGraphDestroy(clonedGraph));\n    checkCudaErrors(cudaStreamDestroy(stream1));\n    checkCudaErrors(cudaStreamDestroy(stream2));\n    checkCudaErrors(cudaStreamDestroy(streamForGraph));\n}\n\nint main(int argc, char **argv)\n{\n    size_t size      = 1 << 24; // number of elements to reduce\n    size_t maxBlocks = 512;\n\n    // This will pick the best possible CUDA capable device\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    printf(\"%zu elements\\n\", size);\n    printf(\"threads per block  = %d\\n\", THREADS_PER_BLOCK);\n    printf(\"Graph Launch iterations = %d\\n\", GRAPH_LAUNCH_ITERATIONS);\n\n    float  *inputVec_d = NULL, *inputVec_h = NULL;\n    double *outputVec_d = NULL, *result_d;\n\n    checkCudaErrors(cudaMallocHost(&inputVec_h, sizeof(float) * size));\n    checkCudaErrors(cudaMalloc(&inputVec_d, sizeof(float) * size));\n    checkCudaErrors(cudaMalloc(&outputVec_d, sizeof(double) * maxBlocks));\n    checkCudaErrors(cudaMalloc(&result_d, sizeof(double)));\n\n    init_input(inputVec_h, size);\n\n    cudaGraphsManual(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);\n    cudaGraphsUsingStreamCapture(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);\n\n    checkCudaErrors(cudaFree(inputVec_d));\n    checkCudaErrors(cudaFree(outputVec_d));\n    checkCudaErrors(cudaFree(result_d));\n    checkCudaErrors(cudaFreeHost(inputVec_h));\n    return EXIT_SUCCESS;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <vector>\n\nnamespace cg = cooperative_groups;\n\n#define THREADS_PER_BLOCK       512\n#define GRAPH_LAUNCH_ITERATIONS 3\n\ntypedef struct callBackData\n{\n    const char *fn_name;\n    double     *data;\n} callBackData_t;\n\n__global__ void reduce(float *inputVec, double *outputVec, size_t inputSize, size_t outputSize)\n{\n    __shared__ double tmp[THREADS_PER_BLOCK];\n\n    cg::thread_block cta       = cg::this_thread_block();\n    size_t           globaltid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    double temp_sum = 0.0;\n    for (int i = globaltid; i < inputSize; i += gridDim.x * blockDim.x) {\n        temp_sum += (double)inputVec[i];\n    }\n    tmp[cta.thread_rank()] = temp_sum;\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    double beta = temp_sum;\n    double temp;\n\n    for (int i = tile32.size() / 2; i > 0; i >>= 1) {\n        if (tile32.thread_rank() < i) {\n            temp = tmp[cta.thread_rank() + i];\n            beta += temp;\n            tmp[cta.thread_rank()] = beta;\n        }\n        cg::sync(tile32);\n    }\n    cg::sync(cta);\n\n    if (cta.thread_rank() == 0 && blockIdx.x < outputSize) {\n        beta = 0.0;\n        for (int i = 0; i < cta.size(); i += tile32.size()) {\n            beta += tmp[i];\n        }\n        outputVec[blockIdx.x] = beta;\n    }\n}\n\n__global__ void reduceFinal(double *inputVec, double *result, size_t inputSize)\n{\n    __shared__ double tmp[THREADS_PER_BLOCK];\n\n    cg::thread_block cta       = cg::this_thread_block();\n    size_t           globaltid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    double temp_sum = 0.0;\n    for (int i = globaltid; i < inputSize; i += gridDim.x * blockDim.x) {\n        temp_sum += (double)inputVec[i];\n    }\n    tmp[cta.thread_rank()] = temp_sum;\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    // do reduction in shared mem\n    if ((blockDim.x >= 512) && (cta.thread_rank() < 256)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockDim.x >= 256) && (cta.thread_rank() < 128)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockDim.x >= 128) && (cta.thread_rank() < 64)) {\n        tmp[cta.thread_rank()] = temp_sum = temp_sum + tmp[cta.thread_rank() + 64];\n    }\n\n    cg::sync(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockDim.x >= 64)\n            temp_sum += tmp[cta.thread_rank() + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            temp_sum += tile32.shfl_down(temp_sum, offset);\n        }\n    }\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        result[0] = temp_sum;\n}\n\nvoid init_input(float *a, size_t size)\n{\n    for (size_t i = 0; i < size; i++)\n        a[i] = (rand() & 0xFF) / (float)RAND_MAX;\n}\n\nvoid CUDART_CB myHostNodeCallback(void *data)\n{\n    // Check status of GPU after stream operations are done\n    callBackData_t *tmp = (callBackData_t *)(data);\n    // checkCudaErrors(tmp->status);\n\n    double *result   = (double *)(tmp->data);\n    char   *function = (char *)(tmp->fn_name);\n    printf(\"[%s] Host callback final reduced sum = %lf\\n\", function, *result);\n    *result = 0.0; // reset the result\n}\n\nvoid cudaGraphsManual(float  *inputVec_h,\n                      float  *inputVec_d,\n                      double *outputVec_d,\n                      double *result_d,\n                      size_t  inputSize,\n                      size_t  numOfBlocks)\n{\n    hipStream_t                 streamForGraph;\n    hipGraph_t                  graph;\n    std::vector<hipGraphNode_t> nodeDependencies;\n    hipGraphNode_t              memcpyNode, kernelNode, memsetNode;\n    double                       result_h = 0.0;\n\n    checkCudaErrors(hipStreamCreate(&streamForGraph));\n\n    hipKernelNodeParams kernelNodeParams = {0};\n    hipMemcpy3DParms    memcpyParams     = {0};\n    hipMemsetParams     memsetParams     = {0};\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_hipPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_hipPitchedPtr(inputVec_h, sizeof(float) * inputSize, inputSize, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_hipPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_hipPitchedPtr(inputVec_d, sizeof(float) * inputSize, inputSize, 1);\n    memcpyParams.extent   = make_hipExtent(sizeof(float) * inputSize, 1, 1);\n    memcpyParams.kind     = hipMemcpyHostToDevice;\n\n    memsetParams.dst         = (void *)outputVec_d;\n    memsetParams.value       = 0;\n    memsetParams.pitch       = 0;\n    memsetParams.elementSize = sizeof(float); // elementSize can be max 4 bytes\n    memsetParams.width       = numOfBlocks * 2;\n    memsetParams.height      = 1;\n\n    checkCudaErrors(hipGraphCreate(&graph, 0));\n    checkCudaErrors(hipGraphAddMemcpyNode(&memcpyNode, graph, NULL, 0, &memcpyParams));\n    checkCudaErrors(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n\n    nodeDependencies.push_back(memsetNode);\n    nodeDependencies.push_back(memcpyNode);\n\n    void *kernelArgs[4] = {(void *)&inputVec_d, (void *)&outputVec_d, &inputSize, &numOfBlocks};\n\n    kernelNodeParams.func           = (void *)reduce;\n    kernelNodeParams.gridDim        = dim3(numOfBlocks, 1, 1);\n    kernelNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes = 0;\n    kernelNodeParams.kernelParams   = (void **)kernelArgs;\n    kernelNodeParams.extra          = NULL;\n\n    checkCudaErrors(hipGraphAddKernelNode(\n        &kernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &kernelNodeParams));\n\n    nodeDependencies.clear();\n    nodeDependencies.push_back(kernelNode);\n\n    memset(&memsetParams, 0, sizeof(memsetParams));\n    memsetParams.dst         = result_d;\n    memsetParams.value       = 0;\n    memsetParams.elementSize = sizeof(float);\n    memsetParams.width       = 2;\n    memsetParams.height      = 1;\n    checkCudaErrors(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n\n    nodeDependencies.push_back(memsetNode);\n\n    memset(&kernelNodeParams, 0, sizeof(kernelNodeParams));\n    kernelNodeParams.func           = (void *)reduceFinal;\n    kernelNodeParams.gridDim        = dim3(1, 1, 1);\n    kernelNodeParams.blockDim       = dim3(THREADS_PER_BLOCK, 1, 1);\n    kernelNodeParams.sharedMemBytes = 0;\n    void *kernelArgs2[3]            = {(void *)&outputVec_d, (void *)&result_d, &numOfBlocks};\n    kernelNodeParams.kernelParams   = kernelArgs2;\n    kernelNodeParams.extra          = NULL;\n\n    checkCudaErrors(hipGraphAddKernelNode(\n        &kernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &kernelNodeParams));\n    nodeDependencies.clear();\n    nodeDependencies.push_back(kernelNode);\n\n    memset(&memcpyParams, 0, sizeof(memcpyParams));\n\n    memcpyParams.srcArray = NULL;\n    memcpyParams.srcPos   = make_hipPos(0, 0, 0);\n    memcpyParams.srcPtr   = make_hipPitchedPtr(result_d, sizeof(double), 1, 1);\n    memcpyParams.dstArray = NULL;\n    memcpyParams.dstPos   = make_hipPos(0, 0, 0);\n    memcpyParams.dstPtr   = make_hipPitchedPtr(&result_h, sizeof(double), 1, 1);\n    memcpyParams.extent   = make_hipExtent(sizeof(double), 1, 1);\n    memcpyParams.kind     = hipMemcpyDeviceToHost;\n    checkCudaErrors(\n        hipGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n    nodeDependencies.clear();\n    nodeDependencies.push_back(memcpyNode);\n\n    hipGraphNode_t    hostNode;\n    hipHostNodeParams hostParams = {0};\n    hostParams.fn                 = myHostNodeCallback;\n    callBackData_t hostFnData;\n    hostFnData.data     = &result_h;\n    hostFnData.fn_name  = \"cudaGraphsManual\";\n    hostParams.userData = &hostFnData;\n\n    checkCudaErrors(\n        hipGraphAddHostNode(&hostNode, graph, nodeDependencies.data(), nodeDependencies.size(), &hostParams));\n\n    hipGraphNode_t *nodes    = NULL;\n    size_t           numNodes = 0;\n    checkCudaErrors(hipGraphGetNodes(graph, nodes, &numNodes));\n    printf(\"\\nNum of nodes in the graph created manually = %zu\\n\", numNodes);\n\n    hipGraphExec_t graphExec;\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    hipGraph_t     clonedGraph;\n    hipGraphExec_t clonedGraphExec;\n    checkCudaErrors(hipGraphClone(&clonedGraph, graph));\n    checkCudaErrors(hipGraphInstantiate(&clonedGraphExec, clonedGraph, NULL, NULL, 0));\n\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(hipGraphLaunch(graphExec, streamForGraph));\n    }\n\n    checkCudaErrors(hipStreamSynchronize(streamForGraph));\n\n    printf(\"Cloned Graph Output.. \\n\");\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(hipGraphLaunch(clonedGraphExec, streamForGraph));\n    }\n    checkCudaErrors(hipStreamSynchronize(streamForGraph));\n\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphExecDestroy(clonedGraphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipGraphDestroy(clonedGraph));\n    checkCudaErrors(hipStreamDestroy(streamForGraph));\n}\n\nvoid cudaGraphsUsingStreamCapture(float  *inputVec_h,\n                                  float  *inputVec_d,\n                                  double *outputVec_d,\n                                  double *result_d,\n                                  size_t  inputSize,\n                                  size_t  numOfBlocks)\n{\n    hipStream_t stream1, stream2, stream3, streamForGraph;\n    hipEvent_t  forkStreamEvent, memsetEvent1, memsetEvent2;\n    hipGraph_t  graph;\n    double       result_h = 0.0;\n\n    checkCudaErrors(hipStreamCreate(&stream1));\n    checkCudaErrors(hipStreamCreate(&stream2));\n    checkCudaErrors(hipStreamCreate(&stream3));\n    checkCudaErrors(hipStreamCreate(&streamForGraph));\n\n    checkCudaErrors(hipEventCreate(&forkStreamEvent));\n    checkCudaErrors(hipEventCreate(&memsetEvent1));\n    checkCudaErrors(hipEventCreate(&memsetEvent2));\n\n    checkCudaErrors(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal));\n\n    checkCudaErrors(hipEventRecord(forkStreamEvent, stream1));\n    checkCudaErrors(hipStreamWaitEvent(stream2, forkStreamEvent, 0));\n    checkCudaErrors(hipStreamWaitEvent(stream3, forkStreamEvent, 0));\n\n    checkCudaErrors(hipMemcpyAsync(inputVec_d, inputVec_h, sizeof(float) * inputSize, hipMemcpyDefault, stream1));\n\n    checkCudaErrors(hipMemsetAsync(outputVec_d, 0, sizeof(double) * numOfBlocks, stream2));\n\n    checkCudaErrors(hipEventRecord(memsetEvent1, stream2));\n\n    checkCudaErrors(hipMemsetAsync(result_d, 0, sizeof(double), stream3));\n    checkCudaErrors(hipEventRecord(memsetEvent2, stream3));\n\n    checkCudaErrors(hipStreamWaitEvent(stream1, memsetEvent1, 0));\n\n    reduce<<<numOfBlocks, THREADS_PER_BLOCK, 0, stream1>>>(inputVec_d, outputVec_d, inputSize, numOfBlocks);\n\n    checkCudaErrors(hipStreamWaitEvent(stream1, memsetEvent2, 0));\n\n    reduceFinal<<<1, THREADS_PER_BLOCK, 0, stream1>>>(outputVec_d, result_d, numOfBlocks);\n    checkCudaErrors(hipMemcpyAsync(&result_h, result_d, sizeof(double), hipMemcpyDefault, stream1));\n\n    callBackData_t hostFnData = {0};\n    hostFnData.data           = &result_h;\n    hostFnData.fn_name        = \"cudaGraphsUsingStreamCapture\";\n    hipHostFn_t fn           = myHostNodeCallback;\n    checkCudaErrors(hipLaunchHostFunc(stream1, fn, &hostFnData));\n    checkCudaErrors(hipStreamEndCapture(stream1, &graph));\n\n    hipGraphNode_t *nodes    = NULL;\n    size_t           numNodes = 0;\n    checkCudaErrors(hipGraphGetNodes(graph, nodes, &numNodes));\n    printf(\"\\nNum of nodes in the graph created using stream capture API = %zu\\n\", numNodes);\n\n    hipGraphExec_t graphExec;\n    checkCudaErrors(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n    hipGraph_t     clonedGraph;\n    hipGraphExec_t clonedGraphExec;\n    checkCudaErrors(hipGraphClone(&clonedGraph, graph));\n    checkCudaErrors(hipGraphInstantiate(&clonedGraphExec, clonedGraph, NULL, NULL, 0));\n\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(hipGraphLaunch(graphExec, streamForGraph));\n    }\n\n    checkCudaErrors(hipStreamSynchronize(streamForGraph));\n\n    printf(\"Cloned Graph Output.. \\n\");\n    for (int i = 0; i < GRAPH_LAUNCH_ITERATIONS; i++) {\n        checkCudaErrors(hipGraphLaunch(clonedGraphExec, streamForGraph));\n    }\n\n    checkCudaErrors(hipStreamSynchronize(streamForGraph));\n\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphExecDestroy(clonedGraphExec));\n    checkCudaErrors(hipGraphDestroy(graph));\n    checkCudaErrors(hipGraphDestroy(clonedGraph));\n    checkCudaErrors(hipStreamDestroy(stream1));\n    checkCudaErrors(hipStreamDestroy(stream2));\n    checkCudaErrors(hipStreamDestroy(streamForGraph));\n}\n\nint main(int argc, char **argv)\n{\n    size_t size      = 1 << 24; // number of elements to reduce\n    size_t maxBlocks = 512;\n\n    // This will pick the best possible CUDA capable device\n    int devID = findCudaDevice(argc, (const char **)argv);\n\n    printf(\"%zu elements\\n\", size);\n    printf(\"threads per block  = %d\\n\", THREADS_PER_BLOCK);\n    printf(\"Graph Launch iterations = %d\\n\", GRAPH_LAUNCH_ITERATIONS);\n\n    float  *inputVec_d = NULL, *inputVec_h = NULL;\n    double *outputVec_d = NULL, *result_d;\n\n    checkCudaErrors(hipHostMalloc(&inputVec_h, sizeof(float) * size));\n    checkCudaErrors(hipMalloc(&inputVec_d, sizeof(float) * size));\n    checkCudaErrors(hipMalloc(&outputVec_d, sizeof(double) * maxBlocks));\n    checkCudaErrors(hipMalloc(&result_d, sizeof(double)));\n\n    init_input(inputVec_h, size);\n\n    cudaGraphsManual(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);\n    cudaGraphsUsingStreamCapture(inputVec_h, inputVec_d, outputVec_d, result_d, size, maxBlocks);\n\n    checkCudaErrors(hipFree(inputVec_d));\n    checkCudaErrors(hipFree(outputVec_d));\n    checkCudaErrors(hipFree(result_d));\n    checkCudaErrors(hipHostFree(inputVec_h));\n    return EXIT_SUCCESS;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/3_CUDA_Features/simpleCudaGraphs/simpleCudaGraphs.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Example showing the use of CUFFT for fast 1D-convolution using FFT. */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <cuda_runtime.h>\n#include <cufft.h>\n#include <cufftXt.h>\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2                            Complex;\nstatic __device__ __host__ inline Complex ComplexAdd(Complex, Complex);\nstatic __device__ __host__ inline Complex ComplexScale(Complex, float);\nstatic __device__ __host__ inline Complex ComplexMul(Complex, Complex);\nstatic __global__ void                    ComplexPointwiseMulAndScale(Complex *, const Complex *, int, float);\n\n// Filtering functions\nvoid Convolve(const Complex *, int, const Complex *, int, Complex *);\n\n// Padding functions\nint PadData(const Complex *, Complex **, int, const Complex *, Complex **, int);\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\n// The filter size is assumed to be a number smaller than the signal size\n#define SIGNAL_SIZE        50\n#define FILTER_KERNEL_SIZE 11\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    printf(\"[simpleCUFFT] is starting...\\n\");\n\n    findCudaDevice(argc, (const char **)argv);\n\n    // Allocate host memory for the signal\n    Complex *h_signal = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * SIGNAL_SIZE));\n\n    // Initialize the memory for the signal\n    for (unsigned int i = 0; i < SIGNAL_SIZE; ++i) {\n        h_signal[i].x = rand() / static_cast<float>(RAND_MAX);\n        h_signal[i].y = 0;\n    }\n\n    // Allocate host memory for the filter\n    Complex *h_filter_kernel = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * FILTER_KERNEL_SIZE));\n\n    // Initialize the memory for the filter\n    for (unsigned int i = 0; i < FILTER_KERNEL_SIZE; ++i) {\n        h_filter_kernel[i].x = rand() / static_cast<float>(RAND_MAX);\n        h_filter_kernel[i].y = 0;\n    }\n\n    // Pad signal and filter kernel\n    Complex *h_padded_signal;\n    Complex *h_padded_filter_kernel;\n    int      new_size =\n        PadData(h_signal, &h_padded_signal, SIGNAL_SIZE, h_filter_kernel, &h_padded_filter_kernel, FILTER_KERNEL_SIZE);\n    int mem_size = sizeof(Complex) * new_size;\n\n    // Allocate device memory for signal\n    Complex *d_signal;\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_signal), mem_size));\n    // Copy host memory to device\n    checkCudaErrors(cudaMemcpy(d_signal, h_padded_signal, mem_size, cudaMemcpyHostToDevice));\n\n    // Allocate device memory for filter kernel\n    Complex *d_filter_kernel;\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_filter_kernel), mem_size));\n\n    // Copy host memory to device\n    checkCudaErrors(cudaMemcpy(d_filter_kernel, h_padded_filter_kernel, mem_size, cudaMemcpyHostToDevice));\n\n    // CUFFT plan simple API\n    cufftHandle plan;\n    checkCudaErrors(cufftPlan1d(&plan, new_size, CUFFT_C2C, 1));\n\n    // CUFFT plan advanced API\n    cufftHandle   plan_adv;\n    size_t        workSize;\n    long long int new_size_long = new_size;\n\n    checkCudaErrors(cufftCreate(&plan_adv));\n    checkCudaErrors(cufftXtMakePlanMany(\n        plan_adv, 1, &new_size_long, NULL, 1, 1, CUDA_C_32F, NULL, 1, 1, CUDA_C_32F, 1, &workSize, CUDA_C_32F));\n    printf(\"Temporary buffer size %li bytes\\n\", workSize);\n\n    // Transform signal and kernel\n    printf(\"Transforming signal cufftExecC2C\\n\");\n    checkCudaErrors(cufftExecC2C(\n        plan, reinterpret_cast<cufftComplex *>(d_signal), reinterpret_cast<cufftComplex *>(d_signal), CUFFT_FORWARD));\n    checkCudaErrors(cufftExecC2C(plan_adv,\n                                 reinterpret_cast<cufftComplex *>(d_filter_kernel),\n                                 reinterpret_cast<cufftComplex *>(d_filter_kernel),\n                                 CUFFT_FORWARD));\n\n    // Multiply the coefficients together and normalize the result\n    printf(\"Launching ComplexPointwiseMulAndScale<<< >>>\\n\");\n    ComplexPointwiseMulAndScale<<<32, 256>>>(d_signal, d_filter_kernel, new_size, 1.0f / new_size);\n\n    // Check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed [ ComplexPointwiseMulAndScale ]\");\n\n    // Transform signal back\n    printf(\"Transforming signal back cufftExecC2C\\n\");\n    checkCudaErrors(cufftExecC2C(\n        plan, reinterpret_cast<cufftComplex *>(d_signal), reinterpret_cast<cufftComplex *>(d_signal), CUFFT_INVERSE));\n\n    // Copy device memory to host\n    Complex *h_convolved_signal = h_padded_signal;\n    checkCudaErrors(cudaMemcpy(h_convolved_signal, d_signal, mem_size, cudaMemcpyDeviceToHost));\n\n    // Allocate host memory for the convolution result\n    Complex *h_convolved_signal_ref = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * SIGNAL_SIZE));\n\n    // Convolve on the host\n    Convolve(h_signal, SIGNAL_SIZE, h_filter_kernel, FILTER_KERNEL_SIZE, h_convolved_signal_ref);\n\n    // check result\n    bool bTestResult = sdkCompareL2fe(reinterpret_cast<float *>(h_convolved_signal_ref),\n                                      reinterpret_cast<float *>(h_convolved_signal),\n                                      2 * SIGNAL_SIZE,\n                                      1e-5f);\n\n    // Destroy CUFFT context\n    checkCudaErrors(cufftDestroy(plan));\n    checkCudaErrors(cufftDestroy(plan_adv));\n\n    // cleanup memory\n    free(h_signal);\n    free(h_filter_kernel);\n    free(h_padded_signal);\n    free(h_padded_filter_kernel);\n    free(h_convolved_signal_ref);\n    checkCudaErrors(cudaFree(d_signal));\n    checkCudaErrors(cudaFree(d_filter_kernel));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n// Pad data\nint PadData(const Complex *signal,\n            Complex      **padded_signal,\n            int            signal_size,\n            const Complex *filter_kernel,\n            Complex      **padded_filter_kernel,\n            int            filter_kernel_size)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n    int new_size  = signal_size + maxRadius;\n\n    // Pad signal\n    Complex *new_data = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * new_size));\n    memcpy(new_data + 0, signal, signal_size * sizeof(Complex));\n    memset(new_data + signal_size, 0, (new_size - signal_size) * sizeof(Complex));\n    *padded_signal = new_data;\n\n    // Pad filter\n    new_data = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * new_size));\n    memcpy(new_data + 0, filter_kernel + minRadius, maxRadius * sizeof(Complex));\n    memset(new_data + maxRadius, 0, (new_size - filter_kernel_size) * sizeof(Complex));\n    memcpy(new_data + new_size - minRadius, filter_kernel, minRadius * sizeof(Complex));\n    *padded_filter_kernel = new_data;\n\n    return new_size;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Filtering operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Computes convolution on the host\nvoid Convolve(const Complex *signal,\n              int            signal_size,\n              const Complex *filter_kernel,\n              int            filter_kernel_size,\n              Complex       *filtered_signal)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n\n    // Loop over output element indices\n    for (int i = 0; i < signal_size; ++i) {\n        filtered_signal[i].x = filtered_signal[i].y = 0;\n\n        // Loop over convolution indices\n        for (int j = -maxRadius + 1; j <= minRadius; ++j) {\n            int k = i + j;\n\n            if (k >= 0 && k < signal_size) {\n                filtered_signal[i] =\n                    ComplexAdd(filtered_signal[i], ComplexMul(signal[k], filter_kernel[minRadius - j]));\n            }\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Complex operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Complex addition\nstatic __device__ __host__ inline Complex ComplexAdd(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n// Complex scale\nstatic __device__ __host__ inline Complex ComplexScale(Complex a, float s)\n{\n    Complex c;\n    c.x = s * a.x;\n    c.y = s * a.y;\n    return c;\n}\n\n// Complex multiplication\nstatic __device__ __host__ inline Complex ComplexMul(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x * b.x - a.y * b.y;\n    c.y = a.x * b.y + a.y * b.x;\n    return c;\n}\n\n// Complex pointwise multiplication\nstatic __global__ void ComplexPointwiseMulAndScale(Complex *a, const Complex *b, int size, float scale)\n{\n    const int numThreads = blockDim.x * gridDim.x;\n    const int threadID   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = threadID; i < size; i += numThreads) {\n        a[i] = ComplexScale(ComplexMul(a[i], b[i]), scale);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Example showing the use of CUFFT for fast 1D-convolution using FFT. */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <hip/hip_runtime.h>\n#include <hipfft/hipfft.h>\n#include <hipfft/hipfftXt.h>\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2                            Complex;\nstatic __device__ __host__ inline Complex ComplexAdd(Complex, Complex);\nstatic __device__ __host__ inline Complex ComplexScale(Complex, float);\nstatic __device__ __host__ inline Complex ComplexMul(Complex, Complex);\nstatic __global__ void                    ComplexPointwiseMulAndScale(Complex *, const Complex *, int, float);\n\n// Filtering functions\nvoid Convolve(const Complex *, int, const Complex *, int, Complex *);\n\n// Padding functions\nint PadData(const Complex *, Complex **, int, const Complex *, Complex **, int);\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nvoid runTest(int argc, char **argv);\n\n// The filter size is assumed to be a number smaller than the signal size\n#define SIGNAL_SIZE        50\n#define FILTER_KERNEL_SIZE 11\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv) { runTest(argc, argv); }\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a simple test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nvoid runTest(int argc, char **argv)\n{\n    printf(\"[simpleCUFFT] is starting...\\n\");\n\n    findCudaDevice(argc, (const char **)argv);\n\n    // Allocate host memory for the signal\n    Complex *h_signal = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * SIGNAL_SIZE));\n\n    // Initialize the memory for the signal\n    for (unsigned int i = 0; i < SIGNAL_SIZE; ++i) {\n        h_signal[i].x = rand() / static_cast<float>(RAND_MAX);\n        h_signal[i].y = 0;\n    }\n\n    // Allocate host memory for the filter\n    Complex *h_filter_kernel = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * FILTER_KERNEL_SIZE));\n\n    // Initialize the memory for the filter\n    for (unsigned int i = 0; i < FILTER_KERNEL_SIZE; ++i) {\n        h_filter_kernel[i].x = rand() / static_cast<float>(RAND_MAX);\n        h_filter_kernel[i].y = 0;\n    }\n\n    // Pad signal and filter kernel\n    Complex *h_padded_signal;\n    Complex *h_padded_filter_kernel;\n    int      new_size =\n        PadData(h_signal, &h_padded_signal, SIGNAL_SIZE, h_filter_kernel, &h_padded_filter_kernel, FILTER_KERNEL_SIZE);\n    int mem_size = sizeof(Complex) * new_size;\n\n    // Allocate device memory for signal\n    Complex *d_signal;\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_signal), mem_size));\n    // Copy host memory to device\n    checkCudaErrors(hipMemcpy(d_signal, h_padded_signal, mem_size, hipMemcpyHostToDevice));\n\n    // Allocate device memory for filter kernel\n    Complex *d_filter_kernel;\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_filter_kernel), mem_size));\n\n    // Copy host memory to device\n    checkCudaErrors(hipMemcpy(d_filter_kernel, h_padded_filter_kernel, mem_size, hipMemcpyHostToDevice));\n\n    // CUFFT plan simple API\n    hipfftHandle plan;\n    checkCudaErrors(hipfftPlan1d(&plan, new_size, HIPFFT_C2C, 1));\n\n    // CUFFT plan advanced API\n    hipfftHandle   plan_adv;\n    size_t        workSize;\n    long long int new_size_long = new_size;\n\n    checkCudaErrors(hipfftCreate(&plan_adv));\n    checkCudaErrors(hipfftXtMakePlanMany(\n        plan_adv, 1, &new_size_long, NULL, 1, 1, HIP_C_32F, NULL, 1, 1, HIP_C_32F, 1, &workSize, HIP_C_32F));\n    printf(\"Temporary buffer size %li bytes\\n\", workSize);\n\n    // Transform signal and kernel\n    printf(\"Transforming signal hipfftExecC2C\\n\");\n    checkCudaErrors(hipfftExecC2C(\n        plan, reinterpret_cast<hipfftComplex *>(d_signal), reinterpret_cast<hipfftComplex *>(d_signal), HIPFFT_FORWARD));\n    checkCudaErrors(hipfftExecC2C(plan_adv,\n                                 reinterpret_cast<hipfftComplex *>(d_filter_kernel),\n                                 reinterpret_cast<hipfftComplex *>(d_filter_kernel),\n                                 HIPFFT_FORWARD));\n\n    // Multiply the coefficients together and normalize the result\n    printf(\"Launching ComplexPointwiseMulAndScale<<< >>>\\n\");\n    ComplexPointwiseMulAndScale<<<32, 256>>>(d_signal, d_filter_kernel, new_size, 1.0f / new_size);\n\n    // Check if kernel execution generated and error\n    getLastCudaError(\"Kernel execution failed [ ComplexPointwiseMulAndScale ]\");\n\n    // Transform signal back\n    printf(\"Transforming signal back hipfftExecC2C\\n\");\n    checkCudaErrors(hipfftExecC2C(\n        plan, reinterpret_cast<hipfftComplex *>(d_signal), reinterpret_cast<hipfftComplex *>(d_signal), HIPFFT_BACKWARD));\n\n    // Copy device memory to host\n    Complex *h_convolved_signal = h_padded_signal;\n    checkCudaErrors(hipMemcpy(h_convolved_signal, d_signal, mem_size, hipMemcpyDeviceToHost));\n\n    // Allocate host memory for the convolution result\n    Complex *h_convolved_signal_ref = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * SIGNAL_SIZE));\n\n    // Convolve on the host\n    Convolve(h_signal, SIGNAL_SIZE, h_filter_kernel, FILTER_KERNEL_SIZE, h_convolved_signal_ref);\n\n    // check result\n    bool bTestResult = sdkCompareL2fe(reinterpret_cast<float *>(h_convolved_signal_ref),\n                                      reinterpret_cast<float *>(h_convolved_signal),\n                                      2 * SIGNAL_SIZE,\n                                      1e-5f);\n\n    // Destroy CUFFT context\n    checkCudaErrors(hipfftDestroy(plan));\n    checkCudaErrors(hipfftDestroy(plan_adv));\n\n    // cleanup memory\n    free(h_signal);\n    free(h_filter_kernel);\n    free(h_padded_signal);\n    free(h_padded_filter_kernel);\n    free(h_convolved_signal_ref);\n    checkCudaErrors(hipFree(d_signal));\n    checkCudaErrors(hipFree(d_filter_kernel));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n// Pad data\nint PadData(const Complex *signal,\n            Complex      **padded_signal,\n            int            signal_size,\n            const Complex *filter_kernel,\n            Complex      **padded_filter_kernel,\n            int            filter_kernel_size)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n    int new_size  = signal_size + maxRadius;\n\n    // Pad signal\n    Complex *new_data = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * new_size));\n    memcpy(new_data + 0, signal, signal_size * sizeof(Complex));\n    memset(new_data + signal_size, 0, (new_size - signal_size) * sizeof(Complex));\n    *padded_signal = new_data;\n\n    // Pad filter\n    new_data = reinterpret_cast<Complex *>(malloc(sizeof(Complex) * new_size));\n    memcpy(new_data + 0, filter_kernel + minRadius, maxRadius * sizeof(Complex));\n    memset(new_data + maxRadius, 0, (new_size - filter_kernel_size) * sizeof(Complex));\n    memcpy(new_data + new_size - minRadius, filter_kernel, minRadius * sizeof(Complex));\n    *padded_filter_kernel = new_data;\n\n    return new_size;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Filtering operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Computes convolution on the host\nvoid Convolve(const Complex *signal,\n              int            signal_size,\n              const Complex *filter_kernel,\n              int            filter_kernel_size,\n              Complex       *filtered_signal)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n\n    // Loop over output element indices\n    for (int i = 0; i < signal_size; ++i) {\n        filtered_signal[i].x = filtered_signal[i].y = 0;\n\n        // Loop over convolution indices\n        for (int j = -maxRadius + 1; j <= minRadius; ++j) {\n            int k = i + j;\n\n            if (k >= 0 && k < signal_size) {\n                filtered_signal[i] =\n                    ComplexAdd(filtered_signal[i], ComplexMul(signal[k], filter_kernel[minRadius - j]));\n            }\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Complex operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Complex addition\nstatic __device__ __host__ inline Complex ComplexAdd(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n// Complex scale\nstatic __device__ __host__ inline Complex ComplexScale(Complex a, float s)\n{\n    Complex c;\n    c.x = s * a.x;\n    c.y = s * a.y;\n    return c;\n}\n\n// Complex multiplication\nstatic __device__ __host__ inline Complex ComplexMul(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x * b.x - a.y * b.y;\n    c.y = a.x * b.y + a.y * b.x;\n    return c;\n}\n\n// Complex pointwise multiplication\nstatic __global__ void ComplexPointwiseMulAndScale(Complex *a, const Complex *b, int size, float scale)\n{\n    const int numThreads = blockDim.x * gridDim.x;\n    const int threadID   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (int i = threadID; i < size; i += numThreads) {\n        a[i] = ComplexScale(ComplexMul(a[i], b[i]), scale);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/simpleCUFFT/simpleCUFFT.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample is an implementation of a simple line-of-sight algorithm:\n// Given a height map and a ray originating at some observation point,\n// it computes all the points along the ray that are visible from the\n// observation point.\n// It is based on the description made in \"Guy E. Blelloch.  Vector models\n// for data-parallel computing. MIT Press, 1990\" and uses open source CUDA\n// Thrust Library\n\n#ifdef _WIN32\n#define NOMINMAX\n#endif\n\n// includes, system\n#include <float.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <helper_cuda.h>\n#include <helper_functions.h>\n#include <helper_math.h>\n\n// includes, library\n#include <thrust/copy.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/scan.h>\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, types\n\n// Boolean\ntypedef unsigned char Bool;\nenum { False = 0, True = 1 };\n\n// 2D height field\nstruct HeightField\n{\n    int    width;\n    float *height;\n};\n\n// Ray\nstruct Ray\n{\n    float3 origin;\n    float2 dir;\n    int    length;\n    float  oneOverLength;\n};\n\n////////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nint                        runTest(int argc, char **argv);\n__global__ void            computeAngles_kernel(const Ray, float *, cudaTextureObject_t);\n__global__ void            computeVisibilities_kernel(const float *, const float *, int, Bool *);\nvoid                       lineOfSight_gold(const HeightField, const Ray, Bool *);\n__device__ __host__ float2 getLocation(const Ray, int);\n__device__ __host__ float  getAngle(const Ray, float2, float);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    int res = runTest(argc, argv);\n\n    if (res != 1) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a line-of-sight test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nint runTest(int argc, char **argv)\n{\n    ////////////////////////////////////////////////////////////////////////////\n    // Device initialization\n\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    ////////////////////////////////////////////////////////////////////////////\n    // Timer\n\n    // Create\n    StopWatchInterface *timer;\n    sdkCreateTimer(&timer);\n\n    // Number of iterations to get accurate timing\n    uint numIterations = 100;\n\n    ////////////////////////////////////////////////////////////////////////////\n    // Height field\n\n    HeightField heightField;\n\n    // Allocate in host memory\n    int2 dim          = make_int2(10000, 100);\n    heightField.width = dim.x;\n    thrust::host_vector<float> height(dim.x * dim.y);\n    heightField.height = (float *)&height[0];\n\n    //\n    // Fill in with an arbitrary sine surface\n    for (int x = 0; x < dim.x; ++x)\n        for (int y = 0; y < dim.y; ++y) {\n            float amp    = 0.1f * (x + y);\n            float period = 2.0f + amp;\n            *(heightField.height + dim.x * y + x) =\n                amp * (sinf(sqrtf((float)(x * x + y * y)) * 2.0f * 3.1416f / period) + 1.0f);\n        }\n\n    // Allocate CUDA array in device memory\n    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    cudaArray            *heightFieldArray;\n    checkCudaErrors(cudaMallocArray(&heightFieldArray, &channelDesc, dim.x, dim.y));\n\n    // Initialize device memory\n    checkCudaErrors(cudaMemcpy2DToArray(heightFieldArray,\n                                        0,\n                                        0,\n                                        heightField.height,\n                                        dim.x * sizeof(float),\n                                        dim.x * sizeof(float),\n                                        dim.y,\n                                        cudaMemcpyHostToDevice));\n\n    cudaTextureObject_t heightFieldTex;\n    cudaResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = heightFieldArray;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.addressMode[1]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&heightFieldTex, &texRes, &texDescr, NULL));\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Ray (starts at origin and traverses the height field diagonally)\n\n    Ray ray;\n    ray.origin        = make_float3(0, 0, 2.0f);\n    int2 dir          = make_int2(dim.x - 1, dim.y - 1);\n    ray.dir           = make_float2((float)dir.x, (float)dir.y);\n    ray.length        = max(abs(dir.x), abs(dir.y));\n    ray.oneOverLength = 1.0f / ray.length;\n\n    //////////////////////////////////////////////////////////////////////////////\n    // View angles\n\n    // Allocate view angles for each point along the ray\n    thrust::device_vector<float> d_angles(ray.length);\n\n    // Allocate result of max-scan operation on the array of view angles\n    thrust::device_vector<float> d_scannedAngles(ray.length);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Visibility results\n\n    // Allocate visibility results for each point along the ray\n    thrust::device_vector<Bool> d_visibilities(ray.length);\n    thrust::host_vector<Bool>   h_visibilities(ray.length);\n    thrust::host_vector<Bool>   h_visibilitiesRef(ray.length);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Reference solution\n    lineOfSight_gold(heightField, ray, (Bool *)&h_visibilitiesRef[0]);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Device solution\n\n    // Execution configuration\n    dim3 block(256);\n    dim3 grid((uint)ceil(ray.length / (double)block.x));\n\n    // Compute device solution\n    printf(\"Line of sight\\n\");\n    sdkStartTimer(&timer);\n\n    for (uint i = 0; i < numIterations; ++i) {\n        // Compute view angle for each point along the ray\n        computeAngles_kernel<<<grid, block>>>(ray, thrust::raw_pointer_cast(&d_angles[0]), heightFieldTex);\n        getLastCudaError(\"Kernel execution failed\");\n\n        // Perform a max-scan operation on the array of view angles\n        thrust::inclusive_scan(d_angles.begin(), d_angles.end(), d_scannedAngles.begin(), thrust::maximum<float>());\n        getLastCudaError(\"Kernel execution failed\");\n\n        // Compute visibility results based on the array of view angles\n        // and its scanned version\n        computeVisibilities_kernel<<<grid, block>>>(thrust::raw_pointer_cast(&d_angles[0]),\n                                                    thrust::raw_pointer_cast(&d_scannedAngles[0]),\n                                                    ray.length,\n                                                    thrust::raw_pointer_cast(&d_visibilities[0]));\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    cudaDeviceSynchronize();\n    sdkStopTimer(&timer);\n    getLastCudaError(\"Kernel execution failed\");\n\n    // Copy visibility results back to the host\n    thrust::copy(d_visibilities.begin(), d_visibilities.end(), h_visibilities.begin());\n\n    // Compare device visibility results against reference results\n    bool res = compareData(thrust::raw_pointer_cast(&h_visibilitiesRef[0]),\n                           thrust::raw_pointer_cast(&h_visibilities[0]),\n                           ray.length,\n                           0.0f,\n                           0.0f);\n    printf(\"Average time: %f ms\\n\\n\", sdkGetTimerValue(&timer) / numIterations);\n    sdkResetTimer(&timer);\n\n    // Cleanup memory\n    checkCudaErrors(cudaFreeArray(heightFieldArray));\n    return res;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute view angles for each point along the ray\n//! @param ray         ray\n//! @param angles      view angles\n////////////////////////////////////////////////////////////////////////////////\n__global__ void computeAngles_kernel(const Ray ray, float *angles, cudaTextureObject_t HeightFieldTex)\n{\n    uint i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < ray.length) {\n        float2 location = getLocation(ray, i + 1);\n        float  height   = tex2D<float>(HeightFieldTex, location.x, location.y);\n        float  angle    = getAngle(ray, location, height);\n        angles[i]       = angle;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute visibility for each point along the ray\n//! @param angles          view angles\n//! @param scannedAngles   max-scanned view angles\n//! @param numAngles       number of view angles\n//! @param visibilities    boolean array indicating the visibility of each point\n//!                        along the ray\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\ncomputeVisibilities_kernel(const float *angles, const float *scannedAngles, int numAngles, Bool *visibilities)\n{\n    uint i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numAngles) {\n        visibilities[i] = scannedAngles[i] <= angles[i];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute reference data set\n//! @param heightField     height field\n//! @param ray             ray\n//! @param visibilities    boolean array indicating the visibility of each point\n//!                        along the ray\n////////////////////////////////////////////////////////////////////////////////\nvoid lineOfSight_gold(const HeightField heightField, const Ray ray, Bool *visibilities)\n{\n    float angleMax = asinf(-1.0f);\n\n    for (int i = 0; i < ray.length; ++i) {\n        float2 location = getLocation(ray, i + 1);\n        float  height   = *(heightField.height + heightField.width * (int)floorf(location.y) + (int)floorf(location.x));\n        float  angle    = getAngle(ray, location, height);\n\n        if (angle > angleMax) {\n            angleMax        = angle;\n            visibilities[i] = True;\n        }\n        else {\n            visibilities[i] = False;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute the 2D coordinates of the point located at i steps from the origin\n//! of the ray\n//! @param ray      ray\n//! @param i        integer offset along the ray\n////////////////////////////////////////////////////////////////////////////////\n__device__ __host__ float2 getLocation(const Ray ray, int i)\n{\n    float step = i * ray.oneOverLength;\n    return make_float2(ray.origin.x, ray.origin.y) + ray.dir * step;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute the angle of view between a 3D point and the origin of the ray\n//! @param ray        ray\n//! @param location   2D coordinates of the input point\n//! @param height     height of the input point\n////////////////////////////////////////////////////////////////////////////////\n__device__ __host__ float getAngle(const Ray ray, float2 location, float height)\n{\n    float2 dir = location - make_float2(ray.origin.x, ray.origin.y);\n    return atanf((height - ray.origin.z) / length(dir));\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample is an implementation of a simple line-of-sight algorithm:\n// Given a height map and a ray originating at some observation point,\n// it computes all the points along the ray that are visible from the\n// observation point.\n// It is based on the description made in \"Guy E. Blelloch.  Vector models\n// for data-parallel computing. MIT Press, 1990\" and uses open source CUDA\n// Thrust Library\n\n#include \"hip/hip_runtime.h\"\n#ifdef _WIN32\n#define NOMINMAX\n#endif\n\n// includes, system\n#include <float.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n#include <hip/hip_vector_types.h>\n\n// includes, library\n#include <thrust/copy.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/scan.h>\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, types\n\n// Boolean\ntypedef unsigned char Bool;\nenum { False = 0, True = 1 };\n\n// 2D height field\nstruct HeightField\n{\n    int    width;\n    float *height;\n};\n\n// Ray\nstruct Ray\n{\n    float3 origin;\n    float2 dir;\n    int    length;\n    float  oneOverLength;\n};\n\n////////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nint                        runTest(int argc, char **argv);\n__global__ void            computeAngles_kernel(const Ray, float *, hipTextureObject_t);\n__global__ void            computeVisibilities_kernel(const float *, const float *, int, Bool *);\nvoid                       lineOfSight_gold(const HeightField, const Ray, Bool *);\n__device__ __host__ float2 getLocation(const Ray, int);\n__device__ __host__ float  getAngle(const Ray, float2, float);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    int res = runTest(argc, argv);\n\n    if (res != 1) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Run a line-of-sight test for CUDA\n////////////////////////////////////////////////////////////////////////////////\nint runTest(int argc, char **argv)\n{\n    ////////////////////////////////////////////////////////////////////////////\n    // Device initialization\n\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    findCudaDevice(argc, (const char **)argv);\n\n    ////////////////////////////////////////////////////////////////////////////\n    // Timer\n\n    // Create\n    StopWatchInterface *timer;\n    sdkCreateTimer(&timer);\n\n    // Number of iterations to get accurate timing\n    uint numIterations = 100;\n\n    ////////////////////////////////////////////////////////////////////////////\n    // Height field\n\n    HeightField heightField;\n\n    // Allocate in host memory\n    int2 dim          = make_int2(10000, 100);\n    heightField.width = dim.x;\n    thrust::host_vector<float> height(dim.x * dim.y);\n    heightField.height = (float *)&height[0];\n\n    //\n    // Fill in with an arbitrary sine surface\n    for (int x = 0; x < dim.x; ++x)\n        for (int y = 0; y < dim.y; ++y) {\n            float amp    = 0.1f * (x + y);\n            float period = 2.0f + amp;\n            *(heightField.height + dim.x * y + x) =\n                amp * (sinf(sqrtf((float)(x * x + y * y)) * 2.0f * 3.1416f / period) + 1.0f);\n        }\n\n    // Allocate CUDA array in device memory\n    hipChannelFormatDesc channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    hipArray            *heightFieldArray;\n    checkCudaErrors(hipMallocArray(&heightFieldArray, &channelDesc, dim.x, dim.y));\n\n    // Initialize device memory\n    checkCudaErrors(hipMemcpy2DToArray(heightFieldArray,\n                                        0,\n                                        0,\n                                        heightField.height,\n                                        dim.x * sizeof(float),\n                                        dim.x * sizeof(float),\n                                        dim.y,\n                                        hipMemcpyHostToDevice));\n\n    hipTextureObject_t heightFieldTex;\n    hipResourceDesc    texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = heightFieldArray;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.addressMode[1]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&heightFieldTex, &texRes, &texDescr, NULL));\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Ray (starts at origin and traverses the height field diagonally)\n\n    Ray ray;\n    ray.origin        = make_float3(0, 0, 2.0f);\n    int2 dir          = make_int2(dim.x - 1, dim.y - 1);\n    ray.dir           = make_float2((float)dir.x, (float)dir.y);\n    ray.length        = max(abs(dir.x), abs(dir.y));\n    ray.oneOverLength = 1.0f / ray.length;\n\n    //////////////////////////////////////////////////////////////////////////////\n    // View angles\n\n    // Allocate view angles for each point along the ray\n    thrust::device_vector<float> d_angles(ray.length);\n\n    // Allocate result of max-scan operation on the array of view angles\n    thrust::device_vector<float> d_scannedAngles(ray.length);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Visibility results\n\n    // Allocate visibility results for each point along the ray\n    thrust::device_vector<Bool> d_visibilities(ray.length);\n    thrust::host_vector<Bool>   h_visibilities(ray.length);\n    thrust::host_vector<Bool>   h_visibilitiesRef(ray.length);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Reference solution\n    lineOfSight_gold(heightField, ray, (Bool *)&h_visibilitiesRef[0]);\n\n    //////////////////////////////////////////////////////////////////////////////\n    // Device solution\n\n    // Execution configuration\n    dim3 block(256);\n    dim3 grid((uint)ceil(ray.length / (double)block.x));\n\n    // Compute device solution\n    printf(\"Line of sight\\n\");\n    sdkStartTimer(&timer);\n\n    for (uint i = 0; i < numIterations; ++i) {\n        // Compute view angle for each point along the ray\n        computeAngles_kernel<<<grid, block>>>(ray, thrust::raw_pointer_cast(&d_angles[0]), heightFieldTex);\n        getLastCudaError(\"Kernel execution failed\");\n\n        // Perform a max-scan operation on the array of view angles\n        thrust::inclusive_scan(d_angles.begin(), d_angles.end(), d_scannedAngles.begin(), thrust::maximum<float>());\n        getLastCudaError(\"Kernel execution failed\");\n\n        // Compute visibility results based on the array of view angles\n        // and its scanned version\n        computeVisibilities_kernel<<<grid, block>>>(thrust::raw_pointer_cast(&d_angles[0]),\n                                                    thrust::raw_pointer_cast(&d_scannedAngles[0]),\n                                                    ray.length,\n                                                    thrust::raw_pointer_cast(&d_visibilities[0]));\n        getLastCudaError(\"Kernel execution failed\");\n    }\n\n    hipDeviceSynchronize();\n    sdkStopTimer(&timer);\n    getLastCudaError(\"Kernel execution failed\");\n\n    // Copy visibility results back to the host\n    thrust::copy(d_visibilities.begin(), d_visibilities.end(), h_visibilities.begin());\n\n    // Compare device visibility results against reference results\n    bool res = compareData(thrust::raw_pointer_cast(&h_visibilitiesRef[0]),\n                           thrust::raw_pointer_cast(&h_visibilities[0]),\n                           ray.length,\n                           0.0f,\n                           0.0f);\n    printf(\"Average time: %f ms\\n\\n\", sdkGetTimerValue(&timer) / numIterations);\n    sdkResetTimer(&timer);\n\n    // Cleanup memory\n    checkCudaErrors(hipFreeArray(heightFieldArray));\n    return res;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute view angles for each point along the ray\n//! @param ray         ray\n//! @param angles      view angles\n////////////////////////////////////////////////////////////////////////////////\n__global__ void computeAngles_kernel(const Ray ray, float *angles, hipTextureObject_t HeightFieldTex)\n{\n    uint i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < ray.length) {\n        float2 location = getLocation(ray, i + 1);\n        float  height   = tex2D<float>(HeightFieldTex, location.x, location.y);\n        float  angle    = getAngle(ray, location, height);\n        angles[i]       = angle;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute visibility for each point along the ray\n//! @param angles          view angles\n//! @param scannedAngles   max-scanned view angles\n//! @param numAngles       number of view angles\n//! @param visibilities    boolean array indicating the visibility of each point\n//!                        along the ray\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\ncomputeVisibilities_kernel(const float *angles, const float *scannedAngles, int numAngles, Bool *visibilities)\n{\n    uint i = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (i < numAngles) {\n        visibilities[i] = scannedAngles[i] <= angles[i];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute reference data set\n//! @param heightField     height field\n//! @param ray             ray\n//! @param visibilities    boolean array indicating the visibility of each point\n//!                        along the ray\n////////////////////////////////////////////////////////////////////////////////\nvoid lineOfSight_gold(const HeightField heightField, const Ray ray, Bool *visibilities)\n{\n    float angleMax = asinf(-1.0f);\n\n    for (int i = 0; i < ray.length; ++i) {\n        float2 location = getLocation(ray, i + 1);\n        float  height   = *(heightField.height + heightField.width * (int)floorf(location.y) + (int)floorf(location.x));\n        float  angle    = getAngle(ray, location, height);\n\n        if (angle > angleMax) {\n            angleMax        = angle;\n            visibilities[i] = True;\n        }\n        else {\n            visibilities[i] = False;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute the 2D coordinates of the point located at i steps from the origin\n//! of the ray\n//! @param ray      ray\n//! @param i        integer offset along the ray\n////////////////////////////////////////////////////////////////////////////////\n__device__ __host__ float2 getLocation(const Ray ray, int i)\n{\n    float step = i * ray.oneOverLength;\n    return make_float2(ray.origin.x, ray.origin.y) + ray.dir * step;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute the angle of view between a 3D point and the origin of the ray\n//! @param ray        ray\n//! @param location   2D coordinates of the input point\n//! @param height     height of the input point\n////////////////////////////////////////////////////////////////////////////////\n__device__ __host__ float getAngle(const Ray ray, float2 location, float height)\n{\n    float2 dir = location - make_float2(ray.origin.x, ray.origin.y);\n    return atanf((height - ray.origin.z) / length(dir));\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/lineOfSight/lineOfSight.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cuda.h>\n#include <helper_cuda.h>\n#include <helper_image.h>\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Rotate an image using texture lookups\n//! @param outputData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void\ntransformKernel(unsigned int *outputData, int width, int height, float theta, cudaTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    float4       pix          = tex2D<float4>(tex, tu + 0.5f, tv + 0.5f);\n    unsigned int pixelInt     = rgbaFloatToInt(pix);\n    outputData[y * width + x] = pixelInt;\n}\n\nstatic __global__ void rgbToGrayscaleKernel(unsigned int *rgbaImage, size_t imageWidth, size_t imageHeight)\n{\n    size_t gidX = blockDim.x * blockIdx.x + threadIdx.x;\n\n    uchar4 *pixArray = (uchar4 *)rgbaImage;\n\n    for (int pixId = gidX; pixId < imageWidth * imageHeight; pixId += gridDim.x * blockDim.x) {\n        uchar4        dataA     = pixArray[pixId];\n        unsigned char grayscale = (unsigned char)(dataA.x * 0.3 + dataA.y * 0.59 + dataA.z * 0.11);\n        uchar4        dataB     = make_uchar4(grayscale, grayscale, grayscale, 0);\n        pixArray[pixId]         = dataB;\n    }\n}\n\nvoid launchGrayScaleKernel(unsigned int *d_rgbaImage,\n                           std::string   image_filename,\n                           size_t        imageWidth,\n                           size_t        imageHeight,\n                           cudaStream_t  stream)\n{\n    int numThreadsPerBlock = 1024;\n    int numOfBlocks        = (imageWidth * imageHeight) / numThreadsPerBlock;\n\n    rgbToGrayscaleKernel<<<numOfBlocks, numThreadsPerBlock, 0, stream>>>(d_rgbaImage, imageWidth, imageHeight);\n\n    unsigned int *outputData;\n    checkCudaErrors(cudaMallocHost((void **)&outputData, sizeof(unsigned int) * imageWidth * imageHeight));\n    checkCudaErrors(cudaMemcpyAsync(\n        outputData, d_rgbaImage, sizeof(unsigned int) * imageWidth * imageHeight, cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    char outputFilename[1024];\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length() - 4, \"_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)outputData, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    checkCudaErrors(cudaFreeHost(outputData));\n}\n\nvoid rotateKernel(cudaTextureObject_t &texObj,\n                  const float          angle,\n                  unsigned int        *d_outputData,\n                  const int            imageWidth,\n                  const int            imageHeight,\n                  cudaStream_t         stream)\n{\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(imageWidth / dimBlock.x, imageHeight / dimBlock.y, 1);\n\n    transformKernel<<<dimGrid, dimBlock, 0, stream>>>(d_outputData, imageWidth, imageHeight, angle, texObj);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <helper_image.h>\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Rotate an image using texture lookups\n//! @param outputData  output data in global memory\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void\ntransformKernel(unsigned int *outputData, int width, int height, float theta, hipTextureObject_t tex)\n{\n    // calculate normalized texture coordinates\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    float u  = (float)x - (float)width / 2;\n    float v  = (float)y - (float)height / 2;\n    float tu = u * cosf(theta) - v * sinf(theta);\n    float tv = v * cosf(theta) + u * sinf(theta);\n\n    tu /= (float)width;\n    tv /= (float)height;\n\n    // read from texture and write to global memory\n    float4       pix          = tex2D<float4>(tex, tu + 0.5f, tv + 0.5f);\n    unsigned int pixelInt     = rgbaFloatToInt(pix);\n    outputData[y * width + x] = pixelInt;\n}\n\nstatic __global__ void rgbToGrayscaleKernel(unsigned int *rgbaImage, size_t imageWidth, size_t imageHeight)\n{\n    size_t gidX = blockDim.x * blockIdx.x + threadIdx.x;\n\n    uchar4 *pixArray = (uchar4 *)rgbaImage;\n\n    for (int pixId = gidX; pixId < imageWidth * imageHeight; pixId += gridDim.x * blockDim.x) {\n        uchar4        dataA     = pixArray[pixId];\n        unsigned char grayscale = (unsigned char)(dataA.x * 0.3 + dataA.y * 0.59 + dataA.z * 0.11);\n        uchar4        dataB     = make_uchar4(grayscale, grayscale, grayscale, 0);\n        pixArray[pixId]         = dataB;\n    }\n}\n\nvoid launchGrayScaleKernel(unsigned int *d_rgbaImage,\n                           std::string   image_filename,\n                           size_t        imageWidth,\n                           size_t        imageHeight,\n                           hipStream_t  stream)\n{\n    int numThreadsPerBlock = 1024;\n    int numOfBlocks        = (imageWidth * imageHeight) / numThreadsPerBlock;\n\n    rgbToGrayscaleKernel<<<numOfBlocks, numThreadsPerBlock, 0, stream>>>(d_rgbaImage, imageWidth, imageHeight);\n\n    unsigned int *outputData;\n    checkCudaErrors(hipHostMalloc((void **)&outputData, sizeof(unsigned int) * imageWidth * imageHeight));\n    checkCudaErrors(hipMemcpyAsync(\n        outputData, d_rgbaImage, sizeof(unsigned int) * imageWidth * imageHeight, hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    char outputFilename[1024];\n    strcpy(outputFilename, image_filename.c_str());\n    strcpy(outputFilename + image_filename.length() - 4, \"_out.ppm\");\n    sdkSavePPM4ub(outputFilename, (unsigned char *)outputData, imageWidth, imageHeight);\n    printf(\"Wrote '%s'\\n\", outputFilename);\n\n    checkCudaErrors(hipHostFree(outputData));\n}\n\nvoid rotateKernel(hipTextureObject_t &texObj,\n                  const float          angle,\n                  unsigned int        *d_outputData,\n                  const int            imageWidth,\n                  const int            imageHeight,\n                  hipStream_t         stream)\n{\n    dim3 dimBlock(8, 8, 1);\n    dim3 dimGrid(imageWidth / dimBlock.x, imageHeight / dimBlock.y, 1);\n\n    transformKernel<<<dimGrid, dimBlock, 0, stream>>>(d_outputData, imageWidth, imageHeight, angle, texObj);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/cudaNvSci/imageKernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on multiple GPU using\n * Unified Memory optimized prefetching and usage hints.\n *\n */\n\n// includes, system\n#include <cuda_runtime.h>\n#include <iostream>\n#include <map>\n#include <set>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <utility>\n\n// Utilities and system includes\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <helper_cuda.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nnamespace cg = cooperative_groups;\n\nconst char *sSDKname = \"conjugateGradientMultiDeviceCG\";\n\n#define ENABLE_CPU_DEBUG_CODE 0\n#define THREADS_PER_BLOCK     512\n\n__device__ double grid_dot_result = 0.0;\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = (float)rand() / RAND_MAX + 10.0f;\n    val[1] = (float)rand() / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = (float)rand() / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = (float)rand() / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n// I - contains location of the given non-zero element in the row of the matrix\n// J - contains location of the given non-zero element in the column of the\n// matrix val - contains values of the given non-zero elements of the matrix\n// inputVecX - input vector to be multiplied\n// outputVecY - resultant vector\nvoid cpuSpMV(int *I, int *J, float *val, int nnz, int num_rows, float alpha, float *inputVecX, float *outputVecY)\n{\n    for (int i = 0; i < num_rows; i++) {\n        int num_elems_this_row = I[i + 1] - I[i];\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[I[i] + j] * inputVecX[J[I[i] + j]];\n        }\n        outputVecY[i] = output;\n    }\n\n    return;\n}\n\nfloat dotProduct(float *vecA, float *vecB, int size)\n{\n    float result = 0.0;\n\n    for (int i = 0; i < size; i++) {\n        result = result + (vecA[i] * vecB[i]);\n    }\n\n    return result;\n}\n\nvoid scaleVector(float *vec, float alpha, int size)\n{\n    for (int i = 0; i < size; i++) {\n        vec[i] = alpha * vec[i];\n    }\n}\n\nvoid saxpy(float *x, float *y, float a, int size)\n{\n    for (int i = 0; i < size; i++) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\nvoid cpuConjugateGrad(int *I, int *J, float *val, float *x, float *Ax, float *p, float *r, int nnz, int N, float tol)\n{\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, b, a, na;\n\n    cpuSpMV(I, J, val, nnz, N, alpha, x, Ax);\n    saxpy(Ax, r, alpham1, N);\n\n    float r1 = dotProduct(r, r, N);\n\n    int k = 1;\n\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            scaleVector(p, b, N);\n\n            saxpy(r, p, alpha, N);\n        }\n        else {\n            for (int i = 0; i < N; i++)\n                p[i] = r[i];\n        }\n\n        cpuSpMV(I, J, val, nnz, N, alpha, p, Ax);\n\n        float dot = dotProduct(p, Ax, N);\n        a         = r1 / dot;\n\n        saxpy(p, x, a, N);\n        na = -a;\n        saxpy(Ax, r, na, N);\n\n        r0 = r1;\n        r1 = dotProduct(r, r, N);\n\n        printf(\"\\nCPU code iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n}\n\n// Data filled on CPU needed for MultiGPU operations.\nstruct MultiDeviceData\n{\n    unsigned char *hostMemoryArrivedList;\n    unsigned int   numDevices;\n    unsigned int   deviceRank;\n};\n\n// Class used for coordination of multiple devices.\nclass PeerGroup\n{\n    const MultiDeviceData &data;\n    const cg::grid_group  &grid;\n\n    __device__ unsigned char load_arrived(unsigned char *arrived) const\n    {\n#if __CUDA_ARCH__ < 700\n        return *(volatile unsigned char *)arrived;\n#else\n        unsigned int result;\n        asm volatile(\"ld.acquire.sys.global.u8 %0, [%1];\" : \"=r\"(result) : \"l\"(arrived) : \"memory\");\n        return result;\n#endif\n    }\n\n    __device__ void store_arrived(unsigned char *arrived, unsigned char val) const\n    {\n#if __CUDA_ARCH__ < 700\n        *(volatile unsigned char *)arrived = val;\n#else\n        unsigned int reg_val = val;\n        asm volatile(\"st.release.sys.global.u8 [%1], %0;\" ::\"r\"(reg_val) \"l\"(arrived) : \"memory\");\n\n        // Avoids compiler warnings from unused variable val.\n        (void)(reg_val = reg_val);\n#endif\n    }\n\npublic:\n    __device__ PeerGroup(const MultiDeviceData &data, const cg::grid_group &grid)\n        : data(data)\n        , grid(grid) {};\n\n    __device__ unsigned int size() const { return data.numDevices * grid.size(); }\n\n    __device__ unsigned int thread_rank() const { return data.deviceRank * grid.size() + grid.thread_rank(); }\n\n    __device__ void sync() const\n    {\n        grid.sync();\n\n        // One thread from each grid participates in the sync.\n        if (grid.thread_rank() == 0) {\n            if (data.deviceRank == 0) {\n                // Leader grid waits for others to join and then releases them.\n                // Other GPUs can arrive in any order, so the leader have to wait for\n                // all others.\n                for (int i = 0; i < data.numDevices - 1; i++) {\n                    while (load_arrived(&data.hostMemoryArrivedList[i]) == 0)\n                        ;\n                }\n                for (int i = 0; i < data.numDevices - 1; i++) {\n                    store_arrived(&data.hostMemoryArrivedList[i], 0);\n                }\n                __threadfence_system();\n            }\n            else {\n                // Other grids note their arrival and wait to be released.\n                store_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1], 1);\n                while (load_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1]) == 1)\n                    ;\n            }\n        }\n\n        grid.sync();\n    }\n};\n\n__device__ void gpuSpMV(int             *I,\n                        int             *J,\n                        float           *val,\n                        int              nnz,\n                        int              num_rows,\n                        float            alpha,\n                        float           *inputVecX,\n                        float           *outputVecY,\n                        const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < num_rows; i += peer_group.size()) {\n        int row_elem           = I[i];\n        int next_row_elem      = I[i + 1];\n        int num_elems_this_row = next_row_elem - row_elem;\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[row_elem + j] * inputVecX[J[row_elem + j]];\n        }\n\n        outputVecY[i] = output;\n    }\n}\n\n__device__ void gpuSaxpy(float *x, float *y, float a, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\n__device__ void\ngpuDotProduct(float *vecA, float *vecB, int size, const cg::thread_block &cta, const PeerGroup &peer_group)\n{\n    extern __shared__ double tmp[];\n\n    double temp_sum = 0.0;\n\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        temp_sum += (double)(vecA[i] * vecB[i]);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = temp_sum;\n    }\n\n    cg::sync(cta);\n\n    if (tile32.meta_group_rank() == 0) {\n        temp_sum = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n        temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(&grid_dot_result, temp_sum);\n        }\n    }\n}\n\n__device__ void gpuCopyVector(float *srcA, float *destB, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        destB[i] = srcA[i];\n    }\n}\n\n__device__ void gpuScaleVectorAndSaxpy(float *x, float *y, float a, float scale, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        y[i] = a * x[i] + scale * y[i];\n    }\n}\n\nextern \"C\" __global__ void multiGpuConjugateGradient(int            *I,\n                                                     int            *J,\n                                                     float          *val,\n                                                     float          *x,\n                                                     float          *Ax,\n                                                     float          *p,\n                                                     float          *r,\n                                                     double         *dot_result,\n                                                     int             nnz,\n                                                     int             N,\n                                                     float           tol,\n                                                     MultiDeviceData multi_device_data)\n{\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n    PeerGroup        peer_group(multi_device_data, grid);\n\n    const int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, r1, b, a, na;\n\n    for (int i = peer_group.thread_rank(); i < N; i += peer_group.size()) {\n        r[i] = 1.0;\n        x[i] = 0.0;\n    }\n\n    cg::sync(grid);\n\n    gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, peer_group);\n\n    cg::sync(grid);\n\n    gpuSaxpy(Ax, r, alpham1, N, peer_group);\n\n    cg::sync(grid);\n\n    gpuDotProduct(r, r, N, cta, peer_group);\n\n    cg::sync(grid);\n\n    if (grid.thread_rank() == 0) {\n        atomicAdd_system(dot_result, grid_dot_result);\n        grid_dot_result = 0.0;\n    }\n    peer_group.sync();\n\n    r1 = *dot_result;\n\n    int k = 1;\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            gpuScaleVectorAndSaxpy(r, p, alpha, b, N, peer_group);\n        }\n        else {\n            gpuCopyVector(r, p, N, peer_group);\n        }\n\n        peer_group.sync();\n\n        gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, peer_group);\n\n        if (peer_group.thread_rank() == 0) {\n            *dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        gpuDotProduct(p, Ax, N, cta, peer_group);\n\n        cg::sync(grid);\n\n        if (grid.thread_rank() == 0) {\n            atomicAdd_system(dot_result, grid_dot_result);\n            grid_dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        a = r1 / *dot_result;\n\n        gpuSaxpy(p, x, a, N, peer_group);\n\n        na = -a;\n\n        gpuSaxpy(Ax, r, na, N, peer_group);\n\n        r0 = r1;\n\n        peer_group.sync();\n\n        if (peer_group.thread_rank() == 0) {\n            *dot_result = 0.0;\n        }\n\n        peer_group.sync();\n\n        gpuDotProduct(r, r, N, cta, peer_group);\n\n        cg::sync(grid);\n\n        if (grid.thread_rank() == 0) {\n            atomicAdd_system(dot_result, grid_dot_result);\n            grid_dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        r1 = *dot_result;\n        k++;\n    }\n}\n\n// Map of device version to device number\nstd::multimap<std::pair<int, int>, int> getIdenticalGPUs()\n{\n    int numGpus = 0;\n    checkCudaErrors(cudaGetDeviceCount(&numGpus));\n\n    std::multimap<std::pair<int, int>, int> identicalGpus;\n\n    for (int i = 0; i < numGpus; i++) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));\n\n        // Filter unsupported devices\n        if (deviceProp.cooperativeLaunch && deviceProp.concurrentManagedAccess) {\n            identicalGpus.emplace(std::make_pair(deviceProp.major, deviceProp.minor), i);\n        }\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    return identicalGpus;\n}\n\nint main(int argc, char **argv)\n{\n    constexpr size_t kNumGpusRequired = 2;\n    int              N = 0, nz = 0, *I = NULL, *J = NULL;\n    float           *val = NULL;\n    const float      tol = 1e-5f;\n    float           *x;\n    float            rhs = 1.0;\n    float            r1;\n    float           *r, *p, *Ax;\n\n    printf(\"Starting [%s]...\\n\", sSDKname);\n    auto gpusByArch = getIdenticalGPUs();\n\n    auto it  = gpusByArch.begin();\n    auto end = gpusByArch.end();\n\n    auto bestFit = std::make_pair(it, it);\n    // use std::distance to find the largest number of GPUs amongst architectures\n    auto distance = [](decltype(bestFit) p) { return std::distance(p.first, p.second); };\n\n    // Read each unique key/pair element in order\n    for (; it != end; it = gpusByArch.upper_bound(it->first)) {\n        // first and second are iterators bounded within the architecture group\n        auto testFit = gpusByArch.equal_range(it->first);\n        // Always use devices with highest architecture version or whichever has the\n        // most devices available\n        if (distance(bestFit) <= distance(testFit))\n            bestFit = testFit;\n    }\n\n    if (distance(bestFit) < kNumGpusRequired) {\n        printf(\"No two or more GPUs with same architecture capable of \"\n               \"concurrentManagedAccess found. \"\n               \"\\nWaiving the sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::set<int> bestFitDeviceIds;\n\n    // Check & select peer-to-peer access capable GPU devices as enabling p2p\n    // access between participating GPUs gives better performance.\n    for (auto itr = bestFit.first; itr != bestFit.second; itr++) {\n        int deviceId = itr->second;\n        checkCudaErrors(cudaSetDevice(deviceId));\n\n        std::for_each(itr, bestFit.second, [&deviceId, &bestFitDeviceIds, &kNumGpusRequired](decltype(*itr) mapPair) {\n            if (deviceId != mapPair.second) {\n                int access = 0;\n                checkCudaErrors(cudaDeviceCanAccessPeer(&access, deviceId, mapPair.second));\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", deviceId, access ? \"CAN\" : \"CANNOT\", mapPair.second);\n                if (access && bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(deviceId);\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n            }\n        });\n\n        if (bestFitDeviceIds.size() >= kNumGpusRequired) {\n            printf(\"Selected p2p capable devices - \");\n            for (auto devicesItr = bestFitDeviceIds.begin(); devicesItr != bestFitDeviceIds.end(); devicesItr++) {\n                printf(\"deviceId = %d  \", *devicesItr);\n            }\n            printf(\"\\n\");\n            break;\n        }\n    }\n\n    // if bestFitDeviceIds.size() == 0 it means the GPUs in system are not p2p\n    // capable, hence we add it without p2p capability check.\n    if (!bestFitDeviceIds.size()) {\n        printf(\"Devices involved are not p2p capable.. selecting %zu of them\\n\", kNumGpusRequired);\n        std::for_each(\n            bestFit.first, bestFit.second, [&bestFitDeviceIds, &kNumGpusRequired](decltype(*bestFit.first) mapPair) {\n                if (bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n                // Insert the sequence into the deviceIds set\n            });\n    }\n    else {\n        // perform cudaDeviceEnablePeerAccess in both directions for all\n        // participating devices.\n        for (auto p1_itr = bestFitDeviceIds.begin(); p1_itr != bestFitDeviceIds.end(); p1_itr++) {\n            checkCudaErrors(cudaSetDevice(*p1_itr));\n            for (auto p2_itr = bestFitDeviceIds.begin(); p2_itr != bestFitDeviceIds.end(); p2_itr++) {\n                if (*p1_itr != *p2_itr) {\n                    checkCudaErrors(cudaDeviceEnablePeerAccess(*p2_itr, 0));\n                    checkCudaErrors(cudaSetDevice(*p1_itr));\n                }\n            }\n        }\n    }\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 10485760 * 2;\n    nz = (N - 2) * 3 + 4;\n\n    checkCudaErrors(cudaMallocManaged((void **)&I, sizeof(int) * (N + 1)));\n    checkCudaErrors(cudaMallocManaged((void **)&J, sizeof(int) * nz));\n    checkCudaErrors(cudaMallocManaged((void **)&val, sizeof(float) * nz));\n\n    float *val_cpu = (float *)malloc(sizeof(float) * nz);\n\n    genTridiag(I, J, val_cpu, N, nz);\n\n    memcpy(val, val_cpu, sizeof(float) * nz);\n    cudaMemLocation deviceLoc;\n    deviceLoc.type = cudaMemLocationTypeDevice;\n    deviceLoc.id   = 0; // Device location with initial device 0\n    checkCudaErrors(cudaMemAdvise(I, sizeof(int) * (N + 1), cudaMemAdviseSetReadMostly, deviceLoc));\n    checkCudaErrors(cudaMemAdvise(J, sizeof(int) * nz, cudaMemAdviseSetReadMostly, deviceLoc));\n    checkCudaErrors(cudaMemAdvise(val, sizeof(float) * nz, cudaMemAdviseSetReadMostly, deviceLoc));\n\n    checkCudaErrors(cudaMallocManaged((void **)&x, sizeof(float) * N));\n\n    double *dot_result;\n    checkCudaErrors(cudaMallocManaged((void **)&dot_result, sizeof(double)));\n\n    checkCudaErrors(cudaMemset(dot_result, 0, sizeof(double)));\n\n    // temp memory for ConjugateGradient\n    checkCudaErrors(cudaMallocManaged((void **)&r, N * sizeof(float)));\n    checkCudaErrors(cudaMallocManaged((void **)&p, N * sizeof(float)));\n    checkCudaErrors(cudaMallocManaged((void **)&Ax, N * sizeof(float)));\n\n    std::cout << \"\\nRunning on GPUs = \" << kNumGpusRequired << std::endl;\n    cudaStream_t nStreams[kNumGpusRequired];\n\n    int  sMemSize       = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);\n    int  numBlocksPerSm = INT_MAX;\n    int  numThreads     = THREADS_PER_BLOCK;\n    int  numSms         = INT_MAX;\n    auto deviceId       = bestFitDeviceIds.begin();\n\n    // set numSms & numBlocksPerSm to be lowest of 2 devices\n    while (deviceId != bestFitDeviceIds.end()) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaSetDevice(*deviceId));\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, *deviceId));\n\n        int numBlocksPerSm_current = 0;\n        checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n            &numBlocksPerSm_current, multiGpuConjugateGradient, numThreads, sMemSize));\n\n        if (numBlocksPerSm > numBlocksPerSm_current) {\n            numBlocksPerSm = numBlocksPerSm_current;\n        }\n        if (numSms > deviceProp.multiProcessorCount) {\n            numSms = deviceProp.multiProcessorCount;\n        }\n        deviceId++;\n    }\n\n    if (!numBlocksPerSm) {\n        printf(\"Max active blocks per SM is returned as 0.\\n Hence, Waiving the \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int device_count       = 0;\n    int totalThreadsPerGPU = numSms * numBlocksPerSm * THREADS_PER_BLOCK;\n    deviceId               = bestFitDeviceIds.begin();\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(cudaSetDevice(*deviceId));\n        checkCudaErrors(cudaStreamCreate(&nStreams[device_count]));\n\n        int perGPUIter = N / (totalThreadsPerGPU * kNumGpusRequired);\n        int offset_Ax  = device_count * totalThreadsPerGPU;\n        int offset_r   = device_count * totalThreadsPerGPU;\n        int offset_p   = device_count * totalThreadsPerGPU;\n        int offset_x   = device_count * totalThreadsPerGPU;\n\n        // Create device location with specific device ID\n        cudaMemLocation deviceLoc;\n        deviceLoc.type = cudaMemLocationTypeDevice;\n        deviceLoc.id   = *deviceId;\n        checkCudaErrors(cudaMemPrefetchAsync(I, sizeof(int) * N, deviceLoc, 0, nStreams[device_count]));\n        checkCudaErrors(cudaMemPrefetchAsync(val, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));\n        checkCudaErrors(cudaMemPrefetchAsync(J, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));\n\n        if (offset_Ax <= N) {\n            for (int i = 0; i < perGPUIter; i++) {\n                cudaMemAdvise(\n                    Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);\n                cudaMemAdvise(\n                    r + offset_r, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);\n                cudaMemAdvise(\n                    x + offset_x, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);\n                cudaMemAdvise(\n                    p + offset_p, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetPreferredLocation, deviceLoc);\n\n                cudaMemAdvise(\n                    Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);\n                cudaMemAdvise(r + offset_r, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);\n                cudaMemAdvise(p + offset_p, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);\n                cudaMemAdvise(x + offset_x, sizeof(float) * totalThreadsPerGPU, cudaMemAdviseSetAccessedBy, deviceLoc);\n\n                offset_Ax += totalThreadsPerGPU * kNumGpusRequired;\n                offset_r += totalThreadsPerGPU * kNumGpusRequired;\n                offset_p += totalThreadsPerGPU * kNumGpusRequired;\n                offset_x += totalThreadsPerGPU * kNumGpusRequired;\n\n                if (offset_Ax >= N) {\n                    break;\n                }\n            }\n        }\n\n        device_count++;\n        deviceId++;\n    }\n\n#if ENABLE_CPU_DEBUG_CODE\n    float *Ax_cpu = (float *)malloc(sizeof(float) * N);\n    float *r_cpu  = (float *)malloc(sizeof(float) * N);\n    float *p_cpu  = (float *)malloc(sizeof(float) * N);\n    float *x_cpu  = (float *)malloc(sizeof(float) * N);\n\n    for (int i = 0; i < N; i++) {\n        r_cpu[i]  = 1.0;\n        Ax_cpu[i] = x_cpu[i] = 0.0;\n    }\n#endif\n\n    printf(\"Total threads per GPU = %d numBlocksPerSm  = %d\\n\",\n           numSms * numBlocksPerSm * THREADS_PER_BLOCK,\n           numBlocksPerSm);\n    dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);\n\n    // Structure used for cross-grid synchronization.\n    MultiDeviceData multi_device_data;\n    checkCudaErrors(cudaHostAlloc(&multi_device_data.hostMemoryArrivedList,\n                                  (kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList),\n                                  cudaHostAllocPortable));\n    memset(multi_device_data.hostMemoryArrivedList,\n           0,\n           (kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList));\n    multi_device_data.numDevices = kNumGpusRequired;\n    multi_device_data.deviceRank = 0;\n\n    void *kernelArgs[] = {\n        (void *)&I,\n        (void *)&J,\n        (void *)&val,\n        (void *)&x,\n        (void *)&Ax,\n        (void *)&p,\n        (void *)&r,\n        (void *)&dot_result,\n        (void *)&nz,\n        (void *)&N,\n        (void *)&tol,\n        (void *)&multi_device_data,\n    };\n\n    printf(\"Launching kernel\\n\");\n\n    deviceId     = bestFitDeviceIds.begin();\n    device_count = 0;\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(cudaSetDevice(*deviceId));\n        checkCudaErrors(cudaLaunchCooperativeKernel(\n            (void *)multiGpuConjugateGradient, dimGrid, dimBlock, kernelArgs, sMemSize, nStreams[device_count++]));\n        multi_device_data.deviceRank++;\n        deviceId++;\n    }\n\n    // Use cudaMemLocationTypeHost for optimal host memory location\n    cudaMemLocation hostLoc;\n    hostLoc.type = cudaMemLocationTypeHost;\n    checkCudaErrors(cudaMemPrefetchAsync(x, sizeof(float) * N, hostLoc, 0));\n    checkCudaErrors(cudaMemPrefetchAsync(dot_result, sizeof(double), hostLoc, 0));\n\n    deviceId     = bestFitDeviceIds.begin();\n    device_count = 0;\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(cudaSetDevice(*deviceId));\n        checkCudaErrors(cudaStreamSynchronize(nStreams[device_count++]));\n        deviceId++;\n    }\n\n    r1 = (float)*dot_result;\n\n    printf(\"GPU Final, residual = %e \\n  \", sqrt(r1));\n\n#if ENABLE_CPU_DEBUG_CODE\n    cpuConjugateGrad(I, J, val, x_cpu, Ax_cpu, p_cpu, r_cpu, nz, N, tol);\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val_cpu[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n    checkCudaErrors(cudaFreeHost(multi_device_data.hostMemoryArrivedList));\n    checkCudaErrors(cudaFree(I));\n    checkCudaErrors(cudaFree(J));\n    checkCudaErrors(cudaFree(val));\n    checkCudaErrors(cudaFree(x));\n    checkCudaErrors(cudaFree(r));\n    checkCudaErrors(cudaFree(p));\n    checkCudaErrors(cudaFree(Ax));\n    checkCudaErrors(cudaFree(dot_result));\n    free(val_cpu);\n\n#if ENABLE_CPU_DEBUG_CODE\n    free(Ax_cpu);\n    free(r_cpu);\n    free(p_cpu);\n    free(x_cpu);\n#endif\n\n    printf(\"Test Summary:  Error amount = %f \\n\", err);\n    fprintf(stdout, \"&&&& conjugateGradientMultiDeviceCG %s\\n\", (sqrt(r1) < tol) ? \"PASSED\" : \"FAILED\");\n    exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on multiple GPU using\n * Unified Memory optimized prefetching and usage hints.\n *\n */\n\n// includes, system\n#include <hip/hip_runtime.h>\n#include <iostream>\n#include <map>\n#include <set>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <utility>\n\n// Utilities and system includes\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <hip/hip_runtime_api.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nnamespace cg = cooperative_groups;\n\nconst char *sSDKname = \"conjugateGradientMultiDeviceCG\";\n\n#define ENABLE_CPU_DEBUG_CODE 0\n#define THREADS_PER_BLOCK     512\n\n__device__ double grid_dot_result = 0.0;\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = (float)rand() / RAND_MAX + 10.0f;\n    val[1] = (float)rand() / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = (float)rand() / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = (float)rand() / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n// I - contains location of the given non-zero element in the row of the matrix\n// J - contains location of the given non-zero element in the column of the\n// matrix val - contains values of the given non-zero elements of the matrix\n// inputVecX - input vector to be multiplied\n// outputVecY - resultant vector\nvoid cpuSpMV(int *I, int *J, float *val, int nnz, int num_rows, float alpha, float *inputVecX, float *outputVecY)\n{\n    for (int i = 0; i < num_rows; i++) {\n        int num_elems_this_row = I[i + 1] - I[i];\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[I[i] + j] * inputVecX[J[I[i] + j]];\n        }\n        outputVecY[i] = output;\n    }\n\n    return;\n}\n\nfloat dotProduct(float *vecA, float *vecB, int size)\n{\n    float result = 0.0;\n\n    for (int i = 0; i < size; i++) {\n        result = result + (vecA[i] * vecB[i]);\n    }\n\n    return result;\n}\n\nvoid scaleVector(float *vec, float alpha, int size)\n{\n    for (int i = 0; i < size; i++) {\n        vec[i] = alpha * vec[i];\n    }\n}\n\nvoid saxpy(float *x, float *y, float a, int size)\n{\n    for (int i = 0; i < size; i++) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\nvoid cpuConjugateGrad(int *I, int *J, float *val, float *x, float *Ax, float *p, float *r, int nnz, int N, float tol)\n{\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, b, a, na;\n\n    cpuSpMV(I, J, val, nnz, N, alpha, x, Ax);\n    saxpy(Ax, r, alpham1, N);\n\n    float r1 = dotProduct(r, r, N);\n\n    int k = 1;\n\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            scaleVector(p, b, N);\n\n            saxpy(r, p, alpha, N);\n        }\n        else {\n            for (int i = 0; i < N; i++)\n                p[i] = r[i];\n        }\n\n        cpuSpMV(I, J, val, nnz, N, alpha, p, Ax);\n\n        float dot = dotProduct(p, Ax, N);\n        a         = r1 / dot;\n\n        saxpy(p, x, a, N);\n        na = -a;\n        saxpy(Ax, r, na, N);\n\n        r0 = r1;\n        r1 = dotProduct(r, r, N);\n\n        printf(\"\\nCPU code iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n}\n\n// Data filled on CPU needed for MultiGPU operations.\nstruct MultiDeviceData\n{\n    unsigned char *hostMemoryArrivedList;\n    unsigned int   numDevices;\n    unsigned int   deviceRank;\n};\n\n// Class used for coordination of multiple devices.\nclass PeerGroup\n{\n    const MultiDeviceData &data;\n    const cg::grid_group  &grid;\n\n    __device__ unsigned char load_arrived(unsigned char *arrived) const\n    {\n#if __CUDA_ARCH__ < 700\n        return *(volatile unsigned char *)arrived;\n#else\n        unsigned int result;\n        asm volatile(\"ld.acquire.sys.global.u8 %0, [%1];\" : \"=r\"(result) : \"l\"(arrived) : \"memory\");\n        return result;\n#endif\n    }\n\n    __device__ void store_arrived(unsigned char *arrived, unsigned char val) const\n    {\n#if __CUDA_ARCH__ < 700\n        *(volatile unsigned char *)arrived = val;\n#else\n        unsigned int reg_val = val;\n        asm volatile(\"st.release.sys.global.u8 [%1], %0;\" ::\"r\"(reg_val) \"l\"(arrived) : \"memory\");\n\n        // Avoids compiler warnings from unused variable val.\n        (void)(reg_val = reg_val);\n#endif\n    }\n\npublic:\n    __device__ PeerGroup(const MultiDeviceData &data, const cg::grid_group &grid)\n        : data(data)\n        , grid(grid) {};\n\n    __device__ unsigned int size() const { return data.numDevices * grid.size(); }\n\n    __device__ unsigned int thread_rank() const { return data.deviceRank * grid.size() + grid.thread_rank(); }\n\n    __device__ void sync() const\n    {\n        grid.sync();\n\n        // One thread from each grid participates in the sync.\n        if (grid.thread_rank() == 0) {\n            if (data.deviceRank == 0) {\n                // Leader grid waits for others to join and then releases them.\n                // Other GPUs can arrive in any order, so the leader have to wait for\n                // all others.\n                for (int i = 0; i < data.numDevices - 1; i++) {\n                    while (load_arrived(&data.hostMemoryArrivedList[i]) == 0)\n                        ;\n                }\n                for (int i = 0; i < data.numDevices - 1; i++) {\n                    store_arrived(&data.hostMemoryArrivedList[i], 0);\n                }\n                __threadfence_system();\n            }\n            else {\n                // Other grids note their arrival and wait to be released.\n                store_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1], 1);\n                while (load_arrived(&data.hostMemoryArrivedList[data.deviceRank - 1]) == 1)\n                    ;\n            }\n        }\n\n        grid.sync();\n    }\n};\n\n__device__ void gpuSpMV(int             *I,\n                        int             *J,\n                        float           *val,\n                        int              nnz,\n                        int              num_rows,\n                        float            alpha,\n                        float           *inputVecX,\n                        float           *outputVecY,\n                        const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < num_rows; i += peer_group.size()) {\n        int row_elem           = I[i];\n        int next_row_elem      = I[i + 1];\n        int num_elems_this_row = next_row_elem - row_elem;\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[row_elem + j] * inputVecX[J[row_elem + j]];\n        }\n\n        outputVecY[i] = output;\n    }\n}\n\n__device__ void gpuSaxpy(float *x, float *y, float a, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\n__device__ void\ngpuDotProduct(float *vecA, float *vecB, int size, const cg::thread_block &cta, const PeerGroup &peer_group)\n{\n    extern __shared__ double tmp[];\n\n    double temp_sum = 0.0;\n\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        temp_sum += (double)(vecA[i] * vecB[i]);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = temp_sum;\n    }\n\n    cg::sync(cta);\n\n    if (tile32.meta_group_rank() == 0) {\n        temp_sum = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n        temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(&grid_dot_result, temp_sum);\n        }\n    }\n}\n\n__device__ void gpuCopyVector(float *srcA, float *destB, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        destB[i] = srcA[i];\n    }\n}\n\n__device__ void gpuScaleVectorAndSaxpy(float *x, float *y, float a, float scale, int size, const PeerGroup &peer_group)\n{\n    for (int i = peer_group.thread_rank(); i < size; i += peer_group.size()) {\n        y[i] = a * x[i] + scale * y[i];\n    }\n}\n\nextern \"C\" __global__ void multiGpuConjugateGradient(int            *I,\n                                                     int            *J,\n                                                     float          *val,\n                                                     float          *x,\n                                                     float          *Ax,\n                                                     float          *p,\n                                                     float          *r,\n                                                     double         *dot_result,\n                                                     int             nnz,\n                                                     int             N,\n                                                     float           tol,\n                                                     MultiDeviceData multi_device_data)\n{\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n    PeerGroup        peer_group(multi_device_data, grid);\n\n    const int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, r1, b, a, na;\n\n    for (int i = peer_group.thread_rank(); i < N; i += peer_group.size()) {\n        r[i] = 1.0;\n        x[i] = 0.0;\n    }\n\n    cg::sync(grid);\n\n    gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, peer_group);\n\n    cg::sync(grid);\n\n    gpuSaxpy(Ax, r, alpham1, N, peer_group);\n\n    cg::sync(grid);\n\n    gpuDotProduct(r, r, N, cta, peer_group);\n\n    cg::sync(grid);\n\n    if (grid.thread_rank() == 0) {\n        atomicAdd_system(dot_result, grid_dot_result);\n        grid_dot_result = 0.0;\n    }\n    peer_group.sync();\n\n    r1 = *dot_result;\n\n    int k = 1;\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            gpuScaleVectorAndSaxpy(r, p, alpha, b, N, peer_group);\n        }\n        else {\n            gpuCopyVector(r, p, N, peer_group);\n        }\n\n        peer_group.sync();\n\n        gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, peer_group);\n\n        if (peer_group.thread_rank() == 0) {\n            *dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        gpuDotProduct(p, Ax, N, cta, peer_group);\n\n        cg::sync(grid);\n\n        if (grid.thread_rank() == 0) {\n            atomicAdd_system(dot_result, grid_dot_result);\n            grid_dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        a = r1 / *dot_result;\n\n        gpuSaxpy(p, x, a, N, peer_group);\n\n        na = -a;\n\n        gpuSaxpy(Ax, r, na, N, peer_group);\n\n        r0 = r1;\n\n        peer_group.sync();\n\n        if (peer_group.thread_rank() == 0) {\n            *dot_result = 0.0;\n        }\n\n        peer_group.sync();\n\n        gpuDotProduct(r, r, N, cta, peer_group);\n\n        cg::sync(grid);\n\n        if (grid.thread_rank() == 0) {\n            atomicAdd_system(dot_result, grid_dot_result);\n            grid_dot_result = 0.0;\n        }\n        peer_group.sync();\n\n        r1 = *dot_result;\n        k++;\n    }\n}\n\n// Map of device version to device number\nstd::multimap<std::pair<int, int>, int> getIdenticalGPUs()\n{\n    int numGpus = 0;\n    checkCudaErrors(hipGetDeviceCount(&numGpus));\n\n    std::multimap<std::pair<int, int>, int> identicalGpus;\n\n    for (int i = 0; i < numGpus; i++) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, i));\n\n        // Filter unsupported devices\n        if (deviceProp.cooperativeLaunch && deviceProp.concurrentManagedAccess) {\n            identicalGpus.emplace(std::make_pair(deviceProp.major, deviceProp.minor), i);\n        }\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    return identicalGpus;\n}\n\nint main(int argc, char **argv)\n{\n    constexpr size_t kNumGpusRequired = 2;\n    int              N = 0, nz = 0, *I = NULL, *J = NULL;\n    float           *val = NULL;\n    const float      tol = 1e-5f;\n    float           *x;\n    float            rhs = 1.0;\n    float            r1;\n    float           *r, *p, *Ax;\n\n    printf(\"Starting [%s]...\\n\", sSDKname);\n    auto gpusByArch = getIdenticalGPUs();\n\n    auto it  = gpusByArch.begin();\n    auto end = gpusByArch.end();\n\n    auto bestFit = std::make_pair(it, it);\n    // use std::distance to find the largest number of GPUs amongst architectures\n    auto distance = [](decltype(bestFit) p) { return std::distance(p.first, p.second); };\n\n    // Read each unique key/pair element in order\n    for (; it != end; it = gpusByArch.upper_bound(it->first)) {\n        // first and second are iterators bounded within the architecture group\n        auto testFit = gpusByArch.equal_range(it->first);\n        // Always use devices with highest architecture version or whichever has the\n        // most devices available\n        if (distance(bestFit) <= distance(testFit))\n            bestFit = testFit;\n    }\n\n    if (distance(bestFit) < kNumGpusRequired) {\n        printf(\"No two or more GPUs with same architecture capable of \"\n               \"concurrentManagedAccess found. \"\n               \"\\nWaiving the sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::set<int> bestFitDeviceIds;\n\n    // Check & select peer-to-peer access capable GPU devices as enabling p2p\n    // access between participating GPUs gives better performance.\n    for (auto itr = bestFit.first; itr != bestFit.second; itr++) {\n        int deviceId = itr->second;\n        checkCudaErrors(hipSetDevice(deviceId));\n\n        std::for_each(itr, bestFit.second, [&deviceId, &bestFitDeviceIds, &kNumGpusRequired](decltype(*itr) mapPair) {\n            if (deviceId != mapPair.second) {\n                int access = 0;\n                checkCudaErrors(hipDeviceCanAccessPeer(&access, deviceId, mapPair.second));\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", deviceId, access ? \"CAN\" : \"CANNOT\", mapPair.second);\n                if (access && bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(deviceId);\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n            }\n        });\n\n        if (bestFitDeviceIds.size() >= kNumGpusRequired) {\n            printf(\"Selected p2p capable devices - \");\n            for (auto devicesItr = bestFitDeviceIds.begin(); devicesItr != bestFitDeviceIds.end(); devicesItr++) {\n                printf(\"deviceId = %d  \", *devicesItr);\n            }\n            printf(\"\\n\");\n            break;\n        }\n    }\n\n    // if bestFitDeviceIds.size() == 0 it means the GPUs in system are not p2p\n    // capable, hence we add it without p2p capability check.\n    if (!bestFitDeviceIds.size()) {\n        printf(\"Devices involved are not p2p capable.. selecting %zu of them\\n\", kNumGpusRequired);\n        std::for_each(\n            bestFit.first, bestFit.second, [&bestFitDeviceIds, &kNumGpusRequired](decltype(*bestFit.first) mapPair) {\n                if (bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n                // Insert the sequence into the deviceIds set\n            });\n    }\n    else {\n        // perform cudaDeviceEnablePeerAccess in both directions for all\n        // participating devices.\n        for (auto p1_itr = bestFitDeviceIds.begin(); p1_itr != bestFitDeviceIds.end(); p1_itr++) {\n            checkCudaErrors(hipSetDevice(*p1_itr));\n            for (auto p2_itr = bestFitDeviceIds.begin(); p2_itr != bestFitDeviceIds.end(); p2_itr++) {\n                if (*p1_itr != *p2_itr) {\n                    checkCudaErrors(hipDeviceEnablePeerAccess(*p2_itr, 0));\n                    checkCudaErrors(hipSetDevice(*p1_itr));\n                }\n            }\n        }\n    }\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 10485760 * 2;\n    nz = (N - 2) * 3 + 4;\n\n    checkCudaErrors(hipMallocManaged((void **)&I, sizeof(int) * (N + 1)));\n    checkCudaErrors(hipMallocManaged((void **)&J, sizeof(int) * nz));\n    checkCudaErrors(hipMallocManaged((void **)&val, sizeof(float) * nz));\n\n    float *val_cpu = (float *)malloc(sizeof(float) * nz);\n\n    genTridiag(I, J, val_cpu, N, nz);\n\n    memcpy(val, val_cpu, sizeof(float) * nz);\n    hipMemLocation deviceLoc;\n    deviceLoc.type = hipMemLocationTypeDevice;\n    deviceLoc.id   = 0; // Device location with initial device 0\n    checkCudaErrors(hipMemAdvise(I, sizeof(int) * (N + 1), hipMemAdviseSetReadMostly, deviceLoc));\n    checkCudaErrors(hipMemAdvise(J, sizeof(int) * nz, hipMemAdviseSetReadMostly, deviceLoc));\n    checkCudaErrors(hipMemAdvise(val, sizeof(float) * nz, hipMemAdviseSetReadMostly, deviceLoc));\n\n    checkCudaErrors(hipMallocManaged((void **)&x, sizeof(float) * N));\n\n    double *dot_result;\n    checkCudaErrors(hipMallocManaged((void **)&dot_result, sizeof(double)));\n\n    checkCudaErrors(hipMemset(dot_result, 0, sizeof(double)));\n\n    // temp memory for ConjugateGradient\n    checkCudaErrors(hipMallocManaged((void **)&r, N * sizeof(float)));\n    checkCudaErrors(hipMallocManaged((void **)&p, N * sizeof(float)));\n    checkCudaErrors(hipMallocManaged((void **)&Ax, N * sizeof(float)));\n\n    std::cout << \"\\nRunning on GPUs = \" << kNumGpusRequired << std::endl;\n    hipStream_t nStreams[kNumGpusRequired];\n\n    int  sMemSize       = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);\n    int  numBlocksPerSm = INT_MAX;\n    int  numThreads     = THREADS_PER_BLOCK;\n    int  numSms         = INT_MAX;\n    auto deviceId       = bestFitDeviceIds.begin();\n\n    // set numSms & numBlocksPerSm to be lowest of 2 devices\n    while (deviceId != bestFitDeviceIds.end()) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipSetDevice(*deviceId));\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, *deviceId));\n\n        int numBlocksPerSm_current = 0;\n        checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(\n            &numBlocksPerSm_current, multiGpuConjugateGradient, numThreads, sMemSize));\n\n        if (numBlocksPerSm > numBlocksPerSm_current) {\n            numBlocksPerSm = numBlocksPerSm_current;\n        }\n        if (numSms > deviceProp.multiProcessorCount) {\n            numSms = deviceProp.multiProcessorCount;\n        }\n        deviceId++;\n    }\n\n    if (!numBlocksPerSm) {\n        printf(\"Max active blocks per SM is returned as 0.\\n Hence, Waiving the \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int device_count       = 0;\n    int totalThreadsPerGPU = numSms * numBlocksPerSm * THREADS_PER_BLOCK;\n    deviceId               = bestFitDeviceIds.begin();\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(hipSetDevice(*deviceId));\n        checkCudaErrors(hipStreamCreate(&nStreams[device_count]));\n\n        int perGPUIter = N / (totalThreadsPerGPU * kNumGpusRequired);\n        int offset_Ax  = device_count * totalThreadsPerGPU;\n        int offset_r   = device_count * totalThreadsPerGPU;\n        int offset_p   = device_count * totalThreadsPerGPU;\n        int offset_x   = device_count * totalThreadsPerGPU;\n\n        // Create device location with specific device ID\n        hipMemLocation deviceLoc;\n        deviceLoc.type = hipMemLocationTypeDevice;\n        deviceLoc.id   = *deviceId;\n        checkCudaErrors(hipMemPrefetchAsync(I, sizeof(int) * N, deviceLoc, 0, nStreams[device_count]));\n        checkCudaErrors(hipMemPrefetchAsync(val, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));\n        checkCudaErrors(hipMemPrefetchAsync(J, sizeof(float) * nz, deviceLoc, 0, nStreams[device_count]));\n\n        if (offset_Ax <= N) {\n            for (int i = 0; i < perGPUIter; i++) {\n                hipMemAdvise(\n                    Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetPreferredLocation, deviceLoc);\n                hipMemAdvise(\n                    r + offset_r, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetPreferredLocation, deviceLoc);\n                hipMemAdvise(\n                    x + offset_x, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetPreferredLocation, deviceLoc);\n                hipMemAdvise(\n                    p + offset_p, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetPreferredLocation, deviceLoc);\n\n                hipMemAdvise(\n                    Ax + offset_Ax, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetAccessedBy, deviceLoc);\n                hipMemAdvise(r + offset_r, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetAccessedBy, deviceLoc);\n                hipMemAdvise(p + offset_p, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetAccessedBy, deviceLoc);\n                hipMemAdvise(x + offset_x, sizeof(float) * totalThreadsPerGPU, hipMemAdviseSetAccessedBy, deviceLoc);\n\n                offset_Ax += totalThreadsPerGPU * kNumGpusRequired;\n                offset_r += totalThreadsPerGPU * kNumGpusRequired;\n                offset_p += totalThreadsPerGPU * kNumGpusRequired;\n                offset_x += totalThreadsPerGPU * kNumGpusRequired;\n\n                if (offset_Ax >= N) {\n                    break;\n                }\n            }\n        }\n\n        device_count++;\n        deviceId++;\n    }\n\n#if ENABLE_CPU_DEBUG_CODE\n    float *Ax_cpu = (float *)malloc(sizeof(float) * N);\n    float *r_cpu  = (float *)malloc(sizeof(float) * N);\n    float *p_cpu  = (float *)malloc(sizeof(float) * N);\n    float *x_cpu  = (float *)malloc(sizeof(float) * N);\n\n    for (int i = 0; i < N; i++) {\n        r_cpu[i]  = 1.0;\n        Ax_cpu[i] = x_cpu[i] = 0.0;\n    }\n#endif\n\n    printf(\"Total threads per GPU = %d numBlocksPerSm  = %d\\n\",\n           numSms * numBlocksPerSm * THREADS_PER_BLOCK,\n           numBlocksPerSm);\n    dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);\n\n    // Structure used for cross-grid synchronization.\n    MultiDeviceData multi_device_data;\n    checkCudaErrors(hipHostAlloc(&multi_device_data.hostMemoryArrivedList,\n                                  (kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList),\n                                  hipHostMallocPortable));\n    memset(multi_device_data.hostMemoryArrivedList,\n           0,\n           (kNumGpusRequired - 1) * sizeof(*multi_device_data.hostMemoryArrivedList));\n    multi_device_data.numDevices = kNumGpusRequired;\n    multi_device_data.deviceRank = 0;\n\n    void *kernelArgs[] = {\n        (void *)&I,\n        (void *)&J,\n        (void *)&val,\n        (void *)&x,\n        (void *)&Ax,\n        (void *)&p,\n        (void *)&r,\n        (void *)&dot_result,\n        (void *)&nz,\n        (void *)&N,\n        (void *)&tol,\n        (void *)&multi_device_data,\n    };\n\n    printf(\"Launching kernel\\n\");\n\n    deviceId     = bestFitDeviceIds.begin();\n    device_count = 0;\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(hipSetDevice(*deviceId));\n        checkCudaErrors(hipLaunchCooperativeKernel(reinterpret_cast<const void*>((void *)multiGpuConjugateGradient), dimGrid, dimBlock, kernelArgs, sMemSize, nStreams[device_count++]));\n        multi_device_data.deviceRank++;\n        deviceId++;\n    }\n\n    // Use cudaMemLocationTypeHost for optimal host memory location\n    hipMemLocation hostLoc;\n    hostLoc.type = hipMemLocationTypeHost;\n    checkCudaErrors(hipMemPrefetchAsync(x, sizeof(float) * N, hostLoc, 0));\n    checkCudaErrors(hipMemPrefetchAsync(dot_result, sizeof(double), hostLoc, 0));\n\n    deviceId     = bestFitDeviceIds.begin();\n    device_count = 0;\n    while (deviceId != bestFitDeviceIds.end()) {\n        checkCudaErrors(hipSetDevice(*deviceId));\n        checkCudaErrors(hipStreamSynchronize(nStreams[device_count++]));\n        deviceId++;\n    }\n\n    r1 = (float)*dot_result;\n\n    printf(\"GPU Final, residual = %e \\n  \", sqrt(r1));\n\n#if ENABLE_CPU_DEBUG_CODE\n    cpuConjugateGrad(I, J, val, x_cpu, Ax_cpu, p_cpu, r_cpu, nz, N, tol);\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val_cpu[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n    checkCudaErrors(hipHostFree(multi_device_data.hostMemoryArrivedList));\n    checkCudaErrors(hipFree(I));\n    checkCudaErrors(hipFree(J));\n    checkCudaErrors(hipFree(val));\n    checkCudaErrors(hipFree(x));\n    checkCudaErrors(hipFree(r));\n    checkCudaErrors(hipFree(p));\n    checkCudaErrors(hipFree(Ax));\n    checkCudaErrors(hipFree(dot_result));\n    free(val_cpu);\n\n#if ENABLE_CPU_DEBUG_CODE\n    free(Ax_cpu);\n    free(r_cpu);\n    free(p_cpu);\n    free(x_cpu);\n#endif\n\n    printf(\"Test Summary:  Error amount = %f \\n\", err);\n    fprintf(stdout, \"&&&& conjugateGradientMultiDeviceCG %s\\n\", (sqrt(r1) < tol) ? \"PASSED\" : \"FAILED\");\n    exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/conjugateGradientMultiDeviceCG/conjugateGradientMultiDeviceCG.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n///////////////////////////////////////////////////////////////////////////////\n#include <cufft.h>\n#include <math_constants.h>\n\n// Round a / b to nearest higher integer value\nint cuda_iDivUp(int a, int b) { return (a + (b - 1)) / b; }\n\n// complex math functions\n__device__ float2 conjugate(float2 arg) { return make_float2(arg.x, -arg.y); }\n\n__device__ float2 complex_exp(float arg) { return make_float2(cosf(arg), sinf(arg)); }\n\n__device__ float2 complex_add(float2 a, float2 b) { return make_float2(a.x + b.x, a.y + b.y); }\n\n__device__ float2 complex_mult(float2 ab, float2 cd)\n{\n    return make_float2(ab.x * cd.x - ab.y * cd.y, ab.x * cd.y + ab.y * cd.x);\n}\n\n// generate wave heightfield at time t based on initial heightfield and\n// dispersion relationship\n__global__ void generateSpectrumKernel(float2      *h0,\n                                       float2      *ht,\n                                       unsigned int in_width,\n                                       unsigned int out_width,\n                                       unsigned int out_height,\n                                       float        t,\n                                       float        patchSize)\n{\n    unsigned int x         = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y         = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int in_index  = y * in_width + x;\n    unsigned int in_mindex = (out_height - y) * in_width + (out_width - x); // mirrored\n    unsigned int out_index = y * out_width + x;\n\n    // calculate wave vector\n    float2 k;\n    k.x = (-(int)out_width / 2.0f + x) * (2.0f * CUDART_PI_F / patchSize);\n    k.y = (-(int)out_width / 2.0f + y) * (2.0f * CUDART_PI_F / patchSize);\n\n    // calculate dispersion w(k)\n    float k_len = sqrtf(k.x * k.x + k.y * k.y);\n    float w     = sqrtf(9.81f * k_len);\n\n    if ((x < out_width) && (y < out_height)) {\n        float2 h0_k  = h0[in_index];\n        float2 h0_mk = h0[in_mindex];\n\n        // output frequency-space complex values\n        ht[out_index] =\n            complex_add(complex_mult(h0_k, complex_exp(w * t)), complex_mult(conjugate(h0_mk), complex_exp(-w * t)));\n        // ht[out_index] = h0_k;\n    }\n}\n\n// update height map values based on output of FFT\n__global__ void updateHeightmapKernel(float *heightMap, float2 *ht, unsigned int width)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    // cos(pi * (m1 + m2))\n    float sign_correction = ((x + y) & 0x01) ? -1.0f : 1.0f;\n\n    heightMap[i] = ht[i].x * sign_correction;\n}\n\n// update height map values based on output of FFT\n__global__ void updateHeightmapKernel_y(float *heightMap, float2 *ht, unsigned int width)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    // cos(pi * (m1 + m2))\n    float sign_correction = ((x + y) & 0x01) ? -1.0f : 1.0f;\n\n    heightMap[i] = ht[i].y * sign_correction;\n}\n\n// generate slope by partial differences in spatial domain\n__global__ void calculateSlopeKernel(float *h, float2 *slopeOut, unsigned int width, unsigned int height)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    float2 slope = make_float2(0.0f, 0.0f);\n\n    if ((x > 0) && (y > 0) && (x < width - 1) && (y < height - 1)) {\n        slope.x = h[i + 1] - h[i - 1];\n        slope.y = h[i + width] - h[i - width];\n    }\n\n    slopeOut[i] = slope;\n}\n\n// wrapper functions\nextern \"C\" void cudaGenerateSpectrumKernel(float2      *d_h0,\n                                           float2      *d_ht,\n                                           unsigned int in_width,\n                                           unsigned int out_width,\n                                           unsigned int out_height,\n                                           float        animTime,\n                                           float        patchSize)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid(cuda_iDivUp(out_width, block.x), cuda_iDivUp(out_height, block.y), 1);\n    generateSpectrumKernel<<<grid, block>>>(d_h0, d_ht, in_width, out_width, out_height, animTime, patchSize);\n}\n\nextern \"C\" void\ncudaUpdateHeightmapKernel(float *d_heightMap, float2 *d_ht, unsigned int width, unsigned int height, bool autoTest)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid(cuda_iDivUp(width, block.x), cuda_iDivUp(height, block.y), 1);\n    if (autoTest) {\n        updateHeightmapKernel_y<<<grid, block>>>(d_heightMap, d_ht, width);\n    }\n    else {\n        updateHeightmapKernel<<<grid, block>>>(d_heightMap, d_ht, width);\n    }\n}\n\nextern \"C\" void cudaCalculateSlopeKernel(float *hptr, float2 *slopeOut, unsigned int width, unsigned int height)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid2(cuda_iDivUp(width, block.x), cuda_iDivUp(height, block.y), 1);\n    calculateSlopeKernel<<<grid2, block>>>(hptr, slopeOut, width, height);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n///////////////////////////////////////////////////////////////////////////////\n#include \"hip/hip_runtime.h\"\n#include <hipfft/hipfft.h>\n#include <hip/hip_math_constants.h>\n\n// Round a / b to nearest higher integer value\nint cuda_iDivUp(int a, int b) { return (a + (b - 1)) / b; }\n\n// complex math functions\n__device__ float2 conjugate(float2 arg) { return make_float2(arg.x, -arg.y); }\n\n__device__ float2 complex_exp(float arg) { return make_float2(cosf(arg), sinf(arg)); }\n\n__device__ float2 complex_add(float2 a, float2 b) { return make_float2(a.x + b.x, a.y + b.y); }\n\n__device__ float2 complex_mult(float2 ab, float2 cd)\n{\n    return make_float2(ab.x * cd.x - ab.y * cd.y, ab.x * cd.y + ab.y * cd.x);\n}\n\n// generate wave heightfield at time t based on initial heightfield and\n// dispersion relationship\n__global__ void generateSpectrumKernel(float2      *h0,\n                                       float2      *ht,\n                                       unsigned int in_width,\n                                       unsigned int out_width,\n                                       unsigned int out_height,\n                                       float        t,\n                                       float        patchSize)\n{\n    unsigned int x         = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y         = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int in_index  = y * in_width + x;\n    unsigned int in_mindex = (out_height - y) * in_width + (out_width - x); // mirrored\n    unsigned int out_index = y * out_width + x;\n\n    // calculate wave vector\n    float2 k;\n    k.x = (-(int)out_width / 2.0f + x) * (2.0f * HIP_PI_F / patchSize);\n    k.y = (-(int)out_width / 2.0f + y) * (2.0f * HIP_PI_F / patchSize);\n\n    // calculate dispersion w(k)\n    float k_len = sqrtf(k.x * k.x + k.y * k.y);\n    float w     = sqrtf(9.81f * k_len);\n\n    if ((x < out_width) && (y < out_height)) {\n        float2 h0_k  = h0[in_index];\n        float2 h0_mk = h0[in_mindex];\n\n        // output frequency-space complex values\n        ht[out_index] =\n            complex_add(complex_mult(h0_k, complex_exp(w * t)), complex_mult(conjugate(h0_mk), complex_exp(-w * t)));\n        // ht[out_index] = h0_k;\n    }\n}\n\n// update height map values based on output of FFT\n__global__ void updateHeightmapKernel(float *heightMap, float2 *ht, unsigned int width)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    // cos(pi * (m1 + m2))\n    float sign_correction = ((x + y) & 0x01) ? -1.0f : 1.0f;\n\n    heightMap[i] = ht[i].x * sign_correction;\n}\n\n// update height map values based on output of FFT\n__global__ void updateHeightmapKernel_y(float *heightMap, float2 *ht, unsigned int width)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    // cos(pi * (m1 + m2))\n    float sign_correction = ((x + y) & 0x01) ? -1.0f : 1.0f;\n\n    heightMap[i] = ht[i].y * sign_correction;\n}\n\n// generate slope by partial differences in spatial domain\n__global__ void calculateSlopeKernel(float *h, float2 *slopeOut, unsigned int width, unsigned int height)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;\n    unsigned int i = y * width + x;\n\n    float2 slope = make_float2(0.0f, 0.0f);\n\n    if ((x > 0) && (y > 0) && (x < width - 1) && (y < height - 1)) {\n        slope.x = h[i + 1] - h[i - 1];\n        slope.y = h[i + width] - h[i - width];\n    }\n\n    slopeOut[i] = slope;\n}\n\n// wrapper functions\nextern \"C\" void cudaGenerateSpectrumKernel(float2      *d_h0,\n                                           float2      *d_ht,\n                                           unsigned int in_width,\n                                           unsigned int out_width,\n                                           unsigned int out_height,\n                                           float        animTime,\n                                           float        patchSize)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid(cuda_iDivUp(out_width, block.x), cuda_iDivUp(out_height, block.y), 1);\n    generateSpectrumKernel<<<grid, block>>>(d_h0, d_ht, in_width, out_width, out_height, animTime, patchSize);\n}\n\nextern \"C\" void\ncudaUpdateHeightmapKernel(float *d_heightMap, float2 *d_ht, unsigned int width, unsigned int height, bool autoTest)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid(cuda_iDivUp(width, block.x), cuda_iDivUp(height, block.y), 1);\n    if (autoTest) {\n        updateHeightmapKernel_y<<<grid, block>>>(d_heightMap, d_ht, width);\n    }\n    else {\n        updateHeightmapKernel<<<grid, block>>>(d_heightMap, d_ht, width);\n    }\n}\n\nextern \"C\" void cudaCalculateSlopeKernel(float *hptr, float2 *slopeOut, unsigned int width, unsigned int height)\n{\n    dim3 block(8, 8, 1);\n    dim3 grid2(cuda_iDivUp(width, block.x), cuda_iDivUp(height, block.y), 1);\n    calculateSlopeKernel<<<grid2, block>>>(hptr, slopeOut, width, height);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/oceanFFT/oceanFFT_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on GPU using\n * Multi Block Cooperative Groups, also uses Unified Memory.\n *\n */\n\n// includes, system\n#include <cuda_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// Utilities and system includes\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <helper_cuda.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nnamespace cg = cooperative_groups;\n\nconst char *sSDKname = \"conjugateGradientMultiBlockCG\";\n\n#define ENABLE_CPU_DEBUG_CODE 0\n#define THREADS_PER_BLOCK     512\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = static_cast<float>(rand()) / RAND_MAX + 10.0f;\n    val[1] = static_cast<float>(rand()) / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = static_cast<float>(rand()) / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n// I - contains location of the given non-zero element in the row of the matrix\n// J - contains location of the given non-zero element in the column of the\n// matrix val - contains values of the given non-zero elements of the matrix\n// inputVecX - input vector to be multiplied\n// outputVecY - resultant vector\nvoid cpuSpMV(int *I, int *J, float *val, int nnz, int num_rows, float alpha, float *inputVecX, float *outputVecY)\n{\n    for (int i = 0; i < num_rows; i++) {\n        int num_elems_this_row = I[i + 1] - I[i];\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[I[i] + j] * inputVecX[J[I[i] + j]];\n        }\n        outputVecY[i] = output;\n    }\n\n    return;\n}\n\ndouble dotProduct(float *vecA, float *vecB, int size)\n{\n    double result = 0.0;\n\n    for (int i = 0; i < size; i++) {\n        result = result + (vecA[i] * vecB[i]);\n    }\n\n    return result;\n}\n\nvoid scaleVector(float *vec, float alpha, int size)\n{\n    for (int i = 0; i < size; i++) {\n        vec[i] = alpha * vec[i];\n    }\n}\n\nvoid saxpy(float *x, float *y, float a, int size)\n{\n    for (int i = 0; i < size; i++) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\nvoid cpuConjugateGrad(int *I, int *J, float *val, float *x, float *Ax, float *p, float *r, int nnz, int N, float tol)\n{\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, b, a, na;\n\n    cpuSpMV(I, J, val, nnz, N, alpha, x, Ax);\n    saxpy(Ax, r, alpham1, N);\n\n    float r1 = dotProduct(r, r, N);\n\n    int k = 1;\n\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            scaleVector(p, b, N);\n\n            saxpy(r, p, alpha, N);\n        }\n        else {\n            for (int i = 0; i < N; i++)\n                p[i] = r[i];\n        }\n\n        cpuSpMV(I, J, val, nnz, N, alpha, p, Ax);\n\n        float dot = dotProduct(p, Ax, N);\n        a         = r1 / dot;\n\n        saxpy(p, x, a, N);\n        na = -a;\n        saxpy(Ax, r, na, N);\n\n        r0 = r1;\n        r1 = dotProduct(r, r, N);\n\n        printf(\"\\nCPU code iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n}\n\n__device__ void gpuSpMV(int                  *I,\n                        int                  *J,\n                        float                *val,\n                        int                   nnz,\n                        int                   num_rows,\n                        float                 alpha,\n                        float                *inputVecX,\n                        float                *outputVecY,\n                        cg::thread_block     &cta,\n                        const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < num_rows; i += grid.size()) {\n        int row_elem           = I[i];\n        int next_row_elem      = I[i + 1];\n        int num_elems_this_row = next_row_elem - row_elem;\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            // I or J or val arrays - can be put in shared memory\n            // as the access is random and reused in next calls of gpuSpMV function.\n            output += alpha * val[row_elem + j] * inputVecX[J[row_elem + j]];\n        }\n\n        outputVecY[i] = output;\n    }\n}\n\n__device__ void gpuSaxpy(float *x, float *y, float a, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\n__device__ void gpuDotProduct(float                  *vecA,\n                              float                  *vecB,\n                              double                 *result,\n                              int                     size,\n                              const cg::thread_block &cta,\n                              const cg::grid_group   &grid)\n{\n    extern __shared__ double tmp[];\n\n    double temp_sum = 0.0;\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        temp_sum += static_cast<double>(vecA[i] * vecB[i]);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = temp_sum;\n    }\n\n    cg::sync(cta);\n\n    if (tile32.meta_group_rank() == 0) {\n        temp_sum = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n        temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(result, temp_sum);\n        }\n    }\n}\n\n__device__ void gpuCopyVector(float *srcA, float *destB, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        destB[i] = srcA[i];\n    }\n}\n\n__device__ void\ngpuScaleVectorAndSaxpy(const float *x, float *y, float a, float scale, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        y[i] = a * x[i] + scale * y[i];\n    }\n}\n\nextern \"C\" __global__ void gpuConjugateGradient(int    *I,\n                                                int    *J,\n                                                float  *val,\n                                                float  *x,\n                                                float  *Ax,\n                                                float  *p,\n                                                float  *r,\n                                                double *dot_result,\n                                                int     nnz,\n                                                int     N,\n                                                float   tol)\n{\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, r1, b, a, na;\n\n    gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, cta, grid);\n\n    cg::sync(grid);\n\n    gpuSaxpy(Ax, r, alpham1, N, grid);\n\n    cg::sync(grid);\n\n    gpuDotProduct(r, r, dot_result, N, cta, grid);\n\n    cg::sync(grid);\n\n    r1 = *dot_result;\n\n    int k = 1;\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            gpuScaleVectorAndSaxpy(r, p, alpha, b, N, grid);\n        }\n        else {\n            gpuCopyVector(r, p, N, grid);\n        }\n\n        cg::sync(grid);\n\n        gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, cta, grid);\n\n        if (threadIdx.x == 0 && blockIdx.x == 0)\n            *dot_result = 0.0;\n\n        cg::sync(grid);\n\n        gpuDotProduct(p, Ax, dot_result, N, cta, grid);\n\n        cg::sync(grid);\n\n        a = r1 / *dot_result;\n\n        gpuSaxpy(p, x, a, N, grid);\n        na = -a;\n        gpuSaxpy(Ax, r, na, N, grid);\n\n        r0 = r1;\n\n        cg::sync(grid);\n        if (threadIdx.x == 0 && blockIdx.x == 0)\n            *dot_result = 0.0;\n\n        cg::sync(grid);\n\n        gpuDotProduct(r, r, dot_result, N, cta, grid);\n\n        cg::sync(grid);\n\n        r1 = *dot_result;\n        k++;\n    }\n}\n\nbool areAlmostEqual(float a, float b, float maxRelDiff)\n{\n    float diff    = fabsf(a - b);\n    float abs_a   = fabsf(a);\n    float abs_b   = fabsf(b);\n    float largest = abs_a > abs_b ? abs_a : abs_b;\n\n    if (diff <= largest * maxRelDiff) {\n        return true;\n    }\n    else {\n        printf(\"maxRelDiff = %.8e\\n\", maxRelDiff);\n        printf(\"diff %.8e > largest * maxRelDiff %.8e therefore %.8e and %.8e are not \"\n               \"same\\n\",\n               diff,\n               largest * maxRelDiff,\n               a,\n               b);\n        return false;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int         N = 0, nz = 0, *I = NULL, *J = NULL;\n    float      *val = NULL;\n    const float tol = 1e-5f;\n    float      *x;\n    float      *rhs;\n    float       r1;\n    float      *r, *p, *Ax;\n    cudaEvent_t start, stop;\n\n    printf(\"Starting [%s]...\\n\", sSDKname);\n\n    // This will pick the best possible CUDA capable device\n    cudaDeviceProp deviceProp;\n    int            devID = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n\n    if (!deviceProp.managedMemory) {\n        // This sample requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // This sample requires being run on a device that supports Cooperative Kernel\n    // Launch\n    if (!deviceProp.cooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               devID);\n        exit(EXIT_WAIVED);\n    }\n\n    // Statistics about the GPU device\n    printf(\"> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\\n\\n\",\n           deviceProp.multiProcessorCount,\n           deviceProp.major,\n           deviceProp.minor);\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 1048576;\n    nz = (N - 2) * 3 + 4;\n\n    cudaMallocManaged(reinterpret_cast<void **>(&I), sizeof(int) * (N + 1));\n    cudaMallocManaged(reinterpret_cast<void **>(&J), sizeof(int) * nz);\n    cudaMallocManaged(reinterpret_cast<void **>(&val), sizeof(float) * nz);\n\n    genTridiag(I, J, val, N, nz);\n\n    cudaMallocManaged(reinterpret_cast<void **>(&x), sizeof(float) * N);\n    cudaMallocManaged(reinterpret_cast<void **>(&rhs), sizeof(float) * N);\n\n    double *dot_result;\n\n    cudaMallocManaged(reinterpret_cast<void **>(&dot_result), sizeof(double));\n\n    *dot_result = 0.0;\n\n    // temp memory for CG\n    checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&r), N * sizeof(float)));\n    checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&p), N * sizeof(float)));\n    checkCudaErrors(cudaMallocManaged(reinterpret_cast<void **>(&Ax), N * sizeof(float)));\n\n    cudaDeviceSynchronize();\n\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n\n#if ENABLE_CPU_DEBUG_CODE\n    float *Ax_cpu = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *r_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *p_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *x_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n\n    for (int i = 0; i < N; i++) {\n        r_cpu[i]  = 1.0;\n        Ax_cpu[i] = x_cpu[i] = 0.0;\n    }\n\n#endif\n\n    for (int i = 0; i < N; i++) {\n        r[i] = rhs[i] = 1.0;\n        x[i]          = 0.0;\n    }\n\n    void *kernelArgs[] = {\n        (void *)&I,\n        (void *)&J,\n        (void *)&val,\n        (void *)&x,\n        (void *)&Ax,\n        (void *)&p,\n        (void *)&r,\n        (void *)&dot_result,\n        (void *)&nz,\n        (void *)&N,\n        (void *)&tol,\n    };\n\n    int sMemSize       = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);\n    int numBlocksPerSm = 0;\n    int numThreads     = THREADS_PER_BLOCK;\n\n    checkCudaErrors(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&numBlocksPerSm, gpuConjugateGradient, numThreads, sMemSize));\n\n    int  numSms = deviceProp.multiProcessorCount;\n    dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);\n    checkCudaErrors(cudaEventRecord(start, 0));\n    checkCudaErrors(\n        cudaLaunchCooperativeKernel((void *)gpuConjugateGradient, dimGrid, dimBlock, kernelArgs, sMemSize, NULL));\n    checkCudaErrors(cudaEventRecord(stop, 0));\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    float time;\n    checkCudaErrors(cudaEventElapsedTime(&time, start, stop));\n\n    r1 = *dot_result;\n\n    printf(\"GPU Final, residual = %e, kernel execution time = %f ms\\n\", sqrt(r1), time);\n\n#if ENABLE_CPU_DEBUG_CODE\n    cpuConjugateGrad(I, J, val, x_cpu, Ax_cpu, p_cpu, r_cpu, nz, N, tol);\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs[i]);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n    checkCudaErrors(cudaFree(I));\n    checkCudaErrors(cudaFree(J));\n    checkCudaErrors(cudaFree(val));\n    checkCudaErrors(cudaFree(x));\n    checkCudaErrors(cudaFree(rhs));\n    checkCudaErrors(cudaFree(r));\n    checkCudaErrors(cudaFree(p));\n    checkCudaErrors(cudaFree(Ax));\n    checkCudaErrors(cudaFree(dot_result));\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n\n#if ENABLE_CPU_DEBUG_CODE\n    free(Ax_cpu);\n    free(r_cpu);\n    free(p_cpu);\n    free(x_cpu);\n#endif\n\n    printf(\"Test Summary:  Error amount = %f \\n\", err);\n    fprintf(stdout, \"&&&& conjugateGradientMultiBlockCG %s\\n\", (sqrt(r1) < tol) ? \"PASSED\" : \"FAILED\");\n    exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on GPU using\n * Multi Block Cooperative Groups, also uses Unified Memory.\n *\n */\n\n// includes, system\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// Utilities and system includes\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <hip/hip_runtime_api.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nnamespace cg = cooperative_groups;\n\nconst char *sSDKname = \"conjugateGradientMultiBlockCG\";\n\n#define ENABLE_CPU_DEBUG_CODE 0\n#define THREADS_PER_BLOCK     512\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = static_cast<float>(rand()) / RAND_MAX + 10.0f;\n    val[1] = static_cast<float>(rand()) / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = static_cast<float>(rand()) / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n// I - contains location of the given non-zero element in the row of the matrix\n// J - contains location of the given non-zero element in the column of the\n// matrix val - contains values of the given non-zero elements of the matrix\n// inputVecX - input vector to be multiplied\n// outputVecY - resultant vector\nvoid cpuSpMV(int *I, int *J, float *val, int nnz, int num_rows, float alpha, float *inputVecX, float *outputVecY)\n{\n    for (int i = 0; i < num_rows; i++) {\n        int num_elems_this_row = I[i + 1] - I[i];\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            output += alpha * val[I[i] + j] * inputVecX[J[I[i] + j]];\n        }\n        outputVecY[i] = output;\n    }\n\n    return;\n}\n\ndouble dotProduct(float *vecA, float *vecB, int size)\n{\n    double result = 0.0;\n\n    for (int i = 0; i < size; i++) {\n        result = result + (vecA[i] * vecB[i]);\n    }\n\n    return result;\n}\n\nvoid scaleVector(float *vec, float alpha, int size)\n{\n    for (int i = 0; i < size; i++) {\n        vec[i] = alpha * vec[i];\n    }\n}\n\nvoid saxpy(float *x, float *y, float a, int size)\n{\n    for (int i = 0; i < size; i++) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\nvoid cpuConjugateGrad(int *I, int *J, float *val, float *x, float *Ax, float *p, float *r, int nnz, int N, float tol)\n{\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, b, a, na;\n\n    cpuSpMV(I, J, val, nnz, N, alpha, x, Ax);\n    saxpy(Ax, r, alpham1, N);\n\n    float r1 = dotProduct(r, r, N);\n\n    int k = 1;\n\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            scaleVector(p, b, N);\n\n            saxpy(r, p, alpha, N);\n        }\n        else {\n            for (int i = 0; i < N; i++)\n                p[i] = r[i];\n        }\n\n        cpuSpMV(I, J, val, nnz, N, alpha, p, Ax);\n\n        float dot = dotProduct(p, Ax, N);\n        a         = r1 / dot;\n\n        saxpy(p, x, a, N);\n        na = -a;\n        saxpy(Ax, r, na, N);\n\n        r0 = r1;\n        r1 = dotProduct(r, r, N);\n\n        printf(\"\\nCPU code iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n}\n\n__device__ void gpuSpMV(int                  *I,\n                        int                  *J,\n                        float                *val,\n                        int                   nnz,\n                        int                   num_rows,\n                        float                 alpha,\n                        float                *inputVecX,\n                        float                *outputVecY,\n                        cg::thread_block     &cta,\n                        const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < num_rows; i += grid.size()) {\n        int row_elem           = I[i];\n        int next_row_elem      = I[i + 1];\n        int num_elems_this_row = next_row_elem - row_elem;\n\n        float output = 0.0;\n        for (int j = 0; j < num_elems_this_row; j++) {\n            // I or J or val arrays - can be put in shared memory\n            // as the access is random and reused in next calls of gpuSpMV function.\n            output += alpha * val[row_elem + j] * inputVecX[J[row_elem + j]];\n        }\n\n        outputVecY[i] = output;\n    }\n}\n\n__device__ void gpuSaxpy(float *x, float *y, float a, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        y[i] = a * x[i] + y[i];\n    }\n}\n\n__device__ void gpuDotProduct(float                  *vecA,\n                              float                  *vecB,\n                              double                 *result,\n                              int                     size,\n                              const cg::thread_block &cta,\n                              const cg::grid_group   &grid)\n{\n    extern __shared__ double tmp[];\n\n    double temp_sum = 0.0;\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        temp_sum += static_cast<double>(vecA[i] * vecB[i]);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n    if (tile32.thread_rank() == 0) {\n        tmp[tile32.meta_group_rank()] = temp_sum;\n    }\n\n    cg::sync(cta);\n\n    if (tile32.meta_group_rank() == 0) {\n        temp_sum = tile32.thread_rank() < tile32.meta_group_size() ? tmp[tile32.thread_rank()] : 0.0;\n        temp_sum = cg::reduce(tile32, temp_sum, cg::plus<double>());\n\n        if (tile32.thread_rank() == 0) {\n            atomicAdd(result, temp_sum);\n        }\n    }\n}\n\n__device__ void gpuCopyVector(float *srcA, float *destB, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        destB[i] = srcA[i];\n    }\n}\n\n__device__ void\ngpuScaleVectorAndSaxpy(const float *x, float *y, float a, float scale, int size, const cg::grid_group &grid)\n{\n    for (int i = grid.thread_rank(); i < size; i += grid.size()) {\n        y[i] = a * x[i] + scale * y[i];\n    }\n}\n\nextern \"C\" __global__ void gpuConjugateGradient(int    *I,\n                                                int    *J,\n                                                float  *val,\n                                                float  *x,\n                                                float  *Ax,\n                                                float  *p,\n                                                float  *r,\n                                                double *dot_result,\n                                                int     nnz,\n                                                int     N,\n                                                float   tol)\n{\n    cg::thread_block cta  = cg::this_thread_block();\n    cg::grid_group   grid = cg::this_grid();\n\n    int max_iter = 10000;\n\n    float alpha   = 1.0;\n    float alpham1 = -1.0;\n    float r0      = 0.0, r1, b, a, na;\n\n    gpuSpMV(I, J, val, nnz, N, alpha, x, Ax, cta, grid);\n\n    cg::sync(grid);\n\n    gpuSaxpy(Ax, r, alpham1, N, grid);\n\n    cg::sync(grid);\n\n    gpuDotProduct(r, r, dot_result, N, cta, grid);\n\n    cg::sync(grid);\n\n    r1 = *dot_result;\n\n    int k = 1;\n    while (r1 > tol * tol && k <= max_iter) {\n        if (k > 1) {\n            b = r1 / r0;\n            gpuScaleVectorAndSaxpy(r, p, alpha, b, N, grid);\n        }\n        else {\n            gpuCopyVector(r, p, N, grid);\n        }\n\n        cg::sync(grid);\n\n        gpuSpMV(I, J, val, nnz, N, alpha, p, Ax, cta, grid);\n\n        if (threadIdx.x == 0 && blockIdx.x == 0)\n            *dot_result = 0.0;\n\n        cg::sync(grid);\n\n        gpuDotProduct(p, Ax, dot_result, N, cta, grid);\n\n        cg::sync(grid);\n\n        a = r1 / *dot_result;\n\n        gpuSaxpy(p, x, a, N, grid);\n        na = -a;\n        gpuSaxpy(Ax, r, na, N, grid);\n\n        r0 = r1;\n\n        cg::sync(grid);\n        if (threadIdx.x == 0 && blockIdx.x == 0)\n            *dot_result = 0.0;\n\n        cg::sync(grid);\n\n        gpuDotProduct(r, r, dot_result, N, cta, grid);\n\n        cg::sync(grid);\n\n        r1 = *dot_result;\n        k++;\n    }\n}\n\nbool areAlmostEqual(float a, float b, float maxRelDiff)\n{\n    float diff    = fabsf(a - b);\n    float abs_a   = fabsf(a);\n    float abs_b   = fabsf(b);\n    float largest = abs_a > abs_b ? abs_a : abs_b;\n\n    if (diff <= largest * maxRelDiff) {\n        return true;\n    }\n    else {\n        printf(\"maxRelDiff = %.8e\\n\", maxRelDiff);\n        printf(\"diff %.8e > largest * maxRelDiff %.8e therefore %.8e and %.8e are not \"\n               \"same\\n\",\n               diff,\n               largest * maxRelDiff,\n               a,\n               b);\n        return false;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int         N = 0, nz = 0, *I = NULL, *J = NULL;\n    float      *val = NULL;\n    const float tol = 1e-5f;\n    float      *x;\n    float      *rhs;\n    float       r1;\n    float      *r, *p, *Ax;\n    hipEvent_t start, stop;\n\n    printf(\"Starting [%s]...\\n\", sSDKname);\n\n    // This will pick the best possible CUDA capable device\n    hipDeviceProp_t deviceProp;\n    int            devID = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n\n    if (!deviceProp.managedMemory) {\n        // This sample requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // This sample requires being run on a device that supports Cooperative Kernel\n    // Launch\n    if (!deviceProp.cooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               devID);\n        exit(EXIT_WAIVED);\n    }\n\n    // Statistics about the GPU device\n    printf(\"> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\\n\\n\",\n           deviceProp.multiProcessorCount,\n           deviceProp.major,\n           deviceProp.minor);\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 1048576;\n    nz = (N - 2) * 3 + 4;\n\n    hipMallocManaged(reinterpret_cast<void **>(&I), sizeof(int) * (N + 1));\n    hipMallocManaged(reinterpret_cast<void **>(&J), sizeof(int) * nz);\n    hipMallocManaged(reinterpret_cast<void **>(&val), sizeof(float) * nz);\n\n    genTridiag(I, J, val, N, nz);\n\n    hipMallocManaged(reinterpret_cast<void **>(&x), sizeof(float) * N);\n    hipMallocManaged(reinterpret_cast<void **>(&rhs), sizeof(float) * N);\n\n    double *dot_result;\n\n    hipMallocManaged(reinterpret_cast<void **>(&dot_result), sizeof(double));\n\n    *dot_result = 0.0;\n\n    // temp memory for CG\n    checkCudaErrors(hipMallocManaged(reinterpret_cast<void **>(&r), N * sizeof(float)));\n    checkCudaErrors(hipMallocManaged(reinterpret_cast<void **>(&p), N * sizeof(float)));\n    checkCudaErrors(hipMallocManaged(reinterpret_cast<void **>(&Ax), N * sizeof(float)));\n\n    hipDeviceSynchronize();\n\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n\n#if ENABLE_CPU_DEBUG_CODE\n    float *Ax_cpu = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *r_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *p_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n    float *x_cpu  = reinterpret_cast<float *>(malloc(sizeof(float) * N));\n\n    for (int i = 0; i < N; i++) {\n        r_cpu[i]  = 1.0;\n        Ax_cpu[i] = x_cpu[i] = 0.0;\n    }\n\n#endif\n\n    for (int i = 0; i < N; i++) {\n        r[i] = rhs[i] = 1.0;\n        x[i]          = 0.0;\n    }\n\n    void *kernelArgs[] = {\n        (void *)&I,\n        (void *)&J,\n        (void *)&val,\n        (void *)&x,\n        (void *)&Ax,\n        (void *)&p,\n        (void *)&r,\n        (void *)&dot_result,\n        (void *)&nz,\n        (void *)&N,\n        (void *)&tol,\n    };\n\n    int sMemSize       = sizeof(double) * ((THREADS_PER_BLOCK / 32) + 1);\n    int numBlocksPerSm = 0;\n    int numThreads     = THREADS_PER_BLOCK;\n\n    checkCudaErrors(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&numBlocksPerSm, gpuConjugateGradient, numThreads, sMemSize));\n\n    int  numSms = deviceProp.multiProcessorCount;\n    dim3 dimGrid(numSms * numBlocksPerSm, 1, 1), dimBlock(THREADS_PER_BLOCK, 1, 1);\n    checkCudaErrors(hipEventRecord(start, 0));\n    checkCudaErrors(\n        hipLaunchCooperativeKernel(reinterpret_cast<const void*>((void *)gpuConjugateGradient), dimGrid, dimBlock, kernelArgs, sMemSize, NULL));\n    checkCudaErrors(hipEventRecord(stop, 0));\n    checkCudaErrors(hipDeviceSynchronize());\n\n    float time;\n    checkCudaErrors(hipEventElapsedTime(&time, start, stop));\n\n    r1 = *dot_result;\n\n    printf(\"GPU Final, residual = %e, kernel execution time = %f ms\\n\", sqrt(r1), time);\n\n#if ENABLE_CPU_DEBUG_CODE\n    cpuConjugateGrad(I, J, val, x_cpu, Ax_cpu, p_cpu, r_cpu, nz, N, tol);\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs[i]);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n    checkCudaErrors(hipFree(I));\n    checkCudaErrors(hipFree(J));\n    checkCudaErrors(hipFree(val));\n    checkCudaErrors(hipFree(x));\n    checkCudaErrors(hipFree(rhs));\n    checkCudaErrors(hipFree(r));\n    checkCudaErrors(hipFree(p));\n    checkCudaErrors(hipFree(Ax));\n    checkCudaErrors(hipFree(dot_result));\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n\n#if ENABLE_CPU_DEBUG_CODE\n    free(Ax_cpu);\n    free(r_cpu);\n    free(p_cpu);\n    free(x_cpu);\n#endif\n\n    printf(\"Test Summary:  Error amount = %f \\n\", err);\n    fprintf(stdout, \"&&&& conjugateGradientMultiBlockCG %s\\n\", (sqrt(r1) < tol) ? \"PASSED\" : \"FAILED\");\n    exit((sqrt(r1) < tol) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/conjugateGradientMultiBlockCG/conjugateGradientMultiBlockCG.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on GPU\n * using CUBLAS and CUSPARSE with CUDA Graphs\n *\n */\n\n// includes, system\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Using updated (v2) interfaces to cublas */\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n#include <cusparse.h>\n\n// Utilities and system includes\n#include <helper_cuda.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nconst char *sSDKname = \"conjugateGradientCudaGraphs\";\n\n#ifndef WITH_GRAPH\n#define WITH_GRAPH 1\n#endif\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = (float)rand() / RAND_MAX + 10.0f;\n    val[1] = (float)rand() / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = (float)rand() / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = (float)rand() / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n__global__ void initVectors(float *rhs, float *x, int N)\n{\n    size_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (size_t i = gid; i < N; i += gridDim.x * blockDim.x) {\n        rhs[i] = 1.0;\n        x[i]   = 0.0;\n    }\n}\n\n__global__ void r1_div_x(float *r1, float *r0, float *b)\n{\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (gid == 0) {\n        b[0] = r1[0] / r0[0];\n    }\n}\n\n__global__ void a_minus(float *a, float *na)\n{\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (gid == 0) {\n        na[0] = -(a[0]);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int         N = 0, nz = 0, *I = NULL, *J = NULL;\n    float      *val      = NULL;\n    const float tol      = 1e-5f;\n    const int   max_iter = 10000;\n    float      *x;\n    float      *rhs;\n    float       r1;\n\n    int   *d_col, *d_row;\n    float *d_val, *d_x;\n    float *d_r, *d_p, *d_Ax;\n    int    k;\n    float  alpha, beta, alpham1;\n\n    cudaStream_t stream1, streamForGraph;\n\n    // This will pick the best possible CUDA capable device\n    cudaDeviceProp deviceProp;\n    int            devID = findCudaDevice(argc, (const char **)argv);\n\n    if (devID < 0) {\n        printf(\"exiting...\\n\");\n        exit(EXIT_SUCCESS);\n    }\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n\n    // Statistics about the GPU device\n    printf(\"> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\\n\\n\",\n           deviceProp.multiProcessorCount,\n           deviceProp.major,\n           deviceProp.minor);\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 1048576;\n    nz = (N - 2) * 3 + 4;\n    checkCudaErrors(cudaMallocHost(&I, sizeof(int) * (N + 1)));\n    checkCudaErrors(cudaMallocHost(&J, sizeof(int) * nz));\n    checkCudaErrors(cudaMallocHost(&val, sizeof(float) * nz));\n    genTridiag(I, J, val, N, nz);\n\n    checkCudaErrors(cudaMallocHost(&x, sizeof(float) * N));\n    rhs = (float *)malloc(sizeof(float) * N);\n\n    for (int i = 0; i < N; i++) {\n        rhs[i] = 1.0;\n        x[i]   = 0.0;\n    }\n\n    /* Get handle to the CUBLAS context */\n    cublasHandle_t cublasHandle = 0;\n    cublasStatus_t cublasStatus;\n    cublasStatus = cublasCreate(&cublasHandle);\n\n    checkCudaErrors(cublasStatus);\n\n    /* Get handle to the CUSPARSE context */\n    cusparseHandle_t cusparseHandle = 0;\n    cusparseStatus_t cusparseStatus;\n    cusparseStatus = cusparseCreate(&cusparseHandle);\n\n    checkCudaErrors(cusparseStatus);\n\n    checkCudaErrors(cudaStreamCreate(&stream1));\n\n    checkCudaErrors(cudaMalloc((void **)&d_col, nz * sizeof(int)));\n    checkCudaErrors(cudaMalloc((void **)&d_row, (N + 1) * sizeof(int)));\n    checkCudaErrors(cudaMalloc((void **)&d_val, nz * sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_x, N * sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_r, N * sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_p, N * sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_Ax, N * sizeof(float)));\n\n    float *d_r1, *d_r0, *d_dot, *d_a, *d_na, *d_b;\n    checkCudaErrors(cudaMalloc((void **)&d_r1, sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_r0, sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_dot, sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_a, sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_na, sizeof(float)));\n    checkCudaErrors(cudaMalloc((void **)&d_b, sizeof(float)));\n\n    /* Wrap raw data into cuSPARSE generic API objects */\n    cusparseSpMatDescr_t matA = NULL;\n    checkCudaErrors(cusparseCreateCsr(&matA,\n                                      N,\n                                      N,\n                                      nz,\n                                      d_row,\n                                      d_col,\n                                      d_val,\n                                      CUSPARSE_INDEX_32I,\n                                      CUSPARSE_INDEX_32I,\n                                      CUSPARSE_INDEX_BASE_ZERO,\n                                      CUDA_R_32F));\n    cusparseDnVecDescr_t vecx = NULL;\n    checkCudaErrors(cusparseCreateDnVec(&vecx, N, d_x, CUDA_R_32F));\n    cusparseDnVecDescr_t vecp = NULL;\n    checkCudaErrors(cusparseCreateDnVec(&vecp, N, d_p, CUDA_R_32F));\n    cusparseDnVecDescr_t vecAx = NULL;\n    checkCudaErrors(cusparseCreateDnVec(&vecAx, N, d_Ax, CUDA_R_32F));\n\n    /* Allocate workspace for cuSPARSE */\n    size_t bufferSize = 0;\n    checkCudaErrors(cusparseSpMV_bufferSize(cusparseHandle,\n                                            CUSPARSE_OPERATION_NON_TRANSPOSE,\n                                            &alpha,\n                                            matA,\n                                            vecx,\n                                            &beta,\n                                            vecAx,\n                                            CUDA_R_32F,\n                                            CUSPARSE_SPMV_ALG_DEFAULT,\n                                            &bufferSize));\n    void *buffer = NULL;\n    checkCudaErrors(cudaMalloc(&buffer, bufferSize));\n\n    cusparseMatDescr_t descr = 0;\n    checkCudaErrors(cusparseCreateMatDescr(&descr));\n\n    checkCudaErrors(cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL));\n    checkCudaErrors(cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO));\n\n    int numBlocks = 0, blockSize = 0;\n    checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&numBlocks, &blockSize, initVectors));\n\n    checkCudaErrors(cudaMemcpyAsync(d_col, J, nz * sizeof(int), cudaMemcpyHostToDevice, stream1));\n    checkCudaErrors(cudaMemcpyAsync(d_row, I, (N + 1) * sizeof(int), cudaMemcpyHostToDevice, stream1));\n    checkCudaErrors(cudaMemcpyAsync(d_val, val, nz * sizeof(float), cudaMemcpyHostToDevice, stream1));\n\n    initVectors<<<numBlocks, blockSize, 0, stream1>>>(d_r, d_x, N);\n\n    alpha   = 1.0;\n    alpham1 = -1.0;\n    beta    = 0.0;\n\n    checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));\n    checkCudaErrors(cusparseSpMV(cusparseHandle,\n                                 CUSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecx,\n                                 &beta,\n                                 vecAx,\n                                 CUDA_R_32F,\n                                 CUSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(cublasSetStream(cublasHandle, stream1));\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpham1, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE));\n    checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    k = 1;\n    // First Iteration when k=1 starts\n    checkCudaErrors(cublasScopy(cublasHandle, N, d_r, 1, d_p, 1));\n    checkCudaErrors(cusparseSpMV(cusparseHandle,\n                                 CUSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecp,\n                                 &beta,\n                                 vecAx,\n                                 CUDA_R_32F,\n                                 CUSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n    a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));\n\n    checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    checkCudaErrors(cudaMemcpyAsync(&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));\n    checkCudaErrors(cudaStreamSynchronize(stream1));\n    printf(\"iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n    // First Iteration when k=1 ends\n    k++;\n\n#if WITH_GRAPH\n    cudaGraph_t initGraph;\n    checkCudaErrors(cudaStreamCreate(&streamForGraph));\n    checkCudaErrors(cublasSetStream(cublasHandle, stream1));\n    checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));\n    checkCudaErrors(cudaStreamBeginCapture(stream1, cudaStreamCaptureModeGlobal));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);\n    cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);\n    checkCudaErrors(cublasSscal(cublasHandle, N, d_b, d_p, 1));\n    cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_HOST);\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));\n    cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);\n\n    checkCudaErrors(cusparseSetPointerMode(cusparseHandle, CUSPARSE_POINTER_MODE_HOST));\n    checkCudaErrors(cusparseSpMV(cusparseHandle,\n                                 CUSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecp,\n                                 &beta,\n                                 vecAx,\n                                 CUDA_R_32F,\n                                 CUSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(cudaMemsetAsync(d_dot, 0, sizeof(float), stream1));\n    checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n    a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n\n    checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));\n    checkCudaErrors(cudaMemsetAsync(d_r1, 0, sizeof(float), stream1));\n\n    checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    checkCudaErrors(cudaMemcpyAsync((float *)&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));\n\n    checkCudaErrors(cudaStreamEndCapture(stream1, &initGraph));\n    cudaGraphExec_t graphExec;\n    checkCudaErrors(cudaGraphInstantiate(&graphExec, initGraph, NULL, NULL, 0));\n#endif\n\n    checkCudaErrors(cublasSetStream(cublasHandle, stream1));\n    checkCudaErrors(cusparseSetStream(cusparseHandle, stream1));\n\n    while (r1 > tol * tol && k <= max_iter) {\n#if WITH_GRAPH\n        checkCudaErrors(cudaGraphLaunch(graphExec, streamForGraph));\n        checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n#else\n        r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);\n        cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);\n        checkCudaErrors(cublasSscal(cublasHandle, N, d_b, d_p, 1));\n\n        cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_HOST);\n        checkCudaErrors(cublasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));\n\n        checkCudaErrors(cusparseSpMV(cusparseHandle,\n                                     CUSPARSE_OPERATION_NON_TRANSPOSE,\n                                     &alpha,\n                                     matA,\n                                     vecp,\n                                     &beta,\n                                     vecAx,\n                                     CUDA_R_32F,\n                                     CUSPARSE_SPMV_ALG_DEFAULT,\n                                     buffer));\n\n        cublasSetPointerMode(cublasHandle, CUBLAS_POINTER_MODE_DEVICE);\n        checkCudaErrors(cublasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n        r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n        checkCudaErrors(cublasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n        a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n        checkCudaErrors(cublasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n        checkCudaErrors(cudaMemcpyAsync(d_r0, d_r1, sizeof(float), cudaMemcpyDeviceToDevice, stream1));\n\n        checkCudaErrors(cublasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n        checkCudaErrors(cudaMemcpyAsync((float *)&r1, d_r1, sizeof(float), cudaMemcpyDeviceToHost, stream1));\n        checkCudaErrors(cudaStreamSynchronize(stream1));\n#endif\n        printf(\"iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n\n#if WITH_GRAPH\n    checkCudaErrors(cudaMemcpyAsync(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost, streamForGraph));\n    checkCudaErrors(cudaStreamSynchronize(streamForGraph));\n#else\n    checkCudaErrors(cudaMemcpyAsync(x, d_x, N * sizeof(float), cudaMemcpyDeviceToHost, stream1));\n    checkCudaErrors(cudaStreamSynchronize(stream1));\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs[i]);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n#if WITH_GRAPH\n    checkCudaErrors(cudaGraphExecDestroy(graphExec));\n    checkCudaErrors(cudaGraphDestroy(initGraph));\n    checkCudaErrors(cudaStreamDestroy(streamForGraph));\n#endif\n    checkCudaErrors(cudaStreamDestroy(stream1));\n    cusparseDestroy(cusparseHandle);\n    cublasDestroy(cublasHandle);\n\n    if (matA) {\n        checkCudaErrors(cusparseDestroySpMat(matA));\n    }\n    if (vecx) {\n        checkCudaErrors(cusparseDestroyDnVec(vecx));\n    }\n    if (vecAx) {\n        checkCudaErrors(cusparseDestroyDnVec(vecAx));\n    }\n    if (vecp) {\n        checkCudaErrors(cusparseDestroyDnVec(vecp));\n    }\n\n    checkCudaErrors(cudaFreeHost(I));\n    checkCudaErrors(cudaFreeHost(J));\n    checkCudaErrors(cudaFreeHost(val));\n    checkCudaErrors(cudaFreeHost(x));\n    free(rhs);\n    checkCudaErrors(cudaFree(d_col));\n    checkCudaErrors(cudaFree(d_row));\n    checkCudaErrors(cudaFree(d_val));\n    checkCudaErrors(cudaFree(d_x));\n    checkCudaErrors(cudaFree(d_r));\n    checkCudaErrors(cudaFree(d_p));\n    checkCudaErrors(cudaFree(d_Ax));\n\n    printf(\"Test Summary:  Error amount = %f\\n\", err);\n    exit((k <= max_iter) ? 0 : 1);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample implements a conjugate gradient solver on GPU\n * using CUBLAS and CUSPARSE with CUDA Graphs\n *\n */\n\n// includes, system\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Using updated (v2) interfaces to cublas */\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include <hipsparse.h>\n\n// Utilities and system includes\n#include <hip/hip_runtime_api.h>      // helper function CUDA error checking and initialization\n#include <helper_functions.h> // helper for shared functions common to CUDA Samples\n\nconst char *sSDKname = \"conjugateGradientCudaGraphs\";\n\n#ifndef WITH_GRAPH\n#define WITH_GRAPH 1\n#endif\n\n/* genTridiag: generate a random tridiagonal symmetric matrix */\nvoid genTridiag(int *I, int *J, float *val, int N, int nz)\n{\n    I[0] = 0, J[0] = 0, J[1] = 1;\n    val[0] = (float)rand() / RAND_MAX + 10.0f;\n    val[1] = (float)rand() / RAND_MAX;\n    int start;\n\n    for (int i = 1; i < N; i++) {\n        if (i > 1) {\n            I[i] = I[i - 1] + 3;\n        }\n        else {\n            I[1] = 2;\n        }\n\n        start        = (i - 1) * 3 + 2;\n        J[start]     = i - 1;\n        J[start + 1] = i;\n\n        if (i < N - 1) {\n            J[start + 2] = i + 1;\n        }\n\n        val[start]     = val[start - 1];\n        val[start + 1] = (float)rand() / RAND_MAX + 10.0f;\n\n        if (i < N - 1) {\n            val[start + 2] = (float)rand() / RAND_MAX;\n        }\n    }\n\n    I[N] = nz;\n}\n\n__global__ void initVectors(float *rhs, float *x, int N)\n{\n    size_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (size_t i = gid; i < N; i += gridDim.x * blockDim.x) {\n        rhs[i] = 1.0;\n        x[i]   = 0.0;\n    }\n}\n\n__global__ void r1_div_x(float *r1, float *r0, float *b)\n{\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (gid == 0) {\n        b[0] = r1[0] / r0[0];\n    }\n}\n\n__global__ void a_minus(float *a, float *na)\n{\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (gid == 0) {\n        na[0] = -(a[0]);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int         N = 0, nz = 0, *I = NULL, *J = NULL;\n    float      *val      = NULL;\n    const float tol      = 1e-5f;\n    const int   max_iter = 10000;\n    float      *x;\n    float      *rhs;\n    float       r1;\n\n    int   *d_col, *d_row;\n    float *d_val, *d_x;\n    float *d_r, *d_p, *d_Ax;\n    int    k;\n    float  alpha, beta, alpham1;\n\n    hipStream_t stream1, streamForGraph;\n\n    // This will pick the best possible CUDA capable device\n    hipDeviceProp_t deviceProp;\n    int            devID = findCudaDevice(argc, (const char **)argv);\n\n    if (devID < 0) {\n        printf(\"exiting...\\n\");\n        exit(EXIT_SUCCESS);\n    }\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n\n    // Statistics about the GPU device\n    printf(\"> GPU device has %d Multi-Processors, SM %d.%d compute capabilities\\n\\n\",\n           deviceProp.multiProcessorCount,\n           deviceProp.major,\n           deviceProp.minor);\n\n    /* Generate a random tridiagonal symmetric matrix in CSR format */\n    N  = 1048576;\n    nz = (N - 2) * 3 + 4;\n    checkCudaErrors(hipHostMalloc(&I, sizeof(int) * (N + 1)));\n    checkCudaErrors(hipHostMalloc(&J, sizeof(int) * nz));\n    checkCudaErrors(hipHostMalloc(&val, sizeof(float) * nz));\n    genTridiag(I, J, val, N, nz);\n\n    checkCudaErrors(hipHostMalloc(&x, sizeof(float) * N));\n    rhs = (float *)malloc(sizeof(float) * N);\n\n    for (int i = 0; i < N; i++) {\n        rhs[i] = 1.0;\n        x[i]   = 0.0;\n    }\n\n    /* Get handle to the CUBLAS context */\n    hipblasHandle_t cublasHandle = 0;\n    hipblasStatus_t hipblasStatus_t;\n    hipblasStatus_t = hipblasCreate(&cublasHandle);\n\n    checkCudaErrors(hipblasStatus_t);\n\n    /* Get handle to the CUSPARSE context */\n    hipsparseHandle_t cusparseHandle = 0;\n    hipsparseStatus_t cusparseStatus;\n    cusparseStatus = hipsparseCreate(&cusparseHandle);\n\n    checkCudaErrors(cusparseStatus);\n\n    checkCudaErrors(hipStreamCreate(&stream1));\n\n    checkCudaErrors(hipMalloc((void **)&d_col, nz * sizeof(int)));\n    checkCudaErrors(hipMalloc((void **)&d_row, (N + 1) * sizeof(int)));\n    checkCudaErrors(hipMalloc((void **)&d_val, nz * sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_x, N * sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_r, N * sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_p, N * sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_Ax, N * sizeof(float)));\n\n    float *d_r1, *d_r0, *d_dot, *d_a, *d_na, *d_b;\n    checkCudaErrors(hipMalloc((void **)&d_r1, sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_r0, sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_dot, sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_a, sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_na, sizeof(float)));\n    checkCudaErrors(hipMalloc((void **)&d_b, sizeof(float)));\n\n    /* Wrap raw data into cuSPARSE generic API objects */\n    hipsparseSpMatDescr_t matA = NULL;\n    checkCudaErrors(hipsparseCreateCsr(&matA,\n                                      N,\n                                      N,\n                                      nz,\n                                      d_row,\n                                      d_col,\n                                      d_val,\n                                      HIPSPARSE_INDEX_32I,\n                                      HIPSPARSE_INDEX_32I,\n                                      HIPSPARSE_INDEX_BASE_ZERO,\n                                      HIP_R_32F));\n    hipsparseDnVecDescr_t vecx = NULL;\n    checkCudaErrors(hipsparseCreateDnVec(&vecx, N, d_x, HIP_R_32F));\n    hipsparseDnVecDescr_t vecp = NULL;\n    checkCudaErrors(hipsparseCreateDnVec(&vecp, N, d_p, HIP_R_32F));\n    hipsparseDnVecDescr_t vecAx = NULL;\n    checkCudaErrors(hipsparseCreateDnVec(&vecAx, N, d_Ax, HIP_R_32F));\n\n    /* Allocate workspace for cuSPARSE */\n    size_t bufferSize = 0;\n    checkCudaErrors(hipsparseSpMV_bufferSize(cusparseHandle,\n                                            HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                                            &alpha,\n                                            matA,\n                                            vecx,\n                                            &beta,\n                                            vecAx,\n                                            HIP_R_32F,\n                                            HIPSPARSE_SPMV_ALG_DEFAULT,\n                                            &bufferSize));\n    void *buffer = NULL;\n    checkCudaErrors(hipMalloc(&buffer, bufferSize));\n\n    hipsparseMatDescr_t descr = 0;\n    checkCudaErrors(hipsparseCreateMatDescr(&descr));\n\n    checkCudaErrors(hipsparseSetMatType(descr, HIPSPARSE_MATRIX_TYPE_GENERAL));\n    checkCudaErrors(hipsparseSetMatIndexBase(descr, HIPSPARSE_INDEX_BASE_ZERO));\n\n    int numBlocks = 0, blockSize = 0;\n    checkCudaErrors(hipOccupancyMaxPotentialBlockSize(&numBlocks, &blockSize, initVectors));\n\n    checkCudaErrors(hipMemcpyAsync(d_col, J, nz * sizeof(int), hipMemcpyHostToDevice, stream1));\n    checkCudaErrors(hipMemcpyAsync(d_row, I, (N + 1) * sizeof(int), hipMemcpyHostToDevice, stream1));\n    checkCudaErrors(hipMemcpyAsync(d_val, val, nz * sizeof(float), hipMemcpyHostToDevice, stream1));\n\n    initVectors<<<numBlocks, blockSize, 0, stream1>>>(d_r, d_x, N);\n\n    alpha   = 1.0;\n    alpham1 = -1.0;\n    beta    = 0.0;\n\n    checkCudaErrors(hipsparseSetStream(cusparseHandle, stream1));\n    checkCudaErrors(hipsparseSpMV(cusparseHandle,\n                                 HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecx,\n                                 &beta,\n                                 vecAx,\n                                 HIP_R_32F,\n                                 HIPSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(hipblasSetStream(cublasHandle, stream1));\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, &alpham1, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_DEVICE));\n    checkCudaErrors(hipblasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    k = 1;\n    // First Iteration when k=1 starts\n    checkCudaErrors(hipblasScopy(cublasHandle, N, d_r, 1, d_p, 1));\n    checkCudaErrors(hipsparseSpMV(cusparseHandle,\n                                 HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecp,\n                                 &beta,\n                                 vecAx,\n                                 HIP_R_32F,\n                                 HIPSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(hipblasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n    a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(hipMemcpyAsync(d_r0, d_r1, sizeof(float), hipMemcpyDeviceToDevice, stream1));\n\n    checkCudaErrors(hipblasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    checkCudaErrors(hipMemcpyAsync(&r1, d_r1, sizeof(float), hipMemcpyDeviceToHost, stream1));\n    checkCudaErrors(hipStreamSynchronize(stream1));\n    printf(\"iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n    // First Iteration when k=1 ends\n    k++;\n\n#if WITH_GRAPH\n    hipGraph_t initGraph;\n    checkCudaErrors(hipStreamCreate(&streamForGraph));\n    checkCudaErrors(hipblasSetStream(cublasHandle, stream1));\n    checkCudaErrors(hipsparseSetStream(cusparseHandle, stream1));\n    checkCudaErrors(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);\n    hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_DEVICE);\n    checkCudaErrors(hipblasSscal(cublasHandle, N, d_b, d_p, 1));\n    hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_HOST);\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));\n    hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_DEVICE);\n\n    checkCudaErrors(hipsparseSetPointerMode(cusparseHandle, HIPSPARSE_POINTER_MODE_HOST));\n    checkCudaErrors(hipsparseSpMV(cusparseHandle,\n                                 HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                                 &alpha,\n                                 matA,\n                                 vecp,\n                                 &beta,\n                                 vecAx,\n                                 HIP_R_32F,\n                                 HIPSPARSE_SPMV_ALG_DEFAULT,\n                                 buffer));\n\n    checkCudaErrors(hipMemsetAsync(d_dot, 0, sizeof(float), stream1));\n    checkCudaErrors(hipblasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n    r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n    a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n\n    checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n    checkCudaErrors(hipMemcpyAsync(d_r0, d_r1, sizeof(float), hipMemcpyDeviceToDevice, stream1));\n    checkCudaErrors(hipMemsetAsync(d_r1, 0, sizeof(float), stream1));\n\n    checkCudaErrors(hipblasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n\n    checkCudaErrors(hipMemcpyAsync((float *)&r1, d_r1, sizeof(float), hipMemcpyDeviceToHost, stream1));\n\n    checkCudaErrors(hipStreamEndCapture(stream1, &initGraph));\n    hipGraphExec_t graphExec;\n    checkCudaErrors(hipGraphInstantiate(&graphExec, initGraph, NULL, NULL, 0));\n#endif\n\n    checkCudaErrors(hipblasSetStream(cublasHandle, stream1));\n    checkCudaErrors(hipsparseSetStream(cusparseHandle, stream1));\n\n    while (r1 > tol * tol && k <= max_iter) {\n#if WITH_GRAPH\n        checkCudaErrors(hipGraphLaunch(graphExec, streamForGraph));\n        checkCudaErrors(hipStreamSynchronize(streamForGraph));\n#else\n        r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_r0, d_b);\n        hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_DEVICE);\n        checkCudaErrors(hipblasSscal(cublasHandle, N, d_b, d_p, 1));\n\n        hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_HOST);\n        checkCudaErrors(hipblasSaxpy(cublasHandle, N, &alpha, d_r, 1, d_p, 1));\n\n        checkCudaErrors(hipsparseSpMV(cusparseHandle,\n                                     HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                                     &alpha,\n                                     matA,\n                                     vecp,\n                                     &beta,\n                                     vecAx,\n                                     HIP_R_32F,\n                                     HIPSPARSE_SPMV_ALG_DEFAULT,\n                                     buffer));\n\n        hipblasSetPointerMode(cublasHandle, HIPBLAS_POINTER_MODE_DEVICE);\n        checkCudaErrors(hipblasSdot(cublasHandle, N, d_p, 1, d_Ax, 1, d_dot));\n\n        r1_div_x<<<1, 1, 0, stream1>>>(d_r1, d_dot, d_a);\n\n        checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_a, d_p, 1, d_x, 1));\n\n        a_minus<<<1, 1, 0, stream1>>>(d_a, d_na);\n        checkCudaErrors(hipblasSaxpy(cublasHandle, N, d_na, d_Ax, 1, d_r, 1));\n\n        checkCudaErrors(hipMemcpyAsync(d_r0, d_r1, sizeof(float), hipMemcpyDeviceToDevice, stream1));\n\n        checkCudaErrors(hipblasSdot(cublasHandle, N, d_r, 1, d_r, 1, d_r1));\n        checkCudaErrors(hipMemcpyAsync((float *)&r1, d_r1, sizeof(float), hipMemcpyDeviceToHost, stream1));\n        checkCudaErrors(hipStreamSynchronize(stream1));\n#endif\n        printf(\"iteration = %3d, residual = %e\\n\", k, sqrt(r1));\n        k++;\n    }\n\n#if WITH_GRAPH\n    checkCudaErrors(hipMemcpyAsync(x, d_x, N * sizeof(float), hipMemcpyDeviceToHost, streamForGraph));\n    checkCudaErrors(hipStreamSynchronize(streamForGraph));\n#else\n    checkCudaErrors(hipMemcpyAsync(x, d_x, N * sizeof(float), hipMemcpyDeviceToHost, stream1));\n    checkCudaErrors(hipStreamSynchronize(stream1));\n#endif\n\n    float rsum, diff, err = 0.0;\n\n    for (int i = 0; i < N; i++) {\n        rsum = 0.0;\n\n        for (int j = I[i]; j < I[i + 1]; j++) {\n            rsum += val[j] * x[J[j]];\n        }\n\n        diff = fabs(rsum - rhs[i]);\n\n        if (diff > err) {\n            err = diff;\n        }\n    }\n\n#if WITH_GRAPH\n    checkCudaErrors(hipGraphExecDestroy(graphExec));\n    checkCudaErrors(hipGraphDestroy(initGraph));\n    checkCudaErrors(hipStreamDestroy(streamForGraph));\n#endif\n    checkCudaErrors(hipStreamDestroy(stream1));\n    hipsparseDestroy(cusparseHandle);\n    hipblasDestroy(cublasHandle);\n\n    if (matA) {\n        checkCudaErrors(hipsparseDestroySpMat(matA));\n    }\n    if (vecx) {\n        checkCudaErrors(hipsparseDestroyDnVec(vecx));\n    }\n    if (vecAx) {\n        checkCudaErrors(hipsparseDestroyDnVec(vecAx));\n    }\n    if (vecp) {\n        checkCudaErrors(hipsparseDestroyDnVec(vecp));\n    }\n\n    checkCudaErrors(hipHostFree(I));\n    checkCudaErrors(hipHostFree(J));\n    checkCudaErrors(hipHostFree(val));\n    checkCudaErrors(hipHostFree(x));\n    free(rhs);\n    checkCudaErrors(hipFree(d_col));\n    checkCudaErrors(hipFree(d_row));\n    checkCudaErrors(hipFree(d_val));\n    checkCudaErrors(hipFree(d_x));\n    checkCudaErrors(hipFree(d_r));\n    checkCudaErrors(hipFree(d_p));\n    checkCudaErrors(hipFree(d_Ax));\n\n    printf(\"Test Summary:  Error amount = %f\\n\", err);\n    exit((k <= max_iter) ? 0 : 1);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/conjugateGradientCudaGraphs/conjugateGradientCudaGraphs.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Example showing the use of CUFFT for fast 1D-convolution using FFT. */\n\n// System includes\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// CUFFT Header file\n#include <cufftXt.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2 Complex;\n\nstatic __device__ __host__ inline Complex ComplexAdd(Complex, Complex);\nstatic __device__ __host__ inline Complex ComplexScale(Complex, float);\nstatic __device__ __host__ inline Complex ComplexMul(Complex, Complex);\nstatic __global__ void                    ComplexPointwiseMulAndScale(cufftComplex *, cufftComplex *, int, float);\n\n// Kernel for GPU\nvoid multiplyCoefficient(cudaLibXtDesc *, cudaLibXtDesc *, int, float, int);\n\n// Filtering functions\nvoid Convolve(const Complex *, int, const Complex *, int, Complex *);\n\n// Padding functions\nint PadData(const Complex *, Complex **, int, const Complex *, Complex **, int);\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n// The filter size is assumed to be a number smaller than the signal size\n///////////////////////////////////////////////////////////////////////////////\nconst int SIGNAL_SIZE        = 1018;\nconst int FILTER_KERNEL_SIZE = 11;\nconst int GPU_COUNT          = 2;\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"\\n[simpleCUFFT_MGPU] is starting...\\n\\n\");\n\n    int GPU_N;\n    checkCudaErrors(cudaGetDeviceCount(&GPU_N));\n\n    if (GPU_N < GPU_COUNT) {\n        printf(\"No. of GPU on node %d\\n\", GPU_N);\n        printf(\"Two GPUs are required to run simpleCUFFT_MGPU sample code\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int *major_minor         = (int *)malloc(sizeof(int) * GPU_N * 2);\n    int  found2IdenticalGPUs = 0;\n    int  nGPUs               = 2;\n    int *whichGPUs;\n    whichGPUs = (int *)malloc(sizeof(int) * nGPUs);\n\n    for (int i = 0; i < GPU_N; i++) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));\n        major_minor[i * 2]     = deviceProp.major;\n        major_minor[i * 2 + 1] = deviceProp.minor;\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    for (int i = 0; i < GPU_N; i++) {\n        for (int j = i + 1; j < GPU_N; j++) {\n            if ((major_minor[i * 2] == major_minor[j * 2]) && (major_minor[i * 2 + 1] == major_minor[j * 2 + 1])) {\n                whichGPUs[0]        = i;\n                whichGPUs[1]        = j;\n                found2IdenticalGPUs = 1;\n                break;\n            }\n        }\n        if (found2IdenticalGPUs) {\n            break;\n        }\n    }\n\n    free(major_minor);\n    if (!found2IdenticalGPUs) {\n        printf(\"No Two GPUs with same architecture found\\nWaiving simpleCUFFT_2d_MGPU \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Allocate host memory for the signal\n    Complex *h_signal = (Complex *)malloc(sizeof(Complex) * SIGNAL_SIZE);\n\n    // Initialize the memory for the signal\n    for (int i = 0; i < SIGNAL_SIZE; ++i) {\n        h_signal[i].x = rand() / (float)RAND_MAX;\n        h_signal[i].y = 0;\n    }\n\n    // Allocate host memory for the filter\n    Complex *h_filter_kernel = (Complex *)malloc(sizeof(Complex) * FILTER_KERNEL_SIZE);\n\n    // Initialize the memory for the filter\n    for (int i = 0; i < FILTER_KERNEL_SIZE; ++i) {\n        h_filter_kernel[i].x = rand() / (float)RAND_MAX;\n        h_filter_kernel[i].y = 0;\n    }\n\n    // Pad signal and filter kernel\n    Complex *h_padded_signal;\n    Complex *h_padded_filter_kernel;\n    int      new_size =\n        PadData(h_signal, &h_padded_signal, SIGNAL_SIZE, h_filter_kernel, &h_padded_filter_kernel, FILTER_KERNEL_SIZE);\n\n    // cufftCreate() - Create an empty plan\n    cufftResult result;\n    cufftHandle plan_input;\n    checkCudaErrors(cufftCreate(&plan_input));\n\n    // cufftXtSetGPUs() - Define which GPUs to use\n    result = cufftXtSetGPUs(plan_input, nGPUs, whichGPUs);\n\n    if (result == CUFFT_INVALID_DEVICE) {\n        printf(\"This sample requires two GPUs on the same board.\\n\");\n        printf(\"No such board was found. Waiving sample.\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else if (result != CUFFT_SUCCESS) {\n        printf(\"cufftXtSetGPUs failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Print the device information to run the code\n    printf(\"\\nRunning on GPUs\\n\");\n    for (int i = 0; i < nGPUs; i++) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, whichGPUs[i]));\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               whichGPUs[i],\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    size_t *worksize;\n    worksize = (size_t *)malloc(sizeof(size_t) * nGPUs);\n\n    // cufftMakePlan1d() - Create the plan\n    checkCudaErrors(cufftMakePlan1d(plan_input, new_size, CUFFT_C2C, 1, worksize));\n\n    // cufftXtMalloc() - Malloc data on multiple GPUs\n    cudaLibXtDesc *d_signal;\n    checkCudaErrors(cufftXtMalloc(plan_input, (cudaLibXtDesc **)&d_signal, CUFFT_XT_FORMAT_INPLACE));\n    cudaLibXtDesc *d_out_signal;\n    checkCudaErrors(cufftXtMalloc(plan_input, (cudaLibXtDesc **)&d_out_signal, CUFFT_XT_FORMAT_INPLACE));\n    cudaLibXtDesc *d_filter_kernel;\n    checkCudaErrors(cufftXtMalloc(plan_input, (cudaLibXtDesc **)&d_filter_kernel, CUFFT_XT_FORMAT_INPLACE));\n    cudaLibXtDesc *d_out_filter_kernel;\n    checkCudaErrors(cufftXtMalloc(plan_input, (cudaLibXtDesc **)&d_out_filter_kernel, CUFFT_XT_FORMAT_INPLACE));\n\n    // cufftXtMemcpy() - Copy data from host to multiple GPUs\n    checkCudaErrors(cufftXtMemcpy(plan_input, d_signal, h_padded_signal, CUFFT_COPY_HOST_TO_DEVICE));\n    checkCudaErrors(cufftXtMemcpy(plan_input, d_filter_kernel, h_padded_filter_kernel, CUFFT_COPY_HOST_TO_DEVICE));\n\n    // cufftXtExecDescriptorC2C() - Execute FFT on data on multiple GPUs\n    checkCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_signal, d_signal, CUFFT_FORWARD));\n    checkCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_filter_kernel, d_filter_kernel, CUFFT_FORWARD));\n\n    // cufftXtMemcpy() - Copy the data to natural order on GPUs\n    checkCudaErrors(cufftXtMemcpy(plan_input, d_out_signal, d_signal, CUFFT_COPY_DEVICE_TO_DEVICE));\n    checkCudaErrors(cufftXtMemcpy(plan_input, d_out_filter_kernel, d_filter_kernel, CUFFT_COPY_DEVICE_TO_DEVICE));\n\n    printf(\"\\n\\nValue of Library Descriptor\\n\");\n    printf(\"Number of GPUs %d\\n\", d_out_signal->descriptor->nGPUs);\n    printf(\"Device id  %d %d\\n\", d_out_signal->descriptor->GPUs[0], d_out_signal->descriptor->GPUs[1]);\n    printf(\"Data size on GPU %ld %ld\\n\",\n           (long)(d_out_signal->descriptor->size[0] / sizeof(cufftComplex)),\n           (long)(d_out_signal->descriptor->size[1] / sizeof(cufftComplex)));\n\n    // Multiply the coefficients together and normalize the result\n    printf(\"Launching ComplexPointwiseMulAndScale<<< >>>\\n\");\n    multiplyCoefficient(d_out_signal, d_out_filter_kernel, new_size, 1.0f / new_size, nGPUs);\n\n    // cufftXtExecDescriptorC2C() - Execute inverse  FFT on data on multiple GPUs\n    printf(\"Transforming signal back cufftExecC2C\\n\");\n    checkCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_out_signal, d_out_signal, CUFFT_INVERSE));\n\n    // Create host pointer pointing to padded signal\n    Complex *h_convolved_signal = h_padded_signal;\n\n    // Allocate host memory for the convolution result\n    Complex *h_convolved_signal_ref = (Complex *)malloc(sizeof(Complex) * SIGNAL_SIZE);\n\n    // cufftXtMemcpy() - Copy data from multiple GPUs to host\n    checkCudaErrors(cufftXtMemcpy(plan_input, h_convolved_signal, d_out_signal, CUFFT_COPY_DEVICE_TO_HOST));\n\n    // Convolve on the host\n    Convolve(h_signal, SIGNAL_SIZE, h_filter_kernel, FILTER_KERNEL_SIZE, h_convolved_signal_ref);\n\n    // Compare CPU and GPU result\n    bool bTestResult =\n        sdkCompareL2fe((float *)h_convolved_signal_ref, (float *)h_convolved_signal, 2 * SIGNAL_SIZE, 1e-5f);\n    printf(\"\\nvalue of TestResult %d\\n\", bTestResult);\n\n    // Cleanup memory\n    free(whichGPUs);\n    free(worksize);\n    free(h_signal);\n    free(h_filter_kernel);\n    free(h_padded_signal);\n    free(h_padded_filter_kernel);\n    free(h_convolved_signal_ref);\n\n    // cudaXtFree() - Free GPU memory\n    checkCudaErrors(cufftXtFree(d_signal));\n    checkCudaErrors(cufftXtFree(d_filter_kernel));\n    checkCudaErrors(cufftXtFree(d_out_signal));\n    checkCudaErrors(cufftXtFree(d_out_filter_kernel));\n\n    // cufftDestroy() - Destroy FFT plan\n    checkCudaErrors(cufftDestroy(plan_input));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n// Function for padding original data\n//////////////////////////////////////////////////////////////////////////////////\nint PadData(const Complex *signal,\n            Complex      **padded_signal,\n            int            signal_size,\n            const Complex *filter_kernel,\n            Complex      **padded_filter_kernel,\n            int            filter_kernel_size)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n    int new_size  = signal_size + maxRadius;\n\n    // Pad signal\n    Complex *new_data = (Complex *)malloc(sizeof(Complex) * new_size);\n    memcpy(new_data + 0, signal, signal_size * sizeof(Complex));\n    memset(new_data + signal_size, 0, (new_size - signal_size) * sizeof(Complex));\n    *padded_signal = new_data;\n\n    // Pad filter\n    new_data = (Complex *)malloc(sizeof(Complex) * new_size);\n    memcpy(new_data + 0, filter_kernel + minRadius, maxRadius * sizeof(Complex));\n    memset(new_data + maxRadius, 0, (new_size - filter_kernel_size) * sizeof(Complex));\n    memcpy(new_data + new_size - minRadius, filter_kernel, minRadius * sizeof(Complex));\n    *padded_filter_kernel = new_data;\n\n    return new_size;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Filtering operations - Computing Convolution on the host\n////////////////////////////////////////////////////////////////////////////////\nvoid Convolve(const Complex *signal,\n              int            signal_size,\n              const Complex *filter_kernel,\n              int            filter_kernel_size,\n              Complex       *filtered_signal)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n\n    // Loop over output element indices\n    for (int i = 0; i < signal_size; ++i) {\n        filtered_signal[i].x = filtered_signal[i].y = 0;\n\n        // Loop over convolution indices\n        for (int j = -maxRadius + 1; j <= minRadius; ++j) {\n            int k = i + j;\n\n            if (k >= 0 && k < signal_size) {\n                filtered_signal[i] =\n                    ComplexAdd(filtered_signal[i], ComplexMul(signal[k], filter_kernel[minRadius - j]));\n            }\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//  Launch Kernel on multiple GPU\n////////////////////////////////////////////////////////////////////////////////\nvoid multiplyCoefficient(cudaLibXtDesc *d_signal, cudaLibXtDesc *d_filter_kernel, int new_size, float val, int nGPUs)\n{\n    int device;\n    // Launch the ComplexPointwiseMulAndScale<<< >>> kernel on multiple GPU\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_signal->descriptor->GPUs[i];\n\n        // Set device\n        checkCudaErrors(cudaSetDevice(device));\n\n        // Perform GPU computations\n        ComplexPointwiseMulAndScale<<<32, 256>>>((cufftComplex *)d_signal->descriptor->data[i],\n                                                 (cufftComplex *)d_filter_kernel->descriptor->data[i],\n                                                 int(d_signal->descriptor->size[i] / sizeof(cufftComplex)),\n                                                 val);\n    }\n\n    // Wait for device to finish all operation\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_signal->descriptor->GPUs[i];\n        checkCudaErrors(cudaSetDevice(device));\n        cudaDeviceSynchronize();\n        // Check if kernel execution generated and error\n        getLastCudaError(\"Kernel execution failed [ ComplexPointwiseMulAndScale ]\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Complex operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Complex addition\nstatic __device__ __host__ inline Complex ComplexAdd(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n// Complex scale\nstatic __device__ __host__ inline Complex ComplexScale(Complex a, float s)\n{\n    Complex c;\n    c.x = s * a.x;\n    c.y = s * a.y;\n    return c;\n}\n\n// Complex multiplication\nstatic __device__ __host__ inline Complex ComplexMul(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x * b.x - a.y * b.y;\n    c.y = a.x * b.y + a.y * b.x;\n    return c;\n}\n// Complex pointwise multiplication\nstatic __global__ void ComplexPointwiseMulAndScale(cufftComplex *a, cufftComplex *b, int size, float scale)\n{\n    const int numThreads = blockDim.x * gridDim.x;\n    const int threadID   = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = threadID; i < size; i += numThreads) {\n        a[i] = ComplexScale(ComplexMul(a[i], b[i]), scale);\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* Example showing the use of CUFFT for fast 1D-convolution using FFT. */\n\n// System includes\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// CUFFT Header file\n#include <hipfft/hipfftXt.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2 Complex;\n\nstatic __device__ __host__ inline Complex ComplexAdd(Complex, Complex);\nstatic __device__ __host__ inline Complex ComplexScale(Complex, float);\nstatic __device__ __host__ inline Complex ComplexMul(Complex, Complex);\nstatic __global__ void                    ComplexPointwiseMulAndScale(hipfftComplex *, hipfftComplex *, int, float);\n\n// Kernel for GPU\nvoid multiplyCoefficient(hipLibXtDesc *, hipLibXtDesc *, int, float, int);\n\n// Filtering functions\nvoid Convolve(const Complex *, int, const Complex *, int, Complex *);\n\n// Padding functions\nint PadData(const Complex *, Complex **, int, const Complex *, Complex **, int);\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n// The filter size is assumed to be a number smaller than the signal size\n///////////////////////////////////////////////////////////////////////////////\nconst int SIGNAL_SIZE        = 1018;\nconst int FILTER_KERNEL_SIZE = 11;\nconst int GPU_COUNT          = 2;\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"\\n[simpleCUFFT_MGPU] is starting...\\n\\n\");\n\n    int GPU_N;\n    checkCudaErrors(hipGetDeviceCount(&GPU_N));\n\n    if (GPU_N < GPU_COUNT) {\n        printf(\"No. of GPU on node %d\\n\", GPU_N);\n        printf(\"Two GPUs are required to run simpleCUFFT_MGPU sample code\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int *major_minor         = (int *)malloc(sizeof(int) * GPU_N * 2);\n    int  found2IdenticalGPUs = 0;\n    int  nGPUs               = 2;\n    int *whichGPUs;\n    whichGPUs = (int *)malloc(sizeof(int) * nGPUs);\n\n    for (int i = 0; i < GPU_N; i++) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, i));\n        major_minor[i * 2]     = deviceProp.major;\n        major_minor[i * 2 + 1] = deviceProp.minor;\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    for (int i = 0; i < GPU_N; i++) {\n        for (int j = i + 1; j < GPU_N; j++) {\n            if ((major_minor[i * 2] == major_minor[j * 2]) && (major_minor[i * 2 + 1] == major_minor[j * 2 + 1])) {\n                whichGPUs[0]        = i;\n                whichGPUs[1]        = j;\n                found2IdenticalGPUs = 1;\n                break;\n            }\n        }\n        if (found2IdenticalGPUs) {\n            break;\n        }\n    }\n\n    free(major_minor);\n    if (!found2IdenticalGPUs) {\n        printf(\"No Two GPUs with same architecture found\\nWaiving simpleCUFFT_2d_MGPU \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Allocate host memory for the signal\n    Complex *h_signal = (Complex *)malloc(sizeof(Complex) * SIGNAL_SIZE);\n\n    // Initialize the memory for the signal\n    for (int i = 0; i < SIGNAL_SIZE; ++i) {\n        h_signal[i].x = rand() / (float)RAND_MAX;\n        h_signal[i].y = 0;\n    }\n\n    // Allocate host memory for the filter\n    Complex *h_filter_kernel = (Complex *)malloc(sizeof(Complex) * FILTER_KERNEL_SIZE);\n\n    // Initialize the memory for the filter\n    for (int i = 0; i < FILTER_KERNEL_SIZE; ++i) {\n        h_filter_kernel[i].x = rand() / (float)RAND_MAX;\n        h_filter_kernel[i].y = 0;\n    }\n\n    // Pad signal and filter kernel\n    Complex *h_padded_signal;\n    Complex *h_padded_filter_kernel;\n    int      new_size =\n        PadData(h_signal, &h_padded_signal, SIGNAL_SIZE, h_filter_kernel, &h_padded_filter_kernel, FILTER_KERNEL_SIZE);\n\n    // cufftCreate() - Create an empty plan\n    hipfftResult result;\n    hipfftHandle plan_input;\n    checkCudaErrors(hipfftCreate(&plan_input));\n\n    // cufftXtSetGPUs() - Define which GPUs to use\n    result = hipfftXtSetGPUs(plan_input, nGPUs, whichGPUs);\n\n    if (result == HIPFFT_INVALID_DEVICE) {\n        printf(\"This sample requires two GPUs on the same board.\\n\");\n        printf(\"No such board was found. Waiving sample.\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else if (result != HIPFFT_SUCCESS) {\n        printf(\"hipfftXtSetGPUs failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Print the device information to run the code\n    printf(\"\\nRunning on GPUs\\n\");\n    for (int i = 0; i < nGPUs; i++) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, whichGPUs[i]));\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               whichGPUs[i],\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    size_t *worksize;\n    worksize = (size_t *)malloc(sizeof(size_t) * nGPUs);\n\n    // cufftMakePlan1d() - Create the plan\n    checkCudaErrors(hipfftMakePlan1d(plan_input, new_size, HIPFFT_C2C, 1, worksize));\n\n    // cufftXtMalloc() - Malloc data on multiple GPUs\n    hipLibXtDesc *d_signal;\n    checkCudaErrors(hipfftXtMalloc(plan_input, (hipLibXtDesc **)&d_signal, HIPFFT_XT_FORMAT_INPLACE));\n    hipLibXtDesc *d_out_signal;\n    checkCudaErrors(hipfftXtMalloc(plan_input, (hipLibXtDesc **)&d_out_signal, HIPFFT_XT_FORMAT_INPLACE));\n    hipLibXtDesc *d_filter_kernel;\n    checkCudaErrors(hipfftXtMalloc(plan_input, (hipLibXtDesc **)&d_filter_kernel, HIPFFT_XT_FORMAT_INPLACE));\n    hipLibXtDesc *d_out_filter_kernel;\n    checkCudaErrors(hipfftXtMalloc(plan_input, (hipLibXtDesc **)&d_out_filter_kernel, HIPFFT_XT_FORMAT_INPLACE));\n\n    // cufftXtMemcpy() - Copy data from host to multiple GPUs\n    checkCudaErrors(hipfftXtMemcpy(plan_input, d_signal, h_padded_signal, HIPFFT_COPY_HOST_TO_DEVICE));\n    checkCudaErrors(hipfftXtMemcpy(plan_input, d_filter_kernel, h_padded_filter_kernel, HIPFFT_COPY_HOST_TO_DEVICE));\n\n    // cufftXtExecDescriptorC2C() - Execute FFT on data on multiple GPUs\n    checkCudaErrors(hipfftXtExecDescriptorC2C(plan_input, d_signal, d_signal, HIPFFT_FORWARD));\n    checkCudaErrors(hipfftXtExecDescriptorC2C(plan_input, d_filter_kernel, d_filter_kernel, HIPFFT_FORWARD));\n\n    // cufftXtMemcpy() - Copy the data to natural order on GPUs\n    checkCudaErrors(hipfftXtMemcpy(plan_input, d_out_signal, d_signal, HIPFFT_COPY_DEVICE_TO_DEVICE));\n    checkCudaErrors(hipfftXtMemcpy(plan_input, d_out_filter_kernel, d_filter_kernel, HIPFFT_COPY_DEVICE_TO_DEVICE));\n\n    printf(\"\\n\\nValue of Library Descriptor\\n\");\n    printf(\"Number of GPUs %d\\n\", d_out_signal->descriptor->nGPUs);\n    printf(\"Device id  %d %d\\n\", d_out_signal->descriptor->GPUs[0], d_out_signal->descriptor->GPUs[1]);\n    printf(\"Data size on GPU %ld %ld\\n\",\n           (long)(d_out_signal->descriptor->size[0] / sizeof(hipfftComplex)),\n           (long)(d_out_signal->descriptor->size[1] / sizeof(hipfftComplex)));\n\n    // Multiply the coefficients together and normalize the result\n    printf(\"Launching ComplexPointwiseMulAndScale<<< >>>\\n\");\n    multiplyCoefficient(d_out_signal, d_out_filter_kernel, new_size, 1.0f / new_size, nGPUs);\n\n    // cufftXtExecDescriptorC2C() - Execute inverse  FFT on data on multiple GPUs\n    printf(\"Transforming signal back hipfftExecC2C\\n\");\n    checkCudaErrors(hipfftXtExecDescriptorC2C(plan_input, d_out_signal, d_out_signal, HIPFFT_BACKWARD));\n\n    // Create host pointer pointing to padded signal\n    Complex *h_convolved_signal = h_padded_signal;\n\n    // Allocate host memory for the convolution result\n    Complex *h_convolved_signal_ref = (Complex *)malloc(sizeof(Complex) * SIGNAL_SIZE);\n\n    // cufftXtMemcpy() - Copy data from multiple GPUs to host\n    checkCudaErrors(hipfftXtMemcpy(plan_input, h_convolved_signal, d_out_signal, HIPFFT_COPY_DEVICE_TO_HOST));\n\n    // Convolve on the host\n    Convolve(h_signal, SIGNAL_SIZE, h_filter_kernel, FILTER_KERNEL_SIZE, h_convolved_signal_ref);\n\n    // Compare CPU and GPU result\n    bool bTestResult =\n        sdkCompareL2fe((float *)h_convolved_signal_ref, (float *)h_convolved_signal, 2 * SIGNAL_SIZE, 1e-5f);\n    printf(\"\\nvalue of TestResult %d\\n\", bTestResult);\n\n    // Cleanup memory\n    free(whichGPUs);\n    free(worksize);\n    free(h_signal);\n    free(h_filter_kernel);\n    free(h_padded_signal);\n    free(h_padded_filter_kernel);\n    free(h_convolved_signal_ref);\n\n    // cudaXtFree() - Free GPU memory\n    checkCudaErrors(hipfftXtFree(d_signal));\n    checkCudaErrors(hipfftXtFree(d_filter_kernel));\n    checkCudaErrors(hipfftXtFree(d_out_signal));\n    checkCudaErrors(hipfftXtFree(d_out_filter_kernel));\n\n    // cufftDestroy() - Destroy FFT plan\n    checkCudaErrors(hipfftDestroy(plan_input));\n\n    exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n///////////////////////////////////////////////////////////////////////////////////\n// Function for padding original data\n//////////////////////////////////////////////////////////////////////////////////\nint PadData(const Complex *signal,\n            Complex      **padded_signal,\n            int            signal_size,\n            const Complex *filter_kernel,\n            Complex      **padded_filter_kernel,\n            int            filter_kernel_size)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n    int new_size  = signal_size + maxRadius;\n\n    // Pad signal\n    Complex *new_data = (Complex *)malloc(sizeof(Complex) * new_size);\n    memcpy(new_data + 0, signal, signal_size * sizeof(Complex));\n    memset(new_data + signal_size, 0, (new_size - signal_size) * sizeof(Complex));\n    *padded_signal = new_data;\n\n    // Pad filter\n    new_data = (Complex *)malloc(sizeof(Complex) * new_size);\n    memcpy(new_data + 0, filter_kernel + minRadius, maxRadius * sizeof(Complex));\n    memset(new_data + maxRadius, 0, (new_size - filter_kernel_size) * sizeof(Complex));\n    memcpy(new_data + new_size - minRadius, filter_kernel, minRadius * sizeof(Complex));\n    *padded_filter_kernel = new_data;\n\n    return new_size;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Filtering operations - Computing Convolution on the host\n////////////////////////////////////////////////////////////////////////////////\nvoid Convolve(const Complex *signal,\n              int            signal_size,\n              const Complex *filter_kernel,\n              int            filter_kernel_size,\n              Complex       *filtered_signal)\n{\n    int minRadius = filter_kernel_size / 2;\n    int maxRadius = filter_kernel_size - minRadius;\n\n    // Loop over output element indices\n    for (int i = 0; i < signal_size; ++i) {\n        filtered_signal[i].x = filtered_signal[i].y = 0;\n\n        // Loop over convolution indices\n        for (int j = -maxRadius + 1; j <= minRadius; ++j) {\n            int k = i + j;\n\n            if (k >= 0 && k < signal_size) {\n                filtered_signal[i] =\n                    ComplexAdd(filtered_signal[i], ComplexMul(signal[k], filter_kernel[minRadius - j]));\n            }\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//  Launch Kernel on multiple GPU\n////////////////////////////////////////////////////////////////////////////////\nvoid multiplyCoefficient(hipLibXtDesc *d_signal, hipLibXtDesc *d_filter_kernel, int new_size, float val, int nGPUs)\n{\n    int device;\n    // Launch the ComplexPointwiseMulAndScale<<< >>> kernel on multiple GPU\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_signal->descriptor->GPUs[i];\n\n        // Set device\n        checkCudaErrors(hipSetDevice(device));\n\n        // Perform GPU computations\n        ComplexPointwiseMulAndScale<<<32, 256>>>((hipfftComplex *)d_signal->descriptor->data[i],\n                                                 (hipfftComplex *)d_filter_kernel->descriptor->data[i],\n                                                 int(d_signal->descriptor->size[i] / sizeof(hipfftComplex)),\n                                                 val);\n    }\n\n    // Wait for device to finish all operation\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_signal->descriptor->GPUs[i];\n        checkCudaErrors(hipSetDevice(device));\n        hipDeviceSynchronize();\n        // Check if kernel execution generated and error\n        getLastCudaError(\"Kernel execution failed [ ComplexPointwiseMulAndScale ]\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Complex operations\n////////////////////////////////////////////////////////////////////////////////\n\n// Complex addition\nstatic __device__ __host__ inline Complex ComplexAdd(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x + b.x;\n    c.y = a.y + b.y;\n    return c;\n}\n\n// Complex scale\nstatic __device__ __host__ inline Complex ComplexScale(Complex a, float s)\n{\n    Complex c;\n    c.x = s * a.x;\n    c.y = s * a.y;\n    return c;\n}\n\n// Complex multiplication\nstatic __device__ __host__ inline Complex ComplexMul(Complex a, Complex b)\n{\n    Complex c;\n    c.x = a.x * b.x - a.y * b.y;\n    c.y = a.x * b.y + a.y * b.x;\n    return c;\n}\n// Complex pointwise multiplication\nstatic __global__ void ComplexPointwiseMulAndScale(hipfftComplex *a, hipfftComplex *b, int size, float scale)\n{\n    const int numThreads = blockDim.x * gridDim.x;\n    const int threadID   = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = threadID; i < size; i += numThreads) {\n        a[i] = ComplexScale(ComplexMul(a[i], b[i]), scale);\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/simpleCUFFT_MGPU/simpleCUFFT_MGPU.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  simpleCUFFT_2d_MGPU.cu\n//\n//  This sample code demonstrate the use of CUFFT library for 2D data on multiple GPU.\n//  Example showing the use of CUFFT for solving 2D-POISSON equation using FFT on multiple GPU.\n//  For reference we have used the equation given in http://www.bu.edu/pasi/files/2011/07/\n//  Lecture83.pdf\n//\n////////////////////////////////////////////////////////////////////////////////\n\n\n// System includes\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// CUFFT Header file\n#include <cufftXt.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2 Complex;\n\n// Data configuration\nconst int GPU_COUNT = 2;\nconst int BSZ_Y     = 4;\nconst int BSZ_X     = 4;\n\n// Forward Declaration\nvoid solvePoissonEquation(cudaLibXtDesc *, cudaLibXtDesc *, float **, int, int);\n\n__global__ void solvePoisson(cufftComplex *, cufftComplex *, float *, int, int, int n_gpu);\n\n///////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"\\nPoisson equation using CUFFT library on Multiple GPUs is \"\n           \"starting...\\n\\n\");\n\n    int GPU_N;\n    checkCudaErrors(cudaGetDeviceCount(&GPU_N));\n\n    if (GPU_N < GPU_COUNT) {\n        printf(\"No. of GPU on node %d\\n\", GPU_N);\n        printf(\"Two GPUs are required to run simpleCUFFT_2d_MGPU sample code\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int *major_minor         = (int *)malloc(sizeof(int) * GPU_N * 2);\n    int  found2IdenticalGPUs = 0;\n    int  nGPUs               = 2;\n    int *whichGPUs;\n    whichGPUs = (int *)malloc(sizeof(int) * nGPUs);\n\n    for (int i = 0; i < GPU_N; i++) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, i));\n        major_minor[i * 2]     = deviceProp.major;\n        major_minor[i * 2 + 1] = deviceProp.minor;\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    for (int i = 0; i < GPU_N; i++) {\n        for (int j = i + 1; j < GPU_N; j++) {\n            if ((major_minor[i * 2] == major_minor[j * 2]) && (major_minor[i * 2 + 1] == major_minor[j * 2 + 1])) {\n                whichGPUs[0]        = i;\n                whichGPUs[1]        = j;\n                found2IdenticalGPUs = 1;\n                break;\n            }\n        }\n        if (found2IdenticalGPUs) {\n            break;\n        }\n    }\n\n    free(major_minor);\n    if (!found2IdenticalGPUs) {\n        printf(\"No Two GPUs with same architecture found\\nWaiving simpleCUFFT_2d_MGPU \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int    N    = 64;\n    float  xMAX = 1.0f, xMIN = 0.0f, yMIN = 0.0f, h = (xMAX - xMIN) / ((float)N), s = 0.1f, s2 = s * s;\n    float *x, *y, *f, *u_a, r2;\n\n    x   = (float *)malloc(sizeof(float) * N * N);\n    y   = (float *)malloc(sizeof(float) * N * N);\n    f   = (float *)malloc(sizeof(float) * N * N);\n    u_a = (float *)malloc(sizeof(float) * N * N);\n\n    for (int j = 0; j < N; j++)\n        for (int i = 0; i < N; i++) {\n            x[N * j + i] = xMIN + i * h;\n            y[N * j + i] = yMIN + j * h;\n            r2 = (x[N * j + i] - 0.5f) * (x[N * j + i] - 0.5f) + (y[N * j + i] - 0.5f) * (y[N * j + i] - 0.5f);\n            f[N * j + i]   = (r2 - 2 * s2) / (s2 * s2) * exp(-r2 / (2 * s2));\n            u_a[N * j + i] = exp(-r2 / (2 * s2)); // analytical solution\n        }\n\n    float *k, *d_k[GPU_COUNT];\n    k = (float *)malloc(sizeof(float) * N);\n    for (int i = 0; i <= N / 2; i++) {\n        k[i] = i * 2 * (float)M_PI;\n    }\n    for (int i = N / 2 + 1; i < N; i++) {\n        k[i] = (i - N) * 2 * (float)M_PI;\n    }\n\n    // Create a complex variable on host\n    Complex *h_f = (Complex *)malloc(sizeof(Complex) * N * N);\n\n    // Initialize the memory for the signal\n    for (int i = 0; i < (N * N); i++) {\n        h_f[i].x = f[i];\n        h_f[i].y = 0.0f;\n    }\n\n    // cufftCreate() - Create an empty plan\n    cufftResult result;\n    cufftHandle planComplex;\n    result = cufftCreate(&planComplex);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"cufftCreate failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtSetGPUs() - Define which GPUs to use\n    result = cufftXtSetGPUs(planComplex, nGPUs, whichGPUs);\n\n    if (result == CUFFT_INVALID_DEVICE) {\n        printf(\"This sample requires two GPUs on the same board.\\n\");\n        printf(\"No such board was found. Waiving sample.\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else if (result != CUFFT_SUCCESS) {\n        printf(\"cufftXtSetGPUs failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Print the device information to run the code\n    printf(\"\\nRunning on GPUs\\n\");\n    for (int i = 0; i < 2; i++) {\n        cudaDeviceProp deviceProp;\n        checkCudaErrors(cudaGetDeviceProperties(&deviceProp, whichGPUs[i]));\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               whichGPUs[i],\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    size_t *worksize;\n    worksize = (size_t *)malloc(sizeof(size_t) * nGPUs);\n\n    // cufftMakePlan2d() - Create the plan\n    result = cufftMakePlan2d(planComplex, N, N, CUFFT_C2C, worksize);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*MakePlan* failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    for (int i = 0; i < nGPUs; i++) {\n        cudaSetDevice(whichGPUs[i]);\n        cudaMalloc((void **)&d_k[i], sizeof(float) * N);\n        cudaMemcpy(d_k[i], k, sizeof(float) * N, cudaMemcpyHostToDevice);\n    }\n\n    // Create a variable on device\n    // d_f - variable on device to store the input data\n    // d_d_f - variable that store the natural order of d_f data\n    // d_out - device output\n    cudaLibXtDesc *d_f, *d_d_f, *d_out;\n\n    // cufftXtMalloc() - Malloc data on multiple GPUs\n\n    result = cufftXtMalloc(planComplex, (cudaLibXtDesc **)&d_f, CUFFT_XT_FORMAT_INPLACE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    result = cufftXtMalloc(planComplex, (cudaLibXtDesc **)&d_d_f, CUFFT_XT_FORMAT_INPLACE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    result = cufftXtMalloc(planComplex, (cudaLibXtDesc **)&d_out, CUFFT_XT_FORMAT_INPLACE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtMemcpy() - Copy the data from host to device\n    result = cufftXtMemcpy(planComplex, d_f, h_f, CUFFT_COPY_HOST_TO_DEVICE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtExecDescriptorC2C() - Execute FFT on data on multiple GPUs\n    printf(\"Forward 2d FFT on multiple GPUs\\n\");\n    result = cufftXtExecDescriptorC2C(planComplex, d_f, d_f, CUFFT_FORWARD);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtExecC2C  failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtMemcpy() - Copy the data to natural order on GPUs\n    result = cufftXtMemcpy(planComplex, d_d_f, d_f, CUFFT_COPY_DEVICE_TO_DEVICE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Solve Poisson Equation\\n\");\n    solvePoissonEquation(d_d_f, d_out, d_k, N, nGPUs);\n\n    printf(\"Inverse 2d FFT on multiple GPUs\\n\");\n    // cufftXtExecDescriptorC2C() - Execute inverse  FFT on data on multiple GPUs\n    result = cufftXtExecDescriptorC2C(planComplex, d_out, d_out, CUFFT_INVERSE);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtExecC2C  failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Create a variable on host to copy the data from device\n    // h_d_out - variable store the output of device\n    Complex *h_d_out = (Complex *)malloc(sizeof(Complex) * N * N);\n\n    // cufftXtMemcpy() - Copy data from multiple GPUs to host\n    result = cufftXtMemcpy(planComplex, h_d_out, d_out, CUFFT_COPY_DEVICE_TO_HOST);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    float *out      = (float *)malloc(sizeof(float) * N * N);\n    float  constant = h_d_out[0].x / N * N;\n    for (int i = 0; i < N * N; i++) {\n        // subtract u[0] to force the arbitrary constant to be 0\n        out[i] = (h_d_out[i].x / (N * N)) - constant;\n    }\n\n    // cleanup memory\n\n    free(h_f);\n    free(k);\n    free(out);\n    free(h_d_out);\n    free(x);\n    free(whichGPUs);\n    free(y);\n    free(f);\n    free(u_a);\n    free(worksize);\n\n    // cudaXtFree() - Free GPU memory\n    for (int i = 0; i < GPU_COUNT; i++) {\n        cudaFree(d_k[i]);\n    }\n    result = cufftXtFree(d_out);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n    result = cufftXtFree(d_f);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n    result = cufftXtFree(d_d_f);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftDestroy() - Destroy FFT plan\n    result = cufftDestroy(planComplex);\n    if (result != CUFFT_SUCCESS) {\n        printf(\"cufftDestroy failed: code %d\\n\", (int)result);\n        exit(EXIT_FAILURE);\n    }\n\n    exit(EXIT_SUCCESS);\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n// Launch kernel on  multiple GPU\n///////////////////////////////////////////////////////////////////////////////////\nvoid solvePoissonEquation(cudaLibXtDesc *d_ft, cudaLibXtDesc *d_ft_k, float **k, int N, int nGPUs)\n{\n    int  device;\n    dim3 dimGrid(int(N / BSZ_X), int((N / 2) / BSZ_Y));\n    dim3 dimBlock(BSZ_X, BSZ_Y);\n\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_ft_k->descriptor->GPUs[i];\n        cudaSetDevice(device);\n        solvePoisson<<<dimGrid, dimBlock>>>(\n            (cufftComplex *)d_ft->descriptor->data[i], (cufftComplex *)d_ft_k->descriptor->data[i], k[i], N, i, nGPUs);\n    }\n\n    // Wait for device to finish all operation\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_ft_k->descriptor->GPUs[i];\n        cudaSetDevice(device);\n        cudaDeviceSynchronize();\n\n        // Check if kernel execution generated and error\n        getLastCudaError(\"Kernel execution failed [ solvePoisson ]\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernel for Solving Poisson equation on GPU\n////////////////////////////////////////////////////////////////////////////////\n__global__ void solvePoisson(cufftComplex *ft, cufftComplex *ft_k, float *k, int N, int gpu_id, int n_gpu)\n{\n    int i     = threadIdx.x + blockIdx.x * blockDim.x;\n    int j     = threadIdx.y + blockIdx.y * blockDim.y;\n    int index = j * N + i;\n    if (i < N && j < N / n_gpu) {\n        float k2 = k[i] * k[i] + k[j + gpu_id * N / n_gpu] * k[j + gpu_id * N / n_gpu];\n        if (i == 0 && j == 0 && gpu_id == 0) {\n            k2 = 1.0f;\n        }\n\n        ft_k[index].x = -ft[index].x * 1 / k2;\n        ft_k[index].y = -ft[index].y * 1 / k2;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n//\n//  simpleCUFFT_2d_MGPU.cu\n//\n//  This sample code demonstrate the use of CUFFT library for 2D data on multiple GPU.\n//  Example showing the use of CUFFT for solving 2D-POISSON equation using FFT on multiple GPU.\n//  For reference we have used the equation given in http://www.bu.edu/pasi/files/2011/07/\n//  Lecture83.pdf\n//\n////////////////////////////////////////////////////////////////////////////////\n\n\n// System includes\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// CUFFT Header file\n#include <hipfft/hipfftXt.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Complex data type\ntypedef float2 Complex;\n\n// Data configuration\nconst int GPU_COUNT = 2;\nconst int BSZ_Y     = 4;\nconst int BSZ_X     = 4;\n\n// Forward Declaration\nvoid solvePoissonEquation(hipLibXtDesc *, hipLibXtDesc *, float **, int, int);\n\n__global__ void solvePoisson(hipfftComplex *, hipfftComplex *, float *, int, int, int n_gpu);\n\n///////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    printf(\"\\nPoisson equation using CUFFT library on Multiple GPUs is \"\n           \"starting...\\n\\n\");\n\n    int GPU_N;\n    checkCudaErrors(hipGetDeviceCount(&GPU_N));\n\n    if (GPU_N < GPU_COUNT) {\n        printf(\"No. of GPU on node %d\\n\", GPU_N);\n        printf(\"Two GPUs are required to run simpleCUFFT_2d_MGPU sample code\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int *major_minor         = (int *)malloc(sizeof(int) * GPU_N * 2);\n    int  found2IdenticalGPUs = 0;\n    int  nGPUs               = 2;\n    int *whichGPUs;\n    whichGPUs = (int *)malloc(sizeof(int) * nGPUs);\n\n    for (int i = 0; i < GPU_N; i++) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, i));\n        major_minor[i * 2]     = deviceProp.major;\n        major_minor[i * 2 + 1] = deviceProp.minor;\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               i,\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    for (int i = 0; i < GPU_N; i++) {\n        for (int j = i + 1; j < GPU_N; j++) {\n            if ((major_minor[i * 2] == major_minor[j * 2]) && (major_minor[i * 2 + 1] == major_minor[j * 2 + 1])) {\n                whichGPUs[0]        = i;\n                whichGPUs[1]        = j;\n                found2IdenticalGPUs = 1;\n                break;\n            }\n        }\n        if (found2IdenticalGPUs) {\n            break;\n        }\n    }\n\n    free(major_minor);\n    if (!found2IdenticalGPUs) {\n        printf(\"No Two GPUs with same architecture found\\nWaiving simpleCUFFT_2d_MGPU \"\n               \"sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    int    N    = 64;\n    float  xMAX = 1.0f, xMIN = 0.0f, yMIN = 0.0f, h = (xMAX - xMIN) / ((float)N), s = 0.1f, s2 = s * s;\n    float *x, *y, *f, *u_a, r2;\n\n    x   = (float *)malloc(sizeof(float) * N * N);\n    y   = (float *)malloc(sizeof(float) * N * N);\n    f   = (float *)malloc(sizeof(float) * N * N);\n    u_a = (float *)malloc(sizeof(float) * N * N);\n\n    for (int j = 0; j < N; j++)\n        for (int i = 0; i < N; i++) {\n            x[N * j + i] = xMIN + i * h;\n            y[N * j + i] = yMIN + j * h;\n            r2 = (x[N * j + i] - 0.5f) * (x[N * j + i] - 0.5f) + (y[N * j + i] - 0.5f) * (y[N * j + i] - 0.5f);\n            f[N * j + i]   = (r2 - 2 * s2) / (s2 * s2) * exp(-r2 / (2 * s2));\n            u_a[N * j + i] = exp(-r2 / (2 * s2)); // analytical solution\n        }\n\n    float *k, *d_k[GPU_COUNT];\n    k = (float *)malloc(sizeof(float) * N);\n    for (int i = 0; i <= N / 2; i++) {\n        k[i] = i * 2 * (float)M_PI;\n    }\n    for (int i = N / 2 + 1; i < N; i++) {\n        k[i] = (i - N) * 2 * (float)M_PI;\n    }\n\n    // Create a complex variable on host\n    Complex *h_f = (Complex *)malloc(sizeof(Complex) * N * N);\n\n    // Initialize the memory for the signal\n    for (int i = 0; i < (N * N); i++) {\n        h_f[i].x = f[i];\n        h_f[i].y = 0.0f;\n    }\n\n    // cufftCreate() - Create an empty plan\n    hipfftResult result;\n    hipfftHandle planComplex;\n    result = hipfftCreate(&planComplex);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"hipfftCreate failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtSetGPUs() - Define which GPUs to use\n    result = hipfftXtSetGPUs(planComplex, nGPUs, whichGPUs);\n\n    if (result == HIPFFT_INVALID_DEVICE) {\n        printf(\"This sample requires two GPUs on the same board.\\n\");\n        printf(\"No such board was found. Waiving sample.\\n\");\n        exit(EXIT_WAIVED);\n    }\n    else if (result != HIPFFT_SUCCESS) {\n        printf(\"hipfftXtSetGPUs failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Print the device information to run the code\n    printf(\"\\nRunning on GPUs\\n\");\n    for (int i = 0; i < 2; i++) {\n        hipDeviceProp_t deviceProp;\n        checkCudaErrors(hipGetDeviceProperties(&deviceProp, whichGPUs[i]));\n        printf(\"GPU Device %d: \\\"%s\\\" with compute capability %d.%d\\n\",\n               whichGPUs[i],\n               deviceProp.name,\n               deviceProp.major,\n               deviceProp.minor);\n    }\n\n    size_t *worksize;\n    worksize = (size_t *)malloc(sizeof(size_t) * nGPUs);\n\n    // cufftMakePlan2d() - Create the plan\n    result = hipfftMakePlan2d(planComplex, N, N, HIPFFT_C2C, worksize);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*MakePlan* failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    for (int i = 0; i < nGPUs; i++) {\n        hipSetDevice(whichGPUs[i]);\n        hipMalloc((void **)&d_k[i], sizeof(float) * N);\n        hipMemcpy(d_k[i], k, sizeof(float) * N, hipMemcpyHostToDevice);\n    }\n\n    // Create a variable on device\n    // d_f - variable on device to store the input data\n    // d_d_f - variable that store the natural order of d_f data\n    // d_out - device output\n    hipLibXtDesc *d_f, *d_d_f, *d_out;\n\n    // cufftXtMalloc() - Malloc data on multiple GPUs\n\n    result = hipfftXtMalloc(planComplex, (hipLibXtDesc **)&d_f, HIPFFT_XT_FORMAT_INPLACE);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    result = hipfftXtMalloc(planComplex, (hipLibXtDesc **)&d_d_f, HIPFFT_XT_FORMAT_INPLACE);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    result = hipfftXtMalloc(planComplex, (hipLibXtDesc **)&d_out, HIPFFT_XT_FORMAT_INPLACE);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMalloc failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtMemcpy() - Copy the data from host to device\n    result = hipfftXtMemcpy(planComplex, d_f, h_f, HIPFFT_COPY_HOST_TO_DEVICE);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtExecDescriptorC2C() - Execute FFT on data on multiple GPUs\n    printf(\"Forward 2d FFT on multiple GPUs\\n\");\n    result = hipfftXtExecDescriptorC2C(planComplex, d_f, d_f, HIPFFT_FORWARD);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtExecC2C  failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftXtMemcpy() - Copy the data to natural order on GPUs\n    result = hipfftXtMemcpy(planComplex, d_d_f, d_f, HIPFFT_COPY_DEVICE_TO_DEVICE);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Solve Poisson Equation\\n\");\n    solvePoissonEquation(d_d_f, d_out, d_k, N, nGPUs);\n\n    printf(\"Inverse 2d FFT on multiple GPUs\\n\");\n    // cufftXtExecDescriptorC2C() - Execute inverse  FFT on data on multiple GPUs\n    result = hipfftXtExecDescriptorC2C(planComplex, d_out, d_out, HIPFFT_BACKWARD);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtExecC2C  failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Create a variable on host to copy the data from device\n    // h_d_out - variable store the output of device\n    Complex *h_d_out = (Complex *)malloc(sizeof(Complex) * N * N);\n\n    // cufftXtMemcpy() - Copy data from multiple GPUs to host\n    result = hipfftXtMemcpy(planComplex, h_d_out, d_out, HIPFFT_COPY_DEVICE_TO_HOST);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtMemcpy failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    float *out      = (float *)malloc(sizeof(float) * N * N);\n    float  constant = h_d_out[0].x / N * N;\n    for (int i = 0; i < N * N; i++) {\n        // subtract u[0] to force the arbitrary constant to be 0\n        out[i] = (h_d_out[i].x / (N * N)) - constant;\n    }\n\n    // cleanup memory\n\n    free(h_f);\n    free(k);\n    free(out);\n    free(h_d_out);\n    free(x);\n    free(whichGPUs);\n    free(y);\n    free(f);\n    free(u_a);\n    free(worksize);\n\n    // cudaXtFree() - Free GPU memory\n    for (int i = 0; i < GPU_COUNT; i++) {\n        hipFree(d_k[i]);\n    }\n    result = hipfftXtFree(d_out);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n    result = hipfftXtFree(d_f);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n    result = hipfftXtFree(d_d_f);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"*XtFree failed\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // cufftDestroy() - Destroy FFT plan\n    result = hipfftDestroy(planComplex);\n    if (result != HIPFFT_SUCCESS) {\n        printf(\"hipfftDestroy failed: code %d\\n\", (int)result);\n        exit(EXIT_FAILURE);\n    }\n\n    exit(EXIT_SUCCESS);\n}\n\n////////////////////////////////////////////////////////////////////////////////////\n// Launch kernel on  multiple GPU\n///////////////////////////////////////////////////////////////////////////////////\nvoid solvePoissonEquation(hipLibXtDesc *d_ft, hipLibXtDesc *d_ft_k, float **k, int N, int nGPUs)\n{\n    int  device;\n    dim3 dimGrid(int(N / BSZ_X), int((N / 2) / BSZ_Y));\n    dim3 dimBlock(BSZ_X, BSZ_Y);\n\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_ft_k->descriptor->GPUs[i];\n        hipSetDevice(device);\n        solvePoisson<<<dimGrid, dimBlock>>>(\n            (hipfftComplex *)d_ft->descriptor->data[i], (hipfftComplex *)d_ft_k->descriptor->data[i], k[i], N, i, nGPUs);\n    }\n\n    // Wait for device to finish all operation\n    for (int i = 0; i < nGPUs; i++) {\n        device = d_ft_k->descriptor->GPUs[i];\n        hipSetDevice(device);\n        hipDeviceSynchronize();\n\n        // Check if kernel execution generated and error\n        getLastCudaError(\"Kernel execution failed [ solvePoisson ]\");\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Kernel for Solving Poisson equation on GPU\n////////////////////////////////////////////////////////////////////////////////\n__global__ void solvePoisson(hipfftComplex *ft, hipfftComplex *ft_k, float *k, int N, int gpu_id, int n_gpu)\n{\n    int i     = threadIdx.x + blockIdx.x * blockDim.x;\n    int j     = threadIdx.y + blockIdx.y * blockDim.y;\n    int index = j * N + i;\n    if (i < N && j < N / n_gpu) {\n        float k2 = k[i] * k[i] + k[j + gpu_id * N / n_gpu] * k[j + gpu_id * N / n_gpu];\n        if (i == 0 && j == 0 && gpu_id == 0) {\n            k2 = 1.0f;\n        }\n\n        ft_k[index].x = -ft[index].x * 1 / k2;\n        ft_k[index].y = -ft[index].y * 1 / k2;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/4_CUDA_Libraries/simpleCUFFT_2d_MGPU/simpleCUFFT_2d_MGPU.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"commonKernels.hpp\"\n\n__global__ void spinWhileLessThanOne(volatile unsigned int *latch)\n{\n    while (latch[0] < 1)\n        ;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"commonKernels.hpp\"\n\n__global__ void spinWhileLessThanOne(volatile unsigned int *latch)\n{\n    while (latch[0] < 1)\n        ;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/UnifiedMemoryPerf/commonKernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <helper_cuda.h>\n#include <helper_timer.h>\n\n#include \"commonDefs.hpp\"\n#include \"commonKernels.hpp\"\n\n#define VERIFY_GPU_CORRECTNESS 0\n\nsize_t maxSampleSizeInMb = 64;\nint    numKernelRuns     = 20;\nint    verboseResults    = 0;\n\nconst char *memAllocTypeStr[MEMALLOC_TYPE_COUNT] = {\"Managed_Memory_With_Hints\",\n                                                    \"Managed_Memory_With_Hints_FullyAsync\",\n                                                    \"Managed_Memory_NoHints\",\n                                                    \"Zero_Copy\",\n                                                    \"Memcpy_HostMalloc_DeviceCudaMalloc\",\n                                                    \"MemcpyAsync_HostMalloc_DeviceCudaMalloc\",\n                                                    \"Memcpy_HostCudaHostAlloc_DeviceCudaMalloc\",\n                                                    \"MemcpyAsync_HostCudaHostAlloc_DeviceCudaMalloc\"};\n\nconst char *memAllocTypeShortStr[MEMALLOC_TYPE_COUNT] = {\n    \"UMhint\",  // Managed Memory With Hints\n    \"UMhntAs\", // Managed Memory With_Hints Async\n    \"UMeasy\",  // Managed_Memory with No Hints\n    \"0Copy\",   // Zero Copy\n    \"MemCopy\", // USE HOST PAGEABLE AND DEVICE_MEMORY\n    \"CpAsync\", // USE HOST PAGEABLE AND DEVICE_MEMORY ASYNC\n    \"CpHpglk\", // USE HOST PAGELOCKED AND DEVICE MEMORY\n    \"CpPglAs\"  // USE HOST PAGELOCKED AND DEVICE MEMORY ASYNC\n};\n\nstatic float RandFloat(float low, float high)\n{\n    float t = (float)rand() / (float)RAND_MAX;\n    return (1.0f - t) * low + t * high;\n}\n\nvoid fillMatrixWithRandomValues(float *matrix, unsigned int matrixDim)\n{\n    unsigned int i, j;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            matrix[j + i * matrixDim] = RandFloat(0.0f, 10.0f);\n        }\n    }\n}\n\n#if VERIFY_GPU_CORRECTNESS\nvoid verifyMatrixMultiplyCorrectness(float *C, float *A, float *B, unsigned int matrixDim)\n{\n    unsigned int i, j, k, numErrors = 0;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            float result = 0.0f;\n            for (k = 0; k < matrixDim; ++k) {\n                result += A[k + i * matrixDim] * B[j + k * matrixDim];\n            }\n            if (fabs(C[j + i * matrixDim] - result) > 0.001 * matrixDim) {\n                printf(\"At [%u, %u]: Expected %f, Found %f\\n\", i, j, result, C[j + i * matrixDim]);\n                ++numErrors;\n            }\n        }\n    }\n    if (numErrors != 0) {\n        printf(\"%d value mismatches occured\\n\", numErrors);\n        fflush(stdout);\n        exit(EXIT_FAILURE); // exit since value mismatches occured\n    }\n}\n#endif\n\nvoid copyMatrix(float *dstMatrix, float *srcMatrix, unsigned int matrixDim)\n{\n    size_t size = matrixDim * matrixDim * sizeof(float);\n    memcpy(dstMatrix, srcMatrix, size);\n}\n\nvoid verifyMatrixData(float *expectedData, float *observedData, unsigned int matrixDim)\n{\n    unsigned int i, j, numErrors = 0;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            if (expectedData[j + i * matrixDim] != observedData[j + i * matrixDim]) {\n                ++numErrors;\n                if (verboseResults) {\n                    printf(\"At [%u, %u]: Expected %f, Found %f\\n\",\n                           i,\n                           j,\n                           expectedData[j + i * matrixDim],\n                           observedData[j + i * matrixDim]);\n                }\n            }\n        }\n    }\n    if (numErrors != 0) {\n        printf(\"%d value mismatches occured\\n\", numErrors);\n        fflush(stdout);\n        exit(EXIT_FAILURE); // exit since value mismatches occured\n    }\n}\n\n#define BLOCK_SIZE 32\n__global__ void matrixMultiplyKernel(float *C, float *A, float *B, unsigned int matrixDim)\n{\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    unsigned int wA = matrixDim;\n    unsigned int wB = matrixDim;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = matrixDim * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nvoid runMatrixMultiplyKernel(unsigned int matrixDim,\n                             int          allocType,\n                             unsigned int numLoops,\n                             double      *gpuLaunchCallsTimes,\n                             double      *gpuTransferToCallsTimes,\n                             double      *gpuTransferFromCallsTimes,\n                             double      *gpuLaunchAndTransferCallsTimes,\n                             double      *gpuLaunchTransferSyncTimes,\n                             double      *cpuAccessTimes,\n                             double      *overallTimes,\n                             int          device_id)\n{\n    float              *dptrA = NULL, *hptrA = NULL;\n    float              *dptrB = NULL, *hptrB = NULL;\n    float              *dptrC = NULL, *hptrC = NULL;\n    float              *randValuesX = NULL, *randValuesY = NULL;\n    float              *randValuesVerifyXmulY = NULL, *randValuesVerifyYmulX = NULL;\n    bool                copyRequired = false, hintsRequired = false;\n    bool                someTransferOpRequired;\n    bool                isAsync = false;\n    cudaStream_t        streamToRunOn;\n    unsigned int       *latch;\n    size_t              size = matrixDim * matrixDim * sizeof(float);\n    dim3                threads(32, 32);\n    dim3                grid(matrixDim / threads.x, matrixDim / threads.y);\n    StopWatchInterface *gpuLaunchCallsTimer = 0, *gpuTransferCallsTimer = 0;\n    StopWatchInterface *gpuSyncTimer = 0, *cpuAccessTimer = 0;\n    sdkCreateTimer(&gpuLaunchCallsTimer);\n    sdkCreateTimer(&gpuTransferCallsTimer);\n    sdkCreateTimer(&gpuSyncTimer);\n    sdkCreateTimer(&cpuAccessTimer);\n    unsigned int i;\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, device_id));\n    checkCudaErrors(cudaStreamCreate(&streamToRunOn));\n\n    randValuesX = (float *)malloc(size);\n    if (!randValuesX) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesY = (float *)malloc(size);\n    if (!randValuesY) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesVerifyXmulY = (float *)malloc(size);\n    if (!randValuesVerifyXmulY) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesVerifyYmulX = (float *)malloc(size);\n    if (!randValuesVerifyYmulX) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    checkCudaErrors(cudaMalloc(&dptrA, size));\n    checkCudaErrors(cudaMalloc(&dptrB, size));\n    checkCudaErrors(cudaMalloc(&dptrC, size));\n\n    fillMatrixWithRandomValues(randValuesX, matrixDim);\n    fillMatrixWithRandomValues(randValuesY, matrixDim);\n\n    checkCudaErrors(cudaMemcpyAsync(dptrA, randValuesX, size, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpyAsync(dptrB, randValuesY, size, cudaMemcpyHostToDevice));\n    matrixMultiplyKernel<<<grid, threads>>>(dptrC, dptrA, dptrB, matrixDim);\n    checkCudaErrors(cudaMemcpyAsync(randValuesVerifyXmulY, dptrC, size, cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaStreamSynchronize(NULL));\n    matrixMultiplyKernel<<<grid, threads>>>(dptrC, dptrB, dptrA, matrixDim);\n    checkCudaErrors(cudaMemcpyAsync(randValuesVerifyYmulX, dptrC, size, cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaStreamSynchronize(NULL));\n#if VERIFY_GPU_CORRECTNESS\n    verifyMatrixMultiplyCorrectness(randValuesVerifyXmulY, randValuesX, randValuesY, matrixDim);\n    verifyMatrixMultiplyCorrectness(randValuesVerifyYmulX, randValuesY, randValuesX, matrixDim);\n#endif\n    checkCudaErrors(cudaFree(dptrA));\n    checkCudaErrors(cudaFree(dptrB));\n    checkCudaErrors(cudaFree(dptrC));\n\n    checkCudaErrors(cudaMallocHost(&latch, sizeof(unsigned int)));\n\n    switch (allocType) {\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC:\n        hptrA = (float *)malloc(size);\n        if (!hptrA) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        hptrB = (float *)malloc(size);\n        if (!hptrB) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        hptrC = (float *)malloc(size);\n        if (!hptrC) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        checkCudaErrors(cudaMalloc(&dptrA, size));\n        checkCudaErrors(cudaMalloc(&dptrB, size));\n        checkCudaErrors(cudaMalloc(&dptrC, size));\n        copyRequired = true;\n        break;\n\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC:\n        checkCudaErrors(cudaMallocHost(&hptrA, size));\n        checkCudaErrors(cudaMallocHost(&hptrB, size));\n        checkCudaErrors(cudaMallocHost(&hptrC, size));\n        checkCudaErrors(cudaMalloc(&dptrA, size));\n        checkCudaErrors(cudaMalloc(&dptrB, size));\n        checkCudaErrors(cudaMalloc(&dptrC, size));\n        copyRequired = true;\n        break;\n\n    case USE_ZERO_COPY:\n        checkCudaErrors(cudaMallocHost(&hptrA, size));\n        checkCudaErrors(cudaMallocHost(&hptrB, size));\n        checkCudaErrors(cudaMallocHost(&hptrC, size));\n        checkCudaErrors(cudaHostGetDevicePointer(&dptrA, hptrA, 0));\n        checkCudaErrors(cudaHostGetDevicePointer(&dptrB, hptrB, 0));\n        checkCudaErrors(cudaHostGetDevicePointer(&dptrC, hptrC, 0));\n        break;\n\n    case USE_MANAGED_MEMORY:\n        checkCudaErrors(cudaMallocManaged(&dptrA, size));\n        checkCudaErrors(cudaMallocManaged(&dptrB, size));\n        checkCudaErrors(cudaMallocManaged(&dptrC, size));\n        hptrA = dptrA;\n        hptrB = dptrB;\n        hptrC = dptrC;\n        break;\n\n    case USE_MANAGED_MEMORY_WITH_HINTS:\n    case USE_MANAGED_MEMORY_WITH_HINTS_ASYNC:\n        if (deviceProp.concurrentManagedAccess) {\n            checkCudaErrors(cudaMallocManaged(&dptrA, size));\n            checkCudaErrors(cudaMallocManaged(&dptrB, size));\n            checkCudaErrors(cudaMallocManaged(&dptrC, size));\n            cudaMemLocation hostLoc;\n            hostLoc.type = cudaMemLocationTypeHost;\n            checkCudaErrors(cudaMemPrefetchAsync(dptrA, size, hostLoc, 0));\n            checkCudaErrors(cudaMemPrefetchAsync(dptrB, size, hostLoc, 0));\n            checkCudaErrors(cudaMemPrefetchAsync(dptrC, size, hostLoc, 0));\n        }\n        else {\n            checkCudaErrors(cudaMallocManaged(&dptrA, size, cudaMemAttachHost));\n            checkCudaErrors(cudaMallocManaged(&dptrB, size, cudaMemAttachHost));\n            checkCudaErrors(cudaMallocManaged(&dptrC, size, cudaMemAttachHost));\n        }\n        hptrA         = dptrA;\n        hptrB         = dptrB;\n        hptrC         = dptrC;\n        hintsRequired = true;\n        break;\n\n    default:\n        exit(EXIT_FAILURE); // exit with error\n    }\n\n    if (allocType == USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC\n        || allocType == USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC\n        || allocType == USE_MANAGED_MEMORY_WITH_HINTS_ASYNC) {\n        isAsync = true;\n    }\n\n    someTransferOpRequired = copyRequired || hintsRequired;\n\n    // fill buffers with 0 to avoid any first access page-fault overheads.\n    memset(hptrA, 0, size);\n    memset(hptrB, 0, size);\n    memset(hptrC, 0, size);\n\n    for (i = 0; i < numLoops; i++) {\n        cpuAccessTimes[i]            = 0.0;\n        gpuLaunchCallsTimes[i]       = 0.0;\n        gpuTransferToCallsTimes[i]   = 0.0;\n        gpuTransferFromCallsTimes[i] = 0.0;\n\n        sdkStartTimer(&cpuAccessTimer);\n        {\n            copyMatrix(hptrA, (i & 0x1 == 0) ? randValuesX : randValuesY, matrixDim);\n            copyMatrix(hptrB, (i & 0x1 == 0) ? randValuesY : randValuesX, matrixDim);\n        }\n        sdkStopTimer(&cpuAccessTimer);\n        cpuAccessTimes[i] += sdkGetAverageTimerValue(&cpuAccessTimer);\n        sdkResetTimer(&cpuAccessTimer);\n\n        if (isAsync && hintsRequired) {\n            *latch = 0;\n            // Prevent any work on stream from starting until all work is pushed\n            spinWhileLessThanOne<<<1, 1, 0, streamToRunOn>>>(latch);\n        }\n\n        if (someTransferOpRequired) {\n            sdkStartTimer(&gpuTransferCallsTimer);\n            if (copyRequired) {\n                if (isAsync) {\n                    checkCudaErrors(cudaMemcpyAsync(dptrA, hptrA, size, cudaMemcpyHostToDevice, streamToRunOn));\n                    checkCudaErrors(cudaMemcpyAsync(dptrB, hptrB, size, cudaMemcpyHostToDevice, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(cudaMemcpy(dptrA, hptrA, size, cudaMemcpyHostToDevice));\n                    checkCudaErrors(cudaMemcpy(dptrB, hptrB, size, cudaMemcpyHostToDevice));\n                }\n            }\n            if (hintsRequired) {\n                if (deviceProp.concurrentManagedAccess) {\n                    cudaMemLocation deviceLoc;\n                    deviceLoc.type = cudaMemLocationTypeDevice;\n                    deviceLoc.id   = device_id;\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrA, size, deviceLoc, 0, streamToRunOn));\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrB, size, deviceLoc, 0, streamToRunOn));\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrC, size, deviceLoc, 0, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrA, 0, cudaMemAttachGlobal));\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrB, 0, cudaMemAttachGlobal));\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrC, 0, cudaMemAttachGlobal));\n                }\n                if (!isAsync) {\n                    checkCudaErrors(cudaStreamSynchronize(streamToRunOn));\n                }\n            }\n\n            sdkStopTimer(&gpuTransferCallsTimer);\n            gpuTransferToCallsTimes[i] += sdkGetAverageTimerValue(&gpuTransferCallsTimer);\n            sdkResetTimer(&gpuTransferCallsTimer);\n        }\n\n        sdkStartTimer(&gpuLaunchCallsTimer);\n        {\n            matrixMultiplyKernel<<<grid, threads, 0, streamToRunOn>>>(dptrC, dptrA, dptrB, matrixDim);\n            if (!isAsync) {\n                checkCudaErrors(cudaStreamSynchronize(streamToRunOn));\n            }\n        }\n        sdkStopTimer(&gpuLaunchCallsTimer);\n\n        gpuLaunchCallsTimes[i] += sdkGetAverageTimerValue(&gpuLaunchCallsTimer);\n        sdkResetTimer(&gpuLaunchCallsTimer);\n\n        if (someTransferOpRequired) {\n            sdkStartTimer(&gpuTransferCallsTimer);\n            if (hintsRequired) {\n                if (deviceProp.concurrentManagedAccess) {\n                    cudaMemLocation hostLoc;\n                    hostLoc.type = cudaMemLocationTypeHost;\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrA, size, hostLoc, 0));\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrB, size, hostLoc, 0));\n                    checkCudaErrors(cudaMemPrefetchAsync(dptrC, size, hostLoc, 0));\n                }\n                else {\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrA, 0, cudaMemAttachHost));\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrB, 0, cudaMemAttachHost));\n                    checkCudaErrors(cudaStreamAttachMemAsync(streamToRunOn, dptrC, 0, cudaMemAttachHost));\n                }\n                if (!isAsync) {\n                    checkCudaErrors(cudaStreamSynchronize(streamToRunOn));\n                }\n            }\n            if (copyRequired) {\n                if (isAsync) {\n                    checkCudaErrors(cudaMemcpyAsync(hptrC, dptrC, size, cudaMemcpyDeviceToHost, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(cudaMemcpy(hptrC, dptrC, size, cudaMemcpyDeviceToHost));\n                }\n            }\n            sdkStopTimer(&gpuTransferCallsTimer);\n            gpuTransferFromCallsTimes[i] += sdkGetAverageTimerValue(&gpuTransferCallsTimer);\n            sdkResetTimer(&gpuTransferCallsTimer);\n        }\n        gpuLaunchAndTransferCallsTimes[i] =\n            gpuLaunchCallsTimes[i] + gpuTransferToCallsTimes[i] + gpuTransferFromCallsTimes[i];\n        gpuLaunchTransferSyncTimes[i] = gpuLaunchAndTransferCallsTimes[i];\n        if (isAsync) {\n            sdkStartTimer(&gpuSyncTimer);\n            {\n                if (hintsRequired) {\n                    *latch = 1;\n                }\n                checkCudaErrors(cudaStreamSynchronize(streamToRunOn));\n            }\n            sdkStopTimer(&gpuSyncTimer);\n            gpuLaunchTransferSyncTimes[i] += sdkGetAverageTimerValue(&gpuSyncTimer);\n            sdkResetTimer(&gpuSyncTimer);\n        }\n\n        sdkStartTimer(&cpuAccessTimer);\n        {\n            verifyMatrixData((i & 0x1 == 0) ? randValuesVerifyXmulY : randValuesVerifyYmulX, hptrC, matrixDim);\n        }\n        sdkStopTimer(&cpuAccessTimer);\n        cpuAccessTimes[i] += sdkGetAverageTimerValue(&cpuAccessTimer);\n        sdkResetTimer(&cpuAccessTimer);\n        overallTimes[i] = cpuAccessTimes[i] + gpuLaunchTransferSyncTimes[i];\n    }\n\n    switch (allocType) {\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC:\n        free(hptrA);\n        free(hptrB);\n        free(hptrC);\n        checkCudaErrors(cudaFree(dptrA));\n        checkCudaErrors(cudaFree(dptrB));\n        checkCudaErrors(cudaFree(dptrC));\n        break;\n\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC:\n        checkCudaErrors(cudaFreeHost(hptrA));\n        checkCudaErrors(cudaFreeHost(hptrB));\n        checkCudaErrors(cudaFreeHost(hptrC));\n        checkCudaErrors(cudaFree(dptrA));\n        checkCudaErrors(cudaFree(dptrB));\n        checkCudaErrors(cudaFree(dptrC));\n        break;\n\n    case USE_ZERO_COPY:\n        checkCudaErrors(cudaFreeHost(hptrA));\n        checkCudaErrors(cudaFreeHost(hptrB));\n        checkCudaErrors(cudaFreeHost(hptrC));\n        break;\n\n    case USE_MANAGED_MEMORY:\n    case USE_MANAGED_MEMORY_WITH_HINTS:\n    case USE_MANAGED_MEMORY_WITH_HINTS_ASYNC:\n        checkCudaErrors(cudaFree(dptrA));\n        checkCudaErrors(cudaFree(dptrB));\n        checkCudaErrors(cudaFree(dptrC));\n        break;\n\n    default:\n        exit(EXIT_FAILURE); // exit due to error\n    }\n\n    checkCudaErrors(cudaStreamDestroy(streamToRunOn));\n    checkCudaErrors(cudaFreeHost(latch));\n    free(randValuesX);\n    free(randValuesY);\n    free(randValuesVerifyXmulY);\n    free(randValuesVerifyYmulX);\n    sdkDeleteTimer(&gpuLaunchCallsTimer);\n    sdkDeleteTimer(&gpuTransferCallsTimer);\n    sdkDeleteTimer(&gpuSyncTimer);\n    sdkDeleteTimer(&cpuAccessTimer);\n}\n\nvoid matrixMultiplyPerfRunner(bool reportAsBandwidth,\n                              bool print_launch_transfer_results,\n                              bool print_std_deviation,\n                              int  device_id)\n{\n    int          i;\n    unsigned int minMatrixDim  = 32;\n    unsigned int multiplierDim = 2;\n    unsigned int matrixDim;\n    unsigned int minSize    = minMatrixDim * minMatrixDim * sizeof(float);\n    unsigned int maxSize    = (maxSampleSizeInMb * ONE_MB) / 4; // 3 buffers are used, but dividing by 4 (power of 2)\n    unsigned int multiplier = multiplierDim * multiplierDim;\n    unsigned int numSizesToTest;\n\n    struct testResults *results;\n    struct resultsData *gpuLaunchCallsTimes;\n    struct resultsData *gpuTransferToCallsTimes;\n    struct resultsData *gpuTransferFromCallsTimes;\n    struct resultsData *gpuLaunchAndTransferCallsTimes;\n    struct resultsData *gpuLaunchTransferSyncTimes;\n    struct resultsData *cpuAccessTimes;\n    struct resultsData *overallTimes;\n    unsigned long      *sizesToTest;\n    unsigned int        j;\n\n    numSizesToTest = findNumSizesToTest(minSize, maxSize, multiplier);\n\n    createAndInitTestResults(&results, \"matrixMultiplyPerf\", numKernelRuns, numSizesToTest);\n\n    sizesToTest = getPtrSizesToTest(results);\n\n    createResultDataAndAddToTestResults(\n        &gpuLaunchCallsTimes, results, \"GPU Kernel Launch Call Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuTransferToCallsTimes, results, \"CPU to GPU Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuTransferFromCallsTimes, results, \"GPU to CPU Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuLaunchAndTransferCallsTimes, results, \"GPU Launch and Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuLaunchTransferSyncTimes, results, \"GPU Launch Transfer and Sync Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(&cpuAccessTimes, results, \"CPU Access Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(&overallTimes, results, \"Overall Time\", false, reportAsBandwidth);\n\n    printf(\"Running \");\n    for (matrixDim = minMatrixDim, j = 0; matrixDim * matrixDim <= maxSize / sizeof(float);\n         matrixDim *= multiplierDim, ++j) {\n        sizesToTest[j] = matrixDim * matrixDim * sizeof(float);\n        for (i = MEMALLOC_TYPE_START; i <= MEMALLOC_TYPE_END; i++) {\n            printf(\".\");\n            fflush(stdout);\n            runMatrixMultiplyKernel(matrixDim,\n                                    i,\n                                    numKernelRuns,\n                                    getPtrRunTimesInMs(gpuLaunchCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuTransferToCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuTransferFromCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuLaunchAndTransferCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuLaunchTransferSyncTimes, i, j),\n                                    getPtrRunTimesInMs(cpuAccessTimes, i, j),\n                                    getPtrRunTimesInMs(overallTimes, i, j),\n                                    device_id);\n        }\n    }\n    printf(\"\\n\");\n    printResults(results, print_launch_transfer_results, print_std_deviation);\n    freeTestResultsAndAllResultsData(results);\n}\n\nstatic void usage()\n{\n    printf(\"./cudaMemoryTypesPerf [-device=<device_id>] [-reportAsBandwidth] \"\n           \"[-print-launch-transfer-results] [-print-std-deviation] [-verbose]\\n\");\n    printf(\"Options:\\n\");\n    printf(\"-reportAsBandwidth:             By default time taken is printed, this \"\n           \"option allows to instead print bandwidth.\\n\");\n    printf(\"-print-launch-transfer-results: By default overall results are printed, \"\n           \"this option allows to print data transfers and kernel time as well.\\n\");\n    printf(\"-print-std-deviation:           Prints std deviation of the results.\\n\");\n    printf(\"-kernel-iterations=<num>:       Number of times the kernel tests should \"\n           \"be run[default is 100 iterations].\\n\");\n    printf(\"-device=<device_id>:            Allows to pass GPU Device ID on which \"\n           \"the tests will be run.\\n\");\n    printf(\"-verbose:                       Prints highly verbose output.\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    bool reportAsBandwidth             = false;\n    bool print_launch_transfer_results = false;\n    bool print_std_deviation           = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        usage();\n        printf(\"&&&& %s WAIVED\\n\", argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"reportAsBandwidth\")) {\n        reportAsBandwidth = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"print-launch-transfer-results\")) {\n        print_launch_transfer_results = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"print-std-deviation\")) {\n        print_std_deviation = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel-iterations\")) {\n        numKernelRuns = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel-iterations\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"verbose\")) {\n        verboseResults = 1;\n    }\n\n    // set device\n    cudaDeviceProp device_prop;\n    int            device_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&device_prop, device_id));\n\n    if (!device_prop.managedMemory) {\n        // This samples requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n\n        exit(EXIT_WAIVED);\n    }\n\n    matrixMultiplyPerfRunner(reportAsBandwidth, print_launch_transfer_results, print_std_deviation, device_id);\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <helper_timer.h>\n\n#include \"commonDefs.hpp\"\n#include \"commonKernels.hpp\"\n\n#define VERIFY_GPU_CORRECTNESS 0\n\nsize_t maxSampleSizeInMb = 64;\nint    numKernelRuns     = 20;\nint    verboseResults    = 0;\n\nconst char *memAllocTypeStr[MEMALLOC_TYPE_COUNT] = {\"Managed_Memory_With_Hints\",\n                                                    \"Managed_Memory_With_Hints_FullyAsync\",\n                                                    \"Managed_Memory_NoHints\",\n                                                    \"Zero_Copy\",\n                                                    \"Memcpy_HostMalloc_DeviceCudaMalloc\",\n                                                    \"MemcpyAsync_HostMalloc_DeviceCudaMalloc\",\n                                                    \"Memcpy_HostCudaHostAlloc_DeviceCudaMalloc\",\n                                                    \"MemcpyAsync_HostCudaHostAlloc_DeviceCudaMalloc\"};\n\nconst char *memAllocTypeShortStr[MEMALLOC_TYPE_COUNT] = {\n    \"UMhint\",  // Managed Memory With Hints\n    \"UMhntAs\", // Managed Memory With_Hints Async\n    \"UMeasy\",  // Managed_Memory with No Hints\n    \"0Copy\",   // Zero Copy\n    \"MemCopy\", // USE HOST PAGEABLE AND DEVICE_MEMORY\n    \"CpAsync\", // USE HOST PAGEABLE AND DEVICE_MEMORY ASYNC\n    \"CpHpglk\", // USE HOST PAGELOCKED AND DEVICE MEMORY\n    \"CpPglAs\"  // USE HOST PAGELOCKED AND DEVICE MEMORY ASYNC\n};\n\nstatic float RandFloat(float low, float high)\n{\n    float t = (float)rand() / (float)RAND_MAX;\n    return (1.0f - t) * low + t * high;\n}\n\nvoid fillMatrixWithRandomValues(float *matrix, unsigned int matrixDim)\n{\n    unsigned int i, j;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            matrix[j + i * matrixDim] = RandFloat(0.0f, 10.0f);\n        }\n    }\n}\n\n#if VERIFY_GPU_CORRECTNESS\nvoid verifyMatrixMultiplyCorrectness(float *C, float *A, float *B, unsigned int matrixDim)\n{\n    unsigned int i, j, k, numErrors = 0;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            float result = 0.0f;\n            for (k = 0; k < matrixDim; ++k) {\n                result += A[k + i * matrixDim] * B[j + k * matrixDim];\n            }\n            if (fabs(C[j + i * matrixDim] - result) > 0.001 * matrixDim) {\n                printf(\"At [%u, %u]: Expected %f, Found %f\\n\", i, j, result, C[j + i * matrixDim]);\n                ++numErrors;\n            }\n        }\n    }\n    if (numErrors != 0) {\n        printf(\"%d value mismatches occured\\n\", numErrors);\n        fflush(stdout);\n        exit(EXIT_FAILURE); // exit since value mismatches occured\n    }\n}\n#endif\n\nvoid copyMatrix(float *dstMatrix, float *srcMatrix, unsigned int matrixDim)\n{\n    size_t size = matrixDim * matrixDim * sizeof(float);\n    memcpy(dstMatrix, srcMatrix, size);\n}\n\nvoid verifyMatrixData(float *expectedData, float *observedData, unsigned int matrixDim)\n{\n    unsigned int i, j, numErrors = 0;\n    for (i = 0; i < matrixDim; ++i) {\n        for (j = 0; j < matrixDim; ++j) {\n            if (expectedData[j + i * matrixDim] != observedData[j + i * matrixDim]) {\n                ++numErrors;\n                if (verboseResults) {\n                    printf(\"At [%u, %u]: Expected %f, Found %f\\n\",\n                           i,\n                           j,\n                           expectedData[j + i * matrixDim],\n                           observedData[j + i * matrixDim]);\n                }\n            }\n        }\n    }\n    if (numErrors != 0) {\n        printf(\"%d value mismatches occured\\n\", numErrors);\n        fflush(stdout);\n        exit(EXIT_FAILURE); // exit since value mismatches occured\n    }\n}\n\n#define BLOCK_SIZE 32\n__global__ void matrixMultiplyKernel(float *C, float *A, float *B, unsigned int matrixDim)\n{\n    // Block index\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    // Thread index\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    unsigned int wA = matrixDim;\n    unsigned int wB = matrixDim;\n\n    // Index of the first sub-matrix of A processed by the block\n    int aBegin = matrixDim * BLOCK_SIZE * by;\n\n    // Index of the last sub-matrix of A processed by the block\n    int aEnd = aBegin + wA - 1;\n\n    // Step size used to iterate through the sub-matrices of A\n    int aStep = BLOCK_SIZE;\n\n    // Index of the first sub-matrix of B processed by the block\n    int bBegin = BLOCK_SIZE * bx;\n\n    // Step size used to iterate through the sub-matrices of B\n    int bStep = BLOCK_SIZE * wB;\n\n    // Csub is used to store the element of the block sub-matrix\n    // that is computed by the thread\n    float Csub = 0;\n\n    // Loop over all the sub-matrices of A and B\n    // required to compute the block sub-matrix\n    for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) {\n        // Declaration of the shared memory array As used to\n        // store the sub-matrix of A\n        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Declaration of the shared memory array Bs used to\n        // store the sub-matrix of B\n        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];\n\n        // Load the matrices from device memory\n        // to shared memory; each thread loads\n        // one element of each matrix\n        As[ty][tx] = A[a + wA * ty + tx];\n        Bs[ty][tx] = B[b + wB * ty + tx];\n\n        // Synchronize to make sure the matrices are loaded\n        __syncthreads();\n\n        // Multiply the two matrices together;\n        // each thread computes one element\n        // of the block sub-matrix\n#pragma unroll\n\n        for (int k = 0; k < BLOCK_SIZE; ++k) {\n            Csub += As[ty][k] * Bs[k][tx];\n        }\n\n        // Synchronize to make sure that the preceding\n        // computation is done before loading two new\n        // sub-matrices of A and B in the next iteration\n        __syncthreads();\n    }\n\n    // Write the block sub-matrix to device memory;\n    // each thread writes one element\n    int c               = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;\n    C[c + wB * ty + tx] = Csub;\n}\n\nvoid runMatrixMultiplyKernel(unsigned int matrixDim,\n                             int          allocType,\n                             unsigned int numLoops,\n                             double      *gpuLaunchCallsTimes,\n                             double      *gpuTransferToCallsTimes,\n                             double      *gpuTransferFromCallsTimes,\n                             double      *gpuLaunchAndTransferCallsTimes,\n                             double      *gpuLaunchTransferSyncTimes,\n                             double      *cpuAccessTimes,\n                             double      *overallTimes,\n                             int          device_id)\n{\n    float              *dptrA = NULL, *hptrA = NULL;\n    float              *dptrB = NULL, *hptrB = NULL;\n    float              *dptrC = NULL, *hptrC = NULL;\n    float              *randValuesX = NULL, *randValuesY = NULL;\n    float              *randValuesVerifyXmulY = NULL, *randValuesVerifyYmulX = NULL;\n    bool                copyRequired = false, hintsRequired = false;\n    bool                someTransferOpRequired;\n    bool                isAsync = false;\n    hipStream_t        streamToRunOn;\n    unsigned int       *latch;\n    size_t              size = matrixDim * matrixDim * sizeof(float);\n    dim3                threads(32, 32);\n    dim3                grid(matrixDim / threads.x, matrixDim / threads.y);\n    StopWatchInterface *gpuLaunchCallsTimer = 0, *gpuTransferCallsTimer = 0;\n    StopWatchInterface *gpuSyncTimer = 0, *cpuAccessTimer = 0;\n    sdkCreateTimer(&gpuLaunchCallsTimer);\n    sdkCreateTimer(&gpuTransferCallsTimer);\n    sdkCreateTimer(&gpuSyncTimer);\n    sdkCreateTimer(&cpuAccessTimer);\n    unsigned int i;\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, device_id));\n    checkCudaErrors(hipStreamCreate(&streamToRunOn));\n\n    randValuesX = (float *)malloc(size);\n    if (!randValuesX) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesY = (float *)malloc(size);\n    if (!randValuesY) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesVerifyXmulY = (float *)malloc(size);\n    if (!randValuesVerifyXmulY) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    randValuesVerifyYmulX = (float *)malloc(size);\n    if (!randValuesVerifyYmulX) {\n        exit(EXIT_FAILURE); // exit since memory allocation error\n    }\n    checkCudaErrors(hipMalloc(&dptrA, size));\n    checkCudaErrors(hipMalloc(&dptrB, size));\n    checkCudaErrors(hipMalloc(&dptrC, size));\n\n    fillMatrixWithRandomValues(randValuesX, matrixDim);\n    fillMatrixWithRandomValues(randValuesY, matrixDim);\n\n    checkCudaErrors(hipMemcpyAsync(dptrA, randValuesX, size, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpyAsync(dptrB, randValuesY, size, hipMemcpyHostToDevice));\n    matrixMultiplyKernel<<<grid, threads>>>(dptrC, dptrA, dptrB, matrixDim);\n    checkCudaErrors(hipMemcpyAsync(randValuesVerifyXmulY, dptrC, size, hipMemcpyDeviceToHost));\n    checkCudaErrors(hipStreamSynchronize(NULL));\n    matrixMultiplyKernel<<<grid, threads>>>(dptrC, dptrB, dptrA, matrixDim);\n    checkCudaErrors(hipMemcpyAsync(randValuesVerifyYmulX, dptrC, size, hipMemcpyDeviceToHost));\n    checkCudaErrors(hipStreamSynchronize(NULL));\n#if VERIFY_GPU_CORRECTNESS\n    verifyMatrixMultiplyCorrectness(randValuesVerifyXmulY, randValuesX, randValuesY, matrixDim);\n    verifyMatrixMultiplyCorrectness(randValuesVerifyYmulX, randValuesY, randValuesX, matrixDim);\n#endif\n    checkCudaErrors(hipFree(dptrA));\n    checkCudaErrors(hipFree(dptrB));\n    checkCudaErrors(hipFree(dptrC));\n\n    checkCudaErrors(hipHostMalloc(&latch, sizeof(unsigned int)));\n\n    switch (allocType) {\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC:\n        hptrA = (float *)malloc(size);\n        if (!hptrA) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        hptrB = (float *)malloc(size);\n        if (!hptrB) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        hptrC = (float *)malloc(size);\n        if (!hptrC) {\n            exit(EXIT_FAILURE); // exit since memory allocation error\n        }\n        checkCudaErrors(hipMalloc(&dptrA, size));\n        checkCudaErrors(hipMalloc(&dptrB, size));\n        checkCudaErrors(hipMalloc(&dptrC, size));\n        copyRequired = true;\n        break;\n\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC:\n        checkCudaErrors(hipHostMalloc(&hptrA, size));\n        checkCudaErrors(hipHostMalloc(&hptrB, size));\n        checkCudaErrors(hipHostMalloc(&hptrC, size));\n        checkCudaErrors(hipMalloc(&dptrA, size));\n        checkCudaErrors(hipMalloc(&dptrB, size));\n        checkCudaErrors(hipMalloc(&dptrC, size));\n        copyRequired = true;\n        break;\n\n    case USE_ZERO_COPY:\n        checkCudaErrors(hipHostMalloc(&hptrA, size));\n        checkCudaErrors(hipHostMalloc(&hptrB, size));\n        checkCudaErrors(hipHostMalloc(&hptrC, size));\n        checkCudaErrors(hipHostGetDevicePointer(&dptrA, hptrA, 0));\n        checkCudaErrors(hipHostGetDevicePointer(&dptrB, hptrB, 0));\n        checkCudaErrors(hipHostGetDevicePointer(&dptrC, hptrC, 0));\n        break;\n\n    case USE_MANAGED_MEMORY:\n        checkCudaErrors(hipMallocManaged(&dptrA, size));\n        checkCudaErrors(hipMallocManaged(&dptrB, size));\n        checkCudaErrors(hipMallocManaged(&dptrC, size));\n        hptrA = dptrA;\n        hptrB = dptrB;\n        hptrC = dptrC;\n        break;\n\n    case USE_MANAGED_MEMORY_WITH_HINTS:\n    case USE_MANAGED_MEMORY_WITH_HINTS_ASYNC:\n        if (deviceProp.concurrentManagedAccess) {\n            checkCudaErrors(hipMallocManaged(&dptrA, size));\n            checkCudaErrors(hipMallocManaged(&dptrB, size));\n            checkCudaErrors(hipMallocManaged(&dptrC, size));\n            hipMemLocation hostLoc;\n            hostLoc.type = hipMemLocationTypeHost;\n            checkCudaErrors(hipMemPrefetchAsync(dptrA, size, hostLoc, 0));\n            checkCudaErrors(hipMemPrefetchAsync(dptrB, size, hostLoc, 0));\n            checkCudaErrors(hipMemPrefetchAsync(dptrC, size, hostLoc, 0));\n        }\n        else {\n            checkCudaErrors(hipMallocManaged(&dptrA, size, hipMemAttachHost));\n            checkCudaErrors(hipMallocManaged(&dptrB, size, hipMemAttachHost));\n            checkCudaErrors(hipMallocManaged(&dptrC, size, hipMemAttachHost));\n        }\n        hptrA         = dptrA;\n        hptrB         = dptrB;\n        hptrC         = dptrC;\n        hintsRequired = true;\n        break;\n\n    default:\n        exit(EXIT_FAILURE); // exit with error\n    }\n\n    if (allocType == USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC\n        || allocType == USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC\n        || allocType == USE_MANAGED_MEMORY_WITH_HINTS_ASYNC) {\n        isAsync = true;\n    }\n\n    someTransferOpRequired = copyRequired || hintsRequired;\n\n    // fill buffers with 0 to avoid any first access page-fault overheads.\n    memset(hptrA, 0, size);\n    memset(hptrB, 0, size);\n    memset(hptrC, 0, size);\n\n    for (i = 0; i < numLoops; i++) {\n        cpuAccessTimes[i]            = 0.0;\n        gpuLaunchCallsTimes[i]       = 0.0;\n        gpuTransferToCallsTimes[i]   = 0.0;\n        gpuTransferFromCallsTimes[i] = 0.0;\n\n        sdkStartTimer(&cpuAccessTimer);\n        {\n            copyMatrix(hptrA, (i & 0x1 == 0) ? randValuesX : randValuesY, matrixDim);\n            copyMatrix(hptrB, (i & 0x1 == 0) ? randValuesY : randValuesX, matrixDim);\n        }\n        sdkStopTimer(&cpuAccessTimer);\n        cpuAccessTimes[i] += sdkGetAverageTimerValue(&cpuAccessTimer);\n        sdkResetTimer(&cpuAccessTimer);\n\n        if (isAsync && hintsRequired) {\n            *latch = 0;\n            // Prevent any work on stream from starting until all work is pushed\n            spinWhileLessThanOne<<<1, 1, 0, streamToRunOn>>>(latch);\n        }\n\n        if (someTransferOpRequired) {\n            sdkStartTimer(&gpuTransferCallsTimer);\n            if (copyRequired) {\n                if (isAsync) {\n                    checkCudaErrors(hipMemcpyAsync(dptrA, hptrA, size, hipMemcpyHostToDevice, streamToRunOn));\n                    checkCudaErrors(hipMemcpyAsync(dptrB, hptrB, size, hipMemcpyHostToDevice, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(hipMemcpy(dptrA, hptrA, size, hipMemcpyHostToDevice));\n                    checkCudaErrors(hipMemcpy(dptrB, hptrB, size, hipMemcpyHostToDevice));\n                }\n            }\n            if (hintsRequired) {\n                if (deviceProp.concurrentManagedAccess) {\n                    hipMemLocation deviceLoc;\n                    deviceLoc.type = hipMemLocationTypeDevice;\n                    deviceLoc.id   = device_id;\n                    checkCudaErrors(hipMemPrefetchAsync(dptrA, size, deviceLoc, 0, streamToRunOn));\n                    checkCudaErrors(hipMemPrefetchAsync(dptrB, size, deviceLoc, 0, streamToRunOn));\n                    checkCudaErrors(hipMemPrefetchAsync(dptrC, size, deviceLoc, 0, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrA, 0, hipMemAttachGlobal));\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrB, 0, hipMemAttachGlobal));\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrC, 0, hipMemAttachGlobal));\n                }\n                if (!isAsync) {\n                    checkCudaErrors(hipStreamSynchronize(streamToRunOn));\n                }\n            }\n\n            sdkStopTimer(&gpuTransferCallsTimer);\n            gpuTransferToCallsTimes[i] += sdkGetAverageTimerValue(&gpuTransferCallsTimer);\n            sdkResetTimer(&gpuTransferCallsTimer);\n        }\n\n        sdkStartTimer(&gpuLaunchCallsTimer);\n        {\n            matrixMultiplyKernel<<<grid, threads, 0, streamToRunOn>>>(dptrC, dptrA, dptrB, matrixDim);\n            if (!isAsync) {\n                checkCudaErrors(hipStreamSynchronize(streamToRunOn));\n            }\n        }\n        sdkStopTimer(&gpuLaunchCallsTimer);\n\n        gpuLaunchCallsTimes[i] += sdkGetAverageTimerValue(&gpuLaunchCallsTimer);\n        sdkResetTimer(&gpuLaunchCallsTimer);\n\n        if (someTransferOpRequired) {\n            sdkStartTimer(&gpuTransferCallsTimer);\n            if (hintsRequired) {\n                if (deviceProp.concurrentManagedAccess) {\n                    hipMemLocation hostLoc;\n                    hostLoc.type = hipMemLocationTypeHost;\n                    checkCudaErrors(hipMemPrefetchAsync(dptrA, size, hostLoc, 0));\n                    checkCudaErrors(hipMemPrefetchAsync(dptrB, size, hostLoc, 0));\n                    checkCudaErrors(hipMemPrefetchAsync(dptrC, size, hostLoc, 0));\n                }\n                else {\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrA, 0, hipMemAttachHost));\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrB, 0, hipMemAttachHost));\n                    checkCudaErrors(hipStreamAttachMemAsync(streamToRunOn, dptrC, 0, hipMemAttachHost));\n                }\n                if (!isAsync) {\n                    checkCudaErrors(hipStreamSynchronize(streamToRunOn));\n                }\n            }\n            if (copyRequired) {\n                if (isAsync) {\n                    checkCudaErrors(hipMemcpyAsync(hptrC, dptrC, size, hipMemcpyDeviceToHost, streamToRunOn));\n                }\n                else {\n                    checkCudaErrors(hipMemcpy(hptrC, dptrC, size, hipMemcpyDeviceToHost));\n                }\n            }\n            sdkStopTimer(&gpuTransferCallsTimer);\n            gpuTransferFromCallsTimes[i] += sdkGetAverageTimerValue(&gpuTransferCallsTimer);\n            sdkResetTimer(&gpuTransferCallsTimer);\n        }\n        gpuLaunchAndTransferCallsTimes[i] =\n            gpuLaunchCallsTimes[i] + gpuTransferToCallsTimes[i] + gpuTransferFromCallsTimes[i];\n        gpuLaunchTransferSyncTimes[i] = gpuLaunchAndTransferCallsTimes[i];\n        if (isAsync) {\n            sdkStartTimer(&gpuSyncTimer);\n            {\n                if (hintsRequired) {\n                    *latch = 1;\n                }\n                checkCudaErrors(hipStreamSynchronize(streamToRunOn));\n            }\n            sdkStopTimer(&gpuSyncTimer);\n            gpuLaunchTransferSyncTimes[i] += sdkGetAverageTimerValue(&gpuSyncTimer);\n            sdkResetTimer(&gpuSyncTimer);\n        }\n\n        sdkStartTimer(&cpuAccessTimer);\n        {\n            verifyMatrixData((i & 0x1 == 0) ? randValuesVerifyXmulY : randValuesVerifyYmulX, hptrC, matrixDim);\n        }\n        sdkStopTimer(&cpuAccessTimer);\n        cpuAccessTimes[i] += sdkGetAverageTimerValue(&cpuAccessTimer);\n        sdkResetTimer(&cpuAccessTimer);\n        overallTimes[i] = cpuAccessTimes[i] + gpuLaunchTransferSyncTimes[i];\n    }\n\n    switch (allocType) {\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGEABLE_AND_DEVICE_MEMORY_ASYNC:\n        free(hptrA);\n        free(hptrB);\n        free(hptrC);\n        checkCudaErrors(hipFree(dptrA));\n        checkCudaErrors(hipFree(dptrB));\n        checkCudaErrors(hipFree(dptrC));\n        break;\n\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY:\n    case USE_HOST_PAGELOCKED_AND_DEVICE_MEMORY_ASYNC:\n        checkCudaErrors(hipHostFree(hptrA));\n        checkCudaErrors(hipHostFree(hptrB));\n        checkCudaErrors(hipHostFree(hptrC));\n        checkCudaErrors(hipFree(dptrA));\n        checkCudaErrors(hipFree(dptrB));\n        checkCudaErrors(hipFree(dptrC));\n        break;\n\n    case USE_ZERO_COPY:\n        checkCudaErrors(hipHostFree(hptrA));\n        checkCudaErrors(hipHostFree(hptrB));\n        checkCudaErrors(hipHostFree(hptrC));\n        break;\n\n    case USE_MANAGED_MEMORY:\n    case USE_MANAGED_MEMORY_WITH_HINTS:\n    case USE_MANAGED_MEMORY_WITH_HINTS_ASYNC:\n        checkCudaErrors(hipFree(dptrA));\n        checkCudaErrors(hipFree(dptrB));\n        checkCudaErrors(hipFree(dptrC));\n        break;\n\n    default:\n        exit(EXIT_FAILURE); // exit due to error\n    }\n\n    checkCudaErrors(hipStreamDestroy(streamToRunOn));\n    checkCudaErrors(hipHostFree(latch));\n    free(randValuesX);\n    free(randValuesY);\n    free(randValuesVerifyXmulY);\n    free(randValuesVerifyYmulX);\n    sdkDeleteTimer(&gpuLaunchCallsTimer);\n    sdkDeleteTimer(&gpuTransferCallsTimer);\n    sdkDeleteTimer(&gpuSyncTimer);\n    sdkDeleteTimer(&cpuAccessTimer);\n}\n\nvoid matrixMultiplyPerfRunner(bool reportAsBandwidth,\n                              bool print_launch_transfer_results,\n                              bool print_std_deviation,\n                              int  device_id)\n{\n    int          i;\n    unsigned int minMatrixDim  = 32;\n    unsigned int multiplierDim = 2;\n    unsigned int matrixDim;\n    unsigned int minSize    = minMatrixDim * minMatrixDim * sizeof(float);\n    unsigned int maxSize    = (maxSampleSizeInMb * ONE_MB) / 4; // 3 buffers are used, but dividing by 4 (power of 2)\n    unsigned int multiplier = multiplierDim * multiplierDim;\n    unsigned int numSizesToTest;\n\n    struct testResults *results;\n    struct resultsData *gpuLaunchCallsTimes;\n    struct resultsData *gpuTransferToCallsTimes;\n    struct resultsData *gpuTransferFromCallsTimes;\n    struct resultsData *gpuLaunchAndTransferCallsTimes;\n    struct resultsData *gpuLaunchTransferSyncTimes;\n    struct resultsData *cpuAccessTimes;\n    struct resultsData *overallTimes;\n    unsigned long      *sizesToTest;\n    unsigned int        j;\n\n    numSizesToTest = findNumSizesToTest(minSize, maxSize, multiplier);\n\n    createAndInitTestResults(&results, \"matrixMultiplyPerf\", numKernelRuns, numSizesToTest);\n\n    sizesToTest = getPtrSizesToTest(results);\n\n    createResultDataAndAddToTestResults(\n        &gpuLaunchCallsTimes, results, \"GPU Kernel Launch Call Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuTransferToCallsTimes, results, \"CPU to GPU Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuTransferFromCallsTimes, results, \"GPU to CPU Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuLaunchAndTransferCallsTimes, results, \"GPU Launch and Transfer Calls Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(\n        &gpuLaunchTransferSyncTimes, results, \"GPU Launch Transfer and Sync Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(&cpuAccessTimes, results, \"CPU Access Time\", false, reportAsBandwidth);\n    createResultDataAndAddToTestResults(&overallTimes, results, \"Overall Time\", false, reportAsBandwidth);\n\n    printf(\"Running \");\n    for (matrixDim = minMatrixDim, j = 0; matrixDim * matrixDim <= maxSize / sizeof(float);\n         matrixDim *= multiplierDim, ++j) {\n        sizesToTest[j] = matrixDim * matrixDim * sizeof(float);\n        for (i = MEMALLOC_TYPE_START; i <= MEMALLOC_TYPE_END; i++) {\n            printf(\".\");\n            fflush(stdout);\n            runMatrixMultiplyKernel(matrixDim,\n                                    i,\n                                    numKernelRuns,\n                                    getPtrRunTimesInMs(gpuLaunchCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuTransferToCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuTransferFromCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuLaunchAndTransferCallsTimes, i, j),\n                                    getPtrRunTimesInMs(gpuLaunchTransferSyncTimes, i, j),\n                                    getPtrRunTimesInMs(cpuAccessTimes, i, j),\n                                    getPtrRunTimesInMs(overallTimes, i, j),\n                                    device_id);\n        }\n    }\n    printf(\"\\n\");\n    printResults(results, print_launch_transfer_results, print_std_deviation);\n    freeTestResultsAndAllResultsData(results);\n}\n\nstatic void usage()\n{\n    printf(\"./cudaMemoryTypesPerf [-device=<device_id>] [-reportAsBandwidth] \"\n           \"[-print-launch-transfer-results] [-print-std-deviation] [-verbose]\\n\");\n    printf(\"Options:\\n\");\n    printf(\"-reportAsBandwidth:             By default time taken is printed, this \"\n           \"option allows to instead print bandwidth.\\n\");\n    printf(\"-print-launch-transfer-results: By default overall results are printed, \"\n           \"this option allows to print data transfers and kernel time as well.\\n\");\n    printf(\"-print-std-deviation:           Prints std deviation of the results.\\n\");\n    printf(\"-kernel-iterations=<num>:       Number of times the kernel tests should \"\n           \"be run[default is 100 iterations].\\n\");\n    printf(\"-device=<device_id>:            Allows to pass GPU Device ID on which \"\n           \"the tests will be run.\\n\");\n    printf(\"-verbose:                       Prints highly verbose output.\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    bool reportAsBandwidth             = false;\n    bool print_launch_transfer_results = false;\n    bool print_std_deviation           = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\") || checkCmdLineFlag(argc, (const char **)argv, \"h\")) {\n        usage();\n        printf(\"&&&& %s WAIVED\\n\", argv[0]);\n        exit(EXIT_WAIVED);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"reportAsBandwidth\")) {\n        reportAsBandwidth = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"print-launch-transfer-results\")) {\n        print_launch_transfer_results = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"print-std-deviation\")) {\n        print_std_deviation = true;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"kernel-iterations\")) {\n        numKernelRuns = getCmdLineArgumentInt(argc, (const char **)argv, \"kernel-iterations\");\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"verbose\")) {\n        verboseResults = 1;\n    }\n\n    // set device\n    hipDeviceProp_t device_prop;\n    int            device_id = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&device_prop, device_id));\n\n    if (!device_prop.managedMemory) {\n        // This samples requires being run on a device that supports Unified Memory\n        fprintf(stderr, \"Unified Memory not supported on this device\\n\");\n\n        exit(EXIT_WAIVED);\n    }\n\n    matrixMultiplyPerfRunner(reportAsBandwidth, print_launch_transfer_results, print_std_deviation, device_id);\n\n    printf(\"\\nNOTE: The CUDA Samples are not meant for performance measurements. \"\n           \"Results may vary when GPU Boost is enabled.\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/UnifiedMemoryPerf/matrixMultiplyPerf.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// -----------------------------------------------------------------------------\n// Transpose\n//\n// This file contains both device and host code for transposing a floating-point\n// matrix.  It performs several transpose kernels, which incrementally improve\n// performance through coalescing, removing shared memory bank conflicts, and\n// eliminating partition camping.  Several of the kernels perform a copy, used\n// to represent the best case performance that a transpose can achieve.\n//\n// Please see the whitepaper in the docs folder of the transpose project for a\n// detailed description of this performance study.\n// -----------------------------------------------------------------------------\n\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n// Utilities and system includes\n#include <helper_cuda.h>   // helper for cuda error checking functions\n#include <helper_image.h>  // helper for image and data comparison\n#include <helper_string.h> // helper for string parsing\n\nconst char *sSDKsample = \"Transpose\";\n\n// Each block transposes/copies a tile of TILE_DIM x TILE_DIM elements\n// using TILE_DIM x BLOCK_ROWS threads, so that each thread transposes\n// TILE_DIM/BLOCK_ROWS elements.  TILE_DIM must be an integral multiple of\n// BLOCK_ROWS\n\n#define TILE_DIM   32\n#define BLOCK_ROWS 16\n\n// This sample assumes that MATRIX_SIZE_X = MATRIX_SIZE_Y\nint MATRIX_SIZE_X = 1024;\nint MATRIX_SIZE_Y = 1024;\nint MUL_FACTOR    = TILE_DIM;\n\n#define FLOOR(a, b) (a - (a % b))\n\n// Compute the tile size necessary to illustrate performance cases for SM20+\n// hardware\nint MAX_TILES = (FLOOR(MATRIX_SIZE_X, 512) * FLOOR(MATRIX_SIZE_Y, 512)) / (TILE_DIM * TILE_DIM);\n\n// Number of repetitions used for timing.  Two sets of repetitions are\n// performed: 1) over kernel launches and 2) inside the kernel over just the\n// loads and stores\n\n#define NUM_REPS 100\n\n// -------------------------------------------------------\n// Copies\n// width and height must be integral multiples of TILE_DIM\n// -------------------------------------------------------\n\n__global__ void copy(float *odata, float *idata, int width, int height)\n{\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index = xIndex + width * yIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index + i * width] = idata[index + i * width];\n    }\n}\n\n__global__ void copySharedMem(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM];\n\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index = xIndex + width * yIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        if (xIndex < width && yIndex < height) {\n            tile[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n            tile[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n        }\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        if (xIndex < height && yIndex < width) {\n            odata[index + i * width] = tile[threadIdx.y + i][threadIdx.x];\n            odata[index + i * width] = tile[threadIdx.y + i][threadIdx.x];\n        }\n    }\n}\n\n// -------------------------------------------------------\n// Transposes\n// width and height must be integral multiples of TILE_DIM\n// -------------------------------------------------------\n\n__global__ void transposeNaive(float *odata, float *idata, int width, int height)\n{\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index_in  = xIndex + width * yIndex;\n    int index_out = yIndex + height * xIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i] = idata[index_in + i * width];\n    }\n}\n\n// coalesced transpose (with bank conflicts)\n\n__global__ void transposeCoalesced(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// Coalesced transpose with no bank conflicts\n\n__global__ void transposeNoBankConflicts(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// Transpose that effectively reorders execution of thread blocks along\n// diagonals of the matrix (also coalesced and has no bank conflicts)\n//\n// Here blockIdx.x is interpreted as the distance along a diagonal and\n// blockIdx.y as corresponding to different diagonals\n//\n// blockIdx_x and blockIdx_y expressions map the diagonal coordinates to the\n// more commonly used cartesian coordinates so that the only changes to the code\n// from the coalesced version are the calculation of the blockIdx_x and\n// blockIdx_y and replacement of blockIdx.x and bloclIdx.y with the subscripted\n// versions in the remaining code\n\n__global__ void transposeDiagonal(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM + 1];\n\n    int blockIdx_x, blockIdx_y;\n\n    // do diagonal reordering\n    if (width == height) {\n        blockIdx_y = blockIdx.x;\n        blockIdx_x = (blockIdx.x + blockIdx.y) % gridDim.x;\n    }\n    else {\n        int bid    = blockIdx.x + gridDim.x * blockIdx.y;\n        blockIdx_y = bid % gridDim.y;\n        blockIdx_x = ((bid / gridDim.y) + blockIdx_y) % gridDim.x;\n    }\n\n    // from here on the code is same as previous kernel except blockIdx_x replaces\n    // blockIdx.x and similarly for y\n\n    int xIndex   = blockIdx_x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx_y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx_y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx_x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// --------------------------------------------------------------------\n// Partial transposes\n// NB: the coarse- and fine-grained routines only perform part of a\n//     transpose and will fail the test against the reference solution\n//\n//     They are used to assess performance characteristics of different\n//     components of a full transpose\n// --------------------------------------------------------------------\n\n__global__ void transposeFineGrained(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float block[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index  = xIndex + (yIndex)*width;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        block[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index + i * height] = block[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n__global__ void transposeCoarseGrained(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float block[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        block[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = block[threadIdx.y + i][threadIdx.x];\n    }\n}\n\n// ---------------------\n// host utility routines\n// ---------------------\n\nvoid computeTransposeGold(float *gold, float *idata, const int size_x, const int size_y)\n{\n    for (int y = 0; y < size_y; ++y) {\n        for (int x = 0; x < size_x; ++x) {\n            gold[(x * size_y) + y] = idata[(y * size_x) + x];\n        }\n    }\n}\n\nvoid getParams(int argc, char **argv, cudaDeviceProp &deviceProp, int &size_x, int &size_y, int max_tile_dim)\n{\n    // set matrix size (if (x,y) dim of matrix is not square, then this will have\n    // to be modified\n    if (checkCmdLineFlag(argc, (const char **)argv, \"dimX\")) {\n        size_x = getCmdLineArgumentInt(argc, (const char **)argv, \"dimX\");\n\n        if (size_x > max_tile_dim) {\n            printf(\"> MatrixSize X = %d is greater than the recommended size = %d\\n\", size_x, max_tile_dim);\n        }\n        else {\n            printf(\"> MatrixSize X = %d\\n\", size_x);\n        }\n    }\n    else {\n        size_x = max_tile_dim;\n        size_x = FLOOR(size_x, 512);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"dimY\")) {\n        size_y = getCmdLineArgumentInt(argc, (const char **)argv, \"dimY\");\n\n        if (size_y > max_tile_dim) {\n            printf(\"> MatrixSize Y = %d is greater than the recommended size = %d\\n\", size_y, max_tile_dim);\n        }\n        else {\n            printf(\"> MatrixSize Y = %d\\n\", size_y);\n        }\n    }\n    else {\n        size_y = max_tile_dim;\n        size_y = FLOOR(size_y, 512);\n    }\n}\n\nvoid showHelp()\n{\n    printf(\"\\n%s : Command line options\\n\", sSDKsample);\n    printf(\"\\t-device=n          (where n=0,1,2.... for the GPU device)\\n\\n\");\n    printf(\"> The default matrix size can be overridden with these parameters\\n\");\n    printf(\"\\t-dimX=row_dim_size (matrix row    dimensions)\\n\");\n    printf(\"\\t-dimY=col_dim_size (matrix column dimensions)\\n\");\n}\n\n// ----\n// main\n// ----\n\nint main(int argc, char **argv)\n{\n    // Start logs\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        showHelp();\n        return 0;\n    }\n\n    int            devID = findCudaDevice(argc, (const char **)argv);\n    cudaDeviceProp deviceProp;\n\n    // get number of SMs on this GPU\n    checkCudaErrors(cudaGetDevice(&devID));\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n\n    // compute the scaling factor (for GPUs with fewer MPs)\n    float scale_factor, total_tiles;\n    scale_factor = max(\n        (192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n        1.0f);\n\n    printf(\"> Device %d: \\\"%s\\\"\\n\", devID, deviceProp.name);\n    printf(\"> SM Capability %d.%d detected:\\n\", deviceProp.major, deviceProp.minor);\n\n    // Calculate number of tiles we will run for the Matrix Transpose performance\n    // tests\n    int size_x, size_y, max_matrix_dim, matrix_size_test;\n\n    matrix_size_test = 512; // we round down max_matrix_dim for this perf test\n    total_tiles      = (float)MAX_TILES / scale_factor;\n\n    max_matrix_dim = FLOOR((int)(floor(sqrt(total_tiles)) * TILE_DIM), matrix_size_test);\n\n    // This is the minimum size allowed\n    if (max_matrix_dim == 0) {\n        max_matrix_dim = matrix_size_test;\n    }\n\n    printf(\"> [%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    printf(\"> Compute performance scaling factor = %4.2f\\n\", scale_factor);\n\n    // Extract parameters if there are any, command line -dimx and -dimy can\n    // override any of these settings\n    getParams(argc, argv, deviceProp, size_x, size_y, max_matrix_dim);\n\n    if (size_x != size_y) {\n        printf(\"\\n[%s] does not support non-square matrices (row_dim_size(%d) != \"\n               \"col_dim_size(%d))\\nExiting...\\n\\n\",\n               sSDKsample,\n               size_x,\n               size_y);\n        exit(EXIT_FAILURE);\n    }\n\n    if (size_x % TILE_DIM != 0 || size_y % TILE_DIM != 0) {\n        printf(\"[%s] Matrix size must be integral multiple of tile \"\n               \"size\\nExiting...\\n\\n\",\n               sSDKsample);\n        exit(EXIT_FAILURE);\n    }\n\n    // kernel pointer and descriptor\n    void (*kernel)(float *, float *, int, int);\n    const char *kernelName;\n\n    // execution configuration parameters\n    dim3 grid(size_x / TILE_DIM, size_y / TILE_DIM), threads(TILE_DIM, BLOCK_ROWS);\n\n    if (grid.x < 1 || grid.y < 1) {\n        printf(\"[%s] grid size computation incorrect in test \\nExiting...\\n\\n\", sSDKsample);\n        exit(EXIT_FAILURE);\n    }\n\n    // CUDA events\n    cudaEvent_t start, stop;\n\n    // size of memory required to store the matrix\n    size_t mem_size = static_cast<size_t>(sizeof(float) * size_x * size_y);\n\n    if (2 * mem_size > deviceProp.totalGlobalMem) {\n        printf(\"Input matrix size is larger than the available device memory!\\n\");\n        printf(\"Please choose a smaller size matrix\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // allocate host memory\n    float *h_idata       = (float *)malloc(mem_size);\n    float *h_odata       = (float *)malloc(mem_size);\n    float *transposeGold = (float *)malloc(mem_size);\n    float *gold;\n\n    // allocate device memory\n    float *d_idata, *d_odata;\n    checkCudaErrors(cudaMalloc((void **)&d_idata, mem_size));\n    checkCudaErrors(cudaMalloc((void **)&d_odata, mem_size));\n\n    // initialize host data\n    for (int i = 0; i < (size_x * size_y); ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // copy host data to device\n    checkCudaErrors(cudaMemcpy(d_idata, h_idata, mem_size, cudaMemcpyHostToDevice));\n\n    // Compute reference transpose solution\n    computeTransposeGold(transposeGold, h_idata, size_x, size_y);\n\n    // print out common data for all kernels\n    printf(\"\\nMatrix size: %dx%d (%dx%d tiles), tile size: %dx%d, block size: \"\n           \"%dx%d\\n\\n\",\n           size_x,\n           size_y,\n           size_x / TILE_DIM,\n           size_y / TILE_DIM,\n           TILE_DIM,\n           TILE_DIM,\n           TILE_DIM,\n           BLOCK_ROWS);\n\n    // initialize events\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n\n    //\n    // loop over different kernels\n    //\n\n    bool success = true;\n\n    for (int k = 0; k < 8; k++) {\n        // set kernel pointer\n        switch (k) {\n        case 0:\n            kernel     = &copy;\n            kernelName = \"simple copy       \";\n            break;\n\n        case 1:\n            kernel     = &copySharedMem;\n            kernelName = \"shared memory copy\";\n            break;\n\n        case 2:\n            kernel     = &transposeNaive;\n            kernelName = \"naive             \";\n            break;\n\n        case 3:\n            kernel     = &transposeCoalesced;\n            kernelName = \"coalesced         \";\n            break;\n\n        case 4:\n            kernel     = &transposeNoBankConflicts;\n            kernelName = \"optimized         \";\n            break;\n\n        case 5:\n            kernel     = &transposeCoarseGrained;\n            kernelName = \"coarse-grained    \";\n            break;\n\n        case 6:\n            kernel     = &transposeFineGrained;\n            kernelName = \"fine-grained      \";\n            break;\n\n        case 7:\n            kernel     = &transposeDiagonal;\n            kernelName = \"diagonal          \";\n            break;\n        }\n\n        // set reference solution\n        if (kernel == &copy || kernel == &copySharedMem) {\n            gold = h_idata;\n        }\n        else if (kernel == &transposeCoarseGrained || kernel == &transposeFineGrained) {\n            gold = h_odata; // fine- and coarse-grained kernels are not full\n                            // transposes, so bypass check\n        }\n        else {\n            gold = transposeGold;\n        }\n\n        // Clear error status\n        checkCudaErrors(cudaGetLastError());\n\n        // warmup to avoid timing startup\n        kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);\n\n        // take measurements for loop over kernel launches\n        checkCudaErrors(cudaEventRecord(start, 0));\n\n        for (int i = 0; i < NUM_REPS; i++) {\n            kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);\n            // Ensure no launch failure\n            checkCudaErrors(cudaGetLastError());\n        }\n\n        checkCudaErrors(cudaEventRecord(stop, 0));\n        checkCudaErrors(cudaEventSynchronize(stop));\n        float kernelTime;\n        checkCudaErrors(cudaEventElapsedTime(&kernelTime, start, stop));\n\n        checkCudaErrors(cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));\n        bool res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);\n\n        if (res == false) {\n            printf(\"*** %s kernel FAILED ***\\n\", kernelName);\n            success = false;\n        }\n\n        // take measurements for loop inside kernel\n        checkCudaErrors(cudaMemcpy(h_odata, d_odata, mem_size, cudaMemcpyDeviceToHost));\n        res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);\n\n        if (res == false) {\n            printf(\"*** %s kernel FAILED ***\\n\", kernelName);\n            success = false;\n        }\n\n        // report effective bandwidths\n        float kernelBandwidth = 2.0f * 1000.0f * mem_size / (1024 * 1024 * 1024) / (kernelTime / NUM_REPS);\n        printf(\"transpose %s, Throughput = %.4f GB/s, Time = %.5f ms, Size = %u fp32 \"\n               \"elements, NumDevsUsed = %u, Workgroup = %u\\n\",\n               kernelName,\n               kernelBandwidth,\n               kernelTime / NUM_REPS,\n               (size_x * size_y),\n               1,\n               TILE_DIM * BLOCK_ROWS);\n\n        // Reset d_odata to zero before starting the next loop iteration to avoid\n        // carrying over results from previous kernels. Without this reset, residual\n        // data from a prior kernel (e.g., 'copy') could make a subsequent\n        // kernel (e.g., 'copySharedMem') appear correct even if it performs no work,\n        // leading to false positives in compareData.\n        for (int i = 0; i < (size_x * size_y); ++i) {\n            h_odata[i] = 0;\n        }\n        // copy host data to device\n        checkCudaErrors(cudaMemcpy(d_odata, h_odata, mem_size, cudaMemcpyHostToDevice));\n\n        // Reset d_odata to zero before starting the next loop iteration to avoid\n        // carrying over results from previous kernels. Without this reset, residual\n        // data from a prior kernel (e.g., 'copy') could make a subsequent\n        // kernel (e.g., 'copySharedMem') appear correct even if it performs no work,\n        // leading to false positives in compareData.\n        for (int i = 0; i < (size_x * size_y); ++i) {\n            h_odata[i] = 0;\n        }\n        // copy host data to device\n        checkCudaErrors(cudaMemcpy(d_odata, h_odata, mem_size, cudaMemcpyHostToDevice));\n    }\n\n    // cleanup\n    free(h_idata);\n    free(h_odata);\n    free(transposeGold);\n    cudaFree(d_idata);\n    cudaFree(d_odata);\n\n    checkCudaErrors(cudaEventDestroy(start));\n    checkCudaErrors(cudaEventDestroy(stop));\n\n    if (!success) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// -----------------------------------------------------------------------------\n// Transpose\n//\n// This file contains both device and host code for transposing a floating-point\n// matrix.  It performs several transpose kernels, which incrementally improve\n// performance through coalescing, removing shared memory bank conflicts, and\n// eliminating partition camping.  Several of the kernels perform a copy, used\n// to represent the best case performance that a transpose can achieve.\n//\n// Please see the whitepaper in the docs folder of the transpose project for a\n// detailed description of this performance study.\n// -----------------------------------------------------------------------------\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n// Utilities and system includes\n#include <hip/hip_runtime_api.h>   // helper for cuda error checking functions\n#include <helper_image.h>  // helper for image and data comparison\n#include <helper_string.h> // helper for string parsing\n\nconst char *sSDKsample = \"Transpose\";\n\n// Each block transposes/copies a tile of TILE_DIM x TILE_DIM elements\n// using TILE_DIM x BLOCK_ROWS threads, so that each thread transposes\n// TILE_DIM/BLOCK_ROWS elements.  TILE_DIM must be an integral multiple of\n// BLOCK_ROWS\n\n#define TILE_DIM   32\n#define BLOCK_ROWS 16\n\n// This sample assumes that MATRIX_SIZE_X = MATRIX_SIZE_Y\nint MATRIX_SIZE_X = 1024;\nint MATRIX_SIZE_Y = 1024;\nint MUL_FACTOR    = TILE_DIM;\n\n#define FLOOR(a, b) (a - (a % b))\n\n// Compute the tile size necessary to illustrate performance cases for SM20+\n// hardware\nint MAX_TILES = (FLOOR(MATRIX_SIZE_X, 512) * FLOOR(MATRIX_SIZE_Y, 512)) / (TILE_DIM * TILE_DIM);\n\n// Number of repetitions used for timing.  Two sets of repetitions are\n// performed: 1) over kernel launches and 2) inside the kernel over just the\n// loads and stores\n\n#define NUM_REPS 100\n\n// -------------------------------------------------------\n// Copies\n// width and height must be integral multiples of TILE_DIM\n// -------------------------------------------------------\n\n__global__ void copy(float *odata, float *idata, int width, int height)\n{\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index = xIndex + width * yIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index + i * width] = idata[index + i * width];\n    }\n}\n\n__global__ void copySharedMem(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM];\n\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index = xIndex + width * yIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        if (xIndex < width && yIndex < height) {\n            tile[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n            tile[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n        }\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        if (xIndex < height && yIndex < width) {\n            odata[index + i * width] = tile[threadIdx.y + i][threadIdx.x];\n            odata[index + i * width] = tile[threadIdx.y + i][threadIdx.x];\n        }\n    }\n}\n\n// -------------------------------------------------------\n// Transposes\n// width and height must be integral multiples of TILE_DIM\n// -------------------------------------------------------\n\n__global__ void transposeNaive(float *odata, float *idata, int width, int height)\n{\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n\n    int index_in  = xIndex + width * yIndex;\n    int index_out = yIndex + height * xIndex;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i] = idata[index_in + i * width];\n    }\n}\n\n// coalesced transpose (with bank conflicts)\n\n__global__ void transposeCoalesced(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// Coalesced transpose with no bank conflicts\n\n__global__ void transposeNoBankConflicts(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// Transpose that effectively reorders execution of thread blocks along\n// diagonals of the matrix (also coalesced and has no bank conflicts)\n//\n// Here blockIdx.x is interpreted as the distance along a diagonal and\n// blockIdx.y as corresponding to different diagonals\n//\n// blockIdx_x and blockIdx_y expressions map the diagonal coordinates to the\n// more commonly used cartesian coordinates so that the only changes to the code\n// from the coalesced version are the calculation of the blockIdx_x and\n// blockIdx_y and replacement of blockIdx.x and bloclIdx.y with the subscripted\n// versions in the remaining code\n\n__global__ void transposeDiagonal(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float tile[TILE_DIM][TILE_DIM + 1];\n\n    int blockIdx_x, blockIdx_y;\n\n    // do diagonal reordering\n    if (width == height) {\n        blockIdx_y = blockIdx.x;\n        blockIdx_x = (blockIdx.x + blockIdx.y) % gridDim.x;\n    }\n    else {\n        int bid    = blockIdx.x + gridDim.x * blockIdx.y;\n        blockIdx_y = bid % gridDim.y;\n        blockIdx_x = ((bid / gridDim.y) + blockIdx_y) % gridDim.x;\n    }\n\n    // from here on the code is same as previous kernel except blockIdx_x replaces\n    // blockIdx.x and similarly for y\n\n    int xIndex   = blockIdx_x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx_y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx_y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx_x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        tile[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = tile[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n// --------------------------------------------------------------------\n// Partial transposes\n// NB: the coarse- and fine-grained routines only perform part of a\n//     transpose and will fail the test against the reference solution\n//\n//     They are used to assess performance characteristics of different\n//     components of a full transpose\n// --------------------------------------------------------------------\n\n__global__ void transposeFineGrained(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float block[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index  = xIndex + (yIndex)*width;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        block[threadIdx.y + i][threadIdx.x] = idata[index + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index + i * height] = block[threadIdx.x][threadIdx.y + i];\n    }\n}\n\n__global__ void transposeCoarseGrained(float *odata, float *idata, int width, int height)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float block[TILE_DIM][TILE_DIM + 1];\n\n    int xIndex   = blockIdx.x * TILE_DIM + threadIdx.x;\n    int yIndex   = blockIdx.y * TILE_DIM + threadIdx.y;\n    int index_in = xIndex + (yIndex)*width;\n\n    xIndex        = blockIdx.y * TILE_DIM + threadIdx.x;\n    yIndex        = blockIdx.x * TILE_DIM + threadIdx.y;\n    int index_out = xIndex + (yIndex)*height;\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        block[threadIdx.y + i][threadIdx.x] = idata[index_in + i * width];\n    }\n\n    cg::sync(cta);\n\n    for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) {\n        odata[index_out + i * height] = block[threadIdx.y + i][threadIdx.x];\n    }\n}\n\n// ---------------------\n// host utility routines\n// ---------------------\n\nvoid computeTransposeGold(float *gold, float *idata, const int size_x, const int size_y)\n{\n    for (int y = 0; y < size_y; ++y) {\n        for (int x = 0; x < size_x; ++x) {\n            gold[(x * size_y) + y] = idata[(y * size_x) + x];\n        }\n    }\n}\n\nvoid getParams(int argc, char **argv, hipDeviceProp_t &deviceProp, int &size_x, int &size_y, int max_tile_dim)\n{\n    // set matrix size (if (x,y) dim of matrix is not square, then this will have\n    // to be modified\n    if (checkCmdLineFlag(argc, (const char **)argv, \"dimX\")) {\n        size_x = getCmdLineArgumentInt(argc, (const char **)argv, \"dimX\");\n\n        if (size_x > max_tile_dim) {\n            printf(\"> MatrixSize X = %d is greater than the recommended size = %d\\n\", size_x, max_tile_dim);\n        }\n        else {\n            printf(\"> MatrixSize X = %d\\n\", size_x);\n        }\n    }\n    else {\n        size_x = max_tile_dim;\n        size_x = FLOOR(size_x, 512);\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"dimY\")) {\n        size_y = getCmdLineArgumentInt(argc, (const char **)argv, \"dimY\");\n\n        if (size_y > max_tile_dim) {\n            printf(\"> MatrixSize Y = %d is greater than the recommended size = %d\\n\", size_y, max_tile_dim);\n        }\n        else {\n            printf(\"> MatrixSize Y = %d\\n\", size_y);\n        }\n    }\n    else {\n        size_y = max_tile_dim;\n        size_y = FLOOR(size_y, 512);\n    }\n}\n\nvoid showHelp()\n{\n    printf(\"\\n%s : Command line options\\n\", sSDKsample);\n    printf(\"\\t-device=n          (where n=0,1,2.... for the GPU device)\\n\\n\");\n    printf(\"> The default matrix size can be overridden with these parameters\\n\");\n    printf(\"\\t-dimX=row_dim_size (matrix row    dimensions)\\n\");\n    printf(\"\\t-dimY=col_dim_size (matrix column dimensions)\\n\");\n}\n\n// ----\n// main\n// ----\n\nint main(int argc, char **argv)\n{\n    // Start logs\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        showHelp();\n        return 0;\n    }\n\n    int            devID = findCudaDevice(argc, (const char **)argv);\n    hipDeviceProp_t deviceProp;\n\n    // get number of SMs on this GPU\n    checkCudaErrors(hipGetDevice(&devID));\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n\n    // compute the scaling factor (for GPUs with fewer MPs)\n    float scale_factor, total_tiles;\n    scale_factor = max(\n        (192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n        1.0f);\n\n    printf(\"> Device %d: \\\"%s\\\"\\n\", devID, deviceProp.name);\n    printf(\"> SM Capability %d.%d detected:\\n\", deviceProp.major, deviceProp.minor);\n\n    // Calculate number of tiles we will run for the Matrix Transpose performance\n    // tests\n    int size_x, size_y, max_matrix_dim, matrix_size_test;\n\n    matrix_size_test = 512; // we round down max_matrix_dim for this perf test\n    total_tiles      = (float)MAX_TILES / scale_factor;\n\n    max_matrix_dim = FLOOR((int)(floor(sqrt(total_tiles)) * TILE_DIM), matrix_size_test);\n\n    // This is the minimum size allowed\n    if (max_matrix_dim == 0) {\n        max_matrix_dim = matrix_size_test;\n    }\n\n    printf(\"> [%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    printf(\"> Compute performance scaling factor = %4.2f\\n\", scale_factor);\n\n    // Extract parameters if there are any, command line -dimx and -dimy can\n    // override any of these settings\n    getParams(argc, argv, deviceProp, size_x, size_y, max_matrix_dim);\n\n    if (size_x != size_y) {\n        printf(\"\\n[%s] does not support non-square matrices (row_dim_size(%d) != \"\n               \"col_dim_size(%d))\\nExiting...\\n\\n\",\n               sSDKsample,\n               size_x,\n               size_y);\n        exit(EXIT_FAILURE);\n    }\n\n    if (size_x % TILE_DIM != 0 || size_y % TILE_DIM != 0) {\n        printf(\"[%s] Matrix size must be integral multiple of tile \"\n               \"size\\nExiting...\\n\\n\",\n               sSDKsample);\n        exit(EXIT_FAILURE);\n    }\n\n    // kernel pointer and descriptor\n    void (*kernel)(float *, float *, int, int);\n    const char *kernelName;\n\n    // execution configuration parameters\n    dim3 grid(size_x / TILE_DIM, size_y / TILE_DIM), threads(TILE_DIM, BLOCK_ROWS);\n\n    if (grid.x < 1 || grid.y < 1) {\n        printf(\"[%s] grid size computation incorrect in test \\nExiting...\\n\\n\", sSDKsample);\n        exit(EXIT_FAILURE);\n    }\n\n    // CUDA events\n    hipEvent_t start, stop;\n\n    // size of memory required to store the matrix\n    size_t mem_size = static_cast<size_t>(sizeof(float) * size_x * size_y);\n\n    if (2 * mem_size > deviceProp.totalGlobalMem) {\n        printf(\"Input matrix size is larger than the available device memory!\\n\");\n        printf(\"Please choose a smaller size matrix\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // allocate host memory\n    float *h_idata       = (float *)malloc(mem_size);\n    float *h_odata       = (float *)malloc(mem_size);\n    float *transposeGold = (float *)malloc(mem_size);\n    float *gold;\n\n    // allocate device memory\n    float *d_idata, *d_odata;\n    checkCudaErrors(hipMalloc((void **)&d_idata, mem_size));\n    checkCudaErrors(hipMalloc((void **)&d_odata, mem_size));\n\n    // initialize host data\n    for (int i = 0; i < (size_x * size_y); ++i) {\n        h_idata[i] = (float)i;\n    }\n\n    // copy host data to device\n    checkCudaErrors(hipMemcpy(d_idata, h_idata, mem_size, hipMemcpyHostToDevice));\n\n    // Compute reference transpose solution\n    computeTransposeGold(transposeGold, h_idata, size_x, size_y);\n\n    // print out common data for all kernels\n    printf(\"\\nMatrix size: %dx%d (%dx%d tiles), tile size: %dx%d, block size: \"\n           \"%dx%d\\n\\n\",\n           size_x,\n           size_y,\n           size_x / TILE_DIM,\n           size_y / TILE_DIM,\n           TILE_DIM,\n           TILE_DIM,\n           TILE_DIM,\n           BLOCK_ROWS);\n\n    // initialize events\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n\n    //\n    // loop over different kernels\n    //\n\n    bool success = true;\n\n    for (int k = 0; k < 8; k++) {\n        // set kernel pointer\n        switch (k) {\n        case 0:\n            kernel     = &copy;\n            kernelName = \"simple copy       \";\n            break;\n\n        case 1:\n            kernel     = &copySharedMem;\n            kernelName = \"shared memory copy\";\n            break;\n\n        case 2:\n            kernel     = &transposeNaive;\n            kernelName = \"naive             \";\n            break;\n\n        case 3:\n            kernel     = &transposeCoalesced;\n            kernelName = \"coalesced         \";\n            break;\n\n        case 4:\n            kernel     = &transposeNoBankConflicts;\n            kernelName = \"optimized         \";\n            break;\n\n        case 5:\n            kernel     = &transposeCoarseGrained;\n            kernelName = \"coarse-grained    \";\n            break;\n\n        case 6:\n            kernel     = &transposeFineGrained;\n            kernelName = \"fine-grained      \";\n            break;\n\n        case 7:\n            kernel     = &transposeDiagonal;\n            kernelName = \"diagonal          \";\n            break;\n        }\n\n        // set reference solution\n        if (kernel == &copy || kernel == &copySharedMem) {\n            gold = h_idata;\n        }\n        else if (kernel == &transposeCoarseGrained || kernel == &transposeFineGrained) {\n            gold = h_odata; // fine- and coarse-grained kernels are not full\n                            // transposes, so bypass check\n        }\n        else {\n            gold = transposeGold;\n        }\n\n        // Clear error status\n        checkCudaErrors(hipGetLastError());\n\n        // warmup to avoid timing startup\n        kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);\n\n        // take measurements for loop over kernel launches\n        checkCudaErrors(hipEventRecord(start, 0));\n\n        for (int i = 0; i < NUM_REPS; i++) {\n            kernel<<<grid, threads>>>(d_odata, d_idata, size_x, size_y);\n            // Ensure no launch failure\n            checkCudaErrors(hipGetLastError());\n        }\n\n        checkCudaErrors(hipEventRecord(stop, 0));\n        checkCudaErrors(hipEventSynchronize(stop));\n        float kernelTime;\n        checkCudaErrors(hipEventElapsedTime(&kernelTime, start, stop));\n\n        checkCudaErrors(hipMemcpy(h_odata, d_odata, mem_size, hipMemcpyDeviceToHost));\n        bool res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);\n\n        if (res == false) {\n            printf(\"*** %s kernel FAILED ***\\n\", kernelName);\n            success = false;\n        }\n\n        // take measurements for loop inside kernel\n        checkCudaErrors(hipMemcpy(h_odata, d_odata, mem_size, hipMemcpyDeviceToHost));\n        res = compareData(gold, h_odata, size_x * size_y, 0.01f, 0.0f);\n\n        if (res == false) {\n            printf(\"*** %s kernel FAILED ***\\n\", kernelName);\n            success = false;\n        }\n\n        // report effective bandwidths\n        float kernelBandwidth = 2.0f * 1000.0f * mem_size / (1024 * 1024 * 1024) / (kernelTime / NUM_REPS);\n        printf(\"transpose %s, Throughput = %.4f GB/s, Time = %.5f ms, Size = %u fp32 \"\n               \"elements, NumDevsUsed = %u, Workgroup = %u\\n\",\n               kernelName,\n               kernelBandwidth,\n               kernelTime / NUM_REPS,\n               (size_x * size_y),\n               1,\n               TILE_DIM * BLOCK_ROWS);\n\n        // Reset d_odata to zero before starting the next loop iteration to avoid\n        // carrying over results from previous kernels. Without this reset, residual\n        // data from a prior kernel (e.g., 'copy') could make a subsequent\n        // kernel (e.g., 'copySharedMem') appear correct even if it performs no work,\n        // leading to false positives in compareData.\n        for (int i = 0; i < (size_x * size_y); ++i) {\n            h_odata[i] = 0;\n        }\n        // copy host data to device\n        checkCudaErrors(hipMemcpy(d_odata, h_odata, mem_size, hipMemcpyHostToDevice));\n\n        // Reset d_odata to zero before starting the next loop iteration to avoid\n        // carrying over results from previous kernels. Without this reset, residual\n        // data from a prior kernel (e.g., 'copy') could make a subsequent\n        // kernel (e.g., 'copySharedMem') appear correct even if it performs no work,\n        // leading to false positives in compareData.\n        for (int i = 0; i < (size_x * size_y); ++i) {\n            h_odata[i] = 0;\n        }\n        // copy host data to device\n        checkCudaErrors(hipMemcpy(d_odata, h_odata, mem_size, hipMemcpyHostToDevice));\n    }\n\n    // cleanup\n    free(h_idata);\n    free(h_odata);\n    free(transposeGold);\n    hipFree(d_idata);\n    hipFree(d_odata);\n\n    checkCudaErrors(hipEventDestroy(start));\n    checkCudaErrors(hipEventDestroy(stop));\n\n    if (!success) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/transpose/transpose.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple test showing huge access speed gap\n * between aligned and misaligned structures\n * (those having/missing __align__ keyword).\n * It measures per-element copy throughput for\n * aligned and misaligned structures on\n * big chunks of data.\n */\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <helper_cuda.h>      // helper functions for CUDA error checking and initialization\n#include <helper_functions.h> // helper utility functions\n\n////////////////////////////////////////////////////////////////////////////////\n// Misaligned types\n////////////////////////////////////////////////////////////////////////////////\ntypedef unsigned char uint8;\n\ntypedef unsigned short int uint16;\n\ntypedef struct\n{\n    unsigned char r, g, b, a;\n} RGBA8_misaligned;\n\ntypedef struct\n{\n    unsigned int l, a;\n} LA32_misaligned;\n\ntypedef struct\n{\n    unsigned int r, g, b;\n} RGB32_misaligned;\n\ntypedef struct\n{\n    unsigned int r, g, b, a;\n} RGBA32_misaligned;\n\n////////////////////////////////////////////////////////////////////////////////\n// Aligned types\n////////////////////////////////////////////////////////////////////////////////\ntypedef struct __align__(4)\n{\n    unsigned char r, g, b, a;\n} RGBA8;\n\ntypedef unsigned int I32;\n\ntypedef struct __align__(8)\n{\n    unsigned int l, a;\n} LA32;\n\ntypedef struct __align__(16)\n{\n    unsigned int r, g, b;\n} RGB32;\n\ntypedef struct __align__(16)\n{\n    unsigned int r, g, b, a;\n} RGBA32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Because G80 class hardware natively supports global memory operations\n// only with data elements of 4, 8 and 16 bytes, if structure size\n// exceeds 16 bytes, it can't be efficiently read or written,\n// since more than one global memory non-coalescable load/store instructions\n// will be generated, even if __align__ option is supplied.\n// \"Structure of arrays\" storage strategy offers best performance\n// in general case. See section 5.1.2 of the Programming Guide.\n////////////////////////////////////////////////////////////////////////////////\ntypedef struct __align__(16)\n{\n    RGBA32 c1, c2;\n} RGBA32_2;\n\n////////////////////////////////////////////////////////////////////////////////\n// Common host and device functions\n////////////////////////////////////////////////////////////////////////////////\n// Round a / b to nearest higher integer value\nint iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Round a / b to nearest lower integer value\nint iDivDown(int a, int b) { return a / b; }\n\n// Align a to nearest higher multiple of b\nint iAlignUp(int a, int b) { return (a % b != 0) ? (a - a % b + b) : a; }\n\n// Align a to nearest lower multiple of b\nint iAlignDown(int a, int b) { return a - a % b; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Simple CUDA kernel.\n// Copy is carried out on per-element basis,\n// so it's not per-byte in case of padded structures.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class TData> __global__ void testKernel(TData *d_odata, TData *d_idata, int numElements)\n{\n    const int tid        = blockDim.x * blockIdx.x + threadIdx.x;\n    const int numThreads = blockDim.x * gridDim.x;\n\n    for (int pos = tid; pos < numElements; pos += numThreads) {\n        d_odata[pos] = d_idata[pos];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Validation routine for simple copy kernel.\n// We must know \"packed\" size of TData (number_of_fields * sizeof(simple_type))\n// and compare only these \"packed\" parts of the structure,\n// containing actual user data. The compiler behavior with padding bytes\n// is undefined, since padding is merely a placeholder\n// and doesn't contain any user data.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class TData> int testCPU(TData *h_odata, TData *h_idata, int numElements, int packedElementSize)\n{\n    for (int pos = 0; pos < numElements; pos++) {\n        TData src = h_idata[pos];\n        TData dst = h_odata[pos];\n\n        for (int i = 0; i < packedElementSize; i++)\n            if (((char *)&src)[i] != ((char *)&dst)[i]) {\n                return 0;\n            }\n    }\n\n    return 1;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n////////////////////////////////////////////////////////////////////////////////\n// Memory chunk size in bytes. Reused for test\nconst int MEM_SIZE       = 50000000;\nconst int NUM_ITERATIONS = 32;\n\n// GPU input and output data\nunsigned char *d_idata, *d_odata;\n// CPU input data and instance of GPU output data\nunsigned char      *h_idataCPU, *h_odataGPU;\nStopWatchInterface *hTimer = NULL;\n\ntemplate <class TData> int runTest(int packedElementSize, int memory_size)\n{\n    const int totalMemSizeAligned = iAlignDown(memory_size, sizeof(TData));\n    const int numElements         = iDivDown(memory_size, sizeof(TData));\n\n    // Clean output buffer before current test\n    checkCudaErrors(cudaMemset(d_odata, 0, memory_size));\n    // Run test\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkResetTimer(&hTimer);\n    sdkStartTimer(&hTimer);\n\n    for (int i = 0; i < NUM_ITERATIONS; i++) {\n        testKernel<TData><<<64, 256>>>((TData *)d_odata, (TData *)d_idata, numElements);\n        getLastCudaError(\"testKernel() execution failed\\n\");\n    }\n\n    checkCudaErrors(cudaDeviceSynchronize());\n    sdkStopTimer(&hTimer);\n    double gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;\n    printf(\"Avg. time: %f ms / Copy throughput: %f GB/s.\\n\",\n           gpuTime,\n           (double)totalMemSizeAligned / (gpuTime * 0.001 * 1073741824.0));\n\n    // Read back GPU results and run validation\n    checkCudaErrors(cudaMemcpy(h_odataGPU, d_odata, memory_size, cudaMemcpyDeviceToHost));\n    int flag = testCPU((TData *)h_odataGPU, (TData *)h_idataCPU, numElements, packedElementSize);\n\n    printf(flag ? \"\\tTEST OK\\n\" : \"\\tTEST FAILURE\\n\");\n\n    return !flag;\n}\n\nint main(int argc, char **argv)\n{\n    int i, nTotalFailures = 0;\n\n    int            devID;\n    cudaDeviceProp deviceProp;\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // find first CUDA device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get number of SMs on this GPU\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, devID));\n    printf(\"[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    // Anything that is less than 192 Cores will have a scaled down workload\n    float scale_factor = max(\n        (192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n        1.0f);\n\n    int MemorySize = (int)(MEM_SIZE / scale_factor) & 0xffffff00; // force multiple of 256 bytes\n\n    printf(\"> Compute scaling value = %4.2f\\n\", scale_factor);\n    printf(\"> Memory Size = %d\\n\", MemorySize);\n\n    sdkCreateTimer(&hTimer);\n\n    printf(\"Allocating memory...\\n\");\n    h_idataCPU = (unsigned char *)malloc(MemorySize);\n    h_odataGPU = (unsigned char *)malloc(MemorySize);\n    checkCudaErrors(cudaMalloc((void **)&d_idata, MemorySize));\n    checkCudaErrors(cudaMalloc((void **)&d_odata, MemorySize));\n\n    printf(\"Generating host input data array...\\n\");\n\n    for (i = 0; i < MemorySize; i++) {\n        h_idataCPU[i] = (i & 0xFF) + 1;\n    }\n\n    printf(\"Uploading input data to GPU memory...\\n\");\n    checkCudaErrors(cudaMemcpy(d_idata, h_idataCPU, MemorySize, cudaMemcpyHostToDevice));\n\n    printf(\"Testing misaligned types...\\n\");\n    printf(\"uint8...\\n\");\n    nTotalFailures += runTest<uint8>(1, MemorySize);\n\n    printf(\"uint16...\\n\");\n    nTotalFailures += runTest<uint16>(2, MemorySize);\n\n    printf(\"RGBA8_misaligned...\\n\");\n    nTotalFailures += runTest<RGBA8_misaligned>(4, MemorySize);\n\n    printf(\"LA32_misaligned...\\n\");\n    nTotalFailures += runTest<LA32_misaligned>(8, MemorySize);\n\n    printf(\"RGB32_misaligned...\\n\");\n    nTotalFailures += runTest<RGB32_misaligned>(12, MemorySize);\n\n    printf(\"RGBA32_misaligned...\\n\");\n    nTotalFailures += runTest<RGBA32_misaligned>(16, MemorySize);\n\n    printf(\"Testing aligned types...\\n\");\n    printf(\"RGBA8...\\n\");\n    nTotalFailures += runTest<RGBA8>(4, MemorySize);\n\n    printf(\"I32...\\n\");\n    nTotalFailures += runTest<I32>(4, MemorySize);\n\n    printf(\"LA32...\\n\");\n    nTotalFailures += runTest<LA32>(8, MemorySize);\n\n    printf(\"RGB32...\\n\");\n    nTotalFailures += runTest<RGB32>(12, MemorySize);\n\n    printf(\"RGBA32...\\n\");\n    nTotalFailures += runTest<RGBA32>(16, MemorySize);\n\n    printf(\"RGBA32_2...\\n\");\n    nTotalFailures += runTest<RGBA32_2>(32, MemorySize);\n\n    printf(\"\\n[alignedTypes] -> Test Results: %d Failures\\n\", nTotalFailures);\n\n    printf(\"Shutting down...\\n\");\n    checkCudaErrors(cudaFree(d_idata));\n    checkCudaErrors(cudaFree(d_odata));\n    free(h_odataGPU);\n    free(h_idataCPU);\n\n    sdkDeleteTimer(&hTimer);\n\n    if (nTotalFailures != 0) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple test showing huge access speed gap\n * between aligned and misaligned structures\n * (those having/missing __align__ keyword).\n * It measures per-element copy throughput for\n * aligned and misaligned structures on\n * big chunks of data.\n */\n\n// includes, system\n#include \"hip/hip_runtime.h\"\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <hip/hip_runtime_api.h>      // helper functions for CUDA error checking and initialization\n#include <helper_functions.h> // helper utility functions\n\n////////////////////////////////////////////////////////////////////////////////\n// Misaligned types\n////////////////////////////////////////////////////////////////////////////////\ntypedef unsigned char uint8;\n\ntypedef unsigned short int uint16;\n\ntypedef struct\n{\n    unsigned char r, g, b, a;\n} RGBA8_misaligned;\n\ntypedef struct\n{\n    unsigned int l, a;\n} LA32_misaligned;\n\ntypedef struct\n{\n    unsigned int r, g, b;\n} RGB32_misaligned;\n\ntypedef struct\n{\n    unsigned int r, g, b, a;\n} RGBA32_misaligned;\n\n////////////////////////////////////////////////////////////////////////////////\n// Aligned types\n////////////////////////////////////////////////////////////////////////////////\ntypedef struct __align__(4)\n{\n    unsigned char r, g, b, a;\n} RGBA8;\n\ntypedef unsigned int I32;\n\ntypedef struct __align__(8)\n{\n    unsigned int l, a;\n} LA32;\n\ntypedef struct __align__(16)\n{\n    unsigned int r, g, b;\n} RGB32;\n\ntypedef struct __align__(16)\n{\n    unsigned int r, g, b, a;\n} RGBA32;\n\n////////////////////////////////////////////////////////////////////////////////\n// Because G80 class hardware natively supports global memory operations\n// only with data elements of 4, 8 and 16 bytes, if structure size\n// exceeds 16 bytes, it can't be efficiently read or written,\n// since more than one global memory non-coalescable load/store instructions\n// will be generated, even if __align__ option is supplied.\n// \"Structure of arrays\" storage strategy offers best performance\n// in general case. See section 5.1.2 of the Programming Guide.\n////////////////////////////////////////////////////////////////////////////////\ntypedef struct __align__(16)\n{\n    RGBA32 c1, c2;\n} RGBA32_2;\n\n////////////////////////////////////////////////////////////////////////////////\n// Common host and device functions\n////////////////////////////////////////////////////////////////////////////////\n// Round a / b to nearest higher integer value\nint iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Round a / b to nearest lower integer value\nint iDivDown(int a, int b) { return a / b; }\n\n// Align a to nearest higher multiple of b\nint iAlignUp(int a, int b) { return (a % b != 0) ? (a - a % b + b) : a; }\n\n// Align a to nearest lower multiple of b\nint iAlignDown(int a, int b) { return a - a % b; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Simple CUDA kernel.\n// Copy is carried out on per-element basis,\n// so it's not per-byte in case of padded structures.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class TData> __global__ void testKernel(TData *d_odata, TData *d_idata, int numElements)\n{\n    const int tid        = blockDim.x * blockIdx.x + threadIdx.x;\n    const int numThreads = blockDim.x * gridDim.x;\n\n    for (int pos = tid; pos < numElements; pos += numThreads) {\n        d_odata[pos] = d_idata[pos];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Validation routine for simple copy kernel.\n// We must know \"packed\" size of TData (number_of_fields * sizeof(simple_type))\n// and compare only these \"packed\" parts of the structure,\n// containing actual user data. The compiler behavior with padding bytes\n// is undefined, since padding is merely a placeholder\n// and doesn't contain any user data.\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class TData> int testCPU(TData *h_odata, TData *h_idata, int numElements, int packedElementSize)\n{\n    for (int pos = 0; pos < numElements; pos++) {\n        TData src = h_idata[pos];\n        TData dst = h_odata[pos];\n\n        for (int i = 0; i < packedElementSize; i++)\n            if (((char *)&src)[i] != ((char *)&dst)[i]) {\n                return 0;\n            }\n    }\n\n    return 1;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Data configuration\n////////////////////////////////////////////////////////////////////////////////\n// Memory chunk size in bytes. Reused for test\nconst int MEM_SIZE       = 50000000;\nconst int NUM_ITERATIONS = 32;\n\n// GPU input and output data\nunsigned char *d_idata, *d_odata;\n// CPU input data and instance of GPU output data\nunsigned char      *h_idataCPU, *h_odataGPU;\nStopWatchInterface *hTimer = NULL;\n\ntemplate <class TData> int runTest(int packedElementSize, int memory_size)\n{\n    const int totalMemSizeAligned = iAlignDown(memory_size, sizeof(TData));\n    const int numElements         = iDivDown(memory_size, sizeof(TData));\n\n    // Clean output buffer before current test\n    checkCudaErrors(hipMemset(d_odata, 0, memory_size));\n    // Run test\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkResetTimer(&hTimer);\n    sdkStartTimer(&hTimer);\n\n    for (int i = 0; i < NUM_ITERATIONS; i++) {\n        testKernel<TData><<<64, 256>>>((TData *)d_odata, (TData *)d_idata, numElements);\n        getLastCudaError(\"testKernel() execution failed\\n\");\n    }\n\n    checkCudaErrors(hipDeviceSynchronize());\n    sdkStopTimer(&hTimer);\n    double gpuTime = sdkGetTimerValue(&hTimer) / NUM_ITERATIONS;\n    printf(\"Avg. time: %f ms / Copy throughput: %f GB/s.\\n\",\n           gpuTime,\n           (double)totalMemSizeAligned / (gpuTime * 0.001 * 1073741824.0));\n\n    // Read back GPU results and run validation\n    checkCudaErrors(hipMemcpy(h_odataGPU, d_odata, memory_size, hipMemcpyDeviceToHost));\n    int flag = testCPU((TData *)h_odataGPU, (TData *)h_idataCPU, numElements, packedElementSize);\n\n    printf(flag ? \"\\tTEST OK\\n\" : \"\\tTEST FAILURE\\n\");\n\n    return !flag;\n}\n\nint main(int argc, char **argv)\n{\n    int i, nTotalFailures = 0;\n\n    int            devID;\n    hipDeviceProp_t deviceProp;\n    printf(\"[%s] - Starting...\\n\", argv[0]);\n\n    // find first CUDA device\n    devID = findCudaDevice(argc, (const char **)argv);\n\n    // get number of SMs on this GPU\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, devID));\n    printf(\"[%s] has %d MP(s) x %d (Cores/MP) = %d (Cores)\\n\",\n           deviceProp.name,\n           deviceProp.multiProcessorCount,\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor),\n           _ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * deviceProp.multiProcessorCount);\n\n    // Anything that is less than 192 Cores will have a scaled down workload\n    float scale_factor = max(\n        (192.0f / (_ConvertSMVer2Cores(deviceProp.major, deviceProp.minor) * (float)deviceProp.multiProcessorCount)),\n        1.0f);\n\n    int MemorySize = (int)(MEM_SIZE / scale_factor) & 0xffffff00; // force multiple of 256 bytes\n\n    printf(\"> Compute scaling value = %4.2f\\n\", scale_factor);\n    printf(\"> Memory Size = %d\\n\", MemorySize);\n\n    sdkCreateTimer(&hTimer);\n\n    printf(\"Allocating memory...\\n\");\n    h_idataCPU = (unsigned char *)malloc(MemorySize);\n    h_odataGPU = (unsigned char *)malloc(MemorySize);\n    checkCudaErrors(hipMalloc((void **)&d_idata, MemorySize));\n    checkCudaErrors(hipMalloc((void **)&d_odata, MemorySize));\n\n    printf(\"Generating host input data array...\\n\");\n\n    for (i = 0; i < MemorySize; i++) {\n        h_idataCPU[i] = (i & 0xFF) + 1;\n    }\n\n    printf(\"Uploading input data to GPU memory...\\n\");\n    checkCudaErrors(hipMemcpy(d_idata, h_idataCPU, MemorySize, hipMemcpyHostToDevice));\n\n    printf(\"Testing misaligned types...\\n\");\n    printf(\"uint8...\\n\");\n    nTotalFailures += runTest<uint8>(1, MemorySize);\n\n    printf(\"uint16...\\n\");\n    nTotalFailures += runTest<uint16>(2, MemorySize);\n\n    printf(\"RGBA8_misaligned...\\n\");\n    nTotalFailures += runTest<RGBA8_misaligned>(4, MemorySize);\n\n    printf(\"LA32_misaligned...\\n\");\n    nTotalFailures += runTest<LA32_misaligned>(8, MemorySize);\n\n    printf(\"RGB32_misaligned...\\n\");\n    nTotalFailures += runTest<RGB32_misaligned>(12, MemorySize);\n\n    printf(\"RGBA32_misaligned...\\n\");\n    nTotalFailures += runTest<RGBA32_misaligned>(16, MemorySize);\n\n    printf(\"Testing aligned types...\\n\");\n    printf(\"RGBA8...\\n\");\n    nTotalFailures += runTest<RGBA8>(4, MemorySize);\n\n    printf(\"I32...\\n\");\n    nTotalFailures += runTest<I32>(4, MemorySize);\n\n    printf(\"LA32...\\n\");\n    nTotalFailures += runTest<LA32>(8, MemorySize);\n\n    printf(\"RGB32...\\n\");\n    nTotalFailures += runTest<RGB32>(12, MemorySize);\n\n    printf(\"RGBA32...\\n\");\n    nTotalFailures += runTest<RGBA32>(16, MemorySize);\n\n    printf(\"RGBA32_2...\\n\");\n    nTotalFailures += runTest<RGBA32_2>(32, MemorySize);\n\n    printf(\"\\n[alignedTypes] -> Test Results: %d Failures\\n\", nTotalFailures);\n\n    printf(\"Shutting down...\\n\");\n    checkCudaErrors(hipFree(d_idata));\n    checkCudaErrors(hipFree(d_odata));\n    free(h_odataGPU);\n    free(h_idataCPU);\n\n    sdkDeleteTimer(&hTimer);\n\n    if (nTotalFailures != 0) {\n        printf(\"Test failed!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    printf(\"Test passed\\n\");\n    exit(EXIT_SUCCESS);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/alignedTypes/alignedTypes.cu"}
{"input": "/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple application showing the performance characteristics of cudaGraphs.\n */\n\n#define USE_NVTX\n\n#include <chrono>\n#include <cstdio>\n#include <cuda_runtime.h>\n#include <vector>\n\ntypedef volatile int LatchType;\n\nstd::chrono::time_point<std::chrono::high_resolution_clock> getCpuTime()\n{\n    return std::chrono::high_resolution_clock::now();\n}\n\ntemplate <typename T> float getMicroSecondDuration(T start, T end)\n{\n    return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() * .001f;\n}\n\nfloat getAsyncMicroSecondDuration(cudaEvent_t start, cudaEvent_t end)\n{\n    float ms;\n    cudaEventElapsedTime(&ms, start, end);\n    return ms * 1000;\n}\n\n#ifdef USE_NVTX\n#include <nvtx3/nvToolsExt.h>\n\nclass Tracer\n{\npublic:\n    Tracer(const char *name) { nvtxRangePushA(name); }\n    ~Tracer() { nvtxRangePop(); }\n};\n#define RANGE(name)      Tracer uniq_name_using_macros(name);\n#define RANGE_PUSH(name) nvtxRangePushA(name)\n#define RANGE_POP()      nvtxRangePop();\n#else\n#define RANGE(name)\n#endif\n\nstd::vector<cudaStream_t> stream;\ncudaEvent_t               event[1];\ncudaEvent_t               timingEvent[2];\n\nstruct hostData\n{\n    long long timeElapsed;\n    bool      timeoutDetected;\n    long long timeElapsed2;\n    bool      timeoutDetected2;\n    LatchType latch;\n    LatchType latch2;\n};\n\nstruct hostData *hostData;\n\n__global__ void empty() {}\n\n// Function to read the GPU nanosecond timer in a kernel\n__device__ __forceinline__ unsigned long long __globaltimer()\n{\n    unsigned long long globaltimer;\n    asm volatile(\"mov.u64 %0, %globaltimer;\" : \"=l\"(globaltimer));\n    return globaltimer;\n}\n\n__global__ void delay(long long ticks)\n{\n    long long endTime = clock64() + ticks;\n    while (clock64() < endTime)\n        ;\n}\n\n__global__ void waitWithTimeout(long long nanoseconds, bool *timeoutDetected, long long *timeElapsed, LatchType *latch)\n{\n    long long startTime = __globaltimer();\n    long long endTime   = startTime + nanoseconds;\n    long long time      = 0;\n    do {\n        time = __globaltimer();\n    } while (time < endTime && (latch == NULL || *latch == 0));\n    if (timeElapsed != NULL) {\n        *timeElapsed = time - startTime;\n    }\n    if (timeoutDetected) {\n        // report timeout if latch not detected\n        *timeoutDetected = (latch == NULL || *latch == 0);\n    }\n}\n\n__global__ void preUploadAnnotation() {}\n\n__global__ void postUploadAnnotation() {}\n\ncudaGraph_t createParallelChain(int length, int width, bool singleEntry = false)\n{\n    RANGE_PUSH(__func__);\n    RANGE(\"capture\");\n    cudaGraph_t graph;\n    cudaStreamBeginCapture(stream[0], cudaStreamCaptureModeGlobal);\n    int streamIdx = 0;\n    if (singleEntry) {\n        empty<<<1, 1, 0, stream[streamIdx]>>>();\n    }\n\n    cudaEventRecord(event[0], stream[0]);\n    for (int i = 1; i < width; i++) {\n        cudaStreamWaitEvent(stream[i], event[0]);\n    }\n\n    for (int i = 0; i < width; i++) {\n        streamIdx = i;\n        for (int j = 0; j < length; j++) {\n            empty<<<1, 1, 0, stream[streamIdx]>>>();\n        }\n    }\n\n    for (int i = 1; i < width; i++) {\n        cudaEventRecord(event[0], stream[i]);\n        cudaStreamWaitEvent(stream[0], event[0]);\n    }\n\n    cudaStreamEndCapture(stream[0], &graph);\n    return graph;\n}\n\nstd::vector<const char *> metricName;\nstd::vector<float>        metricValue;\n\nint  counter2 = 0;\nvoid runDemo(cudaGraph_t graph, int length, int width)\n{\n    cudaGraphExec_t graphExec;\n    {\n        auto start = getCpuTime();\n        cudaGraphInstantiateWithFlags(&graphExec, graph, 0);\n        auto end = getCpuTime();\n        metricName.push_back(\"instantiation\");\n        metricValue.push_back(getMicroSecondDuration(start, end));\n    }\n    {\n        RANGE(\"launch including upload\");\n        auto start = getCpuTime();\n        cudaGraphLaunch(graphExec, stream[0]);\n        auto apiReturn = getCpuTime();\n        cudaStreamSynchronize(stream[0]);\n        auto streamSync = getCpuTime();\n        metricName.push_back(\"first_launch_api\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"first_launch_total\");\n        metricValue.push_back(getMicroSecondDuration(start, streamSync));\n    }\n    {\n        RANGE(\"repeat lauch in empty stream\");\n        auto start = getCpuTime();\n        cudaGraphLaunch(graphExec, stream[0]);\n        auto apiReturn = getCpuTime();\n        cudaStreamSynchronize(stream[0]);\n        auto streamSync = getCpuTime();\n        metricName.push_back(\"repeat_launch_api\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"repeat_launch_total\");\n        metricValue.push_back(getMicroSecondDuration(start, streamSync));\n    }\n    {\n        // re-instantiating the exec to simulate first launch into a busy stream.\n        cudaGraphExecDestroy(graphExec);\n        cudaGraphInstantiateWithFlags(&graphExec, graph, 0);\n\n        long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n\n        RANGE(\"launch including upload in busy stream\");\n        cudaEventRecord(timingEvent[0], stream[0]);\n        cudaGraphLaunch(graphExec, stream[0]);\n        cudaEventRecord(timingEvent[1], stream[0]);\n\n        hostData->latch = 1;\n        cudaStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"first_launch_device\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n        hostData->latch           = 0;\n        hostData->timeoutDetected = 0;\n    }\n    {\n        RANGE(\"repeat lauch in busy stream\");\n        long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n        cudaEventRecord(timingEvent[0], stream[0]);\n        cudaGraphLaunch(graphExec, stream[0]);\n        cudaEventRecord(timingEvent[1], stream[0]);\n\n        hostData->latch = 1;\n        cudaStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"repeat_launch_device\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n        hostData->latch           = 0;\n        hostData->timeoutDetected = 0;\n    }\n    {\n        // re-instantiating the exec to provide upload with work to do.\n        cudaGraphExecDestroy(graphExec);\n        cudaGraphInstantiateWithFlags(&graphExec, graph, 0);\n        long long maxTimeoutNanoSeconds = 4000 + 1000 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected2, &hostData->timeElapsed2, &hostData->latch2);\n        maxTimeoutNanoSeconds = 2000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[1]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n\n        RANGE(\"uploading a graph off of the critical path\");\n        preUploadAnnotation<<<1, 1, 0, stream[1]>>>();\n        cudaEventRecord(timingEvent[0], stream[0]);\n        auto start = getCpuTime();\n        cudaGraphUpload(graphExec, stream[1]);\n        auto apiReturn = getCpuTime();\n        cudaEventRecord(event[0], stream[1]);\n        cudaEventRecord(timingEvent[1], stream[0]);\n        postUploadAnnotation<<<1, 1, 0, stream[1]>>>();\n\n        hostData->latch = 1; // release the blocking kernel for the upload\n        cudaStreamWaitEvent(stream[0], event[0]);\n        cudaGraphLaunch(graphExec, stream[0]);\n        cudaEventSynchronize(event[0]); // upload done, similuate critical path being ready for the graph to run by the\n                                        // release of the second latch\n\n        hostData->latch2 = 1; // release the work\n        cudaStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"upload_api_time\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"updoad_device_time\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n\n        hostData->latch            = 0;\n        hostData->latch2           = 0;\n        hostData->timeoutDetected  = 0;\n        hostData->timeoutDetected2 = 0;\n    }\n    cudaGraphExecDestroy(graphExec);\n    cudaGraphDestroy(graph);\n    RANGE_POP();\n}\n\nvoid usage()\n{\n    printf(\"programName [outputFmt] [numTrials] [length] [width] [pattern] [stride] [maxLength] \\n\");\n    printf(\"\\toutputFmt - program output, default=3 (see below)\\n\");\n    printf(\"\\tnumTrials (per length)\\n\");\n    printf(\"\\tstarting length of the topology\\n\");\n    printf(\"\\twidth - width of the graph topology\\n\");\n    printf(\"\\tpattern - Structure of graph, default=0 (see below)\\n\");\n    printf(\"\\tstride - how to grow the length between each set of trials \\n\");\n    printf(\"\\tmaxLength - maximum lenght to try \\n\");\n    printf(\"\\n\");\n    printf(\"outputFmt can be:\\n\");\n    printf(\"\\t0: this help message\\n\");\n    printf(\"\\t1: csv data headers\\n\");\n    printf(\"\\t2: per trial csv data\\n\");\n    printf(\"\\t3: csv data & headers\\n\");\n    printf(\"\\t4: csv data is printed and trials are averaged for each length\\n\");\n    printf(\"\\t5: csv data is printed and trials are averaged for each length and headers are printed\\n\");\n    printf(\"\\n\");\n    printf(\"Pattern can be:\\n\");\n    printf(\"\\t0: No interconnect between branches\\n\");\n    printf(\"\\t1: Adds an extra root node before the initial fork\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    if (argc < 1) {\n        usage();\n        return 0;\n    }\n\n    int numTrials = 1, length = 20, width = 1, outputFmt = 3, pattern = 0, stride = 1;\n    if (argc > 1)\n        outputFmt = atoi(argv[1]);\n    if (argc > 2)\n        numTrials = atoi(argv[2]);\n    if (argc > 3)\n        length = atoi(argv[3]);\n    if (argc > 4)\n        width = atoi(argv[4]);\n    if (argc > 5)\n        pattern = atoi(argv[5]);\n    if (argc > 6)\n        stride = atoi(argv[6]);\n    int maxLength = length;\n    if (argc > 7)\n        maxLength = atoi(argv[7]);\n    if (maxLength < length) {\n        maxLength = length;\n    }\n\n    if ((outputFmt & 4) && (outputFmt & 2)) {\n        printf(\"printing average and all samples doesn't make sense\\n\");\n    }\n\n    if (length == 0 || width == 0 || outputFmt == 0 || outputFmt > 5 || pattern > 1) {\n        usage();\n        return 0;\n    }\n\n    bool singleEntry = (pattern == 1);\n\n    cudaGraph_t graph;\n\n    cudaFree(0);\n    cudaMallocHost(&hostData, sizeof(*hostData));\n    int numStreams = width;\n    if (numStreams == 1)\n        numStreams = 2; // demo needs two streams even if capture only needs 1.\n    stream.resize(numStreams);\n    for (int i = 0; i < numStreams; i++) {\n        cudaStreamCreate(&stream[i]);\n    }\n\n    cudaEventCreate(&event[0], cudaEventDisableTiming);\n    cudaEventCreate(&timingEvent[0], 0);\n    cudaEventCreate(&timingEvent[1], 0);\n\n    {\n        RANGE(\"warmup\");\n        for (int i = 0; i < width; i++) {\n            empty<<<1, 1, 0, stream[i]>>>();\n        }\n        cudaStreamSynchronize(stream[0]);\n\n        auto start = getCpuTime();\n        graph      = createParallelChain(length, width, singleEntry);\n        auto end   = getCpuTime();\n        metricValue.push_back(getMicroSecondDuration(start, end));\n        metricName.push_back(\"capture\");\n        runDemo(graph, length, width);\n    }\n\n    if (outputFmt & 1) {\n        printf(\"length, width, pattern, \");\n        for (int i = 0; i < metricName.size(); i++) {\n            printf(\"%s, \", metricName[i]);\n        }\n        printf(\"\\r\\n\");\n    }\n\n    if (!(outputFmt & 6)) {\n        printf(\"skipping trials since no output is expected\\n\");\n        return 1;\n    }\n\n    std::vector<double> metricTotal;\n    metricTotal.resize(metricValue.size());\n\n    while (length <= maxLength) {\n        for (int i = 0; i < numTrials; i++) {\n            metricName.clear();\n            metricValue.clear();\n            auto start = getCpuTime();\n            graph      = createParallelChain(length, width, singleEntry);\n            auto end   = getCpuTime();\n            metricValue.push_back(getMicroSecondDuration(start, end));\n\n            runDemo(graph, length, width);\n\n            if (outputFmt & 2) {\n                printf(\"%d, %d, %d, \", length, width, pattern);\n                for (int i = 0; i < metricValue.size(); i++) {\n                    printf(\"%0.3f, \", metricValue[i]);\n                }\n                printf(\"\\r\\n\");\n            }\n            if (outputFmt & 4) {\n                for (int i = 0; i < metricTotal.size(); i++) {\n                    metricTotal[i] += metricValue[i];\n                }\n            }\n        }\n\n        if (outputFmt & 4) {\n            printf(\"%d, %d, %d, \", length, width, pattern);\n            for (int i = 0; i < metricTotal.size(); i++) {\n                printf(\"%0.3f, \", metricTotal[i] / numTrials);\n                metricTotal[i] = 0;\n            }\n            printf(\"\\r\\n\");\n        }\n\n        length += stride;\n    }\n\n    cudaFreeHost(hostData);\n\n    printf(\"\\n\");\n    printf(\"Test passed\\n\");\n    return 0;\n}\n", "output": "/* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple application showing the performance characteristics of cudaGraphs.\n */\n\n#define USE_NVTX\n\n#include <chrono>\n#include <cstdio>\n#include <hip/hip_runtime.h>\n#include <vector>\n\ntypedef volatile int LatchType;\n\nstd::chrono::time_point<std::chrono::high_resolution_clock> getCpuTime()\n{\n    return std::chrono::high_resolution_clock::now();\n}\n\ntemplate <typename T> float getMicroSecondDuration(T start, T end)\n{\n    return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() * .001f;\n}\n\nfloat getAsyncMicroSecondDuration(hipEvent_t start, hipEvent_t end)\n{\n    float ms;\n    hipEventElapsedTime(&ms, start, end);\n    return ms * 1000;\n}\n\n#ifdef USE_NVTX\n#include <nvtx3/nvToolsExt.h>\n\nclass Tracer\n{\npublic:\n    Tracer(const char *name) { nvtxRangePushA(name); }\n    ~Tracer() { nvtxRangePop(); }\n};\n#define RANGE(name)      Tracer uniq_name_using_macros(name);\n#define RANGE_PUSH(name) nvtxRangePushA(name)\n#define RANGE_POP()      nvtxRangePop();\n#else\n#define RANGE(name)\n#endif\n\nstd::vector<hipStream_t> stream;\nhipEvent_t               event[1];\nhipEvent_t               timingEvent[2];\n\nstruct hostData\n{\n    long long timeElapsed;\n    bool      timeoutDetected;\n    long long timeElapsed2;\n    bool      timeoutDetected2;\n    LatchType latch;\n    LatchType latch2;\n};\n\nstruct hostData *hostData;\n\n__global__ void empty() {}\n\n// Function to read the GPU nanosecond timer in a kernel\n__device__ __forceinline__ unsigned long long __globaltimer()\n{\n    unsigned long long globaltimer;\n    asm volatile(\"mov.u64 %0, %globaltimer;\" : \"=l\"(globaltimer));\n    return globaltimer;\n}\n\n__global__ void delay(long long ticks)\n{\n    long long endTime = clock64() + ticks;\n    while (clock64() < endTime)\n        ;\n}\n\n__global__ void waitWithTimeout(long long nanoseconds, bool *timeoutDetected, long long *timeElapsed, LatchType *latch)\n{\n    long long startTime = __globaltimer();\n    long long endTime   = startTime + nanoseconds;\n    long long time      = 0;\n    do {\n        time = __globaltimer();\n    } while (time < endTime && (latch == NULL || *latch == 0));\n    if (timeElapsed != NULL) {\n        *timeElapsed = time - startTime;\n    }\n    if (timeoutDetected) {\n        // report timeout if latch not detected\n        *timeoutDetected = (latch == NULL || *latch == 0);\n    }\n}\n\n__global__ void preUploadAnnotation() {}\n\n__global__ void postUploadAnnotation() {}\n\nhipGraph_t createParallelChain(int length, int width, bool singleEntry = false)\n{\n    RANGE_PUSH(__func__);\n    RANGE(\"capture\");\n    hipGraph_t graph;\n    hipStreamBeginCapture(stream[0], hipStreamCaptureModeGlobal);\n    int streamIdx = 0;\n    if (singleEntry) {\n        empty<<<1, 1, 0, stream[streamIdx]>>>();\n    }\n\n    hipEventRecord(event[0], stream[0]);\n    for (int i = 1; i < width; i++) {\n        hipStreamWaitEvent(stream[i], event[0]);\n    }\n\n    for (int i = 0; i < width; i++) {\n        streamIdx = i;\n        for (int j = 0; j < length; j++) {\n            empty<<<1, 1, 0, stream[streamIdx]>>>();\n        }\n    }\n\n    for (int i = 1; i < width; i++) {\n        hipEventRecord(event[0], stream[i]);\n        hipStreamWaitEvent(stream[0], event[0]);\n    }\n\n    hipStreamEndCapture(stream[0], &graph);\n    return graph;\n}\n\nstd::vector<const char *> metricName;\nstd::vector<float>        metricValue;\n\nint  counter2 = 0;\nvoid runDemo(hipGraph_t graph, int length, int width)\n{\n    hipGraphExec_t graphExec;\n    {\n        auto start = getCpuTime();\n        hipGraphInstantiateWithFlags(&graphExec, graph, 0);\n        auto end = getCpuTime();\n        metricName.push_back(\"instantiation\");\n        metricValue.push_back(getMicroSecondDuration(start, end));\n    }\n    {\n        RANGE(\"launch including upload\");\n        auto start = getCpuTime();\n        hipGraphLaunch(graphExec, stream[0]);\n        auto apiReturn = getCpuTime();\n        hipStreamSynchronize(stream[0]);\n        auto streamSync = getCpuTime();\n        metricName.push_back(\"first_launch_api\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"first_launch_total\");\n        metricValue.push_back(getMicroSecondDuration(start, streamSync));\n    }\n    {\n        RANGE(\"repeat lauch in empty stream\");\n        auto start = getCpuTime();\n        hipGraphLaunch(graphExec, stream[0]);\n        auto apiReturn = getCpuTime();\n        hipStreamSynchronize(stream[0]);\n        auto streamSync = getCpuTime();\n        metricName.push_back(\"repeat_launch_api\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"repeat_launch_total\");\n        metricValue.push_back(getMicroSecondDuration(start, streamSync));\n    }\n    {\n        // re-instantiating the exec to simulate first launch into a busy stream.\n        hipGraphExecDestroy(graphExec);\n        hipGraphInstantiateWithFlags(&graphExec, graph, 0);\n\n        long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n\n        RANGE(\"launch including upload in busy stream\");\n        hipEventRecord(timingEvent[0], stream[0]);\n        hipGraphLaunch(graphExec, stream[0]);\n        hipEventRecord(timingEvent[1], stream[0]);\n\n        hostData->latch = 1;\n        hipStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"first_launch_device\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n        hostData->latch           = 0;\n        hostData->timeoutDetected = 0;\n    }\n    {\n        RANGE(\"repeat lauch in busy stream\");\n        long long maxTimeoutNanoSeconds = 4000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n        hipEventRecord(timingEvent[0], stream[0]);\n        hipGraphLaunch(graphExec, stream[0]);\n        hipEventRecord(timingEvent[1], stream[0]);\n\n        hostData->latch = 1;\n        hipStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"repeat_launch_device\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n        hostData->latch           = 0;\n        hostData->timeoutDetected = 0;\n    }\n    {\n        // re-instantiating the exec to provide upload with work to do.\n        hipGraphExecDestroy(graphExec);\n        hipGraphInstantiateWithFlags(&graphExec, graph, 0);\n        long long maxTimeoutNanoSeconds = 4000 + 1000 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[0]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected2, &hostData->timeElapsed2, &hostData->latch2);\n        maxTimeoutNanoSeconds = 2000 + 500 * length * width;\n        waitWithTimeout<<<1, 1, 0, stream[1]>>>(\n            maxTimeoutNanoSeconds, &hostData->timeoutDetected, &hostData->timeElapsed, &hostData->latch);\n\n        RANGE(\"uploading a graph off of the critical path\");\n        preUploadAnnotation<<<1, 1, 0, stream[1]>>>();\n        hipEventRecord(timingEvent[0], stream[0]);\n        auto start = getCpuTime();\n        hipGraphUpload(graphExec, stream[1]);\n        auto apiReturn = getCpuTime();\n        hipEventRecord(event[0], stream[1]);\n        hipEventRecord(timingEvent[1], stream[0]);\n        postUploadAnnotation<<<1, 1, 0, stream[1]>>>();\n\n        hostData->latch = 1; // release the blocking kernel for the upload\n        hipStreamWaitEvent(stream[0], event[0]);\n        hipGraphLaunch(graphExec, stream[0]);\n        hipEventSynchronize(event[0]); // upload done, similuate critical path being ready for the graph to run by the\n                                        // release of the second latch\n\n        hostData->latch2 = 1; // release the work\n        hipStreamSynchronize(stream[0]);\n\n        metricName.push_back(\"upload_api_time\");\n        metricValue.push_back(getMicroSecondDuration(start, apiReturn));\n        metricName.push_back(\"updoad_device_time\");\n        metricValue.push_back(getAsyncMicroSecondDuration(timingEvent[0], timingEvent[1]));\n        metricName.push_back(\"blockingKernelTimeoutDetected\");\n        metricValue.push_back(hostData->timeoutDetected);\n\n        hostData->latch            = 0;\n        hostData->latch2           = 0;\n        hostData->timeoutDetected  = 0;\n        hostData->timeoutDetected2 = 0;\n    }\n    hipGraphExecDestroy(graphExec);\n    hipGraphDestroy(graph);\n    RANGE_POP();\n}\n\nvoid usage()\n{\n    printf(\"programName [outputFmt] [numTrials] [length] [width] [pattern] [stride] [maxLength] \\n\");\n    printf(\"\\toutputFmt - program output, default=3 (see below)\\n\");\n    printf(\"\\tnumTrials (per length)\\n\");\n    printf(\"\\tstarting length of the topology\\n\");\n    printf(\"\\twidth - width of the graph topology\\n\");\n    printf(\"\\tpattern - Structure of graph, default=0 (see below)\\n\");\n    printf(\"\\tstride - how to grow the length between each set of trials \\n\");\n    printf(\"\\tmaxLength - maximum lenght to try \\n\");\n    printf(\"\\n\");\n    printf(\"outputFmt can be:\\n\");\n    printf(\"\\t0: this help message\\n\");\n    printf(\"\\t1: csv data headers\\n\");\n    printf(\"\\t2: per trial csv data\\n\");\n    printf(\"\\t3: csv data & headers\\n\");\n    printf(\"\\t4: csv data is printed and trials are averaged for each length\\n\");\n    printf(\"\\t5: csv data is printed and trials are averaged for each length and headers are printed\\n\");\n    printf(\"\\n\");\n    printf(\"Pattern can be:\\n\");\n    printf(\"\\t0: No interconnect between branches\\n\");\n    printf(\"\\t1: Adds an extra root node before the initial fork\\n\");\n}\n\nint main(int argc, char **argv)\n{\n    if (argc < 1) {\n        usage();\n        return 0;\n    }\n\n    int numTrials = 1, length = 20, width = 1, outputFmt = 3, pattern = 0, stride = 1;\n    if (argc > 1)\n        outputFmt = atoi(argv[1]);\n    if (argc > 2)\n        numTrials = atoi(argv[2]);\n    if (argc > 3)\n        length = atoi(argv[3]);\n    if (argc > 4)\n        width = atoi(argv[4]);\n    if (argc > 5)\n        pattern = atoi(argv[5]);\n    if (argc > 6)\n        stride = atoi(argv[6]);\n    int maxLength = length;\n    if (argc > 7)\n        maxLength = atoi(argv[7]);\n    if (maxLength < length) {\n        maxLength = length;\n    }\n\n    if ((outputFmt & 4) && (outputFmt & 2)) {\n        printf(\"printing average and all samples doesn't make sense\\n\");\n    }\n\n    if (length == 0 || width == 0 || outputFmt == 0 || outputFmt > 5 || pattern > 1) {\n        usage();\n        return 0;\n    }\n\n    bool singleEntry = (pattern == 1);\n\n    hipGraph_t graph;\n\n    hipFree(0);\n    hipHostMalloc(&hostData, sizeof(*hostData));\n    int numStreams = width;\n    if (numStreams == 1)\n        numStreams = 2; // demo needs two streams even if capture only needs 1.\n    stream.resize(numStreams);\n    for (int i = 0; i < numStreams; i++) {\n        hipStreamCreate(&stream[i]);\n    }\n\n    hipEventCreate(&event[0], hipEventDisableTiming);\n    hipEventCreate(&timingEvent[0], 0);\n    hipEventCreate(&timingEvent[1], 0);\n\n    {\n        RANGE(\"warmup\");\n        for (int i = 0; i < width; i++) {\n            empty<<<1, 1, 0, stream[i]>>>();\n        }\n        hipStreamSynchronize(stream[0]);\n\n        auto start = getCpuTime();\n        graph      = createParallelChain(length, width, singleEntry);\n        auto end   = getCpuTime();\n        metricValue.push_back(getMicroSecondDuration(start, end));\n        metricName.push_back(\"capture\");\n        runDemo(graph, length, width);\n    }\n\n    if (outputFmt & 1) {\n        printf(\"length, width, pattern, \");\n        for (int i = 0; i < metricName.size(); i++) {\n            printf(\"%s, \", metricName[i]);\n        }\n        printf(\"\\r\\n\");\n    }\n\n    if (!(outputFmt & 6)) {\n        printf(\"skipping trials since no output is expected\\n\");\n        return 1;\n    }\n\n    std::vector<double> metricTotal;\n    metricTotal.resize(metricValue.size());\n\n    while (length <= maxLength) {\n        for (int i = 0; i < numTrials; i++) {\n            metricName.clear();\n            metricValue.clear();\n            auto start = getCpuTime();\n            graph      = createParallelChain(length, width, singleEntry);\n            auto end   = getCpuTime();\n            metricValue.push_back(getMicroSecondDuration(start, end));\n\n            runDemo(graph, length, width);\n\n            if (outputFmt & 2) {\n                printf(\"%d, %d, %d, \", length, width, pattern);\n                for (int i = 0; i < metricValue.size(); i++) {\n                    printf(\"%0.3f, \", metricValue[i]);\n                }\n                printf(\"\\r\\n\");\n            }\n            if (outputFmt & 4) {\n                for (int i = 0; i < metricTotal.size(); i++) {\n                    metricTotal[i] += metricValue[i];\n                }\n            }\n        }\n\n        if (outputFmt & 4) {\n            printf(\"%d, %d, %d, \", length, width, pattern);\n            for (int i = 0; i < metricTotal.size(); i++) {\n                printf(\"%0.3f, \", metricTotal[i] / numTrials);\n                metricTotal[i] = 0;\n            }\n            printf(\"\\r\\n\");\n        }\n\n        length += stride;\n    }\n\n    hipHostFree(hostData);\n\n    printf(\"\\n\");\n    printf(\"Test passed\\n\");\n    return 0;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/cudaGraphsPerfScaling/cudaGraphPerfScaling.cu"}
{"input": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple test showing performance and usability\n * improvements with large kernel parameters introduced in CUDA 12.1\n */\n#include <cassert>\n#include <chrono>\n#include <iostream>\n\n// Utility includes\n#include <helper_cuda.h>\n\nusing namespace std;\nusing namespace std::chrono;\n\n#define TEST_ITERATIONS     (1000)\n#define TOTAL_PARAMS        (8000) // ints\n#define KERNEL_PARAM_LIMIT  (1024) // ints\n#define CONST_COPIED_PARAMS (TOTAL_PARAMS - KERNEL_PARAM_LIMIT)\n\n__constant__ int excess_params[CONST_COPIED_PARAMS];\n\ntypedef struct\n{\n    int param[KERNEL_PARAM_LIMIT];\n} param_t;\n\ntypedef struct\n{\n    int param[TOTAL_PARAMS];\n} param_large_t;\n\n// Kernel with 4KB kernel parameter limit\n__global__ void kernelDefault(__grid_constant__ const param_t p, int *result)\n{\n    int tmp = 0;\n\n    // accumulate kernel parameters\n    for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {\n        tmp += p.param[i];\n    }\n\n    // accumulate excess values passed via const memory\n    for (int i = 0; i < CONST_COPIED_PARAMS; ++i) {\n        tmp += excess_params[i];\n    }\n\n    *result = tmp;\n}\n\n// Kernel with 32,764 byte kernel parameter limit\n__global__ void kernelLargeParam(__grid_constant__ const param_large_t p, int *result)\n{\n    int tmp = 0;\n\n    // accumulate kernel parameters\n    for (int i = 0; i < TOTAL_PARAMS; ++i) {\n        tmp += p.param[i];\n    }\n\n    *result = tmp;\n}\n\nstatic void report_time(std::chrono::time_point<std::chrono::steady_clock> start,\n                        std::chrono::time_point<std::chrono::steady_clock> end,\n                        int                                                iters)\n{\n    auto usecs = duration_cast<duration<float, microseconds::period>>(end - start);\n    cout << usecs.count() / iters << endl;\n}\n\nint main()\n{\n    int rc;\n    cudaFree(0);\n\n    param_t       p;\n    param_large_t p_large;\n\n    // pageable host memory that holds excess constants passed via constant memory\n    int *copied_params = (int *)malloc(CONST_COPIED_PARAMS * sizeof(int));\n    assert(copied_params);\n\n    // storage for computed result\n    int *d_result;\n    int  h_result;\n    checkCudaErrors(cudaMalloc(&d_result, sizeof(int)));\n\n    int expected_result = 0;\n\n    // fill in data for validation\n    for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {\n        p.param[i] = (i & 0xFF);\n    }\n    for (int i = KERNEL_PARAM_LIMIT; i < TOTAL_PARAMS; ++i) {\n        copied_params[i - KERNEL_PARAM_LIMIT] = (i & 0xFF);\n    }\n    for (int i = 0; i < TOTAL_PARAMS; ++i) {\n        p_large.param[i] = (i & 0xFF);\n        expected_result += (i & 0xFF);\n    }\n\n    // warmup, verify correctness\n    checkCudaErrors(\n        cudaMemcpyToSymbol(excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));\n    kernelDefault<<<1, 1>>>(p, d_result);\n    checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaDeviceSynchronize());\n    if (h_result != expected_result) {\n        std::cout << \"Test failed\" << std::endl;\n        rc = -1;\n        goto Exit;\n    }\n\n    kernelLargeParam<<<1, 1>>>(p_large, d_result);\n    checkCudaErrors(cudaMemcpy(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaDeviceSynchronize());\n    if (h_result != expected_result) {\n        std::cout << \"Test failed\" << std::endl;\n        rc = -1;\n        goto Exit;\n    }\n\n    // benchmark default kernel parameter limit\n    {\n        auto start = steady_clock::now();\n        for (int i = 0; i < TEST_ITERATIONS; ++i) {\n            checkCudaErrors(cudaMemcpyToSymbol(\n                excess_params, copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, cudaMemcpyHostToDevice));\n            kernelDefault<<<1, 1>>>(p, d_result);\n        }\n        checkCudaErrors(cudaDeviceSynchronize());\n        auto end = steady_clock::now();\n        std::cout << \"Kernel 4KB parameter limit - time (us):\";\n        report_time(start, end, TEST_ITERATIONS);\n\n        // benchmark large kernel parameter limit\n        start = steady_clock::now();\n        for (int i = 0; i < TEST_ITERATIONS; ++i) {\n            kernelLargeParam<<<1, 1>>>(p_large, d_result);\n        }\n        checkCudaErrors(cudaDeviceSynchronize());\n        end = steady_clock::now();\n        std::cout << \"Kernel 32,764 byte parameter limit - time (us):\";\n        report_time(start, end, TEST_ITERATIONS);\n    }\n    std::cout << \"Test passed!\" << std::endl;\n    rc = 0;\nExit:\n    // cleanup\n    cudaFree(d_result);\n    free(copied_params);\n    return rc;\n}\n", "output": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This is a simple test showing performance and usability\n * improvements with large kernel parameters introduced in CUDA 12.1\n */\n#include \"hip/hip_runtime.h\"\n#include <cassert>\n#include <chrono>\n#include <iostream>\n\n// Utility includes\n#include <hip/hip_runtime_api.h>\n\nusing namespace std;\nusing namespace std::chrono;\n\n#define TEST_ITERATIONS     (1000)\n#define TOTAL_PARAMS        (8000) // ints\n#define KERNEL_PARAM_LIMIT  (1024) // ints\n#define CONST_COPIED_PARAMS (TOTAL_PARAMS - KERNEL_PARAM_LIMIT)\n\n__constant__ int excess_params[CONST_COPIED_PARAMS];\n\ntypedef struct\n{\n    int param[KERNEL_PARAM_LIMIT];\n} param_t;\n\ntypedef struct\n{\n    int param[TOTAL_PARAMS];\n} param_large_t;\n\n// Kernel with 4KB kernel parameter limit\n__global__ void kernelDefault(__grid_constant__ const param_t p, int *result)\n{\n    int tmp = 0;\n\n    // accumulate kernel parameters\n    for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {\n        tmp += p.param[i];\n    }\n\n    // accumulate excess values passed via const memory\n    for (int i = 0; i < CONST_COPIED_PARAMS; ++i) {\n        tmp += excess_params[i];\n    }\n\n    *result = tmp;\n}\n\n// Kernel with 32,764 byte kernel parameter limit\n__global__ void kernelLargeParam(__grid_constant__ const param_large_t p, int *result)\n{\n    int tmp = 0;\n\n    // accumulate kernel parameters\n    for (int i = 0; i < TOTAL_PARAMS; ++i) {\n        tmp += p.param[i];\n    }\n\n    *result = tmp;\n}\n\nstatic void report_time(std::chrono::time_point<std::chrono::steady_clock> start,\n                        std::chrono::time_point<std::chrono::steady_clock> end,\n                        int                                                iters)\n{\n    auto usecs = duration_cast<duration<float, microseconds::period>>(end - start);\n    cout << usecs.count() / iters << endl;\n}\n\nint main()\n{\n    int rc;\n    hipFree(0);\n\n    param_t       p;\n    param_large_t p_large;\n\n    // pageable host memory that holds excess constants passed via constant memory\n    int *copied_params = (int *)malloc(CONST_COPIED_PARAMS * sizeof(int));\n    assert(copied_params);\n\n    // storage for computed result\n    int *d_result;\n    int  h_result;\n    checkCudaErrors(hipMalloc(&d_result, sizeof(int)));\n\n    int expected_result = 0;\n\n    // fill in data for validation\n    for (int i = 0; i < KERNEL_PARAM_LIMIT; ++i) {\n        p.param[i] = (i & 0xFF);\n    }\n    for (int i = KERNEL_PARAM_LIMIT; i < TOTAL_PARAMS; ++i) {\n        copied_params[i - KERNEL_PARAM_LIMIT] = (i & 0xFF);\n    }\n    for (int i = 0; i < TOTAL_PARAMS; ++i) {\n        p_large.param[i] = (i & 0xFF);\n        expected_result += (i & 0xFF);\n    }\n\n    // warmup, verify correctness\n    checkCudaErrors(\n        hipMemcpyToSymbol(HIP_SYMBOL(excess_params), copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, hipMemcpyHostToDevice));\n    kernelDefault<<<1, 1>>>(p, d_result);\n    checkCudaErrors(hipMemcpy(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost));\n    checkCudaErrors(hipDeviceSynchronize());\n    if (h_result != expected_result) {\n        std::cout << \"Test failed\" << std::endl;\n        rc = -1;\n        goto Exit;\n    }\n\n    kernelLargeParam<<<1, 1>>>(p_large, d_result);\n    checkCudaErrors(hipMemcpy(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost));\n    checkCudaErrors(hipDeviceSynchronize());\n    if (h_result != expected_result) {\n        std::cout << \"Test failed\" << std::endl;\n        rc = -1;\n        goto Exit;\n    }\n\n    // benchmark default kernel parameter limit\n    {\n        auto start = steady_clock::now();\n        for (int i = 0; i < TEST_ITERATIONS; ++i) {\n            checkCudaErrors(hipMemcpyToSymbol(HIP_SYMBOL(excess_params), copied_params, CONST_COPIED_PARAMS * sizeof(int), 0, hipMemcpyHostToDevice));\n            kernelDefault<<<1, 1>>>(p, d_result);\n        }\n        checkCudaErrors(hipDeviceSynchronize());\n        auto end = steady_clock::now();\n        std::cout << \"Kernel 4KB parameter limit - time (us):\";\n        report_time(start, end, TEST_ITERATIONS);\n\n        // benchmark large kernel parameter limit\n        start = steady_clock::now();\n        for (int i = 0; i < TEST_ITERATIONS; ++i) {\n            kernelLargeParam<<<1, 1>>>(p_large, d_result);\n        }\n        checkCudaErrors(hipDeviceSynchronize());\n        end = steady_clock::now();\n        std::cout << \"Kernel 32,764 byte parameter limit - time (us):\";\n        report_time(start, end, TEST_ITERATIONS);\n    }\n    std::cout << \"Test passed!\" << std::endl;\n    rc = 0;\nExit:\n    // cleanup\n    hipFree(d_result);\n    free(copied_params);\n    return rc;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/6_Performance/LargeKernelParameter/LargeKernelParameter.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/pricingengine.h\"\n\nnamespace cg = cooperative_groups;\n#include <curand_kernel.h>\n\n#include \"../inc/asianoption.h\"\n#include \"../inc/cudasharedmem.h\"\n\nusing std::string;\nusing std::vector;\n\n// RNG init kernel\n__global__ void initRNG(curandState *const rngStates, const unsigned int seed)\n{\n    // Determine thread ID\n    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Initialise the RNG\n    curand_init(seed, tid, 0, &rngStates[tid]);\n}\n\n__device__ inline float getPathStep(float &drift, float &diffusion, curandState &state)\n{\n    return expf(drift + diffusion * curand_normal(&state));\n}\n__device__ inline double getPathStep(double &drift, double &diffusion, curandState &state)\n{\n    return exp(drift + diffusion * curand_normal_double(&state));\n}\n\n// Path generation kernel\ntemplate <typename Real>\n__global__ void generatePaths(Real *const                    paths,\n                              curandState *const             rngStates,\n                              const AsianOption<Real> *const option,\n                              const unsigned int             numSims,\n                              const unsigned int             numTimesteps)\n{\n    // Determine thread ID\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Compute parameters\n    Real drift     = (option->r - static_cast<Real>(0.5) * option->sigma * option->sigma) * option->dt;\n    Real diffusion = option->sigma * sqrt(option->dt);\n\n    // Initialise the RNG\n    curandState localState = rngStates[tid];\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        // Shift the output pointer\n        Real *output = paths + i;\n\n        // Simulate the path\n        Real s = static_cast<Real>(1);\n\n        for (unsigned int t = 0; t < numTimesteps; t++, output += numSims) {\n            s *= getPathStep(drift, diffusion, localState);\n            *output = s;\n        }\n    }\n}\n\ntemplate <typename Real> __device__ Real reduce_sum(Real in, cg::thread_block cta)\n{\n    SharedMemory<Real> sdata;\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Valuation kernel\ntemplate <typename Real>\n__global__ void computeValue(Real *const                    values,\n                             const Real *const              paths,\n                             const AsianOption<Real> *const option,\n                             const unsigned int             numSims,\n                             const unsigned int             numTimesteps)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    Real sumPayoffs = static_cast<Real>(0);\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        // Shift the input pointer\n        const Real *path = paths + i;\n        // Compute the arithmetic average\n        Real avg = static_cast<Real>(0);\n\n        for (unsigned int t = 0; t < numTimesteps; t++, path += numSims) {\n            avg += *path;\n        }\n\n        avg = avg * option->spot / numTimesteps;\n        // Compute the payoff\n        Real payoff = avg - option->strike;\n\n        if (option->type == AsianOption<Real>::Put) {\n            payoff = -payoff;\n        }\n\n        payoff = max(static_cast<Real>(0), payoff);\n        // Accumulate payoff locally\n        sumPayoffs += payoff;\n    }\n\n    // Reduce within the block\n    sumPayoffs = reduce_sum<Real>(sumPayoffs, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        values[bid] = sumPayoffs;\n    }\n}\n\ntemplate <typename Real>\nPricingEngine<Real>::PricingEngine(unsigned int numSims,\n                                   unsigned int device,\n                                   unsigned int threadBlockSize,\n                                   unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> void PricingEngine<Real>::operator()(AsianOption<Real> &option)\n{\n    cudaError_t               cudaResult = cudaSuccess;\n    struct cudaDeviceProp     deviceProperties;\n    struct cudaFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    unsigned int deviceVersion = deviceProperties.major * 10 + deviceProperties.minor;\n\n    if (typeid(Real) == typeid(double) && deviceVersion < 13) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = cudaSetDevice(m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, initRNG);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get generatePaths function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, generatePaths<Real>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for generatePaths kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Setup problem on GPU\n    AsianOption<Real> *d_option = 0;\n    cudaResult                  = cudaMalloc((void **)&d_option, sizeof(AsianOption<Real>));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for option data: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    cudaResult = cudaMemcpy(d_option, &option, sizeof(AsianOption<Real>), cudaMemcpyHostToDevice);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy data to device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for paths\n    Real *d_paths      = 0;\n    int   numTimesteps = static_cast<int>(option.tenor / option.dt);\n    cudaResult         = cudaMalloc((void **)&d_paths, m_numSims * numTimesteps * sizeof(Real));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for paths: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for RNG states\n    curandState *d_rngStates = 0;\n    cudaResult               = cudaMalloc((void **)&d_rngStates, grid.x * block.x * sizeof(curandState));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for RNG state: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    Real *d_values = 0;\n    cudaResult     = cudaMalloc((void **)&d_values, grid.x * sizeof(Real));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, m_seed);\n\n    // Generate paths\n    generatePaths<Real><<<grid, block>>>(d_paths, d_rngStates, d_option, m_numSims, numTimesteps);\n\n    // Compute value\n    computeValue<<<grid, block, block.x * sizeof(Real)>>>(d_values, d_paths, d_option, m_numSims, numTimesteps);\n\n    // Copy partial results back\n    vector<Real> values(grid.x);\n    cudaResult = cudaMemcpy(&values[0], d_values, grid.x * sizeof(Real), cudaMemcpyDeviceToHost);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    option.value = std::accumulate(values.begin(), values.end(), static_cast<Real>(0));\n\n    // Compute the mean\n    option.value /= m_numSims;\n\n    // Discount to present value\n    option.value *= exp(-option.r * option.tenor);\n\n    // Cleanup\n    if (d_option) {\n        cudaFree(d_option);\n        d_option = 0;\n    }\n\n    if (d_paths) {\n        cudaFree(d_paths);\n        d_paths = 0;\n    }\n\n    if (d_rngStates) {\n        cudaFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_values) {\n        cudaFree(d_values);\n        d_values = 0;\n    }\n}\n\n// Explicit template instantiation\ntemplate class PricingEngine<float>;\ntemplate class PricingEngine<double>;\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/pricingengine.h\"\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand_kernel.h>\n\n#include \"../inc/asianoption.h\"\n#include \"../inc/cudasharedmem.h\"\n\nusing std::string;\nusing std::vector;\n\n// RNG init kernel\n__global__ void initRNG(hiprandState *const rngStates, const unsigned int seed)\n{\n    // Determine thread ID\n    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Initialise the RNG\n    hiprand_init(seed, tid, 0, &rngStates[tid]);\n}\n\n__device__ inline float getPathStep(float &drift, float &diffusion, hiprandState &state)\n{\n    return expf(drift + diffusion * hiprand_normal(&state));\n}\n__device__ inline double getPathStep(double &drift, double &diffusion, hiprandState &state)\n{\n    return exp(drift + diffusion * hiprand_normal_double(&state));\n}\n\n// Path generation kernel\ntemplate <typename Real>\n__global__ void generatePaths(Real *const                    paths,\n                              hiprandState *const             rngStates,\n                              const AsianOption<Real> *const option,\n                              const unsigned int             numSims,\n                              const unsigned int             numTimesteps)\n{\n    // Determine thread ID\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Compute parameters\n    Real drift     = (option->r - static_cast<Real>(0.5) * option->sigma * option->sigma) * option->dt;\n    Real diffusion = option->sigma * sqrt(option->dt);\n\n    // Initialise the RNG\n    hiprandState localState = rngStates[tid];\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        // Shift the output pointer\n        Real *output = paths + i;\n\n        // Simulate the path\n        Real s = static_cast<Real>(1);\n\n        for (unsigned int t = 0; t < numTimesteps; t++, output += numSims) {\n            s *= getPathStep(drift, diffusion, localState);\n            *output = s;\n        }\n    }\n}\n\ntemplate <typename Real> __device__ Real reduce_sum(Real in, cg::thread_block cta)\n{\n    SharedMemory<Real> sdata;\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Valuation kernel\ntemplate <typename Real>\n__global__ void computeValue(Real *const                    values,\n                             const Real *const              paths,\n                             const AsianOption<Real> *const option,\n                             const unsigned int             numSims,\n                             const unsigned int             numTimesteps)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    Real sumPayoffs = static_cast<Real>(0);\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        // Shift the input pointer\n        const Real *path = paths + i;\n        // Compute the arithmetic average\n        Real avg = static_cast<Real>(0);\n\n        for (unsigned int t = 0; t < numTimesteps; t++, path += numSims) {\n            avg += *path;\n        }\n\n        avg = avg * option->spot / numTimesteps;\n        // Compute the payoff\n        Real payoff = avg - option->strike;\n\n        if (option->type == AsianOption<Real>::Put) {\n            payoff = -payoff;\n        }\n\n        payoff = max(static_cast<Real>(0), payoff);\n        // Accumulate payoff locally\n        sumPayoffs += payoff;\n    }\n\n    // Reduce within the block\n    sumPayoffs = reduce_sum<Real>(sumPayoffs, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        values[bid] = sumPayoffs;\n    }\n}\n\ntemplate <typename Real>\nPricingEngine<Real>::PricingEngine(unsigned int numSims,\n                                   unsigned int device,\n                                   unsigned int threadBlockSize,\n                                   unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> void PricingEngine<Real>::operator()(AsianOption<Real> &option)\n{\n    hipError_t               cudaResult = hipSuccess;\n    struct hipDeviceProp_t     deviceProperties;\n    struct hipFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = hipGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    unsigned int deviceVersion = deviceProperties.major * 10 + deviceProperties.minor;\n\n    if (typeid(Real) == typeid(double) && deviceVersion < 13) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = hipSetDevice(m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(initRNG));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get generatePaths function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(generatePaths<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for generatePaths kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(computeValue<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Setup problem on GPU\n    AsianOption<Real> *d_option = 0;\n    cudaResult                  = hipMalloc((void **)&d_option, sizeof(AsianOption<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for option data: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    cudaResult = hipMemcpy(d_option, &option, sizeof(AsianOption<Real>), hipMemcpyHostToDevice);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy data to device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for paths\n    Real *d_paths      = 0;\n    int   numTimesteps = static_cast<int>(option.tenor / option.dt);\n    cudaResult         = hipMalloc((void **)&d_paths, m_numSims * numTimesteps * sizeof(Real));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for paths: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for RNG states\n    hiprandState *d_rngStates = 0;\n    cudaResult               = hipMalloc((void **)&d_rngStates, grid.x * block.x * sizeof(hiprandState));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for RNG state: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    Real *d_values = 0;\n    cudaResult     = hipMalloc((void **)&d_values, grid.x * sizeof(Real));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, m_seed);\n\n    // Generate paths\n    generatePaths<Real><<<grid, block>>>(d_paths, d_rngStates, d_option, m_numSims, numTimesteps);\n\n    // Compute value\n    computeValue<<<grid, block, block.x * sizeof(Real)>>>(d_values, d_paths, d_option, m_numSims, numTimesteps);\n\n    // Copy partial results back\n    vector<Real> values(grid.x);\n    cudaResult = hipMemcpy(&values[0], d_values, grid.x * sizeof(Real), hipMemcpyDeviceToHost);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    option.value = std::accumulate(values.begin(), values.end(), static_cast<Real>(0));\n\n    // Compute the mean\n    option.value /= m_numSims;\n\n    // Discount to present value\n    option.value *= exp(-option.r * option.tenor);\n\n    // Cleanup\n    if (d_option) {\n        hipFree(d_option);\n        d_option = 0;\n    }\n\n    if (d_paths) {\n        hipFree(d_paths);\n        d_paths = 0;\n    }\n\n    if (d_rngStates) {\n        hipFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_values) {\n        hipFree(d_values);\n        d_values = 0;\n    }\n}\n\n// Explicit template instantiation\ntemplate class PricingEngine<float>;\ntemplate class PricingEngine<double>;\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/MC_SingleAsianOptionP/src/pricingengine.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates Inter Process Communication\n * using one process per GPU for computation.\n */\n\n#include <cuda.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <vector>\n#define CUDA_DRIVER_API 1\n#include \"helper_cuda.h\"\n#include \"helper_cuda_drvapi.h\"\n#include \"helper_multiprocess.h\"\n\nstatic const char shmName[] = \"streamOrderedAllocationIPCshm\";\nstatic const char ipcName[] = \"streamOrderedAllocationIPC_pipe\";\n// For direct NVLINK and PCI-E peers, at max 8 simultaneous peers are allowed\n// For NVSWITCH connected peers like DGX-2, simultaneous peers are not limited\n// in the same way.\n#define MAX_DEVICES (32)\n#define DATA_SIZE   (64ULL << 20ULL) // 64MB\n\n#if defined(__linux__)\n#define cpu_atomic_add32(a, x) __sync_add_and_fetch(a, x)\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n#define cpu_atomic_add32(a, x) InterlockedAdd((volatile LONG *)a, x)\n#else\n#error Unsupported system\n#endif\n\ntypedef struct shmStruct_st\n{\n    size_t                      nprocesses;\n    int                         barrier;\n    int                         sense;\n    cudaMemAllocationHandleType handleType;\n    int                         devices[MAX_DEVICES];\n    cudaMemPoolPtrExportData    exportPtrData[MAX_DEVICES];\n} shmStruct;\n\n__global__ void simpleKernel(char *ptr, int sz, char val)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n\nstatic void barrierWait(volatile int *barrier, volatile int *sense, unsigned int n)\n{\n    int count;\n\n    // Check-in\n    count = cpu_atomic_add32(barrier, 1);\n    if (count == n) // Last one in\n        *sense = 1;\n    while (!*sense)\n        ;\n\n    // Check-out\n    count = cpu_atomic_add32(barrier, -1);\n    if (count == 0) // Last one out\n        *sense = 0;\n    while (*sense)\n        ;\n}\n\nstatic void childProcess(int id)\n{\n    volatile shmStruct *shm = NULL;\n    cudaStream_t        stream;\n    sharedMemoryInfo    info;\n    size_t              procCount, i;\n    int                 blocks  = 0;\n    int                 threads = 128;\n    cudaDeviceProp      prop;\n    std::vector<void *> ptrs;\n    pid_t               pid;\n    char                pidString[20] = {0};\n    char                lshmName[40]  = {0};\n\n    std::vector<char> verification_buffer(DATA_SIZE);\n\n    pid = getppid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"CP: lshmName = %s\\n\", lshmName);\n\n    ipcHandle *ipcChildHandle = NULL;\n    checkIpcErrors(ipcOpenSocket(ipcChildHandle));\n\n    if (sharedMemoryOpen(lshmName, sizeof(shmStruct), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm       = (volatile shmStruct *)info.addr;\n    procCount = shm->nprocesses;\n\n    barrierWait(&shm->barrier, &shm->sense, (unsigned int)(procCount + 1));\n\n    // Receive all allocation handles shared by Parent.\n    std::vector<ShareableHandle> shHandle(shm->nprocesses);\n    checkIpcErrors(ipcRecvShareableHandles(ipcChildHandle, shHandle));\n\n    checkCudaErrors(cudaSetDevice(shm->devices[id]));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, shm->devices[id]));\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, simpleKernel, threads, 0));\n    blocks *= prop.multiProcessorCount;\n\n    std::vector<cudaMemPool_t> pools(shm->nprocesses);\n\n    cudaMemAllocationHandleType handleType = shm->handleType;\n\n    // Import mem pools from all the devices created in the master\n    // process using shareable handles received via socket\n    // and import the pointer to the allocated buffer using\n    // exportData filled in shared memory by the master process.\n    for (i = 0; i < procCount; i++) {\n        checkCudaErrors(cudaMemPoolImportFromShareableHandle(&pools[i], (void *)shHandle[i], handleType, 0));\n\n        cudaMemAccessFlags accessFlags;\n        cudaMemLocation    location;\n        location.type = cudaMemLocationTypeDevice;\n        location.id   = shm->devices[id];\n        checkCudaErrors(cudaMemPoolGetAccess(&accessFlags, pools[i], &location));\n        if (accessFlags != cudaMemAccessFlagsProtReadWrite) {\n            cudaMemAccessDesc desc;\n            memset(&desc, 0, sizeof(cudaMemAccessDesc));\n            desc.location.type = cudaMemLocationTypeDevice;\n            desc.location.id   = shm->devices[id];\n            desc.flags         = cudaMemAccessFlagsProtReadWrite;\n            checkCudaErrors(cudaMemPoolSetAccess(pools[i], &desc, 1));\n        }\n\n        // Import the allocation from each memory pool by iterating over exportData\n        // until import is success.\n        for (int j = 0; j < procCount; j++) {\n            void *ptr = NULL;\n            // Import the allocation using the opaque export data retrieved through\n            // the shared memory\".\n            cudaError_t ret =\n                cudaMemPoolImportPointer(&ptr, pools[i], (cudaMemPoolPtrExportData *)&shm->exportPtrData[j]);\n\n            if (ret == cudaSuccess) {\n                // Pointer import is successful hence add it to the ptrs bag.\n                ptrs.push_back(ptr);\n                break;\n            }\n            else {\n                // Reset failure error received from cudaMemPoolImportPointer\n                // for further try.\n                cudaGetLastError();\n            }\n        }\n        // Since we have imported allocations shared by the parent with us, we can\n        // close this ShareableHandle.\n        checkIpcErrors(ipcCloseShareableHandle(shHandle[i]));\n    }\n\n    // Since we have imported allocations shared by the parent with us, we can\n    // close the socket.\n    checkIpcErrors(ipcCloseSocket(ipcChildHandle));\n\n    // At each iteration of the loop, each sibling process will push work on\n    // their respective devices accessing the next peer mapped buffer allocated\n    // by the master process (these can come from other sibling processes as\n    // well). To coordinate each process' access, we force the stream to wait for\n    // the work already accessing this buffer.\n    for (i = 0; i < procCount; i++) {\n        size_t bufferId = (i + id) % procCount;\n\n        // Push a simple kernel on it\n        simpleKernel<<<blocks, threads, 0, stream>>>((char *)ptrs[bufferId], DATA_SIZE, id);\n        checkCudaErrors(cudaGetLastError());\n        checkCudaErrors(cudaStreamSynchronize(stream));\n\n        // Wait for all my sibling processes to push this stage of their work\n        // before proceeding to the next. This prevents siblings from racing\n        // ahead and clobbering the recorded event or waiting on the wrong\n        // recorded event.\n        barrierWait(&shm->barrier, &shm->sense, (unsigned int)procCount);\n        if (id == 0) {\n            printf(\"Step %lld done\\n\", (unsigned long long)i);\n        }\n    }\n\n    // Now wait for my buffer to be ready so I can copy it locally and verify it\n    checkCudaErrors(cudaMemcpyAsync(&verification_buffer[0], ptrs[id], DATA_SIZE, cudaMemcpyDeviceToHost, stream));\n\n    // And wait for all the queued up work to complete\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    printf(\"Process %d: verifying...\\n\", id);\n\n    // The contents should have the id of the sibling just after me\n    char compareId = (char)((id + 1) % procCount);\n    for (unsigned long long j = 0; j < DATA_SIZE; j++) {\n        if (verification_buffer[j] != compareId) {\n            printf(\"Process %d: Verification mismatch at %lld: %d != %d\\n\",\n                   id,\n                   j,\n                   (int)verification_buffer[j],\n                   (int)compareId);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < procCount; i++) {\n        // Free the memory before the exporter process frees it\n        checkCudaErrors(cudaFreeAsync(ptrs[i], stream));\n    }\n\n    // And wait for all the queued up work to complete\n    checkCudaErrors(cudaStreamSynchronize(stream));\n    checkCudaErrors(cudaStreamDestroy(stream));\n\n    printf(\"Process %d complete!\\n\", id);\n}\n\nstatic void parentProcess(char *app)\n{\n    sharedMemoryInfo            info;\n    int                         devCount, i;\n    volatile shmStruct         *shm = NULL;\n    std::vector<void *>         ptrs;\n    std::vector<Process>        processes;\n    cudaMemAllocationHandleType handleType = cudaMemHandleTypeNone;\n    pid_t                       pid;\n    char                        pidString[20] = {0};\n    char                        lshmName[40]  = {0};\n\n    pid = getpid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"PP: lshmName = %s\\n\", lshmName);\n\n    checkCudaErrors(cudaGetDeviceCount(&devCount));\n    std::vector<CUdevice> devices(devCount);\n    for (i = 0; i < devCount; i++) {\n        cuDeviceGet(&devices[i], i);\n    }\n\n    if (sharedMemoryCreate(lshmName, sizeof(*shm), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm = (volatile shmStruct *)info.addr;\n    memset((void *)shm, 0, sizeof(*shm));\n\n    // Pick all the devices that can access each other's memory for this test\n    // Keep in mind that CUDA has minimal support for fork() without a\n    // corresponding exec() in the child process, but in this case our\n    // spawnProcess will always exec, so no need to worry.\n    for (i = 0; i < devCount; i++) {\n        bool           allPeers = true;\n        cudaDeviceProp prop;\n        checkCudaErrors(cudaGetDeviceProperties(&prop, i));\n\n        int isMemPoolSupported = 0;\n        checkCudaErrors(cudaDeviceGetAttribute(&isMemPoolSupported, cudaDevAttrMemoryPoolsSupported, i));\n        // CUDA IPC is only supported on devices with unified addressing\n        if (!isMemPoolSupported) {\n            printf(\"Device %d does not support cuda memory pools, skipping...\\n\", i);\n            continue;\n        }\n        int supportedHandleTypes = 0;\n        checkCudaErrors(cudaDeviceGetAttribute(&supportedHandleTypes, cudaDevAttrMemoryPoolSupportedHandleTypes, i));\n        if (supportedHandleTypes == 0) {\n            printf(\"Device %d does not support Memory pool based IPC, skipping...\\n\", i);\n            continue;\n        }\n\n        if (handleType == cudaMemHandleTypeNone) {\n            if (supportedHandleTypes & cudaMemHandleTypePosixFileDescriptor) {\n                handleType = cudaMemHandleTypePosixFileDescriptor;\n            }\n            else if (supportedHandleTypes & cudaMemHandleTypeWin32) {\n                handleType = cudaMemHandleTypeWin32;\n            }\n            else {\n                printf(\"Device %d does not support any supported handle types, skipping...\\n\", i);\n                continue;\n            }\n        }\n        else {\n            if ((supportedHandleTypes & handleType) != handleType) {\n                printf(\"Mixed handle types are not supported, waiving test\\n\");\n                exit(EXIT_WAIVED);\n            }\n        }\n        // This sample requires two processes accessing each device, so we need\n        // to ensure exclusive or prohibited mode is not set\n        int computeMode;\n        checkCudaErrors(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, i));\n        if (computeMode != cudaComputeModeDefault) {\n            printf(\"Device %d is in an unsupported compute mode for this sample\\n\", i);\n            continue;\n        }\n#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n        // CUDA IPC on Windows is only supported on TCC\n        if (!prop.tccDriver) {\n            printf(\"Device %d is not in TCC mode\\n\", i);\n            continue;\n        }\n#endif\n\n        for (int j = 0; j < shm->nprocesses; j++) {\n            int canAccessPeerIJ, canAccessPeerJI;\n            checkCudaErrors(cudaDeviceCanAccessPeer(&canAccessPeerJI, shm->devices[j], i));\n            checkCudaErrors(cudaDeviceCanAccessPeer(&canAccessPeerIJ, i, shm->devices[j]));\n            if (!canAccessPeerIJ || !canAccessPeerJI) {\n                allPeers = false;\n                break;\n            }\n        }\n        if (allPeers) {\n            // Enable peers here.  This isn't necessary for IPC, but it will\n            // setup the peers for the device.  For systems that only allow 8\n            // peers per GPU at a time, this acts to remove devices from CanAccessPeer\n            for (int j = 0; j < shm->nprocesses; j++) {\n                checkCudaErrors(cudaSetDevice(i));\n                checkCudaErrors(cudaDeviceEnablePeerAccess(shm->devices[j], 0));\n                checkCudaErrors(cudaSetDevice(shm->devices[j]));\n                checkCudaErrors(cudaDeviceEnablePeerAccess(i, 0));\n            }\n            shm->devices[shm->nprocesses++] = i;\n            if (shm->nprocesses >= MAX_DEVICES)\n                break;\n        }\n        else {\n            printf(\"Device %d is not peer capable with some other selected peers, \"\n                   \"skipping\\n\",\n                   i);\n        }\n    }\n\n    if (shm->nprocesses == 0) {\n        printf(\"No CUDA devices support IPC\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    if (handleType == cudaMemHandleTypeNone) {\n        printf(\"No supported handle types found, waiving test\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::vector<ShareableHandle> shareableHandles(shm->nprocesses);\n    std::vector<cudaStream_t>    streams(shm->nprocesses);\n    std::vector<cudaMemPool_t>   pools(shm->nprocesses);\n\n    // Now allocate memory for each process and fill the shared\n    // memory buffer with the export data and get memPool handles to communicate\n    for (i = 0; i < shm->nprocesses; i++) {\n        void *ptr = NULL;\n        checkCudaErrors(cudaSetDevice(shm->devices[i]));\n        checkCudaErrors(cudaStreamCreateWithFlags(&streams[i], cudaStreamNonBlocking));\n        // Allocate an explicit pool with IPC capabilities\n        cudaMemPoolProps poolProps;\n        memset(&poolProps, 0, sizeof(cudaMemPoolProps));\n        poolProps.allocType   = cudaMemAllocationTypePinned;\n        poolProps.handleTypes = handleType;\n\n        poolProps.location.type = cudaMemLocationTypeDevice;\n        poolProps.location.id   = shm->devices[i];\n\n        checkCudaErrors(cudaMemPoolCreate(&pools[i], &poolProps));\n\n        // Query the shareable handle for the pool\n        // Allocate memory in a stream from the pool just created\n        checkCudaErrors(cudaMallocAsync(&ptr, DATA_SIZE, pools[i], streams[i]));\n\n        checkCudaErrors(cudaMemPoolExportToShareableHandle(&shareableHandles[i], pools[i], handleType, 0));\n\n        // Memset handle to 0 to make sure call to `cudaMemPoolImportPointer` in\n        // childProcess will fail if the following call to\n        // `cudaMemPoolExportPointer` fails.\n        memset((void *)&shm->exportPtrData[i], 0, sizeof(cudaMemPoolPtrExportData));\n        // Get the opaque \u2018bag-of-bits\u2019 representing the allocation\n        checkCudaErrors(cudaMemPoolExportPointer((cudaMemPoolPtrExportData *)&shm->exportPtrData[i], ptr));\n        ptrs.push_back(ptr);\n    }\n\n    shm->handleType = handleType;\n\n    // Launch the child processes!\n    for (i = 0; i < shm->nprocesses; i++) {\n        char        devIdx[10];\n        char *const args[] = {app, devIdx, NULL};\n        Process     process;\n\n        SPRINTF(devIdx, \"%d\", i);\n\n        if (spawnProcess(&process, app, args)) {\n            printf(\"Failed to create process\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        processes.push_back(process);\n    }\n\n    barrierWait(&shm->barrier, &shm->sense, (unsigned int)(shm->nprocesses + 1));\n\n    ipcHandle *ipcParentHandle = NULL;\n    checkIpcErrors(ipcCreateSocket(ipcParentHandle, ipcName, processes));\n    checkIpcErrors(ipcSendShareableHandles(ipcParentHandle, shareableHandles, processes));\n\n    // Close the shareable handles as they are not needed anymore.\n    for (int i = 0; i < shm->nprocesses; i++) {\n        checkIpcErrors(ipcCloseShareableHandle(shareableHandles[i]));\n    }\n    checkIpcErrors(ipcCloseSocket(ipcParentHandle));\n\n    // And wait for them to finish\n    for (i = 0; i < processes.size(); i++) {\n        if (waitProcess(&processes[i]) != EXIT_SUCCESS) {\n            printf(\"Process %d failed!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < shm->nprocesses; i++) {\n        checkCudaErrors(cudaSetDevice(shm->devices[i]));\n        checkCudaErrors(cudaFreeAsync(ptrs[i], streams[i]));\n        checkCudaErrors(cudaStreamSynchronize(streams[i]));\n        checkCudaErrors(cudaMemPoolDestroy(pools[i]));\n    }\n\n    sharedMemoryClose(&info);\n}\n\n// Host code\nint main(int argc, char **argv)\n{\n#if defined(__arm__) || defined(__aarch64__) || defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n    printf(\"Not supported on ARM or Windows\\n\");\n    return EXIT_WAIVED;\n#else\n    if (argc == 1) {\n        parentProcess(argv[0]);\n    }\n    else {\n        childProcess(atoi(argv[1]));\n    }\n    return EXIT_SUCCESS;\n#endif\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates Inter Process Communication\n * using one process per GPU for computation.\n */\n\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <vector>\n#define CUDA_DRIVER_API 1\n#include \"hip/hip_runtime_api.h\"\n#include \"helper_cuda_drvapi.h\"\n#include \"helper_multiprocess.h\"\n\nstatic const char shmName[] = \"streamOrderedAllocationIPCshm\";\nstatic const char ipcName[] = \"streamOrderedAllocationIPC_pipe\";\n// For direct NVLINK and PCI-E peers, at max 8 simultaneous peers are allowed\n// For NVSWITCH connected peers like DGX-2, simultaneous peers are not limited\n// in the same way.\n#define MAX_DEVICES (32)\n#define DATA_SIZE   (64ULL << 20ULL) // 64MB\n\n#if defined(__linux__)\n#define cpu_atomic_add32(a, x) __sync_add_and_fetch(a, x)\n#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n#define cpu_atomic_add32(a, x) InterlockedAdd((volatile LONG *)a, x)\n#else\n#error Unsupported system\n#endif\n\ntypedef struct shmStruct_st\n{\n    size_t                      nprocesses;\n    int                         barrier;\n    int                         sense;\n    hipMemAllocationHandleType handleType;\n    int                         devices[MAX_DEVICES];\n    hipMemPoolPtrExportData    exportPtrData[MAX_DEVICES];\n} shmStruct;\n\n__global__ void simpleKernel(char *ptr, int sz, char val)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; idx < sz; idx += (gridDim.x * blockDim.x)) {\n        ptr[idx] = val;\n    }\n}\n\nstatic void barrierWait(volatile int *barrier, volatile int *sense, unsigned int n)\n{\n    int count;\n\n    // Check-in\n    count = cpu_atomic_add32(barrier, 1);\n    if (count == n) // Last one in\n        *sense = 1;\n    while (!*sense)\n        ;\n\n    // Check-out\n    count = cpu_atomic_add32(barrier, -1);\n    if (count == 0) // Last one out\n        *sense = 0;\n    while (*sense)\n        ;\n}\n\nstatic void childProcess(int id)\n{\n    volatile shmStruct *shm = NULL;\n    hipStream_t        stream;\n    sharedMemoryInfo    info;\n    size_t              procCount, i;\n    int                 blocks  = 0;\n    int                 threads = 128;\n    hipDeviceProp_t      prop;\n    std::vector<void *> ptrs;\n    pid_t               pid;\n    char                pidString[20] = {0};\n    char                lshmName[40]  = {0};\n\n    std::vector<char> verification_buffer(DATA_SIZE);\n\n    pid = getppid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"CP: lshmName = %s\\n\", lshmName);\n\n    ipcHandle *ipcChildHandle = NULL;\n    checkIpcErrors(ipcOpenSocket(ipcChildHandle));\n\n    if (sharedMemoryOpen(lshmName, sizeof(shmStruct), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm       = (volatile shmStruct *)info.addr;\n    procCount = shm->nprocesses;\n\n    barrierWait(&shm->barrier, &shm->sense, (unsigned int)(procCount + 1));\n\n    // Receive all allocation handles shared by Parent.\n    std::vector<ShareableHandle> shHandle(shm->nprocesses);\n    checkIpcErrors(ipcRecvShareableHandles(ipcChildHandle, shHandle));\n\n    checkCudaErrors(hipSetDevice(shm->devices[id]));\n    checkCudaErrors(hipGetDeviceProperties(&prop, shm->devices[id]));\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks, simpleKernel, threads, 0));\n    blocks *= prop.multiProcessorCount;\n\n    std::vector<hipMemPool_t> pools(shm->nprocesses);\n\n    hipMemAllocationHandleType handleType = shm->handleType;\n\n    // Import mem pools from all the devices created in the master\n    // process using shareable handles received via socket\n    // and import the pointer to the allocated buffer using\n    // exportData filled in shared memory by the master process.\n    for (i = 0; i < procCount; i++) {\n        checkCudaErrors(hipMemPoolImportFromShareableHandle(&pools[i], (void *)shHandle[i], handleType, 0));\n\n        hipMemAccessFlags accessFlags;\n        hipMemLocation    location;\n        location.type = hipMemLocationTypeDevice;\n        location.id   = shm->devices[id];\n        checkCudaErrors(hipMemPoolGetAccess(&accessFlags, pools[i], &location));\n        if (accessFlags != hipMemAccessFlagsProtReadWrite) {\n            hipMemAccessDesc desc;\n            memset(&desc, 0, sizeof(hipMemAccessDesc));\n            desc.location.type = hipMemLocationTypeDevice;\n            desc.location.id   = shm->devices[id];\n            desc.flags         = hipMemAccessFlagsProtReadWrite;\n            checkCudaErrors(hipMemPoolSetAccess(pools[i], &desc, 1));\n        }\n\n        // Import the allocation from each memory pool by iterating over exportData\n        // until import is success.\n        for (int j = 0; j < procCount; j++) {\n            void *ptr = NULL;\n            // Import the allocation using the opaque export data retrieved through\n            // the shared memory\".\n            hipError_t ret =\n                hipMemPoolImportPointer(&ptr, pools[i], (hipMemPoolPtrExportData *)&shm->exportPtrData[j]);\n\n            if (ret == hipSuccess) {\n                // Pointer import is successful hence add it to the ptrs bag.\n                ptrs.push_back(ptr);\n                break;\n            }\n            else {\n                // Reset failure error received from cudaMemPoolImportPointer\n                // for further try.\n                hipGetLastError();\n            }\n        }\n        // Since we have imported allocations shared by the parent with us, we can\n        // close this ShareableHandle.\n        checkIpcErrors(ipcCloseShareableHandle(shHandle[i]));\n    }\n\n    // Since we have imported allocations shared by the parent with us, we can\n    // close the socket.\n    checkIpcErrors(ipcCloseSocket(ipcChildHandle));\n\n    // At each iteration of the loop, each sibling process will push work on\n    // their respective devices accessing the next peer mapped buffer allocated\n    // by the master process (these can come from other sibling processes as\n    // well). To coordinate each process' access, we force the stream to wait for\n    // the work already accessing this buffer.\n    for (i = 0; i < procCount; i++) {\n        size_t bufferId = (i + id) % procCount;\n\n        // Push a simple kernel on it\n        simpleKernel<<<blocks, threads, 0, stream>>>((char *)ptrs[bufferId], DATA_SIZE, id);\n        checkCudaErrors(hipGetLastError());\n        checkCudaErrors(hipStreamSynchronize(stream));\n\n        // Wait for all my sibling processes to push this stage of their work\n        // before proceeding to the next. This prevents siblings from racing\n        // ahead and clobbering the recorded event or waiting on the wrong\n        // recorded event.\n        barrierWait(&shm->barrier, &shm->sense, (unsigned int)procCount);\n        if (id == 0) {\n            printf(\"Step %lld done\\n\", (unsigned long long)i);\n        }\n    }\n\n    // Now wait for my buffer to be ready so I can copy it locally and verify it\n    checkCudaErrors(hipMemcpyAsync(&verification_buffer[0], ptrs[id], DATA_SIZE, hipMemcpyDeviceToHost, stream));\n\n    // And wait for all the queued up work to complete\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    printf(\"Process %d: verifying...\\n\", id);\n\n    // The contents should have the id of the sibling just after me\n    char compareId = (char)((id + 1) % procCount);\n    for (unsigned long long j = 0; j < DATA_SIZE; j++) {\n        if (verification_buffer[j] != compareId) {\n            printf(\"Process %d: Verification mismatch at %lld: %d != %d\\n\",\n                   id,\n                   j,\n                   (int)verification_buffer[j],\n                   (int)compareId);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < procCount; i++) {\n        // Free the memory before the exporter process frees it\n        checkCudaErrors(hipFreeAsync(ptrs[i], stream));\n    }\n\n    // And wait for all the queued up work to complete\n    checkCudaErrors(hipStreamSynchronize(stream));\n    checkCudaErrors(hipStreamDestroy(stream));\n\n    printf(\"Process %d complete!\\n\", id);\n}\n\nstatic void parentProcess(char *app)\n{\n    sharedMemoryInfo            info;\n    int                         devCount, i;\n    volatile shmStruct         *shm = NULL;\n    std::vector<void *>         ptrs;\n    std::vector<Process>        processes;\n    hipMemAllocationHandleType handleType = hipMemHandleTypeNone;\n    pid_t                       pid;\n    char                        pidString[20] = {0};\n    char                        lshmName[40]  = {0};\n\n    pid = getpid();\n    snprintf(pidString, sizeof(pidString), \"%d\", pid);\n    strcat(lshmName, shmName);\n    strcat(lshmName, pidString);\n\n    printf(\"PP: lshmName = %s\\n\", lshmName);\n\n    checkCudaErrors(hipGetDeviceCount(&devCount));\n    std::vector<hipDevice_t> devices(devCount);\n    for (i = 0; i < devCount; i++) {\n        hipDeviceGet(&devices[i], i);\n    }\n\n    if (sharedMemoryCreate(lshmName, sizeof(*shm), &info) != 0) {\n        printf(\"Failed to create shared memory slab\\n\");\n        exit(EXIT_FAILURE);\n    }\n    shm = (volatile shmStruct *)info.addr;\n    memset((void *)shm, 0, sizeof(*shm));\n\n    // Pick all the devices that can access each other's memory for this test\n    // Keep in mind that CUDA has minimal support for fork() without a\n    // corresponding exec() in the child process, but in this case our\n    // spawnProcess will always exec, so no need to worry.\n    for (i = 0; i < devCount; i++) {\n        bool           allPeers = true;\n        hipDeviceProp_t prop;\n        checkCudaErrors(hipGetDeviceProperties(&prop, i));\n\n        int isMemPoolSupported = 0;\n        checkCudaErrors(hipDeviceGetAttribute(&isMemPoolSupported, hipDeviceAttributeMemoryPoolsSupported, i));\n        // CUDA IPC is only supported on devices with unified addressing\n        if (!isMemPoolSupported) {\n            printf(\"Device %d does not support cuda memory pools, skipping...\\n\", i);\n            continue;\n        }\n        int supportedHandleTypes = 0;\n        checkCudaErrors(hipDeviceGetAttribute(&supportedHandleTypes, cudaDevAttrMemoryPoolSupportedHandleTypes, i));\n        if (supportedHandleTypes == 0) {\n            printf(\"Device %d does not support Memory pool based IPC, skipping...\\n\", i);\n            continue;\n        }\n\n        if (handleType == hipMemHandleTypeNone) {\n            if (supportedHandleTypes & hipMemHandleTypePosixFileDescriptor) {\n                handleType = hipMemHandleTypePosixFileDescriptor;\n            }\n            else if (supportedHandleTypes & hipMemHandleTypeWin32) {\n                handleType = hipMemHandleTypeWin32;\n            }\n            else {\n                printf(\"Device %d does not support any supported handle types, skipping...\\n\", i);\n                continue;\n            }\n        }\n        else {\n            if ((supportedHandleTypes & handleType) != handleType) {\n                printf(\"Mixed handle types are not supported, waiving test\\n\");\n                exit(EXIT_WAIVED);\n            }\n        }\n        // This sample requires two processes accessing each device, so we need\n        // to ensure exclusive or prohibited mode is not set\n        int computeMode;\n        checkCudaErrors(hipDeviceGetAttribute(&computeMode, hipDeviceAttributeComputeMode, i));\n        if (computeMode != hipComputeModeDefault) {\n            printf(\"Device %d is in an unsupported compute mode for this sample\\n\", i);\n            continue;\n        }\n#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n        // CUDA IPC on Windows is only supported on TCC\n        if (!prop.tccDriver) {\n            printf(\"Device %d is not in TCC mode\\n\", i);\n            continue;\n        }\n#endif\n\n        for (int j = 0; j < shm->nprocesses; j++) {\n            int canAccessPeerIJ, canAccessPeerJI;\n            checkCudaErrors(hipDeviceCanAccessPeer(&canAccessPeerJI, shm->devices[j], i));\n            checkCudaErrors(hipDeviceCanAccessPeer(&canAccessPeerIJ, i, shm->devices[j]));\n            if (!canAccessPeerIJ || !canAccessPeerJI) {\n                allPeers = false;\n                break;\n            }\n        }\n        if (allPeers) {\n            // Enable peers here.  This isn't necessary for IPC, but it will\n            // setup the peers for the device.  For systems that only allow 8\n            // peers per GPU at a time, this acts to remove devices from CanAccessPeer\n            for (int j = 0; j < shm->nprocesses; j++) {\n                checkCudaErrors(hipSetDevice(i));\n                checkCudaErrors(hipDeviceEnablePeerAccess(shm->devices[j], 0));\n                checkCudaErrors(hipSetDevice(shm->devices[j]));\n                checkCudaErrors(hipDeviceEnablePeerAccess(i, 0));\n            }\n            shm->devices[shm->nprocesses++] = i;\n            if (shm->nprocesses >= MAX_DEVICES)\n                break;\n        }\n        else {\n            printf(\"Device %d is not peer capable with some other selected peers, \"\n                   \"skipping\\n\",\n                   i);\n        }\n    }\n\n    if (shm->nprocesses == 0) {\n        printf(\"No CUDA devices support IPC\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    if (handleType == hipMemHandleTypeNone) {\n        printf(\"No supported handle types found, waiving test\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::vector<ShareableHandle> shareableHandles(shm->nprocesses);\n    std::vector<hipStream_t>    streams(shm->nprocesses);\n    std::vector<hipMemPool_t>   pools(shm->nprocesses);\n\n    // Now allocate memory for each process and fill the shared\n    // memory buffer with the export data and get memPool handles to communicate\n    for (i = 0; i < shm->nprocesses; i++) {\n        void *ptr = NULL;\n        checkCudaErrors(hipSetDevice(shm->devices[i]));\n        checkCudaErrors(hipStreamCreateWithFlags(&streams[i], hipStreamNonBlocking));\n        // Allocate an explicit pool with IPC capabilities\n        hipMemPoolProps poolProps;\n        memset(&poolProps, 0, sizeof(hipMemPoolProps));\n        poolProps.allocType   = hipMemAllocationTypePinned;\n        poolProps.handleTypes = handleType;\n\n        poolProps.location.type = hipMemLocationTypeDevice;\n        poolProps.location.id   = shm->devices[i];\n\n        checkCudaErrors(hipMemPoolCreate(&pools[i], &poolProps));\n\n        // Query the shareable handle for the pool\n        // Allocate memory in a stream from the pool just created\n        checkCudaErrors(hipMallocAsync(&ptr, DATA_SIZE, pools[i], streams[i]));\n\n        checkCudaErrors(hipMemPoolExportToShareableHandle(&shareableHandles[i], pools[i], handleType, 0));\n\n        // Memset handle to 0 to make sure call to `cudaMemPoolImportPointer` in\n        // childProcess will fail if the following call to\n        // `cudaMemPoolExportPointer` fails.\n        memset((void *)&shm->exportPtrData[i], 0, sizeof(hipMemPoolPtrExportData));\n        // Get the opaque \u2018bag-of-bits\u2019 representing the allocation\n        checkCudaErrors(hipMemPoolExportPointer((hipMemPoolPtrExportData *)&shm->exportPtrData[i], ptr));\n        ptrs.push_back(ptr);\n    }\n\n    shm->handleType = handleType;\n\n    // Launch the child processes!\n    for (i = 0; i < shm->nprocesses; i++) {\n        char        devIdx[10];\n        char *const args[] = {app, devIdx, NULL};\n        Process     process;\n\n        SPRINTF(devIdx, \"%d\", i);\n\n        if (spawnProcess(&process, app, args)) {\n            printf(\"Failed to create process\\n\");\n            exit(EXIT_FAILURE);\n        }\n\n        processes.push_back(process);\n    }\n\n    barrierWait(&shm->barrier, &shm->sense, (unsigned int)(shm->nprocesses + 1));\n\n    ipcHandle *ipcParentHandle = NULL;\n    checkIpcErrors(ipcCreateSocket(ipcParentHandle, ipcName, processes));\n    checkIpcErrors(ipcSendShareableHandles(ipcParentHandle, shareableHandles, processes));\n\n    // Close the shareable handles as they are not needed anymore.\n    for (int i = 0; i < shm->nprocesses; i++) {\n        checkIpcErrors(ipcCloseShareableHandle(shareableHandles[i]));\n    }\n    checkIpcErrors(ipcCloseSocket(ipcParentHandle));\n\n    // And wait for them to finish\n    for (i = 0; i < processes.size(); i++) {\n        if (waitProcess(&processes[i]) != EXIT_SUCCESS) {\n            printf(\"Process %d failed!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Clean up!\n    for (i = 0; i < shm->nprocesses; i++) {\n        checkCudaErrors(hipSetDevice(shm->devices[i]));\n        checkCudaErrors(hipFreeAsync(ptrs[i], streams[i]));\n        checkCudaErrors(hipStreamSynchronize(streams[i]));\n        checkCudaErrors(hipMemPoolDestroy(pools[i]));\n    }\n\n    sharedMemoryClose(&info);\n}\n\n// Host code\nint main(int argc, char **argv)\n{\n#if defined(__arm__) || defined(__aarch64__) || defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)\n    printf(\"Not supported on ARM or Windows\\n\");\n    return EXIT_WAIVED;\n#else\n    if (argc == 1) {\n        parentProcess(argv[0]);\n    }\n    else {\n        childProcess(atoi(argv[1]));\n    }\n    return EXIT_SUCCESS;\n#endif\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/streamOrderedAllocationIPC/streamOrderedAllocationIPC.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n  Parallel reduction\n\n  This sample shows how to perform a reduction operation on an array of values\n  to produce a single value in a single kernel (as opposed to two or more\n  kernel calls as shown in the \"reduction\" CUDA Sample).  Single-pass\n  reduction requires Cooperative Groups.\n\n  Reductions are a very common computation in parallel algorithms.  Any time\n  an array of values needs to be reduced to a single value using a binary\n  associative operator, a reduction can be used.  Example applications include\n  statistics computations such as mean and standard deviation, and image\n  processing applications such as finding the total luminance of an\n  image.\n\n  This code performs sum reductions, but any associative operator such as\n  min() or max() could also be used.\n\n  It assumes the input size is a power of 2.\n\n  COMMAND LINE ARGUMENTS\n\n  \"--n=<N>\"         :Specify the number of elements to reduce (default 33554432)\n  \"--threads=<N>\"   :Specify the number of threads per block (default 128)\n  \"--maxblocks=<N>\" :Specify the maximum number of thread blocks to launch\n (kernel 6 only, default 64)\n*/\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\nconst char *sSDKsample = \"reductionMultiBlockCG\";\n\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <cuda_runtime_api.h>\n\nnamespace cg = cooperative_groups;\n\n/*\n  Parallel sum reduction using shared memory\n  - takes log(n) steps for n input elements\n  - uses n/2 threads\n  - only works for power-of-2 arrays\n\n  This version adds multiple elements per thread sequentially. This reduces the\n  overall cost of the algorithm while keeping the work complexity O(n) and the\n  step complexity O(log n).\n  (Brent's Theorem optimization)\n\n  See the CUDA SDK \"reduction\" sample for more information.\n*/\n\n__device__ void reduceBlock(double *sdata, const cg::thread_block &cta)\n{\n    const unsigned int        tid    = cta.thread_rank();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    sdata[tid] = cg::reduce(tile32, sdata[tid], cg::plus<double>());\n    cg::sync(cta);\n\n    double beta = 0.0;\n    if (cta.thread_rank() == 0) {\n        beta = 0;\n        for (int i = 0; i < blockDim.x; i += tile32.size()) {\n            beta += sdata[i];\n        }\n        sdata[0] = beta;\n    }\n    cg::sync(cta);\n}\n\n// This reduction kernel reduces an arbitrary size array in a single kernel\n// invocation\n//\n// For more details on the reduction algorithm (notably the multi-pass\n// approach), see the \"reduction\" sample in the CUDA SDK.\nextern \"C\" __global__ void reduceSinglePassMultiBlockCG(const float *g_idata, float *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block block = cg::this_thread_block();\n    cg::grid_group   grid  = cg::this_grid();\n\n    extern double __shared__ sdata[];\n\n    // Stride over grid and add the values to a shared memory buffer\n    sdata[block.thread_rank()] = 0;\n\n    for (int i = grid.thread_rank(); i < n; i += grid.size()) {\n        sdata[block.thread_rank()] += g_idata[i];\n    }\n\n    cg::sync(block);\n\n    // Reduce each block (called once per block)\n    reduceBlock(sdata, block);\n    // Write out the result to global memory\n    if (block.thread_rank() == 0) {\n        g_odata[blockIdx.x] = sdata[0];\n    }\n    cg::sync(grid);\n\n    if (grid.thread_rank() == 0) {\n        for (int block = 1; block < gridDim.x; block++) {\n            g_odata[0] += g_odata[block];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Wrapper function for kernel launch\n////////////////////////////////////////////////////////////////////////////////\nvoid call_reduceSinglePassMultiBlockCG(int size, int threads, int numBlocks, float *d_idata, float *d_odata)\n{\n    int   smemSize     = threads * sizeof(double);\n    void *kernelArgs[] = {\n        (void *)&d_idata,\n        (void *)&d_odata,\n        (void *)&size,\n    };\n\n    dim3 dimBlock(threads, 1, 1);\n    dim3 dimGrid(numBlocks, 1, 1);\n\n    cudaLaunchCooperativeKernel((void *)reduceSinglePassMultiBlockCG, dimGrid, dimBlock, kernelArgs, smemSize, NULL);\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nbool runTest(int argc, char **argv, int device);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    cudaDeviceProp deviceProp = {0};\n    int            dev;\n\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    dev = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, dev));\n    if (!deviceProp.cooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               dev);\n        exit(EXIT_WAIVED);\n    }\n\n    bool bTestPassed = false;\n    bTestPassed      = runTest(argc, argv, dev);\n\n    exit(bTestPassed ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute sum reduction on CPU\n//! We use Kahan summation for an accurate sum of large arrays.\n//! http://en.wikipedia.org/wiki/Kahan_summation_algorithm\n//!\n//! @param data       pointer to input data\n//! @param size       number of input data elements\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> T reduceCPU(T *data, int size)\n{\n    T sum = data[0];\n    T c   = (T)0.0;\n\n    for (int i = 1; i < size; i++) {\n        T y = data[i] - c;\n        T t = sum + y;\n        c   = (t - sum) - y;\n        sum = t;\n    }\n\n    return sum;\n}\n\nunsigned int nextPow2(unsigned int x)\n{\n    --x;\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    return ++x;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Compute the number of threads and blocks to use for the reduction\n// We set threads / block to the minimum of maxThreads and n/2.\n////////////////////////////////////////////////////////////////////////////////\nvoid getNumBlocksAndThreads(int n, int maxBlocks, int maxThreads, int &blocks, int &threads)\n{\n    if (n == 1) {\n        threads = 1;\n        blocks  = 1;\n    }\n    else {\n        checkCudaErrors(cudaOccupancyMaxPotentialBlockSize(&blocks, &threads, reduceSinglePassMultiBlockCG));\n    }\n\n    blocks = min(maxBlocks, blocks);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// This function performs a reduction of the input data multiple times and\n// measures the average reduction time.\n////////////////////////////////////////////////////////////////////////////////\nfloat benchmarkReduce(int                 n,\n                      int                 numThreads,\n                      int                 numBlocks,\n                      int                 maxThreads,\n                      int                 maxBlocks,\n                      int                 testIterations,\n                      StopWatchInterface *timer,\n                      float              *h_odata,\n                      float              *d_idata,\n                      float              *d_odata)\n{\n    float       gpu_result = 0;\n    cudaError_t error;\n\n    printf(\"\\nLaunching %s kernel\\n\", \"SinglePass Multi Block Cooperative Groups\");\n    for (int i = 0; i < testIterations; ++i) {\n        gpu_result = 0;\n        sdkStartTimer(&timer);\n        call_reduceSinglePassMultiBlockCG(n, numThreads, numBlocks, d_idata, d_odata);\n        cudaDeviceSynchronize();\n        sdkStopTimer(&timer);\n    }\n\n    // copy final sum from device to host\n    error = cudaMemcpy(&gpu_result, d_odata, sizeof(float), cudaMemcpyDeviceToHost);\n    checkCudaErrors(error);\n\n    return gpu_result;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The main function which runs the reduction test.\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, int device)\n{\n    int  size        = 1 << 25; // number of elements to reduce\n    bool bTestPassed = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"n\")) {\n        size = getCmdLineArgumentInt(argc, (const char **)argv, \"n\");\n    }\n\n    printf(\"%d elements\\n\", size);\n\n    // Set the device to be used\n    cudaDeviceProp prop = {0};\n    checkCudaErrors(cudaSetDevice(device));\n    checkCudaErrors(cudaGetDeviceProperties(&prop, device));\n\n    // create random input data on CPU\n    unsigned int bytes = size * sizeof(float);\n\n    float *h_idata = (float *)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        // Keep the numbers small so we don't get truncation error in the sum\n        h_idata[i] = (rand() & 0xFF) / (float)RAND_MAX;\n    }\n\n    // Determine the launch configuration (threads, blocks)\n    int maxThreads = 0;\n    int maxBlocks  = 0;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"threads\")) {\n        maxThreads = getCmdLineArgumentInt(argc, (const char **)argv, \"threads\");\n    }\n    else {\n        maxThreads = prop.maxThreadsPerBlock;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"maxblocks\")) {\n        maxBlocks = getCmdLineArgumentInt(argc, (const char **)argv, \"maxblocks\");\n    }\n    else {\n        maxBlocks = prop.multiProcessorCount * (prop.maxThreadsPerMultiProcessor / prop.maxThreadsPerBlock);\n    }\n\n    int numBlocks  = 0;\n    int numThreads = 0;\n    getNumBlocksAndThreads(size, maxBlocks, maxThreads, numBlocks, numThreads);\n\n    // We calculate the occupancy to know how many block can actually fit on the\n    // GPU\n    int numBlocksPerSm = 0;\n    checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n        &numBlocksPerSm, reduceSinglePassMultiBlockCG, numThreads, numThreads * sizeof(double)));\n\n    int numSms = prop.multiProcessorCount;\n    if (numBlocks > numBlocksPerSm * numSms) {\n        numBlocks = numBlocksPerSm * numSms;\n    }\n    printf(\"numThreads: %d\\n\", numThreads);\n    printf(\"numBlocks: %d\\n\", numBlocks);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(numBlocks * sizeof(float));\n\n    // allocate device memory and data\n    float *d_idata = NULL;\n    float *d_odata = NULL;\n\n    checkCudaErrors(cudaMalloc((void **)&d_idata, bytes));\n    checkCudaErrors(cudaMalloc((void **)&d_odata, numBlocks * sizeof(float)));\n\n    // copy data directly to device memory\n    checkCudaErrors(cudaMemcpy(d_idata, h_idata, bytes, cudaMemcpyHostToDevice));\n    checkCudaErrors(cudaMemcpy(d_odata, h_idata, numBlocks * sizeof(float), cudaMemcpyHostToDevice));\n\n    int testIterations = 100;\n\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n\n    float gpu_result = 0;\n\n    gpu_result = benchmarkReduce(\n        size, numThreads, numBlocks, maxThreads, maxBlocks, testIterations, timer, h_odata, d_idata, d_odata);\n\n    float reduceTime = sdkGetAverageTimerValue(&timer);\n    printf(\"Average time: %f ms\\n\", reduceTime);\n    printf(\"Bandwidth:    %f GB/s\\n\\n\", (size * sizeof(int)) / (reduceTime * 1.0e6));\n\n    // compute reference solution\n    float cpu_result = reduceCPU<float>(h_idata, size);\n    printf(\"GPU result = %0.12f\\n\", gpu_result);\n    printf(\"CPU result = %0.12f\\n\", cpu_result);\n\n    double threshold = 1e-8 * size;\n    double diff      = abs((double)gpu_result - (double)cpu_result);\n    bTestPassed      = (diff < threshold);\n\n    // cleanup\n    sdkDeleteTimer(&timer);\n\n    free(h_idata);\n    free(h_odata);\n    cudaFree(d_idata);\n    cudaFree(d_odata);\n\n    return bTestPassed;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n  Parallel reduction\n\n  This sample shows how to perform a reduction operation on an array of values\n  to produce a single value in a single kernel (as opposed to two or more\n  kernel calls as shown in the \"reduction\" CUDA Sample).  Single-pass\n  reduction requires Cooperative Groups.\n\n  Reductions are a very common computation in parallel algorithms.  Any time\n  an array of values needs to be reduced to a single value using a binary\n  associative operator, a reduction can be used.  Example applications include\n  statistics computations such as mean and standard deviation, and image\n  processing applications such as finding the total luminance of an\n  image.\n\n  This code performs sum reductions, but any associative operator such as\n  min() or max() could also be used.\n\n  It assumes the input size is a power of 2.\n\n  COMMAND LINE ARGUMENTS\n\n  \"--n=<N>\"         :Specify the number of elements to reduce (default 33554432)\n  \"--threads=<N>\"   :Specify the number of threads per block (default 128)\n  \"--maxblocks=<N>\" :Specify the maximum number of thread blocks to launch\n (kernel 6 only, default 64)\n*/\n\n// includes, system\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n// includes, project\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\nconst char *sSDKsample = \"reductionMultiBlockCG\";\n\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n\nnamespace cg = cooperative_groups;\n\n/*\n  Parallel sum reduction using shared memory\n  - takes log(n) steps for n input elements\n  - uses n/2 threads\n  - only works for power-of-2 arrays\n\n  This version adds multiple elements per thread sequentially. This reduces the\n  overall cost of the algorithm while keeping the work complexity O(n) and the\n  step complexity O(log n).\n  (Brent's Theorem optimization)\n\n  See the CUDA SDK \"reduction\" sample for more information.\n*/\n\n__device__ void reduceBlock(double *sdata, const cg::thread_block &cta)\n{\n    const unsigned int        tid    = cta.thread_rank();\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    sdata[tid] = cg::reduce(tile32, sdata[tid], cg::plus<double>());\n    cg::sync(cta);\n\n    double beta = 0.0;\n    if (cta.thread_rank() == 0) {\n        beta = 0;\n        for (int i = 0; i < blockDim.x; i += tile32.size()) {\n            beta += sdata[i];\n        }\n        sdata[0] = beta;\n    }\n    cg::sync(cta);\n}\n\n// This reduction kernel reduces an arbitrary size array in a single kernel\n// invocation\n//\n// For more details on the reduction algorithm (notably the multi-pass\n// approach), see the \"reduction\" sample in the CUDA SDK.\nextern \"C\" __global__ void reduceSinglePassMultiBlockCG(const float *g_idata, float *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block block = cg::this_thread_block();\n    cg::grid_group   grid  = cg::this_grid();\n\n    extern double __shared__ sdata[];\n\n    // Stride over grid and add the values to a shared memory buffer\n    sdata[block.thread_rank()] = 0;\n\n    for (int i = grid.thread_rank(); i < n; i += grid.size()) {\n        sdata[block.thread_rank()] += g_idata[i];\n    }\n\n    cg::sync(block);\n\n    // Reduce each block (called once per block)\n    reduceBlock(sdata, block);\n    // Write out the result to global memory\n    if (block.thread_rank() == 0) {\n        g_odata[blockIdx.x] = sdata[0];\n    }\n    cg::sync(grid);\n\n    if (grid.thread_rank() == 0) {\n        for (int block = 1; block < gridDim.x; block++) {\n            g_odata[0] += g_odata[block];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Wrapper function for kernel launch\n////////////////////////////////////////////////////////////////////////////////\nvoid call_reduceSinglePassMultiBlockCG(int size, int threads, int numBlocks, float *d_idata, float *d_odata)\n{\n    int   smemSize     = threads * sizeof(double);\n    void *kernelArgs[] = {\n        (void *)&d_idata,\n        (void *)&d_odata,\n        (void *)&size,\n    };\n\n    dim3 dimBlock(threads, 1, 1);\n    dim3 dimGrid(numBlocks, 1, 1);\n\n    hipLaunchCooperativeKernel(reinterpret_cast<const void*>((void *)reduceSinglePassMultiBlockCG), dimGrid, dimBlock, kernelArgs, smemSize, NULL);\n    // check if kernel execution generated an error\n    getLastCudaError(\"Kernel execution failed\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// declaration, forward\nbool runTest(int argc, char **argv, int device);\n\n////////////////////////////////////////////////////////////////////////////////\n// Program main\n////////////////////////////////////////////////////////////////////////////////\nint main(int argc, char **argv)\n{\n    hipDeviceProp_t deviceProp = {0};\n    int            dev;\n\n    printf(\"%s Starting...\\n\\n\", sSDKsample);\n\n    dev = findCudaDevice(argc, (const char **)argv);\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, dev));\n    if (!deviceProp.cooperativeLaunch) {\n        printf(\"\\nSelected GPU (%d) does not support Cooperative Kernel Launch, \"\n               \"Waiving the run\\n\",\n               dev);\n        exit(EXIT_WAIVED);\n    }\n\n    bool bTestPassed = false;\n    bTestPassed      = runTest(argc, argv, dev);\n\n    exit(bTestPassed ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//! Compute sum reduction on CPU\n//! We use Kahan summation for an accurate sum of large arrays.\n//! http://en.wikipedia.org/wiki/Kahan_summation_algorithm\n//!\n//! @param data       pointer to input data\n//! @param size       number of input data elements\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> T reduceCPU(T *data, int size)\n{\n    T sum = data[0];\n    T c   = (T)0.0;\n\n    for (int i = 1; i < size; i++) {\n        T y = data[i] - c;\n        T t = sum + y;\n        c   = (t - sum) - y;\n        sum = t;\n    }\n\n    return sum;\n}\n\nunsigned int nextPow2(unsigned int x)\n{\n    --x;\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    return ++x;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Compute the number of threads and blocks to use for the reduction\n// We set threads / block to the minimum of maxThreads and n/2.\n////////////////////////////////////////////////////////////////////////////////\nvoid getNumBlocksAndThreads(int n, int maxBlocks, int maxThreads, int &blocks, int &threads)\n{\n    if (n == 1) {\n        threads = 1;\n        blocks  = 1;\n    }\n    else {\n        checkCudaErrors(hipOccupancyMaxPotentialBlockSize(&blocks, &threads, reduceSinglePassMultiBlockCG));\n    }\n\n    blocks = min(maxBlocks, blocks);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// This function performs a reduction of the input data multiple times and\n// measures the average reduction time.\n////////////////////////////////////////////////////////////////////////////////\nfloat benchmarkReduce(int                 n,\n                      int                 numThreads,\n                      int                 numBlocks,\n                      int                 maxThreads,\n                      int                 maxBlocks,\n                      int                 testIterations,\n                      StopWatchInterface *timer,\n                      float              *h_odata,\n                      float              *d_idata,\n                      float              *d_odata)\n{\n    float       gpu_result = 0;\n    hipError_t error;\n\n    printf(\"\\nLaunching %s kernel\\n\", \"SinglePass Multi Block Cooperative Groups\");\n    for (int i = 0; i < testIterations; ++i) {\n        gpu_result = 0;\n        sdkStartTimer(&timer);\n        call_reduceSinglePassMultiBlockCG(n, numThreads, numBlocks, d_idata, d_odata);\n        hipDeviceSynchronize();\n        sdkStopTimer(&timer);\n    }\n\n    // copy final sum from device to host\n    error = hipMemcpy(&gpu_result, d_odata, sizeof(float), hipMemcpyDeviceToHost);\n    checkCudaErrors(error);\n\n    return gpu_result;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// The main function which runs the reduction test.\n////////////////////////////////////////////////////////////////////////////////\nbool runTest(int argc, char **argv, int device)\n{\n    int  size        = 1 << 25; // number of elements to reduce\n    bool bTestPassed = false;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"n\")) {\n        size = getCmdLineArgumentInt(argc, (const char **)argv, \"n\");\n    }\n\n    printf(\"%d elements\\n\", size);\n\n    // Set the device to be used\n    hipDeviceProp_t prop = {0};\n    checkCudaErrors(hipSetDevice(device));\n    checkCudaErrors(hipGetDeviceProperties(&prop, device));\n\n    // create random input data on CPU\n    unsigned int bytes = size * sizeof(float);\n\n    float *h_idata = (float *)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        // Keep the numbers small so we don't get truncation error in the sum\n        h_idata[i] = (rand() & 0xFF) / (float)RAND_MAX;\n    }\n\n    // Determine the launch configuration (threads, blocks)\n    int maxThreads = 0;\n    int maxBlocks  = 0;\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"threads\")) {\n        maxThreads = getCmdLineArgumentInt(argc, (const char **)argv, \"threads\");\n    }\n    else {\n        maxThreads = prop.maxThreadsPerBlock;\n    }\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"maxblocks\")) {\n        maxBlocks = getCmdLineArgumentInt(argc, (const char **)argv, \"maxblocks\");\n    }\n    else {\n        maxBlocks = prop.multiProcessorCount * (prop.maxThreadsPerMultiProcessor / prop.maxThreadsPerBlock);\n    }\n\n    int numBlocks  = 0;\n    int numThreads = 0;\n    getNumBlocksAndThreads(size, maxBlocks, maxThreads, numBlocks, numThreads);\n\n    // We calculate the occupancy to know how many block can actually fit on the\n    // GPU\n    int numBlocksPerSm = 0;\n    checkCudaErrors(hipOccupancyMaxActiveBlocksPerMultiprocessor(\n        &numBlocksPerSm, reduceSinglePassMultiBlockCG, numThreads, numThreads * sizeof(double)));\n\n    int numSms = prop.multiProcessorCount;\n    if (numBlocks > numBlocksPerSm * numSms) {\n        numBlocks = numBlocksPerSm * numSms;\n    }\n    printf(\"numThreads: %d\\n\", numThreads);\n    printf(\"numBlocks: %d\\n\", numBlocks);\n\n    // allocate mem for the result on host side\n    float *h_odata = (float *)malloc(numBlocks * sizeof(float));\n\n    // allocate device memory and data\n    float *d_idata = NULL;\n    float *d_odata = NULL;\n\n    checkCudaErrors(hipMalloc((void **)&d_idata, bytes));\n    checkCudaErrors(hipMalloc((void **)&d_odata, numBlocks * sizeof(float)));\n\n    // copy data directly to device memory\n    checkCudaErrors(hipMemcpy(d_idata, h_idata, bytes, hipMemcpyHostToDevice));\n    checkCudaErrors(hipMemcpy(d_odata, h_idata, numBlocks * sizeof(float), hipMemcpyHostToDevice));\n\n    int testIterations = 100;\n\n    StopWatchInterface *timer = 0;\n    sdkCreateTimer(&timer);\n\n    float gpu_result = 0;\n\n    gpu_result = benchmarkReduce(\n        size, numThreads, numBlocks, maxThreads, maxBlocks, testIterations, timer, h_odata, d_idata, d_odata);\n\n    float reduceTime = sdkGetAverageTimerValue(&timer);\n    printf(\"Average time: %f ms\\n\", reduceTime);\n    printf(\"Bandwidth:    %f GB/s\\n\\n\", (size * sizeof(int)) / (reduceTime * 1.0e6));\n\n    // compute reference solution\n    float cpu_result = reduceCPU<float>(h_idata, size);\n    printf(\"GPU result = %0.12f\\n\", gpu_result);\n    printf(\"CPU result = %0.12f\\n\", cpu_result);\n\n    double threshold = 1e-8 * size;\n    double diff      = abs((double)gpu_result - (double)cpu_result);\n    bTestPassed      = (diff < threshold);\n\n    // cleanup\n    sdkDeleteTimer(&timer);\n\n    free(h_idata);\n    free(h_odata);\n    hipFree(d_idata);\n    hipFree(d_odata);\n\n    return bTestPassed;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/reductionMultiBlockCG/reductionMultiBlockCG.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    Parallel reduction kernels\n*/\n\n#ifndef _REDUCE_KERNEL_H_\n#define _REDUCE_KERNEL_H_\n\n#include <cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\n// Utility class used to avoid linker errors with extern\n// unsized shared memory arrays with templated type\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\n// specialize for double to avoid unaligned memory\n// access compile errors\ntemplate <> struct SharedMemory<double>\n{\n    __device__ inline operator double *()\n    {\n        extern __shared__ double __smem_d[];\n        return (double *)__smem_d;\n    }\n\n    __device__ inline operator const double *() const\n    {\n        extern __shared__ double __smem_d[];\n        return (double *)__smem_d;\n    }\n};\n\ntemplate <class T> __device__ __forceinline__ T warpReduceSum(unsigned int mask, T mySum)\n{\n    for (int offset = warpSize / 2; offset > 0; offset /= 2) {\n        mySum += __shfl_down_sync(mask, mySum, offset);\n    }\n    return mySum;\n}\n\n#if __CUDA_ARCH__ >= 800\n// Specialize warpReduceFunc for int inputs to use __reduce_add_sync intrinsic\n// when on SM 8.0 or higher\ntemplate <> __device__ __forceinline__ int warpReduceSum<int>(unsigned int mask, int mySum)\n{\n    mySum = __reduce_add_sync(mask, mySum);\n    return mySum;\n}\n#endif\n\n/*\n    Parallel sum reduction using shared memory\n    - takes log(n) steps for n input elements\n    - uses n threads\n    - only works for power-of-2 arrays\n*/\n\n/* This reduction interleaves which threads are active by using the modulo\n   operator.  This operator is very expensive on GPUs, and the interleaved\n   inactivity means that no whole warps are active, which is also very\n   inefficient */\ntemplate <class T> __global__ void reduce0(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = 1; s < blockDim.x; s *= 2) {\n        // modulo arithmetic is slow!\n        if ((tid % (2 * s)) == 0) {\n            sdata[tid] += sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/* This version uses contiguous threads, but its interleaved\n   addressing results in many shared memory bank conflicts.\n*/\ntemplate <class T> __global__ void reduce1(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = 1; s < blockDim.x; s *= 2) {\n        int index = 2 * s * tid;\n\n        if (index < blockDim.x) {\n            sdata[index] += sdata[index + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/*\n    This version uses sequential addressing -- no divergence or bank conflicts.\n*/\ntemplate <class T> __global__ void reduce2(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] += sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/*\n    This version uses n/2 threads --\n    it performs the first level of reduction when reading from global memory.\n*/\ntemplate <class T> __global__ void reduce3(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockDim.x * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockDim.x < n)\n        mySum += g_idata[i + blockDim.x];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] = mySum = mySum + sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version uses the warp shuffle operation if available to reduce\n    warp synchronization. When shuffle is not available the final warp's\n    worth of work is unrolled to reduce looping overhead.\n\n    See\n   http://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/\n    for additional information about using shuffle to perform a reduction\n    within a warp.\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize> __global__ void reduce4(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockDim.x * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockSize < n)\n        mySum += g_idata[i + blockSize];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] = mySum = mySum + sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version is completely unrolled, unless warp shuffle is available, then\n    shuffle is used within a loop.  It uses a template parameter to achieve\n    optimal code for any (power of 2) number of threads.  This requires a switch\n    statement in the host code to handle all the different thread block sizes at\n    compile time. When shuffle is available, it is used to reduce warp\n   synchronization.\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize> __global__ void reduce5(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockSize * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockSize < n)\n        mySum += g_idata[i + blockSize];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    if ((blockSize >= 512) && (tid < 256)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 256) && (tid < 128)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 128) && (tid < 64)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 64];\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version adds multiple elements per thread sequentially.  This reduces\n   the overall cost of the algorithm while keeping the work complexity O(n) and\n   the step complexity O(log n). (Brent's Theorem optimization)\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize, bool nIsPow2> __global__ void reduce6(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid      = threadIdx.x;\n    unsigned int gridSize = blockSize * gridDim.x;\n\n    T mySum = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * blockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            mySum += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + blockSize) < n) {\n                mySum += g_idata[i + blockSize];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * blockSize + threadIdx.x;\n        while (i < n) {\n            mySum += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    // each thread puts its local sum into shared memory\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    if ((blockSize >= 512) && (tid < 256)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 256) && (tid < 128)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 128) && (tid < 64)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 64];\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\ntemplate <typename T, unsigned int blockSize, bool nIsPow2>\n__global__ void reduce7(const T *__restrict__ g_idata, T *__restrict__ g_odata, unsigned int n)\n{\n    T *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid        = threadIdx.x;\n    unsigned int gridSize   = blockSize * gridDim.x;\n    unsigned int maskLength = (blockSize & 31); // 31 = warpSize-1\n    maskLength              = (maskLength > 0) ? (32 - maskLength) : maskLength;\n    const unsigned int mask = (0xffffffff) >> maskLength;\n\n    T mySum = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * blockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            mySum += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + blockSize) < n) {\n                mySum += g_idata[i + blockSize];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * blockSize + threadIdx.x;\n        while (i < n) {\n            mySum += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    // Reduce within warp using shuffle or reduce_add if T==int & CUDA_ARCH ==\n    // SM 8.0\n    mySum = warpReduceSum<T>(mask, mySum);\n\n    // each thread puts its local sum into shared memory\n    if ((tid % warpSize) == 0) {\n        sdata[tid / warpSize] = mySum;\n    }\n\n    __syncthreads();\n\n    const unsigned int shmem_extent  = (blockSize / warpSize) > 0 ? (blockSize / warpSize) : 1;\n    const unsigned int ballot_result = __ballot_sync(mask, tid < shmem_extent);\n    if (tid < shmem_extent) {\n        mySum = sdata[tid];\n        // Reduce final warp using shuffle or reduce_add if T==int & CUDA_ARCH ==\n        // SM 8.0\n        mySum = warpReduceSum<T>(ballot_result, mySum);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0) {\n        g_odata[blockIdx.x] = mySum;\n    }\n}\n\n// Performs a reduction step and updates numTotal with how many are remaining\ntemplate <typename T, typename Group> __device__ T cg_reduce_n(T in, Group &threads)\n{\n    return cg::reduce(threads, in, cg::plus<T>());\n}\n\ntemplate <class T> __global__ void cg_reduce(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Shared memory for intermediate steps\n    T *sdata = SharedMemory<T>();\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Handle to tile in thread block\n    cg::thread_block_tile<32> tile = cg::tiled_partition<32>(cta);\n\n    unsigned int ctaSize     = cta.size();\n    unsigned int numCtas     = gridDim.x;\n    unsigned int threadRank  = cta.thread_rank();\n    unsigned int threadIndex = (blockIdx.x * ctaSize) + threadRank;\n\n    T threadVal = 0;\n    {\n        unsigned int i           = threadIndex;\n        unsigned int indexStride = (numCtas * ctaSize);\n        while (i < n) {\n            threadVal += g_idata[i];\n            i += indexStride;\n        }\n        sdata[threadRank] = threadVal;\n    }\n\n    // Wait for all tiles to finish and reduce within CTA\n    {\n        unsigned int ctaSteps = tile.meta_group_size();\n        unsigned int ctaIndex = ctaSize >> 1;\n        while (ctaIndex >= 32) {\n            cta.sync();\n            if (threadRank < ctaIndex) {\n                threadVal += sdata[threadRank + ctaIndex];\n                sdata[threadRank] = threadVal;\n            }\n            ctaSteps >>= 1;\n            ctaIndex >>= 1;\n        }\n    }\n\n    // Shuffle redux instead of smem redux\n    {\n        cta.sync();\n        if (tile.meta_group_rank() == 0) {\n            threadVal = cg_reduce_n(threadVal, tile);\n        }\n    }\n\n    if (threadRank == 0)\n        g_odata[blockIdx.x] = threadVal;\n}\n\ntemplate <class T, size_t BlockSize, size_t MultiWarpGroupSize>\n__global__ void multi_warp_cg_reduce(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Shared memory for intermediate steps\n    T         *sdata = SharedMemory<T>();\n    __shared__ cg::block_tile_memory<BlockSize> scratch;\n\n    // Handle to thread block group\n    auto cta = cg::this_thread_block(scratch);\n    // Handle to multiWarpTile in thread block\n    auto multiWarpTile = cg::tiled_partition<MultiWarpGroupSize>(cta);\n\n    unsigned int gridSize  = BlockSize * gridDim.x;\n    T            threadVal = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    int nIsPow2 = !(n & n - 1);\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * BlockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            threadVal += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + BlockSize) < n) {\n                threadVal += g_idata[i + blockDim.x];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * BlockSize + threadIdx.x;\n        while (i < n) {\n            threadVal += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    threadVal = cg_reduce_n(threadVal, multiWarpTile);\n\n    if (multiWarpTile.thread_rank() == 0) {\n        sdata[multiWarpTile.meta_group_rank()] = threadVal;\n    }\n    cg::sync(cta);\n\n    if (threadIdx.x == 0) {\n        threadVal = 0;\n        for (int i = 0; i < multiWarpTile.meta_group_size(); i++) {\n            threadVal += sdata[i];\n        }\n        g_odata[blockIdx.x] = threadVal;\n    }\n}\n\nextern \"C\" bool isPow2(unsigned int x);\n\n////////////////////////////////////////////////////////////////////////////////\n// Wrapper function for kernel launch\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> void reduce(int size, int threads, int blocks, int whichKernel, T *d_idata, T *d_odata)\n{\n    dim3 dimBlock(threads, 1, 1);\n    dim3 dimGrid(blocks, 1, 1);\n\n    // when there is only one warp per block, we need to allocate two warps\n    // worth of shared memory so that we don't index shared memory out of bounds\n    int smemSize = (threads <= 32) ? 2 * threads * sizeof(T) : threads * sizeof(T);\n\n    // as kernel 9 - multi_warp_cg_reduce cannot work for more than 64 threads\n    // we choose to set kernel 7 for this purpose.\n    if (threads < 64 && whichKernel == 9) {\n        whichKernel = 7;\n    }\n\n    // choose which of the optimized versions of reduction to launch\n    switch (whichKernel) {\n    case 0:\n        reduce0<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 1:\n        reduce1<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 2:\n        reduce2<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 3:\n        reduce3<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 4:\n        switch (threads) {\n        case 512:\n            reduce4<T, 512><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            reduce4<T, 256><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            reduce4<T, 128><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            reduce4<T, 64><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 32:\n            reduce4<T, 32><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 16:\n            reduce4<T, 16><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 8:\n            reduce4<T, 8><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 4:\n            reduce4<T, 4><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 2:\n            reduce4<T, 2><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 1:\n            reduce4<T, 1><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n        }\n\n        break;\n\n    case 5:\n        switch (threads) {\n        case 512:\n            reduce5<T, 512><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            reduce5<T, 256><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            reduce5<T, 128><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            reduce5<T, 64><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 32:\n            reduce5<T, 32><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 16:\n            reduce5<T, 16><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 8:\n            reduce5<T, 8><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 4:\n            reduce5<T, 4><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 2:\n            reduce5<T, 2><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 1:\n            reduce5<T, 1><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n        }\n\n        break;\n\n    case 6:\n        if (isPow2(size)) {\n            switch (threads) {\n            case 512:\n                reduce6<T, 512, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce6<T, 256, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce6<T, 128, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce6<T, 64, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce6<T, 32, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce6<T, 16, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce6<T, 8, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce6<T, 4, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce6<T, 2, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce6<T, 1, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n        else {\n            switch (threads) {\n            case 512:\n                reduce6<T, 512, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce6<T, 256, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce6<T, 128, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce6<T, 64, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce6<T, 32, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce6<T, 16, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce6<T, 8, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce6<T, 4, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce6<T, 2, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce6<T, 1, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n\n        break;\n\n    case 7:\n        // For reduce7 kernel we require only blockSize/warpSize\n        // number of elements in shared memory\n        smemSize = ((threads / 32) + 1) * sizeof(T);\n        if (isPow2(size)) {\n            switch (threads) {\n            case 1024:\n                reduce7<T, 1024, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            case 512:\n                reduce7<T, 512, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce7<T, 256, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce7<T, 128, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce7<T, 64, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce7<T, 32, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce7<T, 16, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce7<T, 8, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce7<T, 4, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce7<T, 2, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce7<T, 1, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n        else {\n            switch (threads) {\n            case 1024:\n                reduce7<T, 1024, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            case 512:\n                reduce7<T, 512, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce7<T, 256, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce7<T, 128, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce7<T, 64, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce7<T, 32, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce7<T, 16, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce7<T, 8, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce7<T, 4, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce7<T, 2, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce7<T, 1, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n\n        break;\n    case 8:\n        cg_reduce<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n    case 9:\n        constexpr int numOfMultiWarpGroups = 2;\n        smemSize                           = numOfMultiWarpGroups * sizeof(T);\n        switch (threads) {\n        case 1024:\n            multi_warp_cg_reduce<T, 1024, 1024 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 512:\n            multi_warp_cg_reduce<T, 512, 512 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            multi_warp_cg_reduce<T, 256, 256 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            multi_warp_cg_reduce<T, 128, 128 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            multi_warp_cg_reduce<T, 64, 64 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        default:\n            printf(\"thread block size of < 64 is not supported for this kernel\\n\");\n            break;\n        }\n        break;\n    }\n}\n\n// Instantiate the reduction function for 3 types\ntemplate void reduce<int>(int size, int threads, int blocks, int whichKernel, int *d_idata, int *d_odata);\n\ntemplate void reduce<float>(int size, int threads, int blocks, int whichKernel, float *d_idata, float *d_odata);\n\ntemplate void reduce<double>(int size, int threads, int blocks, int whichKernel, double *d_idata, double *d_odata);\n\n#endif // #ifndef _REDUCE_KERNEL_H_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n    Parallel reduction kernels\n*/\n\n#include \"hip/hip_runtime.h\"\n#ifndef _REDUCE_KERNEL_H_\n#define _REDUCE_KERNEL_H_\n\n#include <hip/hip_cooperative_groups.h>\n#include <cooperative_groups/reduce.h>\n#include <stdio.h>\n\nnamespace cg = cooperative_groups;\n\n// Utility class used to avoid linker errors with extern\n// unsized shared memory arrays with templated type\ntemplate <class T> struct SharedMemory\n{\n    __device__ inline operator T *()\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n\n    __device__ inline operator const T *() const\n    {\n        extern __shared__ int __smem[];\n        return (T *)__smem;\n    }\n};\n\n// specialize for double to avoid unaligned memory\n// access compile errors\ntemplate <> struct SharedMemory<double>\n{\n    __device__ inline operator double *()\n    {\n        extern __shared__ double __smem_d[];\n        return (double *)__smem_d;\n    }\n\n    __device__ inline operator const double *() const\n    {\n        extern __shared__ double __smem_d[];\n        return (double *)__smem_d;\n    }\n};\n\ntemplate <class T> __device__ __forceinline__ T warpReduceSum(unsigned int mask, T mySum)\n{\n    for (int offset = warpSize / 2; offset > 0; offset /= 2) {\n        mySum += __shfl_down_sync(mask, mySum, offset);\n    }\n    return mySum;\n}\n\n#if __CUDA_ARCH__ >= 800\n// Specialize warpReduceFunc for int inputs to use __reduce_add_sync intrinsic\n// when on SM 8.0 or higher\ntemplate <> __device__ __forceinline__ int warpReduceSum<int>(unsigned int mask, int mySum)\n{\n    mySum = __reduce_add_sync(mask, mySum);\n    return mySum;\n}\n#endif\n\n/*\n    Parallel sum reduction using shared memory\n    - takes log(n) steps for n input elements\n    - uses n threads\n    - only works for power-of-2 arrays\n*/\n\n/* This reduction interleaves which threads are active by using the modulo\n   operator.  This operator is very expensive on GPUs, and the interleaved\n   inactivity means that no whole warps are active, which is also very\n   inefficient */\ntemplate <class T> __global__ void reduce0(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = 1; s < blockDim.x; s *= 2) {\n        // modulo arithmetic is slow!\n        if ((tid % (2 * s)) == 0) {\n            sdata[tid] += sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/* This version uses contiguous threads, but its interleaved\n   addressing results in many shared memory bank conflicts.\n*/\ntemplate <class T> __global__ void reduce1(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = 1; s < blockDim.x; s *= 2) {\n        int index = 2 * s * tid;\n\n        if (index < blockDim.x) {\n            sdata[index] += sdata[index + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/*\n    This version uses sequential addressing -- no divergence or bank conflicts.\n*/\ntemplate <class T> __global__ void reduce2(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // load shared mem\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sdata[tid] = (i < n) ? g_idata[i] : 0;\n\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] += sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = sdata[0];\n}\n\n/*\n    This version uses n/2 threads --\n    it performs the first level of reduction when reading from global memory.\n*/\ntemplate <class T> __global__ void reduce3(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockDim.x * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockDim.x < n)\n        mySum += g_idata[i + blockDim.x];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] = mySum = mySum + sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version uses the warp shuffle operation if available to reduce\n    warp synchronization. When shuffle is not available the final warp's\n    worth of work is unrolled to reduce looping overhead.\n\n    See\n   http://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/\n    for additional information about using shuffle to perform a reduction\n    within a warp.\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize> __global__ void reduce4(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockDim.x * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockSize < n)\n        mySum += g_idata[i + blockSize];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) {\n        if (tid < s) {\n            sdata[tid] = mySum = mySum + sdata[tid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version is completely unrolled, unless warp shuffle is available, then\n    shuffle is used within a loop.  It uses a template parameter to achieve\n    optimal code for any (power of 2) number of threads.  This requires a switch\n    statement in the host code to handle all the different thread block sizes at\n    compile time. When shuffle is available, it is used to reduce warp\n   synchronization.\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize> __global__ void reduce5(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid = threadIdx.x;\n    unsigned int i   = blockIdx.x * (blockSize * 2) + threadIdx.x;\n\n    T mySum = (i < n) ? g_idata[i] : 0;\n\n    if (i + blockSize < n)\n        mySum += g_idata[i + blockSize];\n\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    if ((blockSize >= 512) && (tid < 256)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 256) && (tid < 128)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 128) && (tid < 64)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 64];\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\n/*\n    This version adds multiple elements per thread sequentially.  This reduces\n   the overall cost of the algorithm while keeping the work complexity O(n) and\n   the step complexity O(log n). (Brent's Theorem optimization)\n\n    Note, this kernel needs a minimum of 64*sizeof(T) bytes of shared memory.\n    In other words if blockSize <= 32, allocate 64*sizeof(T) bytes.\n    If blockSize > 32, allocate blockSize*sizeof(T) bytes.\n*/\ntemplate <class T, unsigned int blockSize, bool nIsPow2> __global__ void reduce6(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Handle to thread block group\n    cg::thread_block cta   = cg::this_thread_block();\n    T               *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid      = threadIdx.x;\n    unsigned int gridSize = blockSize * gridDim.x;\n\n    T mySum = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * blockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            mySum += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + blockSize) < n) {\n                mySum += g_idata[i + blockSize];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * blockSize + threadIdx.x;\n        while (i < n) {\n            mySum += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    // each thread puts its local sum into shared memory\n    sdata[tid] = mySum;\n    cg::sync(cta);\n\n    // do reduction in shared mem\n    if ((blockSize >= 512) && (tid < 256)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 256];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 256) && (tid < 128)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 128];\n    }\n\n    cg::sync(cta);\n\n    if ((blockSize >= 128) && (tid < 64)) {\n        sdata[tid] = mySum = mySum + sdata[tid + 64];\n    }\n\n    cg::sync(cta);\n\n    cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n    if (cta.thread_rank() < 32) {\n        // Fetch final intermediate sum from 2nd warp\n        if (blockSize >= 64)\n            mySum += sdata[tid + 32];\n        // Reduce final warp using shuffle\n        for (int offset = tile32.size() / 2; offset > 0; offset /= 2) {\n            mySum += tile32.shfl_down(mySum, offset);\n        }\n    }\n\n    // write result for this block to global mem\n    if (cta.thread_rank() == 0)\n        g_odata[blockIdx.x] = mySum;\n}\n\ntemplate <typename T, unsigned int blockSize, bool nIsPow2>\n__global__ void reduce7(const T *__restrict__ g_idata, T *__restrict__ g_odata, unsigned int n)\n{\n    T *sdata = SharedMemory<T>();\n\n    // perform first level of reduction,\n    // reading from global memory, writing to shared memory\n    unsigned int tid        = threadIdx.x;\n    unsigned int gridSize   = blockSize * gridDim.x;\n    unsigned int maskLength = (blockSize & 31); // 31 = warpSize-1\n    maskLength              = (maskLength > 0) ? (32 - maskLength) : maskLength;\n    const unsigned int mask = (0xffffffff) >> maskLength;\n\n    T mySum = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * blockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            mySum += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + blockSize) < n) {\n                mySum += g_idata[i + blockSize];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * blockSize + threadIdx.x;\n        while (i < n) {\n            mySum += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    // Reduce within warp using shuffle or reduce_add if T==int & CUDA_ARCH ==\n    // SM 8.0\n    mySum = warpReduceSum<T>(mask, mySum);\n\n    // each thread puts its local sum into shared memory\n    if ((tid % warpSize) == 0) {\n        sdata[tid / warpSize] = mySum;\n    }\n\n    __syncthreads();\n\n    const unsigned int shmem_extent  = (blockSize / warpSize) > 0 ? (blockSize / warpSize) : 1;\n    const unsigned int ballot_result = __ballot_sync(mask, tid < shmem_extent);\n    if (tid < shmem_extent) {\n        mySum = sdata[tid];\n        // Reduce final warp using shuffle or reduce_add if T==int & CUDA_ARCH ==\n        // SM 8.0\n        mySum = warpReduceSum<T>(ballot_result, mySum);\n    }\n\n    // write result for this block to global mem\n    if (tid == 0) {\n        g_odata[blockIdx.x] = mySum;\n    }\n}\n\n// Performs a reduction step and updates numTotal with how many are remaining\ntemplate <typename T, typename Group> __device__ T cg_reduce_n(T in, Group &threads)\n{\n    return cg::reduce(threads, in, cg::plus<T>());\n}\n\ntemplate <class T> __global__ void cg_reduce(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Shared memory for intermediate steps\n    T *sdata = SharedMemory<T>();\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Handle to tile in thread block\n    cg::thread_block_tile<32> tile = cg::tiled_partition<32>(cta);\n\n    unsigned int ctaSize     = cta.size();\n    unsigned int numCtas     = gridDim.x;\n    unsigned int threadRank  = cta.thread_rank();\n    unsigned int threadIndex = (blockIdx.x * ctaSize) + threadRank;\n\n    T threadVal = 0;\n    {\n        unsigned int i           = threadIndex;\n        unsigned int indexStride = (numCtas * ctaSize);\n        while (i < n) {\n            threadVal += g_idata[i];\n            i += indexStride;\n        }\n        sdata[threadRank] = threadVal;\n    }\n\n    // Wait for all tiles to finish and reduce within CTA\n    {\n        unsigned int ctaSteps = tile.meta_group_size();\n        unsigned int ctaIndex = ctaSize >> 1;\n        while (ctaIndex >= 32) {\n            cta.sync();\n            if (threadRank < ctaIndex) {\n                threadVal += sdata[threadRank + ctaIndex];\n                sdata[threadRank] = threadVal;\n            }\n            ctaSteps >>= 1;\n            ctaIndex >>= 1;\n        }\n    }\n\n    // Shuffle redux instead of smem redux\n    {\n        cta.sync();\n        if (tile.meta_group_rank() == 0) {\n            threadVal = cg_reduce_n(threadVal, tile);\n        }\n    }\n\n    if (threadRank == 0)\n        g_odata[blockIdx.x] = threadVal;\n}\n\ntemplate <class T, size_t BlockSize, size_t MultiWarpGroupSize>\n__global__ void multi_warp_cg_reduce(T *g_idata, T *g_odata, unsigned int n)\n{\n    // Shared memory for intermediate steps\n    T         *sdata = SharedMemory<T>();\n    __shared__ cg::block_tile_memory<BlockSize> scratch;\n\n    // Handle to thread block group\n    auto cta = cg::this_thread_block(scratch);\n    // Handle to multiWarpTile in thread block\n    auto multiWarpTile = cg::tiled_partition<MultiWarpGroupSize>(cta);\n\n    unsigned int gridSize  = BlockSize * gridDim.x;\n    T            threadVal = 0;\n\n    // we reduce multiple elements per thread.  The number is determined by the\n    // number of active thread blocks (via gridDim).  More blocks will result\n    // in a larger gridSize and therefore fewer elements per thread\n    int nIsPow2 = !(n & n - 1);\n    if (nIsPow2) {\n        unsigned int i = blockIdx.x * BlockSize * 2 + threadIdx.x;\n        gridSize       = gridSize << 1;\n\n        while (i < n) {\n            threadVal += g_idata[i];\n            // ensure we don't read out of bounds -- this is optimized away for\n            // powerOf2 sized arrays\n            if ((i + BlockSize) < n) {\n                threadVal += g_idata[i + blockDim.x];\n            }\n            i += gridSize;\n        }\n    }\n    else {\n        unsigned int i = blockIdx.x * BlockSize + threadIdx.x;\n        while (i < n) {\n            threadVal += g_idata[i];\n            i += gridSize;\n        }\n    }\n\n    threadVal = cg_reduce_n(threadVal, multiWarpTile);\n\n    if (multiWarpTile.thread_rank() == 0) {\n        sdata[multiWarpTile.meta_group_rank()] = threadVal;\n    }\n    cg::sync(cta);\n\n    if (threadIdx.x == 0) {\n        threadVal = 0;\n        for (int i = 0; i < multiWarpTile.meta_group_size(); i++) {\n            threadVal += sdata[i];\n        }\n        g_odata[blockIdx.x] = threadVal;\n    }\n}\n\nextern \"C\" bool isPow2(unsigned int x);\n\n////////////////////////////////////////////////////////////////////////////////\n// Wrapper function for kernel launch\n////////////////////////////////////////////////////////////////////////////////\ntemplate <class T> void reduce(int size, int threads, int blocks, int whichKernel, T *d_idata, T *d_odata)\n{\n    dim3 dimBlock(threads, 1, 1);\n    dim3 dimGrid(blocks, 1, 1);\n\n    // when there is only one warp per block, we need to allocate two warps\n    // worth of shared memory so that we don't index shared memory out of bounds\n    int smemSize = (threads <= 32) ? 2 * threads * sizeof(T) : threads * sizeof(T);\n\n    // as kernel 9 - multi_warp_cg_reduce cannot work for more than 64 threads\n    // we choose to set kernel 7 for this purpose.\n    if (threads < 64 && whichKernel == 9) {\n        whichKernel = 7;\n    }\n\n    // choose which of the optimized versions of reduction to launch\n    switch (whichKernel) {\n    case 0:\n        reduce0<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 1:\n        reduce1<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 2:\n        reduce2<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 3:\n        reduce3<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n\n    case 4:\n        switch (threads) {\n        case 512:\n            reduce4<T, 512><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            reduce4<T, 256><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            reduce4<T, 128><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            reduce4<T, 64><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 32:\n            reduce4<T, 32><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 16:\n            reduce4<T, 16><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 8:\n            reduce4<T, 8><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 4:\n            reduce4<T, 4><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 2:\n            reduce4<T, 2><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 1:\n            reduce4<T, 1><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n        }\n\n        break;\n\n    case 5:\n        switch (threads) {\n        case 512:\n            reduce5<T, 512><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            reduce5<T, 256><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            reduce5<T, 128><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            reduce5<T, 64><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 32:\n            reduce5<T, 32><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 16:\n            reduce5<T, 16><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 8:\n            reduce5<T, 8><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 4:\n            reduce5<T, 4><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 2:\n            reduce5<T, 2><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 1:\n            reduce5<T, 1><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n        }\n\n        break;\n\n    case 6:\n        if (isPow2(size)) {\n            switch (threads) {\n            case 512:\n                reduce6<T, 512, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce6<T, 256, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce6<T, 128, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce6<T, 64, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce6<T, 32, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce6<T, 16, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce6<T, 8, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce6<T, 4, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce6<T, 2, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce6<T, 1, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n        else {\n            switch (threads) {\n            case 512:\n                reduce6<T, 512, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce6<T, 256, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce6<T, 128, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce6<T, 64, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce6<T, 32, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce6<T, 16, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce6<T, 8, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce6<T, 4, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce6<T, 2, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce6<T, 1, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n\n        break;\n\n    case 7:\n        // For reduce7 kernel we require only blockSize/warpSize\n        // number of elements in shared memory\n        smemSize = ((threads / 32) + 1) * sizeof(T);\n        if (isPow2(size)) {\n            switch (threads) {\n            case 1024:\n                reduce7<T, 1024, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            case 512:\n                reduce7<T, 512, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce7<T, 256, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce7<T, 128, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce7<T, 64, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce7<T, 32, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce7<T, 16, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce7<T, 8, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce7<T, 4, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce7<T, 2, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce7<T, 1, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n        else {\n            switch (threads) {\n            case 1024:\n                reduce7<T, 1024, true><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            case 512:\n                reduce7<T, 512, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 256:\n                reduce7<T, 256, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 128:\n                reduce7<T, 128, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 64:\n                reduce7<T, 64, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 32:\n                reduce7<T, 32, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 16:\n                reduce7<T, 16, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 8:\n                reduce7<T, 8, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 4:\n                reduce7<T, 4, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 2:\n                reduce7<T, 2, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n\n            case 1:\n                reduce7<T, 1, false><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n                break;\n            }\n        }\n\n        break;\n    case 8:\n        cg_reduce<T><<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n        break;\n    case 9:\n        constexpr int numOfMultiWarpGroups = 2;\n        smemSize                           = numOfMultiWarpGroups * sizeof(T);\n        switch (threads) {\n        case 1024:\n            multi_warp_cg_reduce<T, 1024, 1024 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 512:\n            multi_warp_cg_reduce<T, 512, 512 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 256:\n            multi_warp_cg_reduce<T, 256, 256 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 128:\n            multi_warp_cg_reduce<T, 128, 128 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        case 64:\n            multi_warp_cg_reduce<T, 64, 64 / numOfMultiWarpGroups>\n                <<<dimGrid, dimBlock, smemSize>>>(d_idata, d_odata, size);\n            break;\n\n        default:\n            printf(\"thread block size of < 64 is not supported for this kernel\\n\");\n            break;\n        }\n        break;\n    }\n}\n\n// Instantiate the reduction function for 3 types\ntemplate void reduce<int>(int size, int threads, int blocks, int whichKernel, int *d_idata, int *d_odata);\n\ntemplate void reduce<float>(int size, int threads, int blocks, int whichKernel, float *d_idata, float *d_odata);\n\ntemplate void reduce<double>(int size, int threads, int blocks, int whichKernel, double *d_idata, double *d_odata);\n\n#endif // #ifndef _REDUCE_KERNEL_H_\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/reduction/reduction_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <curand.h>\n#include <curand_kernel.h>\n\n#include \"../inc/cudasharedmem.h\"\n\nusing std::string;\nusing std::vector;\n\n// Helper templates to support float and double in same code\ntemplate <typename L, typename R> struct TYPE_IS\n{\n    static const bool test = false;\n};\ntemplate <typename L> struct TYPE_IS<L, L>\n{\n    static const bool test = true;\n};\ntemplate <bool, class L, class R> struct IF\n{\n    typedef R type;\n};\ntemplate <class L, class R> struct IF<true, L, R>\n{\n    typedef L type;\n};\n\n// RNG init kernel\ntemplate <typename rngState_t, typename rngDirectionVectors_t>\n__global__ void\ninitRNG(rngState_t *const rngStates, rngDirectionVectors_t *const rngDirections, unsigned int numDrawsPerDirection)\n{\n    // Determine thread ID\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Determine offset to avoid overlapping sub-sequences\n    unsigned int offset = tid * ((numDrawsPerDirection + step - 1) / step);\n\n    // Initialise the RNG\n    curand_init(rngDirections[0], offset, &rngStates[tid]);\n    curand_init(rngDirections[1], offset, &rngStates[tid + step]);\n}\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n__device__ inline void getPoint(float &x, float &y, curandStateSobol32 &state1, curandStateSobol32 &state2)\n{\n    x = curand_uniform(&state1);\n    y = curand_uniform(&state2);\n}\n__device__ inline void getPoint(double &x, double &y, curandStateSobol64 &state1, curandStateSobol64 &state2)\n{\n    x = curand_uniform_double(&state1);\n    y = curand_uniform_double(&state2);\n}\n\n// Estimator kernel\ntemplate <typename Real, typename rngState_t>\n__global__ void computeValue(unsigned int *const results, rngState_t *const rngStates, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Initialise the RNG\n    rngState_t localState1 = rngStates[tid];\n    rngState_t localState2 = rngStates[tid + step];\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        Real x;\n        Real y;\n        getPoint(x, y, localState1, localState2);\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device, unsigned int threadBlockSize)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    cudaError_t               cudaResult = cudaSuccess;\n    struct cudaDeviceProp     deviceProperties;\n    struct cudaFuncAttributes funcAttributes;\n\n    // Determine type of generator to use (32- or 64-bit)\n    typedef\n        typename IF<TYPE_IS<Real, double>::test, curandStateSobol64_t, curandStateSobol32_t>::type curandStateSobol_sz;\n    typedef typename IF<TYPE_IS<Real, double>::test, curandDirectionVectors64_t, curandDirectionVectors32_t>::type\n        curandDirectionVectors_sz;\n\n    // Get device properties\n    cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = cudaSetDevice(m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, initRNG<curandStateSobol_sz, curandDirectionVectors_sz>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real, curandStateSobol_sz>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for RNG states and direction vectors\n    curandStateSobol_sz       *d_rngStates     = 0;\n    curandDirectionVectors_sz *d_rngDirections = 0;\n    cudaResult = cudaMalloc((void **)&d_rngStates, 2 * grid.x * block.x * sizeof(curandStateSobol_sz));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for RNG states: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    cudaResult = cudaMalloc((void **)&d_rngDirections, 2 * sizeof(curandDirectionVectors_sz));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for RNG direction vectors: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate direction vectors on the host and copy to the device\n    if (typeid(Real) == typeid(float)) {\n        curandDirectionVectors32_t *rngDirections;\n        curandStatus_t curandResult = curandGetDirectionVectors32(&rngDirections, CURAND_DIRECTION_VECTORS_32_JOEKUO6);\n\n        if (curandResult != CURAND_STATUS_SUCCESS) {\n            string msg(\"Could not get direction vectors for quasi-random number \"\n                       \"generator: \");\n            msg += curandResult;\n            throw std::runtime_error(msg);\n        }\n\n        cudaResult =\n            cudaMemcpy(d_rngDirections, rngDirections, 2 * sizeof(curandDirectionVectors32_t), cudaMemcpyHostToDevice);\n\n        if (cudaResult != cudaSuccess) {\n            string msg(\"Could not copy direction vectors to device: \");\n            msg += cudaGetErrorString(cudaResult);\n            throw std::runtime_error(msg);\n        }\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandDirectionVectors64_t *rngDirections;\n        curandStatus_t curandResult = curandGetDirectionVectors64(&rngDirections, CURAND_DIRECTION_VECTORS_64_JOEKUO6);\n\n        if (curandResult != CURAND_STATUS_SUCCESS) {\n            string msg(\"Could not get direction vectors for quasi-random number \"\n                       \"generator: \");\n            msg += curandResult;\n            throw std::runtime_error(msg);\n        }\n\n        cudaResult =\n            cudaMemcpy(d_rngDirections, rngDirections, 2 * sizeof(curandDirectionVectors64_t), cudaMemcpyHostToDevice);\n\n        if (cudaResult != cudaSuccess) {\n            string msg(\"Could not copy direction vectors to device: \");\n            msg += cudaGetErrorString(cudaResult);\n            throw std::runtime_error(msg);\n        }\n    }\n    else {\n        string msg(\"Could not get direction vectors for random number generator of \"\n                   \"specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, d_rngDirections, m_numSims);\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_rngStates, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), cudaMemcpyDeviceToHost);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_rngStates) {\n        cudaFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_rngDirections) {\n        cudaFree(d_rngDirections);\n        d_rngDirections = 0;\n    }\n\n    if (d_results) {\n        cudaFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand.h>\n#include <hiprand/hiprand_kernel.h>\n\n#include \"../inc/cudasharedmem.h\"\n\nusing std::string;\nusing std::vector;\n\n// Helper templates to support float and double in same code\ntemplate <typename L, typename R> struct TYPE_IS\n{\n    static const bool test = false;\n};\ntemplate <typename L> struct TYPE_IS<L, L>\n{\n    static const bool test = true;\n};\ntemplate <bool, class L, class R> struct IF\n{\n    typedef R type;\n};\ntemplate <class L, class R> struct IF<true, L, R>\n{\n    typedef L type;\n};\n\n// RNG init kernel\ntemplate <typename rngState_t, typename rngDirectionVectors_t>\n__global__ void\ninitRNG(rngState_t *const rngStates, rngDirectionVectors_t *const rngDirections, unsigned int numDrawsPerDirection)\n{\n    // Determine thread ID\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Determine offset to avoid overlapping sub-sequences\n    unsigned int offset = tid * ((numDrawsPerDirection + step - 1) / step);\n\n    // Initialise the RNG\n    hiprand_init(rngDirections[0], offset, &rngStates[tid]);\n    hiprand_init(rngDirections[1], offset, &rngStates[tid + step]);\n}\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n__device__ inline void getPoint(float &x, float &y, hiprandStateSobol32 &state1, hiprandStateSobol32 &state2)\n{\n    x = hiprand_uniform(&state1);\n    y = hiprand_uniform(&state2);\n}\n__device__ inline void getPoint(double &x, double &y, hiprandStateSobol64 &state1, hiprandStateSobol64 &state2)\n{\n    x = hiprand_uniform_double(&state1);\n    y = hiprand_uniform_double(&state2);\n}\n\n// Estimator kernel\ntemplate <typename Real, typename rngState_t>\n__global__ void computeValue(unsigned int *const results, rngState_t *const rngStates, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Initialise the RNG\n    rngState_t localState1 = rngStates[tid];\n    rngState_t localState2 = rngStates[tid + step];\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        Real x;\n        Real y;\n        getPoint(x, y, localState1, localState2);\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device, unsigned int threadBlockSize)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    hipError_t               cudaResult = hipSuccess;\n    struct hipDeviceProp_t     deviceProperties;\n    struct hipFuncAttributes funcAttributes;\n\n    // Determine type of generator to use (32- or 64-bit)\n    typedef\n        typename IF<TYPE_IS<Real, double>::test, hiprandStateSobol64_t, hiprandStateSobol32_t>::type curandStateSobol_sz;\n    typedef typename IF<TYPE_IS<Real, double>::test, hiprandDirectionVectors64_t, hiprandDirectionVectors32_t>::type\n        curandDirectionVectors_sz;\n\n    // Get device properties\n    cudaResult = hipGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = hipSetDevice(m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(initRNG<curandStateSobol_sz, curandDirectionVectors_sz>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(computeValue<Real, curandStateSobol_sz>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for RNG states and direction vectors\n    curandStateSobol_sz       *d_rngStates     = 0;\n    curandDirectionVectors_sz *d_rngDirections = 0;\n    cudaResult = hipMalloc((void **)&d_rngStates, 2 * grid.x * block.x * sizeof(curandStateSobol_sz));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for RNG states: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    cudaResult = hipMalloc((void **)&d_rngDirections, 2 * sizeof(curandDirectionVectors_sz));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for RNG direction vectors: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = hipMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate direction vectors on the host and copy to the device\n    if (typeid(Real) == typeid(float)) {\n        hiprandDirectionVectors32_t *rngDirections;\n        hiprandStatus_t curandResult = hiprandGetDirectionVectors32(&rngDirections, HIPRAND_DIRECTION_VECTORS_32_JOEKUO6);\n\n        if (curandResult != HIPRAND_STATUS_SUCCESS) {\n            string msg(\"Could not get direction vectors for quasi-random number \"\n                       \"generator: \");\n            msg += curandResult;\n            throw std::runtime_error(msg);\n        }\n\n        cudaResult =\n            hipMemcpy(d_rngDirections, rngDirections, 2 * sizeof(hiprandDirectionVectors32_t), hipMemcpyHostToDevice);\n\n        if (cudaResult != hipSuccess) {\n            string msg(\"Could not copy direction vectors to device: \");\n            msg += hipGetErrorString(cudaResult);\n            throw std::runtime_error(msg);\n        }\n    }\n    else if (typeid(Real) == typeid(double)) {\n        hiprandDirectionVectors64_t *rngDirections;\n        hiprandStatus_t curandResult = hiprandGetDirectionVectors64(&rngDirections, HIPRAND_DIRECTION_VECTORS_64_JOEKUO6);\n\n        if (curandResult != HIPRAND_STATUS_SUCCESS) {\n            string msg(\"Could not get direction vectors for quasi-random number \"\n                       \"generator: \");\n            msg += curandResult;\n            throw std::runtime_error(msg);\n        }\n\n        cudaResult =\n            hipMemcpy(d_rngDirections, rngDirections, 2 * sizeof(hiprandDirectionVectors64_t), hipMemcpyHostToDevice);\n\n        if (cudaResult != hipSuccess) {\n            string msg(\"Could not copy direction vectors to device: \");\n            msg += hipGetErrorString(cudaResult);\n            throw std::runtime_error(msg);\n        }\n    }\n    else {\n        string msg(\"Could not get direction vectors for random number generator of \"\n                   \"specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, d_rngDirections, m_numSims);\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_rngStates, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = hipMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), hipMemcpyDeviceToHost);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_rngStates) {\n        hipFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_rngDirections) {\n        hipFree(d_rngDirections);\n        d_rngDirections = 0;\n    }\n\n    if (d_results) {\n        hipFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/MC_EstimatePiInlineQ/src/piestimator.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n#include \"histogram_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU-specific common definitions\n////////////////////////////////////////////////////////////////////////////////\n// Data type used for input data fetches\ntypedef uint4 data_t;\n\n// May change on future hardware, so better parametrize the code\n#define SHARED_MEMORY_BANKS 16\n\n////////////////////////////////////////////////////////////////////////////////\n// Main computation pass: compute gridDim.x partial histograms\n////////////////////////////////////////////////////////////////////////////////\n// Count a byte into shared-memory storage\ninline __device__ void addByte(uchar *s_ThreadBase, uint data)\n{\n    s_ThreadBase[UMUL(data, HISTOGRAM64_THREADBLOCK_SIZE)]++;\n}\n\n// Count four bytes of a word\ninline __device__ void addWord(uchar *s_ThreadBase, uint data)\n{\n    // Only higher 6 bits of each byte matter, as this is a 64-bin histogram\n    addByte(s_ThreadBase, (data >> 2) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 10) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 18) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 26) & 0x3FU);\n}\n\n__global__ void histogram64Kernel(uint *d_PartialHistograms, data_t *d_Data, uint dataCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Encode thread index in order to avoid bank conflicts in s_Hist[] access:\n    // each group of SHARED_MEMORY_BANKS threads accesses consecutive shared\n    // memory banks\n    // and the same bytes [0..3] within the banks\n    // Because of this permutation block size should be a multiple of 4 *\n    // SHARED_MEMORY_BANKS\n    const uint threadPos = ((threadIdx.x & ~(SHARED_MEMORY_BANKS * 4 - 1)) << 0)\n                         | ((threadIdx.x & (SHARED_MEMORY_BANKS - 1)) << 2)\n                         | ((threadIdx.x & (SHARED_MEMORY_BANKS * 3)) >> 4);\n\n    // Per-thread histogram storage\n    __shared__ uchar s_Hist[HISTOGRAM64_THREADBLOCK_SIZE * HISTOGRAM64_BIN_COUNT];\n    uchar           *s_ThreadBase = s_Hist + threadPos;\n\n// Initialize shared memory (writing 32-bit words)\n#pragma unroll\n\n    for (uint i = 0; i < (HISTOGRAM64_BIN_COUNT / 4); i++) {\n        ((uint *)s_Hist)[threadIdx.x + i * HISTOGRAM64_THREADBLOCK_SIZE] = 0;\n    }\n\n    // Read data from global memory and submit to the shared-memory histogram\n    // Since histogram counters are byte-sized, every single thread can't do more\n    // than 255 submission\n    cg::sync(cta);\n\n    for (uint pos = UMAD(blockIdx.x, blockDim.x, threadIdx.x); pos < dataCount; pos += UMUL(blockDim.x, gridDim.x)) {\n        data_t data = d_Data[pos];\n        addWord(s_ThreadBase, data.x);\n        addWord(s_ThreadBase, data.y);\n        addWord(s_ThreadBase, data.z);\n        addWord(s_ThreadBase, data.w);\n    }\n\n    // Accumulate per-thread histograms into per-block and write to global memory\n    cg::sync(cta);\n\n    if (threadIdx.x < HISTOGRAM64_BIN_COUNT) {\n        uchar *s_HistBase = s_Hist + UMUL(threadIdx.x, HISTOGRAM64_THREADBLOCK_SIZE);\n\n        uint sum = 0;\n        uint pos = 4 * (threadIdx.x & (SHARED_MEMORY_BANKS - 1));\n\n#pragma unroll\n\n        for (uint i = 0; i < (HISTOGRAM64_THREADBLOCK_SIZE / 4); i++) {\n            sum += s_HistBase[pos + 0] + s_HistBase[pos + 1] + s_HistBase[pos + 2] + s_HistBase[pos + 3];\n            pos = (pos + 4) & (HISTOGRAM64_THREADBLOCK_SIZE - 1);\n        }\n\n        d_PartialHistograms[blockIdx.x * HISTOGRAM64_BIN_COUNT + threadIdx.x] = sum;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge histogram64() output\n// Run one threadblock per bin; each threadbock adds up the same bin counter\n// from every partial histogram. Reads are uncoalesced, but mergeHistogram64\n// takes only a fraction of total processing time\n////////////////////////////////////////////////////////////////////////////////\n#define MERGE_THREADBLOCK_SIZE 256\n\n__global__ void mergeHistogram64Kernel(uint *d_Histogram, uint *d_PartialHistograms, uint histogramCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  data[MERGE_THREADBLOCK_SIZE];\n\n    uint sum = 0;\n\n    for (uint i = threadIdx.x; i < histogramCount; i += MERGE_THREADBLOCK_SIZE) {\n        sum += d_PartialHistograms[blockIdx.x + i * HISTOGRAM64_BIN_COUNT];\n    }\n\n    data[threadIdx.x] = sum;\n\n    for (uint stride = MERGE_THREADBLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n\n        if (threadIdx.x < stride) {\n            data[threadIdx.x] += data[threadIdx.x + stride];\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        d_Histogram[blockIdx.x] = data[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// CPU interface to GPU histogram calculator\n////////////////////////////////////////////////////////////////////////////////\n// histogram64kernel() intermediate results buffer\n// MAX_PARTIAL_HISTOGRAM64_COUNT == 32768 and HISTOGRAM64_THREADBLOCK_SIZE == 64\n// amounts to max. 480MB of input data\nstatic const uint MAX_PARTIAL_HISTOGRAM64_COUNT = 32768;\nstatic uint      *d_PartialHistograms;\n\n// Internal memory allocation\nextern \"C\" void initHistogram64(void)\n{\n    assert(HISTOGRAM64_THREADBLOCK_SIZE % (4 * SHARED_MEMORY_BANKS) == 0);\n    checkCudaErrors(cudaMalloc((void **)&d_PartialHistograms,\n                               MAX_PARTIAL_HISTOGRAM64_COUNT * HISTOGRAM64_BIN_COUNT * sizeof(uint)));\n}\n\n// Internal memory deallocation\nextern \"C\" void closeHistogram64(void) { checkCudaErrors(cudaFree(d_PartialHistograms)); }\n\n// Round a / b to nearest higher integer value\ninline uint iDivUp(uint a, uint b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Snap a to nearest lower multiple of b\ninline uint iSnapDown(uint a, uint b) { return a - a % b; }\n\nextern \"C\" void histogram64(uint *d_Histogram, void *d_Data, uint byteCount)\n{\n    const uint histogramCount = iDivUp(byteCount, HISTOGRAM64_THREADBLOCK_SIZE * iSnapDown(255, sizeof(data_t)));\n\n    assert(byteCount % sizeof(data_t) == 0);\n    assert(histogramCount <= MAX_PARTIAL_HISTOGRAM64_COUNT);\n\n    histogram64Kernel<<<histogramCount, HISTOGRAM64_THREADBLOCK_SIZE>>>(\n        d_PartialHistograms, (data_t *)d_Data, byteCount / sizeof(data_t));\n    getLastCudaError(\"histogram64Kernel() execution failed\\n\");\n\n    mergeHistogram64Kernel<<<HISTOGRAM64_BIN_COUNT, MERGE_THREADBLOCK_SIZE>>>(\n        d_Histogram, d_PartialHistograms, histogramCount);\n    getLastCudaError(\"mergeHistogram64() execution failed\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n#include \"histogram_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU-specific common definitions\n////////////////////////////////////////////////////////////////////////////////\n// Data type used for input data fetches\ntypedef uint4 data_t;\n\n// May change on future hardware, so better parametrize the code\n#define SHARED_MEMORY_BANKS 16\n\n////////////////////////////////////////////////////////////////////////////////\n// Main computation pass: compute gridDim.x partial histograms\n////////////////////////////////////////////////////////////////////////////////\n// Count a byte into shared-memory storage\ninline __device__ void addByte(uchar *s_ThreadBase, uint data)\n{\n    s_ThreadBase[UMUL(data, HISTOGRAM64_THREADBLOCK_SIZE)]++;\n}\n\n// Count four bytes of a word\ninline __device__ void addWord(uchar *s_ThreadBase, uint data)\n{\n    // Only higher 6 bits of each byte matter, as this is a 64-bin histogram\n    addByte(s_ThreadBase, (data >> 2) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 10) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 18) & 0x3FU);\n    addByte(s_ThreadBase, (data >> 26) & 0x3FU);\n}\n\n__global__ void histogram64Kernel(uint *d_PartialHistograms, data_t *d_Data, uint dataCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Encode thread index in order to avoid bank conflicts in s_Hist[] access:\n    // each group of SHARED_MEMORY_BANKS threads accesses consecutive shared\n    // memory banks\n    // and the same bytes [0..3] within the banks\n    // Because of this permutation block size should be a multiple of 4 *\n    // SHARED_MEMORY_BANKS\n    const uint threadPos = ((threadIdx.x & ~(SHARED_MEMORY_BANKS * 4 - 1)) << 0)\n                         | ((threadIdx.x & (SHARED_MEMORY_BANKS - 1)) << 2)\n                         | ((threadIdx.x & (SHARED_MEMORY_BANKS * 3)) >> 4);\n\n    // Per-thread histogram storage\n    __shared__ uchar s_Hist[HISTOGRAM64_THREADBLOCK_SIZE * HISTOGRAM64_BIN_COUNT];\n    uchar           *s_ThreadBase = s_Hist + threadPos;\n\n// Initialize shared memory (writing 32-bit words)\n#pragma unroll\n\n    for (uint i = 0; i < (HISTOGRAM64_BIN_COUNT / 4); i++) {\n        ((uint *)s_Hist)[threadIdx.x + i * HISTOGRAM64_THREADBLOCK_SIZE] = 0;\n    }\n\n    // Read data from global memory and submit to the shared-memory histogram\n    // Since histogram counters are byte-sized, every single thread can't do more\n    // than 255 submission\n    cg::sync(cta);\n\n    for (uint pos = UMAD(blockIdx.x, blockDim.x, threadIdx.x); pos < dataCount; pos += UMUL(blockDim.x, gridDim.x)) {\n        data_t data = d_Data[pos];\n        addWord(s_ThreadBase, data.x);\n        addWord(s_ThreadBase, data.y);\n        addWord(s_ThreadBase, data.z);\n        addWord(s_ThreadBase, data.w);\n    }\n\n    // Accumulate per-thread histograms into per-block and write to global memory\n    cg::sync(cta);\n\n    if (threadIdx.x < HISTOGRAM64_BIN_COUNT) {\n        uchar *s_HistBase = s_Hist + UMUL(threadIdx.x, HISTOGRAM64_THREADBLOCK_SIZE);\n\n        uint sum = 0;\n        uint pos = 4 * (threadIdx.x & (SHARED_MEMORY_BANKS - 1));\n\n#pragma unroll\n\n        for (uint i = 0; i < (HISTOGRAM64_THREADBLOCK_SIZE / 4); i++) {\n            sum += s_HistBase[pos + 0] + s_HistBase[pos + 1] + s_HistBase[pos + 2] + s_HistBase[pos + 3];\n            pos = (pos + 4) & (HISTOGRAM64_THREADBLOCK_SIZE - 1);\n        }\n\n        d_PartialHistograms[blockIdx.x * HISTOGRAM64_BIN_COUNT + threadIdx.x] = sum;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge histogram64() output\n// Run one threadblock per bin; each threadbock adds up the same bin counter\n// from every partial histogram. Reads are uncoalesced, but mergeHistogram64\n// takes only a fraction of total processing time\n////////////////////////////////////////////////////////////////////////////////\n#define MERGE_THREADBLOCK_SIZE 256\n\n__global__ void mergeHistogram64Kernel(uint *d_Histogram, uint *d_PartialHistograms, uint histogramCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  data[MERGE_THREADBLOCK_SIZE];\n\n    uint sum = 0;\n\n    for (uint i = threadIdx.x; i < histogramCount; i += MERGE_THREADBLOCK_SIZE) {\n        sum += d_PartialHistograms[blockIdx.x + i * HISTOGRAM64_BIN_COUNT];\n    }\n\n    data[threadIdx.x] = sum;\n\n    for (uint stride = MERGE_THREADBLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n\n        if (threadIdx.x < stride) {\n            data[threadIdx.x] += data[threadIdx.x + stride];\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        d_Histogram[blockIdx.x] = data[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// CPU interface to GPU histogram calculator\n////////////////////////////////////////////////////////////////////////////////\n// histogram64kernel() intermediate results buffer\n// MAX_PARTIAL_HISTOGRAM64_COUNT == 32768 and HISTOGRAM64_THREADBLOCK_SIZE == 64\n// amounts to max. 480MB of input data\nstatic const uint MAX_PARTIAL_HISTOGRAM64_COUNT = 32768;\nstatic uint      *d_PartialHistograms;\n\n// Internal memory allocation\nextern \"C\" void initHistogram64(void)\n{\n    assert(HISTOGRAM64_THREADBLOCK_SIZE % (4 * SHARED_MEMORY_BANKS) == 0);\n    checkCudaErrors(hipMalloc((void **)&d_PartialHistograms,\n                               MAX_PARTIAL_HISTOGRAM64_COUNT * HISTOGRAM64_BIN_COUNT * sizeof(uint)));\n}\n\n// Internal memory deallocation\nextern \"C\" void closeHistogram64(void) { checkCudaErrors(hipFree(d_PartialHistograms)); }\n\n// Round a / b to nearest higher integer value\ninline uint iDivUp(uint a, uint b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Snap a to nearest lower multiple of b\ninline uint iSnapDown(uint a, uint b) { return a - a % b; }\n\nextern \"C\" void histogram64(uint *d_Histogram, void *d_Data, uint byteCount)\n{\n    const uint histogramCount = iDivUp(byteCount, HISTOGRAM64_THREADBLOCK_SIZE * iSnapDown(255, sizeof(data_t)));\n\n    assert(byteCount % sizeof(data_t) == 0);\n    assert(histogramCount <= MAX_PARTIAL_HISTOGRAM64_COUNT);\n\n    histogram64Kernel<<<histogramCount, HISTOGRAM64_THREADBLOCK_SIZE>>>(\n        d_PartialHistograms, (data_t *)d_Data, byteCount / sizeof(data_t));\n    getLastCudaError(\"histogram64Kernel() execution failed\\n\");\n\n    mergeHistogram64Kernel<<<HISTOGRAM64_BIN_COUNT, MERGE_THREADBLOCK_SIZE>>>(\n        d_Histogram, d_PartialHistograms, histogramCount);\n    getLastCudaError(\"mergeHistogram64() execution failed\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/histogram/histogram64.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n#include \"histogram_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Shortcut shared memory atomic addition functions\n////////////////////////////////////////////////////////////////////////////////\n\n#define TAG_MASK 0xFFFFFFFFU\ninline __device__ void addByte(uint *s_WarpHist, uint data, uint threadTag) { atomicAdd(s_WarpHist + data, 1); }\n\ninline __device__ void addWord(uint *s_WarpHist, uint data, uint tag)\n{\n    addByte(s_WarpHist, (data >> 0) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 8) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 16) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 24) & 0xFFU, tag);\n}\n\n__global__ void histogram256Kernel(uint *d_PartialHistograms, uint *d_Data, uint dataCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Per-warp subhistogram storage\n    __shared__ uint s_Hist[HISTOGRAM256_THREADBLOCK_MEMORY];\n    uint           *s_WarpHist = s_Hist + (threadIdx.x >> LOG2_WARP_SIZE) * HISTOGRAM256_BIN_COUNT;\n\n// Clear shared memory storage for current threadblock before processing\n#pragma unroll\n\n    for (uint i = 0; i < (HISTOGRAM256_THREADBLOCK_MEMORY / HISTOGRAM256_THREADBLOCK_SIZE); i++) {\n        s_Hist[threadIdx.x + i * HISTOGRAM256_THREADBLOCK_SIZE] = 0;\n    }\n\n    // Cycle through the entire data set, update subhistograms for each warp\n    const uint tag = threadIdx.x << (UINT_BITS - LOG2_WARP_SIZE);\n\n    cg::sync(cta);\n\n    for (uint pos = UMAD(blockIdx.x, blockDim.x, threadIdx.x); pos < dataCount; pos += UMUL(blockDim.x, gridDim.x)) {\n        uint data = d_Data[pos];\n        addWord(s_WarpHist, data, tag);\n    }\n\n    // Merge per-warp histograms into per-block and write to global memory\n    cg::sync(cta);\n\n    for (uint bin = threadIdx.x; bin < HISTOGRAM256_BIN_COUNT; bin += HISTOGRAM256_THREADBLOCK_SIZE) {\n        uint sum = 0;\n\n        for (uint i = 0; i < WARP_COUNT; i++) {\n            sum += s_Hist[bin + i * HISTOGRAM256_BIN_COUNT] & TAG_MASK;\n        }\n\n        d_PartialHistograms[blockIdx.x * HISTOGRAM256_BIN_COUNT + bin] = sum;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge histogram256() output\n// Run one threadblock per bin; each threadblock adds up the same bin counter\n// from every partial histogram. Reads are uncoalesced, but mergeHistogram256\n// takes only a fraction of total processing time\n////////////////////////////////////////////////////////////////////////////////\n#define MERGE_THREADBLOCK_SIZE 256\n\n__global__ void mergeHistogram256Kernel(uint *d_Histogram, uint *d_PartialHistograms, uint histogramCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n\n    uint sum = 0;\n\n    for (uint i = threadIdx.x; i < histogramCount; i += MERGE_THREADBLOCK_SIZE) {\n        sum += d_PartialHistograms[blockIdx.x + i * HISTOGRAM256_BIN_COUNT];\n    }\n\n    __shared__ uint data[MERGE_THREADBLOCK_SIZE];\n    data[threadIdx.x] = sum;\n\n    for (uint stride = MERGE_THREADBLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n\n        if (threadIdx.x < stride) {\n            data[threadIdx.x] += data[threadIdx.x + stride];\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        d_Histogram[blockIdx.x] = data[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host interface to GPU histogram\n////////////////////////////////////////////////////////////////////////////////\n// histogram256kernel() intermediate results buffer\nstatic const uint PARTIAL_HISTOGRAM256_COUNT = 240;\nstatic uint      *d_PartialHistograms;\n\n// Internal memory allocation\nextern \"C\" void initHistogram256(void)\n{\n    checkCudaErrors(\n        cudaMalloc((void **)&d_PartialHistograms, PARTIAL_HISTOGRAM256_COUNT * HISTOGRAM256_BIN_COUNT * sizeof(uint)));\n}\n\n// Internal memory deallocation\nextern \"C\" void closeHistogram256(void) { checkCudaErrors(cudaFree(d_PartialHistograms)); }\n\nextern \"C\" void histogram256(uint *d_Histogram, void *d_Data, uint byteCount)\n{\n    assert(byteCount % sizeof(uint) == 0);\n    histogram256Kernel<<<PARTIAL_HISTOGRAM256_COUNT, HISTOGRAM256_THREADBLOCK_SIZE>>>(\n        d_PartialHistograms, (uint *)d_Data, byteCount / sizeof(uint));\n    getLastCudaError(\"histogram256Kernel() execution failed\\n\");\n\n    mergeHistogram256Kernel<<<HISTOGRAM256_BIN_COUNT, MERGE_THREADBLOCK_SIZE>>>(\n        d_Histogram, d_PartialHistograms, PARTIAL_HISTOGRAM256_COUNT);\n    getLastCudaError(\"mergeHistogram256Kernel() execution failed\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n#include \"histogram_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Shortcut shared memory atomic addition functions\n////////////////////////////////////////////////////////////////////////////////\n\n#define TAG_MASK 0xFFFFFFFFU\ninline __device__ void addByte(uint *s_WarpHist, uint data, uint threadTag) { atomicAdd(s_WarpHist + data, 1); }\n\ninline __device__ void addWord(uint *s_WarpHist, uint data, uint tag)\n{\n    addByte(s_WarpHist, (data >> 0) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 8) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 16) & 0xFFU, tag);\n    addByte(s_WarpHist, (data >> 24) & 0xFFU, tag);\n}\n\n__global__ void histogram256Kernel(uint *d_PartialHistograms, uint *d_Data, uint dataCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Per-warp subhistogram storage\n    __shared__ uint s_Hist[HISTOGRAM256_THREADBLOCK_MEMORY];\n    uint           *s_WarpHist = s_Hist + (threadIdx.x >> LOG2_WARP_SIZE) * HISTOGRAM256_BIN_COUNT;\n\n// Clear shared memory storage for current threadblock before processing\n#pragma unroll\n\n    for (uint i = 0; i < (HISTOGRAM256_THREADBLOCK_MEMORY / HISTOGRAM256_THREADBLOCK_SIZE); i++) {\n        s_Hist[threadIdx.x + i * HISTOGRAM256_THREADBLOCK_SIZE] = 0;\n    }\n\n    // Cycle through the entire data set, update subhistograms for each warp\n    const uint tag = threadIdx.x << (UINT_BITS - LOG2_WARP_SIZE);\n\n    cg::sync(cta);\n\n    for (uint pos = UMAD(blockIdx.x, blockDim.x, threadIdx.x); pos < dataCount; pos += UMUL(blockDim.x, gridDim.x)) {\n        uint data = d_Data[pos];\n        addWord(s_WarpHist, data, tag);\n    }\n\n    // Merge per-warp histograms into per-block and write to global memory\n    cg::sync(cta);\n\n    for (uint bin = threadIdx.x; bin < HISTOGRAM256_BIN_COUNT; bin += HISTOGRAM256_THREADBLOCK_SIZE) {\n        uint sum = 0;\n\n        for (uint i = 0; i < WARP_COUNT; i++) {\n            sum += s_Hist[bin + i * HISTOGRAM256_BIN_COUNT] & TAG_MASK;\n        }\n\n        d_PartialHistograms[blockIdx.x * HISTOGRAM256_BIN_COUNT + bin] = sum;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Merge histogram256() output\n// Run one threadblock per bin; each threadblock adds up the same bin counter\n// from every partial histogram. Reads are uncoalesced, but mergeHistogram256\n// takes only a fraction of total processing time\n////////////////////////////////////////////////////////////////////////////////\n#define MERGE_THREADBLOCK_SIZE 256\n\n__global__ void mergeHistogram256Kernel(uint *d_Histogram, uint *d_PartialHistograms, uint histogramCount)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n\n    uint sum = 0;\n\n    for (uint i = threadIdx.x; i < histogramCount; i += MERGE_THREADBLOCK_SIZE) {\n        sum += d_PartialHistograms[blockIdx.x + i * HISTOGRAM256_BIN_COUNT];\n    }\n\n    __shared__ uint data[MERGE_THREADBLOCK_SIZE];\n    data[threadIdx.x] = sum;\n\n    for (uint stride = MERGE_THREADBLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n\n        if (threadIdx.x < stride) {\n            data[threadIdx.x] += data[threadIdx.x + stride];\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        d_Histogram[blockIdx.x] = data[0];\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host interface to GPU histogram\n////////////////////////////////////////////////////////////////////////////////\n// histogram256kernel() intermediate results buffer\nstatic const uint PARTIAL_HISTOGRAM256_COUNT = 240;\nstatic uint      *d_PartialHistograms;\n\n// Internal memory allocation\nextern \"C\" void initHistogram256(void)\n{\n    checkCudaErrors(\n        hipMalloc((void **)&d_PartialHistograms, PARTIAL_HISTOGRAM256_COUNT * HISTOGRAM256_BIN_COUNT * sizeof(uint)));\n}\n\n// Internal memory deallocation\nextern \"C\" void closeHistogram256(void) { checkCudaErrors(hipFree(d_PartialHistograms)); }\n\nextern \"C\" void histogram256(uint *d_Histogram, void *d_Data, uint byteCount)\n{\n    assert(byteCount % sizeof(uint) == 0);\n    histogram256Kernel<<<PARTIAL_HISTOGRAM256_COUNT, HISTOGRAM256_THREADBLOCK_SIZE>>>(\n        d_PartialHistograms, (uint *)d_Data, byteCount / sizeof(uint));\n    getLastCudaError(\"histogram256Kernel() execution failed\\n\");\n\n    mergeHistogram256Kernel<<<HISTOGRAM256_BIN_COUNT, MERGE_THREADBLOCK_SIZE>>>(\n        d_Histogram, d_PartialHistograms, PARTIAL_HISTOGRAM256_COUNT);\n    getLastCudaError(\"mergeHistogram256Kernel() execution failed\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/histogram/histogram256.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <curand.h>\n\nusing std::string;\nusing std::vector;\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, const Real *const points, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Shift the input/output pointers\n    const Real *pointx = points + tid;\n    const Real *pointy = pointx + numSims;\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step, pointx += step, pointy += step) {\n        Real x       = *pointx;\n        Real y       = *pointy;\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device, unsigned int threadBlockSize)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    cudaError_t               cudaResult = cudaSuccess;\n    struct cudaDeviceProp     deviceProperties;\n    struct cudaFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = cudaSetDevice(m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for points\n    // Each simulation has two random numbers to give X and Y coordinate\n    Real *d_points = 0;\n    cudaResult     = cudaMalloc((void **)&d_points, 2 * m_numSims * sizeof(Real));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for random numbers: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate random points in unit square\n    curandStatus_t    curandResult;\n    curandGenerator_t qrng;\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = curandCreateGenerator(&qrng, CURAND_RNG_QUASI_SOBOL32);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = curandCreateGenerator(&qrng, CURAND_RNG_QUASI_SOBOL64);\n    }\n    else {\n        string msg(\"Could not create random number generator of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not create quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = curandSetQuasiRandomGeneratorDimensions(qrng, 2);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not set number of dimensions for quasi-random number \"\n                   \"generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = curandSetGeneratorOrdering(qrng, CURAND_ORDERING_QUASI_DEFAULT);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not set order for quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = curandGenerateUniform(qrng, (float *)d_points, 2 * m_numSims);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = curandGenerateUniformDouble(qrng, (double *)d_points, 2 * m_numSims);\n    }\n    else {\n        string msg(\"Could not generate random numbers of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not generate quasi-random numbers: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = curandDestroyGenerator(qrng);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not destroy quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_points, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), cudaMemcpyDeviceToHost);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_points) {\n        cudaFree(d_points);\n        d_points = 0;\n    }\n\n    if (d_results) {\n        cudaFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand.h>\n\nusing std::string;\nusing std::vector;\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, const Real *const points, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Shift the input/output pointers\n    const Real *pointx = points + tid;\n    const Real *pointy = pointx + numSims;\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step, pointx += step, pointy += step) {\n        Real x       = *pointx;\n        Real y       = *pointy;\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims, unsigned int device, unsigned int threadBlockSize)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    hipError_t               cudaResult = hipSuccess;\n    struct hipDeviceProp_t     deviceProperties;\n    struct hipFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = hipGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = hipSetDevice(m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(computeValue<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for points\n    // Each simulation has two random numbers to give X and Y coordinate\n    Real *d_points = 0;\n    cudaResult     = hipMalloc((void **)&d_points, 2 * m_numSims * sizeof(Real));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for random numbers: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = hipMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate random points in unit square\n    hiprandStatus_t    curandResult;\n    hiprandGenerator_t qrng;\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = hiprandCreateGenerator(&qrng, HIPRAND_RNG_QUASI_SOBOL32);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = hiprandCreateGenerator(&qrng, HIPRAND_RNG_QUASI_SOBOL64);\n    }\n    else {\n        string msg(\"Could not create random number generator of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not create quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = hiprandSetQuasiRandomGeneratorDimensions(qrng, 2);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not set number of dimensions for quasi-random number \"\n                   \"generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = hiprandSetGeneratorOrdering(qrng, HIPRAND_ORDERING_QUASI_DEFAULT);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not set order for quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = hiprandGenerateUniform(qrng, (float *)d_points, 2 * m_numSims);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = hiprandGenerateUniformDouble(qrng, (double *)d_points, 2 * m_numSims);\n    }\n    else {\n        string msg(\"Could not generate random numbers of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not generate quasi-random numbers: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = hiprandDestroyGenerator(qrng);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not destroy quasi-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_points, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = hipMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), hipMemcpyDeviceToHost);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_points) {\n        hipFree(d_points);\n        d_points = 0;\n    }\n\n    if (d_results) {\n        hipFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/MC_EstimatePiQ/src/piestimator.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates peer-to-peer access of stream ordered memory\n * allocated with cudaMallocAsync and cudaMemPool family of APIs through simple\n * kernel which does peer-to-peer to access & scales vector elements.\n */\n\n// System includes\n#include <assert.h>\n#include <iostream>\n#include <map>\n#include <set>\n#include <stdio.h>\n#include <utility>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n// Simple kernel to demonstrate copying cudaMallocAsync memory via P2P to peer\n// device\n__global__ void copyP2PAndScale(const int *src, int *dst, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        // scale & store src vector.\n        dst[idx] = 2 * src[idx];\n    }\n}\n\n// Map of device version to device number\nstd::multimap<std::pair<int, int>, int> getIdenticalGPUs()\n{\n    int numGpus = 0;\n    checkCudaErrors(cudaGetDeviceCount(&numGpus));\n\n    std::multimap<std::pair<int, int>, int> identicalGpus;\n\n    for (int i = 0; i < numGpus; i++) {\n        int isMemPoolSupported = 0;\n        checkCudaErrors(cudaDeviceGetAttribute(&isMemPoolSupported, cudaDevAttrMemoryPoolsSupported, i));\n\n        // Filter unsupported devices\n        if (isMemPoolSupported) {\n            int major = 0, minor = 0;\n            checkCudaErrors(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, i));\n            checkCudaErrors(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, i));\n            identicalGpus.emplace(std::make_pair(major, minor), i);\n        }\n    }\n\n    return identicalGpus;\n}\n\nstd::pair<int, int> getP2PCapableGpuPair()\n{\n    constexpr size_t kNumGpusRequired = 2;\n\n    auto gpusByArch = getIdenticalGPUs();\n\n    auto it  = gpusByArch.begin();\n    auto end = gpusByArch.end();\n\n    auto bestFit = std::make_pair(it, it);\n    // use std::distance to find the largest number of GPUs amongst architectures\n    auto distance = [](decltype(bestFit) p) { return std::distance(p.first, p.second); };\n\n    // Read each unique key/pair element in order\n    for (; it != end; it = gpusByArch.upper_bound(it->first)) {\n        // first and second are iterators bounded within the architecture group\n        auto testFit = gpusByArch.equal_range(it->first);\n        // Always use devices with highest architecture version or whichever has the\n        // most devices available\n        if (distance(bestFit) <= distance(testFit))\n            bestFit = testFit;\n    }\n\n    if (distance(bestFit) < kNumGpusRequired) {\n        printf(\"No Two or more GPUs with same architecture capable of cuda Memory \"\n               \"Pools found.\"\n               \"\\nWaiving the sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::set<int> bestFitDeviceIds;\n\n    // check & select peer-to-peer access capable GPU devices.\n    int devIds[2];\n    for (auto itr = bestFit.first; itr != bestFit.second; itr++) {\n        int deviceId = itr->second;\n        checkCudaErrors(cudaSetDevice(deviceId));\n\n        std::for_each(itr, bestFit.second, [&deviceId, &bestFitDeviceIds, &kNumGpusRequired](decltype(*itr) mapPair) {\n            if (deviceId != mapPair.second) {\n                int access = 0;\n                checkCudaErrors(cudaDeviceCanAccessPeer(&access, deviceId, mapPair.second));\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", deviceId, access ? \"CAN\" : \"CANNOT\", mapPair.second);\n                if (access && bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(deviceId);\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n            }\n        });\n\n        if (bestFitDeviceIds.size() >= kNumGpusRequired) {\n            printf(\"Selected p2p capable devices - \");\n            int i = 0;\n            for (auto devicesItr = bestFitDeviceIds.begin(); devicesItr != bestFitDeviceIds.end(); devicesItr++) {\n                devIds[i++] = *devicesItr;\n                printf(\"deviceId = %d  \", *devicesItr);\n            }\n            printf(\"\\n\");\n            break;\n        }\n    }\n\n    // if bestFitDeviceIds.size() == 0 it means the GPUs in system are not p2p\n    // capable, hence we add it without p2p capability check.\n    if (!bestFitDeviceIds.size()) {\n        printf(\"No Two or more Devices p2p capable found.. exiting..\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    auto p2pGpuPair = std::make_pair(devIds[0], devIds[1]);\n\n    return p2pGpuPair;\n}\n\nint memPoolP2PCopy()\n{\n    int          *dev0_srcVec, *dev1_dstVec; // Device buffers\n    cudaStream_t  stream1, stream2;\n    cudaMemPool_t memPool;\n    cudaEvent_t   waitOnStream1;\n\n    // Allocate CPU memory.\n    size_t nelem = 1048576;\n    size_t bytes = nelem * sizeof(int);\n\n    int *a      = (int *)malloc(bytes);\n    int *output = (int *)malloc(bytes);\n\n    /* Initialize the vectors. */\n    for (int n = 0; n < nelem; n++) {\n        a[n] = rand() / (int)RAND_MAX;\n    }\n\n    auto p2pDevices = getP2PCapableGpuPair();\n    printf(\"selected devices = %d & %d\\n\", p2pDevices.first, p2pDevices.second);\n    checkCudaErrors(cudaSetDevice(p2pDevices.first));\n    checkCudaErrors(cudaEventCreate(&waitOnStream1));\n\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream1, cudaStreamNonBlocking));\n\n    // Get the default mempool for device p2pDevices.first from the pair\n    checkCudaErrors(cudaDeviceGetDefaultMemPool(&memPool, p2pDevices.first));\n\n    // Allocate memory in a stream from the pool set above.\n    checkCudaErrors(cudaMallocAsync(&dev0_srcVec, bytes, stream1));\n\n    checkCudaErrors(cudaMemcpyAsync(dev0_srcVec, a, bytes, cudaMemcpyHostToDevice, stream1));\n    checkCudaErrors(cudaEventRecord(waitOnStream1, stream1));\n\n    checkCudaErrors(cudaSetDevice(p2pDevices.second));\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream2, cudaStreamNonBlocking));\n\n    // Allocate memory in p2pDevices.second device\n    checkCudaErrors(cudaMallocAsync(&dev1_dstVec, bytes, stream2));\n\n    // Setup peer mappings for p2pDevices.second device\n    cudaMemAccessDesc desc;\n    memset(&desc, 0, sizeof(cudaMemAccessDesc));\n    desc.location.type = cudaMemLocationTypeDevice;\n    desc.location.id   = p2pDevices.second;\n    desc.flags         = cudaMemAccessFlagsProtReadWrite;\n    checkCudaErrors(cudaMemPoolSetAccess(memPool, &desc, 1));\n\n    printf(\"> copyP2PAndScale kernel running ...\\n\");\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (int)block.x));\n    checkCudaErrors(cudaStreamWaitEvent(stream2, waitOnStream1));\n    copyP2PAndScale<<<grid, block, 0, stream2>>>(dev0_srcVec, dev1_dstVec, nelem);\n\n    checkCudaErrors(cudaMemcpyAsync(output, dev1_dstVec, bytes, cudaMemcpyDeviceToHost, stream2));\n    checkCudaErrors(cudaFreeAsync(dev0_srcVec, stream2));\n    checkCudaErrors(cudaFreeAsync(dev1_dstVec, stream2));\n    checkCudaErrors(cudaStreamSynchronize(stream2));\n\n    /* Compare the results */\n    printf(\"> Checking the results from copyP2PAndScale() ...\\n\");\n\n    for (int n = 0; n < nelem; n++) {\n        if ((2 * a[n]) != output[n]) {\n            printf(\"mismatch i = %d expected = %d val = %d\\n\", n, 2 * a[n], output[n]);\n            return EXIT_FAILURE;\n        }\n    }\n\n    free(a);\n    free(output);\n    checkCudaErrors(cudaStreamDestroy(stream1));\n    checkCudaErrors(cudaStreamDestroy(stream2));\n    printf(\"PASSED\\n\");\n\n    return EXIT_SUCCESS;\n}\n\nint main(int argc, char **argv)\n{\n    int ret = memPoolP2PCopy();\n    return ret;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates peer-to-peer access of stream ordered memory\n * allocated with cudaMallocAsync and cudaMemPool family of APIs through simple\n * kernel which does peer-to-peer to access & scales vector elements.\n */\n\n// System includes\n#include <assert.h>\n#include <iostream>\n#include <map>\n#include <set>\n#include <stdio.h>\n#include <utility>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n// Simple kernel to demonstrate copying cudaMallocAsync memory via P2P to peer\n// device\n__global__ void copyP2PAndScale(const int *src, int *dst, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        // scale & store src vector.\n        dst[idx] = 2 * src[idx];\n    }\n}\n\n// Map of device version to device number\nstd::multimap<std::pair<int, int>, int> getIdenticalGPUs()\n{\n    int numGpus = 0;\n    checkCudaErrors(hipGetDeviceCount(&numGpus));\n\n    std::multimap<std::pair<int, int>, int> identicalGpus;\n\n    for (int i = 0; i < numGpus; i++) {\n        int isMemPoolSupported = 0;\n        checkCudaErrors(hipDeviceGetAttribute(&isMemPoolSupported, hipDeviceAttributeMemoryPoolsSupported, i));\n\n        // Filter unsupported devices\n        if (isMemPoolSupported) {\n            int major = 0, minor = 0;\n            checkCudaErrors(hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, i));\n            checkCudaErrors(hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, i));\n            identicalGpus.emplace(std::make_pair(major, minor), i);\n        }\n    }\n\n    return identicalGpus;\n}\n\nstd::pair<int, int> getP2PCapableGpuPair()\n{\n    constexpr size_t kNumGpusRequired = 2;\n\n    auto gpusByArch = getIdenticalGPUs();\n\n    auto it  = gpusByArch.begin();\n    auto end = gpusByArch.end();\n\n    auto bestFit = std::make_pair(it, it);\n    // use std::distance to find the largest number of GPUs amongst architectures\n    auto distance = [](decltype(bestFit) p) { return std::distance(p.first, p.second); };\n\n    // Read each unique key/pair element in order\n    for (; it != end; it = gpusByArch.upper_bound(it->first)) {\n        // first and second are iterators bounded within the architecture group\n        auto testFit = gpusByArch.equal_range(it->first);\n        // Always use devices with highest architecture version or whichever has the\n        // most devices available\n        if (distance(bestFit) <= distance(testFit))\n            bestFit = testFit;\n    }\n\n    if (distance(bestFit) < kNumGpusRequired) {\n        printf(\"No Two or more GPUs with same architecture capable of cuda Memory \"\n               \"Pools found.\"\n               \"\\nWaiving the sample\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    std::set<int> bestFitDeviceIds;\n\n    // check & select peer-to-peer access capable GPU devices.\n    int devIds[2];\n    for (auto itr = bestFit.first; itr != bestFit.second; itr++) {\n        int deviceId = itr->second;\n        checkCudaErrors(hipSetDevice(deviceId));\n\n        std::for_each(itr, bestFit.second, [&deviceId, &bestFitDeviceIds, &kNumGpusRequired](decltype(*itr) mapPair) {\n            if (deviceId != mapPair.second) {\n                int access = 0;\n                checkCudaErrors(hipDeviceCanAccessPeer(&access, deviceId, mapPair.second));\n                printf(\"Device=%d %s Access Peer Device=%d\\n\", deviceId, access ? \"CAN\" : \"CANNOT\", mapPair.second);\n                if (access && bestFitDeviceIds.size() < kNumGpusRequired) {\n                    bestFitDeviceIds.emplace(deviceId);\n                    bestFitDeviceIds.emplace(mapPair.second);\n                }\n                else {\n                    printf(\"Ignoring device %i (max devices exceeded)\\n\", mapPair.second);\n                }\n            }\n        });\n\n        if (bestFitDeviceIds.size() >= kNumGpusRequired) {\n            printf(\"Selected p2p capable devices - \");\n            int i = 0;\n            for (auto devicesItr = bestFitDeviceIds.begin(); devicesItr != bestFitDeviceIds.end(); devicesItr++) {\n                devIds[i++] = *devicesItr;\n                printf(\"deviceId = %d  \", *devicesItr);\n            }\n            printf(\"\\n\");\n            break;\n        }\n    }\n\n    // if bestFitDeviceIds.size() == 0 it means the GPUs in system are not p2p\n    // capable, hence we add it without p2p capability check.\n    if (!bestFitDeviceIds.size()) {\n        printf(\"No Two or more Devices p2p capable found.. exiting..\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    auto p2pGpuPair = std::make_pair(devIds[0], devIds[1]);\n\n    return p2pGpuPair;\n}\n\nint memPoolP2PCopy()\n{\n    int          *dev0_srcVec, *dev1_dstVec; // Device buffers\n    hipStream_t  stream1, stream2;\n    hipMemPool_t memPool;\n    hipEvent_t   waitOnStream1;\n\n    // Allocate CPU memory.\n    size_t nelem = 1048576;\n    size_t bytes = nelem * sizeof(int);\n\n    int *a      = (int *)malloc(bytes);\n    int *output = (int *)malloc(bytes);\n\n    /* Initialize the vectors. */\n    for (int n = 0; n < nelem; n++) {\n        a[n] = rand() / (int)RAND_MAX;\n    }\n\n    auto p2pDevices = getP2PCapableGpuPair();\n    printf(\"selected devices = %d & %d\\n\", p2pDevices.first, p2pDevices.second);\n    checkCudaErrors(hipSetDevice(p2pDevices.first));\n    checkCudaErrors(hipEventCreate(&waitOnStream1));\n\n    checkCudaErrors(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking));\n\n    // Get the default mempool for device p2pDevices.first from the pair\n    checkCudaErrors(hipDeviceGetDefaultMemPool(&memPool, p2pDevices.first));\n\n    // Allocate memory in a stream from the pool set above.\n    checkCudaErrors(hipMallocAsync(&dev0_srcVec, bytes, stream1));\n\n    checkCudaErrors(hipMemcpyAsync(dev0_srcVec, a, bytes, hipMemcpyHostToDevice, stream1));\n    checkCudaErrors(hipEventRecord(waitOnStream1, stream1));\n\n    checkCudaErrors(hipSetDevice(p2pDevices.second));\n    checkCudaErrors(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking));\n\n    // Allocate memory in p2pDevices.second device\n    checkCudaErrors(hipMallocAsync(&dev1_dstVec, bytes, stream2));\n\n    // Setup peer mappings for p2pDevices.second device\n    hipMemAccessDesc desc;\n    memset(&desc, 0, sizeof(hipMemAccessDesc));\n    desc.location.type = hipMemLocationTypeDevice;\n    desc.location.id   = p2pDevices.second;\n    desc.flags         = hipMemAccessFlagsProtReadWrite;\n    checkCudaErrors(hipMemPoolSetAccess(memPool, &desc, 1));\n\n    printf(\"> copyP2PAndScale kernel running ...\\n\");\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (int)block.x));\n    checkCudaErrors(hipStreamWaitEvent(stream2, waitOnStream1));\n    copyP2PAndScale<<<grid, block, 0, stream2>>>(dev0_srcVec, dev1_dstVec, nelem);\n\n    checkCudaErrors(hipMemcpyAsync(output, dev1_dstVec, bytes, hipMemcpyDeviceToHost, stream2));\n    checkCudaErrors(hipFreeAsync(dev0_srcVec, stream2));\n    checkCudaErrors(hipFreeAsync(dev1_dstVec, stream2));\n    checkCudaErrors(hipStreamSynchronize(stream2));\n\n    /* Compare the results */\n    printf(\"> Checking the results from copyP2PAndScale() ...\\n\");\n\n    for (int n = 0; n < nelem; n++) {\n        if ((2 * a[n]) != output[n]) {\n            printf(\"mismatch i = %d expected = %d val = %d\\n\", n, 2 * a[n], output[n]);\n            return EXIT_FAILURE;\n        }\n    }\n\n    free(a);\n    free(output);\n    checkCudaErrors(hipStreamDestroy(stream1));\n    checkCudaErrors(hipStreamDestroy(stream2));\n    printf(\"PASSED\\n\");\n\n    return EXIT_SUCCESS;\n}\n\nint main(int argc, char **argv)\n{\n    int ret = memPoolP2PCopy();\n    return ret;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/streamOrderedAllocationP2P/streamOrderedAllocationP2P.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Demonstration of inline PTX (assembly language) usage in CUDA kernels\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n__global__ void sequence_gpu(int *d_ptr, int length)\n{\n    int elemID = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (elemID < length) {\n        unsigned int laneid;\n        // This command gets the lane ID within the current warp\n        asm(\"mov.u32 %0, %%laneid;\" : \"=r\"(laneid));\n        d_ptr[elemID] = laneid;\n    }\n}\n\n\nvoid sequence_cpu(int *h_ptr, int length)\n{\n    for (int elemID = 0; elemID < length; elemID++) {\n        h_ptr[elemID] = elemID % 32;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"CUDA inline PTX assembler sample\\n\");\n\n    const int N = 1000;\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    if (dev == -1) {\n        return EXIT_FAILURE;\n    }\n\n    int *d_ptr;\n    checkCudaErrors(cudaMalloc(&d_ptr, N * sizeof(int)));\n\n    int *h_ptr;\n    checkCudaErrors(cudaMallocHost(&h_ptr, N * sizeof(int)));\n\n    dim3 cudaBlockSize(256, 1, 1);\n    dim3 cudaGridSize((N + cudaBlockSize.x - 1) / cudaBlockSize.x, 1, 1);\n    sequence_gpu<<<cudaGridSize, cudaBlockSize>>>(d_ptr, N);\n    checkCudaErrors(cudaGetLastError());\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    sequence_cpu(h_ptr, N);\n\n    int *h_d_ptr;\n    checkCudaErrors(cudaMallocHost(&h_d_ptr, N * sizeof(int)));\n    checkCudaErrors(cudaMemcpy(h_d_ptr, d_ptr, N * sizeof(int), cudaMemcpyDeviceToHost));\n\n    bool bValid = true;\n\n    for (int i = 0; i < N && bValid; i++) {\n        if (h_ptr[i] != h_d_ptr[i]) {\n            bValid = false;\n        }\n    }\n\n    printf(\"Test %s.\\n\", bValid ? \"Successful\" : \"Failed\");\n\n    checkCudaErrors(cudaFree(d_ptr));\n    checkCudaErrors(cudaFreeHost(h_ptr));\n    checkCudaErrors(cudaFreeHost(h_d_ptr));\n\n    return bValid ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * Demonstration of inline PTX (assembly language) usage in CUDA kernels\n */\n\n// System includes\n#include <assert.h>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n__global__ void sequence_gpu(int *d_ptr, int length)\n{\n    int elemID = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (elemID < length) {\n        unsigned int laneid;\n        // This command gets the lane ID within the current warp\n        asm(\"mov.u32 %0, %%laneid;\" : \"=r\"(laneid));\n        d_ptr[elemID] = laneid;\n    }\n}\n\n\nvoid sequence_cpu(int *h_ptr, int length)\n{\n    for (int elemID = 0; elemID < length; elemID++) {\n        h_ptr[elemID] = elemID % 32;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    printf(\"CUDA inline PTX assembler sample\\n\");\n\n    const int N = 1000;\n\n    int dev = findCudaDevice(argc, (const char **)argv);\n\n    if (dev == -1) {\n        return EXIT_FAILURE;\n    }\n\n    int *d_ptr;\n    checkCudaErrors(hipMalloc(&d_ptr, N * sizeof(int)));\n\n    int *h_ptr;\n    checkCudaErrors(hipHostMalloc(&h_ptr, N * sizeof(int)));\n\n    dim3 cudaBlockSize(256, 1, 1);\n    dim3 cudaGridSize((N + cudaBlockSize.x - 1) / cudaBlockSize.x, 1, 1);\n    sequence_gpu<<<cudaGridSize, cudaBlockSize>>>(d_ptr, N);\n    checkCudaErrors(hipGetLastError());\n    checkCudaErrors(hipDeviceSynchronize());\n\n    sequence_cpu(h_ptr, N);\n\n    int *h_d_ptr;\n    checkCudaErrors(hipHostMalloc(&h_d_ptr, N * sizeof(int)));\n    checkCudaErrors(hipMemcpy(h_d_ptr, d_ptr, N * sizeof(int), hipMemcpyDeviceToHost));\n\n    bool bValid = true;\n\n    for (int i = 0; i < N && bValid; i++) {\n        if (h_ptr[i] != h_d_ptr[i]) {\n            bValid = false;\n        }\n    }\n\n    printf(\"Test %s.\\n\", bValid ? \"Successful\" : \"Failed\");\n\n    checkCudaErrors(hipFree(d_ptr));\n    checkCudaErrors(hipHostFree(h_ptr));\n    checkCudaErrors(hipHostFree(h_d_ptr));\n\n    return bValid ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/inlinePTX/inlinePTX.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _BOXFILTER_KERNEL_CH_\n#define _BOXFILTER_KERNEL_CH_\n\n#include <helper_functions.h>\n#include <helper_math.h>\n\ncudaTextureObject_t tex;\ncudaTextureObject_t texTempArray;\ncudaTextureObject_t rgbaTex;\ncudaTextureObject_t rgbaTexTempArray;\ncudaArray          *d_array, *d_tempArray;\n\n////////////////////////////////////////////////////////////////////////////////\n// These are CUDA Helper functions\n\n// This will output the proper CUDA error strings in the event that a CUDA host\n// call returns an error\n#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)\n\ninline void __checkCudaErrors(cudaError err, const char *file, const int line)\n{\n    if (cudaSuccess != err) {\n        fprintf(stderr, \"%s(%i) : CUDA Runtime API error %d: %s.\\n\", file, line, (int)err, cudaGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n}\n\n/*\n  Perform a fast box filter using the sliding window method.\n\n  As the kernel moves from left to right, we add in the contribution of the\n  new sample on the right, and subtract the value of the exiting sample on the\n  left. This only requires 2 adds and a mul per output value, independent of the\n  filter radius. The box filter is separable, so to perform a 2D box filter we\n  perform the filter in the x direction, followed by the same filter in the y\n  direction. Applying multiple iterations of the box filter converges towards a\n  Gaussian blur. Using CUDA, rows or columns of the image are processed in\n  parallel. This version duplicates edge pixels.\n\n  Note that the x (row) pass suffers from uncoalesced global memory reads,\n  since each thread is reading from a different row. For this reason it is\n  better to use texture lookups for the x pass.\n  The y (column) pass is perfectly coalesced.\n\n  Parameters\n  id - pointer to input data in global memory\n  od - pointer to output data in global memory\n  w  - image width\n  h  - image height\n  r  - filter radius\n\n  e.g. for r = 2, w = 8:\n\n  0 1 2 3 4 5 6 7\n  x - -\n  - x - -\n  - - x - -\n    - - x - -\n      - - x - -\n        - - x - -\n          - - x -\n            - - x\n*/\n\n// process row\n__device__ void d_boxfilter_x(float *id, float *od, int w, int h, int r)\n{\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float t;\n    // do left edge\n    t = id[0] * r;\n\n    for (int x = 0; x < (r + 1); x++) {\n        t += id[x];\n    }\n\n    od[0] = t * scale;\n\n    for (int x = 1; x < (r + 1); x++) {\n        t += id[x + r];\n        t -= id[0];\n        od[x] = t * scale;\n    }\n\n    // main loop\n    for (int x = (r + 1); x < w - r; x++) {\n        t += id[x + r];\n        t -= id[x - r - 1];\n        od[x] = t * scale;\n    }\n\n    // do right edge\n    for (int x = w - r; x < w; x++) {\n        t += id[w - 1];\n        t -= id[x - r - 1];\n        od[x] = t * scale;\n    }\n}\n\n// process column\n__device__ void d_boxfilter_y(float *id, float *od, int w, int h, int r)\n{\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float t;\n    // do left edge\n    t = id[0] * r;\n\n    for (int y = 0; y < (r + 1); y++) {\n        t += id[y * w];\n    }\n\n    od[0] = t * scale;\n\n    for (int y = 1; y < (r + 1); y++) {\n        t += id[(y + r) * w];\n        t -= id[0];\n        od[y * w] = t * scale;\n    }\n\n    // main loop\n    for (int y = (r + 1); y < (h - r); y++) {\n        t += id[(y + r) * w];\n        t -= id[((y - r) * w) - w];\n        od[y * w] = t * scale;\n    }\n\n    // do right edge\n    for (int y = h - r; y < h; y++) {\n        t += id[(h - 1) * w];\n        t -= id[((y - r) * w) - w];\n        od[y * w] = t * scale;\n    }\n}\n\n__global__ void d_boxfilter_x_global(float *id, float *od, int w, int h, int r)\n{\n    unsigned int y = blockIdx.x * blockDim.x + threadIdx.x;\n    d_boxfilter_x(&id[y * w], &od[y * w], w, h, r);\n}\n\n__global__ void d_boxfilter_y_global(float *id, float *od, int w, int h, int r)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    d_boxfilter_y(&id[x], &od[x], w, h, r);\n}\n\n// texture version\n// texture fetches automatically clamp to edge of image\n__global__ void d_boxfilter_x_tex(float *od, int w, int h, int r, cudaTextureObject_t tex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    float t = 0.0f;\n\n    for (int x = -r; x <= r; x++) {\n        t += tex2D<float>(tex, x, y);\n    }\n\n    od[y * w] = t * scale;\n\n    for (int x = 1; x < w; x++) {\n        t += tex2D<float>(tex, x + r, y);\n        t -= tex2D<float>(tex, x - r - 1, y);\n        od[y * w + x] = t * scale;\n    }\n}\n\n__global__ void d_boxfilter_y_tex(float *od, int w, int h, int r, cudaTextureObject_t tex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int x     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    float t = 0.0f;\n\n    for (int y = -r; y <= r; y++) {\n        t += tex2D<float>(tex, x, y);\n    }\n\n    od[x] = t * scale;\n\n    for (int y = 1; y < h; y++) {\n        t += tex2D<float>(tex, x, y + r);\n        t -= tex2D<float>(tex, x, y - r - 1);\n        od[y * w + x] = t * scale;\n    }\n}\n\n// RGBA version\n// reads from 32-bit unsigned int array holding 8-bit RGBA\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n__device__ float4 rgbaIntToFloat(unsigned int c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\n// row pass using texture lookups\n__global__ void d_boxfilter_rgba_x(unsigned int *od, int w, int h, int r, cudaTextureObject_t rgbaTex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // as long as address is always less than height, we do work\n    if (y < h) {\n        float4 t = make_float4(0.0f);\n\n        for (int x = -r; x <= r; x++) {\n            t += tex2D<float4>(rgbaTex, x, y);\n        }\n\n        od[y * w] = rgbaFloatToInt(t * scale);\n\n        for (int x = 1; x < w; x++) {\n            t += tex2D<float4>(rgbaTex, x + r, y);\n            t -= tex2D<float4>(rgbaTex, x - r - 1, y);\n            od[y * w + x] = rgbaFloatToInt(t * scale);\n        }\n    }\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_boxfilter_rgba_y(unsigned int *id, unsigned int *od, int w, int h, int r)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    id             = &id[x];\n    od             = &od[x];\n\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float4 t;\n    // do left edge\n    t = rgbaIntToFloat(id[0]) * r;\n\n    for (int y = 0; y < (r + 1); y++) {\n        t += rgbaIntToFloat(id[y * w]);\n    }\n\n    od[0] = rgbaFloatToInt(t * scale);\n\n    for (int y = 1; y < (r + 1); y++) {\n        t += rgbaIntToFloat(id[(y + r) * w]);\n        t -= rgbaIntToFloat(id[0]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n\n    // main loop\n    for (int y = (r + 1); y < (h - r); y++) {\n        t += rgbaIntToFloat(id[(y + r) * w]);\n        t -= rgbaIntToFloat(id[((y - r) * w) - w]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n\n    // do right edge\n    for (int y = h - r; y < h; y++) {\n        t += rgbaIntToFloat(id[(h - 1) * w]);\n        t -= rgbaIntToFloat(id[((y - r) * w) - w]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n}\n\nextern \"C\" void initTexture(int width, int height, void *pImage, bool useRGBA)\n{\n    // copy image data to array\n    cudaChannelFormatDesc channelDesc;\n    if (useRGBA) {\n        channelDesc = cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindUnsigned);\n    }\n    else {\n        channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);\n    }\n    checkCudaErrors(cudaMallocArray(&d_array, &channelDesc, width, height));\n\n    size_t bytesPerElem = (useRGBA ? sizeof(uchar4) : sizeof(float));\n    checkCudaErrors(cudaMemcpy2DToArray(\n        d_array, 0, 0, pImage, width * bytesPerElem, width * bytesPerElem, height, cudaMemcpyHostToDevice));\n\n    checkCudaErrors(cudaMallocArray(&d_tempArray, &channelDesc, width, height));\n\n    // set texture parameters\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&rgbaTex, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_tempArray;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = cudaFilterModeLinear;\n    texDescr.addressMode[0]   = cudaAddressModeClamp;\n    texDescr.addressMode[1]   = cudaAddressModeClamp;\n    texDescr.readMode         = cudaReadModeNormalizedFloat;\n\n    checkCudaErrors(cudaCreateTextureObject(&rgbaTexTempArray, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_array;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = d_tempArray;\n\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = cudaFilterModePoint;\n    texDescr.addressMode[0]   = cudaAddressModeWrap;\n    texDescr.addressMode[1]   = cudaAddressModeWrap;\n    texDescr.readMode         = cudaReadModeElementType;\n\n    checkCudaErrors(cudaCreateTextureObject(&texTempArray, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeTextures()\n{\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n    checkCudaErrors(cudaDestroyTextureObject(texTempArray));\n    checkCudaErrors(cudaDestroyTextureObject(rgbaTex));\n    checkCudaErrors(cudaDestroyTextureObject(rgbaTexTempArray));\n    checkCudaErrors(cudaFreeArray(d_array));\n    checkCudaErrors(cudaFreeArray(d_tempArray));\n}\n\n/*\n    Perform 2D box filter on image using CUDA\n\n    Parameters:\n    d_temp - pointer to temporary storage in device memory\n    d_dest - pointer to destination image in device memory\n    width  - image width\n    height - image height\n    radius - filter radius\n    iterations - number of iterations\n\n*/\nextern \"C\" double boxFilter(float              *d_temp,\n                            float              *d_dest,\n                            int                 width,\n                            int                 height,\n                            int                 radius,\n                            int                 iterations,\n                            int                 nthreads,\n                            StopWatchInterface *timer)\n{\n    // var for kernel timing\n    double dKernelTime = 0.0;\n\n    // sync host and start computation timer_kernel\n    checkCudaErrors(cudaDeviceSynchronize());\n\n    for (int i = 0; i < iterations; i++) {\n        sdkResetTimer(&timer);\n        // use texture for horizontal pass\n        if (iterations > 1) {\n            d_boxfilter_x_tex<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, texTempArray);\n        }\n        else {\n            d_boxfilter_x_tex<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, tex);\n        }\n\n        d_boxfilter_y_global<<<width / nthreads, nthreads, 0>>>(d_temp, d_dest, width, height, radius);\n\n        // sync host and stop computation timer_kernel\n        checkCudaErrors(cudaDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(cudaMemcpy2DToArray(d_tempArray,\n                                                0,\n                                                0,\n                                                d_dest,\n                                                width * sizeof(float),\n                                                width * sizeof(float),\n                                                height,\n                                                cudaMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n\n// RGBA version\nextern \"C\" double boxFilterRGBA(unsigned int       *d_temp,\n                                unsigned int       *d_dest,\n                                int                 width,\n                                int                 height,\n                                int                 radius,\n                                int                 iterations,\n                                int                 nthreads,\n                                StopWatchInterface *timer)\n{\n    // var for kernel computation timing\n    double dKernelTime;\n\n    for (int i = 0; i < iterations; i++) {\n        // sync host and start kernel computation timer_kernel\n        dKernelTime = 0.0;\n        checkCudaErrors(cudaDeviceSynchronize());\n        sdkResetTimer(&timer);\n\n        // use texture for horizontal pass\n        if (iterations > 1) {\n            d_boxfilter_rgba_x<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, rgbaTexTempArray);\n        }\n        else {\n            d_boxfilter_rgba_x<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, rgbaTex);\n        }\n\n        d_boxfilter_rgba_y<<<width / nthreads, nthreads, 0>>>(d_temp, d_dest, width, height, radius);\n\n        // sync host and stop computation timer_kernel\n        checkCudaErrors(cudaDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(cudaMemcpy2DToArray(d_tempArray,\n                                                0,\n                                                0,\n                                                d_dest,\n                                                width * sizeof(unsigned int),\n                                                width * sizeof(unsigned int),\n                                                height,\n                                                cudaMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n\n#endif // #ifndef _BOXFILTER_KERNEL_H_\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef _BOXFILTER_KERNEL_CH_\n#define _BOXFILTER_KERNEL_CH_\n\n#include <helper_functions.h>\n#include <hip/hip_vector_types.h>\n\nhipTextureObject_t tex;\nhipTextureObject_t texTempArray;\nhipTextureObject_t rgbaTex;\nhipTextureObject_t rgbaTexTempArray;\nhipArray          *d_array, *d_tempArray;\n\n////////////////////////////////////////////////////////////////////////////////\n// These are CUDA Helper functions\n\n// This will output the proper CUDA error strings in the event that a CUDA host\n// call returns an error\n#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)\n\ninline void __checkCudaErrors(hipError_t err, const char *file, const int line)\n{\n    if (hipSuccess != err) {\n        fprintf(stderr, \"%s(%i) : CUDA Runtime API error %d: %s.\\n\", file, line, (int)err, hipGetErrorString(err));\n        exit(EXIT_FAILURE);\n    }\n}\n\n/*\n  Perform a fast box filter using the sliding window method.\n\n  As the kernel moves from left to right, we add in the contribution of the\n  new sample on the right, and subtract the value of the exiting sample on the\n  left. This only requires 2 adds and a mul per output value, independent of the\n  filter radius. The box filter is separable, so to perform a 2D box filter we\n  perform the filter in the x direction, followed by the same filter in the y\n  direction. Applying multiple iterations of the box filter converges towards a\n  Gaussian blur. Using CUDA, rows or columns of the image are processed in\n  parallel. This version duplicates edge pixels.\n\n  Note that the x (row) pass suffers from uncoalesced global memory reads,\n  since each thread is reading from a different row. For this reason it is\n  better to use texture lookups for the x pass.\n  The y (column) pass is perfectly coalesced.\n\n  Parameters\n  id - pointer to input data in global memory\n  od - pointer to output data in global memory\n  w  - image width\n  h  - image height\n  r  - filter radius\n\n  e.g. for r = 2, w = 8:\n\n  0 1 2 3 4 5 6 7\n  x - -\n  - x - -\n  - - x - -\n    - - x - -\n      - - x - -\n        - - x - -\n          - - x -\n            - - x\n*/\n\n// process row\n__device__ void d_boxfilter_x(float *id, float *od, int w, int h, int r)\n{\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float t;\n    // do left edge\n    t = id[0] * r;\n\n    for (int x = 0; x < (r + 1); x++) {\n        t += id[x];\n    }\n\n    od[0] = t * scale;\n\n    for (int x = 1; x < (r + 1); x++) {\n        t += id[x + r];\n        t -= id[0];\n        od[x] = t * scale;\n    }\n\n    // main loop\n    for (int x = (r + 1); x < w - r; x++) {\n        t += id[x + r];\n        t -= id[x - r - 1];\n        od[x] = t * scale;\n    }\n\n    // do right edge\n    for (int x = w - r; x < w; x++) {\n        t += id[w - 1];\n        t -= id[x - r - 1];\n        od[x] = t * scale;\n    }\n}\n\n// process column\n__device__ void d_boxfilter_y(float *id, float *od, int w, int h, int r)\n{\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float t;\n    // do left edge\n    t = id[0] * r;\n\n    for (int y = 0; y < (r + 1); y++) {\n        t += id[y * w];\n    }\n\n    od[0] = t * scale;\n\n    for (int y = 1; y < (r + 1); y++) {\n        t += id[(y + r) * w];\n        t -= id[0];\n        od[y * w] = t * scale;\n    }\n\n    // main loop\n    for (int y = (r + 1); y < (h - r); y++) {\n        t += id[(y + r) * w];\n        t -= id[((y - r) * w) - w];\n        od[y * w] = t * scale;\n    }\n\n    // do right edge\n    for (int y = h - r; y < h; y++) {\n        t += id[(h - 1) * w];\n        t -= id[((y - r) * w) - w];\n        od[y * w] = t * scale;\n    }\n}\n\n__global__ void d_boxfilter_x_global(float *id, float *od, int w, int h, int r)\n{\n    unsigned int y = blockIdx.x * blockDim.x + threadIdx.x;\n    d_boxfilter_x(&id[y * w], &od[y * w], w, h, r);\n}\n\n__global__ void d_boxfilter_y_global(float *id, float *od, int w, int h, int r)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    d_boxfilter_y(&id[x], &od[x], w, h, r);\n}\n\n// texture version\n// texture fetches automatically clamp to edge of image\n__global__ void d_boxfilter_x_tex(float *od, int w, int h, int r, hipTextureObject_t tex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    float t = 0.0f;\n\n    for (int x = -r; x <= r; x++) {\n        t += tex2D<float>(tex, x, y);\n    }\n\n    od[y * w] = t * scale;\n\n    for (int x = 1; x < w; x++) {\n        t += tex2D<float>(tex, x + r, y);\n        t -= tex2D<float>(tex, x - r - 1, y);\n        od[y * w + x] = t * scale;\n    }\n}\n\n__global__ void d_boxfilter_y_tex(float *od, int w, int h, int r, hipTextureObject_t tex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int x     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    float t = 0.0f;\n\n    for (int y = -r; y <= r; y++) {\n        t += tex2D<float>(tex, x, y);\n    }\n\n    od[x] = t * scale;\n\n    for (int y = 1; y < h; y++) {\n        t += tex2D<float>(tex, x, y + r);\n        t -= tex2D<float>(tex, x, y - r - 1);\n        od[y * w + x] = t * scale;\n    }\n}\n\n// RGBA version\n// reads from 32-bit unsigned int array holding 8-bit RGBA\n\n// convert floating point rgba color to 32-bit integer\n__device__ unsigned int rgbaFloatToInt(float4 rgba)\n{\n    rgba.x = __saturatef(rgba.x); // clamp to [0.0, 1.0]\n    rgba.y = __saturatef(rgba.y);\n    rgba.z = __saturatef(rgba.z);\n    rgba.w = __saturatef(rgba.w);\n    return ((unsigned int)(rgba.w * 255.0f) << 24) | ((unsigned int)(rgba.z * 255.0f) << 16)\n         | ((unsigned int)(rgba.y * 255.0f) << 8) | ((unsigned int)(rgba.x * 255.0f));\n}\n\n__device__ float4 rgbaIntToFloat(unsigned int c)\n{\n    float4 rgba;\n    rgba.x = (c & 0xff) * 0.003921568627f;         //  /255.0f;\n    rgba.y = ((c >> 8) & 0xff) * 0.003921568627f;  //  /255.0f;\n    rgba.z = ((c >> 16) & 0xff) * 0.003921568627f; //  /255.0f;\n    rgba.w = ((c >> 24) & 0xff) * 0.003921568627f; //  /255.0f;\n    return rgba;\n}\n\n// row pass using texture lookups\n__global__ void d_boxfilter_rgba_x(unsigned int *od, int w, int h, int r, hipTextureObject_t rgbaTex)\n{\n    float        scale = 1.0f / (float)((r << 1) + 1);\n    unsigned int y     = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // as long as address is always less than height, we do work\n    if (y < h) {\n        float4 t = make_float4(0.0f);\n\n        for (int x = -r; x <= r; x++) {\n            t += tex2D<float4>(rgbaTex, x, y);\n        }\n\n        od[y * w] = rgbaFloatToInt(t * scale);\n\n        for (int x = 1; x < w; x++) {\n            t += tex2D<float4>(rgbaTex, x + r, y);\n            t -= tex2D<float4>(rgbaTex, x - r - 1, y);\n            od[y * w + x] = rgbaFloatToInt(t * scale);\n        }\n    }\n}\n\n// column pass using coalesced global memory reads\n__global__ void d_boxfilter_rgba_y(unsigned int *id, unsigned int *od, int w, int h, int r)\n{\n    unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;\n    id             = &id[x];\n    od             = &od[x];\n\n    float scale = 1.0f / (float)((r << 1) + 1);\n\n    float4 t;\n    // do left edge\n    t = rgbaIntToFloat(id[0]) * r;\n\n    for (int y = 0; y < (r + 1); y++) {\n        t += rgbaIntToFloat(id[y * w]);\n    }\n\n    od[0] = rgbaFloatToInt(t * scale);\n\n    for (int y = 1; y < (r + 1); y++) {\n        t += rgbaIntToFloat(id[(y + r) * w]);\n        t -= rgbaIntToFloat(id[0]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n\n    // main loop\n    for (int y = (r + 1); y < (h - r); y++) {\n        t += rgbaIntToFloat(id[(y + r) * w]);\n        t -= rgbaIntToFloat(id[((y - r) * w) - w]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n\n    // do right edge\n    for (int y = h - r; y < h; y++) {\n        t += rgbaIntToFloat(id[(h - 1) * w]);\n        t -= rgbaIntToFloat(id[((y - r) * w) - w]);\n        od[y * w] = rgbaFloatToInt(t * scale);\n    }\n}\n\nextern \"C\" void initTexture(int width, int height, void *pImage, bool useRGBA)\n{\n    // copy image data to array\n    hipChannelFormatDesc channelDesc;\n    if (useRGBA) {\n        channelDesc = hipCreateChannelDesc(8, 8, 8, 8, hipChannelFormatKindUnsigned);\n    }\n    else {\n        channelDesc = hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);\n    }\n    checkCudaErrors(hipMallocArray(&d_array, &channelDesc, width, height));\n\n    size_t bytesPerElem = (useRGBA ? sizeof(uchar4) : sizeof(float));\n    checkCudaErrors(hipMemcpy2DToArray(\n        d_array, 0, 0, pImage, width * bytesPerElem, width * bytesPerElem, height, hipMemcpyHostToDevice));\n\n    checkCudaErrors(hipMallocArray(&d_tempArray, &channelDesc, width, height));\n\n    // set texture parameters\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&rgbaTex, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_tempArray;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = false;\n    texDescr.filterMode       = hipFilterModeLinear;\n    texDescr.addressMode[0]   = hipAddressModeClamp;\n    texDescr.addressMode[1]   = hipAddressModeClamp;\n    texDescr.readMode         = hipReadModeNormalizedFloat;\n\n    checkCudaErrors(hipCreateTextureObject(&rgbaTexTempArray, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_array;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = d_tempArray;\n\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    texDescr.normalizedCoords = true;\n    texDescr.filterMode       = hipFilterModePoint;\n    texDescr.addressMode[0]   = hipAddressModeWrap;\n    texDescr.addressMode[1]   = hipAddressModeWrap;\n    texDescr.readMode         = hipReadModeElementType;\n\n    checkCudaErrors(hipCreateTextureObject(&texTempArray, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void freeTextures()\n{\n    checkCudaErrors(hipDestroyTextureObject(tex));\n    checkCudaErrors(hipDestroyTextureObject(texTempArray));\n    checkCudaErrors(hipDestroyTextureObject(rgbaTex));\n    checkCudaErrors(hipDestroyTextureObject(rgbaTexTempArray));\n    checkCudaErrors(hipFreeArray(d_array));\n    checkCudaErrors(hipFreeArray(d_tempArray));\n}\n\n/*\n    Perform 2D box filter on image using CUDA\n\n    Parameters:\n    d_temp - pointer to temporary storage in device memory\n    d_dest - pointer to destination image in device memory\n    width  - image width\n    height - image height\n    radius - filter radius\n    iterations - number of iterations\n\n*/\nextern \"C\" double boxFilter(float              *d_temp,\n                            float              *d_dest,\n                            int                 width,\n                            int                 height,\n                            int                 radius,\n                            int                 iterations,\n                            int                 nthreads,\n                            StopWatchInterface *timer)\n{\n    // var for kernel timing\n    double dKernelTime = 0.0;\n\n    // sync host and start computation timer_kernel\n    checkCudaErrors(hipDeviceSynchronize());\n\n    for (int i = 0; i < iterations; i++) {\n        sdkResetTimer(&timer);\n        // use texture for horizontal pass\n        if (iterations > 1) {\n            d_boxfilter_x_tex<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, texTempArray);\n        }\n        else {\n            d_boxfilter_x_tex<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, tex);\n        }\n\n        d_boxfilter_y_global<<<width / nthreads, nthreads, 0>>>(d_temp, d_dest, width, height, radius);\n\n        // sync host and stop computation timer_kernel\n        checkCudaErrors(hipDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(hipMemcpy2DToArray(d_tempArray,\n                                                0,\n                                                0,\n                                                d_dest,\n                                                width * sizeof(float),\n                                                width * sizeof(float),\n                                                height,\n                                                hipMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n\n// RGBA version\nextern \"C\" double boxFilterRGBA(unsigned int       *d_temp,\n                                unsigned int       *d_dest,\n                                int                 width,\n                                int                 height,\n                                int                 radius,\n                                int                 iterations,\n                                int                 nthreads,\n                                StopWatchInterface *timer)\n{\n    // var for kernel computation timing\n    double dKernelTime;\n\n    for (int i = 0; i < iterations; i++) {\n        // sync host and start kernel computation timer_kernel\n        dKernelTime = 0.0;\n        checkCudaErrors(hipDeviceSynchronize());\n        sdkResetTimer(&timer);\n\n        // use texture for horizontal pass\n        if (iterations > 1) {\n            d_boxfilter_rgba_x<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, rgbaTexTempArray);\n        }\n        else {\n            d_boxfilter_rgba_x<<<height / nthreads, nthreads, 0>>>(d_temp, width, height, radius, rgbaTex);\n        }\n\n        d_boxfilter_rgba_y<<<width / nthreads, nthreads, 0>>>(d_temp, d_dest, width, height, radius);\n\n        // sync host and stop computation timer_kernel\n        checkCudaErrors(hipDeviceSynchronize());\n        dKernelTime += sdkGetTimerValue(&timer);\n\n        if (iterations > 1) {\n            // copy result back from global memory to array\n            checkCudaErrors(hipMemcpy2DToArray(d_tempArray,\n                                                0,\n                                                0,\n                                                d_dest,\n                                                width * sizeof(unsigned int),\n                                                width * sizeof(unsigned int),\n                                                height,\n                                                hipMemcpyDeviceToDevice));\n        }\n    }\n\n    return ((dKernelTime / 1000.) / (double)iterations);\n}\n\n#endif // #ifndef _BOXFILTER_KERNEL_H_\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/boxFilter/boxFilter_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nextern \"C\" __global__ void sequence_gpu(int *d_ptr, int length)\n{\n    int elemID = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (elemID < length) {\n        unsigned int laneid;\n\n        // This command gets the lane ID within the current warp\n        asm(\"mov.u32 %0, %%laneid;\" : \"=r\"(laneid));\n\n        d_ptr[elemID] = laneid;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void sequence_gpu(int *d_ptr, int length)\n{\n    int elemID = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (elemID < length) {\n        unsigned int laneid;\n\n        // This command gets the lane ID within the current warp\n        asm(\"mov.u32 %0, %%laneid;\" : \"=r\"(laneid));\n\n        d_ptr[elemID] = laneid;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/inlinePTX_nvrtc/inlinePTX_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Based on http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm\n\n#include <assert.h>\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n#include \"sortingNetworks_common.cuh\"\n#include \"sortingNetworks_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Monolithic bitonic sort kernel for short arrays fitting into shared memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nbitonicSortShared(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more short vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < arrayLength; size <<= 1) {\n        // Bitonic merge\n        uint ddd = dir ^ ((threadIdx.x & (size / 2)) != 0);\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    // ddd == dir for the last bitonic merge step\n    {\n        for (uint stride = arrayLength / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Bitonic sort kernel for large arrays (not fitting into shared memory)\n////////////////////////////////////////////////////////////////////////////////\n// Bottom-level bitonic sort\n// Almost the same as bitonicSortShared with the exception of\n// even / odd subarrays being sorted in opposite directions\n// Bitonic merge accepts both\n// Ascending | descending or descending | ascending sorted pairs\n__global__ void bitonicSortShared1(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for current subarray\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subarray and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < SHARED_SIZE_LIMIT; size <<= 1) {\n        // Bitonic merge\n        uint ddd = (threadIdx.x & (size / 2)) != 0;\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    // Odd / even arrays of SHARED_SIZE_LIMIT elements\n    // sorted in opposite directions\n    uint ddd = blockIdx.x & 1;\n    {\n        for (uint stride = SHARED_SIZE_LIMIT / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n// Bitonic merge iteration for stride >= SHARED_SIZE_LIMIT\n__global__ void bitonicMergeGlobal(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  stride,\n                                   uint  dir)\n{\n    uint global_comparatorI = blockIdx.x * blockDim.x + threadIdx.x;\n    uint comparatorI        = global_comparatorI & (arrayLength / 2 - 1);\n\n    // Bitonic merge\n    uint ddd = dir ^ ((comparatorI & (size / 2)) != 0);\n    uint pos = 2 * global_comparatorI - (global_comparatorI & (stride - 1));\n\n    uint keyA = d_SrcKey[pos + 0];\n    uint valA = d_SrcVal[pos + 0];\n    uint keyB = d_SrcKey[pos + stride];\n    uint valB = d_SrcVal[pos + stride];\n\n    Comparator(keyA, valA, keyB, valB, ddd);\n\n    d_DstKey[pos + 0]      = keyA;\n    d_DstVal[pos + 0]      = valA;\n    d_DstKey[pos + stride] = keyB;\n    d_DstVal[pos + stride] = valB;\n}\n\n// Combined bitonic merge steps for\n// size > SHARED_SIZE_LIMIT and stride = [1 .. SHARED_SIZE_LIMIT / 2]\n__global__ void bitonicMergeShared(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for current subarray\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    // Bitonic merge\n    uint comparatorI = UMAD(blockIdx.x, blockDim.x, threadIdx.x) & ((arrayLength / 2) - 1);\n    uint ddd         = dir ^ ((comparatorI & (size / 2)) != 0);\n\n    for (uint stride = SHARED_SIZE_LIMIT / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n        uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n        Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Helper function (also used by odd-even merge sort)\nextern \"C\" uint factorRadix2(uint *log2L, uint L)\n{\n    if (!L) {\n        *log2L = 0;\n        return 0;\n    }\n    else {\n        for (*log2L = 0; (L & 1) == 0; L >>= 1, *log2L++)\n            ;\n\n        return L;\n    }\n}\n\nextern \"C\" uint\nbitonicSort(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint batchSize, uint arrayLength, uint dir)\n{\n    // Nothing to sort\n    if (arrayLength < 2)\n        return 0;\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    dir = (dir != 0);\n\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (arrayLength <= SHARED_SIZE_LIMIT) {\n        assert((batchSize * arrayLength) % SHARED_SIZE_LIMIT == 0);\n        bitonicSortShared<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, dir);\n    }\n    else {\n        bitonicSortShared1<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal);\n\n        for (uint size = 2 * SHARED_SIZE_LIMIT; size <= arrayLength; size <<= 1)\n            for (unsigned stride = size / 2; stride > 0; stride >>= 1)\n                if (stride >= SHARED_SIZE_LIMIT) {\n                    bitonicMergeGlobal<<<(batchSize * arrayLength) / 512, 256>>>(\n                        d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, stride, dir);\n                }\n                else {\n                    bitonicMergeShared<<<blockCount, threadCount>>>(\n                        d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, dir);\n                    break;\n                }\n    }\n\n    return threadCount;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Based on http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n#include \"sortingNetworks_common.cuh\"\n#include \"sortingNetworks_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Monolithic bitonic sort kernel for short arrays fitting into shared memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\nbitonicSortShared(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more short vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < arrayLength; size <<= 1) {\n        // Bitonic merge\n        uint ddd = dir ^ ((threadIdx.x & (size / 2)) != 0);\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    // ddd == dir for the last bitonic merge step\n    {\n        for (uint stride = arrayLength / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Bitonic sort kernel for large arrays (not fitting into shared memory)\n////////////////////////////////////////////////////////////////////////////////\n// Bottom-level bitonic sort\n// Almost the same as bitonicSortShared with the exception of\n// even / odd subarrays being sorted in opposite directions\n// Bitonic merge accepts both\n// Ascending | descending or descending | ascending sorted pairs\n__global__ void bitonicSortShared1(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for current subarray\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subarray and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size < SHARED_SIZE_LIMIT; size <<= 1) {\n        // Bitonic merge\n        uint ddd = (threadIdx.x & (size / 2)) != 0;\n\n        for (uint stride = size / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    // Odd / even arrays of SHARED_SIZE_LIMIT elements\n    // sorted in opposite directions\n    uint ddd = blockIdx.x & 1;\n    {\n        for (uint stride = SHARED_SIZE_LIMIT / 2; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n// Bitonic merge iteration for stride >= SHARED_SIZE_LIMIT\n__global__ void bitonicMergeGlobal(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  stride,\n                                   uint  dir)\n{\n    uint global_comparatorI = blockIdx.x * blockDim.x + threadIdx.x;\n    uint comparatorI        = global_comparatorI & (arrayLength / 2 - 1);\n\n    // Bitonic merge\n    uint ddd = dir ^ ((comparatorI & (size / 2)) != 0);\n    uint pos = 2 * global_comparatorI - (global_comparatorI & (stride - 1));\n\n    uint keyA = d_SrcKey[pos + 0];\n    uint valA = d_SrcVal[pos + 0];\n    uint keyB = d_SrcKey[pos + stride];\n    uint valB = d_SrcVal[pos + stride];\n\n    Comparator(keyA, valA, keyB, valB, ddd);\n\n    d_DstKey[pos + 0]      = keyA;\n    d_DstVal[pos + 0]      = valA;\n    d_DstKey[pos + stride] = keyB;\n    d_DstVal[pos + stride] = valB;\n}\n\n// Combined bitonic merge steps for\n// size > SHARED_SIZE_LIMIT and stride = [1 .. SHARED_SIZE_LIMIT / 2]\n__global__ void bitonicMergeShared(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for current subarray\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    // Bitonic merge\n    uint comparatorI = UMAD(blockIdx.x, blockDim.x, threadIdx.x) & ((arrayLength / 2) - 1);\n    uint ddd         = dir ^ ((comparatorI & (size / 2)) != 0);\n\n    for (uint stride = SHARED_SIZE_LIMIT / 2; stride > 0; stride >>= 1) {\n        cg::sync(cta);\n        uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n        Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], ddd);\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Helper function (also used by odd-even merge sort)\nextern \"C\" uint factorRadix2(uint *log2L, uint L)\n{\n    if (!L) {\n        *log2L = 0;\n        return 0;\n    }\n    else {\n        for (*log2L = 0; (L & 1) == 0; L >>= 1, *log2L++)\n            ;\n\n        return L;\n    }\n}\n\nextern \"C\" uint\nbitonicSort(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint batchSize, uint arrayLength, uint dir)\n{\n    // Nothing to sort\n    if (arrayLength < 2)\n        return 0;\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    dir = (dir != 0);\n\n    uint blockCount  = batchSize * arrayLength / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (arrayLength <= SHARED_SIZE_LIMIT) {\n        assert((batchSize * arrayLength) % SHARED_SIZE_LIMIT == 0);\n        bitonicSortShared<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, dir);\n    }\n    else {\n        bitonicSortShared1<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal);\n\n        for (uint size = 2 * SHARED_SIZE_LIMIT; size <= arrayLength; size <<= 1)\n            for (unsigned stride = size / 2; stride > 0; stride >>= 1)\n                if (stride >= SHARED_SIZE_LIMIT) {\n                    bitonicMergeGlobal<<<(batchSize * arrayLength) / 512, 256>>>(\n                        d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, stride, dir);\n                }\n                else {\n                    bitonicMergeShared<<<blockCount, threadCount>>>(\n                        d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, dir);\n                    break;\n                }\n    }\n\n    return threadCount;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/sortingNetworks/bitonicSort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Based on http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm\n\n\n#include <assert.h>\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n\n#include \"sortingNetworks_common.cuh\"\n#include \"sortingNetworks_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Monolithic Bacther's sort kernel for short arrays fitting into shared memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\noddEvenMergeSortShared(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more small vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size <= arrayLength; size <<= 1) {\n        uint stride = size / 2;\n        uint offset = threadIdx.x & (stride - 1);\n\n        {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n            stride >>= 1;\n        }\n\n        for (; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n\n            if (offset >= stride)\n                Comparator(s_key[pos - stride], s_val[pos - stride], s_key[pos + 0], s_val[pos + 0], dir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Odd-even merge sort iteration kernel\n// for large arrays (not fitting into shared memory)\n////////////////////////////////////////////////////////////////////////////////\n__global__ void oddEvenMergeGlobal(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  stride,\n                                   uint  dir)\n{\n    uint global_comparatorI = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Odd-even merge\n    uint pos = 2 * global_comparatorI - (global_comparatorI & (stride - 1));\n\n    if (stride < size / 2) {\n        uint offset = global_comparatorI & ((size / 2) - 1);\n\n        if (offset >= stride) {\n            uint keyA = d_SrcKey[pos - stride];\n            uint valA = d_SrcVal[pos - stride];\n            uint keyB = d_SrcKey[pos + 0];\n            uint valB = d_SrcVal[pos + 0];\n\n            Comparator(keyA, valA, keyB, valB, dir);\n\n            d_DstKey[pos - stride] = keyA;\n            d_DstVal[pos - stride] = valA;\n            d_DstKey[pos + 0]      = keyB;\n            d_DstVal[pos + 0]      = valB;\n        }\n    }\n    else {\n        uint keyA = d_SrcKey[pos + 0];\n        uint valA = d_SrcVal[pos + 0];\n        uint keyB = d_SrcKey[pos + stride];\n        uint valB = d_SrcVal[pos + stride];\n\n        Comparator(keyA, valA, keyB, valB, dir);\n\n        d_DstKey[pos + 0]      = keyA;\n        d_DstVal[pos + 0]      = valA;\n        d_DstKey[pos + stride] = keyB;\n        d_DstVal[pos + stride] = valB;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Helper function\nextern \"C\" uint factorRadix2(uint *log2L, uint L);\n\nextern \"C\" void oddEvenMergeSort(uint *d_DstKey,\n                                 uint *d_DstVal,\n                                 uint *d_SrcKey,\n                                 uint *d_SrcVal,\n                                 uint  batchSize,\n                                 uint  arrayLength,\n                                 uint  dir)\n{\n    // Nothing to sort\n    if (arrayLength < 2)\n        return;\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    dir = (dir != 0);\n\n    uint blockCount  = (batchSize * arrayLength) / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (arrayLength <= SHARED_SIZE_LIMIT) {\n        assert(SHARED_SIZE_LIMIT % arrayLength == 0);\n        oddEvenMergeSortShared<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, dir);\n    }\n    else {\n        oddEvenMergeSortShared<<<blockCount, threadCount>>>(\n            d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, SHARED_SIZE_LIMIT, dir);\n\n        for (uint size = 2 * SHARED_SIZE_LIMIT; size <= arrayLength; size <<= 1)\n            for (unsigned stride = size / 2; stride > 0; stride >>= 1) {\n                // Unlike with bitonic sort, combining bitonic merge steps with\n                // stride = [SHARED_SIZE_LIMIT / 2 .. 1] seems to be impossible as there\n                // are dependencies between data elements crossing the SHARED_SIZE_LIMIT\n                // borders\n                oddEvenMergeGlobal<<<(batchSize * arrayLength) / 512, 256>>>(\n                    d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, stride, dir);\n            }\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Based on http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm\n\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n\n#include \"sortingNetworks_common.cuh\"\n#include \"sortingNetworks_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Monolithic Bacther's sort kernel for short arrays fitting into shared memory\n////////////////////////////////////////////////////////////////////////////////\n__global__ void\noddEvenMergeSortShared(uint *d_DstKey, uint *d_DstVal, uint *d_SrcKey, uint *d_SrcVal, uint arrayLength, uint dir)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Shared memory storage for one or more small vectors\n    __shared__ uint s_key[SHARED_SIZE_LIMIT];\n    __shared__ uint s_val[SHARED_SIZE_LIMIT];\n\n    // Offset to the beginning of subbatch and load data\n    d_SrcKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_SrcVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstKey += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    d_DstVal += blockIdx.x * SHARED_SIZE_LIMIT + threadIdx.x;\n    s_key[threadIdx.x + 0]                       = d_SrcKey[0];\n    s_val[threadIdx.x + 0]                       = d_SrcVal[0];\n    s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcKey[(SHARED_SIZE_LIMIT / 2)];\n    s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)] = d_SrcVal[(SHARED_SIZE_LIMIT / 2)];\n\n    for (uint size = 2; size <= arrayLength; size <<= 1) {\n        uint stride = size / 2;\n        uint offset = threadIdx.x & (stride - 1);\n\n        {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n            Comparator(s_key[pos + 0], s_val[pos + 0], s_key[pos + stride], s_val[pos + stride], dir);\n            stride >>= 1;\n        }\n\n        for (; stride > 0; stride >>= 1) {\n            cg::sync(cta);\n            uint pos = 2 * threadIdx.x - (threadIdx.x & (stride - 1));\n\n            if (offset >= stride)\n                Comparator(s_key[pos - stride], s_val[pos - stride], s_key[pos + 0], s_val[pos + 0], dir);\n        }\n    }\n\n    cg::sync(cta);\n    d_DstKey[0]                       = s_key[threadIdx.x + 0];\n    d_DstVal[0]                       = s_val[threadIdx.x + 0];\n    d_DstKey[(SHARED_SIZE_LIMIT / 2)] = s_key[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n    d_DstVal[(SHARED_SIZE_LIMIT / 2)] = s_val[threadIdx.x + (SHARED_SIZE_LIMIT / 2)];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Odd-even merge sort iteration kernel\n// for large arrays (not fitting into shared memory)\n////////////////////////////////////////////////////////////////////////////////\n__global__ void oddEvenMergeGlobal(uint *d_DstKey,\n                                   uint *d_DstVal,\n                                   uint *d_SrcKey,\n                                   uint *d_SrcVal,\n                                   uint  arrayLength,\n                                   uint  size,\n                                   uint  stride,\n                                   uint  dir)\n{\n    uint global_comparatorI = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Odd-even merge\n    uint pos = 2 * global_comparatorI - (global_comparatorI & (stride - 1));\n\n    if (stride < size / 2) {\n        uint offset = global_comparatorI & ((size / 2) - 1);\n\n        if (offset >= stride) {\n            uint keyA = d_SrcKey[pos - stride];\n            uint valA = d_SrcVal[pos - stride];\n            uint keyB = d_SrcKey[pos + 0];\n            uint valB = d_SrcVal[pos + 0];\n\n            Comparator(keyA, valA, keyB, valB, dir);\n\n            d_DstKey[pos - stride] = keyA;\n            d_DstVal[pos - stride] = valA;\n            d_DstKey[pos + 0]      = keyB;\n            d_DstVal[pos + 0]      = valB;\n        }\n    }\n    else {\n        uint keyA = d_SrcKey[pos + 0];\n        uint valA = d_SrcVal[pos + 0];\n        uint keyB = d_SrcKey[pos + stride];\n        uint valB = d_SrcVal[pos + stride];\n\n        Comparator(keyA, valA, keyB, valB, dir);\n\n        d_DstKey[pos + 0]      = keyA;\n        d_DstVal[pos + 0]      = valA;\n        d_DstKey[pos + stride] = keyB;\n        d_DstVal[pos + stride] = valB;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Helper function\nextern \"C\" uint factorRadix2(uint *log2L, uint L);\n\nextern \"C\" void oddEvenMergeSort(uint *d_DstKey,\n                                 uint *d_DstVal,\n                                 uint *d_SrcKey,\n                                 uint *d_SrcVal,\n                                 uint  batchSize,\n                                 uint  arrayLength,\n                                 uint  dir)\n{\n    // Nothing to sort\n    if (arrayLength < 2)\n        return;\n\n    // Only power-of-two array lengths are supported by this implementation\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(&log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    dir = (dir != 0);\n\n    uint blockCount  = (batchSize * arrayLength) / SHARED_SIZE_LIMIT;\n    uint threadCount = SHARED_SIZE_LIMIT / 2;\n\n    if (arrayLength <= SHARED_SIZE_LIMIT) {\n        assert(SHARED_SIZE_LIMIT % arrayLength == 0);\n        oddEvenMergeSortShared<<<blockCount, threadCount>>>(d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, arrayLength, dir);\n    }\n    else {\n        oddEvenMergeSortShared<<<blockCount, threadCount>>>(\n            d_DstKey, d_DstVal, d_SrcKey, d_SrcVal, SHARED_SIZE_LIMIT, dir);\n\n        for (uint size = 2 * SHARED_SIZE_LIMIT; size <= arrayLength; size <<= 1)\n            for (unsigned stride = size / 2; stride > 0; stride >>= 1) {\n                // Unlike with bitonic sort, combining bitonic merge steps with\n                // stride = [SHARED_SIZE_LIMIT / 2 .. 1] seems to be impossible as there\n                // are dependencies between data elements crossing the SHARED_SIZE_LIMIT\n                // borders\n                oddEvenMergeGlobal<<<(batchSize * arrayLength) / 512, 256>>>(\n                    d_DstKey, d_DstVal, d_DstKey, d_DstVal, arrayLength, size, stride, dir);\n            }\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/sortingNetworks/oddEvenMergeSort.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <assert.h>\n#include <cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <helper_cuda.h>\n\n#include \"scan_common.h\"\n\n// All three kernels run 512 threads per workgroup\n// Must be a power of two\n#define THREADBLOCK_SIZE 256\n\n////////////////////////////////////////////////////////////////////////////////\n// Basic scan codelets\n////////////////////////////////////////////////////////////////////////////////\n// Naive inclusive scan: O(N * log2(N)) operations\n// Allocate 2 * 'size' local memory, initialize the first half\n// with 'size' zeros avoiding if(pos >= offset) condition evaluation\n// and saving instructions\ninline __device__ uint scan1Inclusive(uint idata, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    uint pos    = 2 * threadIdx.x - (threadIdx.x & (size - 1));\n    s_Data[pos] = 0;\n    pos += size;\n    s_Data[pos] = idata;\n\n    for (uint offset = 1; offset < size; offset <<= 1) {\n        cg::sync(cta);\n        uint t = s_Data[pos] + s_Data[pos - offset];\n        cg::sync(cta);\n        s_Data[pos] = t;\n    }\n\n    return s_Data[pos];\n}\n\ninline __device__ uint scan1Exclusive(uint idata, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    return scan1Inclusive(idata, s_Data, size, cta) - idata;\n}\n\ninline __device__ uint4 scan4Inclusive(uint4 idata4, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    // Level-0 inclusive scan\n    idata4.y += idata4.x;\n    idata4.z += idata4.y;\n    idata4.w += idata4.z;\n\n    // Level-1 exclusive scan\n    uint oval = scan1Exclusive(idata4.w, s_Data, size / 4, cta);\n\n    idata4.x += oval;\n    idata4.y += oval;\n    idata4.z += oval;\n    idata4.w += oval;\n\n    return idata4;\n}\n\n// Exclusive vector scan: the array to be scanned is stored\n// in local thread memory scope as uint4\ninline __device__ uint4 scan4Exclusive(uint4 idata4, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    uint4 odata4 = scan4Inclusive(idata4, s_Data, size, cta);\n    odata4.x -= idata4.x;\n    odata4.y -= idata4.y;\n    odata4.z -= idata4.z;\n    odata4.w -= idata4.w;\n    return odata4;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Scan kernels\n////////////////////////////////////////////////////////////////////////////////\n__global__ void scanExclusiveShared(uint4 *d_Dst, uint4 *d_Src, uint size)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_Data[2 * THREADBLOCK_SIZE];\n\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Load data\n    uint4 idata4 = d_Src[pos];\n\n    // Calculate exclusive scan\n    uint4 odata4 = scan4Exclusive(idata4, s_Data, size, cta);\n\n    // Write back\n    d_Dst[pos] = odata4;\n}\n\n// Exclusive scan of top elements of bottom-level scans (4 * THREADBLOCK_SIZE)\n__global__ void scanExclusiveShared2(uint *d_Buf, uint *d_Dst, uint *d_Src, uint N, uint arrayLength)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_Data[2 * THREADBLOCK_SIZE];\n\n    // Skip loads and stores for inactive threads of last threadblock (pos >= N)\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Load top elements\n    // Convert results of bottom-level scan back to inclusive\n    uint idata = 0;\n\n    if (pos < N)\n        idata = d_Dst[(4 * THREADBLOCK_SIZE) - 1 + (4 * THREADBLOCK_SIZE) * pos]\n              + d_Src[(4 * THREADBLOCK_SIZE) - 1 + (4 * THREADBLOCK_SIZE) * pos];\n\n    // Compute\n    uint odata = scan1Exclusive(idata, s_Data, arrayLength, cta);\n\n    // Avoid out-of-bound access\n    if (pos < N) {\n        d_Buf[pos] = odata;\n    }\n}\n\n// Final step of large-array scan: combine basic inclusive scan with exclusive\n// scan of top elements of input arrays\n__global__ void uniformUpdate(uint4 *d_Data, uint *d_Buffer)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  buf;\n    uint             pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (threadIdx.x == 0) {\n        buf = d_Buffer[blockIdx.x];\n    }\n\n    cg::sync(cta);\n\n    uint4 data4 = d_Data[pos];\n    data4.x += buf;\n    data4.y += buf;\n    data4.z += buf;\n    data4.w += buf;\n    d_Data[pos] = data4;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Derived as 32768 (max power-of-two gridDim.x) * 4 * THREADBLOCK_SIZE\n// Due to scanExclusiveShared<<<>>>() 1D block addressing\nextern \"C\" const uint MAX_BATCH_ELEMENTS   = 64 * 1048576;\nextern \"C\" const uint MIN_SHORT_ARRAY_SIZE = 4;\nextern \"C\" const uint MAX_SHORT_ARRAY_SIZE = 4 * THREADBLOCK_SIZE;\nextern \"C\" const uint MIN_LARGE_ARRAY_SIZE = 8 * THREADBLOCK_SIZE;\nextern \"C\" const uint MAX_LARGE_ARRAY_SIZE = 4 * THREADBLOCK_SIZE * THREADBLOCK_SIZE;\n\n// Internal exclusive scan buffer\nstatic uint *d_Buf;\n\nextern \"C\" void initScan(void)\n{\n    checkCudaErrors(cudaMalloc((void **)&d_Buf, (MAX_BATCH_ELEMENTS / (4 * THREADBLOCK_SIZE)) * sizeof(uint)));\n}\n\nextern \"C\" void closeScan(void) { checkCudaErrors(cudaFree(d_Buf)); }\n\nstatic uint factorRadix2(uint &log2L, uint L)\n{\n    if (!L) {\n        log2L = 0;\n        return 0;\n    }\n    else {\n        for (log2L = 0; (L & 1) == 0; L >>= 1, log2L++)\n            ;\n\n        return L;\n    }\n}\n\nstatic uint iDivUp(uint dividend, uint divisor)\n{\n    return ((dividend % divisor) == 0) ? (dividend / divisor) : (dividend / divisor + 1);\n}\n\nextern \"C\" size_t scanExclusiveShort(uint *d_Dst, uint *d_Src, uint batchSize, uint arrayLength)\n{\n    // Check power-of-two factorization\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    // Check supported size range\n    assert((arrayLength >= MIN_SHORT_ARRAY_SIZE) && (arrayLength <= MAX_SHORT_ARRAY_SIZE));\n\n    // Check total batch size limit\n    assert((batchSize * arrayLength) <= MAX_BATCH_ELEMENTS);\n\n    // Check all threadblocks to be fully packed with data\n    assert((batchSize * arrayLength) % (4 * THREADBLOCK_SIZE) == 0);\n\n    scanExclusiveShared<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>(\n        (uint4 *)d_Dst, (uint4 *)d_Src, arrayLength);\n    getLastCudaError(\"scanExclusiveShared() execution FAILED\\n\");\n\n    return THREADBLOCK_SIZE;\n}\n\nextern \"C\" size_t scanExclusiveLarge(uint *d_Dst, uint *d_Src, uint batchSize, uint arrayLength)\n{\n    // Check power-of-two factorization\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    // Check supported size range\n    assert((arrayLength >= MIN_LARGE_ARRAY_SIZE) && (arrayLength <= MAX_LARGE_ARRAY_SIZE));\n\n    // Check total batch size limit\n    assert((batchSize * arrayLength) <= MAX_BATCH_ELEMENTS);\n\n    scanExclusiveShared<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>(\n        (uint4 *)d_Dst, (uint4 *)d_Src, 4 * THREADBLOCK_SIZE);\n    getLastCudaError(\"scanExclusiveShared() execution FAILED\\n\");\n\n    // Not all threadblocks need to be packed with input data:\n    // inactive threads of highest threadblock just don't do global reads and\n    // writes\n    const uint blockCount2 = iDivUp((batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE);\n    scanExclusiveShared2<<<blockCount2, THREADBLOCK_SIZE>>>((uint *)d_Buf,\n                                                            (uint *)d_Dst,\n                                                            (uint *)d_Src,\n                                                            (batchSize * arrayLength) / (4 * THREADBLOCK_SIZE),\n                                                            arrayLength / (4 * THREADBLOCK_SIZE));\n    getLastCudaError(\"scanExclusiveShared2() execution FAILED\\n\");\n\n    uniformUpdate<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>((uint4 *)d_Dst,\n                                                                                            (uint *)d_Buf);\n    getLastCudaError(\"uniformUpdate() execution FAILED\\n\");\n\n    return THREADBLOCK_SIZE;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n\nnamespace cg = cooperative_groups;\n#include <hip/hip_runtime_api.h>\n\n#include \"scan_common.h\"\n\n// All three kernels run 512 threads per workgroup\n// Must be a power of two\n#define THREADBLOCK_SIZE 256\n\n////////////////////////////////////////////////////////////////////////////////\n// Basic scan codelets\n////////////////////////////////////////////////////////////////////////////////\n// Naive inclusive scan: O(N * log2(N)) operations\n// Allocate 2 * 'size' local memory, initialize the first half\n// with 'size' zeros avoiding if(pos >= offset) condition evaluation\n// and saving instructions\ninline __device__ uint scan1Inclusive(uint idata, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    uint pos    = 2 * threadIdx.x - (threadIdx.x & (size - 1));\n    s_Data[pos] = 0;\n    pos += size;\n    s_Data[pos] = idata;\n\n    for (uint offset = 1; offset < size; offset <<= 1) {\n        cg::sync(cta);\n        uint t = s_Data[pos] + s_Data[pos - offset];\n        cg::sync(cta);\n        s_Data[pos] = t;\n    }\n\n    return s_Data[pos];\n}\n\ninline __device__ uint scan1Exclusive(uint idata, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    return scan1Inclusive(idata, s_Data, size, cta) - idata;\n}\n\ninline __device__ uint4 scan4Inclusive(uint4 idata4, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    // Level-0 inclusive scan\n    idata4.y += idata4.x;\n    idata4.z += idata4.y;\n    idata4.w += idata4.z;\n\n    // Level-1 exclusive scan\n    uint oval = scan1Exclusive(idata4.w, s_Data, size / 4, cta);\n\n    idata4.x += oval;\n    idata4.y += oval;\n    idata4.z += oval;\n    idata4.w += oval;\n\n    return idata4;\n}\n\n// Exclusive vector scan: the array to be scanned is stored\n// in local thread memory scope as uint4\ninline __device__ uint4 scan4Exclusive(uint4 idata4, volatile uint *s_Data, uint size, cg::thread_block cta)\n{\n    uint4 odata4 = scan4Inclusive(idata4, s_Data, size, cta);\n    odata4.x -= idata4.x;\n    odata4.y -= idata4.y;\n    odata4.z -= idata4.z;\n    odata4.w -= idata4.w;\n    return odata4;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Scan kernels\n////////////////////////////////////////////////////////////////////////////////\n__global__ void scanExclusiveShared(uint4 *d_Dst, uint4 *d_Src, uint size)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_Data[2 * THREADBLOCK_SIZE];\n\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Load data\n    uint4 idata4 = d_Src[pos];\n\n    // Calculate exclusive scan\n    uint4 odata4 = scan4Exclusive(idata4, s_Data, size, cta);\n\n    // Write back\n    d_Dst[pos] = odata4;\n}\n\n// Exclusive scan of top elements of bottom-level scans (4 * THREADBLOCK_SIZE)\n__global__ void scanExclusiveShared2(uint *d_Buf, uint *d_Dst, uint *d_Src, uint N, uint arrayLength)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  s_Data[2 * THREADBLOCK_SIZE];\n\n    // Skip loads and stores for inactive threads of last threadblock (pos >= N)\n    uint pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Load top elements\n    // Convert results of bottom-level scan back to inclusive\n    uint idata = 0;\n\n    if (pos < N)\n        idata = d_Dst[(4 * THREADBLOCK_SIZE) - 1 + (4 * THREADBLOCK_SIZE) * pos]\n              + d_Src[(4 * THREADBLOCK_SIZE) - 1 + (4 * THREADBLOCK_SIZE) * pos];\n\n    // Compute\n    uint odata = scan1Exclusive(idata, s_Data, arrayLength, cta);\n\n    // Avoid out-of-bound access\n    if (pos < N) {\n        d_Buf[pos] = odata;\n    }\n}\n\n// Final step of large-array scan: combine basic inclusive scan with exclusive\n// scan of top elements of input arrays\n__global__ void uniformUpdate(uint4 *d_Data, uint *d_Buffer)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ uint  buf;\n    uint             pos = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (threadIdx.x == 0) {\n        buf = d_Buffer[blockIdx.x];\n    }\n\n    cg::sync(cta);\n\n    uint4 data4 = d_Data[pos];\n    data4.x += buf;\n    data4.y += buf;\n    data4.z += buf;\n    data4.w += buf;\n    d_Data[pos] = data4;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Interface function\n////////////////////////////////////////////////////////////////////////////////\n// Derived as 32768 (max power-of-two gridDim.x) * 4 * THREADBLOCK_SIZE\n// Due to scanExclusiveShared<<<>>>() 1D block addressing\nextern \"C\" const uint MAX_BATCH_ELEMENTS   = 64 * 1048576;\nextern \"C\" const uint MIN_SHORT_ARRAY_SIZE = 4;\nextern \"C\" const uint MAX_SHORT_ARRAY_SIZE = 4 * THREADBLOCK_SIZE;\nextern \"C\" const uint MIN_LARGE_ARRAY_SIZE = 8 * THREADBLOCK_SIZE;\nextern \"C\" const uint MAX_LARGE_ARRAY_SIZE = 4 * THREADBLOCK_SIZE * THREADBLOCK_SIZE;\n\n// Internal exclusive scan buffer\nstatic uint *d_Buf;\n\nextern \"C\" void initScan(void)\n{\n    checkCudaErrors(hipMalloc((void **)&d_Buf, (MAX_BATCH_ELEMENTS / (4 * THREADBLOCK_SIZE)) * sizeof(uint)));\n}\n\nextern \"C\" void closeScan(void) { checkCudaErrors(hipFree(d_Buf)); }\n\nstatic uint factorRadix2(uint &log2L, uint L)\n{\n    if (!L) {\n        log2L = 0;\n        return 0;\n    }\n    else {\n        for (log2L = 0; (L & 1) == 0; L >>= 1, log2L++)\n            ;\n\n        return L;\n    }\n}\n\nstatic uint iDivUp(uint dividend, uint divisor)\n{\n    return ((dividend % divisor) == 0) ? (dividend / divisor) : (dividend / divisor + 1);\n}\n\nextern \"C\" size_t scanExclusiveShort(uint *d_Dst, uint *d_Src, uint batchSize, uint arrayLength)\n{\n    // Check power-of-two factorization\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    // Check supported size range\n    assert((arrayLength >= MIN_SHORT_ARRAY_SIZE) && (arrayLength <= MAX_SHORT_ARRAY_SIZE));\n\n    // Check total batch size limit\n    assert((batchSize * arrayLength) <= MAX_BATCH_ELEMENTS);\n\n    // Check all threadblocks to be fully packed with data\n    assert((batchSize * arrayLength) % (4 * THREADBLOCK_SIZE) == 0);\n\n    scanExclusiveShared<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>(\n        (uint4 *)d_Dst, (uint4 *)d_Src, arrayLength);\n    getLastCudaError(\"scanExclusiveShared() execution FAILED\\n\");\n\n    return THREADBLOCK_SIZE;\n}\n\nextern \"C\" size_t scanExclusiveLarge(uint *d_Dst, uint *d_Src, uint batchSize, uint arrayLength)\n{\n    // Check power-of-two factorization\n    uint log2L;\n    uint factorizationRemainder = factorRadix2(log2L, arrayLength);\n    assert(factorizationRemainder == 1);\n\n    // Check supported size range\n    assert((arrayLength >= MIN_LARGE_ARRAY_SIZE) && (arrayLength <= MAX_LARGE_ARRAY_SIZE));\n\n    // Check total batch size limit\n    assert((batchSize * arrayLength) <= MAX_BATCH_ELEMENTS);\n\n    scanExclusiveShared<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>(\n        (uint4 *)d_Dst, (uint4 *)d_Src, 4 * THREADBLOCK_SIZE);\n    getLastCudaError(\"scanExclusiveShared() execution FAILED\\n\");\n\n    // Not all threadblocks need to be packed with input data:\n    // inactive threads of highest threadblock just don't do global reads and\n    // writes\n    const uint blockCount2 = iDivUp((batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE);\n    scanExclusiveShared2<<<blockCount2, THREADBLOCK_SIZE>>>((uint *)d_Buf,\n                                                            (uint *)d_Dst,\n                                                            (uint *)d_Src,\n                                                            (batchSize * arrayLength) / (4 * THREADBLOCK_SIZE),\n                                                            arrayLength / (4 * THREADBLOCK_SIZE));\n    getLastCudaError(\"scanExclusiveShared2() execution FAILED\\n\");\n\n    uniformUpdate<<<(batchSize * arrayLength) / (4 * THREADBLOCK_SIZE), THREADBLOCK_SIZE>>>((uint4 *)d_Dst,\n                                                                                            (uint *)d_Buf);\n    getLastCudaError(\"uniformUpdate() execution FAILED\\n\");\n\n    return THREADBLOCK_SIZE;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/scan/scan.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <helper_cuda.h>\n\n#include \"FunctionPointers_kernels.h\"\n\n// Texture object for reading image\ncudaTextureObject_t             tex;\nextern __shared__ unsigned char LocalBlock[];\nstatic cudaArray               *array = NULL;\n\n#define RADIUS 1\n\n// pixel value used for thresholding function,\n// works well with sample image 'teapot512'\n#define THRESHOLD 150.0f\n\n#ifdef FIXED_BLOCKWIDTH\n#define BlockWidth  80\n#define SharedPitch 384\n#endif\n\n// A function pointer can be declared explicitly like this line:\n//__device__ unsigned char (*pointFunction)(unsigned char, float ) = NULL;\n// or by using typedef's like below:\n\ntypedef unsigned char (*blockFunction_t)(unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         float);\n\ntypedef unsigned char (*pointFunction_t)(unsigned char, float);\n\n__device__ blockFunction_t blockFunction;\n\n__device__ unsigned char ComputeSobel(unsigned char ul, // upper left\n                                      unsigned char um, // upper middle\n                                      unsigned char ur, // upper right\n                                      unsigned char ml, // middle left\n                                      unsigned char mm, // middle (unused)\n                                      unsigned char mr, // middle right\n                                      unsigned char ll, // lower left\n                                      unsigned char lm, // lower middle\n                                      unsigned char lr, // lower right\n                                      float         fScale)\n{\n    short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;\n    short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;\n    short Sum  = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));\n    return (unsigned char)((Sum < 0) ? 0 : ((Sum > 255) ? 255 : Sum));\n}\n\n// define a function pointer and initialize to NULL\n__device__ unsigned char (*varFunction)(unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        float x) = NULL;\n\n__device__ unsigned char ComputeBox(unsigned char ul, // upper left\n                                    unsigned char um, // upper middle\n                                    unsigned char ur, // upper right\n                                    unsigned char ml, // middle left\n                                    unsigned char mm, // middle...middle\n                                    unsigned char mr, // middle right\n                                    unsigned char ll, // lower left\n                                    unsigned char lm, // lower middle\n                                    unsigned char lr, // lower right\n                                    float         fscale)\n{\n    short Sum = (short)(ul + um + ur + ml + mm + mr + ll + lm + lr) / 9;\n    Sum *= fscale;\n    return (unsigned char)((Sum < 0) ? 0 : ((Sum > 255) ? 255 : Sum));\n}\n__device__ unsigned char Threshold(unsigned char in, float thresh)\n{\n    if (in > thresh) {\n        return 0xFF;\n    }\n    else {\n        return 0;\n    }\n}\n\n// Declare function tables, one for the point function chosen, one for the\n// block function chosen.  The number of entries is determined by the\n// enum in FunctionPointers_kernels.h\n__device__ blockFunction_t blockFunction_table[LAST_BLOCK_FILTER];\n__device__ pointFunction_t pointFunction_table[LAST_POINT_FILTER];\n\n// Declare device side function pointers.  We retrieve them later with\n// cudaMemcpyFromSymbol to set our function tables above in some\n// particular order specified at runtime.\n__device__ blockFunction_t pComputeSobel     = ComputeSobel;\n__device__ blockFunction_t pComputeBox       = ComputeBox;\n__device__ pointFunction_t pComputeThreshold = Threshold;\n\n// Allocate host side tables to mirror the device side, and later, we\n// fill these tables with the function pointers.  This lets us send\n// the pointers to the kernel on invocation, as a method of choosing\n// which function to run.\nblockFunction_t h_blockFunction_table[2];\npointFunction_t h_pointFunction_table[2];\n\n// Perform a filter operation on the data, using shared memory\n// The actual operation performed is\n// determined by the function pointer \"blockFunction\" and selected\n// by the integer argument \"blockOperation\" and has access\n// to an apron around the current pixel being processed.\n// Following the block operation, a per-pixel operation,\n// pointed to by pPointFunction is performed before the final\n// pixel is produced.\n__global__ void SobelShared(uchar4        *pSobelOriginal,\n                            unsigned short SobelPitch,\n#ifndef FIXED_BLOCKWIDTH\n                            short BlockWidth,\n                            short SharedPitch,\n#endif\n                            short               w,\n                            short               h,\n                            float               fScale,\n                            int                 blockOperation,\n                            pointFunction_t     pPointFunction,\n                            cudaTextureObject_t tex)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    short            u   = 4 * blockIdx.x * BlockWidth;\n    short            v   = blockIdx.y * blockDim.y + threadIdx.y;\n    short            ib;\n\n    int SharedIdx = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n        LocalBlock[SharedIdx + 4 * ib + 0] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 1] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 2] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 3] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));\n    }\n\n    if (threadIdx.y < RADIUS * 2) {\n        //\n        // copy trailing RADIUS*2 rows of pixels into shared\n        //\n        SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;\n\n        for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n            LocalBlock[SharedIdx + 4 * ib + 0] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 1] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 2] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 3] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v + blockDim.y - RADIUS));\n        }\n    }\n\n    cg::sync(cta);\n\n    u >>= 2; // index as uchar4 from here\n    uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);\n    SharedIdx      = threadIdx.y * SharedPitch;\n\n    blockFunction = blockFunction_table[blockOperation];\n\n    for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {\n        uchar4 out;\n\n        unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];\n        unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];\n        unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];\n        unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];\n        unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];\n        unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];\n        unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];\n        unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];\n        unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];\n\n        out.x = (*blockFunction)(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];\n        pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];\n        pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];\n        out.y = (*blockFunction)(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22, pix20, fScale);\n\n        pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];\n        pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];\n        pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];\n        out.z = (*blockFunction)(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20, pix21, fScale);\n\n        pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];\n        pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];\n        pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];\n        out.w = (*blockFunction)(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (pPointFunction != NULL) {\n            out.x = (*pPointFunction)(out.x, THRESHOLD);\n            out.y = (*pPointFunction)(out.y, THRESHOLD);\n            out.z = (*pPointFunction)(out.z, THRESHOLD);\n            out.w = (*pPointFunction)(out.w, THRESHOLD);\n        }\n\n        if (u + ib < w / 4 && v < h) {\n            pSobel[u + ib] = out;\n        }\n    }\n\n    cg::sync(cta);\n}\n\n__global__ void\nSobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fscale, cudaTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        pSobel[i] = min(max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale), 0.f), 255.f);\n    }\n}\n\n// Perform block and pointer filtering using texture lookups.\n// The block and point operations are determined by the\n// input argument (see comment above for \"SobelShared\" function)\n__global__ void SobelTex(Pixel              *pSobelOriginal,\n                         unsigned int        Pitch,\n                         int                 w,\n                         int                 h,\n                         float               fScale,\n                         int                 blockOperation,\n                         pointFunction_t     pPointOperation,\n                         cudaTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n    unsigned char  tmp    = 0;\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        unsigned char pix00 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);\n        unsigned char pix01 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);\n        unsigned char pix02 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);\n        unsigned char pix10 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);\n        unsigned char pix11 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);\n        unsigned char pix12 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);\n        unsigned char pix20 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);\n        unsigned char pix21 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);\n        unsigned char pix22 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);\n        tmp                 = (*(blockFunction_table[blockOperation]))(\n            pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (pPointOperation != NULL) {\n            tmp = (*pPointOperation)(tmp, 150.0);\n        }\n\n        pSobel[i] = tmp;\n    }\n}\n\nextern \"C\" void setupTexture(int iw, int ih, Pixel *data, int Bpp)\n{\n    cudaChannelFormatDesc desc;\n\n    if (Bpp == 1) {\n        desc = cudaCreateChannelDesc<unsigned char>();\n    }\n    else {\n        desc = cudaCreateChannelDesc<uchar4>();\n    }\n\n    checkCudaErrors(cudaMallocArray(&array, &desc, iw, ih));\n    checkCudaErrors(cudaMemcpy2DToArray(\n        array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih, cudaMemcpyHostToDevice));\n\n    cudaResourceDesc texRes;\n    memset(&texRes, 0, sizeof(cudaResourceDesc));\n\n    texRes.resType         = cudaResourceTypeArray;\n    texRes.res.array.array = array;\n\n    cudaTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(cudaTextureDesc));\n\n    checkCudaErrors(cudaCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void deleteTexture(void)\n{\n    checkCudaErrors(cudaFreeArray(array));\n    checkCudaErrors(cudaDestroyTextureObject(tex));\n}\n\n// Copy the pointers from the function tables to the host side\nvoid setupFunctionTables()\n{\n    // Dynamically assign the function table.\n    // Copy the function pointers to their appropriate locations according to the\n    // enum\n    checkCudaErrors(cudaMemcpyFromSymbol(&h_blockFunction_table[SOBEL_FILTER], pComputeSobel, sizeof(blockFunction_t)));\n    checkCudaErrors(cudaMemcpyFromSymbol(&h_blockFunction_table[BOX_FILTER], pComputeBox, sizeof(blockFunction_t)));\n\n    // do the same for the point function, where the 2nd function is NULL (\"no-op\"\n    // filter, skipped in kernel code)\n    checkCudaErrors(\n        cudaMemcpyFromSymbol(&h_pointFunction_table[THRESHOLD_FILTER], pComputeThreshold, sizeof(pointFunction_t)));\n    h_pointFunction_table[NULL_FILTER] = NULL;\n\n    // now copy the function tables back to the device, so if we wish we can use\n    // an index into the table to choose them\n    // We have now set the order in the function table according to our enum.\n    checkCudaErrors(\n        cudaMemcpyToSymbol(blockFunction_table, h_blockFunction_table, sizeof(blockFunction_t) * LAST_BLOCK_FILTER));\n    checkCudaErrors(\n        cudaMemcpyToSymbol(pointFunction_table, h_pointFunction_table, sizeof(pointFunction_t) * LAST_POINT_FILTER));\n}\n\n// Wrapper for the __global__ call that sets up the texture and threads\n// Below two methods for selecting the image processing function to run are\n// shown.\n// BlockOperation is an integer kernel argument used as an index into the\n// blockFunction_table on the device side\n// pPointOp is itself a function pointer passed as a kernel argument, retrieved\n// from a host side copy of the function table\nextern \"C\" void sobelFilter(Pixel                *odata,\n                            int                   iw,\n                            int                   ih,\n                            enum SobelDisplayMode mode,\n                            float                 fScale,\n                            int                   blockOperation,\n                            int                   pointOperation)\n{\n    pointFunction_t pPointOp = h_pointFunction_table[pointOperation];\n\n    switch (mode) {\n    case SOBELDISPLAY_IMAGE:\n        SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, tex);\n        break;\n\n    case SOBELDISPLAY_SOBELTEX:\n        SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, blockOperation, pPointOp, tex);\n        break;\n\n    case SOBELDISPLAY_SOBELSHARED: {\n        dim3 threads(16, 4);\n#ifndef FIXED_BLOCKWIDTH\n        int BlockWidth = 80; // must be divisible by 16 for coalescing\n#endif\n        dim3 blocks =\n            dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)), ih / threads.y + (0 != ih % threads.y));\n        int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);\n        int sharedMem   = SharedPitch * (threads.y + 2 * RADIUS);\n\n        // for the shared kernel, width must be divisible by 4\n        iw &= ~3;\n\n        SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata,\n                                                    iw,\n#ifndef FIXED_BLOCKWIDTH\n                                                    BlockWidth,\n                                                    SharedPitch,\n#endif\n                                                    iw,\n                                                    ih,\n                                                    fScale,\n                                                    blockOperation,\n                                                    pPointOp,\n                                                    tex);\n    } break;\n    }\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_cooperative_groups.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nnamespace cg = cooperative_groups;\n\n#include <hip/hip_runtime_api.h>\n\n#include \"FunctionPointers_kernels.h\"\n\n// Texture object for reading image\nhipTextureObject_t             tex;\nextern __shared__ unsigned char LocalBlock[];\nstatic hipArray               *array = NULL;\n\n#define RADIUS 1\n\n// pixel value used for thresholding function,\n// works well with sample image 'teapot512'\n#define THRESHOLD 150.0f\n\n#ifdef FIXED_BLOCKWIDTH\n#define BlockWidth  80\n#define SharedPitch 384\n#endif\n\n// A function pointer can be declared explicitly like this line:\n//__device__ unsigned char (*pointFunction)(unsigned char, float ) = NULL;\n// or by using typedef's like below:\n\ntypedef unsigned char (*blockFunction_t)(unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         unsigned char,\n                                         float);\n\ntypedef unsigned char (*pointFunction_t)(unsigned char, float);\n\n__device__ blockFunction_t blockFunction;\n\n__device__ unsigned char ComputeSobel(unsigned char ul, // upper left\n                                      unsigned char um, // upper middle\n                                      unsigned char ur, // upper right\n                                      unsigned char ml, // middle left\n                                      unsigned char mm, // middle (unused)\n                                      unsigned char mr, // middle right\n                                      unsigned char ll, // lower left\n                                      unsigned char lm, // lower middle\n                                      unsigned char lr, // lower right\n                                      float         fScale)\n{\n    short Horz = ur + 2 * mr + lr - ul - 2 * ml - ll;\n    short Vert = ul + 2 * um + ur - ll - 2 * lm - lr;\n    short Sum  = (short)(fScale * (abs((int)Horz) + abs((int)Vert)));\n    return (unsigned char)((Sum < 0) ? 0 : ((Sum > 255) ? 255 : Sum));\n}\n\n// define a function pointer and initialize to NULL\n__device__ unsigned char (*varFunction)(unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        unsigned char,\n                                        float x) = NULL;\n\n__device__ unsigned char ComputeBox(unsigned char ul, // upper left\n                                    unsigned char um, // upper middle\n                                    unsigned char ur, // upper right\n                                    unsigned char ml, // middle left\n                                    unsigned char mm, // middle...middle\n                                    unsigned char mr, // middle right\n                                    unsigned char ll, // lower left\n                                    unsigned char lm, // lower middle\n                                    unsigned char lr, // lower right\n                                    float         fscale)\n{\n    short Sum = (short)(ul + um + ur + ml + mm + mr + ll + lm + lr) / 9;\n    Sum *= fscale;\n    return (unsigned char)((Sum < 0) ? 0 : ((Sum > 255) ? 255 : Sum));\n}\n__device__ unsigned char Threshold(unsigned char in, float thresh)\n{\n    if (in > thresh) {\n        return 0xFF;\n    }\n    else {\n        return 0;\n    }\n}\n\n// Declare function tables, one for the point function chosen, one for the\n// block function chosen.  The number of entries is determined by the\n// enum in FunctionPointers_kernels.h\n__device__ blockFunction_t blockFunction_table[LAST_BLOCK_FILTER];\n__device__ pointFunction_t pointFunction_table[LAST_POINT_FILTER];\n\n// Declare device side function pointers.  We retrieve them later with\n// cudaMemcpyFromSymbol to set our function tables above in some\n// particular order specified at runtime.\n__device__ blockFunction_t pComputeSobel     = ComputeSobel;\n__device__ blockFunction_t pComputeBox       = ComputeBox;\n__device__ pointFunction_t pComputeThreshold = Threshold;\n\n// Allocate host side tables to mirror the device side, and later, we\n// fill these tables with the function pointers.  This lets us send\n// the pointers to the kernel on invocation, as a method of choosing\n// which function to run.\nblockFunction_t h_blockFunction_table[2];\npointFunction_t h_pointFunction_table[2];\n\n// Perform a filter operation on the data, using shared memory\n// The actual operation performed is\n// determined by the function pointer \"blockFunction\" and selected\n// by the integer argument \"blockOperation\" and has access\n// to an apron around the current pixel being processed.\n// Following the block operation, a per-pixel operation,\n// pointed to by pPointFunction is performed before the final\n// pixel is produced.\n__global__ void SobelShared(uchar4        *pSobelOriginal,\n                            unsigned short SobelPitch,\n#ifndef FIXED_BLOCKWIDTH\n                            short BlockWidth,\n                            short SharedPitch,\n#endif\n                            short               w,\n                            short               h,\n                            float               fScale,\n                            int                 blockOperation,\n                            pointFunction_t     pPointFunction,\n                            hipTextureObject_t tex)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    short            u   = 4 * blockIdx.x * BlockWidth;\n    short            v   = blockIdx.y * blockDim.y + threadIdx.y;\n    short            ib;\n\n    int SharedIdx = threadIdx.y * SharedPitch;\n\n    for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n        LocalBlock[SharedIdx + 4 * ib + 0] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 1] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 2] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v - RADIUS));\n        LocalBlock[SharedIdx + 4 * ib + 3] =\n            tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v - RADIUS));\n    }\n\n    if (threadIdx.y < RADIUS * 2) {\n        //\n        // copy trailing RADIUS*2 rows of pixels into shared\n        //\n        SharedIdx = (blockDim.y + threadIdx.y) * SharedPitch;\n\n        for (ib = threadIdx.x; ib < BlockWidth + 2 * RADIUS; ib += blockDim.x) {\n            LocalBlock[SharedIdx + 4 * ib + 0] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 0), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 1] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 1), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 2] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 2), (float)(v + blockDim.y - RADIUS));\n            LocalBlock[SharedIdx + 4 * ib + 3] =\n                tex2D<unsigned char>(tex, (float)(u + 4 * ib - RADIUS + 3), (float)(v + blockDim.y - RADIUS));\n        }\n    }\n\n    cg::sync(cta);\n\n    u >>= 2; // index as uchar4 from here\n    uchar4 *pSobel = (uchar4 *)(((char *)pSobelOriginal) + v * SobelPitch);\n    SharedIdx      = threadIdx.y * SharedPitch;\n\n    blockFunction = blockFunction_table[blockOperation];\n\n    for (ib = threadIdx.x; ib < BlockWidth; ib += blockDim.x) {\n        uchar4 out;\n\n        unsigned char pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 0];\n        unsigned char pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 1];\n        unsigned char pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 2];\n        unsigned char pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 0];\n        unsigned char pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 1];\n        unsigned char pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 2];\n        unsigned char pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 0];\n        unsigned char pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 1];\n        unsigned char pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 2];\n\n        out.x = (*blockFunction)(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        pix00 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 3];\n        pix10 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 3];\n        pix20 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 3];\n        out.y = (*blockFunction)(pix01, pix02, pix00, pix11, pix12, pix10, pix21, pix22, pix20, fScale);\n\n        pix01 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 4];\n        pix11 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 4];\n        pix21 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 4];\n        out.z = (*blockFunction)(pix02, pix00, pix01, pix12, pix10, pix11, pix22, pix20, pix21, fScale);\n\n        pix02 = LocalBlock[SharedIdx + 4 * ib + 0 * SharedPitch + 5];\n        pix12 = LocalBlock[SharedIdx + 4 * ib + 1 * SharedPitch + 5];\n        pix22 = LocalBlock[SharedIdx + 4 * ib + 2 * SharedPitch + 5];\n        out.w = (*blockFunction)(pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (pPointFunction != NULL) {\n            out.x = (*pPointFunction)(out.x, THRESHOLD);\n            out.y = (*pPointFunction)(out.y, THRESHOLD);\n            out.z = (*pPointFunction)(out.z, THRESHOLD);\n            out.w = (*pPointFunction)(out.w, THRESHOLD);\n        }\n\n        if (u + ib < w / 4 && v < h) {\n            pSobel[u + ib] = out;\n        }\n    }\n\n    cg::sync(cta);\n}\n\n__global__ void\nSobelCopyImage(Pixel *pSobelOriginal, unsigned int Pitch, int w, int h, float fscale, hipTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        pSobel[i] = min(max((tex2D<unsigned char>(tex, (float)i, (float)blockIdx.x) * fscale), 0.f), 255.f);\n    }\n}\n\n// Perform block and pointer filtering using texture lookups.\n// The block and point operations are determined by the\n// input argument (see comment above for \"SobelShared\" function)\n__global__ void SobelTex(Pixel              *pSobelOriginal,\n                         unsigned int        Pitch,\n                         int                 w,\n                         int                 h,\n                         float               fScale,\n                         int                 blockOperation,\n                         pointFunction_t     pPointOperation,\n                         hipTextureObject_t tex)\n{\n    unsigned char *pSobel = (unsigned char *)(((char *)pSobelOriginal) + blockIdx.x * Pitch);\n    unsigned char  tmp    = 0;\n\n    for (int i = threadIdx.x; i < w; i += blockDim.x) {\n        unsigned char pix00 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x - 1);\n        unsigned char pix01 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x - 1);\n        unsigned char pix02 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x - 1);\n        unsigned char pix10 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 0);\n        unsigned char pix11 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 0);\n        unsigned char pix12 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 0);\n        unsigned char pix20 = tex2D<unsigned char>(tex, (float)i - 1, (float)blockIdx.x + 1);\n        unsigned char pix21 = tex2D<unsigned char>(tex, (float)i + 0, (float)blockIdx.x + 1);\n        unsigned char pix22 = tex2D<unsigned char>(tex, (float)i + 1, (float)blockIdx.x + 1);\n        tmp                 = (*(blockFunction_table[blockOperation]))(\n            pix00, pix01, pix02, pix10, pix11, pix12, pix20, pix21, pix22, fScale);\n\n        if (pPointOperation != NULL) {\n            tmp = (*pPointOperation)(tmp, 150.0);\n        }\n\n        pSobel[i] = tmp;\n    }\n}\n\nextern \"C\" void setupTexture(int iw, int ih, Pixel *data, int Bpp)\n{\n    hipChannelFormatDesc desc;\n\n    if (Bpp == 1) {\n        desc = hipCreateChannelDesc<unsigned char>();\n    }\n    else {\n        desc = hipCreateChannelDesc<uchar4>();\n    }\n\n    checkCudaErrors(hipMallocArray(&array, &desc, iw, ih));\n    checkCudaErrors(hipMemcpy2DToArray(\n        array, 0, 0, data, iw * Bpp * sizeof(Pixel), iw * Bpp * sizeof(Pixel), ih, hipMemcpyHostToDevice));\n\n    hipResourceDesc texRes;\n    memset(&texRes, 0, sizeof(hipResourceDesc));\n\n    texRes.resType         = hipResourceTypeArray;\n    texRes.res.array.array = array;\n\n    hipTextureDesc texDescr;\n    memset(&texDescr, 0, sizeof(hipTextureDesc));\n\n    checkCudaErrors(hipCreateTextureObject(&tex, &texRes, &texDescr, NULL));\n}\n\nextern \"C\" void deleteTexture(void)\n{\n    checkCudaErrors(hipFreeArray(array));\n    checkCudaErrors(hipDestroyTextureObject(tex));\n}\n\n// Copy the pointers from the function tables to the host side\nvoid setupFunctionTables()\n{\n    // Dynamically assign the function table.\n    // Copy the function pointers to their appropriate locations according to the\n    // enum\n    checkCudaErrors(hipMemcpyFromSymbol(&h_blockFunction_table[SOBEL_FILTER],HIP_SYMBOL(pComputeSobel), sizeof(blockFunction_t)));\n    checkCudaErrors(hipMemcpyFromSymbol(&h_blockFunction_table[BOX_FILTER],HIP_SYMBOL(pComputeBox), sizeof(blockFunction_t)));\n\n    // do the same for the point function, where the 2nd function is NULL (\"no-op\"\n    // filter, skipped in kernel code)\n    checkCudaErrors(\n        hipMemcpyFromSymbol(&h_pointFunction_table[THRESHOLD_FILTER],HIP_SYMBOL(pComputeThreshold), sizeof(pointFunction_t)));\n    h_pointFunction_table[NULL_FILTER] = NULL;\n\n    // now copy the function tables back to the device, so if we wish we can use\n    // an index into the table to choose them\n    // We have now set the order in the function table according to our enum.\n    checkCudaErrors(\n        hipMemcpyToSymbol(HIP_SYMBOL(blockFunction_table), h_blockFunction_table, sizeof(blockFunction_t) * LAST_BLOCK_FILTER));\n    checkCudaErrors(\n        hipMemcpyToSymbol(HIP_SYMBOL(pointFunction_table), h_pointFunction_table, sizeof(pointFunction_t) * LAST_POINT_FILTER));\n}\n\n// Wrapper for the __global__ call that sets up the texture and threads\n// Below two methods for selecting the image processing function to run are\n// shown.\n// BlockOperation is an integer kernel argument used as an index into the\n// blockFunction_table on the device side\n// pPointOp is itself a function pointer passed as a kernel argument, retrieved\n// from a host side copy of the function table\nextern \"C\" void sobelFilter(Pixel                *odata,\n                            int                   iw,\n                            int                   ih,\n                            enum SobelDisplayMode mode,\n                            float                 fScale,\n                            int                   blockOperation,\n                            int                   pointOperation)\n{\n    pointFunction_t pPointOp = h_pointFunction_table[pointOperation];\n\n    switch (mode) {\n    case SOBELDISPLAY_IMAGE:\n        SobelCopyImage<<<ih, 384>>>(odata, iw, iw, ih, fScale, tex);\n        break;\n\n    case SOBELDISPLAY_SOBELTEX:\n        SobelTex<<<ih, 384>>>(odata, iw, iw, ih, fScale, blockOperation, pPointOp, tex);\n        break;\n\n    case SOBELDISPLAY_SOBELSHARED: {\n        dim3 threads(16, 4);\n#ifndef FIXED_BLOCKWIDTH\n        int BlockWidth = 80; // must be divisible by 16 for coalescing\n#endif\n        dim3 blocks =\n            dim3(iw / (4 * BlockWidth) + (0 != iw % (4 * BlockWidth)), ih / threads.y + (0 != ih % threads.y));\n        int SharedPitch = ~0x3f & (4 * (BlockWidth + 2 * RADIUS) + 0x3f);\n        int sharedMem   = SharedPitch * (threads.y + 2 * RADIUS);\n\n        // for the shared kernel, width must be divisible by 4\n        iw &= ~3;\n\n        SobelShared<<<blocks, threads, sharedMem>>>((uchar4 *)odata,\n                                                    iw,\n#ifndef FIXED_BLOCKWIDTH\n                                                    BlockWidth,\n                                                    SharedPitch,\n#endif\n                                                    iw,\n                                                    ih,\n                                                    fScale,\n                                                    blockOperation,\n                                                    pPointOp,\n                                                    tex);\n    } break;\n    }\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/FunctionPointers/FunctionPointers_kernels.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// DESCRIPTION:   Simple CUDA consumer rendering sample app\n//\n\n#include <EGL/egl.h>\n#include <EGL/eglext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"eglstrm_common.h\"\n\nextern bool isCrossDevice;\n\n__device__ static unsigned int numErrors = 0, errorFound = 0;\n__device__ void                checkProducerDataGPU(char *data, int size, char expectedVal, int frameNumber)\n{\n    if ((data[blockDim.x * blockIdx.x + threadIdx.x] != expectedVal) && (!errorFound)) {\n        printf(\"Producer FOUND:%d expected: %d at %d for trial %d %d\\n\",\n               data[blockDim.x * blockIdx.x + threadIdx.x],\n               expectedVal,\n               (blockDim.x * blockIdx.x + threadIdx.x),\n               frameNumber,\n               numErrors);\n        numErrors++;\n        errorFound = 1;\n        return;\n    }\n}\n\n__device__ void checkConsumerDataGPU(char *data, int size, char expectedVal, int frameNumber)\n{\n    if ((data[blockDim.x * blockIdx.x + threadIdx.x] != expectedVal) && (!errorFound)) {\n        printf(\"Consumer FOUND:%d expected: %d at %d for trial %d %d\\n\",\n               data[blockDim.x * blockIdx.x + threadIdx.x],\n               expectedVal,\n               (blockDim.x * blockIdx.x + threadIdx.x),\n               frameNumber,\n               numErrors);\n        numErrors++;\n        errorFound = 1;\n        return;\n    }\n}\n\n__global__ void writeDataToBuffer(char *pSrc, char newVal) { pSrc[blockDim.x * blockIdx.x + threadIdx.x] = newVal; }\n\n__global__ void testKernelConsumer(char *pSrc, char size, char expectedVal, char newVal, int frameNumber)\n{\n    checkConsumerDataGPU(pSrc, size, expectedVal, frameNumber);\n}\n\n__global__ void testKernelProducer(char *pSrc, char size, char expectedVal, char newVal, int frameNumber)\n{\n    checkProducerDataGPU(pSrc, size, expectedVal, frameNumber);\n}\n__global__ void getNumErrors(int *numErr) { *numErr = numErrors; }\n\ncudaError_t cudaProducer_filter(cudaStream_t pStream,\n                                char        *pSrc,\n                                int          width,\n                                int          height,\n                                char         expectedVal,\n                                char         newVal,\n                                int          frameNumber)\n{\n    // in case where consumer is on dgpu and producer is on igpu when return is\n    // called the frame is not copied back to igpu. So the consumer changes is not\n    // visible to producer\n    if (isCrossDevice == 0) {\n        testKernelProducer<<<(width * height) / 1024, 1024, 1, pStream>>>(\n            pSrc, width * height, expectedVal, newVal, frameNumber);\n    }\n    writeDataToBuffer<<<(width * height) / 1024, 1024, 1, pStream>>>(pSrc, newVal);\n    return cudaSuccess;\n};\n\ncudaError_t cudaConsumer_filter(cudaStream_t cStream,\n                                char        *pSrc,\n                                int          width,\n                                int          height,\n                                char         expectedVal,\n                                char         newVal,\n                                int          frameNumber)\n{\n    testKernelConsumer<<<(width * height) / 1024, 1024, 1, cStream>>>(\n        pSrc, width * height, expectedVal, newVal, frameNumber);\n    writeDataToBuffer<<<(width * height) / 1024, 1024, 1, cStream>>>(pSrc, newVal);\n    return cudaSuccess;\n};\n\ncudaError_t cudaGetValueMismatch()\n{\n    int         numErr_h;\n    int        *numErr_d = NULL;\n    cudaError_t err      = cudaSuccess;\n    err                  = cudaMalloc(&numErr_d, sizeof(int));\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaMalloc failed with %s\\n\", cudaGetErrorString(err));\n        return err;\n    }\n    getNumErrors<<<1, 1>>>(numErr_d);\n    err = cudaDeviceSynchronize();\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaDeviceSynchronize failed with %s\\n\", cudaGetErrorString(err));\n    }\n    err = cudaMemcpy(&numErr_h, numErr_d, sizeof(int), cudaMemcpyDeviceToHost);\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaMemcpy failed with %s\\n\", cudaGetErrorString(err));\n        cudaFree(numErr_d);\n        return err;\n    }\n    err = cudaFree(numErr_d);\n    if (err != cudaSuccess) {\n        printf(\"Cuda Main: cudaFree failed with %s\\n\", cudaGetErrorString(err));\n        return err;\n    }\n    if (numErr_h > 0) {\n        return cudaErrorUnknown;\n    }\n    return cudaSuccess;\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//\n// DESCRIPTION:   Simple CUDA consumer rendering sample app\n//\n\n#include <EGL/egl.h>\n#include <EGL/eglext.h>\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"eglstrm_common.h\"\n\nextern bool isCrossDevice;\n\n__device__ static unsigned int numErrors = 0, errorFound = 0;\n__device__ void                checkProducerDataGPU(char *data, int size, char expectedVal, int frameNumber)\n{\n    if ((data[blockDim.x * blockIdx.x + threadIdx.x] != expectedVal) && (!errorFound)) {\n        printf(\"Producer FOUND:%d expected: %d at %d for trial %d %d\\n\",\n               data[blockDim.x * blockIdx.x + threadIdx.x],\n               expectedVal,\n               (blockDim.x * blockIdx.x + threadIdx.x),\n               frameNumber,\n               numErrors);\n        numErrors++;\n        errorFound = 1;\n        return;\n    }\n}\n\n__device__ void checkConsumerDataGPU(char *data, int size, char expectedVal, int frameNumber)\n{\n    if ((data[blockDim.x * blockIdx.x + threadIdx.x] != expectedVal) && (!errorFound)) {\n        printf(\"Consumer FOUND:%d expected: %d at %d for trial %d %d\\n\",\n               data[blockDim.x * blockIdx.x + threadIdx.x],\n               expectedVal,\n               (blockDim.x * blockIdx.x + threadIdx.x),\n               frameNumber,\n               numErrors);\n        numErrors++;\n        errorFound = 1;\n        return;\n    }\n}\n\n__global__ void writeDataToBuffer(char *pSrc, char newVal) { pSrc[blockDim.x * blockIdx.x + threadIdx.x] = newVal; }\n\n__global__ void testKernelConsumer(char *pSrc, char size, char expectedVal, char newVal, int frameNumber)\n{\n    checkConsumerDataGPU(pSrc, size, expectedVal, frameNumber);\n}\n\n__global__ void testKernelProducer(char *pSrc, char size, char expectedVal, char newVal, int frameNumber)\n{\n    checkProducerDataGPU(pSrc, size, expectedVal, frameNumber);\n}\n__global__ void getNumErrors(int *numErr) { *numErr = numErrors; }\n\nhipError_t cudaProducer_filter(hipStream_t pStream,\n                                char        *pSrc,\n                                int          width,\n                                int          height,\n                                char         expectedVal,\n                                char         newVal,\n                                int          frameNumber)\n{\n    // in case where consumer is on dgpu and producer is on igpu when return is\n    // called the frame is not copied back to igpu. So the consumer changes is not\n    // visible to producer\n    if (isCrossDevice == 0) {\n        testKernelProducer<<<(width * height) / 1024, 1024, 1, pStream>>>(\n            pSrc, width * height, expectedVal, newVal, frameNumber);\n    }\n    writeDataToBuffer<<<(width * height) / 1024, 1024, 1, pStream>>>(pSrc, newVal);\n    return hipSuccess;\n};\n\nhipError_t cudaConsumer_filter(hipStream_t cStream,\n                                char        *pSrc,\n                                int          width,\n                                int          height,\n                                char         expectedVal,\n                                char         newVal,\n                                int          frameNumber)\n{\n    testKernelConsumer<<<(width * height) / 1024, 1024, 1, cStream>>>(\n        pSrc, width * height, expectedVal, newVal, frameNumber);\n    writeDataToBuffer<<<(width * height) / 1024, 1024, 1, cStream>>>(pSrc, newVal);\n    return hipSuccess;\n};\n\nhipError_t cudaGetValueMismatch()\n{\n    int         numErr_h;\n    int        *numErr_d = NULL;\n    hipError_t err      = hipSuccess;\n    err                  = hipMalloc(&numErr_d, sizeof(int));\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipMalloc failed with %s\\n\", hipGetErrorString(err));\n        return err;\n    }\n    getNumErrors<<<1, 1>>>(numErr_d);\n    err = hipDeviceSynchronize();\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipDeviceSynchronize failed with %s\\n\", hipGetErrorString(err));\n    }\n    err = hipMemcpy(&numErr_h, numErr_d, sizeof(int), hipMemcpyDeviceToHost);\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipMemcpy failed with %s\\n\", hipGetErrorString(err));\n        hipFree(numErr_d);\n        return err;\n    }\n    err = hipFree(numErr_d);\n    if (err != hipSuccess) {\n        printf(\"Cuda Main: hipFree failed with %s\\n\", hipGetErrorString(err));\n        return err;\n    }\n    if (numErr_h > 0) {\n        return hipErrorUnknown;\n    }\n    return hipSuccess;\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/EGLStream_CUDA_CrossGPU/kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nextern \"C\" __global__ void kernelFunction(int *input) { input[threadIdx.x] = 32 - threadIdx.x; }\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\nextern \"C\" __global__ void kernelFunction(int *input) { input[threadIdx.x] = 32 - threadIdx.x; }\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/threadMigration/threadMigration_kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Shuffle intrinsics CUDA Sample\n// This sample demonstrates the use of the shuffle intrinsic\n// First, a simple example of a prefix sum using the shuffle to\n// perform a scan operation is provided.\n// Secondly, a more involved example of computing an integral image\n// using the shuffle intrinsic is provided, where the shuffle\n// scan operation and shuffle xor operations are used\n\n#include <cuda_runtime.h>\n#include <helper_cuda.h>\n#include <helper_functions.h>\n#include <stdio.h>\n\n#include \"shfl_integral_image.cuh\"\n\n// Scan using shfl - takes log2(n) steps\n// This function demonstrates basic use of the shuffle intrinsic, __shfl_up,\n// to perform a scan operation across a block.\n// First, it performs a scan (prefix sum in this case) inside a warp\n// Then to continue the scan operation across the block,\n// each warp's sum is placed into shared memory.  A single warp\n// then performs a shuffle scan on that shared memory.  The results\n// are then uniformly added to each warp's threads.\n// This pyramid type approach is continued by placing each block's\n// final sum in global memory and prefix summing that via another kernel call,\n// then uniformly adding across the input data via the uniform_add<<<>>> kernel.\n\n__global__ void shfl_scan_test(int *data, int width, int *partial_sums = NULL)\n{\n    extern __shared__ int sums[];\n    int                   id      = ((blockIdx.x * blockDim.x) + threadIdx.x);\n    int                   lane_id = id % warpSize;\n    // determine a warp_id within a block\n    int warp_id = threadIdx.x / warpSize;\n\n    // Below is the basic structure of using a shfl instruction\n    // for a scan.\n    // Record \"value\" as a variable - we accumulate it along the way\n    int value = data[id];\n\n    // Now accumulate in log steps up the chain\n    // compute sums, with another thread's value who is\n    // distance delta away (i).  Note\n    // those threads where the thread 'i' away would have\n    // been out of bounds of the warp are unaffected.  This\n    // creates the scan sum.\n\n#pragma unroll\n    for (int i = 1; i <= width; i *= 2) {\n        unsigned int mask = 0xffffffff;\n        int          n    = __shfl_up_sync(mask, value, i, width);\n\n        if (lane_id >= i)\n            value += n;\n    }\n\n    // value now holds the scan value for the individual thread\n    // next sum the largest values for each warp\n\n    // write the sum of the warp to smem\n    if (threadIdx.x % warpSize == warpSize - 1) {\n        sums[warp_id] = value;\n    }\n\n    __syncthreads();\n\n    //\n    // scan sum the warp sums\n    // the same shfl scan operation, but performed on warp sums\n    //\n    if (warp_id == 0 && lane_id < (blockDim.x / warpSize)) {\n        int warp_sum = sums[lane_id];\n\n        int mask = (1 << (blockDim.x / warpSize)) - 1;\n        for (int i = 1; i <= (blockDim.x / warpSize); i *= 2) {\n            int n = __shfl_up_sync(mask, warp_sum, i, (blockDim.x / warpSize));\n\n            if (lane_id >= i)\n                warp_sum += n;\n        }\n\n        sums[lane_id] = warp_sum;\n    }\n\n    __syncthreads();\n\n    // perform a uniform add across warps in the block\n    // read neighbouring warp's sum and add it to threads value\n    int blockSum = 0;\n\n    if (warp_id > 0) {\n        blockSum = sums[warp_id - 1];\n    }\n\n    value += blockSum;\n\n    // Now write out our result\n    data[id] = value;\n\n    // last thread has sum, write write out the block's sum\n    if (partial_sums != NULL && threadIdx.x == blockDim.x - 1) {\n        partial_sums[blockIdx.x] = value;\n    }\n}\n\n// Uniform add: add partial sums array\n__global__ void uniform_add(int *data, int *partial_sums, int len)\n{\n    __shared__ int buf;\n    int            id = ((blockIdx.x * blockDim.x) + threadIdx.x);\n\n    if (id > len)\n        return;\n\n    if (threadIdx.x == 0) {\n        buf = partial_sums[blockIdx.x];\n    }\n\n    __syncthreads();\n    data[id] += buf;\n}\n\nstatic unsigned int iDivUp(unsigned int dividend, unsigned int divisor)\n{\n    return ((dividend % divisor) == 0) ? (dividend / divisor) : (dividend / divisor + 1);\n}\n\n// This function verifies the shuffle scan result, for the simple\n// prefix sum case.\nbool CPUverify(int *h_data, int *h_result, int n_elements)\n{\n    // cpu verify\n    for (int i = 0; i < n_elements - 1; i++) {\n        h_data[i + 1] = h_data[i] + h_data[i + 1];\n    }\n\n    int diff = 0;\n\n    for (int i = 0; i < n_elements; i++) {\n        diff += h_data[i] - h_result[i];\n    }\n\n    printf(\"CPU verify result diff (GPUvsCPU) = %d\\n\", diff);\n    bool bTestResult = false;\n\n    if (diff == 0)\n        bTestResult = true;\n\n    StopWatchInterface *hTimer = NULL;\n    sdkCreateTimer(&hTimer);\n    sdkResetTimer(&hTimer);\n    sdkStartTimer(&hTimer);\n\n    for (int j = 0; j < 100; j++)\n        for (int i = 0; i < n_elements - 1; i++) {\n            h_data[i + 1] = h_data[i] + h_data[i + 1];\n        }\n\n    sdkStopTimer(&hTimer);\n    double cput = sdkGetTimerValue(&hTimer);\n    printf(\"CPU sum (naive) took %f ms\\n\", cput / 100);\n    return bTestResult;\n}\n\n// this verifies the row scan result for synthetic data of all 1's\nunsigned int verifyDataRowSums(unsigned int *h_image, int w, int h)\n{\n    unsigned int diff = 0;\n\n    for (int j = 0; j < h; j++) {\n        for (int i = 0; i < w; i++) {\n            int gold = i + 1;\n            diff += abs(static_cast<int>(gold) - static_cast<int>(h_image[j * w + i]));\n        }\n    }\n\n    return diff;\n}\n\nbool shuffle_simple_test(int argc, char **argv)\n{\n    int      *h_data, *h_partial_sums, *h_result;\n    int      *d_data, *d_partial_sums;\n    const int n_elements  = 65536;\n    int       sz          = sizeof(int) * n_elements;\n    int       cuda_device = 0;\n\n    printf(\"Starting shfl_scan\\n\");\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDevice(&cuda_device));\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // __shfl intrinsic needs SM 3.0 or higher\n    if (deviceProp.major < 3) {\n        printf(\"> __shfl() intrinsic requires device SM 3.0+\\n\");\n        printf(\"> Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    checkCudaErrors(cudaMallocHost(reinterpret_cast<void **>(&h_data), sizeof(int) * n_elements));\n    checkCudaErrors(cudaMallocHost(reinterpret_cast<void **>(&h_result), sizeof(int) * n_elements));\n\n    // initialize data:\n    printf(\"Computing Simple Sum test\\n\");\n    printf(\"---------------------------------------------------\\n\");\n\n    printf(\"Initialize test data [1, 1, 1...]\\n\");\n\n    for (int i = 0; i < n_elements; i++) {\n        h_data[i] = 1;\n    }\n\n    int blockSize     = 256;\n    int gridSize      = n_elements / blockSize;\n    int nWarps        = blockSize / 32;\n    int shmem_sz      = nWarps * sizeof(int);\n    int n_partialSums = n_elements / blockSize;\n    int partial_sz    = n_partialSums * sizeof(int);\n\n    printf(\"Scan summation for %d elements, %d partial sums\\n\", n_elements, n_elements / blockSize);\n\n    int p_blockSize = min(n_partialSums, blockSize);\n    int p_gridSize  = iDivUp(n_partialSums, p_blockSize);\n    printf(\"Partial summing %d elements with %d blocks of size %d\\n\", n_partialSums, p_gridSize, p_blockSize);\n\n    // initialize a timer\n    cudaEvent_t start, stop;\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&stop));\n    float et  = 0;\n    float inc = 0;\n\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_data), sz));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_partial_sums), partial_sz));\n    checkCudaErrors(cudaMemset(d_partial_sums, 0, partial_sz));\n\n    checkCudaErrors(cudaMallocHost(reinterpret_cast<void **>(&h_partial_sums), partial_sz));\n    checkCudaErrors(cudaMemcpy(d_data, h_data, sz, cudaMemcpyHostToDevice));\n\n    checkCudaErrors(cudaEventRecord(start, 0));\n    shfl_scan_test<<<gridSize, blockSize, shmem_sz>>>(d_data, 32, d_partial_sums);\n    shfl_scan_test<<<p_gridSize, p_blockSize, shmem_sz>>>(d_partial_sums, 32);\n    uniform_add<<<gridSize - 1, blockSize>>>(d_data + blockSize, d_partial_sums, n_elements);\n    checkCudaErrors(cudaEventRecord(stop, 0));\n    checkCudaErrors(cudaEventSynchronize(stop));\n    checkCudaErrors(cudaEventElapsedTime(&inc, start, stop));\n    et += inc;\n\n    checkCudaErrors(cudaMemcpy(h_result, d_data, sz, cudaMemcpyDeviceToHost));\n    checkCudaErrors(cudaMemcpy(h_partial_sums, d_partial_sums, partial_sz, cudaMemcpyDeviceToHost));\n\n    printf(\"Test Sum: %d\\n\", h_partial_sums[n_partialSums - 1]);\n    printf(\"Time (ms): %f\\n\", et);\n    printf(\"%d elements scanned in %f ms -> %f MegaElements/s\\n\",\n           n_elements,\n           et,\n           n_elements / (et / 1000.0f) / 1000000.0f);\n\n    bool bTestResult = CPUverify(h_data, h_result, n_elements);\n\n    checkCudaErrors(cudaFreeHost(h_data));\n    checkCudaErrors(cudaFreeHost(h_result));\n    checkCudaErrors(cudaFreeHost(h_partial_sums));\n    checkCudaErrors(cudaFree(d_data));\n    checkCudaErrors(cudaFree(d_partial_sums));\n\n    return bTestResult;\n}\n\n// This function tests creation of an integral image using\n// synthetic data, of size 1920x1080 pixels greyscale.\nbool shuffle_integral_image_test()\n{\n    char         *d_data;\n    unsigned int *h_image;\n    unsigned int *d_integral_image;\n    int           w          = 1920;\n    int           h          = 1080;\n    int           n_elements = w * h;\n    int           sz         = sizeof(unsigned int) * n_elements;\n\n    printf(\"\\nComputing Integral Image Test on size %d x %d synthetic data\\n\", w, h);\n    printf(\"---------------------------------------------------\\n\");\n    checkCudaErrors(cudaMallocHost(reinterpret_cast<void **>(&h_image), sz));\n    // fill test \"image\" with synthetic 1's data\n    memset(h_image, 0, sz);\n\n    // each thread handles 16 values, use 1 block/row\n    int blockSize = iDivUp(w, 16);\n    // launch 1 block / row\n    int gridSize = h;\n\n    // Create a synthetic image for testing\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_data), sz));\n    checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&d_integral_image), n_elements * sizeof(int) * 4));\n    checkCudaErrors(cudaMemset(d_data, 1, sz));\n    checkCudaErrors(cudaMemset(d_integral_image, 0, sz));\n\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n    float        et = 0;\n    unsigned int err;\n\n    // Execute scan line prefix sum kernel, and time it\n    cudaEventRecord(start);\n    shfl_intimage_rows<<<gridSize, blockSize>>>(reinterpret_cast<uint4 *>(d_data),\n                                                reinterpret_cast<uint4 *>(d_integral_image));\n    cudaEventRecord(stop);\n    checkCudaErrors(cudaEventSynchronize(stop));\n    checkCudaErrors(cudaEventElapsedTime(&et, start, stop));\n    printf(\"Method: Fast  Time (GPU Timer): %f ms \", et);\n\n    // verify the scan line results\n    checkCudaErrors(cudaMemcpy(h_image, d_integral_image, sz, cudaMemcpyDeviceToHost));\n    err = verifyDataRowSums(h_image, w, h);\n    printf(\"Diff = %d\\n\", err);\n\n    // Execute column prefix sum kernel and time it\n    dim3 blockSz(32, 8);\n    dim3 testGrid(w / blockSz.x, 1);\n\n    cudaEventRecord(start);\n    shfl_vertical_shfl<<<testGrid, blockSz>>>((unsigned int *)d_integral_image, w, h);\n    cudaEventRecord(stop);\n    checkCudaErrors(cudaEventSynchronize(stop));\n    checkCudaErrors(cudaEventElapsedTime(&et, start, stop));\n    printf(\"Method: Vertical Scan  Time (GPU Timer): %f ms \", et);\n\n    // Verify the column results\n    checkCudaErrors(cudaMemcpy(h_image, d_integral_image, sz, cudaMemcpyDeviceToHost));\n    printf(\"\\n\");\n\n    int finalSum = h_image[w * h - 1];\n    printf(\"CheckSum: %d, (expect %dx%d=%d)\\n\", finalSum, w, h, w * h);\n\n    checkCudaErrors(cudaFree(d_data));\n    checkCudaErrors(cudaFree(d_integral_image));\n    checkCudaErrors(cudaFreeHost(h_image));\n    // verify final sum: if the final value in the corner is the same as the size\n    // of the buffer (all 1's) then the integral image was generated successfully\n    return (finalSum == w * h) ? true : false;\n}\n\nint main(int argc, char *argv[])\n{\n    // Initialization.  The shuffle intrinsic is not available on SM < 3.0\n    // so waive the test if the hardware is not present.\n    int cuda_device = 0;\n\n    printf(\"Starting shfl_scan\\n\");\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    cudaDeviceProp deviceProp;\n    checkCudaErrors(cudaGetDevice(&cuda_device));\n\n    checkCudaErrors(cudaGetDeviceProperties(&deviceProp, cuda_device));\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // __shfl intrinsic needs SM 3.0 or higher\n    if (deviceProp.major < 3) {\n        printf(\"> __shfl() intrinsic requires device SM 3.0+\\n\");\n        printf(\"> Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    bool bTestResult = true;\n    bool simpleTest  = shuffle_simple_test(argc, argv);\n    bool intTest     = shuffle_integral_image_test();\n\n    bTestResult = simpleTest & intTest;\n\n    exit((bTestResult) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Shuffle intrinsics CUDA Sample\n// This sample demonstrates the use of the shuffle intrinsic\n// First, a simple example of a prefix sum using the shuffle to\n// perform a scan operation is provided.\n// Secondly, a more involved example of computing an integral image\n// using the shuffle intrinsic is provided, where the shuffle\n// scan operation and shuffle xor operations are used\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n#include <stdio.h>\n\n#include \"shfl_integral_image.cuh\"\n\n// Scan using shfl - takes log2(n) steps\n// This function demonstrates basic use of the shuffle intrinsic, __shfl_up,\n// to perform a scan operation across a block.\n// First, it performs a scan (prefix sum in this case) inside a warp\n// Then to continue the scan operation across the block,\n// each warp's sum is placed into shared memory.  A single warp\n// then performs a shuffle scan on that shared memory.  The results\n// are then uniformly added to each warp's threads.\n// This pyramid type approach is continued by placing each block's\n// final sum in global memory and prefix summing that via another kernel call,\n// then uniformly adding across the input data via the uniform_add<<<>>> kernel.\n\n__global__ void shfl_scan_test(int *data, int width, int *partial_sums = NULL)\n{\n    extern __shared__ int sums[];\n    int                   id      = ((blockIdx.x * blockDim.x) + threadIdx.x);\n    int                   lane_id = id % warpSize;\n    // determine a warp_id within a block\n    int warp_id = threadIdx.x / warpSize;\n\n    // Below is the basic structure of using a shfl instruction\n    // for a scan.\n    // Record \"value\" as a variable - we accumulate it along the way\n    int value = data[id];\n\n    // Now accumulate in log steps up the chain\n    // compute sums, with another thread's value who is\n    // distance delta away (i).  Note\n    // those threads where the thread 'i' away would have\n    // been out of bounds of the warp are unaffected.  This\n    // creates the scan sum.\n\n#pragma unroll\n    for (int i = 1; i <= width; i *= 2) {\n        unsigned int mask = 0xffffffff;\n        int          n    = __shfl_up_sync(mask, value, i, width);\n\n        if (lane_id >= i)\n            value += n;\n    }\n\n    // value now holds the scan value for the individual thread\n    // next sum the largest values for each warp\n\n    // write the sum of the warp to smem\n    if (threadIdx.x % warpSize == warpSize - 1) {\n        sums[warp_id] = value;\n    }\n\n    __syncthreads();\n\n    //\n    // scan sum the warp sums\n    // the same shfl scan operation, but performed on warp sums\n    //\n    if (warp_id == 0 && lane_id < (blockDim.x / warpSize)) {\n        int warp_sum = sums[lane_id];\n\n        int mask = (1 << (blockDim.x / warpSize)) - 1;\n        for (int i = 1; i <= (blockDim.x / warpSize); i *= 2) {\n            int n = __shfl_up_sync(mask, warp_sum, i, (blockDim.x / warpSize));\n\n            if (lane_id >= i)\n                warp_sum += n;\n        }\n\n        sums[lane_id] = warp_sum;\n    }\n\n    __syncthreads();\n\n    // perform a uniform add across warps in the block\n    // read neighbouring warp's sum and add it to threads value\n    int blockSum = 0;\n\n    if (warp_id > 0) {\n        blockSum = sums[warp_id - 1];\n    }\n\n    value += blockSum;\n\n    // Now write out our result\n    data[id] = value;\n\n    // last thread has sum, write write out the block's sum\n    if (partial_sums != NULL && threadIdx.x == blockDim.x - 1) {\n        partial_sums[blockIdx.x] = value;\n    }\n}\n\n// Uniform add: add partial sums array\n__global__ void uniform_add(int *data, int *partial_sums, int len)\n{\n    __shared__ int buf;\n    int            id = ((blockIdx.x * blockDim.x) + threadIdx.x);\n\n    if (id > len)\n        return;\n\n    if (threadIdx.x == 0) {\n        buf = partial_sums[blockIdx.x];\n    }\n\n    __syncthreads();\n    data[id] += buf;\n}\n\nstatic unsigned int iDivUp(unsigned int dividend, unsigned int divisor)\n{\n    return ((dividend % divisor) == 0) ? (dividend / divisor) : (dividend / divisor + 1);\n}\n\n// This function verifies the shuffle scan result, for the simple\n// prefix sum case.\nbool CPUverify(int *h_data, int *h_result, int n_elements)\n{\n    // cpu verify\n    for (int i = 0; i < n_elements - 1; i++) {\n        h_data[i + 1] = h_data[i] + h_data[i + 1];\n    }\n\n    int diff = 0;\n\n    for (int i = 0; i < n_elements; i++) {\n        diff += h_data[i] - h_result[i];\n    }\n\n    printf(\"CPU verify result diff (GPUvsCPU) = %d\\n\", diff);\n    bool bTestResult = false;\n\n    if (diff == 0)\n        bTestResult = true;\n\n    StopWatchInterface *hTimer = NULL;\n    sdkCreateTimer(&hTimer);\n    sdkResetTimer(&hTimer);\n    sdkStartTimer(&hTimer);\n\n    for (int j = 0; j < 100; j++)\n        for (int i = 0; i < n_elements - 1; i++) {\n            h_data[i + 1] = h_data[i] + h_data[i + 1];\n        }\n\n    sdkStopTimer(&hTimer);\n    double cput = sdkGetTimerValue(&hTimer);\n    printf(\"CPU sum (naive) took %f ms\\n\", cput / 100);\n    return bTestResult;\n}\n\n// this verifies the row scan result for synthetic data of all 1's\nunsigned int verifyDataRowSums(unsigned int *h_image, int w, int h)\n{\n    unsigned int diff = 0;\n\n    for (int j = 0; j < h; j++) {\n        for (int i = 0; i < w; i++) {\n            int gold = i + 1;\n            diff += abs(static_cast<int>(gold) - static_cast<int>(h_image[j * w + i]));\n        }\n    }\n\n    return diff;\n}\n\nbool shuffle_simple_test(int argc, char **argv)\n{\n    int      *h_data, *h_partial_sums, *h_result;\n    int      *d_data, *d_partial_sums;\n    const int n_elements  = 65536;\n    int       sz          = sizeof(int) * n_elements;\n    int       cuda_device = 0;\n\n    printf(\"Starting shfl_scan\\n\");\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDevice(&cuda_device));\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // __shfl intrinsic needs SM 3.0 or higher\n    if (deviceProp.major < 3) {\n        printf(\"> __shfl() intrinsic requires device SM 3.0+\\n\");\n        printf(\"> Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    checkCudaErrors(hipHostMalloc(reinterpret_cast<void **>(&h_data), sizeof(int) * n_elements));\n    checkCudaErrors(hipHostMalloc(reinterpret_cast<void **>(&h_result), sizeof(int) * n_elements));\n\n    // initialize data:\n    printf(\"Computing Simple Sum test\\n\");\n    printf(\"---------------------------------------------------\\n\");\n\n    printf(\"Initialize test data [1, 1, 1...]\\n\");\n\n    for (int i = 0; i < n_elements; i++) {\n        h_data[i] = 1;\n    }\n\n    int blockSize     = 256;\n    int gridSize      = n_elements / blockSize;\n    int nWarps        = blockSize / 32;\n    int shmem_sz      = nWarps * sizeof(int);\n    int n_partialSums = n_elements / blockSize;\n    int partial_sz    = n_partialSums * sizeof(int);\n\n    printf(\"Scan summation for %d elements, %d partial sums\\n\", n_elements, n_elements / blockSize);\n\n    int p_blockSize = min(n_partialSums, blockSize);\n    int p_gridSize  = iDivUp(n_partialSums, p_blockSize);\n    printf(\"Partial summing %d elements with %d blocks of size %d\\n\", n_partialSums, p_gridSize, p_blockSize);\n\n    // initialize a timer\n    hipEvent_t start, stop;\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&stop));\n    float et  = 0;\n    float inc = 0;\n\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_data), sz));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_partial_sums), partial_sz));\n    checkCudaErrors(hipMemset(d_partial_sums, 0, partial_sz));\n\n    checkCudaErrors(hipHostMalloc(reinterpret_cast<void **>(&h_partial_sums), partial_sz));\n    checkCudaErrors(hipMemcpy(d_data, h_data, sz, hipMemcpyHostToDevice));\n\n    checkCudaErrors(hipEventRecord(start, 0));\n    shfl_scan_test<<<gridSize, blockSize, shmem_sz>>>(d_data, 32, d_partial_sums);\n    shfl_scan_test<<<p_gridSize, p_blockSize, shmem_sz>>>(d_partial_sums, 32);\n    uniform_add<<<gridSize - 1, blockSize>>>(d_data + blockSize, d_partial_sums, n_elements);\n    checkCudaErrors(hipEventRecord(stop, 0));\n    checkCudaErrors(hipEventSynchronize(stop));\n    checkCudaErrors(hipEventElapsedTime(&inc, start, stop));\n    et += inc;\n\n    checkCudaErrors(hipMemcpy(h_result, d_data, sz, hipMemcpyDeviceToHost));\n    checkCudaErrors(hipMemcpy(h_partial_sums, d_partial_sums, partial_sz, hipMemcpyDeviceToHost));\n\n    printf(\"Test Sum: %d\\n\", h_partial_sums[n_partialSums - 1]);\n    printf(\"Time (ms): %f\\n\", et);\n    printf(\"%d elements scanned in %f ms -> %f MegaElements/s\\n\",\n           n_elements,\n           et,\n           n_elements / (et / 1000.0f) / 1000000.0f);\n\n    bool bTestResult = CPUverify(h_data, h_result, n_elements);\n\n    checkCudaErrors(hipHostFree(h_data));\n    checkCudaErrors(hipHostFree(h_result));\n    checkCudaErrors(hipHostFree(h_partial_sums));\n    checkCudaErrors(hipFree(d_data));\n    checkCudaErrors(hipFree(d_partial_sums));\n\n    return bTestResult;\n}\n\n// This function tests creation of an integral image using\n// synthetic data, of size 1920x1080 pixels greyscale.\nbool shuffle_integral_image_test()\n{\n    char         *d_data;\n    unsigned int *h_image;\n    unsigned int *d_integral_image;\n    int           w          = 1920;\n    int           h          = 1080;\n    int           n_elements = w * h;\n    int           sz         = sizeof(unsigned int) * n_elements;\n\n    printf(\"\\nComputing Integral Image Test on size %d x %d synthetic data\\n\", w, h);\n    printf(\"---------------------------------------------------\\n\");\n    checkCudaErrors(hipHostMalloc(reinterpret_cast<void **>(&h_image), sz));\n    // fill test \"image\" with synthetic 1's data\n    memset(h_image, 0, sz);\n\n    // each thread handles 16 values, use 1 block/row\n    int blockSize = iDivUp(w, 16);\n    // launch 1 block / row\n    int gridSize = h;\n\n    // Create a synthetic image for testing\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_data), sz));\n    checkCudaErrors(hipMalloc(reinterpret_cast<void **>(&d_integral_image), n_elements * sizeof(int) * 4));\n    checkCudaErrors(hipMemset(d_data, 1, sz));\n    checkCudaErrors(hipMemset(d_integral_image, 0, sz));\n\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n    float        et = 0;\n    unsigned int err;\n\n    // Execute scan line prefix sum kernel, and time it\n    hipEventRecord(start);\n    shfl_intimage_rows<<<gridSize, blockSize>>>(reinterpret_cast<uint4 *>(d_data),\n                                                reinterpret_cast<uint4 *>(d_integral_image));\n    hipEventRecord(stop);\n    checkCudaErrors(hipEventSynchronize(stop));\n    checkCudaErrors(hipEventElapsedTime(&et, start, stop));\n    printf(\"Method: Fast  Time (GPU Timer): %f ms \", et);\n\n    // verify the scan line results\n    checkCudaErrors(hipMemcpy(h_image, d_integral_image, sz, hipMemcpyDeviceToHost));\n    err = verifyDataRowSums(h_image, w, h);\n    printf(\"Diff = %d\\n\", err);\n\n    // Execute column prefix sum kernel and time it\n    dim3 blockSz(32, 8);\n    dim3 testGrid(w / blockSz.x, 1);\n\n    hipEventRecord(start);\n    shfl_vertical_shfl<<<testGrid, blockSz>>>((unsigned int *)d_integral_image, w, h);\n    hipEventRecord(stop);\n    checkCudaErrors(hipEventSynchronize(stop));\n    checkCudaErrors(hipEventElapsedTime(&et, start, stop));\n    printf(\"Method: Vertical Scan  Time (GPU Timer): %f ms \", et);\n\n    // Verify the column results\n    checkCudaErrors(hipMemcpy(h_image, d_integral_image, sz, hipMemcpyDeviceToHost));\n    printf(\"\\n\");\n\n    int finalSum = h_image[w * h - 1];\n    printf(\"CheckSum: %d, (expect %dx%d=%d)\\n\", finalSum, w, h, w * h);\n\n    checkCudaErrors(hipFree(d_data));\n    checkCudaErrors(hipFree(d_integral_image));\n    checkCudaErrors(hipHostFree(h_image));\n    // verify final sum: if the final value in the corner is the same as the size\n    // of the buffer (all 1's) then the integral image was generated successfully\n    return (finalSum == w * h) ? true : false;\n}\n\nint main(int argc, char *argv[])\n{\n    // Initialization.  The shuffle intrinsic is not available on SM < 3.0\n    // so waive the test if the hardware is not present.\n    int cuda_device = 0;\n\n    printf(\"Starting shfl_scan\\n\");\n\n    // use command-line specified CUDA device, otherwise use device with highest\n    // Gflops/s\n    cuda_device = findCudaDevice(argc, (const char **)argv);\n\n    hipDeviceProp_t deviceProp;\n    checkCudaErrors(hipGetDevice(&cuda_device));\n\n    checkCudaErrors(hipGetDeviceProperties(&deviceProp, cuda_device));\n\n    printf(\"> Detected Compute SM %d.%d hardware with %d multi-processors\\n\",\n           deviceProp.major,\n           deviceProp.minor,\n           deviceProp.multiProcessorCount);\n\n    // __shfl intrinsic needs SM 3.0 or higher\n    if (deviceProp.major < 3) {\n        printf(\"> __shfl() intrinsic requires device SM 3.0+\\n\");\n        printf(\"> Waiving test.\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    bool bTestResult = true;\n    bool simpleTest  = shuffle_simple_test(argc, argv);\n    bool intTest     = shuffle_integral_image_test();\n\n    bTestResult = simpleTest & intTest;\n\n    exit((bTestResult) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/shfl_scan/shfl_scan.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <curand_kernel.h>\n\nusing std::string;\nusing std::vector;\n\n// RNG init kernel\n__global__ void initRNG(curandState *const rngStates, const unsigned int seed)\n{\n    // Determine thread ID\n    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Initialise the RNG\n    curand_init(seed, tid, 0, &rngStates[tid]);\n}\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n__device__ inline void getPoint(float &x, float &y, curandState &state)\n{\n    x = curand_uniform(&state);\n    y = curand_uniform(&state);\n}\n__device__ inline void getPoint(double &x, double &y, curandState &state)\n{\n    x = curand_uniform_double(&state);\n    y = curand_uniform_double(&state);\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, curandState *const rngStates, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Initialise the RNG\n    curandState localState = rngStates[tid];\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        Real x;\n        Real y;\n        getPoint(x, y, localState);\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims,\n                               unsigned int device,\n                               unsigned int threadBlockSize,\n                               unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    cudaError_t               cudaResult = cudaSuccess;\n    struct cudaDeviceProp     deviceProperties;\n    struct cudaFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = cudaSetDevice(m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, initRNG);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for RNG states\n    curandState *d_rngStates = 0;\n    cudaResult               = cudaMalloc((void **)&d_rngStates, grid.x * block.x * sizeof(curandState));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for RNG states: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, m_seed);\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_rngStates, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), cudaMemcpyDeviceToHost);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_rngStates) {\n        cudaFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_results) {\n        cudaFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand_kernel.h>\n\nusing std::string;\nusing std::vector;\n\n// RNG init kernel\n__global__ void initRNG(hiprandState *const rngStates, const unsigned int seed)\n{\n    // Determine thread ID\n    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Initialise the RNG\n    hiprand_init(seed, tid, 0, &rngStates[tid]);\n}\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n__device__ inline void getPoint(float &x, float &y, hiprandState &state)\n{\n    x = hiprand_uniform(&state);\n    y = hiprand_uniform(&state);\n}\n__device__ inline void getPoint(double &x, double &y, hiprandState &state)\n{\n    x = hiprand_uniform_double(&state);\n    y = hiprand_uniform_double(&state);\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, hiprandState *const rngStates, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Initialise the RNG\n    hiprandState localState = rngStates[tid];\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step) {\n        Real x;\n        Real y;\n        getPoint(x, y, localState);\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims,\n                               unsigned int device,\n                               unsigned int threadBlockSize,\n                               unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    hipError_t               cudaResult = hipSuccess;\n    struct hipDeviceProp_t     deviceProperties;\n    struct hipFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = hipGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = hipSetDevice(m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get initRNG function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(initRNG));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for initRNG kernel\");\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(computeValue<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for RNG states\n    hiprandState *d_rngStates = 0;\n    cudaResult               = hipMalloc((void **)&d_rngStates, grid.x * block.x * sizeof(hiprandState));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for RNG states: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = hipMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Initialise RNG\n    initRNG<<<grid, block>>>(d_rngStates, m_seed);\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_rngStates, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = hipMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), hipMemcpyDeviceToHost);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_rngStates) {\n        hipFree(d_rngStates);\n        d_rngStates = 0;\n    }\n\n    if (d_results) {\n        hipFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/MC_EstimatePiInlineP/src/piestimator.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <helper_cuda.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"convolutionTexture_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU-specific defines\n////////////////////////////////////////////////////////////////////////////////\n// Maps to a single instruction on G8x / G9x / G10x\n#define IMAD(a, b, c) (__mul24((a), (b)) + (c))\n\n// Use unrolled innermost convolution loop\n#define UNROLL_INNER 1\n\n// Round a / b to nearest higher integer value\ninline int iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Align a to nearest higher multiple of b\ninline int iAlignUp(int a, int b) { return (a % b != 0) ? (a - a % b + b) : a; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Convolution kernel and input array storage\n////////////////////////////////////////////////////////////////////////////////\n__constant__ float c_Kernel[KERNEL_LENGTH];\n\nextern \"C\" void setConvolutionKernel(float *h_Kernel)\n{\n    cudaMemcpyToSymbol(c_Kernel, h_Kernel, KERNEL_LENGTH * sizeof(float));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Loop unrolling templates, needed for best performance\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int i> __device__ float convolutionRow(float x, float y, cudaTextureObject_t texSrc)\n{\n    return tex2D<float>(texSrc, x + (float)(KERNEL_RADIUS - i), y) * c_Kernel[i] + convolutionRow<i - 1>(x, y, texSrc);\n}\n\ntemplate <> __device__ float convolutionRow<-1>(float x, float y, cudaTextureObject_t texSrc) { return 0; }\n\ntemplate <int i> __device__ float convolutionColumn(float x, float y, cudaTextureObject_t texSrc)\n{\n    return tex2D<float>(texSrc, x, y + (float)(KERNEL_RADIUS - i)) * c_Kernel[i]\n         + convolutionColumn<i - 1>(x, y, texSrc);\n}\n\ntemplate <> __device__ float convolutionColumn<-1>(float x, float y, cudaTextureObject_t texSrc) { return 0; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Row convolution filter\n////////////////////////////////////////////////////////////////////////////////\n__global__ void convolutionRowsKernel(float *d_Dst, int imageW, int imageH, cudaTextureObject_t texSrc)\n{\n    const int   ix = IMAD(blockDim.x, blockIdx.x, threadIdx.x);\n    const int   iy = IMAD(blockDim.y, blockIdx.y, threadIdx.y);\n    const float x  = (float)ix + 0.5f;\n    const float y  = (float)iy + 0.5f;\n\n    if (ix >= imageW || iy >= imageH) {\n        return;\n    }\n\n    float sum = 0;\n\n#if (UNROLL_INNER)\n    sum = convolutionRow<2 * KERNEL_RADIUS>(x, y, texSrc);\n#else\n\n    for (int k = -KERNEL_RADIUS; k <= KERNEL_RADIUS; k++) {\n        sum += tex2D<float>(texSrc, x + (float)k, y) * c_Kernel[KERNEL_RADIUS - k];\n    }\n\n#endif\n\n    d_Dst[IMAD(iy, imageW, ix)] = sum;\n}\n\nextern \"C\" void convolutionRowsGPU(float *d_Dst, cudaArray *a_Src, int imageW, int imageH, cudaTextureObject_t texSrc)\n{\n    dim3 threads(16, 12);\n    dim3 blocks(iDivUp(imageW, threads.x), iDivUp(imageH, threads.y));\n\n    convolutionRowsKernel<<<blocks, threads>>>(d_Dst, imageW, imageH, texSrc);\n    getLastCudaError(\"convolutionRowsKernel() execution failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Column convolution filter\n////////////////////////////////////////////////////////////////////////////////\n__global__ void convolutionColumnsKernel(float *d_Dst, int imageW, int imageH, cudaTextureObject_t texSrc)\n{\n    const int   ix = IMAD(blockDim.x, blockIdx.x, threadIdx.x);\n    const int   iy = IMAD(blockDim.y, blockIdx.y, threadIdx.y);\n    const float x  = (float)ix + 0.5f;\n    const float y  = (float)iy + 0.5f;\n\n    if (ix >= imageW || iy >= imageH) {\n        return;\n    }\n\n    float sum = 0;\n\n#if (UNROLL_INNER)\n    sum = convolutionColumn<2 * KERNEL_RADIUS>(x, y, texSrc);\n#else\n\n    for (int k = -KERNEL_RADIUS; k <= KERNEL_RADIUS; k++) {\n        sum += tex2D<float>(texSrc, x, y + (float)k) * c_Kernel[KERNEL_RADIUS - k];\n    }\n\n#endif\n\n    d_Dst[IMAD(iy, imageW, ix)] = sum;\n}\n\nextern \"C\" void\nconvolutionColumnsGPU(float *d_Dst, cudaArray *a_Src, int imageW, int imageH, cudaTextureObject_t texSrc)\n{\n    dim3 threads(16, 12);\n    dim3 blocks(iDivUp(imageW, threads.x), iDivUp(imageH, threads.y));\n\n    convolutionColumnsKernel<<<blocks, threads>>>(d_Dst, imageW, imageH, texSrc);\n    getLastCudaError(\"convolutionColumnsKernel() execution failed\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"convolutionTexture_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// GPU-specific defines\n////////////////////////////////////////////////////////////////////////////////\n// Maps to a single instruction on G8x / G9x / G10x\n#define IMAD(a, b, c) (__mul24((a), (b)) + (c))\n\n// Use unrolled innermost convolution loop\n#define UNROLL_INNER 1\n\n// Round a / b to nearest higher integer value\ninline int iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }\n\n// Align a to nearest higher multiple of b\ninline int iAlignUp(int a, int b) { return (a % b != 0) ? (a - a % b + b) : a; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Convolution kernel and input array storage\n////////////////////////////////////////////////////////////////////////////////\n__constant__ float c_Kernel[KERNEL_LENGTH];\n\nextern \"C\" void setConvolutionKernel(float *h_Kernel)\n{\n    hipMemcpyToSymbol(HIP_SYMBOL(c_Kernel), h_Kernel, KERNEL_LENGTH * sizeof(float));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Loop unrolling templates, needed for best performance\n////////////////////////////////////////////////////////////////////////////////\ntemplate <int i> __device__ float convolutionRow(float x, float y, hipTextureObject_t texSrc)\n{\n    return tex2D<float>(texSrc, x + (float)(KERNEL_RADIUS - i), y) * c_Kernel[i] + convolutionRow<i - 1>(x, y, texSrc);\n}\n\ntemplate <> __device__ float convolutionRow<-1>(float x, float y, hipTextureObject_t texSrc) { return 0; }\n\ntemplate <int i> __device__ float convolutionColumn(float x, float y, hipTextureObject_t texSrc)\n{\n    return tex2D<float>(texSrc, x, y + (float)(KERNEL_RADIUS - i)) * c_Kernel[i]\n         + convolutionColumn<i - 1>(x, y, texSrc);\n}\n\ntemplate <> __device__ float convolutionColumn<-1>(float x, float y, hipTextureObject_t texSrc) { return 0; }\n\n////////////////////////////////////////////////////////////////////////////////\n// Row convolution filter\n////////////////////////////////////////////////////////////////////////////////\n__global__ void convolutionRowsKernel(float *d_Dst, int imageW, int imageH, hipTextureObject_t texSrc)\n{\n    const int   ix = IMAD(blockDim.x, blockIdx.x, threadIdx.x);\n    const int   iy = IMAD(blockDim.y, blockIdx.y, threadIdx.y);\n    const float x  = (float)ix + 0.5f;\n    const float y  = (float)iy + 0.5f;\n\n    if (ix >= imageW || iy >= imageH) {\n        return;\n    }\n\n    float sum = 0;\n\n#if (UNROLL_INNER)\n    sum = convolutionRow<2 * KERNEL_RADIUS>(x, y, texSrc);\n#else\n\n    for (int k = -KERNEL_RADIUS; k <= KERNEL_RADIUS; k++) {\n        sum += tex2D<float>(texSrc, x + (float)k, y) * c_Kernel[KERNEL_RADIUS - k];\n    }\n\n#endif\n\n    d_Dst[IMAD(iy, imageW, ix)] = sum;\n}\n\nextern \"C\" void convolutionRowsGPU(float *d_Dst, hipArray *a_Src, int imageW, int imageH, hipTextureObject_t texSrc)\n{\n    dim3 threads(16, 12);\n    dim3 blocks(iDivUp(imageW, threads.x), iDivUp(imageH, threads.y));\n\n    convolutionRowsKernel<<<blocks, threads>>>(d_Dst, imageW, imageH, texSrc);\n    getLastCudaError(\"convolutionRowsKernel() execution failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Column convolution filter\n////////////////////////////////////////////////////////////////////////////////\n__global__ void convolutionColumnsKernel(float *d_Dst, int imageW, int imageH, hipTextureObject_t texSrc)\n{\n    const int   ix = IMAD(blockDim.x, blockIdx.x, threadIdx.x);\n    const int   iy = IMAD(blockDim.y, blockIdx.y, threadIdx.y);\n    const float x  = (float)ix + 0.5f;\n    const float y  = (float)iy + 0.5f;\n\n    if (ix >= imageW || iy >= imageH) {\n        return;\n    }\n\n    float sum = 0;\n\n#if (UNROLL_INNER)\n    sum = convolutionColumn<2 * KERNEL_RADIUS>(x, y, texSrc);\n#else\n\n    for (int k = -KERNEL_RADIUS; k <= KERNEL_RADIUS; k++) {\n        sum += tex2D<float>(texSrc, x, y + (float)k) * c_Kernel[KERNEL_RADIUS - k];\n    }\n\n#endif\n\n    d_Dst[IMAD(iy, imageW, ix)] = sum;\n}\n\nextern \"C\" void\nconvolutionColumnsGPU(float *d_Dst, hipArray *a_Src, int imageW, int imageH, hipTextureObject_t texSrc)\n{\n    dim3 threads(16, 12);\n    dim3 blocks(iDivUp(imageW, threads.x), iDivUp(imageH, threads.y));\n\n    convolutionColumnsKernel<<<blocks, threads>>>(d_Dst, imageW, imageH, texSrc);\n    getLastCudaError(\"convolutionColumnsKernel() execution failed\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/convolutionTexture/convolutionTexture.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates stream ordered memory allocation on a GPU using\n * cudaMallocAsync and cudaMemPool family of APIs.\n *\n * basicStreamOrderedAllocation(): demonstrates stream ordered allocation using\n * cudaMallocAsync/cudaFreeAsync APIs with default settings.\n *\n * streamOrderedAllocationPostSync(): demonstrates if there's a synchronization\n * in between allocations, then setting the release threshold on the pool will\n * make sure the synchronize will not free memory back to the OS.\n */\n\n// System includes\n#include <assert.h>\n#include <climits>\n#include <stdio.h>\n\n// CUDA runtime\n#include <cuda_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <helper_cuda.h>\n#include <helper_functions.h>\n\n#define MAX_ITER 20\n\n/* Add two vectors on the GPU */\n__global__ void vectorAddGPU(const float *a, const float *b, float *c, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        c[idx] = a[idx] + b[idx];\n    }\n}\n\nint basicStreamOrderedAllocation(const int dev, const int nelem, const float *a, const float *b, float *c)\n{\n    float *d_a, *d_b, *d_c; // Device buffers\n    float  errorNorm, refNorm, ref, diff;\n    size_t bytes = nelem * sizeof(float);\n\n    cudaStream_t stream;\n    printf(\"Starting basicStreamOrderedAllocation()\\n\");\n    checkCudaErrors(cudaSetDevice(dev));\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    checkCudaErrors(cudaMallocAsync(&d_a, bytes, stream));\n    checkCudaErrors(cudaMallocAsync(&d_b, bytes, stream));\n    checkCudaErrors(cudaMallocAsync(&d_c, bytes, stream));\n    checkCudaErrors(cudaMemcpyAsync(d_a, a, bytes, cudaMemcpyHostToDevice, stream));\n    checkCudaErrors(cudaMemcpyAsync(d_b, b, bytes, cudaMemcpyHostToDevice, stream));\n\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n    vectorAddGPU<<<grid, block, 0, stream>>>(d_a, d_b, d_c, nelem);\n\n    checkCudaErrors(cudaFreeAsync(d_a, stream));\n    checkCudaErrors(cudaFreeAsync(d_b, stream));\n    checkCudaErrors(cudaMemcpyAsync(c, d_c, bytes, cudaMemcpyDeviceToHost, stream));\n    checkCudaErrors(cudaFreeAsync(d_c, stream));\n    checkCudaErrors(cudaStreamSynchronize(stream));\n\n    /* Compare the results */\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (int n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n    if (errorNorm / refNorm < 1.e-6f)\n        printf(\"basicStreamOrderedAllocation PASSED\\n\");\n\n    checkCudaErrors(cudaStreamDestroy(stream));\n\n    return errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n\n// streamOrderedAllocationPostSync(): demonstrates If the application wants the\n// memory to persist in the pool beyond synchronization, then it sets the\n// release threshold on the pool. This way, when the application reaches the\n// \"steady state\", it is no longer allocating/freeing memory from the OS.\nint streamOrderedAllocationPostSync(const int dev, const int nelem, const float *a, const float *b, float *c)\n{\n    float *d_a, *d_b, *d_c; // Device buffers\n    float  errorNorm, refNorm, ref, diff;\n    size_t bytes = nelem * sizeof(float);\n\n    cudaStream_t  stream;\n    cudaMemPool_t memPool;\n    cudaEvent_t   start, end;\n    printf(\"Starting streamOrderedAllocationPostSync()\\n\");\n    checkCudaErrors(cudaSetDevice(dev));\n    checkCudaErrors(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n    checkCudaErrors(cudaEventCreate(&start));\n    checkCudaErrors(cudaEventCreate(&end));\n\n    checkCudaErrors(cudaDeviceGetDefaultMemPool(&memPool, dev));\n    uint64_t thresholdVal = ULONG_MAX;\n    // set high release threshold on the default pool so that cudaFreeAsync will\n    // not actually release memory to the system. By default, the release\n    // threshold for a memory pool is set to zero. This implies that the CUDA\n    // driver is allowed to release a memory chunk back to the system as long as\n    // it does not contain any active suballocations.\n    checkCudaErrors(cudaMemPoolSetAttribute(memPool, cudaMemPoolAttrReleaseThreshold, (void *)&thresholdVal));\n\n    // Record the start event\n    checkCudaErrors(cudaEventRecord(start, stream));\n    for (int i = 0; i < MAX_ITER; i++) {\n        checkCudaErrors(cudaMallocAsync(&d_a, bytes, stream));\n        checkCudaErrors(cudaMallocAsync(&d_b, bytes, stream));\n        checkCudaErrors(cudaMallocAsync(&d_c, bytes, stream));\n        checkCudaErrors(cudaMemcpyAsync(d_a, a, bytes, cudaMemcpyHostToDevice, stream));\n        checkCudaErrors(cudaMemcpyAsync(d_b, b, bytes, cudaMemcpyHostToDevice, stream));\n\n        dim3 block(256);\n        dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n        vectorAddGPU<<<grid, block, 0, stream>>>(d_a, d_b, d_c, nelem);\n\n        checkCudaErrors(cudaFreeAsync(d_a, stream));\n        checkCudaErrors(cudaFreeAsync(d_b, stream));\n        checkCudaErrors(cudaMemcpyAsync(c, d_c, bytes, cudaMemcpyDeviceToHost, stream));\n        checkCudaErrors(cudaFreeAsync(d_c, stream));\n        checkCudaErrors(cudaStreamSynchronize(stream));\n    }\n    checkCudaErrors(cudaEventRecord(end, stream));\n    // Wait for the end event to complete\n    checkCudaErrors(cudaEventSynchronize(end));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(cudaEventElapsedTime(&msecTotal, start, end));\n    printf(\"Total elapsed time = %f ms over %d iterations\\n\", msecTotal, MAX_ITER);\n\n    /* Compare the results */\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (int n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n    if (errorNorm / refNorm < 1.e-6f)\n        printf(\"streamOrderedAllocationPostSync PASSED\\n\");\n\n    checkCudaErrors(cudaStreamDestroy(stream));\n\n    return errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n\nint main(int argc, char **argv)\n{\n    int    nelem;\n    int    dev = 0; // use default device 0\n    size_t bytes;\n    float *a, *b, *c; // Host\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printf(\"Usage:  streamOrderedAllocation [OPTION]\\n\\n\");\n        printf(\"Options:\\n\");\n        printf(\"  --device=[device #]  Specify the device to be used\\n\");\n        return EXIT_SUCCESS;\n    }\n\n    dev = findCudaDevice(argc, (const char **)argv);\n\n    int isMemPoolSupported = 0;\n    checkCudaErrors(cudaDeviceGetAttribute(&isMemPoolSupported, cudaDevAttrMemoryPoolsSupported, dev));\n    if (!isMemPoolSupported) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Allocate CPU memory.\n    nelem = 1048576;\n    bytes = nelem * sizeof(float);\n\n    a = (float *)malloc(bytes);\n    b = (float *)malloc(bytes);\n    c = (float *)malloc(bytes);\n    /* Initialize the vectors. */\n    for (int n = 0; n < nelem; n++) {\n        a[n] = rand() / (float)RAND_MAX;\n        b[n] = rand() / (float)RAND_MAX;\n    }\n\n    int ret1 = basicStreamOrderedAllocation(dev, nelem, a, b, c);\n    int ret2 = streamOrderedAllocationPostSync(dev, nelem, a, b, c);\n\n    /* Memory clean up */\n    free(a);\n    free(b);\n    free(c);\n\n    return ((ret1 == EXIT_SUCCESS && ret2 == EXIT_SUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This sample demonstrates stream ordered memory allocation on a GPU using\n * cudaMallocAsync and cudaMemPool family of APIs.\n *\n * basicStreamOrderedAllocation(): demonstrates stream ordered allocation using\n * cudaMallocAsync/cudaFreeAsync APIs with default settings.\n *\n * streamOrderedAllocationPostSync(): demonstrates if there's a synchronization\n * in between allocations, then setting the release threshold on the pool will\n * make sure the synchronize will not free memory back to the OS.\n */\n\n// System includes\n#include <assert.h>\n#include <climits>\n#include <stdio.h>\n\n// CUDA runtime\n#include <hip/hip_runtime.h>\n\n// helper functions and utilities to work with CUDA\n#include <hip/hip_runtime_api.h>\n#include <helper_functions.h>\n\n#define MAX_ITER 20\n\n/* Add two vectors on the GPU */\n__global__ void vectorAddGPU(const float *a, const float *b, float *c, int N)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx < N) {\n        c[idx] = a[idx] + b[idx];\n    }\n}\n\nint basicStreamOrderedAllocation(const int dev, const int nelem, const float *a, const float *b, float *c)\n{\n    float *d_a, *d_b, *d_c; // Device buffers\n    float  errorNorm, refNorm, ref, diff;\n    size_t bytes = nelem * sizeof(float);\n\n    hipStream_t stream;\n    printf(\"Starting basicStreamOrderedAllocation()\\n\");\n    checkCudaErrors(hipSetDevice(dev));\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    checkCudaErrors(hipMallocAsync(&d_a, bytes, stream));\n    checkCudaErrors(hipMallocAsync(&d_b, bytes, stream));\n    checkCudaErrors(hipMallocAsync(&d_c, bytes, stream));\n    checkCudaErrors(hipMemcpyAsync(d_a, a, bytes, hipMemcpyHostToDevice, stream));\n    checkCudaErrors(hipMemcpyAsync(d_b, b, bytes, hipMemcpyHostToDevice, stream));\n\n    dim3 block(256);\n    dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n    vectorAddGPU<<<grid, block, 0, stream>>>(d_a, d_b, d_c, nelem);\n\n    checkCudaErrors(hipFreeAsync(d_a, stream));\n    checkCudaErrors(hipFreeAsync(d_b, stream));\n    checkCudaErrors(hipMemcpyAsync(c, d_c, bytes, hipMemcpyDeviceToHost, stream));\n    checkCudaErrors(hipFreeAsync(d_c, stream));\n    checkCudaErrors(hipStreamSynchronize(stream));\n\n    /* Compare the results */\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (int n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n    if (errorNorm / refNorm < 1.e-6f)\n        printf(\"basicStreamOrderedAllocation PASSED\\n\");\n\n    checkCudaErrors(hipStreamDestroy(stream));\n\n    return errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n\n// streamOrderedAllocationPostSync(): demonstrates If the application wants the\n// memory to persist in the pool beyond synchronization, then it sets the\n// release threshold on the pool. This way, when the application reaches the\n// \"steady state\", it is no longer allocating/freeing memory from the OS.\nint streamOrderedAllocationPostSync(const int dev, const int nelem, const float *a, const float *b, float *c)\n{\n    float *d_a, *d_b, *d_c; // Device buffers\n    float  errorNorm, refNorm, ref, diff;\n    size_t bytes = nelem * sizeof(float);\n\n    hipStream_t  stream;\n    hipMemPool_t memPool;\n    hipEvent_t   start, end;\n    printf(\"Starting streamOrderedAllocationPostSync()\\n\");\n    checkCudaErrors(hipSetDevice(dev));\n    checkCudaErrors(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n    checkCudaErrors(hipEventCreate(&start));\n    checkCudaErrors(hipEventCreate(&end));\n\n    checkCudaErrors(hipDeviceGetDefaultMemPool(&memPool, dev));\n    uint64_t thresholdVal = ULONG_MAX;\n    // set high release threshold on the default pool so that cudaFreeAsync will\n    // not actually release memory to the system. By default, the release\n    // threshold for a memory pool is set to zero. This implies that the CUDA\n    // driver is allowed to release a memory chunk back to the system as long as\n    // it does not contain any active suballocations.\n    checkCudaErrors(hipMemPoolSetAttribute(memPool, hipMemPoolAttrReleaseThreshold, (void *)&thresholdVal));\n\n    // Record the start event\n    checkCudaErrors(hipEventRecord(start, stream));\n    for (int i = 0; i < MAX_ITER; i++) {\n        checkCudaErrors(hipMallocAsync(&d_a, bytes, stream));\n        checkCudaErrors(hipMallocAsync(&d_b, bytes, stream));\n        checkCudaErrors(hipMallocAsync(&d_c, bytes, stream));\n        checkCudaErrors(hipMemcpyAsync(d_a, a, bytes, hipMemcpyHostToDevice, stream));\n        checkCudaErrors(hipMemcpyAsync(d_b, b, bytes, hipMemcpyHostToDevice, stream));\n\n        dim3 block(256);\n        dim3 grid((unsigned int)ceil(nelem / (float)block.x));\n        vectorAddGPU<<<grid, block, 0, stream>>>(d_a, d_b, d_c, nelem);\n\n        checkCudaErrors(hipFreeAsync(d_a, stream));\n        checkCudaErrors(hipFreeAsync(d_b, stream));\n        checkCudaErrors(hipMemcpyAsync(c, d_c, bytes, hipMemcpyDeviceToHost, stream));\n        checkCudaErrors(hipFreeAsync(d_c, stream));\n        checkCudaErrors(hipStreamSynchronize(stream));\n    }\n    checkCudaErrors(hipEventRecord(end, stream));\n    // Wait for the end event to complete\n    checkCudaErrors(hipEventSynchronize(end));\n\n    float msecTotal = 0.0f;\n    checkCudaErrors(hipEventElapsedTime(&msecTotal, start, end));\n    printf(\"Total elapsed time = %f ms over %d iterations\\n\", msecTotal, MAX_ITER);\n\n    /* Compare the results */\n    printf(\"> Checking the results from vectorAddGPU() ...\\n\");\n    errorNorm = 0.f;\n    refNorm   = 0.f;\n\n    for (int n = 0; n < nelem; n++) {\n        ref  = a[n] + b[n];\n        diff = c[n] - ref;\n        errorNorm += diff * diff;\n        refNorm += ref * ref;\n    }\n\n    errorNorm = (float)sqrt((double)errorNorm);\n    refNorm   = (float)sqrt((double)refNorm);\n    if (errorNorm / refNorm < 1.e-6f)\n        printf(\"streamOrderedAllocationPostSync PASSED\\n\");\n\n    checkCudaErrors(hipStreamDestroy(stream));\n\n    return errorNorm / refNorm < 1.e-6f ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n\nint main(int argc, char **argv)\n{\n    int    nelem;\n    int    dev = 0; // use default device 0\n    size_t bytes;\n    float *a, *b, *c; // Host\n\n    if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n        printf(\"Usage:  streamOrderedAllocation [OPTION]\\n\\n\");\n        printf(\"Options:\\n\");\n        printf(\"  --device=[device #]  Specify the device to be used\\n\");\n        return EXIT_SUCCESS;\n    }\n\n    dev = findCudaDevice(argc, (const char **)argv);\n\n    int isMemPoolSupported = 0;\n    checkCudaErrors(hipDeviceGetAttribute(&isMemPoolSupported, hipDeviceAttributeMemoryPoolsSupported, dev));\n    if (!isMemPoolSupported) {\n        printf(\"Waiving execution as device does not support Memory Pools\\n\");\n        exit(EXIT_WAIVED);\n    }\n\n    // Allocate CPU memory.\n    nelem = 1048576;\n    bytes = nelem * sizeof(float);\n\n    a = (float *)malloc(bytes);\n    b = (float *)malloc(bytes);\n    c = (float *)malloc(bytes);\n    /* Initialize the vectors. */\n    for (int n = 0; n < nelem; n++) {\n        a[n] = rand() / (float)RAND_MAX;\n        b[n] = rand() / (float)RAND_MAX;\n    }\n\n    int ret1 = basicStreamOrderedAllocation(dev, nelem, a, b, c);\n    int ret2 = streamOrderedAllocationPostSync(dev, nelem, a, b, c);\n\n    /* Memory clean up */\n    free(a);\n    free(b);\n    free(c);\n\n    return ((ret1 == EXIT_SUCCESS && ret2 == EXIT_SUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE);\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/streamOrderedAllocation/streamOrderedAllocation.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <assert.h>\n#include <cooperative_groups.h>\n#include <helper_cuda.h>\n\nnamespace cg = cooperative_groups;\n#include \"convolutionSeparable_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Convolution kernel storage\n////////////////////////////////////////////////////////////////////////////////\n__constant__ float c_Kernel[KERNEL_LENGTH];\n\nextern \"C\" void setConvolutionKernel(float *h_Kernel)\n{\n    cudaMemcpyToSymbol(c_Kernel, h_Kernel, KERNEL_LENGTH * sizeof(float));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Row convolution filter\n////////////////////////////////////////////////////////////////////////////////\n#define ROWS_BLOCKDIM_X   16\n#define ROWS_BLOCKDIM_Y   4\n#define ROWS_RESULT_STEPS 8\n#define ROWS_HALO_STEPS   1\n\n__global__ void convolutionRowsKernel(float *d_Dst, float *d_Src, int imageW, int imageH, int pitch)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float s_Data[ROWS_BLOCKDIM_Y][(ROWS_RESULT_STEPS + 2 * ROWS_HALO_STEPS) * ROWS_BLOCKDIM_X];\n\n    // Offset to the left halo edge\n    const int baseX = (blockIdx.x * ROWS_RESULT_STEPS - ROWS_HALO_STEPS) * ROWS_BLOCKDIM_X + threadIdx.x;\n    const int baseY = blockIdx.y * ROWS_BLOCKDIM_Y + threadIdx.y;\n\n    d_Src += baseY * pitch + baseX;\n    d_Dst += baseY * pitch + baseX;\n\n// Load main data\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] = d_Src[i * ROWS_BLOCKDIM_X];\n    }\n\n// Load left halo\n#pragma unroll\n\n    for (int i = 0; i < ROWS_HALO_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] =\n            (baseX >= -i * ROWS_BLOCKDIM_X) ? d_Src[i * ROWS_BLOCKDIM_X] : 0;\n    }\n\n// Load right halo\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS + ROWS_HALO_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] =\n            (imageW - baseX > i * ROWS_BLOCKDIM_X) ? d_Src[i * ROWS_BLOCKDIM_X] : 0;\n    }\n\n    // Compute and store results\n    cg::sync(cta);\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i++) {\n        float sum = 0;\n\n#pragma unroll\n\n        for (int j = -KERNEL_RADIUS; j <= KERNEL_RADIUS; j++) {\n            sum += c_Kernel[KERNEL_RADIUS - j] * s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X + j];\n        }\n\n        d_Dst[i * ROWS_BLOCKDIM_X] = sum;\n    }\n}\n\nextern \"C\" void convolutionRowsGPU(float *d_Dst, float *d_Src, int imageW, int imageH)\n{\n    assert(ROWS_BLOCKDIM_X * ROWS_HALO_STEPS >= KERNEL_RADIUS);\n    assert(imageW % (ROWS_RESULT_STEPS * ROWS_BLOCKDIM_X) == 0);\n    assert(imageH % ROWS_BLOCKDIM_Y == 0);\n\n    dim3 blocks(imageW / (ROWS_RESULT_STEPS * ROWS_BLOCKDIM_X), imageH / ROWS_BLOCKDIM_Y);\n    dim3 threads(ROWS_BLOCKDIM_X, ROWS_BLOCKDIM_Y);\n\n    convolutionRowsKernel<<<blocks, threads>>>(d_Dst, d_Src, imageW, imageH, imageW);\n    getLastCudaError(\"convolutionRowsKernel() execution failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Column convolution filter\n////////////////////////////////////////////////////////////////////////////////\n#define COLUMNS_BLOCKDIM_X   16\n#define COLUMNS_BLOCKDIM_Y   8\n#define COLUMNS_RESULT_STEPS 8\n#define COLUMNS_HALO_STEPS   1\n\n__global__ void convolutionColumnsKernel(float *d_Dst, float *d_Src, int imageW, int imageH, int pitch)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float s_Data[COLUMNS_BLOCKDIM_X]\n                           [(COLUMNS_RESULT_STEPS + 2 * COLUMNS_HALO_STEPS) * COLUMNS_BLOCKDIM_Y + 1];\n\n    // Offset to the upper halo edge\n    const int baseX = blockIdx.x * COLUMNS_BLOCKDIM_X + threadIdx.x;\n    const int baseY = (blockIdx.y * COLUMNS_RESULT_STEPS - COLUMNS_HALO_STEPS) * COLUMNS_BLOCKDIM_Y + threadIdx.y;\n    d_Src += baseY * pitch + baseX;\n    d_Dst += baseY * pitch + baseX;\n\n// Main data\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS; i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS; i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] = d_Src[i * COLUMNS_BLOCKDIM_Y * pitch];\n    }\n\n// Upper halo\n#pragma unroll\n\n    for (int i = 0; i < COLUMNS_HALO_STEPS; i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] =\n            (baseY >= -i * COLUMNS_BLOCKDIM_Y) ? d_Src[i * COLUMNS_BLOCKDIM_Y * pitch] : 0;\n    }\n\n// Lower halo\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS;\n         i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS + COLUMNS_HALO_STEPS;\n         i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] =\n            (imageH - baseY > i * COLUMNS_BLOCKDIM_Y) ? d_Src[i * COLUMNS_BLOCKDIM_Y * pitch] : 0;\n    }\n\n    // Compute and store results\n    cg::sync(cta);\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS; i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS; i++) {\n        float sum = 0;\n#pragma unroll\n\n        for (int j = -KERNEL_RADIUS; j <= KERNEL_RADIUS; j++) {\n            sum += c_Kernel[KERNEL_RADIUS - j] * s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y + j];\n        }\n\n        d_Dst[i * COLUMNS_BLOCKDIM_Y * pitch] = sum;\n    }\n}\n\nextern \"C\" void convolutionColumnsGPU(float *d_Dst, float *d_Src, int imageW, int imageH)\n{\n    assert(COLUMNS_BLOCKDIM_Y * COLUMNS_HALO_STEPS >= KERNEL_RADIUS);\n    assert(imageW % COLUMNS_BLOCKDIM_X == 0);\n    assert(imageH % (COLUMNS_RESULT_STEPS * COLUMNS_BLOCKDIM_Y) == 0);\n\n    dim3 blocks(imageW / COLUMNS_BLOCKDIM_X, imageH / (COLUMNS_RESULT_STEPS * COLUMNS_BLOCKDIM_Y));\n    dim3 threads(COLUMNS_BLOCKDIM_X, COLUMNS_BLOCKDIM_Y);\n\n    convolutionColumnsKernel<<<blocks, threads>>>(d_Dst, d_Src, imageW, imageH, imageW);\n    getLastCudaError(\"convolutionColumnsKernel() execution failed\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime_api.h>\n\nnamespace cg = cooperative_groups;\n#include \"convolutionSeparable_common.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// Convolution kernel storage\n////////////////////////////////////////////////////////////////////////////////\n__constant__ float c_Kernel[KERNEL_LENGTH];\n\nextern \"C\" void setConvolutionKernel(float *h_Kernel)\n{\n    hipMemcpyToSymbol(HIP_SYMBOL(c_Kernel), h_Kernel, KERNEL_LENGTH * sizeof(float));\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Row convolution filter\n////////////////////////////////////////////////////////////////////////////////\n#define ROWS_BLOCKDIM_X   16\n#define ROWS_BLOCKDIM_Y   4\n#define ROWS_RESULT_STEPS 8\n#define ROWS_HALO_STEPS   1\n\n__global__ void convolutionRowsKernel(float *d_Dst, float *d_Src, int imageW, int imageH, int pitch)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float s_Data[ROWS_BLOCKDIM_Y][(ROWS_RESULT_STEPS + 2 * ROWS_HALO_STEPS) * ROWS_BLOCKDIM_X];\n\n    // Offset to the left halo edge\n    const int baseX = (blockIdx.x * ROWS_RESULT_STEPS - ROWS_HALO_STEPS) * ROWS_BLOCKDIM_X + threadIdx.x;\n    const int baseY = blockIdx.y * ROWS_BLOCKDIM_Y + threadIdx.y;\n\n    d_Src += baseY * pitch + baseX;\n    d_Dst += baseY * pitch + baseX;\n\n// Load main data\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] = d_Src[i * ROWS_BLOCKDIM_X];\n    }\n\n// Load left halo\n#pragma unroll\n\n    for (int i = 0; i < ROWS_HALO_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] =\n            (baseX >= -i * ROWS_BLOCKDIM_X) ? d_Src[i * ROWS_BLOCKDIM_X] : 0;\n    }\n\n// Load right halo\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS + ROWS_HALO_STEPS; i++) {\n        s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X] =\n            (imageW - baseX > i * ROWS_BLOCKDIM_X) ? d_Src[i * ROWS_BLOCKDIM_X] : 0;\n    }\n\n    // Compute and store results\n    cg::sync(cta);\n#pragma unroll\n\n    for (int i = ROWS_HALO_STEPS; i < ROWS_HALO_STEPS + ROWS_RESULT_STEPS; i++) {\n        float sum = 0;\n\n#pragma unroll\n\n        for (int j = -KERNEL_RADIUS; j <= KERNEL_RADIUS; j++) {\n            sum += c_Kernel[KERNEL_RADIUS - j] * s_Data[threadIdx.y][threadIdx.x + i * ROWS_BLOCKDIM_X + j];\n        }\n\n        d_Dst[i * ROWS_BLOCKDIM_X] = sum;\n    }\n}\n\nextern \"C\" void convolutionRowsGPU(float *d_Dst, float *d_Src, int imageW, int imageH)\n{\n    assert(ROWS_BLOCKDIM_X * ROWS_HALO_STEPS >= KERNEL_RADIUS);\n    assert(imageW % (ROWS_RESULT_STEPS * ROWS_BLOCKDIM_X) == 0);\n    assert(imageH % ROWS_BLOCKDIM_Y == 0);\n\n    dim3 blocks(imageW / (ROWS_RESULT_STEPS * ROWS_BLOCKDIM_X), imageH / ROWS_BLOCKDIM_Y);\n    dim3 threads(ROWS_BLOCKDIM_X, ROWS_BLOCKDIM_Y);\n\n    convolutionRowsKernel<<<blocks, threads>>>(d_Dst, d_Src, imageW, imageH, imageW);\n    getLastCudaError(\"convolutionRowsKernel() execution failed\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Column convolution filter\n////////////////////////////////////////////////////////////////////////////////\n#define COLUMNS_BLOCKDIM_X   16\n#define COLUMNS_BLOCKDIM_Y   8\n#define COLUMNS_RESULT_STEPS 8\n#define COLUMNS_HALO_STEPS   1\n\n__global__ void convolutionColumnsKernel(float *d_Dst, float *d_Src, int imageW, int imageH, int pitch)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    __shared__ float s_Data[COLUMNS_BLOCKDIM_X]\n                           [(COLUMNS_RESULT_STEPS + 2 * COLUMNS_HALO_STEPS) * COLUMNS_BLOCKDIM_Y + 1];\n\n    // Offset to the upper halo edge\n    const int baseX = blockIdx.x * COLUMNS_BLOCKDIM_X + threadIdx.x;\n    const int baseY = (blockIdx.y * COLUMNS_RESULT_STEPS - COLUMNS_HALO_STEPS) * COLUMNS_BLOCKDIM_Y + threadIdx.y;\n    d_Src += baseY * pitch + baseX;\n    d_Dst += baseY * pitch + baseX;\n\n// Main data\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS; i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS; i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] = d_Src[i * COLUMNS_BLOCKDIM_Y * pitch];\n    }\n\n// Upper halo\n#pragma unroll\n\n    for (int i = 0; i < COLUMNS_HALO_STEPS; i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] =\n            (baseY >= -i * COLUMNS_BLOCKDIM_Y) ? d_Src[i * COLUMNS_BLOCKDIM_Y * pitch] : 0;\n    }\n\n// Lower halo\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS;\n         i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS + COLUMNS_HALO_STEPS;\n         i++) {\n        s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y] =\n            (imageH - baseY > i * COLUMNS_BLOCKDIM_Y) ? d_Src[i * COLUMNS_BLOCKDIM_Y * pitch] : 0;\n    }\n\n    // Compute and store results\n    cg::sync(cta);\n#pragma unroll\n\n    for (int i = COLUMNS_HALO_STEPS; i < COLUMNS_HALO_STEPS + COLUMNS_RESULT_STEPS; i++) {\n        float sum = 0;\n#pragma unroll\n\n        for (int j = -KERNEL_RADIUS; j <= KERNEL_RADIUS; j++) {\n            sum += c_Kernel[KERNEL_RADIUS - j] * s_Data[threadIdx.x][threadIdx.y + i * COLUMNS_BLOCKDIM_Y + j];\n        }\n\n        d_Dst[i * COLUMNS_BLOCKDIM_Y * pitch] = sum;\n    }\n}\n\nextern \"C\" void convolutionColumnsGPU(float *d_Dst, float *d_Src, int imageW, int imageH)\n{\n    assert(COLUMNS_BLOCKDIM_Y * COLUMNS_HALO_STEPS >= KERNEL_RADIUS);\n    assert(imageW % COLUMNS_BLOCKDIM_X == 0);\n    assert(imageH % (COLUMNS_RESULT_STEPS * COLUMNS_BLOCKDIM_Y) == 0);\n\n    dim3 blocks(imageW / COLUMNS_BLOCKDIM_X, imageH / (COLUMNS_RESULT_STEPS * COLUMNS_BLOCKDIM_Y));\n    dim3 threads(COLUMNS_BLOCKDIM_X, COLUMNS_BLOCKDIM_Y);\n\n    convolutionColumnsKernel<<<blocks, threads>>>(d_Dst, d_Src, imageW, imageH, imageW);\n    getLastCudaError(\"convolutionColumnsKernel() execution failed\\n\");\n}\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/convolutionSeparable/convolutionSeparable.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <cooperative_groups.h>\n#include <cuda_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <curand.h>\n\nusing std::string;\nusing std::vector;\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, const Real *const points, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Shift the input/output pointers\n    const Real *pointx = points + tid;\n    const Real *pointy = pointx + numSims;\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step, pointx += step, pointy += step) {\n        Real x       = *pointx;\n        Real y       = *pointy;\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims,\n                               unsigned int device,\n                               unsigned int threadBlockSize,\n                               unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    cudaError_t               cudaResult = cudaSuccess;\n    struct cudaDeviceProp     deviceProperties;\n    struct cudaFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = cudaGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = cudaSetDevice(m_device);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = cudaFuncGetAttributes(&funcAttributes, computeValue<Real>);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for points\n    // Each simulation has two random numbers to give X and Y coordinate\n    Real *d_points = 0;\n    cudaResult     = cudaMalloc((void **)&d_points, 2 * m_numSims * sizeof(Real));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for random numbers: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = cudaMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate random points in unit square\n    curandStatus_t    curandResult;\n    curandGenerator_t prng;\n    curandResult = curandCreateGenerator(&prng, CURAND_RNG_PSEUDO_DEFAULT);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not create pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = curandSetPseudoRandomGeneratorSeed(prng, m_seed);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not set seed for pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = curandGenerateUniform(prng, (float *)d_points, 2 * m_numSims);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = curandGenerateUniformDouble(prng, (double *)d_points, 2 * m_numSims);\n    }\n    else {\n        string msg(\"Could not generate random numbers of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not generate pseudo-random numbers: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = curandDestroyGenerator(prng);\n\n    if (curandResult != CURAND_STATUS_SUCCESS) {\n        string msg(\"Could not destroy pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_points, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = cudaMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), cudaMemcpyDeviceToHost);\n\n    if (cudaResult != cudaSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += cudaGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_points) {\n        cudaFree(d_points);\n        d_points = 0;\n    }\n\n    if (d_results) {\n        cudaFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <hip/hip_cooperative_groups.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n#include <stdexcept>\n#include <string>\n#include <typeinfo>\n#include <vector>\n\n#include \"../inc/piestimator.h\"\n\nnamespace cg = cooperative_groups;\n#include <hiprand/hiprand.h>\n\nusing std::string;\nusing std::vector;\n\n__device__ unsigned int reduce_sum(unsigned int in, cg::thread_block cta)\n{\n    extern __shared__ unsigned int sdata[];\n\n    // Perform first level of reduction:\n    // - Write to shared memory\n    unsigned int ltid = threadIdx.x;\n\n    sdata[ltid] = in;\n    cg::sync(cta);\n\n    // Do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (ltid < s) {\n            sdata[ltid] += sdata[ltid + s];\n        }\n\n        cg::sync(cta);\n    }\n\n    return sdata[0];\n}\n\n// Estimator kernel\ntemplate <typename Real>\n__global__ void computeValue(unsigned int *const results, const Real *const points, const unsigned int numSims)\n{\n    // Handle to thread block group\n    cg::thread_block cta = cg::this_thread_block();\n    // Determine thread ID\n    unsigned int bid  = blockIdx.x;\n    unsigned int tid  = blockIdx.x * blockDim.x + threadIdx.x;\n    unsigned int step = gridDim.x * blockDim.x;\n\n    // Shift the input/output pointers\n    const Real *pointx = points + tid;\n    const Real *pointy = pointx + numSims;\n\n    // Count the number of points which lie inside the unit quarter-circle\n    unsigned int pointsInside = 0;\n\n    for (unsigned int i = tid; i < numSims; i += step, pointx += step, pointy += step) {\n        Real x       = *pointx;\n        Real y       = *pointy;\n        Real l2norm2 = x * x + y * y;\n\n        if (l2norm2 < static_cast<Real>(1)) {\n            pointsInside++;\n        }\n    }\n\n    // Reduce within the block\n    pointsInside = reduce_sum(pointsInside, cta);\n\n    // Store the result\n    if (threadIdx.x == 0) {\n        results[bid] = pointsInside;\n    }\n}\n\ntemplate <typename Real>\nPiEstimator<Real>::PiEstimator(unsigned int numSims,\n                               unsigned int device,\n                               unsigned int threadBlockSize,\n                               unsigned int seed)\n    : m_numSims(numSims)\n    , m_device(device)\n    , m_threadBlockSize(threadBlockSize)\n    , m_seed(seed)\n{\n}\n\ntemplate <typename Real> Real PiEstimator<Real>::operator()()\n{\n    hipError_t               cudaResult = hipSuccess;\n    struct hipDeviceProp_t     deviceProperties;\n    struct hipFuncAttributes funcAttributes;\n\n    // Get device properties\n    cudaResult = hipGetDeviceProperties(&deviceProperties, m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get device properties: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Check precision is valid\n    if (typeid(Real) == typeid(double)\n        && (deviceProperties.major < 1 || (deviceProperties.major == 1 && deviceProperties.minor < 3))) {\n        throw std::runtime_error(\"Device does not have double precision support\");\n    }\n\n    // Attach to GPU\n    cudaResult = hipSetDevice(m_device);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not set CUDA device: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Determine how to divide the work between cores\n    dim3 block;\n    dim3 grid;\n    block.x = m_threadBlockSize;\n    grid.x  = (m_numSims + m_threadBlockSize - 1) / m_threadBlockSize;\n\n    // Aim to launch around ten or more times as many blocks as there\n    // are multiprocessors on the target device.\n    unsigned int blocksPerSM = 10;\n    unsigned int numSMs      = deviceProperties.multiProcessorCount;\n\n    while (grid.x > 2 * blocksPerSM * numSMs) {\n        grid.x >>= 1;\n    }\n\n    // Get computeValue function properties and check the maximum block size\n    cudaResult = hipFuncGetAttributes(&funcAttributes,reinterpret_cast<const void*>(computeValue<Real>));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not get function attributes: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    if (block.x > (unsigned int)funcAttributes.maxThreadsPerBlock) {\n        throw std::runtime_error(\"Block X dimension is too large for computeValue kernel\");\n    }\n\n    // Check the dimensions are valid\n    if (block.x > (unsigned int)deviceProperties.maxThreadsDim[0]) {\n        throw std::runtime_error(\"Block X dimension is too large for device\");\n    }\n\n    if (grid.x > (unsigned int)deviceProperties.maxGridSize[0]) {\n        throw std::runtime_error(\"Grid X dimension is too large for device\");\n    }\n\n    // Allocate memory for points\n    // Each simulation has two random numbers to give X and Y coordinate\n    Real *d_points = 0;\n    cudaResult     = hipMalloc((void **)&d_points, 2 * m_numSims * sizeof(Real));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for random numbers: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Allocate memory for result\n    // Each thread block will produce one result\n    unsigned int *d_results = 0;\n    cudaResult              = hipMalloc((void **)&d_results, grid.x * sizeof(unsigned int));\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not allocate memory on device for partial results: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Generate random points in unit square\n    hiprandStatus_t    curandResult;\n    hiprandGenerator_t prng;\n    curandResult = hiprandCreateGenerator(&prng, HIPRAND_RNG_PSEUDO_DEFAULT);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not create pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = hiprandSetPseudoRandomGeneratorSeed(prng, m_seed);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not set seed for pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    if (typeid(Real) == typeid(float)) {\n        curandResult = hiprandGenerateUniform(prng, (float *)d_points, 2 * m_numSims);\n    }\n    else if (typeid(Real) == typeid(double)) {\n        curandResult = hiprandGenerateUniformDouble(prng, (double *)d_points, 2 * m_numSims);\n    }\n    else {\n        string msg(\"Could not generate random numbers of specified type\");\n        throw std::runtime_error(msg);\n    }\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not generate pseudo-random numbers: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    curandResult = hiprandDestroyGenerator(prng);\n\n    if (curandResult != HIPRAND_STATUS_SUCCESS) {\n        string msg(\"Could not destroy pseudo-random number generator: \");\n        msg += curandResult;\n        throw std::runtime_error(msg);\n    }\n\n    // Count the points inside unit quarter-circle\n    computeValue<Real><<<grid, block, block.x * sizeof(unsigned int)>>>(d_results, d_points, m_numSims);\n\n    // Copy partial results back\n    vector<unsigned int> results(grid.x);\n    cudaResult = hipMemcpy(&results[0], d_results, grid.x * sizeof(unsigned int), hipMemcpyDeviceToHost);\n\n    if (cudaResult != hipSuccess) {\n        string msg(\"Could not copy partial results to host: \");\n        msg += hipGetErrorString(cudaResult);\n        throw std::runtime_error(msg);\n    }\n\n    // Complete sum-reduction on host\n    Real value = static_cast<Real>(std::accumulate(results.begin(), results.end(), 0));\n\n    // Determine the proportion of points inside the quarter-circle,\n    // i.e. the area of the unit quarter-circle\n    value /= m_numSims;\n\n    // Value is currently an estimate of the area of a unit quarter-circle, so we\n    // can scale to a full circle by multiplying by four. Now since the area of a\n    // circle is pi * r^2, and r is one, the value will be an estimate for the\n    // value of pi.\n    value *= 4;\n\n    // Cleanup\n    if (d_points) {\n        hipFree(d_points);\n        d_points = 0;\n    }\n\n    if (d_results) {\n        hipFree(d_results);\n        d_results = 0;\n    }\n\n    return value;\n}\n\n// Explicit template instantiation\ntemplate class PiEstimator<float>;\ntemplate class PiEstimator<double>;\n", "source": "/kaggle/working/cuda-samples/Samples/2_Concepts_and_Techniques/MC_EstimatePiP/src/piestimator.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <multiply.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <util.h>\n#include <cutil.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#include <cusp/multiply.h>\n#include <matrix.h>\n#include <matrix_cusp.h>\n#include <amgx_cusparse.h>\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <sm_utils.inl>\n\n#include <amgx_types/math.h>\n#include <amgx_types/util.h>\n\n\nnamespace amgx\n{\n\n#define USE_EXPERIMENTAL_4x4\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1;\ntemplate <class Matrix, class Vector, class IVector>\nclass Multiply_1x1_masked;\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask;\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask_restriction;\ntemplate <class Matrix, class Vector>\nclass Multiply_3x3;\ntemplate <class Matrix, class Vector>\nclass Multiply_4x4;\ntemplate <class Matrix, class Vector>\nclass Multiply_bxb;\n\ntemplate <typename TConfig>\nvoid multiply_block_size(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1<TMatrix, TVector>::multiply_1x1(A, B, C, view);\n    }\n    else if (A.get_block_dimy() == 3 && A.get_block_dimx() == 3)\n    {\n        Multiply_3x3<TMatrix, TVector>::multiply_3x3(A, B, C, view);\n    }\n    else if (A.get_block_dimy() == 4 && A.get_block_dimx() == 4)\n    {\n        Multiply_4x4<TMatrix, TVector>::multiply_4x4(A, B, C, view);\n    }\n    else\n    {\n        Multiply_bxb<TMatrix, TVector>::multiply_bxb(A, B, C, view);\n    }\n}\n\ntemplate <typename TConfig>\nvoid multiply(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    bool latencyHiding = (view == A.getViewExterior() && A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && B.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(B, B.tag);\n\n        // Multiply interior rows\n        multiply_block_size(A, B, C, A.getViewInterior());\n\n        // Finish halo exchange\n        A.manager->exchange_halo_split_finish(B, B.tag);\n\n        // Multiply rows with halo dependencies\n        ViewType bnd_view = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n        multiply_block_size(A, B, C, bnd_view);\n    }\n    else\n    {\n        if (view != INTERIOR && !A.is_matrix_singleGPU() && B.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(B, B.tag);\n        }\n\n        multiply_block_size(A, B, C, view);\n    }\n\n    C.dirtybit = 1;\n    C.set_block_dimy(A.get_block_dimx());\n}\n\ntemplate <class TConfig>\nvoid multiply_masked(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, typename Matrix<TConfig>::IVector &mask, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n    typedef typename Matrix<TConfig>::IVector TIVector;\n\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if(A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported blocksize for multiply_masked()\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    Multiply_1x1_masked<TMatrix, TVector, TIVector>::multiply_1x1_masked(A, B, C, mask, view);\n    C.set_block_dimy(A.get_block_dimx());\n}\n\ntemplate<class Matrix, class Vector>\nvoid multiply_with_mask(Matrix &A, Vector &B, Vector &C)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1_with_mask<Matrix, Vector>::multiply_1x1(A, B, C);\n    }\n    else\n    {\n        FatalError(\"multiply with mask not supported for bsize != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    C.set_block_dimy(A.get_block_dimx());\n    C.dirtybit = 1;\n    //if (!A.is_matrix_singleGPU() && C.size() == B.size() && C.delayed_send==0)\n    //  A.manager->exchange_halo_async(C, C.tag);\n}\n\n\ntemplate<class Matrix, class Vector>\nvoid multiply_with_mask_restriction(Matrix &A, Vector &B, Vector &C, Matrix &P)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1_with_mask_restriction<Matrix, Vector>::multiply_1x1(A, B, C, P);\n    }\n    else\n    {\n        FatalError(\"multiply with mask not supported for bsize != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    C.set_block_dimy(A.get_block_dimx());\n    C.dirtybit = 1;\n}\n\n\n\n\ntemplate<class TConfig>\nvoid multiplyMM(const Matrix<TConfig> &A, const Matrix<TConfig> &B, Matrix<TConfig> &C)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimx() || A.get_block_dimy() != B.get_block_dimy())\n    {\n        FatalError(\"Matrices dimensions do not match\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Error, multiplyMM not implemented on device\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        if (A.get_block_size() != 1)\n        {\n            FatalError(\"multiplyMM only works for block_size ==1\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n        else\n        {\n            typedef typename TConfig::IndPrec IndexType;\n            typedef typename TConfig::MatPrec ValueType;\n            typedef typename Matrix<TConfig>::IVector IVector;\n            typedef typename Matrix<TConfig>::MVector MVector;\n            C.set_initialized(0);\n            IndexType num_nonzeros = 0;\n            IVector mask(B.get_num_cols(), IndexType (-1));\n\n            // Compute nnz in C (including explicit zeros)\n            for (size_t i = 0; i < A.get_num_rows(); i++)\n            {\n                for (IndexType jj = A.row_offsets[i]; jj < A.row_offsets[i + 1]; jj++)\n                {\n                    IndexType j = A.col_indices[jj];\n\n                    for (IndexType kk = B.row_offsets[j]; kk < B.row_offsets[j + 1]; kk++)\n                    {\n                        IndexType k = B.col_indices[kk];\n\n                        if (mask[k] != i)\n                        {\n                            mask[k] = i;\n                            num_nonzeros++;\n                        }\n                    }\n                }\n            }\n\n            // Resize output\n            C.resize(A.get_num_rows(), B.get_num_cols(), num_nonzeros);\n            const IndexType unseen = static_cast<IndexType>(-1);\n            const IndexType init   = static_cast<IndexType>(-2);\n            // Compute entries of C\n            IVector next(B.get_num_cols(), unseen);\n            MVector sums(B.get_num_cols(), types::util<ValueType>::get_zero());\n            num_nonzeros = 0;\n            C.row_offsets[0] = 0;\n\n            for (size_t i = 0; i < A.get_num_rows(); i++)\n            {\n                IndexType head   = init;\n                IndexType length =    0;\n                IndexType jj_start = A.row_offsets[i];\n                IndexType jj_end   = A.row_offsets[i + 1];\n\n                for (IndexType jj = jj_start; jj < jj_end; jj++)\n                {\n                    IndexType j = A.col_indices[jj];\n                    ValueType v = A.values[jj];\n                    IndexType kk_start = B.row_offsets[j];\n                    IndexType kk_end   = B.row_offsets[j + 1];\n\n                    for (IndexType kk = kk_start; kk < kk_end; kk++)\n                    {\n                        IndexType k = B.col_indices[kk];\n                        sums[k] = sums[k] + v * B.values[kk];\n\n                        if (next[k] == unseen)\n                        {\n                            next[k] = head;\n                            head  = k;\n                            length++;\n                        }\n                    }\n                }\n\n                for (IndexType jj = 0; jj < length; jj++)\n                {\n                    //if(sums[head] != ValueType(0))\n                    //{\n                    C.col_indices[num_nonzeros] = head;\n                    C.values[num_nonzeros]      = sums[head];\n                    num_nonzeros++;\n                    //}\n                    IndexType temp = head;\n                    head = next[head];\n                    // clear arrays\n                    next[temp] = unseen;\n                    sums[temp] = types::util<ValueType>::get_zero();\n                }\n\n                C.row_offsets[i + 1] = num_nonzeros;\n            }\n\n            // Resize output again since pass2 omits explict zeros\n            //C.resize(A.num_rows, B.num_cols, num_nonzeros);\n            C.set_initialized(1);\n        }\n    }\n}\n\n// --------------------------------\n// KERNELS\n// --------------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernel(const IndexType *row_offsets,\n                               const IndexType *column_indices,\n                               const ValueTypeA *nonzero_values,\n                               const ValueTypeB *B,\n                               ValueTypeB *C,\n                               const IndexType num_block_rows,\n                               const IndexType row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n    ValueTypeB temp[bsize];\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        //i = eighthwarp_id;\n        C_temp = types::util<ValueTypeB>::get_zero();\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n#ifdef USE_EXPERIMENTAL_4x4\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int CTA_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid blockDiaCsrMultiplyKernelDiaProps_4x4( const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const IndexType *dia_ptr,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeB *B,\n        ValueTypeB *C,\n        const int num_block_rows,\n        const int row_offset )\n{\n    const int nHalfWarps = CTA_SIZE / 16; // Number of half warps per CTA.\n    const int laneId = threadIdx.x % warpSize;\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int laneId_div_16 = laneId / 16;\n    const int upperHalf = 16 * laneId_div_16;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowId = blockIdx.x * nHalfWarps + halfWarpId ; aRowId < num_block_rows ; aRowId += gridDim.x * nHalfWarps )\n    {\n        unsigned int active_mask = utils::activemask();\n        // Load one block of B.\n        ValueTypeB my_Ax = types::util<ValueTypeB>::get_zero();\n\n        // The diagonal.\n        if ( halfLaneId_div_4 == 0 )\n        {\n            types::util<ValueTypeB>::volcast( B[4 * aRowId + halfLaneId_mod_4], my_s_mem + halfLaneId);\n        }\n\n        // Load the diagonal.\n        int diagId = dia_ptr[aRowId];\n        // Update my values.\n        ValueTypeA my_val = nonzero_values[16 * diagId + halfLaneId];\n\n        if ( ROW_MAJOR )\n        {\n            my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[halfLaneId_mod_4]);\n        }\n        else\n        {\n            my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[halfLaneId_div_4]);\n        }\n\n        // The range of the rows.\n        int aColBegin = row_offsets[aRowId + 0];\n        int aColEnd   = row_offsets[aRowId + 1];\n\n        // Each warp load column indices of 16 nonzero blocks\n        for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n        {\n            int aColIt = aColBegin + halfLaneId;\n            // Get the ID of the column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = column_indices[aColIt];\n            }\n\n            // Count the number of active columns.\n            int vote =  utils::ballot(aColId != -1, active_mask);\n            // The number of iterations.\n            int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < nCols ; k += 4 )\n            {\n                int my_k = k + halfLaneId_div_4;\n                // Exchange column indices.\n                int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                // Load 8 blocks of X if needed.\n                ValueTypeB my_x = types::util<ValueTypeB>::get_zero();\n\n                if ( waColId != -1 )\n                {\n                    my_x = B[4 * waColId + halfLaneId_mod_4];\n                }\n\n                types::util<ValueTypeB>::volcast( my_x, my_s_mem + halfLaneId);\n                // Load 8 blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                    if ( w_aColTmp < aColEnd )\n                    {\n                        w_aColIt = w_aColTmp;\n                    }\n\n                    ValueTypeA my_val = types::util<ValueTypeA>::get_zero();\n\n                    if ( w_aColIt != -1 )\n                    {\n                        my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[4 * i + halfLaneId_mod_4]);\n                    }\n                    else\n                    {\n                        my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[4 * i + halfLaneId_div_4]);\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Reduce bmAx terms.\n        if ( ROW_MAJOR )\n        {\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 1, warpSize, active_mask );\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 2, warpSize, active_mask );\n        }\n        else\n        {\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 4, warpSize, active_mask );\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 8, warpSize, active_mask );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                C[4 * aRowId + halfLaneId_div_4] = my_Ax;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                C[4 * aRowId + halfLaneId_mod_4] = my_Ax;\n            }\n        }\n    }\n}\n\n#else\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, int bsize_sq, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernelDiaProps_4x4(const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const IndexType *dia_ptr,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeB *B,\n        ValueTypeB *C,\n        const int num_block_rows,\n        const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        //i = eighthwarp_id;\n        C_temp = types::util<ValueTypeB>::get_zero();\n        // Contribution from diagonal\n        offset = eighthwarp_id * bsize + vec_entry_index;\n        types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        ValueTypeA temp[bsize];\n\n        if (ROW_MAJOR)\n        {\n            loadAsVector<bsize>(nonzero_values + bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index * bsize, temp);\n        }\n        else\n        {\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                temp[m] = nonzero_values[bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index + bsize * m];\n            }\n        }\n\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize_sq + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    temp[m] = nonzero_values[offset + bsize * m];\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n#endif\n\n// implementation for arbitrary block size\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, int diag, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernelDiaProps(const IndexType *row_offsets,\n                                       const IndexType *column_indices,\n                                       const IndexType *dia_ptr,\n                                       const ValueTypeA *nonzero_values,\n                                       const ValueTypeB *B,\n                                       ValueTypeB *C,\n                                       const int num_block_rows,\n                                       const int row_offset)\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    int eighthwarp_id = row_offset + blockIdx.x * blockrows_per_cta + tid / bsize;\n    const int block_eighthwarp_id = tid / bsize;\n    const int vec_entry_index = tid % bsize;\n    const int bsize_sq = bsize * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        C_temp = types::util<ValueTypeB>::get_zero();\n        ValueTypeB temp[bsize];\n\n        if ( diag )\n        {\n            // Contribution from diagonal\n            offset = eighthwarp_id * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast( __cachingLoad(&B[offset]), s_xtemp + tid);\n            // Load dia_values and do matrix multiply\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                loadAsVector<bsize>(nonzero_values + bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index * bsize, temp);\n            }\n            else\n            {\n                offset = dia_ptr[eighthwarp_id] * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast( __cachingLoad(&B[offset]), s_xtemp + tid);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize_sq + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\n// --------------------------------------\n//  Methods\n// -------------------------------------\n\n// Method to perform BSPmV on host using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nvoid multiply_common_sqblock_host_diag(const Matrix &A, const Vector &B, Vector &C)\n{\n    typedef typename Matrix::TConfig TConfig;\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Executrion path error: device matrix in host path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        //TODO:: This implementation is very inneficient, Use BLAS\n        typedef typename TConfig::IndPrec IndexType;\n        typedef typename TConfig::MatPrec ValueType;\n        typedef typename Vector::value_type ValueTypeB;\n        IndexType bsize = A.get_block_dimy();\n        ValueTypeB temp;\n\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            // Initialize RHS to 0\n            for (int m = 0; m < bsize; m++)\n            {\n                C[i * bsize + m] = types::util<ValueTypeB>::get_zero();\n            }\n\n            // Contribution from diagonal blocks\n            for (int n = 0; n < bsize; n++)\n            {\n                temp = B[i * bsize + n];\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    C[i * bsize + m] = C[i * bsize + m] + A.values[A.diag[i] * bsize * bsize + m * bsize + n] * temp;\n                }\n            }\n\n            // Contribution from nonzero off-diagonal blocks\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                IndexType jcol = A.col_indices[j];\n\n                for (int n = 0; n < bsize; n++)\n                {\n                    temp = B[jcol * bsize + n];\n\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        C[i * bsize + m] = C[i * bsize + m] + A.values[j * bsize * bsize + m * bsize + n] * temp;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <class Matrix, class Vector>\nvoid multiply_common_sqblock_host_nodiag(const Matrix &A, const Vector &B, Vector &C)\n{\n    typedef typename Matrix::TConfig TConfig;\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Executrion path error: device matrix in host path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        //TODO:: This implementation is very inneficient, Use BLAS\n        typedef typename TConfig::IndPrec IndexType;\n        typedef typename TConfig::MatPrec ValueType;\n        typedef typename Vector::value_type ValueTypeB;\n        IndexType bsize = A.get_block_dimy();\n        ValueTypeB temp;\n\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            // Initialize RHS to 0\n            for (int m = 0; m < bsize; m++)\n            {\n                C[i * bsize + m] = types::util<ValueTypeB>::get_zero();\n            }\n\n            // Contribution from nonzero blocks\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                IndexType jcol = A.col_indices[j];\n\n                for (int n = 0; n < bsize; n++)\n                {\n                    temp = B[jcol * bsize + n];\n\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        C[i * bsize + m] = C[i * bsize + m] + A.values[j * bsize * bsize + m * bsize + n] * temp;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, view);\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector, class IVector>\nclass Multiply_1x1_masked\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1_masked(Matrix &A, Vector &B, Vector &C, IVector mask, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"Masked multiply is not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrxmv<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, mask, view);\n                cudaCheckError();\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"multiply with mask not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv_with_mask<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C );\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask_restriction\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C, Matrix &P)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"multiply with mask not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv_with_mask_restriction<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, P);\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_4x4\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_4x4(const Matrix &A, const Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                //TODO: compare with cublas\n                typedef typename TConfig::IndPrec IndexType;\n                typedef typename TConfig::MatPrec ValueTypeA;\n                typedef typename TConfig::VecPrec ValueTypeB;\n                int num_rows, offset;\n                A.getOffsetAndSizeForView(view, &offset, &num_rows);\n                const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n                const IndexType *A_column_indices_ptr = A.col_indices.raw();\n                const IndexType *A_dia_ind_ptr = A.diag.raw();\n                const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n                const ValueTypeB *B_ptr = B.raw();\n                ValueTypeB *C_ptr = C.raw();\n                cudaCheckError();\n                const unsigned int threads_per_block = 128;\n                const int eightwarps_per_block = threads_per_block / 4;\n                const int num_warps_per_cta = threads_per_block / 32;\n                const int num_blocks =  std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows + num_warps_per_cta - 1) / num_warps_per_cta); // (int) (A.get_num_rows()-1)/eightwarps_per_block + 1;\n\n                if (!A.hasProps(DIAG))\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, true>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, false>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n\n                }\n                else\n                {\n\n#ifdef USE_EXPERIMENTAL_4x4\n                    if ( A.getBlockFormat() == ROW_MAJOR )\n                    {\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, threads_per_block, true > <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, threads_per_block, false > <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n#else\n\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, true>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, false>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n\n#endif\n                }\n                cudaCheckError();\n            }\n        }\n};\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_bxb\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_bxb(Matrix &A, Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, view);\n                cudaCheckError();\n            }\n        }\n};\n\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_3x3\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_3x3(const Matrix &A, const Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                //TODO: compare with cublas\n                typedef typename TConfig::IndPrec IndexType;\n                typedef typename TConfig::MatPrec ValueTypeA;\n                typedef typename TConfig::VecPrec ValueTypeB;\n                int num_rows, offset;\n                A.getOffsetAndSizeForView(view, &offset, &num_rows);\n                const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n                const IndexType *A_column_indices_ptr = A.col_indices.raw();\n                const IndexType *A_dia_ind_ptr = A.diag.raw();\n                const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n                const ValueTypeB *B_ptr = B.raw();\n                ValueTypeB *C_ptr = C.raw();\n                cudaCheckError();\n                const int threads_per_block = 64 * 3;\n                const int blockrows_per_warp = 32 / 3;\n                const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n                const int num_blocks =  std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / blockrows_per_cta + 1); // (int) (A.get_num_rows()-1)/eightwarps_per_block + 1;\n\n                if (!A.hasProps(DIAG))\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, true>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, false>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                }\n                else\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, true>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        cudaFuncSetCacheConfig(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, false>, cudaFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n            }\n        }\n};\n\n// -------------------------------\n// Explict instantiations\n// -------------------------------\n#define AMGX_CASE_LINE(CASE) template void multiplyMM(const Matrix<TemplateMode<CASE>::Type>&, const Matrix<TemplateMode<CASE>::Type>&, Matrix<TemplateMode<CASE>::Type>&);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type> &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_masked(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type> &, typename Matrix<TemplateMode<CASE>::Type>::IVector &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_with_mask(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>  &);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_with_mask_restriction(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>  &, Matrix<TemplateMode<CASE>::Type> & );\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <multiply.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <util.h>\n#include <cutil.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#include <cusp/multiply.h>\n#include <matrix.h>\n#include <matrix_cusp.h>\n#include <amgx_cusparse.h>\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <sm_utils.inl>\n\n#include <amgx_types/math.h>\n#include <amgx_types/util.h>\n\n\nnamespace amgx\n{\n\n#define USE_EXPERIMENTAL_4x4\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1;\ntemplate <class Matrix, class Vector, class IVector>\nclass Multiply_1x1_masked;\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask;\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask_restriction;\ntemplate <class Matrix, class Vector>\nclass Multiply_3x3;\ntemplate <class Matrix, class Vector>\nclass Multiply_4x4;\ntemplate <class Matrix, class Vector>\nclass Multiply_bxb;\n\ntemplate <typename TConfig>\nvoid multiply_block_size(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1<TMatrix, TVector>::multiply_1x1(A, B, C, view);\n    }\n    else if (A.get_block_dimy() == 3 && A.get_block_dimx() == 3)\n    {\n        Multiply_3x3<TMatrix, TVector>::multiply_3x3(A, B, C, view);\n    }\n    else if (A.get_block_dimy() == 4 && A.get_block_dimx() == 4)\n    {\n        Multiply_4x4<TMatrix, TVector>::multiply_4x4(A, B, C, view);\n    }\n    else\n    {\n        Multiply_bxb<TMatrix, TVector>::multiply_bxb(A, B, C, view);\n    }\n}\n\ntemplate <typename TConfig>\nvoid multiply(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n\n    bool latencyHiding = (view == A.getViewExterior() && A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && B.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(B, B.tag);\n\n        // Multiply interior rows\n        multiply_block_size(A, B, C, A.getViewInterior());\n\n        // Finish halo exchange\n        A.manager->exchange_halo_split_finish(B, B.tag);\n\n        // Multiply rows with halo dependencies\n        ViewType bnd_view = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n        multiply_block_size(A, B, C, bnd_view);\n    }\n    else\n    {\n        if (view != INTERIOR && !A.is_matrix_singleGPU() && B.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(B, B.tag);\n        }\n\n        multiply_block_size(A, B, C, view);\n    }\n\n    C.dirtybit = 1;\n    C.set_block_dimy(A.get_block_dimx());\n}\n\ntemplate <class TConfig>\nvoid multiply_masked(Matrix<TConfig> &A, Vector<TConfig> &B, Vector<TConfig> &C, typename Matrix<TConfig>::IVector &mask, ViewType view)\n{\n    typedef Matrix<TConfig> TMatrix;\n    typedef Vector<TConfig> TVector;\n    typedef typename Matrix<TConfig>::IVector TIVector;\n\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if(A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported blocksize for multiply_masked()\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    Multiply_1x1_masked<TMatrix, TVector, TIVector>::multiply_1x1_masked(A, B, C, mask, view);\n    C.set_block_dimy(A.get_block_dimx());\n}\n\ntemplate<class Matrix, class Vector>\nvoid multiply_with_mask(Matrix &A, Vector &B, Vector &C)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1_with_mask<Matrix, Vector>::multiply_1x1(A, B, C);\n    }\n    else\n    {\n        FatalError(\"multiply with mask not supported for bsize != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    C.set_block_dimy(A.get_block_dimx());\n    C.dirtybit = 1;\n    //if (!A.is_matrix_singleGPU() && C.size() == B.size() && C.delayed_send==0)\n    //  A.manager->exchange_halo_async(C, C.tag);\n}\n\n\ntemplate<class Matrix, class Vector>\nvoid multiply_with_mask_restriction(Matrix &A, Vector &B, Vector &C, Matrix &P)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimy())\n    {\n        std::stringstream ss;\n        ss << \"Matrix and vector dimensions don't match: A.dimx == \" << A.get_block_dimx() << \", B.dimy == \" << B.get_block_dimy();\n        FatalError(ss.str().c_str(), AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_size() == 1)\n    {\n        Multiply_1x1_with_mask_restriction<Matrix, Vector>::multiply_1x1(A, B, C, P);\n    }\n    else\n    {\n        FatalError(\"multiply with mask not supported for bsize != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    C.set_block_dimy(A.get_block_dimx());\n    C.dirtybit = 1;\n}\n\n\n\n\ntemplate<class TConfig>\nvoid multiplyMM(const Matrix<TConfig> &A, const Matrix<TConfig> &B, Matrix<TConfig> &C)\n{\n    if (!A.is_initialized())\n    {\n        FatalError(\"Trying to multiply uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (A.get_block_dimx() != B.get_block_dimx() || A.get_block_dimy() != B.get_block_dimy())\n    {\n        FatalError(\"Matrices dimensions do not match\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Error, multiplyMM not implemented on device\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        if (A.get_block_size() != 1)\n        {\n            FatalError(\"multiplyMM only works for block_size ==1\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n        else\n        {\n            typedef typename TConfig::IndPrec IndexType;\n            typedef typename TConfig::MatPrec ValueType;\n            typedef typename Matrix<TConfig>::IVector IVector;\n            typedef typename Matrix<TConfig>::MVector MVector;\n            C.set_initialized(0);\n            IndexType num_nonzeros = 0;\n            IVector mask(B.get_num_cols(), IndexType (-1));\n\n            // Compute nnz in C (including explicit zeros)\n            for (size_t i = 0; i < A.get_num_rows(); i++)\n            {\n                for (IndexType jj = A.row_offsets[i]; jj < A.row_offsets[i + 1]; jj++)\n                {\n                    IndexType j = A.col_indices[jj];\n\n                    for (IndexType kk = B.row_offsets[j]; kk < B.row_offsets[j + 1]; kk++)\n                    {\n                        IndexType k = B.col_indices[kk];\n\n                        if (mask[k] != i)\n                        {\n                            mask[k] = i;\n                            num_nonzeros++;\n                        }\n                    }\n                }\n            }\n\n            // Resize output\n            C.resize(A.get_num_rows(), B.get_num_cols(), num_nonzeros);\n            const IndexType unseen = static_cast<IndexType>(-1);\n            const IndexType init   = static_cast<IndexType>(-2);\n            // Compute entries of C\n            IVector next(B.get_num_cols(), unseen);\n            MVector sums(B.get_num_cols(), types::util<ValueType>::get_zero());\n            num_nonzeros = 0;\n            C.row_offsets[0] = 0;\n\n            for (size_t i = 0; i < A.get_num_rows(); i++)\n            {\n                IndexType head   = init;\n                IndexType length =    0;\n                IndexType jj_start = A.row_offsets[i];\n                IndexType jj_end   = A.row_offsets[i + 1];\n\n                for (IndexType jj = jj_start; jj < jj_end; jj++)\n                {\n                    IndexType j = A.col_indices[jj];\n                    ValueType v = A.values[jj];\n                    IndexType kk_start = B.row_offsets[j];\n                    IndexType kk_end   = B.row_offsets[j + 1];\n\n                    for (IndexType kk = kk_start; kk < kk_end; kk++)\n                    {\n                        IndexType k = B.col_indices[kk];\n                        sums[k] = sums[k] + v * B.values[kk];\n\n                        if (next[k] == unseen)\n                        {\n                            next[k] = head;\n                            head  = k;\n                            length++;\n                        }\n                    }\n                }\n\n                for (IndexType jj = 0; jj < length; jj++)\n                {\n                    //if(sums[head] != ValueType(0))\n                    //{\n                    C.col_indices[num_nonzeros] = head;\n                    C.values[num_nonzeros]      = sums[head];\n                    num_nonzeros++;\n                    //}\n                    IndexType temp = head;\n                    head = next[head];\n                    // clear arrays\n                    next[temp] = unseen;\n                    sums[temp] = types::util<ValueType>::get_zero();\n                }\n\n                C.row_offsets[i + 1] = num_nonzeros;\n            }\n\n            // Resize output again since pass2 omits explict zeros\n            //C.resize(A.num_rows, B.num_cols, num_nonzeros);\n            C.set_initialized(1);\n        }\n    }\n}\n\n// --------------------------------\n// KERNELS\n// --------------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernel(const IndexType *row_offsets,\n                               const IndexType *column_indices,\n                               const ValueTypeA *nonzero_values,\n                               const ValueTypeB *B,\n                               ValueTypeB *C,\n                               const IndexType num_block_rows,\n                               const IndexType row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n    ValueTypeB temp[bsize];\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        //i = eighthwarp_id;\n        C_temp = types::util<ValueTypeB>::get_zero();\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n#ifdef USE_EXPERIMENTAL_4x4\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int CTA_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid blockDiaCsrMultiplyKernelDiaProps_4x4( const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const IndexType *dia_ptr,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeB *B,\n        ValueTypeB *C,\n        const int num_block_rows,\n        const int row_offset )\n{\n    const int nHalfWarps = CTA_SIZE / 16; // Number of half warps per CTA.\n    const int laneId = threadIdx.x % warpSize;\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int laneId_div_16 = laneId / 16;\n    const int upperHalf = 16 * laneId_div_16;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowId = blockIdx.x * nHalfWarps + halfWarpId ; aRowId < num_block_rows ; aRowId += gridDim.x * nHalfWarps )\n    {\n        unsigned int active_mask = utils::activemask();\n        // Load one block of B.\n        ValueTypeB my_Ax = types::util<ValueTypeB>::get_zero();\n\n        // The diagonal.\n        if ( halfLaneId_div_4 == 0 )\n        {\n            types::util<ValueTypeB>::volcast( B[4 * aRowId + halfLaneId_mod_4], my_s_mem + halfLaneId);\n        }\n\n        // Load the diagonal.\n        int diagId = dia_ptr[aRowId];\n        // Update my values.\n        ValueTypeA my_val = nonzero_values[16 * diagId + halfLaneId];\n\n        if ( ROW_MAJOR )\n        {\n            my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[halfLaneId_mod_4]);\n        }\n        else\n        {\n            my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[halfLaneId_div_4]);\n        }\n\n        // The range of the rows.\n        int aColBegin = row_offsets[aRowId + 0];\n        int aColEnd   = row_offsets[aRowId + 1];\n\n        // Each warp load column indices of 16 nonzero blocks\n        for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n        {\n            int aColIt = aColBegin + halfLaneId;\n            // Get the ID of the column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = column_indices[aColIt];\n            }\n\n            // Count the number of active columns.\n            int vote =  utils::ballot(aColId != -1, active_mask);\n            // The number of iterations.\n            int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < nCols ; k += 4 )\n            {\n                int my_k = k + halfLaneId_div_4;\n                // Exchange column indices.\n                int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                // Load 8 blocks of X if needed.\n                ValueTypeB my_x = types::util<ValueTypeB>::get_zero();\n\n                if ( waColId != -1 )\n                {\n                    my_x = B[4 * waColId + halfLaneId_mod_4];\n                }\n\n                types::util<ValueTypeB>::volcast( my_x, my_s_mem + halfLaneId);\n                // Load 8 blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                    if ( w_aColTmp < aColEnd )\n                    {\n                        w_aColIt = w_aColTmp;\n                    }\n\n                    ValueTypeA my_val = types::util<ValueTypeA>::get_zero();\n\n                    if ( w_aColIt != -1 )\n                    {\n                        my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[4 * i + halfLaneId_mod_4]);\n                    }\n                    else\n                    {\n                        my_Ax = my_Ax + my_val * types::util<ValueTypeB>::volcast(my_s_mem[4 * i + halfLaneId_div_4]);\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Reduce bmAx terms.\n        if ( ROW_MAJOR )\n        {\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 1, warpSize, active_mask );\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 2, warpSize, active_mask );\n        }\n        else\n        {\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 4, warpSize, active_mask );\n            my_Ax = my_Ax + utils::shfl_xor( my_Ax, 8, warpSize, active_mask );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                C[4 * aRowId + halfLaneId_div_4] = my_Ax;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                C[4 * aRowId + halfLaneId_mod_4] = my_Ax;\n            }\n        }\n    }\n}\n\n#else\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, int bsize_sq, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernelDiaProps_4x4(const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const IndexType *dia_ptr,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeB *B,\n        ValueTypeB *C,\n        const int num_block_rows,\n        const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        //i = eighthwarp_id;\n        C_temp = types::util<ValueTypeB>::get_zero();\n        // Contribution from diagonal\n        offset = eighthwarp_id * bsize + vec_entry_index;\n        types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        ValueTypeA temp[bsize];\n\n        if (ROW_MAJOR)\n        {\n            loadAsVector<bsize>(nonzero_values + bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index * bsize, temp);\n        }\n        else\n        {\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                temp[m] = nonzero_values[bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index + bsize * m];\n            }\n        }\n\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast(__cachingLoad(&B[offset]), s_xtemp + threadIdx.x);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize_sq + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    temp[m] = nonzero_values[offset + bsize * m];\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n#endif\n\n// implementation for arbitrary block size\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, int diag, bool ROW_MAJOR>\n__global__\nvoid blockDiaCsrMultiplyKernelDiaProps(const IndexType *row_offsets,\n                                       const IndexType *column_indices,\n                                       const IndexType *dia_ptr,\n                                       const ValueTypeA *nonzero_values,\n                                       const ValueTypeB *B,\n                                       ValueTypeB *C,\n                                       const int num_block_rows,\n                                       const int row_offset)\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    int eighthwarp_id = row_offset + blockIdx.x * blockrows_per_cta + tid / bsize;\n    const int block_eighthwarp_id = tid / bsize;\n    const int vec_entry_index = tid % bsize;\n    const int bsize_sq = bsize * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta ];\n    ValueTypeB C_temp;\n    int offset, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        C_temp = types::util<ValueTypeB>::get_zero();\n        ValueTypeB temp[bsize];\n\n        if ( diag )\n        {\n            // Contribution from diagonal\n            offset = eighthwarp_id * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast( __cachingLoad(&B[offset]), s_xtemp + tid);\n            // Load dia_values and do matrix multiply\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                loadAsVector<bsize>(nonzero_values + bsize_sq * dia_ptr[eighthwarp_id] + vec_entry_index * bsize, temp);\n            }\n            else\n            {\n                offset = dia_ptr[eighthwarp_id] * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[eighthwarp_id + 1];\n\n        for (int jind = row_offsets[eighthwarp_id]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            types::util<ValueTypeB>::volcast( __cachingLoad(&B[offset]), s_xtemp + tid);\n            // Load nonzero_values\n            s_offset = block_eighthwarp_id * bsize;\n\n            if (ROW_MAJOR)\n            {\n                offset = jind * bsize_sq + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n            }\n            else\n            {\n                offset = jind * bsize_sq + vec_entry_index;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    types::util<ValueTypeA>::to_uptype(nonzero_values[offset + bsize * m], temp[m]);\n                }\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                C_temp = C_temp + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n            }\n        }\n\n        C[eighthwarp_id * bsize + vec_entry_index] = C_temp;\n        eighthwarp_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\n// --------------------------------------\n//  Methods\n// -------------------------------------\n\n// Method to perform BSPmV on host using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nvoid multiply_common_sqblock_host_diag(const Matrix &A, const Vector &B, Vector &C)\n{\n    typedef typename Matrix::TConfig TConfig;\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Executrion path error: device matrix in host path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        //TODO:: This implementation is very inneficient, Use BLAS\n        typedef typename TConfig::IndPrec IndexType;\n        typedef typename TConfig::MatPrec ValueType;\n        typedef typename Vector::value_type ValueTypeB;\n        IndexType bsize = A.get_block_dimy();\n        ValueTypeB temp;\n\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            // Initialize RHS to 0\n            for (int m = 0; m < bsize; m++)\n            {\n                C[i * bsize + m] = types::util<ValueTypeB>::get_zero();\n            }\n\n            // Contribution from diagonal blocks\n            for (int n = 0; n < bsize; n++)\n            {\n                temp = B[i * bsize + n];\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    C[i * bsize + m] = C[i * bsize + m] + A.values[A.diag[i] * bsize * bsize + m * bsize + n] * temp;\n                }\n            }\n\n            // Contribution from nonzero off-diagonal blocks\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                IndexType jcol = A.col_indices[j];\n\n                for (int n = 0; n < bsize; n++)\n                {\n                    temp = B[jcol * bsize + n];\n\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        C[i * bsize + m] = C[i * bsize + m] + A.values[j * bsize * bsize + m * bsize + n] * temp;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <class Matrix, class Vector>\nvoid multiply_common_sqblock_host_nodiag(const Matrix &A, const Vector &B, Vector &C)\n{\n    typedef typename Matrix::TConfig TConfig;\n\n    if (TConfig::memSpace == AMGX_device)\n    {\n        FatalError(\"Executrion path error: device matrix in host path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        //TODO:: This implementation is very inneficient, Use BLAS\n        typedef typename TConfig::IndPrec IndexType;\n        typedef typename TConfig::MatPrec ValueType;\n        typedef typename Vector::value_type ValueTypeB;\n        IndexType bsize = A.get_block_dimy();\n        ValueTypeB temp;\n\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            // Initialize RHS to 0\n            for (int m = 0; m < bsize; m++)\n            {\n                C[i * bsize + m] = types::util<ValueTypeB>::get_zero();\n            }\n\n            // Contribution from nonzero blocks\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                IndexType jcol = A.col_indices[j];\n\n                for (int n = 0; n < bsize; n++)\n                {\n                    temp = B[jcol * bsize + n];\n\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        C[i * bsize + m] = C[i * bsize + m] + A.values[j * bsize * bsize + m * bsize + n] * temp;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, view);\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector, class IVector>\nclass Multiply_1x1_masked\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1_masked(Matrix &A, Vector &B, Vector &C, IVector mask, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"Masked multiply is not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrxmv<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, mask, view);\n                cudaCheckError();\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"multiply with mask not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv_with_mask<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C );\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate <class Matrix, class Vector>\nclass Multiply_1x1_with_mask_restriction\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_1x1(Matrix &A, Vector &B, Vector &C, Matrix &P)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                FatalError(\"multiply with mask not supported on host\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv_with_mask_restriction<TConfig>(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, P);\n                cudaCheckError();\n                //  FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_4x4\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_4x4(const Matrix &A, const Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                //TODO: compare with cublas\n                typedef typename TConfig::IndPrec IndexType;\n                typedef typename TConfig::MatPrec ValueTypeA;\n                typedef typename TConfig::VecPrec ValueTypeB;\n                int num_rows, offset;\n                A.getOffsetAndSizeForView(view, &offset, &num_rows);\n                const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n                const IndexType *A_column_indices_ptr = A.col_indices.raw();\n                const IndexType *A_dia_ind_ptr = A.diag.raw();\n                const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n                const ValueTypeB *B_ptr = B.raw();\n                ValueTypeB *C_ptr = C.raw();\n                cudaCheckError();\n                const unsigned int threads_per_block = 128;\n                const int eightwarps_per_block = threads_per_block / 4;\n                const int num_warps_per_cta = threads_per_block / 32;\n                const int num_blocks =  std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows + num_warps_per_cta - 1) / num_warps_per_cta); // (int) (A.get_num_rows()-1)/eightwarps_per_block + 1;\n\n                if (!A.hasProps(DIAG))\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, true>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, false>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernel<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n\n                }\n                else\n                {\n\n#ifdef USE_EXPERIMENTAL_4x4\n                    if ( A.getBlockFormat() == ROW_MAJOR )\n                    {\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, threads_per_block, true > <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, threads_per_block, false > <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n#else\n\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, true>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, false>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps_4x4<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n\n#endif\n                }\n                cudaCheckError();\n            }\n        }\n};\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_bxb\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_bxb(Matrix &A, Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                typedef typename TConfig::VecPrec ValueTypeB;\n                Cusparse::bsrmv(types::util<ValueTypeB>::get_one(), A, B, types::util<ValueTypeB>::get_zero(), C, view);\n                cudaCheckError();\n            }\n        }\n};\n\n\n// Method to perform BSPmV on device using block_dia_csr_matrix format\ntemplate <class Matrix, class Vector>\nclass Multiply_3x3\n{\n    public:\n        typedef typename Matrix::TConfig TConfig;\n        static void multiply_3x3(const Matrix &A, const Vector &B, Vector &C, ViewType view)\n        {\n            if (TConfig::memSpace == AMGX_host)\n            {\n                if (A.hasProps(DIAG))\n                {\n                    multiply_common_sqblock_host_diag(A, B, C);\n                }\n                else\n                {\n                    multiply_common_sqblock_host_nodiag(A, B, C);\n                }\n            }\n            else\n            {\n                //TODO: compare with cublas\n                typedef typename TConfig::IndPrec IndexType;\n                typedef typename TConfig::MatPrec ValueTypeA;\n                typedef typename TConfig::VecPrec ValueTypeB;\n                int num_rows, offset;\n                A.getOffsetAndSizeForView(view, &offset, &num_rows);\n                const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n                const IndexType *A_column_indices_ptr = A.col_indices.raw();\n                const IndexType *A_dia_ind_ptr = A.diag.raw();\n                const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n                const ValueTypeB *B_ptr = B.raw();\n                ValueTypeB *C_ptr = C.raw();\n                cudaCheckError();\n                const int threads_per_block = 64 * 3;\n                const int blockrows_per_warp = 32 / 3;\n                const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n                const int num_blocks =  std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / blockrows_per_cta + 1); // (int) (A.get_num_rows()-1)/eightwarps_per_block + 1;\n\n                if (!A.hasProps(DIAG))\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, true>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, false>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 0, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                }\n                else\n                {\n                    if (A.getBlockFormat() == ROW_MAJOR)\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, true>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, true> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        hipFuncSetCacheConfig(reinterpret_cast<const void*>(blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, false>), hipFuncCachePreferL1);\n                        blockDiaCsrMultiplyKernelDiaProps<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 3, 1, false> <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ind_ptr, A_nonzero_values_ptr, B_ptr, C_ptr, offset + num_rows, offset);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n            }\n        }\n};\n\n// -------------------------------\n// Explict instantiations\n// -------------------------------\n#define AMGX_CASE_LINE(CASE) template void multiplyMM(const Matrix<TemplateMode<CASE>::Type>&, const Matrix<TemplateMode<CASE>::Type>&, Matrix<TemplateMode<CASE>::Type>&);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type> &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_masked(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type> &, typename Matrix<TemplateMode<CASE>::Type>::IVector &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_with_mask(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>  &);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void multiply_with_mask_restriction(Matrix<TemplateMode<CASE>::Type> &, Vector<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>  &, Matrix<TemplateMode<CASE>::Type> & );\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/multiply.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <util.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\nnamespace amgx\n{\n\n// Kernel to invert 4by4 matrix on device using one thread\n// TODO: possible optimizations using multiple threads, possible generalisation for NxN blocks\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid gaussianElimination4by4Kernel(const IndexType *dia_indices, const ValueTypeA *values, ValueTypeB *x, const ValueTypeB *b)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB Atemp[BSIZE][BSIZE];\n    ValueTypeB xtemp[BSIZE];\n    ValueTypeB btemp[BSIZE];\n    int offset;\n\n    if (tid == 0)\n    {\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n        {\n            btemp[m]  = b[m];\n        }\n\n        offset = 0;\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n#pragma unroll\n            for (int n = 0; n < BSIZE; n++)\n            {\n                types::util<ValueTypeA>::to_uptype(values[offset++], Atemp[m][n]);    // only one nonzero block - diagonal\n            }\n\n        gaussianElimination4by4(Atemp, xtemp, btemp);\n        // Store x\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n        {\n            x[m] = xtemp[m];\n        }\n    }\n}\n\n// Method to perform gaussian elimination for a matrix stored in row major format\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\nvoid gaussianEliminationRowMajor(ValueTypeA **e, ValueTypeB *x, ValueTypeB *b, const IndexType bsize)\n{\n    if (bsize > 4) { FatalError(\"Warning, Gaussian Elimination code doesn't pivot and bsize > 4\", AMGX_ERR_BAD_PARAMETERS); }\n\n    ValueTypeB pivot, ratio, tmp;\n\n    for (int j = 0; j < bsize; j++)\n    {\n        types::util<ValueTypeA>::to_uptype(e[j][j], pivot);\n\n        for (int k = j + 1; k < bsize; k++)\n        {\n            ValueTypeB temp;\n            types::util<ValueTypeA>::to_uptype(e[k][j], temp);\n            ratio = temp / pivot;\n\n            for (int m = j + 1; m < bsize; m++)\n            {\n                e[k][m] = e[k][m] - e[j][m] * ratio;\n            }\n\n            b[k] = b[k] - b[j] * ratio;\n        }\n    }\n\n    // back substitution\n    for (int j = bsize - 1; j >= 0; j--)\n    {\n        tmp = types::util<ValueTypeB>::get_zero();\n\n        for (int k = j + 1; k < bsize; k++)\n        {\n            tmp = tmp + e[j][k] * x[k];\n        }\n\n        x[j] = (b[j] - tmp) / e[j][j];\n    }\n}\n\n// Method to perform gaussian elimination on matrix stored in block_dia_csr_matrix_h\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination_4x4_host(const Matrix_h &A, Vector_h &x, const Vector_h &b)\n{\n    typedef typename Matrix_h::value_type ValueTypeA;\n    typedef typename Vector_h::value_type ValueTypeB;\n\n    if (A.get_num_rows() != 1)\n    {\n        FatalError(\"Haven't implemented gaussian elimination solver for num_blocks != 1\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        int bsize = A.get_block_dimy();\n        // Allocate space for block_matrix\n        ValueTypeA **Atemp = new ValueTypeA* [bsize];\n\n        for ( int i = 0; i < bsize; i++)\n        {\n            Atemp[i] = new ValueTypeA[bsize];\n        }\n\n        ValueTypeB *btemp = new ValueTypeB[bsize];\n        ValueTypeB *xtemp = new ValueTypeB[bsize];\n\n        // Copy matrix and rhs\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                Atemp[m][n] = A.values[bsize * m + n]; //diag[bsize*m + n]; since we have 1 nonzero only\n            }\n\n            btemp[m] = b[m];\n        }\n\n        ValueTypeB *x_ptr = x.raw();\n        gaussianEliminationRowMajor(Atemp, x_ptr, btemp, bsize);\n    }\n}\n\n// Method to perform gaussian elimination on matrix stored in block_dia_csr_matrix_d\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination_4x4_device(const Matrix_d &A, Vector_d &x, const Vector_d &b)\n{\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename Vector_d::value_type ValueTypeB;\n    typedef typename Matrix_d::index_type IndexType;\n\n    if (A.get_num_rows() != 1)\n    {\n        FatalError(\"Haven't implemented gaussian elimination solver for num_blocks != 1 or block_size != 4\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        const IndexType *dia_ptr = A.diag.raw();\n        const ValueTypeA *values_ptr = A.values.raw();\n        const ValueTypeB *b_ptr = b.raw();\n        ValueTypeB *x_ptr = x.raw();\n        gaussianElimination4by4Kernel <<< 1, 1>>>(dia_ptr, values_ptr, x_ptr, b_ptr);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid GaussianElimination<TConfig>::gaussianElimination(const Matrix<TConfig> &A, Vector<TConfig> &x, const Vector<TConfig> &b)\n{\n    FatalError(\"Matrix format not supported in gaussian_elimination\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination(const Matrix_h &A, Vector_h &x, const Vector_h &b)\n{\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        gaussianElimination_4x4_host(A, x, b);\n    }\n    else\n    {\n        FatalError(\"gaussElimination: Blocksize is unsupported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination(const Matrix_d &A, Vector_d &x, const Vector_d &b)\n{\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        gaussianElimination_4x4_device(A, x, b);\n    }\n    else\n    {\n        FatalError(\"gaussElimination: Blocksize is unsupported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n// -------------------------------\n//  Explict instantiations\n// -------------------------------\n#define AMGX_CASE_LINE(CASE) template class GaussianElimination<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <util.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\nnamespace amgx\n{\n\n// Kernel to invert 4by4 matrix on device using one thread\n// TODO: possible optimizations using multiple threads, possible generalisation for NxN blocks\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid gaussianElimination4by4Kernel(const IndexType *dia_indices, const ValueTypeA *values, ValueTypeB *x, const ValueTypeB *b)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB Atemp[BSIZE][BSIZE];\n    ValueTypeB xtemp[BSIZE];\n    ValueTypeB btemp[BSIZE];\n    int offset;\n\n    if (tid == 0)\n    {\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n        {\n            btemp[m]  = b[m];\n        }\n\n        offset = 0;\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n#pragma unroll\n            for (int n = 0; n < BSIZE; n++)\n            {\n                types::util<ValueTypeA>::to_uptype(values[offset++], Atemp[m][n]);    // only one nonzero block - diagonal\n            }\n\n        gaussianElimination4by4(Atemp, xtemp, btemp);\n        // Store x\n#pragma unroll\n\n        for (int m = 0; m < BSIZE; m++)\n        {\n            x[m] = xtemp[m];\n        }\n    }\n}\n\n// Method to perform gaussian elimination for a matrix stored in row major format\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\nvoid gaussianEliminationRowMajor(ValueTypeA **e, ValueTypeB *x, ValueTypeB *b, const IndexType bsize)\n{\n    if (bsize > 4) { FatalError(\"Warning, Gaussian Elimination code doesn't pivot and bsize > 4\", AMGX_ERR_BAD_PARAMETERS); }\n\n    ValueTypeB pivot, ratio, tmp;\n\n    for (int j = 0; j < bsize; j++)\n    {\n        types::util<ValueTypeA>::to_uptype(e[j][j], pivot);\n\n        for (int k = j + 1; k < bsize; k++)\n        {\n            ValueTypeB temp;\n            types::util<ValueTypeA>::to_uptype(e[k][j], temp);\n            ratio = temp / pivot;\n\n            for (int m = j + 1; m < bsize; m++)\n            {\n                e[k][m] = e[k][m] - e[j][m] * ratio;\n            }\n\n            b[k] = b[k] - b[j] * ratio;\n        }\n    }\n\n    // back substitution\n    for (int j = bsize - 1; j >= 0; j--)\n    {\n        tmp = types::util<ValueTypeB>::get_zero();\n\n        for (int k = j + 1; k < bsize; k++)\n        {\n            tmp = tmp + e[j][k] * x[k];\n        }\n\n        x[j] = (b[j] - tmp) / e[j][j];\n    }\n}\n\n// Method to perform gaussian elimination on matrix stored in block_dia_csr_matrix_h\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination_4x4_host(const Matrix_h &A, Vector_h &x, const Vector_h &b)\n{\n    typedef typename Matrix_h::value_type ValueTypeA;\n    typedef typename Vector_h::value_type ValueTypeB;\n\n    if (A.get_num_rows() != 1)\n    {\n        FatalError(\"Haven't implemented gaussian elimination solver for num_blocks != 1\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        int bsize = A.get_block_dimy();\n        // Allocate space for block_matrix\n        ValueTypeA **Atemp = new ValueTypeA* [bsize];\n\n        for ( int i = 0; i < bsize; i++)\n        {\n            Atemp[i] = new ValueTypeA[bsize];\n        }\n\n        ValueTypeB *btemp = new ValueTypeB[bsize];\n        ValueTypeB *xtemp = new ValueTypeB[bsize];\n\n        // Copy matrix and rhs\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                Atemp[m][n] = A.values[bsize * m + n]; //diag[bsize*m + n]; since we have 1 nonzero only\n            }\n\n            btemp[m] = b[m];\n        }\n\n        ValueTypeB *x_ptr = x.raw();\n        gaussianEliminationRowMajor(Atemp, x_ptr, btemp, bsize);\n    }\n}\n\n// Method to perform gaussian elimination on matrix stored in block_dia_csr_matrix_d\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination_4x4_device(const Matrix_d &A, Vector_d &x, const Vector_d &b)\n{\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename Vector_d::value_type ValueTypeB;\n    typedef typename Matrix_d::index_type IndexType;\n\n    if (A.get_num_rows() != 1)\n    {\n        FatalError(\"Haven't implemented gaussian elimination solver for num_blocks != 1 or block_size != 4\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    else\n    {\n        const IndexType *dia_ptr = A.diag.raw();\n        const ValueTypeA *values_ptr = A.values.raw();\n        const ValueTypeB *b_ptr = b.raw();\n        ValueTypeB *x_ptr = x.raw();\n        gaussianElimination4by4Kernel <<< 1, 1>>>(dia_ptr, values_ptr, x_ptr, b_ptr);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid GaussianElimination<TConfig>::gaussianElimination(const Matrix<TConfig> &A, Vector<TConfig> &x, const Vector<TConfig> &b)\n{\n    FatalError(\"Matrix format not supported in gaussian_elimination\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination(const Matrix_h &A, Vector_h &x, const Vector_h &b)\n{\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        gaussianElimination_4x4_host(A, x, b);\n    }\n    else\n    {\n        FatalError(\"gaussElimination: Blocksize is unsupported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussianElimination<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::gaussianElimination(const Matrix_d &A, Vector_d &x, const Vector_d &b)\n{\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        gaussianElimination_4x4_device(A, x, b);\n    }\n    else\n    {\n        FatalError(\"gaussElimination: Blocksize is unsupported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n// -------------------------------\n//  Explict instantiations\n// -------------------------------\n#define AMGX_CASE_LINE(CASE) template class GaussianElimination<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/gaussian_elimination.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cusp/detail/device/utils.h>\n#include <determinism_checker.h>\n#include <error.h>\n#include <thrust/reduce.h>\n#include <thrust/device_vector.h>\n\n#include <map>\n\nnamespace amgx\n{\nnamespace testing_tools\n{\n\n\nstruct hash_path_determinism_checker_private\n{\n    std::map<std::string, int> checkpoint_counts;\n};\n\nhash_path_determinism_checker *hash_path_determinism_checker::singleton()\n{\n    static hash_path_determinism_checker *m = 0;\n\n    if (!m) { m = new hash_path_determinism_checker; }\n\n    return m;\n}\n\nhash_path_determinism_checker::hash_path_determinism_checker()\n{\n    priv = new hash_path_determinism_checker_private();\n}\nhash_path_determinism_checker::~hash_path_determinism_checker()\n{\n    if (priv) { delete priv; }\n\n    priv = 0;\n}\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n__global__ void fast_hash_kernel(unsigned int *din, unsigned int *dout, long long int N, unsigned int seed)\n{\n    long long int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; tid < N; tid += blockDim.x * gridDim.x)\n    {\n        dout[tid] = hash_function(din[tid], seed);\n    }\n}\n\n__global__ void fast_hash_kernel_no_permute(unsigned int *din, unsigned int *dout, long long int N, unsigned int seed)\n{\n    long long int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; tid < N; tid += blockDim.x * gridDim.x)\n    {\n        dout[tid] = hash_function(din[tid], seed) * hash_function((unsigned int)tid + (unsigned int)(tid >> 32), seed);\n    }\n}\n\n\nvoid hash_path_determinism_checker::checkpoint(const std::string &name, void *data, long long int size_in_bytes, bool no_permute)\n{\n    amgx::thrust::device_vector<unsigned int> hash_buff(size_in_bytes / 4);\n\n    if ( no_permute )\n    {\n        fast_hash_kernel_no_permute <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n        cudaCheckError();\n    }\n    else\n    {\n        fast_hash_kernel <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n    }\n\n    cudaCheckError();\n    unsigned long long int checksum = amgx::thrust::reduce(hash_buff.begin(), hash_buff.end(), 0ull, amgx::thrust::plus<unsigned long long int>());\n    cudaCheckError();\n    int &checkpoint_count = priv->checkpoint_counts[name];\n    printf(\"checksum %s\\t\\t%d\\t%llx\\n\", name.c_str(), checkpoint_count++, checksum);\n}\n\nunsigned long long int hash_path_determinism_checker::checksum( void *data, long long int size_in_bytes, bool no_permute )\n{\n    amgx::thrust::device_vector<unsigned int> hash_buff(size_in_bytes / 4);\n\n    if ( no_permute )\n    {\n        fast_hash_kernel_no_permute <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n        cudaCheckError();\n    }\n    else\n    {\n        fast_hash_kernel <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n    }\n\n    cudaCheckError();\n    unsigned long long int checksum = amgx::thrust::reduce(hash_buff.begin(), hash_buff.end(), 0ull, amgx::thrust::plus<unsigned long long int>());\n    cudaCheckError();\n    return checksum;\n}\n\n\n\n}\n}\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cusp/detail/device/utils.h>\n#include <determinism_checker.h>\n#include <error.h>\n#include <thrust/reduce.h>\n#include <thrust/device_vector.h>\n\n#include <map>\n\nnamespace amgx\n{\nnamespace testing_tools\n{\n\n\nstruct hash_path_determinism_checker_private\n{\n    std::map<std::string, int> checkpoint_counts;\n};\n\nhash_path_determinism_checker *hash_path_determinism_checker::singleton()\n{\n    static hash_path_determinism_checker *m = 0;\n\n    if (!m) { m = new hash_path_determinism_checker; }\n\n    return m;\n}\n\nhash_path_determinism_checker::hash_path_determinism_checker()\n{\n    priv = new hash_path_determinism_checker_private();\n}\nhash_path_determinism_checker::~hash_path_determinism_checker()\n{\n    if (priv) { delete priv; }\n\n    priv = 0;\n}\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n__global__ void fast_hash_kernel(unsigned int *din, unsigned int *dout, long long int N, unsigned int seed)\n{\n    long long int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; tid < N; tid += blockDim.x * gridDim.x)\n    {\n        dout[tid] = hash_function(din[tid], seed);\n    }\n}\n\n__global__ void fast_hash_kernel_no_permute(unsigned int *din, unsigned int *dout, long long int N, unsigned int seed)\n{\n    long long int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; tid < N; tid += blockDim.x * gridDim.x)\n    {\n        dout[tid] = hash_function(din[tid], seed) * hash_function((unsigned int)tid + (unsigned int)(tid >> 32), seed);\n    }\n}\n\n\nvoid hash_path_determinism_checker::checkpoint(const std::string &name, void *data, long long int size_in_bytes, bool no_permute)\n{\n    amgx::thrust::device_vector<unsigned int> hash_buff(size_in_bytes / 4);\n\n    if ( no_permute )\n    {\n        fast_hash_kernel_no_permute <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n        cudaCheckError();\n    }\n    else\n    {\n        fast_hash_kernel <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n    }\n\n    cudaCheckError();\n    unsigned long long int checksum = amgx::thrust::reduce(hash_buff.begin(), hash_buff.end(), 0ull, amgx::thrust::plus<unsigned long long int>());\n    cudaCheckError();\n    int &checkpoint_count = priv->checkpoint_counts[name];\n    printf(\"checksum %s\\t\\t%d\\t%llx\\n\", name.c_str(), checkpoint_count++, checksum);\n}\n\nunsigned long long int hash_path_determinism_checker::checksum( void *data, long long int size_in_bytes, bool no_permute )\n{\n    amgx::thrust::device_vector<unsigned int> hash_buff(size_in_bytes / 4);\n\n    if ( no_permute )\n    {\n        fast_hash_kernel_no_permute <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n        cudaCheckError();\n    }\n    else\n    {\n        fast_hash_kernel <<< 26, 256>>>((unsigned int *)data, amgx::thrust::raw_pointer_cast(hash_buff.data()), size_in_bytes / 4, 1987);\n    }\n\n    cudaCheckError();\n    unsigned long long int checksum = amgx::thrust::reduce(hash_buff.begin(), hash_buff.end(), 0ull, amgx::thrust::plus<unsigned long long int>());\n    cudaCheckError();\n    return checksum;\n}\n\n\n\n}\n}\n\n", "source": "/kaggle/working/amgx/src/determinism_checker.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <matrix.h>\n#include <cutil.h>\n#include <util.h>\n#include <solvers/solver.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/functional.h>\n#include <thrust/device_ptr.h>\n#include <thrust/sort.h>\n#include <thrust/gather.h>\n#include <vector_thrust_allocator.h>\n#include <cusp/detail/format_utils.h>\n#include <permute.h>\n#include <multiply.h>\n#include <amgx_types/util.h>\n#include <algorithm>\n\ntemplate<typename T>\nstruct row_length\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        const T *next_ptr = &x;\n        next_ptr++;\n        return (*next_ptr) - x;\n    }\n};\n\nnamespace amgx\n{\n\n__global__ void computeRowOffsetsKernel(INDEX_TYPE num_rows, INDEX_TYPE num_nz, const INDEX_TYPE *row_indices, INDEX_TYPE *row_offsets )\n{\n    //one thread per non-zero\n    int nz = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (nz == 0)\n    {\n        row_offsets[0] = 0;\n        row_offsets[num_rows] = num_nz;\n    }\n\n    while (nz < num_nz - 1)\n    {\n        int row = row_indices[nz];\n        int next_row = row_indices[nz + 1];\n\n        while (row < next_row) //this loop should probably always execute once, but I'm making it a loop just in case...\n        {\n            row_offsets[++row] = nz + 1;\n        }\n\n        nz += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeRowIndicesKernel(INDEX_TYPE num_rows, const INDEX_TYPE *row_offsets, INDEX_TYPE *row_indices )\n{\n    //one warp per row //possible optimziation:  multiple warps per row\n    int row = (blockIdx.x * blockDim.x + threadIdx.x) / AMGX_WARP_SIZE;\n    int warp_id = threadIdx.x % AMGX_WARP_SIZE;\n\n    while (row < num_rows)\n    {\n        int start = row_offsets[row] + warp_id;\n        int end = row_offsets[row + 1];\n\n        for (int nz = start; nz < end; nz += AMGX_WARP_SIZE)\n        {\n            row_indices[nz] = row;\n        }\n\n        row += blockDim.x * gridDim.x / AMGX_WARP_SIZE;\n    }\n}\n\n__global__ void computeDiagonalKernelCOO(INDEX_TYPE num_nz, INDEX_TYPE *row_indices, INDEX_TYPE *col_indices, INDEX_TYPE *diag, INDEX_TYPE *diag_end_offsets)\n{\n    //BLOCKY*BLOCKX threads per nz\n    INDEX_TYPE nz = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (nz < num_nz)\n    {\n        INDEX_TYPE row = row_indices[nz];\n        INDEX_TYPE col = col_indices[nz];\n\n        if (row == col)\n        {\n            //copy block to diag\n            diag[row] = nz;\n            diag_end_offsets[row] = nz + 1;\n        }\n\n        nz += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeDiagonalKernelCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *diag, INDEX_TYPE null_index, INDEX_TYPE *diag_end_offsets)\n{\n    INDEX_TYPE row = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (row < num_rows)\n    {\n        int nz = row_offsets[row];\n        int last_nz = row_offsets[row + 1];\n\n        while (nz < last_nz)\n        {\n            int col = col_indices[nz];\n\n            if (row == col)\n            {\n                diag[row] = nz;\n                diag_end_offsets[row] = nz + 1;\n                break;\n            }\n\n            nz++;\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeColorOffsetsKernelCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, const INDEX_TYPE *row_colors, INDEX_TYPE *smaller_color_offsets, INDEX_TYPE *larger_color_offsets, INDEX_TYPE *diag)\n{\n    INDEX_TYPE row = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (row < num_rows)\n    {\n        int my_color = row_colors[row];\n        int nz = row_offsets[row];\n        int last_nz = row_offsets[row + 1];\n        int location_small = -1;\n        int location_large = -1;\n\n        while (nz < last_nz)\n        {\n            int col = col_indices[nz];\n\n            if (row_colors[col] >= my_color && location_small == -1)\n            {\n                location_small = nz;\n            }\n\n            if (row_colors[col] > my_color && location_large == -1)\n            {\n                location_large = nz;\n                break;\n            }\n\n            nz++;\n        }\n\n        if (location_large == -1) { location_large = last_nz + 1; }\n\n        if (location_small == -1) { location_small = last_nz + 1; }\n\n        larger_color_offsets[row]  = location_large;\n        smaller_color_offsets[row] = location_small;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeDiagonalKernelDiagProp (INDEX_TYPE num_rows, INDEX_TYPE num_nz, INDEX_TYPE *diag, INDEX_TYPE *diag_end_offsets)\n{\n    INDEX_TYPE r = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (r < num_rows)\n    {\n        diag[r] = num_nz + r;\n        diag_end_offsets[r] = num_nz + r + 1;\n        r += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <class T>\n__global__ void reorderElements (INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, T *data, T *temp, INDEX_TYPE max_row, INDEX_TYPE blockSize)\n{\n    INDEX_TYPE rowId = blockIdx.x;\n\n    while (rowId < num_rows)\n    {\n        INDEX_TYPE rowStart = row_offsets[rowId];\n        INDEX_TYPE rowLen = row_offsets[rowId + 1] - rowStart;\n\n        int i = threadIdx.x;\n\n        //copy and reorder into temp storage\n        while (i < rowLen * blockSize)\n        {\n            temp[max_row * blockIdx.x + i] = data[(permutation[rowStart + i / blockSize]) * blockSize + i % blockSize];\n            i += blockDim.x;\n        }\n\n        __syncthreads();\n        //copy back\n        i = threadIdx.x;\n\n        while (i < rowLen * blockSize)\n        {\n            data[rowStart * blockSize + i] = temp[max_row * blockIdx.x + i];\n            i += blockDim.x;\n        }\n\n        rowId += gridDim.x;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::apply(const Vector<TConfig> &v, Vector<TConfig> &res, ViewType view)\n{\n    Vector<TConfig> &v_ = const_cast<Vector<TConfig>&>(v);\n    multiply(*this, v_, res, view);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::apply(const Vector<TConfig> &v, Vector<TConfig> &res, ViewType view)\n{\n    Vector<TConfig> &v_ = const_cast<Vector<TConfig>&>(v);\n    multiply(*this, v_, res, view);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::print(char *f, char *s, int srows, int erows, int trank)\n{\n    typedef typename TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>::MatPrec ValueType;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int printRowsStart, printRowsEnd;\n    int i, j, ii, xdim, ydim, tnnz;\n    ValueType a;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to ouput to stdout or file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = this->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_l%d_r%d.mtx\", f, level, rank);\n#else\n            snprintf(filename, 1024, \"%s_l%d_r%d.mtx\", f, level, rank);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        printRowsStart = (srows < 0) ? 0                    : srows;\n        printRowsEnd  = (erows < 0) ? this->get_num_rows() : erows;\n        tnnz = this->get_num_nz() * this->get_block_size();\n\n        //matrix might have separate diagonal (need to accoutn for it in nnz)\n        if (this->hasProps(DIAG, this->props))\n        {\n            //matrix might be non-square so take std::min of # of rows and cols\n            tnnz += std::min(this->get_num_rows(), this->get_num_cols()) * this->get_block_size();\n        }\n\n        auto trafI = [&](auto const &I, auto const &i) { return I *  this->get_block_dimy() + i + 1; };\n        auto trafJ = [&](auto const &J, auto const &j) { return J *  this->get_block_dimx() + j + 1; };\n\n        fprintf(fid, \"%%%%MatrixMarket matrix coordinate real general\\n\");\n        fprintf(fid, \"%% %s\\n\", s);\n        fprintf(fid, \"%d %d %d\\n\", this->get_num_rows() * this->get_block_dimx(), this->get_num_cols() * this->get_block_dimy(), tnnz);\n\n        IVector_h this_row_offsets(this->row_offsets.size()); this_row_offsets = this->row_offsets;\n        IVector_h this_col_indices(this->col_indices.size()); this_col_indices = this->col_indices;\n        MVector_h this_values(this->values.size()); this_values = this->values;\n\n        int const bsSquared = this->get_block_dimx() * this->get_block_dimy();\n\n        for (i = printRowsStart; i < printRowsEnd; i++)\n        {\n            for (ydim = 0; ydim < this->get_block_dimy(); ydim++)\n            {\n                if (this->hasProps(DIAG, this->props))\n                {\n                    if (i < std::min(this->get_num_rows(), this->get_num_cols()))\n                    {\n                        for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                        {\n                            a = this_values[this->diag[i] * bsSquared + this->get_block_dimy() * ydim + xdim];\n                            fprintf(fid, \"%d %d \", trafI(i, ydim), trafI(i, xdim));\n                            types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                            fprintf(fid, \"\\n\");\n                        }\n                    }\n                }\n\n                for (ii = this_row_offsets[i]; ii < this_row_offsets[i + 1]; ii++)\n                {\n                    j = this_col_indices[ii];\n\n                    for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                    {\n                        a = this_values[ii * bsSquared + this->get_block_dimy() * ydim + xdim];\n                        fprintf(fid, \"%d %d \", trafI(i, ydim), trafJ(j, xdim));\n                        types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                        fprintf(fid, \"\\n\");\n                    }\n                }\n            }\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        cudaGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::printToFile(char *f, char *s, int srows, int erows)\n{\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different on different ranks/processes\n    this->print(f, s, srows, erows, rank);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::print(char *f, char *s, int srows, int erows, int trank)\n{\n    typedef typename TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>::MatPrec ValueType;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int printRowsStart, printRowsEnd;\n    int i, j, ii, xdim, ydim, tnnz;\n    ValueType a;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to ouput to stdout or file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = this->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#else\n            snprintf(filename, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        printRowsStart = (srows < 0) ? 0                    : srows;\n        printRowsEnd  = (erows < 0) ? this->get_num_rows() : erows;\n        tnnz = this->get_num_nz() * this->get_block_size();\n\n        //matrix might have separate diagonal (need to accoutn for it in nnz)\n        if (this->hasProps(DIAG, this->props))\n        {\n            //matrix might be non-square so take min of # of rows and cols\n            tnnz += std::min(this->get_num_rows(), this->get_num_cols()) * this->get_block_size();\n        }\n\n        fprintf(fid, \"%%%%MatrixMarket matrix coordinate real general\\n\");\n        fprintf(fid, \"%% %s\\n\", s);\n        fprintf(fid, \"%d %d %d\\n\", this->get_num_rows() * this->get_block_dimx(), this->get_num_cols() * this->get_block_dimy(), tnnz);\n\n        auto trafI = [&](auto const &I, auto const &i) { return I *  this->get_block_dimy() + i + 1; };\n\n        for (i = printRowsStart; i < printRowsEnd; i++)\n        {\n            for (ydim = 0; ydim < this->get_block_dimy(); ydim++)\n            {\n                if (this->hasProps(DIAG, this->props))\n                {\n                    if (i < std::min(this->get_num_rows(), this->get_num_cols()))\n                    {\n                        for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                        {\n                            a = this->values[this->diag[i] * this->get_block_dimx() * this->get_block_dimy() + this->get_block_dimy() * ydim + xdim];\n                            fprintf(fid, \"%d %d \", trafI(i, ydim), trafI(i, xdim));\n                            types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                            fprintf(fid, \"\\n\");\n                        }\n                    }\n                }\n\n                for (ii = this->row_offsets[i]; ii < this->row_offsets[i + 1]; ii++)\n                {\n                    j = this->col_indices[ii];\n\n                    for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                    {\n                        a = this->values[ii * this->get_block_dimx() * this->get_block_dimy() + this->get_block_dimy() * ydim + xdim];\n                        fprintf(fid, \"%d %d \", i + 1, j + 1);\n                        types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                        fprintf(fid, \"\\n\");\n                    }\n                }\n            }\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        cudaGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::printToFile(char *f, char *s, int srows, int erows)\n{\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different on different ranks/processes\n    this->print(f, s, srows, erows, rank);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > ::convert( const Matrix<TConfig> &mat, unsigned int new_props, int block_dimy, int block_dimx )\n{\n    if ( !mat.is_initialized() )\n    {\n        FatalError(\"Trying to convert from the uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    this->set_initialized(0);\n    this->addProps(new_props);\n    index_type block_size = block_dimx * block_dimy;\n    index_type new_num_rows = ( mat.get_num_rows() * mat.block_dimy + block_dimy - 1 ) / block_dimy;\n    index_type new_num_cols = ( mat.get_num_cols() * mat.block_dimx + block_dimx - 1 ) / block_dimx;\n    MVector new_values;\n    IVector new_col_indices;\n    index_type new_num_nnz = 0;\n    IVector new_row_indices;\n    IVector new_row_offsets;\n    MVector new_dia_values;\n    new_dia_values.resize( new_num_rows * block_size, types::util<value_type>::get_zero() );\n\n    if ( mat.hasProps(COO) )\n    {\n        std::map< std::pair<index_type, index_type>, index_type> ind;\n\n        for ( index_type i = 0; i < mat.get_num_nz(); i++ )\n            for ( index_type loc_row = 0; loc_row < mat.block_dimy; loc_row++ )\n                for ( index_type loc_col = 0; loc_col < mat.block_dimx; loc_col++ )\n                {\n                    index_type in_row = mat.row_indices[i] * mat.block_dimy + loc_row;\n                    index_type in_col = mat.col_indices[i] * mat.block_dimx + loc_col;\n                    value_type in_val = mat.values[i * mat.block_size + loc_row * mat.block_dimx + loc_col];\n\n                    if ( types::util<value_type>::is_zero(in_val) ) { continue; } // skip zero entries\n\n                    index_type out_br = in_row / block_dimy;\n                    index_type out_bc = in_col / block_dimx;\n                    index_type out_lr = in_row % block_dimy;\n                    index_type out_lc = in_col % block_dimx;\n\n                    if ( ind.find( std::pair<index_type, index_type>( out_br, out_bc ) ) == ind.end() )\n                    {\n                        // allocate a new block\n                        ind.insert( std::pair< std::pair<index_type, index_type>, index_type>( std::pair<index_type, index_type>( out_br, out_bc ), new_num_nnz ) );\n\n                        if (out_br != out_bc || !this->hasProps(DIAG))\n                        {\n                            new_row_indices.push_back( out_br );\n                            new_col_indices.push_back( out_bc );\n                            new_num_nnz++;\n\n                            for ( int b = 0; b < block_size; b++ )\n                            {\n                                new_values.push_back( types::util<value_type>::get_zero() );\n                            }\n                        }\n                    }\n\n                    if ( out_br != out_bc || !this->hasProps(DIAG) )\n                    {\n                        new_values[ ind[std::pair<index_type, index_type>( out_br, out_bc )] * block_size + out_lr * block_dimx + out_lc ] = in_val;\n                    }\n                    else\n                    {\n                        new_dia_values[ out_br * block_size + out_lr * block_dimx + out_lc ] = in_val;\n                    }\n                }\n    } // ( mat.hasProps(COO) )\n    else if ( mat.hasProps(CSR))\n    {\n        new_num_nnz = 0;\n        //MVector new_dia_values;\n        //new_dia_values.resize( new_num_rows*block_size, 0.0 );\n        new_row_offsets.resize( new_num_rows + 1 );\n\n        // process each output block row\n        for ( index_type i = 0; i < new_num_rows; i++ )\n        {\n            new_row_offsets[i] = new_num_nnz;\n            // count non zero column blocks\n            IVector non_zero_blocks( new_num_cols );\n\n            for ( index_type j = i * block_dimy; j < (i + 1) * block_dimy && j < mat.get_num_rows() * mat.block_dimy; j++ )\n            {\n                // input row block / local position\n                index_type in_br = j / mat.block_dimy;\n                index_type in_lr = j % mat.block_dimy;\n\n                // loop through block columns\n                for ( index_type r = mat.row_offsets[in_br]; r < mat.row_offsets[in_br + 1] + ( mat.hasProps(DIAG) ); r++ )\n                {\n                    index_type in_bc = ( r == mat.row_offsets[in_br + 1] ) ? in_br : mat.col_indices[r];\n\n                    // loop through local columns\n                    for ( index_type in_lc = 0; in_lc < mat.block_dimx; in_lc++ )\n                    {\n                        index_type in_col = in_bc * mat.block_dimx + in_lc;\n                        index_type out_bc = in_col / block_dimx;\n                        // fetch input entry value\n                        value_type val = ( r == mat.row_offsets[in_br + 1] ) ?\n                                         mat.values[mat.diag[in_br] * mat.block_size + in_lr * mat.block_dimx + in_lc] :\n                                         mat.values[r * mat.block_size + in_lr * mat.block_dimx + in_lc];\n\n                        if ( types::util<value_type>::is_zero(val) ) { continue; }  // skip zero entries\n\n                        // mark non_zero column\n                        non_zero_blocks[out_bc] = 1;\n                    }\n                }\n            }\n\n            // populate non zero column blocks\n            for ( int bc = 0; bc < new_num_cols; bc++ )\n                if ( non_zero_blocks[bc] != 0 )\n                {\n                    if ( i != bc || !this->hasProps(DIAG) )  // off-diagonal for DIAG\n                    {\n                        non_zero_blocks[bc] = new_num_nnz++;\n                        new_col_indices.push_back( bc );\n\n                        for ( int b = 0; b < block_size; b++ )\n                        {\n                            new_values.push_back( types::util<value_type>::get_zero() );\n                        }\n                    }\n                }\n\n            // fill non zero values\n            for ( index_type j = i * block_dimy; j < (i + 1) * block_dimy && j < mat.get_num_rows() * mat.block_dimy; j++ )\n            {\n                // output row block/local position\n                index_type out_br = j / block_dimy;\n                index_type out_lr = j % block_dimy;\n                // input row block/local position\n                index_type in_br = j / mat.block_dimy;\n                index_type in_lr = j % mat.block_dimy;\n\n                // loop through block columns\n                for ( index_type r = mat.row_offsets[in_br]; r < mat.row_offsets[in_br + 1] + ( mat.hasProps(DIAG) ); r++ )\n                {\n                    index_type in_bc = ( r == mat.row_offsets[in_br + 1] ) ? in_br : mat.col_indices[r];\n\n                    // loop through local columns\n                    for ( index_type in_lc = 0; in_lc < mat.block_dimx; in_lc++ )\n                    {\n                        index_type in_col = in_bc * mat.block_dimx + in_lc;\n                        index_type out_bc = in_col / block_dimx;\n                        index_type out_lc = in_col % block_dimx;\n                        // fetch input entry value\n                        value_type val = ( r == mat.row_offsets[in_br + 1] ) ?\n                                         mat.values[mat.diag[in_br] * mat.block_size + in_lr * mat.block_dimx + in_lc] :\n                                         mat.values[r * mat.block_size + in_lr * mat.block_dimx + in_lc];\n\n                        if ( types::util<value_type>::is_zero(val) ) { continue; }  // skip zero entries\n\n                        // write to new matrix\n                        if ( out_br != out_bc || !this->hasProps(DIAG) )\n                        {\n                            new_values[ non_zero_blocks[out_bc] * block_size + out_lr * block_dimx + out_lc] = val;\n                        }\n                        else\n                        {\n                            new_dia_values[ out_br * block_size + out_lr * block_dimx + out_lc ] = val;\n                        }\n                    }\n                }\n            }\n        } // for( i < new_num_rows )\n\n        // fill extra diagonal for the last block\n        int extra_start = ( mat.get_num_rows() * mat.block_dimy ) % block_dimy;\n\n        if ( extra_start > 0 )\n        {\n            for ( int r = extra_start; r < block_dimy; r++ )\n                if ( this->hasProps(DIAG) )\n                {\n                    new_dia_values[ (new_num_rows - 1) * block_size + r * block_dimx + r] = types::util<value_type>::get_one();\n                }\n                else\n                {\n                    new_values[ (new_num_nnz - 1) * block_size + r * block_dimx + r] = types::util<value_type>::get_one();\n                }\n        }\n\n        new_row_offsets[new_num_rows] = new_num_nnz;\n    } // ( mat.hasProps(CSR) )\n    else\n    {\n        FatalError(\"Input matrix for conversion doesn't have COO or CSR format\", AMGX_ERR_CONFIGURATION);\n    }\n\n    if ( this->hasProps(DIAG) )\n    {\n        new_values.insert(new_values.end(), new_dia_values.begin(), new_dia_values.end());\n    }\n    else\n        for ( int b = 0; b < block_size; b++ )\n        {\n            new_values.push_back( types::util<value_type>::get_zero() );\n        }\n\n    this->resize( new_num_rows, new_num_cols, new_num_nnz, block_dimy, block_dimx );\n    this->values.copy( new_values );\n    this->values.set_block_dimx(block_dimx);\n    this->values.set_block_dimy(block_dimy);\n    this->col_indices.copy( new_col_indices );\n\n    if ( mat.hasProps(COO) )\n    {\n        this->row_indices.copy( new_row_indices );\n        this->props |= COO;\n    }\n\n    if ( mat.hasProps(CSR) )\n    {\n        this->row_offsets.copy( new_row_offsets );\n        this->props |= CSR;\n    }\n\n    if (this->hasProps(COO) && this->row_indices.size() == 0)\n    {\n        this->row_indices.resize(new_num_nnz);\n        computeRowIndices();\n    }\n\n    if (this->hasProps(CSR) && this->row_offsets.size() == 0)\n    {\n        this->row_offsets.resize(new_num_rows + 1);\n        computeRowOffsets();\n    }\n\n    computeDiagonal();\n    this->set_initialized(1);\n}\n\ntemplate <class T_Config> class MatrixBase;\n\ntemplate<class T_Config>\nAMGX_ERROR\nMatrixBase<T_Config>::resize(index_type num_rows, index_type num_cols, index_type num_nz, int skipDiaCompute)\n{\n    if (this->is_initialized())\n    {\n        FatalError(\"Debug throw: resizing already initialized matrix\\n\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    {\n        this->num_rows = num_rows;\n        this->num_cols = num_cols;\n        this->num_nz = num_nz;\n\n        if ( hasProps(DIAG) )\n        {\n            values.resize(num_nz * block_size + num_rows * block_size);\n        }\n        else\n        {\n            values.resize((num_nz + 1)*block_size);\n            //thrust_wrapper::fill<T_Config::memSpace>(values.begin() + num_nz*block_size, values.end(), static_cast<value_type>(0.0));\n        }\n\n        diag.resize(num_rows);\n        m_diag_end_offsets.resize(num_rows);\n        col_indices.resize(num_nz);\n\n        //if( props == NONE ) {props = CSR; props |= DIAG;}\n        if ( hasProps(COO) ) { row_indices.resize(num_nz); }\n\n        if ( hasProps(CSR) ) { row_offsets.resize(num_rows + 1); }\n\n        m_seq_offsets.resize(num_rows + 1);\n        thrust_wrapper::sequence<T_Config::memSpace>(m_seq_offsets.begin(), m_seq_offsets.end());\n        cudaCheckError();\n\n        if (!skipDiaCompute )\n        {\n            computeDiagonal();\n        }\n    }\n\n    return AMGX_OK;\n}\n\n\n\n\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::setupMatrix(Solver<T_Config> *outer_solver, AMG_Config &cfg, bool reuse_matrix_structure)\n{\n    // Check that matrix is initialized\n    if (!this->is_initialized())\n    {\n        FatalError(\"Trying to setup from the uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    this->set_initialized(0);\n    m_separation_interior = cfg.getParameter<ViewType>(\"separation_interior\", \"default\");\n    m_separation_exterior = cfg.getParameter<ViewType>(\"separation_exterior\", \"default\");\n\n    if (m_separation_interior > m_separation_exterior) { FatalError(\"Interior separation cannot be wider than the exterior separation\", AMGX_ERR_CONFIGURATION); }\n\n    // If latency hiding is disabled, the interior is overwritten\n    if(!isLatencyHidingEnabled(cfg))\n    {\n        m_separation_interior = m_separation_exterior;\n    }\n\n    bool is_coloring_needed  = outer_solver->isColoringNeeded();\n\n    if (!reuse_matrix_structure)\n    {\n        // Color the matrix since the structure has changed\n        if ( is_coloring_needed )\n        {\n            // Get the scope of the solver that needs coloring\n            std::string cfg_scope_for_coloring;\n            outer_solver->getColoringScope(cfg_scope_for_coloring);\n            this->colorMatrix(cfg, cfg_scope_for_coloring);\n        }\n    }\n\n    // Set the matrix block format\n    BlockFormat block_format = cfg.getParameter<BlockFormat>( \"block_format\", \"default\" );\n\n    if ( this->getBlockFormat() != block_format )\n    {\n        this->setBlockFormat( block_format );\n    }\n\n    // Permute the values and the column indices (if necessary)\n    bool reorder_cols_by_color = outer_solver->getReorderColsByColorDesired();\n    bool insert_diagonal  = outer_solver->getInsertDiagonalDesired();\n\n    if (reorder_cols_by_color)\n    {\n        if (reuse_matrix_structure) // Only permute the values\n        {\n            this->permuteValues();\n        }\n        else // Permute the values and the columns\n        {\n            this->reorderColumnsByColor(insert_diagonal);\n            this->permuteValues();\n        }\n    }\n\n    this->set_initialized(1);\n    m_is_matrix_setup = true;\n}\n\ntemplate<class T_Config>\nbool MatrixBase<T_Config>::isLatencyHidingEnabled(AMG_Config &cfg)\n{\n    const int min_rows_latency_hiding =\n        cfg.getParameter<int>(\"min_rows_latency_hiding\", \"default\");\n\n    // Test all partitions to check if they all fall below the threshold\n    if (!is_matrix_singleGPU() && min_rows_latency_hiding >= 0)\n    {\n        const auto& nrows_per_part = manager->getNumRowsPerPart();\n\n        // Look at all partitions to check whether the number of rows falls\n        // below the user defined minimum\n        for(auto& nrpp : nrows_per_part)\n        {\n            // If any partitions still have a large enough set of rows, \n            // continue latency hiding\n            if(nrpp >= min_rows_latency_hiding)\n            {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::reorderColumnsByColor(bool insert_diagonal)\n{\n    // If columns already reordered, don't reorder again\n    if (this->m_cols_reordered_by_color)\n    {\n        return;\n    }\n\n    // Check if matrix is colored\n    if (!this->hasProps(COLORING))\n    {\n        FatalError(\"Matrix must be colored in order to be reordered by colors. Try setting coloring_level=1 in the configuration file\", AMGX_ERR_CONFIGURATION);\n    }\n\n    set_initialized(0);\n    // Compute the row indices\n    addProps(COO);\n    set_allow_recompute_diag(false);\n    index_type num_non_zeros = num_nz;\n\n    if (hasProps(DIAG) && insert_diagonal)   // Diagonal stored separetely\n    {\n        num_non_zeros += num_rows;\n    }\n\n    // Append the diagonal if stored separately\n    // The new matrix will have inside diagonal\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        amgx::thrust::counting_iterator<int> first(0);\n        amgx::thrust::counting_iterator<int> last = first + num_rows;\n        // Create new row_indices with appended diagonal\n        IVector new_row_indices(num_non_zeros);\n        IVector new_col_indices(num_non_zeros);\n        amgx::thrust::copy(row_indices.begin(), row_indices.end(), new_row_indices.begin());\n        amgx::thrust::copy(first, last, new_row_indices.begin() + num_nz);\n        // Create new col_indices with appended diagonal\n        amgx::thrust::copy(col_indices.begin(), col_indices.end(), new_col_indices.begin());\n        amgx::thrust::copy(first, last, new_col_indices.begin() + num_nz);\n        row_indices.swap(new_row_indices);\n        col_indices.swap(new_col_indices);\n        new_row_indices.clear();\n        new_row_indices.shrink_to_fit();\n        new_col_indices.clear();\n        new_col_indices.shrink_to_fit();\n    }\n\n    // Compute the color of every column\n    IVector element_colors(num_non_zeros);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->getMatrixColoring().getRowColors().begin(), col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->getMatrixColoring().getRowColors().begin(), col_indices.end()),\n                 element_colors.begin());\n    // Compute the permutation vector by sorting by rows and columns\n    m_values_permutation_vector.resize(num_non_zeros);\n    thrust_wrapper::sequence<T_Config::memSpace>(m_values_permutation_vector.begin(), m_values_permutation_vector.end());\n    cusp::detail::sort_by_row_and_column(row_indices, element_colors, m_values_permutation_vector);\n    cudaCheckError();\n    element_colors.clear();\n    element_colors.shrink_to_fit();\n    // Compute the new column indices sorted by color\n    IVector new_column_indices(num_non_zeros);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(col_indices.begin(), m_values_permutation_vector.begin()),\n                 amgx::thrust::make_permutation_iterator(col_indices.begin(), m_values_permutation_vector.end()),\n                 new_column_indices.begin());\n    col_indices.swap(new_column_indices);\n    new_column_indices.clear();\n    new_column_indices.shrink_to_fit();\n\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        // Change the number of nonzeros\n        set_num_nz(num_non_zeros);\n        values.resize( (num_non_zeros + 1)*this->get_block_size());\n        this->m_is_permutation_inplace = false;\n    }\n    else\n    {\n        this->m_is_permutation_inplace = true;\n    }\n\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        delProps(DIAG);\n        // Force recomputation of row offsets\n        delProps(CSR);\n    }\n\n    // Compute row offsets if input matrix only had COO format or if diagonal was inserted\n    addProps(CSR);\n    // Recompute the diagonal\n    set_allow_recompute_diag(true);\n    computeDiagonal();\n    // Compute the color offsets\n    m_smaller_color_offsets.resize(this->get_num_rows());\n    m_larger_color_offsets.resize(this->get_num_rows());\n    computeColorOffsets();\n    this->m_cols_reordered_by_color = true;\n    set_initialized(1);\n}\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::sortByRowAndColumn()\n{\n    this->set_initialized(0);\n    // Add row_indices array\n    this->addProps(COO);\n    this->set_allow_recompute_diag(false);\n\n    if (this->get_block_dimx() != 1 || this->get_block_dimy() != 1)\n    {\n        FatalError(\"sortByRowAndColumn only works for scalar matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    size_t N = this->row_indices.size();\n    IVector permutation(N);\n    thrust_wrapper::sequence<T_Config::memSpace>(permutation.begin(), permutation.end());\n    cudaCheckError();\n    // compute permutation and sort by (I,J)\n    {\n        IVector temp(this->col_indices);\n        amgx::thrust::stable_sort_by_key(temp.begin(), temp.end(), permutation.begin());\n        temp = this->row_indices;\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->row_indices.begin());\n        amgx::thrust::stable_sort_by_key(this->row_indices.begin(), this->row_indices.end(), permutation.begin());\n        temp = this->col_indices;\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->col_indices.begin());\n    }\n    cudaCheckError();\n    // use permutation to reorder the values\n    {\n        MVector temp(this->values);\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->values.begin());\n    }\n    cudaCheckError();\n    this->set_allow_recompute_diag(true);\n    this->addProps(CSR);\n    // remove row indices array\n    this->delProps(COO);\n    this->computeDiagonal();\n    this->set_initialized(1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDiagonal()\n{\n    ViewType oldView  = this->currentView();\n\n    if (this->m_initialized) { this->setView(ALL); }\n\n    if (this->allow_recompute_diag)\n    {\n        index_type num_rows = this->get_num_rows();\n        index_type num_nz = this->get_num_nz();\n        IVector &row_offsets = this->row_offsets;\n        IVector &row_indices = this->row_indices;\n        IVector &col_indices = this->col_indices;\n        IVector &diag = this->diag;\n\n        if (this->diag.size() != this->get_num_rows()) { this->diag.resize(this->get_num_rows()); }\n\n        if (this->m_diag_end_offsets.size() != this->get_num_rows()) { this->m_diag_end_offsets.resize(this->get_num_rows()); }\n\n        if ( this->hasProps(DIAG) )\n        {\n            int first = num_nz;\n\n            for (int r = 0; r < num_rows; r++)\n            {\n                diag[r] = first++;\n            }\n        }\n        else\n        {\n            index_type null_index = this->get_num_nz();\n\n            if ( this->hasProps(CSR) )\n            {\n                for (int r = 0; r < num_rows; r++)\n                {\n                    int start = row_offsets[r];\n                    int end = row_offsets[r + 1];\n                    diag[r] = null_index;\n\n                    for (int j = start; j < end; j++)\n                    {\n                        if (col_indices[j] == r)\n                        {\n                            diag[r] = j;\n                            break;\n                        }\n                    }\n                }\n            }\n            else if (this->hasProps(COO) )\n            {\n                for (int i = 0; i < num_rows; i++)\n                {\n                    diag[i] = null_index;\n                }\n\n                for (int j = 0; j < num_nz; j++)\n                {\n                    int r = row_indices[j];\n\n                    if (r == col_indices[j])\n                    {\n                        diag[r] = j;\n                    }\n                }\n            }\n        }\n\n        for (int r = 0; r < num_rows; r++)\n        {\n            this->m_diag_end_offsets[r] = diag[r] + 1;\n        }\n    }\n\n    this->setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDiagonal()\n{\n    ViewType oldView;\n    index_type null_index = 0; //this->get_num_nz();\n\n    if (!this->allow_recompute_diag || !(this->get_num_rows() > 0))\n    {\n        return;\n    }\n\n    if (this->m_initialized)\n    {\n        oldView = this->currentView();\n        this->setView(ALL);\n    }\n\n    if (this->diag.size() < this->get_num_rows())\n    {\n        this->diag.resize(this->get_num_rows());\n    }\n\n    if (this->m_diag_end_offsets.size() < this->get_num_rows())\n    {\n        this->m_diag_end_offsets.resize(this->get_num_rows());\n    }\n\n    if (this->hasProps(DIAG))\n    {\n        int num_blocks = std::min(4096, (this->get_num_rows() + 511) / 512);\n        computeDiagonalKernelDiagProp <<< num_blocks, 512, 0, amgx::thrust::global_thread_handle::get_stream()>>>(this->get_num_rows(), this->get_num_nz(), this->diag.raw(), this->m_diag_end_offsets.raw());\n        cudaCheckError();\n    }\n    else if (this->hasProps(COO))\n    {\n        int num_blocks = std::min(4096, (this->get_num_nz() + 511) / 512);\n        computeDiagonalKernelCOO <<< num_blocks, 512>>>(this->get_num_nz(), this->row_indices.raw(), this->col_indices.raw(), this->diag.raw(), this->m_diag_end_offsets.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        int num_blocks = std::min(4096, (this->get_num_rows() + 511) / 512);\n        computeDiagonalKernelCSR <<< num_blocks, 512>>>(this->get_num_rows(), this->row_offsets.raw(), this->col_indices.raw(), this->diag.raw(), null_index, this->m_diag_end_offsets.raw());\n    }\n\n    cudaCheckError();\n\n    if (this->m_initialized)\n    {\n        this->setView(oldView);\n    }\n\n#ifdef DEBUG\n\n    if (this->diag_copy.size() == 0)\n    {\n        this->diag_copy = this->diag;\n    }\n    else\n    {\n        if ((this->diag.size() != this->diag_copy.size()) || (this->diag.size() == 1)) { return; }\n\n        IVector_h h_diag = this->diag;\n        IVector_h h_diag_copy = this->diag_copy;\n        bool equal = true;\n\n        for (unsigned int i = 0; i < this->diag.size(); ++i)\n        {\n            if (h_diag[i] != h_diag_copy[i])\n            {\n                equal = false;\n                break;\n            }\n        }\n\n        if (equal)\n        {\n            FatalError(\"ComputeDiagonal was called, but diagonal hasn't changed\", AMGX_ERR_UNKNOWN);\n        }\n    }\n\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::permuteValues()\n{\n    if (this->m_cols_reordered_by_color && this->m_is_permutation_inplace)\n    {\n        reorderValuesInPlace();\n    }\n    else if (this->m_cols_reordered_by_color && !this->m_is_permutation_inplace )\n    {\n        // might use a lot of memory\n        MVector temp_values;\n        temp_values.resize(this->values.size());\n        temp_values.set_block_dimx(this->values.get_block_dimx());\n        temp_values.set_block_dimy(this->values.get_block_dimy());\n        amgx::unpermuteVector(this->values, temp_values, this->m_values_permutation_vector, (this->get_num_nz()) * (this->get_block_size()));\n        this->values.swap(temp_values);\n        temp_values.clear();\n        temp_values.shrink_to_fit();\n    }\n    else\n    {\n        FatalError(\"Invalid reordering level in permuteValues\", AMGX_ERR_CONFIGURATION);\n    }\n}\n\n\nvoid computeRowOffsetsDevice(int num_blocks, INDEX_TYPE num_rows, INDEX_TYPE num_nz, const INDEX_TYPE *row_indices, INDEX_TYPE *row_offsets, INDEX_TYPE block_size )\n{\n    computeRowOffsetsKernel <<< num_blocks, 512>>>(num_rows, num_nz, row_indices, row_offsets);\n    cudaCheckError();\n}\n\nextern void computeRowIndicesDevice(int num_blocks, INDEX_TYPE num_rows, const INDEX_TYPE *row_offsets, INDEX_TYPE *row_indices, INDEX_TYPE block_size )\n{\n    computeRowIndicesKernel <<< num_blocks, 512>>>(num_rows, row_offsets, row_indices);\n    cudaCheckError();\n}\n\nvoid computeColorOffsetsDeviceCSR(int num_blocks, INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, const INDEX_TYPE *row_colors, INDEX_TYPE *smaller_color_offsets, INDEX_TYPE *larger_color_offsets, INDEX_TYPE block_size, INDEX_TYPE *diag )\n{\n    computeColorOffsetsKernelCSR <<< num_blocks, 512>>>(num_rows, row_offsets, col_indices, row_colors, smaller_color_offsets, larger_color_offsets, diag);\n    cudaCheckError();\n}\n\ntemplate <typename T>\nvoid reorderElementsDeviceCSR(INDEX_TYPE num_rows,\n                              INDEX_TYPE *row_offsets,\n                              INDEX_TYPE *permutation,\n                              INDEX_TYPE *col_indices,\n                              T *values,\n                              INDEX_TYPE block_size)\n{\n    INDEX_TYPE max_row_length = std::max(1, thrust_wrapper::transform_reduce<AMGX_device>(row_offsets, row_offsets + num_rows, row_length<INDEX_TYPE>(), 0, amgx::thrust::maximum<INDEX_TYPE>()));\n    //TODO: optimise this in terms of storage\n    INDEX_TYPE storage_space = 100 * 1024 * 1024 * sizeof(T) / sizeof(cuDoubleComplex); // because we allocate as for cuComplex\n    INDEX_TYPE blocks = 1500 < storage_space / (max_row_length * block_size * sizeof(T)) ? 1500 : storage_space / (max_row_length * block_size * sizeof(T));\n    blocks = blocks < num_rows ? max(1, blocks) : num_rows;\n    INDEX_TYPE aligned_space = ((max_row_length * block_size * sizeof(T) / 128 + 1) * 128) / sizeof(T); //pad to 128 bytes\n    Vector<amgx::TemplateConfig<AMGX_device, AMGX_vecDoubleComplex, AMGX_matDoubleComplex, AMGX_indInt> > tempstorage(blocks * aligned_space);\n    reorderElements <<< blocks, 256>>>(num_rows, row_offsets, permutation, values, (T *)tempstorage.raw(), aligned_space, block_size);\n    cudaCheckError();\n}\n\n// explicitly instantiate reorderElementsDeviceCSR, since we call it from header and it's not a part of some class\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, float *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, double *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, cuComplex *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, cuDoubleComplex *values, INDEX_TYPE block_size);\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MatrixBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Matrix<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n\n\n\n}//end namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <matrix.h>\n#include <cutil.h>\n#include <util.h>\n#include <solvers/solver.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/functional.h>\n#include <thrust/device_ptr.h>\n#include <thrust/sort.h>\n#include <thrust/gather.h>\n#include <vector_thrust_allocator.h>\n#include <cusp/detail/format_utils.h>\n#include <permute.h>\n#include <multiply.h>\n#include <amgx_types/util.h>\n#include <algorithm>\n\ntemplate<typename T>\nstruct row_length\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        const T *next_ptr = &x;\n        next_ptr++;\n        return (*next_ptr) - x;\n    }\n};\n\nnamespace amgx\n{\n\n__global__ void computeRowOffsetsKernel(INDEX_TYPE num_rows, INDEX_TYPE num_nz, const INDEX_TYPE *row_indices, INDEX_TYPE *row_offsets )\n{\n    //one thread per non-zero\n    int nz = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (nz == 0)\n    {\n        row_offsets[0] = 0;\n        row_offsets[num_rows] = num_nz;\n    }\n\n    while (nz < num_nz - 1)\n    {\n        int row = row_indices[nz];\n        int next_row = row_indices[nz + 1];\n\n        while (row < next_row) //this loop should probably always execute once, but I'm making it a loop just in case...\n        {\n            row_offsets[++row] = nz + 1;\n        }\n\n        nz += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeRowIndicesKernel(INDEX_TYPE num_rows, const INDEX_TYPE *row_offsets, INDEX_TYPE *row_indices )\n{\n    //one warp per row //possible optimziation:  multiple warps per row\n    int row = (blockIdx.x * blockDim.x + threadIdx.x) / AMGX_WARP_SIZE;\n    int warp_id = threadIdx.x % AMGX_WARP_SIZE;\n\n    while (row < num_rows)\n    {\n        int start = row_offsets[row] + warp_id;\n        int end = row_offsets[row + 1];\n\n        for (int nz = start; nz < end; nz += AMGX_WARP_SIZE)\n        {\n            row_indices[nz] = row;\n        }\n\n        row += blockDim.x * gridDim.x / AMGX_WARP_SIZE;\n    }\n}\n\n__global__ void computeDiagonalKernelCOO(INDEX_TYPE num_nz, INDEX_TYPE *row_indices, INDEX_TYPE *col_indices, INDEX_TYPE *diag, INDEX_TYPE *diag_end_offsets)\n{\n    //BLOCKY*BLOCKX threads per nz\n    INDEX_TYPE nz = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (nz < num_nz)\n    {\n        INDEX_TYPE row = row_indices[nz];\n        INDEX_TYPE col = col_indices[nz];\n\n        if (row == col)\n        {\n            //copy block to diag\n            diag[row] = nz;\n            diag_end_offsets[row] = nz + 1;\n        }\n\n        nz += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeDiagonalKernelCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *diag, INDEX_TYPE null_index, INDEX_TYPE *diag_end_offsets)\n{\n    INDEX_TYPE row = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (row < num_rows)\n    {\n        int nz = row_offsets[row];\n        int last_nz = row_offsets[row + 1];\n\n        while (nz < last_nz)\n        {\n            int col = col_indices[nz];\n\n            if (row == col)\n            {\n                diag[row] = nz;\n                diag_end_offsets[row] = nz + 1;\n                break;\n            }\n\n            nz++;\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeColorOffsetsKernelCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, const INDEX_TYPE *row_colors, INDEX_TYPE *smaller_color_offsets, INDEX_TYPE *larger_color_offsets, INDEX_TYPE *diag)\n{\n    INDEX_TYPE row = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (row < num_rows)\n    {\n        int my_color = row_colors[row];\n        int nz = row_offsets[row];\n        int last_nz = row_offsets[row + 1];\n        int location_small = -1;\n        int location_large = -1;\n\n        while (nz < last_nz)\n        {\n            int col = col_indices[nz];\n\n            if (row_colors[col] >= my_color && location_small == -1)\n            {\n                location_small = nz;\n            }\n\n            if (row_colors[col] > my_color && location_large == -1)\n            {\n                location_large = nz;\n                break;\n            }\n\n            nz++;\n        }\n\n        if (location_large == -1) { location_large = last_nz + 1; }\n\n        if (location_small == -1) { location_small = last_nz + 1; }\n\n        larger_color_offsets[row]  = location_large;\n        smaller_color_offsets[row] = location_small;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void computeDiagonalKernelDiagProp (INDEX_TYPE num_rows, INDEX_TYPE num_nz, INDEX_TYPE *diag, INDEX_TYPE *diag_end_offsets)\n{\n    INDEX_TYPE r = (blockIdx.x * blockDim.x + threadIdx.x);\n\n    while (r < num_rows)\n    {\n        diag[r] = num_nz + r;\n        diag_end_offsets[r] = num_nz + r + 1;\n        r += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <class T>\n__global__ void reorderElements (INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, T *data, T *temp, INDEX_TYPE max_row, INDEX_TYPE blockSize)\n{\n    INDEX_TYPE rowId = blockIdx.x;\n\n    while (rowId < num_rows)\n    {\n        INDEX_TYPE rowStart = row_offsets[rowId];\n        INDEX_TYPE rowLen = row_offsets[rowId + 1] - rowStart;\n\n        int i = threadIdx.x;\n\n        //copy and reorder into temp storage\n        while (i < rowLen * blockSize)\n        {\n            temp[max_row * blockIdx.x + i] = data[(permutation[rowStart + i / blockSize]) * blockSize + i % blockSize];\n            i += blockDim.x;\n        }\n\n        __syncthreads();\n        //copy back\n        i = threadIdx.x;\n\n        while (i < rowLen * blockSize)\n        {\n            data[rowStart * blockSize + i] = temp[max_row * blockIdx.x + i];\n            i += blockDim.x;\n        }\n\n        rowId += gridDim.x;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::apply(const Vector<TConfig> &v, Vector<TConfig> &res, ViewType view)\n{\n    Vector<TConfig> &v_ = const_cast<Vector<TConfig>&>(v);\n    multiply(*this, v_, res, view);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::apply(const Vector<TConfig> &v, Vector<TConfig> &res, ViewType view)\n{\n    Vector<TConfig> &v_ = const_cast<Vector<TConfig>&>(v);\n    multiply(*this, v_, res, view);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::print(char *f, char *s, int srows, int erows, int trank)\n{\n    typedef typename TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>::MatPrec ValueType;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int printRowsStart, printRowsEnd;\n    int i, j, ii, xdim, ydim, tnnz;\n    ValueType a;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to ouput to stdout or file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = this->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_l%d_r%d.mtx\", f, level, rank);\n#else\n            snprintf(filename, 1024, \"%s_l%d_r%d.mtx\", f, level, rank);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n        printRowsStart = (srows < 0) ? 0                    : srows;\n        printRowsEnd  = (erows < 0) ? this->get_num_rows() : erows;\n        tnnz = this->get_num_nz() * this->get_block_size();\n\n        //matrix might have separate diagonal (need to accoutn for it in nnz)\n        if (this->hasProps(DIAG, this->props))\n        {\n            //matrix might be non-square so take std::min of # of rows and cols\n            tnnz += std::min(this->get_num_rows(), this->get_num_cols()) * this->get_block_size();\n        }\n\n        auto trafI = [&](auto const &I, auto const &i) { return I *  this->get_block_dimy() + i + 1; };\n        auto trafJ = [&](auto const &J, auto const &j) { return J *  this->get_block_dimx() + j + 1; };\n\n        fprintf(fid, \"%%%%MatrixMarket matrix coordinate real general\\n\");\n        fprintf(fid, \"%% %s\\n\", s);\n        fprintf(fid, \"%d %d %d\\n\", this->get_num_rows() * this->get_block_dimx(), this->get_num_cols() * this->get_block_dimy(), tnnz);\n\n        IVector_h this_row_offsets(this->row_offsets.size()); this_row_offsets = this->row_offsets;\n        IVector_h this_col_indices(this->col_indices.size()); this_col_indices = this->col_indices;\n        MVector_h this_values(this->values.size()); this_values = this->values;\n\n        int const bsSquared = this->get_block_dimx() * this->get_block_dimy();\n\n        for (i = printRowsStart; i < printRowsEnd; i++)\n        {\n            for (ydim = 0; ydim < this->get_block_dimy(); ydim++)\n            {\n                if (this->hasProps(DIAG, this->props))\n                {\n                    if (i < std::min(this->get_num_rows(), this->get_num_cols()))\n                    {\n                        for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                        {\n                            a = this_values[this->diag[i] * bsSquared + this->get_block_dimy() * ydim + xdim];\n                            fprintf(fid, \"%d %d \", trafI(i, ydim), trafI(i, xdim));\n                            types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                            fprintf(fid, \"\\n\");\n                        }\n                    }\n                }\n\n                for (ii = this_row_offsets[i]; ii < this_row_offsets[i + 1]; ii++)\n                {\n                    j = this_col_indices[ii];\n\n                    for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                    {\n                        a = this_values[ii * bsSquared + this->get_block_dimy() * ydim + xdim];\n                        fprintf(fid, \"%d %d \", trafI(i, ydim), trafJ(j, xdim));\n                        types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                        fprintf(fid, \"\\n\");\n                    }\n                }\n            }\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n        hipGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::printToFile(char *f, char *s, int srows, int erows)\n{\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different on different ranks/processes\n    this->print(f, s, srows, erows, rank);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::print(char *f, char *s, int srows, int erows, int trank)\n{\n    typedef typename TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>::MatPrec ValueType;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int printRowsStart, printRowsEnd;\n    int i, j, ii, xdim, ydim, tnnz;\n    ValueType a;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to ouput to stdout or file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = this->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#else\n            snprintf(filename, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n        printRowsStart = (srows < 0) ? 0                    : srows;\n        printRowsEnd  = (erows < 0) ? this->get_num_rows() : erows;\n        tnnz = this->get_num_nz() * this->get_block_size();\n\n        //matrix might have separate diagonal (need to accoutn for it in nnz)\n        if (this->hasProps(DIAG, this->props))\n        {\n            //matrix might be non-square so take min of # of rows and cols\n            tnnz += std::min(this->get_num_rows(), this->get_num_cols()) * this->get_block_size();\n        }\n\n        fprintf(fid, \"%%%%MatrixMarket matrix coordinate real general\\n\");\n        fprintf(fid, \"%% %s\\n\", s);\n        fprintf(fid, \"%d %d %d\\n\", this->get_num_rows() * this->get_block_dimx(), this->get_num_cols() * this->get_block_dimy(), tnnz);\n\n        auto trafI = [&](auto const &I, auto const &i) { return I *  this->get_block_dimy() + i + 1; };\n\n        for (i = printRowsStart; i < printRowsEnd; i++)\n        {\n            for (ydim = 0; ydim < this->get_block_dimy(); ydim++)\n            {\n                if (this->hasProps(DIAG, this->props))\n                {\n                    if (i < std::min(this->get_num_rows(), this->get_num_cols()))\n                    {\n                        for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                        {\n                            a = this->values[this->diag[i] * this->get_block_dimx() * this->get_block_dimy() + this->get_block_dimy() * ydim + xdim];\n                            fprintf(fid, \"%d %d \", trafI(i, ydim), trafI(i, xdim));\n                            types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                            fprintf(fid, \"\\n\");\n                        }\n                    }\n                }\n\n                for (ii = this->row_offsets[i]; ii < this->row_offsets[i + 1]; ii++)\n                {\n                    j = this->col_indices[ii];\n\n                    for (xdim = 0; xdim < this->get_block_dimx(); xdim++)\n                    {\n                        a = this->values[ii * this->get_block_dimx() * this->get_block_dimy() + this->get_block_dimy() * ydim + xdim];\n                        fprintf(fid, \"%d %d \", i + 1, j + 1);\n                        types::util<value_type>::fprintf(fid, \"%20.16f\", a);\n                        fprintf(fid, \"\\n\");\n                    }\n                }\n            }\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n        hipGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::printToFile(char *f, char *s, int srows, int erows)\n{\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different on different ranks/processes\n    this->print(f, s, srows, erows, rank);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > ::convert( const Matrix<TConfig> &mat, unsigned int new_props, int block_dimy, int block_dimx )\n{\n    if ( !mat.is_initialized() )\n    {\n        FatalError(\"Trying to convert from the uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    this->set_initialized(0);\n    this->addProps(new_props);\n    index_type block_size = block_dimx * block_dimy;\n    index_type new_num_rows = ( mat.get_num_rows() * mat.block_dimy + block_dimy - 1 ) / block_dimy;\n    index_type new_num_cols = ( mat.get_num_cols() * mat.block_dimx + block_dimx - 1 ) / block_dimx;\n    MVector new_values;\n    IVector new_col_indices;\n    index_type new_num_nnz = 0;\n    IVector new_row_indices;\n    IVector new_row_offsets;\n    MVector new_dia_values;\n    new_dia_values.resize( new_num_rows * block_size, types::util<value_type>::get_zero() );\n\n    if ( mat.hasProps(COO) )\n    {\n        std::map< std::pair<index_type, index_type>, index_type> ind;\n\n        for ( index_type i = 0; i < mat.get_num_nz(); i++ )\n            for ( index_type loc_row = 0; loc_row < mat.block_dimy; loc_row++ )\n                for ( index_type loc_col = 0; loc_col < mat.block_dimx; loc_col++ )\n                {\n                    index_type in_row = mat.row_indices[i] * mat.block_dimy + loc_row;\n                    index_type in_col = mat.col_indices[i] * mat.block_dimx + loc_col;\n                    value_type in_val = mat.values[i * mat.block_size + loc_row * mat.block_dimx + loc_col];\n\n                    if ( types::util<value_type>::is_zero(in_val) ) { continue; } // skip zero entries\n\n                    index_type out_br = in_row / block_dimy;\n                    index_type out_bc = in_col / block_dimx;\n                    index_type out_lr = in_row % block_dimy;\n                    index_type out_lc = in_col % block_dimx;\n\n                    if ( ind.find( std::pair<index_type, index_type>( out_br, out_bc ) ) == ind.end() )\n                    {\n                        // allocate a new block\n                        ind.insert( std::pair< std::pair<index_type, index_type>, index_type>( std::pair<index_type, index_type>( out_br, out_bc ), new_num_nnz ) );\n\n                        if (out_br != out_bc || !this->hasProps(DIAG))\n                        {\n                            new_row_indices.push_back( out_br );\n                            new_col_indices.push_back( out_bc );\n                            new_num_nnz++;\n\n                            for ( int b = 0; b < block_size; b++ )\n                            {\n                                new_values.push_back( types::util<value_type>::get_zero() );\n                            }\n                        }\n                    }\n\n                    if ( out_br != out_bc || !this->hasProps(DIAG) )\n                    {\n                        new_values[ ind[std::pair<index_type, index_type>( out_br, out_bc )] * block_size + out_lr * block_dimx + out_lc ] = in_val;\n                    }\n                    else\n                    {\n                        new_dia_values[ out_br * block_size + out_lr * block_dimx + out_lc ] = in_val;\n                    }\n                }\n    } // ( mat.hasProps(COO) )\n    else if ( mat.hasProps(CSR))\n    {\n        new_num_nnz = 0;\n        //MVector new_dia_values;\n        //new_dia_values.resize( new_num_rows*block_size, 0.0 );\n        new_row_offsets.resize( new_num_rows + 1 );\n\n        // process each output block row\n        for ( index_type i = 0; i < new_num_rows; i++ )\n        {\n            new_row_offsets[i] = new_num_nnz;\n            // count non zero column blocks\n            IVector non_zero_blocks( new_num_cols );\n\n            for ( index_type j = i * block_dimy; j < (i + 1) * block_dimy && j < mat.get_num_rows() * mat.block_dimy; j++ )\n            {\n                // input row block / local position\n                index_type in_br = j / mat.block_dimy;\n                index_type in_lr = j % mat.block_dimy;\n\n                // loop through block columns\n                for ( index_type r = mat.row_offsets[in_br]; r < mat.row_offsets[in_br + 1] + ( mat.hasProps(DIAG) ); r++ )\n                {\n                    index_type in_bc = ( r == mat.row_offsets[in_br + 1] ) ? in_br : mat.col_indices[r];\n\n                    // loop through local columns\n                    for ( index_type in_lc = 0; in_lc < mat.block_dimx; in_lc++ )\n                    {\n                        index_type in_col = in_bc * mat.block_dimx + in_lc;\n                        index_type out_bc = in_col / block_dimx;\n                        // fetch input entry value\n                        value_type val = ( r == mat.row_offsets[in_br + 1] ) ?\n                                         mat.values[mat.diag[in_br] * mat.block_size + in_lr * mat.block_dimx + in_lc] :\n                                         mat.values[r * mat.block_size + in_lr * mat.block_dimx + in_lc];\n\n                        if ( types::util<value_type>::is_zero(val) ) { continue; }  // skip zero entries\n\n                        // mark non_zero column\n                        non_zero_blocks[out_bc] = 1;\n                    }\n                }\n            }\n\n            // populate non zero column blocks\n            for ( int bc = 0; bc < new_num_cols; bc++ )\n                if ( non_zero_blocks[bc] != 0 )\n                {\n                    if ( i != bc || !this->hasProps(DIAG) )  // off-diagonal for DIAG\n                    {\n                        non_zero_blocks[bc] = new_num_nnz++;\n                        new_col_indices.push_back( bc );\n\n                        for ( int b = 0; b < block_size; b++ )\n                        {\n                            new_values.push_back( types::util<value_type>::get_zero() );\n                        }\n                    }\n                }\n\n            // fill non zero values\n            for ( index_type j = i * block_dimy; j < (i + 1) * block_dimy && j < mat.get_num_rows() * mat.block_dimy; j++ )\n            {\n                // output row block/local position\n                index_type out_br = j / block_dimy;\n                index_type out_lr = j % block_dimy;\n                // input row block/local position\n                index_type in_br = j / mat.block_dimy;\n                index_type in_lr = j % mat.block_dimy;\n\n                // loop through block columns\n                for ( index_type r = mat.row_offsets[in_br]; r < mat.row_offsets[in_br + 1] + ( mat.hasProps(DIAG) ); r++ )\n                {\n                    index_type in_bc = ( r == mat.row_offsets[in_br + 1] ) ? in_br : mat.col_indices[r];\n\n                    // loop through local columns\n                    for ( index_type in_lc = 0; in_lc < mat.block_dimx; in_lc++ )\n                    {\n                        index_type in_col = in_bc * mat.block_dimx + in_lc;\n                        index_type out_bc = in_col / block_dimx;\n                        index_type out_lc = in_col % block_dimx;\n                        // fetch input entry value\n                        value_type val = ( r == mat.row_offsets[in_br + 1] ) ?\n                                         mat.values[mat.diag[in_br] * mat.block_size + in_lr * mat.block_dimx + in_lc] :\n                                         mat.values[r * mat.block_size + in_lr * mat.block_dimx + in_lc];\n\n                        if ( types::util<value_type>::is_zero(val) ) { continue; }  // skip zero entries\n\n                        // write to new matrix\n                        if ( out_br != out_bc || !this->hasProps(DIAG) )\n                        {\n                            new_values[ non_zero_blocks[out_bc] * block_size + out_lr * block_dimx + out_lc] = val;\n                        }\n                        else\n                        {\n                            new_dia_values[ out_br * block_size + out_lr * block_dimx + out_lc ] = val;\n                        }\n                    }\n                }\n            }\n        } // for( i < new_num_rows )\n\n        // fill extra diagonal for the last block\n        int extra_start = ( mat.get_num_rows() * mat.block_dimy ) % block_dimy;\n\n        if ( extra_start > 0 )\n        {\n            for ( int r = extra_start; r < block_dimy; r++ )\n                if ( this->hasProps(DIAG) )\n                {\n                    new_dia_values[ (new_num_rows - 1) * block_size + r * block_dimx + r] = types::util<value_type>::get_one();\n                }\n                else\n                {\n                    new_values[ (new_num_nnz - 1) * block_size + r * block_dimx + r] = types::util<value_type>::get_one();\n                }\n        }\n\n        new_row_offsets[new_num_rows] = new_num_nnz;\n    } // ( mat.hasProps(CSR) )\n    else\n    {\n        FatalError(\"Input matrix for conversion doesn't have COO or CSR format\", AMGX_ERR_CONFIGURATION);\n    }\n\n    if ( this->hasProps(DIAG) )\n    {\n        new_values.insert(new_values.end(), new_dia_values.begin(), new_dia_values.end());\n    }\n    else\n        for ( int b = 0; b < block_size; b++ )\n        {\n            new_values.push_back( types::util<value_type>::get_zero() );\n        }\n\n    this->resize( new_num_rows, new_num_cols, new_num_nnz, block_dimy, block_dimx );\n    this->values.copy( new_values );\n    this->values.set_block_dimx(block_dimx);\n    this->values.set_block_dimy(block_dimy);\n    this->col_indices.copy( new_col_indices );\n\n    if ( mat.hasProps(COO) )\n    {\n        this->row_indices.copy( new_row_indices );\n        this->props |= COO;\n    }\n\n    if ( mat.hasProps(CSR) )\n    {\n        this->row_offsets.copy( new_row_offsets );\n        this->props |= CSR;\n    }\n\n    if (this->hasProps(COO) && this->row_indices.size() == 0)\n    {\n        this->row_indices.resize(new_num_nnz);\n        computeRowIndices();\n    }\n\n    if (this->hasProps(CSR) && this->row_offsets.size() == 0)\n    {\n        this->row_offsets.resize(new_num_rows + 1);\n        computeRowOffsets();\n    }\n\n    computeDiagonal();\n    this->set_initialized(1);\n}\n\ntemplate <class T_Config> class MatrixBase;\n\ntemplate<class T_Config>\nAMGX_ERROR\nMatrixBase<T_Config>::resize(index_type num_rows, index_type num_cols, index_type num_nz, int skipDiaCompute)\n{\n    if (this->is_initialized())\n    {\n        FatalError(\"Debug throw: resizing already initialized matrix\\n\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    {\n        this->num_rows = num_rows;\n        this->num_cols = num_cols;\n        this->num_nz = num_nz;\n\n        if ( hasProps(DIAG) )\n        {\n            values.resize(num_nz * block_size + num_rows * block_size);\n        }\n        else\n        {\n            values.resize((num_nz + 1)*block_size);\n            //thrust_wrapper::fill<T_Config::memSpace>(values.begin() + num_nz*block_size, values.end(), static_cast<value_type>(0.0));\n        }\n\n        diag.resize(num_rows);\n        m_diag_end_offsets.resize(num_rows);\n        col_indices.resize(num_nz);\n\n        //if( props == NONE ) {props = CSR; props |= DIAG;}\n        if ( hasProps(COO) ) { row_indices.resize(num_nz); }\n\n        if ( hasProps(CSR) ) { row_offsets.resize(num_rows + 1); }\n\n        m_seq_offsets.resize(num_rows + 1);\n        thrust_wrapper::sequence<T_Config::memSpace>(m_seq_offsets.begin(), m_seq_offsets.end());\n        cudaCheckError();\n\n        if (!skipDiaCompute )\n        {\n            computeDiagonal();\n        }\n    }\n\n    return AMGX_OK;\n}\n\n\n\n\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::setupMatrix(Solver<T_Config> *outer_solver, AMG_Config &cfg, bool reuse_matrix_structure)\n{\n    // Check that matrix is initialized\n    if (!this->is_initialized())\n    {\n        FatalError(\"Trying to setup from the uninitialized matrix\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    this->set_initialized(0);\n    m_separation_interior = cfg.getParameter<ViewType>(\"separation_interior\", \"default\");\n    m_separation_exterior = cfg.getParameter<ViewType>(\"separation_exterior\", \"default\");\n\n    if (m_separation_interior > m_separation_exterior) { FatalError(\"Interior separation cannot be wider than the exterior separation\", AMGX_ERR_CONFIGURATION); }\n\n    // If latency hiding is disabled, the interior is overwritten\n    if(!isLatencyHidingEnabled(cfg))\n    {\n        m_separation_interior = m_separation_exterior;\n    }\n\n    bool is_coloring_needed  = outer_solver->isColoringNeeded();\n\n    if (!reuse_matrix_structure)\n    {\n        // Color the matrix since the structure has changed\n        if ( is_coloring_needed )\n        {\n            // Get the scope of the solver that needs coloring\n            std::string cfg_scope_for_coloring;\n            outer_solver->getColoringScope(cfg_scope_for_coloring);\n            this->colorMatrix(cfg, cfg_scope_for_coloring);\n        }\n    }\n\n    // Set the matrix block format\n    BlockFormat block_format = cfg.getParameter<BlockFormat>( \"block_format\", \"default\" );\n\n    if ( this->getBlockFormat() != block_format )\n    {\n        this->setBlockFormat( block_format );\n    }\n\n    // Permute the values and the column indices (if necessary)\n    bool reorder_cols_by_color = outer_solver->getReorderColsByColorDesired();\n    bool insert_diagonal  = outer_solver->getInsertDiagonalDesired();\n\n    if (reorder_cols_by_color)\n    {\n        if (reuse_matrix_structure) // Only permute the values\n        {\n            this->permuteValues();\n        }\n        else // Permute the values and the columns\n        {\n            this->reorderColumnsByColor(insert_diagonal);\n            this->permuteValues();\n        }\n    }\n\n    this->set_initialized(1);\n    m_is_matrix_setup = true;\n}\n\ntemplate<class T_Config>\nbool MatrixBase<T_Config>::isLatencyHidingEnabled(AMG_Config &cfg)\n{\n    const int min_rows_latency_hiding =\n        cfg.getParameter<int>(\"min_rows_latency_hiding\", \"default\");\n\n    // Test all partitions to check if they all fall below the threshold\n    if (!is_matrix_singleGPU() && min_rows_latency_hiding >= 0)\n    {\n        const auto& nrows_per_part = manager->getNumRowsPerPart();\n\n        // Look at all partitions to check whether the number of rows falls\n        // below the user defined minimum\n        for(auto& nrpp : nrows_per_part)\n        {\n            // If any partitions still have a large enough set of rows, \n            // continue latency hiding\n            if(nrpp >= min_rows_latency_hiding)\n            {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::reorderColumnsByColor(bool insert_diagonal)\n{\n    // If columns already reordered, don't reorder again\n    if (this->m_cols_reordered_by_color)\n    {\n        return;\n    }\n\n    // Check if matrix is colored\n    if (!this->hasProps(COLORING))\n    {\n        FatalError(\"Matrix must be colored in order to be reordered by colors. Try setting coloring_level=1 in the configuration file\", AMGX_ERR_CONFIGURATION);\n    }\n\n    set_initialized(0);\n    // Compute the row indices\n    addProps(COO);\n    set_allow_recompute_diag(false);\n    index_type num_non_zeros = num_nz;\n\n    if (hasProps(DIAG) && insert_diagonal)   // Diagonal stored separetely\n    {\n        num_non_zeros += num_rows;\n    }\n\n    // Append the diagonal if stored separately\n    // The new matrix will have inside diagonal\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        amgx::thrust::counting_iterator<int> first(0);\n        amgx::thrust::counting_iterator<int> last = first + num_rows;\n        // Create new row_indices with appended diagonal\n        IVector new_row_indices(num_non_zeros);\n        IVector new_col_indices(num_non_zeros);\n        amgx::thrust::copy(row_indices.begin(), row_indices.end(), new_row_indices.begin());\n        amgx::thrust::copy(first, last, new_row_indices.begin() + num_nz);\n        // Create new col_indices with appended diagonal\n        amgx::thrust::copy(col_indices.begin(), col_indices.end(), new_col_indices.begin());\n        amgx::thrust::copy(first, last, new_col_indices.begin() + num_nz);\n        row_indices.swap(new_row_indices);\n        col_indices.swap(new_col_indices);\n        new_row_indices.clear();\n        new_row_indices.shrink_to_fit();\n        new_col_indices.clear();\n        new_col_indices.shrink_to_fit();\n    }\n\n    // Compute the color of every column\n    IVector element_colors(num_non_zeros);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->getMatrixColoring().getRowColors().begin(), col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->getMatrixColoring().getRowColors().begin(), col_indices.end()),\n                 element_colors.begin());\n    // Compute the permutation vector by sorting by rows and columns\n    m_values_permutation_vector.resize(num_non_zeros);\n    thrust_wrapper::sequence<T_Config::memSpace>(m_values_permutation_vector.begin(), m_values_permutation_vector.end());\n    cusp::detail::sort_by_row_and_column(row_indices, element_colors, m_values_permutation_vector);\n    cudaCheckError();\n    element_colors.clear();\n    element_colors.shrink_to_fit();\n    // Compute the new column indices sorted by color\n    IVector new_column_indices(num_non_zeros);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(col_indices.begin(), m_values_permutation_vector.begin()),\n                 amgx::thrust::make_permutation_iterator(col_indices.begin(), m_values_permutation_vector.end()),\n                 new_column_indices.begin());\n    col_indices.swap(new_column_indices);\n    new_column_indices.clear();\n    new_column_indices.shrink_to_fit();\n\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        // Change the number of nonzeros\n        set_num_nz(num_non_zeros);\n        values.resize( (num_non_zeros + 1)*this->get_block_size());\n        this->m_is_permutation_inplace = false;\n    }\n    else\n    {\n        this->m_is_permutation_inplace = true;\n    }\n\n    if (hasProps(DIAG) && insert_diagonal)\n    {\n        delProps(DIAG);\n        // Force recomputation of row offsets\n        delProps(CSR);\n    }\n\n    // Compute row offsets if input matrix only had COO format or if diagonal was inserted\n    addProps(CSR);\n    // Recompute the diagonal\n    set_allow_recompute_diag(true);\n    computeDiagonal();\n    // Compute the color offsets\n    m_smaller_color_offsets.resize(this->get_num_rows());\n    m_larger_color_offsets.resize(this->get_num_rows());\n    computeColorOffsets();\n    this->m_cols_reordered_by_color = true;\n    set_initialized(1);\n}\n\ntemplate<class T_Config>\nvoid\nMatrixBase<T_Config>::sortByRowAndColumn()\n{\n    this->set_initialized(0);\n    // Add row_indices array\n    this->addProps(COO);\n    this->set_allow_recompute_diag(false);\n\n    if (this->get_block_dimx() != 1 || this->get_block_dimy() != 1)\n    {\n        FatalError(\"sortByRowAndColumn only works for scalar matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    size_t N = this->row_indices.size();\n    IVector permutation(N);\n    thrust_wrapper::sequence<T_Config::memSpace>(permutation.begin(), permutation.end());\n    cudaCheckError();\n    // compute permutation and sort by (I,J)\n    {\n        IVector temp(this->col_indices);\n        amgx::thrust::stable_sort_by_key(temp.begin(), temp.end(), permutation.begin());\n        temp = this->row_indices;\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->row_indices.begin());\n        amgx::thrust::stable_sort_by_key(this->row_indices.begin(), this->row_indices.end(), permutation.begin());\n        temp = this->col_indices;\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->col_indices.begin());\n    }\n    cudaCheckError();\n    // use permutation to reorder the values\n    {\n        MVector temp(this->values);\n        amgx::thrust::gather(permutation.begin(), permutation.end(), temp.begin(), this->values.begin());\n    }\n    cudaCheckError();\n    this->set_allow_recompute_diag(true);\n    this->addProps(CSR);\n    // remove row indices array\n    this->delProps(COO);\n    this->computeDiagonal();\n    this->set_initialized(1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDiagonal()\n{\n    ViewType oldView  = this->currentView();\n\n    if (this->m_initialized) { this->setView(ALL); }\n\n    if (this->allow_recompute_diag)\n    {\n        index_type num_rows = this->get_num_rows();\n        index_type num_nz = this->get_num_nz();\n        IVector &row_offsets = this->row_offsets;\n        IVector &row_indices = this->row_indices;\n        IVector &col_indices = this->col_indices;\n        IVector &diag = this->diag;\n\n        if (this->diag.size() != this->get_num_rows()) { this->diag.resize(this->get_num_rows()); }\n\n        if (this->m_diag_end_offsets.size() != this->get_num_rows()) { this->m_diag_end_offsets.resize(this->get_num_rows()); }\n\n        if ( this->hasProps(DIAG) )\n        {\n            int first = num_nz;\n\n            for (int r = 0; r < num_rows; r++)\n            {\n                diag[r] = first++;\n            }\n        }\n        else\n        {\n            index_type null_index = this->get_num_nz();\n\n            if ( this->hasProps(CSR) )\n            {\n                for (int r = 0; r < num_rows; r++)\n                {\n                    int start = row_offsets[r];\n                    int end = row_offsets[r + 1];\n                    diag[r] = null_index;\n\n                    for (int j = start; j < end; j++)\n                    {\n                        if (col_indices[j] == r)\n                        {\n                            diag[r] = j;\n                            break;\n                        }\n                    }\n                }\n            }\n            else if (this->hasProps(COO) )\n            {\n                for (int i = 0; i < num_rows; i++)\n                {\n                    diag[i] = null_index;\n                }\n\n                for (int j = 0; j < num_nz; j++)\n                {\n                    int r = row_indices[j];\n\n                    if (r == col_indices[j])\n                    {\n                        diag[r] = j;\n                    }\n                }\n            }\n        }\n\n        for (int r = 0; r < num_rows; r++)\n        {\n            this->m_diag_end_offsets[r] = diag[r] + 1;\n        }\n    }\n\n    this->setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDiagonal()\n{\n    ViewType oldView;\n    index_type null_index = 0; //this->get_num_nz();\n\n    if (!this->allow_recompute_diag || !(this->get_num_rows() > 0))\n    {\n        return;\n    }\n\n    if (this->m_initialized)\n    {\n        oldView = this->currentView();\n        this->setView(ALL);\n    }\n\n    if (this->diag.size() < this->get_num_rows())\n    {\n        this->diag.resize(this->get_num_rows());\n    }\n\n    if (this->m_diag_end_offsets.size() < this->get_num_rows())\n    {\n        this->m_diag_end_offsets.resize(this->get_num_rows());\n    }\n\n    if (this->hasProps(DIAG))\n    {\n        int num_blocks = std::min(4096, (this->get_num_rows() + 511) / 512);\n        computeDiagonalKernelDiagProp <<< num_blocks, 512, 0, amgx::thrust::global_thread_handle::get_stream()>>>(this->get_num_rows(), this->get_num_nz(), this->diag.raw(), this->m_diag_end_offsets.raw());\n        cudaCheckError();\n    }\n    else if (this->hasProps(COO))\n    {\n        int num_blocks = std::min(4096, (this->get_num_nz() + 511) / 512);\n        computeDiagonalKernelCOO <<< num_blocks, 512>>>(this->get_num_nz(), this->row_indices.raw(), this->col_indices.raw(), this->diag.raw(), this->m_diag_end_offsets.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        int num_blocks = std::min(4096, (this->get_num_rows() + 511) / 512);\n        computeDiagonalKernelCSR <<< num_blocks, 512>>>(this->get_num_rows(), this->row_offsets.raw(), this->col_indices.raw(), this->diag.raw(), null_index, this->m_diag_end_offsets.raw());\n    }\n\n    cudaCheckError();\n\n    if (this->m_initialized)\n    {\n        this->setView(oldView);\n    }\n\n#ifdef DEBUG\n\n    if (this->diag_copy.size() == 0)\n    {\n        this->diag_copy = this->diag;\n    }\n    else\n    {\n        if ((this->diag.size() != this->diag_copy.size()) || (this->diag.size() == 1)) { return; }\n\n        IVector_h h_diag = this->diag;\n        IVector_h h_diag_copy = this->diag_copy;\n        bool equal = true;\n\n        for (unsigned int i = 0; i < this->diag.size(); ++i)\n        {\n            if (h_diag[i] != h_diag_copy[i])\n            {\n                equal = false;\n                break;\n            }\n        }\n\n        if (equal)\n        {\n            FatalError(\"ComputeDiagonal was called, but diagonal hasn't changed\", AMGX_ERR_UNKNOWN);\n        }\n    }\n\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid\nMatrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::permuteValues()\n{\n    if (this->m_cols_reordered_by_color && this->m_is_permutation_inplace)\n    {\n        reorderValuesInPlace();\n    }\n    else if (this->m_cols_reordered_by_color && !this->m_is_permutation_inplace )\n    {\n        // might use a lot of memory\n        MVector temp_values;\n        temp_values.resize(this->values.size());\n        temp_values.set_block_dimx(this->values.get_block_dimx());\n        temp_values.set_block_dimy(this->values.get_block_dimy());\n        amgx::unpermuteVector(this->values, temp_values, this->m_values_permutation_vector, (this->get_num_nz()) * (this->get_block_size()));\n        this->values.swap(temp_values);\n        temp_values.clear();\n        temp_values.shrink_to_fit();\n    }\n    else\n    {\n        FatalError(\"Invalid reordering level in permuteValues\", AMGX_ERR_CONFIGURATION);\n    }\n}\n\n\nvoid computeRowOffsetsDevice(int num_blocks, INDEX_TYPE num_rows, INDEX_TYPE num_nz, const INDEX_TYPE *row_indices, INDEX_TYPE *row_offsets, INDEX_TYPE block_size )\n{\n    computeRowOffsetsKernel <<< num_blocks, 512>>>(num_rows, num_nz, row_indices, row_offsets);\n    cudaCheckError();\n}\n\nextern void computeRowIndicesDevice(int num_blocks, INDEX_TYPE num_rows, const INDEX_TYPE *row_offsets, INDEX_TYPE *row_indices, INDEX_TYPE block_size )\n{\n    computeRowIndicesKernel <<< num_blocks, 512>>>(num_rows, row_offsets, row_indices);\n    cudaCheckError();\n}\n\nvoid computeColorOffsetsDeviceCSR(int num_blocks, INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, const INDEX_TYPE *row_colors, INDEX_TYPE *smaller_color_offsets, INDEX_TYPE *larger_color_offsets, INDEX_TYPE block_size, INDEX_TYPE *diag )\n{\n    computeColorOffsetsKernelCSR <<< num_blocks, 512>>>(num_rows, row_offsets, col_indices, row_colors, smaller_color_offsets, larger_color_offsets, diag);\n    cudaCheckError();\n}\n\ntemplate <typename T>\nvoid reorderElementsDeviceCSR(INDEX_TYPE num_rows,\n                              INDEX_TYPE *row_offsets,\n                              INDEX_TYPE *permutation,\n                              INDEX_TYPE *col_indices,\n                              T *values,\n                              INDEX_TYPE block_size)\n{\n    INDEX_TYPE max_row_length = std::max(1, thrust_wrapper::transform_reduce<AMGX_device>(row_offsets, row_offsets + num_rows, row_length<INDEX_TYPE>(), 0, amgx::thrust::maximum<INDEX_TYPE>()));\n    //TODO: optimise this in terms of storage\n    INDEX_TYPE storage_space = 100 * 1024 * 1024 * sizeof(T) / sizeof(hipDoubleComplex); // because we allocate as for cuComplex\n    INDEX_TYPE blocks = 1500 < storage_space / (max_row_length * block_size * sizeof(T)) ? 1500 : storage_space / (max_row_length * block_size * sizeof(T));\n    blocks = blocks < num_rows ? max(1, blocks) : num_rows;\n    INDEX_TYPE aligned_space = ((max_row_length * block_size * sizeof(T) / 128 + 1) * 128) / sizeof(T); //pad to 128 bytes\n    Vector<amgx::TemplateConfig<AMGX_device, AMGX_vecDoubleComplex, AMGX_matDoubleComplex, AMGX_indInt> > tempstorage(blocks * aligned_space);\n    reorderElements <<< blocks, 256>>>(num_rows, row_offsets, permutation, values, (T *)tempstorage.raw(), aligned_space, block_size);\n    cudaCheckError();\n}\n\n// explicitly instantiate reorderElementsDeviceCSR, since we call it from header and it's not a part of some class\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, float *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, double *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, hipComplex *values, INDEX_TYPE block_size);\ntemplate void reorderElementsDeviceCSR(INDEX_TYPE num_rows, INDEX_TYPE *row_offsets, INDEX_TYPE *permutation, INDEX_TYPE *col_indices, hipDoubleComplex *values, INDEX_TYPE block_size);\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MatrixBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Matrix<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n\n\n\n}//end namespace amgx\n", "source": "/kaggle/working/amgx/src/matrix.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cassert>\n#include <iostream>\n#include <thrust/scan.h>\n#include <util.h>\n#include <csr_multiply.h>\n#include <csr_multiply_detail.h>\n#include <device_properties.h>\n#include <thrust_wrapper.h>\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace csr_multiply_detail\n{\n\n#include <amgx_types/util.h>\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__\nvoid flag_halo_rows(int *row_ids, int size, int *flagArray, int neighbor, int global_id)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int row_id = row_ids[tidx];\n        flagArray[row_id] = tidx;\n    }\n}\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_kernel( const int A_num_rows,\n                         const int *A_rows,\n                         const int *A_cols,\n                         const int *B_rows,\n                         const int *B_cols,\n                         int *C_rows,\n                         int *C_cols,\n                         int *Aq1,\n                         int *Bq1,\n                         int *Aq2,\n                         int *Bq2,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n\t    \t\t\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            // The number of valid rows.\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( lane_id < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n\n                        // b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    set.insert( b_col_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[c_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_kernel( const int A_num_rows,\n                         const int *__restrict A_rows,\n                         const int *__restrict A_cols,\n                         const int *__restrict B_rows,\n                         const int *__restrict B_cols,\n                         int *__restrict C_rows,\n                         int *__restrict C_cols,\n                         int *Aq1,\n                         int *Bq1,\n                         int *Aq2,\n                         int *Bq2,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, local_k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( is_active_k && lane_id_mod_num_threads < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n\n                        // b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    set.insert( b_col_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[c_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\ntemplate <int SMEM_SIZE, int NUM_HASH_FCTS, int WARP_SIZE, bool COUNT_ONLY >\n__device__ __forceinline__ void sparse_add_process_row(int row_id, const int *__restrict__ row_offsets, const int *__restrict__ col_indices, int lane_id, Hash_set<int, SMEM_SIZE, NUM_HASH_FCTS, WARP_SIZE> &set, int *wk_status, int global_id, bool print_flag)\n{\n    // Load the range of the row of RAP_int\n    int col_tmp = -1;\n\n    if ( lane_id < 2 )\n    {\n        col_tmp = utils::Ld<utils::LD_NC>::load( &row_offsets[row_id + lane_id] );\n    }\n\n    int col_it  = utils::shfl( col_tmp, 0 );\n    int col_end = utils::shfl( col_tmp, 1 );\n\n    // Iterate over the columns of RAP_int\n    for ( col_it += lane_id ; utils::any(col_it < col_end) ; col_it += WARP_SIZE )\n    {\n        int col_id = -1;\n\n        if ( col_it < col_end )\n        {\n            col_id = utils::Ld<utils::LD_NC>::load( &col_indices[col_it] );\n        }\n\n        set.insert( col_id, COUNT_ONLY ? wk_status : NULL );\n    }\n}\n\n\ntemplate <typename Value_type, int SMEM_SIZE, int NUM_HASH_FCTS, int WARP_SIZE>\n__device__ __forceinline__ void sparse_add_process_row_values(int row_id, const int *__restrict__ row_offsets, const int *__restrict__ col_indices, const Value_type *__restrict vals, int lane_id, Hash_map<int, Value_type, SMEM_SIZE, NUM_HASH_FCTS, WARP_SIZE> &map, int *wk_status)\n{\n    // Load the range of the row.\n    int col_tmp = -1;\n\n    if ( lane_id < 2 )\n    {\n        col_tmp = utils::Ld<utils::LD_NC>::load( &row_offsets[row_id + lane_id] );\n    }\n\n    int col_it  = utils::shfl( col_tmp, 0 );\n    int col_end = utils::shfl( col_tmp, 1 );\n\n    // Iterate over the columns of A.\n    for ( col_it += lane_id ; utils::any(col_it < col_end) ; col_it += WARP_SIZE )\n    {\n        const bool is_active = col_it < col_end;\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int col_id(-1);\n        Value_type value = amgx::types::util<Value_type>::get_zero();\n\n        if ( is_active )\n        {\n            col_id = utils::Ld<utils::LD_NC>::load( &col_indices[col_it] );\n            value  = utils::Ld<utils::LD_NC>::load( &vals[col_it] );\n        }\n\n        map.insert( col_id, value, wk_status );\n    }\n}\n\n\n\n\ntemplate<int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_RAP_ext_kernel( const int RAP_int_num_rows,\n                                 const int *__restrict RAP_int_rows,\n                                 const int *__restrict RAP_int_cols,\n                                 int **RAP_ext_row_ptrs,\n                                 int **RAP_ext_col_ptrs,\n                                 int *__restrict RAP_rows,\n                                 int *__restrict RAP_cols,\n                                 int **flagArray_ptrs,\n                                 const int gmem_size,\n                                 int *g_keys,\n                                 int *wk_work_queue,\n                                 int *wk_status,\n                                 int num_neighbors,\n                                 int global_id )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int rap_int_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[rap_int_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of RAP_ext\n    for ( ; rap_int_row_id < RAP_int_num_rows; rap_int_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // ---------------------------------\n        // First process RAP_int\n        // ---------------------------------\n        bool print_flag = false;\n\n        if (rap_int_row_id == 3 && global_id == 0)\n        {\n            print_flag = true;\n        }\n\n        sparse_add_process_row<SMEM_SIZE, 4, WARP_SIZE, COUNT_ONLY>(rap_int_row_id, RAP_int_rows, RAP_int_cols, lane_id, set, wk_status, global_id, print_flag);\n\n        // ---------------------------------\n        // Then process RAP_ext\n        // ---------------------------------\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int flag = flagArray_ptrs[i][rap_int_row_id];\n\n            if (flag != -1)\n            {\n                int *RAP_ext_rows = RAP_ext_row_ptrs[i];\n                int *RAP_ext_cols = RAP_ext_col_ptrs[i];\n                int pos_in_row_ext = flag;\n                sparse_add_process_row<SMEM_SIZE, 4, WARP_SIZE, COUNT_ONLY>(pos_in_row_ext, RAP_ext_rows, RAP_ext_cols, lane_id, set, wk_status, global_id, print_flag);\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                RAP_rows[rap_int_row_id] = count;\n            }\n        }\n        else\n        {\n            int rap_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                rap_col_tmp = utils::Ld<utils::LD_NC>::load( &RAP_rows[rap_int_row_id + lane_id] );\n            }\n\n            int rap_col_it  = utils::shfl( rap_col_tmp, 0 );\n            int rap_col_end = utils::shfl( rap_col_tmp, 1 );\n            // Store the results.\n            int count = rap_col_end - rap_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &RAP_cols[rap_col_it] );\n        }\n    }\n}\n\n\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstruct Without_external_diag\n{\n    static __device__ __forceinline__ bool is_active  ( int a_col_it, int a_col_end ) { return a_col_it < a_col_end; }\n    static __device__ __forceinline__ bool is_boundary( int a_col_it, int a_col_end ) { return false; }\n};\n\n// ====================================================================================================================\n\nstruct With_external_diag\n{\n    static __device__ __forceinline__ bool is_active  ( int a_col_it, int a_col_end ) { return a_col_it <= a_col_end; }\n    static __device__ __forceinline__ bool is_boundary( int a_col_it, int a_col_end ) { return a_col_it == a_col_end; }\n};\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY, typename Diag_traits >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_ilu1_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const int *__restrict A_coloring,\n                              int *__restrict C_rows,\n                              int *__restrict C_cols,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *wk_work_queue,\n                              int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Tables to broadcast values.\n    __shared__ volatile int s_b_rows[CTA_SIZE], s_b_colors[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // The color of the row.\n        int a_row_color = A_coloring[a_row_id];\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(Diag_traits::is_active(a_col_it, a_col_end)) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n            }\n\n            if ( Diag_traits::is_boundary(a_col_it, a_col_end) )\n            {\n                b_row_id = a_row_id;\n            }\n\n            // Push the columns in the set.\n            set.insert( b_row_id, COUNT_ONLY ? wk_status : NULL );\n\n            // Skip computation if the color of the row is 0.\n            if ( a_row_color != 0 )\n            {\n                // Gather the colors of the columns.\n                int b_row_color = -1;\n\n                if ( is_active )\n                {\n                    b_row_color = A_coloring[b_row_id];\n                }\n\n                // The number of valid rows.\n                int pred = is_active && b_row_color < a_row_color;\n                int vote = utils::ballot( pred );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( pred )\n                {\n                    s_b_rows  [warp_id * WARP_SIZE + dest] = b_row_id;\n                    s_b_colors[warp_id * WARP_SIZE + dest] = b_row_color; // TODO: store an int2 rather than 2 ints of SM35 with 64bit banks.\n                }\n\n                const int num_rows = __popc( vote );\n\n                // Uniform loop: threads collaborate to load other elements.\n                for ( int k = 0 ; k < num_rows ; ++k )\n                {\n                    // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                    const int uniform_b_row_id = s_b_rows  [warp_id * WARP_SIZE + k];\n                    const int uniform_b_color  = s_b_colors[warp_id * WARP_SIZE + k];\n                    // Load the range of the row of B.\n                    int b_col_tmp = -1;\n\n                    if ( lane_id < 2 )\n                    {\n                        b_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[uniform_b_row_id + lane_id] );\n                    }\n\n                    int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                    int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                    // Iterate over the range of columns of B.\n                    for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                    {\n                        int b_col_id = -1, b_col_color = -1;\n\n                        if ( b_col_it < b_col_end )\n                        {\n                            b_col_id    = utils::Ld<utils::LD_NC>::load( &A_cols[b_col_it] );\n                            b_col_color = utils::Ld<utils::LD_NC>::load( &A_coloring[b_col_id] );\n                        }\n\n                        int item = -1;\n\n                        if ( b_col_color >= uniform_b_color && b_col_color != a_row_color )\n                        {\n                            item = b_col_id;\n                        }\n\n                        set.insert( item, COUNT_ONLY ? wk_status : NULL );\n                    }\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[a_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[a_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY, typename Diag_traits >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_ilu1_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const int *__restrict A_coloring,\n                              int *__restrict C_rows,\n                              int *__restrict C_cols,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *wk_work_queue,\n                              int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Tables to broadcast values.\n    __shared__ volatile int s_b_rows[CTA_SIZE], s_b_colors[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // The color of the row.\n        int a_row_color = A_coloring[a_row_id];\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(Diag_traits::is_active(a_col_it, a_col_end)) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n            }\n\n            if ( Diag_traits::is_boundary(a_col_it, a_col_end) )\n            {\n                b_row_id = a_row_id;\n            }\n\n            // Push the columns in the set.\n            set.insert( b_row_id, COUNT_ONLY ? wk_status : NULL );\n\n            // Skip computation if the color of the row is 0.\n            if ( a_row_color != 0 )\n            {\n                // Gather the colors of the columns.\n                int b_row_color = -1;\n\n                if ( is_active )\n                {\n                    b_row_color = A_coloring[b_row_id];\n                }\n\n                // The number of valid rows.\n                int pred = is_active && b_row_color < a_row_color;\n                int vote = utils::ballot( pred );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( pred )\n                {\n                    s_b_rows  [warp_id * WARP_SIZE + dest] = b_row_id;\n                    s_b_colors[warp_id * WARP_SIZE + dest] = b_row_color;\n                }\n\n                const int num_rows = __popc( vote );\n\n                // Uniform loop: threads collaborate to load other elements.\n                for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n                {\n                    int local_k = k + lane_id_div_num_threads;\n                    // Is it an active thread.\n                    bool is_active_k = local_k < num_rows;\n                    // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                    int uniform_b_row_id = -1, uniform_b_color = -1;\n\n                    if ( is_active_k )\n                    {\n                        uniform_b_row_id = s_b_rows  [warp_id * WARP_SIZE + local_k];\n                        uniform_b_color  = s_b_colors[warp_id * WARP_SIZE + local_k];\n                    }\n\n                    // Load the range of the row of B.\n                    int b_col_tmp = -1;\n\n                    if ( is_active_k && lane_id_mod_num_threads < 2 )\n                    {\n                        b_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                    }\n\n                    int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                    int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                    // Iterate over the range of columns of B.\n                    for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                    {\n                        int b_col_id = -1, b_col_color = -1;\n\n                        if ( b_col_it < b_col_end )\n                        {\n                            b_col_id    = utils::Ld<utils::LD_NC>::load( &A_cols[b_col_it] );\n                            b_col_color = utils::Ld<utils::LD_NC>::load( &A_coloring[b_col_id] );\n                        }\n\n                        int item = -1;\n\n                        if ( b_col_color >= uniform_b_color && b_col_color != a_row_color )\n                        {\n                            item = b_col_id;\n                        }\n\n                        set.insert( item, COUNT_ONLY ? wk_status : NULL );\n                    }\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[a_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[a_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_kernel( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Value_type *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const Value_type *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       Value_type *__restrict C_vals,\n                       int *Aq1,\n                       int *Bq1,\n                       int *Aq2,\n                       int *Bq2,\n                       const int gmem_size,\n                       int *g_keys,\n                       Value_type *g_vals,\n                       int *wk_work_queue,\n                       int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map(&s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Clear the map.\n        map.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n            Value_type a_value = amgx::types::util<Value_type>::get_zero();\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n                a_value  = utils::Ld<utils::LD_NC>::load( &A_vals[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k );\n                // The value of A.\n                const Value_type uniform_a_value = utils::shfl( a_value, k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( lane_id < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    int b_col_id = -1;\n                    Value_type b_value = amgx::types::util<Value_type>::get_zero();\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n                        b_value  = utils::Ld<utils::LD_NC>::load( &B_vals[b_col_it] );\n\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    map.insert( b_col_id, uniform_a_value * b_value, wk_status );\n                }\n            }\n        }\n\n        // Store the results.\n        int c_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n        }\n\n        int c_col_it  = utils::shfl( c_col_tmp, 0 );\n        int c_col_end = utils::shfl( c_col_tmp, 1 );\n        // Store the results.\n        int count = c_col_end - c_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &C_cols[c_col_it], &C_vals[c_col_it] );\n    }\n\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE>\n__global__ \nvoid count_non_zeroes_kernel_opt( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       int *__restrict C_row_counts)\n{\n    // Defines for hash container\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    // One row of A per group\n    const int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Block-level hash container storage\n    __shared__ int key_s[ngroups*HASH_SIZE];\n    __shared__ int counts[ngroups];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n    }\n\n    for(int i = 0; i < ngroups; ++i)\n    {\n        counts[i] = 0;\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n\n    if(a_row_id < A_num_rows)\n    {\n        // Distributed columns of row of A over threads in group.\n        for (int a_col_it = A_rows[a_row_id] + lane_id; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE)\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Outer product of element of A and row of B.\n            for (int b_col_it = B_rows[a_col_id]; b_col_it < B_rows[a_col_id + 1] ; ++b_col_it)\n            {\n                int b_col_id = B_cols[b_col_it];\n\n                int hash = b_col_id % HASH_SIZE;\n\n                // By construction this algorithm should guarantee \n                // all keys can be inserted\n                while(true)\n                {\n                    // If the slot is vacant, then attempt acquire\n                    int key = key_group_s[hash];\n\n                    // Check if the key was already b_col_id, or was already set \n                    // Insert the product\n                    if(key == b_col_id)\n                    {\n                        break;\n                    }\n\n                    if(key == SLOT_VACANT)\n                    {\n                        int new_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n\n                        if(new_key == SLOT_VACANT || new_key == b_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    // Store the results.\n    if(a_row_id < A_num_rows)\n    {\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            if(key_group_s[i] != SLOT_VACANT)\n            {\n                atomicAdd(&counts[group_id], 1);\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(a_row_id < A_num_rows && lane_id == 0)\n    {\n        C_row_counts[a_row_id] = counts[group_id];\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE, class ValueType>\n__global__ \nvoid compute_values_kernel_opt( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const ValueType *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const ValueType *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       ValueType *__restrict C_vals)\n{\n    // Defines for hash container\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    // One row of A per group\n    const int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Block-level hash container storage\n    int* key_s = s;\n    ValueType* data_s = (ValueType*)&key_s[ngroups*HASH_SIZE];\n    int* col_ind_s = (int*)&data_s[ngroups*HASH_SIZE];\n\n    // Group-level hash containers\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n    ValueType* data_group_s = &data_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n        data_s[i] = amgx::types::util<ValueType>::get_zero(); // We will sum into values\n    }\n\n    if(lane_id == 0)\n    {\n        col_ind_s[group_id] = 0;\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    if(a_row_id < A_num_rows)\n    {\n        // Distributed columns of row of A over threads in group.\n        for (int a_col_it = A_rows[a_row_id] + lane_id; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE)\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Outer product of element of A and row of B.\n            for (int b_col_it = B_rows[a_col_id]; b_col_it < B_rows[a_col_id + 1] ; ++b_col_it)\n            {\n                ValueType val = A_vals[a_col_it]*B_vals[b_col_it];\n\n                // Short circuit if zero\n                if(amgx::types::util<ValueType>::is_zero(val)) continue;\n\n                int b_col_id = B_cols[b_col_it];\n\n                // XXX UPDATE HASHING APPROACH\n                int hash = b_col_id % HASH_SIZE;\n\n                // By construction this algorithm should guarantee \n                // all keys can be inserted\n                while(true)\n                {\n                    // If the slot is vacant, then attempt acquire\n                    int key = key_group_s[hash];\n                    if(key == SLOT_VACANT)\n                    {\n                        int new_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n                        if(new_key == SLOT_VACANT || new_key == b_col_id)\n                        {\n                            key = b_col_id;\n                        }\n                    }\n\n                    // Check if the key was already b_col_id, or was already set \n                    // Insert the product\n                    if(key == b_col_id)\n                    {\n                        utils::atomic_add(&data_group_s[hash], val);\n                        break;\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    // Store the results.\n    int c_row_id  = a_row_id;\n    int c_col_it  = C_rows[c_row_id];\n\n    if(a_row_id < A_num_rows)\n    {\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            if(key_group_s[i] != SLOT_VACANT)\n            {\n                // Short circuit if zero\n                if(amgx::types::util<ValueType>::is_zero(data_group_s[i])) continue;\n\n                int ind = atomicAdd(&col_ind_s[group_id], 1);\n\n                C_cols[c_col_it + ind] = key_group_s[i];\n                C_vals[c_col_it + ind] = data_group_s[i];\n            }\n        }\n    }\n}\n\ntemplate< int NUM_THREADS_PER_ROW, typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_kernel( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Value_type *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const Value_type *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       Value_type *__restrict C_vals,\n                       int *Aq1,\n                       int *Bq1,\n                       int *Aq2,\n                       int *Bq2,\n                       const int gmem_size,\n                       int *g_keys,\n                       Value_type *g_vals,\n                       int *wk_work_queue,\n                       int *wk_status )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map(&s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Clear the map.\n        map.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id(-1);\n            Value_type a_value = amgx::types::util<Value_type>::get_zero();\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n                a_value  = utils::Ld<utils::LD_NC>::load( &A_vals[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k + lane_id_div_num_threads );\n                // The value of A.\n                const Value_type uniform_a_value = utils::shfl( a_value, k + lane_id_div_num_threads );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( is_active_k && lane_id_mod_num_threads < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int b_col_id(-1);\n                    Value_type b_value = amgx::types::util<Value_type>::get_zero();\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n                        b_value  = utils::Ld<utils::LD_NC>::load( &B_vals[b_col_it] );\n\n                        //b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    map.insert( b_col_id, uniform_a_value * b_value, wk_status );\n                }\n            }\n        }\n\n        // Store the results.\n        int c_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n        }\n\n        int c_col_it  = utils::shfl( c_col_tmp, 0 );\n        int c_col_end = utils::shfl( c_col_tmp, 1 );\n        // Store the results.\n        int count = c_col_end - c_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &C_cols[c_col_it], &C_vals[c_col_it] );\n    }\n\n}\n\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_RAP_ext_kernel( const int RAP_int_num_rows,\n                               const int *__restrict RAP_int_rows,\n                               const int *__restrict RAP_int_cols,\n                               const Value_type *__restrict RAP_int_vals,\n                               int **RAP_ext_row_ptrs,\n                               int **RAP_ext_col_ptrs,\n                               Value_type **RAP_ext_val_ptrs,\n                               int *__restrict RAP_rows,\n                               int *__restrict RAP_cols,\n                               Value_type *__restrict RAP_vals,\n                               int **flagArray_ptrs,\n                               const int gmem_size,\n                               int *g_keys,\n                               Value_type *g_vals,\n                               int *wk_work_queue,\n                               int num_neighbors,\n                               int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int rap_int_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[rap_int_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[rap_int_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of RAP_ext\n    for ( ; rap_int_row_id < RAP_int_num_rows ; rap_int_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Clear the map.\n        map.clear();\n        // ---------------------------------\n        // First process RAP_int\n        // ---------------------------------\n        sparse_add_process_row_values<Value_type, SMEM_SIZE, 4, WARP_SIZE>(rap_int_row_id, RAP_int_rows, RAP_int_cols, RAP_int_vals, lane_id, map, wk_status);\n\n        // ---------------------------------\n        // Then process RAP_ext\n        // ---------------------------------\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int flag = flagArray_ptrs[i][rap_int_row_id];\n\n            if (flag != -1)\n            {\n                int *RAP_ext_rows = RAP_ext_row_ptrs[i];\n                int *RAP_ext_cols = RAP_ext_col_ptrs[i];\n                Value_type *RAP_ext_vals = RAP_ext_val_ptrs[i];\n                int pos_in_row_ext = flag;\n                sparse_add_process_row_values<Value_type, SMEM_SIZE, 4, WARP_SIZE>(pos_in_row_ext, RAP_ext_rows, RAP_ext_cols, RAP_ext_vals, lane_id, map, wk_status);\n            }\n        }\n\n        // Store the results.\n        int rap_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            rap_col_tmp = utils::Ld<utils::LD_NC>::load( &RAP_rows[rap_int_row_id + lane_id] );\n        }\n\n        int rap_col_it  = utils::shfl( rap_col_tmp, 0 );\n        int rap_col_end = utils::shfl( rap_col_tmp, 1 );\n        // Store the results.\n        int count = rap_col_end - rap_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &RAP_cols[rap_col_it], &RAP_vals[rap_col_it] );\n    }\n}\n\n\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace csr_multiply_detail\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace amgx\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nCSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::CSR_Multiply_Detail( bool allocate_values, int grid_size, int max_warp_count, int gmem_size ) :\n    Base(allocate_values, grid_size, max_warp_count, gmem_size)\n{}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, IVector *Aq1, IVector *Bq1, IVector *Aq2, IVector *Bq2 )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n    \n    // Compute non-zero elements.\n    switch ( this->m_num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_RAP_sparse_add( Matrix_d &RAP, const Matrix_d &RAP_int, std::vector<IVector> &RAP_ext_row_offsets, std::vector<IVector> &RAP_ext_col_indices, std::vector<MVector> &RAP_ext_values, std::vector<IVector> &RAP_ext_row_ids)\n\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n    // This is num_owned_coarse_rows\n    int RAP_size = RAP.get_num_rows();\n    int RAP_int_size = RAP_int.row_offsets.size() - 1;\n\n    if (RAP_int_size < RAP_size)\n    {\n        FatalError(\"RAP_int has less rows than RAP, need to modify sparse RAP add to handle that case\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create a device vector of the raw pointers to the array\n    // RAP_ext_row_ids_ptrs\n    // RAP_ext_row_offsets_ptrs\n    // RAP_ext_col_indices_ptrs\n    // RAP_ext_values_ptrs\n    int num_neighbors = RAP_ext_row_offsets.size();\n    std::vector<IVector> flagArray(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray[i].resize(RAP_size);\n        thrust_wrapper::fill<AMGX_device>(flagArray[i].begin(), flagArray[i].end(), -1);\n    }\n\n    cudaCheckError();\n    std::vector<int *> flagArray_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_row_offsets_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_col_indices_ptrs_h(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&flagArray[i][0]);\n        RAP_ext_row_offsets_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets[i][0]);\n        RAP_ext_col_indices_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices[i][0]);\n    }\n\n    device_vector_alloc<int *> flagArray_ptrs = flagArray_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_row_offsets_ptrs = RAP_ext_row_offsets_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_col_indices_ptrs = RAP_ext_col_indices_ptrs_h;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = RAP_ext_row_ids[i].size();\n\n        if (size != 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //write the position in RAP_ext_row_ids\n            csr_multiply_detail::flag_halo_rows <<< num_blocks, 128>>>(\n                RAP_ext_row_ids[i].raw(),\n                size,\n                flagArray[i].raw(),\n                i,\n                RAP.manager->global_id());\n                cudaCheckError();\n        }\n    }\n\n    csr_multiply_detail::count_non_zeroes_RAP_ext_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n        RAP_size,\n        RAP_int.row_offsets.raw(),\n        RAP_int.col_indices.raw(),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices_ptrs[0]),\n        RAP.row_offsets.raw(),\n        (int *) NULL,\n        amgx::thrust::raw_pointer_cast(&flagArray_ptrs[0]),\n        this->m_gmem_size,\n        this->m_keys,\n        this->m_work_queue,\n        this->m_status,\n        num_neighbors,\n        RAP.manager->global_id() );\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< int CTA_SIZE, bool COUNT_ONLY, typename Diag_traits, typename Matrix >\nstatic void\ncount_non_zeroes_ilu1_dispatch( const Matrix &A, Matrix &B, int num_threads_per_row_count, int gmem_size, int *keys, int *work_queue, int *status )\n{\n    const int GRID_SIZE = 1024;\n\n    switch ( num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n    }\n\n    cudaCheckError();\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_ilu1( const Matrix_d &A, Matrix_d &B )\n{\n    const int GRID_SIZE = 1024;\n\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n\n    // Count the number of non zeroes.\n    if ( A.hasProps(DIAG) )\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, true, csr_multiply_detail::With_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            this->m_status );\n    else\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, true, csr_multiply_detail::Without_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            this->m_status );\n\n    // Compute non-zero elements.\n    CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_offsets( Matrix_d &C )\n{\n    amgx::thrust::device_ptr<int> offsets_begin(C.row_offsets.raw());\n    amgx::thrust::device_ptr<int> offsets_end  (C.row_offsets.raw() + C.get_num_rows() + 1);\n    thrust_wrapper::exclusive_scan<AMGX_device>( offsets_begin, offsets_end, offsets_begin );\n    cudaCheckError();\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_sparsity( const Matrix_d &A, const Matrix_d &B, Matrix_d &C )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n\n    // Compute the values.\n    switch ( this->m_num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_sparsity_ilu1( const Matrix_d &A, Matrix_d &B )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n\n    // Count the number of non zeroes.\n    if ( A.hasProps(DIAG) )\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, false, csr_multiply_detail::With_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            NULL );\n    else\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, false, csr_multiply_detail::Without_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            NULL );\n\n    // Make sure it worked properly.\n    CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate <int CTA_SIZE>\n__global__ \nvoid calc_max_nnz_per_row_of_C( \n                       const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const int *__restrict B_rows,\n                       int *__restrict C_row_max)\n{\n    int a_row_id = blockIdx.x*blockDim.x + threadIdx.x;\n\n    int expected_max_row_nnz = 0;\n\n    if(a_row_id < A_num_rows)\n    {\n        for (int a_col_it = A_rows[a_row_id]; a_col_it < A_rows[a_row_id + 1]; ++a_col_it)\n        {\n            int a_col_id = A_cols[a_col_it];\n            expected_max_row_nnz += B_rows[a_col_id+1]-B_rows[a_col_id];\n        }\n    }\n\n    using BR = amgx::cub::BlockReduce<int, CTA_SIZE>;\n\n    __shared__ typename BR::TempStorage max_s;\n    int max_nnz_block = BR(max_s).Reduce(expected_max_row_nnz, [] __device__ (int a, int b) { return a > b ? a : b; });\n\n    if(threadIdx.x == 0)\n    {\n        C_row_max[blockIdx.x] = max_nnz_block;\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nbool CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_opt(\n        const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads)\n{\n    constexpr int cta_size = 128;\n\n    // At least for large matrices it may be optimal to determine the maximum\n    // expected hash size by investigation of the matrices\n\n    int grid_size = A.get_num_rows() / cta_size + 1;\n\n    IVector C_row_max_block(grid_size);\n    calc_max_nnz_per_row_of_C<cta_size><<<grid_size, cta_size>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    B.row_offsets.raw(),\n                    C_row_max_block.raw());\n                    cudaCheckError();\n\n    int max_nnz = thrust_wrapper::reduce<AMGX_device>(\n        C_row_max_block.raw(), \n        C_row_max_block.raw() + C_row_max_block.size(), \n        0, amgx::thrust::maximum<int>());\n\n#define CNZ_OPT(group_size, hash_size) \\\n    csr_multiply_detail::count_non_zeroes_kernel_opt<group_size, cta_size, hash_size> \\\n        <<<grid_size, cta_size>>>( \\\n        A.get_num_rows(), \\\n        A.row_offsets.raw(), \\\n        A.col_indices.raw(), \\\n        B.row_offsets.raw(), \\\n        B.col_indices.raw(), \\\n        C.row_offsets.raw());\n        cudaCheckError();\n\n    // Operation is group per row, where group size is determined by num_threads\n    switch ( num_threads )\n    {\n        // 16 threads per group\n        case 16:\n            {\n                int ngroups = cta_size / 16;\n                int grid_size = A.get_num_rows() / ngroups + 1;\n\n                if(max_nnz < 256)\n                { \n                    CNZ_OPT(16, 256); \n                } \n                else if(max_nnz < 512) \n                { \n                    CNZ_OPT(16, 512); \n                } \n                else if(max_nnz < 1024)\n                { \n                    CNZ_OPT(16, 1024); \n                }\n                else \n                {\n                    return false;\n                }\n            }\n            break;\n\n        // 32 threads per group\n        case 32:\n            {\n                int ngroups = cta_size / 32;\n                int grid_size = A.get_num_rows() / ngroups + 1;\n\n                if(max_nnz < 256)\n                { \n                    CNZ_OPT(32, 256); \n                } \n                else if(max_nnz < 512) \n                { \n                    CNZ_OPT(32, 512); \n                } \n                else if(max_nnz < 1024)\n                { \n                    CNZ_OPT(32, 1024); \n                }\n                else\n                {\n                    return false;\n                }\n            }\n            break;\n\n        default:\n            FatalError(\"count_non_zeros_opt only implemented for group size = 8, 16, 32\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n\n    return true;\n}\n\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size>\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::cvk_opt(const Matrix_d &A, const Matrix_d &B, Matrix_d &C)\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = A.get_num_rows() / ngroups + 1;\n\n    cudaDeviceProp deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(Value_type)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    cudaFuncSetAttribute(csr_multiply_detail::compute_values_kernel_opt \n            <group_size, cta_size, hash_size, Value_type>, \n            cudaFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    csr_multiply_detail::compute_values_kernel_opt \n        <group_size, cta_size, hash_size> \n        <<<grid_size, cta_size, shmem_size>>>( \n                A.get_num_rows(), \n                A.row_offsets.raw(), \n                A.col_indices.raw(), \n                A.values.raw(), \n                B.row_offsets.raw(), \n                B.col_indices.raw(), \n                B.values.raw(), \n                C.row_offsets.raw(), \n                C.col_indices.raw(), \n                C.values.raw()); \n                cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values_opt( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads, int max_nnz )\n{\n    int C_nrows = C.get_num_rows();\n    int C_nnz = C.get_num_nz();\n    int C_max_nnz_per_row = max_nnz;\n\n    // The aim is to minimise the hash size while reducing the impact of the linear\n    // probing. It might actually be more optimal to just use as large tables as \n    // possible, reducing the linear probing cost and maximising the C write cost?\n    float C_max_nnz_log2 = log2(static_cast<float>(C_max_nnz_per_row));\n    float C_max_nnz_log2_ceil = ceil(C_max_nnz_log2);\n    int C_rounded_max = static_cast<int>(2.0*pow(2.0, C_max_nnz_log2_ceil));\n\n    // Operation is group per row, where group size is determined by num_threads\n    switch ( num_threads )\n    {\n        case 16: // 16 threads per group\n            {\n                switch(C_rounded_max)\n                {\n                    case 2: \n                    case 4:\n                    case 8: \n                    case 16:\n                    case 32: cvk_opt<32, 8>(A, B, C); break;\n                    case 64: cvk_opt<64, 8>(A, B, C); break;\n                    case 128: cvk_opt<128, 8>(A, B, C); break;\n                    case 256: cvk_opt<256, 8>(A, B, C); break;\n                    case 512: cvk_opt<512, 16>(A, B, C); break;\n                    default: \n                       FatalError(\"In compute_values_opt the requested hash size is too large.\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n                }\n            }\n            break;\n        case 32: // Warp per group\n            {\n                switch(C_rounded_max)\n                {\n                    case 2: \n                    case 4:\n                    case 8: \n                    case 16:\n                    case 32: cvk_opt<32, 32>(A, B, C); break;\n                    case 64: cvk_opt<64, 32>(A, B, C); break;\n                    case 128: cvk_opt<128, 32>(A, B, C); break;\n                    case 256: cvk_opt<256, 32>(A, B, C); break;\n                    case 512: cvk_opt<512, 32>(A, B, C); break;\n                    case 1024: cvk_opt<1024, 32>(A, B, C); break;\n                    default: \n                       FatalError(\"In compute_values_opt the requested hash size is too large.\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n                }\n            }\n            break;\n        default:\n            FatalError(\"compute_values_opt only implemented for group size = 16, 32\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaDeviceSynchronize();\n\n    cudaCheckError();\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads, IVector *Aq1, IVector *Bq1, IVector *Aq2, IVector *Bq2  )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 128;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n    // Compute the values.\n    int *status = NULL;\n\n    if ( num_threads != this->m_num_threads_per_row_compute )\n    {\n        status = this->m_status;\n    }\n\n    \t\t\n\n    switch ( num_threads )\n    {\n        case 2:\n            csr_multiply_detail::compute_values_kernel< 2, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 4:\n            csr_multiply_detail::compute_values_kernel< 4, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 8:\n            csr_multiply_detail::compute_values_kernel< 8, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 16:\n            csr_multiply_detail::compute_values_kernel<16, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        default:\n            csr_multiply_detail::compute_values_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values_RAP_sparse_add( Matrix_d &RAP, const Matrix_d &RAP_int, std::vector<IVector> &RAP_ext_row_offsets, std::vector<IVector> &RAP_ext_col_indices, std::vector<MVector> &RAP_ext_values, std::vector<IVector> &RAP_ext_row_ids, int num_threads)\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 128;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( cudaMemcpy( this->m_work_queue, &work_offset, sizeof(int), cudaMemcpyHostToDevice ) );\n    // Compute the values.\n    int *status = NULL;\n\n    if ( num_threads != this->m_num_threads_per_row_compute )\n    {\n        status = this->m_status;\n    }\n\n    // This is num_owned_coarse_rows\n    int RAP_size = RAP.get_num_rows();\n    int RAP_int_size = RAP_int.row_offsets.size() - 1;\n\n    if (RAP_int_size < RAP_size)\n    {\n        FatalError(\"RAP_int has less rows than RAP, need to modify sparse RAP add to handle that case\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //TODO: Optimize: reuse arrays from count nonzeros\n    int num_neighbors = RAP_ext_row_offsets.size();\n    std::vector<IVector> flagArray(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray[i].resize(RAP_size);\n        thrust_wrapper::fill<AMGX_device>(flagArray[i].begin(), flagArray[i].end(), -1);\n    }\n\n    cudaCheckError();\n    std::vector<int *> flagArray_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_row_offsets_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_col_indices_ptrs_h(num_neighbors);\n    std::vector<Value_type *> RAP_ext_values_ptrs_h(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&flagArray[i][0]);\n        RAP_ext_row_offsets_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets[i][0]);\n        RAP_ext_col_indices_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices[i][0]);\n        RAP_ext_values_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_values[i][0]);\n    }\n\n    device_vector_alloc<int *> flagArray_ptrs = flagArray_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_row_offsets_ptrs = RAP_ext_row_offsets_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_col_indices_ptrs = RAP_ext_col_indices_ptrs_h;\n    device_vector_alloc<Value_type *> RAP_ext_values_ptrs = RAP_ext_values_ptrs_h;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = RAP_ext_row_ids[i].size();\n\n        if (size != 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //write the position in RAP_ext_row_ids\n            csr_multiply_detail::flag_halo_rows <<< num_blocks, 128>>>(\n                RAP_ext_row_ids[i].raw(),\n                size,\n                flagArray[i].raw(),\n                i,\n                RAP.manager->global_id());\n                cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n    csr_multiply_detail::compute_values_RAP_ext_kernel< Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n        RAP_size,\n        RAP_int.row_offsets.raw(),\n        RAP_int.col_indices.raw(),\n        RAP_int.values.raw(),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_values_ptrs[0]),\n        RAP.row_offsets.raw(),\n        RAP.col_indices.raw(),\n        RAP.values.raw(),\n        amgx::thrust::raw_pointer_cast(&flagArray_ptrs[0]),\n        this->m_gmem_size,\n        this->m_keys,\n        this->m_vals,\n        this->m_work_queue,\n        num_neighbors,\n        status );\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class CSR_Multiply_Detail<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cassert>\n#include <iostream>\n#include <thrust/scan.h>\n#include <util.h>\n#include <csr_multiply.h>\n#include <csr_multiply_detail.h>\n#include <device_properties.h>\n#include <thrust_wrapper.h>\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace csr_multiply_detail\n{\n\n#include <amgx_types/util.h>\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__\nvoid flag_halo_rows(int *row_ids, int size, int *flagArray, int neighbor, int global_id)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int row_id = row_ids[tidx];\n        flagArray[row_id] = tidx;\n    }\n}\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_kernel( const int A_num_rows,\n                         const int *A_rows,\n                         const int *A_cols,\n                         const int *B_rows,\n                         const int *B_cols,\n                         int *C_rows,\n                         int *C_cols,\n                         int *Aq1,\n                         int *Bq1,\n                         int *Aq2,\n                         int *Bq2,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n\t    \t\t\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            // The number of valid rows.\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( lane_id < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n\n                        // b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    set.insert( b_col_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[c_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_kernel( const int A_num_rows,\n                         const int *__restrict A_rows,\n                         const int *__restrict A_cols,\n                         const int *__restrict B_rows,\n                         const int *__restrict B_cols,\n                         int *__restrict C_rows,\n                         int *__restrict C_cols,\n                         int *Aq1,\n                         int *Bq1,\n                         int *Aq2,\n                         int *Bq2,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, local_k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( is_active_k && lane_id_mod_num_threads < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n\n                        // b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    set.insert( b_col_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[c_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\ntemplate <int SMEM_SIZE, int NUM_HASH_FCTS, int WARP_SIZE, bool COUNT_ONLY >\n__device__ __forceinline__ void sparse_add_process_row(int row_id, const int *__restrict__ row_offsets, const int *__restrict__ col_indices, int lane_id, Hash_set<int, SMEM_SIZE, NUM_HASH_FCTS, WARP_SIZE> &set, int *wk_status, int global_id, bool print_flag)\n{\n    // Load the range of the row of RAP_int\n    int col_tmp = -1;\n\n    if ( lane_id < 2 )\n    {\n        col_tmp = utils::Ld<utils::LD_NC>::load( &row_offsets[row_id + lane_id] );\n    }\n\n    int col_it  = utils::shfl( col_tmp, 0 );\n    int col_end = utils::shfl( col_tmp, 1 );\n\n    // Iterate over the columns of RAP_int\n    for ( col_it += lane_id ; utils::any(col_it < col_end) ; col_it += WARP_SIZE )\n    {\n        int col_id = -1;\n\n        if ( col_it < col_end )\n        {\n            col_id = utils::Ld<utils::LD_NC>::load( &col_indices[col_it] );\n        }\n\n        set.insert( col_id, COUNT_ONLY ? wk_status : NULL );\n    }\n}\n\n\ntemplate <typename Value_type, int SMEM_SIZE, int NUM_HASH_FCTS, int WARP_SIZE>\n__device__ __forceinline__ void sparse_add_process_row_values(int row_id, const int *__restrict__ row_offsets, const int *__restrict__ col_indices, const Value_type *__restrict vals, int lane_id, Hash_map<int, Value_type, SMEM_SIZE, NUM_HASH_FCTS, WARP_SIZE> &map, int *wk_status)\n{\n    // Load the range of the row.\n    int col_tmp = -1;\n\n    if ( lane_id < 2 )\n    {\n        col_tmp = utils::Ld<utils::LD_NC>::load( &row_offsets[row_id + lane_id] );\n    }\n\n    int col_it  = utils::shfl( col_tmp, 0 );\n    int col_end = utils::shfl( col_tmp, 1 );\n\n    // Iterate over the columns of A.\n    for ( col_it += lane_id ; utils::any(col_it < col_end) ; col_it += WARP_SIZE )\n    {\n        const bool is_active = col_it < col_end;\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int col_id(-1);\n        Value_type value = amgx::types::util<Value_type>::get_zero();\n\n        if ( is_active )\n        {\n            col_id = utils::Ld<utils::LD_NC>::load( &col_indices[col_it] );\n            value  = utils::Ld<utils::LD_NC>::load( &vals[col_it] );\n        }\n\n        map.insert( col_id, value, wk_status );\n    }\n}\n\n\n\n\ntemplate<int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_RAP_ext_kernel( const int RAP_int_num_rows,\n                                 const int *__restrict RAP_int_rows,\n                                 const int *__restrict RAP_int_cols,\n                                 int **RAP_ext_row_ptrs,\n                                 int **RAP_ext_col_ptrs,\n                                 int *__restrict RAP_rows,\n                                 int *__restrict RAP_cols,\n                                 int **flagArray_ptrs,\n                                 const int gmem_size,\n                                 int *g_keys,\n                                 int *wk_work_queue,\n                                 int *wk_status,\n                                 int num_neighbors,\n                                 int global_id )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int rap_int_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[rap_int_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of RAP_ext\n    for ( ; rap_int_row_id < RAP_int_num_rows; rap_int_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // ---------------------------------\n        // First process RAP_int\n        // ---------------------------------\n        bool print_flag = false;\n\n        if (rap_int_row_id == 3 && global_id == 0)\n        {\n            print_flag = true;\n        }\n\n        sparse_add_process_row<SMEM_SIZE, 4, WARP_SIZE, COUNT_ONLY>(rap_int_row_id, RAP_int_rows, RAP_int_cols, lane_id, set, wk_status, global_id, print_flag);\n\n        // ---------------------------------\n        // Then process RAP_ext\n        // ---------------------------------\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int flag = flagArray_ptrs[i][rap_int_row_id];\n\n            if (flag != -1)\n            {\n                int *RAP_ext_rows = RAP_ext_row_ptrs[i];\n                int *RAP_ext_cols = RAP_ext_col_ptrs[i];\n                int pos_in_row_ext = flag;\n                sparse_add_process_row<SMEM_SIZE, 4, WARP_SIZE, COUNT_ONLY>(pos_in_row_ext, RAP_ext_rows, RAP_ext_cols, lane_id, set, wk_status, global_id, print_flag);\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                RAP_rows[rap_int_row_id] = count;\n            }\n        }\n        else\n        {\n            int rap_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                rap_col_tmp = utils::Ld<utils::LD_NC>::load( &RAP_rows[rap_int_row_id + lane_id] );\n            }\n\n            int rap_col_it  = utils::shfl( rap_col_tmp, 0 );\n            int rap_col_end = utils::shfl( rap_col_tmp, 1 );\n            // Store the results.\n            int count = rap_col_end - rap_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &RAP_cols[rap_col_it] );\n        }\n    }\n}\n\n\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstruct Without_external_diag\n{\n    static __device__ __forceinline__ bool is_active  ( int a_col_it, int a_col_end ) { return a_col_it < a_col_end; }\n    static __device__ __forceinline__ bool is_boundary( int a_col_it, int a_col_end ) { return false; }\n};\n\n// ====================================================================================================================\n\nstruct With_external_diag\n{\n    static __device__ __forceinline__ bool is_active  ( int a_col_it, int a_col_end ) { return a_col_it <= a_col_end; }\n    static __device__ __forceinline__ bool is_boundary( int a_col_it, int a_col_end ) { return a_col_it == a_col_end; }\n};\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY, typename Diag_traits >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_ilu1_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const int *__restrict A_coloring,\n                              int *__restrict C_rows,\n                              int *__restrict C_cols,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *wk_work_queue,\n                              int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Tables to broadcast values.\n    __shared__ volatile int s_b_rows[CTA_SIZE], s_b_colors[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // The color of the row.\n        int a_row_color = A_coloring[a_row_id];\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(Diag_traits::is_active(a_col_it, a_col_end)) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n            }\n\n            if ( Diag_traits::is_boundary(a_col_it, a_col_end) )\n            {\n                b_row_id = a_row_id;\n            }\n\n            // Push the columns in the set.\n            set.insert( b_row_id, COUNT_ONLY ? wk_status : NULL );\n\n            // Skip computation if the color of the row is 0.\n            if ( a_row_color != 0 )\n            {\n                // Gather the colors of the columns.\n                int b_row_color = -1;\n\n                if ( is_active )\n                {\n                    b_row_color = A_coloring[b_row_id];\n                }\n\n                // The number of valid rows.\n                int pred = is_active && b_row_color < a_row_color;\n                int vote = utils::ballot( pred );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( pred )\n                {\n                    s_b_rows  [warp_id * WARP_SIZE + dest] = b_row_id;\n                    s_b_colors[warp_id * WARP_SIZE + dest] = b_row_color; // TODO: store an int2 rather than 2 ints of SM35 with 64bit banks.\n                }\n\n                const int num_rows = __popc( vote );\n\n                // Uniform loop: threads collaborate to load other elements.\n                for ( int k = 0 ; k < num_rows ; ++k )\n                {\n                    // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                    const int uniform_b_row_id = s_b_rows  [warp_id * WARP_SIZE + k];\n                    const int uniform_b_color  = s_b_colors[warp_id * WARP_SIZE + k];\n                    // Load the range of the row of B.\n                    int b_col_tmp = -1;\n\n                    if ( lane_id < 2 )\n                    {\n                        b_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[uniform_b_row_id + lane_id] );\n                    }\n\n                    int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                    int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                    // Iterate over the range of columns of B.\n                    for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                    {\n                        int b_col_id = -1, b_col_color = -1;\n\n                        if ( b_col_it < b_col_end )\n                        {\n                            b_col_id    = utils::Ld<utils::LD_NC>::load( &A_cols[b_col_it] );\n                            b_col_color = utils::Ld<utils::LD_NC>::load( &A_coloring[b_col_id] );\n                        }\n\n                        int item = -1;\n\n                        if ( b_col_color >= uniform_b_color && b_col_color != a_row_color )\n                        {\n                            item = b_col_id;\n                        }\n\n                        set.insert( item, COUNT_ONLY ? wk_status : NULL );\n                    }\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[a_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[a_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool COUNT_ONLY, typename Diag_traits >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncount_non_zeroes_ilu1_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const int *__restrict A_coloring,\n                              int *__restrict C_rows,\n                              int *__restrict C_cols,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *wk_work_queue,\n                              int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Tables to broadcast values.\n    __shared__ volatile int s_b_rows[CTA_SIZE], s_b_colors[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // The color of the row.\n        int a_row_color = A_coloring[a_row_id];\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(Diag_traits::is_active(a_col_it, a_col_end)) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n            }\n\n            if ( Diag_traits::is_boundary(a_col_it, a_col_end) )\n            {\n                b_row_id = a_row_id;\n            }\n\n            // Push the columns in the set.\n            set.insert( b_row_id, COUNT_ONLY ? wk_status : NULL );\n\n            // Skip computation if the color of the row is 0.\n            if ( a_row_color != 0 )\n            {\n                // Gather the colors of the columns.\n                int b_row_color = -1;\n\n                if ( is_active )\n                {\n                    b_row_color = A_coloring[b_row_id];\n                }\n\n                // The number of valid rows.\n                int pred = is_active && b_row_color < a_row_color;\n                int vote = utils::ballot( pred );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( pred )\n                {\n                    s_b_rows  [warp_id * WARP_SIZE + dest] = b_row_id;\n                    s_b_colors[warp_id * WARP_SIZE + dest] = b_row_color;\n                }\n\n                const int num_rows = __popc( vote );\n\n                // Uniform loop: threads collaborate to load other elements.\n                for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n                {\n                    int local_k = k + lane_id_div_num_threads;\n                    // Is it an active thread.\n                    bool is_active_k = local_k < num_rows;\n                    // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                    int uniform_b_row_id = -1, uniform_b_color = -1;\n\n                    if ( is_active_k )\n                    {\n                        uniform_b_row_id = s_b_rows  [warp_id * WARP_SIZE + local_k];\n                        uniform_b_color  = s_b_colors[warp_id * WARP_SIZE + local_k];\n                    }\n\n                    // Load the range of the row of B.\n                    int b_col_tmp = -1;\n\n                    if ( is_active_k && lane_id_mod_num_threads < 2 )\n                    {\n                        b_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                    }\n\n                    int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                    int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                    // Iterate over the range of columns of B.\n                    for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                    {\n                        int b_col_id = -1, b_col_color = -1;\n\n                        if ( b_col_it < b_col_end )\n                        {\n                            b_col_id    = utils::Ld<utils::LD_NC>::load( &A_cols[b_col_it] );\n                            b_col_color = utils::Ld<utils::LD_NC>::load( &A_coloring[b_col_id] );\n                        }\n\n                        int item = -1;\n\n                        if ( b_col_color >= uniform_b_color && b_col_color != a_row_color )\n                        {\n                            item = b_col_id;\n                        }\n\n                        set.insert( item, COUNT_ONLY ? wk_status : NULL );\n                    }\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                C_rows[a_row_id] = count;\n            }\n        }\n        else\n        {\n            int c_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[a_row_id + lane_id] );\n            }\n\n            int c_col_it  = utils::shfl( c_col_tmp, 0 );\n            int c_col_end = utils::shfl( c_col_tmp, 1 );\n            // Store the results.\n            int count = c_col_end - c_col_it;\n\n            if ( count == 0 )\n            {\n                continue;\n            }\n\n            set.store( count, &C_cols[c_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_kernel( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Value_type *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const Value_type *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       Value_type *__restrict C_vals,\n                       int *Aq1,\n                       int *Bq1,\n                       int *Aq2,\n                       int *Bq2,\n                       const int gmem_size,\n                       int *g_keys,\n                       Value_type *g_vals,\n                       int *wk_work_queue,\n                       int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map(&s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Clear the map.\n        map.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id = -1;\n            Value_type a_value = amgx::types::util<Value_type>::get_zero();\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n                a_value  = utils::Ld<utils::LD_NC>::load( &A_vals[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k );\n                // The value of A.\n                const Value_type uniform_a_value = utils::shfl( a_value, k );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( lane_id < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, 0 );\n                int b_col_end = utils::shfl( b_col_tmp, 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    int b_col_id = -1;\n                    Value_type b_value = amgx::types::util<Value_type>::get_zero();\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n                        b_value  = utils::Ld<utils::LD_NC>::load( &B_vals[b_col_it] );\n\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    map.insert( b_col_id, uniform_a_value * b_value, wk_status );\n                }\n            }\n        }\n\n        // Store the results.\n        int c_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n        }\n\n        int c_col_it  = utils::shfl( c_col_tmp, 0 );\n        int c_col_end = utils::shfl( c_col_tmp, 1 );\n        // Store the results.\n        int count = c_col_end - c_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &C_cols[c_col_it], &C_vals[c_col_it] );\n    }\n\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE>\n__global__ \nvoid count_non_zeroes_kernel_opt( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       int *__restrict C_row_counts)\n{\n    // Defines for hash container\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    // One row of A per group\n    const int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Block-level hash container storage\n    __shared__ int key_s[ngroups*HASH_SIZE];\n    __shared__ int counts[ngroups];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n    }\n\n    for(int i = 0; i < ngroups; ++i)\n    {\n        counts[i] = 0;\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n\n    if(a_row_id < A_num_rows)\n    {\n        // Distributed columns of row of A over threads in group.\n        for (int a_col_it = A_rows[a_row_id] + lane_id; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE)\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Outer product of element of A and row of B.\n            for (int b_col_it = B_rows[a_col_id]; b_col_it < B_rows[a_col_id + 1] ; ++b_col_it)\n            {\n                int b_col_id = B_cols[b_col_it];\n\n                int hash = b_col_id % HASH_SIZE;\n\n                // By construction this algorithm should guarantee \n                // all keys can be inserted\n                while(true)\n                {\n                    // If the slot is vacant, then attempt acquire\n                    int key = key_group_s[hash];\n\n                    // Check if the key was already b_col_id, or was already set \n                    // Insert the product\n                    if(key == b_col_id)\n                    {\n                        break;\n                    }\n\n                    if(key == SLOT_VACANT)\n                    {\n                        int new_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n\n                        if(new_key == SLOT_VACANT || new_key == b_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    // Store the results.\n    if(a_row_id < A_num_rows)\n    {\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            if(key_group_s[i] != SLOT_VACANT)\n            {\n                atomicAdd(&counts[group_id], 1);\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(a_row_id < A_num_rows && lane_id == 0)\n    {\n        C_row_counts[a_row_id] = counts[group_id];\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE, class ValueType>\n__global__ \nvoid compute_values_kernel_opt( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const ValueType *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const ValueType *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       ValueType *__restrict C_vals)\n{\n    // Defines for hash container\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    // One row of A per group\n    const int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Block-level hash container storage\n    int* key_s = s;\n    ValueType* data_s = (ValueType*)&key_s[ngroups*HASH_SIZE];\n    int* col_ind_s = (int*)&data_s[ngroups*HASH_SIZE];\n\n    // Group-level hash containers\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n    ValueType* data_group_s = &data_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n        data_s[i] = amgx::types::util<ValueType>::get_zero(); // We will sum into values\n    }\n\n    if(lane_id == 0)\n    {\n        col_ind_s[group_id] = 0;\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    if(a_row_id < A_num_rows)\n    {\n        // Distributed columns of row of A over threads in group.\n        for (int a_col_it = A_rows[a_row_id] + lane_id; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE)\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Outer product of element of A and row of B.\n            for (int b_col_it = B_rows[a_col_id]; b_col_it < B_rows[a_col_id + 1] ; ++b_col_it)\n            {\n                ValueType val = A_vals[a_col_it]*B_vals[b_col_it];\n\n                // Short circuit if zero\n                if(amgx::types::util<ValueType>::is_zero(val)) continue;\n\n                int b_col_id = B_cols[b_col_it];\n\n                // XXX UPDATE HASHING APPROACH\n                int hash = b_col_id % HASH_SIZE;\n\n                // By construction this algorithm should guarantee \n                // all keys can be inserted\n                while(true)\n                {\n                    // If the slot is vacant, then attempt acquire\n                    int key = key_group_s[hash];\n                    if(key == SLOT_VACANT)\n                    {\n                        int new_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n                        if(new_key == SLOT_VACANT || new_key == b_col_id)\n                        {\n                            key = b_col_id;\n                        }\n                    }\n\n                    // Check if the key was already b_col_id, or was already set \n                    // Insert the product\n                    if(key == b_col_id)\n                    {\n                        utils::atomic_add(&data_group_s[hash], val);\n                        break;\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    //__syncwarp();\n    __syncthreads();\n\n    // Store the results.\n    int c_row_id  = a_row_id;\n    int c_col_it  = C_rows[c_row_id];\n\n    if(a_row_id < A_num_rows)\n    {\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            if(key_group_s[i] != SLOT_VACANT)\n            {\n                // Short circuit if zero\n                if(amgx::types::util<ValueType>::is_zero(data_group_s[i])) continue;\n\n                int ind = atomicAdd(&col_ind_s[group_id], 1);\n\n                C_cols[c_col_it + ind] = key_group_s[i];\n                C_vals[c_col_it + ind] = data_group_s[i];\n            }\n        }\n    }\n}\n\ntemplate< int NUM_THREADS_PER_ROW, typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_kernel( const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Value_type *__restrict A_vals,\n                       const int *__restrict B_rows,\n                       const int *__restrict B_cols,\n                       const Value_type *__restrict B_vals,\n                       const int *__restrict C_rows,\n                       int *__restrict C_cols,\n                       Value_type *__restrict C_vals,\n                       int *Aq1,\n                       int *Bq1,\n                       int *Aq2,\n                       int *Bq2,\n                       const int gmem_size,\n                       int *g_keys,\n                       Value_type *g_vals,\n                       int *wk_work_queue,\n                       int *wk_status )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map(&s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int c_row_id = a_row_id;\n\n        if (Aq1 != NULL)\n        {\n            a_row_id = Aq1[a_row_id];\n        }\n\n        // Clear the map.\n        map.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = utils::Ld<utils::LD_NC>::load( &A_rows[a_row_id + lane_id] );\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int b_row_id(-1);\n            Value_type a_value = amgx::types::util<Value_type>::get_zero();\n\n            if ( is_active )\n            {\n                b_row_id = utils::Ld<utils::LD_NC>::load( &A_cols[a_col_it] );\n                a_value  = utils::Ld<utils::LD_NC>::load( &A_vals[a_col_it] );\n\n                //b_row_id is actually column of A\n                if (Aq2 != NULL)\n                {\n                    b_row_id = Aq2[b_row_id];\n                }\n\n                if (Bq1 != NULL)\n                {\n                    b_row_id = Bq1[b_row_id];\n                }\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_b_row_id = utils::shfl( b_row_id, k + lane_id_div_num_threads );\n                // The value of A.\n                const Value_type uniform_a_value = utils::shfl( a_value, k + lane_id_div_num_threads );\n                // Load the range of the row of B.\n                int b_col_tmp = -1;\n\n                if ( is_active_k && lane_id_mod_num_threads < 2 )\n                {\n                    b_col_tmp = utils::Ld<utils::LD_NC>::load( &B_rows[uniform_b_row_id + lane_id_mod_num_threads] );\n                }\n\n                int b_col_it  = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 0 );\n                int b_col_end = utils::shfl( b_col_tmp, lane_id_div_num_threads * NUM_THREADS_PER_ROW + 1 );\n\n                // Iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int b_col_id(-1);\n                    Value_type b_value = amgx::types::util<Value_type>::get_zero();\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = utils::Ld<utils::LD_NC>::load( &B_cols[b_col_it] );\n                        b_value  = utils::Ld<utils::LD_NC>::load( &B_vals[b_col_it] );\n\n                        //b_col_id is actually column of B\n                        if (Bq2 != NULL)\n                        {\n                            b_col_id = Bq2[b_col_id];\n                        }\n                    }\n\n                    map.insert( b_col_id, uniform_a_value * b_value, wk_status );\n                }\n            }\n        }\n\n        // Store the results.\n        int c_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            c_col_tmp = utils::Ld<utils::LD_NC>::load( &C_rows[c_row_id + lane_id] );\n        }\n\n        int c_col_it  = utils::shfl( c_col_tmp, 0 );\n        int c_col_end = utils::shfl( c_col_tmp, 1 );\n        // Store the results.\n        int count = c_col_end - c_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &C_cols[c_col_it], &C_vals[c_col_it] );\n    }\n\n}\n\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE, 6 )\nvoid\ncompute_values_RAP_ext_kernel( const int RAP_int_num_rows,\n                               const int *__restrict RAP_int_rows,\n                               const int *__restrict RAP_int_cols,\n                               const Value_type *__restrict RAP_int_vals,\n                               int **RAP_ext_row_ptrs,\n                               int **RAP_ext_col_ptrs,\n                               Value_type **RAP_ext_val_ptrs,\n                               int *__restrict RAP_rows,\n                               int *__restrict RAP_cols,\n                               Value_type *__restrict RAP_vals,\n                               int **flagArray_ptrs,\n                               const int gmem_size,\n                               int *g_keys,\n                               Value_type *g_vals,\n                               int *wk_work_queue,\n                               int num_neighbors,\n                               int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ /*volatile*/ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int rap_int_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[rap_int_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[rap_int_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of RAP_ext\n    for ( ; rap_int_row_id < RAP_int_num_rows ; rap_int_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Clear the map.\n        map.clear();\n        // ---------------------------------\n        // First process RAP_int\n        // ---------------------------------\n        sparse_add_process_row_values<Value_type, SMEM_SIZE, 4, WARP_SIZE>(rap_int_row_id, RAP_int_rows, RAP_int_cols, RAP_int_vals, lane_id, map, wk_status);\n\n        // ---------------------------------\n        // Then process RAP_ext\n        // ---------------------------------\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int flag = flagArray_ptrs[i][rap_int_row_id];\n\n            if (flag != -1)\n            {\n                int *RAP_ext_rows = RAP_ext_row_ptrs[i];\n                int *RAP_ext_cols = RAP_ext_col_ptrs[i];\n                Value_type *RAP_ext_vals = RAP_ext_val_ptrs[i];\n                int pos_in_row_ext = flag;\n                sparse_add_process_row_values<Value_type, SMEM_SIZE, 4, WARP_SIZE>(pos_in_row_ext, RAP_ext_rows, RAP_ext_cols, RAP_ext_vals, lane_id, map, wk_status);\n            }\n        }\n\n        // Store the results.\n        int rap_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            rap_col_tmp = utils::Ld<utils::LD_NC>::load( &RAP_rows[rap_int_row_id + lane_id] );\n        }\n\n        int rap_col_it  = utils::shfl( rap_col_tmp, 0 );\n        int rap_col_end = utils::shfl( rap_col_tmp, 1 );\n        // Store the results.\n        int count = rap_col_end - rap_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &RAP_cols[rap_col_it], &RAP_vals[rap_col_it] );\n    }\n}\n\n\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace csr_multiply_detail\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace amgx\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nCSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::CSR_Multiply_Detail( bool allocate_values, int grid_size, int max_warp_count, int gmem_size ) :\n    Base(allocate_values, grid_size, max_warp_count, gmem_size)\n{}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, IVector *Aq1, IVector *Bq1, IVector *Aq2, IVector *Bq2 )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n    \n    // Compute non-zero elements.\n    switch ( this->m_num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                NULL,\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                this->m_status );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_RAP_sparse_add( Matrix_d &RAP, const Matrix_d &RAP_int, std::vector<IVector> &RAP_ext_row_offsets, std::vector<IVector> &RAP_ext_col_indices, std::vector<MVector> &RAP_ext_values, std::vector<IVector> &RAP_ext_row_ids)\n\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n    // This is num_owned_coarse_rows\n    int RAP_size = RAP.get_num_rows();\n    int RAP_int_size = RAP_int.row_offsets.size() - 1;\n\n    if (RAP_int_size < RAP_size)\n    {\n        FatalError(\"RAP_int has less rows than RAP, need to modify sparse RAP add to handle that case\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create a device vector of the raw pointers to the array\n    // RAP_ext_row_ids_ptrs\n    // RAP_ext_row_offsets_ptrs\n    // RAP_ext_col_indices_ptrs\n    // RAP_ext_values_ptrs\n    int num_neighbors = RAP_ext_row_offsets.size();\n    std::vector<IVector> flagArray(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray[i].resize(RAP_size);\n        thrust_wrapper::fill<AMGX_device>(flagArray[i].begin(), flagArray[i].end(), -1);\n    }\n\n    cudaCheckError();\n    std::vector<int *> flagArray_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_row_offsets_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_col_indices_ptrs_h(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&flagArray[i][0]);\n        RAP_ext_row_offsets_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets[i][0]);\n        RAP_ext_col_indices_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices[i][0]);\n    }\n\n    device_vector_alloc<int *> flagArray_ptrs = flagArray_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_row_offsets_ptrs = RAP_ext_row_offsets_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_col_indices_ptrs = RAP_ext_col_indices_ptrs_h;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = RAP_ext_row_ids[i].size();\n\n        if (size != 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //write the position in RAP_ext_row_ids\n            csr_multiply_detail::flag_halo_rows <<< num_blocks, 128>>>(\n                RAP_ext_row_ids[i].raw(),\n                size,\n                flagArray[i].raw(),\n                i,\n                RAP.manager->global_id());\n                cudaCheckError();\n        }\n    }\n\n    csr_multiply_detail::count_non_zeroes_RAP_ext_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, true> <<< GRID_SIZE, CTA_SIZE>>>(\n        RAP_size,\n        RAP_int.row_offsets.raw(),\n        RAP_int.col_indices.raw(),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices_ptrs[0]),\n        RAP.row_offsets.raw(),\n        (int *) NULL,\n        amgx::thrust::raw_pointer_cast(&flagArray_ptrs[0]),\n        this->m_gmem_size,\n        this->m_keys,\n        this->m_work_queue,\n        this->m_status,\n        num_neighbors,\n        RAP.manager->global_id() );\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< int CTA_SIZE, bool COUNT_ONLY, typename Diag_traits, typename Matrix >\nstatic void\ncount_non_zeroes_ilu1_dispatch( const Matrix &A, Matrix &B, int num_threads_per_row_count, int gmem_size, int *keys, int *work_queue, int *status )\n{\n    const int GRID_SIZE = 1024;\n\n    switch ( num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_ilu1_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, COUNT_ONLY, Diag_traits> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &A.getMatrixColoring().getRowColors()[0] ),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                gmem_size,\n                keys,\n                work_queue,\n                status );\n    }\n\n    cudaCheckError();\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_ilu1( const Matrix_d &A, Matrix_d &B )\n{\n    const int GRID_SIZE = 1024;\n\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n\n    // Count the number of non zeroes.\n    if ( A.hasProps(DIAG) )\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, true, csr_multiply_detail::With_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            this->m_status );\n    else\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, true, csr_multiply_detail::Without_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            this->m_status );\n\n    // Compute non-zero elements.\n    CUDA_SAFE_CALL( hipGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_offsets( Matrix_d &C )\n{\n    amgx::thrust::device_ptr<int> offsets_begin(C.row_offsets.raw());\n    amgx::thrust::device_ptr<int> offsets_end  (C.row_offsets.raw() + C.get_num_rows() + 1);\n    thrust_wrapper::exclusive_scan<AMGX_device>( offsets_begin, offsets_end, offsets_begin );\n    cudaCheckError();\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_sparsity( const Matrix_d &A, const Matrix_d &B, Matrix_d &C )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n\n    // Compute the values.\n    switch ( this->m_num_threads_per_row_count )\n    {\n        case 2:\n            csr_multiply_detail::count_non_zeroes_kernel< 2, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 4:\n            csr_multiply_detail::count_non_zeroes_kernel< 4, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 8:\n            csr_multiply_detail::count_non_zeroes_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        case 16:\n            csr_multiply_detail::count_non_zeroes_kernel<16, CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n                cudaCheckError();\n            break;\n\n        default:\n            csr_multiply_detail::count_non_zeroes_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                NULL,\n                NULL,\n                NULL,\n                NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_work_queue,\n                NULL );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_sparsity_ilu1( const Matrix_d &A, Matrix_d &B )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n\n    // Count the number of non zeroes.\n    if ( A.hasProps(DIAG) )\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, false, csr_multiply_detail::With_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            NULL );\n    else\n        count_non_zeroes_ilu1_dispatch<CTA_SIZE, false, csr_multiply_detail::Without_external_diag, Matrix_d>(\n            A,\n            B,\n            this->m_num_threads_per_row_count,\n            this->m_gmem_size,\n            this->m_keys,\n            this->m_work_queue,\n            NULL );\n\n    // Make sure it worked properly.\n    CUDA_SAFE_CALL( hipGetLastError() );\n}\n\n// ====================================================================================================================\n\ntemplate <int CTA_SIZE>\n__global__ \nvoid calc_max_nnz_per_row_of_C( \n                       const int A_num_rows,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const int *__restrict B_rows,\n                       int *__restrict C_row_max)\n{\n    int a_row_id = blockIdx.x*blockDim.x + threadIdx.x;\n\n    int expected_max_row_nnz = 0;\n\n    if(a_row_id < A_num_rows)\n    {\n        for (int a_col_it = A_rows[a_row_id]; a_col_it < A_rows[a_row_id + 1]; ++a_col_it)\n        {\n            int a_col_id = A_cols[a_col_it];\n            expected_max_row_nnz += B_rows[a_col_id+1]-B_rows[a_col_id];\n        }\n    }\n\n    using BR = amgx::hipcub::BlockReduce<int, CTA_SIZE>;\n\n    __shared__ typename BR::TempStorage max_s;\n    int max_nnz_block = BR(max_s).Reduce(expected_max_row_nnz, [] __device__ (int a, int b) { return a > b ? a : b; });\n\n    if(threadIdx.x == 0)\n    {\n        C_row_max[blockIdx.x] = max_nnz_block;\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nbool CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::count_non_zeroes_opt(\n        const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads)\n{\n    constexpr int cta_size = 128;\n\n    // At least for large matrices it may be optimal to determine the maximum\n    // expected hash size by investigation of the matrices\n\n    int grid_size = A.get_num_rows() / cta_size + 1;\n\n    IVector C_row_max_block(grid_size);\n    calc_max_nnz_per_row_of_C<cta_size><<<grid_size, cta_size>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    B.row_offsets.raw(),\n                    C_row_max_block.raw());\n                    cudaCheckError();\n\n    int max_nnz = thrust_wrapper::reduce<AMGX_device>(\n        C_row_max_block.raw(), \n        C_row_max_block.raw() + C_row_max_block.size(), \n        0, amgx::thrust::maximum<int>());\n\n#define CNZ_OPT(group_size, hash_size) \\\n    csr_multiply_detail::count_non_zeroes_kernel_opt<group_size, cta_size, hash_size> \\\n        <<<grid_size, cta_size>>>( \\\n        A.get_num_rows(), \\\n        A.row_offsets.raw(), \\\n        A.col_indices.raw(), \\\n        B.row_offsets.raw(), \\\n        B.col_indices.raw(), \\\n        C.row_offsets.raw());\n        cudaCheckError();\n\n    // Operation is group per row, where group size is determined by num_threads\n    switch ( num_threads )\n    {\n        // 16 threads per group\n        case 16:\n            {\n                int ngroups = cta_size / 16;\n                int grid_size = A.get_num_rows() / ngroups + 1;\n\n                if(max_nnz < 256)\n                { \n                    CNZ_OPT(16, 256); \n                } \n                else if(max_nnz < 512) \n                { \n                    CNZ_OPT(16, 512); \n                } \n                else if(max_nnz < 1024)\n                { \n                    CNZ_OPT(16, 1024); \n                }\n                else \n                {\n                    return false;\n                }\n            }\n            break;\n\n        // 32 threads per group\n        case 32:\n            {\n                int ngroups = cta_size / 32;\n                int grid_size = A.get_num_rows() / ngroups + 1;\n\n                if(max_nnz < 256)\n                { \n                    CNZ_OPT(32, 256); \n                } \n                else if(max_nnz < 512) \n                { \n                    CNZ_OPT(32, 512); \n                } \n                else if(max_nnz < 1024)\n                { \n                    CNZ_OPT(32, 1024); \n                }\n                else\n                {\n                    return false;\n                }\n            }\n            break;\n\n        default:\n            FatalError(\"count_non_zeros_opt only implemented for group size = 8, 16, 32\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n\n    return true;\n}\n\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size>\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::cvk_opt(const Matrix_d &A, const Matrix_d &B, Matrix_d &C)\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = A.get_num_rows() / ngroups + 1;\n\n    hipDeviceProp_t deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(Value_type)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    hipFuncSetAttribute(reinterpret_cast<const void*>(csr_multiply_detail::compute_values_kernel_opt \n            <group_size, cta_size, hash_size, Value_type>), \n            hipFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    csr_multiply_detail::compute_values_kernel_opt \n        <group_size, cta_size, hash_size> \n        <<<grid_size, cta_size, shmem_size>>>( \n                A.get_num_rows(), \n                A.row_offsets.raw(), \n                A.col_indices.raw(), \n                A.values.raw(), \n                B.row_offsets.raw(), \n                B.col_indices.raw(), \n                B.values.raw(), \n                C.row_offsets.raw(), \n                C.col_indices.raw(), \n                C.values.raw()); \n                cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values_opt( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads, int max_nnz )\n{\n    int C_nrows = C.get_num_rows();\n    int C_nnz = C.get_num_nz();\n    int C_max_nnz_per_row = max_nnz;\n\n    // The aim is to minimise the hash size while reducing the impact of the linear\n    // probing. It might actually be more optimal to just use as large tables as \n    // possible, reducing the linear probing cost and maximising the C write cost?\n    float C_max_nnz_log2 = log2(static_cast<float>(C_max_nnz_per_row));\n    float C_max_nnz_log2_ceil = ceil(C_max_nnz_log2);\n    int C_rounded_max = static_cast<int>(2.0*pow(2.0, C_max_nnz_log2_ceil));\n\n    // Operation is group per row, where group size is determined by num_threads\n    switch ( num_threads )\n    {\n        case 16: // 16 threads per group\n            {\n                switch(C_rounded_max)\n                {\n                    case 2: \n                    case 4:\n                    case 8: \n                    case 16:\n                    case 32: cvk_opt<32, 8>(A, B, C); break;\n                    case 64: cvk_opt<64, 8>(A, B, C); break;\n                    case 128: cvk_opt<128, 8>(A, B, C); break;\n                    case 256: cvk_opt<256, 8>(A, B, C); break;\n                    case 512: cvk_opt<512, 16>(A, B, C); break;\n                    default: \n                       FatalError(\"In compute_values_opt the requested hash size is too large.\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n                }\n            }\n            break;\n        case 32: // Warp per group\n            {\n                switch(C_rounded_max)\n                {\n                    case 2: \n                    case 4:\n                    case 8: \n                    case 16:\n                    case 32: cvk_opt<32, 32>(A, B, C); break;\n                    case 64: cvk_opt<64, 32>(A, B, C); break;\n                    case 128: cvk_opt<128, 32>(A, B, C); break;\n                    case 256: cvk_opt<256, 32>(A, B, C); break;\n                    case 512: cvk_opt<512, 32>(A, B, C); break;\n                    case 1024: cvk_opt<1024, 32>(A, B, C); break;\n                    default: \n                       FatalError(\"In compute_values_opt the requested hash size is too large.\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n                }\n            }\n            break;\n        default:\n            FatalError(\"compute_values_opt only implemented for group size = 16, 32\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    hipDeviceSynchronize();\n\n    cudaCheckError();\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values( const Matrix_d &A, const Matrix_d &B, Matrix_d &C, int num_threads, IVector *Aq1, IVector *Bq1, IVector *Aq2, IVector *Bq2  )\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 128;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n    // Compute the values.\n    int *status = NULL;\n\n    if ( num_threads != this->m_num_threads_per_row_compute )\n    {\n        status = this->m_status;\n    }\n\n    \t\t\n\n    switch ( num_threads )\n    {\n        case 2:\n            csr_multiply_detail::compute_values_kernel< 2, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 4:\n            csr_multiply_detail::compute_values_kernel< 4, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 8:\n            csr_multiply_detail::compute_values_kernel< 8, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        case 16:\n            csr_multiply_detail::compute_values_kernel<16, Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n            break;\n\n        default:\n            csr_multiply_detail::compute_values_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                B.row_offsets.raw(),\n                B.col_indices.raw(),\n                B.values.raw(),\n                C.row_offsets.raw(),\n                C.col_indices.raw(),\n                C.values.raw(),\n                (Aq1 != NULL) ? Aq1->raw() : NULL,\n                (Bq1 != NULL) ? Bq1->raw() : NULL,\n                (Aq2 != NULL) ? Aq2->raw() : NULL,\n                (Bq2 != NULL) ? Bq2->raw() : NULL,\n                this->m_gmem_size,\n                this->m_keys,\n                this->m_vals,\n                this->m_work_queue,\n                status );\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid CSR_Multiply_Detail<TemplateConfig<AMGX_device, V, M, I> >::compute_values_RAP_sparse_add( Matrix_d &RAP, const Matrix_d &RAP_int, std::vector<IVector> &RAP_ext_row_offsets, std::vector<IVector> &RAP_ext_col_indices, std::vector<MVector> &RAP_ext_values, std::vector<IVector> &RAP_ext_row_ids, int num_threads)\n{\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 128;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Reset the work queue.\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    CUDA_SAFE_CALL( hipMemcpy( this->m_work_queue, &work_offset, sizeof(int), hipMemcpyHostToDevice ) );\n    // Compute the values.\n    int *status = NULL;\n\n    if ( num_threads != this->m_num_threads_per_row_compute )\n    {\n        status = this->m_status;\n    }\n\n    // This is num_owned_coarse_rows\n    int RAP_size = RAP.get_num_rows();\n    int RAP_int_size = RAP_int.row_offsets.size() - 1;\n\n    if (RAP_int_size < RAP_size)\n    {\n        FatalError(\"RAP_int has less rows than RAP, need to modify sparse RAP add to handle that case\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //TODO: Optimize: reuse arrays from count nonzeros\n    int num_neighbors = RAP_ext_row_offsets.size();\n    std::vector<IVector> flagArray(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray[i].resize(RAP_size);\n        thrust_wrapper::fill<AMGX_device>(flagArray[i].begin(), flagArray[i].end(), -1);\n    }\n\n    cudaCheckError();\n    std::vector<int *> flagArray_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_row_offsets_ptrs_h(num_neighbors);\n    std::vector<int *> RAP_ext_col_indices_ptrs_h(num_neighbors);\n    std::vector<Value_type *> RAP_ext_values_ptrs_h(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        flagArray_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&flagArray[i][0]);\n        RAP_ext_row_offsets_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets[i][0]);\n        RAP_ext_col_indices_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices[i][0]);\n        RAP_ext_values_ptrs_h[i] = amgx::thrust::raw_pointer_cast(&RAP_ext_values[i][0]);\n    }\n\n    device_vector_alloc<int *> flagArray_ptrs = flagArray_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_row_offsets_ptrs = RAP_ext_row_offsets_ptrs_h;\n    device_vector_alloc<int *> RAP_ext_col_indices_ptrs = RAP_ext_col_indices_ptrs_h;\n    device_vector_alloc<Value_type *> RAP_ext_values_ptrs = RAP_ext_values_ptrs_h;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = RAP_ext_row_ids[i].size();\n\n        if (size != 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //write the position in RAP_ext_row_ids\n            csr_multiply_detail::flag_halo_rows <<< num_blocks, 128>>>(\n                RAP_ext_row_ids[i].raw(),\n                size,\n                flagArray[i].raw(),\n                i,\n                RAP.manager->global_id());\n                cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n    csr_multiply_detail::compute_values_RAP_ext_kernel< Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n        RAP_size,\n        RAP_int.row_offsets.raw(),\n        RAP_int.col_indices.raw(),\n        RAP_int.values.raw(),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_row_offsets_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_col_indices_ptrs[0]),\n        amgx::thrust::raw_pointer_cast(&RAP_ext_values_ptrs[0]),\n        RAP.row_offsets.raw(),\n        RAP.col_indices.raw(),\n        RAP.values.raw(),\n        amgx::thrust::raw_pointer_cast(&flagArray_ptrs[0]),\n        this->m_gmem_size,\n        this->m_keys,\n        this->m_vals,\n        this->m_work_queue,\n        num_neighbors,\n        status );\n    cudaCheckError();\n    //CUDA_SAFE_CALL( cudaGetLastError() );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class CSR_Multiply_Detail<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/csr_multiply_detail.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <amgx_lapack.h>\n#include <algorithm>\n\n#ifdef AMGX_USE_MAGMA\n#define ADD_ 1\n#define HAVE_CUBLAS 1\n#include <magma.h>\n#endif\n\n#include <amgx_cublas.h>\n\nnamespace amgx\n{\n\n#define lapackCheckError(status)                                \\\n    {                                                           \\\n        if (status < 0)                                         \\\n        {                                                       \\\n            std::stringstream ss;                               \\\n            ss << \"Lapack error: argument number \"              \\\n               << -status << \" had an illegal value.\";          \\\n            FatalError(ss.str(), AMGX_ERR_INTERNAL);           \\\n        }                                                       \\\n        else if (status > 0)                                    \\\n            FatalError(\"Lapack error: internal error.\",         \\\n                       AMGX_ERR_INTERNAL);                     \\\n    }                                                           \\\n\n#define magmaCheckError(status)                                 \\\n    {                                                           \\\n        if (status < 0)                                         \\\n        {                                                       \\\n            std::stringstream ss;                               \\\n            ss << \"Magma error: argument number \"               \\\n               << -status << \" had an illegal value.\";          \\\n            FatalError(ss.str(), AMGX_ERR_INTERNAL);           \\\n        }                                                       \\\n        else if (status > 0)                                    \\\n            FatalError(\"Magma error: internal error.\",          \\\n                       AMGX_ERR_INTERNAL);                     \\\n    }                                                           \\\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::check_lapack_enabled()\n{\n#ifndef AMGX_USE_LAPACK\n    FatalError(\"Error: LAPACK not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::check_magma_enabled()\n{\n#ifndef AMGX_USE_MAGMA\n    FatalError(\"Error: MAGMA not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::check_magma_enabled()\n{\n#ifndef AMGX_USE_MAGMA\n    FatalError(\"Error: MAGMA not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::not_implemented()\n{\n    FatalError(\"Error: LAPACK operation not implemented on host.\", AMGX_ERR_CONFIGURATION);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::not_implemented()\n{\n    FatalError(\"Error: LAPACK operation not implemented on device.\", AMGX_ERR_CONFIGURATION);\n}\n\nnamespace\n{\n\n#ifdef AMGX_USE_LAPACK\nstruct _fcomplex { float re, im; };\ntypedef struct _fcomplex fcomplex;\nstruct _dcomplex { double re, im; };\ntypedef struct _dcomplex dcomplex;\n\nextern \"C\"\nint dgeev_(char *jobvl, char *jobvr, int *n, double *a,\n           int *lda, double *wr, double *wi, double *vl,\n           int *ldvl, double *vr, int *ldvr, double *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint sgeev_(char *jobvl, char *jobvr, int *n, float *a,\n           int *lda, float *wr, float *wi, float *vl,\n           int *ldvl, float *vr, int *ldvr, float *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint cgeev_(char *jobvl, char *jobvr, int *n, fcomplex *a,\n           int *lda, fcomplex *wr, fcomplex *wi, fcomplex *vl,\n           int *ldvl, fcomplex *vr, int *ldvr, fcomplex *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint zgeev_(char *jobvl, char *jobvr, int *n, dcomplex *a,\n           int *lda, dcomplex *wr, dcomplex *wi, dcomplex *vl,\n           int *ldvl, dcomplex *vr, int *ldvr, dcomplex *work,\n           int *lwork, int *info);\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, double *a,\n                         int *lda, double *wr, double *wi, double *vl,\n                         int *ldvl, double *vr, int *ldvr, double *work,\n                         int *lwork, int *info)\n{\n    return dgeev_(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, float *a,\n                         int *lda, float *wr, float *wi, float *vl,\n                         int *ldvl, float *vr, int *ldvr, float *work,\n                         int *lwork, int *info)\n{\n    return sgeev_(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, cuComplex *a,\n                         int *lda, cuComplex *wr, cuComplex *wi, cuComplex *vl,\n                         int *ldvl, cuComplex *vr, int *ldvr, cuComplex *work,\n                         int *lwork, int *info)\n{\n    return cgeev_(jobvl, jobvr, n, \n                  reinterpret_cast<fcomplex *>(a),\n                  lda, \n                  reinterpret_cast<fcomplex *>(wr), \n                  reinterpret_cast<fcomplex *>(wi), \n                  reinterpret_cast<fcomplex *>(vl),\n                  ldvl, \n                  reinterpret_cast<fcomplex *>(vr), \n                  ldvr, \n                  reinterpret_cast<fcomplex *>(work), \n                  lwork,\n                  info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, cuDoubleComplex *a,\n                         int *lda, cuDoubleComplex *wr, cuDoubleComplex *wi, cuDoubleComplex *vl,\n                         int *ldvl, cuDoubleComplex *vr, int *ldvr, cuDoubleComplex *work,\n                         int *lwork, int *info)\n{\n    return zgeev_(jobvl, jobvr, n, \n                  reinterpret_cast<dcomplex *>(a),\n                  lda, \n                  reinterpret_cast<dcomplex *>(wr), \n                  reinterpret_cast<dcomplex *>(wi), \n                  reinterpret_cast<dcomplex *>(vl),\n                  ldvl, \n                  reinterpret_cast<dcomplex *>(vr), \n                  ldvr, \n                  reinterpret_cast<dcomplex *>(work), \n                  lwork,\n                  info);\n}\n\ntemplate <typename T>\nvoid lapack_geev(T *A, T *eigenvalues, int dim, int lda)\n{\n    char job = 'N';\n    T *WI = new T[dim];\n    int ldv = 1;\n    T *vl = 0;\n    int work_size = 6 * dim;\n    T *work = new T[work_size];\n    int info;\n    lapack_geev_dispatch(&job, &job, &dim, A, &lda, eigenvalues, WI, vl, &ldv,\n                         vl, &ldv, work, &work_size, &info);\n    lapackCheckError(info);\n    delete [] WI;\n    delete [] work;\n}\n\ntemplate <typename T>\nvoid lapack_geev(T *A, T *eigenvalues, T *eigenvectors, int dim, int lda, int ldvr)\n{\n    char jobvl = 'N';\n    char jobvr = 'V';\n    T *WI = new T[dim * dim];\n    int work_size = 6 * dim;\n    T *vl = 0;\n    int ldvl = 1;\n    T *work = new T[work_size];\n    int info;\n    lapack_geev_dispatch(&jobvl, &jobvr, &dim, A, &lda, eigenvalues, WI, vl, &ldvl,\n                         eigenvectors, &ldvr, work, &work_size, &info);\n    lapackCheckError(info);\n    delete [] WI;\n    delete [] work;\n}\n\n#endif\n\n} // end anonymous namespace\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A, Vector<TConfig> &eigenvalues)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    // It is possible the matrix has an extra row (e.g. Arnoldi).\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n    value_type *A_ptr = const_cast<value_type *>(A.raw());\n#ifdef AMGX_USE_LAPACK\n    lapack_geev(A_ptr, eigenvalues.raw(), dim, lda);\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A,\n        Vector<TConfig> &eigenvalues,\n        Vector<TConfig> &eigenvector)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    // It is possible the matrix has an extra row (e.g. Arnoldi).\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n    value_type *A_ptr = const_cast<value_type *>(A.raw());\n#ifdef AMGX_USE_LAPACK\n    lapack_geev(A_ptr, eigenvalues.raw(), eigenvector.raw(), dim, lda, eigenvector.get_lda());\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A, Vector<TConfig> &eigenvalues)\n{\n    not_implemented();\n}\n\nnamespace\n{\n#ifdef AMGX_USE_LAPACK\nextern \"C\"\nint dtrtri_(char *uplo, char *diag, int *n, double *\n            a, int *lda, int *info);\n\nextern \"C\"\nint strtri_(char *uplo, char *diag, int *n, float *\n            a, int *lda, int *info);\n\nextern \"C\"\nint ctrtri_(char *uplo, char *diag, int *n, fcomplex *\n            a, int *lda, int *info);\n\nextern \"C\"\nint ztrtri_(char *uplo, char *diag, int *n, dcomplex *\n            a, int *lda, int *info);\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, float *a,\n                          int *lda, int *info)\n{\n    return strtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, double *a,\n                          int *lda, int *info)\n{\n    return dtrtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, fcomplex *a,\n                          int *lda, int *info)\n{\n    return ctrtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, dcomplex *a,\n                          int *lda, int *info)\n{\n    return ztrtri_(uplo, diag, n, a, lda, info);\n}\n\ntemplate <typename T>\nvoid lapack_trtri(T *A, int dim, int lda)\n{\n    char uplo = 'U';\n    char diag = 'N';\n    int info;\n    lapack_trtri_dispatch(&uplo, &diag, &dim, A, &lda, &info);\n    lapackCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::trtri(Vector<TConfig> &A)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_LAPACK\n    lapack_trtri(A.raw(), dim, lda);\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, float *a,\n                         int lda, int *info)\n{\n    return magma_strtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, double *a,\n                         int lda, int *info)\n{\n    return magma_dtrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, cuComplex *a,\n                         int lda, int *info)\n{\n    return magma_ctrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, cuDoubleComplex *a,\n                         int lda, int *info)\n{\n    return magma_ztrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\ntemplate <typename T>\nvoid magma_trtri(T *A, int dim, int lda)\n{\n    magma_uplo_t uplo = MagmaUpper;\n    magma_diag_t diag = MagmaNonUnit;\n    int info;\n    magma_trtri_dispatch(uplo, diag, dim, A, lda, &info);\n    magmaCheckError(info);\n}\n\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::trtri(Vector<TConfig> &A)\n{\n    check_magma_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_trtri(A.raw(), dim, lda);;\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_LAPACK\nextern \"C\"\nint dsygv_(int *itype, char *jobz, char *uplo, int *n,\n           double *a, int *lda, double *b, int *ldb,\n           double *w, double *work, int *lwork, int *info);\n\nextern \"C\"\nint ssygv_(int *itype, char *jobz, char *uplo, int *n,\n           float *a, int *lda, float *b, int *ldb,\n           float *w, float *work, int *lwork, int *info);\n\nextern \"C\"\nint chegv_(int *itype, char *jobz, char *uplo, int *n,\n           fcomplex *a, int *lda, fcomplex *b, int *ldb,\n           fcomplex *w, fcomplex *work, int *lwork, int *info);\n\nextern \"C\"\nint zhegv_(int *itype, char *jobz, char *uplo, int *n,\n           dcomplex *a, int *lda, dcomplex *b, int *ldb,\n           dcomplex *w, dcomplex *work, int *lwork, int *info);\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         double *a, int *lda, double *b, int *ldb,\n                         double *w, double *work, int *lwork, int *info)\n{\n    return dsygv_(itype, jobz, uplo, n, a, lda, b, ldb, w, work, lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         float *a, int *lda, float *b, int *ldb,\n                         float *w, float *work, int *lwork, int *info)\n{\n    return ssygv_(itype, jobz, uplo, n, a, lda, b, ldb, w, work, lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         cuComplex *a, int *lda, cuComplex *b, int *ldb,\n                         cuComplex *w, cuComplex *work, int *lwork, int *info)\n{\n    return chegv_(itype, jobz, uplo, n,\n                  reinterpret_cast<fcomplex *>(a), lda, reinterpret_cast<fcomplex *>(b), ldb,\n                  reinterpret_cast<fcomplex *>(w), reinterpret_cast<fcomplex *>(work), lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         cuComplex *a, int *lda, cuDoubleComplex *b, int *ldb,\n                         cuDoubleComplex *w, cuDoubleComplex *work, int *lwork, int *info)\n{\n    return zhegv_(itype, jobz, uplo, n,\n                  reinterpret_cast<dcomplex *>(a), lda, reinterpret_cast<dcomplex *>(b), ldb,\n                  reinterpret_cast<dcomplex *>(w), reinterpret_cast<dcomplex *>(work), lwork, info);\n}\n\ntemplate <typename T>\nvoid lapack_sygv(T *gramA, T *gramB, T *eigenvector, int dim, int lda, T *work)\n{\n    int itype = 1;\n    char jobz = 'V';\n    char uplo = 'U';\n    int ldb = lda;\n    int lwork = 1024;\n    int info = 0;\n    lapack_sygv_dispatch(&itype, &jobz, &uplo, &dim, gramA, &lda, gramB, &ldb, eigenvector, work, &lwork, &info);\n    lapackCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::sygv(Vector<TConfig> &A, Vector<TConfig> &B,\n        Vector<TConfig> &eigenvalues, Vector<TConfig> &work)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_LAPACK\n    lapack_sygv(A.raw(), B.raw(), eigenvalues.raw(), dim, lda, work.raw());\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nvoid magma_trsm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         float alpha, float const *dA, magma_int_t lda,\n                         float *dB, magma_int_t ldb)\n{\n    return magma_strsm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trsm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         double alpha, double const *dA, magma_int_t lda,\n                         double *dB, magma_int_t ldb)\n{\n    return magma_dtrsm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trmm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         float alpha, float const *dA, magma_int_t lda,\n                         float *dB, magma_int_t ldb)\n{\n    return magma_strmm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trmm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         double alpha, double const *dA, magma_int_t lda,\n                         double *dB, magma_int_t ldb)\n{\n    return magma_dtrmm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nint magma_potrf_gpu_dispatch(magma_uplo_t uplo, int n, float *A, int lda, int *info)\n{\n    return magma_spotrf_gpu(uplo, n, A, lda, info);\n}\n\nint magma_potrf_gpu_dispatch(magma_uplo_t uplo, int n, double *A, int lda, int *info)\n{\n    return magma_dpotrf_gpu(uplo, n, A, lda, info);\n}\n\nint magma_sygst_gpu_dispatch(int itype, magma_uplo_t uplo, magma_int_t n, float *da,\n                             int ldda, float *B, int lddb, int *info)\n{\n    return magma_ssygst_gpu(itype, uplo, n, da, ldda, B, lddb, info);\n}\n\nint magma_sygst_gpu_dispatch(int itype, magma_uplo_t uplo, magma_int_t n, double *da,\n                             int ldda, double *B, int lddb, int *info)\n{\n    return magma_dsygst_gpu(itype, uplo, n, da, ldda, B, lddb, info);\n}\n\nint magma_syevd_gpu_dispatch(magma_vec_t jobz, magma_uplo_t uplo, int n, double *da, int ldda,\n                             double *w, double *wa, int ldwa, double *work,\n                             int lwork, int *iwork, int liwork, int *info)\n{\n    return magma_dsyevd_gpu(jobz, uplo, n, da, ldda, w, wa, ldwa, work, lwork, iwork, liwork, info);\n}\n\nint magma_syevd_gpu_dispatch(magma_vec_t jobz, magma_uplo_t uplo, int n, float *da, int ldda,\n                             float *w, float *wa, int ldwa, float *work,\n                             int lwork, int *iwork, int liwork, int *info)\n{\n    return magma_ssyevd_gpu(jobz, uplo, n, da, ldda, w, wa, ldwa, work, lwork, iwork, liwork, info);\n}\n\n// This is a simple modification of the magma_?sygvd() source code\n// from magma where the matrices are already on the device.\ntemplate <typename T>\nmagma_int_t magma_sygvd_gpu_impl(magma_int_t itype, magma_vec_t jobz, magma_uplo_t uplo, magma_int_t n,\n                                 T *da, magma_int_t lda, T *db, magma_int_t ldb, T *w,\n                                 T *work, magma_int_t lwork, magma_int_t *iwork, magma_int_t liwork,\n                                 T *wa, magma_int_t *info)\n{\n    magma_uplo_t uplo_[2] = {uplo, MagmaLower}; // {uplo, 0}\n    magma_vec_t jobz_[2] = {jobz, MagmaVec};//{jobz, 0};\n    T d_one = MAGMA_D_ONE;\n    magma_int_t ldda = n;\n    magma_int_t lddb = n;\n    static magma_int_t lower;\n    static char trans[1];\n    static magma_int_t wantz, lquery;\n    static magma_int_t lopt, lwmin, liopt, liwmin;\n    static cudaStream_t stream;\n    magma_queue_create( &stream );\n    wantz = jobz_[0] == MagmaVec;\n    lower = uplo_[0] == MagmaLower;\n    lquery = lwork == -1 || liwork == -1;\n    *info = 0;\n\n    if (itype < 1 || itype > 3)\n    {\n        *info = -1;\n    }\n    else if (! (wantz || jobz_[0] == MagmaNoVec))\n    {\n        *info = -2;\n    }\n    else if (! (lower || uplo_[0] == MagmaUpper))\n    {\n        *info = -3;\n    }\n    else if (n < 0)\n    {\n        *info = -4;\n    }\n    else if (lda < max(1, n))\n    {\n        *info = -6;\n    }\n    else if (ldb < max(1, n))\n    {\n        *info = -8;\n    }\n\n    magma_int_t nb = magma_get_dsytrd_nb(n);\n\n    if (n < 1)\n    {\n        liwmin = 1;\n        lwmin = 1;\n    }\n    else if (wantz)\n    {\n        lwmin = 1 + 6 * n * nb + 2 * n * n;\n        liwmin = 5 * n + 3;\n    }\n    else\n    {\n        lwmin = 2 * n * nb + 1;\n        liwmin = 1;\n    }\n\n    lopt = lwmin;\n    liopt = liwmin;\n    work[ 0] = lopt;\n    iwork[0] = liopt;\n\n    if (lwork < lwmin && ! lquery)\n    {\n        *info = -11;\n    }\n    else if (liwork < liwmin && ! lquery)\n    {\n        *info = -13;\n    }\n\n    if (*info != 0)\n    {\n        magma_xerbla( __func__, -(*info) );\n        return MAGMA_ERR_ILLEGAL_VALUE;\n    }\n    else if (lquery)\n    {\n        return MAGMA_SUCCESS;\n    }\n\n    /* Quick return if possible */\n    if (n == 0)\n    {\n        return 0;\n    }\n\n    magma_potrf_gpu_dispatch(uplo_[0], n, db, lddb, info);\n\n    if (*info != 0)\n    {\n        *info = n + *info;\n        return 0;\n    }\n\n    /* Transform problem to standard eigenvalue problem and solve. */\n    magma_sygst_gpu_dispatch(itype, uplo_[0], n, da, ldda, db, lddb, info);\n    magma_syevd_gpu_dispatch(jobz_[0], uplo_[0], n, da, ldda, w, wa, lda,\n                             work, lwork, iwork, liwork, info);\n    lopt = max( lopt, (magma_int_t) work[0]);\n    liopt = max(liopt, iwork[0]);\n\n    if (wantz && *info == 0)\n    {\n        /* Backtransform eigenvectors to the original problem. */\n        if (itype == 1 || itype == 2)\n        {\n            /* For A*x=(lambda)*B*x and A*B*x=(lambda)*x;\n               backtransform eigenvectors: x = inv(L)'*y or inv(U)*y */\n            if (lower)\n            {\n                *(unsigned char *)trans = MagmaTrans;\n            }\n            else\n            {\n                *(unsigned char *)trans = MagmaNoTrans;\n            }\n\n            magma_trsm_dispatch(MagmaLeft, uplo_[0], *trans, MagmaNonUnit,\n                                n, n, d_one, db, lddb, da, ldda);\n        }\n        else if (itype == 3)\n        {\n            /* For B*A*x=(lambda)*x;\n               backtransform eigenvectors: x = L*y or U'*y */\n            if (lower)\n            {\n                *(unsigned char *)trans = MagmaNoTrans;\n            }\n            else\n            {\n                *(unsigned char *)trans = MagmaTrans;\n            }\n\n            magma_trmm_dispatch(MagmaLeft, uplo_[0], *trans, MagmaNonUnit,\n                                n, n, d_one, db, lddb, da, ldda);\n        }\n    }\n\n    magma_queue_sync( stream );\n    magma_queue_destroy( stream );\n    work[0] = (T) lopt;\n    iwork[0] = liopt;\n    return MAGMA_SUCCESS;\n}\n\ncublasStatus_t cublas_trsm(cublasHandle_t handle,\n                           cublasSideMode_t side, cublasFillMode_t uplo,\n                           cublasOperation_t trans, cublasDiagType_t diag,\n                           int m, int n,\n                           const float *alpha,\n                           const float *A, int lda,\n                           float *B, int ldb)\n{\n    return cublasStrsm(handle, side, uplo, trans, diag, m, n, alpha, A, lda, B, ldb);\n}\n\ncublasStatus_t cublas_trsm(cublasHandle_t handle,\n                           cublasSideMode_t side, cublasFillMode_t uplo,\n                           cublasOperation_t trans, cublasDiagType_t diag,\n                           int m, int n,\n                           const double *alpha,\n                           const double *A, int lda,\n                           double *B, int ldb)\n{\n    return cublasDtrsm(handle, side, uplo, trans, diag, m, n, alpha, A, lda, B, ldb);\n}\n\n\ntemplate <typename T>\nvoid magma_sygvd_gpu(T *A, T *B, T *eigenvalues, int dim, int lda)\n{\n    int itype = 1;\n    magma_vec_t jobz = MagmaVec;\n    magma_uplo_t uplo = MagmaUpper;\n    int N = dim;\n    int ldb = lda;\n    int nb = 32;\n    int lwork = 1 + 6 * N * nb + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    T *work = &s_work[0];\n    int liwork = 3 + 5 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int *iwork = &s_iwork[0];\n    static std::vector<T> s_wa;\n    s_wa.resize(lda * N);\n    T *wa = &s_wa[0];\n    int ldwa = N;\n    int info;\n    /*\n            magma_sygvd_gpu_impl(itype, jobz, uplo, N, A, lda, B, ldb, eigenvalues, work, lwork, iwork, liwork, wa, &info);\n    */\n    magma_potrf_gpu_dispatch(uplo, N, B, lda, &info);\n    magmaCheckError(info);\n    magma_sygst_gpu_dispatch(itype, uplo, N, A, lda, B, ldb, &info);\n    magmaCheckError(info);\n    magma_syevd_gpu_dispatch(jobz, uplo, N, A, lda, eigenvalues, wa, ldwa, work, lwork, iwork, liwork, &info);\n    magmaCheckError(info);\n    T one = 1;\n    cublasHandle_t handle = Cublas::get_handle();\n    cublas_trsm(handle, CUBLAS_SIDE_LEFT, CUBLAS_FILL_MODE_UPPER, CUBLAS_OP_N, CUBLAS_DIAG_NON_UNIT, N, N, &one, B, ldb, A, lda);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::sygv(Vector<TConfig> &A, Vector<TConfig> &B,\n        Vector<TConfig_h> &eigenvalues, Vector<TConfig> &work)\n{\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_sygvd_gpu(A.raw(), B.raw(), eigenvalues.raw(), dim, lda);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\ntemplate <typename T>\nvoid magma_syevd_gpu(T *A, T *eigenvalues, int dim, int lda)\n{\n    magma_vec_t jobz = MagmaVec;\n    magma_uplo_t uplo = MagmaUpper;\n    int N = dim;\n    int nb = 32;\n    int lwork = 1 + 6 * N * nb + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    T *work = &s_work[0];\n    int liwork = 3 + 5 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int *iwork = &s_iwork[0];\n    static std::vector<T> s_wa;\n    s_wa.resize(lda * N);\n    T *wa = &s_wa[0];\n    int ldwa = N;\n    int info;\n    magma_syevd_gpu_dispatch(jobz, uplo, N, A, lda, eigenvalues, wa, ldwa, work, lwork, iwork, liwork, &info);\n    magmaCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::syevd(Vector<TConfig> &A,\n        Vector<TConfig_h> &eigenvalues)\n{\n    check_magma_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_syevd_gpu(A.raw(), eigenvalues.raw(), dim, lda);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::syevd(Vector<TConfig> &A,\n        Vector<TConfig> &eigenvalues)\n{\n    not_implemented();\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_stedx_dispatch(magma_range_t range, int n,\n                         double vl, double vu,\n                         int il, int iu,\n                         double *d, double *e, double *z, int ldz,\n                         double *work, int lwork, int *iwork, int liwork,\n                         double *dwork, int *info)\n{\n    return magma_dstedx(range, n, vl, vu, il, iu, d, e, z, ldz, work, lwork, iwork, liwork, dwork, info);\n}\n\nint magma_stedx_dispatch(magma_range_t range, int n,\n                         float vl, float vu,\n                         int il, int iu,\n                         float *d, float *e, float *z, int ldz,\n                         float *work, int lwork, int *iwork, int liwork,\n                         float *dwork, int *info)\n{\n    return magma_sstedx(range, n, vl, vu, il, iu, d, e, z, ldz, work, lwork, iwork, liwork, dwork, info);\n}\n\ntemplate <typename T>\nvoid magma_stedx(T *diagonal, T *subdiagonal, T *eigenvectors,\n                 int lower, int upper, int dim, int ldz, T *dwork, int dwork_size)\n{\n    magma_range_t range = MagmaRangeI;\n    int N = dim;\n    T vl = 0;\n    T vu = 0;\n    int il = lower;\n    int iu = upper;\n    int lwork = 1 + 4 * N + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    int liwork = 3 + 6 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int info;\n    magma_stedx_dispatch(range, N, vl, vu, il, iu, diagonal, subdiagonal, eigenvectors, ldz,\n                         &s_work[0], lwork, &s_iwork[0], liwork, dwork, &info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::stedx(Vector<TConfig> &diagonal,\n        Vector<TConfig> &subdiagonal,\n        Vector<TConfig> &eigenvectors,\n        int dim,\n        Vector<TConfig_d> &dwork)\n{\n    check_magma_enabled();\n#ifdef AMGX_USE_MAGMA\n    magma_stedx(diagonal.raw(), subdiagonal.raw(), eigenvectors.raw(),\n                dim, dim, dim, eigenvectors.get_lda(),\n                dwork.raw(), dwork.size());\n#endif\n}\n\n\nnamespace\n{\n\ntemplate <typename T>\nvoid larf(int m, int n, T *v,\n          int incv, T *tau, T *c, int ldc,\n          T *work)\n{\n    /* Table of constant values */\n    static T c_b4 = 1.;\n    static T c_b5 = 0.;\n    static int c1 = 1;\n    /*        Form  H * C */\n    /*        w := C' * v */\n    Cublas::gemv(true, m, n, &c_b4, c, ldc,\n                 v, incv, &c_b5, work, c1);\n    /*        C := C - v * w' */\n    Cublas::ger(m, n, tau, v, incv, work, c1, c, ldc);\n}\n\ntemplate <typename T>\n__global__\nvoid set1(T *a)\n{\n    *a = 1.;\n}\n\ntemplate <typename T>\n__global__\nvoid add_tau(T *a, T tau)\n{\n    *a = 1 + tau;\n}\n\ntemplate <typename T>\nvoid gpu_orgqr(int m, int n, int k,\n               T *a, int lda, T *tau, T *work, int lwork)\n{\n    int i1, i2;\n\n    for (int i = k - 1; i >= 0; --i)\n    {\n        /*        Apply H(i) to A(i:m,i:n) from the left */\n        if (i < n - 1)\n        {\n            set1 <<< 1, 1>>>(&a[i + i * lda]);\n            cudaCheckError();\n            i1 = m - i;\n            i2 = n - i - 1;\n            larf(i1, i2, &a[i + i * lda], 1, &tau[i],\n                 &a[i + (i + 1) * lda], lda, work);\n        }\n\n        if (i < m - 1)\n        {\n            i1 = m - i - 1;\n            Cublas::scal(i1, &tau[i], &a[i + 1 + i * lda], 1);\n        }\n\n        add_tau <<< 1, 1>>>(&a[i + i * lda], tau[i]);\n        cudaCheckError();\n        /*        Set A(1:i-1,i) to zero */\n        cudaError_t cuda_rc = cudaMemset(&a[i * lda], 0, sizeof(T) * i);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaMemset failed in geqr2\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <typename T>\n__device__ __host__\nT lapy2_(T *a, T *b)\n{\n    T va = *a;\n    T vb = *b;\n    return sqrt(va * va + vb * vb);\n}\n\ntemplate <typename T>\n__device__ __host__\nT d_sign(T a, T b)\n{\n    T x;\n    x = (a >= 0 ? a : - a);\n    return (b >= 0 ? x : -x);\n}\n\ntemplate <typename T>\nvoid compute_tau_host(T *alpha, T *norm,\n                      T *tau, T *d1)\n{\n    *d1 = lapy2_(alpha, norm);\n    T beta = -d_sign(*d1, *alpha);\n    // LAPACK: skipped part about scaling.\n    // Negated compared to LAPACK code, avoid negating value on device later.\n    *tau = -(beta - *alpha) / beta;\n    *d1 = 1. / (*alpha - beta);\n    *alpha = beta;\n}\n\ntemplate <typename T>\nvoid larfg(int n, T *alpha, T *x,\n           int incx, T *tau)\n{\n    if (n <= 1)\n    {\n        *tau = 0.;\n        return;\n    }\n\n    int i1 = n - 1;\n    T xnorm;\n    Cublas::nrm2(i1, x, incx, &xnorm);\n    T h_alpha;\n    cudaError_t cuda_rc = cudaMemcpy(&h_alpha, alpha, sizeof(T), cudaMemcpyDeviceToHost);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy D2H failed in larfg\", AMGX_ERR_CUDA_FAILURE);\n    }\n    T d1;\n    compute_tau_host(&h_alpha, &xnorm, tau, &d1);\n    Cublas::scal(i1, d1, x, incx);\n    // Update the diagonal value on the device.\n    cuda_rc = cudaMemcpy(alpha, &h_alpha, sizeof(T), cudaMemcpyHostToDevice);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy H2D failed in larfg\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n\ntemplate <typename T>\nvoid gpu_geqrf(int m, int n, T *a, int lda,\n               T *tau, T *work)\n{\n    int k = std::min(m, n);\n    T *aii;\n    cudaError_t cuda_rc = amgx::memory::cudaMallocAsync((void**)&aii, sizeof(T));\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMallocAsync failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for (int i = 0; i < k; ++i)\n    {\n        /*        Generate elementary reflector H(i) to annihilate A(i+1:m,i) */\n        int i2 = m - i;\n        /* Computing MIN */\n        int i3 = i + 1;\n        larfg(i2, &a[i + i * lda],\n              &a[std::min(i3, m - 1) + i * lda],\n              1, &tau[i]);\n\n        if (i < n - 1)\n        {\n            /*           Apply H(i) to A(i:m,i+1:n) from the left */\n            cuda_rc = cudaMemcpy(aii, &a[i + i * lda], sizeof(T), cudaMemcpyDeviceToDevice);\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaMemcpy D2D failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n            }\n            set1 <<< 1, 1>>>(&a[i + i * lda]);\n            cudaCheckError();\n            i2 = m - i;\n            i3 = n - i - 1;\n            larf(i2, i3, &a[i + i * lda], 1,\n                 &tau[i], &a[i + (i + 1) * lda], lda, work);\n            cuda_rc = cudaMemcpy(&a[i + i * lda], aii, sizeof(T), cudaMemcpyDeviceToDevice);\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaMemcpy D2D failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n\n    cuda_rc = amgx::memory::cudaFreeAsync(aii);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaFreeAsync failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n} // end anonymous namespace\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_geqrf_dispatch(int m, int n, float *A, int lda,\n                         float *tau, float *work, int *info)\n{\n    return magma_sgeqrf_gpu(m, n, A, lda, tau, work, info);\n}\n\nint magma_geqrf_dispatch(int m, int n, double *A, int lda,\n                         double *tau, double *work, int *info)\n{\n    return magma_dgeqrf_gpu(m, n, A, lda, tau, work, info);\n}\n\ntemplate <typename T>\nvoid magma_geqrf(int m, int n, T *A, int lda,\n                 T *tau, T *work)\n{\n    int info;\n    magma_geqrf_dispatch(m, n, A, lda, tau, work, &info);\n    magmaCheckError(info);\n}\n\nint magma_orgqr_dispatch(int m, int n, int k, float *A, int lda,\n                         float *tau, float *work, int lwork, int *info)\n{\n    return magma_sorgqr_gpu(m, n, k, A, lda, tau, work, lwork, info);\n}\n\nint magma_orgqr_dispatch(int m, int n, int k, double *A, int lda,\n                         double *tau, double *work, int lwork, int *info)\n{\n    return magma_dorgqr_gpu(m, n, k, A, lda, tau, work, lwork, info);\n}\n\ntemplate <typename T>\nvoid magma_orgqr(int m, int n, int k, T *A, int lda,\n                 T *tau, T *work, int lwork)\n{\n    int info;\n    magma_orgqr_dispatch(m, n, k, A, lda, tau, work, lwork, &info);\n    magmaCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geqrf(Vector<TConfig> &A,\n        Vector<TConfig> &tau,\n        Vector<TConfig> &work)\n{\n    not_implemented();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::geqrf(Vector<TConfig> &A,\n        Vector<TConfig_h> &tau,\n        Vector<TConfig> &work)\n{\n    int rows = A.get_num_rows();\n    int cols = A.get_num_cols();\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_geqrf(rows, cols, A.raw(), lda, tau.raw(), work.raw());\n#else\n    gpu_geqrf(rows, cols, A.raw(), lda, tau.raw(), work.raw());\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::orgqr(Vector<TConfig> &A,\n        Vector<TConfig> &tau,\n        Vector<TConfig> &work)\n{\n    not_implemented();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::orgqr(Vector<TConfig> &A,\n        Vector<TConfig_h> &tau,\n        Vector<TConfig> &work)\n{\n    int rows = A.get_num_rows();\n    int cols = A.get_num_cols();\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_orgqr(rows, cols, cols, A.raw(), lda, tau.raw(), work.raw(), 1);\n#else\n    gpu_orgqr(rows, cols, cols, A.raw(), lda, tau.raw(), work.raw(), 1);\n#endif\n}\n\n\n#define AMGX_CASE_LINE(CASE) \\\n    template class Lapack<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <amgx_lapack.h>\n#include <algorithm>\n\n#ifdef AMGX_USE_MAGMA\n#define ADD_ 1\n#define HAVE_CUBLAS 1\n#include <magma.h>\n#endif\n\n#include <amgx_cublas.h>\n\nnamespace amgx\n{\n\n#define lapackCheckError(status)                                \\\n    {                                                           \\\n        if (status < 0)                                         \\\n        {                                                       \\\n            std::stringstream ss;                               \\\n            ss << \"Lapack error: argument number \"              \\\n               << -status << \" had an illegal value.\";          \\\n            FatalError(ss.str(), AMGX_ERR_INTERNAL);           \\\n        }                                                       \\\n        else if (status > 0)                                    \\\n            FatalError(\"Lapack error: internal error.\",         \\\n                       AMGX_ERR_INTERNAL);                     \\\n    }                                                           \\\n\n#define magmaCheckError(status)                                 \\\n    {                                                           \\\n        if (status < 0)                                         \\\n        {                                                       \\\n            std::stringstream ss;                               \\\n            ss << \"Magma error: argument number \"               \\\n               << -status << \" had an illegal value.\";          \\\n            FatalError(ss.str(), AMGX_ERR_INTERNAL);           \\\n        }                                                       \\\n        else if (status > 0)                                    \\\n            FatalError(\"Magma error: internal error.\",          \\\n                       AMGX_ERR_INTERNAL);                     \\\n    }                                                           \\\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::check_lapack_enabled()\n{\n#ifndef AMGX_USE_LAPACK\n    FatalError(\"Error: LAPACK not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::check_magma_enabled()\n{\n#ifndef AMGX_USE_MAGMA\n    FatalError(\"Error: MAGMA not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::check_magma_enabled()\n{\n#ifndef AMGX_USE_MAGMA\n    FatalError(\"Error: MAGMA not enabled.\", AMGX_ERR_CONFIGURATION);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::not_implemented()\n{\n    FatalError(\"Error: LAPACK operation not implemented on host.\", AMGX_ERR_CONFIGURATION);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::not_implemented()\n{\n    FatalError(\"Error: LAPACK operation not implemented on device.\", AMGX_ERR_CONFIGURATION);\n}\n\nnamespace\n{\n\n#ifdef AMGX_USE_LAPACK\nstruct _fcomplex { float re, im; };\ntypedef struct _fcomplex fcomplex;\nstruct _dcomplex { double re, im; };\ntypedef struct _dcomplex dcomplex;\n\nextern \"C\"\nint dgeev_(char *jobvl, char *jobvr, int *n, double *a,\n           int *lda, double *wr, double *wi, double *vl,\n           int *ldvl, double *vr, int *ldvr, double *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint sgeev_(char *jobvl, char *jobvr, int *n, float *a,\n           int *lda, float *wr, float *wi, float *vl,\n           int *ldvl, float *vr, int *ldvr, float *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint cgeev_(char *jobvl, char *jobvr, int *n, fcomplex *a,\n           int *lda, fcomplex *wr, fcomplex *wi, fcomplex *vl,\n           int *ldvl, fcomplex *vr, int *ldvr, fcomplex *work,\n           int *lwork, int *info);\n\nextern \"C\"\nint zgeev_(char *jobvl, char *jobvr, int *n, dcomplex *a,\n           int *lda, dcomplex *wr, dcomplex *wi, dcomplex *vl,\n           int *ldvl, dcomplex *vr, int *ldvr, dcomplex *work,\n           int *lwork, int *info);\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, double *a,\n                         int *lda, double *wr, double *wi, double *vl,\n                         int *ldvl, double *vr, int *ldvr, double *work,\n                         int *lwork, int *info)\n{\n    return dgeev_(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, float *a,\n                         int *lda, float *wr, float *wi, float *vl,\n                         int *ldvl, float *vr, int *ldvr, float *work,\n                         int *lwork, int *info)\n{\n    return sgeev_(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, hipComplex *a,\n                         int *lda, hipComplex *wr, hipComplex *wi, hipComplex *vl,\n                         int *ldvl, hipComplex *vr, int *ldvr, hipComplex *work,\n                         int *lwork, int *info)\n{\n    return cgeev_(jobvl, jobvr, n, \n                  reinterpret_cast<fcomplex *>(a),\n                  lda, \n                  reinterpret_cast<fcomplex *>(wr), \n                  reinterpret_cast<fcomplex *>(wi), \n                  reinterpret_cast<fcomplex *>(vl),\n                  ldvl, \n                  reinterpret_cast<fcomplex *>(vr), \n                  ldvr, \n                  reinterpret_cast<fcomplex *>(work), \n                  lwork,\n                  info);\n}\n\nint lapack_geev_dispatch(char *jobvl, char *jobvr, int *n, hipDoubleComplex *a,\n                         int *lda, hipDoubleComplex *wr, hipDoubleComplex *wi, hipDoubleComplex *vl,\n                         int *ldvl, hipDoubleComplex *vr, int *ldvr, hipDoubleComplex *work,\n                         int *lwork, int *info)\n{\n    return zgeev_(jobvl, jobvr, n, \n                  reinterpret_cast<dcomplex *>(a),\n                  lda, \n                  reinterpret_cast<dcomplex *>(wr), \n                  reinterpret_cast<dcomplex *>(wi), \n                  reinterpret_cast<dcomplex *>(vl),\n                  ldvl, \n                  reinterpret_cast<dcomplex *>(vr), \n                  ldvr, \n                  reinterpret_cast<dcomplex *>(work), \n                  lwork,\n                  info);\n}\n\ntemplate <typename T>\nvoid lapack_geev(T *A, T *eigenvalues, int dim, int lda)\n{\n    char job = 'N';\n    T *WI = new T[dim];\n    int ldv = 1;\n    T *vl = 0;\n    int work_size = 6 * dim;\n    T *work = new T[work_size];\n    int info;\n    lapack_geev_dispatch(&job, &job, &dim, A, &lda, eigenvalues, WI, vl, &ldv,\n                         vl, &ldv, work, &work_size, &info);\n    lapackCheckError(info);\n    delete [] WI;\n    delete [] work;\n}\n\ntemplate <typename T>\nvoid lapack_geev(T *A, T *eigenvalues, T *eigenvectors, int dim, int lda, int ldvr)\n{\n    char jobvl = 'N';\n    char jobvr = 'V';\n    T *WI = new T[dim * dim];\n    int work_size = 6 * dim;\n    T *vl = 0;\n    int ldvl = 1;\n    T *work = new T[work_size];\n    int info;\n    lapack_geev_dispatch(&jobvl, &jobvr, &dim, A, &lda, eigenvalues, WI, vl, &ldvl,\n                         eigenvectors, &ldvr, work, &work_size, &info);\n    lapackCheckError(info);\n    delete [] WI;\n    delete [] work;\n}\n\n#endif\n\n} // end anonymous namespace\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A, Vector<TConfig> &eigenvalues)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    // It is possible the matrix has an extra row (e.g. Arnoldi).\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n    value_type *A_ptr = const_cast<value_type *>(A.raw());\n#ifdef AMGX_USE_LAPACK\n    lapack_geev(A_ptr, eigenvalues.raw(), dim, lda);\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A,\n        Vector<TConfig> &eigenvalues,\n        Vector<TConfig> &eigenvector)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    // It is possible the matrix has an extra row (e.g. Arnoldi).\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n    value_type *A_ptr = const_cast<value_type *>(A.raw());\n#ifdef AMGX_USE_LAPACK\n    lapack_geev(A_ptr, eigenvalues.raw(), eigenvector.raw(), dim, lda, eigenvector.get_lda());\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::geev(const Vector<TConfig> &A, Vector<TConfig> &eigenvalues)\n{\n    not_implemented();\n}\n\nnamespace\n{\n#ifdef AMGX_USE_LAPACK\nextern \"C\"\nint dtrtri_(char *uplo, char *diag, int *n, double *\n            a, int *lda, int *info);\n\nextern \"C\"\nint strtri_(char *uplo, char *diag, int *n, float *\n            a, int *lda, int *info);\n\nextern \"C\"\nint ctrtri_(char *uplo, char *diag, int *n, fcomplex *\n            a, int *lda, int *info);\n\nextern \"C\"\nint ztrtri_(char *uplo, char *diag, int *n, dcomplex *\n            a, int *lda, int *info);\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, float *a,\n                          int *lda, int *info)\n{\n    return strtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, double *a,\n                          int *lda, int *info)\n{\n    return dtrtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, fcomplex *a,\n                          int *lda, int *info)\n{\n    return ctrtri_(uplo, diag, n, a, lda, info);\n}\n\nint lapack_trtri_dispatch(char *uplo, char *diag, int *n, dcomplex *a,\n                          int *lda, int *info)\n{\n    return ztrtri_(uplo, diag, n, a, lda, info);\n}\n\ntemplate <typename T>\nvoid lapack_trtri(T *A, int dim, int lda)\n{\n    char uplo = 'U';\n    char diag = 'N';\n    int info;\n    lapack_trtri_dispatch(&uplo, &diag, &dim, A, &lda, &info);\n    lapackCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::trtri(Vector<TConfig> &A)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_LAPACK\n    lapack_trtri(A.raw(), dim, lda);\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, float *a,\n                         int lda, int *info)\n{\n    return magma_strtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, double *a,\n                         int lda, int *info)\n{\n    return magma_dtrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, hipComplex *a,\n                         int lda, int *info)\n{\n    return magma_ctrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\nint magma_trtri_dispatch(magma_uplo_t uplo, magma_diag_t diag, int n, hipDoubleComplex *a,\n                         int lda, int *info)\n{\n    return magma_ztrtri_gpu(uplo, diag, n, a, lda, info);\n}\n\ntemplate <typename T>\nvoid magma_trtri(T *A, int dim, int lda)\n{\n    magma_uplo_t uplo = MagmaUpper;\n    magma_diag_t diag = MagmaNonUnit;\n    int info;\n    magma_trtri_dispatch(uplo, diag, dim, A, lda, &info);\n    magmaCheckError(info);\n}\n\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::trtri(Vector<TConfig> &A)\n{\n    check_magma_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_trtri(A.raw(), dim, lda);;\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_LAPACK\nextern \"C\"\nint dsygv_(int *itype, char *jobz, char *uplo, int *n,\n           double *a, int *lda, double *b, int *ldb,\n           double *w, double *work, int *lwork, int *info);\n\nextern \"C\"\nint ssygv_(int *itype, char *jobz, char *uplo, int *n,\n           float *a, int *lda, float *b, int *ldb,\n           float *w, float *work, int *lwork, int *info);\n\nextern \"C\"\nint chegv_(int *itype, char *jobz, char *uplo, int *n,\n           fcomplex *a, int *lda, fcomplex *b, int *ldb,\n           fcomplex *w, fcomplex *work, int *lwork, int *info);\n\nextern \"C\"\nint zhegv_(int *itype, char *jobz, char *uplo, int *n,\n           dcomplex *a, int *lda, dcomplex *b, int *ldb,\n           dcomplex *w, dcomplex *work, int *lwork, int *info);\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         double *a, int *lda, double *b, int *ldb,\n                         double *w, double *work, int *lwork, int *info)\n{\n    return dsygv_(itype, jobz, uplo, n, a, lda, b, ldb, w, work, lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         float *a, int *lda, float *b, int *ldb,\n                         float *w, float *work, int *lwork, int *info)\n{\n    return ssygv_(itype, jobz, uplo, n, a, lda, b, ldb, w, work, lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         hipComplex *a, int *lda, hipComplex *b, int *ldb,\n                         hipComplex *w, hipComplex *work, int *lwork, int *info)\n{\n    return chegv_(itype, jobz, uplo, n,\n                  reinterpret_cast<fcomplex *>(a), lda, reinterpret_cast<fcomplex *>(b), ldb,\n                  reinterpret_cast<fcomplex *>(w), reinterpret_cast<fcomplex *>(work), lwork, info);\n}\n\nint lapack_sygv_dispatch(int *itype, char *jobz, char *uplo, int *n,\n                         hipComplex *a, int *lda, hipDoubleComplex *b, int *ldb,\n                         hipDoubleComplex *w, hipDoubleComplex *work, int *lwork, int *info)\n{\n    return zhegv_(itype, jobz, uplo, n,\n                  reinterpret_cast<dcomplex *>(a), lda, reinterpret_cast<dcomplex *>(b), ldb,\n                  reinterpret_cast<dcomplex *>(w), reinterpret_cast<dcomplex *>(work), lwork, info);\n}\n\ntemplate <typename T>\nvoid lapack_sygv(T *gramA, T *gramB, T *eigenvector, int dim, int lda, T *work)\n{\n    int itype = 1;\n    char jobz = 'V';\n    char uplo = 'U';\n    int ldb = lda;\n    int lwork = 1024;\n    int info = 0;\n    lapack_sygv_dispatch(&itype, &jobz, &uplo, &dim, gramA, &lda, gramB, &ldb, eigenvector, work, &lwork, &info);\n    lapackCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::sygv(Vector<TConfig> &A, Vector<TConfig> &B,\n        Vector<TConfig> &eigenvalues, Vector<TConfig> &work)\n{\n    check_lapack_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_LAPACK\n    lapack_sygv(A.raw(), B.raw(), eigenvalues.raw(), dim, lda, work.raw());\n#else\n    FatalError(\"Lapack is not supported in this build\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nvoid magma_trsm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         float alpha, float const *dA, magma_int_t lda,\n                         float *dB, magma_int_t ldb)\n{\n    return magma_strsm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trsm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         double alpha, double const *dA, magma_int_t lda,\n                         double *dB, magma_int_t ldb)\n{\n    return magma_dtrsm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trmm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         float alpha, float const *dA, magma_int_t lda,\n                         float *dB, magma_int_t ldb)\n{\n    return magma_strmm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nvoid magma_trmm_dispatch(magma_side_t side, magma_uplo_t uplo, magma_trans_t trans,\n                         magma_diag_t diag, magma_int_t m, magma_int_t n,\n                         double alpha, double const *dA, magma_int_t lda,\n                         double *dB, magma_int_t ldb)\n{\n    return magma_dtrmm(side, uplo, trans, diag, m, n, alpha, dA, lda, dB, ldb);\n}\n\nint magma_potrf_gpu_dispatch(magma_uplo_t uplo, int n, float *A, int lda, int *info)\n{\n    return magma_spotrf_gpu(uplo, n, A, lda, info);\n}\n\nint magma_potrf_gpu_dispatch(magma_uplo_t uplo, int n, double *A, int lda, int *info)\n{\n    return magma_dpotrf_gpu(uplo, n, A, lda, info);\n}\n\nint magma_sygst_gpu_dispatch(int itype, magma_uplo_t uplo, magma_int_t n, float *da,\n                             int ldda, float *B, int lddb, int *info)\n{\n    return magma_ssygst_gpu(itype, uplo, n, da, ldda, B, lddb, info);\n}\n\nint magma_sygst_gpu_dispatch(int itype, magma_uplo_t uplo, magma_int_t n, double *da,\n                             int ldda, double *B, int lddb, int *info)\n{\n    return magma_dsygst_gpu(itype, uplo, n, da, ldda, B, lddb, info);\n}\n\nint magma_syevd_gpu_dispatch(magma_vec_t jobz, magma_uplo_t uplo, int n, double *da, int ldda,\n                             double *w, double *wa, int ldwa, double *work,\n                             int lwork, int *iwork, int liwork, int *info)\n{\n    return magma_dsyevd_gpu(jobz, uplo, n, da, ldda, w, wa, ldwa, work, lwork, iwork, liwork, info);\n}\n\nint magma_syevd_gpu_dispatch(magma_vec_t jobz, magma_uplo_t uplo, int n, float *da, int ldda,\n                             float *w, float *wa, int ldwa, float *work,\n                             int lwork, int *iwork, int liwork, int *info)\n{\n    return magma_ssyevd_gpu(jobz, uplo, n, da, ldda, w, wa, ldwa, work, lwork, iwork, liwork, info);\n}\n\n// This is a simple modification of the magma_?sygvd() source code\n// from magma where the matrices are already on the device.\ntemplate <typename T>\nmagma_int_t magma_sygvd_gpu_impl(magma_int_t itype, magma_vec_t jobz, magma_uplo_t uplo, magma_int_t n,\n                                 T *da, magma_int_t lda, T *db, magma_int_t ldb, T *w,\n                                 T *work, magma_int_t lwork, magma_int_t *iwork, magma_int_t liwork,\n                                 T *wa, magma_int_t *info)\n{\n    magma_uplo_t uplo_[2] = {uplo, MagmaLower}; // {uplo, 0}\n    magma_vec_t jobz_[2] = {jobz, MagmaVec};//{jobz, 0};\n    T d_one = MAGMA_D_ONE;\n    magma_int_t ldda = n;\n    magma_int_t lddb = n;\n    static magma_int_t lower;\n    static char trans[1];\n    static magma_int_t wantz, lquery;\n    static magma_int_t lopt, lwmin, liopt, liwmin;\n    static hipStream_t stream;\n    magma_queue_create( &stream );\n    wantz = jobz_[0] == MagmaVec;\n    lower = uplo_[0] == MagmaLower;\n    lquery = lwork == -1 || liwork == -1;\n    *info = 0;\n\n    if (itype < 1 || itype > 3)\n    {\n        *info = -1;\n    }\n    else if (! (wantz || jobz_[0] == MagmaNoVec))\n    {\n        *info = -2;\n    }\n    else if (! (lower || uplo_[0] == MagmaUpper))\n    {\n        *info = -3;\n    }\n    else if (n < 0)\n    {\n        *info = -4;\n    }\n    else if (lda < max(1, n))\n    {\n        *info = -6;\n    }\n    else if (ldb < max(1, n))\n    {\n        *info = -8;\n    }\n\n    magma_int_t nb = magma_get_dsytrd_nb(n);\n\n    if (n < 1)\n    {\n        liwmin = 1;\n        lwmin = 1;\n    }\n    else if (wantz)\n    {\n        lwmin = 1 + 6 * n * nb + 2 * n * n;\n        liwmin = 5 * n + 3;\n    }\n    else\n    {\n        lwmin = 2 * n * nb + 1;\n        liwmin = 1;\n    }\n\n    lopt = lwmin;\n    liopt = liwmin;\n    work[ 0] = lopt;\n    iwork[0] = liopt;\n\n    if (lwork < lwmin && ! lquery)\n    {\n        *info = -11;\n    }\n    else if (liwork < liwmin && ! lquery)\n    {\n        *info = -13;\n    }\n\n    if (*info != 0)\n    {\n        magma_xerbla( __func__, -(*info) );\n        return MAGMA_ERR_ILLEGAL_VALUE;\n    }\n    else if (lquery)\n    {\n        return MAGMA_SUCCESS;\n    }\n\n    /* Quick return if possible */\n    if (n == 0)\n    {\n        return 0;\n    }\n\n    magma_potrf_gpu_dispatch(uplo_[0], n, db, lddb, info);\n\n    if (*info != 0)\n    {\n        *info = n + *info;\n        return 0;\n    }\n\n    /* Transform problem to standard eigenvalue problem and solve. */\n    magma_sygst_gpu_dispatch(itype, uplo_[0], n, da, ldda, db, lddb, info);\n    magma_syevd_gpu_dispatch(jobz_[0], uplo_[0], n, da, ldda, w, wa, lda,\n                             work, lwork, iwork, liwork, info);\n    lopt = max( lopt, (magma_int_t) work[0]);\n    liopt = max(liopt, iwork[0]);\n\n    if (wantz && *info == 0)\n    {\n        /* Backtransform eigenvectors to the original problem. */\n        if (itype == 1 || itype == 2)\n        {\n            /* For A*x=(lambda)*B*x and A*B*x=(lambda)*x;\n               backtransform eigenvectors: x = inv(L)'*y or inv(U)*y */\n            if (lower)\n            {\n                *(unsigned char *)trans = MagmaTrans;\n            }\n            else\n            {\n                *(unsigned char *)trans = MagmaNoTrans;\n            }\n\n            magma_trsm_dispatch(MagmaLeft, uplo_[0], *trans, MagmaNonUnit,\n                                n, n, d_one, db, lddb, da, ldda);\n        }\n        else if (itype == 3)\n        {\n            /* For B*A*x=(lambda)*x;\n               backtransform eigenvectors: x = L*y or U'*y */\n            if (lower)\n            {\n                *(unsigned char *)trans = MagmaNoTrans;\n            }\n            else\n            {\n                *(unsigned char *)trans = MagmaTrans;\n            }\n\n            magma_trmm_dispatch(MagmaLeft, uplo_[0], *trans, MagmaNonUnit,\n                                n, n, d_one, db, lddb, da, ldda);\n        }\n    }\n\n    magma_queue_sync( stream );\n    magma_queue_destroy( stream );\n    work[0] = (T) lopt;\n    iwork[0] = liopt;\n    return MAGMA_SUCCESS;\n}\n\nhipblasStatus_t cublas_trsm(hipblasHandle_t handle,\n                           hipblasSideMode_t side, hipblasFillMode_t uplo,\n                           hipblasOperation_t trans, hipblasDiagType_t diag,\n                           int m, int n,\n                           const float *alpha,\n                           const float *A, int lda,\n                           float *B, int ldb)\n{\n    return hipblasStrsm(handle, side, uplo, trans, diag, m, n, alpha, A, lda, B, ldb);\n}\n\nhipblasStatus_t cublas_trsm(hipblasHandle_t handle,\n                           hipblasSideMode_t side, hipblasFillMode_t uplo,\n                           hipblasOperation_t trans, hipblasDiagType_t diag,\n                           int m, int n,\n                           const double *alpha,\n                           const double *A, int lda,\n                           double *B, int ldb)\n{\n    return hipblasDtrsm(handle, side, uplo, trans, diag, m, n, alpha, A, lda, B, ldb);\n}\n\n\ntemplate <typename T>\nvoid magma_sygvd_gpu(T *A, T *B, T *eigenvalues, int dim, int lda)\n{\n    int itype = 1;\n    magma_vec_t jobz = MagmaVec;\n    magma_uplo_t uplo = MagmaUpper;\n    int N = dim;\n    int ldb = lda;\n    int nb = 32;\n    int lwork = 1 + 6 * N * nb + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    T *work = &s_work[0];\n    int liwork = 3 + 5 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int *iwork = &s_iwork[0];\n    static std::vector<T> s_wa;\n    s_wa.resize(lda * N);\n    T *wa = &s_wa[0];\n    int ldwa = N;\n    int info;\n    /*\n            magma_sygvd_gpu_impl(itype, jobz, uplo, N, A, lda, B, ldb, eigenvalues, work, lwork, iwork, liwork, wa, &info);\n    */\n    magma_potrf_gpu_dispatch(uplo, N, B, lda, &info);\n    magmaCheckError(info);\n    magma_sygst_gpu_dispatch(itype, uplo, N, A, lda, B, ldb, &info);\n    magmaCheckError(info);\n    magma_syevd_gpu_dispatch(jobz, uplo, N, A, lda, eigenvalues, wa, ldwa, work, lwork, iwork, liwork, &info);\n    magmaCheckError(info);\n    T one = 1;\n    hipblasHandle_t handle = Cublas::get_handle();\n    cublas_trsm(handle, HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_UPPER, HIPBLAS_OP_N, HIPBLAS_DIAG_NON_UNIT, N, N, &one, B, ldb, A, lda);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::sygv(Vector<TConfig> &A, Vector<TConfig> &B,\n        Vector<TConfig_h> &eigenvalues, Vector<TConfig> &work)\n{\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_sygvd_gpu(A.raw(), B.raw(), eigenvalues.raw(), dim, lda);\n#endif\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\ntemplate <typename T>\nvoid magma_syevd_gpu(T *A, T *eigenvalues, int dim, int lda)\n{\n    magma_vec_t jobz = MagmaVec;\n    magma_uplo_t uplo = MagmaUpper;\n    int N = dim;\n    int nb = 32;\n    int lwork = 1 + 6 * N * nb + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    T *work = &s_work[0];\n    int liwork = 3 + 5 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int *iwork = &s_iwork[0];\n    static std::vector<T> s_wa;\n    s_wa.resize(lda * N);\n    T *wa = &s_wa[0];\n    int ldwa = N;\n    int info;\n    magma_syevd_gpu_dispatch(jobz, uplo, N, A, lda, eigenvalues, wa, ldwa, work, lwork, iwork, liwork, &info);\n    magmaCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::syevd(Vector<TConfig> &A,\n        Vector<TConfig_h> &eigenvalues)\n{\n    check_magma_enabled();\n    typedef typename Vector<TConfig>::value_type value_type;\n    int dim = std::min(A.get_num_rows(), A.get_num_cols());\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_syevd_gpu(A.raw(), eigenvalues.raw(), dim, lda);\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::syevd(Vector<TConfig> &A,\n        Vector<TConfig> &eigenvalues)\n{\n    not_implemented();\n}\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_stedx_dispatch(magma_range_t range, int n,\n                         double vl, double vu,\n                         int il, int iu,\n                         double *d, double *e, double *z, int ldz,\n                         double *work, int lwork, int *iwork, int liwork,\n                         double *dwork, int *info)\n{\n    return magma_dstedx(range, n, vl, vu, il, iu, d, e, z, ldz, work, lwork, iwork, liwork, dwork, info);\n}\n\nint magma_stedx_dispatch(magma_range_t range, int n,\n                         float vl, float vu,\n                         int il, int iu,\n                         float *d, float *e, float *z, int ldz,\n                         float *work, int lwork, int *iwork, int liwork,\n                         float *dwork, int *info)\n{\n    return magma_sstedx(range, n, vl, vu, il, iu, d, e, z, ldz, work, lwork, iwork, liwork, dwork, info);\n}\n\ntemplate <typename T>\nvoid magma_stedx(T *diagonal, T *subdiagonal, T *eigenvectors,\n                 int lower, int upper, int dim, int ldz, T *dwork, int dwork_size)\n{\n    magma_range_t range = MagmaRangeI;\n    int N = dim;\n    T vl = 0;\n    T vu = 0;\n    int il = lower;\n    int iu = upper;\n    int lwork = 1 + 4 * N + 2 * N * N;\n    static std::vector<T> s_work;\n    s_work.resize(lwork);\n    int liwork = 3 + 6 * N;\n    static std::vector<int> s_iwork;\n    s_iwork.resize(liwork);\n    int info;\n    magma_stedx_dispatch(range, N, vl, vu, il, iu, diagonal, subdiagonal, eigenvectors, ldz,\n                         &s_work[0], lwork, &s_iwork[0], liwork, dwork, &info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::stedx(Vector<TConfig> &diagonal,\n        Vector<TConfig> &subdiagonal,\n        Vector<TConfig> &eigenvectors,\n        int dim,\n        Vector<TConfig_d> &dwork)\n{\n    check_magma_enabled();\n#ifdef AMGX_USE_MAGMA\n    magma_stedx(diagonal.raw(), subdiagonal.raw(), eigenvectors.raw(),\n                dim, dim, dim, eigenvectors.get_lda(),\n                dwork.raw(), dwork.size());\n#endif\n}\n\n\nnamespace\n{\n\ntemplate <typename T>\nvoid larf(int m, int n, T *v,\n          int incv, T *tau, T *c, int ldc,\n          T *work)\n{\n    /* Table of constant values */\n    static T c_b4 = 1.;\n    static T c_b5 = 0.;\n    static int c1 = 1;\n    /*        Form  H * C */\n    /*        w := C' * v */\n    Cublas::gemv(true, m, n, &c_b4, c, ldc,\n                 v, incv, &c_b5, work, c1);\n    /*        C := C - v * w' */\n    Cublas::ger(m, n, tau, v, incv, work, c1, c, ldc);\n}\n\ntemplate <typename T>\n__global__\nvoid set1(T *a)\n{\n    *a = 1.;\n}\n\ntemplate <typename T>\n__global__\nvoid add_tau(T *a, T tau)\n{\n    *a = 1 + tau;\n}\n\ntemplate <typename T>\nvoid gpu_orgqr(int m, int n, int k,\n               T *a, int lda, T *tau, T *work, int lwork)\n{\n    int i1, i2;\n\n    for (int i = k - 1; i >= 0; --i)\n    {\n        /*        Apply H(i) to A(i:m,i:n) from the left */\n        if (i < n - 1)\n        {\n            set1 <<< 1, 1>>>(&a[i + i * lda]);\n            cudaCheckError();\n            i1 = m - i;\n            i2 = n - i - 1;\n            larf(i1, i2, &a[i + i * lda], 1, &tau[i],\n                 &a[i + (i + 1) * lda], lda, work);\n        }\n\n        if (i < m - 1)\n        {\n            i1 = m - i - 1;\n            Cublas::scal(i1, &tau[i], &a[i + 1 + i * lda], 1);\n        }\n\n        add_tau <<< 1, 1>>>(&a[i + i * lda], tau[i]);\n        cudaCheckError();\n        /*        Set A(1:i-1,i) to zero */\n        hipError_t cuda_rc = hipMemset(&a[i * lda], 0, sizeof(T) * i);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipMemset failed in geqr2\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <typename T>\n__device__ __host__\nT lapy2_(T *a, T *b)\n{\n    T va = *a;\n    T vb = *b;\n    return sqrt(va * va + vb * vb);\n}\n\ntemplate <typename T>\n__device__ __host__\nT d_sign(T a, T b)\n{\n    T x;\n    x = (a >= 0 ? a : - a);\n    return (b >= 0 ? x : -x);\n}\n\ntemplate <typename T>\nvoid compute_tau_host(T *alpha, T *norm,\n                      T *tau, T *d1)\n{\n    *d1 = lapy2_(alpha, norm);\n    T beta = -d_sign(*d1, *alpha);\n    // LAPACK: skipped part about scaling.\n    // Negated compared to LAPACK code, avoid negating value on device later.\n    *tau = -(beta - *alpha) / beta;\n    *d1 = 1. / (*alpha - beta);\n    *alpha = beta;\n}\n\ntemplate <typename T>\nvoid larfg(int n, T *alpha, T *x,\n           int incx, T *tau)\n{\n    if (n <= 1)\n    {\n        *tau = 0.;\n        return;\n    }\n\n    int i1 = n - 1;\n    T xnorm;\n    Cublas::nrm2(i1, x, incx, &xnorm);\n    T h_alpha;\n    hipError_t cuda_rc = hipMemcpy(&h_alpha, alpha, sizeof(T), hipMemcpyDeviceToHost);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy D2H failed in larfg\", AMGX_ERR_CUDA_FAILURE);\n    }\n    T d1;\n    compute_tau_host(&h_alpha, &xnorm, tau, &d1);\n    Cublas::scal(i1, d1, x, incx);\n    // Update the diagonal value on the device.\n    cuda_rc = hipMemcpy(alpha, &h_alpha, sizeof(T), hipMemcpyHostToDevice);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy H2D failed in larfg\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n\ntemplate <typename T>\nvoid gpu_geqrf(int m, int n, T *a, int lda,\n               T *tau, T *work)\n{\n    int k = std::min(m, n);\n    T *aii;\n    hipError_t cuda_rc = amgx::memory::hipMallocAsync((void**)&aii, sizeof(T));\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMallocAsync failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for (int i = 0; i < k; ++i)\n    {\n        /*        Generate elementary reflector H(i) to annihilate A(i+1:m,i) */\n        int i2 = m - i;\n        /* Computing MIN */\n        int i3 = i + 1;\n        larfg(i2, &a[i + i * lda],\n              &a[std::min(i3, m - 1) + i * lda],\n              1, &tau[i]);\n\n        if (i < n - 1)\n        {\n            /*           Apply H(i) to A(i:m,i+1:n) from the left */\n            cuda_rc = hipMemcpy(aii, &a[i + i * lda], sizeof(T), hipMemcpyDeviceToDevice);\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipMemcpy D2D failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n            }\n            set1 <<< 1, 1>>>(&a[i + i * lda]);\n            cudaCheckError();\n            i2 = m - i;\n            i3 = n - i - 1;\n            larf(i2, i3, &a[i + i * lda], 1,\n                 &tau[i], &a[i + (i + 1) * lda], lda, work);\n            cuda_rc = hipMemcpy(&a[i + i * lda], aii, sizeof(T), hipMemcpyDeviceToDevice);\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipMemcpy D2D failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n\n    cuda_rc = amgx::memory::hipFreeAsync(aii);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipFreeAsync failed in gpu_geqrf\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n} // end anonymous namespace\n\nnamespace\n{\n#ifdef AMGX_USE_MAGMA\nint magma_geqrf_dispatch(int m, int n, float *A, int lda,\n                         float *tau, float *work, int *info)\n{\n    return magma_sgeqrf_gpu(m, n, A, lda, tau, work, info);\n}\n\nint magma_geqrf_dispatch(int m, int n, double *A, int lda,\n                         double *tau, double *work, int *info)\n{\n    return magma_dgeqrf_gpu(m, n, A, lda, tau, work, info);\n}\n\ntemplate <typename T>\nvoid magma_geqrf(int m, int n, T *A, int lda,\n                 T *tau, T *work)\n{\n    int info;\n    magma_geqrf_dispatch(m, n, A, lda, tau, work, &info);\n    magmaCheckError(info);\n}\n\nint magma_orgqr_dispatch(int m, int n, int k, float *A, int lda,\n                         float *tau, float *work, int lwork, int *info)\n{\n    return magma_sorgqr_gpu(m, n, k, A, lda, tau, work, lwork, info);\n}\n\nint magma_orgqr_dispatch(int m, int n, int k, double *A, int lda,\n                         double *tau, double *work, int lwork, int *info)\n{\n    return magma_dorgqr_gpu(m, n, k, A, lda, tau, work, lwork, info);\n}\n\ntemplate <typename T>\nvoid magma_orgqr(int m, int n, int k, T *A, int lda,\n                 T *tau, T *work, int lwork)\n{\n    int info;\n    magma_orgqr_dispatch(m, n, k, A, lda, tau, work, lwork, &info);\n    magmaCheckError(info);\n}\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::geqrf(Vector<TConfig> &A,\n        Vector<TConfig> &tau,\n        Vector<TConfig> &work)\n{\n    not_implemented();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::geqrf(Vector<TConfig> &A,\n        Vector<TConfig_h> &tau,\n        Vector<TConfig> &work)\n{\n    int rows = A.get_num_rows();\n    int cols = A.get_num_cols();\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_geqrf(rows, cols, A.raw(), lda, tau.raw(), work.raw());\n#else\n    gpu_geqrf(rows, cols, A.raw(), lda, tau.raw(), work.raw());\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::orgqr(Vector<TConfig> &A,\n        Vector<TConfig> &tau,\n        Vector<TConfig> &work)\n{\n    not_implemented();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Lapack< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::orgqr(Vector<TConfig> &A,\n        Vector<TConfig_h> &tau,\n        Vector<TConfig> &work)\n{\n    int rows = A.get_num_rows();\n    int cols = A.get_num_cols();\n    int lda = A.get_lda();\n#ifdef AMGX_USE_MAGMA\n    magma_orgqr(rows, cols, cols, A.raw(), lda, tau.raw(), work.raw(), 1);\n#else\n    gpu_orgqr(rows, cols, cols, A.raw(), lda, tau.raw(), work.raw(), 1);\n#endif\n}\n\n\n#define AMGX_CASE_LINE(CASE) \\\n    template class Lapack<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n", "source": "/kaggle/working/amgx/src/amgx_lapack.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <truncate.h>\n#include <specific_spmv.h>\n#include <basic_types.h>\n#include <util.h>\n#include <algorithm>\n#include <thrust_wrapper.h>\n\n#include \"amgx_types/util.h\"\n\n#define USE_EXPERIMENTAL_FACTOR_COPY\n// #define USE_EXPERIMENTAL_NLARGEST\n\nnamespace amgx\n{\n\n__host__ __device__ __forceinline__\ndouble ourHash2(const int i)\n{\n    unsigned int a = i;\n    double result;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    result = (a ^ 0x4a51e590) / (double) UINT_MAX;\n    return result;\n}\n\ntemplate <typename IndexType, typename metricType, typename matrixType>\nstruct copy_pred\n{\n    double _trunc_factor;\n    metricType *_metric_arr;\n\n    copy_pred() : _trunc_factor(0), _metric_arr(NULL) {};\n    copy_pred(const double trunc_factor, metricType *metric_arr) : _trunc_factor(trunc_factor),\n        _metric_arr(metric_arr) {};\n\n    __host__ __device__\n    bool operator()(const amgx::thrust::tuple<IndexType, IndexType, matrixType> &a)\n    {\n        metricType metric = _metric_arr[amgx::thrust::get<0>(a)];\n\n        if (  types::util<matrixType>::abs(amgx::thrust::get<2>(a)) >= _trunc_factor * metric) { return true; }\n\n        return false;\n    }\n\n    copy_pred<IndexType, metricType, matrixType> &operator=(const copy_pred<IndexType, metricType, matrixType> &a)\n    {\n        this->_trunc_factor = a._trunc_factor;\n        this->_metric_arr = a._metric_arr;\n        return *this;\n    }\n};\n\ntemplate <typename IndexType, typename VectorType, typename MatrixType>\nstruct scale_op\n{\n    const VectorType *scale_vec;\n\n    scale_op(const VectorType *s) : scale_vec(s) {};\n\n    __host__ __device__\n    amgx::thrust::tuple<IndexType, MatrixType> operator()(const amgx::thrust::tuple<IndexType, MatrixType> &a)\n    {\n        const IndexType row = amgx::thrust::get<0>(a);\n        return amgx::thrust::tuple<IndexType, MatrixType>(row, amgx::thrust::get<1>(a) * scale_vec[row]);\n    }\n};\n\ntemplate <typename index_type, typename value_type>\n__global__\nvoid scale_kernel(const index_type *row_indices, value_type *values, const value_type *new_row_sums, const value_type *old_row_sums, const int size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int row_index = row_indices[tidx];\n        value_type multiplier = ( types::util<value_type>::abs(new_row_sums[row_index]) == 0. ) ? types::util<value_type>::get_one() : (old_row_sums[row_index] / new_row_sums[row_index]);\n        values[tidx] = values[tidx] * multiplier;\n\n        if (values[tidx] != values[tidx]) { printf(\"%d (%d): NaN after scaling\\n\", tidx, row_index); }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid get_row_lengths_kernel(const IndexType *offsets, const IndexType num_rows, IndexType *row_lengths,\n                            const int max_elmts)\n{\n    for (int row = threadIdx.x + blockIdx.x * blockDim.x; row < num_rows; row += blockDim.x * gridDim.x)\n    {\n        row_lengths[row] = min(offsets[row + 1] - offsets[row], max_elmts);\n    }\n}\n\n// sort array by abs(values), largest element in [0]\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid sortByFabs(IndexType *indices, ValueType *values, int elements)\n{\n    int n = elements;\n\n    do\n    {\n        int newn = 0;\n\n        for (int i = 1; i < n; i++)\n        {\n            if (types::util<ValueType>::abs(values[i - 1]) < types::util<ValueType>::abs(values[i]))\n            {\n                ValueType temp_v = values[i - 1];\n                IndexType temp_i = indices[i - 1];\n                values[i - 1] = values[i];\n                values[i] = temp_v;\n                indices[i - 1] = indices[i];\n                indices[i] = temp_i;\n                newn = i;\n            }\n        }\n\n        n = newn;\n    }\n    while (n > 0);\n}\n\n#ifdef USE_EXPERIMENTAL_NLARGEST\n\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid insert_new_max(volatile ValueType *Mv, volatile IndexType *Mi, volatile int *T, const ValueType S,\n                    const IndexType I, const int thread, const int N)\n{\n    if (thread < N)\n    {\n        T[thread] = 1;\n\n        // new value larger - shift my value down\n        if (Mv[thread] < S)\n        {\n            T[thread] = 0;\n            Mv[thread + 1] = Mv[thread];\n            Mi[thread + 1] = Mi[thread];\n            T[thread + 1]  = 1;\n        }\n\n        // insert new largest value here\n        if (!T[thread])\n        {\n            Mv[thread] = S;\n            Mi[thread] = I;\n            // inserted to me -- note to zero out\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType, typename VecValueType,\n          int BLOCKSIZE, int THREADS_PER_VECTOR>\n__global__\nvoid truncateNandScale_kernel(const IndexType *A_offsets, const IndexType *A_indices, const ValueType *A_values,\n                              const IndexType num_rows,\n                              const IndexType *At_offsets, IndexType *At_indices, ValueType *At_values,\n                              const int max_elmts, const VecValueType *old_row_sums)\n{\n    const int WARP_SIZE = 32;\n    const int VECTORS_PER_WARP = WARP_SIZE / THREADS_PER_VECTOR;\n    const int WARPS_PER_BLOCK  = BLOCKSIZE / WARP_SIZE;\n    const int N_LARGEST_SIZE   = (THREADS_PER_VECTOR + 1) * VECTORS_PER_WARP * WARPS_PER_BLOCK;\n    __shared__ volatile int T[N_LARGEST_SIZE]; // has M array been modified\n    __shared__ volatile ValueType Mv[N_LARGEST_SIZE]; // maximum values\n    __shared__ volatile int Mi[N_LARGEST_SIZE]; // maximum indices\n    __shared__ volatile ValueType S[WARP_SIZE * WARPS_PER_BLOCK]; // temporary holding for currently read values\n    __shared__ volatile IndexType I[WARP_SIZE * WARPS_PER_BLOCK]; //               -- \" --                indices\n    ValueType S_copy;\n    IndexType I_copy;\n    __shared__ volatile int Ap[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // store row offset pointers\n    __shared__ volatile ValueType old_row_sum[WARPS_PER_BLOCK * VECTORS_PER_WARP];\n    const int num_vectors = blockDim.x / THREADS_PER_VECTOR * gridDim.x;\n    const int vector_id = (threadIdx.x + blockIdx.x * blockDim.x) / THREADS_PER_VECTOR;\n    const int thread_lane = threadIdx.x & (THREADS_PER_VECTOR - 1);\n    const int vector_lane = threadIdx.x / THREADS_PER_VECTOR;\n\n    // initialise relevant shared memory arrays\n    for (int i = threadIdx.x; i < N_LARGEST_SIZE; i += blockDim.x)\n    {\n        Mv[i] = 0.;\n        Mi[i] = -1;\n        T[i] = 0;\n    }\n\n    __syncthreads();\n\n    // outer loop over rows\n    for (int row = vector_id; row < num_rows; row += num_vectors)\n    {\n        // load row offsets into shared memory\n        if (thread_lane < 2)\n        {\n            Ap[vector_lane][thread_lane] = A_offsets[row + thread_lane];\n        }\n\n        const int row_start = Ap[vector_lane][0];\n        const int row_end   = Ap[vector_lane][1];\n        const int row_len   = row_end - row_start;\n        const int max_elmts_row = min(At_offsets[row + 1] - At_offsets[row], max_elmts);\n\n        // degenerate case -- simply copy over entire row\n        if (row_len <= max_elmts && row_len > 0 && thread_lane < row_len)\n        {\n            At_indices[At_offsets[row] + thread_lane] = A_indices[row_start + thread_lane];\n            At_values[At_offsets[row] + thread_lane] = A_values[row_start + thread_lane];\n            continue;\n        }\n\n        // loop over chunks of the row\n        const int nloops = (row_len < THREADS_PER_VECTOR) ? 1 : (int)ceil((float)row_len / THREADS_PER_VECTOR);\n\n        for (int i = 0; i < nloops; i++)\n        {\n            // zero out temp shared memory\n            S[threadIdx.x] =  0;\n            I[threadIdx.x] = -2;\n\n            // read chunk into shared memory\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                S[threadIdx.x] = fabs(A_values[row_start + i * THREADS_PER_VECTOR + thread_lane]);\n                I[threadIdx.x] = A_indices[row_start + i * THREADS_PER_VECTOR + thread_lane];\n            }\n\n            // loop over n' max elements\n            for (int j = 0; j < max_elmts_row; j++)\n            {\n                // first take a copy of this section of the row so we don't re-read it\n                S_copy = S[threadIdx.x];\n                I_copy = I[threadIdx.x];\n                // local max & associated index (save volatile loads)\n                ValueType max = S_copy; // S[threadIdx.x];\n                IndexType ind = I_copy; // I[threadIdx.x];\n\n                // reduce to get max\n                if (THREADS_PER_VECTOR > 16)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x + 16] ? ind : I[threadIdx.x + 16];\n                    S[threadIdx.x] = max = max > S[threadIdx.x + 16] ? max : S[threadIdx.x + 16];\n                }\n\n                if (THREADS_PER_VECTOR > 8)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  8] ? ind : I[threadIdx.x +  8];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  8] ? max : S[threadIdx.x +  8];\n                }\n\n                if (THREADS_PER_VECTOR > 4)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  4] ? ind : I[threadIdx.x +  4];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  4] ? max : S[threadIdx.x +  4];\n                }\n\n                if (THREADS_PER_VECTOR > 2)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  2] ? ind : I[threadIdx.x +  2];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  2] ? max : S[threadIdx.x +  2];\n                }\n\n                if (THREADS_PER_VECTOR >  1)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  1] ? ind : I[threadIdx.x +  1];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  1] ? max : S[threadIdx.x +  1];\n                }\n\n                // maximum now in S_copy[vector_lane*32] -- insert into maximum array\n                const int sm_offset = vector_lane * (THREADS_PER_VECTOR + 1);\n                insert_new_max(&Mv[sm_offset], &Mi[sm_offset], &T[sm_offset],\n                               S[vector_lane * THREADS_PER_VECTOR], I[vector_lane * THREADS_PER_VECTOR], thread_lane,\n                               max_elmts);\n                S[threadIdx.x] = (I[vector_lane * THREADS_PER_VECTOR] == I_copy) ? 0 : S_copy;\n                I[threadIdx.x] = I_copy;\n            }\n        }\n\n        // get scaling factor for new row\n        // copy Mv -> S\n        S[threadIdx.x] = 0; // intialise unneeded S array to 0\n\n        if (thread_lane < max_elmts_row)\n        {\n            S[threadIdx.x] = Mv[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane];\n        }\n\n        // reduce on S\n        ValueType sum = fabs(S[threadIdx.x]);\n\n        if (THREADS_PER_VECTOR > 16) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 16]; }\n\n        if (THREADS_PER_VECTOR >  8) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 8]; }\n\n        if (THREADS_PER_VECTOR >  4) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 4]; }\n\n        if (THREADS_PER_VECTOR >  2) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 2]; }\n\n        if (THREADS_PER_VECTOR >  1) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 1]; }\n\n        const ValueType scale = old_row_sums[row] / S[vector_lane * THREADS_PER_VECTOR];\n\n        // copy final scaled results into output buffer\n        if (thread_lane < max_elmts_row)\n        {\n            At_values[At_offsets[row] + thread_lane]  = Mv[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane] * scale;\n            At_indices[At_offsets[row] + thread_lane] = Mi[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane];\n        }\n    }\n}\n\n#else\n// sort array by column index\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid sortByIndex(IndexType *indices, ValueType *values, int elements)\n{\n    int n = elements;\n\n    do\n    {\n        int newn = 0;\n\n        for (int i = 1; i < n; i++)\n        {\n            if (indices[i - 1] > indices[i])\n            {\n                ValueType temp_v = values[i - 1];\n                IndexType temp_i = indices[i - 1];\n                values[i - 1] = values[i];\n                values[i] = temp_v;\n                indices[i - 1] = indices[i];\n                indices[i] = temp_i;\n                newn = i;\n            }\n        }\n\n        n = newn;\n    }\n    while (n > 0);\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid row_sum_kernel(const IndexType *A_offsets, const ValueType *A_values,\n                    ValueType *row_sums, const IndexType num_rows, IndexType *row_lengths, const int max_elmts, bool compute_row_length)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n\n        if (compute_row_length)\n        {\n            row_lengths[tidx] = min(A_end - A_start, max_elmts);\n        }\n\n        ValueType row_sum = 0.;\n        bool found_nans = false;\n\n        for (int i = A_start; i < A_end; i++)\n        {\n            row_sum += A_values[i];\n\n            if (A_values[i] != A_values[i]) { found_nans = true; }\n        }\n\n        if (found_nans)\n        {\n            printf(\"row %d has NaNs in input\\n\", tidx);\n        }\n\n        row_sums[tidx] = row_sum;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid __launch_bounds__(256)\ntruncate_kernel(const IndexType *A_offsets, const IndexType *A_col_indices, const ValueType *A_values,\n                const IndexType num_rows,\n                IndexType *trunc_offsets, IndexType *trunc_col_indices, ValueType *trunc_values,\n                const int max_elmts)\n{\n    // const int tidx = threadIdx.x + blockIdx.x*blockDim.x;\n    __shared__ IndexType s_ind[1024]; // assume <= 256 threads per block\n    __shared__ ValueType s_val[1024];\n    const int smem_start = threadIdx.x * max_elmts;\n\n    // if (tidx < num_rows)\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n        IndexType trunc_start = trunc_offsets[tidx];\n        IndexType A_len = A_end - A_start;\n\n        // copy my entire row if possible\n        if (A_len <= max_elmts)\n        {\n            for (int j = 0; j < A_len; j++)\n            {\n                trunc_col_indices[trunc_start + j] = A_col_indices[A_start + j];\n                trunc_values[trunc_start + j] = A_values[A_start + j];\n            }\n        }\n        else // normal case\n        {\n            // read first max_elmts elements into shared\n            for (int j = 0; j < max_elmts; j++)\n            {\n                s_ind[smem_start + j] = A_col_indices[A_start + j];\n                s_val[smem_start + j] = A_values[A_start + j];\n            }\n\n            // sort the initially inserted elements (start = largest)\n            sortByFabs(&s_ind[smem_start], &s_val[smem_start], max_elmts);\n\n            // loop over all other elements on the row\n            for (int j = A_start + max_elmts; j < A_end; j++)\n            {\n                // check against current maximums\n                for (int i = 0; i < max_elmts; i++)\n                {\n                    if (types::util<ValueType>::abs(A_values[j]) > types::util<ValueType>::abs(s_val[smem_start + i]))\n                    {\n                        // shift remaining values down\n                        for (int k = max_elmts - 1; k > i; k--)\n                        {\n                            s_val[smem_start + k] = s_val[smem_start + k - 1];\n                            s_ind[smem_start + k] = s_ind[smem_start + k - 1];\n                        }\n\n                        // tentatively replace element\n                        s_val[smem_start + i] = A_values[j];\n                        s_ind[smem_start + i] = A_col_indices[j];\n                        break;\n                    }\n                }\n            }\n\n            //sortByIndex(&s_ind[smem_start],&s_val[smem_start],max_elmts);\n            // now we have the 4 largest elements (hopefully) -- output\n            for (int i = 0; i < max_elmts; i++)\n            {\n                trunc_col_indices[trunc_start + i] = s_ind[smem_start + i];\n                trunc_values[trunc_start + i] = s_val[smem_start + i];\n            }\n        }\n    }\n}\n#endif\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid perturb_kernel(const IndexType *A_offsets, const IndexType *A_col_indices, ValueType *A_values,\n                    const IndexType num_rows,\n                    const int max_elmts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n\n        // perturb A slightly\n        for (int j = A_start; j < A_end; j++)\n        {\n            A_values[j] += ourHash2(A_col_indices[j]) * A_values[j];\n        }\n    }\n}\n\n\n// warp-based inclusive scan\n// from: Sengupta, Harris, Garland 2008\n// idx is index of thread\ntemplate <typename IndexType>\n__device__\nint scan_warp(volatile IndexType *ptr, const unsigned int idx)\n{\n    const unsigned int lane = idx & 31; // index of thread in warp\n    IndexType p = ptr[idx];\n\n    if (lane >=  1) { ptr[idx] = p = ptr[idx -  1] + p; }\n\n    if (lane >=  2) { ptr[idx] = p = ptr[idx -  2] + p; }\n\n    if (lane >=  4) { ptr[idx] = p = ptr[idx -  4] + p; }\n\n    if (lane >=  8) { ptr[idx] = p = ptr[idx -  8] + p; }\n\n    if (lane >= 16) { ptr[idx] = p = ptr[idx - 16] + p; }\n\n    return ptr[idx];\n}\n\ntemplate <typename IndexType, typename matValueType, typename vecValueType, int THREADS_PER_BLOCK,\n          int THREADS_PER_VECTOR>\n__global__\nvoid truncateAndScale_kernel(const IndexType *A_offsets, const IndexType *A_indices,\n                             const matValueType *A_values,\n                             const IndexType num_rows,\n                             IndexType *At_offsets, IndexType *At_indices, matValueType *At_values,\n                             const vecValueType *old_row_sums, const vecValueType *new_row_sums,\n                             const double truncate_factor, const vecValueType *metric)\n{\n    const int WARP_SIZE = 32;\n    const int WARPS_PER_BLOCK = THREADS_PER_BLOCK / WARP_SIZE;\n    const int VECTORS_PER_WARP = WARP_SIZE / THREADS_PER_VECTOR;\n    // allocate shared memory for warp-based scans (assume up to 512 threads / block)\n    __shared__ volatile int s_scan[WARP_SIZE * WARPS_PER_BLOCK];\n    __shared__ volatile int s_A_ptr[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // row offsets for A\n    __shared__ volatile int s_At_ptr[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // row offsets for At\n    __shared__ double s_scale[WARPS_PER_BLOCK * VECTORS_PER_WARP]; // scaling factor for each warp\n    const int vectors_per_block = THREADS_PER_BLOCK / WARP_SIZE * VECTORS_PER_WARP;\n    const int num_vectors = THREADS_PER_BLOCK / THREADS_PER_VECTOR * gridDim.x;\n    const int vector_id = (threadIdx.x + blockIdx.x * blockDim.x) / THREADS_PER_VECTOR;\n    const int thread_lane = threadIdx.x & (THREADS_PER_VECTOR - 1);\n    const int vector_lane = threadIdx.x / THREADS_PER_VECTOR;\n    int scan_offset = 0;\n    matValueType val = 0., fval = 0.;\n    IndexType ind = 0;\n\n    // outer loop over rows\n    for (int row = vector_id; row < num_rows; row += num_vectors)\n    {\n        scan_offset = 0;\n        val = 0.;\n        fval = 0.; // fabs(val)\n\n        if (threadIdx.x < vectors_per_block && threadIdx.x < num_rows - row) // first threads in first warp\n        {\n            s_scale[threadIdx.x] = old_row_sums[row + threadIdx.x] / new_row_sums[row + threadIdx.x];\n        }\n\n        __syncthreads();\n\n        if (thread_lane < 2)\n        {\n            s_A_ptr[vector_lane][thread_lane] = A_offsets[row + thread_lane];\n            s_At_ptr[vector_lane][thread_lane] = At_offsets[row + thread_lane];\n        }\n\n        const int row_start = s_A_ptr[vector_lane][0];\n        const int row_end   = s_A_ptr[vector_lane][1];\n        const int row_len = row_end - row_start;\n        const int At_row_start = s_At_ptr[vector_lane][0];\n        // mark & scan over consecutive blocks of 32 threads\n        int nloops = (int)ceilf((float)row_len / THREADS_PER_VECTOR);\n#pragma unroll 2\n\n        for (int i = 0; i < nloops; i++)\n        {\n            // initialise shared memory to 0\n            s_scan[threadIdx.x] = 0;\n\n            // mark appropriate elements of the row\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                val = A_values[row_start + i * THREADS_PER_VECTOR + thread_lane];\n                ind = A_indices[row_start + i * THREADS_PER_VECTOR + thread_lane];\n                fval = fabs(val);\n                s_scan[threadIdx.x] = fval >= truncate_factor * metric[row] ? 1 : 0;\n            }\n\n            // now prefix scan over this\n            scan_warp(&s_scan[vector_lane * THREADS_PER_VECTOR], thread_lane); // threadIdx.x%32);\n\n            // copy relevant parts of A to At\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                if (fval >= truncate_factor * metric[row])\n                {\n                    // get relevant index in At\n                    const int At_index = At_row_start + scan_offset + s_scan[threadIdx.x] - 1;\n\n                    if (s_scale[vector_lane] == 0.)\n                    {\n                        At_values[At_index] = val;\n                    }\n                    else\n                    {\n                        At_values[At_index] = val / s_scale[vector_lane];\n                    }\n\n                    At_indices[At_index] = ind;\n                }\n            }\n\n            // note new scan_offset\n            scan_offset = s_scan[vector_lane * THREADS_PER_VECTOR + THREADS_PER_VECTOR - 1];\n        }\n    }\n}\n\ntemplate <typename Matrix, typename Vector, int THREADS_PER_BLOCK, int THREADS_PER_VECTOR>\nvoid truncateAndScale(const Matrix &A, Matrix &A_trunc, const Vector &row_sum, const Vector &new_row_sum,\n                      const double trunc_factor, const Vector &metric)\n{\n    const int num_blocks = std::min(4096, A.get_num_rows());\n    truncateAndScale_kernel<typename Matrix::index_type, typename Matrix::value_type,\n                            typename Vector::value_type, THREADS_PER_BLOCK, THREADS_PER_VECTOR>\n                            <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                                    A.get_num_rows(),\n                                    A_trunc.row_offsets.raw(), A_trunc.col_indices.raw(),\n                                    A_trunc.values.raw(),\n                                    row_sum.raw(), new_row_sum.raw(),\n                                    trunc_factor, metric.raw());\n    cudaCheckError();\n}\n\n// host code\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_h &A,\n        const double trunc_factor, const AMGX_TruncateType truncType)\n{\n    // throw error\n    FatalError(\"Truncation on host not implemented yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::truncateByMaxElements(Matrix_h &A,\n        const int max_elmts)\n{\n    // throw error\n    FatalError(\"Truncation on host not implemented yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n// device code\n#ifdef _WIN32\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_d &A, const double trunc_factor, const AMGX_TruncateType truncType)\n#else\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_d &A, const double trunc_factor, const AMGX_TruncateType truncType) __attribute__((noinline))\n#endif\n{\n    typedef typename TConfig_d::IndPrec index_type;\n    typedef typename TConfig_d::VecPrec vec_value_type;\n    typedef typename TConfig_d::MatPrec value_type;\n    // store the weighting values per row\n    Matrix<TConfig_d> A_trunc(0, 0, 0, CSR);\n    typedef Vector<TConfig_d> VVector;\n    typedef typename Matrix<TConfig_d>::MVector MVector;\n    typedef Vector<typename TConfig_d::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    VVector row_sum(A.get_num_rows());\n    VVector new_row_sum(A.get_num_rows());\n    VVector max_coef;\n    // point to either row_sum or max_coef depending on type of truncation desired\n    // vec_value_type *metric = 0;\n    VVector *metric;\n\n    // get either max row coefficient or row sum\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        absRowSum(A, row_sum);\n        metric = &row_sum;\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        max_coef.resize(A.get_num_rows());\n        maxCoefAndSum(A, max_coef, row_sum);\n        metric = &max_coef;\n    }\n    else\n    {\n        FatalError(\"Truncation type not implemented\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IVector row_counts(A.get_num_rows());\n\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        countTruncElements(A, trunc_factor, row_sum, row_counts, new_row_sum);\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        countTruncElements(A, trunc_factor, max_coef, row_counts, new_row_sum);\n    }\n\n    cudaCheckError();\n    // initial resize (so we can scan into the row_offsets array)\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), 0);\n    cudaCheckError();\n\n    thrust_wrapper::exclusive_scan<IVector::TConfig::memSpace>(row_counts.begin(), row_counts.end(), A_trunc.row_offsets.begin());\n    cudaCheckError();\n    \n    const int nnz = A_trunc.row_offsets[A.get_num_rows() - 1] + row_counts[A.get_num_rows() - 1];\n    A_trunc.row_offsets[A.get_num_rows()] = nnz;\n\n    if (nnz == A.get_num_nz()) // early return -- nothing truncated\n    {\n        return;\n    }\n\n    // final resize & get row indices for both matrices\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), nnz);\n#ifndef USE_EXPERIMENTAL_FACTOR_COPY\n    A_trunc.addProps(COO);\n    A.addProps(COO);\n    // copy relevant values from A -> A_trunc\n    copy_pred<index_type, vec_value_type, value_type> pred;\n\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        pred = copy_pred<index_type, vec_value_type, value_type>(trunc_factor, row_sum.raw());\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        pred = copy_pred<index_type, vec_value_type, value_type>(trunc_factor, max_coef.raw());\n    }\n\n    using amgx::thrust::make_zip_iterator;\n    using amgx::thrust::make_tuple;\n    amgx::thrust::copy_if(\n        make_zip_iterator(make_tuple(A.row_indices.begin(), A.col_indices.begin(), A.values.begin())),\n        make_zip_iterator(make_tuple(A.row_indices.end(), A.col_indices.end(), A.values.end())),\n        make_zip_iterator(make_tuple(A_trunc.row_indices.begin(), A_trunc.col_indices.begin(),\n                                     A_trunc.values.begin())),\n        pred\n    );\n    // scale each row of the truncated matrix - per-row scale in new_row_sum\n    amgx::thrust::transform(row_sum.begin(), row_sum.end(), new_row_sum.begin(), new_row_sum.begin(),\n                      amgx::thrust::divides<vec_value_type>());\n    scale_op<index_type, vec_value_type, value_type> op(new_row_sum.raw());\n    amgx::thrust::for_each(\n        make_zip_iterator(make_tuple(A_trunc.row_indices.begin(), A_trunc.values.begin())),\n        make_zip_iterator(make_tuple(A_trunc.row_indices.end(), A_trunc.values.end())),\n        op\n    );\n#else\n    const int avg_row_len = (int)ceil(sqrt((float)A.get_num_nz() / A.get_num_rows()));\n\n    if (avg_row_len > 16)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 32>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else if (avg_row_len > 8)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 16>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else if (avg_row_len > 4)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 8>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 4>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n\n#endif\n    cudaCheckError();\n    // copy truncated matrix to this\n    A.copy(A_trunc);\n}\n\n#ifdef USE_EXPERIMENTAL_NLARGEST\ntemplate <typename Matrix, typename Vector, int BLOCKSIZE, int THREADS_PER_VECTOR>\nvoid truncateNandScale(const Matrix &A, Matrix &A_trunc, const int max_elmts, const Vector &orig_row_sums)\n{\n    const int nthreads = BLOCKSIZE;\n    const int nblocks = std::min(4096, A.get_num_rows() / (nthreads / THREADS_PER_VECTOR) + 1);\n    truncateNandScale_kernel<typename Matrix::index_type, typename Matrix::value_type,\n                             typename Vector::value_type, BLOCKSIZE, THREADS_PER_VECTOR>\n                             <<< nblocks, nthreads>>>(\n                                 A.row_offsets.raw(),\n                                 A.col_indices.raw(),\n                                 A.values.raw(),\n                                 A.get_num_rows(),\n                                 A_trunc.row_offsets.raw(),\n                                 A_trunc.col_indices.raw(),\n                                 A_trunc.values.raw(),\n                                 max_elmts,\n                                 orig_row_sums.raw()\n                             );\n    cudaCheckError();\n}\n#endif\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByMaxElements(Matrix_d &A,\n        const int max_elmts)\n{\n    if (max_elmts > 32)   // not supported\n    {\n        FatalError(\"Matrix truncation to > 32 elements not supported\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename TConfig_d::IndPrec index_type;\n    typedef typename TConfig_d::VecPrec vec_value_type;\n    typedef typename TConfig_d::MatPrec value_type;\n    // store the weighting values per row\n    typedef Vector<TConfig_d> VVector;\n    typedef typename Matrix<TConfig_d>::MVector MVector;\n    typedef Vector<typename TConfig_d::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    IVector row_lengths(A.get_num_rows());\n    MVector orig_row_sums(A.get_num_rows(), 0);\n    MVector new_row_sums(A.get_num_rows(), 0);\n    const int blocksize = 128;\n    const int num_blocks = A.get_num_rows() / blocksize + 1;\n\n    // get the original row sums of A\n    row_sum_kernel <<< 4096, blocksize>>>(A.row_offsets.raw(), A.values.raw(), orig_row_sums.raw(), A.get_num_rows(), row_lengths.raw(), max_elmts, true);\n    cudaCheckError();\n    // initial definition\n    Matrix_d A_trunc(A.get_num_rows(), A.get_num_cols(), 0, CSR); // add CSR prop\n    // exclusive scan to get new row structure\n    thrust_wrapper::exclusive_scan<IVector::TConfig::memSpace>(row_lengths.begin(), row_lengths.end(), A_trunc.row_offsets.begin());\n    int nnz = A_trunc.row_offsets[A.get_num_rows() - 1] + row_lengths[A.get_num_rows() - 1];\n    A_trunc.row_offsets[A.get_num_rows()] = nnz;\n    // set final size of truncated matrix\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), nnz);\n#ifdef USE_EXPERIMENTAL_NLARGEST\n\n    // run kernel with appropriate vector size\n    if (max_elmts > 16)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 32>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else if (max_elmts > 8)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 16>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else if (max_elmts > 4)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 192, 8>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 4>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n\n#else\n    cudaCheckError();\n\n    truncate_kernel <<< 4096, blocksize>>>(\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        A.values.raw(),\n        A.get_num_rows(),\n        A_trunc.row_offsets.raw(),\n        A_trunc.col_indices.raw(),\n        A_trunc.values.raw(),\n        max_elmts\n    );\n    cudaCheckError();\n    // get the new row sums of A_trunc\n    row_sum_kernel <<< 4096, blocksize>>>(A_trunc.row_offsets.raw(), A_trunc.values.raw(), new_row_sums.raw(), A_trunc.get_num_rows(), (index_type *) NULL, 0, false);\n    cudaCheckError();\n    A_trunc.addProps(COO);\n    scale_kernel <<< 4096, blocksize>>>(A_trunc.row_indices.raw(), A_trunc.values.raw(), new_row_sums.raw(), orig_row_sums.raw(), nnz);\n    cudaCheckError();\n#endif\n    cudaCheckError();\n    A_trunc.delProps(COO);\n    A.set_initialized(0);\n    A.copy(A_trunc);\n    A.computeDiagonal();\n    A.set_initialized(1);\n}\n\n// -------------------------------\n//  Explict instantiations\n// -------------------------------\n\n#define AMGX_CASE_LINE(CASE) template class Truncate<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <truncate.h>\n#include <specific_spmv.h>\n#include <basic_types.h>\n#include <util.h>\n#include <algorithm>\n#include <thrust_wrapper.h>\n\n#include \"amgx_types/util.h\"\n\n#define USE_EXPERIMENTAL_FACTOR_COPY\n// #define USE_EXPERIMENTAL_NLARGEST\n\nnamespace amgx\n{\n\n__host__ __device__ __forceinline__\ndouble ourHash2(const int i)\n{\n    unsigned int a = i;\n    double result;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    result = (a ^ 0x4a51e590) / (double) UINT_MAX;\n    return result;\n}\n\ntemplate <typename IndexType, typename metricType, typename matrixType>\nstruct copy_pred\n{\n    double _trunc_factor;\n    metricType *_metric_arr;\n\n    copy_pred() : _trunc_factor(0), _metric_arr(NULL) {};\n    copy_pred(const double trunc_factor, metricType *metric_arr) : _trunc_factor(trunc_factor),\n        _metric_arr(metric_arr) {};\n\n    __host__ __device__\n    bool operator()(const amgx::thrust::tuple<IndexType, IndexType, matrixType> &a)\n    {\n        metricType metric = _metric_arr[amgx::thrust::get<0>(a)];\n\n        if (  types::util<matrixType>::abs(amgx::thrust::get<2>(a)) >= _trunc_factor * metric) { return true; }\n\n        return false;\n    }\n\n    copy_pred<IndexType, metricType, matrixType> &operator=(const copy_pred<IndexType, metricType, matrixType> &a)\n    {\n        this->_trunc_factor = a._trunc_factor;\n        this->_metric_arr = a._metric_arr;\n        return *this;\n    }\n};\n\ntemplate <typename IndexType, typename VectorType, typename MatrixType>\nstruct scale_op\n{\n    const VectorType *scale_vec;\n\n    scale_op(const VectorType *s) : scale_vec(s) {};\n\n    __host__ __device__\n    amgx::thrust::tuple<IndexType, MatrixType> operator()(const amgx::thrust::tuple<IndexType, MatrixType> &a)\n    {\n        const IndexType row = amgx::thrust::get<0>(a);\n        return amgx::thrust::tuple<IndexType, MatrixType>(row, amgx::thrust::get<1>(a) * scale_vec[row]);\n    }\n};\n\ntemplate <typename index_type, typename value_type>\n__global__\nvoid scale_kernel(const index_type *row_indices, value_type *values, const value_type *new_row_sums, const value_type *old_row_sums, const int size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int row_index = row_indices[tidx];\n        value_type multiplier = ( types::util<value_type>::abs(new_row_sums[row_index]) == 0. ) ? types::util<value_type>::get_one() : (old_row_sums[row_index] / new_row_sums[row_index]);\n        values[tidx] = values[tidx] * multiplier;\n\n        if (values[tidx] != values[tidx]) { printf(\"%d (%d): NaN after scaling\\n\", tidx, row_index); }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid get_row_lengths_kernel(const IndexType *offsets, const IndexType num_rows, IndexType *row_lengths,\n                            const int max_elmts)\n{\n    for (int row = threadIdx.x + blockIdx.x * blockDim.x; row < num_rows; row += blockDim.x * gridDim.x)\n    {\n        row_lengths[row] = min(offsets[row + 1] - offsets[row], max_elmts);\n    }\n}\n\n// sort array by abs(values), largest element in [0]\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid sortByFabs(IndexType *indices, ValueType *values, int elements)\n{\n    int n = elements;\n\n    do\n    {\n        int newn = 0;\n\n        for (int i = 1; i < n; i++)\n        {\n            if (types::util<ValueType>::abs(values[i - 1]) < types::util<ValueType>::abs(values[i]))\n            {\n                ValueType temp_v = values[i - 1];\n                IndexType temp_i = indices[i - 1];\n                values[i - 1] = values[i];\n                values[i] = temp_v;\n                indices[i - 1] = indices[i];\n                indices[i] = temp_i;\n                newn = i;\n            }\n        }\n\n        n = newn;\n    }\n    while (n > 0);\n}\n\n#ifdef USE_EXPERIMENTAL_NLARGEST\n\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid insert_new_max(volatile ValueType *Mv, volatile IndexType *Mi, volatile int *T, const ValueType S,\n                    const IndexType I, const int thread, const int N)\n{\n    if (thread < N)\n    {\n        T[thread] = 1;\n\n        // new value larger - shift my value down\n        if (Mv[thread] < S)\n        {\n            T[thread] = 0;\n            Mv[thread + 1] = Mv[thread];\n            Mi[thread + 1] = Mi[thread];\n            T[thread + 1]  = 1;\n        }\n\n        // insert new largest value here\n        if (!T[thread])\n        {\n            Mv[thread] = S;\n            Mi[thread] = I;\n            // inserted to me -- note to zero out\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType, typename VecValueType,\n          int BLOCKSIZE, int THREADS_PER_VECTOR>\n__global__\nvoid truncateNandScale_kernel(const IndexType *A_offsets, const IndexType *A_indices, const ValueType *A_values,\n                              const IndexType num_rows,\n                              const IndexType *At_offsets, IndexType *At_indices, ValueType *At_values,\n                              const int max_elmts, const VecValueType *old_row_sums)\n{\n    const int WARP_SIZE = 32;\n    const int VECTORS_PER_WARP = WARP_SIZE / THREADS_PER_VECTOR;\n    const int WARPS_PER_BLOCK  = BLOCKSIZE / WARP_SIZE;\n    const int N_LARGEST_SIZE   = (THREADS_PER_VECTOR + 1) * VECTORS_PER_WARP * WARPS_PER_BLOCK;\n    __shared__ volatile int T[N_LARGEST_SIZE]; // has M array been modified\n    __shared__ volatile ValueType Mv[N_LARGEST_SIZE]; // maximum values\n    __shared__ volatile int Mi[N_LARGEST_SIZE]; // maximum indices\n    __shared__ volatile ValueType S[WARP_SIZE * WARPS_PER_BLOCK]; // temporary holding for currently read values\n    __shared__ volatile IndexType I[WARP_SIZE * WARPS_PER_BLOCK]; //               -- \" --                indices\n    ValueType S_copy;\n    IndexType I_copy;\n    __shared__ volatile int Ap[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // store row offset pointers\n    __shared__ volatile ValueType old_row_sum[WARPS_PER_BLOCK * VECTORS_PER_WARP];\n    const int num_vectors = blockDim.x / THREADS_PER_VECTOR * gridDim.x;\n    const int vector_id = (threadIdx.x + blockIdx.x * blockDim.x) / THREADS_PER_VECTOR;\n    const int thread_lane = threadIdx.x & (THREADS_PER_VECTOR - 1);\n    const int vector_lane = threadIdx.x / THREADS_PER_VECTOR;\n\n    // initialise relevant shared memory arrays\n    for (int i = threadIdx.x; i < N_LARGEST_SIZE; i += blockDim.x)\n    {\n        Mv[i] = 0.;\n        Mi[i] = -1;\n        T[i] = 0;\n    }\n\n    __syncthreads();\n\n    // outer loop over rows\n    for (int row = vector_id; row < num_rows; row += num_vectors)\n    {\n        // load row offsets into shared memory\n        if (thread_lane < 2)\n        {\n            Ap[vector_lane][thread_lane] = A_offsets[row + thread_lane];\n        }\n\n        const int row_start = Ap[vector_lane][0];\n        const int row_end   = Ap[vector_lane][1];\n        const int row_len   = row_end - row_start;\n        const int max_elmts_row = min(At_offsets[row + 1] - At_offsets[row], max_elmts);\n\n        // degenerate case -- simply copy over entire row\n        if (row_len <= max_elmts && row_len > 0 && thread_lane < row_len)\n        {\n            At_indices[At_offsets[row] + thread_lane] = A_indices[row_start + thread_lane];\n            At_values[At_offsets[row] + thread_lane] = A_values[row_start + thread_lane];\n            continue;\n        }\n\n        // loop over chunks of the row\n        const int nloops = (row_len < THREADS_PER_VECTOR) ? 1 : (int)ceil((float)row_len / THREADS_PER_VECTOR);\n\n        for (int i = 0; i < nloops; i++)\n        {\n            // zero out temp shared memory\n            S[threadIdx.x] =  0;\n            I[threadIdx.x] = -2;\n\n            // read chunk into shared memory\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                S[threadIdx.x] = fabs(A_values[row_start + i * THREADS_PER_VECTOR + thread_lane]);\n                I[threadIdx.x] = A_indices[row_start + i * THREADS_PER_VECTOR + thread_lane];\n            }\n\n            // loop over n' max elements\n            for (int j = 0; j < max_elmts_row; j++)\n            {\n                // first take a copy of this section of the row so we don't re-read it\n                S_copy = S[threadIdx.x];\n                I_copy = I[threadIdx.x];\n                // local max & associated index (save volatile loads)\n                ValueType max = S_copy; // S[threadIdx.x];\n                IndexType ind = I_copy; // I[threadIdx.x];\n\n                // reduce to get max\n                if (THREADS_PER_VECTOR > 16)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x + 16] ? ind : I[threadIdx.x + 16];\n                    S[threadIdx.x] = max = max > S[threadIdx.x + 16] ? max : S[threadIdx.x + 16];\n                }\n\n                if (THREADS_PER_VECTOR > 8)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  8] ? ind : I[threadIdx.x +  8];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  8] ? max : S[threadIdx.x +  8];\n                }\n\n                if (THREADS_PER_VECTOR > 4)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  4] ? ind : I[threadIdx.x +  4];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  4] ? max : S[threadIdx.x +  4];\n                }\n\n                if (THREADS_PER_VECTOR > 2)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  2] ? ind : I[threadIdx.x +  2];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  2] ? max : S[threadIdx.x +  2];\n                }\n\n                if (THREADS_PER_VECTOR >  1)\n                {\n                    I[threadIdx.x] = ind = max > S[threadIdx.x +  1] ? ind : I[threadIdx.x +  1];\n                    S[threadIdx.x] = max = max > S[threadIdx.x +  1] ? max : S[threadIdx.x +  1];\n                }\n\n                // maximum now in S_copy[vector_lane*32] -- insert into maximum array\n                const int sm_offset = vector_lane * (THREADS_PER_VECTOR + 1);\n                insert_new_max(&Mv[sm_offset], &Mi[sm_offset], &T[sm_offset],\n                               S[vector_lane * THREADS_PER_VECTOR], I[vector_lane * THREADS_PER_VECTOR], thread_lane,\n                               max_elmts);\n                S[threadIdx.x] = (I[vector_lane * THREADS_PER_VECTOR] == I_copy) ? 0 : S_copy;\n                I[threadIdx.x] = I_copy;\n            }\n        }\n\n        // get scaling factor for new row\n        // copy Mv -> S\n        S[threadIdx.x] = 0; // intialise unneeded S array to 0\n\n        if (thread_lane < max_elmts_row)\n        {\n            S[threadIdx.x] = Mv[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane];\n        }\n\n        // reduce on S\n        ValueType sum = fabs(S[threadIdx.x]);\n\n        if (THREADS_PER_VECTOR > 16) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 16]; }\n\n        if (THREADS_PER_VECTOR >  8) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 8]; }\n\n        if (THREADS_PER_VECTOR >  4) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 4]; }\n\n        if (THREADS_PER_VECTOR >  2) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 2]; }\n\n        if (THREADS_PER_VECTOR >  1) { S[threadIdx.x] = sum = sum + S[threadIdx.x + 1]; }\n\n        const ValueType scale = old_row_sums[row] / S[vector_lane * THREADS_PER_VECTOR];\n\n        // copy final scaled results into output buffer\n        if (thread_lane < max_elmts_row)\n        {\n            At_values[At_offsets[row] + thread_lane]  = Mv[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane] * scale;\n            At_indices[At_offsets[row] + thread_lane] = Mi[vector_lane * (THREADS_PER_VECTOR + 1) + thread_lane];\n        }\n    }\n}\n\n#else\n// sort array by column index\ntemplate <typename IndexType, typename ValueType>\n__device__\nvoid sortByIndex(IndexType *indices, ValueType *values, int elements)\n{\n    int n = elements;\n\n    do\n    {\n        int newn = 0;\n\n        for (int i = 1; i < n; i++)\n        {\n            if (indices[i - 1] > indices[i])\n            {\n                ValueType temp_v = values[i - 1];\n                IndexType temp_i = indices[i - 1];\n                values[i - 1] = values[i];\n                values[i] = temp_v;\n                indices[i - 1] = indices[i];\n                indices[i] = temp_i;\n                newn = i;\n            }\n        }\n\n        n = newn;\n    }\n    while (n > 0);\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid row_sum_kernel(const IndexType *A_offsets, const ValueType *A_values,\n                    ValueType *row_sums, const IndexType num_rows, IndexType *row_lengths, const int max_elmts, bool compute_row_length)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n\n        if (compute_row_length)\n        {\n            row_lengths[tidx] = min(A_end - A_start, max_elmts);\n        }\n\n        ValueType row_sum = 0.;\n        bool found_nans = false;\n\n        for (int i = A_start; i < A_end; i++)\n        {\n            row_sum += A_values[i];\n\n            if (A_values[i] != A_values[i]) { found_nans = true; }\n        }\n\n        if (found_nans)\n        {\n            printf(\"row %d has NaNs in input\\n\", tidx);\n        }\n\n        row_sums[tidx] = row_sum;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid __launch_bounds__(256)\ntruncate_kernel(const IndexType *A_offsets, const IndexType *A_col_indices, const ValueType *A_values,\n                const IndexType num_rows,\n                IndexType *trunc_offsets, IndexType *trunc_col_indices, ValueType *trunc_values,\n                const int max_elmts)\n{\n    // const int tidx = threadIdx.x + blockIdx.x*blockDim.x;\n    __shared__ IndexType s_ind[1024]; // assume <= 256 threads per block\n    __shared__ ValueType s_val[1024];\n    const int smem_start = threadIdx.x * max_elmts;\n\n    // if (tidx < num_rows)\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n        IndexType trunc_start = trunc_offsets[tidx];\n        IndexType A_len = A_end - A_start;\n\n        // copy my entire row if possible\n        if (A_len <= max_elmts)\n        {\n            for (int j = 0; j < A_len; j++)\n            {\n                trunc_col_indices[trunc_start + j] = A_col_indices[A_start + j];\n                trunc_values[trunc_start + j] = A_values[A_start + j];\n            }\n        }\n        else // normal case\n        {\n            // read first max_elmts elements into shared\n            for (int j = 0; j < max_elmts; j++)\n            {\n                s_ind[smem_start + j] = A_col_indices[A_start + j];\n                s_val[smem_start + j] = A_values[A_start + j];\n            }\n\n            // sort the initially inserted elements (start = largest)\n            sortByFabs(&s_ind[smem_start], &s_val[smem_start], max_elmts);\n\n            // loop over all other elements on the row\n            for (int j = A_start + max_elmts; j < A_end; j++)\n            {\n                // check against current maximums\n                for (int i = 0; i < max_elmts; i++)\n                {\n                    if (types::util<ValueType>::abs(A_values[j]) > types::util<ValueType>::abs(s_val[smem_start + i]))\n                    {\n                        // shift remaining values down\n                        for (int k = max_elmts - 1; k > i; k--)\n                        {\n                            s_val[smem_start + k] = s_val[smem_start + k - 1];\n                            s_ind[smem_start + k] = s_ind[smem_start + k - 1];\n                        }\n\n                        // tentatively replace element\n                        s_val[smem_start + i] = A_values[j];\n                        s_ind[smem_start + i] = A_col_indices[j];\n                        break;\n                    }\n                }\n            }\n\n            //sortByIndex(&s_ind[smem_start],&s_val[smem_start],max_elmts);\n            // now we have the 4 largest elements (hopefully) -- output\n            for (int i = 0; i < max_elmts; i++)\n            {\n                trunc_col_indices[trunc_start + i] = s_ind[smem_start + i];\n                trunc_values[trunc_start + i] = s_val[smem_start + i];\n            }\n        }\n    }\n}\n#endif\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid perturb_kernel(const IndexType *A_offsets, const IndexType *A_col_indices, ValueType *A_values,\n                    const IndexType num_rows,\n                    const int max_elmts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows; tidx += blockDim.x * gridDim.x)\n    {\n        IndexType A_start = A_offsets[tidx], A_end = A_offsets[tidx + 1];\n\n        // perturb A slightly\n        for (int j = A_start; j < A_end; j++)\n        {\n            A_values[j] += ourHash2(A_col_indices[j]) * A_values[j];\n        }\n    }\n}\n\n\n// warp-based inclusive scan\n// from: Sengupta, Harris, Garland 2008\n// idx is index of thread\ntemplate <typename IndexType>\n__device__\nint scan_warp(volatile IndexType *ptr, const unsigned int idx)\n{\n    const unsigned int lane = idx & 31; // index of thread in warp\n    IndexType p = ptr[idx];\n\n    if (lane >=  1) { ptr[idx] = p = ptr[idx -  1] + p; }\n\n    if (lane >=  2) { ptr[idx] = p = ptr[idx -  2] + p; }\n\n    if (lane >=  4) { ptr[idx] = p = ptr[idx -  4] + p; }\n\n    if (lane >=  8) { ptr[idx] = p = ptr[idx -  8] + p; }\n\n    if (lane >= 16) { ptr[idx] = p = ptr[idx - 16] + p; }\n\n    return ptr[idx];\n}\n\ntemplate <typename IndexType, typename matValueType, typename vecValueType, int THREADS_PER_BLOCK,\n          int THREADS_PER_VECTOR>\n__global__\nvoid truncateAndScale_kernel(const IndexType *A_offsets, const IndexType *A_indices,\n                             const matValueType *A_values,\n                             const IndexType num_rows,\n                             IndexType *At_offsets, IndexType *At_indices, matValueType *At_values,\n                             const vecValueType *old_row_sums, const vecValueType *new_row_sums,\n                             const double truncate_factor, const vecValueType *metric)\n{\n    const int WARP_SIZE = 32;\n    const int WARPS_PER_BLOCK = THREADS_PER_BLOCK / WARP_SIZE;\n    const int VECTORS_PER_WARP = WARP_SIZE / THREADS_PER_VECTOR;\n    // allocate shared memory for warp-based scans (assume up to 512 threads / block)\n    __shared__ volatile int s_scan[WARP_SIZE * WARPS_PER_BLOCK];\n    __shared__ volatile int s_A_ptr[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // row offsets for A\n    __shared__ volatile int s_At_ptr[WARPS_PER_BLOCK * VECTORS_PER_WARP][2]; // row offsets for At\n    __shared__ double s_scale[WARPS_PER_BLOCK * VECTORS_PER_WARP]; // scaling factor for each warp\n    const int vectors_per_block = THREADS_PER_BLOCK / WARP_SIZE * VECTORS_PER_WARP;\n    const int num_vectors = THREADS_PER_BLOCK / THREADS_PER_VECTOR * gridDim.x;\n    const int vector_id = (threadIdx.x + blockIdx.x * blockDim.x) / THREADS_PER_VECTOR;\n    const int thread_lane = threadIdx.x & (THREADS_PER_VECTOR - 1);\n    const int vector_lane = threadIdx.x / THREADS_PER_VECTOR;\n    int scan_offset = 0;\n    matValueType val = 0., fval = 0.;\n    IndexType ind = 0;\n\n    // outer loop over rows\n    for (int row = vector_id; row < num_rows; row += num_vectors)\n    {\n        scan_offset = 0;\n        val = 0.;\n        fval = 0.; // fabs(val)\n\n        if (threadIdx.x < vectors_per_block && threadIdx.x < num_rows - row) // first threads in first warp\n        {\n            s_scale[threadIdx.x] = old_row_sums[row + threadIdx.x] / new_row_sums[row + threadIdx.x];\n        }\n\n        __syncthreads();\n\n        if (thread_lane < 2)\n        {\n            s_A_ptr[vector_lane][thread_lane] = A_offsets[row + thread_lane];\n            s_At_ptr[vector_lane][thread_lane] = At_offsets[row + thread_lane];\n        }\n\n        const int row_start = s_A_ptr[vector_lane][0];\n        const int row_end   = s_A_ptr[vector_lane][1];\n        const int row_len = row_end - row_start;\n        const int At_row_start = s_At_ptr[vector_lane][0];\n        // mark & scan over consecutive blocks of 32 threads\n        int nloops = (int)ceilf((float)row_len / THREADS_PER_VECTOR);\n#pragma unroll 2\n\n        for (int i = 0; i < nloops; i++)\n        {\n            // initialise shared memory to 0\n            s_scan[threadIdx.x] = 0;\n\n            // mark appropriate elements of the row\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                val = A_values[row_start + i * THREADS_PER_VECTOR + thread_lane];\n                ind = A_indices[row_start + i * THREADS_PER_VECTOR + thread_lane];\n                fval = fabs(val);\n                s_scan[threadIdx.x] = fval >= truncate_factor * metric[row] ? 1 : 0;\n            }\n\n            // now prefix scan over this\n            scan_warp(&s_scan[vector_lane * THREADS_PER_VECTOR], thread_lane); // threadIdx.x%32);\n\n            // copy relevant parts of A to At\n            if (i * THREADS_PER_VECTOR + thread_lane < row_len)\n            {\n                if (fval >= truncate_factor * metric[row])\n                {\n                    // get relevant index in At\n                    const int At_index = At_row_start + scan_offset + s_scan[threadIdx.x] - 1;\n\n                    if (s_scale[vector_lane] == 0.)\n                    {\n                        At_values[At_index] = val;\n                    }\n                    else\n                    {\n                        At_values[At_index] = val / s_scale[vector_lane];\n                    }\n\n                    At_indices[At_index] = ind;\n                }\n            }\n\n            // note new scan_offset\n            scan_offset = s_scan[vector_lane * THREADS_PER_VECTOR + THREADS_PER_VECTOR - 1];\n        }\n    }\n}\n\ntemplate <typename Matrix, typename Vector, int THREADS_PER_BLOCK, int THREADS_PER_VECTOR>\nvoid truncateAndScale(const Matrix &A, Matrix &A_trunc, const Vector &row_sum, const Vector &new_row_sum,\n                      const double trunc_factor, const Vector &metric)\n{\n    const int num_blocks = std::min(4096, A.get_num_rows());\n    truncateAndScale_kernel<typename Matrix::index_type, typename Matrix::value_type,\n                            typename Vector::value_type, THREADS_PER_BLOCK, THREADS_PER_VECTOR>\n                            <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                                    A.get_num_rows(),\n                                    A_trunc.row_offsets.raw(), A_trunc.col_indices.raw(),\n                                    A_trunc.values.raw(),\n                                    row_sum.raw(), new_row_sum.raw(),\n                                    trunc_factor, metric.raw());\n    cudaCheckError();\n}\n\n// host code\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_h &A,\n        const double trunc_factor, const AMGX_TruncateType truncType)\n{\n    // throw error\n    FatalError(\"Truncation on host not implemented yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::truncateByMaxElements(Matrix_h &A,\n        const int max_elmts)\n{\n    // throw error\n    FatalError(\"Truncation on host not implemented yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n// device code\n#ifdef _WIN32\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_d &A, const double trunc_factor, const AMGX_TruncateType truncType)\n#else\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByFactor(Matrix_d &A, const double trunc_factor, const AMGX_TruncateType truncType) __attribute__((noinline))\n#endif\n{\n    typedef typename TConfig_d::IndPrec index_type;\n    typedef typename TConfig_d::VecPrec vec_value_type;\n    typedef typename TConfig_d::MatPrec value_type;\n    // store the weighting values per row\n    Matrix<TConfig_d> A_trunc(0, 0, 0, CSR);\n    typedef Vector<TConfig_d> VVector;\n    typedef typename Matrix<TConfig_d>::MVector MVector;\n    typedef Vector<typename TConfig_d::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    VVector row_sum(A.get_num_rows());\n    VVector new_row_sum(A.get_num_rows());\n    VVector max_coef;\n    // point to either row_sum or max_coef depending on type of truncation desired\n    // vec_value_type *metric = 0;\n    VVector *metric;\n\n    // get either max row coefficient or row sum\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        absRowSum(A, row_sum);\n        metric = &row_sum;\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        max_coef.resize(A.get_num_rows());\n        maxCoefAndSum(A, max_coef, row_sum);\n        metric = &max_coef;\n    }\n    else\n    {\n        FatalError(\"Truncation type not implemented\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IVector row_counts(A.get_num_rows());\n\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        countTruncElements(A, trunc_factor, row_sum, row_counts, new_row_sum);\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        countTruncElements(A, trunc_factor, max_coef, row_counts, new_row_sum);\n    }\n\n    cudaCheckError();\n    // initial resize (so we can scan into the row_offsets array)\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), 0);\n    cudaCheckError();\n\n    thrust_wrapper::exclusive_scan<IVector::TConfig::memSpace>(row_counts.begin(), row_counts.end(), A_trunc.row_offsets.begin());\n    cudaCheckError();\n    \n    const int nnz = A_trunc.row_offsets[A.get_num_rows() - 1] + row_counts[A.get_num_rows() - 1];\n    A_trunc.row_offsets[A.get_num_rows()] = nnz;\n\n    if (nnz == A.get_num_nz()) // early return -- nothing truncated\n    {\n        return;\n    }\n\n    // final resize & get row indices for both matrices\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), nnz);\n#ifndef USE_EXPERIMENTAL_FACTOR_COPY\n    A_trunc.addProps(COO);\n    A.addProps(COO);\n    // copy relevant values from A -> A_trunc\n    copy_pred<index_type, vec_value_type, value_type> pred;\n\n    if (truncType == AMGX_TruncateByRowSum)\n    {\n        pred = copy_pred<index_type, vec_value_type, value_type>(trunc_factor, row_sum.raw());\n    }\n    else if (truncType == AMGX_TruncateByMaxCoefficient)\n    {\n        pred = copy_pred<index_type, vec_value_type, value_type>(trunc_factor, max_coef.raw());\n    }\n\n    using amgx::thrust::make_zip_iterator;\n    using amgx::thrust::make_tuple;\n    amgx::thrust::copy_if(\n        make_zip_iterator(make_tuple(A.row_indices.begin(), A.col_indices.begin(), A.values.begin())),\n        make_zip_iterator(make_tuple(A.row_indices.end(), A.col_indices.end(), A.values.end())),\n        make_zip_iterator(make_tuple(A_trunc.row_indices.begin(), A_trunc.col_indices.begin(),\n                                     A_trunc.values.begin())),\n        pred\n    );\n    // scale each row of the truncated matrix - per-row scale in new_row_sum\n    amgx::thrust::transform(row_sum.begin(), row_sum.end(), new_row_sum.begin(), new_row_sum.begin(),\n                      amgx::thrust::divides<vec_value_type>());\n    scale_op<index_type, vec_value_type, value_type> op(new_row_sum.raw());\n    amgx::thrust::for_each(\n        make_zip_iterator(make_tuple(A_trunc.row_indices.begin(), A_trunc.values.begin())),\n        make_zip_iterator(make_tuple(A_trunc.row_indices.end(), A_trunc.values.end())),\n        op\n    );\n#else\n    const int avg_row_len = (int)ceil(sqrt((float)A.get_num_nz() / A.get_num_rows()));\n\n    if (avg_row_len > 16)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 32>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else if (avg_row_len > 8)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 16>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else if (avg_row_len > 4)\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 8>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n    else\n    {\n        truncateAndScale<Matrix<TConfig>, Vector<TConfig>, 128, 4>(A, A_trunc, row_sum, new_row_sum,\n                trunc_factor, *metric);\n    }\n\n#endif\n    cudaCheckError();\n    // copy truncated matrix to this\n    A.copy(A_trunc);\n}\n\n#ifdef USE_EXPERIMENTAL_NLARGEST\ntemplate <typename Matrix, typename Vector, int BLOCKSIZE, int THREADS_PER_VECTOR>\nvoid truncateNandScale(const Matrix &A, Matrix &A_trunc, const int max_elmts, const Vector &orig_row_sums)\n{\n    const int nthreads = BLOCKSIZE;\n    const int nblocks = std::min(4096, A.get_num_rows() / (nthreads / THREADS_PER_VECTOR) + 1);\n    truncateNandScale_kernel<typename Matrix::index_type, typename Matrix::value_type,\n                             typename Vector::value_type, BLOCKSIZE, THREADS_PER_VECTOR>\n                             <<< nblocks, nthreads>>>(\n                                 A.row_offsets.raw(),\n                                 A.col_indices.raw(),\n                                 A.values.raw(),\n                                 A.get_num_rows(),\n                                 A_trunc.row_offsets.raw(),\n                                 A_trunc.col_indices.raw(),\n                                 A_trunc.values.raw(),\n                                 max_elmts,\n                                 orig_row_sums.raw()\n                             );\n    cudaCheckError();\n}\n#endif\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Truncate<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::truncateByMaxElements(Matrix_d &A,\n        const int max_elmts)\n{\n    if (max_elmts > 32)   // not supported\n    {\n        FatalError(\"Matrix truncation to > 32 elements not supported\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename TConfig_d::IndPrec index_type;\n    typedef typename TConfig_d::VecPrec vec_value_type;\n    typedef typename TConfig_d::MatPrec value_type;\n    // store the weighting values per row\n    typedef Vector<TConfig_d> VVector;\n    typedef typename Matrix<TConfig_d>::MVector MVector;\n    typedef Vector<typename TConfig_d::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    IVector row_lengths(A.get_num_rows());\n    MVector orig_row_sums(A.get_num_rows(), 0);\n    MVector new_row_sums(A.get_num_rows(), 0);\n    const int blocksize = 128;\n    const int num_blocks = A.get_num_rows() / blocksize + 1;\n\n    // get the original row sums of A\n    row_sum_kernel <<< 4096, blocksize>>>(A.row_offsets.raw(), A.values.raw(), orig_row_sums.raw(), A.get_num_rows(), row_lengths.raw(), max_elmts, true);\n    cudaCheckError();\n    // initial definition\n    Matrix_d A_trunc(A.get_num_rows(), A.get_num_cols(), 0, CSR); // add CSR prop\n    // exclusive scan to get new row structure\n    thrust_wrapper::exclusive_scan<IVector::TConfig::memSpace>(row_lengths.begin(), row_lengths.end(), A_trunc.row_offsets.begin());\n    int nnz = A_trunc.row_offsets[A.get_num_rows() - 1] + row_lengths[A.get_num_rows() - 1];\n    A_trunc.row_offsets[A.get_num_rows()] = nnz;\n    // set final size of truncated matrix\n    A_trunc.resize(A.get_num_rows(), A.get_num_cols(), nnz);\n#ifdef USE_EXPERIMENTAL_NLARGEST\n\n    // run kernel with appropriate vector size\n    if (max_elmts > 16)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 32>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else if (max_elmts > 8)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 16>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else if (max_elmts > 4)\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 192, 8>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n    else\n    {\n        truncateNandScale<Matrix<TConfig_d>, Vector<TConfig_d>, 128, 4>(A, A_trunc, max_elmts, orig_row_sums);\n    }\n\n#else\n    cudaCheckError();\n\n    truncate_kernel <<< 4096, blocksize>>>(\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        A.values.raw(),\n        A.get_num_rows(),\n        A_trunc.row_offsets.raw(),\n        A_trunc.col_indices.raw(),\n        A_trunc.values.raw(),\n        max_elmts\n    );\n    cudaCheckError();\n    // get the new row sums of A_trunc\n    row_sum_kernel <<< 4096, blocksize>>>(A_trunc.row_offsets.raw(), A_trunc.values.raw(), new_row_sums.raw(), A_trunc.get_num_rows(), (index_type *) NULL, 0, false);\n    cudaCheckError();\n    A_trunc.addProps(COO);\n    scale_kernel <<< 4096, blocksize>>>(A_trunc.row_indices.raw(), A_trunc.values.raw(), new_row_sums.raw(), orig_row_sums.raw(), nnz);\n    cudaCheckError();\n#endif\n    cudaCheckError();\n    A_trunc.delProps(COO);\n    A.set_initialized(0);\n    A.copy(A_trunc);\n    A.computeDiagonal();\n    A.set_initialized(1);\n}\n\n// -------------------------------\n//  Explict instantiations\n// -------------------------------\n\n#define AMGX_CASE_LINE(CASE) template class Truncate<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/truncate.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <error.h>\n#include <global_thread_handle.h>\n\n__global__ void profileLevelUp_kernel() {}\n__global__ void profileLevelDown_kernel() {}\n__global__ void profileLevelZero_kernel() {}\n__global__ void profilePhaseSetup_kernel() {}\n__global__ void profilePhaseSolve_kernel() {}\n__global__ void profilePhaseNone_kernel() {}\n__global__ void profileSubphaseMatrixColoring_kernel() {}\n__global__ void profileSubphaseSmootherSetup_kernel() {}\n__global__ void profileSubphaseFindAggregates_kernel() {}\n__global__ void profileSubphaseComputeRestriction_kernel() {}\n__global__ void profileSubphaseComputeCoarseA_kernel() {}\n__global__ void profileSubphaseNone_kernel() {}\n__global__ void profileSubphaseTruncateP_kernel() {}\n\nnamespace amgx\n{\n\nvoid profileLevelUp()\n{\n    profileLevelUp_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profileLevelDown()\n{\n    profileLevelDown_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profileLevelZero()\n{\n    profileLevelZero_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseSetup()\n{\n    profilePhaseSetup_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseSolve()\n{\n    profilePhaseSolve_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseNone()\n{\n    profilePhaseNone_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseMatrixColoring()\n{\n    profileSubphaseMatrixColoring_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseSmootherSetup()\n{\n    profileSubphaseSmootherSetup_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseFindAggregates()\n{\n    profileSubphaseFindAggregates_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseComputeRestriction()\n{\n    profileSubphaseComputeRestriction_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseComputeCoarseA()\n{\n    profileSubphaseComputeCoarseA_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseNone()\n{\n    profileSubphaseNone_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseTruncateP()\n{\n    profileSubphaseTruncateP_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <error.h>\n#include <global_thread_handle.h>\n\n__global__ void profileLevelUp_kernel() {}\n__global__ void profileLevelDown_kernel() {}\n__global__ void profileLevelZero_kernel() {}\n__global__ void profilePhaseSetup_kernel() {}\n__global__ void profilePhaseSolve_kernel() {}\n__global__ void profilePhaseNone_kernel() {}\n__global__ void profileSubphaseMatrixColoring_kernel() {}\n__global__ void profileSubphaseSmootherSetup_kernel() {}\n__global__ void profileSubphaseFindAggregates_kernel() {}\n__global__ void profileSubphaseComputeRestriction_kernel() {}\n__global__ void profileSubphaseComputeCoarseA_kernel() {}\n__global__ void profileSubphaseNone_kernel() {}\n__global__ void profileSubphaseTruncateP_kernel() {}\n\nnamespace amgx\n{\n\nvoid profileLevelUp()\n{\n    profileLevelUp_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profileLevelDown()\n{\n    profileLevelDown_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profileLevelZero()\n{\n    profileLevelZero_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseSetup()\n{\n    profilePhaseSetup_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseSolve()\n{\n    profilePhaseSolve_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\nvoid profilePhaseNone()\n{\n    profilePhaseNone_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseMatrixColoring()\n{\n    profileSubphaseMatrixColoring_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseSmootherSetup()\n{\n    profileSubphaseSmootherSetup_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseFindAggregates()\n{\n    profileSubphaseFindAggregates_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseComputeRestriction()\n{\n    profileSubphaseComputeRestriction_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseComputeCoarseA()\n{\n    profileSubphaseComputeCoarseA_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseNone()\n{\n    profileSubphaseNone_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\nvoid profileSubphaseTruncateP()\n{\n    profileSubphaseTruncateP_kernel <<< 1, 1, 0, thrust::global_thread_handle::get_stream()>>>();\n    cudaCheckError();\n}\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/profile.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cusparse_v2.h>\n#include <error.h>\n#include <matrix.h>\n#include <vector.h>\n#include <types.h>\n#include <thrust/sequence.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <device_properties.h>\n\n#include <amgx_cusparse.h>\n\n#include <amgx_types/util.h>\n\n#if CUDART_VERSION < 11000\n#define CUSPARSE_SPMM_ALG_DEFAULT CUSPARSE_MM_ALG_DEFAULT\n#endif\n\n#if CUDART_VERSION >= 12000\n#define CUSPARSE_CSRMV_ALG1 CUSPARSE_SPMV_CSR_ALG1\n#define CUSPARSE_CSRMV_ALG2 CUSPARSE_SPMV_CSR_ALG2\n#endif\n\nnamespace amgx\n{\n\nCusparse::Cusparse() : m_handle(0), m_determinism_flag(false)\n{\n    cusparseCheckError( cusparseCreate(&m_handle) );\n}\n\nCusparse::~Cusparse()\n{\n    destroy_handle();\n}\n\nCusparse &Cusparse::get_instance()\n{\n    static Cusparse s_instance;\n    s_instance.create_handle();\n    return s_instance;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    ViewType view )\n{\n    cudaStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    // Handle cases where the view is set by the calling routine\n    if(view != A.getViewExterior())\n    {\n        bsrmv_internal(alphaConst, A, x, betaConst, y, view, null_stream);\n        return;\n    }\n\n    bool latencyHiding = (A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(x, x.tag);\n\n        // Multiply interior rows\n        bsrmv_internal(alphaConst, A, x, betaConst, y, A.getViewInterior(), null_stream);\n\n        // Finish halo exchange\n        A.manager->exchange_halo_split_finish(x, x.tag);\n\n        // Multiply rows with halo dependencies\n        ViewType bnd_view = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n        bsrmv_internal(alphaConst, A, x, betaConst, y, bnd_view, null_stream);\n    }\n    else\n    {\n        if (!A.is_matrix_singleGPU() && x.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(x, x.tag);\n        }\n\n        bsrmv_internal(alphaConst, A, x, betaConst, y, A.getViewExterior(), null_stream);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_with_mask(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y)\n{\n    cudaStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    bool latencyHiding = (A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(x, x.tag);\n\n        // Multiply interior\n        bsrmv_internal_with_mask(alphaConst, A, x, betaConst, y, INTERIOR, null_stream);\n\n        A.manager->exchange_halo_split_finish(x, x.tag);\n\n        // Multiply exterior\n        bsrmv_internal_with_mask(alphaConst, A, x, betaConst, y, BOUNDARY, null_stream);\n    }\n    else\n    {\n        if (!A.is_matrix_singleGPU() && x.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(x, x.tag);\n        }\n\n        bsrmv_internal(alphaConst, A, x, betaConst, y, OWNED, null_stream);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_with_mask_restriction(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &R,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    Matrix<TConfig> &P)\n{\n    // If only COO, add CSR since bsrmv doesn't support COO\n    //if (A.hasProps(COO) && !A.hasProps(CSR))\n    //{\n    //  A.set_initialized(0);\n    //  A.addProps(CSR);\n    //  A.computeDiagonal();\n    //  A.set_initialized(1);\n    //}\n\n    bool latencyHiding = (R.getViewInterior() != R.getViewExterior() && !P.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        cudaStream_t null_stream = 0;\n        bsrmv_internal_with_mask_restriction(alphaConst, R, x, betaConst, y, HALO1, null_stream, P);\n        P.manager->add_from_halo_split_gather(y, y.tag);\n        cudaError_t cuda_rc = cudaEventRecord(P.manager->get_comm_event());\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaEventRecord failed\", AMGX_ERR_CUDA_FAILURE);\n        }\n        bsrmv_internal_with_mask_restriction(alphaConst, R, x, betaConst, y, OWNED, null_stream, P);\n\n        if (P.manager->neighbors.size() != 0)\n        {\n            cuda_rc = cudaEventSynchronize(P.manager->get_comm_event());\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaEventSynchronize failed\", AMGX_ERR_CUDA_FAILURE);\n            }\n            P.manager->add_from_halo_split_finish(y, y.tag, P.manager->get_bdy_stream());\n            cuda_rc = cudaStreamSynchronize(P.manager->get_bdy_stream());\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaStreamSynchronize failed\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n    else\n    {\n        bsrmv_internal(alphaConst, R, x, betaConst, y, OWNED, 0);\n\n        // Add contribution from neighbors\n        P.manager->add_from_halo_v2(y, y.tag);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrxmv(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    typename Matrix<TConfig>::IVector &mask,\n    ViewType view )\n{\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    const int *start_offsets, *end_offsets;\n    start_offsets = A.row_offsets.raw();\n    end_offsets = A.row_offsets.raw() + 1;\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    if (view == OWNED)  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrxmv_internal(Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE,\n                        mask.size(),\n                        A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                        A.cuMatDescr,\n                        A.values.raw(),\n                        mask.raw(),\n                        start_offsets, end_offsets,\n                        A.col_indices.raw(),\n                        A.get_block_dimx(),\n                        x.raw(),\n                        &betaConst,\n                        y.raw());\n    }\n    else //Try and do latency hiding\n    {\n        // latency hiding?\n        FatalError(\"Trying to do latency hiding in the bsrxmv\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n// E is a vector that represents a diagonal matrix\n// operate on all rows and columns\n// y= alpha*E.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      const typename Matrix<TConfig>::MVector &E,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view )\n{\n    cudaStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior()) //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrmv_internal(alphaConst, A, E, x, betaConst, y, view, null_stream);\n    }\n    else  //Try and do latency hiding\n    {\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        ViewType flags;\n        bool latencyHiding = true;\n\n        //if (A.manager->num_neighbors() == 0 || (x.dirtybit == 0)) {\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU())\n        //  if (y.size() == x.size() && y.delayed_send==0) A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n// operate only on columns specified by columnColorSelector, see enum ColumnColorSelector above\n// operate only on rows of specified color, given by A.offsets_rows_per_color, A.sorted_rows_by_color\n// y= alpha*A.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( ColumnColorSelector columnColorSelector,\n                      const int color,\n                      const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view )\n{\n    cudaStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior())  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        // std::cerr << \"exterior view with selector\" << std::endl;\n        bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, view, null_stream);\n    }\n    else\n    {\n        //Try and do latency hiding\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        ViewType flags;\n        bool latencyHiding = true;\n\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU() && y.size() == x.size() && y.delayed_send==0)\n        //  A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n// E is a vector that represents a diagonal matrix\n// operate only on rows of specified color, given by A.offsets_rows_per_color, A.sorted_rows_by_color\n// y= alpha*E.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( const int color,\n                      const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      const typename Matrix<TConfig>::MVector &E,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view)\n{\n    cudaStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior())  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, view, null_stream);\n    }\n    else  //Try and do latency hiding\n    {\n        //std::ccooor << \"de\" << std::endl;\n        //std::cerr << \"not an exterior view\" << std::endl;\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            //std::cerr << \"exchange_halo_wait\" << std::endl;\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        //std::cerr << \"xxxeded\" << std::endl;\n        ViewType flags;\n        bool latencyHiding = true;\n\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU() && y.size() == x.size() && y.delayed_send==0)\n        //  A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n__global__ void offset_by_col_off(int nrows, int* rows, const int* bsrRowPtr)\n{\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if(i >= nrows+1)\n    {\n        return;\n    }\n\n    rows[i] = bsrRowPtr[i] - bsrRowPtr[0];\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const cudaStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueTypeB;\n    int rowOff, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    bool has_offdiag = nnz != 0;\n\n    if (has_offdiag )\n    {\n        bsrmv( Cusparse::get_instance().m_handle,  direction, CUSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, A.get_num_cols(), nnz, &alphaConst,\n               A.cuMatDescr,\n               A.values.raw(),\n               A.m_seq_offsets.raw() + rowOff,\n               A.row_offsets.raw() + rowOff,\n               A.col_indices.raw(),\n               rowOff,\n               A.get_block_dimx(),\n               x.raw(),\n               &betaConst,\n               y.raw() + rowOff * A.get_block_dimx(),\n               stream);\n    }\n\n    if (A.hasProps(DIAG))\n    {\n        ValueTypeB beta;\n\n        if (!has_offdiag)\n        {\n            beta = betaConst;\n        }\n        else\n        {\n            beta = types::util<ValueTypeB>::get_one();\n        }\n\n        bsrmv( Cusparse::get_instance().m_handle,  direction, CUSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, A.get_num_cols(), A.get_num_rows(), &alphaConst,\n               A.cuMatDescr,\n               A.values.raw() + A.diagOffset()*A.get_block_size(),\n               A.m_seq_offsets.raw(),\n               A.m_seq_offsets.raw() + rowOff,\n               A.m_seq_offsets.raw(),\n               rowOff,\n               A.get_block_dimx(),\n               x.raw(), &beta,\n               y.raw() + rowOff * A.get_block_dimx(),\n               stream);\n    }\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal_with_mask( const typename TConfig::VecPrec alphaConst,\n        const Matrix<TConfig> &A,\n        const Vector<TConfig> &x,\n        const typename TConfig::VecPrec betaConst,\n        Vector<TConfig> &y,\n        ViewType view,\n        const cudaStream_t &stream)\n{\n    if (A.is_matrix_singleGPU())\n    {\n        FatalError(\"Should not be here in bsrmv_internal_with_mask\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if(view != INTERIOR && view != BOUNDARY)\n    {\n        FatalError(\"Only INTERIOR and BOUNDARY views supported for bsrmv_internal_with_mask\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename TConfig::VecPrec ValueType;\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    const int *start_offsets, *end_offsets;\n    start_offsets = A.row_offsets.raw();\n    end_offsets = A.row_offsets.raw() + 1;\n    typedef typename Matrix<TConfig>::index_type index_type;\n\n    int offset, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &offset, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    if (nrows <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    bool has_offdiag = nnz != 0;\n\n    if (has_offdiag)\n    {\n        cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, stream));\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE, nrows,\n                         nrows, A.get_num_cols(), nnz, &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw(),\n                         A.manager->getRowsListForView(view).raw(),\n                         start_offsets, end_offsets, A.col_indices.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &betaConst,\n                         y.raw() );\n\n        // Reset to default stream\n        cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, 0));\n    }\n\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Diag not supported in multiply with mask\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal_with_mask_restriction( const typename TConfig::VecPrec alphaConst,\n        const Matrix<TConfig> &R,\n        const Vector<TConfig> &x,\n        const typename TConfig::VecPrec betaConst,\n        Vector<TConfig> &y,\n        ViewType view,\n        const cudaStream_t &stream,\n        Matrix<TConfig> &P)\n{\n    if (P.is_matrix_singleGPU())\n    {\n        FatalError(\"Should not be here in bsrmv_internal_with_mask_with_restriction\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if(view != OWNED && view != HALO1)\n    {\n        FatalError(\"View not supported in restriction operation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename TConfig::VecPrec ValueType;\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( R.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    int rowOff, nrows, nnz;\n    R.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    R.getNnzForView(view, &nnz);\n\n    bool has_offdiag = nnz != 0;\n    typedef typename Matrix<TConfig>::index_type index_type;\n\n    if (nrows <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    if (has_offdiag)\n    {\n        bsrmv( Cusparse::get_instance().m_handle,  direction, CUSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, R.get_num_cols(), nnz, &alphaConst,\n               R.cuMatDescr,\n               R.values.raw(),\n               R.m_seq_offsets.raw() + rowOff,\n               R.row_offsets.raw() + rowOff,\n               R.col_indices.raw(),\n               rowOff,\n               R.get_block_dimx(),\n               x.raw(),\n               &betaConst,\n               y.raw() + rowOff * R.get_block_dimx(),\n               stream);\n    }\n\n    if (R.hasProps(DIAG))\n    {\n        FatalError(\"Diag not supported in multiply with mask\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const typename Matrix<TConfig>::MVector &E,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const cudaStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n    int rowOff, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    cusparseDirection_t direction = A.getBlockFormat() == ROW_MAJOR ? CUSPARSE_DIRECTION_ROW : CUSPARSE_DIRECTION_COLUMN;\n\n    bsrmv( Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE,\n           nrows, A.get_num_cols(), nnz, &alphaConst,\n           A.cuMatDescr,\n           E.raw(),\n           A.m_seq_offsets.raw(),\n           A.m_seq_offsets.raw() + rowOff,\n           A.m_seq_offsets.raw(),\n           rowOff,\n           A.get_block_dimx(),\n           x.raw(), &betaConst,\n           y.raw() + rowOff * A.get_block_dimx(),\n           stream);\n}\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( ColumnColorSelector columnColorSelector,\n                               const int color,\n                               const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const cudaStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n\n    if (!A.hasProps(COLORING))\n    {\n        FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (color < 0 || color >= A.getMatrixColoring().getNumColors())\n    {\n        FatalError(\"Unknown color\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename Matrix<TConfig>::index_type index_type;\n    index_type colorStart = 0;\n\n    if ( !(view & INTERIOR) )\n    {\n        colorStart = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorStart = A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    }\n\n    index_type colorNum = 0;\n\n    if ( view == A.getViewInterior() )\n    {\n        colorNum = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorNum = A.getMatrixColoring().getOffsetsRowsPerColor()[color + 1];\n    }\n\n    colorNum -= colorStart;\n\n    if (colorNum <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    if (columnColorSelector == DIAG_COL)\n    {\n        FatalError(\"DIAG_COL has been disabled to avoid allocating diag_offsets\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    const int *start_offsets, *end_offsets;\n\n    switch (columnColorSelector)\n    {\n        case ALL_COLORS:\n            start_offsets = A.row_offsets.raw();\n            end_offsets = A.row_offsets.raw() + 1;\n            break;\n\n        case SMALLER_COLORS:\n            start_offsets = A.row_offsets.raw();\n            end_offsets = A.m_smaller_color_offsets.raw();\n            break;\n\n        case LARGER_COLORS:\n            start_offsets = A.m_larger_color_offsets.raw();\n            end_offsets = A.row_offsets.raw() + 1;\n            break;\n\n        case DIAG_COL:\n            start_offsets = A.diag.raw();\n            end_offsets = A.m_diag_end_offsets.raw();\n            break;\n\n        default:\n            FatalError(\"Unknown color selector\", AMGX_ERR_CORE);\n    }\n\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    bool has_offdiag = A.get_num_nz() != 0;\n    cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, stream));\n\n    if (has_offdiag)\n    {\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                         A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw(),\n                         A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                         start_offsets, end_offsets, A.col_indices.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &betaConst,\n                         y.raw() );\n    }\n\n    if (A.hasProps(DIAG) && columnColorSelector == ALL_COLORS)\n    {\n        typename TConfig::VecPrec beta = betaConst;\n\n        if (has_offdiag)\n        {\n            beta = types::util<typename TConfig::VecPrec>::get_one();\n        }\n\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                         A.get_num_rows(), A.get_num_cols(), A.get_num_rows(), &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw() + A.diagOffset()*A.get_block_size(),\n                         A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                         A.m_seq_offsets.raw(),\n                         NULL,\n                         A.m_seq_offsets.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &beta,\n                         y.raw() );\n    }\n\n    // Reset to default stream\n    cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, 0));\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const int color,\n                               const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const typename Matrix<TConfig>::MVector &E,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const cudaStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n\n    if ( !A.hasProps(COLORING) )\n    {\n        FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if ( color < 0 || color >= A.getMatrixColoring().getNumColors() )\n    {\n        FatalError(\"Unknown color\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename Matrix<TConfig>::index_type index_type;\n    //const index_type colorStart = ((view & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color] : A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    //const index_type colorNum = ((view == A.getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color] : A.getMatrixColoring().getOffsetsRowsPerColor()[color+1]) - colorStart;\n    //const index_type colorStart= A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    //const index_type colorNum= A.getMatrixColoring().getOffsetsRowsPerColor()[color+1] - colorStart;\n    index_type colorStart = 0;\n\n    if ( !(view & INTERIOR) )\n    {\n        colorStart = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorStart = A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    }\n\n    index_type colorNum = 0;\n\n    if ( view == A.getViewInterior() )\n    {\n        colorNum = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorNum = A.getMatrixColoring().getOffsetsRowsPerColor()[color + 1];\n    }\n\n    colorNum -= colorStart;\n\n    if (colorNum <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    cusparseDirection_t direction = CUSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = CUSPARSE_DIRECTION_ROW;\n    }\n\n    cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, stream));\n\n    bsrxmv_internal( Cusparse::get_instance().m_handle, direction, CUSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                     A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                     A.cuMatDescr,\n                     E.raw(),\n                     A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                     A.m_seq_offsets.raw(),\n                     NULL,\n                     A.m_seq_offsets.raw(),\n                     A.get_block_dimx(),\n                     x.raw(), &betaConst,\n                     y.raw() );\n    // Reset to default stream\n    cusparseCheckError(cusparseSetStream(Cusparse::get_instance().m_handle, 0));\n}\n\n// Simple custom implementation of matrix-vector product that has only 1 kernel.\ntemplate<unsigned UNROLL, class T>\n__global__ void csrmv(\n    int nrows,\n    const T alpha,\n    const T* __restrict__ csrVal,\n    const int* __restrict__ csrRow,\n    const int* __restrict__ csrCol,\n    const T* __restrict__ x,\n    const T beta,\n    T* __restrict__ y)\n{\n    for(int i = threadIdx.x + blockIdx.x*blockDim.x; i < nrows; i += blockDim.x*gridDim.x)\n    {\n        T y_tmp = amgx::types::util<T>::get_zero();\n\n        int row_b = csrRow[i];\n        int row_e = csrRow[i+1];\n\n        // Unrolling is important for performance here.\n        // Possible to squeeze more performance out of the key kernels if we\n        // measure the sparsity and use it to inform unrolling.\n        for (int col = row_b; col < row_e; col += UNROLL)\n        {\n#pragma unroll UNROLL\n            for(int off = 0; off < UNROLL; ++off)\n            {\n                int c = col + off;\n                if(c < row_e) y_tmp = alpha * csrVal[c] * x[csrCol[c]] + y_tmp;\n            }\n        }\n\n        // Don't read y unnecessarily\n        if(amgx::types::util<T>::is_zero(beta))\n        {\n            y[i] = y_tmp;\n        }\n        else\n        {\n            y[i] = beta*y[i] + y_tmp;\n        }\n    }\n}\n\ntemplate<class MatType, class VecType, class IndType>\ninline void generic_SpMV(cusparseHandle_t handle, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb, int rowOff,\n                             const MatType *alpha,\n                             const MatType *val,\n                             const IndType *rowPtr,\n                             const IndType *colInd,\n                             const VecType *x,\n                             const VecType *beta,\n                             VecType *y,\n                             cudaDataType matType,\n                             cudaDataType vecType,\n                             const cudaStream_t& stream)\n{\n    constexpr int cta_size = 128;\n    const int sm_count = getSMCount();\n\n    // Assuming that csrmv will be more efficient than cuSPARSE for row counts \n    // that are lower than the 3 times the total number of threads\n    // cuSPARSE does not like the offsetting required when latency hiding\n    // it's possible to reverse the offsets, but requires extra kernel invocation \n    // and usually the dependent part of the call is smaller\n    if(rowOff > 0 || mb < cta_size * sm_count * 3)\n    {\n        // Custom single-kernel SpMV, we could actually determine unroll factor\n        // more accurately here by checking non-zeros per row\n        constexpr int unroll_factor = 16;\n        int nblocks = mb / cta_size + 1;\n        csrmv<unroll_factor><<<nblocks, cta_size>>>(mb, *alpha, val, rowPtr, colInd, x, *beta, y);\n        cudaCheckError();\n    }\n    else\n    {\n        IndType* rows = const_cast<IndType*>(rowPtr);\n        IndType* cols = const_cast<IndType*>(colInd);\n        MatType* vals = const_cast<MatType*>(val);\n\n        cusparseSpMatDescr_t matA_descr;\n        cusparseDnVecDescr_t vecX_descr;\n        cusparseDnVecDescr_t vecY_descr;\n        cusparseCheckError(cusparseCreateDnVec(&vecX_descr, nb, const_cast<VecType*>(x), vecType));\n        cusparseCheckError(cusparseCreateDnVec(&vecY_descr, mb, const_cast<VecType*>(y), vecType));\n        cusparseCheckError(\n            cusparseCreateCsr(&matA_descr, mb, nb, nnzb, const_cast<IndType*>(rows), const_cast<IndType*>(cols),\n                              const_cast<MatType*>(vals), CUSPARSE_INDEX_32I, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_BASE_ZERO, matType));\n\n        // Use ALG2 for deterministic results when determinism_flag is set\n        cusparseSpMVAlg_t spmv_alg = Cusparse::get_instance().is_deterministic() ? CUSPARSE_CSRMV_ALG2 : CUSPARSE_CSRMV_ALG1;\n\n        size_t bufferSize = 0;\n        cusparseCheckError(cusparseSpMV_bufferSize(handle, trans, alpha, matA_descr, vecX_descr, beta, vecY_descr, matType, spmv_alg, &bufferSize));\n\n        void* dBuffer = NULL;\n        if(bufferSize > 0)\n        {\n            cudaError_t cuda_rc = amgx::memory::cudaMallocAsync(&dBuffer, bufferSize, stream);\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaMallocAsync failed in generic_SpMV\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n\n        cusparseCheckError(cusparseSpMV(handle, trans, alpha, matA_descr, vecX_descr, beta, vecY_descr, matType, spmv_alg, dBuffer) );\n\n        cusparseCheckError(cusparseDestroySpMat(matA_descr));\n        cusparseCheckError(cusparseDestroyDnVec(vecX_descr));\n        cusparseCheckError(cusparseDestroyDnVec(vecY_descr));\n\n        if(bufferSize > 0)\n        {\n            cudaError_t cuda_rc = amgx::memory::cudaFreeAsync(dBuffer);\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaFreeAsync failed in generic_SpMV\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n}\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const float *alpha,\n                             const cusparseMatDescr_t descr,\n                             const float *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const float *x,\n                             const float *beta,\n                             float *y,\n                             const cudaStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(cusparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, CUDA_R_32F, CUDA_R_32F, stream);\n    }\n    else\n    {\n        cusparseCheckError(cusparseSbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(cusparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const double *alpha,\n                             const cusparseMatDescr_t descr,\n                             const double *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const double *x,\n                             const double *beta,\n                             double *y,\n                             const cudaStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(cusparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, CUDA_R_64F, CUDA_R_64F, stream);\n    }\n    else\n    {\n        cusparseCheckError(cusparseDbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(cusparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const double *alpha,\n                             const cusparseMatDescr_t descr,\n                             const float *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const double *x,\n                             const double *beta,\n                             double *y,\n                             const cudaStream_t& stream)\n{\n    #if 0\n        // Run cuSparse on selected stream\n        cusparseCheckError(cusparseSetStream(handle, stream));\n\n        const double *d_bsrVal = reinterpret_cast<const double *>(const_cast<float *>(bsrVal)); // this works due to private API call in the matrix initialization which sets cusparse matrix description in the half precision mode\n        cusparseCheckError(cusparseDbsrxmv(handle, dir, trans, mb, mb, nb, nnzb, alpha, descr, d_bsrVal, bsrMaskPtr, bsrRowPtr, bsrRowPtr + 1, bsrColInd, blockDim, x, beta, y));\n\n        // Reset cuSparse to default stream\n        cusparseCheckError(cusparseSetStream(handle, 0));\n    #else\n        FatalError(\"Mixed precision modes not currently supported for CUDA 10.1 or later.\", AMGX_ERR_NOT_IMPLEMENTED);\n    #endif\n}\n\n// Custom implementation of matrix-vector product to replace the original bsrxmv,\n// but with block size of 1.\ntemplate<unsigned UNROLL, class T>\n__global__ void csrxmv(\n    int sizeOfMask,\n    const T alpha,\n    const T* __restrict__ csrVal,\n    const int* __restrict__ csrMask,\n    const int* __restrict__ csrRow,\n    const int* __restrict__ csrCol,\n    const T* __restrict__ x,\n    const T beta,\n    T* __restrict__ y)\n{\n    for(int i = threadIdx.x + blockIdx.x*blockDim.x; i < sizeOfMask; i += blockDim.x*gridDim.x)\n    {\n        int row = csrMask[i];\n        T y_tmp = amgx::types::util<T>::get_zero();\n\n        int row_b = csrRow[row];\n        int row_e = csrRow[row+1];\n\n        // Unrolling is important for performance here.\n        // Possible to squeeze more performance out of the key kernels if we\n        // measure the sparsity and use it to inform unrolling.\n        for (int col = row_b; col < row_e; col += UNROLL)\n        {\n#pragma unroll UNROLL\n            for(int off = 0; off < UNROLL; ++off)\n            {\n                int c = col + off;\n                if(c < row_e) y_tmp = alpha * csrVal[c] * x[csrCol[c]] + y_tmp;\n            }\n        }\n\n        // Don't read y unnecessarily\n        if(amgx::types::util<T>::is_zero(beta))\n        {\n            y[row] = y_tmp;\n        }\n        else\n        {\n            y[row] = beta*y[row] + y_tmp;\n        }\n    }\n}\n\n// Replaces the functionality of cusparse?bsrxmv for blockDim == 1\ntemplate<class T>\ninline void Xcsrxmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const T *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const T *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const T *x,\n                                       const T *beta,\n                                       T *y)\n{\n    if(blockDim != 1)\n    {\n        FatalError(\"Xcsrxmv only to be called with scalar matrices.\", AMGX_ERR_INTERNAL);\n    }\n    if (trans != CUSPARSE_OPERATION_NON_TRANSPOSE)\n    {\n        FatalError(\"Cannot currently latency hide a transposed matrix.\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    if (dir != CUSPARSE_DIRECTION_ROW)\n    {\n        FatalError(\"Cannot currently latency hide if matrix is not row major.\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    constexpr int cta_size = 128;\n    constexpr int unroll_factor = 16;\n    int nblocks = sizeOfMask / cta_size + 1;\n    csrxmv<unroll_factor><<<nblocks, cta_size>>>(sizeOfMask, *alpha, bsrVal, bsrMaskPtr, bsrRowPtr, bsrColInd, x, *beta, y);\n    cudaCheckError();\n}\n\n// overloaded C++ wrappers for cusparse?bsrxmv\n// bsrxmv\n// matrix - float\n// vector - float\ninline void Cusparse::bsrxmv_internal(cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                      int mb, int nb, int nnzb,\n                                      const float *alpha,\n                                      const cusparseMatDescr_t descr,\n                                      const float *bsrVal,\n                                      const int *bsrMaskPtr,\n                                      const int *bsrRowPtr,\n                                      const int *bsrEndPtr,\n                                      const int *bsrColInd,\n                                      int blockDim,\n                                      const float *x,\n                                      const float *beta,\n                                      float *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(cusparseSbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n// bsrxmv\n// matrix - float\n// vector - double\ninline void Cusparse::bsrxmv_internal( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const double *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const float *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const double *x,\n                                       const double *beta,\n                                       double *y)\n{\n    cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\n// bsrxmv\n// matrix - double\n// vector - double\ninline void Cusparse::bsrxmv_internal( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const double *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const double *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const double *x,\n                                       const double *beta,\n                                       double *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(cusparseDbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const cuComplex *alpha,\n                             const cusparseMatDescr_t descr,\n                             const cuComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const cuComplex *x,\n                             const cuComplex *beta,\n                             cuComplex *y,\n                             const cudaStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(cusparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, CUDA_C_32F, CUDA_C_32F, stream);\n    }\n    else\n    {\n        cusparseCheckError(cusparseCbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(cusparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const cuDoubleComplex *alpha,\n                             const cusparseMatDescr_t descr,\n                             const cuDoubleComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const cuDoubleComplex *x,\n                             const cuDoubleComplex *beta,\n                             cuDoubleComplex *y,\n                             const cudaStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(cusparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, CUDA_C_64F, CUDA_C_64F, stream);\n    }\n    else\n    {\n        cusparseCheckError(cusparseZbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(cusparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const cuDoubleComplex *alpha,\n                             const cusparseMatDescr_t descr,\n                             const cuComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const cuDoubleComplex *x,\n                             const cuDoubleComplex *beta,\n                             cuDoubleComplex *y,\n                             const cudaStream_t& stream)\n{\n    #if 0\n        // Run cuSparse on selected stream\n        cusparseCheckError(cusparseSetStream(handle, stream));\n\n        const cuDoubleComplex *d_bsrVal = reinterpret_cast<cuDoubleComplex *>(const_cast<cuComplex *>(bsrVal));\n        cusparseCheckError(cusparseZbsrxmv(handle, dir, trans, mb, mb, nb, nnzb, alpha, descr, d_bsrVal, bsrMaskPtr, bsrRowPtr, bsrRowPtr + 1, bsrColInd, blockDim, x, beta, y));\n\n        // Reset cuSparse to default stream\n        cusparseCheckError(cusparseSetStream(handle, 0));\n    #else\n        FatalError(\"Mixed precision modes not currently supported for CUDA 10.1 or later.\", AMGX_ERR_NOT_IMPLEMENTED);\n    #endif\n}\n\n\n// overloaded C++ wrappers for cusparse?bsrxmv\n// bsrxmv\n// matrix - cuComplex\n// vector - cuComplex\ninline void Cusparse::bsrxmv_internal( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const cuComplex *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const cuComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const cuComplex *x,\n                                       const cuComplex *beta,\n                                       cuComplex *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(cusparseCbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n// bsrxmv\n// matrix - cuComplex\n// vector - cuDoubleComplex\ninline void Cusparse::bsrxmv_internal( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const cuDoubleComplex *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const cuComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const cuDoubleComplex *x,\n                                       const cuDoubleComplex *beta,\n                                       cuDoubleComplex *y)\n{\n    cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\n// bsrxmv\n// matrix - cuDoubleComplex\n// vector - cuDoubleComplex\ninline void Cusparse::bsrxmv_internal( cusparseHandle_t handle, cusparseDirection_t dir, cusparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const cuDoubleComplex *alpha,\n                                       const cusparseMatDescr_t descr,\n                                       const cuDoubleComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const cuDoubleComplex *x,\n                                       const cuDoubleComplex *beta,\n                                       cuDoubleComplex *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(cusparseZbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n\nnamespace\n{\ntemplate<class MatType, class IndType>\ninline void\ngeneric_SpMM(cusparseHandle_t handle, cusparseOperation_t transA,\n             int m, int n, int k, int nnz,\n             int ldb, int ldc,\n             const MatType *alpha,\n             const MatType *Avals,\n             const MatType *Bvals,\n             MatType *Cvals,\n             const IndType *rowPtr,\n             const IndType *colInd,\n             const MatType *beta,\n             cudaDataType matType)\n{\n    // Create the matrix descriptors\n    cusparseSpMatDescr_t matA_descr;\n    cusparseDnMatDescr_t matB_descr;\n    cusparseDnMatDescr_t matC_descr;\n    cusparseCheckError(\n        cusparseCreateCsr(&matA_descr, m, k, nnz, const_cast<IndType*>(rowPtr), const_cast<IndType*>(colInd),\n                          const_cast<MatType*>(Avals), CUSPARSE_INDEX_32I, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_BASE_ZERO, matType));\n    cusparseCheckError(\n        cusparseCreateDnMat(&matB_descr, k, n, ldb, const_cast<MatType*>(Bvals), matType, CUSPARSE_ORDER_COL));\n    cusparseCheckError(\n        cusparseCreateDnMat(&matC_descr, m, n, ldc, const_cast<MatType*>(Cvals), matType, CUSPARSE_ORDER_COL));\n\n    // Check if a buffer is required, and if so allocate it using caching allocator\n    size_t bufferSize = 0;\n    cusparseCheckError(\n        cusparseSpMM_bufferSize(handle, transA, CUSPARSE_OPERATION_NON_TRANSPOSE, alpha, matA_descr, matB_descr,\n                                beta, matC_descr, matType, CUSPARSE_SPMM_ALG_DEFAULT, &bufferSize));\n\n    void* dBuffer = NULL;\n    if(bufferSize > 0)\n    {\n        cudaError_t cuda_rc = amgx::memory::cudaMallocAsync(&dBuffer, bufferSize);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaMallocAsync failed in generic_SpMM\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    // Compute the sparse matrix - dense matrix product\n    cusparseCheckError(\n        cusparseSpMM(handle, transA, CUSPARSE_OPERATION_NON_TRANSPOSE, alpha, matA_descr, matB_descr, beta,\n                     matC_descr, matType, CUSPARSE_SPMM_ALG_DEFAULT, dBuffer));\n\n    // Clean up\n    cusparseCheckError(cusparseDestroySpMat(matA_descr));\n    cusparseCheckError(cusparseDestroyDnMat(matB_descr));\n    cusparseCheckError(cusparseDestroyDnMat(matC_descr));\n\n    if(bufferSize > 0)\n    {\n        cudaError_t cuda_rc = amgx::memory::cudaFreeAsync(dBuffer);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaFreeAsync failed in generic_SpMM\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const float           *alpha,\n               const cusparseMatDescr_t descrA,\n               const float            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const float            *B, int ldb,\n               const float            *beta, float          *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, CUDA_R_32F);\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const double            *alpha,\n               const cusparseMatDescr_t descrA,\n               const float            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const double            *B, int ldb,\n               const double           *beta, double          *C, int ldc)\n{\n    cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const double          *alpha,\n               const cusparseMatDescr_t descrA,\n               const double          *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const double           *B, int ldb,\n               const double           *beta, double         *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, CUDA_R_64F);\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const cuComplex           *alpha,\n               const cusparseMatDescr_t descrA,\n               const cuComplex            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const cuComplex            *B, int ldb,\n               const cuComplex            *beta, cuComplex          *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, CUDA_C_32F);\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const cuDoubleComplex            *alpha,\n               const cusparseMatDescr_t descrA,\n               const cuComplex            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const cuDoubleComplex            *B, int ldb,\n               const cuDoubleComplex           *beta, cuDoubleComplex          *C, int ldc)\n{\n    cusparseCheckError(CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\nvoid\ncusparse_csrmm(cusparseHandle_t handle, cusparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const cuDoubleComplex          *alpha,\n               const cusparseMatDescr_t descrA,\n               const cuDoubleComplex          *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const cuDoubleComplex           *B, int ldb,\n               const cuDoubleComplex           *beta, cuDoubleComplex         *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, CUDA_C_64F);\n}\n}\n\n\ntemplate <class TConfig>\nvoid Cusparse::csrmm(typename TConfig::VecPrec alpha,\n                     Matrix<TConfig> &A,\n                     Vector<TConfig> &V,\n                     typename TConfig::VecPrec beta,\n                     Vector<TConfig> &Res)\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(V, V.tag);\n    }\n\n    if (Res.get_num_rows() != A.get_num_rows() || Res.get_num_cols() != V.get_num_cols())\n    {\n        FatalError(\"Cusparse::csrmm error, dimensions of result matrix do not match input matrices.\", AMGX_ERR_INTERNAL);\n    }\n\n    cusparseHandle_t handle = Cusparse::get_instance().m_handle;\n    cusparse_csrmm(handle, CUSPARSE_OPERATION_NON_TRANSPOSE,\n                   A.get_num_rows(), V.get_num_cols(), A.get_num_cols(),\n                   A.values.size(), &alpha, A.cuMatDescr,\n                   A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n                   V.raw(), V.get_lda(),\n                   &beta, Res.raw(), Res.get_lda());\n    Res.dirtybit = 1;\n}\n\ntemplate <class T>\nvoid transpose_internal(cusparseHandle_t handle, int nRows, int nCols, int nNz, const T* Avals, const int* Arows, const int* Acols, T* Bvals, int* Brows, int* Bcols, cudaDataType valType)\n{\n    size_t bufferSize;\n    cusparseCheckError(cusparseCsr2cscEx2_bufferSize(\n        handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, valType,\n        CUSPARSE_ACTION_NUMERIC, CUSPARSE_INDEX_BASE_ZERO, CUSPARSE_CSR2CSC_ALG1, &bufferSize));\n\n    void *buffer = nullptr;\n    if (bufferSize > 0)\n    {\n        cudaError_t cuda_rc = amgx::memory::cudaMallocAsync(&buffer, bufferSize);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaMallocAsync failed in transpose_internal\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    cusparseCheckError(cusparseCsr2cscEx2(\n        handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, valType,\n        CUSPARSE_ACTION_NUMERIC, CUSPARSE_INDEX_BASE_ZERO, CUSPARSE_CSR2CSC_ALG1, buffer));\n\n    if(bufferSize > 0)\n    {\n        cudaError_t cuda_rc = amgx::memory::cudaFreeAsync(buffer);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaFreeAsync failed in transpose_internal\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n}\n\nvoid transpose_internal(cusparseHandle_t handle, int nRows, int nCols, int nNz, const float* Avals, const int* Arows, const int* Acols, float* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, CUDA_R_32F);\n}\nvoid transpose_internal(cusparseHandle_t handle, int nRows, int nCols, int nNz, const double* Avals, const int* Arows, const int* Acols, double* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, CUDA_R_64F);\n}\nvoid transpose_internal(cusparseHandle_t handle, int nRows, int nCols, int nNz, const cuComplex* Avals, const int* Arows, const int* Acols, cuComplex* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, CUDA_C_32F);\n}\nvoid transpose_internal(cusparseHandle_t handle, int nRows, int nCols, int nNz, const cuDoubleComplex* Avals, const int* Arows, const int* Acols, cuDoubleComplex* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, CUDA_C_64F);\n}\n\ntemplate <class TConfig>\nvoid Cusparse::transpose(const Matrix<TConfig>& A, Matrix<TConfig>& B, const int nRows, const int nNz)\n{\n    cusparseHandle_t handle = Cusparse::get_instance().m_handle;\n    transpose_internal(handle, nRows, A.get_num_cols(), nNz,\n        A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n        B.values.raw(), B.row_offsets.raw(), B.col_indices.raw());\n}\n\ntemplate <class TConfig>\nvoid Cusparse::transpose(const Matrix<TConfig>& A, Matrix<TConfig>& B)\n{\n    cusparseHandle_t handle = Cusparse::get_instance().m_handle;\n    transpose_internal(handle, A.get_num_rows(), A.get_num_cols(), A.get_num_nz(),\n        A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n        B.values.raw(), B.row_offsets.raw(), B.col_indices.raw());\n}\n\n//#define AMGX_CASE_LINE(CASE) template class Cusparse<TemplateMode<CASE>::Type>;\n//    AMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrxmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>&, typename Matrix<TemplateMode<CASE>::Type>::IVector&, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv_with_mask(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv_with_mask_restriction(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, Matrix<TemplateMode<CASE>::Type>& );\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, const typename Matrix<TemplateMode<CASE>::Type>::MVector&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(ColumnColorSelector, const int, const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const int, const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, const typename Matrix<TemplateMode<CASE>::Type>::MVector &, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::csrmm(typename TemplateMode<CASE>::Type::VecPrec, Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>&);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::transpose(const Matrix<TemplateMode<CASE>::Type>& A, Matrix<TemplateMode<CASE>::Type>& B); \\\n  template void Cusparse::transpose(const Matrix<TemplateMode<CASE>::Type>& A, Matrix<TemplateMode<CASE>::Type>& B, const int nRows, const int nNz);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <hipsparse.h>\n#include <error.h>\n#include <matrix.h>\n#include <vector.h>\n#include <types.h>\n#include <thrust/sequence.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <device_properties.h>\n\n#include <amgx_cusparse.h>\n\n#include <amgx_types/util.h>\n\n#if CUDART_VERSION < 11000\n#define HIPSPARSE_SPMM_ALG_DEFAULT HIPSPARSE_MM_ALG_DEFAULT\n#endif\n\n#if CUDART_VERSION >= 12000\n#define HIPSPARSE_CSRMV_ALG1 HIPSPARSE_SPMV_CSR_ALG1\n#define HIPSPARSE_CSRMV_ALG2 HIPSPARSE_SPMV_CSR_ALG2\n#endif\n\nnamespace amgx\n{\n\nCusparse::Cusparse() : m_handle(0), m_determinism_flag(false)\n{\n    cusparseCheckError( hipsparseCreate(&m_handle) );\n}\n\nCusparse::~Cusparse()\n{\n    destroy_handle();\n}\n\nCusparse &Cusparse::get_instance()\n{\n    static Cusparse s_instance;\n    s_instance.create_handle();\n    return s_instance;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    ViewType view )\n{\n    hipStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    // Handle cases where the view is set by the calling routine\n    if(view != A.getViewExterior())\n    {\n        bsrmv_internal(alphaConst, A, x, betaConst, y, view, null_stream);\n        return;\n    }\n\n    bool latencyHiding = (A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(x, x.tag);\n\n        // Multiply interior rows\n        bsrmv_internal(alphaConst, A, x, betaConst, y, A.getViewInterior(), null_stream);\n\n        // Finish halo exchange\n        A.manager->exchange_halo_split_finish(x, x.tag);\n\n        // Multiply rows with halo dependencies\n        ViewType bnd_view = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n        bsrmv_internal(alphaConst, A, x, betaConst, y, bnd_view, null_stream);\n    }\n    else\n    {\n        if (!A.is_matrix_singleGPU() && x.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(x, x.tag);\n        }\n\n        bsrmv_internal(alphaConst, A, x, betaConst, y, A.getViewExterior(), null_stream);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_with_mask(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y)\n{\n    hipStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    bool latencyHiding = (A.getViewInterior() != A.getViewExterior() && !A.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        A.manager->exchange_halo_split_gather(x, x.tag);\n\n        // Multiply interior\n        bsrmv_internal_with_mask(alphaConst, A, x, betaConst, y, INTERIOR, null_stream);\n\n        A.manager->exchange_halo_split_finish(x, x.tag);\n\n        // Multiply exterior\n        bsrmv_internal_with_mask(alphaConst, A, x, betaConst, y, BOUNDARY, null_stream);\n    }\n    else\n    {\n        if (!A.is_matrix_singleGPU() && x.dirtybit != 0)\n        {\n            A.manager->exchange_halo_v2(x, x.tag);\n        }\n\n        bsrmv_internal(alphaConst, A, x, betaConst, y, OWNED, null_stream);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_with_mask_restriction(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &R,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    Matrix<TConfig> &P)\n{\n    // If only COO, add CSR since bsrmv doesn't support COO\n    //if (A.hasProps(COO) && !A.hasProps(CSR))\n    //{\n    //  A.set_initialized(0);\n    //  A.addProps(CSR);\n    //  A.computeDiagonal();\n    //  A.set_initialized(1);\n    //}\n\n    bool latencyHiding = (R.getViewInterior() != R.getViewExterior() && !P.is_matrix_singleGPU() && x.dirtybit != 0);\n\n    if (latencyHiding)\n    {\n        hipStream_t null_stream = 0;\n        bsrmv_internal_with_mask_restriction(alphaConst, R, x, betaConst, y, HALO1, null_stream, P);\n        P.manager->add_from_halo_split_gather(y, y.tag);\n        hipError_t cuda_rc = hipEventRecord(P.manager->get_comm_event());\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipEventRecord failed\", AMGX_ERR_CUDA_FAILURE);\n        }\n        bsrmv_internal_with_mask_restriction(alphaConst, R, x, betaConst, y, OWNED, null_stream, P);\n\n        if (P.manager->neighbors.size() != 0)\n        {\n            cuda_rc = hipEventSynchronize(P.manager->get_comm_event());\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipEventSynchronize failed\", AMGX_ERR_CUDA_FAILURE);\n            }\n            P.manager->add_from_halo_split_finish(y, y.tag, P.manager->get_bdy_stream());\n            cuda_rc = hipStreamSynchronize(P.manager->get_bdy_stream());\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipStreamSynchronize failed\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n    else\n    {\n        bsrmv_internal(alphaConst, R, x, betaConst, y, OWNED, 0);\n\n        // Add contribution from neighbors\n        P.manager->add_from_halo_v2(y, y.tag);\n    }\n\n    y.dirtybit = 1;\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrxmv(\n    const typename TConfig::VecPrec alphaConst,\n    Matrix<TConfig> &A,\n    Vector<TConfig> &x,\n    const typename TConfig::VecPrec betaConst,\n    Vector<TConfig> &y,\n    typename Matrix<TConfig>::IVector &mask,\n    ViewType view )\n{\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.set_initialized(0);\n        A.addProps(CSR);\n        A.computeDiagonal();\n        A.set_initialized(1);\n    }\n\n    const int *start_offsets, *end_offsets;\n    start_offsets = A.row_offsets.raw();\n    end_offsets = A.row_offsets.raw() + 1;\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    if (view == OWNED)  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrxmv_internal(Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                        mask.size(),\n                        A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                        A.cuMatDescr,\n                        A.values.raw(),\n                        mask.raw(),\n                        start_offsets, end_offsets,\n                        A.col_indices.raw(),\n                        A.get_block_dimx(),\n                        x.raw(),\n                        &betaConst,\n                        y.raw());\n    }\n    else //Try and do latency hiding\n    {\n        // latency hiding?\n        FatalError(\"Trying to do latency hiding in the bsrxmv\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n// E is a vector that represents a diagonal matrix\n// operate on all rows and columns\n// y= alpha*E.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      const typename Matrix<TConfig>::MVector &E,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view )\n{\n    hipStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior()) //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrmv_internal(alphaConst, A, E, x, betaConst, y, view, null_stream);\n    }\n    else  //Try and do latency hiding\n    {\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        ViewType flags;\n        bool latencyHiding = true;\n\n        //if (A.manager->num_neighbors() == 0 || (x.dirtybit == 0)) {\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(alphaConst, A, E, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU())\n        //  if (y.size() == x.size() && y.delayed_send==0) A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n// operate only on columns specified by columnColorSelector, see enum ColumnColorSelector above\n// operate only on rows of specified color, given by A.offsets_rows_per_color, A.sorted_rows_by_color\n// y= alpha*A.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( ColumnColorSelector columnColorSelector,\n                      const int color,\n                      const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view )\n{\n    hipStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior())  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        // std::cerr << \"exterior view with selector\" << std::endl;\n        bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, view, null_stream);\n    }\n    else\n    {\n        //Try and do latency hiding\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        ViewType flags;\n        bool latencyHiding = true;\n\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(columnColorSelector, color, alphaConst, A, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU() && y.size() == x.size() && y.delayed_send==0)\n        //  A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n// E is a vector that represents a diagonal matrix\n// operate only on rows of specified color, given by A.offsets_rows_per_color, A.sorted_rows_by_color\n// y= alpha*E.x + beta*y\ntemplate< class TConfig >\nvoid Cusparse::bsrmv( const int color,\n                      const typename TConfig::VecPrec alphaConst,\n                      Matrix<TConfig> &A,\n                      const typename Matrix<TConfig>::MVector &E,\n                      Vector<TConfig> &x,\n                      const typename TConfig::VecPrec betaConst,\n                      Vector<TConfig> &y,\n                      ViewType view)\n{\n    hipStream_t null_stream = 0;\n\n    // If only COO, add CSR since bsrmv doesn't support COO\n    if (A.hasProps(COO) && !A.hasProps(CSR))\n    {\n        A.addProps(CSR);\n    }\n\n    if (view != A.getViewExterior())  //This is already a view, thus do not even attempt to do latency hiding\n    {\n        bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, view, null_stream);\n    }\n    else  //Try and do latency hiding\n    {\n        //std::ccooor << \"de\" << std::endl;\n        //std::cerr << \"not an exterior view\" << std::endl;\n        ViewType oldView = A.currentView();\n\n        if (!A.is_matrix_singleGPU())\n        {\n            A.manager->exchange_halo_async(x, x.tag);\n        }\n\n        if (A.getViewExterior() == A.getViewInterior())\n        {\n            //std::cerr << \"exchange_halo_wait\" << std::endl;\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n        }\n\n        //std::cerr << \"xxxeded\" << std::endl;\n        ViewType flags;\n        bool latencyHiding = true;\n\n        if (A.is_matrix_singleGPU() || (x.dirtybit == 0))\n        {\n            latencyHiding = false;\n            A.setViewExterior();\n            flags = (ViewType)(A.getViewExterior());\n        }\n        else\n        {\n            flags = (ViewType)(A.getViewInterior());\n            A.setViewInterior();\n        }\n\n        if (latencyHiding)\n        {\n            bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->exchange_halo_wait(x, x.tag);\n            }\n\n            A.setViewExterior();\n            flags = (ViewType)(~(A.getViewInterior()) & A.getViewExterior());\n\n            if (flags != 0)\n            {\n                bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n            }\n        }\n        else\n        {\n            bsrmv_internal(color, alphaConst, A, E, x, betaConst, y, flags, null_stream);\n        }\n\n        y.dirtybit = 1;\n        //if (!A.is_matrix_singleGPU() && y.size() == x.size() && y.delayed_send==0)\n        //  A.manager->exchange_halo_async(y, y.tag);\n        A.setView(oldView);\n    }\n}\n\n__global__ void offset_by_col_off(int nrows, int* rows, const int* bsrRowPtr)\n{\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if(i >= nrows+1)\n    {\n        return;\n    }\n\n    rows[i] = bsrRowPtr[i] - bsrRowPtr[0];\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const hipStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueTypeB;\n    int rowOff, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    bool has_offdiag = nnz != 0;\n\n    if (has_offdiag )\n    {\n        bsrmv( Cusparse::get_instance().m_handle,  direction, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, A.get_num_cols(), nnz, &alphaConst,\n               A.cuMatDescr,\n               A.values.raw(),\n               A.m_seq_offsets.raw() + rowOff,\n               A.row_offsets.raw() + rowOff,\n               A.col_indices.raw(),\n               rowOff,\n               A.get_block_dimx(),\n               x.raw(),\n               &betaConst,\n               y.raw() + rowOff * A.get_block_dimx(),\n               stream);\n    }\n\n    if (A.hasProps(DIAG))\n    {\n        ValueTypeB beta;\n\n        if (!has_offdiag)\n        {\n            beta = betaConst;\n        }\n        else\n        {\n            beta = types::util<ValueTypeB>::get_one();\n        }\n\n        bsrmv( Cusparse::get_instance().m_handle,  direction, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, A.get_num_cols(), A.get_num_rows(), &alphaConst,\n               A.cuMatDescr,\n               A.values.raw() + A.diagOffset()*A.get_block_size(),\n               A.m_seq_offsets.raw(),\n               A.m_seq_offsets.raw() + rowOff,\n               A.m_seq_offsets.raw(),\n               rowOff,\n               A.get_block_dimx(),\n               x.raw(), &beta,\n               y.raw() + rowOff * A.get_block_dimx(),\n               stream);\n    }\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal_with_mask( const typename TConfig::VecPrec alphaConst,\n        const Matrix<TConfig> &A,\n        const Vector<TConfig> &x,\n        const typename TConfig::VecPrec betaConst,\n        Vector<TConfig> &y,\n        ViewType view,\n        const hipStream_t &stream)\n{\n    if (A.is_matrix_singleGPU())\n    {\n        FatalError(\"Should not be here in bsrmv_internal_with_mask\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if(view != INTERIOR && view != BOUNDARY)\n    {\n        FatalError(\"Only INTERIOR and BOUNDARY views supported for bsrmv_internal_with_mask\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename TConfig::VecPrec ValueType;\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    const int *start_offsets, *end_offsets;\n    start_offsets = A.row_offsets.raw();\n    end_offsets = A.row_offsets.raw() + 1;\n    typedef typename Matrix<TConfig>::index_type index_type;\n\n    int offset, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &offset, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    if (nrows <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    bool has_offdiag = nnz != 0;\n\n    if (has_offdiag)\n    {\n        cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, stream));\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE, nrows,\n                         nrows, A.get_num_cols(), nnz, &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw(),\n                         A.manager->getRowsListForView(view).raw(),\n                         start_offsets, end_offsets, A.col_indices.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &betaConst,\n                         y.raw() );\n\n        // Reset to default stream\n        cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, 0));\n    }\n\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Diag not supported in multiply with mask\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal_with_mask_restriction( const typename TConfig::VecPrec alphaConst,\n        const Matrix<TConfig> &R,\n        const Vector<TConfig> &x,\n        const typename TConfig::VecPrec betaConst,\n        Vector<TConfig> &y,\n        ViewType view,\n        const hipStream_t &stream,\n        Matrix<TConfig> &P)\n{\n    if (P.is_matrix_singleGPU())\n    {\n        FatalError(\"Should not be here in bsrmv_internal_with_mask_with_restriction\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if(view != OWNED && view != HALO1)\n    {\n        FatalError(\"View not supported in restriction operation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename TConfig::VecPrec ValueType;\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( R.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    int rowOff, nrows, nnz;\n    R.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    R.getNnzForView(view, &nnz);\n\n    bool has_offdiag = nnz != 0;\n    typedef typename Matrix<TConfig>::index_type index_type;\n\n    if (nrows <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    if (has_offdiag)\n    {\n        bsrmv( Cusparse::get_instance().m_handle,  direction, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n               nrows, R.get_num_cols(), nnz, &alphaConst,\n               R.cuMatDescr,\n               R.values.raw(),\n               R.m_seq_offsets.raw() + rowOff,\n               R.row_offsets.raw() + rowOff,\n               R.col_indices.raw(),\n               rowOff,\n               R.get_block_dimx(),\n               x.raw(),\n               &betaConst,\n               y.raw() + rowOff * R.get_block_dimx(),\n               stream);\n    }\n\n    if (R.hasProps(DIAG))\n    {\n        FatalError(\"Diag not supported in multiply with mask\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const typename Matrix<TConfig>::MVector &E,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const hipStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n    int rowOff, nrows, nnz;\n    A.getOffsetAndSizeForView(view, &rowOff, &nrows);\n    A.getNnzForView(view, &nnz);\n\n    hipsparseDirection_t direction = A.getBlockFormat() == ROW_MAJOR ? HIPSPARSE_DIRECTION_ROW : HIPSPARSE_DIRECTION_COLUMN;\n\n    bsrmv( Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n           nrows, A.get_num_cols(), nnz, &alphaConst,\n           A.cuMatDescr,\n           E.raw(),\n           A.m_seq_offsets.raw(),\n           A.m_seq_offsets.raw() + rowOff,\n           A.m_seq_offsets.raw(),\n           rowOff,\n           A.get_block_dimx(),\n           x.raw(), &betaConst,\n           y.raw() + rowOff * A.get_block_dimx(),\n           stream);\n}\n\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( ColumnColorSelector columnColorSelector,\n                               const int color,\n                               const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const hipStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n\n    if (!A.hasProps(COLORING))\n    {\n        FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if (color < 0 || color >= A.getMatrixColoring().getNumColors())\n    {\n        FatalError(\"Unknown color\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename Matrix<TConfig>::index_type index_type;\n    index_type colorStart = 0;\n\n    if ( !(view & INTERIOR) )\n    {\n        colorStart = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorStart = A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    }\n\n    index_type colorNum = 0;\n\n    if ( view == A.getViewInterior() )\n    {\n        colorNum = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorNum = A.getMatrixColoring().getOffsetsRowsPerColor()[color + 1];\n    }\n\n    colorNum -= colorStart;\n\n    if (colorNum <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    if (columnColorSelector == DIAG_COL)\n    {\n        FatalError(\"DIAG_COL has been disabled to avoid allocating diag_offsets\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    const int *start_offsets, *end_offsets;\n\n    switch (columnColorSelector)\n    {\n        case ALL_COLORS:\n            start_offsets = A.row_offsets.raw();\n            end_offsets = A.row_offsets.raw() + 1;\n            break;\n\n        case SMALLER_COLORS:\n            start_offsets = A.row_offsets.raw();\n            end_offsets = A.m_smaller_color_offsets.raw();\n            break;\n\n        case LARGER_COLORS:\n            start_offsets = A.m_larger_color_offsets.raw();\n            end_offsets = A.row_offsets.raw() + 1;\n            break;\n\n        case DIAG_COL:\n            start_offsets = A.diag.raw();\n            end_offsets = A.m_diag_end_offsets.raw();\n            break;\n\n        default:\n            FatalError(\"Unknown color selector\", AMGX_ERR_CORE);\n    }\n\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    bool has_offdiag = A.get_num_nz() != 0;\n    cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, stream));\n\n    if (has_offdiag)\n    {\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                         A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw(),\n                         A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                         start_offsets, end_offsets, A.col_indices.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &betaConst,\n                         y.raw() );\n    }\n\n    if (A.hasProps(DIAG) && columnColorSelector == ALL_COLORS)\n    {\n        typename TConfig::VecPrec beta = betaConst;\n\n        if (has_offdiag)\n        {\n            beta = types::util<typename TConfig::VecPrec>::get_one();\n        }\n\n        bsrxmv_internal( Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                         A.get_num_rows(), A.get_num_cols(), A.get_num_rows(), &alphaConst,\n                         A.cuMatDescr,\n                         A.values.raw() + A.diagOffset()*A.get_block_size(),\n                         A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                         A.m_seq_offsets.raw(),\n                         NULL,\n                         A.m_seq_offsets.raw(),\n                         A.get_block_dimx(),\n                         x.raw(), &beta,\n                         y.raw() );\n    }\n\n    // Reset to default stream\n    cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, 0));\n}\n\ntemplate< class TConfig >\nvoid Cusparse::bsrmv_internal( const int color,\n                               const typename TConfig::VecPrec alphaConst,\n                               const Matrix<TConfig> &A,\n                               const typename Matrix<TConfig>::MVector &E,\n                               const Vector<TConfig> &x,\n                               const typename TConfig::VecPrec betaConst,\n                               Vector<TConfig> &y,\n                               ViewType view,\n                               const hipStream_t &stream)\n{\n    typedef typename TConfig::VecPrec ValueType;\n\n    if ( !A.hasProps(COLORING) )\n    {\n        FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    if ( color < 0 || color >= A.getMatrixColoring().getNumColors() )\n    {\n        FatalError(\"Unknown color\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    typedef typename Matrix<TConfig>::index_type index_type;\n    //const index_type colorStart = ((view & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color] : A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    //const index_type colorNum = ((view == A.getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color] : A.getMatrixColoring().getOffsetsRowsPerColor()[color+1]) - colorStart;\n    //const index_type colorStart= A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    //const index_type colorNum= A.getMatrixColoring().getOffsetsRowsPerColor()[color+1] - colorStart;\n    index_type colorStart = 0;\n\n    if ( !(view & INTERIOR) )\n    {\n        colorStart = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorStart = A.getMatrixColoring().getOffsetsRowsPerColor()[color];\n    }\n\n    index_type colorNum = 0;\n\n    if ( view == A.getViewInterior() )\n    {\n        colorNum = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[color];\n    }\n    else\n    {\n        colorNum = A.getMatrixColoring().getOffsetsRowsPerColor()[color + 1];\n    }\n\n    colorNum -= colorStart;\n\n    if (colorNum <= 0)\n    {\n        return;    // nothing to do, early exit\n    }\n\n    hipsparseDirection_t direction = HIPSPARSE_DIRECTION_COLUMN;\n\n    if ( A.getBlockFormat() == ROW_MAJOR )\n    {\n        direction = HIPSPARSE_DIRECTION_ROW;\n    }\n\n    cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, stream));\n\n    bsrxmv_internal( Cusparse::get_instance().m_handle, direction, HIPSPARSE_OPERATION_NON_TRANSPOSE, colorNum,\n                     A.get_num_rows(), A.get_num_cols(), A.get_num_nz(), &alphaConst,\n                     A.cuMatDescr,\n                     E.raw(),\n                     A.getMatrixColoring().getSortedRowsByColor().raw() + colorStart,\n                     A.m_seq_offsets.raw(),\n                     NULL,\n                     A.m_seq_offsets.raw(),\n                     A.get_block_dimx(),\n                     x.raw(), &betaConst,\n                     y.raw() );\n    // Reset to default stream\n    cusparseCheckError(hipsparseSetStream(Cusparse::get_instance().m_handle, 0));\n}\n\n// Simple custom implementation of matrix-vector product that has only 1 kernel.\ntemplate<unsigned UNROLL, class T>\n__global__ void csrmv(\n    int nrows,\n    const T alpha,\n    const T* __restrict__ csrVal,\n    const int* __restrict__ csrRow,\n    const int* __restrict__ csrCol,\n    const T* __restrict__ x,\n    const T beta,\n    T* __restrict__ y)\n{\n    for(int i = threadIdx.x + blockIdx.x*blockDim.x; i < nrows; i += blockDim.x*gridDim.x)\n    {\n        T y_tmp = amgx::types::util<T>::get_zero();\n\n        int row_b = csrRow[i];\n        int row_e = csrRow[i+1];\n\n        // Unrolling is important for performance here.\n        // Possible to squeeze more performance out of the key kernels if we\n        // measure the sparsity and use it to inform unrolling.\n        for (int col = row_b; col < row_e; col += UNROLL)\n        {\n#pragma unroll UNROLL\n            for(int off = 0; off < UNROLL; ++off)\n            {\n                int c = col + off;\n                if(c < row_e) y_tmp = alpha * csrVal[c] * x[csrCol[c]] + y_tmp;\n            }\n        }\n\n        // Don't read y unnecessarily\n        if(amgx::types::util<T>::is_zero(beta))\n        {\n            y[i] = y_tmp;\n        }\n        else\n        {\n            y[i] = beta*y[i] + y_tmp;\n        }\n    }\n}\n\ntemplate<class MatType, class VecType, class IndType>\ninline void generic_SpMV(hipsparseHandle_t handle, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb, int rowOff,\n                             const MatType *alpha,\n                             const MatType *val,\n                             const IndType *rowPtr,\n                             const IndType *colInd,\n                             const VecType *x,\n                             const VecType *beta,\n                             VecType *y,\n                             hipDataType matType,\n                             hipDataType vecType,\n                             const hipStream_t& stream)\n{\n    constexpr int cta_size = 128;\n    const int sm_count = getSMCount();\n\n    // Assuming that csrmv will be more efficient than cuSPARSE for row counts \n    // that are lower than the 3 times the total number of threads\n    // cuSPARSE does not like the offsetting required when latency hiding\n    // it's possible to reverse the offsets, but requires extra kernel invocation \n    // and usually the dependent part of the call is smaller\n    if(rowOff > 0 || mb < cta_size * sm_count * 3)\n    {\n        // Custom single-kernel SpMV, we could actually determine unroll factor\n        // more accurately here by checking non-zeros per row\n        constexpr int unroll_factor = 16;\n        int nblocks = mb / cta_size + 1;\n        csrmv<unroll_factor><<<nblocks, cta_size>>>(mb, *alpha, val, rowPtr, colInd, x, *beta, y);\n        cudaCheckError();\n    }\n    else\n    {\n        IndType* rows = const_cast<IndType*>(rowPtr);\n        IndType* cols = const_cast<IndType*>(colInd);\n        MatType* vals = const_cast<MatType*>(val);\n\n        hipsparseSpMatDescr_t matA_descr;\n        hipsparseDnVecDescr_t vecX_descr;\n        hipsparseDnVecDescr_t vecY_descr;\n        cusparseCheckError(hipsparseCreateDnVec(&vecX_descr, nb, const_cast<VecType*>(x), vecType));\n        cusparseCheckError(hipsparseCreateDnVec(&vecY_descr, mb, const_cast<VecType*>(y), vecType));\n        cusparseCheckError(\n            hipsparseCreateCsr(&matA_descr, mb, nb, nnzb, const_cast<IndType*>(rows), const_cast<IndType*>(cols),\n                              const_cast<MatType*>(vals), HIPSPARSE_INDEX_32I, HIPSPARSE_INDEX_32I, HIPSPARSE_INDEX_BASE_ZERO, matType));\n\n        // Use ALG2 for deterministic results when determinism_flag is set\n        hipsparseSpMVAlg_t spmv_alg = Cusparse::get_instance().is_deterministic() ? HIPSPARSE_CSRMV_ALG2 : HIPSPARSE_CSRMV_ALG1;\n\n        size_t bufferSize = 0;\n        cusparseCheckError(hipsparseSpMV_bufferSize(handle, trans, alpha, matA_descr, vecX_descr, beta, vecY_descr, matType, spmv_alg, &bufferSize));\n\n        void* dBuffer = NULL;\n        if(bufferSize > 0)\n        {\n            hipError_t cuda_rc = amgx::memory::hipMallocAsync(&dBuffer, bufferSize, stream);\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipMallocAsync failed in generic_SpMV\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n\n        cusparseCheckError(hipsparseSpMV(handle, trans, alpha, matA_descr, vecX_descr, beta, vecY_descr, matType, spmv_alg, dBuffer) );\n\n        cusparseCheckError(hipsparseDestroySpMat(matA_descr));\n        cusparseCheckError(hipsparseDestroyDnVec(vecX_descr));\n        cusparseCheckError(hipsparseDestroyDnVec(vecY_descr));\n\n        if(bufferSize > 0)\n        {\n            hipError_t cuda_rc = amgx::memory::hipFreeAsync(dBuffer);\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipFreeAsync failed in generic_SpMV\", AMGX_ERR_CUDA_FAILURE);\n            }\n        }\n    }\n}\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const float *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const float *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const float *x,\n                             const float *beta,\n                             float *y,\n                             const hipStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(hipsparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, HIP_R_32F, HIP_R_32F, stream);\n    }\n    else\n    {\n        cusparseCheckError(hipsparseSbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(hipsparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const double *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const double *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const double *x,\n                             const double *beta,\n                             double *y,\n                             const hipStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(hipsparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, HIP_R_64F, HIP_R_64F, stream);\n    }\n    else\n    {\n        cusparseCheckError(hipsparseDbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(hipsparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const double *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const float *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const double *x,\n                             const double *beta,\n                             double *y,\n                             const hipStream_t& stream)\n{\n    #if 0\n        // Run cuSparse on selected stream\n        cusparseCheckError(hipsparseSetStream(handle, stream));\n\n        const double *d_bsrVal = reinterpret_cast<const double *>(const_cast<float *>(bsrVal)); // this works due to private API call in the matrix initialization which sets cusparse matrix description in the half precision mode\n        cusparseCheckError(hipsparseDbsrxmv(handle, dir, trans, mb, mb, nb, nnzb, alpha, descr, d_bsrVal, bsrMaskPtr, bsrRowPtr, bsrRowPtr + 1, bsrColInd, blockDim, x, beta, y));\n\n        // Reset cuSparse to default stream\n        cusparseCheckError(hipsparseSetStream(handle, 0));\n    #else\n        FatalError(\"Mixed precision modes not currently supported for CUDA 10.1 or later.\", AMGX_ERR_NOT_IMPLEMENTED);\n    #endif\n}\n\n// Custom implementation of matrix-vector product to replace the original bsrxmv,\n// but with block size of 1.\ntemplate<unsigned UNROLL, class T>\n__global__ void csrxmv(\n    int sizeOfMask,\n    const T alpha,\n    const T* __restrict__ csrVal,\n    const int* __restrict__ csrMask,\n    const int* __restrict__ csrRow,\n    const int* __restrict__ csrCol,\n    const T* __restrict__ x,\n    const T beta,\n    T* __restrict__ y)\n{\n    for(int i = threadIdx.x + blockIdx.x*blockDim.x; i < sizeOfMask; i += blockDim.x*gridDim.x)\n    {\n        int row = csrMask[i];\n        T y_tmp = amgx::types::util<T>::get_zero();\n\n        int row_b = csrRow[row];\n        int row_e = csrRow[row+1];\n\n        // Unrolling is important for performance here.\n        // Possible to squeeze more performance out of the key kernels if we\n        // measure the sparsity and use it to inform unrolling.\n        for (int col = row_b; col < row_e; col += UNROLL)\n        {\n#pragma unroll UNROLL\n            for(int off = 0; off < UNROLL; ++off)\n            {\n                int c = col + off;\n                if(c < row_e) y_tmp = alpha * csrVal[c] * x[csrCol[c]] + y_tmp;\n            }\n        }\n\n        // Don't read y unnecessarily\n        if(amgx::types::util<T>::is_zero(beta))\n        {\n            y[row] = y_tmp;\n        }\n        else\n        {\n            y[row] = beta*y[row] + y_tmp;\n        }\n    }\n}\n\n// Replaces the functionality of cusparse?bsrxmv for blockDim == 1\ntemplate<class T>\ninline void Xcsrxmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const T *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const T *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const T *x,\n                                       const T *beta,\n                                       T *y)\n{\n    if(blockDim != 1)\n    {\n        FatalError(\"Xcsrxmv only to be called with scalar matrices.\", AMGX_ERR_INTERNAL);\n    }\n    if (trans != HIPSPARSE_OPERATION_NON_TRANSPOSE)\n    {\n        FatalError(\"Cannot currently latency hide a transposed matrix.\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    if (dir != HIPSPARSE_DIRECTION_ROW)\n    {\n        FatalError(\"Cannot currently latency hide if matrix is not row major.\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    constexpr int cta_size = 128;\n    constexpr int unroll_factor = 16;\n    int nblocks = sizeOfMask / cta_size + 1;\n    csrxmv<unroll_factor><<<nblocks, cta_size>>>(sizeOfMask, *alpha, bsrVal, bsrMaskPtr, bsrRowPtr, bsrColInd, x, *beta, y);\n    cudaCheckError();\n}\n\n// overloaded C++ wrappers for cusparse?bsrxmv\n// bsrxmv\n// matrix - float\n// vector - float\ninline void Cusparse::bsrxmv_internal(hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                      int mb, int nb, int nnzb,\n                                      const float *alpha,\n                                      const hipsparseMatDescr_t descr,\n                                      const float *bsrVal,\n                                      const int *bsrMaskPtr,\n                                      const int *bsrRowPtr,\n                                      const int *bsrEndPtr,\n                                      const int *bsrColInd,\n                                      int blockDim,\n                                      const float *x,\n                                      const float *beta,\n                                      float *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(hipsparseSbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n// bsrxmv\n// matrix - float\n// vector - double\ninline void Cusparse::bsrxmv_internal( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const double *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const float *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const double *x,\n                                       const double *beta,\n                                       double *y)\n{\n    cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\n// bsrxmv\n// matrix - double\n// vector - double\ninline void Cusparse::bsrxmv_internal( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const double *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const double *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const double *x,\n                                       const double *beta,\n                                       double *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(hipsparseDbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const hipComplex *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const hipComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const hipComplex *x,\n                             const hipComplex *beta,\n                             hipComplex *y,\n                             const hipStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(hipsparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, HIP_C_32F, HIP_C_32F, stream);\n    }\n    else\n    {\n        cusparseCheckError(hipsparseCbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(hipsparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const hipDoubleComplex *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const hipDoubleComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const hipDoubleComplex *x,\n                             const hipDoubleComplex *beta,\n                             hipDoubleComplex *y,\n                             const hipStream_t& stream)\n{\n    // Run cuSparse on selected stream\n    cusparseCheckError(hipsparseSetStream(handle, stream));\n\n    if (blockDim == 1)\n    {\n        generic_SpMV(handle, trans, mb, nb, nnzb, rowOff, alpha, bsrVal, bsrRowPtr, bsrColInd, x, beta, y, HIP_C_64F, HIP_C_64F, stream);\n    }\n    else\n    {\n        cusparseCheckError(hipsparseZbsrmv(handle, dir, trans, mb, nb, nnzb, alpha, descr, bsrVal, bsrRowPtr, bsrColInd, blockDim, x, beta, y));\n    }\n\n    // Reset cuSparse to default stream\n    cusparseCheckError(hipsparseSetStream(handle, 0));\n}\n\ninline void Cusparse::bsrmv( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans,\n                             int mb, int nb, int nnzb,\n                             const hipDoubleComplex *alpha,\n                             const hipsparseMatDescr_t descr,\n                             const hipComplex *bsrVal,\n                             const int *bsrMaskPtr,\n                             const int *bsrRowPtr,\n                             const int *bsrColInd,\n                             int rowOff,\n                             int blockDim,\n                             const hipDoubleComplex *x,\n                             const hipDoubleComplex *beta,\n                             hipDoubleComplex *y,\n                             const hipStream_t& stream)\n{\n    #if 0\n        // Run cuSparse on selected stream\n        cusparseCheckError(hipsparseSetStream(handle, stream));\n\n        const hipDoubleComplex *d_bsrVal = reinterpret_cast<hipDoubleComplex *>(const_cast<hipComplex *>(bsrVal));\n        cusparseCheckError(hipsparseZbsrxmv(handle, dir, trans, mb, mb, nb, nnzb, alpha, descr, d_bsrVal, bsrMaskPtr, bsrRowPtr, bsrRowPtr + 1, bsrColInd, blockDim, x, beta, y));\n\n        // Reset cuSparse to default stream\n        cusparseCheckError(hipsparseSetStream(handle, 0));\n    #else\n        FatalError(\"Mixed precision modes not currently supported for CUDA 10.1 or later.\", AMGX_ERR_NOT_IMPLEMENTED);\n    #endif\n}\n\n\n// overloaded C++ wrappers for cusparse?bsrxmv\n// bsrxmv\n// matrix - cuComplex\n// vector - cuComplex\ninline void Cusparse::bsrxmv_internal( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const hipComplex *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const hipComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const hipComplex *x,\n                                       const hipComplex *beta,\n                                       hipComplex *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(hipsparseCbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n// bsrxmv\n// matrix - cuComplex\n// vector - cuDoubleComplex\ninline void Cusparse::bsrxmv_internal( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const hipDoubleComplex *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const hipComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const hipDoubleComplex *x,\n                                       const hipDoubleComplex *beta,\n                                       hipDoubleComplex *y)\n{\n    cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\n// bsrxmv\n// matrix - cuDoubleComplex\n// vector - cuDoubleComplex\ninline void Cusparse::bsrxmv_internal( hipsparseHandle_t handle, hipsparseDirection_t dir, hipsparseOperation_t trans, int sizeOfMask,\n                                       int mb, int nb, int nnzb,\n                                       const hipDoubleComplex *alpha,\n                                       const hipsparseMatDescr_t descr,\n                                       const hipDoubleComplex *bsrVal,\n                                       const int *bsrMaskPtr,\n                                       const int *bsrRowPtr,\n                                       const int *bsrEndPtr,\n                                       const int *bsrColInd,\n                                       int blockDim,\n                                       const hipDoubleComplex *x,\n                                       const hipDoubleComplex *beta,\n                                       hipDoubleComplex *y)\n{\n    if (bsrEndPtr == NULL && bsrMaskPtr == NULL)\n    {\n        cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n    }\n    else\n    {\n        if (bsrEndPtr == NULL)\n        {\n            bsrEndPtr = bsrRowPtr + 1;\n        }\n\n        if (blockDim == 1)\n        {\n            Xcsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y);\n        }\n        else\n        {\n            cusparseCheckError(hipsparseZbsrxmv(handle, dir, trans, sizeOfMask, mb, nb, nnzb, alpha, descr, bsrVal, bsrMaskPtr, bsrRowPtr, bsrEndPtr, bsrColInd, blockDim, x, beta, y));\n        }\n    }\n}\n\n\nnamespace\n{\ntemplate<class MatType, class IndType>\ninline void\ngeneric_SpMM(hipsparseHandle_t handle, hipsparseOperation_t transA,\n             int m, int n, int k, int nnz,\n             int ldb, int ldc,\n             const MatType *alpha,\n             const MatType *Avals,\n             const MatType *Bvals,\n             MatType *Cvals,\n             const IndType *rowPtr,\n             const IndType *colInd,\n             const MatType *beta,\n             hipDataType matType)\n{\n    // Create the matrix descriptors\n    hipsparseSpMatDescr_t matA_descr;\n    hipsparseDnMatDescr_t matB_descr;\n    hipsparseDnMatDescr_t matC_descr;\n    cusparseCheckError(\n        hipsparseCreateCsr(&matA_descr, m, k, nnz, const_cast<IndType*>(rowPtr), const_cast<IndType*>(colInd),\n                          const_cast<MatType*>(Avals), HIPSPARSE_INDEX_32I, HIPSPARSE_INDEX_32I, HIPSPARSE_INDEX_BASE_ZERO, matType));\n    cusparseCheckError(\n        hipsparseCreateDnMat(&matB_descr, k, n, ldb, const_cast<MatType*>(Bvals), matType, HIPSPARSE_ORDER_COL));\n    cusparseCheckError(\n        hipsparseCreateDnMat(&matC_descr, m, n, ldc, const_cast<MatType*>(Cvals), matType, HIPSPARSE_ORDER_COL));\n\n    // Check if a buffer is required, and if so allocate it using caching allocator\n    size_t bufferSize = 0;\n    cusparseCheckError(\n        hipsparseSpMM_bufferSize(handle, transA, HIPSPARSE_OPERATION_NON_TRANSPOSE, alpha, matA_descr, matB_descr,\n                                beta, matC_descr, matType, HIPSPARSE_SPMM_ALG_DEFAULT, &bufferSize));\n\n    void* dBuffer = NULL;\n    if(bufferSize > 0)\n    {\n        hipError_t cuda_rc = amgx::memory::hipMallocAsync(&dBuffer, bufferSize);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipMallocAsync failed in generic_SpMM\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    // Compute the sparse matrix - dense matrix product\n    cusparseCheckError(\n        hipsparseSpMM(handle, transA, HIPSPARSE_OPERATION_NON_TRANSPOSE, alpha, matA_descr, matB_descr, beta,\n                     matC_descr, matType, HIPSPARSE_SPMM_ALG_DEFAULT, dBuffer));\n\n    // Clean up\n    cusparseCheckError(hipsparseDestroySpMat(matA_descr));\n    cusparseCheckError(hipsparseDestroyDnMat(matB_descr));\n    cusparseCheckError(hipsparseDestroyDnMat(matC_descr));\n\n    if(bufferSize > 0)\n    {\n        hipError_t cuda_rc = amgx::memory::hipFreeAsync(dBuffer);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipFreeAsync failed in generic_SpMM\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const float           *alpha,\n               const hipsparseMatDescr_t descrA,\n               const float            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const float            *B, int ldb,\n               const float            *beta, float          *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, HIP_R_32F);\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const double            *alpha,\n               const hipsparseMatDescr_t descrA,\n               const float            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const double            *B, int ldb,\n               const double           *beta, double          *C, int ldc)\n{\n    cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const double          *alpha,\n               const hipsparseMatDescr_t descrA,\n               const double          *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const double           *B, int ldb,\n               const double           *beta, double         *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, HIP_R_64F);\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const hipComplex           *alpha,\n               const hipsparseMatDescr_t descrA,\n               const hipComplex            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const hipComplex            *B, int ldb,\n               const hipComplex            *beta, hipComplex          *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, HIP_C_32F);\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const hipDoubleComplex            *alpha,\n               const hipsparseMatDescr_t descrA,\n               const hipComplex            *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const hipDoubleComplex            *B, int ldb,\n               const hipDoubleComplex           *beta, hipDoubleComplex          *C, int ldc)\n{\n    cusparseCheckError(HIPSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED);\n}\n\nvoid\ncusparse_csrmm(hipsparseHandle_t handle, hipsparseOperation_t transA,\n               int m, int n, int k, int nnz,\n               const hipDoubleComplex          *alpha,\n               const hipsparseMatDescr_t descrA,\n               const hipDoubleComplex          *csrValA,\n               const int *csrRowPtrA, const int *csrColIndA,\n               const hipDoubleComplex           *B, int ldb,\n               const hipDoubleComplex           *beta, hipDoubleComplex         *C, int ldc)\n{\n    generic_SpMM(handle, transA, m, n, k, nnz, ldb, ldc, alpha, csrValA, B, C, csrRowPtrA, csrColIndA, beta, HIP_C_64F);\n}\n}\n\n\ntemplate <class TConfig>\nvoid Cusparse::csrmm(typename TConfig::VecPrec alpha,\n                     Matrix<TConfig> &A,\n                     Vector<TConfig> &V,\n                     typename TConfig::VecPrec beta,\n                     Vector<TConfig> &Res)\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(V, V.tag);\n    }\n\n    if (Res.get_num_rows() != A.get_num_rows() || Res.get_num_cols() != V.get_num_cols())\n    {\n        FatalError(\"Cusparse::csrmm error, dimensions of result matrix do not match input matrices.\", AMGX_ERR_INTERNAL);\n    }\n\n    hipsparseHandle_t handle = Cusparse::get_instance().m_handle;\n    cusparse_csrmm(handle, HIPSPARSE_OPERATION_NON_TRANSPOSE,\n                   A.get_num_rows(), V.get_num_cols(), A.get_num_cols(),\n                   A.values.size(), &alpha, A.cuMatDescr,\n                   A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n                   V.raw(), V.get_lda(),\n                   &beta, Res.raw(), Res.get_lda());\n    Res.dirtybit = 1;\n}\n\ntemplate <class T>\nvoid transpose_internal(hipsparseHandle_t handle, int nRows, int nCols, int nNz, const T* Avals, const int* Arows, const int* Acols, T* Bvals, int* Brows, int* Bcols, hipDataType valType)\n{\n    size_t bufferSize;\n    cusparseCheckError(hipsparseCsr2cscEx2_bufferSize(\n        handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, valType,\n        HIPSPARSE_ACTION_NUMERIC, HIPSPARSE_INDEX_BASE_ZERO, HIPSPARSE_CSR2CSC_ALG1, &bufferSize));\n\n    void *buffer = nullptr;\n    if (bufferSize > 0)\n    {\n        hipError_t cuda_rc = amgx::memory::hipMallocAsync(&buffer, bufferSize);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipMallocAsync failed in transpose_internal\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n\n    cusparseCheckError(hipsparseCsr2cscEx2(\n        handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, valType,\n        HIPSPARSE_ACTION_NUMERIC, HIPSPARSE_INDEX_BASE_ZERO, HIPSPARSE_CSR2CSC_ALG1, buffer));\n\n    if(bufferSize > 0)\n    {\n        hipError_t cuda_rc = amgx::memory::hipFreeAsync(buffer);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipFreeAsync failed in transpose_internal\", AMGX_ERR_CUDA_FAILURE);\n        }\n    }\n}\n\nvoid transpose_internal(hipsparseHandle_t handle, int nRows, int nCols, int nNz, const float* Avals, const int* Arows, const int* Acols, float* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, HIP_R_32F);\n}\nvoid transpose_internal(hipsparseHandle_t handle, int nRows, int nCols, int nNz, const double* Avals, const int* Arows, const int* Acols, double* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, HIP_R_64F);\n}\nvoid transpose_internal(hipsparseHandle_t handle, int nRows, int nCols, int nNz, const hipComplex* Avals, const int* Arows, const int* Acols, hipComplex* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, HIP_C_32F);\n}\nvoid transpose_internal(hipsparseHandle_t handle, int nRows, int nCols, int nNz, const hipDoubleComplex* Avals, const int* Arows, const int* Acols, hipDoubleComplex* Bvals, int* Brows, int* Bcols)\n{\n    transpose_internal(handle, nRows, nCols, nNz, Avals, Arows, Acols, Bvals, Brows, Bcols, HIP_C_64F);\n}\n\ntemplate <class TConfig>\nvoid Cusparse::transpose(const Matrix<TConfig>& A, Matrix<TConfig>& B, const int nRows, const int nNz)\n{\n    hipsparseHandle_t handle = Cusparse::get_instance().m_handle;\n    transpose_internal(handle, nRows, A.get_num_cols(), nNz,\n        A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n        B.values.raw(), B.row_offsets.raw(), B.col_indices.raw());\n}\n\ntemplate <class TConfig>\nvoid Cusparse::transpose(const Matrix<TConfig>& A, Matrix<TConfig>& B)\n{\n    hipsparseHandle_t handle = Cusparse::get_instance().m_handle;\n    transpose_internal(handle, A.get_num_rows(), A.get_num_cols(), A.get_num_nz(),\n        A.values.raw(), A.row_offsets.raw(), A.col_indices.raw(),\n        B.values.raw(), B.row_offsets.raw(), B.col_indices.raw());\n}\n\n//#define AMGX_CASE_LINE(CASE) template class Cusparse<TemplateMode<CASE>::Type>;\n//    AMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrxmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>&, typename Matrix<TemplateMode<CASE>::Type>::IVector&, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv_with_mask(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv_with_mask_restriction(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, Matrix<TemplateMode<CASE>::Type>& );\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, const typename Matrix<TemplateMode<CASE>::Type>::MVector&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(ColumnColorSelector, const int, const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::bsrmv(const int, const typename TemplateMode<CASE>::Type::VecPrec ,  Matrix<TemplateMode<CASE>::Type>&, const typename Matrix<TemplateMode<CASE>::Type>::MVector &, Vector<TemplateMode<CASE>::Type>&, const typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>  &, ViewType);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::csrmm(typename TemplateMode<CASE>::Type::VecPrec, Matrix<TemplateMode<CASE>::Type>&, Vector<TemplateMode<CASE>::Type>&, typename TemplateMode<CASE>::Type::VecPrec, Vector<TemplateMode<CASE>::Type>&);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  template void Cusparse::transpose(const Matrix<TemplateMode<CASE>::Type>& A, Matrix<TemplateMode<CASE>::Type>& B); \\\n  template void Cusparse::transpose(const Matrix<TemplateMode<CASE>::Type>& A, Matrix<TemplateMode<CASE>::Type>& B, const int nRows, const int nNz);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/amgx_cusparse.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <norm.h>\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n#include <blas.h>\n#include <cusp/blas.h>\n#include <basic_types.h>\n#include <util.h>\n#include <types.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n#include <thrust/sort.h>\n#include \"strided_reduction.h\"\n\n#include \"amgx_timer.h\"\n#include \"amgx_types/util.h\"\n#include \"thrust_wrapper.h\"\n\nnamespace amgx\n{\n\n/**********************************************************\n * Returns the norm of a vector\n *********************************************************/\n\ntemplate<class VectorType, class MatrixType>\ntypename types::PODTypes<typename VectorType::value_type>::type get_norm(const MatrixType &A, const VectorType &r, const NormType norm_type, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    typedef typename types::PODTypes<typename VectorType::value_type>::type value_type;\n    value_type nrm;\n    int offset, size;\n    A.getOffsetAndSizeForView(OWNED, &offset, &size);\n\n    if (norm_type == L1 || norm_type == L1_SCALED)\n    {\n        nrm = nrm1(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            A.getManager()->global_reduce_sum(&nrm);\n        }\n\n        return (norm_type == L1_SCALED) ? nrm / norm_factor : nrm;\n    }\n    else if (norm_type == L2)\n    {\n        nrm = nrm2(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            nrm = nrm * nrm;\n            A.getManager()->global_reduce_sum(&nrm);\n            nrm = sqrt(nrm);\n        }\n        \n        return nrm;\n    }\n    else if (norm_type == LMAX)\n    {\n        nrm = nrmmax(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            typedef TemplateConfig<AMGX_host, types::PODTypes<typename VectorType::value_type>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type;\n            typedef Vector<hvector_type> HVector;\n            //collect values from all neighbors, and do the \"reduction\" part\n            std::vector<HVector> values(0);\n            HVector my_nrm(1);\n            my_nrm[0] = nrm;\n            A.getManager()->getComms()->global_reduce(values, my_nrm, A, 3);\n\n            for (int j = 0; j < values.size(); j++)\n            {\n                nrm = (nrm > values[j][0] ? nrm : values[j][0]);\n            }\n        }\n\n        return nrm;\n    }\n\n    return -1;\n}\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nclass Norm_1x1;\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nvoid get_1x1_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    Norm_1x1<VectorType, MatrixType, PlainVectorType>::get_1x1_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n}\n\ntemplate <AMGX_MemorySpace t_memSpace, AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec >\nclass Norm_1x1< Vector<TemplateConfig<t_memSpace, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>>>\n{\n    public:\n        typedef Vector<TemplateConfig<t_memSpace, t_vecPrec, t_matPrec, t_indPrec> > Vector_h;\n        typedef Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>> PODHostVec; // return type\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n\n        static void get_1x1_norm(const MatrixType &A, const Vector_h &r, const int block_size, const NormType norm_type, PODHostVec &block_nrm, typename types::PODTypes<ValueTypeB>::type norm_factor)\n        {\n            //collect values from all neighbors, and do the \"reduction\" part\n            std::vector<PODHostVec> values(0);\n            block_nrm.resize(1);\n            int offset, size;\n            double sum = 0.l;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                block_nrm[0] = nrm1(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 4);\n                    block_nrm[0] = 0;\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        sum += values[j][0];\n                    }\n\n                    block_nrm[0] = sum;\n                }\n\n                if (norm_type == L1_SCALED)\n                {\n                    block_nrm[0] /= norm_factor;\n                }\n            }\n            else if (norm_type == L2)\n            {\n                block_nrm[0] = nrm2(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    block_nrm[0] *= block_nrm[0];\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 5);\n                    block_nrm[0] = 0;\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        sum += values[j][0];\n                    }\n\n                    block_nrm[0] = sqrt(sum);\n                }\n            }\n            else if (norm_type == LMAX)\n            {\n                block_nrm[0] = nrmmax(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 6);\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        block_nrm[0] = (block_nrm[0] > values[j][0] ? block_nrm[0] : values[j][0]);\n                    }\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype is not supported in get_1x1_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        };\n};\n\ntemplate <class Vector, class MatrixType, class PlainVectorType>\nclass Norm_Square;\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nvoid get_sq_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    Norm_Square<VectorType, MatrixType, PlainVectorType>::get_sq_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec >\nclass Norm_Square<Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>>>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h; // host vector of current TConfig(r)\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n\n        static void get_sq_norm(const MatrixType &A, const Vector_h &r, const int block_size, const NormType norm_type, HVector &block_nrm, typename types::PODTypes<ValueTypeB>::type  norm_factor)\n        {\n            int bsize = block_nrm.size();\n            int offset, size;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n            std::vector <double> norm(block_size, 0.l);\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                if ( (size * r.get_block_size()) % bsize != 0)\n                {\n                    FatalError(\"r.size should be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                block_nrm.resize(bsize, 0.);\n                //    for (int j=0;j<bsize;j++)\n                //      block_nrm[j] = 0.;\n                int num_cells = (size * r.get_block_size()) / bsize;\n\n                for (int i = 0; i < num_cells; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        norm[j] += types::util<ValueTypeB>::abs(r[(offset + i) * bsize + j]);\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = norm[j];\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 7);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++) { norm[i] += values[j][i]; }\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = (norm_type == L1_SCALED) ? norm[j] / norm_factor : norm[j];\n                }\n            }\n            else if (norm_type == L2)\n            {\n                if ( (size * r.get_block_size()) % bsize != 0)\n                {\n                    FatalError(\"r.size should be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                block_nrm.resize(bsize, 0.);\n                int num_cells = (size * r.get_block_size()) / bsize;\n\n                for (int i = 0; i < num_cells; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        norm[j] = norm[j] + types::util<ValueTypeB>::abs(r[(offset + i) * bsize + j] * types::util<ValueTypeB>::conjugate(r[(offset + i) * bsize + j]));\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = norm[j];\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 8);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        norm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++) { norm[i] += values[j][i]; }\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = sqrt(norm[j]);\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype not supported in get_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\n__global__\nvoid createReductionMapKernel(int *map, const int bsize, const int map_size)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < map_size; tid += blockDim.x * gridDim.x)\n    {\n        map[tid] = tid % bsize;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec>\nclass Norm_Square<Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>> >\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > Vector_d;\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h; // host vector of current TConfig(r)\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n        static void get_sq_norm(const MatrixType &A, const Vector_d &r, const int block_size, const NormType norm_type, HVector &block_nrm, typename types::PODTypes<ValueTypeB>::type norm_factor)\n        {\n            int bsize = block_nrm.size();\n            int offset, size;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n            const int ncells = (size * r.get_block_size()) / bsize;\n\n            if ( (size * r.get_block_size()) % bsize != 0)\n            {\n                FatalError(\"Size of vector r must be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                amgx::strided_reduction::reduction_generic_dispatch(\n                    bsize,\n                    &block_nrm[0], r.raw() + offset * r.get_block_size(),\n                    size * r.get_block_size(), amgx::strided_reduction::fabs_transform());\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 9);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++)\n                        {\n                            block_nrm[i] += values[j][i];\n                        }\n                    }\n                }\n\n                if (norm_type == L1_SCALED)\n                {\n                    for (int i = 0; i < block_nrm.size(); ++i)\n                    {\n                        block_nrm[i] /= norm_factor;\n                    }\n                }\n            }\n            else if (norm_type == L2)\n            {\n                amgx::strided_reduction::reduction_generic_dispatch(\n                    bsize, &block_nrm[0],\n                    r.raw() + offset * r.get_block_size(), size * r.get_block_size(),\n                    amgx::strided_reduction::square_transform());\n\n                for (int i = 0; i < bsize; i++)\n                {\n                    block_nrm[i] = sqrt(block_nrm[i]);\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] *= block_nrm[i];\n                    }\n\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 10);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++)\n                        {\n                            block_nrm[i] += values[j][i];\n                        }\n                    }\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = sqrt(block_nrm[i]);\n                    }\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype not supported in get_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate<class VectorType, class MatrixType, class PlainVectorType>\nvoid get_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    if (block_size == 1)\n    {\n        get_1x1_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n    }\n    else\n    {\n        get_sq_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n    }\n}\n\ntemplate <class VectorType, class MatrixType>\nclass Norm_Factor;\n\ntemplate <class VectorType, class MatrixType>\nvoid compute_norm_factor(MatrixType &A, VectorType &b, VectorType &x, const NormType normType, typename types::PODTypes<typename VectorType::value_type>::type &normFactor)\n{\n    if(normType == L1_SCALED)\n    {\n        Norm_Factor<VectorType, MatrixType>::compute_norm_factor(A, b, x, normFactor);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType >\nclass Norm_Factor<Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h;\n        typedef typename Vector_h::value_type ValueTypeVec;\n        typedef typename types::PODTypes<ValueTypeVec>::type ValueTypeNorm;\n\n        static void compute_norm_factor(MatrixType &A, Vector_h &b, Vector_h &x, ValueTypeNorm& normFactor)\n        {\n            FatalError(\"L1 scaled norm not supported with host execution.\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n};\n\ntemplate<int warpSize, class ValueTypeMat, class IndexTypeVec, class ValueTypeVec, class ValueTypeNorm>\n__global__ void scaled_norm_factor_calc(\n    int nRows, ValueTypeMat *Avals, IndexTypeVec *Arows, ValueTypeVec *Ax, ValueTypeVec *b, \n    ValueTypeVec xAvg, ValueTypeNorm *localNormFactor, ValueTypeMat *diaVals = nullptr)\n{\n    int r = threadIdx.x + blockIdx.x * blockDim.x;\n\n    __shared__ ValueTypeNorm normFactor_s;\n    if(threadIdx.x == 0)\n    {\n        normFactor_s = amgx::types::util<ValueTypeNorm>::get_zero();\n    }\n\n    ValueTypeNorm normFactor = 0.0;\n\n    if (r < nRows)\n    {\n        ValueTypeMat Arow_sum = amgx::types::util<ValueTypeMat>::get_zero();\n\n        // Read in the row\n#pragma unroll\n        for (int i = Arows[r]; i < Arows[r + 1]; ++i)\n        {\n            Arow_sum = Arow_sum + Avals[i];\n        }\n        if(diaVals) Arow_sum = Arow_sum + diaVals[r];\n\n        normFactor =\n            types::util<ValueTypeVec>::abs(Ax[r] - Arow_sum * xAvg) +\n            types::util<ValueTypeVec>::abs(b[r] - Arow_sum * xAvg);\n    }\n\n    // Ensure normFactor_s is initialised\n    __syncthreads();\n\n    // Warp-local reduction to lane 0\n    for(int i = warpSize/2; i > 0; i /= 2)\n    {\n        normFactor += utils::shfl_down(normFactor, i);\n    }\n\n    // Fast shared atomic add by lane 0 of each warp\n    int laneId = threadIdx.x % warpSize;\n    if(laneId == 0)\n    {\n        utils::atomic_add(&normFactor_s, normFactor);\n    }\n\n    // Ensure normFactor_s is final\n    __syncthreads();\n\n    // Final output of normFactor by first thread of each block\n    if(threadIdx.x == 0)\n    {\n        utils::atomic_add(localNormFactor, normFactor_s);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType>\nclass Norm_Factor<Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > Vector_d;\n        typedef typename Vector_d::value_type ValueTypeVec;\n        typedef typename Vector_d::index_type IndexTypeVec;\n        typedef typename types::PODTypes<ValueTypeVec>::type ValueTypeNorm;\n        typedef TemplateConfig<AMGX_device, types::PODTypes<ValueTypeVec>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> NormVectorType;\n        typedef Vector<NormVectorType> NVector_d;\n\n        static void compute_norm_factor(MatrixType &A, Vector_d &b, Vector_d &x, ValueTypeNorm &normFactor)\n        {\n            if (A.get_block_dimx() != 1 || A.get_block_dimy() != 1)\n            {\n                FatalError(\"L1 scaled norm only supported with scalar matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n\n            // Calculate Ax\n            int offset, nRows;\n            A.getOffsetAndSizeForView(OWNED, &offset, &nRows);\n\n            Vector_d Ax(nRows);\n            A.apply(x, Ax);\n\n            // Calculate global average x\n            ValueTypeVec xAvg = amgx::thrust::reduce(x.begin(), x.begin() + nRows, amgx::types::util<ValueTypeVec>::get_zero());\n\n            int64_t nr = nRows;\n            if (A.is_matrix_distributed())\n            {\n                A.manager->global_reduce_sum(&xAvg);\n                nr = A.manager->num_rows_global;\n            }\n            amgx::types::util<ValueTypeVec>::divide_by_integer(xAvg, nr);\n\n            // Make a copy of b\n            Vector_d bTmp(b);\n\n            // Calculate row sums then the local norm factors\n            constexpr int nThreads = 128;\n            constexpr int warpSize = 32;\n            const int nBlocks = nRows/nThreads + 1;\n            NVector_d localNormFactor(1, amgx::types::util<ValueTypeNorm>::get_zero());\n            if(A.hasProps(DIAG))\n            {\n                scaled_norm_factor_calc<warpSize><<<nBlocks, nThreads>>>(\n                    nRows,\n                    A.values.raw(),\n                    A.row_offsets.raw(),\n                    Ax.raw(),\n                    bTmp.raw(),\n                    xAvg,\n                    localNormFactor.raw(),\n                    A.values.raw() + A.diagOffset()*A.get_block_size());\n                cudaCheckError();\n            }\n            else\n            {\n                scaled_norm_factor_calc<warpSize><<<nBlocks, nThreads>>>(\n                    nRows,\n                    A.values.raw(),\n                    A.row_offsets.raw(),\n                    Ax.raw(),\n                    bTmp.raw(),\n                    xAvg,\n                    localNormFactor.raw());\n                cudaCheckError();\n            }\n\n            // Fetch the normFactor result and reduce across all ranks\n            normFactor = localNormFactor[0];\n\n            if (A.is_matrix_distributed())\n            {\n                A.manager->global_reduce_sum(&normFactor);\n            }\n\n            // Print the norm factor\n            std::stringstream info;\n            info.precision(12);\n            info << \"\\tAmgX Scaled Norm Factor: \" << std::scientific << normFactor << \"\\n\";\n            amgx_output(info.str().c_str(), info.str().length());\n        }\n};\n\n#define AMGX_CASE_LINE(CASE) template typename types::PODTypes< typename Vector<TemplateMode<CASE>::Type>::value_type>::type get_norm(const Matrix<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const NormType norm_type, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template typename types::PODTypes< typename Vector<TemplateMode<CASE>::Type>::value_type>::type get_norm(const Operator<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const NormType norm_type, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  typedef typename Vector< TemplateMode<CASE>::Type >::value_type ValueTypeMB##CASE ;\\\n  typedef TemplateMode<CASE>::Type::template setMemSpace<AMGX_host>::Type::template setVecPrec< types::PODTypes< ValueTypeMB##CASE >::vec_prec >::Type CurTConfigMB_h##CASE ;\\\n  template void get_norm(const Matrix<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const int block_size, const NormType norm_type, Vector< CurTConfigMB_h##CASE >& block_nrm, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor); \\\n  template void compute_norm_factor(Matrix<TemplateMode<CASE>::Type> &A, Vector<TemplateMode<CASE>::Type> &b, Vector<TemplateMode<CASE>::Type> &x, const NormType normType, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type &normFactor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  typedef typename Vector< TemplateMode<CASE>::Type >::value_type ValueTypeOB##CASE ;\\\n  typedef TemplateMode<CASE>::Type::template setMemSpace<AMGX_host>::Type::template setVecPrec< types::PODTypes< ValueTypeOB##CASE >::vec_prec >::Type CurTConfigOB_h##CASE ;\\\n  template void get_norm(const Operator<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const int block_size, const NormType norm_type, Vector< CurTConfigOB_h##CASE >& block_nrm, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <norm.h>\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n#include <blas.h>\n#include <cusp/blas.h>\n#include <basic_types.h>\n#include <util.h>\n#include <types.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n#include <thrust/sort.h>\n#include \"strided_reduction.h\"\n\n#include \"amgx_timer.h\"\n#include \"amgx_types/util.h\"\n#include \"thrust_wrapper.h\"\n\nnamespace amgx\n{\n\n/**********************************************************\n * Returns the norm of a vector\n *********************************************************/\n\ntemplate<class VectorType, class MatrixType>\ntypename types::PODTypes<typename VectorType::value_type>::type get_norm(const MatrixType &A, const VectorType &r, const NormType norm_type, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    typedef typename types::PODTypes<typename VectorType::value_type>::type value_type;\n    value_type nrm;\n    int offset, size;\n    A.getOffsetAndSizeForView(OWNED, &offset, &size);\n\n    if (norm_type == L1 || norm_type == L1_SCALED)\n    {\n        nrm = nrm1(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            A.getManager()->global_reduce_sum(&nrm);\n        }\n\n        return (norm_type == L1_SCALED) ? nrm / norm_factor : nrm;\n    }\n    else if (norm_type == L2)\n    {\n        nrm = nrm2(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            nrm = nrm * nrm;\n            A.getManager()->global_reduce_sum(&nrm);\n            nrm = sqrt(nrm);\n        }\n        \n        return nrm;\n    }\n    else if (norm_type == LMAX)\n    {\n        nrm = nrmmax(r, offset, size);\n\n        if (A.is_matrix_distributed())\n        {\n            typedef TemplateConfig<AMGX_host, types::PODTypes<typename VectorType::value_type>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type;\n            typedef Vector<hvector_type> HVector;\n            //collect values from all neighbors, and do the \"reduction\" part\n            std::vector<HVector> values(0);\n            HVector my_nrm(1);\n            my_nrm[0] = nrm;\n            A.getManager()->getComms()->global_reduce(values, my_nrm, A, 3);\n\n            for (int j = 0; j < values.size(); j++)\n            {\n                nrm = (nrm > values[j][0] ? nrm : values[j][0]);\n            }\n        }\n\n        return nrm;\n    }\n\n    return -1;\n}\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nclass Norm_1x1;\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nvoid get_1x1_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    Norm_1x1<VectorType, MatrixType, PlainVectorType>::get_1x1_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n}\n\ntemplate <AMGX_MemorySpace t_memSpace, AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec >\nclass Norm_1x1< Vector<TemplateConfig<t_memSpace, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>>>\n{\n    public:\n        typedef Vector<TemplateConfig<t_memSpace, t_vecPrec, t_matPrec, t_indPrec> > Vector_h;\n        typedef Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>> PODHostVec; // return type\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n\n        static void get_1x1_norm(const MatrixType &A, const Vector_h &r, const int block_size, const NormType norm_type, PODHostVec &block_nrm, typename types::PODTypes<ValueTypeB>::type norm_factor)\n        {\n            //collect values from all neighbors, and do the \"reduction\" part\n            std::vector<PODHostVec> values(0);\n            block_nrm.resize(1);\n            int offset, size;\n            double sum = 0.l;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                block_nrm[0] = nrm1(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 4);\n                    block_nrm[0] = 0;\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        sum += values[j][0];\n                    }\n\n                    block_nrm[0] = sum;\n                }\n\n                if (norm_type == L1_SCALED)\n                {\n                    block_nrm[0] /= norm_factor;\n                }\n            }\n            else if (norm_type == L2)\n            {\n                block_nrm[0] = nrm2(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    block_nrm[0] *= block_nrm[0];\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 5);\n                    block_nrm[0] = 0;\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        sum += values[j][0];\n                    }\n\n                    block_nrm[0] = sqrt(sum);\n                }\n            }\n            else if (norm_type == LMAX)\n            {\n                block_nrm[0] = nrmmax(r, offset, size);\n\n                if (A.is_matrix_distributed())\n                {\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 6);\n\n                    for (int j = 0; j < values.size(); j++)\n                    {\n                        block_nrm[0] = (block_nrm[0] > values[j][0] ? block_nrm[0] : values[j][0]);\n                    }\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype is not supported in get_1x1_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        };\n};\n\ntemplate <class Vector, class MatrixType, class PlainVectorType>\nclass Norm_Square;\n\ntemplate <class VectorType, class MatrixType, class PlainVectorType>\nvoid get_sq_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    Norm_Square<VectorType, MatrixType, PlainVectorType>::get_sq_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec >\nclass Norm_Square<Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>>>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h; // host vector of current TConfig(r)\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n\n        static void get_sq_norm(const MatrixType &A, const Vector_h &r, const int block_size, const NormType norm_type, HVector &block_nrm, typename types::PODTypes<ValueTypeB>::type  norm_factor)\n        {\n            int bsize = block_nrm.size();\n            int offset, size;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n            std::vector <double> norm(block_size, 0.l);\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                if ( (size * r.get_block_size()) % bsize != 0)\n                {\n                    FatalError(\"r.size should be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                block_nrm.resize(bsize, 0.);\n                //    for (int j=0;j<bsize;j++)\n                //      block_nrm[j] = 0.;\n                int num_cells = (size * r.get_block_size()) / bsize;\n\n                for (int i = 0; i < num_cells; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        norm[j] += types::util<ValueTypeB>::abs(r[(offset + i) * bsize + j]);\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = norm[j];\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 7);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++) { norm[i] += values[j][i]; }\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = (norm_type == L1_SCALED) ? norm[j] / norm_factor : norm[j];\n                }\n            }\n            else if (norm_type == L2)\n            {\n                if ( (size * r.get_block_size()) % bsize != 0)\n                {\n                    FatalError(\"r.size should be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                block_nrm.resize(bsize, 0.);\n                int num_cells = (size * r.get_block_size()) / bsize;\n\n                for (int i = 0; i < num_cells; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        norm[j] = norm[j] + types::util<ValueTypeB>::abs(r[(offset + i) * bsize + j] * types::util<ValueTypeB>::conjugate(r[(offset + i) * bsize + j]));\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = norm[j];\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 8);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        norm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++) { norm[i] += values[j][i]; }\n                    }\n                }\n\n                for (int j = 0; j < bsize; j++)\n                {\n                    block_nrm[j] = sqrt(norm[j]);\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype not supported in get_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\n__global__\nvoid createReductionMapKernel(int *map, const int bsize, const int map_size)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < map_size; tid += blockDim.x * gridDim.x)\n    {\n        map[tid] = tid % bsize;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType, AMGX_VecPrecision t_pod_vecPrec>\nclass Norm_Square<Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType, Vector<TemplateConfig<AMGX_host, t_pod_vecPrec, t_matPrec, t_indPrec>> >\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > Vector_d;\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h; // host vector of current TConfig(r)\n        typedef typename Vector_h::value_type ValueTypeB; // vector's valuetype\n        typedef TemplateConfig<AMGX_host, types::PODTypes<ValueTypeB>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> hvector_type; // TConfig host with pod-values for ValueTypeB\n        typedef Vector<hvector_type> HVector; //vectors for saving norms from allgather\n        static void get_sq_norm(const MatrixType &A, const Vector_d &r, const int block_size, const NormType norm_type, HVector &block_nrm, typename types::PODTypes<ValueTypeB>::type norm_factor)\n        {\n            int bsize = block_nrm.size();\n            int offset, size;\n            A.getOffsetAndSizeForView(OWNED, &offset, &size);\n            const int ncells = (size * r.get_block_size()) / bsize;\n\n            if ( (size * r.get_block_size()) % bsize != 0)\n            {\n                FatalError(\"Size of vector r must be multiple of block size\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            if (norm_type == L1 || norm_type == L1_SCALED)\n            {\n                amgx::strided_reduction::reduction_generic_dispatch(\n                    bsize,\n                    &block_nrm[0], r.raw() + offset * r.get_block_size(),\n                    size * r.get_block_size(), amgx::strided_reduction::fabs_transform());\n\n                if (A.is_matrix_distributed())\n                {\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 9);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++)\n                        {\n                            block_nrm[i] += values[j][i];\n                        }\n                    }\n                }\n\n                if (norm_type == L1_SCALED)\n                {\n                    for (int i = 0; i < block_nrm.size(); ++i)\n                    {\n                        block_nrm[i] /= norm_factor;\n                    }\n                }\n            }\n            else if (norm_type == L2)\n            {\n                amgx::strided_reduction::reduction_generic_dispatch(\n                    bsize, &block_nrm[0],\n                    r.raw() + offset * r.get_block_size(), size * r.get_block_size(),\n                    amgx::strided_reduction::square_transform());\n\n                for (int i = 0; i < bsize; i++)\n                {\n                    block_nrm[i] = sqrt(block_nrm[i]);\n                }\n\n                if (A.is_matrix_distributed())\n                {\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] *= block_nrm[i];\n                    }\n\n                    //collect values from all neighbors, and do the \"reduction\" part\n                    std::vector<HVector> values(0);\n                    A.getManager()->getComms()->global_reduce(values, block_nrm, A, 10);\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = 0;\n\n                        for (int j = 0; j < values.size(); j++)\n                        {\n                            block_nrm[i] += values[j][i];\n                        }\n                    }\n\n                    for (int i = 0; i < bsize; i++)\n                    {\n                        block_nrm[i] = sqrt(block_nrm[i]);\n                    }\n                }\n            }\n            else\n            {\n                FatalError(\"Normtype not supported in get_norm\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n        }\n};\n\ntemplate<class VectorType, class MatrixType, class PlainVectorType>\nvoid get_norm(const MatrixType &A, const VectorType &r, const int block_size, const NormType norm_type, PlainVectorType &block_nrm, typename types::PODTypes<typename VectorType::value_type>::type norm_factor)\n{\n    if (block_size == 1)\n    {\n        get_1x1_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n    }\n    else\n    {\n        get_sq_norm(A, r, block_size, norm_type, block_nrm, norm_factor);\n    }\n}\n\ntemplate <class VectorType, class MatrixType>\nclass Norm_Factor;\n\ntemplate <class VectorType, class MatrixType>\nvoid compute_norm_factor(MatrixType &A, VectorType &b, VectorType &x, const NormType normType, typename types::PODTypes<typename VectorType::value_type>::type &normFactor)\n{\n    if(normType == L1_SCALED)\n    {\n        Norm_Factor<VectorType, MatrixType>::compute_norm_factor(A, b, x, normFactor);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType >\nclass Norm_Factor<Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > Vector_h;\n        typedef typename Vector_h::value_type ValueTypeVec;\n        typedef typename types::PODTypes<ValueTypeVec>::type ValueTypeNorm;\n\n        static void compute_norm_factor(MatrixType &A, Vector_h &b, Vector_h &x, ValueTypeNorm& normFactor)\n        {\n            FatalError(\"L1 scaled norm not supported with host execution.\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n};\n\ntemplate<int warpSize, class ValueTypeMat, class IndexTypeVec, class ValueTypeVec, class ValueTypeNorm>\n__global__ void scaled_norm_factor_calc(\n    int nRows, ValueTypeMat *Avals, IndexTypeVec *Arows, ValueTypeVec *Ax, ValueTypeVec *b, \n    ValueTypeVec xAvg, ValueTypeNorm *localNormFactor, ValueTypeMat *diaVals = nullptr)\n{\n    int r = threadIdx.x + blockIdx.x * blockDim.x;\n\n    __shared__ ValueTypeNorm normFactor_s;\n    if(threadIdx.x == 0)\n    {\n        normFactor_s = amgx::types::util<ValueTypeNorm>::get_zero();\n    }\n\n    ValueTypeNorm normFactor = 0.0;\n\n    if (r < nRows)\n    {\n        ValueTypeMat Arow_sum = amgx::types::util<ValueTypeMat>::get_zero();\n\n        // Read in the row\n#pragma unroll\n        for (int i = Arows[r]; i < Arows[r + 1]; ++i)\n        {\n            Arow_sum = Arow_sum + Avals[i];\n        }\n        if(diaVals) Arow_sum = Arow_sum + diaVals[r];\n\n        normFactor =\n            types::util<ValueTypeVec>::abs(Ax[r] - Arow_sum * xAvg) +\n            types::util<ValueTypeVec>::abs(b[r] - Arow_sum * xAvg);\n    }\n\n    // Ensure normFactor_s is initialised\n    __syncthreads();\n\n    // Warp-local reduction to lane 0\n    for(int i = warpSize/2; i > 0; i /= 2)\n    {\n        normFactor += utils::shfl_down(normFactor, i);\n    }\n\n    // Fast shared atomic add by lane 0 of each warp\n    int laneId = threadIdx.x % warpSize;\n    if(laneId == 0)\n    {\n        utils::atomic_add(&normFactor_s, normFactor);\n    }\n\n    // Ensure normFactor_s is final\n    __syncthreads();\n\n    // Final output of normFactor by first thread of each block\n    if(threadIdx.x == 0)\n    {\n        utils::atomic_add(localNormFactor, normFactor_s);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec, class MatrixType>\nclass Norm_Factor<Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >, MatrixType>\n{\n    public:\n        typedef Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > Vector_d;\n        typedef typename Vector_d::value_type ValueTypeVec;\n        typedef typename Vector_d::index_type IndexTypeVec;\n        typedef typename types::PODTypes<ValueTypeVec>::type ValueTypeNorm;\n        typedef TemplateConfig<AMGX_device, types::PODTypes<ValueTypeVec>::vec_prec, MatrixType::TConfig::matPrec, MatrixType::TConfig::indPrec> NormVectorType;\n        typedef Vector<NormVectorType> NVector_d;\n\n        static void compute_norm_factor(MatrixType &A, Vector_d &b, Vector_d &x, ValueTypeNorm &normFactor)\n        {\n            if (A.get_block_dimx() != 1 || A.get_block_dimy() != 1)\n            {\n                FatalError(\"L1 scaled norm only supported with scalar matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n\n            // Calculate Ax\n            int offset, nRows;\n            A.getOffsetAndSizeForView(OWNED, &offset, &nRows);\n\n            Vector_d Ax(nRows);\n            A.apply(x, Ax);\n\n            // Calculate global average x\n            ValueTypeVec xAvg = amgx::thrust::reduce(x.begin(), x.begin() + nRows, amgx::types::util<ValueTypeVec>::get_zero());\n\n            int64_t nr = nRows;\n            if (A.is_matrix_distributed())\n            {\n                A.manager->global_reduce_sum(&xAvg);\n                nr = A.manager->num_rows_global;\n            }\n            amgx::types::util<ValueTypeVec>::divide_by_integer(xAvg, nr);\n\n            // Make a copy of b\n            Vector_d bTmp(b);\n\n            // Calculate row sums then the local norm factors\n            constexpr int nThreads = 128;\n            constexpr int warpSize = 32;\n            const int nBlocks = nRows/nThreads + 1;\n            NVector_d localNormFactor(1, amgx::types::util<ValueTypeNorm>::get_zero());\n            if(A.hasProps(DIAG))\n            {\n                scaled_norm_factor_calc<warpSize><<<nBlocks, nThreads>>>(\n                    nRows,\n                    A.values.raw(),\n                    A.row_offsets.raw(),\n                    Ax.raw(),\n                    bTmp.raw(),\n                    xAvg,\n                    localNormFactor.raw(),\n                    A.values.raw() + A.diagOffset()*A.get_block_size());\n                cudaCheckError();\n            }\n            else\n            {\n                scaled_norm_factor_calc<warpSize><<<nBlocks, nThreads>>>(\n                    nRows,\n                    A.values.raw(),\n                    A.row_offsets.raw(),\n                    Ax.raw(),\n                    bTmp.raw(),\n                    xAvg,\n                    localNormFactor.raw());\n                cudaCheckError();\n            }\n\n            // Fetch the normFactor result and reduce across all ranks\n            normFactor = localNormFactor[0];\n\n            if (A.is_matrix_distributed())\n            {\n                A.manager->global_reduce_sum(&normFactor);\n            }\n\n            // Print the norm factor\n            std::stringstream info;\n            info.precision(12);\n            info << \"\\tAmgX Scaled Norm Factor: \" << std::scientific << normFactor << \"\\n\";\n            amgx_output(info.str().c_str(), info.str().length());\n        }\n};\n\n#define AMGX_CASE_LINE(CASE) template typename types::PODTypes< typename Vector<TemplateMode<CASE>::Type>::value_type>::type get_norm(const Matrix<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const NormType norm_type, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template typename types::PODTypes< typename Vector<TemplateMode<CASE>::Type>::value_type>::type get_norm(const Operator<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const NormType norm_type, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  typedef typename Vector< TemplateMode<CASE>::Type >::value_type ValueTypeMB##CASE ;\\\n  typedef TemplateMode<CASE>::Type::template setMemSpace<AMGX_host>::Type::template setVecPrec< types::PODTypes< ValueTypeMB##CASE >::vec_prec >::Type CurTConfigMB_h##CASE ;\\\n  template void get_norm(const Matrix<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const int block_size, const NormType norm_type, Vector< CurTConfigMB_h##CASE >& block_nrm, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor); \\\n  template void compute_norm_factor(Matrix<TemplateMode<CASE>::Type> &A, Vector<TemplateMode<CASE>::Type> &b, Vector<TemplateMode<CASE>::Type> &x, const NormType normType, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type &normFactor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) \\\n  typedef typename Vector< TemplateMode<CASE>::Type >::value_type ValueTypeOB##CASE ;\\\n  typedef TemplateMode<CASE>::Type::template setMemSpace<AMGX_host>::Type::template setVecPrec< types::PODTypes< ValueTypeOB##CASE >::vec_prec >::Type CurTConfigOB_h##CASE ;\\\n  template void get_norm(const Operator<TemplateMode<CASE>::Type>& A, const Vector<TemplateMode<CASE>::Type>& r, const int block_size, const NormType norm_type, Vector< CurTConfigOB_h##CASE >& block_nrm, typename types::PODTypes<typename Vector<TemplateMode<CASE>::Type>::value_type>::type norm_factor);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/norm.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <thrust/count.h>\n#include <thrust/functional.h>\n#include <thrust/inner_product.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n#include <thrust/transform_scan.h>\n#include <thrust_wrapper.h>\n\n#include <energymin/interpolators/em.h>\n#include <energymin/interpolators/common.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <cmath> // std::signbit\n#include <assert.h>\n\n#include \"util.h\"\n\n#include \"amgx_cublas.h\"\n\nnamespace amgx\n{\nnamespace energymin\n{\n\n\n/*************************************************************************\n* create the interpolation matrix P by computing p and using energy minimization\n************************************************************************/\n\n\n/*************************************************************************\n* create the interpolation matrix P on GPU\n************************************************************************/\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid count_PnnzPerCol_kernel( const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n                              const ValueType *Adiag, const IndexType numCoarse,\n                              const int *cf_map, const int *fullToCoarseMap, const int *coarse_idx_ptr,\n                              int *PnnzPerCol, ValueType *thresh_per_row )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numCoarse; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int row_idx = coarse_idx_ptr[tIdx];\n        const int ArowBegin = ArowOffsets[row_idx];\n        const int ArowEnd   = ArowOffsets[row_idx + 1];\n        // Matlab pseudo-code:\n        // p = sparse(n, nc);\n        // p(coarse,:) = speye(nc);\n        // fAj = AD(j,fine)/AD(j,j) < -.1;  // fine neighbors of j with oppositive sign from AD_jj\n        // become the non-zeros in initial P.\n        ValueType minVal(0), maxVal(0), diag_value(0);\n        ValueType fine_avg_thresh = 0.;\n        const ValueType thresh = (ValueType)0.25;\n\n        for (int j = ArowBegin; j < ArowEnd; j++)\n        {\n            ValueType aValue = Avalues[j];\n\n            if ( j != tIdx )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n            else\n            {\n                diag_value = aValue;\n            }\n        }\n\n        if ( diag_value < 0.0)\n        {\n            fine_avg_thresh = thresh * maxVal;\n        }\n        else\n        {\n            fine_avg_thresh = thresh * minVal;\n        }\n\n        thresh_per_row[tIdx] = fine_avg_thresh;\n        int p_col_nnz = 0;\n\n        for (int j = ArowBegin; j < ArowEnd; j++)\n        {\n            int AjColIndex = AcolInd[j];\n\n            if (AjColIndex == tIdx)\n            {\n                p_col_nnz++;\n                continue;\n            }\n\n            if (cf_map[AjColIndex] == FINE)\n            {\n                if (diag_value * Avalues[j] < diag_value * fine_avg_thresh)\n                {\n                    p_col_nnz++;\n                }\n            }\n        }\n\n        PnnzPerCol[tIdx] = p_col_nnz;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid init_ProwInd_greedy_aggregation_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n        const ValueType *Adiag, const IndexType AnumRows,\n        const int *cf_map, const int *fullToCoarseMap,\n        const IndexType *PcolOffsets, IndexType *ProwInd,\n        bool *Ma_nzDiagRows)\n{\n    // for each coarse point form aggregates\n    /* Matlab-like pseudo-code\n    p = sparse(n, nc);\n    p(coarse,:) = speye(nc);\n    for (int j : coarse)              // coarse point with FULL index j\n      fAj = AD(j,fine)/AD(j,j) < -.1; // fine neighbors of j with oppositive sign from AD_jj\n      P(fine(fAj),cj)=1;              // become the non-zeros in initial P.\n    end\n    */\n\n    // size of P and its column offsets were precomputed.\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time (builds a column of P)\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == COARSE)     // \"coarse\" row of A\n        {\n            // P is CSC format since each thread works on each coarse point\n            // and is therefore building a single column of P.\n            // There are numCoarse columns of P\n            // Columns of P correspond to rows of A (A is assumed symmetric)\n            bool PcolCoarseDiagUnset = 1;\n            const int PcolIndex = fullToCoarseMap[tIdx];\n            int PentryNum = PcolOffsets[PcolIndex];  // starting location of the current column of P\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (cf_map[AjColIndex] == FINE && Avalues[j] / Adiag[tIdx] < -.1)\n                {\n                    //p(AjColIndex, fullToCoarseMap(j))=1;\n                    if (AjColIndex > tIdx && PcolCoarseDiagUnset)     // COARSE diagonal of P is identity\n                    {\n                        // set COARSE diagonal of P\n                        ProwInd[PentryNum] = tIdx; // set COARSE diagonal entry of P to 1.\n                        PentryNum++;\n                        PcolCoarseDiagUnset = 0;\n                        Ma_nzDiagRows[tIdx] = 1;    // record the newly added row index of P\n                    }\n\n                    ProwInd[PentryNum] = AjColIndex; // columns of A become rows in P (since A is assumed symmetric)\n                    PentryNum++;\n                    Ma_nzDiagRows[AjColIndex] = 1;    // record the newly added row index of P\n                }\n            }\n\n            if (PcolCoarseDiagUnset)     // COARSE diagonal of P is identity\n            {\n                // set COARSE diagonal of P\n                ProwInd[PentryNum] = tIdx; // columns of A become rows in P (since A is assumed symmetric)\n                PentryNum++;\n                PcolCoarseDiagUnset = 0;\n                Ma_nzDiagRows[tIdx] = 1;    // record the newly added row index of P\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  extract_dense_Aijs_col_major_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n        const IndexType *PcolOffsets, const IndexType *ProwInd, const IndexType PnumCols,\n        const IndexType *AijOffsets, ValueType *dense_Aijs)\n{\n    /* Matlab-like pseudo-code\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the nz pattern in the j-th column of P\n      ij = find(p(:,j));              // col vec of subindices\n      Aij{j} = A(ij,ij);\n    }\n    */\n\n    // each thread builds one Aij\n    for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works with one column of P\n        const unsigned int PcolBegin  = PcolOffsets[tIdx];\n        const unsigned int PcolEnd    = PcolOffsets[tIdx + 1];\n        const unsigned int AijNumRows = PcolEnd - PcolBegin;\n        // pointer to the beginning of the specific Aij submatrix\n        ValueType *dense_Aij = dense_Aijs + AijOffsets[tIdx];\n\n        for (unsigned int i = PcolBegin; i < PcolEnd; i++)\n        {\n            // go thru each non-zero row of the given column of P\n            // and scan the corresponding rows of A\n            unsigned int PiRowIndex = ProwInd[i];\n            const unsigned int ArowBegin = ArowOffsets[PiRowIndex];\n            const unsigned int ArowEnd   = ArowOffsets[PiRowIndex + 1];\n\n            for (unsigned int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // scan the current row of A and pick the elts based on the sparsity pattern of P\n                unsigned int AjColIndex = AcolInd[j];\n\n                // if AjColIndex is an index of a non-zero entry in the current column of P, take it.\n                for (unsigned int ii = PcolBegin; ii < PcolEnd; ii++)\n                {\n                    if (ProwInd[ii] == AjColIndex)\n                    {\n                        // add elt to Aij in column-major format\n                        unsigned int AijRowIndex = i - PcolBegin;\n                        unsigned int AijColIndex = ii - PcolBegin;\n                        dense_Aij[AijColIndex * AijNumRows + AijRowIndex] = Avalues[j]; //wrong\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n// kernel to initialize invAij's to dense identity matrices\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  init_dense_invAijs_col_major_kernel(const IndexType *PcolOffsets, const IndexType PnumCols,\n        const IndexType *AijOffsets, ValueType *dense_invAijs)\n{\n    for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread initializes one invAij\n        const unsigned int PcolBegin  = PcolOffsets[tIdx];\n        const unsigned int PcolEnd    = PcolOffsets[tIdx + 1];\n        const unsigned int AijNumRows = PcolEnd - PcolBegin;\n        // pointer to the beginning of the specific invAij submatrix\n        ValueType *dense_invAij = dense_invAijs + AijOffsets[tIdx];\n\n        for (unsigned int i = 0; i < AijNumRows; i++)\n        {\n            dense_invAij[i * AijNumRows + i] = 1;\n        }\n    }\n}\n\n\n\n\n__global__\nvoid  count_Ma_nnz_per_row_kernel(const int *PcolOffsets, const int *ProwInd, const int PnumCols,\n                                  int *Ma_nnzPerRow, const int AnumRows, const bool *Ma_nzDiagRows)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // every thread works on a single row of Ma (figures out nnz in that row)\n        bool rowNotFilled = 1;          // marks whether the Ma row nnz count has been initialized\n        int nnzPerRow = 0;\n\n        for (int j = 0; j < PnumCols; j++)\n        {\n            // each thread goes thru all columns of P\n            const int PjColBegin  = PcolOffsets[j];\n            const int PjColEnd    = PcolOffsets[j + 1];\n            //const int PjCol_nnz   = PjColEnd - PjColBegin;\n\n            // check that the column is relevant (i.e. has a non-zero in the tIdx row)\n            if (tIdx < ProwInd[PjColBegin] || ProwInd[PjColEnd - 1] < tIdx) { continue; }\n\n            // Make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n            bool relevantCol = 0;\n\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                if (ProwInd[i] == tIdx) { relevantCol = 1; break; }\n            }\n\n            if (!relevantCol) { continue; }\n\n            if (rowNotFilled)\n            {\n                nnzPerRow = PjColEnd - PjColBegin;  // initial nnz per row of Ma\n                rowNotFilled = 0;\n                continue;\n            }\n\n            // if the row nnz count has been initialized, we need to figure out which elts haven't already appeared\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                // grab a row in the current column j of P\n                const int PiRowIndex = ProwInd[i];\n                bool rowPrevAppeared = 0;\n\n                for (int jj = j - 1; jj >= 0; jj--) // scan columns of P backwards since it's more likely that the same row entry\n                {\n                    // appeared in the direct neighbors of the current column\n                    const int PjjColBegin  = PcolOffsets[jj];\n                    const int PjjColEnd    = PcolOffsets[jj + 1];\n\n                    // check that the column is relevant (i.e. has a non-zero in the tIdx row)\n                    if (tIdx < ProwInd[PjjColBegin] || ProwInd[PjjColEnd - 1] < tIdx) { continue; }\n\n                    if (PiRowIndex < ProwInd[PjjColBegin] || ProwInd[PjjColEnd - 1] < PiRowIndex) { continue; }\n\n                    // make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n                    bool relevantPcol = 0;\n\n                    for (int ii = PjjColBegin; ii < PjjColEnd; ii++)\n                    {\n                        if (ProwInd[ii] == tIdx) { relevantPcol = 1; break; }\n                    }\n\n                    if (!relevantPcol) { continue; }\n\n                    // see if PiRowIndex has already appeared previously\n                    for (int ii = PjjColBegin; ii < PjjColEnd; ii++)\n                    {\n                        if (ProwInd[ii] == PiRowIndex) { rowPrevAppeared = 1; break; }\n                    }\n\n                    if (rowPrevAppeared) { break; } // stop scanning columns of P, since we already accounted for PiRowIndex previously\n                }\n\n                if (!rowPrevAppeared)  // if we haven't come across the P column with the nz in the PiRowIndex,\n                {\n                    // then we can increment the row nnz count\n                    nnzPerRow++;\n                }\n            }\n        }\n\n        if (nnzPerRow == 0)           { Ma_nnzPerRow[tIdx] = 1; }\n        else if (Ma_nzDiagRows[tIdx]) { Ma_nnzPerRow[tIdx] = nnzPerRow; }\n        else                          { Ma_nnzPerRow[tIdx] = nnzPerRow + 1; }\n    }\n}\n\n\n/*\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  find_Ma_nz_diag_rows_kernel(const IndexType *PcolOffsets, const IndexType *ProwInd,\n                                  const IndexType PnumCols, bool *Ma_nzDiagRows)\n{\n  for (unsigned int tIdx = threadIdx.x + blockDim.x*blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x*gridDim.x)\n  { // each thread goes thru a single column of P\n\n    const unsigned int PcolBegin = PcolOffsets[tIdx];\n    const unsigned int PcolEnd   = PcolOffsets[tIdx+1];\n\n    for (unsigned int i=PcolBegin; i<PcolEnd; i++)\n    {\n      if (tIdx == ProwInd[i])\n      {\n        Ma_nzDiagRows[tIdx] = 1;  // Mark the non-zero diagonal rows of Ma\n        break;\n      }\n    }\n\n  }\n}\n*/\n\n__global__\nvoid  get_unsorted_MaColInd_kernel( const int *PcolOffsets, const int *ProwInd, const int PnumCols,\n                                    const int *MaRowOffsets, int *MaColInd, const int AnumRows,\n                                    const bool *Ma_nzDiagRows )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on a single row of Ma (figures out column indices for that row)\n        int rowIsFilled = 0;         // indicates that MaColInd has not been initialized\n        const int MaRowBegin = MaRowOffsets[tIdx];\n        const int MaRowEnd   = MaRowOffsets[tIdx + 1];\n        const int MaRowSize  = MaRowEnd - MaRowBegin;\n\n        // Skip empty rows of Ma (should never happen if Ma_nzDiagRows is initialized ??)\n\n        if (MaRowSize <= 1)\n        {\n            if (MaRowSize == 0)\n            {\n                printf(\"\\nWARNING: found zero row of Ma in row %d. Ma_nzDiagRows[%d] = %d \\n\",\n                       tIdx, tIdx, Ma_nzDiagRows[tIdx]);\n            }\n            else { MaColInd[MaRowBegin] = tIdx; }   // diagonal entry is the only one.\n\n            continue;\n        }\n\n        int MaIndex = MaRowBegin;    // keeps count of the number of assigned indices to the Ma row\n\n        for (int j = 0; j < PnumCols; j++)\n        {\n            // thread goes thru all columns of P\n            const int PjColBegin = PcolOffsets[j];\n            const int PjColEnd   = PcolOffsets[j + 1];\n\n            // Check that the column is relevant (i.e. has a non-zero in the tIdx row)\n            if (tIdx < ProwInd[PjColBegin] || ProwInd[PjColEnd - 1] < tIdx) { continue; }\n\n            // Make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n            bool relevantCol = 0;\n\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                if (ProwInd[i] == tIdx) { relevantCol = 1; break; }\n            }\n\n            if (!relevantCol) { continue; }\n\n            if (rowIsFilled == 0)  // Simply copy the row indices in the current P column\n            {\n                // to the column indices of the Ma row\n                for (int i = PjColBegin; i < PjColEnd; i++)\n                {\n                    MaColInd[MaIndex] = ProwInd[i];\n                    MaIndex++;          // Increment the number of assigned indices to the current row.\n                }\n\n                rowIsFilled = 1;      // indicates that Ma row is initialized but not completely filled\n                continue;\n            }\n\n            // If the row has been initialized, we need to take\n            // only those row indices of P that haven't already appeared\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                // grab the next row index of the current column j of P\n                const int PiRowIndex = ProwInd[i];\n                bool indexPrevAppeared = 0;   // indicate that PiRowIndex haven't been previously assigned to the row of Ma\n\n                for (int jj = MaRowBegin; jj < MaIndex; jj++) // scan the accumulated Ma column indices and check whether\n                {\n                    // the current P row index already exists\n                    if (MaColInd[jj] == PiRowIndex)\n                    {\n                        // the index already exists, so skip it\n                        indexPrevAppeared = 1;\n                        break;\n                    }\n                }\n\n                if (!indexPrevAppeared)   // if the index is new to the MaColInd array, add it to the end (i.e. non-sorted)\n                {\n                    MaColInd[MaIndex] = PiRowIndex;\n                    MaIndex++;\n                }\n\n                if (MaIndex == MaRowEnd)  // stop searching, since we filled the entire Ma row.\n                {\n                    rowIsFilled = 2;        // indicates that Ma row is completely filled with column indices\n                    break;\n                }\n            }\n\n            if (rowIsFilled == 2) { break; } // stop searching, since we filled the entire Ma row.\n        }\n\n        if (!Ma_nzDiagRows[tIdx]) { MaColInd[MaIndex] = tIdx; }\n    } // end for (int tIdx ...)\n}\n\n\n\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  add_invAijs_to_Ma_col_major_kernel( const IndexType *PcolOffsets, const IndexType *ProwInd, const IndexType PnumCols,\n        const IndexType *AijOffsets, const ValueType *dense_invAijs,\n        const IndexType *MaRowOffsets, const IndexType *MaColInd,\n        ValueType *MaValues )\n{\n    // go thru all invAij matrices\n    for (unsigned int j = 0; j < PnumCols; j++)\n    {\n        // every thread runs through every invAij matrix and works on a single row of each invAij\n        const int AijOffset = AijOffsets[j];\n        const ValueType *invAij = dense_invAijs + AijOffset;\n        const unsigned int PcolBegin = PcolOffsets[j];\n        // dimension of the current Aij within dense_Aijs\n        const unsigned int AijNumRows = PcolOffsets[j + 1] - PcolOffsets[j];\n        const unsigned int lda = AijNumRows;  // leading dim of the current Aij (col-major)\n\n        for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AijNumRows; tIdx += blockDim.x * gridDim.x)\n        {\n            // each thread works on a single row of the current invAij\n            const unsigned int MaRowIndex = ProwInd[PcolBegin + tIdx];\n            // get the bounds of each thread's Ma row\n            const unsigned int MaRowBegin = MaRowOffsets[MaRowIndex];\n            const unsigned int MaRowEnd   = MaRowOffsets[MaRowIndex + 1];\n            unsigned int MaEntryIndex = MaRowBegin;\n\n            for (unsigned int i = 0; i < AijNumRows; i++)\n            {\n                // go through each element in the given row of invAij and put this element\n                // into its corresponding (\"spattered\") location in Ma\n                const unsigned int MaColIndex = ProwInd[PcolBegin + i];\n\n                // there are potentially more nz entries in a given row of Ma\n                // than the dimension of the current invAij, hence need this loop\n                // (note that we do not restart count since the col indices are stored in order)\n                for ( ; MaEntryIndex < MaRowEnd; MaEntryIndex++)\n                {\n                    if (MaColInd[MaEntryIndex] == MaColIndex)\n                    {\n                        MaValues[MaEntryIndex] += invAij[tIdx + i * lda];\n                        break;\n                    }\n                }\n            }\n        }\n\n        // to avoid race conditions when accessing rows of Ma, all threads need to be in sync.\n        __syncthreads();\n    }\n}\n\n\n// This kernel perturbs the diagonal of Ma (i.e. adds a specified value to each diag entry)\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  perturb_Ma_diag_kernel( const IndexType *MaRowOffsets, const IndexType *MaColInd,\n                              ValueType *MaValues, const IndexType AnumRows,\n                              const ValueType perturb_mag, const bool *Ma_nzDiagRows )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread perturbs one diagonal entry of Ma\n        const int MaRowBegin = MaRowOffsets[tIdx];\n        const int MaRowEnd   = MaRowOffsets[tIdx + 1];\n        bool diagIsZero = 1;\n\n        for (int j = MaRowBegin; j < MaRowEnd; j++)\n        {\n            // find the diagonal entry in the current row\n            if (tIdx == MaColInd[j])\n            {\n                diagIsZero = 0;\n\n                if (!Ma_nzDiagRows[tIdx]) { MaValues[j] = perturb_mag; break;}\n\n                MaValues[j] += perturb_mag;\n                break;\n            }\n        }\n\n        if (diagIsZero)\n        {\n            printf(\"\\nWARNING: found zero diagonal entry of Ma in row %d. Ma_nzDiagRows[%d] = %d \\n\",\n                   tIdx, tIdx, Ma_nzDiagRows[tIdx]);\n            break;\n        }\n    }\n}\n\n// This kernel initializes each column of P to v_x.\ntemplate <typename IndexType, typename ValueType, typename ValueTypeB>\n__global__\nvoid init_Pvalues_kernel( const IndexType *PcolOffsets, const IndexType *ProwInd,\n                          const IndexType PnumCols, const ValueTypeB *v_x,\n                          ValueType *Pvalues )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one column of P in CSC format (P^T in CSR format)\n        const unsigned int PcolBegin = PcolOffsets[tIdx];\n        const unsigned int PcolEnd   = PcolOffsets[tIdx + 1];\n\n        for (int j = PcolBegin; j < PcolEnd; j++)\n        {\n            // go thru each entry in the given column of P\n            Pvalues[j] = v_x[ProwInd[j]];\n        }\n    }\n}\n\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nEM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\nEM_Interpolator() : EM_InterpolatorBase<TConfig_d>(),\n    m_cuds_handle(0),\n    m_dense_Aijs(0),\n    m_dense_invAijs(0),\n    m_ipiv(0),\n    m_cuds_info(0),\n    m_cuds_wspace(0)\n{\n    // Allocate a handle for cudense\n    cusolverStatus_t status = cusolverDnCreate(&m_cuds_handle);\n\n    if ( status != CUSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Define the cudense stream.\n    status = cusolverDnSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n    if ( status != CUSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    allocMem(m_cuds_info, sizeof(int), false);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nEM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~EM_Interpolator()\n{\n    if (m_cuds_handle)\n    {\n        cusolverDnDestroy(m_cuds_handle);\n    }\n\n    if (m_dense_Aijs)\n    {\n        amgx::memory::cudaFreeAsync(m_dense_Aijs);\n        m_dense_Aijs = 0;\n    }\n\n    if (m_dense_invAijs)\n    {\n        amgx::memory::cudaFreeAsync(m_dense_invAijs);\n        m_dense_invAijs = 0;\n    }\n\n    if (m_ipiv)\n    {\n        amgx::memory::cudaFreeAsync(m_ipiv);\n        m_ipiv = 0;\n    }\n\n    if (m_cuds_wspace)\n    {\n        amgx::memory::cudaFreeAsync(m_cuds_wspace);\n        m_cuds_wspace = 0;\n    }\n\n    if (m_cuds_info)\n    {\n        amgx::memory::cudaFreeAsync(m_cuds_info);\n        m_cuds_info = 0;\n    }\n}\n\n\n\n// Private functions for the implementation on device\n\n// Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePsparsity( const Matrix_d &A, const IVector &cf_map, const IVector &coarse_idx,\n                    Matrix_d &P, IVector &PnnzPerCol,\n                    BVector &Ma_nzDiagRows )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // temporary vectors - diagonal and non-zero offsets\n    VVector Adiag(A.get_num_rows(), 0.0);\n    ValueType *Adiag_ptr  = Adiag.raw();\n    // fullToCoarseMap array gives the COARSE index corresponding to the given FULL index\n    // (meaningless if the full index corresponds to a FINE pt)\n    IntVector fullToCoarseMap(AnumRows, 0);\n    int *fullToCoarseMap_ptr = fullToCoarseMap.raw();\n    amgx::thrust::transform_exclusive_scan( cf_map.begin(), cf_map.end(), fullToCoarseMap.begin(),\n                                      is_coarse(), 0, amgx::thrust::plus<int>() );\n    cudaCheckError();\n    // Extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(AnumRows, A.diag.raw(), Avalues_ptr, Adiag_ptr);\n    cudaCheckError();\n    // Coarse/Fine map\n    const int *cf_map_ptr = cf_map.raw();\n    // Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\n    // Count the number of non-zeros in the interpolation if\n    const int numCoarse = coarse_idx.size();\n    const int *coarse_idx_ptr = coarse_idx.raw();\n    VVector thresh_per_row(AnumRows);\n    count_PnnzPerCol_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                Adiag_ptr, numCoarse,\n                                cf_map_ptr, fullToCoarseMap_ptr, coarse_idx_ptr,\n                                PnnzPerCol.raw(), thresh_per_row.raw());\n                                cudaCheckError();\n    printMatrixStats(A);\n    cudaCheckError();\n    // Column offsets of P (CSC format)\n    IntVector PnzOffsets(numCoarse + 1);\n    PnzOffsets[0] = 0;\n    // get the offsets in P with an inclusive scan\n    thrust_wrapper::inclusive_scan<AMGX_device>(PnnzPerCol.begin(), PnnzPerCol.end(), PnzOffsets.begin() + 1);\n    cudaCheckError();\n    // get total num of non-zeros in P\n    const int Pnnz = PnzOffsets[numCoarse];\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);  // IMPORTANT: P actually stores P^T in CSR format\n    //            because we build P by columns (and CSC format is not supported)\n    P.resize(numCoarse, AnumRows, Pnnz, 1);\n    // set P offsets (P column offsets or P^T row offsets)\n    amgx::thrust::copy(PnzOffsets.begin(), PnzOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // treat P as if it were in the CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    IndexType *ProwInd_ptr           = P.col_indices.raw();\n    bool *Ma_nzDiagRows_ptr = Ma_nzDiagRows.raw();\n    // Since CSC format is not supported in AMGX matrix, we instead\n    // compute P^T (which is stored in P) in the CSR format.\n    // Initialize P by performing greedy aggregation based on the coarse set (cf_map)\n    // The values of P remain uninitialized, since at this point we only get the sparsity pattern of P.\n    init_ProwInd_greedy_aggregation_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                Adiag_ptr, AnumRows,\n                                cf_map_ptr, fullToCoarseMap_ptr,\n                                PcolOffsets_ptr, ProwInd_ptr,\n                                Ma_nzDiagRows_ptr);\n    cudaCheckError();\n}\n\n\n// Compute the submatrices Aij's of A and their inverses invAij's\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computeAijSubmatrices(const Matrix_d &A, const int numCoarse, const Matrix_d &P,\n                        ValueType *dense_Aijs, ValueType *dense_invAijs,\n                        const IntVector &AijOffsets, int *ipiv,\n                        cusolverDnHandle_t &cuds_handle, int *cuds_info)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1));\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    // Treat P as if it were in the CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    const IndexType *AijOffsets_ptr  = AijOffsets.raw();\n    // Extract Aij's from A based on the sparsity pattern of each column of P.\n    // Column major format is used for storage of Aij's.\n    extract_dense_Aijs_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                AijOffsets_ptr, dense_Aijs);\n    cudaCheckError();\n    // Initialize invAij's to (dense) identity matrices.\n    init_dense_invAijs_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, numCoarse,\n                                AijOffsets_ptr, dense_invAijs);\n    cudaCheckError();\n    // Sequentially perform dense LU factorization on all Aij's\n    // LU factors stored in place of the Aij's\n    // Then, perform inversion of Aij submatrices by solving with RHS == identity matrix\n    cusolverStatus_t cudsStat;\n\n    if (!cuds_handle)\n    {\n        cusolverStatus_t status = cusolverDnCreate(&m_cuds_handle);\n\n        if ( status != CUSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n        }\n\n        // Define the cudense stream.\n        status = cusolverDnSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n        if ( status != CUSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n        }\n    }\n\n    // Pointer to info from cudense\n    if (!cuds_info) { cuds_info = (int *) malloc(sizeof(int)); }\n\n    // Offset of cudense pivoting sequence for each Aij.\n    int ipivOffset = 0;\n    IVector_h sizes(numCoarse);\n    int max_wsize = 0;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int cur_wsize = 0;\n        int AijOffset  = AijOffsets[j];\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;\n        cusolverStatus_t status = cusolverDnXgetrf_bufferSize(cuds_handle, AijNumRows, AijNumRows, dense_Aijs + AijOffset, lda, &cur_wsize);\n\n        if (status != CUSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform LU factorization\", AMGX_ERR_INTERNAL);\n        }\n\n        max_wsize = std::max(cur_wsize, max_wsize);\n    }\n\n    allocMem(m_cuds_wspace, max_wsize, false);\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];                           // offset of the current Aij\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j]; // dimension of the next Aij within dense_Aijsc\n        int lda = AijNumRows;                                     // leading dim for the next Aij (col-major)\n        sizes[j] = AijNumRows;\n        // [L,U,P] = lu(Aij) (LU factors stored in place of the current Aij)\n        cudsStat = cusolverDnXgetrf(cuds_handle, AijNumRows, AijNumRows,\n                                    dense_Aijs + AijOffset, lda, m_cuds_wspace,\n                                    ipiv + ipivOffset, cuds_info);\n\n        if (cudsStat != CUSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform LU factorization\", AMGX_ERR_INTERNAL);\n        }\n\n        // invAij = U\\(L\\P) (i.e. find inverse of Aij).\n        cudsStat = cusolverDnXgetrs(cuds_handle, CUBLAS_OP_N, AijNumRows, AijNumRows,\n                                    dense_Aijs + AijOffset, lda,\n                                    ipiv + ipivOffset, dense_invAijs + AijOffset, lda, cuds_info );\n\n        if (cudsStat != CUSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform triangular solve\", AMGX_ERR_INTERNAL);\n        }\n\n        ipivOffset += AijNumRows;   // starting offset of the next pivoting sequence\n    }\n}\n\n\n// Compute Ma matrix\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computeMa(Matrix_d &Ma, const int AnumRows, const int numCoarse, const Matrix_d &P,\n            const ValueType *dense_invAijs, const IntVector &AijOffsets,\n            const BVector &Ma_nzDiagRows,\n            const bool perturb_Ma_diag, const ValueType perturb_mag)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    // Number of non-zeros in each row of Ma\n    IntVector Ma_nnzPerRow(AnumRows, 0);\n    // 1. Compute Ma_nnzPerRow (kernel).\n    count_Ma_nnz_per_row_kernel <<< numBlocks, blocksize>>>( PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n            Ma_nnzPerRow.raw(), AnumRows, Ma_nzDiagRows.raw() );\n    cudaCheckError();\n    // 2. Resize Ma and assign MaRowOffsets by performing a scan on Ma_nnzPerRow.\n    // Get the offsets in Ma with an inclusive scan (in-place)\n    thrust_wrapper::inclusive_scan<AMGX_device>(Ma_nnzPerRow.begin(), Ma_nnzPerRow.end(), Ma_nnzPerRow.begin());\n    cudaCheckError();\n    // Get total num of non-zeros in Ma\n    const int Ma_nnz = Ma_nnzPerRow[AnumRows - 1];\n    // Resize Ma\n    Ma.resize(0, 0, 0, 1);\n    Ma.addProps(CSR);\n    Ma.resize(AnumRows, AnumRows, Ma_nnz, 1);\n    Ma.row_offsets[0] = 0;\n    // set Ma row offsets\n    amgx::thrust::copy(Ma_nnzPerRow.begin(), Ma_nnzPerRow.end(), Ma.row_offsets.begin() + 1);\n    cudaCheckError();\n    const IndexType *MaRowOffsets_ptr = Ma.row_offsets.raw();\n    IndexType *MaColInd_ptr           = Ma.col_indices.raw();\n    ValueType *MaValues_ptr           = Ma.values.raw();\n    // 3. Get the unsorted column indices of Ma.\n    get_unsorted_MaColInd_kernel <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n            MaRowOffsets_ptr, MaColInd_ptr, AnumRows,\n            Ma_nzDiagRows.raw());\n    cudaCheckError();\n\n    // 4. Sort column indices to get the final MaColInd.\n    for (unsigned int MaRow = 0; MaRow < AnumRows; MaRow++)\n    {\n        int MaRowBegin = Ma.row_offsets[MaRow];\n        int MaRowEnd   = Ma.row_offsets[MaRow + 1];\n\n        if (MaRowEnd - MaRowBegin > 1)\n        {\n            thrust_wrapper::sort<AMGX_device>( Ma.col_indices.begin() + Ma.row_offsets[MaRow],\n                          Ma.col_indices.begin() + Ma.row_offsets[MaRow + 1] );\n        }\n    }\n\n    // 5. Compute MaValues by summing up the \"spattered\" inverses of Aij submatrices.\n    add_invAijs_to_Ma_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                AijOffsets.raw(), dense_invAijs,\n                                MaRowOffsets_ptr, MaColInd_ptr,\n                                MaValues_ptr);\n    cudaCheckError();\n\n    // Perturb the diagonal of Ma here (prevents Ma from being singular)\n    // Ma = 1.0e-8*speye(n,n);\n    if (perturb_Ma_diag)\n    {\n        // Default: perturb_mag = 1.0e-8\n        perturb_Ma_diag_kernel<IndexType, ValueType>\n        <<< numBlocks, blocksize>>>(MaRowOffsets_ptr, MaColInd_ptr,\n                                    MaValues_ptr, AnumRows,\n                                    perturb_mag, Ma_nzDiagRows.raw());\n        cudaCheckError();\n    }\n\n    Ma.set_initialized(1);\n}\n\n\n// Solve for x: Ma*x=e (solved with pcg)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::solveMa_e(Matrix_d &Ma, const int AnumRows, Vector_d &v_x)\n{\n    typedef typename Matrix_d::value_type ValueType;\n    // 1. Initialize x (vector of 0's) and e (vector of 1's)\n    v_x.resize(AnumRows, 0.0);\n    Vector_d v_e(AnumRows, 1.0);\n    // ------------------------------ define pcg solver ---------------------------------\n    // temporary config and pointer to main config (json format)\n    AMG_Config cfg;\n    std::string cfg_string = \"{ \\\"config_version\\\": 2\";\n    cfg_string += \", \\\"solver\\\": { \\\"scope\\\": \\\"pcg_scope\\\", \\\"solver\\\": \\\"PCG\\\"\";   // Set the solver to PCG\n    cfg_string += \" ,\\\"preconditioner\\\": \\\"NOSOLVER\\\"\";    // IMPORTANT to have this in order to allocate PCG\n    cfg_string += \" } }\";\n    cfg.parseParameterString(cfg_string.c_str());\n    // define and allocate solver\n    Solver<TConfig_d> *Ma_solver = 0;\n    Ma_solver = SolverFactory<TConfig_d>::allocate(cfg, \"default\", \"solver\");\n    // ------------------------------ end define pcg ------------------------------------\n    const int Ma_solveIts = 20;\n    const ValueType Ma_solveTol = (ValueType) 1.e-4;\n    Ma_solver->setup(Ma, false);\n    Ma_solver->set_max_iters(Ma_solveIts);\n    Ma_solver->setTolerance(Ma_solveTol);\n    // Apply pcg to solve x = Ma \\ e\n    Ma_solver->solve( v_e, v_x, true );\n    Vector_d v_res(AnumRows, 0.);\n    Ma_solver->compute_residual( v_e, v_x, v_res );\n\n    if (Ma_solver) { delete Ma_solver; Ma_solver = 0; }\n}\n\n\n// Compute the values of P (using LU factorization of Aij submatrices)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePvalues( const int AnumRows, const int numCoarse, Matrix_d &P, const Vector_d &v_x,\n                  const ValueType *dense_Aijs, const IntVector &AijOffsets, const int *ipiv,\n                  cusolverDnHandle_t &cuds_handle, int *cuds_info )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    ValueType *Pvalues_ptr           = P.values.raw();\n    init_Pvalues_kernel<IndexType, ValueType, ValueTypeB>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                v_x.raw(), Pvalues_ptr);\n    cudaCheckError();\n    cusolverStatus_t cudsStat;\n\n    if (!cuds_handle)\n    {\n        cusolverStatus_t status = cusolverDnCreate(&m_cuds_handle);\n\n        if ( status != CUSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n        }\n\n        // Define the cudense stream.\n        status = cusolverDnSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n        if ( status != CUSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n        }\n    }\n\n    // Pointer to info from cudense\n    if (!cuds_info) { cuds_info = (int *) malloc(sizeof(int)); }\n\n    // Offset of cudense pivoting sequence for each Aij.\n    int ipivOffset = 0;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];\n        int PcolOffset = P.row_offsets[j];\n        // dimension of the next Aij within dense_Aijs\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;  // leading dim for the next Aij (col-major)\n\n        if (lda == 0) { continue; }\n\n        // invAij = U\\(L\\P) (i.e. find inverse of Aij).\n        cudsStat = cusolverDnXgetrs(cuds_handle, CUBLAS_OP_N, AijNumRows, 1,\n                                    dense_Aijs + AijOffset, lda,\n                                    ipiv + ipivOffset, Pvalues_ptr + PcolOffset, lda, cuds_info);\n\n        if (cudsStat != CUSOLVER_STATUS_SUCCESS)\n        { FatalError(\"failed to perform triangular solve\", AMGX_ERR_INTERNAL); }\n\n        ipivOffset += AijNumRows;   // starting offset of the next pivoting sequence\n    }\n\n    if (cuds_info)  { free(cuds_info);  cuds_info = 0; }\n}\n\n\n\n// Compute the values of P (using inverses of Aij submatrices)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePvalues( const int AnumRows, const int numCoarse, Matrix_d &P, const Vector_d &v_x,\n                  const ValueType *dense_invAijs, const IntVector &AijOffsets )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    ValueType *Pvalues_ptr           = P.values.raw();\n    init_Pvalues_kernel<IndexType, ValueType, ValueTypeB>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                v_x.raw(), Pvalues_ptr);\n    cudaCheckError();\n    ValueType *place_holder_ptr = 0;\n    allocMem(place_holder_ptr, sizeof(ValueType), false);\n    ValueType one = (ValueType) 1.;\n    ValueType zero = (ValueType) 0.;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];\n        int PcolOffset = P.row_offsets[j];\n        // dimension of the next Aij within dense_Aijs\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;  // leading dim for the next Aij (col-major)\n\n        if (lda == 0) { continue; }\n\n        Cublas::gemv(false, AijNumRows, AijNumRows, &one,\n                     dense_invAijs + AijOffset, lda,\n                     Pvalues_ptr + PcolOffset, 1,\n                     &zero, place_holder_ptr, 1);\n        cudaDeviceSynchronize();\n        cudaCheckError();\n    }\n\n    if (place_holder_ptr) { amgx::memory::cudaFreeAsync(place_holder_ptr); place_holder_ptr = 0; }\n}\n\n\n\n// ----------------------------\n//  specialization for device\n// ----------------------------\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::generateInterpolationMatrix_1x1(const Matrix_d &A, const IntVector &cf_map,\n                                  Matrix_d &P, void *amg)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Number of rows in Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Count the number of coarse points\n    const int numCoarse = (int) amgx::thrust::count(cf_map.begin(), cf_map.end(), (int)COARSE);\n    cudaCheckError();\n    IVector coarse_idx(numCoarse, -1);\n    amgx::thrust::counting_iterator<int> zero(0);\n    amgx::thrust::counting_iterator<int> zero_plus_nc = zero + numCoarse;\n    amgx::thrust::copy_if(zero, zero_plus_nc, cf_map.begin(), coarse_idx.begin(), is_coarse());\n    // ..................................................................................\n    // ------------------------ Begin Compute P code ------------------------------------\n    // Store the number of non-zeros in each column of the interpolation matrix P\n    IntVector PnnzPerCol(numCoarse);\n    BVector Ma_nzDiagRows(AnumRows, 0);\n    // Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\n    computePsparsity(A, cf_map, coarse_idx, P, PnnzPerCol, Ma_nzDiagRows);\n    // Get total num of non-zeros in P\n    int Pnnz = P.row_offsets[numCoarse];\n    // ..................................................................................\n    // Now follows Jacobi smoothing (skip for now since it might be unnecessary)\n    // ..................................................................................\n    // -------------------------- End Compute P code ------------------------------------\n    // ..................................................................................\n    // ..................................................................................\n    // ----------------------- Begin Energy Minimization Code ---------------------------\n    // Given:\n    //        A: the problem matrix\n    //        P: sparsity pattern for P (n x nc)\n    // Main loop (Ma here is the inverse of Ma in the paper)\n    // I. Build Ma.\n    // 0. Allocate space for storing LU factors for each Aij submatrix of A (use dense format)\n    //    There are numCoarse of LU factors.\n    //    Since the number of non-zeros in each column of P determines the size of each Aij,\n    //    we know the dimensions of all Aij based on the row offsets of P^T.\n    // Calculate offset of each Aij matrix in the dense_Aijs array\n    IntVector AijOffsets(numCoarse + 1);\n    AijOffsets[0] = 0;\n    // Get the offsets with an inclusive scan (nnz of each column of P is the dimension of each Aij)\n    amgx::thrust::transform_inclusive_scan( PnnzPerCol.begin(), PnnzPerCol.end(), AijOffsets.begin() + 1,\n                                      square(), amgx::thrust::plus<int>() );\n    cudaCheckError();\n    PnnzPerCol.clear();\n    // Cumulative num of entries in all (dense) Aijs (last entry in the offsets)\n    const int Aijs_size = AijOffsets[numCoarse];\n    // Allocate memory for the dense Aij submatrices and initialize to zero.\n    allocMem(m_dense_Aijs, Aijs_size, true);\n    // Allocate memory for the dense invAij's and initialize to zero.\n    allocMem(m_dense_invAijs, Aijs_size, true);\n    // Allocate memory for cudense pivoting sequence.\n    allocMem(m_ipiv, Pnnz, false);\n    // Compute the submatrices Aij's of A and the inverses invAij's\n    computeAijSubmatrices(A, numCoarse, P, m_dense_Aijs, m_dense_invAijs,\n                          AijOffsets, m_ipiv, m_cuds_handle, m_cuds_info);\n    // Ma matrix build sequence.\n    // 1. Compute Ma_nnzPerRow (kernel).\n    // 2. Resize Ma and assign MaRowOffsets by performing a scan on Ma_nnzPerRow (thrust).\n    // 3. Get the unsorted column indices of Ma (kernel).\n    // 4. Sort column indices to get the final MaColInd (thrust).\n    // 5. Compute MaValues by summing up the \"spattered\" inverses of Aij submatrices (kernel).\n    // Declare Ma\n    Matrix_d Ma;\n    // Compute Ma matrix\n    computeMa(Ma, AnumRows, numCoarse, P, m_dense_invAijs, AijOffsets, Ma_nzDiagRows);\n\n    if (m_dense_invAijs) { amgx::memory::cudaFreeAsync(m_dense_invAijs);  m_dense_invAijs = 0; }\n\n    /* At this point, we are done with the following Matlab code analogue.\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the dofs in the j-th block\n      ij = find(p(:,j));              // col vec of subindices\n      ni = length(ij);\n      It = sparse(n,ni);\n      It(ij,:) = speye(ni);\n      Aij{j} = inv(A(ij,ij));\n      // Form Ma\n      // A(ii,ii): submatrix of A corresponding to the dofs in the i-th block\n      Ma = Ma + It*(Aij{j}*It_T);\n    }\n    */\n    Ma_nzDiagRows.clear();\n    // II. Solve for x: Ma*x=e\n    //m_Mae_norm = (ValueType) 0.;\n    Vector_d v_x;\n    solveMa_e(Ma, AnumRows, v_x);\n    cudaDeviceSynchronize();\n    cudaCheckError();\n    Ma.set_initialized(0);\n    Ma.resize(0, 0, 0, 1);\n    /*  Now, we are done with the following Matlab code analogue.\n    e = ones(n,1);\n    Mae = zeros(size(e));  // Ma\\e;\n    [Mae,flag,relres,iter,resvecCG] = pcg(Ma,e,1e-4,100);\n    */\n    // III. Construct the columns of P in parallel.\n    // P is already initialized (and at this point stores P^T in CSR format)\n    // We will be performing the following operation to build each column of P\n    // P(:,j) = It{j}*(invAij{j}*(It^T*x));\n    // This is done with the following steps.\n    // First, apply It{j}^T mask to x to get x{j} (project x onto the dimension of Aij{j}).\n    // Then solve Aij\\x{j}\n    // Compute the values of P\n    computePvalues( AnumRows, numCoarse, P, v_x, m_dense_Aijs, AijOffsets, m_ipiv, m_cuds_handle, m_cuds_info );\n\n    if (m_dense_Aijs) { amgx::memory::cudaFreeAsync(m_dense_Aijs);  m_dense_Aijs = 0; }\n\n    if (m_ipiv)       { amgx::memory::cudaFreeAsync(m_ipiv);        m_ipiv = 0; }\n\n    if (m_cuds_info)  { amgx::memory::cudaFreeAsync(m_cuds_info);   m_cuds_info = 0; }\n\n    //if (m_dense_invAijs) { amgx::memory::cudaFreeAsync(m_dense_invAijs);  m_dense_invAijs = 0; }\n    /* Now, we are done building columns of P (stored as rows of P^T in CSR).\n     * Here's the equivalent matlab code.\n    P = zeros(n,nc);\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the dofs in the j-th block\n      ij=find(p(:,j));\n      ni = length(ij);\n      It = zeros(n,ni);\n      It(ij,:) = eye(ni);\n\n      // Form j-th column of P\n      P(:,j) = It*(Aij{j}*(It_T*Mae));\n    }\n    */\n    //TODO: truncate rows/cols of P to keep the sparsity low on all levels.\n    // ------------------------- End Energy Minimization Code ---------------------------\n    // ..................................................................................\n}\n\n\n\ntemplate< class T_Config>\nvoid EM_InterpolatorBase<T_Config>\n::generateInterpolationMatrix(const Matrix<T_Config> &A, const IntVector &cf_map,\n                              Matrix<T_Config> &P, void *amg)\n{\n    P.set_initialized(0);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, P, amg);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for distance1 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    P.computeDiagonal();\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class EM_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class EM_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace energymin\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n#include <thrust/functional.h>\n#include <thrust/inner_product.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n#include <thrust/transform_scan.h>\n#include <thrust_wrapper.h>\n\n#include <energymin/interpolators/em.h>\n#include <energymin/interpolators/common.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <cmath> // std::signbit\n#include <assert.h>\n\n#include \"util.h\"\n\n#include \"amgx_cublas.h\"\n\nnamespace amgx\n{\nnamespace energymin\n{\n\n\n/*************************************************************************\n* create the interpolation matrix P by computing p and using energy minimization\n************************************************************************/\n\n\n/*************************************************************************\n* create the interpolation matrix P on GPU\n************************************************************************/\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid count_PnnzPerCol_kernel( const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n                              const ValueType *Adiag, const IndexType numCoarse,\n                              const int *cf_map, const int *fullToCoarseMap, const int *coarse_idx_ptr,\n                              int *PnnzPerCol, ValueType *thresh_per_row )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numCoarse; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int row_idx = coarse_idx_ptr[tIdx];\n        const int ArowBegin = ArowOffsets[row_idx];\n        const int ArowEnd   = ArowOffsets[row_idx + 1];\n        // Matlab pseudo-code:\n        // p = sparse(n, nc);\n        // p(coarse,:) = speye(nc);\n        // fAj = AD(j,fine)/AD(j,j) < -.1;  // fine neighbors of j with oppositive sign from AD_jj\n        // become the non-zeros in initial P.\n        ValueType minVal(0), maxVal(0), diag_value(0);\n        ValueType fine_avg_thresh = 0.;\n        const ValueType thresh = (ValueType)0.25;\n\n        for (int j = ArowBegin; j < ArowEnd; j++)\n        {\n            ValueType aValue = Avalues[j];\n\n            if ( j != tIdx )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n            else\n            {\n                diag_value = aValue;\n            }\n        }\n\n        if ( diag_value < 0.0)\n        {\n            fine_avg_thresh = thresh * maxVal;\n        }\n        else\n        {\n            fine_avg_thresh = thresh * minVal;\n        }\n\n        thresh_per_row[tIdx] = fine_avg_thresh;\n        int p_col_nnz = 0;\n\n        for (int j = ArowBegin; j < ArowEnd; j++)\n        {\n            int AjColIndex = AcolInd[j];\n\n            if (AjColIndex == tIdx)\n            {\n                p_col_nnz++;\n                continue;\n            }\n\n            if (cf_map[AjColIndex] == FINE)\n            {\n                if (diag_value * Avalues[j] < diag_value * fine_avg_thresh)\n                {\n                    p_col_nnz++;\n                }\n            }\n        }\n\n        PnnzPerCol[tIdx] = p_col_nnz;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid init_ProwInd_greedy_aggregation_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n        const ValueType *Adiag, const IndexType AnumRows,\n        const int *cf_map, const int *fullToCoarseMap,\n        const IndexType *PcolOffsets, IndexType *ProwInd,\n        bool *Ma_nzDiagRows)\n{\n    // for each coarse point form aggregates\n    /* Matlab-like pseudo-code\n    p = sparse(n, nc);\n    p(coarse,:) = speye(nc);\n    for (int j : coarse)              // coarse point with FULL index j\n      fAj = AD(j,fine)/AD(j,j) < -.1; // fine neighbors of j with oppositive sign from AD_jj\n      P(fine(fAj),cj)=1;              // become the non-zeros in initial P.\n    end\n    */\n\n    // size of P and its column offsets were precomputed.\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time (builds a column of P)\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == COARSE)     // \"coarse\" row of A\n        {\n            // P is CSC format since each thread works on each coarse point\n            // and is therefore building a single column of P.\n            // There are numCoarse columns of P\n            // Columns of P correspond to rows of A (A is assumed symmetric)\n            bool PcolCoarseDiagUnset = 1;\n            const int PcolIndex = fullToCoarseMap[tIdx];\n            int PentryNum = PcolOffsets[PcolIndex];  // starting location of the current column of P\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (cf_map[AjColIndex] == FINE && Avalues[j] / Adiag[tIdx] < -.1)\n                {\n                    //p(AjColIndex, fullToCoarseMap(j))=1;\n                    if (AjColIndex > tIdx && PcolCoarseDiagUnset)     // COARSE diagonal of P is identity\n                    {\n                        // set COARSE diagonal of P\n                        ProwInd[PentryNum] = tIdx; // set COARSE diagonal entry of P to 1.\n                        PentryNum++;\n                        PcolCoarseDiagUnset = 0;\n                        Ma_nzDiagRows[tIdx] = 1;    // record the newly added row index of P\n                    }\n\n                    ProwInd[PentryNum] = AjColIndex; // columns of A become rows in P (since A is assumed symmetric)\n                    PentryNum++;\n                    Ma_nzDiagRows[AjColIndex] = 1;    // record the newly added row index of P\n                }\n            }\n\n            if (PcolCoarseDiagUnset)     // COARSE diagonal of P is identity\n            {\n                // set COARSE diagonal of P\n                ProwInd[PentryNum] = tIdx; // columns of A become rows in P (since A is assumed symmetric)\n                PentryNum++;\n                PcolCoarseDiagUnset = 0;\n                Ma_nzDiagRows[tIdx] = 1;    // record the newly added row index of P\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  extract_dense_Aijs_col_major_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd, const ValueType *Avalues,\n        const IndexType *PcolOffsets, const IndexType *ProwInd, const IndexType PnumCols,\n        const IndexType *AijOffsets, ValueType *dense_Aijs)\n{\n    /* Matlab-like pseudo-code\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the nz pattern in the j-th column of P\n      ij = find(p(:,j));              // col vec of subindices\n      Aij{j} = A(ij,ij);\n    }\n    */\n\n    // each thread builds one Aij\n    for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works with one column of P\n        const unsigned int PcolBegin  = PcolOffsets[tIdx];\n        const unsigned int PcolEnd    = PcolOffsets[tIdx + 1];\n        const unsigned int AijNumRows = PcolEnd - PcolBegin;\n        // pointer to the beginning of the specific Aij submatrix\n        ValueType *dense_Aij = dense_Aijs + AijOffsets[tIdx];\n\n        for (unsigned int i = PcolBegin; i < PcolEnd; i++)\n        {\n            // go thru each non-zero row of the given column of P\n            // and scan the corresponding rows of A\n            unsigned int PiRowIndex = ProwInd[i];\n            const unsigned int ArowBegin = ArowOffsets[PiRowIndex];\n            const unsigned int ArowEnd   = ArowOffsets[PiRowIndex + 1];\n\n            for (unsigned int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // scan the current row of A and pick the elts based on the sparsity pattern of P\n                unsigned int AjColIndex = AcolInd[j];\n\n                // if AjColIndex is an index of a non-zero entry in the current column of P, take it.\n                for (unsigned int ii = PcolBegin; ii < PcolEnd; ii++)\n                {\n                    if (ProwInd[ii] == AjColIndex)\n                    {\n                        // add elt to Aij in column-major format\n                        unsigned int AijRowIndex = i - PcolBegin;\n                        unsigned int AijColIndex = ii - PcolBegin;\n                        dense_Aij[AijColIndex * AijNumRows + AijRowIndex] = Avalues[j]; //wrong\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n// kernel to initialize invAij's to dense identity matrices\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  init_dense_invAijs_col_major_kernel(const IndexType *PcolOffsets, const IndexType PnumCols,\n        const IndexType *AijOffsets, ValueType *dense_invAijs)\n{\n    for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread initializes one invAij\n        const unsigned int PcolBegin  = PcolOffsets[tIdx];\n        const unsigned int PcolEnd    = PcolOffsets[tIdx + 1];\n        const unsigned int AijNumRows = PcolEnd - PcolBegin;\n        // pointer to the beginning of the specific invAij submatrix\n        ValueType *dense_invAij = dense_invAijs + AijOffsets[tIdx];\n\n        for (unsigned int i = 0; i < AijNumRows; i++)\n        {\n            dense_invAij[i * AijNumRows + i] = 1;\n        }\n    }\n}\n\n\n\n\n__global__\nvoid  count_Ma_nnz_per_row_kernel(const int *PcolOffsets, const int *ProwInd, const int PnumCols,\n                                  int *Ma_nnzPerRow, const int AnumRows, const bool *Ma_nzDiagRows)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // every thread works on a single row of Ma (figures out nnz in that row)\n        bool rowNotFilled = 1;          // marks whether the Ma row nnz count has been initialized\n        int nnzPerRow = 0;\n\n        for (int j = 0; j < PnumCols; j++)\n        {\n            // each thread goes thru all columns of P\n            const int PjColBegin  = PcolOffsets[j];\n            const int PjColEnd    = PcolOffsets[j + 1];\n            //const int PjCol_nnz   = PjColEnd - PjColBegin;\n\n            // check that the column is relevant (i.e. has a non-zero in the tIdx row)\n            if (tIdx < ProwInd[PjColBegin] || ProwInd[PjColEnd - 1] < tIdx) { continue; }\n\n            // Make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n            bool relevantCol = 0;\n\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                if (ProwInd[i] == tIdx) { relevantCol = 1; break; }\n            }\n\n            if (!relevantCol) { continue; }\n\n            if (rowNotFilled)\n            {\n                nnzPerRow = PjColEnd - PjColBegin;  // initial nnz per row of Ma\n                rowNotFilled = 0;\n                continue;\n            }\n\n            // if the row nnz count has been initialized, we need to figure out which elts haven't already appeared\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                // grab a row in the current column j of P\n                const int PiRowIndex = ProwInd[i];\n                bool rowPrevAppeared = 0;\n\n                for (int jj = j - 1; jj >= 0; jj--) // scan columns of P backwards since it's more likely that the same row entry\n                {\n                    // appeared in the direct neighbors of the current column\n                    const int PjjColBegin  = PcolOffsets[jj];\n                    const int PjjColEnd    = PcolOffsets[jj + 1];\n\n                    // check that the column is relevant (i.e. has a non-zero in the tIdx row)\n                    if (tIdx < ProwInd[PjjColBegin] || ProwInd[PjjColEnd - 1] < tIdx) { continue; }\n\n                    if (PiRowIndex < ProwInd[PjjColBegin] || ProwInd[PjjColEnd - 1] < PiRowIndex) { continue; }\n\n                    // make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n                    bool relevantPcol = 0;\n\n                    for (int ii = PjjColBegin; ii < PjjColEnd; ii++)\n                    {\n                        if (ProwInd[ii] == tIdx) { relevantPcol = 1; break; }\n                    }\n\n                    if (!relevantPcol) { continue; }\n\n                    // see if PiRowIndex has already appeared previously\n                    for (int ii = PjjColBegin; ii < PjjColEnd; ii++)\n                    {\n                        if (ProwInd[ii] == PiRowIndex) { rowPrevAppeared = 1; break; }\n                    }\n\n                    if (rowPrevAppeared) { break; } // stop scanning columns of P, since we already accounted for PiRowIndex previously\n                }\n\n                if (!rowPrevAppeared)  // if we haven't come across the P column with the nz in the PiRowIndex,\n                {\n                    // then we can increment the row nnz count\n                    nnzPerRow++;\n                }\n            }\n        }\n\n        if (nnzPerRow == 0)           { Ma_nnzPerRow[tIdx] = 1; }\n        else if (Ma_nzDiagRows[tIdx]) { Ma_nnzPerRow[tIdx] = nnzPerRow; }\n        else                          { Ma_nnzPerRow[tIdx] = nnzPerRow + 1; }\n    }\n}\n\n\n/*\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  find_Ma_nz_diag_rows_kernel(const IndexType *PcolOffsets, const IndexType *ProwInd,\n                                  const IndexType PnumCols, bool *Ma_nzDiagRows)\n{\n  for (unsigned int tIdx = threadIdx.x + blockDim.x*blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x*gridDim.x)\n  { // each thread goes thru a single column of P\n\n    const unsigned int PcolBegin = PcolOffsets[tIdx];\n    const unsigned int PcolEnd   = PcolOffsets[tIdx+1];\n\n    for (unsigned int i=PcolBegin; i<PcolEnd; i++)\n    {\n      if (tIdx == ProwInd[i])\n      {\n        Ma_nzDiagRows[tIdx] = 1;  // Mark the non-zero diagonal rows of Ma\n        break;\n      }\n    }\n\n  }\n}\n*/\n\n__global__\nvoid  get_unsorted_MaColInd_kernel( const int *PcolOffsets, const int *ProwInd, const int PnumCols,\n                                    const int *MaRowOffsets, int *MaColInd, const int AnumRows,\n                                    const bool *Ma_nzDiagRows )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on a single row of Ma (figures out column indices for that row)\n        int rowIsFilled = 0;         // indicates that MaColInd has not been initialized\n        const int MaRowBegin = MaRowOffsets[tIdx];\n        const int MaRowEnd   = MaRowOffsets[tIdx + 1];\n        const int MaRowSize  = MaRowEnd - MaRowBegin;\n\n        // Skip empty rows of Ma (should never happen if Ma_nzDiagRows is initialized ??)\n\n        if (MaRowSize <= 1)\n        {\n            if (MaRowSize == 0)\n            {\n                printf(\"\\nWARNING: found zero row of Ma in row %d. Ma_nzDiagRows[%d] = %d \\n\",\n                       tIdx, tIdx, Ma_nzDiagRows[tIdx]);\n            }\n            else { MaColInd[MaRowBegin] = tIdx; }   // diagonal entry is the only one.\n\n            continue;\n        }\n\n        int MaIndex = MaRowBegin;    // keeps count of the number of assigned indices to the Ma row\n\n        for (int j = 0; j < PnumCols; j++)\n        {\n            // thread goes thru all columns of P\n            const int PjColBegin = PcolOffsets[j];\n            const int PjColEnd   = PcolOffsets[j + 1];\n\n            // Check that the column is relevant (i.e. has a non-zero in the tIdx row)\n            if (tIdx < ProwInd[PjColBegin] || ProwInd[PjColEnd - 1] < tIdx) { continue; }\n\n            // Make sure the column is actually relevant (i.e. has a non-zero in the tIdx row)\n            bool relevantCol = 0;\n\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                if (ProwInd[i] == tIdx) { relevantCol = 1; break; }\n            }\n\n            if (!relevantCol) { continue; }\n\n            if (rowIsFilled == 0)  // Simply copy the row indices in the current P column\n            {\n                // to the column indices of the Ma row\n                for (int i = PjColBegin; i < PjColEnd; i++)\n                {\n                    MaColInd[MaIndex] = ProwInd[i];\n                    MaIndex++;          // Increment the number of assigned indices to the current row.\n                }\n\n                rowIsFilled = 1;      // indicates that Ma row is initialized but not completely filled\n                continue;\n            }\n\n            // If the row has been initialized, we need to take\n            // only those row indices of P that haven't already appeared\n            for (int i = PjColBegin; i < PjColEnd; i++)\n            {\n                // grab the next row index of the current column j of P\n                const int PiRowIndex = ProwInd[i];\n                bool indexPrevAppeared = 0;   // indicate that PiRowIndex haven't been previously assigned to the row of Ma\n\n                for (int jj = MaRowBegin; jj < MaIndex; jj++) // scan the accumulated Ma column indices and check whether\n                {\n                    // the current P row index already exists\n                    if (MaColInd[jj] == PiRowIndex)\n                    {\n                        // the index already exists, so skip it\n                        indexPrevAppeared = 1;\n                        break;\n                    }\n                }\n\n                if (!indexPrevAppeared)   // if the index is new to the MaColInd array, add it to the end (i.e. non-sorted)\n                {\n                    MaColInd[MaIndex] = PiRowIndex;\n                    MaIndex++;\n                }\n\n                if (MaIndex == MaRowEnd)  // stop searching, since we filled the entire Ma row.\n                {\n                    rowIsFilled = 2;        // indicates that Ma row is completely filled with column indices\n                    break;\n                }\n            }\n\n            if (rowIsFilled == 2) { break; } // stop searching, since we filled the entire Ma row.\n        }\n\n        if (!Ma_nzDiagRows[tIdx]) { MaColInd[MaIndex] = tIdx; }\n    } // end for (int tIdx ...)\n}\n\n\n\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  add_invAijs_to_Ma_col_major_kernel( const IndexType *PcolOffsets, const IndexType *ProwInd, const IndexType PnumCols,\n        const IndexType *AijOffsets, const ValueType *dense_invAijs,\n        const IndexType *MaRowOffsets, const IndexType *MaColInd,\n        ValueType *MaValues )\n{\n    // go thru all invAij matrices\n    for (unsigned int j = 0; j < PnumCols; j++)\n    {\n        // every thread runs through every invAij matrix and works on a single row of each invAij\n        const int AijOffset = AijOffsets[j];\n        const ValueType *invAij = dense_invAijs + AijOffset;\n        const unsigned int PcolBegin = PcolOffsets[j];\n        // dimension of the current Aij within dense_Aijs\n        const unsigned int AijNumRows = PcolOffsets[j + 1] - PcolOffsets[j];\n        const unsigned int lda = AijNumRows;  // leading dim of the current Aij (col-major)\n\n        for (unsigned int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AijNumRows; tIdx += blockDim.x * gridDim.x)\n        {\n            // each thread works on a single row of the current invAij\n            const unsigned int MaRowIndex = ProwInd[PcolBegin + tIdx];\n            // get the bounds of each thread's Ma row\n            const unsigned int MaRowBegin = MaRowOffsets[MaRowIndex];\n            const unsigned int MaRowEnd   = MaRowOffsets[MaRowIndex + 1];\n            unsigned int MaEntryIndex = MaRowBegin;\n\n            for (unsigned int i = 0; i < AijNumRows; i++)\n            {\n                // go through each element in the given row of invAij and put this element\n                // into its corresponding (\"spattered\") location in Ma\n                const unsigned int MaColIndex = ProwInd[PcolBegin + i];\n\n                // there are potentially more nz entries in a given row of Ma\n                // than the dimension of the current invAij, hence need this loop\n                // (note that we do not restart count since the col indices are stored in order)\n                for ( ; MaEntryIndex < MaRowEnd; MaEntryIndex++)\n                {\n                    if (MaColInd[MaEntryIndex] == MaColIndex)\n                    {\n                        MaValues[MaEntryIndex] += invAij[tIdx + i * lda];\n                        break;\n                    }\n                }\n            }\n        }\n\n        // to avoid race conditions when accessing rows of Ma, all threads need to be in sync.\n        __syncthreads();\n    }\n}\n\n\n// This kernel perturbs the diagonal of Ma (i.e. adds a specified value to each diag entry)\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid  perturb_Ma_diag_kernel( const IndexType *MaRowOffsets, const IndexType *MaColInd,\n                              ValueType *MaValues, const IndexType AnumRows,\n                              const ValueType perturb_mag, const bool *Ma_nzDiagRows )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread perturbs one diagonal entry of Ma\n        const int MaRowBegin = MaRowOffsets[tIdx];\n        const int MaRowEnd   = MaRowOffsets[tIdx + 1];\n        bool diagIsZero = 1;\n\n        for (int j = MaRowBegin; j < MaRowEnd; j++)\n        {\n            // find the diagonal entry in the current row\n            if (tIdx == MaColInd[j])\n            {\n                diagIsZero = 0;\n\n                if (!Ma_nzDiagRows[tIdx]) { MaValues[j] = perturb_mag; break;}\n\n                MaValues[j] += perturb_mag;\n                break;\n            }\n        }\n\n        if (diagIsZero)\n        {\n            printf(\"\\nWARNING: found zero diagonal entry of Ma in row %d. Ma_nzDiagRows[%d] = %d \\n\",\n                   tIdx, tIdx, Ma_nzDiagRows[tIdx]);\n            break;\n        }\n    }\n}\n\n// This kernel initializes each column of P to v_x.\ntemplate <typename IndexType, typename ValueType, typename ValueTypeB>\n__global__\nvoid init_Pvalues_kernel( const IndexType *PcolOffsets, const IndexType *ProwInd,\n                          const IndexType PnumCols, const ValueTypeB *v_x,\n                          ValueType *Pvalues )\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < PnumCols; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one column of P in CSC format (P^T in CSR format)\n        const unsigned int PcolBegin = PcolOffsets[tIdx];\n        const unsigned int PcolEnd   = PcolOffsets[tIdx + 1];\n\n        for (int j = PcolBegin; j < PcolEnd; j++)\n        {\n            // go thru each entry in the given column of P\n            Pvalues[j] = v_x[ProwInd[j]];\n        }\n    }\n}\n\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nEM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\nEM_Interpolator() : EM_InterpolatorBase<TConfig_d>(),\n    m_cuds_handle(0),\n    m_dense_Aijs(0),\n    m_dense_invAijs(0),\n    m_ipiv(0),\n    m_cuds_info(0),\n    m_cuds_wspace(0)\n{\n    // Allocate a handle for cudense\n    hipsolverStatus_t status = hipsolverDnCreate(&m_cuds_handle);\n\n    if ( status != HIPSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Define the cudense stream.\n    status = hipsolverSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n    if ( status != HIPSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    allocMem(m_cuds_info, sizeof(int), false);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nEM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~EM_Interpolator()\n{\n    if (m_cuds_handle)\n    {\n        hipsolverDnDestroy(m_cuds_handle);\n    }\n\n    if (m_dense_Aijs)\n    {\n        amgx::memory::hipFreeAsync(m_dense_Aijs);\n        m_dense_Aijs = 0;\n    }\n\n    if (m_dense_invAijs)\n    {\n        amgx::memory::hipFreeAsync(m_dense_invAijs);\n        m_dense_invAijs = 0;\n    }\n\n    if (m_ipiv)\n    {\n        amgx::memory::hipFreeAsync(m_ipiv);\n        m_ipiv = 0;\n    }\n\n    if (m_cuds_wspace)\n    {\n        amgx::memory::hipFreeAsync(m_cuds_wspace);\n        m_cuds_wspace = 0;\n    }\n\n    if (m_cuds_info)\n    {\n        amgx::memory::hipFreeAsync(m_cuds_info);\n        m_cuds_info = 0;\n    }\n}\n\n\n\n// Private functions for the implementation on device\n\n// Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePsparsity( const Matrix_d &A, const IVector &cf_map, const IVector &coarse_idx,\n                    Matrix_d &P, IVector &PnnzPerCol,\n                    BVector &Ma_nzDiagRows )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // temporary vectors - diagonal and non-zero offsets\n    VVector Adiag(A.get_num_rows(), 0.0);\n    ValueType *Adiag_ptr  = Adiag.raw();\n    // fullToCoarseMap array gives the COARSE index corresponding to the given FULL index\n    // (meaningless if the full index corresponds to a FINE pt)\n    IntVector fullToCoarseMap(AnumRows, 0);\n    int *fullToCoarseMap_ptr = fullToCoarseMap.raw();\n    amgx::thrust::transform_exclusive_scan( cf_map.begin(), cf_map.end(), fullToCoarseMap.begin(),\n                                      is_coarse(), 0, amgx::thrust::plus<int>() );\n    cudaCheckError();\n    // Extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(AnumRows, A.diag.raw(), Avalues_ptr, Adiag_ptr);\n    cudaCheckError();\n    // Coarse/Fine map\n    const int *cf_map_ptr = cf_map.raw();\n    // Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\n    // Count the number of non-zeros in the interpolation if\n    const int numCoarse = coarse_idx.size();\n    const int *coarse_idx_ptr = coarse_idx.raw();\n    VVector thresh_per_row(AnumRows);\n    count_PnnzPerCol_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                Adiag_ptr, numCoarse,\n                                cf_map_ptr, fullToCoarseMap_ptr, coarse_idx_ptr,\n                                PnnzPerCol.raw(), thresh_per_row.raw());\n                                cudaCheckError();\n    printMatrixStats(A);\n    cudaCheckError();\n    // Column offsets of P (CSC format)\n    IntVector PnzOffsets(numCoarse + 1);\n    PnzOffsets[0] = 0;\n    // get the offsets in P with an inclusive scan\n    thrust_wrapper::inclusive_scan<AMGX_device>(PnnzPerCol.begin(), PnnzPerCol.end(), PnzOffsets.begin() + 1);\n    cudaCheckError();\n    // get total num of non-zeros in P\n    const int Pnnz = PnzOffsets[numCoarse];\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);  // IMPORTANT: P actually stores P^T in CSR format\n    //            because we build P by columns (and CSC format is not supported)\n    P.resize(numCoarse, AnumRows, Pnnz, 1);\n    // set P offsets (P column offsets or P^T row offsets)\n    amgx::thrust::copy(PnzOffsets.begin(), PnzOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // treat P as if it were in the CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    IndexType *ProwInd_ptr           = P.col_indices.raw();\n    bool *Ma_nzDiagRows_ptr = Ma_nzDiagRows.raw();\n    // Since CSC format is not supported in AMGX matrix, we instead\n    // compute P^T (which is stored in P) in the CSR format.\n    // Initialize P by performing greedy aggregation based on the coarse set (cf_map)\n    // The values of P remain uninitialized, since at this point we only get the sparsity pattern of P.\n    init_ProwInd_greedy_aggregation_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                Adiag_ptr, AnumRows,\n                                cf_map_ptr, fullToCoarseMap_ptr,\n                                PcolOffsets_ptr, ProwInd_ptr,\n                                Ma_nzDiagRows_ptr);\n    cudaCheckError();\n}\n\n\n// Compute the submatrices Aij's of A and their inverses invAij's\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computeAijSubmatrices(const Matrix_d &A, const int numCoarse, const Matrix_d &P,\n                        ValueType *dense_Aijs, ValueType *dense_invAijs,\n                        const IntVector &AijOffsets, int *ipiv,\n                        hipsolverHandle_t &cuds_handle, int *cuds_info)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1));\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    // Treat P as if it were in the CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    const IndexType *AijOffsets_ptr  = AijOffsets.raw();\n    // Extract Aij's from A based on the sparsity pattern of each column of P.\n    // Column major format is used for storage of Aij's.\n    extract_dense_Aijs_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr, Avalues_ptr,\n                                PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                AijOffsets_ptr, dense_Aijs);\n    cudaCheckError();\n    // Initialize invAij's to (dense) identity matrices.\n    init_dense_invAijs_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, numCoarse,\n                                AijOffsets_ptr, dense_invAijs);\n    cudaCheckError();\n    // Sequentially perform dense LU factorization on all Aij's\n    // LU factors stored in place of the Aij's\n    // Then, perform inversion of Aij submatrices by solving with RHS == identity matrix\n    hipsolverStatus_t cudsStat;\n\n    if (!cuds_handle)\n    {\n        hipsolverStatus_t status = hipsolverDnCreate(&m_cuds_handle);\n\n        if ( status != HIPSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n        }\n\n        // Define the cudense stream.\n        status = hipsolverSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n        if ( status != HIPSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n        }\n    }\n\n    // Pointer to info from cudense\n    if (!cuds_info) { cuds_info = (int *) malloc(sizeof(int)); }\n\n    // Offset of cudense pivoting sequence for each Aij.\n    int ipivOffset = 0;\n    IVector_h sizes(numCoarse);\n    int max_wsize = 0;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int cur_wsize = 0;\n        int AijOffset  = AijOffsets[j];\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;\n        hipsolverStatus_t status = hipsolverDnXgetrf_bufferSize(cuds_handle, AijNumRows, AijNumRows, dense_Aijs + AijOffset, lda, &cur_wsize);\n\n        if (status != HIPSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform LU factorization\", AMGX_ERR_INTERNAL);\n        }\n\n        max_wsize = std::max(cur_wsize, max_wsize);\n    }\n\n    allocMem(m_cuds_wspace, max_wsize, false);\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];                           // offset of the current Aij\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j]; // dimension of the next Aij within dense_Aijsc\n        int lda = AijNumRows;                                     // leading dim for the next Aij (col-major)\n        sizes[j] = AijNumRows;\n        // [L,U,P] = lu(Aij) (LU factors stored in place of the current Aij)\n        cudsStat = hipsolverDnXgetrf(cuds_handle, AijNumRows, AijNumRows,\n                                    dense_Aijs + AijOffset, lda, m_cuds_wspace,\n                                    ipiv + ipivOffset, cuds_info);\n\n        if (cudsStat != HIPSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform LU factorization\", AMGX_ERR_INTERNAL);\n        }\n\n        // invAij = U\\(L\\P) (i.e. find inverse of Aij).\n        cudsStat = hipsolverDnXgetrs(cuds_handle, HIPBLAS_OP_N, AijNumRows, AijNumRows,\n                                    dense_Aijs + AijOffset, lda,\n                                    ipiv + ipivOffset, dense_invAijs + AijOffset, lda, cuds_info );\n\n        if (cudsStat != HIPSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"failed to perform triangular solve\", AMGX_ERR_INTERNAL);\n        }\n\n        ipivOffset += AijNumRows;   // starting offset of the next pivoting sequence\n    }\n}\n\n\n// Compute Ma matrix\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computeMa(Matrix_d &Ma, const int AnumRows, const int numCoarse, const Matrix_d &P,\n            const ValueType *dense_invAijs, const IntVector &AijOffsets,\n            const BVector &Ma_nzDiagRows,\n            const bool perturb_Ma_diag, const ValueType perturb_mag)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    // Number of non-zeros in each row of Ma\n    IntVector Ma_nnzPerRow(AnumRows, 0);\n    // 1. Compute Ma_nnzPerRow (kernel).\n    count_Ma_nnz_per_row_kernel <<< numBlocks, blocksize>>>( PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n            Ma_nnzPerRow.raw(), AnumRows, Ma_nzDiagRows.raw() );\n    cudaCheckError();\n    // 2. Resize Ma and assign MaRowOffsets by performing a scan on Ma_nnzPerRow.\n    // Get the offsets in Ma with an inclusive scan (in-place)\n    thrust_wrapper::inclusive_scan<AMGX_device>(Ma_nnzPerRow.begin(), Ma_nnzPerRow.end(), Ma_nnzPerRow.begin());\n    cudaCheckError();\n    // Get total num of non-zeros in Ma\n    const int Ma_nnz = Ma_nnzPerRow[AnumRows - 1];\n    // Resize Ma\n    Ma.resize(0, 0, 0, 1);\n    Ma.addProps(CSR);\n    Ma.resize(AnumRows, AnumRows, Ma_nnz, 1);\n    Ma.row_offsets[0] = 0;\n    // set Ma row offsets\n    amgx::thrust::copy(Ma_nnzPerRow.begin(), Ma_nnzPerRow.end(), Ma.row_offsets.begin() + 1);\n    cudaCheckError();\n    const IndexType *MaRowOffsets_ptr = Ma.row_offsets.raw();\n    IndexType *MaColInd_ptr           = Ma.col_indices.raw();\n    ValueType *MaValues_ptr           = Ma.values.raw();\n    // 3. Get the unsorted column indices of Ma.\n    get_unsorted_MaColInd_kernel <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n            MaRowOffsets_ptr, MaColInd_ptr, AnumRows,\n            Ma_nzDiagRows.raw());\n    cudaCheckError();\n\n    // 4. Sort column indices to get the final MaColInd.\n    for (unsigned int MaRow = 0; MaRow < AnumRows; MaRow++)\n    {\n        int MaRowBegin = Ma.row_offsets[MaRow];\n        int MaRowEnd   = Ma.row_offsets[MaRow + 1];\n\n        if (MaRowEnd - MaRowBegin > 1)\n        {\n            thrust_wrapper::sort<AMGX_device>( Ma.col_indices.begin() + Ma.row_offsets[MaRow],\n                          Ma.col_indices.begin() + Ma.row_offsets[MaRow + 1] );\n        }\n    }\n\n    // 5. Compute MaValues by summing up the \"spattered\" inverses of Aij submatrices.\n    add_invAijs_to_Ma_col_major_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                AijOffsets.raw(), dense_invAijs,\n                                MaRowOffsets_ptr, MaColInd_ptr,\n                                MaValues_ptr);\n    cudaCheckError();\n\n    // Perturb the diagonal of Ma here (prevents Ma from being singular)\n    // Ma = 1.0e-8*speye(n,n);\n    if (perturb_Ma_diag)\n    {\n        // Default: perturb_mag = 1.0e-8\n        perturb_Ma_diag_kernel<IndexType, ValueType>\n        <<< numBlocks, blocksize>>>(MaRowOffsets_ptr, MaColInd_ptr,\n                                    MaValues_ptr, AnumRows,\n                                    perturb_mag, Ma_nzDiagRows.raw());\n        cudaCheckError();\n    }\n\n    Ma.set_initialized(1);\n}\n\n\n// Solve for x: Ma*x=e (solved with pcg)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::solveMa_e(Matrix_d &Ma, const int AnumRows, Vector_d &v_x)\n{\n    typedef typename Matrix_d::value_type ValueType;\n    // 1. Initialize x (vector of 0's) and e (vector of 1's)\n    v_x.resize(AnumRows, 0.0);\n    Vector_d v_e(AnumRows, 1.0);\n    // ------------------------------ define pcg solver ---------------------------------\n    // temporary config and pointer to main config (json format)\n    AMG_Config cfg;\n    std::string cfg_string = \"{ \\\"config_version\\\": 2\";\n    cfg_string += \", \\\"solver\\\": { \\\"scope\\\": \\\"pcg_scope\\\", \\\"solver\\\": \\\"PCG\\\"\";   // Set the solver to PCG\n    cfg_string += \" ,\\\"preconditioner\\\": \\\"NOSOLVER\\\"\";    // IMPORTANT to have this in order to allocate PCG\n    cfg_string += \" } }\";\n    cfg.parseParameterString(cfg_string.c_str());\n    // define and allocate solver\n    Solver<TConfig_d> *Ma_solver = 0;\n    Ma_solver = SolverFactory<TConfig_d>::allocate(cfg, \"default\", \"solver\");\n    // ------------------------------ end define pcg ------------------------------------\n    const int Ma_solveIts = 20;\n    const ValueType Ma_solveTol = (ValueType) 1.e-4;\n    Ma_solver->setup(Ma, false);\n    Ma_solver->set_max_iters(Ma_solveIts);\n    Ma_solver->setTolerance(Ma_solveTol);\n    // Apply pcg to solve x = Ma \\ e\n    Ma_solver->solve( v_e, v_x, true );\n    Vector_d v_res(AnumRows, 0.);\n    Ma_solver->compute_residual( v_e, v_x, v_res );\n\n    if (Ma_solver) { delete Ma_solver; Ma_solver = 0; }\n}\n\n\n// Compute the values of P (using LU factorization of Aij submatrices)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePvalues( const int AnumRows, const int numCoarse, Matrix_d &P, const Vector_d &v_x,\n                  const ValueType *dense_Aijs, const IntVector &AijOffsets, const int *ipiv,\n                  hipsolverHandle_t &cuds_handle, int *cuds_info )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    ValueType *Pvalues_ptr           = P.values.raw();\n    init_Pvalues_kernel<IndexType, ValueType, ValueTypeB>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                v_x.raw(), Pvalues_ptr);\n    cudaCheckError();\n    hipsolverStatus_t cudsStat;\n\n    if (!cuds_handle)\n    {\n        hipsolverStatus_t status = hipsolverDnCreate(&m_cuds_handle);\n\n        if ( status != HIPSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n        }\n\n        // Define the cudense stream.\n        status = hipsolverSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n        if ( status != HIPSOLVER_STATUS_SUCCESS )\n        {\n            FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n        }\n    }\n\n    // Pointer to info from cudense\n    if (!cuds_info) { cuds_info = (int *) malloc(sizeof(int)); }\n\n    // Offset of cudense pivoting sequence for each Aij.\n    int ipivOffset = 0;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];\n        int PcolOffset = P.row_offsets[j];\n        // dimension of the next Aij within dense_Aijs\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;  // leading dim for the next Aij (col-major)\n\n        if (lda == 0) { continue; }\n\n        // invAij = U\\(L\\P) (i.e. find inverse of Aij).\n        cudsStat = hipsolverDnXgetrs(cuds_handle, HIPBLAS_OP_N, AijNumRows, 1,\n                                    dense_Aijs + AijOffset, lda,\n                                    ipiv + ipivOffset, Pvalues_ptr + PcolOffset, lda, cuds_info);\n\n        if (cudsStat != HIPSOLVER_STATUS_SUCCESS)\n        { FatalError(\"failed to perform triangular solve\", AMGX_ERR_INTERNAL); }\n\n        ipivOffset += AijNumRows;   // starting offset of the next pivoting sequence\n    }\n\n    if (cuds_info)  { free(cuds_info);  cuds_info = 0; }\n}\n\n\n\n// Compute the values of P (using inverses of Aij submatrices)\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::computePvalues( const int AnumRows, const int numCoarse, Matrix_d &P, const Vector_d &v_x,\n                  const ValueType *dense_invAijs, const IntVector &AijOffsets )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Treat P as if it were in CSC format.\n    const IndexType *PcolOffsets_ptr = P.row_offsets.raw();\n    const IndexType *ProwInd_ptr     = P.col_indices.raw();\n    ValueType *Pvalues_ptr           = P.values.raw();\n    init_Pvalues_kernel<IndexType, ValueType, ValueTypeB>\n    <<< numBlocks, blocksize>>>(PcolOffsets_ptr, ProwInd_ptr, numCoarse,\n                                v_x.raw(), Pvalues_ptr);\n    cudaCheckError();\n    ValueType *place_holder_ptr = 0;\n    allocMem(place_holder_ptr, sizeof(ValueType), false);\n    ValueType one = (ValueType) 1.;\n    ValueType zero = (ValueType) 0.;\n\n    for (int j = 0; j < numCoarse; j++)\n    {\n        int AijOffset  = AijOffsets[j];\n        int PcolOffset = P.row_offsets[j];\n        // dimension of the next Aij within dense_Aijs\n        int AijNumRows = P.row_offsets[j + 1] - P.row_offsets[j];\n        int lda = AijNumRows;  // leading dim for the next Aij (col-major)\n\n        if (lda == 0) { continue; }\n\n        Cublas::gemv(false, AijNumRows, AijNumRows, &one,\n                     dense_invAijs + AijOffset, lda,\n                     Pvalues_ptr + PcolOffset, 1,\n                     &zero, place_holder_ptr, 1);\n        hipDeviceSynchronize();\n        cudaCheckError();\n    }\n\n    if (place_holder_ptr) { amgx::memory::hipFreeAsync(place_holder_ptr); place_holder_ptr = 0; }\n}\n\n\n\n// ----------------------------\n//  specialization for device\n// ----------------------------\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid EM_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::generateInterpolationMatrix_1x1(const Matrix_d &A, const IntVector &cf_map,\n                                  Matrix_d &P, void *amg)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Number of rows in Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Count the number of coarse points\n    const int numCoarse = (int) amgx::thrust::count(cf_map.begin(), cf_map.end(), (int)COARSE);\n    cudaCheckError();\n    IVector coarse_idx(numCoarse, -1);\n    amgx::thrust::counting_iterator<int> zero(0);\n    amgx::thrust::counting_iterator<int> zero_plus_nc = zero + numCoarse;\n    amgx::thrust::copy_if(zero, zero_plus_nc, cf_map.begin(), coarse_idx.begin(), is_coarse());\n    // ..................................................................................\n    // ------------------------ Begin Compute P code ------------------------------------\n    // Store the number of non-zeros in each column of the interpolation matrix P\n    IntVector PnnzPerCol(numCoarse);\n    BVector Ma_nzDiagRows(AnumRows, 0);\n    // Find the sparse dimensions of P (i.e. number of non-zeros in each column of P)\n    computePsparsity(A, cf_map, coarse_idx, P, PnnzPerCol, Ma_nzDiagRows);\n    // Get total num of non-zeros in P\n    int Pnnz = P.row_offsets[numCoarse];\n    // ..................................................................................\n    // Now follows Jacobi smoothing (skip for now since it might be unnecessary)\n    // ..................................................................................\n    // -------------------------- End Compute P code ------------------------------------\n    // ..................................................................................\n    // ..................................................................................\n    // ----------------------- Begin Energy Minimization Code ---------------------------\n    // Given:\n    //        A: the problem matrix\n    //        P: sparsity pattern for P (n x nc)\n    // Main loop (Ma here is the inverse of Ma in the paper)\n    // I. Build Ma.\n    // 0. Allocate space for storing LU factors for each Aij submatrix of A (use dense format)\n    //    There are numCoarse of LU factors.\n    //    Since the number of non-zeros in each column of P determines the size of each Aij,\n    //    we know the dimensions of all Aij based on the row offsets of P^T.\n    // Calculate offset of each Aij matrix in the dense_Aijs array\n    IntVector AijOffsets(numCoarse + 1);\n    AijOffsets[0] = 0;\n    // Get the offsets with an inclusive scan (nnz of each column of P is the dimension of each Aij)\n    amgx::thrust::transform_inclusive_scan( PnnzPerCol.begin(), PnnzPerCol.end(), AijOffsets.begin() + 1,\n                                      square(), amgx::thrust::plus<int>() );\n    cudaCheckError();\n    PnnzPerCol.clear();\n    // Cumulative num of entries in all (dense) Aijs (last entry in the offsets)\n    const int Aijs_size = AijOffsets[numCoarse];\n    // Allocate memory for the dense Aij submatrices and initialize to zero.\n    allocMem(m_dense_Aijs, Aijs_size, true);\n    // Allocate memory for the dense invAij's and initialize to zero.\n    allocMem(m_dense_invAijs, Aijs_size, true);\n    // Allocate memory for cudense pivoting sequence.\n    allocMem(m_ipiv, Pnnz, false);\n    // Compute the submatrices Aij's of A and the inverses invAij's\n    computeAijSubmatrices(A, numCoarse, P, m_dense_Aijs, m_dense_invAijs,\n                          AijOffsets, m_ipiv, m_cuds_handle, m_cuds_info);\n    // Ma matrix build sequence.\n    // 1. Compute Ma_nnzPerRow (kernel).\n    // 2. Resize Ma and assign MaRowOffsets by performing a scan on Ma_nnzPerRow (thrust).\n    // 3. Get the unsorted column indices of Ma (kernel).\n    // 4. Sort column indices to get the final MaColInd (thrust).\n    // 5. Compute MaValues by summing up the \"spattered\" inverses of Aij submatrices (kernel).\n    // Declare Ma\n    Matrix_d Ma;\n    // Compute Ma matrix\n    computeMa(Ma, AnumRows, numCoarse, P, m_dense_invAijs, AijOffsets, Ma_nzDiagRows);\n\n    if (m_dense_invAijs) { amgx::memory::hipFreeAsync(m_dense_invAijs);  m_dense_invAijs = 0; }\n\n    /* At this point, we are done with the following Matlab code analogue.\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the dofs in the j-th block\n      ij = find(p(:,j));              // col vec of subindices\n      ni = length(ij);\n      It = sparse(n,ni);\n      It(ij,:) = speye(ni);\n      Aij{j} = inv(A(ij,ij));\n      // Form Ma\n      // A(ii,ii): submatrix of A corresponding to the dofs in the i-th block\n      Ma = Ma + It*(Aij{j}*It_T);\n    }\n    */\n    Ma_nzDiagRows.clear();\n    // II. Solve for x: Ma*x=e\n    //m_Mae_norm = (ValueType) 0.;\n    Vector_d v_x;\n    solveMa_e(Ma, AnumRows, v_x);\n    hipDeviceSynchronize();\n    cudaCheckError();\n    Ma.set_initialized(0);\n    Ma.resize(0, 0, 0, 1);\n    /*  Now, we are done with the following Matlab code analogue.\n    e = ones(n,1);\n    Mae = zeros(size(e));  // Ma\\e;\n    [Mae,flag,relres,iter,resvecCG] = pcg(Ma,e,1e-4,100);\n    */\n    // III. Construct the columns of P in parallel.\n    // P is already initialized (and at this point stores P^T in CSR format)\n    // We will be performing the following operation to build each column of P\n    // P(:,j) = It{j}*(invAij{j}*(It^T*x));\n    // This is done with the following steps.\n    // First, apply It{j}^T mask to x to get x{j} (project x onto the dimension of Aij{j}).\n    // Then solve Aij\\x{j}\n    // Compute the values of P\n    computePvalues( AnumRows, numCoarse, P, v_x, m_dense_Aijs, AijOffsets, m_ipiv, m_cuds_handle, m_cuds_info );\n\n    if (m_dense_Aijs) { amgx::memory::hipFreeAsync(m_dense_Aijs);  m_dense_Aijs = 0; }\n\n    if (m_ipiv)       { amgx::memory::hipFreeAsync(m_ipiv);        m_ipiv = 0; }\n\n    if (m_cuds_info)  { amgx::memory::hipFreeAsync(m_cuds_info);   m_cuds_info = 0; }\n\n    //if (m_dense_invAijs) { amgx::memory::cudaFreeAsync(m_dense_invAijs);  m_dense_invAijs = 0; }\n    /* Now, we are done building columns of P (stored as rows of P^T in CSR).\n     * Here's the equivalent matlab code.\n    P = zeros(n,nc);\n    for (int j=1; j<=nc; j++)\n    {\n      // Find the dofs in the j-th block\n      ij=find(p(:,j));\n      ni = length(ij);\n      It = zeros(n,ni);\n      It(ij,:) = eye(ni);\n\n      // Form j-th column of P\n      P(:,j) = It*(Aij{j}*(It_T*Mae));\n    }\n    */\n    //TODO: truncate rows/cols of P to keep the sparsity low on all levels.\n    // ------------------------- End Energy Minimization Code ---------------------------\n    // ..................................................................................\n}\n\n\n\ntemplate< class T_Config>\nvoid EM_InterpolatorBase<T_Config>\n::generateInterpolationMatrix(const Matrix<T_Config> &A, const IntVector &cf_map,\n                              Matrix<T_Config> &P, void *amg)\n{\n    P.set_initialized(0);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, P, amg);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for distance1 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    P.computeDiagonal();\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class EM_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class EM_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace energymin\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/energymin/interpolators/em.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <distributed/distributed_arranger.h>\n#include <thrust/sequence.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/functional.h>\n#include <basic_types.h>\n#include <thrust/host_vector.h>\n#include <thrust/scan.h>\n#include <thrust/count.h>\n#include <thrust/sort.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/gather.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <csr_multiply.h>\n#include <algorithm>\n#include <thrust_wrapper.h>\n\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\n\n/***************************************\n * Source Definitions\n ***************************************/\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x) const\n    {\n        return x < 0;\n    }\n};\n\n\nstruct is_non_neg\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x >= 0;\n    }\n};\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid flag_boundary_nodes_P(const IndexType *A_rows, const IndexType *A_cols, int *boundary_flags,  int num_owned_coarse_pts, int A_num_rows, int* nnz_interior)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    int nnz_int = 0;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        // Load A row IDs.\n        int aColIt  = A_rows[aRowId  ];\n        int aColEnd = A_rows[aRowId + 1];\n        int nnz = aColEnd - aColIt;\n        bool halo_found = 0;\n\n        // Iterate over the columns of A.\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) && !halo_found; aColIt += 32 )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n            bool is_halo = (aColIt < aColEnd) && (aColId >= num_owned_coarse_pts);\n\n            // Push coarse and strongly connected nodes in the set.\n            if (utils::any(is_halo))\n            {\n                halo_found = true;\n                break;\n            }\n        }\n\n        if (laneId == 0)\n        {\n            if(halo_found)\n            {\n                boundary_flags[aRowId] = 1;\n            }\n            else\n            {\n                nnz_int += nnz;\n            }\n        }\n    }\n\n    if(laneId == 0)\n    {\n        atomicAdd(nnz_interior, nnz_int);\n    }\n}\n\n\n__global__\nvoid flag_boundary_nodes(int *b2l_maps, int *boundary_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        boundary_flags[b2l_maps[tidx]] = 1;\n    }\n}\n\n__global__\nvoid flag_interior_nodes(int *boundary_flags, int *interior_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        interior_flags[tidx] = !boundary_flags[tidx];\n    }\n}\n\n__global__\nvoid fill_rows_lists(int *__restrict__ interior_rows_lists, int *__restrict__ boundary_rows_lists, const int *__restrict__ interior_flags, const int *__restrict__ boundary_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int bdy_flag = boundary_flags[tidx];\n\n        if (bdy_flag != boundary_flags[tidx + 1]) { boundary_rows_lists[bdy_flag] = tidx; }\n\n        int int_flag = interior_flags[tidx];\n\n        if (int_flag != interior_flags[tidx + 1]) { interior_rows_lists[int_flag] = tidx; }\n    }\n}\n\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid fill_B_B2L_map(const IndexType *maps_offsets, const IndexType *A_b2l_map, const IndexType *row_offsets, IndexType *C_hat_b2l_map, int size, int rank)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < size ; aRowId += gridDim.x * nWarps )\n    {\n        // Load A row IDs.\n        int aColIt  = maps_offsets[aRowId  ];\n        int size = maps_offsets[aRowId + 1] - aColIt;\n        int start = row_offsets[A_b2l_map[aRowId]];\n        int end = start + size;\n\n        // Iterate over the columns of A.\n        for ( start += laneId, aColIt += laneId ; utils::any( start < end ) ; start += 32, aColIt += 32 )\n        {\n            if (start < end)\n            {\n                C_hat_b2l_map[aColIt] = start;\n            }\n        }\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void fill_halo_rows_row_ids(int64_t *halo_rows_row_ids, INDEX_TYPE *halo_rows_local_row_ids, int64_t base_index, INDEX_TYPE num_owned_coarse_pts, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int64_t gid = halo_rows_row_ids[tidx];\n        halo_rows_local_row_ids[tidx] = (INDEX_TYPE) (gid - base_index);\n\n        if (gid < base_index || gid > base_index + num_owned_coarse_pts)\n        {\n            printf(\"Should not be here in fill_halo_rows_row_ids\\n\");\n        }\n    }\n}\n\n__global__\nvoid fill_local_to_global_map(int *bdy_list, int64_t *local_to_global, int64_t offset, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        local_to_global[tidx] = bdy_list[tidx] + offset;\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void remove_fine_halo(int64_t *cf_map_global, INDEX_TYPE *flag, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        flag[tidx] = (cf_map_global[tidx] == -1) ? 0 : 1;\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void fill_P_local_to_global(int64_t *P_local_to_global, int64_t *cf_map_global, INDEX_TYPE *flag, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        if (cf_map_global[tidx] != -1)\n        {\n            P_local_to_global[flag[tidx]] = cf_map_global[tidx];\n        }\n    }\n}\n\ntemplate <int coop>\n__global__ void calc_num_neighbors(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < part_offsets[my_id] || col >= part_offsets[my_id + 1])\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_offsets[part] || col >= part_offsets[part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_offsets[part] && col < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n// this is assuming local indices (used in matrix A)\ntemplate <int coop>\n__global__ void calc_num_neighbors_v2_local(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, int64_t *local_to_global_map, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            // Check if column point to halo node\n            if (col >= num_rows)\n            {\n                int64_t gcol = local_to_global_map[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (gcol < part_offsets[part] || gcol >= part_offsets[part + 1]))\n                {\n                    part++;\n                }\n\n                if (part < num_part && (gcol >= part_offsets[part] && gcol < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n// this is assuming global indices (used in halo rows)\ntemplate <int coop>\n__global__ void calc_num_neighbors_v2_global(INDEX_TYPE *row_offsets, int64_t *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n    int64_t start = part_offsets[my_id];\n    int64_t end = part_offsets[my_id + 1];\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = col_indices[i];\n\n            // Check if column point to halo node\n            if (col < start || col >= end)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_offsets[part] || col >= part_offsets[part + 1]))\n                {\n                    part++;\n                }\n\n                if (part < num_part && (col >= part_offsets[part] && col < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void flag_halo_nodes(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + col - part_ranges[2 * part]] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop, int set_val>\n__global__ void flag_halo_nodes_local(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t global_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = set_val > 0 ? set_val : -col - 1; // if -1, then set -(local index)-1 to restore later\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n__global__ void flag_halo_nodes_from_local_to_global_map(int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE size, int64_t *local_to_global)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < size)\n    {\n        int64_t global_col = local_to_global[tid];\n        int part = 0;\n\n        while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1]))\n        {\n            part++;\n        }\n\n        if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n        {\n            int col_id = range + tid;\n            flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = -1 - col_id;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void flag_halo_nodes_local_v2(int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE local_to_global_size, int64_t *local_to_global)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < local_to_global_size)\n    {\n        int64_t global_col = local_to_global[tid];\n        int col =  range + tid;\n        // Identify the partition that owns that node\n        int part = 0;\n\n        while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n        // Flag the corresponding node in flags array\n        if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n        {\n            flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = -col - 1;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <int coop>\n__global__ void flag_halo_nodes_global_v2(INDEX_TYPE *row_offsets, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t global_col = global_col_indices[i];\n\n            if (global_col < base || global_col >= base + (int64_t) range)\n            {\n                int part = 0;\n\n                while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + (int) (global_col - part_ranges[2 * part])] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n\n\ntemplate <int coop, int set_val>\n__global__ void flag_halo_nodes_global(INDEX_TYPE *row_offsets, INDEX_TYPE num_rows, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE *local_col_indices)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                // exterior node\n                int part = 0;\n\n                while (part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    // check if the flag is already set (i.e. 1-ring node) then skip it\n                    int flag = flags[flag_offsets[part] + col - part_ranges[2 * part]];\n\n                    if (flag == 0)\n                    {\n                        flags[flag_offsets[part] + col - part_ranges[2 * part]] = set_val;\n                    }\n                    else if (flag < 0)    // it is possible that flag is already set to 1 in this kernel, so if it's negative then it's the 1st ring\n                    {\n                        // update local index, note that local to global mapping is unchanged for 1st ring\n                        local_col_indices[i] = -flags[flag_offsets[part] + col - part_ranges[2 * part]] - 1;\n                    }\n                }\n            }\n            else\n            {\n                // interior node\n                local_col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void renumber_col_indices_global(INDEX_TYPE *row_offsets, int *col_indices, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                // Need to get his local index from the flag array\n                int part = 0;\n\n                while ( part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int pos_in_flag = flag_offsets[part] + col - part_ranges[2 * part];\n                    //int local_col = flags[pos_in_flag];\n                    col_indices[i] = range + flags[pos_in_flag];\n                    //bool flag = (local_col != flags[pos_in_flag+1]);\n\n                    if (flags[pos_in_flag] == flags[pos_in_flag + 1])\n                    {\n                        printf(\"should not be here in renumber_col_indices_global\\n\");\n                    }\n\n                    //if (flag)\n                }\n            }\n            else\n            {\n                // interior node\n                col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global, int64_t *new_local_to_global, INDEX_TYPE *halo_indices, int halo_base, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t glob_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (glob_col < part_ranges[2 * part] || glob_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (glob_col >= part_ranges[2 * part] && glob_col < part_ranges[2 * part + 1]))\n                {\n                    int pos = glob_col - part_ranges[2 * part];\n                    int actual_col = halo_base + halo_indices[pos];\n                    col_indices[i] = -1 - actual_col;\n                    new_local_to_global[actual_col - num_rows] = glob_col;\n                    // when we renumber we also store mapping to use later in residual restriction since matrix R has other order of halo indices\n                    // store both direct and inverse renumbering for P & R matrices\n                    //halo_P_renumbering[col - num_rows] = actual_col;\n                    //halo_R_renumbering[actual_col - num_rows] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping_v3(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global, int64_t *new_local_to_global, INDEX_TYPE *halo_indices, int halo_base, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t glob_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (glob_col < part_ranges[2 * part] || glob_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (glob_col >= part_ranges[2 * part] && glob_col < part_ranges[2 * part + 1]))\n                {\n                    int pos = glob_col - part_ranges[2 * part];\n                    int actual_col = halo_base + halo_indices[pos];\n                    col_indices[i] = -1 - actual_col;\n                    new_local_to_global[actual_col - num_rows] = glob_col;\n                    // when we renumber we also store mapping to use later in residual restriction since matrix R has other order of halo indices\n                    // store both direct and inverse renumbering for P & R matrices\n                    //halo_P_renumbering[col - num_rows] = actual_col;\n                    //halo_R_renumbering[actual_col - num_rows] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void flip_sign_col(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < 0)\n            {\n                col_indices[i] = -col_indices[i] - 1;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping_ring2(INDEX_TYPE *row_offsets, INDEX_TYPE num_rows, int64_t *global_col_indices, int64_t *part_ranges, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE *halo_offsets, INDEX_TYPE *neighbor_offsets, INDEX_TYPE *neighbor_nodes, int64_t *local_to_global, INDEX_TYPE *local_col_indices)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if ((col < base || col >= base + range) && local_col_indices[i] == -1)\n            {\n                // update only 2nd ring halo indices here, 1st ring is updated already\n                int part = 0;\n\n                while (part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int pos = col - part_ranges[2 * part];\n                    local_col_indices[i] = halo_offsets[part] + neighbor_nodes[neighbor_offsets[part] + pos];\n                    local_to_global[local_col_indices[i] - range] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void renumber_col_indices(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *halo_renum, INDEX_TYPE *halo_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global_map)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int local_index = halo_renum[halo_offsets[part] + col - part_ranges[2 * part]];\n                    local_to_global_map[local_index - range] = col_indices[i];\n                    col_indices[i] = local_index;\n                }\n            }\n            else\n            {\n                col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n__global__ void populate_B2L(INDEX_TYPE *indexing, INDEX_TYPE *output, INDEX_TYPE last, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        if (row == size - 1)\n        {\n            if (last) { output[indexing[row]] = row; }\n        }\n        else if (indexing[row] != indexing[row + 1])\n        {\n            output[indexing[row]] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void populate_bdy_list_and_l2h_map(INDEX_TYPE *l2h_map, INDEX_TYPE *halo_nodes, INDEX_TYPE *halo_indices, INDEX_TYPE *bdy_list, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int scanned_id = halo_indices[row];\n\n        if (scanned_id != halo_indices[row + 1])\n        {\n            bdy_list[scanned_id] = row;\n            l2h_map[scanned_id] = -halo_nodes[row] - 1;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n__global__ void populate_B2L_v2(INDEX_TYPE *indexing, INDEX_TYPE *output, INDEX_TYPE offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        if (indexing[row] != indexing[row + 1])\n        {\n            output[indexing[row] - offset] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n\ntemplate <int coop, class T>\n__global__ void export_matrix_elements(INDEX_TYPE *row_offsets, T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, INDEX_TYPE *pointers, T *output, INDEX_TYPE *col_indices, INDEX_TYPE *output2, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = pointers[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += coop)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += coop)\n        {\n            output2[dst_base + m] = col_indices[src_base + m];\n        }\n\n        idx += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void export_matrix_elements_global(INDEX_TYPE *row_offsets, T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, INDEX_TYPE *pointers, T *output, INDEX_TYPE *col_indices, int64_t *output2, INDEX_TYPE size, int64_t *local_to_global, INDEX_TYPE *q, INDEX_TYPE num_owned_pts, int64_t base_index)\n{\n    int idx = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n\n        if (q != NULL)\n        {\n            row = q[row];\n        }\n\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = pointers[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += coop)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += coop)\n        {\n            int col = col_indices[src_base + m];\n\n            if (col < num_owned_pts)\n            {\n                output2[dst_base + m] = (int64_t) col_indices[src_base + m] + base_index;\n            }\n            else\n            {\n                output2[dst_base + m] = local_to_global[col_indices[src_base + m] - num_owned_pts];\n            }\n        }\n\n        idx += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <class T>\n__global__ void export_matrix_diagonal(T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, T *output, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row;\n        INDEX_TYPE dst_base = idx;\n\n        for (int m = 0; m < bsize; m++)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void write_matrix_rowsize(INDEX_TYPE *maps, INDEX_TYPE *row_offsets, INDEX_TYPE size, INDEX_TYPE *output)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        output[idx] = row_offsets[row + 1] - row_offsets[row];\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void find_next_ring(INDEX_TYPE *maps, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE size, INDEX_TYPE base, INDEX_TYPE range, INDEX_TYPE *halo_rings)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n\n        for (int col = row_offsets[row]; col < row_offsets[row + 1]; col++)\n        {\n            int colIdx = col_indices[col];\n\n            if (colIdx >= base && colIdx < base + range)\n            {\n                halo_rings[colIdx - base] = 1;\n            }\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void remove_previous_rings(INDEX_TYPE *map, INDEX_TYPE *halo_rings, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = map[idx];\n        halo_rings[row] = 0;\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void renumber_halo_lists(INDEX_TYPE *halo_list, INDEX_TYPE *halo_renum, int64_t base, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        halo_list[row] = halo_renum[halo_list[row] - base];\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void write_ring_rows(INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = indices[row];\n\n        if (idx == 1 && row == 0)\n        {\n            maps[idx - 1] = row;\n        }\n        else if (row > 0 && idx != indices[row - 1])\n        {\n            maps[idx - 1] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <class T_Config>\nvoid DistributedArrangerBase<T_Config>::set_part_offsets(const INDEX_TYPE num_part, const VecInt_t *part_offsets_h)\n{\n    part_offsets.resize(num_part + 1);\n    amgx::thrust::copy(&part_offsets_h[0], &part_offsets_h[num_part + 1], part_offsets.begin());\n    cudaCheckError();\n    this->num_part =  num_part;\n}\n\n// TODO Make distributed reader use 64bit ints for offsets\ntemplate <class T_Config>\nvoid DistributedArrangerBase<T_Config>::set_part_offsets(const INDEX_TYPE num_part, const int64_t *part_offsets_h)\n{\n    part_offsets.resize(num_part + 1);\n    amgx::thrust::copy(&part_offsets_h[0], &part_offsets_h[num_part + 1], part_offsets.begin());\n    cudaCheckError();\n    this->num_part =  num_part;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t &base_index, INDEX_TYPE &index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    I64Vector_h halo_ranges_h;\n    // initialize base_index, index_range, neighbors, halo_ranges_h\n    this->create_neighbors(A, my_id, base_index, index_range, neighbors, halo_ranges_h);\n    this->create_B2L_from_neighbors(A, my_id, rings, base_index, index_range, neighbors,\n                                    halo_ranges_h, halo_ranges, B2L_maps, L2H_maps, B2L_rings, comms, halo_rows, halo_btl);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_maps(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    int num_neighbors = neighbors.size();\n    this->halo_coloring = (*comms)->halo_coloring;\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n    // initialize halo_btl\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    // initialize halo_rows\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    //this->create_halo_matrices(A, 1, base_index, index_range, my_id, neighbors, B2L_maps, B2L_rings, &(*halo_rows),  &(*halo_btl));\n    if (this->halo_coloring != LAST)\n    {\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_maps(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        Vector<ivec_value_type_h> &neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    this->halo_coloring = (*comms)->halo_coloring;\n    int num_neighbors = neighbors.size();\n    int64_t base_index = 0;\n    int index_range = A.get_num_rows();\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        halo_lists[i] = &L2H_maps[i];\n    }\n\n    /*\n     EXAMPLE\n     Say we want 2 rings of halo. The output of these function for the example partition 1 that at this point looks like:\n    A.num_rows = 4; A.num_nz = 20\n    A.row_offsets = [0 4 11 15 20]\n    A.col_indices = [4 0 1 2\n                   4 5 0 1 2 3 7\n                   0 1 2 3\n                   1 2 3 6 7]\n    num_neighbors=2; neighbors = [0 2]\n    B2L_rings[[0 2][0 2]] B2L_maps [[0 1][1 3]] (Converted from the following input: btl_sizes = [0 2 4] btl_maps = [0 1| 1 3])\n    L2H_maps (and halo_lists) [[4 5][6 7]]\n    halo_rows = NULL, halo_btl=NULL\n    (global indices mapping to local indices: 4-0 5-1 6-2 7-3 2-4 3-5 9-6 10-7)\n     */\n    // initialize ring>1 B2L_maps and B2L_rings\n    this->create_rings(A, rings, num_neighbors, B2L_maps, B2L_rings);\n    /*\n     EXAMPLE\n     having mapped out two rings of halo (already had one), B2L_maps and B2L_rings look like:\n     B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n     */\n    // initialize halo_btl, creating DistributedManager objects for each halo row matrix to be sent to each neighbor, storing B2L and L2H maps\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    /*\n     EXAMPLE\n     We created to two DistributedManager objects for the two neighbors. if halo_coloring != LAST, then the relevant fields are:\n     halo_btl[0] (for neighbor ID 0)\n     global_id = 1; base_index=0; index_range=6 B2L_rings[0] = [0 2 4] B2L_maps[0] = [0 1| 2 3] L2H_maps = [4 5]\n     halo_btl[1] (for neighbor ID 2)\n     global_id = 1; base_index=0; index_range=8 B2L_rings[0] = [0 2 4] B2L_maps[0] = [1 3| 0 2] L2H_maps = [6 7]\n     */\n    // Extract halo rows, ring by ring\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    /*\n     EXAMPLE\n     Extracted two rings of rows for the two neighbors, the two halo row matrices look like:\n     halo_rows[0] (for neighbor ID 0)\n     halo_rows[0].row_offsets = [0 4 11 15 20]\n     halo_rows[0].col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n\n     halo_rows[1] (for neighbor ID 2)\n     halo_rows[1].row_offsets = [0 7 12 16 20]\n     halo_rows[1].col_indices = [4 5 0 1 2 3 7\n                    1 2 3 6 7\n                    4 0 1 2\n                    0 1 2 3]\n      Note, that the ordering of rows corresponds to the list of indices in B2L_maps[i] (and halo_btl[i].B2L_maps[0])\n     */\n    //this->create_halo_matrices(A, 1, base_index, index_range, my_id, neighbors, B2L_maps, B2L_rings, &(*halo_rows),  &(*halo_btl));\n    if (this->halo_coloring != LAST)   //For multiple rings, we still need 0-num_rings-1 rows, just not the last one.\n    {\n        //Set up dummy matrix and dummy manager, and exchange the halo rows\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        I64Vector_d tmp_halo_ranges;\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, tmp_halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n\n    /*\n     EXAMPLE\n     The partitions exchange their halo_rows and halo_btl fields, partition 1 ends up with:\n     halo_btl[0] (received from neighbor ID 0)\n     global_id = 0; base_index=0; index_range=6; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [2 3| 0 1] L2H_maps = [4 5]\n     halo_rows[0].row_offsets = [0 5 13 17 21]\n     halo_rows[0].col_indices = [1 2 3 4 5\n                      0 1 2 3 4 5 6 7\n                      0 1 3 6\n                      0 1 2 3]\n\n     halo_btl[1] (received from neighbor ID 2)\n     global_id = 2; base_index=0; index_range=8; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [1 2| 0 3] L2H_maps = [6 7]\n     halo_rows[1].row_offsets = [0 4 11 16 20]\n     halo_rows[1].col_indices = [7 1 2 3\n                      5 6 7 0 1 2 3\n                      4 5 0 2 3\n                      0 1 2 3]\n     */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_to_local(Matrix_d &A,\n        std::vector<IVector> &boundary_lists,\n        std::vector<IVector *> &halo_lists,\n        INDEX_TYPE my_id,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        IVector &halo_nodes)\n{\n    //IVector &halo_nodes = *halo_nodes_p;\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n    }\n\n    //IVector halo_nodes(total_rows_of_neighbors);\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    int last_halo = halo_nodes[halo_nodes.size() - 1];\n    //create renumbering of halo indices (contiguously neighbor - by - neighbor, indexing starting with the number of owned rows\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin(), size);\n    cudaCheckError();\n    A.manager->local_to_global_map.resize(halo_nodes[halo_nodes.size() - 1] - size + last_halo);\n    renumber_col_indices<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), base_index, index_range, num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n\n    for (int i =  0; i < num_neighbors; i ++)\n    {\n        int size = halo_lists[i]->size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        renumber_halo_lists <<< num_blocks, 128>>>(halo_lists[i]->raw(), halo_nodes.raw() + halo_offsets[i], halo_ranges_h[2 * i], size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_to_local(Matrix_d &A,\n        std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists,\n        INDEX_TYPE my_id, I64Vector_h &halo_ranges_h, IVector &halo_nodes)\n{\n    //IVector &halo_nodes = *halo_nodes_p;\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n    }\n\n    //IVector halo_nodes(total_rows_of_neighbors+1);\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    int last_halo = (halo_nodes.size() > 0) ? halo_nodes[halo_nodes.size() - 1] : 0;\n    //create renumbering of halo indices (contiguously neighbor - by - neighbor, indexing starting with the number of owned rows\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin(), size);\n    cudaCheckError();\n\n    if (halo_nodes.size() > 0)\n    {\n        A.manager->local_to_global_map.resize(halo_nodes[halo_nodes.size() - 1] - size + last_halo);\n    }\n\n    renumber_col_indices<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n\n    for (int i =  0; i < num_neighbors; i ++)\n    {\n        int size = halo_lists[i]->size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        renumber_halo_lists <<< num_blocks, 128>>>(halo_lists[i]->raw(), halo_nodes.raw() + halo_offsets[i], halo_ranges_h[2 * i], size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prepare_local(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &L2H_maps,\n        std::vector<IVector> &boundary_lists,\n        std::vector<IVector *> &halo_lists,\n        DistributedComms<TConfig_d> **comms)\n{\n    int num_neighbors = neighbors.size();\n    (*comms)->set_neighbors(num_neighbors);\n    IVector halo_nodes;\n    // copy halo_ranges_h to device and populate boundary_lists\n    this->create_boundary_lists(A, base_index, index_range, halo_ranges_h, halo_ranges, boundary_lists, halo_nodes);\n    L2H_maps.resize(num_neighbors);\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        L2H_maps[i].resize(boundary_lists[i].size());\n        thrust_wrapper::transform<AMGX_device>(boundary_lists[i].begin(), boundary_lists[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE > (halo_ranges_h[2 * i]), L2H_maps[i].begin(), amgx::thrust::plus<INDEX_TYPE > ());\n        halo_lists[i] = &L2H_maps[i];\n    }\n\n    cudaCheckError();\n    // setting up tmp manager in order to use Comm API\n    Matrix_d tmpA(0, 0, 0, A.getProps());\n    tmpA.set_block_dimx(A.get_block_dimx());\n    tmpA.set_block_dimy(A.get_block_dimy());\n    tmpA.setResources(A.getResources());\n    std::vector<IVector > B2L_maps;\n    std::vector<std::vector<VecInt_t> > B2L_rings;\n    DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n    tmpA.setManager(tmp_manager);\n    tmpA.manager->getComms()->exchange_vectors(boundary_lists, tmpA, 0);\n    this->renumber_to_local(A, boundary_lists, halo_lists, my_id, base_index, index_range, neighbors, halo_ranges_h, halo_ranges, halo_nodes);\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_neighbors(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    this->halo_coloring = (*comms)->halo_coloring;\n    std::vector<IVector> boundary_lists;\n    std::vector<IVector *> halo_lists;\n    this->prepare_local(A, my_id, rings, base_index, index_range, neighbors, halo_ranges_h, halo_ranges, L2H_maps, boundary_lists, halo_lists, comms);\n    int num_neighbors = neighbors.size();\n    // initialize BTL_maps and BTl_rings\n    this->create_maps(A, rings, num_neighbors, B2L_maps, B2L_rings, boundary_lists);\n    // initialize halo_btl\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    // initialize halo_rows\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    if (this->halo_coloring != LAST)\n    {\n        // setting up tmp manager in order to use Comm API\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors(Matrix_d &A, INDEX_TYPE my_id,\n        int64_t &base_index, INDEX_TYPE &index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h)\n{\n    //if (this->num_part>32) FatalError(\"Matrix slice Level 1 module supports < 32 partitions\", AMGX_ERR_NOT_IMPLEMENTED);\n    if (this->num_part == 0 || this->part_offsets.size() == 0)\n    {\n        FatalError(\"Partitioning scheme is not set\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    IVector part_sizes(this->num_part, 0);\n    IVector inv_neighbors(this->num_part, 0);\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    base_index = this->part_offsets[my_id];\n    index_range = this->part_offsets[my_id + 1] - this->part_offsets[my_id];\n    //\n    // Step 1: flag neighbors that I have an edge to\n    //\n    //go through all the column indices and compare it to index ranges of nodes\n    calc_num_neighbors<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), this->part_offsets.raw(), part_sizes.raw(), this->num_part, my_id, size);\n    cudaCheckError();\n    std::vector<int> neighbor_flags(this->num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    //total number of neighbors\n    int num_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    neighbors.resize(num_neighbors);\n    halo_ranges_h.resize(num_neighbors * 2);\n    //initialize manager\n    int active_part = 0;\n\n    for (int i = 0; i < this->num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = this->part_offsets[i];\n            halo_ranges_h[2 * active_part + 1] = this->part_offsets[i + 1];\n            active_part++;\n        }\n    }\n}\n\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors(Matrix_d &A, INDEX_TYPE my_id,\n        I64Vector_h &halo_ranges_h)\n{\n    if (this->num_part == 0 || this->part_offsets.size() == 0)\n    {\n        FatalError(\"Partitioning scheme is not set\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    IVector part_sizes(this->num_part, 0);\n    IVector inv_neighbors(this->num_part, 0);\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    A.manager->set_base_index(this->part_offsets[my_id]);\n    A.manager->set_index_range(this->part_offsets[my_id + 1] - this->part_offsets[my_id]);\n    //\n    // Step 1: flag neighbors that I have an edge to\n    //\n    //go through all the column indices and compare it to index ranges of nodes\n    calc_num_neighbors<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), this->part_offsets.raw(), part_sizes.raw(), this->num_part, my_id, size);\n    cudaCheckError();\n    std::vector<int> neighbor_flags(this->num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    //total number of neighbors\n    int num_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    A.manager->neighbors.resize(num_neighbors);\n    halo_ranges_h.resize(num_neighbors * 2);\n    //initialize manager\n    int active_part = 0;\n\n    for (int i = 0; i < this->num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            A.manager->neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = this->part_offsets[i];\n            halo_ranges_h[2 * active_part + 1] = this->part_offsets[i + 1];\n            active_part++;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::update_neighbors_list(\n    Matrix_d &A, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, I64Vector_h &part_offsets_h, I64Vector &part_offsets, std::vector<IVector> &halo_rows_row_offsets, std::vector<I64Vector> &halo_rows_col_indices)\n{\n    int num_partitions = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    int num_neighbors = neighbors.size();     // current number of neighbors\n    int total_halo_rows = 0;\n    int total_halo_nnz = 0;\n    IVector neighbor_flags(num_partitions, 0);\n\n    for (int i = 0; i < halo_rows_row_offsets.size(); i++)\n    {\n        int num_halo_rows = halo_rows_row_offsets[i].size() - 1;\n\n        if (num_halo_rows > 0)\n        {\n            total_halo_rows += num_halo_rows;\n            total_halo_nnz += halo_rows_row_offsets[i][num_halo_rows];\n            int num_blocks = std::min(4096, (num_halo_rows + 127) / 128);\n            calc_num_neighbors_v2_global<16> <<< num_blocks, 128>>>(halo_rows_row_offsets[i].raw(), halo_rows_col_indices[i].raw(),\n                    part_offsets.raw(), neighbor_flags.raw(), num_partitions, my_id, num_halo_rows);\n                    cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    IVector_h neighbor_flags_h = neighbor_flags;\n\n    // unset 1-ring neighbors & myself\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neighbor_flags_h[neighbors[i]] = 0;\n    }\n\n    neighbor_flags_h[my_id] = 0;\n    // this will update neighbor list and halo ranges, note that we don't change 1-ring neighbors order\n    append_neighbors(A, neighbors, halo_ranges_h, halo_ranges, neighbor_flags_h, part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::append_neighbors(Matrix_d &A, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, IVector_h &neighbor_flags, I64Vector_h &part_offsets_h)\n{\n    // This function creates arrays neighbors, halo_ranges_h and halo_ranges\n    // base on neighbor_flags\n    // Here do an MPI_allgather to figure out which partitions need data from me\n    // This is required for non-symmetric matrices\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    // pack 0/1 array into array of integers (size/32)\n    int packed_size = (num_part + 32 - 1) / 32;\n    IVector_h packed_nf(packed_size, 0);\n\n    for (int i = 0; i < num_part; i++)\n    {\n        int packed_pos = i / 32;\n        int bit_pos = i % 32;\n        packed_nf[packed_pos] += (neighbor_flags[i] << bit_pos);\n    }\n\n    // exchange packed neighbor flags\n    IVector_h gathered_packed_nf;\n    A.manager->getComms()->all_gather_v(packed_nf, gathered_packed_nf, num_part);\n    // assign neighbors that have edges to me\n    int my_id_pos = my_id / 32;\n    int my_id_bit = my_id % 32;\n\n    for (int i = 0; i < num_part; i++)\n        if (gathered_packed_nf[i * packed_size + my_id_pos] & (1 << my_id_bit)) // check my bit\n        {\n            neighbor_flags[i] = 1;\n        }\n\n    // compute total number of new neighbors\n    int new_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    // save old size\n    int old_neighbors = neighbors.size();\n    neighbors.resize(old_neighbors + new_neighbors);\n    halo_ranges_h.resize(old_neighbors * 2 + new_neighbors * 2);\n    // initialize manager->neighbors and manager->halo_ranges_h for the new nodes\n    int active_part = old_neighbors;\n    this->num_part = num_part;\n\n    for (int i = 0; i < num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = part_offsets_h[i];\n            halo_ranges_h[2 * active_part + 1] = part_offsets_h[i + 1];\n            active_part++;\n        }\n    }\n\n    halo_ranges.resize(old_neighbors * 2 + new_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin() + old_neighbors * 2, halo_ranges_h.end(), halo_ranges.begin() + old_neighbors * 2);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors_v2(Matrix_d &A)\n{\n    // This function counts the number of neighbors, identifies the id of each neighbor,\n    // And creates array halo_ranges, which is the range of rows for each neighbor\n    // Outputs: A.manager->neighbors\n    //          A.manager->halo_ranges_h\n    //          A.manager->halo_ranges\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    A.manager->set_base_index(A.manager->part_offsets_h[my_id]);\n    A.manager->set_index_range(A.manager->part_offsets_h[my_id + 1] - A.manager->part_offsets_h[my_id]);\n    IVector inv_neighbors(num_part, 0);\n    IVector part_sizes(num_part, 0);\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        A.manager->neighbors.resize(0);\n    }\n\n    // TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    // -----------------------------------------------\n    // Step 1: flag neighbors that I have an edge to\n    // -----------------------------------------------\n    //go through all the column indices and check if they are halo vertices\n    if (num_blocks != 0)\n    {\n        calc_num_neighbors_v2_local<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->part_offsets.raw(), part_sizes.raw(), A.manager->local_to_global_map.raw(), num_part, my_id, size);\n        cudaCheckError();\n    }\n\n    // D2H copy\n    IVector_h neighbor_flags(num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    cudaCheckError();\n    append_neighbors(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, neighbor_flags, A.manager->part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors_v2_global(Matrix_d &A, I64Vector_d &A_col_indices_global)\n{\n    // This function counts the number of neighbors, identifies the id of each neighbor,\n    // And creates array halo_ranges, which is the range of rows for each neighbor\n    // Outputs: A.manager->neighbors\n    //          A.manager->halo_ranges_h\n    //          A.manager->halo_ranges\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    IVector part_sizes(num_part, 0);\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        A.manager->neighbors.resize(0);\n    }\n\n    // TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    // -----------------------------------------------\n    // Step 1: flag neighbors that I have an edge to\n    // -----------------------------------------------\n    //go through all the column indices and check if they are halo vertices\n    if (num_blocks != 0)\n    {\n        calc_num_neighbors_v2_global<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A_col_indices_global.raw(), A.manager->part_offsets.raw(), part_sizes.raw(), num_part, my_id, size);\n        cudaCheckError();\n    }\n\n    // D2H copy\n    IVector_h neighbor_flags(num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    cudaCheckError();\n    append_neighbors(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, neighbor_flags, A.manager->part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_one_ring(Matrix_d &A)\n{\n    std::vector<IVector> boundary_lists;\n    // create my boundary lists, renumber halos, create L2H maps\n    create_boundary_lists_v2(A, boundary_lists);\n    // compute halo offsets\n    int neighbors = A.manager->num_neighbors();\n    A.manager->halo_offsets.resize(neighbors + 1, 0);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->halo_offsets[i] = boundary_lists[i].size();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(A.manager->halo_offsets.begin(), A.manager->halo_offsets.end(), A.manager->halo_offsets.begin(), A.get_num_rows());\n    cudaCheckError();\n    // receive neighbors boundary lists for b2l maps\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    // copy boundary lists to B2L maps\n    // release internal boundary lists array\n    A.manager->B2L_maps.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_maps[i] = boundary_lists[i];\n    }\n\n    A.manager->B2L_rings.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_rings[i].resize(2);\n        A.manager->B2L_rings[i][0] = 0;\n        A.manager->B2L_rings[i][1] = A.manager->B2L_maps[i].size();\n    }\n\n    this->initialize_B2L_maps_offsets(A, 1);\n    A.manager->A = &A;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createRowsLists(Matrix_d &A, bool is_matrix_P)\n{\n    int A_num_rows, offset;\n    A.getOffsetAndSizeForView(OWNED, &offset, &A_num_rows);\n    IVector boundary_flags(A_num_rows + 1, 0);\n    IVector interior_flags(A_num_rows + 1, 0);\n\n    int num_nz_interior;\n    if (!is_matrix_P)\n    {\n        for (int i = 0; i < A.manager->neighbors.size(); i++)\n        {\n            int size = A.manager->B2L_rings[i][1];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n\n            if ( size > 0)\n            {\n                flag_boundary_nodes <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), boundary_flags.raw(), size);\n                cudaCheckError();\n            }\n        }\n    }\n    else\n    {\n        const int cta_size = 256;\n        const int nWarps = cta_size / 32;\n        int grid_size = std::min( 4096, (int) (A_num_rows + nWarps - 1) / nWarps);\n\n        if (A_num_rows > 0)\n        {\n            IVector nnz_interior(1, 0);\n            flag_boundary_nodes_P<index_type, cta_size> <<< grid_size, cta_size>>>(A.row_offsets.raw(), A.col_indices.raw(), boundary_flags.raw(), A.manager->halo_offsets[0], A_num_rows, nnz_interior.raw());\n            cudaCheckError();\n            cudaMemcpy(&num_nz_interior, nnz_interior.raw(), sizeof(int), cudaMemcpyDefault);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    int size = A_num_rows;\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    if (size > 0)\n    {\n        flag_interior_nodes <<< num_blocks, 128>>>(boundary_flags.raw(), interior_flags.raw(), size);\n        cudaCheckError();\n    }\n\n    if (boundary_flags.size() > 1)\n    {\n        thrust_wrapper::exclusive_scan<AMGX_device>(boundary_flags.begin(), boundary_flags.end(), boundary_flags.begin());\n        cudaCheckError();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(interior_flags.begin(), interior_flags.end(), interior_flags.begin());\n    cudaCheckError();\n    int num_interior_rows = interior_flags[size];\n    int num_boundary_rows = boundary_flags[size];\n    A.manager->interior_rows_list.resize(num_interior_rows);\n    A.manager->boundary_rows_list.resize(num_boundary_rows);\n\n    // Create the list of interior and boundary nodes, using the interior_flag and boundary_flag\n    if (size > 0)\n    {\n        fill_rows_lists <<< num_blocks, 128>>>(A.manager->interior_rows_list.raw(), A.manager->boundary_rows_list.raw(), interior_flags.raw(), boundary_flags.raw(), size);\n        cudaCheckError();\n    }\n\n    // Manually set the view sizes for P\n    if(is_matrix_P)\n    {\n        int num_rows_owned = num_interior_rows + num_boundary_rows;\n        int num_nz_owned = A.row_offsets[num_rows_owned];\n        A.manager->setViewSizes(num_interior_rows, num_nz_interior, num_rows_owned, num_nz_owned, num_rows_owned, num_nz_owned, A.get_num_rows(), A.get_num_nz());\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists_v3(Matrix_d &A)\n{\n    // -----------------------------------------------\n    // Flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    // -----------------------------------------------\n    int A_num_rows = A.get_num_rows();\n\n    if (A_num_rows == 0)\n    {\n        return;\n    }\n\n    int num_neighbors = A.manager->neighbors.size();\n    // compute halo offsets\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows_neighbor = A.manager->halo_ranges_h[2 * i + 1] - A.manager->halo_ranges_h[2 * i];\n        total_rows_of_neighbors += num_rows_neighbor;\n        halo_offsets[i + 1] = halo_offsets[i] + num_rows_neighbor;\n        max_neighbor_size = max_neighbor_size > num_rows_neighbor ? max_neighbor_size : num_rows_neighbor;;\n    }\n\n    // copy offsets to device\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    // flag neighbors nodes that I need\n    IVector halo_nodes(total_rows_of_neighbors + 1);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    int size = A.manager->local_to_global_map.size();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    if (size > 0)\n    {\n        flag_halo_nodes_from_local_to_global_map <<< num_blocks, 128>>>(A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n        cudaCheckError();\n    }\n\n    IVector halo_indices(max_neighbor_size + 1);\n    A.manager->L2H_maps.resize(num_neighbors);\n    A.manager->B2L_maps.resize(num_neighbors);\n    is_less_than_zero pred;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // compute halo indices map [<neighbor inner node>] = 0-based halo number\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        amgx::thrust::replace_copy_if(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin(), pred, 1);\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_indices.begin(), halo_indices.begin() + size + 1, halo_indices.begin());\n        cudaCheckError();\n        int num_halo = halo_indices[size];\n        A.manager->L2H_maps[i].resize(num_halo);\n        A.manager->B2L_maps[i].resize(num_halo);\n        // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n        // For each node in B2L_map, need to find corresponding local id and store in L2H_map\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        populate_bdy_list_and_l2h_map <<< num_blocks, 128>>>(A.manager->L2H_maps[i].raw(),  halo_nodes.raw() + halo_offsets[i], halo_indices.raw(), A.manager->B2L_maps[i].raw(), size);\n        cudaCheckError();\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_B2L_maps_offsets(Matrix_d &A, int num_rings)\n{\n    // Compute the total number of bdy rows in each ring\n    std::vector<IVector_h> B2L_maps_offsets_h(num_rings);\n    A.manager->B2L_rings_sizes.resize(num_rings);\n    int num_neighbors = A.manager->neighbors.size();\n\n    for (int k = 0; k < num_rings; k++)\n    {\n        B2L_maps_offsets_h[k].resize(num_neighbors + 1);;\n        B2L_maps_offsets_h[k][0] = 0;\n\n        for (int j = 0; j < num_neighbors; j++)\n        {\n            B2L_maps_offsets_h[k][j + 1] = B2L_maps_offsets_h[k][j] + A.manager->B2L_rings[j][k + 1];\n        }\n\n        A.manager->B2L_rings_sizes[k] = B2L_maps_offsets_h[k][num_neighbors];\n    }\n\n    // Copy maps_offsets to device\n    A.manager->B2L_maps_offsets.resize(num_rings);\n\n    for (int i = 0; i < num_rings; i++)\n    {\n        A.manager->B2L_maps_offsets[i] = B2L_maps_offsets_h[i];\n    }\n\n    // Store the B2L_maps ptrs on the device\n    std::vector<int *> B2L_maps_ptrs_h(num_neighbors);\n\n    for (int j = 0; j < num_neighbors; j++)\n    {\n        B2L_maps_ptrs_h[j] = A.manager->B2L_maps[j].raw();\n    }\n\n    A.manager->B2L_maps_ptrs = B2L_maps_ptrs_h;\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_one_ring_halo_rows(Matrix_d &A)\n{\n    // what it does:\n    //   appends halo rows to matrix A\n    //   creates new B2L_rings, B2L_maps and L2H_maps\n    // input:\n    //   matrix A and 1-ring B2L_maps\n    int num_partitions = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    int num_neighbors = A.manager->B2L_maps.size();\n    int num_ring1_neighbors = num_neighbors;\n    int num_ring1_indices = A.manager->local_to_global_map.size();\n    std::vector<IVector> halo_row_offsets(num_neighbors);\n    std::vector<I64Vector> halo_global_indices(num_neighbors);\n    std::vector<IVector> halo_local_indices(num_neighbors);\n    std::vector<MVector> halo_values(num_neighbors);\n    // step 1: setup halo rows with global indices\n    create_halo_rows_global_indices(A, halo_row_offsets, halo_global_indices, halo_values);\n    // step 2: exchange halo rows\n    std::vector<I64Vector> dummy_halo_ids(0);\n    A.manager->getComms()->exchange_matrix_halo(halo_row_offsets, halo_global_indices, halo_values, dummy_halo_ids, A.manager->neighbors, A.manager->global_id());\n    // step 3: append the list  f neighbors with the new ring-2 neighbors\n    update_neighbors_list(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, A.manager->part_offsets_h, A.manager->part_offsets, halo_row_offsets, halo_global_indices);\n    num_neighbors = A.manager->neighbors.size();\n    // step 4: mark neighbor nodes, create local halo indices for the 2nd ring\n    // this function also creates boundary lists for the 2nd ring if boundary lists array size > 0\n    std::vector<IVector> boundary_lists(num_neighbors);\n    compute_local_halo_indices(A.row_offsets, A.col_indices, halo_row_offsets, halo_global_indices, halo_local_indices, A.manager->local_to_global_map, boundary_lists,  A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, A.manager->halo_offsets, A.manager->base_index(), A.manager->index_range(), A.get_num_rows(), 1);\n    // update renumbering arrays (set identity for 2nd ring)\n    //A.manager->halo_P_renumbering.resize(A.manager->local_to_global_map.size());\n    //A.manager->halo_R_renumbering.resize(A.manager->local_to_global_map.size());\n    //thrust_wrapper::sequence<AMGX_device>(A.manager->halo_P_renumbering.begin() + num_ring1_indices, A.manager->halo_P_renumbering.end(), num_ring1_indices + A.get_num_rows());\n    //thrust_wrapper::sequence<AMGX_device>(A.manager->halo_R_renumbering.begin() + num_ring1_indices, A.manager->halo_R_renumbering.end(), num_ring1_indices + A.get_num_rows());\n    // step 5: update L2H maps = identity\n    A.manager->getComms()->set_neighbors(A.manager->neighbors.size());\n    // step 6: receive neighbors boundary lists for 2-ring b2l maps\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    // step 7: update B2L rings & B2L maps\n    int rings = 2;\n    A.manager->B2L_rings.resize(num_neighbors);\n    A.manager->B2L_maps.resize(num_neighbors);\n    // modify 1-ring where necessary\n    int ring = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // set size to 0 for 2-ring neighbors only\n        if (i >= num_ring1_neighbors)\n        {\n            A.manager->B2L_maps[i].resize(0);\n        }\n\n        A.manager->B2L_rings[i].resize(rings + 1);\n        A.manager->B2L_rings[i][0] = 0;\n        A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_maps[i].size();\n    }\n\n    // fill up 2-ring maps\n    ring = 1;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // append 2nd ring\n        int ring1_size = A.manager->B2L_maps[i].size();\n        A.manager->B2L_maps[i].resize(ring1_size + boundary_lists[i].size());\n        amgx::thrust::copy(boundary_lists[i].begin(), boundary_lists[i].end(), A.manager->B2L_maps[i].begin() + ring1_size);\n        A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_maps[i].size();\n    }\n\n    cudaCheckError();\n    this->initialize_B2L_maps_offsets(A, 2);\n    // step 8: append halo_rows to matrix A\n    // compute new # of rows & nnz\n    this->append_halo_rows(A, halo_row_offsets, halo_local_indices, halo_values);\n    // initialize the manager\n    A.manager->set_initialized(A.row_offsets);\n    // Compute the diagonal\n    // TODO: Should only compute diagonal of 1-ring halo rows\n    ViewType oldView = A.currentView();\n    A.setView(FULL);\n    A.set_allow_recompute_diag(true);\n    A.computeDiagonal();\n    A.setView(oldView);\n    // the following steps are necessary only for latency hiding/renumbering, i.e. to use reorder_matrix()\n#if 0\n    // step 6: for 2 rings: setup halo_btl\n    create_halo_btl_multiple_rings(A, 2);\n#endif\n}\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists(Matrix_d &A,\n        int64_t base_index, INDEX_TYPE index_range,\n        I64Vector_h &halo_ranges_h, I64Vector_d &halo_ranges,\n        std::vector<IVector> &boundary_lists, IVector &halo_nodes)\n{\n    //\n    // Step 2: flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    //\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    halo_ranges.resize(num_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin(), halo_ranges_h.end(), halo_ranges.begin());\n    cudaCheckError();\n    boundary_lists.resize(num_neighbors);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);;\n    }\n\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    halo_nodes.resize(total_rows_of_neighbors);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), base_index, index_range, num_neighbors, size);\n    cudaCheckError();\n    //\n    // Step 3: Write the 1-ring halo indices for each neighbor\n    //\n    IVector halo_indices(max_neighbor_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        int num_halo = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last;\n        //A.manager.B2L_maps[i].resize(num_halo);\n        boundary_lists[i].resize(num_halo);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        //int num_blocks=min(4096,(size+127)/128);\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(),/*A.manager.B2L_maps[i].raw(),*/ last, size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists(Matrix_d &A,\n        I64Vector_h &halo_ranges_h,\n        std::vector<IVector> &boundary_lists, IVector &halo_nodes)\n{\n    //\n    // Step 2: flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    //\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    A.manager->halo_ranges.resize(num_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin(), halo_ranges_h.end(), A.manager->halo_ranges.begin());\n    cudaCheckError();\n    boundary_lists.resize(num_neighbors);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);;\n    }\n\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    halo_nodes.resize(total_rows_of_neighbors);;\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size);\n    cudaCheckError();\n    //\n    // Step 3: Write the 1-ring halo indices for each neighbor\n    //\n    IVector halo_indices(max_neighbor_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        int num_halo = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last;\n        //A.manager.B2L_maps[i].resize(num_halo);\n        boundary_lists[i].resize(num_halo);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        //int num_blocks=min(4096,(size+127)/128);\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(),/*A.manager.B2L_maps[i].raw(),*/ last, size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists_v2(Matrix_d &A, std::vector<IVector> &boundary_lists)\n{\n    // -----------------------------------------------\n    // Flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    // -----------------------------------------------\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        return;\n    }\n\n    //TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = A.manager->neighbors.size();\n    boundary_lists.resize(num_neighbors);\n    // compute halo offsets\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows_neighbor = A.manager->halo_ranges_h[2 * i + 1] - A.manager->halo_ranges_h[2 * i];\n        total_rows_of_neighbors += num_rows_neighbor;\n        halo_offsets[i + 1] = halo_offsets[i] + num_rows_neighbor;\n        max_neighbor_size = max_neighbor_size > num_rows_neighbor ? max_neighbor_size : num_rows_neighbor;;\n    }\n\n    // copy offsets to device\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    // flag neighbors nodes that I need\n    IVector halo_nodes(total_rows_of_neighbors);\n    amgx::thrust::fill(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes_local<16, 1> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n    // store renumbering so later can use on P\n    //A.manager->halo_P_renumbering.resize(A.manager->local_to_global_map.size());\n    //A.manager->halo_R_renumbering.resize(A.manager->local_to_global_map.size());\n    // we need to update local toglobal maps as well because we will be renumbering halo nodes\n    I64Vector new_local_to_global_map(A.manager->local_to_global_map.size(), -1);\n    // compute new local halo indices for each neighbor node\n    // renumbering: compute new column indices sorted by global index\n    IVector halo_indices(max_neighbor_size);\n    std::vector<int> num_halo(num_neighbors);\n    std::vector<int> last(num_neighbors);\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n    A.manager->L2H_maps.resize(num_neighbors);\n    int halo_base = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // compute halo indices map [<neighbor inner node>] = 0-based halo number\n        last[i] = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        num_halo[i] = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last[i];\n        // renumber my columns for this particular neighbor\n        calc_new_halo_mapping<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw() + (int64_t) (2 * i), 1, size, A.manager->local_to_global_map.raw(), new_local_to_global_map.raw(), halo_indices.raw(), halo_base, A.manager->global_id());\n        cudaCheckError();\n        //calc_new_halo_mapping_v2<16><<<num_blocks, 128>>>( A.manager->halo_ranges.raw() + (int64_t) (2*i), 1, size, A.manager->local_to_global_map.raw(), new_local_to_global_map.raw(), halo_indices.raw(), halo_base, A.manager->local_to_global_map.size(),A.manager->global_id());\n        cudaCheckError();\n        // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n        boundary_lists[i].resize(num_halo[i]);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(), last[i], size);\n        cudaCheckError();\n        // identity L2H\n        A.manager->L2H_maps[i].resize(num_halo[i]);\n        thrust_wrapper::sequence<AMGX_device>(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), halo_base);\n        halo_lists[i] = &A.manager->L2H_maps[i];\n        // continous numbering for halo indices\n        halo_base += num_halo[i];\n    }\n\n    cudaCheckError();\n    // Now change the negative col to positive\n    flip_sign_col<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), size);\n    cudaCheckError();\n    // copy new local to global mapping\n    amgx::thrust::copy(new_local_to_global_map.begin(), new_local_to_global_map.end(), A.manager->local_to_global_map.begin());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_local_halo_indices( IVector &A_row_offsets, IVector &A_col_indices, std::vector<IVector> &halo_row_offsets, std::vector<I64Vector> &halo_global_indices,\n        std::vector<IVector> &halo_local_indices, I64Vector &local_to_global, std::vector<IVector> &boundary_lists, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, IVector_h &halo_offsets, int64_t base_index, int64_t index_range, int A_num_rows, int current_num_rings)\n{\n    // This function checks the halo_col_indices received from the neighbors, and identifies\n    // new halo_indices and  updates halo_offsets, local_to_global_map accordingly\n    // input: halo row offsets,\n    //        halo global column indices\n    //        current local to global map\n    //        neighbors (new discovered neighbors should already be included)\n    //        halo_ranges for the neighbors\n    //\n    // output: halo offsets,\n    //         halo local column indices,\n    //         updated local to global map (in place)\n    int size = A_num_rows;\n    //TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = neighbors.size();\n    // compute neighbor offsets & total number of neighbor rows\n    int total_rows_of_neighbors = 0;\n    std::vector<int> neighbor_offsets_h(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        neighbor_offsets_h[i + 1] = neighbor_offsets_h[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);\n    }\n\n    // copy offsets to device\n    IVector neighbor_offsets(num_neighbors + 1);\n    amgx::thrust::copy(neighbor_offsets_h.begin(), neighbor_offsets_h.end(), neighbor_offsets.begin());\n    // store flags for all neighbor nodes\n    IVector neighbor_nodes(total_rows_of_neighbors);\n    // flag neighbor nodes that are in the existing rings as -(local_index)-1\n    thrust_wrapper::fill<AMGX_device>(neighbor_nodes.begin(), neighbor_nodes.end(), 0);\n    cudaCheckError();\n    int local_to_global_size = local_to_global.size();\n    int num_blocks2 = std::min(4096, (local_to_global_size + 127) / 128);\n\n    if (local_to_global_size != 0)\n    {\n        flag_halo_nodes_local_v2 <<< num_blocks2, 128>>>(halo_ranges.raw(), neighbor_nodes.raw(), neighbor_offsets.raw(), base_index, index_range, num_neighbors, local_to_global_size, local_to_global.raw());\n        cudaCheckError();\n    }\n\n    // 1) flag NEW neighbors nodes that I need as 1, they will be in the 2nd ring\n    // 2) fill out local indices for 1st ring & internal indices\n    int num_halos = halo_row_offsets.size();\n\n    for (int i = 0; i < num_halos; i++)\n    {\n        int size = halo_global_indices[i].size();\n\n        if (size > 0)\n        {\n            halo_local_indices[i].resize(size);\n            thrust_wrapper::fill<AMGX_device>(halo_local_indices[i].begin(), halo_local_indices[i].end(), -1); // fill with -1\n            // TODO: launch only on halo rows\n            flag_halo_nodes_global<16, 1> <<< num_blocks, 128>>>(halo_row_offsets[i].raw(), halo_row_offsets[i].size() - 1, halo_global_indices[i].raw(), halo_ranges.raw(), neighbor_nodes.raw(), neighbor_offsets.raw(), base_index, index_range, num_neighbors, halo_local_indices[i].raw());\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    // replace all negative values with 0 in neighbor flags\n    is_less_than_zero pred;\n    amgx::thrust::replace_if(neighbor_nodes.begin(), neighbor_nodes.end(), pred, 0);\n    cudaCheckError();\n    // fill halo offsets for the current number of  ring for new neighbors (it will be of size 0)\n    int current_num_neighbors = (halo_offsets.size() - 1) / current_num_rings;;\n    int num_rings = current_num_rings + 1;\n    IVector_h new_halo_offsets(num_rings * num_neighbors + 1);\n\n    for (int j = 0; j < current_num_rings; j++)\n    {\n        for (int i = 0 ; i <= current_num_neighbors; i++)\n        {\n            new_halo_offsets[num_neighbors * j + i] = halo_offsets[current_num_neighbors * j + i];\n        }\n\n        for (int i = current_num_neighbors; i < num_neighbors; i++)\n        {\n            new_halo_offsets[num_neighbors * j + i + 1] = new_halo_offsets[num_neighbors * j + i];\n        }\n    }\n\n    halo_offsets = new_halo_offsets;\n    int ring = current_num_rings;\n    int current_num_halo_indices = local_to_global.size();\n    //int halo_base = index_range + current_num_halo_indices;\n    cudaCheckError();\n\n    // compute neighbors nodes indices (in-place) for each neighbor\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last_node, num_halo;\n\n        if (neighbor_offsets_h[i + 1] != neighbor_offsets_h[i])\n        {\n            last_node = neighbor_nodes[neighbor_offsets_h[i + 1] - 1];\n            thrust_wrapper::exclusive_scan<AMGX_device>(neighbor_nodes.begin() + neighbor_offsets_h[i], neighbor_nodes.begin() + neighbor_offsets_h[i + 1], neighbor_nodes.begin() + neighbor_offsets_h[i]);\n            num_halo = neighbor_nodes[neighbor_offsets_h[i + 1] - 1] + last_node;\n        }\n        else\n        {\n            num_halo = 0;\n            last_node = 0;\n        }\n\n        // update halo offsets (L2H)\n        halo_offsets[ring * num_neighbors + i + 1] = halo_offsets[ring * num_neighbors + i] + num_halo;\n\n        // if size = 0 then we don't need to compute it\n        if (boundary_lists.size() > 0)\n        {\n            // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n            // basically this will be our 2-ring B2L_maps\n            boundary_lists[i].resize(num_halo);\n            int size = neighbor_offsets_h[i + 1] - neighbor_offsets_h[i];\n            num_blocks = std::min(4096, (size + 127) / 128);\n\n            if (size > 0)\n            {\n                populate_B2L <<< num_blocks, 128>>>(neighbor_nodes.raw() + neighbor_offsets_h[i], boundary_lists[i].raw(), last_node, size);\n                cudaCheckError();\n            }\n        }\n    }\n\n    cudaCheckError();\n    // compute local indices and new local to global mapping\n    int new_num_halo_indices = halo_offsets[num_rings * num_neighbors] - halo_offsets[current_num_rings * num_neighbors];\n    local_to_global.resize(current_num_halo_indices + new_num_halo_indices);\n    IVector halo_offsets_d(halo_offsets.size());\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    cudaCheckError();\n\n    // do this for all ring-1 neighbors\n    for (int i = 0; i < num_halos; i++)\n    {\n        int num_neighbor_rows = halo_row_offsets[i].size() - 1;\n        num_blocks = std::min(4096, (num_neighbor_rows + 127) / 128);\n\n        if (num_blocks > 0)\n        {\n            calc_new_halo_mapping_ring2<16> <<< num_blocks, 128>>>(\n                halo_row_offsets[i].raw(), num_neighbor_rows, halo_global_indices[i].raw(),                     // halo rows to process\n                halo_ranges.raw(), base_index, index_range, num_neighbors,                          // ranges and # of neighbors\n                halo_offsets_d.raw() + current_num_rings * num_neighbors, neighbor_offsets.raw(), neighbor_nodes.raw(),     // halo offsets, neighbor offsets and indices\n                local_to_global.raw(), halo_local_indices[i].raw());                                    // output\n                cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_rings(Matrix_d &A, INDEX_TYPE rings,\n        INDEX_TYPE neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    B2L_rings.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i ++)\n    {\n        B2L_rings[i].resize(rings + 1);\n        B2L_rings[i][0] = 0;\n    }\n\n    /*\n     EXAMPLE\n     For example matrix. partition 1, the matrix look like:\n     A.row_offsets = [0 4 11 15 20]\n     A.col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n     num_neighbors=2; neighbors = [0 2]\n     B2L_rings[[0 2][0 2]] B2L_maps [[0 1][1 3]] (Converted from the following input: btl_sizes = [0 2 4] btl_maps = [0 1| 1 3])\n     L2H_maps (and halo_lists) [[4 5][6 7]]\n     and we want to map further halo rings\n     */\n    //Scratch space computation\n    for (int i = 0; i < neighbors; i++)\n    {\n        B2L_rings[i][1] = B2L_maps[i].size();\n    }\n\n    //flag array\n    IVector halo_rings(A.get_num_rows());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // For each ring: flag nodes that are neighbors of the previous ring, then remove the nodes of the previous rings from the marking\n        // count the number of marked nodes, and append their row indices to the B2L_maps\n        //\n        for (int ring = 1; ring < rings; ring++)\n        {\n            size = B2L_rings[i][ring] - B2L_rings[i][ring - 1];\n            thrust_wrapper::fill<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, 0);\n            num_blocks = std::min(4096, (size + 127) / 128);\n            //Set 1 in flag array for any column index appreaing in the rows that belong to the previous ring\n            find_next_ring <<< num_blocks, 128>>>(B2L_maps[i].raw() + B2L_rings[i][ring - 1], A.row_offsets.raw(), A.col_indices.raw(), size, 0/*base_index*/, /*index_range*/size, halo_rings.raw());\n            cudaCheckError();\n            /*\n            EXAMPLE\n            for neighbor ID 0, halo_rings array initially [0 0 0 0], find_next_ring look at rows in the first ring [0 1] and sets halo_rings array positions to 1 for each column index in those rows:\n            halo_rings: [1 1 1 1]\n             */\n            //Remove all flags that belong to rows in any previous rings\n            remove_previous_rings <<< num_blocks, 128>>>(B2L_maps[i].raw(), halo_rings.raw(), B2L_rings[i][ring]);\n            cudaCheckError();\n            /*\n             EXAMPLE\n             for neighbor ID 0, halo_rings array [1 1 1 1], remove_previous_rings sets halo_rings positions to 0 for each index in previous B2L rings ([0 1] in this case):\n             halo_rings: [0 0 1 1]\n             */\n            //Count, index and append them to B2L_maps and update B2L_rings\n            thrust_wrapper::inclusive_scan<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, halo_rings.begin());\n            B2L_rings[i][ring + 1] = B2L_rings[i][ring] + halo_rings[size];\n            B2L_maps[i].resize(B2L_rings[i][ring + 1]);\n\n            /*\n             EXAMPLE\n             after inclusive scan: [0 0 1 2], B2L_rings[0][2] = 2+2, and B2L_maps[0] gets resized to 4\n             */\n            if (B2L_rings[i][ring] == B2L_rings[i][ring + 1])\n            {\n                for (int fill = ring; fill <= rings; fill++) { B2L_rings[i][fill] = B2L_rings[i][ring]; }\n\n                break;\n            }\n\n            num_blocks = std::min(4096, (A.get_num_rows() + 127) / 128);\n            write_ring_rows <<< num_blocks, 128>>>(halo_rings.raw(), B2L_maps[i].raw() + B2L_rings[i][ring], A.get_num_rows());\n            cudaCheckError();\n            /*\n             EXAMPLE\n             appends positions i to B2L_maps, where halo_rings[i-1] != halo_rings[i]\n             B2L_maps[0] = [0 1| 2 3] with B2L_rings[0] = [0 2 4]\n             */\n        }\n\n        cudaCheckError();\n    }\n}\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_rings(Matrix_d &A, INDEX_TYPE rings)\n{\n    int neighbors = A.manager->num_neighbors();\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    A.manager->B2L_rings.resize(neighbors * rings + 1);\n\n    for (int i = 0; i < neighbors; i ++)\n    {\n        A.manager->B2L_rings[i].resize(rings + 1);\n        A.manager->B2L_rings[i][0] = 0;\n    }\n\n    //Scratch space computation\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_rings[i][1] = A.manager->B2L_maps[i].size();\n    }\n\n    IVector halo_rings(A.get_num_rows());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // For each ring: flag nodes that are neighbors of the previous ring, then remove the nodes of the previous ring from the marking\n        // count the number of marked nodes, and append their row indices to the B2L_maps\n        //\n        for (int ring = 1; ring < rings; ring++)\n        {\n            size = A.manager->B2L_rings[i][ring] - A.manager->B2L_rings[i][ring - 1];\n            thrust_wrapper::fill<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, 0);\n            num_blocks = std::min(4096, (size + 127) / 128);\n            find_next_ring <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw() + A.manager->B2L_rings[i][ring - 1], A.row_offsets.raw(), A.col_indices.raw(), size, 0/*base_index*/, /*index_range*/size, halo_rings.raw());\n            cudaCheckError();\n            remove_previous_rings <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), halo_rings.raw(), A.manager->B2L_rings[i][ring]);\n            cudaCheckError();\n            thrust_wrapper::inclusive_scan<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, halo_rings.begin());\n            A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_rings[i][ring] + halo_rings[size];\n            A.manager->B2L_maps[i].resize(A.manager->B2L_rings[i][ring + 1]);\n\n            if (A.manager->B2L_rings[i][ring] == A.manager->B2L_rings[i][ring + 1])\n            {\n                for (int fill = ring; fill <= rings; fill++) { A.manager->B2L_rings[i][fill] = A.manager->B2L_rings[i][ring]; }\n\n                break;\n            }\n\n            num_blocks = std::min(4096, (A.get_num_rows() + 127) / 128);\n            write_ring_rows <<< num_blocks, 128>>>(halo_rings.raw(), A.manager->B2L_maps[i].raw() + A.manager->B2L_rings[i][ring], A.get_num_rows());\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_maps(Matrix_d &A, INDEX_TYPE rings,\n        INDEX_TYPE neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings, std::vector<IVector> &boundary_lists)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    B2L_maps.resize(neighbors);\n\n    //\n    // Step 4: export n-ring halo matrices\n    //\n    for (int i = 0; i < neighbors; i++)\n    {\n        B2L_maps[i] = boundary_lists[i];\n    }\n\n    create_rings(A, rings, neighbors, B2L_maps, B2L_rings);\n    //B2L_rings.resize(neighbors*rings+1);\n}\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_maps(Matrix_d &A, INDEX_TYPE rings, std::vector<IVector> &boundary_lists)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int neighbors = A.manager->num_neighbors();\n    A.manager->B2L_maps.resize(neighbors);\n\n    //\n    // Step 4: export n-ring halo matrices\n    //\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_maps[i] = boundary_lists[i];\n    }\n\n    create_rings(A, rings);\n    // A.manager->B2L_rings.resize(neighbors*rings+1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows(Matrix_d &A, INDEX_TYPE rings, INDEX_TYPE neighbors, std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings, std::vector<Matrix_d > **halo_rows_p, std::vector<IVector *> &halo_lists)\n{\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    *halo_rows_p = new std::vector<Matrix<TConfig> >(neighbors);\n    std::vector<Matrix<TConfig> > &halo_rows = **halo_rows_p;\n\n    if (this->halo_coloring == LAST)   //Create dummy matrices\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            halo_rows[i].addProps(CSR);\n\n            if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n            halo_rows[i].resize(halo_lists[i]->size(), halo_lists[i]->size(), (A.hasProps(DIAG) ? 0 : halo_lists[i]->size()), A.get_block_dimy(), A.get_block_dimx(), 1);\n\n            if (A.hasProps(DIAG))\n            {\n                thrust_wrapper::fill<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), 0);\n            }\n            else\n            {\n                thrust_wrapper::sequence<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end());\n                amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_rows[i].col_indices.begin());\n            }\n\n            thrust_wrapper::fill<AMGX_device>(halo_rows[i].values.begin(), halo_rows[i].values.end(), types::util< ValueTypeA >::get_one());\n            cudaCheckError();\n        }\n\n        return;\n    }\n\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        max_size = max_size > B2L_maps[i].size() ? max_size : B2L_maps[i].size();\n        //B2L_rings[i][1]=B2L_maps[i].size(); //I don't know what this was doing here, definitely stupid for rings > 1\n    }\n\n    IVector matrix_halo_sizes((max_size + 1)*rings);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        //\n        int size = B2L_rings[i][rings];\n        matrix_halo_sizes.resize(size + 1);\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        write_matrix_rowsize <<< num_blocks, 128>>>(B2L_maps[i].raw(), A.row_offsets.raw(), size, matrix_halo_sizes.raw());\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n        int nnz_count =  matrix_halo_sizes[size];\n        //\n        // Resize export halo matrix, and copy over the rows\n        //\n        halo_rows[i].addProps(CSR);\n\n        if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n        halo_rows[i].resize(B2L_maps[i].size(), B2L_maps[i].size(), nnz_count, A.get_block_dimy(), A.get_block_dimx(), 1);\n        export_matrix_elements<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows[i].values.raw(), A.col_indices.raw(), halo_rows[i].col_indices.raw(), size);\n        cudaCheckError();\n        amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows[i].row_offsets.begin());\n\n        if (A.hasProps(DIAG))\n        {\n            export_matrix_diagonal <<< num_blocks, 128>>>(A.values.raw() + A.row_offsets[A.get_num_rows()]*A.get_block_size(), A.get_block_size(), B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*A.get_block_size(), size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_manager(Matrix_d &A, Matrix_d &P, int num_owned_coarse_pts)\n{\n    // This function initializes the manager of matrix P based on value of num_owned_coarse_pts\n    // The following member of the manager are initialized:\n    // global_id, num_partitions, comms (copied from manager of matrix A)\n    // part_offsets_h, part_offsets, base_index, index_range, num_rows_global, halo_offsets[0] (those are initialized based on value of num_owned_coarse_pts of all partitions in communicator)\n    if (P.manager == NULL)\n    {\n        P.manager = new DistributedManager<TConfig_d>();\n    }\n\n    P.setView(OWNED);\n    // Copy some information from manager of matrix A\n    P.manager->A = &P;\n    P.manager->setComms(A.manager->getComms());\n    index_type num_parts = A.manager->get_num_partitions();\n    index_type my_rank = A.manager->global_id();\n    P.manager->set_global_id(my_rank);\n    P.manager->set_num_partitions(num_parts);\n    // Do a global reduce to obtain part_offsets for RAP\n    IVector_h gathered_count;\n    A.manager->getComms()->all_gather(num_owned_coarse_pts, gathered_count, num_parts);\n    // Compute P.manager->part_offsets\n    P.manager->part_offsets_h.resize(num_parts + 1);\n    P.manager->part_offsets_h[0] = (int64_t) 0;\n\n    for (int i = 1; i < num_parts + 1; i++)\n    {\n        P.manager->part_offsets_h[i] = P.manager->part_offsets_h[i - 1] + (int64_t) gathered_count[i - 1];\n    }\n\n    P.manager->part_offsets = P.manager->part_offsets_h;\n    // Compute P.manager->halo_ranges\n    P.manager->set_base_index(P.manager->part_offsets_h[my_rank]);\n    P.manager->set_index_range(num_owned_coarse_pts);\n    P.manager->num_rows_global = P.manager->part_offsets_h[num_parts];\n    // Set halo_offsets[0]\n    P.manager->halo_offsets.resize(1);\n    P.manager->halo_offsets[0] = num_owned_coarse_pts;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_manager_from_global_col_indices(Matrix_d &P, I64Vector_d &P_col_indices_global)\n{\n    // This function finds the number of neighbors of P\n    // fills halo_offsets, local_to_global_map, B2L_maps based on global col indices of matrix P\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    // By checking the global col indices, identify neighbors\n    P.manager->neighbors.resize(0);\n    this->create_neighbors_v2_global(P, P_col_indices_global);\n    int num_neighbors = P.manager->neighbors.size();\n    P.manager->getComms()->set_neighbors(num_neighbors);\n    P.manager->halo_offsets.resize(num_neighbors + 1, 0);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> flag_offsets_h(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows = (int) (P.manager->halo_ranges_h[2 * i + 1] - P.manager->halo_ranges_h[2 * i]);\n        total_rows_of_neighbors += num_rows;\n        flag_offsets_h[i + 1] = flag_offsets_h[i] + num_rows;\n    }\n\n    // Copy to device\n    IVector flag_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(flag_offsets_h.begin(), flag_offsets_h.end(), flag_offsets_d.begin());\n    // Allocate halo_nodes array that will be used to flag halo nodes\n    // Flag_offsets array can be used to determine where each neighbor begins\n    IVector halo_nodes(total_rows_of_neighbors + 1);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    int num_blocks = std::min(4096, (P.get_num_rows() + 127) / 128);\n\n    if (P.get_num_rows() > 0)\n    {\n        flag_halo_nodes_global_v2<16> <<< num_blocks, 128>>>(P.row_offsets.raw(), P_col_indices_global.raw(), P.manager->halo_ranges.raw(), halo_nodes.raw(), flag_offsets_d.raw(), P.manager->base_index(), P.manager->index_range(), num_neighbors, P.get_num_rows());\n        cudaCheckError();\n    }\n\n    // Step 3: Write the 1-ring global nodes for each neighbor\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin());\n    cudaCheckError();\n    std::vector<IVector> bdy_lists;\n    bdy_lists.resize(num_neighbors);\n    int count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo = halo_nodes[flag_offsets_h[i + 1]] - count;\n        P.manager->halo_offsets[i + 1] = P.manager->halo_offsets[i] + num_halo;\n        bdy_lists[i].resize(num_halo);\n\n        if (num_halo > 0)\n        {\n            int size = flag_offsets_h[i + 1] - flag_offsets_h[i];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            populate_B2L_v2 <<< num_blocks, 128>>>(halo_nodes.raw() + flag_offsets_h[i], bdy_lists[i].raw(), count, size);\n            cudaCheckError();\n        }\n\n        count += num_halo;\n    }\n\n    cudaCheckError();\n\n    // Fill the col_indices\n    if (P.get_num_rows() > 0)\n    {\n        renumber_col_indices_global<16> <<< num_blocks, 128>>>(P.row_offsets.raw(), P.col_indices.raw(), P_col_indices_global.raw(), P.manager->halo_ranges.raw(), halo_nodes.raw(), flag_offsets_d.raw(), P.manager->base_index(), P.manager->index_range(), num_neighbors, P.get_num_rows(), P.manager->global_id());\n        cudaCheckError();\n    }\n\n    // Create P.local_to_global_map\n    P.manager->local_to_global_map.resize(count);\n    count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo = bdy_lists[i].size();\n\n        if (num_halo > 0)\n        {\n            int num_blocks = std::min(4096, (num_halo + 127) / 128);\n            fill_local_to_global_map <<< num_blocks, 128>>>(bdy_lists[i].raw(), P.manager->local_to_global_map.raw() + count, P.manager->halo_ranges_h[2 * i], num_halo);\n            cudaCheckError();\n        }\n\n        count += num_halo;\n    }\n\n    cudaCheckError();\n    P.manager->getComms()->exchange_vectors(bdy_lists, P, 0);\n    // copy boundary lists to B2L maps\n    // release internal boundary lists array\n    P.manager->B2L_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        P.manager->B2L_maps[i] = bdy_lists[i];\n    }\n\n    P.manager->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        P.manager->B2L_rings[i].resize(2);\n        P.manager->B2L_rings[i][0] = 0;\n        P.manager->B2L_rings[i][1] = P.manager->B2L_maps[i].size();\n    }\n\n    this->initialize_B2L_maps_offsets(P, 1);\n    P.manager->_num_interior_nodes = P.get_num_rows();\n    P.manager->_num_boundary_nodes = 0;\n    P.manager->set_initialized(P.row_offsets);\n    int size_one_ring, offset;\n    P.manager->getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    P.setView(OWNED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createTempManager(Matrix_d &B, Matrix_d &A, IVector &row_offsets)\n{\n    // Initialize P.manager\n    if (B.manager == NULL)\n    {\n        B.manager = new DistributedManager<TConfig_d>();\n    }\n\n    index_type num_parts = A.manager->get_num_partitions();\n    index_type my_rank = A.manager->global_id();\n    B.manager->A = &B;\n    B.manager->setComms(A.manager->getComms());\n    B.manager->set_global_id(my_rank);\n    B.manager->set_num_partitions(num_parts);\n    B.manager->neighbors = A.manager->neighbors;\n    int num_neighbors = A.manager->neighbors.size();\n    B.manager->getComms()->set_neighbors(num_neighbors);\n    // --------------------------------------\n    // Create B.manager->B2L_maps\n    // --------------------------------------\n    B.manager->B2L_maps.resize(num_neighbors);\n    int max_B2L_maps_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int A_b2l_map_size = A.manager->B2L_rings[i][1];\n\n        if (A_b2l_map_size > max_B2L_maps_size)\n        {\n            max_B2L_maps_size = A_b2l_map_size;\n        }\n    }\n\n    IVector row_sizes(row_offsets.size() - 1);\n    thrust_wrapper::adjacent_difference<AMGX_device>(row_offsets.begin() + 1, row_offsets.end(), row_sizes.begin());\n    cudaCheckError();\n    IVector B2L_maps_offsets(max_B2L_maps_size + 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int A_b2l_map_size = A.manager->B2L_rings[i][1];\n\n        if (A_b2l_map_size > 0)\n        {\n            // For each node in the B2L_map for that neighbor, write the row size in B2L_maps_offsets\n            thrust_wrapper::gather<AMGX_device>(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].begin() + A_b2l_map_size, row_sizes.begin(), B2L_maps_offsets.begin());\n            // Do exclusive scan, to go from row length to row offsets\n            thrust_wrapper::exclusive_scan<AMGX_device>(B2L_maps_offsets.begin(), B2L_maps_offsets.begin() + A_b2l_map_size + 1, B2L_maps_offsets.begin());\n            B.manager->B2L_maps[i].resize(B2L_maps_offsets[A_b2l_map_size]);\n            const int cta_size = 128;\n            const int nWarps = cta_size / 32;\n            int grid_size = std::min( 4096, (int) (A_b2l_map_size + nWarps - 1) / nWarps);\n            // Populate the B2L_map array\n            fill_B_B2L_map<int, cta_size > <<< grid_size, cta_size >>>(\n                B2L_maps_offsets.raw(),\n                A.manager->B2L_maps[i].raw(),\n                row_offsets.raw(),\n                B.manager->B2L_maps[i].raw(),\n                A_b2l_map_size,\n                A.manager->global_id());\n                cudaCheckError();\n        }\n        else\n        {\n            B.manager->B2L_maps[i].resize(0);\n        }\n\n        cudaCheckError();\n    }\n\n    B.manager->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B.manager->B2L_rings[i].resize(2);\n        B.manager->B2L_rings[i][0] = 0;\n        B.manager->B2L_rings[i][1] = B.manager->B2L_maps[i].size();\n    }\n\n    // Initialize the B2L_maps_offsets (required by gather_v2 and scatter_v2) from the B2L_rings values\n    this->initialize_B2L_maps_offsets(B, 1);\n    B.manager->halo_offsets.resize(num_neighbors + 1);\n\n    // Fill halo_offsets\n    for (int i = 0; i < num_neighbors + 1; i++)\n    {\n        B.manager->halo_offsets[i] = row_offsets[A.manager->halo_offsets[i]];\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::exchange_halo_rows_P(Matrix_d &A, Matrix_d &P, I64Vector &RAP_local_to_global_map, IVector_h &P_neighbors, I64Vector_h &P_halo_ranges_h, I64Vector &P_halo_ranges, IVector_h &P_halo_offsets, I64Vector_h &RAP_part_offsets_h, I64Vector &RAP_part_offsets, index_type num_owned_coarse_pts, int64_t coarse_base_index)\n{\n    if (P.hasProps(DIAG) || P.get_block_size() != 1)\n    {\n        FatalError(\"P with external diagonal or block_size != 1 not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int num_neighbors = A.manager->num_neighbors() ;\n    std::vector<IVector> halo_rows_P_row_offsets;\n    std::vector<I64Vector> halo_rows_P_col_indices;\n    std::vector<IVector> halo_rows_P_local_col_indices;\n    std::vector<MVector> halo_rows_P_values;\n    // In this function, store in halo_rows_P_row_offsets, halo_rows_P_col_indices and halo_rows_P_values, the rows of P that need to be sent to each neighbors\n    // halo_rows_P_col_indices stores global indices\n    this->pack_halo_rows_P(A, P, halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_values, RAP_local_to_global_map, num_owned_coarse_pts, coarse_base_index);\n    // Do the exchange with the neighbors\n    // On return, halo_rows_P_rows_offsets, halo_rows_P_col_indices and halo_rows_P_values stores the rows of P received from each neighbor (rows needed to perform A*P)\n    std::vector<I64Vector> dummy_halo_ids(0);\n    A.manager->getComms()->exchange_matrix_halo(halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_values, dummy_halo_ids, A.manager->neighbors, A.manager->global_id());\n    halo_rows_P_local_col_indices.resize(halo_rows_P_col_indices.size());\n\n    for (int i = 0; i < halo_rows_P_col_indices.size(); i++)\n    {\n        halo_rows_P_local_col_indices[i].resize(halo_rows_P_col_indices[i].size());\n    }\n\n    // Update the list of neighbors \"P_neighbors\" and the corresponding ranges, offsets\n    update_neighbors_list(A, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP_part_offsets_h, RAP_part_offsets, halo_rows_P_row_offsets, halo_rows_P_col_indices);\n    std::vector<IVector> dummy_boundary_list(0);\n    int current_num_rings = 1;\n    halo_rows_P_local_col_indices.resize(halo_rows_P_col_indices.size());\n    // Convert the global indices in the rows just received to local indices\n    // This function updates halo offsets, halo_rows_P_local_col_indices, local_to_global_map\n    // This should not modify the existing P manager\n    this->compute_local_halo_indices( P.row_offsets, P.col_indices, halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_local_col_indices, RAP_local_to_global_map,  dummy_boundary_list, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, coarse_base_index, num_owned_coarse_pts, P.get_num_rows(), current_num_rings);\n    // Append the new rows to the matrix P\n    P.set_initialized(0);\n    this->append_halo_rows(P, halo_rows_P_row_offsets, halo_rows_P_local_col_indices, halo_rows_P_values);\n    P.set_initialized(1);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::append_halo_rows(Matrix_d &A, std::vector<IVector> &halo_row_offsets, std::vector<IVector> &halo_local_indices, std::vector<MVector> &halo_values )\n{\n    int new_num_rows = A.get_num_rows();\n    int new_num_nnz = A.row_offsets[A.get_num_rows()];\n    int cur_row = A.get_num_rows();\n    int cur_offset = new_num_nnz;\n    int num_neighbors = halo_row_offsets.size();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = halo_row_offsets[i].size();\n\n        if (size != 0)\n        {\n            new_num_rows += halo_row_offsets[i].size() - 1;\n            new_num_nnz += halo_local_indices[i].size();\n        }\n    }\n\n    A.resize(new_num_rows, new_num_rows, new_num_nnz, A.get_block_dimx(), A.get_block_dimy(), 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo_rows = halo_row_offsets[i].size() - 1;\n\n        if (num_halo_rows > 0)\n        {\n            // update halo row offsets in-place\n            thrust_wrapper::transform<AMGX_device>(halo_row_offsets[i].begin(), halo_row_offsets[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE>(cur_offset), halo_row_offsets[i].begin(), amgx::thrust::plus<INDEX_TYPE>());\n            // insert halo rows\n            amgx::thrust::copy(halo_row_offsets[i].begin(), halo_row_offsets[i].end() - 1, A.row_offsets.begin() + cur_row);\n            amgx::thrust::copy(halo_local_indices[i].begin(), halo_local_indices[i].end(), A.col_indices.begin() + cur_offset);\n            amgx::thrust::copy(halo_values[i].begin(), halo_values[i].end(), A.values.begin() + cur_offset);\n            // update counters\n            cur_offset = halo_row_offsets[i][num_halo_rows];\n            cur_row += num_halo_rows;\n        }\n    }\n\n    cudaCheckError();\n    A.row_offsets[A.get_num_rows()] = cur_offset;\n    int num_cols = -1;\n    num_cols = thrust_wrapper::reduce<AMGX_device>(A.col_indices.begin(), A.col_indices.end(), num_cols, amgx::thrust::maximum<int>()) + 1;\n    cudaCheckError();\n    A.set_num_cols(num_cols);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::exchange_RAP_ext(Matrix_d &RAP, Matrix_d &RAP_full, Matrix_d &A, Matrix_d &P, IVector_h &P_halo_offsets, I64Vector &RAP_local_to_global_map, IVector_h &P_neighbors, I64Vector_h &P_halo_ranges_h, I64Vector &P_halo_ranges, I64Vector_h &RAP_part_offsets_h, I64Vector &RAP_part_offsets, index_type num_owned_coarse_pts, int64_t coarse_base_index, void *wk)\n{\n    if (RAP_full.hasProps(DIAG) || RAP_full.get_block_size() != 1)\n    {\n        FatalError(\"RAP_full with external diagonal or block_size != 1 not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // --------------------------------------------------------------------------------------\n    // Pack the rows of RAP_full that need to be sent to neighbors, using the manager of P\n    // --------------------------------------------------------------------------------------\n    std::vector<IVector> halo_rows_RAP_row_offsets;\n    std::vector<I64Vector> halo_rows_RAP_row_ids;\n    std::vector<I64Vector> halo_rows_RAP_col_indices;\n    std::vector<MVector> halo_rows_RAP_values;\n    this->pack_halo_rows_RAP(RAP_full, P, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_values, halo_rows_RAP_row_ids, RAP_local_to_global_map);\n    // Send the rows to the neighbors, receive RAP rows from neighbors\n    // Col indices received from neighbors are global indices\n    // We also receive from neighbors the global row ids\n    P.manager->getComms()->exchange_matrix_halo(halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_values, halo_rows_RAP_row_ids, P.manager->neighbors, P.manager->global_id());\n    std::vector<IVector> halo_rows_RAP_local_col_indices;\n    halo_rows_RAP_local_col_indices.resize(halo_rows_RAP_col_indices.size());\n\n    for (int i = 0; i < halo_rows_RAP_col_indices.size(); i++)\n    {\n        halo_rows_RAP_local_col_indices[i].resize(halo_rows_RAP_col_indices[i].size());\n    }\n\n    // This function finds the new number of neighbors, renumber halo_cols with local_ids, modifies halo_offsets\n    update_neighbors_list(A, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP_part_offsets_h, RAP_part_offsets, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices);\n    // Now renumber find local indices for global col indices, update local_to_global\n    std::vector<IVector> dummy_boundary_list(0);\n    int current_num_rings = 2;\n    this->compute_local_halo_indices(RAP_full.row_offsets, RAP_full.col_indices, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_local_col_indices, RAP_local_to_global_map, dummy_boundary_list, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, coarse_base_index, num_owned_coarse_pts, num_owned_coarse_pts, current_num_rings );\n    // Now find local row ids from global ids of rows received\n    std::vector<IVector> halo_rows_RAP_local_row_ids;\n    halo_rows_RAP_local_row_ids.resize(halo_rows_RAP_row_ids.size());\n\n    for (int i = 0; i < halo_rows_RAP_row_ids.size(); i++)\n    {\n        int size = halo_rows_RAP_row_ids[i].size();\n        halo_rows_RAP_local_row_ids[i].resize(size);\n\n        if (size != 0)\n        {\n            int cta_size = 128;\n            int grid_size = std::min( 4096, (size + cta_size - 1) / cta_size);\n            fill_halo_rows_row_ids <<< grid_size, cta_size >>>( halo_rows_RAP_row_ids[i].raw(), halo_rows_RAP_local_row_ids[i].raw(), coarse_base_index, num_owned_coarse_pts, size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    // Add the rows received from neighbors to owned rows in RAP_full and create RAP matrix\n    RAP.set_initialized( 0 );\n    RAP.set_num_rows(num_owned_coarse_pts);\n    RAP.set_num_cols(num_owned_coarse_pts + RAP.manager->local_to_global_map.size());\n    RAP.row_offsets.resize(num_owned_coarse_pts + 1);\n    RAP_full.setView(OWNED);\n    CSR_Multiply<TConfig_d>::csr_RAP_sparse_add( RAP, RAP_full, halo_rows_RAP_row_offsets, halo_rows_RAP_local_col_indices, halo_rows_RAP_values, halo_rows_RAP_local_row_ids, wk );\n    RAP.set_initialized( 1 );\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::pack_halo_rows_P(Matrix_d &A, Matrix_d &P, std::vector<IVector> &halo_rows_P_row_offsets, std::vector<I64Vector> &halo_rows_P_col_indices, std::vector<MVector> &halo_rows_P_values, I64Vector &RAP_local_to_global_map, index_type num_owned_coarse_pts, int64_t coarse_base_index)\n{\n    int num_neighbors = A.manager->num_neighbors();\n    halo_rows_P_row_offsets.resize(num_neighbors);\n    halo_rows_P_col_indices.resize(num_neighbors);\n    halo_rows_P_values.resize(num_neighbors);\n    int num_rings_to_send = 1;\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > A.manager->B2L_rings[i][num_rings_to_send] ? max_size : A.manager->B2L_rings[i][num_rings_to_send];\n    }\n\n    IVector matrix_halo_sizes(max_size + 1);\n\n    // Here only using the 1-ring of matrix A\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        int size = A.manager->B2L_rings[i][num_rings_to_send];\n\n        if (size != 0)\n        {\n            //matrix_halo_sizes.resize(size+1);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), P.row_offsets.raw(), size, matrix_halo_sizes.raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n            int nnz_count =  matrix_halo_sizes[size];\n            // Resize export halo matrix, and copy over the rows\n            halo_rows_P_row_offsets[i].resize(size + 1);\n            halo_rows_P_col_indices[i].resize(nnz_count);\n            halo_rows_P_values[i].resize(nnz_count);\n            /* WARNING: Since A is reordered (into interior and boundary nodes), while R and P are not reordered,\n                        you must unreorder A when performing R*A*P product in ordre to obtain the correct result. */\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(P.row_offsets.raw(), P.values.raw(), P.get_block_size(), A.manager->B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows_P_values[i].raw(), P.col_indices.raw(), halo_rows_P_col_indices[i].raw(), size, RAP_local_to_global_map.raw(), NULL /*A.manager->inverse_renumbering.raw()*/, num_owned_coarse_pts, coarse_base_index);\n            cudaCheckError();\n            amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows_P_row_offsets[i].begin());\n        }\n        else\n        {\n            halo_rows_P_row_offsets[i].resize(0);\n            halo_rows_P_col_indices[i].resize(0);\n            halo_rows_P_values[i].resize(0);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::pack_halo_rows_RAP(Matrix_d &RAP, Matrix_d &P, std::vector<IVector> &halo_rows_RAP_row_offsets, std::vector<I64Vector> &halo_rows_RAP_col_indices, std::vector<MVector> &halo_rows_RAP_values, std::vector<I64Vector> &halo_rows_RAP_row_ids, I64Vector &RAP_local_to_global_map)\n{\n    int num_neighbors = P.manager->neighbors.size();\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    int64_t P_col_base_index = P.manager->base_index();\n    halo_rows_RAP_row_offsets.resize(num_neighbors);\n    halo_rows_RAP_row_ids.resize(num_neighbors);\n    halo_rows_RAP_col_indices.resize(num_neighbors);\n    halo_rows_RAP_values.resize(num_neighbors);\n    // Create tmp_B2L_maps that contains the list of rows to send\n    std::vector<IVector> tmp_B2L_maps(num_neighbors);\n\n    // Fill B2L_maps with list of halo indices for all rings to send\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        tmp_B2L_maps[i].resize(0);\n        halo_rows_RAP_row_ids[i].resize(0);\n        int begin = P.manager->halo_offsets[i];\n        int end =  P.manager->halo_offsets[i + 1];\n\n        if (begin > RAP.row_offsets.size() - 1)\n        {\n            break;\n        }\n\n        int local_to_global_begin = begin - P.manager->halo_offsets[0];\n        int size = end - begin;\n\n        if (end > RAP.row_offsets.size() - 1)\n        {\n            size = RAP.row_offsets.size() - 1 - begin;\n        }\n\n        if (size > 0)\n        {\n            tmp_B2L_maps[i].resize(size);\n            halo_rows_RAP_row_ids[i].resize(size);\n            thrust_wrapper::sequence<AMGX_device>(tmp_B2L_maps[i].begin(), tmp_B2L_maps[i].begin() + size, begin);\n            amgx::thrust::copy(P.manager->local_to_global_map.begin() + (int64_t) local_to_global_begin, P.manager->local_to_global_map.begin() + (int64_t) local_to_global_begin + (int64_t) size, halo_rows_RAP_row_ids[i].begin());\n        }\n    }\n\n    cudaCheckError();\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = tmp_B2L_maps[i].size();\n        max_size = max_size > size ? max_size : size;\n    }\n\n    IVector matrix_halo_sizes(max_size + 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        int size = tmp_B2L_maps[i].size();\n\n        if (size > 0)\n        {\n            //matrix_halo_sizes.resize(size+1);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(tmp_B2L_maps[i].raw(), RAP.row_offsets.raw(), size, matrix_halo_sizes.raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n            int nnz_count =  matrix_halo_sizes[size];\n            // Resize export halo matrix, and copy over the rows\n            halo_rows_RAP_row_offsets[i].resize(tmp_B2L_maps[i].size() + 1);\n            halo_rows_RAP_col_indices[i].resize(nnz_count);\n            halo_rows_RAP_values[i].resize(nnz_count);\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(RAP.row_offsets.raw(), RAP.values.raw(), RAP.get_block_size(), tmp_B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows_RAP_values[i].raw(), RAP.col_indices.raw(), halo_rows_RAP_col_indices[i].raw(), size, RAP_local_to_global_map.raw(), NULL, num_owned_coarse_pts, P_col_base_index);\n            cudaCheckError();\n            amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows_RAP_row_offsets[i].begin());\n        }\n        else\n        {\n            halo_rows_RAP_row_offsets[i].resize(0);\n            halo_rows_RAP_col_indices[i].resize(0);\n            halo_rows_RAP_values[i].resize(0);\n        }\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::sparse_matrix_add(Matrix_d &RAP, std::vector<IVector> &halo_rows_RAP_row_offsets, std::vector<I64Vector> &halo_rows_RAP_col_indices, std::vector<MVector> &halo_rows_RAP_values, std::vector<I64Vector> halo_rows_RAP_row_ids, I64Vector_d &P_local_to_global_map, index_type P_col_num_owned_pts, int64_t P_col_base_index)\n{\n    // Look at csr_galerkin_product implementation, use hash\n}\n\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows(Matrix_d &A,\n        std::vector<IVector *> &halo_lists\n                                                                                                         )\n{\n    int neighbors = A.manager->num_neighbors();\n\n    if (neighbors == 0)\n    {\n        return;\n    }\n\n    int rings = (A.manager->B2L_rings.size() - 1) / neighbors;\n    A.manager->halo_rows = new std::vector<Matrix<TConfig> >(neighbors);\n    std::vector<Matrix<TConfig> > &halo_rows = *A.manager->halo_rows;\n\n    if (this->halo_coloring == LAST)   //Create dummy matrices, only containing diagonal elements\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            halo_rows[i].addProps(CSR);\n\n            if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n            halo_rows[i].resize(halo_lists[i]->size(), halo_lists[i]->size(), (A.hasProps(DIAG) ? 0 : halo_lists[i]->size()), A.get_block_dimy(), A.get_block_dimx(), 1);\n\n            if (A.hasProps(DIAG))\n            {\n                thrust_wrapper::fill<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), 0);\n            }\n            else\n            {\n                thrust_wrapper::sequence<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end());\n                amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_rows[i].col_indices.begin());\n            }\n\n            cudaCheckError();\n        }\n\n        return;\n    }\n\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        max_size = max_size > A.manager->B2L_maps[i].size() ? max_size : A.manager->B2L_maps[i].size();\n        //B2L_rings[i][1]=B2L_maps[i].size(); //I don't know what this was doing here, definitely stupid for rings > 1\n    }\n\n    IVector matrix_halo_sizes((max_size + 1)*rings);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        //\n        int size = A.manager->B2L_rings[i][rings];\n        matrix_halo_sizes.resize(size + 1);\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), A.row_offsets.raw(), size, matrix_halo_sizes.raw());\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n        cudaCheckError();\n        int nnz_count =  matrix_halo_sizes[size];\n        //\n        // Resize export halo matrix, and copy over the rows\n        //\n        halo_rows[i].addProps(CSR);\n\n        if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n        halo_rows[i].resize(A.manager->B2L_maps[i].size(), A.manager->B2L_maps[i].size(), nnz_count, A.get_block_dimy(), A.get_block_dimx(), 1);\n        export_matrix_elements<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), A.manager->B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows[i].values.raw(), A.col_indices.raw(), halo_rows[i].col_indices.raw(), size);\n        cudaCheckError();\n        amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows[i].row_offsets.begin());\n        cudaCheckError();\n\n        if (A.hasProps(DIAG))\n        {\n            export_matrix_diagonal <<< num_blocks, 128>>>(A.values.raw() + A.row_offsets[A.get_num_rows()]*A.get_block_size(), A.get_block_size(), A.manager->B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*A.get_block_size(), size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows_global_indices(Matrix_d &A,\n        std::vector<IVector> &halo_rows_row_offsets,\n        std::vector<I64Vector> &halo_rows_col_indices,\n        std::vector<MVector> &halo_rows_values)\n{\n    // in this function we assume that we only have 1-ring so far\n    int neighbors = A.manager->num_neighbors();\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        // compute row offsets and nnz count\n        int size = A.manager->B2L_maps[i].size();\n        halo_rows_row_offsets[i].resize(size + 1);\n\n        if (size > 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), A.row_offsets.raw(), size, halo_rows_row_offsets[i].raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(halo_rows_row_offsets[i].begin(), halo_rows_row_offsets[i].begin() + size + 1, halo_rows_row_offsets[i].begin());\n            // compute global indices\n            int nnz_count = halo_rows_row_offsets[i][size];\n            halo_rows_col_indices[i].resize(nnz_count);\n            halo_rows_values[i].resize(nnz_count);\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), A.manager->B2L_maps[i].raw(), halo_rows_row_offsets[i].raw(), halo_rows_values[i].raw(), A.col_indices.raw(), halo_rows_col_indices[i].raw(), size, A.manager->local_to_global_map.raw(), NULL, A.get_num_rows(), A.manager->base_index());\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl(Matrix_d &A, INDEX_TYPE rings, INDEX_TYPE neighbors,\n        Vector<ivec_value_type_h> &neighbors_list,\n        int64_t base_index, INDEX_TYPE index_range,\n        INDEX_TYPE global_id, std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        std::vector<Manager_d > **halo_btl_p,\n\n        std::vector<IVector *> &halo_lists\n                                                                                                        )\n{\n    *halo_btl_p = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = **halo_btl_p;\n    /*\n    //calling constructors on the allocated memory\n    for(int i=0; i<neighbors; i++)\n        new (&halo_btl[i]) DistributedManager<TConfig>();\n    */\n\n    if (this->halo_coloring != LAST)\n    {\n        //If we are sending matrices over\n        for (int i = 0; i < neighbors; i++)\n        {\n            //\n            // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n            //\n            /*\n            EXAMPLE\n            For example partition 1, with 2 rings, and the following B2L and L2H (called halo_lists here) info\n            num_neighbors=2; neighbors = [0 2]\n            B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n            L2H_maps (halo_lists) [[4 5][6 7]]\n            */\n            int size = B2L_rings[i][rings];\n            halo_btl[i].set_global_id(global_id);\n            halo_btl[i].set_base_index(0);\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(halo_lists[i]->size());\n            //send over B2L and L2H maps corresponding to current neighbor, so that it will be able to make sense of indices (local to this partition)\n            amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].L2H_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            amgx::thrust::copy(B2L_rings[i].begin(), B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n            /*\n             EXAMPLE\n             These fields are going to be:\n             halo_btl[0] (for neighbor ID 0)\n             global_id = 1; base_index=0; index_range=6 B2L_rings[0] = [0 2 4] B2L_maps[0] = [0 1| 2 3] L2H_maps = [4 5]\n             halo_btl[1] (for neighbor ID 2)\n            global_id = 1; base_index=0; index_range=8 B2L_rings[0] = [0 2 4] B2L_maps[0] = [1 3| 0 2] L2H_maps = [6 7]\n             */\n        }\n    }\n    else\n    {\n        if (rings > 1) { FatalError(\"halo_coloring=LAST dummy halo rows creation not implemented\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n        //Create dummy halo matrices\n        for (int i = 0; i < neighbors; i++)\n        {\n            int size = halo_lists[i]->size();\n            halo_btl[i].set_global_id(neighbors_list[i]);\n            halo_btl[i].set_base_index(0);\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(B2L_rings[i][rings]);\n            //Even though in this case matrices are not being sent over, we create the same data structures, so they can be handled the same way\n            amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].B2L_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = size;\n        }\n    }\n\n    cudaCheckError();\n}\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl(Matrix_d &A,\n        std::vector<IVector *> &halo_lists)\n{\n    int neighbors = A.manager->num_neighbors();\n\n    if (neighbors == 0)\n    {\n        return;\n    }\n\n    int rings = (A.manager->B2L_rings.size() - 1) / neighbors;\n    A.manager->halo_btl = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = *A.manager->halo_btl;\n\n    if (this->halo_coloring != LAST)\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            //\n            // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n            //\n            int size = A.manager->B2L_rings[i][rings];\n            halo_btl[i].set_global_id(A.manager->global_id());\n            halo_btl[i].set_base_index(0);\n            //Adding one more ring to store halo numbering\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(halo_lists[i]->size());\n            amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].L2H_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            amgx::thrust::copy(A.manager->B2L_rings[i].begin(), A.manager->B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n        }\n    }\n    else\n    {\n        if (rings > 1) { FatalError(\"halo_coloring=LAST dummy halo rows creation not implemented\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n        //Create dummy halo matrices\n        for (int i = 0; i < neighbors; i++)\n        {\n            int size = halo_lists[i]->size();\n            halo_btl[i].set_global_id(A.manager->neighbors[i]);\n            halo_btl[i].set_base_index(0);\n            //Adding one more ring to store halo numbering\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(A.manager->B2L_rings[i][rings]);\n            amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].B2L_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = size;\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl_multiple_rings(Matrix_d &A, int rings)\n{\n    // creates halo btl managers\n    // required input from A.manager: num_neighbors, B2L_rings, B2L_maps, L2H_maps\n    int neighbors = A.manager->num_neighbors();\n    A.manager->halo_btl = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = *A.manager->halo_btl;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n        int size = A.manager->B2L_rings[i][rings];\n        halo_btl[i].set_global_id(A.manager->global_id());\n        halo_btl[i].set_base_index(0);\n        // Adding one more ring to store halo numbering\n        halo_btl[i].resize(1, rings);\n        halo_btl[i].B2L_maps[0].resize(size);\n        halo_btl[i].L2H_maps.resize(1);\n        halo_btl[i].L2H_maps[0].resize(A.manager->L2H_maps[i].size());\n        amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n        amgx::thrust::copy(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n        amgx::thrust::copy(A.manager->B2L_rings[i].begin(), A.manager->B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n        // compute index range\n        int max_index = thrust_wrapper::reduce<AMGX_device>(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n        max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n        halo_btl[i].set_index_range(max_index);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_matrices(Matrix_d &A, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        INDEX_TYPE global_id, Vector<ivec_value_type_h> &neighbors_,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        std::vector<Matrix_d > **halo_rows_p,\n        std::vector<Manager_d > **halo_btl_p, std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists)\n{\n    int neighbors = neighbors_.size();\n    this->create_maps(A, rings, neighbors, B2L_maps, B2L_rings, boundary_lists);\n    this->create_halo_btl(A, rings, neighbors, neighbors_, base_index, index_range, global_id, B2L_maps, B2L_rings, halo_btl_p, halo_lists);\n    this->create_halo_rows(A, rings, neighbors, B2L_maps, B2L_rings, halo_rows_p, halo_lists);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_d &A, INDEX_TYPE my_id, INDEX_TYPE rings)\n{\n    this->halo_coloring = A.manager->getComms()->halo_coloring;\n    I64Vector_h halo_ranges_h;\n    this->create_neighbors(A, my_id, halo_ranges_h);\n    A.manager->set_global_id(my_id);\n    int num_neighbors = A.manager->neighbors.size();\n    A.manager->resize(num_neighbors, rings);\n    A.manager->getComms()->set_neighbors(num_neighbors);\n    std::vector<IVector> boundary_lists;\n    IVector halo_nodes;\n    this->create_boundary_lists(A, halo_ranges_h, boundary_lists, halo_nodes);\n    A.manager->L2H_maps.resize(num_neighbors);\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        A.manager->L2H_maps[i].resize(boundary_lists[i].size());\n        thrust_wrapper::transform<AMGX_device>(boundary_lists[i].begin(), boundary_lists[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE > (halo_ranges_h[2 * i]), A.manager->L2H_maps[i].begin(), amgx::thrust::plus<INDEX_TYPE > ());\n        halo_lists[i] = &A.manager->L2H_maps[i];\n    }\n\n    cudaCheckError();\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    this->renumber_to_local(A, boundary_lists, halo_lists, my_id, halo_ranges_h, halo_nodes);\n    this->create_halo_matrices(A, 1, boundary_lists, halo_lists);\n\n    if (this->halo_coloring != LAST)\n    {\n        A.manager->getComms()->exchange_matrix_halo(*A.manager->halo_rows, *A.manager->halo_btl, A);\n    }\n\n    A.manager->A = &A;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_matrices(Matrix_d &A, INDEX_TYPE rings, std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists)\n{\n    this->create_maps(A, rings, boundary_lists);\n    this->create_halo_btl(A, halo_lists);\n    this->create_halo_rows(A, halo_lists);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_h &A, INDEX_TYPE my_id, INDEX_TYPE rings)\n{\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DistributedArrangerBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DistributedArranger<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n\n\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <distributed/distributed_arranger.h>\n#include <thrust/sequence.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/functional.h>\n#include <basic_types.h>\n#include <thrust/host_vector.h>\n#include <thrust/scan.h>\n#include <thrust/count.h>\n#include <thrust/sort.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/gather.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <csr_multiply.h>\n#include <algorithm>\n#include <thrust_wrapper.h>\n\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\n\n/***************************************\n * Source Definitions\n ***************************************/\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x) const\n    {\n        return x < 0;\n    }\n};\n\n\nstruct is_non_neg\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x >= 0;\n    }\n};\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid flag_boundary_nodes_P(const IndexType *A_rows, const IndexType *A_cols, int *boundary_flags,  int num_owned_coarse_pts, int A_num_rows, int* nnz_interior)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    int nnz_int = 0;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        // Load A row IDs.\n        int aColIt  = A_rows[aRowId  ];\n        int aColEnd = A_rows[aRowId + 1];\n        int nnz = aColEnd - aColIt;\n        bool halo_found = 0;\n\n        // Iterate over the columns of A.\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) && !halo_found; aColIt += 32 )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n            bool is_halo = (aColIt < aColEnd) && (aColId >= num_owned_coarse_pts);\n\n            // Push coarse and strongly connected nodes in the set.\n            if (utils::any(is_halo))\n            {\n                halo_found = true;\n                break;\n            }\n        }\n\n        if (laneId == 0)\n        {\n            if(halo_found)\n            {\n                boundary_flags[aRowId] = 1;\n            }\n            else\n            {\n                nnz_int += nnz;\n            }\n        }\n    }\n\n    if(laneId == 0)\n    {\n        atomicAdd(nnz_interior, nnz_int);\n    }\n}\n\n\n__global__\nvoid flag_boundary_nodes(int *b2l_maps, int *boundary_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        boundary_flags[b2l_maps[tidx]] = 1;\n    }\n}\n\n__global__\nvoid flag_interior_nodes(int *boundary_flags, int *interior_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        interior_flags[tidx] = !boundary_flags[tidx];\n    }\n}\n\n__global__\nvoid fill_rows_lists(int *__restrict__ interior_rows_lists, int *__restrict__ boundary_rows_lists, const int *__restrict__ interior_flags, const int *__restrict__ boundary_flags, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int bdy_flag = boundary_flags[tidx];\n\n        if (bdy_flag != boundary_flags[tidx + 1]) { boundary_rows_lists[bdy_flag] = tidx; }\n\n        int int_flag = interior_flags[tidx];\n\n        if (int_flag != interior_flags[tidx + 1]) { interior_rows_lists[int_flag] = tidx; }\n    }\n}\n\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid fill_B_B2L_map(const IndexType *maps_offsets, const IndexType *A_b2l_map, const IndexType *row_offsets, IndexType *C_hat_b2l_map, int size, int rank)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < size ; aRowId += gridDim.x * nWarps )\n    {\n        // Load A row IDs.\n        int aColIt  = maps_offsets[aRowId  ];\n        int size = maps_offsets[aRowId + 1] - aColIt;\n        int start = row_offsets[A_b2l_map[aRowId]];\n        int end = start + size;\n\n        // Iterate over the columns of A.\n        for ( start += laneId, aColIt += laneId ; utils::any( start < end ) ; start += 32, aColIt += 32 )\n        {\n            if (start < end)\n            {\n                C_hat_b2l_map[aColIt] = start;\n            }\n        }\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void fill_halo_rows_row_ids(int64_t *halo_rows_row_ids, INDEX_TYPE *halo_rows_local_row_ids, int64_t base_index, INDEX_TYPE num_owned_coarse_pts, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        int64_t gid = halo_rows_row_ids[tidx];\n        halo_rows_local_row_ids[tidx] = (INDEX_TYPE) (gid - base_index);\n\n        if (gid < base_index || gid > base_index + num_owned_coarse_pts)\n        {\n            printf(\"Should not be here in fill_halo_rows_row_ids\\n\");\n        }\n    }\n}\n\n__global__\nvoid fill_local_to_global_map(int *bdy_list, int64_t *local_to_global, int64_t offset, int size )\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        local_to_global[tidx] = bdy_list[tidx] + offset;\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void remove_fine_halo(int64_t *cf_map_global, INDEX_TYPE *flag, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        flag[tidx] = (cf_map_global[tidx] == -1) ? 0 : 1;\n    }\n}\n\ntemplate <typename INDEX_TYPE>\n__global__ void fill_P_local_to_global(int64_t *P_local_to_global, int64_t *cf_map_global, INDEX_TYPE *flag, INDEX_TYPE size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        if (cf_map_global[tidx] != -1)\n        {\n            P_local_to_global[flag[tidx]] = cf_map_global[tidx];\n        }\n    }\n}\n\ntemplate <int coop>\n__global__ void calc_num_neighbors(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < part_offsets[my_id] || col >= part_offsets[my_id + 1])\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_offsets[part] || col >= part_offsets[part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_offsets[part] && col < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n// this is assuming local indices (used in matrix A)\ntemplate <int coop>\n__global__ void calc_num_neighbors_v2_local(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, int64_t *local_to_global_map, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            // Check if column point to halo node\n            if (col >= num_rows)\n            {\n                int64_t gcol = local_to_global_map[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (gcol < part_offsets[part] || gcol >= part_offsets[part + 1]))\n                {\n                    part++;\n                }\n\n                if (part < num_part && (gcol >= part_offsets[part] && gcol < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n// this is assuming global indices (used in halo rows)\ntemplate <int coop>\n__global__ void calc_num_neighbors_v2_global(INDEX_TYPE *row_offsets, int64_t *col_indices, int64_t *part_offsets, INDEX_TYPE *exists, INDEX_TYPE num_part, INDEX_TYPE my_id, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n    int64_t start = part_offsets[my_id];\n    int64_t end = part_offsets[my_id + 1];\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = col_indices[i];\n\n            // Check if column point to halo node\n            if (col < start || col >= end)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_offsets[part] || col >= part_offsets[part + 1]))\n                {\n                    part++;\n                }\n\n                if (part < num_part && (col >= part_offsets[part] && col < part_offsets[part + 1]))\n                {\n                    exists[part] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void flag_halo_nodes(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + col - part_ranges[2 * part]] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop, int set_val>\n__global__ void flag_halo_nodes_local(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t global_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = set_val > 0 ? set_val : -col - 1; // if -1, then set -(local index)-1 to restore later\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n__global__ void flag_halo_nodes_from_local_to_global_map(int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE size, int64_t *local_to_global)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < size)\n    {\n        int64_t global_col = local_to_global[tid];\n        int part = 0;\n\n        while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1]))\n        {\n            part++;\n        }\n\n        if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n        {\n            int col_id = range + tid;\n            flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = -1 - col_id;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void flag_halo_nodes_local_v2(int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE local_to_global_size, int64_t *local_to_global)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < local_to_global_size)\n    {\n        int64_t global_col = local_to_global[tid];\n        int col =  range + tid;\n        // Identify the partition that owns that node\n        int part = 0;\n\n        while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n        // Flag the corresponding node in flags array\n        if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n        {\n            flags[flag_offsets[part] + global_col - part_ranges[2 * part]] = -col - 1;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <int coop>\n__global__ void flag_halo_nodes_global_v2(INDEX_TYPE *row_offsets, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t global_col = global_col_indices[i];\n\n            if (global_col < base || global_col >= base + (int64_t) range)\n            {\n                int part = 0;\n\n                while (part < num_part && (global_col < part_ranges[2 * part] || global_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (global_col >= part_ranges[2 * part] && global_col < part_ranges[2 * part + 1]))\n                {\n                    flags[flag_offsets[part] + (int) (global_col - part_ranges[2 * part])] = 1;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n\n\ntemplate <int coop, int set_val>\n__global__ void flag_halo_nodes_global(INDEX_TYPE *row_offsets, INDEX_TYPE num_rows, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE *local_col_indices)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                // exterior node\n                int part = 0;\n\n                while (part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    // check if the flag is already set (i.e. 1-ring node) then skip it\n                    int flag = flags[flag_offsets[part] + col - part_ranges[2 * part]];\n\n                    if (flag == 0)\n                    {\n                        flags[flag_offsets[part] + col - part_ranges[2 * part]] = set_val;\n                    }\n                    else if (flag < 0)    // it is possible that flag is already set to 1 in this kernel, so if it's negative then it's the 1st ring\n                    {\n                        // update local index, note that local to global mapping is unchanged for 1st ring\n                        local_col_indices[i] = -flags[flag_offsets[part] + col - part_ranges[2 * part]] - 1;\n                    }\n                }\n            }\n            else\n            {\n                // interior node\n                local_col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void renumber_col_indices_global(INDEX_TYPE *row_offsets, int *col_indices, int64_t *global_col_indices, int64_t *part_ranges, INDEX_TYPE *flags, INDEX_TYPE *flag_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                // Need to get his local index from the flag array\n                int part = 0;\n\n                while ( part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int pos_in_flag = flag_offsets[part] + col - part_ranges[2 * part];\n                    //int local_col = flags[pos_in_flag];\n                    col_indices[i] = range + flags[pos_in_flag];\n                    //bool flag = (local_col != flags[pos_in_flag+1]);\n\n                    if (flags[pos_in_flag] == flags[pos_in_flag + 1])\n                    {\n                        printf(\"should not be here in renumber_col_indices_global\\n\");\n                    }\n\n                    //if (flag)\n                }\n            }\n            else\n            {\n                // interior node\n                col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global, int64_t *new_local_to_global, INDEX_TYPE *halo_indices, int halo_base, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t glob_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (glob_col < part_ranges[2 * part] || glob_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (glob_col >= part_ranges[2 * part] && glob_col < part_ranges[2 * part + 1]))\n                {\n                    int pos = glob_col - part_ranges[2 * part];\n                    int actual_col = halo_base + halo_indices[pos];\n                    col_indices[i] = -1 - actual_col;\n                    new_local_to_global[actual_col - num_rows] = glob_col;\n                    // when we renumber we also store mapping to use later in residual restriction since matrix R has other order of halo indices\n                    // store both direct and inverse renumbering for P & R matrices\n                    //halo_P_renumbering[col - num_rows] = actual_col;\n                    //halo_R_renumbering[actual_col - num_rows] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping_v3(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global, int64_t *new_local_to_global, INDEX_TYPE *halo_indices, int halo_base, int rank)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col >= num_rows)\n            {\n                int64_t glob_col = local_to_global[col - num_rows];\n                int part = 0;\n\n                while (part < num_part && (glob_col < part_ranges[2 * part] || glob_col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (glob_col >= part_ranges[2 * part] && glob_col < part_ranges[2 * part + 1]))\n                {\n                    int pos = glob_col - part_ranges[2 * part];\n                    int actual_col = halo_base + halo_indices[pos];\n                    col_indices[i] = -1 - actual_col;\n                    new_local_to_global[actual_col - num_rows] = glob_col;\n                    // when we renumber we also store mapping to use later in residual restriction since matrix R has other order of halo indices\n                    // store both direct and inverse renumbering for P & R matrices\n                    //halo_P_renumbering[col - num_rows] = actual_col;\n                    //halo_R_renumbering[actual_col - num_rows] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void flip_sign_col(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < 0)\n            {\n                col_indices[i] = -col_indices[i] - 1;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void calc_new_halo_mapping_ring2(INDEX_TYPE *row_offsets, INDEX_TYPE num_rows, int64_t *global_col_indices, int64_t *part_ranges, int64_t base, INDEX_TYPE range, INDEX_TYPE num_neighbors, INDEX_TYPE *halo_offsets, INDEX_TYPE *neighbor_offsets, INDEX_TYPE *neighbor_nodes, int64_t *local_to_global, INDEX_TYPE *local_col_indices)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            int64_t col = global_col_indices[i];\n\n            if ((col < base || col >= base + range) && local_col_indices[i] == -1)\n            {\n                // update only 2nd ring halo indices here, 1st ring is updated already\n                int part = 0;\n\n                while (part < num_neighbors && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_neighbors && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int pos = col - part_ranges[2 * part];\n                    local_col_indices[i] = halo_offsets[part] + neighbor_nodes[neighbor_offsets[part] + pos];\n                    local_to_global[local_col_indices[i] - range] = col;\n                }\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop>\n__global__ void renumber_col_indices(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *part_ranges, INDEX_TYPE *halo_renum, INDEX_TYPE *halo_offsets, int64_t base, INDEX_TYPE range, INDEX_TYPE num_part, INDEX_TYPE num_rows, int64_t *local_to_global_map)\n{\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        for (int i = row_offsets[row] + coopIdx; i < row_offsets[row + 1]; i += coop)\n        {\n            INDEX_TYPE col = col_indices[i];\n\n            if (col < base || col >= base + range)\n            {\n                int part = 0;\n\n                while (part < num_part && (col < part_ranges[2 * part] || col >= part_ranges[2 * part + 1])) { part++; }\n\n                if (part < num_part && (col >= part_ranges[2 * part] && col < part_ranges[2 * part + 1]))\n                {\n                    int local_index = halo_renum[halo_offsets[part] + col - part_ranges[2 * part]];\n                    local_to_global_map[local_index - range] = col_indices[i];\n                    col_indices[i] = local_index;\n                }\n            }\n            else\n            {\n                col_indices[i] = col - base;\n            }\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n__global__ void populate_B2L(INDEX_TYPE *indexing, INDEX_TYPE *output, INDEX_TYPE last, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        if (row == size - 1)\n        {\n            if (last) { output[indexing[row]] = row; }\n        }\n        else if (indexing[row] != indexing[row + 1])\n        {\n            output[indexing[row]] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void populate_bdy_list_and_l2h_map(INDEX_TYPE *l2h_map, INDEX_TYPE *halo_nodes, INDEX_TYPE *halo_indices, INDEX_TYPE *bdy_list, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int scanned_id = halo_indices[row];\n\n        if (scanned_id != halo_indices[row + 1])\n        {\n            bdy_list[scanned_id] = row;\n            l2h_map[scanned_id] = -halo_nodes[row] - 1;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n__global__ void populate_B2L_v2(INDEX_TYPE *indexing, INDEX_TYPE *output, INDEX_TYPE offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        if (indexing[row] != indexing[row + 1])\n        {\n            output[indexing[row] - offset] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n\ntemplate <int coop, class T>\n__global__ void export_matrix_elements(INDEX_TYPE *row_offsets, T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, INDEX_TYPE *pointers, T *output, INDEX_TYPE *col_indices, INDEX_TYPE *output2, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = pointers[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += coop)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += coop)\n        {\n            output2[dst_base + m] = col_indices[src_base + m];\n        }\n\n        idx += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void export_matrix_elements_global(INDEX_TYPE *row_offsets, T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, INDEX_TYPE *pointers, T *output, INDEX_TYPE *col_indices, int64_t *output2, INDEX_TYPE size, int64_t *local_to_global, INDEX_TYPE *q, INDEX_TYPE num_owned_pts, int64_t base_index)\n{\n    int idx = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n\n        if (q != NULL)\n        {\n            row = q[row];\n        }\n\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = pointers[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += coop)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += coop)\n        {\n            int col = col_indices[src_base + m];\n\n            if (col < num_owned_pts)\n            {\n                output2[dst_base + m] = (int64_t) col_indices[src_base + m] + base_index;\n            }\n            else\n            {\n                output2[dst_base + m] = local_to_global[col_indices[src_base + m] - num_owned_pts];\n            }\n        }\n\n        idx += gridDim.x * blockDim.x / coop;\n    }\n}\n\ntemplate <class T>\n__global__ void export_matrix_diagonal(T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, T *output, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row;\n        INDEX_TYPE dst_base = idx;\n\n        for (int m = 0; m < bsize; m++)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void write_matrix_rowsize(INDEX_TYPE *maps, INDEX_TYPE *row_offsets, INDEX_TYPE size, INDEX_TYPE *output)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        output[idx] = row_offsets[row + 1] - row_offsets[row];\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void find_next_ring(INDEX_TYPE *maps, INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE size, INDEX_TYPE base, INDEX_TYPE range, INDEX_TYPE *halo_rings)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n\n        for (int col = row_offsets[row]; col < row_offsets[row + 1]; col++)\n        {\n            int colIdx = col_indices[col];\n\n            if (colIdx >= base && colIdx < base + range)\n            {\n                halo_rings[colIdx - base] = 1;\n            }\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void remove_previous_rings(INDEX_TYPE *map, INDEX_TYPE *halo_rings, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = map[idx];\n        halo_rings[row] = 0;\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void renumber_halo_lists(INDEX_TYPE *halo_list, INDEX_TYPE *halo_renum, int64_t base, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        halo_list[row] = halo_renum[halo_list[row] - base];\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void write_ring_rows(INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = indices[row];\n\n        if (idx == 1 && row == 0)\n        {\n            maps[idx - 1] = row;\n        }\n        else if (row > 0 && idx != indices[row - 1])\n        {\n            maps[idx - 1] = row;\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <class T_Config>\nvoid DistributedArrangerBase<T_Config>::set_part_offsets(const INDEX_TYPE num_part, const VecInt_t *part_offsets_h)\n{\n    part_offsets.resize(num_part + 1);\n    amgx::thrust::copy(&part_offsets_h[0], &part_offsets_h[num_part + 1], part_offsets.begin());\n    cudaCheckError();\n    this->num_part =  num_part;\n}\n\n// TODO Make distributed reader use 64bit ints for offsets\ntemplate <class T_Config>\nvoid DistributedArrangerBase<T_Config>::set_part_offsets(const INDEX_TYPE num_part, const int64_t *part_offsets_h)\n{\n    part_offsets.resize(num_part + 1);\n    amgx::thrust::copy(&part_offsets_h[0], &part_offsets_h[num_part + 1], part_offsets.begin());\n    cudaCheckError();\n    this->num_part =  num_part;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t &base_index, INDEX_TYPE &index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    I64Vector_h halo_ranges_h;\n    // initialize base_index, index_range, neighbors, halo_ranges_h\n    this->create_neighbors(A, my_id, base_index, index_range, neighbors, halo_ranges_h);\n    this->create_B2L_from_neighbors(A, my_id, rings, base_index, index_range, neighbors,\n                                    halo_ranges_h, halo_ranges, B2L_maps, L2H_maps, B2L_rings, comms, halo_rows, halo_btl);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_maps(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    int num_neighbors = neighbors.size();\n    this->halo_coloring = (*comms)->halo_coloring;\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n    // initialize halo_btl\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    // initialize halo_rows\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    //this->create_halo_matrices(A, 1, base_index, index_range, my_id, neighbors, B2L_maps, B2L_rings, &(*halo_rows),  &(*halo_btl));\n    if (this->halo_coloring != LAST)\n    {\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_maps(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        Vector<ivec_value_type_h> &neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    this->halo_coloring = (*comms)->halo_coloring;\n    int num_neighbors = neighbors.size();\n    int64_t base_index = 0;\n    int index_range = A.get_num_rows();\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        halo_lists[i] = &L2H_maps[i];\n    }\n\n    /*\n     EXAMPLE\n     Say we want 2 rings of halo. The output of these function for the example partition 1 that at this point looks like:\n    A.num_rows = 4; A.num_nz = 20\n    A.row_offsets = [0 4 11 15 20]\n    A.col_indices = [4 0 1 2\n                   4 5 0 1 2 3 7\n                   0 1 2 3\n                   1 2 3 6 7]\n    num_neighbors=2; neighbors = [0 2]\n    B2L_rings[[0 2][0 2]] B2L_maps [[0 1][1 3]] (Converted from the following input: btl_sizes = [0 2 4] btl_maps = [0 1| 1 3])\n    L2H_maps (and halo_lists) [[4 5][6 7]]\n    halo_rows = NULL, halo_btl=NULL\n    (global indices mapping to local indices: 4-0 5-1 6-2 7-3 2-4 3-5 9-6 10-7)\n     */\n    // initialize ring>1 B2L_maps and B2L_rings\n    this->create_rings(A, rings, num_neighbors, B2L_maps, B2L_rings);\n    /*\n     EXAMPLE\n     having mapped out two rings of halo (already had one), B2L_maps and B2L_rings look like:\n     B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n     */\n    // initialize halo_btl, creating DistributedManager objects for each halo row matrix to be sent to each neighbor, storing B2L and L2H maps\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    /*\n     EXAMPLE\n     We created to two DistributedManager objects for the two neighbors. if halo_coloring != LAST, then the relevant fields are:\n     halo_btl[0] (for neighbor ID 0)\n     global_id = 1; base_index=0; index_range=6 B2L_rings[0] = [0 2 4] B2L_maps[0] = [0 1| 2 3] L2H_maps = [4 5]\n     halo_btl[1] (for neighbor ID 2)\n     global_id = 1; base_index=0; index_range=8 B2L_rings[0] = [0 2 4] B2L_maps[0] = [1 3| 0 2] L2H_maps = [6 7]\n     */\n    // Extract halo rows, ring by ring\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    /*\n     EXAMPLE\n     Extracted two rings of rows for the two neighbors, the two halo row matrices look like:\n     halo_rows[0] (for neighbor ID 0)\n     halo_rows[0].row_offsets = [0 4 11 15 20]\n     halo_rows[0].col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n\n     halo_rows[1] (for neighbor ID 2)\n     halo_rows[1].row_offsets = [0 7 12 16 20]\n     halo_rows[1].col_indices = [4 5 0 1 2 3 7\n                    1 2 3 6 7\n                    4 0 1 2\n                    0 1 2 3]\n      Note, that the ordering of rows corresponds to the list of indices in B2L_maps[i] (and halo_btl[i].B2L_maps[0])\n     */\n    //this->create_halo_matrices(A, 1, base_index, index_range, my_id, neighbors, B2L_maps, B2L_rings, &(*halo_rows),  &(*halo_btl));\n    if (this->halo_coloring != LAST)   //For multiple rings, we still need 0-num_rings-1 rows, just not the last one.\n    {\n        //Set up dummy matrix and dummy manager, and exchange the halo rows\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        I64Vector_d tmp_halo_ranges;\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, tmp_halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n\n    /*\n     EXAMPLE\n     The partitions exchange their halo_rows and halo_btl fields, partition 1 ends up with:\n     halo_btl[0] (received from neighbor ID 0)\n     global_id = 0; base_index=0; index_range=6; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [2 3| 0 1] L2H_maps = [4 5]\n     halo_rows[0].row_offsets = [0 5 13 17 21]\n     halo_rows[0].col_indices = [1 2 3 4 5\n                      0 1 2 3 4 5 6 7\n                      0 1 3 6\n                      0 1 2 3]\n\n     halo_btl[1] (received from neighbor ID 2)\n     global_id = 2; base_index=0; index_range=8; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [1 2| 0 3] L2H_maps = [6 7]\n     halo_rows[1].row_offsets = [0 4 11 16 20]\n     halo_rows[1].col_indices = [7 1 2 3\n                      5 6 7 0 1 2 3\n                      4 5 0 2 3\n                      0 1 2 3]\n     */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_to_local(Matrix_d &A,\n        std::vector<IVector> &boundary_lists,\n        std::vector<IVector *> &halo_lists,\n        INDEX_TYPE my_id,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        IVector &halo_nodes)\n{\n    //IVector &halo_nodes = *halo_nodes_p;\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n    }\n\n    //IVector halo_nodes(total_rows_of_neighbors);\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    int last_halo = halo_nodes[halo_nodes.size() - 1];\n    //create renumbering of halo indices (contiguously neighbor - by - neighbor, indexing starting with the number of owned rows\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin(), size);\n    cudaCheckError();\n    A.manager->local_to_global_map.resize(halo_nodes[halo_nodes.size() - 1] - size + last_halo);\n    renumber_col_indices<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), base_index, index_range, num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n\n    for (int i =  0; i < num_neighbors; i ++)\n    {\n        int size = halo_lists[i]->size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        renumber_halo_lists <<< num_blocks, 128>>>(halo_lists[i]->raw(), halo_nodes.raw() + halo_offsets[i], halo_ranges_h[2 * i], size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_to_local(Matrix_d &A,\n        std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists,\n        INDEX_TYPE my_id, I64Vector_h &halo_ranges_h, IVector &halo_nodes)\n{\n    //IVector &halo_nodes = *halo_nodes_p;\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n    }\n\n    //IVector halo_nodes(total_rows_of_neighbors+1);\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    int last_halo = (halo_nodes.size() > 0) ? halo_nodes[halo_nodes.size() - 1] : 0;\n    //create renumbering of halo indices (contiguously neighbor - by - neighbor, indexing starting with the number of owned rows\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin(), size);\n    cudaCheckError();\n\n    if (halo_nodes.size() > 0)\n    {\n        A.manager->local_to_global_map.resize(halo_nodes[halo_nodes.size() - 1] - size + last_halo);\n    }\n\n    renumber_col_indices<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n\n    for (int i =  0; i < num_neighbors; i ++)\n    {\n        int size = halo_lists[i]->size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        renumber_halo_lists <<< num_blocks, 128>>>(halo_lists[i]->raw(), halo_nodes.raw() + halo_offsets[i], halo_ranges_h[2 * i], size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prepare_local(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &L2H_maps,\n        std::vector<IVector> &boundary_lists,\n        std::vector<IVector *> &halo_lists,\n        DistributedComms<TConfig_d> **comms)\n{\n    int num_neighbors = neighbors.size();\n    (*comms)->set_neighbors(num_neighbors);\n    IVector halo_nodes;\n    // copy halo_ranges_h to device and populate boundary_lists\n    this->create_boundary_lists(A, base_index, index_range, halo_ranges_h, halo_ranges, boundary_lists, halo_nodes);\n    L2H_maps.resize(num_neighbors);\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        L2H_maps[i].resize(boundary_lists[i].size());\n        thrust_wrapper::transform<AMGX_device>(boundary_lists[i].begin(), boundary_lists[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE > (halo_ranges_h[2 * i]), L2H_maps[i].begin(), amgx::thrust::plus<INDEX_TYPE > ());\n        halo_lists[i] = &L2H_maps[i];\n    }\n\n    cudaCheckError();\n    // setting up tmp manager in order to use Comm API\n    Matrix_d tmpA(0, 0, 0, A.getProps());\n    tmpA.set_block_dimx(A.get_block_dimx());\n    tmpA.set_block_dimy(A.get_block_dimy());\n    tmpA.setResources(A.getResources());\n    std::vector<IVector > B2L_maps;\n    std::vector<std::vector<VecInt_t> > B2L_rings;\n    DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n    tmpA.setManager(tmp_manager);\n    tmpA.manager->getComms()->exchange_vectors(boundary_lists, tmpA, 0);\n    this->renumber_to_local(A, boundary_lists, halo_lists, my_id, base_index, index_range, neighbors, halo_ranges_h, halo_ranges, halo_nodes);\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_from_neighbors(Matrix_d &A,\n        INDEX_TYPE my_id, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h,\n        I64Vector_d &halo_ranges,\n        std::vector<IVector > &B2L_maps,\n        std::vector<IVector > &L2H_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        DistributedComms<TConfig_d> **comms,\n        std::vector<Matrix_d > **halo_rows,\n        std::vector<Manager_d > **halo_btl)\n{\n    this->halo_coloring = (*comms)->halo_coloring;\n    std::vector<IVector> boundary_lists;\n    std::vector<IVector *> halo_lists;\n    this->prepare_local(A, my_id, rings, base_index, index_range, neighbors, halo_ranges_h, halo_ranges, L2H_maps, boundary_lists, halo_lists, comms);\n    int num_neighbors = neighbors.size();\n    // initialize BTL_maps and BTl_rings\n    this->create_maps(A, rings, num_neighbors, B2L_maps, B2L_rings, boundary_lists);\n    // initialize halo_btl\n    this->create_halo_btl(A, rings, num_neighbors, neighbors, base_index, index_range, my_id, B2L_maps, B2L_rings, halo_btl, halo_lists);\n    // initialize halo_rows\n    this->create_halo_rows(A, rings, num_neighbors, B2L_maps, B2L_rings, halo_rows, halo_lists);\n\n    if (this->halo_coloring != LAST)\n    {\n        // setting up tmp manager in order to use Comm API\n        Matrix_d tmpA(0, 0, 0, A.getProps());\n        tmpA.set_block_dimx(A.get_block_dimx());\n        tmpA.set_block_dimy(A.get_block_dimy());\n        tmpA.setResources(A.getResources());\n        DistributedManager<TConfig_d> tmp_manager(my_id, base_index, index_range, tmpA, neighbors, halo_ranges, B2L_maps, B2L_rings, comms, NULL, NULL);\n        tmpA.setManager(tmp_manager);\n        tmpA.manager->getComms()->exchange_matrix_halo(**halo_rows, **halo_btl, tmpA);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors(Matrix_d &A, INDEX_TYPE my_id,\n        int64_t &base_index, INDEX_TYPE &index_range,\n        Vector<ivec_value_type_h> &neighbors,\n        I64Vector_h &halo_ranges_h)\n{\n    //if (this->num_part>32) FatalError(\"Matrix slice Level 1 module supports < 32 partitions\", AMGX_ERR_NOT_IMPLEMENTED);\n    if (this->num_part == 0 || this->part_offsets.size() == 0)\n    {\n        FatalError(\"Partitioning scheme is not set\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    IVector part_sizes(this->num_part, 0);\n    IVector inv_neighbors(this->num_part, 0);\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    base_index = this->part_offsets[my_id];\n    index_range = this->part_offsets[my_id + 1] - this->part_offsets[my_id];\n    //\n    // Step 1: flag neighbors that I have an edge to\n    //\n    //go through all the column indices and compare it to index ranges of nodes\n    calc_num_neighbors<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), this->part_offsets.raw(), part_sizes.raw(), this->num_part, my_id, size);\n    cudaCheckError();\n    std::vector<int> neighbor_flags(this->num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    //total number of neighbors\n    int num_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    neighbors.resize(num_neighbors);\n    halo_ranges_h.resize(num_neighbors * 2);\n    //initialize manager\n    int active_part = 0;\n\n    for (int i = 0; i < this->num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = this->part_offsets[i];\n            halo_ranges_h[2 * active_part + 1] = this->part_offsets[i + 1];\n            active_part++;\n        }\n    }\n}\n\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors(Matrix_d &A, INDEX_TYPE my_id,\n        I64Vector_h &halo_ranges_h)\n{\n    if (this->num_part == 0 || this->part_offsets.size() == 0)\n    {\n        FatalError(\"Partitioning scheme is not set\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    IVector part_sizes(this->num_part, 0);\n    IVector inv_neighbors(this->num_part, 0);\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    A.manager->set_base_index(this->part_offsets[my_id]);\n    A.manager->set_index_range(this->part_offsets[my_id + 1] - this->part_offsets[my_id]);\n    //\n    // Step 1: flag neighbors that I have an edge to\n    //\n    //go through all the column indices and compare it to index ranges of nodes\n    calc_num_neighbors<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), this->part_offsets.raw(), part_sizes.raw(), this->num_part, my_id, size);\n    cudaCheckError();\n    std::vector<int> neighbor_flags(this->num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    //total number of neighbors\n    int num_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    A.manager->neighbors.resize(num_neighbors);\n    halo_ranges_h.resize(num_neighbors * 2);\n    //initialize manager\n    int active_part = 0;\n\n    for (int i = 0; i < this->num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            A.manager->neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = this->part_offsets[i];\n            halo_ranges_h[2 * active_part + 1] = this->part_offsets[i + 1];\n            active_part++;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::update_neighbors_list(\n    Matrix_d &A, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, I64Vector_h &part_offsets_h, I64Vector &part_offsets, std::vector<IVector> &halo_rows_row_offsets, std::vector<I64Vector> &halo_rows_col_indices)\n{\n    int num_partitions = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    int num_neighbors = neighbors.size();     // current number of neighbors\n    int total_halo_rows = 0;\n    int total_halo_nnz = 0;\n    IVector neighbor_flags(num_partitions, 0);\n\n    for (int i = 0; i < halo_rows_row_offsets.size(); i++)\n    {\n        int num_halo_rows = halo_rows_row_offsets[i].size() - 1;\n\n        if (num_halo_rows > 0)\n        {\n            total_halo_rows += num_halo_rows;\n            total_halo_nnz += halo_rows_row_offsets[i][num_halo_rows];\n            int num_blocks = std::min(4096, (num_halo_rows + 127) / 128);\n            calc_num_neighbors_v2_global<16> <<< num_blocks, 128>>>(halo_rows_row_offsets[i].raw(), halo_rows_col_indices[i].raw(),\n                    part_offsets.raw(), neighbor_flags.raw(), num_partitions, my_id, num_halo_rows);\n                    cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    IVector_h neighbor_flags_h = neighbor_flags;\n\n    // unset 1-ring neighbors & myself\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neighbor_flags_h[neighbors[i]] = 0;\n    }\n\n    neighbor_flags_h[my_id] = 0;\n    // this will update neighbor list and halo ranges, note that we don't change 1-ring neighbors order\n    append_neighbors(A, neighbors, halo_ranges_h, halo_ranges, neighbor_flags_h, part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::append_neighbors(Matrix_d &A, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, IVector_h &neighbor_flags, I64Vector_h &part_offsets_h)\n{\n    // This function creates arrays neighbors, halo_ranges_h and halo_ranges\n    // base on neighbor_flags\n    // Here do an MPI_allgather to figure out which partitions need data from me\n    // This is required for non-symmetric matrices\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    // pack 0/1 array into array of integers (size/32)\n    int packed_size = (num_part + 32 - 1) / 32;\n    IVector_h packed_nf(packed_size, 0);\n\n    for (int i = 0; i < num_part; i++)\n    {\n        int packed_pos = i / 32;\n        int bit_pos = i % 32;\n        packed_nf[packed_pos] += (neighbor_flags[i] << bit_pos);\n    }\n\n    // exchange packed neighbor flags\n    IVector_h gathered_packed_nf;\n    A.manager->getComms()->all_gather_v(packed_nf, gathered_packed_nf, num_part);\n    // assign neighbors that have edges to me\n    int my_id_pos = my_id / 32;\n    int my_id_bit = my_id % 32;\n\n    for (int i = 0; i < num_part; i++)\n        if (gathered_packed_nf[i * packed_size + my_id_pos] & (1 << my_id_bit)) // check my bit\n        {\n            neighbor_flags[i] = 1;\n        }\n\n    // compute total number of new neighbors\n    int new_neighbors = amgx::thrust::reduce(neighbor_flags.begin(), neighbor_flags.end());\n    cudaCheckError();\n    // save old size\n    int old_neighbors = neighbors.size();\n    neighbors.resize(old_neighbors + new_neighbors);\n    halo_ranges_h.resize(old_neighbors * 2 + new_neighbors * 2);\n    // initialize manager->neighbors and manager->halo_ranges_h for the new nodes\n    int active_part = old_neighbors;\n    this->num_part = num_part;\n\n    for (int i = 0; i < num_part; i++)\n    {\n        if (neighbor_flags[i] > 0)\n        {\n            neighbors[active_part] = i;\n            halo_ranges_h[2 * active_part] = part_offsets_h[i];\n            halo_ranges_h[2 * active_part + 1] = part_offsets_h[i + 1];\n            active_part++;\n        }\n    }\n\n    halo_ranges.resize(old_neighbors * 2 + new_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin() + old_neighbors * 2, halo_ranges_h.end(), halo_ranges.begin() + old_neighbors * 2);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors_v2(Matrix_d &A)\n{\n    // This function counts the number of neighbors, identifies the id of each neighbor,\n    // And creates array halo_ranges, which is the range of rows for each neighbor\n    // Outputs: A.manager->neighbors\n    //          A.manager->halo_ranges_h\n    //          A.manager->halo_ranges\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    A.manager->set_base_index(A.manager->part_offsets_h[my_id]);\n    A.manager->set_index_range(A.manager->part_offsets_h[my_id + 1] - A.manager->part_offsets_h[my_id]);\n    IVector inv_neighbors(num_part, 0);\n    IVector part_sizes(num_part, 0);\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        A.manager->neighbors.resize(0);\n    }\n\n    // TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    // -----------------------------------------------\n    // Step 1: flag neighbors that I have an edge to\n    // -----------------------------------------------\n    //go through all the column indices and check if they are halo vertices\n    if (num_blocks != 0)\n    {\n        calc_num_neighbors_v2_local<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->part_offsets.raw(), part_sizes.raw(), A.manager->local_to_global_map.raw(), num_part, my_id, size);\n        cudaCheckError();\n    }\n\n    // D2H copy\n    IVector_h neighbor_flags(num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    cudaCheckError();\n    append_neighbors(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, neighbor_flags, A.manager->part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_neighbors_v2_global(Matrix_d &A, I64Vector_d &A_col_indices_global)\n{\n    // This function counts the number of neighbors, identifies the id of each neighbor,\n    // And creates array halo_ranges, which is the range of rows for each neighbor\n    // Outputs: A.manager->neighbors\n    //          A.manager->halo_ranges_h\n    //          A.manager->halo_ranges\n    int num_part = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    IVector part_sizes(num_part, 0);\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        A.manager->neighbors.resize(0);\n    }\n\n    // TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    // -----------------------------------------------\n    // Step 1: flag neighbors that I have an edge to\n    // -----------------------------------------------\n    //go through all the column indices and check if they are halo vertices\n    if (num_blocks != 0)\n    {\n        calc_num_neighbors_v2_global<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A_col_indices_global.raw(), A.manager->part_offsets.raw(), part_sizes.raw(), num_part, my_id, size);\n        cudaCheckError();\n    }\n\n    // D2H copy\n    IVector_h neighbor_flags(num_part);\n    amgx::thrust::copy(part_sizes.begin(), part_sizes.end(), neighbor_flags.begin());\n    cudaCheckError();\n    append_neighbors(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, neighbor_flags, A.manager->part_offsets_h);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L_one_ring(Matrix_d &A)\n{\n    std::vector<IVector> boundary_lists;\n    // create my boundary lists, renumber halos, create L2H maps\n    create_boundary_lists_v2(A, boundary_lists);\n    // compute halo offsets\n    int neighbors = A.manager->num_neighbors();\n    A.manager->halo_offsets.resize(neighbors + 1, 0);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->halo_offsets[i] = boundary_lists[i].size();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(A.manager->halo_offsets.begin(), A.manager->halo_offsets.end(), A.manager->halo_offsets.begin(), A.get_num_rows());\n    cudaCheckError();\n    // receive neighbors boundary lists for b2l maps\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    // copy boundary lists to B2L maps\n    // release internal boundary lists array\n    A.manager->B2L_maps.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_maps[i] = boundary_lists[i];\n    }\n\n    A.manager->B2L_rings.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_rings[i].resize(2);\n        A.manager->B2L_rings[i][0] = 0;\n        A.manager->B2L_rings[i][1] = A.manager->B2L_maps[i].size();\n    }\n\n    this->initialize_B2L_maps_offsets(A, 1);\n    A.manager->A = &A;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createRowsLists(Matrix_d &A, bool is_matrix_P)\n{\n    int A_num_rows, offset;\n    A.getOffsetAndSizeForView(OWNED, &offset, &A_num_rows);\n    IVector boundary_flags(A_num_rows + 1, 0);\n    IVector interior_flags(A_num_rows + 1, 0);\n\n    int num_nz_interior;\n    if (!is_matrix_P)\n    {\n        for (int i = 0; i < A.manager->neighbors.size(); i++)\n        {\n            int size = A.manager->B2L_rings[i][1];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n\n            if ( size > 0)\n            {\n                flag_boundary_nodes <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), boundary_flags.raw(), size);\n                cudaCheckError();\n            }\n        }\n    }\n    else\n    {\n        const int cta_size = 256;\n        const int nWarps = cta_size / 32;\n        int grid_size = std::min( 4096, (int) (A_num_rows + nWarps - 1) / nWarps);\n\n        if (A_num_rows > 0)\n        {\n            IVector nnz_interior(1, 0);\n            flag_boundary_nodes_P<index_type, cta_size> <<< grid_size, cta_size>>>(A.row_offsets.raw(), A.col_indices.raw(), boundary_flags.raw(), A.manager->halo_offsets[0], A_num_rows, nnz_interior.raw());\n            cudaCheckError();\n            hipMemcpy(&num_nz_interior, nnz_interior.raw(), sizeof(int), hipMemcpyDefault);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    int size = A_num_rows;\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    if (size > 0)\n    {\n        flag_interior_nodes <<< num_blocks, 128>>>(boundary_flags.raw(), interior_flags.raw(), size);\n        cudaCheckError();\n    }\n\n    if (boundary_flags.size() > 1)\n    {\n        thrust_wrapper::exclusive_scan<AMGX_device>(boundary_flags.begin(), boundary_flags.end(), boundary_flags.begin());\n        cudaCheckError();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(interior_flags.begin(), interior_flags.end(), interior_flags.begin());\n    cudaCheckError();\n    int num_interior_rows = interior_flags[size];\n    int num_boundary_rows = boundary_flags[size];\n    A.manager->interior_rows_list.resize(num_interior_rows);\n    A.manager->boundary_rows_list.resize(num_boundary_rows);\n\n    // Create the list of interior and boundary nodes, using the interior_flag and boundary_flag\n    if (size > 0)\n    {\n        fill_rows_lists <<< num_blocks, 128>>>(A.manager->interior_rows_list.raw(), A.manager->boundary_rows_list.raw(), interior_flags.raw(), boundary_flags.raw(), size);\n        cudaCheckError();\n    }\n\n    // Manually set the view sizes for P\n    if(is_matrix_P)\n    {\n        int num_rows_owned = num_interior_rows + num_boundary_rows;\n        int num_nz_owned = A.row_offsets[num_rows_owned];\n        A.manager->setViewSizes(num_interior_rows, num_nz_interior, num_rows_owned, num_nz_owned, num_rows_owned, num_nz_owned, A.get_num_rows(), A.get_num_nz());\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists_v3(Matrix_d &A)\n{\n    // -----------------------------------------------\n    // Flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    // -----------------------------------------------\n    int A_num_rows = A.get_num_rows();\n\n    if (A_num_rows == 0)\n    {\n        return;\n    }\n\n    int num_neighbors = A.manager->neighbors.size();\n    // compute halo offsets\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows_neighbor = A.manager->halo_ranges_h[2 * i + 1] - A.manager->halo_ranges_h[2 * i];\n        total_rows_of_neighbors += num_rows_neighbor;\n        halo_offsets[i + 1] = halo_offsets[i] + num_rows_neighbor;\n        max_neighbor_size = max_neighbor_size > num_rows_neighbor ? max_neighbor_size : num_rows_neighbor;;\n    }\n\n    // copy offsets to device\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    // flag neighbors nodes that I need\n    IVector halo_nodes(total_rows_of_neighbors + 1);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    int size = A.manager->local_to_global_map.size();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n\n    if (size > 0)\n    {\n        flag_halo_nodes_from_local_to_global_map <<< num_blocks, 128>>>(A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n        cudaCheckError();\n    }\n\n    IVector halo_indices(max_neighbor_size + 1);\n    A.manager->L2H_maps.resize(num_neighbors);\n    A.manager->B2L_maps.resize(num_neighbors);\n    is_less_than_zero pred;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // compute halo indices map [<neighbor inner node>] = 0-based halo number\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        amgx::thrust::replace_copy_if(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin(), pred, 1);\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_indices.begin(), halo_indices.begin() + size + 1, halo_indices.begin());\n        cudaCheckError();\n        int num_halo = halo_indices[size];\n        A.manager->L2H_maps[i].resize(num_halo);\n        A.manager->B2L_maps[i].resize(num_halo);\n        // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n        // For each node in B2L_map, need to find corresponding local id and store in L2H_map\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        populate_bdy_list_and_l2h_map <<< num_blocks, 128>>>(A.manager->L2H_maps[i].raw(),  halo_nodes.raw() + halo_offsets[i], halo_indices.raw(), A.manager->B2L_maps[i].raw(), size);\n        cudaCheckError();\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_B2L_maps_offsets(Matrix_d &A, int num_rings)\n{\n    // Compute the total number of bdy rows in each ring\n    std::vector<IVector_h> B2L_maps_offsets_h(num_rings);\n    A.manager->B2L_rings_sizes.resize(num_rings);\n    int num_neighbors = A.manager->neighbors.size();\n\n    for (int k = 0; k < num_rings; k++)\n    {\n        B2L_maps_offsets_h[k].resize(num_neighbors + 1);;\n        B2L_maps_offsets_h[k][0] = 0;\n\n        for (int j = 0; j < num_neighbors; j++)\n        {\n            B2L_maps_offsets_h[k][j + 1] = B2L_maps_offsets_h[k][j] + A.manager->B2L_rings[j][k + 1];\n        }\n\n        A.manager->B2L_rings_sizes[k] = B2L_maps_offsets_h[k][num_neighbors];\n    }\n\n    // Copy maps_offsets to device\n    A.manager->B2L_maps_offsets.resize(num_rings);\n\n    for (int i = 0; i < num_rings; i++)\n    {\n        A.manager->B2L_maps_offsets[i] = B2L_maps_offsets_h[i];\n    }\n\n    // Store the B2L_maps ptrs on the device\n    std::vector<int *> B2L_maps_ptrs_h(num_neighbors);\n\n    for (int j = 0; j < num_neighbors; j++)\n    {\n        B2L_maps_ptrs_h[j] = A.manager->B2L_maps[j].raw();\n    }\n\n    A.manager->B2L_maps_ptrs = B2L_maps_ptrs_h;\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_one_ring_halo_rows(Matrix_d &A)\n{\n    // what it does:\n    //   appends halo rows to matrix A\n    //   creates new B2L_rings, B2L_maps and L2H_maps\n    // input:\n    //   matrix A and 1-ring B2L_maps\n    int num_partitions = A.manager->get_num_partitions();\n    int my_id = A.manager->global_id();\n    int num_neighbors = A.manager->B2L_maps.size();\n    int num_ring1_neighbors = num_neighbors;\n    int num_ring1_indices = A.manager->local_to_global_map.size();\n    std::vector<IVector> halo_row_offsets(num_neighbors);\n    std::vector<I64Vector> halo_global_indices(num_neighbors);\n    std::vector<IVector> halo_local_indices(num_neighbors);\n    std::vector<MVector> halo_values(num_neighbors);\n    // step 1: setup halo rows with global indices\n    create_halo_rows_global_indices(A, halo_row_offsets, halo_global_indices, halo_values);\n    // step 2: exchange halo rows\n    std::vector<I64Vector> dummy_halo_ids(0);\n    A.manager->getComms()->exchange_matrix_halo(halo_row_offsets, halo_global_indices, halo_values, dummy_halo_ids, A.manager->neighbors, A.manager->global_id());\n    // step 3: append the list  f neighbors with the new ring-2 neighbors\n    update_neighbors_list(A, A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, A.manager->part_offsets_h, A.manager->part_offsets, halo_row_offsets, halo_global_indices);\n    num_neighbors = A.manager->neighbors.size();\n    // step 4: mark neighbor nodes, create local halo indices for the 2nd ring\n    // this function also creates boundary lists for the 2nd ring if boundary lists array size > 0\n    std::vector<IVector> boundary_lists(num_neighbors);\n    compute_local_halo_indices(A.row_offsets, A.col_indices, halo_row_offsets, halo_global_indices, halo_local_indices, A.manager->local_to_global_map, boundary_lists,  A.manager->neighbors, A.manager->halo_ranges_h, A.manager->halo_ranges, A.manager->halo_offsets, A.manager->base_index(), A.manager->index_range(), A.get_num_rows(), 1);\n    // update renumbering arrays (set identity for 2nd ring)\n    //A.manager->halo_P_renumbering.resize(A.manager->local_to_global_map.size());\n    //A.manager->halo_R_renumbering.resize(A.manager->local_to_global_map.size());\n    //thrust_wrapper::sequence<AMGX_device>(A.manager->halo_P_renumbering.begin() + num_ring1_indices, A.manager->halo_P_renumbering.end(), num_ring1_indices + A.get_num_rows());\n    //thrust_wrapper::sequence<AMGX_device>(A.manager->halo_R_renumbering.begin() + num_ring1_indices, A.manager->halo_R_renumbering.end(), num_ring1_indices + A.get_num_rows());\n    // step 5: update L2H maps = identity\n    A.manager->getComms()->set_neighbors(A.manager->neighbors.size());\n    // step 6: receive neighbors boundary lists for 2-ring b2l maps\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    // step 7: update B2L rings & B2L maps\n    int rings = 2;\n    A.manager->B2L_rings.resize(num_neighbors);\n    A.manager->B2L_maps.resize(num_neighbors);\n    // modify 1-ring where necessary\n    int ring = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // set size to 0 for 2-ring neighbors only\n        if (i >= num_ring1_neighbors)\n        {\n            A.manager->B2L_maps[i].resize(0);\n        }\n\n        A.manager->B2L_rings[i].resize(rings + 1);\n        A.manager->B2L_rings[i][0] = 0;\n        A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_maps[i].size();\n    }\n\n    // fill up 2-ring maps\n    ring = 1;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // append 2nd ring\n        int ring1_size = A.manager->B2L_maps[i].size();\n        A.manager->B2L_maps[i].resize(ring1_size + boundary_lists[i].size());\n        amgx::thrust::copy(boundary_lists[i].begin(), boundary_lists[i].end(), A.manager->B2L_maps[i].begin() + ring1_size);\n        A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_maps[i].size();\n    }\n\n    cudaCheckError();\n    this->initialize_B2L_maps_offsets(A, 2);\n    // step 8: append halo_rows to matrix A\n    // compute new # of rows & nnz\n    this->append_halo_rows(A, halo_row_offsets, halo_local_indices, halo_values);\n    // initialize the manager\n    A.manager->set_initialized(A.row_offsets);\n    // Compute the diagonal\n    // TODO: Should only compute diagonal of 1-ring halo rows\n    ViewType oldView = A.currentView();\n    A.setView(FULL);\n    A.set_allow_recompute_diag(true);\n    A.computeDiagonal();\n    A.setView(oldView);\n    // the following steps are necessary only for latency hiding/renumbering, i.e. to use reorder_matrix()\n#if 0\n    // step 6: for 2 rings: setup halo_btl\n    create_halo_btl_multiple_rings(A, 2);\n#endif\n}\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists(Matrix_d &A,\n        int64_t base_index, INDEX_TYPE index_range,\n        I64Vector_h &halo_ranges_h, I64Vector_d &halo_ranges,\n        std::vector<IVector> &boundary_lists, IVector &halo_nodes)\n{\n    //\n    // Step 2: flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    //\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    halo_ranges.resize(num_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin(), halo_ranges_h.end(), halo_ranges.begin());\n    cudaCheckError();\n    boundary_lists.resize(num_neighbors);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);;\n    }\n\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    halo_nodes.resize(total_rows_of_neighbors);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), base_index, index_range, num_neighbors, size);\n    cudaCheckError();\n    //\n    // Step 3: Write the 1-ring halo indices for each neighbor\n    //\n    IVector halo_indices(max_neighbor_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        int num_halo = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last;\n        //A.manager.B2L_maps[i].resize(num_halo);\n        boundary_lists[i].resize(num_halo);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        //int num_blocks=min(4096,(size+127)/128);\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(),/*A.manager.B2L_maps[i].raw(),*/ last, size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists(Matrix_d &A,\n        I64Vector_h &halo_ranges_h,\n        std::vector<IVector> &boundary_lists, IVector &halo_nodes)\n{\n    //\n    // Step 2: flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    //\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = halo_ranges_h.size() / 2;\n    A.manager->halo_ranges.resize(num_neighbors * 2);\n    amgx::thrust::copy(halo_ranges_h.begin(), halo_ranges_h.end(), A.manager->halo_ranges.begin());\n    cudaCheckError();\n    boundary_lists.resize(num_neighbors);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        halo_offsets[i + 1] = halo_offsets[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);;\n    }\n\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    halo_nodes.resize(total_rows_of_neighbors);;\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size);\n    cudaCheckError();\n    //\n    // Step 3: Write the 1-ring halo indices for each neighbor\n    //\n    IVector halo_indices(max_neighbor_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        int num_halo = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last;\n        //A.manager.B2L_maps[i].resize(num_halo);\n        boundary_lists[i].resize(num_halo);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        //int num_blocks=min(4096,(size+127)/128);\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(),/*A.manager.B2L_maps[i].raw(),*/ last, size);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_boundary_lists_v2(Matrix_d &A, std::vector<IVector> &boundary_lists)\n{\n    // -----------------------------------------------\n    // Flag all nodes in neighbors that I will need (that is I have an edge pointing to it)\n    // -----------------------------------------------\n    int size = A.get_num_rows();\n\n    if (size == 0)\n    {\n        return;\n    }\n\n    //TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = A.manager->neighbors.size();\n    boundary_lists.resize(num_neighbors);\n    // compute halo offsets\n    int total_rows_of_neighbors = 0;\n    std::vector<int> halo_offsets(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows_neighbor = A.manager->halo_ranges_h[2 * i + 1] - A.manager->halo_ranges_h[2 * i];\n        total_rows_of_neighbors += num_rows_neighbor;\n        halo_offsets[i + 1] = halo_offsets[i] + num_rows_neighbor;\n        max_neighbor_size = max_neighbor_size > num_rows_neighbor ? max_neighbor_size : num_rows_neighbor;;\n    }\n\n    // copy offsets to device\n    IVector halo_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    // flag neighbors nodes that I need\n    IVector halo_nodes(total_rows_of_neighbors);\n    amgx::thrust::fill(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    flag_halo_nodes_local<16, 1> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw(), halo_nodes.raw(), halo_offsets_d.raw(), A.manager->base_index(), A.manager->index_range(), num_neighbors, size, A.manager->local_to_global_map.raw());\n    cudaCheckError();\n    // store renumbering so later can use on P\n    //A.manager->halo_P_renumbering.resize(A.manager->local_to_global_map.size());\n    //A.manager->halo_R_renumbering.resize(A.manager->local_to_global_map.size());\n    // we need to update local toglobal maps as well because we will be renumbering halo nodes\n    I64Vector new_local_to_global_map(A.manager->local_to_global_map.size(), -1);\n    // compute new local halo indices for each neighbor node\n    // renumbering: compute new column indices sorted by global index\n    IVector halo_indices(max_neighbor_size);\n    std::vector<int> num_halo(num_neighbors);\n    std::vector<int> last(num_neighbors);\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n    A.manager->L2H_maps.resize(num_neighbors);\n    int halo_base = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // compute halo indices map [<neighbor inner node>] = 0-based halo number\n        last[i] = halo_nodes[halo_offsets[i + 1] - 1];\n        thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin() + halo_offsets[i], halo_nodes.begin() + halo_offsets[i + 1], halo_indices.begin());\n        num_halo[i] = halo_indices[halo_offsets[i + 1] - halo_offsets[i] - 1] + last[i];\n        // renumber my columns for this particular neighbor\n        calc_new_halo_mapping<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), A.manager->halo_ranges.raw() + (int64_t) (2 * i), 1, size, A.manager->local_to_global_map.raw(), new_local_to_global_map.raw(), halo_indices.raw(), halo_base, A.manager->global_id());\n        cudaCheckError();\n        //calc_new_halo_mapping_v2<16><<<num_blocks, 128>>>( A.manager->halo_ranges.raw() + (int64_t) (2*i), 1, size, A.manager->local_to_global_map.raw(), new_local_to_global_map.raw(), halo_indices.raw(), halo_base, A.manager->local_to_global_map.size(),A.manager->global_id());\n        cudaCheckError();\n        // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n        boundary_lists[i].resize(num_halo[i]);\n        int size = halo_offsets[i + 1] - halo_offsets[i];\n        populate_B2L <<< num_blocks, 128>>>(halo_indices.raw(), boundary_lists[i].raw(), last[i], size);\n        cudaCheckError();\n        // identity L2H\n        A.manager->L2H_maps[i].resize(num_halo[i]);\n        thrust_wrapper::sequence<AMGX_device>(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), halo_base);\n        halo_lists[i] = &A.manager->L2H_maps[i];\n        // continous numbering for halo indices\n        halo_base += num_halo[i];\n    }\n\n    cudaCheckError();\n    // Now change the negative col to positive\n    flip_sign_col<16> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.col_indices.raw(), size);\n    cudaCheckError();\n    // copy new local to global mapping\n    amgx::thrust::copy(new_local_to_global_map.begin(), new_local_to_global_map.end(), A.manager->local_to_global_map.begin());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_local_halo_indices( IVector &A_row_offsets, IVector &A_col_indices, std::vector<IVector> &halo_row_offsets, std::vector<I64Vector> &halo_global_indices,\n        std::vector<IVector> &halo_local_indices, I64Vector &local_to_global, std::vector<IVector> &boundary_lists, IVector_h &neighbors, I64Vector_h &halo_ranges_h, I64Vector &halo_ranges, IVector_h &halo_offsets, int64_t base_index, int64_t index_range, int A_num_rows, int current_num_rings)\n{\n    // This function checks the halo_col_indices received from the neighbors, and identifies\n    // new halo_indices and  updates halo_offsets, local_to_global_map accordingly\n    // input: halo row offsets,\n    //        halo global column indices\n    //        current local to global map\n    //        neighbors (new discovered neighbors should already be included)\n    //        halo_ranges for the neighbors\n    //\n    // output: halo offsets,\n    //         halo local column indices,\n    //         updated local to global map (in place)\n    int size = A_num_rows;\n    //TODO: Are these the optimal block_sizes?\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int num_neighbors = neighbors.size();\n    // compute neighbor offsets & total number of neighbor rows\n    int total_rows_of_neighbors = 0;\n    std::vector<int> neighbor_offsets_h(num_neighbors + 1, 0);\n    int max_neighbor_size = 0;\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        total_rows_of_neighbors += halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        neighbor_offsets_h[i + 1] = neighbor_offsets_h[i] + halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i];\n        max_neighbor_size = max_neighbor_size > (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]) ? max_neighbor_size : (halo_ranges_h[2 * i + 1] - halo_ranges_h[2 * i]);\n    }\n\n    // copy offsets to device\n    IVector neighbor_offsets(num_neighbors + 1);\n    amgx::thrust::copy(neighbor_offsets_h.begin(), neighbor_offsets_h.end(), neighbor_offsets.begin());\n    // store flags for all neighbor nodes\n    IVector neighbor_nodes(total_rows_of_neighbors);\n    // flag neighbor nodes that are in the existing rings as -(local_index)-1\n    thrust_wrapper::fill<AMGX_device>(neighbor_nodes.begin(), neighbor_nodes.end(), 0);\n    cudaCheckError();\n    int local_to_global_size = local_to_global.size();\n    int num_blocks2 = std::min(4096, (local_to_global_size + 127) / 128);\n\n    if (local_to_global_size != 0)\n    {\n        flag_halo_nodes_local_v2 <<< num_blocks2, 128>>>(halo_ranges.raw(), neighbor_nodes.raw(), neighbor_offsets.raw(), base_index, index_range, num_neighbors, local_to_global_size, local_to_global.raw());\n        cudaCheckError();\n    }\n\n    // 1) flag NEW neighbors nodes that I need as 1, they will be in the 2nd ring\n    // 2) fill out local indices for 1st ring & internal indices\n    int num_halos = halo_row_offsets.size();\n\n    for (int i = 0; i < num_halos; i++)\n    {\n        int size = halo_global_indices[i].size();\n\n        if (size > 0)\n        {\n            halo_local_indices[i].resize(size);\n            thrust_wrapper::fill<AMGX_device>(halo_local_indices[i].begin(), halo_local_indices[i].end(), -1); // fill with -1\n            // TODO: launch only on halo rows\n            flag_halo_nodes_global<16, 1> <<< num_blocks, 128>>>(halo_row_offsets[i].raw(), halo_row_offsets[i].size() - 1, halo_global_indices[i].raw(), halo_ranges.raw(), neighbor_nodes.raw(), neighbor_offsets.raw(), base_index, index_range, num_neighbors, halo_local_indices[i].raw());\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    // replace all negative values with 0 in neighbor flags\n    is_less_than_zero pred;\n    amgx::thrust::replace_if(neighbor_nodes.begin(), neighbor_nodes.end(), pred, 0);\n    cudaCheckError();\n    // fill halo offsets for the current number of  ring for new neighbors (it will be of size 0)\n    int current_num_neighbors = (halo_offsets.size() - 1) / current_num_rings;;\n    int num_rings = current_num_rings + 1;\n    IVector_h new_halo_offsets(num_rings * num_neighbors + 1);\n\n    for (int j = 0; j < current_num_rings; j++)\n    {\n        for (int i = 0 ; i <= current_num_neighbors; i++)\n        {\n            new_halo_offsets[num_neighbors * j + i] = halo_offsets[current_num_neighbors * j + i];\n        }\n\n        for (int i = current_num_neighbors; i < num_neighbors; i++)\n        {\n            new_halo_offsets[num_neighbors * j + i + 1] = new_halo_offsets[num_neighbors * j + i];\n        }\n    }\n\n    halo_offsets = new_halo_offsets;\n    int ring = current_num_rings;\n    int current_num_halo_indices = local_to_global.size();\n    //int halo_base = index_range + current_num_halo_indices;\n    cudaCheckError();\n\n    // compute neighbors nodes indices (in-place) for each neighbor\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int last_node, num_halo;\n\n        if (neighbor_offsets_h[i + 1] != neighbor_offsets_h[i])\n        {\n            last_node = neighbor_nodes[neighbor_offsets_h[i + 1] - 1];\n            thrust_wrapper::exclusive_scan<AMGX_device>(neighbor_nodes.begin() + neighbor_offsets_h[i], neighbor_nodes.begin() + neighbor_offsets_h[i + 1], neighbor_nodes.begin() + neighbor_offsets_h[i]);\n            num_halo = neighbor_nodes[neighbor_offsets_h[i + 1] - 1] + last_node;\n        }\n        else\n        {\n            num_halo = 0;\n            last_node = 0;\n        }\n\n        // update halo offsets (L2H)\n        halo_offsets[ring * num_neighbors + i + 1] = halo_offsets[ring * num_neighbors + i] + num_halo;\n\n        // if size = 0 then we don't need to compute it\n        if (boundary_lists.size() > 0)\n        {\n            // create my boundary lists = list of neighbor inner nodes corresponding to halo numbers 0..M\n            // basically this will be our 2-ring B2L_maps\n            boundary_lists[i].resize(num_halo);\n            int size = neighbor_offsets_h[i + 1] - neighbor_offsets_h[i];\n            num_blocks = std::min(4096, (size + 127) / 128);\n\n            if (size > 0)\n            {\n                populate_B2L <<< num_blocks, 128>>>(neighbor_nodes.raw() + neighbor_offsets_h[i], boundary_lists[i].raw(), last_node, size);\n                cudaCheckError();\n            }\n        }\n    }\n\n    cudaCheckError();\n    // compute local indices and new local to global mapping\n    int new_num_halo_indices = halo_offsets[num_rings * num_neighbors] - halo_offsets[current_num_rings * num_neighbors];\n    local_to_global.resize(current_num_halo_indices + new_num_halo_indices);\n    IVector halo_offsets_d(halo_offsets.size());\n    amgx::thrust::copy(halo_offsets.begin(), halo_offsets.end(), halo_offsets_d.begin());\n    cudaCheckError();\n\n    // do this for all ring-1 neighbors\n    for (int i = 0; i < num_halos; i++)\n    {\n        int num_neighbor_rows = halo_row_offsets[i].size() - 1;\n        num_blocks = std::min(4096, (num_neighbor_rows + 127) / 128);\n\n        if (num_blocks > 0)\n        {\n            calc_new_halo_mapping_ring2<16> <<< num_blocks, 128>>>(\n                halo_row_offsets[i].raw(), num_neighbor_rows, halo_global_indices[i].raw(),                     // halo rows to process\n                halo_ranges.raw(), base_index, index_range, num_neighbors,                          // ranges and # of neighbors\n                halo_offsets_d.raw() + current_num_rings * num_neighbors, neighbor_offsets.raw(), neighbor_nodes.raw(),     // halo offsets, neighbor offsets and indices\n                local_to_global.raw(), halo_local_indices[i].raw());                                    // output\n                cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_rings(Matrix_d &A, INDEX_TYPE rings,\n        INDEX_TYPE neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    B2L_rings.resize(neighbors);\n\n    for (int i = 0; i < neighbors; i ++)\n    {\n        B2L_rings[i].resize(rings + 1);\n        B2L_rings[i][0] = 0;\n    }\n\n    /*\n     EXAMPLE\n     For example matrix. partition 1, the matrix look like:\n     A.row_offsets = [0 4 11 15 20]\n     A.col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n     num_neighbors=2; neighbors = [0 2]\n     B2L_rings[[0 2][0 2]] B2L_maps [[0 1][1 3]] (Converted from the following input: btl_sizes = [0 2 4] btl_maps = [0 1| 1 3])\n     L2H_maps (and halo_lists) [[4 5][6 7]]\n     and we want to map further halo rings\n     */\n    //Scratch space computation\n    for (int i = 0; i < neighbors; i++)\n    {\n        B2L_rings[i][1] = B2L_maps[i].size();\n    }\n\n    //flag array\n    IVector halo_rings(A.get_num_rows());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // For each ring: flag nodes that are neighbors of the previous ring, then remove the nodes of the previous rings from the marking\n        // count the number of marked nodes, and append their row indices to the B2L_maps\n        //\n        for (int ring = 1; ring < rings; ring++)\n        {\n            size = B2L_rings[i][ring] - B2L_rings[i][ring - 1];\n            thrust_wrapper::fill<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, 0);\n            num_blocks = std::min(4096, (size + 127) / 128);\n            //Set 1 in flag array for any column index appreaing in the rows that belong to the previous ring\n            find_next_ring <<< num_blocks, 128>>>(B2L_maps[i].raw() + B2L_rings[i][ring - 1], A.row_offsets.raw(), A.col_indices.raw(), size, 0/*base_index*/, /*index_range*/size, halo_rings.raw());\n            cudaCheckError();\n            /*\n            EXAMPLE\n            for neighbor ID 0, halo_rings array initially [0 0 0 0], find_next_ring look at rows in the first ring [0 1] and sets halo_rings array positions to 1 for each column index in those rows:\n            halo_rings: [1 1 1 1]\n             */\n            //Remove all flags that belong to rows in any previous rings\n            remove_previous_rings <<< num_blocks, 128>>>(B2L_maps[i].raw(), halo_rings.raw(), B2L_rings[i][ring]);\n            cudaCheckError();\n            /*\n             EXAMPLE\n             for neighbor ID 0, halo_rings array [1 1 1 1], remove_previous_rings sets halo_rings positions to 0 for each index in previous B2L rings ([0 1] in this case):\n             halo_rings: [0 0 1 1]\n             */\n            //Count, index and append them to B2L_maps and update B2L_rings\n            thrust_wrapper::inclusive_scan<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, halo_rings.begin());\n            B2L_rings[i][ring + 1] = B2L_rings[i][ring] + halo_rings[size];\n            B2L_maps[i].resize(B2L_rings[i][ring + 1]);\n\n            /*\n             EXAMPLE\n             after inclusive scan: [0 0 1 2], B2L_rings[0][2] = 2+2, and B2L_maps[0] gets resized to 4\n             */\n            if (B2L_rings[i][ring] == B2L_rings[i][ring + 1])\n            {\n                for (int fill = ring; fill <= rings; fill++) { B2L_rings[i][fill] = B2L_rings[i][ring]; }\n\n                break;\n            }\n\n            num_blocks = std::min(4096, (A.get_num_rows() + 127) / 128);\n            write_ring_rows <<< num_blocks, 128>>>(halo_rings.raw(), B2L_maps[i].raw() + B2L_rings[i][ring], A.get_num_rows());\n            cudaCheckError();\n            /*\n             EXAMPLE\n             appends positions i to B2L_maps, where halo_rings[i-1] != halo_rings[i]\n             B2L_maps[0] = [0 1| 2 3] with B2L_rings[0] = [0 2 4]\n             */\n        }\n\n        cudaCheckError();\n    }\n}\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_rings(Matrix_d &A, INDEX_TYPE rings)\n{\n    int neighbors = A.manager->num_neighbors();\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    A.manager->B2L_rings.resize(neighbors * rings + 1);\n\n    for (int i = 0; i < neighbors; i ++)\n    {\n        A.manager->B2L_rings[i].resize(rings + 1);\n        A.manager->B2L_rings[i][0] = 0;\n    }\n\n    //Scratch space computation\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_rings[i][1] = A.manager->B2L_maps[i].size();\n    }\n\n    IVector halo_rings(A.get_num_rows());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // For each ring: flag nodes that are neighbors of the previous ring, then remove the nodes of the previous ring from the marking\n        // count the number of marked nodes, and append their row indices to the B2L_maps\n        //\n        for (int ring = 1; ring < rings; ring++)\n        {\n            size = A.manager->B2L_rings[i][ring] - A.manager->B2L_rings[i][ring - 1];\n            thrust_wrapper::fill<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, 0);\n            num_blocks = std::min(4096, (size + 127) / 128);\n            find_next_ring <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw() + A.manager->B2L_rings[i][ring - 1], A.row_offsets.raw(), A.col_indices.raw(), size, 0/*base_index*/, /*index_range*/size, halo_rings.raw());\n            cudaCheckError();\n            remove_previous_rings <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), halo_rings.raw(), A.manager->B2L_rings[i][ring]);\n            cudaCheckError();\n            thrust_wrapper::inclusive_scan<AMGX_device>(halo_rings.begin(), halo_rings.begin() + size + 1, halo_rings.begin());\n            A.manager->B2L_rings[i][ring + 1] = A.manager->B2L_rings[i][ring] + halo_rings[size];\n            A.manager->B2L_maps[i].resize(A.manager->B2L_rings[i][ring + 1]);\n\n            if (A.manager->B2L_rings[i][ring] == A.manager->B2L_rings[i][ring + 1])\n            {\n                for (int fill = ring; fill <= rings; fill++) { A.manager->B2L_rings[i][fill] = A.manager->B2L_rings[i][ring]; }\n\n                break;\n            }\n\n            num_blocks = std::min(4096, (A.get_num_rows() + 127) / 128);\n            write_ring_rows <<< num_blocks, 128>>>(halo_rings.raw(), A.manager->B2L_maps[i].raw() + A.manager->B2L_rings[i][ring], A.get_num_rows());\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_maps(Matrix_d &A, INDEX_TYPE rings,\n        INDEX_TYPE neighbors,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings, std::vector<IVector> &boundary_lists)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    B2L_maps.resize(neighbors);\n\n    //\n    // Step 4: export n-ring halo matrices\n    //\n    for (int i = 0; i < neighbors; i++)\n    {\n        B2L_maps[i] = boundary_lists[i];\n    }\n\n    create_rings(A, rings, neighbors, B2L_maps, B2L_rings);\n    //B2L_rings.resize(neighbors*rings+1);\n}\n\n// need for CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_maps(Matrix_d &A, INDEX_TYPE rings, std::vector<IVector> &boundary_lists)\n{\n    int size = A.get_num_rows();\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    int neighbors = A.manager->num_neighbors();\n    A.manager->B2L_maps.resize(neighbors);\n\n    //\n    // Step 4: export n-ring halo matrices\n    //\n    for (int i = 0; i < neighbors; i++)\n    {\n        A.manager->B2L_maps[i] = boundary_lists[i];\n    }\n\n    create_rings(A, rings);\n    // A.manager->B2L_rings.resize(neighbors*rings+1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows(Matrix_d &A, INDEX_TYPE rings, INDEX_TYPE neighbors, std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings, std::vector<Matrix_d > **halo_rows_p, std::vector<IVector *> &halo_lists)\n{\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    *halo_rows_p = new std::vector<Matrix<TConfig> >(neighbors);\n    std::vector<Matrix<TConfig> > &halo_rows = **halo_rows_p;\n\n    if (this->halo_coloring == LAST)   //Create dummy matrices\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            halo_rows[i].addProps(CSR);\n\n            if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n            halo_rows[i].resize(halo_lists[i]->size(), halo_lists[i]->size(), (A.hasProps(DIAG) ? 0 : halo_lists[i]->size()), A.get_block_dimy(), A.get_block_dimx(), 1);\n\n            if (A.hasProps(DIAG))\n            {\n                thrust_wrapper::fill<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), 0);\n            }\n            else\n            {\n                thrust_wrapper::sequence<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end());\n                amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_rows[i].col_indices.begin());\n            }\n\n            thrust_wrapper::fill<AMGX_device>(halo_rows[i].values.begin(), halo_rows[i].values.end(), types::util< ValueTypeA >::get_one());\n            cudaCheckError();\n        }\n\n        return;\n    }\n\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        max_size = max_size > B2L_maps[i].size() ? max_size : B2L_maps[i].size();\n        //B2L_rings[i][1]=B2L_maps[i].size(); //I don't know what this was doing here, definitely stupid for rings > 1\n    }\n\n    IVector matrix_halo_sizes((max_size + 1)*rings);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        //\n        int size = B2L_rings[i][rings];\n        matrix_halo_sizes.resize(size + 1);\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        write_matrix_rowsize <<< num_blocks, 128>>>(B2L_maps[i].raw(), A.row_offsets.raw(), size, matrix_halo_sizes.raw());\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n        int nnz_count =  matrix_halo_sizes[size];\n        //\n        // Resize export halo matrix, and copy over the rows\n        //\n        halo_rows[i].addProps(CSR);\n\n        if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n        halo_rows[i].resize(B2L_maps[i].size(), B2L_maps[i].size(), nnz_count, A.get_block_dimy(), A.get_block_dimx(), 1);\n        export_matrix_elements<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows[i].values.raw(), A.col_indices.raw(), halo_rows[i].col_indices.raw(), size);\n        cudaCheckError();\n        amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows[i].row_offsets.begin());\n\n        if (A.hasProps(DIAG))\n        {\n            export_matrix_diagonal <<< num_blocks, 128>>>(A.values.raw() + A.row_offsets[A.get_num_rows()]*A.get_block_size(), A.get_block_size(), B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*A.get_block_size(), size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_manager(Matrix_d &A, Matrix_d &P, int num_owned_coarse_pts)\n{\n    // This function initializes the manager of matrix P based on value of num_owned_coarse_pts\n    // The following member of the manager are initialized:\n    // global_id, num_partitions, comms (copied from manager of matrix A)\n    // part_offsets_h, part_offsets, base_index, index_range, num_rows_global, halo_offsets[0] (those are initialized based on value of num_owned_coarse_pts of all partitions in communicator)\n    if (P.manager == NULL)\n    {\n        P.manager = new DistributedManager<TConfig_d>();\n    }\n\n    P.setView(OWNED);\n    // Copy some information from manager of matrix A\n    P.manager->A = &P;\n    P.manager->setComms(A.manager->getComms());\n    index_type num_parts = A.manager->get_num_partitions();\n    index_type my_rank = A.manager->global_id();\n    P.manager->set_global_id(my_rank);\n    P.manager->set_num_partitions(num_parts);\n    // Do a global reduce to obtain part_offsets for RAP\n    IVector_h gathered_count;\n    A.manager->getComms()->all_gather(num_owned_coarse_pts, gathered_count, num_parts);\n    // Compute P.manager->part_offsets\n    P.manager->part_offsets_h.resize(num_parts + 1);\n    P.manager->part_offsets_h[0] = (int64_t) 0;\n\n    for (int i = 1; i < num_parts + 1; i++)\n    {\n        P.manager->part_offsets_h[i] = P.manager->part_offsets_h[i - 1] + (int64_t) gathered_count[i - 1];\n    }\n\n    P.manager->part_offsets = P.manager->part_offsets_h;\n    // Compute P.manager->halo_ranges\n    P.manager->set_base_index(P.manager->part_offsets_h[my_rank]);\n    P.manager->set_index_range(num_owned_coarse_pts);\n    P.manager->num_rows_global = P.manager->part_offsets_h[num_parts];\n    // Set halo_offsets[0]\n    P.manager->halo_offsets.resize(1);\n    P.manager->halo_offsets[0] = num_owned_coarse_pts;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::initialize_manager_from_global_col_indices(Matrix_d &P, I64Vector_d &P_col_indices_global)\n{\n    // This function finds the number of neighbors of P\n    // fills halo_offsets, local_to_global_map, B2L_maps based on global col indices of matrix P\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    // By checking the global col indices, identify neighbors\n    P.manager->neighbors.resize(0);\n    this->create_neighbors_v2_global(P, P_col_indices_global);\n    int num_neighbors = P.manager->neighbors.size();\n    P.manager->getComms()->set_neighbors(num_neighbors);\n    P.manager->halo_offsets.resize(num_neighbors + 1, 0);\n    int total_rows_of_neighbors = 0;\n    std::vector<int> flag_offsets_h(num_neighbors + 1, 0);\n\n    for (int i = 0; i < num_neighbors; i ++)\n    {\n        int num_rows = (int) (P.manager->halo_ranges_h[2 * i + 1] - P.manager->halo_ranges_h[2 * i]);\n        total_rows_of_neighbors += num_rows;\n        flag_offsets_h[i + 1] = flag_offsets_h[i] + num_rows;\n    }\n\n    // Copy to device\n    IVector flag_offsets_d(num_neighbors + 1);\n    amgx::thrust::copy(flag_offsets_h.begin(), flag_offsets_h.end(), flag_offsets_d.begin());\n    // Allocate halo_nodes array that will be used to flag halo nodes\n    // Flag_offsets array can be used to determine where each neighbor begins\n    IVector halo_nodes(total_rows_of_neighbors + 1);\n    thrust_wrapper::fill<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), 0);\n    cudaCheckError();\n    int num_blocks = std::min(4096, (P.get_num_rows() + 127) / 128);\n\n    if (P.get_num_rows() > 0)\n    {\n        flag_halo_nodes_global_v2<16> <<< num_blocks, 128>>>(P.row_offsets.raw(), P_col_indices_global.raw(), P.manager->halo_ranges.raw(), halo_nodes.raw(), flag_offsets_d.raw(), P.manager->base_index(), P.manager->index_range(), num_neighbors, P.get_num_rows());\n        cudaCheckError();\n    }\n\n    // Step 3: Write the 1-ring global nodes for each neighbor\n    thrust_wrapper::exclusive_scan<AMGX_device>(halo_nodes.begin(), halo_nodes.end(), halo_nodes.begin());\n    cudaCheckError();\n    std::vector<IVector> bdy_lists;\n    bdy_lists.resize(num_neighbors);\n    int count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo = halo_nodes[flag_offsets_h[i + 1]] - count;\n        P.manager->halo_offsets[i + 1] = P.manager->halo_offsets[i] + num_halo;\n        bdy_lists[i].resize(num_halo);\n\n        if (num_halo > 0)\n        {\n            int size = flag_offsets_h[i + 1] - flag_offsets_h[i];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            populate_B2L_v2 <<< num_blocks, 128>>>(halo_nodes.raw() + flag_offsets_h[i], bdy_lists[i].raw(), count, size);\n            cudaCheckError();\n        }\n\n        count += num_halo;\n    }\n\n    cudaCheckError();\n\n    // Fill the col_indices\n    if (P.get_num_rows() > 0)\n    {\n        renumber_col_indices_global<16> <<< num_blocks, 128>>>(P.row_offsets.raw(), P.col_indices.raw(), P_col_indices_global.raw(), P.manager->halo_ranges.raw(), halo_nodes.raw(), flag_offsets_d.raw(), P.manager->base_index(), P.manager->index_range(), num_neighbors, P.get_num_rows(), P.manager->global_id());\n        cudaCheckError();\n    }\n\n    // Create P.local_to_global_map\n    P.manager->local_to_global_map.resize(count);\n    count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo = bdy_lists[i].size();\n\n        if (num_halo > 0)\n        {\n            int num_blocks = std::min(4096, (num_halo + 127) / 128);\n            fill_local_to_global_map <<< num_blocks, 128>>>(bdy_lists[i].raw(), P.manager->local_to_global_map.raw() + count, P.manager->halo_ranges_h[2 * i], num_halo);\n            cudaCheckError();\n        }\n\n        count += num_halo;\n    }\n\n    cudaCheckError();\n    P.manager->getComms()->exchange_vectors(bdy_lists, P, 0);\n    // copy boundary lists to B2L maps\n    // release internal boundary lists array\n    P.manager->B2L_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        P.manager->B2L_maps[i] = bdy_lists[i];\n    }\n\n    P.manager->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        P.manager->B2L_rings[i].resize(2);\n        P.manager->B2L_rings[i][0] = 0;\n        P.manager->B2L_rings[i][1] = P.manager->B2L_maps[i].size();\n    }\n\n    this->initialize_B2L_maps_offsets(P, 1);\n    P.manager->_num_interior_nodes = P.get_num_rows();\n    P.manager->_num_boundary_nodes = 0;\n    P.manager->set_initialized(P.row_offsets);\n    int size_one_ring, offset;\n    P.manager->getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    P.setView(OWNED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createTempManager(Matrix_d &B, Matrix_d &A, IVector &row_offsets)\n{\n    // Initialize P.manager\n    if (B.manager == NULL)\n    {\n        B.manager = new DistributedManager<TConfig_d>();\n    }\n\n    index_type num_parts = A.manager->get_num_partitions();\n    index_type my_rank = A.manager->global_id();\n    B.manager->A = &B;\n    B.manager->setComms(A.manager->getComms());\n    B.manager->set_global_id(my_rank);\n    B.manager->set_num_partitions(num_parts);\n    B.manager->neighbors = A.manager->neighbors;\n    int num_neighbors = A.manager->neighbors.size();\n    B.manager->getComms()->set_neighbors(num_neighbors);\n    // --------------------------------------\n    // Create B.manager->B2L_maps\n    // --------------------------------------\n    B.manager->B2L_maps.resize(num_neighbors);\n    int max_B2L_maps_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int A_b2l_map_size = A.manager->B2L_rings[i][1];\n\n        if (A_b2l_map_size > max_B2L_maps_size)\n        {\n            max_B2L_maps_size = A_b2l_map_size;\n        }\n    }\n\n    IVector row_sizes(row_offsets.size() - 1);\n    thrust_wrapper::adjacent_difference<AMGX_device>(row_offsets.begin() + 1, row_offsets.end(), row_sizes.begin());\n    cudaCheckError();\n    IVector B2L_maps_offsets(max_B2L_maps_size + 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int A_b2l_map_size = A.manager->B2L_rings[i][1];\n\n        if (A_b2l_map_size > 0)\n        {\n            // For each node in the B2L_map for that neighbor, write the row size in B2L_maps_offsets\n            thrust_wrapper::gather<AMGX_device>(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].begin() + A_b2l_map_size, row_sizes.begin(), B2L_maps_offsets.begin());\n            // Do exclusive scan, to go from row length to row offsets\n            thrust_wrapper::exclusive_scan<AMGX_device>(B2L_maps_offsets.begin(), B2L_maps_offsets.begin() + A_b2l_map_size + 1, B2L_maps_offsets.begin());\n            B.manager->B2L_maps[i].resize(B2L_maps_offsets[A_b2l_map_size]);\n            const int cta_size = 128;\n            const int nWarps = cta_size / 32;\n            int grid_size = std::min( 4096, (int) (A_b2l_map_size + nWarps - 1) / nWarps);\n            // Populate the B2L_map array\n            fill_B_B2L_map<int, cta_size > <<< grid_size, cta_size >>>(\n                B2L_maps_offsets.raw(),\n                A.manager->B2L_maps[i].raw(),\n                row_offsets.raw(),\n                B.manager->B2L_maps[i].raw(),\n                A_b2l_map_size,\n                A.manager->global_id());\n                cudaCheckError();\n        }\n        else\n        {\n            B.manager->B2L_maps[i].resize(0);\n        }\n\n        cudaCheckError();\n    }\n\n    B.manager->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B.manager->B2L_rings[i].resize(2);\n        B.manager->B2L_rings[i][0] = 0;\n        B.manager->B2L_rings[i][1] = B.manager->B2L_maps[i].size();\n    }\n\n    // Initialize the B2L_maps_offsets (required by gather_v2 and scatter_v2) from the B2L_rings values\n    this->initialize_B2L_maps_offsets(B, 1);\n    B.manager->halo_offsets.resize(num_neighbors + 1);\n\n    // Fill halo_offsets\n    for (int i = 0; i < num_neighbors + 1; i++)\n    {\n        B.manager->halo_offsets[i] = row_offsets[A.manager->halo_offsets[i]];\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::exchange_halo_rows_P(Matrix_d &A, Matrix_d &P, I64Vector &RAP_local_to_global_map, IVector_h &P_neighbors, I64Vector_h &P_halo_ranges_h, I64Vector &P_halo_ranges, IVector_h &P_halo_offsets, I64Vector_h &RAP_part_offsets_h, I64Vector &RAP_part_offsets, index_type num_owned_coarse_pts, int64_t coarse_base_index)\n{\n    if (P.hasProps(DIAG) || P.get_block_size() != 1)\n    {\n        FatalError(\"P with external diagonal or block_size != 1 not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int num_neighbors = A.manager->num_neighbors() ;\n    std::vector<IVector> halo_rows_P_row_offsets;\n    std::vector<I64Vector> halo_rows_P_col_indices;\n    std::vector<IVector> halo_rows_P_local_col_indices;\n    std::vector<MVector> halo_rows_P_values;\n    // In this function, store in halo_rows_P_row_offsets, halo_rows_P_col_indices and halo_rows_P_values, the rows of P that need to be sent to each neighbors\n    // halo_rows_P_col_indices stores global indices\n    this->pack_halo_rows_P(A, P, halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_values, RAP_local_to_global_map, num_owned_coarse_pts, coarse_base_index);\n    // Do the exchange with the neighbors\n    // On return, halo_rows_P_rows_offsets, halo_rows_P_col_indices and halo_rows_P_values stores the rows of P received from each neighbor (rows needed to perform A*P)\n    std::vector<I64Vector> dummy_halo_ids(0);\n    A.manager->getComms()->exchange_matrix_halo(halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_values, dummy_halo_ids, A.manager->neighbors, A.manager->global_id());\n    halo_rows_P_local_col_indices.resize(halo_rows_P_col_indices.size());\n\n    for (int i = 0; i < halo_rows_P_col_indices.size(); i++)\n    {\n        halo_rows_P_local_col_indices[i].resize(halo_rows_P_col_indices[i].size());\n    }\n\n    // Update the list of neighbors \"P_neighbors\" and the corresponding ranges, offsets\n    update_neighbors_list(A, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP_part_offsets_h, RAP_part_offsets, halo_rows_P_row_offsets, halo_rows_P_col_indices);\n    std::vector<IVector> dummy_boundary_list(0);\n    int current_num_rings = 1;\n    halo_rows_P_local_col_indices.resize(halo_rows_P_col_indices.size());\n    // Convert the global indices in the rows just received to local indices\n    // This function updates halo offsets, halo_rows_P_local_col_indices, local_to_global_map\n    // This should not modify the existing P manager\n    this->compute_local_halo_indices( P.row_offsets, P.col_indices, halo_rows_P_row_offsets, halo_rows_P_col_indices, halo_rows_P_local_col_indices, RAP_local_to_global_map,  dummy_boundary_list, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, coarse_base_index, num_owned_coarse_pts, P.get_num_rows(), current_num_rings);\n    // Append the new rows to the matrix P\n    P.set_initialized(0);\n    this->append_halo_rows(P, halo_rows_P_row_offsets, halo_rows_P_local_col_indices, halo_rows_P_values);\n    P.set_initialized(1);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::append_halo_rows(Matrix_d &A, std::vector<IVector> &halo_row_offsets, std::vector<IVector> &halo_local_indices, std::vector<MVector> &halo_values )\n{\n    int new_num_rows = A.get_num_rows();\n    int new_num_nnz = A.row_offsets[A.get_num_rows()];\n    int cur_row = A.get_num_rows();\n    int cur_offset = new_num_nnz;\n    int num_neighbors = halo_row_offsets.size();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = halo_row_offsets[i].size();\n\n        if (size != 0)\n        {\n            new_num_rows += halo_row_offsets[i].size() - 1;\n            new_num_nnz += halo_local_indices[i].size();\n        }\n    }\n\n    A.resize(new_num_rows, new_num_rows, new_num_nnz, A.get_block_dimx(), A.get_block_dimy(), 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int num_halo_rows = halo_row_offsets[i].size() - 1;\n\n        if (num_halo_rows > 0)\n        {\n            // update halo row offsets in-place\n            thrust_wrapper::transform<AMGX_device>(halo_row_offsets[i].begin(), halo_row_offsets[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE>(cur_offset), halo_row_offsets[i].begin(), amgx::thrust::plus<INDEX_TYPE>());\n            // insert halo rows\n            amgx::thrust::copy(halo_row_offsets[i].begin(), halo_row_offsets[i].end() - 1, A.row_offsets.begin() + cur_row);\n            amgx::thrust::copy(halo_local_indices[i].begin(), halo_local_indices[i].end(), A.col_indices.begin() + cur_offset);\n            amgx::thrust::copy(halo_values[i].begin(), halo_values[i].end(), A.values.begin() + cur_offset);\n            // update counters\n            cur_offset = halo_row_offsets[i][num_halo_rows];\n            cur_row += num_halo_rows;\n        }\n    }\n\n    cudaCheckError();\n    A.row_offsets[A.get_num_rows()] = cur_offset;\n    int num_cols = -1;\n    num_cols = thrust_wrapper::reduce<AMGX_device>(A.col_indices.begin(), A.col_indices.end(), num_cols, amgx::thrust::maximum<int>()) + 1;\n    cudaCheckError();\n    A.set_num_cols(num_cols);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::exchange_RAP_ext(Matrix_d &RAP, Matrix_d &RAP_full, Matrix_d &A, Matrix_d &P, IVector_h &P_halo_offsets, I64Vector &RAP_local_to_global_map, IVector_h &P_neighbors, I64Vector_h &P_halo_ranges_h, I64Vector &P_halo_ranges, I64Vector_h &RAP_part_offsets_h, I64Vector &RAP_part_offsets, index_type num_owned_coarse_pts, int64_t coarse_base_index, void *wk)\n{\n    if (RAP_full.hasProps(DIAG) || RAP_full.get_block_size() != 1)\n    {\n        FatalError(\"RAP_full with external diagonal or block_size != 1 not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // --------------------------------------------------------------------------------------\n    // Pack the rows of RAP_full that need to be sent to neighbors, using the manager of P\n    // --------------------------------------------------------------------------------------\n    std::vector<IVector> halo_rows_RAP_row_offsets;\n    std::vector<I64Vector> halo_rows_RAP_row_ids;\n    std::vector<I64Vector> halo_rows_RAP_col_indices;\n    std::vector<MVector> halo_rows_RAP_values;\n    this->pack_halo_rows_RAP(RAP_full, P, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_values, halo_rows_RAP_row_ids, RAP_local_to_global_map);\n    // Send the rows to the neighbors, receive RAP rows from neighbors\n    // Col indices received from neighbors are global indices\n    // We also receive from neighbors the global row ids\n    P.manager->getComms()->exchange_matrix_halo(halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_values, halo_rows_RAP_row_ids, P.manager->neighbors, P.manager->global_id());\n    std::vector<IVector> halo_rows_RAP_local_col_indices;\n    halo_rows_RAP_local_col_indices.resize(halo_rows_RAP_col_indices.size());\n\n    for (int i = 0; i < halo_rows_RAP_col_indices.size(); i++)\n    {\n        halo_rows_RAP_local_col_indices[i].resize(halo_rows_RAP_col_indices[i].size());\n    }\n\n    // This function finds the new number of neighbors, renumber halo_cols with local_ids, modifies halo_offsets\n    update_neighbors_list(A, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP_part_offsets_h, RAP_part_offsets, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices);\n    // Now renumber find local indices for global col indices, update local_to_global\n    std::vector<IVector> dummy_boundary_list(0);\n    int current_num_rings = 2;\n    this->compute_local_halo_indices(RAP_full.row_offsets, RAP_full.col_indices, halo_rows_RAP_row_offsets, halo_rows_RAP_col_indices, halo_rows_RAP_local_col_indices, RAP_local_to_global_map, dummy_boundary_list, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, coarse_base_index, num_owned_coarse_pts, num_owned_coarse_pts, current_num_rings );\n    // Now find local row ids from global ids of rows received\n    std::vector<IVector> halo_rows_RAP_local_row_ids;\n    halo_rows_RAP_local_row_ids.resize(halo_rows_RAP_row_ids.size());\n\n    for (int i = 0; i < halo_rows_RAP_row_ids.size(); i++)\n    {\n        int size = halo_rows_RAP_row_ids[i].size();\n        halo_rows_RAP_local_row_ids[i].resize(size);\n\n        if (size != 0)\n        {\n            int cta_size = 128;\n            int grid_size = std::min( 4096, (size + cta_size - 1) / cta_size);\n            fill_halo_rows_row_ids <<< grid_size, cta_size >>>( halo_rows_RAP_row_ids[i].raw(), halo_rows_RAP_local_row_ids[i].raw(), coarse_base_index, num_owned_coarse_pts, size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n    // Add the rows received from neighbors to owned rows in RAP_full and create RAP matrix\n    RAP.set_initialized( 0 );\n    RAP.set_num_rows(num_owned_coarse_pts);\n    RAP.set_num_cols(num_owned_coarse_pts + RAP.manager->local_to_global_map.size());\n    RAP.row_offsets.resize(num_owned_coarse_pts + 1);\n    RAP_full.setView(OWNED);\n    CSR_Multiply<TConfig_d>::csr_RAP_sparse_add( RAP, RAP_full, halo_rows_RAP_row_offsets, halo_rows_RAP_local_col_indices, halo_rows_RAP_values, halo_rows_RAP_local_row_ids, wk );\n    RAP.set_initialized( 1 );\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::pack_halo_rows_P(Matrix_d &A, Matrix_d &P, std::vector<IVector> &halo_rows_P_row_offsets, std::vector<I64Vector> &halo_rows_P_col_indices, std::vector<MVector> &halo_rows_P_values, I64Vector &RAP_local_to_global_map, index_type num_owned_coarse_pts, int64_t coarse_base_index)\n{\n    int num_neighbors = A.manager->num_neighbors();\n    halo_rows_P_row_offsets.resize(num_neighbors);\n    halo_rows_P_col_indices.resize(num_neighbors);\n    halo_rows_P_values.resize(num_neighbors);\n    int num_rings_to_send = 1;\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > A.manager->B2L_rings[i][num_rings_to_send] ? max_size : A.manager->B2L_rings[i][num_rings_to_send];\n    }\n\n    IVector matrix_halo_sizes(max_size + 1);\n\n    // Here only using the 1-ring of matrix A\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        int size = A.manager->B2L_rings[i][num_rings_to_send];\n\n        if (size != 0)\n        {\n            //matrix_halo_sizes.resize(size+1);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), P.row_offsets.raw(), size, matrix_halo_sizes.raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n            int nnz_count =  matrix_halo_sizes[size];\n            // Resize export halo matrix, and copy over the rows\n            halo_rows_P_row_offsets[i].resize(size + 1);\n            halo_rows_P_col_indices[i].resize(nnz_count);\n            halo_rows_P_values[i].resize(nnz_count);\n            /* WARNING: Since A is reordered (into interior and boundary nodes), while R and P are not reordered,\n                        you must unreorder A when performing R*A*P product in ordre to obtain the correct result. */\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(P.row_offsets.raw(), P.values.raw(), P.get_block_size(), A.manager->B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows_P_values[i].raw(), P.col_indices.raw(), halo_rows_P_col_indices[i].raw(), size, RAP_local_to_global_map.raw(), NULL /*A.manager->inverse_renumbering.raw()*/, num_owned_coarse_pts, coarse_base_index);\n            cudaCheckError();\n            amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows_P_row_offsets[i].begin());\n        }\n        else\n        {\n            halo_rows_P_row_offsets[i].resize(0);\n            halo_rows_P_col_indices[i].resize(0);\n            halo_rows_P_values[i].resize(0);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::pack_halo_rows_RAP(Matrix_d &RAP, Matrix_d &P, std::vector<IVector> &halo_rows_RAP_row_offsets, std::vector<I64Vector> &halo_rows_RAP_col_indices, std::vector<MVector> &halo_rows_RAP_values, std::vector<I64Vector> &halo_rows_RAP_row_ids, I64Vector &RAP_local_to_global_map)\n{\n    int num_neighbors = P.manager->neighbors.size();\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    int64_t P_col_base_index = P.manager->base_index();\n    halo_rows_RAP_row_offsets.resize(num_neighbors);\n    halo_rows_RAP_row_ids.resize(num_neighbors);\n    halo_rows_RAP_col_indices.resize(num_neighbors);\n    halo_rows_RAP_values.resize(num_neighbors);\n    // Create tmp_B2L_maps that contains the list of rows to send\n    std::vector<IVector> tmp_B2L_maps(num_neighbors);\n\n    // Fill B2L_maps with list of halo indices for all rings to send\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        tmp_B2L_maps[i].resize(0);\n        halo_rows_RAP_row_ids[i].resize(0);\n        int begin = P.manager->halo_offsets[i];\n        int end =  P.manager->halo_offsets[i + 1];\n\n        if (begin > RAP.row_offsets.size() - 1)\n        {\n            break;\n        }\n\n        int local_to_global_begin = begin - P.manager->halo_offsets[0];\n        int size = end - begin;\n\n        if (end > RAP.row_offsets.size() - 1)\n        {\n            size = RAP.row_offsets.size() - 1 - begin;\n        }\n\n        if (size > 0)\n        {\n            tmp_B2L_maps[i].resize(size);\n            halo_rows_RAP_row_ids[i].resize(size);\n            thrust_wrapper::sequence<AMGX_device>(tmp_B2L_maps[i].begin(), tmp_B2L_maps[i].begin() + size, begin);\n            amgx::thrust::copy(P.manager->local_to_global_map.begin() + (int64_t) local_to_global_begin, P.manager->local_to_global_map.begin() + (int64_t) local_to_global_begin + (int64_t) size, halo_rows_RAP_row_ids[i].begin());\n        }\n    }\n\n    cudaCheckError();\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = tmp_B2L_maps[i].size();\n        max_size = max_size > size ? max_size : size;\n    }\n\n    IVector matrix_halo_sizes(max_size + 1);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        int size = tmp_B2L_maps[i].size();\n\n        if (size > 0)\n        {\n            //matrix_halo_sizes.resize(size+1);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(tmp_B2L_maps[i].raw(), RAP.row_offsets.raw(), size, matrix_halo_sizes.raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n            int nnz_count =  matrix_halo_sizes[size];\n            // Resize export halo matrix, and copy over the rows\n            halo_rows_RAP_row_offsets[i].resize(tmp_B2L_maps[i].size() + 1);\n            halo_rows_RAP_col_indices[i].resize(nnz_count);\n            halo_rows_RAP_values[i].resize(nnz_count);\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(RAP.row_offsets.raw(), RAP.values.raw(), RAP.get_block_size(), tmp_B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows_RAP_values[i].raw(), RAP.col_indices.raw(), halo_rows_RAP_col_indices[i].raw(), size, RAP_local_to_global_map.raw(), NULL, num_owned_coarse_pts, P_col_base_index);\n            cudaCheckError();\n            amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows_RAP_row_offsets[i].begin());\n        }\n        else\n        {\n            halo_rows_RAP_row_offsets[i].resize(0);\n            halo_rows_RAP_col_indices[i].resize(0);\n            halo_rows_RAP_values[i].resize(0);\n        }\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::sparse_matrix_add(Matrix_d &RAP, std::vector<IVector> &halo_rows_RAP_row_offsets, std::vector<I64Vector> &halo_rows_RAP_col_indices, std::vector<MVector> &halo_rows_RAP_values, std::vector<I64Vector> halo_rows_RAP_row_ids, I64Vector_d &P_local_to_global_map, index_type P_col_num_owned_pts, int64_t P_col_base_index)\n{\n    // Look at csr_galerkin_product implementation, use hash\n}\n\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows(Matrix_d &A,\n        std::vector<IVector *> &halo_lists\n                                                                                                         )\n{\n    int neighbors = A.manager->num_neighbors();\n\n    if (neighbors == 0)\n    {\n        return;\n    }\n\n    int rings = (A.manager->B2L_rings.size() - 1) / neighbors;\n    A.manager->halo_rows = new std::vector<Matrix<TConfig> >(neighbors);\n    std::vector<Matrix<TConfig> > &halo_rows = *A.manager->halo_rows;\n\n    if (this->halo_coloring == LAST)   //Create dummy matrices, only containing diagonal elements\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            halo_rows[i].addProps(CSR);\n\n            if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n            halo_rows[i].resize(halo_lists[i]->size(), halo_lists[i]->size(), (A.hasProps(DIAG) ? 0 : halo_lists[i]->size()), A.get_block_dimy(), A.get_block_dimx(), 1);\n\n            if (A.hasProps(DIAG))\n            {\n                thrust_wrapper::fill<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), 0);\n            }\n            else\n            {\n                thrust_wrapper::sequence<AMGX_device>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end());\n                amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_rows[i].col_indices.begin());\n            }\n\n            cudaCheckError();\n        }\n\n        return;\n    }\n\n    //Scratch space computation\n    int max_size = 0;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        max_size = max_size > A.manager->B2L_maps[i].size() ? max_size : A.manager->B2L_maps[i].size();\n        //B2L_rings[i][1]=B2L_maps[i].size(); //I don't know what this was doing here, definitely stupid for rings > 1\n    }\n\n    IVector matrix_halo_sizes((max_size + 1)*rings);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        //\n        // Write the length of the rows in the order of B2L_maps, then calculate row_offsets\n        //\n        int size = A.manager->B2L_rings[i][rings];\n        matrix_halo_sizes.resize(size + 1);\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), A.row_offsets.raw(), size, matrix_halo_sizes.raw());\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<AMGX_device>(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, matrix_halo_sizes.begin());\n        cudaCheckError();\n        int nnz_count =  matrix_halo_sizes[size];\n        //\n        // Resize export halo matrix, and copy over the rows\n        //\n        halo_rows[i].addProps(CSR);\n\n        if (A.hasProps(DIAG)) { halo_rows[i].addProps(DIAG); }\n\n        halo_rows[i].resize(A.manager->B2L_maps[i].size(), A.manager->B2L_maps[i].size(), nnz_count, A.get_block_dimy(), A.get_block_dimx(), 1);\n        export_matrix_elements<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), A.manager->B2L_maps[i].raw(), matrix_halo_sizes.raw(), halo_rows[i].values.raw(), A.col_indices.raw(), halo_rows[i].col_indices.raw(), size);\n        cudaCheckError();\n        amgx::thrust::copy(matrix_halo_sizes.begin(), matrix_halo_sizes.begin() + size + 1, halo_rows[i].row_offsets.begin());\n        cudaCheckError();\n\n        if (A.hasProps(DIAG))\n        {\n            export_matrix_diagonal <<< num_blocks, 128>>>(A.values.raw() + A.row_offsets[A.get_num_rows()]*A.get_block_size(), A.get_block_size(), A.manager->B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*A.get_block_size(), size);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_rows_global_indices(Matrix_d &A,\n        std::vector<IVector> &halo_rows_row_offsets,\n        std::vector<I64Vector> &halo_rows_col_indices,\n        std::vector<MVector> &halo_rows_values)\n{\n    // in this function we assume that we only have 1-ring so far\n    int neighbors = A.manager->num_neighbors();\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        // compute row offsets and nnz count\n        int size = A.manager->B2L_maps[i].size();\n        halo_rows_row_offsets[i].resize(size + 1);\n\n        if (size > 0)\n        {\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_matrix_rowsize <<< num_blocks, 128>>>(A.manager->B2L_maps[i].raw(), A.row_offsets.raw(), size, halo_rows_row_offsets[i].raw());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<AMGX_device>(halo_rows_row_offsets[i].begin(), halo_rows_row_offsets[i].begin() + size + 1, halo_rows_row_offsets[i].begin());\n            // compute global indices\n            int nnz_count = halo_rows_row_offsets[i][size];\n            halo_rows_col_indices[i].resize(nnz_count);\n            halo_rows_values[i].resize(nnz_count);\n            export_matrix_elements_global<32> <<< num_blocks, 128>>>(A.row_offsets.raw(), A.values.raw(), A.get_block_size(), A.manager->B2L_maps[i].raw(), halo_rows_row_offsets[i].raw(), halo_rows_values[i].raw(), A.col_indices.raw(), halo_rows_col_indices[i].raw(), size, A.manager->local_to_global_map.raw(), NULL, A.get_num_rows(), A.manager->base_index());\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl(Matrix_d &A, INDEX_TYPE rings, INDEX_TYPE neighbors,\n        Vector<ivec_value_type_h> &neighbors_list,\n        int64_t base_index, INDEX_TYPE index_range,\n        INDEX_TYPE global_id, std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        std::vector<Manager_d > **halo_btl_p,\n\n        std::vector<IVector *> &halo_lists\n                                                                                                        )\n{\n    *halo_btl_p = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = **halo_btl_p;\n    /*\n    //calling constructors on the allocated memory\n    for(int i=0; i<neighbors; i++)\n        new (&halo_btl[i]) DistributedManager<TConfig>();\n    */\n\n    if (this->halo_coloring != LAST)\n    {\n        //If we are sending matrices over\n        for (int i = 0; i < neighbors; i++)\n        {\n            //\n            // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n            //\n            /*\n            EXAMPLE\n            For example partition 1, with 2 rings, and the following B2L and L2H (called halo_lists here) info\n            num_neighbors=2; neighbors = [0 2]\n            B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n            L2H_maps (halo_lists) [[4 5][6 7]]\n            */\n            int size = B2L_rings[i][rings];\n            halo_btl[i].set_global_id(global_id);\n            halo_btl[i].set_base_index(0);\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(halo_lists[i]->size());\n            //send over B2L and L2H maps corresponding to current neighbor, so that it will be able to make sense of indices (local to this partition)\n            amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].L2H_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            amgx::thrust::copy(B2L_rings[i].begin(), B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n            /*\n             EXAMPLE\n             These fields are going to be:\n             halo_btl[0] (for neighbor ID 0)\n             global_id = 1; base_index=0; index_range=6 B2L_rings[0] = [0 2 4] B2L_maps[0] = [0 1| 2 3] L2H_maps = [4 5]\n             halo_btl[1] (for neighbor ID 2)\n            global_id = 1; base_index=0; index_range=8 B2L_rings[0] = [0 2 4] B2L_maps[0] = [1 3| 0 2] L2H_maps = [6 7]\n             */\n        }\n    }\n    else\n    {\n        if (rings > 1) { FatalError(\"halo_coloring=LAST dummy halo rows creation not implemented\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n        //Create dummy halo matrices\n        for (int i = 0; i < neighbors; i++)\n        {\n            int size = halo_lists[i]->size();\n            halo_btl[i].set_global_id(neighbors_list[i]);\n            halo_btl[i].set_base_index(0);\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(B2L_rings[i][rings]);\n            //Even though in this case matrices are not being sent over, we create the same data structures, so they can be handled the same way\n            amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].B2L_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = size;\n        }\n    }\n\n    cudaCheckError();\n}\n\n// CUDA 4.2\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl(Matrix_d &A,\n        std::vector<IVector *> &halo_lists)\n{\n    int neighbors = A.manager->num_neighbors();\n\n    if (neighbors == 0)\n    {\n        return;\n    }\n\n    int rings = (A.manager->B2L_rings.size() - 1) / neighbors;\n    A.manager->halo_btl = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = *A.manager->halo_btl;\n\n    if (this->halo_coloring != LAST)\n    {\n        for (int i = 0; i < neighbors; i++)\n        {\n            //\n            // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n            //\n            int size = A.manager->B2L_rings[i][rings];\n            halo_btl[i].set_global_id(A.manager->global_id());\n            halo_btl[i].set_base_index(0);\n            //Adding one more ring to store halo numbering\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(halo_lists[i]->size());\n            amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].L2H_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            amgx::thrust::copy(A.manager->B2L_rings[i].begin(), A.manager->B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n        }\n    }\n    else\n    {\n        if (rings > 1) { FatalError(\"halo_coloring=LAST dummy halo rows creation not implemented\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n        //Create dummy halo matrices\n        for (int i = 0; i < neighbors; i++)\n        {\n            int size = halo_lists[i]->size();\n            halo_btl[i].set_global_id(A.manager->neighbors[i]);\n            halo_btl[i].set_base_index(0);\n            //Adding one more ring to store halo numbering\n            halo_btl[i].resize(1, rings);\n            halo_btl[i].B2L_maps[0].resize(size);\n            halo_btl[i].L2H_maps.resize(1);\n            halo_btl[i].L2H_maps[0].resize(A.manager->B2L_rings[i][rings]);\n            amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n            amgx::thrust::copy(halo_lists[i]->begin(), halo_lists[i]->end(), halo_btl[i].B2L_maps[0].begin());\n            int max_index = thrust_wrapper::reduce<AMGX_device>(halo_lists[i]->begin(), halo_lists[i]->end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n            max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n            halo_btl[i].set_index_range(max_index);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = size;\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_btl_multiple_rings(Matrix_d &A, int rings)\n{\n    // creates halo btl managers\n    // required input from A.manager: num_neighbors, B2L_rings, B2L_maps, L2H_maps\n    int neighbors = A.manager->num_neighbors();\n    A.manager->halo_btl = new std::vector<DistributedManager<TConfig> >(neighbors);\n    std::vector<DistributedManager<TConfig> > &halo_btl = *A.manager->halo_btl;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        // Transfer indexing data too (global indices of exported rows, and sizes of the halo rings)\n        int size = A.manager->B2L_rings[i][rings];\n        halo_btl[i].set_global_id(A.manager->global_id());\n        halo_btl[i].set_base_index(0);\n        // Adding one more ring to store halo numbering\n        halo_btl[i].resize(1, rings);\n        halo_btl[i].B2L_maps[0].resize(size);\n        halo_btl[i].L2H_maps.resize(1);\n        halo_btl[i].L2H_maps[0].resize(A.manager->L2H_maps[i].size());\n        amgx::thrust::copy(A.manager->B2L_maps[i].begin(), A.manager->B2L_maps[i].end(), halo_btl[i].B2L_maps[0].begin());\n        amgx::thrust::copy(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), halo_btl[i].L2H_maps[0].begin());\n        amgx::thrust::copy(A.manager->B2L_rings[i].begin(), A.manager->B2L_rings[i].end(), halo_btl[i].B2L_rings[0].begin());\n        // compute index range\n        int max_index = thrust_wrapper::reduce<AMGX_device>(A.manager->L2H_maps[i].begin(), A.manager->L2H_maps[i].end(), (int)0, amgx::thrust::maximum<int>()) + 1;\n        max_index = max_index > A.get_num_rows() ? max_index : A.get_num_rows();\n        halo_btl[i].set_index_range(max_index);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_matrices(Matrix_d &A, INDEX_TYPE rings,\n        int64_t base_index, INDEX_TYPE index_range,\n        INDEX_TYPE global_id, Vector<ivec_value_type_h> &neighbors_,\n        std::vector<IVector > &B2L_maps,\n        std::vector<std::vector<VecInt_t> > &B2L_rings,\n        std::vector<Matrix_d > **halo_rows_p,\n        std::vector<Manager_d > **halo_btl_p, std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists)\n{\n    int neighbors = neighbors_.size();\n    this->create_maps(A, rings, neighbors, B2L_maps, B2L_rings, boundary_lists);\n    this->create_halo_btl(A, rings, neighbors, neighbors_, base_index, index_range, global_id, B2L_maps, B2L_rings, halo_btl_p, halo_lists);\n    this->create_halo_rows(A, rings, neighbors, B2L_maps, B2L_rings, halo_rows_p, halo_lists);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_d &A, INDEX_TYPE my_id, INDEX_TYPE rings)\n{\n    this->halo_coloring = A.manager->getComms()->halo_coloring;\n    I64Vector_h halo_ranges_h;\n    this->create_neighbors(A, my_id, halo_ranges_h);\n    A.manager->set_global_id(my_id);\n    int num_neighbors = A.manager->neighbors.size();\n    A.manager->resize(num_neighbors, rings);\n    A.manager->getComms()->set_neighbors(num_neighbors);\n    std::vector<IVector> boundary_lists;\n    IVector halo_nodes;\n    this->create_boundary_lists(A, halo_ranges_h, boundary_lists, halo_nodes);\n    A.manager->L2H_maps.resize(num_neighbors);\n    std::vector<IVector *> halo_lists;\n    halo_lists.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        A.manager->L2H_maps[i].resize(boundary_lists[i].size());\n        thrust_wrapper::transform<AMGX_device>(boundary_lists[i].begin(), boundary_lists[i].end(), amgx::thrust::constant_iterator<INDEX_TYPE > (halo_ranges_h[2 * i]), A.manager->L2H_maps[i].begin(), amgx::thrust::plus<INDEX_TYPE > ());\n        halo_lists[i] = &A.manager->L2H_maps[i];\n    }\n\n    cudaCheckError();\n    A.manager->getComms()->exchange_vectors(boundary_lists, A, 0);\n    this->renumber_to_local(A, boundary_lists, halo_lists, my_id, halo_ranges_h, halo_nodes);\n    this->create_halo_matrices(A, 1, boundary_lists, halo_lists);\n\n    if (this->halo_coloring != LAST)\n    {\n        A.manager->getComms()->exchange_matrix_halo(*A.manager->halo_rows, *A.manager->halo_btl, A);\n    }\n\n    A.manager->A = &A;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::create_halo_matrices(Matrix_d &A, INDEX_TYPE rings, std::vector<IVector> &boundary_lists, std::vector<IVector *> &halo_lists)\n{\n    this->create_maps(A, rings, boundary_lists);\n    this->create_halo_btl(A, halo_lists);\n    this->create_halo_rows(A, halo_lists);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedArranger<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::create_B2L(Matrix_h &A, INDEX_TYPE my_id, INDEX_TYPE rings)\n{\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DistributedArrangerBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DistributedArranger<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n\n\n\n", "source": "/kaggle/working/amgx/src/distributed/distributed_arranger.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <distributed/distributed_manager.h>\n#include <distributed/comms_mpi_gpudirect.h>\n#include <distributed/comms_mpi_hostbuffer_stream.h>\n#include <distributed/comms_visitors.h>\n#include <thrust/scan.h>\n#include <thrust/sort.h>\n#include <thrust/remove.h>\n#include <thrust/unique.h>\n#include <thrust/binary_search.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust_wrapper.h>\n#include <basic_types.h>\n#include <error.h>\n#include <util.h>\n#include <types.h>\n#include <iostream>\n#include <sstream>\n#include <fstream>\n#include <assert.h>\n#include \"cuda_runtime.h\"\n#include \"reorder_partition.h\"\n\n#include \"amgx_types/util.h\"\n\n#include <algorithm>\n#include <iostream> //debug only:\n\nstruct is_my_part\n{\n    const int _my_part;\n    is_my_part(int my_part) : _my_part(my_part) { }\n\n    __host__ __device__\n    bool operator()(const int part)\n    {\n        return (part == _my_part);\n    }\n};\n\nnamespace amgx\n{\n\nstatic int insertDiagonals = 1;\n\ntemplate <typename index_type>\nstatic __device__ __forceinline__\nindex_type internal_index(index_type i, index_type j, index_type k, index_type nx, index_type ny, index_type nz)\n{\n    return k * (nx * ny) + j * nx + i;\n}\n\ntemplate <typename index_type>\nstatic __device__ __forceinline__\nint64_t get_global_offset(index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    int rank_id = r * (P * Q) + q * P + p;\n    return ((int64_t) rank_id) * ((int64_t) num_rows);\n}\n\ntemplate <typename index_type>\n__global__\nvoid poisson7pt_count_row_len(index_type *row_len, index_type nx, index_type ny, index_type nz, index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows ; tidx += blockDim.x * gridDim.x)\n    {\n        /* compute p,q,r from P,Q,R and myid */\n        int i = tidx % nx; // Position in x direction\n        int j = (( tidx - i) / nx) % ny; // Position in y\n        int k = ( tidx - i - nx * j) / ( nx * ny ); // Position in z\n        int substract = ((i == 0) && (p == 0));\n        substract += ((i == nx - 1) && (p == P - 1));\n        substract += ((j == 0) && (q == 0));\n        substract += ((j == ny - 1) && (q == Q - 1));\n        substract += ((k == 0) && (r == 0));\n        substract += ((k == nz - 1) && (r == R - 1));\n        // Store 7 in position (num_rows+1), such that row_len[num_rows+1] = 0\n        //substract = (tidx == num_rows+1) ? 7 : substract;\n        row_len[tidx] = 7 - substract;\n    }\n}\n\ntemplate <typename index_type, typename mat_value_type>\n__global__\nvoid poisson7pt_set_col_values(const index_type *__restrict__ row_offsets, index_type *__restrict__ col_indices, mat_value_type *__restrict__  values, index_type *__restrict__ diag, int64_t *__restrict__ local_to_global, index_type nx, index_type ny, index_type nz, index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    for (int row = threadIdx.x + blockIdx.x * blockDim.x; row < num_rows ; row += blockDim.x * gridDim.x)\n    {\n        /* compute p,q,r from P,Q,R and myid */\n        int i = row % nx; // Position in x direction\n        int j = (( row - i) / nx) % ny; // Position in y\n        int k = ( row - i - nx * j) / ( nx * ny ); // Position in z\n        int halo_offset = num_rows;\n        int pos = row_offsets[row];\n        // Diagonal element\n        diag[row] = pos;\n        col_indices[pos] = row;\n        values[pos++] = types::util<mat_value_type>::get_one() * 6.;\n\n        // ----------------------------\n        // Neighbor at position i-1\n        // ----------------------------\n        if (i)\n        {\n            // Has a i-1 neighbor, which is an internal node at position (i-1,j,k)\n            col_indices[pos] = internal_index(i - 1, j, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (p)\n        {\n            // Has a i-1 neighbor, which is a halo node\n            int halo_index = halo_offset + k * ny + j;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p - 1, q, r, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(nx - 1, j, k, nx, ny, nz);\n        }\n\n        if (p)\n        {\n            halo_offset += ny * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position i+1\n        // ----------------------------\n        if (i < nx - 1)\n        {\n            // Has i+1 neighbor, which is an internal node at position (i+1,j,k)\n            col_indices[pos] = internal_index(i + 1, j, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (p < P - 1)\n            {\n                // Has i+1 neighbor, which is a halo node\n                int halo_index = halo_offset + k * ny + j;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p + 1, q, r, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(0, j, k, nx, ny, nz);\n            }\n        }\n\n        if (p < P - 1)\n        {\n            halo_offset += ny * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position j-1\n        // ----------------------------\n        if (j)\n        {\n            // Has a j-1 neighbor, which is an internal node at position (i,j-1,k)\n            col_indices[pos] = internal_index(i, j - 1, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (q)\n        {\n            // Has a j-1 neighbor, which is a halo node\n            int halo_index = halo_offset + k * nx + i;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p, q - 1, r, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(i, ny - 1, k, nx, ny, nz);\n        }\n\n        if (q)\n        {\n            halo_offset += nx * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position j+1\n        // ----------------------------\n        if (j < ny - 1)\n        {\n            // Has a j+1 neighbor, which is an internal node at position (i,j+1,k)\n            col_indices[pos] = internal_index(i, j + 1, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (q < Q - 1)\n            {\n                // Has a j+1 neighbor, which is a halo node\n                int halo_index = halo_offset + k * nx + i;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p, q + 1, r, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(i, 0, k, nx, ny, nz);\n            }\n        }\n\n        if (q < Q - 1)\n        {\n            halo_offset += nx * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position k-1\n        // ----------------------------\n        if (k)\n        {\n            // Has a k-1 neighbor, which is an internal node at position (i,j,k-1)\n            col_indices[pos] = internal_index(i, j, k - 1, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (r)\n        {\n            // Has a k-1 neighbor, which is a halo node\n            int halo_index = halo_offset + j * nx + i;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p, q, r - 1, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(i, j, nz - 1, nx, ny, nz);\n        }\n\n        if (r)\n        {\n            halo_offset += nx * ny;\n        }\n\n        // ----------------------------\n        // Neighbor at position k+1\n        // ----------------------------\n        if (k < nz - 1)\n        {\n            // Has a k+1 neighbor, which is an internal node at position (i,j,k+1)\n            col_indices[pos] = internal_index(i, j, k + 1, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (r < R - 1)\n            {\n                // Has a k+1 neighbor, which is a halo node\n                int halo_index = halo_offset + j * nx + i;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p, q, r + 1, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(i, j, 0, nx, ny, nz);\n            }\n        }\n\n        if (r < R - 1)\n        {\n            halo_offset += nx * ny;\n        }\n    }\n}\n\ntemplate <typename mat_value_type>\n__global__\nvoid set_halo_cols_values(int *row_offsets, int *col_indices, mat_value_type *values, int n, int total_rows, int bsize)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < (total_rows - n) )\n    {\n        int offset = row_offsets[n + tid];\n        col_indices[offset] = n + tid;\n#pragma unroll\n\n        for (int i = 0; i < bsize; i++)\n        {\n            values[offset * bsize + i] = types::util<mat_value_type>::get_one();    // This is arbitrary\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename mat_value_type>\n__global__\nvoid zero_copy_row_lengths_ids_offsets(int *d_old_row_offsets, int *root_row_offsets, int *d_row_ids, int n, int total_num_halos, mat_value_type *diag)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < n + total_num_halos)\n    {\n        int new_row_id = d_row_ids[tid];\n\n        if (tid < n)\n        {\n            int start = d_old_row_offsets[tid];\n            int row_length = d_old_row_offsets[tid + 1] - start; // zero-copy\n\n            if (diag != NULL) // will insert the diagonal\n            {\n                row_length++;\n            }\n\n            root_row_offsets[new_row_id] = row_length;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate< typename mat_value_type>\n__global__\nvoid ipc_consolidation_upload_matrix(int num_rows, int *row_ids, const int *old_row_offsets, int *new_row_offsets, const int *h_old_col_indices, int *new_col_indices, const mat_value_type *h_old_values, mat_value_type *new_values, const mat_value_type *h_old_diag, int bsize)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        int new_row = row_ids[row];\n        int src_base = old_row_offsets[row];\n        int dst_base = new_row_offsets[new_row];\n\n        // Insert the diagonal at the beginning of each row\n        if (h_old_diag != NULL)\n        {\n            new_col_indices[dst_base] = new_row;\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[dst_base * bsize + j] = h_old_diag[row * bsize + j];\n            }\n\n            // Increment dst_base by one\n            dst_base++;\n        }\n\n        int end = old_row_offsets[row + 1] - src_base;\n\n        for (int i = 0; i < end; i++)\n        {\n            int old_col = h_old_col_indices[src_base + i];\n            int new_col = row_ids[old_col];\n            new_col_indices[dst_base + i] = new_col;\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[ (dst_base + i)*bsize + j ] = h_old_values[ (src_base + i) * bsize + j ];\n            }\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate< typename mat_value_type>\n__global__\nvoid ipc_consolidation_replace_values(int num_rows, int *row_ids, const int *old_row_offsets, int *new_row_offsets, const mat_value_type *h_old_values, mat_value_type *new_values, const mat_value_type *h_old_diag, int bsize)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        int new_row = row_ids[row];\n        int src_base = old_row_offsets[row];\n        int dst_base = new_row_offsets[new_row];\n\n        // Insert the diagonal at the beginning of each row\n        if (h_old_diag != NULL)\n        {\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[dst_base * bsize + j] = h_old_diag[row * bsize + j];\n            }\n\n            // Increment dst_base by one\n            dst_base++;\n        }\n\n        int end = old_row_offsets[row + 1] - src_base;\n\n        for (int i = 0; i < end; i++)\n        {\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[ (dst_base + i)*bsize + j ] = h_old_values[ (src_base + i) * bsize + j ];\n            }\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n__global__ void flag_halo_ids_kernel(INDEX_TYPE *flags, INDEX_TYPE *ids, INDEX_TYPE offset, INDEX_TYPE size, INDEX_TYPE upper)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[ids[idx] - offset] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void read_halo_ids_kernel(INDEX_TYPE *flags, INDEX_TYPE *ids, INDEX_TYPE offset, INDEX_TYPE size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        ids[idx] = flags[ids[idx] - offset];\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate<class T>\n__global__ void reorder_vector_values(T *dst, const T *src, const INDEX_TYPE *map, INDEX_TYPE blocksize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * (blockDim.x / blocksize) + threadIdx.x / blocksize; //vectorised by block size\n    int vec_id = threadIdx.x % blocksize;\n\n    if (threadIdx.x >= (blockDim.x / blocksize)*blocksize ) { return; }\n\n    while (row < num_rows)\n    {\n        dst[map[row]*blocksize + vec_id] = src[row * blocksize + vec_id];\n        row += gridDim.x * (blockDim.x / blocksize);\n    }\n}\n\ntemplate<class T>\n__global__ void inverse_reorder_vector_values(T *dst, T *src, INDEX_TYPE *map, INDEX_TYPE blocksize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * (blockDim.x / blocksize) + threadIdx.x / blocksize;\n    int vec_id = threadIdx.x % blocksize;\n\n    if (threadIdx.x >= (blockDim.x / blocksize)*blocksize ) { return; }\n\n    while (row < num_rows)\n    {\n        dst[row * blocksize + vec_id] = src[map[row] * blocksize + vec_id];\n        row += gridDim.x * (blockDim.x / blocksize);\n    }\n}\n\n__global__ void remove_boundary_kernel(INDEX_TYPE *flags, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < size)\n    {\n        flags[maps[element]] = 0; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void get_unassigned_kernel(INDEX_TYPE *unassigned_flags, INDEX_TYPE *map, INDEX_TYPE *output, INDEX_TYPE part_size, INDEX_TYPE uf_size )\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < uf_size)\n        {\n            if (unassigned_flags[map[idx]] == 0)\n            {\n                unassigned_flags[map[idx]] = 1;\n                output[idx] = 1;\n            }\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void set_unassigned_kernel(INDEX_TYPE *part_assigned_flags, INDEX_TYPE *part_num, INDEX_TYPE *map, INDEX_TYPE *renum, INDEX_TYPE part_size, INDEX_TYPE max_element, INDEX_TYPE renum_size /*, INDEX_TYPE rank*/)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < renum_size)\n        {\n            if (part_assigned_flags[idx] == 1)\n            {\n                renum[map[idx]] = max_element + part_num[idx];\n            }\n\n            //also update the B2L map\n            map[idx] = renum[map[idx]];\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void renumber_b2l_maps(INDEX_TYPE *map, INDEX_TYPE *renum, INDEX_TYPE part_size, INDEX_TYPE renum_size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < renum_size)\n        {\n            //update the B2L map\n            map[idx] = renum[map[idx]];\n            idx += blockDim.x * gridDim.x;\n        }\n    }\n}\n\n__global__ void calc_inverse_renumbering(INDEX_TYPE *renum, INDEX_TYPE *irenum, INDEX_TYPE max_element)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < max_element)\n    {\n        if (renum[idx] < 0 || renum[idx] >= max_element) { printf(\"Renumbering error: %d %d\\n\", renum[idx], max_element); }\n\n        irenum[renum[idx]] = idx;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void create_halo_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, int64_t base_index, INDEX_TYPE map_offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = map_offset + row;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void apply_h2l2b_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, int64_t base_index, INDEX_TYPE *b2l_map, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = b2l_map[row];\n        row += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <int coop>\n__global__ void map_col_indices_and_count_rowlen(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *row_length,\n        INDEX_TYPE *mapping, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal)\n{\n    extern __shared__ volatile int reduction[];\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        int valid = 0;\n\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += coop) //this may look horrible, but I expect low branch divergence, because col indices in a row usually belong to the same partition (or at most one more)\n        {\n            int colIdx = col_indices[idx];\n            int new_col_idx = mapping[colIdx];\n\n            if (new_col_idx >= 0)\n            {\n                valid++;\n                col_indices[idx] = new_col_idx;\n            }\n            else\n            {\n                col_indices[idx] = -1;\n            }\n        }\n\n        reduction[threadIdx.x] = valid;\n\n        for (int s = 2; s > 0; s >>= 1)\n        {\n            if (coopIdx < s)\n            {\n                reduction[threadIdx.x] += reduction[threadIdx.x + s];\n            }\n\n            __syncthreads();\n        }\n\n        if (coopIdx == 0)\n        {\n            row_length[row] = reduction[threadIdx.x] + insert_diagonal;\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n__global__ void renumber_P_col_indices(INDEX_TYPE *__restrict__ col_indices, const INDEX_TYPE *__restrict__ renum, INDEX_TYPE num_owned_coarse_pts, INDEX_TYPE num_owned_fine_pts)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < num_owned_fine_pts )\n    {\n        INDEX_TYPE col_id = col_indices[idx];\n\n        if (col_id < num_owned_coarse_pts)\n        {\n            col_indices[idx] = renum[col_id];\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n\ntemplate <int coop, class T>\n__global__ void reorder_R_matrix(const INDEX_TYPE *old_rows, const INDEX_TYPE *old_cols, const T *old_vals, const INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE num_owned_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = row < num_owned_rows ? rows[renumbering[row]] : src_base;\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = old_vals[src_base * bsize + i];\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1] - src_base; i += coop)\n        {\n            cols[dst_base + i] = old_cols[src_base + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\n\n\ntemplate <int coop, class T>\n__global__ void reorder_whole_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        if (insert_diagonal)\n        {\n            if (coopIdx == 0) { cols[dst_base] = renumbering[row]; }\n\n            for (int i = coopIdx; i < bsize; i += coop)\n            {\n                vals[dst_base * bsize + i] = old_vals[(old_rows[num_rows] + row) * bsize + i];\n            }\n\n            dst_base++;\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = old_vals[src_base * bsize + i];\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1] - src_base; i += coop)\n        {\n            cols[dst_base + i] = old_cols[src_base + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void replace_values_matrix(const T *src_vals_h, const T *src_diag_h, const INDEX_TYPE *old_rows, const INDEX_TYPE *rows, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        for (int i = coopIdx; i < bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_diag_h[row * bsize + i];\n        }\n\n        dst_base++;\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_vals_h[src_base * bsize + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void replace_values_matrix(const T *src_vals_h, const INDEX_TYPE *old_rows, const INDEX_TYPE *rows, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_vals_h[src_base * bsize + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\n//TODO: optimize by vectorizing\ntemplate <class T>\n__global__ void reorder_whole_halo_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals,\n        INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal,\n        INDEX_TYPE global_offset, INDEX_TYPE local_offset, INDEX_TYPE halo_rows)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst = rows[row];\n\n        if (insert_diagonal)\n        {\n            cols[dst] = global_offset + row;\n\n            for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(old_rows[halo_rows - local_offset] + local_offset + row) * bsize + j]; }\n\n            dst++;\n        }\n\n        for (int i = 0; i < old_rows[row + 1] - src_base; i++)\n        {\n            INDEX_TYPE colIdx = old_cols[src_base + i];\n\n            if (colIdx >= 0)\n            {\n                cols[dst] = colIdx;\n\n                for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(src_base + i) * bsize + j]; }\n\n                dst++;\n            }\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_rowlen_reorder(INDEX_TYPE *row_offsets, INDEX_TYPE *row_len, INDEX_TYPE *map, INDEX_TYPE size, INDEX_TYPE insert_diag)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        row_len[map[idx]] = row_offsets[idx + 1] - row_offsets[idx] + insert_diag;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::remove_boundary(IVector_d &flagArray, IVector_d &B2L_map, int size)\n{\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), B2L_map.raw(), size);\n    cudaCheckError();\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::get_unassigned(IVector_d &flagArray, IVector_d &B2L_map, IVector_d &partition_flags, int size, int global_size /*, int rank*/)\n{\n    int num_blocks = std::min(4096, (size + 191) / 192);\n    get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n            B2L_map.raw(),\n            partition_flags.raw(), size, global_size /*, rank*/);\n    cudaCheckError();\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::set_unassigned(IVector_d &partition_flags, IVector_d &partition_renum, IVector_d &B2L_map, IVector_d &renumbering, int size, int max_element, int global_size /*, int rank*/)\n{\n    int num_blocks = std::min(4096, (size + 191) / 192);\n    set_unassigned_kernel <<< num_blocks, 192>>>(partition_flags.raw(),\n            partition_renum.raw(),\n            B2L_map.raw(),\n            renumbering.raw(),\n            size, max_element, global_size /*,rank*/);\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig >\ninline void DistributedManagerBase<TConfig>::set_initialized(IVector &row_offsets)\n{\n    // For P and R sizes the sizes are fixed at creation\n    if(m_fixed_view_size)\n    {\n        return;\n    }\n\n    if (neighbors.size() > 0)\n    {\n        //distributed: cache num_rows/num_nz for different views\n        _num_rows_interior = _num_interior_nodes;\n        _num_nz_interior = row_offsets[_num_rows_interior];\n        _num_rows_owned = _num_interior_nodes + _num_boundary_nodes;\n        _num_nz_owned = row_offsets[_num_rows_owned];\n        _num_rows_full = halo_offsets[neighbors.size()];\n\n        if (_num_rows_full >= row_offsets.size())\n        {\n            _num_nz_full = row_offsets[row_offsets.size() - 1];\n        }\n        else\n        {\n            _num_nz_full = row_offsets[_num_rows_full];\n        }\n\n        _num_rows_all = halo_offsets[halo_offsets.size() - 1];\n        _num_nz_all = _num_nz_full;\n    }\n    else\n    {\n        _num_rows_interior = _num_interior_nodes;\n        _num_nz_interior = row_offsets[_num_rows_interior];\n        _num_rows_owned = _num_interior_nodes;\n        _num_nz_owned = row_offsets[_num_rows_owned];\n        _num_rows_full = _num_rows_owned;\n        _num_nz_full = _num_nz_owned;\n        _num_rows_all = _num_rows_owned;\n        _num_nz_all = _num_nz_owned;\n    }\n}\n\ntemplate <class TConfig >\nvoid DistributedManagerBase<TConfig>::createAggregatesRenumbering(IVector_h &renumbering, IVector_h_vector &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    createAggRenumbering(renumbering, B2L_maps, size, num_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n}\n\ntemplate <class TConfig >\nvoid DistributedManagerBase<TConfig>::createAggregatesRenumbering(IVector_d &renumbering, IVector_d_vector &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    createAggRenumbering(renumbering, B2L_maps, size, num_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n}\n\ntemplate <class TConfig >\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::createAggRenumbering(IVector_hd &renumbering, std::vector<IVector_hd> &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //int num_neighbors = this->neighbors.size();\n    if (num_neighbors == 0)\n    {\n        num_boundary_aggregates = 0;\n        num_interior_aggregates = size;\n        return;\n    }\n\n    //initial size to size+1 so we have the total size after a scan\n    int global_size = size;\n    renumbering.resize(size + 1);\n    //\n    // Step 1 - in the main matrix, separate interior and boundary nodes (1/0 in flagArray), renumber interior ones with an exclusive scan\n    //\n    IVector_hd flagArray(size + 1);\n    thrust_wrapper::fill<IVector_hd::TConfig::memSpace>(flagArray.begin(), flagArray.begin() + size + 1, 1);\n    cudaCheckError();\n\n    //sets 1 for interior nodes, 0 for boundary node\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = B2L_maps[i].size();\n        remove_boundary(flagArray, B2L_maps[i], size);\n    }\n\n    //gets the renumbering of interior nodes\n    thrust_wrapper::exclusive_scan<IVector_hd::TConfig::memSpace>(flagArray.begin(), flagArray.begin() + size + 1, renumbering.begin());\n    cudaCheckError();\n    //\n    // Step 2 - Renumber nodes that are in the boundary, stepping through each B2L map, and renumbering ones that have not been renumbered yet\n    //\n    //what is the biggest B2L size\n    INDEX_TYPE max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > B2L_maps[i].size() ? max_size : B2L_maps[i].size();\n    }\n\n    //allocate work vectors (should be pretty small)\n    IVector_hd partition_flags(max_size);\n    IVector_hd partition_renum(max_size);\n    //the number of renumbered nodes so far\n    int max_element = renumbering[size];\n    num_interior_aggregates = max_element;\n    num_boundary_aggregates = size - max_element;\n    renumbering.resize(size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //find nodes that are part of the current boundary and they haven't been renumbered yet\n        thrust_wrapper::fill<IVector_hd::TConfig::memSpace>(partition_flags.begin(), partition_flags.begin() + max_size, 0);\n        int size = B2L_maps[i].size();\n        get_unassigned(flagArray, B2L_maps[i], partition_flags, size, global_size/*,0*/);\n        //calculate the local renumbering (within this boundary region) of these nodes\n        thrust_wrapper::exclusive_scan<IVector_hd::TConfig::memSpace>(partition_flags.begin(), partition_flags.begin() + max_size, partition_renum.begin());\n        //apply renumbering to the big numbering table\n        set_unassigned(partition_flags, partition_renum, B2L_maps[i], renumbering, size, max_element, global_size/*,0*/);\n        //update the number of renumbered nodes\n        max_element += partition_renum[max_size - 1] + partition_flags[max_size - 1];\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\ninline DistributedManagerBase<TConfig>::DistributedManagerBase(Matrix<TConfig> &a) :\n    m_fine_level_comms(NULL), A(&a), m_pinned_buffer_size(0), m_pinned_buffer(NULL), _num_interior_nodes(0), _num_boundary_nodes(0), _comms(NULL), has_B2L(false),\n    neighbors(_neighbors), B2L_maps(_B2L_maps), L2H_maps(_L2H_maps),  B2L_rings(_B2L_rings),\n    halo_rows_ref_count(0), halo_btl_ref_count(0), halo_ranges(_halo_ranges), halo_ranges_h(_halo_ranges_h), part_offsets(_part_offsets), part_offsets_h(_part_offsets_h), halo_rows(NULL), halo_btl(NULL), m_is_root_partition(false), m_is_glued(false), m_is_fine_level_glued(false), m_is_fine_level_consolidated(false), m_is_fine_level_root_partition(false), m_use_cuda_ipc_consolidation(false), m_fixed_view_size(false)\n{\n    cudaEventCreate(&comm_event);\n    cudaCheckError();\n    cudaStreamCreateWithFlags(&m_int_stream, cudaStreamNonBlocking);\n    cudaCheckError();\n    cudaStreamCreateWithFlags(&m_bdy_stream, cudaStreamNonBlocking);\n    cudaCheckError();\n    this->createComms(A->getResources());\n    int my_id = this->getComms()->get_global_id();\n    int num_parts = this->getComms()->get_num_partitions();\n    this->set_global_id(my_id);\n    this->set_num_partitions(num_parts);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generatePoisson7pt(int nx, int ny, int nz, int P, int Q, int R)\n{\n    int my_id = this->getComms()->get_global_id();\n    int p, q, r;\n\n    if (nx < P  || ny < Q || nz < R)\n    {\n        FatalError(\"(nx < P) or (ny < Q) or (nz < R) not supported\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    /* compute p,q,r from P,Q,R and myid */\n    p = my_id % P; // Position in x direction\n    q = (( my_id - p) / P) % Q; // Position in y\n    r = ( my_id - p - P * q) / ( P * Q ); // Position in z\n    // Create A.row_indices, A.col_indices, A.values, A.diag\n    int num_rows = nx * ny * nz;\n    int num_nonzeros = num_rows * 7; // Ignoring any boundary, 7 nnz per row\n    int num_substract = 0;\n\n    if (p == 0) { num_substract += ny * nz; }\n\n    if (p == P - 1) { num_substract += ny * nz; }\n\n    if (q == 0) { num_substract += nx * nz; }\n\n    if (q == Q - 1) { num_substract += nx * nz; }\n\n    if (r == 0) { num_substract += nx * ny; }\n\n    if (r == R - 1) { num_substract += nx * ny; }\n\n    num_nonzeros -= num_substract;\n    int num_halo_nodes = 2 * (ny * nz + nx * nz + nx * ny) - num_substract;\n    this->local_to_global_map.resize(num_halo_nodes);\n    this->A->set_initialized(0);\n    this->A->resize(0, 0, 0, 1, 1, 1);\n    this->A->addProps(CSR);\n    this->A->resize(num_rows, num_rows + num_halo_nodes, num_nonzeros, 1, 1, 1);\n    const int cta_size = 128;\n    const int grid_size = std::min( 4096, (num_rows + cta_size - 1) / cta_size );\n    poisson7pt_count_row_len <<< grid_size, cta_size>>>(this->A->row_offsets.raw(), nx, ny, nz, p, q, r, P, Q, R, num_rows);\n    cudaCheckError();\n    thrust_wrapper::exclusive_scan<AMGX_device>(this->A->row_offsets.begin(), this->A->row_offsets.end(), this->A->row_offsets.begin());\n    cudaCheckError();\n    // Now set nonzeros columns and values\n    // TODO: vectorize this\n    const int grid_size2 = std::min( 4096, (num_rows + cta_size - 1) / cta_size );\n    poisson7pt_set_col_values <<< grid_size2, cta_size>>>\n    (this->A->row_offsets.raw(),\n     this->A->col_indices.raw(),\n     this->A->values.raw(),\n     this->A->diag.raw(),\n     this->local_to_global_map.raw(),\n     nx, ny, nz,\n     p, q, r,\n     P, Q, R,\n     num_rows);\n    cudaCheckError();\n    // fill parts_offsets_h\n    // All ranks have same number of nodes\n    int num_ranks = P * Q * R;\n    this->part_offsets_h.resize(num_ranks + 1);\n    this->part_offsets_h[0] = (int64_t) 0;\n\n    for (int i = 1; i < num_ranks + 1; i++)\n    {\n        this->part_offsets_h[i] = this->part_offsets_h[i - 1] + (int64_t) num_rows;\n    }\n\n    // Device to host copy\n    this->part_offsets = this->part_offsets_h;\n    this->num_rows_global = P * Q * R * nx * ny * nz;\n//  this->A->set_initialized(1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_SetOffsets(\n    int num_ranks, int num_rows_global, const t_colIndex* partition_offsets)\n{\n    // fill part offsets internal data structures\n    this->part_offsets_h.resize(num_ranks + 1);\n\n    for (int i = 0; i <= num_ranks; i++)\n    {\n        this->part_offsets_h[i] = partition_offsets[i];\n    }\n    // copy to device\n    this->part_offsets = this->part_offsets_h;\n    // set num of global rows\n    this->num_rows_global = num_rows_global;\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nstd::map<t_colIndex, int> DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_LocalToGlobal(int num_rows, I64Vector_h &off_diag_cols)\n{\n    // sort global column indices\n    amgx::thrust::sort(off_diag_cols.begin(), off_diag_cols.end());\n    // find unique columns and set local <-> global mappings\n    // 1) Removed unneeded vector 2) Create map on host first, upload later (less thrust calls)\n    I64Vector_h local_to_global_h;\n    std::map<t_colIndex, int> global_to_local;        // temporary\n\n    if (off_diag_cols.size() > 0)\n    {\n        global_to_local[off_diag_cols[0]] = num_rows;\n        local_to_global_h.push_back(off_diag_cols[0]);\n    }\n\n    for (int i = 1; i < off_diag_cols.size(); i++)\n    {\n        if (off_diag_cols[i] != off_diag_cols[i - 1])\n        {\n            global_to_local[off_diag_cols[i]] = num_rows + local_to_global_h.size();\n            local_to_global_h.push_back(off_diag_cols[i]);\n        }\n    }\n    // Upload finished map in one piece\n    this->local_to_global_map.resize(local_to_global_h.size());\n    amgx::thrust::copy(local_to_global_h.begin(), local_to_global_h.end(), this->local_to_global_map.begin());\n    return global_to_local;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_InitLocalMatrix(\n    IVector_h local_col_indices,\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const mat_value_type *values,\n    const void *diag)\n{\n    // init local matrix\n    this->A->set_initialized(0);\n    this->A->resize(0, 0, 0, 1, 1, 1);\n    this->A->addProps(CSR);\n\n    if (diag)\n    {\n        this->A->addProps(DIAG);\n    }\n\n    this->A->resize(num_rows, num_rows + this->local_to_global_map.size(), num_nonzeros, block_dimx, block_dimy, 1);\n    cudaCheckError();\n    // set local matrix\n    amgx::thrust::copy(row_offsets, row_offsets + num_rows + 1, this->A->row_offsets.begin());\n    this->A->col_indices = local_col_indices;\n\n    amgx::thrust::copy(values, values + num_nonzeros * block_dimx * block_dimy, this->A->values.begin());\n    cudaCheckError();\n\n    // setup diagonal\n    if (diag)\n    {\n        cudaMemcpy(this->A->values.raw() + this->A->diagOffset()*this->A->get_block_size(), diag, sizeof(mat_value_type) * num_rows * block_dimx * block_dimy, cudaMemcpyDefault);\n        cudaCheckError();\n    }\n    else\n    {\n        this->A->computeDiagonal();\n    }\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrixPartitionVec(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const int *partition)\n{\n    // fetch my rank\n    int my_id = this->getComms()->get_global_id();\n    // setup partition vector\n    IVector_h partitionVec(num_rows_global);\n\n    if (partition == NULL)\n    {\n        IVector_h rowCounts(num_ranks);\n        this->getComms()->all_gather(num_rows, rowCounts, 1);\n\n        int p = 0;\n        for (int i = 0; i < num_ranks; ++i)\n        {\n            for (int j = 0; j < rowCounts[i]; ++j)\n            {\n                partitionVec[p++] = i;\n            }\n        }\n    }\n    else\n    {\n        // use existing partition info\n        for (int i = 0; i < num_rows_global; i++)\n        {\n            partitionVec[i] = partition[i];\n        }\n    }\n\n    // compute partition offsets (based on number of elements per partition). Will be modified when calculating partition map.\n    t_colIndex *partition_offsets = (t_colIndex *)calloc(num_ranks + 1, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        int pvi = partitionVec[i];\n        partition_offsets[pvi + 1]++;\n    }\n\n    thrust_wrapper::inclusive_scan<AMGX_host>(partition_offsets, partition_offsets + num_ranks + 1, partition_offsets);\n\n    loadDistributed_SetOffsets(num_ranks, num_rows_global, partition_offsets);\n\n    // compute partition map (which tells you how the global elements are mapped into the partitions)\n    t_colIndex *partition_map = (t_colIndex *)calloc(num_rows_global, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        int     pvi = partitionVec[i];\n        t_colIndex poi = partition_offsets[pvi];\n        partition_map[poi] = i;\n        partition_offsets[pvi]++;\n    }\n    free(partition_offsets);\n\n    // compute the inverse partition map\n    t_colIndex *ipartition_map = (t_colIndex *)calloc(num_rows_global, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        ipartition_map[partition_map[i]] = i;\n    }\n    free(partition_map);\n\n    int h_cidx_allocated = 0;\n    const t_colIndex *h_col_indices_global = (const t_colIndex *)this->getHostPointerForData(col_indices, num_nonzeros * sizeof(t_colIndex), &h_cidx_allocated);\n    // gather all off-diag columns\n    I64Vector_h off_diag_cols;\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] != my_id)\n        {\n            off_diag_cols.push_back(ipartition_map[h_col_indices_global[i]]);\n        }\n    }\n\n    auto global_to_local = loadDistributed_LocalToGlobal<t_colIndex>(num_rows, off_diag_cols);\n\n    // set 1, then scan to compute local row indices\n    IVector_h my_indices(num_rows_global);\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] == my_id)     // find my local columns and set to 1\n        {\n            my_indices[ipartition_map[h_col_indices_global[i]]] = 1;\n        }\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(my_indices.begin(), my_indices.end(), my_indices.begin());\n    // remap colums to local\n    IVector_h local_col_indices(num_nonzeros);\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] != my_id)\n        {\n            // off-diag\n            local_col_indices[i] = global_to_local[ipartition_map[h_col_indices_global[i]]];\n        }\n        else\n        {\n            // diag\n            local_col_indices[i] = my_indices[ipartition_map[h_col_indices_global[i]]];\n        }\n    }\n    free(ipartition_map);\n\n    loadDistributed_InitLocalMatrix(local_col_indices, num_rows, num_nonzeros, block_dimx, block_dimy, row_offsets, values, diag);\n\n    cudaCheckError();\n\n    // don't free possibly allocated pinned buffer, since it could be used later. if it would not - it would be deallocated automatically\n    /*if (h_cidx_allocated)\n    {\n      free((void*)h_col_indices_global);\n    }*/\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrixPartitionOffsets(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const t_colIndex *partition_offsets)\n{\n    // fetch my rank\n    int my_id = this->getComms()->get_global_id();\n    // sanity check, cheap to perform, and helps prevent harder-to-debug errors later on\n    if (!std::is_sorted(partition_offsets, partition_offsets + num_ranks + 1)) {\n        FatalError(\"Partition offsets are not sorted.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    loadDistributed_SetOffsets(num_ranks, num_rows_global, partition_offsets);\n\n    // Create predicate to determine if a column is in the local diagonal block\n    t_colIndex my_first_col = this->part_offsets_h[my_id];\n    t_colIndex one_past_my_last_col = this->part_offsets_h[my_id + 1];\n    auto in_local_diagonal_block = [my_first_col, one_past_my_last_col](const t_colIndex col_index) {\n        return col_index >= my_first_col && col_index < one_past_my_last_col;\n    };\n\n    int h_cidx_allocated = 0;\n    const t_colIndex *h_col_indices_global = (const t_colIndex *)this->getHostPointerForData(col_indices, num_nonzeros * sizeof(t_colIndex), &h_cidx_allocated);\n    // gather all off-diag columns\n    I64Vector_h off_diag_cols;\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (!in_local_diagonal_block(h_col_indices_global[i]))\n        {\n            off_diag_cols.push_back(h_col_indices_global[i]);\n        }\n    }\n    auto global_to_local = loadDistributed_LocalToGlobal<t_colIndex>(num_rows, off_diag_cols);\n    // set 1, then scan to compute local row indices\n    // \"coordinate-shift\" columns so they lie in much smaller range of my diagonal indices\n    int diagonal_size = this->part_offsets_h[my_id  + 1] - this->part_offsets_h[my_id];\n    IVector_h my_indices(diagonal_size);\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        t_colIndex col_index = h_col_indices_global[i];\n        if (in_local_diagonal_block(h_col_indices_global[i]))     // find my local columns and set to 1\n        {\n            // olumns that are on *my* diag partition cannot have an index from 0..num_rows_global\n            // instead, part_offsets_h[my_id] <= col_index < part_offsets[my_id+1]\n            col_index -= this->part_offsets_h[my_id];\n            my_indices[col_index] = 1;\n        }\n    }\n    thrust_wrapper::exclusive_scan<AMGX_host>(my_indices.begin(), my_indices.end(), my_indices.begin());\n\n    // remap colums to local\n    IVector_h local_col_indices(num_nonzeros);\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        t_colIndex col_index = h_col_indices_global[i];\n        if (!in_local_diagonal_block(col_index))\n        {\n            // off-diag\n            local_col_indices[i] = global_to_local[col_index];\n        }\n        else\n        {\n            // diag\n            col_index -= this->part_offsets_h[my_id];\n            local_col_indices[i] = my_indices[col_index];\n        }\n    }\n    loadDistributed_InitLocalMatrix(local_col_indices, num_rows, num_nonzeros, block_dimx, block_dimy, row_offsets, values, diag);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrix(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const MatrixDistribution &dist)\n{\n    using PI = MatrixDistribution::PartitionInformation;\n    switch (dist.getPartitionInformationStyle()) {\n        case PI::PartitionVec:\n            loadDistributedMatrixPartitionVec(num_rows, num_nonzeros, block_dimx, block_dimy, \n                row_offsets, col_indices, values, num_ranks, num_rows_global, diag, (const int*) dist.getPartitionData());\n            break;\n        case PI::PartitionOffsets:\n            loadDistributedMatrixPartitionOffsets(num_rows, num_nonzeros, block_dimx, block_dimy, \n                row_offsets, col_indices, values, num_ranks, num_rows_global, diag, (const t_colIndex*) dist.getPartitionData());\n            break;\n        default:\n            FatalError(\"Unsupported partitioning data format used with loadDistributedMatrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::renumberMatrixOneRing(int update_neighbours)\n{\n    FatalError(\"Distributed classical AMG not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumberMatrixOneRing(int update_neighbours)\n{\n    // Step 1: Using halo_ranges, flag neighbors and at the same time, flag halo_nodes (flag_halo_nodes_local)\n    int my_id = this->global_id();\n    int num_parts = this->get_num_partitions();\n    this->set_base_index(this->part_offsets_h[my_id]);\n    this->set_index_range(this->part_offsets_h[my_id + 1] - this->part_offsets_h[my_id]);\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n\n    // Create/update list of neighbors\n    if (update_neighbours)\n    {\n        typedef typename TConfig::template setVecPrec<AMGX_vecInt64>::Type i64vec_value_type;\n        typedef Vector<i64vec_value_type> I64Vector;\n        typedef typename Matrix<TConfig>::MVector MVector;\n        std::vector<IVector> halo_row_offsets(this->neighbors.size());\n        std::vector<I64Vector> halo_global_indices(this->neighbors.size());\n        std::vector<MVector> halo_values(this->neighbors.size());\n        prep->create_halo_rows_global_indices(*(this->A), halo_row_offsets, halo_global_indices, halo_values);\n        prep->update_neighbors_list(*(this->A), this->neighbors, this->halo_ranges_h, this->halo_ranges, this->part_offsets_h, this->part_offsets, halo_row_offsets, halo_global_indices);\n    }\n    else\n    {\n        prep->create_neighbors_v2(*(this->A));\n    }\n\n    this->getComms()->set_neighbors(this->neighbors.size());\n    // Create B2L_maps and L2H_maps\n    prep->create_boundary_lists_v3(*(this->A));\n    // halo_offsets\n    int neighbors = this->A->manager->num_neighbors();\n    int A_num_rows, offset;\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &A_num_rows);\n    this->halo_offsets.resize(neighbors + 1, 0);\n    this->halo_offsets[0] = A_num_rows;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        this->halo_offsets[i + 1] = this->halo_offsets[i] + this->B2L_maps[i].size();\n    }\n\n    this->getComms()->exchange_vectors(this->A->manager->B2L_maps, *(this->A), 0);\n    // Initialize B2L_rings\n    int num_neighbors = this->neighbors.size();\n    this->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->B2L_rings[i].resize(2);\n        this->B2L_rings[i][0] = 0;\n        this->B2L_rings[i][1] = this->B2L_maps[i].size();\n    }\n\n    prep->initialize_B2L_maps_offsets(*(this->A), 1);\n    delete prep;\n    //Use the exchanged halo row matrices and the boundary/halo index lists to renumber and consolidate the matrix\n    // Step 5: renumber all owned rows and columns\n    this->reorder_matrix_owned();\n    // Step 6: renumber local_to_global_map\n    int num_owned_rows = this->A->manager->halo_offsets[0];\n    int size_one_ring;\n    this->A->getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    I64Vector_d global_col_indices(size_one_ring);\n    thrust_wrapper::sequence<AMGX_device>(global_col_indices.begin(), global_col_indices.begin() + num_owned_rows, this->base_index() );\n    cudaCheckError();\n    global_col_indices.dirtybit = 1;\n    this->exchange_halo(global_col_indices, global_col_indices.tag);\n    thrust_wrapper::copy<AMGX_device>(global_col_indices.begin() + num_owned_rows, global_col_indices.begin() + size_one_ring, this->local_to_global_map.begin(), this->get_int_stream(), true);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::renumber_P_R(Matrix_h &P, Matrix_h &R, Matrix_h &A_fine)\n{\n    FatalError(\"Distributed classical AMG not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_P_R(Matrix_d &P, Matrix_d &R, Matrix_d &A_fine)\n{\n    int cta_size = 256;\n    int num_owned_fine_pts = A_fine.manager->halo_offsets[0];\n    int num_owned_coarse_pts, offset;\n    // matrix Ac\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &num_owned_coarse_pts);\n    // Renumber the owned col indices of P (not the halo columns ,since P.manager was created assunming some other numbering)\n    int nnz_owned_fine_pts = P.row_offsets[num_owned_fine_pts];\n    int num_blocks_fine = std::min(4096, (nnz_owned_fine_pts + cta_size - 1) / cta_size);\n\n    if (num_blocks_fine > 0)\n    {\n        renumber_P_col_indices <<< num_blocks_fine, cta_size>>>(P.col_indices.raw(), this->renumbering.raw(), num_owned_coarse_pts, nnz_owned_fine_pts);\n        cudaCheckError();\n    }\n\n    // Renumber the B2L_maps of P\n    for (int i = 0; i < P.manager->neighbors.size(); i++)\n    {\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), P.manager->B2L_maps[i].begin()),\n                     amgx::thrust::make_permutation_iterator(this->renumbering.begin(), P.manager->B2L_maps[i].end()),\n                     P.manager->B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n    // Don't renumber the L2H_maps or the halo\n    // Renumber the local_to_global_map of matrix P (since neighbors renumbered their owned rows)\n    // Swap owned rows of R\n    IVector new_row_offsets(R.row_offsets.size());\n    int insert = 0;\n    // Only renumber the owned rows\n    int num_blocks_owned = std::min(4096, (num_owned_coarse_pts + cta_size - 1) / cta_size);\n\n    if (num_blocks_owned > 0)\n    {\n        calc_rowlen_reorder <<< num_blocks_owned, cta_size >>>(R.row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), num_owned_coarse_pts, insert);\n        cudaCheckError();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + num_owned_coarse_pts + 1, new_row_offsets.begin());\n    cudaCheckError();\n    // Copy the row_offsets for halo rows\n    amgx::thrust::copy(R.row_offsets.begin() + num_owned_coarse_pts, R.row_offsets.end(), new_row_offsets.begin() + num_owned_coarse_pts);\n    cudaCheckError();\n    // Reorder the rows of R (no need to reorder the column indices)\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    int halo_offset = new_row_offsets[num_owned_coarse_pts];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values(new_nnz * R.get_block_size(), types::util< ValueTypeA >::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n    int num_blocks_total = std::min(4096, (R.get_num_rows() + cta_size - 1) / cta_size);\n\n    if (num_blocks_total > 0)\n    {\n        reorder_R_matrix <32> <<< num_blocks_total, 512>>>(R.row_offsets.raw(), R.col_indices.raw(), R.values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), R.get_block_size(), R.get_num_rows(), num_owned_coarse_pts);\n        cudaCheckError();\n    }\n\n    R.col_indices.swap(new_col_indices);\n    R.row_offsets.swap(new_row_offsets);\n    R.values.swap(new_values);\n\n    // Renumber the local_to_global_map (since neighbors have changed their owned numbering)\n    if (P.manager->neighbors.size() != 0)\n    {\n        int size_one_ring = P.manager->halo_offsets[P.manager->neighbors.size()];\n        I64Vector_d global_col_indices(size_one_ring);\n        thrust_wrapper::sequence<AMGX_device>(global_col_indices.begin(), global_col_indices.begin() + num_owned_coarse_pts, this->base_index());\n        cudaCheckError();\n        global_col_indices.dirtybit = 1;\n        P.manager->exchange_halo(global_col_indices, global_col_indices.tag);\n        thrust_wrapper::copy<AMGX_device>(global_col_indices.begin() + num_owned_coarse_pts, global_col_indices.begin() + size_one_ring, P.manager->local_to_global_map.begin(), this->get_int_stream(), true);\n        cudaCheckError();\n    }\n\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->initialize_B2L_maps_offsets(P, 1);\n    delete prep;\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingB2Lmaps()\n{\n    // Input:\n    // a matrix with N rows, whose column indices are local indices from 0 to N+M-1,\n    // where M is a number of 1-ring halo vertices\n    // The matrix also contains array \"local_to_global_map\" of size M, which stores the global index of each halo index\n    // Ex: assuming a column has index N+K, where 0 <= K < M, then it's global id is local_to_global_map[K]\n    // The matrix also contains part_offsets_h and part_offsets array, which stores where each partition begins\n    // Output:\n    // This function creates all the necessary data to to 1-ring exchanges\n    // i.e. list of 1-ring neighbors, B2L_maps for 1-ring, halo_offsets for 1-ring,\n    // Also, the function reorders the halo indices, such that 1-ring indices are in the order\n    // of neighbors, and therefore, exchange_halo doesn't have to be changed (i.e. L2H = identity)\n    // What is does:\n    // Based on the global indices of its halo vertices, count the number of neighbors\n    // For each neighbor, receive the halo indices that will be needed by neighbor\n    // From those, create B2L_maps[0], which contains for all neighbors\n    // This function assumes that:\n    // part_offset is defined\n\n    // B2L_maps\n    int my_id = this->global_id();\n    int num_parts = this->get_num_partitions();\n    this->set_base_index(this->part_offsets_h[my_id]);\n    this->set_index_range(this->part_offsets_h[my_id + 1] - this->part_offsets_h[my_id]);\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    // This function creates the array neighbors, which contains a list of partitions to which data\n    // needs to be sent and/or received\n    prep->create_neighbors_v2(*(this->A));\n    // Here change the manager if some partitions have no neighbors\n    this->getComms()->set_neighbors(this->neighbors.size());\n    prep->create_B2L_one_ring(*(this->A));\n    delete prep;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingHaloRows()\n{\n    // Input:\n    // A matrix with 1-ring B2L_maps, 1-ring halo_offsets\n    // Outputs:\n    // A matrix with: 1-ring rows,\n    //                2-ring B2L_maps,\n    //                2-ring halo_offsets\n    //                2-ring neighbors\n    // Implement here:\n    // Look at function create_B2L_from_maps, which calls create_rings, create_halo_btl, create_halo_rows and comms->exchange_matrix_halo\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->create_one_ring_halo_rows(*(this->A));\n    // I believe this can be removed since we don't use masked SpMV anymore\n    prep->createRowsLists(*(this->A), false);\n    delete prep;\n    // this is not necessary anymore becasue we don't use latency hiding\n    // however in future we might want to get back to this in case we want to use latency hiding\n    //this->reorder_matrix();\n}\n\ntemplate <class TConfig>\ninline DistributedManagerBase<TConfig>::DistributedManagerBase(\n    Matrix<TConfig> &a,\n    INDEX_TYPE allocated_halo_depth,\n    INDEX_TYPE num_import_rings,\n    int num_neighbors,\n    const VecInt_t *neighbors_) : m_fine_level_comms(NULL), A(&a), m_pinned_buffer_size(0), m_pinned_buffer(NULL), _num_interior_nodes(0), _num_boundary_nodes(0), _comms(NULL), has_B2L(false), neighbors(_neighbors), halo_rows_ref_count(0), halo_rows(NULL), halo_btl_ref_count(0), halo_btl(NULL), halo_ranges(_halo_ranges), halo_ranges_h(_halo_ranges_h), part_offsets(_part_offsets), part_offsets_h(_part_offsets_h),\n    B2L_maps(_B2L_maps),  L2H_maps(_L2H_maps), B2L_rings(_B2L_rings), m_is_root_partition(false), m_is_glued(false), m_is_fine_level_glued(false), m_is_fine_level_consolidated(false), m_is_fine_level_root_partition(false), m_use_cuda_ipc_consolidation(false), m_fixed_view_size(false)\n{\n    cudaStreamCreateWithFlags(&m_int_stream, cudaStreamNonBlocking);\n    cudaCheckError();\n    cudaStreamCreateWithFlags(&m_bdy_stream, cudaStreamNonBlocking);\n    cudaCheckError();\n\n    if (num_import_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (allocated_halo_depth != 1)\n    {\n        FatalError(\"allocated_halo_depth > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    this->set_num_halo_rings(num_import_rings);\n    neighbors.resize(num_neighbors);\n    cudaMemcpy(neighbors.raw(), neighbors_, num_neighbors * sizeof(VecInt_t), cudaMemcpyDefault);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMaps(const VecInt_t *b2l_maps, const VecInt_t *b2l_ptrs, const VecInt_t *l2h_maps, const VecInt_t *l2h_ptrs)\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = b2l_ptrs[i + 1] - b2l_ptrs[i];\n        this->cached_B2L_maps[i].resize(size);\n        int count = 0;\n\n        for (int j = b2l_ptrs[i]; j < b2l_ptrs[i + 1]; j++)\n        {\n            this->cached_B2L_maps[i][count] = b2l_maps[j];\n            count++;\n        }\n\n        size = l2h_ptrs[i + 1] - l2h_ptrs[i];\n        this->cached_L2H_maps[i].resize(size);\n        count = 0;\n\n        for (int j = l2h_ptrs[i]; j < l2h_ptrs[i + 1]; j++)\n        {\n            this->cached_L2H_maps[i][count] = l2h_maps[j];\n            count++;\n        }\n    }\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMapsOneRing()\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->cached_B2L_maps[i] = this->B2L_maps[i];\n        this->cached_L2H_maps[i] = this->L2H_maps[i];\n    }\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMapsOneRing(const VecInt_t **b2l_maps, const VecInt_t *b2l_sizes, const VecInt_t **l2h_maps, const VecInt_t *l2h_sizes)\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n    // buffering in the case of GPU data. This shouldn't much affect performance\n    std::vector<VecInt_t *> b2l_buffer, l2h_buffer;\n    std::vector<VecInt_t> b2l_sizes_buffer, l2h_sizes_buffer;\n    b2l_buffer.resize(num_neighbors);\n    l2h_buffer.resize(num_neighbors);\n    b2l_sizes_buffer.resize(num_neighbors);\n    l2h_sizes_buffer.resize(num_neighbors);\n    cudaError_t cuda_rc = cudaMemcpy(&(b2l_sizes_buffer[0]), b2l_sizes, sizeof(VecInt_t) * num_neighbors, cudaMemcpyDefault);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy b2l_sizes failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy(&(l2h_sizes_buffer[0]), l2h_sizes, sizeof(VecInt_t) * num_neighbors, cudaMemcpyDefault);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy l2h_sizes failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy(&(b2l_buffer[0]), b2l_maps, sizeof(VecInt_t *) * num_neighbors, cudaMemcpyDefault);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy b2l_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy(&(l2h_buffer[0]), l2h_maps, sizeof(VecInt_t *) * num_neighbors, cudaMemcpyDefault);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy l2h_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    // caching all of the maps\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = b2l_sizes_buffer[i];\n        this->cached_B2L_maps[i].resize(size);\n        cuda_rc = cudaMemcpy(&(this->cached_B2L_maps[i][0]), b2l_buffer[i], sizeof(VecInt_t) * size, cudaMemcpyDefault);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaMemcpy cached_B2L_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n        }\n        cudaCheckError();\n        size = l2h_sizes_buffer[i];\n        this->cached_L2H_maps[i].resize(size);\n        cudaMemcpy(&(this->cached_L2H_maps[i][0]), l2h_buffer[i], sizeof(VecInt_t) * size, cudaMemcpyDefault);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::setAConsolidationFlags( Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    AMG_Config *rsrc_cfg = this->A->getResources()->getResourcesConfig();\n    std::string scope;\n    int consolidate_flag, cuda_ipc_flag;\n    rsrc_cfg->getParameter<int>(\"fine_level_consolidation\", consolidate_flag, \"default\", scope);\n    rsrc_cfg->getParameter<int>(\"use_cuda_ipc_consolidation\", cuda_ipc_flag, \"default\", scope);\n    this->m_is_fine_level_consolidated = (consolidate_flag != 0);\n    this->m_use_cuda_ipc_consolidation = (cuda_ipc_flag != 0);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::uploadMatrix(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->setAConsolidationFlags(in_A);\n\n    if (this->m_is_fine_level_consolidated)\n    {\n        this->A->manager->consolidateAndUploadAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n    }\n    else\n    {\n        this->A->manager->initializeUploadReorderAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::checkPinnedBuffer(size_t size)\n{\n    if ((m_pinned_buffer_size < size) && (m_pinned_buffer != NULL))\n    {\n        amgx::memory::cudaFreeHost(m_pinned_buffer);\n        m_pinned_buffer = NULL;\n        m_pinned_buffer_size = 0;\n    }\n\n    if (m_pinned_buffer == NULL)\n    {\n        m_pinned_buffer_size = (size_t)(size * 1.1);\n        amgx::memory::cudaMallocHost(&m_pinned_buffer, m_pinned_buffer_size);\n    }\n}\n\n\ntemplate <class TConfig>\nDistributedManagerBase<TConfig>::~DistributedManagerBase()\n{\n    if (m_pinned_buffer != NULL)\n    {\n        amgx::memory::cudaFreeHost(m_pinned_buffer);\n    }\n\n    destroyComms();\n    // from childrens:\n    cudaStreamDestroy(this->m_int_stream);\n    cudaCheckError();\n    cudaStreamDestroy(this->m_bdy_stream);\n    cudaCheckError();\n\n    if (!this->halo_rows_ref_count && this->halo_rows != NULL)\n    {\n        delete this->halo_rows;\n        this->halo_rows = NULL;\n    }\n\n    if (!this->halo_btl_ref_count && this->halo_btl != NULL)\n    {\n        delete this->halo_btl;\n        this->halo_btl = NULL;\n    }\n}\n\n// if pointer is host pointer - returns data. If it is device pointer - copies it to the m_pinned_buffer and returns pointer to m_pinned_buffer\ntemplate <class TConfig>\nvoid *DistributedManagerBase<TConfig>::getHostPointerForData(void *ptr, size_t size, int *allocated)\n{\n    cudaError_t rc;\n    cudaPointerAttributes att;\n    void *ptr_h;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    /*\n    // original implementation\n    cudaPointerGetAttributes(&att, ptr);\n    if (att.hostPointer == NULL)\n    {\n      checkPinnedBuffer(size);\n      cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n      cudaCheckError();\n      return m_pinned_buffer;\n    }\n    else {\n      return ptr;\n    }\n    */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = cudaPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == cudaSuccess && (att.type == cudaMemoryTypeDevice || att.type == cudaMemoryTypeManaged))\n    {\n        checkPinnedBuffer(size);\n        rc = cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        ptr_h = m_pinned_buffer;\n        *allocated = 1;\n        cudaGetLastError(); //to reset last error\n        return ptr_h;\n    }\n    else\n    {\n        cudaGetLastError(); //to reset last error\n        return ptr;\n    }\n#else\n    if (rc == cudaSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        if (att.hostPointer == NULL)\n        {\n            //you are in case 2\n            checkPinnedBuffer(size);\n            rc = cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            ptr_h = m_pinned_buffer;\n            *allocated = 1;\n        }\n        else\n        {\n            //you are in case 4\n            ptr_h = ptr;\n        }\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        ptr_h = ptr;\n    }\n\n    cudaGetLastError(); //to reset last error\n\n    /* check for null pointers */\n    if (ptr_h == NULL)\n    {\n        FatalError(\"Result of (host) allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    return ptr_h;\n#endif\n}\n\n// if pointer is host pointer - returns data. If it is device pointer - copies it to the m_pinned_buffer and returns pointer to m_pinned_buffer\ntemplate <class TConfig>\nconst void *DistributedManagerBase<TConfig>::getHostPointerForData(const void *ptr, size_t size, int *allocated)\n{\n    cudaError_t rc;\n    cudaPointerAttributes att;\n    void *ptr_h;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = cudaPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == cudaSuccess && (att.type == cudaMemoryTypeDevice || att.type == cudaMemoryTypeManaged))\n    {\n        checkPinnedBuffer(size);\n        rc = cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        ptr_h = m_pinned_buffer;\n        *allocated = 1;\n        cudaGetLastError(); //to reset last error\n        return ptr_h;\n    }\n    else\n    {\n        cudaGetLastError(); //to reset last error\n        return ptr;\n    }\n#else\n    if (rc == cudaSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        if (att.hostPointer == NULL)\n        {\n            //you are in case 2\n            checkPinnedBuffer(size);\n            rc = cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            ptr_h = m_pinned_buffer;\n            *allocated = 1;\n            cudaGetLastError(); //to reset last error\n            return ptr_h;\n        }\n        else\n        {\n            //you are in case 4\n            cudaGetLastError(); //to reset last error\n            return ptr;\n        }\n    }\n    else\n    {\n        cudaGetLastError(); //to reset last error\n        //you are in case 1 or 3 from the above comment\n        return ptr;\n    }\n#endif\n}\n\n\ntemplate <class TConfig>\nvoid *DistributedManagerBase<TConfig>::getDevicePointerForData(void *ptr, size_t size, int *allocated)\n{\n    cudaError_t rc;\n    cudaPointerAttributes att;\n    void *ptr_d;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = cudaPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == cudaSuccess && att.type != cudaMemoryTypeUnregistered)\n    {\n        // Received device registered data (device, managed, malloc+cudaHostRegister, or cudaMallocHost)\n        ptr_d = (void*)att.devicePointer;\n\n        if (ptr_d == NULL)\n        {\n            FatalError(\"Data passed is registered with device but devicePointer null.\", AMGX_ERR_INTERNAL);\n        }\n    }\n    else\n    {\n        // Received unregistered or host allocated data (malloc)\n        rc = amgx::memory::cudaMallocAsync(&ptr_d, size);\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        rc = cudaMemcpy(ptr_d, ptr, size, cudaMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        *allocated = 1;\n    }\n#else\n\n    if (rc == cudaSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        ptr_d = (void *)att.devicePointer;\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        rc = cudaHostGetDevicePointer(&ptr_d, ptr, 0);\n\n        if (rc != cudaSuccess)\n        {\n            //you are in case 1\n            rc = amgx::memory::cudaMallocAsync(&ptr_d, size);\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            rc = cudaMemcpy(ptr_d, ptr, size, cudaMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            *allocated = 1;\n        }\n    }\n\n    /* check for null pointers */\n    if (ptr_d == NULL)\n    {\n        FatalError(\"Result of allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n#endif\n\n    cudaGetLastError(); //to reset last error\n    return ptr_d;\n}\n\ntemplate <class TConfig>\nconst void *DistributedManagerBase<TConfig>::getDevicePointerForData(const void *ptr, size_t size, int *allocated)\n{\n    cudaError_t rc;\n    cudaPointerAttributes att;\n    void *ptr_d;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = cudaPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == cudaSuccess && att.type != cudaMemoryTypeUnregistered)\n    {\n        // Received device registered data (device, managed, malloc+cudaHostRegister, or cudaMallocHost)\n        ptr_d = (void*)att.devicePointer;\n\n        if (ptr_d == NULL)\n        {\n            FatalError(\"Data passed is registered with device but devicePointer null.\", AMGX_ERR_INTERNAL);\n        }\n    }\n    else\n    {\n        // Received unregistered or host allocated data (malloc)\n        rc = amgx::memory::cudaMallocAsync(&ptr_d, size);\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        rc = cudaMemcpy(ptr_d, ptr, size, cudaMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != cudaSuccess)\n        {\n            FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        *allocated = 1;\n    }\n#else\n    if (rc == cudaSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        cudaGetLastError(); //to reset last error\n        return (const void *)att.devicePointer;\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        rc = cudaHostGetDevicePointer(&ptr_d, (void *)ptr, 0);\n\n        if (rc != cudaSuccess)\n        {\n            //you are in case 1\n            rc = amgx::memory::cudaMallocAsync(&ptr_d, size);\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            rc = cudaMemcpy(ptr_d, ptr, size, cudaMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != cudaSuccess)\n            {\n                FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the cudaMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            *allocated = 1;\n            cudaGetLastError(); //to reset last error\n            return (const void *)ptr_d;\n        }\n    }\n\n    /* check for null pointers */\n    if (ptr_d == NULL)\n    {\n        FatalError(\"Result of allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n#endif\n\n    // shouldn't get there\n    cudaGetLastError(); //to reset last error\n    return NULL;\n}\n\ntemplate <class TConfig>\nvoid initializeMatrixCopyAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> *A)\n{\n    typedef typename TConfig::MatPrec  mat_value_type;\n    A->resize( n, n, nnz, block_dimx, block_dimy );\n    //Upload the entire matrix\n    cudaMemcpy( A->row_offsets.raw(), row_ptrs, (n + 1) * sizeof(int), cudaMemcpyDefault );\n    cudaCheckError();\n    cudaMemcpy( A->col_indices.raw(), col_indices, (nnz) * sizeof(int), cudaMemcpyDefault );\n    cudaCheckError();\n    cudaMemcpy( A->values.raw(), (mat_value_type *)data, (nnz * block_dimx * block_dimy) * sizeof(mat_value_type), cudaMemcpyDefault );\n    cudaCheckError();\n\n    if (diag)\n    {\n        cudaMemcpy( A->values.raw() + A->diagOffset()*A->get_block_size(), (mat_value_type *)diag, (n * block_dimx * block_dimy) * sizeof(mat_value_type), cudaMemcpyDefault );\n        cudaCheckError();\n    }\n    else\n    {\n        A->computeDiagonal();\n    }\n\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::updateMapsReorder()\n{\n    int my_id = this->getComms()->get_global_id();\n    DistributedComms<TConfig> *comms_tmp = this->getComms();\n    DistributedComms<TConfig> **comms_ = &comms_tmp;\n    // Copy B2L_maps in their final place\n    int num_neighbors = this->neighbors.size();\n    B2L_maps.resize(num_neighbors);\n    L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B2L_maps[i] = this->cached_B2L_maps[i];\n        L2H_maps[i] = this->cached_L2H_maps[i];\n    }\n\n    //Create a DistributedArranger object to map further halo rings and to construct halo row matrices and exchange them (if halo_coloring != LAST)\n    DistributedArranger<TConfig> *prep = new DistributedArranger<TConfig>;\n    prep->create_B2L_from_maps( (*(this->A)), my_id, this->num_halo_rings(), neighbors,\n                                B2L_maps, L2H_maps, B2L_rings, comms_, &halo_rows, &halo_btl);\n    DistributedManagerBaseInit(my_id, 0, this->A->get_num_rows(), *(this->A), comms_, NULL, NULL);\n    //Use the exchanged halo row matrices and the boundary/halo index lists to renumber and consolidate the matrix\n    this->reorder_matrix();\n    prep->initialize_B2L_maps_offsets(*(this->A), this->num_halo_rings());\n    delete prep;\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::initializeUploadReorderAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    initializeMatrixCopyAll<TConfig>(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, this->A);\n    this->updateMapsReorder();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::destroyComms()\n{\n    if ( (this->_comms) != NULL )\n    {\n        if (this->_comms->decr_ref_count())\n        {\n            delete (this->_comms);\n            this->_comms = NULL;\n        }\n    }\n\n    if ( (this->m_fine_level_comms) != NULL)\n    {\n        if (this->m_fine_level_comms->decr_ref_count())\n        {\n            delete (this->m_fine_level_comms);\n            this->m_fine_level_comms = NULL;\n        }\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::initComms(Resources *rsrc)\n{\n    this->createComms(rsrc);\n    int my_id = this->getComms()->get_global_id();\n    int num_parts = this->getComms()->get_num_partitions();\n    this->set_global_id(my_id);\n    this->set_num_partitions(num_parts);\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createComms(Resources *rsrc)\n{\n    // create communicator\n#ifdef AMGX_WITH_MPI\n    destroyComms();\n    if (rsrc == NULL)\n        FatalError(\"Resources should not be NULL\", AMGX_ERR_INTERNAL);\n\n    MPI_Comm *mpi_comm = rsrc->getMpiComm();\n    AMG_Config *cfg = rsrc->getResourcesConfig();\n    std::string comm_value, comm_scope;\n    cfg->getParameter<std::string>(\"communicator\", comm_value, \"default\", comm_scope);\n    int rank = -1;\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n\n    if (comm_value == \"MPI_DIRECT\")\n    {\n        _comms = new CommsMPIDirect<TConfig>(*cfg, comm_scope, mpi_comm);\n        std::string comm_log(\"Using CUDA-Aware MPI (GPU Direct) communicator...\\n\");\n        amgx_distributed_output(comm_log.c_str(), comm_log.length());\n    }\n    else if (comm_value == \"MPI\")\n    {\n        _comms =  new CommsMPIHostBufferStream<TConfig>(*cfg, comm_scope, mpi_comm);\n        std::string comm_log(\"Using Normal MPI (Hostbuffer) communicator...\\n\");\n        amgx_distributed_output(comm_log.c_str(), comm_log.length());\n    }\n    else \n    { \n        FatalError(\"Bad communicator value\", AMGX_ERR_BAD_PARAMETERS); \n    }\n\n#endif\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::malloc_export_maps(VecInt_t ***b2l_maps_e, VecInt_t **b2l_maps_sizes_e, VecInt_t ***l2h_maps_e, VecInt_t **l2h_maps_sizes_e)\n{\n    *b2l_maps_e = (VecInt_t **) malloc(sizeof(VecInt_t *)*this->num_neighbors());\n    *l2h_maps_e = (VecInt_t **) malloc(sizeof(VecInt_t *)*this->num_neighbors());\n    *b2l_maps_sizes_e = (VecInt_t *) malloc(sizeof(VecInt_t) * (this->num_neighbors()));\n    *l2h_maps_sizes_e = (VecInt_t *) malloc(sizeof(VecInt_t) * (this->num_neighbors()));\n\n    for (int i = 0; i < this->num_neighbors(); i++)\n    {\n        (*b2l_maps_sizes_e)[i] = B2L_maps[i].size();\n        (*l2h_maps_sizes_e)[i] = L2H_maps[i].size();\n        (*b2l_maps_e)[i] =  (VecInt_t *) malloc(sizeof(VecInt_t) * ( (*b2l_maps_sizes_e)[i]) );\n\n        if (L2H_maps[i].size() != 0)\n        {\n            (*l2h_maps_e)[i] =  (VecInt_t *) malloc(sizeof(VecInt_t) * ( (*l2h_maps_sizes_e)[i]) );\n            amgx::thrust::copy(L2H_maps[i].begin(), L2H_maps[i].end(), (*l2h_maps_e)[i]);\n        }\n\n        amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), (*b2l_maps_e)[i]);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createRenumbering(IVector &renumbering)\n{\n    int num_neighbors = this->neighbors.size();\n    // still renumber if the number of neighbors = 0, to support non-symmetric matrices\n    // if (num_neighbors == 0) return;\n    /*\n      EXAMPLE\n      Example matrix, partition 1 arrives with state:\n      A.row_offsets = [0 4 11 15 20]\n      A.col_indices = [4 0 1 2\n                       4 5 0 1 2 3 7\n                       0 1 2 3\n                       1 2 3 6 7]\n      num_neighbors=2; neighbors = [0 2]\n      B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n      L2H_maps (and halo_lists) [[4 5][6 7]]\n    */\n    int size = 0;\n\n    if (this->L2H_maps.size())\n    {\n        size = thrust_wrapper::reduce<AMGX_device>(this->A->col_indices.begin(), this->A->col_indices.end(), int(0), amgx::thrust::maximum<int>()) + 1; //Sufficient to do reduction on lth maps\n        cudaCheckError();\n    }\n    else\n    {\n        size = this->A->get_num_rows();\n    }\n\n    int rings = (this->B2L_rings.size() > 0) ? this->B2L_rings[0].size() - 1 : 0;\n    //initial size to size+1 so we have the total size after a scan\n    renumbering.resize(size + 1);\n    int global_size = size;\n    //\n    // Step 1 - in the main matrix, separate interior and boundary nodes (1/0 in flagArray), renumber interior ones with an exclusive scan\n    //\n    IVector flagArray(size + 1);\n    thrust_wrapper::fill<AMGX_device>(flagArray.begin(), flagArray.begin() + size + 1, 1);\n    cudaCheckError();\n\n    //sets 1 for interior nodes, 0 for boundary node\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = this->B2L_rings[i][1];\n        int num_blocks = std::min(4096, (size + 127) / 128);\n\n        if (size > 0)\n        {\n            remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), this->B2L_maps[i].raw(), size);\n            cudaCheckError();\n        }\n\n        //If there are any L2H maps\n        if (this->L2H_maps.size() && this->L2H_maps[i].size())\n        {\n            int size = this->L2H_maps[i].size();\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), this->L2H_maps[i].raw(), size);\n        }\n\n        cudaCheckError();\n    }\n\n    //gets the renumbering of interior nodes\n    thrust_wrapper::exclusive_scan<AMGX_device>(flagArray.begin(), flagArray.begin() + size + 1, renumbering.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     After removing 1-ring boundary nodes and halo nodes from flagArray: [0 0 1 0 0 0 0 0]\n     After exclusive scan, which gives renumbering for interior nodes (only node #2)\n     renumbering: [0 0 0 1 1 1 1 1]\n     */\n    //\n    // Step 2 - Renumber nodes that are in the boundary, stepping through each B2L map, and renumbering ones that have not been renumbered yet\n    //\n    //what is the biggest B2L size\n    INDEX_TYPE max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > this->B2L_rings[i][1] ? max_size : this->B2L_rings[i][1];\n\n        if (this->L2H_maps.size())\n        {\n            max_size = max_size > this->L2H_maps[i].size() ? max_size : this->L2H_maps[i].size();\n        }\n    }\n\n    //allocate work vectors (should be pretty small) that are used to renumber boundary nodes\n    IVector boundary_renum_flags(max_size);\n    IVector boundary_renum(max_size);\n    //the number of renumbered nodes so far\n    int max_element = renumbering[size];\n    this->_num_interior_nodes = max_element;\n    this->_num_boundary_nodes = this->A->get_num_rows() - max_element;\n    renumbering.resize(size);\n\n    /*\n     EXAMPLE\n     size = 8\n     max_size = 2, max_element = 1, num_interior_nodes=1, num_boundary_nodes = 4-1 = 3\n     */\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //find nodes that are part of the current boundary and they haven't been renumbered yet\n        thrust_wrapper::fill<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, 0);\n        int size = this->B2L_rings[i][1];\n        int num_blocks = std::min(4096, (size + 191) / 192);\n\n        if (size > 0)\n            get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n                    this->B2L_maps[i].raw(),\n                    boundary_renum_flags.raw(), size, global_size /*,rank*/);\n                    cudaCheckError();\n\n        //calculate the local renumbering (within this boundary region) of these nodes\n        thrust_wrapper::exclusive_scan<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, boundary_renum.begin());\n\n        //apply renumbering to the big numbering table\n        if (size > 0)\n            set_unassigned_kernel <<< num_blocks, 192>>>(boundary_renum_flags.raw(),\n                    boundary_renum.raw(),\n                    this->B2L_maps[i].raw(),\n                    renumbering.raw(),\n                    size, max_element, global_size /*,rank*/);\n                    cudaCheckError();\n\n        //update the number of renumbered nodes\n        max_element += boundary_renum[max_size - 1] + boundary_renum_flags[max_size - 1];\n        /*\n         EXAMPLE\n         for neighbor 0 (ID 0)\n         boundary_renum_flags = [0 0], size = 2, flagArray [0 0 1 0 0 0 0 0]\n         get_unassigned_kernel's output: boundary_renum_flags = [1 1] flagArray [1 1 1 0 0 0 0 0]\n         after exclusive scan: boundary_renum [0 1]\n         set_unassigned_kernel updates these arrays and renumbers B2L map:\n         renumbering = [1 2 0 1 1 1 1 1] B2L_maps[0] = [1 2| 2 3] (note that after element 3 in renumbering and after element 2 we have invalid/not yet updated values)\n         max_element = 3\n\n         for neighbor 1 (ID 2)\n         get_unassigned_kernels's output: boundary_renum_flags [0 1] flagArray [1 1 1 1 0 0 0 0]\n         after exclusive scan boundary_renum [0 0]\n         set_unassigned_kernel renumbering [1 2 0 3 1 1 1 1] B2L_maps[1] = [2 3| 0 2]\n         max_element = 4\n         */\n    }\n\n    cudaCheckError();\n\n    //Get renumbering for halo indices\n    if (this->L2H_maps.size())\n    {\n        //TODO: simplify this, we don't need to check whether it has already been renumbered, there is no overlap between halos\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            //find nodes that are part of the current boundary and they haven't been renumbered yet\n            thrust_wrapper::fill<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, 0);\n            int size = this->L2H_maps[i].size();\n            int num_blocks = std::min(4096, (size + 191) / 192);\n\n            if (size > 0)\n                get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n                        this->L2H_maps[i].raw(),\n                        boundary_renum_flags.raw(), size, global_size /*,rank*/);\n                        cudaCheckError();\n\n            //calculate the local renumbering (within this boundary region) of these nodes\n            thrust_wrapper::exclusive_scan<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, boundary_renum.begin());\n\n            //apply renumbering to the big numbering table\n            if (size > 0)\n                set_unassigned_kernel <<< num_blocks, 192>>>(boundary_renum_flags.raw(),\n                        boundary_renum.raw(),\n                        this->L2H_maps[i].raw(),\n                        renumbering.raw(),\n                        size, max_element, global_size /*,rank*/);\n                        cudaCheckError();\n\n            //update the number of renumbered nodes\n            max_element += boundary_renum[max_size - 1] + boundary_renum_flags[max_size - 1];\n            /*\n             EXAMPLE\n             for neighbor 0 (ID 0)\n             boundary_renum_flags = [0 0], size = 2, flagArray [1 1 1 1 0 0 0 0]\n             get_unassigned_kernel's output: boundary_renum_flags = [1 1] flagArray [1 1 1 1 1 1 0 0]\n             after exclusive scan: boundary_renum [0 1]\n             set_unassigned_kernel updates these arrays and renumbers B2L map:\n             renumbering = [1 2 0 3 4 5 1 1] L2H_maps[0] = [4 5]\n             max_element = 6\n\n             for neighbor 1 (ID 2)\n             get_unassigned_kernels's output: boundary_renum_flags [1 1] flagArray [1 1 1 1 1 1 1 1]\n             after exclusive scan boundary_renum [0 1]\n             set_unassigned_kernel renumbering = [1 2 0 3 4 5 6 7] L2H_maps[1] = [6 7]\n             max_element = 8\n             */\n        }\n\n        cudaCheckError();\n    }\n\n    //apply renumbering to further halo rings too\n    if (rings > 1)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = this->B2L_rings[i][this->B2L_rings[i].size() - 1] - this->B2L_rings[i][1];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            renumber_b2l_maps <<< num_blocks, 128>>>(this->B2L_maps[i].raw() + this->B2L_rings[i][1], renumbering.raw(), size, global_size /*, rank*/);\n        }\n\n        cudaCheckError();\n    }\n\n    /*\n     EXAMPLE\n     renumbers further boundary rings as listed in B2L_maps, since they have not been replaced yet with their renumbered values\n     B2L_maps [[1 2| 0 3][2 3| 1 0]]\n     */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix_owned()\n{\n    int num_neighbors = this->neighbors.size();\n    int size = this->A->get_num_rows();\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    int rings = (this->B2L_rings.size() > 0) ? this->B2L_rings[0].size() - 1 : 0;\n    this->set_num_halo_rings(rings);\n    int diag = this->A->hasProps(DIAG);\n\n    if (diag)\n    {\n        FatalError(\"External diag not supported in classical path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n//\n// Step 1 & 2 - create renumbering\n//\n    this->createRenumbering(this->renumbering);\n    //now we have the full renumbering table in renum, calculate the inverse\n    this->inverse_renumbering.resize(this->renumbering.size());\n\n    if (this->renumbering.size() > 1)\n    {\n        calc_inverse_renumbering <<< std::min(4096, ((int)this->renumbering.size() + 511) / 512), 512 >>> (this->renumbering.raw(), this->inverse_renumbering.raw(), this->renumbering.size());\n        cudaCheckError();\n    }\n\n//\n// Step 4 - calculate number/offset of nodes in the halos from the neighbors, ring by ring\n//\n    this->halo_offsets.resize(num_neighbors + 1);\n    this->halo_offsets[0] = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->halo_offsets[i + 1] = this->halo_offsets[i] + this->L2H_maps[i].size();\n    }\n\n    this->set_num_halo_rows(this->halo_offsets[this->halo_offsets.size() - 1] - size);\n    int nh = this->num_halo_rows();\n    int total_rows = size + nh;\n    cudaCheckError();\n//\n// Step 6 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n//\n    int insert = 0;\n    //recalculate row_offsets\n    IVector new_row_offsets(size + 1);\n\n    if (num_blocks > 0)\n    {\n        calc_rowlen_reorder <<< num_blocks, 512>>>(this->A->row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), size, insert);\n        cudaCheckError();\n    }\n\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.end()),\n                 this->A->col_indices.begin());\n    cudaCheckError();\n    //row_offsets array created by exclusive scan of row sizes\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + size + 1, new_row_offsets.begin());\n    cudaCheckError();\n//\n// Step 7 - consolidate column indices and values\n//\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values((new_nnz + 1 )* this->A->get_block_size(), types::util<ValueTypeA>::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n\n    //reorder based on row permutation\n    if (num_blocks > 0)\n    {\n        reorder_whole_matrix <32> <<< num_blocks, 512>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), this->A->get_block_size(), size, insert);\n        cudaCheckError();\n    }\n\n    //create and append halo rows size\n    //create an identity matrix in CSR format\n    int nnz = this->A->get_num_nz();\n    IVector identity_csr_rows(nh + 1);\n    IVector identity_csr_cols(nh);\n    VVector identity_csr_vals(nh, types::util<ValueTypeA>::get_one()); //needs to be changed to MVector, but this definition is messed up in the header file (should fix later)\n    thrust_wrapper::sequence<AMGX_device>(identity_csr_rows.begin(), identity_csr_rows.end());\n    thrust_wrapper::sequence<AMGX_device>(identity_csr_cols.begin(), identity_csr_cols.end());\n    /*for example, 2x2 identity_csr matrix is created:\n      identity_csr_rows = {   0,   1,   2  }\n      identity_csr_cols = {   0,   1 }\n      identity_csr_vals = { 1.0, 1.0 } */\n    //shift identity tmatrix by size = this->A->get_num_rows();\n    thrust_wrapper::transform<AMGX_device>(identity_csr_rows.begin(), identity_csr_rows.end(), amgx::thrust::constant_iterator<INDEX_TYPE>(nnz), identity_csr_rows.begin(), amgx::thrust::plus<INDEX_TYPE>());\n    thrust_wrapper::transform<AMGX_device>(identity_csr_cols.begin(), identity_csr_cols.end(), amgx::thrust::constant_iterator<INDEX_TYPE>(size), identity_csr_cols.begin(), amgx::thrust::plus<INDEX_TYPE>());\n    /*for example, 2x2 identity_csr matrix is created:\n      identity_csr_rows = {   0,   1,   2  }\n      identity_csr_cols = {size, size+1 }\n      identity_csr_vals = { 1.0, 1.0 } */\n    /* WARNING: you must be very careful with the view you are setting (cuurently the view coming here by default is ALL = FULL). If\n                - classical path is selected then the createOneRingHaloRows -> create_one_ring_halo_rows -> append_halo_rows\n                routine will be called. It will overwrite the halo rows setup here (and will use view OWNED, which will ignore the\n                halo rows setup here, to determine how the new halo rows should be placed).\n                - aggregation path is selected then the extra rows setup here will be used in the R*A*P product, where (in order to match\n                dimensions of R and P) it is assumed that (the local partition) matrix A is square, therefore it must be padded by identity\n                rows at the bottom to compensate for the \"extra\" columns that are outside of the main square part. The old routines for the\n                aggregation path do this padding at the end of the reorder_matrix routine below.  */\n    //ViewType v = this->A->currentView();\n    //this->A->setView(ALL);\n    //Approach 1: use existing routine to append the identity matrix to the existing one\n    //            (seems like too much overhead, also need identity matrix per neighbor)\n    //DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    //prep->append_halo_rows(this->A, identity_csr_rows, identity_csr_cols, identity_csr_vals);\n    //delete prep;\n    //Approach 2: custom for this routine\n    new_row_offsets.resize(total_rows + 1);\n    new_col_indices.resize(nnz + nh);\n    int bsize = this->A->get_block_size();\n    new_values.resize((nnz + nh)*bsize + 1); //extra 1 element stores zero at the end (to follow the original design)\n    //new_values[nnz]=-1;        //marker to track the last element\n    amgx::thrust::copy(identity_csr_rows.begin(), identity_csr_rows.end(), new_row_offsets.begin() + size );\n    amgx::thrust::copy(identity_csr_cols.begin(), identity_csr_cols.end(), new_col_indices.begin() + nnz);\n    amgx::thrust::copy(new_values.begin() + nnz*bsize,    new_values.begin() + (nnz + 1)*bsize, new_values.begin() + (nnz + nh)*bsize);\n    amgx::thrust::copy(identity_csr_vals.begin(), identity_csr_vals.end(),  new_values.begin() + (nnz*bsize));\n    /* WARNING: see above. */\n    this->A->set_num_cols(total_rows);\n    this->A->set_num_rows(total_rows);\n    this->A->col_indices.swap(new_col_indices);\n    new_row_offsets.resize(total_rows + 1);\n    this->A->row_offsets.swap(new_row_offsets);\n    new_row_offsets.swap(this->old_row_offsets);\n    this->A->values.swap(new_values);\n    this->A->m_seq_offsets.resize(total_rows + 1);\n    thrust_wrapper::sequence<AMGX_device>(this->A->m_seq_offsets.begin(), this->A->m_seq_offsets.end());\n    cudaCheckError();\n    //TODO: only do this if AMG_Config matrix_halo_exchange!=2\n    this->A->delProps(COO);\n    if (!insert)\n    {\n        this->A->computeDiagonal();\n    }\n\n    this->set_initialized(this->A->row_offsets);\n    this->A->setView(OWNED);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix()\n{\n    int num_neighbors = this->neighbors.size();\n\n    if (num_neighbors == 0) { return; }\n\n    int size = this->A->get_num_rows();\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    int rings = this->B2L_rings[0].size() - 1;\n    this->set_num_halo_rings(rings);\n    int diag = this->A->hasProps(DIAG);\n    std::vector<Matrix<TConfig_d> > &halo_rows = *this->halo_rows;\n    std::vector<DistributedManager<TConfig_d> > &halo_btl = *this->halo_btl;\n    /*\n     EXAMPLE\n     The example matrix, on partition 1 arrives at this point with the following state:\n     num_rings=2\n     A.num_rows = 4; A.num_nz = 20\n     A.row_offsets = [0 4 11 15 20]\n     A.col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n     num_neighbors=2; neighbors = [0 2]\n     B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n     L2H_maps (and halo_lists) [[4 5][6 7]]\n\n     With the exchange halo rows:\n     halo_btl[0] (received from neighbor ID 0)\n     global_id = 0; base_index=0; index_range=6; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [2 3| 0 1] L2H_maps = [4 5]\n     halo_rows[0].row_offsets = [0 5 13 17 21]\n     halo_rows[0].col_indices = [1 2 3 4 5\n                      0 1 2 3 4 5 6 7\n                      0 1 3 6\n                      0 1 2 3]\n\n     halo_btl[1] (received from neighbor ID 2)\n     global_id = 2; base_index=0; index_range=8; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [1 2| 0 3] L2H_maps = [6 7]\n     halo_rows[1].row_offsets = [0 4 11 16 20]\n     halo_rows[1].col_indices = [7 1 2 3\n                      5 6 7 0 1 2 3\n                      4 5 0 2 3\n                      0 1 2 3]\n     */\n//\n// Step 1 & 2 - create renumbering\n//\n    this->createRenumbering(this->renumbering);\n    cudaCheckError();\n    /*\n     EXAMPLE\n     this->renumbering = [1 2 0 3 4 5 6 7]\n     B2L_maps = [[1 2| 0 3][2 3| 1 0]]\n     L2H_maps = [[4 5][6 7]]\n     */\n//\n// Step 3 - given a full renumbering of owned nodes, calculate inverse renumbering\n//\n    //now we have the full renumbering table in renum, calculate the inverse\n    this->inverse_renumbering.resize(this->renumbering.size());\n    calc_inverse_renumbering <<< std::min(4096, ((int)this->renumbering.size() + 511) / 512), 512 >>> (this->renumbering.raw(), this->inverse_renumbering.raw(), this->renumbering.size());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     this->inverse_renumbering = [2 0 1 3 4 5 6 7]\n     */\n//\n// Step 4 - calculate number/offset of nodes in the halos from the neighbors, ring by ring\n//\n    this->halo_offsets.resize(rings * num_neighbors + 1, 0);\n\n    for (int ring = 0; ring < rings; ring++)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            this->halo_offsets[ring * num_neighbors + i] = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n        }\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(this->halo_offsets.begin(), this->halo_offsets.end(), this->halo_offsets.begin(), size);\n    cudaCheckError();\n    this->set_num_halo_rows(this->halo_offsets[this->halo_offsets.size() - 1] - size);\n    int total_rows = size + this->num_halo_rows();\n\n    if (total_rows < this->renumbering.size()) { FatalError(\"total rows < renumbering.size(), send/recv maps should cover all matrix halo columns\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    if (total_rows > this->renumbering.size())\n    {\n        this->A->getResources()->warning(\"# owned nodes + # halo nodes > matrix columns: send/recv maps have some unreferences halo indices, they are not directly connected to our partition and therefore we won't compute them, please use 2-ring comms maps if you want to specify 2nd ring neighbors\");\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     halo_offsets [2 2 2 2]\n     after exclusive scan: 4 + [0 2 4 6 8] = [4 6 8 10 12]\n     num_halo_rows = 8, total_rows = 12\n     */\n//\n// Step 5 - create big mapping table of all halo indices we received (this may use a little too much memory)\n//\n    //count number of fine rows of neighbors\n    amgx::thrust::host_vector<INDEX_TYPE> neighbor_rows(num_neighbors + 1);\n    int max_num_rows = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neighbor_rows[i] = halo_btl[i].index_range();\n        max_num_rows = max_num_rows > halo_rows[i].get_num_rows() ? max_num_rows : halo_rows[i].get_num_rows();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows.begin());\n    cudaCheckError();\n    int total_rows_of_neighbors = neighbor_rows[num_neighbors];\n    /*\n     EXAMPLE\n     neigbor_rows = [0 6 14]\n     total_rows_of_neighbors = 14\n     */\n    IVector halo_mapping(total_rows_of_neighbors);\n    thrust_wrapper::fill<AMGX_device>(halo_mapping.begin(), halo_mapping.end(), -1);\n    cudaCheckError();\n\n    //ring by ring, neighbor by neighbor assign sequentially increasing numbers for halo nodes\n    for (int ring = 0; ring < rings; ring++)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //This renumbering has to result in the same renumbering that comes out of L2H renumbering\n            create_halo_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                    halo_btl[i].B2L_maps[0].raw() + halo_btl[i].B2L_rings[0][ring],\n                    halo_btl[i].base_index(), this->halo_offsets[ring * num_neighbors + i], size);\n            cudaCheckError();\n            /*\n             EXAMPLE\n             ring 0 neighbor 0 - halo_btl[0].B2L_maps[0] = [2 3| 0 1] halo_btl[0].L2H_maps = [4 5]\n            halo_mapping = [-1 -1 4 5 -1 -1 |-1 -1 -1 -1 -1 -1 -1 -1]\n            ring 0 neighbor 1 - halo_btl[1].B2L_maps[0] = [1 2| 0 3] halo_btl[1].L2H_maps = [6 7]\n            halo_mapping = [-1 -1 4 5 -1 -1 |-1 6 7 -1 -1 -1 -1 -1]\n            ring 1 neighbor 0 - halo_btl[0].B2L_maps[0] = [2 3| 0 1] halo_btl[0].L2H_maps = [4 5]\n            halo_mapping = [8 9 4 5 -1 -1 |-1 6 7 -1 -1 -1 -1 -1]\n            ring 1 neighbor 1 - halo_btl[1].B2L_maps[0] = [1 2| 0 3] halo_btl[1].L2H_maps = [6 7]\n            halo_mapping = [8 9 4 5 -1 -1 |10 6 7 11 -1 -1 -1 -1]\n\n             */\n        }\n    }\n\n    cudaCheckError();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = halo_btl[i].L2H_maps[0].size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        //Map the column indices of the halo rows that point back to boundary nodes\n        apply_h2l2b_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                halo_btl[i].L2H_maps[0].raw(),\n                halo_btl[i].base_index(), this->B2L_maps[i].raw(), size);\n        cudaCheckError();\n        /*\n         EXAMPLE\n         neighbor 0 - mapping back to our own (boundary) indices\n         halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 -1 -1]\n         neighbor 1 - mapping back to our own (boundary) indices\n        halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n         */\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     neighbor_rows = [0 6 14]\n     halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n     The first part (0-6) of halo_mapping gives a local index for all the indices that we want to know about in halo_btl[0]\n     The second part (7-14) gives local indices for halo_btl[1], that is both halo ring there, and the column indices representing vertices in this partition's boundary.\n     Note that it does not give indices (-1) for vertices 5 and 6 in neighbor 1 (ID 2), which are column indices connecting it to neighbor 0, hence the two halo regions are not connected\n     */\n//\n// Step 6 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n//\n    int insert = 0;\n\n    if (this->A->hasProps(DIAG) && insertDiagonals) { insert = 1; }\n\n    diag = diag && !insertDiagonals;\n    //recalculate row_offsets\n    IVector new_row_offsets(size + this->num_halo_rows() + 1);\n    calc_rowlen_reorder <<< num_blocks, 512>>>(this->A->row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), size, insert);\n    cudaCheckError();\n    IVector neighbor_rows_d(num_neighbors + 1);\n    amgx::thrust::copy(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows_d.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     get row length according to renumbering\n     new_row_offsets = [4 4 7 5 0 0 0 0 0 0 0 0 0]\n     */\n    //map column indices of my own matrix\n    /*map_col_indices<4><<<num_blocks, 512>>>(this->A->row_offsets.raw(),\n                                         this->A->col_indices.raw(),\n                                         this->renumbering.raw(),\n                                         this->halo_ranges.raw(),\n                                         halo_mapping.raw(),\n                                         neighbor_rows_d.raw(),\n                                         this->base_index(), num_neighbors, size);*/\n                                         cudaCheckError();\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.end()),\n                 this->A->col_indices.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     use this->renumbering = [1 2 0 3 4 5 6 7]\n     to map old column indices to new column indices (i.e. according to interior - boundary - halo separation), but do not reshuffle them into their place yet\n     A.col_indices = [4 0 1 2\n                  4 5 0 1 2 3 7\n                  0 1 2 3\n                  1 2 3 6 7]\n    becomes\n    A.col_indices = [4 1 2 0\n                  4 5 1 2 0 3 7\n                  1 2 0 3\n                  2 0 3 6 7]\n\n     */\n    cudaCheckError();\n    IVector temp_row_len(max_num_rows);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //map column indices of halo matrices and count of nonzeros we will keep\n        int size = halo_rows[i].get_num_rows();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        map_col_indices_and_count_rowlen<4> <<< num_blocks, 128, 128 * sizeof(INDEX_TYPE)>>>(\n            halo_rows[i].row_offsets.raw(),\n            halo_rows[i].col_indices.raw(),\n            temp_row_len.raw(),\n            halo_mapping.raw() + neighbor_rows[i],\n            size, insert);\n        cudaCheckError();\n\n        //number of nonzeros per row copied into big row sizes array\n        for (int ring = 0; ring < rings; ring++)\n        {\n            amgx::thrust::copy(temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring], temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring + 1], new_row_offsets.begin() + this->halo_offsets[ring * num_neighbors + i]);\n        }\n\n        cudaCheckError();\n        /*\n         EXAMPLE\n         halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n         look at halo row matrices, and halo_mapping, count column indices that do not map to -1 and map them to their new, local index\n         halo_rows[0].col_indices = [1 2 3 4 5\n                    0 1 2 3 4 5 6 7\n                    0 1 3 6\n                    0 1 2 3]\n        becomes\n        halo_rows[0].col_indices = [9 4 5 1 2\n                    8 9 4 5 1 2 -1 -1\n                    8 9 5 -1\n                    8 9 4 5]\n        with temp_row_len = [5 6 3 4] copied into new_row_offsets: [4 4 7 5| 5 6| 0 0| 3 4| 0 0 0]\n\n        halo_rows[1].col_indices = [7 1 2 3\n                    5 6 7 0 1 2 3\n                    4 5 0 2 3\n                    0 1 2 3]\n        becomes\n        halo_rows[1].col_indices = [3 6 7 11\n                    -1 2 3 10 6 7 11\n                    -1 -1 10 7 11\n                    10 6 7 11]\n        with temp_row_len = [4 6 3 4] copied into new_row_offsets: [4 4 7 5| 5 6| 4 6| 3 4| 3 4 0]\n         */\n    }\n\n    cudaCheckError();\n    //row_offsets array created by exclusive scan of row sizes\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + size + this->num_halo_rows() + 1, new_row_offsets.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     Exclusive scan to get new_row_offsets array:\n     new_row_offsets = [0 4 8 15 20| 25 31| 35 41| 44 48| 51 55]\n     */\n//\n// Step 7 - consolidate column indices and values\n//\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values((new_nnz + 1 + diag * (total_rows - 1))* this->A->get_block_size(), types::util<ValueTypeA>::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n    //reorder based on row permutation\n    reorder_whole_matrix <32> <<< num_blocks, 512>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), this->A->get_block_size(), size, insert);\n    cudaCheckError();\n\n    if (diag)\n    {\n        //reorder based on row permutation\n        reorder_vector_values <<< num_blocks, 512>>>(new_values.raw() + new_row_offsets[total_rows]*this->A->get_block_size(),\n                this->A->values.raw() + this->A->row_offsets[size]*this->A->get_block_size(),\n                this->renumbering.raw(),\n                this->A->get_block_size(), size);\n        cudaCheckError();\n    }\n\n    int cumulative_num_rows = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        for (int ring = 0; ring < rings; ring++)\n        {\n            int num_rows = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n            int num_blocks = std::min(4096, (num_rows + 127) / 128);\n            //copy in nonzeros that we are keeping\n            //TODO: access pattern - should be implemented with warp-wide scans to decide which nonzeros we are keeping and where the rest is going\n            reorder_whole_halo_matrix <<< num_blocks, 128>>>(halo_rows[i].row_offsets.raw() + halo_btl[i].B2L_rings[0][ring],\n                    halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(),\n                    new_row_offsets.raw() + this->halo_offsets[ring * num_neighbors + i],\n                    new_col_indices.raw(), new_values.raw(), NULL, this->A->get_block_size(), num_rows,\n                    insert, this->halo_offsets[ring * num_neighbors + i], halo_btl[i].B2L_rings[0][ring], halo_btl[i].B2L_rings[0][rings]);\n                    cudaCheckError();\n\n            if (diag)\n            {\n                amgx::thrust::copy(halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring])*this->A->get_block_size(),\n                             halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring + 1])*this->A->get_block_size(),\n                             new_values.begin() + (new_row_offsets[total_rows] + cumulative_num_rows)*this->A->get_block_size());\n                cumulative_num_rows += num_rows;\n            }\n        }\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     copy everything in place, dropping -1 column indices in the halo and reordering the owned rows\n     new_row_offsets = [0 4 8 15 20| 25 31| 35 41| 44 48| 51 55]\n     new_col_indices = [1 2 0 3\n                  4 1 2 0\n                  4 5 1 2 0 3 7\n                  2 0 3 6 7 -end of owned\n                  9 4 5 1 2\n                  8 9 4 5 1 2 - end of neighbor 0 ring 0\n                  3 6 7 11\n                  2 3 10 6 7 11 - end of neighbor 1 ring 0\n                  8 9 5\n                  8 9 4 5 - end of neighbor 0 ring 1\n                  10 7 11\n                  10 6 7 11] - end of neighbor 1 ring 1\n\n     */\n    this->A->set_num_cols(total_rows);\n    this->A->set_num_rows(size);\n    this->A->col_indices.swap(new_col_indices);\n    new_row_offsets.resize(total_rows + 1);\n    this->A->row_offsets.swap(new_row_offsets);\n    new_row_offsets.swap(this->old_row_offsets);\n    this->A->values.swap(new_values);\n    this->A->m_seq_offsets.resize(total_rows + 1);\n    thrust_wrapper::sequence<AMGX_device>(this->A->m_seq_offsets.begin(), this->A->m_seq_offsets.end());\n\n    if (insert)\n    {\n        this->A->delProps(DIAG);\n        this->A->diag.resize(total_rows);\n        amgx::thrust::copy(this->A->row_offsets.begin(), this->A->row_offsets.end() - 1, this->A->diag.begin());\n    }\n\n    cudaCheckError();\n    delete this->halo_rows;\n    delete this->halo_btl;\n    //set halo_rows and halo_btl to NULL to avoid a potential double free situation in the future\n    this->halo_rows = NULL;\n    this->halo_btl = NULL;\n    this->A->delProps(COO);\n    this->A->set_initialized(1);\n\n    //TODO: only do this if AMG_Config matrix_halo_exchange!=2\n    if (!insert) { this->A->computeDiagonal(); }\n\n    this->A->setView(OWNED);\n}\n\n//function object (functor) for thrust calls (it is a unary operator to add a constant)\ntemplate<typename T>\nclass add_constant_op\n{\n        const T c;\n    public:\n        add_constant_op(T _c) : c(_c) {}\n        __host__ __device__ T operator()(const T &x) const\n        {\n            return x + c;\n        }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::obtain_shift_l2g_reordering(index_type n, I64Vector_d &l2g, IVector_d &p, IVector_d &q)\n{\n    /* WARNING: Exchange halo of the inverse_reordering, which is implicitly based on the local_to_global_map (l2g).\n                Notice that it is implicit in the exchange_halo routine, since you are getting exactly the vector\n                halo elements, which are exactly the elements you need. They however must be shifted by the partition\n                starting points (starting global row indices, which are containe din array part_offsets).\n                This allows us to avoid constructing the global vector for inverse permutation,\n                as is done in reference MATLAB code. */\n    //Recall that part_offsets provide the starting point (global row index) of every partition, in other words,\n    //they contain the prefix sum of number of rows assigned to each partition. Also, notice that part_offsets and\n    //part_offsets_h have the same values on device and host, respectively. See below few lines for details:\n    index_type tag = 1 * 133 + 3 * 7 + 0; //some random number for the tag\n    index_type l = p.size();\n    q.resize(l);\n    amgx::thrust::copy     (p.begin(), p.end(),     q.begin());\n    thrust_wrapper::transform<AMGX_device>(q.begin(), q.end(),     q.begin(), add_constant_op<index_type>(this->part_offsets[this->global_id()]));\n    this->exchange_halo(q, tag);\n    thrust_wrapper::sequence <AMGX_device>(q.begin(), q.begin() + n);\n    thrust_wrapper::transform<AMGX_device>(q.begin(), q.begin() + n, q.begin(), add_constant_op<index_type>(this->part_offsets[this->global_id()]));\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::unpack_partition(index_type *Bp, index_type *Bc, mat_value_type *Bv)\n{\n    index_type l, n, nnz, offset, block_dimx, block_dimy;\n    index_type     *ir;\n    index_type     *Ap;\n    index_type     *Ac;\n    mat_value_type *Av;\n    IVector q;\n    //some initializations\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &n);\n    this->A->getNnzForView(OWNED, &nnz);\n\n    block_dimx = this->A->get_block_dimx();\n    block_dimy = this->A->get_block_dimy();\n\n    l  = this->inverse_renumbering.size();\n    ir = this->inverse_renumbering.raw();\n    Ap = this->A->row_offsets.raw();\n    Ac = this->A->col_indices.raw();\n    Av = this->A->values.raw();\n    //(i) reorder the matrix back (into mixed interior-boundary nodes)\n    //applies to rows and columns (out-of-place)\n    reorder_partition<index_type, mat_value_type, true, true>\n\t(n, nnz, Ap, Ac, Av, Bp, Bc, Bv, l, ir, block_dimx, block_dimy);\n    cudaCheckError();\n    //obtain reordering q that combines the shift of the diagonal block with the off-diagonal block indices conversion from local to global\n    this->obtain_shift_l2g_reordering(n, this->local_to_global_map, this->inverse_renumbering, q);\n    cudaCheckError();\n    //(ii) reorder the matrix back (shift the diagonal block and convert off-diagonal block column indices from local to global)\n    //applies columns only (in-place)\n    reorder_partition<index_type, mat_value_type, false, true>\n    (n, nnz, Bp, Bc, Bv, Bp, Bc, Bv, q.size(), q.raw(), block_dimx, block_dimy);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createNeighToDestPartMap(IVector_h &neigh_to_part, IVector_h &neighbors, IVector_h &destination_part, int num_neighbors)\n{\n    neigh_to_part.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neigh_to_part[i] = destination_part[neighbors[i]];\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createConsolidatedNeighToPartMap(IVector_h &cons_neigh_to_part, IVector_h &neigh_to_part, int my_destination_part, IVector_h &destination_part, int &num_cons_neighbors)\n{\n    // input: non-initialized cons_neigh_to_part\n    //        fine_neigh_to_part\n    //        my_destination_part\n    // output: cons_neigh_to_part\n    //         num_cons_neighbors\n    cons_neigh_to_part = neigh_to_part;\n    amgx::thrust::sort(cons_neigh_to_part.begin(), cons_neigh_to_part.end());\n    cudaCheckError();\n    cons_neigh_to_part.erase(amgx::thrust::unique(cons_neigh_to_part.begin(), cons_neigh_to_part.end()), cons_neigh_to_part.end());\n    // Remove if fine_neigh maps to same coarse partition\n    cons_neigh_to_part.erase(amgx::thrust::remove_if(cons_neigh_to_part.begin(), cons_neigh_to_part.end(), is_my_part(my_destination_part)), cons_neigh_to_part.end());\n    num_cons_neighbors = cons_neigh_to_part.size();\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createNeighToConsNeigh(IVector_h &neigh_to_cons_neigh, IVector_h &cons_neigh_to_part, IVector_h &neigh_to_part, int my_destination_part, int &num_neighbors)\n{\n    neigh_to_cons_neigh.resize(num_neighbors);\n    amgx::thrust::lower_bound(cons_neigh_to_part.begin(), cons_neigh_to_part.end(), neigh_to_part.begin(), neigh_to_part.end(), neigh_to_cons_neigh.begin());\n    cudaCheckError();\n\n    // Flagging fine neighbors that go to same partition (haven't been found in previous step)\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        if ( neigh_to_part[i] == my_destination_part)\n        {\n            neigh_to_cons_neigh[i] = -1;\n        }\n    }\n}\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consB2Lmaps(std::vector<IVector_hd> &dest_coarse_B2L_maps, std::vector<IVector_hd> &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    //Merge B2L fine maps per coarse destination\n    dest_coarse_B2L_maps.resize(num_coarse_neighbors);\n    std::vector<int> dest_coarse_B2L_maps_scratch_sizes(num_coarse_neighbors, 0);\n    int my_id = this->global_id();\n\n    // Loop over the fine neighbors, to compute size of each dest_coarse_B2L_maps\n    for (int i = 0; i < num_fine_neighbors; i++)\n    {\n        int k = fine_neigh_to_coarse_neigh[i];\n\n        if (k != -1)\n        {\n            dest_coarse_B2L_maps_scratch_sizes[k] += coarse_B2L_maps[i].size();\n        }\n    }\n\n    // Now fill dest_coarse_B2L_maps\n    for (int k = 0; k < num_coarse_neighbors; k++)\n    {\n        dest_coarse_B2L_maps[k].resize( dest_coarse_B2L_maps_scratch_sizes[k] );\n        // Reset sizes to 0 (fill use as offset in next loop);\n        dest_coarse_B2L_maps_scratch_sizes[k] = 0;\n    }\n\n    for (int i = 0; i < num_fine_neighbors; i++)\n    {\n        int k = fine_neigh_to_coarse_neigh[i];\n\n        if (k != -1)\n        {\n            int offset = dest_coarse_B2L_maps_scratch_sizes[k];\n            amgx::thrust::copy(coarse_B2L_maps[i].begin(), coarse_B2L_maps[i].end(), dest_coarse_B2L_maps[k].begin() + offset);\n            dest_coarse_B2L_maps_scratch_sizes[k] += coarse_B2L_maps[i].size();\n        }\n    }\n\n    cudaCheckError();\n    int max_size = 0;\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        int size = dest_coarse_B2L_maps[i].size();\n\n        if (size > max_size) { max_size = size; }\n    }\n\n    // Remove duplicates (aggregates in boundary that go to same merged partition)\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        int size = dest_coarse_B2L_maps[i].size();\n        amgx::thrust::sort(dest_coarse_B2L_maps[i].begin(), dest_coarse_B2L_maps[i].begin() + size);\n        index_type num_unique = amgx::thrust::unique(dest_coarse_B2L_maps[i].begin(), dest_coarse_B2L_maps[i].begin() + size) - dest_coarse_B2L_maps[i].begin();\n        dest_coarse_B2L_maps[i].erase(dest_coarse_B2L_maps[i].begin() + num_unique, dest_coarse_B2L_maps[i].end());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeConsolidatedOffsets(const int my_id, const int my_destination_part, const bool is_root_partition, const int num_interior_rows, const int num_boundary_rows, IVector_h_vector &vertex_counts, const IVector_h &parts_to_consolidate, const int num_parts_to_consolidate, int &interior_offset, int &boundary_offset, int &total_interior_rows_in_merged, int &total_boundary_rows_in_merged, int &total_rows_in_merged, DistributedComms<TConfig> *comms)\n{\n    IVector_h my_offsets(4);\n    IVector_h my_sizes(2);\n    my_sizes[0] = num_interior_rows;\n    my_sizes[1] = num_boundary_rows;\n\n    if (!is_root_partition)\n    {\n        //Send number of interior and boundary nodes to root\n        comms->send_vector_async(my_sizes, my_destination_part, 777);\n        comms->recv_vector(my_offsets, my_destination_part, 778);\n        comms->send_vector_wait_all(my_sizes);\n    }\n    else\n    {\n        vertex_counts.resize(num_parts_to_consolidate);\n        IVector_h child_sizes(2);\n        IVector_h offsets_interior(num_parts_to_consolidate);\n        IVector_h offsets_boundary(num_parts_to_consolidate);\n        int count_int = 0;\n        int count_bdy = 0;\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            if (parts_to_consolidate[i] == my_id)\n            {\n                child_sizes = my_sizes;\n            }\n            else\n            {\n                comms->recv_vector(child_sizes, parts_to_consolidate[i], 777);\n            }\n\n            //Do a simple cumulative sum to determine total number of interior/boundary rows and their offsets on a per contributing partition basis\n            offsets_interior[i] = count_int;\n            offsets_boundary[i] = count_bdy;\n            count_int += child_sizes[0];\n            count_bdy += child_sizes[1];\n            //Save them\n            vertex_counts[i].resize(2);\n            vertex_counts[i][0] = child_sizes[0];\n            vertex_counts[i][1] = child_sizes[1];\n        }\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            //Send back to contributing partitions\n            IVector_h offsets_to_send(4);\n            offsets_to_send[0] = offsets_interior[i];\n            offsets_to_send[1] = offsets_boundary[i];\n            offsets_to_send[2] = count_int;\n            offsets_to_send[3] = count_bdy;\n\n            if (parts_to_consolidate[i] == my_id)\n            {\n                my_offsets = offsets_to_send;\n            }\n            else\n            {\n                comms->send_vector(offsets_to_send, parts_to_consolidate[i], 778);    // cannot make async, rewriting internal buffer\n            }\n        }\n    }\n\n    interior_offset = my_offsets[0];\n    boundary_offset = my_offsets[1] + my_offsets[2] - num_interior_rows;\n    total_interior_rows_in_merged = my_offsets[2];\n    total_boundary_rows_in_merged = my_offsets[3];\n    total_rows_in_merged = my_offsets[2] + my_offsets[3];\n}\n\n\n\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consB2LmapsOnRoot(int &num_consolidated_neighbors, std::vector<IVector_hd> &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, std::vector<IVector_hd> &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    // TODO: it is possible to optimize exchanges, for example fuse recv_vector in recreating coarse neigbours\n\n    //  output: num_consolidated_neighbor, consolidated_B2L_maps, consolidated_coarse_ids\n    //  input:  dest_coarse_B2L_maps, is_root_partition, my_id, my_destination_part, num_fine_parts_to_consolidate, num_coarse_neighbors, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh\n    if (my_destination_part != my_id)\n    {\n        //if not root, send coarse neighbor list using fine indices and the corresponding boundary lists\n        IVector_h num_coarse_neigh(1);\n        num_coarse_neigh[0] = num_coarse_neighbors;\n        comms->send_vector_async(num_coarse_neigh, my_destination_part, 1111);\n        comms->send_vector_async(coarse_neigh_to_fine_part, my_destination_part, 2222);\n        comms->send_vector_async(num_bdy_per_coarse_neigh, my_destination_part, 3333);\n\n        for (int i = 0; i < num_coarse_neighbors; i++)\n        {\n            comms->send_vector_async(dest_coarse_B2L_maps[i], my_destination_part, 4444 + i) ;\n        }\n\n        comms->send_vector_wait_all(num_coarse_neigh);\n        comms->send_vector_wait_all(coarse_neigh_to_fine_part);\n        comms->send_vector_wait_all(num_bdy_per_coarse_neigh);\n\n        for (int i = 0; i < num_coarse_neighbors; i++)\n        {\n            comms->send_vector_wait_all(dest_coarse_B2L_maps[i]) ;\n        }\n    }\n\n    if (is_root_partition)\n    {\n        IVector_h num_coarse_ids_from_part(fine_parts_to_consolidate);\n        IVector_h_vector coarse_ids_from_part(num_fine_parts_to_consolidate);\n        IVector_h_vector num_coarse_neigh_bdys_from_part(num_fine_parts_to_consolidate);\n        //If root, receive sizes, and resize receive buffers\n        int total_num_coarse_ids = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h temp(1);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(temp, current_part, 1111);\n            }\n            else\n            {\n                temp[0] = num_coarse_neighbors;\n            }\n\n            num_coarse_ids_from_part[i] = temp[0];\n            coarse_ids_from_part[i].resize(temp[0]);\n            num_coarse_neigh_bdys_from_part[i].resize(temp[0]);\n            total_num_coarse_ids += temp[0];\n        }\n\n        //Create a neighbor list for the consolidated coarse matrix, by merging coarse neighbor lists from partitions that are being merged\n        consolidated_coarse_ids.resize(total_num_coarse_ids);\n        int count = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            // Get from each partition the coarse partition ids in their B2L maps\n            if (current_part != my_id)\n            {\n                comms->recv_vector(coarse_ids_from_part[i], current_part, 2222);\n                comms->recv_vector(num_coarse_neigh_bdys_from_part[i], current_part, 3333);\n            }\n            else\n            {\n                coarse_ids_from_part[i] = coarse_neigh_to_fine_part;\n                num_coarse_neigh_bdys_from_part[i] = num_bdy_per_coarse_neigh;\n            }\n\n            amgx::thrust::copy(coarse_ids_from_part[i].begin(), coarse_ids_from_part[i].end(), consolidated_coarse_ids.begin() + count);\n            count += num_coarse_ids_from_part[i];\n        }\n\n        cudaCheckError();\n        //eliminate duplicates\n        amgx::thrust::sort(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end());\n        cudaCheckError();\n        consolidated_coarse_ids.erase(amgx::thrust::unique(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end()), consolidated_coarse_ids.end());\n        cudaCheckError();\n        num_consolidated_neighbors = consolidated_coarse_ids.size();\n        IVector_h_vector coarse_ids_from_part_to_consolidated_neighbor(num_fine_parts_to_consolidate);;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            coarse_ids_from_part_to_consolidated_neighbor[i].resize(num_coarse_ids_from_part[i]);\n            amgx::thrust::lower_bound(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end(), coarse_ids_from_part[i].begin(), coarse_ids_from_part[i].end(), coarse_ids_from_part_to_consolidated_neighbor[i].begin());\n        }\n\n        cudaCheckError();\n        // Map each coarse partition to new coarse ID\n        consolidated_B2L_maps.resize(num_consolidated_neighbors);\n        IVector_h consolidated_B2L_maps_sizes(num_consolidated_neighbors);\n        // Offset in the consolidated_B2L_maps\n        IVector_h_vector coarse_ids_offsets(num_fine_parts_to_consolidate);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            consolidated_B2L_maps_sizes[i] = 0;\n        }\n\n        // Compute the size of each consolidated_B2L_maps and offsets into it, where we will receive the parts coming from partitions that are getting merged into this one\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            coarse_ids_offsets[i].resize(num_coarse_ids_from_part[i]);\n\n            for (int j = 0; j < num_coarse_ids_from_part[i]; j++)\n            {\n                int coarse_id = coarse_ids_from_part[i][j];\n                int k = num_coarse_neigh_bdys_from_part[i][j];\n                coarse_ids_offsets[i][j] = consolidated_B2L_maps_sizes[ coarse_ids_from_part_to_consolidated_neighbor[i][j] ];\n                consolidated_B2L_maps_sizes[ coarse_ids_from_part_to_consolidated_neighbor[i][j] ] += k;\n            }\n        }\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            consolidated_B2L_maps[i].resize(consolidated_B2L_maps_sizes[i]);\n        }\n\n        // Receive the B2L maps from each child partition, concatenate them (gets sorted outside)\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            for (int j = 0; j < num_coarse_ids_from_part[i]; j++)\n            {\n                int my_coarse_neigh = coarse_ids_from_part_to_consolidated_neighbor[i][j];\n                int offset = coarse_ids_offsets[i][j];\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector( consolidated_B2L_maps[my_coarse_neigh], current_part, 4444 + j, offset,  num_coarse_neigh_bdys_from_part[i][j]); //Need to do proper tagging here, otherwise messages from the same source would get mixed up\n                }\n                else\n                {\n                    amgx::thrust::copy(dest_coarse_B2L_maps[j].begin(), dest_coarse_B2L_maps[j].end(), consolidated_B2L_maps[my_coarse_neigh].begin() + offset);\n                }\n            }\n        }\n\n        cudaCheckError();\n    }\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateAndRenumberHalos(IVector_h &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_ids, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    consAndRenumberHalos(aggregates, manager_halo_offsets, halo_offsets, neighbors, num_fine_neighbors, consolidated_coarse_ids, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, num_all_aggregates, comms);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateAndRenumberHalos(IVector_d &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_ids, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    consAndRenumberHalos(aggregates, manager_halo_offsets, halo_offsets, neighbors, num_fine_neighbors, consolidated_coarse_ids, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, num_all_aggregates, comms);\n}\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consAndRenumberHalos(IVector_hd &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_neigh_to_fine_part, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    /*\n         * EXAMPLE 2\n         This example is independent from the previous ones.\n         Say partition 0 and 1 are merging (into 0) partition 0 is neighbors with 1,2,3 and partition 1 is neighbors with 0,3,4\n         Partitions 3 and 4 are merging (into partition 3) and partition 2 is not merging with anyone.\n         This example details the renumbering of halo indices on partition 0 and partition 1.\n         aggregates on partition 0:\n         [(fine interior nodes) (fine boundary nodes) (fine halo from part 1) (fine halo from part 2) (fine halo from part 3)]\n         [(fine interior nodes) (fine boundary nodes) (13 13 15) (12 15 17) (14 16 18)]\n         aggregates on partition 1:\n         [(fine interior nodes) (fine boundary nodes) (fine halo from part 0) (fine halo from part 3) (fine halo from part 4)]\n         [(fine interior nodes) (fine boundary nodes) (14 16 17) (18 19 19) (15 15 17)]\n\n         manager_halo_offsets on partition 0:\n         [22 25 28 31]\n         manager_halo_offsets on partition 1:\n         [20 23 26 29]\n         halo_offsets on both partitions are uninitialised: [0 0 0] and [0 0]\n         neighbors on partition 0: [1 2 3] partition 1: [0 3 4]\n         num_fine_neighbors partition 0: 3 partition 1: 3\n         consolidated_coarse_neigh_to_fine_part partition 0: [2 3] partition 1: [3]\n         num_consolidated_neighbors partition 0: 2 partition 1: 1\n         destination_part [0 0 2 3 3]\n         my_destination_part partition 0: 0 partition 1: 0\n         is_root_partition partition 0: true partition 1: false\n         fine_parts_to_consolidate partition 0: [0 1]\n         num_fine_parts_to_consolidate partition 0: 2\n         num_parts 5\n         my_id partition 0: 0 partition 1: 1\n         total_rows_in_merged partition 0 and 1: 24 (=sum of the two below)\n         num_all_aggregates partition partition 0: 13 partition 1: 11 - will be updated with the number of halo aggregates\n         */\n    //\n    // Step 9.2 - com up with nonmerge lists\n    //\n    int num_fine_nonmerge_neighbors;// = fine_nonmerge_neighbors.size();\n    //NUmber of neighbors we are not merging with\n    num_fine_nonmerge_neighbors = 0;\n\n    for (int i = 0 ; i < num_fine_neighbors; i++)\n    {\n        if (destination_part[neighbors[i]] != my_destination_part)\n        {\n            num_fine_nonmerge_neighbors++;\n        }\n    }\n\n    IVector_h halo_sizes(num_fine_nonmerge_neighbors);\n    IVector_h fine_nonmerge_neigh_to_cons_fine_part(num_fine_nonmerge_neighbors);\n    IVector_h fine_nonmerge_neighbor_to_fine_neighbor(num_fine_nonmerge_neighbors);\n    num_fine_nonmerge_neighbors = 0;\n\n    for (int i = 0 ; i < num_fine_neighbors; i++)\n    {\n        if (destination_part[neighbors[i]] != my_destination_part)\n        {\n            halo_sizes[num_fine_nonmerge_neighbors] = manager_halo_offsets[i + 1] - manager_halo_offsets[i];\n            fine_nonmerge_neighbor_to_fine_neighbor[num_fine_nonmerge_neighbors] = i;\n            fine_nonmerge_neigh_to_cons_fine_part[num_fine_nonmerge_neighbors] = destination_part[neighbors[i]];\n            num_fine_nonmerge_neighbors++;\n        }\n    }\n\n    /*\n     * EXAMPLE 2\n     num_fine_nonmerge_neighbors partition 0: 2 partition 1: 2\n     fine_nonmerge_neighbor_to_fine_neighbor partition 0: [1 2] partition 1: [1 2]\n     fine_nonmerge_neigh_to_cons_fine_part partition 0: [2 3] partition 1: [3 3]\n     halo_sizes partition 0: [3 3] partition 1: [3 3]\n     */\n\n    //Send them to root along with the halo parts of the aggregates vector\n    if (!is_root_partition)\n    {\n        IVector_h num_fine_nonmerge_neigh(1);\n        num_fine_nonmerge_neigh[0] = num_fine_nonmerge_neighbors;\n        // TODO: async? might be faster.\n        comms->send_vector(num_fine_nonmerge_neigh, my_destination_part, 1111);\n        comms->send_vector(halo_sizes, my_destination_part, 2222);\n        comms->send_vector(fine_nonmerge_neigh_to_cons_fine_part, my_destination_part, 3333);\n\n        // Here check l2h_identity flag and act accordingly\n        for (int i = 0; i < num_fine_nonmerge_neighbors; i++)\n        {\n            comms->send_vector_async(aggregates, my_destination_part, 4444 + i, manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[i]], halo_sizes[i]) ;\n        }\n\n        //comms->send_vector_wait_all(num_fine_nonmerge_neigh);\n        //comms->send_vector_wait_all(halo_sizes);\n        //comms->send_vector_wait_all(fine_nonmerge_neigh_to_cons_fine_part);\n        comms->send_vector_wait_all(aggregates);\n        /*\n         * EXAMPLE 2\n         Partition 1 sends to partition 0:\n         num_fine_nonmerge_neigh 2\n         halo_sizes [3 3]\n         fine_nonmerge_neigh_to_cons_fine_part [3 3]\n         for loop: sends two pieces: [(18 19 19)] [(15 15 17)]\n         */\n    }\n\n    if (is_root_partition)\n    {\n        //\n        // Step 9.3 Root receives this info, creates metadata\n        //\n        std::vector<VecInt_t> num_fine_nonmerge_neigh_array(num_fine_parts_to_consolidate);\n        IVector_h_vector halo_sizes_array(num_fine_parts_to_consolidate);\n        IVector_h_vector fine_nonmerge_neigh_to_cons_fine_part_array(num_fine_parts_to_consolidate);\n        std::vector<std::vector<IVector> > fine_halo_aggregates_to_root_array(num_fine_parts_to_consolidate);\n        std::vector<VecInt_t> min_index_coarse_halo(num_consolidated_neighbors, 0x7FFFFFFF);\n        std::vector<VecInt_t> max_index_coarse_halo(num_consolidated_neighbors, 0);\n        std::vector<VecInt_t> fine_part_to_consolidated_neighbor(num_parts, -1);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            fine_part_to_consolidated_neighbor[consolidated_coarse_neigh_to_fine_part[i]] = i;\n        }\n\n        /*\n         * EXAMPLE 2\n         everything from here on is for partition 0, since that is the root partition\n         fine_part_to_consolidated_neighbor [-1 -1 0 1 -1]\n         */\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h temp(1);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(temp, current_part, 1111);\n            }\n            else\n            {\n                temp[0] = num_fine_nonmerge_neighbors;\n            }\n\n            num_fine_nonmerge_neigh_array[i] = temp[0];\n            halo_sizes_array[i].resize(temp[0]);\n            fine_nonmerge_neigh_to_cons_fine_part_array[i].resize(temp[0]);\n            fine_halo_aggregates_to_root_array[i].resize(temp[0]);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(halo_sizes_array[i], current_part, 2222);\n            }\n            else\n            {\n                halo_sizes_array[i] = halo_sizes;\n            }\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(fine_nonmerge_neigh_to_cons_fine_part_array[i], current_part, 3333);\n            }\n            else\n            {\n                fine_nonmerge_neigh_to_cons_fine_part_array[i] = fine_nonmerge_neigh_to_cons_fine_part;\n            }\n\n            //Receive the halo regions\n            for (int j = 0; j < temp[0]; j++)\n            {\n                fine_halo_aggregates_to_root_array[i][j].resize(halo_sizes_array[i][j]);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(fine_halo_aggregates_to_root_array[i][j], current_part, 4444 + j);\n                }\n                else\n                {\n                    //HERE\n                    amgx::thrust::copy(aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[j]],\n                                 aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[j]] + halo_sizes[j],\n                                 fine_halo_aggregates_to_root_array[i][j].begin()); //TODO: not do this copying around on the root\n                }\n\n#define MIN(a,b) a<b?a:b;\n#define MAX(a,b) a>b?a:b;\n                //Find minimum and maximum halo indices as not to allocate too much scratch space later\n                int min_index = amgx::thrust::reduce(fine_halo_aggregates_to_root_array[i][j].begin(), fine_halo_aggregates_to_root_array[i][j].end(), int(0x7FFFFFFF), amgx::thrust::minimum<int>());\n                int max_index = amgx::thrust::reduce(fine_halo_aggregates_to_root_array[i][j].begin(), fine_halo_aggregates_to_root_array[i][j].end(), int(0), amgx::thrust::maximum<int>());\n                min_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]] = MIN((int)min_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]], min_index);\n                max_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]] = MAX((int)max_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]], max_index);\n            }\n        }\n\n        cudaCheckError();\n        /*\n         * EXAMPLE 2\n         num_fine_nonmerge_neigh_array = [2 2]\n         halo_sizes_array = [[3 3][3 3]]\n         fine_nonmerge_neigh_to_cons_fine_part_array[][] = [[2 3][3 3]]\n         fine_halo_aggregates_to_root_array[from][to][fine halo vertex] [[[12 15 17][14 16 18]]\n                                                                         [[18 19 19][15 15 17]]]\n        min_index_coarse_halo[12 14]\n        max_index_coarse_halo[17 19]\n         */\n        halo_offsets[0] = total_rows_in_merged;\n        //Now we have all the halo nodes, let's renumber them.\n        int min_index = amgx::thrust::reduce(min_index_coarse_halo.begin(), min_index_coarse_halo.end(), int(0x7FFFFFFF), amgx::thrust::minimum<int>());\n        int max_index = amgx::thrust::reduce(max_index_coarse_halo.begin(), max_index_coarse_halo.end(), int(0), amgx::thrust::maximum<int>());\n        cudaCheckError();\n        //\n        // Step 9.4 compute halo indices on root nodes\n        //\n        int scratch_size;\n\n        if (num_consolidated_neighbors == 0)\n        {\n            scratch_size = 1;\n        }\n        else\n        {\n            scratch_size = max_index - min_index + 2;\n        }\n\n        IVector scratch(scratch_size);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            thrust_wrapper::fill<TConfig::memSpace>(scratch.begin(), scratch.end(), 0);\n            int dest_part = consolidated_coarse_neigh_to_fine_part[i];\n\n            //Flag halo indices that occur for a specific coarse neighbor\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int size = halo_sizes_array[j][k];\n                        this->flag_halo_ids(size, scratch, fine_halo_aggregates_to_root_array[j][k], min_index_coarse_halo[i], max_index, min_index);\n                    }\n                }\n            }\n\n            thrust_wrapper::exclusive_scan<TConfig::memSpace>(scratch.begin(), scratch.end(), scratch.begin(), halo_offsets[i]); //renumber them with the proper offset into our halo\n            halo_offsets[i + 1] = scratch[scratch.size() - 1];\n\n            //now read them back\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int size = halo_sizes_array[j][k];\n                        int block_size = 128;\n                        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n                        this->read_halo_ids(size, scratch, fine_halo_aggregates_to_root_array[j][k], min_index_coarse_halo[i]);\n                        //and send them back to contributing partitions\n                        cudaDeviceSynchronize(); //TODO: don't need to synchronize when using GPUDirect\n                        cudaCheckError();\n                        int current_part = fine_parts_to_consolidate[j];\n                        int tag = 4444 + dest_part;\n\n                        if (current_part != my_id)\n                        {\n                            comms->send_vector_async(fine_halo_aggregates_to_root_array[j][k], current_part, tag);    //!!!!: we are sending them back not in sequential order, need tags!!!!\n                        }\n                        else\n                        {\n                            amgx::thrust::copy(fine_halo_aggregates_to_root_array[j][k].begin(), fine_halo_aggregates_to_root_array[j][k].end(), aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[k]]);\n                        }\n                    }\n                }\n            }\n\n            /*\n             * EXAMPLE 2\n             the array that is sent back in pieces:\n             fine_halo_aggregates_to_root_array[from][to][fine halo vertex] [[[24 25 26][27 29 31]]\n                                                                         [[31 32 32][28 28 30]]]\n            halo_offsets = [24 27 33]\n             */\n        } // Loop over consolidated neighbors\n\n        cudaCheckError();\n\n        // Wait for sends to have completed (this is to prevent fine_halo_aggregates_to_root_array to be destroyed before send has finished)\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            int dest_part = consolidated_coarse_neigh_to_fine_part[i];\n\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int current_part = fine_parts_to_consolidate[j];\n\n                        if (current_part != my_id)\n                        {\n                            comms->send_vector_wait_all(fine_halo_aggregates_to_root_array[j][k]);\n                        }\n                    }\n                }\n            }\n        } // Loop over consolidated neighbors\n\n        //Send total number of rows in the aggregated matrix\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h total_rows(1);\n            total_rows[0] = halo_offsets[num_consolidated_neighbors];\n\n            if (current_part != my_id)\n            {\n                comms->send_vector(total_rows, current_part, 5555);\n            }\n            else\n            {\n                num_all_aggregates = total_rows[0];\n            }\n        }\n    } // If is root partition\n\n    if (!is_root_partition)\n    {\n        for (int i = 0; i < num_fine_nonmerge_neighbors; i++)\n        {\n            int tag = 4444 + fine_nonmerge_neigh_to_cons_fine_part[i];\n            comms->recv_vector(aggregates, my_destination_part, tag, manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[i]], halo_sizes[i]);\n        }\n\n        IVector_h total_rows(1);\n        comms->recv_vector(total_rows, my_destination_part, 5555);\n        num_all_aggregates = total_rows[0];\n    }\n\n    /*\n     * EXAMPLE 2\n     num_all_aggregates = 33 (both partitions 0 and 1\n     */\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcExchangePtr(void *&ptr, bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_root_partition, int my_id, DistributedComms<TConfig> *comms)\n{\n    cudaIpcMemHandle_t handle;\n\n    if (is_root_partition)\n    {\n        cudaIpcGetMemHandle( (cudaIpcMemHandle_t *) &handle, ptr ) ;\n        cudaCheckError();\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part !=  my_id)\n            {\n                comms->send_raw_data(&handle, sizeof(handle), current_part, 456);\n            }\n        }\n    }\n    else\n    {\n        comms->recv_raw_data(&handle, sizeof(handle), my_root_partition, 456);\n        cudaError_t err = cudaIpcOpenMemHandle( (void **) &ptr, handle, cudaIpcMemLazyEnablePeerAccess);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcWaitForChildren(bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_destination_part, int my_id, DistributedComms<TConfig> *comms)\n{\n    cudaEvent_t event;\n    cudaIpcEventHandle_t event_handle;\n    cudaEventCreateWithFlags(&event, cudaEventDisableTiming | cudaEventInterprocess);\n    cudaCheckError();\n    cudaIpcGetEventHandle( &event_handle, event);\n    cudaCheckError();\n    // Each rank record the event\n    cudaEventRecord(event);\n    cudaCheckError();\n\n    if (is_root_partition)\n    {\n        std::vector<cudaEvent_t> child_events(num_parts_to_consolidate);\n        std::vector<cudaIpcEventHandle_t> child_event_handles(num_parts_to_consolidate);\n\n        // Root partition receives event_handles from child and stores in child_event_handles\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                comms->recv_raw_data(&(child_event_handles[i]), sizeof(cudaIpcEventHandle_t), current_part, 987 + current_part);\n                cudaIpcOpenEventHandle(&child_events[i], child_event_handles[i]);\n                cudaCheckError();\n            }\n        }\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            if (parts_to_consolidate[i] != my_id)\n            {\n                cudaEventSynchronize(child_events[i]);\n                cudaCheckError();\n            }\n        }\n    }\n    else\n    {\n        comms->send_raw_data(&event_handle, sizeof(cudaIpcEventHandle_t), my_destination_part, 987 + my_id);\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcWaitForRoot(bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_destination_part, int my_id, DistributedComms<TConfig> *comms)\n{\n    cudaEvent_t event;\n    cudaIpcEventHandle_t event_handle;\n    cudaEventCreateWithFlags(&event, cudaEventDisableTiming | cudaEventInterprocess);\n\n    if (is_root_partition)\n    {\n        cudaIpcGetEventHandle( &event_handle, event);\n        cudaCheckError();\n        // Root records the event\n        cudaEventRecord(event);\n        cudaCheckError();\n\n        // Root partition sends event_handles to child\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                comms->send_raw_data(&event_handle, sizeof(event_handle), current_part, 988 + current_part);\n            }\n        }\n    }\n    else\n    {\n        comms->recv_raw_data(&event_handle, sizeof(event_handle), my_destination_part, 988 + my_id);\n        cudaIpcOpenEventHandle(&event, event_handle);\n        cudaCheckError();\n        cudaEventSynchronize(event);\n        cudaCheckError();\n    }\n}\n\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::read_halo_ids(int size, IVector_d &scratch, IVector_d &halo_aggregates, VecInt_t min_index_coarse_halo)\n{\n    int block_size = 128;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n    read_halo_ids_kernel <<< num_blocks, block_size>>>(scratch.raw(), halo_aggregates.raw(), min_index_coarse_halo, size);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::read_halo_ids(int size, IVector_h &scratch, IVector_h &halo_aggregates, VecInt_t min_index_coarse_halo)\n{\n    FatalError(\"read_halo_ids not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::flag_halo_ids(int size, IVector_d &scratch, IVector_d &halo_aggregates, VecInt_t min_index_coarse_halo,  int max_index, int min_index)\n{\n    int block_size = 128;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n    flag_halo_ids_kernel <<< num_blocks, block_size>>>(scratch.raw(), halo_aggregates.raw(), min_index_coarse_halo, size, max_index - min_index + 1);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::flag_halo_ids(int size, IVector_h &scratch, IVector_h &halo_aggregates, VecInt_t min_index_coarse_halo,  int max_index, int min_index)\n{\n    FatalError(\"flag_halo_ids not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::consolidateAndUploadAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &A)\n{\n    FatalError(\"Fine level consolidation not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::exchangeSolveResultsConsolidation(int &num_iters, std::vector<PODVector_h> &res_history, AMGX_STATUS &status, bool store_res_history)\n{\n    int bsize = this->A->get_block_size();\n    PODVector_h res_history_tmp;\n\n    if (!m_is_fine_level_consolidated)\n    {\n        return;\n    }\n    else\n    {\n        int my_id = this->getFineLevelComms()->get_global_id();\n        IVector_h my_num_iters(1);\n\n        if (m_is_fine_level_root_partition)\n        {\n            my_num_iters[0] = num_iters;\n\n            if (store_res_history)\n            {\n                // Pack the res_history vector into array\n                res_history_tmp.resize( (num_iters + 1)*bsize);\n\n                for (int i = 0; i < num_iters + 1; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        res_history_tmp[i * bsize + j] = res_history[i][j];\n                    }\n                }\n            }\n\n            for (int i = 0; i < m_num_fine_level_parts_to_consolidate; i++)\n            {\n                int current_part = m_fine_level_parts_to_consolidate[i];\n\n                if (my_id != current_part)\n                {\n                    getFineLevelComms()->send_vector_async(my_num_iters, current_part, 245);\n\n                    if (store_res_history)\n                    {\n                        getFineLevelComms()->send_vector_async(res_history_tmp, current_part, 246);\n                    }\n                }\n            }\n\n            for (int i = 0; i < m_num_fine_level_parts_to_consolidate; i++)\n            {\n                int current_part = m_fine_level_parts_to_consolidate[i];\n\n                if (my_id != current_part)\n                {\n                    getFineLevelComms()->send_raw_data(&status, sizeof(status), current_part, 247);\n                }\n            }\n\n            getFineLevelComms()->send_vector_wait_all(my_num_iters);\n\n            if (store_res_history)\n            {\n                getFineLevelComms()->send_vector_wait_all(res_history_tmp);\n            }\n        }\n        else\n        {\n            // Store num_iters\n            getFineLevelComms()->recv_vector(my_num_iters, m_my_fine_level_destination_part, 245);\n            num_iters = my_num_iters[0];\n\n            if (store_res_history)\n            {\n                // Fill res_history vector\n                res_history.resize(num_iters + 1);\n                res_history_tmp.resize( (num_iters + 1)*bsize);\n                getFineLevelComms()->recv_vector(res_history_tmp, m_my_fine_level_destination_part, 246);\n\n                for (int i = 0; i < num_iters + 1; i++)\n                {\n                    res_history[i].resize(bsize);\n\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        res_history[i][j] = res_history_tmp[i * bsize + j];\n                    }\n                }\n            }\n\n            getFineLevelComms()->recv_raw_data(&status, sizeof(status), m_my_fine_level_destination_part, 247);\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::consolidateAndUploadAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    this->createComms(this->A->getResources()); //refresh comms\n    DistributedComms<TConfig> *comms = this->getComms();\n    int my_id = comms->get_global_id();\n    int num_parts = comms->get_num_partitions();\n    int num_rings = this->num_halo_rings();\n    int num_neighbors = this->neighbors.size();\n    // All partitions have to call this, otherwise it fails\n    // Step 1: Figure out which partition should be consolidated together based on their host_name and their PCI-E slot ID\n    IVector_h destination_part(num_parts);\n    this->computeDestinationPartitionsWithCons(my_id, num_parts, destination_part, comms);\n    int my_destination_part = destination_part[my_id];\n    // Check if I'm root partition and how many msgs I will receive\n    bool is_root_partition = false;\n    int num_parts_to_consolidate = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            is_root_partition = true;\n            num_parts_to_consolidate++;\n        }\n    }\n\n    if (my_destination_part >= num_parts)\n    {\n        FatalError(\"During consolidation, sending data to partition that doesn't exist\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create cons_part_to_part map\n    IVector_h cons_part_to_part = destination_part;\n    amgx::thrust::sort(cons_part_to_part.begin(), cons_part_to_part.end());\n    cudaCheckError();\n    cons_part_to_part.erase(amgx::thrust::unique(cons_part_to_part.begin(), cons_part_to_part.end()), cons_part_to_part.end());\n    cudaCheckError();\n    int num_cons_partitions = cons_part_to_part.size();\n\n    // If number of consolidated partitions is the same as number of partitions, simply call uploadAll\n    if (num_cons_partitions ==  num_parts)\n    {\n        this->initializeUploadReorderAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n        this->m_is_fine_level_consolidated = false;\n        return;\n    }\n\n    if (is_root_partition)\n    {\n        this->A->getResources()->expandRootPool();\n    }\n\n    this->m_is_fine_level_consolidated = true;\n\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Fill with b2l_maps\n    IVector_h_vector B2L_maps_tmp;\n    B2L_maps_tmp.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B2L_maps_tmp[i] = this->cached_B2L_maps[i];\n    }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int *col_indices_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    int col_alloc = 0;\n    col_indices_hd = (int *) this->getDevicePointerForData((void *)col_indices, nnz * block_dimx * block_dimy * sizeof(int), &col_alloc);\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data, nnz * block_dimx * block_dimy * sizeof(mat_value_type), &data_alloc);\n\n    if (diag != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag, nnz * block_dimx * block_dimy * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    // Copy the original row_offsets array (this is required when replacing coefficients\n    this->m_old_row_offsets_CONS.resize(n + 1);\n    cudaMemcpy(this->m_old_row_offsets_CONS.raw(), row_ptrs, (n + 1)*sizeof(int), cudaMemcpyDefault);\n    cudaCheckError();\n    this->m_old_nnz_CONS = nnz;\n    // This function:\n    // Creates fine level consolidated matrices\n    // Modifies the btl_maps, lth_maps\n    // Create part_to_cons_part map\n    IVector_h part_to_cons_part(num_parts);\n    amgx::thrust::lower_bound(cons_part_to_part.begin(), cons_part_to_part.end(), destination_part.begin(), destination_part.end(), part_to_cons_part.begin());\n    cudaCheckError();\n    IVector_h neigh_to_part;\n    this->createNeighToDestPartMap(neigh_to_part, this->neighbors, destination_part, num_neighbors);\n    IVector_h cons_neigh_to_part;\n    int num_cons_neighbors;\n    this->createConsolidatedNeighToPartMap(cons_neigh_to_part, neigh_to_part, my_destination_part, destination_part, num_cons_neighbors);\n    IVector_h neigh_to_cons_neigh;\n    this->createNeighToConsNeigh( neigh_to_cons_neigh, cons_neigh_to_part, neigh_to_part, my_destination_part, num_neighbors);\n    // ---------------------------------------\n    // MERGE B2L MAPS BASED ON DEST PARTITION\n    // ---------------------------------------\n    IVector_h_vector dest_B2L_maps;\n    this->consolidateB2Lmaps(dest_B2L_maps,  B2L_maps_tmp, neigh_to_cons_neigh,  num_cons_neighbors, num_neighbors);\n    // ------------------------------------\n    // Renumber interior and boundary rows\n    // ------------------------------------\n    int num_interior_rows;\n    int num_boundary_rows;\n    IVector_h renumbering;\n    this->createAggregatesRenumbering(renumbering, dest_B2L_maps, n, num_cons_neighbors, num_interior_rows, num_boundary_rows, num_rings);\n    // --------------------------------------------------\n    // Create list of destination parts to consolidate\n    // --------------------------------------------------\n    // Store whether or not this is a root partition on fine level\n    IVector_h parts_to_consolidate;\n    parts_to_consolidate.resize(num_parts_to_consolidate);\n    int count = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            parts_to_consolidate[count] = i;\n            count++;\n        }\n    }\n\n    // ---------------------------------------------------------------------\n    // Each partition computes its offset for its interior and boundary nodes\n    // ---------------------------------------------------------------------\n    IVector_h_vector vertex_counts;\n    int interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged;\n    int total_rows_in_merged;\n    this->computeConsolidatedOffsets(my_id, my_destination_part, is_root_partition, num_interior_rows, num_boundary_rows, vertex_counts, parts_to_consolidate, num_parts_to_consolidate, interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged, total_rows_in_merged, comms);\n    // -----------------------------------\n    // Each partition renumber it's rows\n    // -----------------------------------\n    int total_num_halos = 0;\n\n    // Pack new bdy_ids\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        total_num_halos += this->cached_L2H_maps[i].size();\n    }\n\n    IVector_h row_ids(n + total_num_halos, -1);\n    this->m_row_ids_CONS.resize(n + total_num_halos);\n\n    // Renumber the interior and boundary rows\n    for (int i = 0; i < n; i++)\n    {\n        int new_id;\n\n        if (renumbering.size() == 0)\n        {\n            new_id = i;\n        }\n        else\n        {\n            new_id = renumbering[i];\n        }\n\n        new_id += ((new_id >= num_interior_rows) ? boundary_offset : interior_offset);\n        row_ids[i] = new_id;\n    }\n\n    for (int i = 0; i < num_cons_neighbors; i++)\n    {\n        thrust_wrapper::transform<AMGX_host>(dest_B2L_maps[i].begin(),\n                          dest_B2L_maps[i].end(),\n                          amgx::thrust::constant_iterator<index_type>(boundary_offset),\n                          dest_B2L_maps[i].begin(),\n                          amgx::thrust::plus<index_type>());\n    }\n\n    cudaCheckError();\n    // -------------------------------------------------\n    // Send dest_B2L_maps to root partitions\n    // ------------------------------------------------\n    IVector_h num_bdy_per_cons_neigh(num_cons_neighbors);\n\n    for (int i = 0; i < num_cons_neighbors; i++)\n    {\n        num_bdy_per_cons_neigh[i] = dest_B2L_maps[i].size();\n    }\n\n    IVector_h root_cons_neighbors;\n    int root_num_cons_neighbors = 0;\n    IVector_h_vector cons_B2L_maps;\n    this->consolidateB2LmapsOnRoot(root_num_cons_neighbors, cons_B2L_maps, root_cons_neighbors, dest_B2L_maps, cons_neigh_to_part, num_bdy_per_cons_neigh, parts_to_consolidate, num_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_cons_neighbors, comms);\n    IVector_h halo_ids_offsets(num_neighbors + 1);\n    IVector_h halo_ids;\n    int halo_ids_size = 0;\n    halo_ids_offsets[0] = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        halo_ids_size += this->cached_L2H_maps[i].size();\n        halo_ids_offsets[i + 1] = halo_ids_size;\n    }\n\n    halo_ids.resize(halo_ids_size);\n    // Do exchange with neighbors\n    // Pack new bdy_ids\n    IVector_h_vector bdy_ids;\n    bdy_ids.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = this->cached_B2L_maps[i].size();\n        bdy_ids[i].resize(size);\n\n        // Pack buffer\n        for (int j = 0; j < size; j++)\n        {\n            bdy_ids[i][j] = row_ids[this->cached_B2L_maps[i][j]];\n        }\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->send_vector_async(bdy_ids[i], this->neighbors[i], 6666 + this->neighbors[i]);\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->recv_vector(halo_ids, this->neighbors[i], 6666 + my_id, halo_ids_offsets[i], this->cached_L2H_maps[i].size());\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->send_vector_wait_all(bdy_ids[i]);\n    }\n\n    IVector_h halo_offsets(root_num_cons_neighbors + 1, 0);\n    int root_num_rows;\n    this->consolidateAndRenumberHalos(halo_ids, halo_ids_offsets, halo_offsets, this->neighbors, num_neighbors, root_cons_neighbors, root_num_cons_neighbors, destination_part, my_destination_part, is_root_partition, parts_to_consolidate, num_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, root_num_rows, comms);\n\n    if (is_root_partition)\n    {\n        this->B2L_maps.resize(cons_B2L_maps.size());\n\n        for (int i = 0; i < cons_B2L_maps.size(); i++)\n        {\n            amgx::thrust::sort(cons_B2L_maps[i].begin(), cons_B2L_maps[i].end());\n            this->B2L_maps[i].copy(cons_B2L_maps[i]); // H2D copy of B2L maps\n        }\n\n        cudaCheckError();\n    }\n\n    // Now renumber the row_ids based on lth_maps\n    count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        for (int j = 0; j < this->cached_L2H_maps[i].size(); j++)\n        {\n            row_ids[this->cached_L2H_maps[i][j]] = halo_ids[count];\n            count++;\n        }\n    }\n\n    cudaMemcpy(this->m_row_ids_CONS.raw(), row_ids.raw(), (n + total_num_halos)*sizeof(int), cudaMemcpyDefault);\n    cudaCheckError();\n    int bsize = block_dimx * block_dimy;\n\n    if (is_root_partition)\n    {\n        this->A->row_offsets.resize(root_num_rows + 1);\n    }\n\n    void *root_row_ptr = (void *) this->A->row_offsets.raw();\n\n    if (useCudaIpc)\n    {\n        // ----------------------------------------------------\n        // 1. cudaIPC to get pointer to root's row_offset array\n        // ----------------------------------------------------\n        this->ipcExchangePtr(root_row_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n        // -------------------------------------------------------------------\n        // 2. each rank copy it's row length on root partition using row_ids\n        // -------------------------------------------------------------------\n        int cta_size = 128;\n        int grid_size = std::min(4096, (n + total_num_halos + cta_size - 1) / cta_size);\n        zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_old_row_offsets_CONS.raw(), ((int *) root_row_ptr) /* IPC */, this->m_row_ids_CONS.raw(), n, total_num_halos, (mat_value_type *) diag);\n        cudaCheckError();\n        // Root partition waits for children to be done writing their result\n        this->ipcWaitForChildren(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n    }\n    else   // CudaIpcNotAvailable\n    {\n        this->checkPinnedBuffer( std::max( nnz * sizeof(mat_value_type), (n + 1)*std::max(sizeof(index_type), sizeof(value_type)) ) );\n\n        if (!is_root_partition)\n        {\n            IVector_h data_to_send(3);\n            data_to_send[0] = n;\n            data_to_send[1] = nnz;\n            data_to_send[2] = total_num_halos;\n            int dummy;\n            void *row_ptrs_to_send = this->getHostPointerForData((void *)row_ptrs, sizeof(index_type) * (n + 1), &dummy);\n            comms->send_vector(data_to_send, my_destination_part, 10000 + my_id);\n            comms->send_raw_data(row_ptrs_to_send, (n + 1)*sizeof(int), my_destination_part, 10001 + my_id);\n            comms->send_raw_data(&row_ids[0], (n + total_num_halos)*sizeof(int), my_destination_part, 10002 + my_id);\n        }\n        else\n        {\n            cudaEvent_t event;\n            cudaEventCreate(&event);\n            cudaCheckError();\n            //TODO: Could use streams here\n            //TODO: Avoid extra device to host copies\n            std::vector<IVector_h> data_recv(num_parts_to_consolidate);\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                data_recv[i].resize(3);\n                int current_part =  parts_to_consolidate[i];\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(data_recv[i], current_part, 10000 + current_part);\n                }\n                else\n                {\n                    data_recv[i][0] = n;\n                    data_recv[i][1] = nnz;\n                    data_recv[i][2] = total_num_halos;\n                }\n            }\n\n            this->m_child_n.resize(num_parts_to_consolidate);\n            this->m_child_nnz.resize(num_parts_to_consolidate);\n            this->m_child_num_halos.resize(num_parts_to_consolidate);\n            this->m_child_row_ids.resize(num_parts_to_consolidate);\n            this->m_child_old_row_offsets.resize(num_parts_to_consolidate);\n            int max_n = 0;\n            int max_nnz = 0;\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                this->m_child_n[i] = data_recv[i][0];\n                this->m_child_nnz[i] = data_recv[i][1];\n                this->m_child_num_halos[i] = data_recv[i][2];\n\n                if (this->m_child_n[i] > max_n) { max_n = this->m_child_n[i]; }\n\n                if (this->m_child_nnz[i] > max_nnz) { max_nnz = this->m_child_nnz[i]; }\n\n                this->m_child_row_ids[i].resize(this->m_child_n[i] + this->m_child_num_halos[i]);\n                this->m_child_old_row_offsets[i].resize(this->m_child_n[i] + 1);\n            }\n\n            this->m_child_max_n = max_n;\n            this->m_child_max_nnz = max_nnz;\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                int cta_size = 128;\n                int grid_size = std::min(4096, (this->m_child_n[i] + this->m_child_num_halos[i] + cta_size - 1) / cta_size);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(this->m_child_old_row_offsets[i], current_part, 10001 + current_part, 0, this->m_child_n[i] + 1);\n                    comms->recv_vector(this->m_child_row_ids[i], current_part, 10002 + current_part, 0, this->m_child_n[i] + this->m_child_num_halos[i]);\n                    zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), this->m_child_row_ids[i].raw(), this->m_child_n[i], this->m_child_num_halos[i], (mat_value_type *) diag);\n                    cudaCheckError();\n                    // Wait for kernel to finish before overwriting host buffer\n                    cudaEventRecord(event);\n                    cudaCheckError();\n                    cudaEventSynchronize(event);\n                    cudaCheckError();\n                }\n                else\n                {\n                    zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), this->m_row_ids_CONS.raw(), n, total_num_halos, (mat_value_type *) diag);\n                    cudaCheckError();\n                    cudaEventRecord(event);\n                    cudaCheckError();\n                    cudaEventSynchronize(event);\n                    cudaCheckError();\n                }\n            }\n\n            cudaCheckError();\n            cudaEventDestroy(event);\n            cudaCheckError();\n        } // If root partition\n\n        //TODO: is this necessary\n        comms->barrier();\n    }\n\n    //3. root does a exclusive_scan\n    if (is_root_partition)\n    {\n        cudaEvent_t event;\n        cudaEventCreate(&event);\n        cudaCheckError();\n        // Populate the halo rows with diagonal, increase the length of the halo rows\n        thrust_wrapper::fill<TConfig::memSpace>(this->A->row_offsets.begin() + halo_offsets[0], this->A->row_offsets.begin() + halo_offsets[root_num_cons_neighbors], 1);\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(this->A->row_offsets.begin(), this->A->row_offsets.end(), this->A->row_offsets.begin());\n        cudaEventRecord(event);\n        cudaCheckError();\n        cudaEventSynchronize(event);\n        cudaCheckError();\n        this->A->set_initialized(0);\n        this->A->delProps(DIAG); // We always insert the diagonal\n        this->A->delProps(COO); // No COO\n        this->A->setColsReorderedByColor(false); // Cols not reordered by color\n        int nnz = this->A->row_offsets[root_num_rows]; // This is a device to host copy\n        this->A->resize(root_num_rows, root_num_rows, nnz, block_dimx, block_dimy);\n        this->A->set_num_nz(nnz); // num_nz doesn't include halo rows\n        //this->A->set_initialized(1);\n        cudaEventDestroy(event);\n        cudaCheckError();\n    }\n    else\n    {\n        this->A->set_initialized(0);\n        this->A->resize( 0, 0, 0, block_dimx, block_dimy );\n        this->A->delProps(DIAG); // We always insert the diagonal\n        this->A->delProps(COO); // No COO\n        this->A->setColsReorderedByColor(false); // Cols not reordered by color\n        //this->A->set_initialized(1);\n    }\n\n    if (useCudaIpc)\n    {\n        // ----------------------------------------------\n        // 4. Do ipc consolidation of values and columns\n        // ----------------------------------------------\n        // Child partition waits for parent to create row_offsets\n        this->ipcWaitForRoot(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        void *root_col_ptr = (void *) this->A->col_indices.raw();\n        void *root_val_ptr = (void *) this->A->values.raw();\n        this->ipcExchangePtr(root_col_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        this->ipcExchangePtr(root_val_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        int cta_size2 = 128;\n        int grid_size2 = std::min(4096, (n + cta_size2 - 1) / cta_size2);\n        ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(n, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), ( (int *) root_row_ptr ) /*IPC*/, col_indices_hd, ( (int *) root_col_ptr) /*IPC*/, data_hd, ( (mat_value_type *) root_val_ptr ) /*IPC*/, diag_hd, bsize);\n        cudaCheckError();\n        // Root partition waits for children to upload their matrices\n        this->ipcWaitForChildren(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n\n        // Child partitions close their mem handle (they are done upload data)\n        if (!is_root_partition)\n        {\n            cudaIpcCloseMemHandle(root_row_ptr);\n            cudaIpcCloseMemHandle(root_val_ptr);\n            cudaIpcCloseMemHandle(root_col_ptr);\n        }\n    }\n    else // If cudaIpcNotAvailable\n    {\n        if (!is_root_partition)\n        {\n            int dummy;\n            void *col_indices_to_send = this->getHostPointerForData((void *)col_indices, sizeof(index_type) * nnz, &dummy);\n            comms->send_raw_data(col_indices_to_send, nnz * sizeof(int), my_destination_part, 10000 + my_id);\n            void *data_to_send = this->getHostPointerForData((void *)data, sizeof(mat_value_type) * nnz, &dummy);\n            comms->send_raw_data(data_to_send, nnz * bsize * sizeof(mat_value_type), my_destination_part, 10001 + my_id);\n\n            if (diag != NULL)\n            {\n                void *diag_to_send = this->getHostPointerForData((void *)diag, sizeof(mat_value_type) * n, &dummy);\n                comms->send_raw_data(diag_to_send, n * bsize * sizeof(mat_value_type), my_destination_part, 10002 + my_id);\n            }\n        }\n        else\n        {\n            cudaEvent_t event;\n            cudaEventCreate(&event);\n            cudaCheckError();\n            //TODO: Could use streams here\n            int *child_col_indices;\n            mat_value_type *child_data;\n            mat_value_type *child_diag = NULL;\n            cudaHostAlloc( (void **) &child_col_indices, this->m_child_max_nnz * sizeof(int), cudaHostAllocMapped);\n            cudaHostAlloc( (void **) &child_data, this->m_child_max_nnz * bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n\n            if (diag != NULL)\n            {\n                cudaHostAlloc( (void **) &child_diag, (this->m_child_max_n)*bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n            }\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                int cta_size2 = 128;\n                int grid_size2 = std::min(4096, (this->m_child_n[i] + cta_size2 - 1) / cta_size2);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_raw_data(child_col_indices, this->m_child_nnz[i]*sizeof(int), current_part, 10000 + current_part);\n                    comms->recv_raw_data(child_data, this->m_child_nnz[i]*bsize * sizeof(mat_value_type), current_part, 10001 + current_part);\n\n                    if (diag != NULL)\n                    {\n                        comms->recv_raw_data(child_diag, this->m_child_n[i]*bsize * sizeof(mat_value_type), current_part, 10002 + current_part);\n                    }\n\n                    int *child_col_indices_hd;\n                    mat_value_type *child_data_hd;\n                    mat_value_type *child_diag_hd = NULL;\n                    cudaHostGetDevicePointer(&child_col_indices_hd, child_col_indices, 0);\n                    cudaHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                    if (diag != NULL)\n                    {\n                        cudaHostGetDevicePointer(&child_diag_hd, child_diag, 0);\n                    }\n\n                    ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(this->m_child_n[i], this->m_child_row_ids[i].raw(), this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), child_col_indices_hd, this->A->col_indices.raw(), child_data_hd, this->A->values.raw(), child_diag_hd, bsize);\n                    cudaCheckError();\n                    // Wait for kernel to finish before overwriting host buffer\n                    cudaEventRecord(event);\n                    cudaCheckError();\n                    cudaEventSynchronize(event);\n                    cudaCheckError();\n                }\n                else\n                {\n                    ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(n, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), col_indices_hd, this->A->col_indices.raw(), data_hd, this->A->values.raw(), diag_hd, bsize);\n                    cudaCheckError();\n                    cudaEventRecord(event);\n                    cudaCheckError();\n                    cudaEventSynchronize(event);\n                    cudaCheckError();\n                }\n            }\n\n            cudaCheckError();\n            cudaEventDestroy(event);\n            cudaCheckError();\n            amgx::memory::cudaFreeHost(child_col_indices);\n            amgx::memory::cudaFreeHost(child_data);\n\n            if (diag != NULL)\n            {\n                amgx::memory::cudaFreeHost(child_diag);\n            }\n        } // If root partition\n\n        //TODO: is this necessary\n        comms->barrier();\n    }\n\n    // Store the original fine level communicator\n    this->m_is_fine_level_root_partition = is_root_partition;\n    this->m_my_fine_level_destination_part = my_destination_part;\n    // Create a clone of the original communicator\n    this->m_fine_level_comms = comms; //this->_comms is the same pointer that this->m_fine_level_comms right now, so we can overwrite this->_comms, but make sure that we release m_fine_level_cons\n    this->_comms = this->m_fine_level_comms->CloneSubComm(cons_part_to_part, is_root_partition);   // this->_comms will be empty comm for non-root partition and new comm for root ranks only if root partition\n    this->m_fine_level_id = my_id;\n\n    if (is_root_partition)\n    {\n        int cta_size = 128;\n        int grid_size3 = std::min(4096, ( (root_num_rows - halo_offsets[0]) + cta_size - 1) / cta_size);\n\n        if (grid_size3 != 0)\n        {\n            set_halo_cols_values <<< grid_size3, cta_size>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), halo_offsets[0], root_num_rows, bsize);\n            cudaCheckError();\n        }\n\n        int my_cons_id = part_to_cons_part[my_id];\n        this->_global_id = my_cons_id;\n        this->_num_interior_nodes = total_interior_rows_in_merged;\n        this->_num_boundary_nodes = total_boundary_rows_in_merged;\n\n        for (int i = 0; i < root_num_cons_neighbors; i++)\n        {\n            root_cons_neighbors[i] = part_to_cons_part[root_cons_neighbors[i]];\n        }\n\n        this->_comms->set_neighbors(root_num_cons_neighbors);\n        this->neighbors = root_cons_neighbors;\n        this->halo_offsets = halo_offsets; // H2D copy of halo offsets\n        this->m_num_fine_level_parts_to_consolidate = num_parts_to_consolidate;\n        this->m_fine_level_parts_to_consolidate = parts_to_consolidate;\n        this->set_num_halo_rings(num_rings);\n        this->set_num_halo_rows(halo_offsets[root_num_cons_neighbors] - halo_offsets[0]);\n        // B2L_maps has already been copied\n        this->B2L_rings.resize(root_num_cons_neighbors);\n\n        for (int i = 0; i < root_num_cons_neighbors; i++)\n        {\n            this->B2L_rings[i].resize(2);\n            this->B2L_rings[i][0] = 0;\n            this->B2L_rings[i][1] = cons_B2L_maps[i].size();\n        }\n\n        this->set_initialized(this->A->row_offsets);\n        this->A->set_initialized(0);\n        this->A->delProps(DIAG);\n        this->A->diag.resize(root_num_rows);\n        this->A->computeDiagonal(); //\n        this->A->setView(OWNED);\n        cudaEventCreate(&(this->comm_event));\n        cudaCheckError();\n        this->A->set_initialized(1);\n    }\n    else\n    {\n        this->neighbors.resize(0);\n        this->halo_offsets.resize(0);\n    }\n\n    /* free memory (if needed) */\n    if (col_alloc) { amgx::memory::cudaFreeAsync(col_indices_hd); }\n    if (data_alloc) { amgx::memory::cudaFreeAsync(data_hd); }\n    if (diag_alloc) { amgx::memory::cudaFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsNoCons(int n, int nnz, const mat_value_type *data_pinned, const mat_value_type *diag_pinned)\n{\n    //matrix parameters\n    //int num_nnz  = this->A->get_num_nz();\n    int num_rows   = this->halo_offsets[0];\n    int total_rows = num_rows + this->num_halo_rows();\n    int block_size = this->A->get_block_size();\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    //cuda parameters\n    int num_blocks = std::min(4096, (num_rows + 127) / 128);\n\n    /* WARNING: the number of non-zero elements (nnz) in the array data_pinned and A->values (num_nnz) might be different at this point.\n       1. If the matrix has CSR property and therefore diagonal is included in the matrix this values will be the same.\n       2. If the matrix has DIAG property and therefore diagonal is originally stored separately, and later appended to the array\n          of values, and subsequently inserted into the matrix than num_nnz = nnz + n. We have to account for this fact when replacing the\n          coefficients (and use nnz not num_nnz).\n       obs.: see calls to computeDiagonal (matrix.cu), AMGX_matrix_upload and AMGX_replace_coefficients (amgx_c.cu), and\n          uploadMatrix and replaceMatrixCoefficients[No|With]Cons (distributed_manager.cu) for details. */\n\n    /* check early exit */\n    if ((this->neighbors.size() == 0 || this->renumbering.size() == 0) && !this->m_is_fine_level_glued)\n    {\n        return;\n    }\n\n    cudaCheckError();\n    /* allocate if data and diag if they are not pinned */\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data_pinned, nnz * block_size * sizeof(mat_value_type), &data_alloc);\n\n    if (diag_pinned != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag_pinned, num_rows * block_size * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    /* replace the values (reordering them if needed) */\n    if (insertDiagonals && diag_pinned != NULL)\n    {\n        replace_values_matrix <32> <<< num_blocks, 512>>>(data_hd, diag_hd, this->old_row_offsets.raw(), this->A->row_offsets.raw(), this->A->values.raw(), this->renumbering.raw(), block_size, num_rows);\n        cudaCheckError();\n    }\n    else\n    {\n        replace_values_matrix <32> <<< num_blocks, 512>>>(data_hd, this->old_row_offsets.raw(), this->A->row_offsets.raw(), this->A->values.raw(), this->renumbering.raw(), block_size, num_rows);\n        cudaCheckError();\n\n        if (diag_pinned != NULL)\n        {\n            reorder_vector_values <<< num_blocks, 512>>>(this->A->values.raw() + this->A->row_offsets[total_rows]*block_size, diag_hd, this->renumbering.raw(), block_size, num_rows);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::cudaFreeAsync(data_hd); }\n    if (diag_alloc) { amgx::memory::cudaFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsWithCons(int n, int nnz, const mat_value_type *data_pinned, const mat_value_type *diag_pinned)\n{\n    //matrix parameters\n    //int num_nnz  = this->A->get_num_nz();\n    /* WARNING: in consolidation, for non-root partitions, halo_offsets\n       might be NULL due to the call halo_offsets.resize(0); at the end\n       of the routine uploadMatrix->consolidateAndUploadAll. We should\n       use the parameter n instead this->halo_offsets[0] for num_rows. */\n    int num_rows   = n;\n    int block_size = this->A->get_block_size();\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data_pinned, nnz * block_size * sizeof(mat_value_type), &data_alloc);\n\n    if (diag_pinned != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag_pinned, num_rows * block_size * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n\n    if (useCudaIpc)\n    {\n        // Child partitions wait for root to be done\n        this->ipcWaitForRoot(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        void *root_row_ptr = (void *) this->A->row_offsets.raw();\n        void *root_val_ptr = (void *) this->A->values.raw();\n        this->ipcExchangePtr(root_row_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        this->ipcExchangePtr(root_val_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        // replace the values, insert the diagonal\n        int ncons = this->m_old_row_offsets_CONS.size() - 1;\n        int cta_size = 128;\n        int grid_size2 = std::min(4096, (ncons + cta_size - 1) / cta_size);\n        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size>>>(ncons, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), ( (int *) root_row_ptr )/*IPC*/, data_hd, ( (mat_value_type *) root_val_ptr )/*IPC*/, diag_hd, this->A->get_block_size() );\n        cudaCheckError();\n        // Root partition wait for child to be done replacing their values\n        this->ipcWaitForChildren(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            cudaIpcCloseMemHandle(root_row_ptr);\n            cudaIpcCloseMemHandle(root_val_ptr);\n        }\n    }\n    else // cudaIpcNotAvailable\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            int bsize = this->A->get_block_size();\n            int ncons = this->m_old_row_offsets_CONS.size() - 1;\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                int nnzcons = this->m_old_nnz_CONS;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, nnzcons * bsize * sizeof(mat_value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data(data_to_send, nnzcons * bsize * sizeof(mat_value_type), this->m_my_fine_level_destination_part, 10001 + this->fine_level_id());\n\n                if (diag_pinned != NULL)\n                {\n                    void *diag_to_send = this->getHostPointerForData((void *)diag_pinned, ncons * bsize * sizeof(mat_value_type), &dummy);\n                    this->getFineLevelComms()->send_raw_data(diag_to_send, ncons * bsize * sizeof(mat_value_type), this->m_my_fine_level_destination_part, 10002 + this->fine_level_id());\n                }\n            }\n            else\n            {\n                cudaEvent_t event;\n                cudaEventCreate(&event);\n                cudaCheckError();\n                //TODO: Could use streams here\n                mat_value_type *child_data;\n                mat_value_type *child_diag = NULL;\n                cudaHostAlloc( (void **) &child_data, this->m_child_max_nnz * bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n\n                if (diag_pinned != NULL)\n                {\n                    cudaHostAlloc( (void **) &child_diag, (this->m_child_max_n)*bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n                }\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part =  this->m_fine_level_parts_to_consolidate[i];\n                    int cta_size2 = 128;\n                    int grid_size2 = std::min(4096, (this->m_child_n[i] + cta_size2 - 1) / cta_size2);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data(child_data, this->m_child_nnz[i]*bsize * sizeof(mat_value_type), current_part, 10001 + current_part);\n\n                        if (diag_pinned != NULL)\n                        {\n                            this->getFineLevelComms()->recv_raw_data(child_diag, this->m_child_n[i]*bsize * sizeof(mat_value_type), current_part, 10002 + current_part);\n                        }\n\n                        mat_value_type *child_data_hd;\n                        mat_value_type *child_diag_hd = NULL;\n                        cudaHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                        if (diag_pinned != NULL)\n                        {\n                            cudaHostGetDevicePointer(&child_diag_hd, child_diag, 0);\n                        }\n\n                        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size2>>>(this->m_child_n[i], this->m_child_row_ids[i].raw(), this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), child_data_hd, this->A->values.raw(), child_diag_hd, bsize);\n                        cudaCheckError();\n                        // Wait for kernel to finish before overwriting host buffer\n                        cudaEventRecord(event);\n                        cudaCheckError();\n                        cudaEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size2>>>(ncons, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), data_hd, this->A->values.raw(), diag_hd, bsize);\n                        cudaCheckError();\n                        //cudaEventRecord(event);\n                        cudaCheckError();\n                        //cudaEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n                cudaEventDestroy(event);\n                cudaCheckError();\n                amgx::memory::cudaFreeHost(child_data);\n\n                if (diag_pinned != NULL)\n                {\n                    amgx::memory::cudaFreeHost(child_diag);\n                }\n            } // If root partition\n\n            //TODO: is this necessary\n            this->getFineLevelComms()->barrier();\n        } //agg\n        else if (this->m_is_fine_level_glued) // classical\n        {\n            int bsize = this->A->get_block_size();\n            int ncons = this->m_old_row_offsets_CONS.size() - 1;\n            IVector_h nnz_off;\n            nnz_off.resize(this->getConsolidationArrayOffsets().size());\n            IVector_h nnz_array;\n            nnz_array.resize(this->getConsolidationArrayOffsets().size() - 1);\n            this->getFineLevelComms()->all_gather( nnz,\n                                                   nnz_array,\n                                                   this->getConsolidationArrayOffsets().size() - 1);\n            nnz_off[0] = 0;\n\n            for (int i = 0; i < nnz_array.size(); i++)\n            {\n                nnz_off[i + 1] = nnz_off[i] + nnz_array[i];\n            }\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, nnz * bsize * sizeof(mat_value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data( data_to_send,\n                        nnz * bsize * sizeof(mat_value_type),\n                        this->m_my_fine_level_destination_part,\n                        10001 + this->fine_level_id());\n\n                if (diag_pinned != NULL)\n                {\n                    void *diag_to_send = this->getHostPointerForData((void *)diag_pinned, n * sizeof(mat_value_type), &dummy);\n                    this->getFineLevelComms()->send_raw_data( diag_to_send,\n                            n * bsize * sizeof(mat_value_type),\n                            this->m_my_fine_level_destination_part,\n                            10002 + this->fine_level_id());\n                    //diag.resize(0);\n                    cudaCheckError();\n                }\n\n                //values.resize(0);\n                cudaCheckError();\n            }\n            else\n            {\n                //TODO: Could use streams here\n                mat_value_type *child_data;\n                mat_value_type *child_diag = NULL;\n                // Assumes partions have been glued already\n                this->A->getNnzForView(OWNED, &nnz);\n                cudaHostAlloc( (void **) &child_data, nnz * bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n\n                if (diag_pinned != NULL)\n                {\n                    cudaHostAlloc( (void **) &child_diag, this->halo_offsets[this->neighbors.size()]*bsize * sizeof(mat_value_type), cudaHostAllocMapped);\n                }\n\n                // roots copy their data\n                memcpy ( &child_data[0], data_pinned, nnz_array[this->fine_level_id()]*sizeof(value_type));\n\n                if (diag_pinned != NULL)\n                {\n                    memcpy ( &child_diag[0], diag_pinned, n * sizeof(value_type));\n                }\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part =  this->m_fine_level_parts_to_consolidate[i];\n                    int current_offset = nnz_off[current_part] - nnz_off[this->fine_level_id()] ;\n                    int current_nnz = nnz_array[current_part];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data( &child_data[current_offset],\n                                current_nnz * bsize * sizeof(mat_value_type),\n                                current_part,\n                                10001 + current_part);\n\n                        if (diag_pinned != NULL)\n                            this->getFineLevelComms()->recv_raw_data( &child_diag[this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()]],\n                                    (this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part])*bsize * sizeof(mat_value_type),\n                                    current_part,\n                                    10002 + current_part);\n                    }\n                }\n\n                cudaCheckError();\n                // we can follow the usual upload path for raw data now\n                // Assumes partions have been glued already\n                int os;\n                this->A->getOffsetAndSizeForView(OWNED, &os, &n);\n                replaceMatrixCoefficientsNoCons( n, nnz,  child_data, child_diag);\n                cudaCheckError();\n                amgx::memory::cudaFreeHost(child_data);\n\n                if (diag_pinned != NULL)\n                {\n                    amgx::memory::cudaFreeHost(child_diag);\n                }\n            } // If root partition\n\n            //TODO: is this necessary\n            this->getFineLevelComms()->barrier();\n        } // cla\n    } // not ipc\n\n    this->A->setView(OWNED);\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::cudaFreeAsync(data_hd); }\n\n    if (diag_alloc) { amgx::memory::cudaFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->isFineLevelConsolidated() || (this->isFineLevelGlued() && !this->isGlued()))\n    {\n        transformAndUploadVectorWithCons(v, data, n, block_dim);\n    }\n    else\n    {\n        v.resize(n * block_dim);\n        cudaCheckError();\n        // Upload on host\n        cudaMemcpy(v.raw(), (value_type *)data, n * block_dim * sizeof(value_type), cudaMemcpyDefault);\n        cudaCheckError();\n        // Permute based on renumbering vector\n        transformVector(v);\n        int tag = 0;\n        // Exchange halos\n        this->exchange_halo(v, tag);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVectorWithCons(VVector_v &v, const void *data_pinned, int n, int block_dim)\n{\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n    this->getFineLevelComms()->barrier();\n    void *root_temp_ptr = NULL;\n    VVector_v temp;\n\n    if (this->m_is_fine_level_root_partition && !this->m_is_fine_level_glued )\n    {\n        temp.resize(this->halo_offsets[this->neighbors.size()]*v.get_block_size(), types::util<value_type>::get_zero());\n        temp.set_block_dimx(v.get_block_dimx());\n        temp.set_block_dimy(v.get_block_dimy());\n        root_temp_ptr = (void *) temp.raw();\n    }\n\n    cudaCheckError();\n    int data_alloc = 0;\n    value_type *data_hd = NULL;\n\n    if (!this->m_is_fine_level_glued )\n    {\n        data_hd = (value_type *) this->getDevicePointerForData((void *)data_pinned, n * block_dim * sizeof(value_type), &data_alloc);\n    }\n\n    if (useCudaIpc)\n    {\n        // Do IPC\n        this->ipcExchangePtr(root_temp_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        int num_blocks = std::min(4096, (n + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, data_hd, this->m_row_ids_CONS.raw(), v.get_block_size(), n);\n        cudaCheckError();\n        // Root partition waits for children to be done\n        this->ipcWaitForChildren(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            cudaIpcCloseMemHandle(root_temp_ptr);\n        }\n    }\n    else // If cudaIpcNotAvail\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            // Exchange the vector between root and child\n            if (!this->m_is_fine_level_root_partition)\n            {\n                IVector_h size(1);\n                size[0] = n;\n                this->getFineLevelComms()->send_vector(size, this->m_my_fine_level_destination_part, 20000 + this->fine_level_id());\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data(data_to_send, n * v.get_block_size()*sizeof(value_type), this->m_my_fine_level_destination_part, 20001 + this->fine_level_id());\n            }\n            else\n            {\n                cudaEvent_t event;\n                cudaEventCreate(&event);\n                cudaCheckError();\n                IVector_h child_n(this->m_num_fine_level_parts_to_consolidate);\n                int max_n = 0;\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_vector(child_n, current_part, 20000 + current_part, i, 1);\n                    }\n                    else\n                    {\n                        child_n[i] = n;\n                    }\n\n                    if (child_n[i] > max_n) { max_n = child_n[i]; }\n                }\n\n                value_type *child_data;\n                cudaHostAlloc( (void **) &child_data, max_n * v.get_block_size()*sizeof(value_type), cudaHostAllocMapped);\n                value_type *child_data_hd;\n                cudaHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n                    int num_blocks = std::min(4096, (child_n[i] + 511) / 512);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data(&child_data[0], child_n[i]*v.get_block_size()*sizeof(value_type), current_part, 20001 + current_part);\n                        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, child_data_hd, this->m_child_row_ids[i].raw(), v.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                        cudaEventRecord(event);\n                        cudaCheckError();\n                        cudaEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, data_hd, this->m_row_ids_CONS.raw(), v.get_block_size(), n);\n                        cudaCheckError();\n                    }\n                } // Loop over parts to consolidate\n\n                cudaCheckError();\n                cudaEventDestroy(event);\n                cudaCheckError();\n                amgx::memory::cudaFreeHost(child_data);\n            } // If root partition\n        } //agg\n        else if (this->m_is_fine_level_glued) // cla\n        {\n            value_type *child_data = NULL;\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data( data_to_send,\n                        n * v.get_block_size()*sizeof(value_type),\n                        this->m_my_fine_level_destination_part,\n                        20001 + this->fine_level_id());\n                //v.resize(0); // just in case something resized it betwen iterations\n                cudaCheckError();\n            }\n            else\n            {\n                cudaHostAlloc( (void **) &child_data, this->halo_offsets[this->neighbors.size()]*v.get_block_size()*sizeof(value_type), cudaHostAllocMapped);\n                value_type *child_data_hd;\n                cudaHostGetDevicePointer(&child_data_hd, child_data, 0);\n                // roots copy their data\n                int dummy;\n                void *my_data = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                memcpy ( &child_data[0], data_pinned, n * v.get_block_size()*sizeof(value_type));\n\n                // Loop over parts to consolidate\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data( &child_data[this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()]],\n                                sizeof(value_type) * (this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part]),\n                                current_part,\n                                20001 + current_part );\n                    }\n                }\n\n                // usual path\n                // Upload on host\n                cudaMemcpy(v.raw(), (value_type *)child_data, v.size()* sizeof(value_type), cudaMemcpyDefault);\n                cudaCheckError();\n            } // If root partition\n\n            // Permute based on renumbering vector\n            transformVector(v);\n            cudaCheckError();\n            // Exchange halos\n            int tag = 0;\n            this->exchange_halo(v, tag);\n            cudaCheckError();\n            v.set_unconsolidated_size(n);\n\n            // free host\n            if (child_data) { amgx::memory::cudaFreeHost(child_data); }\n\n            cudaCheckError();\n        } //cla\n    } // If cudaIpcAvailable\n\n    if (!this->m_is_fine_level_glued) // not needed for classcical\n    {\n        if (this->m_is_fine_level_root_partition)\n        {\n            v.swap(temp);\n            int tag = 0;\n            // Root partitions do the exchange\n            this->exchange_halo(v, tag);\n        }\n\n        v.set_unconsolidated_size(n * v.get_block_size());\n        v.set_transformed();\n    }\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::cudaFreeAsync(data_hd); }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformVector(VVector_v &v)\n{\n    if (this->neighbors.size() == 0) { return; }\n    else if (this->renumbering.size() == 0)\n    {\n        v.resize(this->halo_offsets[this->neighbors.size()]*v.get_block_size());\n        return;\n    }\n\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    if (v.size() < this->halo_offsets[this->neighbors.size()]*v.get_block_size())\n    {\n        VVector_v temp(this->halo_offsets[this->neighbors.size()]*v.get_block_size(), types::util<value_type>::get_zero());\n        temp.set_block_dimx(v.get_block_dimx());\n        temp.set_block_dimy(v.get_block_dimy());\n\n        if (v.size() < this->halo_offsets[0]*this->A->get_block_dimx())\n        {\n            FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        //reorder based on row permutation\n        int size = this->halo_offsets[0];\n        int num_blocks = std::min(4096, (size + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n        cudaCheckError();\n        v.swap(temp);\n    }\n    else\n    {\n        VVector_v temp(this->halo_offsets[0]*v.get_block_size());\n        int size = this->halo_offsets[0];\n        int num_blocks = std::min(4096, (size + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n        cudaCheckError();\n        amgx::thrust::copy(temp.begin(), temp.end(), v.begin());\n    }\n\n    cudaCheckError();\n    v.set_transformed();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v)\n{\n    if (this->neighbors.size() == 0 || this->renumbering.size() == 0) { return; }\n\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    VVector_v temp(this->halo_offsets[0]*this->A->get_block_dimx());\n\n    if (v.size() < this->halo_offsets[0]*v.get_block_size())\n    {\n        FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //reorder based on row permutation\n    int size = this->halo_offsets[0];\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    inverse_reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n    //reorder_vector_values<<<num_blocks, 512>>>(temp.raw(), v.raw(), this->inverse_renumbering.raw(), v.get_block_size(), size);\n    cudaCheckError();\n    v.resize(this->halo_offsets[0]*this->A->get_block_dimx());\n    amgx::thrust::copy(temp.begin(), temp.end(), v.begin());\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeDestinationPartitions(INDEX_TYPE upper_threshold, float avg_size, const int num_parts, int &new_num_parts, bool &wantNeighbors)\n{\n    m_destination_partitions.resize(num_parts);\n    std::vector<int> dp(num_parts);\n\n    if (avg_size < 1.f) { avg_size = 1.f; } // avoid floating point exception\n\n    int wanted_num_fine_parts_to_consolidate = ( upper_threshold + (int) avg_size - 1) / ( (int) avg_size );\n    new_num_parts = (num_parts + wanted_num_fine_parts_to_consolidate - 1) / wanted_num_fine_parts_to_consolidate;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        dp[i] = i % new_num_parts;\n    }\n\n    // example wantNeighbors = true -> destination_part = [0 0 0 0 4 4 4 4 8 8 8 8]\n    // example wantNeighbors = false -> destination_part = [0 1 2 3 0 1 2 3 0 1 2 3]\n    if (wantNeighbors)\n    {\n        std::sort (dp.begin(), dp.end());\n        m_destination_partitions[0] = 0;\n\n        for (int i = 1; i < num_parts; i++)\n        {\n            if (dp[i - 1] < dp[i])\n            {\n                m_destination_partitions[i]  = i;\n            }\n            else\n            {\n                m_destination_partitions[i] = m_destination_partitions[i - 1];\n            }\n        }\n    }\n\n    m_my_destination_part =  m_destination_partitions[global_id()];\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeDestinationPartitionsWithCons(int my_id, int num_parts, IVector_h &destination_part, DistributedComms<TConfig> *comms)\n{\n    int device_id = this->A->getResources()->getDevice(0);\n    std::string my_hostname_tmp;\n    comms->get_hostname(my_hostname_tmp);\n    // Append PCI-E ID to string\n    cudaDeviceProp dev_prop;\n    cudaGetDeviceProperties(&dev_prop, device_id);\n    std::stringstream s;\n    s << my_hostname_tmp << \"_\" << dev_prop.pciBusID << \"_\" << dev_prop.pciDeviceID;\n    std::string my_hostname(s.str());\n    std::vector<std::string> hostnames;\n    comms->exchange_hostnames(my_hostname, hostnames, num_parts);\n    std::vector<std::string>::iterator low = std::find( hostnames.begin(), hostnames.end(), my_hostname );\n    int my_destination_part = low - hostnames.begin();\n    // Do a gather into destination_part\n    comms->all_gather(my_destination_part, destination_part, num_parts);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVector(VVector_v &v_in, const void *data, int n, int block_dimy)\n{\n    if (this->isFineLevelConsolidated() || this->isFineLevelGlued())\n    {\n        revertAndDownloadVectorWithCons(v_in, data, n, block_dimy);\n    }\n    else\n    {\n        if ( n == 0 )\n        {\n            FatalError(\"Cannot download if size = 0\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (data == NULL )\n        {\n            FatalError(\"Cannot download to a NULL pointer\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (v_in.size() == 0 )\n        {\n            FatalError(\"Cannot download an empty vector\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        VVector_v v_out;\n        revertVector(v_in, v_out);\n        cudaMemcpy((value_type *)data, v_out.raw(), n * block_dimy * sizeof(value_type), cudaMemcpyDefault);\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v_in, VVector_v &v_out)\n{\n    if (!this->isFineLevelGlued() && this->neighbors.size() == 0 || this->renumbering.size() == 0) { return;}\n\n    if (v_in.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    if (v_in.size() < this->halo_offsets[0]*v_in.get_block_size())\n    {\n        FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int size = this->halo_offsets[0];\n\n    if (v_out.size() != size * this->A->get_block_dimx())\n    {\n        v_out.resize(size * this->A->get_block_dimx());\n    }\n\n    //reorder based on row permutation\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    inverse_reorder_vector_values <<< num_blocks, 512>>>(v_out.raw(), v_in.raw(), this->renumbering.raw(), v_in.get_block_size(), size);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVectorWithCons(VVector_v &v_in, const void *data_pinned, int n, int block_dimy)\n{\n    if (v_in.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    void *root_v_ptr = NULL;\n    int size = v_in.get_unconsolidated_size();\n    int num_rows = size / v_in.get_block_size();\n\n    if (this->m_is_fine_level_root_partition)\n    {\n        root_v_ptr = (void *) v_in.raw();\n    }\n\n    VVector_v temp;\n    temp.set_block_dimx(v_in.get_block_dimx());\n    temp.set_block_dimy(v_in.get_block_dimy());\n    temp.resize(size);\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n\n    if (useCudaIpc)\n    {\n        // Do IPC\n        this->ipcExchangePtr(root_v_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        // Children partition waits for parent to be done updating vector\n        this->ipcWaitForRoot(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        //reorder based on row permutation\n        int num_blocks = std::min(4096, (num_rows + 511) / 512);\n        inverse_reorder_vector_values <<< num_blocks, 512>>>( temp.raw(), (value_type *) root_v_ptr, this->m_row_ids_CONS.raw(), v_in.get_block_size(), num_rows);\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            cudaIpcCloseMemHandle(root_v_ptr);\n        }\n    }\n    else\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            if (this->m_is_fine_level_root_partition)\n            {\n                IVector_h child_n(this->m_num_fine_level_parts_to_consolidate);\n                int max_n = 0;\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_vector(child_n, current_part, 30000 + current_part, i, 1);\n                    }\n                    else\n                    {\n                        child_n[i] = num_rows;\n                    }\n\n                    if (child_n[i] > max_n) { max_n = child_n[i]; }\n                }\n\n                // Resize temp vector\n                VVector_v child_temp;;\n                child_temp.resize(max_n * v_in.get_block_size());\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n                    // Pack the vector to be sent\n                    int num_blocks = std::min(4096, (child_n[i] + 511) / 512);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        inverse_reorder_vector_values <<< num_blocks, 512>>>( child_temp.raw(), (value_type *) root_v_ptr, this->m_child_row_ids[i].raw(), v_in.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                        this->getFineLevelComms()->send_vector(child_temp, current_part, 30001 + current_part, 0, child_n[i]*v_in.get_block_size());\n                    }\n                    else\n                    {\n                        inverse_reorder_vector_values <<< num_blocks, 512>>>( temp.raw(), (value_type *) root_v_ptr, this->m_row_ids_CONS.raw(), v_in.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n            }\n            else\n            {\n                IVector_h size(1);\n                size[0] = num_rows;\n                this->getFineLevelComms()->send_vector(size, this->m_my_fine_level_destination_part, 30000 + this->fine_level_id());\n                this->getFineLevelComms()->recv_vector(temp, this->m_my_fine_level_destination_part, 30001 + this->fine_level_id());\n            }\n        }\n        else if (this->m_is_fine_level_glued) // classical\n        {\n            if (this->m_is_fine_level_root_partition)\n            {\n                temp.resize(v_in.size());\n                revertVector(v_in, temp);\n                cudaCheckError();\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->send_vector( temp,\n                                                                current_part,\n                                                                current_part + 30001,\n                                                                this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()],\n                                                                this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part] );\n                        cudaCheckError();\n                    }\n                }\n            }\n            else\n            {\n                this->getFineLevelComms()->recv_vector(temp, this->m_my_fine_level_destination_part, 30001 + this->fine_level_id());\n                cudaCheckError();\n            }\n\n            temp.resize(this->getConsolidationArrayOffsets()[this->fine_level_id() + 1] - this->getConsolidationArrayOffsets()[this->fine_level_id()]);\n            cudaCheckError();\n        }\n    }\n\n    // Copy on host\n    cudaMemcpy((value_type *)data_pinned, temp.raw(), temp.size() * sizeof(value_type), cudaMemcpyDefault);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformVector(VVector_v &v)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVectorWithCons(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v_in, VVector_v &v_out)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVectorWithCons(VVector_v &v_in, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix_owned()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::obtain_shift_l2g_reordering(index_type n, I64Vector_h &l2g, IVector_h &p, IVector_h &q)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::unpack_partition(index_type *Bp, index_type *Bc, mat_value_type *Bv)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generatePoisson7pt(int nx, int ny, int nz, int P, int Q, int R)\n{\n    FatalError(\"GeneratePoisson7pt only implemented on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrix(\n    int num_rows, int num_nonzeros, const int block_dimx, const int block_dimy, const int *row_offsets, \n    const t_colIndex *col_indices, const mat_value_type *values, int num_ranks, int num_rows_global, const void *diag, const MatrixDistribution &dist)\n{\n    FatalError(\"loadDistributedMatrix only implemented on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingB2Lmaps()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingHaloRows()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsNoCons(int n, int nnz, const mat_value_type *data, const mat_value_type *diag)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsWithCons(int n, int nnz, const mat_value_type *data, const mat_value_type *diag)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createRenumbering(IVector &renumbering)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::remove_boundary(IVector_h &flagArray, IVector_h &B2L_map, int size)\n{\n    for (int i = 0; i < size; i++)\n    {\n        flagArray[B2L_map[i]] = 0;\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::get_unassigned(IVector_h &flagArray, IVector_h &B2L_map, IVector_h &partition_flags, int size, int fa_size/*, int rank*/)\n{\n    for (int i = 0; i < size; i++)\n    {\n        if (B2L_map[i] < fa_size)\n        {\n            if (flagArray[B2L_map[i]] == 0)\n            {\n                flagArray[B2L_map[i]] = 1;\n                partition_flags[i] = 1;\n            }\n        }\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::set_unassigned(IVector_h &partition_flags, IVector_h &partition_renum, IVector_h &B2L_map, IVector_h &renumbering, int size, int max_element, int renum_size/*, int rank*/)\n{\n    for (int i = 0; i < size; i++)\n    {\n        if (B2L_map[i] < renum_size)\n        {\n            if (partition_flags[i] == 1)\n            {\n                renumbering[B2L_map[i]] = max_element + partition_renum[i];\n            }\n\n            B2L_map[i] = renumbering[B2L_map[i]];\n        }\n    }\n}\n\n/* print manager for target rank to a file or stdout */\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::print(char *f, char *s, int trank)\n{\n    DistributedManagerBase<TConfig> *m = this;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int i, j, k, t1, t2;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to output to stdout or a file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = m->A->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#else\n            snprintf(filename, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        fprintf(fid, \"%s\\n\", s);\n        //--- communication info ---\n        //compare neighbors\n        t1 = m->neighbors.size();\n        fprintf(fid, \"neighbors %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->neighbors[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare B2L_rings\n        t1 = B2L_rings.size();\n        fprintf(fid, \"B2L_rings %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->B2L_rings[i].size();\n            fprintf(fid, \"B2L_rings-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->B2L_rings[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //compare B2L_maps\n        t1 = B2L_maps.size();\n        fprintf(fid, \"B2L_maps %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->B2L_maps[i].size();\n            fprintf(fid, \"B2L_maps-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->B2L_maps[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //compare L2H_maps\n        t1 = L2H_maps.size();\n        fprintf(fid, \"L2H_maps %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->L2H_maps[i].size();\n            fprintf(fid, \"L2H_maps-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->L2H_maps[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //--- matrix info ---\n        fprintf(fid, \"num_rows_global=%ld\\n\", num_rows_global);\n        fprintf(fid, \"_num_rows_interior=%d\\n\", m->_num_rows_interior);\n        fprintf(fid, \"_num_rows_owned=%d\\n\", m->_num_rows_owned);\n        fprintf(fid, \"_num_rows_full=%d\\n\", m->_num_rows_full);\n        fprintf(fid, \"_num_rows_all=%d\\n\", m->_num_rows_all);\n        fprintf(fid, \"_num_nz_interior=%d\\n\", m->_num_nz_interior);\n        fprintf(fid, \"_num_nz_owned=%d\\n\", m->_num_nz_owned);\n        fprintf(fid, \"_num_nz_full=%d\\n\", m->_num_nz_full);\n        fprintf(fid, \"_num_nz_all=%d\\n\", m->_num_nz_all);\n        //compare # halo rows and halo offsets\n        fprintf(fid, \"# halo rings %d and rows %d\\n\", m->num_halo_rings(), m->num_halo_rows());\n        t1 = m->halo_offsets.size();\n        fprintf(fid, \"halo_offsets %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_offsets[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo ranges\n        t1 = m->halo_ranges.size();\n        fprintf(fid, \"halo_ranges %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_ranges[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo ranges (host)\n        t1 = m->halo_ranges_h.size();\n        fprintf(fid, \"halo_ranges_h %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_ranges_h[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare part offsets\n        t1 = m->part_offsets.size();\n        fprintf(fid, \"part_offsets %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->part_offsets[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare part offsets (host)\n        t1 = m->part_offsets_h.size();\n        fprintf(fid, \"part_offsets_h %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->part_offsets_h[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare interior row list\n        t1 = m->interior_rows_list.size();\n        fprintf(fid, \"interior_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->interior_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare boundary row list\n        t1 = m->boundary_rows_list.size();\n        fprintf(fid, \"boundary_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->boundary_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo1 row list\n        t1 = m->halo1_rows_list.size();\n        fprintf(fid, \"halo1_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo1_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        fprintf(fid, \"pointers halo_rows=%p and halo_btl=%p\\n\", m->halo_rows, m->halo_btl);\n        //--- packing info ---\n        //compare local to global map\n        t1 = m->local_to_global_map.size();\n        fprintf(fid, \"local_to_global_map %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->local_to_global_map[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare renumbering\n        t1 = m->renumbering.size();\n        fprintf(fid, \"renumbering %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->renumbering[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare inverse renumbering\n        t1 = m->inverse_renumbering.size();\n        fprintf(fid, \"inverse_renumbering %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->inverse_renumbering[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //--- GPU related and miscellaneous info\n        //streams\n        fprintf(fid, \"streams i=%p, b=%p\\n\", m->get_int_stream(), m->get_bdy_stream());\n        //miscellaneous info\n        int64_t bi = m->base_index();       //inlined function\n        int np = m->get_num_partitions();   //inlined function\n        int rp = (int)m->isRootPartition(); //cast from boolean to int\n        fprintf(fid, \"gid=%d,bi=%ld,np=%d,rp=%d,ir=%d,in=%d,bn=%d\\n\", m->global_id(), bi, np, rp, m->index_range(), m->num_interior_nodes(), m->num_boundary_nodes());\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        cudaGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n/* print manager for target rank to a file or stdout (for all ranks) */\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::printToFile(char *f, char *s)\n{\n    DistributedManagerBase<TConfig> *m = this;\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different ranks/processes\n    m->print(f, s, rank);\n}\n\n/* compare two managers */\ntemplate<class TConfig>\nint DistributedManagerBase<TConfig>::compare(DistributedManagerBase<TConfig> *m2)\n{\n    DistributedManagerBase<TConfig> *m1 = this;\n    int i, j, t1, t2;\n    //compare neighbors\n    t1 = m1->neighbors.size();\n    t2 = m2->neighbors.size();\n\n    if (t1 != t2)\n    {\n        return 1;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->neighbors[i] != m2->neighbors[i])\n        {\n            return 2;\n        }\n    }\n\n    //compare B2L_rings\n    for (i = 0; i < (m1->neighbors.size()); i++)\n    {\n        t1 = m1->B2L_rings[i].size();\n        t2 = m2->B2L_rings[i].size();\n\n        if (t1 != t2)\n        {\n            return 3;\n        }\n\n        for (j = 0; j < t1; j++)\n        {\n            if (m1->B2L_rings[i][j] != m2->B2L_rings[i][j])\n            {\n                return 4;\n            }\n        }\n    }\n\n    //compare B2L_maps\n    t1 = m1->B2L_maps.size();\n    t2 = m2->B2L_maps.size();\n\n    if (t1 != t2)\n    {\n        return 5;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->B2L_maps[i] != m2->B2L_maps[i])\n        {\n            return 6;\n        }\n    }\n\n    //compare L2H_maps\n    t1 = m1->L2H_maps.size();\n    t2 = m2->L2H_maps.size();\n\n    if (t1 != t2)\n    {\n        return 7;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->L2H_maps[i] != m2->L2H_maps[i])\n        {\n            return 8;\n        }\n    }\n\n    return 0;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistributedManager< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~DistributedManager< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >()\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistributedManager< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::~DistributedManager< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >()\n{\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2Lmaps(IVector_h_vector &dest_coarse_B2L_maps, IVector_h_vector &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    consB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2Lmaps(IVector_d_vector &dest_coarse_B2L_maps, IVector_d_vector &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    consB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2LmapsOnRoot(int &num_consolidated_neighbors, IVector_h_vector &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, IVector_h_vector &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    consB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_ids, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, comms);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2LmapsOnRoot(int &num_consolidated_neighbors, IVector_d_vector &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, IVector_d_vector &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    consB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_ids, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, comms);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DistributedManager<TemplateMode<CASE>::Type >;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void DistributedManager<TemplateMode<CASE>::Type>::loadDistributedMatrix( \\\n    int, int, const int, const int, const int*, const int *col_indices, const mat_value_type*, int, int, const void*, const MatrixDistribution &dist); \\\n    template void DistributedManager<TemplateMode<CASE>::Type>::loadDistributedMatrix( \\\n    int, int, const int, const int, const int*, const int64_t *col_indices, const mat_value_type*, int, int, const void*, const MatrixDistribution &dist);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DistributedManagerBase<TemplateMode<CASE>::Type >;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <distributed/distributed_manager.h>\n#include <distributed/comms_mpi_gpudirect.h>\n#include <distributed/comms_mpi_hostbuffer_stream.h>\n#include <distributed/comms_visitors.h>\n#include <thrust/scan.h>\n#include <thrust/sort.h>\n#include <thrust/remove.h>\n#include <thrust/unique.h>\n#include <thrust/binary_search.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust_wrapper.h>\n#include <basic_types.h>\n#include <error.h>\n#include <util.h>\n#include <types.h>\n#include <iostream>\n#include <sstream>\n#include <fstream>\n#include <assert.h>\n#include \"hip/hip_runtime.h\"\n#include \"reorder_partition.h\"\n\n#include \"amgx_types/util.h\"\n\n#include <algorithm>\n#include <iostream> //debug only:\n\nstruct is_my_part\n{\n    const int _my_part;\n    is_my_part(int my_part) : _my_part(my_part) { }\n\n    __host__ __device__\n    bool operator()(const int part)\n    {\n        return (part == _my_part);\n    }\n};\n\nnamespace amgx\n{\n\nstatic int insertDiagonals = 1;\n\ntemplate <typename index_type>\nstatic __device__ __forceinline__\nindex_type internal_index(index_type i, index_type j, index_type k, index_type nx, index_type ny, index_type nz)\n{\n    return k * (nx * ny) + j * nx + i;\n}\n\ntemplate <typename index_type>\nstatic __device__ __forceinline__\nint64_t get_global_offset(index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    int rank_id = r * (P * Q) + q * P + p;\n    return ((int64_t) rank_id) * ((int64_t) num_rows);\n}\n\ntemplate <typename index_type>\n__global__\nvoid poisson7pt_count_row_len(index_type *row_len, index_type nx, index_type ny, index_type nz, index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_rows ; tidx += blockDim.x * gridDim.x)\n    {\n        /* compute p,q,r from P,Q,R and myid */\n        int i = tidx % nx; // Position in x direction\n        int j = (( tidx - i) / nx) % ny; // Position in y\n        int k = ( tidx - i - nx * j) / ( nx * ny ); // Position in z\n        int substract = ((i == 0) && (p == 0));\n        substract += ((i == nx - 1) && (p == P - 1));\n        substract += ((j == 0) && (q == 0));\n        substract += ((j == ny - 1) && (q == Q - 1));\n        substract += ((k == 0) && (r == 0));\n        substract += ((k == nz - 1) && (r == R - 1));\n        // Store 7 in position (num_rows+1), such that row_len[num_rows+1] = 0\n        //substract = (tidx == num_rows+1) ? 7 : substract;\n        row_len[tidx] = 7 - substract;\n    }\n}\n\ntemplate <typename index_type, typename mat_value_type>\n__global__\nvoid poisson7pt_set_col_values(const index_type *__restrict__ row_offsets, index_type *__restrict__ col_indices, mat_value_type *__restrict__  values, index_type *__restrict__ diag, int64_t *__restrict__ local_to_global, index_type nx, index_type ny, index_type nz, index_type p, index_type q, index_type r, index_type P, index_type Q, index_type R, index_type num_rows)\n{\n    for (int row = threadIdx.x + blockIdx.x * blockDim.x; row < num_rows ; row += blockDim.x * gridDim.x)\n    {\n        /* compute p,q,r from P,Q,R and myid */\n        int i = row % nx; // Position in x direction\n        int j = (( row - i) / nx) % ny; // Position in y\n        int k = ( row - i - nx * j) / ( nx * ny ); // Position in z\n        int halo_offset = num_rows;\n        int pos = row_offsets[row];\n        // Diagonal element\n        diag[row] = pos;\n        col_indices[pos] = row;\n        values[pos++] = types::util<mat_value_type>::get_one() * 6.;\n\n        // ----------------------------\n        // Neighbor at position i-1\n        // ----------------------------\n        if (i)\n        {\n            // Has a i-1 neighbor, which is an internal node at position (i-1,j,k)\n            col_indices[pos] = internal_index(i - 1, j, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (p)\n        {\n            // Has a i-1 neighbor, which is a halo node\n            int halo_index = halo_offset + k * ny + j;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p - 1, q, r, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(nx - 1, j, k, nx, ny, nz);\n        }\n\n        if (p)\n        {\n            halo_offset += ny * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position i+1\n        // ----------------------------\n        if (i < nx - 1)\n        {\n            // Has i+1 neighbor, which is an internal node at position (i+1,j,k)\n            col_indices[pos] = internal_index(i + 1, j, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (p < P - 1)\n            {\n                // Has i+1 neighbor, which is a halo node\n                int halo_index = halo_offset + k * ny + j;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p + 1, q, r, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(0, j, k, nx, ny, nz);\n            }\n        }\n\n        if (p < P - 1)\n        {\n            halo_offset += ny * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position j-1\n        // ----------------------------\n        if (j)\n        {\n            // Has a j-1 neighbor, which is an internal node at position (i,j-1,k)\n            col_indices[pos] = internal_index(i, j - 1, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (q)\n        {\n            // Has a j-1 neighbor, which is a halo node\n            int halo_index = halo_offset + k * nx + i;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p, q - 1, r, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(i, ny - 1, k, nx, ny, nz);\n        }\n\n        if (q)\n        {\n            halo_offset += nx * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position j+1\n        // ----------------------------\n        if (j < ny - 1)\n        {\n            // Has a j+1 neighbor, which is an internal node at position (i,j+1,k)\n            col_indices[pos] = internal_index(i, j + 1, k, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (q < Q - 1)\n            {\n                // Has a j+1 neighbor, which is a halo node\n                int halo_index = halo_offset + k * nx + i;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p, q + 1, r, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(i, 0, k, nx, ny, nz);\n            }\n        }\n\n        if (q < Q - 1)\n        {\n            halo_offset += nx * nz;\n        }\n\n        // ----------------------------\n        // Neighbor at position k-1\n        // ----------------------------\n        if (k)\n        {\n            // Has a k-1 neighbor, which is an internal node at position (i,j,k-1)\n            col_indices[pos] = internal_index(i, j, k - 1, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else if (r)\n        {\n            // Has a k-1 neighbor, which is a halo node\n            int halo_index = halo_offset + j * nx + i;\n            col_indices[pos] = halo_index;\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n            int64_t global_offset = get_global_offset(p, q, r - 1, P, Q, R, num_rows);\n            local_to_global[halo_index - num_rows] = global_offset + internal_index(i, j, nz - 1, nx, ny, nz);\n        }\n\n        if (r)\n        {\n            halo_offset += nx * ny;\n        }\n\n        // ----------------------------\n        // Neighbor at position k+1\n        // ----------------------------\n        if (k < nz - 1)\n        {\n            // Has a k+1 neighbor, which is an internal node at position (i,j,k+1)\n            col_indices[pos] = internal_index(i, j, k + 1, nx, ny, nz);\n            values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n        }\n        else\n        {\n            if (r < R - 1)\n            {\n                // Has a k+1 neighbor, which is a halo node\n                int halo_index = halo_offset + j * nx + i;\n                col_indices[pos] = halo_index;\n                values[pos++] = types::util<mat_value_type>::invert(types::util<mat_value_type>::get_one());\n                int64_t global_offset = get_global_offset(p, q, r + 1, P, Q, R, num_rows);\n                local_to_global[halo_index - num_rows] = global_offset + internal_index(i, j, 0, nx, ny, nz);\n            }\n        }\n\n        if (r < R - 1)\n        {\n            halo_offset += nx * ny;\n        }\n    }\n}\n\ntemplate <typename mat_value_type>\n__global__\nvoid set_halo_cols_values(int *row_offsets, int *col_indices, mat_value_type *values, int n, int total_rows, int bsize)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < (total_rows - n) )\n    {\n        int offset = row_offsets[n + tid];\n        col_indices[offset] = n + tid;\n#pragma unroll\n\n        for (int i = 0; i < bsize; i++)\n        {\n            values[offset * bsize + i] = types::util<mat_value_type>::get_one();    // This is arbitrary\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename mat_value_type>\n__global__\nvoid zero_copy_row_lengths_ids_offsets(int *d_old_row_offsets, int *root_row_offsets, int *d_row_ids, int n, int total_num_halos, mat_value_type *diag)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < n + total_num_halos)\n    {\n        int new_row_id = d_row_ids[tid];\n\n        if (tid < n)\n        {\n            int start = d_old_row_offsets[tid];\n            int row_length = d_old_row_offsets[tid + 1] - start; // zero-copy\n\n            if (diag != NULL) // will insert the diagonal\n            {\n                row_length++;\n            }\n\n            root_row_offsets[new_row_id] = row_length;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate< typename mat_value_type>\n__global__\nvoid ipc_consolidation_upload_matrix(int num_rows, int *row_ids, const int *old_row_offsets, int *new_row_offsets, const int *h_old_col_indices, int *new_col_indices, const mat_value_type *h_old_values, mat_value_type *new_values, const mat_value_type *h_old_diag, int bsize)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        int new_row = row_ids[row];\n        int src_base = old_row_offsets[row];\n        int dst_base = new_row_offsets[new_row];\n\n        // Insert the diagonal at the beginning of each row\n        if (h_old_diag != NULL)\n        {\n            new_col_indices[dst_base] = new_row;\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[dst_base * bsize + j] = h_old_diag[row * bsize + j];\n            }\n\n            // Increment dst_base by one\n            dst_base++;\n        }\n\n        int end = old_row_offsets[row + 1] - src_base;\n\n        for (int i = 0; i < end; i++)\n        {\n            int old_col = h_old_col_indices[src_base + i];\n            int new_col = row_ids[old_col];\n            new_col_indices[dst_base + i] = new_col;\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[ (dst_base + i)*bsize + j ] = h_old_values[ (src_base + i) * bsize + j ];\n            }\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate< typename mat_value_type>\n__global__\nvoid ipc_consolidation_replace_values(int num_rows, int *row_ids, const int *old_row_offsets, int *new_row_offsets, const mat_value_type *h_old_values, mat_value_type *new_values, const mat_value_type *h_old_diag, int bsize)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        int new_row = row_ids[row];\n        int src_base = old_row_offsets[row];\n        int dst_base = new_row_offsets[new_row];\n\n        // Insert the diagonal at the beginning of each row\n        if (h_old_diag != NULL)\n        {\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[dst_base * bsize + j] = h_old_diag[row * bsize + j];\n            }\n\n            // Increment dst_base by one\n            dst_base++;\n        }\n\n        int end = old_row_offsets[row + 1] - src_base;\n\n        for (int i = 0; i < end; i++)\n        {\n#pragma unroll\n\n            for (int j = 0; j < bsize; j++)\n            {\n                new_values[ (dst_base + i)*bsize + j ] = h_old_values[ (src_base + i) * bsize + j ];\n            }\n        }\n\n        row += gridDim.x * blockDim.x;\n    }\n}\n\n\n__global__ void flag_halo_ids_kernel(INDEX_TYPE *flags, INDEX_TYPE *ids, INDEX_TYPE offset, INDEX_TYPE size, INDEX_TYPE upper)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[ids[idx] - offset] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void read_halo_ids_kernel(INDEX_TYPE *flags, INDEX_TYPE *ids, INDEX_TYPE offset, INDEX_TYPE size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        ids[idx] = flags[ids[idx] - offset];\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate<class T>\n__global__ void reorder_vector_values(T *dst, const T *src, const INDEX_TYPE *map, INDEX_TYPE blocksize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * (blockDim.x / blocksize) + threadIdx.x / blocksize; //vectorised by block size\n    int vec_id = threadIdx.x % blocksize;\n\n    if (threadIdx.x >= (blockDim.x / blocksize)*blocksize ) { return; }\n\n    while (row < num_rows)\n    {\n        dst[map[row]*blocksize + vec_id] = src[row * blocksize + vec_id];\n        row += gridDim.x * (blockDim.x / blocksize);\n    }\n}\n\ntemplate<class T>\n__global__ void inverse_reorder_vector_values(T *dst, T *src, INDEX_TYPE *map, INDEX_TYPE blocksize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * (blockDim.x / blocksize) + threadIdx.x / blocksize;\n    int vec_id = threadIdx.x % blocksize;\n\n    if (threadIdx.x >= (blockDim.x / blocksize)*blocksize ) { return; }\n\n    while (row < num_rows)\n    {\n        dst[row * blocksize + vec_id] = src[map[row] * blocksize + vec_id];\n        row += gridDim.x * (blockDim.x / blocksize);\n    }\n}\n\n__global__ void remove_boundary_kernel(INDEX_TYPE *flags, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < size)\n    {\n        flags[maps[element]] = 0; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void get_unassigned_kernel(INDEX_TYPE *unassigned_flags, INDEX_TYPE *map, INDEX_TYPE *output, INDEX_TYPE part_size, INDEX_TYPE uf_size )\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < uf_size)\n        {\n            if (unassigned_flags[map[idx]] == 0)\n            {\n                unassigned_flags[map[idx]] = 1;\n                output[idx] = 1;\n            }\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void set_unassigned_kernel(INDEX_TYPE *part_assigned_flags, INDEX_TYPE *part_num, INDEX_TYPE *map, INDEX_TYPE *renum, INDEX_TYPE part_size, INDEX_TYPE max_element, INDEX_TYPE renum_size /*, INDEX_TYPE rank*/)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < renum_size)\n        {\n            if (part_assigned_flags[idx] == 1)\n            {\n                renum[map[idx]] = max_element + part_num[idx];\n            }\n\n            //also update the B2L map\n            map[idx] = renum[map[idx]];\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void renumber_b2l_maps(INDEX_TYPE *map, INDEX_TYPE *renum, INDEX_TYPE part_size, INDEX_TYPE renum_size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < part_size)\n    {\n        if (map[idx] < renum_size)\n        {\n            //update the B2L map\n            map[idx] = renum[map[idx]];\n            idx += blockDim.x * gridDim.x;\n        }\n    }\n}\n\n__global__ void calc_inverse_renumbering(INDEX_TYPE *renum, INDEX_TYPE *irenum, INDEX_TYPE max_element)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < max_element)\n    {\n        if (renum[idx] < 0 || renum[idx] >= max_element) { printf(\"Renumbering error: %d %d\\n\", renum[idx], max_element); }\n\n        irenum[renum[idx]] = idx;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void create_halo_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, int64_t base_index, INDEX_TYPE map_offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = map_offset + row;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void apply_h2l2b_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, int64_t base_index, INDEX_TYPE *b2l_map, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = b2l_map[row];\n        row += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <int coop>\n__global__ void map_col_indices_and_count_rowlen(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *row_length,\n        INDEX_TYPE *mapping, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal)\n{\n    extern __shared__ volatile int reduction[];\n    int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        int valid = 0;\n\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += coop) //this may look horrible, but I expect low branch divergence, because col indices in a row usually belong to the same partition (or at most one more)\n        {\n            int colIdx = col_indices[idx];\n            int new_col_idx = mapping[colIdx];\n\n            if (new_col_idx >= 0)\n            {\n                valid++;\n                col_indices[idx] = new_col_idx;\n            }\n            else\n            {\n                col_indices[idx] = -1;\n            }\n        }\n\n        reduction[threadIdx.x] = valid;\n\n        for (int s = 2; s > 0; s >>= 1)\n        {\n            if (coopIdx < s)\n            {\n                reduction[threadIdx.x] += reduction[threadIdx.x + s];\n            }\n\n            __syncthreads();\n        }\n\n        if (coopIdx == 0)\n        {\n            row_length[row] = reduction[threadIdx.x] + insert_diagonal;\n        }\n\n        row += gridDim.x * blockDim.x / coop;\n    }\n}\n\n\n__global__ void renumber_P_col_indices(INDEX_TYPE *__restrict__ col_indices, const INDEX_TYPE *__restrict__ renum, INDEX_TYPE num_owned_coarse_pts, INDEX_TYPE num_owned_fine_pts)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < num_owned_fine_pts )\n    {\n        INDEX_TYPE col_id = col_indices[idx];\n\n        if (col_id < num_owned_coarse_pts)\n        {\n            col_indices[idx] = renum[col_id];\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n\ntemplate <int coop, class T>\n__global__ void reorder_R_matrix(const INDEX_TYPE *old_rows, const INDEX_TYPE *old_cols, const T *old_vals, const INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE num_owned_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = row < num_owned_rows ? rows[renumbering[row]] : src_base;\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = old_vals[src_base * bsize + i];\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1] - src_base; i += coop)\n        {\n            cols[dst_base + i] = old_cols[src_base + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\n\n\ntemplate <int coop, class T>\n__global__ void reorder_whole_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        if (insert_diagonal)\n        {\n            if (coopIdx == 0) { cols[dst_base] = renumbering[row]; }\n\n            for (int i = coopIdx; i < bsize; i += coop)\n            {\n                vals[dst_base * bsize + i] = old_vals[(old_rows[num_rows] + row) * bsize + i];\n            }\n\n            dst_base++;\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = old_vals[src_base * bsize + i];\n        }\n\n        for (int i = coopIdx; i < old_rows[row + 1] - src_base; i += coop)\n        {\n            cols[dst_base + i] = old_cols[src_base + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void replace_values_matrix(const T *src_vals_h, const T *src_diag_h, const INDEX_TYPE *old_rows, const INDEX_TYPE *rows, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        for (int i = coopIdx; i < bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_diag_h[row * bsize + i];\n        }\n\n        dst_base++;\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_vals_h[src_base * bsize + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\ntemplate <int coop, class T>\n__global__ void replace_values_matrix(const T *src_vals_h, const INDEX_TYPE *old_rows, const INDEX_TYPE *rows, T *vals, const INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x / coop  + threadIdx.x / coop;\n    int coopIdx = threadIdx.x % coop;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst_base = rows[renumbering[row]];\n\n        for (int i = coopIdx; i < old_rows[row + 1]*bsize - src_base * bsize; i += coop)\n        {\n            vals[dst_base * bsize + i] = src_vals_h[src_base * bsize + i];\n        }\n\n        row += blockDim.x * gridDim.x / coop;\n    }\n}\n\n//TODO: optimize by vectorizing\ntemplate <class T>\n__global__ void reorder_whole_halo_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals,\n        INDEX_TYPE *renumbering, INDEX_TYPE bsize, INDEX_TYPE num_rows, INDEX_TYPE insert_diagonal,\n        INDEX_TYPE global_offset, INDEX_TYPE local_offset, INDEX_TYPE halo_rows)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst = rows[row];\n\n        if (insert_diagonal)\n        {\n            cols[dst] = global_offset + row;\n\n            for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(old_rows[halo_rows - local_offset] + local_offset + row) * bsize + j]; }\n\n            dst++;\n        }\n\n        for (int i = 0; i < old_rows[row + 1] - src_base; i++)\n        {\n            INDEX_TYPE colIdx = old_cols[src_base + i];\n\n            if (colIdx >= 0)\n            {\n                cols[dst] = colIdx;\n\n                for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(src_base + i) * bsize + j]; }\n\n                dst++;\n            }\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_rowlen_reorder(INDEX_TYPE *row_offsets, INDEX_TYPE *row_len, INDEX_TYPE *map, INDEX_TYPE size, INDEX_TYPE insert_diag)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        row_len[map[idx]] = row_offsets[idx + 1] - row_offsets[idx] + insert_diag;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::remove_boundary(IVector_d &flagArray, IVector_d &B2L_map, int size)\n{\n    int num_blocks = std::min(4096, (size + 127) / 128);\n    remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), B2L_map.raw(), size);\n    cudaCheckError();\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::get_unassigned(IVector_d &flagArray, IVector_d &B2L_map, IVector_d &partition_flags, int size, int global_size /*, int rank*/)\n{\n    int num_blocks = std::min(4096, (size + 191) / 192);\n    get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n            B2L_map.raw(),\n            partition_flags.raw(), size, global_size /*, rank*/);\n    cudaCheckError();\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::set_unassigned(IVector_d &partition_flags, IVector_d &partition_renum, IVector_d &B2L_map, IVector_d &renumbering, int size, int max_element, int global_size /*, int rank*/)\n{\n    int num_blocks = std::min(4096, (size + 191) / 192);\n    set_unassigned_kernel <<< num_blocks, 192>>>(partition_flags.raw(),\n            partition_renum.raw(),\n            B2L_map.raw(),\n            renumbering.raw(),\n            size, max_element, global_size /*,rank*/);\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig >\ninline void DistributedManagerBase<TConfig>::set_initialized(IVector &row_offsets)\n{\n    // For P and R sizes the sizes are fixed at creation\n    if(m_fixed_view_size)\n    {\n        return;\n    }\n\n    if (neighbors.size() > 0)\n    {\n        //distributed: cache num_rows/num_nz for different views\n        _num_rows_interior = _num_interior_nodes;\n        _num_nz_interior = row_offsets[_num_rows_interior];\n        _num_rows_owned = _num_interior_nodes + _num_boundary_nodes;\n        _num_nz_owned = row_offsets[_num_rows_owned];\n        _num_rows_full = halo_offsets[neighbors.size()];\n\n        if (_num_rows_full >= row_offsets.size())\n        {\n            _num_nz_full = row_offsets[row_offsets.size() - 1];\n        }\n        else\n        {\n            _num_nz_full = row_offsets[_num_rows_full];\n        }\n\n        _num_rows_all = halo_offsets[halo_offsets.size() - 1];\n        _num_nz_all = _num_nz_full;\n    }\n    else\n    {\n        _num_rows_interior = _num_interior_nodes;\n        _num_nz_interior = row_offsets[_num_rows_interior];\n        _num_rows_owned = _num_interior_nodes;\n        _num_nz_owned = row_offsets[_num_rows_owned];\n        _num_rows_full = _num_rows_owned;\n        _num_nz_full = _num_nz_owned;\n        _num_rows_all = _num_rows_owned;\n        _num_nz_all = _num_nz_owned;\n    }\n}\n\ntemplate <class TConfig >\nvoid DistributedManagerBase<TConfig>::createAggregatesRenumbering(IVector_h &renumbering, IVector_h_vector &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    createAggRenumbering(renumbering, B2L_maps, size, num_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n}\n\ntemplate <class TConfig >\nvoid DistributedManagerBase<TConfig>::createAggregatesRenumbering(IVector_d &renumbering, IVector_d_vector &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    createAggRenumbering(renumbering, B2L_maps, size, num_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n}\n\ntemplate <class TConfig >\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::createAggRenumbering(IVector_hd &renumbering, std::vector<IVector_hd> &B2L_maps, int size, int num_neighbors, int &num_interior_aggregates, int &num_boundary_aggregates, int num_rings)\n{\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //int num_neighbors = this->neighbors.size();\n    if (num_neighbors == 0)\n    {\n        num_boundary_aggregates = 0;\n        num_interior_aggregates = size;\n        return;\n    }\n\n    //initial size to size+1 so we have the total size after a scan\n    int global_size = size;\n    renumbering.resize(size + 1);\n    //\n    // Step 1 - in the main matrix, separate interior and boundary nodes (1/0 in flagArray), renumber interior ones with an exclusive scan\n    //\n    IVector_hd flagArray(size + 1);\n    thrust_wrapper::fill<IVector_hd::TConfig::memSpace>(flagArray.begin(), flagArray.begin() + size + 1, 1);\n    cudaCheckError();\n\n    //sets 1 for interior nodes, 0 for boundary node\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = B2L_maps[i].size();\n        remove_boundary(flagArray, B2L_maps[i], size);\n    }\n\n    //gets the renumbering of interior nodes\n    thrust_wrapper::exclusive_scan<IVector_hd::TConfig::memSpace>(flagArray.begin(), flagArray.begin() + size + 1, renumbering.begin());\n    cudaCheckError();\n    //\n    // Step 2 - Renumber nodes that are in the boundary, stepping through each B2L map, and renumbering ones that have not been renumbered yet\n    //\n    //what is the biggest B2L size\n    INDEX_TYPE max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > B2L_maps[i].size() ? max_size : B2L_maps[i].size();\n    }\n\n    //allocate work vectors (should be pretty small)\n    IVector_hd partition_flags(max_size);\n    IVector_hd partition_renum(max_size);\n    //the number of renumbered nodes so far\n    int max_element = renumbering[size];\n    num_interior_aggregates = max_element;\n    num_boundary_aggregates = size - max_element;\n    renumbering.resize(size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //find nodes that are part of the current boundary and they haven't been renumbered yet\n        thrust_wrapper::fill<IVector_hd::TConfig::memSpace>(partition_flags.begin(), partition_flags.begin() + max_size, 0);\n        int size = B2L_maps[i].size();\n        get_unassigned(flagArray, B2L_maps[i], partition_flags, size, global_size/*,0*/);\n        //calculate the local renumbering (within this boundary region) of these nodes\n        thrust_wrapper::exclusive_scan<IVector_hd::TConfig::memSpace>(partition_flags.begin(), partition_flags.begin() + max_size, partition_renum.begin());\n        //apply renumbering to the big numbering table\n        set_unassigned(partition_flags, partition_renum, B2L_maps[i], renumbering, size, max_element, global_size/*,0*/);\n        //update the number of renumbered nodes\n        max_element += partition_renum[max_size - 1] + partition_flags[max_size - 1];\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\ninline DistributedManagerBase<TConfig>::DistributedManagerBase(Matrix<TConfig> &a) :\n    m_fine_level_comms(NULL), A(&a), m_pinned_buffer_size(0), m_pinned_buffer(NULL), _num_interior_nodes(0), _num_boundary_nodes(0), _comms(NULL), has_B2L(false),\n    neighbors(_neighbors), B2L_maps(_B2L_maps), L2H_maps(_L2H_maps),  B2L_rings(_B2L_rings),\n    halo_rows_ref_count(0), halo_btl_ref_count(0), halo_ranges(_halo_ranges), halo_ranges_h(_halo_ranges_h), part_offsets(_part_offsets), part_offsets_h(_part_offsets_h), halo_rows(NULL), halo_btl(NULL), m_is_root_partition(false), m_is_glued(false), m_is_fine_level_glued(false), m_is_fine_level_consolidated(false), m_is_fine_level_root_partition(false), m_use_cuda_ipc_consolidation(false), m_fixed_view_size(false)\n{\n    hipEventCreate(&comm_event);\n    cudaCheckError();\n    hipStreamCreateWithFlags(&m_int_stream, hipStreamNonBlocking);\n    cudaCheckError();\n    hipStreamCreateWithFlags(&m_bdy_stream, hipStreamNonBlocking);\n    cudaCheckError();\n    this->createComms(A->getResources());\n    int my_id = this->getComms()->get_global_id();\n    int num_parts = this->getComms()->get_num_partitions();\n    this->set_global_id(my_id);\n    this->set_num_partitions(num_parts);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generatePoisson7pt(int nx, int ny, int nz, int P, int Q, int R)\n{\n    int my_id = this->getComms()->get_global_id();\n    int p, q, r;\n\n    if (nx < P  || ny < Q || nz < R)\n    {\n        FatalError(\"(nx < P) or (ny < Q) or (nz < R) not supported\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    /* compute p,q,r from P,Q,R and myid */\n    p = my_id % P; // Position in x direction\n    q = (( my_id - p) / P) % Q; // Position in y\n    r = ( my_id - p - P * q) / ( P * Q ); // Position in z\n    // Create A.row_indices, A.col_indices, A.values, A.diag\n    int num_rows = nx * ny * nz;\n    int num_nonzeros = num_rows * 7; // Ignoring any boundary, 7 nnz per row\n    int num_substract = 0;\n\n    if (p == 0) { num_substract += ny * nz; }\n\n    if (p == P - 1) { num_substract += ny * nz; }\n\n    if (q == 0) { num_substract += nx * nz; }\n\n    if (q == Q - 1) { num_substract += nx * nz; }\n\n    if (r == 0) { num_substract += nx * ny; }\n\n    if (r == R - 1) { num_substract += nx * ny; }\n\n    num_nonzeros -= num_substract;\n    int num_halo_nodes = 2 * (ny * nz + nx * nz + nx * ny) - num_substract;\n    this->local_to_global_map.resize(num_halo_nodes);\n    this->A->set_initialized(0);\n    this->A->resize(0, 0, 0, 1, 1, 1);\n    this->A->addProps(CSR);\n    this->A->resize(num_rows, num_rows + num_halo_nodes, num_nonzeros, 1, 1, 1);\n    const int cta_size = 128;\n    const int grid_size = std::min( 4096, (num_rows + cta_size - 1) / cta_size );\n    poisson7pt_count_row_len <<< grid_size, cta_size>>>(this->A->row_offsets.raw(), nx, ny, nz, p, q, r, P, Q, R, num_rows);\n    cudaCheckError();\n    thrust_wrapper::exclusive_scan<AMGX_device>(this->A->row_offsets.begin(), this->A->row_offsets.end(), this->A->row_offsets.begin());\n    cudaCheckError();\n    // Now set nonzeros columns and values\n    // TODO: vectorize this\n    const int grid_size2 = std::min( 4096, (num_rows + cta_size - 1) / cta_size );\n    poisson7pt_set_col_values <<< grid_size2, cta_size>>>\n    (this->A->row_offsets.raw(),\n     this->A->col_indices.raw(),\n     this->A->values.raw(),\n     this->A->diag.raw(),\n     this->local_to_global_map.raw(),\n     nx, ny, nz,\n     p, q, r,\n     P, Q, R,\n     num_rows);\n    cudaCheckError();\n    // fill parts_offsets_h\n    // All ranks have same number of nodes\n    int num_ranks = P * Q * R;\n    this->part_offsets_h.resize(num_ranks + 1);\n    this->part_offsets_h[0] = (int64_t) 0;\n\n    for (int i = 1; i < num_ranks + 1; i++)\n    {\n        this->part_offsets_h[i] = this->part_offsets_h[i - 1] + (int64_t) num_rows;\n    }\n\n    // Device to host copy\n    this->part_offsets = this->part_offsets_h;\n    this->num_rows_global = P * Q * R * nx * ny * nz;\n//  this->A->set_initialized(1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_SetOffsets(\n    int num_ranks, int num_rows_global, const t_colIndex* partition_offsets)\n{\n    // fill part offsets internal data structures\n    this->part_offsets_h.resize(num_ranks + 1);\n\n    for (int i = 0; i <= num_ranks; i++)\n    {\n        this->part_offsets_h[i] = partition_offsets[i];\n    }\n    // copy to device\n    this->part_offsets = this->part_offsets_h;\n    // set num of global rows\n    this->num_rows_global = num_rows_global;\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nstd::map<t_colIndex, int> DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_LocalToGlobal(int num_rows, I64Vector_h &off_diag_cols)\n{\n    // sort global column indices\n    amgx::thrust::sort(off_diag_cols.begin(), off_diag_cols.end());\n    // find unique columns and set local <-> global mappings\n    // 1) Removed unneeded vector 2) Create map on host first, upload later (less thrust calls)\n    I64Vector_h local_to_global_h;\n    std::map<t_colIndex, int> global_to_local;        // temporary\n\n    if (off_diag_cols.size() > 0)\n    {\n        global_to_local[off_diag_cols[0]] = num_rows;\n        local_to_global_h.push_back(off_diag_cols[0]);\n    }\n\n    for (int i = 1; i < off_diag_cols.size(); i++)\n    {\n        if (off_diag_cols[i] != off_diag_cols[i - 1])\n        {\n            global_to_local[off_diag_cols[i]] = num_rows + local_to_global_h.size();\n            local_to_global_h.push_back(off_diag_cols[i]);\n        }\n    }\n    // Upload finished map in one piece\n    this->local_to_global_map.resize(local_to_global_h.size());\n    amgx::thrust::copy(local_to_global_h.begin(), local_to_global_h.end(), this->local_to_global_map.begin());\n    return global_to_local;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributed_InitLocalMatrix(\n    IVector_h local_col_indices,\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const mat_value_type *values,\n    const void *diag)\n{\n    // init local matrix\n    this->A->set_initialized(0);\n    this->A->resize(0, 0, 0, 1, 1, 1);\n    this->A->addProps(CSR);\n\n    if (diag)\n    {\n        this->A->addProps(DIAG);\n    }\n\n    this->A->resize(num_rows, num_rows + this->local_to_global_map.size(), num_nonzeros, block_dimx, block_dimy, 1);\n    cudaCheckError();\n    // set local matrix\n    amgx::thrust::copy(row_offsets, row_offsets + num_rows + 1, this->A->row_offsets.begin());\n    this->A->col_indices = local_col_indices;\n\n    amgx::thrust::copy(values, values + num_nonzeros * block_dimx * block_dimy, this->A->values.begin());\n    cudaCheckError();\n\n    // setup diagonal\n    if (diag)\n    {\n        hipMemcpy(this->A->values.raw() + this->A->diagOffset()*this->A->get_block_size(), diag, sizeof(mat_value_type) * num_rows * block_dimx * block_dimy, hipMemcpyDefault);\n        cudaCheckError();\n    }\n    else\n    {\n        this->A->computeDiagonal();\n    }\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrixPartitionVec(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const int *partition)\n{\n    // fetch my rank\n    int my_id = this->getComms()->get_global_id();\n    // setup partition vector\n    IVector_h partitionVec(num_rows_global);\n\n    if (partition == NULL)\n    {\n        IVector_h rowCounts(num_ranks);\n        this->getComms()->all_gather(num_rows, rowCounts, 1);\n\n        int p = 0;\n        for (int i = 0; i < num_ranks; ++i)\n        {\n            for (int j = 0; j < rowCounts[i]; ++j)\n            {\n                partitionVec[p++] = i;\n            }\n        }\n    }\n    else\n    {\n        // use existing partition info\n        for (int i = 0; i < num_rows_global; i++)\n        {\n            partitionVec[i] = partition[i];\n        }\n    }\n\n    // compute partition offsets (based on number of elements per partition). Will be modified when calculating partition map.\n    t_colIndex *partition_offsets = (t_colIndex *)calloc(num_ranks + 1, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        int pvi = partitionVec[i];\n        partition_offsets[pvi + 1]++;\n    }\n\n    thrust_wrapper::inclusive_scan<AMGX_host>(partition_offsets, partition_offsets + num_ranks + 1, partition_offsets);\n\n    loadDistributed_SetOffsets(num_ranks, num_rows_global, partition_offsets);\n\n    // compute partition map (which tells you how the global elements are mapped into the partitions)\n    t_colIndex *partition_map = (t_colIndex *)calloc(num_rows_global, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        int     pvi = partitionVec[i];\n        t_colIndex poi = partition_offsets[pvi];\n        partition_map[poi] = i;\n        partition_offsets[pvi]++;\n    }\n    free(partition_offsets);\n\n    // compute the inverse partition map\n    t_colIndex *ipartition_map = (t_colIndex *)calloc(num_rows_global, sizeof(t_colIndex));\n\n    for (int i = 0; i < num_rows_global; i++)\n    {\n        ipartition_map[partition_map[i]] = i;\n    }\n    free(partition_map);\n\n    int h_cidx_allocated = 0;\n    const t_colIndex *h_col_indices_global = (const t_colIndex *)this->getHostPointerForData(col_indices, num_nonzeros * sizeof(t_colIndex), &h_cidx_allocated);\n    // gather all off-diag columns\n    I64Vector_h off_diag_cols;\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] != my_id)\n        {\n            off_diag_cols.push_back(ipartition_map[h_col_indices_global[i]]);\n        }\n    }\n\n    auto global_to_local = loadDistributed_LocalToGlobal<t_colIndex>(num_rows, off_diag_cols);\n\n    // set 1, then scan to compute local row indices\n    IVector_h my_indices(num_rows_global);\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] == my_id)     // find my local columns and set to 1\n        {\n            my_indices[ipartition_map[h_col_indices_global[i]]] = 1;\n        }\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(my_indices.begin(), my_indices.end(), my_indices.begin());\n    // remap colums to local\n    IVector_h local_col_indices(num_nonzeros);\n\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (partitionVec[h_col_indices_global[i]] != my_id)\n        {\n            // off-diag\n            local_col_indices[i] = global_to_local[ipartition_map[h_col_indices_global[i]]];\n        }\n        else\n        {\n            // diag\n            local_col_indices[i] = my_indices[ipartition_map[h_col_indices_global[i]]];\n        }\n    }\n    free(ipartition_map);\n\n    loadDistributed_InitLocalMatrix(local_col_indices, num_rows, num_nonzeros, block_dimx, block_dimy, row_offsets, values, diag);\n\n    cudaCheckError();\n\n    // don't free possibly allocated pinned buffer, since it could be used later. if it would not - it would be deallocated automatically\n    /*if (h_cidx_allocated)\n    {\n      free((void*)h_col_indices_global);\n    }*/\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrixPartitionOffsets(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const t_colIndex *partition_offsets)\n{\n    // fetch my rank\n    int my_id = this->getComms()->get_global_id();\n    // sanity check, cheap to perform, and helps prevent harder-to-debug errors later on\n    if (!std::is_sorted(partition_offsets, partition_offsets + num_ranks + 1)) {\n        FatalError(\"Partition offsets are not sorted.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n    loadDistributed_SetOffsets(num_ranks, num_rows_global, partition_offsets);\n\n    // Create predicate to determine if a column is in the local diagonal block\n    t_colIndex my_first_col = this->part_offsets_h[my_id];\n    t_colIndex one_past_my_last_col = this->part_offsets_h[my_id + 1];\n    auto in_local_diagonal_block = [my_first_col, one_past_my_last_col](const t_colIndex col_index) {\n        return col_index >= my_first_col && col_index < one_past_my_last_col;\n    };\n\n    int h_cidx_allocated = 0;\n    const t_colIndex *h_col_indices_global = (const t_colIndex *)this->getHostPointerForData(col_indices, num_nonzeros * sizeof(t_colIndex), &h_cidx_allocated);\n    // gather all off-diag columns\n    I64Vector_h off_diag_cols;\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        if (!in_local_diagonal_block(h_col_indices_global[i]))\n        {\n            off_diag_cols.push_back(h_col_indices_global[i]);\n        }\n    }\n    auto global_to_local = loadDistributed_LocalToGlobal<t_colIndex>(num_rows, off_diag_cols);\n    // set 1, then scan to compute local row indices\n    // \"coordinate-shift\" columns so they lie in much smaller range of my diagonal indices\n    int diagonal_size = this->part_offsets_h[my_id  + 1] - this->part_offsets_h[my_id];\n    IVector_h my_indices(diagonal_size);\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        t_colIndex col_index = h_col_indices_global[i];\n        if (in_local_diagonal_block(h_col_indices_global[i]))     // find my local columns and set to 1\n        {\n            // olumns that are on *my* diag partition cannot have an index from 0..num_rows_global\n            // instead, part_offsets_h[my_id] <= col_index < part_offsets[my_id+1]\n            col_index -= this->part_offsets_h[my_id];\n            my_indices[col_index] = 1;\n        }\n    }\n    thrust_wrapper::exclusive_scan<AMGX_host>(my_indices.begin(), my_indices.end(), my_indices.begin());\n\n    // remap colums to local\n    IVector_h local_col_indices(num_nonzeros);\n    for (int i = 0; i < num_nonzeros; i++)\n    {\n        t_colIndex col_index = h_col_indices_global[i];\n        if (!in_local_diagonal_block(col_index))\n        {\n            // off-diag\n            local_col_indices[i] = global_to_local[col_index];\n        }\n        else\n        {\n            // diag\n            col_index -= this->part_offsets_h[my_id];\n            local_col_indices[i] = my_indices[col_index];\n        }\n    }\n    loadDistributed_InitLocalMatrix(local_col_indices, num_rows, num_nonzeros, block_dimx, block_dimy, row_offsets, values, diag);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrix(\n    int num_rows,\n    int num_nonzeros,\n    const int block_dimx,\n    const int block_dimy,\n    const int *row_offsets,\n    const t_colIndex *col_indices,\n    const mat_value_type *values,\n    int num_ranks,\n    int num_rows_global,\n    const void *diag,\n    const MatrixDistribution &dist)\n{\n    using PI = MatrixDistribution::PartitionInformation;\n    switch (dist.getPartitionInformationStyle()) {\n        case PI::PartitionVec:\n            loadDistributedMatrixPartitionVec(num_rows, num_nonzeros, block_dimx, block_dimy, \n                row_offsets, col_indices, values, num_ranks, num_rows_global, diag, (const int*) dist.getPartitionData());\n            break;\n        case PI::PartitionOffsets:\n            loadDistributedMatrixPartitionOffsets(num_rows, num_nonzeros, block_dimx, block_dimy, \n                row_offsets, col_indices, values, num_ranks, num_rows_global, diag, (const t_colIndex*) dist.getPartitionData());\n            break;\n        default:\n            FatalError(\"Unsupported partitioning data format used with loadDistributedMatrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::renumberMatrixOneRing(int update_neighbours)\n{\n    FatalError(\"Distributed classical AMG not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumberMatrixOneRing(int update_neighbours)\n{\n    // Step 1: Using halo_ranges, flag neighbors and at the same time, flag halo_nodes (flag_halo_nodes_local)\n    int my_id = this->global_id();\n    int num_parts = this->get_num_partitions();\n    this->set_base_index(this->part_offsets_h[my_id]);\n    this->set_index_range(this->part_offsets_h[my_id + 1] - this->part_offsets_h[my_id]);\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n\n    // Create/update list of neighbors\n    if (update_neighbours)\n    {\n        typedef typename TConfig::template setVecPrec<AMGX_vecInt64>::Type i64vec_value_type;\n        typedef Vector<i64vec_value_type> I64Vector;\n        typedef typename Matrix<TConfig>::MVector MVector;\n        std::vector<IVector> halo_row_offsets(this->neighbors.size());\n        std::vector<I64Vector> halo_global_indices(this->neighbors.size());\n        std::vector<MVector> halo_values(this->neighbors.size());\n        prep->create_halo_rows_global_indices(*(this->A), halo_row_offsets, halo_global_indices, halo_values);\n        prep->update_neighbors_list(*(this->A), this->neighbors, this->halo_ranges_h, this->halo_ranges, this->part_offsets_h, this->part_offsets, halo_row_offsets, halo_global_indices);\n    }\n    else\n    {\n        prep->create_neighbors_v2(*(this->A));\n    }\n\n    this->getComms()->set_neighbors(this->neighbors.size());\n    // Create B2L_maps and L2H_maps\n    prep->create_boundary_lists_v3(*(this->A));\n    // halo_offsets\n    int neighbors = this->A->manager->num_neighbors();\n    int A_num_rows, offset;\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &A_num_rows);\n    this->halo_offsets.resize(neighbors + 1, 0);\n    this->halo_offsets[0] = A_num_rows;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        this->halo_offsets[i + 1] = this->halo_offsets[i] + this->B2L_maps[i].size();\n    }\n\n    this->getComms()->exchange_vectors(this->A->manager->B2L_maps, *(this->A), 0);\n    // Initialize B2L_rings\n    int num_neighbors = this->neighbors.size();\n    this->B2L_rings.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->B2L_rings[i].resize(2);\n        this->B2L_rings[i][0] = 0;\n        this->B2L_rings[i][1] = this->B2L_maps[i].size();\n    }\n\n    prep->initialize_B2L_maps_offsets(*(this->A), 1);\n    delete prep;\n    //Use the exchanged halo row matrices and the boundary/halo index lists to renumber and consolidate the matrix\n    // Step 5: renumber all owned rows and columns\n    this->reorder_matrix_owned();\n    // Step 6: renumber local_to_global_map\n    int num_owned_rows = this->A->manager->halo_offsets[0];\n    int size_one_ring;\n    this->A->getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    I64Vector_d global_col_indices(size_one_ring);\n    thrust_wrapper::sequence<AMGX_device>(global_col_indices.begin(), global_col_indices.begin() + num_owned_rows, this->base_index() );\n    cudaCheckError();\n    global_col_indices.dirtybit = 1;\n    this->exchange_halo(global_col_indices, global_col_indices.tag);\n    thrust_wrapper::copy<AMGX_device>(global_col_indices.begin() + num_owned_rows, global_col_indices.begin() + size_one_ring, this->local_to_global_map.begin(), this->get_int_stream(), true);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::renumber_P_R(Matrix_h &P, Matrix_h &R, Matrix_h &A_fine)\n{\n    FatalError(\"Distributed classical AMG not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::renumber_P_R(Matrix_d &P, Matrix_d &R, Matrix_d &A_fine)\n{\n    int cta_size = 256;\n    int num_owned_fine_pts = A_fine.manager->halo_offsets[0];\n    int num_owned_coarse_pts, offset;\n    // matrix Ac\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &num_owned_coarse_pts);\n    // Renumber the owned col indices of P (not the halo columns ,since P.manager was created assunming some other numbering)\n    int nnz_owned_fine_pts = P.row_offsets[num_owned_fine_pts];\n    int num_blocks_fine = std::min(4096, (nnz_owned_fine_pts + cta_size - 1) / cta_size);\n\n    if (num_blocks_fine > 0)\n    {\n        renumber_P_col_indices <<< num_blocks_fine, cta_size>>>(P.col_indices.raw(), this->renumbering.raw(), num_owned_coarse_pts, nnz_owned_fine_pts);\n        cudaCheckError();\n    }\n\n    // Renumber the B2L_maps of P\n    for (int i = 0; i < P.manager->neighbors.size(); i++)\n    {\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), P.manager->B2L_maps[i].begin()),\n                     amgx::thrust::make_permutation_iterator(this->renumbering.begin(), P.manager->B2L_maps[i].end()),\n                     P.manager->B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n    // Don't renumber the L2H_maps or the halo\n    // Renumber the local_to_global_map of matrix P (since neighbors renumbered their owned rows)\n    // Swap owned rows of R\n    IVector new_row_offsets(R.row_offsets.size());\n    int insert = 0;\n    // Only renumber the owned rows\n    int num_blocks_owned = std::min(4096, (num_owned_coarse_pts + cta_size - 1) / cta_size);\n\n    if (num_blocks_owned > 0)\n    {\n        calc_rowlen_reorder <<< num_blocks_owned, cta_size >>>(R.row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), num_owned_coarse_pts, insert);\n        cudaCheckError();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + num_owned_coarse_pts + 1, new_row_offsets.begin());\n    cudaCheckError();\n    // Copy the row_offsets for halo rows\n    amgx::thrust::copy(R.row_offsets.begin() + num_owned_coarse_pts, R.row_offsets.end(), new_row_offsets.begin() + num_owned_coarse_pts);\n    cudaCheckError();\n    // Reorder the rows of R (no need to reorder the column indices)\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    int halo_offset = new_row_offsets[num_owned_coarse_pts];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values(new_nnz * R.get_block_size(), types::util< ValueTypeA >::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n    int num_blocks_total = std::min(4096, (R.get_num_rows() + cta_size - 1) / cta_size);\n\n    if (num_blocks_total > 0)\n    {\n        reorder_R_matrix <32> <<< num_blocks_total, 512>>>(R.row_offsets.raw(), R.col_indices.raw(), R.values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), R.get_block_size(), R.get_num_rows(), num_owned_coarse_pts);\n        cudaCheckError();\n    }\n\n    R.col_indices.swap(new_col_indices);\n    R.row_offsets.swap(new_row_offsets);\n    R.values.swap(new_values);\n\n    // Renumber the local_to_global_map (since neighbors have changed their owned numbering)\n    if (P.manager->neighbors.size() != 0)\n    {\n        int size_one_ring = P.manager->halo_offsets[P.manager->neighbors.size()];\n        I64Vector_d global_col_indices(size_one_ring);\n        thrust_wrapper::sequence<AMGX_device>(global_col_indices.begin(), global_col_indices.begin() + num_owned_coarse_pts, this->base_index());\n        cudaCheckError();\n        global_col_indices.dirtybit = 1;\n        P.manager->exchange_halo(global_col_indices, global_col_indices.tag);\n        thrust_wrapper::copy<AMGX_device>(global_col_indices.begin() + num_owned_coarse_pts, global_col_indices.begin() + size_one_ring, P.manager->local_to_global_map.begin(), this->get_int_stream(), true);\n        cudaCheckError();\n    }\n\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->initialize_B2L_maps_offsets(P, 1);\n    delete prep;\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingB2Lmaps()\n{\n    // Input:\n    // a matrix with N rows, whose column indices are local indices from 0 to N+M-1,\n    // where M is a number of 1-ring halo vertices\n    // The matrix also contains array \"local_to_global_map\" of size M, which stores the global index of each halo index\n    // Ex: assuming a column has index N+K, where 0 <= K < M, then it's global id is local_to_global_map[K]\n    // The matrix also contains part_offsets_h and part_offsets array, which stores where each partition begins\n    // Output:\n    // This function creates all the necessary data to to 1-ring exchanges\n    // i.e. list of 1-ring neighbors, B2L_maps for 1-ring, halo_offsets for 1-ring,\n    // Also, the function reorders the halo indices, such that 1-ring indices are in the order\n    // of neighbors, and therefore, exchange_halo doesn't have to be changed (i.e. L2H = identity)\n    // What is does:\n    // Based on the global indices of its halo vertices, count the number of neighbors\n    // For each neighbor, receive the halo indices that will be needed by neighbor\n    // From those, create B2L_maps[0], which contains for all neighbors\n    // This function assumes that:\n    // part_offset is defined\n\n    // B2L_maps\n    int my_id = this->global_id();\n    int num_parts = this->get_num_partitions();\n    this->set_base_index(this->part_offsets_h[my_id]);\n    this->set_index_range(this->part_offsets_h[my_id + 1] - this->part_offsets_h[my_id]);\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    // This function creates the array neighbors, which contains a list of partitions to which data\n    // needs to be sent and/or received\n    prep->create_neighbors_v2(*(this->A));\n    // Here change the manager if some partitions have no neighbors\n    this->getComms()->set_neighbors(this->neighbors.size());\n    prep->create_B2L_one_ring(*(this->A));\n    delete prep;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingHaloRows()\n{\n    // Input:\n    // A matrix with 1-ring B2L_maps, 1-ring halo_offsets\n    // Outputs:\n    // A matrix with: 1-ring rows,\n    //                2-ring B2L_maps,\n    //                2-ring halo_offsets\n    //                2-ring neighbors\n    // Implement here:\n    // Look at function create_B2L_from_maps, which calls create_rings, create_halo_btl, create_halo_rows and comms->exchange_matrix_halo\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->create_one_ring_halo_rows(*(this->A));\n    // I believe this can be removed since we don't use masked SpMV anymore\n    prep->createRowsLists(*(this->A), false);\n    delete prep;\n    // this is not necessary anymore becasue we don't use latency hiding\n    // however in future we might want to get back to this in case we want to use latency hiding\n    //this->reorder_matrix();\n}\n\ntemplate <class TConfig>\ninline DistributedManagerBase<TConfig>::DistributedManagerBase(\n    Matrix<TConfig> &a,\n    INDEX_TYPE allocated_halo_depth,\n    INDEX_TYPE num_import_rings,\n    int num_neighbors,\n    const VecInt_t *neighbors_) : m_fine_level_comms(NULL), A(&a), m_pinned_buffer_size(0), m_pinned_buffer(NULL), _num_interior_nodes(0), _num_boundary_nodes(0), _comms(NULL), has_B2L(false), neighbors(_neighbors), halo_rows_ref_count(0), halo_rows(NULL), halo_btl_ref_count(0), halo_btl(NULL), halo_ranges(_halo_ranges), halo_ranges_h(_halo_ranges_h), part_offsets(_part_offsets), part_offsets_h(_part_offsets_h),\n    B2L_maps(_B2L_maps),  L2H_maps(_L2H_maps), B2L_rings(_B2L_rings), m_is_root_partition(false), m_is_glued(false), m_is_fine_level_glued(false), m_is_fine_level_consolidated(false), m_is_fine_level_root_partition(false), m_use_cuda_ipc_consolidation(false), m_fixed_view_size(false)\n{\n    hipStreamCreateWithFlags(&m_int_stream, hipStreamNonBlocking);\n    cudaCheckError();\n    hipStreamCreateWithFlags(&m_bdy_stream, hipStreamNonBlocking);\n    cudaCheckError();\n\n    if (num_import_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (allocated_halo_depth != 1)\n    {\n        FatalError(\"allocated_halo_depth > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    this->set_num_halo_rings(num_import_rings);\n    neighbors.resize(num_neighbors);\n    hipMemcpy(neighbors.raw(), neighbors_, num_neighbors * sizeof(VecInt_t), hipMemcpyDefault);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMaps(const VecInt_t *b2l_maps, const VecInt_t *b2l_ptrs, const VecInt_t *l2h_maps, const VecInt_t *l2h_ptrs)\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = b2l_ptrs[i + 1] - b2l_ptrs[i];\n        this->cached_B2L_maps[i].resize(size);\n        int count = 0;\n\n        for (int j = b2l_ptrs[i]; j < b2l_ptrs[i + 1]; j++)\n        {\n            this->cached_B2L_maps[i][count] = b2l_maps[j];\n            count++;\n        }\n\n        size = l2h_ptrs[i + 1] - l2h_ptrs[i];\n        this->cached_L2H_maps[i].resize(size);\n        count = 0;\n\n        for (int j = l2h_ptrs[i]; j < l2h_ptrs[i + 1]; j++)\n        {\n            this->cached_L2H_maps[i][count] = l2h_maps[j];\n            count++;\n        }\n    }\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMapsOneRing()\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->cached_B2L_maps[i] = this->B2L_maps[i];\n        this->cached_L2H_maps[i] = this->L2H_maps[i];\n    }\n}\n\ntemplate <class TConfig>\ninline void DistributedManagerBase<TConfig>::cacheMapsOneRing(const VecInt_t **b2l_maps, const VecInt_t *b2l_sizes, const VecInt_t **l2h_maps, const VecInt_t *l2h_sizes)\n{\n    int num_neighbors = this->neighbors.size();\n    this->cached_B2L_maps.resize(num_neighbors);\n    this->cached_L2H_maps.resize(num_neighbors);\n    // buffering in the case of GPU data. This shouldn't much affect performance\n    std::vector<VecInt_t *> b2l_buffer, l2h_buffer;\n    std::vector<VecInt_t> b2l_sizes_buffer, l2h_sizes_buffer;\n    b2l_buffer.resize(num_neighbors);\n    l2h_buffer.resize(num_neighbors);\n    b2l_sizes_buffer.resize(num_neighbors);\n    l2h_sizes_buffer.resize(num_neighbors);\n    hipError_t cuda_rc = hipMemcpy(&(b2l_sizes_buffer[0]), b2l_sizes, sizeof(VecInt_t) * num_neighbors, hipMemcpyDefault);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy b2l_sizes failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy(&(l2h_sizes_buffer[0]), l2h_sizes, sizeof(VecInt_t) * num_neighbors, hipMemcpyDefault);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy l2h_sizes failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy(&(b2l_buffer[0]), b2l_maps, sizeof(VecInt_t *) * num_neighbors, hipMemcpyDefault);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy b2l_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy(&(l2h_buffer[0]), l2h_maps, sizeof(VecInt_t *) * num_neighbors, hipMemcpyDefault);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy l2h_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    // caching all of the maps\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = b2l_sizes_buffer[i];\n        this->cached_B2L_maps[i].resize(size);\n        cuda_rc = hipMemcpy(&(this->cached_B2L_maps[i][0]), b2l_buffer[i], sizeof(VecInt_t) * size, hipMemcpyDefault);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipMemcpy cached_B2L_maps failed in distributed_manager\", AMGX_ERR_CUDA_FAILURE);\n        }\n        cudaCheckError();\n        size = l2h_sizes_buffer[i];\n        this->cached_L2H_maps[i].resize(size);\n        hipMemcpy(&(this->cached_L2H_maps[i][0]), l2h_buffer[i], sizeof(VecInt_t) * size, hipMemcpyDefault);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::setAConsolidationFlags( Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    AMG_Config *rsrc_cfg = this->A->getResources()->getResourcesConfig();\n    std::string scope;\n    int consolidate_flag, cuda_ipc_flag;\n    rsrc_cfg->getParameter<int>(\"fine_level_consolidation\", consolidate_flag, \"default\", scope);\n    rsrc_cfg->getParameter<int>(\"use_cuda_ipc_consolidation\", cuda_ipc_flag, \"default\", scope);\n    this->m_is_fine_level_consolidated = (consolidate_flag != 0);\n    this->m_use_cuda_ipc_consolidation = (cuda_ipc_flag != 0);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::uploadMatrix(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->setAConsolidationFlags(in_A);\n\n    if (this->m_is_fine_level_consolidated)\n    {\n        this->A->manager->consolidateAndUploadAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n    }\n    else\n    {\n        this->A->manager->initializeUploadReorderAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::checkPinnedBuffer(size_t size)\n{\n    if ((m_pinned_buffer_size < size) && (m_pinned_buffer != NULL))\n    {\n        amgx::memory::hipHostFree(m_pinned_buffer);\n        m_pinned_buffer = NULL;\n        m_pinned_buffer_size = 0;\n    }\n\n    if (m_pinned_buffer == NULL)\n    {\n        m_pinned_buffer_size = (size_t)(size * 1.1);\n        amgx::memory::hipHostMalloc(&m_pinned_buffer, m_pinned_buffer_size);\n    }\n}\n\n\ntemplate <class TConfig>\nDistributedManagerBase<TConfig>::~DistributedManagerBase()\n{\n    if (m_pinned_buffer != NULL)\n    {\n        amgx::memory::hipHostFree(m_pinned_buffer);\n    }\n\n    destroyComms();\n    // from childrens:\n    hipStreamDestroy(this->m_int_stream);\n    cudaCheckError();\n    hipStreamDestroy(this->m_bdy_stream);\n    cudaCheckError();\n\n    if (!this->halo_rows_ref_count && this->halo_rows != NULL)\n    {\n        delete this->halo_rows;\n        this->halo_rows = NULL;\n    }\n\n    if (!this->halo_btl_ref_count && this->halo_btl != NULL)\n    {\n        delete this->halo_btl;\n        this->halo_btl = NULL;\n    }\n}\n\n// if pointer is host pointer - returns data. If it is device pointer - copies it to the m_pinned_buffer and returns pointer to m_pinned_buffer\ntemplate <class TConfig>\nvoid *DistributedManagerBase<TConfig>::getHostPointerForData(void *ptr, size_t size, int *allocated)\n{\n    hipError_t rc;\n    hipPointerAttribute_t att;\n    void *ptr_h;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    /*\n    // original implementation\n    cudaPointerGetAttributes(&att, ptr);\n    if (att.hostPointer == NULL)\n    {\n      checkPinnedBuffer(size);\n      cudaMemcpy(m_pinned_buffer, ptr, size, cudaMemcpyDefault);\n      cudaCheckError();\n      return m_pinned_buffer;\n    }\n    else {\n      return ptr;\n    }\n    */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = hipPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == hipSuccess && (att.type == hipMemoryTypeDevice || att.type == hipMemoryTypeManaged))\n    {\n        checkPinnedBuffer(size);\n        rc = hipMemcpy(m_pinned_buffer, ptr, size, hipMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        ptr_h = m_pinned_buffer;\n        *allocated = 1;\n        hipGetLastError(); //to reset last error\n        return ptr_h;\n    }\n    else\n    {\n        hipGetLastError(); //to reset last error\n        return ptr;\n    }\n#else\n    if (rc == hipSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        if (att.hostPointer == NULL)\n        {\n            //you are in case 2\n            checkPinnedBuffer(size);\n            rc = hipMemcpy(m_pinned_buffer, ptr, size, hipMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            ptr_h = m_pinned_buffer;\n            *allocated = 1;\n        }\n        else\n        {\n            //you are in case 4\n            ptr_h = ptr;\n        }\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        ptr_h = ptr;\n    }\n\n    hipGetLastError(); //to reset last error\n\n    /* check for null pointers */\n    if (ptr_h == NULL)\n    {\n        FatalError(\"Result of (host) allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    return ptr_h;\n#endif\n}\n\n// if pointer is host pointer - returns data. If it is device pointer - copies it to the m_pinned_buffer and returns pointer to m_pinned_buffer\ntemplate <class TConfig>\nconst void *DistributedManagerBase<TConfig>::getHostPointerForData(const void *ptr, size_t size, int *allocated)\n{\n    hipError_t rc;\n    hipPointerAttribute_t att;\n    void *ptr_h;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = hipPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == hipSuccess && (att.type == hipMemoryTypeDevice || att.type == hipMemoryTypeManaged))\n    {\n        checkPinnedBuffer(size);\n        rc = hipMemcpy(m_pinned_buffer, ptr, size, hipMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        ptr_h = m_pinned_buffer;\n        *allocated = 1;\n        hipGetLastError(); //to reset last error\n        return ptr_h;\n    }\n    else\n    {\n        hipGetLastError(); //to reset last error\n        return ptr;\n    }\n#else\n    if (rc == hipSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        if (att.hostPointer == NULL)\n        {\n            //you are in case 2\n            checkPinnedBuffer(size);\n            rc = hipMemcpy(m_pinned_buffer, ptr, size, hipMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not copy into the temporary (host) storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            ptr_h = m_pinned_buffer;\n            *allocated = 1;\n            hipGetLastError(); //to reset last error\n            return ptr_h;\n        }\n        else\n        {\n            //you are in case 4\n            hipGetLastError(); //to reset last error\n            return ptr;\n        }\n    }\n    else\n    {\n        hipGetLastError(); //to reset last error\n        //you are in case 1 or 3 from the above comment\n        return ptr;\n    }\n#endif\n}\n\n\ntemplate <class TConfig>\nvoid *DistributedManagerBase<TConfig>::getDevicePointerForData(void *ptr, size_t size, int *allocated)\n{\n    hipError_t rc;\n    hipPointerAttribute_t att;\n    void *ptr_d;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = hipPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == hipSuccess && att.type != hipMemoryTypeUnregistered)\n    {\n        // Received device registered data (device, managed, malloc+cudaHostRegister, or cudaMallocHost)\n        ptr_d = (void*)att.devicePointer;\n\n        if (ptr_d == NULL)\n        {\n            FatalError(\"Data passed is registered with device but devicePointer null.\", AMGX_ERR_INTERNAL);\n        }\n    }\n    else\n    {\n        // Received unregistered or host allocated data (malloc)\n        rc = amgx::memory::hipMallocAsync(&ptr_d, size);\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        rc = hipMemcpy(ptr_d, ptr, size, hipMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        *allocated = 1;\n    }\n#else\n\n    if (rc == hipSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        ptr_d = (void *)att.devicePointer;\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        rc = hipHostGetDevicePointer(&ptr_d, ptr, 0);\n\n        if (rc != hipSuccess)\n        {\n            //you are in case 1\n            rc = amgx::memory::hipMallocAsync(&ptr_d, size);\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            rc = hipMemcpy(ptr_d, ptr, size, hipMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            *allocated = 1;\n        }\n    }\n\n    /* check for null pointers */\n    if (ptr_d == NULL)\n    {\n        FatalError(\"Result of allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n#endif\n\n    hipGetLastError(); //to reset last error\n    return ptr_d;\n}\n\ntemplate <class TConfig>\nconst void *DistributedManagerBase<TConfig>::getDevicePointerForData(const void *ptr, size_t size, int *allocated)\n{\n    hipError_t rc;\n    hipPointerAttribute_t att;\n    void *ptr_d;\n    cudaCheckError();\n    /* WARNING: We may accept the following types of allocation for ptr:\n       1. malloc                    [host memory]\n       2. cudaMalloc                [device memory]\n       3. malloc + cudaHostRegister [AMGX_pin_memory/AMGX_unpin_memory host memory]\n       4. cudaHostAlloc             [pinned host memory form the beginning]\n\n       The correct way to conver these cases is the following:\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2, 3 or 4.\n       }\n       else{\n         //you are in case 1.\n       }\n       The following pattern of checks should be implemented\n       cudaPointerAttributes att;\n       cudaError_t st = cudaPointerGetAttributes(&att, ptr);\n       if (st == cudaSuccess) {\n         //you are in case 2 or 4.\n       }\n       else{\n         st = cudaHostGetDevicePointer(ptr_on_device, ptr, 0);\n         if (st == cudaSuccess){\n           //you are in case 3.\n         }\n         else{\n           //you are in case 1.\n         }\n       }\n       The above pattern will be used whenever we need to process input data.\n\n       Obs.: parameter size is in bytes and\n             parameter allocated indicates whether memory was allocated\n             and needs to be release later on. */\n    *allocated = 0;\n    // get pointer to values on the device\n    rc = hipPointerGetAttributes(&att, ptr);\n\n#if CUDART_VERSION >= 11000\n    if(rc == hipSuccess && att.type != hipMemoryTypeUnregistered)\n    {\n        // Received device registered data (device, managed, malloc+cudaHostRegister, or cudaMallocHost)\n        ptr_d = (void*)att.devicePointer;\n\n        if (ptr_d == NULL)\n        {\n            FatalError(\"Data passed is registered with device but devicePointer null.\", AMGX_ERR_INTERNAL);\n        }\n    }\n    else\n    {\n        // Received unregistered or host allocated data (malloc)\n        rc = amgx::memory::hipMallocAsync(&ptr_d, size);\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        rc = hipMemcpy(ptr_d, ptr, size, hipMemcpyDefault);\n        cudaCheckError();\n\n        if (rc != hipSuccess)\n        {\n            FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n        }\n\n        *allocated = 1;\n    }\n#else\n    if (rc == hipSuccess)\n    {\n        //you are in case 2 or 4 from the above comment.\n        hipGetLastError(); //to reset last error\n        return (const void *)att.devicePointer;\n    }\n    else\n    {\n        //you are in case 1 or 3 from the above comment\n        rc = hipHostGetDevicePointer(&ptr_d, (void *)ptr, 0);\n\n        if (rc != hipSuccess)\n        {\n            //you are in case 1\n            rc = amgx::memory::hipMallocAsync(&ptr_d, size);\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not allocate required temporary storage. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            rc = hipMemcpy(ptr_d, ptr, size, hipMemcpyDefault);\n            cudaCheckError();\n\n            if (rc != hipSuccess)\n            {\n                FatalError(\"Could not copy into the temporary storage. Try pinning the memory to avoid the hipMemcpy.\", AMGX_ERR_BAD_PARAMETERS);\n            }\n\n            *allocated = 1;\n            hipGetLastError(); //to reset last error\n            return (const void *)ptr_d;\n        }\n    }\n\n    /* check for null pointers */\n    if (ptr_d == NULL)\n    {\n        FatalError(\"Result of allocation of required temporary storage is NULL. Try pinning the memory to reduce storage requirements.\", AMGX_ERR_BAD_PARAMETERS);\n    }\n#endif\n\n    // shouldn't get there\n    hipGetLastError(); //to reset last error\n    return NULL;\n}\n\ntemplate <class TConfig>\nvoid initializeMatrixCopyAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> *A)\n{\n    typedef typename TConfig::MatPrec  mat_value_type;\n    A->resize( n, n, nnz, block_dimx, block_dimy );\n    //Upload the entire matrix\n    hipMemcpy( A->row_offsets.raw(), row_ptrs, (n + 1) * sizeof(int), hipMemcpyDefault );\n    cudaCheckError();\n    hipMemcpy( A->col_indices.raw(), col_indices, (nnz) * sizeof(int), hipMemcpyDefault );\n    cudaCheckError();\n    hipMemcpy( A->values.raw(), (mat_value_type *)data, (nnz * block_dimx * block_dimy) * sizeof(mat_value_type), hipMemcpyDefault );\n    cudaCheckError();\n\n    if (diag)\n    {\n        hipMemcpy( A->values.raw() + A->diagOffset()*A->get_block_size(), (mat_value_type *)diag, (n * block_dimx * block_dimy) * sizeof(mat_value_type), hipMemcpyDefault );\n        cudaCheckError();\n    }\n    else\n    {\n        A->computeDiagonal();\n    }\n\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::updateMapsReorder()\n{\n    int my_id = this->getComms()->get_global_id();\n    DistributedComms<TConfig> *comms_tmp = this->getComms();\n    DistributedComms<TConfig> **comms_ = &comms_tmp;\n    // Copy B2L_maps in their final place\n    int num_neighbors = this->neighbors.size();\n    B2L_maps.resize(num_neighbors);\n    L2H_maps.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B2L_maps[i] = this->cached_B2L_maps[i];\n        L2H_maps[i] = this->cached_L2H_maps[i];\n    }\n\n    //Create a DistributedArranger object to map further halo rings and to construct halo row matrices and exchange them (if halo_coloring != LAST)\n    DistributedArranger<TConfig> *prep = new DistributedArranger<TConfig>;\n    prep->create_B2L_from_maps( (*(this->A)), my_id, this->num_halo_rings(), neighbors,\n                                B2L_maps, L2H_maps, B2L_rings, comms_, &halo_rows, &halo_btl);\n    DistributedManagerBaseInit(my_id, 0, this->A->get_num_rows(), *(this->A), comms_, NULL, NULL);\n    //Use the exchanged halo row matrices and the boundary/halo index lists to renumber and consolidate the matrix\n    this->reorder_matrix();\n    prep->initialize_B2L_maps_offsets(*(this->A), this->num_halo_rings());\n    delete prep;\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::initializeUploadReorderAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    initializeMatrixCopyAll<TConfig>(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, this->A);\n    this->updateMapsReorder();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::destroyComms()\n{\n    if ( (this->_comms) != NULL )\n    {\n        if (this->_comms->decr_ref_count())\n        {\n            delete (this->_comms);\n            this->_comms = NULL;\n        }\n    }\n\n    if ( (this->m_fine_level_comms) != NULL)\n    {\n        if (this->m_fine_level_comms->decr_ref_count())\n        {\n            delete (this->m_fine_level_comms);\n            this->m_fine_level_comms = NULL;\n        }\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::initComms(Resources *rsrc)\n{\n    this->createComms(rsrc);\n    int my_id = this->getComms()->get_global_id();\n    int num_parts = this->getComms()->get_num_partitions();\n    this->set_global_id(my_id);\n    this->set_num_partitions(num_parts);\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createComms(Resources *rsrc)\n{\n    // create communicator\n#ifdef AMGX_WITH_MPI\n    destroyComms();\n    if (rsrc == NULL)\n        FatalError(\"Resources should not be NULL\", AMGX_ERR_INTERNAL);\n\n    MPI_Comm *mpi_comm = rsrc->getMpiComm();\n    AMG_Config *cfg = rsrc->getResourcesConfig();\n    std::string comm_value, comm_scope;\n    cfg->getParameter<std::string>(\"communicator\", comm_value, \"default\", comm_scope);\n    int rank = -1;\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n\n    if (comm_value == \"MPI_DIRECT\")\n    {\n        _comms = new CommsMPIDirect<TConfig>(*cfg, comm_scope, mpi_comm);\n        std::string comm_log(\"Using CUDA-Aware MPI (GPU Direct) communicator...\\n\");\n        amgx_distributed_output(comm_log.c_str(), comm_log.length());\n    }\n    else if (comm_value == \"MPI\")\n    {\n        _comms =  new CommsMPIHostBufferStream<TConfig>(*cfg, comm_scope, mpi_comm);\n        std::string comm_log(\"Using Normal MPI (Hostbuffer) communicator...\\n\");\n        amgx_distributed_output(comm_log.c_str(), comm_log.length());\n    }\n    else \n    { \n        FatalError(\"Bad communicator value\", AMGX_ERR_BAD_PARAMETERS); \n    }\n\n#endif\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::malloc_export_maps(VecInt_t ***b2l_maps_e, VecInt_t **b2l_maps_sizes_e, VecInt_t ***l2h_maps_e, VecInt_t **l2h_maps_sizes_e)\n{\n    *b2l_maps_e = (VecInt_t **) malloc(sizeof(VecInt_t *)*this->num_neighbors());\n    *l2h_maps_e = (VecInt_t **) malloc(sizeof(VecInt_t *)*this->num_neighbors());\n    *b2l_maps_sizes_e = (VecInt_t *) malloc(sizeof(VecInt_t) * (this->num_neighbors()));\n    *l2h_maps_sizes_e = (VecInt_t *) malloc(sizeof(VecInt_t) * (this->num_neighbors()));\n\n    for (int i = 0; i < this->num_neighbors(); i++)\n    {\n        (*b2l_maps_sizes_e)[i] = B2L_maps[i].size();\n        (*l2h_maps_sizes_e)[i] = L2H_maps[i].size();\n        (*b2l_maps_e)[i] =  (VecInt_t *) malloc(sizeof(VecInt_t) * ( (*b2l_maps_sizes_e)[i]) );\n\n        if (L2H_maps[i].size() != 0)\n        {\n            (*l2h_maps_e)[i] =  (VecInt_t *) malloc(sizeof(VecInt_t) * ( (*l2h_maps_sizes_e)[i]) );\n            amgx::thrust::copy(L2H_maps[i].begin(), L2H_maps[i].end(), (*l2h_maps_e)[i]);\n        }\n\n        amgx::thrust::copy(B2L_maps[i].begin(), B2L_maps[i].end(), (*b2l_maps_e)[i]);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createRenumbering(IVector &renumbering)\n{\n    int num_neighbors = this->neighbors.size();\n    // still renumber if the number of neighbors = 0, to support non-symmetric matrices\n    // if (num_neighbors == 0) return;\n    /*\n      EXAMPLE\n      Example matrix, partition 1 arrives with state:\n      A.row_offsets = [0 4 11 15 20]\n      A.col_indices = [4 0 1 2\n                       4 5 0 1 2 3 7\n                       0 1 2 3\n                       1 2 3 6 7]\n      num_neighbors=2; neighbors = [0 2]\n      B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n      L2H_maps (and halo_lists) [[4 5][6 7]]\n    */\n    int size = 0;\n\n    if (this->L2H_maps.size())\n    {\n        size = thrust_wrapper::reduce<AMGX_device>(this->A->col_indices.begin(), this->A->col_indices.end(), int(0), amgx::thrust::maximum<int>()) + 1; //Sufficient to do reduction on lth maps\n        cudaCheckError();\n    }\n    else\n    {\n        size = this->A->get_num_rows();\n    }\n\n    int rings = (this->B2L_rings.size() > 0) ? this->B2L_rings[0].size() - 1 : 0;\n    //initial size to size+1 so we have the total size after a scan\n    renumbering.resize(size + 1);\n    int global_size = size;\n    //\n    // Step 1 - in the main matrix, separate interior and boundary nodes (1/0 in flagArray), renumber interior ones with an exclusive scan\n    //\n    IVector flagArray(size + 1);\n    thrust_wrapper::fill<AMGX_device>(flagArray.begin(), flagArray.begin() + size + 1, 1);\n    cudaCheckError();\n\n    //sets 1 for interior nodes, 0 for boundary node\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = this->B2L_rings[i][1];\n        int num_blocks = std::min(4096, (size + 127) / 128);\n\n        if (size > 0)\n        {\n            remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), this->B2L_maps[i].raw(), size);\n            cudaCheckError();\n        }\n\n        //If there are any L2H maps\n        if (this->L2H_maps.size() && this->L2H_maps[i].size())\n        {\n            int size = this->L2H_maps[i].size();\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            remove_boundary_kernel <<< num_blocks, 128>>>(flagArray.raw(), this->L2H_maps[i].raw(), size);\n        }\n\n        cudaCheckError();\n    }\n\n    //gets the renumbering of interior nodes\n    thrust_wrapper::exclusive_scan<AMGX_device>(flagArray.begin(), flagArray.begin() + size + 1, renumbering.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     After removing 1-ring boundary nodes and halo nodes from flagArray: [0 0 1 0 0 0 0 0]\n     After exclusive scan, which gives renumbering for interior nodes (only node #2)\n     renumbering: [0 0 0 1 1 1 1 1]\n     */\n    //\n    // Step 2 - Renumber nodes that are in the boundary, stepping through each B2L map, and renumbering ones that have not been renumbered yet\n    //\n    //what is the biggest B2L size\n    INDEX_TYPE max_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        max_size = max_size > this->B2L_rings[i][1] ? max_size : this->B2L_rings[i][1];\n\n        if (this->L2H_maps.size())\n        {\n            max_size = max_size > this->L2H_maps[i].size() ? max_size : this->L2H_maps[i].size();\n        }\n    }\n\n    //allocate work vectors (should be pretty small) that are used to renumber boundary nodes\n    IVector boundary_renum_flags(max_size);\n    IVector boundary_renum(max_size);\n    //the number of renumbered nodes so far\n    int max_element = renumbering[size];\n    this->_num_interior_nodes = max_element;\n    this->_num_boundary_nodes = this->A->get_num_rows() - max_element;\n    renumbering.resize(size);\n\n    /*\n     EXAMPLE\n     size = 8\n     max_size = 2, max_element = 1, num_interior_nodes=1, num_boundary_nodes = 4-1 = 3\n     */\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //find nodes that are part of the current boundary and they haven't been renumbered yet\n        thrust_wrapper::fill<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, 0);\n        int size = this->B2L_rings[i][1];\n        int num_blocks = std::min(4096, (size + 191) / 192);\n\n        if (size > 0)\n            get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n                    this->B2L_maps[i].raw(),\n                    boundary_renum_flags.raw(), size, global_size /*,rank*/);\n                    cudaCheckError();\n\n        //calculate the local renumbering (within this boundary region) of these nodes\n        thrust_wrapper::exclusive_scan<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, boundary_renum.begin());\n\n        //apply renumbering to the big numbering table\n        if (size > 0)\n            set_unassigned_kernel <<< num_blocks, 192>>>(boundary_renum_flags.raw(),\n                    boundary_renum.raw(),\n                    this->B2L_maps[i].raw(),\n                    renumbering.raw(),\n                    size, max_element, global_size /*,rank*/);\n                    cudaCheckError();\n\n        //update the number of renumbered nodes\n        max_element += boundary_renum[max_size - 1] + boundary_renum_flags[max_size - 1];\n        /*\n         EXAMPLE\n         for neighbor 0 (ID 0)\n         boundary_renum_flags = [0 0], size = 2, flagArray [0 0 1 0 0 0 0 0]\n         get_unassigned_kernel's output: boundary_renum_flags = [1 1] flagArray [1 1 1 0 0 0 0 0]\n         after exclusive scan: boundary_renum [0 1]\n         set_unassigned_kernel updates these arrays and renumbers B2L map:\n         renumbering = [1 2 0 1 1 1 1 1] B2L_maps[0] = [1 2| 2 3] (note that after element 3 in renumbering and after element 2 we have invalid/not yet updated values)\n         max_element = 3\n\n         for neighbor 1 (ID 2)\n         get_unassigned_kernels's output: boundary_renum_flags [0 1] flagArray [1 1 1 1 0 0 0 0]\n         after exclusive scan boundary_renum [0 0]\n         set_unassigned_kernel renumbering [1 2 0 3 1 1 1 1] B2L_maps[1] = [2 3| 0 2]\n         max_element = 4\n         */\n    }\n\n    cudaCheckError();\n\n    //Get renumbering for halo indices\n    if (this->L2H_maps.size())\n    {\n        //TODO: simplify this, we don't need to check whether it has already been renumbered, there is no overlap between halos\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            //find nodes that are part of the current boundary and they haven't been renumbered yet\n            thrust_wrapper::fill<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, 0);\n            int size = this->L2H_maps[i].size();\n            int num_blocks = std::min(4096, (size + 191) / 192);\n\n            if (size > 0)\n                get_unassigned_kernel <<< num_blocks, 192>>>(flagArray.raw(),\n                        this->L2H_maps[i].raw(),\n                        boundary_renum_flags.raw(), size, global_size /*,rank*/);\n                        cudaCheckError();\n\n            //calculate the local renumbering (within this boundary region) of these nodes\n            thrust_wrapper::exclusive_scan<AMGX_device>(boundary_renum_flags.begin(), boundary_renum_flags.begin() + max_size, boundary_renum.begin());\n\n            //apply renumbering to the big numbering table\n            if (size > 0)\n                set_unassigned_kernel <<< num_blocks, 192>>>(boundary_renum_flags.raw(),\n                        boundary_renum.raw(),\n                        this->L2H_maps[i].raw(),\n                        renumbering.raw(),\n                        size, max_element, global_size /*,rank*/);\n                        cudaCheckError();\n\n            //update the number of renumbered nodes\n            max_element += boundary_renum[max_size - 1] + boundary_renum_flags[max_size - 1];\n            /*\n             EXAMPLE\n             for neighbor 0 (ID 0)\n             boundary_renum_flags = [0 0], size = 2, flagArray [1 1 1 1 0 0 0 0]\n             get_unassigned_kernel's output: boundary_renum_flags = [1 1] flagArray [1 1 1 1 1 1 0 0]\n             after exclusive scan: boundary_renum [0 1]\n             set_unassigned_kernel updates these arrays and renumbers B2L map:\n             renumbering = [1 2 0 3 4 5 1 1] L2H_maps[0] = [4 5]\n             max_element = 6\n\n             for neighbor 1 (ID 2)\n             get_unassigned_kernels's output: boundary_renum_flags [1 1] flagArray [1 1 1 1 1 1 1 1]\n             after exclusive scan boundary_renum [0 1]\n             set_unassigned_kernel renumbering = [1 2 0 3 4 5 6 7] L2H_maps[1] = [6 7]\n             max_element = 8\n             */\n        }\n\n        cudaCheckError();\n    }\n\n    //apply renumbering to further halo rings too\n    if (rings > 1)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = this->B2L_rings[i][this->B2L_rings[i].size() - 1] - this->B2L_rings[i][1];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            renumber_b2l_maps <<< num_blocks, 128>>>(this->B2L_maps[i].raw() + this->B2L_rings[i][1], renumbering.raw(), size, global_size /*, rank*/);\n        }\n\n        cudaCheckError();\n    }\n\n    /*\n     EXAMPLE\n     renumbers further boundary rings as listed in B2L_maps, since they have not been replaced yet with their renumbered values\n     B2L_maps [[1 2| 0 3][2 3| 1 0]]\n     */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix_owned()\n{\n    int num_neighbors = this->neighbors.size();\n    int size = this->A->get_num_rows();\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    int rings = (this->B2L_rings.size() > 0) ? this->B2L_rings[0].size() - 1 : 0;\n    this->set_num_halo_rings(rings);\n    int diag = this->A->hasProps(DIAG);\n\n    if (diag)\n    {\n        FatalError(\"External diag not supported in classical path\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n//\n// Step 1 & 2 - create renumbering\n//\n    this->createRenumbering(this->renumbering);\n    //now we have the full renumbering table in renum, calculate the inverse\n    this->inverse_renumbering.resize(this->renumbering.size());\n\n    if (this->renumbering.size() > 1)\n    {\n        calc_inverse_renumbering <<< std::min(4096, ((int)this->renumbering.size() + 511) / 512), 512 >>> (this->renumbering.raw(), this->inverse_renumbering.raw(), this->renumbering.size());\n        cudaCheckError();\n    }\n\n//\n// Step 4 - calculate number/offset of nodes in the halos from the neighbors, ring by ring\n//\n    this->halo_offsets.resize(num_neighbors + 1);\n    this->halo_offsets[0] = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        this->halo_offsets[i + 1] = this->halo_offsets[i] + this->L2H_maps[i].size();\n    }\n\n    this->set_num_halo_rows(this->halo_offsets[this->halo_offsets.size() - 1] - size);\n    int nh = this->num_halo_rows();\n    int total_rows = size + nh;\n    cudaCheckError();\n//\n// Step 6 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n//\n    int insert = 0;\n    //recalculate row_offsets\n    IVector new_row_offsets(size + 1);\n\n    if (num_blocks > 0)\n    {\n        calc_rowlen_reorder <<< num_blocks, 512>>>(this->A->row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), size, insert);\n        cudaCheckError();\n    }\n\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.end()),\n                 this->A->col_indices.begin());\n    cudaCheckError();\n    //row_offsets array created by exclusive scan of row sizes\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + size + 1, new_row_offsets.begin());\n    cudaCheckError();\n//\n// Step 7 - consolidate column indices and values\n//\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values((new_nnz + 1 )* this->A->get_block_size(), types::util<ValueTypeA>::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n\n    //reorder based on row permutation\n    if (num_blocks > 0)\n    {\n        reorder_whole_matrix <32> <<< num_blocks, 512>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), this->A->get_block_size(), size, insert);\n        cudaCheckError();\n    }\n\n    //create and append halo rows size\n    //create an identity matrix in CSR format\n    int nnz = this->A->get_num_nz();\n    IVector identity_csr_rows(nh + 1);\n    IVector identity_csr_cols(nh);\n    VVector identity_csr_vals(nh, types::util<ValueTypeA>::get_one()); //needs to be changed to MVector, but this definition is messed up in the header file (should fix later)\n    thrust_wrapper::sequence<AMGX_device>(identity_csr_rows.begin(), identity_csr_rows.end());\n    thrust_wrapper::sequence<AMGX_device>(identity_csr_cols.begin(), identity_csr_cols.end());\n    /*for example, 2x2 identity_csr matrix is created:\n      identity_csr_rows = {   0,   1,   2  }\n      identity_csr_cols = {   0,   1 }\n      identity_csr_vals = { 1.0, 1.0 } */\n    //shift identity tmatrix by size = this->A->get_num_rows();\n    thrust_wrapper::transform<AMGX_device>(identity_csr_rows.begin(), identity_csr_rows.end(), amgx::thrust::constant_iterator<INDEX_TYPE>(nnz), identity_csr_rows.begin(), amgx::thrust::plus<INDEX_TYPE>());\n    thrust_wrapper::transform<AMGX_device>(identity_csr_cols.begin(), identity_csr_cols.end(), amgx::thrust::constant_iterator<INDEX_TYPE>(size), identity_csr_cols.begin(), amgx::thrust::plus<INDEX_TYPE>());\n    /*for example, 2x2 identity_csr matrix is created:\n      identity_csr_rows = {   0,   1,   2  }\n      identity_csr_cols = {size, size+1 }\n      identity_csr_vals = { 1.0, 1.0 } */\n    /* WARNING: you must be very careful with the view you are setting (cuurently the view coming here by default is ALL = FULL). If\n                - classical path is selected then the createOneRingHaloRows -> create_one_ring_halo_rows -> append_halo_rows\n                routine will be called. It will overwrite the halo rows setup here (and will use view OWNED, which will ignore the\n                halo rows setup here, to determine how the new halo rows should be placed).\n                - aggregation path is selected then the extra rows setup here will be used in the R*A*P product, where (in order to match\n                dimensions of R and P) it is assumed that (the local partition) matrix A is square, therefore it must be padded by identity\n                rows at the bottom to compensate for the \"extra\" columns that are outside of the main square part. The old routines for the\n                aggregation path do this padding at the end of the reorder_matrix routine below.  */\n    //ViewType v = this->A->currentView();\n    //this->A->setView(ALL);\n    //Approach 1: use existing routine to append the identity matrix to the existing one\n    //            (seems like too much overhead, also need identity matrix per neighbor)\n    //DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    //prep->append_halo_rows(this->A, identity_csr_rows, identity_csr_cols, identity_csr_vals);\n    //delete prep;\n    //Approach 2: custom for this routine\n    new_row_offsets.resize(total_rows + 1);\n    new_col_indices.resize(nnz + nh);\n    int bsize = this->A->get_block_size();\n    new_values.resize((nnz + nh)*bsize + 1); //extra 1 element stores zero at the end (to follow the original design)\n    //new_values[nnz]=-1;        //marker to track the last element\n    amgx::thrust::copy(identity_csr_rows.begin(), identity_csr_rows.end(), new_row_offsets.begin() + size );\n    amgx::thrust::copy(identity_csr_cols.begin(), identity_csr_cols.end(), new_col_indices.begin() + nnz);\n    amgx::thrust::copy(new_values.begin() + nnz*bsize,    new_values.begin() + (nnz + 1)*bsize, new_values.begin() + (nnz + nh)*bsize);\n    amgx::thrust::copy(identity_csr_vals.begin(), identity_csr_vals.end(),  new_values.begin() + (nnz*bsize));\n    /* WARNING: see above. */\n    this->A->set_num_cols(total_rows);\n    this->A->set_num_rows(total_rows);\n    this->A->col_indices.swap(new_col_indices);\n    new_row_offsets.resize(total_rows + 1);\n    this->A->row_offsets.swap(new_row_offsets);\n    new_row_offsets.swap(this->old_row_offsets);\n    this->A->values.swap(new_values);\n    this->A->m_seq_offsets.resize(total_rows + 1);\n    thrust_wrapper::sequence<AMGX_device>(this->A->m_seq_offsets.begin(), this->A->m_seq_offsets.end());\n    cudaCheckError();\n    //TODO: only do this if AMG_Config matrix_halo_exchange!=2\n    this->A->delProps(COO);\n    if (!insert)\n    {\n        this->A->computeDiagonal();\n    }\n\n    this->set_initialized(this->A->row_offsets);\n    this->A->setView(OWNED);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix()\n{\n    int num_neighbors = this->neighbors.size();\n\n    if (num_neighbors == 0) { return; }\n\n    int size = this->A->get_num_rows();\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    int rings = this->B2L_rings[0].size() - 1;\n    this->set_num_halo_rings(rings);\n    int diag = this->A->hasProps(DIAG);\n    std::vector<Matrix<TConfig_d> > &halo_rows = *this->halo_rows;\n    std::vector<DistributedManager<TConfig_d> > &halo_btl = *this->halo_btl;\n    /*\n     EXAMPLE\n     The example matrix, on partition 1 arrives at this point with the following state:\n     num_rings=2\n     A.num_rows = 4; A.num_nz = 20\n     A.row_offsets = [0 4 11 15 20]\n     A.col_indices = [4 0 1 2\n                    4 5 0 1 2 3 7\n                    0 1 2 3\n                    1 2 3 6 7]\n     num_neighbors=2; neighbors = [0 2]\n     B2L_rings[[0 2 4][0 2 4]] B2L_maps[[0 1| 2 3][1 3| 0 2]]\n     L2H_maps (and halo_lists) [[4 5][6 7]]\n\n     With the exchange halo rows:\n     halo_btl[0] (received from neighbor ID 0)\n     global_id = 0; base_index=0; index_range=6; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [2 3| 0 1] L2H_maps = [4 5]\n     halo_rows[0].row_offsets = [0 5 13 17 21]\n     halo_rows[0].col_indices = [1 2 3 4 5\n                      0 1 2 3 4 5 6 7\n                      0 1 3 6\n                      0 1 2 3]\n\n     halo_btl[1] (received from neighbor ID 2)\n     global_id = 2; base_index=0; index_range=8; B2L_rings[0] = [0 2 4]; B2L_maps[0] = [1 2| 0 3] L2H_maps = [6 7]\n     halo_rows[1].row_offsets = [0 4 11 16 20]\n     halo_rows[1].col_indices = [7 1 2 3\n                      5 6 7 0 1 2 3\n                      4 5 0 2 3\n                      0 1 2 3]\n     */\n//\n// Step 1 & 2 - create renumbering\n//\n    this->createRenumbering(this->renumbering);\n    cudaCheckError();\n    /*\n     EXAMPLE\n     this->renumbering = [1 2 0 3 4 5 6 7]\n     B2L_maps = [[1 2| 0 3][2 3| 1 0]]\n     L2H_maps = [[4 5][6 7]]\n     */\n//\n// Step 3 - given a full renumbering of owned nodes, calculate inverse renumbering\n//\n    //now we have the full renumbering table in renum, calculate the inverse\n    this->inverse_renumbering.resize(this->renumbering.size());\n    calc_inverse_renumbering <<< std::min(4096, ((int)this->renumbering.size() + 511) / 512), 512 >>> (this->renumbering.raw(), this->inverse_renumbering.raw(), this->renumbering.size());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     this->inverse_renumbering = [2 0 1 3 4 5 6 7]\n     */\n//\n// Step 4 - calculate number/offset of nodes in the halos from the neighbors, ring by ring\n//\n    this->halo_offsets.resize(rings * num_neighbors + 1, 0);\n\n    for (int ring = 0; ring < rings; ring++)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            this->halo_offsets[ring * num_neighbors + i] = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n        }\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(this->halo_offsets.begin(), this->halo_offsets.end(), this->halo_offsets.begin(), size);\n    cudaCheckError();\n    this->set_num_halo_rows(this->halo_offsets[this->halo_offsets.size() - 1] - size);\n    int total_rows = size + this->num_halo_rows();\n\n    if (total_rows < this->renumbering.size()) { FatalError(\"total rows < renumbering.size(), send/recv maps should cover all matrix halo columns\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    if (total_rows > this->renumbering.size())\n    {\n        this->A->getResources()->warning(\"# owned nodes + # halo nodes > matrix columns: send/recv maps have some unreferences halo indices, they are not directly connected to our partition and therefore we won't compute them, please use 2-ring comms maps if you want to specify 2nd ring neighbors\");\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     halo_offsets [2 2 2 2]\n     after exclusive scan: 4 + [0 2 4 6 8] = [4 6 8 10 12]\n     num_halo_rows = 8, total_rows = 12\n     */\n//\n// Step 5 - create big mapping table of all halo indices we received (this may use a little too much memory)\n//\n    //count number of fine rows of neighbors\n    amgx::thrust::host_vector<INDEX_TYPE> neighbor_rows(num_neighbors + 1);\n    int max_num_rows = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neighbor_rows[i] = halo_btl[i].index_range();\n        max_num_rows = max_num_rows > halo_rows[i].get_num_rows() ? max_num_rows : halo_rows[i].get_num_rows();\n    }\n\n    thrust_wrapper::exclusive_scan<AMGX_host>(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows.begin());\n    cudaCheckError();\n    int total_rows_of_neighbors = neighbor_rows[num_neighbors];\n    /*\n     EXAMPLE\n     neigbor_rows = [0 6 14]\n     total_rows_of_neighbors = 14\n     */\n    IVector halo_mapping(total_rows_of_neighbors);\n    thrust_wrapper::fill<AMGX_device>(halo_mapping.begin(), halo_mapping.end(), -1);\n    cudaCheckError();\n\n    //ring by ring, neighbor by neighbor assign sequentially increasing numbers for halo nodes\n    for (int ring = 0; ring < rings; ring++)\n    {\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            //This renumbering has to result in the same renumbering that comes out of L2H renumbering\n            create_halo_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                    halo_btl[i].B2L_maps[0].raw() + halo_btl[i].B2L_rings[0][ring],\n                    halo_btl[i].base_index(), this->halo_offsets[ring * num_neighbors + i], size);\n            cudaCheckError();\n            /*\n             EXAMPLE\n             ring 0 neighbor 0 - halo_btl[0].B2L_maps[0] = [2 3| 0 1] halo_btl[0].L2H_maps = [4 5]\n            halo_mapping = [-1 -1 4 5 -1 -1 |-1 -1 -1 -1 -1 -1 -1 -1]\n            ring 0 neighbor 1 - halo_btl[1].B2L_maps[0] = [1 2| 0 3] halo_btl[1].L2H_maps = [6 7]\n            halo_mapping = [-1 -1 4 5 -1 -1 |-1 6 7 -1 -1 -1 -1 -1]\n            ring 1 neighbor 0 - halo_btl[0].B2L_maps[0] = [2 3| 0 1] halo_btl[0].L2H_maps = [4 5]\n            halo_mapping = [8 9 4 5 -1 -1 |-1 6 7 -1 -1 -1 -1 -1]\n            ring 1 neighbor 1 - halo_btl[1].B2L_maps[0] = [1 2| 0 3] halo_btl[1].L2H_maps = [6 7]\n            halo_mapping = [8 9 4 5 -1 -1 |10 6 7 11 -1 -1 -1 -1]\n\n             */\n        }\n    }\n\n    cudaCheckError();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = halo_btl[i].L2H_maps[0].size();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        //Map the column indices of the halo rows that point back to boundary nodes\n        apply_h2l2b_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                halo_btl[i].L2H_maps[0].raw(),\n                halo_btl[i].base_index(), this->B2L_maps[i].raw(), size);\n        cudaCheckError();\n        /*\n         EXAMPLE\n         neighbor 0 - mapping back to our own (boundary) indices\n         halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 -1 -1]\n         neighbor 1 - mapping back to our own (boundary) indices\n        halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n         */\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     neighbor_rows = [0 6 14]\n     halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n     The first part (0-6) of halo_mapping gives a local index for all the indices that we want to know about in halo_btl[0]\n     The second part (7-14) gives local indices for halo_btl[1], that is both halo ring there, and the column indices representing vertices in this partition's boundary.\n     Note that it does not give indices (-1) for vertices 5 and 6 in neighbor 1 (ID 2), which are column indices connecting it to neighbor 0, hence the two halo regions are not connected\n     */\n//\n// Step 6 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n//\n    int insert = 0;\n\n    if (this->A->hasProps(DIAG) && insertDiagonals) { insert = 1; }\n\n    diag = diag && !insertDiagonals;\n    //recalculate row_offsets\n    IVector new_row_offsets(size + this->num_halo_rows() + 1);\n    calc_rowlen_reorder <<< num_blocks, 512>>>(this->A->row_offsets.raw(), new_row_offsets.raw(), this->renumbering.raw(), size, insert);\n    cudaCheckError();\n    IVector neighbor_rows_d(num_neighbors + 1);\n    amgx::thrust::copy(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows_d.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     get row length according to renumbering\n     new_row_offsets = [4 4 7 5 0 0 0 0 0 0 0 0 0]\n     */\n    //map column indices of my own matrix\n    /*map_col_indices<4><<<num_blocks, 512>>>(this->A->row_offsets.raw(),\n                                         this->A->col_indices.raw(),\n                                         this->renumbering.raw(),\n                                         this->halo_ranges.raw(),\n                                         halo_mapping.raw(),\n                                         neighbor_rows_d.raw(),\n                                         this->base_index(), num_neighbors, size);*/\n                                         cudaCheckError();\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.begin()),\n                 amgx::thrust::make_permutation_iterator(this->renumbering.begin(), this->A->col_indices.end()),\n                 this->A->col_indices.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     use this->renumbering = [1 2 0 3 4 5 6 7]\n     to map old column indices to new column indices (i.e. according to interior - boundary - halo separation), but do not reshuffle them into their place yet\n     A.col_indices = [4 0 1 2\n                  4 5 0 1 2 3 7\n                  0 1 2 3\n                  1 2 3 6 7]\n    becomes\n    A.col_indices = [4 1 2 0\n                  4 5 1 2 0 3 7\n                  1 2 0 3\n                  2 0 3 6 7]\n\n     */\n    cudaCheckError();\n    IVector temp_row_len(max_num_rows);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        //map column indices of halo matrices and count of nonzeros we will keep\n        int size = halo_rows[i].get_num_rows();\n        int num_blocks = std::min(4096, (size + 127) / 128);\n        map_col_indices_and_count_rowlen<4> <<< num_blocks, 128, 128 * sizeof(INDEX_TYPE)>>>(\n            halo_rows[i].row_offsets.raw(),\n            halo_rows[i].col_indices.raw(),\n            temp_row_len.raw(),\n            halo_mapping.raw() + neighbor_rows[i],\n            size, insert);\n        cudaCheckError();\n\n        //number of nonzeros per row copied into big row sizes array\n        for (int ring = 0; ring < rings; ring++)\n        {\n            amgx::thrust::copy(temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring], temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring + 1], new_row_offsets.begin() + this->halo_offsets[ring * num_neighbors + i]);\n        }\n\n        cudaCheckError();\n        /*\n         EXAMPLE\n         halo_mapping = [8 9 4 5 1 2 |10 6 7 11 -1 -1 2 3]\n         look at halo row matrices, and halo_mapping, count column indices that do not map to -1 and map them to their new, local index\n         halo_rows[0].col_indices = [1 2 3 4 5\n                    0 1 2 3 4 5 6 7\n                    0 1 3 6\n                    0 1 2 3]\n        becomes\n        halo_rows[0].col_indices = [9 4 5 1 2\n                    8 9 4 5 1 2 -1 -1\n                    8 9 5 -1\n                    8 9 4 5]\n        with temp_row_len = [5 6 3 4] copied into new_row_offsets: [4 4 7 5| 5 6| 0 0| 3 4| 0 0 0]\n\n        halo_rows[1].col_indices = [7 1 2 3\n                    5 6 7 0 1 2 3\n                    4 5 0 2 3\n                    0 1 2 3]\n        becomes\n        halo_rows[1].col_indices = [3 6 7 11\n                    -1 2 3 10 6 7 11\n                    -1 -1 10 7 11\n                    10 6 7 11]\n        with temp_row_len = [4 6 3 4] copied into new_row_offsets: [4 4 7 5| 5 6| 4 6| 3 4| 3 4 0]\n         */\n    }\n\n    cudaCheckError();\n    //row_offsets array created by exclusive scan of row sizes\n    thrust_wrapper::exclusive_scan<AMGX_device>(new_row_offsets.begin(), new_row_offsets.begin() + size + this->num_halo_rows() + 1, new_row_offsets.begin());\n    cudaCheckError();\n    /*\n     EXAMPLE\n     Exclusive scan to get new_row_offsets array:\n     new_row_offsets = [0 4 8 15 20| 25 31| 35 41| 44 48| 51 55]\n     */\n//\n// Step 7 - consolidate column indices and values\n//\n    int new_nnz = new_row_offsets[new_row_offsets.size() - 1];\n    typedef typename MatPrecisionMap<t_matPrec>::Type ValueTypeA;\n    VVector new_values((new_nnz + 1 + diag * (total_rows - 1))* this->A->get_block_size(), types::util<ValueTypeA>::get_zero());\n    IVector new_col_indices(new_nnz, 0);\n    //reorder based on row permutation\n    reorder_whole_matrix <32> <<< num_blocks, 512>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), new_row_offsets.raw(), new_col_indices.raw(), new_values.raw(), this->renumbering.raw(), this->A->get_block_size(), size, insert);\n    cudaCheckError();\n\n    if (diag)\n    {\n        //reorder based on row permutation\n        reorder_vector_values <<< num_blocks, 512>>>(new_values.raw() + new_row_offsets[total_rows]*this->A->get_block_size(),\n                this->A->values.raw() + this->A->row_offsets[size]*this->A->get_block_size(),\n                this->renumbering.raw(),\n                this->A->get_block_size(), size);\n        cudaCheckError();\n    }\n\n    int cumulative_num_rows = size;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        for (int ring = 0; ring < rings; ring++)\n        {\n            int num_rows = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n            int num_blocks = std::min(4096, (num_rows + 127) / 128);\n            //copy in nonzeros that we are keeping\n            //TODO: access pattern - should be implemented with warp-wide scans to decide which nonzeros we are keeping and where the rest is going\n            reorder_whole_halo_matrix <<< num_blocks, 128>>>(halo_rows[i].row_offsets.raw() + halo_btl[i].B2L_rings[0][ring],\n                    halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(),\n                    new_row_offsets.raw() + this->halo_offsets[ring * num_neighbors + i],\n                    new_col_indices.raw(), new_values.raw(), NULL, this->A->get_block_size(), num_rows,\n                    insert, this->halo_offsets[ring * num_neighbors + i], halo_btl[i].B2L_rings[0][ring], halo_btl[i].B2L_rings[0][rings]);\n                    cudaCheckError();\n\n            if (diag)\n            {\n                amgx::thrust::copy(halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring])*this->A->get_block_size(),\n                             halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring + 1])*this->A->get_block_size(),\n                             new_values.begin() + (new_row_offsets[total_rows] + cumulative_num_rows)*this->A->get_block_size());\n                cumulative_num_rows += num_rows;\n            }\n        }\n    }\n\n    cudaCheckError();\n    /*\n     EXAMPLE\n     copy everything in place, dropping -1 column indices in the halo and reordering the owned rows\n     new_row_offsets = [0 4 8 15 20| 25 31| 35 41| 44 48| 51 55]\n     new_col_indices = [1 2 0 3\n                  4 1 2 0\n                  4 5 1 2 0 3 7\n                  2 0 3 6 7 -end of owned\n                  9 4 5 1 2\n                  8 9 4 5 1 2 - end of neighbor 0 ring 0\n                  3 6 7 11\n                  2 3 10 6 7 11 - end of neighbor 1 ring 0\n                  8 9 5\n                  8 9 4 5 - end of neighbor 0 ring 1\n                  10 7 11\n                  10 6 7 11] - end of neighbor 1 ring 1\n\n     */\n    this->A->set_num_cols(total_rows);\n    this->A->set_num_rows(size);\n    this->A->col_indices.swap(new_col_indices);\n    new_row_offsets.resize(total_rows + 1);\n    this->A->row_offsets.swap(new_row_offsets);\n    new_row_offsets.swap(this->old_row_offsets);\n    this->A->values.swap(new_values);\n    this->A->m_seq_offsets.resize(total_rows + 1);\n    thrust_wrapper::sequence<AMGX_device>(this->A->m_seq_offsets.begin(), this->A->m_seq_offsets.end());\n\n    if (insert)\n    {\n        this->A->delProps(DIAG);\n        this->A->diag.resize(total_rows);\n        amgx::thrust::copy(this->A->row_offsets.begin(), this->A->row_offsets.end() - 1, this->A->diag.begin());\n    }\n\n    cudaCheckError();\n    delete this->halo_rows;\n    delete this->halo_btl;\n    //set halo_rows and halo_btl to NULL to avoid a potential double free situation in the future\n    this->halo_rows = NULL;\n    this->halo_btl = NULL;\n    this->A->delProps(COO);\n    this->A->set_initialized(1);\n\n    //TODO: only do this if AMG_Config matrix_halo_exchange!=2\n    if (!insert) { this->A->computeDiagonal(); }\n\n    this->A->setView(OWNED);\n}\n\n//function object (functor) for thrust calls (it is a unary operator to add a constant)\ntemplate<typename T>\nclass add_constant_op\n{\n        const T c;\n    public:\n        add_constant_op(T _c) : c(_c) {}\n        __host__ __device__ T operator()(const T &x) const\n        {\n            return x + c;\n        }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::obtain_shift_l2g_reordering(index_type n, I64Vector_d &l2g, IVector_d &p, IVector_d &q)\n{\n    /* WARNING: Exchange halo of the inverse_reordering, which is implicitly based on the local_to_global_map (l2g).\n                Notice that it is implicit in the exchange_halo routine, since you are getting exactly the vector\n                halo elements, which are exactly the elements you need. They however must be shifted by the partition\n                starting points (starting global row indices, which are containe din array part_offsets).\n                This allows us to avoid constructing the global vector for inverse permutation,\n                as is done in reference MATLAB code. */\n    //Recall that part_offsets provide the starting point (global row index) of every partition, in other words,\n    //they contain the prefix sum of number of rows assigned to each partition. Also, notice that part_offsets and\n    //part_offsets_h have the same values on device and host, respectively. See below few lines for details:\n    index_type tag = 1 * 133 + 3 * 7 + 0; //some random number for the tag\n    index_type l = p.size();\n    q.resize(l);\n    amgx::thrust::copy     (p.begin(), p.end(),     q.begin());\n    thrust_wrapper::transform<AMGX_device>(q.begin(), q.end(),     q.begin(), add_constant_op<index_type>(this->part_offsets[this->global_id()]));\n    this->exchange_halo(q, tag);\n    thrust_wrapper::sequence <AMGX_device>(q.begin(), q.begin() + n);\n    thrust_wrapper::transform<AMGX_device>(q.begin(), q.begin() + n, q.begin(), add_constant_op<index_type>(this->part_offsets[this->global_id()]));\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::unpack_partition(index_type *Bp, index_type *Bc, mat_value_type *Bv)\n{\n    index_type l, n, nnz, offset, block_dimx, block_dimy;\n    index_type     *ir;\n    index_type     *Ap;\n    index_type     *Ac;\n    mat_value_type *Av;\n    IVector q;\n    //some initializations\n    this->A->getOffsetAndSizeForView(OWNED, &offset, &n);\n    this->A->getNnzForView(OWNED, &nnz);\n\n    block_dimx = this->A->get_block_dimx();\n    block_dimy = this->A->get_block_dimy();\n\n    l  = this->inverse_renumbering.size();\n    ir = this->inverse_renumbering.raw();\n    Ap = this->A->row_offsets.raw();\n    Ac = this->A->col_indices.raw();\n    Av = this->A->values.raw();\n    //(i) reorder the matrix back (into mixed interior-boundary nodes)\n    //applies to rows and columns (out-of-place)\n    reorder_partition<index_type, mat_value_type, true, true>\n\t(n, nnz, Ap, Ac, Av, Bp, Bc, Bv, l, ir, block_dimx, block_dimy);\n    cudaCheckError();\n    //obtain reordering q that combines the shift of the diagonal block with the off-diagonal block indices conversion from local to global\n    this->obtain_shift_l2g_reordering(n, this->local_to_global_map, this->inverse_renumbering, q);\n    cudaCheckError();\n    //(ii) reorder the matrix back (shift the diagonal block and convert off-diagonal block column indices from local to global)\n    //applies columns only (in-place)\n    reorder_partition<index_type, mat_value_type, false, true>\n    (n, nnz, Bp, Bc, Bv, Bp, Bc, Bv, q.size(), q.raw(), block_dimx, block_dimy);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createNeighToDestPartMap(IVector_h &neigh_to_part, IVector_h &neighbors, IVector_h &destination_part, int num_neighbors)\n{\n    neigh_to_part.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        neigh_to_part[i] = destination_part[neighbors[i]];\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createConsolidatedNeighToPartMap(IVector_h &cons_neigh_to_part, IVector_h &neigh_to_part, int my_destination_part, IVector_h &destination_part, int &num_cons_neighbors)\n{\n    // input: non-initialized cons_neigh_to_part\n    //        fine_neigh_to_part\n    //        my_destination_part\n    // output: cons_neigh_to_part\n    //         num_cons_neighbors\n    cons_neigh_to_part = neigh_to_part;\n    amgx::thrust::sort(cons_neigh_to_part.begin(), cons_neigh_to_part.end());\n    cudaCheckError();\n    cons_neigh_to_part.erase(amgx::thrust::unique(cons_neigh_to_part.begin(), cons_neigh_to_part.end()), cons_neigh_to_part.end());\n    // Remove if fine_neigh maps to same coarse partition\n    cons_neigh_to_part.erase(amgx::thrust::remove_if(cons_neigh_to_part.begin(), cons_neigh_to_part.end(), is_my_part(my_destination_part)), cons_neigh_to_part.end());\n    num_cons_neighbors = cons_neigh_to_part.size();\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::createNeighToConsNeigh(IVector_h &neigh_to_cons_neigh, IVector_h &cons_neigh_to_part, IVector_h &neigh_to_part, int my_destination_part, int &num_neighbors)\n{\n    neigh_to_cons_neigh.resize(num_neighbors);\n    amgx::thrust::lower_bound(cons_neigh_to_part.begin(), cons_neigh_to_part.end(), neigh_to_part.begin(), neigh_to_part.end(), neigh_to_cons_neigh.begin());\n    cudaCheckError();\n\n    // Flagging fine neighbors that go to same partition (haven't been found in previous step)\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        if ( neigh_to_part[i] == my_destination_part)\n        {\n            neigh_to_cons_neigh[i] = -1;\n        }\n    }\n}\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consB2Lmaps(std::vector<IVector_hd> &dest_coarse_B2L_maps, std::vector<IVector_hd> &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    //Merge B2L fine maps per coarse destination\n    dest_coarse_B2L_maps.resize(num_coarse_neighbors);\n    std::vector<int> dest_coarse_B2L_maps_scratch_sizes(num_coarse_neighbors, 0);\n    int my_id = this->global_id();\n\n    // Loop over the fine neighbors, to compute size of each dest_coarse_B2L_maps\n    for (int i = 0; i < num_fine_neighbors; i++)\n    {\n        int k = fine_neigh_to_coarse_neigh[i];\n\n        if (k != -1)\n        {\n            dest_coarse_B2L_maps_scratch_sizes[k] += coarse_B2L_maps[i].size();\n        }\n    }\n\n    // Now fill dest_coarse_B2L_maps\n    for (int k = 0; k < num_coarse_neighbors; k++)\n    {\n        dest_coarse_B2L_maps[k].resize( dest_coarse_B2L_maps_scratch_sizes[k] );\n        // Reset sizes to 0 (fill use as offset in next loop);\n        dest_coarse_B2L_maps_scratch_sizes[k] = 0;\n    }\n\n    for (int i = 0; i < num_fine_neighbors; i++)\n    {\n        int k = fine_neigh_to_coarse_neigh[i];\n\n        if (k != -1)\n        {\n            int offset = dest_coarse_B2L_maps_scratch_sizes[k];\n            amgx::thrust::copy(coarse_B2L_maps[i].begin(), coarse_B2L_maps[i].end(), dest_coarse_B2L_maps[k].begin() + offset);\n            dest_coarse_B2L_maps_scratch_sizes[k] += coarse_B2L_maps[i].size();\n        }\n    }\n\n    cudaCheckError();\n    int max_size = 0;\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        int size = dest_coarse_B2L_maps[i].size();\n\n        if (size > max_size) { max_size = size; }\n    }\n\n    // Remove duplicates (aggregates in boundary that go to same merged partition)\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        int size = dest_coarse_B2L_maps[i].size();\n        amgx::thrust::sort(dest_coarse_B2L_maps[i].begin(), dest_coarse_B2L_maps[i].begin() + size);\n        index_type num_unique = amgx::thrust::unique(dest_coarse_B2L_maps[i].begin(), dest_coarse_B2L_maps[i].begin() + size) - dest_coarse_B2L_maps[i].begin();\n        dest_coarse_B2L_maps[i].erase(dest_coarse_B2L_maps[i].begin() + num_unique, dest_coarse_B2L_maps[i].end());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeConsolidatedOffsets(const int my_id, const int my_destination_part, const bool is_root_partition, const int num_interior_rows, const int num_boundary_rows, IVector_h_vector &vertex_counts, const IVector_h &parts_to_consolidate, const int num_parts_to_consolidate, int &interior_offset, int &boundary_offset, int &total_interior_rows_in_merged, int &total_boundary_rows_in_merged, int &total_rows_in_merged, DistributedComms<TConfig> *comms)\n{\n    IVector_h my_offsets(4);\n    IVector_h my_sizes(2);\n    my_sizes[0] = num_interior_rows;\n    my_sizes[1] = num_boundary_rows;\n\n    if (!is_root_partition)\n    {\n        //Send number of interior and boundary nodes to root\n        comms->send_vector_async(my_sizes, my_destination_part, 777);\n        comms->recv_vector(my_offsets, my_destination_part, 778);\n        comms->send_vector_wait_all(my_sizes);\n    }\n    else\n    {\n        vertex_counts.resize(num_parts_to_consolidate);\n        IVector_h child_sizes(2);\n        IVector_h offsets_interior(num_parts_to_consolidate);\n        IVector_h offsets_boundary(num_parts_to_consolidate);\n        int count_int = 0;\n        int count_bdy = 0;\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            if (parts_to_consolidate[i] == my_id)\n            {\n                child_sizes = my_sizes;\n            }\n            else\n            {\n                comms->recv_vector(child_sizes, parts_to_consolidate[i], 777);\n            }\n\n            //Do a simple cumulative sum to determine total number of interior/boundary rows and their offsets on a per contributing partition basis\n            offsets_interior[i] = count_int;\n            offsets_boundary[i] = count_bdy;\n            count_int += child_sizes[0];\n            count_bdy += child_sizes[1];\n            //Save them\n            vertex_counts[i].resize(2);\n            vertex_counts[i][0] = child_sizes[0];\n            vertex_counts[i][1] = child_sizes[1];\n        }\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            //Send back to contributing partitions\n            IVector_h offsets_to_send(4);\n            offsets_to_send[0] = offsets_interior[i];\n            offsets_to_send[1] = offsets_boundary[i];\n            offsets_to_send[2] = count_int;\n            offsets_to_send[3] = count_bdy;\n\n            if (parts_to_consolidate[i] == my_id)\n            {\n                my_offsets = offsets_to_send;\n            }\n            else\n            {\n                comms->send_vector(offsets_to_send, parts_to_consolidate[i], 778);    // cannot make async, rewriting internal buffer\n            }\n        }\n    }\n\n    interior_offset = my_offsets[0];\n    boundary_offset = my_offsets[1] + my_offsets[2] - num_interior_rows;\n    total_interior_rows_in_merged = my_offsets[2];\n    total_boundary_rows_in_merged = my_offsets[3];\n    total_rows_in_merged = my_offsets[2] + my_offsets[3];\n}\n\n\n\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consB2LmapsOnRoot(int &num_consolidated_neighbors, std::vector<IVector_hd> &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, std::vector<IVector_hd> &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    // TODO: it is possible to optimize exchanges, for example fuse recv_vector in recreating coarse neigbours\n\n    //  output: num_consolidated_neighbor, consolidated_B2L_maps, consolidated_coarse_ids\n    //  input:  dest_coarse_B2L_maps, is_root_partition, my_id, my_destination_part, num_fine_parts_to_consolidate, num_coarse_neighbors, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh\n    if (my_destination_part != my_id)\n    {\n        //if not root, send coarse neighbor list using fine indices and the corresponding boundary lists\n        IVector_h num_coarse_neigh(1);\n        num_coarse_neigh[0] = num_coarse_neighbors;\n        comms->send_vector_async(num_coarse_neigh, my_destination_part, 1111);\n        comms->send_vector_async(coarse_neigh_to_fine_part, my_destination_part, 2222);\n        comms->send_vector_async(num_bdy_per_coarse_neigh, my_destination_part, 3333);\n\n        for (int i = 0; i < num_coarse_neighbors; i++)\n        {\n            comms->send_vector_async(dest_coarse_B2L_maps[i], my_destination_part, 4444 + i) ;\n        }\n\n        comms->send_vector_wait_all(num_coarse_neigh);\n        comms->send_vector_wait_all(coarse_neigh_to_fine_part);\n        comms->send_vector_wait_all(num_bdy_per_coarse_neigh);\n\n        for (int i = 0; i < num_coarse_neighbors; i++)\n        {\n            comms->send_vector_wait_all(dest_coarse_B2L_maps[i]) ;\n        }\n    }\n\n    if (is_root_partition)\n    {\n        IVector_h num_coarse_ids_from_part(fine_parts_to_consolidate);\n        IVector_h_vector coarse_ids_from_part(num_fine_parts_to_consolidate);\n        IVector_h_vector num_coarse_neigh_bdys_from_part(num_fine_parts_to_consolidate);\n        //If root, receive sizes, and resize receive buffers\n        int total_num_coarse_ids = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h temp(1);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(temp, current_part, 1111);\n            }\n            else\n            {\n                temp[0] = num_coarse_neighbors;\n            }\n\n            num_coarse_ids_from_part[i] = temp[0];\n            coarse_ids_from_part[i].resize(temp[0]);\n            num_coarse_neigh_bdys_from_part[i].resize(temp[0]);\n            total_num_coarse_ids += temp[0];\n        }\n\n        //Create a neighbor list for the consolidated coarse matrix, by merging coarse neighbor lists from partitions that are being merged\n        consolidated_coarse_ids.resize(total_num_coarse_ids);\n        int count = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            // Get from each partition the coarse partition ids in their B2L maps\n            if (current_part != my_id)\n            {\n                comms->recv_vector(coarse_ids_from_part[i], current_part, 2222);\n                comms->recv_vector(num_coarse_neigh_bdys_from_part[i], current_part, 3333);\n            }\n            else\n            {\n                coarse_ids_from_part[i] = coarse_neigh_to_fine_part;\n                num_coarse_neigh_bdys_from_part[i] = num_bdy_per_coarse_neigh;\n            }\n\n            amgx::thrust::copy(coarse_ids_from_part[i].begin(), coarse_ids_from_part[i].end(), consolidated_coarse_ids.begin() + count);\n            count += num_coarse_ids_from_part[i];\n        }\n\n        cudaCheckError();\n        //eliminate duplicates\n        amgx::thrust::sort(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end());\n        cudaCheckError();\n        consolidated_coarse_ids.erase(amgx::thrust::unique(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end()), consolidated_coarse_ids.end());\n        cudaCheckError();\n        num_consolidated_neighbors = consolidated_coarse_ids.size();\n        IVector_h_vector coarse_ids_from_part_to_consolidated_neighbor(num_fine_parts_to_consolidate);;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            coarse_ids_from_part_to_consolidated_neighbor[i].resize(num_coarse_ids_from_part[i]);\n            amgx::thrust::lower_bound(consolidated_coarse_ids.begin(), consolidated_coarse_ids.end(), coarse_ids_from_part[i].begin(), coarse_ids_from_part[i].end(), coarse_ids_from_part_to_consolidated_neighbor[i].begin());\n        }\n\n        cudaCheckError();\n        // Map each coarse partition to new coarse ID\n        consolidated_B2L_maps.resize(num_consolidated_neighbors);\n        IVector_h consolidated_B2L_maps_sizes(num_consolidated_neighbors);\n        // Offset in the consolidated_B2L_maps\n        IVector_h_vector coarse_ids_offsets(num_fine_parts_to_consolidate);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            consolidated_B2L_maps_sizes[i] = 0;\n        }\n\n        // Compute the size of each consolidated_B2L_maps and offsets into it, where we will receive the parts coming from partitions that are getting merged into this one\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            coarse_ids_offsets[i].resize(num_coarse_ids_from_part[i]);\n\n            for (int j = 0; j < num_coarse_ids_from_part[i]; j++)\n            {\n                int coarse_id = coarse_ids_from_part[i][j];\n                int k = num_coarse_neigh_bdys_from_part[i][j];\n                coarse_ids_offsets[i][j] = consolidated_B2L_maps_sizes[ coarse_ids_from_part_to_consolidated_neighbor[i][j] ];\n                consolidated_B2L_maps_sizes[ coarse_ids_from_part_to_consolidated_neighbor[i][j] ] += k;\n            }\n        }\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            consolidated_B2L_maps[i].resize(consolidated_B2L_maps_sizes[i]);\n        }\n\n        // Receive the B2L maps from each child partition, concatenate them (gets sorted outside)\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            for (int j = 0; j < num_coarse_ids_from_part[i]; j++)\n            {\n                int my_coarse_neigh = coarse_ids_from_part_to_consolidated_neighbor[i][j];\n                int offset = coarse_ids_offsets[i][j];\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector( consolidated_B2L_maps[my_coarse_neigh], current_part, 4444 + j, offset,  num_coarse_neigh_bdys_from_part[i][j]); //Need to do proper tagging here, otherwise messages from the same source would get mixed up\n                }\n                else\n                {\n                    amgx::thrust::copy(dest_coarse_B2L_maps[j].begin(), dest_coarse_B2L_maps[j].end(), consolidated_B2L_maps[my_coarse_neigh].begin() + offset);\n                }\n            }\n        }\n\n        cudaCheckError();\n    }\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateAndRenumberHalos(IVector_h &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_ids, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    consAndRenumberHalos(aggregates, manager_halo_offsets, halo_offsets, neighbors, num_fine_neighbors, consolidated_coarse_ids, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, num_all_aggregates, comms);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateAndRenumberHalos(IVector_d &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_ids, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    consAndRenumberHalos(aggregates, manager_halo_offsets, halo_offsets, neighbors, num_fine_neighbors, consolidated_coarse_ids, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, num_all_aggregates, comms);\n}\n\ntemplate <class TConfig>\ntemplate <class IVector_hd>\nvoid DistributedManagerBase<TConfig>::consAndRenumberHalos(IVector_hd &aggregates, const IVector_h &manager_halo_offsets, IVector_h &halo_offsets, const IVector_h &neighbors, int num_fine_neighbors, const IVector_h &consolidated_coarse_neigh_to_fine_part, int num_consolidated_neighbors, const IVector_h &destination_part, int my_destination_part, bool is_root_partition, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int num_parts, int my_id, int total_rows_in_merged, int &num_all_aggregates, DistributedComms<TConfig> *comms)\n{\n    /*\n         * EXAMPLE 2\n         This example is independent from the previous ones.\n         Say partition 0 and 1 are merging (into 0) partition 0 is neighbors with 1,2,3 and partition 1 is neighbors with 0,3,4\n         Partitions 3 and 4 are merging (into partition 3) and partition 2 is not merging with anyone.\n         This example details the renumbering of halo indices on partition 0 and partition 1.\n         aggregates on partition 0:\n         [(fine interior nodes) (fine boundary nodes) (fine halo from part 1) (fine halo from part 2) (fine halo from part 3)]\n         [(fine interior nodes) (fine boundary nodes) (13 13 15) (12 15 17) (14 16 18)]\n         aggregates on partition 1:\n         [(fine interior nodes) (fine boundary nodes) (fine halo from part 0) (fine halo from part 3) (fine halo from part 4)]\n         [(fine interior nodes) (fine boundary nodes) (14 16 17) (18 19 19) (15 15 17)]\n\n         manager_halo_offsets on partition 0:\n         [22 25 28 31]\n         manager_halo_offsets on partition 1:\n         [20 23 26 29]\n         halo_offsets on both partitions are uninitialised: [0 0 0] and [0 0]\n         neighbors on partition 0: [1 2 3] partition 1: [0 3 4]\n         num_fine_neighbors partition 0: 3 partition 1: 3\n         consolidated_coarse_neigh_to_fine_part partition 0: [2 3] partition 1: [3]\n         num_consolidated_neighbors partition 0: 2 partition 1: 1\n         destination_part [0 0 2 3 3]\n         my_destination_part partition 0: 0 partition 1: 0\n         is_root_partition partition 0: true partition 1: false\n         fine_parts_to_consolidate partition 0: [0 1]\n         num_fine_parts_to_consolidate partition 0: 2\n         num_parts 5\n         my_id partition 0: 0 partition 1: 1\n         total_rows_in_merged partition 0 and 1: 24 (=sum of the two below)\n         num_all_aggregates partition partition 0: 13 partition 1: 11 - will be updated with the number of halo aggregates\n         */\n    //\n    // Step 9.2 - com up with nonmerge lists\n    //\n    int num_fine_nonmerge_neighbors;// = fine_nonmerge_neighbors.size();\n    //NUmber of neighbors we are not merging with\n    num_fine_nonmerge_neighbors = 0;\n\n    for (int i = 0 ; i < num_fine_neighbors; i++)\n    {\n        if (destination_part[neighbors[i]] != my_destination_part)\n        {\n            num_fine_nonmerge_neighbors++;\n        }\n    }\n\n    IVector_h halo_sizes(num_fine_nonmerge_neighbors);\n    IVector_h fine_nonmerge_neigh_to_cons_fine_part(num_fine_nonmerge_neighbors);\n    IVector_h fine_nonmerge_neighbor_to_fine_neighbor(num_fine_nonmerge_neighbors);\n    num_fine_nonmerge_neighbors = 0;\n\n    for (int i = 0 ; i < num_fine_neighbors; i++)\n    {\n        if (destination_part[neighbors[i]] != my_destination_part)\n        {\n            halo_sizes[num_fine_nonmerge_neighbors] = manager_halo_offsets[i + 1] - manager_halo_offsets[i];\n            fine_nonmerge_neighbor_to_fine_neighbor[num_fine_nonmerge_neighbors] = i;\n            fine_nonmerge_neigh_to_cons_fine_part[num_fine_nonmerge_neighbors] = destination_part[neighbors[i]];\n            num_fine_nonmerge_neighbors++;\n        }\n    }\n\n    /*\n     * EXAMPLE 2\n     num_fine_nonmerge_neighbors partition 0: 2 partition 1: 2\n     fine_nonmerge_neighbor_to_fine_neighbor partition 0: [1 2] partition 1: [1 2]\n     fine_nonmerge_neigh_to_cons_fine_part partition 0: [2 3] partition 1: [3 3]\n     halo_sizes partition 0: [3 3] partition 1: [3 3]\n     */\n\n    //Send them to root along with the halo parts of the aggregates vector\n    if (!is_root_partition)\n    {\n        IVector_h num_fine_nonmerge_neigh(1);\n        num_fine_nonmerge_neigh[0] = num_fine_nonmerge_neighbors;\n        // TODO: async? might be faster.\n        comms->send_vector(num_fine_nonmerge_neigh, my_destination_part, 1111);\n        comms->send_vector(halo_sizes, my_destination_part, 2222);\n        comms->send_vector(fine_nonmerge_neigh_to_cons_fine_part, my_destination_part, 3333);\n\n        // Here check l2h_identity flag and act accordingly\n        for (int i = 0; i < num_fine_nonmerge_neighbors; i++)\n        {\n            comms->send_vector_async(aggregates, my_destination_part, 4444 + i, manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[i]], halo_sizes[i]) ;\n        }\n\n        //comms->send_vector_wait_all(num_fine_nonmerge_neigh);\n        //comms->send_vector_wait_all(halo_sizes);\n        //comms->send_vector_wait_all(fine_nonmerge_neigh_to_cons_fine_part);\n        comms->send_vector_wait_all(aggregates);\n        /*\n         * EXAMPLE 2\n         Partition 1 sends to partition 0:\n         num_fine_nonmerge_neigh 2\n         halo_sizes [3 3]\n         fine_nonmerge_neigh_to_cons_fine_part [3 3]\n         for loop: sends two pieces: [(18 19 19)] [(15 15 17)]\n         */\n    }\n\n    if (is_root_partition)\n    {\n        //\n        // Step 9.3 Root receives this info, creates metadata\n        //\n        std::vector<VecInt_t> num_fine_nonmerge_neigh_array(num_fine_parts_to_consolidate);\n        IVector_h_vector halo_sizes_array(num_fine_parts_to_consolidate);\n        IVector_h_vector fine_nonmerge_neigh_to_cons_fine_part_array(num_fine_parts_to_consolidate);\n        std::vector<std::vector<IVector> > fine_halo_aggregates_to_root_array(num_fine_parts_to_consolidate);\n        std::vector<VecInt_t> min_index_coarse_halo(num_consolidated_neighbors, 0x7FFFFFFF);\n        std::vector<VecInt_t> max_index_coarse_halo(num_consolidated_neighbors, 0);\n        std::vector<VecInt_t> fine_part_to_consolidated_neighbor(num_parts, -1);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            fine_part_to_consolidated_neighbor[consolidated_coarse_neigh_to_fine_part[i]] = i;\n        }\n\n        /*\n         * EXAMPLE 2\n         everything from here on is for partition 0, since that is the root partition\n         fine_part_to_consolidated_neighbor [-1 -1 0 1 -1]\n         */\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h temp(1);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(temp, current_part, 1111);\n            }\n            else\n            {\n                temp[0] = num_fine_nonmerge_neighbors;\n            }\n\n            num_fine_nonmerge_neigh_array[i] = temp[0];\n            halo_sizes_array[i].resize(temp[0]);\n            fine_nonmerge_neigh_to_cons_fine_part_array[i].resize(temp[0]);\n            fine_halo_aggregates_to_root_array[i].resize(temp[0]);\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(halo_sizes_array[i], current_part, 2222);\n            }\n            else\n            {\n                halo_sizes_array[i] = halo_sizes;\n            }\n\n            if (current_part != my_id)\n            {\n                comms->recv_vector(fine_nonmerge_neigh_to_cons_fine_part_array[i], current_part, 3333);\n            }\n            else\n            {\n                fine_nonmerge_neigh_to_cons_fine_part_array[i] = fine_nonmerge_neigh_to_cons_fine_part;\n            }\n\n            //Receive the halo regions\n            for (int j = 0; j < temp[0]; j++)\n            {\n                fine_halo_aggregates_to_root_array[i][j].resize(halo_sizes_array[i][j]);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(fine_halo_aggregates_to_root_array[i][j], current_part, 4444 + j);\n                }\n                else\n                {\n                    //HERE\n                    amgx::thrust::copy(aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[j]],\n                                 aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[j]] + halo_sizes[j],\n                                 fine_halo_aggregates_to_root_array[i][j].begin()); //TODO: not do this copying around on the root\n                }\n\n#define MIN(a,b) a<b?a:b;\n#define MAX(a,b) a>b?a:b;\n                //Find minimum and maximum halo indices as not to allocate too much scratch space later\n                int min_index = amgx::thrust::reduce(fine_halo_aggregates_to_root_array[i][j].begin(), fine_halo_aggregates_to_root_array[i][j].end(), int(0x7FFFFFFF), amgx::thrust::minimum<int>());\n                int max_index = amgx::thrust::reduce(fine_halo_aggregates_to_root_array[i][j].begin(), fine_halo_aggregates_to_root_array[i][j].end(), int(0), amgx::thrust::maximum<int>());\n                min_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]] = MIN((int)min_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]], min_index);\n                max_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]] = MAX((int)max_index_coarse_halo[fine_part_to_consolidated_neighbor[fine_nonmerge_neigh_to_cons_fine_part_array[i][j]]], max_index);\n            }\n        }\n\n        cudaCheckError();\n        /*\n         * EXAMPLE 2\n         num_fine_nonmerge_neigh_array = [2 2]\n         halo_sizes_array = [[3 3][3 3]]\n         fine_nonmerge_neigh_to_cons_fine_part_array[][] = [[2 3][3 3]]\n         fine_halo_aggregates_to_root_array[from][to][fine halo vertex] [[[12 15 17][14 16 18]]\n                                                                         [[18 19 19][15 15 17]]]\n        min_index_coarse_halo[12 14]\n        max_index_coarse_halo[17 19]\n         */\n        halo_offsets[0] = total_rows_in_merged;\n        //Now we have all the halo nodes, let's renumber them.\n        int min_index = amgx::thrust::reduce(min_index_coarse_halo.begin(), min_index_coarse_halo.end(), int(0x7FFFFFFF), amgx::thrust::minimum<int>());\n        int max_index = amgx::thrust::reduce(max_index_coarse_halo.begin(), max_index_coarse_halo.end(), int(0), amgx::thrust::maximum<int>());\n        cudaCheckError();\n        //\n        // Step 9.4 compute halo indices on root nodes\n        //\n        int scratch_size;\n\n        if (num_consolidated_neighbors == 0)\n        {\n            scratch_size = 1;\n        }\n        else\n        {\n            scratch_size = max_index - min_index + 2;\n        }\n\n        IVector scratch(scratch_size);\n\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            thrust_wrapper::fill<TConfig::memSpace>(scratch.begin(), scratch.end(), 0);\n            int dest_part = consolidated_coarse_neigh_to_fine_part[i];\n\n            //Flag halo indices that occur for a specific coarse neighbor\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int size = halo_sizes_array[j][k];\n                        this->flag_halo_ids(size, scratch, fine_halo_aggregates_to_root_array[j][k], min_index_coarse_halo[i], max_index, min_index);\n                    }\n                }\n            }\n\n            thrust_wrapper::exclusive_scan<TConfig::memSpace>(scratch.begin(), scratch.end(), scratch.begin(), halo_offsets[i]); //renumber them with the proper offset into our halo\n            halo_offsets[i + 1] = scratch[scratch.size() - 1];\n\n            //now read them back\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int size = halo_sizes_array[j][k];\n                        int block_size = 128;\n                        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n                        this->read_halo_ids(size, scratch, fine_halo_aggregates_to_root_array[j][k], min_index_coarse_halo[i]);\n                        //and send them back to contributing partitions\n                        hipDeviceSynchronize(); //TODO: don't need to synchronize when using GPUDirect\n                        cudaCheckError();\n                        int current_part = fine_parts_to_consolidate[j];\n                        int tag = 4444 + dest_part;\n\n                        if (current_part != my_id)\n                        {\n                            comms->send_vector_async(fine_halo_aggregates_to_root_array[j][k], current_part, tag);    //!!!!: we are sending them back not in sequential order, need tags!!!!\n                        }\n                        else\n                        {\n                            amgx::thrust::copy(fine_halo_aggregates_to_root_array[j][k].begin(), fine_halo_aggregates_to_root_array[j][k].end(), aggregates.begin() + manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[k]]);\n                        }\n                    }\n                }\n            }\n\n            /*\n             * EXAMPLE 2\n             the array that is sent back in pieces:\n             fine_halo_aggregates_to_root_array[from][to][fine halo vertex] [[[24 25 26][27 29 31]]\n                                                                         [[31 32 32][28 28 30]]]\n            halo_offsets = [24 27 33]\n             */\n        } // Loop over consolidated neighbors\n\n        cudaCheckError();\n\n        // Wait for sends to have completed (this is to prevent fine_halo_aggregates_to_root_array to be destroyed before send has finished)\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            int dest_part = consolidated_coarse_neigh_to_fine_part[i];\n\n            for (int j = 0; j < num_fine_parts_to_consolidate; j++)\n            {\n                for (int k = 0; k < num_fine_nonmerge_neigh_array[j]; k++)\n                {\n                    if (fine_nonmerge_neigh_to_cons_fine_part_array[j][k] == dest_part)\n                    {\n                        int current_part = fine_parts_to_consolidate[j];\n\n                        if (current_part != my_id)\n                        {\n                            comms->send_vector_wait_all(fine_halo_aggregates_to_root_array[j][k]);\n                        }\n                    }\n                }\n            }\n        } // Loop over consolidated neighbors\n\n        //Send total number of rows in the aggregated matrix\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            IVector_h total_rows(1);\n            total_rows[0] = halo_offsets[num_consolidated_neighbors];\n\n            if (current_part != my_id)\n            {\n                comms->send_vector(total_rows, current_part, 5555);\n            }\n            else\n            {\n                num_all_aggregates = total_rows[0];\n            }\n        }\n    } // If is root partition\n\n    if (!is_root_partition)\n    {\n        for (int i = 0; i < num_fine_nonmerge_neighbors; i++)\n        {\n            int tag = 4444 + fine_nonmerge_neigh_to_cons_fine_part[i];\n            comms->recv_vector(aggregates, my_destination_part, tag, manager_halo_offsets[fine_nonmerge_neighbor_to_fine_neighbor[i]], halo_sizes[i]);\n        }\n\n        IVector_h total_rows(1);\n        comms->recv_vector(total_rows, my_destination_part, 5555);\n        num_all_aggregates = total_rows[0];\n    }\n\n    /*\n     * EXAMPLE 2\n     num_all_aggregates = 33 (both partitions 0 and 1\n     */\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcExchangePtr(void *&ptr, bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_root_partition, int my_id, DistributedComms<TConfig> *comms)\n{\n    hipIpcMemHandle_t handle;\n\n    if (is_root_partition)\n    {\n        hipIpcGetMemHandle( (hipIpcMemHandle_t *) &handle, ptr ) ;\n        cudaCheckError();\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part !=  my_id)\n            {\n                comms->send_raw_data(&handle, sizeof(handle), current_part, 456);\n            }\n        }\n    }\n    else\n    {\n        comms->recv_raw_data(&handle, sizeof(handle), my_root_partition, 456);\n        hipError_t err = hipIpcOpenMemHandle( (void **) &ptr, handle, hipIpcMemLazyEnablePeerAccess);\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcWaitForChildren(bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_destination_part, int my_id, DistributedComms<TConfig> *comms)\n{\n    hipEvent_t event;\n    hipIpcEventHandle_t event_handle;\n    hipEventCreateWithFlags(&event, hipEventDisableTiming | hipEventInterprocess);\n    cudaCheckError();\n    hipIpcGetEventHandle( &event_handle, event);\n    cudaCheckError();\n    // Each rank record the event\n    hipEventRecord(event);\n    cudaCheckError();\n\n    if (is_root_partition)\n    {\n        std::vector<hipEvent_t> child_events(num_parts_to_consolidate);\n        std::vector<hipIpcEventHandle_t> child_event_handles(num_parts_to_consolidate);\n\n        // Root partition receives event_handles from child and stores in child_event_handles\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                comms->recv_raw_data(&(child_event_handles[i]), sizeof(hipIpcEventHandle_t), current_part, 987 + current_part);\n                hipIpcOpenEventHandle(&child_events[i], child_event_handles[i]);\n                cudaCheckError();\n            }\n        }\n\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            if (parts_to_consolidate[i] != my_id)\n            {\n                hipEventSynchronize(child_events[i]);\n                cudaCheckError();\n            }\n        }\n    }\n    else\n    {\n        comms->send_raw_data(&event_handle, sizeof(hipIpcEventHandle_t), my_destination_part, 987 + my_id);\n    }\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::ipcWaitForRoot(bool is_root_partition, int num_parts_to_consolidate, IVector_h &parts_to_consolidate, int my_destination_part, int my_id, DistributedComms<TConfig> *comms)\n{\n    hipEvent_t event;\n    hipIpcEventHandle_t event_handle;\n    hipEventCreateWithFlags(&event, hipEventDisableTiming | hipEventInterprocess);\n\n    if (is_root_partition)\n    {\n        hipIpcGetEventHandle( &event_handle, event);\n        cudaCheckError();\n        // Root records the event\n        hipEventRecord(event);\n        cudaCheckError();\n\n        // Root partition sends event_handles to child\n        for (int i = 0; i < num_parts_to_consolidate; i++)\n        {\n            int current_part = parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                comms->send_raw_data(&event_handle, sizeof(event_handle), current_part, 988 + current_part);\n            }\n        }\n    }\n    else\n    {\n        comms->recv_raw_data(&event_handle, sizeof(event_handle), my_destination_part, 988 + my_id);\n        hipIpcOpenEventHandle(&event, event_handle);\n        cudaCheckError();\n        hipEventSynchronize(event);\n        cudaCheckError();\n    }\n}\n\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::read_halo_ids(int size, IVector_d &scratch, IVector_d &halo_aggregates, VecInt_t min_index_coarse_halo)\n{\n    int block_size = 128;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n    read_halo_ids_kernel <<< num_blocks, block_size>>>(scratch.raw(), halo_aggregates.raw(), min_index_coarse_halo, size);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::read_halo_ids(int size, IVector_h &scratch, IVector_h &halo_aggregates, VecInt_t min_index_coarse_halo)\n{\n    FatalError(\"read_halo_ids not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::flag_halo_ids(int size, IVector_d &scratch, IVector_d &halo_aggregates, VecInt_t min_index_coarse_halo,  int max_index, int min_index)\n{\n    int block_size = 128;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n    flag_halo_ids_kernel <<< num_blocks, block_size>>>(scratch.raw(), halo_aggregates.raw(), min_index_coarse_halo, size, max_index - min_index + 1);\n    cudaCheckError();\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::flag_halo_ids(int size, IVector_h &scratch, IVector_h &halo_aggregates, VecInt_t min_index_coarse_halo,  int max_index, int min_index)\n{\n    FatalError(\"flag_halo_ids not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::consolidateAndUploadAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &A)\n{\n    FatalError(\"Fine level consolidation not implemented on host yet\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::exchangeSolveResultsConsolidation(int &num_iters, std::vector<PODVector_h> &res_history, AMGX_STATUS &status, bool store_res_history)\n{\n    int bsize = this->A->get_block_size();\n    PODVector_h res_history_tmp;\n\n    if (!m_is_fine_level_consolidated)\n    {\n        return;\n    }\n    else\n    {\n        int my_id = this->getFineLevelComms()->get_global_id();\n        IVector_h my_num_iters(1);\n\n        if (m_is_fine_level_root_partition)\n        {\n            my_num_iters[0] = num_iters;\n\n            if (store_res_history)\n            {\n                // Pack the res_history vector into array\n                res_history_tmp.resize( (num_iters + 1)*bsize);\n\n                for (int i = 0; i < num_iters + 1; i++)\n                {\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        res_history_tmp[i * bsize + j] = res_history[i][j];\n                    }\n                }\n            }\n\n            for (int i = 0; i < m_num_fine_level_parts_to_consolidate; i++)\n            {\n                int current_part = m_fine_level_parts_to_consolidate[i];\n\n                if (my_id != current_part)\n                {\n                    getFineLevelComms()->send_vector_async(my_num_iters, current_part, 245);\n\n                    if (store_res_history)\n                    {\n                        getFineLevelComms()->send_vector_async(res_history_tmp, current_part, 246);\n                    }\n                }\n            }\n\n            for (int i = 0; i < m_num_fine_level_parts_to_consolidate; i++)\n            {\n                int current_part = m_fine_level_parts_to_consolidate[i];\n\n                if (my_id != current_part)\n                {\n                    getFineLevelComms()->send_raw_data(&status, sizeof(status), current_part, 247);\n                }\n            }\n\n            getFineLevelComms()->send_vector_wait_all(my_num_iters);\n\n            if (store_res_history)\n            {\n                getFineLevelComms()->send_vector_wait_all(res_history_tmp);\n            }\n        }\n        else\n        {\n            // Store num_iters\n            getFineLevelComms()->recv_vector(my_num_iters, m_my_fine_level_destination_part, 245);\n            num_iters = my_num_iters[0];\n\n            if (store_res_history)\n            {\n                // Fill res_history vector\n                res_history.resize(num_iters + 1);\n                res_history_tmp.resize( (num_iters + 1)*bsize);\n                getFineLevelComms()->recv_vector(res_history_tmp, m_my_fine_level_destination_part, 246);\n\n                for (int i = 0; i < num_iters + 1; i++)\n                {\n                    res_history[i].resize(bsize);\n\n                    for (int j = 0; j < bsize; j++)\n                    {\n                        res_history[i][j] = res_history_tmp[i * bsize + j];\n                    }\n                }\n            }\n\n            getFineLevelComms()->recv_raw_data(&status, sizeof(status), m_my_fine_level_destination_part, 247);\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::consolidateAndUploadAll(int n, int nnz, int block_dimx, int block_dimy, const int *row_ptrs, const int *col_indices, const void *data, const void *diag, Matrix<TConfig> &in_A)\n{\n    this->A = &in_A;\n    this->createComms(this->A->getResources()); //refresh comms\n    DistributedComms<TConfig> *comms = this->getComms();\n    int my_id = comms->get_global_id();\n    int num_parts = comms->get_num_partitions();\n    int num_rings = this->num_halo_rings();\n    int num_neighbors = this->neighbors.size();\n    // All partitions have to call this, otherwise it fails\n    // Step 1: Figure out which partition should be consolidated together based on their host_name and their PCI-E slot ID\n    IVector_h destination_part(num_parts);\n    this->computeDestinationPartitionsWithCons(my_id, num_parts, destination_part, comms);\n    int my_destination_part = destination_part[my_id];\n    // Check if I'm root partition and how many msgs I will receive\n    bool is_root_partition = false;\n    int num_parts_to_consolidate = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            is_root_partition = true;\n            num_parts_to_consolidate++;\n        }\n    }\n\n    if (my_destination_part >= num_parts)\n    {\n        FatalError(\"During consolidation, sending data to partition that doesn't exist\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create cons_part_to_part map\n    IVector_h cons_part_to_part = destination_part;\n    amgx::thrust::sort(cons_part_to_part.begin(), cons_part_to_part.end());\n    cudaCheckError();\n    cons_part_to_part.erase(amgx::thrust::unique(cons_part_to_part.begin(), cons_part_to_part.end()), cons_part_to_part.end());\n    cudaCheckError();\n    int num_cons_partitions = cons_part_to_part.size();\n\n    // If number of consolidated partitions is the same as number of partitions, simply call uploadAll\n    if (num_cons_partitions ==  num_parts)\n    {\n        this->initializeUploadReorderAll(n, nnz, block_dimx, block_dimy, row_ptrs, col_indices, data, diag, *(this->A));\n        this->m_is_fine_level_consolidated = false;\n        return;\n    }\n\n    if (is_root_partition)\n    {\n        this->A->getResources()->expandRootPool();\n    }\n\n    this->m_is_fine_level_consolidated = true;\n\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in fine_level consolidation\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Fill with b2l_maps\n    IVector_h_vector B2L_maps_tmp;\n    B2L_maps_tmp.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        B2L_maps_tmp[i] = this->cached_B2L_maps[i];\n    }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int *col_indices_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    int col_alloc = 0;\n    col_indices_hd = (int *) this->getDevicePointerForData((void *)col_indices, nnz * block_dimx * block_dimy * sizeof(int), &col_alloc);\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data, nnz * block_dimx * block_dimy * sizeof(mat_value_type), &data_alloc);\n\n    if (diag != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag, nnz * block_dimx * block_dimy * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    // Copy the original row_offsets array (this is required when replacing coefficients\n    this->m_old_row_offsets_CONS.resize(n + 1);\n    hipMemcpy(this->m_old_row_offsets_CONS.raw(), row_ptrs, (n + 1)*sizeof(int), hipMemcpyDefault);\n    cudaCheckError();\n    this->m_old_nnz_CONS = nnz;\n    // This function:\n    // Creates fine level consolidated matrices\n    // Modifies the btl_maps, lth_maps\n    // Create part_to_cons_part map\n    IVector_h part_to_cons_part(num_parts);\n    amgx::thrust::lower_bound(cons_part_to_part.begin(), cons_part_to_part.end(), destination_part.begin(), destination_part.end(), part_to_cons_part.begin());\n    cudaCheckError();\n    IVector_h neigh_to_part;\n    this->createNeighToDestPartMap(neigh_to_part, this->neighbors, destination_part, num_neighbors);\n    IVector_h cons_neigh_to_part;\n    int num_cons_neighbors;\n    this->createConsolidatedNeighToPartMap(cons_neigh_to_part, neigh_to_part, my_destination_part, destination_part, num_cons_neighbors);\n    IVector_h neigh_to_cons_neigh;\n    this->createNeighToConsNeigh( neigh_to_cons_neigh, cons_neigh_to_part, neigh_to_part, my_destination_part, num_neighbors);\n    // ---------------------------------------\n    // MERGE B2L MAPS BASED ON DEST PARTITION\n    // ---------------------------------------\n    IVector_h_vector dest_B2L_maps;\n    this->consolidateB2Lmaps(dest_B2L_maps,  B2L_maps_tmp, neigh_to_cons_neigh,  num_cons_neighbors, num_neighbors);\n    // ------------------------------------\n    // Renumber interior and boundary rows\n    // ------------------------------------\n    int num_interior_rows;\n    int num_boundary_rows;\n    IVector_h renumbering;\n    this->createAggregatesRenumbering(renumbering, dest_B2L_maps, n, num_cons_neighbors, num_interior_rows, num_boundary_rows, num_rings);\n    // --------------------------------------------------\n    // Create list of destination parts to consolidate\n    // --------------------------------------------------\n    // Store whether or not this is a root partition on fine level\n    IVector_h parts_to_consolidate;\n    parts_to_consolidate.resize(num_parts_to_consolidate);\n    int count = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            parts_to_consolidate[count] = i;\n            count++;\n        }\n    }\n\n    // ---------------------------------------------------------------------\n    // Each partition computes its offset for its interior and boundary nodes\n    // ---------------------------------------------------------------------\n    IVector_h_vector vertex_counts;\n    int interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged;\n    int total_rows_in_merged;\n    this->computeConsolidatedOffsets(my_id, my_destination_part, is_root_partition, num_interior_rows, num_boundary_rows, vertex_counts, parts_to_consolidate, num_parts_to_consolidate, interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged, total_rows_in_merged, comms);\n    // -----------------------------------\n    // Each partition renumber it's rows\n    // -----------------------------------\n    int total_num_halos = 0;\n\n    // Pack new bdy_ids\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        total_num_halos += this->cached_L2H_maps[i].size();\n    }\n\n    IVector_h row_ids(n + total_num_halos, -1);\n    this->m_row_ids_CONS.resize(n + total_num_halos);\n\n    // Renumber the interior and boundary rows\n    for (int i = 0; i < n; i++)\n    {\n        int new_id;\n\n        if (renumbering.size() == 0)\n        {\n            new_id = i;\n        }\n        else\n        {\n            new_id = renumbering[i];\n        }\n\n        new_id += ((new_id >= num_interior_rows) ? boundary_offset : interior_offset);\n        row_ids[i] = new_id;\n    }\n\n    for (int i = 0; i < num_cons_neighbors; i++)\n    {\n        thrust_wrapper::transform<AMGX_host>(dest_B2L_maps[i].begin(),\n                          dest_B2L_maps[i].end(),\n                          amgx::thrust::constant_iterator<index_type>(boundary_offset),\n                          dest_B2L_maps[i].begin(),\n                          amgx::thrust::plus<index_type>());\n    }\n\n    cudaCheckError();\n    // -------------------------------------------------\n    // Send dest_B2L_maps to root partitions\n    // ------------------------------------------------\n    IVector_h num_bdy_per_cons_neigh(num_cons_neighbors);\n\n    for (int i = 0; i < num_cons_neighbors; i++)\n    {\n        num_bdy_per_cons_neigh[i] = dest_B2L_maps[i].size();\n    }\n\n    IVector_h root_cons_neighbors;\n    int root_num_cons_neighbors = 0;\n    IVector_h_vector cons_B2L_maps;\n    this->consolidateB2LmapsOnRoot(root_num_cons_neighbors, cons_B2L_maps, root_cons_neighbors, dest_B2L_maps, cons_neigh_to_part, num_bdy_per_cons_neigh, parts_to_consolidate, num_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_cons_neighbors, comms);\n    IVector_h halo_ids_offsets(num_neighbors + 1);\n    IVector_h halo_ids;\n    int halo_ids_size = 0;\n    halo_ids_offsets[0] = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        halo_ids_size += this->cached_L2H_maps[i].size();\n        halo_ids_offsets[i + 1] = halo_ids_size;\n    }\n\n    halo_ids.resize(halo_ids_size);\n    // Do exchange with neighbors\n    // Pack new bdy_ids\n    IVector_h_vector bdy_ids;\n    bdy_ids.resize(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = this->cached_B2L_maps[i].size();\n        bdy_ids[i].resize(size);\n\n        // Pack buffer\n        for (int j = 0; j < size; j++)\n        {\n            bdy_ids[i][j] = row_ids[this->cached_B2L_maps[i][j]];\n        }\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->send_vector_async(bdy_ids[i], this->neighbors[i], 6666 + this->neighbors[i]);\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->recv_vector(halo_ids, this->neighbors[i], 6666 + my_id, halo_ids_offsets[i], this->cached_L2H_maps[i].size());\n    }\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        comms->send_vector_wait_all(bdy_ids[i]);\n    }\n\n    IVector_h halo_offsets(root_num_cons_neighbors + 1, 0);\n    int root_num_rows;\n    this->consolidateAndRenumberHalos(halo_ids, halo_ids_offsets, halo_offsets, this->neighbors, num_neighbors, root_cons_neighbors, root_num_cons_neighbors, destination_part, my_destination_part, is_root_partition, parts_to_consolidate, num_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, root_num_rows, comms);\n\n    if (is_root_partition)\n    {\n        this->B2L_maps.resize(cons_B2L_maps.size());\n\n        for (int i = 0; i < cons_B2L_maps.size(); i++)\n        {\n            amgx::thrust::sort(cons_B2L_maps[i].begin(), cons_B2L_maps[i].end());\n            this->B2L_maps[i].copy(cons_B2L_maps[i]); // H2D copy of B2L maps\n        }\n\n        cudaCheckError();\n    }\n\n    // Now renumber the row_ids based on lth_maps\n    count = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        for (int j = 0; j < this->cached_L2H_maps[i].size(); j++)\n        {\n            row_ids[this->cached_L2H_maps[i][j]] = halo_ids[count];\n            count++;\n        }\n    }\n\n    hipMemcpy(this->m_row_ids_CONS.raw(), row_ids.raw(), (n + total_num_halos)*sizeof(int), hipMemcpyDefault);\n    cudaCheckError();\n    int bsize = block_dimx * block_dimy;\n\n    if (is_root_partition)\n    {\n        this->A->row_offsets.resize(root_num_rows + 1);\n    }\n\n    void *root_row_ptr = (void *) this->A->row_offsets.raw();\n\n    if (useCudaIpc)\n    {\n        // ----------------------------------------------------\n        // 1. cudaIPC to get pointer to root's row_offset array\n        // ----------------------------------------------------\n        this->ipcExchangePtr(root_row_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n        // -------------------------------------------------------------------\n        // 2. each rank copy it's row length on root partition using row_ids\n        // -------------------------------------------------------------------\n        int cta_size = 128;\n        int grid_size = std::min(4096, (n + total_num_halos + cta_size - 1) / cta_size);\n        zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_old_row_offsets_CONS.raw(), ((int *) root_row_ptr) /* IPC */, this->m_row_ids_CONS.raw(), n, total_num_halos, (mat_value_type *) diag);\n        cudaCheckError();\n        // Root partition waits for children to be done writing their result\n        this->ipcWaitForChildren(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n    }\n    else   // CudaIpcNotAvailable\n    {\n        this->checkPinnedBuffer( std::max( nnz * sizeof(mat_value_type), (n + 1)*std::max(sizeof(index_type), sizeof(value_type)) ) );\n\n        if (!is_root_partition)\n        {\n            IVector_h data_to_send(3);\n            data_to_send[0] = n;\n            data_to_send[1] = nnz;\n            data_to_send[2] = total_num_halos;\n            int dummy;\n            void *row_ptrs_to_send = this->getHostPointerForData((void *)row_ptrs, sizeof(index_type) * (n + 1), &dummy);\n            comms->send_vector(data_to_send, my_destination_part, 10000 + my_id);\n            comms->send_raw_data(row_ptrs_to_send, (n + 1)*sizeof(int), my_destination_part, 10001 + my_id);\n            comms->send_raw_data(&row_ids[0], (n + total_num_halos)*sizeof(int), my_destination_part, 10002 + my_id);\n        }\n        else\n        {\n            hipEvent_t event;\n            hipEventCreate(&event);\n            cudaCheckError();\n            //TODO: Could use streams here\n            //TODO: Avoid extra device to host copies\n            std::vector<IVector_h> data_recv(num_parts_to_consolidate);\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                data_recv[i].resize(3);\n                int current_part =  parts_to_consolidate[i];\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(data_recv[i], current_part, 10000 + current_part);\n                }\n                else\n                {\n                    data_recv[i][0] = n;\n                    data_recv[i][1] = nnz;\n                    data_recv[i][2] = total_num_halos;\n                }\n            }\n\n            this->m_child_n.resize(num_parts_to_consolidate);\n            this->m_child_nnz.resize(num_parts_to_consolidate);\n            this->m_child_num_halos.resize(num_parts_to_consolidate);\n            this->m_child_row_ids.resize(num_parts_to_consolidate);\n            this->m_child_old_row_offsets.resize(num_parts_to_consolidate);\n            int max_n = 0;\n            int max_nnz = 0;\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                this->m_child_n[i] = data_recv[i][0];\n                this->m_child_nnz[i] = data_recv[i][1];\n                this->m_child_num_halos[i] = data_recv[i][2];\n\n                if (this->m_child_n[i] > max_n) { max_n = this->m_child_n[i]; }\n\n                if (this->m_child_nnz[i] > max_nnz) { max_nnz = this->m_child_nnz[i]; }\n\n                this->m_child_row_ids[i].resize(this->m_child_n[i] + this->m_child_num_halos[i]);\n                this->m_child_old_row_offsets[i].resize(this->m_child_n[i] + 1);\n            }\n\n            this->m_child_max_n = max_n;\n            this->m_child_max_nnz = max_nnz;\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                int cta_size = 128;\n                int grid_size = std::min(4096, (this->m_child_n[i] + this->m_child_num_halos[i] + cta_size - 1) / cta_size);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_vector(this->m_child_old_row_offsets[i], current_part, 10001 + current_part, 0, this->m_child_n[i] + 1);\n                    comms->recv_vector(this->m_child_row_ids[i], current_part, 10002 + current_part, 0, this->m_child_n[i] + this->m_child_num_halos[i]);\n                    zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), this->m_child_row_ids[i].raw(), this->m_child_n[i], this->m_child_num_halos[i], (mat_value_type *) diag);\n                    cudaCheckError();\n                    // Wait for kernel to finish before overwriting host buffer\n                    hipEventRecord(event);\n                    cudaCheckError();\n                    hipEventSynchronize(event);\n                    cudaCheckError();\n                }\n                else\n                {\n                    zero_copy_row_lengths_ids_offsets<mat_value_type> <<< grid_size, cta_size>>>(this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), this->m_row_ids_CONS.raw(), n, total_num_halos, (mat_value_type *) diag);\n                    cudaCheckError();\n                    hipEventRecord(event);\n                    cudaCheckError();\n                    hipEventSynchronize(event);\n                    cudaCheckError();\n                }\n            }\n\n            cudaCheckError();\n            hipEventDestroy(event);\n            cudaCheckError();\n        } // If root partition\n\n        //TODO: is this necessary\n        comms->barrier();\n    }\n\n    //3. root does a exclusive_scan\n    if (is_root_partition)\n    {\n        hipEvent_t event;\n        hipEventCreate(&event);\n        cudaCheckError();\n        // Populate the halo rows with diagonal, increase the length of the halo rows\n        thrust_wrapper::fill<TConfig::memSpace>(this->A->row_offsets.begin() + halo_offsets[0], this->A->row_offsets.begin() + halo_offsets[root_num_cons_neighbors], 1);\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(this->A->row_offsets.begin(), this->A->row_offsets.end(), this->A->row_offsets.begin());\n        hipEventRecord(event);\n        cudaCheckError();\n        hipEventSynchronize(event);\n        cudaCheckError();\n        this->A->set_initialized(0);\n        this->A->delProps(DIAG); // We always insert the diagonal\n        this->A->delProps(COO); // No COO\n        this->A->setColsReorderedByColor(false); // Cols not reordered by color\n        int nnz = this->A->row_offsets[root_num_rows]; // This is a device to host copy\n        this->A->resize(root_num_rows, root_num_rows, nnz, block_dimx, block_dimy);\n        this->A->set_num_nz(nnz); // num_nz doesn't include halo rows\n        //this->A->set_initialized(1);\n        hipEventDestroy(event);\n        cudaCheckError();\n    }\n    else\n    {\n        this->A->set_initialized(0);\n        this->A->resize( 0, 0, 0, block_dimx, block_dimy );\n        this->A->delProps(DIAG); // We always insert the diagonal\n        this->A->delProps(COO); // No COO\n        this->A->setColsReorderedByColor(false); // Cols not reordered by color\n        //this->A->set_initialized(1);\n    }\n\n    if (useCudaIpc)\n    {\n        // ----------------------------------------------\n        // 4. Do ipc consolidation of values and columns\n        // ----------------------------------------------\n        // Child partition waits for parent to create row_offsets\n        this->ipcWaitForRoot(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        void *root_col_ptr = (void *) this->A->col_indices.raw();\n        void *root_val_ptr = (void *) this->A->values.raw();\n        this->ipcExchangePtr(root_col_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        this->ipcExchangePtr(root_val_ptr, is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        int cta_size2 = 128;\n        int grid_size2 = std::min(4096, (n + cta_size2 - 1) / cta_size2);\n        ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(n, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), ( (int *) root_row_ptr ) /*IPC*/, col_indices_hd, ( (int *) root_col_ptr) /*IPC*/, data_hd, ( (mat_value_type *) root_val_ptr ) /*IPC*/, diag_hd, bsize);\n        cudaCheckError();\n        // Root partition waits for children to upload their matrices\n        this->ipcWaitForChildren(is_root_partition, num_parts_to_consolidate, parts_to_consolidate, my_destination_part, my_id, comms);\n        cudaCheckError();\n\n        // Child partitions close their mem handle (they are done upload data)\n        if (!is_root_partition)\n        {\n            hipIpcCloseMemHandle(root_row_ptr);\n            hipIpcCloseMemHandle(root_val_ptr);\n            hipIpcCloseMemHandle(root_col_ptr);\n        }\n    }\n    else // If cudaIpcNotAvailable\n    {\n        if (!is_root_partition)\n        {\n            int dummy;\n            void *col_indices_to_send = this->getHostPointerForData((void *)col_indices, sizeof(index_type) * nnz, &dummy);\n            comms->send_raw_data(col_indices_to_send, nnz * sizeof(int), my_destination_part, 10000 + my_id);\n            void *data_to_send = this->getHostPointerForData((void *)data, sizeof(mat_value_type) * nnz, &dummy);\n            comms->send_raw_data(data_to_send, nnz * bsize * sizeof(mat_value_type), my_destination_part, 10001 + my_id);\n\n            if (diag != NULL)\n            {\n                void *diag_to_send = this->getHostPointerForData((void *)diag, sizeof(mat_value_type) * n, &dummy);\n                comms->send_raw_data(diag_to_send, n * bsize * sizeof(mat_value_type), my_destination_part, 10002 + my_id);\n            }\n        }\n        else\n        {\n            hipEvent_t event;\n            hipEventCreate(&event);\n            cudaCheckError();\n            //TODO: Could use streams here\n            int *child_col_indices;\n            mat_value_type *child_data;\n            mat_value_type *child_diag = NULL;\n            hipHostAlloc( (void **) &child_col_indices, this->m_child_max_nnz * sizeof(int), hipHostMallocMapped);\n            hipHostAlloc( (void **) &child_data, this->m_child_max_nnz * bsize * sizeof(mat_value_type), hipHostMallocMapped);\n\n            if (diag != NULL)\n            {\n                hipHostAlloc( (void **) &child_diag, (this->m_child_max_n)*bsize * sizeof(mat_value_type), hipHostMallocMapped);\n            }\n\n            for (int i = 0; i < num_parts_to_consolidate; i++)\n            {\n                int current_part =  parts_to_consolidate[i];\n                int cta_size2 = 128;\n                int grid_size2 = std::min(4096, (this->m_child_n[i] + cta_size2 - 1) / cta_size2);\n\n                if (current_part != my_id)\n                {\n                    comms->recv_raw_data(child_col_indices, this->m_child_nnz[i]*sizeof(int), current_part, 10000 + current_part);\n                    comms->recv_raw_data(child_data, this->m_child_nnz[i]*bsize * sizeof(mat_value_type), current_part, 10001 + current_part);\n\n                    if (diag != NULL)\n                    {\n                        comms->recv_raw_data(child_diag, this->m_child_n[i]*bsize * sizeof(mat_value_type), current_part, 10002 + current_part);\n                    }\n\n                    int *child_col_indices_hd;\n                    mat_value_type *child_data_hd;\n                    mat_value_type *child_diag_hd = NULL;\n                    hipHostGetDevicePointer(&child_col_indices_hd, child_col_indices, 0);\n                    hipHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                    if (diag != NULL)\n                    {\n                        hipHostGetDevicePointer(&child_diag_hd, child_diag, 0);\n                    }\n\n                    ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(this->m_child_n[i], this->m_child_row_ids[i].raw(), this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), child_col_indices_hd, this->A->col_indices.raw(), child_data_hd, this->A->values.raw(), child_diag_hd, bsize);\n                    cudaCheckError();\n                    // Wait for kernel to finish before overwriting host buffer\n                    hipEventRecord(event);\n                    cudaCheckError();\n                    hipEventSynchronize(event);\n                    cudaCheckError();\n                }\n                else\n                {\n                    ipc_consolidation_upload_matrix<mat_value_type> <<< grid_size2, cta_size2>>>(n, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), col_indices_hd, this->A->col_indices.raw(), data_hd, this->A->values.raw(), diag_hd, bsize);\n                    cudaCheckError();\n                    hipEventRecord(event);\n                    cudaCheckError();\n                    hipEventSynchronize(event);\n                    cudaCheckError();\n                }\n            }\n\n            cudaCheckError();\n            hipEventDestroy(event);\n            cudaCheckError();\n            amgx::memory::hipHostFree(child_col_indices);\n            amgx::memory::hipHostFree(child_data);\n\n            if (diag != NULL)\n            {\n                amgx::memory::hipHostFree(child_diag);\n            }\n        } // If root partition\n\n        //TODO: is this necessary\n        comms->barrier();\n    }\n\n    // Store the original fine level communicator\n    this->m_is_fine_level_root_partition = is_root_partition;\n    this->m_my_fine_level_destination_part = my_destination_part;\n    // Create a clone of the original communicator\n    this->m_fine_level_comms = comms; //this->_comms is the same pointer that this->m_fine_level_comms right now, so we can overwrite this->_comms, but make sure that we release m_fine_level_cons\n    this->_comms = this->m_fine_level_comms->CloneSubComm(cons_part_to_part, is_root_partition);   // this->_comms will be empty comm for non-root partition and new comm for root ranks only if root partition\n    this->m_fine_level_id = my_id;\n\n    if (is_root_partition)\n    {\n        int cta_size = 128;\n        int grid_size3 = std::min(4096, ( (root_num_rows - halo_offsets[0]) + cta_size - 1) / cta_size);\n\n        if (grid_size3 != 0)\n        {\n            set_halo_cols_values <<< grid_size3, cta_size>>>(this->A->row_offsets.raw(), this->A->col_indices.raw(), this->A->values.raw(), halo_offsets[0], root_num_rows, bsize);\n            cudaCheckError();\n        }\n\n        int my_cons_id = part_to_cons_part[my_id];\n        this->_global_id = my_cons_id;\n        this->_num_interior_nodes = total_interior_rows_in_merged;\n        this->_num_boundary_nodes = total_boundary_rows_in_merged;\n\n        for (int i = 0; i < root_num_cons_neighbors; i++)\n        {\n            root_cons_neighbors[i] = part_to_cons_part[root_cons_neighbors[i]];\n        }\n\n        this->_comms->set_neighbors(root_num_cons_neighbors);\n        this->neighbors = root_cons_neighbors;\n        this->halo_offsets = halo_offsets; // H2D copy of halo offsets\n        this->m_num_fine_level_parts_to_consolidate = num_parts_to_consolidate;\n        this->m_fine_level_parts_to_consolidate = parts_to_consolidate;\n        this->set_num_halo_rings(num_rings);\n        this->set_num_halo_rows(halo_offsets[root_num_cons_neighbors] - halo_offsets[0]);\n        // B2L_maps has already been copied\n        this->B2L_rings.resize(root_num_cons_neighbors);\n\n        for (int i = 0; i < root_num_cons_neighbors; i++)\n        {\n            this->B2L_rings[i].resize(2);\n            this->B2L_rings[i][0] = 0;\n            this->B2L_rings[i][1] = cons_B2L_maps[i].size();\n        }\n\n        this->set_initialized(this->A->row_offsets);\n        this->A->set_initialized(0);\n        this->A->delProps(DIAG);\n        this->A->diag.resize(root_num_rows);\n        this->A->computeDiagonal(); //\n        this->A->setView(OWNED);\n        hipEventCreate(&(this->comm_event));\n        cudaCheckError();\n        this->A->set_initialized(1);\n    }\n    else\n    {\n        this->neighbors.resize(0);\n        this->halo_offsets.resize(0);\n    }\n\n    /* free memory (if needed) */\n    if (col_alloc) { amgx::memory::hipFreeAsync(col_indices_hd); }\n    if (data_alloc) { amgx::memory::hipFreeAsync(data_hd); }\n    if (diag_alloc) { amgx::memory::hipFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsNoCons(int n, int nnz, const mat_value_type *data_pinned, const mat_value_type *diag_pinned)\n{\n    //matrix parameters\n    //int num_nnz  = this->A->get_num_nz();\n    int num_rows   = this->halo_offsets[0];\n    int total_rows = num_rows + this->num_halo_rows();\n    int block_size = this->A->get_block_size();\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    //cuda parameters\n    int num_blocks = std::min(4096, (num_rows + 127) / 128);\n\n    /* WARNING: the number of non-zero elements (nnz) in the array data_pinned and A->values (num_nnz) might be different at this point.\n       1. If the matrix has CSR property and therefore diagonal is included in the matrix this values will be the same.\n       2. If the matrix has DIAG property and therefore diagonal is originally stored separately, and later appended to the array\n          of values, and subsequently inserted into the matrix than num_nnz = nnz + n. We have to account for this fact when replacing the\n          coefficients (and use nnz not num_nnz).\n       obs.: see calls to computeDiagonal (matrix.cu), AMGX_matrix_upload and AMGX_replace_coefficients (amgx_c.cu), and\n          uploadMatrix and replaceMatrixCoefficients[No|With]Cons (distributed_manager.cu) for details. */\n\n    /* check early exit */\n    if ((this->neighbors.size() == 0 || this->renumbering.size() == 0) && !this->m_is_fine_level_glued)\n    {\n        return;\n    }\n\n    cudaCheckError();\n    /* allocate if data and diag if they are not pinned */\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data_pinned, nnz * block_size * sizeof(mat_value_type), &data_alloc);\n\n    if (diag_pinned != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag_pinned, num_rows * block_size * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    /* replace the values (reordering them if needed) */\n    if (insertDiagonals && diag_pinned != NULL)\n    {\n        replace_values_matrix <32> <<< num_blocks, 512>>>(data_hd, diag_hd, this->old_row_offsets.raw(), this->A->row_offsets.raw(), this->A->values.raw(), this->renumbering.raw(), block_size, num_rows);\n        cudaCheckError();\n    }\n    else\n    {\n        replace_values_matrix <32> <<< num_blocks, 512>>>(data_hd, this->old_row_offsets.raw(), this->A->row_offsets.raw(), this->A->values.raw(), this->renumbering.raw(), block_size, num_rows);\n        cudaCheckError();\n\n        if (diag_pinned != NULL)\n        {\n            reorder_vector_values <<< num_blocks, 512>>>(this->A->values.raw() + this->A->row_offsets[total_rows]*block_size, diag_hd, this->renumbering.raw(), block_size, num_rows);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::hipFreeAsync(data_hd); }\n    if (diag_alloc) { amgx::memory::hipFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsWithCons(int n, int nnz, const mat_value_type *data_pinned, const mat_value_type *diag_pinned)\n{\n    //matrix parameters\n    //int num_nnz  = this->A->get_num_nz();\n    /* WARNING: in consolidation, for non-root partitions, halo_offsets\n       might be NULL due to the call halo_offsets.resize(0); at the end\n       of the routine uploadMatrix->consolidateAndUploadAll. We should\n       use the parameter n instead this->halo_offsets[0] for num_rows. */\n    int num_rows   = n;\n    int block_size = this->A->get_block_size();\n    mat_value_type *data_hd = NULL;\n    mat_value_type *diag_hd = NULL;\n    int data_alloc = 0;\n    int diag_alloc = 0;\n    data_hd = (mat_value_type *) this->getDevicePointerForData((void *)data_pinned, nnz * block_size * sizeof(mat_value_type), &data_alloc);\n\n    if (diag_pinned != NULL)\n    {\n        diag_hd = (mat_value_type *) this->getDevicePointerForData((void *)diag_pinned, num_rows * block_size * sizeof(mat_value_type), &diag_alloc);\n    }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n\n    if (useCudaIpc)\n    {\n        // Child partitions wait for root to be done\n        this->ipcWaitForRoot(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        void *root_row_ptr = (void *) this->A->row_offsets.raw();\n        void *root_val_ptr = (void *) this->A->values.raw();\n        this->ipcExchangePtr(root_row_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        this->ipcExchangePtr(root_val_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        // replace the values, insert the diagonal\n        int ncons = this->m_old_row_offsets_CONS.size() - 1;\n        int cta_size = 128;\n        int grid_size2 = std::min(4096, (ncons + cta_size - 1) / cta_size);\n        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size>>>(ncons, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), ( (int *) root_row_ptr )/*IPC*/, data_hd, ( (mat_value_type *) root_val_ptr )/*IPC*/, diag_hd, this->A->get_block_size() );\n        cudaCheckError();\n        // Root partition wait for child to be done replacing their values\n        this->ipcWaitForChildren(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            hipIpcCloseMemHandle(root_row_ptr);\n            hipIpcCloseMemHandle(root_val_ptr);\n        }\n    }\n    else // cudaIpcNotAvailable\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            int bsize = this->A->get_block_size();\n            int ncons = this->m_old_row_offsets_CONS.size() - 1;\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                int nnzcons = this->m_old_nnz_CONS;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, nnzcons * bsize * sizeof(mat_value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data(data_to_send, nnzcons * bsize * sizeof(mat_value_type), this->m_my_fine_level_destination_part, 10001 + this->fine_level_id());\n\n                if (diag_pinned != NULL)\n                {\n                    void *diag_to_send = this->getHostPointerForData((void *)diag_pinned, ncons * bsize * sizeof(mat_value_type), &dummy);\n                    this->getFineLevelComms()->send_raw_data(diag_to_send, ncons * bsize * sizeof(mat_value_type), this->m_my_fine_level_destination_part, 10002 + this->fine_level_id());\n                }\n            }\n            else\n            {\n                hipEvent_t event;\n                hipEventCreate(&event);\n                cudaCheckError();\n                //TODO: Could use streams here\n                mat_value_type *child_data;\n                mat_value_type *child_diag = NULL;\n                hipHostAlloc( (void **) &child_data, this->m_child_max_nnz * bsize * sizeof(mat_value_type), hipHostMallocMapped);\n\n                if (diag_pinned != NULL)\n                {\n                    hipHostAlloc( (void **) &child_diag, (this->m_child_max_n)*bsize * sizeof(mat_value_type), hipHostMallocMapped);\n                }\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part =  this->m_fine_level_parts_to_consolidate[i];\n                    int cta_size2 = 128;\n                    int grid_size2 = std::min(4096, (this->m_child_n[i] + cta_size2 - 1) / cta_size2);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data(child_data, this->m_child_nnz[i]*bsize * sizeof(mat_value_type), current_part, 10001 + current_part);\n\n                        if (diag_pinned != NULL)\n                        {\n                            this->getFineLevelComms()->recv_raw_data(child_diag, this->m_child_n[i]*bsize * sizeof(mat_value_type), current_part, 10002 + current_part);\n                        }\n\n                        mat_value_type *child_data_hd;\n                        mat_value_type *child_diag_hd = NULL;\n                        hipHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                        if (diag_pinned != NULL)\n                        {\n                            hipHostGetDevicePointer(&child_diag_hd, child_diag, 0);\n                        }\n\n                        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size2>>>(this->m_child_n[i], this->m_child_row_ids[i].raw(), this->m_child_old_row_offsets[i].raw(), this->A->row_offsets.raw(), child_data_hd, this->A->values.raw(), child_diag_hd, bsize);\n                        cudaCheckError();\n                        // Wait for kernel to finish before overwriting host buffer\n                        hipEventRecord(event);\n                        cudaCheckError();\n                        hipEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        ipc_consolidation_replace_values<mat_value_type> <<< grid_size2, cta_size2>>>(ncons, this->m_row_ids_CONS.raw(), this->m_old_row_offsets_CONS.raw(), this->A->row_offsets.raw(), data_hd, this->A->values.raw(), diag_hd, bsize);\n                        cudaCheckError();\n                        //cudaEventRecord(event);\n                        cudaCheckError();\n                        //cudaEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n                hipEventDestroy(event);\n                cudaCheckError();\n                amgx::memory::hipHostFree(child_data);\n\n                if (diag_pinned != NULL)\n                {\n                    amgx::memory::hipHostFree(child_diag);\n                }\n            } // If root partition\n\n            //TODO: is this necessary\n            this->getFineLevelComms()->barrier();\n        } //agg\n        else if (this->m_is_fine_level_glued) // classical\n        {\n            int bsize = this->A->get_block_size();\n            int ncons = this->m_old_row_offsets_CONS.size() - 1;\n            IVector_h nnz_off;\n            nnz_off.resize(this->getConsolidationArrayOffsets().size());\n            IVector_h nnz_array;\n            nnz_array.resize(this->getConsolidationArrayOffsets().size() - 1);\n            this->getFineLevelComms()->all_gather( nnz,\n                                                   nnz_array,\n                                                   this->getConsolidationArrayOffsets().size() - 1);\n            nnz_off[0] = 0;\n\n            for (int i = 0; i < nnz_array.size(); i++)\n            {\n                nnz_off[i + 1] = nnz_off[i] + nnz_array[i];\n            }\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, nnz * bsize * sizeof(mat_value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data( data_to_send,\n                        nnz * bsize * sizeof(mat_value_type),\n                        this->m_my_fine_level_destination_part,\n                        10001 + this->fine_level_id());\n\n                if (diag_pinned != NULL)\n                {\n                    void *diag_to_send = this->getHostPointerForData((void *)diag_pinned, n * sizeof(mat_value_type), &dummy);\n                    this->getFineLevelComms()->send_raw_data( diag_to_send,\n                            n * bsize * sizeof(mat_value_type),\n                            this->m_my_fine_level_destination_part,\n                            10002 + this->fine_level_id());\n                    //diag.resize(0);\n                    cudaCheckError();\n                }\n\n                //values.resize(0);\n                cudaCheckError();\n            }\n            else\n            {\n                //TODO: Could use streams here\n                mat_value_type *child_data;\n                mat_value_type *child_diag = NULL;\n                // Assumes partions have been glued already\n                this->A->getNnzForView(OWNED, &nnz);\n                hipHostAlloc( (void **) &child_data, nnz * bsize * sizeof(mat_value_type), hipHostMallocMapped);\n\n                if (diag_pinned != NULL)\n                {\n                    hipHostAlloc( (void **) &child_diag, this->halo_offsets[this->neighbors.size()]*bsize * sizeof(mat_value_type), hipHostMallocMapped);\n                }\n\n                // roots copy their data\n                memcpy ( &child_data[0], data_pinned, nnz_array[this->fine_level_id()]*sizeof(value_type));\n\n                if (diag_pinned != NULL)\n                {\n                    memcpy ( &child_diag[0], diag_pinned, n * sizeof(value_type));\n                }\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part =  this->m_fine_level_parts_to_consolidate[i];\n                    int current_offset = nnz_off[current_part] - nnz_off[this->fine_level_id()] ;\n                    int current_nnz = nnz_array[current_part];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data( &child_data[current_offset],\n                                current_nnz * bsize * sizeof(mat_value_type),\n                                current_part,\n                                10001 + current_part);\n\n                        if (diag_pinned != NULL)\n                            this->getFineLevelComms()->recv_raw_data( &child_diag[this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()]],\n                                    (this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part])*bsize * sizeof(mat_value_type),\n                                    current_part,\n                                    10002 + current_part);\n                    }\n                }\n\n                cudaCheckError();\n                // we can follow the usual upload path for raw data now\n                // Assumes partions have been glued already\n                int os;\n                this->A->getOffsetAndSizeForView(OWNED, &os, &n);\n                replaceMatrixCoefficientsNoCons( n, nnz,  child_data, child_diag);\n                cudaCheckError();\n                amgx::memory::hipHostFree(child_data);\n\n                if (diag_pinned != NULL)\n                {\n                    amgx::memory::hipHostFree(child_diag);\n                }\n            } // If root partition\n\n            //TODO: is this necessary\n            this->getFineLevelComms()->barrier();\n        } // cla\n    } // not ipc\n\n    this->A->setView(OWNED);\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::hipFreeAsync(data_hd); }\n\n    if (diag_alloc) { amgx::memory::hipFreeAsync(diag_hd); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->isFineLevelConsolidated() || (this->isFineLevelGlued() && !this->isGlued()))\n    {\n        transformAndUploadVectorWithCons(v, data, n, block_dim);\n    }\n    else\n    {\n        v.resize(n * block_dim);\n        cudaCheckError();\n        // Upload on host\n        hipMemcpy(v.raw(), (value_type *)data, n * block_dim * sizeof(value_type), hipMemcpyDefault);\n        cudaCheckError();\n        // Permute based on renumbering vector\n        transformVector(v);\n        int tag = 0;\n        // Exchange halos\n        this->exchange_halo(v, tag);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVectorWithCons(VVector_v &v, const void *data_pinned, int n, int block_dim)\n{\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n    this->getFineLevelComms()->barrier();\n    void *root_temp_ptr = NULL;\n    VVector_v temp;\n\n    if (this->m_is_fine_level_root_partition && !this->m_is_fine_level_glued )\n    {\n        temp.resize(this->halo_offsets[this->neighbors.size()]*v.get_block_size(), types::util<value_type>::get_zero());\n        temp.set_block_dimx(v.get_block_dimx());\n        temp.set_block_dimy(v.get_block_dimy());\n        root_temp_ptr = (void *) temp.raw();\n    }\n\n    cudaCheckError();\n    int data_alloc = 0;\n    value_type *data_hd = NULL;\n\n    if (!this->m_is_fine_level_glued )\n    {\n        data_hd = (value_type *) this->getDevicePointerForData((void *)data_pinned, n * block_dim * sizeof(value_type), &data_alloc);\n    }\n\n    if (useCudaIpc)\n    {\n        // Do IPC\n        this->ipcExchangePtr(root_temp_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        int num_blocks = std::min(4096, (n + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, data_hd, this->m_row_ids_CONS.raw(), v.get_block_size(), n);\n        cudaCheckError();\n        // Root partition waits for children to be done\n        this->ipcWaitForChildren(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            hipIpcCloseMemHandle(root_temp_ptr);\n        }\n    }\n    else // If cudaIpcNotAvail\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            // Exchange the vector between root and child\n            if (!this->m_is_fine_level_root_partition)\n            {\n                IVector_h size(1);\n                size[0] = n;\n                this->getFineLevelComms()->send_vector(size, this->m_my_fine_level_destination_part, 20000 + this->fine_level_id());\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data(data_to_send, n * v.get_block_size()*sizeof(value_type), this->m_my_fine_level_destination_part, 20001 + this->fine_level_id());\n            }\n            else\n            {\n                hipEvent_t event;\n                hipEventCreate(&event);\n                cudaCheckError();\n                IVector_h child_n(this->m_num_fine_level_parts_to_consolidate);\n                int max_n = 0;\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_vector(child_n, current_part, 20000 + current_part, i, 1);\n                    }\n                    else\n                    {\n                        child_n[i] = n;\n                    }\n\n                    if (child_n[i] > max_n) { max_n = child_n[i]; }\n                }\n\n                value_type *child_data;\n                hipHostAlloc( (void **) &child_data, max_n * v.get_block_size()*sizeof(value_type), hipHostMallocMapped);\n                value_type *child_data_hd;\n                hipHostGetDevicePointer(&child_data_hd, child_data, 0);\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n                    int num_blocks = std::min(4096, (child_n[i] + 511) / 512);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data(&child_data[0], child_n[i]*v.get_block_size()*sizeof(value_type), current_part, 20001 + current_part);\n                        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, child_data_hd, this->m_child_row_ids[i].raw(), v.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                        hipEventRecord(event);\n                        cudaCheckError();\n                        hipEventSynchronize(event);\n                        cudaCheckError();\n                    }\n                    else\n                    {\n                        reorder_vector_values <<< num_blocks, 512>>>( (value_type *) root_temp_ptr, data_hd, this->m_row_ids_CONS.raw(), v.get_block_size(), n);\n                        cudaCheckError();\n                    }\n                } // Loop over parts to consolidate\n\n                cudaCheckError();\n                hipEventDestroy(event);\n                cudaCheckError();\n                amgx::memory::hipHostFree(child_data);\n            } // If root partition\n        } //agg\n        else if (this->m_is_fine_level_glued) // cla\n        {\n            value_type *child_data = NULL;\n\n            if (!this->m_is_fine_level_root_partition)\n            {\n                int dummy;\n                void *data_to_send = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                this->getFineLevelComms()->send_raw_data( data_to_send,\n                        n * v.get_block_size()*sizeof(value_type),\n                        this->m_my_fine_level_destination_part,\n                        20001 + this->fine_level_id());\n                //v.resize(0); // just in case something resized it betwen iterations\n                cudaCheckError();\n            }\n            else\n            {\n                hipHostAlloc( (void **) &child_data, this->halo_offsets[this->neighbors.size()]*v.get_block_size()*sizeof(value_type), hipHostMallocMapped);\n                value_type *child_data_hd;\n                hipHostGetDevicePointer(&child_data_hd, child_data, 0);\n                // roots copy their data\n                int dummy;\n                void *my_data = this->getHostPointerForData((void *)data_pinned, n * v.get_block_size() * sizeof(value_type), &dummy);\n                memcpy ( &child_data[0], data_pinned, n * v.get_block_size()*sizeof(value_type));\n\n                // Loop over parts to consolidate\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_raw_data( &child_data[this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()]],\n                                sizeof(value_type) * (this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part]),\n                                current_part,\n                                20001 + current_part );\n                    }\n                }\n\n                // usual path\n                // Upload on host\n                hipMemcpy(v.raw(), (value_type *)child_data, v.size()* sizeof(value_type), hipMemcpyDefault);\n                cudaCheckError();\n            } // If root partition\n\n            // Permute based on renumbering vector\n            transformVector(v);\n            cudaCheckError();\n            // Exchange halos\n            int tag = 0;\n            this->exchange_halo(v, tag);\n            cudaCheckError();\n            v.set_unconsolidated_size(n);\n\n            // free host\n            if (child_data) { amgx::memory::hipHostFree(child_data); }\n\n            cudaCheckError();\n        } //cla\n    } // If cudaIpcAvailable\n\n    if (!this->m_is_fine_level_glued) // not needed for classcical\n    {\n        if (this->m_is_fine_level_root_partition)\n        {\n            v.swap(temp);\n            int tag = 0;\n            // Root partitions do the exchange\n            this->exchange_halo(v, tag);\n        }\n\n        v.set_unconsolidated_size(n * v.get_block_size());\n        v.set_transformed();\n    }\n\n    /* free memory (if needed) */\n    if (data_alloc) { amgx::memory::hipFreeAsync(data_hd); }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::transformVector(VVector_v &v)\n{\n    if (this->neighbors.size() == 0) { return; }\n    else if (this->renumbering.size() == 0)\n    {\n        v.resize(this->halo_offsets[this->neighbors.size()]*v.get_block_size());\n        return;\n    }\n\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    if (v.size() < this->halo_offsets[this->neighbors.size()]*v.get_block_size())\n    {\n        VVector_v temp(this->halo_offsets[this->neighbors.size()]*v.get_block_size(), types::util<value_type>::get_zero());\n        temp.set_block_dimx(v.get_block_dimx());\n        temp.set_block_dimy(v.get_block_dimy());\n\n        if (v.size() < this->halo_offsets[0]*this->A->get_block_dimx())\n        {\n            FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        //reorder based on row permutation\n        int size = this->halo_offsets[0];\n        int num_blocks = std::min(4096, (size + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n        cudaCheckError();\n        v.swap(temp);\n    }\n    else\n    {\n        VVector_v temp(this->halo_offsets[0]*v.get_block_size());\n        int size = this->halo_offsets[0];\n        int num_blocks = std::min(4096, (size + 511) / 512);\n        reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n        cudaCheckError();\n        amgx::thrust::copy(temp.begin(), temp.end(), v.begin());\n    }\n\n    cudaCheckError();\n    v.set_transformed();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v)\n{\n    if (this->neighbors.size() == 0 || this->renumbering.size() == 0) { return; }\n\n    if (v.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    VVector_v temp(this->halo_offsets[0]*this->A->get_block_dimx());\n\n    if (v.size() < this->halo_offsets[0]*v.get_block_size())\n    {\n        FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    //reorder based on row permutation\n    int size = this->halo_offsets[0];\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    inverse_reorder_vector_values <<< num_blocks, 512>>>(temp.raw(), v.raw(), this->renumbering.raw(), v.get_block_size(), size);\n    //reorder_vector_values<<<num_blocks, 512>>>(temp.raw(), v.raw(), this->inverse_renumbering.raw(), v.get_block_size(), size);\n    cudaCheckError();\n    v.resize(this->halo_offsets[0]*this->A->get_block_dimx());\n    amgx::thrust::copy(temp.begin(), temp.end(), v.begin());\n    cudaCheckError();\n}\n\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeDestinationPartitions(INDEX_TYPE upper_threshold, float avg_size, const int num_parts, int &new_num_parts, bool &wantNeighbors)\n{\n    m_destination_partitions.resize(num_parts);\n    std::vector<int> dp(num_parts);\n\n    if (avg_size < 1.f) { avg_size = 1.f; } // avoid floating point exception\n\n    int wanted_num_fine_parts_to_consolidate = ( upper_threshold + (int) avg_size - 1) / ( (int) avg_size );\n    new_num_parts = (num_parts + wanted_num_fine_parts_to_consolidate - 1) / wanted_num_fine_parts_to_consolidate;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        dp[i] = i % new_num_parts;\n    }\n\n    // example wantNeighbors = true -> destination_part = [0 0 0 0 4 4 4 4 8 8 8 8]\n    // example wantNeighbors = false -> destination_part = [0 1 2 3 0 1 2 3 0 1 2 3]\n    if (wantNeighbors)\n    {\n        std::sort (dp.begin(), dp.end());\n        m_destination_partitions[0] = 0;\n\n        for (int i = 1; i < num_parts; i++)\n        {\n            if (dp[i - 1] < dp[i])\n            {\n                m_destination_partitions[i]  = i;\n            }\n            else\n            {\n                m_destination_partitions[i] = m_destination_partitions[i - 1];\n            }\n        }\n    }\n\n    m_my_destination_part =  m_destination_partitions[global_id()];\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::computeDestinationPartitionsWithCons(int my_id, int num_parts, IVector_h &destination_part, DistributedComms<TConfig> *comms)\n{\n    int device_id = this->A->getResources()->getDevice(0);\n    std::string my_hostname_tmp;\n    comms->get_hostname(my_hostname_tmp);\n    // Append PCI-E ID to string\n    hipDeviceProp_t dev_prop;\n    hipGetDeviceProperties(&dev_prop, device_id);\n    std::stringstream s;\n    s << my_hostname_tmp << \"_\" << dev_prop.pciBusID << \"_\" << dev_prop.pciDeviceID;\n    std::string my_hostname(s.str());\n    std::vector<std::string> hostnames;\n    comms->exchange_hostnames(my_hostname, hostnames, num_parts);\n    std::vector<std::string>::iterator low = std::find( hostnames.begin(), hostnames.end(), my_hostname );\n    int my_destination_part = low - hostnames.begin();\n    // Do a gather into destination_part\n    comms->all_gather(my_destination_part, destination_part, num_parts);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVector(VVector_v &v_in, const void *data, int n, int block_dimy)\n{\n    if (this->isFineLevelConsolidated() || this->isFineLevelGlued())\n    {\n        revertAndDownloadVectorWithCons(v_in, data, n, block_dimy);\n    }\n    else\n    {\n        if ( n == 0 )\n        {\n            FatalError(\"Cannot download if size = 0\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (data == NULL )\n        {\n            FatalError(\"Cannot download to a NULL pointer\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (v_in.size() == 0 )\n        {\n            FatalError(\"Cannot download an empty vector\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        VVector_v v_out;\n        revertVector(v_in, v_out);\n        hipMemcpy((value_type *)data, v_out.raw(), n * block_dimy * sizeof(value_type), hipMemcpyDefault);\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v_in, VVector_v &v_out)\n{\n    if (!this->isFineLevelGlued() && this->neighbors.size() == 0 || this->renumbering.size() == 0) { return;}\n\n    if (v_in.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    if (v_in.size() < this->halo_offsets[0]*v_in.get_block_size())\n    {\n        FatalError(\"Unknown size of input vector - smaller than the number of rows owned by this partition\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int size = this->halo_offsets[0];\n\n    if (v_out.size() != size * this->A->get_block_dimx())\n    {\n        v_out.resize(size * this->A->get_block_dimx());\n    }\n\n    //reorder based on row permutation\n    int num_blocks = std::min(4096, (size + 511) / 512);\n    inverse_reorder_vector_values <<< num_blocks, 512>>>(v_out.raw(), v_in.raw(), this->renumbering.raw(), v_in.get_block_size(), size);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVectorWithCons(VVector_v &v_in, const void *data_pinned, int n, int block_dimy)\n{\n    if (v_in.get_block_size() != this->A->get_block_dimx()) { printf(\"Blocksize mismatch!\\n\"); }\n\n    void *root_v_ptr = NULL;\n    int size = v_in.get_unconsolidated_size();\n    int num_rows = size / v_in.get_block_size();\n\n    if (this->m_is_fine_level_root_partition)\n    {\n        root_v_ptr = (void *) v_in.raw();\n    }\n\n    VVector_v temp;\n    temp.set_block_dimx(v_in.get_block_dimx());\n    temp.set_block_dimy(v_in.get_block_dimy());\n    temp.resize(size);\n    bool useCudaIpc = this->m_use_cuda_ipc_consolidation;\n\n    if (useCudaIpc)\n    {\n        // Do IPC\n        this->ipcExchangePtr(root_v_ptr, this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        // Children partition waits for parent to be done updating vector\n        this->ipcWaitForRoot(this->m_is_fine_level_root_partition, this->m_num_fine_level_parts_to_consolidate, this->m_fine_level_parts_to_consolidate, this->m_my_fine_level_destination_part, this->fine_level_id(), this->getFineLevelComms());\n        cudaCheckError();\n        //reorder based on row permutation\n        int num_blocks = std::min(4096, (num_rows + 511) / 512);\n        inverse_reorder_vector_values <<< num_blocks, 512>>>( temp.raw(), (value_type *) root_v_ptr, this->m_row_ids_CONS.raw(), v_in.get_block_size(), num_rows);\n        cudaCheckError();\n\n        if (!this->m_is_fine_level_root_partition)\n        {\n            hipIpcCloseMemHandle(root_v_ptr);\n        }\n    }\n    else\n    {\n        if (this->m_is_fine_level_consolidated) // aggregation\n        {\n            if (this->m_is_fine_level_root_partition)\n            {\n                IVector_h child_n(this->m_num_fine_level_parts_to_consolidate);\n                int max_n = 0;\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->recv_vector(child_n, current_part, 30000 + current_part, i, 1);\n                    }\n                    else\n                    {\n                        child_n[i] = num_rows;\n                    }\n\n                    if (child_n[i] > max_n) { max_n = child_n[i]; }\n                }\n\n                // Resize temp vector\n                VVector_v child_temp;;\n                child_temp.resize(max_n * v_in.get_block_size());\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n                    // Pack the vector to be sent\n                    int num_blocks = std::min(4096, (child_n[i] + 511) / 512);\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        inverse_reorder_vector_values <<< num_blocks, 512>>>( child_temp.raw(), (value_type *) root_v_ptr, this->m_child_row_ids[i].raw(), v_in.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                        this->getFineLevelComms()->send_vector(child_temp, current_part, 30001 + current_part, 0, child_n[i]*v_in.get_block_size());\n                    }\n                    else\n                    {\n                        inverse_reorder_vector_values <<< num_blocks, 512>>>( temp.raw(), (value_type *) root_v_ptr, this->m_row_ids_CONS.raw(), v_in.get_block_size(), child_n[i]);\n                        cudaCheckError();\n                    }\n                }\n\n                cudaCheckError();\n            }\n            else\n            {\n                IVector_h size(1);\n                size[0] = num_rows;\n                this->getFineLevelComms()->send_vector(size, this->m_my_fine_level_destination_part, 30000 + this->fine_level_id());\n                this->getFineLevelComms()->recv_vector(temp, this->m_my_fine_level_destination_part, 30001 + this->fine_level_id());\n            }\n        }\n        else if (this->m_is_fine_level_glued) // classical\n        {\n            if (this->m_is_fine_level_root_partition)\n            {\n                temp.resize(v_in.size());\n                revertVector(v_in, temp);\n                cudaCheckError();\n\n                for (int i = 0; i < this->m_num_fine_level_parts_to_consolidate; i++)\n                {\n                    int current_part = this->m_fine_level_parts_to_consolidate[i];\n\n                    if (current_part != this->fine_level_id())\n                    {\n                        this->getFineLevelComms()->send_vector( temp,\n                                                                current_part,\n                                                                current_part + 30001,\n                                                                this->getConsolidationArrayOffsets()[current_part] - this->getConsolidationArrayOffsets()[this->fine_level_id()],\n                                                                this->getConsolidationArrayOffsets()[current_part + 1] - this->getConsolidationArrayOffsets()[current_part] );\n                        cudaCheckError();\n                    }\n                }\n            }\n            else\n            {\n                this->getFineLevelComms()->recv_vector(temp, this->m_my_fine_level_destination_part, 30001 + this->fine_level_id());\n                cudaCheckError();\n            }\n\n            temp.resize(this->getConsolidationArrayOffsets()[this->fine_level_id() + 1] - this->getConsolidationArrayOffsets()[this->fine_level_id()]);\n            cudaCheckError();\n        }\n    }\n\n    // Copy on host\n    hipMemcpy((value_type *)data_pinned, temp.raw(), temp.size() * sizeof(value_type), hipMemcpyDefault);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformVector(VVector_v &v)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::transformAndUploadVectorWithCons(VVector_v &v, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVector(VVector_v &v, const void *data, int n, int block_dim)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v_in, VVector_v &v_out)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertVector(VVector_v &v)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::revertAndDownloadVectorWithCons(VVector_v &v_in, const void *data, int n, int block_dim)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::reorder_matrix_owned()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::obtain_shift_l2g_reordering(index_type n, I64Vector_h &l2g, IVector_h &p, IVector_h &q)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::unpack_partition(index_type *Bp, index_type *Bc, mat_value_type *Bv)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generatePoisson7pt(int nx, int ny, int nz, int P, int Q, int R)\n{\n    FatalError(\"GeneratePoisson7pt only implemented on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate <typename t_colIndex>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::loadDistributedMatrix(\n    int num_rows, int num_nonzeros, const int block_dimx, const int block_dimy, const int *row_offsets, \n    const t_colIndex *col_indices, const mat_value_type *values, int num_ranks, int num_rows_global, const void *diag, const MatrixDistribution &dist)\n{\n    FatalError(\"loadDistributedMatrix only implemented on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingB2Lmaps()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createOneRingHaloRows()\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsNoCons(int n, int nnz, const mat_value_type *data, const mat_value_type *diag)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::replaceMatrixCoefficientsWithCons(int n, int nnz, const mat_value_type *data, const mat_value_type *diag)\n{\n    FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DistributedManager<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::createRenumbering(IVector &renumbering)\n{\n    if (this->neighbors.size() > 0)\n    {\n        FatalError(\"Distributed solve only supported on devices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::remove_boundary(IVector_h &flagArray, IVector_h &B2L_map, int size)\n{\n    for (int i = 0; i < size; i++)\n    {\n        flagArray[B2L_map[i]] = 0;\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::get_unassigned(IVector_h &flagArray, IVector_h &B2L_map, IVector_h &partition_flags, int size, int fa_size/*, int rank*/)\n{\n    for (int i = 0; i < size; i++)\n    {\n        if (B2L_map[i] < fa_size)\n        {\n            if (flagArray[B2L_map[i]] == 0)\n            {\n                flagArray[B2L_map[i]] = 1;\n                partition_flags[i] = 1;\n            }\n        }\n    }\n}\n\ntemplate < class TConfig >\nvoid DistributedManagerBase<TConfig>::set_unassigned(IVector_h &partition_flags, IVector_h &partition_renum, IVector_h &B2L_map, IVector_h &renumbering, int size, int max_element, int renum_size/*, int rank*/)\n{\n    for (int i = 0; i < size; i++)\n    {\n        if (B2L_map[i] < renum_size)\n        {\n            if (partition_flags[i] == 1)\n            {\n                renumbering[B2L_map[i]] = max_element + partition_renum[i];\n            }\n\n            B2L_map[i] = renumbering[B2L_map[i]];\n        }\n    }\n}\n\n/* print manager for target rank to a file or stdout */\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::print(char *f, char *s, int trank)\n{\n    DistributedManagerBase<TConfig> *m = this;\n    int rank = 0;\n    int level = 0;\n    char filename[1024];\n    FILE *fid = NULL;\n    int i, j, k, t1, t2;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n\n    //check target rank\n    if (rank == trank)\n    {\n        //check whether to output to stdout or a file\n        if (f == NULL)\n        {\n            fid = stdout;\n        }\n        else\n        {\n            level = m->A->amg_level_index;\n#ifdef _WIN32\n            _snprintf_s(filename, 1024, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#else\n            snprintf(filename, 1024, \"%s_r%d_l%d.mtx\", f, rank, level);\n#endif\n            fid = fopen(filename, \"w\");\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n        fprintf(fid, \"%s\\n\", s);\n        //--- communication info ---\n        //compare neighbors\n        t1 = m->neighbors.size();\n        fprintf(fid, \"neighbors %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->neighbors[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare B2L_rings\n        t1 = B2L_rings.size();\n        fprintf(fid, \"B2L_rings %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->B2L_rings[i].size();\n            fprintf(fid, \"B2L_rings-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->B2L_rings[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //compare B2L_maps\n        t1 = B2L_maps.size();\n        fprintf(fid, \"B2L_maps %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->B2L_maps[i].size();\n            fprintf(fid, \"B2L_maps-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->B2L_maps[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //compare L2H_maps\n        t1 = L2H_maps.size();\n        fprintf(fid, \"L2H_maps %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            t2 = m->L2H_maps[i].size();\n            fprintf(fid, \"L2H_maps-%d [%d]\\n\", i, t2);\n\n            for (j = 0; j < t2; j++)\n            {\n                k = m->L2H_maps[i][j];\n                fprintf(fid, \"%d\\n\", k);\n            }\n        }\n\n        //--- matrix info ---\n        fprintf(fid, \"num_rows_global=%ld\\n\", num_rows_global);\n        fprintf(fid, \"_num_rows_interior=%d\\n\", m->_num_rows_interior);\n        fprintf(fid, \"_num_rows_owned=%d\\n\", m->_num_rows_owned);\n        fprintf(fid, \"_num_rows_full=%d\\n\", m->_num_rows_full);\n        fprintf(fid, \"_num_rows_all=%d\\n\", m->_num_rows_all);\n        fprintf(fid, \"_num_nz_interior=%d\\n\", m->_num_nz_interior);\n        fprintf(fid, \"_num_nz_owned=%d\\n\", m->_num_nz_owned);\n        fprintf(fid, \"_num_nz_full=%d\\n\", m->_num_nz_full);\n        fprintf(fid, \"_num_nz_all=%d\\n\", m->_num_nz_all);\n        //compare # halo rows and halo offsets\n        fprintf(fid, \"# halo rings %d and rows %d\\n\", m->num_halo_rings(), m->num_halo_rows());\n        t1 = m->halo_offsets.size();\n        fprintf(fid, \"halo_offsets %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_offsets[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo ranges\n        t1 = m->halo_ranges.size();\n        fprintf(fid, \"halo_ranges %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_ranges[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo ranges (host)\n        t1 = m->halo_ranges_h.size();\n        fprintf(fid, \"halo_ranges_h %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo_ranges_h[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare part offsets\n        t1 = m->part_offsets.size();\n        fprintf(fid, \"part_offsets %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->part_offsets[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare part offsets (host)\n        t1 = m->part_offsets_h.size();\n        fprintf(fid, \"part_offsets_h %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->part_offsets_h[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare interior row list\n        t1 = m->interior_rows_list.size();\n        fprintf(fid, \"interior_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->interior_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare boundary row list\n        t1 = m->boundary_rows_list.size();\n        fprintf(fid, \"boundary_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->boundary_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare halo1 row list\n        t1 = m->halo1_rows_list.size();\n        fprintf(fid, \"halo1_rows_list %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->halo1_rows_list[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        fprintf(fid, \"pointers halo_rows=%p and halo_btl=%p\\n\", m->halo_rows, m->halo_btl);\n        //--- packing info ---\n        //compare local to global map\n        t1 = m->local_to_global_map.size();\n        fprintf(fid, \"local_to_global_map %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->local_to_global_map[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare renumbering\n        t1 = m->renumbering.size();\n        fprintf(fid, \"renumbering %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->renumbering[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //compare inverse renumbering\n        t1 = m->inverse_renumbering.size();\n        fprintf(fid, \"inverse_renumbering %d\\n\", t1);\n\n        for (i = 0; i < t1; i++)\n        {\n            k = m->inverse_renumbering[i];\n            fprintf(fid, \"%d\\n\", k);\n        }\n\n        //--- GPU related and miscellaneous info\n        //streams\n        fprintf(fid, \"streams i=%p, b=%p\\n\", m->get_int_stream(), m->get_bdy_stream());\n        //miscellaneous info\n        int64_t bi = m->base_index();       //inlined function\n        int np = m->get_num_partitions();   //inlined function\n        int rp = (int)m->isRootPartition(); //cast from boolean to int\n        fprintf(fid, \"gid=%d,bi=%ld,np=%d,rp=%d,ir=%d,in=%d,bn=%d\\n\", m->global_id(), bi, np, rp, m->index_range(), m->num_interior_nodes(), m->num_boundary_nodes());\n        hipDeviceSynchronize();\n        cudaCheckError();\n        hipGetLastError();\n\n        if (fid != stdout)\n        {\n            fclose(fid);\n        }\n    }\n}\n\n/* print manager for target rank to a file or stdout (for all ranks) */\ntemplate<class TConfig>\nvoid DistributedManagerBase<TConfig>::printToFile(char *f, char *s)\n{\n    DistributedManagerBase<TConfig> *m = this;\n    int rank = 0;\n#ifdef AMGX_WITH_MPI\n    MPI_Comm_rank(MPI_COMM_WORLD, &rank);\n#endif\n    //notice that print will be called with different (target) rank on different ranks/processes\n    m->print(f, s, rank);\n}\n\n/* compare two managers */\ntemplate<class TConfig>\nint DistributedManagerBase<TConfig>::compare(DistributedManagerBase<TConfig> *m2)\n{\n    DistributedManagerBase<TConfig> *m1 = this;\n    int i, j, t1, t2;\n    //compare neighbors\n    t1 = m1->neighbors.size();\n    t2 = m2->neighbors.size();\n\n    if (t1 != t2)\n    {\n        return 1;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->neighbors[i] != m2->neighbors[i])\n        {\n            return 2;\n        }\n    }\n\n    //compare B2L_rings\n    for (i = 0; i < (m1->neighbors.size()); i++)\n    {\n        t1 = m1->B2L_rings[i].size();\n        t2 = m2->B2L_rings[i].size();\n\n        if (t1 != t2)\n        {\n            return 3;\n        }\n\n        for (j = 0; j < t1; j++)\n        {\n            if (m1->B2L_rings[i][j] != m2->B2L_rings[i][j])\n            {\n                return 4;\n            }\n        }\n    }\n\n    //compare B2L_maps\n    t1 = m1->B2L_maps.size();\n    t2 = m2->B2L_maps.size();\n\n    if (t1 != t2)\n    {\n        return 5;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->B2L_maps[i] != m2->B2L_maps[i])\n        {\n            return 6;\n        }\n    }\n\n    //compare L2H_maps\n    t1 = m1->L2H_maps.size();\n    t2 = m2->L2H_maps.size();\n\n    if (t1 != t2)\n    {\n        return 7;\n    }\n\n    for (i = 0; i < t1; i++)\n    {\n        if (m1->L2H_maps[i] != m2->L2H_maps[i])\n        {\n            return 8;\n        }\n    }\n\n    return 0;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistributedManager< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~DistributedManager< TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >()\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistributedManager< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::~DistributedManager< TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >()\n{\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2Lmaps(IVector_h_vector &dest_coarse_B2L_maps, IVector_h_vector &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    consB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2Lmaps(IVector_d_vector &dest_coarse_B2L_maps, IVector_d_vector &coarse_B2L_maps, IVector_h &fine_neigh_to_coarse_neigh, int num_coarse_neighbors, int num_fine_neighbors)\n{\n    consB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2LmapsOnRoot(int &num_consolidated_neighbors, IVector_h_vector &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, IVector_h_vector &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    consB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_ids, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, comms);\n}\n\ntemplate <class TConfig>\nvoid DistributedManagerBase<TConfig>::consolidateB2LmapsOnRoot(int &num_consolidated_neighbors, IVector_d_vector &consolidated_B2L_maps, IVector_h &consolidated_coarse_ids, IVector_d_vector &dest_coarse_B2L_maps, IVector_h &coarse_neigh_to_fine_part, IVector_h &num_bdy_per_coarse_neigh, IVector_h &fine_parts_to_consolidate, int num_fine_parts_to_consolidate, int my_id, int my_destination_part, bool is_root_partition, int num_coarse_neighbors, DistributedComms<TConfig> *comms)\n{\n    consB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_ids, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, comms);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DistributedManager<TemplateMode<CASE>::Type >;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template void DistributedManager<TemplateMode<CASE>::Type>::loadDistributedMatrix( \\\n    int, int, const int, const int, const int*, const int *col_indices, const mat_value_type*, int, int, const void*, const MatrixDistribution &dist); \\\n    template void DistributedManager<TemplateMode<CASE>::Type>::loadDistributedMatrix( \\\n    int, int, const int, const int, const int*, const int64_t *col_indices, const mat_value_type*, int, int, const void*, const MatrixDistribution &dist);\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DistributedManagerBase<TemplateMode<CASE>::Type >;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/distributed/distributed_manager.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <distributed/comms_mpi_hostbuffer_stream.h>\n#include <basic_types.h>\n#include <cutil.h>\n\nnamespace amgx\n{\n\n/***************************************\n * Source Definitions\n ***************************************/\n\ntemplate <typename value_type>\n__global__\nvoid copy_buffer(value_type *__restrict__ dest, const value_type *__restrict__ src, int size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        dest[tidx] = src[tidx];\n    }\n}\n\ntemplate <typename TConfig>\nstatic void error_vector_too_small(const Vector<TConfig> &v, int required_size)\n{\n    std::stringstream ss;\n    ss << \"Vector size too small: not enough space for halo elements.\" << std::endl;\n    ss << \"Vector: {tag = \" << v.tag << \", \" << \"size = \" << v.size() << \"}\" << std::endl;\n    ss << \"Required size: \" << required_size << std::endl;\n    FatalError(ss.str(), AMGX_ERR_INTERNAL);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::printString(const std::string &str)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    int part = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    MPI_Comm_rank( mpi_comm, &part);\n\n    for (int i = 0; i < total; i++)\n    {\n        MPI_Barrier(mpi_comm);\n\n        if (i == part)\n        {\n            std::cout << \"my_id=\" << i << std::endl;\n            std::cout << str << std::endl;\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::do_reduction(T &arrays, T2 &data, const Operator<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    int numRanks = 0;\n    MPI_Comm_size( mpi_comm, &numRanks );\n    typedef typename T2::value_type value_type;\n    std::vector<value_type> linear_buffer(numRanks * data.size());\n    // Allgather() copy data from all ranks into a 1D array (linear_buffer)\n    MPI_Allgather(&data[0], sizeof(value_type)*data.size(), MPI_BYTE,\n                  &linear_buffer[0], sizeof(value_type)*data.size(), MPI_BYTE, mpi_comm);\n    // then it is copied from 1D array to the 2D array (called arrays)\n    arrays.resize(numRanks);\n\n    // first copy data from other ranks\n    for (int i = 0; i < numRanks; i++)\n    {\n        if (i != m.getManager()->global_id())\n        {\n            arrays[i].assign(linear_buffer.begin() + i * data.size(), linear_buffer.begin() + (i + 1)*data.size());\n        }\n    }\n\n    // then copy data from its won rank\n    arrays[m.getManager()->global_id()] = data;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate<typename T>\n__global__\nvoid print_data(T **data, int size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    T *loc = data[0];\n    printf(\"data = %d\\n\", loc[tid]);\n}\n\n\ntemplate<typename T>\n__global__\nvoid print_data_f(T **data, int size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    T *loc = data[0];\n    printf(\"data = %f\\n\", loc[tid]);\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_setup(T &b, const Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    int bsize = b.get_block_size();\n    int num_cols = b.get_num_cols();\n\n    if (bsize != 1 && num_cols != 1)\n        FatalError(\"Error: vector cannot have block size and subspace size.\",\n                   AMGX_ERR_INTERNAL);\n\n    // set num neighbors = size of b2l_rings\n    // need to do this because comms might have more neighbors than our matrix knows about\n    set_neighbors(m.manager->B2L_rings.size());\n\n    if (b.in_transfer & SENDING)\n    {\n        b.in_transfer = IDLE;\n    }\n\n    b.requests.resize(2 * neighbors); //first part is sends second is receives\n    b.statuses.resize(2 * neighbors);\n\n    for (int i = 0; i < 2 * neighbors; i++)\n    {\n        b.requests[i] = MPI_REQUEST_NULL;\n    }\n\n    typedef typename T::value_type vtyp;\n\n    if (b.linear_buffers_size < neighbors)\n    {\n        if (b.linear_buffers_size != 0) { amgx::memory::cudaFreeHost(b.linear_buffers); }\n        cudaCheckError();\n\n        amgx::memory::cudaMallocHost((void **) & (b.linear_buffers), neighbors * sizeof(vtyp *));\n        cudaCheckError();\n        b.linear_buffers_size = neighbors;\n        b.linear_buffers_ptrs.resize(neighbors);\n    }\n\n    cudaCheckError();\n\n    int send_size = 0;\n    for (int i = 0; i < neighbors; i++)\n    {\n        send_size += m.manager->B2L_rings[i][num_rings] * bsize * num_cols;\n    }\n\n    int total_size = send_size + (m.manager->halo_offsets[num_rings * neighbors] - m.manager->halo_offsets[0]) * bsize * num_cols;\n\n    b.buffer_size = send_size;\n\n    if (b.buffer == NULL)\n    {\n        b.buffer = new T(total_size);\n    }\n    else if (total_size > b.buffer->size())\n    {\n        b.buffer->resize(total_size);\n\n        // It is more efficient to synchronise only when linear buffers change\n        cudaStreamSynchronize(0);\n        cudaCheckError();\n    }\n\n    int offset = 0;\n    for (int i = 0; i < neighbors; i++)\n    {\n        b.linear_buffers[i] = b.buffer->raw() + offset;\n        offset += m.manager->B2L_rings[i][num_rings] * bsize * num_cols;\n    }\n\n    // Copy to device\n    {\n        cudaMemcpy(amgx::thrust::raw_pointer_cast(&b.linear_buffers_ptrs[0]), &(b.linear_buffers[0]), neighbors * sizeof(vtyp *), cudaMemcpyDefault);\n        cudaCheckError();\n    }\n\n    if (total_size != 0)\n    {\n        if (b.explicit_host_buffer == NULL)\n        {\n            b.host_buffer.resize(1);\n            cudaEventCreateWithFlags(&b.mpi_event, cudaEventDisableTiming);\n            amgx::memory::cudaMallocHost((void **)&b.explicit_host_buffer, total_size * sizeof(vtyp));\n            cudaCheckError();\n        }\n        else if (total_size > b.explicit_buffer_size)\n        {\n            amgx::memory::cudaFreeHost(b.explicit_host_buffer);\n            cudaCheckError();\n            amgx::memory::cudaMallocHost((void **)&b.explicit_host_buffer, total_size * sizeof(vtyp));\n        }\n\n        cudaCheckError();\n        b.explicit_buffer_size = total_size;\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_setup_L2H(T &b, Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n    b.requests.resize(2 * num_neighbors);\n    b.statuses.resize(2 * num_neighbors);\n\n    for (int i = 0; i < 2 * num_neighbors; i++)\n    {\n        b.requests[i] = MPI_REQUEST_NULL;\n    }\n\n    // compute new sizes\n    int send_size = (m.manager->halo_offsets[num_rings * num_neighbors] - m.manager->halo_offsets[0]) * bsize;\n    int recv_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        recv_size += m.manager->B2L_rings[i][num_rings] * bsize;\n    }\n\n    int size = send_size + recv_size;\n    // resize device and host buffers\n    b.buffer_size = size;\n\n    if (b.buffer == NULL)\n    {\n        b.buffer = new T(size);\n    }\n    else if (size > b.buffer->size())\n    {\n        b.buffer->resize(size);\n        cudaStreamSynchronize(0);\n        cudaCheckError();\n    }\n\n    b.host_buffer.resize(send_size + recv_size);\n    typedef typename T::value_type vtyp;\n\n    if (size != 0)\n    {\n        if (b.explicit_host_buffer == NULL)\n        {\n            b.host_buffer.resize(1);\n            cudaEventCreateWithFlags(&b.mpi_event, cudaEventDisableTiming);\n            amgx::memory::cudaMallocHost((void **)&b.explicit_host_buffer, size * sizeof(vtyp));\n            cudaCheckError();\n        }\n        else if (size > b.explicit_buffer_size)\n        {\n            amgx::memory::cudaFreeHost(b.explicit_host_buffer);\n            cudaCheckError();\n            amgx::memory::cudaMallocHost((void **)&b.explicit_host_buffer, size * sizeof(vtyp));\n        }\n\n        cudaCheckError();\n        b.explicit_buffer_size = size;\n    }\n\n    // setup linear buffers\n    if (b.linear_buffers_size < neighbors)\n    {\n        if (b.linear_buffers_size != 0) { amgx::memory::cudaFreeHost(b.linear_buffers); }\n        cudaCheckError();\n\n        amgx::memory::cudaMallocHost((void **) & (b.linear_buffers), neighbors * sizeof(vtyp *));\n        cudaCheckError();\n        b.linear_buffers_size = neighbors;\n    }\n\n    int total = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        b.linear_buffers[i] = b.buffer->raw() + total;\n\n        for (int j = 0; j < num_rings; j++)\n        {\n            total += (m.manager->halo_offsets[j * num_neighbors + i + 1] - m.manager->halo_offsets[j * num_neighbors + i]) * bsize;\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CommsMPIHostBufferStream<T_Config>::do_vec_exchange(std::vector<Vector<TemplateConfig<TConfig::memSpace, t_vecPrec, t_matPrec, t_indPrec> > > &b, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    using VecHost = Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>>;\n    using VecType = Vector<TemplateConfig<TConfig::memSpace, t_vecPrec, t_matPrec, t_indPrec>>;\n    std::vector<VecHost> host_send(b.size());\n    std::vector<VecHost> host_recv(b.size());\n    std::vector<int> send_sizes(b.size());\n    std::vector<int> recv_sizes(b.size());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        send_sizes[i] = b[i].size();\n        MPI_Irecv(&recv_sizes[i], 1, MPI_INT, m.manager->neighbors[i], m.manager->neighbors[i], mpi_comm, &requests[neighbors + i]);\n    }\n\n    for (int i = 0; i < neighbors; i++ )\n    {\n        MPI_Isend(&send_sizes[i], 1, MPI_INT, m.manager->neighbors[i], m.manager->global_id(), mpi_comm, &requests[i]);\n    }\n\n    SynchronizerVisitor<T_Config, VecType, VecHost> syncV(Direction::DeviceToHost, b, host_send, recv_sizes, m);\n    Accept(syncV);\n    MPI_Waitall(2 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I wait for data to be sent too, as I will deallocate it upon exit\n    ReceiverVisitor<T_Config, VecType, VecHost> receiverV(b, host_recv, recv_sizes, m);\n    Accept(receiverV);\n    SenderVisitor<T_Config, VecType, VecHost> senderV(b, host_send, send_sizes, m);\n    Accept(senderV);\n    MPI_Waitall(2 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I wait for data to be sent too, as I will deallocate it upon exit\n    syncV.switch_direction();\n    syncV.set_local(receiverV.get_local());//only CUDA-aware MPI needs this\n    syncV.set_host_vec(host_recv);//only Normal MPI needs this\n    Accept(syncV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec(T &b, int destination, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    {\n        // Wait for any outstanding receive calls\n        int num_recv_requests = b.recv_requests.size();\n\n        if (num_recv_requests != 0)\n        {\n            MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n            b.recv_requests.clear();\n            b.recv_statuses.clear();\n        }\n    }\n\n    SynchSendVecVisitor<T_Config, T> sv(b, destination, tag, offset, size);\n    Accept(sv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec_async(T &b, int destination, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding receive calls\n    int num_recv_requests = b.recv_requests.size();\n\n    if (num_recv_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n        b.recv_requests.clear();\n        b.recv_statuses.clear();\n    }\n\n    MPI_Request new_req;\n    MPI_Status new_status;\n    b.send_requests.push_back(new_req);\n    b.send_statuses.push_back(new_status);\n    int num_send_requests = b.send_requests.size();\n    AsynchSendVecVisitor<T_Config, T> sv(b, destination, tag, offset, size);\n    Accept(sv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec_wait_all(T &b)\n{\n#ifdef AMGX_WITH_MPI\n    int num_send_requests = b.send_requests.size();\n    MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n    b.send_requests.clear();\n    b.send_statuses.clear();\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec(T &b, int source, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding sendcalls\n    int num_send_requests = b.send_requests.size();\n\n    if (num_send_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n        b.send_requests.clear();\n        b.send_statuses.clear();\n    }\n\n    SynchRecvVecVisitor<T_Config, T> rv(b, source, tag, offset, size);\n    Accept(rv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec_async(T &b, int source, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding sendcalls\n    int num_send_requests = b.send_requests.size();\n\n    if (num_send_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n        b.send_requests.clear();\n        b.send_statuses.clear();\n    }\n\n    MPI_Request new_req;\n    MPI_Status new_status;\n    b.recv_requests.push_back(new_req);\n    b.recv_statuses.push_back(new_status);\n    int num_recv_requests = b.recv_requests.size();\n    AsynchRecvVecVisitor<T_Config, T> rv(b, source, tag, offset, size);\n    Accept(rv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec_wait_all(T &b)\n{\n#ifdef AMGX_WITH_MPI\n    int num_recv_requests = b.recv_requests.size();\n    MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n    b.recv_requests.clear();\n    b.recv_statuses.clear();\n    CopyHostFunctor<T_Config, T> cpyf(b);\n    LightVisitor<T_Config> lvcpy(cpyf);\n    Accept(lvcpy);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_gather_L2H(T &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n    // Gather L2H\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int total = 0;\n\n        for (int j = 0; j < num_rings; j++)\n        {\n            int size = (m.manager->halo_offsets[j * num_neighbors + i + 1] - m.manager->halo_offsets[j * num_neighbors + i]) * bsize;\n\n            if (size != 0)\n            {\n                // we need to use new indices after renumbering - these are stored in L2H_maps\n                cudaMemcpyAsync(b.linear_buffers[i] + total, b.raw() + m.manager->halo_offsets[j * num_neighbors + i], size*sizeof(typename T::value_type), cudaMemcpyDefault, stream);\n                cudaCheckError();\n                total += size;\n            }\n        }\n\n        cudaCheckError();\n    }\n    cudaStreamSynchronize(stream);\n    cudaCheckError();\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_gather_L2H_v2(T &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (num_rings != 1)\n    {\n        FatalError(\"Halo exchange with ring > 1 not implemented in do_gather_L2H\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Gather L2H\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n    int total = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        total += (m.manager->halo_offsets[i + 1] - m.manager->halo_offsets[i]) * bsize;\n    }\n\n    if (total != 0)\n    {\n        cudaMemcpyAsync(b.linear_buffers[0], b.raw() + m.manager->halo_offsets[0], total*sizeof(typename T::value_type), cudaMemcpyDefault, stream);\n        cudaCheckError();\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_add_from_halo(T &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t &stream)\n{\n#ifdef AMGX_WITH_MPI\n    typedef typename T::value_type vtyp;\n    AddFromHalo1Functor<T_Config, T> ex1(b, m, num_rings, 0, stream);\n    AddFromHalo2Functor<T_Config, T> ex2(b, m, num_rings, 0, stream);\n    AddFromHalo3Functor<T_Config, T> ex3(b, m, num_rings, 0, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    ex2.get_send_size() = ex1.get_send_size();//pass relevant info from one state to another\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n\n    Accept(fsmV);\n    //pass relevant info from one state to another:\n    //\n    ex3.get_recv_size() = ex2.get_offset();\n    ex3.get_send_size() = ex2.get_send_size();\n    fsmV.next();//advance FSM\n\n    // I only wait to receive data, I can start working before all my buffers were sent\n    MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n    b.dirtybit = 0;\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo(T &b, const Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    typedef typename T::value_type value_type;\n    ExcHalo1Functor<T_Config, T> ex1(b, m, num_rings, 0);\n    ExcHalo2Functor<T_Config, T> ex2(b, m, num_rings, 0);\n    ExcHalo3Functor<T_Config, T> ex3(b, m, num_rings, 0);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    ex3.get_offset() = ex2.get_offset();//pass relevant info from one state to another\n    fsmV.next();//advance FSM\n    MPI_Waitall(2 * neighbors, &b.requests[0],  MPI_STATUSES_IGNORE); //I only wait to receive data, I can start working before all my buffers were sent\n    b.dirtybit = 0;\n    //FSM step 3:\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo_async(T &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (tag == -1) { FatalError(\"Untagged vector\\n\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    cudaEventSynchronize(event);\n    cudaCheckError();\n    ExcHalo1Functor<T_Config, T> ex1(b, m, 1, 0, tag, stream);\n    ExcHalo2AsyncFunctor<T_Config, T> ex2(b, m, 1, 0, tag);\n    ExcHalo3AsyncFunctor<T_Config, T> ex3(b, m, 1, 0, tag, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n\n    if (min_rows_latency_hiding < 0 || m.manager->num_rows_all() < min_rows_latency_hiding)\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], /*&b.statuses[0]*/ MPI_STATUSES_IGNORE); //I only wait to receive data, I can start working before all my buffers were sent\n        b.dirtybit = 0;\n        Accept(fsmV);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo_wait(T &b, const Matrix<TConfig> &m, cudaStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n    int bsize = b.get_block_size();\n\n    if (!(min_rows_latency_hiding < 0 || m.manager->num_rows_all() < min_rows_latency_hiding))\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n        b.dirtybit = 0;\n        b.in_transfer = IDLE;\n        HalloWaitCopyFunctor<T_Config, T> hcpyf(b, m, stream);\n        LightVisitor<T_Config> lvcpy(hcpyf);//Visitor branching off: no-op for GPU Direct\n        Accept(lvcpy);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_send_receive_wait(T &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (tag == -1)\n    {\n        FatalError(\"Untagged vector\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    SendRecvWait1Functor<T_Config, T> ex1(b, m, 1, 0, tag, stream);\n    SendRecvWait2Functor<T_Config, T> ex2(b, m, 1, 0, tag, stream);\n    SendRecvWait3Functor<T_Config, T> ex3(b, m, 1, 0, tag, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    cudaEventSynchronize(event);\n    cudaCheckError();\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n\n    if (neighbors > 0)\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n    }\n\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nbool CommsMPIHostBufferStream<T_Config>::do_exchange_halo_query(T &b, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (b.dirtybit == 0) { return true; }\n\n    if ((b.in_transfer & RECEIVING) == 0) { return false; }\n\n    int finished = 1;\n    MPI_Status status;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        int res = MPI_Test(&b.requests[neighbors + i], &finished, &status);\n\n        if (finished == 0  || res != MPI_SUCCESS)\n        {\n            return false;\n        }\n    }\n\n    b.dirtybit = 0;\n    b.in_transfer = SENDING;\n    HalloWaitCopyFunctor<T_Config, T> hcpyf(b, m, NULL);\n    LightVisitor<T_Config> lvcpy(hcpyf);//Visitor branching off: no-op for GPU Direct\n    Accept(lvcpy);\n    return true;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_matrix_halo(IVector_Array &row_offsets, I64Vector_Array &col_indices, MVector_Array &values, I64Vector_Array &halo_row_ids, IVector_h &neighbors_list, int global_id)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    int num_neighbors = neighbors_list.size();\n    std::vector<HIVector> local_row_offsets(num_neighbors);\n    std::vector<HI64Vector> local_col_indices(num_neighbors);\n    std::vector<HMVector> local_values(num_neighbors);\n    std::vector<HIVector> send_row_offsets(num_neighbors);\n    std::vector<HI64Vector> send_col_indices(num_neighbors);\n    std::vector<HMVector> send_values(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        send_row_offsets[i] = row_offsets[i];\n        send_col_indices[i] = col_indices[i];\n        send_values[i] = values[i];\n    }\n\n    std::vector<HI64Vector> local_row_ids(0);\n    std::vector<HI64Vector> send_row_ids(0);\n\n    if (halo_row_ids.size() != 0)\n    {\n        local_row_ids.resize(num_neighbors);\n        send_row_ids.resize(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            send_row_ids[i] = halo_row_ids[i];\n        }\n    }\n\n    // send metadata\n    std::vector<INDEX_TYPE> metadata(num_neighbors * 2); // num_rows+1, num_nz\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        metadata[i * 2 + 0] = row_offsets[i].size();\n        metadata[i * 2 + 1] = col_indices[i].size();\n        MPI_Isend(&metadata[i * 2 + 0], 2, MPI_INT, neighbors_list[i], 0, mpi_comm, &requests[i]);\n    }\n\n    // receive metadata\n    std::vector<INDEX_TYPE> metadata_recv(2);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Recv(&metadata_recv[0], 2, MPI_INT, neighbors_list[i], 0, mpi_comm, MPI_STATUSES_IGNORE);\n        local_row_offsets[i].resize(metadata_recv[0]);\n        local_col_indices[i].resize(metadata_recv[1]);\n        local_values[i].resize(metadata_recv[1]);\n\n        if (local_row_ids.size() != 0)\n        {\n            if (metadata_recv[0] - 1 > 0)\n            {\n                local_row_ids[i].resize(metadata_recv[0] - 1);    // row_ids is one smaller than row_offsets\n            }\n        }\n    }\n\n    MPI_Waitall(num_neighbors, &requests[0], MPI_STATUSES_IGNORE); // data is already received, just closing the handles\n    // receive matrix data\n    typedef typename T_Config::MatPrec mvalue;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Irecv(local_row_offsets[i].raw(), local_row_offsets[i].size(), MPI_INT, neighbors_list[i], 10 * neighbors_list[i] + 0, mpi_comm, &requests[3 * num_neighbors + i]);\n        MPI_Irecv(local_col_indices[i].raw(), local_col_indices[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 1, mpi_comm, &requests[4 * num_neighbors + i]);\n        MPI_Irecv(local_values[i].raw(), local_values[i].size()*sizeof(mvalue), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 2, mpi_comm, &requests[5 * num_neighbors + i]);\n\n        if (send_row_ids.size() != 0)\n        {\n            MPI_Irecv(local_row_ids[i].raw(), local_row_ids[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 3, mpi_comm, &requests[7 * num_neighbors + i]);\n        }\n    }\n    //Note: GPU Direct should use row_offsets[], col_indices[], values[] directly in here:\n    // send matrix: row offsets, col indices, values\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Isend(send_row_offsets[i].raw(), send_row_offsets[i].size(), MPI_INT, neighbors_list[i], 10 * global_id + 0, mpi_comm, &requests[i]);\n        MPI_Isend(send_col_indices[i].raw(), send_col_indices[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * global_id + 1, mpi_comm, &requests[num_neighbors + i]);\n        MPI_Isend(send_values[i].raw(), send_values[i].size()*sizeof(mvalue), MPI_BYTE, neighbors_list[i], 10 * global_id + 2, mpi_comm, &requests[2 * num_neighbors + i]);\n\n        if (send_row_ids.size() != 0)\n        {\n            MPI_Isend(send_row_ids[i].raw(), send_row_ids[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * global_id + 3, mpi_comm, &requests[6 * num_neighbors + i]);\n        }\n    }\n\n    if (halo_row_ids.size() != 0)\n    {\n        MPI_Waitall(8 * num_neighbors, &requests[0], MPI_STATUSES_IGNORE);    //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    }\n    else\n    {\n        MPI_Waitall(6 * num_neighbors, &requests[0], MPI_STATUSES_IGNORE);    //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    }\n\n    //Note: GPU Direct should swap here\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        row_offsets[i] = local_row_offsets[i];\n        col_indices[i] = local_col_indices[i];\n        values[i] = local_values[i];\n\n        if (halo_row_ids.size() != 0)\n        {\n            halo_row_ids[i] = local_row_ids[i];\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_matrix_halo(Matrix_Array &halo_rows, DistributedManager_Array &halo_btl, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    int bsize = m.get_block_size();\n    int rings = m.manager->B2L_rings[0].size() - 1;\n    int diag = m.hasProps(DIAG);\n    std::vector<Matrix_h> local_copy(halo_rows.size());\n    std::vector<Matrix_h> send(halo_rows.size());\n    std::vector<Manager_h> local_copy_manager(halo_rows.size());\n    std::vector<Manager_h> send_manager(halo_rows.size());\n\n    //Note: GPU Direct does *NOT* do that:\n    for (int i = 0; i < halo_rows.size(); i++)\n    {\n        send_manager[i] = halo_btl[i];\n        send[i] = halo_rows[i];\n    }\n\n    {\n        // there shouldn't be any uncompleted requests, because we don't want to rewrite them\n        int completed;\n        MPI_Testall(requests.size(), &requests[0], &completed, MPI_STATUSES_IGNORE);\n\n        if (!completed)\n        {\n            MPI_Waitall(requests.size(), &requests[0], MPI_STATUSES_IGNORE);\n        }\n    }\n\n    std::vector<INDEX_TYPE> metadata(neighbors * (rings + 1 + 5)); //ring offsets (rings+1), num_rows, num_nz, base_index, index_range\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        for (int j = 0; j <= rings; j++) { metadata[i * (rings + 1 + 5) + j] = halo_btl[i].B2L_rings[0][j]; }\n\n        metadata[i * (rings + 1 + 5) + rings + 1] = halo_rows[i].get_num_rows();\n        metadata[i * (rings + 1 + 5) + rings + 2] = halo_rows[i].get_num_nz();\n        metadata[i * (rings + 1 + 5) + rings + 3] = halo_btl[i].base_index();\n        metadata[i * (rings + 1 + 5) + rings + 4] = halo_btl[i].index_range();\n        metadata[i * (rings + 1 + 5) + rings + 5] = halo_btl[i].L2H_maps[0].size();\n        MPI_Isend(&metadata[i * (rings + 1 + 5)], rings + 6, MPI_INT, m.manager->neighbors[i], 0, mpi_comm, &requests[i]);\n    }\n\n    std::vector<INDEX_TYPE> metadata_recv(rings + 1 + 5);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Recv(&metadata_recv[0], rings + 6, MPI_INT, m.manager->neighbors[i], 0, mpi_comm, /*&status*/MPI_STATUSES_IGNORE);\n        local_copy[i].addProps(CSR);\n\n        if (diag) { local_copy[i].addProps(DIAG); }\n\n        local_copy[i].resize(metadata_recv[rings + 1], metadata_recv[rings + 1], metadata_recv[rings + 2], m.get_block_dimy(), m.get_block_dimx(), 1);\n        local_copy_manager[i].set_base_index(metadata_recv[rings + 3]);\n        local_copy_manager[i].set_index_range(metadata_recv[rings + 4]);\n        local_copy_manager[i].B2L_rings.resize(1);\n        local_copy_manager[i].B2L_rings[0].resize(rings + 1);\n        local_copy_manager[i].B2L_maps.resize(1);\n        local_copy_manager[i].B2L_maps[0].resize(local_copy[i].get_num_rows());\n        local_copy_manager[i].L2H_maps.resize(1);\n        local_copy_manager[i].L2H_maps[0].resize(metadata_recv[rings + 5]);\n\n        for (int j = 0; j <= rings; j++) { local_copy_manager[i].B2L_rings[0][j] = metadata_recv[j]; }\n    }\n\n    MPI_Waitall(neighbors, &requests[0], MPI_STATUSES_IGNORE); //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    typedef typename T_Config::MatPrec mvalue;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Irecv(local_copy[i].row_offsets.raw(), local_copy[i].row_offsets.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 0, mpi_comm, &requests[5 * neighbors + 5 * i]);\n        MPI_Irecv(local_copy[i].col_indices.raw(), local_copy[i].col_indices.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 1, mpi_comm, &requests[5 * neighbors + 5 * i + 1]);\n        MPI_Irecv(local_copy_manager[i].B2L_maps[0].raw(), local_copy_manager[i].B2L_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 2, mpi_comm, &requests[5 * neighbors + 5 * i + 2]);\n        MPI_Irecv(local_copy_manager[i].L2H_maps[0].raw(), local_copy_manager[i].L2H_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 3, mpi_comm, &requests[5 * neighbors + 5 * i + 3]);\n        MPI_Irecv(local_copy[i].values.raw(), local_copy[i].values.size()*sizeof(mvalue), MPI_BYTE, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 4, mpi_comm, &requests[5 * neighbors + 5 * i + 4]);\n    }\n\n    //Note: GPU Direct uses halo_rows and halo_btl in here:\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Isend(send[i].row_offsets.raw(), send[i].row_offsets.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 0, mpi_comm, &requests[5 * i]);\n        MPI_Isend(send[i].col_indices.raw(), send[i].col_indices.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 1, mpi_comm, &requests[5 * i + 1]);\n        MPI_Isend(send_manager[i].B2L_maps[0].raw(), send_manager[i].B2L_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 2, mpi_comm, &requests[5 * i + 2]);\n        MPI_Isend(send_manager[i].L2H_maps[0].raw(), send_manager[i].L2H_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 3, mpi_comm, &requests[5 * i + 3]);\n        MPI_Isend(send[i].values.raw(), send[i].values.size()*sizeof(mvalue), MPI_BYTE, m.manager->neighbors[i], 10 * m.manager->global_id() + 4, mpi_comm, &requests[5 * i + 4]);\n    }\n\n    MPI_Waitall(2 * 5 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n\n    //Note: GPU Direct swaps here:\n    for (int i = 0; i < halo_rows.size(); i++)\n    {\n        halo_btl[i] = local_copy_manager[i];\n        halo_rows[i] = local_copy[i];\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_raw_data(const void *ptr, int size, int destination, int tag )\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Send(const_cast<void *>(ptr), size, MPI_BYTE, destination, tag, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_raw_data(void *ptr, int size, int source, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Recv(ptr, size, MPI_BYTE, source, tag, mpi_comm, MPI_STATUSES_IGNORE);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(DVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(DVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(FVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(FVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(ZVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(ZVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(CVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(CVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(IVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(IVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(BVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(BVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED); /*do_exchange_halo(b, m);*/}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(BVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_exchange_halo_async(b,m, event, tag, stream);*/}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(BVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_exchange_halo_wait(b, m, stream);*/}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(I64Vector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(I64Vector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(I64Vector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(I64Vector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(DVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(IVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(FVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(CVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(ZVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(BVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(I64Vector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(DVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(IVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(FVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(CVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(ZVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(BVector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    FatalError(\"MPI Comms send-receive with streams is not implemented\", AMGX_ERR_NOT_IMPLEMENTED);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(I64Vector &b, const Matrix<TConfig> &m, cudaEvent_t event, int tag, cudaStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings, cudaStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings, cudaStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings, cudaStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings, cudaStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings, cudaStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(DVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(FVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(CVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(ZVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(IVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(DVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(FVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(CVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(ZVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(IVector &b, const Matrix<TConfig> &m, int num_rings, cudaStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\n\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(DVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(FVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(CVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(ZVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(IVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(BVector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return false;}\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(I64Vector &b, const Matrix<TConfig> &m, cudaEvent_t event) {      return false;}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HDVector_Array &a, HDVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HFVector_Array &a, HFVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HCVector_Array &a, HCVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HZVector_Array &a, HZVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HIVector_Array &a, HIVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HBVector_Array &a, HBVector &b, const Operator<TConfig> &m, int tag) {   FatalError(\"MPI Comms boolean reduction not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_reduction(a,b,m);*/};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HI64Vector_Array &a, HI64Vector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HDVector &a, HDVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_DOUBLE, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HFVector &a, HFVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_FLOAT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HCVector &a, HCVector &b, const Matrix<TConfig> &m, int tag)\n{\n    FatalError(\"MPI sum reduce is now supported with complex types\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HZVector &a, HZVector &b, const Matrix<TConfig> &m, int tag)\n{\n    FatalError(\"MPI sum reduce is now supported with complex types\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HIVector &a, HIVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_INT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HI64Vector &a, HI64Vector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_LONG_LONG_INT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(DVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(FVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(CVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(ZVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(IVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(BVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DIVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HIVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DDVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HDVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DFVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HFVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DCVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HCVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DZVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HZVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DIVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HIVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DDVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HDVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DFVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HFVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DCVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HCVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DZVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HZVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DIVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HIVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DDVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HDVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DFVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HFVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DCVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HCVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DZVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HZVector &a) { send_vec_wait_all(a);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DIVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HIVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DDVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HDVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DFVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HFVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DCVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HCVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DZVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HZVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DIVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HIVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DDVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HDVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DFVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HFVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DCVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HCVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DZVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HZVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DIVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HIVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DDVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HDVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DFVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HFVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DCVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HCVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DZVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HZVector &a) { recv_vec_wait_all(a);};\n\ntemplate <class T_Config>\nint CommsMPIHostBufferStream<T_Config>::get_num_partitions()\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    return total;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nint CommsMPIHostBufferStream<T_Config>::get_global_id()\n{\n#ifdef AMGX_WITH_MPI\n    int rank = 0;\n    MPI_Comm_rank( mpi_comm, &rank);\n    return rank;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::barrier()\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Barrier(mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n#define MAX_HOSTNAME_LENGTH 256\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::get_hostname(std::string &my_hostname)\n{\n#ifdef AMGX_WITH_MPI\n    my_hostname.resize(MAX_HOSTNAME_LENGTH);\n    int hostname_length;\n    MPI_Get_processor_name(&my_hostname[0], &hostname_length);\n    my_hostname.resize(hostname_length);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_hostnames(std::string &my_hostname, std::vector<std::string> &hostnames, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (num_parts != this->get_num_partitions())\n    {\n        FatalError(\"Inconsistent number of partitions\", AMGX_ERR_CORE);\n    }\n\n    hostnames.resize(num_parts);\n    std::vector<int> hostname_lengths(num_parts);\n    std::vector<int> displs(num_parts + 1);\n    int my_hostname_length = my_hostname.length();\n    MPI_Allgather( &my_hostname_length, 1, MPI_INT, hostname_lengths.data(), 1, MPI_INT, mpi_comm);\n    // Compute displacement for each hostname\n    displs[0] = 0;\n    int totlen = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        displs[i + 1] = displs[i] + hostname_lengths[i];\n        totlen      += hostname_lengths[i];\n    }\n\n    std::string all_names;\n    all_names.resize(totlen);\n    MPI_Allgatherv( &my_hostname[0], my_hostname_length, MPI_CHAR,\n                    (void *)(all_names.data()), hostname_lengths.data(), displs.data(), MPI_CHAR,\n                    mpi_comm);\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        hostnames[i] = std::string(&all_names[displs[i]], hostname_lengths[i]);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather(const int &my_data, HIVector &gathered_data, int num_parts) { all_gather_templated(my_data, gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather(const int64_t &my_data, HI64Vector &gathered_data, int num_parts) { all_gather_templated(my_data, gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HIVector &my_data, HIVector &gathered_data, int num_parts) { all_gather_v_templated(my_data[0], my_data.size(), gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_reduce_max(IndexType_h &my_data, IndexType_h &result_data)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&my_data, &result_data, 1, MPI_INT, MPI_MAX, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_templated(const T &my_data, T2 &gathered_data, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n    gathered_data.resize(num_parts);\n    MPI_Allgather(&my_data, sizeof(T), MPI_BYTE,\n                  &gathered_data[0], sizeof(T), MPI_BYTE,\n                  mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v_templated(T &my_data, int num_elems, T2 &gathered_data, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n    gathered_data.resize(num_parts * num_elems);\n    MPI_Allgather(&my_data, num_elems * sizeof(T), MPI_BYTE,\n                  &gathered_data[0], num_elems * sizeof(T), MPI_BYTE,\n                  mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HDVector& data, int num_elems, HDVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_DOUBLE, gathered_data.raw(), counts.raw(), displs.raw(), MPI_DOUBLE, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HFVector& data, int num_elems, HFVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_FLOAT, gathered_data.raw(), counts.raw(), displs.raw(), MPI_FLOAT, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HCVector& data, int num_elems, HCVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    FatalError(\"AllgatherV with complex data.\", AMGX_ERR_NOT_IMPLEMENTED);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HZVector& data, int num_elems, HZVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    FatalError(\"AllgatherV with complex data.\", AMGX_ERR_NOT_IMPLEMENTED);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HIVector& data, int num_elems, HIVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_INT, gathered_data.raw(), counts.raw(), displs.raw(), MPI_INT, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CommsMPIHostBufferStream<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <distributed/comms_mpi_hostbuffer_stream.h>\n#include <basic_types.h>\n#include <cutil.h>\n\nnamespace amgx\n{\n\n/***************************************\n * Source Definitions\n ***************************************/\n\ntemplate <typename value_type>\n__global__\nvoid copy_buffer(value_type *__restrict__ dest, const value_type *__restrict__ src, int size)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        dest[tidx] = src[tidx];\n    }\n}\n\ntemplate <typename TConfig>\nstatic void error_vector_too_small(const Vector<TConfig> &v, int required_size)\n{\n    std::stringstream ss;\n    ss << \"Vector size too small: not enough space for halo elements.\" << std::endl;\n    ss << \"Vector: {tag = \" << v.tag << \", \" << \"size = \" << v.size() << \"}\" << std::endl;\n    ss << \"Required size: \" << required_size << std::endl;\n    FatalError(ss.str(), AMGX_ERR_INTERNAL);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::printString(const std::string &str)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    int part = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    MPI_Comm_rank( mpi_comm, &part);\n\n    for (int i = 0; i < total; i++)\n    {\n        MPI_Barrier(mpi_comm);\n\n        if (i == part)\n        {\n            std::cout << \"my_id=\" << i << std::endl;\n            std::cout << str << std::endl;\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::do_reduction(T &arrays, T2 &data, const Operator<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    int numRanks = 0;\n    MPI_Comm_size( mpi_comm, &numRanks );\n    typedef typename T2::value_type value_type;\n    std::vector<value_type> linear_buffer(numRanks * data.size());\n    // Allgather() copy data from all ranks into a 1D array (linear_buffer)\n    MPI_Allgather(&data[0], sizeof(value_type)*data.size(), MPI_BYTE,\n                  &linear_buffer[0], sizeof(value_type)*data.size(), MPI_BYTE, mpi_comm);\n    // then it is copied from 1D array to the 2D array (called arrays)\n    arrays.resize(numRanks);\n\n    // first copy data from other ranks\n    for (int i = 0; i < numRanks; i++)\n    {\n        if (i != m.getManager()->global_id())\n        {\n            arrays[i].assign(linear_buffer.begin() + i * data.size(), linear_buffer.begin() + (i + 1)*data.size());\n        }\n    }\n\n    // then copy data from its won rank\n    arrays[m.getManager()->global_id()] = data;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate<typename T>\n__global__\nvoid print_data(T **data, int size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    T *loc = data[0];\n    printf(\"data = %d\\n\", loc[tid]);\n}\n\n\ntemplate<typename T>\n__global__\nvoid print_data_f(T **data, int size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    T *loc = data[0];\n    printf(\"data = %f\\n\", loc[tid]);\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_setup(T &b, const Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    int bsize = b.get_block_size();\n    int num_cols = b.get_num_cols();\n\n    if (bsize != 1 && num_cols != 1)\n        FatalError(\"Error: vector cannot have block size and subspace size.\",\n                   AMGX_ERR_INTERNAL);\n\n    // set num neighbors = size of b2l_rings\n    // need to do this because comms might have more neighbors than our matrix knows about\n    set_neighbors(m.manager->B2L_rings.size());\n\n    if (b.in_transfer & SENDING)\n    {\n        b.in_transfer = IDLE;\n    }\n\n    b.requests.resize(2 * neighbors); //first part is sends second is receives\n    b.statuses.resize(2 * neighbors);\n\n    for (int i = 0; i < 2 * neighbors; i++)\n    {\n        b.requests[i] = MPI_REQUEST_NULL;\n    }\n\n    typedef typename T::value_type vtyp;\n\n    if (b.linear_buffers_size < neighbors)\n    {\n        if (b.linear_buffers_size != 0) { amgx::memory::hipHostFree(b.linear_buffers); }\n        cudaCheckError();\n\n        amgx::memory::hipHostMalloc((void **) & (b.linear_buffers), neighbors * sizeof(vtyp *));\n        cudaCheckError();\n        b.linear_buffers_size = neighbors;\n        b.linear_buffers_ptrs.resize(neighbors);\n    }\n\n    cudaCheckError();\n\n    int send_size = 0;\n    for (int i = 0; i < neighbors; i++)\n    {\n        send_size += m.manager->B2L_rings[i][num_rings] * bsize * num_cols;\n    }\n\n    int total_size = send_size + (m.manager->halo_offsets[num_rings * neighbors] - m.manager->halo_offsets[0]) * bsize * num_cols;\n\n    b.buffer_size = send_size;\n\n    if (b.buffer == NULL)\n    {\n        b.buffer = new T(total_size);\n    }\n    else if (total_size > b.buffer->size())\n    {\n        b.buffer->resize(total_size);\n\n        // It is more efficient to synchronise only when linear buffers change\n        hipStreamSynchronize(0);\n        cudaCheckError();\n    }\n\n    int offset = 0;\n    for (int i = 0; i < neighbors; i++)\n    {\n        b.linear_buffers[i] = b.buffer->raw() + offset;\n        offset += m.manager->B2L_rings[i][num_rings] * bsize * num_cols;\n    }\n\n    // Copy to device\n    {\n        hipMemcpy(amgx::thrust::raw_pointer_cast(&b.linear_buffers_ptrs[0]), &(b.linear_buffers[0]), neighbors * sizeof(vtyp *), hipMemcpyDefault);\n        cudaCheckError();\n    }\n\n    if (total_size != 0)\n    {\n        if (b.explicit_host_buffer == NULL)\n        {\n            b.host_buffer.resize(1);\n            hipEventCreateWithFlags(&b.mpi_event, hipEventDisableTiming);\n            amgx::memory::hipHostMalloc((void **)&b.explicit_host_buffer, total_size * sizeof(vtyp));\n            cudaCheckError();\n        }\n        else if (total_size > b.explicit_buffer_size)\n        {\n            amgx::memory::hipHostFree(b.explicit_host_buffer);\n            cudaCheckError();\n            amgx::memory::hipHostMalloc((void **)&b.explicit_host_buffer, total_size * sizeof(vtyp));\n        }\n\n        cudaCheckError();\n        b.explicit_buffer_size = total_size;\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_setup_L2H(T &b, Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n    b.requests.resize(2 * num_neighbors);\n    b.statuses.resize(2 * num_neighbors);\n\n    for (int i = 0; i < 2 * num_neighbors; i++)\n    {\n        b.requests[i] = MPI_REQUEST_NULL;\n    }\n\n    // compute new sizes\n    int send_size = (m.manager->halo_offsets[num_rings * num_neighbors] - m.manager->halo_offsets[0]) * bsize;\n    int recv_size = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        recv_size += m.manager->B2L_rings[i][num_rings] * bsize;\n    }\n\n    int size = send_size + recv_size;\n    // resize device and host buffers\n    b.buffer_size = size;\n\n    if (b.buffer == NULL)\n    {\n        b.buffer = new T(size);\n    }\n    else if (size > b.buffer->size())\n    {\n        b.buffer->resize(size);\n        hipStreamSynchronize(0);\n        cudaCheckError();\n    }\n\n    b.host_buffer.resize(send_size + recv_size);\n    typedef typename T::value_type vtyp;\n\n    if (size != 0)\n    {\n        if (b.explicit_host_buffer == NULL)\n        {\n            b.host_buffer.resize(1);\n            hipEventCreateWithFlags(&b.mpi_event, hipEventDisableTiming);\n            amgx::memory::hipHostMalloc((void **)&b.explicit_host_buffer, size * sizeof(vtyp));\n            cudaCheckError();\n        }\n        else if (size > b.explicit_buffer_size)\n        {\n            amgx::memory::hipHostFree(b.explicit_host_buffer);\n            cudaCheckError();\n            amgx::memory::hipHostMalloc((void **)&b.explicit_host_buffer, size * sizeof(vtyp));\n        }\n\n        cudaCheckError();\n        b.explicit_buffer_size = size;\n    }\n\n    // setup linear buffers\n    if (b.linear_buffers_size < neighbors)\n    {\n        if (b.linear_buffers_size != 0) { amgx::memory::hipHostFree(b.linear_buffers); }\n        cudaCheckError();\n\n        amgx::memory::hipHostMalloc((void **) & (b.linear_buffers), neighbors * sizeof(vtyp *));\n        cudaCheckError();\n        b.linear_buffers_size = neighbors;\n    }\n\n    int total = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        b.linear_buffers[i] = b.buffer->raw() + total;\n\n        for (int j = 0; j < num_rings; j++)\n        {\n            total += (m.manager->halo_offsets[j * num_neighbors + i + 1] - m.manager->halo_offsets[j * num_neighbors + i]) * bsize;\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CommsMPIHostBufferStream<T_Config>::do_vec_exchange(std::vector<Vector<TemplateConfig<TConfig::memSpace, t_vecPrec, t_matPrec, t_indPrec> > > &b, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    using VecHost = Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>>;\n    using VecType = Vector<TemplateConfig<TConfig::memSpace, t_vecPrec, t_matPrec, t_indPrec>>;\n    std::vector<VecHost> host_send(b.size());\n    std::vector<VecHost> host_recv(b.size());\n    std::vector<int> send_sizes(b.size());\n    std::vector<int> recv_sizes(b.size());\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        send_sizes[i] = b[i].size();\n        MPI_Irecv(&recv_sizes[i], 1, MPI_INT, m.manager->neighbors[i], m.manager->neighbors[i], mpi_comm, &requests[neighbors + i]);\n    }\n\n    for (int i = 0; i < neighbors; i++ )\n    {\n        MPI_Isend(&send_sizes[i], 1, MPI_INT, m.manager->neighbors[i], m.manager->global_id(), mpi_comm, &requests[i]);\n    }\n\n    SynchronizerVisitor<T_Config, VecType, VecHost> syncV(Direction::DeviceToHost, b, host_send, recv_sizes, m);\n    Accept(syncV);\n    MPI_Waitall(2 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I wait for data to be sent too, as I will deallocate it upon exit\n    ReceiverVisitor<T_Config, VecType, VecHost> receiverV(b, host_recv, recv_sizes, m);\n    Accept(receiverV);\n    SenderVisitor<T_Config, VecType, VecHost> senderV(b, host_send, send_sizes, m);\n    Accept(senderV);\n    MPI_Waitall(2 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I wait for data to be sent too, as I will deallocate it upon exit\n    syncV.switch_direction();\n    syncV.set_local(receiverV.get_local());//only CUDA-aware MPI needs this\n    syncV.set_host_vec(host_recv);//only Normal MPI needs this\n    Accept(syncV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec(T &b, int destination, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    {\n        // Wait for any outstanding receive calls\n        int num_recv_requests = b.recv_requests.size();\n\n        if (num_recv_requests != 0)\n        {\n            MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n            b.recv_requests.clear();\n            b.recv_statuses.clear();\n        }\n    }\n\n    SynchSendVecVisitor<T_Config, T> sv(b, destination, tag, offset, size);\n    Accept(sv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec_async(T &b, int destination, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding receive calls\n    int num_recv_requests = b.recv_requests.size();\n\n    if (num_recv_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n        b.recv_requests.clear();\n        b.recv_statuses.clear();\n    }\n\n    MPI_Request new_req;\n    MPI_Status new_status;\n    b.send_requests.push_back(new_req);\n    b.send_statuses.push_back(new_status);\n    int num_send_requests = b.send_requests.size();\n    AsynchSendVecVisitor<T_Config, T> sv(b, destination, tag, offset, size);\n    Accept(sv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::send_vec_wait_all(T &b)\n{\n#ifdef AMGX_WITH_MPI\n    int num_send_requests = b.send_requests.size();\n    MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n    b.send_requests.clear();\n    b.send_statuses.clear();\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec(T &b, int source, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding sendcalls\n    int num_send_requests = b.send_requests.size();\n\n    if (num_send_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n        b.send_requests.clear();\n        b.send_statuses.clear();\n    }\n\n    SynchRecvVecVisitor<T_Config, T> rv(b, source, tag, offset, size);\n    Accept(rv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec_async(T &b, int source, int tag, int offset, int size)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (size == -1) { size = b.size(); }\n\n    if (size == 0) { return; }\n\n    // Wait for any outstanding sendcalls\n    int num_send_requests = b.send_requests.size();\n\n    if (num_send_requests != 0)\n    {\n        // Wait for any outstanding sends, receives to finish\n        MPI_Waitall(num_send_requests, &b.send_requests[0], MPI_STATUSES_IGNORE);\n        b.send_requests.clear();\n        b.send_statuses.clear();\n    }\n\n    MPI_Request new_req;\n    MPI_Status new_status;\n    b.recv_requests.push_back(new_req);\n    b.recv_statuses.push_back(new_status);\n    int num_recv_requests = b.recv_requests.size();\n    AsynchRecvVecVisitor<T_Config, T> rv(b, source, tag, offset, size);\n    Accept(rv);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vec_wait_all(T &b)\n{\n#ifdef AMGX_WITH_MPI\n    int num_recv_requests = b.recv_requests.size();\n    MPI_Waitall(num_recv_requests, &b.recv_requests[0], MPI_STATUSES_IGNORE);\n    b.recv_requests.clear();\n    b.recv_statuses.clear();\n    CopyHostFunctor<T_Config, T> cpyf(b);\n    LightVisitor<T_Config> lvcpy(cpyf);\n    Accept(lvcpy);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_gather_L2H(T &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n    // Gather L2H\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int total = 0;\n\n        for (int j = 0; j < num_rings; j++)\n        {\n            int size = (m.manager->halo_offsets[j * num_neighbors + i + 1] - m.manager->halo_offsets[j * num_neighbors + i]) * bsize;\n\n            if (size != 0)\n            {\n                // we need to use new indices after renumbering - these are stored in L2H_maps\n                hipMemcpyAsync(b.linear_buffers[i] + total, b.raw() + m.manager->halo_offsets[j * num_neighbors + i], size*sizeof(typename T::value_type), hipMemcpyDefault, stream);\n                cudaCheckError();\n                total += size;\n            }\n        }\n\n        cudaCheckError();\n    }\n    hipStreamSynchronize(stream);\n    cudaCheckError();\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_gather_L2H_v2(T &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (num_rings != 1)\n    {\n        FatalError(\"Halo exchange with ring > 1 not implemented in do_gather_L2H\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Gather L2H\n    int num_neighbors = m.manager->neighbors.size();\n    int bsize = b.get_block_size();\n    int total = 0;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        total += (m.manager->halo_offsets[i + 1] - m.manager->halo_offsets[i]) * bsize;\n    }\n\n    if (total != 0)\n    {\n        hipMemcpyAsync(b.linear_buffers[0], b.raw() + m.manager->halo_offsets[0], total*sizeof(typename T::value_type), hipMemcpyDefault, stream);\n        cudaCheckError();\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_add_from_halo(T &b, const Matrix<TConfig> &m, int num_rings, hipStream_t &stream)\n{\n#ifdef AMGX_WITH_MPI\n    typedef typename T::value_type vtyp;\n    AddFromHalo1Functor<T_Config, T> ex1(b, m, num_rings, 0, stream);\n    AddFromHalo2Functor<T_Config, T> ex2(b, m, num_rings, 0, stream);\n    AddFromHalo3Functor<T_Config, T> ex3(b, m, num_rings, 0, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    ex2.get_send_size() = ex1.get_send_size();//pass relevant info from one state to another\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n\n    Accept(fsmV);\n    //pass relevant info from one state to another:\n    //\n    ex3.get_recv_size() = ex2.get_offset();\n    ex3.get_send_size() = ex2.get_send_size();\n    fsmV.next();//advance FSM\n\n    // I only wait to receive data, I can start working before all my buffers were sent\n    MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n    b.dirtybit = 0;\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo(T &b, const Matrix<TConfig> &m, int num_rings)\n{\n#ifdef AMGX_WITH_MPI\n    typedef typename T::value_type value_type;\n    ExcHalo1Functor<T_Config, T> ex1(b, m, num_rings, 0);\n    ExcHalo2Functor<T_Config, T> ex2(b, m, num_rings, 0);\n    ExcHalo3Functor<T_Config, T> ex3(b, m, num_rings, 0);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    ex3.get_offset() = ex2.get_offset();//pass relevant info from one state to another\n    fsmV.next();//advance FSM\n    MPI_Waitall(2 * neighbors, &b.requests[0],  MPI_STATUSES_IGNORE); //I only wait to receive data, I can start working before all my buffers were sent\n    b.dirtybit = 0;\n    //FSM step 3:\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo_async(T &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (tag == -1) { FatalError(\"Untagged vector\\n\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    hipEventSynchronize(event);\n    cudaCheckError();\n    ExcHalo1Functor<T_Config, T> ex1(b, m, 1, 0, tag, stream);\n    ExcHalo2AsyncFunctor<T_Config, T> ex2(b, m, 1, 0, tag);\n    ExcHalo3AsyncFunctor<T_Config, T> ex3(b, m, 1, 0, tag, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n\n    if (min_rows_latency_hiding < 0 || m.manager->num_rows_all() < min_rows_latency_hiding)\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], /*&b.statuses[0]*/ MPI_STATUSES_IGNORE); //I only wait to receive data, I can start working before all my buffers were sent\n        b.dirtybit = 0;\n        Accept(fsmV);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_exchange_halo_wait(T &b, const Matrix<TConfig> &m, hipStream_t stream)\n{\n#ifdef AMGX_WITH_MPI\n    int bsize = b.get_block_size();\n\n    if (!(min_rows_latency_hiding < 0 || m.manager->num_rows_all() < min_rows_latency_hiding))\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n        b.dirtybit = 0;\n        b.in_transfer = IDLE;\n        HalloWaitCopyFunctor<T_Config, T> hcpyf(b, m, stream);\n        LightVisitor<T_Config> lvcpy(hcpyf);//Visitor branching off: no-op for GPU Direct\n        Accept(lvcpy);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nvoid CommsMPIHostBufferStream<T_Config>::do_send_receive_wait(T &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (tag == -1)\n    {\n        FatalError(\"Untagged vector\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    SendRecvWait1Functor<T_Config, T> ex1(b, m, 1, 0, tag, stream);\n    SendRecvWait2Functor<T_Config, T> ex2(b, m, 1, 0, tag, stream);\n    SendRecvWait3Functor<T_Config, T> ex3(b, m, 1, 0, tag, stream);\n    FSMVisitor<T_Config> fsmV;\n    fsmV.get_functors().push_back(&ex1);\n    fsmV.get_functors().push_back(&ex2);\n    fsmV.get_functors().push_back(&ex3);\n    hipEventSynchronize(event);\n    cudaCheckError();\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n    b.in_transfer = RECEIVING | SENDING;\n    Accept(fsmV);\n    fsmV.next();//advance FSM\n\n    if (neighbors > 0)\n    {\n        MPI_Waitall(2 * neighbors, &b.requests[0], MPI_STATUSES_IGNORE);\n    }\n\n    Accept(fsmV);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T>\nbool CommsMPIHostBufferStream<T_Config>::do_exchange_halo_query(T &b, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (b.dirtybit == 0) { return true; }\n\n    if ((b.in_transfer & RECEIVING) == 0) { return false; }\n\n    int finished = 1;\n    MPI_Status status;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        int res = MPI_Test(&b.requests[neighbors + i], &finished, &status);\n\n        if (finished == 0  || res != MPI_SUCCESS)\n        {\n            return false;\n        }\n    }\n\n    b.dirtybit = 0;\n    b.in_transfer = SENDING;\n    HalloWaitCopyFunctor<T_Config, T> hcpyf(b, m, NULL);\n    LightVisitor<T_Config> lvcpy(hcpyf);//Visitor branching off: no-op for GPU Direct\n    Accept(lvcpy);\n    return true;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_matrix_halo(IVector_Array &row_offsets, I64Vector_Array &col_indices, MVector_Array &values, I64Vector_Array &halo_row_ids, IVector_h &neighbors_list, int global_id)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    int num_neighbors = neighbors_list.size();\n    std::vector<HIVector> local_row_offsets(num_neighbors);\n    std::vector<HI64Vector> local_col_indices(num_neighbors);\n    std::vector<HMVector> local_values(num_neighbors);\n    std::vector<HIVector> send_row_offsets(num_neighbors);\n    std::vector<HI64Vector> send_col_indices(num_neighbors);\n    std::vector<HMVector> send_values(num_neighbors);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        send_row_offsets[i] = row_offsets[i];\n        send_col_indices[i] = col_indices[i];\n        send_values[i] = values[i];\n    }\n\n    std::vector<HI64Vector> local_row_ids(0);\n    std::vector<HI64Vector> send_row_ids(0);\n\n    if (halo_row_ids.size() != 0)\n    {\n        local_row_ids.resize(num_neighbors);\n        send_row_ids.resize(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            send_row_ids[i] = halo_row_ids[i];\n        }\n    }\n\n    // send metadata\n    std::vector<INDEX_TYPE> metadata(num_neighbors * 2); // num_rows+1, num_nz\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        metadata[i * 2 + 0] = row_offsets[i].size();\n        metadata[i * 2 + 1] = col_indices[i].size();\n        MPI_Isend(&metadata[i * 2 + 0], 2, MPI_INT, neighbors_list[i], 0, mpi_comm, &requests[i]);\n    }\n\n    // receive metadata\n    std::vector<INDEX_TYPE> metadata_recv(2);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Recv(&metadata_recv[0], 2, MPI_INT, neighbors_list[i], 0, mpi_comm, MPI_STATUSES_IGNORE);\n        local_row_offsets[i].resize(metadata_recv[0]);\n        local_col_indices[i].resize(metadata_recv[1]);\n        local_values[i].resize(metadata_recv[1]);\n\n        if (local_row_ids.size() != 0)\n        {\n            if (metadata_recv[0] - 1 > 0)\n            {\n                local_row_ids[i].resize(metadata_recv[0] - 1);    // row_ids is one smaller than row_offsets\n            }\n        }\n    }\n\n    MPI_Waitall(num_neighbors, &requests[0], MPI_STATUSES_IGNORE); // data is already received, just closing the handles\n    // receive matrix data\n    typedef typename T_Config::MatPrec mvalue;\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Irecv(local_row_offsets[i].raw(), local_row_offsets[i].size(), MPI_INT, neighbors_list[i], 10 * neighbors_list[i] + 0, mpi_comm, &requests[3 * num_neighbors + i]);\n        MPI_Irecv(local_col_indices[i].raw(), local_col_indices[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 1, mpi_comm, &requests[4 * num_neighbors + i]);\n        MPI_Irecv(local_values[i].raw(), local_values[i].size()*sizeof(mvalue), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 2, mpi_comm, &requests[5 * num_neighbors + i]);\n\n        if (send_row_ids.size() != 0)\n        {\n            MPI_Irecv(local_row_ids[i].raw(), local_row_ids[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * neighbors_list[i] + 3, mpi_comm, &requests[7 * num_neighbors + i]);\n        }\n    }\n    //Note: GPU Direct should use row_offsets[], col_indices[], values[] directly in here:\n    // send matrix: row offsets, col indices, values\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        MPI_Isend(send_row_offsets[i].raw(), send_row_offsets[i].size(), MPI_INT, neighbors_list[i], 10 * global_id + 0, mpi_comm, &requests[i]);\n        MPI_Isend(send_col_indices[i].raw(), send_col_indices[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * global_id + 1, mpi_comm, &requests[num_neighbors + i]);\n        MPI_Isend(send_values[i].raw(), send_values[i].size()*sizeof(mvalue), MPI_BYTE, neighbors_list[i], 10 * global_id + 2, mpi_comm, &requests[2 * num_neighbors + i]);\n\n        if (send_row_ids.size() != 0)\n        {\n            MPI_Isend(send_row_ids[i].raw(), send_row_ids[i].size()*sizeof(int64_t), MPI_BYTE, neighbors_list[i], 10 * global_id + 3, mpi_comm, &requests[6 * num_neighbors + i]);\n        }\n    }\n\n    if (halo_row_ids.size() != 0)\n    {\n        MPI_Waitall(8 * num_neighbors, &requests[0], MPI_STATUSES_IGNORE);    //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    }\n    else\n    {\n        MPI_Waitall(6 * num_neighbors, &requests[0], MPI_STATUSES_IGNORE);    //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    }\n\n    //Note: GPU Direct should swap here\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        row_offsets[i] = local_row_offsets[i];\n        col_indices[i] = local_col_indices[i];\n        values[i] = local_values[i];\n\n        if (halo_row_ids.size() != 0)\n        {\n            halo_row_ids[i] = local_row_ids[i];\n        }\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_matrix_halo(Matrix_Array &halo_rows, DistributedManager_Array &halo_btl, const Matrix<TConfig> &m)\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    int bsize = m.get_block_size();\n    int rings = m.manager->B2L_rings[0].size() - 1;\n    int diag = m.hasProps(DIAG);\n    std::vector<Matrix_h> local_copy(halo_rows.size());\n    std::vector<Matrix_h> send(halo_rows.size());\n    std::vector<Manager_h> local_copy_manager(halo_rows.size());\n    std::vector<Manager_h> send_manager(halo_rows.size());\n\n    //Note: GPU Direct does *NOT* do that:\n    for (int i = 0; i < halo_rows.size(); i++)\n    {\n        send_manager[i] = halo_btl[i];\n        send[i] = halo_rows[i];\n    }\n\n    {\n        // there shouldn't be any uncompleted requests, because we don't want to rewrite them\n        int completed;\n        MPI_Testall(requests.size(), &requests[0], &completed, MPI_STATUSES_IGNORE);\n\n        if (!completed)\n        {\n            MPI_Waitall(requests.size(), &requests[0], MPI_STATUSES_IGNORE);\n        }\n    }\n\n    std::vector<INDEX_TYPE> metadata(neighbors * (rings + 1 + 5)); //ring offsets (rings+1), num_rows, num_nz, base_index, index_range\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        for (int j = 0; j <= rings; j++) { metadata[i * (rings + 1 + 5) + j] = halo_btl[i].B2L_rings[0][j]; }\n\n        metadata[i * (rings + 1 + 5) + rings + 1] = halo_rows[i].get_num_rows();\n        metadata[i * (rings + 1 + 5) + rings + 2] = halo_rows[i].get_num_nz();\n        metadata[i * (rings + 1 + 5) + rings + 3] = halo_btl[i].base_index();\n        metadata[i * (rings + 1 + 5) + rings + 4] = halo_btl[i].index_range();\n        metadata[i * (rings + 1 + 5) + rings + 5] = halo_btl[i].L2H_maps[0].size();\n        MPI_Isend(&metadata[i * (rings + 1 + 5)], rings + 6, MPI_INT, m.manager->neighbors[i], 0, mpi_comm, &requests[i]);\n    }\n\n    std::vector<INDEX_TYPE> metadata_recv(rings + 1 + 5);\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Recv(&metadata_recv[0], rings + 6, MPI_INT, m.manager->neighbors[i], 0, mpi_comm, /*&status*/MPI_STATUSES_IGNORE);\n        local_copy[i].addProps(CSR);\n\n        if (diag) { local_copy[i].addProps(DIAG); }\n\n        local_copy[i].resize(metadata_recv[rings + 1], metadata_recv[rings + 1], metadata_recv[rings + 2], m.get_block_dimy(), m.get_block_dimx(), 1);\n        local_copy_manager[i].set_base_index(metadata_recv[rings + 3]);\n        local_copy_manager[i].set_index_range(metadata_recv[rings + 4]);\n        local_copy_manager[i].B2L_rings.resize(1);\n        local_copy_manager[i].B2L_rings[0].resize(rings + 1);\n        local_copy_manager[i].B2L_maps.resize(1);\n        local_copy_manager[i].B2L_maps[0].resize(local_copy[i].get_num_rows());\n        local_copy_manager[i].L2H_maps.resize(1);\n        local_copy_manager[i].L2H_maps[0].resize(metadata_recv[rings + 5]);\n\n        for (int j = 0; j <= rings; j++) { local_copy_manager[i].B2L_rings[0][j] = metadata_recv[j]; }\n    }\n\n    MPI_Waitall(neighbors, &requests[0], MPI_STATUSES_IGNORE); //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n    typedef typename T_Config::MatPrec mvalue;\n\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Irecv(local_copy[i].row_offsets.raw(), local_copy[i].row_offsets.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 0, mpi_comm, &requests[5 * neighbors + 5 * i]);\n        MPI_Irecv(local_copy[i].col_indices.raw(), local_copy[i].col_indices.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 1, mpi_comm, &requests[5 * neighbors + 5 * i + 1]);\n        MPI_Irecv(local_copy_manager[i].B2L_maps[0].raw(), local_copy_manager[i].B2L_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 2, mpi_comm, &requests[5 * neighbors + 5 * i + 2]);\n        MPI_Irecv(local_copy_manager[i].L2H_maps[0].raw(), local_copy_manager[i].L2H_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 3, mpi_comm, &requests[5 * neighbors + 5 * i + 3]);\n        MPI_Irecv(local_copy[i].values.raw(), local_copy[i].values.size()*sizeof(mvalue), MPI_BYTE, m.manager->neighbors[i], 10 * m.manager->neighbors[i] + 4, mpi_comm, &requests[5 * neighbors + 5 * i + 4]);\n    }\n\n    //Note: GPU Direct uses halo_rows and halo_btl in here:\n    for (int i = 0; i < neighbors; i++)\n    {\n        MPI_Isend(send[i].row_offsets.raw(), send[i].row_offsets.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 0, mpi_comm, &requests[5 * i]);\n        MPI_Isend(send[i].col_indices.raw(), send[i].col_indices.size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 1, mpi_comm, &requests[5 * i + 1]);\n        MPI_Isend(send_manager[i].B2L_maps[0].raw(), send_manager[i].B2L_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 2, mpi_comm, &requests[5 * i + 2]);\n        MPI_Isend(send_manager[i].L2H_maps[0].raw(), send_manager[i].L2H_maps[0].size(), MPI_INT, m.manager->neighbors[i], 10 * m.manager->global_id() + 3, mpi_comm, &requests[5 * i + 3]);\n        MPI_Isend(send[i].values.raw(), send[i].values.size()*sizeof(mvalue), MPI_BYTE, m.manager->neighbors[i], 10 * m.manager->global_id() + 4, mpi_comm, &requests[5 * i + 4]);\n    }\n\n    MPI_Waitall(2 * 5 * neighbors, &requests[0], MPI_STATUSES_IGNORE); //I have to wait for my stuff to be sent too, because I deallocate those matrices upon exditing this function\n\n    //Note: GPU Direct swaps here:\n    for (int i = 0; i < halo_rows.size(); i++)\n    {\n        halo_btl[i] = local_copy_manager[i];\n        halo_rows[i] = local_copy[i];\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_raw_data(const void *ptr, int size, int destination, int tag )\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Send(const_cast<void *>(ptr), size, MPI_BYTE, destination, tag, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_raw_data(void *ptr, int size, int source, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Recv(ptr, size, MPI_BYTE, source, tag, mpi_comm, MPI_STATUSES_IGNORE);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(DVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(DVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(FVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(FVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(ZVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(ZVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(CVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(CVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(IVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(IVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(BVector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(BVector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED); /*do_exchange_halo(b, m);*/}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(BVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_exchange_halo_async(b,m, event, tag, stream);*/}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(BVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      FatalError(\"MPI Comms boolean exchange not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_exchange_halo_wait(b, m, stream);*/}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup(I64Vector &b, const Matrix<TConfig> &m, int tag, int num_rings) { do_setup(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo(I64Vector &b, const Matrix<TConfig> &m, int tag, int num_rings) {           do_exchange_halo(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_async(I64Vector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {     do_exchange_halo_async(b, m, event, tag, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_halo_wait(I64Vector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t stream) {      do_exchange_halo_wait(b, m, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(DVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(IVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(FVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(CVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(ZVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(BVector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::setup_L2H(I64Vector &b, Matrix<TConfig> &m, int num_rings) { do_setup_L2H(b, m, num_rings);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(DVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(IVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(FVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(CVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(ZVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(BVector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    FatalError(\"MPI Comms send-receive with streams is not implemented\", AMGX_ERR_NOT_IMPLEMENTED);\n}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_receive_wait(I64Vector &b, const Matrix<TConfig> &m, hipEvent_t event, int tag, hipStream_t &stream)\n{\n    do_send_receive_wait(b, m, event, tag, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(DVector &b, const Matrix<TConfig> &m, int tag, int num_rings, hipStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(FVector &b, const Matrix<TConfig> &m, int tag, int num_rings, hipStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(CVector &b, const Matrix<TConfig> &m, int tag, int num_rings, hipStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(ZVector &b, const Matrix<TConfig> &m, int tag, int num_rings, hipStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::add_from_halo(IVector &b, const Matrix<TConfig> &m, int tag, int num_rings, hipStream_t &stream) {           do_add_from_halo(b, m, num_rings, stream);}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(DVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(FVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(CVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(ZVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H(IVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H(b, m, num_rings, stream);\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(DVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(FVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(CVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(ZVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::gather_L2H_v2(IVector &b, const Matrix<TConfig> &m, int num_rings, hipStream_t stream)\n{\n    do_gather_L2H_v2(b, m, num_rings, stream);;\n}\n\n\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(DVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(FVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(CVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(ZVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(IVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return do_exchange_halo_query(b, m); }\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(BVector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return false;}\ntemplate <class T_Config>\nbool CommsMPIHostBufferStream<T_Config>::exchange_halo_query(I64Vector &b, const Matrix<TConfig> &m, hipEvent_t event) {      return false;}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HDVector_Array &a, HDVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HFVector_Array &a, HFVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HCVector_Array &a, HCVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HZVector_Array &a, HZVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HIVector_Array &a, HIVector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HBVector_Array &a, HBVector &b, const Operator<TConfig> &m, int tag) {   FatalError(\"MPI Comms boolean reduction not implemented\", AMGX_ERR_NOT_IMPLEMENTED);/*do_reduction(a,b,m);*/};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce(HI64Vector_Array &a, HI64Vector &b, const Operator<TConfig> &m, int tag) {   do_reduction(a, b, m);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HDVector &a, HDVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_DOUBLE, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HFVector &a, HFVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_FLOAT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HCVector &a, HCVector &b, const Matrix<TConfig> &m, int tag)\n{\n    FatalError(\"MPI sum reduce is now supported with complex types\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HZVector &a, HZVector &b, const Matrix<TConfig> &m, int tag)\n{\n    FatalError(\"MPI sum reduce is now supported with complex types\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HIVector &a, HIVector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_INT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::global_reduce_sum(HI64Vector &a, HI64Vector &b, const Matrix<TConfig> &m, int tag)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&b[0], &a[0], b.size(), MPI_LONG_LONG_INT, MPI_SUM, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(DVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(FVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(CVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(ZVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(IVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_vectors(BVector_Array &a, const Matrix<TConfig> &m, int tag) {              do_vec_exchange(a, m);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DIVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HIVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DDVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HDVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DFVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HFVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DCVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HCVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(DZVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector(HZVector &a, int destination, int tag, int offset, int size) { send_vec(a, destination, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DIVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HIVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DDVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HDVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DFVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HFVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DCVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HCVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(DZVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_async(HZVector &a, int destination, int tag, int offset, int size) { send_vec_async(a, destination, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DIVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HIVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DDVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HDVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DFVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HFVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DCVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HCVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(DZVector &a) { send_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::send_vector_wait_all(HZVector &a) { send_vec_wait_all(a);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DIVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HIVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DDVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HDVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DFVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HFVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DCVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HCVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(DZVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector(HZVector &a, int source, int tag, int offset, int size) { recv_vec(a, source, tag, offset, size);};\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DIVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HIVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DDVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HDVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DFVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HFVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DCVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HCVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(DZVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_async(HZVector &a, int source, int tag, int offset, int size) { recv_vec_async(a, source, tag, offset, size);};\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DIVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HIVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DDVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HDVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DFVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HFVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DCVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HCVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(DZVector &a) { recv_vec_wait_all(a);};\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::recv_vector_wait_all(HZVector &a) { recv_vec_wait_all(a);};\n\ntemplate <class T_Config>\nint CommsMPIHostBufferStream<T_Config>::get_num_partitions()\n{\n#ifdef AMGX_WITH_MPI\n    int total = 0;\n    MPI_Comm_size( mpi_comm, &total );\n    return total;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nint CommsMPIHostBufferStream<T_Config>::get_global_id()\n{\n#ifdef AMGX_WITH_MPI\n    int rank = 0;\n    MPI_Comm_rank( mpi_comm, &rank);\n    return rank;\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::barrier()\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Barrier(mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n#define MAX_HOSTNAME_LENGTH 256\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::get_hostname(std::string &my_hostname)\n{\n#ifdef AMGX_WITH_MPI\n    my_hostname.resize(MAX_HOSTNAME_LENGTH);\n    int hostname_length;\n    MPI_Get_processor_name(&my_hostname[0], &hostname_length);\n    my_hostname.resize(hostname_length);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::exchange_hostnames(std::string &my_hostname, std::vector<std::string> &hostnames, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n\n    if (num_parts != this->get_num_partitions())\n    {\n        FatalError(\"Inconsistent number of partitions\", AMGX_ERR_CORE);\n    }\n\n    hostnames.resize(num_parts);\n    std::vector<int> hostname_lengths(num_parts);\n    std::vector<int> displs(num_parts + 1);\n    int my_hostname_length = my_hostname.length();\n    MPI_Allgather( &my_hostname_length, 1, MPI_INT, hostname_lengths.data(), 1, MPI_INT, mpi_comm);\n    // Compute displacement for each hostname\n    displs[0] = 0;\n    int totlen = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        displs[i + 1] = displs[i] + hostname_lengths[i];\n        totlen      += hostname_lengths[i];\n    }\n\n    std::string all_names;\n    all_names.resize(totlen);\n    MPI_Allgatherv( &my_hostname[0], my_hostname_length, MPI_CHAR,\n                    (void *)(all_names.data()), hostname_lengths.data(), displs.data(), MPI_CHAR,\n                    mpi_comm);\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        hostnames[i] = std::string(&all_names[displs[i]], hostname_lengths[i]);\n    }\n\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather(const int &my_data, HIVector &gathered_data, int num_parts) { all_gather_templated(my_data, gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather(const int64_t &my_data, HI64Vector &gathered_data, int num_parts) { all_gather_templated(my_data, gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HIVector &my_data, HIVector &gathered_data, int num_parts) { all_gather_v_templated(my_data[0], my_data.size(), gathered_data, num_parts); }\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_reduce_max(IndexType_h &my_data, IndexType_h &result_data)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allreduce(&my_data, &result_data, 1, MPI_INT, MPI_MAX, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_templated(const T &my_data, T2 &gathered_data, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n    gathered_data.resize(num_parts);\n    MPI_Allgather(&my_data, sizeof(T), MPI_BYTE,\n                  &gathered_data[0], sizeof(T), MPI_BYTE,\n                  mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\ntemplate <class T, class T2>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v_templated(T &my_data, int num_elems, T2 &gathered_data, int num_parts)\n{\n#ifdef AMGX_WITH_MPI\n    gathered_data.resize(num_parts * num_elems);\n    MPI_Allgather(&my_data, num_elems * sizeof(T), MPI_BYTE,\n                  &gathered_data[0], num_elems * sizeof(T), MPI_BYTE,\n                  mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HDVector& data, int num_elems, HDVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_DOUBLE, gathered_data.raw(), counts.raw(), displs.raw(), MPI_DOUBLE, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HFVector& data, int num_elems, HFVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_FLOAT, gathered_data.raw(), counts.raw(), displs.raw(), MPI_FLOAT, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HCVector& data, int num_elems, HCVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    FatalError(\"AllgatherV with complex data.\", AMGX_ERR_NOT_IMPLEMENTED);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HZVector& data, int num_elems, HZVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    FatalError(\"AllgatherV with complex data.\", AMGX_ERR_NOT_IMPLEMENTED);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\ntemplate <class T_Config>\nvoid CommsMPIHostBufferStream<T_Config>::all_gather_v(HIVector& data, int num_elems, HIVector& gathered_data, HIVector counts, HIVector displs)\n{\n#ifdef AMGX_WITH_MPI\n    MPI_Allgatherv(data.raw(), num_elems, MPI_INT, gathered_data.raw(), counts.raw(), displs.raw(), MPI_INT, mpi_comm);\n#else\n    FatalError(\"MPI Comms module requires compiling with MPI\", AMGX_ERR_NOT_IMPLEMENTED);\n#endif\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CommsMPIHostBufferStream<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/distributed/comms_mpi_hostbuffer_stream.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <device_properties.h>\n#include <stream.h>\n\n#include \"sm_utils.inl\"\n\nnamespace amgx\n{\nnamespace multicolor_gauss_seidel_solver\n{\n\n// -------------------------\n//  Kernels\n// -------------------------\n\ntemplate<typename IndexType, typename ValueTypeA>\n__global__\nvoid setupBlockGSSmooth1x1(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_rows)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < num_rows)\n    {\n        //epsilon (val) is a small number with the same sign as val\n        ValueTypeA val =  values[dia_indices[tid]];\n        Dinv[tid] =  isNotCloseToZero(val) ? ValueTypeA(1) / val :  ValueTypeA(1) / epsilon(val);\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmooth3by3BlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[tid] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockGSSmooth4by4BlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockGSSmoothbBybBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ char sharedc[];\n    ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&sharedc[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    s_Amat[s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind] = e_out[t1 * tile_num + t2];\n                }\n\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  int blockrows_per_cta, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmoothBlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices,\n        ValueTypeA *Dinv, const int num_block_rows)\n{\n    int cta_blockrow_id = threadIdx.x / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = threadIdx.x - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta];\n    int offset, s_offset;\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows && cta_blockrow_id < blockrows_per_cta)\n    {\n        // Store the diagonal\n        offset = blockrow_id * bsize_sq + mat_entry_index;\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n\n        s_offset = cta_blockrow_id * bsize_sq;\n// THIS IS DEFINED IN BLOCK COMMON\n//#define s_A(ROW,COL)   s_Amat[s_offset+ROW*bsize+COL]\n        ValueTypeA diag;\n\n        for (int row = 0; row < bsize; row++)\n        {\n            diag =   isNotCloseToZero(s_A(row, row)) ? ValueTypeA(1) / s_A(row, row) : ValueTypeA(1) / epsilon(s_A(row, row));\n            //This condition is a subset of the condition below for i_ind==0\n            //if ((i_ind == 0) && !(j_ind == row))\n            //  s_A(row,j_ind) = s_A(row,j_ind)*diag;\n\n            if ((i_ind != row) && !(j_ind == row))\n            {\n                s_A(i_ind, j_ind) = -(s_A(i_ind, row) * s_A(row, j_ind)) + s_A(i_ind, j_ind);\n            }\n\n            if (i_ind == 0)\n            {\n                s_A(j_ind, row) = (j_ind == row) ? diag : -(s_A(j_ind, row) * diag);\n            }\n        }\n\n        Dinv[offset] = s_A(i_ind, j_ind);\n        blockrow_id += (gridDim.x * blockDim.x) / bsize_sq;\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = tid >> log_bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (eighthwarp_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[eighthwarp_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        loadAsVector<bsize>(nonzero_values + dia_indices[i]*bsize * bsize + vec_entry_index * bsize, temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n                s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        eighthwarp_id +=  (gridDim.x * blockDim.x >> log_bsize);\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int bsize>\n__global__\nvoid multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    //const int vec_entry_index = threadIdx.x & (bsize-1);\n    //const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    //const int tid = blockDim.x*blockIdx.x+ threadIdx.x;\n    //int eighthwarp_id = tid >> log_bsize;\n    //volatile __shared__ ValueType s_xtemp[ bsize*eighthwarps_per_block ];\n    int cta_blockrow_id = (threadIdx.x) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = threadIdx.x - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (blockrow_id < num_rows_per_color && cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = cta_blockrow_id * bsize;\n        offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = nonzero_values[offset + k];\n        }\n\n        //loadAsVector<bsize>(dia_values+offset,temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n                for (int k = 0; k < bsize; k++)\n                {\n                    temp[k] = nonzero_values[offset + k];\n                }\n\n                //loadAsVector<bsize>(nonzero_values+offset,temp);\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = Dinv[offset + k];\n        }\n\n        //loadAsVector<bsize>(Dinv+offset,temp);\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        blockrow_id +=  (gridDim.x * blockDim.x) / bsize;\n    }\n}\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\n// This is TERRIBLE in terms of memory access pattern\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid multicolorGSSmoothCsrKernel_NAIVE_tex(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeA dia;\n\n    while (tid < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[tid];\n        bmAx = b[i];\n        xin = x[i];\n        dia = isNotCloseToZero(nonzero_values[diag[i]]) ? nonzero_values[diag[i]] : epsilon( nonzero_values[diag[i]]);\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[jcol];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[i] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to smooth, warp per row version\n// Each warp processes a row; contributions of threads are reduced and stored to x by lane 0\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid multicolorGSSmoothCsrKernel_WarpPerRow(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeA dia, diatemp;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    \n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n\n    constexpr unsigned full_mask = 0xffffffff;\n    \n    int row_id = blockIdx.x * NUM_WARPS + warp_id;\n    int a_col_tmp;\n\n    while (row_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[row_id];\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = row_offsets[i + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        xin = x[i];\n        bmAx = amgx::types::util<ValueTypeB>::get_zero(); //utils::Ld<utils::LD_NC>::load(&b[i]);\n\n        for (a_col_it += lane_id; utils::any(a_col_it < a_col_end); a_col_it += WARP_SIZE )\n        {\n            const bool is_active = a_col_it < a_col_end;        \n       \n            int x_row_id;\n            ValueTypeB a_value;\n            ValueTypeB x_value;\n\n            if (is_active)\n            {\n                x_row_id = column_indices[a_col_it];\n                a_value  = nonzero_values[a_col_it];\n                x_value = x[x_row_id];\n\n                bmAx -= a_value*x_value;\n            }\n        }\n\n        #pragma unroll\n        for (int offset = 32 >> 1 ; offset > 0; offset = offset >> 1) \n        {\n            bmAx  += __shfl_down_sync(full_mask, bmAx, offset);\n        }\n        \n        if (lane_id == 0)\n        {\n            bmAx += b[i];\n            diatemp = nonzero_values[diag[i]];\n            dia = isNotCloseToZero(diatemp) ? diatemp : epsilon(diatemp);\n            bmAx /= dia;\n            xout[i] = xin + weight * bmAx;\n        }\n\n        row_id += gridDim.x * NUM_WARPS;\n    }\n}\n\n// Kernel to smooth, N per row version\n// Each row is processed by N threads; contributions of threads are reduced and stored to x by first thread processing each row\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB, int CTA_SIZE, int WARP_SIZE, int n_per_row>\n__global__\nvoid multicolorGSSmoothCsrKernel_nPerRow(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    constexpr unsigned full_mask = 0xffffffff;\n\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n\n    const int me_in_row = lane_id % n_per_row;\n    const int batch_row = lane_id / n_per_row;\n\n    int row_id = blockIdx.x * CTA_SIZE/n_per_row + (warp_id*WARP_SIZE)/n_per_row + batch_row;\n    const int row_id_increment = gridDim.x * blockDim.x/n_per_row;\n\n    int i;\n    ValueTypeB bmAx, temp, s_xtemp, diatemp;\n    ValueTypeA dia;\n    IndexType jcol;\n    int jit, jmax;\n\n    while (row_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[row_id];\n        bmAx = amgx::types::util<ValueTypeB>::get_zero();\n        jit  = row_offsets[i];\n        jmax = row_offsets[i + 1];     \n\n        for (jit = jit + me_in_row; jit < jmax; jit += n_per_row)\n        {\n            jcol = column_indices[jit];\n            temp = nonzero_values[jit];            \n            s_xtemp = x[jcol];\n\n            bmAx -= temp * s_xtemp;\n        }\n\n        #pragma unroll\n        for (int offset = n_per_row >> 1 ; offset > 0; offset = offset >> 1) \n        {\n            bmAx  += __shfl_down_sync(full_mask, bmAx, offset);\n        }\n\n        if (me_in_row == 0)\n        {\n            diatemp = nonzero_values[diag[i]];\n            dia = isNotCloseToZero(diatemp) ? diatemp : epsilon(diatemp);\n            bmAx += b[i];\n            bmAx /= dia;\n           \n            xout[i] = x[i] + weight * bmAx;\n        }\n\n        row_id += row_id_increment;\n   }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\n// Batch version\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid multicolorGSSmoothCsrKernel_NAIVE_tex_batched(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv, const int batch_sz,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int bid = tid / batch_sz;\n    int vid = tid % batch_sz;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeA dia;\n\n    while (bid < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[bid];\n        bmAx = b[batch_sz * i + vid];\n        xin = x[batch_sz * i + vid];\n        dia = isNotCloseToZero(nonzero_values[diag[i]]) ? nonzero_values[diag[i]] : epsilon( nonzero_values[diag[i]]);\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[batch_sz * jcol + vid];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[batch_sz * i + vid] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n        bid = tid / batch_sz;\n    }\n}\n\n// -------------------\n// Methods\n// -------------------\n\n// Constructor\ntemplate<class T_Config>\nMulticolorGaussSeidelSolver_Base<T_Config>::MulticolorGaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->symFlag = cfg.AMG_Config::template getParameter<int>(\"symmetric_GS\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->use_bsrxmv = 1;\n    }\n    else\n    {\n        this->use_bsrxmv = 0;\n    }\n\n    if (this->weight == 0)\n    {\n        this->weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor GaussSeidel smoother\\n\");\n    }\n\n    this->gs_method = KernelMethod::DEFAULT;\n\n    cudaEventCreateWithFlags(&m_start, cudaEventDisableTiming);\n    cudaCheckError();\n    cudaEventCreateWithFlags(&m_end, cudaEventDisableTiming);\n    cudaCheckError();\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorGaussSeidelSolver_Base<T_Config>::~MulticolorGaussSeidelSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid MulticolorGaussSeidelSolver_Base<T_Config>::computeDinv(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        computeDinv_2x2(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        computeDinv_5x5(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        computeDinv_bxb(A, A.get_block_dimx());\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver computeEinv\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n// Method to compute the diagonal matrix E in DILU smoother\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>, cudaFuncCachePreferL1);\n    setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    //TODO: check correctness with blocksizes > 5\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr= this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize-1)/4+1)*((bsize-1)/4+1));\n    ValueTypeA *temp_ptr= temp.raw();\n\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block/16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows()-1)/halfwarps_per_block + 1);\n\n    cudaFuncSetCacheConfig(setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType,ValueTypeA,threads_per_block,halfwarps_per_block>,cudaFuncCachePreferL1);\n    setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType,ValueTypeA,threads_per_block,halfwarps_per_block> <<<num_blocks,threads_per_block, sizeof(ValueTypeB)*bsize*bsize*halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize*bsize, temp_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 3;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 2;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 5;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    if (this->use_bsrxmv && A.get_block_dimx() == A.get_block_dimy())\n    {\n        this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        cudaFuncSetCacheConfig(setupBlockGSSmooth1x1<IndexType, ValueTypeA>, cudaFuncCachePreferL1);\n        setupBlockGSSmooth1x1<IndexType, ValueTypeA> <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), A.diag.raw(), this->Dinv.raw(), A.get_num_rows());\n        cudaCheckError();\n    }\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->use_bsrxmv << std::endl;\n    std::cout << \"symmetric_GS = \" << this->symFlag << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorGaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if ( this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor gauss-seidel solver. Try setting: coloring_level=1 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if ( this->m_explicit_A->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Multicolor DILU solver only supports row major format for the blocks\", AMGX_ERR_CONFIGURATION);\n    }\n\n    computeDinv( *this->m_explicit_A );\n}\n\n//\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n        multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    cudaStream_t stream = 0; // Stream where previous work is done. Currently - default stream\n    cudaStream_t work_stream = stream;\n    int use_l2_hint = 0;\n    int use_aux_stream = 0; // use separate async stream to do smoothing\n    cudaDeviceProp props = getDeviceProperties();\n    int arch = 10 * props.major + props.minor;\n\n    const int WARP_SIZE = 32;\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 672;\n    \n    KernelMethod selected_method = this->gs_method;\n    if (this->gs_method == KernelMethod::DEFAULT)\n    {\n        // Use 4 threads per row\n        // If >500K rows, use L2 hint to keep \u2018x\u2019 cached (Ampere and newer)\n        // If <500K NNZ per color, use T32_PER_ROW (WARP_PER_ROW might work as well or better)\n        // If >20 NNZ per row, use T32_PER_ROW (WARP_PER_ROW might work as well or better)\n        selected_method = KernelMethod::T4_PER_ROW;\n        if (A.get_num_rows() > 500000) use_l2_hint = 1;\n        if (A.get_num_nz() / A.get_num_rows() > 20) selected_method = KernelMethod::T32_PER_ROW;\n        if (A.get_num_nz() / this->m_explicit_A->getMatrixColoring().getNumColors() < 500000) selected_method = KernelMethod::T32_PER_ROW;\n    }\n\n    if (use_l2_hint) use_aux_stream = 1; // l2 hint can be enabled only on async stream\n    if (use_aux_stream) work_stream = this->get_aux_stream();\n\n    if (use_aux_stream)\n    {\n        cudaEventRecord(this->m_start, stream);\n        cudaCheckError();\n        cudaStreamWaitEvent(work_stream, this->m_start, 0); // work_stream to wait for stream \n    }\n\n    // try to keep 'x' in L2 cache, if at least Ampere & CUDA 11\n#if CUDART_VERSION >= 11000\n    cudaStreamAttrValue stream_attribute;\n    if (arch >= 80 && use_l2_hint == 1)\n    {\n        cudaDeviceProp prop = getDeviceProperties();\n        size_t x_size = min( A.get_num_rows()*8 , prop.persistingL2CacheMaxSize );      // set-aside length of 'x' (number of rows in A)*8 bytes\n        cudaDeviceSetLimit( cudaLimitPersistingL2CacheSize, x_size);                    //            for persisting accesses or the max allowed\n        stream_attribute.accessPolicyWindow.base_ptr  = reinterpret_cast<void*>(x_ptr); \n        stream_attribute.accessPolicyWindow.num_bytes = x_size;\n        stream_attribute.accessPolicyWindow.hitRatio  = 1.0;                            // Hint for cache hit ratio\n        stream_attribute.accessPolicyWindow.hitProp   = cudaAccessPropertyPersisting;   // Type of access property on cache hit\n        stream_attribute.accessPolicyWindow.missProp  = cudaAccessPropertyStreaming;    // Type of access property on cache miss.\n        cudaStreamSetAttribute(work_stream, cudaStreamAttributeAccessPolicyWindow, &stream_attribute);   \n    }\n#endif\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        if (selected_method == KernelMethod::NAIVE)\n        {\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / CTA_SIZE + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n            b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        }\n        else if (selected_method == KernelMethod::WARP_PER_ROW)\n        {\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        }\n        else if (selected_method == KernelMethod::T32_PER_ROW)\n        {\n            const int N_PER_ROW = 32;\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n        }\n        else if (selected_method == KernelMethod::T4_PER_ROW)\n        {\n            const int N_PER_ROW = 4;\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n        }\n        else\n        {\n            FatalError(\"Incorrect GS configuration (gs_method value?)\", AMGX_ERR_CONFIGURATION);\n        }\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            if (selected_method == KernelMethod::NAIVE)\n            {\n                const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / CTA_SIZE + 1 );\n                cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            }\n            else if (selected_method == KernelMethod::WARP_PER_ROW)\n            {\n                cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE>, cudaFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            }\n            else if (selected_method == KernelMethod::T32_PER_ROW)\n            {\n                const int N_PER_ROW = 32;\n                cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>, cudaFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n            }\n            else if (selected_method == KernelMethod::T4_PER_ROW)\n            {\n                const int N_PER_ROW = 4;\n                cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>, cudaFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n            }\n            else\n            {\n                FatalError(\"Incorrect GS configuration (gs_method value?)\", AMGX_ERR_CONFIGURATION);\n            }\n            cudaCheckError();  \n        }\n    }\n\n    // reset persisting L2 cache\n#if CUDART_VERSION >= 11000\n    if (arch >= 80 && use_l2_hint == 1)\n    {        \n        stream_attribute.accessPolicyWindow.num_bytes = 0;                                          // Setting the window size to 0 disable it\n        cudaStreamSetAttribute(work_stream, cudaStreamAttributeAccessPolicyWindow, &stream_attribute);   // Overwrite the access policy attribute to a CUDA Stream\n        cudaCtxResetPersistingL2Cache();                                                            // Remove any persistent lines in L2 \n    }\n#endif\n\n    if (use_aux_stream)\n    {\n        cudaEventRecord(this->m_start, work_stream);\n        cudaCheckError();\n        cudaStreamWaitEvent(stream, this->m_start, 0); // stream to wait for work_stream\n    }\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::batch_smooth_1x1(const Matrix_d &A, int batch_sz, const VVector &b, VVector &x)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int ivec = 0; ivec < batch_sz; ivec++)\n    {\n        const ValueTypeB *b_ptr = b.raw() + ivec * A.get_num_rows();\n        ValueTypeB *x_ptr = x.raw() + ivec * A.get_num_rows();\n\n        for (int i = 0; i < num_colors; i++)\n        {\n            const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int threads_per_block = 512;\n            //const int eightwarps_per_block = threads_per_block/4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n\n        if (this->symFlag == 1)\n        {\n            for (int i = num_colors - 1; i >= 0; i--)\n            {\n                const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n                const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n                if (num_rows_per_color == 0) { continue; }\n\n                const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n                cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n                cudaCheckError();\n            }\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::batch_smooth_1x1_fast(const Matrix_d &A, int batch_sz, const VVector &b, VVector &x)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    const ValueTypeB *b_ptr = b.raw();\n    ValueTypeB *x_ptr = x.raw() ;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n        multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr, batch_sz,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr, batch_sz,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int eightwarps_per_block = threads_per_block / 4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n        cudaFuncSetCacheConfig(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>, cudaFuncCachePreferL1);\n        multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int threads_per_block = 512;\n            const int eightwarps_per_block = threads_per_block / 4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>, cudaFuncCachePreferL1);\n            multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n\n    //cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int blockrows_per_cta = threads_per_block / 3;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n        cudaFuncSetCacheConfig(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>, cudaFuncCachePreferL1);\n        multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int blockrows_per_cta = threads_per_block / 3;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n            cudaFuncSetCacheConfig(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>, cudaFuncCachePreferL1);\n            multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    int x_dirty_status = x.dirtybit;\n    int x_delayed = x.delayed_send;\n    int b_delayed = b.delayed_send;\n    VVector y(b.size());\n    y.set_block_dimx(b.get_block_dimx());\n    y.set_block_dimy(b.get_block_dimy());\n    y.tag = this->tag * 100 + 3;\n    amgx::thrust::copy(b.begin(), b.end(), y.begin());\n    cudaCheckError();\n    y.dirtybit = 0;\n    y.delayed_send = 1;\n    x.delayed_send = 1;\n    b.delayed_send = 1;\n\n    for (int color = 0; color < num_colors; color++)\n    {\n        Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeA) - 1.0f, A, x, (ValueTypeA)1.0f, y, separation_flag); // y= -A.x + y      for current color\n\n        if (separation_flag == A.getViewExterior()) { y.dirtybit = 0; }\n\n        Cusparse::bsrmv(color, (ValueTypeA)this->weight, A, this->Dinv, y, (ValueTypeA)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n\n        if (separation_flag == A.getViewExterior()) { x.dirtybit = 0; }\n    }\n\n    if (this->symFlag)\n    {\n        amgx::thrust::copy(b.begin(), b.end(), y.begin());\n\n        for (int color = num_colors - 1; color >= 0; color--)\n        {\n            Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeA) - 1.0f, A, x, (ValueTypeA)1.0f, y, separation_flag); // y= -A.x + y      for current color\n\n            if (separation_flag == A.getViewExterior()) { y.dirtybit = 0; }\n\n            Cusparse::bsrmv(color, (ValueTypeA)this->weight, A, this->Dinv, y, (ValueTypeA)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n\n            if (separation_flag == A.getViewExterior()) { x.dirtybit = 0; }\n        }\n\n        cudaCheckError();\n    }\n\n    x.delayed_send = x_delayed;\n    b.delayed_send = b_delayed;\n    x.dirtybit = x_dirty_status;\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n        this->m_explicit_A->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    bool latencyHiding = true;\n    ViewType flags;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = this->m_explicit_A->getViewExterior();\n    }\n    else\n    {\n        this->m_explicit_A->setViewInterior();\n        flags = this->m_explicit_A->getViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), 0.);\n        cudaCheckError();\n    }\n\n    if ( this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1 )\n    {\n        if (this->use_bsrxmv)\n        {\n            smooth_BxB(*this->m_explicit_A, b, x, flags);\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, flags);\n        }\n    }\n    else if ( this->m_explicit_A->get_block_dimx() == 3 && this->m_explicit_A->get_block_dimy() == 3 )\n    {\n        //if (this->use_bsrxmv)\n        smooth_BxB(*this->m_explicit_A, b, x, flags);\n        //else\n        //  smooth_3x3(*this->m_explicit_A, b, x, flags);\n    }\n    else if ( this->m_explicit_A->get_block_dimx() == 4 && this->m_explicit_A->get_block_dimy() == 4 )\n    {\n        if (this->use_bsrxmv)\n        {\n            smooth_BxB(*this->m_explicit_A, b, x, flags);\n        }\n        else\n        {\n            smooth_4x4(*this->m_explicit_A, b, x, flags);\n        }\n    }\n    else if (this->m_explicit_A->get_block_dimx() == this->m_explicit_A->get_block_dimy())\n    {\n        smooth_BxB(*this->m_explicit_A, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(~(this->m_explicit_A->getViewInterior()) & this->m_explicit_A->getViewExterior());\n\n        if (flags != 0)\n        {\n            if ( this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1 )\n            {\n                if (this->use_bsrxmv)\n                {\n                    smooth_BxB(*this->m_explicit_A, b, x, flags);\n                }\n                else\n                {\n                    smooth_1x1(*this->m_explicit_A, b, x, flags);\n                }\n            }\n            else if ( this->m_explicit_A->get_block_dimx() == 3 && this->m_explicit_A->get_block_dimy() == 3 )\n            {\n                smooth_BxB(*this->m_explicit_A, b, x, flags);\n            }\n            else if ( this->m_explicit_A->get_block_dimx() == 4 && this->m_explicit_A->get_block_dimy() == 4 )\n            {\n                if (this->use_bsrxmv)\n                {\n                    smooth_BxB(*this->m_explicit_A, b, x, flags);\n                }\n                else\n                {\n                    smooth_4x4(*this->m_explicit_A, b, x, flags);\n                }\n            }\n            else if (this->m_explicit_A->get_block_dimx() == this->m_explicit_A->get_block_dimy())\n            {\n                smooth_BxB(*this->m_explicit_A, b, x, flags);\n            }\n            else\n            {\n                FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    //if (!this->m_explicit_A->is_matrix_singleGPU() && x.delayed_send==0)\n    //  this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\ncudaStream_t\nMulticolorGaussSeidelSolver_Base<T_Config>::get_aux_stream()\n{\n    static std::shared_ptr<Stream> aux_stream;\n    if (!aux_stream)\n    {\n        aux_stream = std::make_shared<Stream>();\n    }\n\n    return aux_stream->get();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorGaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorGaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <device_properties.h>\n#include <stream.h>\n\n#include \"sm_utils.inl\"\n\nnamespace amgx\n{\nnamespace multicolor_gauss_seidel_solver\n{\n\n// -------------------------\n//  Kernels\n// -------------------------\n\ntemplate<typename IndexType, typename ValueTypeA>\n__global__\nvoid setupBlockGSSmooth1x1(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_rows)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < num_rows)\n    {\n        //epsilon (val) is a small number with the same sign as val\n        ValueTypeA val =  values[dia_indices[tid]];\n        Dinv[tid] =  isNotCloseToZero(val) ? ValueTypeA(1) / val :  ValueTypeA(1) / epsilon(val);\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmooth3by3BlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[tid] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockGSSmooth4by4BlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockGSSmoothbBybBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ char sharedc[];\n    ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&sharedc[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    s_Amat[s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind] = e_out[t1 * tile_num + t2];\n                }\n\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  int blockrows_per_cta, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmoothBlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices,\n        ValueTypeA *Dinv, const int num_block_rows)\n{\n    int cta_blockrow_id = threadIdx.x / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = threadIdx.x - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta];\n    int offset, s_offset;\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows && cta_blockrow_id < blockrows_per_cta)\n    {\n        // Store the diagonal\n        offset = blockrow_id * bsize_sq + mat_entry_index;\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n\n        s_offset = cta_blockrow_id * bsize_sq;\n// THIS IS DEFINED IN BLOCK COMMON\n//#define s_A(ROW,COL)   s_Amat[s_offset+ROW*bsize+COL]\n        ValueTypeA diag;\n\n        for (int row = 0; row < bsize; row++)\n        {\n            diag =   isNotCloseToZero(s_A(row, row)) ? ValueTypeA(1) / s_A(row, row) : ValueTypeA(1) / epsilon(s_A(row, row));\n            //This condition is a subset of the condition below for i_ind==0\n            //if ((i_ind == 0) && !(j_ind == row))\n            //  s_A(row,j_ind) = s_A(row,j_ind)*diag;\n\n            if ((i_ind != row) && !(j_ind == row))\n            {\n                s_A(i_ind, j_ind) = -(s_A(i_ind, row) * s_A(row, j_ind)) + s_A(i_ind, j_ind);\n            }\n\n            if (i_ind == 0)\n            {\n                s_A(j_ind, row) = (j_ind == row) ? diag : -(s_A(j_ind, row) * diag);\n            }\n        }\n\n        Dinv[offset] = s_A(i_ind, j_ind);\n        blockrow_id += (gridDim.x * blockDim.x) / bsize_sq;\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = tid >> log_bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (eighthwarp_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[eighthwarp_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        loadAsVector<bsize>(nonzero_values + dia_indices[i]*bsize * bsize + vec_entry_index * bsize, temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n                s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        eighthwarp_id +=  (gridDim.x * blockDim.x >> log_bsize);\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int bsize>\n__global__\nvoid multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    //const int vec_entry_index = threadIdx.x & (bsize-1);\n    //const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    //const int tid = blockDim.x*blockIdx.x+ threadIdx.x;\n    //int eighthwarp_id = tid >> log_bsize;\n    //volatile __shared__ ValueType s_xtemp[ bsize*eighthwarps_per_block ];\n    int cta_blockrow_id = (threadIdx.x) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = threadIdx.x - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (blockrow_id < num_rows_per_color && cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = cta_blockrow_id * bsize;\n        offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = nonzero_values[offset + k];\n        }\n\n        //loadAsVector<bsize>(dia_values+offset,temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n                for (int k = 0; k < bsize; k++)\n                {\n                    temp[k] = nonzero_values[offset + k];\n                }\n\n                //loadAsVector<bsize>(nonzero_values+offset,temp);\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = Dinv[offset + k];\n        }\n\n        //loadAsVector<bsize>(Dinv+offset,temp);\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        blockrow_id +=  (gridDim.x * blockDim.x) / bsize;\n    }\n}\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\n// This is TERRIBLE in terms of memory access pattern\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid multicolorGSSmoothCsrKernel_NAIVE_tex(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeA dia;\n\n    while (tid < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[tid];\n        bmAx = b[i];\n        xin = x[i];\n        dia = isNotCloseToZero(nonzero_values[diag[i]]) ? nonzero_values[diag[i]] : epsilon( nonzero_values[diag[i]]);\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[jcol];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[i] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to smooth, warp per row version\n// Each warp processes a row; contributions of threads are reduced and stored to x by lane 0\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid multicolorGSSmoothCsrKernel_WarpPerRow(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeA dia, diatemp;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    \n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n\n    constexpr unsigned full_mask = 0xffffffff;\n    \n    int row_id = blockIdx.x * NUM_WARPS + warp_id;\n    int a_col_tmp;\n\n    while (row_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[row_id];\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = row_offsets[i + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        xin = x[i];\n        bmAx = amgx::types::util<ValueTypeB>::get_zero(); //utils::Ld<utils::LD_NC>::load(&b[i]);\n\n        for (a_col_it += lane_id; utils::any(a_col_it < a_col_end); a_col_it += WARP_SIZE )\n        {\n            const bool is_active = a_col_it < a_col_end;        \n       \n            int x_row_id;\n            ValueTypeB a_value;\n            ValueTypeB x_value;\n\n            if (is_active)\n            {\n                x_row_id = column_indices[a_col_it];\n                a_value  = nonzero_values[a_col_it];\n                x_value = x[x_row_id];\n\n                bmAx -= a_value*x_value;\n            }\n        }\n\n        #pragma unroll\n        for (int offset = 32 >> 1 ; offset > 0; offset = offset >> 1) \n        {\n            bmAx  += __shfl_down_sync(full_mask, bmAx, offset);\n        }\n        \n        if (lane_id == 0)\n        {\n            bmAx += b[i];\n            diatemp = nonzero_values[diag[i]];\n            dia = isNotCloseToZero(diatemp) ? diatemp : epsilon(diatemp);\n            bmAx /= dia;\n            xout[i] = xin + weight * bmAx;\n        }\n\n        row_id += gridDim.x * NUM_WARPS;\n    }\n}\n\n// Kernel to smooth, N per row version\n// Each row is processed by N threads; contributions of threads are reduced and stored to x by first thread processing each row\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB, int CTA_SIZE, int WARP_SIZE, int n_per_row>\n__global__\nvoid multicolorGSSmoothCsrKernel_nPerRow(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    constexpr unsigned full_mask = 0xffffffff;\n\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n\n    const int me_in_row = lane_id % n_per_row;\n    const int batch_row = lane_id / n_per_row;\n\n    int row_id = blockIdx.x * CTA_SIZE/n_per_row + (warp_id*WARP_SIZE)/n_per_row + batch_row;\n    const int row_id_increment = gridDim.x * blockDim.x/n_per_row;\n\n    int i;\n    ValueTypeB bmAx, temp, s_xtemp, diatemp;\n    ValueTypeA dia;\n    IndexType jcol;\n    int jit, jmax;\n\n    while (row_id < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[row_id];\n        bmAx = amgx::types::util<ValueTypeB>::get_zero();\n        jit  = row_offsets[i];\n        jmax = row_offsets[i + 1];     \n\n        for (jit = jit + me_in_row; jit < jmax; jit += n_per_row)\n        {\n            jcol = column_indices[jit];\n            temp = nonzero_values[jit];            \n            s_xtemp = x[jcol];\n\n            bmAx -= temp * s_xtemp;\n        }\n\n        #pragma unroll\n        for (int offset = n_per_row >> 1 ; offset > 0; offset = offset >> 1) \n        {\n            bmAx  += __shfl_down_sync(full_mask, bmAx, offset);\n        }\n\n        if (me_in_row == 0)\n        {\n            diatemp = nonzero_values[diag[i]];\n            dia = isNotCloseToZero(diatemp) ? diatemp : epsilon(diatemp);\n            bmAx += b[i];\n            bmAx /= dia;\n           \n            xout[i] = x[i] + weight * bmAx;\n        }\n\n        row_id += row_id_increment;\n   }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\n// Batch version\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid multicolorGSSmoothCsrKernel_NAIVE_tex_batched(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv, const int batch_sz,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int bid = tid / batch_sz;\n    int vid = tid % batch_sz;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeA dia;\n\n    while (bid < num_rows_per_color)\n    {\n        i = sorted_rows_by_color[bid];\n        bmAx = b[batch_sz * i + vid];\n        xin = x[batch_sz * i + vid];\n        dia = isNotCloseToZero(nonzero_values[diag[i]]) ? nonzero_values[diag[i]] : epsilon( nonzero_values[diag[i]]);\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[batch_sz * jcol + vid];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[batch_sz * i + vid] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n        bid = tid / batch_sz;\n    }\n}\n\n// -------------------\n// Methods\n// -------------------\n\n// Constructor\ntemplate<class T_Config>\nMulticolorGaussSeidelSolver_Base<T_Config>::MulticolorGaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->symFlag = cfg.AMG_Config::template getParameter<int>(\"symmetric_GS\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->use_bsrxmv = 1;\n    }\n    else\n    {\n        this->use_bsrxmv = 0;\n    }\n\n    if (this->weight == 0)\n    {\n        this->weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor GaussSeidel smoother\\n\");\n    }\n\n    this->gs_method = KernelMethod::DEFAULT;\n\n    hipEventCreateWithFlags(&m_start, hipEventDisableTiming);\n    cudaCheckError();\n    hipEventCreateWithFlags(&m_end, hipEventDisableTiming);\n    cudaCheckError();\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorGaussSeidelSolver_Base<T_Config>::~MulticolorGaussSeidelSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid MulticolorGaussSeidelSolver_Base<T_Config>::computeDinv(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        computeDinv_2x2(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        computeDinv_5x5(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        computeDinv_bxb(A, A.get_block_dimx());\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver computeEinv\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n// Method to compute the diagonal matrix E in DILU smoother\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>), hipFuncCachePreferL1);\n    setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    //TODO: check correctness with blocksizes > 5\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr= this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize-1)/4+1)*((bsize-1)/4+1));\n    ValueTypeA *temp_ptr= temp.raw();\n\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block/16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows()-1)/halfwarps_per_block + 1);\n\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType,ValueTypeA,threads_per_block,halfwarps_per_block>),hipFuncCachePreferL1);\n    setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType,ValueTypeA,threads_per_block,halfwarps_per_block> <<<num_blocks,threads_per_block, sizeof(ValueTypeB)*bsize*bsize*halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize*bsize, temp_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 3;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 2;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int bsize = 5;\n    const int bsize_sq = bsize * bsize;\n    const int WARP_SIZE = 32;\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = WARP_SIZE / bsize_sq;\n    const int blockrows_per_cta = (threads_per_block / WARP_SIZE) * blockrows_per_warp;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq>), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize_sq> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    if (this->use_bsrxmv && A.get_block_dimx() == A.get_block_dimy())\n    {\n        this->Dinv.resize(A.get_num_cols()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth1x1<IndexType, ValueTypeA>), hipFuncCachePreferL1);\n        setupBlockGSSmooth1x1<IndexType, ValueTypeA> <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), A.diag.raw(), this->Dinv.raw(), A.get_num_rows());\n        cudaCheckError();\n    }\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->use_bsrxmv << std::endl;\n    std::cout << \"symmetric_GS = \" << this->symFlag << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorGaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if ( this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor gauss-seidel solver. Try setting: coloring_level=1 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if ( this->m_explicit_A->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Multicolor DILU solver only supports row major format for the blocks\", AMGX_ERR_CONFIGURATION);\n    }\n\n    computeDinv( *this->m_explicit_A );\n}\n\n//\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n        multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    hipStream_t stream = 0; // Stream where previous work is done. Currently - default stream\n    hipStream_t work_stream = stream;\n    int use_l2_hint = 0;\n    int use_aux_stream = 0; // use separate async stream to do smoothing\n    hipDeviceProp_t props = getDeviceProperties();\n    int arch = 10 * props.major + props.minor;\n\n    const int WARP_SIZE = 32;\n    const int GRID_SIZE = 1024;\n    const int CTA_SIZE  = 672;\n    \n    KernelMethod selected_method = this->gs_method;\n    if (this->gs_method == KernelMethod::DEFAULT)\n    {\n        // Use 4 threads per row\n        // If >500K rows, use L2 hint to keep \u2018x\u2019 cached (Ampere and newer)\n        // If <500K NNZ per color, use T32_PER_ROW (WARP_PER_ROW might work as well or better)\n        // If >20 NNZ per row, use T32_PER_ROW (WARP_PER_ROW might work as well or better)\n        selected_method = KernelMethod::T4_PER_ROW;\n        if (A.get_num_rows() > 500000) use_l2_hint = 1;\n        if (A.get_num_nz() / A.get_num_rows() > 20) selected_method = KernelMethod::T32_PER_ROW;\n        if (A.get_num_nz() / this->m_explicit_A->getMatrixColoring().getNumColors() < 500000) selected_method = KernelMethod::T32_PER_ROW;\n    }\n\n    if (use_l2_hint) use_aux_stream = 1; // l2 hint can be enabled only on async stream\n    if (use_aux_stream) work_stream = this->get_aux_stream();\n\n    if (use_aux_stream)\n    {\n        hipEventRecord(this->m_start, stream);\n        cudaCheckError();\n        hipStreamWaitEvent(work_stream, this->m_start, 0); // work_stream to wait for stream \n    }\n\n    // try to keep 'x' in L2 cache, if at least Ampere & CUDA 11\n#if CUDART_VERSION >= 11000\n    hipLaunchAttributeValue stream_attribute;\n    if (arch >= 80 && use_l2_hint == 1)\n    {\n        hipDeviceProp_t prop = getDeviceProperties();\n        size_t x_size = min( A.get_num_rows()*8 , prop.persistingL2CacheMaxSize );      // set-aside length of 'x' (number of rows in A)*8 bytes\n        hipDeviceSetLimit( cudaLimitPersistingL2CacheSize, x_size);                    //            for persisting accesses or the max allowed\n        stream_attribute.accessPolicyWindow.base_ptr  = reinterpret_cast<void*>(x_ptr); \n        stream_attribute.accessPolicyWindow.num_bytes = x_size;\n        stream_attribute.accessPolicyWindow.hitRatio  = 1.0;                            // Hint for cache hit ratio\n        stream_attribute.accessPolicyWindow.hitProp   = hipAccessPropertyPersisting;   // Type of access property on cache hit\n        stream_attribute.accessPolicyWindow.missProp  = hipAccessPropertyStreaming;    // Type of access property on cache miss.\n        hipStreamSetAttribute(work_stream, hipLaunchAttributeAccessPolicyWindow, &stream_attribute);   \n    }\n#endif\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        if (selected_method == KernelMethod::NAIVE)\n        {\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / CTA_SIZE + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n            b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        }\n        else if (selected_method == KernelMethod::WARP_PER_ROW)\n        {\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        }\n        else if (selected_method == KernelMethod::T32_PER_ROW)\n        {\n            const int N_PER_ROW = 32;\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n        }\n        else if (selected_method == KernelMethod::T4_PER_ROW)\n        {\n            const int N_PER_ROW = 4;\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n        }\n        else\n        {\n            FatalError(\"Incorrect GS configuration (gs_method value?)\", AMGX_ERR_CONFIGURATION);\n        }\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            if (selected_method == KernelMethod::NAIVE)\n            {\n                const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / CTA_SIZE + 1 );\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            }\n            else if (selected_method == KernelMethod::WARP_PER_ROW)\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE>), hipFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_WarpPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            }\n            else if (selected_method == KernelMethod::T32_PER_ROW)\n            {\n                const int N_PER_ROW = 32;\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>), hipFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n            }\n            else if (selected_method == KernelMethod::T4_PER_ROW)\n            {\n                const int N_PER_ROW = 4;\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW>), hipFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_nPerRow<IndexType, ValueTypeA, ValueTypeB, CTA_SIZE, WARP_SIZE, N_PER_ROW> <<< GRID_SIZE, CTA_SIZE, 0, work_stream>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);  \n            }\n            else\n            {\n                FatalError(\"Incorrect GS configuration (gs_method value?)\", AMGX_ERR_CONFIGURATION);\n            }\n            cudaCheckError();  \n        }\n    }\n\n    // reset persisting L2 cache\n#if CUDART_VERSION >= 11000\n    if (arch >= 80 && use_l2_hint == 1)\n    {        \n        stream_attribute.accessPolicyWindow.num_bytes = 0;                                          // Setting the window size to 0 disable it\n        hipStreamSetAttribute(work_stream, hipLaunchAttributeAccessPolicyWindow, &stream_attribute);   // Overwrite the access policy attribute to a CUDA Stream\n        cudaCtxResetPersistingL2Cache();                                                            // Remove any persistent lines in L2 \n    }\n#endif\n\n    if (use_aux_stream)\n    {\n        hipEventRecord(this->m_start, work_stream);\n        cudaCheckError();\n        hipStreamWaitEvent(stream, this->m_start, 0); // stream to wait for work_stream\n    }\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::batch_smooth_1x1(const Matrix_d &A, int batch_sz, const VVector &b, VVector &x)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int ivec = 0; ivec < batch_sz; ivec++)\n    {\n        const ValueTypeB *b_ptr = b.raw() + ivec * A.get_num_rows();\n        ValueTypeB *x_ptr = x.raw() + ivec * A.get_num_rows();\n\n        for (int i = 0; i < num_colors; i++)\n        {\n            const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int threads_per_block = 512;\n            //const int eightwarps_per_block = threads_per_block/4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n\n        if (this->symFlag == 1)\n        {\n            for (int i = num_colors - 1; i >= 0; i--)\n            {\n                const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n                const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n                if (num_rows_per_color == 0) { continue; }\n\n                const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n                multicolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n                (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n                 b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n                cudaCheckError();\n            }\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::batch_smooth_1x1_fast(const Matrix_d &A, int batch_sz, const VVector &b, VVector &x)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    const ValueTypeB *b_ptr = b.raw();\n    ValueTypeB *x_ptr = x.raw() ;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n        multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr, batch_sz,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset =  A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n            multicolorGSSmoothCsrKernel_NAIVE_tex_batched<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr, batch_sz,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int eightwarps_per_block = threads_per_block / 4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>), hipFuncCachePreferL1);\n        multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int threads_per_block = 512;\n            const int eightwarps_per_block = threads_per_block / 4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>), hipFuncCachePreferL1);\n            multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n\n    //cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int blockrows_per_cta = threads_per_block / 3;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>), hipFuncCachePreferL1);\n        multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int blockrows_per_cta = threads_per_block / 3;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>), hipFuncCachePreferL1);\n            multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    int x_dirty_status = x.dirtybit;\n    int x_delayed = x.delayed_send;\n    int b_delayed = b.delayed_send;\n    VVector y(b.size());\n    y.set_block_dimx(b.get_block_dimx());\n    y.set_block_dimy(b.get_block_dimy());\n    y.tag = this->tag * 100 + 3;\n    amgx::thrust::copy(b.begin(), b.end(), y.begin());\n    cudaCheckError();\n    y.dirtybit = 0;\n    y.delayed_send = 1;\n    x.delayed_send = 1;\n    b.delayed_send = 1;\n\n    for (int color = 0; color < num_colors; color++)\n    {\n        Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeA) - 1.0f, A, x, (ValueTypeA)1.0f, y, separation_flag); // y= -A.x + y      for current color\n\n        if (separation_flag == A.getViewExterior()) { y.dirtybit = 0; }\n\n        Cusparse::bsrmv(color, (ValueTypeA)this->weight, A, this->Dinv, y, (ValueTypeA)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n\n        if (separation_flag == A.getViewExterior()) { x.dirtybit = 0; }\n    }\n\n    if (this->symFlag)\n    {\n        amgx::thrust::copy(b.begin(), b.end(), y.begin());\n\n        for (int color = num_colors - 1; color >= 0; color--)\n        {\n            Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeA) - 1.0f, A, x, (ValueTypeA)1.0f, y, separation_flag); // y= -A.x + y      for current color\n\n            if (separation_flag == A.getViewExterior()) { y.dirtybit = 0; }\n\n            Cusparse::bsrmv(color, (ValueTypeA)this->weight, A, this->Dinv, y, (ValueTypeA)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n\n            if (separation_flag == A.getViewExterior()) { x.dirtybit = 0; }\n        }\n\n        cudaCheckError();\n    }\n\n    x.delayed_send = x_delayed;\n    b.delayed_send = b_delayed;\n    x.dirtybit = x_dirty_status;\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n        this->m_explicit_A->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    bool latencyHiding = true;\n    ViewType flags;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = this->m_explicit_A->getViewExterior();\n    }\n    else\n    {\n        this->m_explicit_A->setViewInterior();\n        flags = this->m_explicit_A->getViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), 0.);\n        cudaCheckError();\n    }\n\n    if ( this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1 )\n    {\n        if (this->use_bsrxmv)\n        {\n            smooth_BxB(*this->m_explicit_A, b, x, flags);\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, flags);\n        }\n    }\n    else if ( this->m_explicit_A->get_block_dimx() == 3 && this->m_explicit_A->get_block_dimy() == 3 )\n    {\n        //if (this->use_bsrxmv)\n        smooth_BxB(*this->m_explicit_A, b, x, flags);\n        //else\n        //  smooth_3x3(*this->m_explicit_A, b, x, flags);\n    }\n    else if ( this->m_explicit_A->get_block_dimx() == 4 && this->m_explicit_A->get_block_dimy() == 4 )\n    {\n        if (this->use_bsrxmv)\n        {\n            smooth_BxB(*this->m_explicit_A, b, x, flags);\n        }\n        else\n        {\n            smooth_4x4(*this->m_explicit_A, b, x, flags);\n        }\n    }\n    else if (this->m_explicit_A->get_block_dimx() == this->m_explicit_A->get_block_dimy())\n    {\n        smooth_BxB(*this->m_explicit_A, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(~(this->m_explicit_A->getViewInterior()) & this->m_explicit_A->getViewExterior());\n\n        if (flags != 0)\n        {\n            if ( this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1 )\n            {\n                if (this->use_bsrxmv)\n                {\n                    smooth_BxB(*this->m_explicit_A, b, x, flags);\n                }\n                else\n                {\n                    smooth_1x1(*this->m_explicit_A, b, x, flags);\n                }\n            }\n            else if ( this->m_explicit_A->get_block_dimx() == 3 && this->m_explicit_A->get_block_dimy() == 3 )\n            {\n                smooth_BxB(*this->m_explicit_A, b, x, flags);\n            }\n            else if ( this->m_explicit_A->get_block_dimx() == 4 && this->m_explicit_A->get_block_dimy() == 4 )\n            {\n                if (this->use_bsrxmv)\n                {\n                    smooth_BxB(*this->m_explicit_A, b, x, flags);\n                }\n                else\n                {\n                    smooth_4x4(*this->m_explicit_A, b, x, flags);\n                }\n            }\n            else if (this->m_explicit_A->get_block_dimx() == this->m_explicit_A->get_block_dimy())\n            {\n                smooth_BxB(*this->m_explicit_A, b, x, flags);\n            }\n            else\n            {\n                FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    //if (!this->m_explicit_A->is_matrix_singleGPU() && x.delayed_send==0)\n    //  this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorGaussSeidelSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nhipStream_t\nMulticolorGaussSeidelSolver_Base<T_Config>::get_aux_stream()\n{\n    static std::shared_ptr<Stream> aux_stream;\n    if (!aux_stream)\n    {\n        aux_stream = std::make_shared<Stream>();\n    }\n\n    return aux_stream->get();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorGaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorGaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/multicolor_gauss_seidel_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/fixcolor_gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n\nnamespace amgx\n{\nnamespace fixcolor_gauss_seidel_solver\n{\n\n// -------------------------\n//  Kernels\n// -------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmooth3by3BlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[tid] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockGSSmooth4by4BlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockGSSmoothbBybBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ volatile char sharedc[];\n    volatile ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&sharedc[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    s_Amat[s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind] = e_out[t1 * tile_num + t2];\n                }\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmoothBlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices,\n        ValueTypeA *Dinv, const int num_block_rows)\n{\n    int cta_blockrow_id = threadIdx.x / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = threadIdx.x - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta];\n    int offset, s_offset;\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows && cta_blockrow_id < blockrows_per_cta)\n    {\n        // Store the diagonal\n        offset = blockrow_id * bsize_sq + mat_entry_index;\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        s_offset = cta_blockrow_id * bsize_sq;\n#define s_A(ROW,COL)   s_Amat[s_offset+ROW*bsize+COL]\n        ValueTypeA diag;\n\n        for (int row = 0; row < bsize; row++)\n        {\n            diag = 1.0 / s_A(row, row);\n\n            if ((i_ind == 0) && !(j_ind == row))\n            {\n                s_A(row, j_ind) = s_A(row, j_ind) * diag;\n            }\n\n            if ((i_ind != row) && !(j_ind == row))\n            {\n                s_A(i_ind, j_ind) = -(s_A(i_ind, row) * s_A(row, j_ind)) + s_A(i_ind, j_ind);\n            }\n\n            if (i_ind == 0)\n            {\n                s_A(j_ind, row) = (j_ind == row) ? diag : -(s_A(j_ind, row) * diag);\n            }\n        }\n\n        Dinv[offset] = s_A(i_ind, j_ind);\n        blockrow_id += (gridDim.x * blockDim.x) / bsize_sq;\n    }\n}\n\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int num_rows_per_line, const int color_num, const int num_block_rows, ValueTypeB *xout)\n{\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = tid >> log_bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n    int n1d2 =  num_rows_per_line * num_rows_per_line;\n    int pln, tid2, row, col;\n    int idx_1 = color_num & 1;\n    int idx_2 = (color_num & 2) >> 1;\n    int idx_3 = (color_num & 4) >> 2;\n\n    while (true)\n    {\n        pln = eighthwarp_id / n1d2;\n        tid2 = eighthwarp_id % n1d2;\n        row = tid2 / num_rows_per_line;\n        col = tid2 % num_rows_per_line;\n        i = 4 * n1d2 * (2 * pln + idx_1) + 2 * num_rows_per_line * (2 * row + idx_2) + 2 * col + idx_3;\n\n        //if (pln >= num_rows_per_line) return;\n        //if (row >= num_rows_per_line) return;\n        if (i > num_block_rows) { return; }\n\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        loadAsVector<bsize>(nonzero_values + dia_indices[i]*bsize * bsize + vec_entry_index * bsize, temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n                s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        eighthwarp_id +=  (gridDim.x * blockDim.x >> log_bsize);\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int bsize>\n__global__\nvoid multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int cta_blockrow_id = (threadIdx.x) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = threadIdx.x - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (blockrow_id < num_rows_per_color && cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = cta_blockrow_id * bsize;\n        offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = nonzero_values[offset + k];\n        }\n\n        //loadAsVector<bsize>(dia_values+offset,temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n                for (int k = 0; k < bsize; k++)\n                {\n                    temp[k] = nonzero_values[offset + k];\n                }\n\n                //loadAsVector<bsize>(nonzero_values+offset,temp);\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = Dinv[offset + k];\n        }\n\n        //loadAsVector<bsize>(Dinv+offset,temp);\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        blockrow_id +=  (gridDim.x * blockDim.x) / bsize;\n    }\n}\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\n// This is TERRIBLE in terms of memory access pattern\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid fixcolorGSSmoothCsrKernel_NAIVE_tex(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int num_rows_per_line, const int color_num, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int n1d2 =  num_rows_per_line * num_rows_per_line;\n    int pln, tid2, row, col;\n    int idx_1 = color_num & 1;\n    int idx_2 = (color_num & 2) >> 1;\n    int idx_3 = (color_num & 4) >> 2;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeB dia;\n\n    while (true)\n    {\n        pln = tid / n1d2;\n        tid2 = tid % n1d2;\n        row = tid2 / num_rows_per_line;\n        col = tid2 % num_rows_per_line;\n        i = 4 * n1d2 * (2 * pln + idx_1) + 2 * num_rows_per_line * (2 * row + idx_2) + 2 * col + idx_3;\n\n        //if (pln >= num_rows_per_line) return;\n        //if (row >= num_rows_per_line) return;\n        if (i > num_block_rows) { return; }\n\n        bmAx = b[i];\n        xin = x[i];\n        dia = nonzero_values[diag[i]];\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[jcol];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[i] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n    }\n}\n\n\n// -------------------\n// Methods\n// -------------------\n\n\n// Constructor\ntemplate<class T_Config>\nFixcolorGaussSeidelSolver_Base<T_Config>::FixcolorGaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    symFlag = cfg.AMG_Config::template getParameter<int>(\"symmetric_GS\", cfg_scope);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->use_bsrxmv = 1;\n    }\n    else\n    {\n        this->use_bsrxmv = 0;\n    }\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor GaussSeidel smoother\\n\");\n    }\n\n    num_colors = 8;\n}\n\n// Destructor\ntemplate<class T_Config>\nFixcolorGaussSeidelSolver_Base<T_Config>::~FixcolorGaussSeidelSolver_Base()\n{\n}\n\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->use_bsrxmv << std::endl;\n    std::cout << \"symmetric_GS = \" << this->symFlag << std::endl;\n}\n\n\ntemplate<class T_Config>\nvoid FixcolorGaussSeidelSolver_Base<T_Config>::computeDinv(Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        computeDinv_2x2(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        computeDinv_5x5(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        computeDinv_bxb(A, A.get_block_dimx());\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for FixcolorGaussSeidelSolver computeEinv\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n// Method to compute the diagonal matrix E in DILU smoother\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>, cudaFuncCachePreferL1);\n    setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize - 1) / 4 + 1) * ((bsize - 1) / 4 + 1));\n    ValueTypeA *temp_ptr = temp.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block>, cudaFuncCachePreferL1);\n    setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block> <<< num_blocks, threads_per_block, sizeof(ValueTypeA)*bsize *bsize *halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize * bsize, temp_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 9;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 9, 3, 9 >, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 9, 3, 9 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 4;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 4, 2, 4 >, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 4, 2, 4 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 25;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 25, 5, 25 >, cudaFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 25, 5, 25 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"FixcolorGaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (!this->use_bsrxmv)\n    {\n        FatalError(\"Use bsrxmv implementation, old implementation is buggy for diagonal matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    computeDinv(*A_as_matrix);\n}\n\n//\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    int n = A.get_num_rows();\n    int num_rows_per_line;\n    num_rows_per_line = (int) ceil(cbrt((double)n) / 2);\n\n    for (int i = 0; i < this->num_colors; i++)\n    {\n        int num_rows_per_color = n / this->num_colors + 1;\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        cudaFuncSetCacheConfig(fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n        fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n    }\n\n    if (this->symFlag == 1)\n    {\n        int num_rows_per_color = n / this->num_colors + 1;\n\n        for (int i = this->num_colors - 1; i >= 0; i--)\n        {\n            const int threads_per_block = 512;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            cudaFuncSetCacheConfig(fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>, cudaFuncCachePreferL1);\n            fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    //const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    int n = A.get_num_rows();\n    int num_rows_per_line;\n    num_rows_per_line = (int) ceil(cbrt((double)n) / 2);\n\n    for (int i = 0; i < this->num_colors; i++)\n    {\n        const IndexType num_rows_per_color = n / this->num_colors + 1;\n        const int threads_per_block = 512;\n        const int eightwarps_per_block = threads_per_block / 4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n        cudaFuncSetCacheConfig(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>, cudaFuncCachePreferL1);\n        multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = this->num_colors - 1; i >= 0; i--)\n        {\n            const IndexType num_rows_per_color = n / this->num_colors + 1;\n            const int threads_per_block = 512;\n            const int eightwarps_per_block = threads_per_block / 4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n            cudaFuncSetCacheConfig(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>, cudaFuncCachePreferL1);\n            multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n\n    if (!A.hasProps(COLORING)) { FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS); }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    //TODO: generate required indices on the fly since coloring is not requested for this solver\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    int num_colors = this->num_colors;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == A_as_matrix->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color <= 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int blockrows_per_cta = threads_per_block / 3;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n        cudaFuncSetCacheConfig(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>, cudaFuncCachePreferL1);\n        multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == A_as_matrix->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int blockrows_per_cta = threads_per_block / 3;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n            cudaFuncSetCacheConfig(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>, cudaFuncCachePreferL1);\n            multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> >::smooth_BxB(Matrix<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &A, Vector<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &b, Vector<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &x, ViewType separation_flag)\n{\n    FatalError(\"Mixed precision is not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    if (!A.hasProps(COLORING)) { FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS); }\n\n    int num_colors = this->num_colors;\n    VVector y(b.size());\n    amgx::thrust::copy(b.begin(), b.end(), y.begin());                                                                          // y= b             for all colors\n    cudaCheckError();\n\n    for (int color = 0; color < num_colors; color++)\n    {\n        Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeB) - 1.0f, A, x, (ValueTypeB)1.0f, y, separation_flag); // y= -A.x + y      for current color\n        Cusparse::bsrmv(color, (ValueTypeB)this->weight, A, this->Dinv, y, (ValueTypeB)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n    }\n\n    cudaCheckError();\n\n    if (this->symFlag)\n    {\n        y = b;                                                                                        // y= b             for all colors\n\n        for (int color = num_colors - 1; color >= 0; color--)\n        {\n            Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeB) - 1.0f, A, x, (ValueTypeB)1.0f, y, separation_flag); // y= -A.x + y      for current color\n            Cusparse::bsrmv(color, (ValueTypeB)this->weight, A, this->Dinv, y, (ValueTypeB)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n        }\n    }\n\n    cudaCheckError();\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!A_as_matrix->is_matrix_singleGPU())\n    {\n        A_as_matrix->manager->exchange_halo_async(x, x.tag);\n        A_as_matrix->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (A_as_matrix->getViewExterior() == A_as_matrix->getViewInterior())\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    if ((A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4) || (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1))\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = A_as_matrix->currentView();\n    bool latencyHiding = true;\n    ViewType flags;\n\n    if (A_as_matrix->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        A_as_matrix->setViewExterior();\n        flags = A_as_matrix->getViewExterior();\n    }\n    else\n    {\n        flags = A_as_matrix->getViewInterior();\n        A_as_matrix->setViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), 0.);\n        cudaCheckError();\n    }\n\n    if (this->use_bsrxmv && A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n    {\n        smooth_BxB(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4 )\n    {\n        smooth_4x4(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 3 && A_as_matrix->get_block_dimy() == 3 )\n    {\n        smooth_3x3(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1 )\n    {\n        smooth_1x1(*A_as_matrix, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(~(A_as_matrix->getViewInterior()) & A_as_matrix->getViewExterior());\n\n        if (flags != 0)\n        {\n            if (this->use_bsrxmv && A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n            {\n                smooth_BxB(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4 )\n            {\n                smooth_4x4(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 3 && A_as_matrix->get_block_dimy() == 3 )\n            {\n                smooth_3x3(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1 )\n            {\n                smooth_1x1(*A_as_matrix, b, x, flags);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_finalize(VVector &b, VVector &x)\n{\n}\n\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class FixcolorGaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class FixcolorGaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/fixcolor_gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n\nnamespace amgx\n{\nnamespace fixcolor_gauss_seidel_solver\n{\n\n// -------------------------\n//  Kernels\n// -------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmooth3by3BlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[tid] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockGSSmooth4by4BlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        compute_block_inverse_row_major<IndexType, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockGSSmoothbBybBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ volatile char sharedc[];\n    volatile ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&sharedc[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    s_Amat[s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind] = e_out[t1 * tile_num + t2];\n                }\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int bsize, int bsize_sq>\n__global__\nvoid setupBlockGSSmoothBlockDiaCsrKernel_V2(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices,\n        ValueTypeA *Dinv, const int num_block_rows)\n{\n    int cta_blockrow_id = threadIdx.x / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = threadIdx.x - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta];\n    int offset, s_offset;\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows && cta_blockrow_id < blockrows_per_cta)\n    {\n        // Store the diagonal\n        offset = blockrow_id * bsize_sq + mat_entry_index;\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        s_Amat[threadIdx.x] = e_out;\n        s_offset = cta_blockrow_id * bsize_sq;\n#define s_A(ROW,COL)   s_Amat[s_offset+ROW*bsize+COL]\n        ValueTypeA diag;\n\n        for (int row = 0; row < bsize; row++)\n        {\n            diag = 1.0 / s_A(row, row);\n\n            if ((i_ind == 0) && !(j_ind == row))\n            {\n                s_A(row, j_ind) = s_A(row, j_ind) * diag;\n            }\n\n            if ((i_ind != row) && !(j_ind == row))\n            {\n                s_A(i_ind, j_ind) = -(s_A(i_ind, row) * s_A(row, j_ind)) + s_A(i_ind, j_ind);\n            }\n\n            if (i_ind == 0)\n            {\n                s_A(j_ind, row) = (j_ind == row) ? diag : -(s_A(j_ind, row) * diag);\n            }\n        }\n\n        Dinv[offset] = s_A(i_ind, j_ind);\n        blockrow_id += (gridDim.x * blockDim.x) / bsize_sq;\n    }\n}\n\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int num_rows_per_line, const int color_num, const int num_block_rows, ValueTypeB *xout)\n{\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = tid >> log_bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n    int n1d2 =  num_rows_per_line * num_rows_per_line;\n    int pln, tid2, row, col;\n    int idx_1 = color_num & 1;\n    int idx_2 = (color_num & 2) >> 1;\n    int idx_3 = (color_num & 4) >> 2;\n\n    while (true)\n    {\n        pln = eighthwarp_id / n1d2;\n        tid2 = eighthwarp_id % n1d2;\n        row = tid2 / num_rows_per_line;\n        col = tid2 % num_rows_per_line;\n        i = 4 * n1d2 * (2 * pln + idx_1) + 2 * num_rows_per_line * (2 * row + idx_2) + 2 * col + idx_3;\n\n        //if (pln >= num_rows_per_line) return;\n        //if (row >= num_rows_per_line) return;\n        if (i > num_block_rows) { return; }\n\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = block_eighthwarp_id * bsize;\n        loadAsVector<bsize>(nonzero_values + dia_indices[i]*bsize * bsize + vec_entry_index * bsize, temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n                s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        eighthwarp_id +=  (gridDim.x * blockDim.x >> log_bsize);\n    }\n}\n\n// Kernel to smooth, NAIVE implementation with texture\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int bsize>\n__global__\nvoid multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int *sorted_rows_by_color, const int num_rows_per_color, const int num_block_rows, ValueTypeB *xout)\n{\n    int cta_blockrow_id = (threadIdx.x) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = threadIdx.x - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (blockrow_id < num_rows_per_color && cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        s_xtemp[threadIdx.x] = xin;\n        // Load dia_values and do matrix multiply\n        s_offset = cta_blockrow_id * bsize;\n        offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = nonzero_values[offset + k];\n        }\n\n        //loadAsVector<bsize>(dia_values+offset,temp);\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx -= temp[m] * s_xtemp[s_offset + m];\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                s_xtemp[threadIdx.x] = x[offset];\n                // Load nonzero_values\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n                for (int k = 0; k < bsize; k++)\n                {\n                    temp[k] = nonzero_values[offset + k];\n                }\n\n                //loadAsVector<bsize>(nonzero_values+offset,temp);\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_xtemp[s_offset + m];\n                }\n            }\n        }\n\n        s_xtemp[threadIdx.x] = bmAx;\n        bmAx = 0.;\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n#pragma unroll\n\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k] = Dinv[offset + k];\n        }\n\n        //loadAsVector<bsize>(Dinv+offset,temp);\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset + m];\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + weight * bmAx;\n        blockrow_id +=  (gridDim.x * blockDim.x) / bsize;\n    }\n}\n\n\n\n// Kernel to smooth, NAIVE implementation with texture\n// This is TERRIBLE in terms of memory access pattern\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__\nvoid fixcolorGSSmoothCsrKernel_NAIVE_tex(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *diag, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, ValueTypeB weight, const int num_rows_per_line, const int color_num, const int num_block_rows, ValueTypeB *xout)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int n1d2 =  num_rows_per_line * num_rows_per_line;\n    int pln, tid2, row, col;\n    int idx_1 = color_num & 1;\n    int idx_2 = (color_num & 2) >> 1;\n    int idx_3 = (color_num & 4) >> 2;\n    int i;\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp, s_xtemp;\n    ValueTypeB dia;\n\n    while (true)\n    {\n        pln = tid / n1d2;\n        tid2 = tid % n1d2;\n        row = tid2 / num_rows_per_line;\n        col = tid2 % num_rows_per_line;\n        i = 4 * n1d2 * (2 * pln + idx_1) + 2 * num_rows_per_line * (2 * row + idx_2) + 2 * col + idx_3;\n\n        //if (pln >= num_rows_per_line) return;\n        //if (row >= num_rows_per_line) return;\n        if (i > num_block_rows) { return; }\n\n        bmAx = b[i];\n        xin = x[i];\n        dia = nonzero_values[diag[i]];\n        bmAx -= xin * dia;\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n\n            if (jcol != i)\n            {\n                s_xtemp = x[jcol];\n                temp = nonzero_values[jind];\n                bmAx -= temp * s_xtemp;\n            }\n        }\n\n        bmAx /= dia;\n        xout[i] = xin + weight * bmAx;\n        tid +=  gridDim.x * blockDim.x;\n    }\n}\n\n\n// -------------------\n// Methods\n// -------------------\n\n\n// Constructor\ntemplate<class T_Config>\nFixcolorGaussSeidelSolver_Base<T_Config>::FixcolorGaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    symFlag = cfg.AMG_Config::template getParameter<int>(\"symmetric_GS\", cfg_scope);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->use_bsrxmv = 1;\n    }\n    else\n    {\n        this->use_bsrxmv = 0;\n    }\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor GaussSeidel smoother\\n\");\n    }\n\n    num_colors = 8;\n}\n\n// Destructor\ntemplate<class T_Config>\nFixcolorGaussSeidelSolver_Base<T_Config>::~FixcolorGaussSeidelSolver_Base()\n{\n}\n\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->use_bsrxmv << std::endl;\n    std::cout << \"symmetric_GS = \" << this->symFlag << std::endl;\n}\n\n\ntemplate<class T_Config>\nvoid FixcolorGaussSeidelSolver_Base<T_Config>::computeDinv(Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        computeDinv_2x2(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        computeDinv_5x5(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        computeDinv_bxb(A, A.get_block_dimx());\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for FixcolorGaussSeidelSolver computeEinv\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n// Method to compute the diagonal matrix E in DILU smoother\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor GS smoother not implemented with host format, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>), hipFuncCachePreferL1);\n    setupBlockGSSmooth4by4BlockDiaCsrKernel_V2<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_bxb(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize - 1) / 4 + 1) * ((bsize - 1) / 4 + 1));\n    ValueTypeA *temp_ptr = temp.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block>), hipFuncCachePreferL1);\n    setupBlockGSSmoothbBybBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block> <<< num_blocks, threads_per_block, sizeof(ValueTypeA)*bsize *bsize *halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize * bsize, temp_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 9;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 9, 3, 9 >), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 9, 3, 9 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_2x2(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 4;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 4, 2, 4 >), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 4, 2, 4 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_5x5(const Matrix_d &A)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block / 25;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 25, 5, 25 >), hipFuncCachePreferL1);\n    setupBlockGSSmooth3by3BlockDiaCsrKernel < IndexType, ValueTypeA, blockrows_per_cta, 32 / 25, 5, 25 > <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"FixcolorGaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (!this->use_bsrxmv)\n    {\n        FatalError(\"Use bsrxmv implementation, old implementation is buggy for diagonal matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    computeDinv(*A_as_matrix);\n}\n\n//\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    FatalError(\"Haven't implemented Block Multicolor Gauss-Seidel smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const IndexType *A_diag_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    int n = A.get_num_rows();\n    int num_rows_per_line;\n    num_rows_per_line = (int) ceil(cbrt((double)n) / 2);\n\n    for (int i = 0; i < this->num_colors; i++)\n    {\n        int num_rows_per_color = n / this->num_colors + 1;\n        const int threads_per_block = 512;\n        //const int eightwarps_per_block = threads_per_block/4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n        fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n    }\n\n    if (this->symFlag == 1)\n    {\n        int num_rows_per_color = n / this->num_colors + 1;\n\n        for (int i = this->num_colors - 1; i >= 0; i--)\n        {\n            const int threads_per_block = 512;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / threads_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB>), hipFuncCachePreferL1);\n            fixcolorGSSmoothCsrKernel_NAIVE_tex<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_diag_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n        }\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    //const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    int n = A.get_num_rows();\n    int num_rows_per_line;\n    num_rows_per_line = (int) ceil(cbrt((double)n) / 2);\n\n    for (int i = 0; i < this->num_colors; i++)\n    {\n        const IndexType num_rows_per_color = n / this->num_colors + 1;\n        const int threads_per_block = 512;\n        const int eightwarps_per_block = threads_per_block / 4;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>), hipFuncCachePreferL1);\n        multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = this->num_colors - 1; i >= 0; i--)\n        {\n            const IndexType num_rows_per_color = n / this->num_colors + 1;\n            const int threads_per_block = 512;\n            const int eightwarps_per_block = threads_per_block / 4;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int)(num_rows_per_color - 1) / eightwarps_per_block + 1 );\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>), hipFuncCachePreferL1);\n            multicolorGSSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, num_rows_per_line, i, n, x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_3x3(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flag)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n\n    if (!A.hasProps(COLORING)) { FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS); }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    //TODO: generate required indices on the fly since coloring is not requested for this solver\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const int threads_per_block = 64;\n    int num_colors = this->num_colors;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flag == A_as_matrix->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color <= 0) { continue; }\n\n        const int threads_per_block = 512;\n        const int blockrows_per_cta = threads_per_block / 3;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>), hipFuncCachePreferL1);\n        multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n         b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n        cudaCheckError();\n    }\n\n    if (this->symFlag == 1)\n    {\n        for (int i = num_colors - 1; i >= 0; i--)\n        {\n            const IndexType color_offset = ((separation_flag & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n            const IndexType num_rows_per_color = ((separation_flag == A_as_matrix->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n            if (num_rows_per_color == 0) { continue; }\n\n            const int blockrows_per_cta = threads_per_block / 3;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color - 1) / blockrows_per_cta + 1);\n            hipFuncSetCacheConfig(reinterpret_cast<const void*>(multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3>), hipFuncCachePreferL1);\n            multicolorGSSmoothBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 3> <<< num_blocks, threads_per_block>>>\n            (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n             b_ptr, x_ptr, this->weight, A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color, A.get_num_rows(), x_ptr);\n            cudaCheckError();\n        } // End of loop over colors\n    } // End of if symFlag\n}\n\ntemplate <>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> >::smooth_BxB(Matrix<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &A, Vector<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &b, Vector<TemplateConfig<AMGX_device, AMGX_vecDouble, AMGX_matFloat, AMGX_indInt> > &x, ViewType separation_flag)\n{\n    FatalError(\"Mixed precision is not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid FixcolorGaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag)\n{\n    if (!A.hasProps(COLORING)) { FatalError(\"Matrix is not colored, exiting\", AMGX_ERR_BAD_PARAMETERS); }\n\n    int num_colors = this->num_colors;\n    VVector y(b.size());\n    amgx::thrust::copy(b.begin(), b.end(), y.begin());                                                                          // y= b             for all colors\n    cudaCheckError();\n\n    for (int color = 0; color < num_colors; color++)\n    {\n        Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeB) - 1.0f, A, x, (ValueTypeB)1.0f, y, separation_flag); // y= -A.x + y      for current color\n        Cusparse::bsrmv(color, (ValueTypeB)this->weight, A, this->Dinv, y, (ValueTypeB)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n    }\n\n    cudaCheckError();\n\n    if (this->symFlag)\n    {\n        y = b;                                                                                        // y= b             for all colors\n\n        for (int color = num_colors - 1; color >= 0; color--)\n        {\n            Cusparse::bsrmv(Cusparse::ALL_COLORS, color, (ValueTypeB) - 1.0f, A, x, (ValueTypeB)1.0f, y, separation_flag); // y= -A.x + y      for current color\n            Cusparse::bsrmv(color, (ValueTypeB)this->weight, A, this->Dinv, y, (ValueTypeB)1.0f, x, separation_flag);                // x= w*Dinv.y + x  for current color\n        }\n    }\n\n    cudaCheckError();\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!A_as_matrix->is_matrix_singleGPU())\n    {\n        A_as_matrix->manager->exchange_halo_async(x, x.tag);\n        A_as_matrix->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (A_as_matrix->getViewExterior() == A_as_matrix->getViewInterior())\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    if ((A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4) || (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1))\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = A_as_matrix->currentView();\n    bool latencyHiding = true;\n    ViewType flags;\n\n    if (A_as_matrix->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        A_as_matrix->setViewExterior();\n        flags = A_as_matrix->getViewExterior();\n    }\n    else\n    {\n        flags = A_as_matrix->getViewInterior();\n        A_as_matrix->setViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), 0.);\n        cudaCheckError();\n    }\n\n    if (this->use_bsrxmv && A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n    {\n        smooth_BxB(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4 )\n    {\n        smooth_4x4(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 3 && A_as_matrix->get_block_dimy() == 3 )\n    {\n        smooth_3x3(*A_as_matrix, b, x, flags);\n    }\n    else if ( A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1 )\n    {\n        smooth_1x1(*A_as_matrix, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MulticolorGaussSeidelSolver smooth\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        if (!A_as_matrix->is_matrix_singleGPU())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n            A_as_matrix->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(~(A_as_matrix->getViewInterior()) & A_as_matrix->getViewExterior());\n\n        if (flags != 0)\n        {\n            if (this->use_bsrxmv && A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n            {\n                smooth_BxB(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4 )\n            {\n                smooth_4x4(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 3 && A_as_matrix->get_block_dimy() == 3 )\n            {\n                smooth_3x3(*A_as_matrix, b, x, flags);\n            }\n            else if ( A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1 )\n            {\n                smooth_1x1(*A_as_matrix, b, x, flags);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nFixcolorGaussSeidelSolver_Base<T_Config>::solve_finalize(VVector &b, VVector &x)\n{\n}\n\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class FixcolorGaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class FixcolorGaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/fixcolor_gauss_seidel_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/block_jacobi_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#include <texture.h>\n#include <amgx_cusparse.h>\n#include <ostream>\n\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\nnamespace block_jacobi_solver\n{\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    double omega;\n    jacobi_presmooth_functor( double omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? b * omega / d : b * omega / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    double omega;\n    jacobi_postsmooth_functor( double omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d = types::util<ValueTypeA>::get_one() / d;\n        b = b - y;\n        b = b * omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\ntemplate<typename T>\n__device__ __forceinline__ T fmnaOp (T a, T b, T c)\n{\n    return -(a * b) + c;\n}\ntemplate<typename T>\n__device__ __forceinline__ T mulOp (T a, T b)\n{\n    return a * b;\n}\ntemplate<typename T>\n__device__ __forceinline__ T rcpOp (T a)\n{\n    return 1.0 / (isNotCloseToZero(a) ? a : epsilon(a));\n}\ntemplate<typename T>\n__device__ __forceinline__ T absOp (T a)\n{\n    return fabs(a);\n}\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate<typename T1, typename T2, int N>\n__global__ void matinv_matrix_per_thread_pivot (const T1 *A, T2 *Ainv, int batch)\n{\n#define A(row,col)     A[(col)*N+(row)]\n#define AA(row,col)    AA[(col)*N+(row)]\n#define Ainv(row,col)  Ainv[(col)*N+(row)]\n    const int blkNum = blockIdx.x * blockDim.x + threadIdx.x;\n    int perm0, perm1, perm2, perm3;\n    int icol0, icol1, icol2, icol3;\n    T2 AA00, AA01, AA02, AA03, AA10, AA11, AA12, AA13;\n    T2 AA20, AA21, AA22, AA23, AA30, AA31, AA32, AA33;\n    T2 p, t;\n    int i, pvt;\n    A    += blkNum * N * N;\n    Ainv += blkNum * N * N;\n\n    if (blkNum < batch)\n    {\n        AA00 = A[0];\n        AA10 = A[1];\n        AA20 = A[2];\n        AA30 = A[3];\n        AA01 = A[4];\n        AA11 = A[5];\n        AA21 = A[6];\n        AA31 = A[7];\n        AA02 = A[8];\n        AA12 = A[9];\n        AA22 = A[10];\n        AA32 = A[11];\n        AA03 = A[12];\n        AA13 = A[13];\n        AA23 = A[14];\n        AA33 = A[15];\n        perm0 = 0;\n        perm1 = 1;\n        perm2 = 2;\n        perm3 = 3;\n        /****************** iteration 0 ***********/\n        /* search pivot row */\n        p = absOp (AA00);\n        pvt = 0;\n        t = absOp (AA10);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 1;\n        }\n\n        t = absOp (AA20);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 2;\n        }\n\n        t = absOp (AA30);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 0 */\n        if (pvt == 1)\n        {\n            t = AA00;\n            AA00 = AA10;\n            AA10 = t;\n            t = AA01;\n            AA01 = AA11;\n            AA11 = t;\n            t = AA02;\n            AA02 = AA12;\n            AA12 = t;\n            t = AA03;\n            AA03 = AA13;\n            AA13 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm1;\n            perm1 = i;\n        }\n\n        if (pvt == 2)\n        {\n            t = AA00;\n            AA00 = AA20;\n            AA20 = t;\n            t = AA01;\n            AA01 = AA21;\n            AA21 = t;\n            t = AA02;\n            AA02 = AA22;\n            AA22 = t;\n            t = AA03;\n            AA03 = AA23;\n            AA23 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm2;\n            perm2 = i;\n        }\n\n        if (pvt == 3)\n        {\n            t = AA00;\n            AA00 = AA30;\n            AA30 = t;\n            t = AA01;\n            AA01 = AA31;\n            AA31 = t;\n            t = AA02;\n            AA02 = AA32;\n            AA32 = t;\n            t = AA03;\n            AA03 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA00);\n        icol0 = perm0;\n        AA00 = t;\n        AA01 = mulOp (t, AA01);\n        AA02 = mulOp (t, AA02);\n        AA03 = mulOp (t, AA03);\n        /* eliminate above and below current row */\n        t = AA10;\n        AA10 = mulOp (-t, AA00);\n        AA11 = fmnaOp (t, AA01, AA11);\n        AA12 = fmnaOp (t, AA02, AA12);\n        AA13 = fmnaOp (t, AA03, AA13);\n        t = AA20;\n        AA20 = mulOp (-t, AA00);\n        AA21 = fmnaOp (t, AA01, AA21);\n        AA22 = fmnaOp (t, AA02, AA22);\n        AA23 = fmnaOp (t, AA03, AA23);\n        t = AA30;\n        AA30 = mulOp (-t, AA00);\n        AA31 = fmnaOp (t, AA01, AA31);\n        AA32 = fmnaOp (t, AA02, AA32);\n        AA33 = fmnaOp (t, AA03, AA33);\n        /****************** iteration 1 ***********/\n        /* search pivot row */\n        p = absOp (AA11);\n        pvt = 1;\n        t = absOp (AA21);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 2;\n        }\n\n        t = absOp (AA31);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 1 */\n        if (pvt == 2)\n        {\n            t = AA10;\n            AA10 = AA20;\n            AA20 = t;\n            t = AA11;\n            AA11 = AA21;\n            AA21 = t;\n            t = AA12;\n            AA12 = AA22;\n            AA22 = t;\n            t = AA13;\n            AA13 = AA23;\n            AA23 = t;\n            /* update permutation vector based on row swap */\n            i = perm1;\n            perm1 = perm2;\n            perm2 = i;\n        }\n        else if (pvt == 3)\n        {\n            t = AA10;\n            AA10 = AA30;\n            AA30 = t;\n            t = AA11;\n            AA11 = AA31;\n            AA31 = t;\n            t = AA12;\n            AA12 = AA32;\n            AA32 = t;\n            t = AA13;\n            AA13 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm1;\n            perm1 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA11);\n        icol1 = perm1;\n        AA10 = mulOp (t, AA10);\n        AA11 = t;\n        AA12 = mulOp (t, AA12);\n        AA13 = mulOp (t, AA13);\n        /* eliminate above and below current row */\n        t = AA01;\n        AA00 = fmnaOp (t, AA10, AA00);\n        AA01 = mulOp (-t, AA11);\n        AA02 = fmnaOp (t, AA12, AA02);\n        AA03 = fmnaOp (t, AA13, AA03);\n        t = AA21;\n        AA20 = fmnaOp (t, AA10, AA20);\n        AA21 = mulOp (-t, AA11);\n        AA22 = fmnaOp (t, AA12, AA22);\n        AA23 = fmnaOp (t, AA13, AA23);\n        t = AA31;\n        AA30 = fmnaOp (t, AA10, AA30);\n        AA31 = mulOp (-t, AA11);\n        AA32 = fmnaOp (t, AA12, AA32);\n        AA33 = fmnaOp (t, AA13, AA33);\n        /****************** iteration 2 ****************/\n        /* search pivot row */\n        p = absOp (AA22);\n        pvt = 2;\n        t = absOp (AA32);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 2 */\n        if (pvt == 3)\n        {\n            t = AA20;\n            AA20 = AA30;\n            AA30 = t;\n            t = AA21;\n            AA21 = AA31;\n            AA31 = t;\n            t = AA22;\n            AA22 = AA32;\n            AA32 = t;\n            t = AA23;\n            AA23 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm2;\n            perm2 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA22);\n        icol2 = perm2;\n        AA20 = mulOp (t, AA20);\n        AA21 = mulOp (t, AA21);\n        AA22 = t;\n        AA23 = mulOp (t, AA23);\n        /* eliminate above and below current row */\n        t = AA02;\n        AA00 = fmnaOp (t, AA20, AA00);\n        AA01 = fmnaOp (t, AA21, AA01);\n        AA02 = mulOp (-t, AA22);\n        AA03 = fmnaOp (t, AA23, AA03);\n        t = AA12;\n        AA10 = fmnaOp (t, AA20, AA10);\n        AA11 = fmnaOp (t, AA21, AA11);\n        AA12 = mulOp (-t, AA22);\n        AA13 = fmnaOp (t, AA23, AA13);\n        t = AA32;\n        AA30 = fmnaOp (t, AA20, AA30);\n        AA31 = fmnaOp (t, AA21, AA31);\n        AA32 = mulOp (-t, AA22);\n        AA33 = fmnaOp (t, AA23, AA33);\n        /****************** iteration 3 ****************/\n        /* scale current row */\n        t = rcpOp (AA33);\n        icol3 = perm3;\n        AA30 = mulOp (t, AA30);\n        AA31 = mulOp (t, AA31);\n        AA32 = mulOp (t, AA32);\n        AA33 = t;\n        /* eliminate above and below current row */\n        t = AA03;\n        AA00 = fmnaOp (t, AA30, AA00);\n        AA01 = fmnaOp (t, AA31, AA01);\n        AA02 = fmnaOp (t, AA32, AA02);\n        AA03 = mulOp (-t, AA33);\n        t = AA13;\n        AA10 = fmnaOp (t, AA30, AA10);\n        AA11 = fmnaOp (t, AA31, AA11);\n        AA12 = fmnaOp (t, AA32, AA12);\n        AA13 = mulOp (-t, AA33);\n        t = AA23;\n        AA20 = fmnaOp (t, AA30, AA20);\n        AA21 = fmnaOp (t, AA31, AA21);\n        AA22 = fmnaOp (t, AA32, AA22);\n        AA23 = mulOp (-t, AA33);\n        /* sort columns into the correct order */\n        Ainv(0, icol0) = AA00;\n        Ainv(1, icol0) = AA10;\n        Ainv(2, icol0) = AA20;\n        Ainv(3, icol0) = AA30;\n        Ainv(0, icol1) = AA01;\n        Ainv(1, icol1) = AA11;\n        Ainv(2, icol1) = AA21;\n        Ainv(3, icol1) = AA31;\n        Ainv(0, icol2) = AA02;\n        Ainv(1, icol2) = AA12;\n        Ainv(2, icol2) = AA22;\n        Ainv(3, icol2) = AA32;\n        Ainv(0, icol3) = AA03;\n        Ainv(1, icol3) = AA13;\n        Ainv(2, icol3) = AA23;\n        Ainv(3, icol3) = AA33;\n    }\n}\n\n\ntemplate<typename T, int N>\n__global__ void matinv_matrix_per_thread_no_pivot (const T *A, T *Ainv, int batch)\n{\n#define A(row,col)     A[(col)*N+(row)]\n#define AA(row,col)    AA[(col)*N+(row)]\n#define Ainv(row,col)  Ainv[(col)*N+(row)]\n    const int blkNum = blockIdx.x * blockDim.x + threadIdx.x;\n    T AA00, AA01, AA02, AA03, AA10, AA11, AA12, AA13;\n    T AA20, AA21, AA22, AA23, AA30, AA31, AA32, AA33;\n    T t;\n    A    += blkNum * N * N;\n    Ainv += blkNum * N * N;\n\n    if (blkNum < batch)\n    {\n        AA00 = A[0];\n        AA10 = A[1];\n        AA20 = A[2];\n        AA30 = A[3];\n        AA01 = A[4];\n        AA11 = A[5];\n        AA21 = A[6];\n        AA31 = A[7];\n        AA02 = A[8];\n        AA12 = A[9];\n        AA22 = A[10];\n        AA32 = A[11];\n        AA03 = A[12];\n        AA13 = A[13];\n        AA23 = A[14];\n        AA33 = A[15];\n        /****************** iteration 0 ***********/\n        /* search pivot row */\n        t = 1.0 / (AA00);\n        AA00 = t;\n        AA01 = t * AA01;\n        AA02 = t * AA02;\n        AA03 = t * AA03;\n        /* eliminate above and below current row */\n        t = AA10;\n        AA10 = -t * AA00;\n        AA11 = fmnaOp (t, AA01, AA11);\n        AA12 = fmnaOp (t, AA02, AA12);\n        AA13 = fmnaOp (t, AA03, AA13);\n        t = AA20;\n        AA20 = -t * AA00;\n        AA21 = fmnaOp (t, AA01, AA21);\n        AA22 = fmnaOp (t, AA02, AA22);\n        AA23 = fmnaOp (t, AA03, AA23);\n        t = AA30;\n        AA30 = -t * AA00;\n        AA31 = fmnaOp (t, AA01, AA31);\n        AA32 = fmnaOp (t, AA02, AA32);\n        AA33 = fmnaOp (t, AA03, AA33);\n        /****************** iteration 1 ***********/\n        /* scale current row */\n        t = 1.0 / (AA11);\n        AA10 = t * AA10;\n        AA11 = t;\n        AA12 = t * AA12;\n        AA13 = t * AA13;\n        /* eliminate above and below current row */\n        t = AA01;\n        AA00 = fmnaOp (t, AA10, AA00);\n        AA01 = -t * AA11;\n        AA02 = fmnaOp (t, AA12, AA02);\n        AA03 = fmnaOp (t, AA13, AA03);\n        t = AA21;\n        AA20 = fmnaOp (t, AA10, AA20);\n        AA21 = -t * AA11;\n        AA22 = fmnaOp (t, AA12, AA22);\n        AA23 = fmnaOp (t, AA13, AA23);\n        t = AA31;\n        AA30 = fmnaOp (t, AA10, AA30);\n        AA31 = -t * AA11;\n        AA32 = fmnaOp (t, AA12, AA32);\n        AA33 = fmnaOp (t, AA13, AA33);\n        /****************** iteration 2 ****************/\n        /* scale current row */\n        t = 1.0 / (AA22);\n        AA20 = t * AA20;\n        AA21 = t * AA21;\n        AA22 = t;\n        AA23 = t * AA23;\n        /* eliminate above and below current row */\n        t = AA02;\n        AA00 = fmnaOp (t, AA20, AA00);\n        AA01 = fmnaOp (t, AA21, AA01);\n        AA02 = -t * AA22;\n        AA03 = fmnaOp (t, AA23, AA03);\n        t = AA12;\n        AA10 = fmnaOp (t, AA20, AA10);\n        AA11 = fmnaOp (t, AA21, AA11);\n        AA12 = -t * AA22;\n        AA13 = fmnaOp (t, AA23, AA13);\n        t = AA32;\n        AA30 = fmnaOp (t, AA20, AA30);\n        AA31 = fmnaOp (t, AA21, AA31);\n        AA32 = -t * AA22;\n        AA33 = fmnaOp (t, AA23, AA33);\n        /****************** iteration 3 ****************/\n        /* scale current row */\n        t = 1.0 / (AA33);\n        AA30 =  t * AA30;\n        AA31 =  t * AA31;\n        AA32 =  t * AA32;\n        AA33 = t;\n        /* eliminate above and below current row */\n        t = AA03;\n        AA00 = fmnaOp (t, AA30, AA00);\n        AA01 = fmnaOp (t, AA31, AA01);\n        AA02 = fmnaOp (t, AA32, AA02);\n        AA03 = -t * AA33;\n        t = AA13;\n        AA10 = fmnaOp (t, AA30, AA10);\n        AA11 = fmnaOp (t, AA31, AA11);\n        AA12 = fmnaOp (t, AA32, AA12);\n        AA13 = -t * AA33;\n        t = AA23;\n        AA20 = fmnaOp (t, AA30, AA20);\n        AA21 = fmnaOp (t, AA31, AA21);\n        AA22 = fmnaOp (t, AA32, AA22);\n        AA23 = -t * AA33;\n        /* sort columns into the correct order */\n        Ainv(0, 0) = AA00;\n        Ainv(1, 0) = AA10;\n        Ainv(2, 0) = AA20;\n        Ainv(3, 0) = AA30;\n        Ainv(0, 1) = AA01;\n        Ainv(1, 1) = AA11;\n        Ainv(2, 1) = AA21;\n        Ainv(3, 1) = AA31;\n        Ainv(0, 2) = AA02;\n        Ainv(1, 2) = AA12;\n        Ainv(2, 2) = AA22;\n        Ainv(3, 2) = AA32;\n        Ainv(0, 3) = AA03;\n        Ainv(1, 3) = AA13;\n        Ainv(2, 3) = AA23;\n        Ainv(3, 3) = AA33;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockJacobiSmoothbBigBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ volatile char schar[];\n    volatile ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&schar[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    types::util<ValueTypeA>::volcast( e_out[t1 * tile_num + t2], s_Amat + (s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind) );\n                }\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockJacobiSmoothBbyBBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        types::util<ValueTypeA>::volcast(e_out, s_Amat + tid);\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename ValueTypeA, typename ValueTypeB, typename IndexType, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2(const IndexType *dia_indices, const ValueTypeA *A_values, ValueTypeA *Dinv, const int num_block_rows)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    int offset;\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        // Store the diagonal\n        offset = halfwarp_id * bsize_sq;\n        e_out = A_values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        types::util<ValueTypeA>::volcast(e_out, s_Amat + threadIdx.x);\n        compute_block_inverse_row_major<int, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset + mat_entry_index, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\n// Kernel to smooth with Jacobi smoother, Dinv assumed to be computed\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, int bsize_sq>\n__global__\nvoid jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, double weight, const int num_block_rows, ValueTypeB *xout, const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        i = eighthwarp_id;\n        offset = i * bsize + vec_entry_index;\n        // 1. COMPUTING b-Ax\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        types::util<ValueTypeB>::volcast(xin, s_xtemp + threadIdx.x);\n        // Load dia_values and do matrix multiply\n        loadAsVector<bsize>(nonzero_values + bsize_sq * dia_indices[i] + vec_entry_index * bsize, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx - temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = __cachingLoad(&column_indices[jind]);\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                types::util<ValueTypeB>::volcast(x[offset], s_xtemp + threadIdx.x);\n                // Load nonzero_values\n                s_offset = block_eighthwarp_id * bsize;\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx = bmAx - temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n                }\n            }\n        }\n\n        types::util<ValueTypeB>::volcast(bmAx, s_xtemp + threadIdx.x);\n        bmAx = types::util<ValueTypeB>::get_zero();\n        // 2. Multiply by Dinv\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + bmAx * weight;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n// Kernel to smooth with jacobi smoother, zero initial guess\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2(const ValueTypeA *Dinv, const ValueTypeB *b, double weight, const int num_block_rows, ValueTypeB *xout, const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    for (int eighthwarp_id = row_offset + (tid >> log_bsize); eighthwarp_id < num_block_rows; eighthwarp_id += (gridDim.x * blockDim.x >> log_bsize))\n    {\n        i = eighthwarp_id;\n        offset = i * bsize + vec_entry_index;\n        types::util<ValueTypeB>::volcast(b[offset], s_xtemp + threadIdx.x);\n        bmAx = types::util<ValueTypeB>::get_zero();\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        xout[i * bsize + vec_entry_index] = bmAx * weight;\n    }\n}\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nBlockJacobiSolver_Base<T_Config>::BlockJacobiSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nBlockJacobiSolver_Base<T_Config>::~BlockJacobiSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"BlockJacobiSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    computeDinv( *A_as_matrix );\n\n    if ( A_as_matrix->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Block Jacobi solver only supports row major format\", AMGX_ERR_CONFIGURATION);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nBlockJacobiSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    //bool done = false;\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    ViewType oldView = A_as_matrix->currentView();\n\n    A_as_matrix->setViewExterior();\n    ViewType flags = (ViewType)(A_as_matrix->getViewInterior() | A_as_matrix->getViewExterior());\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*A_as_matrix, b, x, flags);\n        }\n        else\n        {\n            smooth_1x1(*A_as_matrix, b, x, flags);\n        }\n    }\n    else if (A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_4x4(*A_as_matrix, b, x, flags);\n        }\n        else\n        {\n            smooth_4x4(*A_as_matrix, b, x, flags);\n        }\n    }\n    else if (A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n    {\n        if (xIsZero)\n        {\n            thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), types::util<ValueTypeB>::get_zero());\n            cudaCheckError();\n        }\n\n        smooth_BxB(*A_as_matrix, b, x, true, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4)\n    {\n        if (!xIsZero) // we write to t_res vector to avoid race condition in the kernel\n        {\n            x.swap(this->t_res);\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid BlockJacobiSolver_Base<T_Config>::computeDinv( Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        this->computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        this->computeDinv_bxb<2>(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        this->computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        this->computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        this->computeDinv_bxb<5>(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy() && A.get_block_dimy() > 5)\n    {\n        this->computeDinv_Big(A, A.get_block_dimx());\n    }\n\n    A.setView(oldView);\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n\n    if ( A_as_matrix->hasProps(DIAG) )\n    {\n        const int num_values = A_as_matrix->diagOffset() * A_as_matrix->get_block_size();\n        amgx::thrust::copy( A_as_matrix->values.begin() + num_values, A_as_matrix->values.begin() + num_values + A_as_matrix->get_num_rows()*A_as_matrix->get_block_size(), this->Dinv.begin() );\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag( *A_as_matrix );\n    }\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    // Do nothing\n}\n\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    //FatalError(\"Block Jacobi smoother not implemented with this format, exiting\");\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"3*3 Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_Big(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Big Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_h &A )\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->Dinv[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                FatalError(\"Could not find a diagonal value\", AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_d &A )\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::find_diag \" );\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((float)(A.get_num_rows()) / (float)(THREADS_PER_BLOCK)));\n    find_diag_kernel_indexed_dia <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>(\n        A.get_num_rows(),\n        A.diag.raw(),\n        A.values.raw(),\n        this->Dinv.raw());\n    cudaCheckError();\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_indices_ptr = A.diag.raw();\n    const ValueTypeA *A_values = A.values.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n#if 1\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    cudaFuncSetCacheConfig(setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2<ValueTypeA, ValueTypeB, IndexType, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>, cudaFuncCachePreferL1);\n    setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2<ValueTypeA, ValueTypeB, IndexType, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_dia_indices_ptr, A_values, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n#else\n    cudaFuncSetCacheConfig(matinv_matrix_per_thread_pivot<ValueTypeA, ValueTypeB, 4>, cudaFuncCachePreferL1);\n    matinv_matrix_per_thread_pivot<ValueTypeA, ValueTypeB, 4> <<< (A.num_block_rows + 127) / 128, 128 >>> (A_dia_values_ptr, Dinv_ptr, A.num_block_rows);\n    cudaCheckError();\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n    const int bsize = 3;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = 32 / (bsize * bsize);\n    // blockrows per cta = blockrows_per_warp * number_of_warps_per_cta\n    const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp ;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    cudaFuncSetCacheConfig(setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize>, cudaFuncCachePreferL1);\n    setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_Big(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize - 1) / 4 + 1) * ((bsize - 1) / 4 + 1));\n    ValueTypeA *temp_ptr = temp.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    cudaFuncSetCacheConfig(setupBlockJacobiSmoothbBigBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block>, cudaFuncCachePreferL1);\n    setupBlockJacobiSmoothbBigBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block> <<< num_blocks, threads_per_block, sizeof(ValueTypeA)*bsize *bsize *halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize * bsize, temp_ptr);\n    cudaCheckError();\n}\n\n\ntemplate<class T_Config>\ntemplate<int bsize>\nvoid BlockJacobiSolver_Base<T_Config>::computeDinv_bxb(const Matrix<T_Config> &A)\n{\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"BlockJacobiSmooth Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        // supports both diag\n        this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n        const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n        const IndexType *A_column_indices_ptr = A.col_indices.raw();\n        const IndexType *A_dia_idx_ptr = A.diag.raw();\n        ValueTypeA *Dinv_ptr = this->Dinv.raw();\n        const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n        // MUST BE MULTIPLE OF 16\n        const int threads_per_block = 256;\n        const int blockrows_per_warp = 32 / (bsize * bsize);\n        const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n        cudaFuncSetCacheConfig(setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize>, cudaFuncCachePreferL1);\n        setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    VVector newx(x.size());\n    int bsize = A.get_block_dimx();\n    // Allocate space for block_matrix\n    ValueTypeA **E = new ValueTypeA* [bsize];\n\n    for ( int i = 0; i < bsize; i++)\n    {\n        E[i] = new ValueTypeA[bsize];\n    }\n\n    ValueTypeB *bmAx = new ValueTypeB[bsize];\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    //for each block row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // Compute b - sum_j A_j x_j (denoted by bmAx) for block_row i\n        // Load diagonal\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                E[m][n] = A.values[A.diag[i * bsize * bsize + bsize * m + n]];\n            }\n\n            bmAx[m] = types::util<ValueTypeB>::get_zero();\n            temp[m] = types::util<ValueTypeB>::get_zero();\n        }\n\n        // Contribution from diagonal\n        for (int m = 0; m < bsize; m++)\n            for (int n = 0; n < bsize; n++)\n            {\n                bmAx[m] = bmAx[m] - E[m][n] * x[i * bsize + n];\n            }\n\n        // Contribution from each nonzero column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            IndexType jcol = A.col_indices[j];\n\n            for (int m = 0; m < bsize; m++)\n                for (int n = 0; n < bsize; n++)\n                {\n                    bmAx[m] = bmAx[m]  - A.values[j * bsize * bsize + bsize * m + n] * x[jcol * bsize + n];\n                }\n        }\n\n        // Add b\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx[m] = bmAx[m] + b[i * bsize + m];\n        }\n\n        gaussianEliminationRowMajor(E, temp, bmAx, bsize);\n\n        // Compute new value of x\n        for (int m = 0; m < bsize; m++)\n        {\n            newx[i * bsize + m] = x[i * bsize + m] + temp[m] * this->weight;\n        }\n    }\n\n    x.swap(newx);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    IndexType bsize = A.get_block_dimy();\n    ValueTypeA **E = new ValueTypeA* [bsize];\n\n    for ( int i = 0; i < bsize; i++)\n    {\n        E[i] = new ValueTypeA[bsize];\n    }\n\n    ValueTypeB *rhs = new ValueTypeB[bsize];\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    //for each block row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // Load diagonal\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                E[m][n] = A.values[A.diag[i * bsize * bsize + bsize * m + n]];\n            }\n\n            rhs[m] = types::util<ValueTypeB>::get_zero();\n        }\n\n        //rhs\n        for (int m = 0; m < bsize; m++)\n        {\n            rhs[m] = rhs[m] + b[i * bsize + m];\n        }\n\n        // Solve for temp\n        gaussianEliminationRowMajor(E, temp, rhs, bsize);\n\n        for (int m = 0; m < bsize; m++)\n        {\n            x[i * bsize + m] = temp[m] * this->weight;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_const(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    VVector newx(x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = types::util<ValueTypeB>::get_zero();\n        ValueTypeB d = types::util<ValueTypeB>::get_one() * A.values[A.diag[i]];\n        ValueTypeB mydiaginv = types::util<ValueTypeB>::get_one() * this->weight / (isNotCloseToZero(d) ? d : epsilon(d) );\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi = Axi + A.values[j] * x[A.col_indices[j]];\n        }\n\n        newx[i] = x[i] +  (b[i] - Axi) * mydiaginv ;\n    }\n\n    x.swap(newx);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    this->smooth_1x1_const(A, b, x, separation_flags);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_1x1 \" );\n\n    if (this->t_res.size() != x.size())\n    {\n        this->t_res.resize(x.size());\n    }\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    this->y.dirtybit = 0;\n\n    multiply( A, x, this->y, separation_flags );\n\n    thrust_wrapper::transform<AMGX_device>( \n            amgx::thrust::make_zip_iterator(\n                amgx::thrust::make_tuple(x.begin() + offset, this->Dinv.begin() + offset, b.begin() + offset, this->y.begin() + offset)), \n            amgx::thrust::make_zip_iterator(\n                amgx::thrust::make_tuple(x.begin() + A.get_num_rows(),   this->Dinv.begin() + A.get_num_rows(),   b.begin() + A.get_num_rows(), this->y.begin() + A.get_num_rows())),\n                       x.begin() + offset,\n                       jacobi_postsmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d = types::util<ValueTypeB>::get_one() * A.values[A.diag[i]];\n        ValueTypeB mydiag = types::util<ValueTypeB>::get_one() * this->weight / (isNotCloseToZero(d) ? d : epsilon(d));\n        x[i] =  b[i] * mydiag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_with_0_initial_guess_1x1 \" );\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    thrust_wrapper::transform<AMGX_device>( b.begin( ) + offset,\n                       b.begin( ) + A.get_num_rows(),\n                       this->Dinv.begin( ) + offset,\n                       x.begin( ) + offset,\n                       jacobi_presmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    IndexType num_rows;\n    IndexType offset;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n\n    // aux vector initialization\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    amgx::thrust::copy(b.begin(), b.end(), this->y.begin()); // copy of vector b\n    cudaCheckError();\n    Cusparse::bsrmv(types::util<ValueTypeB>::get_minus_one(), A, x, types::util<ValueTypeB>::get_one(), this->y, separation_flags);         // y= -1.0f*(A.x) + y\n    cudaCheckError();\n\n    Cusparse::bsrmv(types::util<ValueTypeB>::get_one() * this->weight, A, this->Dinv, this->y, types::util<ValueTypeB>::get_one(), x, separation_flags); // t_res = t_res + w*(Dinv.y) @ view\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    if (this->t_res.size() != x.size())\n    {\n        this->t_res.resize(x.size());\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    ValueTypeB *x_ptr = x.raw();\n    ValueTypeB *xout_ptr = this->t_res.raw(); // always store original x\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 512;\n    const int eightwarps_per_block = threads_per_block / 4;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / eightwarps_per_block + 1);\n\n    // XXX We need to add latency hiding here\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(x, x.tag);\n    }\n\n    cudaFuncSetCacheConfig(jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16>, cudaFuncCachePreferL1);\n    jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n     b_ptr, x_ptr, this->weight, offset + num_rows, xout_ptr, offset);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    cudaCheckError();\n    const ValueTypeA *A_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 512;\n    const int eightwarps_per_block = threads_per_block / 4;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / eightwarps_per_block + 1);\n    cudaFuncSetCacheConfig(jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>, cudaFuncCachePreferL1);\n    jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n    (Dinv_ptr, b_ptr, this->weight, offset + num_rows, x_ptr, offset);\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class BlockJacobiSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class BlockJacobiSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace block_jacobi\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/block_jacobi_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#include <texture.h>\n#include <amgx_cusparse.h>\n#include <ostream>\n\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\nnamespace block_jacobi_solver\n{\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    double omega;\n    jacobi_presmooth_functor( double omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? b * omega / d : b * omega / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    double omega;\n    jacobi_postsmooth_functor( double omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d = types::util<ValueTypeA>::get_one() / d;\n        b = b - y;\n        b = b * omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\ntemplate<typename T>\n__device__ __forceinline__ T fmnaOp (T a, T b, T c)\n{\n    return -(a * b) + c;\n}\ntemplate<typename T>\n__device__ __forceinline__ T mulOp (T a, T b)\n{\n    return a * b;\n}\ntemplate<typename T>\n__device__ __forceinline__ T rcpOp (T a)\n{\n    return 1.0 / (isNotCloseToZero(a) ? a : epsilon(a));\n}\ntemplate<typename T>\n__device__ __forceinline__ T absOp (T a)\n{\n    return fabs(a);\n}\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate<typename T1, typename T2, int N>\n__global__ void matinv_matrix_per_thread_pivot (const T1 *A, T2 *Ainv, int batch)\n{\n#define A(row,col)     A[(col)*N+(row)]\n#define AA(row,col)    AA[(col)*N+(row)]\n#define Ainv(row,col)  Ainv[(col)*N+(row)]\n    const int blkNum = blockIdx.x * blockDim.x + threadIdx.x;\n    int perm0, perm1, perm2, perm3;\n    int icol0, icol1, icol2, icol3;\n    T2 AA00, AA01, AA02, AA03, AA10, AA11, AA12, AA13;\n    T2 AA20, AA21, AA22, AA23, AA30, AA31, AA32, AA33;\n    T2 p, t;\n    int i, pvt;\n    A    += blkNum * N * N;\n    Ainv += blkNum * N * N;\n\n    if (blkNum < batch)\n    {\n        AA00 = A[0];\n        AA10 = A[1];\n        AA20 = A[2];\n        AA30 = A[3];\n        AA01 = A[4];\n        AA11 = A[5];\n        AA21 = A[6];\n        AA31 = A[7];\n        AA02 = A[8];\n        AA12 = A[9];\n        AA22 = A[10];\n        AA32 = A[11];\n        AA03 = A[12];\n        AA13 = A[13];\n        AA23 = A[14];\n        AA33 = A[15];\n        perm0 = 0;\n        perm1 = 1;\n        perm2 = 2;\n        perm3 = 3;\n        /****************** iteration 0 ***********/\n        /* search pivot row */\n        p = absOp (AA00);\n        pvt = 0;\n        t = absOp (AA10);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 1;\n        }\n\n        t = absOp (AA20);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 2;\n        }\n\n        t = absOp (AA30);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 0 */\n        if (pvt == 1)\n        {\n            t = AA00;\n            AA00 = AA10;\n            AA10 = t;\n            t = AA01;\n            AA01 = AA11;\n            AA11 = t;\n            t = AA02;\n            AA02 = AA12;\n            AA12 = t;\n            t = AA03;\n            AA03 = AA13;\n            AA13 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm1;\n            perm1 = i;\n        }\n\n        if (pvt == 2)\n        {\n            t = AA00;\n            AA00 = AA20;\n            AA20 = t;\n            t = AA01;\n            AA01 = AA21;\n            AA21 = t;\n            t = AA02;\n            AA02 = AA22;\n            AA22 = t;\n            t = AA03;\n            AA03 = AA23;\n            AA23 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm2;\n            perm2 = i;\n        }\n\n        if (pvt == 3)\n        {\n            t = AA00;\n            AA00 = AA30;\n            AA30 = t;\n            t = AA01;\n            AA01 = AA31;\n            AA31 = t;\n            t = AA02;\n            AA02 = AA32;\n            AA32 = t;\n            t = AA03;\n            AA03 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm0;\n            perm0 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA00);\n        icol0 = perm0;\n        AA00 = t;\n        AA01 = mulOp (t, AA01);\n        AA02 = mulOp (t, AA02);\n        AA03 = mulOp (t, AA03);\n        /* eliminate above and below current row */\n        t = AA10;\n        AA10 = mulOp (-t, AA00);\n        AA11 = fmnaOp (t, AA01, AA11);\n        AA12 = fmnaOp (t, AA02, AA12);\n        AA13 = fmnaOp (t, AA03, AA13);\n        t = AA20;\n        AA20 = mulOp (-t, AA00);\n        AA21 = fmnaOp (t, AA01, AA21);\n        AA22 = fmnaOp (t, AA02, AA22);\n        AA23 = fmnaOp (t, AA03, AA23);\n        t = AA30;\n        AA30 = mulOp (-t, AA00);\n        AA31 = fmnaOp (t, AA01, AA31);\n        AA32 = fmnaOp (t, AA02, AA32);\n        AA33 = fmnaOp (t, AA03, AA33);\n        /****************** iteration 1 ***********/\n        /* search pivot row */\n        p = absOp (AA11);\n        pvt = 1;\n        t = absOp (AA21);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 2;\n        }\n\n        t = absOp (AA31);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 1 */\n        if (pvt == 2)\n        {\n            t = AA10;\n            AA10 = AA20;\n            AA20 = t;\n            t = AA11;\n            AA11 = AA21;\n            AA21 = t;\n            t = AA12;\n            AA12 = AA22;\n            AA22 = t;\n            t = AA13;\n            AA13 = AA23;\n            AA23 = t;\n            /* update permutation vector based on row swap */\n            i = perm1;\n            perm1 = perm2;\n            perm2 = i;\n        }\n        else if (pvt == 3)\n        {\n            t = AA10;\n            AA10 = AA30;\n            AA30 = t;\n            t = AA11;\n            AA11 = AA31;\n            AA31 = t;\n            t = AA12;\n            AA12 = AA32;\n            AA32 = t;\n            t = AA13;\n            AA13 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm1;\n            perm1 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA11);\n        icol1 = perm1;\n        AA10 = mulOp (t, AA10);\n        AA11 = t;\n        AA12 = mulOp (t, AA12);\n        AA13 = mulOp (t, AA13);\n        /* eliminate above and below current row */\n        t = AA01;\n        AA00 = fmnaOp (t, AA10, AA00);\n        AA01 = mulOp (-t, AA11);\n        AA02 = fmnaOp (t, AA12, AA02);\n        AA03 = fmnaOp (t, AA13, AA03);\n        t = AA21;\n        AA20 = fmnaOp (t, AA10, AA20);\n        AA21 = mulOp (-t, AA11);\n        AA22 = fmnaOp (t, AA12, AA22);\n        AA23 = fmnaOp (t, AA13, AA23);\n        t = AA31;\n        AA30 = fmnaOp (t, AA10, AA30);\n        AA31 = mulOp (-t, AA11);\n        AA32 = fmnaOp (t, AA12, AA32);\n        AA33 = fmnaOp (t, AA13, AA33);\n        /****************** iteration 2 ****************/\n        /* search pivot row */\n        p = absOp (AA22);\n        pvt = 2;\n        t = absOp (AA32);\n\n        if (t > p)\n        {\n            p = t;\n            pvt = 3;\n        }\n\n        /* swap pivot row with row 2 */\n        if (pvt == 3)\n        {\n            t = AA20;\n            AA20 = AA30;\n            AA30 = t;\n            t = AA21;\n            AA21 = AA31;\n            AA31 = t;\n            t = AA22;\n            AA22 = AA32;\n            AA32 = t;\n            t = AA23;\n            AA23 = AA33;\n            AA33 = t;\n            /* update permutation vector based on row swap */\n            i = perm2;\n            perm2 = perm3;\n            perm3 = i;\n        }\n\n        /* scale current row */\n        t = rcpOp (AA22);\n        icol2 = perm2;\n        AA20 = mulOp (t, AA20);\n        AA21 = mulOp (t, AA21);\n        AA22 = t;\n        AA23 = mulOp (t, AA23);\n        /* eliminate above and below current row */\n        t = AA02;\n        AA00 = fmnaOp (t, AA20, AA00);\n        AA01 = fmnaOp (t, AA21, AA01);\n        AA02 = mulOp (-t, AA22);\n        AA03 = fmnaOp (t, AA23, AA03);\n        t = AA12;\n        AA10 = fmnaOp (t, AA20, AA10);\n        AA11 = fmnaOp (t, AA21, AA11);\n        AA12 = mulOp (-t, AA22);\n        AA13 = fmnaOp (t, AA23, AA13);\n        t = AA32;\n        AA30 = fmnaOp (t, AA20, AA30);\n        AA31 = fmnaOp (t, AA21, AA31);\n        AA32 = mulOp (-t, AA22);\n        AA33 = fmnaOp (t, AA23, AA33);\n        /****************** iteration 3 ****************/\n        /* scale current row */\n        t = rcpOp (AA33);\n        icol3 = perm3;\n        AA30 = mulOp (t, AA30);\n        AA31 = mulOp (t, AA31);\n        AA32 = mulOp (t, AA32);\n        AA33 = t;\n        /* eliminate above and below current row */\n        t = AA03;\n        AA00 = fmnaOp (t, AA30, AA00);\n        AA01 = fmnaOp (t, AA31, AA01);\n        AA02 = fmnaOp (t, AA32, AA02);\n        AA03 = mulOp (-t, AA33);\n        t = AA13;\n        AA10 = fmnaOp (t, AA30, AA10);\n        AA11 = fmnaOp (t, AA31, AA11);\n        AA12 = fmnaOp (t, AA32, AA12);\n        AA13 = mulOp (-t, AA33);\n        t = AA23;\n        AA20 = fmnaOp (t, AA30, AA20);\n        AA21 = fmnaOp (t, AA31, AA21);\n        AA22 = fmnaOp (t, AA32, AA22);\n        AA23 = mulOp (-t, AA33);\n        /* sort columns into the correct order */\n        Ainv(0, icol0) = AA00;\n        Ainv(1, icol0) = AA10;\n        Ainv(2, icol0) = AA20;\n        Ainv(3, icol0) = AA30;\n        Ainv(0, icol1) = AA01;\n        Ainv(1, icol1) = AA11;\n        Ainv(2, icol1) = AA21;\n        Ainv(3, icol1) = AA31;\n        Ainv(0, icol2) = AA02;\n        Ainv(1, icol2) = AA12;\n        Ainv(2, icol2) = AA22;\n        Ainv(3, icol2) = AA32;\n        Ainv(0, icol3) = AA03;\n        Ainv(1, icol3) = AA13;\n        Ainv(2, icol3) = AA23;\n        Ainv(3, icol3) = AA33;\n    }\n}\n\n\ntemplate<typename T, int N>\n__global__ void matinv_matrix_per_thread_no_pivot (const T *A, T *Ainv, int batch)\n{\n#define A(row,col)     A[(col)*N+(row)]\n#define AA(row,col)    AA[(col)*N+(row)]\n#define Ainv(row,col)  Ainv[(col)*N+(row)]\n    const int blkNum = blockIdx.x * blockDim.x + threadIdx.x;\n    T AA00, AA01, AA02, AA03, AA10, AA11, AA12, AA13;\n    T AA20, AA21, AA22, AA23, AA30, AA31, AA32, AA33;\n    T t;\n    A    += blkNum * N * N;\n    Ainv += blkNum * N * N;\n\n    if (blkNum < batch)\n    {\n        AA00 = A[0];\n        AA10 = A[1];\n        AA20 = A[2];\n        AA30 = A[3];\n        AA01 = A[4];\n        AA11 = A[5];\n        AA21 = A[6];\n        AA31 = A[7];\n        AA02 = A[8];\n        AA12 = A[9];\n        AA22 = A[10];\n        AA32 = A[11];\n        AA03 = A[12];\n        AA13 = A[13];\n        AA23 = A[14];\n        AA33 = A[15];\n        /****************** iteration 0 ***********/\n        /* search pivot row */\n        t = 1.0 / (AA00);\n        AA00 = t;\n        AA01 = t * AA01;\n        AA02 = t * AA02;\n        AA03 = t * AA03;\n        /* eliminate above and below current row */\n        t = AA10;\n        AA10 = -t * AA00;\n        AA11 = fmnaOp (t, AA01, AA11);\n        AA12 = fmnaOp (t, AA02, AA12);\n        AA13 = fmnaOp (t, AA03, AA13);\n        t = AA20;\n        AA20 = -t * AA00;\n        AA21 = fmnaOp (t, AA01, AA21);\n        AA22 = fmnaOp (t, AA02, AA22);\n        AA23 = fmnaOp (t, AA03, AA23);\n        t = AA30;\n        AA30 = -t * AA00;\n        AA31 = fmnaOp (t, AA01, AA31);\n        AA32 = fmnaOp (t, AA02, AA32);\n        AA33 = fmnaOp (t, AA03, AA33);\n        /****************** iteration 1 ***********/\n        /* scale current row */\n        t = 1.0 / (AA11);\n        AA10 = t * AA10;\n        AA11 = t;\n        AA12 = t * AA12;\n        AA13 = t * AA13;\n        /* eliminate above and below current row */\n        t = AA01;\n        AA00 = fmnaOp (t, AA10, AA00);\n        AA01 = -t * AA11;\n        AA02 = fmnaOp (t, AA12, AA02);\n        AA03 = fmnaOp (t, AA13, AA03);\n        t = AA21;\n        AA20 = fmnaOp (t, AA10, AA20);\n        AA21 = -t * AA11;\n        AA22 = fmnaOp (t, AA12, AA22);\n        AA23 = fmnaOp (t, AA13, AA23);\n        t = AA31;\n        AA30 = fmnaOp (t, AA10, AA30);\n        AA31 = -t * AA11;\n        AA32 = fmnaOp (t, AA12, AA32);\n        AA33 = fmnaOp (t, AA13, AA33);\n        /****************** iteration 2 ****************/\n        /* scale current row */\n        t = 1.0 / (AA22);\n        AA20 = t * AA20;\n        AA21 = t * AA21;\n        AA22 = t;\n        AA23 = t * AA23;\n        /* eliminate above and below current row */\n        t = AA02;\n        AA00 = fmnaOp (t, AA20, AA00);\n        AA01 = fmnaOp (t, AA21, AA01);\n        AA02 = -t * AA22;\n        AA03 = fmnaOp (t, AA23, AA03);\n        t = AA12;\n        AA10 = fmnaOp (t, AA20, AA10);\n        AA11 = fmnaOp (t, AA21, AA11);\n        AA12 = -t * AA22;\n        AA13 = fmnaOp (t, AA23, AA13);\n        t = AA32;\n        AA30 = fmnaOp (t, AA20, AA30);\n        AA31 = fmnaOp (t, AA21, AA31);\n        AA32 = -t * AA22;\n        AA33 = fmnaOp (t, AA23, AA33);\n        /****************** iteration 3 ****************/\n        /* scale current row */\n        t = 1.0 / (AA33);\n        AA30 =  t * AA30;\n        AA31 =  t * AA31;\n        AA32 =  t * AA32;\n        AA33 = t;\n        /* eliminate above and below current row */\n        t = AA03;\n        AA00 = fmnaOp (t, AA30, AA00);\n        AA01 = fmnaOp (t, AA31, AA01);\n        AA02 = fmnaOp (t, AA32, AA02);\n        AA03 = -t * AA33;\n        t = AA13;\n        AA10 = fmnaOp (t, AA30, AA10);\n        AA11 = fmnaOp (t, AA31, AA11);\n        AA12 = fmnaOp (t, AA32, AA12);\n        AA13 = -t * AA33;\n        t = AA23;\n        AA20 = fmnaOp (t, AA30, AA20);\n        AA21 = fmnaOp (t, AA31, AA21);\n        AA22 = fmnaOp (t, AA32, AA22);\n        AA23 = -t * AA33;\n        /* sort columns into the correct order */\n        Ainv(0, 0) = AA00;\n        Ainv(1, 0) = AA10;\n        Ainv(2, 0) = AA20;\n        Ainv(3, 0) = AA30;\n        Ainv(0, 1) = AA01;\n        Ainv(1, 1) = AA11;\n        Ainv(2, 1) = AA21;\n        Ainv(3, 1) = AA31;\n        Ainv(0, 2) = AA02;\n        Ainv(1, 2) = AA12;\n        Ainv(2, 2) = AA22;\n        Ainv(3, 2) = AA32;\n        Ainv(0, 3) = AA03;\n        Ainv(1, 3) = AA13;\n        Ainv(2, 3) = AA23;\n        Ainv(3, 3) = AA33;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int threads_per_block, int halfwarps_per_block>\n__global__\nvoid setupBlockJacobiSmoothbBigBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows, int bsize, int bsize_sq, ValueTypeA *temp1)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> 4;\n    const int block_halfwarp_id = threadIdx.x >> 4;\n    const int mat_entry_index = threadIdx.x & (16 - 1);\n    const int i_ind = mat_entry_index >> 2;\n    const int j_ind = mat_entry_index & 3;\n    extern __shared__ volatile char schar[];\n    volatile ValueTypeA *s_Amat;\n    s_Amat = (ValueTypeA *)&schar[0];\n    int tile_num = (bsize - 1) / 4 + 1;\n    ValueTypeA *e_out = &temp1[(blockIdx.x * blockDim.x + threadIdx.x) * tile_num * tile_num];\n\n    while (halfwarp_id < num_block_rows)\n    {\n        int offset = halfwarp_id * bsize_sq + i_ind * bsize + j_ind;\n        int s_offset = block_halfwarp_id * bsize_sq;\n\n        // Store the diagonal\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    e_out[t1 * tile_num + t2] = values[bsize_sq * dia_indices[halfwarp_id] + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind];\n                }\n\n        // Each thread stores its entry in s_Amat\n        for (int t1 = 0; t1 < tile_num; t1++)\n            for (int t2 = 0; t2 < tile_num; t2++)\n                if ((t1 * 4 + i_ind) < bsize && (t2 * 4 + j_ind) < bsize)\n                {\n                    types::util<ValueTypeA>::volcast( e_out[t1 * tile_num + t2], s_Amat + (s_offset + (t1 * 4 + i_ind) * bsize + t2 * 4 + j_ind) );\n                }\n\n        compute_block_inverse2<IndexType, ValueTypeA, halfwarps_per_block>\n        ( s_Amat, s_offset, offset, i_ind, j_ind, Dinv, tile_num, bsize, bsize_sq );\n        halfwarp_id += gridDim.x * halfwarps_per_block;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, int blockrows_per_cta, int blockrows_per_warp, int bsize, int bsize_sq>\n__global__\nvoid setupBlockJacobiSmoothBbyBBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, ValueTypeA *Dinv, const int num_block_rows)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize_sq ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize_sq + warp_thread_id;\n    int cta_blockrow_id = tid / bsize_sq;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int mat_entry_index = tid - cta_blockrow_id * bsize_sq;\n    const int i_ind = mat_entry_index / bsize;\n    const int j_ind = mat_entry_index - i_ind * bsize;\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * blockrows_per_cta ];\n    ValueTypeA e_out;\n\n    while (blockrow_id < num_block_rows)\n    {\n        int offset = blockrow_id * bsize_sq + mat_entry_index;\n        // Store the diagonal\n        e_out = values[bsize_sq * dia_indices[blockrow_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        types::util<ValueTypeA>::volcast(e_out, s_Amat + tid);\n        compute_block_inverse_row_major<IndexType, ValueTypeA, blockrows_per_cta, bsize, bsize_sq>\n        ( s_Amat, cta_blockrow_id * bsize_sq, offset, i_ind, j_ind, Dinv );\n        blockrow_id += gridDim.x * blockrows_per_cta;\n    }\n}\n\ntemplate<typename ValueTypeA, typename ValueTypeB, typename IndexType, int threads_per_block, int halfwarps_per_block, int bsize, int log_bsize, int bsize_sq, int log_bsize_sq>\n__global__\nvoid setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2(const IndexType *dia_indices, const ValueTypeA *A_values, ValueTypeA *Dinv, const int num_block_rows)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int halfwarp_id = tid >> log_bsize_sq;\n    const int block_halfwarp_id = threadIdx.x >> log_bsize_sq;\n    const int mat_entry_index = threadIdx.x & (bsize_sq - 1);\n    const int i_ind = mat_entry_index >> log_bsize;\n    const int j_ind = mat_entry_index & (bsize - 1);\n    volatile __shared__ ValueTypeA s_Amat[bsize_sq * halfwarps_per_block ];\n    int offset;\n    ValueTypeA e_out;\n\n    while (halfwarp_id < num_block_rows)\n    {\n        // Store the diagonal\n        offset = halfwarp_id * bsize_sq;\n        e_out = A_values[bsize_sq * dia_indices[halfwarp_id] + mat_entry_index];\n        // Each thread stores its entry in s_Amat\n        types::util<ValueTypeA>::volcast(e_out, s_Amat + threadIdx.x);\n        compute_block_inverse_row_major<int, ValueTypeA, halfwarps_per_block, bsize, bsize_sq>\n        ( s_Amat, block_halfwarp_id * bsize_sq, offset + mat_entry_index, i_ind, j_ind, Dinv );\n        halfwarp_id += gridDim.x * blockDim.x >> log_bsize_sq;\n    }\n}\n\n// Kernel to smooth with Jacobi smoother, Dinv assumed to be computed\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize, int bsize_sq>\n__global__\nvoid jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values, const ValueTypeA *Dinv,\n        const ValueTypeB *b, const ValueTypeB *x, double weight, const int num_block_rows, ValueTypeB *xout, const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    int eighthwarp_id = row_offset + (tid >> log_bsize);\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx, xin;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    while (eighthwarp_id < num_block_rows)\n    {\n        i = eighthwarp_id;\n        offset = i * bsize + vec_entry_index;\n        // 1. COMPUTING b-Ax\n        bmAx = b[offset];\n        // Contribution from diagonal\n        xin = x[offset];\n        types::util<ValueTypeB>::volcast(xin, s_xtemp + threadIdx.x);\n        // Load dia_values and do matrix multiply\n        loadAsVector<bsize>(nonzero_values + bsize_sq * dia_indices[i] + vec_entry_index * bsize, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx - temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = __cachingLoad(&column_indices[jind]);\n\n            if (jcol != i)\n            {\n                offset = jcol * bsize + vec_entry_index;\n                types::util<ValueTypeB>::volcast(x[offset], s_xtemp + threadIdx.x);\n                // Load nonzero_values\n                s_offset = block_eighthwarp_id * bsize;\n                offset = jind * bsize * bsize + vec_entry_index * bsize;\n                loadAsVector<bsize>(nonzero_values + offset, temp);\n#pragma unroll\n\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx = bmAx - temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n                }\n            }\n        }\n\n        types::util<ValueTypeB>::volcast(bmAx, s_xtemp + threadIdx.x);\n        bmAx = types::util<ValueTypeB>::get_zero();\n        // 2. Multiply by Dinv\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        xout[i * bsize + vec_entry_index] = xin + bmAx * weight;\n        eighthwarp_id += gridDim.x * blockDim.x >> log_bsize;\n    }\n}\n\n// Kernel to smooth with jacobi smoother, zero initial guess\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int eighthwarps_per_block, int bsize, int log_bsize, int half_bsize>\n__global__\nvoid jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2(const ValueTypeA *Dinv, const ValueTypeB *b, double weight, const int num_block_rows, ValueTypeB *xout, const int row_offset)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    const int block_eighthwarp_id = threadIdx.x >> log_bsize;\n    const int vec_entry_index = threadIdx.x & (bsize - 1);\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * eighthwarps_per_block ];\n    ValueTypeB bmAx;\n    ValueTypeB temp[bsize];\n    int offset, i, s_offset;\n\n    for (int eighthwarp_id = row_offset + (tid >> log_bsize); eighthwarp_id < num_block_rows; eighthwarp_id += (gridDim.x * blockDim.x >> log_bsize))\n    {\n        i = eighthwarp_id;\n        offset = i * bsize + vec_entry_index;\n        types::util<ValueTypeB>::volcast(b[offset], s_xtemp + threadIdx.x);\n        bmAx = types::util<ValueTypeB>::get_zero();\n        // Load Dinv and multiply to RHS\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(Dinv + offset, temp);\n        s_offset = block_eighthwarp_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx = bmAx + temp[m] * types::util<ValueTypeB>::volcast(s_xtemp[s_offset + m]);\n        }\n\n        xout[i * bsize + vec_entry_index] = bmAx * weight;\n    }\n}\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nBlockJacobiSolver_Base<T_Config>::BlockJacobiSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nBlockJacobiSolver_Base<T_Config>::~BlockJacobiSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"BlockJacobiSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    computeDinv( *A_as_matrix );\n\n    if ( A_as_matrix->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Block Jacobi solver only supports row major format\", AMGX_ERR_CONFIGURATION);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nBlockJacobiSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    //bool done = false;\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    ViewType oldView = A_as_matrix->currentView();\n\n    A_as_matrix->setViewExterior();\n    ViewType flags = (ViewType)(A_as_matrix->getViewInterior() | A_as_matrix->getViewExterior());\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*A_as_matrix, b, x, flags);\n        }\n        else\n        {\n            smooth_1x1(*A_as_matrix, b, x, flags);\n        }\n    }\n    else if (A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_4x4(*A_as_matrix, b, x, flags);\n        }\n        else\n        {\n            smooth_4x4(*A_as_matrix, b, x, flags);\n        }\n    }\n    else if (A_as_matrix->get_block_dimx() == A_as_matrix->get_block_dimy())\n    {\n        if (xIsZero)\n        {\n            thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), types::util<ValueTypeB>::get_zero());\n            cudaCheckError();\n        }\n\n        smooth_BxB(*A_as_matrix, b, x, true, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (A_as_matrix->get_block_dimx() == 4 && A_as_matrix->get_block_dimy() == 4)\n    {\n        if (!xIsZero) // we write to t_res vector to avoid race condition in the kernel\n        {\n            x.swap(this->t_res);\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nBlockJacobiSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid BlockJacobiSolver_Base<T_Config>::computeDinv( Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        this->computeDinv_1x1(A);\n    }\n    else if (A.get_block_dimx() == 2 && A.get_block_dimy() == 2)\n    {\n        this->computeDinv_bxb<2>(A);\n    }\n    else if (A.get_block_dimx() == 3 && A.get_block_dimy() == 3)\n    {\n        this->computeDinv_3x3(A);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        this->computeDinv_4x4(A);\n    }\n    else if (A.get_block_dimx() == 5 && A.get_block_dimy() == 5)\n    {\n        this->computeDinv_bxb<5>(A);\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy() && A.get_block_dimy() > 5)\n    {\n        this->computeDinv_Big(A, A.get_block_dimx());\n    }\n\n    A.setView(oldView);\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n\n    if ( A_as_matrix->hasProps(DIAG) )\n    {\n        const int num_values = A_as_matrix->diagOffset() * A_as_matrix->get_block_size();\n        amgx::thrust::copy( A_as_matrix->values.begin() + num_values, A_as_matrix->values.begin() + num_values + A_as_matrix->get_num_rows()*A_as_matrix->get_block_size(), this->Dinv.begin() );\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag( *A_as_matrix );\n    }\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    // Do nothing\n}\n\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_h &A)\n{\n    //FatalError(\"Block Jacobi smoother not implemented with this format, exiting\");\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_h &A)\n{\n    FatalError(\"3*3 Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_Big(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Big Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n    //std::cout << \"Warning, CPU version doesn't store the inverse of the blocks, like the GPU version\" << std::endl;\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_h &A )\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->Dinv[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                FatalError(\"Could not find a diagonal value\", AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_d &A )\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::find_diag \" );\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((float)(A.get_num_rows()) / (float)(THREADS_PER_BLOCK)));\n    find_diag_kernel_indexed_dia <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>(\n        A.get_num_rows(),\n        A.diag.raw(),\n        A.values.raw(),\n        this->Dinv.raw());\n    cudaCheckError();\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_4x4(const Matrix_d &A)\n{\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_indices_ptr = A.diag.raw();\n    const ValueTypeA *A_values = A.values.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n#if 1\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2<ValueTypeA, ValueTypeB, IndexType, threads_per_block, halfwarps_per_block, 4, 2, 16, 4>), hipFuncCachePreferL1);\n    setupBlockJacobiSmooth4by4BlockDiaCsrKernel_V2<ValueTypeA, ValueTypeB, IndexType, threads_per_block, halfwarps_per_block, 4, 2, 16, 4> <<< num_blocks, threads_per_block>>>\n    (A_dia_indices_ptr, A_values, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n#else\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(matinv_matrix_per_thread_pivot<ValueTypeA, ValueTypeB, 4>), hipFuncCachePreferL1);\n    matinv_matrix_per_thread_pivot<ValueTypeA, ValueTypeB, 4> <<< (A.num_block_rows + 127) / 128, 128 >>> (A_dia_values_ptr, Dinv_ptr, A.num_block_rows);\n    cudaCheckError();\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_3x3(const Matrix_d &A)\n{\n    const int bsize = 3;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 256;\n    const int blockrows_per_warp = 32 / (bsize * bsize);\n    // blockrows per cta = blockrows_per_warp * number_of_warps_per_cta\n    const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp ;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize>), hipFuncCachePreferL1);\n    setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_Big(const Matrix_d &A, const int bsize)\n{\n//both DIAG supported\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    MVector temp(AMGX_GRID_MAX_SIZE * ((bsize - 1) / 4 + 1) * ((bsize - 1) / 4 + 1));\n    ValueTypeA *temp_ptr = temp.raw();\n    // MUST BE MULTIPLE OF 16\n    const int threads_per_block = 512;\n    const int halfwarps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / halfwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockJacobiSmoothbBigBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block>), hipFuncCachePreferL1);\n    setupBlockJacobiSmoothbBigBlockDiaCsrKernel<IndexType, ValueTypeA, threads_per_block, halfwarps_per_block> <<< num_blocks, threads_per_block, sizeof(ValueTypeA)*bsize *bsize *halfwarps_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows(), bsize, bsize * bsize, temp_ptr);\n    cudaCheckError();\n}\n\n\ntemplate<class T_Config>\ntemplate<int bsize>\nvoid BlockJacobiSolver_Base<T_Config>::computeDinv_bxb(const Matrix<T_Config> &A)\n{\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"BlockJacobiSmooth Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        // supports both diag\n        this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), types::util<ValueTypeA>::get_zero());\n        const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n        const IndexType *A_column_indices_ptr = A.col_indices.raw();\n        const IndexType *A_dia_idx_ptr = A.diag.raw();\n        ValueTypeA *Dinv_ptr = this->Dinv.raw();\n        const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n        // MUST BE MULTIPLE OF 16\n        const int threads_per_block = 256;\n        const int blockrows_per_warp = 32 / (bsize * bsize);\n        const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize>), hipFuncCachePreferL1);\n        setupBlockJacobiSmoothBbyBBlockDiaCsrKernel<IndexType, ValueTypeA, blockrows_per_cta, blockrows_per_warp, bsize, bsize *bsize> <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, Dinv_ptr, A.get_num_rows());\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M Block Jacobi smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    VVector newx(x.size());\n    int bsize = A.get_block_dimx();\n    // Allocate space for block_matrix\n    ValueTypeA **E = new ValueTypeA* [bsize];\n\n    for ( int i = 0; i < bsize; i++)\n    {\n        E[i] = new ValueTypeA[bsize];\n    }\n\n    ValueTypeB *bmAx = new ValueTypeB[bsize];\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    //for each block row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // Compute b - sum_j A_j x_j (denoted by bmAx) for block_row i\n        // Load diagonal\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                E[m][n] = A.values[A.diag[i * bsize * bsize + bsize * m + n]];\n            }\n\n            bmAx[m] = types::util<ValueTypeB>::get_zero();\n            temp[m] = types::util<ValueTypeB>::get_zero();\n        }\n\n        // Contribution from diagonal\n        for (int m = 0; m < bsize; m++)\n            for (int n = 0; n < bsize; n++)\n            {\n                bmAx[m] = bmAx[m] - E[m][n] * x[i * bsize + n];\n            }\n\n        // Contribution from each nonzero column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            IndexType jcol = A.col_indices[j];\n\n            for (int m = 0; m < bsize; m++)\n                for (int n = 0; n < bsize; n++)\n                {\n                    bmAx[m] = bmAx[m]  - A.values[j * bsize * bsize + bsize * m + n] * x[jcol * bsize + n];\n                }\n        }\n\n        // Add b\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx[m] = bmAx[m] + b[i * bsize + m];\n        }\n\n        gaussianEliminationRowMajor(E, temp, bmAx, bsize);\n\n        // Compute new value of x\n        for (int m = 0; m < bsize; m++)\n        {\n            newx[i * bsize + m] = x[i * bsize + m] + temp[m] * this->weight;\n        }\n    }\n\n    x.swap(newx);\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_4x4(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    IndexType bsize = A.get_block_dimy();\n    ValueTypeA **E = new ValueTypeA* [bsize];\n\n    for ( int i = 0; i < bsize; i++)\n    {\n        E[i] = new ValueTypeA[bsize];\n    }\n\n    ValueTypeB *rhs = new ValueTypeB[bsize];\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    //for each block row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // Load diagonal\n        for (int m = 0; m < bsize; m++)\n        {\n            for (int n = 0; n < bsize; n++)\n            {\n                E[m][n] = A.values[A.diag[i * bsize * bsize + bsize * m + n]];\n            }\n\n            rhs[m] = types::util<ValueTypeB>::get_zero();\n        }\n\n        //rhs\n        for (int m = 0; m < bsize; m++)\n        {\n            rhs[m] = rhs[m] + b[i * bsize + m];\n        }\n\n        // Solve for temp\n        gaussianEliminationRowMajor(E, temp, rhs, bsize);\n\n        for (int m = 0; m < bsize; m++)\n        {\n            x[i * bsize + m] = temp[m] * this->weight;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_const(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    VVector newx(x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = types::util<ValueTypeB>::get_zero();\n        ValueTypeB d = types::util<ValueTypeB>::get_one() * A.values[A.diag[i]];\n        ValueTypeB mydiaginv = types::util<ValueTypeB>::get_one() * this->weight / (isNotCloseToZero(d) ? d : epsilon(d) );\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi = Axi + A.values[j] * x[A.col_indices[j]];\n        }\n\n        newx[i] = x[i] +  (b[i] - Axi) * mydiaginv ;\n    }\n\n    x.swap(newx);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    this->smooth_1x1_const(A, b, x, separation_flags);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_1x1 \" );\n\n    if (this->t_res.size() != x.size())\n    {\n        this->t_res.resize(x.size());\n    }\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    this->y.dirtybit = 0;\n\n    multiply( A, x, this->y, separation_flags );\n\n    thrust_wrapper::transform<AMGX_device>( \n            amgx::thrust::make_zip_iterator(\n                amgx::thrust::make_tuple(x.begin() + offset, this->Dinv.begin() + offset, b.begin() + offset, this->y.begin() + offset)), \n            amgx::thrust::make_zip_iterator(\n                amgx::thrust::make_tuple(x.begin() + A.get_num_rows(),   this->Dinv.begin() + A.get_num_rows(),   b.begin() + A.get_num_rows(), this->y.begin() + A.get_num_rows())),\n                       x.begin() + offset,\n                       jacobi_postsmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d = types::util<ValueTypeB>::get_one() * A.values[A.diag[i]];\n        ValueTypeB mydiag = types::util<ValueTypeB>::get_one() * this->weight / (isNotCloseToZero(d) ? d : epsilon(d));\n        x[i] =  b[i] * mydiag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_with_0_initial_guess_1x1 \" );\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    thrust_wrapper::transform<AMGX_device>( b.begin( ) + offset,\n                       b.begin( ) + A.get_num_rows(),\n                       this->Dinv.begin( ) + offset,\n                       x.begin( ) + offset,\n                       jacobi_presmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    IndexType num_rows;\n    IndexType offset;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n\n    // aux vector initialization\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    amgx::thrust::copy(b.begin(), b.end(), this->y.begin()); // copy of vector b\n    cudaCheckError();\n    Cusparse::bsrmv(types::util<ValueTypeB>::get_minus_one(), A, x, types::util<ValueTypeB>::get_one(), this->y, separation_flags);         // y= -1.0f*(A.x) + y\n    cudaCheckError();\n\n    Cusparse::bsrmv(types::util<ValueTypeB>::get_one() * this->weight, A, this->Dinv, this->y, types::util<ValueTypeB>::get_one(), x, separation_flags); // t_res = t_res + w*(Dinv.y) @ view\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    if (this->t_res.size() != x.size())\n    {\n        this->t_res.resize(x.size());\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    ValueTypeB *x_ptr = x.raw();\n    ValueTypeB *xout_ptr = this->t_res.raw(); // always store original x\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 512;\n    const int eightwarps_per_block = threads_per_block / 4;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / eightwarps_per_block + 1);\n\n    // XXX We need to add latency hiding here\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(x, x.tag);\n    }\n\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16>), hipFuncCachePreferL1);\n    jacobiSmooth4by4BlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2, 16> <<< num_blocks, threads_per_block>>>\n    (A_row_offsets_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, Dinv_ptr,\n     b_ptr, x_ptr, this->weight, offset + num_rows, xout_ptr, offset);\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BlockJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_4x4(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    cudaCheckError();\n    const ValueTypeA *A_values_ptr = A.values.raw();\n    const ValueTypeB *b_ptr = b.raw();\n    const ValueTypeA *Dinv_ptr = this->Dinv.raw();\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 512;\n    const int eightwarps_per_block = threads_per_block / 4;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / eightwarps_per_block + 1);\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2>), hipFuncCachePreferL1);\n    jacobiSmooth4by4ZeroBlockDiaCsrKernel_NAIVE_tex_readDinv2<IndexType, ValueTypeA, ValueTypeB, eightwarps_per_block, 4, 2, 2> <<< num_blocks, threads_per_block>>>\n    (Dinv_ptr, b_ptr, this->weight, offset + num_rows, x_ptr, offset);\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class BlockJacobiSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class BlockJacobiSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace block_jacobi\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/block_jacobi_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/kaczmarz_solver.h>\n#include <solvers/block_common_solver.h>\n#include <thrust/transform.h>\n#include <basic_types.h>\n#include <string.h>\n#include <cutil.h>\n#include <util.h>\n#include <miscmath.h>\n#include <sm_utils.inl>\n\nnamespace amgx\n{\n\n// -----------\n// Kernels\n// -----------\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ static int ourHash(const int i, const int max)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return int(((a ^ 0x4a51e590) / (float)UINT_MAX) * max);\n}\n\nstruct prg\n{\n    float a, b;\n    int max_int;\n\n    __host__ __device__\n    prg(int _max_int, float _a = 0.f, float _b = 1.f) : a(_a), b(_b), max_int(_max_int) {};\n\n    __host__ __device__\n    int operator()(const unsigned int n) const\n    {\n        int  ru = ourHash(n, max_int);\n        return (ru);\n    }\n};\n\ntemplate <class Vector>\nvoid initRandom(Vector &vec, int size, int max_int)\n{\n    vec.resize(size);\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    amgx::thrust::transform(index_sequence_begin,\n                      index_sequence_begin + size,\n                      vec.begin(),\n                      prg(max_int));\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_anorm_kernel(const IndexType num_rows,\n                                     const IndexType *Ap,\n                                     const IndexType *Aj,\n                                     const ValueTypeA *Ax,\n                                     ValueTypeA *d)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n            d_ += Aij * Aij;\n        }\n\n        // Store L2-norm\n        d[ridx] = d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_multicolor_anorm_kernel(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const int *sorted_rows_by_color,\n        const int  num_rows_per_color)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows_per_color; ridx += blockDim.x * gridDim.x)\n    {\n        int i = sorted_rows_by_color[ridx];\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n            d_ += Aij * Aij;\n        }\n\n        // Store L2-norm\n        d[ridx] = d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_cumul_inv_kernel(const IndexType a_cum_num_rows,\n        ValueTypeA *a_cum,\n        ValueTypeB d_inv,\n        int c_inv_sz,\n        IndexType *c_inv)\n{\n    const int tidx = blockDim.x * blockIdx.x + threadIdx.x;\n    int idx, idx1, idx2;\n\n    for (int ridx = tidx; ridx < a_cum_num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        // printf(\"%d %f %f\\n\", ridx, d_inv, a_cum[ridx]);\n        //printf(\"%f\\n\", a_cum[ridx]);\n        double a = a_cum[ridx];\n        // if (ridx < 0 || ridx >= a_cum_num_rows)\n        //   printf(\"!! %d %d\\n\", ridx, idx);\n        idx1 = int(a / d_inv) - 1; // get index in inverse table (floor - 1)\n\n        if (ridx < a_cum_num_rows - 1)\n        {\n            idx2 = a_cum[ridx + 1] / d_inv - 1; // get index in inverse table (floor - 1)\n        }\n        else\n        {\n            idx2 = c_inv_sz;\n        }\n\n        // printf(\"%d %d\\n\", idx1, idx2);\n        for ( idx = idx1; idx < idx2; idx++)\n        {\n            if (idx >= c_inv_sz || idx < 0)\n            {\n                printf(\"Ai! %d %d\\n\", idx, ridx);\n            }\n\n            c_inv[idx] = ridx;\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_amax_kernel(const IndexType num_rows,\n                                    const IndexType *Ap,\n                                    const IndexType *Aj,\n                                    const ValueTypeA *Ax,\n                                    IndexType *amax_idx)\n{\n    ValueTypeA maxVal(0), avalue;\n    IndexType jmax;\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        jmax = row_start;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            avalue = Ax[j];\n\n            if (avalue > maxVal)\n            {\n                maxVal = avalue;\n                jmax = j;\n            }\n        }\n\n        // Store position of maxvalue\n        amax_idx[ridx] = jmax;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void kaczmarz_smooth_kernel_naive_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    // Naive implementation, needs x copy in xout at the very beginning\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * xout[Aj[j]];\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            //xout[Aj[j]] += r*Ax[j];\n            utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void kaczmarz_smooth_kernel_warp_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            //ValueTypeB dx =  j < row_end ? r*Ax[j] : ValueTypeB(0);\n            //int aj = j < row_end ? r*Ax[j] : ValueTypeB(0);\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void randomized_kaczmarz_smooth_kernel_warp_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const IndexType *c_inv,\n        const IndexType *rnd_rows,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        int irow = c_inv[rnd_rows[ridx]];\n        IndexType row_start = Ap[irow];\n        IndexType row_end   = Ap[irow + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n        ValueTypeA aa;\n        ValueTypeA AA = 0.0;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            aa = utils::warp_reduce<1, utils::Add>(aValue * aValue);\n            Axi += r;\n            AA += aa;\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( AA) ? AA : epsilon(AA) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            //ValueTypeB dx =  j < row_end ? r*Ax[j] : ValueTypeB(0);\n            //int aj = j < row_end ? r*Ax[j] : ValueTypeB(0);\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void kaczmarz_smooth_kernel(const IndexType num_rows,\n                                       const IndexType *Ap,\n                                       const IndexType *Aj,\n                                       const ValueTypeA *Ax,\n                                       const IndexType *amax,\n                                       const ValueTypeB *b,\n                                       const ValueTypeB *x,\n                                       ValueTypeB *xout,\n                                       const IndexType row_offset)\n\n{\n    // Naive implementation, needs x copy in xout at the very beginning\n    //IndexType tidx = blockDim.x*blockIdx.x + threadIdx.x;\n    IndexType i, t;\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n        i = ourHash(ridx, num_rows);\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n            //Axi += utils::Warp_reduce_linear<1,32>::execute<utils::Add,ValueTypeB>(aValue * xValue);\n            //Axi += Ax[j] * xout[Aj[j]];\n            printf(\"j = %d, r = %f\\n\", j, r);\n        }\n\n        if (laneId == 0)\n        {\n            r = (b[i] - Axi);// / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n            t = row_start + ourHash(ridx, row_end - row_start);\n            printf(\"ridx=%d, i=%d, t=%d, Aj[t]=%d, r=%f\\n\", ridx, i, t, Aj[t], r);\n            xout[Aj[t]] += r * ((row_end - row_start) * Ax[t]) * 0.5;\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void multicolor_kaczmarz_smooth_kernel_naive(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB weight,\n        const int *sorted_rows_by_color,\n        const int     num_rows_per_color,\n        ValueTypeB *xout)\n\n{\n    int i;\n    // Naive implementation, needs x copy in xout at the very beginning\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows_per_color; ridx += blockDim.x * gridDim.x)\n    {\n        i = sorted_rows_by_color[ridx];\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * xout[Aj[j]];\n        }\n\n        r = (b[i] - Axi) / ( isNotCloseToZero( d[i]) ? d[i] : epsilon(d[i]) );\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void multicolor_kaczmarz_smooth_kernel(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB weight,\n        const int *sorted_rows_by_color,\n        const int     num_rows_per_color,\n        ValueTypeB *xout)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n    int i;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows_per_color ;\n            ridx += num_rows_per_iter )\n    {\n        i = sorted_rows_by_color[ridx];\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n        }\n\n        r = (b[i] - Axi) / ( isNotCloseToZero( d[i]) ? d[i] : epsilon(d[i]) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_with_0_initial_guess_kernel(const IndexType num_rows,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        ValueTypeB *x,\n        ValueTypeB weight,\n        const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        x[ridx] = weight * b[ridx] /  ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n// -----------------\n// Methods\n// -----------------\n\n// Constructor\ntemplate<class T_Config>\nKaczmarzSolver_Base<T_Config>::KaczmarzSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), m_an(0), m_amax(0), m_c_inv(0)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_coloring_needed = (cfg.AMG_Config::template getParameter<int>(\"kaczmarz_coloring_needed\", cfg_scope) != 0);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_randomized = true;\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nKaczmarzSolver_Base<T_Config>::~KaczmarzSolver_Base()\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!m_explicit_A)\n    {\n        FatalError(\"Kaczmarz solver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    compute_anorm( *this->m_explicit_A );\n\n    if (m_randomized) // MC RK is not supported here\n    {\n        if (m_coloring_needed)\n        {\n            //FatalError(\"Randomized Kaczmarz solver does not support coloring\", AMGX_ERR_INTERNAL);\n            m_coloring_needed = false;\n        }\n\n        double d_inv = this->m_an[0];\n        int c_sz = this->m_an.size();\n        d_inv = amgx::thrust::reduce(this->m_an.begin(), this->m_an.end(), d_inv, amgx::thrust::minimum<ValueTypeA>());\n        amgx::thrust::inclusive_scan(this->m_an.begin(), this->m_an.end(), this->m_an.begin()); // in-place scan\n        int c_inv_sz = (this->m_an[c_sz - 1] + d_inv - 1 ) / d_inv;\n        this->m_c_inv.resize(c_inv_sz, -1);\n        const size_t THREADS_PER_BLOCK  = 128;\n        const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)c_sz / (ValueTypeB)THREADS_PER_BLOCK));\n\n        if (c_sz > 0)\n        {\n            device_vector_alloc<ValueTypeA> aa(c_sz, 1);\n            compute_cumul_inv_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n            (c_sz,\n             this->m_an.raw(),\n             d_inv,\n             c_inv_sz,\n             this->m_c_inv.raw());\n             cudaCheckError();\n        }\n\n        cudaDeviceSynchronize();\n        cudaCheckError();\n    }\n}\n\ntemplate<class T_Config>\nvoid KaczmarzSolver_Base<T_Config>::compute_anorm( Matrix<T_Config> &A)\n{\n    this->m_an.resize(A.get_num_rows()*A.get_block_dimx());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_anorm_1x1(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for KaczmarzSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate<class T_Config>\nvoid KaczmarzSolver_Base<T_Config>::compute_amax( Matrix<T_Config> &A)\n{\n    this->m_amax.resize(A.get_num_rows()*A.get_block_dimx());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_amax_1x1(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for KaczmarzSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_anorm_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_anorm_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_amax_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_amax_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_amax.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nKaczmarzSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n\n        if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = this->m_explicit_A->getViewExterior();\n    }\n    else\n    {\n        flags = this->m_explicit_A->getViewInterior();\n        this->m_explicit_A->setViewInterior();\n    }\n\n    if (this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            //smooth_with_0_initial_guess_1x1(*this->m_explicit_A, b, x, flags);\n            smooth_1x1(*this->m_explicit_A, b, x, flags, latencyHiding);\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, flags, latencyHiding);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Kaczmarz_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\n// Multicolor version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_MC(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flags & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flags == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 128;\n        const int blockrows_per_warp = 1;\n        const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color / blockrows_per_cta + 1));\n        multicolor_kaczmarz_smooth_kernel<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw(),\n         b.raw(),\n         x_ptr,\n         this->weight,\n         A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color,\n         x_ptr);\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    // Skipping Multi-GPU logic for now\n    // Current. Will be exact only with one warp per grid\n    const int threads_per_block = 32;\n    const int num_blocks = 1;\n\n    if (this->m_randomized)\n    {\n        IVector rnd_rows;\n        int c_inv_sz = this->m_c_inv.size();\n        initRandom(rnd_rows, A.get_num_rows(), c_inv_sz);\n        randomized_kaczmarz_smooth_kernel_warp_atomics<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_c_inv.raw(),\n         rnd_rows.raw(),\n         b.raw(),\n         x_ptr,\n         x_ptr,\n         offset);\n         cudaCheckError();\n    }\n    else\n    {\n        kaczmarz_smooth_kernel_warp_atomics<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw(),\n         b.raw(),\n         x_ptr,\n         x_ptr,\n         offset);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    if (this->m_coloring_needed)\n    {\n        smooth_1x1_MC(A, b, x, separation_flags, latency_hiding);\n    }\n    else\n    {\n        smooth_1x1_naive(A, b, x, separation_flags, latency_hiding);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    ViewType oldView = A.currentView();\n    // Process all rows\n    A.setViewExterior();\n    ViewType flags = A.getViewExterior();\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(flags, &offset, &num_rows);\n    A.setView(oldView);\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class KaczmarzSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class KaczmarzSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/kaczmarz_solver.h>\n#include <solvers/block_common_solver.h>\n#include <thrust/transform.h>\n#include <basic_types.h>\n#include <string.h>\n#include <cutil.h>\n#include <util.h>\n#include <miscmath.h>\n#include <sm_utils.inl>\n\nnamespace amgx\n{\n\n// -----------\n// Kernels\n// -----------\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ static int ourHash(const int i, const int max)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return int(((a ^ 0x4a51e590) / (float)UINT_MAX) * max);\n}\n\nstruct prg\n{\n    float a, b;\n    int max_int;\n\n    __host__ __device__\n    prg(int _max_int, float _a = 0.f, float _b = 1.f) : a(_a), b(_b), max_int(_max_int) {};\n\n    __host__ __device__\n    int operator()(const unsigned int n) const\n    {\n        int  ru = ourHash(n, max_int);\n        return (ru);\n    }\n};\n\ntemplate <class Vector>\nvoid initRandom(Vector &vec, int size, int max_int)\n{\n    vec.resize(size);\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    amgx::thrust::transform(index_sequence_begin,\n                      index_sequence_begin + size,\n                      vec.begin(),\n                      prg(max_int));\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_anorm_kernel(const IndexType num_rows,\n                                     const IndexType *Ap,\n                                     const IndexType *Aj,\n                                     const ValueTypeA *Ax,\n                                     ValueTypeA *d)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n            d_ += Aij * Aij;\n        }\n\n        // Store L2-norm\n        d[ridx] = d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_multicolor_anorm_kernel(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const int *sorted_rows_by_color,\n        const int  num_rows_per_color)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows_per_color; ridx += blockDim.x * gridDim.x)\n    {\n        int i = sorted_rows_by_color[ridx];\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n            d_ += Aij * Aij;\n        }\n\n        // Store L2-norm\n        d[ridx] = d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_cumul_inv_kernel(const IndexType a_cum_num_rows,\n        ValueTypeA *a_cum,\n        ValueTypeB d_inv,\n        int c_inv_sz,\n        IndexType *c_inv)\n{\n    const int tidx = blockDim.x * blockIdx.x + threadIdx.x;\n    int idx, idx1, idx2;\n\n    for (int ridx = tidx; ridx < a_cum_num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        // printf(\"%d %f %f\\n\", ridx, d_inv, a_cum[ridx]);\n        //printf(\"%f\\n\", a_cum[ridx]);\n        double a = a_cum[ridx];\n        // if (ridx < 0 || ridx >= a_cum_num_rows)\n        //   printf(\"!! %d %d\\n\", ridx, idx);\n        idx1 = int(a / d_inv) - 1; // get index in inverse table (floor - 1)\n\n        if (ridx < a_cum_num_rows - 1)\n        {\n            idx2 = a_cum[ridx + 1] / d_inv - 1; // get index in inverse table (floor - 1)\n        }\n        else\n        {\n            idx2 = c_inv_sz;\n        }\n\n        // printf(\"%d %d\\n\", idx1, idx2);\n        for ( idx = idx1; idx < idx2; idx++)\n        {\n            if (idx >= c_inv_sz || idx < 0)\n            {\n                printf(\"Ai! %d %d\\n\", idx, ridx);\n            }\n\n            c_inv[idx] = ridx;\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_amax_kernel(const IndexType num_rows,\n                                    const IndexType *Ap,\n                                    const IndexType *Aj,\n                                    const ValueTypeA *Ax,\n                                    IndexType *amax_idx)\n{\n    ValueTypeA maxVal(0), avalue;\n    IndexType jmax;\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        jmax = row_start;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            avalue = Ax[j];\n\n            if (avalue > maxVal)\n            {\n                maxVal = avalue;\n                jmax = j;\n            }\n        }\n\n        // Store position of maxvalue\n        amax_idx[ridx] = jmax;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void kaczmarz_smooth_kernel_naive_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    // Naive implementation, needs x copy in xout at the very beginning\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * xout[Aj[j]];\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            //xout[Aj[j]] += r*Ax[j];\n            utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void kaczmarz_smooth_kernel_warp_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            //ValueTypeB dx =  j < row_end ? r*Ax[j] : ValueTypeB(0);\n            //int aj = j < row_end ? r*Ax[j] : ValueTypeB(0);\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void randomized_kaczmarz_smooth_kernel_warp_atomics(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const IndexType *c_inv,\n        const IndexType *rnd_rows,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        int irow = c_inv[rnd_rows[ridx]];\n        IndexType row_start = Ap[irow];\n        IndexType row_end   = Ap[irow + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n        ValueTypeA aa;\n        ValueTypeA AA = 0.0;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            aa = utils::warp_reduce<1, utils::Add>(aValue * aValue);\n            Axi += r;\n            AA += aa;\n        }\n\n        r = (b[ridx] - Axi) / ( isNotCloseToZero( AA) ? AA : epsilon(AA) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            //ValueTypeB dx =  j < row_end ? r*Ax[j] : ValueTypeB(0);\n            //int aj = j < row_end ? r*Ax[j] : ValueTypeB(0);\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void kaczmarz_smooth_kernel(const IndexType num_rows,\n                                       const IndexType *Ap,\n                                       const IndexType *Aj,\n                                       const ValueTypeA *Ax,\n                                       const IndexType *amax,\n                                       const ValueTypeB *b,\n                                       const ValueTypeB *x,\n                                       ValueTypeB *xout,\n                                       const IndexType row_offset)\n\n{\n    // Naive implementation, needs x copy in xout at the very beginning\n    //IndexType tidx = blockDim.x*blockIdx.x + threadIdx.x;\n    IndexType i, t;\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows ;\n            ridx += num_rows_per_iter )\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n        i = ourHash(ridx, num_rows);\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n            //Axi += utils::Warp_reduce_linear<1,32>::execute<utils::Add,ValueTypeB>(aValue * xValue);\n            //Axi += Ax[j] * xout[Aj[j]];\n            printf(\"j = %d, r = %f\\n\", j, r);\n        }\n\n        if (laneId == 0)\n        {\n            r = (b[i] - Axi);// / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n            t = row_start + ourHash(ridx, row_end - row_start);\n            printf(\"ridx=%d, i=%d, t=%d, Aj[t]=%d, r=%f\\n\", ridx, i, t, Aj[t], r);\n            xout[Aj[t]] += r * ((row_end - row_start) * Ax[t]) * 0.5;\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void multicolor_kaczmarz_smooth_kernel_naive(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB weight,\n        const int *sorted_rows_by_color,\n        const int     num_rows_per_color,\n        ValueTypeB *xout)\n\n{\n    int i;\n    // Naive implementation, needs x copy in xout at the very beginning\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows_per_color; ridx += blockDim.x * gridDim.x)\n    {\n        i = sorted_rows_by_color[ridx];\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * xout[Aj[j]];\n        }\n\n        r = (b[i] - Axi) / ( isNotCloseToZero( d[i]) ? d[i] : epsilon(d[i]) );\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize>\n__global__ void multicolor_kaczmarz_smooth_kernel(const IndexType num_rows,\n        const IndexType *Ap,\n        const IndexType *Aj,\n        const ValueTypeA *Ax,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB weight,\n        const int *sorted_rows_by_color,\n        const int     num_rows_per_color,\n        ValueTypeB *xout)\n\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n    int i;\n\n    for ( int ridx = blockIdx.x * num_warps + warpId ; ridx < num_rows_per_color ;\n            ridx += num_rows_per_iter )\n    {\n        i = sorted_rows_by_color[ridx];\n        IndexType row_start = Ap[i];\n        IndexType row_end   = Ap[i + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeB r;\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            ValueTypeB aValue = j < row_end ? Ax[j] : ValueTypeB(0);\n            ValueTypeB xValue = j < row_end ? xout[Aj[j]] : ValueTypeB(0);\n            r = utils::warp_reduce<1, utils::Add>(aValue * xValue);\n            Axi += r;\n        }\n\n        r = (b[i] - Axi) / ( isNotCloseToZero( d[i]) ? d[i] : epsilon(d[i]) );\n\n        for (int j = row_start + laneId; utils::any( j < row_end) ; j += 32)\n        {\n            if (j < row_end)\n            {\n                utils::atomic_add(&xout[Aj[j]], r * Ax[j]);\n            }\n        }\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_with_0_initial_guess_kernel(const IndexType num_rows,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        ValueTypeB *x,\n        ValueTypeB weight,\n        const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        x[ridx] = weight * b[ridx] /  ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n// -----------------\n// Methods\n// -----------------\n\n// Constructor\ntemplate<class T_Config>\nKaczmarzSolver_Base<T_Config>::KaczmarzSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), m_an(0), m_amax(0), m_c_inv(0)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_coloring_needed = (cfg.AMG_Config::template getParameter<int>(\"kaczmarz_coloring_needed\", cfg_scope) != 0);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_randomized = true;\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nKaczmarzSolver_Base<T_Config>::~KaczmarzSolver_Base()\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!m_explicit_A)\n    {\n        FatalError(\"Kaczmarz solver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    compute_anorm( *this->m_explicit_A );\n\n    if (m_randomized) // MC RK is not supported here\n    {\n        if (m_coloring_needed)\n        {\n            //FatalError(\"Randomized Kaczmarz solver does not support coloring\", AMGX_ERR_INTERNAL);\n            m_coloring_needed = false;\n        }\n\n        double d_inv = this->m_an[0];\n        int c_sz = this->m_an.size();\n        d_inv = amgx::thrust::reduce(this->m_an.begin(), this->m_an.end(), d_inv, amgx::thrust::minimum<ValueTypeA>());\n        amgx::thrust::inclusive_scan(this->m_an.begin(), this->m_an.end(), this->m_an.begin()); // in-place scan\n        int c_inv_sz = (this->m_an[c_sz - 1] + d_inv - 1 ) / d_inv;\n        this->m_c_inv.resize(c_inv_sz, -1);\n        const size_t THREADS_PER_BLOCK  = 128;\n        const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)c_sz / (ValueTypeB)THREADS_PER_BLOCK));\n\n        if (c_sz > 0)\n        {\n            device_vector_alloc<ValueTypeA> aa(c_sz, 1);\n            compute_cumul_inv_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n            (c_sz,\n             this->m_an.raw(),\n             d_inv,\n             c_inv_sz,\n             this->m_c_inv.raw());\n             cudaCheckError();\n        }\n\n        hipDeviceSynchronize();\n        cudaCheckError();\n    }\n}\n\ntemplate<class T_Config>\nvoid KaczmarzSolver_Base<T_Config>::compute_anorm( Matrix<T_Config> &A)\n{\n    this->m_an.resize(A.get_num_rows()*A.get_block_dimx());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_anorm_1x1(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for KaczmarzSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate<class T_Config>\nvoid KaczmarzSolver_Base<T_Config>::compute_amax( Matrix<T_Config> &A)\n{\n    this->m_amax.resize(A.get_num_rows()*A.get_block_dimx());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_amax_1x1(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for KaczmarzSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_anorm_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_anorm_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_amax_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_amax_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_amax.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nKaczmarzSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n\n        if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = this->m_explicit_A->getViewExterior();\n    }\n    else\n    {\n        flags = this->m_explicit_A->getViewInterior();\n        this->m_explicit_A->setViewInterior();\n    }\n\n    if (this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            //smooth_with_0_initial_guess_1x1(*this->m_explicit_A, b, x, flags);\n            smooth_1x1(*this->m_explicit_A, b, x, flags, latencyHiding);\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, flags, latencyHiding);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Kaczmarz_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nKaczmarzSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\n// Multicolor version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_MC(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n    const IndexType *A_sorted_rows_by_color_ptr = A.getMatrixColoring().getSortedRowsByColor().raw();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = ((separation_flags & INTERIOR) == 0) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = ((separation_flags == this->m_explicit_A->getViewInterior()) ? A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i] : A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1]) - color_offset;\n\n        if (num_rows_per_color == 0) { continue; }\n\n        const int threads_per_block = 128;\n        const int blockrows_per_warp = 1;\n        const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color / blockrows_per_cta + 1));\n        multicolor_kaczmarz_smooth_kernel<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw(),\n         b.raw(),\n         x_ptr,\n         this->weight,\n         A_sorted_rows_by_color_ptr + color_offset, num_rows_per_color,\n         x_ptr);\n        cudaCheckError();\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1_naive(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    ValueTypeB *x_ptr = x.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n    // Skipping Multi-GPU logic for now\n    // Current. Will be exact only with one warp per grid\n    const int threads_per_block = 32;\n    const int num_blocks = 1;\n\n    if (this->m_randomized)\n    {\n        IVector rnd_rows;\n        int c_inv_sz = this->m_c_inv.size();\n        initRandom(rnd_rows, A.get_num_rows(), c_inv_sz);\n        randomized_kaczmarz_smooth_kernel_warp_atomics<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_c_inv.raw(),\n         rnd_rows.raw(),\n         b.raw(),\n         x_ptr,\n         x_ptr,\n         offset);\n         cudaCheckError();\n    }\n    else\n    {\n        kaczmarz_smooth_kernel_warp_atomics<IndexType, ValueTypeA, ValueTypeB, threads_per_block> <<< num_blocks, threads_per_block >>>\n        (A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_an.raw(),\n         b.raw(),\n         x_ptr,\n         x_ptr,\n         offset);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    if (this->m_coloring_needed)\n    {\n        smooth_1x1_MC(A, b, x, separation_flags, latency_hiding);\n    }\n    else\n    {\n        smooth_1x1_naive(A, b, x, separation_flags, latency_hiding);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid KaczmarzSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    ViewType oldView = A.currentView();\n    // Process all rows\n    A.setViewExterior();\n    ViewType flags = A.getViewExterior();\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(flags, &offset, &num_rows);\n    A.setView(oldView);\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class KaczmarzSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class KaczmarzSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/kaczmarz_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n\nnamespace amgx\n{\n\n// Constructor\ntemplate<class T_Config>\nGaussSeidelSolver_Base<T_Config>::GaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (this->weight == 0)\n    {\n        this->weight = 1.;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nGaussSeidelSolver_Base<T_Config>::~GaussSeidelSolver_Base()\n{\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"GaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if ( A_as_matrix->get_block_size() != 1 )\n    {\n        FatalError(\"Unsupported block size for GaussSeidelSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    this->diag.resize((int) A_as_matrix->get_num_rows());\n\n    if (A_as_matrix->hasProps(DIAG))\n    {\n        amgx::thrust::copy( A_as_matrix->values.begin() + A_as_matrix->diagOffset()*A_as_matrix->get_block_size()/*block_size == 1*/, A_as_matrix->values.end(), this->diag.begin());\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag(*A_as_matrix);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if ( A_as_matrix->get_block_size() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1( *A_as_matrix, b, x );\n        }\n        else\n        {\n            smooth_1x1( *A_as_matrix, b, x );\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GaussSeidel_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::find_diag( const Matrix<TConfig> &in_A )\n{\n    if (in_A.get_block_size() == 1)\n    {\n        find_diag_1x1(in_A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GaussSeidelSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n/********************************************/\n\n// Finding diag on host, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag_1x1(const Matrix_h &A)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->diag[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                std::string error = \"Could not find a diagonal value at row \" + std::to_string(i);\n                FatalError(error.c_str(), AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeA diag = isNotCloseToZero(this->diag[i]) ? this->diag[i] : epsilon(this->diag[i]);\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        x[i] = x[i] + this->weight * (b[i] - Axi) / diag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeA diag = isNotCloseToZero(this->diag[i]) ? this->diag[i] : epsilon(this->diag[i]);\n        x[i] = this->weight * b[i] / diag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag_1x1(const Matrix_d &A)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueType)A.get_num_rows() / (ValueType)THREADS_PER_BLOCK));\n    this->diag.resize(A.get_num_rows());\n    find_diag_kernel<IndexType, ValueType> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->diag.raw());\n    cudaCheckError();\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__ void GS_smooth_kernel(const IndexType num_rows,\n                                 const IndexType *Ap,\n                                 const IndexType *Aj,\n                                 const ValueTypeA *Ax,\n                                 const ValueTypeB *diag,\n                                 const ValueTypeB *b,\n                                 const ValueTypeB weight,\n                                 ValueTypeB *x)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeA mydiag = isNotCloseToZero(diag[ridx]) ? diag[ridx] : epsilon(diag[ridx]);\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * x[Aj[j]];\n        }\n\n        ValueTypeB tmp = x[ridx] + weight * (b[ridx] - Axi) / mydiag;\n        x[ridx] = tmp;\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename VVector::value_type ValueTypeB;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n    GS_smooth_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->diag.raw(),\n     b.raw(),\n     this->weight,\n     x.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    thrust_wrapper::fill<AMGX_device>(x.begin(), x.end(), ValueTypeB(0));\n    cudaCheckError();\n    smooth_1x1(A, b, x);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class GaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class GaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/gauss_seidel_solver.h>\n#include <solvers/block_common_solver.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n\nnamespace amgx\n{\n\n// Constructor\ntemplate<class T_Config>\nGaussSeidelSolver_Base<T_Config>::GaussSeidelSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (this->weight == 0)\n    {\n        this->weight = 1.;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nGaussSeidelSolver_Base<T_Config>::~GaussSeidelSolver_Base()\n{\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"GaussSeidelSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if ( A_as_matrix->get_block_size() != 1 )\n    {\n        FatalError(\"Unsupported block size for GaussSeidelSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    this->diag.resize((int) A_as_matrix->get_num_rows());\n\n    if (A_as_matrix->hasProps(DIAG))\n    {\n        amgx::thrust::copy( A_as_matrix->values.begin() + A_as_matrix->diagOffset()*A_as_matrix->get_block_size()/*block_size == 1*/, A_as_matrix->values.end(), this->diag.begin());\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag(*A_as_matrix);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nGaussSeidelSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if ( A_as_matrix->get_block_size() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1( *A_as_matrix, b, x );\n        }\n        else\n        {\n            smooth_1x1( *A_as_matrix, b, x );\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GaussSeidel_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\ntemplate<class T_Config>\nvoid\nGaussSeidelSolver_Base<T_Config>::find_diag( const Matrix<TConfig> &in_A )\n{\n    if (in_A.get_block_size() == 1)\n    {\n        find_diag_1x1(in_A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GaussSeidelSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n/********************************************/\n\n// Finding diag on host, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag_1x1(const Matrix_h &A)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->diag[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                std::string error = \"Could not find a diagonal value at row \" + std::to_string(i);\n                FatalError(error.c_str(), AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeA diag = isNotCloseToZero(this->diag[i]) ? this->diag[i] : epsilon(this->diag[i]);\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        x[i] = x[i] + this->weight * (b[i] - Axi) / diag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeA diag = isNotCloseToZero(this->diag[i]) ? this->diag[i] : epsilon(this->diag[i]);\n        x[i] = this->weight * b[i] / diag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag_1x1(const Matrix_d &A)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueType)A.get_num_rows() / (ValueType)THREADS_PER_BLOCK));\n    this->diag.resize(A.get_num_rows());\n    find_diag_kernel<IndexType, ValueType> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->diag.raw());\n    cudaCheckError();\n}\n\ntemplate<typename IndexType, typename ValueTypeA,  typename ValueTypeB>\n__global__ void GS_smooth_kernel(const IndexType num_rows,\n                                 const IndexType *Ap,\n                                 const IndexType *Aj,\n                                 const ValueTypeA *Ax,\n                                 const ValueTypeB *diag,\n                                 const ValueTypeB *b,\n                                 const ValueTypeB weight,\n                                 ValueTypeB *x)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n        ValueTypeA mydiag = isNotCloseToZero(diag[ridx]) ? diag[ridx] : epsilon(diag[ridx]);\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * x[Aj[j]];\n        }\n\n        ValueTypeB tmp = x[ridx] + weight * (b[ridx] - Axi) / mydiag;\n        x[ridx] = tmp;\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename VVector::value_type ValueTypeB;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n    GS_smooth_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->diag.raw(),\n     b.raw(),\n     this->weight,\n     x.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GaussSeidelSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    thrust_wrapper::fill<AMGX_device>(x.begin(), x.end(), ValueTypeB(0));\n    cudaCheckError();\n    smooth_1x1(A, b, x);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class GaussSeidelSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class GaussSeidelSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/gauss_seidel_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/polynomial_solver.h>\n#include <solvers/block_common_solver.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <multiply.h>\n#include <miscmath.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#include <cusp/relaxation/polynomial.h>\n#include <cusp/detail/spectral_radius.h>\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <matrix_cusp.h>\n\n\nnamespace amgx\n{\nnamespace polynomial_solver\n{\n\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid aux_norm1_csr(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_values, const ValueTypeA *nonzero_values,\n                   const int num_rows, int bsize, ValueTypeB *row_sum)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_rows)\n    {\n        ValueTypeB tmp_sum = 0;\n        int idx_i = tid / bsize;\n        int offset_i = tid % bsize;\n\n        for (int i = 0; i < bsize; i++)\n        {\n            tmp_sum += fabs(nonzero_values[dia_values[idx_i] * bsize_sq + offset_i * bsize + i]);\n        }\n\n        for (int j = row_offsets[idx_i]; j < row_offsets[idx_i + 1]; j++)\n        {\n            // Compute edge weight\n            for (int i = 0; i < bsize; i++)\n            {\n                tmp_sum += fabs(nonzero_values[j * bsize_sq + offset_i * bsize + i]);\n            }\n        }\n\n        row_sum[tid] = tmp_sum / fabs(nonzero_values[dia_values[idx_i] * bsize_sq + offset_i * bsize + offset_i]);\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid get_diaginv(const IndexType *dia_idx, const ValueTypeA *nonzero_values,\n                 const int num_rows, int bsize, ValueTypeB *Dinv)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_rows)\n    {\n        int idx_i = tid / bsize;\n        int offset_i = tid % bsize;\n        ValueTypeB diag = nonzero_values[dia_idx[idx_i] * bsize_sq + offset_i * bsize + offset_i];\n        Dinv[tid] = ValueTypeB(1) / (isNotCloseToZero(diag) ? diag : epsilon(diag) );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n// Constructor\ntemplate<class T_Config>\nPolynomialSolverBase<T_Config>::PolynomialSolverBase( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), R(0)\n{\n    ndeg0 = cfg.AMG_Config::template getParameter<int>(\"kpz_order\", cfg_scope);\n}\n\n// Destructor\ntemplate<class T_Config>\nPolynomialSolverBase<T_Config>::~PolynomialSolverBase()\n{\n}\n\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::printSolverParameters() const\n{\n    std::cout << \"kpz_order = \" << this->ndeg0 << std::endl;\n}\n\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"PolynomialSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (ndeg0 == 0) { ndeg0 = 6; }\n\n    int N = this->m_explicit_A->get_num_rows() * this->m_explicit_A->get_block_dimy();\n    ValueTypeA  mu0, mu1, smu0, smu1;\n    const IndexType *A_row_offsets_ptr = this->m_explicit_A->row_offsets.raw();\n    const IndexType *A_column_indices_ptr = this->m_explicit_A->col_indices.raw();\n    const IndexType *A_dia_ptr = this->m_explicit_A->diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = this->m_explicit_A->values.raw();\n    VVector row_sum(N);\n    ValueTypeB *row_sum_ptr = row_sum.raw();\n    const int threads_per_block = 512;\n    const int num_blocks = std::min((N - 1) / threads_per_block + 1, AMGX_GRID_MAX_SIZE);\n    aux_norm1_csr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ptr, A_nonzero_values_ptr,\n            N, this->m_explicit_A->get_block_dimy(), row_sum_ptr);\n    cudaCheckError();\n    mu0 = nrmmax(row_sum);\n    cudaCheckError();\n\n    if (mu0 == 0) { mu0 = nrmmax(this->m_explicit_A->values); }\n\n    cudaCheckError();\n    mu0 = 1.0 / mu0;\n    mu1 = 4.0 * mu0; // default set 8;\n    smu0 =  sqrt(mu0);\n    smu1 = sqrt(mu1);\n    k[1] = (mu0 + mu1) / 2.0;\n    k[2] = (smu0 + smu1) * (smu0 + smu1) / 2.0;\n    k[3] = mu0 * mu1;\n    k[4] = 2.0 * k[3] / k[2]; // 4.0*mu0*mu1/(sqrt(mu0)+sqrt(mu1))/(sqrt(mu0)+sqrt(mu1));\n    k[5] = (mu1 - 2.0 * smu0 * smu1 + mu0) / (mu1 + 2.0 * smu0 * smu1 + mu0); // square of (sqrt(kappa)-1)/(sqrt(kappa)+1);\n\n    if (this->m_explicit_A->get_block_size() == 1)\n    {\n        MatrixCusp<T_Config, cusp::csr_format> wA((Matrix<T_Config> *) &*this->m_explicit_A);\n        ValueTypeA rho = cusp::detail::ritz_spectral_radius_symmetric(wA, 8);\n        cudaCheckError();\n        cusp::array1d<ValueTypeA, cusp::host_memory> coeffs;\n        cusp::relaxation::detail::chebyshev_polynomial_coefficients(rho, coeffs);\n        cudaCheckError();\n        poly = cusp::relaxation::polynomial<ValueTypeA, typename Matrix<T_Config>::memory_space > (wA, coeffs);\n        cudaCheckError();\n    }\n\n    R.resize(N);\n    V0.resize(N);\n    V.resize(N);\n    Rbar.resize(N);\n    Sn.resize(N);\n    Dinv.resize(N);\n    R.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    R.set_block_dimx(1);\n    V0.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    V0.set_block_dimx(1);\n    V.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    V.set_block_dimx(1);\n    Rbar.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Rbar.set_block_dimx(1);\n    Sn.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Sn.set_block_dimx(1);\n    Dinv.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Dinv.set_block_dimx(1);\n    ValueTypeB *Dinv_ptr = Dinv.raw();\n    get_diaginv <<< num_blocks, threads_per_block>>>(A_dia_ptr, A_nonzero_values_ptr, N, this->m_explicit_A->get_block_dimy(), Dinv_ptr);\n    cudaCheckError();\n}\n\n//\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nPolynomialSolverBase<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    smooth_common_sqblocks( *this->m_explicit_A, b, x );\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\ntemplate<class T_Config>\nstruct poly_smooth\n{\n    static void poly_postsmooth(const Matrix<T_Config> &A, const Vector<T_Config> &B, Vector<T_Config> &C, cusp::relaxation::polynomial<typename T_Config::MatPrec, typename T_Config::MemSpace> &poly)\n    {\n        FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    static void poly_presmooth(const Matrix<T_Config> &A, const Vector<T_Config> &B, Vector<T_Config> &C, cusp::relaxation::polynomial<typename T_Config::MatPrec, typename T_Config::MemSpace> &poly)\n    {\n        FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n};\n\ntemplate<AMGX_MemorySpace t_memSpace, AMGX_IndPrecision t_indInt>\nstruct poly_smooth<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>>\n{\n    static void poly_postsmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &C, cusp::relaxation::polynomial<double, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > *) &A);\n        poly.postsmooth(wA, B, C);\n        cudaCheckError();\n    }\n\n    static void poly_presmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &C, cusp::relaxation::polynomial<double, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > *) &A);\n        poly.presmooth(wA, B, C);\n        cudaCheckError();\n    }\n};\n\ntemplate<AMGX_MemorySpace t_memSpace, AMGX_IndPrecision t_indInt>\nstruct poly_smooth<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>>\n{\n    static void poly_postsmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &C, cusp::relaxation::polynomial<float, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > *) &A);\n        poly.postsmooth(wA, B, C);\n        cudaCheckError();\n    }\n\n    static void poly_presmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &C, cusp::relaxation::polynomial<float, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > *) &A);\n        poly.presmooth(wA, B, C);\n        cudaCheckError();\n    }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &B, VVector &C)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &B, VVector &C)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported block size for PolynomialSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    poly_smooth<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>>::poly_postsmooth(A, B, C, this->poly);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_common_sqblocks(Matrix_h &A, const VVector &b, VVector &x)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_common_sqblocks(Matrix_d &A, const VVector &b, VVector &x)\n{\n    //0, get residule : R = b - Ax\n    multiply( A, x, this->R );\n    cusp::blas::axpby( b, this->R, this->R, ValueTypeB(1), ValueTypeB(-1) );\n    //1, set up rbar\n    cusp::blas::xmy(this->Dinv, this->R, this->Rbar);\n    //2, set up V and V0\n    multiply( A, this->Rbar, this->V );\n    cusp::blas::xmy(this->Dinv, this->V, this->V);\n    this->V0 = this->Rbar;\n    cusp::blas::scal(this->V0, this->k[1]);\n    cusp::blas::axpby(this->Rbar, this->V, this->V, this->k[2], this->k[3]);\n\n    //3, iterate to get v_{j+1}\n    for (int i = 0; i < this->ndeg0; i++)\n    {\n        multiply( A, this->V, this->Rbar );\n        cusp::blas::axpby( this->R, this->Rbar, this->Rbar, ValueTypeB(1), ValueTypeB(-1) );\n        cusp::blas::xmy(this->Dinv, this->Rbar, this->Rbar);\n        cusp::blas::axpbypcz(this->Rbar, this->V, this->V0, this->Sn,\n                             this->k[4], this->k[5] + 1.0, -1.0 * this->k[5]);\n        // V0 = V\n        this->V0 = this->V;\n        // V = V+Sn\n        cusp::blas::copy(this->Sn, this->V);\n    }\n\n    //4, update solution\n    cusp::blas::axpy(this->V, x, ValueTypeB(1));\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported block size for PolynomialSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    poly_smooth<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>>::poly_presmooth(A, b, x, this->poly);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class PolynomialSolverBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class PolynomialSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n\n\n} // namespace polynomial_solver\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/polynomial_solver.h>\n#include <solvers/block_common_solver.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <multiply.h>\n#include <miscmath.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#include <cusp/relaxation/polynomial.h>\n#include <cusp/detail/spectral_radius.h>\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <matrix_cusp.h>\n\n\nnamespace amgx\n{\nnamespace polynomial_solver\n{\n\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid aux_norm1_csr(const IndexType *row_offsets, const IndexType *column_indices, const IndexType *dia_values, const ValueTypeA *nonzero_values,\n                   const int num_rows, int bsize, ValueTypeB *row_sum)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_rows)\n    {\n        ValueTypeB tmp_sum = 0;\n        int idx_i = tid / bsize;\n        int offset_i = tid % bsize;\n\n        for (int i = 0; i < bsize; i++)\n        {\n            tmp_sum += fabs(nonzero_values[dia_values[idx_i] * bsize_sq + offset_i * bsize + i]);\n        }\n\n        for (int j = row_offsets[idx_i]; j < row_offsets[idx_i + 1]; j++)\n        {\n            // Compute edge weight\n            for (int i = 0; i < bsize; i++)\n            {\n                tmp_sum += fabs(nonzero_values[j * bsize_sq + offset_i * bsize + i]);\n            }\n        }\n\n        row_sum[tid] = tmp_sum / fabs(nonzero_values[dia_values[idx_i] * bsize_sq + offset_i * bsize + offset_i]);\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid get_diaginv(const IndexType *dia_idx, const ValueTypeA *nonzero_values,\n                 const int num_rows, int bsize, ValueTypeB *Dinv)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_rows)\n    {\n        int idx_i = tid / bsize;\n        int offset_i = tid % bsize;\n        ValueTypeB diag = nonzero_values[dia_idx[idx_i] * bsize_sq + offset_i * bsize + offset_i];\n        Dinv[tid] = ValueTypeB(1) / (isNotCloseToZero(diag) ? diag : epsilon(diag) );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n// Constructor\ntemplate<class T_Config>\nPolynomialSolverBase<T_Config>::PolynomialSolverBase( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), R(0)\n{\n    ndeg0 = cfg.AMG_Config::template getParameter<int>(\"kpz_order\", cfg_scope);\n}\n\n// Destructor\ntemplate<class T_Config>\nPolynomialSolverBase<T_Config>::~PolynomialSolverBase()\n{\n}\n\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::printSolverParameters() const\n{\n    std::cout << \"kpz_order = \" << this->ndeg0 << std::endl;\n}\n\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"PolynomialSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (ndeg0 == 0) { ndeg0 = 6; }\n\n    int N = this->m_explicit_A->get_num_rows() * this->m_explicit_A->get_block_dimy();\n    ValueTypeA  mu0, mu1, smu0, smu1;\n    const IndexType *A_row_offsets_ptr = this->m_explicit_A->row_offsets.raw();\n    const IndexType *A_column_indices_ptr = this->m_explicit_A->col_indices.raw();\n    const IndexType *A_dia_ptr = this->m_explicit_A->diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = this->m_explicit_A->values.raw();\n    VVector row_sum(N);\n    ValueTypeB *row_sum_ptr = row_sum.raw();\n    const int threads_per_block = 512;\n    const int num_blocks = std::min((N - 1) / threads_per_block + 1, AMGX_GRID_MAX_SIZE);\n    aux_norm1_csr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_ptr, A_nonzero_values_ptr,\n            N, this->m_explicit_A->get_block_dimy(), row_sum_ptr);\n    cudaCheckError();\n    mu0 = nrmmax(row_sum);\n    cudaCheckError();\n\n    if (mu0 == 0) { mu0 = nrmmax(this->m_explicit_A->values); }\n\n    cudaCheckError();\n    mu0 = 1.0 / mu0;\n    mu1 = 4.0 * mu0; // default set 8;\n    smu0 =  sqrt(mu0);\n    smu1 = sqrt(mu1);\n    k[1] = (mu0 + mu1) / 2.0;\n    k[2] = (smu0 + smu1) * (smu0 + smu1) / 2.0;\n    k[3] = mu0 * mu1;\n    k[4] = 2.0 * k[3] / k[2]; // 4.0*mu0*mu1/(sqrt(mu0)+sqrt(mu1))/(sqrt(mu0)+sqrt(mu1));\n    k[5] = (mu1 - 2.0 * smu0 * smu1 + mu0) / (mu1 + 2.0 * smu0 * smu1 + mu0); // square of (sqrt(kappa)-1)/(sqrt(kappa)+1);\n\n    if (this->m_explicit_A->get_block_size() == 1)\n    {\n        MatrixCusp<T_Config, cusp::csr_format> wA((Matrix<T_Config> *) &*this->m_explicit_A);\n        ValueTypeA rho = cusp::detail::ritz_spectral_radius_symmetric(wA, 8);\n        cudaCheckError();\n        cusp::array1d<ValueTypeA, cusp::host_memory> coeffs;\n        cusp::relaxation::detail::chebyshev_polynomial_coefficients(rho, coeffs);\n        cudaCheckError();\n        poly = cusp::relaxation::polynomial<ValueTypeA, typename Matrix<T_Config>::memory_space > (wA, coeffs);\n        cudaCheckError();\n    }\n\n    R.resize(N);\n    V0.resize(N);\n    V.resize(N);\n    Rbar.resize(N);\n    Sn.resize(N);\n    Dinv.resize(N);\n    R.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    R.set_block_dimx(1);\n    V0.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    V0.set_block_dimx(1);\n    V.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    V.set_block_dimx(1);\n    Rbar.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Rbar.set_block_dimx(1);\n    Sn.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Sn.set_block_dimx(1);\n    Dinv.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    Dinv.set_block_dimx(1);\n    ValueTypeB *Dinv_ptr = Dinv.raw();\n    get_diaginv <<< num_blocks, threads_per_block>>>(A_dia_ptr, A_nonzero_values_ptr, N, this->m_explicit_A->get_block_dimy(), Dinv_ptr);\n    cudaCheckError();\n}\n\n//\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nPolynomialSolverBase<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    smooth_common_sqblocks( *this->m_explicit_A, b, x );\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nPolynomialSolverBase<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\ntemplate<class T_Config>\nstruct poly_smooth\n{\n    static void poly_postsmooth(const Matrix<T_Config> &A, const Vector<T_Config> &B, Vector<T_Config> &C, cusp::relaxation::polynomial<typename T_Config::MatPrec, typename T_Config::MemSpace> &poly)\n    {\n        FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    static void poly_presmooth(const Matrix<T_Config> &A, const Vector<T_Config> &B, Vector<T_Config> &C, cusp::relaxation::polynomial<typename T_Config::MatPrec, typename T_Config::MemSpace> &poly)\n    {\n        FatalError(\"Mixed precision is not supported for scalar matrix type\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n};\n\ntemplate<AMGX_MemorySpace t_memSpace, AMGX_IndPrecision t_indInt>\nstruct poly_smooth<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>>\n{\n    static void poly_postsmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &C, cusp::relaxation::polynomial<double, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > *) &A);\n        poly.postsmooth(wA, B, C);\n        cudaCheckError();\n    }\n\n    static void poly_presmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > &C, cusp::relaxation::polynomial<double, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecDouble, AMGX_matDouble, t_indInt> > *) &A);\n        poly.presmooth(wA, B, C);\n        cudaCheckError();\n    }\n};\n\ntemplate<AMGX_MemorySpace t_memSpace, AMGX_IndPrecision t_indInt>\nstruct poly_smooth<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>>\n{\n    static void poly_postsmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &C, cusp::relaxation::polynomial<float, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > *) &A);\n        poly.postsmooth(wA, B, C);\n        cudaCheckError();\n    }\n\n    static void poly_presmooth(const Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &A, const Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &B, Vector<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > &C, cusp::relaxation::polynomial<float, typename MemorySpaceMap<t_memSpace>::Type> &poly)\n    {\n        MatrixCusp<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt>, cusp::csr_format> wA((Matrix<TemplateConfig<t_memSpace, AMGX_vecFloat, AMGX_matFloat, t_indInt> > *) &A);\n        poly.presmooth(wA, B, C);\n        cudaCheckError();\n    }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_h &A, const VVector &B, VVector &C)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(const Matrix_d &A, const VVector &B, VVector &C)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported block size for PolynomialSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    poly_smooth<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>>::poly_postsmooth(A, B, C, this->poly);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_common_sqblocks(Matrix_h &A, const VVector &b, VVector &x)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_common_sqblocks(Matrix_d &A, const VVector &b, VVector &x)\n{\n    //0, get residule : R = b - Ax\n    multiply( A, x, this->R );\n    cusp::blas::axpby( b, this->R, this->R, ValueTypeB(1), ValueTypeB(-1) );\n    //1, set up rbar\n    cusp::blas::xmy(this->Dinv, this->R, this->Rbar);\n    //2, set up V and V0\n    multiply( A, this->Rbar, this->V );\n    cusp::blas::xmy(this->Dinv, this->V, this->V);\n    this->V0 = this->Rbar;\n    cusp::blas::scal(this->V0, this->k[1]);\n    cusp::blas::axpby(this->Rbar, this->V, this->V, this->k[2], this->k[3]);\n\n    //3, iterate to get v_{j+1}\n    for (int i = 0; i < this->ndeg0; i++)\n    {\n        multiply( A, this->V, this->Rbar );\n        cusp::blas::axpby( this->R, this->Rbar, this->Rbar, ValueTypeB(1), ValueTypeB(-1) );\n        cusp::blas::xmy(this->Dinv, this->Rbar, this->Rbar);\n        cusp::blas::axpbypcz(this->Rbar, this->V, this->V0, this->Sn,\n                             this->k[4], this->k[5] + 1.0, -1.0 * this->k[5]);\n        // V0 = V\n        this->V0 = this->V;\n        // V = V+Sn\n        cusp::blas::copy(this->Sn, this->V);\n    }\n\n    //4, update solution\n    cusp::blas::axpy(this->V, x, ValueTypeB(1));\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x)\n{\n    FatalError(\"Unsupported on host for Polynomial smoother\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PolynomialSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_block_size() != 1)\n    {\n        FatalError(\"Unsupported block size for PolynomialSolver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    poly_smooth<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec>>::poly_presmooth(A, b, x, this->poly);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class PolynomialSolverBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class PolynomialSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n\n\n} // namespace polynomial_solver\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/polynomial_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/jacobi_l1_solver.h>\n#include <solvers/block_common_solver.h>\n#include <thrust/transform.h>\n#include <basic_types.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <sm_utils.inl>\n#include <util.h>\n#include <multiply.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n\n#include <algorithm>\n\nnamespace amgx\n{\n\n// -----------\n// Kernels\n// -----------\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_l1_postsmooth(\n    int n, ValueTypeB omega, ValueTypeB* x, ValueTypeA* d_in, ValueTypeB* b_in, ValueTypeB* y_in)\n{\n    int gid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = gid; i < n; i += blockDim.x * gridDim.x)\n    {\n        ValueTypeA d = d_in[i];\n        ValueTypeB b = b_in[i];\n        ValueTypeB y = y_in[i];\n        d = ValueTypeA( 1 ) / (isNotCloseToZero(d) ? d : epsilon(d));\n        b -= y;\n        b *= omega;\n        x[i] = b * d + x[i];\n    }\n}\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_l1_postsmooth_zero(\n    int n, ValueTypeB omega, ValueTypeB* x, ValueTypeA* d_in, ValueTypeB* b_in)\n{\n    int gid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = gid; i < n; i += blockDim.x * gridDim.x)\n    {\n        ValueTypeA d = d_in[i];\n        ValueTypeB b = b_in[i];\n        x[i] = omega * b / (isNotCloseToZero(d) ? d : epsilon(d));\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_d_kernel(const IndexType num_rows,\n                                 const IndexType *Ap,\n                                 const IndexType *Aj,\n                                 const ValueTypeA *Ax,\n                                 ValueTypeA *d)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        // check if we need +ve or -ve d\n        bool is_npd = false;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n\n            if (Aj[j] == ridx && Aij < 0.) { is_npd = true; }\n\n            //if not the diagonal then compute the absolute value\n            //if(Aj[j]!=ridxa)  JE: must include diagonal here or else we can get cancellation and Nan results\n            Aij = fabs(Aij);\n            d_ += Aij;\n        }\n\n        // set sign of L1-norm appropriately\n        d[ridx] = (is_npd) ? -d_ : d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize>\n__global__ void compute_d_4x4_kernel(const IndexType num_rows,\n                                     const IndexType *A_row_offsets,\n                                     const IndexType *A_col_indices,\n                                     const IndexType *A_dia_indices,\n                                     const ValueTypeA *A_nonzero_values,\n                                     ValueTypeA *d)\n{\n    const int nWarps = CtaSize / 32; // Number of warps per block\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    __shared__ volatile ValueTypeA s_diag[nWarps][32];\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    while (aRowId < num_rows )\n    {\n        int aColBeg = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n        s_diag[warpId][laneId] = 0.;\n\n        //TODO: Should prefetch the column indices\n        for (int i = aColBeg; i < aColEnd; i += 2)\n        {\n            const int my_i = i + lane_id_div_16;\n            int aColId = my_i < aColEnd ? A_col_indices[my_i] : int (-1);\n            s_diag[warpId][laneId] += (aRowId != aColId && aColId != -1) ? fabs(A_nonzero_values[16 * my_i + lane_id_mod_16]) : 0.;\n        }\n\n        // Threads 0 to 15 compute inverse\n        if (lane_id_div_16 == 0)\n        {\n            s_diag[warpId][laneId] += s_diag[warpId][laneId + 16];\n            s_diag[warpId][laneId] += fabs( A_nonzero_values[A_dia_indices[aRowId] * 16 + laneId]);\n            const int offset = 16 * aRowId;\n            compute_block_inverse_row_major4x4_formula2<int, ValueTypeA, 4, true>\n            (s_diag[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, d);\n        }\n\n        aRowId += nWarps * gridDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_kernel(const IndexType num_rows,\n                                     const IndexType *Ap,\n                                     const IndexType *Aj,\n                                     const ValueTypeA *Ax,\n                                     const ValueTypeA *d,\n                                     const ValueTypeB *b,\n                                     const ValueTypeB *x,\n                                     ValueTypeB *xout,\n                                     const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * x[Aj[j]];\n        }\n\n        xout[ridx] = x[ridx] + (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize>\n__global__ void jacobi_smooth_4x4_kernel(const IndexType num_rows,\n        const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = blockrow_id;\n        // Load RHS and x\n        // groups of 4 threads load 32-byte, through texture, no wasted bandwidth\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            s_xtemp[tid] = x[offset] * (jcol != i);\n            //for some reason we do better in perf if we write to volatile shared memory and then read this back...\n            //s_xtemp[threadIdx.x] += delta[offset]*(row_colors[jcol] < current_color && current_color!= 0);\n            // Load nonzero_values\n            offset = jind * bsize * bsize + vec_entry_index * bsize;\n            loadAsVector<bsize>(nonzero_values + offset, temp);\n            // Do matrix multiply\n            s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                bmAx -= temp[m] * s_xtemp[s_offset++];\n            }\n        }\n\n        // Each thread stores bmAx in xtemp\n        s_xtemp[tid] = bmAx;\n        bmAx = 0.;\n        // Load Dinv\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(d + offset, temp);\n        // Do matrix-vector multiply\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset++];\n        }\n\n        xout[i * bsize + vec_entry_index] = bmAx;\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_with_0_initial_guess_kernel(const IndexType num_rows,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        ValueTypeB *x,\n        ValueTypeB weight,\n        const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        x[ridx] = weight * b[ridx] /  ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n// -----------------\n// Methods\n// -----------------\n\n// Constructor\ntemplate<class T_Config>\nJacobiL1Solver_Base<T_Config>::JacobiL1Solver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), m_d(0)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nJacobiL1Solver_Base<T_Config>::~JacobiL1Solver_Base()\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!m_explicit_A)\n    {\n        FatalError(\"JacobiL1Solver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    compute_d( *this->m_explicit_A );\n    int N = this->m_explicit_A->get_num_cols() * this->m_explicit_A->get_block_dimy();\n    this->y_tmp.resize(N);\n}\n\ntemplate<class T_Config>\nvoid JacobiL1Solver_Base<T_Config>::compute_d( Matrix<T_Config> &A)\n{\n    this->m_d.resize(A.get_num_rows()*A.get_block_size());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_d_1x1(A);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        compute_d_4x4(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for JacobiL1Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_1x1(const Matrix_h &A)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d_ = 0;\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            ValueTypeB Aij = A.values[j];\n            //if not the diagonal then compute the absolute value\n            //if(A.col_indices[j]!=i) JE have to include diagonal or risk cancellation if diagonal is negative\n            Aij = fabs(Aij);\n            d_ += Aij;\n        }\n\n        this->m_d[i] = d_;\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_4x4(const Matrix_h &A)\n{\n    FatalError(\"4x4 block matrices not supported on host\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_d_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_d.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_4x4(const Matrix_d &A)\n{\n    // now leaving !DIAG only\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const int cta_size = 128;\n    const int nWarps = cta_size / 32;\n    int grid_size = std::min( 1024, ( A.get_num_rows() + nWarps - 1 ) / nWarps );\n    compute_d_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, cta_size > <<< grid_size, cta_size >>>\n    (A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.diag.raw(),\n     A.values.raw(),\n     this->m_d.raw());\n    cudaCheckError();\n}\n\n\n\n\n\n//\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nJacobiL1Solver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    this->m_explicit_A->setViewExterior();\n\n    if (this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*this->m_explicit_A, b, x, this->m_explicit_A->getViewExterior());\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, this->m_explicit_A->getViewExterior(), false);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for JacobiL1_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    VVector newx((int)x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        ValueTypeA d = this->m_d[i];\n        newx[i] = x[i] + (b[i] - Axi) /  ( isNotCloseToZero( d) ? d : epsilon(d) );\n    }\n\n    x.swap(newx);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"4x4 block size not supported on host\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeA d = this->m_d[i];\n        x[i] = b[i] / ( isNotCloseToZero( d) ? d : epsilon(d) );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    this->y_tmp.set_block_dimx(b.get_block_dimx());\n    this->y_tmp.set_block_dimy(b.get_block_dimy());\n\n    multiply(A, x, this->y_tmp, A.getViewExterior());\n\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &num_rows);\n\n    int nthreads_per_block = 128;\n    int n = num_rows - offset;\n    int nblocks = n / nthreads_per_block + 1;\n    jacobi_l1_postsmooth<<<nblocks, nthreads_per_block>>>(n, this->weight, x.raw() + offset, this->m_d.raw() + offset, b.raw() + offset, this->y_tmp.raw() + offset);\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    VVector xout(x.size());\n    ValueTypeB *x_ptr = x.raw();\n    ValueTypeB *xout_ptr = xout.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n\n    //It is only safe to swap these vectors if there is no halo exchange in process\n    if (separation_flags != this->m_explicit_A->getViewExterior())\n    {\n        amgx::thrust::copy(x.begin(), x.end(), xout.begin()); //TODO: only interior+bndry part\n        cudaCheckError();\n        x_ptr = xout.raw();\n        xout_ptr = x.raw();\n\n        if (separation_flags & INTERIOR)\n        {\n        }\n        else\n        {\n            A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n        }\n    }\n\n    const int threads_per_block = 128;\n    const int blockrows_per_warp = 32 / 4;\n    const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    jacobi_smooth_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 4> <<< num_blocks, threads_per_block >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->m_d.raw(),\n     b.raw(),\n     x_ptr,\n     xout_ptr, offset);\n    cudaCheckError();\n\n    if ((separation_flags != this->m_explicit_A->getViewExterior())/* && (separation_flags & INTERIOR)*/) {}\n    else { x.swap(xout); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    ViewType oldView = A.currentView();\n    // Process all rows\n    A.setViewExterior();\n\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &num_rows);\n\n    int nthreads_per_block = 128;\n    int n = num_rows - offset;\n    int nblocks = n / nthreads_per_block + 1;\n    jacobi_l1_postsmooth_zero<<<nblocks, nthreads_per_block>>>(n, this->weight, x.raw() + offset, this->m_d.raw() + offset, b.raw() + offset);\n    cudaCheckError();\n\n    A.setView(oldView);\n    cudaCheckError();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class JacobiL1Solver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class JacobiL1Solver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/jacobi_l1_solver.h>\n#include <solvers/block_common_solver.h>\n#include <thrust/transform.h>\n#include <basic_types.h>\n#include <blas.h>\n#include <string.h>\n#include <cutil.h>\n#include <sm_utils.inl>\n#include <util.h>\n#include <multiply.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n\n#include <algorithm>\n\nnamespace amgx\n{\n\n// -----------\n// Kernels\n// -----------\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_l1_postsmooth(\n    int n, ValueTypeB omega, ValueTypeB* x, ValueTypeA* d_in, ValueTypeB* b_in, ValueTypeB* y_in)\n{\n    int gid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = gid; i < n; i += blockDim.x * gridDim.x)\n    {\n        ValueTypeA d = d_in[i];\n        ValueTypeB b = b_in[i];\n        ValueTypeB y = y_in[i];\n        d = ValueTypeA( 1 ) / (isNotCloseToZero(d) ? d : epsilon(d));\n        b -= y;\n        b *= omega;\n        x[i] = b * d + x[i];\n    }\n}\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_l1_postsmooth_zero(\n    int n, ValueTypeB omega, ValueTypeB* x, ValueTypeA* d_in, ValueTypeB* b_in)\n{\n    int gid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = gid; i < n; i += blockDim.x * gridDim.x)\n    {\n        ValueTypeA d = d_in[i];\n        ValueTypeB b = b_in[i];\n        x[i] = omega * b / (isNotCloseToZero(d) ? d : epsilon(d));\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void compute_d_kernel(const IndexType num_rows,\n                                 const IndexType *Ap,\n                                 const IndexType *Aj,\n                                 const ValueTypeA *Ax,\n                                 ValueTypeA *d)\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        ValueTypeB d_ = 0;\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        // check if we need +ve or -ve d\n        bool is_npd = false;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            ValueTypeB Aij = Ax[j];\n\n            if (Aj[j] == ridx && Aij < 0.) { is_npd = true; }\n\n            //if not the diagonal then compute the absolute value\n            //if(Aj[j]!=ridxa)  JE: must include diagonal here or else we can get cancellation and Nan results\n            Aij = fabs(Aij);\n            d_ += Aij;\n        }\n\n        // set sign of L1-norm appropriately\n        d[ridx] = (is_npd) ? -d_ : d_;\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize>\n__global__ void compute_d_4x4_kernel(const IndexType num_rows,\n                                     const IndexType *A_row_offsets,\n                                     const IndexType *A_col_indices,\n                                     const IndexType *A_dia_indices,\n                                     const ValueTypeA *A_nonzero_values,\n                                     ValueTypeA *d)\n{\n    const int nWarps = CtaSize / 32; // Number of warps per block\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    __shared__ volatile ValueTypeA s_diag[nWarps][32];\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    while (aRowId < num_rows )\n    {\n        int aColBeg = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n        s_diag[warpId][laneId] = 0.;\n\n        //TODO: Should prefetch the column indices\n        for (int i = aColBeg; i < aColEnd; i += 2)\n        {\n            const int my_i = i + lane_id_div_16;\n            int aColId = my_i < aColEnd ? A_col_indices[my_i] : int (-1);\n            s_diag[warpId][laneId] += (aRowId != aColId && aColId != -1) ? fabs(A_nonzero_values[16 * my_i + lane_id_mod_16]) : 0.;\n        }\n\n        // Threads 0 to 15 compute inverse\n        if (lane_id_div_16 == 0)\n        {\n            s_diag[warpId][laneId] += s_diag[warpId][laneId + 16];\n            s_diag[warpId][laneId] += fabs( A_nonzero_values[A_dia_indices[aRowId] * 16 + laneId]);\n            const int offset = 16 * aRowId;\n            compute_block_inverse_row_major4x4_formula2<int, ValueTypeA, 4, true>\n            (s_diag[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, d);\n        }\n\n        aRowId += nWarps * gridDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_kernel(const IndexType num_rows,\n                                     const IndexType *Ap,\n                                     const IndexType *Aj,\n                                     const ValueTypeA *Ax,\n                                     const ValueTypeA *d,\n                                     const ValueTypeB *b,\n                                     const ValueTypeB *x,\n                                     ValueTypeB *xout,\n                                     const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        IndexType row_start = Ap[ridx];\n        IndexType row_end   = Ap[ridx + 1];\n        ValueTypeB Axi = 0.0;\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            Axi += Ax[j] * x[Aj[j]];\n        }\n\n        xout[ridx] = x[ridx] + (b[ridx] - Axi) / ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize>\n__global__ void jacobi_smooth_4x4_kernel(const IndexType num_rows,\n        const IndexType *row_offsets,\n        const IndexType *column_indices,\n        const ValueTypeA *nonzero_values,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        const ValueTypeB *x,\n        ValueTypeB *xout,\n        const IndexType row_offset)\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_xtemp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = blockrow_id;\n        // Load RHS and x\n        // groups of 4 threads load 32-byte, through texture, no wasted bandwidth\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n        // Contribution from each nonzero column\n        int jmax = row_offsets[i + 1];\n\n        for (int jind = row_offsets[i]; jind < jmax; jind++)\n        {\n            IndexType jcol = column_indices[jind];\n            offset = jcol * bsize + vec_entry_index;\n            s_xtemp[tid] = x[offset] * (jcol != i);\n            //for some reason we do better in perf if we write to volatile shared memory and then read this back...\n            //s_xtemp[threadIdx.x] += delta[offset]*(row_colors[jcol] < current_color && current_color!= 0);\n            // Load nonzero_values\n            offset = jind * bsize * bsize + vec_entry_index * bsize;\n            loadAsVector<bsize>(nonzero_values + offset, temp);\n            // Do matrix multiply\n            s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                bmAx -= temp[m] * s_xtemp[s_offset++];\n            }\n        }\n\n        // Each thread stores bmAx in xtemp\n        s_xtemp[tid] = bmAx;\n        bmAx = 0.;\n        // Load Dinv\n        offset = i * bsize * bsize + vec_entry_index * bsize;\n        loadAsVector<bsize>(d + offset, temp);\n        // Do matrix-vector multiply\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_xtemp[s_offset++];\n        }\n\n        xout[i * bsize + vec_entry_index] = bmAx;\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n\n\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__ void jacobi_smooth_with_0_initial_guess_kernel(const IndexType num_rows,\n        const ValueTypeA *d,\n        const ValueTypeB *b,\n        ValueTypeB *x,\n        ValueTypeB weight,\n        const IndexType row_offset)\n\n{\n    IndexType tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int ridx = row_offset + tidx; ridx < num_rows; ridx += blockDim.x * gridDim.x)\n    {\n        x[ridx] = weight * b[ridx] /  ( isNotCloseToZero( d[ridx]) ? d[ridx] : epsilon(d[ridx]) );\n    }\n}\n\n// -----------------\n// Methods\n// -----------------\n\n// Constructor\ntemplate<class T_Config>\nJacobiL1Solver_Base<T_Config>::JacobiL1Solver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope), m_d(0)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");;\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nJacobiL1Solver_Base<T_Config>::~JacobiL1Solver_Base()\n{\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(Base::m_A);\n\n    if (!m_explicit_A)\n    {\n        FatalError(\"JacobiL1Solver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    compute_d( *this->m_explicit_A );\n    int N = this->m_explicit_A->get_num_cols() * this->m_explicit_A->get_block_dimy();\n    this->y_tmp.resize(N);\n}\n\ntemplate<class T_Config>\nvoid JacobiL1Solver_Base<T_Config>::compute_d( Matrix<T_Config> &A)\n{\n    this->m_d.resize(A.get_num_rows()*A.get_block_size());\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        compute_d_1x1(A);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        compute_d_4x4(A);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for JacobiL1Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_1x1(const Matrix_h &A)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d_ = 0;\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            ValueTypeB Aij = A.values[j];\n            //if not the diagonal then compute the absolute value\n            //if(A.col_indices[j]!=i) JE have to include diagonal or risk cancellation if diagonal is negative\n            Aij = fabs(Aij);\n            d_ += Aij;\n        }\n\n        this->m_d[i] = d_;\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_4x4(const Matrix_h &A)\n{\n    FatalError(\"4x4 block matrices not supported on host\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_1x1(const Matrix_d &A)\n{\n//DIAG: starnge issues trying to add DIAG property handling\n// now leaving !DIAG only\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n\n    if (A.get_num_rows() > 0)\n    {\n        compute_d_kernel<IndexType, ValueTypeA, ValueTypeB> <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>\n        ((int)A.get_num_rows(),\n         A.row_offsets.raw(),\n         A.col_indices.raw(),\n         A.values.raw(),\n         this->m_d.raw());\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_d_4x4(const Matrix_d &A)\n{\n    // now leaving !DIAG only\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    const int cta_size = 128;\n    const int nWarps = cta_size / 32;\n    int grid_size = std::min( 1024, ( A.get_num_rows() + nWarps - 1 ) / nWarps );\n    compute_d_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, cta_size > <<< grid_size, cta_size >>>\n    (A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.diag.raw(),\n     A.values.raw(),\n     this->m_d.raw());\n    cudaCheckError();\n}\n\n\n\n\n\n//\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nJacobiL1Solver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if (xIsZero) { x.dirtybit = 0; }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    this->m_explicit_A->setViewExterior();\n\n    if (this->m_explicit_A->get_block_dimx() == 1 && this->m_explicit_A->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*this->m_explicit_A, b, x, this->m_explicit_A->getViewExterior());\n        }\n        else\n        {\n            smooth_1x1(*this->m_explicit_A, b, x, this->m_explicit_A->getViewExterior(), false);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for JacobiL1_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nJacobiL1Solver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    VVector newx((int)x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        ValueTypeA d = this->m_d[i];\n        newx[i] = x[i] + (b[i] - Axi) /  ( isNotCloseToZero( d) ? d : epsilon(d) );\n    }\n\n    x.swap(newx);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"4x4 block size not supported on host\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeA d = this->m_d[i];\n        x[i] = b[i] / ( isNotCloseToZero( d) ? d : epsilon(d) );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags, bool latency_hiding)\n{\n    this->y_tmp.set_block_dimx(b.get_block_dimx());\n    this->y_tmp.set_block_dimy(b.get_block_dimy());\n\n    multiply(A, x, this->y_tmp, A.getViewExterior());\n\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &num_rows);\n\n    int nthreads_per_block = 128;\n    int n = num_rows - offset;\n    int nblocks = n / nthreads_per_block + 1;\n    jacobi_l1_postsmooth<<<nblocks, nthreads_per_block>>>(n, this->weight, x.raw() + offset, this->m_d.raw() + offset, b.raw() + offset, this->y_tmp.raw() + offset);\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    VVector xout(x.size());\n    ValueTypeB *x_ptr = x.raw();\n    ValueTypeB *xout_ptr = xout.raw();\n    IndexType num_rows = A.get_num_rows();\n    IndexType offset = 0;\n\n    //It is only safe to swap these vectors if there is no halo exchange in process\n    if (separation_flags != this->m_explicit_A->getViewExterior())\n    {\n        amgx::thrust::copy(x.begin(), x.end(), xout.begin()); //TODO: only interior+bndry part\n        cudaCheckError();\n        x_ptr = xout.raw();\n        xout_ptr = x.raw();\n\n        if (separation_flags & INTERIOR)\n        {\n        }\n        else\n        {\n            A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n        }\n    }\n\n    const int threads_per_block = 128;\n    const int blockrows_per_warp = 32 / 4;\n    const int blockrows_per_cta = (threads_per_block / 32) * blockrows_per_warp;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    jacobi_smooth_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, blockrows_per_warp, 4> <<< num_blocks, threads_per_block >>>\n    ((int)A.get_num_rows(),\n     A.row_offsets.raw(),\n     A.col_indices.raw(),\n     A.values.raw(),\n     this->m_d.raw(),\n     b.raw(),\n     x_ptr,\n     xout_ptr, offset);\n    cudaCheckError();\n\n    if ((separation_flags != this->m_explicit_A->getViewExterior())/* && (separation_flags & INTERIOR)*/) {}\n    else { x.swap(xout); }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid JacobiL1Solver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    ViewType oldView = A.currentView();\n    // Process all rows\n    A.setViewExterior();\n\n    int offset, num_rows;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &num_rows);\n\n    int nthreads_per_block = 128;\n    int n = num_rows - offset;\n    int nblocks = n / nthreads_per_block + 1;\n    jacobi_l1_postsmooth_zero<<<nblocks, nthreads_per_block>>>(n, this->weight, x.raw() + offset, this->m_d.raw() + offset, b.raw() + offset);\n    cudaCheckError();\n\n    A.setView(oldView);\n    cudaCheckError();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class JacobiL1Solver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class JacobiL1Solver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/jacobi_l1_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/chebyshev_poly.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#define _USE_MATH_DEFINES\n#include \"math.h\"\n#include <amgx_cublas.h>\n#include <amgx_cusparse.h>\n\n#include <thrust/extrema.h>\n\n#undef DEBUG_CHEBYSHEV_OUTPUT\n\nnamespace amgx\n{\nnamespace chebyshev_poly_smoother\n{\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_presmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? omega * b / d : omega * b / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_postsmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d  = ValueTypeA( 1 ) / d;\n        b -= y;\n        b *= omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate <typename ValueTypeB>\n__host__ __device__ ValueTypeB magicDampBeta(int m)\n{\n    return M_PI / (4 * (ValueTypeB)m + 2);\n}\n\ntemplate <typename ValueTypeB>\n__host__ __device__ ValueTypeB magicDamp(int n, ValueTypeB beta)\n{\n    return cos(beta) * cos(beta) / (cos(beta * (2 * n + 1)) * cos(beta * (2 * n + 1)) - sin(beta) * sin(beta));\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int threads_per_block, int warps_per_block, bool diag>\n__global__\nvoid getLambdaEstimate(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, const int num_rows, ValueTypeB *out)\n\n{\n    int row_id = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB max_sum = (ValueTypeB)0.0;\n\n    while (row_id < num_rows)\n    {\n        ValueTypeB cur_sum = (ValueTypeB)0.0;\n\n        for (int j = row_offsets[row_id]; j < row_offsets[row_id + 1]; j++)\n        {\n            cur_sum += abs(values[j]);\n        }\n\n        if (diag)\n        {\n            cur_sum += abs(values[dia_indices[row_id]]);\n        }\n\n        max_sum = max(max_sum, cur_sum);\n        row_id += gridDim.x * blockDim.x;\n    }\n\n    out[blockDim.x * blockIdx.x + threadIdx.x] = max_sum;\n}\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nChebyshevPolySolver_Base<T_Config>::ChebyshevPolySolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    poly_order = cfg.AMG_Config::template getParameter<int>(\"chebyshev_polynomial_order\", cfg_scope);\n    poly_order = min(10, max(poly_order, 1));\n    tau.resize(poly_order);\n}\n\n// Destructor\ntemplate<class T_Config>\nChebyshevPolySolver_Base<T_Config>::~ChebyshevPolySolver_Base()\n{\n    this->tau.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"chebyshev_polynomial_order= \" << this->poly_order << std::endl;\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_eigenmax_estimate(const Matrix_d &A, ValueTypeB &lambda)\n{\n#define LAMBDA_BLOCK_SIZE 256\n    VVector tsum(A.get_num_rows());\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    if (A.hasProps(DIAG))\n    {\n        cudaFuncSetCacheConfig(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true >, cudaFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        cudaFuncSetCacheConfig(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false >, cudaFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n\n    lambda = *(amgx::thrust::max_element(tsum.begin(), tsum.end()));\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n    printf(\"Lambda for A on row %lu is: %f\\n\", amgx::thrust::max_element(tsum.begin(), tsum.end()) - tsum.begin(), lambda);\n#endif\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_eigenmax_estimate(const Matrix_h &A, ValueTypeB &lambda)\n{\n    FatalError(\"compute_eigenmax_estimate is not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"ChebyshevPolySolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    ValueTypeB lambda = 0.0;\n    compute_eigenmax_estimate( *A_as_matrix, lambda );\n    ValueTypeB beta = magicDampBeta<ValueTypeB>(poly_order);\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n    int lvl = 0;\n    A_as_matrix->getParameter(\"level\", lvl);\n    printf(\"Tau values for level %d : \", lvl);\n#endif\n\n    for (int i = 0; i < poly_order; i++)\n    {\n        tau[i] = magicDamp(i, beta) / lambda;\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n        printf(\"%f%s\", tau[i], (i == poly_order - 1) ? \"\\n\" : \" \");\n#endif\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nChebyshevPolySolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!A_as_matrix->is_matrix_singleGPU())\n    {\n        A_as_matrix->manager->exchange_halo_async(x, x.tag);\n\n        if (A_as_matrix->getViewExterior() == A_as_matrix->getViewInterior())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n        }\n    }\n\n    ViewType oldView = A_as_matrix->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (A_as_matrix->is_matrix_singleGPU() || (x.dirtybit == 0))\n    {\n        latencyHiding = false;\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(A_as_matrix->getViewInterior() | A_as_matrix->getViewExterior());\n    }\n    else\n    {\n        flags = A_as_matrix->getViewInterior();\n        A_as_matrix->setViewInterior();\n    }\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        smooth_1x1(*A_as_matrix, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(~(A_as_matrix->getViewInterior()) & A_as_matrix->getViewExterior());\n\n        if (flags != 0)\n        {\n            if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n            {\n                smooth_1x1(*A_as_matrix, b, x, flags);\n            }\n            else\n            {\n                FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <typename ValueTypeB>\nstruct chebyshev_poly_functor\n{\n    ValueTypeB tau;\n    chebyshev_poly_functor( ValueTypeB tau ) : tau( tau ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeB b = amgx::thrust::get<1>(t);\n        ValueTypeB y = amgx::thrust::get<2>(t);\n        return x + tau * (b - y);\n    }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    bool latencyHiding = separation_flags != A.getViewIntExt();// we change view only when do latency hiding, maybe it's better to use some explicit flag attached to matrix?\n\n    for (int i = 0; i < this->poly_order; i++)\n    {\n        this->y.dirtybit = 0;\n        //y = A*x\n        multiply( A, x, this->y, separation_flags );\n        //x += tau_i * (b - y)\n        amgx::thrust::transform( amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple( x.begin() + offset, b.begin() + offset, this->y.begin() + offset)),\n                           amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple( x.begin() + num_rows, b.begin() + num_rows, this->y.begin() + num_rows)),\n                           x.begin() + offset,\n                           chebyshev_poly_functor<ValueTypeB>( this->tau[i] ));\n        //Cublas::axpy(num_rows, (ValueTypeB)(this->tau[i]),      b.raw(),       1, x.raw(), 1);\n        //Cublas::axpy(num_rows, (ValueTypeB)(-1.0*this->tau[i]), this->y.raw(), 1, x.raw(), 1);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class ChebyshevPolySolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class ChebyshevPolySolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace chebyshev_poly_smoother\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/chebyshev_poly.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#define _USE_MATH_DEFINES\n#include \"math.h\"\n#include <amgx_cublas.h>\n#include <amgx_cusparse.h>\n\n#include <thrust/extrema.h>\n\n#undef DEBUG_CHEBYSHEV_OUTPUT\n\nnamespace amgx\n{\nnamespace chebyshev_poly_smoother\n{\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_presmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? omega * b / d : omega * b / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_postsmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d  = ValueTypeA( 1 ) / d;\n        b -= y;\n        b *= omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate <typename ValueTypeB>\n__host__ __device__ ValueTypeB magicDampBeta(int m)\n{\n    return M_PI / (4 * (ValueTypeB)m + 2);\n}\n\ntemplate <typename ValueTypeB>\n__host__ __device__ ValueTypeB magicDamp(int n, ValueTypeB beta)\n{\n    return cos(beta) * cos(beta) / (cos(beta * (2 * n + 1)) * cos(beta * (2 * n + 1)) - sin(beta) * sin(beta));\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int threads_per_block, int warps_per_block, bool diag>\n__global__\nvoid getLambdaEstimate(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, const int num_rows, ValueTypeB *out)\n\n{\n    int row_id = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB max_sum = (ValueTypeB)0.0;\n\n    while (row_id < num_rows)\n    {\n        ValueTypeB cur_sum = (ValueTypeB)0.0;\n\n        for (int j = row_offsets[row_id]; j < row_offsets[row_id + 1]; j++)\n        {\n            cur_sum += abs(values[j]);\n        }\n\n        if (diag)\n        {\n            cur_sum += abs(values[dia_indices[row_id]]);\n        }\n\n        max_sum = max(max_sum, cur_sum);\n        row_id += gridDim.x * blockDim.x;\n    }\n\n    out[blockDim.x * blockIdx.x + threadIdx.x] = max_sum;\n}\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nChebyshevPolySolver_Base<T_Config>::ChebyshevPolySolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    poly_order = cfg.AMG_Config::template getParameter<int>(\"chebyshev_polynomial_order\", cfg_scope);\n    poly_order = min(10, max(poly_order, 1));\n    tau.resize(poly_order);\n}\n\n// Destructor\ntemplate<class T_Config>\nChebyshevPolySolver_Base<T_Config>::~ChebyshevPolySolver_Base()\n{\n    this->tau.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"chebyshev_polynomial_order= \" << this->poly_order << std::endl;\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::compute_eigenmax_estimate(const Matrix_d &A, ValueTypeB &lambda)\n{\n#define LAMBDA_BLOCK_SIZE 256\n    VVector tsum(A.get_num_rows());\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    if (A.hasProps(DIAG))\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true >), hipFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false >), hipFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n\n    lambda = *(amgx::thrust::max_element(tsum.begin(), tsum.end()));\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n    printf(\"Lambda for A on row %lu is: %f\\n\", amgx::thrust::max_element(tsum.begin(), tsum.end()) - tsum.begin(), lambda);\n#endif\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::compute_eigenmax_estimate(const Matrix_h &A, ValueTypeB &lambda)\n{\n    FatalError(\"compute_eigenmax_estimate is not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"ChebyshevPolySolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    ValueTypeB lambda = 0.0;\n    compute_eigenmax_estimate( *A_as_matrix, lambda );\n    ValueTypeB beta = magicDampBeta<ValueTypeB>(poly_order);\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n    int lvl = 0;\n    A_as_matrix->getParameter(\"level\", lvl);\n    printf(\"Tau values for level %d : \", lvl);\n#endif\n\n    for (int i = 0; i < poly_order; i++)\n    {\n        tau[i] = magicDamp(i, beta) / lambda;\n#ifdef DEBUG_CHEBYSHEV_OUTPUT\n        printf(\"%f%s\", tau[i], (i == poly_order - 1) ? \"\\n\" : \" \");\n#endif\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nChebyshevPolySolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (!A_as_matrix->is_matrix_singleGPU())\n    {\n        A_as_matrix->manager->exchange_halo_async(x, x.tag);\n\n        if (A_as_matrix->getViewExterior() == A_as_matrix->getViewInterior())\n        {\n            A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n        }\n    }\n\n    ViewType oldView = A_as_matrix->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (A_as_matrix->is_matrix_singleGPU() || (x.dirtybit == 0))\n    {\n        latencyHiding = false;\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(A_as_matrix->getViewInterior() | A_as_matrix->getViewExterior());\n    }\n    else\n    {\n        flags = A_as_matrix->getViewInterior();\n        A_as_matrix->setViewInterior();\n    }\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        smooth_1x1(*A_as_matrix, b, x, flags);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (latencyHiding)\n    {\n        A_as_matrix->manager->exchange_halo_wait(x, x.tag);\n        A_as_matrix->setViewExterior();\n        flags = (ViewType)(~(A_as_matrix->getViewInterior()) & A_as_matrix->getViewExterior());\n\n        if (flags != 0)\n        {\n            if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n            {\n                smooth_1x1(*A_as_matrix, b, x, flags);\n            }\n            else\n            {\n                FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n            }\n        }\n    }\n\n    x.dirtybit = 1;\n    A_as_matrix->setView(oldView);\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nChebyshevPolySolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <typename ValueTypeB>\nstruct chebyshev_poly_functor\n{\n    ValueTypeB tau;\n    chebyshev_poly_functor( ValueTypeB tau ) : tau( tau ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeB b = amgx::thrust::get<1>(t);\n        ValueTypeB y = amgx::thrust::get<2>(t);\n        return x + tau * (b - y);\n    }\n};\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, ViewType separation_flags)\n{\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    bool latencyHiding = separation_flags != A.getViewIntExt();// we change view only when do latency hiding, maybe it's better to use some explicit flag attached to matrix?\n\n    for (int i = 0; i < this->poly_order; i++)\n    {\n        this->y.dirtybit = 0;\n        //y = A*x\n        multiply( A, x, this->y, separation_flags );\n        //x += tau_i * (b - y)\n        amgx::thrust::transform( amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple( x.begin() + offset, b.begin() + offset, this->y.begin() + offset)),\n                           amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple( x.begin() + num_rows, b.begin() + num_rows, this->y.begin() + num_rows)),\n                           x.begin() + offset,\n                           chebyshev_poly_functor<ValueTypeB>( this->tau[i] ));\n        //Cublas::axpy(num_rows, (ValueTypeB)(this->tau[i]),      b.raw(),       1, x.raw(), 1);\n        //Cublas::axpy(num_rows, (ValueTypeB)(-1.0*this->tau[i]), this->y.raw(), 1, x.raw(), 1);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_h &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n    FatalError(\"chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(const Matrix_d &A, const VVector &b, VVector &x, ViewType separation_flags)\n{\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_h &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ChebyshevPolySolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_BxB(Matrix_d &A, VVector &b, VVector &x, bool firstStep, ViewType separation_flags)\n{\n    FatalError(\"M*M chebyshev poly smoother not implemented with host format, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class ChebyshevPolySolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class ChebyshevPolySolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace chebyshev_poly_smoother\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/chebyshev_poly.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_ilu_solver.h>\n#include <solvers/block_common_solver.h>\n#include <csr_multiply.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <matrix_io.h>\n#include <permute.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n#include <algorithm>\n\n// TODO: Have 2 groups of 16 threads collaborate\n// TODO: Add support for outside diagonal\n// TODO: Add support for unsorted rows\n\n#define EXPERIMENTAL_LU_FACTORS\n#define EXPERIMENTAL_LU_FORWARD\n#define EXPERIMENTAL_LU_BACKWARD\n\nnamespace amgx\n{\n\nnamespace multicolor_ilu_solver\n{\n\n// -----------\n// Kernels\n// -----------\n\n#ifdef EXPERIMENTAL_LU_FORWARD\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize, int bsize, bool ROW_MAJOR, bool hasDiag>\n__global__\n__launch_bounds__( CtaSize, 16 )\nvoid LU_forward_4x4_kernel_warp( const IndexType *LU_row_offsets,\n                                 const IndexType *LU_smaller_color_offsets,\n                                 const IndexType *LU_column_indices,\n                                 const ValueTypeA *LU_nonzero_values,\n                                 const IndexType *A_row_offsets,\n                                 const IndexType *A_column_indices,\n                                 const ValueTypeA *A_nonzero_values,\n                                 const IndexType *A_dia_indices,\n                                 const ValueTypeB *x,\n                                 const ValueTypeB *b,\n                                 ValueTypeB *delta,\n                                 const int *sorted_rows_by_color,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 bool xIsZero )\n{\n    const int nHalfWarps = CtaSize / 16; // Number of half warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CtaSize];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        // Load one block of B.\n        ValueTypeB my_bmAx(0);\n\n        unsigned int active_mask = utils::activemask();\n\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * aRowId + halfLaneId_div_4]);\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * aRowId + halfLaneId_mod_4]);\n            }\n        }\n\n        // Don't do anything if X is zero.\n        if ( !xIsZero )\n        {\n            int aColBegin = A_row_offsets[aRowId  ];\n            int aColEnd   = A_row_offsets[aRowId + 1];\n            int aColMax = aColEnd;\n\n            if ( hasDiag )\n            {\n                ++aColMax;\n            }\n\n            // Each warp load column indices of 32 nonzero blocks\n            for ( ; utils::any( aColBegin < aColMax, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                // Get the ID of the column.\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = A_column_indices[aColIt];\n                }\n\n                if ( hasDiag && aColIt == aColEnd )\n                {\n                    aColId = aRowId;\n                }\n\n                // Count the number of active columns.\n                int vote =  utils::ballot(aColId != -1, active_mask);\n                // The number of iterations.\n                int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n                // Loop over columns. We compute 8 columns per iteration.\n                for ( int k = 0 ; k < nCols ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Load 8 blocks of X.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    ValueTypeB my_x(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_x = __cachingLoad(&x[4 * waColId + halfLaneId_mod_4]);\n                    }\n\n                    my_s_mem[halfLaneId] = my_x;\n                    // Load 8 blocks of A.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        if ( hasDiag && w_aColTmp == aColEnd )\n                        {\n                            w_aColIt = A_dia_indices[aRowId];\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = A_nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n        } // if xIsZero\n\n        // Contribution from each nonzero column that has color less than yours\n        if ( current_color != 0 )\n        {\n            // TODO: Use constant or texture here\n            int aColBegin = LU_row_offsets[aRowId];\n            int aColEnd   = LU_smaller_color_offsets[aRowId];\n\n            // Each warp load column indices of 32 nonzero blocks\n            for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = LU_column_indices[aColIt];\n                }\n\n                // Count the number of active columns.\n                int vote =  utils::ballot(aColId != -1, active_mask);\n                // The number of iterations.\n                int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n                for ( int k = 0 ; k < nCols ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Load 8 blocks of X.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    ValueTypeB my_delta(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_delta = delta[4 * waColId + halfLaneId_mod_4];\n                    }\n\n                    my_s_mem[halfLaneId] = my_delta;\n                    utils::syncwarp(); // making sure smem write propagated\n                    // Update b-Ax.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = LU_nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n        } // If current_color != 0\n\n        // Reduce bmAx terms.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n            my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n        }\n        else\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n            my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                delta[4 * aRowId + halfLaneId_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                delta[4 * aRowId + halfLaneId_mod_4] = my_bmAx;\n            }\n        }\n    }\n}\n\n#else\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, bool ROW_MAJOR>\n__global__\nvoid LU_forward_4x4_kernel(const IndexType *LU_row_offsets, const IndexType *LU_smaller_color_offsets, const IndexType *LU_column_indices, const ValueTypeA *LU_nonzero_values,  const IndexType *A_row_offsets, const IndexType *A_column_indices, const ValueTypeA *A_nonzero_values,\n                           const ValueTypeB *x, const ValueTypeB *b,  ValueTypeB *delta, const int *sorted_rows_by_color,\n                           const int num_rows_per_color, const int current_color, bool xIsZero)\n\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_delta_temp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows_per_color &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        // Load RHS and x\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n\n        if (!xIsZero)\n        {\n            int jmin = A_row_offsets[i];\n            int jmax = A_row_offsets[i + 1];\n\n            //TODO: Assumes inside diagonal\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = A_column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n                s_delta_temp[tid] = x[offset];\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(A_nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = A_nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_delta_temp[s_offset++];\n                }\n            }\n        }\n\n        // Contribution from each nonzero column that has color less than yours\n        if (current_color != 0)\n        {\n            int jmin = LU_row_offsets[i];\n            int jmax = LU_smaller_color_offsets[i];\n\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = LU_column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n                s_delta_temp[tid] = __ldcg(delta + offset);\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(LU_nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = LU_nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_delta_temp[s_offset++];\n                }\n            }\n        }\n\n        delta[i * bsize + vec_entry_index] = bmAx;\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n#endif\n\n#ifdef EXPERIMENTAL_LU_BACKWARD\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CtaSize, 16 )\nvoid LU_backward_4x4_kernel_warp( const IndexType *row_offsets,\n                                  const IndexType *larger_color_offsets,\n                                  const IndexType *column_indices,\n                                  const IndexType *dia_indices,\n                                  const ValueTypeA *nonzero_values,\n                                  const ValueTypeB *delta,\n                                  ValueTypeB *Delta,\n                                  ValueTypeB *x,\n                                  const int *sorted_rows_by_color,\n                                  const int num_rows_per_color,\n                                  const int current_color,\n                                  const int num_colors,\n                                  const ValueTypeB weight,\n                                  bool xIsZero )\n{\n    const int nHalfWarps = CtaSize / 16; // Number of half warps per CTA.\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CtaSize];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        unsigned int active_mask = utils::activemask();\n        // Load one block of B.\n        ValueTypeB my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_bmAx = delta[4 * aRowId + halfLaneId_div_4];\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_bmAx = delta[4 * aRowId + halfLaneId_mod_4];\n            }\n        }\n\n        // Don't do anything if the color is not the interesting one.\n        if ( current_color != num_colors - 1 )\n        {\n            // The range of the rows.\n            int aColBegin = larger_color_offsets[aRowId], aColEnd = row_offsets[aRowId + 1];\n\n            // Each warp load column indices of 16 nonzero blocks\n            for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                // Get the ID of the column.\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = column_indices[aColIt];\n                }\n\n                // Loop over columns. We compute 8 columns per iteration.\n                for ( int k = 0 ; k < 16 ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Exchange column indices.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    // Load 8 blocks of X if needed.\n                    ValueTypeB *my_ptr = Delta;\n\n                    if ( xIsZero )\n                    {\n                        my_ptr = x;\n                    }\n\n                    ValueTypeB my_x(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_x = my_ptr[4 * waColId + halfLaneId_mod_4];\n                    }\n\n                    my_s_mem[halfLaneId] = my_x;\n                    utils::syncwarp();\n                    // Load 8 blocks of A.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n\n            // Reduce bmAx terms.\n            if ( ROW_MAJOR )\n            {\n                my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n                my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n            }\n            else\n            {\n                my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n                my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n            }\n        } // if current_color != num_colors-1\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_s_mem[halfLaneId_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_s_mem[halfLaneId_mod_4] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        int w_aColIt = dia_indices[aRowId];\n        ValueTypeA my_val(0);\n        utils::syncwarp();\n\n        if ( w_aColIt != -1 )\n        {\n            my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n        }\n\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_val * my_s_mem[halfLaneId_mod_4];\n        }\n        else\n        {\n            my_bmAx = my_val * my_s_mem[halfLaneId_div_4];\n        }\n\n        // Regroup results.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 1 );\n            my_bmAx += utils::shfl_xor( my_bmAx, 2 );\n        }\n        else\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 4 );\n            my_bmAx += utils::shfl_xor( my_bmAx, 8 );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            ValueTypeB my_x(0);\n\n            if ( !xIsZero && halfLaneId_mod_4 == 0 )\n            {\n                my_x = x[4 * aRowId + halfLaneId_div_4];\n            }\n\n            my_x += weight * my_bmAx;\n\n            if ( !xIsZero && halfLaneId_mod_4 == 0 )\n            {\n                Delta[4 * aRowId + halfLaneId_div_4] = my_bmAx;\n            }\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                x[4 * aRowId + halfLaneId_div_4] = my_x;\n            }\n        }\n        else\n        {\n            ValueTypeB my_x(0);\n\n            if ( !xIsZero && halfLaneId_div_4 == 0 )\n            {\n                my_x = x[4 * aRowId + halfLaneId_mod_4];\n            }\n\n            my_x += weight * my_bmAx;\n\n            if ( !xIsZero && halfLaneId_div_4 == 0 )\n            {\n                Delta[4 * aRowId + halfLaneId_mod_4] = my_bmAx;\n            }\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                x[4 * aRowId + halfLaneId_mod_4] = my_x;\n            }\n        }\n    }\n}\n\n#else\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, bool ROW_MAJOR>\n__global__\nvoid LU_backward_4x4_kernel(const IndexType *row_offsets, const IndexType *larger_color_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values,\n                            const ValueTypeB *delta,  ValueTypeB *Delta, ValueTypeB *x, const int *sorted_rows_by_color,\n                            const int num_rows_per_color, const int current_color, const int num_colors, const ValueTypeB weight, bool xIsZero)\n\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_x_temp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows_per_color &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        // Load RHS and x\n        offset = i * bsize + vec_entry_index;\n        bmAx = delta[offset];\n\n        // Contribution from each nonzero column that has color less than yours\n        if (current_color != num_colors)\n        {\n            int jmin = larger_color_offsets[i];\n            int jmax = row_offsets[i + 1];\n\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n\n                if (xIsZero)\n                {\n                    s_x_temp[tid] = __ldcg(x + offset);\n                }\n                else\n                {\n                    s_x_temp[tid] = __ldcg(Delta + offset);\n                }\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_x_temp[s_offset++];\n                }\n            }\n        }\n\n        s_x_temp[tid] = bmAx;\n        bmAx = 0.;\n\n        // Load diagonals (which store the inverse)\n        if (ROW_MAJOR)\n        {\n            offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n            loadAsVector<bsize>(nonzero_values + offset, temp);\n        }\n        else\n        {\n            offset = dia_indices[i] * bsize * bsize + vec_entry_index;\n#pragma unroll\n            for (int m = 0; m < bsize; m++)\n            {\n                temp[m] = nonzero_values[offset + bsize * m];\n            }\n        }\n\n        // Do matrix-vector multiply\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_x_temp[s_offset++];\n        }\n\n        offset = i * bsize + vec_entry_index;\n\n        if (xIsZero)\n        {\n            x[offset] = weight * bmAx;\n        }\n        else\n        {\n            Delta[offset] = bmAx;\n            x[offset] += weight * bmAx ;\n        }\n\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n\n#endif\n\n// Assumptions:\n// CtaSize must be multiple of 32\n// SMemSize should be larger than the maximum number of columns in the matrix\n// Matrix B is superset of matrix A\n\ntemplate< int CtaSize, int SMemSize>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeAtoLUmapping_kernel( int A_nRows,\n                            const int *__restrict A_row_offsets,\n                            const int *__restrict A_col_indices,\n                            const int *__restrict B_row_offsets,\n                            const int *__restrict B_col_indices,\n                            int *__restrict AtoBmapping,\n                            int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Rows are stored in SMEM. Linear storage.\n    __shared__ volatile int s_colInd[nWarps][SMemSize];\n    // The row this warp is responsible for\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    // Loop over rows of A.\n    for ( ; aRowId < A_nRows ; aRowId += nWarps * gridDim.x )\n    {\n        // Insert all the column indices of matrix B in the shared memory table\n        int bColBeg = B_row_offsets[aRowId];\n        int bColEnd = B_row_offsets[aRowId + 1];\n        // The number of columns.\n        const int nCols = bColEnd - bColBeg;\n\n        //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n        if ( nCols > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Fill-in the local table.\n        const int NUM_STEPS = SMemSize / 32;\n#pragma unroll\n        for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n        {\n            int bColIt = bColBeg + k;\n            int bColId = -1;\n\n            if ( bColIt < bColEnd )\n            {\n                bColId = B_col_indices[bColIt];\n            }\n\n            s_colInd[warpId][k] = bColId;\n        }\n\n        // Now load column indices of current row of A\n        int aColIt  = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n\n        for ( aColIt += laneId ; utils::any(aColIt < aColEnd) ; aColIt += 32 )\n        {\n            // The column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = A_col_indices[aColIt];\n            }\n\n            // Each thread searches for its column id, and gets the corresponding bColIt\n            // TODO: Try binary search or using hash table\n            int foundOffset = -1;\n\n            if ( aColId == -1 )\n            {\n                foundOffset = -2;\n            }\n\n            for ( int i = 0 ; i < nCols && utils::any(foundOffset == -1) ; ++i )\n                if ( foundOffset == -1 && s_colInd[warpId][i] == aColId )\n                {\n                    foundOffset = i;\n                }\n\n            // Store the result.\n            if ( aColIt < aColEnd )\n            {\n                AtoBmapping[aColIt] = bColBeg + foundOffset;\n            }\n        }\n    } // if RowId < A_nRows;\n}\n\ntemplate< int CtaSize, int SMemSize>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeAtoLUmappingExtDiag_kernel( int A_nRows,\n                                   const int *__restrict A_row_offsets,\n                                   const int *__restrict A_col_indices,\n                                   const int *__restrict A_dia_indices,\n                                   const int *__restrict B_row_offsets,\n                                   const int *__restrict B_col_indices,\n                                   int *__restrict AtoBmapping,\n                                   int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Rows are stored in SMEM. Linear storage.\n    __shared__ volatile int s_colInd[nWarps][SMemSize];\n    // The row this warp is responsible for\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    // Loop over rows of A.\n    for ( ; aRowId < A_nRows ; aRowId += nWarps * gridDim.x )\n    {\n        // Insert all the column indices of matrix B in the shared memory table\n        int bColBeg = B_row_offsets[aRowId];\n        int bColEnd = B_row_offsets[aRowId + 1];\n        // The number of columns.\n        const int nCols = bColEnd - bColBeg;\n\n        //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n        if ( nCols > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Fill-in the local table.\n        const int NUM_STEPS = SMemSize / 32;\n#pragma unroll\n        for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n        {\n            int bColIt = bColBeg + k;\n            int bColId = -1;\n\n            if ( bColIt < bColEnd )\n            {\n                bColId = B_col_indices[bColIt];\n            }\n\n            s_colInd[warpId][k] = bColId;\n        }\n\n        // Now load column indices of current row of A\n        int aColIt  = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n\n        for ( aColIt += laneId ; utils::any(aColIt <= aColEnd) ; aColIt += 32 )\n        {\n            // The column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = A_col_indices[aColIt];\n            }\n\n            if ( aColIt == aColEnd )\n            {\n                aColId = aRowId;\n            }\n\n            // Each thread searches for its column id, and gets the corresponding bColIt\n            // TODO: Try binary search or using hash table\n            int foundOffset = -1;\n\n            if ( aColId == -1 )\n            {\n                foundOffset = -2;\n            }\n\n            for ( int i = 0 ; i < nCols && utils::any(foundOffset == -1) ; ++i )\n                if ( foundOffset == -1 && s_colInd[warpId][i] == aColId )\n                {\n                    foundOffset = i;\n                }\n\n            // Store the result.\n            int aDst = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aDst = aColIt;\n            }\n\n            if ( aColIt == aColEnd )\n            {\n                aDst = A_dia_indices[aRowId];\n            }\n\n            if ( aDst != -1 )\n            {\n                AtoBmapping[aDst] = bColBeg + foundOffset;\n            }\n        }\n    }\n}\n\n#ifdef EXPERIMENTAL_LU_FACTORS\n\ntemplate< typename ValueTypeA, int CtaSize, int SMemSize, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CtaSize, 12 )\nvoid\ncompute_LU_factors_4x4_kernel_warp( int A_nRows,\n                                    const int *__restrict A_row_offsets,\n                                    const int *__restrict A_col_indices,\n                                    const int *__restrict A_dia_indices,\n                                    ValueTypeA *__restrict A_nonzero_values,\n                                    const int *__restrict A_smaller_color_offsets,\n                                    const int *__restrict A_larger_color_offsets,\n                                    const int *sorted_rows_by_color,\n                                    const int num_rows_per_color,\n                                    const int current_color,\n                                    int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    int globalWarpId = blockIdx.x * nWarps + warpId;\n    // Shared memory to store the blocks to process\n    __shared__ volatile ValueTypeA s_C_mtx[nWarps][32];\n    __shared__ volatile ValueTypeA s_F_mtx[nWarps][16];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile int s_aColSrc[nWarps][32];\n    // Shared memory to store the column indices of the current row\n    __shared__ volatile int s_keys[nWarps][SMemSize];\n\n    while (globalWarpId < num_rows_per_color)\n    {\n        int storedRowId[2];\n        int I = 0;\n\n        for (; I < 2 && globalWarpId < num_rows_per_color ; I++)\n        {\n            int aRowId = sorted_rows_by_color[globalWarpId];\n            storedRowId[I] = aRowId;\n            int aColBeg = A_row_offsets[aRowId + 0];\n            int aColEnd = A_row_offsets[aRowId + 1];\n            int aColSmaller = A_smaller_color_offsets[aRowId];\n            // The number of columns.\n            const int nCols = aColEnd - aColBeg;\n\n            //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n            if ( nCols > SMemSize )\n            {\n                wk_returnValue[0] = 1;\n                return;\n            }\n\n            // Fill-in the local table.\n            const int NUM_STEPS = SMemSize / 32;\n\n#pragma unroll\n            for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n            {\n                int aColIt = aColBeg + k;\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = A_col_indices[aColIt];\n                }\n\n                s_keys[warpId][k] = aColId;\n            }\n\n            // Now load all column indices of neighbours that have colors smaller than yours\n            for ( int aColIt = aColBeg; aColIt < aColSmaller ; aColIt++)\n            {\n                unsigned int active_mask = utils::activemask();\n                // Read the row to process, should be a broadcast\n                int waRowId = s_keys[warpId][aColIt - aColBeg];\n                // Compute multiplicative factor, load C_jj in first half, C_ij in second half\n                int aColIdx = aColIt;\n\n                if ( lane_id_div_16 == 0 )\n                {\n                    aColIdx = A_dia_indices[waRowId];\n                }\n\n                s_C_mtx[warpId][laneId] = A_nonzero_values[16 * aColIdx + lane_id_mod_16];\n                // Threads 0-15 perform the matrix product\n                ValueTypeA tmp(0);\n\n                if (ROW_MAJOR)\n                {\n\n#pragma unroll\n                    for ( int m = 0 ; m < 4 ; ++m )\n                    {\n                        tmp += s_C_mtx[warpId][16 + 4 * idx_i + m] * s_C_mtx[warpId][4 * m + idx_j];\n                    }\n                }\n                else\n                {\n\n#pragma unroll\n                    for ( int m = 0 ; m < 4 ; ++m )\n                    {\n                        tmp += s_C_mtx[warpId][16 + 4 * m + idx_j] * s_C_mtx[warpId][4 * idx_i + m];\n                    }\n                }\n\n                if ( lane_id_div_16 == 0 )\n                {\n                    s_F_mtx[warpId][laneId] = tmp;\n                    A_nonzero_values[16 * aColIt + laneId] = tmp;\n                }\n\n                int waColIt  = __ldcg(A_larger_color_offsets + waRowId);\n                int waColEnd = __ldcg(A_row_offsets + waRowId + 1);\n\n                // Load the first 32 columns of waRowId\n                for (waColIt += laneId ; utils::any(waColIt < waColEnd, active_mask ); waColIt += 32 )\n                {\n                    // Each thread loads its column id\n                    int waColId = -1;\n\n                    if ( waColIt < waColEnd )\n                    {\n                        waColId = A_col_indices[waColIt];\n                    }\n\n                    // Find the right column.\n                    int found_aColIt = -1;\n\n#pragma unroll 4\n                    for ( int i = 0, num_keys = aColEnd - aColBeg ; i < num_keys ; ++i )\n                        if ( s_keys[warpId][i] == waColId )\n                        {\n                            found_aColIt = i;\n                        }\n\n                    if ( found_aColIt != -1 )\n                    {\n                        found_aColIt += aColBeg;\n                    }\n\n                    // Store all the columns that have been found\n                    const int pred = found_aColIt != -1;\n                    int vote = utils::ballot( pred, active_mask );\n                    const int idst = __popc(vote & utils::lane_mask_lt());\n\n                    if (pred)\n                    {\n                        s_aColSrc[warpId][idst] = laneId;\n                    }\n                    utils::syncwarp(active_mask);\n\n                    const int n_cols = __popc( vote );\n\n                    // Process all columns that have been found\n                    for ( int k = 0 ; k < n_cols ; k += 2 )\n                    {\n                        const int my_k = k + lane_id_div_16;\n                        // Where to get columns from.\n                        int a_col_it = -1, w_col_it = -1;\n                        // Load column to load\n                        a_col_it = utils::shfl(found_aColIt, s_aColSrc[warpId][my_k], warpSize, active_mask);\n                        w_col_it = utils::shfl(waColIt,      s_aColSrc[warpId][my_k], warpSize, active_mask);\n\n                        if ( my_k >= n_cols )\n                        {\n                            a_col_it = -1;\n                            w_col_it = -1;\n                        }\n\n                        ValueTypeA my_C(0);\n\n                        if ( w_col_it != -1 )\n                        {\n                            my_C = A_nonzero_values[16 * w_col_it + lane_id_mod_16];\n                        }\n\n                        s_C_mtx[warpId][laneId] = my_C;\n                        // Run the matrix-matrix product.\n                        ValueTypeA tmp(0);\n                        utils::syncwarp( active_mask );\n\n                        if (ROW_MAJOR)\n                        {\n#pragma unroll\n                            for ( int m = 0 ; m < 4 ; ++m )\n                            {\n                                tmp += s_F_mtx[warpId][4 * idx_i + m] * s_C_mtx[warpId][16 * lane_id_div_16 + 4 * m + idx_j];\n                            }\n                        }\n                        else\n                        {\n#pragma unroll\n                            for ( int m = 0 ; m < 4 ; ++m )\n                            {\n                                tmp += s_F_mtx[warpId][4 * m + idx_j] * s_C_mtx[warpId][16 * lane_id_div_16 + 4 * idx_i + m];\n                            }\n                        }\n\n                        if ( a_col_it != -1 )\n                        {\n                            A_nonzero_values[16 * a_col_it + lane_id_mod_16] -= tmp;\n                        }\n                    } // Loop over columns that have a match (for k=0;k<n_cols)\n                } // Loop over the columns of waRowId\n\n                //}  // Loop j=0;j<32\n            } // Loop over the columns of aRowId\n\n            globalWarpId += nWarps * gridDim.x;\n        } // end of loop over I\n\n        // Now compute the inverse of the block C_jj\n        if ( lane_id_div_16 == 0 || I == 2 )\n        {\n            const int offset = 16 * A_dia_indices[storedRowId[lane_id_div_16]] + lane_id_mod_16;\n            s_C_mtx[warpId][laneId] = A_nonzero_values[offset];\n            utils::syncwarp(utils::activemask());\n\n            if (ROW_MAJOR)\n            {\n                compute_block_inverse_row_major4x4_formula2<int, ValueTypeA, 4, true>( s_C_mtx[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, A_nonzero_values );\n            }\n            else\n            {\n                compute_block_inverse_col_major4x4_formula2<int, ValueTypeA, 4, true>( s_C_mtx[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, A_nonzero_values );\n            }\n        } // End of if statement\n    } // End of while loop\n}\n\n#else\n\ntemplate< typename ValueTypeA, int CtaSize, int SMemSize, bool ROW_MAJOR>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeLUFactors_4x4_kernel( int A_nRows,\n                             const int *__restrict A_row_offsets,\n                             const int *__restrict A_col_indices,\n                             const int *__restrict A_dia_indices,\n                             ValueTypeA *__restrict A_nonzero_values,\n                             const int *__restrict A_smaller_color_offsets,\n                             const int *__restrict A_larger_color_offsets,\n                             const int *sorted_rows_by_color,\n                             const int num_rows_per_color,\n                             const int current_color,\n                             int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    int lane_mask_lt = utils::lane_mask_lt();\n\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    int globalWarpId = blockIdx.x * nWarps + warpId;\n    // Shared memory to store the blocks to process\n    __shared__ volatile ValueTypeA s_C_mtx[nWarps][32];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile int s_aColItToLoad[nWarps][32];\n    __shared__ volatile int s_waColItToLoad[nWarps][32];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile unsigned s_aColIds[nWarps][32];\n    // The size of the hash table (one per warp - shared memory).\n    __shared__ volatile int s_size[nWarps][2];\n    // Shared memory to store the column indices of the current row\n    __shared__ volatile int s_keys[nWarps][SMemSize];\n\n    while (globalWarpId < num_rows_per_color)\n    {\n        int aRowId = sorted_rows_by_color[globalWarpId];\n        // Insert all the column indices in shared memory\n        // TODO: Use texture here\n        int aColBeg = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n        int aColIt  = aColBeg;\n\n        // Check if number of nonzeros will fit in shared memory\n        if ( (aColEnd - aColBeg) > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Load the all the column indices of row into shared memory\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n        {\n            int aColId = aColIt < aColEnd ? (int) A_col_indices[aColIt] : -1;\n            s_keys[warpId][aColIt - aColBeg] = aColId;\n        }\n\n        // Now load all column indices of neighbours that have colors smaller than yours\n        aColIt  = aColBeg;\n        int aColSmaller = A_smaller_color_offsets[aRowId];\n\n        for ( ; utils::any( (aColIt + laneId) < aColSmaller ) ; aColIt += 32 )\n        {\n            int aColId = (aColIt + laneId) < aColSmaller ? (int) A_col_indices[aColIt + laneId] : -1;\n            // Each thread pushes its column\n            s_aColIds[warpId][laneId] = aColId;\n\n            // Have warp collaborate to load each row\n            for ( int j = 0; j < 32; j++)\n            {\n                // Check if row to load is valid\n                if ( ( aColIt + j ) >= aColSmaller ) { break; }\n\n                // Read the row to process, should be a broadcast\n                int waRowId = s_aColIds[warpId][j];\n\n                // Compute multiplicative factor, load C_jj in first half, C_ij in second half\n                if (lane_id_div_16 == 0)\n                {\n                    s_C_mtx[warpId][laneId] = A_nonzero_values[ 16 * A_dia_indices[waRowId] + lane_id_mod_16 ];\n                }\n                else\n                {\n                    s_C_mtx[warpId][laneId] = A_nonzero_values[ 16 * (aColIt + j) + lane_id_mod_16 ];\n                }\n\n                // Threads 0-15 perform the matrix product\n                utils::syncwarp();\n                if (lane_id_div_16 == 0)\n                {\n                    ValueTypeA tmp(0);\n\n                    if (ROW_MAJOR)\n                    {\n#pragma unroll\n                        for ( int m = 0 ; m < 4 ; ++m )\n                        {\n                            tmp += s_C_mtx[warpId][16 + 4 * idx_i + m] * s_C_mtx[warpId][4 * m + idx_j];\n                        }\n                    }\n                    else\n                    {\n#pragma unroll\n                        for ( int m = 0 ; m < 4 ; ++m )\n                        {\n                            tmp += s_C_mtx[warpId][16 + 4 * m + idx_j] * s_C_mtx[warpId][4 * idx_i + m];\n                        }\n                    }\n\n                    s_C_mtx[warpId][laneId] = tmp;\n                    A_nonzero_values[16 * (aColIt + j) + laneId] = tmp;\n                }\n\n                int waColIt  = A_larger_color_offsets[waRowId];\n                int waColEnd = A_row_offsets[waRowId + 1];\n\n                //// Load the first 32 columns of waRowId\n                for (waColIt += laneId ; utils::any(waColIt < waColEnd ); waColIt += 32 )\n                {\n                    // Each thread loads its column id\n                    int waColId = waColIt < waColEnd ? A_col_indices[waColIt] : int (-1);\n                    // TODO: Try binary search if columns are ordered\n                    int found_aColIt = -1;\n\n                    //TODO: if invalid waColId, don't search\n                    for (int i = 0 ; utils::any(found_aColIt == -1) && i < aColEnd - aColBeg ; i++)\n                    {\n                        if (s_keys[warpId][i] == waColId) { found_aColIt = aColBeg + i; }\n                    }\n\n                    // Store all the columns that have been found\n                    const int pred = found_aColIt != -1;\n                    const int vote = utils::ballot( pred );\n                    const int idst = __popc(vote & lane_mask_lt);\n\n                    if (pred)\n                    {\n                        s_aColItToLoad [warpId][idst] = found_aColIt;\n                        s_waColItToLoad[warpId][idst] = waColIt;\n                    }\n\n                    const int n_cols = __popc( vote );\n\n                    // Process all columns that have been found\n                    for ( int k = 0 ; k < n_cols ; k++ )\n                    {\n                        // Load column to load\n                        const int a_col_it = k < n_cols ? s_aColItToLoad [warpId][k] : -1;\n                        const int w_col_it = k < n_cols ? s_waColItToLoad[warpId][k] : -1;\n\n                        if (lane_id_div_16 == 1)\n                        {\n                            s_C_mtx[warpId][laneId] = A_nonzero_values[16 * w_col_it + lane_id_mod_16];\n                            // Run the matrix-matrix product.\n                            ValueTypeA tmp(0);\n                            utils::syncwarp(utils::activemask());\n\n                            if (ROW_MAJOR)\n                            {\n\n#pragma unroll\n                                for ( int m = 0 ; m < 4 ; ++m )\n                                {\n                                    tmp += s_C_mtx[warpId][4 * idx_i + m] * s_C_mtx[warpId][16 + 4 * m + idx_j];\n                                }\n                            }\n                            else\n                            {\n\n#pragma unroll\n                                for ( int m = 0 ; m < 4 ; ++m )\n                                {\n                                    tmp += s_C_mtx[warpId][4 * m + idx_j] * s_C_mtx[warpId][16 + 4 * idx_i + m];\n                                }\n                            }\n\n                            A_nonzero_values[16 * a_col_it + lane_id_mod_16] -= tmp;\n                        }\n                    } // Loop over columns that have a match (for k=0;k<n_cols)\n                } // Loop over the columns of waRowId\n            }  // Loop j=0;j<32\n        } // Loop over the columns of aRowId\n\n        // TODO: Have one warp deal with two rows\n        // Now compute the inverse of the block C_jj\n        if (lane_id_div_16 == 0)\n        {\n            const int offset = 16 * A_dia_indices[aRowId] + lane_id_mod_16;\n            s_C_mtx[warpId][laneId] = A_nonzero_values[offset];\n            utils::syncwarp(utils::activemask());\n\n            if (ROW_MAJOR)\n            {\n                compute_block_inverse_row_major<int, ValueTypeA, 0, 4, 16>\n                (s_C_mtx[warpId], 0, offset, idx_i, idx_j, A_nonzero_values);\n            }\n            else\n            {\n                compute_block_inverse_col_major<int, ValueTypeA, 0, 4, 16>\n                (s_C_mtx[warpId], 0, offset, idx_i, idx_j, A_nonzero_values);\n            }\n        }\n\n        globalWarpId += nWarps * gridDim.x;\n    } // if RowId < A_nRows;\n}\n#endif\n// ----------\n// Methods\n// ----------\n\n// Constructor\ntemplate<class T_Config>\nMulticolorILUSolver_Base<T_Config>::MulticolorILUSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    m_sparsity_level = cfg.AMG_Config::template getParameter<int>(\"ilu_sparsity_level\", cfg_scope);\n    m_weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->m_use_bsrxmv = 1;\n    }\n    else\n    {\n        this->m_use_bsrxmv = 0;\n    }\n\n    if (m_weight == ValueTypeB(0.))\n    {\n        m_weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor DILU smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorILUSolver_Base<T_Config>::~MulticolorILUSolver_Base()\n{\n    m_LU.set_initialized(0);\n    m_A_to_LU_mapping.clear();\n    m_A_to_LU_mapping.shrink_to_fit();\n    m_LU.resize(0, 0, 0, 1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAtoLUmapping()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAtoLUmapping()\n{\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    const int CtaSize = 128; // Number of threads per CTA\n    const int SMemSize = 128;  // per warp\n    const int nWarps = CtaSize / 32;\n    int GridSize = std::min( AMGX_GRID_MAX_SIZE, ( this->m_explicit_A->get_num_rows( ) + nWarps - 1 ) / nWarps );\n    // Global memory workspaces\n    device_vector_alloc<int> returnValue(1);\n    returnValue[0] = 0;\n\n    if (this->m_explicit_A->hasProps(DIAG))\n    {\n        computeAtoLUmappingExtDiag_kernel<CtaSize, SMemSize> <<< GridSize, CtaSize >>> (\n            m_A.get_num_rows( ),\n            amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.diag[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n            amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n    }\n    else\n    {\n        computeAtoLUmapping_kernel<CtaSize, SMemSize> <<< GridSize, CtaSize >>> (\n            m_A.get_num_rows( ),\n            amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n            amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n    }\n\n    cudaCheckError();\n\n    // fallback path that allows 1024 nonzeros per row\n    if (returnValue[0] == 1)\n    {\n        returnValue[0] = 0;\n        const int SMemSize2 = 1024 ;  // per warp\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            computeAtoLUmappingExtDiag_kernel<CtaSize, SMemSize2> <<< GridSize, CtaSize >>> (\n                m_A.get_num_rows( ),\n                amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.diag[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n                amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n        }\n        else\n        {\n            computeAtoLUmapping_kernel<CtaSize, SMemSize2> <<< GridSize, CtaSize >>> (\n                m_A.get_num_rows( ),\n                amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n                amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n        }\n\n        cudaCheckError();\n    }\n\n    if (returnValue[0] == 1)\n    {\n        FatalError( \"Number of nonzeros per row exceeds allocated shared memory\", AMGX_ERR_NO_MEMORY);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::fillLUValuesWithAValues()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::fillLUValuesWithAValues()\n{\n    if (this->m_sparsity_level == 0)\n    {\n        this->m_LU.values = this->m_explicit_A->values;\n    }\n    else\n    {\n        // TODO: Should probably store the inverse mapping of AtoLUmapping instead\n        //       This will allow to use unpermuteVector and have coalesced writes\n        //       instead of coalesced reads\n        thrust_wrapper::fill<AMGX_device>(this->m_LU.values.begin(), this->m_LU.values.end(), 0.);\n        cudaCheckError();\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            amgx::permuteVector(this->m_explicit_A->values, this->m_LU.values, this->m_A_to_LU_mapping, (this->m_explicit_A->get_num_nz() + this->m_explicit_A->get_num_rows())*this->m_explicit_A->get_block_size());\n        }\n        else\n        {\n            amgx::permuteVector(this->m_explicit_A->values, this->m_LU.values, this->m_A_to_LU_mapping, this->m_explicit_A->get_num_nz()*this->m_explicit_A->get_block_size());\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeLUSparsityPattern()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeLUSparsityPattern()\n{\n    // ILU0\n    if (this->m_sparsity_level == 0)\n    {\n        // Copy everything except the values\n        this->m_LU.copy_structure(*this->m_explicit_A);\n    }\n    // ILU1\n    else if (this->m_sparsity_level == 1)\n    {\n        this->sparsity_wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *this->m_cfg, \"default\" );\n        CSR_Multiply<TConfig_d>::csr_sparsity_ilu1( *this->m_explicit_A, this->m_LU, this->sparsity_wk );\n        CSR_Multiply<TConfig_d>::csr_workspace_delete( this->sparsity_wk );\n\n        if (this->m_use_bsrxmv)\n        {\n            this->m_LU.set_initialized(0);\n            this->m_LU.computeDiagonal();\n            this->m_LU.set_initialized(1);\n        }\n\n        this->m_LU.setMatrixColoring(&(this->m_explicit_A->getMatrixColoring()));\n    }\n    else\n    {\n        FatalError(\"Haven't implemented Multicolor ILU smoother for this sparsity level. \", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeLUFactors()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeLUFactors()\n{\n    const int CtaSize = 128; // Number of threads per CTA\n    const int SMemSize = 128;\n    const int nWarps = CtaSize / 32;\n    device_vector_alloc<int> returnValue(1);\n    returnValue[0] = 0;\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n    const IndexType *LU_sorted_rows_by_color_ptr = this->m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = this->m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = this->m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_FACTORS\n        int GridSize = std::min( 2048, ( num_rows_per_color + nWarps - 1 ) / nWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4 )\n        {\n            if ( this->m_explicit_A->getBlockFormat() == ROW_MAJOR )\n            {\n                compute_LU_factors_4x4_kernel_warp<ValueTypeA, CtaSize, SMemSize, true> <<< GridSize, CtaSize>>>(\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n            else\n            {\n                compute_LU_factors_4x4_kernel_warp<ValueTypeA, CtaSize, SMemSize, false> <<< GridSize, CtaSize>>>(\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"Unsupported block size for Multicolor ILU solver, computeLUFactors\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n#else\n        int GridSize = std::min( AMGX_GRID_MAX_SIZE, ( num_rows_per_color + nWarps - 1 ) / nWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4 )\n        {\n            //computeLUFactors_4x4_kernel<ValueTypeA,CtaSize,SMemSize> <<< GridSize, CtaSize>>> (\n            if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n            {\n                computeLUFactors_4x4_kernel<ValueTypeA, CtaSize, SMemSize, true> <<< GridSize, CtaSize>>> (\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n            else\n            {\n                computeLUFactors_4x4_kernel<ValueTypeA, CtaSize, SMemSize, false> <<< GridSize, CtaSize>>> (\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"Unsupported block size for Multicolor ILU solver, computeLUFactors\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n#endif\n    }\n\n    // Check returnValue flag\n    if ( returnValue[0] == 1 )\n    {\n        FatalError( \"Number of nonzeros per row exceeds allocated shared memory\", AMGX_ERR_NO_MEMORY);\n    }\n}\n\n\n// Solver pre-setup\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::pre_setup()\n{\n    // Check if matrix is colored\n    if (this->m_explicit_A->getColoringLevel() < m_sparsity_level + 1)\n    {\n        FatalError(\"Matrix must be colored with coloring_level > sparsity_level for the multicolorILUsolver\", AMGX_ERR_CONFIGURATION);\n    }\n\n    // Compute extended sparsity pattern based on coloring and matrix A\n    computeLUSparsityPattern();\n\n    if (this->m_LU.hasProps(DIAG))\n    {\n        FatalError(\"Multicolor ILU smoother does not support outside diagonal. Try setting reorder_cols_by_color=1 and insert_diag_while_reordering=1 in the multicolor_ilu solver scope in configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (m_sparsity_level == 0 && !this->m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"Multicolor ILU smoother requires matrix to be reordered by color with ILU0 solver. Try setting reorder_cols_by_color=1 and insert_diag_while_reordering=1 in the multicolor_ilu solver scope in configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Reorder the columns of LU by color\n    if (m_sparsity_level != 0)\n    {\n        // Reorder columns of LU by color\n        m_LU.reorderColumnsByColor(false);\n\n        // Compute mapping between entries in A and entries in LU\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            m_A_to_LU_mapping.resize(this->m_explicit_A->get_num_nz() + this->m_explicit_A->get_num_rows());\n        }\n        else\n        {\n            m_A_to_LU_mapping.resize(this->m_explicit_A->get_num_nz());\n        }\n\n        computeAtoLUmapping();\n    }\n\n    int N = this->m_LU.get_num_rows() * this->m_LU.get_block_dimy();\n    m_delta.resize(N);\n    m_Delta.resize(N);\n    m_Delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_Delta.set_block_dimx(1);\n    m_delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_delta.set_block_dimx(1);\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor = \" << this->m_weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->m_use_bsrxmv << std::endl;\n    std::cout << \"ilu_sparsity_level = \" << this->m_sparsity_level <<  std::endl;\n}\n\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorILUSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor ilu solver. Try setting: coloring_level=1 or coloring_level=2 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (!reuse_matrix_structure)\n    {\n        this->pre_setup();\n    }\n\n    // Fill LU sparsity pattern\n    fillLUValuesWithAValues();\n    // Compute LU factors in place (update LU.values)\n    computeLUFactors();\n}\n\n//\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorILUSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if ( !m_use_bsrxmv && (this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4) )\n    {\n        smooth_4x4(b, x, xIsZero);\n    }\n    else\n    {\n        smooth_bxb(b, x, xIsZero);\n    }\n\n    // Do we converge ?\n    return this->converged(b, x);\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const VVector &b, VVector &x, bool xIsZero)\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<TConfig_d> &m_LU = this->m_LU;\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    int N = m_LU.get_num_rows() * m_LU.get_block_dimy();\n    cudaCheckError();\n\n    if (!m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"ILU solver currently only works if columns are reordered by color. Try setting reordering_cols_by_color=1 in the multicolor_ilu solver scope in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // ---------------------------------------------------------\n    // Solving Lower triangular system, with identity diagonal\n    // ---------------------------------------------------------\n    const IndexType *LU_sorted_rows_by_color_ptr = m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_FORWARD\n        const int CtaSize = 128; // Number of threads per CTA\n        const int nHalfWarps = CtaSize / 16;\n        int GridSize = std::min( 2048, ( num_rows_per_color + nHalfWarps - 1 ) / nHalfWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_explicit_A->getBlockFormat() == ROW_MAJOR )\n        {\n            if (m_A.hasProps(DIAG))\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, true, true> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n            else\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, true, false> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n        }\n        else\n        {\n            // COL_MAJOR\n            if (m_A.hasProps(DIAG))\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, false, true> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n            else\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, false, false> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n        }\n\n#else\n        const int CtaSize = 128;\n        const int blockrows_per_cta = CtaSize / 4;\n        const int GridSize = min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color + blockrows_per_cta - 1) / blockrows_per_cta);\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            FatalError(\"this implementation of LU forward solve does not support A with external diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_forward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, true> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_smaller_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.values.raw(),\n             m_A.row_offsets.raw(),\n             m_A.col_indices.raw(),\n             m_A.values.raw(),\n             x.raw(),\n             b.raw(),\n             delta.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             xIsZero);\n        }\n        else\n        {\n            LU_forward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, false> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_smaller_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.values.raw(),\n             m_A.row_offsets.raw(),\n             m_A.col_indices.raw(),\n             m_A.values.raw(),\n             x.raw(),\n             b.raw(),\n             delta.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             xIsZero);\n        }\n\n#endif\n        cudaCheckError();\n    }\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for (int i = num_colors - 1; i >= 0; i--)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_BACKWARD\n        const int CtaSize = 128; // Number of threads per CTA\n        const int nHalfWarps = CtaSize / 16;\n        int GridSize = std::min( 2048, ( num_rows_per_color + nHalfWarps - 1 ) / nHalfWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_backward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, true> <<< GridSize, CtaSize>>>(\n                m_LU.row_offsets.raw(),\n                m_LU.m_larger_color_offsets.raw(),\n                m_LU.col_indices.raw(),\n                m_LU.diag.raw(),\n                m_LU.values.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                x.raw(),\n                LU_sorted_rows_by_color_ptr + color_offset,\n                num_rows_per_color,\n                i,\n                num_colors,\n                this->m_weight,\n                xIsZero);\n        }\n        else\n        {\n            LU_backward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, false> <<< GridSize, CtaSize>>>(\n                m_LU.row_offsets.raw(),\n                m_LU.m_larger_color_offsets.raw(),\n                m_LU.col_indices.raw(),\n                m_LU.diag.raw(),\n                m_LU.values.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                x.raw(),\n                LU_sorted_rows_by_color_ptr + color_offset,\n                num_rows_per_color,\n                i,\n                num_colors,\n                this->m_weight,\n                xIsZero);\n        }\n\n#else\n        const int CtaSize = 128;\n        const int blockrows_per_cta = CtaSize / 4;\n        const int GridSize = min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color + blockrows_per_cta - 1) / blockrows_per_cta);\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_backward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, true> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_larger_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.diag.raw(),\n             m_LU.values.raw(),\n             this->m_delta.raw(),\n             this->m_Delta.raw(),\n             x.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             num_colors,\n             this->m_weight,\n             xIsZero);\n        }\n        else\n        {\n            LU_backward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, false> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_larger_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.diag.raw(),\n             m_LU.values.raw(),\n             this->m_delta.raw(),\n             this->m_Delta.raw(),\n             x.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             num_colors,\n             this->m_weight,\n             xIsZero);\n        }\n\n#endif\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_bxb(const VVector &b, VVector &x, bool xIsZero)\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_bxb(const VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<TConfig_d> &m_LU = this->m_LU;\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    int N = m_LU.get_num_rows() * m_LU.get_block_dimy();\n\n    if (!m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"ILU solver currently only works if columns are reordered by color. Try setting reorder_cols_by_color=1 in the multicolor_ilu solver scope in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (this->m_explicit_A->getBlockFormat() == COL_MAJOR)\n    {\n        FatalError(\"ILU solver for arbitrary block sizes only works with ROW_MAJOR matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // ---------------------------------------------------------\n    // Solving Lower triangular system, with identity diagonal\n    // ---------------------------------------------------------\n    const IndexType *LU_sorted_rows_by_color_ptr = m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n    //delta = b;\n    amgx::thrust::copy(b.begin(), b.end(), this->m_delta.begin());\n    //delta = delta - Ax;\n    Cusparse::bsrmv((ValueTypeA) - 1.0, m_A, x, (ValueTypeA)1.0, this->m_delta);\n    cudaCheckError();\n    // Setting Delta to zero\n    thrust_wrapper::fill<AMGX_device>(this->m_Delta.begin(), this->m_Delta.end(), (ValueTypeB)0.0f);\n    cudaCheckError();\n    bool skipped_end = false;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if (num_rows_per_color == 0) { continue; } // if perfect coloring (color 0 has no vertices)\n\n        if (skipped_end)\n        {\n            // delta = delta - LU*Delta smaller colors\n            Cusparse::bsrmv(Cusparse::SMALLER_COLORS, i, (ValueTypeA) - 1.0f, m_LU, this->m_delta, (ValueTypeA)1.0f, this->m_delta);\n        }\n\n        if (num_rows_per_color > 0)\n        {\n            skipped_end = true;\n        }\n    }\n\n    cudaCheckError();\n    skipped_end = false;\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for (int i = num_colors - 1; i >= 0; i--)\n    {\n        // delta = delta - LU*Delta larger colors\n        Cusparse::bsrmv(Cusparse::LARGER_COLORS, i, (ValueTypeA) - 1.0f, m_LU, this->m_Delta, (ValueTypeA)1.0f, this->m_delta);\n        // Multiple by inverse stored on diagonal\n        Cusparse::bsrmv(Cusparse::DIAG_COL, i, (ValueTypeA) 1.0f, m_LU, this->m_delta, 0.0f, this->m_Delta);\n    }\n\n    cudaCheckError();\n    axpy(this->m_Delta, x, this->m_weight, 0, x.size());\n    cudaCheckError();\n}\n\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorILUSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorILUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_ilu_solver.h>\n#include <solvers/block_common_solver.h>\n#include <csr_multiply.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <matrix_io.h>\n#include <permute.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n#include <algorithm>\n\n// TODO: Have 2 groups of 16 threads collaborate\n// TODO: Add support for outside diagonal\n// TODO: Add support for unsorted rows\n\n#define EXPERIMENTAL_LU_FACTORS\n#define EXPERIMENTAL_LU_FORWARD\n#define EXPERIMENTAL_LU_BACKWARD\n\nnamespace amgx\n{\n\nnamespace multicolor_ilu_solver\n{\n\n// -----------\n// Kernels\n// -----------\n\n#ifdef EXPERIMENTAL_LU_FORWARD\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize, int bsize, bool ROW_MAJOR, bool hasDiag>\n__global__\n__launch_bounds__( CtaSize, 16 )\nvoid LU_forward_4x4_kernel_warp( const IndexType *LU_row_offsets,\n                                 const IndexType *LU_smaller_color_offsets,\n                                 const IndexType *LU_column_indices,\n                                 const ValueTypeA *LU_nonzero_values,\n                                 const IndexType *A_row_offsets,\n                                 const IndexType *A_column_indices,\n                                 const ValueTypeA *A_nonzero_values,\n                                 const IndexType *A_dia_indices,\n                                 const ValueTypeB *x,\n                                 const ValueTypeB *b,\n                                 ValueTypeB *delta,\n                                 const int *sorted_rows_by_color,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 bool xIsZero )\n{\n    const int nHalfWarps = CtaSize / 16; // Number of half warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CtaSize];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        // Load one block of B.\n        ValueTypeB my_bmAx(0);\n\n        unsigned int active_mask = utils::activemask();\n\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * aRowId + halfLaneId_div_4]);\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * aRowId + halfLaneId_mod_4]);\n            }\n        }\n\n        // Don't do anything if X is zero.\n        if ( !xIsZero )\n        {\n            int aColBegin = A_row_offsets[aRowId  ];\n            int aColEnd   = A_row_offsets[aRowId + 1];\n            int aColMax = aColEnd;\n\n            if ( hasDiag )\n            {\n                ++aColMax;\n            }\n\n            // Each warp load column indices of 32 nonzero blocks\n            for ( ; utils::any( aColBegin < aColMax, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                // Get the ID of the column.\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = A_column_indices[aColIt];\n                }\n\n                if ( hasDiag && aColIt == aColEnd )\n                {\n                    aColId = aRowId;\n                }\n\n                // Count the number of active columns.\n                int vote =  utils::ballot(aColId != -1, active_mask);\n                // The number of iterations.\n                int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n                // Loop over columns. We compute 8 columns per iteration.\n                for ( int k = 0 ; k < nCols ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Load 8 blocks of X.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    ValueTypeB my_x(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_x = __cachingLoad(&x[4 * waColId + halfLaneId_mod_4]);\n                    }\n\n                    my_s_mem[halfLaneId] = my_x;\n                    // Load 8 blocks of A.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        if ( hasDiag && w_aColTmp == aColEnd )\n                        {\n                            w_aColIt = A_dia_indices[aRowId];\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = A_nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n        } // if xIsZero\n\n        // Contribution from each nonzero column that has color less than yours\n        if ( current_color != 0 )\n        {\n            // TODO: Use constant or texture here\n            int aColBegin = LU_row_offsets[aRowId];\n            int aColEnd   = LU_smaller_color_offsets[aRowId];\n\n            // Each warp load column indices of 32 nonzero blocks\n            for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = LU_column_indices[aColIt];\n                }\n\n                // Count the number of active columns.\n                int vote =  utils::ballot(aColId != -1, active_mask);\n                // The number of iterations.\n                int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n                for ( int k = 0 ; k < nCols ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Load 8 blocks of X.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    ValueTypeB my_delta(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_delta = delta[4 * waColId + halfLaneId_mod_4];\n                    }\n\n                    my_s_mem[halfLaneId] = my_delta;\n                    utils::syncwarp(); // making sure smem write propagated\n                    // Update b-Ax.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = LU_nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n        } // If current_color != 0\n\n        // Reduce bmAx terms.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n            my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n        }\n        else\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n            my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                delta[4 * aRowId + halfLaneId_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                delta[4 * aRowId + halfLaneId_mod_4] = my_bmAx;\n            }\n        }\n    }\n}\n\n#else\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, bool ROW_MAJOR>\n__global__\nvoid LU_forward_4x4_kernel(const IndexType *LU_row_offsets, const IndexType *LU_smaller_color_offsets, const IndexType *LU_column_indices, const ValueTypeA *LU_nonzero_values,  const IndexType *A_row_offsets, const IndexType *A_column_indices, const ValueTypeA *A_nonzero_values,\n                           const ValueTypeB *x, const ValueTypeB *b,  ValueTypeB *delta, const int *sorted_rows_by_color,\n                           const int num_rows_per_color, const int current_color, bool xIsZero)\n\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_delta_temp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows_per_color &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        // Load RHS and x\n        offset = i * bsize + vec_entry_index;\n        bmAx = b[offset];\n\n        if (!xIsZero)\n        {\n            int jmin = A_row_offsets[i];\n            int jmax = A_row_offsets[i + 1];\n\n            //TODO: Assumes inside diagonal\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = A_column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n                s_delta_temp[tid] = x[offset];\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(A_nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = A_nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_delta_temp[s_offset++];\n                }\n            }\n        }\n\n        // Contribution from each nonzero column that has color less than yours\n        if (current_color != 0)\n        {\n            int jmin = LU_row_offsets[i];\n            int jmax = LU_smaller_color_offsets[i];\n\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = LU_column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n                s_delta_temp[tid] = __ldcg(delta + offset);\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(LU_nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = LU_nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_delta_temp[s_offset++];\n                }\n            }\n        }\n\n        delta[i * bsize + vec_entry_index] = bmAx;\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n#endif\n\n#ifdef EXPERIMENTAL_LU_BACKWARD\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int CtaSize, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CtaSize, 16 )\nvoid LU_backward_4x4_kernel_warp( const IndexType *row_offsets,\n                                  const IndexType *larger_color_offsets,\n                                  const IndexType *column_indices,\n                                  const IndexType *dia_indices,\n                                  const ValueTypeA *nonzero_values,\n                                  const ValueTypeB *delta,\n                                  ValueTypeB *Delta,\n                                  ValueTypeB *x,\n                                  const int *sorted_rows_by_color,\n                                  const int num_rows_per_color,\n                                  const int current_color,\n                                  const int num_colors,\n                                  const ValueTypeB weight,\n                                  bool xIsZero )\n{\n    const int nHalfWarps = CtaSize / 16; // Number of half warps per CTA.\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CtaSize];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        unsigned int active_mask = utils::activemask();\n        // Load one block of B.\n        ValueTypeB my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_bmAx = delta[4 * aRowId + halfLaneId_div_4];\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_bmAx = delta[4 * aRowId + halfLaneId_mod_4];\n            }\n        }\n\n        // Don't do anything if the color is not the interesting one.\n        if ( current_color != num_colors - 1 )\n        {\n            // The range of the rows.\n            int aColBegin = larger_color_offsets[aRowId], aColEnd = row_offsets[aRowId + 1];\n\n            // Each warp load column indices of 16 nonzero blocks\n            for ( ; utils::any( aColBegin < aColEnd, active_mask ) ; aColBegin += 16 )\n            {\n                int aColIt = aColBegin + halfLaneId;\n                // Get the ID of the column.\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = column_indices[aColIt];\n                }\n\n                // Loop over columns. We compute 8 columns per iteration.\n                for ( int k = 0 ; k < 16 ; k += 4 )\n                {\n                    int my_k = k + halfLaneId_div_4;\n                    // Exchange column indices.\n                    int waColId = utils::shfl( aColId, upperHalf + my_k, warpSize, active_mask );\n                    // Load 8 blocks of X if needed.\n                    ValueTypeB *my_ptr = Delta;\n\n                    if ( xIsZero )\n                    {\n                        my_ptr = x;\n                    }\n\n                    ValueTypeB my_x(0);\n\n                    if ( waColId != -1 )\n                    {\n                        my_x = my_ptr[4 * waColId + halfLaneId_mod_4];\n                    }\n\n                    my_s_mem[halfLaneId] = my_x;\n                    utils::syncwarp();\n                    // Load 8 blocks of A.\n#pragma unroll\n                    for ( int i = 0 ; i < 4 ; ++i )\n                    {\n                        int w_aColTmp = aColBegin + k + i, w_aColIt = -1;\n\n                        if ( w_aColTmp < aColEnd )\n                        {\n                            w_aColIt = w_aColTmp;\n                        }\n\n                        ValueTypeA my_val(0);\n\n                        if ( w_aColIt != -1 )\n                        {\n                            my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                        }\n\n                        if ( ROW_MAJOR )\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                        }\n                        else\n                        {\n                            my_bmAx -= my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                        }\n                    }\n                } // Loop over k\n            } // Loop over aColIt\n\n            // Reduce bmAx terms.\n            if ( ROW_MAJOR )\n            {\n                my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n                my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n            }\n            else\n            {\n                my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n                my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n            }\n        } // if current_color != num_colors-1\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_s_mem[halfLaneId_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_s_mem[halfLaneId_mod_4] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        int w_aColIt = dia_indices[aRowId];\n        ValueTypeA my_val(0);\n        utils::syncwarp();\n\n        if ( w_aColIt != -1 )\n        {\n            my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n        }\n\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_val * my_s_mem[halfLaneId_mod_4];\n        }\n        else\n        {\n            my_bmAx = my_val * my_s_mem[halfLaneId_div_4];\n        }\n\n        // Regroup results.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 1 );\n            my_bmAx += utils::shfl_xor( my_bmAx, 2 );\n        }\n        else\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, 4 );\n            my_bmAx += utils::shfl_xor( my_bmAx, 8 );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            ValueTypeB my_x(0);\n\n            if ( !xIsZero && halfLaneId_mod_4 == 0 )\n            {\n                my_x = x[4 * aRowId + halfLaneId_div_4];\n            }\n\n            my_x += weight * my_bmAx;\n\n            if ( !xIsZero && halfLaneId_mod_4 == 0 )\n            {\n                Delta[4 * aRowId + halfLaneId_div_4] = my_bmAx;\n            }\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                x[4 * aRowId + halfLaneId_div_4] = my_x;\n            }\n        }\n        else\n        {\n            ValueTypeB my_x(0);\n\n            if ( !xIsZero && halfLaneId_div_4 == 0 )\n            {\n                my_x = x[4 * aRowId + halfLaneId_mod_4];\n            }\n\n            my_x += weight * my_bmAx;\n\n            if ( !xIsZero && halfLaneId_div_4 == 0 )\n            {\n                Delta[4 * aRowId + halfLaneId_mod_4] = my_bmAx;\n            }\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                x[4 * aRowId + halfLaneId_mod_4] = my_x;\n            }\n        }\n    }\n}\n\n#else\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int blockrows_per_cta, int blockrows_per_warp, int bsize, bool ROW_MAJOR>\n__global__\nvoid LU_backward_4x4_kernel(const IndexType *row_offsets, const IndexType *larger_color_offsets, const IndexType *column_indices, const IndexType *dia_indices, const ValueTypeA *nonzero_values,\n                            const ValueTypeB *delta,  ValueTypeB *Delta, ValueTypeB *x, const int *sorted_rows_by_color,\n                            const int num_rows_per_color, const int current_color, const int num_colors, const ValueTypeB weight, bool xIsZero)\n\n\n{\n    int warp_id = threadIdx.x / 32;\n    int warp_thread_id = threadIdx.x & 31;\n\n    // padding row blocks to fit in a single warp\n    if ( warp_thread_id >= blockrows_per_warp * bsize ) { return; }\n\n    // new thread id with padding\n    int tid = warp_id * blockrows_per_warp * bsize + warp_thread_id;\n    // Here we use one thread per row (not block row)\n    int cta_blockrow_id = (tid) / bsize;\n    int blockrow_id = blockIdx.x * blockrows_per_cta + cta_blockrow_id;\n    const int vec_entry_index = tid - cta_blockrow_id * bsize;\n    volatile __shared__ ValueTypeB s_x_temp[ bsize * blockrows_per_cta];\n    int offset, s_offset, i;\n    ValueTypeB bmAx, temp[bsize];\n\n    while (blockrow_id < num_rows_per_color &&  cta_blockrow_id < blockrows_per_cta)\n    {\n        i = sorted_rows_by_color[blockrow_id];\n        // Load RHS and x\n        offset = i * bsize + vec_entry_index;\n        bmAx = delta[offset];\n\n        // Contribution from each nonzero column that has color less than yours\n        if (current_color != num_colors)\n        {\n            int jmin = larger_color_offsets[i];\n            int jmax = row_offsets[i + 1];\n\n            for (int jind = jmin; jind < jmax; jind++)\n            {\n                IndexType jcol = column_indices[jind];\n                offset = jcol * bsize + vec_entry_index;\n\n                if (xIsZero)\n                {\n                    s_x_temp[tid] = __ldcg(x + offset);\n                }\n                else\n                {\n                    s_x_temp[tid] = __ldcg(Delta + offset);\n                }\n\n                // Load nonzero_values\n                if (ROW_MAJOR)\n                {\n                    offset = jind * bsize * bsize + vec_entry_index * bsize;\n                    loadAsVector<bsize>(nonzero_values + offset, temp);\n                }\n                else\n                {\n                    offset = jind * bsize * bsize + vec_entry_index;\n#pragma unroll\n                    for (int m = 0; m < bsize; m++)\n                    {\n                        temp[m] = nonzero_values[offset + bsize * m];\n                    }\n                }\n\n                // Do matrix multiply\n                s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n                for (int m = 0; m < bsize; m++)\n                {\n                    bmAx -= temp[m] * s_x_temp[s_offset++];\n                }\n            }\n        }\n\n        s_x_temp[tid] = bmAx;\n        bmAx = 0.;\n\n        // Load diagonals (which store the inverse)\n        if (ROW_MAJOR)\n        {\n            offset = dia_indices[i] * bsize * bsize + vec_entry_index * bsize;\n            loadAsVector<bsize>(nonzero_values + offset, temp);\n        }\n        else\n        {\n            offset = dia_indices[i] * bsize * bsize + vec_entry_index;\n#pragma unroll\n            for (int m = 0; m < bsize; m++)\n            {\n                temp[m] = nonzero_values[offset + bsize * m];\n            }\n        }\n\n        // Do matrix-vector multiply\n        s_offset = cta_blockrow_id * bsize;\n#pragma unroll\n        for (int m = 0; m < bsize; m++)\n        {\n            bmAx += temp[m] * s_x_temp[s_offset++];\n        }\n\n        offset = i * bsize + vec_entry_index;\n\n        if (xIsZero)\n        {\n            x[offset] = weight * bmAx;\n        }\n        else\n        {\n            Delta[offset] = bmAx;\n            x[offset] += weight * bmAx ;\n        }\n\n        blockrow_id += blockrows_per_cta * gridDim.x;\n    }\n}\n\n#endif\n\n// Assumptions:\n// CtaSize must be multiple of 32\n// SMemSize should be larger than the maximum number of columns in the matrix\n// Matrix B is superset of matrix A\n\ntemplate< int CtaSize, int SMemSize>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeAtoLUmapping_kernel( int A_nRows,\n                            const int *__restrict A_row_offsets,\n                            const int *__restrict A_col_indices,\n                            const int *__restrict B_row_offsets,\n                            const int *__restrict B_col_indices,\n                            int *__restrict AtoBmapping,\n                            int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Rows are stored in SMEM. Linear storage.\n    __shared__ volatile int s_colInd[nWarps][SMemSize];\n    // The row this warp is responsible for\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    // Loop over rows of A.\n    for ( ; aRowId < A_nRows ; aRowId += nWarps * gridDim.x )\n    {\n        // Insert all the column indices of matrix B in the shared memory table\n        int bColBeg = B_row_offsets[aRowId];\n        int bColEnd = B_row_offsets[aRowId + 1];\n        // The number of columns.\n        const int nCols = bColEnd - bColBeg;\n\n        //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n        if ( nCols > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Fill-in the local table.\n        const int NUM_STEPS = SMemSize / 32;\n#pragma unroll\n        for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n        {\n            int bColIt = bColBeg + k;\n            int bColId = -1;\n\n            if ( bColIt < bColEnd )\n            {\n                bColId = B_col_indices[bColIt];\n            }\n\n            s_colInd[warpId][k] = bColId;\n        }\n\n        // Now load column indices of current row of A\n        int aColIt  = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n\n        for ( aColIt += laneId ; utils::any(aColIt < aColEnd) ; aColIt += 32 )\n        {\n            // The column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = A_col_indices[aColIt];\n            }\n\n            // Each thread searches for its column id, and gets the corresponding bColIt\n            // TODO: Try binary search or using hash table\n            int foundOffset = -1;\n\n            if ( aColId == -1 )\n            {\n                foundOffset = -2;\n            }\n\n            for ( int i = 0 ; i < nCols && utils::any(foundOffset == -1) ; ++i )\n                if ( foundOffset == -1 && s_colInd[warpId][i] == aColId )\n                {\n                    foundOffset = i;\n                }\n\n            // Store the result.\n            if ( aColIt < aColEnd )\n            {\n                AtoBmapping[aColIt] = bColBeg + foundOffset;\n            }\n        }\n    } // if RowId < A_nRows;\n}\n\ntemplate< int CtaSize, int SMemSize>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeAtoLUmappingExtDiag_kernel( int A_nRows,\n                                   const int *__restrict A_row_offsets,\n                                   const int *__restrict A_col_indices,\n                                   const int *__restrict A_dia_indices,\n                                   const int *__restrict B_row_offsets,\n                                   const int *__restrict B_col_indices,\n                                   int *__restrict AtoBmapping,\n                                   int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Rows are stored in SMEM. Linear storage.\n    __shared__ volatile int s_colInd[nWarps][SMemSize];\n    // The row this warp is responsible for\n    int aRowId = blockIdx.x * nWarps + warpId;\n\n    // Loop over rows of A.\n    for ( ; aRowId < A_nRows ; aRowId += nWarps * gridDim.x )\n    {\n        // Insert all the column indices of matrix B in the shared memory table\n        int bColBeg = B_row_offsets[aRowId];\n        int bColEnd = B_row_offsets[aRowId + 1];\n        // The number of columns.\n        const int nCols = bColEnd - bColBeg;\n\n        //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n        if ( nCols > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Fill-in the local table.\n        const int NUM_STEPS = SMemSize / 32;\n#pragma unroll\n        for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n        {\n            int bColIt = bColBeg + k;\n            int bColId = -1;\n\n            if ( bColIt < bColEnd )\n            {\n                bColId = B_col_indices[bColIt];\n            }\n\n            s_colInd[warpId][k] = bColId;\n        }\n\n        // Now load column indices of current row of A\n        int aColIt  = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n\n        for ( aColIt += laneId ; utils::any(aColIt <= aColEnd) ; aColIt += 32 )\n        {\n            // The column.\n            int aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColId = A_col_indices[aColIt];\n            }\n\n            if ( aColIt == aColEnd )\n            {\n                aColId = aRowId;\n            }\n\n            // Each thread searches for its column id, and gets the corresponding bColIt\n            // TODO: Try binary search or using hash table\n            int foundOffset = -1;\n\n            if ( aColId == -1 )\n            {\n                foundOffset = -2;\n            }\n\n            for ( int i = 0 ; i < nCols && utils::any(foundOffset == -1) ; ++i )\n                if ( foundOffset == -1 && s_colInd[warpId][i] == aColId )\n                {\n                    foundOffset = i;\n                }\n\n            // Store the result.\n            int aDst = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aDst = aColIt;\n            }\n\n            if ( aColIt == aColEnd )\n            {\n                aDst = A_dia_indices[aRowId];\n            }\n\n            if ( aDst != -1 )\n            {\n                AtoBmapping[aDst] = bColBeg + foundOffset;\n            }\n        }\n    }\n}\n\n#ifdef EXPERIMENTAL_LU_FACTORS\n\ntemplate< typename ValueTypeA, int CtaSize, int SMemSize, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CtaSize, 12 )\nvoid\ncompute_LU_factors_4x4_kernel_warp( int A_nRows,\n                                    const int *__restrict A_row_offsets,\n                                    const int *__restrict A_col_indices,\n                                    const int *__restrict A_dia_indices,\n                                    ValueTypeA *__restrict A_nonzero_values,\n                                    const int *__restrict A_smaller_color_offsets,\n                                    const int *__restrict A_larger_color_offsets,\n                                    const int *sorted_rows_by_color,\n                                    const int num_rows_per_color,\n                                    const int current_color,\n                                    int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    int globalWarpId = blockIdx.x * nWarps + warpId;\n    // Shared memory to store the blocks to process\n    __shared__ volatile ValueTypeA s_C_mtx[nWarps][32];\n    __shared__ volatile ValueTypeA s_F_mtx[nWarps][16];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile int s_aColSrc[nWarps][32];\n    // Shared memory to store the column indices of the current row\n    __shared__ volatile int s_keys[nWarps][SMemSize];\n\n    while (globalWarpId < num_rows_per_color)\n    {\n        int storedRowId[2];\n        int I = 0;\n\n        for (; I < 2 && globalWarpId < num_rows_per_color ; I++)\n        {\n            int aRowId = sorted_rows_by_color[globalWarpId];\n            storedRowId[I] = aRowId;\n            int aColBeg = A_row_offsets[aRowId + 0];\n            int aColEnd = A_row_offsets[aRowId + 1];\n            int aColSmaller = A_smaller_color_offsets[aRowId];\n            // The number of columns.\n            const int nCols = aColEnd - aColBeg;\n\n            //TODO: Add fallback for cases where number of nonzeros exceed SMemSize\n            if ( nCols > SMemSize )\n            {\n                wk_returnValue[0] = 1;\n                return;\n            }\n\n            // Fill-in the local table.\n            const int NUM_STEPS = SMemSize / 32;\n\n#pragma unroll\n            for ( int step = 0, k = laneId ; step < NUM_STEPS ; ++step, k += 32 )\n            {\n                int aColIt = aColBeg + k;\n                int aColId = -1;\n\n                if ( aColIt < aColEnd )\n                {\n                    aColId = A_col_indices[aColIt];\n                }\n\n                s_keys[warpId][k] = aColId;\n            }\n\n            // Now load all column indices of neighbours that have colors smaller than yours\n            for ( int aColIt = aColBeg; aColIt < aColSmaller ; aColIt++)\n            {\n                unsigned int active_mask = utils::activemask();\n                // Read the row to process, should be a broadcast\n                int waRowId = s_keys[warpId][aColIt - aColBeg];\n                // Compute multiplicative factor, load C_jj in first half, C_ij in second half\n                int aColIdx = aColIt;\n\n                if ( lane_id_div_16 == 0 )\n                {\n                    aColIdx = A_dia_indices[waRowId];\n                }\n\n                s_C_mtx[warpId][laneId] = A_nonzero_values[16 * aColIdx + lane_id_mod_16];\n                // Threads 0-15 perform the matrix product\n                ValueTypeA tmp(0);\n\n                if (ROW_MAJOR)\n                {\n\n#pragma unroll\n                    for ( int m = 0 ; m < 4 ; ++m )\n                    {\n                        tmp += s_C_mtx[warpId][16 + 4 * idx_i + m] * s_C_mtx[warpId][4 * m + idx_j];\n                    }\n                }\n                else\n                {\n\n#pragma unroll\n                    for ( int m = 0 ; m < 4 ; ++m )\n                    {\n                        tmp += s_C_mtx[warpId][16 + 4 * m + idx_j] * s_C_mtx[warpId][4 * idx_i + m];\n                    }\n                }\n\n                if ( lane_id_div_16 == 0 )\n                {\n                    s_F_mtx[warpId][laneId] = tmp;\n                    A_nonzero_values[16 * aColIt + laneId] = tmp;\n                }\n\n                int waColIt  = __ldcg(A_larger_color_offsets + waRowId);\n                int waColEnd = __ldcg(A_row_offsets + waRowId + 1);\n\n                // Load the first 32 columns of waRowId\n                for (waColIt += laneId ; utils::any(waColIt < waColEnd, active_mask ); waColIt += 32 )\n                {\n                    // Each thread loads its column id\n                    int waColId = -1;\n\n                    if ( waColIt < waColEnd )\n                    {\n                        waColId = A_col_indices[waColIt];\n                    }\n\n                    // Find the right column.\n                    int found_aColIt = -1;\n\n#pragma unroll 4\n                    for ( int i = 0, num_keys = aColEnd - aColBeg ; i < num_keys ; ++i )\n                        if ( s_keys[warpId][i] == waColId )\n                        {\n                            found_aColIt = i;\n                        }\n\n                    if ( found_aColIt != -1 )\n                    {\n                        found_aColIt += aColBeg;\n                    }\n\n                    // Store all the columns that have been found\n                    const int pred = found_aColIt != -1;\n                    int vote = utils::ballot( pred, active_mask );\n                    const int idst = __popc(vote & utils::lane_mask_lt());\n\n                    if (pred)\n                    {\n                        s_aColSrc[warpId][idst] = laneId;\n                    }\n                    utils::syncwarp(active_mask);\n\n                    const int n_cols = __popc( vote );\n\n                    // Process all columns that have been found\n                    for ( int k = 0 ; k < n_cols ; k += 2 )\n                    {\n                        const int my_k = k + lane_id_div_16;\n                        // Where to get columns from.\n                        int a_col_it = -1, w_col_it = -1;\n                        // Load column to load\n                        a_col_it = utils::shfl(found_aColIt, s_aColSrc[warpId][my_k], warpSize, active_mask);\n                        w_col_it = utils::shfl(waColIt,      s_aColSrc[warpId][my_k], warpSize, active_mask);\n\n                        if ( my_k >= n_cols )\n                        {\n                            a_col_it = -1;\n                            w_col_it = -1;\n                        }\n\n                        ValueTypeA my_C(0);\n\n                        if ( w_col_it != -1 )\n                        {\n                            my_C = A_nonzero_values[16 * w_col_it + lane_id_mod_16];\n                        }\n\n                        s_C_mtx[warpId][laneId] = my_C;\n                        // Run the matrix-matrix product.\n                        ValueTypeA tmp(0);\n                        utils::syncwarp( active_mask );\n\n                        if (ROW_MAJOR)\n                        {\n#pragma unroll\n                            for ( int m = 0 ; m < 4 ; ++m )\n                            {\n                                tmp += s_F_mtx[warpId][4 * idx_i + m] * s_C_mtx[warpId][16 * lane_id_div_16 + 4 * m + idx_j];\n                            }\n                        }\n                        else\n                        {\n#pragma unroll\n                            for ( int m = 0 ; m < 4 ; ++m )\n                            {\n                                tmp += s_F_mtx[warpId][4 * m + idx_j] * s_C_mtx[warpId][16 * lane_id_div_16 + 4 * idx_i + m];\n                            }\n                        }\n\n                        if ( a_col_it != -1 )\n                        {\n                            A_nonzero_values[16 * a_col_it + lane_id_mod_16] -= tmp;\n                        }\n                    } // Loop over columns that have a match (for k=0;k<n_cols)\n                } // Loop over the columns of waRowId\n\n                //}  // Loop j=0;j<32\n            } // Loop over the columns of aRowId\n\n            globalWarpId += nWarps * gridDim.x;\n        } // end of loop over I\n\n        // Now compute the inverse of the block C_jj\n        if ( lane_id_div_16 == 0 || I == 2 )\n        {\n            const int offset = 16 * A_dia_indices[storedRowId[lane_id_div_16]] + lane_id_mod_16;\n            s_C_mtx[warpId][laneId] = A_nonzero_values[offset];\n            utils::syncwarp(utils::activemask());\n\n            if (ROW_MAJOR)\n            {\n                compute_block_inverse_row_major4x4_formula2<int, ValueTypeA, 4, true>( s_C_mtx[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, A_nonzero_values );\n            }\n            else\n            {\n                compute_block_inverse_col_major4x4_formula2<int, ValueTypeA, 4, true>( s_C_mtx[warpId], 16 * lane_id_div_16, offset, idx_i, idx_j, A_nonzero_values );\n            }\n        } // End of if statement\n    } // End of while loop\n}\n\n#else\n\ntemplate< typename ValueTypeA, int CtaSize, int SMemSize, bool ROW_MAJOR>\n__global__ __launch_bounds__( CtaSize )\nvoid\ncomputeLUFactors_4x4_kernel( int A_nRows,\n                             const int *__restrict A_row_offsets,\n                             const int *__restrict A_col_indices,\n                             const int *__restrict A_dia_indices,\n                             ValueTypeA *__restrict A_nonzero_values,\n                             const int *__restrict A_smaller_color_offsets,\n                             const int *__restrict A_larger_color_offsets,\n                             const int *sorted_rows_by_color,\n                             const int num_rows_per_color,\n                             const int current_color,\n                             int *wk_returnValue )\n{\n    const int nWarps = CtaSize / 32; // Number of warps per Cta\n    const int warpId = utils::warp_id();\n    const int laneId = utils::lane_id();\n    int lane_mask_lt = utils::lane_mask_lt();\n\n    // Lane ID in the 2 16-wide segments.\n    const int lane_id_div_16 = laneId / 16;\n    const int lane_id_mod_16 = laneId % 16;\n    // Coordinates inside a 4x4 block of the matrix.\n    const int idx_i = lane_id_mod_16 / 4;\n    const int idx_j = lane_id_mod_16 % 4;\n    int globalWarpId = blockIdx.x * nWarps + warpId;\n    // Shared memory to store the blocks to process\n    __shared__ volatile ValueTypeA s_C_mtx[nWarps][32];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile int s_aColItToLoad[nWarps][32];\n    __shared__ volatile int s_waColItToLoad[nWarps][32];\n    // Shared memory to store the proposed column to load\n    __shared__ volatile unsigned s_aColIds[nWarps][32];\n    // The size of the hash table (one per warp - shared memory).\n    __shared__ volatile int s_size[nWarps][2];\n    // Shared memory to store the column indices of the current row\n    __shared__ volatile int s_keys[nWarps][SMemSize];\n\n    while (globalWarpId < num_rows_per_color)\n    {\n        int aRowId = sorted_rows_by_color[globalWarpId];\n        // Insert all the column indices in shared memory\n        // TODO: Use texture here\n        int aColBeg = A_row_offsets[aRowId];\n        int aColEnd = A_row_offsets[aRowId + 1];\n        int aColIt  = aColBeg;\n\n        // Check if number of nonzeros will fit in shared memory\n        if ( (aColEnd - aColBeg) > SMemSize )\n        {\n            wk_returnValue[0] = 1;\n            return;\n        }\n\n        // Load the all the column indices of row into shared memory\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n        {\n            int aColId = aColIt < aColEnd ? (int) A_col_indices[aColIt] : -1;\n            s_keys[warpId][aColIt - aColBeg] = aColId;\n        }\n\n        // Now load all column indices of neighbours that have colors smaller than yours\n        aColIt  = aColBeg;\n        int aColSmaller = A_smaller_color_offsets[aRowId];\n\n        for ( ; utils::any( (aColIt + laneId) < aColSmaller ) ; aColIt += 32 )\n        {\n            int aColId = (aColIt + laneId) < aColSmaller ? (int) A_col_indices[aColIt + laneId] : -1;\n            // Each thread pushes its column\n            s_aColIds[warpId][laneId] = aColId;\n\n            // Have warp collaborate to load each row\n            for ( int j = 0; j < 32; j++)\n            {\n                // Check if row to load is valid\n                if ( ( aColIt + j ) >= aColSmaller ) { break; }\n\n                // Read the row to process, should be a broadcast\n                int waRowId = s_aColIds[warpId][j];\n\n                // Compute multiplicative factor, load C_jj in first half, C_ij in second half\n                if (lane_id_div_16 == 0)\n                {\n                    s_C_mtx[warpId][laneId] = A_nonzero_values[ 16 * A_dia_indices[waRowId] + lane_id_mod_16 ];\n                }\n                else\n                {\n                    s_C_mtx[warpId][laneId] = A_nonzero_values[ 16 * (aColIt + j) + lane_id_mod_16 ];\n                }\n\n                // Threads 0-15 perform the matrix product\n                utils::syncwarp();\n                if (lane_id_div_16 == 0)\n                {\n                    ValueTypeA tmp(0);\n\n                    if (ROW_MAJOR)\n                    {\n#pragma unroll\n                        for ( int m = 0 ; m < 4 ; ++m )\n                        {\n                            tmp += s_C_mtx[warpId][16 + 4 * idx_i + m] * s_C_mtx[warpId][4 * m + idx_j];\n                        }\n                    }\n                    else\n                    {\n#pragma unroll\n                        for ( int m = 0 ; m < 4 ; ++m )\n                        {\n                            tmp += s_C_mtx[warpId][16 + 4 * m + idx_j] * s_C_mtx[warpId][4 * idx_i + m];\n                        }\n                    }\n\n                    s_C_mtx[warpId][laneId] = tmp;\n                    A_nonzero_values[16 * (aColIt + j) + laneId] = tmp;\n                }\n\n                int waColIt  = A_larger_color_offsets[waRowId];\n                int waColEnd = A_row_offsets[waRowId + 1];\n\n                //// Load the first 32 columns of waRowId\n                for (waColIt += laneId ; utils::any(waColIt < waColEnd ); waColIt += 32 )\n                {\n                    // Each thread loads its column id\n                    int waColId = waColIt < waColEnd ? A_col_indices[waColIt] : int (-1);\n                    // TODO: Try binary search if columns are ordered\n                    int found_aColIt = -1;\n\n                    //TODO: if invalid waColId, don't search\n                    for (int i = 0 ; utils::any(found_aColIt == -1) && i < aColEnd - aColBeg ; i++)\n                    {\n                        if (s_keys[warpId][i] == waColId) { found_aColIt = aColBeg + i; }\n                    }\n\n                    // Store all the columns that have been found\n                    const int pred = found_aColIt != -1;\n                    const int vote = utils::ballot( pred );\n                    const int idst = __popc(vote & lane_mask_lt);\n\n                    if (pred)\n                    {\n                        s_aColItToLoad [warpId][idst] = found_aColIt;\n                        s_waColItToLoad[warpId][idst] = waColIt;\n                    }\n\n                    const int n_cols = __popc( vote );\n\n                    // Process all columns that have been found\n                    for ( int k = 0 ; k < n_cols ; k++ )\n                    {\n                        // Load column to load\n                        const int a_col_it = k < n_cols ? s_aColItToLoad [warpId][k] : -1;\n                        const int w_col_it = k < n_cols ? s_waColItToLoad[warpId][k] : -1;\n\n                        if (lane_id_div_16 == 1)\n                        {\n                            s_C_mtx[warpId][laneId] = A_nonzero_values[16 * w_col_it + lane_id_mod_16];\n                            // Run the matrix-matrix product.\n                            ValueTypeA tmp(0);\n                            utils::syncwarp(utils::activemask());\n\n                            if (ROW_MAJOR)\n                            {\n\n#pragma unroll\n                                for ( int m = 0 ; m < 4 ; ++m )\n                                {\n                                    tmp += s_C_mtx[warpId][4 * idx_i + m] * s_C_mtx[warpId][16 + 4 * m + idx_j];\n                                }\n                            }\n                            else\n                            {\n\n#pragma unroll\n                                for ( int m = 0 ; m < 4 ; ++m )\n                                {\n                                    tmp += s_C_mtx[warpId][4 * m + idx_j] * s_C_mtx[warpId][16 + 4 * idx_i + m];\n                                }\n                            }\n\n                            A_nonzero_values[16 * a_col_it + lane_id_mod_16] -= tmp;\n                        }\n                    } // Loop over columns that have a match (for k=0;k<n_cols)\n                } // Loop over the columns of waRowId\n            }  // Loop j=0;j<32\n        } // Loop over the columns of aRowId\n\n        // TODO: Have one warp deal with two rows\n        // Now compute the inverse of the block C_jj\n        if (lane_id_div_16 == 0)\n        {\n            const int offset = 16 * A_dia_indices[aRowId] + lane_id_mod_16;\n            s_C_mtx[warpId][laneId] = A_nonzero_values[offset];\n            utils::syncwarp(utils::activemask());\n\n            if (ROW_MAJOR)\n            {\n                compute_block_inverse_row_major<int, ValueTypeA, 0, 4, 16>\n                (s_C_mtx[warpId], 0, offset, idx_i, idx_j, A_nonzero_values);\n            }\n            else\n            {\n                compute_block_inverse_col_major<int, ValueTypeA, 0, 4, 16>\n                (s_C_mtx[warpId], 0, offset, idx_i, idx_j, A_nonzero_values);\n            }\n        }\n\n        globalWarpId += nWarps * gridDim.x;\n    } // if RowId < A_nRows;\n}\n#endif\n// ----------\n// Methods\n// ----------\n\n// Constructor\ntemplate<class T_Config>\nMulticolorILUSolver_Base<T_Config>::MulticolorILUSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    m_sparsity_level = cfg.AMG_Config::template getParameter<int>(\"ilu_sparsity_level\", cfg_scope);\n    m_weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n\n    if (cfg.AMG_Config::template getParameter<int>(\"use_bsrxmv\", cfg_scope))\n    {\n        this->m_use_bsrxmv = 1;\n    }\n    else\n    {\n        this->m_use_bsrxmv = 0;\n    }\n\n    if (m_weight == ValueTypeB(0.))\n    {\n        m_weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor DILU smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorILUSolver_Base<T_Config>::~MulticolorILUSolver_Base()\n{\n    m_LU.set_initialized(0);\n    m_A_to_LU_mapping.clear();\n    m_A_to_LU_mapping.shrink_to_fit();\n    m_LU.resize(0, 0, 0, 1);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAtoLUmapping()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAtoLUmapping()\n{\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    const int CtaSize = 128; // Number of threads per CTA\n    const int SMemSize = 128;  // per warp\n    const int nWarps = CtaSize / 32;\n    int GridSize = std::min( AMGX_GRID_MAX_SIZE, ( this->m_explicit_A->get_num_rows( ) + nWarps - 1 ) / nWarps );\n    // Global memory workspaces\n    device_vector_alloc<int> returnValue(1);\n    returnValue[0] = 0;\n\n    if (this->m_explicit_A->hasProps(DIAG))\n    {\n        computeAtoLUmappingExtDiag_kernel<CtaSize, SMemSize> <<< GridSize, CtaSize >>> (\n            m_A.get_num_rows( ),\n            amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.diag[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n            amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n    }\n    else\n    {\n        computeAtoLUmapping_kernel<CtaSize, SMemSize> <<< GridSize, CtaSize >>> (\n            m_A.get_num_rows( ),\n            amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n            amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n            amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n    }\n\n    cudaCheckError();\n\n    // fallback path that allows 1024 nonzeros per row\n    if (returnValue[0] == 1)\n    {\n        returnValue[0] = 0;\n        const int SMemSize2 = 1024 ;  // per warp\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            computeAtoLUmappingExtDiag_kernel<CtaSize, SMemSize2> <<< GridSize, CtaSize >>> (\n                m_A.get_num_rows( ),\n                amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.diag[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n                amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n        }\n        else\n        {\n            computeAtoLUmapping_kernel<CtaSize, SMemSize2> <<< GridSize, CtaSize >>> (\n                m_A.get_num_rows( ),\n                amgx::thrust::raw_pointer_cast( &m_A.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &m_A.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                amgx::thrust::raw_pointer_cast( &this->m_A_to_LU_mapping[0] ),\n                amgx::thrust::raw_pointer_cast( &returnValue[0] ));\n        }\n\n        cudaCheckError();\n    }\n\n    if (returnValue[0] == 1)\n    {\n        FatalError( \"Number of nonzeros per row exceeds allocated shared memory\", AMGX_ERR_NO_MEMORY);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::fillLUValuesWithAValues()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::fillLUValuesWithAValues()\n{\n    if (this->m_sparsity_level == 0)\n    {\n        this->m_LU.values = this->m_explicit_A->values;\n    }\n    else\n    {\n        // TODO: Should probably store the inverse mapping of AtoLUmapping instead\n        //       This will allow to use unpermuteVector and have coalesced writes\n        //       instead of coalesced reads\n        thrust_wrapper::fill<AMGX_device>(this->m_LU.values.begin(), this->m_LU.values.end(), 0.);\n        cudaCheckError();\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            amgx::permuteVector(this->m_explicit_A->values, this->m_LU.values, this->m_A_to_LU_mapping, (this->m_explicit_A->get_num_nz() + this->m_explicit_A->get_num_rows())*this->m_explicit_A->get_block_size());\n        }\n        else\n        {\n            amgx::permuteVector(this->m_explicit_A->values, this->m_LU.values, this->m_A_to_LU_mapping, this->m_explicit_A->get_num_nz()*this->m_explicit_A->get_block_size());\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeLUSparsityPattern()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeLUSparsityPattern()\n{\n    // ILU0\n    if (this->m_sparsity_level == 0)\n    {\n        // Copy everything except the values\n        this->m_LU.copy_structure(*this->m_explicit_A);\n    }\n    // ILU1\n    else if (this->m_sparsity_level == 1)\n    {\n        this->sparsity_wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *this->m_cfg, \"default\" );\n        CSR_Multiply<TConfig_d>::csr_sparsity_ilu1( *this->m_explicit_A, this->m_LU, this->sparsity_wk );\n        CSR_Multiply<TConfig_d>::csr_workspace_delete( this->sparsity_wk );\n\n        if (this->m_use_bsrxmv)\n        {\n            this->m_LU.set_initialized(0);\n            this->m_LU.computeDiagonal();\n            this->m_LU.set_initialized(1);\n        }\n\n        this->m_LU.setMatrixColoring(&(this->m_explicit_A->getMatrixColoring()));\n    }\n    else\n    {\n        FatalError(\"Haven't implemented Multicolor ILU smoother for this sparsity level. \", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeLUFactors()\n{\n    FatalError(\"Haven't implemented Multicolor ILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeLUFactors()\n{\n    const int CtaSize = 128; // Number of threads per CTA\n    const int SMemSize = 128;\n    const int nWarps = CtaSize / 32;\n    device_vector_alloc<int> returnValue(1);\n    returnValue[0] = 0;\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n    const IndexType *LU_sorted_rows_by_color_ptr = this->m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = this->m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = this->m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_FACTORS\n        int GridSize = std::min( 2048, ( num_rows_per_color + nWarps - 1 ) / nWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4 )\n        {\n            if ( this->m_explicit_A->getBlockFormat() == ROW_MAJOR )\n            {\n                compute_LU_factors_4x4_kernel_warp<ValueTypeA, CtaSize, SMemSize, true> <<< GridSize, CtaSize>>>(\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n            else\n            {\n                compute_LU_factors_4x4_kernel_warp<ValueTypeA, CtaSize, SMemSize, false> <<< GridSize, CtaSize>>>(\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"Unsupported block size for Multicolor ILU solver, computeLUFactors\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n#else\n        int GridSize = std::min( AMGX_GRID_MAX_SIZE, ( num_rows_per_color + nWarps - 1 ) / nWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4 )\n        {\n            //computeLUFactors_4x4_kernel<ValueTypeA,CtaSize,SMemSize> <<< GridSize, CtaSize>>> (\n            if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n            {\n                computeLUFactors_4x4_kernel<ValueTypeA, CtaSize, SMemSize, true> <<< GridSize, CtaSize>>> (\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n            else\n            {\n                computeLUFactors_4x4_kernel<ValueTypeA, CtaSize, SMemSize, false> <<< GridSize, CtaSize>>> (\n                    this->m_LU.get_num_rows( ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.row_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.col_indices[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.diag[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.values[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_smaller_color_offsets[0] ),\n                    amgx::thrust::raw_pointer_cast( &this->m_LU.m_larger_color_offsets[0] ),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    amgx::thrust::raw_pointer_cast( &returnValue[0] ) );\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"Unsupported block size for Multicolor ILU solver, computeLUFactors\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n#endif\n    }\n\n    // Check returnValue flag\n    if ( returnValue[0] == 1 )\n    {\n        FatalError( \"Number of nonzeros per row exceeds allocated shared memory\", AMGX_ERR_NO_MEMORY);\n    }\n}\n\n\n// Solver pre-setup\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::pre_setup()\n{\n    // Check if matrix is colored\n    if (this->m_explicit_A->getColoringLevel() < m_sparsity_level + 1)\n    {\n        FatalError(\"Matrix must be colored with coloring_level > sparsity_level for the multicolorILUsolver\", AMGX_ERR_CONFIGURATION);\n    }\n\n    // Compute extended sparsity pattern based on coloring and matrix A\n    computeLUSparsityPattern();\n\n    if (this->m_LU.hasProps(DIAG))\n    {\n        FatalError(\"Multicolor ILU smoother does not support outside diagonal. Try setting reorder_cols_by_color=1 and insert_diag_while_reordering=1 in the multicolor_ilu solver scope in configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (m_sparsity_level == 0 && !this->m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"Multicolor ILU smoother requires matrix to be reordered by color with ILU0 solver. Try setting reorder_cols_by_color=1 and insert_diag_while_reordering=1 in the multicolor_ilu solver scope in configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Reorder the columns of LU by color\n    if (m_sparsity_level != 0)\n    {\n        // Reorder columns of LU by color\n        m_LU.reorderColumnsByColor(false);\n\n        // Compute mapping between entries in A and entries in LU\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            m_A_to_LU_mapping.resize(this->m_explicit_A->get_num_nz() + this->m_explicit_A->get_num_rows());\n        }\n        else\n        {\n            m_A_to_LU_mapping.resize(this->m_explicit_A->get_num_nz());\n        }\n\n        computeAtoLUmapping();\n    }\n\n    int N = this->m_LU.get_num_rows() * this->m_LU.get_block_dimy();\n    m_delta.resize(N);\n    m_Delta.resize(N);\n    m_Delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_Delta.set_block_dimx(1);\n    m_delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_delta.set_block_dimx(1);\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor = \" << this->m_weight << std::endl;\n    std::cout << \"use_bsrxmv = \" << this->m_use_bsrxmv << std::endl;\n    std::cout << \"ilu_sparsity_level = \" << this->m_sparsity_level <<  std::endl;\n}\n\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    this->m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorILUSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    if (this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor ilu solver. Try setting: coloring_level=1 or coloring_level=2 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (!reuse_matrix_structure)\n    {\n        this->pre_setup();\n    }\n\n    // Fill LU sparsity pattern\n    fillLUValuesWithAValues();\n    // Compute LU factors in place (update LU.values)\n    computeLUFactors();\n}\n\n//\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorILUSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if ( !m_use_bsrxmv && (this->m_LU.get_block_dimx() == 4 && this->m_LU.get_block_dimy() == 4) )\n    {\n        smooth_4x4(b, x, xIsZero);\n    }\n    else\n    {\n        smooth_bxb(b, x, xIsZero);\n    }\n\n    // Do we converge ?\n    return this->converged(b, x);\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorILUSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const VVector &b, VVector &x, bool xIsZero)\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_4x4(const VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<TConfig_d> &m_LU = this->m_LU;\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    int N = m_LU.get_num_rows() * m_LU.get_block_dimy();\n    cudaCheckError();\n\n    if (!m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"ILU solver currently only works if columns are reordered by color. Try setting reordering_cols_by_color=1 in the multicolor_ilu solver scope in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // ---------------------------------------------------------\n    // Solving Lower triangular system, with identity diagonal\n    // ---------------------------------------------------------\n    const IndexType *LU_sorted_rows_by_color_ptr = m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_FORWARD\n        const int CtaSize = 128; // Number of threads per CTA\n        const int nHalfWarps = CtaSize / 16;\n        int GridSize = std::min( 2048, ( num_rows_per_color + nHalfWarps - 1 ) / nHalfWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if ( this->m_explicit_A->getBlockFormat() == ROW_MAJOR )\n        {\n            if (m_A.hasProps(DIAG))\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, true, true> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n            else\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, true, false> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n        }\n        else\n        {\n            // COL_MAJOR\n            if (m_A.hasProps(DIAG))\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, false, true> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n            else\n            {\n                LU_forward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, 4, false, false> <<< GridSize, CtaSize>>>(\n                    m_LU.row_offsets.raw(),\n                    m_LU.m_smaller_color_offsets.raw(),\n                    m_LU.col_indices.raw(),\n                    m_LU.values.raw(),\n                    m_A.row_offsets.raw(),\n                    m_A.col_indices.raw(),\n                    m_A.values.raw(),\n                    m_A.diag.raw(),\n                    x.raw(),\n                    b.raw(),\n                    this->m_delta.raw(),\n                    LU_sorted_rows_by_color_ptr + color_offset,\n                    num_rows_per_color,\n                    i,\n                    xIsZero );\n            }\n        }\n\n#else\n        const int CtaSize = 128;\n        const int blockrows_per_cta = CtaSize / 4;\n        const int GridSize = min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color + blockrows_per_cta - 1) / blockrows_per_cta);\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->hasProps(DIAG))\n        {\n            FatalError(\"this implementation of LU forward solve does not support A with external diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_forward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, true> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_smaller_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.values.raw(),\n             m_A.row_offsets.raw(),\n             m_A.col_indices.raw(),\n             m_A.values.raw(),\n             x.raw(),\n             b.raw(),\n             delta.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             xIsZero);\n        }\n        else\n        {\n            LU_forward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, false> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_smaller_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.values.raw(),\n             m_A.row_offsets.raw(),\n             m_A.col_indices.raw(),\n             m_A.values.raw(),\n             x.raw(),\n             b.raw(),\n             delta.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             xIsZero);\n        }\n\n#endif\n        cudaCheckError();\n    }\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for (int i = num_colors - 1; i >= 0; i--)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n#ifdef EXPERIMENTAL_LU_BACKWARD\n        const int CtaSize = 128; // Number of threads per CTA\n        const int nHalfWarps = CtaSize / 16;\n        int GridSize = std::min( 2048, ( num_rows_per_color + nHalfWarps - 1 ) / nHalfWarps );\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_backward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, true> <<< GridSize, CtaSize>>>(\n                m_LU.row_offsets.raw(),\n                m_LU.m_larger_color_offsets.raw(),\n                m_LU.col_indices.raw(),\n                m_LU.diag.raw(),\n                m_LU.values.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                x.raw(),\n                LU_sorted_rows_by_color_ptr + color_offset,\n                num_rows_per_color,\n                i,\n                num_colors,\n                this->m_weight,\n                xIsZero);\n        }\n        else\n        {\n            LU_backward_4x4_kernel_warp<IndexType, ValueTypeA, ValueTypeB, CtaSize, false> <<< GridSize, CtaSize>>>(\n                m_LU.row_offsets.raw(),\n                m_LU.m_larger_color_offsets.raw(),\n                m_LU.col_indices.raw(),\n                m_LU.diag.raw(),\n                m_LU.values.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                x.raw(),\n                LU_sorted_rows_by_color_ptr + color_offset,\n                num_rows_per_color,\n                i,\n                num_colors,\n                this->m_weight,\n                xIsZero);\n        }\n\n#else\n        const int CtaSize = 128;\n        const int blockrows_per_cta = CtaSize / 4;\n        const int GridSize = min( AMGX_GRID_MAX_SIZE, (int) (num_rows_per_color + blockrows_per_cta - 1) / blockrows_per_cta);\n\n        if ( GridSize == 0 )\n        {\n            continue;    // if perfect coloring (color 0 has no vertices)\n        }\n\n        if (this->m_explicit_A->getBlockFormat() == ROW_MAJOR)\n        {\n            LU_backward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, true> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_larger_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.diag.raw(),\n             m_LU.values.raw(),\n             this->m_delta.raw(),\n             this->m_Delta.raw(),\n             x.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             num_colors,\n             this->m_weight,\n             xIsZero);\n        }\n        else\n        {\n            LU_backward_4x4_kernel<IndexType, ValueTypeA, ValueTypeB, blockrows_per_cta, 8, 4, false> <<< GridSize, CtaSize>>>\n            (m_LU.row_offsets.raw(),\n             m_LU.m_larger_color_offsets.raw(),\n             m_LU.col_indices.raw(),\n             m_LU.diag.raw(),\n             m_LU.values.raw(),\n             this->m_delta.raw(),\n             this->m_Delta.raw(),\n             x.raw(),\n             LU_sorted_rows_by_color_ptr + color_offset,\n             num_rows_per_color,\n             i,\n             num_colors,\n             this->m_weight,\n             xIsZero);\n        }\n\n#endif\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_bxb(const VVector &b, VVector &x, bool xIsZero)\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MulticolorILUSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_bxb(const VVector &b, VVector &x, bool xIsZero)\n{\n    Matrix<TConfig_d> &m_LU = this->m_LU;\n    Matrix<TConfig_d> &m_A = *this->m_explicit_A;\n    int N = m_LU.get_num_rows() * m_LU.get_block_dimy();\n\n    if (!m_LU.getColsReorderedByColor())\n    {\n        FatalError(\"ILU solver currently only works if columns are reordered by color. Try setting reorder_cols_by_color=1 in the multicolor_ilu solver scope in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (this->m_explicit_A->getBlockFormat() == COL_MAJOR)\n    {\n        FatalError(\"ILU solver for arbitrary block sizes only works with ROW_MAJOR matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // ---------------------------------------------------------\n    // Solving Lower triangular system, with identity diagonal\n    // ---------------------------------------------------------\n    const IndexType *LU_sorted_rows_by_color_ptr = m_LU.getMatrixColoring().getSortedRowsByColor().raw();\n    int num_colors = this->m_LU.getMatrixColoring().getNumColors();\n    //delta = b;\n    amgx::thrust::copy(b.begin(), b.end(), this->m_delta.begin());\n    //delta = delta - Ax;\n    Cusparse::bsrmv((ValueTypeA) - 1.0, m_A, x, (ValueTypeA)1.0, this->m_delta);\n    cudaCheckError();\n    // Setting Delta to zero\n    thrust_wrapper::fill<AMGX_device>(this->m_Delta.begin(), this->m_Delta.end(), (ValueTypeB)0.0f);\n    cudaCheckError();\n    bool skipped_end = false;\n\n    for (int i = 0; i < num_colors; i++)\n    {\n        const IndexType color_offset = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const IndexType num_rows_per_color = m_LU.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if (num_rows_per_color == 0) { continue; } // if perfect coloring (color 0 has no vertices)\n\n        if (skipped_end)\n        {\n            // delta = delta - LU*Delta smaller colors\n            Cusparse::bsrmv(Cusparse::SMALLER_COLORS, i, (ValueTypeA) - 1.0f, m_LU, this->m_delta, (ValueTypeA)1.0f, this->m_delta);\n        }\n\n        if (num_rows_per_color > 0)\n        {\n            skipped_end = true;\n        }\n    }\n\n    cudaCheckError();\n    skipped_end = false;\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for (int i = num_colors - 1; i >= 0; i--)\n    {\n        // delta = delta - LU*Delta larger colors\n        Cusparse::bsrmv(Cusparse::LARGER_COLORS, i, (ValueTypeA) - 1.0f, m_LU, this->m_Delta, (ValueTypeA)1.0f, this->m_delta);\n        // Multiple by inverse stored on diagonal\n        Cusparse::bsrmv(Cusparse::DIAG_COL, i, (ValueTypeA) 1.0f, m_LU, this->m_delta, 0.0f, this->m_Delta);\n    }\n\n    cudaCheckError();\n    axpy(this->m_Delta, x, this->m_weight, 0, x.size());\n    cudaCheckError();\n}\n\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorILUSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorILUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/multicolor_ilu_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_dilu_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <matrix_io.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n\n#include <amgx_types/util.h>\n\n#include <algorithm>\n\n#define AMGX_ILU_COLORING\n\nnamespace amgx\n{\nnamespace multicolor_dilu_solver\n{\n\nenum { CTA_SIZE = 128, WARP_SIZE = 32 };\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_setup_NxN_kernel_large( const int *__restrict A_rows,\n                                  const int *__restrict A_cols,\n                                  const int *__restrict A_diag,\n                                  const Matrix_type *__restrict A_vals,\n                                  Matrix_type *__restrict Einv,\n                                  const int *sorted_rows_by_color,\n                                  const int *row_colors,\n                                  const int  num_rows_per_color,\n                                  const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Shared memory to broadcast column IDs.\n    __shared__ volatile int s_a_col_ids[CTA_SIZE];\n    __shared__ volatile int s_a_col_its[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_a_col_ids = &s_a_col_ids[threadIdx.x - lane_id];\n    volatile int *my_s_a_col_its = &s_a_col_its[threadIdx.x - lane_id];\n    // Shared memory to store the matrices.\n    __shared__ volatile Vector_type s_A_mtx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    __shared__ volatile Vector_type s_B_mtx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_A_mtx = &s_A_mtx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    volatile Vector_type *my_s_B_mtx = &s_B_mtx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Shared memory to store the index of the element Aji.\n    __shared__ volatile int s_A_ji[NUM_WARPS_PER_CTA];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_A_ji = &s_A_ji[warp_id];\n    // Precomputing some stuff\n    int idx[NUM_WARP_ITERS_PER_BLOCK];\n    int idy[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n    {\n        const int id = (WARP_SIZE * wb + lane_id) % NxN;\n        idx[wb] = id / N;\n        idy[wb] = id % N;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; utils::any( a_row_it < num_rows_per_color ) ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = -1;\n\n        if ( a_row_it < num_rows_per_color )\n        {\n            a_row_id = sorted_rows_by_color[a_row_it];\n        }\n\n        // Load the diagonal.\n        Vector_type e_out[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n        {\n            e_out[wb] = (Vector_type)0.0;\n        }\n\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n            if ( a_row_id != -1 && (wb * WARP_SIZE + lane_id) < NxN)\n            {\n                e_out[wb] = A_vals[NxN * A_diag[a_row_id] + wb * WARP_SIZE + lane_id];\n            }\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the rows.\n            int a_col_begin(0), a_col_end(0);\n\n            if ( a_row_id != -1 )\n            {\n                a_col_begin = A_rows[a_row_id  ];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += NxN )\n            {\n                // Each thread loads a single element. If !is_active, a_col_end == 0.\n                int a_col_it = a_col_begin + lane_id;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1 );\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                my_s_a_col_its[dest] = a_col_it;\n                // Temporary storage with zeros for OOB\n                Vector_type my_A[NUM_WARP_ITERS_PER_BLOCK], my_B[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n                for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                {\n                    my_A[wb] = (Vector_type)0.0;\n                    my_B[wb] = (Vector_type)0.0;\n                }\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < WARP_SIZE ; ++k )\n                {\n                    // Exchange column indices.\n                    const int uniform_a_col_id = my_s_a_col_ids[k];\n\n                    // Early exit.\n                    if ( uniform_a_col_id == -1 )\n                    {\n                        break;\n                    }\n\n                    // Load the iterator.\n                    const int uniform_a_col_it = my_s_a_col_its[k];\n                    // Load the two matrices.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                        if ((wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_A[wb] = A_vals[NxN * uniform_a_col_it + wb * WARP_SIZE + lane_id];\n                            my_B[wb] = Einv  [NxN * uniform_a_col_id + wb * WARP_SIZE + lane_id];\n                        }\n\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_s_A_mtx[lane_id + wb * WARP_SIZE] = my_A[wb];\n                        my_s_B_mtx[lane_id + wb * WARP_SIZE] = my_B[wb];\n                    }\n\n                    // Compute the product of matrices.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_A[wb] = (Vector_type)0.0;\n#pragma unroll\n\n                        for ( int m = 0 ; m < N ; ++m )\n                        {\n                            my_A[wb] += my_s_A_mtx[N * idx[wb] + m] * my_s_B_mtx[N * m + idy[wb]];\n                        }\n                    }\n\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                        if ((wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_s_A_mtx[lane_id + wb * WARP_SIZE] = my_A[wb];\n                        }\n\n                    // We looking for columns in the two rows we're interested in.\n                    int b_col_it  = A_rows[uniform_a_col_id  ];\n                    int b_col_end = A_rows[uniform_a_col_id + 1];\n\n                    // Init the marker to -1.\n                    if ( lane_id == 0 )\n                    {\n                        *my_s_A_ji = -1;\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id;\n                    int shared_found = utils::ballot( lane_id == 0 && uniform_a_col_id == -1 );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            *my_s_A_ji = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found);\n                        b_col_it += NxN;\n                    }\n                    while ( __popc( shared_found ) == 0 && utils::any( b_col_it < b_col_end ) );\n\n                    // Load the blocks.\n                    const int w_aji = *my_s_A_ji;\n                    Vector_type my_C[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_C[wb] = (Vector_type)0.0;\n\n                        if ( w_aji != -1 && (wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_C[wb] = A_vals[NxN * w_aji + wb * WARP_SIZE + lane_id];\n                        }\n\n                        my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_C[wb];\n                    }\n\n                    // Update e_out.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n#pragma unroll\n\n                        for ( int m = 0 ; m < N ; ++m )\n                        {\n                            e_out[wb] -= my_s_A_mtx[N * idx[wb] + m] * my_s_B_mtx[N * m + idy[wb]];\n                        }\n                    }\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Store e_out in A\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n        {\n            my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id] = e_out[wb];\n        }\n\n        // Invert the matrices.\n#pragma unroll\n\n        for ( int row = 0 ; row < N ; ++row )\n        {\n            Vector_type diag(0), diag_tmp = my_s_A_mtx[N * row + row];\n\n            if ( isNotCloseToZero(diag_tmp) )\n            {\n                diag = Vector_type(1) / diag_tmp;\n            }\n            else\n            {\n                diag = Vector_type(1) / epsilon(diag_tmp);\n            }\n\n            if ( lane_id < N && lane_id != row)\n            {\n                my_s_A_mtx[N * row + lane_id] = my_s_B_mtx[N * row + lane_id] = my_s_B_mtx[N * row + lane_id] * diag;\n            }\n\n#pragma unroll\n\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                if ( idx[wb] != row && idy[wb] != row)\n                {\n                    my_s_A_mtx[wb * WARP_SIZE + lane_id] = my_s_B_mtx[wb * WARP_SIZE + lane_id] - my_s_B_mtx[N * idx[wb] + row] * my_s_B_mtx[N * row + idy[wb]];\n                }\n\n            if ( lane_id < N )\n            {\n                Vector_type tmp = diag;\n\n                if ( lane_id != row )\n                {\n                    tmp = -my_s_A_mtx[N * lane_id + row] * diag;\n                }\n\n                my_s_A_mtx[N * lane_id + row] = tmp;\n            }\n\n#pragma unroll\n\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n            {\n                my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id];\n            }\n        }\n\n        // Store the results to Einv.\n        if ( a_row_id != -1 )\n#pragma unroll\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                if (wb * WARP_SIZE + lane_id < NxN)\n                {\n                    Einv[NxN * a_row_id + wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id];\n                }\n    }\n}\n\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_setup_NxN_kernel( const int *__restrict A_rows,\n                            const int *__restrict A_cols,\n                            const int *__restrict A_diag,\n                            const Matrix_type *__restrict A_vals,\n                            Matrix_type *__restrict Einv,\n                            const int *sorted_rows_by_color,\n                            const int *row_colors,\n                            const int  num_rows_per_color,\n                            const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Upper-bound on the number of items per warp.\n    const int NUM_ITEMS_PER_WARP_CEIL = (WARP_SIZE + NxN - 1) / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We need NxN to compute a NxN block. Encode a mask for the first block.\n    int mask_tmp = utils::ballot( lane_id_div_NxN == 0 );\n    // Mask for ballots. We shift the mask with NxN active bits by the needed number of bits.\n    const int mask_NxN = mask_tmp << (lane_id_div_NxN * __popc(mask_tmp));\n    // Shared memory to broadcast column IDs.\n    __shared__ volatile int s_a_col_ids[CTA_SIZE];\n    __shared__ volatile int s_a_col_its[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_a_col_ids = &s_a_col_ids[threadIdx.x - lane_id_mod_NxN];\n    volatile int *my_s_a_col_its = &s_a_col_its[threadIdx.x - lane_id_mod_NxN];\n    // Shared memory to store the matrices.\n    __shared__ volatile Vector_type s_A_mtx[CTA_SIZE];\n    __shared__ volatile Vector_type s_B_mtx[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_A_mtx = &s_A_mtx[threadIdx.x - lane_id_mod_NxN];\n    volatile Vector_type *my_s_B_mtx = &s_B_mtx[threadIdx.x - lane_id_mod_NxN];\n    // Shared memory to store the index of the element Aji.\n    __shared__ volatile int s_A_ji[NUM_WARPS_PER_CTA * NUM_ITEMS_PER_WARP_CEIL];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_A_ji = &s_A_ji[warp_id * NUM_ITEMS_PER_WARP_CEIL + lane_id_div_NxN];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; utils::any( a_row_it < num_rows_per_color ) ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n        // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n        // to false ; that's the common trick to determine if a number is a power of 2).\n        int is_active = true;\n\n        if ( NxN & (NxN - 1) )\n        {\n            is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n        }\n\n        int a_row_id = -1;\n\n        if ( is_active && a_row_it < num_rows_per_color )\n        {\n            a_row_id = sorted_rows_by_color[a_row_it];\n        }\n\n        // Load the diagonal.\n        Vector_type e_out(0);\n\n        if ( a_row_id != -1 )\n        {\n            e_out = A_vals[NxN * A_diag[a_row_id] + lane_id_mod_NxN];\n        }\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the rows.\n            int a_col_begin(0), a_col_end(0);\n\n            if ( a_row_id != -1 )\n            {\n                a_col_begin = A_rows[a_row_id  ];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += NxN )\n            {\n                unsigned int active_mask = utils::activemask();\n                // Each thread loads a single element. If !is_active, a_col_end == 0.\n                int a_col_it = a_col_begin + lane_id_mod_NxN;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1, active_mask ) & mask_NxN;\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id_mod_NxN - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                my_s_a_col_its[dest] = a_col_it;\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    // Exchange column indices.\n                    const int uniform_a_col_id = my_s_a_col_ids[k];\n\n                    // Early exit.\n                    if ( utils::all( uniform_a_col_id == -1, active_mask ) )\n                    {\n                        break;\n                    }\n\n                    // Load the iterator.\n                    const int uniform_a_col_it = my_s_a_col_its[k];\n                    // Load the two matrices.\n                    Vector_type my_A(0), my_B(0);\n\n                    if ( uniform_a_col_id != -1 )\n                    {\n                        my_A = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                        my_B = Einv  [NxN * uniform_a_col_id + lane_id_mod_NxN];\n                    }\n\n                    my_s_A_mtx[lane_id_mod_NxN] = my_A;\n                    my_s_B_mtx[lane_id_mod_NxN] = my_B;\n                    utils::syncwarp(active_mask);\n                    // Compute the product of matrices.\n                    Vector_type tmp(0);\n#pragma unroll\n\n                    for ( int m = 0 ; m < N ; ++m )\n                    {\n                        tmp += my_s_A_mtx[N * lane_id_mod_NxN_div_N + m] * my_s_B_mtx[N * m + lane_id_mod_NxN_mod_N];\n                    }\n\n                    my_s_A_mtx[lane_id_mod_NxN] = tmp;\n                    // We looking for columns in the two rows we're interested in.\n                    int b_col_it(0), b_col_end(0);\n\n                    if ( is_active && uniform_a_col_id != -1 )\n                    {\n                        b_col_it  = A_rows[uniform_a_col_id  ];\n                        b_col_end = A_rows[uniform_a_col_id + 1];\n                    }\n\n                    // Init the marker to -1.\n                    if ( lane_id_mod_NxN == 0 )\n                    {\n                        *my_s_A_ji = -1;\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id_mod_NxN;\n                    int shared_found = utils::ballot( lane_id_mod_NxN == 0 && uniform_a_col_id == -1, active_mask );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            *my_s_A_ji = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found, active_mask);\n                        b_col_it += NxN;\n                    }\n                    while ( __popc( shared_found ) < NUM_ITEMS_PER_WARP && utils::any( b_col_it < b_col_end, active_mask ) );\n\n                    // Load the blocks.\n                    const int w_aji = *my_s_A_ji;\n                    Vector_type my_C(0);\n\n                    if ( w_aji != -1 )\n                    {\n                        my_C = A_vals[NxN * w_aji + lane_id_mod_NxN];\n                    }\n\n                    my_s_B_mtx[lane_id_mod_NxN] = my_C;\n                    // Update e_out.\n#pragma unroll\n\n                    for ( int m = 0 ; m < N ; ++m )\n                    {\n                        e_out -= my_s_A_mtx[N * lane_id_mod_NxN_div_N + m] * my_s_B_mtx[N * m + lane_id_mod_NxN_mod_N];\n                    }\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Store e_out in A\n        my_s_A_mtx[lane_id_mod_NxN] = e_out;\n        utils::syncwarp();\n\n        // Invert the matrices.\n#pragma unroll\n\n        for ( int row = 0 ; row < N ; ++row )\n        {\n            Vector_type diag(0), diag_tmp = my_s_A_mtx[N * row + row];\n\n            if ( isNotCloseToZero(diag_tmp) )\n            {\n                diag = Vector_type(1) / diag_tmp;\n            }\n            else\n            {\n                diag = Vector_type(1) / epsilon(diag_tmp);\n            }\n\n            if ( is_active && lane_id_mod_NxN_div_N == 0 && lane_id_mod_NxN_mod_N != row )\n            {\n                my_s_A_mtx[N * row + lane_id_mod_NxN_mod_N] *= diag;\n            }\n            utils::syncwarp();\n\n            if ( is_active && lane_id_mod_NxN_div_N != row && lane_id_mod_NxN_mod_N != row )\n            {\n                my_s_A_mtx[lane_id_mod_NxN] -= my_s_A_mtx[N * lane_id_mod_NxN_div_N + row] * my_s_A_mtx[N * row + lane_id_mod_NxN_mod_N];\n            }\n            utils::syncwarp();\n\n            if ( is_active && lane_id_mod_NxN_div_N == 0 )\n            {\n                Vector_type tmp = diag;\n\n                if ( lane_id_mod_NxN_mod_N != row )\n                {\n                    tmp = -my_s_A_mtx[N * lane_id_mod_NxN_mod_N + row] * diag;\n                }\n\n                my_s_A_mtx[N * lane_id_mod_NxN_mod_N + row] = tmp;\n            }\n            utils::syncwarp();\n        }\n\n        // Store the results to Einv.\n        if ( a_row_id != -1 )\n        {\n            Einv[NxN * a_row_id + lane_id_mod_NxN] = my_s_A_mtx[lane_id_mod_NxN];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_setup_1x1_kernel( const int *__restrict A_rows,\n                            const int *__restrict A_cols,\n                            const int *__restrict A_diag,\n                            const Matrix_type *__restrict A_vals,\n                            Matrix_type *__restrict Einv,\n                            const int *sorted_rows_by_color,\n                            const int *row_colors,\n                            const int  num_rows_per_color,\n                            const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per grid.\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NTPR = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Shared memory to broadcast column IDs.\n    __shared__ int s_a_col_ids[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    int *my_s_a_col_ids = &s_a_col_ids[warp_id * WARP_SIZE];\n    // Shared memory to store the matrices.\n    __shared__ int s_A_ji[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    int *my_s_A_ji = &s_A_ji[warp_id * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_WARPS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load the diagonal.\n        Vector_type e_out(0);\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the row.\n            int a_col_begin = A_rows[a_row_id  ];\n            int a_col_end   = A_rows[a_row_id + 1];\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n            {\n                // Each thread loads a single element.\n                int a_col_it = a_col_begin + lane_id;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1 );\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                // Reset A_jis.\n                my_s_A_ji[lane_id] = -1;\n\n                __syncwarp();\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < ones ; k += WARP_SIZE / NUM_THREADS_PER_ROW )\n                {\n                    const int local_k = k + lane_id_div_NTPR;\n                    // Exchange column indices.\n                    int uniform_a_col_id = -1;\n\n                    if ( local_k < ones )\n                    {\n                        uniform_a_col_id = my_s_a_col_ids[local_k];\n                    }\n\n                    // We look for columns in the rows we're interested in.\n                    int b_col_it(0), b_col_end(0);\n\n                    if ( uniform_a_col_id != -1 )\n                    {\n                        b_col_it  = A_rows[uniform_a_col_id  ];\n                        b_col_end = A_rows[uniform_a_col_id + 1];\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id_mod_NTPR;\n                    int shared_found = utils::ballot( lane_id_mod_NTPR == 0 && uniform_a_col_id == -1 );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            my_s_A_ji[local_k] = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found);\n                        b_col_it += NUM_THREADS_PER_ROW;\n                    }\n                    while ( __popc( shared_found ) < WARP_SIZE / NUM_THREADS_PER_ROW && utils::any( b_col_it < b_col_end ) );\n                }\n\n                __syncwarp();\n\n                // Where to get my A_ji from (if any).\n                int a_ji_it = my_s_A_ji[dest];\n                // Grab A_jis.\n                Matrix_type a_ji(0);\n\n                if ( a_ji_it != -1 )\n                {\n                    a_ji = A_vals[a_ji_it];\n                }\n\n                // Update e_out.\n                if ( a_col_id != -1 )\n                {\n                    e_out += a_ji * Einv[a_col_id] * A_vals[a_col_it];\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Reduce the e_outs in one value.\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            e_out += utils::shfl_xor( e_out, mask );\n        }\n\n        // Store the result.\n        if ( lane_id == 0 )\n        {\n            Matrix_type res = A_vals[A_diag[a_row_id]] - e_out;\n\n            if ( res != Matrix_type(0) )\n            {\n                res = Matrix_type(1) / res;\n            }\n\n            Einv[a_row_id] = static_cast<Vector_type>(res);\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int N, bool ROW_MAJOR, int WARP_SIZE, typename Value_type >\nstatic __device__ __forceinline__\nValue_type reduce_distributed_vectors( Value_type x, int is_leader )\n{\n    if ( N & (N - 1) )\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; ++i )\n        {\n            Value_type other_x = utils::shfl_down( x, ROW_MAJOR ? i : N * i );\n\n            if ( is_leader )\n            {\n                x += other_x;\n            }\n        }\n    }\n    else\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; i <<= 1 )\n        {\n            x += utils::shfl_xor( x, ROW_MAJOR ? i : N * i );\n        }\n    }\n\n    return x;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_NxN_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_rows_per_color;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id_mod_NxN_div_N]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_bmAx = b[N * a_row_id + lane_id_mod_NxN_mod_N];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_NxN_mod_N]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[N * uniform_a_col_id + lane_id_mod_NxN_mod_N];\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && is_active && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv = Einv[NxN * a_row_id + lane_id_mod_NxN];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                delta[N * a_row_id + lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                delta[N * a_row_id + lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n    }\n}\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, bool HAS_EXTERNAL_DIAG, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_NxN_kernel_large( const int *__restrict A_rows,\n                                    const int *__restrict A_cols,\n                                    const Matrix_type *__restrict A_vals,\n                                    const int *__restrict A_diag,\n                                    const Vector_type *x,\n                                    const Vector_type *b,\n                                    Vector_type *__restrict delta,\n                                    const int *__restrict sorted_rows_by_color,\n                                    const int num_rows_per_color,\n                                    const int current_color,\n                                    const int *__restrict row_colors,\n                                    const Matrix_type *__restrict Einv,\n                                    const ColoringType boundary_coloring,\n                                    const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of rows computed per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of rows? per grid.\n    const int NUM_ITEMS_PER_GRID = CTA_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    // Useful index to compute matrix products.\n    const int lane_id_div_N = lane_id / N;\n    const int lane_id_mod_N = lane_id % N; // id of a lane inside the block\n    const int blocks_per_warp = WARP_SIZE / N; // we process this cols per warp per row\n    const int row_elems_per_warp  = blocks_per_warp * N;\n    // Shared to store bmAx\n    __shared__ volatile Vector_type bmAx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    volatile Vector_type *my_bmAx_s = &bmAx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( lane_id < N )\n        {\n            my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id]);\n        }\n\n#pragma unroll\n\n        for (int i = 0; i < NUM_WARP_ITERS_PER_BLOCK; i++)\n        {\n            my_bmAx_s[WARP_SIZE * i + lane_id] = 0.0;\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += WARP_SIZE ) // NxN\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n\n            // Loop over columns. We compute blocks_per_warp columns per iteration.\n            for ( int k = 0 ; k < WARP_SIZE ; k += blocks_per_warp )\n            {\n                // id of the processed block by this thread\n                int my_k = k + lane_id_div_N;\n                // Load N blocks of X (if valid)\n                int uniform_a_col_id = utils::shfl( a_col_id, my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && lane_id < row_elems_per_warp)\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_N]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid && lane_id < row_elems_per_warp)\n                {\n                    my_x += delta[N * uniform_a_col_id + lane_id_mod_N];\n                }\n\n                //my_s_mem[lane_id] = my_x;\n#pragma unroll\n\n                for ( int i = 0 ; i < blocks_per_warp ; ++i )\n                {\n                    // k-th batch of blocks, i-th block. each thread process a column/row of a_it = uniform_a_col_tmp\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    // check if we are going out of bounds/color\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    // swipe with the whole warp\n                    if (uniform_a_col_it != -1)\n                    {\n                        int block_inside_id = lane_id;\n#pragma unroll\n\n                        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n                        {\n                            Matrix_type my_val(0);\n\n                            if ( uniform_a_col_it != -1 && block_inside_id < NxN)\n                            {\n                                my_val = A_vals[NxN * uniform_a_col_it + block_inside_id];\n                            }\n\n                            my_bmAx_s[block_inside_id] -= my_val * utils::shfl(my_x, N * i + block_inside_id % N); // MOD IS SLOW!\n                            block_inside_id += WARP_SIZE;\n                        }\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_Einv[j] = 0.0;\n        }\n\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            if ((WARP_SIZE * j + lane_id) < NxN)\n            {\n                my_Einv[j] = Einv[NxN * a_row_id + WARP_SIZE * j + lane_id];\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_bmAx += my_bmAx_s[N * lane_id + i];\n                }\n            }\n        }\n        // Update the diagonal term.\n        int block_inside_id = lane_id;\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_bmAx_s[block_inside_id] = my_Einv[j] * utils::shfl(my_bmAx, block_inside_id % N);\n            block_inside_id += WARP_SIZE;\n        }\n\n        // Reduce bmAx terms.\n        {\n            my_bmAx = 0.0;\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                int idx = N * lane_id + i;\n\n                if ( lane_id < N )\n                {\n                    my_bmAx += my_bmAx_s[idx];\n                }\n            }\n        }\n\n        // Store the results.\n        if ( lane_id < N )\n        {\n            delta[N * a_row_id + lane_id] = my_bmAx;\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_4x4_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / 16;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_16 = lane_id % 16;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_16_div_4 = lane_id_mod_16 / 4;\n    const int lane_id_mod_16_mod_4 = lane_id_mod_16 % 4;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_16;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_16];\n    // Determine which 16 block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + threadIdx.x / 16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * a_row_id + lane_id_mod_16_div_4]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                my_bmAx = b[4 * a_row_id + lane_id_mod_16_mod_4];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += 16 )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + lane_id_mod_16_div_4;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[4 * uniform_a_col_id + lane_id_mod_16_mod_4]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[4 * uniform_a_col_id + lane_id_mod_16_mod_4];\n                }\n\n                my_s_mem[lane_id_mod_16] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[16 * uniform_a_col_it + lane_id_mod_16];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[4 * i + lane_id_mod_16_mod_4];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[4 * i + lane_id_mod_16_div_4];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv = Einv[16 * a_row_id + lane_id_mod_16];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_16_div_4 == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_16_mod_4 == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<4, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                my_s_mem[lane_id_mod_16_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                my_s_mem[lane_id_mod_16_mod_4] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_16_mod_4];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_16_div_4];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<4, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                delta[4 * a_row_id + lane_id_mod_16_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                delta[4 * a_row_id + lane_id_mod_16_mod_4] = my_bmAx;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int CTA_SIZE, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_4x4_kernel_row_major_vec4( const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Matrix_type *__restrict A_vals,\n        const int *__restrict A_diag,\n        const Vector_type *x,\n        const Vector_type *b,\n        Vector_type *__restrict delta,\n        const int *__restrict sorted_rows_by_color,\n        const int num_rows_per_color,\n        const int current_color,\n        const int *__restrict row_colors,\n        const Matrix_type *Einv,\n        const ColoringType boundary_coloring,\n        const int boundary_index )\n{\n    // Number of half warps per CTA.\n    const int NUM_HALF_WARPS = CTA_SIZE / 16;\n    // Coordinates of the thread.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Coordinates of the thread in the CTA.\n    const int thread_id_div_16 = threadIdx.x / 16;\n    const int thread_id_mod_16 = threadIdx.x % 16;\n    // Useful constants.\n    const int thread_id_mod_16_div_4 = thread_id_mod_16 / 4;\n    const int thread_id_mod_16_mod_4 = thread_id_mod_16 % 4;\n    const int shfl_offset = 16 * (lane_id / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[16 * thread_id_div_16];\n    // The iterator over rows.\n    int a_row_it = blockIdx.x * NUM_HALF_WARPS + thread_id_div_16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_HALF_WARPS )\n    {\n        unsigned int active_mask = utils::activemask();\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_bmAx = __cachingLoad(&b[4 * a_row_id + thread_id_mod_16_mod_4]);\n        }\n\n        // The range of the row.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If it has an external diagonal, we need one more item to put the diag.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; a_col_begin < a_col_max ; a_col_begin += 16 )\n        {\n            unsigned int active_mask_inner = utils::activemask();\n            int a_col_it = a_col_begin + thread_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = __cachingLoad(&A_cols[a_col_it]);\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && __cachingLoad(&row_colors[a_col_id]) < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + thread_id_mod_16_div_4;\n                // Load 8 blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, warpSize, active_mask_inner );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k, warpSize, active_mask_inner );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[4 * uniform_a_col_id + thread_id_mod_16_mod_4]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[4 * uniform_a_col_id + thread_id_mod_16_mod_4];\n                }\n\n                my_s_mem[thread_id_mod_16] = my_x;\n                int uniform_a_col_tmp = a_col_begin + my_k, uniform_a_col_it = -1;\n\n                if ( uniform_a_col_tmp < a_col_end )\n                {\n                    uniform_a_col_it = uniform_a_col_tmp;\n                }\n\n                if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                {\n                    uniform_a_col_it = A_diag[a_row_id];\n                }\n\n                Matrix_type my_vals[4] = { Matrix_type(0) };\n\n                if ( uniform_a_col_it != -1 )\n                {\n                    utils::load_vec4( my_vals, &A_vals[16 * uniform_a_col_it + 4 * thread_id_mod_16_mod_4] );\n                }\n\n                my_bmAx -= my_vals[0] * my_s_mem[4 * thread_id_mod_16_div_4 + 0];\n                my_bmAx -= my_vals[1] * my_s_mem[4 * thread_id_mod_16_div_4 + 1];\n                my_bmAx -= my_vals[2] * my_s_mem[4 * thread_id_mod_16_div_4 + 2];\n                my_bmAx -= my_vals[3] * my_s_mem[4 * thread_id_mod_16_div_4 + 3];\n            }\n        }\n\n        // Load Einvs.\n        Matrix_type my_Einv = Einv[16 * a_row_id + thread_id_mod_16];\n        // Reduce bmAx terms.\n        my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n        my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n\n        // Update the shared terms.\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_s_mem[thread_id_mod_16_mod_4] = my_bmAx;\n        }\n\n        // Update the diagonal term.\n        my_bmAx = my_Einv * my_s_mem[thread_id_mod_16_mod_4];\n        // Reduce bmAx terms.\n        my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n        my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n\n        // Store the results.\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            delta[4 * a_row_id + thread_id_mod_16_div_4] = my_bmAx;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_1x1_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    // Number of items per CTA.\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    // Number of items per grid.\n    const int NUM_ROWS_PER_GRID = gridDim.x * NUM_ROWS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ROWS_PER_CTA + (threadIdx.x / NUM_THREADS_PER_ROW);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ROWS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx = amgx::types::util<Vector_type>::get_zero();\n\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            my_bmAx = __cachingLoad(&b[a_row_id]);\n        }\n\n        // If it has an external diag.\n        if ( HAS_EXTERNAL_DIAG && lane_id_mod_NTPR == 0 )\n        {\n            my_bmAx -= A_vals[A_diag[a_row_id]] * x[a_row_id];\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_it  = A_rows[a_row_id  ];\n        int a_col_end = A_rows[a_row_id + 1];\n\n        // If the diagonal is stored separately, we have a special treatment.\n        //if( HAS_EXTERNAL_DIAG )\n        //  ++a_col_end;\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( a_col_it += lane_id_mod_NTPR ; utils::any( a_col_it < a_col_end ) ; a_col_it += NUM_THREADS_PER_ROW )\n        {\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Ignore the diagonal element since its color is smaller, and been accounted for above\n            if (HAS_EXTERNAL_DIAG && a_col_id == a_row_id)\n            {\n                a_col_id = -1;\n            }\n\n            // Load x.\n            Vector_type my_x(0);\n\n            if ( a_col_id != -1 )\n            {\n                my_x = __cachingLoad(&x[a_col_id]);\n            }\n\n            // Is it really a valid column (due to coloring).\n            int valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                valid = row_colors[a_col_tmp] < current_color;\n            }\n\n#endif\n\n            // Load my x value.\n            if ( valid )\n            {\n                my_x += delta[a_col_id];\n            }\n\n            // Load my item from A.\n            Matrix_type my_val(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                my_val = A_vals[a_col_it];\n            }\n\n            // Update bmAx.\n            my_bmAx -= my_val * my_x;\n        }\n\n        // Reduce bmAx terms.\n#pragma unroll\n\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, mask );\n        }\n\n        // Store the results.\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            delta[a_row_id] = Einv[a_row_id] * my_bmAx;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_NxN_kernel( const int *__restrict A_rows,\n                               const int *__restrict A_cols,\n                               const Matrix_type *__restrict A_vals,\n                               Vector_type *__restrict x,\n                               const WeightType weight,\n                               const int *__restrict sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const Matrix_type *__restrict Einv,\n                               const Vector_type *delta,\n                               Vector_type *__restrict Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_rows_per_color;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_end ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_tmp = -1, a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_tmp = A_cols[a_col_it];\n            }\n\n            // Make sure the column is interesting.\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_tmp != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_tmp >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_tmp < boundary_index && row_colors[a_col_tmp] > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_tmp != -1 && row_colors[a_col_tmp] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( valid )\n            {\n                a_col_id = a_col_tmp;\n            }\n\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = Delta[N * uniform_a_col_id + lane_id_mod_NxN_mod_N];\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    //if( uniform_a_col_id == -1 )\n                    //  break;\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_delta += my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_delta += my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Matrix_type my_Einv = Einv[NxN * a_row_id + lane_id_mod_NxN];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_delta = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_delta, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_delta;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_delta;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_delta = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_delta = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_delta = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_delta, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            const int offset = N * a_row_id + lane_id_mod_NxN_div_N;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n        else\n        {\n            const int offset = N * a_row_id + lane_id_mod_NxN_mod_N;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_NxN_kernel_large( const int *__restrict A_rows,\n                                     const int *__restrict A_cols,\n                                     const Matrix_type *__restrict A_vals,\n                                     Vector_type *__restrict x,\n                                     const WeightType weight,\n                                     const int *__restrict sorted_rows_by_color,\n                                     const int *__restrict row_colors,\n                                     const Matrix_type *__restrict Einv,\n                                     const Vector_type *delta,\n                                     Vector_type *__restrict Delta,\n                                     const int num_rows_per_color,\n                                     const int current_color,\n                                     const ColoringType boundary_coloring,\n                                     const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_N = lane_id / N;\n    const int lane_id_mod_N = lane_id % N; // id of a lane inside the block\n    const int blocks_per_warp = WARP_SIZE / N; // we process this cols per warp per row\n    const int row_elems_per_warp  = blocks_per_warp * N;\n    // Shared to store t_delta\n    __shared__ volatile Vector_type delta_s[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    volatile Vector_type *my_delta_s = &delta_s[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Accumulator\n        Vector_type my_delta(0);\n        //Vector_type mAx[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int i = 0; i < NUM_WARP_ITERS_PER_BLOCK; i++)\n        {\n            my_delta_s[WARP_SIZE * i + lane_id] = 0.0;\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_end ) ; a_col_begin += WARP_SIZE )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id;\n            // Get the ID of the column.\n            int a_col_tmp = -1, a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_tmp = A_cols[a_col_it];\n            }\n\n            // Make sure the column is interesting.\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_tmp != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_tmp >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_tmp < boundary_index && row_colors[a_col_tmp] > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_tmp != -1 && row_colors[a_col_tmp] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( valid )\n            {\n                a_col_id = a_col_tmp;\n            }\n\n            // Loop over columns. We compute blocks_per_warp columns per iteration.\n            for ( int k = 0 ; k < WARP_SIZE ; k += blocks_per_warp )\n            {\n                // id of the processed block by this thread\n                int my_k = k + lane_id_div_N;\n                // Load N blocks of X (if valid)\n                int uniform_a_col_id = utils::shfl( a_col_id, my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && lane_id < row_elems_per_warp)\n                {\n                    my_x = Delta[N * uniform_a_col_id + lane_id_mod_N];\n                }\n\n                // Load blocks of A.\n                // for each block in a batch\n#pragma unroll\n\n                for ( int i = 0 ; i < blocks_per_warp ; ++i )\n                {\n                    // k-th batch of blocks, i-th block. each thread process a column/row of a_it = uniform_a_col_tmp\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    // check if we are going out of bounds/color\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    // swipe with the whole warp\n                    if (uniform_a_col_it != -1)\n                    {\n                        int block_inside_id = lane_id;\n#pragma unroll\n\n                        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n                        {\n                            Matrix_type my_val(0);\n\n                            if ( uniform_a_col_it != -1 && block_inside_id < NxN)\n                            {\n                                my_val = A_vals[NxN * uniform_a_col_it + block_inside_id];\n                            }\n\n                            my_delta_s[block_inside_id] -= my_val * utils::shfl(my_x, N * i + block_inside_id % N); //my_s_mem[N*i + block_inside_id % N]; // MOD IS SLOW!\n                            block_inside_id += WARP_SIZE;\n                        }\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_Einv[j] = 0.0;\n        }\n\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            if ((WARP_SIZE * j + lane_id) < NxN)\n            {\n                my_Einv[j] = Einv[NxN * a_row_id + WARP_SIZE * j + lane_id];\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_delta += my_delta_s[N * lane_id + i];\n                }\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            int block_inside_id = lane_id;\n#pragma unroll\n\n            for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n            {\n                my_delta_s[block_inside_id] = my_Einv[j] * utils::shfl(my_delta, block_inside_id % N);\n                block_inside_id += WARP_SIZE;\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n            my_delta = 0.0;\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_delta += my_delta_s[N * lane_id + i];\n                }\n            }\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            const int offset = N * a_row_id + lane_id;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id < N )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id < N )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, typename WeightType, int CTA_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_4x4_kernel( const IndexType *row_offsets,\n                               const IndexType *column_indices,\n                               const ValueTypeA *nonzero_values,\n                               ValueTypeB *x,\n                               const WeightType weight,\n                               const int *sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const ValueTypeA *Einv,\n                               const ValueTypeB *delta,\n                               ValueTypeB *Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const IndexType boundary_index)\n{\n    const int nHalfWarps = CTA_SIZE / 16; // Number of half warps per CTA.\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        // Load one block of B.\n        ValueTypeB my_delta(0);\n        // The range of the rows.\n        int aColBegin = row_offsets[aRowId  ];\n        int aColEnd   = row_offsets[aRowId + 1];\n\n        // Each warp load column indices of 16 nonzero blocks\n        for ( ; aColBegin < aColEnd ; aColBegin += 16 )\n        {\n            int aColIt = aColBegin + halfLaneId;\n            // Get the ID of the column.\n            int aColTmp = -1, aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColTmp = column_indices[aColIt];\n            }\n\n#ifdef AMGX_ILU_COLORING\n            bool valid = (((aColTmp < boundary_index || boundary_coloring == SYNC_COLORS) && (row_colors[aColTmp] > current_color)) || (aColTmp >= boundary_index && boundary_coloring == LAST));\n\n            if ( aColTmp != -1 && valid )\n            {\n                aColId = aColTmp;\n            }\n\n#else\n\n            if ( aColTmp != -1 && row_colors[aColTmp] > current_color )\n            {\n                aColId = aColTmp;\n            }\n\n#endif\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + halfLaneId_div_4;\n                // Exchange column indices.\n                int waColId = utils::shfl( aColId, upperHalf + my_k );\n                // Load 8 blocks of X if needed.\n                ValueTypeB my_x(0);\n\n                if ( waColId != -1 )\n                {\n                    my_x = Delta[4 * waColId + halfLaneId_mod_4];\n                }\n\n                my_s_mem[halfLaneId] = my_x;\n                // Load 8 blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    const int k_i = k + i;\n                    int w_aColTmp = aColBegin + k_i, w_aColIt = -1;\n                    if ( utils::shfl( aColId, upperHalf + k_i ) != -1 && w_aColTmp < aColEnd )\n                        w_aColIt = w_aColTmp;\n\n                    ValueTypeA my_val(0);\n\n                    if ( w_aColIt != -1 )\n                    {\n                        my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_delta += my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                    }\n                    else\n                    {\n                        my_delta += my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load EINV values.\n        ValueTypeA my_Einv = Einv[16 * aRowId + halfLaneId];\n        // Reduce delta terms.\n        if ( ROW_MAJOR )\n        {\n            my_delta += utils::shfl_xor( my_delta, 1 );\n            my_delta += utils::shfl_xor( my_delta, 2 );\n        }\n        else\n        {\n            my_delta += utils::shfl_xor( my_delta, 4 );\n            my_delta += utils::shfl_xor( my_delta, 8 );\n        }\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_s_mem[halfLaneId_div_4] = my_delta;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_s_mem[halfLaneId_mod_4] = my_delta;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_delta = my_Einv * my_s_mem[halfLaneId_mod_4];\n        }\n        else\n        {\n            my_delta = my_Einv * my_s_mem[halfLaneId_div_4];\n        }\n\n        // Regroup results.\n        if ( ROW_MAJOR )\n        {\n            my_delta += utils::shfl_xor( my_delta, 1 );\n            my_delta += utils::shfl_xor( my_delta, 2 );\n        }\n        else\n        {\n            my_delta += utils::shfl_xor( my_delta, 4 );\n            my_delta += utils::shfl_xor( my_delta, 8 );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            int offset = 4 * aRowId + halfLaneId_div_4;\n            ValueTypeB my_b(0), my_x(0);\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x[offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                x[offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n        else\n        {\n            int offset = 4 * aRowId + halfLaneId_mod_4;\n            ValueTypeB my_b(0), my_x(0);\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x[offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                x[offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int CTA_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_4x4_kernel_row_major_vec4( const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Matrix_type *__restrict A_vals,\n        Vector_type *__restrict x,\n        const WeightType weight,\n        const int *__restrict sorted_rows_by_color,\n        const int *__restrict row_colors,\n        const Matrix_type *__restrict Einv,\n        const Vector_type *delta,\n        Vector_type *__restrict Delta,\n        const int num_rows_per_color,\n        const int current_color,\n        const ColoringType boundary_coloring,\n        const int boundary_index )\n{\n    // Number of half warps per CTA.\n    const int NUM_HALF_WARPS = CTA_SIZE / 16;\n    // Coordinates of the thread.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Coordinates of the thread in the CTA.\n    const int thread_id_div_16 = threadIdx.x / 16;\n    const int thread_id_mod_16 = threadIdx.x % 16;\n    // Useful constants.\n    const int thread_id_mod_16_div_4 = thread_id_mod_16 / 4;\n    const int thread_id_mod_16_mod_4 = thread_id_mod_16 % 4;\n    const int shfl_offset = 16 * (lane_id / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[16 * thread_id_div_16];\n    // The iterator over rows.\n    int a_row_it = blockIdx.x * NUM_HALF_WARPS + thread_id_div_16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_HALF_WARPS )\n    {\n        unsigned int active_mask = utils::activemask();\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; a_col_begin < a_col_end ; a_col_begin += 16 )\n        {\n            unsigned int active_mask_inner = utils::activemask();\n            int a_col_it = a_col_begin + thread_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = __cachingLoad(&A_cols[a_col_it]);\n            }\n\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_id < boundary_index && __cachingLoad(&row_colors[a_col_id]) > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_id != -1 && row_colors[a_col_id] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( !valid )\n            {\n                a_col_id = -1;\n            }\n\n            // Loop over columns. We compute 8 columns per iteration.\n#pragma unroll 2\n\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + thread_id_mod_16_div_4;\n                // Load 8 blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, warpSize, active_mask_inner );\n                Vector_type my_Delta(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_Delta = Delta[4 * uniform_a_col_id + thread_id_mod_16_mod_4];\n                }\n\n                my_s_mem[thread_id_mod_16] = my_Delta;\n                int uniform_a_col_it = a_col_begin + my_k;\n\n                if ( uniform_a_col_id == -1 || uniform_a_col_it >= a_col_end )\n                {\n                    uniform_a_col_it = -1;\n                }\n\n                Matrix_type my_vals[4] = { Matrix_type(0) };\n\n                if ( uniform_a_col_it != -1 )\n                {\n                    utils::load_vec4( my_vals, &A_vals[16 * uniform_a_col_it + 4 * thread_id_mod_16_mod_4] );\n                }\n\n                my_delta += my_vals[0] * my_s_mem[4 * thread_id_mod_16_div_4 + 0];\n                my_delta += my_vals[1] * my_s_mem[4 * thread_id_mod_16_div_4 + 1];\n                my_delta += my_vals[2] * my_s_mem[4 * thread_id_mod_16_div_4 + 2];\n                my_delta += my_vals[3] * my_s_mem[4 * thread_id_mod_16_div_4 + 3];\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load EINV values.\n        Matrix_type my_Einv = Einv[16 * a_row_id + thread_id_mod_16];\n        // Reduce delta terms.\n        my_delta += utils::shfl_xor( my_delta, 4, warpSize, active_mask );\n        my_delta += utils::shfl_xor( my_delta, 8, warpSize, active_mask );\n\n        // Update the shared terms.\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_s_mem[thread_id_mod_16_mod_4] = my_delta;\n        }\n\n        // Update the diagonal term.\n        my_delta = my_Einv * my_s_mem[thread_id_mod_16_mod_4];\n        // Regroup results.\n        my_delta += utils::shfl_xor( my_delta, 1, warpSize, active_mask );\n        my_delta += utils::shfl_xor( my_delta, 2, warpSize, active_mask );\n        // Store the results.\n        int offset = 4 * a_row_id + thread_id_mod_16_div_4;\n        Vector_type my_b(0), my_x(0);\n\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            my_b = __cachingLoad(&delta[offset]);\n            my_x = x    [offset];\n        }\n\n        my_delta = my_b - my_delta;\n\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            x    [offset] = my_x + weight * my_delta;\n            Delta[offset] = my_delta;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_1x1_kernel( const int *__restrict A_rows,\n                               const int *__restrict A_cols,\n                               const Matrix_type *__restrict A_vals,\n                               Vector_type *__restrict x,\n                               const WeightType weight,\n                               const int *__restrict sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const Matrix_type *__restrict Einv,\n                               const Vector_type *delta,\n                               Vector_type *__restrict Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const int boundary_index )\n{\n    // Number of items per CTA.\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    // Number of items per grid.\n    const int NUM_ROWS_PER_GRID = gridDim.x * NUM_ROWS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ROWS_PER_CTA + (threadIdx.x / NUM_THREADS_PER_ROW);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ROWS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_it  = A_rows[a_row_id  ];\n        int a_col_end = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( a_col_it += lane_id_mod_NTPR ; utils::any( a_col_it < a_col_end ) ; a_col_it += NUM_THREADS_PER_ROW )\n        {\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it really a valid column (due to coloring).\n            int valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                //if( boundary_coloring == LAST )\n                //  valid = a_col_id >= boundary_index;\n                //else\n                //  valid = a_col_id < boundary_index && row_colors[a_col_id] > current_color;\n                valid = (((a_col_id < boundary_index || boundary_coloring == SYNC_COLORS) && (row_colors[a_col_id] > current_color)) || (a_col_id >= boundary_index && boundary_coloring == LAST));\n            }\n\n#else\n\n            //if( a_col_id != -1 && current_color != 0 )\n            if ( a_col_id != -1  )\n            {\n                valid = row_colors[a_col_id] > current_color;\n            }\n\n#endif\n            // Load my Delta value.\n            Vector_type my_Delta(0);\n\n            if ( valid )\n            {\n                my_Delta = Delta[a_col_id];\n            }\n\n            // Load my item from A.\n            Matrix_type my_val(0);\n\n            if ( valid )\n            {\n                my_val = A_vals[a_col_it];\n            }\n\n            // Update bmAx.\n            my_delta += my_val * my_Delta;\n        }\n\n        // Reduce bmAx terms.\n#pragma unroll\n\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_delta += utils::shfl_xor( my_delta, mask );\n        }\n\n        // Store the results.\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            Vector_type my_x = __cachingLoad(&delta[a_row_id]) - Einv[a_row_id] * my_delta;\n            x    [a_row_id] += weight * my_x;\n            Delta[a_row_id]  = my_x;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_NxN_kernel_skip( Vector_type *__restrict x,\n                                    const WeightType weight,\n                                    const int *__restrict sorted_rows_by_color,\n                                    const Vector_type *delta,\n                                    Vector_type *__restrict Delta,\n                                    const int num_rows_per_color )\n{\n    const int NUM_ITEMS_PER_CTA = CTA_SIZE / N; // Number of updated block items per CTA\n    const int ITEM_ID           = threadIdx.x / N;\n    const int ITEM_BLOCK_OFFSET = threadIdx.x % N;\n    const int is_active    = ITEM_ID < NUM_ITEMS_PER_CTA;\n    // The first row.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + ITEM_ID;\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_ITEMS_PER_CTA )\n    {\n        if ( is_active )\n        {\n            int a_row_id = sorted_rows_by_color[a_row_it];\n            const int idx = N * a_row_id + ITEM_BLOCK_OFFSET;\n            Vector_type my_b = __cachingLoad(&delta[idx]);\n            Vector_type my_x = x[idx];\n            x[idx] = my_x + weight * my_b;\n            Delta[idx] = my_b;\n        }\n    }\n}\n\n// ----------\n// Methods\n// ----------\n\ntemplate< typename Matrix_type, typename Vector_type, int N >\nvoid DILU_forward_NxN_dispatch( const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const Matrix_type *__restrict A_vals,\n                                const int *__restrict A_diag,\n                                const Vector_type *x,\n                                const Vector_type *b,\n                                Vector_type *__restrict delta,\n                                const int *__restrict sorted_rows_by_color,\n                                const int num_rows_per_color,\n                                const int current_color,\n                                const int *__restrict row_colors,\n                                const Matrix_type *__restrict Einv,\n                                const ColoringType boundary_coloring,\n                                const int boundary_index,\n                                const int row_major,\n                                const int has_external_diag )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = std::max(WARP_SIZE / NxN, 1);\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n    // Branch to the correct kernel call.\n    int code = 2 * (row_major ? 1 : 0) + (has_external_diag ? 1 : 0);\n\n    switch ( code )\n    {\n        case 0: // Column-major, no external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 1: // Column-major, external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 2: // Row-major, no external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 3: // Row-major, external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\ntemplate< typename Matrix_type, typename Vector_type, int N >\nvoid DILU_forward_NxN_dispatch_large( const int *__restrict A_rows,\n                                      const int *__restrict A_cols,\n                                      const Matrix_type *__restrict A_vals,\n                                      const int *__restrict A_diag,\n                                      const Vector_type *x,\n                                      const Vector_type *b,\n                                      Vector_type *__restrict delta,\n                                      const int *__restrict sorted_rows_by_color,\n                                      const int num_rows_per_color,\n                                      const int current_color,\n                                      const int *__restrict row_colors,\n                                      const Matrix_type *__restrict Einv,\n                                      const ColoringType boundary_coloring,\n                                      const int boundary_index,\n                                      const int row_major,\n                                      const int has_external_diag )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Each warp is going to sweep through bloock this many times\n    const int NUM_WARP_ITERS_PER_BLOCK = ((NxN - 1) / WARP_SIZE) + 1;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if (!row_major)\n    {\n        FatalError(\"COL MAJOR is not supported for this large block_size\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    switch ( has_external_diag )\n    {\n        case 0: // Row-major, no external diagonal.\n            DILU_forward_NxN_kernel_large<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 1: // Row-major, external diagonal.\n            DILU_forward_NxN_kernel_large<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type >\nvoid DILU_forward_NxN_dispatch( const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const Matrix_type *__restrict A_vals,\n                                const int *__restrict A_diag,\n                                const Vector_type *x,\n                                const Vector_type *b,\n                                Vector_type *__restrict delta,\n                                const int *__restrict sorted_rows_by_color,\n                                const int num_rows_per_color,\n                                const int current_color,\n                                const int *__restrict row_colors,\n                                const Matrix_type *__restrict Einv,\n                                const ColoringType boundary_coloring,\n                                const int boundary_index,\n                                const int block_size,\n                                const int row_major,\n                                const int has_external_diag )\n{\n    switch ( block_size )\n    {\n        case 1:\n            {\n                const int NUM_THREADS_PER_ROW = 8;\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n                if ( has_external_diag )\n                {\n                    DILU_forward_1x1_kernel<Matrix_type, Vector_type, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                }\n                else\n                {\n                    DILU_forward_1x1_kernel<Matrix_type, Vector_type, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                }\n\n                cudaCheckError();\n            }\n            break;\n\n        case 2:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 3:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 3>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 4:\n            if ( row_major )\n            {\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / 16;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n                if ( has_external_diag )\n                    //DILU_forward_4x4_kernel<Matrix_type, Vector_type, CTA_SIZE, WARP_SIZE, true, true><<<grid_size, CTA_SIZE>>>(\n                    DILU_forward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, CTA_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                else\n                    DILU_forward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, CTA_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n\n                cudaCheckError();\n            }\n            else\n                DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 4>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    A_diag,\n                    x,\n                    b,\n                    delta,\n                    sorted_rows_by_color,\n                    num_rows_per_color,\n                    current_color,\n                    row_colors,\n                    Einv,\n                    boundary_coloring,\n                    boundary_index,\n                    row_major,\n                    has_external_diag );\n\n            break;\n\n        case 5:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 5>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 8:\n            DILU_forward_NxN_dispatch_large<Matrix_type, Vector_type, 8>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 10:\n            DILU_forward_NxN_dispatch_large<Matrix_type, Vector_type, 10>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N >\nvoid DILU_backward_NxN_dispatch( const int *__restrict A_rows,\n                                 const int *__restrict A_cols,\n                                 const Matrix_type *__restrict A_vals,\n                                 Vector_type *__restrict x,\n                                 const WeightType weight,\n                                 const int *__restrict sorted_rows_by_color,\n                                 const int *__restrict row_colors,\n                                 const Matrix_type *__restrict Einv,\n                                 const Vector_type *delta,\n                                 Vector_type *__restrict Delta,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 const ColoringType boundary_coloring,\n                                 const int boundary_index,\n                                 const int row_major )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = std::max(WARP_SIZE / NxN, 1);\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if ( row_major )\n    {\n        DILU_backward_NxN_kernel<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n    else\n    {\n        DILU_backward_NxN_kernel<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n\n    cudaCheckError();\n}\n\n\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int NUM_WARP_ITERS_PER_BLOCK >\nvoid DILU_backward_NxN_dispatch_large( const int *__restrict A_rows,\n                                       const int *__restrict A_cols,\n                                       const Matrix_type *__restrict A_vals,\n                                       Vector_type *__restrict x,\n                                       const WeightType weight,\n                                       const int *__restrict sorted_rows_by_color,\n                                       const int *__restrict row_colors,\n                                       const Matrix_type *__restrict Einv,\n                                       const Vector_type *delta,\n                                       Vector_type *__restrict Delta,\n                                       const int num_rows_per_color,\n                                       const int current_color,\n                                       const ColoringType boundary_coloring,\n                                       const int boundary_index,\n                                       const int row_major )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if ( row_major )\n    {\n        DILU_backward_NxN_kernel_large<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, true, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n    else\n    {\n        FatalError(\"col major is not supported for this blocksize in multicolor DILU solver\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType>\nvoid DILU_backward_NxN_dispatch( const int *__restrict A_rows,\n                                 const int *__restrict A_cols,\n                                 const Matrix_type *__restrict A_vals,\n                                 Vector_type *__restrict x,\n                                 const WeightType weight,\n                                 const int *__restrict sorted_rows_by_color,\n                                 const int *__restrict row_colors,\n                                 const Matrix_type *__restrict Einv,\n                                 const Vector_type *delta,\n                                 Vector_type *__restrict Delta,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 const ColoringType boundary_coloring,\n                                 const int boundary_index,\n                                 const int block_size,\n                                 const int row_major )\n{\n    switch ( block_size )\n    {\n        case 1:\n            {\n                const int NUM_THREADS_PER_ROW = 8;\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n                DILU_backward_1x1_kernel<Matrix_type, Vector_type, WeightType, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index );\n                cudaCheckError();\n            }\n            break;\n\n        case 2:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 3:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 3>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 4:\n\n            //if( false )\n            if ( row_major )\n            {\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / 16;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n                //DILU_backward_NxN_kernel<Matrix_type, Vector_type, 4, CTA_SIZE, WARP_SIZE, true><<<grid_size, CTA_SIZE>>>(\n                DILU_backward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, WeightType, CTA_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index );\n                cudaCheckError();\n            }\n            else\n                DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 4>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index,\n                    row_major );\n\n            break;\n\n        case 5:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 5>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 8:\n            DILU_backward_NxN_dispatch_large<Matrix_type, Vector_type, WeightType, 8, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 10:\n            DILU_backward_NxN_dispatch_large<Matrix_type, Vector_type, WeightType, 10, 4>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< class T_Config >\nMulticolorDILUSolver_Base<T_Config>::MulticolorDILUSolver_Base( AMG_Config &cfg,\n        const std::string &cfg_scope,\n        ThreadManager *tmng ) :\n    Solver<T_Config>( cfg, cfg_scope, tmng )\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n    this->m_boundary_coloring = cfg.AMG_Config::template getParameter<ColoringType>(\"boundary_coloring\", cfg_scope);\n    this->always_obey_coloring = 0;\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor DILU smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorDILUSolver_Base<T_Config>::~MulticolorDILUSolver_Base()\n{\n    Einv.clear();\n    Einv.shrink_to_fit();\n}\n\ntemplate<class T_Config>\nvoid MulticolorDILUSolver_Base<T_Config>::computeEinv(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if ( A.get_block_dimx() != A.get_block_dimy() )\n    {\n        FatalError(\"DILU implemented only for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( A.get_block_dimx() > 32) // actually much more less than 32 doe to register file limitations, but...\n    {\n        FatalError(\"DILU implemented only for squared blocks of size <= 32\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    computeEinv_NxN( A, A.get_block_dimx() );\n    A.setView(oldView);\n}\n\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor = \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorDILUSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    int N = this->m_explicit_A->get_num_cols() * this->m_explicit_A->get_block_dimy();\n\n    if (this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor dilu solver. Try setting: coloring_level=1 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    m_delta.resize(N);\n    m_Delta.resize(N);\n    m_delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_Delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_delta.set_block_dimx(1);\n    m_Delta.set_block_dimx(1);\n\n    if ( this->m_explicit_A->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Multicolor DILU solver only supports row major format for the blocks\", AMGX_ERR_CONFIGURATION);\n    }\n\n    computeEinv( *this->m_explicit_A );\n}\n\n//\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorDILUSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if ( this->m_explicit_A->get_block_dimx() != this->m_explicit_A->get_block_dimy() )\n    {\n        FatalError(\"DILU implemented only for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( this->m_explicit_A->get_block_dimx() > 32) // actually much more less than 32 doe to register file limitations, but...\n    {\n        FatalError(\"DILU implemented only for squared blocks of size <= 32\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (xIsZero)\n    {\n        x.dirtybit = 0;\n    }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n        this->m_explicit_A->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(this->m_explicit_A->getViewExterior());\n    }\n    else\n    {\n        flags = (ViewType)(this->m_explicit_A->getViewInterior());\n        this->m_explicit_A->setViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), types::util<ValueTypeB>::get_zero());\n        cudaCheckError();\n    }\n\n    this->smooth_NxN(*this->m_explicit_A, b, x, flags);\n\n    if (latencyHiding)\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(~(this->m_explicit_A->getViewInterior()) & this->m_explicit_A->getViewExterior());\n\n        if (flags != 0)\n        {\n            this->smooth_NxN(*this->m_explicit_A, b, x, flags);\n        }\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return (this->converged(b, x));\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorDILUSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_host, V, M, I> >::computeEinv_NxN(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor DILU smoother not implemented for host format, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_host, V, M, I> >::smooth_NxN( const Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag )\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nMulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::MulticolorDILUSolver(\n    AMG_Config &cfg,\n    const std::string &cfg_scope,\n    ThreadManager *tmng ) :\n    MulticolorDILUSolver_Base<TemplateConfig<AMGX_device, V, M, I> >( cfg, cfg_scope, tmng )\n{\n    int device = 0;\n    cudaGetDevice( &device );\n    cudaCheckError();\n    cudaDeviceProp properties;\n    cudaGetDeviceProperties( &properties, device );\n    cudaCheckError();\n    m_is_kepler = properties.major >= 3;\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::computeEinv_NxN(const Matrix_d &A, const int bsize)\n{\n    const int bsize_sq = bsize * bsize;\n    this->Einv.resize( A.get_num_cols()*bsize_sq, 0.0 );\n\n    // sol::prof_start();\n    for ( int i = 0, num_colors = A.getMatrixColoring().getNumColors() ; i < num_colors ; ++i )\n    {\n        const int color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const int num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        const int CTA_SIZE = 128;\n        const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n        int ROWS_PER_WARP = 1;\n\n        if ( bsize_sq > 1 && bsize_sq < 6 )\n        {\n            ROWS_PER_WARP = 32 / bsize_sq;\n        }\n\n        const int ROWS_PER_CTA = ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n        const int GRID_SIZE = std::min( 4096, (num_rows_per_color + ROWS_PER_CTA - 1) / ROWS_PER_CTA );\n        cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n\n        switch ( bsize )\n        {\n            case 1:\n                DILU_setup_1x1_kernel<ValueTypeA, ValueTypeB, 8, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 2:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 2, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 3:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 3, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 4:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 4, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 5:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 5, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 8:\n                DILU_setup_NxN_kernel_large<ValueTypeA, ValueTypeB, 8, CTA_SIZE, 32, 2> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 10:\n                DILU_setup_NxN_kernel_large<ValueTypeA, ValueTypeB, 10, CTA_SIZE, 32, 4> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            default:\n                FatalError( \"Multicolor-DILU Setup: block size was not enabled in the code, contact AMGX developers.\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::smooth_NxN( const Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag )\n{\n    AMGX_CPU_PROFILER( \"MulticolorDILUSolver::smooth_NxN \" );\n    int offset = 0, separation = 0;\n    A.getOffsetAndSizeForView(INTERIOR, &offset, &separation);\n\n    // Only have separation=num interior rows if we are only working on the interior\n    // and the boundary coloring is FIRST or LAST, otherwise set separation offset to\n    // total number of rows\n    if ( separation_flag != this->m_explicit_A->getViewInterior() ||\n            this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior() ||\n            this->m_boundary_coloring != LAST && this->m_boundary_coloring != FIRST )\n    {\n        separation = A.row_offsets.size() - 1;\n    }\n    else\n    {\n        amgx_printf(\"separation active\\n\");\n    }\n\n    // --------------------\n    // Forward Sweep\n    // --------------------\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for ( int i = 0 ; i < num_colors ; ++i )\n    {\n        int color_offset(0);\n\n        if ( separation_flag & INTERIOR )\n        {\n            color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            color_offset = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n\n        int num_rows_per_color(0);\n\n        if ( separation_flag == this->m_explicit_A->getViewInterior() )\n        {\n            num_rows_per_color = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1];\n        }\n\n        num_rows_per_color -= color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        int boundary_index = separation;\n\n        if ( this->m_boundary_coloring == SYNC_COLORS )\n        {\n            boundary_index = A.get_num_rows();\n        }\n\n        DILU_forward_NxN_dispatch(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            A.diag.raw(),\n            x.raw(),\n            b.raw(),\n            this->m_delta.raw(),\n            A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n            num_rows_per_color,\n            i,\n            A.getMatrixColoring().getRowColors().raw(),\n            this->Einv.raw(),\n            this->m_boundary_coloring,\n            boundary_index,\n            A.get_block_dimy(),\n            A.getBlockFormat() == ROW_MAJOR,\n            A.hasProps(DIAG) );\n        cudaCheckError();\n    }\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for ( int i = num_colors - 1 ; i >= 0 ; --i )\n    {\n        int color_offset(0);\n\n        if ( separation_flag & INTERIOR )\n        {\n            color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            color_offset = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n\n        int num_rows_per_color(0);\n\n        if ( separation_flag == this->m_explicit_A->getViewInterior() )\n        {\n            num_rows_per_color = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1];\n        }\n\n        num_rows_per_color -= color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        if ( i == num_colors - 1 )\n        {\n            const int NUM_ROWS_PER_CTA = CTA_SIZE / A.get_block_dimy();\n            const int GRID_SIZE = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n            switch ( A.get_block_dimy() )\n            {\n                case 1:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 1, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 2:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 2, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 3:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 3, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 4:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 4, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 5:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 5, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 8:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 8, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 10:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 10, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            DILU_backward_NxN_dispatch(\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                x.raw(),\n                this->weight,\n                A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                A.getMatrixColoring().getRowColors().raw(),\n                this->Einv.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                num_rows_per_color,\n                i,\n                this->m_boundary_coloring,\n                separation,\n                A.get_block_dimy(),\n                A.getBlockFormat() == ROW_MAJOR );\n            cudaCheckError();\n        }\n    }\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorDILUSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//    AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorDILUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//    AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <solvers/multicolor_dilu_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <util.h>\n#include <texture.h>\n#include <matrix_io.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n\n#include <amgx_types/util.h>\n\n#include <algorithm>\n\n#define AMGX_ILU_COLORING\n\nnamespace amgx\n{\nnamespace multicolor_dilu_solver\n{\n\nenum { CTA_SIZE = 128, WARP_SIZE = 32 };\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_setup_NxN_kernel_large( const int *__restrict A_rows,\n                                  const int *__restrict A_cols,\n                                  const int *__restrict A_diag,\n                                  const Matrix_type *__restrict A_vals,\n                                  Matrix_type *__restrict Einv,\n                                  const int *sorted_rows_by_color,\n                                  const int *row_colors,\n                                  const int  num_rows_per_color,\n                                  const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Shared memory to broadcast column IDs.\n    __shared__ volatile int s_a_col_ids[CTA_SIZE];\n    __shared__ volatile int s_a_col_its[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_a_col_ids = &s_a_col_ids[threadIdx.x - lane_id];\n    volatile int *my_s_a_col_its = &s_a_col_its[threadIdx.x - lane_id];\n    // Shared memory to store the matrices.\n    __shared__ volatile Vector_type s_A_mtx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    __shared__ volatile Vector_type s_B_mtx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_A_mtx = &s_A_mtx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    volatile Vector_type *my_s_B_mtx = &s_B_mtx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Shared memory to store the index of the element Aji.\n    __shared__ volatile int s_A_ji[NUM_WARPS_PER_CTA];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_A_ji = &s_A_ji[warp_id];\n    // Precomputing some stuff\n    int idx[NUM_WARP_ITERS_PER_BLOCK];\n    int idy[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n    {\n        const int id = (WARP_SIZE * wb + lane_id) % NxN;\n        idx[wb] = id / N;\n        idy[wb] = id % N;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; utils::any( a_row_it < num_rows_per_color ) ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = -1;\n\n        if ( a_row_it < num_rows_per_color )\n        {\n            a_row_id = sorted_rows_by_color[a_row_it];\n        }\n\n        // Load the diagonal.\n        Vector_type e_out[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n        {\n            e_out[wb] = (Vector_type)0.0;\n        }\n\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n            if ( a_row_id != -1 && (wb * WARP_SIZE + lane_id) < NxN)\n            {\n                e_out[wb] = A_vals[NxN * A_diag[a_row_id] + wb * WARP_SIZE + lane_id];\n            }\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the rows.\n            int a_col_begin(0), a_col_end(0);\n\n            if ( a_row_id != -1 )\n            {\n                a_col_begin = A_rows[a_row_id  ];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += NxN )\n            {\n                // Each thread loads a single element. If !is_active, a_col_end == 0.\n                int a_col_it = a_col_begin + lane_id;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1 );\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                my_s_a_col_its[dest] = a_col_it;\n                // Temporary storage with zeros for OOB\n                Vector_type my_A[NUM_WARP_ITERS_PER_BLOCK], my_B[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n                for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                {\n                    my_A[wb] = (Vector_type)0.0;\n                    my_B[wb] = (Vector_type)0.0;\n                }\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < WARP_SIZE ; ++k )\n                {\n                    // Exchange column indices.\n                    const int uniform_a_col_id = my_s_a_col_ids[k];\n\n                    // Early exit.\n                    if ( uniform_a_col_id == -1 )\n                    {\n                        break;\n                    }\n\n                    // Load the iterator.\n                    const int uniform_a_col_it = my_s_a_col_its[k];\n                    // Load the two matrices.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                        if ((wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_A[wb] = A_vals[NxN * uniform_a_col_it + wb * WARP_SIZE + lane_id];\n                            my_B[wb] = Einv  [NxN * uniform_a_col_id + wb * WARP_SIZE + lane_id];\n                        }\n\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_s_A_mtx[lane_id + wb * WARP_SIZE] = my_A[wb];\n                        my_s_B_mtx[lane_id + wb * WARP_SIZE] = my_B[wb];\n                    }\n\n                    // Compute the product of matrices.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_A[wb] = (Vector_type)0.0;\n#pragma unroll\n\n                        for ( int m = 0 ; m < N ; ++m )\n                        {\n                            my_A[wb] += my_s_A_mtx[N * idx[wb] + m] * my_s_B_mtx[N * m + idy[wb]];\n                        }\n                    }\n\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                        if ((wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_s_A_mtx[lane_id + wb * WARP_SIZE] = my_A[wb];\n                        }\n\n                    // We looking for columns in the two rows we're interested in.\n                    int b_col_it  = A_rows[uniform_a_col_id  ];\n                    int b_col_end = A_rows[uniform_a_col_id + 1];\n\n                    // Init the marker to -1.\n                    if ( lane_id == 0 )\n                    {\n                        *my_s_A_ji = -1;\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id;\n                    int shared_found = utils::ballot( lane_id == 0 && uniform_a_col_id == -1 );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            *my_s_A_ji = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found);\n                        b_col_it += NxN;\n                    }\n                    while ( __popc( shared_found ) == 0 && utils::any( b_col_it < b_col_end ) );\n\n                    // Load the blocks.\n                    const int w_aji = *my_s_A_ji;\n                    Vector_type my_C[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n                        my_C[wb] = (Vector_type)0.0;\n\n                        if ( w_aji != -1 && (wb * WARP_SIZE + lane_id) < NxN)\n                        {\n                            my_C[wb] = A_vals[NxN * w_aji + wb * WARP_SIZE + lane_id];\n                        }\n\n                        my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_C[wb];\n                    }\n\n                    // Update e_out.\n#pragma unroll\n\n                    for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                    {\n#pragma unroll\n\n                        for ( int m = 0 ; m < N ; ++m )\n                        {\n                            e_out[wb] -= my_s_A_mtx[N * idx[wb] + m] * my_s_B_mtx[N * m + idy[wb]];\n                        }\n                    }\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Store e_out in A\n#pragma unroll\n\n        for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n        {\n            my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id] = e_out[wb];\n        }\n\n        // Invert the matrices.\n#pragma unroll\n\n        for ( int row = 0 ; row < N ; ++row )\n        {\n            Vector_type diag(0), diag_tmp = my_s_A_mtx[N * row + row];\n\n            if ( isNotCloseToZero(diag_tmp) )\n            {\n                diag = Vector_type(1) / diag_tmp;\n            }\n            else\n            {\n                diag = Vector_type(1) / epsilon(diag_tmp);\n            }\n\n            if ( lane_id < N && lane_id != row)\n            {\n                my_s_A_mtx[N * row + lane_id] = my_s_B_mtx[N * row + lane_id] = my_s_B_mtx[N * row + lane_id] * diag;\n            }\n\n#pragma unroll\n\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                if ( idx[wb] != row && idy[wb] != row)\n                {\n                    my_s_A_mtx[wb * WARP_SIZE + lane_id] = my_s_B_mtx[wb * WARP_SIZE + lane_id] - my_s_B_mtx[N * idx[wb] + row] * my_s_B_mtx[N * row + idy[wb]];\n                }\n\n            if ( lane_id < N )\n            {\n                Vector_type tmp = diag;\n\n                if ( lane_id != row )\n                {\n                    tmp = -my_s_A_mtx[N * lane_id + row] * diag;\n                }\n\n                my_s_A_mtx[N * lane_id + row] = tmp;\n            }\n\n#pragma unroll\n\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n            {\n                my_s_B_mtx[wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id];\n            }\n        }\n\n        // Store the results to Einv.\n        if ( a_row_id != -1 )\n#pragma unroll\n            for (int wb = 0; wb < NUM_WARP_ITERS_PER_BLOCK; wb++)\n                if (wb * WARP_SIZE + lane_id < NxN)\n                {\n                    Einv[NxN * a_row_id + wb * WARP_SIZE + lane_id] = my_s_A_mtx[wb * WARP_SIZE + lane_id];\n                }\n    }\n}\n\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_setup_NxN_kernel( const int *__restrict A_rows,\n                            const int *__restrict A_cols,\n                            const int *__restrict A_diag,\n                            const Matrix_type *__restrict A_vals,\n                            Matrix_type *__restrict Einv,\n                            const int *sorted_rows_by_color,\n                            const int *row_colors,\n                            const int  num_rows_per_color,\n                            const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Upper-bound on the number of items per warp.\n    const int NUM_ITEMS_PER_WARP_CEIL = (WARP_SIZE + NxN - 1) / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We need NxN to compute a NxN block. Encode a mask for the first block.\n    int mask_tmp = utils::ballot( lane_id_div_NxN == 0 );\n    // Mask for ballots. We shift the mask with NxN active bits by the needed number of bits.\n    const int mask_NxN = mask_tmp << (lane_id_div_NxN * __popc(mask_tmp));\n    // Shared memory to broadcast column IDs.\n    __shared__ volatile int s_a_col_ids[CTA_SIZE];\n    __shared__ volatile int s_a_col_its[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_a_col_ids = &s_a_col_ids[threadIdx.x - lane_id_mod_NxN];\n    volatile int *my_s_a_col_its = &s_a_col_its[threadIdx.x - lane_id_mod_NxN];\n    // Shared memory to store the matrices.\n    __shared__ volatile Vector_type s_A_mtx[CTA_SIZE];\n    __shared__ volatile Vector_type s_B_mtx[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_A_mtx = &s_A_mtx[threadIdx.x - lane_id_mod_NxN];\n    volatile Vector_type *my_s_B_mtx = &s_B_mtx[threadIdx.x - lane_id_mod_NxN];\n    // Shared memory to store the index of the element Aji.\n    __shared__ volatile int s_A_ji[NUM_WARPS_PER_CTA * NUM_ITEMS_PER_WARP_CEIL];\n    // Each thread keeps its own pointer.\n    volatile int *my_s_A_ji = &s_A_ji[warp_id * NUM_ITEMS_PER_WARP_CEIL + lane_id_div_NxN];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; utils::any( a_row_it < num_rows_per_color ) ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n        // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n        // to false ; that's the common trick to determine if a number is a power of 2).\n        int is_active = true;\n\n        if ( NxN & (NxN - 1) )\n        {\n            is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n        }\n\n        int a_row_id = -1;\n\n        if ( is_active && a_row_it < num_rows_per_color )\n        {\n            a_row_id = sorted_rows_by_color[a_row_it];\n        }\n\n        // Load the diagonal.\n        Vector_type e_out(0);\n\n        if ( a_row_id != -1 )\n        {\n            e_out = A_vals[NxN * A_diag[a_row_id] + lane_id_mod_NxN];\n        }\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the rows.\n            int a_col_begin(0), a_col_end(0);\n\n            if ( a_row_id != -1 )\n            {\n                a_col_begin = A_rows[a_row_id  ];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += NxN )\n            {\n                unsigned int active_mask = utils::activemask();\n                // Each thread loads a single element. If !is_active, a_col_end == 0.\n                int a_col_it = a_col_begin + lane_id_mod_NxN;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1, active_mask ) & mask_NxN;\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id_mod_NxN - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                my_s_a_col_its[dest] = a_col_it;\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    // Exchange column indices.\n                    const int uniform_a_col_id = my_s_a_col_ids[k];\n\n                    // Early exit.\n                    if ( utils::all( uniform_a_col_id == -1, active_mask ) )\n                    {\n                        break;\n                    }\n\n                    // Load the iterator.\n                    const int uniform_a_col_it = my_s_a_col_its[k];\n                    // Load the two matrices.\n                    Vector_type my_A(0), my_B(0);\n\n                    if ( uniform_a_col_id != -1 )\n                    {\n                        my_A = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                        my_B = Einv  [NxN * uniform_a_col_id + lane_id_mod_NxN];\n                    }\n\n                    my_s_A_mtx[lane_id_mod_NxN] = my_A;\n                    my_s_B_mtx[lane_id_mod_NxN] = my_B;\n                    utils::syncwarp(active_mask);\n                    // Compute the product of matrices.\n                    Vector_type tmp(0);\n#pragma unroll\n\n                    for ( int m = 0 ; m < N ; ++m )\n                    {\n                        tmp += my_s_A_mtx[N * lane_id_mod_NxN_div_N + m] * my_s_B_mtx[N * m + lane_id_mod_NxN_mod_N];\n                    }\n\n                    my_s_A_mtx[lane_id_mod_NxN] = tmp;\n                    // We looking for columns in the two rows we're interested in.\n                    int b_col_it(0), b_col_end(0);\n\n                    if ( is_active && uniform_a_col_id != -1 )\n                    {\n                        b_col_it  = A_rows[uniform_a_col_id  ];\n                        b_col_end = A_rows[uniform_a_col_id + 1];\n                    }\n\n                    // Init the marker to -1.\n                    if ( lane_id_mod_NxN == 0 )\n                    {\n                        *my_s_A_ji = -1;\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id_mod_NxN;\n                    int shared_found = utils::ballot( lane_id_mod_NxN == 0 && uniform_a_col_id == -1, active_mask );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            *my_s_A_ji = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found, active_mask);\n                        b_col_it += NxN;\n                    }\n                    while ( __popc( shared_found ) < NUM_ITEMS_PER_WARP && utils::any( b_col_it < b_col_end, active_mask ) );\n\n                    // Load the blocks.\n                    const int w_aji = *my_s_A_ji;\n                    Vector_type my_C(0);\n\n                    if ( w_aji != -1 )\n                    {\n                        my_C = A_vals[NxN * w_aji + lane_id_mod_NxN];\n                    }\n\n                    my_s_B_mtx[lane_id_mod_NxN] = my_C;\n                    // Update e_out.\n#pragma unroll\n\n                    for ( int m = 0 ; m < N ; ++m )\n                    {\n                        e_out -= my_s_A_mtx[N * lane_id_mod_NxN_div_N + m] * my_s_B_mtx[N * m + lane_id_mod_NxN_mod_N];\n                    }\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Store e_out in A\n        my_s_A_mtx[lane_id_mod_NxN] = e_out;\n        utils::syncwarp();\n\n        // Invert the matrices.\n#pragma unroll\n\n        for ( int row = 0 ; row < N ; ++row )\n        {\n            Vector_type diag(0), diag_tmp = my_s_A_mtx[N * row + row];\n\n            if ( isNotCloseToZero(diag_tmp) )\n            {\n                diag = Vector_type(1) / diag_tmp;\n            }\n            else\n            {\n                diag = Vector_type(1) / epsilon(diag_tmp);\n            }\n\n            if ( is_active && lane_id_mod_NxN_div_N == 0 && lane_id_mod_NxN_mod_N != row )\n            {\n                my_s_A_mtx[N * row + lane_id_mod_NxN_mod_N] *= diag;\n            }\n            utils::syncwarp();\n\n            if ( is_active && lane_id_mod_NxN_div_N != row && lane_id_mod_NxN_mod_N != row )\n            {\n                my_s_A_mtx[lane_id_mod_NxN] -= my_s_A_mtx[N * lane_id_mod_NxN_div_N + row] * my_s_A_mtx[N * row + lane_id_mod_NxN_mod_N];\n            }\n            utils::syncwarp();\n\n            if ( is_active && lane_id_mod_NxN_div_N == 0 )\n            {\n                Vector_type tmp = diag;\n\n                if ( lane_id_mod_NxN_mod_N != row )\n                {\n                    tmp = -my_s_A_mtx[N * lane_id_mod_NxN_mod_N + row] * diag;\n                }\n\n                my_s_A_mtx[N * lane_id_mod_NxN_mod_N + row] = tmp;\n            }\n            utils::syncwarp();\n        }\n\n        // Store the results to Einv.\n        if ( a_row_id != -1 )\n        {\n            Einv[NxN * a_row_id + lane_id_mod_NxN] = my_s_A_mtx[lane_id_mod_NxN];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_setup_1x1_kernel( const int *__restrict A_rows,\n                            const int *__restrict A_cols,\n                            const int *__restrict A_diag,\n                            const Matrix_type *__restrict A_vals,\n                            Matrix_type *__restrict Einv,\n                            const int *sorted_rows_by_color,\n                            const int *row_colors,\n                            const int  num_rows_per_color,\n                            const int  current_color )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per grid.\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NTPR = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Shared memory to broadcast column IDs.\n    __shared__ int s_a_col_ids[CTA_SIZE];\n    // Each thread keeps its own pointer.\n    int *my_s_a_col_ids = &s_a_col_ids[warp_id * WARP_SIZE];\n    // Shared memory to store the matrices.\n    __shared__ int s_A_ji[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    int *my_s_A_ji = &s_A_ji[warp_id * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_WARPS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load the diagonal.\n        Vector_type e_out(0);\n\n        // Skip the 1st iteration of the outer-loop (that loop runs on the host).\n        if ( current_color != 0 )\n        {\n            // Ranges of the row.\n            int a_col_begin = A_rows[a_row_id  ];\n            int a_col_end   = A_rows[a_row_id + 1];\n\n            // Iterate over the elements in the columns.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n            {\n                // Each thread loads a single element.\n                int a_col_it = a_col_begin + lane_id;\n                // The identifier of the column if the iterator is valid.\n                int a_col_tmp = -1, a_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_tmp = A_cols[a_col_it];\n                }\n\n                if ( a_col_tmp != -1 && row_colors[a_col_tmp] < current_color )\n                {\n                    a_col_id = a_col_tmp;\n                }\n\n                // When the diagonal is stored inside the matrix, we have to reject it. We\n                // could be using a template parameter but it's not needed since that\n                // rejection is really cheap (a couple of extra cycles -- CMP+MOV).\n                if ( a_col_id == a_row_id )\n                {\n                    a_col_id = -1;\n                }\n\n                // We partition valid and invalid column ids. Valid ones come first.\n                int vote = utils::ballot( a_col_id != -1 );\n                int ones = __popc( vote );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( a_col_id == -1 )\n                {\n                    dest = ones + lane_id - dest;\n                }\n\n                my_s_a_col_ids[dest] = a_col_id;\n                // Reset A_jis.\n                my_s_A_ji[lane_id] = -1;\n\n                __syncwarp();\n\n                // Threads collaborate to load the rows.\n                for ( int k = 0 ; k < ones ; k += WARP_SIZE / NUM_THREADS_PER_ROW )\n                {\n                    const int local_k = k + lane_id_div_NTPR;\n                    // Exchange column indices.\n                    int uniform_a_col_id = -1;\n\n                    if ( local_k < ones )\n                    {\n                        uniform_a_col_id = my_s_a_col_ids[local_k];\n                    }\n\n                    // We look for columns in the rows we're interested in.\n                    int b_col_it(0), b_col_end(0);\n\n                    if ( uniform_a_col_id != -1 )\n                    {\n                        b_col_it  = A_rows[uniform_a_col_id  ];\n                        b_col_end = A_rows[uniform_a_col_id + 1];\n                    }\n\n                    // Run the loop.\n                    b_col_it += lane_id_mod_NTPR;\n                    int shared_found = utils::ballot( lane_id_mod_NTPR == 0 && uniform_a_col_id == -1 );\n\n                    do\n                    {\n                        bool found = b_col_it < b_col_end && A_cols[b_col_it] == a_row_id;\n\n                        if ( found )\n                        {\n                            my_s_A_ji[local_k] = b_col_it;\n                        }\n\n                        shared_found = shared_found | utils::ballot(found);\n                        b_col_it += NUM_THREADS_PER_ROW;\n                    }\n                    while ( __popc( shared_found ) < WARP_SIZE / NUM_THREADS_PER_ROW && utils::any( b_col_it < b_col_end ) );\n                }\n\n                __syncwarp();\n\n                // Where to get my A_ji from (if any).\n                int a_ji_it = my_s_A_ji[dest];\n                // Grab A_jis.\n                Matrix_type a_ji(0);\n\n                if ( a_ji_it != -1 )\n                {\n                    a_ji = A_vals[a_ji_it];\n                }\n\n                // Update e_out.\n                if ( a_col_id != -1 )\n                {\n                    e_out += a_ji * Einv[a_col_id] * A_vals[a_col_it];\n                }\n            } // a_col_begin < a_col_end\n        } // current_color != 0\n\n        // Reduce the e_outs in one value.\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            e_out += utils::shfl_xor( e_out, mask );\n        }\n\n        // Store the result.\n        if ( lane_id == 0 )\n        {\n            Matrix_type res = A_vals[A_diag[a_row_id]] - e_out;\n\n            if ( res != Matrix_type(0) )\n            {\n                res = Matrix_type(1) / res;\n            }\n\n            Einv[a_row_id] = static_cast<Vector_type>(res);\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int N, bool ROW_MAJOR, int WARP_SIZE, typename Value_type >\nstatic __device__ __forceinline__\nValue_type reduce_distributed_vectors( Value_type x, int is_leader )\n{\n    if ( N & (N - 1) )\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; ++i )\n        {\n            Value_type other_x = utils::shfl_down( x, ROW_MAJOR ? i : N * i );\n\n            if ( is_leader )\n            {\n                x += other_x;\n            }\n        }\n    }\n    else\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; i <<= 1 )\n        {\n            x += utils::shfl_xor( x, ROW_MAJOR ? i : N * i );\n        }\n    }\n\n    return x;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_NxN_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_rows_per_color;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id_mod_NxN_div_N]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_bmAx = b[N * a_row_id + lane_id_mod_NxN_mod_N];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_NxN_mod_N]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[N * uniform_a_col_id + lane_id_mod_NxN_mod_N];\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && is_active && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv = Einv[NxN * a_row_id + lane_id_mod_NxN];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                delta[N * a_row_id + lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                delta[N * a_row_id + lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n    }\n}\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE, int WARP_SIZE, bool HAS_EXTERNAL_DIAG, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_NxN_kernel_large( const int *__restrict A_rows,\n                                    const int *__restrict A_cols,\n                                    const Matrix_type *__restrict A_vals,\n                                    const int *__restrict A_diag,\n                                    const Vector_type *x,\n                                    const Vector_type *b,\n                                    Vector_type *__restrict delta,\n                                    const int *__restrict sorted_rows_by_color,\n                                    const int num_rows_per_color,\n                                    const int current_color,\n                                    const int *__restrict row_colors,\n                                    const Matrix_type *__restrict Einv,\n                                    const ColoringType boundary_coloring,\n                                    const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of rows computed per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of rows? per grid.\n    const int NUM_ITEMS_PER_GRID = CTA_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    // Useful index to compute matrix products.\n    const int lane_id_div_N = lane_id / N;\n    const int lane_id_mod_N = lane_id % N; // id of a lane inside the block\n    const int blocks_per_warp = WARP_SIZE / N; // we process this cols per warp per row\n    const int row_elems_per_warp  = blocks_per_warp * N;\n    // Shared to store bmAx\n    __shared__ volatile Vector_type bmAx[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    volatile Vector_type *my_bmAx_s = &bmAx[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( lane_id < N )\n        {\n            my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id]);\n        }\n\n#pragma unroll\n\n        for (int i = 0; i < NUM_WARP_ITERS_PER_BLOCK; i++)\n        {\n            my_bmAx_s[WARP_SIZE * i + lane_id] = 0.0;\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += WARP_SIZE ) // NxN\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n\n            // Loop over columns. We compute blocks_per_warp columns per iteration.\n            for ( int k = 0 ; k < WARP_SIZE ; k += blocks_per_warp )\n            {\n                // id of the processed block by this thread\n                int my_k = k + lane_id_div_N;\n                // Load N blocks of X (if valid)\n                int uniform_a_col_id = utils::shfl( a_col_id, my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && lane_id < row_elems_per_warp)\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_N]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid && lane_id < row_elems_per_warp)\n                {\n                    my_x += delta[N * uniform_a_col_id + lane_id_mod_N];\n                }\n\n                //my_s_mem[lane_id] = my_x;\n#pragma unroll\n\n                for ( int i = 0 ; i < blocks_per_warp ; ++i )\n                {\n                    // k-th batch of blocks, i-th block. each thread process a column/row of a_it = uniform_a_col_tmp\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    // check if we are going out of bounds/color\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    // swipe with the whole warp\n                    if (uniform_a_col_it != -1)\n                    {\n                        int block_inside_id = lane_id;\n#pragma unroll\n\n                        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n                        {\n                            Matrix_type my_val(0);\n\n                            if ( uniform_a_col_it != -1 && block_inside_id < NxN)\n                            {\n                                my_val = A_vals[NxN * uniform_a_col_it + block_inside_id];\n                            }\n\n                            my_bmAx_s[block_inside_id] -= my_val * utils::shfl(my_x, N * i + block_inside_id % N); // MOD IS SLOW!\n                            block_inside_id += WARP_SIZE;\n                        }\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_Einv[j] = 0.0;\n        }\n\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            if ((WARP_SIZE * j + lane_id) < NxN)\n            {\n                my_Einv[j] = Einv[NxN * a_row_id + WARP_SIZE * j + lane_id];\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_bmAx += my_bmAx_s[N * lane_id + i];\n                }\n            }\n        }\n        // Update the diagonal term.\n        int block_inside_id = lane_id;\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_bmAx_s[block_inside_id] = my_Einv[j] * utils::shfl(my_bmAx, block_inside_id % N);\n            block_inside_id += WARP_SIZE;\n        }\n\n        // Reduce bmAx terms.\n        {\n            my_bmAx = 0.0;\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                int idx = N * lane_id + i;\n\n                if ( lane_id < N )\n                {\n                    my_bmAx += my_bmAx_s[idx];\n                }\n            }\n        }\n\n        // Store the results.\n        if ( lane_id < N )\n        {\n            delta[N * a_row_id + lane_id] = my_bmAx;\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_4x4_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / 16;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_16 = lane_id % 16;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_16_div_4 = lane_id_mod_16 / 4;\n    const int lane_id_mod_16_mod_4 = lane_id_mod_16 % 4;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_16;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_16];\n    // Determine which 16 block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + threadIdx.x / 16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[4 * a_row_id + lane_id_mod_16_div_4]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                my_bmAx = b[4 * a_row_id + lane_id_mod_16_mod_4];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += 16 )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + lane_id_mod_16_div_4;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[4 * uniform_a_col_id + lane_id_mod_16_mod_4]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[4 * uniform_a_col_id + lane_id_mod_16_mod_4];\n                }\n\n                my_s_mem[lane_id_mod_16] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                    {\n                        uniform_a_col_it = A_diag[a_row_id];\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[16 * uniform_a_col_it + lane_id_mod_16];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[4 * i + lane_id_mod_16_mod_4];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[4 * i + lane_id_mod_16_div_4];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv = Einv[16 * a_row_id + lane_id_mod_16];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_16_div_4 == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_16_mod_4 == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<4, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                my_s_mem[lane_id_mod_16_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                my_s_mem[lane_id_mod_16_mod_4] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_16_mod_4];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_16_div_4];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<4, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_16_mod_4 == 0 )\n            {\n                delta[4 * a_row_id + lane_id_mod_16_div_4] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_16_div_4 == 0 )\n            {\n                delta[4 * a_row_id + lane_id_mod_16_mod_4] = my_bmAx;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int CTA_SIZE, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_4x4_kernel_row_major_vec4( const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Matrix_type *__restrict A_vals,\n        const int *__restrict A_diag,\n        const Vector_type *x,\n        const Vector_type *b,\n        Vector_type *__restrict delta,\n        const int *__restrict sorted_rows_by_color,\n        const int num_rows_per_color,\n        const int current_color,\n        const int *__restrict row_colors,\n        const Matrix_type *Einv,\n        const ColoringType boundary_coloring,\n        const int boundary_index )\n{\n    // Number of half warps per CTA.\n    const int NUM_HALF_WARPS = CTA_SIZE / 16;\n    // Coordinates of the thread.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Coordinates of the thread in the CTA.\n    const int thread_id_div_16 = threadIdx.x / 16;\n    const int thread_id_mod_16 = threadIdx.x % 16;\n    // Useful constants.\n    const int thread_id_mod_16_div_4 = thread_id_mod_16 / 4;\n    const int thread_id_mod_16_mod_4 = thread_id_mod_16 % 4;\n    const int shfl_offset = 16 * (lane_id / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[16 * thread_id_div_16];\n    // The iterator over rows.\n    int a_row_it = blockIdx.x * NUM_HALF_WARPS + thread_id_div_16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_HALF_WARPS )\n    {\n        unsigned int active_mask = utils::activemask();\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_bmAx = __cachingLoad(&b[4 * a_row_id + thread_id_mod_16_mod_4]);\n        }\n\n        // The range of the row.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If it has an external diagonal, we need one more item to put the diag.\n        int a_col_max = a_col_end;\n\n        if ( HAS_EXTERNAL_DIAG )\n        {\n            ++a_col_max;\n        }\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; a_col_begin < a_col_max ; a_col_begin += 16 )\n        {\n            unsigned int active_mask_inner = utils::activemask();\n            int a_col_it = a_col_begin + thread_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = __cachingLoad(&A_cols[a_col_it]);\n            }\n\n            if ( HAS_EXTERNAL_DIAG && a_col_it == a_col_end )\n            {\n                a_col_id = a_row_id;\n            }\n\n            // Determine if the color is valid.\n            int a_col_is_valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    a_col_is_valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    a_col_is_valid = a_col_id < boundary_index && __cachingLoad(&row_colors[a_col_id]) < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                a_col_is_valid = row_colors[a_col_id] < current_color;\n            }\n\n#endif\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + thread_id_mod_16_div_4;\n                // Load 8 blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, warpSize, active_mask_inner );\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k, warpSize, active_mask_inner );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = __cachingLoad(&x[4 * uniform_a_col_id + thread_id_mod_16_mod_4]);\n                }\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid )\n                {\n                    my_x += delta[4 * uniform_a_col_id + thread_id_mod_16_mod_4];\n                }\n\n                my_s_mem[thread_id_mod_16] = my_x;\n                int uniform_a_col_tmp = a_col_begin + my_k, uniform_a_col_it = -1;\n\n                if ( uniform_a_col_tmp < a_col_end )\n                {\n                    uniform_a_col_it = uniform_a_col_tmp;\n                }\n\n                if ( HAS_EXTERNAL_DIAG && uniform_a_col_tmp == a_col_end )\n                {\n                    uniform_a_col_it = A_diag[a_row_id];\n                }\n\n                Matrix_type my_vals[4] = { Matrix_type(0) };\n\n                if ( uniform_a_col_it != -1 )\n                {\n                    utils::load_vec4( my_vals, &A_vals[16 * uniform_a_col_it + 4 * thread_id_mod_16_mod_4] );\n                }\n\n                my_bmAx -= my_vals[0] * my_s_mem[4 * thread_id_mod_16_div_4 + 0];\n                my_bmAx -= my_vals[1] * my_s_mem[4 * thread_id_mod_16_div_4 + 1];\n                my_bmAx -= my_vals[2] * my_s_mem[4 * thread_id_mod_16_div_4 + 2];\n                my_bmAx -= my_vals[3] * my_s_mem[4 * thread_id_mod_16_div_4 + 3];\n            }\n        }\n\n        // Load Einvs.\n        Matrix_type my_Einv = Einv[16 * a_row_id + thread_id_mod_16];\n        // Reduce bmAx terms.\n        my_bmAx += utils::shfl_xor( my_bmAx, 4, warpSize, active_mask );\n        my_bmAx += utils::shfl_xor( my_bmAx, 8, warpSize, active_mask );\n\n        // Update the shared terms.\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_s_mem[thread_id_mod_16_mod_4] = my_bmAx;\n        }\n\n        // Update the diagonal term.\n        my_bmAx = my_Einv * my_s_mem[thread_id_mod_16_mod_4];\n        // Reduce bmAx terms.\n        my_bmAx += utils::shfl_xor( my_bmAx, 1, warpSize, active_mask );\n        my_bmAx += utils::shfl_xor( my_bmAx, 2, warpSize, active_mask );\n\n        // Store the results.\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            delta[4 * a_row_id + thread_id_mod_16_div_4] = my_bmAx;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_forward_1x1_kernel( const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Matrix_type *__restrict A_vals,\n                              const int *__restrict A_diag,\n                              const Vector_type *x,\n                              const Vector_type *b,\n                              Vector_type *__restrict delta,\n                              const int *__restrict sorted_rows_by_color,\n                              const int num_rows_per_color,\n                              const int current_color,\n                              const int *__restrict row_colors,\n                              const Matrix_type *__restrict Einv,\n                              const ColoringType boundary_coloring,\n                              const int boundary_index )\n{\n    // Number of items per CTA.\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    // Number of items per grid.\n    const int NUM_ROWS_PER_GRID = gridDim.x * NUM_ROWS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ROWS_PER_CTA + (threadIdx.x / NUM_THREADS_PER_ROW);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ROWS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_bmAx = amgx::types::util<Vector_type>::get_zero();\n\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            my_bmAx = __cachingLoad(&b[a_row_id]);\n        }\n\n        // If it has an external diag.\n        if ( HAS_EXTERNAL_DIAG && lane_id_mod_NTPR == 0 )\n        {\n            my_bmAx -= A_vals[A_diag[a_row_id]] * x[a_row_id];\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_it  = A_rows[a_row_id  ];\n        int a_col_end = A_rows[a_row_id + 1];\n\n        // If the diagonal is stored separately, we have a special treatment.\n        //if( HAS_EXTERNAL_DIAG )\n        //  ++a_col_end;\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( a_col_it += lane_id_mod_NTPR ; utils::any( a_col_it < a_col_end ) ; a_col_it += NUM_THREADS_PER_ROW )\n        {\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Ignore the diagonal element since its color is smaller, and been accounted for above\n            if (HAS_EXTERNAL_DIAG && a_col_id == a_row_id)\n            {\n                a_col_id = -1;\n            }\n\n            // Load x.\n            Vector_type my_x(0);\n\n            if ( a_col_id != -1 )\n            {\n                my_x = __cachingLoad(&x[a_col_id]);\n            }\n\n            // Is it really a valid column (due to coloring).\n            int valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == FIRST )\n                {\n                    valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_id < boundary_index && row_colors[a_col_id] < current_color;\n                }\n            }\n\n#else\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                valid = row_colors[a_col_tmp] < current_color;\n            }\n\n#endif\n\n            // Load my x value.\n            if ( valid )\n            {\n                my_x += delta[a_col_id];\n            }\n\n            // Load my item from A.\n            Matrix_type my_val(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                my_val = A_vals[a_col_it];\n            }\n\n            // Update bmAx.\n            my_bmAx -= my_val * my_x;\n        }\n\n        // Reduce bmAx terms.\n#pragma unroll\n\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_bmAx += utils::shfl_xor( my_bmAx, mask );\n        }\n\n        // Store the results.\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            delta[a_row_id] = Einv[a_row_id] * my_bmAx;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_NxN_kernel( const int *__restrict A_rows,\n                               const int *__restrict A_cols,\n                               const Matrix_type *__restrict A_vals,\n                               Vector_type *__restrict x,\n                               const WeightType weight,\n                               const int *__restrict sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const Matrix_type *__restrict Einv,\n                               const Vector_type *delta,\n                               Vector_type *__restrict Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_rows_per_color;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_end ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_tmp = -1, a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_tmp = A_cols[a_col_it];\n            }\n\n            // Make sure the column is interesting.\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_tmp != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_tmp >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_tmp < boundary_index && row_colors[a_col_tmp] > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_tmp != -1 && row_colors[a_col_tmp] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( valid )\n            {\n                a_col_id = a_col_tmp;\n            }\n\n            // Count the number of active columns.\n            // int vote =  utils::ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_x = Delta[N * uniform_a_col_id + lane_id_mod_NxN_mod_N];\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    //if( uniform_a_col_id == -1 )\n                    //  break;\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_delta += my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_delta += my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Matrix_type my_Einv = Einv[NxN * a_row_id + lane_id_mod_NxN];\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_delta = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_delta, is_leader );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_delta;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_delta;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_delta = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_delta = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_delta = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_delta, is_leader );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            const int offset = N * a_row_id + lane_id_mod_NxN_div_N;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n        else\n        {\n            const int offset = N * a_row_id + lane_id_mod_NxN_mod_N;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE, int WARP_SIZE, bool ROW_MAJOR, int NUM_WARP_ITERS_PER_BLOCK >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_NxN_kernel_large( const int *__restrict A_rows,\n                                     const int *__restrict A_cols,\n                                     const Matrix_type *__restrict A_vals,\n                                     Vector_type *__restrict x,\n                                     const WeightType weight,\n                                     const int *__restrict sorted_rows_by_color,\n                                     const int *__restrict row_colors,\n                                     const Matrix_type *__restrict Einv,\n                                     const Vector_type *delta,\n                                     Vector_type *__restrict Delta,\n                                     const int num_rows_per_color,\n                                     const int current_color,\n                                     const ColoringType boundary_coloring,\n                                     const int boundary_index )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_N = lane_id / N;\n    const int lane_id_mod_N = lane_id % N; // id of a lane inside the block\n    const int blocks_per_warp = WARP_SIZE / N; // we process this cols per warp per row\n    const int row_elems_per_warp  = blocks_per_warp * N;\n    // Shared to store t_delta\n    __shared__ volatile Vector_type delta_s[CTA_SIZE * NUM_WARP_ITERS_PER_BLOCK];\n    volatile Vector_type *my_delta_s = &delta_s[warp_id * NUM_WARP_ITERS_PER_BLOCK * WARP_SIZE];\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Accumulator\n        Vector_type my_delta(0);\n        //Vector_type mAx[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int i = 0; i < NUM_WARP_ITERS_PER_BLOCK; i++)\n        {\n            my_delta_s[WARP_SIZE * i + lane_id] = 0.0;\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_end ) ; a_col_begin += WARP_SIZE )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id;\n            // Get the ID of the column.\n            int a_col_tmp = -1, a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_tmp = A_cols[a_col_it];\n            }\n\n            // Make sure the column is interesting.\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_tmp != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_tmp >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_tmp < boundary_index && row_colors[a_col_tmp] > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_tmp != -1 && row_colors[a_col_tmp] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( valid )\n            {\n                a_col_id = a_col_tmp;\n            }\n\n            // Loop over columns. We compute blocks_per_warp columns per iteration.\n            for ( int k = 0 ; k < WARP_SIZE ; k += blocks_per_warp )\n            {\n                // id of the processed block by this thread\n                int my_k = k + lane_id_div_N;\n                // Load N blocks of X (if valid)\n                int uniform_a_col_id = utils::shfl( a_col_id, my_k );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && lane_id < row_elems_per_warp)\n                {\n                    my_x = Delta[N * uniform_a_col_id + lane_id_mod_N];\n                }\n\n                // Load blocks of A.\n                // for each block in a batch\n#pragma unroll\n\n                for ( int i = 0 ; i < blocks_per_warp ; ++i )\n                {\n                    // k-th batch of blocks, i-th block. each thread process a column/row of a_it = uniform_a_col_tmp\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    // check if we are going out of bounds/color\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    // swipe with the whole warp\n                    if (uniform_a_col_it != -1)\n                    {\n                        int block_inside_id = lane_id;\n#pragma unroll\n\n                        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n                        {\n                            Matrix_type my_val(0);\n\n                            if ( uniform_a_col_it != -1 && block_inside_id < NxN)\n                            {\n                                my_val = A_vals[NxN * uniform_a_col_it + block_inside_id];\n                            }\n\n                            my_delta_s[block_inside_id] -= my_val * utils::shfl(my_x, N * i + block_inside_id % N); //my_s_mem[N*i + block_inside_id % N]; // MOD IS SLOW!\n                            block_inside_id += WARP_SIZE;\n                        }\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load Einvs.\n        Vector_type my_Einv[NUM_WARP_ITERS_PER_BLOCK];\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            my_Einv[j] = 0.0;\n        }\n\n#pragma unroll\n\n        for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n        {\n            if ((WARP_SIZE * j + lane_id) < NxN)\n            {\n                my_Einv[j] = Einv[NxN * a_row_id + WARP_SIZE * j + lane_id];\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_delta += my_delta_s[N * lane_id + i];\n                }\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            int block_inside_id = lane_id;\n#pragma unroll\n\n            for (int j = 0; j < NUM_WARP_ITERS_PER_BLOCK; j++)\n            {\n                my_delta_s[block_inside_id] = my_Einv[j] * utils::shfl(my_delta, block_inside_id % N);\n                block_inside_id += WARP_SIZE;\n            }\n        }\n\n        // Reduce bmAx terms.\n        {\n            my_delta = 0.0;\n#pragma unroll\n\n            for ( int i = 0 ; i < N ; ++i )\n            {\n                if ( lane_id < N )\n                {\n                    my_delta += my_delta_s[N * lane_id + i];\n                }\n            }\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            const int offset = N * a_row_id + lane_id;\n            Vector_type my_b(0), my_x(0);\n\n            if ( lane_id < N )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x    [offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( lane_id < N )\n            {\n                x    [offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, typename WeightType, int CTA_SIZE, bool ROW_MAJOR >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_4x4_kernel( const IndexType *row_offsets,\n                               const IndexType *column_indices,\n                               const ValueTypeA *nonzero_values,\n                               ValueTypeB *x,\n                               const WeightType weight,\n                               const int *sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const ValueTypeA *Einv,\n                               const ValueTypeB *delta,\n                               ValueTypeB *Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const IndexType boundary_index)\n{\n    const int nHalfWarps = CTA_SIZE / 16; // Number of half warps per CTA.\n    const int laneId = utils::lane_id();\n    const int halfWarpId = threadIdx.x / 16;\n    const int halfLaneId = threadIdx.x % 16;\n    const int halfLaneId_div_4 = halfLaneId / 4;\n    const int halfLaneId_mod_4 = halfLaneId % 4;\n    const int upperHalf = 16 * (laneId / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile ValueTypeB s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile ValueTypeB *my_s_mem = &s_mem[16 * halfWarpId];\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( int aRowIt = blockIdx.x * nHalfWarps + halfWarpId ; aRowIt < num_rows_per_color ; aRowIt += gridDim.x * nHalfWarps )\n    {\n        int aRowId = sorted_rows_by_color[aRowIt];\n        // Load one block of B.\n        ValueTypeB my_delta(0);\n        // The range of the rows.\n        int aColBegin = row_offsets[aRowId  ];\n        int aColEnd   = row_offsets[aRowId + 1];\n\n        // Each warp load column indices of 16 nonzero blocks\n        for ( ; aColBegin < aColEnd ; aColBegin += 16 )\n        {\n            int aColIt = aColBegin + halfLaneId;\n            // Get the ID of the column.\n            int aColTmp = -1, aColId = -1;\n\n            if ( aColIt < aColEnd )\n            {\n                aColTmp = column_indices[aColIt];\n            }\n\n#ifdef AMGX_ILU_COLORING\n            bool valid = (((aColTmp < boundary_index || boundary_coloring == SYNC_COLORS) && (row_colors[aColTmp] > current_color)) || (aColTmp >= boundary_index && boundary_coloring == LAST));\n\n            if ( aColTmp != -1 && valid )\n            {\n                aColId = aColTmp;\n            }\n\n#else\n\n            if ( aColTmp != -1 && row_colors[aColTmp] > current_color )\n            {\n                aColId = aColTmp;\n            }\n\n#endif\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + halfLaneId_div_4;\n                // Exchange column indices.\n                int waColId = utils::shfl( aColId, upperHalf + my_k );\n                // Load 8 blocks of X if needed.\n                ValueTypeB my_x(0);\n\n                if ( waColId != -1 )\n                {\n                    my_x = Delta[4 * waColId + halfLaneId_mod_4];\n                }\n\n                my_s_mem[halfLaneId] = my_x;\n                // Load 8 blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < 4 ; ++i )\n                {\n                    const int k_i = k + i;\n                    int w_aColTmp = aColBegin + k_i, w_aColIt = -1;\n                    if ( utils::shfl( aColId, upperHalf + k_i ) != -1 && w_aColTmp < aColEnd )\n                        w_aColIt = w_aColTmp;\n\n                    ValueTypeA my_val(0);\n\n                    if ( w_aColIt != -1 )\n                    {\n                        my_val = nonzero_values[16 * w_aColIt + halfLaneId];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_delta += my_val * my_s_mem[4 * i + halfLaneId_mod_4];\n                    }\n                    else\n                    {\n                        my_delta += my_val * my_s_mem[4 * i + halfLaneId_div_4];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load EINV values.\n        ValueTypeA my_Einv = Einv[16 * aRowId + halfLaneId];\n        // Reduce delta terms.\n        if ( ROW_MAJOR )\n        {\n            my_delta += utils::shfl_xor( my_delta, 1 );\n            my_delta += utils::shfl_xor( my_delta, 2 );\n        }\n        else\n        {\n            my_delta += utils::shfl_xor( my_delta, 4 );\n            my_delta += utils::shfl_xor( my_delta, 8 );\n        }\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_s_mem[halfLaneId_div_4] = my_delta;\n            }\n        }\n        else\n        {\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_s_mem[halfLaneId_mod_4] = my_delta;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_delta = my_Einv * my_s_mem[halfLaneId_mod_4];\n        }\n        else\n        {\n            my_delta = my_Einv * my_s_mem[halfLaneId_div_4];\n        }\n\n        // Regroup results.\n        if ( ROW_MAJOR )\n        {\n            my_delta += utils::shfl_xor( my_delta, 1 );\n            my_delta += utils::shfl_xor( my_delta, 2 );\n        }\n        else\n        {\n            my_delta += utils::shfl_xor( my_delta, 4 );\n            my_delta += utils::shfl_xor( my_delta, 8 );\n        }\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            int offset = 4 * aRowId + halfLaneId_div_4;\n            ValueTypeB my_b(0), my_x(0);\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x[offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( halfLaneId_mod_4 == 0 )\n            {\n                x[offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n        else\n        {\n            int offset = 4 * aRowId + halfLaneId_mod_4;\n            ValueTypeB my_b(0), my_x(0);\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                my_b = __cachingLoad(&delta[offset]);\n                my_x = x[offset];\n            }\n\n            my_delta = my_b - my_delta;\n\n            if ( halfLaneId_div_4 == 0 )\n            {\n                x[offset] = my_x + weight * my_delta;\n                Delta[offset] = my_delta;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int CTA_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_4x4_kernel_row_major_vec4( const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Matrix_type *__restrict A_vals,\n        Vector_type *__restrict x,\n        const WeightType weight,\n        const int *__restrict sorted_rows_by_color,\n        const int *__restrict row_colors,\n        const Matrix_type *__restrict Einv,\n        const Vector_type *delta,\n        Vector_type *__restrict Delta,\n        const int num_rows_per_color,\n        const int current_color,\n        const ColoringType boundary_coloring,\n        const int boundary_index )\n{\n    // Number of half warps per CTA.\n    const int NUM_HALF_WARPS = CTA_SIZE / 16;\n    // Coordinates of the thread.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Coordinates of the thread in the CTA.\n    const int thread_id_div_16 = threadIdx.x / 16;\n    const int thread_id_mod_16 = threadIdx.x % 16;\n    // Useful constants.\n    const int thread_id_mod_16_div_4 = thread_id_mod_16 / 4;\n    const int thread_id_mod_16_mod_4 = thread_id_mod_16 % 4;\n    const int shfl_offset = 16 * (lane_id / 16);\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[16 * thread_id_div_16];\n    // The iterator over rows.\n    int a_row_it = blockIdx.x * NUM_HALF_WARPS + thread_id_div_16;\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_HALF_WARPS )\n    {\n        unsigned int active_mask = utils::activemask();\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; a_col_begin < a_col_end ; a_col_begin += 16 )\n        {\n            unsigned int active_mask_inner = utils::activemask();\n            int a_col_it = a_col_begin + thread_id_mod_16;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = __cachingLoad(&A_cols[a_col_it]);\n            }\n\n#ifdef AMGX_ILU_COLORING\n            int valid = false;\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                if ( boundary_coloring == LAST )\n                {\n                    valid = a_col_id >= boundary_index;\n                }\n                else\n                {\n                    valid = a_col_id < boundary_index && __cachingLoad(&row_colors[a_col_id]) > current_color;\n                }\n            }\n\n#else\n            int valid = false;\n\n            if ( a_col_id != -1 && row_colors[a_col_id] > current_color )\n            {\n                valid = true;\n            }\n\n#endif\n\n            // Set the column id.\n            if ( !valid )\n            {\n                a_col_id = -1;\n            }\n\n            // Loop over columns. We compute 8 columns per iteration.\n#pragma unroll 2\n\n            for ( int k = 0 ; k < 16 ; k += 4 )\n            {\n                int my_k = k + thread_id_mod_16_div_4;\n                // Load 8 blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, warpSize, active_mask_inner );\n                Vector_type my_Delta(0);\n\n                if ( uniform_a_col_id != -1 )\n                {\n                    my_Delta = Delta[4 * uniform_a_col_id + thread_id_mod_16_mod_4];\n                }\n\n                my_s_mem[thread_id_mod_16] = my_Delta;\n                int uniform_a_col_it = a_col_begin + my_k;\n\n                if ( uniform_a_col_id == -1 || uniform_a_col_it >= a_col_end )\n                {\n                    uniform_a_col_it = -1;\n                }\n\n                Matrix_type my_vals[4] = { Matrix_type(0) };\n\n                if ( uniform_a_col_it != -1 )\n                {\n                    utils::load_vec4( my_vals, &A_vals[16 * uniform_a_col_it + 4 * thread_id_mod_16_mod_4] );\n                }\n\n                my_delta += my_vals[0] * my_s_mem[4 * thread_id_mod_16_div_4 + 0];\n                my_delta += my_vals[1] * my_s_mem[4 * thread_id_mod_16_div_4 + 1];\n                my_delta += my_vals[2] * my_s_mem[4 * thread_id_mod_16_div_4 + 2];\n                my_delta += my_vals[3] * my_s_mem[4 * thread_id_mod_16_div_4 + 3];\n            } // Loop over k\n        } // Loop over aColIt\n\n        // Load EINV values.\n        Matrix_type my_Einv = Einv[16 * a_row_id + thread_id_mod_16];\n        // Reduce delta terms.\n        my_delta += utils::shfl_xor( my_delta, 4, warpSize, active_mask );\n        my_delta += utils::shfl_xor( my_delta, 8, warpSize, active_mask );\n\n        // Update the shared terms.\n        if ( thread_id_mod_16_div_4 == 0 )\n        {\n            my_s_mem[thread_id_mod_16_mod_4] = my_delta;\n        }\n\n        // Update the diagonal term.\n        my_delta = my_Einv * my_s_mem[thread_id_mod_16_mod_4];\n        // Regroup results.\n        my_delta += utils::shfl_xor( my_delta, 1, warpSize, active_mask );\n        my_delta += utils::shfl_xor( my_delta, 2, warpSize, active_mask );\n        // Store the results.\n        int offset = 4 * a_row_id + thread_id_mod_16_div_4;\n        Vector_type my_b(0), my_x(0);\n\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            my_b = __cachingLoad(&delta[offset]);\n            my_x = x    [offset];\n        }\n\n        my_delta = my_b - my_delta;\n\n        if ( thread_id_mod_16_mod_4 == 0 )\n        {\n            x    [offset] = my_x + weight * my_delta;\n            Delta[offset] = my_delta;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid DILU_backward_1x1_kernel( const int *__restrict A_rows,\n                               const int *__restrict A_cols,\n                               const Matrix_type *__restrict A_vals,\n                               Vector_type *__restrict x,\n                               const WeightType weight,\n                               const int *__restrict sorted_rows_by_color,\n                               const int *__restrict row_colors,\n                               const Matrix_type *__restrict Einv,\n                               const Vector_type *delta,\n                               Vector_type *__restrict Delta,\n                               const int num_rows_per_color,\n                               const int current_color,\n                               const ColoringType boundary_coloring,\n                               const int boundary_index )\n{\n    // Number of items per CTA.\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    // Number of items per grid.\n    const int NUM_ROWS_PER_GRID = gridDim.x * NUM_ROWS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_mod_NTPR = lane_id % NUM_THREADS_PER_ROW;\n    // Determine which NxN block the threads work with.\n    int a_row_it = blockIdx.x * NUM_ROWS_PER_CTA + (threadIdx.x / NUM_THREADS_PER_ROW);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += NUM_ROWS_PER_GRID )\n    {\n        int a_row_id = sorted_rows_by_color[a_row_it];\n        // Load one block of B.\n        Vector_type my_delta(0);\n        // Don't do anything if X is zero.\n        int a_col_it  = A_rows[a_row_id  ];\n        int a_col_end = A_rows[a_row_id + 1];\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( a_col_it += lane_id_mod_NTPR ; utils::any( a_col_it < a_col_end ) ; a_col_it += NUM_THREADS_PER_ROW )\n        {\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it really a valid column (due to coloring).\n            int valid = false;\n#ifdef AMGX_ILU_COLORING\n\n            if ( a_col_id != -1 && current_color != 0 )\n            {\n                //if( boundary_coloring == LAST )\n                //  valid = a_col_id >= boundary_index;\n                //else\n                //  valid = a_col_id < boundary_index && row_colors[a_col_id] > current_color;\n                valid = (((a_col_id < boundary_index || boundary_coloring == SYNC_COLORS) && (row_colors[a_col_id] > current_color)) || (a_col_id >= boundary_index && boundary_coloring == LAST));\n            }\n\n#else\n\n            //if( a_col_id != -1 && current_color != 0 )\n            if ( a_col_id != -1  )\n            {\n                valid = row_colors[a_col_id] > current_color;\n            }\n\n#endif\n            // Load my Delta value.\n            Vector_type my_Delta(0);\n\n            if ( valid )\n            {\n                my_Delta = Delta[a_col_id];\n            }\n\n            // Load my item from A.\n            Matrix_type my_val(0);\n\n            if ( valid )\n            {\n                my_val = A_vals[a_col_it];\n            }\n\n            // Update bmAx.\n            my_delta += my_val * my_Delta;\n        }\n\n        // Reduce bmAx terms.\n#pragma unroll\n\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_delta += utils::shfl_xor( my_delta, mask );\n        }\n\n        // Store the results.\n        if ( lane_id_mod_NTPR == 0 )\n        {\n            Vector_type my_x = __cachingLoad(&delta[a_row_id]) - Einv[a_row_id] * my_delta;\n            x    [a_row_id] += weight * my_x;\n            Delta[a_row_id]  = my_x;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int CTA_SIZE >\n__global__\n__launch_bounds__( CTA_SIZE, 16 )\nvoid DILU_backward_NxN_kernel_skip( Vector_type *__restrict x,\n                                    const WeightType weight,\n                                    const int *__restrict sorted_rows_by_color,\n                                    const Vector_type *delta,\n                                    Vector_type *__restrict Delta,\n                                    const int num_rows_per_color )\n{\n    const int NUM_ITEMS_PER_CTA = CTA_SIZE / N; // Number of updated block items per CTA\n    const int ITEM_ID           = threadIdx.x / N;\n    const int ITEM_BLOCK_OFFSET = threadIdx.x % N;\n    const int is_active    = ITEM_ID < NUM_ITEMS_PER_CTA;\n    // The first row.\n    int a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + ITEM_ID;\n\n    // Iterate over the rows of the matrix. One warp per two rows.\n    for ( ; a_row_it < num_rows_per_color ; a_row_it += gridDim.x * NUM_ITEMS_PER_CTA )\n    {\n        if ( is_active )\n        {\n            int a_row_id = sorted_rows_by_color[a_row_it];\n            const int idx = N * a_row_id + ITEM_BLOCK_OFFSET;\n            Vector_type my_b = __cachingLoad(&delta[idx]);\n            Vector_type my_x = x[idx];\n            x[idx] = my_x + weight * my_b;\n            Delta[idx] = my_b;\n        }\n    }\n}\n\n// ----------\n// Methods\n// ----------\n\ntemplate< typename Matrix_type, typename Vector_type, int N >\nvoid DILU_forward_NxN_dispatch( const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const Matrix_type *__restrict A_vals,\n                                const int *__restrict A_diag,\n                                const Vector_type *x,\n                                const Vector_type *b,\n                                Vector_type *__restrict delta,\n                                const int *__restrict sorted_rows_by_color,\n                                const int num_rows_per_color,\n                                const int current_color,\n                                const int *__restrict row_colors,\n                                const Matrix_type *__restrict Einv,\n                                const ColoringType boundary_coloring,\n                                const int boundary_index,\n                                const int row_major,\n                                const int has_external_diag )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = std::max(WARP_SIZE / NxN, 1);\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n    // Branch to the correct kernel call.\n    int code = 2 * (row_major ? 1 : 0) + (has_external_diag ? 1 : 0);\n\n    switch ( code )\n    {\n        case 0: // Column-major, no external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 1: // Column-major, external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 2: // Row-major, no external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 3: // Row-major, external diagonal.\n            DILU_forward_NxN_kernel<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\ntemplate< typename Matrix_type, typename Vector_type, int N >\nvoid DILU_forward_NxN_dispatch_large( const int *__restrict A_rows,\n                                      const int *__restrict A_cols,\n                                      const Matrix_type *__restrict A_vals,\n                                      const int *__restrict A_diag,\n                                      const Vector_type *x,\n                                      const Vector_type *b,\n                                      Vector_type *__restrict delta,\n                                      const int *__restrict sorted_rows_by_color,\n                                      const int num_rows_per_color,\n                                      const int current_color,\n                                      const int *__restrict row_colors,\n                                      const Matrix_type *__restrict Einv,\n                                      const ColoringType boundary_coloring,\n                                      const int boundary_index,\n                                      const int row_major,\n                                      const int has_external_diag )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_WARPS_PER_CTA;\n    // Each warp is going to sweep through bloock this many times\n    const int NUM_WARP_ITERS_PER_BLOCK = ((NxN - 1) / WARP_SIZE) + 1;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if (!row_major)\n    {\n        FatalError(\"COL MAJOR is not supported for this large block_size\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    switch ( has_external_diag )\n    {\n        case 0: // Row-major, no external diagonal.\n            DILU_forward_NxN_kernel_large<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        case 1: // Row-major, external diagonal.\n            DILU_forward_NxN_kernel_large<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type >\nvoid DILU_forward_NxN_dispatch( const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const Matrix_type *__restrict A_vals,\n                                const int *__restrict A_diag,\n                                const Vector_type *x,\n                                const Vector_type *b,\n                                Vector_type *__restrict delta,\n                                const int *__restrict sorted_rows_by_color,\n                                const int num_rows_per_color,\n                                const int current_color,\n                                const int *__restrict row_colors,\n                                const Matrix_type *__restrict Einv,\n                                const ColoringType boundary_coloring,\n                                const int boundary_index,\n                                const int block_size,\n                                const int row_major,\n                                const int has_external_diag )\n{\n    switch ( block_size )\n    {\n        case 1:\n            {\n                const int NUM_THREADS_PER_ROW = 8;\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n                if ( has_external_diag )\n                {\n                    DILU_forward_1x1_kernel<Matrix_type, Vector_type, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                }\n                else\n                {\n                    DILU_forward_1x1_kernel<Matrix_type, Vector_type, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                }\n\n                cudaCheckError();\n            }\n            break;\n\n        case 2:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 3:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 3>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 4:\n            if ( row_major )\n            {\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / 16;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n                if ( has_external_diag )\n                    //DILU_forward_4x4_kernel<Matrix_type, Vector_type, CTA_SIZE, WARP_SIZE, true, true><<<grid_size, CTA_SIZE>>>(\n                    DILU_forward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, CTA_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n                else\n                    DILU_forward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, CTA_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n                        A_rows,\n                        A_cols,\n                        A_vals,\n                        A_diag,\n                        x,\n                        b,\n                        delta,\n                        sorted_rows_by_color,\n                        num_rows_per_color,\n                        current_color,\n                        row_colors,\n                        Einv,\n                        boundary_coloring,\n                        boundary_index );\n\n                cudaCheckError();\n            }\n            else\n                DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 4>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    A_diag,\n                    x,\n                    b,\n                    delta,\n                    sorted_rows_by_color,\n                    num_rows_per_color,\n                    current_color,\n                    row_colors,\n                    Einv,\n                    boundary_coloring,\n                    boundary_index,\n                    row_major,\n                    has_external_diag );\n\n            break;\n\n        case 5:\n            DILU_forward_NxN_dispatch<Matrix_type, Vector_type, 5>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 8:\n            DILU_forward_NxN_dispatch_large<Matrix_type, Vector_type, 8>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        case 10:\n            DILU_forward_NxN_dispatch_large<Matrix_type, Vector_type, 10>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                delta,\n                sorted_rows_by_color,\n                num_rows_per_color,\n                current_color,\n                row_colors,\n                Einv,\n                boundary_coloring,\n                boundary_index,\n                row_major,\n                has_external_diag );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N >\nvoid DILU_backward_NxN_dispatch( const int *__restrict A_rows,\n                                 const int *__restrict A_cols,\n                                 const Matrix_type *__restrict A_vals,\n                                 Vector_type *__restrict x,\n                                 const WeightType weight,\n                                 const int *__restrict sorted_rows_by_color,\n                                 const int *__restrict row_colors,\n                                 const Matrix_type *__restrict Einv,\n                                 const Vector_type *delta,\n                                 Vector_type *__restrict Delta,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 const ColoringType boundary_coloring,\n                                 const int boundary_index,\n                                 const int row_major )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = std::max(WARP_SIZE / NxN, 1);\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if ( row_major )\n    {\n        DILU_backward_NxN_kernel<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, true> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n    else\n    {\n        DILU_backward_NxN_kernel<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, false> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n\n    cudaCheckError();\n}\n\n\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType, int N, int NUM_WARP_ITERS_PER_BLOCK >\nvoid DILU_backward_NxN_dispatch_large( const int *__restrict A_rows,\n                                       const int *__restrict A_cols,\n                                       const Matrix_type *__restrict A_vals,\n                                       Vector_type *__restrict x,\n                                       const WeightType weight,\n                                       const int *__restrict sorted_rows_by_color,\n                                       const int *__restrict row_colors,\n                                       const Matrix_type *__restrict Einv,\n                                       const Vector_type *delta,\n                                       Vector_type *__restrict Delta,\n                                       const int num_rows_per_color,\n                                       const int current_color,\n                                       const ColoringType boundary_coloring,\n                                       const int boundary_index,\n                                       const int row_major )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n    // Branch to the correct kernel call.\n    if ( row_major )\n    {\n        DILU_backward_NxN_kernel_large<Matrix_type, Vector_type, WeightType, N, CTA_SIZE, WARP_SIZE, true, NUM_WARP_ITERS_PER_BLOCK> <<< grid_size, CTA_SIZE>>>(\n            A_rows,\n            A_cols,\n            A_vals,\n            x,\n            weight,\n            sorted_rows_by_color,\n            row_colors,\n            Einv,\n            delta,\n            Delta,\n            num_rows_per_color,\n            current_color,\n            boundary_coloring,\n            boundary_index );\n    }\n    else\n    {\n        FatalError(\"col major is not supported for this blocksize in multicolor DILU solver\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Matrix_type, typename Vector_type, typename WeightType>\nvoid DILU_backward_NxN_dispatch( const int *__restrict A_rows,\n                                 const int *__restrict A_cols,\n                                 const Matrix_type *__restrict A_vals,\n                                 Vector_type *__restrict x,\n                                 const WeightType weight,\n                                 const int *__restrict sorted_rows_by_color,\n                                 const int *__restrict row_colors,\n                                 const Matrix_type *__restrict Einv,\n                                 const Vector_type *delta,\n                                 Vector_type *__restrict Delta,\n                                 const int num_rows_per_color,\n                                 const int current_color,\n                                 const ColoringType boundary_coloring,\n                                 const int boundary_index,\n                                 const int block_size,\n                                 const int row_major )\n{\n    switch ( block_size )\n    {\n        case 1:\n            {\n                const int NUM_THREADS_PER_ROW = 8;\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n                DILU_backward_1x1_kernel<Matrix_type, Vector_type, WeightType, NUM_THREADS_PER_ROW, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index );\n                cudaCheckError();\n            }\n            break;\n\n        case 2:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 3:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 3>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 4:\n\n            //if( false )\n            if ( row_major )\n            {\n                // Number of items computer per CTA.\n                const int NUM_ROWS_PER_CTA = CTA_SIZE / 16;\n                // The number of threads to launch.\n                const int grid_size = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n                //DILU_backward_NxN_kernel<Matrix_type, Vector_type, 4, CTA_SIZE, WARP_SIZE, true><<<grid_size, CTA_SIZE>>>(\n                DILU_backward_4x4_kernel_row_major_vec4<Matrix_type, Vector_type, WeightType, CTA_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index );\n                cudaCheckError();\n            }\n            else\n                DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 4>(\n                    A_rows,\n                    A_cols,\n                    A_vals,\n                    x,\n                    weight,\n                    sorted_rows_by_color,\n                    row_colors,\n                    Einv,\n                    delta,\n                    Delta,\n                    num_rows_per_color,\n                    current_color,\n                    boundary_coloring,\n                    boundary_index,\n                    row_major );\n\n            break;\n\n        case 5:\n            DILU_backward_NxN_dispatch<Matrix_type, Vector_type, WeightType, 5>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 8:\n            DILU_backward_NxN_dispatch_large<Matrix_type, Vector_type, WeightType, 8, 2>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        case 10:\n            DILU_backward_NxN_dispatch_large<Matrix_type, Vector_type, WeightType, 10, 4>(\n                A_rows,\n                A_cols,\n                A_vals,\n                x,\n                weight,\n                sorted_rows_by_color,\n                row_colors,\n                Einv,\n                delta,\n                Delta,\n                num_rows_per_color,\n                current_color,\n                boundary_coloring,\n                boundary_index,\n                row_major );\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< class T_Config >\nMulticolorDILUSolver_Base<T_Config>::MulticolorDILUSolver_Base( AMG_Config &cfg,\n        const std::string &cfg_scope,\n        ThreadManager *tmng ) :\n    Solver<T_Config>( cfg, cfg_scope, tmng )\n{\n    this->weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    this->m_reorder_cols_by_color_desired = (cfg.AMG_Config::template getParameter<int>(\"reorder_cols_by_color\", cfg_scope) != 0);\n    this->m_insert_diagonal_desired = (cfg.AMG_Config::template getParameter<int>(\"insert_diag_while_reordering\", cfg_scope) != 0);\n    this->m_boundary_coloring = cfg.AMG_Config::template getParameter<ColoringType>(\"boundary_coloring\", cfg_scope);\n    this->always_obey_coloring = 0;\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Multicolor DILU smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nMulticolorDILUSolver_Base<T_Config>::~MulticolorDILUSolver_Base()\n{\n    Einv.clear();\n    Einv.shrink_to_fit();\n}\n\ntemplate<class T_Config>\nvoid MulticolorDILUSolver_Base<T_Config>::computeEinv(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    A.setView(this->m_explicit_A->getViewExterior());\n\n    if ( A.get_block_dimx() != A.get_block_dimy() )\n    {\n        FatalError(\"DILU implemented only for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( A.get_block_dimx() > 32) // actually much more less than 32 doe to register file limitations, but...\n    {\n        FatalError(\"DILU implemented only for squared blocks of size <= 32\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    computeEinv_NxN( A, A.get_block_dimx() );\n    A.setView(oldView);\n}\n\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor = \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    m_explicit_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!this->m_explicit_A)\n    {\n        FatalError(\"MulticolorDILUSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    int N = this->m_explicit_A->get_num_cols() * this->m_explicit_A->get_block_dimy();\n\n    if (this->m_explicit_A->getColoringLevel() < 1)\n    {\n        FatalError(\"Matrix must be colored to use multicolor dilu solver. Try setting: coloring_level=1 in the configuration file\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    m_delta.resize(N);\n    m_Delta.resize(N);\n    m_delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_Delta.set_block_dimy(this->m_explicit_A->get_block_dimy());\n    m_delta.set_block_dimx(1);\n    m_Delta.set_block_dimx(1);\n\n    if ( this->m_explicit_A->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\"Multicolor DILU solver only supports row major format for the blocks\", AMGX_ERR_CONFIGURATION);\n    }\n\n    computeEinv( *this->m_explicit_A );\n}\n\n//\ntemplate< class T_Config >\nvoid\nMulticolorDILUSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nMulticolorDILUSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    if ( this->m_explicit_A->get_block_dimx() != this->m_explicit_A->get_block_dimy() )\n    {\n        FatalError(\"DILU implemented only for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( this->m_explicit_A->get_block_dimx() > 32) // actually much more less than 32 doe to register file limitations, but...\n    {\n        FatalError(\"DILU implemented only for squared blocks of size <= 32\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (xIsZero)\n    {\n        x.dirtybit = 0;\n    }\n\n    if (!this->m_explicit_A->is_matrix_singleGPU())\n    {\n        this->m_explicit_A->manager->exchange_halo_async(x, x.tag);\n        this->m_explicit_A->manager->exchange_halo_async(b, b.tag);\n    }\n\n    if (this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior())\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n    }\n\n    ViewType oldView = this->m_explicit_A->currentView();\n    ViewType flags;\n    bool latencyHiding = true;\n\n    if (this->m_explicit_A->is_matrix_singleGPU() || (x.dirtybit == 0 && b.dirtybit == 0))\n    {\n        latencyHiding = false;\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(this->m_explicit_A->getViewExterior());\n    }\n    else\n    {\n        flags = (ViewType)(this->m_explicit_A->getViewInterior());\n        this->m_explicit_A->setViewInterior();\n    }\n\n    if (xIsZero)\n    {\n        thrust_wrapper::fill<T_Config::memSpace>(x.begin(), x.end(), types::util<ValueTypeB>::get_zero());\n        cudaCheckError();\n    }\n\n    this->smooth_NxN(*this->m_explicit_A, b, x, flags);\n\n    if (latencyHiding)\n    {\n        if (!this->m_explicit_A->is_matrix_singleGPU())\n        {\n            this->m_explicit_A->manager->exchange_halo_wait(x, x.tag);\n            this->m_explicit_A->manager->exchange_halo_wait(b, b.tag);\n        }\n\n        this->m_explicit_A->setViewExterior();\n        flags = (ViewType)(~(this->m_explicit_A->getViewInterior()) & this->m_explicit_A->getViewExterior());\n\n        if (flags != 0)\n        {\n            this->smooth_NxN(*this->m_explicit_A, b, x, flags);\n        }\n    }\n\n    x.dirtybit = 1;\n    this->m_explicit_A->setView(oldView);\n    return (this->converged(b, x));\n}\n\ntemplate<class T_Config>\nvoid\nMulticolorDILUSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_host, V, M, I> >::computeEinv_NxN(const Matrix_h &A, const int bsize)\n{\n    FatalError(\"Multicolor DILU smoother not implemented for host format, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_host, V, M, I> >::smooth_NxN( const Matrix_h &A, VVector &b, VVector &x, ViewType separation_flag )\n{\n    FatalError(\"Haven't implemented Multicolor DILU smoother for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nMulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::MulticolorDILUSolver(\n    AMG_Config &cfg,\n    const std::string &cfg_scope,\n    ThreadManager *tmng ) :\n    MulticolorDILUSolver_Base<TemplateConfig<AMGX_device, V, M, I> >( cfg, cfg_scope, tmng )\n{\n    int device = 0;\n    hipGetDevice( &device );\n    cudaCheckError();\n    hipDeviceProp_t properties;\n    hipGetDeviceProperties( &properties, device );\n    cudaCheckError();\n    m_is_kepler = properties.major >= 3;\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::computeEinv_NxN(const Matrix_d &A, const int bsize)\n{\n    const int bsize_sq = bsize * bsize;\n    this->Einv.resize( A.get_num_cols()*bsize_sq, 0.0 );\n\n    // sol::prof_start();\n    for ( int i = 0, num_colors = A.getMatrixColoring().getNumColors() ; i < num_colors ; ++i )\n    {\n        const int color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        const int num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1] - color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        const int CTA_SIZE = 128;\n        const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n        int ROWS_PER_WARP = 1;\n\n        if ( bsize_sq > 1 && bsize_sq < 6 )\n        {\n            ROWS_PER_WARP = 32 / bsize_sq;\n        }\n\n        const int ROWS_PER_CTA = ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n        const int GRID_SIZE = std::min( 4096, (num_rows_per_color + ROWS_PER_CTA - 1) / ROWS_PER_CTA );\n        hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n\n        switch ( bsize )\n        {\n            case 1:\n                DILU_setup_1x1_kernel<ValueTypeA, ValueTypeB, 8, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 2:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 2, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 3:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 3, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 4:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 4, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 5:\n                DILU_setup_NxN_kernel<ValueTypeA, ValueTypeB, 5, CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 8:\n                DILU_setup_NxN_kernel_large<ValueTypeA, ValueTypeB, 8, CTA_SIZE, 32, 2> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            case 10:\n                DILU_setup_NxN_kernel_large<ValueTypeA, ValueTypeB, 10, CTA_SIZE, 32, 4> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.diag.raw(),\n                    A.values.raw(),\n                    this->Einv.raw(),\n                    A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                    A.getMatrixColoring().getRowColors().raw(),\n                    num_rows_per_color,\n                    i );\n                break;\n\n            default:\n                FatalError( \"Multicolor-DILU Setup: block size was not enabled in the code, contact AMGX developers.\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid MulticolorDILUSolver<TemplateConfig<AMGX_device, V, M, I> >::smooth_NxN( const Matrix_d &A, VVector &b, VVector &x, ViewType separation_flag )\n{\n    AMGX_CPU_PROFILER( \"MulticolorDILUSolver::smooth_NxN \" );\n    int offset = 0, separation = 0;\n    A.getOffsetAndSizeForView(INTERIOR, &offset, &separation);\n\n    // Only have separation=num interior rows if we are only working on the interior\n    // and the boundary coloring is FIRST or LAST, otherwise set separation offset to\n    // total number of rows\n    if ( separation_flag != this->m_explicit_A->getViewInterior() ||\n            this->m_explicit_A->getViewExterior() == this->m_explicit_A->getViewInterior() ||\n            this->m_boundary_coloring != LAST && this->m_boundary_coloring != FIRST )\n    {\n        separation = A.row_offsets.size() - 1;\n    }\n    else\n    {\n        amgx_printf(\"separation active\\n\");\n    }\n\n    // --------------------\n    // Forward Sweep\n    // --------------------\n    const int num_colors = this->m_explicit_A->getMatrixColoring().getNumColors();\n\n    for ( int i = 0 ; i < num_colors ; ++i )\n    {\n        int color_offset(0);\n\n        if ( separation_flag & INTERIOR )\n        {\n            color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            color_offset = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n\n        int num_rows_per_color(0);\n\n        if ( separation_flag == this->m_explicit_A->getViewInterior() )\n        {\n            num_rows_per_color = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1];\n        }\n\n        num_rows_per_color -= color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        int boundary_index = separation;\n\n        if ( this->m_boundary_coloring == SYNC_COLORS )\n        {\n            boundary_index = A.get_num_rows();\n        }\n\n        DILU_forward_NxN_dispatch(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            A.diag.raw(),\n            x.raw(),\n            b.raw(),\n            this->m_delta.raw(),\n            A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n            num_rows_per_color,\n            i,\n            A.getMatrixColoring().getRowColors().raw(),\n            this->Einv.raw(),\n            this->m_boundary_coloring,\n            boundary_index,\n            A.get_block_dimy(),\n            A.getBlockFormat() == ROW_MAJOR,\n            A.hasProps(DIAG) );\n        cudaCheckError();\n    }\n\n    // --------------------\n    // Backward Sweep\n    // --------------------\n    for ( int i = num_colors - 1 ; i >= 0 ; --i )\n    {\n        int color_offset(0);\n\n        if ( separation_flag & INTERIOR )\n        {\n            color_offset = A.getMatrixColoring().getOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            color_offset = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n\n        int num_rows_per_color(0);\n\n        if ( separation_flag == this->m_explicit_A->getViewInterior() )\n        {\n            num_rows_per_color = A.getMatrixColoring().getSeparationOffsetsRowsPerColor()[i];\n        }\n        else\n        {\n            num_rows_per_color = A.getMatrixColoring().getOffsetsRowsPerColor()[i + 1];\n        }\n\n        num_rows_per_color -= color_offset;\n\n        if ( num_rows_per_color == 0 )\n        {\n            continue;\n        }\n\n        if ( i == num_colors - 1 )\n        {\n            const int NUM_ROWS_PER_CTA = CTA_SIZE / A.get_block_dimy();\n            const int GRID_SIZE = std::min( 4096, (num_rows_per_color + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n\n            switch ( A.get_block_dimy() )\n            {\n                case 1:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 1, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 2:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 2, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 3:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 3, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 4:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 4, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 5:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 5, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 8:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 8, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n\n                case 10:\n                    DILU_backward_NxN_kernel_skip<ValueTypeA, ValueTypeB, WeightType, 10, CTA_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                        x.raw(),\n                        this->weight,\n                        A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                        this->m_delta.raw(),\n                        this->m_Delta.raw(),\n                        num_rows_per_color );\n                    break;\n            }\n\n            cudaCheckError();\n        }\n        else\n        {\n            DILU_backward_NxN_dispatch(\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                x.raw(),\n                this->weight,\n                A.getMatrixColoring().getSortedRowsByColor().raw() + color_offset,\n                A.getMatrixColoring().getRowColors().raw(),\n                this->Einv.raw(),\n                this->m_delta.raw(),\n                this->m_Delta.raw(),\n                num_rows_per_color,\n                i,\n                this->m_boundary_coloring,\n                separation,\n                A.get_block_dimy(),\n                A.getBlockFormat() == ROW_MAJOR );\n            cudaCheckError();\n        }\n    }\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MulticolorDILUSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//    AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MulticolorDILUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//    AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/solvers/multicolor_dilu_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/cf_jacobi_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#include <texture.h>\n#include <amgx_cusparse.h>\n\n#include <thrust/partition.h>\n\nnamespace amgx\n{\nnamespace cf_jacobi_solver\n{\n\nstruct is_coarse\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == COARSE);\n    }\n};\n\nstruct is_fine\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == FINE);\n    }\n};\n\nstruct is_eq_minus_one\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == -1);\n    }\n};\n\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_presmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? omega * b / d : omega * b / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_postsmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d  = ValueTypeA( 1 ) / d;\n        b -= y;\n        b *= omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\ntemplate<typename T>\n__device__ __forceinline__ T fmnaOp (T a, T b, T c)\n{\n    return -(a * b) + c;\n}\ntemplate<typename T>\n__device__ __forceinline__ T mulOp (T a, T b)\n{\n    return a * b;\n}\ntemplate<typename T>\n__device__ __forceinline__ T rcpOp (T a)\n{\n    return 1.0 / (isNotCloseToZero(a) ? a : epsilon(a));\n}\ntemplate<typename T>\n__device__ __forceinline__ T absOp (T a)\n{\n    return fabs(a);\n}\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid jacobi_zero_ini_masked_step(const int *row_ids, const int nrows_to_process, const ValueTypeA *Dinv, const ValueTypeB *b, const ValueTypeB relaxation_factor, ValueTypeB *x)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows_to_process)\n    {\n        int i = row_ids[tid];\n        ValueTypeB d = Dinv[i];\n        d = isNotCloseToZero(d) ? d : epsilon(d);\n        x[i] = relaxation_factor * b[i] / d;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid jacobi_masked_step(const int *row_ids, const int nrows_to_process, const ValueTypeA *Dinv, const ValueTypeB *b, const ValueTypeB *y, const ValueTypeB relaxation_factor, ValueTypeB *x)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows_to_process)\n    {\n        int i = row_ids[tid];\n        ValueTypeB d = Dinv[i];\n        d = isNotCloseToZero(d) ? d : epsilon(d);\n        x[i] += relaxation_factor * (b[i] - y[i]) / d;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\n__global__\nvoid agg_write_agg(const int *agg_map, const int nrows, int *dst)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows)\n    {\n        dst[agg_map[tid]] = tid;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nCFJacobiSolver_Base<T_Config>::CFJacobiSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    int param_mode = cfg.AMG_Config::template getParameter<int>(\"cf_smoothing_mode\", cfg_scope);\n\n    switch (param_mode)\n    {\n        case 0:\n            this->mode = CF_CF;\n            break;\n\n        case 1:\n            this->mode = CF_FC;\n            break;\n\n        case 2:\n            this->mode = CF_FCF;\n            break;\n\n        case 3:\n            this->mode = CF_CFC;\n            break;\n\n        default:\n            this->mode = CF_CF;\n    }\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nCFJacobiSolver_Base<T_Config>::~CFJacobiSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"CFJacobiSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    computeDinv( *A_as_matrix );\n\n    if ( A_as_matrix->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\" CFJacobiSolver only supports row major format\", AMGX_ERR_CONFIGURATION);\n    }\n\n    if (A_as_matrix->hasParameter(\"cf_map\")) // classical case\n    {\n        IVector *cf_map = A_as_matrix->template getParameterPtr< IVector >(\"cf_map\");\n        int nrows = A_as_matrix->get_num_rows();\n        this->num_coarse = amgx::thrust::count(cf_map->begin(), cf_map->end(), (int)COARSE);\n        this->c_rows.resize(this->num_coarse);\n        this->f_rows.resize(nrows - this->num_coarse);\n        amgx::thrust::counting_iterator<int> zero(0);\n        amgx::thrust::counting_iterator<int> zero_plus_nrows = zero + nrows;\n        amgx::thrust::copy_if(zero, zero_plus_nrows, cf_map->begin(), this->c_rows.begin(), is_coarse());\n        amgx::thrust::copy_if(zero, zero_plus_nrows, cf_map->begin(), this->f_rows.begin(), is_fine());\n        cudaCheckError();\n        // partitioning check\n        /*\n        {\n          typedef typename TConfig::template setMemSpace<AMGX_host  >::Type TConfig_h;\n          typedef typename TConfig_h::template setVecPrec<AMGX_vecInt>::Type ivec_value_type_h;\n          typedef Vector<ivec_value_type_h> IVector_h;\n\n          IVector_h cf_map_h = *cf_map;\n          IVector_h cf_rows = this->cf_rows;\n          for (int i = 0; i < cf_map_h.size(); i++)\n          {\n\n              if ((cf_map_h[cf_rows[i]] == FINE && i < this->num_coarse) || (cf_map_h[cf_rows[i]] == COARSE && i >= this->num_coarse))\n                  printf(\"CFJ FAIL at i==%d: cf_rows[i]==%d, cf_map[row]==%d, num_coarse==%d\\n\", i, cf_rows[i], cf_map_h[cf_rows[i]], this->num_coarse);\n          }\n        }\n        */\n    }\n    else if (A_as_matrix->hasParameter(\"aggregates_map\")) // aggregation case\n    {\n        IVector *agg_map = A_as_matrix->template getParameterPtr< IVector >(\"aggregates_map\");\n        int agg_num = A_as_matrix->template getParameter< int >(\"aggregates_num\");\n        this->num_coarse = agg_num;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A_as_matrix->get_num_rows() + threads_per_block - 1) / threads_per_block);\n        int nrows = A_as_matrix->get_num_rows();\n        this->c_rows.resize(this->num_coarse);\n        this->f_rows.resize(nrows - this->num_coarse);\n        IVector tmap(nrows, -1);\n        // can use thrust permutation operator here.\n        agg_write_agg <<< num_blocks, threads_per_block>>>(agg_map->raw(), agg_map->size(), this->c_rows.raw());\n        cudaCheckError();\n        agg_write_agg <<< num_blocks, threads_per_block>>>(this->c_rows.raw(), this->num_coarse, tmap.raw());\n        cudaCheckError();\n        amgx::thrust::counting_iterator<int> zero(0);\n        amgx::thrust::counting_iterator<int> zero_plus_nrows = zero + nrows;\n        amgx::thrust::copy_if(zero, zero_plus_nrows, tmap.begin(), this->f_rows.begin(), is_eq_minus_one());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"No info from AMG level was found for C-F separation, use different smoother or drink 1 beer\", AMGX_ERR_BAD_PARAMETERS);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nCFJacobiSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    //bool done = false;\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    int *smoothing_direction = A_as_matrix->template getParameterPtr<int> (\"smoothing_direction\");\n    // smoothing direction == 0 : presmoothing\n    // smoothing direction == 1 : postsmoothing\n    SmoothingOrder current_order;\n\n    switch (this->mode)\n    {\n        case CF_CF:\n            current_order = (*smoothing_direction == 0) ? CF_CF : CF_FC;\n            break;\n\n        case CF_FC:\n            current_order = (*smoothing_direction == 1) ? CF_CF : CF_FC;\n            break;\n\n        case CF_FCF:\n            current_order = (*smoothing_direction == 0) ? CF_FCF : CF_CFC;\n            break;\n\n        case CF_CFC:\n            current_order = (*smoothing_direction == 1) ? CF_FCF : CF_CFC;\n            break;\n    }\n\n// no multi-gpu for now\n    ViewType flags = OWNED;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*A_as_matrix, b, x, current_order, flags);\n        }\n        else\n        {\n            smooth_1x1(*A_as_matrix, b, x, current_order, flags);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid CFJacobiSolver_Base<T_Config>::computeDinv( Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        this->computeDinv_1x1(A);\n    }\n\n    A.setView(oldView);\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n\n    if ( A_as_matrix->hasProps(DIAG) )\n    {\n        const int num_values = A_as_matrix->diagOffset() * A_as_matrix->get_block_size();\n        amgx::thrust::copy( A_as_matrix->values.begin() + num_values, A_as_matrix->values.begin() + num_values + A_as_matrix->get_num_rows()*A_as_matrix->get_block_size(), this->Dinv.begin() );\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag( *A_as_matrix );\n    }\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    // Do nothing\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_h &A )\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->Dinv[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                FatalError(\"Could not find a diagonal value\", AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_d &A )\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::find_diag \" );\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n    find_diag_kernel_indexed_dia <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>(\n        A.get_num_rows(),\n        A.diag.raw(),\n        A.values.raw(),\n        this->Dinv.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    VVector newx(x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeB d = A.values[A.diag[i]];\n        ValueTypeB mydiaginv = this->weight / (isNotCloseToZero(d) ? d : epsilon(d) );\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        newx[i] = x[i] +  (b[i] - Axi) * mydiaginv ;\n    }\n\n    x.swap(newx);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_1x1 \" );\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n\n    if (order == CF_CF || order == CF_FC)\n    {\n        IVector &rows_first = (order == CF_CF) ? this->c_rows : this->f_rows;\n        IVector &rows_second = (order == CF_CF) ? this->f_rows : this->c_rows;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        // if use transform + permutation iterator it will yield into two separate permutation reads - for src and dst, so using simple kernel here\n        multiply_masked( A, x, this->y, rows_first, separation_flags );\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_first.raw(),\n            rows_first.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n        multiply_masked( A, x, this->y, rows_second, separation_flags );\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_second.raw(),\n            rows_second.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"CF_FCF and CF_CFC is not yet done\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1( Matrix_h &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d = A.values[A.diag[i]];\n        ValueTypeB mydiag = this->weight / (isNotCloseToZero(d) ? d : epsilon(d));\n        x[i] =  b[i] * mydiag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_with_0_initial_guess_1x1 \" );\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    if (order == CF_CF || order == CF_FC)\n    {\n//   IVector& rows_first = (order == CF_CF) ? this->c_rows : this->f_rows;\n        IVector &rows_second = (order == CF_CF) ? this->f_rows : this->c_rows;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        /*jacobi_zero_ini_masked_step<IndexType, ValueTypeA, ValueTypeB><<<num_blocks,threads_per_block>>>(\n          rows_first.raw(),\n          rows_first.size(),\n          this->Dinv.raw(),\n          b.raw(),\n          this->weight,\n          x.raw());*/\n          cudaCheckError();\n        // it is not so much harder to initialize whole vector instead of just C or F points\n        amgx::thrust::transform(b.begin( ),\n                          b.begin( ) + A.get_num_rows(),\n                          this->Dinv.begin( ),\n                          x.begin( ),\n                          jacobi_presmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n        cudaCheckError();\n        multiply_masked( A, x, this->y, rows_second, separation_flags );\n        cudaCheckError();\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_second.raw(),\n            rows_second.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"CF_FCF and CF_CFC is not yet done\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CFJacobiSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CFJacobiSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace block_jacobi\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/cf_jacobi_solver.h>\n#include <solvers/block_common_solver.h>\n#include <gaussian_elimination.h>\n#include <basic_types.h>\n#include <cutil.h>\n#include <util.h>\n#include <string>\n#include <miscmath.h>\n#include <texture.h>\n#include <amgx_cusparse.h>\n\n#include <thrust/partition.h>\n\nnamespace amgx\n{\nnamespace cf_jacobi_solver\n{\n\nstruct is_coarse\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == COARSE);\n    }\n};\n\nstruct is_fine\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == FINE);\n    }\n};\n\nstruct is_eq_minus_one\n{\n    __host__ __device__\n    int operator()(const int &x)\n    {\n        return (int) (x == -1);\n    }\n};\n\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_presmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_presmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    __host__ __device__ ValueTypeB operator()( const ValueTypeB &b, const ValueTypeA &d ) const { return isNotCloseToZero(d) ? omega * b / d : omega * b / epsilon(d); }\n};\n\ntemplate <typename ValueTypeA, typename ValueTypeB>\nstruct jacobi_postsmooth_functor\n{\n    ValueTypeB omega;\n    jacobi_postsmooth_functor( ValueTypeB omega ) : omega( omega ) {}\n    template<typename Tuple> __host__ __device__  ValueTypeB operator( )( const Tuple &t ) const\n    {\n        ValueTypeB x = amgx::thrust::get<0>(t);\n        ValueTypeA d = amgx::thrust::get<1>(t);\n        ValueTypeB b = amgx::thrust::get<2>(t);\n        ValueTypeB y = amgx::thrust::get<3>(t);\n        // return x + omega * (b - y) / d.\n        d = isNotCloseToZero(d) ? d :  epsilon(d);\n        d  = ValueTypeA( 1 ) / d;\n        b -= y;\n        b *= omega;\n        return b * d + x;\n    }\n};\n\ntemplate <typename ValueTypeB>\nstruct add_functor\n{\n    __host__ __device__  ValueTypeB operator()( const ValueTypeB &x, const ValueTypeB &y )const { return x + y; }\n};\n\ntemplate<typename T>\n__device__ __forceinline__ T fmnaOp (T a, T b, T c)\n{\n    return -(a * b) + c;\n}\ntemplate<typename T>\n__device__ __forceinline__ T mulOp (T a, T b)\n{\n    return a * b;\n}\ntemplate<typename T>\n__device__ __forceinline__ T rcpOp (T a)\n{\n    return 1.0 / (isNotCloseToZero(a) ? a : epsilon(a));\n}\ntemplate<typename T>\n__device__ __forceinline__ T absOp (T a)\n{\n    return fabs(a);\n}\n\n// -----------------------------------\n//  KERNELS\n// -----------------------------------\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid jacobi_zero_ini_masked_step(const int *row_ids, const int nrows_to_process, const ValueTypeA *Dinv, const ValueTypeB *b, const ValueTypeB relaxation_factor, ValueTypeB *x)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows_to_process)\n    {\n        int i = row_ids[tid];\n        ValueTypeB d = Dinv[i];\n        d = isNotCloseToZero(d) ? d : epsilon(d);\n        x[i] = relaxation_factor * b[i] / d;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid jacobi_masked_step(const int *row_ids, const int nrows_to_process, const ValueTypeA *Dinv, const ValueTypeB *b, const ValueTypeB *y, const ValueTypeB relaxation_factor, ValueTypeB *x)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows_to_process)\n    {\n        int i = row_ids[tid];\n        ValueTypeB d = Dinv[i];\n        d = isNotCloseToZero(d) ? d : epsilon(d);\n        x[i] += relaxation_factor * (b[i] - y[i]) / d;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\n__global__\nvoid agg_write_agg(const int *agg_map, const int nrows, int *dst)\n{\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (tid < nrows)\n    {\n        dst[agg_map[tid]] = tid;\n        tid += (blockDim.x * gridDim.x);\n    }\n}\n\n\n//--------------------------------\n// Methods\n//--------------------------------\n\n// Constructor\ntemplate<class T_Config>\nCFJacobiSolver_Base<T_Config>::CFJacobiSolver_Base( AMG_Config &cfg, const std::string &cfg_scope) : Solver<T_Config>( cfg, cfg_scope)\n{\n    weight = cfg.AMG_Config::template getParameter<double>(\"relaxation_factor\", cfg_scope);\n    int param_mode = cfg.AMG_Config::template getParameter<int>(\"cf_smoothing_mode\", cfg_scope);\n\n    switch (param_mode)\n    {\n        case 0:\n            this->mode = CF_CF;\n            break;\n\n        case 1:\n            this->mode = CF_FC;\n            break;\n\n        case 2:\n            this->mode = CF_FCF;\n            break;\n\n        case 3:\n            this->mode = CF_CFC;\n            break;\n\n        default:\n            this->mode = CF_CF;\n    }\n\n    if (weight == 0)\n    {\n        weight = 1.;\n        amgx_printf(\"Warning, setting weight to 1 instead of estimating largest_eigen_value in Block Jacobi smoother\\n\");\n    }\n}\n\n// Destructor\ntemplate<class T_Config>\nCFJacobiSolver_Base<T_Config>::~CFJacobiSolver_Base()\n{\n    this->Dinv.resize(0);\n}\n\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::printSolverParameters() const\n{\n    std::cout << \"relaxation_factor= \" << this->weight << std::endl;\n}\n\n// Solver setup\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    Matrix<T_Config> *A_as_matrix = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n\n    if (!A_as_matrix)\n    {\n        FatalError(\"CFJacobiSolver only works with explicit matrices\", AMGX_ERR_INTERNAL);\n    }\n\n    computeDinv( *A_as_matrix );\n\n    if ( A_as_matrix->getBlockFormat() != ROW_MAJOR )\n    {\n        FatalError(\" CFJacobiSolver only supports row major format\", AMGX_ERR_CONFIGURATION);\n    }\n\n    if (A_as_matrix->hasParameter(\"cf_map\")) // classical case\n    {\n        IVector *cf_map = A_as_matrix->template getParameterPtr< IVector >(\"cf_map\");\n        int nrows = A_as_matrix->get_num_rows();\n        this->num_coarse = amgx::thrust::count(cf_map->begin(), cf_map->end(), (int)COARSE);\n        this->c_rows.resize(this->num_coarse);\n        this->f_rows.resize(nrows - this->num_coarse);\n        amgx::thrust::counting_iterator<int> zero(0);\n        amgx::thrust::counting_iterator<int> zero_plus_nrows = zero + nrows;\n        amgx::thrust::copy_if(zero, zero_plus_nrows, cf_map->begin(), this->c_rows.begin(), is_coarse());\n        amgx::thrust::copy_if(zero, zero_plus_nrows, cf_map->begin(), this->f_rows.begin(), is_fine());\n        cudaCheckError();\n        // partitioning check\n        /*\n        {\n          typedef typename TConfig::template setMemSpace<AMGX_host  >::Type TConfig_h;\n          typedef typename TConfig_h::template setVecPrec<AMGX_vecInt>::Type ivec_value_type_h;\n          typedef Vector<ivec_value_type_h> IVector_h;\n\n          IVector_h cf_map_h = *cf_map;\n          IVector_h cf_rows = this->cf_rows;\n          for (int i = 0; i < cf_map_h.size(); i++)\n          {\n\n              if ((cf_map_h[cf_rows[i]] == FINE && i < this->num_coarse) || (cf_map_h[cf_rows[i]] == COARSE && i >= this->num_coarse))\n                  printf(\"CFJ FAIL at i==%d: cf_rows[i]==%d, cf_map[row]==%d, num_coarse==%d\\n\", i, cf_rows[i], cf_map_h[cf_rows[i]], this->num_coarse);\n          }\n        }\n        */\n    }\n    else if (A_as_matrix->hasParameter(\"aggregates_map\")) // aggregation case\n    {\n        IVector *agg_map = A_as_matrix->template getParameterPtr< IVector >(\"aggregates_map\");\n        int agg_num = A_as_matrix->template getParameter< int >(\"aggregates_num\");\n        this->num_coarse = agg_num;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A_as_matrix->get_num_rows() + threads_per_block - 1) / threads_per_block);\n        int nrows = A_as_matrix->get_num_rows();\n        this->c_rows.resize(this->num_coarse);\n        this->f_rows.resize(nrows - this->num_coarse);\n        IVector tmap(nrows, -1);\n        // can use thrust permutation operator here.\n        agg_write_agg <<< num_blocks, threads_per_block>>>(agg_map->raw(), agg_map->size(), this->c_rows.raw());\n        cudaCheckError();\n        agg_write_agg <<< num_blocks, threads_per_block>>>(this->c_rows.raw(), this->num_coarse, tmap.raw());\n        cudaCheckError();\n        amgx::thrust::counting_iterator<int> zero(0);\n        amgx::thrust::counting_iterator<int> zero_plus_nrows = zero + nrows;\n        amgx::thrust::copy_if(zero, zero_plus_nrows, tmap.begin(), this->f_rows.begin(), is_eq_minus_one());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"No info from AMG level was found for C-F separation, use different smoother or drink 1 beer\", AMGX_ERR_BAD_PARAMETERS);\n    }\n}\n\n//\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n}\n\n// Solve one iteration\ntemplate<class T_Config>\nAMGX_STATUS\nCFJacobiSolver_Base<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    //bool done = false;\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    int *smoothing_direction = A_as_matrix->template getParameterPtr<int> (\"smoothing_direction\");\n    // smoothing direction == 0 : presmoothing\n    // smoothing direction == 1 : postsmoothing\n    SmoothingOrder current_order;\n\n    switch (this->mode)\n    {\n        case CF_CF:\n            current_order = (*smoothing_direction == 0) ? CF_CF : CF_FC;\n            break;\n\n        case CF_FC:\n            current_order = (*smoothing_direction == 1) ? CF_CF : CF_FC;\n            break;\n\n        case CF_FCF:\n            current_order = (*smoothing_direction == 0) ? CF_FCF : CF_CFC;\n            break;\n\n        case CF_CFC:\n            current_order = (*smoothing_direction == 1) ? CF_FCF : CF_CFC;\n            break;\n    }\n\n// no multi-gpu for now\n    ViewType flags = OWNED;\n\n    if (xIsZero) { x.dirtybit = 0; }\n\n    if (A_as_matrix->get_block_dimx() == 1 && A_as_matrix->get_block_dimy() == 1)\n    {\n        if (xIsZero)\n        {\n            smooth_with_0_initial_guess_1x1(*A_as_matrix, b, x, current_order, flags);\n        }\n        else\n        {\n            smooth_1x1(*A_as_matrix, b, x, current_order, flags);\n        }\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for BlockJacobi_Solver\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    return this->converged( b, x );\n}\n\ntemplate<class T_Config>\nvoid\nCFJacobiSolver_Base<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid CFJacobiSolver_Base<T_Config>::computeDinv( Matrix<T_Config> &A)\n{\n    Matrix<T_Config> *A_as_matrix = (Matrix<T_Config> *) this->m_A;\n    ViewType oldView = A.currentView();\n    A.setView(A_as_matrix->getViewExterior());\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        this->computeDinv_1x1(A);\n    }\n\n    A.setView(oldView);\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_d &A)\n{\n    Matrix_d *A_as_matrix = (Matrix_d *) this->m_A;\n// supports both diag\n    this->Dinv.resize(A.get_num_rows()*A.get_block_dimx()*A.get_block_dimy(), 0.0);\n\n    if ( A_as_matrix->hasProps(DIAG) )\n    {\n        const int num_values = A_as_matrix->diagOffset() * A_as_matrix->get_block_size();\n        amgx::thrust::copy( A_as_matrix->values.begin() + num_values, A_as_matrix->values.begin() + num_values + A_as_matrix->get_num_rows()*A_as_matrix->get_block_size(), this->Dinv.begin() );\n        cudaCheckError();\n    }\n    else\n    {\n        find_diag( *A_as_matrix );\n    }\n}\n\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeDinv_1x1(const Matrix_h &A)\n{\n    // Do nothing\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_h &A )\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                this->Dinv[i] = A.values[j];\n                break;\n            }\n\n            if (j == A.row_offsets[i + 1] - 1)\n            {\n                FatalError(\"Could not find a diagonal value\", AMGX_ERR_BAD_PARAMETERS);\n            }\n        }\n    }\n}\n\n\n// Finding diag on device, CSR format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::find_diag( const Matrix_d &A )\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::find_diag \" );\n    const size_t THREADS_PER_BLOCK  = 128;\n    const size_t NUM_BLOCKS = std::min(AMGX_GRID_MAX_SIZE, (int)ceil((ValueTypeB)A.get_num_rows() / (ValueTypeB)THREADS_PER_BLOCK));\n    find_diag_kernel_indexed_dia <<< (unsigned int)NUM_BLOCKS, (unsigned int)THREADS_PER_BLOCK>>>(\n        A.get_num_rows(),\n        A.diag.raw(),\n        A.values.raw(),\n        this->Dinv.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_h &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    VVector newx(x.size());\n\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB Axi = 0.0;\n        ValueTypeB d = A.values[A.diag[i]];\n        ValueTypeB mydiaginv = this->weight / (isNotCloseToZero(d) ? d : epsilon(d) );\n\n        //for each column\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            Axi += A.values[j] * x[A.col_indices[j]];\n        }\n\n        newx[i] = x[i] +  (b[i] - Axi) * mydiaginv ;\n    }\n\n    x.swap(newx);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_1x1(Matrix_d &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_1x1 \" );\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n\n    if (order == CF_CF || order == CF_FC)\n    {\n        IVector &rows_first = (order == CF_CF) ? this->c_rows : this->f_rows;\n        IVector &rows_second = (order == CF_CF) ? this->f_rows : this->c_rows;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        // if use transform + permutation iterator it will yield into two separate permutation reads - for src and dst, so using simple kernel here\n        multiply_masked( A, x, this->y, rows_first, separation_flags );\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_first.raw(),\n            rows_first.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n        multiply_masked( A, x, this->y, rows_second, separation_flags );\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_second.raw(),\n            rows_second.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"CF_FCF and CF_CFC is not yet done\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1( Matrix_h &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    //for each row\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        ValueTypeB d = A.values[A.diag[i]];\n        ValueTypeB mydiag = this->weight / (isNotCloseToZero(d) ? d : epsilon(d));\n        x[i] =  b[i] * mydiag;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CFJacobiSolver<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::smooth_with_0_initial_guess_1x1(Matrix_d &A, VVector &b, VVector &x, SmoothingOrder order, ViewType separation_flags)\n{\n    AMGX_CPU_PROFILER( \"JacobiSolver::smooth_with_0_initial_guess_1x1 \" );\n    int num_rows = A.get_num_rows();\n    int offset = 0;\n    A.getOffsetAndSizeForView(separation_flags, &offset, &num_rows);\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n\n    if (this->y.size() != b.size())\n    {\n        this->y.resize(b.size());\n        this->y.tag = this->tag * 100 + 3;\n        this->y.set_block_dimx(b.get_block_dimx());\n        this->y.set_block_dimy(b.get_block_dimy());\n    }\n\n    if (order == CF_CF || order == CF_FC)\n    {\n//   IVector& rows_first = (order == CF_CF) ? this->c_rows : this->f_rows;\n        IVector &rows_second = (order == CF_CF) ? this->f_rows : this->c_rows;\n        const int threads_per_block = 256;\n        const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() + threads_per_block - 1) / threads_per_block);\n        /*jacobi_zero_ini_masked_step<IndexType, ValueTypeA, ValueTypeB><<<num_blocks,threads_per_block>>>(\n          rows_first.raw(),\n          rows_first.size(),\n          this->Dinv.raw(),\n          b.raw(),\n          this->weight,\n          x.raw());*/\n          cudaCheckError();\n        // it is not so much harder to initialize whole vector instead of just C or F points\n        amgx::thrust::transform(b.begin( ),\n                          b.begin( ) + A.get_num_rows(),\n                          this->Dinv.begin( ),\n                          x.begin( ),\n                          jacobi_presmooth_functor<ValueTypeA, ValueTypeB>( this->weight ));\n        cudaCheckError();\n        multiply_masked( A, x, this->y, rows_second, separation_flags );\n        cudaCheckError();\n        jacobi_masked_step<IndexType, ValueTypeA, ValueTypeB> <<< num_blocks, threads_per_block>>>(\n            rows_second.raw(),\n            rows_second.size(),\n            this->Dinv.raw(),\n            b.raw(),\n            this->y.raw(),\n            this->weight,\n            x.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        FatalError(\"CF_FCF and CF_CFC is not yet done\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CFJacobiSolver_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CFJacobiSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace block_jacobi\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/cf_jacobi_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <solvers/cheb_solver.h>\n#include <operators/solver_operator.h>\n#include <blas.h>\n#include <util.h>\n\n#include <thrust/extrema.h> // for amgx::thrust::max_element\n\nnamespace amgx\n{\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int threads_per_block, int warps_per_block, bool diag>\n__global__\nvoid getLambdaEstimate(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, const int num_rows, ValueTypeB *out)\n\n{\n    int row_id = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB max_sum = (ValueTypeB)0.0;\n\n    while (row_id < num_rows)\n    {\n        ValueTypeB cur_sum = (ValueTypeB)0.0;\n\n        for (int j = row_offsets[row_id]; j < row_offsets[row_id + 1]; j++)\n        {\n            cur_sum += abs(values[j]);\n        }\n\n        if (diag)\n        {\n            cur_sum += abs(values[dia_indices[row_id]]);\n        }\n\n        max_sum = max(max_sum, cur_sum);\n        row_id += gridDim.x * blockDim.x;\n    }\n\n    out[blockDim.x * blockIdx.x + threadIdx.x] = max_sum;\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <class T_Config>\nvoid Chebyshev_Solver<T_Config>::compute_eigenmax_estimate(const Matrix<T_Config> &A, ValueTypeB &lambda)\n{\n#define LAMBDA_BLOCK_SIZE 256\n    VVector tsum(A.get_num_rows());\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    if (A.hasProps(DIAG))\n    {\n        cudaFuncSetCacheConfig(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true >, cudaFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        cudaFuncSetCacheConfig(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false >, cudaFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n\n    lambda = *(amgx::thrust::max_element(tsum.begin(), tsum.end()));\n}\n\n// Constructor\ntemplate< class T_Config>\nChebyshev_Solver<T_Config>::Chebyshev_Solver( AMG_Config &cfg, const std::string &cfg_scope) :\n    Solver<T_Config>( cfg, cfg_scope),\n    m_buffer_N(0), m_eigsolver(NULL)\n{\n    std::string solverName, new_scope, tmp_scope;\n    cfg.getParameter<std::string>( \"preconditioner\", solverName, cfg_scope, new_scope );\n    m_lambda_mode = cfg.AMG_Config::template getParameter<int>(\"chebyshev_lambda_estimate_mode\", cfg_scope);\n    m_cheby_order = cfg.AMG_Config::template getParameter<int>(\"chebyshev_polynomial_order\", cfg_scope);\n    // 0 - use eigensolver to get BOTH estimates\n    // 1 - use eigensolver to get maximum estimate\n    // 2 - use max sum of abs values as a rough estimate for maximum eigenvalue\n    // 3 - use user provided cheby_max_lambda and cheby_min_lambda\n\n    if (m_lambda_mode == 3)\n    {\n        m_user_max_lambda = cfg.AMG_Config::template getParameter<double>(\"cheby_max_lambda\", cfg_scope);\n        m_user_min_lambda = cfg.AMG_Config::template getParameter<double>(\"cheby_min_lambda\", cfg_scope);\n    }\n\n    if (solverName.compare(\"NOSOLVER\") == 0)\n    {\n        no_preconditioner = true;\n        m_preconditioner = NULL;\n    }\n    else\n    {\n        no_preconditioner = false;\n        m_preconditioner = SolverFactory<T_Config>::allocate( cfg, cfg_scope, \"preconditioner\" );\n    }\n\n    std::string eig_cfg_string =  \"algorithm=AGGREGATION,\\n\"\n                                  \"eig_solver=LANCZOS,\\n\"\n                                  \"verbosity_level=0,\\n\"\n                                  \"eig_max_iters=128,\\n\"\n                                  \"eig_tolerance=1e-4,\\n\"\n                                  \"eig_which=largest,\\n\"\n                                  \"eig_eigenvector=0,\\n\"\n                                  \"eig_eigenvector_solver=default\";\n\n    if (m_lambda_mode < 2)\n    {\n        AMG_Configuration eig_cfg;\n        eig_cfg.parseParameterString(eig_cfg_string.c_str());\n        m_eigsolver = EigenSolverFactory<T_Config>::allocate(*eig_cfg.getConfigObject(), \"default\", \"eig_solver\");\n    }\n}\n\ntemplate<class T_Config>\nChebyshev_Solver<T_Config>::~Chebyshev_Solver()\n{\n    if (!no_preconditioner) { delete m_preconditioner; }\n\n    if (!m_eigsolver) { delete m_eigsolver; }\n}\n\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solver_setup \" );\n    ViewType oldView = this->m_A->currentView();\n    this->m_A->setViewExterior();\n\n    // Setup the preconditionner\n    if (!no_preconditioner)\n    {\n        m_preconditioner->setup(*this->m_A, reuse_matrix_structure);\n    }\n\n    // The number of elements in temporary vectors.\n    this->m_buffer_N = static_cast<int>( this->m_A->get_num_cols() * this->m_A->get_block_dimy() );\n    Matrix<T_Config> *mtx_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n    VVector eig_solver_t_x;\n\n    // m_lambda_mode:\n    // 0: use eigensolver to get lmin and lmax estimate\n    // 1: use eigensolver to get lmax estimate, set lmin = lmax/8\n    // 2: use max row sum as lmax estimate, set lmin = lmax/8\n    if (m_lambda_mode < 2)\n    {\n        if (!no_preconditioner)\n        {\n            SolverOperator<T_Config> *MA = new SolverOperator<T_Config> (this->m_A, m_preconditioner);\n            m_eigsolver->setup(*MA);\n            m_eigsolver->solve(eig_solver_t_x);\n            delete MA;\n        }\n        else\n        {\n            m_eigsolver->setup(*mtx_A);\n            m_eigsolver->solve(eig_solver_t_x);\n        }\n\n        const std::vector<ValueTypeB> &lambdas = m_eigsolver->get_eigenvalues();\n        this->m_lmax = lambdas[0] * 1.05;\n\n        if (m_lambda_mode == 0)\n        {\n            this->m_lmin = lambdas[lambdas.size() - 1] * 0.95;\n        }\n        else\n        {\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n    }\n    else if (m_lambda_mode == 2)\n    {\n        if (no_preconditioner)\n        {\n            Matrix<T_Config> *pA = dynamic_cast< Matrix<T_Config>* > (this->m_A);\n            compute_eigenmax_estimate(*pA, this->m_lmax);\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n        else\n        {\n            // assuming that this preconditioner would be good enough to reduce spectrum to the largest eigen value = 1.0\n            this->m_lmax = 0.9;\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n    }\n    else if (m_lambda_mode == 3)\n    {\n        if (no_preconditioner)\n        {\n            Matrix<T_Config> *pA = dynamic_cast< Matrix<T_Config>* > (this->m_A);\n            compute_eigenmax_estimate(*pA, this->m_lmax);\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n        else\n        {\n            // Use user input estimates\n            this->m_lmax = this->m_user_max_lambda;\n            this->m_lmin = this->m_user_min_lambda;\n        }\n    }\n    else\n    {\n        FatalError(\"Not supported chebyshev_lambda_estimate_mode.\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    // Allocate memory needed for iterating.\n    m_p.resize( this->m_buffer_N );\n    m_z.resize( this->m_buffer_N );\n    m_Ap.resize( this->m_buffer_N );\n    m_xp.resize( this->m_buffer_N );\n    m_rp.resize( this->m_buffer_N );\n    m_p.set_block_dimy(this->m_A->get_block_dimy());\n    m_p.set_block_dimx(1);\n    m_p.dirtybit = 1;\n    m_p.delayed_send = 1;\n    m_p.tag = this->tag * 100 + 1;\n    m_Ap.set_block_dimy(this->m_A->get_block_dimy());\n    m_Ap.set_block_dimx(1);\n    m_Ap.dirtybit = 1;\n    m_Ap.delayed_send = 1;\n    m_Ap.tag = this->tag * 100 + 2;\n    m_z.set_block_dimy(this->m_A->get_block_dimy());\n    m_z.set_block_dimx(1);\n    m_z.dirtybit = 1;\n    m_z.delayed_send = 1;\n    m_z.tag = this->tag * 100 + 3;\n    m_xp.set_block_dimy(this->m_A->get_block_dimy());\n    m_xp.set_block_dimx(1);\n    m_xp.dirtybit = 1;\n    m_xp.delayed_send = 1;\n    m_xp.tag = this->tag * 100 + 4;\n    this->m_A->setView(oldView);\n}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solve_init \" );\n    Operator<T_Config> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n\n    // Run one iteration of preconditioner with zero initial guess\n    if (no_preconditioner)\n    {\n        copy(*this->m_r, m_z, offset, size);\n    }\n    else\n    {\n        m_z.delayed_send = 1;\n        this->m_r->delayed_send = 1;\n        m_preconditioner->solve( *this->m_r, m_z, true );\n        m_z.delayed_send = 1;\n        this->m_r->delayed_send = 1;\n    }\n\n    // m_p - res after precond\n    copy( m_z, m_p, offset, size );\n    A.setView(oldView);\n    m_gamma = 0.;\n    m_beta = 0.;\n    first_iter = 0;\n}\n\ntemplate<class T_Config>\nAMGX_STATUS\nChebyshev_Solver<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    AMGX_STATUS conv_stat = AMGX_ST_NOT_CONVERGED;\n\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solve_iteration \" );\n    Operator<T_Config> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    ValueTypeB a = (this->m_lmax + this->m_lmin) / 2;\n    ValueTypeB c = (this->m_lmax - this->m_lmin) / 2;\n\n    for (int i = 0; i < m_cheby_order; i++)\n    {\n        // apply precond\n        if (no_preconditioner)\n        {\n            copy(*this->m_r, m_z, offset, size);\n        }\n        else\n        {\n            m_z.delayed_send = 1;\n            this->m_r->delayed_send = 1;\n            m_preconditioner->solve( *this->m_r, m_z, true );\n            m_z.delayed_send = 1;\n            this->m_r->delayed_send = 1;\n        }\n\n        if (first_iter == 0)\n        {\n            m_gamma = 1. / a;\n            first_iter = 1;\n        }\n        else\n        {\n            m_beta = c * c * m_gamma * m_gamma / 4.;\n\n            if (m_gamma != ValueTypeB(0) && (a - (m_beta / m_gamma)) != ValueTypeB(0))\n            {\n                m_gamma = 1. / (a - m_beta / m_gamma);\n            }\n\n            axpby( m_z, m_p, m_p, ValueTypeB( 1 ), m_beta, offset, size);\n        }\n\n        axpy( m_p, x, m_gamma, offset, size );\n        this->compute_residual( b, x);\n    }\n\n    // Do we converge ?\n    if ( this->m_monitor_convergence &&\n         isDone( ( conv_stat = this->compute_norm_and_converged() ) ) )\n    {\n        A.setView(oldView);\n        return conv_stat;\n    }\n\n    // No convergence so far.\n    A.setView(oldView);\n    return this->m_monitor_convergence ? AMGX_ST_NOT_CONVERGED : AMGX_ST_CONVERGED;\n}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::printSolverParameters() const\n{\n    if (!no_preconditioner)\n    {\n        std::cout << \"preconditioner: \" << this->m_preconditioner->getName()\n                  << \" with scope name: \"\n                  << this->m_preconditioner->getScope() << std::endl;\n    }\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Chebyshev_Solver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <solvers/cheb_solver.h>\n#include <operators/solver_operator.h>\n#include <blas.h>\n#include <util.h>\n\n#include <thrust/extrema.h> // for amgx::thrust::max_element\n\nnamespace amgx\n{\n\ntemplate<typename IndexType, typename ValueTypeA, typename ValueTypeB, int threads_per_block, int warps_per_block, bool diag>\n__global__\nvoid getLambdaEstimate(const IndexType *row_offsets, const IndexType *column_indices, const ValueTypeA *values, const IndexType *dia_indices, const int num_rows, ValueTypeB *out)\n\n{\n    int row_id = blockDim.x * blockIdx.x + threadIdx.x;\n    ValueTypeB max_sum = (ValueTypeB)0.0;\n\n    while (row_id < num_rows)\n    {\n        ValueTypeB cur_sum = (ValueTypeB)0.0;\n\n        for (int j = row_offsets[row_id]; j < row_offsets[row_id + 1]; j++)\n        {\n            cur_sum += abs(values[j]);\n        }\n\n        if (diag)\n        {\n            cur_sum += abs(values[dia_indices[row_id]]);\n        }\n\n        max_sum = max(max_sum, cur_sum);\n        row_id += gridDim.x * blockDim.x;\n    }\n\n    out[blockDim.x * blockIdx.x + threadIdx.x] = max_sum;\n}\n\n// Method to compute the inverse of the diagonal blocks\ntemplate <class T_Config>\nvoid Chebyshev_Solver<T_Config>::compute_eigenmax_estimate(const Matrix<T_Config> &A, ValueTypeB &lambda)\n{\n#define LAMBDA_BLOCK_SIZE 256\n    VVector tsum(A.get_num_rows());\n    const int threads_per_block = 256;\n    const int blockrows_per_cta = threads_per_block;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() - 1) / blockrows_per_cta + 1);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueTypeA *A_nonzero_values_ptr = A.values.raw();\n\n    if (A.hasProps(DIAG))\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true >), hipFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, true > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false >), hipFuncCachePreferL1);\n        getLambdaEstimate < IndexType, ValueTypeA, ValueTypeB, LAMBDA_BLOCK_SIZE, LAMBDA_BLOCK_SIZE / 32, false > <<< num_blocks, threads_per_block>>>\n        (A_row_offsets_ptr, A_column_indices_ptr, A_nonzero_values_ptr, A_dia_idx_ptr, A.get_num_rows(), tsum.raw());\n        cudaCheckError();\n    }\n\n    lambda = *(amgx::thrust::max_element(tsum.begin(), tsum.end()));\n}\n\n// Constructor\ntemplate< class T_Config>\nChebyshev_Solver<T_Config>::Chebyshev_Solver( AMG_Config &cfg, const std::string &cfg_scope) :\n    Solver<T_Config>( cfg, cfg_scope),\n    m_buffer_N(0), m_eigsolver(NULL)\n{\n    std::string solverName, new_scope, tmp_scope;\n    cfg.getParameter<std::string>( \"preconditioner\", solverName, cfg_scope, new_scope );\n    m_lambda_mode = cfg.AMG_Config::template getParameter<int>(\"chebyshev_lambda_estimate_mode\", cfg_scope);\n    m_cheby_order = cfg.AMG_Config::template getParameter<int>(\"chebyshev_polynomial_order\", cfg_scope);\n    // 0 - use eigensolver to get BOTH estimates\n    // 1 - use eigensolver to get maximum estimate\n    // 2 - use max sum of abs values as a rough estimate for maximum eigenvalue\n    // 3 - use user provided cheby_max_lambda and cheby_min_lambda\n\n    if (m_lambda_mode == 3)\n    {\n        m_user_max_lambda = cfg.AMG_Config::template getParameter<double>(\"cheby_max_lambda\", cfg_scope);\n        m_user_min_lambda = cfg.AMG_Config::template getParameter<double>(\"cheby_min_lambda\", cfg_scope);\n    }\n\n    if (solverName.compare(\"NOSOLVER\") == 0)\n    {\n        no_preconditioner = true;\n        m_preconditioner = NULL;\n    }\n    else\n    {\n        no_preconditioner = false;\n        m_preconditioner = SolverFactory<T_Config>::allocate( cfg, cfg_scope, \"preconditioner\" );\n    }\n\n    std::string eig_cfg_string =  \"algorithm=AGGREGATION,\\n\"\n                                  \"eig_solver=LANCZOS,\\n\"\n                                  \"verbosity_level=0,\\n\"\n                                  \"eig_max_iters=128,\\n\"\n                                  \"eig_tolerance=1e-4,\\n\"\n                                  \"eig_which=largest,\\n\"\n                                  \"eig_eigenvector=0,\\n\"\n                                  \"eig_eigenvector_solver=default\";\n\n    if (m_lambda_mode < 2)\n    {\n        AMG_Configuration eig_cfg;\n        eig_cfg.parseParameterString(eig_cfg_string.c_str());\n        m_eigsolver = EigenSolverFactory<T_Config>::allocate(*eig_cfg.getConfigObject(), \"default\", \"eig_solver\");\n    }\n}\n\ntemplate<class T_Config>\nChebyshev_Solver<T_Config>::~Chebyshev_Solver()\n{\n    if (!no_preconditioner) { delete m_preconditioner; }\n\n    if (!m_eigsolver) { delete m_eigsolver; }\n}\n\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solver_setup(bool reuse_matrix_structure)\n{\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solver_setup \" );\n    ViewType oldView = this->m_A->currentView();\n    this->m_A->setViewExterior();\n\n    // Setup the preconditionner\n    if (!no_preconditioner)\n    {\n        m_preconditioner->setup(*this->m_A, reuse_matrix_structure);\n    }\n\n    // The number of elements in temporary vectors.\n    this->m_buffer_N = static_cast<int>( this->m_A->get_num_cols() * this->m_A->get_block_dimy() );\n    Matrix<T_Config> *mtx_A = dynamic_cast<Matrix<T_Config>*>(this->m_A);\n    VVector eig_solver_t_x;\n\n    // m_lambda_mode:\n    // 0: use eigensolver to get lmin and lmax estimate\n    // 1: use eigensolver to get lmax estimate, set lmin = lmax/8\n    // 2: use max row sum as lmax estimate, set lmin = lmax/8\n    if (m_lambda_mode < 2)\n    {\n        if (!no_preconditioner)\n        {\n            SolverOperator<T_Config> *MA = new SolverOperator<T_Config> (this->m_A, m_preconditioner);\n            m_eigsolver->setup(*MA);\n            m_eigsolver->solve(eig_solver_t_x);\n            delete MA;\n        }\n        else\n        {\n            m_eigsolver->setup(*mtx_A);\n            m_eigsolver->solve(eig_solver_t_x);\n        }\n\n        const std::vector<ValueTypeB> &lambdas = m_eigsolver->get_eigenvalues();\n        this->m_lmax = lambdas[0] * 1.05;\n\n        if (m_lambda_mode == 0)\n        {\n            this->m_lmin = lambdas[lambdas.size() - 1] * 0.95;\n        }\n        else\n        {\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n    }\n    else if (m_lambda_mode == 2)\n    {\n        if (no_preconditioner)\n        {\n            Matrix<T_Config> *pA = dynamic_cast< Matrix<T_Config>* > (this->m_A);\n            compute_eigenmax_estimate(*pA, this->m_lmax);\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n        else\n        {\n            // assuming that this preconditioner would be good enough to reduce spectrum to the largest eigen value = 1.0\n            this->m_lmax = 0.9;\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n    }\n    else if (m_lambda_mode == 3)\n    {\n        if (no_preconditioner)\n        {\n            Matrix<T_Config> *pA = dynamic_cast< Matrix<T_Config>* > (this->m_A);\n            compute_eigenmax_estimate(*pA, this->m_lmax);\n            this->m_lmin = this->m_lmax * 0.125;\n        }\n        else\n        {\n            // Use user input estimates\n            this->m_lmax = this->m_user_max_lambda;\n            this->m_lmin = this->m_user_min_lambda;\n        }\n    }\n    else\n    {\n        FatalError(\"Not supported chebyshev_lambda_estimate_mode.\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    // Allocate memory needed for iterating.\n    m_p.resize( this->m_buffer_N );\n    m_z.resize( this->m_buffer_N );\n    m_Ap.resize( this->m_buffer_N );\n    m_xp.resize( this->m_buffer_N );\n    m_rp.resize( this->m_buffer_N );\n    m_p.set_block_dimy(this->m_A->get_block_dimy());\n    m_p.set_block_dimx(1);\n    m_p.dirtybit = 1;\n    m_p.delayed_send = 1;\n    m_p.tag = this->tag * 100 + 1;\n    m_Ap.set_block_dimy(this->m_A->get_block_dimy());\n    m_Ap.set_block_dimx(1);\n    m_Ap.dirtybit = 1;\n    m_Ap.delayed_send = 1;\n    m_Ap.tag = this->tag * 100 + 2;\n    m_z.set_block_dimy(this->m_A->get_block_dimy());\n    m_z.set_block_dimx(1);\n    m_z.dirtybit = 1;\n    m_z.delayed_send = 1;\n    m_z.tag = this->tag * 100 + 3;\n    m_xp.set_block_dimy(this->m_A->get_block_dimy());\n    m_xp.set_block_dimx(1);\n    m_xp.dirtybit = 1;\n    m_xp.delayed_send = 1;\n    m_xp.tag = this->tag * 100 + 4;\n    this->m_A->setView(oldView);\n}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solve_init( VVector &b, VVector &x, bool xIsZero )\n{\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solve_init \" );\n    Operator<T_Config> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n\n    // Run one iteration of preconditioner with zero initial guess\n    if (no_preconditioner)\n    {\n        copy(*this->m_r, m_z, offset, size);\n    }\n    else\n    {\n        m_z.delayed_send = 1;\n        this->m_r->delayed_send = 1;\n        m_preconditioner->solve( *this->m_r, m_z, true );\n        m_z.delayed_send = 1;\n        this->m_r->delayed_send = 1;\n    }\n\n    // m_p - res after precond\n    copy( m_z, m_p, offset, size );\n    A.setView(oldView);\n    m_gamma = 0.;\n    m_beta = 0.;\n    first_iter = 0;\n}\n\ntemplate<class T_Config>\nAMGX_STATUS\nChebyshev_Solver<T_Config>::solve_iteration( VVector &b, VVector &x, bool xIsZero )\n{\n    AMGX_STATUS conv_stat = AMGX_ST_NOT_CONVERGED;\n\n    AMGX_CPU_PROFILER( \"Chebyshev_Solver::solve_iteration \" );\n    Operator<T_Config> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    ValueTypeB a = (this->m_lmax + this->m_lmin) / 2;\n    ValueTypeB c = (this->m_lmax - this->m_lmin) / 2;\n\n    for (int i = 0; i < m_cheby_order; i++)\n    {\n        // apply precond\n        if (no_preconditioner)\n        {\n            copy(*this->m_r, m_z, offset, size);\n        }\n        else\n        {\n            m_z.delayed_send = 1;\n            this->m_r->delayed_send = 1;\n            m_preconditioner->solve( *this->m_r, m_z, true );\n            m_z.delayed_send = 1;\n            this->m_r->delayed_send = 1;\n        }\n\n        if (first_iter == 0)\n        {\n            m_gamma = 1. / a;\n            first_iter = 1;\n        }\n        else\n        {\n            m_beta = c * c * m_gamma * m_gamma / 4.;\n\n            if (m_gamma != ValueTypeB(0) && (a - (m_beta / m_gamma)) != ValueTypeB(0))\n            {\n                m_gamma = 1. / (a - m_beta / m_gamma);\n            }\n\n            axpby( m_z, m_p, m_p, ValueTypeB( 1 ), m_beta, offset, size);\n        }\n\n        axpy( m_p, x, m_gamma, offset, size );\n        this->compute_residual( b, x);\n    }\n\n    // Do we converge ?\n    if ( this->m_monitor_convergence &&\n         isDone( ( conv_stat = this->compute_norm_and_converged() ) ) )\n    {\n        A.setView(oldView);\n        return conv_stat;\n    }\n\n    // No convergence so far.\n    A.setView(oldView);\n    return this->m_monitor_convergence ? AMGX_ST_NOT_CONVERGED : AMGX_ST_CONVERGED;\n}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::solve_finalize( VVector &b, VVector &x )\n{}\n\ntemplate<class T_Config>\nvoid\nChebyshev_Solver<T_Config>::printSolverParameters() const\n{\n    if (!no_preconditioner)\n    {\n        std::cout << \"preconditioner: \" << this->m_preconditioner->getName()\n                  << \" with scope name: \"\n                  << this->m_preconditioner->getScope() << std::endl;\n    }\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Chebyshev_Solver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/solvers/cheb_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <basic_types.h>\n#include <util.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n#include <texture.h>\n#include <typeinfo>\n\n#include \"solvers/dense_lu_solver.h\"\n#include \"solvers/block_common_solver.h\"\n\n#include \"amgx_types/util.h\"\n\n#include <algorithm>\n\n//trick to get nvcc to print a defined value\n//(__CUDA_ARCH__) @ compile time:\n//\nnamespace //unnamed namespace\n{\nstruct X\n{\n    private:\n        X(void) {}\n};\n\ntemplate<int N>\nstruct Print_N_atCompile\n{\n    X array[N];\n};\n}//end unnamed namespace\n\nnamespace amgx\n{\nnamespace dense_lu_solver\n{\n\nenum { WARP_SIZE = 32, CTA_SIZE = 128 };\n\n//\n// supporting kernels\n//\ntemplate< typename T,  typename Tv, int WARP_SIZE >\n__global__\nvoid csr_to_dense_kernel(\n    const int num_rows,\n    const int num_cols,\n    const int block_num_rows,\n    const int block_num_cols,\n    const int *__restrict A_csr_rows,\n    const int *__restrict A_csr_cols,\n    const T   *__restrict A_csr_vals,\n    const int *__restrict A_csr_diag,\n    T   *__restrict A_dense,\n    const int lda)\n{\n    // Note:\n    // To handle block CSR, the sparsity pattern csr_rows and csr_cols only store\n    // the typical csr info assuming each block is a scalar.\n    // The values in csr_vals has all entries in the blocks, using row major to\n    // store the block. So we need the number of entries in each block as stride.\n    const int block_mxn = block_num_rows * block_num_cols;\n    // Each lane copies one entry in a block and iterate through row sparsity pattern.\n    // Essentially one warp per row-block. For 4x4, we have 16 working threads per warp.\n    const int lane_id = threadIdx.x % WARP_SIZE;\n    // find the (row,col) local to a block\n    const int block_row = lane_id / block_num_cols;\n    const int block_col = lane_id % block_num_cols;\n\n    // These are wasted threads per warp\n    if ( block_row >= block_num_rows ) { return; }\n\n    // The first row to consider. One row per warp.\n    int row = (blockIdx.x * blockDim.x + threadIdx.x) / WARP_SIZE;\n    const int row_offset = blockDim.x * gridDim.x / WARP_SIZE;\n\n    for ( ; row < num_rows ; row += row_offset )\n    {\n        int dense_row = row * block_num_rows + block_row;\n        // Iterate over each row and copy the elements into col-major A_dense\n        int row_end = A_csr_rows[row + 1];\n\n        for (int row_it = A_csr_rows[row]; row_it < row_end ; ++row_it )\n        {\n            int col = A_csr_cols[row_it];\n\n            if ( col >= num_rows ) { continue; } // Skip entries corresponding to halo\n\n            int dense_col = col * block_num_cols + block_col;\n            A_dense[dense_col * lda + dense_row] = A_csr_vals[block_mxn * row_it + lane_id];\n        }\n\n        // copy diagonal block\n        if ( A_csr_diag )\n        {\n            int diag_it = A_csr_diag[row];\n            int dense_col = row * block_num_cols + block_col; // diag means row=col\n            A_dense[dense_col * lda + dense_row] = A_csr_vals[block_mxn * diag_it + lane_id];\n        }\n    }\n}\n\n\ntemplate< int N, bool ROW_MAJOR, int WARP_SIZE, typename Value_type >\nstatic __device__ __forceinline__\nValue_type reduce_distributed_vectors( Value_type x, int is_leader, unsigned int active_mask )\n{\n    if ( N & (N - 1) )\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; ++i )\n        {\n            Value_type other_x = utils::shfl_down( x, ROW_MAJOR ? i : N * i, WARP_SIZE, active_mask );\n\n            if ( is_leader )\n            {\n                x += other_x;\n            }\n        }\n    }\n    else\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; i <<= 1 )\n        {\n            x += utils::shfl_xor( x, ROW_MAJOR ? i : N * i, WARP_SIZE, active_mask );\n        }\n    }\n\n    return x;\n}\n\n\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE,\n          int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid b_minus_A_halo_x( const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Matrix_type *__restrict A_vals,\n                       const int *__restrict A_diag,\n                       const Vector_type *x,\n                       const Vector_type *b,\n                       Vector_type *new_rhs,\n                       const int num_owned_rows)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_owned_rows;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    unsigned int active_mask = utils::ballot(is_active);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_owned_rows; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = a_row_it;\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id_mod_NxN_div_N]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_bmAx = b[N * a_row_id + lane_id_mod_NxN_mod_N];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Determine if the column is halo column\n            int a_col_is_valid = (a_col_id != -1) && (a_col_id >= num_owned_rows);\n            // Count the number of active columns.\n            // int vote =  __ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, WARP_SIZE, active_mask);\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k, WARP_SIZE, active_mask );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid)\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_NxN_mod_N]);\n                    //printf(\"loading entry %d, num_rows = %d, my_x = %f\\n\",uniform_a_col_id,num_owned_rows,my_x);\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        Vector_type my_Einv = (lane_id_mod_NxN == 0 || lane_id_mod_NxN == 5 || lane_id_mod_NxN == 10 || lane_id_mod_NxN == 15) ? 1. : 0.;\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader, active_mask );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader, active_mask );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                new_rhs[N * a_row_id + lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                new_rhs[N * a_row_id + lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n\ntemplate< typename Vector_type, typename Matrix_type, int N>\nvoid distributed_rhs_mod_dispatch( const int *__restrict A_rows,\n                                   const int *__restrict A_cols,\n                                   const Matrix_type *__restrict A_vals,\n                                   const int *__restrict A_diag,\n                                   const Vector_type *x,\n                                   const Vector_type *b,\n                                   Vector_type *new_rhs,\n                                   const int num_owned_rows,\n                                   const int row_major,\n                                   const int has_external_diag\n                                 )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_owned_rows + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n    // Branch to the correct kernel call.\n    int code = 2 * (row_major ? 1 : 0) + (has_external_diag ? 1 : 0);\n\n    switch ( code )\n    {\n        case 0: // Column-major, no external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 1: // Column-major, external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 2: // Row-major, no external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 3: // Row-major, external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\ndistributed_rhs_mod(\n    const Vector_d &x,\n    const Vector_d &b,\n    Vector_d &new_rhs)\n{\n    const Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n\n    switch (A->get_block_dimy())\n    {\n        case 1:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 1>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 2:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 2>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 3:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 3>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 4:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 4>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 5:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 5>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n    }\n}\n\n// copy non zero elements only\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\ncsr_to_dense(\n)\n{\n    const Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n    const int block_size = 256;\n    const int num_warps  = block_size / WARP_SIZE;\n    const int grid_size = (A->get_num_rows() + num_warps - 1) / num_warps;\n    cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n    csr_to_dense_kernel<Matrix_data, Vector_data, WARP_SIZE> <<< grid_size, block_size, 0, stream>>>(\n        A->get_num_rows(),\n        A->get_num_cols(),\n        A->get_block_dimx(),\n        A->get_block_dimy(),\n        A->row_offsets.raw(),\n        A->col_indices.raw(),\n        A->values.raw(),\n        A->hasProps(DIAG) ? A->diag.raw() : NULL,\n        m_dense_A,\n        m_lda\n    );\n    cudaCheckError();\n}\n\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::cudense_getrf()\n{\n    int wsize = 0;\n    cusolverStatus_t status1 = cusolverDnXgetrf_bufferSize(m_cuds_handle,\n                               m_num_rows,\n                               m_num_cols,\n                               m_dense_A,\n                               m_lda,\n                               &wsize);\n\n    if ( status1 != CUSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"Failed kernel in DenseLU\", AMGX_ERR_INTERNAL);\n    }\n\n    cudaCheckError();\n    allocMem(m_trf_wspace, wsize, false);\n    status1 = cusolverDnXgetrf(m_cuds_handle, m_num_rows, m_num_cols,\n                               m_dense_A, m_lda, m_trf_wspace, m_ipiv, m_cuds_info);\n    cudaCheckError();\n\n    if ( status1 != CUSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"failed kernel in Dense LU is likely due to invalid input parameters\",\n                    AMGX_ERR_INTERNAL);\n    }\n    else\n    {\n        int t_info;\n        cudaMemcpy(&t_info, m_cuds_info, sizeof(int), cudaMemcpyDefault);\n        cudaCheckError();\n\n        if (t_info != 0)\n        {\n            FatalError( \"Fail to get info from cudense\", AMGX_ERR_INTERNAL);\n        }\n        else\n        {\n            // We follow the standard established by Lapack and used in cudense.\n            if (t_info > 0)\n            {\n                FatalError( \"Dense LU factorization failed due to a singular matrix\", AMGX_ERR_INTERNAL);\n            }\n            else if (t_info < 0)\n            {\n                FatalError( \"Invalid input parameter(s) to dense LU\", AMGX_ERR_INTERNAL);\n            }\n        }\n    }\n\n    cudaCheckError();\n\n    if (m_trf_wspace)\n    {\n        amgx::memory::cudaFreeAsync(m_trf_wspace);\n        m_trf_wspace = 0;\n    }\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::cudense_getrs( Vector_d &x )\n{\n    //Solve L*X = RHS\n    cusolverStatus_t status = cusolverDnXgetrs(m_cuds_handle,\n                              CUBLAS_OP_N,\n                              m_num_rows,\n                              1,\n                              m_dense_A,\n                              m_lda,\n                              m_ipiv,\n                              (Matrix_data *)(x.raw()),\n                              m_num_rows,\n                              m_cuds_info);\n\n    if (status != CUSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"cuSolver trsv failed to solve Lx=rhs\", AMGX_ERR_INTERNAL);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate< class DataType, class IndexType >\nvoid\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nallocMem(DataType *&ptr,\n         IndexType numEntry,\n         bool initToZero)\n{\n    if ( ptr != NULL ) { amgx::memory::cudaFreeAsync(ptr); }\n    cudaCheckError();\n\n    size_t sz = numEntry * sizeof(DataType);\n    amgx::memory::cudaMallocAsync((void **)&ptr, sz);\n    cudaCheckError();\n\n    if (initToZero)\n    {\n        cudaMemset(ptr, 0x0, sz);\n        cudaCheckError();\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nDenseLUSolver(AMG_Config &cfg,\n              const std::string &cfg_scope,\n              ThreadManager *tmng)\n    : Solver<Config_d>(cfg, cfg_scope, tmng),\n      m_cuds_handle(0),\n      m_num_rows(0),\n      m_num_cols(0),\n      m_lda(0),\n      m_dense_A(0),\n      m_ipiv(0),\n      m_cuds_info(0),\n      m_trf_wspace(0)\n{\n    // Allocate a handle for cudense\n    cusolverStatus_t status = cusolverDnCreate(&m_cuds_handle);\n\n    if ( status != CUSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Allocate a handle for cublas\n    cublasStatus_t cublasStatus = cublasCreate(&m_cublas_handle);\n\n    if ( cublasStatus != CUBLAS_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUBLAS handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Define the cudense stream.\n    status = cusolverDnSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n    if ( status != CUSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Make sure we don't run more than 1 iteration.\n    this->set_max_iters(1);\n    allocMem(m_cuds_info, sizeof(int), false);\n\n    // Determine if the scalable coarse solve optimisation is enabled\n    m_enable_exact_solve = (\n        cfg.getParameter<AlgorithmType>( \"algorithm\", cfg_scope) == CLASSICAL &&\n        cfg.getParameter<int>( \"exact_coarse_solve\", cfg_scope) == 1);\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::~DenseLUSolver()\n{\n    if (m_cuds_handle) { cusolverDnDestroy(m_cuds_handle); }\n\n    if (m_cublas_handle) { cublasDestroy(m_cublas_handle); }\n\n    if (m_dense_A)\n    {\n        amgx::memory::cudaFreeAsync(m_dense_A);\n    }\n\n    if (m_ipiv)\n    {\n        amgx::memory::cudaFreeAsync(m_ipiv);\n    }\n\n    if (m_trf_wspace)\n    {\n        amgx::memory::cudaFreeAsync(m_trf_wspace);\n    }\n\n    if (m_cuds_info)\n    {\n        amgx::memory::cudaFreeAsync(m_cuds_info);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::solve_init(Vector_d &, Vector_d &, bool)\n{}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::solve_finalize(Vector_d &, Vector_d &)\n{}\n\n// Offset the local row offsets to global row offsets\ntemplate <class IndexType>\n__global__ void local_row_offsets_to_global(\n        int num_rows, int offset,\n        IndexType* local_Arows)\n{\n    int i = threadIdx.x + blockIdx.x*blockDim.x;\n    if(i >= num_rows) { return; }\n\n    local_Arows[i] += offset;\n}\n\n// Offset local packed column indices to global unpacked indices\ntemplate <class IndexType, class L2GType>\n__global__ void local_col_indices_to_global(\n        int nnz, int num_rows, int offset, IndexType* local_Acols, L2GType* l2g)\n{\n    int i = threadIdx.x + blockIdx.x*blockDim.x;\n    if(i >= nnz) { return; }\n\n    if(local_Acols[i] >= num_rows)\n    {\n        local_Acols[i] = l2g[local_Acols[i] - num_rows];\n    }\n    else\n    {\n        local_Acols[i] += offset;\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nsolver_setup(bool reuse_matrix_structure)\n{\n    Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n\n    if (!A)\n    {\n        FatalError(\"DenseLUSolver only works with explicit matrices\",\n                   AMGX_ERR_INTERNAL);\n    }\n\n    ViewType oldView = A->currentView();\n    A->setViewExterior();\n\n    if(A->is_matrix_distributed() && m_enable_exact_solve)\n    {\n#ifdef AMGX_WITH_MPI\n        int rank = A->manager->global_id();\n        int nranks = A->manager->get_num_partitions();\n        MPI_Comm comm = A->manager->getComms()->get_mpi_comm();\n\n        int offset, num_rows, nnz;\n        A->getOffsetAndSizeForView(OWNED, &offset, &num_rows);\n        A->getNnzForView(OWNED, &nnz);\n\n        m_num_rows = A->manager->num_rows_global * A->get_block_dimx();\n        m_num_cols = A->manager->num_rows_global * A->get_block_dimy();\n        m_lda = m_num_rows; // col-major\n\n        // Allocate mem for cudense pivoting sequence.\n        allocMem(m_ipiv, m_num_rows, false);\n\n        // Allocate memory to store the dense A and initialize to zero.\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_num_rows, true);\n\n        // Much of the data can be reused if we are performing a resetup\n        if (!reuse_matrix_structure)\n        {\n            // Gather the number of non zeros on each rank\n            A->manager->getComms()->all_gather(nnz, nz_all, nranks);\n\n            // Gather the number of rows on each rank\n            A->manager->getComms()->all_gather(num_rows, row_all, nranks);\n\n            // Get the number of non zeros on all ranks\n            m_nnz_global = thrust_wrapper::reduce<AMGX_host>(nz_all.begin(), nz_all.end());\n\n            // Turn the non-zero counts into displacements\n            nz_displs.resize(nranks);\n            thrust_wrapper::exclusive_scan<AMGX_host>(nz_all.begin(), nz_all.end(), nz_displs.begin());\n\n            // Turn the number of rows into displacements\n            row_displs.resize(nranks);\n            thrust_wrapper::exclusive_scan<AMGX_host>(row_all.begin(), row_all.end(), row_displs.begin());\n\n            IVector_d local_Acols_d(nnz);\n            IVector_d local_Arows_d(num_rows);\n\n            amgx::thrust::copy(A->col_indices.begin(), A->col_indices.begin() + nnz, local_Acols_d.begin());\n            amgx::thrust::copy(A->row_offsets.begin(), A->row_offsets.begin() + num_rows, local_Arows_d.begin());\n\n            // XXX Local to global map is the current limiting factor to enabling this\n            // code for the aggregation based path. It's not clear whether there is\n            // a structure that provides the same inverse mapping with aggregation.\n            // Note that at one point inverse_renumbering was tested for aggregation\n            // but didn't appear to work in all cases.\n\n            // Convert the local column indices and row offsets to the global index space\n            constexpr int nthreads = 128;\n            int nblocks = nnz / nthreads + 1;\n            local_col_indices_to_global<<<nblocks, nthreads>>>(nnz, num_rows, row_displs[rank], local_Acols_d.raw(), A->manager->local_to_global_map.raw());\n            cudaCheckError();\n\n            nblocks = num_rows / nthreads + 1;\n            local_row_offsets_to_global<<<nblocks, nthreads>>>(num_rows, nz_displs[rank], local_Arows_d.raw());\n            cudaCheckError();\n\n            // Copy the transformed indices to the host\n            IVector_h local_Acols_h(nnz);\n            IVector_h local_Arows_h(num_rows);\n            amgx::thrust::copy(local_Acols_d.begin(), local_Acols_d.end(), local_Acols_h.begin());\n            amgx::thrust::copy(local_Arows_d.begin(), local_Arows_d.end(), local_Arows_h.begin());\n\n            // Gather the local matrix structure redundantly to every rank\n            IVector_h Acols_global_h(m_nnz_global);\n            A->manager->getComms()->all_gather_v(local_Acols_h, nnz, Acols_global_h, nz_all, nz_displs);\n\n            // Note: Copy the local data to global without guard value\n            IVector_h Arows_global_h(m_num_rows + 1);\n            A->manager->getComms()->all_gather_v(local_Arows_h, num_rows, Arows_global_h, row_all, row_displs);\n\n            // Manually set the guard value on the global matrix\n            Arows_global_h[m_num_rows] = m_nnz_global;\n            Acols_global.resize(m_nnz_global);\n            Arows_global.resize(m_num_rows + 1);\n            amgx::thrust::copy(Acols_global_h.begin(), Acols_global_h.end(), Acols_global.begin());\n            amgx::thrust::copy(Arows_global_h.begin(), Arows_global_h.end(), Arows_global.begin());\n        }\n\n        // Fetch to the host a copy of the local sparse matrix\n        MVector_h local_Avals_h(nnz);\n        amgx::thrust::copy(A->values.begin(), A->values.begin() + nnz, local_Avals_h.begin());\n\n        // Gather the matrix values to all ranks\n        MVector_h Avals_global_h(m_nnz_global);\n        A->manager->getComms()->all_gather_v(local_Avals_h, nnz, Avals_global_h, nz_all, nz_displs);\n\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_lda, true);\n\n        MVector_d Avals_global(m_nnz_global);\n        amgx::thrust::copy(Avals_global_h.begin(), Avals_global_h.end(), Avals_global.begin());\n\n        const int block_size = 256;\n        const int num_warps = block_size / WARP_SIZE;\n        const int grid_size = (m_nnz_global + num_warps - 1) / num_warps;\n        cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n        csr_to_dense_kernel<Matrix_data, Vector_data, WARP_SIZE><<<grid_size, block_size, 0, stream>>>(\n            m_num_rows,\n            m_num_cols,\n            A->get_block_dimx(),\n            A->get_block_dimy(),\n            Arows_global.raw(),\n            Acols_global.raw(),\n            Avals_global.raw(),\n            A->hasProps(DIAG) ? A->diag.raw() : NULL,\n            m_dense_A,\n            m_lda);\n            cudaCheckError();\n\n        cudaStreamSynchronize(stream);\n        cudaCheckError();\n#endif\n    }\n    else\n    {\n        ViewType oldView = A->currentView();\n        A->setViewExterior();\n        m_num_rows = A->get_num_rows() * A->get_block_dimx();\n        // don't use A->get_num_cols() because A is rectangular.\n        // Only the diagonal block owned by this rank is factored.\n        m_num_cols = A->get_num_rows() * A->get_block_dimy();\n        m_lda = m_num_rows; // col-major\n        // Allocate mem for cudense pivoting sequence.\n        allocMem(m_ipiv, m_num_rows, false);\n        // Allocate memory to store the dense A and initialize to zero.\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_lda, true);\n        csr_to_dense(); // copy sparse A to dense_A\n    }\n\n    cudense_getrf(); // do LU factor\n    A->setView(oldView);\n}\n\n// There is one subtle point here (for inexact solve):\n// We only do LU on the diagonal blocks associated with each rank.\n// Halo is used to update the right-hand-side (RHS) vector.\n// For multi GPU cases, this is essentially block Jacobi. Since the block size\n// is the size of each partition, this is OK trade-off between accuracy and runtime.\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nAMGX_STATUS\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nsolve_iteration(Vector_d &rhs,\n                Vector_d &x,\n                bool xIsZero)\n{\n    Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n    ViewType oldView = A->currentView();\n    A->setViewExterior();\n\n    if(A->is_matrix_distributed() && m_enable_exact_solve)\n    {\n#ifdef AMGX_WITH_MPI\n        int offset, num_rows;\n        A->getOffsetAndSizeForView(OWNED, &offset, &num_rows);\n\n        int rank = A->manager->global_id();\n        int nranks = A->manager->get_num_partitions();\n        MPI_Comm comm = A->manager->getComms()->get_mpi_comm();\n\n        // Make host copy of the RHS\n        MVector_h rhs_local_h(num_rows);\n        amgx::thrust::copy(rhs.begin(), rhs.begin() + num_rows, rhs_local_h.begin());\n\n        // Gather the local RHS from all ranks to global vectors on all ranks\n        MVector_h rhs_global_h(m_num_rows);\n        A->manager->getComms()->all_gather_v(rhs_local_h, num_rows, rhs_global_h, row_all, row_displs);\n\n        //Solve L*X = RHS\n        MVector_d x_global(m_num_rows);\n        amgx::thrust::copy(rhs_global_h.begin(), rhs_global_h.end(), x_global.begin());\n        cusolverStatus_t status = \n            cusolverDnXgetrs(m_cuds_handle,\n                             CUBLAS_OP_N,\n                             m_num_rows,\n                             1,\n                             m_dense_A,\n                             m_lda,\n                             m_ipiv,\n                             x_global.raw(),\n                             m_num_rows,\n                             m_cuds_info);\n\n        // Copy the local portion of the solution back into x\n        amgx::thrust::copy(x_global.begin() + row_displs[rank], x_global.begin() + row_displs[rank] + num_rows, x.begin());\n\n        if (status != CUSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"cuSolver trsv failed to solve Lx=rhs\", AMGX_ERR_INTERNAL);\n        }\n#endif\n    }\n    else\n    {\n        Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n        ViewType oldView = A->currentView();\n        A->setViewExterior();\n\n        if ((!A->is_matrix_singleGPU()) && (!xIsZero))\n        {\n            // Modify rhs to include contribution from halo nodes\n            // i.e. new_rhs = b - A_halo*x;\n            // Note: dense_lu solver doesn't support latency hiding\n            A->manager->exchange_halo_async(x, x.tag);\n            A->manager->exchange_halo_wait(x, x.tag);\n            Vector_d new_rhs(rhs.size());\n            distributed_rhs_mod(x, rhs, new_rhs);\n            amgx::thrust::copy(new_rhs.begin(), new_rhs.begin() + m_num_rows, x.begin());\n            cudaCheckError();\n        }\n        else\n        {\n            x.copy(rhs);\n        }\n\n        cudense_getrs(x); // triangular solves\n    }\n\n    //Speculative send of x vector\n    x.dirtybit = 1;\n    A->setView(oldView);\n    return AMGX_ST_CONVERGED; // direct solver always converges\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class DenseLUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//  AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace dense_lu\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <string.h>\n#include <cutil.h>\n#include <miscmath.h>\n#include <amgx_cusparse.h>\n#include <thrust/copy.h>\n#include <basic_types.h>\n#include <util.h>\n#include <thrust/logical.h>\n#include <sm_utils.inl>\n#include <texture.h>\n#include <typeinfo>\n\n#include \"solvers/dense_lu_solver.h\"\n#include \"solvers/block_common_solver.h\"\n\n#include \"amgx_types/util.h\"\n\n#include <algorithm>\n\n//trick to get nvcc to print a defined value\n//(__CUDA_ARCH__) @ compile time:\n//\nnamespace //unnamed namespace\n{\nstruct X\n{\n    private:\n        X(void) {}\n};\n\ntemplate<int N>\nstruct Print_N_atCompile\n{\n    X array[N];\n};\n}//end unnamed namespace\n\nnamespace amgx\n{\nnamespace dense_lu_solver\n{\n\nenum { WARP_SIZE = 32, CTA_SIZE = 128 };\n\n//\n// supporting kernels\n//\ntemplate< typename T,  typename Tv, int WARP_SIZE >\n__global__\nvoid csr_to_dense_kernel(\n    const int num_rows,\n    const int num_cols,\n    const int block_num_rows,\n    const int block_num_cols,\n    const int *__restrict A_csr_rows,\n    const int *__restrict A_csr_cols,\n    const T   *__restrict A_csr_vals,\n    const int *__restrict A_csr_diag,\n    T   *__restrict A_dense,\n    const int lda)\n{\n    // Note:\n    // To handle block CSR, the sparsity pattern csr_rows and csr_cols only store\n    // the typical csr info assuming each block is a scalar.\n    // The values in csr_vals has all entries in the blocks, using row major to\n    // store the block. So we need the number of entries in each block as stride.\n    const int block_mxn = block_num_rows * block_num_cols;\n    // Each lane copies one entry in a block and iterate through row sparsity pattern.\n    // Essentially one warp per row-block. For 4x4, we have 16 working threads per warp.\n    const int lane_id = threadIdx.x % WARP_SIZE;\n    // find the (row,col) local to a block\n    const int block_row = lane_id / block_num_cols;\n    const int block_col = lane_id % block_num_cols;\n\n    // These are wasted threads per warp\n    if ( block_row >= block_num_rows ) { return; }\n\n    // The first row to consider. One row per warp.\n    int row = (blockIdx.x * blockDim.x + threadIdx.x) / WARP_SIZE;\n    const int row_offset = blockDim.x * gridDim.x / WARP_SIZE;\n\n    for ( ; row < num_rows ; row += row_offset )\n    {\n        int dense_row = row * block_num_rows + block_row;\n        // Iterate over each row and copy the elements into col-major A_dense\n        int row_end = A_csr_rows[row + 1];\n\n        for (int row_it = A_csr_rows[row]; row_it < row_end ; ++row_it )\n        {\n            int col = A_csr_cols[row_it];\n\n            if ( col >= num_rows ) { continue; } // Skip entries corresponding to halo\n\n            int dense_col = col * block_num_cols + block_col;\n            A_dense[dense_col * lda + dense_row] = A_csr_vals[block_mxn * row_it + lane_id];\n        }\n\n        // copy diagonal block\n        if ( A_csr_diag )\n        {\n            int diag_it = A_csr_diag[row];\n            int dense_col = row * block_num_cols + block_col; // diag means row=col\n            A_dense[dense_col * lda + dense_row] = A_csr_vals[block_mxn * diag_it + lane_id];\n        }\n    }\n}\n\n\ntemplate< int N, bool ROW_MAJOR, int WARP_SIZE, typename Value_type >\nstatic __device__ __forceinline__\nValue_type reduce_distributed_vectors( Value_type x, int is_leader, unsigned int active_mask )\n{\n    if ( N & (N - 1) )\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; ++i )\n        {\n            Value_type other_x = utils::shfl_down( x, ROW_MAJOR ? i : N * i, WARP_SIZE, active_mask );\n\n            if ( is_leader )\n            {\n                x += other_x;\n            }\n        }\n    }\n    else\n    {\n#pragma unroll\n\n        for ( int i = 1 ; i < N ; i <<= 1 )\n        {\n            x += utils::shfl_xor( x, ROW_MAJOR ? i : N * i, WARP_SIZE, active_mask );\n        }\n    }\n\n    return x;\n}\n\n\n\ntemplate< typename Matrix_type, typename Vector_type, int N, int CTA_SIZE,\n          int WARP_SIZE, bool ROW_MAJOR, bool HAS_EXTERNAL_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 12 )\nvoid b_minus_A_halo_x( const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       const Matrix_type *__restrict A_vals,\n                       const int *__restrict A_diag,\n                       const Vector_type *x,\n                       const Vector_type *b,\n                       Vector_type *new_rhs,\n                       const int num_owned_rows)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ITEMS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ITEMS_PER_CTA = NUM_ITEMS_PER_WARP * NUM_WARPS_PER_CTA;\n    // Number of items per grid.\n    const int NUM_ITEMS_PER_GRID = gridDim.x * NUM_ITEMS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    // Useful index to compute matrix products.\n    const int lane_id_mod_NxN_div_N = lane_id_mod_NxN / N;\n    const int lane_id_mod_NxN_mod_N = lane_id_mod_NxN % N;\n    // We to get my data from when I use SHFL.\n    const int shfl_offset = lane_id - lane_id_mod_NxN;\n    // Shared memory needed to exchange X and delta.\n    __shared__ volatile Vector_type s_mem[CTA_SIZE];\n    // Each thread keeps its own pointer to shared memory to avoid some extra computations.\n    volatile Vector_type *my_s_mem = &s_mem[threadIdx.x - lane_id_mod_NxN];\n    // Is the thread active? For example, for 5x5 only the first 25 threads are active per warp.\n    // At compile time, the compiler will see is_active == true for 2x2 (since NxN & (NxN-1) evals\n    // to false ; that's the common trick to determine if a number is a power of 2).\n    int is_active = true;\n\n    if ( NxN & (NxN - 1) )\n    {\n        is_active = lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n    }\n\n    // Determine which NxN block the threads work with.\n    int a_row_it = num_owned_rows;\n\n    if ( is_active )\n    {\n        a_row_it = blockIdx.x * NUM_ITEMS_PER_CTA + warp_id * NUM_ITEMS_PER_WARP + lane_id_div_NxN;\n    }\n\n    unsigned int active_mask = utils::ballot(is_active);\n\n    // Iterate over the rows of the matrix. One warp per row.\n    for ( ; a_row_it < num_owned_rows; a_row_it += NUM_ITEMS_PER_GRID )\n    {\n        int a_row_id = a_row_it;\n        // Load one block of B.\n        Vector_type my_bmAx(0);\n\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_bmAx = __cachingLoad(&b[N * a_row_id + lane_id_mod_NxN_div_N]);\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_bmAx = b[N * a_row_id + lane_id_mod_NxN_mod_N];\n            }\n        }\n\n        // Don't do anything if X is zero.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // If the diagonal is stored separately, we have a special treatment.\n        int a_col_max = a_col_end;\n\n        // Each warp load column indices of 32 nonzero blocks\n        for ( ; utils::any( a_col_begin < a_col_max ) ; a_col_begin += NxN )\n        {\n            // Each thread loads a single element. If !is_active, a_col_end == 0.\n            int a_col_it = a_col_begin + lane_id_mod_NxN;\n            // Get the ID of the column.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Determine if the column is halo column\n            int a_col_is_valid = (a_col_id != -1) && (a_col_id >= num_owned_rows);\n            // Count the number of active columns.\n            // int vote =  __ballot(aColId != -1);\n            // The number of iterations.\n            // int nCols = max( __popc( vote & 0x0000ffff ), __popc( vote & 0xffff0000 ) );\n\n            // Loop over columns. We compute 8 columns per iteration.\n            for ( int k = 0 ; k < NxN ; k += N )\n            {\n                int my_k = k + lane_id_mod_NxN_div_N;\n                // Load N blocks of X.\n                int uniform_a_col_id = utils::shfl( a_col_id, shfl_offset + my_k, WARP_SIZE, active_mask);\n                int uniform_a_col_is_valid = utils::shfl( a_col_is_valid, shfl_offset + my_k, WARP_SIZE, active_mask );\n                Vector_type my_x(0);\n\n                if ( uniform_a_col_id != -1 && uniform_a_col_is_valid)\n                {\n                    my_x = __cachingLoad(&x[N * uniform_a_col_id + lane_id_mod_NxN_mod_N]);\n                    //printf(\"loading entry %d, num_rows = %d, my_x = %f\\n\",uniform_a_col_id,num_owned_rows,my_x);\n                }\n\n                my_s_mem[lane_id_mod_NxN] = my_x;\n                // Load N blocks of A.\n#pragma unroll\n\n                for ( int i = 0 ; i < N ; ++i )\n                {\n                    int uniform_a_col_tmp = a_col_begin + k + i, uniform_a_col_it = -1;\n\n                    if ( uniform_a_col_tmp < a_col_end )\n                    {\n                        uniform_a_col_it = uniform_a_col_tmp;\n                    }\n\n                    Matrix_type my_val(0);\n\n                    if ( uniform_a_col_it != -1 )\n                    {\n                        my_val = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    if ( ROW_MAJOR )\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_mod_N];\n                    }\n                    else\n                    {\n                        my_bmAx -= my_val * my_s_mem[N * i + lane_id_mod_NxN_div_N];\n                    }\n                }\n            } // Loop over k\n        } // Loop over aColIt\n\n        Vector_type my_Einv = (lane_id_mod_NxN == 0 || lane_id_mod_NxN == 5 || lane_id_mod_NxN == 10 || lane_id_mod_NxN == 15) ? 1. : 0.;\n        // Reduce bmAx terms.\n        int is_leader = lane_id_mod_NxN_div_N == 0;\n\n        if ( ROW_MAJOR )\n        {\n            is_leader = lane_id_mod_NxN_mod_N == 0;\n        }\n\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader, active_mask );\n\n        // Update the shared terms.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                my_s_mem[lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n\n        // Update the diagonal term.\n        if ( ROW_MAJOR )\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_mod_N];\n        }\n        else\n        {\n            my_bmAx = my_Einv * my_s_mem[lane_id_mod_NxN_div_N];\n        }\n\n        // Reduce bmAx terms.\n        my_bmAx = reduce_distributed_vectors<N, ROW_MAJOR, WARP_SIZE>( my_bmAx, is_leader, active_mask );\n\n        // Store the results.\n        if ( ROW_MAJOR )\n        {\n            if ( lane_id_mod_NxN_mod_N == 0 )\n            {\n                new_rhs[N * a_row_id + lane_id_mod_NxN_div_N] = my_bmAx;\n            }\n        }\n        else\n        {\n            if ( lane_id_mod_NxN_div_N == 0 )\n            {\n                new_rhs[N * a_row_id + lane_id_mod_NxN_mod_N] = my_bmAx;\n            }\n        }\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n\ntemplate< typename Vector_type, typename Matrix_type, int N>\nvoid distributed_rhs_mod_dispatch( const int *__restrict A_rows,\n                                   const int *__restrict A_cols,\n                                   const Matrix_type *__restrict A_vals,\n                                   const int *__restrict A_diag,\n                                   const Vector_type *x,\n                                   const Vector_type *b,\n                                   Vector_type *new_rhs,\n                                   const int num_owned_rows,\n                                   const int row_major,\n                                   const int has_external_diag\n                                 )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int NUM_ROWS_PER_WARP = WARP_SIZE / NxN;\n    // Number of items computer per CTA.\n    const int NUM_ROWS_PER_CTA = NUM_ROWS_PER_WARP * NUM_WARPS_PER_CTA;\n    // The number of threads to launch.\n    const int grid_size = std::min( 4096, (num_owned_rows + NUM_ROWS_PER_CTA - 1) / NUM_ROWS_PER_CTA );\n    // Branch to the correct kernel call.\n    int code = 2 * (row_major ? 1 : 0) + (has_external_diag ? 1 : 0);\n\n    switch ( code )\n    {\n        case 0: // Column-major, no external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 1: // Column-major, external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, false, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 2: // Row-major, no external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, false> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        case 3: // Row-major, external diagonal.\n            b_minus_A_halo_x<Matrix_type, Vector_type, N, CTA_SIZE, WARP_SIZE, true, true> <<< grid_size, CTA_SIZE>>>(\n                A_rows,\n                A_cols,\n                A_vals,\n                A_diag,\n                x,\n                b,\n                new_rhs,\n                num_owned_rows\n            );\n            cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"Internal error\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\ndistributed_rhs_mod(\n    const Vector_d &x,\n    const Vector_d &b,\n    Vector_d &new_rhs)\n{\n    const Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n\n    switch (A->get_block_dimy())\n    {\n        case 1:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 1>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 2:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 2>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 3:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 3>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 4:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 4>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n\n        case 5:\n            distributed_rhs_mod_dispatch<Vector_data, Matrix_data, 5>(A->row_offsets.raw(), A->col_indices.raw(), A->values.raw(), A->diag.raw(), x.raw(), b.raw(), new_rhs.raw(), A->get_num_rows(), A->getBlockFormat() == ROW_MAJOR, A->hasProps(DIAG));\n            break;\n    }\n}\n\n// copy non zero elements only\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\ncsr_to_dense(\n)\n{\n    const Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n    const int block_size = 256;\n    const int num_warps  = block_size / WARP_SIZE;\n    const int grid_size = (A->get_num_rows() + num_warps - 1) / num_warps;\n    hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n    csr_to_dense_kernel<Matrix_data, Vector_data, WARP_SIZE> <<< grid_size, block_size, 0, stream>>>(\n        A->get_num_rows(),\n        A->get_num_cols(),\n        A->get_block_dimx(),\n        A->get_block_dimy(),\n        A->row_offsets.raw(),\n        A->col_indices.raw(),\n        A->values.raw(),\n        A->hasProps(DIAG) ? A->diag.raw() : NULL,\n        m_dense_A,\n        m_lda\n    );\n    cudaCheckError();\n}\n\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::cudense_getrf()\n{\n    int wsize = 0;\n    hipsolverStatus_t status1 = hipsolverDnXgetrf_bufferSize(m_cuds_handle,\n                               m_num_rows,\n                               m_num_cols,\n                               m_dense_A,\n                               m_lda,\n                               &wsize);\n\n    if ( status1 != HIPSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"Failed kernel in DenseLU\", AMGX_ERR_INTERNAL);\n    }\n\n    cudaCheckError();\n    allocMem(m_trf_wspace, wsize, false);\n    status1 = hipsolverDnXgetrf(m_cuds_handle, m_num_rows, m_num_cols,\n                               m_dense_A, m_lda, m_trf_wspace, m_ipiv, m_cuds_info);\n    cudaCheckError();\n\n    if ( status1 != HIPSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"failed kernel in Dense LU is likely due to invalid input parameters\",\n                    AMGX_ERR_INTERNAL);\n    }\n    else\n    {\n        int t_info;\n        hipMemcpy(&t_info, m_cuds_info, sizeof(int), hipMemcpyDefault);\n        cudaCheckError();\n\n        if (t_info != 0)\n        {\n            FatalError( \"Fail to get info from cudense\", AMGX_ERR_INTERNAL);\n        }\n        else\n        {\n            // We follow the standard established by Lapack and used in cudense.\n            if (t_info > 0)\n            {\n                FatalError( \"Dense LU factorization failed due to a singular matrix\", AMGX_ERR_INTERNAL);\n            }\n            else if (t_info < 0)\n            {\n                FatalError( \"Invalid input parameter(s) to dense LU\", AMGX_ERR_INTERNAL);\n            }\n        }\n    }\n\n    cudaCheckError();\n\n    if (m_trf_wspace)\n    {\n        amgx::memory::hipFreeAsync(m_trf_wspace);\n        m_trf_wspace = 0;\n    }\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::cudense_getrs( Vector_d &x )\n{\n    //Solve L*X = RHS\n    hipsolverStatus_t status = hipsolverDnXgetrs(m_cuds_handle,\n                              HIPBLAS_OP_N,\n                              m_num_rows,\n                              1,\n                              m_dense_A,\n                              m_lda,\n                              m_ipiv,\n                              (Matrix_data *)(x.raw()),\n                              m_num_rows,\n                              m_cuds_info);\n\n    if (status != HIPSOLVER_STATUS_SUCCESS)\n    {\n        FatalError( \"cuSolver trsv failed to solve Lx=rhs\", AMGX_ERR_INTERNAL);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate< class DataType, class IndexType >\nvoid\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nallocMem(DataType *&ptr,\n         IndexType numEntry,\n         bool initToZero)\n{\n    if ( ptr != NULL ) { amgx::memory::hipFreeAsync(ptr); }\n    cudaCheckError();\n\n    size_t sz = numEntry * sizeof(DataType);\n    amgx::memory::hipMallocAsync((void **)&ptr, sz);\n    cudaCheckError();\n\n    if (initToZero)\n    {\n        hipMemset(ptr, 0x0, sz);\n        cudaCheckError();\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nDenseLUSolver(AMG_Config &cfg,\n              const std::string &cfg_scope,\n              ThreadManager *tmng)\n    : Solver<Config_d>(cfg, cfg_scope, tmng),\n      m_cuds_handle(0),\n      m_num_rows(0),\n      m_num_cols(0),\n      m_lda(0),\n      m_dense_A(0),\n      m_ipiv(0),\n      m_cuds_info(0),\n      m_trf_wspace(0)\n{\n    // Allocate a handle for cudense\n    hipsolverStatus_t status = hipsolverDnCreate(&m_cuds_handle);\n\n    if ( status != HIPSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUDENSE handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Allocate a handle for cublas\n    hipblasStatus_t hipblasStatus_t = hipblasCreate(&m_cublas_handle);\n\n    if ( hipblasStatus_t != HIPBLAS_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not create the CUBLAS handle\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Define the cudense stream.\n    status = hipsolverSetStream(m_cuds_handle, amgx::thrust::global_thread_handle::get_stream());\n\n    if ( status != HIPSOLVER_STATUS_SUCCESS )\n    {\n        FatalError( \"Could not set the stream for CUDENSE\", AMGX_ERR_CUDA_FAILURE );\n    }\n\n    // Make sure we don't run more than 1 iteration.\n    this->set_max_iters(1);\n    allocMem(m_cuds_info, sizeof(int), false);\n\n    // Determine if the scalable coarse solve optimisation is enabled\n    m_enable_exact_solve = (\n        cfg.getParameter<AlgorithmType>( \"algorithm\", cfg_scope) == CLASSICAL &&\n        cfg.getParameter<int>( \"exact_coarse_solve\", cfg_scope) == 1);\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::~DenseLUSolver()\n{\n    if (m_cuds_handle) { hipsolverDnDestroy(m_cuds_handle); }\n\n    if (m_cublas_handle) { hipblasDestroy(m_cublas_handle); }\n\n    if (m_dense_A)\n    {\n        amgx::memory::hipFreeAsync(m_dense_A);\n    }\n\n    if (m_ipiv)\n    {\n        amgx::memory::hipFreeAsync(m_ipiv);\n    }\n\n    if (m_trf_wspace)\n    {\n        amgx::memory::hipFreeAsync(m_trf_wspace);\n    }\n\n    if (m_cuds_info)\n    {\n        amgx::memory::hipFreeAsync(m_cuds_info);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::solve_init(Vector_d &, Vector_d &, bool)\n{}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::solve_finalize(Vector_d &, Vector_d &)\n{}\n\n// Offset the local row offsets to global row offsets\ntemplate <class IndexType>\n__global__ void local_row_offsets_to_global(\n        int num_rows, int offset,\n        IndexType* local_Arows)\n{\n    int i = threadIdx.x + blockIdx.x*blockDim.x;\n    if(i >= num_rows) { return; }\n\n    local_Arows[i] += offset;\n}\n\n// Offset local packed column indices to global unpacked indices\ntemplate <class IndexType, class L2GType>\n__global__ void local_col_indices_to_global(\n        int nnz, int num_rows, int offset, IndexType* local_Acols, L2GType* l2g)\n{\n    int i = threadIdx.x + blockIdx.x*blockDim.x;\n    if(i >= nnz) { return; }\n\n    if(local_Acols[i] >= num_rows)\n    {\n        local_Acols[i] = l2g[local_Acols[i] - num_rows];\n    }\n    else\n    {\n        local_Acols[i] += offset;\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid DenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nsolver_setup(bool reuse_matrix_structure)\n{\n    Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n\n    if (!A)\n    {\n        FatalError(\"DenseLUSolver only works with explicit matrices\",\n                   AMGX_ERR_INTERNAL);\n    }\n\n    ViewType oldView = A->currentView();\n    A->setViewExterior();\n\n    if(A->is_matrix_distributed() && m_enable_exact_solve)\n    {\n#ifdef AMGX_WITH_MPI\n        int rank = A->manager->global_id();\n        int nranks = A->manager->get_num_partitions();\n        MPI_Comm comm = A->manager->getComms()->get_mpi_comm();\n\n        int offset, num_rows, nnz;\n        A->getOffsetAndSizeForView(OWNED, &offset, &num_rows);\n        A->getNnzForView(OWNED, &nnz);\n\n        m_num_rows = A->manager->num_rows_global * A->get_block_dimx();\n        m_num_cols = A->manager->num_rows_global * A->get_block_dimy();\n        m_lda = m_num_rows; // col-major\n\n        // Allocate mem for cudense pivoting sequence.\n        allocMem(m_ipiv, m_num_rows, false);\n\n        // Allocate memory to store the dense A and initialize to zero.\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_num_rows, true);\n\n        // Much of the data can be reused if we are performing a resetup\n        if (!reuse_matrix_structure)\n        {\n            // Gather the number of non zeros on each rank\n            A->manager->getComms()->all_gather(nnz, nz_all, nranks);\n\n            // Gather the number of rows on each rank\n            A->manager->getComms()->all_gather(num_rows, row_all, nranks);\n\n            // Get the number of non zeros on all ranks\n            m_nnz_global = thrust_wrapper::reduce<AMGX_host>(nz_all.begin(), nz_all.end());\n\n            // Turn the non-zero counts into displacements\n            nz_displs.resize(nranks);\n            thrust_wrapper::exclusive_scan<AMGX_host>(nz_all.begin(), nz_all.end(), nz_displs.begin());\n\n            // Turn the number of rows into displacements\n            row_displs.resize(nranks);\n            thrust_wrapper::exclusive_scan<AMGX_host>(row_all.begin(), row_all.end(), row_displs.begin());\n\n            IVector_d local_Acols_d(nnz);\n            IVector_d local_Arows_d(num_rows);\n\n            amgx::thrust::copy(A->col_indices.begin(), A->col_indices.begin() + nnz, local_Acols_d.begin());\n            amgx::thrust::copy(A->row_offsets.begin(), A->row_offsets.begin() + num_rows, local_Arows_d.begin());\n\n            // XXX Local to global map is the current limiting factor to enabling this\n            // code for the aggregation based path. It's not clear whether there is\n            // a structure that provides the same inverse mapping with aggregation.\n            // Note that at one point inverse_renumbering was tested for aggregation\n            // but didn't appear to work in all cases.\n\n            // Convert the local column indices and row offsets to the global index space\n            constexpr int nthreads = 128;\n            int nblocks = nnz / nthreads + 1;\n            local_col_indices_to_global<<<nblocks, nthreads>>>(nnz, num_rows, row_displs[rank], local_Acols_d.raw(), A->manager->local_to_global_map.raw());\n            cudaCheckError();\n\n            nblocks = num_rows / nthreads + 1;\n            local_row_offsets_to_global<<<nblocks, nthreads>>>(num_rows, nz_displs[rank], local_Arows_d.raw());\n            cudaCheckError();\n\n            // Copy the transformed indices to the host\n            IVector_h local_Acols_h(nnz);\n            IVector_h local_Arows_h(num_rows);\n            amgx::thrust::copy(local_Acols_d.begin(), local_Acols_d.end(), local_Acols_h.begin());\n            amgx::thrust::copy(local_Arows_d.begin(), local_Arows_d.end(), local_Arows_h.begin());\n\n            // Gather the local matrix structure redundantly to every rank\n            IVector_h Acols_global_h(m_nnz_global);\n            A->manager->getComms()->all_gather_v(local_Acols_h, nnz, Acols_global_h, nz_all, nz_displs);\n\n            // Note: Copy the local data to global without guard value\n            IVector_h Arows_global_h(m_num_rows + 1);\n            A->manager->getComms()->all_gather_v(local_Arows_h, num_rows, Arows_global_h, row_all, row_displs);\n\n            // Manually set the guard value on the global matrix\n            Arows_global_h[m_num_rows] = m_nnz_global;\n            Acols_global.resize(m_nnz_global);\n            Arows_global.resize(m_num_rows + 1);\n            amgx::thrust::copy(Acols_global_h.begin(), Acols_global_h.end(), Acols_global.begin());\n            amgx::thrust::copy(Arows_global_h.begin(), Arows_global_h.end(), Arows_global.begin());\n        }\n\n        // Fetch to the host a copy of the local sparse matrix\n        MVector_h local_Avals_h(nnz);\n        amgx::thrust::copy(A->values.begin(), A->values.begin() + nnz, local_Avals_h.begin());\n\n        // Gather the matrix values to all ranks\n        MVector_h Avals_global_h(m_nnz_global);\n        A->manager->getComms()->all_gather_v(local_Avals_h, nnz, Avals_global_h, nz_all, nz_displs);\n\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_lda, true);\n\n        MVector_d Avals_global(m_nnz_global);\n        amgx::thrust::copy(Avals_global_h.begin(), Avals_global_h.end(), Avals_global.begin());\n\n        const int block_size = 256;\n        const int num_warps = block_size / WARP_SIZE;\n        const int grid_size = (m_nnz_global + num_warps - 1) / num_warps;\n        hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n        csr_to_dense_kernel<Matrix_data, Vector_data, WARP_SIZE><<<grid_size, block_size, 0, stream>>>(\n            m_num_rows,\n            m_num_cols,\n            A->get_block_dimx(),\n            A->get_block_dimy(),\n            Arows_global.raw(),\n            Acols_global.raw(),\n            Avals_global.raw(),\n            A->hasProps(DIAG) ? A->diag.raw() : NULL,\n            m_dense_A,\n            m_lda);\n            cudaCheckError();\n\n        hipStreamSynchronize(stream);\n        cudaCheckError();\n#endif\n    }\n    else\n    {\n        ViewType oldView = A->currentView();\n        A->setViewExterior();\n        m_num_rows = A->get_num_rows() * A->get_block_dimx();\n        // don't use A->get_num_cols() because A is rectangular.\n        // Only the diagonal block owned by this rank is factored.\n        m_num_cols = A->get_num_rows() * A->get_block_dimy();\n        m_lda = m_num_rows; // col-major\n        // Allocate mem for cudense pivoting sequence.\n        allocMem(m_ipiv, m_num_rows, false);\n        // Allocate memory to store the dense A and initialize to zero.\n        allocMem(m_dense_A, static_cast<size_t>(m_num_cols) * m_lda, true);\n        csr_to_dense(); // copy sparse A to dense_A\n    }\n\n    cudense_getrf(); // do LU factor\n    A->setView(oldView);\n}\n\n// There is one subtle point here (for inexact solve):\n// We only do LU on the diagonal blocks associated with each rank.\n// Halo is used to update the right-hand-side (RHS) vector.\n// For multi GPU cases, this is essentially block Jacobi. Since the block size\n// is the size of each partition, this is OK trade-off between accuracy and runtime.\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nAMGX_STATUS\nDenseLUSolver<TemplateConfig<AMGX_device, V, M, I> >::\nsolve_iteration(Vector_d &rhs,\n                Vector_d &x,\n                bool xIsZero)\n{\n    Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n    ViewType oldView = A->currentView();\n    A->setViewExterior();\n\n    if(A->is_matrix_distributed() && m_enable_exact_solve)\n    {\n#ifdef AMGX_WITH_MPI\n        int offset, num_rows;\n        A->getOffsetAndSizeForView(OWNED, &offset, &num_rows);\n\n        int rank = A->manager->global_id();\n        int nranks = A->manager->get_num_partitions();\n        MPI_Comm comm = A->manager->getComms()->get_mpi_comm();\n\n        // Make host copy of the RHS\n        MVector_h rhs_local_h(num_rows);\n        amgx::thrust::copy(rhs.begin(), rhs.begin() + num_rows, rhs_local_h.begin());\n\n        // Gather the local RHS from all ranks to global vectors on all ranks\n        MVector_h rhs_global_h(m_num_rows);\n        A->manager->getComms()->all_gather_v(rhs_local_h, num_rows, rhs_global_h, row_all, row_displs);\n\n        //Solve L*X = RHS\n        MVector_d x_global(m_num_rows);\n        amgx::thrust::copy(rhs_global_h.begin(), rhs_global_h.end(), x_global.begin());\n        hipsolverStatus_t status = \n            hipsolverDnXgetrs(m_cuds_handle,\n                             HIPBLAS_OP_N,\n                             m_num_rows,\n                             1,\n                             m_dense_A,\n                             m_lda,\n                             m_ipiv,\n                             x_global.raw(),\n                             m_num_rows,\n                             m_cuds_info);\n\n        // Copy the local portion of the solution back into x\n        amgx::thrust::copy(x_global.begin() + row_displs[rank], x_global.begin() + row_displs[rank] + num_rows, x.begin());\n\n        if (status != HIPSOLVER_STATUS_SUCCESS)\n        {\n            FatalError(\"cuSolver trsv failed to solve Lx=rhs\", AMGX_ERR_INTERNAL);\n        }\n#endif\n    }\n    else\n    {\n        Matrix_d *A = dynamic_cast<Matrix_d *>(Base::m_A);\n        ViewType oldView = A->currentView();\n        A->setViewExterior();\n\n        if ((!A->is_matrix_singleGPU()) && (!xIsZero))\n        {\n            // Modify rhs to include contribution from halo nodes\n            // i.e. new_rhs = b - A_halo*x;\n            // Note: dense_lu solver doesn't support latency hiding\n            A->manager->exchange_halo_async(x, x.tag);\n            A->manager->exchange_halo_wait(x, x.tag);\n            Vector_d new_rhs(rhs.size());\n            distributed_rhs_mod(x, rhs, new_rhs);\n            amgx::thrust::copy(new_rhs.begin(), new_rhs.begin() + m_num_rows, x.begin());\n            cudaCheckError();\n        }\n        else\n        {\n            x.copy(rhs);\n        }\n\n        cudense_getrs(x); // triangular solves\n    }\n\n    //Speculative send of x vector\n    x.dirtybit = 1;\n    A->setView(oldView);\n    return AMGX_ST_CONVERGED; // direct solver always converges\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class DenseLUSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n//  AMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace dense_lu\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/solvers/dense_lu_solver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <scalers/binormalization.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n#include <solvers/block_common_solver.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\ntemplate<class TConfig> class BinormalizationScaler;\n\n/**********************************************************************\n * HOST FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                          VectorType *x, VectorType *y, VectorType *beta, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n            gamma[col] += (val * val) * x[i];\n        }\n\n        beta[i] = bi;\n    }\n}\n\n// compute Gamma on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                      VectorType *x, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            gamma[col] += (val * val) * x[i];\n        }\n    }\n}\n\n// compute Beta on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaHost(int nrows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                     VectorType *y, VectorType *beta)\n{\n    for (int i = 0; i < nrows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n        }\n\n        beta[i] = bi;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid scaleMatrixHost(int nrows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                     VectorType *x, VectorType *y)\n{\n    for (int i = 0; i < nrows; i++)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n            values[jj] *= fi * gj;\n        }\n    }\n}\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\n\n// compute initial beta, which is B*[1,...,1]'\ntemplate <typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaIniDevice(int nrows, IndexType *offsets, IndexType *indices, MatrixValue *values, VectorValue *beta)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        VectorValue rowsum = 0.0;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            rowsum += values[jj] * values[jj];\n        }\n\n        beta[i] = rowsum;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid grabDiagonalVector(int nrows, IndexType *offsets, IndexType *indices, MatrixType *values, VectorType *diag)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n}\n\n// functor to generate stddev of vectors\ntemplate <typename T>\nstruct std_f\n{\n    std_f(T x) : v(x) {};\n    T v;\n\n    __host__ __device__\n    T operator()(const T &x1, const T &x2) const\n    {\n        return (x1 * x2 - v) * (x1 * x2 - v);\n    }\n};\n\n// scaled the matrix using diag(F)*A*diag(G), f = sqrt(fabs(x)), g = sqrt(fabs(y))\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid scaleMatrixDevice(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                       VectorType *x)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        VectorType fi = fabs(x[i]);\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType fj = fabs(x[j]);\n            values[jj] *= sqrt(fabs(fi * fj));\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid getColRowNorms(int rows, IndexType *offsets, IndexType *indices, ValueTypeA *values,\n                    ValueTypeB *rownorms, ValueTypeB *colnorms)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        ValueTypeB rownorm = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            ValueTypeB curval = values[jj] * values[jj];\n            rownorm += curval;\n            utils::atomic_add(colnorms + j, curval);\n        }\n\n        rownorms[i] = rownorm;\n    }\n}\n\n// these warp reductions should be able to be replaced with amgx:: functions\ntemplate <int warpSize, typename T>\n__device__ __inline__ T warpReduceSum(T val)\n{\n    if (warpSize > 16) { val += utils::shfl_down(val, 16, warpSize); }\n\n    if (warpSize > 8) { val += utils::shfl_down(val, 8, warpSize); }\n\n    if (warpSize > 4) { val += utils::shfl_down(val, 4, warpSize); }\n\n    if (warpSize > 2) { val += utils::shfl_down(val, 2, warpSize); }\n\n    if (warpSize > 1) { val += utils::shfl_down(val, 1, warpSize); }\n\n    return val;\n}\n\ntemplate <int warpSize, typename T>\n__device__ T warpReduceSumShared(volatile T *vals, const int lane_id)\n{\n    if (warpSize > 16) { vals[lane_id] += vals[lane_id + 16]; }\n\n    if (warpSize > 8) { vals[lane_id] += vals[lane_id + 8]; }\n\n    if (warpSize > 4) { vals[lane_id] += vals[lane_id + 4]; }\n\n    if (warpSize > 2) { vals[lane_id] += vals[lane_id + 2]; }\n\n    if (warpSize > 1) { vals[lane_id] += vals[lane_id + 1]; }\n\n    return vals[lane_id];\n}\n\n// compute gamma = B^T*x (B = A.^2)\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid computeBetaDevice(const int nrows, IndexType *offsets, IndexType *indices, ValueTypeA *values,\n                       ValueTypeB *diag, ValueTypeB *x, ValueTypeB *xn, ValueTypeB *beta, const ValueTypeB avg, ValueTypeB *avg_vec)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        ValueTypeB xi = x[i];\n        ValueTypeB bi = beta[i];\n        ValueTypeB di = diag[i];\n        ValueTypeB c0 = -di * xi * xi + 2 * bi * xi - nrows * avg;\n        ValueTypeB c1 = (nrows - 2) * (bi - di * xi);\n        ValueTypeB c2 = (nrows - 1) * di;\n        assert(c0 > epsilon(c0)); //\n        // delta = xi - x(i)\n        ValueTypeB dx = (2 * c0) / (-c1 - sqrt(c1 * c1 - 4 * c2 * c0)) - x[i];\n        ValueTypeB davg = 0.;\n        ValueTypeB dbeta = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            ValueTypeA Bij = values[jj] * values[jj];\n            davg += Bij * x[j]; // += x' * B(:, i) == B(i, :)*x, because B is symmetric\n            dbeta += dx * Bij;\n        }\n\n        beta[i] = bi + dbeta;\n        avg_vec[i] = dx * (davg + bi + di * dx) / nrows;\n        //utils::atomic_add(avg, davg);\n    }\n}\n\n\ntemplate<typename T>\nstruct square_value\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        return x * x;\n    }\n};\n\n// vector constant scale operand\ntemplate <typename T>\nstruct vmul_scale_const\n{\n    T _alpha;\n\n    vmul_scale_const(T alpha): _alpha(alpha) {};\n\n    __host__ __device__\n    T operator()(const T &vec) const\n    {\n        return vec * _alpha;\n    }\n};\n\n// vector scale operand\ntemplate <typename T>\nstruct vmul_scale\n{\n\n    vmul_scale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec * sqrt(fabs(alpha)));\n    }\n};\n\n// vector unscale operand\ntemplate <typename T>\nstruct vmul_unscale\n{\n\n    vmul_unscale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec / sqrt(fabs(alpha)));\n    }\n};\n\n\n// Setup on  Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n    VVector diag(A.get_num_rows());\n    grabDiagonalVector <<< 4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    int nrows = A.get_num_rows();\n    // temporary vectors\n    VVector x(nrows, 1), xn(nrows), davg(nrows), beta(nrows, 0);\n    computeBetaIniDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), beta.raw());\n    cudaCheckError();\n    ValueTypeB avg = thrust_wrapper::reduce<AMGX_device>(beta.begin(), beta.end()) / nrows;\n    // calculate initial std1 and std2\n    amgx::thrust::device_ptr<ValueTypeB> x_ptr(x.raw()), beta_ptr(beta.raw());\n    ValueTypeB stdx = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + nrows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(avg)) / nrows) / avg;\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (fabs(stdx) < tolerance) { break; } // finished\n\n        computeBetaDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                                           diag.raw(), x.raw(), xn.raw(), beta.raw(), avg, davg.raw());\n        cudaCheckError();\n        avg += thrust_wrapper::reduce<AMGX_device>(davg.begin(), davg.end());\n        // ValueTypeB stdx_old = stdx;\n        stdx = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + nrows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(avg)) / nrows) / avg;\n        // print it #, current error, convergence rate\n        // printf(\"ITER: %d     %.3e  %.3e   %.4lg\\n\",t, stdx, stdx_old, stdx / stdx_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    scale_vector = VVector(x);\n}\n\n\n// Matrix Scaling on Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    if (scale_vector.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int nrows = A.get_num_rows();\n    /*VVector rownorms(nrows, 0.0);\n    VVector colnorms(nrows, 0.0);\n    getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n    cudaCheckError();\n    ValueTypeB row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n    ValueTypeB row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n    ValueTypeB col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n    ValueTypeB col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n    cudaCheckError();\n    printf(\"Original Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n    scaleMatrixDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), scale_vector.raw());\n    cudaCheckError();\n    ValueTypeB C_norm = sqrt(thrust_wrapper::transform_reduce<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz() * A.get_block_size(), square_value<ValueTypeB>(), 0., amgx::thrust::plus<ValueTypeB>()) / nrows);\n    thrust_wrapper::transform<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), A.values.begin(), vmul_scale_const<ValueTypeB>(1. / C_norm) );\n    thrust_wrapper::transform<AMGX_device>(scale_vector.begin(), scale_vector.end(), scale_vector.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / C_norm)) );\n    cudaCheckError();\n    /*thrust_wrapper::fill<AMGX_device>(rownorms.begin(), rownorms.end(), 0.);\n      thrust_wrapper::fill<AMGX_device>(colnorms.begin(), colnorms.end(), 0.);\n    getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n    cudaCheckError();\n    row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n    row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n    col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n    col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n    cudaCheckError();\n    printf(\"Scaled Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n    exit(0);\n}\n\n// Setup on Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    FatalError(\"Host not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*if (A.is_matrix_distributed()) {\n      FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                         x.raw(), y.raw(), beta.raw(), gamma.raw());\n\n    double std1 = 0., std2 = 0., sum1 = cols, sum2 = rows;\n    // calculate initial std1 and std2\n    for (int i=0; i<rows; i++) {\n      std1 += pow(x[i]*beta[i] - sum1, 2.0);\n    }\n    std1 = sqrt(std1 / rows) / sum1;\n\n    for (int i=0; i<cols; i++) {\n      std2 += pow(y[i]*gamma[i] - sum2, 2.0);\n    }\n    std2 = sqrt(std2 / cols) / sum2;\n\n    //printf(\"std1: %lg, std2: %lg\\n\",std1, std2);\n    double std_initial = sqrt((std1*std1)+(std2*std2));\n    double std = std_initial;\n\n    for (int t=0; t<max_iters; t++) {\n\n      if (std < tolerance) break; // finished\n\n      // x = sum1 ./ beta\n      for (int i=0; i<rows; i++) x[i] = ( isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 / epsilon(beta[i]) );\n\n      // gamma = C*x\n      computeGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n\n      // gamma = 1 ./ beta\n      for (int i=0; i<cols; i++) y[i] = ( isNotCloseToZero(gamma[i]) ? sum2/gamma[i] : sum2 / epsilon(gamma[i]) );\n\n      // beta = B*y\n      computeBetaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n\n      //ValueTypeB std_old = std;\n      std = 0.;\n      for (int i=0; i<rows; i++) {\n        std += pow(x[i]*beta[i] - sum1, 2.0);\n      }\n      std = sqrt(std / rows) / sum1;\n\n      // print it #, current error, convergence rate\n      //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale= VVector(gamma);\n    */\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    FatalError(\"Host not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*if (A.is_matrix_distributed()) {\n      FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    if (left_scale.size() != A.get_num_rows()) {\n      FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n    // A_ij = f_i * A_ij * g_j\n    scaleMatrixHost(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());*/\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), this->scale_vector.begin(), v.begin(), vmul_scale<ValueTypeB>() );\n    }\n    else\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), this->scale_vector.begin(), v.begin(), vmul_unscale<ValueTypeB>() );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    FatalError(\"4x4 block size not supported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class BinormalizationScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class BinormalizationScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <scalers/binormalization.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n#include <solvers/block_common_solver.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\ntemplate<class TConfig> class BinormalizationScaler;\n\n/**********************************************************************\n * HOST FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                          VectorType *x, VectorType *y, VectorType *beta, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n            gamma[col] += (val * val) * x[i];\n        }\n\n        beta[i] = bi;\n    }\n}\n\n// compute Gamma on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                      VectorType *x, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            gamma[col] += (val * val) * x[i];\n        }\n    }\n}\n\n// compute Beta on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaHost(int nrows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                     VectorType *y, VectorType *beta)\n{\n    for (int i = 0; i < nrows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n        }\n\n        beta[i] = bi;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid scaleMatrixHost(int nrows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                     VectorType *x, VectorType *y)\n{\n    for (int i = 0; i < nrows; i++)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n            values[jj] *= fi * gj;\n        }\n    }\n}\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\n\n// compute initial beta, which is B*[1,...,1]'\ntemplate <typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaIniDevice(int nrows, IndexType *offsets, IndexType *indices, MatrixValue *values, VectorValue *beta)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        VectorValue rowsum = 0.0;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            rowsum += values[jj] * values[jj];\n        }\n\n        beta[i] = rowsum;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid grabDiagonalVector(int nrows, IndexType *offsets, IndexType *indices, MatrixType *values, VectorType *diag)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n}\n\n// functor to generate stddev of vectors\ntemplate <typename T>\nstruct std_f\n{\n    std_f(T x) : v(x) {};\n    T v;\n\n    __host__ __device__\n    T operator()(const T &x1, const T &x2) const\n    {\n        return (x1 * x2 - v) * (x1 * x2 - v);\n    }\n};\n\n// scaled the matrix using diag(F)*A*diag(G), f = sqrt(fabs(x)), g = sqrt(fabs(y))\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid scaleMatrixDevice(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                       VectorType *x)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        VectorType fi = fabs(x[i]);\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType fj = fabs(x[j]);\n            values[jj] *= sqrt(fabs(fi * fj));\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid getColRowNorms(int rows, IndexType *offsets, IndexType *indices, ValueTypeA *values,\n                    ValueTypeB *rownorms, ValueTypeB *colnorms)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        ValueTypeB rownorm = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            ValueTypeB curval = values[jj] * values[jj];\n            rownorm += curval;\n            utils::atomic_add(colnorms + j, curval);\n        }\n\n        rownorms[i] = rownorm;\n    }\n}\n\n// these warp reductions should be able to be replaced with amgx:: functions\ntemplate <int warpSize, typename T>\n__device__ __inline__ T warpReduceSum(T val)\n{\n    if (warpSize > 16) { val += utils::shfl_down(val, 16, warpSize); }\n\n    if (warpSize > 8) { val += utils::shfl_down(val, 8, warpSize); }\n\n    if (warpSize > 4) { val += utils::shfl_down(val, 4, warpSize); }\n\n    if (warpSize > 2) { val += utils::shfl_down(val, 2, warpSize); }\n\n    if (warpSize > 1) { val += utils::shfl_down(val, 1, warpSize); }\n\n    return val;\n}\n\ntemplate <int warpSize, typename T>\n__device__ T warpReduceSumShared(volatile T *vals, const int lane_id)\n{\n    if (warpSize > 16) { vals[lane_id] += vals[lane_id + 16]; }\n\n    if (warpSize > 8) { vals[lane_id] += vals[lane_id + 8]; }\n\n    if (warpSize > 4) { vals[lane_id] += vals[lane_id + 4]; }\n\n    if (warpSize > 2) { vals[lane_id] += vals[lane_id + 2]; }\n\n    if (warpSize > 1) { vals[lane_id] += vals[lane_id + 1]; }\n\n    return vals[lane_id];\n}\n\n// compute gamma = B^T*x (B = A.^2)\ntemplate <typename IndexType, typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid computeBetaDevice(const int nrows, IndexType *offsets, IndexType *indices, ValueTypeA *values,\n                       ValueTypeB *diag, ValueTypeB *x, ValueTypeB *xn, ValueTypeB *beta, const ValueTypeB avg, ValueTypeB *avg_vec)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < nrows; i += gridDim.x * blockDim.x)\n    {\n        ValueTypeB xi = x[i];\n        ValueTypeB bi = beta[i];\n        ValueTypeB di = diag[i];\n        ValueTypeB c0 = -di * xi * xi + 2 * bi * xi - nrows * avg;\n        ValueTypeB c1 = (nrows - 2) * (bi - di * xi);\n        ValueTypeB c2 = (nrows - 1) * di;\n        assert(c0 > epsilon(c0)); //\n        // delta = xi - x(i)\n        ValueTypeB dx = (2 * c0) / (-c1 - sqrt(c1 * c1 - 4 * c2 * c0)) - x[i];\n        ValueTypeB davg = 0.;\n        ValueTypeB dbeta = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            ValueTypeA Bij = values[jj] * values[jj];\n            davg += Bij * x[j]; // += x' * B(:, i) == B(i, :)*x, because B is symmetric\n            dbeta += dx * Bij;\n        }\n\n        beta[i] = bi + dbeta;\n        avg_vec[i] = dx * (davg + bi + di * dx) / nrows;\n        //utils::atomic_add(avg, davg);\n    }\n}\n\n\ntemplate<typename T>\nstruct square_value\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        return x * x;\n    }\n};\n\n// vector constant scale operand\ntemplate <typename T>\nstruct vmul_scale_const\n{\n    T _alpha;\n\n    vmul_scale_const(T alpha): _alpha(alpha) {};\n\n    __host__ __device__\n    T operator()(const T &vec) const\n    {\n        return vec * _alpha;\n    }\n};\n\n// vector scale operand\ntemplate <typename T>\nstruct vmul_scale\n{\n\n    vmul_scale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec * sqrt(fabs(alpha)));\n    }\n};\n\n// vector unscale operand\ntemplate <typename T>\nstruct vmul_unscale\n{\n\n    vmul_unscale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec / sqrt(fabs(alpha)));\n    }\n};\n\n\n// Setup on  Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n    VVector diag(A.get_num_rows());\n    grabDiagonalVector <<< 4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    int nrows = A.get_num_rows();\n    // temporary vectors\n    VVector x(nrows, 1), xn(nrows), davg(nrows), beta(nrows, 0);\n    computeBetaIniDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), beta.raw());\n    cudaCheckError();\n    ValueTypeB avg = thrust_wrapper::reduce<AMGX_device>(beta.begin(), beta.end()) / nrows;\n    // calculate initial std1 and std2\n    amgx::thrust::device_ptr<ValueTypeB> x_ptr(x.raw()), beta_ptr(beta.raw());\n    ValueTypeB stdx = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + nrows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(avg)) / nrows) / avg;\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (fabs(stdx) < tolerance) { break; } // finished\n\n        computeBetaDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                                           diag.raw(), x.raw(), xn.raw(), beta.raw(), avg, davg.raw());\n        cudaCheckError();\n        avg += thrust_wrapper::reduce<AMGX_device>(davg.begin(), davg.end());\n        // ValueTypeB stdx_old = stdx;\n        stdx = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + nrows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(avg)) / nrows) / avg;\n        // print it #, current error, convergence rate\n        // printf(\"ITER: %d     %.3e  %.3e   %.4lg\\n\",t, stdx, stdx_old, stdx / stdx_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    scale_vector = VVector(x);\n}\n\n\n// Matrix Scaling on Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    if (scale_vector.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int nrows = A.get_num_rows();\n    /*VVector rownorms(nrows, 0.0);\n    VVector colnorms(nrows, 0.0);\n    getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n    cudaCheckError();\n    ValueTypeB row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n    ValueTypeB row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n    ValueTypeB col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n    ValueTypeB col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n    cudaCheckError();\n    printf(\"Original Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n    scaleMatrixDevice <<< 4096, 256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), scale_vector.raw());\n    cudaCheckError();\n    ValueTypeB C_norm = sqrt(thrust_wrapper::transform_reduce<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz() * A.get_block_size(), square_value<ValueTypeB>(), 0., amgx::thrust::plus<ValueTypeB>()) / nrows);\n    thrust_wrapper::transform<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), A.values.begin(), vmul_scale_const<ValueTypeB>(1. / C_norm) );\n    thrust_wrapper::transform<AMGX_device>(scale_vector.begin(), scale_vector.end(), scale_vector.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / C_norm)) );\n    cudaCheckError();\n    /*thrust_wrapper::fill<AMGX_device>(rownorms.begin(), rownorms.end(), 0.);\n      thrust_wrapper::fill<AMGX_device>(colnorms.begin(), colnorms.end(), 0.);\n    getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n    cudaCheckError();\n    row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n    row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n    col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n    col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n    cudaCheckError();\n    printf(\"Scaled Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n    exit(0);\n}\n\n// Setup on Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    FatalError(\"Host not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*if (A.is_matrix_distributed()) {\n      FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                         x.raw(), y.raw(), beta.raw(), gamma.raw());\n\n    double std1 = 0., std2 = 0., sum1 = cols, sum2 = rows;\n    // calculate initial std1 and std2\n    for (int i=0; i<rows; i++) {\n      std1 += pow(x[i]*beta[i] - sum1, 2.0);\n    }\n    std1 = sqrt(std1 / rows) / sum1;\n\n    for (int i=0; i<cols; i++) {\n      std2 += pow(y[i]*gamma[i] - sum2, 2.0);\n    }\n    std2 = sqrt(std2 / cols) / sum2;\n\n    //printf(\"std1: %lg, std2: %lg\\n\",std1, std2);\n    double std_initial = sqrt((std1*std1)+(std2*std2));\n    double std = std_initial;\n\n    for (int t=0; t<max_iters; t++) {\n\n      if (std < tolerance) break; // finished\n\n      // x = sum1 ./ beta\n      for (int i=0; i<rows; i++) x[i] = ( isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 / epsilon(beta[i]) );\n\n      // gamma = C*x\n      computeGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n\n      // gamma = 1 ./ beta\n      for (int i=0; i<cols; i++) y[i] = ( isNotCloseToZero(gamma[i]) ? sum2/gamma[i] : sum2 / epsilon(gamma[i]) );\n\n      // beta = B*y\n      computeBetaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n\n      //ValueTypeB std_old = std;\n      std = 0.;\n      for (int i=0; i<rows; i++) {\n        std += pow(x[i]*beta[i] - sum1, 2.0);\n      }\n      std = sqrt(std / rows) / sum1;\n\n      // print it #, current error, convergence rate\n      //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale= VVector(gamma);\n    */\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    FatalError(\"Host not supported\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*if (A.is_matrix_distributed()) {\n      FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    if (left_scale.size() != A.get_num_rows()) {\n      FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n    // A_ij = f_i * A_ij * g_j\n    scaleMatrixHost(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());*/\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), this->scale_vector.begin(), v.begin(), vmul_scale<ValueTypeB>() );\n    }\n    else\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), this->scale_vector.begin(), v.begin(), vmul_unscale<ValueTypeB>() );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid BinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    FatalError(\"4x4 block size not supported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class BinormalizationScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class BinormalizationScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/scalers/binormalization.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <scalers/diagonal_symmetric.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n\nnamespace amgx\n{\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid ScaleMatrix(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                 VectorType *scale, ScaleDirection scaleOrUnscale)\n{\n    if ( scaleOrUnscale == SCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            VectorType si = scale[i];\n\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                VectorType sj = scale[j];\n                // scale in-place\n                values[jj] *= si * sj;\n            }\n        }\n\n    if ( scaleOrUnscale == UNSCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            VectorType si = scale[i];\n\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                VectorType sj = scale[j];\n                // scale in-place\n                values[jj] /= si * sj;\n            }\n        }\n}\n\ntemplate <typename VectorType>\n__global__\nvoid scaleVectorKernel(int rows, VectorType *values, VectorType *diag, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    //Ignore the scaleside, it is the same for both in the diagonal symmetric case\n    if (scaleOrUnscale == SCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            values[i] *= diag[i];\n        }\n\n    if (scaleOrUnscale == UNSCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            values[i] /= diag[i];\n        }\n}\n\ntemplate <typename T>\n__global__\nvoid checkPositiveVector(int N, T *v, bool *positive)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        if (v[i] < T(0)) { positive[0] = false; }\n    }\n}\n\ntemplate <typename T>\n__global__\nvoid sqrtVector(int N, T *v)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        // we know by this point that the diagonal is +ve\n        v[i] = 1. / sqrt(v[i]);\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid grabDiagonalVector(int rows, IndexType *offsets, IndexType *indices, MatrixType *values, VectorType *diag)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n}\n\n// Setup scaling vector\n// Device version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Diagonal Symmetric scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // grab diagonal\n    diag = VVector(A.get_num_rows());\n    grabDiagonalVector <<< 4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    // check diagonal +ve\n    bool positive = true, *d_positive;\n    amgx::memory::cudaMallocAsync((void**)&d_positive, sizeof(bool));\n    cudaCheckError();\n    cudaMemcpy(d_positive, &positive, sizeof(bool), cudaMemcpyHostToDevice);\n    cudaCheckError();\n    checkPositiveVector <<< 4096, 256>>>(diag.size(), diag.raw(), d_positive);\n    cudaCheckError();\n    cudaMemcpy(&positive, d_positive, sizeof(bool), cudaMemcpyDeviceToHost);\n    cudaCheckError();\n\n    // fail out if necessary\n    if (!positive)\n    {\n        FatalError(\"Diagonal symmetric scaling only applies to SPD systems with positive diagonal entries\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // set scale = 1./sqrt(diag) , reciprocal square root\n    sqrtVector <<< 4096, 256>>>(diag.size(), diag.raw());\n    cudaCheckError();\n}\n\n//Host version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Diagonal Symmetric scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // grab diagonal\n    diag = VVector(A.get_num_rows());\n    //grabDiagonalVector<<<4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    IndexType *offsets =  A.row_offsets.raw();\n    IndexType *indices = A.col_indices.raw();\n    ValueTypeA *values = A.values.raw();\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n\n    // check diagonal +ve\n    bool positive = true;\n\n    //checkPositiveVector<<<4096, 256>>>(diag.size(), diag.raw(), d_positive);\n    cudaCheckError();\n    for (int i = 0; i < diag.size(); i++)\n    {\n        if (diag[i] < ValueTypeB(0)) { positive = false; }\n    }\n\n    // fail out if necessary\n    if (!positive)\n    {\n        FatalError(\"Diagonal symmetric scaling only applies to SPD systems with positive diagonal entries\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // set scale = 1./sqrt(diag) , reciprocal square root\n    // sqrtVector<<<4096, 256>>>(diag.size(), diag.raw());\n    cudaCheckError();\n    for (int i = 0; i < diag.size(); i++)\n    {\n        // we know by this point that the diagonal is +ve\n        diag[i] = 1. / sqrt(diag[i]);\n    }\n}\n\n// 1x1 Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    // A_ij *= scale[i] * scale[j];\n    ScaleMatrix <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(),\n                                 A.values.raw(), diag.raw(), scaleOrUnscale);\n    cudaCheckError();\n}\n\n// 1x1 Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    IndexType *offsets =  A.row_offsets.raw();\n    IndexType *indices = A.col_indices.raw();\n    ValueTypeA *values = A.values.raw();\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if ( scaleOrUnscale == SCALE)\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                values[jj] *= diag[i] * diag[j];\n            }\n\n        if ( scaleOrUnscale == UNSCALE)\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                values[jj] /= diag[i] * diag[j];\n            }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    scaleVectorKernel <<< 4096, 256>>>(v.size(), v.raw(), diag.raw(), scaleOrUnscale, leftOrRight);\n    cudaCheckError();\n}\n\n// 4x4 Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    //ignore leftOrRIght for diagonal scale, they are the same\n    if ( scaleOrUnscale == SCALE)\n        for (int i = 0; i < diag.size(); i++)\n        {\n            v[i] *= diag[i];\n        }\n\n    if ( scaleOrUnscale == UNSCALE)\n        for (int i = 0; i < diag.size(); i++)\n        {\n            v[i] /= diag[i];\n        }\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DiagonalSymmetricScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DiagonalSymmetricScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <scalers/diagonal_symmetric.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n\nnamespace amgx\n{\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid ScaleMatrix(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                 VectorType *scale, ScaleDirection scaleOrUnscale)\n{\n    if ( scaleOrUnscale == SCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            VectorType si = scale[i];\n\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                VectorType sj = scale[j];\n                // scale in-place\n                values[jj] *= si * sj;\n            }\n        }\n\n    if ( scaleOrUnscale == UNSCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            VectorType si = scale[i];\n\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                VectorType sj = scale[j];\n                // scale in-place\n                values[jj] /= si * sj;\n            }\n        }\n}\n\ntemplate <typename VectorType>\n__global__\nvoid scaleVectorKernel(int rows, VectorType *values, VectorType *diag, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    //Ignore the scaleside, it is the same for both in the diagonal symmetric case\n    if (scaleOrUnscale == SCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            values[i] *= diag[i];\n        }\n\n    if (scaleOrUnscale == UNSCALE)\n        for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n        {\n            values[i] /= diag[i];\n        }\n}\n\ntemplate <typename T>\n__global__\nvoid checkPositiveVector(int N, T *v, bool *positive)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        if (v[i] < T(0)) { positive[0] = false; }\n    }\n}\n\ntemplate <typename T>\n__global__\nvoid sqrtVector(int N, T *v)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        // we know by this point that the diagonal is +ve\n        v[i] = 1. / sqrt(v[i]);\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid grabDiagonalVector(int rows, IndexType *offsets, IndexType *indices, MatrixType *values, VectorType *diag)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n}\n\n// Setup scaling vector\n// Device version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Diagonal Symmetric scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // grab diagonal\n    diag = VVector(A.get_num_rows());\n    grabDiagonalVector <<< 4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    // check diagonal +ve\n    bool positive = true, *d_positive;\n    amgx::memory::hipMallocAsync((void**)&d_positive, sizeof(bool));\n    cudaCheckError();\n    hipMemcpy(d_positive, &positive, sizeof(bool), hipMemcpyHostToDevice);\n    cudaCheckError();\n    checkPositiveVector <<< 4096, 256>>>(diag.size(), diag.raw(), d_positive);\n    cudaCheckError();\n    hipMemcpy(&positive, d_positive, sizeof(bool), hipMemcpyDeviceToHost);\n    cudaCheckError();\n\n    // fail out if necessary\n    if (!positive)\n    {\n        FatalError(\"Diagonal symmetric scaling only applies to SPD systems with positive diagonal entries\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // set scale = 1./sqrt(diag) , reciprocal square root\n    sqrtVector <<< 4096, 256>>>(diag.size(), diag.raw());\n    cudaCheckError();\n}\n\n//Host version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Diagonal Symmetric scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // grab diagonal\n    diag = VVector(A.get_num_rows());\n    //grabDiagonalVector<<<4096, 128>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), diag.raw());\n    cudaCheckError();\n    IndexType *offsets =  A.row_offsets.raw();\n    IndexType *indices = A.col_indices.raw();\n    ValueTypeA *values = A.values.raw();\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            IndexType j = indices[jj];\n\n            if (i == j) { diag[i] = values[jj]; }\n        }\n    }\n\n    // check diagonal +ve\n    bool positive = true;\n\n    //checkPositiveVector<<<4096, 256>>>(diag.size(), diag.raw(), d_positive);\n    cudaCheckError();\n    for (int i = 0; i < diag.size(); i++)\n    {\n        if (diag[i] < ValueTypeB(0)) { positive = false; }\n    }\n\n    // fail out if necessary\n    if (!positive)\n    {\n        FatalError(\"Diagonal symmetric scaling only applies to SPD systems with positive diagonal entries\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // set scale = 1./sqrt(diag) , reciprocal square root\n    // sqrtVector<<<4096, 256>>>(diag.size(), diag.raw());\n    cudaCheckError();\n    for (int i = 0; i < diag.size(); i++)\n    {\n        // we know by this point that the diagonal is +ve\n        diag[i] = 1. / sqrt(diag[i]);\n    }\n}\n\n// 1x1 Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    // A_ij *= scale[i] * scale[j];\n    ScaleMatrix <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(),\n                                 A.values.raw(), diag.raw(), scaleOrUnscale);\n    cudaCheckError();\n}\n\n// 1x1 Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    IndexType *offsets =  A.row_offsets.raw();\n    IndexType *indices = A.col_indices.raw();\n    ValueTypeA *values = A.values.raw();\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if ( scaleOrUnscale == SCALE)\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                values[jj] *= diag[i] * diag[j];\n            }\n\n        if ( scaleOrUnscale == UNSCALE)\n            for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n            {\n                IndexType j = indices[jj];\n                values[jj] /= diag[i] * diag[j];\n            }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    scaleVectorKernel <<< 4096, 256>>>(v.size(), v.raw(), diag.raw(), scaleOrUnscale, leftOrRight);\n    cudaCheckError();\n}\n\n// 4x4 Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid DiagonalSymmetricScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    //ignore leftOrRIght for diagonal scale, they are the same\n    if ( scaleOrUnscale == SCALE)\n        for (int i = 0; i < diag.size(); i++)\n        {\n            v[i] *= diag[i];\n        }\n\n    if ( scaleOrUnscale == UNSCALE)\n        for (int i = 0; i < diag.size(); i++)\n        {\n            v[i] /= diag[i];\n        }\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class DiagonalSymmetricScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class DiagonalSymmetricScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/scalers/diagonal_symmetric.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <scalers/nbinormalization.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n#include <solvers/block_common_solver.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\ntemplate<class TConfig> class NBinormalizationScaler;\n\n/**********************************************************************\n * HOST FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                          VectorType *x, VectorType *y, VectorType *beta, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n            gamma[col] += (val * val) * x[i];\n        }\n\n        beta[i] = bi;\n    }\n}\n\n// compute Gamma on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                      VectorType *x, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            gamma[col] += (val * val) * x[i];\n        }\n    }\n}\n\n// compute Beta on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                     VectorType *y, VectorType *beta)\n{\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n        }\n\n        beta[i] = bi;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid scaleMatrixHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                     VectorType *x, VectorType *y)\n{\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n            values[jj] *= fi * gj;\n        }\n    }\n}\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\n\n// these warp reductions should be able to be replaced with amgx:: functions\ntemplate <int warpSize, typename T>\n__device__ __inline__ T warpReduceSum(T val)\n{\n    if (warpSize > 16) { val += utils::shfl_down(val, 16, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 8) { val += utils::shfl_down(val, 8, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 4) { val += utils::shfl_down(val, 4, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 2) { val += utils::shfl_down(val, 2, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 1) { val += utils::shfl_down(val, 1, warpSize); }\n\n    return val;\n}\n\ntemplate <int warpSize, typename T>\n__device__ T warpReduceSumShared(volatile T *vals, const int lane_id)\n{\n    if (warpSize > 16) { vals[lane_id] += vals[lane_id + 16]; }\n\n    if (warpSize > 8) { vals[lane_id] += vals[lane_id + 8]; }\n\n    if (warpSize > 4) { vals[lane_id] += vals[lane_id + 4]; }\n\n    if (warpSize > 2) { vals[lane_id] += vals[lane_id + 2]; }\n\n    if (warpSize > 1) { vals[lane_id] += vals[lane_id + 1]; }\n\n    return vals[lane_id];\n}\n\n// compute beta = B*y, gamma = C*x (B = A.^2, C = B^T)\ntemplate <int CTASize, int VectorSize, int VectorsPerCTA, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaGammaDevice(IndexType rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                            VectorValue *x, VectorValue *y, VectorValue *beta, VectorValue *gamma)\n{\n    const int vectors_per_block = VectorsPerCTA;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n        VectorValue bi(0.);\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val(0.);\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                bi += (val * val) * y[col];\n                utils::atomic_add(&gamma[col], (val * val) * x[i]);\n            }\n        }\n\n        // reduce over bi\n        VectorValue bi_s = warpReduceSum<VectorSize>(bi);\n\n        if (lane_id == 0)\n        {\n            beta[i] = bi_s;\n        }\n    }\n}\n\n// compute gamma = B^T*x (B = A.^2)\ntemplate <int CTASize, int VectorSize, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeGammaDevice(int rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                        VectorValue *x, VectorValue *gamma)\n{\n    const int vectors_per_block = CTASize / VectorSize;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val = 0.;\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                utils::atomic_add(&gamma[col], (val * val) * x[i]);\n            }\n        }\n    }\n}\n\n// compute beta = B*y (B = A.^2)\ntemplate <int CTASize, int VectorSize, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaDevice(int rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                       VectorValue *y, VectorValue *beta)\n{\n    const int vectors_per_block = CTASize / VectorSize;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n        VectorValue bi = 0.;\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val = 0.;\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                bi += (val * val) * y[col];\n            }\n        }\n\n        // reduce over bi\n        VectorValue bi_s = warpReduceSum<VectorSize>(bi);\n\n        if (lane_id == 0)\n        {\n            beta[i] = bi_s;\n        }\n    }\n}\n\ntemplate <typename ValueType>\n__global__\nvoid setOneOverVector(int N, ValueType *x, ValueType sum1, ValueType *beta)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        //x[i] = (  isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 /  epsilon(beta[i]) );\n        x[i] = (  isNotCloseToZero(beta[i]) ? (sum1 / beta[i]) : (ValueType)1. );\n    }\n}\n\ntemplate<typename T>\nstruct square_value\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        return x * x;\n    }\n};\n\n// functor to generate stddev of vectors\ntemplate <typename T>\nstruct std_f\n{\n    std_f(T x) : v(x) {};\n    T v;\n\n    __host__ __device__\n    T operator()(const T &x1, const T &x2) const\n    {\n        return (x1 * x2 - v) * (x1 * x2 - v);\n    }\n};\n\n// scaled the matrix using diag(F)*A*diag(G), f = sqrt(fabs(x)), g = sqrt(fabs(y))\ntemplate <amgx::ScaleDirection direction, typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid scaleMatrixDevice(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                       VectorType *x, VectorType *y)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n\n            // scale matrix value in place\n            if (direction == amgx::SCALE)\n            {\n                values[jj] *= fi * gj;\n            }\n            else\n            {\n                values[jj] /= fi * gj;\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid getColRowNorms(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                    VectorType *rownorms, VectorType *colnorms)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType curval = values[jj] * values[jj];\n            rownorms[i] += curval;\n            utils::atomic_add(colnorms + j, curval);\n        }\n    }\n}\n\n// vector constant scale operand\ntemplate <typename T>\nstruct vmul_scale_const\n{\n    T _alpha;\n\n    vmul_scale_const(T alpha): _alpha(alpha) {};\n\n    __host__ __device__\n    T operator()(const T &vec) const\n    {\n        return vec * _alpha;\n    }\n};\n\n\n// vector scale operand\ntemplate <typename T>\nstruct vmul_scale\n{\n\n    vmul_scale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec * sqrt(fabs(alpha)));\n    }\n};\n\n// vector unscale operand\ntemplate <typename T>\nstruct vmul_unscale\n{\n\n    vmul_unscale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec / sqrt(fabs(alpha)));\n    }\n};\n\n\n// Setup on  Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 50;\n    const ValueTypeB tolerance = 1e-10;\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaDevice<256, 8, 32> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n            x.raw(), y.raw(), beta.raw(), gamma.raw());\n    cudaCheckError();\n    ValueTypeB sum1 = cols, sum2 = rows, std1, std2;\n    // calculate initial std1 and std2\n    amgx::thrust::device_ptr<ValueTypeB> x_ptr(x.raw()), y_ptr(y.raw()), beta_ptr(beta.raw()), gamma_ptr(gamma.raw());\n    std1 = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + rows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum1)) / rows) / sum1;\n    std2 = sqrt(amgx::thrust::inner_product(y_ptr, y_ptr + cols, gamma_ptr, ValueTypeB(0.), thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum2)) / cols) / sum2;\n    ValueTypeB std = sqrt(std1 * std1 + std2 * std2);\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (std < tolerance) { break; } // finished\n\n        // x = sum1 ./ beta\n        setOneOverVector <<< 4096, 256>>>(rows, x.raw(), sum1, beta.raw());\n        cudaCheckError();\n        // gamma = C*x := B'*x\n        thrust_wrapper::fill<AMGX_device>(gamma.begin(), gamma.end(), ValueTypeB(0.));\n        computeGammaDevice<256, 8> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n        cudaCheckError();\n        // gamma = 1 ./ beta\n        setOneOverVector <<< 4096, 256>>>(cols, y.raw(), sum2, gamma.raw());\n        cudaCheckError();\n        // beta = B*y\n        computeBetaDevice<256, 8> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n        cudaCheckError();\n        //ValueTypeB std_old = std;\n        std = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + rows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum1)) / rows) / sum1;\n        // print it #, current error, convergence rate\n        //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale = VVector(gamma);\n    this->scaled_before = false;\n}\n\n\n// Matrix Scaling on Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    if (left_scale.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int nrows = A.get_num_rows();\n\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        /*VVector rownorms(nrows, 0.0);\n        VVector colnorms(nrows, 0.0);\n        getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n        cudaCheckError();\n        ValueTypeB row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n        ValueTypeB row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n        ValueTypeB col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n        ValueTypeB col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n        cudaCheckError();\n        printf(\"Original Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n        // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n        // A_ij = f_i * A_ij * g_j\n        scaleMatrixDevice<amgx::SCALE> <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n        cudaCheckError();\n\n        if (!scaled_before)\n        {\n            this->norm_coef = sqrt(thrust_wrapper::transform_reduce<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz() * A.get_block_size(), square_value<ValueTypeB>(), 0., amgx::thrust::plus<ValueTypeB>()) / A.get_num_rows());\n            cudaCheckError();\n            thrust_wrapper::transform<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), A.values.begin(), vmul_scale_const<ValueTypeB>(1. / this->norm_coef) );\n            thrust_wrapper::transform<AMGX_device>(left_scale.begin(), left_scale.end(), left_scale.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / this->norm_coef)) );\n            thrust_wrapper::transform<AMGX_device>(right_scale.begin(), right_scale.end(), right_scale.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / this->norm_coef)) );\n            cudaCheckError();\n            /*thrust_wrapper::fill<AMGX_device>(rownorms.begin(), rownorms.end(), 0.);\n              thrust_wrapper::fill<AMGX_device>(colnorms.begin(), colnorms.end(), 0.);\n            getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n            cudaCheckError();\n            row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n            row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n            col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n            col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n            cudaCheckError();\n            printf(\"Scaled Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n        }\n\n        this->scaled_before = true;\n    }\n    else\n    {\n        scaleMatrixDevice<amgx::UNSCALE> <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n        cudaCheckError();\n    }\n}\n\n// Setup on Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                         x.raw(), y.raw(), beta.raw(), gamma.raw());\n    double std1 = 0., std2 = 0., sum1 = cols, sum2 = rows;\n\n    // calculate initial std1 and std2\n    for (int i = 0; i < rows; i++)\n    {\n        std1 += pow(x[i] * beta[i] - sum1, 2.0);\n    }\n\n    std1 = sqrt(std1 / rows) / sum1;\n\n    for (int i = 0; i < cols; i++)\n    {\n        std2 += pow(y[i] * gamma[i] - sum2, 2.0);\n    }\n\n    std2 = sqrt(std2 / cols) / sum2;\n    //printf(\"std1: %lg, std2: %lg\\n\",std1, std2);\n    double std_initial = sqrt((std1 * std1) + (std2 * std2));\n    double std = std_initial;\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (std < tolerance) { break; } // finished\n\n        // x = sum1 ./ beta\n        for (int i = 0; i < rows; i++) { x[i] = ( isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 / epsilon(beta[i]) ); }\n\n        // gamma = C*x\n        computeGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n\n        // gamma = 1 ./ beta\n        for (int i = 0; i < cols; i++) { y[i] = ( isNotCloseToZero(gamma[i]) ? sum2 / gamma[i] : sum2 / epsilon(gamma[i]) ); }\n\n        // beta = B*y\n        computeBetaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n        //ValueTypeB std_old = std;\n        std = 0.;\n\n        for (int i = 0; i < rows; i++)\n        {\n            std += pow(x[i] * beta[i] - sum1, 2.0);\n        }\n\n        std = sqrt(std / rows) / sum1;\n        // print it #, current error, convergence rate\n        //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale = VVector(gamma);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (left_scale.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n    // A_ij = f_i * A_ij * g_j\n    scaleMatrixHost(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    VVector *scale_vector = (leftOrRight == amgx::LEFT) ? &this->left_scale : &this->right_scale;\n\n    //thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), (scaleOrUnscale == amgx::SCALE) ? vmul_scale<ValueTypeB>() : vmul_unscale<ValueTypeB>() );\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), vmul_scale<ValueTypeB>() );\n    }\n    else\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), vmul_unscale<ValueTypeB>() );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    FatalError(\"4x4 block size not supported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class NBinormalizationScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class NBinormalizationScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <scalers/nbinormalization.h>\n#include <sm_utils.inl>\n#include <thrust/inner_product.h>\n#include <solvers/block_common_solver.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\ntemplate<class TConfig> class NBinormalizationScaler;\n\n/**********************************************************************\n * HOST FUNCTIONS\n *********************************************************************/\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                          VectorType *x, VectorType *y, VectorType *beta, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n            gamma[col] += (val * val) * x[i];\n        }\n\n        beta[i] = bi;\n    }\n}\n\n// compute Gamma on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeGammaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                      VectorType *x, VectorType *gamma)\n{\n    for (int i = 0; i < rows; i++) { gamma[i] = 0.; }\n\n    for (int i = 0; i < rows; i++)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            gamma[col] += (val * val) * x[i];\n        }\n    }\n}\n\n// compute Beta on its own\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid computeBetaHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *vals,\n                     VectorType *y, VectorType *beta)\n{\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType bi = 0.;\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int col = indices[jj];\n            VectorType val = vals[jj];\n            bi += (val * val) * y[col];\n        }\n\n        beta[i] = bi;\n    }\n}\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\nvoid scaleMatrixHost(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                     VectorType *x, VectorType *y)\n{\n    for (int i = 0; i < rows; i++)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n            values[jj] *= fi * gj;\n        }\n    }\n}\n\n/**********************************************************************\n * DEVICE FUNCTIONS\n *********************************************************************/\n\n// these warp reductions should be able to be replaced with amgx:: functions\ntemplate <int warpSize, typename T>\n__device__ __inline__ T warpReduceSum(T val)\n{\n    if (warpSize > 16) { val += utils::shfl_down(val, 16, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 8) { val += utils::shfl_down(val, 8, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 4) { val += utils::shfl_down(val, 4, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 2) { val += utils::shfl_down(val, 2, warpSize); }\n\n    utils::syncwarp();\n\n    if (warpSize > 1) { val += utils::shfl_down(val, 1, warpSize); }\n\n    return val;\n}\n\ntemplate <int warpSize, typename T>\n__device__ T warpReduceSumShared(volatile T *vals, const int lane_id)\n{\n    if (warpSize > 16) { vals[lane_id] += vals[lane_id + 16]; }\n\n    if (warpSize > 8) { vals[lane_id] += vals[lane_id + 8]; }\n\n    if (warpSize > 4) { vals[lane_id] += vals[lane_id + 4]; }\n\n    if (warpSize > 2) { vals[lane_id] += vals[lane_id + 2]; }\n\n    if (warpSize > 1) { vals[lane_id] += vals[lane_id + 1]; }\n\n    return vals[lane_id];\n}\n\n// compute beta = B*y, gamma = C*x (B = A.^2, C = B^T)\ntemplate <int CTASize, int VectorSize, int VectorsPerCTA, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaGammaDevice(IndexType rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                            VectorValue *x, VectorValue *y, VectorValue *beta, VectorValue *gamma)\n{\n    const int vectors_per_block = VectorsPerCTA;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n        VectorValue bi(0.);\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val(0.);\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                bi += (val * val) * y[col];\n                utils::atomic_add(&gamma[col], (val * val) * x[i]);\n            }\n        }\n\n        // reduce over bi\n        VectorValue bi_s = warpReduceSum<VectorSize>(bi);\n\n        if (lane_id == 0)\n        {\n            beta[i] = bi_s;\n        }\n    }\n}\n\n// compute gamma = B^T*x (B = A.^2)\ntemplate <int CTASize, int VectorSize, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeGammaDevice(int rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                        VectorValue *x, VectorValue *gamma)\n{\n    const int vectors_per_block = CTASize / VectorSize;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val = 0.;\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                utils::atomic_add(&gamma[col], (val * val) * x[i]);\n            }\n        }\n    }\n}\n\n// compute beta = B*y (B = A.^2)\ntemplate <int CTASize, int VectorSize, typename IndexType, typename MatrixValue, typename VectorValue>\n__global__\nvoid computeBetaDevice(int rows, IndexType *offsets, IndexType *indices, MatrixValue *values,\n                       VectorValue *y, VectorValue *beta)\n{\n    const int vectors_per_block = CTASize / VectorSize;\n    const int vector_id = threadIdx.x / VectorSize;\n    const int lane_id = threadIdx.x % VectorSize;\n\n    for (int i = vectors_per_block * blockIdx.x + vector_id; i < rows; i += vectors_per_block * gridDim.x)\n    {\n        // load start + end pointers\n        int row_tmp;\n\n        if (lane_id < 2)\n        {\n            row_tmp = offsets[i + lane_id];\n        }\n\n        // distribute to all other threads in warp\n        int row_begin = utils::shfl(row_tmp, vector_id * VectorSize, warpSize, utils::activemask());\n        int row_end = utils::shfl(row_tmp, vector_id * VectorSize + 1, warpSize, utils::activemask());\n        VectorValue bi = 0.;\n\n        for (int jj = row_begin + lane_id; utils::any(jj < row_end, utils::activemask()); jj += VectorSize)\n        {\n            int col = -1;\n            VectorValue val = 0.;\n\n            if (jj < row_end)\n            {\n                col = indices[jj];\n                val = values[jj];\n                bi += (val * val) * y[col];\n            }\n        }\n\n        // reduce over bi\n        VectorValue bi_s = warpReduceSum<VectorSize>(bi);\n\n        if (lane_id == 0)\n        {\n            beta[i] = bi_s;\n        }\n    }\n}\n\ntemplate <typename ValueType>\n__global__\nvoid setOneOverVector(int N, ValueType *x, ValueType sum1, ValueType *beta)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x * blockDim.x)\n    {\n        //x[i] = (  isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 /  epsilon(beta[i]) );\n        x[i] = (  isNotCloseToZero(beta[i]) ? (sum1 / beta[i]) : (ValueType)1. );\n    }\n}\n\ntemplate<typename T>\nstruct square_value\n{\n    __host__ __device__ T operator()(const T &x) const\n    {\n        return x * x;\n    }\n};\n\n// functor to generate stddev of vectors\ntemplate <typename T>\nstruct std_f\n{\n    std_f(T x) : v(x) {};\n    T v;\n\n    __host__ __device__\n    T operator()(const T &x1, const T &x2) const\n    {\n        return (x1 * x2 - v) * (x1 * x2 - v);\n    }\n};\n\n// scaled the matrix using diag(F)*A*diag(G), f = sqrt(fabs(x)), g = sqrt(fabs(y))\ntemplate <amgx::ScaleDirection direction, typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid scaleMatrixDevice(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                       VectorType *x, VectorType *y)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        VectorType fi = sqrt(fabs(x[i]));\n\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType gj = sqrt(fabs(y[j]));\n\n            // scale matrix value in place\n            if (direction == amgx::SCALE)\n            {\n                values[jj] *= fi * gj;\n            }\n            else\n            {\n                values[jj] /= fi * gj;\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType, typename MatrixType, typename VectorType>\n__global__\nvoid getColRowNorms(int rows, IndexType *offsets, IndexType *indices, MatrixType *values,\n                    VectorType *rownorms, VectorType *colnorms)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < rows; i += gridDim.x * blockDim.x)\n    {\n        for (int jj = offsets[i]; jj < offsets[i + 1]; jj++)\n        {\n            int j = indices[jj];\n            VectorType curval = values[jj] * values[jj];\n            rownorms[i] += curval;\n            utils::atomic_add(colnorms + j, curval);\n        }\n    }\n}\n\n// vector constant scale operand\ntemplate <typename T>\nstruct vmul_scale_const\n{\n    T _alpha;\n\n    vmul_scale_const(T alpha): _alpha(alpha) {};\n\n    __host__ __device__\n    T operator()(const T &vec) const\n    {\n        return vec * _alpha;\n    }\n};\n\n\n// vector scale operand\ntemplate <typename T>\nstruct vmul_scale\n{\n\n    vmul_scale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec * sqrt(fabs(alpha)));\n    }\n};\n\n// vector unscale operand\ntemplate <typename T>\nstruct vmul_unscale\n{\n\n    vmul_unscale() {};\n\n    __host__ __device__\n    T operator()(const T &vec, const T &alpha) const\n    {\n        return (vec / sqrt(fabs(alpha)));\n    }\n};\n\n\n// Setup on  Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_d &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 50;\n    const ValueTypeB tolerance = 1e-10;\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaDevice<256, 8, 32> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n            x.raw(), y.raw(), beta.raw(), gamma.raw());\n    cudaCheckError();\n    ValueTypeB sum1 = cols, sum2 = rows, std1, std2;\n    // calculate initial std1 and std2\n    amgx::thrust::device_ptr<ValueTypeB> x_ptr(x.raw()), y_ptr(y.raw()), beta_ptr(beta.raw()), gamma_ptr(gamma.raw());\n    std1 = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + rows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum1)) / rows) / sum1;\n    std2 = sqrt(amgx::thrust::inner_product(y_ptr, y_ptr + cols, gamma_ptr, ValueTypeB(0.), thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum2)) / cols) / sum2;\n    ValueTypeB std = sqrt(std1 * std1 + std2 * std2);\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (std < tolerance) { break; } // finished\n\n        // x = sum1 ./ beta\n        setOneOverVector <<< 4096, 256>>>(rows, x.raw(), sum1, beta.raw());\n        cudaCheckError();\n        // gamma = C*x := B'*x\n        thrust_wrapper::fill<AMGX_device>(gamma.begin(), gamma.end(), ValueTypeB(0.));\n        computeGammaDevice<256, 8> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n        cudaCheckError();\n        // gamma = 1 ./ beta\n        setOneOverVector <<< 4096, 256>>>(cols, y.raw(), sum2, gamma.raw());\n        cudaCheckError();\n        // beta = B*y\n        computeBetaDevice<256, 8> <<< 4096, 256>>>(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n        cudaCheckError();\n        //ValueTypeB std_old = std;\n        std = sqrt(amgx::thrust::inner_product(x_ptr, x_ptr + rows, beta_ptr, ValueTypeB(0.), amgx::thrust::plus<ValueTypeB>(), std_f<ValueTypeB>(sum1)) / rows) / sum1;\n        // print it #, current error, convergence rate\n        //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale = VVector(gamma);\n    this->scaled_before = false;\n}\n\n\n// Matrix Scaling on Device\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_d &A, ScaleDirection scaleOrUnscale)\n{\n    if (left_scale.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    int nrows = A.get_num_rows();\n\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        /*VVector rownorms(nrows, 0.0);\n        VVector colnorms(nrows, 0.0);\n        getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n        cudaCheckError();\n        ValueTypeB row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n        ValueTypeB row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n        ValueTypeB col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n        ValueTypeB col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n        cudaCheckError();\n        printf(\"Original Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n        // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n        // A_ij = f_i * A_ij * g_j\n        scaleMatrixDevice<amgx::SCALE> <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n        cudaCheckError();\n\n        if (!scaled_before)\n        {\n            this->norm_coef = sqrt(thrust_wrapper::transform_reduce<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz() * A.get_block_size(), square_value<ValueTypeB>(), 0., amgx::thrust::plus<ValueTypeB>()) / A.get_num_rows());\n            cudaCheckError();\n            thrust_wrapper::transform<AMGX_device>(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), A.values.begin(), vmul_scale_const<ValueTypeB>(1. / this->norm_coef) );\n            thrust_wrapper::transform<AMGX_device>(left_scale.begin(), left_scale.end(), left_scale.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / this->norm_coef)) );\n            thrust_wrapper::transform<AMGX_device>(right_scale.begin(), right_scale.end(), right_scale.begin(), vmul_scale_const<ValueTypeB>(sqrt(1. / this->norm_coef)) );\n            cudaCheckError();\n            /*thrust_wrapper::fill<AMGX_device>(rownorms.begin(), rownorms.end(), 0.);\n              thrust_wrapper::fill<AMGX_device>(colnorms.begin(), colnorms.end(), 0.);\n            getColRowNorms<<<4096,256>>>(nrows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), rownorms.raw(), colnorms.raw());\n            cudaCheckError();\n            row_max = *(amgx::thrust::max_element(rownorms.begin(), rownorms.end()));\n            row_min = *(amgx::thrust::min_element(rownorms.begin(), rownorms.end()));\n            col_max = *(amgx::thrust::max_element(colnorms.begin(), colnorms.end()));\n            col_min = *(amgx::thrust::min_element(colnorms.begin(), colnorms.end()));\n            cudaCheckError();\n            printf(\"Scaled Matrix: rowmax: %e, rowmin: %e, colmax: %e, colmin: %e\\n\", row_max, row_min, col_max, col_min);fflush(stdout);*/\n        }\n\n        this->scaled_before = true;\n    }\n    else\n    {\n        scaleMatrixDevice<amgx::UNSCALE> <<< 4096, 256>>>(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n        cudaCheckError();\n    }\n}\n\n// Setup on Host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setup(Matrix_h &A)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // move these out to config parameters?\n    const int max_iters = 10;\n    const ValueTypeB tolerance = 1e-10;\n    int rows = A.get_num_rows(), cols = A.get_num_cols();\n    // temporary vectors\n    VVector x(rows, 1), y(cols, 1), beta(rows, 0), gamma(cols, 0);\n    // perform matvecs to get beta and gamma (spmv for beta, spmvT for gamma)\n    computeBetaGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(),\n                         x.raw(), y.raw(), beta.raw(), gamma.raw());\n    double std1 = 0., std2 = 0., sum1 = cols, sum2 = rows;\n\n    // calculate initial std1 and std2\n    for (int i = 0; i < rows; i++)\n    {\n        std1 += pow(x[i] * beta[i] - sum1, 2.0);\n    }\n\n    std1 = sqrt(std1 / rows) / sum1;\n\n    for (int i = 0; i < cols; i++)\n    {\n        std2 += pow(y[i] * gamma[i] - sum2, 2.0);\n    }\n\n    std2 = sqrt(std2 / cols) / sum2;\n    //printf(\"std1: %lg, std2: %lg\\n\",std1, std2);\n    double std_initial = sqrt((std1 * std1) + (std2 * std2));\n    double std = std_initial;\n\n    for (int t = 0; t < max_iters; t++)\n    {\n        if (std < tolerance) { break; } // finished\n\n        // x = sum1 ./ beta\n        for (int i = 0; i < rows; i++) { x[i] = ( isNotCloseToZero(beta[i]) ? sum1 / beta[i] : sum1 / epsilon(beta[i]) ); }\n\n        // gamma = C*x\n        computeGammaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), x.raw(), gamma.raw());\n\n        // gamma = 1 ./ beta\n        for (int i = 0; i < cols; i++) { y[i] = ( isNotCloseToZero(gamma[i]) ? sum2 / gamma[i] : sum2 / epsilon(gamma[i]) ); }\n\n        // beta = B*y\n        computeBetaHost(rows, A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), y.raw(), beta.raw());\n        //ValueTypeB std_old = std;\n        std = 0.;\n\n        for (int i = 0; i < rows; i++)\n        {\n            std += pow(x[i] * beta[i] - sum1, 2.0);\n        }\n\n        std = sqrt(std / rows) / sum1;\n        // print it #, current error, convergence rate\n        //printf(\"ITER: %d     %.3e     %.4lg\\n\",t, std, std / std_old);\n    }\n\n    //Save scaling vectors for later user, setup complete\n    left_scale = VVector(beta);\n    right_scale = VVector(gamma);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleMatrix(Matrix_h &A, ScaleDirection scaleOrUnscale)\n{\n    if (A.is_matrix_distributed())\n    {\n        FatalError(\"Binormalization scaling not supported for distributed matrices\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (left_scale.size() != A.get_num_rows())\n    {\n        FatalError(\"Must call setup(A) before binormalization scaling can scale matrix\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // A_scaled = F*A*G (f = diag(F) = sqrt(fabs(x)), g = diag(G) = sqrt(fabs(y))\n    // A_ij = f_i * A_ij * g_j\n    scaleMatrixHost(A.get_num_rows(), A.row_offsets.raw(), A.col_indices.raw(), A.values.raw(), left_scale.raw(), right_scale.raw());\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    VVector *scale_vector = (leftOrRight == amgx::LEFT) ? &this->left_scale : &this->right_scale;\n\n    //thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), (scaleOrUnscale == amgx::SCALE) ? vmul_scale<ValueTypeB>() : vmul_unscale<ValueTypeB>() );\n    if (scaleOrUnscale == amgx::SCALE)\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), vmul_scale<ValueTypeB>() );\n    }\n    else\n    {\n        thrust_wrapper::transform<AMGX_device>(v.begin(), v.end(), scale_vector->begin(), v.begin(), vmul_unscale<ValueTypeB>() );\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid NBinormalizationScaler<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::scaleVector(VVector &v, ScaleDirection scaleOrUnscale, ScaleSide leftOrRight)\n{\n    FatalError(\"4x4 block size not supported\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class NBinormalizationScaler_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class NBinormalizationScaler<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/scalers/nbinormalization.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"unit_test.h\"\n#include \"vector.h\"\n#include \"test_utils.h\"\n#include \"util.h\"\n#include \"time.h\"\n\nnamespace amgx\n\n{\n\ntemplate<class T>\nstatic __global__\nvoid set_raw(T *v, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n    {\n        v[i] = i;\n    }\n}\ntemplate<class T>\nstatic __global__\nvoid set_flat(T v, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n    {\n        v[i] = i;\n    }\n}\n\ntemplate<class T>\nstatic __global__\nvoid set_block(T v, int K, int I, int J)\n{\n    for (int k = threadIdx.x; k < K; k += blockDim.x)\n        for (int i = 0; i < I; i++)\n            for (int j = 0; j < J; j++)\n            {\n                v(k, i, j) = k * I * J + i * J + j;\n            }\n}\n\n// parameter is used as test name\nDECLARE_UNITTEST_BEGIN(VectorTests);\n\n\n\nvoid run()\n{\n    typedef typename Vector_h::index_type index_type;\n    typedef typename Vector_h::value_type value_type;\n    Vector_h h_v1;\n    Vector_h h_v2;\n    Vector_d d_v1;\n    Vector_d d_v2;\n    UNITTEST_ASSERT_TRUE(h_v1.size() == 0);\n    UNITTEST_ASSERT_TRUE(h_v1.raw() == 0);\n    UNITTEST_ASSERT_TRUE(d_v1.size() == 0);\n    UNITTEST_ASSERT_TRUE(d_v1.raw() == 0);\n    h_v1.resize(4);\n    UNITTEST_ASSERT_TRUE_DESC(\"vector resize\", h_v1.size() == 4);\n    h_v1[3] = 1;\n    UNITTEST_ASSERT_TRUE_DESC(\"vector assign value\", h_v1[3] == 1);\n    h_v1.clear();\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", h_v1.empty());\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", h_v1.size() == 0);\n    d_v1.resize(4);\n    UNITTEST_ASSERT_TRUE_DESC(\"vector resize\", d_v1.size() == 4);\n    d_v1[3] = 1;\n    UNITTEST_ASSERT_TRUE_DESC(\"vector assign value\", d_v1[3] == 1);\n    d_v1.clear();\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", d_v1.empty());\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", d_v1.size() == 0);\n    const Vector_d d_one(64, 1);\n    const Vector_d d_zero(64, 0);\n    const Vector_d h_one(64, 1);\n    const Vector_d h_zero(64, 0);\n    UNITTEST_ASSERT_TRUE_DESC(\"device vector copy constructor size\", d_one.size() == 64);\n    UNITTEST_ASSERT_TRUE_DESC(\"device vector copy constructor value\", d_one[5] == 1);\n    UNITTEST_ASSERT_TRUE_DESC(\"host vector copy constructor size\", h_one.size() == 64);\n    UNITTEST_ASSERT_TRUE_DESC(\"host vector copy constructor value\", h_one[5] == 1);\n    d_v1 = h_one;\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector host to device\", d_v1, d_one);\n    h_v2 = d_one;\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector device to host\", h_v2, h_one);\n    d_v1 = d_one;\n    d_v2 = d_one;\n    h_v1 = h_zero;\n    h_v2 = h_zero;\n    h_v2.copy_async(d_v1);\n    d_v2.copy_async(h_v1);\n    h_v2.sync();\n    d_v2.sync();\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector device to host async\", h_v2, h_one);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector host to device async\", h_v1, d_zero);\n    d_v1 = d_zero;\n    d_v2 = d_one;\n    d_v1.swap(d_v2);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector swap1\", d_v1, d_one);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector swap2\", d_v2, d_zero);\n    //set vector to 0,1,2,3,...,N using the raw pointer\n    set_raw <<< 1, 64>>>(d_v1.raw(), 64);\n    cudaCheckError();\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(4);\n    d_v2.set_block_dimy(4);\n    //set vector to 0,1,2,3,...,N using operator[]\n    set_flat <<< 1, 64>>>(d_v2.pod(), 64);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator[]\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(4);\n    d_v2.set_block_dimy(4);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 4, 4);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 4x4\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(8);\n    d_v2.set_block_dimy(2);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 2, 8);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 2x8\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(2);\n    d_v2.set_block_dimy(8);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 8, 2);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 8x2\", d_v1, d_v2);\n}\n\nDECLARE_UNITTEST_END(VectorTests);\n\n\n// if you want to be able run this test for all available configs you can write this:\n#define AMGX_CASE_LINE(CASE) VectorTests <TemplateMode<CASE>::Type>  VectorTests_instance_mode##CASE;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n// or you can specify several desired configs\n//VectorTests <TemplateMode<AMGX_mode_dDDI>::Type>  VectorTests_instance_mode_dDDI;\n\n} //namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include \"unit_test.h\"\n#include \"vector.h\"\n#include \"test_utils.h\"\n#include \"util.h\"\n#include \"time.h\"\n\nnamespace amgx\n\n{\n\ntemplate<class T>\nstatic __global__\nvoid set_raw(T *v, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n    {\n        v[i] = i;\n    }\n}\ntemplate<class T>\nstatic __global__\nvoid set_flat(T v, int N)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i < N)\n    {\n        v[i] = i;\n    }\n}\n\ntemplate<class T>\nstatic __global__\nvoid set_block(T v, int K, int I, int J)\n{\n    for (int k = threadIdx.x; k < K; k += blockDim.x)\n        for (int i = 0; i < I; i++)\n            for (int j = 0; j < J; j++)\n            {\n                v(k, i, j) = k * I * J + i * J + j;\n            }\n}\n\n// parameter is used as test name\nDECLARE_UNITTEST_BEGIN(VectorTests);\n\n\n\nvoid run()\n{\n    typedef typename Vector_h::index_type index_type;\n    typedef typename Vector_h::value_type value_type;\n    Vector_h h_v1;\n    Vector_h h_v2;\n    Vector_d d_v1;\n    Vector_d d_v2;\n    UNITTEST_ASSERT_TRUE(h_v1.size() == 0);\n    UNITTEST_ASSERT_TRUE(h_v1.raw() == 0);\n    UNITTEST_ASSERT_TRUE(d_v1.size() == 0);\n    UNITTEST_ASSERT_TRUE(d_v1.raw() == 0);\n    h_v1.resize(4);\n    UNITTEST_ASSERT_TRUE_DESC(\"vector resize\", h_v1.size() == 4);\n    h_v1[3] = 1;\n    UNITTEST_ASSERT_TRUE_DESC(\"vector assign value\", h_v1[3] == 1);\n    h_v1.clear();\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", h_v1.empty());\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", h_v1.size() == 0);\n    d_v1.resize(4);\n    UNITTEST_ASSERT_TRUE_DESC(\"vector resize\", d_v1.size() == 4);\n    d_v1[3] = 1;\n    UNITTEST_ASSERT_TRUE_DESC(\"vector assign value\", d_v1[3] == 1);\n    d_v1.clear();\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", d_v1.empty());\n    UNITTEST_ASSERT_TRUE_DESC(\"vector clear\", d_v1.size() == 0);\n    const Vector_d d_one(64, 1);\n    const Vector_d d_zero(64, 0);\n    const Vector_d h_one(64, 1);\n    const Vector_d h_zero(64, 0);\n    UNITTEST_ASSERT_TRUE_DESC(\"device vector copy constructor size\", d_one.size() == 64);\n    UNITTEST_ASSERT_TRUE_DESC(\"device vector copy constructor value\", d_one[5] == 1);\n    UNITTEST_ASSERT_TRUE_DESC(\"host vector copy constructor size\", h_one.size() == 64);\n    UNITTEST_ASSERT_TRUE_DESC(\"host vector copy constructor value\", h_one[5] == 1);\n    d_v1 = h_one;\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector host to device\", d_v1, d_one);\n    h_v2 = d_one;\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector device to host\", h_v2, h_one);\n    d_v1 = d_one;\n    d_v2 = d_one;\n    h_v1 = h_zero;\n    h_v2 = h_zero;\n    h_v2.copy_async(d_v1);\n    d_v2.copy_async(h_v1);\n    h_v2.sync();\n    d_v2.sync();\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector device to host async\", h_v2, h_one);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector host to device async\", h_v1, d_zero);\n    d_v1 = d_zero;\n    d_v2 = d_one;\n    d_v1.swap(d_v2);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector swap1\", d_v1, d_one);\n    UNITTEST_ASSERT_EQUAL_DESC(\"vector swap2\", d_v2, d_zero);\n    //set vector to 0,1,2,3,...,N using the raw pointer\n    set_raw <<< 1, 64>>>(d_v1.raw(), 64);\n    cudaCheckError();\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(4);\n    d_v2.set_block_dimy(4);\n    //set vector to 0,1,2,3,...,N using operator[]\n    set_flat <<< 1, 64>>>(d_v2.pod(), 64);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator[]\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(4);\n    d_v2.set_block_dimy(4);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 4, 4);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 4x4\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(8);\n    d_v2.set_block_dimy(2);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 2, 8);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 2x8\", d_v1, d_v2);\n    d_v2 = d_zero;\n    d_v2.set_block_dimx(2);\n    d_v2.set_block_dimy(8);\n    //set vector to 0,1,2,3,...,N using operator()\n    set_block <<< 1, 64>>>(d_v2.pod(), 4, 8, 2);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL_DESC(\"block vector operator() 8x2\", d_v1, d_v2);\n}\n\nDECLARE_UNITTEST_END(VectorTests);\n\n\n// if you want to be able run this test for all available configs you can write this:\n#define AMGX_CASE_LINE(CASE) VectorTests <TemplateMode<CASE>::Type>  VectorTests_instance_mode##CASE;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n// or you can specify several desired configs\n//VectorTests <TemplateMode<AMGX_mode_dDDI>::Type>  VectorTests_instance_mode_dDDI;\n\n} //namespace amgx\n", "source": "/kaggle/working/amgx/src/tests/vector_tests.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cublas_v2.h>\n#include \"unit_test.h\"\n#include \"matrix.h\"\n#include \"blas.h\"\n#include \"multiply.h\"\n#include \"solvers/dense_lu_solver.h\"\n\nnamespace amgx\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename T >\n__global__ void split_l_and_u(int n, const T *lu, int lda, T *l, T *u )\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    int j = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (i >= n || j >= n)\n    {\n        return;\n    }\n\n    T l_ij = i == j ? T(1) : T(0), u_ij = T(0);\n    T lu_ij = lu[i * lda + j];\n\n    if (i <= j)\n    {\n        u_ij = lu_ij;\n    }\n    else\n    {\n        l_ij = lu_ij;\n    }\n\n    l[i * lda + j] = l_ij;\n    u[i * lda + j] = u_ij;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN(DenseLUSolverTest_Base);\n\nstd::string base_keywords()\n{\n    return \"dense_lu\";\n}\n\ntemplate< typename Matrix >\nvoid make_identity(Matrix &A)\n{\n    typedef typename Matrix::TConfig Matrix_config;\n    typedef typename Matrix_config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef typename Config_h::template setVecPrec<AMGX_vecInt>::Type IVector_config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef Vector<IVector_config_h> IVector_h;\n    const int num_rows = A.get_num_rows();\n    IVector_h row_offsets(num_rows + 1), col_indices(num_rows);\n\n    for ( int i = 0 ; i < num_rows ; ++i )\n    {\n        row_offsets[i] = i;\n        col_indices[i] = i;\n    }\n\n    row_offsets.back() = num_rows;\n    FVector_h values(num_rows, 1.0);\n    A.row_offsets.copy(row_offsets);\n    A.col_indices.copy(col_indices);\n    A.values.     copy(values);\n}\n\ntemplate< typename Matrix_h, typename Matrix_data >\nvoid csr_to_dense(const Matrix_h &A_h, Matrix_data *dense_A_h, int lda)\n{\n    for ( int i = 0 ; i < A_h.get_num_rows() ; ++i )\n        for ( int j = A_h.row_offsets[i] ; j < A_h.row_offsets[i + 1] ; ++j )\n        {\n            dense_A_h[i * lda + A_h.col_indices[j]] = A_h.values[j];\n        }\n}\n\ntemplate< typename Matrix_data >\nvoid l_times_u(int n, Matrix_data *lu_d, int lda)\n{\n    Matrix_data *l_d, *u_d;\n    amgx::memory::cudaMallocAsync((void **) &l_d, n * lda * sizeof(Matrix_data), 0);\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n    UNITTEST_ASSERT_EQUAL(cudaStreamSynchronize(0), cudaSuccess);\n    cudaCheckError();\n    amgx::memory::cudaMallocAsync((void **) &u_d, n * lda * sizeof(Matrix_data), 0);\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n    UNITTEST_ASSERT_EQUAL(cudaStreamSynchronize(0), cudaSuccess);\n    cudaCheckError();\n    // Split LU.\n    dim3 block_dim(16, 16);\n    dim3 grid_dim((n + block_dim.x - 1) / block_dim.x, (n + block_dim.y - 1) / block_dim.y);\n    split_l_and_u <<< grid_dim, block_dim>>>(n, lu_d, lda, l_d, u_d);\n    cudaCheckError();\n    cudaError_t status = cudaDeviceSynchronize();\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(status, cudaSuccess);\n    // LxU = LU.\n    Matrix_data one(1), zero(0);\n    cublasHandle_t cublas_handle;\n    cublasStatus_t cublas_status;\n    cublas_status = cublasCreate(&cublas_handle);\n    UNITTEST_ASSERT_EQUAL(cublas_status, CUBLAS_STATUS_SUCCESS);\n    cublas_status = cublasGemm(cublas_handle,\n                               CUBLAS_OP_N,\n                               CUBLAS_OP_N,\n                               n, n, n,\n                               &one, l_d, lda,\n                               u_d, lda,\n                               &zero, lu_d, lda);\n    UNITTEST_ASSERT_EQUAL(cublas_status, CUBLAS_STATUS_SUCCESS);\n    cublas_status = cublasDestroy(cublas_handle);\n    UNITTEST_ASSERT_EQUAL(cublas_status, CUBLAS_STATUS_SUCCESS);\n    cudaFreeAsync(l_d, 0);\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n    UNITTEST_ASSERT_EQUAL(cudaStreamSynchronize(0), cudaSuccess);\n    cudaCheckError();\n    cudaFreeAsync(u_d, 0);\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n    UNITTEST_ASSERT_EQUAL(cudaStreamSynchronize(0), cudaSuccess);\n    cudaCheckError();\n}\n\ntemplate< typename Matrix, typename Matrix_data >\nvoid check_lu_product(const Matrix &A_h, int n, Matrix_data *lu_d, int lda)\n{\n    // Compute LxU.\n    l_times_u(n, lu_d, lda);\n    // Copy LxU to the host.\n    Matrix_data *lu_h = new Matrix_data[n * lda];\n    cudaMemcpy(lu_h, lu_d, n * lda * sizeof(Matrix_data), cudaMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n    // Make sure LxU equals A.\n    Matrix_data *dense_A_h = new Matrix_data[n * lda];\n    std::memset(dense_A_h, 0x0, n * lda * sizeof(Matrix_data));\n    csr_to_dense(A_h, dense_A_h, lda);\n\n    // Compare the matrices.\n    for ( int i = 0 ; i < n ; ++i )\n        for ( int j = 0 ; j < n ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(lu_h[i * lda + j], dense_A_h[i * lda + j]);\n        }\n\n    delete[] dense_A_h;\n    delete[] lu_h;\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Base);\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Factorization_Id_32, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    // Make sure the solver produces A=LU where A is the identity matrix.\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 32;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h m(N * N);\n    cudaMemcpy(m.raw(), solver.get_dense_A(), N * N * sizeof(Matrix_data), cudaMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n\n    for ( int i = 0 ; i < N ; ++i )\n        for ( int j = 0 ; j < N ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(m[i * solver.get_lda() + j], i == j ? Matrix_data(1) : Matrix_data(0));\n        }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Factorization_Id_32)\n\nDenseLUSolverTest_Factorization_Id_32<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Factorization_Id_32_dDDI;\nDenseLUSolverTest_Factorization_Id_32<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Factorization_Id_32_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Factorization_Id_256, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    // Make sure the solver produces A=LU where A is the identity matrix.\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 256;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h m(N * N);\n    cudaMemcpy(m.raw(), solver.get_dense_A(), N * N * sizeof(Matrix_data), cudaMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(cudaGetLastError(), cudaSuccess);\n\n    for ( int i = 0 ; i < N ; ++i )\n        for ( int j = 0 ; j < N ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(m[i * solver.get_lda() + j], i == j ? Matrix_data(1) : Matrix_data(0));\n        }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Factorization_Id_256)\n\nDenseLUSolverTest_Factorization_Id_256<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Factorization_Id_256_dDDI;\nDenseLUSolverTest_Factorization_Id_256<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Factorization_Id_256_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Poisson3D, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::MatPrec Matrix_data;\n    typedef typename T_Config::template setMemSpace<AMGX_device>::Type Config_d;\n    typedef typename T_Config::template setMemSpace<AMGX_host>  ::Type Config_h;\n    typedef Matrix<Config_d> Matrix_d;\n    typedef Vector<Config_d> Vector_d;\n    typedef Matrix<Config_h> Matrix_h;\n    typedef Vector<Config_h> Vector_h;\n    Matrix_h A_h;\n    Vector_h x_h, b_h;\n    \n    A_h.set_initialized(0);\n    generatePoissonForTest(A_h, 1, 0, 27, 16, 16, 16);\n    A_h.set_initialized(1);\n    \n    AMG_Config cfg;\n    cfg.parseParameterString(\"monitor_residual=1\");\n    Matrix_d A_d(A_h);\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"default\", NULL);\n    solver.setup(A_d, false);\n    const int n = A_h.get_num_rows();\n    Vector_d b_d(n), x_d(n), r_d(n);\n    thrust_wrapper::fill<AMGX_device>(b_d.begin(), b_d.end(), Matrix_data(1));\n    solver.solve(b_d, x_d, false);\n    solver.compute_residual(b_d, x_d, r_d);\n    Vector_h resid_nrm(1);\n    solver.compute_norm(r_d, resid_nrm);\n    Vector_h rhs_nrm(1);\n    solver.compute_norm(b_d, rhs_nrm);\n    double relError = resid_nrm[0] / rhs_nrm[0];\n\n    if ( T_Config::matPrec == AMGX_matDouble )\n    {\n        UNITTEST_ASSERT_EQUAL_TOL(resid_nrm[0], 0.0, 1.0e-12);\n    }\n    else\n    {\n        UNITTEST_ASSERT_EQUAL_TOL(resid_nrm[0], 0.0f, 1.0e-6f);\n    }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Poisson3D)\n\nDenseLUSolverTest_Solve_Poisson3D<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Poisson3D_dDDI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Id_32, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 32;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h b_h(N);\n\n    for ( int i = 0 ; i < N ; ++i )\n    {\n        b_h[i] = Matrix_data(rand()) / RAND_MAX;\n    }\n\n    Vector<T_Config> b(b_h), x(N), r(N);\n    solver.solve(b, x, false);\n    UNITTEST_ASSERT_EQUAL(x, b);\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Id_32)\n\nDenseLUSolverTest_Solve_Id_32<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Id_32_dDDI;\nDenseLUSolverTest_Solve_Id_32<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Solve_Id_32_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Id_256, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 256;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h b_h(N);\n\n    for ( int i = 0 ; i < N ; ++i )\n    {\n        b_h[i] = Matrix_data(rand()) / RAND_MAX;\n    }\n\n    Vector<T_Config> b(b_h), x(N);\n    solver.solve(b, x, false);\n    UNITTEST_ASSERT_EQUAL(x, b);\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Id_256)\n\nDenseLUSolverTest_Solve_Id_256<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Id_256_dDDI;\nDenseLUSolverTest_Solve_Id_256<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Solve_Id_256_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <hipblas.h>\n#include \"unit_test.h\"\n#include \"matrix.h\"\n#include \"blas.h\"\n#include \"multiply.h\"\n#include \"solvers/dense_lu_solver.h\"\n\nnamespace amgx\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename T >\n__global__ void split_l_and_u(int n, const T *lu, int lda, T *l, T *u )\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    int j = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (i >= n || j >= n)\n    {\n        return;\n    }\n\n    T l_ij = i == j ? T(1) : T(0), u_ij = T(0);\n    T lu_ij = lu[i * lda + j];\n\n    if (i <= j)\n    {\n        u_ij = lu_ij;\n    }\n    else\n    {\n        l_ij = lu_ij;\n    }\n\n    l[i * lda + j] = l_ij;\n    u[i * lda + j] = u_ij;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN(DenseLUSolverTest_Base);\n\nstd::string base_keywords()\n{\n    return \"dense_lu\";\n}\n\ntemplate< typename Matrix >\nvoid make_identity(Matrix &A)\n{\n    typedef typename Matrix::TConfig Matrix_config;\n    typedef typename Matrix_config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef typename Config_h::template setVecPrec<AMGX_vecInt>::Type IVector_config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef Vector<IVector_config_h> IVector_h;\n    const int num_rows = A.get_num_rows();\n    IVector_h row_offsets(num_rows + 1), col_indices(num_rows);\n\n    for ( int i = 0 ; i < num_rows ; ++i )\n    {\n        row_offsets[i] = i;\n        col_indices[i] = i;\n    }\n\n    row_offsets.back() = num_rows;\n    FVector_h values(num_rows, 1.0);\n    A.row_offsets.copy(row_offsets);\n    A.col_indices.copy(col_indices);\n    A.values.     copy(values);\n}\n\ntemplate< typename Matrix_h, typename Matrix_data >\nvoid csr_to_dense(const Matrix_h &A_h, Matrix_data *dense_A_h, int lda)\n{\n    for ( int i = 0 ; i < A_h.get_num_rows() ; ++i )\n        for ( int j = A_h.row_offsets[i] ; j < A_h.row_offsets[i + 1] ; ++j )\n        {\n            dense_A_h[i * lda + A_h.col_indices[j]] = A_h.values[j];\n        }\n}\n\ntemplate< typename Matrix_data >\nvoid l_times_u(int n, Matrix_data *lu_d, int lda)\n{\n    Matrix_data *l_d, *u_d;\n    amgx::memory::hipMallocAsync((void **) &l_d, n * lda * sizeof(Matrix_data), 0);\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n    UNITTEST_ASSERT_EQUAL(hipStreamSynchronize(0), hipSuccess);\n    cudaCheckError();\n    amgx::memory::hipMallocAsync((void **) &u_d, n * lda * sizeof(Matrix_data), 0);\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n    UNITTEST_ASSERT_EQUAL(hipStreamSynchronize(0), hipSuccess);\n    cudaCheckError();\n    // Split LU.\n    dim3 block_dim(16, 16);\n    dim3 grid_dim((n + block_dim.x - 1) / block_dim.x, (n + block_dim.y - 1) / block_dim.y);\n    split_l_and_u <<< grid_dim, block_dim>>>(n, lu_d, lda, l_d, u_d);\n    cudaCheckError();\n    hipError_t status = hipDeviceSynchronize();\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(status, hipSuccess);\n    // LxU = LU.\n    Matrix_data one(1), zero(0);\n    hipblasHandle_t cublas_handle;\n    hipblasStatus_t cublas_status;\n    cublas_status = hipblasCreate(&cublas_handle);\n    UNITTEST_ASSERT_EQUAL(cublas_status, HIPBLAS_STATUS_SUCCESS);\n    cublas_status = cublasGemm(cublas_handle,\n                               HIPBLAS_OP_N,\n                               HIPBLAS_OP_N,\n                               n, n, n,\n                               &one, l_d, lda,\n                               u_d, lda,\n                               &zero, lu_d, lda);\n    UNITTEST_ASSERT_EQUAL(cublas_status, HIPBLAS_STATUS_SUCCESS);\n    cublas_status = hipblasDestroy(cublas_handle);\n    UNITTEST_ASSERT_EQUAL(cublas_status, HIPBLAS_STATUS_SUCCESS);\n    hipFreeAsync(l_d, 0);\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n    UNITTEST_ASSERT_EQUAL(hipStreamSynchronize(0), hipSuccess);\n    cudaCheckError();\n    hipFreeAsync(u_d, 0);\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n    UNITTEST_ASSERT_EQUAL(hipStreamSynchronize(0), hipSuccess);\n    cudaCheckError();\n}\n\ntemplate< typename Matrix, typename Matrix_data >\nvoid check_lu_product(const Matrix &A_h, int n, Matrix_data *lu_d, int lda)\n{\n    // Compute LxU.\n    l_times_u(n, lu_d, lda);\n    // Copy LxU to the host.\n    Matrix_data *lu_h = new Matrix_data[n * lda];\n    hipMemcpy(lu_h, lu_d, n * lda * sizeof(Matrix_data), hipMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n    // Make sure LxU equals A.\n    Matrix_data *dense_A_h = new Matrix_data[n * lda];\n    std::memset(dense_A_h, 0x0, n * lda * sizeof(Matrix_data));\n    csr_to_dense(A_h, dense_A_h, lda);\n\n    // Compare the matrices.\n    for ( int i = 0 ; i < n ; ++i )\n        for ( int j = 0 ; j < n ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(lu_h[i * lda + j], dense_A_h[i * lda + j]);\n        }\n\n    delete[] dense_A_h;\n    delete[] lu_h;\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Base);\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Factorization_Id_32, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    // Make sure the solver produces A=LU where A is the identity matrix.\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 32;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h m(N * N);\n    hipMemcpy(m.raw(), solver.get_dense_A(), N * N * sizeof(Matrix_data), hipMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n\n    for ( int i = 0 ; i < N ; ++i )\n        for ( int j = 0 ; j < N ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(m[i * solver.get_lda() + j], i == j ? Matrix_data(1) : Matrix_data(0));\n        }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Factorization_Id_32)\n\nDenseLUSolverTest_Factorization_Id_32<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Factorization_Id_32_dDDI;\nDenseLUSolverTest_Factorization_Id_32<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Factorization_Id_32_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Factorization_Id_256, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    // Make sure the solver produces A=LU where A is the identity matrix.\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 256;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h m(N * N);\n    hipMemcpy(m.raw(), solver.get_dense_A(), N * N * sizeof(Matrix_data), hipMemcpyDeviceToHost);\n    cudaCheckError();\n    UNITTEST_ASSERT_EQUAL(hipGetLastError(), hipSuccess);\n\n    for ( int i = 0 ; i < N ; ++i )\n        for ( int j = 0 ; j < N ; ++j )\n        {\n            UNITTEST_ASSERT_EQUAL(m[i * solver.get_lda() + j], i == j ? Matrix_data(1) : Matrix_data(0));\n        }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Factorization_Id_256)\n\nDenseLUSolverTest_Factorization_Id_256<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Factorization_Id_256_dDDI;\nDenseLUSolverTest_Factorization_Id_256<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Factorization_Id_256_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Poisson3D, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::MatPrec Matrix_data;\n    typedef typename T_Config::template setMemSpace<AMGX_device>::Type Config_d;\n    typedef typename T_Config::template setMemSpace<AMGX_host>  ::Type Config_h;\n    typedef Matrix<Config_d> Matrix_d;\n    typedef Vector<Config_d> Vector_d;\n    typedef Matrix<Config_h> Matrix_h;\n    typedef Vector<Config_h> Vector_h;\n    Matrix_h A_h;\n    Vector_h x_h, b_h;\n    \n    A_h.set_initialized(0);\n    generatePoissonForTest(A_h, 1, 0, 27, 16, 16, 16);\n    A_h.set_initialized(1);\n    \n    AMG_Config cfg;\n    cfg.parseParameterString(\"monitor_residual=1\");\n    Matrix_d A_d(A_h);\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"default\", NULL);\n    solver.setup(A_d, false);\n    const int n = A_h.get_num_rows();\n    Vector_d b_d(n), x_d(n), r_d(n);\n    thrust_wrapper::fill<AMGX_device>(b_d.begin(), b_d.end(), Matrix_data(1));\n    solver.solve(b_d, x_d, false);\n    solver.compute_residual(b_d, x_d, r_d);\n    Vector_h resid_nrm(1);\n    solver.compute_norm(r_d, resid_nrm);\n    Vector_h rhs_nrm(1);\n    solver.compute_norm(b_d, rhs_nrm);\n    double relError = resid_nrm[0] / rhs_nrm[0];\n\n    if ( T_Config::matPrec == AMGX_matDouble )\n    {\n        UNITTEST_ASSERT_EQUAL_TOL(resid_nrm[0], 0.0, 1.0e-12);\n    }\n    else\n    {\n        UNITTEST_ASSERT_EQUAL_TOL(resid_nrm[0], 0.0f, 1.0e-6f);\n    }\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Poisson3D)\n\nDenseLUSolverTest_Solve_Poisson3D<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Poisson3D_dDDI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Id_32, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 32;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h b_h(N);\n\n    for ( int i = 0 ; i < N ; ++i )\n    {\n        b_h[i] = Matrix_data(rand()) / RAND_MAX;\n    }\n\n    Vector<T_Config> b(b_h), x(N), r(N);\n    solver.solve(b, x, false);\n    UNITTEST_ASSERT_EQUAL(x, b);\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Id_32)\n\nDenseLUSolverTest_Solve_Id_32<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Id_32_dDDI;\nDenseLUSolverTest_Solve_Id_32<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Solve_Id_32_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nDECLARE_UNITTEST_BEGIN_EXTD(DenseLUSolverTest_Solve_Id_256, DenseLUSolverTest_Base<T_Config>);\n\nvoid run()\n{\n    typedef typename T_Config::template setMemSpace<AMGX_host>::Type Config_h;\n    typedef Vector<Config_h> FVector_h;\n    typedef typename TConfig::MatPrec Matrix_data;\n    const int N = 256;\n    Matrix<T_Config> A(N, N, N, CSR);\n    A.set_initialized(0);\n    this->make_identity(A);\n    A.set_initialized(1);\n    AMG_Config cfg;\n    dense_lu_solver::DenseLUSolver<T_Config> solver(cfg, \"\", NULL);\n    solver.setup(A, false);\n    FVector_h b_h(N);\n\n    for ( int i = 0 ; i < N ; ++i )\n    {\n        b_h[i] = Matrix_data(rand()) / RAND_MAX;\n    }\n\n    Vector<T_Config> b(b_h), x(N);\n    solver.solve(b, x, false);\n    UNITTEST_ASSERT_EQUAL(x, b);\n}\n\nDECLARE_UNITTEST_END(DenseLUSolverTest_Solve_Id_256)\n\nDenseLUSolverTest_Solve_Id_256<TemplateMode<AMGX_mode_dDDI>::Type> DenseLUSolverTest_Solve_Id_256_dDDI;\nDenseLUSolverTest_Solve_Id_256<TemplateMode<AMGX_mode_dFFI>::Type> DenseLUSolverTest_Solve_Id_256_dFFI;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/tests/dense_lu.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"unit_test.h\"\n#include \"amg_solver.h\"\n#include \"test_utils.h\"\n#include \"matrix_coloring/min_max.h\"\n#include \"matrix_coloring/multi_hash.h\"\n#include \"matrix_coloring/parallel_greedy.h\"\n#include <thrust/iterator/counting_iterator.h>\n#include \"matrix_analysis.h\"\n\nnamespace amgx\n{\n\n__global__ void start_coloring_mark() {}\n__global__ void end_coloring_mark() {}\n\ntemplate<int LEVEL>\nstruct neighbor_color_validity_checker\n{\n    __host__ __device__ static int check(const int *A_row_offsets, const int *A_col_indices, const int *color, const int row_start, const int row_end, const int my_color, const int self)\n    {\n        for (int row_it = row_start; row_it < row_end; row_it++)\n        {\n            int c   = A_col_indices[row_it];\n            int col = color[c];\n\n            if (my_color == col && c != self)\n            {\n                return 0;\n            }\n\n            return neighbor_color_validity_checker < LEVEL - 1 >::check(A_row_offsets, A_col_indices, color, A_row_offsets[c], A_row_offsets[c + 1], my_color, self);\n        }\n\n        return 1; //empty row: ok\n    }\n\n};\n\ntemplate<>\nstruct neighbor_color_validity_checker<0>\n{\n    __host__ __device__ static int check(const int *A_row_offsets, const int *A_col_indices, const int *color, const int row_start, const int row_end, const int my_color, const int self)\n    {\n        //end compile time recursion\n        return 1;\n    }\n};\n\ntemplate<typename IndexType>\nstruct CheckColoring\n{\n    const IndexType *_A_offsets;\n    const IndexType *_A_cols;\n    const IndexType *_colors;\n    const int _coloring_level;\n\n    CheckColoring(const IndexType *A_offsets, const IndexType *A_cols, const IndexType *colors, const int coloring_level) : _A_offsets(A_offsets), _A_cols(A_cols), _colors(colors), _coloring_level(coloring_level) { }\n\n    __host__ __device__ IndexType operator()(IndexType i)\n    {\n        IndexType col = _colors[i];\n        // do not count uncolored nodes\n\n        if (col == 0)\n        {\n            return 0;\n        }\n\n        IndexType row_start = _A_offsets[i];\n        IndexType row_end = _A_offsets[i + 1];\n\n        if (_coloring_level == 4)\n        {\n            return neighbor_color_validity_checker<4>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 5)\n        {\n            return neighbor_color_validity_checker<5>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 6)\n        {\n            return neighbor_color_validity_checker<6>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 7)\n        {\n            return neighbor_color_validity_checker<7>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else\n        {\n            for (IndexType r = row_start; r < row_end; r++)\n            {\n                IndexType j  = _A_cols[r];\n\n                // skip diagonal\n                if (j == i) { continue; }\n\n                IndexType jcol = _colors[j];\n\n                if (jcol == 0) { continue; }\n\n                // if 2 colors are adjacent, return 1\n                if (jcol == col)\n                {\n                    //printf(\"invalid coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j,col,jcol);\n                    return 1;\n                }\n\n                if (_coloring_level >= 2)\n                {\n                    IndexType row_start = _A_offsets[j];\n                    IndexType row_end = _A_offsets[j + 1];\n\n                    for (IndexType r = row_start; r < row_end; r++)\n                    {\n                        IndexType k  = _A_cols[r];\n\n                        // skip diagonal\n                        if (k == i) { continue; }\n\n                        IndexType jcol = _colors[k];\n\n                        if (jcol == 0) { continue; }\n\n                        // if 2 colors are adjacent, return 1\n                        if (jcol == col)\n                        {\n                            //printf(\"invalid 2 coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j2,col,jcol);\n                            return 1;\n                        }\n\n                        if (_coloring_level >= 3)\n                        {\n                            IndexType row_start = _A_offsets[k];\n                            IndexType row_end = _A_offsets[k + 1];\n\n                            for (IndexType r = row_start; r < row_end; r++)\n                            {\n                                IndexType l  = _A_cols[r];\n\n                                // skip diagonal\n                                if (l == i) { continue; }\n\n                                IndexType jcol = _colors[l];\n\n                                if (jcol == 0) { continue; }\n\n                                // if 2 colors are adjacent, return 1\n                                if (jcol == col)\n                                {\n                                    //printf(\"invalid 2 coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j2,col,jcol);\n                                    return 1;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        } // loop over first ring neighbours\n\n        // no conflict => return 0\n        return 0;\n    }\n};\n\nstatic std::map<std::string, double> total_runtime; //for global aggregate scoring across tests\nstatic std::map<std::string, double> coloring_score;\nstatic std::map<std::string, double> coloring_runs;\n\nstatic FILE *f_csv_file = 0; //once per test rewrite\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\nDECLARE_UNITTEST_BEGIN(MatrixColoringTest_Base);\n\nstd::string base_keywords()\n{\n    return \"coloring\";\n}\n\n// dense histogram using binary search\ntemplate <typename Vector1,\n          typename Vector2>\nvoid color_histogram(const Vector1 &row_colors, Vector2 &histogram)\n{\n    typedef typename Vector1::value_type ValueType; // input value type\n    typedef typename Vector2::value_type IndexType; // histogram index type\n    // copy input data (could be skipped if input is allowed to be modified)\n    device_vector_alloc<ValueType> data(row_colors);\n    // sort data to bring equal elements together\n    thrust_wrapper::sort<AMGX_device>(data.begin(), data.end());\n    // number of histogram bins is equal to the maximum value plus one\n    IndexType num_bins = data.back() + 1;\n    // resize histogram storage\n    histogram.resize(num_bins);\n    // find the end of each bin of values\n    amgx::thrust::counting_iterator<IndexType> search_begin(0);\n    amgx::thrust::upper_bound(data.begin(), data.end(),\n                        search_begin, search_begin + num_bins,\n                        histogram.begin());\n    // compute the histogram by taking differences of the cumulative histogram\n    amgx::thrust::adjacent_difference(histogram.begin(), histogram.end(),\n                                histogram.begin());\n}\n\n\n\n\ntemplate< AMGX_VecPrecision VecPrecision, AMGX_MatPrecision MatPrecision >\nvoid\ncolor_matrix_file(const std::string &filename, int max_coloring_level = 3)\n{\n    AMG_Config cfg;\n    size_t pool_size = cfg.getParameter<size_t>(\"device_mem_pool_size\", \"default\");\n    size_t max_alloc_size = cfg.getParameter<size_t>(\"device_mem_pool_max_alloc_size\", \"default\");\n\n    if ( memory::hasPinnedMemoryPool() )\n    {\n        memory::setPinnedMemoryPool( new memory::PinnedMemoryPool() );\n    }\n\n    if ( memory::hasDeviceMemoryPool() )\n    {\n        memory::setDeviceMemoryPool( new memory::DeviceMemoryPool(pool_size, max_alloc_size, 0) );\n    }\n\n    cudaDeviceSynchronize(); // why ????\n    cudaCheckError();\n    bool verbose_output = 0;\n    const std::string cfg_scope = \"default\";\n    std::stringstream config_string_base;\n    config_string_base <<  \"max_uncolored_percentage=0, coloring_level=1\";\n    std::vector<std::string> coloring_schemes;\n#define ALL 1\n#if ALL\n    coloring_schemes.push_back(\"GREEDY_RECOLOR\");\n    coloring_schemes.push_back(\"MIN_MAX\");\n    coloring_schemes.push_back(\"MIN_MAX_2RING\");\n    coloring_schemes.push_back(\"PARALLEL_GREEDY\");\n    coloring_schemes.push_back(\"MULTI_HASH,max_uncolored_percentage=0.0\");\n    coloring_schemes.push_back(\"GREEDY_RECOLOR,coloring_custom_arg=FIRSTSTEP\");\n    coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING\");\n    //coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_custom_arg=IDO\");\n    coloring_schemes.push_back(\"SERIAL_GREEDY_BFS\");\n#else\n    //max_coloring_level = 2;\n#endif\n\n    if (max_coloring_level >= 2)\n    {\n#if 1\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"MIN_MAX, coloring_level=2\");\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=2\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2\");\n        //coloring_schemes.push_back(\"GREEDY_RECOLOR, coloring_level=2\");\n        //coloring_schemes.push_back(\"GREEDY_RECOLOR, coloring_level=2,coloring_custom_arg=FIRSTSTEP\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=FULL64\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=FULL256\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=CN1\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=CN2\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=2\");\n        /*coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_custom_arg=IDO, coloring_level=2\");\n        */\n#else\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=2\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2\");\n#endif\n    }\n\n    if (max_coloring_level >= 3)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=3\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=3\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=3\");\n    }\n\n    if (max_coloring_level >= 4)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=4\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=4\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=4\");\n    }\n\n    if (max_coloring_level >= 5)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=5\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=5\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=5\");\n    }\n\n    if (verbose_output) //enable for writing a csv with all the info\n    {\n        if (!f_csv_file)\n        {\n            f_csv_file = fopen(\"matrix_coloring_test_out.txt\", \"w\");\n        }\n        else\n        {\n            f_csv_file = fopen(\"matrix_coloring_test_out.txt\", \"a\");\n        }\n    }\n\n    if (verbose_output) { std::cout << std::endl; }\n\n    std::vector<std::string> text_outputs(coloring_schemes.size());\n    int min_time_i = -1;\n    float min_time = 0;\n    int min_colors_i = -1;\n    int min_colors = 0;\n    int min_colors_iX = -1;\n    int min_colorsX = 0;\n    typedef TemplateConfig<AMGX_host,   VecPrecision, MatPrecision, AMGX_indInt> Config_h;\n    typedef TemplateConfig<AMGX_device, VecPrecision, MatPrecision, AMGX_indInt> Config_d;\n    typedef Matrix<Config_h> Matrix_h;\n    typedef Vector<Config_h> Vector_h;\n    typedef Matrix<Config_d> Matrix_d;\n    typedef Vector<Config_d> Vector_d;\n    Matrix_h A_h;\n    Vector_h x_h, b_h;\n    A_h.set_initialized(0);\n    A_h.addProps(CSR);\n    std::string fail_msg = \"Cannot open \" + filename;\n    this->PrintOnFail(fail_msg.c_str());\n    //UNITTEST_ASSERT_TRUE(this->read_system( filename.c_str(), A_h, b_h, x_h ));\n    UNITTEST_ASSERT_TRUE(MatrixIO<Config_h>::readSystem( filename.c_str(), A_h, b_h, x_h ) == AMGX_OK);;\n    A_h.set_initialized(1);\n    MatrixAnalysis<TConfig_h> ana(&A_h, &b_h);\n    bool str_sym, sym;\n    bool verbose = false;\n    ana.checkSymmetry(str_sym, sym, verbose);\n    Matrix_d A_d( A_h );\n\n    for (int i = 0; i < coloring_schemes.size(); i++)\n    {\n        if (coloring_schemes[i] == \"\")\n        {\n            if (verbose_output) { text_outputs[i] = \"\\n\"; }\n\n            if (verbose_output) { std::cout << \"\\n\"; }\n\n            min_colorsX = 0;\n            min_colors_iX = -1;\n            continue;\n        }\n\n        std::stringstream config_string;\n        std::stringstream text_output;\n        config_string << config_string_base.str() << \", matrix_coloring_scheme=\" << coloring_schemes[i] << std::endl;\n        AMG_Config  cfg;\n        UNITTEST_ASSERT_TRUE ( cfg.parseParameterString(config_string.str().c_str()) == AMGX_OK);\n        UNITTEST_ASSERT_TRUE_DESC(\"Matrix does not have structural symmetry, unit test invalid\", str_sym);;\n        A_d.set_initialized(0);\n        A_d.addProps(CSR);\n//      cudaDeviceSynchronize();\ncudaCheckError();\n        cudaEvent_t color_start, color_stop;\n        cudaEventCreate(&color_start);\n        cudaCheckError();\n        cudaEventCreate(&color_stop);\n        cudaCheckError();\n        //A_d.colorMatrix(cfg,cfg_scope);\n        //A_d.set_initialized(0);\n        float elapsed_time;\n        cudaEventRecord( color_start);\n        cudaCheckError();\n        A_d.colorMatrix(cfg, cfg_scope);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"colors\",  (void*)A_d.getMatrixColoring().getRowColors().raw(), A_d.getMatrixColoring().getRowColors().size()*4);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"cols\",  (void*)A_d.col_indices.raw(), A_d.col_indices.size()*4);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"rows\",  (void*)A_d.row_offsets.raw(), A_d.row_offsets.size()*4);\n        cudaEventRecord( color_stop);\n        cudaCheckError();\n        cudaEventSynchronize( color_stop);\n        cudaCheckError();\n        cudaEventElapsedTime( &elapsed_time, color_start, color_stop);\n        elapsed_time *= 1e-3f;\n        A_d.set_initialized(1);\n        // check coloring\n        const IndexType *A_row_offsets_ptr    = A_d.row_offsets.raw();\n        const IndexType *A_column_indices_ptr = A_d.col_indices.raw();\n        const IndexType *row_colors_ptr = A_d.getMatrixColoring().getRowColors().raw();\n        IndexType num_bad, num_bad_plus;\n        {\n            //order +1\n            CheckColoring<IndexType> checker(A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, 1 + A_d.getMatrixColoring().getColoringLevel());\n            num_bad_plus =\n                thrust_wrapper::transform_reduce<AMGX_device>(amgx::thrust::counting_iterator<IndexType>(0), amgx::thrust::counting_iterator<IndexType>(A_d.get_num_rows()),\n                                         checker, (IndexType)0, amgx::thrust::plus<IndexType>());\n        }\n        {\n            CheckColoring<IndexType> checker(A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, A_d.getMatrixColoring().getColoringLevel());\n            num_bad =\n                thrust_wrapper::transform_reduce<AMGX_device>(amgx::thrust::counting_iterator<IndexType>(0), amgx::thrust::counting_iterator<IndexType>(A_d.get_num_rows()),\n                                         checker, (IndexType)0, amgx::thrust::plus<IndexType>());\n        }\n        int num_uncolored = (int) amgx::thrust::count_if( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), is_zero() );\n        int mincol        = (int) amgx::thrust::reduce( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), 0, amgx::thrust::minimum<int>() );\n        int maxcol        = (int) amgx::thrust::reduce( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), 0, amgx::thrust::maximum<int>() );\n        std::stringstream error_str;\n        error_str << \"Coloring scheme \" << coloring_schemes[i] << \" with \" << num_bad << \" invalid rows, \" << num_uncolored << \"(\" << mincol << \",\" << maxcol << \")\" << \" uncolored rows and \" << A_d.getMatrixColoring().getNumColors()  << \" used colors and bonus = \" << num_bad_plus;\n\n        if (verbose_output)\n        {\n            text_output << std::setw(28) << coloring_schemes[i] << \" : num_colors=\" << std::setw(6) << A_d.getMatrixColoring().getNumColors() << \" , runtime= \" << std::setw(8) << std::fixed << std::setprecision(5) << elapsed_time << \" mM(\" << mincol << \",\" << maxcol << \")\"; // << std::endl;;\n        }\n\n        if (num_bad == 0 && num_uncolored == 0)\n        {\n            if (elapsed_time < min_time || min_time_i == -1)\n            {\n                min_time_i = i;\n                min_time = elapsed_time;\n            }\n\n            if (A_d.getMatrixColoring().getNumColors() < min_colors || min_colors_i == -1)\n            {\n                min_colors_i = i;\n                min_colors = A_d.getMatrixColoring().getNumColors();\n            }\n\n            if (A_d.getMatrixColoring().getNumColors() < min_colorsX || min_colors_iX == -1)\n            {\n                min_colors_iX = i;\n                min_colorsX = A_d.getMatrixColoring().getNumColors();\n            }\n        }\n\n        int degree;\n        {\n            device_vector_alloc<int> hist;\n            amgx::thrust::host_vector<int> h_hist;\n            device_vector_alloc<int> columns(A_d.get_num_rows());\n            amgx::thrust::adjacent_difference(A_d.row_offsets.begin(), A_d.row_offsets.end(), columns.begin());\n            degree = amgx::thrust::reduce(columns.begin(), columns.end(), -1, amgx::thrust::maximum<int>());\n            color_histogram(A_d.getMatrixColoring().getRowColors(), hist);\n            h_hist = hist;\n            float total = 0, m = h_hist[1], M = h_hist[1]; //0 is 0\n\n            for (int j = 1; j < h_hist.size(); j++)\n            {\n                if (h_hist[j] > M) { M = h_hist[j]; }\n\n                if (h_hist[j] < m) { m = h_hist[j]; }\n\n                total += h_hist[j];\n            }\n\n            float avg = total / (h_hist.size() - 1);\n            float rms = 0;\n\n            for (int j = 0; j < h_hist.size(); j++)\n            {\n                rms = (h_hist[j] - avg) * (h_hist[j] - avg);\n            }\n\n            rms = sqrt(rms / (h_hist.size() - 1));\n\n            if (verbose_output) { text_output << \" [\" << \" min=\" << m << \", max=\" << M << \", avg=\" << avg << \", rms=\" << rms << \", deg=\" << degree << \", uncol=\" << num_uncolored << \" bonus=\" << num_bad_plus << \"]\"; }\n\n            if (f_csv_file) { fprintf(f_csv_file, \"%s;%d;%d;%d;%.3f;%.3f;%.3f;%.3f;%.3f\\n\", coloring_schemes[i].c_str(), A_d.getMatrixColoring().getNumColors(), num_bad, num_uncolored, elapsed_time * 1e3, m, M, avg, rms); }\n        }\n        total_runtime[coloring_schemes[i]]  += elapsed_time;\n        coloring_score[coloring_schemes[i]] += double(A_d.getMatrixColoring().getNumColors() - 1) / pow(static_cast<double>(degree), static_cast<double>(A_d.getMatrixColoring().getColoringLevel()));\n        coloring_runs[coloring_schemes[i]]  += 1.0;\n\n        if (verbose_output) { text_output << std::endl; }\n\n        if (verbose_output) { printf(\"%s\", text_output.str().c_str()); }\n\n        text_outputs[i] = text_output.str();\n        UNITTEST_ASSERT_EQUAL_DESC(error_str.str().c_str(), num_bad, 0);\n    }\n\n    if (verbose_output && 0)\n    {\n        for (int i = 0; i < text_outputs.size(); i++)\n        {\n            if (i == min_colors_i) { std::cout << \"*\"; }\n\n            if (i == min_time_i) { std::cout << \"#\"; }\n\n            std::cout << text_outputs[i];\n        }\n    }\n\n    if (f_csv_file)\n    {\n        fprintf(f_csv_file, \"\\n\");\n        fclose(f_csv_file);\n    }\n\n    if (verbose_output)\n    {\n        printf(\"\\n\\n\\n\");\n\n        for (int i = 0; i < text_outputs.size(); i++)\n        {\n            if (coloring_schemes[i] == \"\")\n            {\n                std::cout << \"\\n\";\n                continue;\n            }\n\n            std::string name = coloring_schemes[i];\n            std::cout << std::setw(80) << coloring_schemes[i];\n            std::cout << \" runtime=\" << std::setw(8) << std::fixed << std::setprecision(5) << total_runtime[name];\n            std::cout << \" inefficiency=\" << std::setw(6) << std::fixed << std::setprecision(3) << coloring_score[name] / coloring_runs[name]; // << std::endl;;\n            std::cout << \"\\n\";\n        }\n\n        printf(\"\\n\\n\\n\");\n    }\n\n    cudaDeviceSynchronize();\n    cudaCheckError();\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTest_Base);\n\n\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestatmosmodj, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Public/Florida/atmosmodj.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestatmosmodj)\nMatrixColoringTestatmosmodj<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestatmosmodj_dDDI;\n\n\n//--------------------------------------------------------------------------------------------------\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestatmosmodl, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Public/Florida/atmosmodl.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestatmosmodl)\nMatrixColoringTestatmosmodl<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestatmosmodl_dDDI;\n\n\n//--------------------------------------------------------------------------------------------------\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestpoisson, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Internal/poisson/poisson27x50x50x50.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestpisson)\nMatrixColoringTestpoisson<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestpoisson_dDDI;\n\n} //namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include \"unit_test.h\"\n#include \"amg_solver.h\"\n#include \"test_utils.h\"\n#include \"matrix_coloring/min_max.h\"\n#include \"matrix_coloring/multi_hash.h\"\n#include \"matrix_coloring/parallel_greedy.h\"\n#include <thrust/iterator/counting_iterator.h>\n#include \"matrix_analysis.h\"\n\nnamespace amgx\n{\n\n__global__ void start_coloring_mark() {}\n__global__ void end_coloring_mark() {}\n\ntemplate<int LEVEL>\nstruct neighbor_color_validity_checker\n{\n    __host__ __device__ static int check(const int *A_row_offsets, const int *A_col_indices, const int *color, const int row_start, const int row_end, const int my_color, const int self)\n    {\n        for (int row_it = row_start; row_it < row_end; row_it++)\n        {\n            int c   = A_col_indices[row_it];\n            int col = color[c];\n\n            if (my_color == col && c != self)\n            {\n                return 0;\n            }\n\n            return neighbor_color_validity_checker < LEVEL - 1 >::check(A_row_offsets, A_col_indices, color, A_row_offsets[c], A_row_offsets[c + 1], my_color, self);\n        }\n\n        return 1; //empty row: ok\n    }\n\n};\n\ntemplate<>\nstruct neighbor_color_validity_checker<0>\n{\n    __host__ __device__ static int check(const int *A_row_offsets, const int *A_col_indices, const int *color, const int row_start, const int row_end, const int my_color, const int self)\n    {\n        //end compile time recursion\n        return 1;\n    }\n};\n\ntemplate<typename IndexType>\nstruct CheckColoring\n{\n    const IndexType *_A_offsets;\n    const IndexType *_A_cols;\n    const IndexType *_colors;\n    const int _coloring_level;\n\n    CheckColoring(const IndexType *A_offsets, const IndexType *A_cols, const IndexType *colors, const int coloring_level) : _A_offsets(A_offsets), _A_cols(A_cols), _colors(colors), _coloring_level(coloring_level) { }\n\n    __host__ __device__ IndexType operator()(IndexType i)\n    {\n        IndexType col = _colors[i];\n        // do not count uncolored nodes\n\n        if (col == 0)\n        {\n            return 0;\n        }\n\n        IndexType row_start = _A_offsets[i];\n        IndexType row_end = _A_offsets[i + 1];\n\n        if (_coloring_level == 4)\n        {\n            return neighbor_color_validity_checker<4>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 5)\n        {\n            return neighbor_color_validity_checker<5>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 6)\n        {\n            return neighbor_color_validity_checker<6>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else if (_coloring_level == 7)\n        {\n            return neighbor_color_validity_checker<7>::check(_A_offsets, _A_cols, _colors, row_start, row_end, col, i) == 0;\n        }\n        else\n        {\n            for (IndexType r = row_start; r < row_end; r++)\n            {\n                IndexType j  = _A_cols[r];\n\n                // skip diagonal\n                if (j == i) { continue; }\n\n                IndexType jcol = _colors[j];\n\n                if (jcol == 0) { continue; }\n\n                // if 2 colors are adjacent, return 1\n                if (jcol == col)\n                {\n                    //printf(\"invalid coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j,col,jcol);\n                    return 1;\n                }\n\n                if (_coloring_level >= 2)\n                {\n                    IndexType row_start = _A_offsets[j];\n                    IndexType row_end = _A_offsets[j + 1];\n\n                    for (IndexType r = row_start; r < row_end; r++)\n                    {\n                        IndexType k  = _A_cols[r];\n\n                        // skip diagonal\n                        if (k == i) { continue; }\n\n                        IndexType jcol = _colors[k];\n\n                        if (jcol == 0) { continue; }\n\n                        // if 2 colors are adjacent, return 1\n                        if (jcol == col)\n                        {\n                            //printf(\"invalid 2 coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j2,col,jcol);\n                            return 1;\n                        }\n\n                        if (_coloring_level >= 3)\n                        {\n                            IndexType row_start = _A_offsets[k];\n                            IndexType row_end = _A_offsets[k + 1];\n\n                            for (IndexType r = row_start; r < row_end; r++)\n                            {\n                                IndexType l  = _A_cols[r];\n\n                                // skip diagonal\n                                if (l == i) { continue; }\n\n                                IndexType jcol = _colors[l];\n\n                                if (jcol == 0) { continue; }\n\n                                // if 2 colors are adjacent, return 1\n                                if (jcol == col)\n                                {\n                                    //printf(\"invalid 2 coloring, row=%d, col=%d, row_color=%d, col_color=%d\\n\",i,j2,col,jcol);\n                                    return 1;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        } // loop over first ring neighbours\n\n        // no conflict => return 0\n        return 0;\n    }\n};\n\nstatic std::map<std::string, double> total_runtime; //for global aggregate scoring across tests\nstatic std::map<std::string, double> coloring_score;\nstatic std::map<std::string, double> coloring_runs;\n\nstatic FILE *f_csv_file = 0; //once per test rewrite\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\nDECLARE_UNITTEST_BEGIN(MatrixColoringTest_Base);\n\nstd::string base_keywords()\n{\n    return \"coloring\";\n}\n\n// dense histogram using binary search\ntemplate <typename Vector1,\n          typename Vector2>\nvoid color_histogram(const Vector1 &row_colors, Vector2 &histogram)\n{\n    typedef typename Vector1::value_type ValueType; // input value type\n    typedef typename Vector2::value_type IndexType; // histogram index type\n    // copy input data (could be skipped if input is allowed to be modified)\n    device_vector_alloc<ValueType> data(row_colors);\n    // sort data to bring equal elements together\n    thrust_wrapper::sort<AMGX_device>(data.begin(), data.end());\n    // number of histogram bins is equal to the maximum value plus one\n    IndexType num_bins = data.back() + 1;\n    // resize histogram storage\n    histogram.resize(num_bins);\n    // find the end of each bin of values\n    amgx::thrust::counting_iterator<IndexType> search_begin(0);\n    amgx::thrust::upper_bound(data.begin(), data.end(),\n                        search_begin, search_begin + num_bins,\n                        histogram.begin());\n    // compute the histogram by taking differences of the cumulative histogram\n    amgx::thrust::adjacent_difference(histogram.begin(), histogram.end(),\n                                histogram.begin());\n}\n\n\n\n\ntemplate< AMGX_VecPrecision VecPrecision, AMGX_MatPrecision MatPrecision >\nvoid\ncolor_matrix_file(const std::string &filename, int max_coloring_level = 3)\n{\n    AMG_Config cfg;\n    size_t pool_size = cfg.getParameter<size_t>(\"device_mem_pool_size\", \"default\");\n    size_t max_alloc_size = cfg.getParameter<size_t>(\"device_mem_pool_max_alloc_size\", \"default\");\n\n    if ( memory::hasPinnedMemoryPool() )\n    {\n        memory::setPinnedMemoryPool( new memory::PinnedMemoryPool() );\n    }\n\n    if ( memory::hasDeviceMemoryPool() )\n    {\n        memory::setDeviceMemoryPool( new memory::DeviceMemoryPool(pool_size, max_alloc_size, 0) );\n    }\n\n    hipDeviceSynchronize(); // why ????\n    cudaCheckError();\n    bool verbose_output = 0;\n    const std::string cfg_scope = \"default\";\n    std::stringstream config_string_base;\n    config_string_base <<  \"max_uncolored_percentage=0, coloring_level=1\";\n    std::vector<std::string> coloring_schemes;\n#define ALL 1\n#if ALL\n    coloring_schemes.push_back(\"GREEDY_RECOLOR\");\n    coloring_schemes.push_back(\"MIN_MAX\");\n    coloring_schemes.push_back(\"MIN_MAX_2RING\");\n    coloring_schemes.push_back(\"PARALLEL_GREEDY\");\n    coloring_schemes.push_back(\"MULTI_HASH,max_uncolored_percentage=0.0\");\n    coloring_schemes.push_back(\"GREEDY_RECOLOR,coloring_custom_arg=FIRSTSTEP\");\n    coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING\");\n    //coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_custom_arg=IDO\");\n    coloring_schemes.push_back(\"SERIAL_GREEDY_BFS\");\n#else\n    //max_coloring_level = 2;\n#endif\n\n    if (max_coloring_level >= 2)\n    {\n#if 1\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"MIN_MAX, coloring_level=2\");\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=2\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2\");\n        //coloring_schemes.push_back(\"GREEDY_RECOLOR, coloring_level=2\");\n        //coloring_schemes.push_back(\"GREEDY_RECOLOR, coloring_level=2,coloring_custom_arg=FIRSTSTEP\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=FULL64\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=FULL256\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=CN1\");\n        //coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2, coloring_custom_arg=CN2\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=2\");\n        /*coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_custom_arg=IDO, coloring_level=2\");\n        */\n#else\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=2\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=2\");\n#endif\n    }\n\n    if (max_coloring_level >= 3)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=3\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=3\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=3\");\n    }\n\n    if (max_coloring_level >= 4)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=4\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=4\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=4\");\n    }\n\n    if (max_coloring_level >= 5)\n    {\n        coloring_schemes.push_back(\"\"); //placeholder, newline in output if enabled\n        coloring_schemes.push_back(\"PARALLEL_GREEDY, coloring_level=5\");\n        coloring_schemes.push_back(\"GREEDY_MIN_MAX_2RING, coloring_level=5\");\n        coloring_schemes.push_back(\"SERIAL_GREEDY_BFS, coloring_level=5\");\n    }\n\n    if (verbose_output) //enable for writing a csv with all the info\n    {\n        if (!f_csv_file)\n        {\n            f_csv_file = fopen(\"matrix_coloring_test_out.txt\", \"w\");\n        }\n        else\n        {\n            f_csv_file = fopen(\"matrix_coloring_test_out.txt\", \"a\");\n        }\n    }\n\n    if (verbose_output) { std::cout << std::endl; }\n\n    std::vector<std::string> text_outputs(coloring_schemes.size());\n    int min_time_i = -1;\n    float min_time = 0;\n    int min_colors_i = -1;\n    int min_colors = 0;\n    int min_colors_iX = -1;\n    int min_colorsX = 0;\n    typedef TemplateConfig<AMGX_host,   VecPrecision, MatPrecision, AMGX_indInt> Config_h;\n    typedef TemplateConfig<AMGX_device, VecPrecision, MatPrecision, AMGX_indInt> Config_d;\n    typedef Matrix<Config_h> Matrix_h;\n    typedef Vector<Config_h> Vector_h;\n    typedef Matrix<Config_d> Matrix_d;\n    typedef Vector<Config_d> Vector_d;\n    Matrix_h A_h;\n    Vector_h x_h, b_h;\n    A_h.set_initialized(0);\n    A_h.addProps(CSR);\n    std::string fail_msg = \"Cannot open \" + filename;\n    this->PrintOnFail(fail_msg.c_str());\n    //UNITTEST_ASSERT_TRUE(this->read_system( filename.c_str(), A_h, b_h, x_h ));\n    UNITTEST_ASSERT_TRUE(MatrixIO<Config_h>::readSystem( filename.c_str(), A_h, b_h, x_h ) == AMGX_OK);;\n    A_h.set_initialized(1);\n    MatrixAnalysis<TConfig_h> ana(&A_h, &b_h);\n    bool str_sym, sym;\n    bool verbose = false;\n    ana.checkSymmetry(str_sym, sym, verbose);\n    Matrix_d A_d( A_h );\n\n    for (int i = 0; i < coloring_schemes.size(); i++)\n    {\n        if (coloring_schemes[i] == \"\")\n        {\n            if (verbose_output) { text_outputs[i] = \"\\n\"; }\n\n            if (verbose_output) { std::cout << \"\\n\"; }\n\n            min_colorsX = 0;\n            min_colors_iX = -1;\n            continue;\n        }\n\n        std::stringstream config_string;\n        std::stringstream text_output;\n        config_string << config_string_base.str() << \", matrix_coloring_scheme=\" << coloring_schemes[i] << std::endl;\n        AMG_Config  cfg;\n        UNITTEST_ASSERT_TRUE ( cfg.parseParameterString(config_string.str().c_str()) == AMGX_OK);\n        UNITTEST_ASSERT_TRUE_DESC(\"Matrix does not have structural symmetry, unit test invalid\", str_sym);;\n        A_d.set_initialized(0);\n        A_d.addProps(CSR);\n//      cudaDeviceSynchronize();\ncudaCheckError();\n        hipEvent_t color_start, color_stop;\n        hipEventCreate(&color_start);\n        cudaCheckError();\n        hipEventCreate(&color_stop);\n        cudaCheckError();\n        //A_d.colorMatrix(cfg,cfg_scope);\n        //A_d.set_initialized(0);\n        float elapsed_time;\n        hipEventRecord( color_start);\n        cudaCheckError();\n        A_d.colorMatrix(cfg, cfg_scope);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"colors\",  (void*)A_d.getMatrixColoring().getRowColors().raw(), A_d.getMatrixColoring().getRowColors().size()*4);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"cols\",  (void*)A_d.col_indices.raw(), A_d.col_indices.size()*4);\n        //amgx::testing_tools::hash_path_determinism_checker::singleton()->checkpoint(\"rows\",  (void*)A_d.row_offsets.raw(), A_d.row_offsets.size()*4);\n        hipEventRecord( color_stop);\n        cudaCheckError();\n        hipEventSynchronize( color_stop);\n        cudaCheckError();\n        hipEventElapsedTime( &elapsed_time, color_start, color_stop);\n        elapsed_time *= 1e-3f;\n        A_d.set_initialized(1);\n        // check coloring\n        const IndexType *A_row_offsets_ptr    = A_d.row_offsets.raw();\n        const IndexType *A_column_indices_ptr = A_d.col_indices.raw();\n        const IndexType *row_colors_ptr = A_d.getMatrixColoring().getRowColors().raw();\n        IndexType num_bad, num_bad_plus;\n        {\n            //order +1\n            CheckColoring<IndexType> checker(A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, 1 + A_d.getMatrixColoring().getColoringLevel());\n            num_bad_plus =\n                thrust_wrapper::transform_reduce<AMGX_device>(amgx::thrust::counting_iterator<IndexType>(0), amgx::thrust::counting_iterator<IndexType>(A_d.get_num_rows()),\n                                         checker, (IndexType)0, amgx::thrust::plus<IndexType>());\n        }\n        {\n            CheckColoring<IndexType> checker(A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, A_d.getMatrixColoring().getColoringLevel());\n            num_bad =\n                thrust_wrapper::transform_reduce<AMGX_device>(amgx::thrust::counting_iterator<IndexType>(0), amgx::thrust::counting_iterator<IndexType>(A_d.get_num_rows()),\n                                         checker, (IndexType)0, amgx::thrust::plus<IndexType>());\n        }\n        int num_uncolored = (int) amgx::thrust::count_if( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), is_zero() );\n        int mincol        = (int) amgx::thrust::reduce( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), 0, amgx::thrust::minimum<int>() );\n        int maxcol        = (int) amgx::thrust::reduce( A_d.getMatrixColoring().getRowColors().begin(), A_d.getMatrixColoring().getRowColors().begin() + A_d.get_num_rows(), 0, amgx::thrust::maximum<int>() );\n        std::stringstream error_str;\n        error_str << \"Coloring scheme \" << coloring_schemes[i] << \" with \" << num_bad << \" invalid rows, \" << num_uncolored << \"(\" << mincol << \",\" << maxcol << \")\" << \" uncolored rows and \" << A_d.getMatrixColoring().getNumColors()  << \" used colors and bonus = \" << num_bad_plus;\n\n        if (verbose_output)\n        {\n            text_output << std::setw(28) << coloring_schemes[i] << \" : num_colors=\" << std::setw(6) << A_d.getMatrixColoring().getNumColors() << \" , runtime= \" << std::setw(8) << std::fixed << std::setprecision(5) << elapsed_time << \" mM(\" << mincol << \",\" << maxcol << \")\"; // << std::endl;;\n        }\n\n        if (num_bad == 0 && num_uncolored == 0)\n        {\n            if (elapsed_time < min_time || min_time_i == -1)\n            {\n                min_time_i = i;\n                min_time = elapsed_time;\n            }\n\n            if (A_d.getMatrixColoring().getNumColors() < min_colors || min_colors_i == -1)\n            {\n                min_colors_i = i;\n                min_colors = A_d.getMatrixColoring().getNumColors();\n            }\n\n            if (A_d.getMatrixColoring().getNumColors() < min_colorsX || min_colors_iX == -1)\n            {\n                min_colors_iX = i;\n                min_colorsX = A_d.getMatrixColoring().getNumColors();\n            }\n        }\n\n        int degree;\n        {\n            device_vector_alloc<int> hist;\n            amgx::thrust::host_vector<int> h_hist;\n            device_vector_alloc<int> columns(A_d.get_num_rows());\n            amgx::thrust::adjacent_difference(A_d.row_offsets.begin(), A_d.row_offsets.end(), columns.begin());\n            degree = amgx::thrust::reduce(columns.begin(), columns.end(), -1, amgx::thrust::maximum<int>());\n            color_histogram(A_d.getMatrixColoring().getRowColors(), hist);\n            h_hist = hist;\n            float total = 0, m = h_hist[1], M = h_hist[1]; //0 is 0\n\n            for (int j = 1; j < h_hist.size(); j++)\n            {\n                if (h_hist[j] > M) { M = h_hist[j]; }\n\n                if (h_hist[j] < m) { m = h_hist[j]; }\n\n                total += h_hist[j];\n            }\n\n            float avg = total / (h_hist.size() - 1);\n            float rms = 0;\n\n            for (int j = 0; j < h_hist.size(); j++)\n            {\n                rms = (h_hist[j] - avg) * (h_hist[j] - avg);\n            }\n\n            rms = sqrt(rms / (h_hist.size() - 1));\n\n            if (verbose_output) { text_output << \" [\" << \" min=\" << m << \", max=\" << M << \", avg=\" << avg << \", rms=\" << rms << \", deg=\" << degree << \", uncol=\" << num_uncolored << \" bonus=\" << num_bad_plus << \"]\"; }\n\n            if (f_csv_file) { fprintf(f_csv_file, \"%s;%d;%d;%d;%.3f;%.3f;%.3f;%.3f;%.3f\\n\", coloring_schemes[i].c_str(), A_d.getMatrixColoring().getNumColors(), num_bad, num_uncolored, elapsed_time * 1e3, m, M, avg, rms); }\n        }\n        total_runtime[coloring_schemes[i]]  += elapsed_time;\n        coloring_score[coloring_schemes[i]] += double(A_d.getMatrixColoring().getNumColors() - 1) / pow(static_cast<double>(degree), static_cast<double>(A_d.getMatrixColoring().getColoringLevel()));\n        coloring_runs[coloring_schemes[i]]  += 1.0;\n\n        if (verbose_output) { text_output << std::endl; }\n\n        if (verbose_output) { printf(\"%s\", text_output.str().c_str()); }\n\n        text_outputs[i] = text_output.str();\n        UNITTEST_ASSERT_EQUAL_DESC(error_str.str().c_str(), num_bad, 0);\n    }\n\n    if (verbose_output && 0)\n    {\n        for (int i = 0; i < text_outputs.size(); i++)\n        {\n            if (i == min_colors_i) { std::cout << \"*\"; }\n\n            if (i == min_time_i) { std::cout << \"#\"; }\n\n            std::cout << text_outputs[i];\n        }\n    }\n\n    if (f_csv_file)\n    {\n        fprintf(f_csv_file, \"\\n\");\n        fclose(f_csv_file);\n    }\n\n    if (verbose_output)\n    {\n        printf(\"\\n\\n\\n\");\n\n        for (int i = 0; i < text_outputs.size(); i++)\n        {\n            if (coloring_schemes[i] == \"\")\n            {\n                std::cout << \"\\n\";\n                continue;\n            }\n\n            std::string name = coloring_schemes[i];\n            std::cout << std::setw(80) << coloring_schemes[i];\n            std::cout << \" runtime=\" << std::setw(8) << std::fixed << std::setprecision(5) << total_runtime[name];\n            std::cout << \" inefficiency=\" << std::setw(6) << std::fixed << std::setprecision(3) << coloring_score[name] / coloring_runs[name]; // << std::endl;;\n            std::cout << \"\\n\";\n        }\n\n        printf(\"\\n\\n\\n\");\n    }\n\n    hipDeviceSynchronize();\n    cudaCheckError();\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTest_Base);\n\n\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestatmosmodj, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Public/Florida/atmosmodj.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestatmosmodj)\nMatrixColoringTestatmosmodj<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestatmosmodj_dDDI;\n\n\n//--------------------------------------------------------------------------------------------------\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestatmosmodl, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Public/Florida/atmosmodl.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestatmosmodl)\nMatrixColoringTestatmosmodl<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestatmosmodl_dDDI;\n\n\n//--------------------------------------------------------------------------------------------------\nDECLARE_UNITTEST_BEGIN_EXTD(MatrixColoringTestpoisson, MatrixColoringTest_Base<T_Config>);\n\nvoid run()\n{\n    //std::cout << std::endl;\n    MatrixColoringTest_Base<T_Config>::template color_matrix_file<T_Config::vecPrec, T_Config::matPrec>( UnitTest::get_configuration().data_folder + \"Internal/poisson/poisson27x50x50x50.mtx.bin\");\n}\n\nDECLARE_UNITTEST_END(MatrixColoringTestpisson)\nMatrixColoringTestpoisson<TemplateMode<AMGX_mode_dDDI>::Type> MatrixColoringTestpoisson_dDDI;\n\n} //namespace amgx\n", "source": "/kaggle/working/amgx/src/tests/matrix_coloring_test.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"unit_test.h\"\n#include \"amg_solver.h\"\n#include \"aggregation/coarseAgenerators/low_deg_coarse_A_generator.h\"\n#include <matrix_io.h>\n#include <basic_types.h>\n#include \"test_utils.h\"\n#include \"util.h\"\n#include \"time.h\"\n#include <cfloat>\n#include <thrust/sequence.h>\n\nnamespace amgx\n{\n\n\nstruct Not_empty_row\n{\n    typedef bool result_type;\n    template< typename Tuple >\n    inline __device__ __host__ bool operator()( const Tuple &t ) const\n    {\n        return amgx::thrust::get<0>(t) != amgx::thrust::get<1>(t);\n    }\n};\n\ntemplate< typename Matrix, typename Vector >\nstatic\nvoid build_sort_permutation( const Matrix &M, Vector &permutation )\n{\n    int num_nz = M.get_num_nz();\n    Vector row_indices( num_nz, 0 );\n    amgx::thrust::scatter_if( amgx::thrust::counting_iterator<int>(0),\n                        amgx::thrust::counting_iterator<int>(M.row_offsets.size() - 1),\n                        M.row_offsets.begin(),\n                        amgx::thrust::make_transform_iterator(\n                            amgx::thrust::make_zip_iterator( amgx::thrust::make_tuple( M.row_offsets.begin(), M.row_offsets.begin() + 1 ) ),\n                            Not_empty_row()),\n                        row_indices.begin());\n    cudaCheckError();\n    amgx::thrust::inclusive_scan( row_indices.begin(), row_indices.begin() + M.get_num_nz(), row_indices.begin(), amgx::thrust::maximum<int>() );\n    cudaCheckError();\n    permutation.resize( num_nz );\n    thrust_wrapper::sequence<Vector::TConfig::memSpace>( permutation.begin(), permutation.end() );\n    cudaCheckError();\n    Vector tmp( M.col_indices );\n    amgx::thrust::stable_sort_by_key( tmp.begin(), tmp.end(), permutation.begin() );\n    cudaCheckError();\n    tmp = row_indices;\n    amgx::thrust::gather( permutation.begin(), permutation.end(), tmp.begin(), row_indices.begin() );\n    cudaCheckError();\n    amgx::thrust::stable_sort_by_key( row_indices.begin(), row_indices.end(), permutation.begin() );\n    cudaCheckError();\n}\n\ntemplate< typename Value_type >\nstatic __device__ __inline__\nbool equal( Value_type x, Value_type y, Value_type epsilon, Value_type max_abs_error )\n{\n    if ( x == y )\n    {\n        return true;\n    }\n\n    if ( abs(x - y) < max_abs_error )\n    {\n        return true;\n    }\n\n    if ( abs(x - y) <= epsilon )\n    {\n        return true;\n    }\n\n    return false;\n}\n\nstatic __device__ __inline__\nbool equal( float x, float y )\n{\n    return equal( x, y, 1.0e-6f, 1.0e10f * FLT_MIN );\n}\n\nstatic __device__ __inline__\nbool equal( double x, double y )\n{\n    return equal( x, y, 1.0e-12, 1.0e10 * DBL_MIN );\n}\n\ntemplate< typename Value_type >\n__global__\nvoid compare_matrices_kernel( const int  num_rows,\n                              const int  block_size_sq,\n                              const int  has_diag,\n                              const int *A_rows,\n                              const int *A_cols,\n                              const int *A_diag,\n                              const Value_type *A_vals,\n                              const int *A_perm,\n                              const int *B_rows,\n                              const int *B_cols,\n                              const int *B_diag,\n                              const Value_type *B_vals,\n                              const int *B_perm,\n                              int *ok )\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += gridDim.x * blockDim.x )\n    {\n        if ( has_diag )\n        {\n            int a_it = A_diag[row_id];\n            int b_it = B_diag[row_id];\n            Value_type a_val = A_vals[a_it];\n            Value_type b_val = A_vals[b_it];\n\n            if ( !equal( a_val, b_val ) )\n            {\n                printf( \"ERROR: row=%d: Diag A[%d]=%f and B[%d]=%f are different!!!\\n\", row_id, a_it, a_val, b_it, b_val );\n                ok[0] = 0;\n                return;\n            }\n        }\n\n        int row_it  = A_rows[row_id + 0];\n        int row_end = A_rows[row_id + 1];\n\n        if ( row_it != B_rows[row_id + 0] || row_end != B_rows[row_id + 1] )\n        {\n            printf( \"ERROR: Rows A[%d] and B[%d] have different lenghts!!!\\n\", row_id, row_id );\n            ok[0] = 0;\n            return;\n        }\n\n        for ( ; row_it < row_end ; ++row_it )\n        {\n            const int a_it = A_perm[row_it];\n            const int b_it = B_perm[row_it];\n            const int a_col = A_cols[a_it];\n            const int b_col = B_cols[b_it];\n\n            if ( a_col != b_col )\n            {\n                printf( \"ERROR: row=%d row_it=%d: Cols A[%d]=%d and B[%d]=%d are different!!!\\n\", row_id, row_it, a_it, a_col, b_it, b_col );\n                ok[0] = 0;\n                return;\n            }\n\n            for ( int k = 0 ; k < block_size_sq ; ++k )\n            {\n                Value_type a_val = A_vals[block_size_sq * a_it + k];\n                Value_type b_val = B_vals[block_size_sq * b_it + k];\n\n                if ( !equal( a_val, b_val ) )\n                {\n                    printf( \"ERROR: row=%d row_it=%d: Vals A[%d]=%f and B[%d]=%f are different!!!\\n\", row_id, row_it, a_it, a_val, b_it, b_val );\n                    ok[0] = 0;\n                    return;\n                }\n            }\n        }\n    }\n}\n\n\n\n\n\n\n// parameter is used as test name\nDECLARE_UNITTEST_BEGIN(AggregatesCoarseGeneratorTest);\n\n// setup restriction on HOST\nvoid fillRowOffsetsAndColIndices(const int num_aggregates,\n                                 typename Matrix_h::IVector aggregates,\n                                 const int R_num_cols,\n                                 typename Matrix_h::IVector &R_row_offsets,\n                                 typename Matrix_h::IVector &R_col_indices)\n{\n    for (int i = 0; i < num_aggregates + 1; i++)\n    {\n        R_row_offsets[i] = 0;\n    }\n\n    // Count number of neighbors for each row\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = aggregates[i];\n        R_row_offsets[I]++;\n    } \n\n    R_row_offsets[num_aggregates] = R_num_cols;\n\n    for (int i = num_aggregates - 1; i >= 0; i--)\n    {\n        R_row_offsets[i] = R_row_offsets[i + 1] - R_row_offsets[i];\n    }\n\n    /* Set column indices. */\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = aggregates[i];\n        int Ip = R_row_offsets[I]++;\n        R_col_indices[Ip] = i;\n    }\n\n    /* Reset r[i] to start of row memory. */\n    for (int i = num_aggregates - 1; i > 0; i--)\n    {\n        R_row_offsets[i] = R_row_offsets[i - 1];\n    }\n\n    R_row_offsets[0] = 0;\n}\n\ntemplate< typename Matrix_type >\nvoid compare_matrices( const Matrix_type &A, const Matrix_type &B )\n{\n    typedef typename Matrix_type::IVector IVector;\n    IVector A_perm;\n    build_sort_permutation( A, A_perm );\n    IVector B_perm;\n    build_sort_permutation( B, B_perm );\n    IVector ok( 1, 1 );\n    compare_matrices_kernel <<< 2048, 256>>>( A.get_num_rows(),\n            A.get_block_dimx() * A.get_block_dimy(),\n            A.hasProps(DIAG),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            A_perm.raw(),\n            B.row_offsets.raw(),\n            B.col_indices.raw(),\n            B.diag.raw(),\n            B.values.raw(),\n            B_perm.raw(),\n            ok.raw() );\n    cudaCheckError();\n    UNITTEST_ASSERT_TRUE( ok[0] == 1 );\n}\n\nvoid run()\n{\n    cudaCheckError();\n    this->randomize( 375139 );\n    AMG_Config cfg;\n    const std::string &cfg_scope = \"default\";\n    // setup generators\n    cudaCheckError();\n    aggregation::CoarseAGenerator<TConfig> *d_generator = new aggregation::LowDegCoarseAGenerator<TConfig>(cfg, cfg_scope);\n    this->PrintOnFail(\"Device generator creation\");\n    UNITTEST_ASSERT_TRUE(d_generator != NULL);\n    cudaCheckError();\n    aggregation::CoarseAGenerator<TConfig_h> *h_generator = new aggregation::LowDegCoarseAGenerator<TConfig_h>(cfg, cfg_scope);\n    this->PrintOnFail(\"Host generator creation\");\n    UNITTEST_ASSERT_TRUE(h_generator != NULL);\n    cudaCheckError();\n    MatrixA A, Ac;\n    int bsizes[] = {1, 2, 3, 4, 5, 8, 10};\n\n    for (int diag_prop = 0; diag_prop < 2; diag_prop++)\n    {\n        for (int bs = 0; bs < sizeof(bsizes)/sizeof(bsizes[0]); bs++)\n        {\n            int b = bsizes[bs];\n            // setup matrix A\n            cudaCheckError();\n            generateMatrixRandomStruct<TConfig>::generate(A, 128, diag_prop, b, false);\n            cudaCheckError();\n            random_fill(A);\n            cudaCheckError();\n            Matrix_h h_A = A;\n            cudaCheckError();\n            Matrix_h h_Ac;\n            cudaCheckError();\n            // setup aggregates on CPU\n            int num_aggregates = (A.get_num_rows() - 1) / 2 + 1; //A.get_num_rows();\n            typename Matrix_h::IVector h_aggregates;\n            h_aggregates.resize( A.get_num_rows() );\n\n            for ( int i = 0; i < h_aggregates.size(); i++ )\n            {\n                h_aggregates[i] = i / 2;    //i;\n            }\n\n            // setup R matrix on CPU\n            typename Matrix_h::IVector h_R_row_offsets;\n            typename Matrix_h::IVector h_R_col_indices;\n            h_R_row_offsets.resize( num_aggregates + 1 );\n            h_R_col_indices.resize( A.get_num_rows() );\n            fillRowOffsetsAndColIndices( num_aggregates, h_aggregates, A.get_num_rows(), h_R_row_offsets, h_R_col_indices );\n            // assign GPU vectors\n            IVector aggregates = h_aggregates;\n            IVector R_row_offsets = h_R_row_offsets;\n            IVector R_col_indices = h_R_col_indices;\n            // compute Galerkin product on CPU and GPU\n            h_generator->computeAOperator(h_A, h_Ac, h_aggregates, h_R_row_offsets, h_R_col_indices, num_aggregates);\n            d_generator->computeAOperator(A, Ac, aggregates, R_row_offsets, R_col_indices, num_aggregates);\n            // simple check on matrix size\n            this->PrintOnFail(\"Coarse matrix has wrong size %i != num aggregates %i\", Ac.get_num_rows(), num_aggregates);\n            UNITTEST_ASSERT_TRUE( Ac.get_num_rows() == num_aggregates );\n            // dump matrix to file\n            VVector v;\n            v.resize(Ac.get_num_rows() * Ac.get_block_dimy());\n            random_fill(v);\n            // compare structure\n            this->PrintOnFail(\"Coarse matrix has incorrect structure, diag prop %i, block size %i, num rows %i, num aggregates %i\", diag_prop, b, A.get_num_rows(), num_aggregates);\n            compare_matrices(Ac, MatrixA(h_Ac) /*, b==1 && !diag_prop */ );\n        }\n    }\n\n    delete d_generator;\n    delete h_generator;\n}\n\nDECLARE_UNITTEST_END(AggregatesCoarseGeneratorTest);\n\n// run for all device configs\n#define AMGX_CASE_LINE(CASE) AggregatesCoarseGeneratorTest<TemplateMode<CASE>::Type> AggregatesCoarseGeneratorTest_##CASE;\nAMGX_FORALL_BUILDS_DEVICE(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n//AggregatesCoarseGeneratorTest<TemplateMode<AMGX_mode_dDDI>::Type> AggregatesCoarseGeneratorTest_dDDI;\n\n\n} //namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include \"unit_test.h\"\n#include \"amg_solver.h\"\n#include \"aggregation/coarseAgenerators/low_deg_coarse_A_generator.h\"\n#include <matrix_io.h>\n#include <basic_types.h>\n#include \"test_utils.h\"\n#include \"util.h\"\n#include \"time.h\"\n#include <cfloat>\n#include <thrust/sequence.h>\n\nnamespace amgx\n{\n\n\nstruct Not_empty_row\n{\n    typedef bool result_type;\n    template< typename Tuple >\n    inline __device__ __host__ bool operator()( const Tuple &t ) const\n    {\n        return amgx::thrust::get<0>(t) != amgx::thrust::get<1>(t);\n    }\n};\n\ntemplate< typename Matrix, typename Vector >\nstatic\nvoid build_sort_permutation( const Matrix &M, Vector &permutation )\n{\n    int num_nz = M.get_num_nz();\n    Vector row_indices( num_nz, 0 );\n    amgx::thrust::scatter_if( amgx::thrust::counting_iterator<int>(0),\n                        amgx::thrust::counting_iterator<int>(M.row_offsets.size() - 1),\n                        M.row_offsets.begin(),\n                        amgx::thrust::make_transform_iterator(\n                            amgx::thrust::make_zip_iterator( amgx::thrust::make_tuple( M.row_offsets.begin(), M.row_offsets.begin() + 1 ) ),\n                            Not_empty_row()),\n                        row_indices.begin());\n    cudaCheckError();\n    amgx::thrust::inclusive_scan( row_indices.begin(), row_indices.begin() + M.get_num_nz(), row_indices.begin(), amgx::thrust::maximum<int>() );\n    cudaCheckError();\n    permutation.resize( num_nz );\n    thrust_wrapper::sequence<Vector::TConfig::memSpace>( permutation.begin(), permutation.end() );\n    cudaCheckError();\n    Vector tmp( M.col_indices );\n    amgx::thrust::stable_sort_by_key( tmp.begin(), tmp.end(), permutation.begin() );\n    cudaCheckError();\n    tmp = row_indices;\n    amgx::thrust::gather( permutation.begin(), permutation.end(), tmp.begin(), row_indices.begin() );\n    cudaCheckError();\n    amgx::thrust::stable_sort_by_key( row_indices.begin(), row_indices.end(), permutation.begin() );\n    cudaCheckError();\n}\n\ntemplate< typename Value_type >\nstatic __device__ __inline__\nbool equal( Value_type x, Value_type y, Value_type epsilon, Value_type max_abs_error )\n{\n    if ( x == y )\n    {\n        return true;\n    }\n\n    if ( abs(x - y) < max_abs_error )\n    {\n        return true;\n    }\n\n    if ( abs(x - y) <= epsilon )\n    {\n        return true;\n    }\n\n    return false;\n}\n\nstatic __device__ __inline__\nbool equal( float x, float y )\n{\n    return equal( x, y, 1.0e-6f, 1.0e10f * FLT_MIN );\n}\n\nstatic __device__ __inline__\nbool equal( double x, double y )\n{\n    return equal( x, y, 1.0e-12, 1.0e10 * DBL_MIN );\n}\n\ntemplate< typename Value_type >\n__global__\nvoid compare_matrices_kernel( const int  num_rows,\n                              const int  block_size_sq,\n                              const int  has_diag,\n                              const int *A_rows,\n                              const int *A_cols,\n                              const int *A_diag,\n                              const Value_type *A_vals,\n                              const int *A_perm,\n                              const int *B_rows,\n                              const int *B_cols,\n                              const int *B_diag,\n                              const Value_type *B_vals,\n                              const int *B_perm,\n                              int *ok )\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += gridDim.x * blockDim.x )\n    {\n        if ( has_diag )\n        {\n            int a_it = A_diag[row_id];\n            int b_it = B_diag[row_id];\n            Value_type a_val = A_vals[a_it];\n            Value_type b_val = A_vals[b_it];\n\n            if ( !equal( a_val, b_val ) )\n            {\n                printf( \"ERROR: row=%d: Diag A[%d]=%f and B[%d]=%f are different!!!\\n\", row_id, a_it, a_val, b_it, b_val );\n                ok[0] = 0;\n                return;\n            }\n        }\n\n        int row_it  = A_rows[row_id + 0];\n        int row_end = A_rows[row_id + 1];\n\n        if ( row_it != B_rows[row_id + 0] || row_end != B_rows[row_id + 1] )\n        {\n            printf( \"ERROR: Rows A[%d] and B[%d] have different lenghts!!!\\n\", row_id, row_id );\n            ok[0] = 0;\n            return;\n        }\n\n        for ( ; row_it < row_end ; ++row_it )\n        {\n            const int a_it = A_perm[row_it];\n            const int b_it = B_perm[row_it];\n            const int a_col = A_cols[a_it];\n            const int b_col = B_cols[b_it];\n\n            if ( a_col != b_col )\n            {\n                printf( \"ERROR: row=%d row_it=%d: Cols A[%d]=%d and B[%d]=%d are different!!!\\n\", row_id, row_it, a_it, a_col, b_it, b_col );\n                ok[0] = 0;\n                return;\n            }\n\n            for ( int k = 0 ; k < block_size_sq ; ++k )\n            {\n                Value_type a_val = A_vals[block_size_sq * a_it + k];\n                Value_type b_val = B_vals[block_size_sq * b_it + k];\n\n                if ( !equal( a_val, b_val ) )\n                {\n                    printf( \"ERROR: row=%d row_it=%d: Vals A[%d]=%f and B[%d]=%f are different!!!\\n\", row_id, row_it, a_it, a_val, b_it, b_val );\n                    ok[0] = 0;\n                    return;\n                }\n            }\n        }\n    }\n}\n\n\n\n\n\n\n// parameter is used as test name\nDECLARE_UNITTEST_BEGIN(AggregatesCoarseGeneratorTest);\n\n// setup restriction on HOST\nvoid fillRowOffsetsAndColIndices(const int num_aggregates,\n                                 typename Matrix_h::IVector aggregates,\n                                 const int R_num_cols,\n                                 typename Matrix_h::IVector &R_row_offsets,\n                                 typename Matrix_h::IVector &R_col_indices)\n{\n    for (int i = 0; i < num_aggregates + 1; i++)\n    {\n        R_row_offsets[i] = 0;\n    }\n\n    // Count number of neighbors for each row\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = aggregates[i];\n        R_row_offsets[I]++;\n    } \n\n    R_row_offsets[num_aggregates] = R_num_cols;\n\n    for (int i = num_aggregates - 1; i >= 0; i--)\n    {\n        R_row_offsets[i] = R_row_offsets[i + 1] - R_row_offsets[i];\n    }\n\n    /* Set column indices. */\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = aggregates[i];\n        int Ip = R_row_offsets[I]++;\n        R_col_indices[Ip] = i;\n    }\n\n    /* Reset r[i] to start of row memory. */\n    for (int i = num_aggregates - 1; i > 0; i--)\n    {\n        R_row_offsets[i] = R_row_offsets[i - 1];\n    }\n\n    R_row_offsets[0] = 0;\n}\n\ntemplate< typename Matrix_type >\nvoid compare_matrices( const Matrix_type &A, const Matrix_type &B )\n{\n    typedef typename Matrix_type::IVector IVector;\n    IVector A_perm;\n    build_sort_permutation( A, A_perm );\n    IVector B_perm;\n    build_sort_permutation( B, B_perm );\n    IVector ok( 1, 1 );\n    compare_matrices_kernel <<< 2048, 256>>>( A.get_num_rows(),\n            A.get_block_dimx() * A.get_block_dimy(),\n            A.hasProps(DIAG),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            A_perm.raw(),\n            B.row_offsets.raw(),\n            B.col_indices.raw(),\n            B.diag.raw(),\n            B.values.raw(),\n            B_perm.raw(),\n            ok.raw() );\n    cudaCheckError();\n    UNITTEST_ASSERT_TRUE( ok[0] == 1 );\n}\n\nvoid run()\n{\n    cudaCheckError();\n    this->randomize( 375139 );\n    AMG_Config cfg;\n    const std::string &cfg_scope = \"default\";\n    // setup generators\n    cudaCheckError();\n    aggregation::CoarseAGenerator<TConfig> *d_generator = new aggregation::LowDegCoarseAGenerator<TConfig>(cfg, cfg_scope);\n    this->PrintOnFail(\"Device generator creation\");\n    UNITTEST_ASSERT_TRUE(d_generator != NULL);\n    cudaCheckError();\n    aggregation::CoarseAGenerator<TConfig_h> *h_generator = new aggregation::LowDegCoarseAGenerator<TConfig_h>(cfg, cfg_scope);\n    this->PrintOnFail(\"Host generator creation\");\n    UNITTEST_ASSERT_TRUE(h_generator != NULL);\n    cudaCheckError();\n    MatrixA A, Ac;\n    int bsizes[] = {1, 2, 3, 4, 5, 8, 10};\n\n    for (int diag_prop = 0; diag_prop < 2; diag_prop++)\n    {\n        for (int bs = 0; bs < sizeof(bsizes)/sizeof(bsizes[0]); bs++)\n        {\n            int b = bsizes[bs];\n            // setup matrix A\n            cudaCheckError();\n            generateMatrixRandomStruct<TConfig>::generate(A, 128, diag_prop, b, false);\n            cudaCheckError();\n            random_fill(A);\n            cudaCheckError();\n            Matrix_h h_A = A;\n            cudaCheckError();\n            Matrix_h h_Ac;\n            cudaCheckError();\n            // setup aggregates on CPU\n            int num_aggregates = (A.get_num_rows() - 1) / 2 + 1; //A.get_num_rows();\n            typename Matrix_h::IVector h_aggregates;\n            h_aggregates.resize( A.get_num_rows() );\n\n            for ( int i = 0; i < h_aggregates.size(); i++ )\n            {\n                h_aggregates[i] = i / 2;    //i;\n            }\n\n            // setup R matrix on CPU\n            typename Matrix_h::IVector h_R_row_offsets;\n            typename Matrix_h::IVector h_R_col_indices;\n            h_R_row_offsets.resize( num_aggregates + 1 );\n            h_R_col_indices.resize( A.get_num_rows() );\n            fillRowOffsetsAndColIndices( num_aggregates, h_aggregates, A.get_num_rows(), h_R_row_offsets, h_R_col_indices );\n            // assign GPU vectors\n            IVector aggregates = h_aggregates;\n            IVector R_row_offsets = h_R_row_offsets;\n            IVector R_col_indices = h_R_col_indices;\n            // compute Galerkin product on CPU and GPU\n            h_generator->computeAOperator(h_A, h_Ac, h_aggregates, h_R_row_offsets, h_R_col_indices, num_aggregates);\n            d_generator->computeAOperator(A, Ac, aggregates, R_row_offsets, R_col_indices, num_aggregates);\n            // simple check on matrix size\n            this->PrintOnFail(\"Coarse matrix has wrong size %i != num aggregates %i\", Ac.get_num_rows(), num_aggregates);\n            UNITTEST_ASSERT_TRUE( Ac.get_num_rows() == num_aggregates );\n            // dump matrix to file\n            VVector v;\n            v.resize(Ac.get_num_rows() * Ac.get_block_dimy());\n            random_fill(v);\n            // compare structure\n            this->PrintOnFail(\"Coarse matrix has incorrect structure, diag prop %i, block size %i, num rows %i, num aggregates %i\", diag_prop, b, A.get_num_rows(), num_aggregates);\n            compare_matrices(Ac, MatrixA(h_Ac) /*, b==1 && !diag_prop */ );\n        }\n    }\n\n    delete d_generator;\n    delete h_generator;\n}\n\nDECLARE_UNITTEST_END(AggregatesCoarseGeneratorTest);\n\n// run for all device configs\n#define AMGX_CASE_LINE(CASE) AggregatesCoarseGeneratorTest<TemplateMode<CASE>::Type> AggregatesCoarseGeneratorTest_##CASE;\nAMGX_FORALL_BUILDS_DEVICE(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n//AggregatesCoarseGeneratorTest<TemplateMode<AMGX_mode_dDDI>::Type> AggregatesCoarseGeneratorTest_dDDI;\n\n\n} //namespace amgx\n", "source": "/kaggle/working/amgx/src/tests/aggregates_coarse_generator.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <eigensolvers/single_iteration_eigensolver.h>\n#include <solvers/solver.h>\n#include <blas.h>\n#include <multiply.h>\n#include <norm.h>\n#include <transpose.h>\n#include <operators/shifted_operator.h>\n#include <operators/solve_operator.h>\n#include <operators/pagerank_operator.h>\n#include <algorithm>\n#define AMGX_EXPLICIT_SHIFT 1\nnamespace amgx\n{\n\ntemplate <typename index_type, typename value_type>\n__global__ void shift_diagonal(index_type num_rows,\n                               const index_type *row_offsets,\n                               const index_type *col_indices,\n                               value_type *values,\n                               value_type shift)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        index_type row_start = row_offsets[r];\n        index_type row_end = row_offsets[r + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            if (col_indices[j] == r)\n            {\n                values[j] += shift;\n                continue;\n            }\n        }\n    }\n}\n// OBSOLETE compute a when the input is H only\ntemplate <typename index_type, typename value_type>\n__global__ void dangling_nodes(index_type num_rows,\n                               const index_type *row_offsets,\n                               value_type *aa)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        index_type row_start = row_offsets[r];\n        index_type row_end = row_offsets[r + 1];\n\n        // NOTE 1 : a = alpha*a + (1-alpha)e\n        // NOTE 2 : a is initialized to (1-alpha)\n        if (row_start == row_end)\n        {\n            aa[r] = 1.0;    // NOTE 3 : alpha*1 + (1-alpha)*1 = 1.0\n        }\n    }\n}\n// used when a is given as input and the matrix is H^T\ntemplate <typename index_type, typename value_type>\n__global__ void update_a(index_type num_rows,\n                         value_type *aa,\n                         value_type beta)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        // NOTE 1 : a = alpha*a + (1-alpha)e\n        if (aa[r] == 0.0)\n        {\n            aa[r] = beta;    // NOTE 2 : alpha*0 + (1-alpha)*1 = (1-alpha)\n        }\n    }\n}\n\ntemplate <class TConfig>\nSingleIteration_EigenSolver<TConfig>::SingleIteration_EigenSolver(AMG_Config &cfg,\n        const std::string &cfg_scope)\n    : Base(cfg, cfg_scope), m_cfg(cfg), m_operator(NULL)\n{\n    m_convergence_check_freq = cfg.getParameter<int>(\"eig_convergence_check_freq\", cfg_scope);\n}\n\ntemplate <class TConfig>\nSingleIteration_EigenSolver<TConfig>::~SingleIteration_EigenSolver()\n{\n    /*if (m_operator)\n        delete m_operator;*/\n    if (this->m_which == EIG_SMALLEST)\n    {\n        delete m_operator;\n    }\n\n    if (this->m_which == EIG_PAGERANK)\n    {\n        delete m_operator;\n    }\n\n    free_allocated();\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::free_allocated()\n{\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::shift_matrix()\n{\n    ValueTypeMat shift = this->m_shift;\n\n    if (shift == 0)\n    {\n        return;\n    }\n\n    Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n    Matrix<TConfig> &A = *pA;\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = A.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    shift_diagonal <<< num_blocks, num_threads>>>(num_rows, A.row_offsets.raw(),\n            A.col_indices.raw(), A.values.raw(), -shift);\n    cudaCheckError();\n}\n// OBSOLETE compute a when the input is H only\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::get_dangling_nodes()\n{\n    Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n    Matrix<TConfig> &A = *pA;\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = A.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    dangling_nodes <<< num_blocks, num_threads>>>(num_rows, A.row_offsets.raw(), m_a.raw());\n    cudaCheckError();\n    // CPU CODE : you shouldn't use it, it is very slow\n    /* for (int i = 0; i < num_rows;  ++i)\n    {\n        if (A.row_offsets[i] == A.row_offsets[i+1])\n            m_a[i] = 1.0; // alpha*1 + (1-alpha)*1 = 1.0\n        else\n            m_a[i] = beta;\n    } */\n}\n\n// 0 are replaced by 1-alpha\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::update_dangling_nodes()\n{\n    Operator<TConfig> &A = *this->m_A;\n    ValueTypeVec beta = 1.0 - this->m_damping_factor;\n    int num_rows = A.get_num_rows();\n\n    // CPU\n    if (TConfig::memSpace == AMGX_host)\n    {\n        for (int i = 0; i < num_rows; i++)\n        {\n            // NOTE 1 : a = alpha*a + (1-alpha)e\n            if (m_a[i] == 0.0)\n            {\n                m_a[i] = beta;    // NOTE 2 : alpha*0 + (1-alpha)*1 = (1-alpha)\n            }\n        }\n    }\n    //GPU\n    else\n    {\n        int num_threads = 128;\n        int max_grid_size = 4096;\n        int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n        update_a <<< num_blocks, num_threads>>>(num_rows, m_a.raw(), beta );\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solver_setup()\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n\n    if (this->m_which == EIG_PAGERANK)\n    {\n        PagerankOperator<TConfig> *op = new PagerankOperator<TConfig>(A, &m_a, &m_b, this->m_damping_factor);\n        m_operator = op;\n    }\n    else if (this->m_which == EIG_SMALLEST)\n    {\n        Solver<TConfig> *solver = SolverFactory<TConfig>::allocate(m_cfg, \"default\", \"solver\");\n#ifdef AMGX_EXPLICIT_SHIFT\n        shift_matrix();\n        SolveOperator<TConfig> *solve_op = new SolveOperator<TConfig>(A, *solver);\n#else\n        ShiftedOperator<TConfig> *op = new ShiftedOperator<TConfig>(A, -this->m_shift);\n        SolveOperator<TConfig> *solve_op = new SolveOperator<TConfig>(*op, *solver);\n#endif\n        solve_op->setup();\n        m_operator = solve_op;\n    }\n    else\n    {\n        m_operator = &A;\n    }\n\n    const int N = static_cast<int>(A.get_num_cols() * A.get_block_dimy());\n    // Allocate two vectors.\n    m_v.resize(N);\n    m_x.resize(N);\n    m_allocated_vectors.push_back(&m_v);\n    m_allocated_vectors.push_back(&m_x);\n\n    // Vectors \"a\" and \"b\" are needed only for PR\n    if (this->m_which == EIG_PAGERANK)\n    {\n        m_a.resize(N);\n        m_b.resize(N);\n        m_allocated_vectors.push_back(&m_a);\n        m_allocated_vectors.push_back(&m_b);\n    }\n\n    int start_tag = 100;\n\n    for (int i = 0; i < m_allocated_vectors.size(); ++i)\n    {\n        VVector *v = m_allocated_vectors[i];\n        v->tag = start_tag + i;\n        v->set_block_dimy(A.get_block_dimy());\n        v->set_block_dimx(1);\n        v->dirtybit = 1;\n        v->delayed_send = 1;\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solver_pagerank_setup(VVector &a)\n{\n    if (this->m_which == EIG_PAGERANK)\n    {\n        Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n        Matrix<TConfig> &A = *pA;\n        ViewType oldView = A.currentView();\n        A.setViewExterior();\n        int offset, size;\n        A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n        copy(a, m_a, offset, size);\n        //Get the number of rows of the matrix\n        int num_rows_loc = A.get_num_rows();\n        int num_rows_glob = num_rows_loc;\n        // MPI?\n#ifdef AMGX_WITH_MPI\n        int mpi_initialized = 0;\n        MPI_Initialized(&mpi_initialized);\n\n        if (mpi_initialized)\n        {\n            if (A.is_matrix_distributed())\n            {\n                A.getManager()->global_reduce_sum(&num_rows_glob);\n            }\n        }\n\n#endif\n        // Requiered to compute b\n        // a = alpha*a + (1-alpha)e\n        update_dangling_nodes();\n        // b is a constant and uniform vector\n        ValueTypeMat tmp = 1.0 / num_rows_glob;\n        fill(m_b, tmp);\n        A.setView(oldView);\n    }\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solve_init(VVector &x)\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    copy(x, m_x, offset, size);\n    std::swap(m_x, m_v);\n    A.setView(oldView);\n}\n\n// One iteration of the single iteration.\n// MATLAB code of the algorithm:\n// while true\n//   V = X / norm(X);\n//   X = linsolve(A, V);\n//   lambda = V' * X;\n//   R = X - lambda * V;\n//   if norm(R) < tol * abs(lambda)\n//     break;\n//   end\n\ntemplate <class TConfig>\nbool SingleIteration_EigenSolver<TConfig>::solve_iteration(VVector &x)\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    // V = X / norm(X)\n    scal(m_v, ValueTypeVec(1) / get_norm(A, m_v, this->m_norm_type), offset, size);\n    // X = linsolve(A, v)\n    m_operator->apply(m_v, m_x);\n\n    if ((this->m_curr_iter % this->m_convergence_check_freq) == 0)\n    {\n        ValueTypeVec lambda;\n\n        if (this->m_which == EIG_PAGERANK)\n        {\n            // The maximum eigenvalue of G is 1.0\n            lambda = 1.0;\n            // v = x - v\n            // axpy(m_x, m_v, ValueTypeVec(-1),offset, size);\n            axpby(m_v, m_x, m_v, ValueTypeVec(-1.0), ValueTypeVec(1), offset, size);\n        }\n        else\n        {\n            // lambda = v.x\n            lambda = dot(A, m_v, m_x);\n            // v = x - lambda * v\n            axpby(m_v, m_x, m_v, -lambda, ValueTypeVec(1), offset, size);\n        }\n\n        ValueTypeVec residual_norm = get_norm(A, m_v, this->m_norm_type);\n        this->m_residuals.push_back(residual_norm / fabs(lambda));\n\n        // Check convergence.\n        if (residual_norm < this->m_tolerance * fabs(lambda))\n        {\n            // Normalize eigenvector.\n            if (this->m_which == EIG_PAGERANK)\n            {\n                //Norm L1 is more fited for the output of PageRank\n                ValueTypeVec norm = get_norm(A, m_x, L1);\n                scal(m_x, ValueTypeVec(1) / norm, offset, size);\n            }\n            else\n            {\n                ValueTypeVec norm = get_norm(A, m_x, this->m_norm_type);\n                scal(m_x, ValueTypeVec(1) / norm, offset, size);\n            }\n\n            // With inverse iteration we need to scale the eigenvector by the inverse of the eigenvalue,\n            // but this doesn't seems to be needed since we already normalized the eigenvector just above.\n            this->m_eigenvectors.push_back(m_x);\n            copy(m_x, x, offset, size);\n            this->m_eigenvalues.push_back(lambda);\n            this->postprocess_eigenpairs();\n            return true;\n        }\n    }\n\n    std::swap(m_x, m_v);\n    A.setView(oldView);\n    return false;\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solve_finalize()\n{\n}\n\n// Explicit template instantiation.\n#define AMGX_CASE_LINE(CASE) template class SingleIteration_EigenSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n};\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <eigensolvers/single_iteration_eigensolver.h>\n#include <solvers/solver.h>\n#include <blas.h>\n#include <multiply.h>\n#include <norm.h>\n#include <transpose.h>\n#include <operators/shifted_operator.h>\n#include <operators/solve_operator.h>\n#include <operators/pagerank_operator.h>\n#include <algorithm>\n#define AMGX_EXPLICIT_SHIFT 1\nnamespace amgx\n{\n\ntemplate <typename index_type, typename value_type>\n__global__ void shift_diagonal(index_type num_rows,\n                               const index_type *row_offsets,\n                               const index_type *col_indices,\n                               value_type *values,\n                               value_type shift)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        index_type row_start = row_offsets[r];\n        index_type row_end = row_offsets[r + 1];\n\n        for (int j = row_start; j < row_end; j++)\n        {\n            if (col_indices[j] == r)\n            {\n                values[j] += shift;\n                continue;\n            }\n        }\n    }\n}\n// OBSOLETE compute a when the input is H only\ntemplate <typename index_type, typename value_type>\n__global__ void dangling_nodes(index_type num_rows,\n                               const index_type *row_offsets,\n                               value_type *aa)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        index_type row_start = row_offsets[r];\n        index_type row_end = row_offsets[r + 1];\n\n        // NOTE 1 : a = alpha*a + (1-alpha)e\n        // NOTE 2 : a is initialized to (1-alpha)\n        if (row_start == row_end)\n        {\n            aa[r] = 1.0;    // NOTE 3 : alpha*1 + (1-alpha)*1 = 1.0\n        }\n    }\n}\n// used when a is given as input and the matrix is H^T\ntemplate <typename index_type, typename value_type>\n__global__ void update_a(index_type num_rows,\n                         value_type *aa,\n                         value_type beta)\n{\n    index_type tidx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int r = tidx; r < num_rows; r += blockDim.x * gridDim.x)\n    {\n        // NOTE 1 : a = alpha*a + (1-alpha)e\n        if (aa[r] == 0.0)\n        {\n            aa[r] = beta;    // NOTE 2 : alpha*0 + (1-alpha)*1 = (1-alpha)\n        }\n    }\n}\n\ntemplate <class TConfig>\nSingleIteration_EigenSolver<TConfig>::SingleIteration_EigenSolver(AMG_Config &cfg,\n        const std::string &cfg_scope)\n    : Base(cfg, cfg_scope), m_cfg(cfg), m_operator(NULL)\n{\n    m_convergence_check_freq = cfg.getParameter<int>(\"eig_convergence_check_freq\", cfg_scope);\n}\n\ntemplate <class TConfig>\nSingleIteration_EigenSolver<TConfig>::~SingleIteration_EigenSolver()\n{\n    /*if (m_operator)\n        delete m_operator;*/\n    if (this->m_which == EIG_SMALLEST)\n    {\n        delete m_operator;\n    }\n\n    if (this->m_which == EIG_PAGERANK)\n    {\n        delete m_operator;\n    }\n\n    free_allocated();\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::free_allocated()\n{\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::shift_matrix()\n{\n    ValueTypeMat shift = this->m_shift;\n\n    if (shift == 0)\n    {\n        return;\n    }\n\n    Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n    Matrix<TConfig> &A = *pA;\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = A.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    shift_diagonal <<< num_blocks, num_threads>>>(num_rows, A.row_offsets.raw(),\n            A.col_indices.raw(), A.values.raw(), -shift);\n    cudaCheckError();\n}\n// OBSOLETE compute a when the input is H only\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::get_dangling_nodes()\n{\n    Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n    Matrix<TConfig> &A = *pA;\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = A.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    dangling_nodes <<< num_blocks, num_threads>>>(num_rows, A.row_offsets.raw(), m_a.raw());\n    cudaCheckError();\n    // CPU CODE : you shouldn't use it, it is very slow\n    /* for (int i = 0; i < num_rows;  ++i)\n    {\n        if (A.row_offsets[i] == A.row_offsets[i+1])\n            m_a[i] = 1.0; // alpha*1 + (1-alpha)*1 = 1.0\n        else\n            m_a[i] = beta;\n    } */\n}\n\n// 0 are replaced by 1-alpha\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::update_dangling_nodes()\n{\n    Operator<TConfig> &A = *this->m_A;\n    ValueTypeVec beta = 1.0 - this->m_damping_factor;\n    int num_rows = A.get_num_rows();\n\n    // CPU\n    if (TConfig::memSpace == AMGX_host)\n    {\n        for (int i = 0; i < num_rows; i++)\n        {\n            // NOTE 1 : a = alpha*a + (1-alpha)e\n            if (m_a[i] == 0.0)\n            {\n                m_a[i] = beta;    // NOTE 2 : alpha*0 + (1-alpha)*1 = (1-alpha)\n            }\n        }\n    }\n    //GPU\n    else\n    {\n        int num_threads = 128;\n        int max_grid_size = 4096;\n        int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n        update_a <<< num_blocks, num_threads>>>(num_rows, m_a.raw(), beta );\n        cudaCheckError();\n    }\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solver_setup()\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n\n    if (this->m_which == EIG_PAGERANK)\n    {\n        PagerankOperator<TConfig> *op = new PagerankOperator<TConfig>(A, &m_a, &m_b, this->m_damping_factor);\n        m_operator = op;\n    }\n    else if (this->m_which == EIG_SMALLEST)\n    {\n        Solver<TConfig> *solver = SolverFactory<TConfig>::allocate(m_cfg, \"default\", \"solver\");\n#ifdef AMGX_EXPLICIT_SHIFT\n        shift_matrix();\n        SolveOperator<TConfig> *solve_op = new SolveOperator<TConfig>(A, *solver);\n#else\n        ShiftedOperator<TConfig> *op = new ShiftedOperator<TConfig>(A, -this->m_shift);\n        SolveOperator<TConfig> *solve_op = new SolveOperator<TConfig>(*op, *solver);\n#endif\n        solve_op->setup();\n        m_operator = solve_op;\n    }\n    else\n    {\n        m_operator = &A;\n    }\n\n    const int N = static_cast<int>(A.get_num_cols() * A.get_block_dimy());\n    // Allocate two vectors.\n    m_v.resize(N);\n    m_x.resize(N);\n    m_allocated_vectors.push_back(&m_v);\n    m_allocated_vectors.push_back(&m_x);\n\n    // Vectors \"a\" and \"b\" are needed only for PR\n    if (this->m_which == EIG_PAGERANK)\n    {\n        m_a.resize(N);\n        m_b.resize(N);\n        m_allocated_vectors.push_back(&m_a);\n        m_allocated_vectors.push_back(&m_b);\n    }\n\n    int start_tag = 100;\n\n    for (int i = 0; i < m_allocated_vectors.size(); ++i)\n    {\n        VVector *v = m_allocated_vectors[i];\n        v->tag = start_tag + i;\n        v->set_block_dimy(A.get_block_dimy());\n        v->set_block_dimx(1);\n        v->dirtybit = 1;\n        v->delayed_send = 1;\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solver_pagerank_setup(VVector &a)\n{\n    if (this->m_which == EIG_PAGERANK)\n    {\n        Matrix<TConfig> *pA = dynamic_cast< Matrix<TConfig>* > (this->m_A);\n        Matrix<TConfig> &A = *pA;\n        ViewType oldView = A.currentView();\n        A.setViewExterior();\n        int offset, size;\n        A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n        copy(a, m_a, offset, size);\n        //Get the number of rows of the matrix\n        int num_rows_loc = A.get_num_rows();\n        int num_rows_glob = num_rows_loc;\n        // MPI?\n#ifdef AMGX_WITH_MPI\n        int mpi_initialized = 0;\n        MPI_Initialized(&mpi_initialized);\n\n        if (mpi_initialized)\n        {\n            if (A.is_matrix_distributed())\n            {\n                A.getManager()->global_reduce_sum(&num_rows_glob);\n            }\n        }\n\n#endif\n        // Requiered to compute b\n        // a = alpha*a + (1-alpha)e\n        update_dangling_nodes();\n        // b is a constant and uniform vector\n        ValueTypeMat tmp = 1.0 / num_rows_glob;\n        fill(m_b, tmp);\n        A.setView(oldView);\n    }\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solve_init(VVector &x)\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    copy(x, m_x, offset, size);\n    std::swap(m_x, m_v);\n    A.setView(oldView);\n}\n\n// One iteration of the single iteration.\n// MATLAB code of the algorithm:\n// while true\n//   V = X / norm(X);\n//   X = linsolve(A, V);\n//   lambda = V' * X;\n//   R = X - lambda * V;\n//   if norm(R) < tol * abs(lambda)\n//     break;\n//   end\n\ntemplate <class TConfig>\nbool SingleIteration_EigenSolver<TConfig>::solve_iteration(VVector &x)\n{\n    Operator<TConfig> &A = *this->m_A;\n    ViewType oldView = A.currentView();\n    A.setViewExterior();\n    int offset, size;\n    A.getOffsetAndSizeForView(A.getViewExterior(), &offset, &size);\n    // V = X / norm(X)\n    scal(m_v, ValueTypeVec(1) / get_norm(A, m_v, this->m_norm_type), offset, size);\n    // X = linsolve(A, v)\n    m_operator->apply(m_v, m_x);\n\n    if ((this->m_curr_iter % this->m_convergence_check_freq) == 0)\n    {\n        ValueTypeVec lambda;\n\n        if (this->m_which == EIG_PAGERANK)\n        {\n            // The maximum eigenvalue of G is 1.0\n            lambda = 1.0;\n            // v = x - v\n            // axpy(m_x, m_v, ValueTypeVec(-1),offset, size);\n            axpby(m_v, m_x, m_v, ValueTypeVec(-1.0), ValueTypeVec(1), offset, size);\n        }\n        else\n        {\n            // lambda = v.x\n            lambda = dot(A, m_v, m_x);\n            // v = x - lambda * v\n            axpby(m_v, m_x, m_v, -lambda, ValueTypeVec(1), offset, size);\n        }\n\n        ValueTypeVec residual_norm = get_norm(A, m_v, this->m_norm_type);\n        this->m_residuals.push_back(residual_norm / fabs(lambda));\n\n        // Check convergence.\n        if (residual_norm < this->m_tolerance * fabs(lambda))\n        {\n            // Normalize eigenvector.\n            if (this->m_which == EIG_PAGERANK)\n            {\n                //Norm L1 is more fited for the output of PageRank\n                ValueTypeVec norm = get_norm(A, m_x, L1);\n                scal(m_x, ValueTypeVec(1) / norm, offset, size);\n            }\n            else\n            {\n                ValueTypeVec norm = get_norm(A, m_x, this->m_norm_type);\n                scal(m_x, ValueTypeVec(1) / norm, offset, size);\n            }\n\n            // With inverse iteration we need to scale the eigenvector by the inverse of the eigenvalue,\n            // but this doesn't seems to be needed since we already normalized the eigenvector just above.\n            this->m_eigenvectors.push_back(m_x);\n            copy(m_x, x, offset, size);\n            this->m_eigenvalues.push_back(lambda);\n            this->postprocess_eigenpairs();\n            return true;\n        }\n    }\n\n    std::swap(m_x, m_v);\n    A.setView(oldView);\n    return false;\n}\n\ntemplate <class TConfig>\nvoid SingleIteration_EigenSolver<TConfig>::solve_finalize()\n{\n}\n\n// Explicit template instantiation.\n#define AMGX_CASE_LINE(CASE) template class SingleIteration_EigenSolver<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n};\n", "source": "/kaggle/working/amgx/src/eigensolvers/single_iteration_eigensolver.cu"}
{"input": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <eigensolvers/eigensolver.h>\n#include <cublas_v2.h>\n#include <matrix.h>\n#include <eigensolvers/qr.h>\n#include <amgx_lapack.h>\n#include <amgx_cublas.h>\n#include <stack>\n#include <queue>\n#include <fstream>\n#include <blas.h>\n#include <norm.h>\n#include <algorithm>\n\nnamespace amgx\n{\n\nnamespace\n{\n\ntemplate <typename T>\n__global__\nvoid kernel_clear_lower_triangular(T *V, int dim, int lda)\n{\n    int r = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (r < dim)\n    {\n        for (int c = 0; c < r; ++c)\n        {\n            V[c * lda + r] = 0.;\n        }\n    }\n}\n\ntemplate <typename Vector>\nvoid clear_lower_triangular(Vector &V)\n{\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = V.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    kernel_clear_lower_triangular <<< num_blocks, num_threads>>>(V.raw(), num_rows, V.get_lda());\n    cudaCheckError();\n}\n\n\nstruct TreeNode\n{\n    TreeNode(int v)\n        : left(0), right(0), parent(0), value(v)\n    {\n    }\n\n    bool is_leaf() const\n    {\n        return left == right;\n    }\n\n    bool is_root() const\n    {\n        return !parent;\n    }\n\n    TreeNode *left;\n    TreeNode *right;\n    TreeNode *parent;\n    int value;\n};\n\n// Recursive function to print a reduction tree to an ofstream in dot\n// format.\nvoid print_tree_rec(TreeNode *n, std::ofstream &f)\n{\n    TreeNode *left = n->left;\n    TreeNode *right = n->right;\n    TreeNode *parent = n->parent;\n    // We use the address of the nodes as labels in dot format to\n    // avoid having to renumber the nodes.\n    size_t address = reinterpret_cast<size_t>(n);\n    size_t parent_address = reinterpret_cast<size_t>(parent);\n    size_t left_address = reinterpret_cast<size_t>(left);\n    size_t right_address = reinterpret_cast<size_t>(right);\n    f << address << \"[label=\" << n->value << \"];\" << std::endl;\n\n    if (parent)\n    {\n        f << address << \" -> \" << parent_address << std::endl;\n    }\n\n    if (left)\n    {\n        f << address << \" -> \" << left_address << std::endl;\n        print_tree_rec(left, f);\n    }\n\n    if (right)\n    {\n        f << address << \" -> \" << right_address << std::endl;\n        print_tree_rec(right, f);\n    }\n}\n\n#ifdef DEBUG\n// Debug function: print a reduction tree to a file in dot format.\nvoid print_tree(TreeNode *root, const char *filename)\n{\n    std::ofstream f;\n    f.open(filename);\n    f << \"digraph A {\" << std::endl;\n    print_tree_rec(root, f);\n    f << \"}\" << std::endl;\n}\n\n// Create a degenerate tree of size N, this is not an efficient\n// reduction tree for a parallel operation (rank 0 is involved in all\n// operations) but it is used for testing.\n// Example with N == 4:\n//       0\n//      / \\\n//     0   \\\n//    / \\   \\\n//   0   \\   \\\n//  / \\   \\   \\\n// 0   1   2   3\n// The label on each node is the active process for this operation.\nTreeNode *build_degenerate_tree(int N)\n{\n    std::vector<TreeNode *> leaves;\n\n    // Allocate one leaf node for each rank.\n    for (int i = 0; i < N; ++i)\n    {\n        leaves.push_back(new TreeNode(i));\n    }\n\n    TreeNode *previous_node = leaves[0];\n\n    // For each leaf except the first one, create the new root of the\n    // tree labeled with 0.\n    // The left child of this node is the previous root and the right\n    // child is the current leaf.\n    for (int i = 0; i < N - 1; ++i)\n    {\n        TreeNode *left = previous_node;\n        TreeNode *right = leaves[i + 1];\n        TreeNode *ancestor = new TreeNode(previous_node->value);\n        left->parent = ancestor;\n        right->parent = ancestor;\n        ancestor->left = left;\n        ancestor->right = right;\n        previous_node = ancestor;\n    }\n\n    TreeNode *root = previous_node;\n    return root;\n}\n\n#endif\n// Create a full tree of size N, N must be a power of 2.\n// For instance if N == 4:\n//        0\n//       / \\\n//      /   \\\n//     0     2\n//    / \\   / \\\n//   0   1 2   3\n// The label on each node is the active process for this operation.\nTreeNode *build_full_tree(int N)\n{\n    // Nodes of the previous (deepest) level of the tree.\n    std::vector<TreeNode *> previous_level;\n    std::vector<TreeNode *> current_level;\n\n    // Allocate N leaves nodes, one for each process.\n    for (int i = 0; i < N; ++i)\n    {\n        previous_level.push_back(new TreeNode(i));\n    }\n\n    // Level i of the tree will have 2^i nodes, we construct the tree\n    // from leaves to root. At each level we allocate new nodes and\n    // connect these nodes to the previous level.\n    for (int stride = 2; stride <= N; stride <<= 1)\n    {\n        current_level.clear();\n        int children_index = 0;\n\n        for (int j = 0; j < N; j += stride)\n        {\n            TreeNode *left = previous_level[children_index++];\n            TreeNode *right = previous_level[children_index++];\n            TreeNode *ancestor = new TreeNode(j);\n            left->parent = ancestor;\n            right->parent = ancestor;\n            ancestor->left = left;\n            ancestor->right = right;\n            current_level.push_back(ancestor);\n        }\n\n        previous_level = current_level;\n    }\n\n    TreeNode *root = current_level.front();\n    return root;\n}\n\nvoid delete_tree(TreeNode *root)\n{\n    if (!root)\n    {\n        return;\n    }\n\n    delete_tree(root->left);\n    delete_tree(root->right);\n    delete root;\n}\n\nint previous_power_2(int v)\n{\n    unsigned p = 1;\n\n    while (p <= v)\n    {\n        p <<= 1;\n    }\n\n    return p >> 1;\n}\n\n// Build a reduction tree of size N. We build a full tree of size M,\n// the nearest lower power of 2 and we add remaining nodes in a\n// similar process as build_degenerate tree: rank 0 is involved in all\n// operations.\n\nTreeNode *build_reduction_tree(int N)\n{\n    int nearest_pow2 = previous_power_2(N);\n    TreeNode *root = build_full_tree(nearest_pow2);\n\n    if (nearest_pow2 == N)\n    {\n        return root;\n    }\n\n    // Number of ranks is not a power of two, create additional nodes for the remaining ranks.\n    TreeNode *previous = root;\n    int root_value = root->value;\n\n    for (int i = nearest_pow2; i < N; ++i)\n    {\n        TreeNode *left = previous;\n        TreeNode *right = new TreeNode(i);\n        TreeNode *ancestor = new TreeNode(root_value);\n        left->parent = ancestor;\n        right->parent = ancestor;\n        ancestor->left = left;\n        ancestor->right = right;\n        previous = ancestor;\n    }\n\n    return previous;\n}\n\n// Given a reduction tree, we have to compute for a given rank what\n// are the actions to perform, i.e. we need to find the list of nodes\n// in the tree whose value is the current rank.\n// For the up-sweep order, we need the list of these nodes ordered\n// from bottom to top (root). This order is given by performing a\n// breadth-first traversal of the tree and pushing encountered nodes\n// to a stack.\nvoid compute_up_sweep_order(TreeNode *root, int v, std::stack<TreeNode *> &order)\n{\n    std::queue<TreeNode *> to_visit;\n    to_visit.push(root);\n\n    while (!to_visit.empty())\n    {\n        TreeNode *current = to_visit.front();\n        to_visit.pop();\n\n        if (current->value == v)\n        {\n            order.push(current);\n        }\n\n        if (current->left)\n        {\n            to_visit.push(current->left);\n        }\n\n        if (current->right)\n        {\n            to_visit.push(current->right);\n        }\n    }\n}\n\ntemplate <typename TConfig>\nvoid\nvstack(Vector<TConfig> &dst, const Vector<TConfig> &top, const Vector<TConfig> &bottom)\n{\n    typedef typename Vector<TConfig>::value_type value_type;\n    dst.resize((top.get_num_rows() + bottom.get_num_rows()) * top.get_num_cols());\n    dst.set_num_rows(top.get_num_rows() + bottom.get_num_rows());\n    dst.set_num_cols(top.get_num_cols());\n    dst.set_lda(top.get_num_rows() + bottom.get_num_rows());\n    int dpitch = dst.get_lda() * sizeof(value_type);\n    int spitch = top.get_lda() * sizeof(value_type);\n    int width = top.get_num_cols() * sizeof(value_type);\n    int height = top.get_num_rows();\n    cudaMemcpy2D(dst.raw(), dpitch, top.raw(), spitch, width, height,\n                 cudaMemcpyDeviceToDevice);\n    cudaMemcpy2D(dst.raw() + top.get_num_cols(), dpitch, bottom.raw(), spitch, width, height,\n                 cudaMemcpyDeviceToDevice);\n}\n\ntemplate <typename Vector>\nvoid vslice(const Vector &src, Vector &dst, int start, int end)\n{\n    typedef typename Vector::value_type value_type;\n    int cols = src.get_num_cols();\n    int dst_rows = end - start;\n    dst.resize(dst_rows * cols);\n    dst.set_num_rows(dst_rows);\n    dst.set_num_cols(cols);\n    dst.set_lda(dst_rows);\n    int dpitch = dst.get_lda() * sizeof(value_type);\n    int spitch = src.get_lda() * sizeof(value_type);\n    int width = dst.get_num_cols() * sizeof(value_type);\n    int height = dst.get_num_rows();\n    cudaMemcpy2D(dst.raw(), dpitch, src.raw() + start, spitch, width, height,\n                 cudaMemcpyDeviceToDevice);\n}\n\n} // end anonymous namespace.\n\ntemplate <typename TConfig>\nHouseholderQR<TConfig>::HouseholderQR(TMatrix &A)\n    : m_A(A), m_use_R_inverse(true)\n{\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR(TVector &V)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    Lapack<TConfig>::geqrf(V, m_tau, m_work);\n    Lapack<TConfig>::orgqr(V, m_tau, m_work);\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR(TVector &V, TVector &R)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    R.resize(cols * cols);\n    R.set_num_rows(cols);\n    R.set_num_cols(cols);\n    R.set_lda(cols);\n    Lapack<TConfig>::geqrf(V, m_tau, m_work);\n    vslice(V, R, 0, cols);\n    clear_lower_triangular(R);\n    Lapack<TConfig>::orgqr(V, m_tau, m_work);\n}\n\n\n// Send a vector to rank 'destination'.\n// If 'destination' is the current rank, we don't perform any call to\n// MPI and instead save the vector in a stack.\ntemplate <typename TConfig>\nvoid\nHouseholderQR<TConfig>::send_vector(TVector &V, int destination)\n{\n    TMatrix &A = m_A;\n    int current_rank = A.manager->global_id();\n\n    if (destination == current_rank)\n    {\n        m_local_comms_stack.push(V);\n    }\n    else\n    {\n        A.manager->getComms()->send_vector(V, destination, 0);\n    }\n}\n\n// Receive a vector from rank 'source'.\n// If 'source' is the current rank, we just pop the vector from our\n// local stack.\ntemplate <typename TConfig>\nvoid\nHouseholderQR<TConfig>::receive_vector(TVector &V, int source)\n{\n    TMatrix &A = m_A;\n    int current_rank = A.manager->global_id();\n\n    if (source == current_rank)\n    {\n        V = m_local_comms_stack.top();\n        m_local_comms_stack.pop();\n    }\n    else\n    {\n        A.manager->getComms()->recv_vector(V, source, 0);\n    }\n}\n\n// Instead of performing the down-sweep phase of the TSQR algorithm to\n// get the Q matrix, we can compute Q = A * R ^ -1. This alternative\n// use less communications.\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::inverse_phase(TVector &V, TVector &R, int root)\n{\n    TMatrix &A = m_A;\n    int num_partitions = A.manager->getComms()->get_num_partitions();\n    int current_partition = A.manager->global_id();\n\n    if (current_partition == root)\n    {\n        for (int i = 0; i < num_partitions; ++i)\n        {\n            send_vector(R, i);\n        }\n    }\n\n    receive_vector(R, root);\n    Vector_h h_R = R;\n    Lapack<TConfig_h>::trtri(h_R);\n    R = h_R;\n    // Q = V * R^-1\n    Cublas::gemm(1, V, R, 0, V);\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR_decomposition(TVector &V)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    TMatrix &A = m_A;\n\n    if (cols == 1)\n    {\n        ValueTypeVec norm = get_norm(A, V, L2);\n        scal(V, 1 / norm, 0, rows);\n        return;\n    }\n\n    m_tau.resize(cols);\n    m_work.resize(rows * cols);\n\n    if (A.is_matrix_singleGPU())\n    {\n        QR(V);\n        return;\n    }\n\n    // Multi-GPU path.\n    int num_partitions = A.manager->getComms()->get_num_partitions();\n    int current_partition = A.manager->global_id();\n    // Stack of the Q matrices compute during reduction.\n    std::stack<TVector> Q_stack;\n    TVector B1(cols * cols);\n    B1.set_num_rows(cols);\n    B1.set_num_cols(cols);\n    B1.set_lda(cols);\n    TVector B2(cols * cols);\n    B2.set_num_rows(cols);\n    B2.set_num_cols(cols);\n    B2.set_lda(cols);\n    TVector B;\n    TVector R;\n    TreeNode *reduce_tree = build_reduction_tree(num_partitions);\n    // Debug: uncomment to print reduction tree.\n    /*\n        if (current_partition == 0)\n            print_tree(reduce_tree, \"tree.dot\");\n    */\n    std::stack<TreeNode *> up_sweep_order;\n    compute_up_sweep_order(reduce_tree, current_partition, up_sweep_order);\n    std::stack<TreeNode *> down_sweep_order;\n    std::stack<TreeNode *> order = up_sweep_order;\n\n    while (!order.empty())\n    {\n        down_sweep_order.push(order.top());\n        order.pop();\n    }\n\n    // Up-sweep phase:\n    // - At each leaf node: compute a QR on its part of the vector then send the R matrix to its parent.\n    // - At each non-leaf node: receives a R matrix from both\n    //   children, compute a QR and send the result R to the parent.\n    //   Q matrices are saved in a stack.\n    while (!up_sweep_order.empty())\n    {\n        TreeNode *current = up_sweep_order.top();\n        up_sweep_order.pop();\n        TreeNode *left_child = current->left;\n        TreeNode *right_child = current->right;\n        TreeNode *parent = current->parent;\n\n        if (current->is_leaf())\n        {\n            B = V;\n        }\n        else\n        {\n            // Receive R matrices from children and stack the two one above the other.\n            receive_vector(B1, left_child->value);\n            receive_vector(B2, right_child->value);\n            vstack(B, B1, B2);\n        }\n\n        // costly for first iteration since we have to copy the vector to the stack.\n        QR(B, R);\n        Q_stack.push(B);\n\n        if (!current->is_root())\n        {\n            send_vector(R, parent->value);\n        }\n    }\n\n    if (m_use_R_inverse)\n    {\n        int reduce_root = reduce_tree->value;\n        delete_tree(reduce_tree);\n        inverse_phase(V, R, reduce_root);\n        return;\n    }\n\n    // Down-sweep phase:\n    // - At each node: receive a matrix from your parent (root of the\n    //   tree uses the identity matrix) and multiply this matrix with\n    //   the saved Q matrix for this node (computed during the\n    //   up-sweep phase).\n    //   Split this vector vertically and send the first half to your\n    //   left child and the second half to your right child.\n    //   Leaves nodes do not send anything, after multiplication the\n    //   result is the local part of the global Q matrix.\n    while (!down_sweep_order.empty())\n    {\n        TreeNode *current = down_sweep_order.top();\n        down_sweep_order.pop();\n        TreeNode *left_child = current->left;\n        TreeNode *right_child = current->right;\n        TreeNode *parent = current->parent;\n        TVector Q = Q_stack.top();\n        Q_stack.pop();\n\n        if (current->is_root())\n        {\n            B = Q;\n        }\n        else\n        {\n            // If leaf, write directly to output vector.\n            TVector &output = current->is_leaf() ? V : B;\n            receive_vector(B1, parent->value);\n            Cublas::gemm(1, Q, B1, 0, output);\n        }\n\n        if (current->is_leaf())\n        {\n            continue;\n        }\n\n        int rows = B.get_num_rows();\n        vslice(B, B1, 0, rows / 2);\n        vslice(B, B2, rows / 2, rows);\n        send_vector(B1, left_child->value);\n        send_vector(B2, right_child->value);\n    }\n\n    delete_tree(reduce_tree);\n}\n\n#define AMGX_CASE_LINE(CASE) template class HouseholderQR<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n\n", "output": "// SPDX-FileCopyrightText: 2013 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <eigensolvers/eigensolver.h>\n#include <hipblas.h>\n#include <matrix.h>\n#include <eigensolvers/qr.h>\n#include <amgx_lapack.h>\n#include <amgx_cublas.h>\n#include <stack>\n#include <queue>\n#include <fstream>\n#include <blas.h>\n#include <norm.h>\n#include <algorithm>\n\nnamespace amgx\n{\n\nnamespace\n{\n\ntemplate <typename T>\n__global__\nvoid kernel_clear_lower_triangular(T *V, int dim, int lda)\n{\n    int r = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (r < dim)\n    {\n        for (int c = 0; c < r; ++c)\n        {\n            V[c * lda + r] = 0.;\n        }\n    }\n}\n\ntemplate <typename Vector>\nvoid clear_lower_triangular(Vector &V)\n{\n    int num_threads = 128;\n    int max_grid_size = 4096;\n    int num_rows = V.get_num_rows();\n    int num_blocks = std::min(max_grid_size, (num_rows + num_threads - 1) / num_rows);\n    kernel_clear_lower_triangular <<< num_blocks, num_threads>>>(V.raw(), num_rows, V.get_lda());\n    cudaCheckError();\n}\n\n\nstruct TreeNode\n{\n    TreeNode(int v)\n        : left(0), right(0), parent(0), value(v)\n    {\n    }\n\n    bool is_leaf() const\n    {\n        return left == right;\n    }\n\n    bool is_root() const\n    {\n        return !parent;\n    }\n\n    TreeNode *left;\n    TreeNode *right;\n    TreeNode *parent;\n    int value;\n};\n\n// Recursive function to print a reduction tree to an ofstream in dot\n// format.\nvoid print_tree_rec(TreeNode *n, std::ofstream &f)\n{\n    TreeNode *left = n->left;\n    TreeNode *right = n->right;\n    TreeNode *parent = n->parent;\n    // We use the address of the nodes as labels in dot format to\n    // avoid having to renumber the nodes.\n    size_t address = reinterpret_cast<size_t>(n);\n    size_t parent_address = reinterpret_cast<size_t>(parent);\n    size_t left_address = reinterpret_cast<size_t>(left);\n    size_t right_address = reinterpret_cast<size_t>(right);\n    f << address << \"[label=\" << n->value << \"];\" << std::endl;\n\n    if (parent)\n    {\n        f << address << \" -> \" << parent_address << std::endl;\n    }\n\n    if (left)\n    {\n        f << address << \" -> \" << left_address << std::endl;\n        print_tree_rec(left, f);\n    }\n\n    if (right)\n    {\n        f << address << \" -> \" << right_address << std::endl;\n        print_tree_rec(right, f);\n    }\n}\n\n#ifdef DEBUG\n// Debug function: print a reduction tree to a file in dot format.\nvoid print_tree(TreeNode *root, const char *filename)\n{\n    std::ofstream f;\n    f.open(filename);\n    f << \"digraph A {\" << std::endl;\n    print_tree_rec(root, f);\n    f << \"}\" << std::endl;\n}\n\n// Create a degenerate tree of size N, this is not an efficient\n// reduction tree for a parallel operation (rank 0 is involved in all\n// operations) but it is used for testing.\n// Example with N == 4:\n//       0\n//      / \\\n//     0   \\\n//    / \\   \\\n//   0   \\   \\\n//  / \\   \\   \\\n// 0   1   2   3\n// The label on each node is the active process for this operation.\nTreeNode *build_degenerate_tree(int N)\n{\n    std::vector<TreeNode *> leaves;\n\n    // Allocate one leaf node for each rank.\n    for (int i = 0; i < N; ++i)\n    {\n        leaves.push_back(new TreeNode(i));\n    }\n\n    TreeNode *previous_node = leaves[0];\n\n    // For each leaf except the first one, create the new root of the\n    // tree labeled with 0.\n    // The left child of this node is the previous root and the right\n    // child is the current leaf.\n    for (int i = 0; i < N - 1; ++i)\n    {\n        TreeNode *left = previous_node;\n        TreeNode *right = leaves[i + 1];\n        TreeNode *ancestor = new TreeNode(previous_node->value);\n        left->parent = ancestor;\n        right->parent = ancestor;\n        ancestor->left = left;\n        ancestor->right = right;\n        previous_node = ancestor;\n    }\n\n    TreeNode *root = previous_node;\n    return root;\n}\n\n#endif\n// Create a full tree of size N, N must be a power of 2.\n// For instance if N == 4:\n//        0\n//       / \\\n//      /   \\\n//     0     2\n//    / \\   / \\\n//   0   1 2   3\n// The label on each node is the active process for this operation.\nTreeNode *build_full_tree(int N)\n{\n    // Nodes of the previous (deepest) level of the tree.\n    std::vector<TreeNode *> previous_level;\n    std::vector<TreeNode *> current_level;\n\n    // Allocate N leaves nodes, one for each process.\n    for (int i = 0; i < N; ++i)\n    {\n        previous_level.push_back(new TreeNode(i));\n    }\n\n    // Level i of the tree will have 2^i nodes, we construct the tree\n    // from leaves to root. At each level we allocate new nodes and\n    // connect these nodes to the previous level.\n    for (int stride = 2; stride <= N; stride <<= 1)\n    {\n        current_level.clear();\n        int children_index = 0;\n\n        for (int j = 0; j < N; j += stride)\n        {\n            TreeNode *left = previous_level[children_index++];\n            TreeNode *right = previous_level[children_index++];\n            TreeNode *ancestor = new TreeNode(j);\n            left->parent = ancestor;\n            right->parent = ancestor;\n            ancestor->left = left;\n            ancestor->right = right;\n            current_level.push_back(ancestor);\n        }\n\n        previous_level = current_level;\n    }\n\n    TreeNode *root = current_level.front();\n    return root;\n}\n\nvoid delete_tree(TreeNode *root)\n{\n    if (!root)\n    {\n        return;\n    }\n\n    delete_tree(root->left);\n    delete_tree(root->right);\n    delete root;\n}\n\nint previous_power_2(int v)\n{\n    unsigned p = 1;\n\n    while (p <= v)\n    {\n        p <<= 1;\n    }\n\n    return p >> 1;\n}\n\n// Build a reduction tree of size N. We build a full tree of size M,\n// the nearest lower power of 2 and we add remaining nodes in a\n// similar process as build_degenerate tree: rank 0 is involved in all\n// operations.\n\nTreeNode *build_reduction_tree(int N)\n{\n    int nearest_pow2 = previous_power_2(N);\n    TreeNode *root = build_full_tree(nearest_pow2);\n\n    if (nearest_pow2 == N)\n    {\n        return root;\n    }\n\n    // Number of ranks is not a power of two, create additional nodes for the remaining ranks.\n    TreeNode *previous = root;\n    int root_value = root->value;\n\n    for (int i = nearest_pow2; i < N; ++i)\n    {\n        TreeNode *left = previous;\n        TreeNode *right = new TreeNode(i);\n        TreeNode *ancestor = new TreeNode(root_value);\n        left->parent = ancestor;\n        right->parent = ancestor;\n        ancestor->left = left;\n        ancestor->right = right;\n        previous = ancestor;\n    }\n\n    return previous;\n}\n\n// Given a reduction tree, we have to compute for a given rank what\n// are the actions to perform, i.e. we need to find the list of nodes\n// in the tree whose value is the current rank.\n// For the up-sweep order, we need the list of these nodes ordered\n// from bottom to top (root). This order is given by performing a\n// breadth-first traversal of the tree and pushing encountered nodes\n// to a stack.\nvoid compute_up_sweep_order(TreeNode *root, int v, std::stack<TreeNode *> &order)\n{\n    std::queue<TreeNode *> to_visit;\n    to_visit.push(root);\n\n    while (!to_visit.empty())\n    {\n        TreeNode *current = to_visit.front();\n        to_visit.pop();\n\n        if (current->value == v)\n        {\n            order.push(current);\n        }\n\n        if (current->left)\n        {\n            to_visit.push(current->left);\n        }\n\n        if (current->right)\n        {\n            to_visit.push(current->right);\n        }\n    }\n}\n\ntemplate <typename TConfig>\nvoid\nvstack(Vector<TConfig> &dst, const Vector<TConfig> &top, const Vector<TConfig> &bottom)\n{\n    typedef typename Vector<TConfig>::value_type value_type;\n    dst.resize((top.get_num_rows() + bottom.get_num_rows()) * top.get_num_cols());\n    dst.set_num_rows(top.get_num_rows() + bottom.get_num_rows());\n    dst.set_num_cols(top.get_num_cols());\n    dst.set_lda(top.get_num_rows() + bottom.get_num_rows());\n    int dpitch = dst.get_lda() * sizeof(value_type);\n    int spitch = top.get_lda() * sizeof(value_type);\n    int width = top.get_num_cols() * sizeof(value_type);\n    int height = top.get_num_rows();\n    hipMemcpy2D(dst.raw(), dpitch, top.raw(), spitch, width, height,\n                 hipMemcpyDeviceToDevice);\n    hipMemcpy2D(dst.raw() + top.get_num_cols(), dpitch, bottom.raw(), spitch, width, height,\n                 hipMemcpyDeviceToDevice);\n}\n\ntemplate <typename Vector>\nvoid vslice(const Vector &src, Vector &dst, int start, int end)\n{\n    typedef typename Vector::value_type value_type;\n    int cols = src.get_num_cols();\n    int dst_rows = end - start;\n    dst.resize(dst_rows * cols);\n    dst.set_num_rows(dst_rows);\n    dst.set_num_cols(cols);\n    dst.set_lda(dst_rows);\n    int dpitch = dst.get_lda() * sizeof(value_type);\n    int spitch = src.get_lda() * sizeof(value_type);\n    int width = dst.get_num_cols() * sizeof(value_type);\n    int height = dst.get_num_rows();\n    hipMemcpy2D(dst.raw(), dpitch, src.raw() + start, spitch, width, height,\n                 hipMemcpyDeviceToDevice);\n}\n\n} // end anonymous namespace.\n\ntemplate <typename TConfig>\nHouseholderQR<TConfig>::HouseholderQR(TMatrix &A)\n    : m_A(A), m_use_R_inverse(true)\n{\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR(TVector &V)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    Lapack<TConfig>::geqrf(V, m_tau, m_work);\n    Lapack<TConfig>::orgqr(V, m_tau, m_work);\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR(TVector &V, TVector &R)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    R.resize(cols * cols);\n    R.set_num_rows(cols);\n    R.set_num_cols(cols);\n    R.set_lda(cols);\n    Lapack<TConfig>::geqrf(V, m_tau, m_work);\n    vslice(V, R, 0, cols);\n    clear_lower_triangular(R);\n    Lapack<TConfig>::orgqr(V, m_tau, m_work);\n}\n\n\n// Send a vector to rank 'destination'.\n// If 'destination' is the current rank, we don't perform any call to\n// MPI and instead save the vector in a stack.\ntemplate <typename TConfig>\nvoid\nHouseholderQR<TConfig>::send_vector(TVector &V, int destination)\n{\n    TMatrix &A = m_A;\n    int current_rank = A.manager->global_id();\n\n    if (destination == current_rank)\n    {\n        m_local_comms_stack.push(V);\n    }\n    else\n    {\n        A.manager->getComms()->send_vector(V, destination, 0);\n    }\n}\n\n// Receive a vector from rank 'source'.\n// If 'source' is the current rank, we just pop the vector from our\n// local stack.\ntemplate <typename TConfig>\nvoid\nHouseholderQR<TConfig>::receive_vector(TVector &V, int source)\n{\n    TMatrix &A = m_A;\n    int current_rank = A.manager->global_id();\n\n    if (source == current_rank)\n    {\n        V = m_local_comms_stack.top();\n        m_local_comms_stack.pop();\n    }\n    else\n    {\n        A.manager->getComms()->recv_vector(V, source, 0);\n    }\n}\n\n// Instead of performing the down-sweep phase of the TSQR algorithm to\n// get the Q matrix, we can compute Q = A * R ^ -1. This alternative\n// use less communications.\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::inverse_phase(TVector &V, TVector &R, int root)\n{\n    TMatrix &A = m_A;\n    int num_partitions = A.manager->getComms()->get_num_partitions();\n    int current_partition = A.manager->global_id();\n\n    if (current_partition == root)\n    {\n        for (int i = 0; i < num_partitions; ++i)\n        {\n            send_vector(R, i);\n        }\n    }\n\n    receive_vector(R, root);\n    Vector_h h_R = R;\n    Lapack<TConfig_h>::trtri(h_R);\n    R = h_R;\n    // Q = V * R^-1\n    Cublas::gemm(1, V, R, 0, V);\n}\n\ntemplate <typename TConfig>\nvoid HouseholderQR<TConfig>::QR_decomposition(TVector &V)\n{\n    int rows = V.get_num_rows();\n    int cols = V.get_num_cols();\n    int lda = V.get_lda();\n    TMatrix &A = m_A;\n\n    if (cols == 1)\n    {\n        ValueTypeVec norm = get_norm(A, V, L2);\n        scal(V, 1 / norm, 0, rows);\n        return;\n    }\n\n    m_tau.resize(cols);\n    m_work.resize(rows * cols);\n\n    if (A.is_matrix_singleGPU())\n    {\n        QR(V);\n        return;\n    }\n\n    // Multi-GPU path.\n    int num_partitions = A.manager->getComms()->get_num_partitions();\n    int current_partition = A.manager->global_id();\n    // Stack of the Q matrices compute during reduction.\n    std::stack<TVector> Q_stack;\n    TVector B1(cols * cols);\n    B1.set_num_rows(cols);\n    B1.set_num_cols(cols);\n    B1.set_lda(cols);\n    TVector B2(cols * cols);\n    B2.set_num_rows(cols);\n    B2.set_num_cols(cols);\n    B2.set_lda(cols);\n    TVector B;\n    TVector R;\n    TreeNode *reduce_tree = build_reduction_tree(num_partitions);\n    // Debug: uncomment to print reduction tree.\n    /*\n        if (current_partition == 0)\n            print_tree(reduce_tree, \"tree.dot\");\n    */\n    std::stack<TreeNode *> up_sweep_order;\n    compute_up_sweep_order(reduce_tree, current_partition, up_sweep_order);\n    std::stack<TreeNode *> down_sweep_order;\n    std::stack<TreeNode *> order = up_sweep_order;\n\n    while (!order.empty())\n    {\n        down_sweep_order.push(order.top());\n        order.pop();\n    }\n\n    // Up-sweep phase:\n    // - At each leaf node: compute a QR on its part of the vector then send the R matrix to its parent.\n    // - At each non-leaf node: receives a R matrix from both\n    //   children, compute a QR and send the result R to the parent.\n    //   Q matrices are saved in a stack.\n    while (!up_sweep_order.empty())\n    {\n        TreeNode *current = up_sweep_order.top();\n        up_sweep_order.pop();\n        TreeNode *left_child = current->left;\n        TreeNode *right_child = current->right;\n        TreeNode *parent = current->parent;\n\n        if (current->is_leaf())\n        {\n            B = V;\n        }\n        else\n        {\n            // Receive R matrices from children and stack the two one above the other.\n            receive_vector(B1, left_child->value);\n            receive_vector(B2, right_child->value);\n            vstack(B, B1, B2);\n        }\n\n        // costly for first iteration since we have to copy the vector to the stack.\n        QR(B, R);\n        Q_stack.push(B);\n\n        if (!current->is_root())\n        {\n            send_vector(R, parent->value);\n        }\n    }\n\n    if (m_use_R_inverse)\n    {\n        int reduce_root = reduce_tree->value;\n        delete_tree(reduce_tree);\n        inverse_phase(V, R, reduce_root);\n        return;\n    }\n\n    // Down-sweep phase:\n    // - At each node: receive a matrix from your parent (root of the\n    //   tree uses the identity matrix) and multiply this matrix with\n    //   the saved Q matrix for this node (computed during the\n    //   up-sweep phase).\n    //   Split this vector vertically and send the first half to your\n    //   left child and the second half to your right child.\n    //   Leaves nodes do not send anything, after multiplication the\n    //   result is the local part of the global Q matrix.\n    while (!down_sweep_order.empty())\n    {\n        TreeNode *current = down_sweep_order.top();\n        down_sweep_order.pop();\n        TreeNode *left_child = current->left;\n        TreeNode *right_child = current->right;\n        TreeNode *parent = current->parent;\n        TVector Q = Q_stack.top();\n        Q_stack.pop();\n\n        if (current->is_root())\n        {\n            B = Q;\n        }\n        else\n        {\n            // If leaf, write directly to output vector.\n            TVector &output = current->is_leaf() ? V : B;\n            receive_vector(B1, parent->value);\n            Cublas::gemm(1, Q, B1, 0, output);\n        }\n\n        if (current->is_leaf())\n        {\n            continue;\n        }\n\n        int rows = B.get_num_rows();\n        vslice(B, B1, 0, rows / 2);\n        vslice(B, B2, rows / 2, rows);\n        send_vector(B1, left_child->value);\n        send_vector(B2, right_child->value);\n    }\n\n    delete_tree(reduce_tree);\n}\n\n#define AMGX_CASE_LINE(CASE) template class HouseholderQR<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n\n", "source": "/kaggle/working/amgx/src/eigensolvers/qr.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <types.h>\n#include <gaussian_elimination.h>\n#include <norm.h>\n#include <blas.h>\n#include <cycles/cycle.h>\n\n#include <assert.h>\n#include <amgx_types/math.h>\n\nnamespace amgx\n{\n\n// Methods to check if Ax=b is easily invertible\ntemplate <class T_Config>\nbool Cycle_Base<T_Config>::isASolvable(const Matrix<T_Config> &A)\n{\n    // currently we have to disable this because in the distributed solver we have to launch coarse solver to exchange halos\n    // otherwise in some cases nodes with > 1 coarse rows will be stuck waiting on halos from the node with 1 coarse row\n    return false;\n    /*return (A.get_num_rows()==1) && (A.get_block_dimy()==A.get_block_dimx());*/\n}\n\ntemplate<typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid direct_1x1_solve(const ValueTypeA *values, const ValueTypeB *rhs, ValueTypeB *x)\n\n{\n    // it is always 1 thread because 1x1, but whatever\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (tid == 0) { x[tid] = rhs[tid] / values[tid]; }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_1x1(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    x[0] = b[0] / A.values[0];\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_1x1(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    // only 1 thread needed though\n    direct_1x1_solve <<< 1, 32>>>(\n        A.values.raw(),\n        b.raw(),\n        x.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_4x4(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    GaussianElimination<T_Config>::gaussianElimination(A, x, b);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_4x4(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    GaussianElimination<T_Config>::gaussianElimination(A, x, b);\n}\n\ntemplate <class T_Config>\nvoid Cycle_Base<T_Config>::solveExactly(const Matrix<T_Config> &A, VVector &x, VVector &b)\n{\n    if (A.get_block_size() == 1)\n    {\n        solveExactly_1x1(A, x, b);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        solveExactly_4x4(A, x, b);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\ntemplate<class T_Config>\nstd::map<std::string, CycleFactory<T_Config>*> &\nCycleFactory<T_Config>::getFactories( )\n{\n    static std::map<std::string, CycleFactory<T_Config>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::registerFactory(std::string name, CycleFactory<T_Config> *f)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"CycleFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::unregisterFactory(std::string name)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"CycleFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    CycleFactory<T_Config> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::unregisterFactories( )\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        CycleFactory<T_Config> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class T_Config>\nCycle<T_Config> *CycleFactory<T_Config>::allocate(AMG_Class *amg, AMG_Level<T_Config> *level, VVector &b, VVector &c)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    std::string cycle_name = amg->m_cfg->AMG_Config::template getParameter<std::string>(\"cycle\", amg->m_cfg_scope);\n    typename std::map<std::string, CycleFactory<T_Config> *>::const_iterator it = factories.find(cycle_name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"CycleFactory '\" + cycle_name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(amg, level, b, c);\n};\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::generate(AMG_Class *amg, AMG_Level<T_Config> *level, VVector &b, VVector &c)\n{\n    Cycle<T_Config> *cycle = allocate( amg, level, b, c );\n    delete cycle;\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n\n#define AMGX_CASE_LINE(CASE) template class Cycle_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Cycle<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CycleFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <types.h>\n#include <gaussian_elimination.h>\n#include <norm.h>\n#include <blas.h>\n#include <cycles/cycle.h>\n\n#include <assert.h>\n#include <amgx_types/math.h>\n\nnamespace amgx\n{\n\n// Methods to check if Ax=b is easily invertible\ntemplate <class T_Config>\nbool Cycle_Base<T_Config>::isASolvable(const Matrix<T_Config> &A)\n{\n    // currently we have to disable this because in the distributed solver we have to launch coarse solver to exchange halos\n    // otherwise in some cases nodes with > 1 coarse rows will be stuck waiting on halos from the node with 1 coarse row\n    return false;\n    /*return (A.get_num_rows()==1) && (A.get_block_dimy()==A.get_block_dimx());*/\n}\n\ntemplate<typename ValueTypeA, typename ValueTypeB>\n__global__\nvoid direct_1x1_solve(const ValueTypeA *values, const ValueTypeB *rhs, ValueTypeB *x)\n\n{\n    // it is always 1 thread because 1x1, but whatever\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (tid == 0) { x[tid] = rhs[tid] / values[tid]; }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_1x1(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    x[0] = b[0] / A.values[0];\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_1x1(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    // only 1 thread needed though\n    direct_1x1_solve <<< 1, 32>>>(\n        A.values.raw(),\n        b.raw(),\n        x.raw());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_4x4(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    GaussianElimination<T_Config>::gaussianElimination(A, x, b);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Cycle<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::solveExactly_4x4(const Matrix<T_Config> &A, Vector<T_Config> &x, Vector<T_Config> &b)\n{\n    GaussianElimination<T_Config>::gaussianElimination(A, x, b);\n}\n\ntemplate <class T_Config>\nvoid Cycle_Base<T_Config>::solveExactly(const Matrix<T_Config> &A, VVector &x, VVector &b)\n{\n    if (A.get_block_size() == 1)\n    {\n        solveExactly_1x1(A, x, b);\n    }\n    else if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        solveExactly_4x4(A, x, b);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\ntemplate<class T_Config>\nstd::map<std::string, CycleFactory<T_Config>*> &\nCycleFactory<T_Config>::getFactories( )\n{\n    static std::map<std::string, CycleFactory<T_Config>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::registerFactory(std::string name, CycleFactory<T_Config> *f)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"CycleFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::unregisterFactory(std::string name)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"CycleFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    CycleFactory<T_Config> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::unregisterFactories( )\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, CycleFactory<T_Config> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        CycleFactory<T_Config> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class T_Config>\nCycle<T_Config> *CycleFactory<T_Config>::allocate(AMG_Class *amg, AMG_Level<T_Config> *level, VVector &b, VVector &c)\n{\n    std::map<std::string, CycleFactory<T_Config>*> &factories = getFactories( );\n    std::string cycle_name = amg->m_cfg->AMG_Config::template getParameter<std::string>(\"cycle\", amg->m_cfg_scope);\n    typename std::map<std::string, CycleFactory<T_Config> *>::const_iterator it = factories.find(cycle_name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"CycleFactory '\" + cycle_name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(amg, level, b, c);\n};\n\ntemplate<class T_Config>\nvoid CycleFactory<T_Config>::generate(AMG_Class *amg, AMG_Level<T_Config> *level, VVector &b, VVector &c)\n{\n    Cycle<T_Config> *cycle = allocate( amg, level, b, c );\n    delete cycle;\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n\n#define AMGX_CASE_LINE(CASE) template class Cycle_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Cycle<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CycleFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/cycles/cycle.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/locally_downwind.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <queue>\n#include <iostream>\n\nnamespace amgx\n{\nnamespace locally_downwind_kernels\n{\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(IndexType *row_colors, const int num_colors, const int num_rows)\n{\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += gridDim.x * blockDim.x)\n    {\n        row_colors[i] = i % num_colors;\n    }\n}\n\n\n//is this edge and outgoing edge?\ntemplate <typename IndexType, typename ValueType>\n__host__ __device__\nbool outgoing_edge( const IndexType *ia, const IndexType *ja, const ValueType *aa, IndexType i, IndexType ii, int blocksize )\n{\n    IndexType j = ja[ii];\n    ValueType weight = 0.0;\n\n    for (IndexType iii = ii * blocksize; iii < (ii + 1)*blocksize; iii++)\n    {\n        weight += (aa[iii] * aa[iii]);\n    }\n\n    for (IndexType jj = ia[j]; jj < ia[j + 1]; jj++)\n    {\n        if ( ja[jj] == i )\n        {\n            ValueType counter_weight = 0.0;\n\n            for (IndexType jjj = jj * blocksize; jjj < (jj + 1)*blocksize; jjj++)\n            {\n                counter_weight += (aa[jjj] * aa[jjj]);\n            }\n\n            return (weight >= counter_weight);\n        }\n    }\n\n    return true;\n}\n\ntemplate <typename IndexType>\nstruct Buffer\n{\n    IndexType *buffer;\n    int buffersize;\n    int loptr;\n    int hiptr;\n    int offset;\n\n    __device__ int size();\n    __device__ IndexType pop();\n    __device__ void push( IndexType node );\n};\n\ntemplate <typename IndexType>\n__device__\nIndexType Buffer<IndexType>::pop()\n{\n    int pos = loptr % buffersize;\n    loptr++;\n    return buffer[offset + pos];\n}\n\ntemplate <typename IndexType>\n__device__\nvoid Buffer<IndexType>::push(IndexType element )\n{\n    buffer[offset + hiptr] = element;\n    hiptr = (hiptr + 1) % buffersize;\n}\n\ntemplate <typename IndexType>\n__device__\nint Buffer<IndexType>::size()\n{\n    return (hiptr - loptr) % buffersize;\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid traverse( const IndexType *ia, const IndexType *ja, const ValueType *aa, const IndexType *ria, const IndexType *rja, const IndexType *agg, IndexType *color, IndexType numAggregates, int buffersize, int blocksize )\n{\n    extern __shared__ IndexType tr_smem[];\n    Buffer<IndexType> ring;\n    ring.buffer = &tr_smem[0];\n    ring.buffersize = buffersize;\n    ring.loptr = 0;\n    ring.hiptr = 0;\n    ring.offset = threadIdx.x * buffersize;\n    int aggregate = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if ( aggregate < numAggregates )\n    {\n        bool nodesLeft = true;\n\n        for (int allowed_incoming_edges = 0; nodesLeft; allowed_incoming_edges++)\n        {\n            nodesLeft = false;\n\n            for (IndexType ii = ria[aggregate]; ii < ria[aggregate + 1]; ii++)\n            {\n                //find possible root node for modified BFS coloring\n                IndexType node = rja[ii];\n                int found_edges = 0;\n\n                //not colored yet\n                if ( color[node] == -1 )\n                {\n                    for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                    {\n                        if ( agg[ja[jj]] == aggregate && !outgoing_edge( ia, ja, aa, node, jj, blocksize ) )\n                        {\n                            found_edges++;\n                        }\n                    }\n\n                    nodesLeft = true;\n                }\n                else\n                {\n                    found_edges = allowed_incoming_edges + 1;\n                }\n\n                //start modified BFS\n                if ( found_edges <= allowed_incoming_edges )\n                {\n                    color[node] = 0; //TODO: make the node look around for already set neighbors first\n                    ring.push(node);\n\n                    while ( ring.size() > 0 )\n                    {\n                        //this node is already colored.\n                        node = ring.pop();\n                        int myInitialColor = color[node];\n                        //traverse all neighbors to determin minimum own color\n                        int myColor = myInitialColor;\n\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType j = ja[jj];\n\n                            if ( color[j] == myColor && j != node)\n                            {\n                                //try next color\n                                myColor++;\n                                jj = ia[node];\n                            }\n                        }\n\n                        //repair own color\n                        //color[node] = myColor;\n\n                        //traverse all children\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType child = ja[jj];\n\n                            if ( agg[child] != aggregate || //only set colors in own aggregate\n                                    !outgoing_edge( ia, ja, aa, node, jj, blocksize )) //only process outgoing edges\n                            {\n                                continue;\n                            }\n\n                            //unset -> set and push\n                            if ( color[child] == -1)\n                            {\n                                color[child] = myColor + 1;\n                                ring.push( child );\n                            }\n\n                            if ( color[child] >= myInitialColor )\n                            {\n                                color[child] = myColor + 1; //max( myColor+1, color[child] );\n                                ring.push( child );\n                            }\n\n                            /*\n                            //same color: reset and reset children, no push\n                            if( color[child] == myColor )\n                            {\n                                color[child] = myColor+1;\n                                for(IndexType kk = ia[child]; kk < ia[child+1]; kk++)\n                                {\n                                    //same restrictions for setting colors apply here:\n                                    //stay in aggregate, only set outgoing edges, only set uncolored or same level\n                                    IndexType grandchild = ja[kk];\n                                    if( agg[grandchild] != aggregate || !outgoing_edge( ia, ja, aa, child, kk, blocksize ) )\n                                        continue;\n\n                                    if( color[grandchild] == -1 )\n                                    {\n                                        color[grandchild] = myColor+2;\n                                        ring.push( grandchild );\n                                    }\n                                    if( color[grandchild] == myColor+1 )\n                                        color[grandchild] = myColor+2;\n                                }\n                            }\n                            */\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid repair( const IndexType *ia, const IndexType *ja, const ValueType *aa, const IndexType *ria, const IndexType *rja, const IndexType *agg, IndexType *color, IndexType numAggregates, int buffersize, int blocksize )\n{\n    extern __shared__ IndexType tr_smem[];\n    Buffer<IndexType> ring;\n    ring.buffer = &tr_smem[0];\n    ring.buffersize = buffersize;\n    ring.loptr = 0;\n    ring.hiptr = 0;\n    ring.offset = threadIdx.x * buffersize;\n    int aggregate = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if ( aggregate < numAggregates )\n    {\n        bool nodesLeft = true;\n\n        for (int allowed_incoming_edges = 0; nodesLeft; allowed_incoming_edges++)\n        {\n            nodesLeft = false;\n\n            for (IndexType ii = ria[aggregate]; ii < ria[aggregate + 1]; ii++)\n            {\n                //find possible root node for modified BFS coloring\n                IndexType node = rja[ii];\n                int found_edges = 0;\n\n                //not colored yet\n                if ( color[node] == -1 )\n                {\n                    for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                    {\n                        if ( agg[ja[jj]] == aggregate && !outgoing_edge( ia, ja, aa, node, jj, blocksize ) )\n                        {\n                            found_edges++;\n                        }\n                    }\n\n                    nodesLeft = true;\n                }\n                else\n                {\n                    found_edges = allowed_incoming_edges + 1;\n                }\n\n                //start modified BFS\n                if ( found_edges <= allowed_incoming_edges )\n                {\n                    color[node] = 0; //TODO: make the node look around for already set neighbors first\n                    ring.push(node);\n\n                    while ( ring.size() > 0 )\n                    {\n                        //this node is already colored.\n                        node = ring.pop();\n                        int myInitialColor = color[node];\n                        //traverse all neighbors to determin minimum own color\n                        int myColor = myInitialColor;\n\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType j = ja[jj];\n\n                            if ( color[j] == myColor && j != node)\n                            {\n                                //try next color\n                                myColor++;\n                                jj = ia[node];\n                            }\n                        }\n\n                        //traverse all children\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType child = ja[jj];\n\n                            if ( agg[child] != aggregate || //only set colors in own aggregate\n                                    !outgoing_edge( ia, ja, aa, node, jj, blocksize )) //only process outgoing edges\n                            {\n                                continue;\n                            }\n\n                            //unset -> set and push\n                            if ( color[child] == -1)\n                            {\n                                color[child] = myColor + 1;\n                                ring.push( child );\n                            }\n\n                            if ( color[child] >= myInitialColor )\n                            {\n                                color[child] = myColor + 1; //max( myColor+1, color[child] );\n                                ring.push( child );\n                            }\n\n                            /*\n                            //same color: reset and reset children, no push\n                            if( color[child] == myColor )\n                            {\n                                color[child] = myColor+1;\n                                for(IndexType kk = ia[child]; kk < ia[child+1]; kk++)\n                                {\n                                    //same restrictions for setting colors apply here:\n                                    //stay in aggregate, only set outgoing edges, only set uncolored or same level\n                                    IndexType grandchild = ja[kk];\n                                    if( agg[grandchild] != aggregate || !outgoing_edge( ia, ja, aa, child, kk, blocksize ) )\n                                        continue;\n\n                                    if( color[grandchild] == -1 )\n                                    {\n                                        color[grandchild] = myColor+2;\n                                        ring.push( grandchild );\n                                    }\n                                    if( color[grandchild] == myColor+1 )\n                                        color[grandchild] = myColor+2;\n                                }\n                            }\n                            */\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n}//locally_downwind_kernels namepsace\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nLocallyDownwindColoringBase<T_Config>::LocallyDownwindColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    this->m_num_colors = cfg.AMG_Config::template getParameter<int>(\"num_colors\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid LocallyDownwindColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    //wait for colorMatrixUsingAggregates to be called\n    std::cout << \"coloring denied\" << std::endl;\n    return;\n}\n\ntemplate<class TConfig>\nvoid LocallyDownwindColoringBase<TConfig>::colorMatrixUsingAggregates(Matrix<TConfig> &A, IVector &R_row_offsets, IVector &R_col_indices, IVector &aggregates )\n{\n#define CPU_VERSION\n#ifdef CPU_VERSION\n    IndexType numRows = A.get_num_rows();\n    IndexType nnz = A.get_num_nz();\n    int blockdim = A.get_block_dimx() * A.get_block_dimy();\n    //allocate memory on host\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[A.col_indices.size()];\n    ValueType *aa = new ValueType[nnz * A.get_block_dimx()*A.get_block_dimy()];\n    IndexType *ria = new IndexType[R_row_offsets.size()];\n    IndexType *rja = new IndexType[R_col_indices.size()];\n    IndexType *agg = new IndexType[aggregates.size()];\n    IndexType *color = new IndexType[numRows];\n    //copy data from device to host\n    cudaMemcpy(ia, A.row_offsets.raw(), (numRows + 1)*sizeof(IndexType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy(ja, A.col_indices.raw(), A.col_indices.size()*sizeof(IndexType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy(aa, A.values.raw(), nnz * blockdim * sizeof(ValueType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy(ria, R_row_offsets.raw(), R_row_offsets.size()*sizeof(IndexType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy(rja, R_col_indices.raw(), R_col_indices.size()*sizeof(IndexType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy(agg, aggregates.raw(), aggregates.size()*sizeof(IndexType), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        color[i] = -1;\n    }\n\n    //color aggregate by aggregate\n    for (IndexType aggregate = 0; aggregate < R_row_offsets.size() - 1; aggregate++)\n    {\n        std::queue<IndexType> q;\n        bool nodesLeft = true;\n\n        while ( nodesLeft )\n        {\n            //find uncolored node to start with (i.e. with minimum in degree)\n            IndexType min_in_degree = numRows;\n            IndexType next_node = -1;\n            nodesLeft = false;\n\n            for (IndexType node_index = ria[aggregate]; node_index < ria[aggregate + 1]; node_index++)\n            {\n                IndexType node = rja[node_index];\n                int in_degree = 0;\n\n                if ( color[node] == -1 )\n                {\n                    nodesLeft = true;\n\n                    for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                    {\n                        if ( agg[ja[ii]] == aggregate && !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, ii, blockdim ) )\n                        {\n                            in_degree++;\n                        }\n                    }\n\n                    if ( in_degree < min_in_degree )\n                    {\n                        min_in_degree = in_degree;\n                        next_node = node;\n                    }\n                }\n            }\n\n            if (!nodesLeft)\n            {\n                break;\n            }\n\n            //start modified BFS\n            color[next_node] = 0;\n            q.push( next_node );\n\n            while ( q.size() > 0 )\n            {\n                IndexType node = q.front();\n                q.pop();\n                int myInitialColor = color[node];\n                int myColor = myInitialColor;\n\n                //find valid color for this node\n                for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                {\n                    if ( color[ja[ii]] == myColor && ja[ii] != node && (agg[ja[ii]] != aggregate || !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, ii, blockdim )) )\n                    {\n                        myColor++;\n                        ii = ia[node] - 1;\n                    }\n                }\n\n                //set color\n                color[node] = myColor;\n\n                //update children\n                for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                {\n                    IndexType child = ja[jj];\n\n                    if ( agg[child] != aggregate || //only set colors in own aggregate\n                            !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, jj, blockdim ) ||//only process outgoing edges\n                            child == node)  // don't mess with yourself\n                    {\n                        continue;\n                    }\n\n                    //unset -> set and push\n                    if ( color[child] >= myInitialColor )\n                    {\n                        color[child] = max( myColor + 1, color[child] );\n                        q.push( child );\n                    }\n\n                    if ( color[child] == -1)\n                    {\n                        color[child] = myColor + 1;\n                        q.push( child );\n                    }\n                }\n            }\n        }\n    }\n\n    //copy back results\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n    cudaMemcpy( this->m_row_colors.raw(), color, numRows * sizeof(IndexType), cudaMemcpyHostToDevice );\n    cudaCheckError();\n    //free all the others\n    delete [] ia;\n    delete [] ja;\n    delete [] aa;\n    delete [] agg;\n    delete [] ria;\n    delete [] rja;\n    delete [] color;\n#else\n    std::cout << \"coloring with aggregate information\" << std::endl;\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if    (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        IndexType numRows = A.get_num_rows();\n        IndexType numAggregates = R_row_offsets.size() - 1;\n        int blocksize = A.get_block_dimx() * A.get_block_dimy();\n        int max_aggregate_size = 100;\n        const int threads_per_block = 64;\n        const int num_blocks = (numAggregates - 1) / threads_per_block + 1;\n        const int smem_size = max_aggregate_size * threads_per_block * sizeof(IndexType);\n        this->m_row_colors.resize( numRows );\n        thrust_wrapper::fill( this->m_row_colors.begin(), this->m_row_colors.end(), -1 );\n        cudaCheckError();\n        std::cout << \"start coloring kernel\" << std::endl;\n        locally_downwind_kernels::traverse <<< num_blocks, threads_per_block, smem_size>>>( A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                R_row_offsets.raw(),\n                R_col_indices.raw(),\n                aggregates.raw(),\n                this->m_row_colors.raw(),\n                numAggregates,\n                max_aggregate_size,\n                blocksize);\n                cudaCheckError();\n        cudaDeviceSynchronize();\n        cudaCheckError();\n        std::cout << \"uncolored nodes: \" << amgx::thrust::count( this->m_row_colors.begin(), this->m_row_colors.end(), -1 ) << std::endl;\n    }\n    else\n    {\n        FatalError(\"Locally Downwind coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n#endif\n}\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid LocallyDownwindColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    FatalError(\"This method is no longer needed\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n\n    const int threads_per_block = 64;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows-1)/threads_per_block + 1);\n\n    locally_downwind_kernels::colorRowsKernel<IndexType> <<<num_blocks,threads_per_block>>>(row_colors_ptr, this->m_num_colors, num_rows);\n    cudaCheckError();\n    */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid LocallyDownwindColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented locally downwind coloring for host\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class LocallyDownwindColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class LocallyDownwindColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/locally_downwind.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <queue>\n#include <iostream>\n\nnamespace amgx\n{\nnamespace locally_downwind_kernels\n{\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(IndexType *row_colors, const int num_colors, const int num_rows)\n{\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += gridDim.x * blockDim.x)\n    {\n        row_colors[i] = i % num_colors;\n    }\n}\n\n\n//is this edge and outgoing edge?\ntemplate <typename IndexType, typename ValueType>\n__host__ __device__\nbool outgoing_edge( const IndexType *ia, const IndexType *ja, const ValueType *aa, IndexType i, IndexType ii, int blocksize )\n{\n    IndexType j = ja[ii];\n    ValueType weight = 0.0;\n\n    for (IndexType iii = ii * blocksize; iii < (ii + 1)*blocksize; iii++)\n    {\n        weight += (aa[iii] * aa[iii]);\n    }\n\n    for (IndexType jj = ia[j]; jj < ia[j + 1]; jj++)\n    {\n        if ( ja[jj] == i )\n        {\n            ValueType counter_weight = 0.0;\n\n            for (IndexType jjj = jj * blocksize; jjj < (jj + 1)*blocksize; jjj++)\n            {\n                counter_weight += (aa[jjj] * aa[jjj]);\n            }\n\n            return (weight >= counter_weight);\n        }\n    }\n\n    return true;\n}\n\ntemplate <typename IndexType>\nstruct Buffer\n{\n    IndexType *buffer;\n    int buffersize;\n    int loptr;\n    int hiptr;\n    int offset;\n\n    __device__ int size();\n    __device__ IndexType pop();\n    __device__ void push( IndexType node );\n};\n\ntemplate <typename IndexType>\n__device__\nIndexType Buffer<IndexType>::pop()\n{\n    int pos = loptr % buffersize;\n    loptr++;\n    return buffer[offset + pos];\n}\n\ntemplate <typename IndexType>\n__device__\nvoid Buffer<IndexType>::push(IndexType element )\n{\n    buffer[offset + hiptr] = element;\n    hiptr = (hiptr + 1) % buffersize;\n}\n\ntemplate <typename IndexType>\n__device__\nint Buffer<IndexType>::size()\n{\n    return (hiptr - loptr) % buffersize;\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid traverse( const IndexType *ia, const IndexType *ja, const ValueType *aa, const IndexType *ria, const IndexType *rja, const IndexType *agg, IndexType *color, IndexType numAggregates, int buffersize, int blocksize )\n{\n    extern __shared__ IndexType tr_smem[];\n    Buffer<IndexType> ring;\n    ring.buffer = &tr_smem[0];\n    ring.buffersize = buffersize;\n    ring.loptr = 0;\n    ring.hiptr = 0;\n    ring.offset = threadIdx.x * buffersize;\n    int aggregate = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if ( aggregate < numAggregates )\n    {\n        bool nodesLeft = true;\n\n        for (int allowed_incoming_edges = 0; nodesLeft; allowed_incoming_edges++)\n        {\n            nodesLeft = false;\n\n            for (IndexType ii = ria[aggregate]; ii < ria[aggregate + 1]; ii++)\n            {\n                //find possible root node for modified BFS coloring\n                IndexType node = rja[ii];\n                int found_edges = 0;\n\n                //not colored yet\n                if ( color[node] == -1 )\n                {\n                    for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                    {\n                        if ( agg[ja[jj]] == aggregate && !outgoing_edge( ia, ja, aa, node, jj, blocksize ) )\n                        {\n                            found_edges++;\n                        }\n                    }\n\n                    nodesLeft = true;\n                }\n                else\n                {\n                    found_edges = allowed_incoming_edges + 1;\n                }\n\n                //start modified BFS\n                if ( found_edges <= allowed_incoming_edges )\n                {\n                    color[node] = 0; //TODO: make the node look around for already set neighbors first\n                    ring.push(node);\n\n                    while ( ring.size() > 0 )\n                    {\n                        //this node is already colored.\n                        node = ring.pop();\n                        int myInitialColor = color[node];\n                        //traverse all neighbors to determin minimum own color\n                        int myColor = myInitialColor;\n\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType j = ja[jj];\n\n                            if ( color[j] == myColor && j != node)\n                            {\n                                //try next color\n                                myColor++;\n                                jj = ia[node];\n                            }\n                        }\n\n                        //repair own color\n                        //color[node] = myColor;\n\n                        //traverse all children\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType child = ja[jj];\n\n                            if ( agg[child] != aggregate || //only set colors in own aggregate\n                                    !outgoing_edge( ia, ja, aa, node, jj, blocksize )) //only process outgoing edges\n                            {\n                                continue;\n                            }\n\n                            //unset -> set and push\n                            if ( color[child] == -1)\n                            {\n                                color[child] = myColor + 1;\n                                ring.push( child );\n                            }\n\n                            if ( color[child] >= myInitialColor )\n                            {\n                                color[child] = myColor + 1; //max( myColor+1, color[child] );\n                                ring.push( child );\n                            }\n\n                            /*\n                            //same color: reset and reset children, no push\n                            if( color[child] == myColor )\n                            {\n                                color[child] = myColor+1;\n                                for(IndexType kk = ia[child]; kk < ia[child+1]; kk++)\n                                {\n                                    //same restrictions for setting colors apply here:\n                                    //stay in aggregate, only set outgoing edges, only set uncolored or same level\n                                    IndexType grandchild = ja[kk];\n                                    if( agg[grandchild] != aggregate || !outgoing_edge( ia, ja, aa, child, kk, blocksize ) )\n                                        continue;\n\n                                    if( color[grandchild] == -1 )\n                                    {\n                                        color[grandchild] = myColor+2;\n                                        ring.push( grandchild );\n                                    }\n                                    if( color[grandchild] == myColor+1 )\n                                        color[grandchild] = myColor+2;\n                                }\n                            }\n                            */\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid repair( const IndexType *ia, const IndexType *ja, const ValueType *aa, const IndexType *ria, const IndexType *rja, const IndexType *agg, IndexType *color, IndexType numAggregates, int buffersize, int blocksize )\n{\n    extern __shared__ IndexType tr_smem[];\n    Buffer<IndexType> ring;\n    ring.buffer = &tr_smem[0];\n    ring.buffersize = buffersize;\n    ring.loptr = 0;\n    ring.hiptr = 0;\n    ring.offset = threadIdx.x * buffersize;\n    int aggregate = threadIdx.x + blockDim.x * blockIdx.x;\n\n    if ( aggregate < numAggregates )\n    {\n        bool nodesLeft = true;\n\n        for (int allowed_incoming_edges = 0; nodesLeft; allowed_incoming_edges++)\n        {\n            nodesLeft = false;\n\n            for (IndexType ii = ria[aggregate]; ii < ria[aggregate + 1]; ii++)\n            {\n                //find possible root node for modified BFS coloring\n                IndexType node = rja[ii];\n                int found_edges = 0;\n\n                //not colored yet\n                if ( color[node] == -1 )\n                {\n                    for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                    {\n                        if ( agg[ja[jj]] == aggregate && !outgoing_edge( ia, ja, aa, node, jj, blocksize ) )\n                        {\n                            found_edges++;\n                        }\n                    }\n\n                    nodesLeft = true;\n                }\n                else\n                {\n                    found_edges = allowed_incoming_edges + 1;\n                }\n\n                //start modified BFS\n                if ( found_edges <= allowed_incoming_edges )\n                {\n                    color[node] = 0; //TODO: make the node look around for already set neighbors first\n                    ring.push(node);\n\n                    while ( ring.size() > 0 )\n                    {\n                        //this node is already colored.\n                        node = ring.pop();\n                        int myInitialColor = color[node];\n                        //traverse all neighbors to determin minimum own color\n                        int myColor = myInitialColor;\n\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType j = ja[jj];\n\n                            if ( color[j] == myColor && j != node)\n                            {\n                                //try next color\n                                myColor++;\n                                jj = ia[node];\n                            }\n                        }\n\n                        //traverse all children\n                        for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                        {\n                            IndexType child = ja[jj];\n\n                            if ( agg[child] != aggregate || //only set colors in own aggregate\n                                    !outgoing_edge( ia, ja, aa, node, jj, blocksize )) //only process outgoing edges\n                            {\n                                continue;\n                            }\n\n                            //unset -> set and push\n                            if ( color[child] == -1)\n                            {\n                                color[child] = myColor + 1;\n                                ring.push( child );\n                            }\n\n                            if ( color[child] >= myInitialColor )\n                            {\n                                color[child] = myColor + 1; //max( myColor+1, color[child] );\n                                ring.push( child );\n                            }\n\n                            /*\n                            //same color: reset and reset children, no push\n                            if( color[child] == myColor )\n                            {\n                                color[child] = myColor+1;\n                                for(IndexType kk = ia[child]; kk < ia[child+1]; kk++)\n                                {\n                                    //same restrictions for setting colors apply here:\n                                    //stay in aggregate, only set outgoing edges, only set uncolored or same level\n                                    IndexType grandchild = ja[kk];\n                                    if( agg[grandchild] != aggregate || !outgoing_edge( ia, ja, aa, child, kk, blocksize ) )\n                                        continue;\n\n                                    if( color[grandchild] == -1 )\n                                    {\n                                        color[grandchild] = myColor+2;\n                                        ring.push( grandchild );\n                                    }\n                                    if( color[grandchild] == myColor+1 )\n                                        color[grandchild] = myColor+2;\n                                }\n                            }\n                            */\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n}//locally_downwind_kernels namepsace\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nLocallyDownwindColoringBase<T_Config>::LocallyDownwindColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    this->m_num_colors = cfg.AMG_Config::template getParameter<int>(\"num_colors\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid LocallyDownwindColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    //wait for colorMatrixUsingAggregates to be called\n    std::cout << \"coloring denied\" << std::endl;\n    return;\n}\n\ntemplate<class TConfig>\nvoid LocallyDownwindColoringBase<TConfig>::colorMatrixUsingAggregates(Matrix<TConfig> &A, IVector &R_row_offsets, IVector &R_col_indices, IVector &aggregates )\n{\n#define CPU_VERSION\n#ifdef CPU_VERSION\n    IndexType numRows = A.get_num_rows();\n    IndexType nnz = A.get_num_nz();\n    int blockdim = A.get_block_dimx() * A.get_block_dimy();\n    //allocate memory on host\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[A.col_indices.size()];\n    ValueType *aa = new ValueType[nnz * A.get_block_dimx()*A.get_block_dimy()];\n    IndexType *ria = new IndexType[R_row_offsets.size()];\n    IndexType *rja = new IndexType[R_col_indices.size()];\n    IndexType *agg = new IndexType[aggregates.size()];\n    IndexType *color = new IndexType[numRows];\n    //copy data from device to host\n    hipMemcpy(ia, A.row_offsets.raw(), (numRows + 1)*sizeof(IndexType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy(ja, A.col_indices.raw(), A.col_indices.size()*sizeof(IndexType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy(aa, A.values.raw(), nnz * blockdim * sizeof(ValueType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy(ria, R_row_offsets.raw(), R_row_offsets.size()*sizeof(IndexType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy(rja, R_col_indices.raw(), R_col_indices.size()*sizeof(IndexType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy(agg, aggregates.raw(), aggregates.size()*sizeof(IndexType), hipMemcpyDeviceToHost );\n    cudaCheckError();\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        color[i] = -1;\n    }\n\n    //color aggregate by aggregate\n    for (IndexType aggregate = 0; aggregate < R_row_offsets.size() - 1; aggregate++)\n    {\n        std::queue<IndexType> q;\n        bool nodesLeft = true;\n\n        while ( nodesLeft )\n        {\n            //find uncolored node to start with (i.e. with minimum in degree)\n            IndexType min_in_degree = numRows;\n            IndexType next_node = -1;\n            nodesLeft = false;\n\n            for (IndexType node_index = ria[aggregate]; node_index < ria[aggregate + 1]; node_index++)\n            {\n                IndexType node = rja[node_index];\n                int in_degree = 0;\n\n                if ( color[node] == -1 )\n                {\n                    nodesLeft = true;\n\n                    for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                    {\n                        if ( agg[ja[ii]] == aggregate && !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, ii, blockdim ) )\n                        {\n                            in_degree++;\n                        }\n                    }\n\n                    if ( in_degree < min_in_degree )\n                    {\n                        min_in_degree = in_degree;\n                        next_node = node;\n                    }\n                }\n            }\n\n            if (!nodesLeft)\n            {\n                break;\n            }\n\n            //start modified BFS\n            color[next_node] = 0;\n            q.push( next_node );\n\n            while ( q.size() > 0 )\n            {\n                IndexType node = q.front();\n                q.pop();\n                int myInitialColor = color[node];\n                int myColor = myInitialColor;\n\n                //find valid color for this node\n                for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                {\n                    if ( color[ja[ii]] == myColor && ja[ii] != node && (agg[ja[ii]] != aggregate || !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, ii, blockdim )) )\n                    {\n                        myColor++;\n                        ii = ia[node] - 1;\n                    }\n                }\n\n                //set color\n                color[node] = myColor;\n\n                //update children\n                for (IndexType jj = ia[node]; jj < ia[node + 1]; jj++)\n                {\n                    IndexType child = ja[jj];\n\n                    if ( agg[child] != aggregate || //only set colors in own aggregate\n                            !locally_downwind_kernels::outgoing_edge( ia, ja, aa, node, jj, blockdim ) ||//only process outgoing edges\n                            child == node)  // don't mess with yourself\n                    {\n                        continue;\n                    }\n\n                    //unset -> set and push\n                    if ( color[child] >= myInitialColor )\n                    {\n                        color[child] = max( myColor + 1, color[child] );\n                        q.push( child );\n                    }\n\n                    if ( color[child] == -1)\n                    {\n                        color[child] = myColor + 1;\n                        q.push( child );\n                    }\n                }\n            }\n        }\n    }\n\n    //copy back results\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n    hipMemcpy( this->m_row_colors.raw(), color, numRows * sizeof(IndexType), hipMemcpyHostToDevice );\n    cudaCheckError();\n    //free all the others\n    delete [] ia;\n    delete [] ja;\n    delete [] aa;\n    delete [] agg;\n    delete [] ria;\n    delete [] rja;\n    delete [] color;\n#else\n    std::cout << \"coloring with aggregate information\" << std::endl;\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if    (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        IndexType numRows = A.get_num_rows();\n        IndexType numAggregates = R_row_offsets.size() - 1;\n        int blocksize = A.get_block_dimx() * A.get_block_dimy();\n        int max_aggregate_size = 100;\n        const int threads_per_block = 64;\n        const int num_blocks = (numAggregates - 1) / threads_per_block + 1;\n        const int smem_size = max_aggregate_size * threads_per_block * sizeof(IndexType);\n        this->m_row_colors.resize( numRows );\n        thrust_wrapper::fill( this->m_row_colors.begin(), this->m_row_colors.end(), -1 );\n        cudaCheckError();\n        std::cout << \"start coloring kernel\" << std::endl;\n        locally_downwind_kernels::traverse <<< num_blocks, threads_per_block, smem_size>>>( A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                R_row_offsets.raw(),\n                R_col_indices.raw(),\n                aggregates.raw(),\n                this->m_row_colors.raw(),\n                numAggregates,\n                max_aggregate_size,\n                blocksize);\n                cudaCheckError();\n        hipDeviceSynchronize();\n        cudaCheckError();\n        std::cout << \"uncolored nodes: \" << amgx::thrust::count( this->m_row_colors.begin(), this->m_row_colors.end(), -1 ) << std::endl;\n    }\n    else\n    {\n        FatalError(\"Locally Downwind coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n#endif\n}\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid LocallyDownwindColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    FatalError(\"This method is no longer needed\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n\n    const int threads_per_block = 64;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows-1)/threads_per_block + 1);\n\n    locally_downwind_kernels::colorRowsKernel<IndexType> <<<num_blocks,threads_per_block>>>(row_colors_ptr, this->m_num_colors, num_rows);\n    cudaCheckError();\n    */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid LocallyDownwindColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented locally downwind coloring for host\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class LocallyDownwindColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class LocallyDownwindColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/locally_downwind.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/multi_hash.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\n__host__ __device__ unsigned int hash(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x < 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\ntemplate <typename IndexType, int num_hash>\n__global__\nvoid colorRowsMultiHashKernel_1step(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const int num_rows, const int next_color)\n{\n    int hash_j;\n    int hash_i;\n    int my_colors[num_hash];\n    int my_row_color;\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += blockDim.x * gridDim.x)\n    {\n        int num_possible_colors = 0;\n        bool max_i, min_i;\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n\n        //#pragma unroll //can't unroll because it is not the innermost loop\n        for (int k = 0; k < num_hash; k++)\n        {\n            max_i = true;\n            min_i = true;\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >= num_rows) { continue; }\n\n                hash_j = hash(j, k);\n                hash_i = hash(i, k);\n\n                // There is an uncolored neighbour that is greater\n                if ( hash_j > hash_i)\n                {\n                    max_i = false;\n                }\n\n                // There is an uncolored neighbour that is smaller\n                if (hash_j < hash_i)\n                {\n                    min_i = false;\n                }\n            }\n\n            // If not colored or colored but coin flip decides color should be changed\n            if (max_i)\n            {\n                my_colors[num_possible_colors++] = 2 * k;\n            }\n            else if (min_i)\n            {\n                my_colors[num_possible_colors++] = 2 * k + 1;\n            }\n        }\n\n        if (num_possible_colors)\n        {\n            int rand_pick = hash(i, 0) % num_possible_colors;\n            my_row_color = my_colors[rand_pick];\n        }\n        else\n        {\n            my_row_color = 2 * num_hash;\n        }\n\n        row_colors[i] = my_row_color;\n    }\n}\n\ntemplate <typename IndexType, int num_hash>\n__global__\nvoid colorRowsMultiHashKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const int num_rows, const int next_color, const int seed)\n{\n    unsigned int i_rand[num_hash];\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += blockDim.x * gridDim.x)\n    {\n        // skip if previously matched\n        if (row_colors[i] != -1) { continue; }\n\n        int not_min = 0, not_max = 0;\n        int t;\n\n#pragma unroll\n        for (t = 0; t < num_hash; t++)\n        {\n            i_rand[t] = hash(i, seed + 1043 * t);\n        }\n\n        // have we been proved to be not min or max\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n        int possible_colors = 2 * num_hash;\n\n        for (int r = row_start; r < row_end; r++)\n        {\n            int j = A_column_indices[r];\n\n            // skip diagonal\n            if (j == i || j >= num_rows)\n            {\n                continue;\n            }\n\n            int j_color = row_colors[j];\n\n            if (j_color != -1 && j_color < next_color)\n            {\n                continue;\n            }\n\n#pragma unroll\n            for (t = 0; t < num_hash; t++)\n            {\n                unsigned int j_rand = hash(j, seed + 1043 * t);\n\n                // bail if any neighbor is greater\n                if (i_rand[t] <= j_rand && !(not_max & (0x1 << t)  ))\n                {\n                    not_max |= (0x1 << t);\n                    possible_colors--;\n                }\n\n                if (i_rand[t] >= j_rand && !(not_min & (0x1 << t)  ))\n                {\n                    not_min |= (0x1 << t);\n                    possible_colors--;\n                }\n            }\n\n            if (possible_colors == 0)\n            {\n                break;\n            }\n        }\n\n        if (possible_colors == 0) { continue; }\n\n        // made it here, so possible_colors > 0\n        // pick one of the possible colors pseudo-randomly\n        int col_id = i % possible_colors;\n        int this_col_id = 0;\n\n        for (t = 0; t < num_hash; t++)\n        {\n            if (!(not_min & (0x1 << t) ) && col_id == this_col_id)\n            {\n                row_colors[i] = 2 * t + next_color;\n                return;\n            }\n\n            this_col_id += !(not_min & (0x1 << t));\n\n            if (!(not_max & (0x1 << t) ) && col_id == this_col_id)\n            {\n                row_colors[i] = 2 * t + 1 + next_color;\n                return;\n            }\n\n            this_col_id += !(not_max & (0x1 << t));\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nMultiHashMatrixColoringBase<T_Config>::MultiHashMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0.;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    max_num_hash = cfg.AMG_Config::template getParameter<int>(\"max_num_hash\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid MultiHashMatrixColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else\n    {\n        FatalError(\"Multi-hash coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n}\n\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiHashMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, -1);\n    cudaCheckError();\n    const IndexType *A_row_offsets_ptr    = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / threads_per_block + 1);\n    this->m_num_colors = 0;\n    // Heuristic for setting the number of hash function to use\n    int avg_nonzero = 1.5 * A.row_offsets[num_rows] / num_rows;\n    this->num_hash = min(avg_nonzero, this->max_num_hash);\n    int next_color = 0;\n    int it = 0;\n    int seed = 1012;\n\n    if (avg_nonzero != 0)\n    {\n        for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n        {\n            it++;\n\n            // Assign all nodes to 0 colors by default\n            if (this->num_hash == 1)\n            {\n                colorRowsMultiHashKernel<IndexType, 1> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 2)\n            {\n                colorRowsMultiHashKernel<IndexType, 2> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 3)\n            {\n                colorRowsMultiHashKernel<IndexType, 3> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 4)\n            {\n                colorRowsMultiHashKernel<IndexType, 4> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 5)\n            {\n                colorRowsMultiHashKernel<IndexType, 5> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 6)\n            {\n                colorRowsMultiHashKernel<IndexType, 6> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 7)\n            {\n                colorRowsMultiHashKernel<IndexType, 7> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 8)\n            {\n                colorRowsMultiHashKernel<IndexType, 8> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 9)\n            {\n                colorRowsMultiHashKernel<IndexType, 9> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 10)\n            {\n                colorRowsMultiHashKernel<IndexType, 10> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 11)\n            {\n                colorRowsMultiHashKernel<IndexType, 11> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 12)\n            {\n                colorRowsMultiHashKernel<IndexType, 12> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 13)\n            {\n                colorRowsMultiHashKernel<IndexType, 13> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 14)\n            {\n                colorRowsMultiHashKernel<IndexType, 14> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 15)\n            {\n                colorRowsMultiHashKernel<IndexType, 15> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 16)\n            {\n                colorRowsMultiHashKernel<IndexType, 16> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 17)\n            {\n                colorRowsMultiHashKernel<IndexType, 17> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 18)\n            {\n                colorRowsMultiHashKernel<IndexType, 18> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 19)\n            {\n                colorRowsMultiHashKernel<IndexType, 19> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 20)\n            {\n                colorRowsMultiHashKernel<IndexType, 20> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 21)\n            {\n                colorRowsMultiHashKernel<IndexType, 21> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 22)\n            {\n                colorRowsMultiHashKernel<IndexType, 22> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 23)\n            {\n                colorRowsMultiHashKernel<IndexType, 23> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 24)\n            {\n                colorRowsMultiHashKernel<IndexType, 24> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 25)\n            {\n                colorRowsMultiHashKernel<IndexType, 25> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash > 25)\n            {\n                FatalError(\"Multi-hash coloring algorithm currently can't handle more than 25 hash functions\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n\n            cudaCheckError();\n            seed = hash(seed, 0);\n            next_color += 2 * this->num_hash;\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_less_than_zero() );\n            cudaCheckError();\n        }\n    }\n    else\n    {\n        thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n        cudaCheckError();\n    }\n\n    this->m_num_colors = *amgx::thrust::max_element(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows) + 1;\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiHashMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MultiHash matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class MultiHashMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MultiHashMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/multi_hash.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\n__host__ __device__ unsigned int hash(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x < 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\ntemplate <typename IndexType, int num_hash>\n__global__\nvoid colorRowsMultiHashKernel_1step(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const int num_rows, const int next_color)\n{\n    int hash_j;\n    int hash_i;\n    int my_colors[num_hash];\n    int my_row_color;\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += blockDim.x * gridDim.x)\n    {\n        int num_possible_colors = 0;\n        bool max_i, min_i;\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n\n        //#pragma unroll //can't unroll because it is not the innermost loop\n        for (int k = 0; k < num_hash; k++)\n        {\n            max_i = true;\n            min_i = true;\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >= num_rows) { continue; }\n\n                hash_j = hash(j, k);\n                hash_i = hash(i, k);\n\n                // There is an uncolored neighbour that is greater\n                if ( hash_j > hash_i)\n                {\n                    max_i = false;\n                }\n\n                // There is an uncolored neighbour that is smaller\n                if (hash_j < hash_i)\n                {\n                    min_i = false;\n                }\n            }\n\n            // If not colored or colored but coin flip decides color should be changed\n            if (max_i)\n            {\n                my_colors[num_possible_colors++] = 2 * k;\n            }\n            else if (min_i)\n            {\n                my_colors[num_possible_colors++] = 2 * k + 1;\n            }\n        }\n\n        if (num_possible_colors)\n        {\n            int rand_pick = hash(i, 0) % num_possible_colors;\n            my_row_color = my_colors[rand_pick];\n        }\n        else\n        {\n            my_row_color = 2 * num_hash;\n        }\n\n        row_colors[i] = my_row_color;\n    }\n}\n\ntemplate <typename IndexType, int num_hash>\n__global__\nvoid colorRowsMultiHashKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const int num_rows, const int next_color, const int seed)\n{\n    unsigned int i_rand[num_hash];\n\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += blockDim.x * gridDim.x)\n    {\n        // skip if previously matched\n        if (row_colors[i] != -1) { continue; }\n\n        int not_min = 0, not_max = 0;\n        int t;\n\n#pragma unroll\n        for (t = 0; t < num_hash; t++)\n        {\n            i_rand[t] = hash(i, seed + 1043 * t);\n        }\n\n        // have we been proved to be not min or max\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n        int possible_colors = 2 * num_hash;\n\n        for (int r = row_start; r < row_end; r++)\n        {\n            int j = A_column_indices[r];\n\n            // skip diagonal\n            if (j == i || j >= num_rows)\n            {\n                continue;\n            }\n\n            int j_color = row_colors[j];\n\n            if (j_color != -1 && j_color < next_color)\n            {\n                continue;\n            }\n\n#pragma unroll\n            for (t = 0; t < num_hash; t++)\n            {\n                unsigned int j_rand = hash(j, seed + 1043 * t);\n\n                // bail if any neighbor is greater\n                if (i_rand[t] <= j_rand && !(not_max & (0x1 << t)  ))\n                {\n                    not_max |= (0x1 << t);\n                    possible_colors--;\n                }\n\n                if (i_rand[t] >= j_rand && !(not_min & (0x1 << t)  ))\n                {\n                    not_min |= (0x1 << t);\n                    possible_colors--;\n                }\n            }\n\n            if (possible_colors == 0)\n            {\n                break;\n            }\n        }\n\n        if (possible_colors == 0) { continue; }\n\n        // made it here, so possible_colors > 0\n        // pick one of the possible colors pseudo-randomly\n        int col_id = i % possible_colors;\n        int this_col_id = 0;\n\n        for (t = 0; t < num_hash; t++)\n        {\n            if (!(not_min & (0x1 << t) ) && col_id == this_col_id)\n            {\n                row_colors[i] = 2 * t + next_color;\n                return;\n            }\n\n            this_col_id += !(not_min & (0x1 << t));\n\n            if (!(not_max & (0x1 << t) ) && col_id == this_col_id)\n            {\n                row_colors[i] = 2 * t + 1 + next_color;\n                return;\n            }\n\n            this_col_id += !(not_max & (0x1 << t));\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nMultiHashMatrixColoringBase<T_Config>::MultiHashMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0.;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    max_num_hash = cfg.AMG_Config::template getParameter<int>(\"max_num_hash\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid MultiHashMatrixColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else\n    {\n        FatalError(\"Multi-hash coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n}\n\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiHashMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, -1);\n    cudaCheckError();\n    const IndexType *A_row_offsets_ptr    = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min(AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / threads_per_block + 1);\n    this->m_num_colors = 0;\n    // Heuristic for setting the number of hash function to use\n    int avg_nonzero = 1.5 * A.row_offsets[num_rows] / num_rows;\n    this->num_hash = min(avg_nonzero, this->max_num_hash);\n    int next_color = 0;\n    int it = 0;\n    int seed = 1012;\n\n    if (avg_nonzero != 0)\n    {\n        for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n        {\n            it++;\n\n            // Assign all nodes to 0 colors by default\n            if (this->num_hash == 1)\n            {\n                colorRowsMultiHashKernel<IndexType, 1> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 2)\n            {\n                colorRowsMultiHashKernel<IndexType, 2> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 3)\n            {\n                colorRowsMultiHashKernel<IndexType, 3> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 4)\n            {\n                colorRowsMultiHashKernel<IndexType, 4> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 5)\n            {\n                colorRowsMultiHashKernel<IndexType, 5> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 6)\n            {\n                colorRowsMultiHashKernel<IndexType, 6> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 7)\n            {\n                colorRowsMultiHashKernel<IndexType, 7> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 8)\n            {\n                colorRowsMultiHashKernel<IndexType, 8> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 9)\n            {\n                colorRowsMultiHashKernel<IndexType, 9> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 10)\n            {\n                colorRowsMultiHashKernel<IndexType, 10> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 11)\n            {\n                colorRowsMultiHashKernel<IndexType, 11> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 12)\n            {\n                colorRowsMultiHashKernel<IndexType, 12> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 13)\n            {\n                colorRowsMultiHashKernel<IndexType, 13> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 14)\n            {\n                colorRowsMultiHashKernel<IndexType, 14> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 15)\n            {\n                colorRowsMultiHashKernel<IndexType, 15> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 16)\n            {\n                colorRowsMultiHashKernel<IndexType, 16> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 17)\n            {\n                colorRowsMultiHashKernel<IndexType, 17> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 18)\n            {\n                colorRowsMultiHashKernel<IndexType, 18> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 19)\n            {\n                colorRowsMultiHashKernel<IndexType, 19> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 20)\n            {\n                colorRowsMultiHashKernel<IndexType, 20> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 21)\n            {\n                colorRowsMultiHashKernel<IndexType, 21> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 22)\n            {\n                colorRowsMultiHashKernel<IndexType, 22> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 23)\n            {\n                colorRowsMultiHashKernel<IndexType, 23> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 24)\n            {\n                colorRowsMultiHashKernel<IndexType, 24> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash == 25)\n            {\n                colorRowsMultiHashKernel<IndexType, 25> <<< num_blocks, threads_per_block>>> (A_row_offsets_ptr, A_column_indices_ptr, row_colors_ptr, num_rows, next_color, seed);\n            }\n            else if (this->num_hash > 25)\n            {\n                FatalError(\"Multi-hash coloring algorithm currently can't handle more than 25 hash functions\", AMGX_ERR_NOT_IMPLEMENTED);\n            }\n\n            cudaCheckError();\n            seed = hash(seed, 0);\n            next_color += 2 * this->num_hash;\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_less_than_zero() );\n            cudaCheckError();\n        }\n    }\n    else\n    {\n        thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n        cudaCheckError();\n    }\n\n    this->m_num_colors = *amgx::thrust::max_element(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows) + 1;\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiHashMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MultiHash matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class MultiHashMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MultiHashMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/multi_hash.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n#include <thrust/copy.h>\n#include <thrust/count.h>\n#include <thrust/inner_product.h>\n#include <thrust/binary_search.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <error.h>\n#include <vector_thrust_allocator.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n\nnamespace amgx\n{\n\nvoid coloring_histogram(int *out_hist_host, int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::device_ptr<int> data(row_colors);\n\n    if (false) //TODO: enable, since almost always faster\n    {\n        device_vector_alloc<int> histogram(num_rows);\n        thrust_wrapper::sort<AMGX_device>(data, data + num_rows);\n        cudaCheckError();\n        amgx::thrust::counting_iterator<int> search_begin(0);\n        amgx::thrust::upper_bound(data, data + num_rows,\n                            search_begin, search_begin + num_rows,\n                            histogram.begin());\n        cudaCheckError();\n        amgx::thrust::adjacent_difference(histogram.begin(), histogram.end(), histogram.begin());\n        cudaCheckError();\n        amgx::thrust::host_vector<int> h(max_color + 1);\n        amgx::thrust::copy(histogram.begin(), histogram.begin() + max_color + 1, h.begin());\n        cudaCheckError();\n\n        for (int i = 0; i <= max_color; i++)\n        {\n            out_hist_host[i] = h[i]; //TODO avoid this copy\n        }\n    }\n    else\n    {\n        for (int i = 0; i <= max_color; i++)\n        {\n            out_hist_host[i] = amgx::thrust::count(data, data + num_rows, i);\n        }\n\n        cudaCheckError();\n    }\n}\n\n\n__global__ void permute_colors_kernel(int num_rows, int *row_colors, int *color_permutation)\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += blockDim.x * gridDim.x )\n    {\n        int color = row_colors[row_id];\n        color = __ldg(color_permutation + color);\n        row_colors[row_id] = color;\n    }\n}\n\n__global__ void reverse_colors_kernel(int num_rows, int max_color, int *row_colors)\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += blockDim.x * gridDim.x )\n    {\n        int color = row_colors[row_id];\n\n        if (color > 0)\n        {\n            //1 -> max_color\n            //max_color -> 1\n            color = max_color - color + 1;\n        }\n\n        row_colors[row_id] = color;\n    }\n}\n\nint reverse_colors(int num_rows, int max_color, int *row_colors)\n{\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    reverse_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, max_color, row_colors);\n    cudaCheckError();\n    return max_color;\n}\n\nint eliminate_null_colors(int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::host_vector<int> hist(max_color + 1, 0);\n    amgx::thrust::host_vector<int> perm(max_color + 1, 0);\n    device_vector_alloc<int> perm_d(max_color + 1, 0);\n    coloring_histogram(amgx::thrust::raw_pointer_cast(hist.data()), num_rows, max_color, row_colors);\n    int nonempty_color = 1;\n\n    for (int i = 1; i <= max_color; i++) //0 is blank\n    {\n        if (hist[i] > 0) //keep it if not empty\n        {\n            perm[i] = nonempty_color++;\n        }\n    }\n\n    perm_d = perm;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    permute_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, row_colors, amgx::thrust::raw_pointer_cast(perm_d.data()));\n    cudaCheckError();\n    return nonempty_color - 1;\n}\nint reorder_colors_by_frequency(int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::host_vector<int> hist(max_color + 1, 0);\n    amgx::thrust::host_vector<int> perm(max_color + 1, 0);\n    device_vector_alloc<int> perm_d(max_color + 1, 0);\n    coloring_histogram(amgx::thrust::raw_pointer_cast(hist.data()), num_rows, max_color, row_colors);\n    int nonempty_color = 1;\n\n    for (int i = 1; i <= max_color; i++) //0 is blank\n    {\n        if (hist[i] > 0) //keep it if not empty\n        {\n            perm[i] = nonempty_color++;\n        }\n    }\n\n    perm_d = perm;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    permute_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, row_colors, amgx::thrust::raw_pointer_cast(perm_d.data()));\n    cudaCheckError();\n    return nonempty_color - 1;\n    //return 0;\n}\n\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sort.h>\n#include <thrust/copy.h>\n#include <thrust/count.h>\n#include <thrust/inner_product.h>\n#include <thrust/binary_search.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <error.h>\n#include <vector_thrust_allocator.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n\nnamespace amgx\n{\n\nvoid coloring_histogram(int *out_hist_host, int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::device_ptr<int> data(row_colors);\n\n    if (false) //TODO: enable, since almost always faster\n    {\n        device_vector_alloc<int> histogram(num_rows);\n        thrust_wrapper::sort<AMGX_device>(data, data + num_rows);\n        cudaCheckError();\n        amgx::thrust::counting_iterator<int> search_begin(0);\n        amgx::thrust::upper_bound(data, data + num_rows,\n                            search_begin, search_begin + num_rows,\n                            histogram.begin());\n        cudaCheckError();\n        amgx::thrust::adjacent_difference(histogram.begin(), histogram.end(), histogram.begin());\n        cudaCheckError();\n        amgx::thrust::host_vector<int> h(max_color + 1);\n        amgx::thrust::copy(histogram.begin(), histogram.begin() + max_color + 1, h.begin());\n        cudaCheckError();\n\n        for (int i = 0; i <= max_color; i++)\n        {\n            out_hist_host[i] = h[i]; //TODO avoid this copy\n        }\n    }\n    else\n    {\n        for (int i = 0; i <= max_color; i++)\n        {\n            out_hist_host[i] = amgx::thrust::count(data, data + num_rows, i);\n        }\n\n        cudaCheckError();\n    }\n}\n\n\n__global__ void permute_colors_kernel(int num_rows, int *row_colors, int *color_permutation)\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += blockDim.x * gridDim.x )\n    {\n        int color = row_colors[row_id];\n        color = __ldg(color_permutation + color);\n        row_colors[row_id] = color;\n    }\n}\n\n__global__ void reverse_colors_kernel(int num_rows, int max_color, int *row_colors)\n{\n    int row_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for ( ; row_id < num_rows ; row_id += blockDim.x * gridDim.x )\n    {\n        int color = row_colors[row_id];\n\n        if (color > 0)\n        {\n            //1 -> max_color\n            //max_color -> 1\n            color = max_color - color + 1;\n        }\n\n        row_colors[row_id] = color;\n    }\n}\n\nint reverse_colors(int num_rows, int max_color, int *row_colors)\n{\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    reverse_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, max_color, row_colors);\n    cudaCheckError();\n    return max_color;\n}\n\nint eliminate_null_colors(int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::host_vector<int> hist(max_color + 1, 0);\n    amgx::thrust::host_vector<int> perm(max_color + 1, 0);\n    device_vector_alloc<int> perm_d(max_color + 1, 0);\n    coloring_histogram(amgx::thrust::raw_pointer_cast(hist.data()), num_rows, max_color, row_colors);\n    int nonempty_color = 1;\n\n    for (int i = 1; i <= max_color; i++) //0 is blank\n    {\n        if (hist[i] > 0) //keep it if not empty\n        {\n            perm[i] = nonempty_color++;\n        }\n    }\n\n    perm_d = perm;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    permute_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, row_colors, amgx::thrust::raw_pointer_cast(perm_d.data()));\n    cudaCheckError();\n    return nonempty_color - 1;\n}\nint reorder_colors_by_frequency(int num_rows, int max_color, int *row_colors)\n{\n    amgx::thrust::host_vector<int> hist(max_color + 1, 0);\n    amgx::thrust::host_vector<int> perm(max_color + 1, 0);\n    device_vector_alloc<int> perm_d(max_color + 1, 0);\n    coloring_histogram(amgx::thrust::raw_pointer_cast(hist.data()), num_rows, max_color, row_colors);\n    int nonempty_color = 1;\n\n    for (int i = 1; i <= max_color; i++) //0 is blank\n    {\n        if (hist[i] > 0) //keep it if not empty\n        {\n            perm[i] = nonempty_color++;\n        }\n    }\n\n    perm_d = perm;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + 256 - 1) / 256 );\n    permute_colors_kernel <<< GRID_SIZE_1, 256>>>(num_rows, row_colors, amgx::thrust::raw_pointer_cast(perm_d.data()));\n    cudaCheckError();\n    return nonempty_color - 1;\n    //return 0;\n}\n\n}\n", "source": "/kaggle/working/amgx/src/matrix_coloring/coloring_utils.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <matrix_coloring/matrix_coloring.h>\n#include <blas.h>\n#include <basic_types.h>\n#include <error.h>\n#include <types.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/functional.h>\n#include <thrust/device_ptr.h>\n#include <thrust/sort.h>\n#include <thrust/count.h>\n#include <thrust/binary_search.h>\n#include <assert.h>\n#include <sm_utils.inl>\n#include <algorithm>\n\n#include <amgx_types/util.h>\n#include <amgx_types/math.h>\n\nnamespace amgx\n{\n/***************************************\n * Source Definitions\n ***************************************/\n\ntemplate<class TConfig>\nMatrixColoring<TConfig>::MatrixColoring(AMG_Config &cfg, const std::string &cfg_scope) :\n    m_num_colors(0), m_row_colors(0), m_sorted_rows_by_color(0), m_offsets_rows_per_color(0), m_ref_count(1), m_boundary_coloring(SYNC_COLORS), m_halo_coloring(FIRST)\n{\n    m_coloring_level = cfg.getParameter<int>(\"coloring_level\", cfg_scope);\n    m_boundary_coloring = cfg.getParameter<ColoringType>(\"boundary_coloring\", cfg_scope);\n    m_halo_coloring = cfg.getParameter<ColoringType>(\"halo_coloring\", cfg_scope);\n}\n\ntemplate<class TConfig>\nMatrixColoring<TConfig>::~MatrixColoring()\n{\n}\n\n__global__ void findSeparation(INDEX_TYPE *rows_by_color, INDEX_TYPE *offsets_by_color, INDEX_TYPE *separation, INDEX_TYPE boundary, INDEX_TYPE num_colors, INDEX_TYPE num_rows)\n{\n    int block_offset = blockIdx.x * (blockDim.x / 32) * 31; //each warp does 31 rows, 1 on the left side is redundant\n    int lane = threadIdx.x % 32;\n    int element = block_offset + (threadIdx.x / 32) * 31 + lane - 1;\n\n    while (element < num_rows)\n    {\n        int color = 0;\n        int row = -1;\n\n        if (element != -1)\n        {\n            row = rows_by_color[element];\n\n            while ((color < num_colors) && ((element < offsets_by_color[color]) || (element >= offsets_by_color[color + 1]))) { color ++; }\n\n            if ((element == offsets_by_color[color]) && (row >= boundary)) { separation[color] = element; } //special case when first row of color is immediately a boundary node\n\n            if ((element == offsets_by_color[color + 1] - 1) && (row < boundary)) { separation[color] = element + 1; } //special case when I am the last, but I am still not a boundary\n        }\n\n        unsigned int result = utils::ballot(row >= boundary, utils::activemask());\n\n        //if (result>0) printf(\"%x\\n\", result);\n        if (lane > 0 && row >= boundary && ((result >> (lane - 1)) & 1) == 0) { separation[color] = element; }\n\n        element += gridDim.x * (blockDim.x / 32) * 31;\n    }\n}\n\n//prints how many edges fail to obey coloring property\n//if the optional aggregates parameter is specified, it also measures the downwind coloring property:\n//for each incoming edge (j,i), where i and j share the same aggregate, holds: color(j) < color(i)\ntemplate <class TConfig>\nvoid MatrixColoring<TConfig>::assertColoring( Matrix<TConfig> &A, IVector &aggregates )\n{\n    IndexType numRows = A.get_num_rows();\n    IndexType nnz = A.get_num_nz();\n    IndexType blocksize = A.get_block_dimx() * A.get_block_dimy();\n    IVector &coloring = this->m_row_colors;\n    bool check_downwind = aggregates.size() == A.get_num_rows();\n    //allocate host memory\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[nnz];\n    ValueType *aa = new ValueType[nnz * blocksize];\n    IndexType *color = new IndexType[numRows];\n    //copy to host\n    cudaMemcpy( ia, A.row_offsets.raw(), sizeof(IndexType) * (numRows + 1), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy( ja, A.col_indices.raw(), sizeof(IndexType)*nnz, cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy( aa, A.values.raw(), sizeof(ValueType)*blocksize * nnz, cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    cudaMemcpy( color, coloring.raw(), sizeof(IndexType)*numRows, cudaMemcpyDeviceToHost );\n    cudaCheckError();\n    IndexType *agg = new IndexType[numRows];\n\n    if ( check_downwind )\n    {\n        cudaMemcpy( agg, aggregates.raw(), sizeof(IndexType)*numRows, cudaMemcpyDeviceToHost );\n        cudaCheckError();\n    }\n\n    //count how many nodes have a color\n    IndexType *color_used = new IndexType[numRows];\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        color_used[i] = 0;\n    }\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        if ( color[i] >= 0 && color[i] < numRows )\n        {\n            color_used[color[i]]++;\n        }\n        else\n        {\n            std::cout << \"color out of range: color[\" << i << \"] = \" << color[i] << std::endl;\n        }\n    }\n\n    // count violations of these two properties:\n    // 1. locally downwind: for incoming edges (j,i) in same aggregate: color(j) < color(i)\n    // 2. valid coloring: for neighbors j: color(j) != color(i)\n    int violation_1 = 0;\n    int property_1 = 0;\n    int violation_2 = 0;\n    int property_2 = 0;\n    int inner_edges = 0;\n\n    //note: property 1 cannot be enforeced all the time. Each cycle for example will violate it regardless of the coloring.\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        for (IndexType ii = ia[i]; ii < ia[i + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( j == i )\n            {\n                continue;\n            }\n\n            //check coloring property\n            if ( color[j] == color[i] )\n            {\n                violation_2++;\n            }\n\n            property_2++;\n\n            if ( check_downwind && agg[j] == agg[i] )\n            {\n                //look for transpose edge to decide outgoing or not\n                bool outgoing = true;\n\n                for (IndexType jj = ia[j]; jj < ia[j + 1]; jj++)\n                {\n                    //found\n                    if ( ja[jj] == i )\n                    {\n                        ValueType weight = types::util<ValueType>::get_zero();\n\n                        for (IndexType iii = ii * blocksize; iii < (ii + 1)*blocksize; iii++)\n                        {\n                            weight = weight + aa[iii] * aa[iii];\n                        }\n\n                        ValueType counter_weight = types::util<ValueType>::get_zero();\n\n                        for (IndexType jjj = jj * blocksize; jjj < (jj + 1)*blocksize; jjj++)\n                        {\n                            counter_weight = counter_weight + aa[jjj] * aa[jjj];\n                        }\n\n                        outgoing = types::util<ValueType>::abs(weight) > types::util<ValueType>::abs(counter_weight);\n                        break;\n                    }\n                }\n\n                //outgoing -> check downwind property\n                if ( outgoing )\n                {\n                    if ( color[j] <= color[i] )\n                    {\n                        violation_1++;\n                    }\n\n                    property_1++;\n                }\n\n                inner_edges++;\n            }\n        }\n    }\n\n    //tell results\n    if ( check_downwind )\n    {\n        std::cout << 200 * property_1 / double(inner_edges) << \"% of all edges inside an aggregate are directed\" << std::endl;\n\n        if ( property_1 > 0 )\n        {\n            std::cout << 100 * violation_1 / double(property_1) << \"% of all outgoing edges inside an aggregate are not colored downwind\" << std::endl;\n        }\n    }\n\n    std::cout << 100 * violation_2 / double(property_2) << \"% of all edges violated coloring property\" << std::endl;\n    std::cout << \"number of nodes that use this color:\" << std::endl;\n\n    for (IndexType i = 0; i < numRows; i++)\n        if ( color_used[i] > 0 )\n        {\n            std::cout << i << \": \" << color_used[i] << std::endl;\n        }\n\n    //free!\n    delete [] ia;\n    delete [] ja;\n    delete [] aa;\n    delete [] agg;\n    delete [] color;\n    delete [] color_used;\n}\ntemplate<class TConfig>\nvoid MatrixColoring<TConfig>::createColorArrays(Matrix<TConfig> &A)\n{\n    ViewType old = A.currentView();\n    A.setViewExterior();\n    int num_rows = A.get_num_rows();\n\n    //Disabled since currently we are not doing halo exchanges during colored execution\n    /*typedef TemplateConfig<AMGX_host,AMGX_vecInt,matPrec,indPrec> hvector_type;\n    typedef Vector<hvector_type> HVector;\n    if (!A.is_matrix_singleGPU()) {\n      HVector num_colors(1);\n      std::vector<HVector> partition_num_colors(0);\n      num_colors[0] = m_num_colors;\n      A.manager->getComms()->global_reduce(partition_num_colors, num_colors, A, 6332);\n      int max_partition_colors = 0;\n      for (int i = 0; i < partition_num_colors.size(); i++)\n          max_partition_colors = std::max(partition_num_colors[i][0],max_partition_colors);\n      m_num_colors = max_partition_colors;\n    }*/\n\n    if (m_halo_coloring == LAST)\n    {\n        thrust_wrapper::fill<TConfig::memSpace>(m_row_colors.begin() + num_rows, m_row_colors.end(), m_num_colors);\n        cudaCheckError();\n    }\n\n    IVector offsets_rows_per_color;\n\n    if (m_offsets_rows_per_color.size() == 0)\n    {\n        // Sort the vertices based o their color\n        m_sorted_rows_by_color.resize(num_rows);\n        // Copy row colors\n        IVector row_colors(m_row_colors);\n        thrust_wrapper::sequence<TConfig::memSpace>(m_sorted_rows_by_color.begin(), m_sorted_rows_by_color.end());\n        amgx::thrust::sort_by_key(row_colors.begin(), row_colors.begin() + num_rows, m_sorted_rows_by_color.begin());\n        cudaCheckError();\n        // Compute the offset for each color\n        offsets_rows_per_color.resize(m_num_colors + 1);\n        m_offsets_rows_per_color.resize(m_num_colors + 1);\n        // Compute interior-exterior separation for every color\n        m_offsets_rows_per_color_separation.resize(m_num_colors);\n        //m_offsets_rows_per_color_separation_halo.resize(m_num_colors);\n        amgx::thrust::lower_bound(row_colors.begin(),\n                            row_colors.begin() + num_rows,\n                            amgx::thrust::counting_iterator<IndexType>(0),\n                            amgx::thrust::counting_iterator<IndexType>(offsets_rows_per_color.size()),\n                            offsets_rows_per_color.begin());\n        // Copy from device to host\n        m_offsets_rows_per_color = offsets_rows_per_color;\n        cudaCheckError();\n    }\n    else\n    {\n        m_offsets_rows_per_color_separation.resize(m_num_colors);\n    }\n\n    cudaCheckError();\n\n    if (!A.is_matrix_singleGPU() && (A.getViewExterior() != A.getViewInterior()))\n    {\n        A.setViewInterior();\n        int separation = A.get_num_rows();\n\n        if (TConfig::memSpace == AMGX_host)\n        {\n            for (int i = 0; i < m_num_colors; i++)\n            {\n                m_offsets_rows_per_color_separation[i] =  m_offsets_rows_per_color[i]\n                        + (amgx::thrust::lower_bound(m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i],\n                                               m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i + 1],\n                                               separation)\n                           - (m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i]));\n            }\n\n            cudaCheckError();\n        }\n        // this is not a proper search, rather we look at every single element. But it is still a lot faster than the above (~10*)\n        else\n        {\n            IVector separation_offsets_rows_per_color(m_num_colors);\n            int size = num_rows;\n            int num_blocks = std::min(4096, (size + 123) / 124);\n            findSeparation <<< num_blocks, 128>>>(m_sorted_rows_by_color.raw(), offsets_rows_per_color.raw(), separation_offsets_rows_per_color.raw(), separation, m_num_colors, num_rows);\n            cudaCheckError();\n            amgx::thrust::copy(separation_offsets_rows_per_color.begin(), separation_offsets_rows_per_color.end(), m_offsets_rows_per_color_separation.begin());\n            cudaCheckError();\n\n            for (int i = 0; i < m_num_colors; i++)\n            {\n                if (this->m_offsets_rows_per_color[i] == this->m_offsets_rows_per_color[i + 1])\n                {\n                    this->m_offsets_rows_per_color_separation[i] = this->m_offsets_rows_per_color[i + 1];\n                }\n            }\n        }\n    }\n    else\n    {\n        amgx::thrust::copy(m_offsets_rows_per_color.begin() + 1, m_offsets_rows_per_color.end(), m_offsets_rows_per_color_separation.begin());\n        cudaCheckError();\n    }\n\n    A.setView(old);\n}\n\ntemplate<class TConfig>\nstd::map<std::string, MatrixColoringFactory<TConfig>*> &\nMatrixColoringFactory<TConfig>::getFactories( )\n{\n    static std::map<std::string, MatrixColoringFactory<TConfig> *> s_factories;\n    return s_factories;\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::registerFactory(std::string name, MatrixColoringFactory<TConfig> *f)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig> *> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::unregisterFactory(std::string name)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    MatrixColoringFactory<TConfig> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::unregisterFactories( )\n{\n    std::map<std::string, MatrixColoringFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        MatrixColoringFactory<TConfig> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class TConfig>\nMatrixColoring<TConfig> *MatrixColoringFactory<TConfig>::allocate(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig> *> &factories = getFactories( );\n    std::string matrix_coloring_scheme = cfg.getParameter<std::string>(\"matrix_coloring_scheme\", cfg_scope);\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::const_iterator it = factories.find(matrix_coloring_scheme);\n\n    if (it == factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + matrix_coloring_scheme + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, cfg_scope);\n};\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MatrixColoringFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <matrix_coloring/matrix_coloring.h>\n#include <blas.h>\n#include <basic_types.h>\n#include <error.h>\n#include <types.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/functional.h>\n#include <thrust/device_ptr.h>\n#include <thrust/sort.h>\n#include <thrust/count.h>\n#include <thrust/binary_search.h>\n#include <assert.h>\n#include <sm_utils.inl>\n#include <algorithm>\n\n#include <amgx_types/util.h>\n#include <amgx_types/math.h>\n\nnamespace amgx\n{\n/***************************************\n * Source Definitions\n ***************************************/\n\ntemplate<class TConfig>\nMatrixColoring<TConfig>::MatrixColoring(AMG_Config &cfg, const std::string &cfg_scope) :\n    m_num_colors(0), m_row_colors(0), m_sorted_rows_by_color(0), m_offsets_rows_per_color(0), m_ref_count(1), m_boundary_coloring(SYNC_COLORS), m_halo_coloring(FIRST)\n{\n    m_coloring_level = cfg.getParameter<int>(\"coloring_level\", cfg_scope);\n    m_boundary_coloring = cfg.getParameter<ColoringType>(\"boundary_coloring\", cfg_scope);\n    m_halo_coloring = cfg.getParameter<ColoringType>(\"halo_coloring\", cfg_scope);\n}\n\ntemplate<class TConfig>\nMatrixColoring<TConfig>::~MatrixColoring()\n{\n}\n\n__global__ void findSeparation(INDEX_TYPE *rows_by_color, INDEX_TYPE *offsets_by_color, INDEX_TYPE *separation, INDEX_TYPE boundary, INDEX_TYPE num_colors, INDEX_TYPE num_rows)\n{\n    int block_offset = blockIdx.x * (blockDim.x / 32) * 31; //each warp does 31 rows, 1 on the left side is redundant\n    int lane = threadIdx.x % 32;\n    int element = block_offset + (threadIdx.x / 32) * 31 + lane - 1;\n\n    while (element < num_rows)\n    {\n        int color = 0;\n        int row = -1;\n\n        if (element != -1)\n        {\n            row = rows_by_color[element];\n\n            while ((color < num_colors) && ((element < offsets_by_color[color]) || (element >= offsets_by_color[color + 1]))) { color ++; }\n\n            if ((element == offsets_by_color[color]) && (row >= boundary)) { separation[color] = element; } //special case when first row of color is immediately a boundary node\n\n            if ((element == offsets_by_color[color + 1] - 1) && (row < boundary)) { separation[color] = element + 1; } //special case when I am the last, but I am still not a boundary\n        }\n\n        unsigned int result = utils::ballot(row >= boundary, utils::activemask());\n\n        //if (result>0) printf(\"%x\\n\", result);\n        if (lane > 0 && row >= boundary && ((result >> (lane - 1)) & 1) == 0) { separation[color] = element; }\n\n        element += gridDim.x * (blockDim.x / 32) * 31;\n    }\n}\n\n//prints how many edges fail to obey coloring property\n//if the optional aggregates parameter is specified, it also measures the downwind coloring property:\n//for each incoming edge (j,i), where i and j share the same aggregate, holds: color(j) < color(i)\ntemplate <class TConfig>\nvoid MatrixColoring<TConfig>::assertColoring( Matrix<TConfig> &A, IVector &aggregates )\n{\n    IndexType numRows = A.get_num_rows();\n    IndexType nnz = A.get_num_nz();\n    IndexType blocksize = A.get_block_dimx() * A.get_block_dimy();\n    IVector &coloring = this->m_row_colors;\n    bool check_downwind = aggregates.size() == A.get_num_rows();\n    //allocate host memory\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[nnz];\n    ValueType *aa = new ValueType[nnz * blocksize];\n    IndexType *color = new IndexType[numRows];\n    //copy to host\n    hipMemcpy( ia, A.row_offsets.raw(), sizeof(IndexType) * (numRows + 1), hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy( ja, A.col_indices.raw(), sizeof(IndexType)*nnz, hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy( aa, A.values.raw(), sizeof(ValueType)*blocksize * nnz, hipMemcpyDeviceToHost );\n    cudaCheckError();\n    hipMemcpy( color, coloring.raw(), sizeof(IndexType)*numRows, hipMemcpyDeviceToHost );\n    cudaCheckError();\n    IndexType *agg = new IndexType[numRows];\n\n    if ( check_downwind )\n    {\n        hipMemcpy( agg, aggregates.raw(), sizeof(IndexType)*numRows, hipMemcpyDeviceToHost );\n        cudaCheckError();\n    }\n\n    //count how many nodes have a color\n    IndexType *color_used = new IndexType[numRows];\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        color_used[i] = 0;\n    }\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        if ( color[i] >= 0 && color[i] < numRows )\n        {\n            color_used[color[i]]++;\n        }\n        else\n        {\n            std::cout << \"color out of range: color[\" << i << \"] = \" << color[i] << std::endl;\n        }\n    }\n\n    // count violations of these two properties:\n    // 1. locally downwind: for incoming edges (j,i) in same aggregate: color(j) < color(i)\n    // 2. valid coloring: for neighbors j: color(j) != color(i)\n    int violation_1 = 0;\n    int property_1 = 0;\n    int violation_2 = 0;\n    int property_2 = 0;\n    int inner_edges = 0;\n\n    //note: property 1 cannot be enforeced all the time. Each cycle for example will violate it regardless of the coloring.\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        for (IndexType ii = ia[i]; ii < ia[i + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( j == i )\n            {\n                continue;\n            }\n\n            //check coloring property\n            if ( color[j] == color[i] )\n            {\n                violation_2++;\n            }\n\n            property_2++;\n\n            if ( check_downwind && agg[j] == agg[i] )\n            {\n                //look for transpose edge to decide outgoing or not\n                bool outgoing = true;\n\n                for (IndexType jj = ia[j]; jj < ia[j + 1]; jj++)\n                {\n                    //found\n                    if ( ja[jj] == i )\n                    {\n                        ValueType weight = types::util<ValueType>::get_zero();\n\n                        for (IndexType iii = ii * blocksize; iii < (ii + 1)*blocksize; iii++)\n                        {\n                            weight = weight + aa[iii] * aa[iii];\n                        }\n\n                        ValueType counter_weight = types::util<ValueType>::get_zero();\n\n                        for (IndexType jjj = jj * blocksize; jjj < (jj + 1)*blocksize; jjj++)\n                        {\n                            counter_weight = counter_weight + aa[jjj] * aa[jjj];\n                        }\n\n                        outgoing = types::util<ValueType>::abs(weight) > types::util<ValueType>::abs(counter_weight);\n                        break;\n                    }\n                }\n\n                //outgoing -> check downwind property\n                if ( outgoing )\n                {\n                    if ( color[j] <= color[i] )\n                    {\n                        violation_1++;\n                    }\n\n                    property_1++;\n                }\n\n                inner_edges++;\n            }\n        }\n    }\n\n    //tell results\n    if ( check_downwind )\n    {\n        std::cout << 200 * property_1 / double(inner_edges) << \"% of all edges inside an aggregate are directed\" << std::endl;\n\n        if ( property_1 > 0 )\n        {\n            std::cout << 100 * violation_1 / double(property_1) << \"% of all outgoing edges inside an aggregate are not colored downwind\" << std::endl;\n        }\n    }\n\n    std::cout << 100 * violation_2 / double(property_2) << \"% of all edges violated coloring property\" << std::endl;\n    std::cout << \"number of nodes that use this color:\" << std::endl;\n\n    for (IndexType i = 0; i < numRows; i++)\n        if ( color_used[i] > 0 )\n        {\n            std::cout << i << \": \" << color_used[i] << std::endl;\n        }\n\n    //free!\n    delete [] ia;\n    delete [] ja;\n    delete [] aa;\n    delete [] agg;\n    delete [] color;\n    delete [] color_used;\n}\ntemplate<class TConfig>\nvoid MatrixColoring<TConfig>::createColorArrays(Matrix<TConfig> &A)\n{\n    ViewType old = A.currentView();\n    A.setViewExterior();\n    int num_rows = A.get_num_rows();\n\n    //Disabled since currently we are not doing halo exchanges during colored execution\n    /*typedef TemplateConfig<AMGX_host,AMGX_vecInt,matPrec,indPrec> hvector_type;\n    typedef Vector<hvector_type> HVector;\n    if (!A.is_matrix_singleGPU()) {\n      HVector num_colors(1);\n      std::vector<HVector> partition_num_colors(0);\n      num_colors[0] = m_num_colors;\n      A.manager->getComms()->global_reduce(partition_num_colors, num_colors, A, 6332);\n      int max_partition_colors = 0;\n      for (int i = 0; i < partition_num_colors.size(); i++)\n          max_partition_colors = std::max(partition_num_colors[i][0],max_partition_colors);\n      m_num_colors = max_partition_colors;\n    }*/\n\n    if (m_halo_coloring == LAST)\n    {\n        thrust_wrapper::fill<TConfig::memSpace>(m_row_colors.begin() + num_rows, m_row_colors.end(), m_num_colors);\n        cudaCheckError();\n    }\n\n    IVector offsets_rows_per_color;\n\n    if (m_offsets_rows_per_color.size() == 0)\n    {\n        // Sort the vertices based o their color\n        m_sorted_rows_by_color.resize(num_rows);\n        // Copy row colors\n        IVector row_colors(m_row_colors);\n        thrust_wrapper::sequence<TConfig::memSpace>(m_sorted_rows_by_color.begin(), m_sorted_rows_by_color.end());\n        amgx::thrust::sort_by_key(row_colors.begin(), row_colors.begin() + num_rows, m_sorted_rows_by_color.begin());\n        cudaCheckError();\n        // Compute the offset for each color\n        offsets_rows_per_color.resize(m_num_colors + 1);\n        m_offsets_rows_per_color.resize(m_num_colors + 1);\n        // Compute interior-exterior separation for every color\n        m_offsets_rows_per_color_separation.resize(m_num_colors);\n        //m_offsets_rows_per_color_separation_halo.resize(m_num_colors);\n        amgx::thrust::lower_bound(row_colors.begin(),\n                            row_colors.begin() + num_rows,\n                            amgx::thrust::counting_iterator<IndexType>(0),\n                            amgx::thrust::counting_iterator<IndexType>(offsets_rows_per_color.size()),\n                            offsets_rows_per_color.begin());\n        // Copy from device to host\n        m_offsets_rows_per_color = offsets_rows_per_color;\n        cudaCheckError();\n    }\n    else\n    {\n        m_offsets_rows_per_color_separation.resize(m_num_colors);\n    }\n\n    cudaCheckError();\n\n    if (!A.is_matrix_singleGPU() && (A.getViewExterior() != A.getViewInterior()))\n    {\n        A.setViewInterior();\n        int separation = A.get_num_rows();\n\n        if (TConfig::memSpace == AMGX_host)\n        {\n            for (int i = 0; i < m_num_colors; i++)\n            {\n                m_offsets_rows_per_color_separation[i] =  m_offsets_rows_per_color[i]\n                        + (amgx::thrust::lower_bound(m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i],\n                                               m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i + 1],\n                                               separation)\n                           - (m_sorted_rows_by_color.begin() + m_offsets_rows_per_color[i]));\n            }\n\n            cudaCheckError();\n        }\n        // this is not a proper search, rather we look at every single element. But it is still a lot faster than the above (~10*)\n        else\n        {\n            IVector separation_offsets_rows_per_color(m_num_colors);\n            int size = num_rows;\n            int num_blocks = std::min(4096, (size + 123) / 124);\n            findSeparation <<< num_blocks, 128>>>(m_sorted_rows_by_color.raw(), offsets_rows_per_color.raw(), separation_offsets_rows_per_color.raw(), separation, m_num_colors, num_rows);\n            cudaCheckError();\n            amgx::thrust::copy(separation_offsets_rows_per_color.begin(), separation_offsets_rows_per_color.end(), m_offsets_rows_per_color_separation.begin());\n            cudaCheckError();\n\n            for (int i = 0; i < m_num_colors; i++)\n            {\n                if (this->m_offsets_rows_per_color[i] == this->m_offsets_rows_per_color[i + 1])\n                {\n                    this->m_offsets_rows_per_color_separation[i] = this->m_offsets_rows_per_color[i + 1];\n                }\n            }\n        }\n    }\n    else\n    {\n        amgx::thrust::copy(m_offsets_rows_per_color.begin() + 1, m_offsets_rows_per_color.end(), m_offsets_rows_per_color_separation.begin());\n        cudaCheckError();\n    }\n\n    A.setView(old);\n}\n\ntemplate<class TConfig>\nstd::map<std::string, MatrixColoringFactory<TConfig>*> &\nMatrixColoringFactory<TConfig>::getFactories( )\n{\n    static std::map<std::string, MatrixColoringFactory<TConfig> *> s_factories;\n    return s_factories;\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::registerFactory(std::string name, MatrixColoringFactory<TConfig> *f)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig> *> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::unregisterFactory(std::string name)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    MatrixColoringFactory<TConfig> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class TConfig>\nvoid MatrixColoringFactory<TConfig>::unregisterFactories( )\n{\n    std::map<std::string, MatrixColoringFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        MatrixColoringFactory<TConfig> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class TConfig>\nMatrixColoring<TConfig> *MatrixColoringFactory<TConfig>::allocate(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    std::map<std::string, MatrixColoringFactory<TConfig> *> &factories = getFactories( );\n    std::string matrix_coloring_scheme = cfg.getParameter<std::string>(\"matrix_coloring_scheme\", cfg_scope);\n    typename std::map<std::string, MatrixColoringFactory<TConfig> *>::const_iterator it = factories.find(matrix_coloring_scheme);\n\n    if (it == factories.end())\n    {\n        std::string error = \"MatrixColoringFactory '\" + matrix_coloring_scheme + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, cfg_scope);\n};\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class MatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MatrixColoringFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/matrix_coloring.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/parallel_greedy.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n\n#include <algorithm>\n\n// Pseudo-random number generator\n\nnamespace amgx\n{\n\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_warp( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors, int *new_color, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    volatile __shared__ unsigned long long colors_used[NUM_WARPS_PER_CTA][32];\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        colors_used[warp_id][lane_id] = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += 32)\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_color = A_colors[col_id];\n            }\n\n            if (col_color != -1)\n            {\n                // Set the bit corresponding to that color to 1\n                colors_used[warp_id][lane_id] |= (1 << col_color);\n            }\n\n            max_row &= (row_hash > col_hash || col_color != 0 || col_color == -1);\n        }\n\n        max_row &= utils::all( max_row );\n\n        if (max_row) // If I'm a max\n        {\n            unsigned int active_mask = utils::activemask();\n            // warp reduction to figure out what color to assign\n            if (lane_id < 16) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 16]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 8) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 8]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 4) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 4]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 2) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 2]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 1) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 1]; } utils::syncwarp(active_mask);\n\n            if (lane_id == 0)\n            {\n                // Find the first unassigned color, starting at 1\n#pragma unroll\n                for (int i = 1; i < 64; i++)\n                {\n                    unsigned long long mask = 1 << i;\n\n                    if ( !(colors_used[warp_id][0] & mask) )\n                    {\n                        A_colors[row_id] = i;\n\n                        if (i >= num_colors) { new_color[0] = 1; }\n\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_thread( const int A_num_rows, const int *A_rows, const int *A_cols, int *d_num_colors,  int *d_new_color, int *A_colors, int *d_num_uncolored_block, int *d_done)\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    if (d_done[0] == 1)\n    {\n        return;\n    }\n\n    int num_uncolored_thread = 0;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n        else\n        {\n            num_uncolored_thread++;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        //int row_hash = row_id;\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows || col_id == row_id) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color > 0)\n            {\n                used_colors |= 1ull << (64 - col_color);\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || col_color != 0);\n        }\n\n        if ( max_row )\n        {\n            int my_color = 64 - utils::bfind( ~used_colors );\n\n            if ( my_color > 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            if ( my_color >= d_num_colors[0] )\n            {\n                d_new_color[0] = 1;\n            }\n        }\n    }\n\n    // Do per block reduction\n    __shared__ volatile int smem[CTA_SIZE];\n    smem[threadIdx.x] = num_uncolored_thread;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        d_num_uncolored_block[blockIdx.x] = smem[0];\n    }\n}\n\ntemplate<int MAX_CTA_SIZE, bool TERMINATE_IF_FULL_COLORS_USED>\n__global__\nvoid count_uncolored_and_num_colors_kernel(int *d_new_color, int *d_num_colors, int max_uncolored_rows, int *d_num_uncolored_block, int *d_num_uncolored, int *d_done, int *h_done, int *d_start_color, int CTA_SIZE)\n{\n    // Do block reduction\n    __shared__ volatile int smem[MAX_CTA_SIZE];\n\n    if (threadIdx.x >= CTA_SIZE)\n    {\n        smem[threadIdx.x] = 0;\n    }\n    else\n    {\n        smem[threadIdx.x] = d_num_uncolored_block[threadIdx.x];\n    }\n\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        int prev_num_uncolored = d_num_uncolored[0];\n        int num_uncolored = smem[0];\n        int num_colors = d_num_colors[0];\n\n        if (d_new_color[0])\n        {\n            num_colors++; // New color has been added\n            d_new_color[0] = 0; // Reset to 0\n        }\n\n        // Check for termination\n        if (TERMINATE_IF_FULL_COLORS_USED)\n        {\n            // Check for termination\n            if (num_colors == 64 || prev_num_uncolored == num_uncolored || num_uncolored <= max_uncolored_rows)\n            {\n                h_done[0] = 1;\n                d_done[0] = 1;\n            }\n        }\n        else\n        {\n            if (prev_num_uncolored == num_uncolored || num_uncolored <= max_uncolored_rows)\n            {\n                if (num_uncolored <= max_uncolored_rows)\n                {\n                    h_done[0] = 1;\n                    d_done[0] = 1;\n                }\n                else\n                {\n                    d_start_color[0] += 32;\n                }\n            }\n        }\n\n        // Write to gmem\n        d_num_uncolored[0] = num_uncolored;\n        d_num_colors[0] = num_colors;\n    }\n}\n\n\n//////////////////////////////\n// ANYRING version: begin\n// This unrolled recursion is used to visit neighbors.\n//////////////////////////////\n\ntemplate<int COLORING_LEVEL>\nstruct parallel_greedy_neighborhood\n{\n    __device__ __inline__ static void visit(int row_id, int row_hash, int visit_id, const int A_num_rows, const int *A_rows, const int *A_cols, int *A_colors,\n                                            const int &start_color, bool &max_row, unsigned long long &used_colors\n                                           )\n    {\n        int row_begin = A_rows[visit_id  ];\n        int row_end   = A_rows[visit_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows || col_id == visit_id) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color - start_color > 0)\n            {\n                used_colors |= 1ull << (64 - col_color + start_color);\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || (row_hash == col_hash && col_id >= row_id)  || col_color != 0);\n\n            if (!max_row) { break; }\n\n            parallel_greedy_neighborhood < COLORING_LEVEL - 1 >::visit(row_id, row_hash, col_id, A_num_rows, A_rows, A_cols, A_colors, start_color, max_row, used_colors);\n\n            if (!max_row) { break; }\n        }\n    }\n};\n\ntemplate<>\nstruct parallel_greedy_neighborhood<0>\n{\n    __device__ __inline__ static void visit(int row_id, int row_hash, int visit_id, const int A_num_rows, const int *A_rows, const int *A_cols, int *A_colors,\n                                            const int &start_color, bool &max_row, unsigned long long &used_colors\n                                           )\n    {\n        ;\n    }\n};\n\n\n//////////////////////////////\n// This kernel is equivalent to color_kernel_thread, but if COLORING_LEVEL>=2 it recurses to neighbors'+ neighbors.\n// it also uses start_color_ to continue coloring with parallel greedy instead of falling back to min_max if more than 64 colors are used\n//////////////////////////////\n\ntemplate< int CTA_SIZE, int COLORING_LEVEL >\n__global__\nvoid color_kernel_thread_anyring( const int A_num_rows, const int *A_rows, const int *A_cols, int *d_num_colors,  int *d_new_color, int *A_colors, int *d_num_uncolored_block, int *d_done,\n                                  const int *start_color_)\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    if (d_done[0] == 1)\n    {\n        return;\n    }\n\n    const int start_color = start_color_[0];// (d_num_colors[0] / 64)*64;\n    int num_uncolored_thread = 0;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n        else\n        {\n            num_uncolored_thread++;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        //int row_hash = row_id;\n        bool max_row = true;\n\n        if (COLORING_LEVEL == 1)\n        {\n            // This path is equivalent to color_kernel_thread, but it uses start_color.\n            // Iterators over my row.\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( int row_it = row_begin; row_it < row_end ; row_it++)\n            {\n                // Get the column index (if the iterator is valid).\n                int col_id = A_cols[row_it];\n\n                if (col_id >= A_num_rows || col_id == row_id) { continue; }\n\n                // Each thread hashes its column id.\n                int col_hash = hash_function( col_id, 0 );\n                // Get the color of the column.\n                int col_color = A_colors[col_id];\n\n                // Update the color.\n                if ( col_color - start_color > 0)\n                {\n                    used_colors |= 1ull << (64 - col_color + start_color);\n                }\n\n                // Is it still a max row?\n                max_row &= (row_hash > col_hash || (row_hash == col_hash && col_id >= row_id)  || col_color != 0);\n\n                if (!max_row) { break; }\n            }\n        }\n        else\n        {\n            //recurse\n            parallel_greedy_neighborhood<COLORING_LEVEL>::visit(row_id, row_hash, row_id, A_num_rows, A_rows, A_cols, A_colors, start_color, max_row, used_colors);\n        }\n\n        if ( max_row )\n        {\n            int my_color = 64 - utils::bfind( ~used_colors );\n\n            if ( my_color > 0 && my_color < 64)\n            {\n                A_colors[row_id] = my_color + start_color;\n\n                if ( my_color + start_color >= d_num_colors[0] )\n                {\n                    d_new_color[0] = 1;\n                }\n            }\n        }\n    }\n\n    // Do per block reduction\n    __shared__ volatile int smem[CTA_SIZE];\n    smem[threadIdx.x] = num_uncolored_thread;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        d_num_uncolored_block[blockIdx.x] = smem[0];\n    }\n}\n\n//////////////////////////////\n// ANYRING version: end\n//////////////////////////////\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_thread_fallback( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors,  int *new_color, int *A_colors )\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        int max_color = 0;\n\n        //if (row_id == 2) printf(\"row_id=%d\\n\",row_id);\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color > max_color)\n            {\n                max_color = col_color;\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || col_color != 0);\n        }\n\n        if ( max_row )\n        {\n            int my_color = max_color + 1;\n\n            if ( my_color > 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            if ( my_color >= num_colors )\n            {\n                new_color[0] = 1;\n            }\n        }\n    }\n}\n\n\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_check_coloring_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_colors, const int *A_gtlt_count, int *error_found )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Is there something wrong ??\n            if ( row_id != col_id && row_color == col_color )\n            {\n                if ( A_gtlt_count != NULL && !error_found[0] )\n                {\n                    printf( \"row_id=%d, row_color=%d, col_id=%d, col_color=%d\\n\", row_id, row_color, col_id, col_color );\n                }\n\n                error_found[0] = 1;\n            }\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nParallel_Greedy_Matrix_Coloring_Base<T_Config>::Parallel_Greedy_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level > 5 )\n    {\n        FatalError( \"Not implemented for coloring_level > 5\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallel_Greedy_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int MAX_GRID_SIZE = 1024;\n\n    if (MAX_GRID_SIZE > 1024)\n    {\n        FatalError(\"Grid size must be less than 1024\\n\", AMGX_ERR_CORE);\n    }\n\n    const int GRID_SIZE = std::min( MAX_GRID_SIZE, (num_rows + CTA_SIZE - 1) / CTA_SIZE);\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    typedef typename Matrix_d::IVector IVector_d;\n    cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n    IVector_d d_new_color(1);\n    cudaMemsetAsync(d_new_color.raw(), 0, sizeof(int), stream);\n    cudaCheckError();\n    this->m_num_colors = 1;\n    IVector_d d_num_colors(1);\n    IVector_d d_start_color(1); //for coloring safely without fallback\n    cudaMemcpyAsync(d_num_colors.raw(), &this->m_num_colors, sizeof(int), cudaMemcpyHostToDevice, stream);\n    cudaCheckError();\n    int tmp = 0;\n    cudaMemcpyAsync(d_start_color.raw(), &tmp, sizeof(int), cudaMemcpyHostToDevice, stream);\n    cudaCheckError();\n    IVector_d d_num_uncolored(1);\n    IVector_d d_num_uncolored_block(MAX_GRID_SIZE);\n    int *h_done = NULL;\n    amgx::memory::cudaMallocHost( (void **) &h_done, sizeof(int));\n    cudaCheckError();\n    IVector_d d_done(1);\n    d_done[0] = 0;\n    *h_done = 0;\n    cudaEvent_t     throttle_event = 0;\n    cudaEventCreateWithFlags(&throttle_event, cudaEventDisableTiming);\n    int iteration = 0;\n\n    while (h_done[0] == 0)\n    {\n#define PG_ARGS                 num_rows,\\\n        A.row_offsets.raw(),\\\n        A.col_indices.raw(),\\\n        d_num_colors.raw(),\\\n        d_new_color.raw(),\\\n        this->m_row_colors.raw(),\\\n        d_num_uncolored_block.raw(),\\\n        d_done.raw()\n\n        if (this->m_coloring_level == 1)\n        {\n            color_kernel_thread<CTA_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS);\n            cudaCheckError();\n            count_uncolored_and_num_colors_kernel<MAX_GRID_SIZE, true> <<< 1, MAX_GRID_SIZE, 0, stream>>>(d_new_color.raw(), d_num_colors.raw(), max_uncolored_rows, d_num_uncolored_block.raw(), d_num_uncolored.raw(), d_done.raw(), h_done, d_start_color.raw(), GRID_SIZE);\n            cudaCheckError();\n        }\n        else\n        {\n            if (this->m_coloring_level == 2)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 2> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 3)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 3> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 4)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 4> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 5)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 5> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n\n            count_uncolored_and_num_colors_kernel<MAX_GRID_SIZE, false> <<< 1, MAX_GRID_SIZE, 0, stream>>>(d_new_color.raw(), d_num_colors.raw(), max_uncolored_rows, d_num_uncolored_block.raw(), d_num_uncolored.raw(), d_done.raw(), h_done, d_start_color.raw(), GRID_SIZE);\n            cudaCheckError();\n        }\n\n        // Throttle every 4 iteration\n        if (iteration % 4 == 0)\n        {\n            cudaEventRecord(throttle_event);\n            cudaCheckError();\n        }\n        else\n        {\n            cudaEventSynchronize(throttle_event);\n            cudaCheckError();\n        };\n\n        iteration++;\n\n        cudaCheckError();\n    }\n\n    typedef typename Matrix_h::IVector IVector_h;\n    IVector_h new_color(1);\n    cudaEventDestroy(throttle_event);\n    cudaCheckError();\n    this->m_num_colors = d_num_colors[0];\n    int num_uncolored = d_num_uncolored[0];\n    int prev_num_uncolored = 0;\n\n    // Fallback path if # colors exceeds 64, instead of picking the smallest unassigned color,\n    // it sets its color to max neighbor color + 1\n    if (this->m_num_colors == 64)\n    {\n        // Choose highest unassigned color among the neighbors\n        for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows && prev_num_uncolored != num_uncolored ; )\n        {\n            prev_num_uncolored = num_uncolored;\n            new_color[0] = 0; // Host to device transfer\n            color_kernel_thread_fallback<CTA_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                this->m_num_colors,\n                amgx::thrust::raw_pointer_cast (&new_color.front()),\n                this->m_row_colors.raw() );\n            cudaCheckError();\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n\n            if (new_color[0]) { this->m_num_colors++; }\n\n            cudaCheckError();\n        }\n    }\n\n#if 0\n    device_vector_alloc<int> error_found( 1, 0 );\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        NULL,\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n\n    if ( error_found[0] != 0 )\n    {\n        std::cout << \"INVALID COLORING !!! Two neighbors have the same color!!!\" << std::endl;\n    }\n\n#endif\n    amgx::memory::cudaFreeHost(h_done);\n    cudaCheckError();\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class Parallel_Greedy_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Parallel_Greedy_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/parallel_greedy.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n\n#include <algorithm>\n\n// Pseudo-random number generator\n\nnamespace amgx\n{\n\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_warp( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors, int *new_color, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    volatile __shared__ unsigned long long colors_used[NUM_WARPS_PER_CTA][32];\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        colors_used[warp_id][lane_id] = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += 32)\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_color = A_colors[col_id];\n            }\n\n            if (col_color != -1)\n            {\n                // Set the bit corresponding to that color to 1\n                colors_used[warp_id][lane_id] |= (1 << col_color);\n            }\n\n            max_row &= (row_hash > col_hash || col_color != 0 || col_color == -1);\n        }\n\n        max_row &= utils::all( max_row );\n\n        if (max_row) // If I'm a max\n        {\n            unsigned int active_mask = utils::activemask();\n            // warp reduction to figure out what color to assign\n            if (lane_id < 16) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 16]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 8) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 8]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 4) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 4]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 2) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 2]; } utils::syncwarp(active_mask);\n\n            if (lane_id < 1) { colors_used[warp_id][lane_id] |= colors_used[warp_id][lane_id + 1]; } utils::syncwarp(active_mask);\n\n            if (lane_id == 0)\n            {\n                // Find the first unassigned color, starting at 1\n#pragma unroll\n                for (int i = 1; i < 64; i++)\n                {\n                    unsigned long long mask = 1 << i;\n\n                    if ( !(colors_used[warp_id][0] & mask) )\n                    {\n                        A_colors[row_id] = i;\n\n                        if (i >= num_colors) { new_color[0] = 1; }\n\n                        break;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_thread( const int A_num_rows, const int *A_rows, const int *A_cols, int *d_num_colors,  int *d_new_color, int *A_colors, int *d_num_uncolored_block, int *d_done)\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    if (d_done[0] == 1)\n    {\n        return;\n    }\n\n    int num_uncolored_thread = 0;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n        else\n        {\n            num_uncolored_thread++;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        //int row_hash = row_id;\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows || col_id == row_id) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color > 0)\n            {\n                used_colors |= 1ull << (64 - col_color);\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || col_color != 0);\n        }\n\n        if ( max_row )\n        {\n            int my_color = 64 - utils::bfind( ~used_colors );\n\n            if ( my_color > 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            if ( my_color >= d_num_colors[0] )\n            {\n                d_new_color[0] = 1;\n            }\n        }\n    }\n\n    // Do per block reduction\n    __shared__ volatile int smem[CTA_SIZE];\n    smem[threadIdx.x] = num_uncolored_thread;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        d_num_uncolored_block[blockIdx.x] = smem[0];\n    }\n}\n\ntemplate<int MAX_CTA_SIZE, bool TERMINATE_IF_FULL_COLORS_USED>\n__global__\nvoid count_uncolored_and_num_colors_kernel(int *d_new_color, int *d_num_colors, int max_uncolored_rows, int *d_num_uncolored_block, int *d_num_uncolored, int *d_done, int *h_done, int *d_start_color, int CTA_SIZE)\n{\n    // Do block reduction\n    __shared__ volatile int smem[MAX_CTA_SIZE];\n\n    if (threadIdx.x >= CTA_SIZE)\n    {\n        smem[threadIdx.x] = 0;\n    }\n    else\n    {\n        smem[threadIdx.x] = d_num_uncolored_block[threadIdx.x];\n    }\n\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        int prev_num_uncolored = d_num_uncolored[0];\n        int num_uncolored = smem[0];\n        int num_colors = d_num_colors[0];\n\n        if (d_new_color[0])\n        {\n            num_colors++; // New color has been added\n            d_new_color[0] = 0; // Reset to 0\n        }\n\n        // Check for termination\n        if (TERMINATE_IF_FULL_COLORS_USED)\n        {\n            // Check for termination\n            if (num_colors == 64 || prev_num_uncolored == num_uncolored || num_uncolored <= max_uncolored_rows)\n            {\n                h_done[0] = 1;\n                d_done[0] = 1;\n            }\n        }\n        else\n        {\n            if (prev_num_uncolored == num_uncolored || num_uncolored <= max_uncolored_rows)\n            {\n                if (num_uncolored <= max_uncolored_rows)\n                {\n                    h_done[0] = 1;\n                    d_done[0] = 1;\n                }\n                else\n                {\n                    d_start_color[0] += 32;\n                }\n            }\n        }\n\n        // Write to gmem\n        d_num_uncolored[0] = num_uncolored;\n        d_num_colors[0] = num_colors;\n    }\n}\n\n\n//////////////////////////////\n// ANYRING version: begin\n// This unrolled recursion is used to visit neighbors.\n//////////////////////////////\n\ntemplate<int COLORING_LEVEL>\nstruct parallel_greedy_neighborhood\n{\n    __device__ __inline__ static void visit(int row_id, int row_hash, int visit_id, const int A_num_rows, const int *A_rows, const int *A_cols, int *A_colors,\n                                            const int &start_color, bool &max_row, unsigned long long &used_colors\n                                           )\n    {\n        int row_begin = A_rows[visit_id  ];\n        int row_end   = A_rows[visit_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows || col_id == visit_id) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color - start_color > 0)\n            {\n                used_colors |= 1ull << (64 - col_color + start_color);\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || (row_hash == col_hash && col_id >= row_id)  || col_color != 0);\n\n            if (!max_row) { break; }\n\n            parallel_greedy_neighborhood < COLORING_LEVEL - 1 >::visit(row_id, row_hash, col_id, A_num_rows, A_rows, A_cols, A_colors, start_color, max_row, used_colors);\n\n            if (!max_row) { break; }\n        }\n    }\n};\n\ntemplate<>\nstruct parallel_greedy_neighborhood<0>\n{\n    __device__ __inline__ static void visit(int row_id, int row_hash, int visit_id, const int A_num_rows, const int *A_rows, const int *A_cols, int *A_colors,\n                                            const int &start_color, bool &max_row, unsigned long long &used_colors\n                                           )\n    {\n        ;\n    }\n};\n\n\n//////////////////////////////\n// This kernel is equivalent to color_kernel_thread, but if COLORING_LEVEL>=2 it recurses to neighbors'+ neighbors.\n// it also uses start_color_ to continue coloring with parallel greedy instead of falling back to min_max if more than 64 colors are used\n//////////////////////////////\n\ntemplate< int CTA_SIZE, int COLORING_LEVEL >\n__global__\nvoid color_kernel_thread_anyring( const int A_num_rows, const int *A_rows, const int *A_cols, int *d_num_colors,  int *d_new_color, int *A_colors, int *d_num_uncolored_block, int *d_done,\n                                  const int *start_color_)\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    if (d_done[0] == 1)\n    {\n        return;\n    }\n\n    const int start_color = start_color_[0];// (d_num_colors[0] / 64)*64;\n    int num_uncolored_thread = 0;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n        else\n        {\n            num_uncolored_thread++;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        //int row_hash = row_id;\n        bool max_row = true;\n\n        if (COLORING_LEVEL == 1)\n        {\n            // This path is equivalent to color_kernel_thread, but it uses start_color.\n            // Iterators over my row.\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( int row_it = row_begin; row_it < row_end ; row_it++)\n            {\n                // Get the column index (if the iterator is valid).\n                int col_id = A_cols[row_it];\n\n                if (col_id >= A_num_rows || col_id == row_id) { continue; }\n\n                // Each thread hashes its column id.\n                int col_hash = hash_function( col_id, 0 );\n                // Get the color of the column.\n                int col_color = A_colors[col_id];\n\n                // Update the color.\n                if ( col_color - start_color > 0)\n                {\n                    used_colors |= 1ull << (64 - col_color + start_color);\n                }\n\n                // Is it still a max row?\n                max_row &= (row_hash > col_hash || (row_hash == col_hash && col_id >= row_id)  || col_color != 0);\n\n                if (!max_row) { break; }\n            }\n        }\n        else\n        {\n            //recurse\n            parallel_greedy_neighborhood<COLORING_LEVEL>::visit(row_id, row_hash, row_id, A_num_rows, A_rows, A_cols, A_colors, start_color, max_row, used_colors);\n        }\n\n        if ( max_row )\n        {\n            int my_color = 64 - utils::bfind( ~used_colors );\n\n            if ( my_color > 0 && my_color < 64)\n            {\n                A_colors[row_id] = my_color + start_color;\n\n                if ( my_color + start_color >= d_num_colors[0] )\n                {\n                    d_new_color[0] = 1;\n                }\n            }\n        }\n    }\n\n    // Do per block reduction\n    __shared__ volatile int smem[CTA_SIZE];\n    smem[threadIdx.x] = num_uncolored_thread;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        d_num_uncolored_block[blockIdx.x] = smem[0];\n    }\n}\n\n//////////////////////////////\n// ANYRING version: end\n//////////////////////////////\n\ntemplate< int CTA_SIZE>\n__global__\nvoid color_kernel_thread_fallback( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors,  int *new_color, int *A_colors )\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 0);\n        bool max_row = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        int max_color = 0;\n\n        //if (row_id == 2) printf(\"row_id=%d\\n\",row_id);\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            // Get the column index (if the iterator is valid).\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows) { continue; }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, 0 );\n            // Get the color of the column.\n            int col_color = A_colors[col_id];\n\n            // Update the color.\n            if ( col_color > max_color)\n            {\n                max_color = col_color;\n            }\n\n            // Is it still a max row?\n            max_row &= (row_hash > col_hash || col_color != 0);\n        }\n\n        if ( max_row )\n        {\n            int my_color = max_color + 1;\n\n            if ( my_color > 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            if ( my_color >= num_colors )\n            {\n                new_color[0] = 1;\n            }\n        }\n    }\n}\n\n\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_check_coloring_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_colors, const int *A_gtlt_count, int *error_found )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Is there something wrong ??\n            if ( row_id != col_id && row_color == col_color )\n            {\n                if ( A_gtlt_count != NULL && !error_found[0] )\n                {\n                    printf( \"row_id=%d, row_color=%d, col_id=%d, col_color=%d\\n\", row_id, row_color, col_id, col_color );\n                }\n\n                error_found[0] = 1;\n            }\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nParallel_Greedy_Matrix_Coloring_Base<T_Config>::Parallel_Greedy_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level > 5 )\n    {\n        FatalError( \"Not implemented for coloring_level > 5\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallel_Greedy_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int MAX_GRID_SIZE = 1024;\n\n    if (MAX_GRID_SIZE > 1024)\n    {\n        FatalError(\"Grid size must be less than 1024\\n\", AMGX_ERR_CORE);\n    }\n\n    const int GRID_SIZE = std::min( MAX_GRID_SIZE, (num_rows + CTA_SIZE - 1) / CTA_SIZE);\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    typedef typename Matrix_d::IVector IVector_d;\n    hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n    IVector_d d_new_color(1);\n    hipMemsetAsync(d_new_color.raw(), 0, sizeof(int), stream);\n    cudaCheckError();\n    this->m_num_colors = 1;\n    IVector_d d_num_colors(1);\n    IVector_d d_start_color(1); //for coloring safely without fallback\n    hipMemcpyAsync(d_num_colors.raw(), &this->m_num_colors, sizeof(int), hipMemcpyHostToDevice, stream);\n    cudaCheckError();\n    int tmp = 0;\n    hipMemcpyAsync(d_start_color.raw(), &tmp, sizeof(int), hipMemcpyHostToDevice, stream);\n    cudaCheckError();\n    IVector_d d_num_uncolored(1);\n    IVector_d d_num_uncolored_block(MAX_GRID_SIZE);\n    int *h_done = NULL;\n    amgx::memory::hipHostMalloc( (void **) &h_done, sizeof(int));\n    cudaCheckError();\n    IVector_d d_done(1);\n    d_done[0] = 0;\n    *h_done = 0;\n    hipEvent_t     throttle_event = 0;\n    hipEventCreateWithFlags(&throttle_event, hipEventDisableTiming);\n    int iteration = 0;\n\n    while (h_done[0] == 0)\n    {\n#define PG_ARGS                 num_rows,\\\n        A.row_offsets.raw(),\\\n        A.col_indices.raw(),\\\n        d_num_colors.raw(),\\\n        d_new_color.raw(),\\\n        this->m_row_colors.raw(),\\\n        d_num_uncolored_block.raw(),\\\n        d_done.raw()\n\n        if (this->m_coloring_level == 1)\n        {\n            color_kernel_thread<CTA_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS);\n            cudaCheckError();\n            count_uncolored_and_num_colors_kernel<MAX_GRID_SIZE, true> <<< 1, MAX_GRID_SIZE, 0, stream>>>(d_new_color.raw(), d_num_colors.raw(), max_uncolored_rows, d_num_uncolored_block.raw(), d_num_uncolored.raw(), d_done.raw(), h_done, d_start_color.raw(), GRID_SIZE);\n            cudaCheckError();\n        }\n        else\n        {\n            if (this->m_coloring_level == 2)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 2> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 3)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 3> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 4)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 4> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n            else if (this->m_coloring_level == 5)\n            {\n                color_kernel_thread_anyring<CTA_SIZE, 5> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(PG_ARGS, d_start_color.raw());\n                cudaCheckError();\n            }\n\n            count_uncolored_and_num_colors_kernel<MAX_GRID_SIZE, false> <<< 1, MAX_GRID_SIZE, 0, stream>>>(d_new_color.raw(), d_num_colors.raw(), max_uncolored_rows, d_num_uncolored_block.raw(), d_num_uncolored.raw(), d_done.raw(), h_done, d_start_color.raw(), GRID_SIZE);\n            cudaCheckError();\n        }\n\n        // Throttle every 4 iteration\n        if (iteration % 4 == 0)\n        {\n            hipEventRecord(throttle_event);\n            cudaCheckError();\n        }\n        else\n        {\n            hipEventSynchronize(throttle_event);\n            cudaCheckError();\n        };\n\n        iteration++;\n\n        cudaCheckError();\n    }\n\n    typedef typename Matrix_h::IVector IVector_h;\n    IVector_h new_color(1);\n    hipEventDestroy(throttle_event);\n    cudaCheckError();\n    this->m_num_colors = d_num_colors[0];\n    int num_uncolored = d_num_uncolored[0];\n    int prev_num_uncolored = 0;\n\n    // Fallback path if # colors exceeds 64, instead of picking the smallest unassigned color,\n    // it sets its color to max neighbor color + 1\n    if (this->m_num_colors == 64)\n    {\n        // Choose highest unassigned color among the neighbors\n        for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows && prev_num_uncolored != num_uncolored ; )\n        {\n            prev_num_uncolored = num_uncolored;\n            new_color[0] = 0; // Host to device transfer\n            color_kernel_thread_fallback<CTA_SIZE> <<< GRID_SIZE, CTA_SIZE, 0, stream>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                this->m_num_colors,\n                amgx::thrust::raw_pointer_cast (&new_color.front()),\n                this->m_row_colors.raw() );\n            cudaCheckError();\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n\n            if (new_color[0]) { this->m_num_colors++; }\n\n            cudaCheckError();\n        }\n    }\n\n#if 0\n    device_vector_alloc<int> error_found( 1, 0 );\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        NULL,\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n\n    if ( error_found[0] != 0 )\n    {\n        std::cout << \"INVALID COLORING !!! Two neighbors have the same color!!!\" << std::endl;\n    }\n\n#endif\n    amgx::memory::hipHostFree(h_done);\n    cudaCheckError();\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class Parallel_Greedy_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Parallel_Greedy_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/parallel_greedy.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/uniform.h>\n#include <thrust/count.h>\n\nnamespace amgx\n{\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate <typename IndexType>\n__global__\nvoid color3d(IndexType *row_colors, const int n, const int n1d)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n1d2 = n1d * n1d;\n    int x, y, z;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        int label = idx % n1d2;\n        x = (label % n1d) % 2;\n        y = (label / n1d) % 2;\n        z = (idx / n1d2) % 2 ;\n        row_colors[idx] = z * 4 + y * 2 + x;\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid color2d(IndexType *row_colors, const int n, const int n1d)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int x, y;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        x = (idx % n1d) % 2;\n        y = (idx / n1d) % 2;\n        row_colors[idx] = 2 * y + x;\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nUniformMatrixColoringBase<T_Config>::UniformMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n}\n\n\n// Block version\ntemplate <class T_Config>\nvoid UniformMatrixColoringBase<T_Config>::colorMatrix(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else\n    {\n        int num_rows = A.get_num_rows();\n        int dim = 2;\n        this->m_num_colors = 2 << dim;\n\n        if (A.get_num_rows() < this->m_num_colors)\n        {\n            thrust_wrapper::sequence<T_Config::memSpace>(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows);\n            cudaCheckError();\n            this->m_num_colors = A.get_num_rows();\n        }\n        else\n        {\n            IndexType *row_colors_ptr = this->m_row_colors.raw();\n            const int threads_per_block = 512;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows - 1) / threads_per_block + 1 );\n\n            if (dim == 3)\n            {\n                int n1d = round(cbrt((double)num_rows));\n                //printf(\"Uniform coloring: n==%d, dim == %d, n1d == %d\\n\", num_rows, dim, n1d);\n                color3d <<< num_blocks, threads_per_block>>>(row_colors_ptr, num_rows, n1d);\n                cudaCheckError();\n            }\n            else\n            {\n                int n1d = round(sqrt((double)num_rows));\n                //printf(\"Uniform coloring: n==%d, dim == %d, n1d == %d\\n\", num_rows, dim, n1d);\n                color2d <<< num_blocks, threads_per_block>>>(row_colors_ptr, num_rows, n1d);\n            }\n\n            cudaCheckError();\n        }\n    }\n\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class UniformMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class UniformMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/uniform.h>\n#include <thrust/count.h>\n\nnamespace amgx\n{\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate <typename IndexType>\n__global__\nvoid color3d(IndexType *row_colors, const int n, const int n1d)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n1d2 = n1d * n1d;\n    int x, y, z;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        int label = idx % n1d2;\n        x = (label % n1d) % 2;\n        y = (label / n1d) % 2;\n        z = (idx / n1d2) % 2 ;\n        row_colors[idx] = z * 4 + y * 2 + x;\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid color2d(IndexType *row_colors, const int n, const int n1d)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int x, y;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        x = (idx % n1d) % 2;\n        y = (idx / n1d) % 2;\n        row_colors[idx] = 2 * y + x;\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nUniformMatrixColoringBase<T_Config>::UniformMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n}\n\n\n// Block version\ntemplate <class T_Config>\nvoid UniformMatrixColoringBase<T_Config>::colorMatrix(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else\n    {\n        int num_rows = A.get_num_rows();\n        int dim = 2;\n        this->m_num_colors = 2 << dim;\n\n        if (A.get_num_rows() < this->m_num_colors)\n        {\n            thrust_wrapper::sequence<T_Config::memSpace>(this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows);\n            cudaCheckError();\n            this->m_num_colors = A.get_num_rows();\n        }\n        else\n        {\n            IndexType *row_colors_ptr = this->m_row_colors.raw();\n            const int threads_per_block = 512;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows - 1) / threads_per_block + 1 );\n\n            if (dim == 3)\n            {\n                int n1d = round(cbrt((double)num_rows));\n                //printf(\"Uniform coloring: n==%d, dim == %d, n1d == %d\\n\", num_rows, dim, n1d);\n                color3d <<< num_blocks, threads_per_block>>>(row_colors_ptr, num_rows, n1d);\n                cudaCheckError();\n            }\n            else\n            {\n                int n1d = round(sqrt((double)num_rows));\n                //printf(\"Uniform coloring: n==%d, dim == %d, n1d == %d\\n\", num_rows, dim, n1d);\n                color2d <<< num_blocks, threads_per_block>>>(row_colors_ptr, num_rows, n1d);\n            }\n\n            cudaCheckError();\n        }\n    }\n\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class UniformMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class UniformMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/uniform.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <algorithm>\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/greedy_min_max_2ring.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n#include <thrust/remove.h>\n#include  <matrix_coloring/coloring_utils.h>\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\nstatic __host__ __device__ __forceinline__ unsigned int hash_function(unsigned int a, unsigned int seed, unsigned int rows = 0)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n// ---------------------------\n// Kernels: Any ring parallel greedy with color buffers and max id propagation.\n// ---------------------------\n\n\n/*\n * This kernel computes the index of the node with maximum hash in a neighborhood and the colors used in a vertex neighborhood.\n * Since both operation are associative (max and or), it is possible to iterate the operation propagating the values across neigborhoods (first, second and so on rings).\n *\n * The N-th iteration computes, for each vertex:\n * 1. The ID of the uncolored vertex with ID of the vertex having max hash inside the N-ring neighborhood.\n *    If all neighbors in the N-hood are colored, saves -1 (this allows work efficiency).\n * 2. The buffer of used colors in the n-ring neighborhood.\n *\n * And for that, it uses data from previous iteration.\n *\n * For avoiding the waste of work, once any n-ring neighborhood is all colored, it starts getting ignored:\n *  no new MAX HASH can be found there and the set of used colors for that ring will not change.\n */\n\n\ntemplate< bool USE_EXT, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid find_max_neighbor_kernel_and_propagate_used_colors( const int A_num_rows,\n        const int *A_rows,\n        const int *A_cols,\n        const int *__restrict A_colors,\n\n        int *level_n0_max_id_in, //for multiple iterations. load the ID of the neighbor with max hash in n-1 ring hood\n        int *level_n1_out_max_id,//for storing the ID of the n-ring neighbor with max hash (in the n-ring hood)\n\n        unsigned long long int *level_n0_used_colors_in, //for multiple iterations: load used color buffers from n-1 level\n        unsigned long long int *level_n1_used_colors_out,//for storing the used color buffers of the n-level neighborhood of each vertex\n\n        //int* interesting,\n        const int currentSafeColor, //starts assigning colors from this. multiple of 64\n        const int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        const int n1_max_id = level_n1_out_max_id[row_id];\n\n        if (n1_max_id == -1)\n        {\n            //-1 means that all the vertices in this n-ring\n            //level neighborhood were colored during the previous coloring epochs.\n            //Since nothing could have changed, just skip:\n            //save work and use the color buffers computed in last step.\n            continue;\n        }\n\n        //If the old max 'n1_max_id' is still uncolored, it still will be the max in this iteration.\n        //Plus, nothing else would get colored in its n1-hood, since 'n1_max_id' is the max.\n        if (A_colors[n1_max_id] <= 0)\n        {\n            continue;\n        }\n\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n\n        //For multiple iterations: load the used color buffer from previous iter.\n        if (USE_EXT)\n        {\n            used_colors = level_n0_used_colors_in[row_id];\n        }\n\n        if (row_color - currentSafeColor > 0) //if the color belongs to the current buffer [currentSafeColor,currentSafeColor+64)\n        {\n            used_colors |= (1ull << (64 - row_color + currentSafeColor)); //add my color to buffer\n        }\n\n        // Hash my row id.\n        int max_hash = hash_function( row_id, seed, 0 );\n        int max_hash_id = row_id; //id of the neighbor with max hash, initialized with myself\n\n        //If already colored, don't count me for finding max\n        if (row_color != 0)\n        {\n            max_hash_id = -1; //-1 means no max\n            max_hash = 0;//LONG_LONG_MIN;\n        }\n\n        //iterate the neighbors\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed, 0);\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n\n                if ( col_color - currentSafeColor > 0 ) //if in the current buffer..\n                {\n                    used_colors |= (1ull << (64 - col_color + currentSafeColor)); //..add it to buffer\n                }\n\n                if (USE_EXT)\n                {\n                    //combine used_colors buffer with the ones computed in previous iterations\n                    used_colors |= level_n0_used_colors_in[col_id];\n                    col_id = level_n0_max_id_in[col_id];\n\n                    if (col_id < 0) //row and its k-1 level neigbhors all colored\n                    {\n                        col_hash = 0;//LONG_LONG_MIN;\n                        col_color = -1;\n                    }\n                    else\n                    {\n                        col_hash  = hash_function(col_id, seed, 0); //col hash is the max hash of its neighbors in n-1 level\n                        col_color = 0; //there is an uncolored neighbor\n                    }\n                }\n            }\n\n            //check if any neighbor has a greater max id.\n            if (col_color == 0 && (max_hash_id < 0  || col_hash > max_hash || (col_hash == max_hash && col_id >= max_hash_id)))\n            {\n                max_hash_id = col_id;\n                max_hash = col_hash;\n            }\n        }\n\n        //BEGIN: Reduce used_colors/max_hashes amongst subwarps\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp    = utils::shfl_xor(max_hash, i, WARP_SIZE); //ss_max_hash[threadIdx.x+i];\n            int tmp_id = utils::shfl_xor(max_hash_id, i, WARP_SIZE);\n\n            if (tmp_id >= 0 && (max_hash_id < 0 || tmp > max_hash || (tmp == max_hash && tmp_id >= max_hash_id)))\n            {\n                max_hash = tmp;\n                max_hash_id = tmp_id;\n            }\n\n            //\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmpu = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmpu;\n        }\n\n        //END: Reduce used_colors/max_hashes amongst subwarps\n\n        //The subwarp leader stores the result.\n        if ( lane_id == 0 )\n        {\n            //if max_hash_id=1, will skip work next time: every n-ring neighbor is colored\n            level_n1_out_max_id[row_id] = max_hash_id;\n            level_n1_used_colors_out[row_id] = used_colors;\n        }\n    }\n}\n\n\n/*\n * This kernel colors rows using a greedy strategy and the used colors buffer from propagate iterations.\n */\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid color_kernel_greedy_onlymax(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    const int *max_hash_id_in_neighborhood,\n\n    const int seed,\n    int *A_colors,\n    const unsigned long long *used_colors_in,\n    int *finished_colors_flag,\n    const int currentSafeColor)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        //int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        //int row_hash = hash_function(row_id,seed,0);\n        // The number of vertices that are greater/smaller than me.\n        int max_id = max_hash_id_in_neighborhood[row_id];\n\n        if (max_id != row_id) //was not a max in its own neighborhood\n        {\n            continue;\n        }\n\n        int max_hash = hash_function(max_id, seed, 0);\n        //A_colors[row_id] = 1;\n        //continue;\n        bool is_max_vertex = true;\n        //bool all_neighbors_colored = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n        used_colors |= used_colors_in[row_id];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Filter out used colors\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                //int col_color = A_colors[col_id];\n                //if(col_color == 0) all_neighbors_colored = false;\n                used_colors |= used_colors_in[col_id];\n                int nn_max_id = max_hash_id_in_neighborhood[col_id];\n                int nn_max = hash_function(nn_max_id, seed, 0);\n                is_max_vertex &= (max_hash > nn_max || (max_hash == nn_max && row_id >= nn_max_id));\n            }\n\n            if (!is_max_vertex) { break; }\n        }\n\n        //reduce used colors bit by bit.\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color_1 = 64 - utils::bfind( ~used_colors );\n\n        if (__popc(used_colors) >= 64 || my_color_1 > 64 || my_color_1 <= 0)\n        {\n            if (lane_id == 0)\n            {\n                finished_colors_flag[0] = 1;\n            }\n\n            is_max_vertex = 0;\n        }\n\n        my_color_1 += currentSafeColor;\n\n        if ( subwarp<WARP_SIZE>::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_1;\n            }\n\n            continue;\n        }\n    }\n}\n\n\n/*\n *\n * One ring parallel greedy with greater hash count metric instead of raw hash metric. Similar to MIN_MAX_2RING\n *\n * */\ntemplate< int CTA_SIZE, int WARP_SIZE, bool USE_EXTERNAL_METRIC >\n__global__\nvoid count_gtlt_kernel( const int A_num_rows,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *__restrict A_colors,\n                        int *A_gtlt_count,\n                        const int seed,\n                        const int *metric_in)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_gtlt_count[row_id] = -1;\n            }\n\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int gt_count = 0, lt_count = 0;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        // Hash my row id.\n        int row_cols = row_end - row_begin;\n        int row_hash = hash_function( row_id, seed, row_cols );\n\n        if (USE_EXTERNAL_METRIC)\n        {\n            row_hash = metric_in[row_id] >> 16; //gt count\n        }\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n            int col_cols = 1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n#if USE_ROWS\n                col_cols = A_rows[col_id + 1] - A_rows[col_id];\n#endif\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed, col_cols);\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n\n                if (USE_EXTERNAL_METRIC)\n                {\n                    col_hash = metric_in[col_id] >> 16; //gt count\n                }\n            }\n\n            // Threads determine if they are greater than the row hash.\n            int gt_pred = col_color == 0 && col_hash > row_hash;\n            int lt_pred = col_color == 0 && col_hash < row_hash;\n            // Count greater/smaller neighbors.\n            gt_count += __popc( subwarp<WARP_SIZE>::masked_ballot( gt_pred ) );//subwarp<WARP_SIZE>::masked_ballot\n            lt_count += __popc( subwarp<WARP_SIZE>::masked_ballot( lt_pred ) );\n        }\n\n        // The warp leader stores the result.\n        int my_gtlt_count = (gt_count << 16) | lt_count;\n\n        if ( lane_id == 0 )\n        {\n            A_gtlt_count[row_id] = my_gtlt_count;\n        }\n    }\n}\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LOAD_EXTERNAL_USED_COLORS >\n__global__\nvoid color_kernel_greedy_gtlt(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    const int *A_gtlt_count, const int current_color,\n    const int weakness_bound, const int max_color_to_use,\n    int *A_colors,\n    const int min_degree, const unsigned long long *used_colors_in)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        //int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color > 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        if ( row_color < 0 )\n        {\n            A_colors[row_id] = 0; //discovered\n\n            if (row_id != 0) { continue; } //process next time\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = row_gt_count <= weakness_bound;\n        int is_min_vertex = row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        int degree = row_end - row_begin;\n\n        if (degree < min_degree && min_degree > 0)\n        {\n            continue;\n        }\n\n        unsigned long long used_colors = 0ull;\n\n        if (LOAD_EXTERNAL_USED_COLORS)\n        {\n            used_colors |= used_colors_in[row_id];\n        }\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                int col_color = A_colors[col_id];\n\n                if ( col_color > 0 )\n                {\n                    used_colors |= (1ull << (64 - col_color));\n                }\n            }\n\n            if (LOAD_EXTERNAL_USED_COLORS)\n            {\n                used_colors |= used_colors_in[col_id];\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n\n            // Threads determine if they are greater than the row hash.\n            //if( col_gt_count != -1 )\n            //  is_max_vertex &= col_gt_count < row_gt_count || (col_gt_count == row_gt_count && col_id < row_id);\n            //if( col_lt_count != -1 )\n            //  is_min_vertex &= col_lt_count /*>*/ < row_lt_count || (col_lt_count == row_lt_count && col_id > row_id);\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id >= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        //is_max_vertex = row_gt_count==0;\n        //is_min_vertex = false;\n        //reduce used colors bit by bit.\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color_1 = 0;\n        int my_color_2 = 0;\n        int free_colors = __popc(used_colors);\n        my_color_1 = 64 - utils::bfind( ~used_colors );\n        used_colors |= (1ull << (64 - my_color_1));\n        my_color_2 = 64 - utils::bfind( ~used_colors );\n        free_colors = 0;\n\n        if ( candidate && subwarp<WARP_SIZE>::all( is_max_vertex ) && my_color_1 < max_color_to_use)\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_1;\n            }\n\n            continue;\n        }\n\n        if ( candidate && subwarp<WARP_SIZE>::all( is_min_vertex ) && free_colors >= 2)\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_2;\n            }\n\n            continue;\n        }\n    }\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\nstruct is_minus_one\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == -1;\n    }\n};\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nGreedy_Min_Max_2Ring_Matrix_Coloring_Base<T_Config>::Greedy_Min_Max_2Ring_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level > 5 || this->m_coloring_level < 0)\n    {\n        FatalError( \"Not implemented for coloring_level != 1\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\") == 1 )\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    m_weakness_bound = cfg.AMG_Config::template getParameter<int>( \"weakness_bound\", cfg_scope );\n    m_late_rejection = cfg.AMG_Config::template getParameter<int>( \"late_rejection\", cfg_scope ) != 0;\n    m_coloring_try_remove_last_color = cfg.AMG_Config::template getParameter<int>( \"coloring_try_remove_last_colors\", cfg_scope );\n    m_coloring_custom_arg = cfg.AMG_Config::template getParameter<std::string>( \"coloring_custom_arg\", cfg_scope );\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate<int NNZ>\nvoid\nGreedy_Min_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_step(\n    Matrix_d &A, int seed, int max_color )\n{\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int WARP_SIZE = NNZ;\n    const int CTA_SIZE = 256;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    const int NUM_WARPS_PER_CTA_1 = CTA_SIZE;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA_1 - 1) / NUM_WARPS_PER_CTA_1 );\n    device_vector_alloc<int> finished_colors_flag(1);\n    finished_colors_flag[0] = 0;\n    int *finished_colors_flag_ptr = amgx::thrust::raw_pointer_cast( &finished_colors_flag.front() );\n    device_vector_alloc<int> gtlt_count;\n    std::vector<device_vector_alloc<int> > max_id_per_level(this->m_coloring_level);\n    std::vector<device_vector_alloc<unsigned long long int> > used_colors_per_level(this->m_coloring_level);\n\n    if (this->m_coloring_level == 1)\n    {\n        gtlt_count.resize(num_rows);\n    }\n    else\n    {\n        for (int i = 1; i < this->m_coloring_level; i++)\n        {\n            max_id_per_level[i].resize(num_rows);\n            used_colors_per_level[i].resize(num_rows);\n        }\n    }\n\n    int prev_uncolored = num_rows;\n    int currentSafeColor = 0;\n    int iter = 0;\n    int changes_seed = 0;\n    int num_color_box = 64;\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows; iter++)\n    {\n        if (this->m_coloring_level == 1)\n        {\n            //parallel greedy with greater hash count metric instead of raw hash\n            count_gtlt_kernel<CTA_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                this->m_row_colors.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                seed\n                , 0);\n            cudaCheckError();\n            color_kernel_greedy_gtlt<CTA_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound, max_color,\n                this->m_row_colors.raw(), 0, 0);\n            cudaCheckError();\n        }\n        else\n        {\n            //Any ring parallel greedy with propagation\n            for (int i = 0; i < this->m_coloring_level - 1; i++)\n            {\n                if (i == 0)\n                    find_max_neighbor_kernel_and_propagate_used_colors<false, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(num_rows,\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            this->m_row_colors.raw(),\n                            0, // in\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i + 1].data()),  //out\n                            0, // in\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i + 1].data()),  //out\n                            //amgx::thrust::raw_pointer_cast(interesting_in.data()),\n                            currentSafeColor,\n                            seed);\n                else\n                    find_max_neighbor_kernel_and_propagate_used_colors<true, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(num_rows,\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            this->m_row_colors.raw(),\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i  ].data()),  // in\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i + 1].data()),  //out\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i  ].data()),  // in\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i + 1].data()),  //out\n                            //amgx::thrust::raw_pointer_cast(interesting_in.data()),\n                            currentSafeColor,\n                            seed);\n\n                cudaCheckError();\n            }\n\n            color_kernel_greedy_onlymax<CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>( num_rows,\n                    A.row_offsets.raw(), A.col_indices.raw(),\n                    amgx::thrust::raw_pointer_cast( max_id_per_level[this->m_coloring_level - 1].data()), //in: max_id_tmp\n                    seed,\n                    this->m_row_colors.raw(),\n                    (unsigned long long *)amgx::thrust::raw_pointer_cast( used_colors_per_level[this->m_coloring_level - 1].data() ),\n                    finished_colors_flag_ptr,\n                    currentSafeColor);\n            cudaCheckError();\n        }\n\n        int nnz_per_row = float(A.get_num_nz()) / A.get_num_rows();\n\n        if (iter > std::min((int)std::pow(nnz_per_row, this->m_coloring_level), 63)) //estimate number of epochs required for coloring\n        {\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n            cudaCheckError();\n\n            if (num_uncolored == prev_uncolored)\n            {\n                max_color++;\n\n                if (this->m_coloring_level >= 2)\n                {\n                    //printf(\"%d Fail with numcolor %d %d\\n\",seed,max_color,num_uncolored);\n                    if (changes_seed < 0)\n                    {\n                        seed++;\n                        changes_seed++;\n                    }\n                    else\n                    {\n                        changes_seed = 0;\n                        currentSafeColor += num_color_box;\n\n                        //reset buffers\n                        for (int i = 1; i < this->m_coloring_level; i++)\n                        {\n                            thrust_wrapper::fill<AMGX_device>(max_id_per_level[i].begin(), max_id_per_level[i]    .end(), 0);\n                            thrust_wrapper::fill<AMGX_device>(used_colors_per_level[i].begin(), used_colors_per_level[i].end(), 0);\n                        }\n\n                        cudaCheckError();\n                        //printf(\"opening new color box %d\\n\",currentSafeColor);\n                    }\n                }\n            }\n\n            prev_uncolored = num_uncolored;\n            //printf(\"%d\\t%d\\t%d\\n\", iter, 0, num_uncolored);\n        }\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n}\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nGreedy_Min_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    float avg_nnz_per_row = A.get_num_nz() / float(A.get_num_rows());\n    //Choose subwarp (group of threads processing a row) size\n    {\n        if ( avg_nnz_per_row < 4.0f )\n        {\n            color_step<1>(A, 0, 64);\n        }\n        else if ( avg_nnz_per_row < 8.0f )\n        {\n            color_step<1>(A, 0, 64);\n        }\n        else if (avg_nnz_per_row < 32)\n        {\n            color_step<4>(A, 0, 64);\n        }\n        else\n        {\n            color_step<32>(A, 0, 64);\n        }\n    }\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Min_Max_2Ring_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Min_Max_2Ring_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/greedy_min_max_2ring.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n#include <thrust/remove.h>\n#include  <matrix_coloring/coloring_utils.h>\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\nstatic __host__ __device__ __forceinline__ unsigned int hash_function(unsigned int a, unsigned int seed, unsigned int rows = 0)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n// ---------------------------\n// Kernels: Any ring parallel greedy with color buffers and max id propagation.\n// ---------------------------\n\n\n/*\n * This kernel computes the index of the node with maximum hash in a neighborhood and the colors used in a vertex neighborhood.\n * Since both operation are associative (max and or), it is possible to iterate the operation propagating the values across neigborhoods (first, second and so on rings).\n *\n * The N-th iteration computes, for each vertex:\n * 1. The ID of the uncolored vertex with ID of the vertex having max hash inside the N-ring neighborhood.\n *    If all neighbors in the N-hood are colored, saves -1 (this allows work efficiency).\n * 2. The buffer of used colors in the n-ring neighborhood.\n *\n * And for that, it uses data from previous iteration.\n *\n * For avoiding the waste of work, once any n-ring neighborhood is all colored, it starts getting ignored:\n *  no new MAX HASH can be found there and the set of used colors for that ring will not change.\n */\n\n\ntemplate< bool USE_EXT, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid find_max_neighbor_kernel_and_propagate_used_colors( const int A_num_rows,\n        const int *A_rows,\n        const int *A_cols,\n        const int *__restrict A_colors,\n\n        int *level_n0_max_id_in, //for multiple iterations. load the ID of the neighbor with max hash in n-1 ring hood\n        int *level_n1_out_max_id,//for storing the ID of the n-ring neighbor with max hash (in the n-ring hood)\n\n        unsigned long long int *level_n0_used_colors_in, //for multiple iterations: load used color buffers from n-1 level\n        unsigned long long int *level_n1_used_colors_out,//for storing the used color buffers of the n-level neighborhood of each vertex\n\n        //int* interesting,\n        const int currentSafeColor, //starts assigning colors from this. multiple of 64\n        const int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        const int n1_max_id = level_n1_out_max_id[row_id];\n\n        if (n1_max_id == -1)\n        {\n            //-1 means that all the vertices in this n-ring\n            //level neighborhood were colored during the previous coloring epochs.\n            //Since nothing could have changed, just skip:\n            //save work and use the color buffers computed in last step.\n            continue;\n        }\n\n        //If the old max 'n1_max_id' is still uncolored, it still will be the max in this iteration.\n        //Plus, nothing else would get colored in its n1-hood, since 'n1_max_id' is the max.\n        if (A_colors[n1_max_id] <= 0)\n        {\n            continue;\n        }\n\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n\n        //For multiple iterations: load the used color buffer from previous iter.\n        if (USE_EXT)\n        {\n            used_colors = level_n0_used_colors_in[row_id];\n        }\n\n        if (row_color - currentSafeColor > 0) //if the color belongs to the current buffer [currentSafeColor,currentSafeColor+64)\n        {\n            used_colors |= (1ull << (64 - row_color + currentSafeColor)); //add my color to buffer\n        }\n\n        // Hash my row id.\n        int max_hash = hash_function( row_id, seed, 0 );\n        int max_hash_id = row_id; //id of the neighbor with max hash, initialized with myself\n\n        //If already colored, don't count me for finding max\n        if (row_color != 0)\n        {\n            max_hash_id = -1; //-1 means no max\n            max_hash = 0;//LONG_LONG_MIN;\n        }\n\n        //iterate the neighbors\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed, 0);\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n\n                if ( col_color - currentSafeColor > 0 ) //if in the current buffer..\n                {\n                    used_colors |= (1ull << (64 - col_color + currentSafeColor)); //..add it to buffer\n                }\n\n                if (USE_EXT)\n                {\n                    //combine used_colors buffer with the ones computed in previous iterations\n                    used_colors |= level_n0_used_colors_in[col_id];\n                    col_id = level_n0_max_id_in[col_id];\n\n                    if (col_id < 0) //row and its k-1 level neigbhors all colored\n                    {\n                        col_hash = 0;//LONG_LONG_MIN;\n                        col_color = -1;\n                    }\n                    else\n                    {\n                        col_hash  = hash_function(col_id, seed, 0); //col hash is the max hash of its neighbors in n-1 level\n                        col_color = 0; //there is an uncolored neighbor\n                    }\n                }\n            }\n\n            //check if any neighbor has a greater max id.\n            if (col_color == 0 && (max_hash_id < 0  || col_hash > max_hash || (col_hash == max_hash && col_id >= max_hash_id)))\n            {\n                max_hash_id = col_id;\n                max_hash = col_hash;\n            }\n        }\n\n        //BEGIN: Reduce used_colors/max_hashes amongst subwarps\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp    = utils::shfl_xor(max_hash, i, WARP_SIZE); //ss_max_hash[threadIdx.x+i];\n            int tmp_id = utils::shfl_xor(max_hash_id, i, WARP_SIZE);\n\n            if (tmp_id >= 0 && (max_hash_id < 0 || tmp > max_hash || (tmp == max_hash && tmp_id >= max_hash_id)))\n            {\n                max_hash = tmp;\n                max_hash_id = tmp_id;\n            }\n\n            //\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmpu = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmpu;\n        }\n\n        //END: Reduce used_colors/max_hashes amongst subwarps\n\n        //The subwarp leader stores the result.\n        if ( lane_id == 0 )\n        {\n            //if max_hash_id=1, will skip work next time: every n-ring neighbor is colored\n            level_n1_out_max_id[row_id] = max_hash_id;\n            level_n1_used_colors_out[row_id] = used_colors;\n        }\n    }\n}\n\n\n/*\n * This kernel colors rows using a greedy strategy and the used colors buffer from propagate iterations.\n */\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid color_kernel_greedy_onlymax(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    const int *max_hash_id_in_neighborhood,\n\n    const int seed,\n    int *A_colors,\n    const unsigned long long *used_colors_in,\n    int *finished_colors_flag,\n    const int currentSafeColor)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        //int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        //int row_hash = hash_function(row_id,seed,0);\n        // The number of vertices that are greater/smaller than me.\n        int max_id = max_hash_id_in_neighborhood[row_id];\n\n        if (max_id != row_id) //was not a max in its own neighborhood\n        {\n            continue;\n        }\n\n        int max_hash = hash_function(max_id, seed, 0);\n        //A_colors[row_id] = 1;\n        //continue;\n        bool is_max_vertex = true;\n        //bool all_neighbors_colored = true;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n        used_colors |= used_colors_in[row_id];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Filter out used colors\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                //int col_color = A_colors[col_id];\n                //if(col_color == 0) all_neighbors_colored = false;\n                used_colors |= used_colors_in[col_id];\n                int nn_max_id = max_hash_id_in_neighborhood[col_id];\n                int nn_max = hash_function(nn_max_id, seed, 0);\n                is_max_vertex &= (max_hash > nn_max || (max_hash == nn_max && row_id >= nn_max_id));\n            }\n\n            if (!is_max_vertex) { break; }\n        }\n\n        //reduce used colors bit by bit.\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color_1 = 64 - utils::bfind( ~used_colors );\n\n        if (__popc(used_colors) >= 64 || my_color_1 > 64 || my_color_1 <= 0)\n        {\n            if (lane_id == 0)\n            {\n                finished_colors_flag[0] = 1;\n            }\n\n            is_max_vertex = 0;\n        }\n\n        my_color_1 += currentSafeColor;\n\n        if ( subwarp<WARP_SIZE>::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_1;\n            }\n\n            continue;\n        }\n    }\n}\n\n\n/*\n *\n * One ring parallel greedy with greater hash count metric instead of raw hash metric. Similar to MIN_MAX_2RING\n *\n * */\ntemplate< int CTA_SIZE, int WARP_SIZE, bool USE_EXTERNAL_METRIC >\n__global__\nvoid count_gtlt_kernel( const int A_num_rows,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *__restrict A_colors,\n                        int *A_gtlt_count,\n                        const int seed,\n                        const int *metric_in)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_gtlt_count[row_id] = -1;\n            }\n\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int gt_count = 0, lt_count = 0;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        // Hash my row id.\n        int row_cols = row_end - row_begin;\n        int row_hash = hash_function( row_id, seed, row_cols );\n\n        if (USE_EXTERNAL_METRIC)\n        {\n            row_hash = metric_in[row_id] >> 16; //gt count\n        }\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n            int col_cols = 1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n#if USE_ROWS\n                col_cols = A_rows[col_id + 1] - A_rows[col_id];\n#endif\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed, col_cols);\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n\n                if (USE_EXTERNAL_METRIC)\n                {\n                    col_hash = metric_in[col_id] >> 16; //gt count\n                }\n            }\n\n            // Threads determine if they are greater than the row hash.\n            int gt_pred = col_color == 0 && col_hash > row_hash;\n            int lt_pred = col_color == 0 && col_hash < row_hash;\n            // Count greater/smaller neighbors.\n            gt_count += __popc( subwarp<WARP_SIZE>::masked_ballot( gt_pred ) );//subwarp<WARP_SIZE>::masked_ballot\n            lt_count += __popc( subwarp<WARP_SIZE>::masked_ballot( lt_pred ) );\n        }\n\n        // The warp leader stores the result.\n        int my_gtlt_count = (gt_count << 16) | lt_count;\n\n        if ( lane_id == 0 )\n        {\n            A_gtlt_count[row_id] = my_gtlt_count;\n        }\n    }\n}\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LOAD_EXTERNAL_USED_COLORS >\n__global__\nvoid color_kernel_greedy_gtlt(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    const int *A_gtlt_count, const int current_color,\n    const int weakness_bound, const int max_color_to_use,\n    int *A_colors,\n    const int min_degree, const unsigned long long *used_colors_in)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        //int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color > 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        if ( row_color < 0 )\n        {\n            A_colors[row_id] = 0; //discovered\n\n            if (row_id != 0) { continue; } //process next time\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = row_gt_count <= weakness_bound;\n        int is_min_vertex = row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        int degree = row_end - row_begin;\n\n        if (degree < min_degree && min_degree > 0)\n        {\n            continue;\n        }\n\n        unsigned long long used_colors = 0ull;\n\n        if (LOAD_EXTERNAL_USED_COLORS)\n        {\n            used_colors |= used_colors_in[row_id];\n        }\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                int col_color = A_colors[col_id];\n\n                if ( col_color > 0 )\n                {\n                    used_colors |= (1ull << (64 - col_color));\n                }\n            }\n\n            if (LOAD_EXTERNAL_USED_COLORS)\n            {\n                used_colors |= used_colors_in[col_id];\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n\n            // Threads determine if they are greater than the row hash.\n            //if( col_gt_count != -1 )\n            //  is_max_vertex &= col_gt_count < row_gt_count || (col_gt_count == row_gt_count && col_id < row_id);\n            //if( col_lt_count != -1 )\n            //  is_min_vertex &= col_lt_count /*>*/ < row_lt_count || (col_lt_count == row_lt_count && col_id > row_id);\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id >= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        //is_max_vertex = row_gt_count==0;\n        //is_min_vertex = false;\n        //reduce used colors bit by bit.\n#pragma unroll\n\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2loint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color_1 = 0;\n        int my_color_2 = 0;\n        int free_colors = __popc(used_colors);\n        my_color_1 = 64 - utils::bfind( ~used_colors );\n        used_colors |= (1ull << (64 - my_color_1));\n        my_color_2 = 64 - utils::bfind( ~used_colors );\n        free_colors = 0;\n\n        if ( candidate && subwarp<WARP_SIZE>::all( is_max_vertex ) && my_color_1 < max_color_to_use)\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_1;\n            }\n\n            continue;\n        }\n\n        if ( candidate && subwarp<WARP_SIZE>::all( is_min_vertex ) && free_colors >= 2)\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color_2;\n            }\n\n            continue;\n        }\n    }\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\nstruct is_minus_one\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == -1;\n    }\n};\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nGreedy_Min_Max_2Ring_Matrix_Coloring_Base<T_Config>::Greedy_Min_Max_2Ring_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level > 5 || this->m_coloring_level < 0)\n    {\n        FatalError( \"Not implemented for coloring_level != 1\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\") == 1 )\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    m_weakness_bound = cfg.AMG_Config::template getParameter<int>( \"weakness_bound\", cfg_scope );\n    m_late_rejection = cfg.AMG_Config::template getParameter<int>( \"late_rejection\", cfg_scope ) != 0;\n    m_coloring_try_remove_last_color = cfg.AMG_Config::template getParameter<int>( \"coloring_try_remove_last_colors\", cfg_scope );\n    m_coloring_custom_arg = cfg.AMG_Config::template getParameter<std::string>( \"coloring_custom_arg\", cfg_scope );\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate<int NNZ>\nvoid\nGreedy_Min_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_step(\n    Matrix_d &A, int seed, int max_color )\n{\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int WARP_SIZE = NNZ;\n    const int CTA_SIZE = 256;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    const int NUM_WARPS_PER_CTA_1 = CTA_SIZE;\n    const int GRID_SIZE_1 = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA_1 - 1) / NUM_WARPS_PER_CTA_1 );\n    device_vector_alloc<int> finished_colors_flag(1);\n    finished_colors_flag[0] = 0;\n    int *finished_colors_flag_ptr = amgx::thrust::raw_pointer_cast( &finished_colors_flag.front() );\n    device_vector_alloc<int> gtlt_count;\n    std::vector<device_vector_alloc<int> > max_id_per_level(this->m_coloring_level);\n    std::vector<device_vector_alloc<unsigned long long int> > used_colors_per_level(this->m_coloring_level);\n\n    if (this->m_coloring_level == 1)\n    {\n        gtlt_count.resize(num_rows);\n    }\n    else\n    {\n        for (int i = 1; i < this->m_coloring_level; i++)\n        {\n            max_id_per_level[i].resize(num_rows);\n            used_colors_per_level[i].resize(num_rows);\n        }\n    }\n\n    int prev_uncolored = num_rows;\n    int currentSafeColor = 0;\n    int iter = 0;\n    int changes_seed = 0;\n    int num_color_box = 64;\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows; iter++)\n    {\n        if (this->m_coloring_level == 1)\n        {\n            //parallel greedy with greater hash count metric instead of raw hash\n            count_gtlt_kernel<CTA_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                this->m_row_colors.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                seed\n                , 0);\n            cudaCheckError();\n            color_kernel_greedy_gtlt<CTA_SIZE, WARP_SIZE, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound, max_color,\n                this->m_row_colors.raw(), 0, 0);\n            cudaCheckError();\n        }\n        else\n        {\n            //Any ring parallel greedy with propagation\n            for (int i = 0; i < this->m_coloring_level - 1; i++)\n            {\n                if (i == 0)\n                    find_max_neighbor_kernel_and_propagate_used_colors<false, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(num_rows,\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            this->m_row_colors.raw(),\n                            0, // in\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i + 1].data()),  //out\n                            0, // in\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i + 1].data()),  //out\n                            //amgx::thrust::raw_pointer_cast(interesting_in.data()),\n                            currentSafeColor,\n                            seed);\n                else\n                    find_max_neighbor_kernel_and_propagate_used_colors<true, CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(num_rows,\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            this->m_row_colors.raw(),\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i  ].data()),  // in\n                            amgx::thrust::raw_pointer_cast(max_id_per_level[i + 1].data()),  //out\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i  ].data()),  // in\n                            amgx::thrust::raw_pointer_cast(used_colors_per_level[i + 1].data()),  //out\n                            //amgx::thrust::raw_pointer_cast(interesting_in.data()),\n                            currentSafeColor,\n                            seed);\n\n                cudaCheckError();\n            }\n\n            color_kernel_greedy_onlymax<CTA_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>( num_rows,\n                    A.row_offsets.raw(), A.col_indices.raw(),\n                    amgx::thrust::raw_pointer_cast( max_id_per_level[this->m_coloring_level - 1].data()), //in: max_id_tmp\n                    seed,\n                    this->m_row_colors.raw(),\n                    (unsigned long long *)amgx::thrust::raw_pointer_cast( used_colors_per_level[this->m_coloring_level - 1].data() ),\n                    finished_colors_flag_ptr,\n                    currentSafeColor);\n            cudaCheckError();\n        }\n\n        int nnz_per_row = float(A.get_num_nz()) / A.get_num_rows();\n\n        if (iter > std::min((int)std::pow(nnz_per_row, this->m_coloring_level), 63)) //estimate number of epochs required for coloring\n        {\n            num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n            cudaCheckError();\n\n            if (num_uncolored == prev_uncolored)\n            {\n                max_color++;\n\n                if (this->m_coloring_level >= 2)\n                {\n                    //printf(\"%d Fail with numcolor %d %d\\n\",seed,max_color,num_uncolored);\n                    if (changes_seed < 0)\n                    {\n                        seed++;\n                        changes_seed++;\n                    }\n                    else\n                    {\n                        changes_seed = 0;\n                        currentSafeColor += num_color_box;\n\n                        //reset buffers\n                        for (int i = 1; i < this->m_coloring_level; i++)\n                        {\n                            thrust_wrapper::fill<AMGX_device>(max_id_per_level[i].begin(), max_id_per_level[i]    .end(), 0);\n                            thrust_wrapper::fill<AMGX_device>(used_colors_per_level[i].begin(), used_colors_per_level[i].end(), 0);\n                        }\n\n                        cudaCheckError();\n                        //printf(\"opening new color box %d\\n\",currentSafeColor);\n                    }\n                }\n            }\n\n            prev_uncolored = num_uncolored;\n            //printf(\"%d\\t%d\\t%d\\n\", iter, 0, num_uncolored);\n        }\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n}\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nGreedy_Min_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    float avg_nnz_per_row = A.get_num_nz() / float(A.get_num_rows());\n    //Choose subwarp (group of threads processing a row) size\n    {\n        if ( avg_nnz_per_row < 4.0f )\n        {\n            color_step<1>(A, 0, 64);\n        }\n        else if ( avg_nnz_per_row < 8.0f )\n        {\n            color_step<1>(A, 0, 64);\n        }\n        else if (avg_nnz_per_row < 32)\n        {\n            color_step<4>(A, 0, 64);\n        }\n        else\n        {\n            color_step<32>(A, 0, 64);\n        }\n    }\n    A.setView(oldView);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Min_Max_2Ring_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Min_Max_2Ring_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n", "source": "/kaggle/working/amgx/src/matrix_coloring/greedy_min_max_2ring.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/greedy_recolor.h>\n#include <strided_reduction.h>\n#include <math.h> //std::pow\n#include <matrix_coloring/coloring_utils.h>\n#include <map>\n#include <thrust/sort.h>\n#include <thrust/count.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/binary_search.h>\n#include <thrust/replace.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n\n#define GET_COLORS_THRUST 0\n#define GET_COLORS_TWOPASS 1\n#define GET_COLORS_ATOMIC 2\n#define GET_COLORS_GATHER 3\n#define GETMAX_THRUST  0\n#define GETMAX_TWOPASS 1\n\n//CONFIG\n\n#define GET_COLORS GET_COLORS_TWOPASS\n//#define GET_COLORS GET_COLORS_THRUST\n\n#define GETMAX_COLOR GETMAX_TWOPASS\n//#define GETMAX_COLOR GETMAX_THRUST\n\n//#define DO_SORT 1\n#define USE_GTLT 0\n#define DISCARD_COLORED (1 && !USE_GTLT)\n#define ONLY_FIRSTSTEP 0\n\nnamespace amgx\n{\n//normal hash function\n__host__ __device__ __forceinline__ unsigned int hash2(unsigned int a, const unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n//rehashes a given hash (h), rotating its bits.\n__host__ __device__ __forceinline__ unsigned int rehash(const unsigned int &a, const unsigned int h, const  unsigned int k, const unsigned int seed)\n{\n    return (h << k) ^ (h >> k);\n}\n\n//compute hashes for a column, compare with the row's hashes and the updated bitfield is_max/is_min for the row.\ntemplate<int HASHES>\n__device__ __forceinline__ void compute_hashes(const int &row_id, const int &col_id, const int &base_row_hash, const int &base_col_hash, const int &seed, unsigned long long int &is_min, unsigned long long int &is_max)\n{\n    int col_hash_k = base_col_hash;\n    int row_hash_k = base_row_hash;\n#pragma unroll\n\n    for (int k = 0; k < HASHES; k++)\n    {\n        col_hash_k = rehash(col_id, base_col_hash, k, seed);\n        row_hash_k = rehash(row_id, base_row_hash, k, seed);\n        unsigned long long q = (~(1ull << (63 - k))); //bit of hash k in bitfields\n\n        if (col_hash_k > row_hash_k || (col_hash_k == row_hash_k && row_id < col_id))\n        {\n            is_max &= q;\n        }\n\n        //unsigned long long x2 = col_hash_k < row_hash_k;\n        if (col_hash_k < row_hash_k || (col_hash_k == row_hash_k && row_id > col_id))\n        {\n            is_min &= q;\n        }\n    }\n}\n\ntemplate<int COLORING_LEVEL>\nstruct neigbor_visitor\n{\n\n    template<int WARP_SIZE, int HASHES, bool DISCARD_COLORED_T>\n    static __device__ __forceinline__ void visit(const int &row_id, const int &base_row_hash, const int &explore_row, const int *A_rows, const int *A_cols, const int *A_colors_in, const int lane_id, const int seed, unsigned long long int &is_min, unsigned long long int &is_max)\n    {\n        int row_begin = A_rows[explore_row  ];\n        int row_end   = A_rows[explore_row + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n        {\n            int row_it = row_begin + lane_id;\n            int col_id = -1;\n            bool do_compute_hashes = true;\n            col_id = A_cols[row_it];//__load_global(A_cols+row_it);\n#if DISCARD_COLORED\n\n            if (DISCARD_COLORED_T)\n            {\n                int col_color = __ldg(A_colors_in + col_id);\n\n                if (col_color != 0) { do_compute_hashes = false; }\n            }\n\n#endif\n\n            if (do_compute_hashes)\n            {\n                //#pragma unroll\n                //for(int k=0; k<(HASHES/16>0)?HASHES/16:1; k++)\n                //{\n                int base_col_hash = hash2(col_id, seed/*+k*12358*/);\n                compute_hashes<HASHES>(row_id, col_id, base_row_hash, base_col_hash, seed, is_min, is_max);\n                //}\n            }\n\n            neigbor_visitor < COLORING_LEVEL - 1 >::template visit<WARP_SIZE, HASHES, DISCARD_COLORED_T>(row_id, base_row_hash, col_id, A_rows, A_cols, A_colors_in, lane_id, seed, is_min, is_max);\n        }\n    }\n\n};\ntemplate<>\nstruct neigbor_visitor<0>\n{\n\n    template<int WARP_SIZE, int HASHES, bool DISCARD_COLORED_T>\n    static __device__ __forceinline__ void visit(const int &row_id, const int &base_row_hash, const int &explore_row, const int *A_rows, const int *A_cols, const int *A_colors_in, const int lane_id, const int seed, unsigned long long int &is_min, unsigned long long int &is_max)\n    {\n        //is_min |= 0ull;\n    }\n\n};\n\n\ntemplate< bool DISCARD_COLORED_T, int HASHES, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, const int *A_colors_in,\n    int c0, int seed,\n    int *sets_per_block)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int warp_count = 0;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int available_color = 0;\n\n        if (row_id < A_num_rows)\n        {\n#if DISCARD_COLORED\n\n            if (DISCARD_COLORED_T)\n            {\n                row_color = A_colors_in[row_id];\n                A_colors[row_id] = row_color;\n            }\n            else\n#endif\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        if (row_color == 0)\n        {\n            unsigned long long int is_min = ~0ull;\n            unsigned long long int is_max = ~0ull;\n            int base_row_hash = hash2(row_id, seed);\n#if 1 //COLORING_LEVELR>1\n            neigbor_visitor<COLORING_LEVEL>::template visit<WARP_SIZE, HASHES, DISCARD_COLORED_T>(row_id, base_row_hash, row_id, A_rows, A_cols, A_colors_in, lane_id, seed, is_min, is_max);\n#else\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                col_id = A_cols[row_it];//__load_global(A_cols+row_it);\n#if DISCARD_COLORED\n\n                if (DISCARD_COLORED_T)\n                {\n                    int col_color = __ldg(A_colors_in + col_id);\n\n                    if (col_color != 0) { continue; }\n                }\n\n#endif\n                int base_col_hash = hash2(col_id, seed);\n                compute_hashes<HASHES>(row_id, col_id, base_row_hash, base_col_hash, seed, is_min, is_max);\n            }\n\n#endif\n\n            if (WARP_SIZE > 1)\n            {\n                using namespace amgx::strided_reduction;\n                amgx::strided_reduction::op_and op;\n                warp_reduce<1, CTA_SIZE, WARP_SIZE>(is_max, op);\n                warp_reduce<1, CTA_SIZE, WARP_SIZE>(is_min, op);\n            }\n\n#if 1\n            int cmax = 63 - utils::bfind(is_max);\n            int cmin = 63 - utils::bfind(is_min);\n\n            if (cmin < HASHES)\n            {\n                available_color = cmin * 2 + 2;\n            }\n\n            if (cmax < HASHES)\n            {\n                available_color = cmax * 2 + 1;\n            }\n\n#else\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                if (is_max & (1ull << 63 - k))\n                {\n                    available_color = 2 * k + 1;\n                    break;\n                }\n                else if (is_min & (1ull << 63 - k))\n                {\n                    available_color = 2 * k + 2;\n                    break;\n                }\n            }\n\n#endif\n        }\n\n        bool has_set_color = (lane_id == 0) && (available_color != 0);\n\n        if (has_set_color)\n        {\n            A_colors[row_id] = available_color + c0;\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_color);\n    }\n\n#if GET_COLORS == GET_COLORS_ATOMIC\n    amgx::strided_reduction::block_count_atomic<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n    //__threadfence_system();\n#elif GET_COLORS == GET_COLORS_TWOPASS\n    amgx::strided_reduction::block_count<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n#elif GET_COLORS == GET_COLORS_GATHER\n    amgx::strided_reduction::block_count<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n    amgx::strided_reduction::block_count_gatherer<int, 1> global_gather(sets_per_block);\n#endif\n}\n\n////////////////////////////\n\ntemplate< int HASHES, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel_gtlt_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, unsigned long long int *gtlt_out,\n    int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    //int warp_count = 0;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n\n        if (row_id < A_num_rows)\n        {\n            row_color = A_colors[row_id];\n        }\n\n        if (row_color == 0)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n            int base_row_hash = hash2(row_id, seed);\n            unsigned char count_gt[HASHES];\n            unsigned char count_lt[HASHES];\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                count_gt[k] = 0;\n                count_lt[k] = 0;\n            }\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                col_id = A_cols[row_it];\n                int col_color = A_colors[col_id];\n\n                if (col_color != 0) { continue; }\n\n                int base_col_hash = hash2(col_id, seed);\n                int col_hash_k = base_col_hash;\n                int row_hash_k = base_row_hash;\n#pragma unroll\n\n                for (int k = 0; k < HASHES; k++)\n                {\n                    col_hash_k = rehash(col_id, base_col_hash, k, seed);\n                    row_hash_k = rehash(row_id, base_row_hash, k, seed);\n\n                    if (col_hash_k > row_hash_k || (col_hash_k == row_hash_k && row_id < col_id))\n                    {\n                        if (count_gt[k] < 255) //avoid overflow\n                        {\n                            ++count_gt[k];\n                        }\n                    }\n\n                    if (col_hash_k < row_hash_k || (col_hash_k == row_hash_k && row_id > col_id))\n                    {\n                        if (count_lt[k] < 255)\n                        {\n                            ++count_lt[k];\n                        }\n                    }\n                }\n            }\n\n            //compress count_gt,lt in 2 bits and pack in an integer\n            unsigned long long int packed_gtlt = 0;\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                unsigned long long int max3_gt = count_gt[k];\n                unsigned long long int max3_lt = count_lt[k];\n\n                if (max3_gt > 3) { max3_gt = 3; } //TODO use only bits above\n\n                if (max3_lt > 3) { max3_lt = 3; }\n\n                //TODO: use BFI\n                packed_gtlt |= (max3_gt << (k * 2));\n                packed_gtlt |= (max3_lt << (k * 2 + 32));\n            }\n\n            if (lane_id == 0)\n            {\n                gtlt_out[row_id] = packed_gtlt;\n            }\n        }\n    }\n}\n\ntemplate< int HASHES, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel_gtlt_assign_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, unsigned long long int *gtlt_in,\n    int c0)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        unsigned long long int row_gtlt = 0ull;\n\n        if (row_id < A_num_rows)\n        {\n            row_color = A_colors[row_id];\n            row_gtlt = gtlt_in[row_id];\n        }\n\n        int available_color = 0;\n\n        if (row_color == 0)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n            unsigned long long int is_min = ~0ull;\n            unsigned long long int is_max = ~0ull;\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                unsigned long long int col_gtlt = 0ull;\n\n                if (row_it < row_end)\n                {\n                    col_id   = A_cols[row_it];\n                    col_gtlt = gtlt_in[col_id];\n#pragma unroll\n\n                    for (int k = 0; k < HASHES; k++)\n                    {\n                        int row_gt_k = (row_gtlt >> (k * 2 + 0)) & 3ull;\n                        int row_lt_k = (row_gtlt >> (k * 2 + 32)) & 3ull;\n                        int col_gt_k = (col_gtlt >> (k * 2 + 0)) & 3ull;\n                        int col_lt_k = (col_gtlt >> (k * 2 + 32)) & 3ull;\n                        unsigned long long q = (~(1ull << (63 - k)));\n\n                        if ((row_gt_k > col_gt_k) || (col_gt_k == row_gt_k && row_id < col_id))\n                        {\n                            is_max &= q; //xor with k bit: if was 1, becomes 0, if was 0 stays 0\n                        }\n\n                        if ((row_lt_k > col_lt_k) || (col_lt_k == row_lt_k && row_id > col_id))\n                        {\n                            is_min &= q;\n                        }\n                    }\n                }\n            }\n\n            int cmax = 63 - utils::bfind(is_max);\n            int cmin = 63 - utils::bfind(is_min);\n\n            //if(cmin < HASHES)\n            //  available_color = cmin*2+2;\n            if (cmax < HASHES)\n            {\n                available_color = cmax * 2 + 1;\n            }\n        }\n\n        bool has_set_color = (lane_id == 0) && (available_color != 0);\n\n        if (has_set_color)\n        {\n            A_colors[row_id] = available_color + c0;\n        }\n    }\n}\n\n////////////////////////////\n\ntemplate< bool SORTED_ROWS, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid recolor_greedy_kernel(\n    const int A_num_rows, const int *__restrict A_rows,\n    const int *__restrict A_cols,\n    int color_to_reassign,\n    int *__restrict A_colors,\n    int *sorted_rows_by_color, int offset_start, int offset_end\n    , int *maxs_per_block\n)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int job_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int last_job  = A_num_rows;\n\n    if (SORTED_ROWS)\n    {\n        job_id = job_id + offset_start;\n        last_job  = offset_end;\n    }\n\n    int thread_max = 0;\n\n    //const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    //int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; utils::any(job_id < last_job) ; job_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int row_id = -1;\n\n        if (job_id < last_job)\n        {\n            if (SORTED_ROWS)\n            {\n                row_id    = sorted_rows_by_color[job_id];\n            }\n            else\n            {\n                row_id = job_id;\n            }\n\n            if (row_id < A_num_rows)\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        used_color_structure_64_bit used_colors;\n        used_color_structure_64_bit::aux_tmp aux;\n        //used_color_structure_64_bit_colorbox<5> used_colors; //still buggy\n\n        if (row_color == color_to_reassign)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            //used_colors.use_color(color_to_reassign);\n            //int num_greater = 0;\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n\n                if (row_it < row_end)\n                {\n                    col_id = A_cols[row_it];\n                }\n\n                if (row_it < row_end)// && col_id < A_num_rows)\n                {\n                    int col_color = A_colors[col_id];\n\n                    //if(col_color>40) printf(\"%d\\n\",col_color);\n                    if (col_color != 0)\n                    {\n                        used_colors.use_color(col_color, aux);\n                    }\n                }\n\n                if (COLORING_LEVEL == 2)\n                {\n                    int row_begin = A_rows[col_id  ];\n                    int row_end   = A_rows[col_id + 1];\n\n                    for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n                    {\n                        int row_it = row_begin + lane_id;\n                        int col_id = -1;\n\n                        if (row_it < row_end)\n                        {\n                            col_id = A_cols[row_it];\n                        }\n\n                        if (row_it < row_end)// && col_id < A_num_rows)\n                        {\n                            int col_color = A_colors[col_id];\n\n                            //if(col_color>40) printf(\"%d\\n\",col_color);\n                            if (col_color != 0)\n                            {\n                                used_colors.use_color(col_color, aux);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        used_colors.sync_subwarp<CTA_SIZE, WARP_SIZE>(lane_id);\n\n        /*if(row_color != color_to_reassign && row_id != -1)\n        {\n          printf(\"ERR %d %d!\\n\", row_color, color_to_reassign);\n        }*/\n        if (row_color == color_to_reassign && lane_id == 0)\n        {\n            int new_color = used_colors.first_available_color(aux);\n\n            if (new_color != row_color && new_color > 0)\n            {\n                //printf(\"changing %d from %d to %d\\n\",row_id,row_color,new_color);\n                A_colors[row_id] = new_color;\n            }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n\n            if (new_color > thread_max)\n            {\n                thread_max = new_color;\n            }\n\n#endif\n            /*if(new_color >= 7)\n            {\n                printf(\"SEVEN %d %d\\n\", new_color, thread_max);\n            }*/\n        }\n    }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    using namespace amgx::strided_reduction;\n    amgx::strided_reduction::op_max op;\n    warp_reduce<1, CTA_SIZE, 32>(thread_max, op);\n    block_reduce<1, CTA_SIZE, 32, true>(thread_max, maxs_per_block, op);\n#endif\n}\n\n////////////////////////////\n\n\n\ntemplate< bool SORTED_ROWS, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid recolor_greedy_kernel_anyring(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int color_to_reassign, int *A_colors,\n    int *sorted_rows_by_color, int offset_start, int offset_end, int *maxs_per_block,\n\n    used_color_structure_64_bit *used_colors_inout\n)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int job_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int last_job  = A_num_rows;\n\n    if (SORTED_ROWS)\n    {\n        job_id = job_id + offset_start;\n        last_job  = offset_end;\n    }\n\n    int thread_max = 0;\n\n    //const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    //int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; utils::any(job_id < last_job) ; job_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int row_id = -1;\n\n        if (job_id < last_job)\n        {\n            if (SORTED_ROWS)\n            {\n                row_id    = sorted_rows_by_color[job_id];\n            }\n            else\n            {\n                row_id = job_id;\n            }\n\n            if (row_id < A_num_rows)\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        used_color_structure_64_bit used_colors;\n        used_color_structure_64_bit::aux_tmp aux;\n        used_colors = used_colors_inout[row_id];\n        //used_color_structure_64_bit_colorbox<5> used_colors; //still buggy\n\n        if (row_color == color_to_reassign)\n        {\n            int new_color = used_colors.first_available_color(aux);\n\n            if (lane_id == 0)\n            {\n                if (new_color != row_color && new_color > 0)\n                {\n                    //printf(\"changing %d from %d to %d\\n\",row_id,row_color,new_color);\n                    A_colors[row_id] = new_color;\n                }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n\n                if (new_color > thread_max)\n                {\n                    thread_max = new_color;\n                }\n\n#endif\n            }\n\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n\n                if (row_it < row_end)\n                {\n                    col_id = A_cols[row_it];\n                }\n\n                if (row_it < row_end)// && col_id < A_num_rows)\n                {\n                    int col_color = A_colors[col_id];\n\n                    if (col_color == 0)\n                    {\n                        used_color_structure_64_bit used_colors_col = used_colors_inout[col_id];\n                        used_colors_col.use_color(new_color, aux);\n                        used_colors_inout[col_id] = used_colors_col;\n                    }\n\n                    if (COLORING_LEVEL == 2)\n                    {\n                        int row_begin = A_rows[col_id  ];\n                        int row_end   = A_rows[col_id + 1];\n\n                        for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n                        {\n                            int row_it = row_begin + lane_id;\n                            int col_id = -1;\n\n                            if (row_it < row_end)\n                            {\n                                col_id = A_cols[row_it];\n                            }\n\n                            if (row_it < row_end)// && col_id < A_num_rows)\n                            {\n                                int col_color = A_colors[col_id];\n\n                                if (col_color == 0)\n                                {\n                                    used_color_structure_64_bit used_colors_col = used_colors_inout[col_id];\n                                    used_colors_col.use_color(new_color, aux);\n                                    used_colors_inout[col_id] = used_colors_col;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    using namespace amgx::strided_reduction;\n    amgx::strided_reduction::op_max op;\n    warp_reduce<1, CTA_SIZE, 32>(thread_max, op);\n    block_reduce<1, CTA_SIZE, 32, true>(thread_max, maxs_per_block, op);\n#endif\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nGreedy_Recolor_MatrixColoring_Base<T_Config>::Greedy_Recolor_MatrixColoring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    first_pass_config = cfg;\n    first_pass_config_scope = cfg_scope;\n    //MULTI_HASH: it would be faster but it leaves some uncolored vertices: uncolored vertices cannot be processed in parallel\n    first_pass_config.setParameter(\"matrix_coloring_scheme\", std::string(\"MULTI_HASH\"), first_pass_config_scope);\n    first_pass_config.setParameter(\"max_uncolored_percentage\", 0.0, first_pass_config_scope);\n    m_coloring_custom_arg = cfg.AMG_Config::template getParameter<std::string>( \"coloring_custom_arg\", cfg_scope );\n    m_coloring_try_remove_last_color_ = cfg.AMG_Config::template getParameter<int>( \"coloring_try_remove_last_colors\", cfg_scope );\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nGreedy_Recolor_MatrixColoring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    const int num_rows = A.get_num_rows();\n    const int num_nz   = A.get_num_nz();\n    int avg_nz   = num_nz / num_rows;\n    //printf(\"AVG %d \",avg_nz);\n    //P(single) = 1-((avg-1)/avg)^(K)\n    //P(all)    = P(single)^(N)\n    //          = (1-((avg-1)/avg)^(K))^(N) == 0.99\n    //          = (1-((avg-1)/avg)^(K)) == Pow(0.99,1/N)\n    //P(single == 0.99) K = Log(1-0.99)/Log(1/avg)\n    //P(all    == 0.99) K = Log(1-Pow(0.99,1/N))/Log(1/avg)\n    //99% probability\n    //table here http://www.wolframalpha.com/input/?i=Log%281-0.99%29%2FLog%28%28a-1%29%2Fa%29+for+a+in+[2%2C10]\n    avg_nz = pow(static_cast<double>(avg_nz), static_cast<double>(this->m_coloring_level));\n\n    if (avg_nz > 16) { avg_nz = 16; }\n\n    if (this->m_coloring_custom_arg != \"\")\n    {\n        int x = atoi(this->m_coloring_custom_arg.c_str());\n\n        if (x > 0)\n        {\n            avg_nz = x;\n        }\n    }\n\n#define CASENZ(X,ORDER,DO_SORT)\\\n else if(avg_nz <= X)\\\n {\\\n     color_matrix_specialized_per_numhash<X,ORDER,DO_SORT>(A);\\\n }\n#define CASEORDER(ORDER,DO_SORT)\\\n if(0)\\\n {\\\n\\\n }\\\n CASENZ(2,ORDER,DO_SORT)\\\n CASENZ(4,ORDER,DO_SORT)\\\n CASENZ(8,ORDER,DO_SORT)\\\n CASENZ(12,ORDER,DO_SORT)\\\n CASENZ(16,ORDER,DO_SORT)\\\n CASENZ(20,ORDER,DO_SORT)\\\n CASENZ(24,ORDER,DO_SORT)\\\n CASENZ(32,ORDER,DO_SORT)\\\n CASENZ(48,ORDER,DO_SORT)\\\n else\\\n {\\\n     color_matrix_specialized_per_numhash<16,ORDER,DO_SORT>(A);\\\n }\n\n    if (0)\n    {\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        CASEORDER(1, 0);\n    }\n    else if (this->m_coloring_level == 2)\n    {\n        CASEORDER(2, 1);\n    }\n    else if (this->m_coloring_level == 3)\n    {\n        CASEORDER(3, 1);\n    }\n    else if (this->m_coloring_level == 4)\n    {\n        CASEORDER(4, 1);\n    }\n    else if (this->m_coloring_level == 5)\n    {\n        CASEORDER(5, 1);\n    }\n}\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate<int K1, int COLORING_LEVEL, int DO_SORT>\nvoid\nGreedy_Recolor_MatrixColoring<TemplateConfig<AMGX_device, V, M, I> >::color_matrix_specialized_per_numhash( Matrix_d &A )\n{\n    const int num_rows = A.get_num_rows();\n    const int n_blocks = 26 * 4; //TODO: choose optimal blocks number\n    const int block_size = 256;\n    ViewType oldView = A.currentView();\n#if 1 //use fast multihash above as precolorer\n    this->m_row_colors.resize( A.row_offsets.size() - 1, 0 );\n    typedef typename Matrix<TemplateConfig<AMGX_host, V, M, I> > ::IVector IVector_h;\n    IVector sorted_rows_by_color;\n    IVector_h offsets_rows_per_color;\n    {\n        device_vector_alloc<int> sets_per_block(n_blocks + 1);\n        int *sets_per_block_ptr = amgx::thrust::raw_pointer_cast(sets_per_block.data());\n\n        if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n        else { A.setViewExterior(); }\n\n        int num_uncolored = num_rows;\n        int i = 0;\n#if DISCARD_COLORED\n        device_vector_alloc<int> color_in(num_rows);\n#else\n        device_vector_alloc<int> color_in;\n#endif\n#if USE_GTLT\n        device_vector_alloc<unsigned long long int> gtlt(num_rows);\n        const int K = K1 * 0.5;\n#else\n        const int K = K1;\n#endif\n        int prev_num_uncolored = num_uncolored;\n        unsigned int seed = 0;\n\n        while (num_uncolored > 0)\n        {\n            //int mx  = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n            //if(mx > 2*K*i) printf(\"ERR UX %d %d %d\\n\", num_uncolored, mx, 2*K*i);\n#if USE_GTLT\n            fast_multihash_kernel_gtlt_kernel<K, block_size, 1> <<< n_blocks, block_size>>>\n            (num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n             this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(gtlt.data()),\n             seed);\n            cudaCheckError();\n            fast_multihash_kernel_gtlt_assign_kernel<K, block_size, 1> <<< n_blocks, block_size>>>\n            (num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n             this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(gtlt.data()),\n             2 * K * i);\n            cudaCheckError();\n#else\n\n            if (num_uncolored == num_rows || (!DISCARD_COLORED))\n            {\n                fast_multihash_kernel<false, K, COLORING_LEVEL, block_size, 1> <<< n_blocks, block_size>>>(num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n                        this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(color_in.data()),\n                        2 * K * i, seed,\n                        sets_per_block_ptr);\n                        cudaCheckError();\n            }\n            else\n            {\n                fast_multihash_kernel<true, K, COLORING_LEVEL, block_size, 1> <<< n_blocks, block_size>>>(num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n                        this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(color_in.data()),\n                        2 * K * i, seed,\n                        sets_per_block_ptr);\n            }\n\n            cudaCheckError();\n#endif\n            seed = hash2(seed, 0);\n#if GET_COLORS == GET_COLORS_THRUST || USE_GTLT\n            num_uncolored = amgx::thrust::count( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0);\n            cudaCheckError();\n#elif GET_COLORS == GET_COLORS_TWOPASS\n            num_uncolored -= amgx::strided_reduction::count_block_results_1(n_blocks, sets_per_block_ptr, amgx::strided_reduction::op_sum());\n#elif (GET_COLORS == GET_COLORS_ATOMIC) || (GET_COLORS == GET_COLORS_GATHER)\n            num_uncolored -= sets_per_block[0];\n#endif\n            //printf(\"%d -> %d\\n\", prev_num_uncolored,num_uncolored,prev_num_uncolored-num_uncolored);\n            i++;\n\n            if (prev_num_uncolored == num_uncolored)\n            {\n                //printf(\"ERR %d %d %d\\n\", prev_num_uncolored,num_uncolored,num_rows);\n                break;\n            }\n\n            prev_num_uncolored = num_uncolored;\n#if DISCARD_COLORED\n            color_in.swap(this->m_row_colors);\n#endif\n            /*if (iteration++%4==0) {\n                cudaEventRecord(throttle_event);\n                cudaCheckError();\n            } else {\n                cudaEventSynchronize(throttle_event);\n                cudaCheckError();\n            }*/\n        }\n\n#if DISCARD_COLORED\n        color_in.swap(this->m_row_colors);\n#endif\n        int max_color = i * K * 2; //available_color+c0 = 2*K*i + 2*(K-1)+2=2K(i+1), but i++\n        //max_color = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n        //printf(\"CC %d<%d\\n\",max_color,i*K*2+2);\n        this->m_num_colors = max_color + 1;\n#if ONLY_FIRSTSTEP\n\n        if (1)\n#else\n        if (this->m_coloring_custom_arg == \"FIRSTSTEP\")\n#endif\n        {\n            A.setView(oldView);\n            return;\n        }\n\n        if (DO_SORT)\n        {\n            //return;\n            //this->createColorArrays(A)\n            IVector row_colors(this->m_row_colors); //useless copy\n            sorted_rows_by_color.resize(num_rows);\n            //this->m_sorted_rows_by_color.resize(num_rows);\n            thrust_wrapper::sequence<AMGX_device>(sorted_rows_by_color.begin(), sorted_rows_by_color.end()); //useless sequence\n            thrust_wrapper::sort_by_key<AMGX_device>(row_colors.begin(), row_colors.begin() + num_rows, sorted_rows_by_color.begin()); //useless read from sequence\n            cudaCheckError();\n            IVector offsets_rows_per_color_d(this->m_num_colors + 1);\n            amgx::thrust::lower_bound(row_colors.begin(),\n                                row_colors.begin() + num_rows,\n                                amgx::thrust::counting_iterator<IndexType>(0),\n                                amgx::thrust::counting_iterator<IndexType>(offsets_rows_per_color_d.size()),\n                                offsets_rows_per_color_d.begin());\n            // Copy from device to host\n            offsets_rows_per_color = offsets_rows_per_color_d;\n            cudaCheckError();\n            /*for(int i=0;i<offsets_rows_per_color_d.size(); i++)\n            {\n                int x= offsets_rows_per_color[i];\n            }*/\n            /*oldView = A.currentView();\n            if  (this->m_halo_coloring == SYNC_COLORS) A.setView(ALL);\n            else A.setViewExterior();*/\n        }\n    }\n#else\n    //1st pass: color with a fast method\n    MatrixColoring<typename Matrix_d::TConfig> *first_pass_matrix_coloring = MatrixColoringFactory<typename Matrix_d::TConfig>::allocate(this->first_pass_config, this->first_pass_config_scope);\n    first_pass_matrix_coloring->colorMatrix(A);\n#if DO_SORT\n    first_pass_matrix_coloring->createColorArrays(A);\n#endif\n    this->m_row_colors = first_pass_matrix_coloring->getRowColors();\n    this->m_num_colors = first_pass_matrix_coloring->getNumColors();\n    sorted_rows_by_color = first_pass_matrix_coloring->getSortedRowsByColor();\n    offsets_rows_per_color = first_pass_matrix_coloring->getOffsetsRowsPerColor();\n    delete first_pass_matrix_coloring;\n    //ViewType oldView = A.currentView();\n    //if  (this->m_halo_coloring == SYNC_COLORS) A.setView(ALL);\n    //else A.setViewExterior();\n#endif\n    cudaCheckError();\n    //2nd pass: optimize using parallel greedy for each color.\n    //If 1st pass coloring is valid all elements of same color can be processed in parallel by definition\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    device_vector_alloc<int> max_color_per_block(n_blocks + 1, 1);\n    int *max_color_per_block_ptr = amgx::thrust::raw_pointer_cast(max_color_per_block.data());\n#else\n    int *max_color_per_block_ptr = 0;\n#endif\n    float avg_nnz_per_row = A.get_num_nz() / float(A.get_num_rows());\n    int WARP_SIZE = 2;\n\n    if ( avg_nnz_per_row < 4.0f )\n    {\n        WARP_SIZE = 2;\n    }\n    else if ( avg_nnz_per_row < 8.0f )\n    {\n        WARP_SIZE = 4;\n    }\n    else if ( avg_nnz_per_row < 32.0f )\n    {\n        WARP_SIZE = 8;\n    }\n    else\n    {\n        WARP_SIZE = 32;\n    }\n\n    if (this->m_coloring_level >= 2)\n    {\n        WARP_SIZE = 1;\n    }\n\n    //;\n    //int max_colorx = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n    //printf(\"MAX %d\",max_colorx);\n#define RECOLOR_CALL_CASE(__WSIZE) if(WARP_SIZE==__WSIZE){ \\\n  recolor_greedy_kernel<DO_SORT,COLORING_LEVEL,block_size,__WSIZE><<<GRID_SIZE, block_size>>>(\\\n    num_rows,\\\n    A.row_offsets.raw(),\\\n    A.col_indices.raw(),\\\n    color, this->m_row_colors.raw(),\\\n    sorted_rows_by_color.raw(),start,end,\\\n    max_color_per_block_ptr\\\n    ); cudaCheckError(); }\n    cudaCheckError();\n#if 1 //straight order: generates a color histogram similar to parallel greedy\n    //start from color=2: 1st color would be assigned to 1, no change\n\n    //iteration = 0;\n\n    for (int color = 2; color <= this->m_num_colors; color++)\n    {\n        const int GRID_SIZE = n_blocks;//\n        int start, end;\n\n        if (DO_SORT)\n        {\n            start = offsets_rows_per_color[color];\n            end  = num_rows;\n\n            if (color < this->m_num_colors) { end = offsets_rows_per_color[color + 1]; }\n\n            if (end >= num_rows) { end = num_rows; }\n\n            //printf(\"F %d %d %d < num_rows: %d\\n\",color,start,end,num_rows,(int)end<=start);\n            if (end <= start) { continue; }\n\n            //int NUM_WARPS_PER_CTA = block_size/WARP_SIZE;\n            //const int GRID_SIZE = std::min( 2048, ((end-start) + NUM_WARPS_PER_CTA-1) / NUM_WARPS_PER_CTA );\n            //printf(\"%d - %d > %d %d %d\\n\",end-start,color,start,end,num_rows);\n        }\n        else\n        {\n            start = 0;\n            end = num_rows;\n        }\n\n        //printf(\"%d : %d %d\\n\", color, start, end);\n        RECOLOR_CALL_CASE(1);\n        RECOLOR_CALL_CASE(2);\n        RECOLOR_CALL_CASE(4);\n        RECOLOR_CALL_CASE(8);\n        RECOLOR_CALL_CASE(16);\n        RECOLOR_CALL_CASE(32);\n        cudaCheckError();\n        /*if (iteration++%4==0) {\n            cudaEventRecord(throttle_event);\n            cudaCheckError();\n        } else {\n            cudaEventSynchronize(throttle_event);\n            cudaCheckError();\n        }*/\n    }\n\n#else //reverse order: generates a different kind of histogram\n\n    for (int color = this->m_num_colors; color >= 0; color--)\n    {\n        RECOLOR_CALL_CASE(1);\n        RECOLOR_CALL_CASE(2);\n        RECOLOR_CALL_CASE(4);\n        RECOLOR_CALL_CASE(8);\n        RECOLOR_CALL_CASE(16);\n        RECOLOR_CALL_CASE(32);\n    }\n\n#endif\n    cudaCheckError();\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    int max_color = amgx::strided_reduction::count_block_results_1(n_blocks, max_color_per_block_ptr, amgx::strided_reduction::op_max());\n    /*int max_colorg = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n\n    max_color=0;\n    for(int i=0;i<n_blocks;i++)\n    {\n      if(max_color_per_block[i] > max_color) max_color = max_color_per_block[i];\n    }\n    printf(\"MAXCOLOR ref=%d %d\\n\",max_colorg,max_color);\n    if(max_colorg!=max_color)exit(1);*/\n#else\n    int max_color = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() );\n#endif\n    //printf(\"MAXCOLOR %d %d\\n\",max_color_gold,max_color);\n    cudaCheckError();\n    this->m_num_colors = max_color + 1;\n    A.setView(oldView);\n}\n\n\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Recolor_MatrixColoring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Recolor_MatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/greedy_recolor.h>\n#include <strided_reduction.h>\n#include <math.h> //std::pow\n#include <matrix_coloring/coloring_utils.h>\n#include <map>\n#include <thrust/sort.h>\n#include <thrust/count.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/binary_search.h>\n#include <thrust/replace.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n\n#define GET_COLORS_THRUST 0\n#define GET_COLORS_TWOPASS 1\n#define GET_COLORS_ATOMIC 2\n#define GET_COLORS_GATHER 3\n#define GETMAX_THRUST  0\n#define GETMAX_TWOPASS 1\n\n//CONFIG\n\n#define GET_COLORS GET_COLORS_TWOPASS\n//#define GET_COLORS GET_COLORS_THRUST\n\n#define GETMAX_COLOR GETMAX_TWOPASS\n//#define GETMAX_COLOR GETMAX_THRUST\n\n//#define DO_SORT 1\n#define USE_GTLT 0\n#define DISCARD_COLORED (1 && !USE_GTLT)\n#define ONLY_FIRSTSTEP 0\n\nnamespace amgx\n{\n//normal hash function\n__host__ __device__ __forceinline__ unsigned int hash2(unsigned int a, const unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n//rehashes a given hash (h), rotating its bits.\n__host__ __device__ __forceinline__ unsigned int rehash(const unsigned int &a, const unsigned int h, const  unsigned int k, const unsigned int seed)\n{\n    return (h << k) ^ (h >> k);\n}\n\n//compute hashes for a column, compare with the row's hashes and the updated bitfield is_max/is_min for the row.\ntemplate<int HASHES>\n__device__ __forceinline__ void compute_hashes(const int &row_id, const int &col_id, const int &base_row_hash, const int &base_col_hash, const int &seed, unsigned long long int &is_min, unsigned long long int &is_max)\n{\n    int col_hash_k = base_col_hash;\n    int row_hash_k = base_row_hash;\n#pragma unroll\n\n    for (int k = 0; k < HASHES; k++)\n    {\n        col_hash_k = rehash(col_id, base_col_hash, k, seed);\n        row_hash_k = rehash(row_id, base_row_hash, k, seed);\n        unsigned long long q = (~(1ull << (63 - k))); //bit of hash k in bitfields\n\n        if (col_hash_k > row_hash_k || (col_hash_k == row_hash_k && row_id < col_id))\n        {\n            is_max &= q;\n        }\n\n        //unsigned long long x2 = col_hash_k < row_hash_k;\n        if (col_hash_k < row_hash_k || (col_hash_k == row_hash_k && row_id > col_id))\n        {\n            is_min &= q;\n        }\n    }\n}\n\ntemplate<int COLORING_LEVEL>\nstruct neigbor_visitor\n{\n\n    template<int WARP_SIZE, int HASHES, bool DISCARD_COLORED_T>\n    static __device__ __forceinline__ void visit(const int &row_id, const int &base_row_hash, const int &explore_row, const int *A_rows, const int *A_cols, const int *A_colors_in, const int lane_id, const int seed, unsigned long long int &is_min, unsigned long long int &is_max)\n    {\n        int row_begin = A_rows[explore_row  ];\n        int row_end   = A_rows[explore_row + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n        {\n            int row_it = row_begin + lane_id;\n            int col_id = -1;\n            bool do_compute_hashes = true;\n            col_id = A_cols[row_it];//__load_global(A_cols+row_it);\n#if DISCARD_COLORED\n\n            if (DISCARD_COLORED_T)\n            {\n                int col_color = __ldg(A_colors_in + col_id);\n\n                if (col_color != 0) { do_compute_hashes = false; }\n            }\n\n#endif\n\n            if (do_compute_hashes)\n            {\n                //#pragma unroll\n                //for(int k=0; k<(HASHES/16>0)?HASHES/16:1; k++)\n                //{\n                int base_col_hash = hash2(col_id, seed/*+k*12358*/);\n                compute_hashes<HASHES>(row_id, col_id, base_row_hash, base_col_hash, seed, is_min, is_max);\n                //}\n            }\n\n            neigbor_visitor < COLORING_LEVEL - 1 >::template visit<WARP_SIZE, HASHES, DISCARD_COLORED_T>(row_id, base_row_hash, col_id, A_rows, A_cols, A_colors_in, lane_id, seed, is_min, is_max);\n        }\n    }\n\n};\ntemplate<>\nstruct neigbor_visitor<0>\n{\n\n    template<int WARP_SIZE, int HASHES, bool DISCARD_COLORED_T>\n    static __device__ __forceinline__ void visit(const int &row_id, const int &base_row_hash, const int &explore_row, const int *A_rows, const int *A_cols, const int *A_colors_in, const int lane_id, const int seed, unsigned long long int &is_min, unsigned long long int &is_max)\n    {\n        //is_min |= 0ull;\n    }\n\n};\n\n\ntemplate< bool DISCARD_COLORED_T, int HASHES, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, const int *A_colors_in,\n    int c0, int seed,\n    int *sets_per_block)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int warp_count = 0;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int available_color = 0;\n\n        if (row_id < A_num_rows)\n        {\n#if DISCARD_COLORED\n\n            if (DISCARD_COLORED_T)\n            {\n                row_color = A_colors_in[row_id];\n                A_colors[row_id] = row_color;\n            }\n            else\n#endif\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        if (row_color == 0)\n        {\n            unsigned long long int is_min = ~0ull;\n            unsigned long long int is_max = ~0ull;\n            int base_row_hash = hash2(row_id, seed);\n#if 1 //COLORING_LEVELR>1\n            neigbor_visitor<COLORING_LEVEL>::template visit<WARP_SIZE, HASHES, DISCARD_COLORED_T>(row_id, base_row_hash, row_id, A_rows, A_cols, A_colors_in, lane_id, seed, is_min, is_max);\n#else\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                col_id = A_cols[row_it];//__load_global(A_cols+row_it);\n#if DISCARD_COLORED\n\n                if (DISCARD_COLORED_T)\n                {\n                    int col_color = __ldg(A_colors_in + col_id);\n\n                    if (col_color != 0) { continue; }\n                }\n\n#endif\n                int base_col_hash = hash2(col_id, seed);\n                compute_hashes<HASHES>(row_id, col_id, base_row_hash, base_col_hash, seed, is_min, is_max);\n            }\n\n#endif\n\n            if (WARP_SIZE > 1)\n            {\n                using namespace amgx::strided_reduction;\n                amgx::strided_reduction::op_and op;\n                warp_reduce<1, CTA_SIZE, WARP_SIZE>(is_max, op);\n                warp_reduce<1, CTA_SIZE, WARP_SIZE>(is_min, op);\n            }\n\n#if 1\n            int cmax = 63 - utils::bfind(is_max);\n            int cmin = 63 - utils::bfind(is_min);\n\n            if (cmin < HASHES)\n            {\n                available_color = cmin * 2 + 2;\n            }\n\n            if (cmax < HASHES)\n            {\n                available_color = cmax * 2 + 1;\n            }\n\n#else\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                if (is_max & (1ull << 63 - k))\n                {\n                    available_color = 2 * k + 1;\n                    break;\n                }\n                else if (is_min & (1ull << 63 - k))\n                {\n                    available_color = 2 * k + 2;\n                    break;\n                }\n            }\n\n#endif\n        }\n\n        bool has_set_color = (lane_id == 0) && (available_color != 0);\n\n        if (has_set_color)\n        {\n            A_colors[row_id] = available_color + c0;\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_color);\n    }\n\n#if GET_COLORS == GET_COLORS_ATOMIC\n    amgx::strided_reduction::block_count_atomic<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n    //__threadfence_system();\n#elif GET_COLORS == GET_COLORS_TWOPASS\n    amgx::strided_reduction::block_count<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n#elif GET_COLORS == GET_COLORS_GATHER\n    amgx::strided_reduction::block_count<1, CTA_SIZE, 32, int>(warp_count, sets_per_block);\n    amgx::strided_reduction::block_count_gatherer<int, 1> global_gather(sets_per_block);\n#endif\n}\n\n////////////////////////////\n\ntemplate< int HASHES, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel_gtlt_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, unsigned long long int *gtlt_out,\n    int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    //int warp_count = 0;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n\n        if (row_id < A_num_rows)\n        {\n            row_color = A_colors[row_id];\n        }\n\n        if (row_color == 0)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n            int base_row_hash = hash2(row_id, seed);\n            unsigned char count_gt[HASHES];\n            unsigned char count_lt[HASHES];\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                count_gt[k] = 0;\n                count_lt[k] = 0;\n            }\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                col_id = A_cols[row_it];\n                int col_color = A_colors[col_id];\n\n                if (col_color != 0) { continue; }\n\n                int base_col_hash = hash2(col_id, seed);\n                int col_hash_k = base_col_hash;\n                int row_hash_k = base_row_hash;\n#pragma unroll\n\n                for (int k = 0; k < HASHES; k++)\n                {\n                    col_hash_k = rehash(col_id, base_col_hash, k, seed);\n                    row_hash_k = rehash(row_id, base_row_hash, k, seed);\n\n                    if (col_hash_k > row_hash_k || (col_hash_k == row_hash_k && row_id < col_id))\n                    {\n                        if (count_gt[k] < 255) //avoid overflow\n                        {\n                            ++count_gt[k];\n                        }\n                    }\n\n                    if (col_hash_k < row_hash_k || (col_hash_k == row_hash_k && row_id > col_id))\n                    {\n                        if (count_lt[k] < 255)\n                        {\n                            ++count_lt[k];\n                        }\n                    }\n                }\n            }\n\n            //compress count_gt,lt in 2 bits and pack in an integer\n            unsigned long long int packed_gtlt = 0;\n#pragma unroll\n\n            for (int k = 0; k < HASHES; k++)\n            {\n                unsigned long long int max3_gt = count_gt[k];\n                unsigned long long int max3_lt = count_lt[k];\n\n                if (max3_gt > 3) { max3_gt = 3; } //TODO use only bits above\n\n                if (max3_lt > 3) { max3_lt = 3; }\n\n                //TODO: use BFI\n                packed_gtlt |= (max3_gt << (k * 2));\n                packed_gtlt |= (max3_lt << (k * 2 + 32));\n            }\n\n            if (lane_id == 0)\n            {\n                gtlt_out[row_id] = packed_gtlt;\n            }\n        }\n    }\n}\n\ntemplate< int HASHES, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid fast_multihash_kernel_gtlt_assign_kernel(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int *A_colors, unsigned long long int *gtlt_in,\n    int c0)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    for ( ; utils::any(row_id < A_num_rows) ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        unsigned long long int row_gtlt = 0ull;\n\n        if (row_id < A_num_rows)\n        {\n            row_color = A_colors[row_id];\n            row_gtlt = gtlt_in[row_id];\n        }\n\n        int available_color = 0;\n\n        if (row_color == 0)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n            unsigned long long int is_min = ~0ull;\n            unsigned long long int is_max = ~0ull;\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n                unsigned long long int col_gtlt = 0ull;\n\n                if (row_it < row_end)\n                {\n                    col_id   = A_cols[row_it];\n                    col_gtlt = gtlt_in[col_id];\n#pragma unroll\n\n                    for (int k = 0; k < HASHES; k++)\n                    {\n                        int row_gt_k = (row_gtlt >> (k * 2 + 0)) & 3ull;\n                        int row_lt_k = (row_gtlt >> (k * 2 + 32)) & 3ull;\n                        int col_gt_k = (col_gtlt >> (k * 2 + 0)) & 3ull;\n                        int col_lt_k = (col_gtlt >> (k * 2 + 32)) & 3ull;\n                        unsigned long long q = (~(1ull << (63 - k)));\n\n                        if ((row_gt_k > col_gt_k) || (col_gt_k == row_gt_k && row_id < col_id))\n                        {\n                            is_max &= q; //xor with k bit: if was 1, becomes 0, if was 0 stays 0\n                        }\n\n                        if ((row_lt_k > col_lt_k) || (col_lt_k == row_lt_k && row_id > col_id))\n                        {\n                            is_min &= q;\n                        }\n                    }\n                }\n            }\n\n            int cmax = 63 - utils::bfind(is_max);\n            int cmin = 63 - utils::bfind(is_min);\n\n            //if(cmin < HASHES)\n            //  available_color = cmin*2+2;\n            if (cmax < HASHES)\n            {\n                available_color = cmax * 2 + 1;\n            }\n        }\n\n        bool has_set_color = (lane_id == 0) && (available_color != 0);\n\n        if (has_set_color)\n        {\n            A_colors[row_id] = available_color + c0;\n        }\n    }\n}\n\n////////////////////////////\n\ntemplate< bool SORTED_ROWS, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid recolor_greedy_kernel(\n    const int A_num_rows, const int *__restrict A_rows,\n    const int *__restrict A_cols,\n    int color_to_reassign,\n    int *__restrict A_colors,\n    int *sorted_rows_by_color, int offset_start, int offset_end\n    , int *maxs_per_block\n)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int job_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int last_job  = A_num_rows;\n\n    if (SORTED_ROWS)\n    {\n        job_id = job_id + offset_start;\n        last_job  = offset_end;\n    }\n\n    int thread_max = 0;\n\n    //const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    //int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; utils::any(job_id < last_job) ; job_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int row_id = -1;\n\n        if (job_id < last_job)\n        {\n            if (SORTED_ROWS)\n            {\n                row_id    = sorted_rows_by_color[job_id];\n            }\n            else\n            {\n                row_id = job_id;\n            }\n\n            if (row_id < A_num_rows)\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        used_color_structure_64_bit used_colors;\n        used_color_structure_64_bit::aux_tmp aux;\n        //used_color_structure_64_bit_colorbox<5> used_colors; //still buggy\n\n        if (row_color == color_to_reassign)\n        {\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            //used_colors.use_color(color_to_reassign);\n            //int num_greater = 0;\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n\n                if (row_it < row_end)\n                {\n                    col_id = A_cols[row_it];\n                }\n\n                if (row_it < row_end)// && col_id < A_num_rows)\n                {\n                    int col_color = A_colors[col_id];\n\n                    //if(col_color>40) printf(\"%d\\n\",col_color);\n                    if (col_color != 0)\n                    {\n                        used_colors.use_color(col_color, aux);\n                    }\n                }\n\n                if (COLORING_LEVEL == 2)\n                {\n                    int row_begin = A_rows[col_id  ];\n                    int row_end   = A_rows[col_id + 1];\n\n                    for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n                    {\n                        int row_it = row_begin + lane_id;\n                        int col_id = -1;\n\n                        if (row_it < row_end)\n                        {\n                            col_id = A_cols[row_it];\n                        }\n\n                        if (row_it < row_end)// && col_id < A_num_rows)\n                        {\n                            int col_color = A_colors[col_id];\n\n                            //if(col_color>40) printf(\"%d\\n\",col_color);\n                            if (col_color != 0)\n                            {\n                                used_colors.use_color(col_color, aux);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        used_colors.sync_subwarp<CTA_SIZE, WARP_SIZE>(lane_id);\n\n        /*if(row_color != color_to_reassign && row_id != -1)\n        {\n          printf(\"ERR %d %d!\\n\", row_color, color_to_reassign);\n        }*/\n        if (row_color == color_to_reassign && lane_id == 0)\n        {\n            int new_color = used_colors.first_available_color(aux);\n\n            if (new_color != row_color && new_color > 0)\n            {\n                //printf(\"changing %d from %d to %d\\n\",row_id,row_color,new_color);\n                A_colors[row_id] = new_color;\n            }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n\n            if (new_color > thread_max)\n            {\n                thread_max = new_color;\n            }\n\n#endif\n            /*if(new_color >= 7)\n            {\n                printf(\"SEVEN %d %d\\n\", new_color, thread_max);\n            }*/\n        }\n    }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    using namespace amgx::strided_reduction;\n    amgx::strided_reduction::op_max op;\n    warp_reduce<1, CTA_SIZE, 32>(thread_max, op);\n    block_reduce<1, CTA_SIZE, 32, true>(thread_max, maxs_per_block, op);\n#endif\n}\n\n////////////////////////////\n\n\n\ntemplate< bool SORTED_ROWS, int COLORING_LEVEL, int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid recolor_greedy_kernel_anyring(\n    const int A_num_rows, const int *A_rows, const int *A_cols,\n    int color_to_reassign, int *A_colors,\n    int *sorted_rows_by_color, int offset_start, int offset_end, int *maxs_per_block,\n\n    used_color_structure_64_bit *used_colors_inout\n)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    const int warp_id = subwarp<WARP_SIZE>::subwarp_id();\n    const int lane_id = subwarp<WARP_SIZE>::sublane_id();\n    int job_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n    int last_job  = A_num_rows;\n\n    if (SORTED_ROWS)\n    {\n        job_id = job_id + offset_start;\n        last_job  = offset_end;\n    }\n\n    int thread_max = 0;\n\n    //const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    //int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; utils::any(job_id < last_job) ; job_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = -1;\n        int row_id = -1;\n\n        if (job_id < last_job)\n        {\n            if (SORTED_ROWS)\n            {\n                row_id    = sorted_rows_by_color[job_id];\n            }\n            else\n            {\n                row_id = job_id;\n            }\n\n            if (row_id < A_num_rows)\n            {\n                row_color = A_colors[row_id];\n            }\n        }\n\n        used_color_structure_64_bit used_colors;\n        used_color_structure_64_bit::aux_tmp aux;\n        used_colors = used_colors_inout[row_id];\n        //used_color_structure_64_bit_colorbox<5> used_colors; //still buggy\n\n        if (row_color == color_to_reassign)\n        {\n            int new_color = used_colors.first_available_color(aux);\n\n            if (lane_id == 0)\n            {\n                if (new_color != row_color && new_color > 0)\n                {\n                    //printf(\"changing %d from %d to %d\\n\",row_id,row_color,new_color);\n                    A_colors[row_id] = new_color;\n                }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n\n                if (new_color > thread_max)\n                {\n                    thread_max = new_color;\n                }\n\n#endif\n            }\n\n            int row_begin = A_rows[row_id  ];\n            int row_end   = A_rows[row_id + 1];\n\n            for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n            {\n                int row_it = row_begin + lane_id;\n                int col_id = -1;\n\n                if (row_it < row_end)\n                {\n                    col_id = A_cols[row_it];\n                }\n\n                if (row_it < row_end)// && col_id < A_num_rows)\n                {\n                    int col_color = A_colors[col_id];\n\n                    if (col_color == 0)\n                    {\n                        used_color_structure_64_bit used_colors_col = used_colors_inout[col_id];\n                        used_colors_col.use_color(new_color, aux);\n                        used_colors_inout[col_id] = used_colors_col;\n                    }\n\n                    if (COLORING_LEVEL == 2)\n                    {\n                        int row_begin = A_rows[col_id  ];\n                        int row_end   = A_rows[col_id + 1];\n\n                        for ( ; row_begin < row_end ; row_begin += WARP_SIZE)\n                        {\n                            int row_it = row_begin + lane_id;\n                            int col_id = -1;\n\n                            if (row_it < row_end)\n                            {\n                                col_id = A_cols[row_it];\n                            }\n\n                            if (row_it < row_end)// && col_id < A_num_rows)\n                            {\n                                int col_color = A_colors[col_id];\n\n                                if (col_color == 0)\n                                {\n                                    used_color_structure_64_bit used_colors_col = used_colors_inout[col_id];\n                                    used_colors_col.use_color(new_color, aux);\n                                    used_colors_inout[col_id] = used_colors_col;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    using namespace amgx::strided_reduction;\n    amgx::strided_reduction::op_max op;\n    warp_reduce<1, CTA_SIZE, 32>(thread_max, op);\n    block_reduce<1, CTA_SIZE, 32, true>(thread_max, maxs_per_block, op);\n#endif\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nGreedy_Recolor_MatrixColoring_Base<T_Config>::Greedy_Recolor_MatrixColoring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    first_pass_config = cfg;\n    first_pass_config_scope = cfg_scope;\n    //MULTI_HASH: it would be faster but it leaves some uncolored vertices: uncolored vertices cannot be processed in parallel\n    first_pass_config.setParameter(\"matrix_coloring_scheme\", std::string(\"MULTI_HASH\"), first_pass_config_scope);\n    first_pass_config.setParameter(\"max_uncolored_percentage\", 0.0, first_pass_config_scope);\n    m_coloring_custom_arg = cfg.AMG_Config::template getParameter<std::string>( \"coloring_custom_arg\", cfg_scope );\n    m_coloring_try_remove_last_color_ = cfg.AMG_Config::template getParameter<int>( \"coloring_try_remove_last_colors\", cfg_scope );\n}\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nGreedy_Recolor_MatrixColoring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    const int num_rows = A.get_num_rows();\n    const int num_nz   = A.get_num_nz();\n    int avg_nz   = num_nz / num_rows;\n    //printf(\"AVG %d \",avg_nz);\n    //P(single) = 1-((avg-1)/avg)^(K)\n    //P(all)    = P(single)^(N)\n    //          = (1-((avg-1)/avg)^(K))^(N) == 0.99\n    //          = (1-((avg-1)/avg)^(K)) == Pow(0.99,1/N)\n    //P(single == 0.99) K = Log(1-0.99)/Log(1/avg)\n    //P(all    == 0.99) K = Log(1-Pow(0.99,1/N))/Log(1/avg)\n    //99% probability\n    //table here http://www.wolframalpha.com/input/?i=Log%281-0.99%29%2FLog%28%28a-1%29%2Fa%29+for+a+in+[2%2C10]\n    avg_nz = pow(static_cast<double>(avg_nz), static_cast<double>(this->m_coloring_level));\n\n    if (avg_nz > 16) { avg_nz = 16; }\n\n    if (this->m_coloring_custom_arg != \"\")\n    {\n        int x = atoi(this->m_coloring_custom_arg.c_str());\n\n        if (x > 0)\n        {\n            avg_nz = x;\n        }\n    }\n\n#define CASENZ(X,ORDER,DO_SORT)\\\n else if(avg_nz <= X)\\\n {\\\n     color_matrix_specialized_per_numhash<X,ORDER,DO_SORT>(A);\\\n }\n#define CASEORDER(ORDER,DO_SORT)\\\n if(0)\\\n {\\\n\\\n }\\\n CASENZ(2,ORDER,DO_SORT)\\\n CASENZ(4,ORDER,DO_SORT)\\\n CASENZ(8,ORDER,DO_SORT)\\\n CASENZ(12,ORDER,DO_SORT)\\\n CASENZ(16,ORDER,DO_SORT)\\\n CASENZ(20,ORDER,DO_SORT)\\\n CASENZ(24,ORDER,DO_SORT)\\\n CASENZ(32,ORDER,DO_SORT)\\\n CASENZ(48,ORDER,DO_SORT)\\\n else\\\n {\\\n     color_matrix_specialized_per_numhash<16,ORDER,DO_SORT>(A);\\\n }\n\n    if (0)\n    {\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        CASEORDER(1, 0);\n    }\n    else if (this->m_coloring_level == 2)\n    {\n        CASEORDER(2, 1);\n    }\n    else if (this->m_coloring_level == 3)\n    {\n        CASEORDER(3, 1);\n    }\n    else if (this->m_coloring_level == 4)\n    {\n        CASEORDER(4, 1);\n    }\n    else if (this->m_coloring_level == 5)\n    {\n        CASEORDER(5, 1);\n    }\n}\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\ntemplate<int K1, int COLORING_LEVEL, int DO_SORT>\nvoid\nGreedy_Recolor_MatrixColoring<TemplateConfig<AMGX_device, V, M, I> >::color_matrix_specialized_per_numhash( Matrix_d &A )\n{\n    const int num_rows = A.get_num_rows();\n    const int n_blocks = 26 * 4; //TODO: choose optimal blocks number\n    const int block_size = 256;\n    ViewType oldView = A.currentView();\n#if 1 //use fast multihash above as precolorer\n    this->m_row_colors.resize( A.row_offsets.size() - 1, 0 );\n    typedef typename Matrix<TemplateConfig<AMGX_host, V, M, I> > ::IVector IVector_h;\n    IVector sorted_rows_by_color;\n    IVector_h offsets_rows_per_color;\n    {\n        device_vector_alloc<int> sets_per_block(n_blocks + 1);\n        int *sets_per_block_ptr = amgx::thrust::raw_pointer_cast(sets_per_block.data());\n\n        if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n        else { A.setViewExterior(); }\n\n        int num_uncolored = num_rows;\n        int i = 0;\n#if DISCARD_COLORED\n        device_vector_alloc<int> color_in(num_rows);\n#else\n        device_vector_alloc<int> color_in;\n#endif\n#if USE_GTLT\n        device_vector_alloc<unsigned long long int> gtlt(num_rows);\n        const int K = K1 * 0.5;\n#else\n        const int K = K1;\n#endif\n        int prev_num_uncolored = num_uncolored;\n        unsigned int seed = 0;\n\n        while (num_uncolored > 0)\n        {\n            //int mx  = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n            //if(mx > 2*K*i) printf(\"ERR UX %d %d %d\\n\", num_uncolored, mx, 2*K*i);\n#if USE_GTLT\n            fast_multihash_kernel_gtlt_kernel<K, block_size, 1> <<< n_blocks, block_size>>>\n            (num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n             this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(gtlt.data()),\n             seed);\n            cudaCheckError();\n            fast_multihash_kernel_gtlt_assign_kernel<K, block_size, 1> <<< n_blocks, block_size>>>\n            (num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n             this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(gtlt.data()),\n             2 * K * i);\n            cudaCheckError();\n#else\n\n            if (num_uncolored == num_rows || (!DISCARD_COLORED))\n            {\n                fast_multihash_kernel<false, K, COLORING_LEVEL, block_size, 1> <<< n_blocks, block_size>>>(num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n                        this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(color_in.data()),\n                        2 * K * i, seed,\n                        sets_per_block_ptr);\n                        cudaCheckError();\n            }\n            else\n            {\n                fast_multihash_kernel<true, K, COLORING_LEVEL, block_size, 1> <<< n_blocks, block_size>>>(num_rows, A.row_offsets.raw(), A.col_indices.raw(),\n                        this->m_row_colors.raw(), amgx::thrust::raw_pointer_cast(color_in.data()),\n                        2 * K * i, seed,\n                        sets_per_block_ptr);\n            }\n\n            cudaCheckError();\n#endif\n            seed = hash2(seed, 0);\n#if GET_COLORS == GET_COLORS_THRUST || USE_GTLT\n            num_uncolored = amgx::thrust::count( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0);\n            cudaCheckError();\n#elif GET_COLORS == GET_COLORS_TWOPASS\n            num_uncolored -= amgx::strided_reduction::count_block_results_1(n_blocks, sets_per_block_ptr, amgx::strided_reduction::op_sum());\n#elif (GET_COLORS == GET_COLORS_ATOMIC) || (GET_COLORS == GET_COLORS_GATHER)\n            num_uncolored -= sets_per_block[0];\n#endif\n            //printf(\"%d -> %d\\n\", prev_num_uncolored,num_uncolored,prev_num_uncolored-num_uncolored);\n            i++;\n\n            if (prev_num_uncolored == num_uncolored)\n            {\n                //printf(\"ERR %d %d %d\\n\", prev_num_uncolored,num_uncolored,num_rows);\n                break;\n            }\n\n            prev_num_uncolored = num_uncolored;\n#if DISCARD_COLORED\n            color_in.swap(this->m_row_colors);\n#endif\n            /*if (iteration++%4==0) {\n                cudaEventRecord(throttle_event);\n                cudaCheckError();\n            } else {\n                cudaEventSynchronize(throttle_event);\n                cudaCheckError();\n            }*/\n        }\n\n#if DISCARD_COLORED\n        color_in.swap(this->m_row_colors);\n#endif\n        int max_color = i * K * 2; //available_color+c0 = 2*K*i + 2*(K-1)+2=2K(i+1), but i++\n        //max_color = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n        //printf(\"CC %d<%d\\n\",max_color,i*K*2+2);\n        this->m_num_colors = max_color + 1;\n#if ONLY_FIRSTSTEP\n\n        if (1)\n#else\n        if (this->m_coloring_custom_arg == \"FIRSTSTEP\")\n#endif\n        {\n            A.setView(oldView);\n            return;\n        }\n\n        if (DO_SORT)\n        {\n            //return;\n            //this->createColorArrays(A)\n            IVector row_colors(this->m_row_colors); //useless copy\n            sorted_rows_by_color.resize(num_rows);\n            //this->m_sorted_rows_by_color.resize(num_rows);\n            thrust_wrapper::sequence<AMGX_device>(sorted_rows_by_color.begin(), sorted_rows_by_color.end()); //useless sequence\n            thrust_wrapper::sort_by_key<AMGX_device>(row_colors.begin(), row_colors.begin() + num_rows, sorted_rows_by_color.begin()); //useless read from sequence\n            cudaCheckError();\n            IVector offsets_rows_per_color_d(this->m_num_colors + 1);\n            amgx::thrust::lower_bound(row_colors.begin(),\n                                row_colors.begin() + num_rows,\n                                amgx::thrust::counting_iterator<IndexType>(0),\n                                amgx::thrust::counting_iterator<IndexType>(offsets_rows_per_color_d.size()),\n                                offsets_rows_per_color_d.begin());\n            // Copy from device to host\n            offsets_rows_per_color = offsets_rows_per_color_d;\n            cudaCheckError();\n            /*for(int i=0;i<offsets_rows_per_color_d.size(); i++)\n            {\n                int x= offsets_rows_per_color[i];\n            }*/\n            /*oldView = A.currentView();\n            if  (this->m_halo_coloring == SYNC_COLORS) A.setView(ALL);\n            else A.setViewExterior();*/\n        }\n    }\n#else\n    //1st pass: color with a fast method\n    MatrixColoring<typename Matrix_d::TConfig> *first_pass_matrix_coloring = MatrixColoringFactory<typename Matrix_d::TConfig>::allocate(this->first_pass_config, this->first_pass_config_scope);\n    first_pass_matrix_coloring->colorMatrix(A);\n#if DO_SORT\n    first_pass_matrix_coloring->createColorArrays(A);\n#endif\n    this->m_row_colors = first_pass_matrix_coloring->getRowColors();\n    this->m_num_colors = first_pass_matrix_coloring->getNumColors();\n    sorted_rows_by_color = first_pass_matrix_coloring->getSortedRowsByColor();\n    offsets_rows_per_color = first_pass_matrix_coloring->getOffsetsRowsPerColor();\n    delete first_pass_matrix_coloring;\n    //ViewType oldView = A.currentView();\n    //if  (this->m_halo_coloring == SYNC_COLORS) A.setView(ALL);\n    //else A.setViewExterior();\n#endif\n    cudaCheckError();\n    //2nd pass: optimize using parallel greedy for each color.\n    //If 1st pass coloring is valid all elements of same color can be processed in parallel by definition\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    device_vector_alloc<int> max_color_per_block(n_blocks + 1, 1);\n    int *max_color_per_block_ptr = amgx::thrust::raw_pointer_cast(max_color_per_block.data());\n#else\n    int *max_color_per_block_ptr = 0;\n#endif\n    float avg_nnz_per_row = A.get_num_nz() / float(A.get_num_rows());\n    int WARP_SIZE = 2;\n\n    if ( avg_nnz_per_row < 4.0f )\n    {\n        WARP_SIZE = 2;\n    }\n    else if ( avg_nnz_per_row < 8.0f )\n    {\n        WARP_SIZE = 4;\n    }\n    else if ( avg_nnz_per_row < 32.0f )\n    {\n        WARP_SIZE = 8;\n    }\n    else\n    {\n        WARP_SIZE = 32;\n    }\n\n    if (this->m_coloring_level >= 2)\n    {\n        WARP_SIZE = 1;\n    }\n\n    //;\n    //int max_colorx = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n    //printf(\"MAX %d\",max_colorx);\n#define RECOLOR_CALL_CASE(__WSIZE) if(WARP_SIZE==__WSIZE){ \\\n  recolor_greedy_kernel<DO_SORT,COLORING_LEVEL,block_size,__WSIZE><<<GRID_SIZE, block_size>>>(\\\n    num_rows,\\\n    A.row_offsets.raw(),\\\n    A.col_indices.raw(),\\\n    color, this->m_row_colors.raw(),\\\n    sorted_rows_by_color.raw(),start,end,\\\n    max_color_per_block_ptr\\\n    ); cudaCheckError(); }\n    cudaCheckError();\n#if 1 //straight order: generates a color histogram similar to parallel greedy\n    //start from color=2: 1st color would be assigned to 1, no change\n\n    //iteration = 0;\n\n    for (int color = 2; color <= this->m_num_colors; color++)\n    {\n        const int GRID_SIZE = n_blocks;//\n        int start, end;\n\n        if (DO_SORT)\n        {\n            start = offsets_rows_per_color[color];\n            end  = num_rows;\n\n            if (color < this->m_num_colors) { end = offsets_rows_per_color[color + 1]; }\n\n            if (end >= num_rows) { end = num_rows; }\n\n            //printf(\"F %d %d %d < num_rows: %d\\n\",color,start,end,num_rows,(int)end<=start);\n            if (end <= start) { continue; }\n\n            //int NUM_WARPS_PER_CTA = block_size/WARP_SIZE;\n            //const int GRID_SIZE = std::min( 2048, ((end-start) + NUM_WARPS_PER_CTA-1) / NUM_WARPS_PER_CTA );\n            //printf(\"%d - %d > %d %d %d\\n\",end-start,color,start,end,num_rows);\n        }\n        else\n        {\n            start = 0;\n            end = num_rows;\n        }\n\n        //printf(\"%d : %d %d\\n\", color, start, end);\n        RECOLOR_CALL_CASE(1);\n        RECOLOR_CALL_CASE(2);\n        RECOLOR_CALL_CASE(4);\n        RECOLOR_CALL_CASE(8);\n        RECOLOR_CALL_CASE(16);\n        RECOLOR_CALL_CASE(32);\n        cudaCheckError();\n        /*if (iteration++%4==0) {\n            cudaEventRecord(throttle_event);\n            cudaCheckError();\n        } else {\n            cudaEventSynchronize(throttle_event);\n            cudaCheckError();\n        }*/\n    }\n\n#else //reverse order: generates a different kind of histogram\n\n    for (int color = this->m_num_colors; color >= 0; color--)\n    {\n        RECOLOR_CALL_CASE(1);\n        RECOLOR_CALL_CASE(2);\n        RECOLOR_CALL_CASE(4);\n        RECOLOR_CALL_CASE(8);\n        RECOLOR_CALL_CASE(16);\n        RECOLOR_CALL_CASE(32);\n    }\n\n#endif\n    cudaCheckError();\n#if GETMAX_COLOR == GETMAX_TWOPASS\n    int max_color = amgx::strided_reduction::count_block_results_1(n_blocks, max_color_per_block_ptr, amgx::strided_reduction::op_max());\n    /*int max_colorg = amgx::thrust::reduce( this->m_row_colors.begin(), this->m_row_colors.begin()+num_rows, 0, amgx::thrust::maximum<int>() );\n\n    max_color=0;\n    for(int i=0;i<n_blocks;i++)\n    {\n      if(max_color_per_block[i] > max_color) max_color = max_color_per_block[i];\n    }\n    printf(\"MAXCOLOR ref=%d %d\\n\",max_colorg,max_color);\n    if(max_colorg!=max_color)exit(1);*/\n#else\n    int max_color = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() );\n#endif\n    //printf(\"MAXCOLOR %d %d\\n\",max_color_gold,max_color);\n    cudaCheckError();\n    this->m_num_colors = max_color + 1;\n    A.setView(oldView);\n}\n\n\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Recolor_MatrixColoring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Greedy_Recolor_MatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n", "source": "/kaggle/working/amgx/src/matrix_coloring/greedy_recolor.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/min_max.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\n#include <sm_utils.inl>\n\n#define MAX(a, b) (((a) > (b)) ? (a) : (b))\n\n//#define USE_EXPERIMENTAL_MIN_MAX\n#define USE_EXPERIMENTAL_NEIGHBORS\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\n__host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\n#ifdef USE_EXPERIMENTAL_MIN_MAX\ntemplate< typename IndexType, int kCtaSize >\n__global__\nvoid colorRowsKernel(const IndexType *A_offsets,\n                     const IndexType *A_column_indices,\n                     IndexType *row_colors,\n                     const int current_color,\n                     const int num_rows)\n{\n    const int num_rows_per_grid = gridDim.x * ( kCtaSize / 32 );\n    const int lane_id = threadIdx.x % 32;\n\n    for ( int row_id = ( blockIdx.x * kCtaSize + threadIdx.x ) / 32 ; row_id < num_rows ; row_id += num_rows_per_grid )\n    {\n        int my_row_color = row_colors[row_id];\n\n        if ( my_row_color != 0 )\n        {\n            continue;\n        }\n\n        int hash_i = hash_function(row_id, 0);\n        bool max_i = true;\n        bool min_i = true;\n        int row_it  = A_offsets[row_id  ];\n        int row_end = A_offsets[row_id + 1];\n\n        for ( row_it += lane_id ; utils::any(row_it < row_end) ; row_it += 32 )\n        {\n            int j = row_it < row_end ? A_column_indices[row_it] : -1;\n            int hash_j = hash_function(j, 0);\n            int row_color_j = -1;\n\n            if ( row_it < row_end && hash_i != hash_j && j < num_rows ) //TODO: is this correct? (ignore edges thru bndry\n            {\n                row_color_j = row_colors[j];\n            }\n\n            // There is an uncolored neighbour that is greater\n            max_i &= utils::all( hash_i > hash_j || ( row_color_j != 0 && row_color_j != current_color ) );\n            // There is an uncolored neighbour that is smaller\n            min_i &= utils::all( hash_i < hash_j || ( row_color_j != 0 && row_color_j != current_color + 1 ) );\n        }\n\n        if ( lane_id == 0 && (max_i || min_i) )\n        {\n            row_colors[row_id] = current_color + (min_i ? 1 : 0);\n        }\n    }\n}\n#else\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors,\n                     const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int my_row_color = 0;\n\n        // skip if already colored\n        if (row_colors[i] == 0)\n        {\n            int hash_i = hash_function(i, 0);\n            bool max_i, min_i;\n            max_i = true;\n            min_i = true;\n            int row_start = A_offsets[i];\n            int row_end = A_offsets[i + 1];\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >= num_rows) { continue; }\n\n                int hash_j = hash_function(j, 0);\n                int row_color_j = row_colors[j];\n\n                // There is an uncolored neighbour that is greater\n                if ( hash_j > hash_i && (row_color_j == 0 || row_color_j == current_color) )\n                {\n                    max_i = false;\n                }\n\n                // There is an uncolored neighbour that is smaller\n                if (hash_j < hash_i && (row_color_j == 0 || row_color_j == current_color + 1) )\n                {\n                    min_i = false;\n                }\n            }\n\n            if (max_i)\n            {\n                my_row_color = current_color;\n            }\n            else if (min_i)\n            {\n                my_row_color = current_color + 1;\n            }\n\n            if (my_row_color != 0)\n            {\n                row_colors[i] = my_row_color;\n            }\n        }\n    }\n}\n#endif\n\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n\ntemplate< int CTA_SIZE, int NUM_THREADS_PER_ROW >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nfind_min_max_neighbors_kernel( const int *__restrict A_offsets,\n                               const int *__restrict A_column_indices,\n                               const int *__restrict row_colors,\n                               int *__restrict max_hash_array,\n                               int *__restrict min_hash_array,\n                               const int current_color,\n                               const int num_rows )\n{\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    const int warp_id = threadIdx.x / NUM_THREADS_PER_ROW;\n    const int lane_id = threadIdx.x % NUM_THREADS_PER_ROW;\n\n    for ( int row_id = blockIdx.x * NUM_ROWS_PER_CTA + warp_id ; utils::any(row_id < num_rows) ; row_id += gridDim.x * NUM_ROWS_PER_CTA )\n    {\n        int max_hash = INT_MIN;\n        int min_hash = INT_MAX;\n\n        if ( lane_id == 0 && row_id < num_rows )\n        {\n            int hash_i = hash_function(row_id, 0);\n\n            if ( row_colors[row_id] == 0 )\n            {\n                max_hash = hash_i;\n                min_hash = hash_i;\n            }\n        }\n\n        int row_it = 0, row_end = 0;\n\n        if ( row_id < num_rows )\n        {\n            row_it  = A_offsets[row_id + 0];\n            row_end = A_offsets[row_id + 1];\n        }\n\n        for ( row_it += lane_id ; utils::any(row_it < row_end) ; row_it += NUM_THREADS_PER_ROW )\n        {\n            int a_col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                a_col_id = A_column_indices[row_it];\n            }\n\n            int hash_j = 0;\n            int col_color = -1;\n\n            if ( a_col_id != -1 && a_col_id < num_rows )\n            {\n                hash_j    = hash_function(a_col_id, 0);\n                col_color = row_colors[a_col_id];\n            }\n\n            // There is an uncolored neighbour that is greater\n            if ( col_color == 0 && hash_j > max_hash )\n            {\n                max_hash = hash_j;\n            }\n\n            // There is an uncolored neighbour that is smaller\n            if ( col_color == 0 && hash_j < min_hash )\n            {\n                min_hash = hash_j;\n            }\n        }\n\n#pragma unroll\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            min_hash = min( min_hash, utils::shfl_xor( min_hash, mask ) );\n            max_hash = max( max_hash, utils::shfl_xor( max_hash, mask ) );\n        }\n\n        if ( row_id < num_rows && lane_id == 0 )\n        {\n            max_hash_array[row_id] = max_hash;\n            min_hash_array[row_id] = min_hash;\n        }\n    }\n}\n\n#else\n\ntemplate <typename IndexType>\n__global__\nvoid FindMaxMinNeighboursKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, IndexType *max_hash_array, IndexType *min_hash_array,\n                                const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int max_hash;\n        int min_hash;\n        int hash_i = hash_function(i, 0);\n        int row_col = row_colors[i];\n        max_hash = (row_col == 0) ? hash_i : INT_MIN;\n        min_hash = (row_col == 0) ? hash_i : INT_MAX;\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n\n        for (int r = row_start; r < row_end; r++)\n        {\n            int j = A_column_indices[r];\n\n            if (j >= num_rows) { continue; }\n\n            int hash_j = hash_function(j, 0);\n            int row_color_j = row_colors[j];\n            // Only consider if neighbour is not colored\n            // There is an uncolored neighbour that is greater\n            max_hash = (row_color_j == 0 && hash_j > max_hash) ? hash_j : max_hash;\n            // There is an uncolored neighbour that is smaller\n            min_hash = (row_color_j == 0 && hash_j < min_hash) ? hash_j : min_hash;\n        }\n\n        max_hash_array[i] = max_hash;\n        min_hash_array[i] = min_hash;\n    }\n}\n\n#endif\n\ntemplate <typename IndexType>\n__global__\nvoid colorRowsRingTwoKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const IndexType *max_hash_array, const IndexType *min_hash_array,\n                            const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int my_row_color = 0;\n\n        // skip if already colored\n        if (row_colors[i] == 0)\n        {\n            int hash_i = hash_function(i, 0);\n            int max_hash = max_hash_array[i];\n            int min_hash = min_hash_array[i];\n            bool max_i, min_i;\n            max_i = true;\n            min_i = true;\n\n            // Check if I was a max or min in previous step\n            if ( hash_i != max_hash && hash_i != min_hash)\n            {\n                continue;\n            }\n\n            if (hash_i != min_hash) { min_i = false; }\n\n            if (hash_i != max_hash) { max_i = false; }\n\n            int row_start = A_offsets[i];\n            int row_end = A_offsets[i + 1];\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >=  num_rows) { continue; }\n\n                int min_hash_j = min_hash_array[j];\n                int max_hash_j = max_hash_array[j];\n                // There is a neighbour that has a uncolored neighbour with larger hash\n                max_i = (max_hash_j > max_hash && max_hash_j != INT_MIN) ? false : max_i;\n                // There is a neighbour that has a uncolored neighbour with smaller hash\n                min_i = (min_hash_j < min_hash && min_hash_j != INT_MAX) ? false : min_i;\n            }\n\n            if (max_i && min_i)\n            {\n                my_row_color = (i % 2) ? current_color : current_color + 1;\n            }\n            else if (max_i)\n            {\n                my_row_color = current_color;\n            }\n            else if (min_i)\n            {\n                my_row_color = current_color + 1;\n            }\n\n            if (my_row_color != 0)\n            {\n                row_colors[i] = my_row_color;\n            }\n        }\n    }\n}\n\n\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nMinMaxMatrixColoringBase<T_Config>::MinMaxMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n}\n\n\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    const int num_rows =  A.get_num_rows();\n    int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 256;\n    const int warps_per_block = threads_per_block / 32;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + warps_per_block - 1) / warps_per_block );\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n    cudaCheckError();\n\n    for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n    {\n#ifdef USE_EXPERIMENTAL_MIN_MAX\n        colorRowsKernel<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            row_colors_ptr,\n            this->m_num_colors,\n            num_rows);\n            cudaCheckError();\n#else\n        colorRowsKernel<IndexType> <<< num_blocks, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(A.row_offsets.raw(), A.col_indices.raw(), row_colors_ptr, this->m_num_colors, num_rows);\n        cudaCheckError();\n#endif\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n}\n\n// Block version\ntemplate <class T_Config>\nvoid MinMaxMatrixColoringBase<T_Config>::colorMatrix(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else if (this->m_coloring_level == 2)\n    {\n        this->colorMatrixTwoRing(A);\n    }\n    else\n    {\n        FatalError(\"Min-max coloring does not support coloring_level > 2\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixTwoRing(Matrix_d &A)\n{\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    const int threads_per_block = 256;\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n    const int half_warps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + half_warps_per_block - 1) / half_warps_per_block );\n#else\n    const int warps_per_block = threads_per_block / 32;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + warps_per_block - 1) / warps_per_block );\n#endif\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n    cudaCheckError();\n    IVector max_hash_array(num_rows);\n    IVector min_hash_array(num_rows);\n    float avg_nnz_per_row = A.get_num_nz() / float(num_rows);\n\n    for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n    {\n        // Each vertex checks its neighbours and store the max and min values of its neighbours\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n        if ( avg_nnz_per_row < 8.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 2> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n        else if ( avg_nnz_per_row < 16.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 4> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n        else // if( avg_nnz_per_row < 32.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 8> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n\n#else\n        FindMaxMinNeighboursKernel <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows);\n        cudaCheckError();\n#endif\n        // Each vertex checks if its still the min or max\n        colorRowsRingTwoKernel<IndexType> <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows);\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n#if 0\n    std::cout << \"Num colors=\" << this->m_num_colors << std::endl;\n    // Print coloring statistics\n    //for (int i=0;i<this->m_num_colors;i++)\n    //  std::cout << \"color= \" << i << \", num_rows = \" << A.offsets_rows_per_color[i+1] - A.offsets_rows_per_color[i] << std::endl;\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MinMax matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixTwoRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MinMax matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class MinMaxMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MinMaxMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/min_max.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\n#include <sm_utils.inl>\n\n#define MAX(a, b) (((a) > (b)) ? (a) : (b))\n\n//#define USE_EXPERIMENTAL_MIN_MAX\n#define USE_EXPERIMENTAL_NEIGHBORS\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\n__host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\n#ifdef USE_EXPERIMENTAL_MIN_MAX\ntemplate< typename IndexType, int kCtaSize >\n__global__\nvoid colorRowsKernel(const IndexType *A_offsets,\n                     const IndexType *A_column_indices,\n                     IndexType *row_colors,\n                     const int current_color,\n                     const int num_rows)\n{\n    const int num_rows_per_grid = gridDim.x * ( kCtaSize / 32 );\n    const int lane_id = threadIdx.x % 32;\n\n    for ( int row_id = ( blockIdx.x * kCtaSize + threadIdx.x ) / 32 ; row_id < num_rows ; row_id += num_rows_per_grid )\n    {\n        int my_row_color = row_colors[row_id];\n\n        if ( my_row_color != 0 )\n        {\n            continue;\n        }\n\n        int hash_i = hash_function(row_id, 0);\n        bool max_i = true;\n        bool min_i = true;\n        int row_it  = A_offsets[row_id  ];\n        int row_end = A_offsets[row_id + 1];\n\n        for ( row_it += lane_id ; utils::any(row_it < row_end) ; row_it += 32 )\n        {\n            int j = row_it < row_end ? A_column_indices[row_it] : -1;\n            int hash_j = hash_function(j, 0);\n            int row_color_j = -1;\n\n            if ( row_it < row_end && hash_i != hash_j && j < num_rows ) //TODO: is this correct? (ignore edges thru bndry\n            {\n                row_color_j = row_colors[j];\n            }\n\n            // There is an uncolored neighbour that is greater\n            max_i &= utils::all( hash_i > hash_j || ( row_color_j != 0 && row_color_j != current_color ) );\n            // There is an uncolored neighbour that is smaller\n            min_i &= utils::all( hash_i < hash_j || ( row_color_j != 0 && row_color_j != current_color + 1 ) );\n        }\n\n        if ( lane_id == 0 && (max_i || min_i) )\n        {\n            row_colors[row_id] = current_color + (min_i ? 1 : 0);\n        }\n    }\n}\n#else\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors,\n                     const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int my_row_color = 0;\n\n        // skip if already colored\n        if (row_colors[i] == 0)\n        {\n            int hash_i = hash_function(i, 0);\n            bool max_i, min_i;\n            max_i = true;\n            min_i = true;\n            int row_start = A_offsets[i];\n            int row_end = A_offsets[i + 1];\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >= num_rows) { continue; }\n\n                int hash_j = hash_function(j, 0);\n                int row_color_j = row_colors[j];\n\n                // There is an uncolored neighbour that is greater\n                if ( hash_j > hash_i && (row_color_j == 0 || row_color_j == current_color) )\n                {\n                    max_i = false;\n                }\n\n                // There is an uncolored neighbour that is smaller\n                if (hash_j < hash_i && (row_color_j == 0 || row_color_j == current_color + 1) )\n                {\n                    min_i = false;\n                }\n            }\n\n            if (max_i)\n            {\n                my_row_color = current_color;\n            }\n            else if (min_i)\n            {\n                my_row_color = current_color + 1;\n            }\n\n            if (my_row_color != 0)\n            {\n                row_colors[i] = my_row_color;\n            }\n        }\n    }\n}\n#endif\n\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n\ntemplate< int CTA_SIZE, int NUM_THREADS_PER_ROW >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nfind_min_max_neighbors_kernel( const int *__restrict A_offsets,\n                               const int *__restrict A_column_indices,\n                               const int *__restrict row_colors,\n                               int *__restrict max_hash_array,\n                               int *__restrict min_hash_array,\n                               const int current_color,\n                               const int num_rows )\n{\n    const int NUM_ROWS_PER_CTA = CTA_SIZE / NUM_THREADS_PER_ROW;\n    const int warp_id = threadIdx.x / NUM_THREADS_PER_ROW;\n    const int lane_id = threadIdx.x % NUM_THREADS_PER_ROW;\n\n    for ( int row_id = blockIdx.x * NUM_ROWS_PER_CTA + warp_id ; utils::any(row_id < num_rows) ; row_id += gridDim.x * NUM_ROWS_PER_CTA )\n    {\n        int max_hash = INT_MIN;\n        int min_hash = INT_MAX;\n\n        if ( lane_id == 0 && row_id < num_rows )\n        {\n            int hash_i = hash_function(row_id, 0);\n\n            if ( row_colors[row_id] == 0 )\n            {\n                max_hash = hash_i;\n                min_hash = hash_i;\n            }\n        }\n\n        int row_it = 0, row_end = 0;\n\n        if ( row_id < num_rows )\n        {\n            row_it  = A_offsets[row_id + 0];\n            row_end = A_offsets[row_id + 1];\n        }\n\n        for ( row_it += lane_id ; utils::any(row_it < row_end) ; row_it += NUM_THREADS_PER_ROW )\n        {\n            int a_col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                a_col_id = A_column_indices[row_it];\n            }\n\n            int hash_j = 0;\n            int col_color = -1;\n\n            if ( a_col_id != -1 && a_col_id < num_rows )\n            {\n                hash_j    = hash_function(a_col_id, 0);\n                col_color = row_colors[a_col_id];\n            }\n\n            // There is an uncolored neighbour that is greater\n            if ( col_color == 0 && hash_j > max_hash )\n            {\n                max_hash = hash_j;\n            }\n\n            // There is an uncolored neighbour that is smaller\n            if ( col_color == 0 && hash_j < min_hash )\n            {\n                min_hash = hash_j;\n            }\n        }\n\n#pragma unroll\n        for ( int mask = NUM_THREADS_PER_ROW / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            min_hash = min( min_hash, utils::shfl_xor( min_hash, mask ) );\n            max_hash = max( max_hash, utils::shfl_xor( max_hash, mask ) );\n        }\n\n        if ( row_id < num_rows && lane_id == 0 )\n        {\n            max_hash_array[row_id] = max_hash;\n            min_hash_array[row_id] = min_hash;\n        }\n    }\n}\n\n#else\n\ntemplate <typename IndexType>\n__global__\nvoid FindMaxMinNeighboursKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, IndexType *max_hash_array, IndexType *min_hash_array,\n                                const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int max_hash;\n        int min_hash;\n        int hash_i = hash_function(i, 0);\n        int row_col = row_colors[i];\n        max_hash = (row_col == 0) ? hash_i : INT_MIN;\n        min_hash = (row_col == 0) ? hash_i : INT_MAX;\n        int row_start = A_offsets[i];\n        int row_end = A_offsets[i + 1];\n\n        for (int r = row_start; r < row_end; r++)\n        {\n            int j = A_column_indices[r];\n\n            if (j >= num_rows) { continue; }\n\n            int hash_j = hash_function(j, 0);\n            int row_color_j = row_colors[j];\n            // Only consider if neighbour is not colored\n            // There is an uncolored neighbour that is greater\n            max_hash = (row_color_j == 0 && hash_j > max_hash) ? hash_j : max_hash;\n            // There is an uncolored neighbour that is smaller\n            min_hash = (row_color_j == 0 && hash_j < min_hash) ? hash_j : min_hash;\n        }\n\n        max_hash_array[i] = max_hash;\n        min_hash_array[i] = min_hash;\n    }\n}\n\n#endif\n\ntemplate <typename IndexType>\n__global__\nvoid colorRowsRingTwoKernel(const IndexType *A_offsets, const IndexType *A_column_indices, IndexType *row_colors, const IndexType *max_hash_array, const IndexType *min_hash_array,\n                            const int current_color, const int num_rows)\n{\n    for ( int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows ; i += gridDim.x * blockDim.x )\n    {\n        int my_row_color = 0;\n\n        // skip if already colored\n        if (row_colors[i] == 0)\n        {\n            int hash_i = hash_function(i, 0);\n            int max_hash = max_hash_array[i];\n            int min_hash = min_hash_array[i];\n            bool max_i, min_i;\n            max_i = true;\n            min_i = true;\n\n            // Check if I was a max or min in previous step\n            if ( hash_i != max_hash && hash_i != min_hash)\n            {\n                continue;\n            }\n\n            if (hash_i != min_hash) { min_i = false; }\n\n            if (hash_i != max_hash) { max_i = false; }\n\n            int row_start = A_offsets[i];\n            int row_end = A_offsets[i + 1];\n\n            for (int r = row_start; r < row_end; r++)\n            {\n                int j = A_column_indices[r];\n\n                if (j >=  num_rows) { continue; }\n\n                int min_hash_j = min_hash_array[j];\n                int max_hash_j = max_hash_array[j];\n                // There is a neighbour that has a uncolored neighbour with larger hash\n                max_i = (max_hash_j > max_hash && max_hash_j != INT_MIN) ? false : max_i;\n                // There is a neighbour that has a uncolored neighbour with smaller hash\n                min_i = (min_hash_j < min_hash && min_hash_j != INT_MAX) ? false : min_i;\n            }\n\n            if (max_i && min_i)\n            {\n                my_row_color = (i % 2) ? current_color : current_color + 1;\n            }\n            else if (max_i)\n            {\n                my_row_color = current_color;\n            }\n            else if (min_i)\n            {\n                my_row_color = current_color + 1;\n            }\n\n            if (my_row_color != 0)\n            {\n                row_colors[i] = my_row_color;\n            }\n        }\n    }\n}\n\n\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nMinMaxMatrixColoringBase<T_Config>::MinMaxMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        m_uncolored_fraction = 0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n}\n\n\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    const int num_rows =  A.get_num_rows();\n    int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 256;\n    const int warps_per_block = threads_per_block / 32;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + warps_per_block - 1) / warps_per_block );\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n    cudaCheckError();\n\n    for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n    {\n#ifdef USE_EXPERIMENTAL_MIN_MAX\n        colorRowsKernel<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            row_colors_ptr,\n            this->m_num_colors,\n            num_rows);\n            cudaCheckError();\n#else\n        colorRowsKernel<IndexType> <<< num_blocks, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(A.row_offsets.raw(), A.col_indices.raw(), row_colors_ptr, this->m_num_colors, num_rows);\n        cudaCheckError();\n#endif\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n}\n\n// Block version\ntemplate <class T_Config>\nvoid MinMaxMatrixColoringBase<T_Config>::colorMatrix(Matrix<T_Config> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Calling coloring scheme but coloring level==0\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else if (this->m_coloring_level == 2)\n    {\n        this->colorMatrixTwoRing(A);\n    }\n    else\n    {\n        FatalError(\"Min-max coloring does not support coloring_level > 2\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n\n    A.setView(oldView);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixTwoRing(Matrix_d &A)\n{\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = (int) (this->m_uncolored_fraction * ((ValueType) num_rows));\n    const int threads_per_block = 256;\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n    const int half_warps_per_block = threads_per_block / 16;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + half_warps_per_block - 1) / half_warps_per_block );\n#else\n    const int warps_per_block = threads_per_block / 32;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows + warps_per_block - 1) / warps_per_block );\n#endif\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>(this->m_row_colors.begin(), this->m_row_colors.end(), 0);\n    cudaCheckError();\n    IVector max_hash_array(num_rows);\n    IVector min_hash_array(num_rows);\n    float avg_nnz_per_row = A.get_num_nz() / float(num_rows);\n\n    for ( int num_uncolored = num_rows; num_uncolored > max_uncolored_rows ; )\n    {\n        // Each vertex checks its neighbours and store the max and min values of its neighbours\n#ifdef USE_EXPERIMENTAL_NEIGHBORS\n        if ( avg_nnz_per_row < 8.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 2> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n        else if ( avg_nnz_per_row < 16.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 4> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n        else // if( avg_nnz_per_row < 32.0f )\n        {\n            find_min_max_neighbors_kernel<threads_per_block, 8> <<< num_blocks, threads_per_block>>>( A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows );\n            cudaCheckError();\n        }\n\n#else\n        FindMaxMinNeighboursKernel <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows);\n        cudaCheckError();\n#endif\n        // Each vertex checks if its still the min or max\n        colorRowsRingTwoKernel<IndexType> <<< num_blocks, threads_per_block>>>(A.row_offsets.raw(), A.col_indices.raw(), this->m_row_colors.raw(), max_hash_array.raw(), min_hash_array.raw(), this->m_num_colors, num_rows);\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n#if 0\n    std::cout << \"Num colors=\" << this->m_num_colors << std::endl;\n    // Print coloring statistics\n    //for (int i=0;i<this->m_num_colors;i++)\n    //  std::cout << \"color= \" << i << \", num_rows = \" << A.offsets_rows_per_color[i+1] - A.offsets_rows_per_color[i] << std::endl;\n#endif\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MinMax matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MinMaxMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixTwoRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented MinMax matrix coloring for host format\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\n#define AMGX_CASE_LINE(CASE) template class MinMaxMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class MinMaxMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/min_max.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/round_robin.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\nnamespace amgx\n{\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(IndexType *row_colors, const int num_colors, const int num_rows)\n{\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += gridDim.x * blockDim.x)\n    {\n        row_colors[i] = i % num_colors;\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nRoundRobinMatrixColoringBase<T_Config>::RoundRobinMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        FatalError(\"Current implementation of the round-robin coloring does not permit an exact coloring, and therefore cannot lead to deterministic results. Implementation of a deterministic round-robin coloring algorithm still pending\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    this->m_num_colors = cfg.AMG_Config::template getParameter<int>(\"num_colors\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid RoundRobinMatrixColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Callingn coloring scheme but coloring level==0\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else\n    {\n        FatalError(\"Round robin coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n}\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid RoundRobinMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 64;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / threads_per_block + 1);\n    colorRowsKernel<IndexType> <<< num_blocks, threads_per_block>>>(row_colors_ptr, this->m_num_colors, num_rows);\n    cudaCheckError();\n    /*\n    // Sort the vertices based on their color\n    A.sorted_rows_by_color.resize(num_owned_rows);\n\n    // Copy row colors\n    IVector row_colors(num_owned_rows);\n    amgx::thrust::copy(A.row_colors.begin(), A.row_colors.begin()+num_owned_rows, row_colors.begin());\n\n    thrust_wrapper::sequence(A.sorted_rows_by_color.begin(),A.sorted_rows_by_color.end());\n    amgx::thrust::sort_by_key(row_colors.begin(),row_colors.end(),A.sorted_rows_by_color.begin());\n    cudaCheckError();\n\n    // Compute the offset for each color\n    IVector d_offsets_rows_per_color(A.num_colors+1);\n    A.offsets_rows_per_color.resize(A.num_colors+1);\n\n    amgx::thrust::lower_bound(row_colors.begin(),\n                        row_colors.end(),\n                        amgx::thrust::counting_iterator<IndexType>(0),\n                         amgx::thrust::counting_iterator<IndexType>(d_offsets_rows_per_color.size()),\n                         d_offsets_rows_per_color.begin());\n\n    cudaCheckError();\n\n    // Copy from device to host\n    A.offsets_rows_per_color = d_offsets_rows_per_color;\n    */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid RoundRobinMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented RoundRobin matrix coloring for host\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class RoundRobinMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class RoundRobinMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/round_robin.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n\nnamespace amgx\n{\n// ---------------------------\n// Kernels\n// ---------------------------\n\n// Kernel to color the rows of the matrix, using min-max approach\ntemplate <typename IndexType>\n__global__\nvoid colorRowsKernel(IndexType *row_colors, const int num_colors, const int num_rows)\n{\n    for (int i = threadIdx.x + blockDim.x * blockIdx.x; i < num_rows; i += gridDim.x * blockDim.x)\n    {\n        row_colors[i] = i % num_colors;\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate<class T_Config>\nRoundRobinMatrixColoringBase<T_Config>::RoundRobinMatrixColoringBase(AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if (cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\"))\n    {\n        FatalError(\"Current implementation of the round-robin coloring does not permit an exact coloring, and therefore cannot lead to deterministic results. Implementation of a deterministic round-robin coloring algorithm still pending\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    this->m_num_colors = cfg.AMG_Config::template getParameter<int>(\"num_colors\", cfg_scope);\n}\n\ntemplate<class TConfig>\nvoid RoundRobinMatrixColoringBase<TConfig>::colorMatrix(Matrix<TConfig> &A)\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize(A.row_offsets.size() - 1, 0);\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    if (this->m_coloring_level == 0)\n    {\n        FatalError(\"Callingn coloring scheme but coloring level==0\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else if (this->m_coloring_level == 1)\n    {\n        this->colorMatrixOneRing(A);\n    }\n    else\n    {\n        FatalError(\"Round robin coloring algorithm can only do one ring coloring\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    A.setView(oldView);\n}\n\n// Block version\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid RoundRobinMatrixColoring<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_d &A)\n{\n    // One thread per row\n    const int num_rows = A.get_num_rows();\n    IndexType *row_colors_ptr = this->m_row_colors.raw();\n    const int threads_per_block = 64;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (int) (num_rows - 1) / threads_per_block + 1);\n    colorRowsKernel<IndexType> <<< num_blocks, threads_per_block>>>(row_colors_ptr, this->m_num_colors, num_rows);\n    cudaCheckError();\n    /*\n    // Sort the vertices based on their color\n    A.sorted_rows_by_color.resize(num_owned_rows);\n\n    // Copy row colors\n    IVector row_colors(num_owned_rows);\n    amgx::thrust::copy(A.row_colors.begin(), A.row_colors.begin()+num_owned_rows, row_colors.begin());\n\n    thrust_wrapper::sequence(A.sorted_rows_by_color.begin(),A.sorted_rows_by_color.end());\n    amgx::thrust::sort_by_key(row_colors.begin(),row_colors.end(),A.sorted_rows_by_color.begin());\n    cudaCheckError();\n\n    // Compute the offset for each color\n    IVector d_offsets_rows_per_color(A.num_colors+1);\n    A.offsets_rows_per_color.resize(A.num_colors+1);\n\n    amgx::thrust::lower_bound(row_colors.begin(),\n                        row_colors.end(),\n                        amgx::thrust::counting_iterator<IndexType>(0),\n                         amgx::thrust::counting_iterator<IndexType>(d_offsets_rows_per_color.size()),\n                         d_offsets_rows_per_color.begin());\n\n    cudaCheckError();\n\n    // Copy from device to host\n    A.offsets_rows_per_color = d_offsets_rows_per_color;\n    */\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid RoundRobinMatrixColoring<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::colorMatrixOneRing(Matrix_h &A)\n{\n    FatalError(\"Haven't implemented RoundRobin matrix coloring for host\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#define AMGX_CASE_LINE(CASE) template class RoundRobinMatrixColoringBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class RoundRobinMatrixColoring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/round_robin.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <algorithm>\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/min_max_2ring.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n\n#define COLORING_DEBUG 1\n\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed, unsigned int rows = 0)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid count_gtlt_kernel( const int A_num_rows,\n                        const int *__restrict A_rows,\n                        const int *__restrict A_cols,\n                        const int *__restrict A_colors,\n                        int *A_gtlt_count,\n                        const int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_gtlt_count[row_id] = -1;\n            }\n\n            continue;\n        }\n\n        // Hash my row id.\n        int row_hash = hash_function( row_id, seed );\n        // The number of vertices that are greater/smaller than me.\n        int gt_count = 0, lt_count = 0;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed );\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Threads determine if they are greater than the row hash.\n            int gt_pred = col_color == 0 && col_hash > row_hash;\n            int lt_pred = col_color == 0 && col_hash < row_hash;\n            // Count greater/smaller neighbors.\n            gt_count += __popc( utils::ballot( gt_pred ) );\n            lt_count += __popc( utils::ballot( lt_pred ) );\n        }\n\n        // The warp leader stores the result.\n        int my_gtlt_count = (gt_count << 16) | lt_count;\n\n        if ( lane_id == 0 )\n        {\n            A_gtlt_count[row_id] = my_gtlt_count;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LATE_REJECTION >\n__global__\nvoid color_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_gtlt_count, const int current_color, const int weakness_bound, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n\n        // Min-max algorithm.\n        if ( row_gt_count == 0 && row_lt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + (row_id & 1);\n            }\n\n            continue;\n        }\n\n        if ( row_gt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color;\n            }\n\n            continue;\n        }\n\n        if ( row_lt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + 1;\n            }\n\n            continue;\n        }\n\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = row_gt_count <= weakness_bound;\n        int is_min_vertex = row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column (it could help late rejection).\n            if ( LATE_REJECTION )\n            {\n                int col_color = -1;\n\n                if ( row_it < row_end && col_id < A_num_rows)\n                {\n                    col_color = A_colors[col_id];\n                }\n\n                // Late rejection test.\n                if ( col_color == current_color )\n                {\n                    is_max_vertex = 0;\n                }\n\n                if ( col_color == current_color + 1 )\n                {\n                    is_min_vertex = 0;\n                }\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n            // Threads determine if they are greater than the row hash.\n\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id <= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        // The warp leader stores the result.\n        if ( candidate && utils::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color;\n            }\n\n            continue;\n        }\n\n        if ( candidate && utils::all( is_min_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + 1;\n            }\n\n            continue;\n        }\n    }\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_check_coloring_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_colors, const int *A_gtlt_count, int *error_found )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Is there something wrong ??\n            if ( row_id != col_id && row_color == col_color && row_color != 0)\n            {\n                if ( A_gtlt_count != NULL && !error_found[0] )\n                {\n                    //printf( \"row_id=%d, row_color=%d, col_id=%d, col_color=%d\\n\", row_id, row_color, col_id, col_color );\n                    /*\n                    // The number of vertices that are greater/smaller than me.\n                    int row_gtlt_count = A_gtlt_count[row_id];\n                    int row_gt_count = row_gtlt_count >> 16;\n                    int row_lt_count = row_gtlt_count  & 0xffff;\n                    printf( \"row_gt_count=%d, row_gt_count=%d\\n\", row_gt_count, row_lt_count );\n\n                    int col_gtlt_count = A_gtlt_count[col_id];\n                    int col_gt_count = col_gtlt_count >> 16;\n                    int col_lt_count = col_gtlt_count  & 0xffff;\n                    printf( \"col_gt_count=%d, col_gt_count=%d\\n\", col_gt_count, col_lt_count ); */\n                }\n\n                error_found[0] = 1;\n                atomicAdd(error_found + 1, 1);\n            }\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nMin_Max_2Ring_Matrix_Coloring_Base<T_Config>::Min_Max_2Ring_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level != 1 )\n    {\n        FatalError( \"Not implemented for coloring_level != 1\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\") )\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    m_weakness_bound = cfg.AMG_Config::template getParameter<int>( \"weakness_bound\", cfg_scope );\n    m_late_rejection = cfg.AMG_Config::template getParameter<int>( \"late_rejection\", cfg_scope ) != 0;\n}\n\n\n\n#if !NEW_COLORER_TESTS\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    device_vector_alloc<int> gtlt_count( num_rows );\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows ; )\n    {\n        count_gtlt_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ), 0);\n        cudaCheckError();\n\n        if ( this->m_late_rejection )\n            color_kernel<CTA_SIZE, 32, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n        else\n            color_kernel<CTA_SIZE, 32, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n\n        cudaCheckError();\n#if 0\n        device_vector_alloc<int> error_found( 1, 0 );\n        dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n            amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n        cudaCheckError();\n#endif\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n#if 0\n    device_vector_alloc<int> error_found( 1, 0 );\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        NULL,\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n\n    if ( error_found[0] != 0 )\n    {\n        std::cout << \"INVALID COLORING !!! Two neighbors have the same color!!!\" << std::endl;\n    }\n\n#endif\n    A.setView(oldView);\n}\n\n\n#else\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_coloring_histogram_kernel( int *colors_count, const int A_num_rows, const int *A_colors, const int n_colors )\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    __shared__ unsigned int color_counts[256];\n\n    if (threadIdx.x < n_colors)\n    {\n        color_counts[threadIdx.x] = 0;\n    }\n\n    __syncthreads();\n\n    for (; tid < A_num_rows; tid += blockDim.x * gridDim.x)\n    {\n        int col = A_colors[tid];\n        atomicAdd(color_counts + col, 1);\n    }\n\n    __syncthreads();\n\n    if (threadIdx.x < n_colors)\n    {\n        atomicAdd(colors_count + threadIdx.x, color_counts[threadIdx.x]);\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::debug_coloring(Matrix_d &A, int step)\n{\n#if COLORING_DEBUG\n    const int num_rows = A.get_num_rows();\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    int num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n    cudaCheckError();\n    int maxr = A.row_offsets[1] - A.row_offsets[0];\n    /*for(int i=2;i<=num_rows;i++)\n    {\n     int d=A.row_offsets[i]-A.row_offsets[i-1];\n     if(d>maxr)\n     {\n        maxr=d;\n     }\n    }*/\n    device_vector_alloc<int> error_found( 2, 0 );\n    error_found[0] = 0;\n    error_found[1] = 0;\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        0,//amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n    {\n        device_vector_alloc<int> color_histogram(this->m_num_colors + 1);\n\n        for (int i = 0; i < color_histogram.size(); i++)\n        {\n            color_histogram[i] = 0;\n        }\n\n        dbg_coloring_histogram_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>( amgx::thrust::raw_pointer_cast(&color_histogram.front()), num_rows, this->m_row_colors.raw(), this->m_num_colors + 1);\n        cudaCheckError();\n\n        for (int i = 0; i < color_histogram.size(); i++)\n        {\n            std::cout << step << \"\\t\" << \"H[\" << i << \"] = \" << color_histogram[i] << std::endl;\n        }\n\n        std::cout << step << \"\\t\" << \"Errors=\" << error_found[1] << std::endl;\n        std::cout << step << \"\\t\" << \"Uncolored=\" << num_uncolored << std::endl;\n        std::cout << step << \"\\t\" << \"Num colors=\" << this->m_num_colors << \"/\" << maxr << std::endl;\n    }\n#endif\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LATE_REJECTION >\n__global__\nvoid color_kernel_greedy( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_gtlt_count, const int current_color, const int weakness_bound, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = true;//row_gt_count <= weakness_bound;\n        int is_min_vertex = true;//row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                int col_color = A_colors[col_id];\n\n                if ( col_color > 0 )\n                {\n                    used_colors |= (1ull << (64 - col_color));\n                }\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n\n            // Threads determine if they are greater than the row hash.\n            //if( col_gt_count != -1 )\n            //  is_max_vertex &= col_gt_count < row_gt_count || (col_gt_count == row_gt_count && col_id < row_id);\n            //if( col_lt_count != -1 )\n            //  is_min_vertex &= col_lt_count /*>*/ < row_lt_count || (col_lt_count == row_lt_count && col_id > row_id);\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id >= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        is_min_vertex = false;\n        //reduce used colors bit by bit.\n#pragma unroll\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2hiint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color = 64 - utils::bfind( ~used_colors );\n\n        if (my_color <= 0) { my_color = 1; }\n\n        /*int sets=0;\n        for(int c=1; c<=64; c++)\n        {\n            unsigned long long int b = (1ull << (64-c));\n            if((~used_colors & b) && sets < (dice % 3)-1)\n            {\n                sets++;\n                my_color = c;\n            }\n        }*/\n\n        // The warp leader stores the result.\n        if ( candidate && utils::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            continue;\n        }\n\n        if ( candidate && utils::all( is_min_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color + 1;\n            }\n\n            continue;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid color_kernel_reassign_tail_thread( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors, int first_tail_color, int *A_colors )\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color < first_tail_color ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 198719871987L, 0);\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows) { continue; }\n\n            int col_hash = hash_function( col_id, 1987, 0 );\n            int col_color = A_colors[col_id];\n        }\n    }\n}\n\n__global__ void unassign_color(int *A_colors, int num_rows)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        if (tid % 9 == 0)\n        {\n            A_colors[tid] = 0;\n        }\n    }\n}\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_step( Matrix_d &A, int seed )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    device_vector_alloc<int> gtlt_count( num_rows );\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows ; )\n    {\n        count_gtlt_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n            seed);\n        cudaCheckError();\n\n        if ( this->m_late_rejection )\n            color_kernel_greedy<CTA_SIZE, 32, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n        else\n            color_kernel_greedy<CTA_SIZE, 32, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n}\n\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_matrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    color_step(A, 0);\n\n    A.setView(oldView);\n}\n\n\n#endif\n\n\n#define AMGX_CASE_LINE(CASE) template class Min_Max_2Ring_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Min_Max_2Ring_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n\n#include <basic_types.h>\n#include <util.h>\n#include <error.h>\n#include <types.h>\n#include <matrix_coloring/min_max_2ring.h>\n#include <cusp/format.h>\n#include <cusp/copy.h>\n#include <cusp/detail/random.h>\n#include <thrust/count.h>\n#include <thrust/extrema.h>\n#include <sm_utils.inl>\n\n#define COLORING_DEBUG 1\n\n\n// Pseudo-random number generator\nnamespace amgx\n{\n\nstatic __host__ __device__ unsigned int hash_function(unsigned int a, unsigned int seed, unsigned int rows = 0)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == 0;\n    }\n};\n\n// ---------------------------\n// Kernels\n// ---------------------------\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid count_gtlt_kernel( const int A_num_rows,\n                        const int *__restrict A_rows,\n                        const int *__restrict A_cols,\n                        const int *__restrict A_colors,\n                        int *A_gtlt_count,\n                        const int seed)\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_gtlt_count[row_id] = -1;\n            }\n\n            continue;\n        }\n\n        // Hash my row id.\n        int row_hash = hash_function( row_id, seed );\n        // The number of vertices that are greater/smaller than me.\n        int gt_count = 0, lt_count = 0;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Each thread hashes its column id.\n            int col_hash = hash_function( col_id, seed );\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Threads determine if they are greater than the row hash.\n            int gt_pred = col_color == 0 && col_hash > row_hash;\n            int lt_pred = col_color == 0 && col_hash < row_hash;\n            // Count greater/smaller neighbors.\n            gt_count += __popc( utils::ballot( gt_pred ) );\n            lt_count += __popc( utils::ballot( lt_pred ) );\n        }\n\n        // The warp leader stores the result.\n        int my_gtlt_count = (gt_count << 16) | lt_count;\n\n        if ( lane_id == 0 )\n        {\n            A_gtlt_count[row_id] = my_gtlt_count;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LATE_REJECTION >\n__global__\nvoid color_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_gtlt_count, const int current_color, const int weakness_bound, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n\n        // Min-max algorithm.\n        if ( row_gt_count == 0 && row_lt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + (row_id & 1);\n            }\n\n            continue;\n        }\n\n        if ( row_gt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color;\n            }\n\n            continue;\n        }\n\n        if ( row_lt_count == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + 1;\n            }\n\n            continue;\n        }\n\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = row_gt_count <= weakness_bound;\n        int is_min_vertex = row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column (it could help late rejection).\n            if ( LATE_REJECTION )\n            {\n                int col_color = -1;\n\n                if ( row_it < row_end && col_id < A_num_rows)\n                {\n                    col_color = A_colors[col_id];\n                }\n\n                // Late rejection test.\n                if ( col_color == current_color )\n                {\n                    is_max_vertex = 0;\n                }\n\n                if ( col_color == current_color + 1 )\n                {\n                    is_min_vertex = 0;\n                }\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n            // Threads determine if they are greater than the row hash.\n\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id <= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        // The warp leader stores the result.\n        if ( candidate && utils::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color;\n            }\n\n            continue;\n        }\n\n        if ( candidate && utils::all( is_min_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = current_color + 1;\n            }\n\n            continue;\n        }\n    }\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_check_coloring_kernel( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_colors, const int *A_gtlt_count, int *error_found )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int row_color = A_colors[row_id];\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            // Get the color of the column.\n            int col_color = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows)\n            {\n                col_color = A_colors[col_id];\n            }\n\n            // Is there something wrong ??\n            if ( row_id != col_id && row_color == col_color && row_color != 0)\n            {\n                if ( A_gtlt_count != NULL && !error_found[0] )\n                {\n                    //printf( \"row_id=%d, row_color=%d, col_id=%d, col_color=%d\\n\", row_id, row_color, col_id, col_color );\n                    /*\n                    // The number of vertices that are greater/smaller than me.\n                    int row_gtlt_count = A_gtlt_count[row_id];\n                    int row_gt_count = row_gtlt_count >> 16;\n                    int row_lt_count = row_gtlt_count  & 0xffff;\n                    printf( \"row_gt_count=%d, row_gt_count=%d\\n\", row_gt_count, row_lt_count );\n\n                    int col_gtlt_count = A_gtlt_count[col_id];\n                    int col_gt_count = col_gtlt_count >> 16;\n                    int col_lt_count = col_gtlt_count  & 0xffff;\n                    printf( \"col_gt_count=%d, col_gt_count=%d\\n\", col_gt_count, col_lt_count ); */\n                }\n\n                error_found[0] = 1;\n                atomicAdd(error_found + 1, 1);\n            }\n        }\n    }\n}\n\n// ---------------------------\n// Methods\n// ---------------------------\n\ntemplate< class T_Config >\nMin_Max_2Ring_Matrix_Coloring_Base<T_Config>::Min_Max_2Ring_Matrix_Coloring_Base( AMG_Config &cfg, const std::string &cfg_scope) : MatrixColoring<T_Config>(cfg, cfg_scope)\n{\n    if ( this->m_coloring_level != 1 )\n    {\n        FatalError( \"Not implemented for coloring_level != 1\", AMGX_ERR_NOT_SUPPORTED_TARGET );\n    }\n\n    if ( cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\") )\n    {\n        m_uncolored_fraction = 0.0;\n    }\n    else\n    {\n        m_uncolored_fraction = cfg.AMG_Config::template getParameter<double>(\"max_uncolored_percentage\", cfg_scope);\n    }\n\n    m_weakness_bound = cfg.AMG_Config::template getParameter<int>( \"weakness_bound\", cfg_scope );\n    m_late_rejection = cfg.AMG_Config::template getParameter<int>( \"late_rejection\", cfg_scope ) != 0;\n}\n\n\n\n#if !NEW_COLORER_TESTS\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::colorMatrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    device_vector_alloc<int> gtlt_count( num_rows );\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows ; )\n    {\n        count_gtlt_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ), 0);\n        cudaCheckError();\n\n        if ( this->m_late_rejection )\n            color_kernel<CTA_SIZE, 32, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n        else\n            color_kernel<CTA_SIZE, 32, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n\n        cudaCheckError();\n#if 0\n        device_vector_alloc<int> error_found( 1, 0 );\n        dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n            amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n        cudaCheckError();\n#endif\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n    cudaCheckError();\n#if 0\n    device_vector_alloc<int> error_found( 1, 0 );\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        NULL,\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n\n    if ( error_found[0] != 0 )\n    {\n        std::cout << \"INVALID COLORING !!! Two neighbors have the same color!!!\" << std::endl;\n    }\n\n#endif\n    A.setView(oldView);\n}\n\n\n#else\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid\ndbg_coloring_histogram_kernel( int *colors_count, const int A_num_rows, const int *A_colors, const int n_colors )\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    __shared__ unsigned int color_counts[256];\n\n    if (threadIdx.x < n_colors)\n    {\n        color_counts[threadIdx.x] = 0;\n    }\n\n    __syncthreads();\n\n    for (; tid < A_num_rows; tid += blockDim.x * gridDim.x)\n    {\n        int col = A_colors[tid];\n        atomicAdd(color_counts + col, 1);\n    }\n\n    __syncthreads();\n\n    if (threadIdx.x < n_colors)\n    {\n        atomicAdd(colors_count + threadIdx.x, color_counts[threadIdx.x]);\n    }\n}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::debug_coloring(Matrix_d &A, int step)\n{\n#if COLORING_DEBUG\n    const int num_rows = A.get_num_rows();\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    int num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n    cudaCheckError();\n    int maxr = A.row_offsets[1] - A.row_offsets[0];\n    /*for(int i=2;i<=num_rows;i++)\n    {\n     int d=A.row_offsets[i]-A.row_offsets[i-1];\n     if(d>maxr)\n     {\n        maxr=d;\n     }\n    }*/\n    device_vector_alloc<int> error_found( 2, 0 );\n    error_found[0] = 0;\n    error_found[1] = 0;\n    dbg_check_coloring_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        this->m_row_colors.raw(),\n        0,//amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n        amgx::thrust::raw_pointer_cast( &error_found.front() ) );\n    cudaCheckError();\n    {\n        device_vector_alloc<int> color_histogram(this->m_num_colors + 1);\n\n        for (int i = 0; i < color_histogram.size(); i++)\n        {\n            color_histogram[i] = 0;\n        }\n\n        dbg_coloring_histogram_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>( amgx::thrust::raw_pointer_cast(&color_histogram.front()), num_rows, this->m_row_colors.raw(), this->m_num_colors + 1);\n        cudaCheckError();\n\n        for (int i = 0; i < color_histogram.size(); i++)\n        {\n            std::cout << step << \"\\t\" << \"H[\" << i << \"] = \" << color_histogram[i] << std::endl;\n        }\n\n        std::cout << step << \"\\t\" << \"Errors=\" << error_found[1] << std::endl;\n        std::cout << step << \"\\t\" << \"Uncolored=\" << num_uncolored << std::endl;\n        std::cout << step << \"\\t\" << \"Num colors=\" << this->m_num_colors << \"/\" << maxr << std::endl;\n    }\n#endif\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE, bool LATE_REJECTION >\n__global__\nvoid color_kernel_greedy( const int A_num_rows, const int *A_rows, const int *A_cols, const int *A_gtlt_count, const int current_color, const int weakness_bound, int *A_colors )\n{\n    const int NUM_WARPS_PER_CTA  = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // Thread coordinates.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Row identifier.\n    int row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Iterate over the rows of the matrix.\n    for ( ; row_id < A_num_rows ; row_id += NUM_WARPS_PER_GRID )\n    {\n        int dice = hash_function(row_id, 19881988, 0);\n        int row_color = A_colors[row_id];\n\n        if ( row_color != 0 ) // Already colored!!!\n        {\n            continue;\n        }\n\n        // The number of vertices that are greater/smaller than me.\n        int row_gtlt_count = A_gtlt_count[row_id];\n        // Split gtlt_count into 2.\n        int row_gt_count = row_gtlt_count >> 16;\n        int row_lt_count = row_gtlt_count  & 0xffff;\n        // Do we skip it.\n        int candidate = 1;\n        // Predicates. Is a vertex min/max.\n        int is_max_vertex = true;//row_gt_count <= weakness_bound;\n        int is_min_vertex = true;//row_lt_count <= weakness_bound;\n        // Iterators over my row.\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n        unsigned long long used_colors = 0ull;\n\n        for ( ; row_begin < row_end ; row_begin += WARP_SIZE )\n        {\n            // Iterator.\n            int row_it = row_begin + lane_id;\n            // Get the column index (if the iterator is valid).\n            int col_id = -1;\n\n            if ( row_it < row_end )\n            {\n                col_id = A_cols[row_it];\n            }\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                int col_color = A_colors[col_id];\n\n                if ( col_color > 0 )\n                {\n                    used_colors |= (1ull << (64 - col_color));\n                }\n            }\n\n            // Get the gt/lt count.\n            int col_gtlt_count = -1;\n\n            if ( row_it < row_end && col_id < A_num_rows )\n            {\n                col_gtlt_count = A_gtlt_count[col_id];\n            }\n\n            // Split gtlt_count into 2. col_gt/lt_count == -1 if already colored.\n            int col_gt_count = col_gtlt_count >> 16;\n            int col_lt_count = col_gtlt_count  & 0xffff;\n\n            // Threads determine if they are greater than the row hash.\n            //if( col_gt_count != -1 )\n            //  is_max_vertex &= col_gt_count < row_gt_count || (col_gt_count == row_gt_count && col_id < row_id);\n            //if( col_lt_count != -1 )\n            //  is_min_vertex &= col_lt_count /*>*/ < row_lt_count || (col_lt_count == row_lt_count && col_id > row_id);\n            if ( col_gtlt_count != -1 )\n            {\n                is_max_vertex &= col_gt_count > row_gt_count || (col_gt_count == row_gt_count && col_id >= row_id);\n                is_min_vertex &= col_lt_count > row_lt_count || (col_lt_count == row_lt_count && col_id >= row_id);\n            }\n        }\n\n        is_min_vertex = false;\n        //reduce used colors bit by bit.\n#pragma unroll\n        for (int i = WARP_SIZE / 2; i >= 1; i /= 2)\n        {\n            int tmp_hi = __double2hiint( __longlong_as_double( used_colors ) );\n            int tmp_lo = __double2hiint( __longlong_as_double( used_colors ) );\n            tmp_hi = utils::shfl_xor(tmp_hi, i, WARP_SIZE);\n            tmp_lo = utils::shfl_xor(tmp_lo, i, WARP_SIZE);\n            long long tmp = __double_as_longlong(__hiloint2double(tmp_hi, tmp_lo));\n            used_colors |= tmp;\n        }\n\n        int my_color = 64 - utils::bfind( ~used_colors );\n\n        if (my_color <= 0) { my_color = 1; }\n\n        /*int sets=0;\n        for(int c=1; c<=64; c++)\n        {\n            unsigned long long int b = (1ull << (64-c));\n            if((~used_colors & b) && sets < (dice % 3)-1)\n            {\n                sets++;\n                my_color = c;\n            }\n        }*/\n\n        // The warp leader stores the result.\n        if ( candidate && utils::all( is_max_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color;\n            }\n\n            continue;\n        }\n\n        if ( candidate && utils::all( is_min_vertex ) )\n        {\n            if ( lane_id == 0 )\n            {\n                A_colors[row_id] = my_color + 1;\n            }\n\n            continue;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE, int WARP_SIZE>\n__global__\nvoid color_kernel_reassign_tail_thread( const int A_num_rows, const int *A_rows, const int *A_cols, int num_colors, int first_tail_color, int *A_colors )\n{\n    const int NUM_THREADS_PER_GRID = gridDim.x * CTA_SIZE;\n    // Row identifier.\n    int row_id = blockIdx.x * CTA_SIZE + threadIdx.x;\n\n    // Iterate over rows.\n    for ( ; row_id < A_num_rows ; row_id += NUM_THREADS_PER_GRID )\n    {\n        unsigned long long used_colors = 0ull;\n        int row_color = A_colors[row_id];\n\n        if ( row_color < first_tail_color ) // Already colored!!!\n        {\n            continue;\n        }\n\n        int row_hash = hash_function(row_id, 198719871987L, 0);\n        int row_begin = A_rows[row_id  ];\n        int row_end   = A_rows[row_id + 1];\n\n        for ( int row_it = row_begin; row_it < row_end ; row_it++)\n        {\n            int col_id = A_cols[row_it];\n\n            if (col_id >= A_num_rows) { continue; }\n\n            int col_hash = hash_function( col_id, 1987, 0 );\n            int col_color = A_colors[col_id];\n        }\n    }\n}\n\n__global__ void unassign_color(int *A_colors, int num_rows)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        if (tid % 9 == 0)\n        {\n            A_colors[tid] = 0;\n        }\n    }\n}\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_step( Matrix_d &A, int seed )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    const int num_rows = A.get_num_rows();\n    const int max_uncolored_rows = static_cast<int>( this->m_uncolored_fraction * num_rows );\n    const int CTA_SIZE = 128;\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int GRID_SIZE = std::min( 2048, (num_rows + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA );\n    device_vector_alloc<int> gtlt_count( num_rows );\n\n    for ( int num_uncolored = num_rows ; num_uncolored > max_uncolored_rows ; )\n    {\n        count_gtlt_kernel<CTA_SIZE, 32> <<< GRID_SIZE, CTA_SIZE>>>(\n            num_rows,\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            this->m_row_colors.raw(),\n            amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n            seed);\n        cudaCheckError();\n\n        if ( this->m_late_rejection )\n            color_kernel_greedy<CTA_SIZE, 32, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n        else\n            color_kernel_greedy<CTA_SIZE, 32, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast( &gtlt_count.front() ),\n                this->m_num_colors,\n                this->m_weakness_bound,\n                this->m_row_colors.raw() );\n\n        cudaCheckError();\n        this->m_num_colors += 2;\n        num_uncolored = (int) amgx::thrust::count_if( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, is_zero() );\n        cudaCheckError();\n    }\n\n    this->m_num_colors = thrust_wrapper::reduce<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.begin() + num_rows, 0, amgx::thrust::maximum<int>() ) + 1;\n}\n\n\n\n\n// Block version\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nMin_Max_2Ring_Matrix_Coloring<TemplateConfig<AMGX_device, V, M, I> >::color_matrix( Matrix_d &A )\n{\n    ViewType oldView = A.currentView();\n    this->m_row_colors.resize( A.row_offsets.size() - 1 );\n\n    if  (this->m_halo_coloring == SYNC_COLORS) { A.setView(ALL); }\n    else { A.setViewExterior(); }\n\n    this->m_num_colors = 1;\n    thrust_wrapper::fill<AMGX_device>( this->m_row_colors.begin(), this->m_row_colors.end(), 0 );\n    cudaCheckError();\n    color_step(A, 0);\n\n    A.setView(oldView);\n}\n\n\n#endif\n\n\n#define AMGX_CASE_LINE(CASE) template class Min_Max_2Ring_Matrix_Coloring_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Min_Max_2Ring_Matrix_Coloring<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // end namespace amgx\n\n", "source": "/kaggle/working/amgx/src/matrix_coloring/min_max_2ring.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/aggregation_amg_level.h>\n#include <matrix_analysis.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <basic_types.h>\n#include <util.h>\n#include <fstream>\n#include <cutil.h>\n#include <multiply.h>\n#include <transpose.h>\n#include <blas.h>\n#include <string>\n#include <string.h>\n#include <iostream>\n#include <algorithm>\n#include <amgx_timer.h>\n\n#include <amgx_types/util.h>\n\n#include <thrust/sort.h>\n#include <thrust/remove.h>\n#include <thrust/transform.h>\n#include <thrust/binary_search.h>\n#include <thrust/unique.h>\n#include <thrust/inner_product.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\n\n// ----------------------\n// Kernels\n// ----------------------\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid set_to_one_kernel(IndexType start, IndexType end, IndexType *ind, ValueType *v)\n{\n    for (int tid = start + blockDim.x * blockIdx.x + threadIdx.x; tid < end; tid += gridDim.x * blockDim.x)\n    {\n        v[ind[tid]] = types::util<ValueType>::get_one();\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid renumberAggregatesKernel(const IndexType *renumbering, const int interior_offset, const int bdy_offset, IndexType *aggregates, const int num_aggregates, const int n_interior, const int renumbering_size)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < num_aggregates)\n    {\n        IndexType new_agg_id;\n\n        if (renumbering_size == 0)\n        {\n            new_agg_id = aggregates[tid];\n        }\n        else\n        {\n            new_agg_id = renumbering[aggregates[tid]];\n        }\n\n        //if (aggregates[tid] > num_aggregates)\n        //{\n        //printf(\"ID %d old %d + %d = %d\\n\", tid, new_agg_id, ((new_agg_id >= n_interior) ? bdy_offset : interior_offset), new_agg_id + ((new_agg_id >= n_interior) ? bdy_offset : interior_offset));\n        //}\n        new_agg_id +=  ((new_agg_id >= n_interior) ? bdy_offset : interior_offset);\n        aggregates[tid] = new_agg_id;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to restrict residual using csr_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid restrictResidualKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *r, ValueType *rr, const int num_aggregates)\n{\n    int jmin, jmax;\n\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_aggregates; tid += gridDim.x * blockDim.x)\n    {\n        ValueType temp(types::util<ValueType>::get_zero());\n        jmin = row_offsets[tid];\n        jmax = row_offsets[tid + 1];\n\n        for (int j = jmin; j < jmax; j++)\n        {\n            int j_col = column_indices[j];\n            temp = temp + r[j_col];\n        }\n\n        rr[tid] = temp;\n    }\n}\n\n// Kernel to restrict residual using block_dia_csr_format\ntemplate <typename IndexType, typename ValueType, int bsize>\n__global__\nvoid restrictResidualBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *r, ValueType *rr, const int num_aggregates)\n{\n    ValueType rr_temp[bsize];\n    int offset, jmin, jmax;\n\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_aggregates; tid += gridDim.x * blockDim.x)\n    {\n        // Initialize to zero\n#pragma unroll\n        for (int m = 0; m < bsize; m++)\n        {\n            rr_temp[m] = types::util<ValueType>::get_zero();\n        }\n\n        jmin = row_offsets[tid];\n        jmax = row_offsets[tid + 1];\n\n        for (int j = jmin; j < jmax; j++)\n        {\n            int jcol = column_indices[j];\n            offset = jcol * bsize;\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                rr_temp[m] = rr_temp[m] + r[offset + m];\n            }\n        }\n\n        offset = tid * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            rr[offset + m] = rr_temp[m];\n        };\n    }\n}\n\n// Kernel to prolongate and apply the correction for csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateAndApplyCorrectionKernel(const ValueType alpha, const int num_rows, ValueType *x, const ValueType *e, const IndexType *aggregates, IndexType num_aggregates)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        IndexType I = aggregates[tid];\n        x[tid] = x[tid] + alpha * e[I];\n    }\n}\n\n// Kernel to prolongate and apply the correction for block-dia-csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateAndApplyCorrectionBlockDiaCsrKernel(const ValueType alpha, const int num_block_rows, ValueType *x, const ValueType *e, const IndexType *aggregates, IndexType num_aggregates, const int bsize)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_block_rows; tid += gridDim.x * blockDim.x)\n    {\n        IndexType I = aggregates[tid];\n\n        for (int  m = 0; m < bsize; m++)\n        {\n            x[tid * bsize + m] = x[tid * bsize + m] + alpha * e[I * bsize + m];\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateVector(const IndexType *aggregates, const ValueType *in, ValueType *out, IndexType fine_rows, IndexType coarse_rows, int blocksize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < fine_rows * blocksize )\n    {\n        int i = tid / blocksize;\n        int e = tid % blocksize;\n        IndexType I = aggregates[i];\n        out[tid] = in[ I * blocksize + e ];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid applyCorrection(ValueType lambda, const ValueType *e, ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = x[tid] + lambda * e[tid];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -------------------------------\n//  Methods\n// ------------------------------\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::transfer_level(AMG_Level<TConfig1> *ref_lvl)\n{\n    Aggregation_AMG_Level_Base<TConfig1> *ref_agg_lvl = dynamic_cast<Aggregation_AMG_Level_Base<TConfig1>*>(ref_lvl);\n    this->scale_counter = ref_agg_lvl->scale_counter;\n    this->scale = ref_agg_lvl->scale;\n    this->m_R_row_offsets.copy(ref_agg_lvl->m_R_row_offsets);\n    this->m_R_column_indices.copy(ref_agg_lvl->m_R_column_indices);\n    this->m_aggregates.copy(ref_agg_lvl->m_aggregates);\n    this->m_aggregates_fine_idx.copy(ref_agg_lvl->m_aggregates_fine_idx);\n    this->m_num_aggregates = ref_agg_lvl->m_num_aggregates;\n    this->m_num_all_aggregates = ref_agg_lvl->m_num_all_aggregates;\n}\n\n\ntypedef std::pair<int, int> mypair;\nbool comparator ( const mypair &l, const mypair &r) { return l.first < r.first; }\n\n// Method to compute R\n// General path\n// TODO: this could be merged with selector to save some computations\ntemplate <typename T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::computeRestrictionOperator_common()\n{\n    m_R_row_offsets.resize(m_num_all_aggregates + 1); //create one more row for the pseudo aggregate\n    IVector R_row_indices(m_aggregates);\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    bool use_cpu = m_aggregates.size() < 4096;\n\n    if (use_cpu)\n    {\n        struct computeRestrictionTask : public task\n        {\n            Aggregation_AMG_Level_Base<T_Config> *self;\n            IVector *R_row_indices;\n\n            void run()\n            {\n                int N = self->m_aggregates.size();\n                IVector_h R_row_indices_host(self->m_aggregates);\n                std::vector<mypair> pairs(N);\n\n                for (int i = 0; i < N; i++)\n                {\n                    pairs[i].first = R_row_indices_host[i];\n                    pairs[i].second = i;\n                }\n\n                std::stable_sort(pairs.begin(), pairs.end(), comparator);\n                IVector_h R_column_indices(self->A->get_num_rows());\n\n                for (int i = 0; i < N; i++)\n                {\n                    R_column_indices[i] = pairs[i].second;\n                    R_row_indices_host[i] = pairs[i].first;\n                }\n\n                self->m_R_column_indices = R_column_indices;\n                *R_row_indices = R_row_indices_host;\n            }\n        };\n        computeRestrictionTask *t = new computeRestrictionTask();\n        t->self = this;\n        t->R_row_indices = &R_row_indices;\n        t->run();\n        delete t;\n    }\n    else\n#endif\n    {\n        m_R_column_indices.resize(this->A->get_num_rows());\n        thrust_wrapper::sequence<TConfig::memSpace>(m_R_column_indices.begin(), m_R_column_indices.end());\n        cudaCheckError();\n        amgx::thrust::sort_by_key(R_row_indices.begin(), R_row_indices.end(), m_R_column_indices.begin());\n        cudaCheckError();\n    }\n\n    amgx::thrust::lower_bound(R_row_indices.begin(),\n                        R_row_indices.end(),\n                        amgx::thrust::counting_iterator<typename IVector::value_type>(0),\n                        amgx::thrust::counting_iterator<typename IVector::value_type>(m_R_row_offsets.size()),\n                        m_R_row_offsets.begin());\n    cudaCheckError();\n}\n\n\n// two methods below could be merged\n// Method to compute R on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_1x1()\n{\n    this->m_R_row_offsets.resize(this->m_num_all_aggregates + 1);\n    this->m_R_column_indices.resize(this->A->get_num_rows());\n    this->fillRowOffsetsAndColIndices(this->A->get_num_rows());\n}\n\n// Method to compute R on HOST using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_4x4()\n{\n    this->m_R_row_offsets.resize(this->m_num_all_aggregates + 1);\n    this->m_R_column_indices.resize(this->A->get_num_rows());\n    this->fillRowOffsetsAndColIndices(this->A->get_num_rows());\n}\n\n// Method to create R_row_offsest and R_column_indices array on HOST using csr or block dia-csr format\ntemplate <typename T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::fillRowOffsetsAndColIndices(const int R_num_cols)\n{\n    for (int i = 0; i < m_num_all_aggregates + 1; i++)\n    {\n        m_R_row_offsets[i] = 0;\n    }\n\n    // Count number of neighbors for each row\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = m_aggregates[i];\n        m_R_row_offsets[I]++;\n    }\n\n    m_R_row_offsets[m_num_all_aggregates] = R_num_cols;\n\n    for (int i = m_num_all_aggregates - 1; i >= 0; i--)\n    {\n        m_R_row_offsets[i] = m_R_row_offsets[i + 1] - m_R_row_offsets[i];\n    }\n\n    /* Set column indices. */\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = m_aggregates[i];\n        int Ip = m_R_row_offsets[I]++;\n        m_R_column_indices[Ip] = i;\n    }\n\n    /* Reset r[i] to start of row memory. */\n    for (int i = m_num_all_aggregates - 1; i > 0; i--)\n    {\n        m_R_row_offsets[i] = m_R_row_offsets[i - 1];\n    }\n\n    m_R_row_offsets[0] = 0;\n}\n\n// Method to compute R on DEVICE using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_4x4()\n{\n    this->computeRestrictionOperator_common();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_1x1()\n{\n    this->computeRestrictionOperator_common();\n}\n\n// Method to restrict Residual on host using csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_1x1(const VVector &r, VVector &rr)\n{\n    ValueTypeB temp;\n\n    for (int i = 0; i < this->m_num_aggregates; i++)\n    {\n        temp = types::util<ValueTypeB>::get_zero();\n\n        for (int j = this->m_R_row_offsets[i]; j < this->m_R_row_offsets[i + 1]; j++)\n        {\n            int j_col = this->m_R_column_indices[j];\n            temp = temp + r[j_col];\n        }\n\n        rr[i] = temp;\n    }\n}\n\n// Method to restrict Residual on host using block_dia_csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_4x4(const VVector &r, VVector &rr)\n{\n    IndexType bsize = this->A->get_block_dimy();\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    for (int i = 0; i < this->m_num_aggregates; i++)\n    {\n        // Initialize temp to 0\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k]  =  types::util<ValueTypeB>::get_zero();\n        }\n\n        // Add contributions from each fine point\n        for (int j = this->m_R_row_offsets[i]; j < this->m_R_row_offsets[i + 1]; j++)\n        {\n            int j_col = this->m_R_column_indices[j];\n\n            for (int k = 0; k < bsize; k++)\n            {\n                temp[k] = temp[k] + r[j_col * bsize + k];\n            }\n        }\n\n        // Store result\n        for (int k = 0; k < bsize; k++)\n        {\n            rr[i * bsize + k] = temp[k];\n        }\n    }\n}\n\n// Method to restrict Residual on device using csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_1x1(const VVector &r, VVector &rr)\n{\n    int block_size = 128;\n    int max_threads;\n\n    if (!this->isConsolidationLevel())\n    {\n        max_threads = this->m_num_aggregates;\n    }\n    else\n    {\n        max_threads = this->m_num_all_aggregates;\n    }\n\n    int num_blocks = max_threads / block_size + 1;\n    const IndexType *R_row_offsets_ptr = this->m_R_row_offsets.raw();\n    const IndexType *R_column_indices_ptr = this->m_R_column_indices.raw();\n    const ValueTypeB *r_ptr = r.raw();\n    ValueTypeB *rr_ptr = rr.raw();\n    restrictResidualKernel <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n    cudaCheckError();\n}\n\n// Method to restrict Residual on device using block_dia_csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_4x4(const VVector &r, VVector &rr)\n{\n    int block_size = 128;\n    int max_threads;\n\n    if (!this->isConsolidationLevel())\n    {\n        max_threads = this->m_num_aggregates;\n    }\n    else\n    {\n        max_threads = this->m_num_all_aggregates;\n    };\n\n    const int num_blocks = max_threads / block_size + 1;\n    const IndexType *R_row_offsets_ptr = this->m_R_row_offsets.raw();\n\n    const IndexType *R_column_indices_ptr = this->m_R_column_indices.raw();\n\n    const ValueTypeB *r_ptr = r.raw();\n\n    ValueTypeB *rr_ptr = rr.raw();\n\n    cudaCheckError();\n\n    switch ( this->getA().get_block_dimy() )\n    {\n        case 2:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 2> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 3:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 3> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 4:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 4> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 5:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 5> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 8:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 8> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 10:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 10> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"Unsupported block size in restrictResidual_4x4!!!\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    cudaCheckError();\n}\n\n__inline__ float getAlpha(float &nom, float &denom)\n{\n    float alpha;\n\n    if (nom * denom <= 0. || std::abs(nom) < std::abs(denom))\n    {\n        alpha = 1.;\n    }\n    else if (std::abs(nom) > 2.*std::abs(denom))\n    {\n        alpha = 2.;\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ double getAlpha(double &nom, double &denom)\n{\n    double alpha;\n\n    if (nom * denom <= 0. || std::abs(nom) < std::abs(denom))\n    {\n        alpha = 1.;\n    }\n    else if (std::abs(nom) > 2.*std::abs(denom))\n    {\n        alpha = 2.;\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ cuComplex getAlpha(cuComplex &nom, cuComplex &denom)\n{\n    cuComplex alpha;\n\n    if (types::util<cuComplex>::abs(nom) < types::util<cuComplex>::abs(denom))\n    {\n        alpha = make_cuComplex(1.f, 0.f);\n    }\n    else if (types::util<cuComplex>::abs(nom) > 2.*types::util<cuComplex>::abs(denom))\n    {\n        alpha = make_cuComplex(2.f, 0.f);\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ cuDoubleComplex getAlpha(cuDoubleComplex &nom, cuDoubleComplex &denom)\n{\n    cuDoubleComplex alpha;\n\n    if (types::util<cuDoubleComplex>::abs(nom) < types::util<cuDoubleComplex>::abs(denom))\n    {\n        alpha = make_cuDoubleComplex(1., 0.);\n    }\n    else if (types::util<cuDoubleComplex>::abs(nom) > 2.*types::util<cuDoubleComplex>::abs(denom))\n    {\n        alpha = make_cuDoubleComplex(2., 0.);\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\ntemplate< class T_Config>\ntypename T_Config::VecPrec Aggregation_AMG_Level_Base<T_Config>::computeAlpha(const Vector<T_Config> &e, const Vector<T_Config> &bc, const Vector<T_Config> &tmp)\n{\n    typename T_Config::VecPrec alpha =  types::util<ValueTypeB>::get_one();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    int size = Ac.get_num_rows();\n    VVector v(2,  types::util<ValueTypeB>::get_zero());\n    v[0] = amgx::thrust::inner_product(e.begin(), e.begin() + size, bc.begin(),  types::util<ValueTypeB>::get_zero());\n    v[1] = amgx::thrust::inner_product(e.begin(), e.begin() + size, tmp.begin(),  types::util<ValueTypeB>::get_zero());\n    cudaCheckError();\n    return getAlpha(v[0], v[1]);\n}\n\n// Method to prolongate the error on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>  >::prolongateAndApplyCorrection_1x1(Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A = this->getA();\n    Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &C = this->next_h->getA();\n\n    if ( this->m_error_scaling >= 2 )\n    {\n        FatalError(\"error_scaling=2,3 is not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n\n    if (this->m_error_scaling)\n    {\n        multiply(this->next_h->getA(), e, tmp);\n        alpha = this->computeAlpha (e, bc, tmp);\n    }\n\n    // Apply correction on all (interior and exterior) equations.\n    for (int i = 0; i < A.get_num_cols(); i++)\n    {\n        int I = this->m_aggregates[i];\n        x[i] = x[i] + alpha * e[I];\n    }\n}\n\n// Method to prolongate the error on HOST using block_dia_csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_4x4(Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    if (this->A->get_block_dimy() != this->A->get_block_dimx())\n    {\n        FatalError(\"Aggregation_AMG_Level not implemented for non square blocks, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( this->m_error_scaling >= 2 )\n    {\n        FatalError(\"error_scaling=2,3 is not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    Matrix<TConfig> &C = this->next_h->getA();\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n\n    if (this->m_error_scaling)\n    {\n        multiply(this->next_h->getA(), e, tmp);\n        alpha = this->computeAlpha (e, bc, tmp);\n    }\n\n    // Apply correction on all equations.\n    for (int i = 0; i < this->A->get_num_rows(); i++)\n    {\n        int I = this->m_aggregates[i];\n\n        for (int k = 0; k < this->A->get_block_dimy(); k++)\n        {\n            x[i * this->A->get_block_dimy() + k] =  x[i * this->A->get_block_dimy() + k] + alpha * e[I * this->A->get_block_dimy() + k];\n        }\n    }\n}\n\n// Prolongate the error on DEVICE using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_1x1(Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n    const int block_size = 128;\n    const int num_blocks = this->A->get_num_rows() / block_size + 1;\n    const IndexType *aggregates_ptr = this->m_aggregates.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const ValueTypeB *e_ptr = e.raw();\n\n    if (this->m_error_scaling)\n    {\n        FatalError(\"error_scaling=1 is deprecated\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    prolongateAndApplyCorrectionKernel <<< num_blocks, block_size>>>(alpha, (int)this->A->get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates);\n    cudaCheckError();\n}\n\n// Prolongate the error on DEVICE using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_4x4(Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &ec,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &bf,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &xf,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &rf)\n{\n    if ( this->m_error_scaling >= 2 )\n    {\n        if ( this->scale_counter > 0 )\n        {\n            const IndexType *aggregates_ptr = this->m_aggregates.raw();\n            ValueTypeB *x_ptr = xf.raw();\n            const ValueTypeB *e_ptr = ec.raw();\n            const int block_size = 128;\n            const int num_blocks = (this->A->get_num_rows() - 1) / block_size + 1;\n            prolongateAndApplyCorrectionBlockDiaCsrKernel <<< num_blocks, block_size>>>(this->scale, (int)this->getA().get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates, this->getA().get_block_dimy());\n            cudaCheckError();\n            this->scale_counter--;\n            return;\n        }\n\n        bool vanek_scaling = this->m_error_scaling > 3;\n        IndexType numRowsCoarse = this->next_d->getA().get_num_rows();\n        IndexType numRowsFine = this->A->get_num_rows();\n        IndexType blockdim = this->A->get_block_dimx();\n\n        if ( blockdim != this->A->get_block_dimy() )\n        {\n            FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n        VVector ef( rf.size() );\n        VVector Aef( rf.size() );\n        ef.set_block_dimy( blockdim );\n        Aef.set_block_dimy( blockdim );\n        // prolongate e\n        const int threads_per_block = 256;\n        const int num_block_values = std::min( AMGX_GRID_MAX_SIZE, (numRowsFine * blockdim - 1) / threads_per_block + 1);\n        const cudaStream_t stream = nullptr;\n        prolongateVector <<< num_block_values, threads_per_block, 0, stream>>>( this->m_aggregates.raw(), ec.raw(), ef.raw(), numRowsFine, numRowsCoarse, blockdim );\n        cudaCheckError();\n        ef.dirtybit = 1;\n        cudaStreamSynchronize(stream);\n        cudaCheckError();\n        int preSmooth;\n\n        if ( vanek_scaling )\n        {\n            preSmooth = this->amg->getNumPostsweeps();\n        }\n        else\n        {\n            preSmooth = this->scaling_smoother_steps;\n        }\n\n        //smooth error\n        this->smoother->setTolerance( 0.0 );\n        this->smoother->set_max_iters( preSmooth );\n\n        if ( vanek_scaling )\n        {\n            thrust_wrapper::fill<TConfig::memSpace>( Aef.begin(), Aef.end(), types::util<ValueTypeB>::get_zero() );\n            cudaCheckError();\n            this->smoother->solve( Aef, ef, false ); //smooth correction with rhs 0\n            this->smoother->solve( bf, xf, false ); // smooth x with rhs residual\n            //recompute residual\n            int offset, size;\n            this->getA().getOffsetAndSizeForView(OWNED, &offset, &size);\n            axmb( this->getA(), xf, bf, rf, offset, size );\n        }\n        else\n        {\n            this->smoother->solve( rf, ef, false ); //smooth correction with rhs residual\n        }\n\n        // multiply for lambda computation\n        multiply(this->getA(), ef, Aef, OWNED);\n        ValueTypeB nominator, denominator;\n        int offset = 0, size = 0;\n        this->A->getOffsetAndSizeForView(OWNED, &offset, &size);\n\n        if ( this->m_error_scaling == 2 || this->m_error_scaling == 4 )\n        {\n            // compute lambda=<rf,Aef>/<Aef,Aef>\n            nominator = amgx::thrust::inner_product( rf.begin(), rf.end(), Aef.begin(), types::util<ValueTypeB>::get_zero() );\n            denominator = amgx::thrust::inner_product( Aef.begin(), Aef.end(), Aef.begin(), types::util<ValueTypeB>::get_zero() );\n            cudaCheckError();\n        }\n\n        if ( this->m_error_scaling == 3 || this->m_error_scaling == 5)\n        {\n            // compute lambda=<rf,ef>/<ef,Aef>\n            nominator = amgx::thrust::inner_product( rf.begin(), rf.begin() + size * blockdim, ef.begin(), types::util<ValueTypeB>::get_zero() );\n            denominator = amgx::thrust::inner_product( ef.begin(), ef.begin() + size * blockdim, Aef.begin(), types::util<ValueTypeB>::get_zero() );\n\n            if (!this->A->is_matrix_singleGPU())\n            {\n                this->A->getManager()->global_reduce_sum(&nominator);\n                this->A->getManager()->global_reduce_sum(&denominator);\n            }\n\n            cudaCheckError();\n        }\n\n        if (types::util<ValueTypeB>::abs(denominator) == 0.0)\n        {\n            nominator = denominator = types::util<ValueTypeB>::get_one();\n        }\n\n        // apply correction x <- x + lambda*e\n        const int num_block_fine = std::min( AMGX_GRID_MAX_SIZE, (numRowsFine * blockdim - 1) / threads_per_block + 1 );\n        ValueTypeB alpha = nominator / denominator;\n\n        if ( types::util<ValueTypeB>::abs(alpha) < .3 )\n        {\n            alpha = (alpha / types::util<ValueTypeB>::abs(alpha)) * .3;    // it was this before: alpha = .3, which is not 100% equal\n        }\n\n        if ( types::util<ValueTypeB>::abs(alpha) > 10 )\n        {\n            alpha = (alpha / types::util<ValueTypeB>::abs(alpha)) * 10.;    // it was this before: alpha = 10., which is not 100% equal\n        }\n\n        applyCorrection <<< num_block_fine, threads_per_block, 0, stream>>>( alpha, ef.raw(), xf.raw(), numRowsFine * blockdim );\n        cudaCheckError();\n        this->scale_counter = this->reuse_scale; //reuse this scale scale_counter times\n        this->scale = alpha;\n        return;\n    }\n\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n    const int block_size = 128;\n    const int num_blocks = this->A->get_num_rows() / block_size + 1;\n    const IndexType *aggregates_ptr = this->m_aggregates.raw();\n    ValueTypeB *x_ptr = xf.raw();\n    const ValueTypeB *e_ptr = ec.raw();\n\n    if (this->m_error_scaling == 1)\n    {\n        FatalError(\"error_scaling=1 is deprecated\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    prolongateAndApplyCorrectionBlockDiaCsrKernel <<< num_blocks, block_size>>>(alpha, (int)this->A->get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates, this->A->get_block_dimy());\n    cudaCheckError();\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config >::prolongateAndApplyCorrection(VVector &e, VVector &bf, VVector &x, VVector &tmp)\n{\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    //this is dirty, but error scaling 2 and 3 do not have a specialized version. Instead, the general version sits in the 4x4 function\n    if ( this->m_error_scaling >= 2 )\n    {\n        prolongateAndApplyCorrection_4x4(e, bf, x, tmp);\n    }\n    else if (this->A->get_block_size() == 1)\n    {\n        prolongateAndApplyCorrection_1x1(e, bf, x, tmp);\n    }\n    else if (this->A->get_block_dimx() == this->A->get_block_dimy() )\n    {\n        prolongateAndApplyCorrection_4x4(e, bf, x, tmp);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n\n    if (!this->A->is_matrix_singleGPU() && x.delayed_send == 0)\n    {\n        if (x.in_transfer & RECEIVING) { this->A->manager->exchange_halo_wait(x, x.tag); }\n\n        this->A->manager->exchange_halo_async(x, x.tag);\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::restrictResidual(VVector &r, VVector &rr)\n{\n    if (this->A->get_block_size() == 1)\n    {\n        restrictResidual_1x1(r, rr);\n    }\n    else if (this->A->get_block_dimx() == this->A->get_block_dimy() )\n    {\n        restrictResidual_4x4(r, rr);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    //TODO: check level transfer between host and device for multiGPU\n    if (!this->A->is_matrix_singleGPU())\n    {\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n        rr.dirtybit = 1;\n\n        if (!Ac.is_matrix_singleGPU() && !this->isConsolidationLevel() && rr.delayed_send == 0)\n        {\n            Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA(); //TODO problem in memoryspace transfer is here\n\n            if (rr.in_transfer & RECEIVING) { Ac.manager->exchange_halo_wait(rr, rr.tag); }\n\n            Ac.manager->exchange_halo_async(rr, rr.tag);\n        }\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::computeRestrictionOperator()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeRestrictionOperator_1x1();\n    }\n    else if (this->A->get_block_dimx() == 4 && this->A->get_block_dimy() == 4)\n    {\n        computeRestrictionOperator_4x4();\n    }\n    else\n    {\n        this->computeRestrictionOperator_common();\n    }\n}\n\ntemplate <typename IndexType>\n__global__ void coarse_to_global(IndexType *aggregates, IndexType *aggregates_global, IndexType *renumbering, IndexType num_elements, int64_t offset)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < num_elements)\n    {\n        renumbering[aggregates[element]] = aggregates_global[element] + offset; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename T, typename IndexType>\n__global__ void export_matrix_elements(IndexType *row_offsets, IndexType *col_indices, T *values, IndexType *maps, IndexType *renumbering, IndexType *new_row_offsets, IndexType *new_col_indices, T *new_values, IndexType bsize, IndexType size)\n{\n    int idx = blockIdx.x * blockDim.x / 32 + threadIdx.x / 32;\n    int coopIdx = threadIdx.x % 32;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = new_row_offsets[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += 32)\n        {\n            new_values[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += 32)\n        {\n            new_col_indices[dst_base + m] = renumbering[col_indices[src_base + m]];\n        }\n\n        idx += gridDim.x * blockDim.x / 32;\n    }\n}\n\ntemplate <class T>\n__global__ void export_matrix_diagonal(T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, T *output, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row;\n        INDEX_TYPE dst_base = idx;\n\n        for (int m = 0; m < bsize; m++)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void remove_boundary(INDEX_TYPE *flags, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < size)\n    {\n        flags[maps[element]] = 0; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_inverse_renumbering(INDEX_TYPE *renum, INDEX_TYPE *irenum, INDEX_TYPE *renum_gbl, INDEX_TYPE base_index, INDEX_TYPE max_element)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < max_element)\n    {\n        irenum[renum[idx]] = renum_gbl[idx] - base_index;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void create_halo_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, INDEX_TYPE base_index, INDEX_TYPE map_offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = map_offset + row;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void map_col_indices_and_count_rowlen(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *row_length, INDEX_TYPE *renumbering, INDEX_TYPE *mapping, INDEX_TYPE *map_offsets, int64_t *index_ranges, INDEX_TYPE part_id, INDEX_TYPE my_id, INDEX_TYPE base_index, INDEX_TYPE my_range, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows)\n{\n    extern __shared__ volatile int reduction[];\n    int row = blockIdx.x * blockDim.x / 4 + threadIdx.x / 4;\n    int coopIdx = threadIdx.x % 4;\n\n    while (row < num_rows)\n    {\n        int valid = 0;\n\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += 4) //this may look horrible, but I expect low branch divergence, because col indices in a row usually belong to the same partition (or at most one more)\n        {\n            int colIdx = col_indices[idx];\n            int part = -2;\n\n            if (colIdx >= index_ranges[2 * part_id] && colIdx < index_ranges[2 * part_id + 1]) //the col index probably belongs to the partition I am working on\n            {\n                part = part_id;\n            }\n            else if (colIdx >= base_index && colIdx < base_index + my_range)     //or points back to the owned partition\n            {\n                part = -1;\n            }\n            else        //or else it points to a third partition\n            {\n                for (int i = 0; i < num_neighbors; i++)\n                {\n                    if (colIdx >= index_ranges[2 * i] && colIdx < index_ranges[2 * i + 1])\n                    {\n                        part = i;\n                    }\n                }\n            }\n\n            if (part == -2)\n            {\n                col_indices[idx] = -1;\n#ifdef DEBUG\n                printf(\"Column index encountered that does not belong to any of my neighbors!! %d\\n\", colIdx);\n#endif\n            }\n            else\n            {\n                if (part == -1)\n                {\n                    col_indices[idx] = renumbering[colIdx - base_index];\n                    valid++;\n                }\n                else\n                {\n                    int new_col_idx = mapping[map_offsets[part] + colIdx - index_ranges[2 * part]];\n\n                    if (new_col_idx >= 0)\n                    {\n                        valid++;\n                        col_indices[idx] = new_col_idx;\n                    }\n                    else\n                    {\n                        col_indices[idx] = -1;\n                    }\n                }\n            }\n        }\n\n        reduction[threadIdx.x] = valid;\n\n        for (int s = 2; s > 0; s >>= 1)\n        {\n            if (coopIdx < s)\n            {\n                reduction[threadIdx.x] += reduction[threadIdx.x + s];\n            }\n\n            __syncthreads();\n        }\n\n        if (coopIdx == 0)\n        {\n            row_length[row] = reduction[threadIdx.x];\n        }\n\n        row += gridDim.x * blockDim.x / 4;\n    }\n}\n\n__global__ void map_col_indices(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *halo_ranges, INDEX_TYPE *halo_renumbering, INDEX_TYPE *halo_rows, INDEX_TYPE *global_renumbering, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows, INDEX_TYPE num_rows_processed)\n{\n    int row = blockIdx.x * blockDim.x / 4 + threadIdx.x / 4;\n    int coopIdx = threadIdx.x % 4;\n\n    while (row < num_rows_processed)\n    {\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += 4)\n        {\n            int colIdx = col_indices[idx];\n            int part = 0;\n\n            if (colIdx < num_rows)\n            {\n                part = -1;\n            }\n            else\n            {\n                colIdx = global_renumbering[colIdx];\n\n                for (int i = 0; i < num_neighbors; i++)\n                {\n                    if (colIdx >= halo_ranges[2 * i] && colIdx < halo_ranges[2 * i + 1])\n                    {\n                        part = i;\n                        break;\n                    }\n                }\n            }\n\n            if (part == -1)\n            {\n                col_indices[idx] = colIdx;\n            }\n            else\n            {\n                col_indices[idx] = halo_renumbering[halo_rows[part] + colIdx - halo_ranges[2 * part]];\n            }\n        }\n\n        row += gridDim.x * blockDim.x / 4;\n    }\n}\n\ntemplate <class T>\n__global__ void reorder_whole_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE dst_row = row;\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst = rows[dst_row];\n\n        for (int i = 0; i < old_rows[row + 1] - src_base; i++)\n        {\n            INDEX_TYPE colIdx = old_cols[src_base + i];\n\n            if (colIdx >= 0)\n            {\n                cols[dst] = colIdx;\n\n                for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(src_base + i) * bsize + j]; }\n\n                dst++;\n            }\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_gbl_renumbering(INDEX_TYPE *inv_renum, INDEX_TYPE *gbl_renum, INDEX_TYPE size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        gbl_renum[inv_renum[idx]] = idx;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename ValueType>\n__global__ void write_diagonals(ValueType *values, INDEX_TYPE *diag, INDEX_TYPE *map, ValueType *output, INDEX_TYPE bsize, INDEX_TYPE size)\n{\n    int nzPerBlock = blockDim.x / bsize;\n    int row = blockIdx.x * nzPerBlock + threadIdx.x / bsize;\n    int vecIdx = threadIdx.x % bsize;\n\n    if (threadIdx.x >= (blockDim.x / bsize)*bsize) { return; }\n\n    while (row < size)\n    {\n        output[row * bsize + vecIdx] = values[diag[map[row]] * bsize + vecIdx];\n        row += gridDim.x * nzPerBlock;\n    }\n}\n\ntemplate <typename ValueType>\n__global__ void write_diagonals_back(ValueType *values, INDEX_TYPE *diag, ValueType *source, INDEX_TYPE bsize, INDEX_TYPE size)\n{\n    int nzPerBlock = blockDim.x / bsize;\n    int row = blockIdx.x * nzPerBlock + threadIdx.x / bsize;\n    int vecIdx = threadIdx.x % bsize;\n\n    if (threadIdx.x >= (blockDim.x / bsize)*bsize) { return; }\n\n    while (row < size)\n    {\n        values[diag[row]*bsize + vecIdx] = source[row * bsize + vecIdx];\n        row += gridDim.x * nzPerBlock;\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_full(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.get_num_rows();\n        int f_size = A.get_num_rows();\n        int diag = Ac.hasProps(DIAG);\n\n        if (A.manager->B2L_rings[0].size() > 2) { FatalError(\"Aggregation_AMG_Level prepareNextLevelMatrix not implemented >1 halo rings\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE); }\n\n        //get coarse -> fine global renumbering\n        IVector renumbering(c_size);\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), renumbering.raw(), f_size, 0);\n        cudaCheckError();\n        //\n        // Step 0 - form halo matrices that are exported to neighbors\n        //\n        std::vector<Matrix<TConfig> > halo_rows(num_neighbors);\n        std::vector<DistributedManager<TConfig> > halo_btl(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++ )\n        {\n            int num_unique = Ac.manager->B2L_rings[i][1];\n            //prepare export halo matrices\n            halo_btl[i].resize(1, 1);\n            halo_btl[i].set_global_id(Ac.manager->global_id());\n            halo_btl[i].B2L_maps[0].resize(num_unique);\n            halo_btl[i].B2L_rings[0].resize(2);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = num_unique;\n            halo_btl[i].set_index_range(A.manager->index_range());\n            halo_btl[i].set_base_index(A.manager->base_index());\n            //global indices of rows of the halo matrix\n            amgx::thrust::copy(amgx::thrust::make_permutation_iterator( renumbering.begin(), Ac.manager->B2L_maps[i].begin()),\n                         amgx::thrust::make_permutation_iterator( renumbering.begin(), Ac.manager->B2L_maps[i].begin() + num_unique),\n                         halo_btl[i].B2L_maps[0].begin());\n            cudaCheckError();\n            halo_rows[i].addProps(CSR);\n\n            if (diag) { halo_rows[i].addProps(DIAG); }\n\n            //calculate row length and row_offsets\n            halo_rows[i].row_offsets.resize(num_unique + 1);\n            thrust_wrapper::transform<TConfig::memSpace>(amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin() + 1, Ac.manager->B2L_maps[i].begin()),\n                              amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin() + 1, Ac.manager->B2L_maps[i].end()),\n                              amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin(), Ac.manager->B2L_maps[i].begin()),\n                              halo_rows[i].row_offsets.begin(),\n                              amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<TConfig::memSpace>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), halo_rows[i].row_offsets.begin());\n            cudaCheckError();\n            //resize halo matrix\n            IndexType num_nz = halo_rows[i].row_offsets[num_unique];\n            halo_rows[i].resize(num_unique, num_unique, num_nz, Ac.get_block_dimy(), Ac.get_block_dimx(), 1);\n            //copy relevant rows and renumber their column indices\n            num_blocks = std::min(4096, (num_unique + 127) / 128);\n            export_matrix_elements <<< num_blocks, 128>>>(Ac.row_offsets.raw(), Ac.col_indices.raw(), Ac.values.raw(), Ac.manager->B2L_maps[i].raw(), renumbering.raw(), halo_rows[i].row_offsets.raw(), halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(), A.get_block_size(), num_unique);\n            cudaCheckError();\n\n            if (diag)\n            {\n                export_matrix_diagonal <<< num_blocks, 128>>>(Ac.values.raw() + Ac.row_offsets[Ac.get_num_rows()]*Ac.get_block_size(), Ac.get_block_size(), Ac.manager->B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*Ac.get_block_size(), num_unique);\n                cudaCheckError();\n            }\n        }\n\n        Ac.manager->getComms()->exchange_matrix_halo(halo_rows, halo_btl, Ac);\n        //--------------------- renumbering/reordering matrix, integrating halo -----------------------------\n        Ac.set_initialized(0);\n        //number of owned rows\n        c_size = Ac.manager->halo_offsets[0];\n        f_size = A.manager->halo_offsets[0];\n        num_blocks = std::min(4096, (c_size + 511) / 512);\n        int rings = 1;\n        //\n        // Step 1 - calculate inverse renumbering (to global indices - base_index)\n        //\n        Ac.manager->inverse_renumbering.resize(c_size);\n        thrust_wrapper::transform<TConfig::memSpace>(renumbering.begin(),\n                          renumbering.begin() + c_size,\n                          amgx::thrust::constant_iterator<IndexType>(A.manager->base_index()),\n                          Ac.manager->inverse_renumbering.begin(),\n                          amgx::thrust::minus<IndexType>());\n        cudaCheckError();\n        //big renumbering table for going from global index to owned local index\n        IVector global_to_coarse_local(Ac.manager->index_range());\n        thrust_wrapper::fill<TConfig::memSpace>(global_to_coarse_local.begin(), global_to_coarse_local.begin() + Ac.manager->index_range(), -1);\n        cudaCheckError();\n        calc_gbl_renumbering <<< num_blocks, 512>>>(Ac.manager->inverse_renumbering.raw(), global_to_coarse_local.raw(), c_size);\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n        cudaCheckError();\n        //\n        // Step 2 - create big mapping table of all halo indices we received (this may use a little too much memory sum(fine nodes per neighbor)\n        //\n        amgx::thrust::host_vector<INDEX_TYPE> neighbor_rows(num_neighbors + 1);\n        int max_num_rows = 0;\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            neighbor_rows[i] = halo_rows[i].manager->index_range();\n            max_num_rows = max_num_rows > halo_rows[i].get_num_rows() ? max_num_rows : halo_rows[i].get_num_rows();\n        }\n\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows.begin());\n        cudaCheckError();\n        int total_rows_of_neighbors = neighbor_rows[num_neighbors];\n        IVector halo_mapping(total_rows_of_neighbors);\n        thrust_wrapper::fill<TConfig::memSpace>(halo_mapping.begin(), halo_mapping.end(), -1);\n        cudaCheckError();\n\n        for (int ring = 0; ring < rings; ring++)\n        {\n            for (int i = 0; i < num_neighbors; i++)\n            {\n                int size = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n                int num_blocks = std::min(4096, (size + 127) / 128);\n                create_halo_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                        halo_btl[i].B2L_maps[0].raw() + halo_btl[i].B2L_rings[0][ring],\n                        halo_btl[i].base_index(),\n                        Ac.manager->halo_offsets[ring * num_neighbors + i], size);\n                        cudaCheckError();\n            }\n        }\n\n        cudaCheckError();\n        //\n        // Step 3 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n        //\n        INDEX_TYPE owned_nnz = Ac.row_offsets[c_size];\n        IVector neighbor_rows_d(num_neighbors + 1);\n        amgx::thrust::copy(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows_d.begin());\n        cudaCheckError();\n        //map column indices of my own matrix (the ones that point outward)\n        map_col_indices <<< num_blocks, 512>>>(Ac.row_offsets.raw() + Ac.manager->num_interior_nodes(),\n                                               Ac.col_indices.raw(),\n                                               Ac.manager->halo_ranges.raw(),\n                                               halo_mapping.raw(),\n                                               neighbor_rows_d.raw(),\n                                               renumbering.raw(),\n                                               num_neighbors, c_size, c_size - Ac.manager->num_interior_nodes());\n        cudaCheckError();\n        IVector temp_row_len(max_num_rows);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            //map column indices of halo matrices\n            int size = halo_rows[i].get_num_rows();\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            map_col_indices_and_count_rowlen <<< num_blocks, 128, 128 * sizeof(INDEX_TYPE)>>>(\n                halo_rows[i].row_offsets.raw(),\n                halo_rows[i].col_indices.raw(),\n                temp_row_len.raw(),\n                global_to_coarse_local.raw(),\n                halo_mapping.raw(),\n                neighbor_rows_d.raw(),\n                Ac.manager->halo_ranges.raw(),\n                i,\n                Ac.manager->global_id(),\n                Ac.manager->base_index(),\n                Ac.manager->index_range(),\n                num_neighbors,\n                size);\n                cudaCheckError();\n\n            for (int ring = 0; ring < rings; ring++)\n            {\n                amgx::thrust::copy(temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring], temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring + 1], Ac.row_offsets.begin() + Ac.manager->halo_offsets[ring * num_neighbors + i]);\n            }\n        }\n\n        cudaCheckError();\n        INDEX_TYPE old_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(Ac.row_offsets.begin() + c_size, Ac.row_offsets.end(), Ac.row_offsets.begin() + c_size, owned_nnz);\n        cudaCheckError();\n        //\n        // Step 4 - consolidate column indices and values\n        //\n        int new_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n\n        Ac.col_indices.resize(new_nnz);\n        Ac.values.resize((new_nnz + 1 + diag * (Ac.row_offsets.size() - 2)) * A.get_block_size());\n\n        if (diag)\n        {\n            MVector diags(c_size * Ac.get_block_size());\n            amgx::thrust::copy(Ac.values.begin() + old_nnz * Ac.get_block_size(),\n                         Ac.values.begin() + old_nnz * Ac.get_block_size() + c_size * Ac.get_block_size(),\n                         diags.begin());\n            amgx::thrust::copy(diags.begin(), diags.begin() + c_size * Ac.get_block_size(),\n                         Ac.values.begin() + Ac.row_offsets[Ac.get_num_rows()]*Ac.get_block_size());\n            cudaCheckError();\n        }\n\n        int cumulative_num_rows = c_size;\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            for (int ring = 0; ring < rings; ring++)\n            {\n                int num_rows = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n                int num_blocks = std::min(4096, (num_rows + 127) / 128);\n                reorder_whole_matrix <<< num_blocks, 128>>>(halo_rows[i].row_offsets.raw() + halo_btl[i].B2L_rings[0][ring], halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(), Ac.row_offsets.raw() + Ac.manager->halo_offsets[ring * num_neighbors + i], Ac.col_indices.raw(), Ac.values.raw(), Ac.get_block_size(), num_rows);\n                cudaCheckError();\n\n                if (diag)\n                {\n                    amgx::thrust::copy(halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring])*Ac.get_block_size(),\n                                 halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring + 1])*Ac.get_block_size(),\n                                 Ac.values.begin() + (Ac.row_offsets[Ac.get_num_rows()] + cumulative_num_rows)*Ac.get_block_size());\n                    cumulative_num_rows += num_rows;\n                }\n            }\n        }\n\n        cudaCheckError();\n        Ac.set_num_cols(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1]);\n        Ac.set_num_rows(Ac.get_num_cols());\n        Ac.set_num_nz(new_nnz);\n        Ac.delProps(COO);\n        Ac.set_initialized(1);\n        Ac.computeDiagonal();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_diag(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.manager->halo_offsets[0];\n        int f_size = A.manager->halo_offsets[0];\n        int diag = Ac.hasProps(DIAG);\n        Ac.manager->inverse_renumbering.resize(c_size);\n        //get coarse -> fine renumbering\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), Ac.manager->inverse_renumbering.raw(), f_size, -1 * A.manager->base_index());\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n\n        if (!diag) { Ac.computeDiagonal(); }\n\n        Ac.set_initialized(1);\n        std::vector<MVector> diagonals(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = Ac.manager->B2L_rings[i][Ac.manager->B2L_rings.size() - 1];\n            diagonals[i].resize(Ac.get_block_size()*size);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_diagonals <<< num_blocks, 128>>>(Ac.values.raw(), Ac.diag.raw(), Ac.manager->B2L_maps[i].raw(), diagonals[i].raw(), Ac.get_block_size(), size);\n        }\n\n        cudaCheckError();\n        Ac.manager->getComms()->exchange_vectors(diagonals, Ac, this->tag * 100 + 10 + 2);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = Ac.manager->halo_offsets[i + 1] - Ac.manager->halo_offsets[i];\n\n            if (Ac.hasProps(DIAG)) { amgx::thrust::copy(diagonals[i].begin(), diagonals[i].begin() + Ac.get_block_size()*size, Ac.values.begin() + Ac.get_block_size() * (Ac.diagOffset() + Ac.manager->halo_offsets[i])); }\n            else\n            {\n                int num_blocks = std::min(4096, (size + 127) / 128);\n                write_diagonals_back <<< num_blocks, 128>>>(Ac.values.raw(), Ac.diag.raw() + Ac.manager->halo_offsets[i], diagonals[i].raw(), Ac.get_block_size(), size);\n                cudaCheckError();\n            }\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_none(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.manager->halo_offsets[0];\n        int f_size = A.manager->halo_offsets[0];\n        int diag = Ac.hasProps(DIAG);\n        Ac.manager->inverse_renumbering.resize(c_size);\n        //get coarse -> fine renumbering\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), Ac.manager->inverse_renumbering.raw(), f_size, 0);\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n        Ac.set_initialized(1);\n\n        if (!diag) { Ac.computeDiagonal(); }\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (m_matrix_halo_exchange == 0)\n    {\n        this->prepareNextLevelMatrix_none(A, Ac);\n    }\n    else if (m_matrix_halo_exchange == 1)\n    {\n        this->prepareNextLevelMatrix_diag(A, Ac);\n    }\n    else if (m_matrix_halo_exchange == 2)\n    {\n        this->prepareNextLevelMatrix_full(A, Ac);\n    }\n    else\n    {\n        FatalError(\"Invalid Aggregation matrix_halo_exchange parameter\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\n__global__ void set_halo_rowlen(INDEX_TYPE *work, INDEX_TYPE *output, INDEX_TYPE  size, INDEX_TYPE diag)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        if (work[idx + 1] - work[idx] > 0)\n        {\n            output[idx] += work[idx + 1] - work[idx] - (1 - diag);\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename T>\n__global__ void append_halo_nz(INDEX_TYPE *row_offsets, INDEX_TYPE *new_row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *new_col_indices, T *values, T *new_values, INDEX_TYPE size, INDEX_TYPE diag, INDEX_TYPE halo_offset, INDEX_TYPE block_size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int add_diag = !diag;\n\n        if (!diag && new_col_indices[new_row_offsets[idx]] != -1) { add_diag = 0; } //if diag or there is already soimething in the row, then don't add diagonal nonzero (inside diag)\n\n        int append_offset = -1;\n\n        for (int i = new_row_offsets[idx]; i < new_row_offsets[idx + 1]; i++)\n        {\n            if (new_col_indices[i] == -1) {append_offset = i; break;}\n        }\n\n        for (int i = row_offsets[idx]; i < row_offsets[idx + 1]; i++)\n        {\n            if (diag && i == row_offsets[idx])   //if outside diag and this is the first nonzero in a non-empty row, overwrite diagonal value\n            {\n                for (int j = 0; j < block_size; j++)\n                {\n                    new_values[(new_row_offsets[size] + halo_offset + idx)*block_size + j] = values[(row_offsets[size] + halo_offset + idx) * block_size + j];\n                }\n            }\n\n            int col_idx = col_indices[i];\n\n            if (append_offset == -1 && (col_idx != halo_offset + idx)) {printf(\"ERROR: append offset is -1 but row has nonzeros in it old %d to %d new %d to %d\\n\", row_offsets[idx], row_offsets[idx + 1], new_row_offsets[idx], new_row_offsets[idx + 1]); append_offset = 0;}\n\n            if (col_idx != halo_offset + idx || add_diag)\n            {\n                new_col_indices[append_offset] = col_idx;\n\n                for (int j = 0; j < block_size; j++)\n                {\n                    new_values[append_offset * block_size + j] = values[i * block_size + j];\n                }\n\n                append_offset++;\n            }\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseB2LMaps(std::vector<IVector> &in_coarse_B2L_maps)\n{\n    Matrix<TConfig> &A = this->getA();\n    m_num_all_aggregates = m_num_aggregates;\n    int num_neighbors = A.manager->neighbors.size();\n    IndexType max_b2l = 0;\n\n    for (int i = 0; i < num_neighbors; i++ ) { max_b2l = max_b2l > A.manager->B2L_rings[i][1] ? max_b2l : A.manager->B2L_rings[i][1]; }\n\n    IVector B2L_aggregates(max_b2l);\n    IVector indices(max_b2l);\n\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = A.manager->B2L_rings[i][1];\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + size, 0);\n        thrust_wrapper::sequence<TConfig::memSpace>(indices.begin(), indices.begin() + size);\n        //substitute coarse aggregate indices for fine boundary nodes\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin()),\n                     amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin() + size),\n                     B2L_aggregates.begin());\n        //find the unique ones\n        amgx::thrust::sort_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin());\n        IndexType num_unique = amgx::thrust::unique_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin()).first - B2L_aggregates.begin();\n        in_coarse_B2L_maps[i].resize(num_unique);\n        //sort it back so we have the original ordering\n        amgx::thrust::sort_by_key(indices.begin(), indices.begin() + num_unique, B2L_aggregates.begin());\n        amgx::thrust::copy(B2L_aggregates.begin(), B2L_aggregates.begin() + num_unique, in_coarse_B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n}\n\n\n__global__ void populate_coarse_boundary(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE *output, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        output[flags[maps[indices[idx]]]] = maps[indices[idx]];\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void flag_coarse_boundary(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[maps[indices[idx]]] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void flag_halo_indices(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE  offset, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[indices[idx] - offset] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void apply_halo_aggregate_indices(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *output, INDEX_TYPE offset, INDEX_TYPE aggregates_offset, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        output[idx] = flags[indices[idx] - offset] + aggregates_offset;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n// renumbering the aggregates/communicationg with neighbors\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::setNeighborAggregates()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    m_num_all_aggregates = m_num_aggregates;\n\n    /* WARNING: the matrix reordering always happens inside createRenumbering routine. There are three ways to get to this routine\n       1. matrix_upload_all -> uploadMatrix -> initializeUploadReorderAll -> reorder_matrix -> createRenumbering\n       2. read_system_distributed -> renumberMatrixOneRing -> reorder_matrix_owned -> createRenumbering\n       3. solver_setup -> ... -> AMG_Level::setup -> createCoarseMatrices -> setNeighborAggregates -> createRenumbering\n       If you are reading the renumbering from file you might need to add intercept code in if statement below,\n       otherwise this routine will exit before calling createRenumbering routine (in case of single or disjoint partitions).\n    */\n    if (this->getA().is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    //\n    // Step 0 - set up coarse matrix metadata\n    //\n    if (Ac.manager == NULL) { Ac.manager = new DistributedManager<T_Config>(); }\n\n    Ac.manager->resize(A.manager->neighbors.size(), 1);\n    Ac.manager->A = &Ac;\n    int f_size = A.get_num_rows();\n    Ac.manager->setComms(A.manager->getComms());\n    Ac.manager->set_global_id(A.manager->global_id());\n    Ac.manager->neighbors = A.manager->neighbors;\n    Ac.manager->set_base_index(A.manager->base_index());\n    Ac.manager->halo_ranges = A.manager->halo_ranges;\n    Ac.manager->set_index_range(A.manager->index_range());\n    //-------------------------------------- Section 1 - renumbering -----------------------------------------------------------\n    //\n    // Step 1 - calculate coarse level B2L maps - any aggregate that has a fine boundary node, becomes a coarse boundary node\n    //\n    m_num_all_aggregates = m_num_aggregates;\n    int vec_size = m_num_aggregates + 1; //A.manager->num_boundary_nodes()+1;\n    IVector B2L_aggregates(vec_size);\n\n    for (int i = 0; i < A.manager->neighbors.size(); i++)\n    {\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + vec_size, 0);\n        int size = A.manager->B2L_rings[i][1];\n        int block_size = 128;\n        int grid_size = std::min( 4096, ( size + block_size - 1 ) / block_size);\n        flag_coarse_boundary <<< grid_size, block_size>>>(B2L_aggregates.raw(), A.manager->B2L_maps[i].raw(), this->m_aggregates.raw(), size);\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + vec_size, B2L_aggregates.begin());\n        (Ac.manager->B2L_maps)[i].resize(B2L_aggregates[vec_size - 1]);\n        populate_coarse_boundary <<< grid_size, block_size>>>(B2L_aggregates.raw(), A.manager->B2L_maps[i].raw(), this->m_aggregates.raw(), Ac.manager->B2L_maps[i].raw(), size);\n    }\n\n    cudaCheckError();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        Ac.manager->B2L_rings[i].resize(2);\n        Ac.manager->B2L_rings[i][0] = 0;\n        Ac.manager->B2L_rings[i][1] = Ac.manager->B2L_maps[i].size();\n    }\n\n    DistributedArranger<T_Config> *prep = new DistributedArranger<T_Config>;\n    prep->initialize_B2L_maps_offsets(Ac, 1);\n    delete prep;\n    Ac.set_num_rows(m_num_aggregates);\n    IVector renumbering(m_num_aggregates + 1); /* +1 is actually not needed, it will be resized in createRenumbering */\n    Ac.manager->createRenumbering(renumbering);\n    //\n    // Step 2 - renumber aggregates, so boundary nodes will have higher index than interior ones (based on the renumberiong we have been calculating)\n    //\n    /* WARNING: 1. Thrust scatter and gather routines seem more appropriate here, but they implicitly assume that the input\n                and output have certain size correlation, which is not matched by vectors in our case. The only remaining option\n                is to use make_permutation as is done below. Example of Thrust scatter and gather calls\n                IVector ttt(f_size,-1);\n                amgx::thrust::scatter(this->m_aggregates.begin(), this->m_aggregates.begin()+f_size, renumbering.begin(), ttt.begin());\n                amgx::thrust::gather(renumbering.begin(), renumbering.end(), this->m_aggregates.begin(), ttt.begin());\n                amgx::thrust::copy(ttt.begin(), ttt.end(), this->m_aggregates.begin());\n\n                2. The original thrust composite call is illegal because it uses the same array (m_aggregates) for input and output.\n                amgx::thrust::copy(amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()),\n                amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()+f_size),\n                             this->m_aggregates.begin());\n                Although it somehow still works, it is much safer to use explicit temporary storage for the intermediate result.\n    */\n    /* WARNING: must save unreordered aggregates for later use before reordering them. */\n    IVector unreordered_aggregates(this->m_aggregates);\n    /* WARNING: change Thrust call to explicitly use temporary storage for the intermediate result. The earlier version is illegal, but somehow still works. */\n    IVector ttt(f_size, -1);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin() + f_size),\n                 ttt.begin());\n    amgx::thrust::copy(ttt.begin(), ttt.end(), this->m_aggregates.begin());\n    cudaCheckError();\n\n    //we don't need renumbering anymore, it will be identity on the coarse level\n\n    //-------------------------------------- Section 2 - communication -----------------------------------------------------------\n\n    //\n    // Step 3 - populate aggregates_fine_idx, which stores for every fine node the original global index of the aggregate (which is lowest global index of nodes aggregated together)\n    //\n\n    //\n    // These are different when we do /don't do matrix halo exchanges - when we do we need global indices to match nodes,\n    // and in this case Ac after computeA will not have the same ordering of halo nodes as after prepareNextLevel_full.\n    // However when we do not do matrix halo exchange we are only interested in the ordering of halo nodes on the coarse level,\n    // and we can get that by exchanging the (already renumbered) aggregates vector.\n    //\n    if (m_matrix_halo_exchange == 2)\n    {\n        //Find original global indices of nodes that have the minimum id in the aggregates.\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(A.manager->inverse_renumbering.begin(), this->m_aggregates_fine_idx.begin()),\n                     amgx::thrust::make_permutation_iterator(A.manager->inverse_renumbering.begin(), this->m_aggregates_fine_idx.begin() + f_size),\n                     this->m_aggregates_fine_idx.begin());\n        thrust_wrapper::transform<TConfig::memSpace>(this->m_aggregates_fine_idx.begin(),\n                          this->m_aggregates_fine_idx.begin() + f_size,\n                          amgx::thrust::constant_iterator<IndexType>(A.manager->base_index()),\n                          this->m_aggregates_fine_idx.begin(),\n                          amgx::thrust::plus<IndexType>());\n        //communicate\n        this->m_aggregates_fine_idx.set_block_dimx(1);\n        this->m_aggregates_fine_idx.set_block_dimy(1);\n        m_aggregates_fine_idx.dirtybit = 1;\n        A.manager->exchange_halo(m_aggregates_fine_idx, this->tag * 100 + 1 * 10 + 0);\n    }\n    else\n    {\n        //communicate\n        this->m_aggregates.set_block_dimx(1);\n        this->m_aggregates.set_block_dimy(1);\n        m_aggregates.dirtybit = 1;\n        /* WARNING: you should exchange unreordered aggregates, and append them to your own reordered aggregates, to conform to asusmptions done by distributed_mamanger. */\n        //A.manager->exchange_halo(m_aggregates, this->tag*100+1*10+0); //wrong\n        A.manager->exchange_halo(unreordered_aggregates, this->tag * 100 + 1 * 10 + 0);\n        amgx::thrust::copy(unreordered_aggregates.begin() + f_size, unreordered_aggregates.end(), this->m_aggregates.begin() + f_size);\n    }\n\n    cudaCheckError();\n    //\n    // Step 4 - consolidate neighbors' aggregates into own list to be able to perform Galerkin product with the n-ring halo\n    //\n    IVector &exchanged_aggregates = m_matrix_halo_exchange == 2 ? this->m_aggregates_fine_idx : this->m_aggregates;\n    int min_index = amgx::thrust::reduce(exchanged_aggregates.begin() + A.manager->halo_offsets[0], exchanged_aggregates.begin() + A.manager->halo_offsets[num_neighbors], (int)0xFFFFFFF, amgx::thrust::minimum<int>());\n    int max_index = amgx::thrust::reduce(exchanged_aggregates.begin() + A.manager->halo_offsets[0], exchanged_aggregates.begin() + A.manager->halo_offsets[num_neighbors], (int)0, amgx::thrust::maximum<int>());\n    cudaCheckError();\n    int s_size = max_index - min_index + 2;\n    IVector scratch(s_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = A.manager->halo_offsets[i + 1] - A.manager->halo_offsets[i];\n        //Could also use local minimums to perform the same operation. The results are the same.\n        //int min_local = amgx::thrust::reduce(exchanged_aggregates.begin()+A.manager->halo_offsets[i], exchanged_aggregates.begin()+A.manager->halo_offsets[i+1], (int)0xFFFFFFF, amgx::thrust::minimum<int>());\n        thrust_wrapper::fill<TConfig::memSpace>(scratch.begin(), scratch.begin() + s_size, 0);\n        int block_size = 128;\n        int grid_size = std::min( 4096, ( size + block_size - 1 ) / block_size);\n        flag_halo_indices <<< grid_size, block_size>>>(scratch.raw(), exchanged_aggregates.raw() + A.manager->halo_offsets[i], min_index /*min_local*/, size);\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(scratch.begin(), scratch.begin() + s_size, scratch.begin());\n        apply_halo_aggregate_indices <<< grid_size, block_size>>>(scratch.raw(), exchanged_aggregates.raw() + A.manager->halo_offsets[i], this->m_aggregates.raw() + A.manager->halo_offsets[i], min_index /*min_local*/, m_num_all_aggregates, size);\n        cudaCheckError();\n        Ac.manager->halo_offsets[i] = m_num_all_aggregates;\n        m_num_all_aggregates += scratch[s_size - 1];\n    }\n\n    cudaCheckError();\n    Ac.manager->halo_offsets[num_neighbors] = m_num_all_aggregates;\n}\n\n//TODO: The consolidate and unconsolidate parts could be made more efficient by only sending the\n//      nonzero values\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidateVector(VVector &x)\n{\n    int my_id = this->getA().manager->global_id();\n\n    if (this->getA().manager->isRootPartition())\n    {\n        // Here all partitions being consolidated should have same vector size, see TODO above\n        INDEX_TYPE num_parts = this->getA().manager->getNumPartsToConsolidate();\n\n        for (int i = 0; i < num_parts; i++)\n        {\n            int current_part = this->getA().manager->getPartsToConsolidate()[i];\n\n            // Vector has been set to correct size\n            if (current_part != my_id)\n            {\n                //printf(\"Root partition %d receiving %d -> %d and %d -> %d (total %d)\\n\", this->getA().manager->global_id(), this->getA().manager->getConsolidationArrayOffsets()[i], this->getA().manager->getConsolidationArrayOffsets()[i+1], this->getA().manager->getConsolidationArrayOffsets()[num_parts+i], this->getA().manager->getConsolidationArrayOffsets()[num_parts+i+1], (int)x.size()/x.get_block_size());\n                this->getA().manager->getComms()->recv_vector(x, current_part, 10000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[i + 1] - this->getA().manager->getConsolidationArrayOffsets()[i]));\n                this->getA().manager->getComms()->recv_vector(x, current_part, 20000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[num_parts + i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[num_parts + i + 1] - this->getA().manager->getConsolidationArrayOffsets()[num_parts + i]));\n            }\n        }\n    }\n    else\n    {\n        int my_destination_part = this->getA().manager->getMyDestinationPartition();\n        int i_off, i_size, b_off, b_size;\n        this->getA().manager->getConsolidationOffsets(&i_off, &i_size, &b_off, &b_size);\n        // Here all partitions being consolidated should have same vector size, see TODO above\n        this->getA().manager->getComms()->send_vector_async(x, my_destination_part, 10000 + my_id, i_off * x.get_block_size(), i_size * x.get_block_size());\n        this->getA().manager->getComms()->send_vector_async(x, my_destination_part, 20000 + my_id, b_off * x.get_block_size(), b_size * x.get_block_size());\n    }\n}\n\n//TODO: The consolidate and unconsolidate parts could be made more efficient by only sending the\n//      nonzero values\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::unconsolidateVector(VVector &x)\n{\n    if (this->getA().manager->isRootPartition())\n    {\n        INDEX_TYPE num_parts = this->getA().manager->getNumPartsToConsolidate();\n\n        for (int i = 0; i < num_parts; i++)\n        {\n            int current_part = this->getA().manager->getPartsToConsolidate()[i];\n\n            // Vector has been set to correct size\n            if (current_part != this->getA().manager->global_id())\n            {\n                this->getA().manager->getComms()->send_vector_async(x, current_part, 30000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[i + 1] - this->getA().manager->getConsolidationArrayOffsets()[i]));\n                this->getA().manager->getComms()->send_vector_async(x, current_part, 40000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[num_parts + i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[num_parts + i + 1] - this->getA().manager->getConsolidationArrayOffsets()[num_parts + i]));\n            }\n        }\n    }\n    else\n    {\n        int my_destination_part = this->getA().manager->getMyDestinationPartition();\n        // Vector x is of unknown size\n        int i_off, i_size, b_off, b_size;\n        this->getA().manager->getConsolidationOffsets(&i_off, &i_size, &b_off, &b_size);\n        this->getA().manager->getComms()->recv_vector(x, my_destination_part, 30000 + this->getA().manager->global_id(), i_off * x.get_block_size(), i_size * x.get_block_size());\n        this->getA().manager->getComms()->recv_vector(x, my_destination_part, 40000 + this->getA().manager->global_id(), b_off * x.get_block_size(), b_size * x.get_block_size());\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseVertices()\n{\n    //Set the aggregates\n    this->m_selector->setAggregates(this->getA(), this->m_aggregates, this->m_aggregates_fine_idx, this->m_num_aggregates);\n\n    if ( this->m_print_aggregation_info )\n    {\n        this->m_selector->printAggregationInfo( this->m_aggregates, this->m_aggregates_fine_idx, this->m_num_aggregates );\n    }\n\n    this->getA().template setParameter< int > (\"aggregates_num\", this->m_num_aggregates); // ptr to aaggregates\n}\n\n//  Creating the next level\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseMatrices()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0).\n                Notice that in aggregation path, prolongation P is implicit,\n                and is used through the aggregates array. */\n\n    bool const consolidation_level = !A.is_matrix_singleGPU() && this->isConsolidationLevel();\n\n    // bookkeeping for the coarse grid: renumber aggregates,\n    // if consolidation compute consolidated halo-offsets, etc\n    if (!this->isReuseLevel())\n    {\n        if (consolidation_level)\n        {\n            // Consolidation-path steps 1-9\n            this->consolidationBookKeeping();\n        }\n        else\n        {\n            this->setNeighborAggregates();\n        }\n    }\n\n    this->getA().setView(ALL);\n\n    // Compute restriction operator\n    // TODO: computing the restriction operator could be merged with the selector to save some work\n    // If we reuse the level we keep the previous restriction operator\n    if (this->isReuseLevel() == false)\n    {\n        computeRestrictionOperator();\n    }\n\n    Ac.set_initialized(0);\n    Ac.copyAuxData(&A);\n    this->m_coarseAGenerator->computeAOperator(A, Ac, this->m_aggregates, this->m_R_row_offsets, this->m_R_column_indices, this->m_num_all_aggregates);\n    Ac.setColsReorderedByColor(false);\n    Ac.setView(FULL);\n\n    if (consolidation_level)\n    {\n        // Consolidation-path Steps 11-12, send matrices to root, consolidate, final bookkeeping\n        this->consolidateCoarseGridMatrix();\n    }\n    else\n    {\n        this->prepareNextLevelMatrix(A, Ac);\n    }\n\n    A.setView(OWNED);\n    Ac.setView(OWNED);\n\n    this->m_next_level_size = this->m_num_all_aggregates * Ac.get_block_dimy();\n\n    if (this->m_print_aggregation_info)\n    {\n        MatrixAnalysis<TConfig> ana(&Ac);\n        ana.aggregatesQuality2(this->m_aggregates, this->m_num_aggregates, A);\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidationBookKeeping()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n\n    int num_parts, num_fine_neighbors, my_id;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        num_parts = A.manager->getComms()->get_num_partitions();\n        num_fine_neighbors = A.manager->neighbors.size();\n        my_id = A.manager->global_id();\n    }\n    else\n    {\n        num_parts = 1;\n        num_fine_neighbors = 0;\n        my_id = 0;\n    }\n\n    // ----------------------------------------------------\n    // Consolidate multiple fine matrices into one coarse matrix\n    // ----------------------------------------------------\n    // ----------------\n    // Step 1\n    // Decide which partitions should be merged together, store in destination_partitions vector\n    // ---------------\n    IVector_h &destination_part = A.manager->getDestinationPartitions();\n    int my_destination_part = A.manager->getMyDestinationPartition();\n\n    if (my_destination_part >= num_parts)\n    {\n        FatalError(\"During consolidation, sending data to partition that doesn't exist\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create mapping from coarse partition indices (ranks on the coarse consolidated level) to partition indices on the fine level (ranks on the fine level)\n    IVector_h coarse_part_to_fine_part = destination_part;\n    amgx::thrust::sort(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end());\n    cudaCheckError();\n    coarse_part_to_fine_part.erase(thrust::unique(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end()), coarse_part_to_fine_part.end());\n    cudaCheckError();\n    //Then, the number of coarse partitions is simply the size of this vector\n    int num_coarse_partitions = coarse_part_to_fine_part.size();\n    // Create mapping from fine partition indices to coarse partition indices, with fine partitions that are merging together having the same coarse indices\n    IVector_h fine_part_to_coarse_part(num_parts);\n    amgx::thrust::lower_bound(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end(), destination_part.begin(), destination_part.end(), fine_part_to_coarse_part.begin());\n    cudaCheckError();\n    // Create mapping from this specific partition's neighbors to consolidated coarse neighbors, but using their fine index (aka. destination partition indices for my neighbors)\n    IVector_h fine_neigh_to_fine_part;\n    A.manager->createNeighToDestPartMap(fine_neigh_to_fine_part, A.manager->neighbors, destination_part, num_fine_neighbors);\n    // Create mapping from consolidated coarse neighbors to fine partition indices (even if the current partition is not going to be a root)\n    IVector_h coarse_neigh_to_fine_part;\n    int num_coarse_neighbors;\n    A.manager->createConsolidatedNeighToPartMap(coarse_neigh_to_fine_part, fine_neigh_to_fine_part, my_destination_part, destination_part, num_coarse_neighbors);\n    // Create mapping from fine neighbors to coarse neighbors, with fine neighbors this partition is merging with labeled with -1\n    IVector_h fine_neigh_to_coarse_neigh;\n    A.manager->createNeighToConsNeigh(fine_neigh_to_coarse_neigh, coarse_neigh_to_fine_part, fine_neigh_to_fine_part, my_destination_part, num_fine_neighbors);\n    /*\n        EXAMPLE\n        Take the following partition graph (that describes connections between partitions, vertices are the partitions themselves), this is the same graph that is used in the setup example\n        number of partitions num_parts=12\n        CSR row_offsets [0 4 8 13 21 25 32 36 41 46 50 57 61]\n        CSR col_indices [0 1 3 8\n                    0 1 2 3\n                    1 2 3 4 5\n                    0 1 2 3 4 5 8 10\n                    2 4 5 6\n                    2 3 4 5 6 7 10\n                    4 5 6 7\n                    5 6 7 9 10\n                    0 3 8 10 11\n                    7 9 10 11\n                    3 5 7 8 9 10 11\n                    8 9 10 11]\n        destination_part = [0 0 0 0 4 4 4 4 8 8 8 8]\n        coarse_part_to_fine_part = [0 4 8] num_coarse_partitions = 3\n        fine_part_to_coarse_part = [0 0 0 0 1 1 1 1 2 2 2 2]\n        original neighbor lists correspond to the rows of the matrix, minus the diagonal elements: (part 0)[1 3 8] (part 3)[0 1 2 4 5 8 10] (part 10)[3 5 7 8 9 11]\n        fine_neigh_to_fine_part (part 0)[0 0 2] (part 3)[0 0 0 0 1 2 2] (part 10)[0 1 1 2 2 2]\n        coarse_neigh_to_fine_part (part 0)[8] (part 3)[4 8] (part 10)[0 4]\n        fine_neigh_to_coarse_neigh (part 0)[-1 -1 0] (part 3)[-1 -1 -1 0 0 1 1] (part 10)[0 1 1 -1 -1 -1]\n        */\n    // --------------------------\n    // Step 2\n    // Create coarse B2L_maps, by mapping fine B2L maps to coarse indices using this->m_aggregates and eliminating duplicates\n    // --------------------------\n    std::vector<IVector> coarse_B2L_maps(num_fine_neighbors);\n    m_num_all_aggregates = m_num_aggregates;\n    int num_neighbors_temp = A.manager->neighbors.size();\n    int num_rings = A.manager->B2L_rings[0].size() - 1;\n\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in consolidation\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IndexType max_b2l = 0;\n\n\n    for (int i = 0; i < num_neighbors_temp; i++ ) { max_b2l = max_b2l > A.manager->B2L_rings[i][1] ? max_b2l : A.manager->B2L_rings[i][1]; }\n\n    IVector B2L_aggregates(max_b2l);\n    IVector indices(max_b2l);\n\n    //TODO: use the algorithm from setNeighborAggregates()\n    for (int i = 0; i < num_neighbors_temp; i++ )\n    {\n        int size = A.manager->B2L_rings[i][1];\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + size, 0);\n        thrust_wrapper::sequence<TConfig::memSpace>(indices.begin(), indices.begin() + size);\n        //substitute coarse aggregate indices for fine boundary nodes\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin()),\n                        amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin() + size),\n                        B2L_aggregates.begin());\n        //find the unique ones\n        amgx::thrust::sort_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin());\n        IndexType num_unique = amgx::thrust::unique_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin()).first - B2L_aggregates.begin();\n        coarse_B2L_maps[i].resize(num_unique);\n        //sort it back so we have the original ordering\n        amgx::thrust::sort_by_key(indices.begin(), indices.begin() + num_unique, B2L_aggregates.begin());\n        amgx::thrust::copy(B2L_aggregates.begin(), B2L_aggregates.begin() + num_unique, coarse_B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n    /*\n        * EXAMPLE\n        say, partition 3 has the following coarse B2L_maps:\n        neighbors [0 1 2 4 5 8 10]\n        B2L_maps[0(=0)] = [6 7 8]\n        B2L_maps[1(=1)] = [8 9 10]\n        B2L_maps[2(=2)] = [10 11 12 13]\n        B2L_maps[3(=4)] = [13 14 15]\n        B2L_maps[4(=5)] = [15 16 17]\n        B2L_maps[5(=8)] = [6 18 19]\n        B2L_maps[6(=10)] = [17 20 19]\n        */\n    // ---------------------------------------------------\n    // Step 3\n    // create new B2L maps for each merged destination neighbor and drop B2L maps to neighbors we are merging with\n    // ---------------------------------------------------\n    std::vector<IVector> dest_coarse_B2L_maps;\n    A.manager->consolidateB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n    /*\n        * EXAMPLE\n        Then, merging the coarse B2L maps on partition 3, we get:\n        coarse_neigh_to_fine_part [4 8]\n        dest_coarse_B2L_maps[0(=4)] = [13 14 15 16 17]\n        dest_coarse_B2L_maps[1(=8)] = [6 17 18 19 20]\n        */\n    // -----------------------\n    // Step 4\n    // Create interior-boundary renumbering of aggregates according to dest_coarse_B2L_maps\n    // -----------------------\n    // Now renumber the aggregates with all interior aggregates first, boundary aggregates second\n    int num_interior_aggregates; //returned by createAggregatesRenumbering\n    int num_boundary_aggregates; //returned by createAggregatesRenumbering\n    IVector renumbering; //returned by createAggregatesRenumbering\n    // Following calls create renumbering array and modifies B2L_maps\n    A.manager->createAggregatesRenumbering(renumbering, dest_coarse_B2L_maps, this->m_num_aggregates, num_coarse_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n    /*\n        * EXAMPLE\n        Partition 3 will get a renumbering vector of size 21, for the 21 owned agggregates:\n        [0 1 2 3 4 5 17 6 7 8 9 10 11 12 13 14 15 16 18 19 20]\n        num_interior_aggregates = 12\n        num_boundary_aggregates = 9\n        */\n    // -------------------------------------------------\n    // Step 5\n    // Determine whether root partition, make list of partitions merged into one\n    // ------------------------------------------------\n    // Check if I'm root partition and how fine partitions (including myself) are merging into me\n    // bool is_root_partition = false;\n    bool &is_root_partition = this->m_is_root_partition;\n    is_root_partition = false; \n    int num_fine_parts_to_consolidate = 0;\n    // IVector_h fine_parts_to_consolidate;\n    IVector_h &fine_parts_to_consolidate = this->m_fine_parts_to_consolidate;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            is_root_partition = true;\n            num_fine_parts_to_consolidate++;\n        }\n    }\n\n    fine_parts_to_consolidate.resize(num_fine_parts_to_consolidate);\n    int count = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            fine_parts_to_consolidate[count] = i;\n            count++;\n        }\n    }\n\n    //save this information as state, as this will also be required during solve for restriction/prolongation\n    A.manager->setIsRootPartition(is_root_partition);\n    A.manager->setNumPartsToConsolidate(num_fine_parts_to_consolidate);\n    A.manager->setPartsToConsolidate(fine_parts_to_consolidate);\n\n    // Create a new distributed communicator for coarse levels that only contains active partitions\n    if (Ac.manager == NULL)\n    {\n        Ac.manager = new DistributedManager<TConfig>();\n    }\n\n    Ac.manager->setComms(A.manager->getComms()->Clone());\n    Ac.manager->getComms()->createSubComm(coarse_part_to_fine_part, is_root_partition);\n\n\n    /*\n        * EXAMPLE\n        isRootPartition is true for partitions 0,4,8 false for others\n        num_fine_parts_to_consolidate = 4 for partitions 0,4,8\n        fine_parts_to_consolidate (part 0)[0 1 2 3] (part 4)[4 5 6 7] (part 8)[8 9 10 11]\n        */\n    // ----------------------\n    // Step 6\n    // Compute number of interior, boundary and total nodes in the consolidated coarse matrix. Create offsets so that partitions being merged together will have their aggregate indices ordered like this:\n    // [num_interior(fine_parts_to_consolidate[0]] num_interior(fine_parts_to_consolidate[1]] ... num_interior(fine_parts_to_consolidate[num_fine_parts_to_consolidate]\n    //        num_boundary(fine_parts_to_consolidate[0]] num_boundary(fine_parts_to_consolidate[1]] ... num_boundary(fine_parts_to_consolidate[num_fine_parts_to_consolidate] ]\n    // ----------------------\n    // Gather to get number of interior/boundary aggregates of neighbors I will merge with\n    // std::vector<IVector_h> vertex_counts;\n    std::vector<IVector_h> &vertex_counts = this->m_vertex_counts;\n    // int interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged;\n    int interior_offset, boundary_offset;\n    int &total_interior_rows_in_merged = this->m_total_interior_rows_in_merged;\n    int &total_boundary_rows_in_merged = this->m_total_boundary_rows_in_merged;\n    int total_rows_in_merged;\n    //Computes these offsets on the root, sends them back\n    A.manager->computeConsolidatedOffsets(my_id, my_destination_part, is_root_partition, num_interior_aggregates, num_boundary_aggregates, vertex_counts, fine_parts_to_consolidate, num_fine_parts_to_consolidate, interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged, total_rows_in_merged, A.manager->getComms());\n    //Partitions save these offsets, as it will be required during solve restriction/prolongation\n    A.manager->setConsolidationOffsets(interior_offset, num_interior_aggregates, boundary_offset + num_interior_aggregates, num_boundary_aggregates);\n    /*\n        * EXAMPLE\n        For root partition 0, say we have the following interior/boundary counts (note that partition 1 has 0 boundary, as it is only connected to partitions it is merging with)\n        part 0 - interior: 10 boundary 3\n        part 1 - interior: 18\n        part 2 - interior: 10 boundary 16\n        part 3 - interior: 12 boundary 9\n        interior_offset for partitions 0,1,2,3: 0 10 28 38 (total_interior_rows_in_merged 50)\n        boundary_offset for partitions 0,1,2,3: 0 3 3 19 (total_boundary_rows_in_merged 28)\n        */\n    // ----------------------\n    // Step 7\n    // Each partition renumbers its aggregates and dest_coarse_B2L_maps using offsets computed in Step 6 and permutation in Step 4\n    // ----------------------\n    // Kernel to renumber the aggregates\n    int block_size = 128;\n    int grid_size = std::min( 4096, ( A.manager->halo_offsets[0] + block_size - 1 ) / block_size);\n    renumberAggregatesKernel <<< grid_size, block_size >>>(renumbering.raw(), interior_offset, boundary_offset, this->m_aggregates.raw(), A.manager->halo_offsets[0], num_interior_aggregates, renumbering.size());\n    cudaCheckError();\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        thrust_wrapper::transform<TConfig::memSpace>(dest_coarse_B2L_maps[i].begin(),\n                            dest_coarse_B2L_maps[i].end(),\n                            amgx::thrust::constant_iterator<IndexType>(boundary_offset),\n                            dest_coarse_B2L_maps[i].begin(),\n                            amgx::thrust::plus<IndexType>());\n    }\n\n    cudaCheckError();\n    /*\n        * EXAMPLE\n        Partition 3 had a renumbering vector:\n        [0 1 2 3 4 5 17 6 7 8 9 10 11 12 13 14 15 16 18 19 20]\n        which is now adjusted to account for the consolidated coarse matrices' indices:\n        [38 39 40 41 42 43 74 44 45 46 47 48 49 69 70 71 72 73 75 76 77]\n        And the dest_coarse_B2L_maps, which looked like:\n        dest_coarse_B2L_maps[0(=4)] = [13 14 15 16 17]\n        dest_coarse_B2L_maps[1(=8)] = [6 17 18 19 20]\n        is now:\n        dest_coarse_B2L_maps[0(=4)] = [69 70 71 72 73]\n        dest_coarse_B2L_maps[1(=8)] = [74 73 75 76 77]\n        */\n    // -------------------------------------------------\n    // Step 8\n    // Send dest_coarse_B2L_maps to root partitions\n    // ------------------------------------------------\n    // Each fine partition sends to its root the number of coarse neighbors it has, their ids, and the number of boundary nodes for each coarse neighbor\n    IVector_h num_bdy_per_coarse_neigh(num_coarse_neighbors);\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        num_bdy_per_coarse_neigh[i] = dest_coarse_B2L_maps[i].size();\n    }\n\n    IVector_h consolidated_coarse_neigh_to_fine_part; //consolidated list of coarse neighbors for the root partition, using fine partition indices\n    int num_consolidated_neighbors = 0;\n    // std::vector<IVector> consolidated_B2L_maps; //concatenates dest_coarse_B2L_maps received from partitions that are merging into the same root and pointing to the same destination coarse neighbor\n    std::vector<IVector> &consolidated_B2L_maps = this->m_consolidated_B2L_maps;\n    A.manager->consolidateB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_neigh_to_fine_part, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, A.manager->getComms());\n    //\n    // Step 9 - figuring out halo aggregate IDs\n    //\n    //Now we need to update halo aggregate IDs - this is just a halo exchange on this->m_aggregates between partitions\n    //that are being merged together, but we need to send other halos to the root to come up with the halo renumbering\n    //TODO: separate transactions, send \"real halo\" to the root nodes (coarse neighbors) immediately\n    //Step 9.1: takes care of synchronizing the aggregate IDs between partitions we are merging together and got consistent halo aggregate IDs for neighbor we are not merging with (which are going to be sent to the root in 9.2)\n    A.manager->exchange_halo(this->m_aggregates, 6666);\n    /*\n        * EXAMPLE 2\n        This example is independent from the previous ones.\n        Say partition 0 and 1 are merging (into 0) partition 0 is neighbors with 1,2,3 and partition 1 is neighbors with 0,3,4\n        Partitions 3 and 4 are merging (into partition 3) and partition 2 is not merging with anyone.\n        This example details the renumbering of halo indices on partition 0 and partition 1.\n        After the exchange halo, we have:\n        this->m_aggregates on partition 0:\n        [(fine interior nodes) (fine boundary nodes) (fine halo from part 1) (fine halo from part 2) (fine halo from part 3)]\n        [(fine interior nodes) (fine boundary nodes) (13 13 15) (12 15 17) (14 16 18)]\n        aggregates on partition 1:\n        [(fine interior nodes) (fine boundary nodes) (fine halo from part 0) (fine halo from part 3) (fine halo from part 4)]\n        [(fine interior nodes) (fine boundary nodes) (14 16 17) (18 19 19) (15 15 17)]\n        indices in  (fine halo from part 0) and (fine halo from part 1) actually contain interior aggregate indices (if they are not connected to partitions 2,3 or 4), because the boundary is disappearing there.\n        Indices in halo regions contain remote-local indices.\n\n        This example is used throughout consolidateAndRenumberHalos\n        */\n    //Step 9.2 - 9.5\n    // IVector_h halo_offsets(num_consolidated_neighbors + 1, 0);\n    IVector_h &halo_offsets = this->m_consolidated_halo_offsets;\n    halo_offsets = IVector_h(num_consolidated_neighbors + 1, 0);\n    A.manager->consolidateAndRenumberHalos(this->m_aggregates, A.manager->halo_offsets, halo_offsets, A.manager->neighbors, num_fine_neighbors, consolidated_coarse_neigh_to_fine_part, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, this->m_num_all_aggregates, A.manager->getComms());\n\n    if (is_root_partition)\n    {\n        for (int i = 0; i < consolidated_B2L_maps.size(); i++)\n        {\n            amgx::thrust::sort(consolidated_B2L_maps[i].begin(), consolidated_B2L_maps[i].end());\n        }\n\n        this->m_consolidated_neighbors.resize(num_consolidated_neighbors);\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            this->m_consolidated_neighbors[i] = fine_part_to_coarse_part[consolidated_coarse_neigh_to_fine_part[i]];\n        }\n            \n        cudaCheckError();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidateCoarseGridMatrix()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    int my_id = A.manager->global_id();\n        \n    IVector_h &destination_part = A.manager->getDestinationPartitions();\n    int my_destination_part = A.manager->getMyDestinationPartition();\n\n    // bookkeeping stored in AMG_Level_Base\n    std::vector<IVector_h> &vertex_counts = this->m_vertex_counts;\n    IVector_h &fine_parts_to_consolidate  = this->m_fine_parts_to_consolidate;\n\n    // bookkeeping stored in either AMG_Level_Base or Acs' DistributedManager\n    IVector_h &halo_offsets                     = this->isReuseLevel() ? Ac.manager->getHaloOffsets() : this->m_consolidated_halo_offsets;\n    std::vector<IVector> &consolidated_B2L_maps = this->isReuseLevel() ? Ac.manager->getB2Lmaps()     : this->m_consolidated_B2L_maps;\n\n    int num_consolidated_neighbors = this->isRootPartition() ? this->m_consolidated_neighbors.size() : 0;\n\n    if (!this->isRootPartition())\n    {\n        A.manager->getComms()->send_vector_async(Ac.row_offsets, my_destination_part, 1111);\n        A.manager->getComms()->send_vector_async(Ac.col_indices, my_destination_part, 1112);\n        A.manager->getComms()->send_vector_async(Ac.values, my_destination_part, 1113);\n    }\n    else\n    {\n        int num_fine_parts_to_consolidate = fine_parts_to_consolidate.size();\n\n        int total_num_rows = this->m_num_all_aggregates;\n        IVector new_row_offsets(total_num_rows + 1, 0);\n\n        //if diags are inside then we won't be counting those twice when computing halo row length\n        if (!Ac.hasProps(DIAG))\n        {\n            thrust_wrapper::fill<TConfig::memSpace>(new_row_offsets.begin() + halo_offsets[0], new_row_offsets.begin() + halo_offsets[num_consolidated_neighbors], 1);\n            cudaCheckError();\n        }\n\n        std::vector<IVector> recv_row_offsets(num_fine_parts_to_consolidate);\n        std::vector<VecInt_t> num_nz(num_fine_parts_to_consolidate);\n        IVector *work_row_offsets;\n        std::vector<VecInt_t> index_offset_array(2 * num_fine_parts_to_consolidate + 1);\n        int interior_offset = 0;\n        int boundary_offset = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            boundary_offset += vertex_counts[i][0];\n        }\n\n        int max_num_nz = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            //receive row offsets\n            if (current_part != my_id)\n            {\n                recv_row_offsets[i].resize(total_num_rows + 1);\n                A.manager->getComms()->recv_vector(recv_row_offsets[i], current_part, 1111);\n                work_row_offsets = &(recv_row_offsets[i]);\n                num_nz[i] = (*work_row_offsets)[work_row_offsets->size() - 1];\n                max_num_nz = max_num_nz > num_nz[i] ? max_num_nz : num_nz[i];\n            }\n            else\n            {\n                work_row_offsets = &(Ac.row_offsets);\n                num_nz[i] = Ac.get_num_nz();\n            }\n\n            //Get interior row length\n            thrust_wrapper::transform<TConfig::memSpace>(work_row_offsets->begin() + interior_offset + 1,\n                                work_row_offsets->begin() + interior_offset + vertex_counts[i][0] + 1,\n                                work_row_offsets->begin() + interior_offset,\n                                new_row_offsets.begin() + interior_offset,\n                                amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            //Get boundary row length\n            thrust_wrapper::transform<TConfig::memSpace>(work_row_offsets->begin() + boundary_offset + 1,\n                                work_row_offsets->begin() + boundary_offset + vertex_counts[i][1] + 1,\n                                work_row_offsets->begin() + boundary_offset,\n                                new_row_offsets.begin() + boundary_offset,\n                                amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            //Increment halo row length by one for every nonzero that is an edge from the halo into this partition\n            int size = halo_offsets[num_consolidated_neighbors] - halo_offsets[0];\n            const int block_size = 128;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n            set_halo_rowlen <<< num_blocks, block_size>>>(work_row_offsets->raw() + halo_offsets[0], new_row_offsets.raw() + halo_offsets[0], size, Ac.hasProps(DIAG));\n            cudaCheckError();\n            index_offset_array[i] = interior_offset;\n            index_offset_array[num_fine_parts_to_consolidate + i] = boundary_offset;\n            interior_offset += vertex_counts[i][0];\n            boundary_offset += vertex_counts[i][1];\n            index_offset_array[i + 1] = interior_offset;\n            index_offset_array[num_fine_parts_to_consolidate + i + 1] = boundary_offset;\n        }\n\n        A.manager->setConsolidationArrayOffsets(index_offset_array);\n        //Exclusive scan row length array to get row offsets\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(new_row_offsets.begin(), new_row_offsets.end(), new_row_offsets.begin());\n        cudaCheckError();\n        //Prepare to receive column indices and values\n        int num_nz_consolidated = new_row_offsets[new_row_offsets.size() - 1];\n        IVector recv_col_indices(max_num_nz);\n        IVector new_col_indices(num_nz_consolidated);\n        MVector recv_values((max_num_nz + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n        MVector new_values((num_nz_consolidated + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n        thrust_wrapper::fill<TConfig::memSpace>(new_col_indices.begin() + new_row_offsets[halo_offsets[0]], new_col_indices.end(), -1); //Set all the halo col indices to -1\n\n\n        if (!Ac.hasProps(DIAG)) { thrust_wrapper::fill<TConfig::memSpace>(new_values.begin() + num_nz_consolidated * Ac.get_block_size(), new_values.end(), types::util<ValueTypeA>::get_zero()); }\n\n        cudaCheckError();\n        IVector *work_col_indices;\n        MVector *work_values;\n        interior_offset = 0;\n        boundary_offset = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            boundary_offset += vertex_counts[i][0];\n        }\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                A.manager->getComms()->recv_vector(recv_col_indices, current_part, 1112, 0, num_nz[i]);\n                A.manager->getComms()->recv_vector(recv_values, current_part, 1113, 0, (num_nz[i] + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n                work_col_indices = &(recv_col_indices);\n                work_row_offsets = &(recv_row_offsets[i]);\n                work_values = &(recv_values);\n            }\n            else\n            {\n                work_row_offsets = &(Ac.row_offsets);\n                work_col_indices = &(Ac.col_indices);\n                work_values = &(Ac.values);\n            }\n\n            //Put interior rows in place\n            amgx::thrust::copy(work_col_indices->begin() + (*work_row_offsets)[interior_offset],\n                            work_col_indices->begin() + (*work_row_offsets)[interior_offset + vertex_counts[i][0]],\n                            new_col_indices.begin() + new_row_offsets[interior_offset]);\n            cudaCheckError();\n            amgx::thrust::copy(work_values->begin() + (*work_row_offsets)[interior_offset]*Ac.get_block_size(),\n                            work_values->begin() + ((*work_row_offsets)[interior_offset + vertex_counts[i][0]])*Ac.get_block_size(),\n                            new_values.begin() + new_row_offsets[interior_offset]*Ac.get_block_size());\n            cudaCheckError();\n            //Put boundary rows in place\n            amgx::thrust::copy(work_col_indices->begin() + (*work_row_offsets)[boundary_offset],\n                            work_col_indices->begin() + (*work_row_offsets)[boundary_offset + vertex_counts[i][1]],\n                            new_col_indices.begin() + new_row_offsets[boundary_offset]);\n            cudaCheckError();\n            amgx::thrust::copy(work_values->begin() + (*work_row_offsets)[boundary_offset]*Ac.get_block_size(),\n                            work_values->begin() + ((*work_row_offsets)[boundary_offset + vertex_counts[i][1]])*Ac.get_block_size(),\n                            new_values.begin() + new_row_offsets[boundary_offset]*Ac.get_block_size());\n            cudaCheckError();\n            //Process halo rows (merge)\n            int size = halo_offsets[num_consolidated_neighbors] - halo_offsets[0];\n            const int block_size = 128;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n            //TODO: vectorise this kernel, will be inefficient for larger block sizes\n            append_halo_nz <<< num_blocks, block_size>>>(work_row_offsets->raw() + halo_offsets[0],\n                    new_row_offsets.raw() + halo_offsets[0],\n                    work_col_indices->raw(),\n                    new_col_indices.raw(),\n                    work_values->raw(),\n                    new_values.raw(),\n                    size, Ac.hasProps(DIAG), halo_offsets[0], Ac.get_block_size());\n            cudaCheckError();\n\n            // Diagonals\n            if (Ac.hasProps(DIAG))\n            {\n                // Diagonal corresponding to interior rows\n                amgx::thrust::copy(work_values->begin() + (num_nz[i] + interior_offset)*Ac.get_block_size(),\n                                work_values->begin() + (num_nz[i] + interior_offset + vertex_counts[i][0])*Ac.get_block_size(),\n                                new_values.begin() + (new_row_offsets[halo_offsets[halo_offsets.size() - 1]] + interior_offset)*Ac.get_block_size());\n                // Diagonal corresponding to boundary rows\n                amgx::thrust::copy(work_values->begin() + (num_nz[i] + boundary_offset)*Ac.get_block_size(),\n                                work_values->begin() + (num_nz[i] + boundary_offset + vertex_counts[i][1])*Ac.get_block_size(),\n                                new_values.begin() + (new_row_offsets[halo_offsets[halo_offsets.size() - 1]] + boundary_offset)*Ac.get_block_size());\n                cudaCheckError();\n            }\n\n            interior_offset += vertex_counts[i][0];\n            boundary_offset += vertex_counts[i][1];\n        }\n\n        Ac.set_initialized(0);\n        Ac.row_offsets = new_row_offsets;\n        Ac.col_indices = new_col_indices;\n        Ac.values = new_values;\n    }\n\n    // A new distributed communicator for coarse levels that only contains active partitions\n    // has already been created in consolidatedBookKeeping!\n\n    //\n    // Step 12 - finalizing, bookkeping\n    //\n    if (this->isRootPartition())\n    {\n        // int my_consolidated_id = fine_part_to_coarse_part[my_id];\n        int my_consolidated_id = Ac.manager->getComms()->get_global_id();\n\n        if (!this->isReuseLevel())\n        {\n             Ac.manager->initializeAfterConsolidation(\n                 my_consolidated_id,\n                 Ac,\n                this->m_consolidated_neighbors,\n                this->m_total_interior_rows_in_merged,\n                this->m_total_boundary_rows_in_merged,\n                this->m_num_all_aggregates,\n                this->m_consolidated_halo_offsets,\n                this->m_consolidated_B2L_maps,\n                1,\n                true);\n\n            // this is now stored in Acs DistributedManager\n            this->m_consolidated_neighbors.resize(0);\n            this->m_consolidated_halo_offsets.resize(0);\n            this->m_consolidated_B2L_maps.resize(0);\n\n            Ac.manager->B2L_rings.resize(num_consolidated_neighbors + 1);\n\n            for (int i = 0; i < num_consolidated_neighbors; i++)\n            {\n                Ac.manager->B2L_rings[i].resize(2);\n                Ac.manager->B2L_rings[i][0] = 0;\n                Ac.manager->B2L_rings[i][1] = consolidated_B2L_maps[i].size();\n            }\n        }\n\n        Ac.manager->set_initialized(Ac.row_offsets);\n        Ac.manager->getComms()->set_neighbors(num_consolidated_neighbors);\n        int new_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n\n        Ac.set_num_nz(new_nnz);\n        Ac.set_num_cols(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1]);\n        Ac.set_num_rows(Ac.get_num_cols());\n\n        if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n        Ac.computeDiagonal();\n        Ac.set_initialized(1);\n    }\n    else\n    {\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.row_offsets);\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.col_indices);\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.values);\n\n        Ac.set_initialized(0);\n        // set size of Ac to be zero\n        Ac.resize(0, 0, 0, 1);\n        Ac.set_initialized(1);\n    }\n}\n\n// -------------------------------------------------------------\n// Explicit instantiations\n// -------------------------------------------------------------\n\n#define AMGX_CASE_LINE(CASE) template class Aggregation_AMG_Level<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/aggregation_amg_level.h>\n#include <matrix_analysis.h>\n\n#ifdef _WIN32\n#pragma warning (push)\n#pragma warning (disable : 4244 4267 4521)\n#endif\n#ifdef _WIN32\n#pragma warning (pop)\n#endif\n\n#include <basic_types.h>\n#include <util.h>\n#include <fstream>\n#include <cutil.h>\n#include <multiply.h>\n#include <transpose.h>\n#include <blas.h>\n#include <string>\n#include <string.h>\n#include <iostream>\n#include <algorithm>\n#include <amgx_timer.h>\n\n#include <amgx_types/util.h>\n\n#include <thrust/sort.h>\n#include <thrust/remove.h>\n#include <thrust/transform.h>\n#include <thrust/binary_search.h>\n#include <thrust/unique.h>\n#include <thrust/inner_product.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\n\n// ----------------------\n// Kernels\n// ----------------------\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid set_to_one_kernel(IndexType start, IndexType end, IndexType *ind, ValueType *v)\n{\n    for (int tid = start + blockDim.x * blockIdx.x + threadIdx.x; tid < end; tid += gridDim.x * blockDim.x)\n    {\n        v[ind[tid]] = types::util<ValueType>::get_one();\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid renumberAggregatesKernel(const IndexType *renumbering, const int interior_offset, const int bdy_offset, IndexType *aggregates, const int num_aggregates, const int n_interior, const int renumbering_size)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (tid < num_aggregates)\n    {\n        IndexType new_agg_id;\n\n        if (renumbering_size == 0)\n        {\n            new_agg_id = aggregates[tid];\n        }\n        else\n        {\n            new_agg_id = renumbering[aggregates[tid]];\n        }\n\n        //if (aggregates[tid] > num_aggregates)\n        //{\n        //printf(\"ID %d old %d + %d = %d\\n\", tid, new_agg_id, ((new_agg_id >= n_interior) ? bdy_offset : interior_offset), new_agg_id + ((new_agg_id >= n_interior) ? bdy_offset : interior_offset));\n        //}\n        new_agg_id +=  ((new_agg_id >= n_interior) ? bdy_offset : interior_offset);\n        aggregates[tid] = new_agg_id;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to restrict residual using csr_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid restrictResidualKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *r, ValueType *rr, const int num_aggregates)\n{\n    int jmin, jmax;\n\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_aggregates; tid += gridDim.x * blockDim.x)\n    {\n        ValueType temp(types::util<ValueType>::get_zero());\n        jmin = row_offsets[tid];\n        jmax = row_offsets[tid + 1];\n\n        for (int j = jmin; j < jmax; j++)\n        {\n            int j_col = column_indices[j];\n            temp = temp + r[j_col];\n        }\n\n        rr[tid] = temp;\n    }\n}\n\n// Kernel to restrict residual using block_dia_csr_format\ntemplate <typename IndexType, typename ValueType, int bsize>\n__global__\nvoid restrictResidualBlockDiaCsrKernel(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *r, ValueType *rr, const int num_aggregates)\n{\n    ValueType rr_temp[bsize];\n    int offset, jmin, jmax;\n\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_aggregates; tid += gridDim.x * blockDim.x)\n    {\n        // Initialize to zero\n#pragma unroll\n        for (int m = 0; m < bsize; m++)\n        {\n            rr_temp[m] = types::util<ValueType>::get_zero();\n        }\n\n        jmin = row_offsets[tid];\n        jmax = row_offsets[tid + 1];\n\n        for (int j = jmin; j < jmax; j++)\n        {\n            int jcol = column_indices[j];\n            offset = jcol * bsize;\n#pragma unroll\n\n            for (int m = 0; m < bsize; m++)\n            {\n                rr_temp[m] = rr_temp[m] + r[offset + m];\n            }\n        }\n\n        offset = tid * bsize;\n#pragma unroll\n\n        for (int m = 0; m < bsize; m++)\n        {\n            rr[offset + m] = rr_temp[m];\n        };\n    }\n}\n\n// Kernel to prolongate and apply the correction for csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateAndApplyCorrectionKernel(const ValueType alpha, const int num_rows, ValueType *x, const ValueType *e, const IndexType *aggregates, IndexType num_aggregates)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        IndexType I = aggregates[tid];\n        x[tid] = x[tid] + alpha * e[I];\n    }\n}\n\n// Kernel to prolongate and apply the correction for block-dia-csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateAndApplyCorrectionBlockDiaCsrKernel(const ValueType alpha, const int num_block_rows, ValueType *x, const ValueType *e, const IndexType *aggregates, IndexType num_aggregates, const int bsize)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_block_rows; tid += gridDim.x * blockDim.x)\n    {\n        IndexType I = aggregates[tid];\n\n        for (int  m = 0; m < bsize; m++)\n        {\n            x[tid * bsize + m] = x[tid * bsize + m] + alpha * e[I * bsize + m];\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid prolongateVector(const IndexType *aggregates, const ValueType *in, ValueType *out, IndexType fine_rows, IndexType coarse_rows, int blocksize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < fine_rows * blocksize )\n    {\n        int i = tid / blocksize;\n        int e = tid % blocksize;\n        IndexType I = aggregates[i];\n        out[tid] = in[ I * blocksize + e ];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid applyCorrection(ValueType lambda, const ValueType *e, ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = x[tid] + lambda * e[tid];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -------------------------------\n//  Methods\n// ------------------------------\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::transfer_level(AMG_Level<TConfig1> *ref_lvl)\n{\n    Aggregation_AMG_Level_Base<TConfig1> *ref_agg_lvl = dynamic_cast<Aggregation_AMG_Level_Base<TConfig1>*>(ref_lvl);\n    this->scale_counter = ref_agg_lvl->scale_counter;\n    this->scale = ref_agg_lvl->scale;\n    this->m_R_row_offsets.copy(ref_agg_lvl->m_R_row_offsets);\n    this->m_R_column_indices.copy(ref_agg_lvl->m_R_column_indices);\n    this->m_aggregates.copy(ref_agg_lvl->m_aggregates);\n    this->m_aggregates_fine_idx.copy(ref_agg_lvl->m_aggregates_fine_idx);\n    this->m_num_aggregates = ref_agg_lvl->m_num_aggregates;\n    this->m_num_all_aggregates = ref_agg_lvl->m_num_all_aggregates;\n}\n\n\ntypedef std::pair<int, int> mypair;\nbool comparator ( const mypair &l, const mypair &r) { return l.first < r.first; }\n\n// Method to compute R\n// General path\n// TODO: this could be merged with selector to save some computations\ntemplate <typename T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::computeRestrictionOperator_common()\n{\n    m_R_row_offsets.resize(m_num_all_aggregates + 1); //create one more row for the pseudo aggregate\n    IVector R_row_indices(m_aggregates);\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    bool use_cpu = m_aggregates.size() < 4096;\n\n    if (use_cpu)\n    {\n        struct computeRestrictionTask : public task\n        {\n            Aggregation_AMG_Level_Base<T_Config> *self;\n            IVector *R_row_indices;\n\n            void run()\n            {\n                int N = self->m_aggregates.size();\n                IVector_h R_row_indices_host(self->m_aggregates);\n                std::vector<mypair> pairs(N);\n\n                for (int i = 0; i < N; i++)\n                {\n                    pairs[i].first = R_row_indices_host[i];\n                    pairs[i].second = i;\n                }\n\n                std::stable_sort(pairs.begin(), pairs.end(), comparator);\n                IVector_h R_column_indices(self->A->get_num_rows());\n\n                for (int i = 0; i < N; i++)\n                {\n                    R_column_indices[i] = pairs[i].second;\n                    R_row_indices_host[i] = pairs[i].first;\n                }\n\n                self->m_R_column_indices = R_column_indices;\n                *R_row_indices = R_row_indices_host;\n            }\n        };\n        computeRestrictionTask *t = new computeRestrictionTask();\n        t->self = this;\n        t->R_row_indices = &R_row_indices;\n        t->run();\n        delete t;\n    }\n    else\n#endif\n    {\n        m_R_column_indices.resize(this->A->get_num_rows());\n        thrust_wrapper::sequence<TConfig::memSpace>(m_R_column_indices.begin(), m_R_column_indices.end());\n        cudaCheckError();\n        amgx::thrust::sort_by_key(R_row_indices.begin(), R_row_indices.end(), m_R_column_indices.begin());\n        cudaCheckError();\n    }\n\n    amgx::thrust::lower_bound(R_row_indices.begin(),\n                        R_row_indices.end(),\n                        amgx::thrust::counting_iterator<typename IVector::value_type>(0),\n                        amgx::thrust::counting_iterator<typename IVector::value_type>(m_R_row_offsets.size()),\n                        m_R_row_offsets.begin());\n    cudaCheckError();\n}\n\n\n// two methods below could be merged\n// Method to compute R on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_1x1()\n{\n    this->m_R_row_offsets.resize(this->m_num_all_aggregates + 1);\n    this->m_R_column_indices.resize(this->A->get_num_rows());\n    this->fillRowOffsetsAndColIndices(this->A->get_num_rows());\n}\n\n// Method to compute R on HOST using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_4x4()\n{\n    this->m_R_row_offsets.resize(this->m_num_all_aggregates + 1);\n    this->m_R_column_indices.resize(this->A->get_num_rows());\n    this->fillRowOffsetsAndColIndices(this->A->get_num_rows());\n}\n\n// Method to create R_row_offsest and R_column_indices array on HOST using csr or block dia-csr format\ntemplate <typename T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::fillRowOffsetsAndColIndices(const int R_num_cols)\n{\n    for (int i = 0; i < m_num_all_aggregates + 1; i++)\n    {\n        m_R_row_offsets[i] = 0;\n    }\n\n    // Count number of neighbors for each row\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = m_aggregates[i];\n        m_R_row_offsets[I]++;\n    }\n\n    m_R_row_offsets[m_num_all_aggregates] = R_num_cols;\n\n    for (int i = m_num_all_aggregates - 1; i >= 0; i--)\n    {\n        m_R_row_offsets[i] = m_R_row_offsets[i + 1] - m_R_row_offsets[i];\n    }\n\n    /* Set column indices. */\n    for (int i = 0; i < R_num_cols; i++)\n    {\n        int I = m_aggregates[i];\n        int Ip = m_R_row_offsets[I]++;\n        m_R_column_indices[Ip] = i;\n    }\n\n    /* Reset r[i] to start of row memory. */\n    for (int i = m_num_all_aggregates - 1; i > 0; i--)\n    {\n        m_R_row_offsets[i] = m_R_row_offsets[i - 1];\n    }\n\n    m_R_row_offsets[0] = 0;\n}\n\n// Method to compute R on DEVICE using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_4x4()\n{\n    this->computeRestrictionOperator_common();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeRestrictionOperator_1x1()\n{\n    this->computeRestrictionOperator_common();\n}\n\n// Method to restrict Residual on host using csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_1x1(const VVector &r, VVector &rr)\n{\n    ValueTypeB temp;\n\n    for (int i = 0; i < this->m_num_aggregates; i++)\n    {\n        temp = types::util<ValueTypeB>::get_zero();\n\n        for (int j = this->m_R_row_offsets[i]; j < this->m_R_row_offsets[i + 1]; j++)\n        {\n            int j_col = this->m_R_column_indices[j];\n            temp = temp + r[j_col];\n        }\n\n        rr[i] = temp;\n    }\n}\n\n// Method to restrict Residual on host using block_dia_csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_4x4(const VVector &r, VVector &rr)\n{\n    IndexType bsize = this->A->get_block_dimy();\n    ValueTypeB *temp = new ValueTypeB[bsize];\n\n    for (int i = 0; i < this->m_num_aggregates; i++)\n    {\n        // Initialize temp to 0\n        for (int k = 0; k < bsize; k++)\n        {\n            temp[k]  =  types::util<ValueTypeB>::get_zero();\n        }\n\n        // Add contributions from each fine point\n        for (int j = this->m_R_row_offsets[i]; j < this->m_R_row_offsets[i + 1]; j++)\n        {\n            int j_col = this->m_R_column_indices[j];\n\n            for (int k = 0; k < bsize; k++)\n            {\n                temp[k] = temp[k] + r[j_col * bsize + k];\n            }\n        }\n\n        // Store result\n        for (int k = 0; k < bsize; k++)\n        {\n            rr[i * bsize + k] = temp[k];\n        }\n    }\n}\n\n// Method to restrict Residual on device using csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_1x1(const VVector &r, VVector &rr)\n{\n    int block_size = 128;\n    int max_threads;\n\n    if (!this->isConsolidationLevel())\n    {\n        max_threads = this->m_num_aggregates;\n    }\n    else\n    {\n        max_threads = this->m_num_all_aggregates;\n    }\n\n    int num_blocks = max_threads / block_size + 1;\n    const IndexType *R_row_offsets_ptr = this->m_R_row_offsets.raw();\n    const IndexType *R_column_indices_ptr = this->m_R_column_indices.raw();\n    const ValueTypeB *r_ptr = r.raw();\n    ValueTypeB *rr_ptr = rr.raw();\n    restrictResidualKernel <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n    cudaCheckError();\n}\n\n// Method to restrict Residual on device using block_dia_csr_matrix format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::restrictResidual_4x4(const VVector &r, VVector &rr)\n{\n    int block_size = 128;\n    int max_threads;\n\n    if (!this->isConsolidationLevel())\n    {\n        max_threads = this->m_num_aggregates;\n    }\n    else\n    {\n        max_threads = this->m_num_all_aggregates;\n    };\n\n    const int num_blocks = max_threads / block_size + 1;\n    const IndexType *R_row_offsets_ptr = this->m_R_row_offsets.raw();\n\n    const IndexType *R_column_indices_ptr = this->m_R_column_indices.raw();\n\n    const ValueTypeB *r_ptr = r.raw();\n\n    ValueTypeB *rr_ptr = rr.raw();\n\n    cudaCheckError();\n\n    switch ( this->getA().get_block_dimy() )\n    {\n        case 2:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 2> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 3:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 3> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 4:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 4> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 5:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 5> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 8:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 8> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        case 10:\n            restrictResidualBlockDiaCsrKernel<IndexType, ValueTypeB, 10> <<< num_blocks, block_size>>>(R_row_offsets_ptr, R_column_indices_ptr, r_ptr, rr_ptr, max_threads);\n            cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"Unsupported block size in restrictResidual_4x4!!!\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    cudaCheckError();\n}\n\n__inline__ float getAlpha(float &nom, float &denom)\n{\n    float alpha;\n\n    if (nom * denom <= 0. || std::abs(nom) < std::abs(denom))\n    {\n        alpha = 1.;\n    }\n    else if (std::abs(nom) > 2.*std::abs(denom))\n    {\n        alpha = 2.;\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ double getAlpha(double &nom, double &denom)\n{\n    double alpha;\n\n    if (nom * denom <= 0. || std::abs(nom) < std::abs(denom))\n    {\n        alpha = 1.;\n    }\n    else if (std::abs(nom) > 2.*std::abs(denom))\n    {\n        alpha = 2.;\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ hipComplex getAlpha(hipComplex &nom, hipComplex &denom)\n{\n    hipComplex alpha;\n\n    if (types::util<hipComplex>::abs(nom) < types::util<hipComplex>::abs(denom))\n    {\n        alpha = make_hipComplex(1.f, 0.f);\n    }\n    else if (types::util<hipComplex>::abs(nom) > 2.*types::util<hipComplex>::abs(denom))\n    {\n        alpha = make_hipComplex(2.f, 0.f);\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\n__inline__ hipDoubleComplex getAlpha(hipDoubleComplex &nom, hipDoubleComplex &denom)\n{\n    hipDoubleComplex alpha;\n\n    if (types::util<hipDoubleComplex>::abs(nom) < types::util<hipDoubleComplex>::abs(denom))\n    {\n        alpha = make_hipDoubleComplex(1., 0.);\n    }\n    else if (types::util<hipDoubleComplex>::abs(nom) > 2.*types::util<hipDoubleComplex>::abs(denom))\n    {\n        alpha = make_hipDoubleComplex(2., 0.);\n    }\n    else\n    {\n        alpha = nom / denom;\n    }\n\n    return alpha;\n}\n\ntemplate< class T_Config>\ntypename T_Config::VecPrec Aggregation_AMG_Level_Base<T_Config>::computeAlpha(const Vector<T_Config> &e, const Vector<T_Config> &bc, const Vector<T_Config> &tmp)\n{\n    typename T_Config::VecPrec alpha =  types::util<ValueTypeB>::get_one();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    int size = Ac.get_num_rows();\n    VVector v(2,  types::util<ValueTypeB>::get_zero());\n    v[0] = amgx::thrust::inner_product(e.begin(), e.begin() + size, bc.begin(),  types::util<ValueTypeB>::get_zero());\n    v[1] = amgx::thrust::inner_product(e.begin(), e.begin() + size, tmp.begin(),  types::util<ValueTypeB>::get_zero());\n    cudaCheckError();\n    return getAlpha(v[0], v[1]);\n}\n\n// Method to prolongate the error on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec>  >::prolongateAndApplyCorrection_1x1(Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A = this->getA();\n    Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &C = this->next_h->getA();\n\n    if ( this->m_error_scaling >= 2 )\n    {\n        FatalError(\"error_scaling=2,3 is not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n\n    if (this->m_error_scaling)\n    {\n        multiply(this->next_h->getA(), e, tmp);\n        alpha = this->computeAlpha (e, bc, tmp);\n    }\n\n    // Apply correction on all (interior and exterior) equations.\n    for (int i = 0; i < A.get_num_cols(); i++)\n    {\n        int I = this->m_aggregates[i];\n        x[i] = x[i] + alpha * e[I];\n    }\n}\n\n// Method to prolongate the error on HOST using block_dia_csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_4x4(Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    if (this->A->get_block_dimy() != this->A->get_block_dimx())\n    {\n        FatalError(\"Aggregation_AMG_Level not implemented for non square blocks, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if ( this->m_error_scaling >= 2 )\n    {\n        FatalError(\"error_scaling=2,3 is not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    Matrix<TConfig> &C = this->next_h->getA();\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n\n    if (this->m_error_scaling)\n    {\n        multiply(this->next_h->getA(), e, tmp);\n        alpha = this->computeAlpha (e, bc, tmp);\n    }\n\n    // Apply correction on all equations.\n    for (int i = 0; i < this->A->get_num_rows(); i++)\n    {\n        int I = this->m_aggregates[i];\n\n        for (int k = 0; k < this->A->get_block_dimy(); k++)\n        {\n            x[i * this->A->get_block_dimy() + k] =  x[i * this->A->get_block_dimy() + k] + alpha * e[I * this->A->get_block_dimy() + k];\n        }\n    }\n}\n\n// Prolongate the error on DEVICE using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_1x1(Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &e, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &bc, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &x, Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &tmp)\n{\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n    const int block_size = 128;\n    const int num_blocks = this->A->get_num_rows() / block_size + 1;\n    const IndexType *aggregates_ptr = this->m_aggregates.raw();\n    ValueTypeB *x_ptr = x.raw();\n    const ValueTypeB *e_ptr = e.raw();\n\n    if (this->m_error_scaling)\n    {\n        FatalError(\"error_scaling=1 is deprecated\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    prolongateAndApplyCorrectionKernel <<< num_blocks, block_size>>>(alpha, (int)this->A->get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates);\n    cudaCheckError();\n}\n\n// Prolongate the error on DEVICE using block dia-csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Aggregation_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::prolongateAndApplyCorrection_4x4(Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &ec,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &bf,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &xf,\n        Vector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &rf)\n{\n    if ( this->m_error_scaling >= 2 )\n    {\n        if ( this->scale_counter > 0 )\n        {\n            const IndexType *aggregates_ptr = this->m_aggregates.raw();\n            ValueTypeB *x_ptr = xf.raw();\n            const ValueTypeB *e_ptr = ec.raw();\n            const int block_size = 128;\n            const int num_blocks = (this->A->get_num_rows() - 1) / block_size + 1;\n            prolongateAndApplyCorrectionBlockDiaCsrKernel <<< num_blocks, block_size>>>(this->scale, (int)this->getA().get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates, this->getA().get_block_dimy());\n            cudaCheckError();\n            this->scale_counter--;\n            return;\n        }\n\n        bool vanek_scaling = this->m_error_scaling > 3;\n        IndexType numRowsCoarse = this->next_d->getA().get_num_rows();\n        IndexType numRowsFine = this->A->get_num_rows();\n        IndexType blockdim = this->A->get_block_dimx();\n\n        if ( blockdim != this->A->get_block_dimy() )\n        {\n            FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        }\n\n        VVector ef( rf.size() );\n        VVector Aef( rf.size() );\n        ef.set_block_dimy( blockdim );\n        Aef.set_block_dimy( blockdim );\n        // prolongate e\n        const int threads_per_block = 256;\n        const int num_block_values = std::min( AMGX_GRID_MAX_SIZE, (numRowsFine * blockdim - 1) / threads_per_block + 1);\n        const hipStream_t stream = nullptr;\n        prolongateVector <<< num_block_values, threads_per_block, 0, stream>>>( this->m_aggregates.raw(), ec.raw(), ef.raw(), numRowsFine, numRowsCoarse, blockdim );\n        cudaCheckError();\n        ef.dirtybit = 1;\n        hipStreamSynchronize(stream);\n        cudaCheckError();\n        int preSmooth;\n\n        if ( vanek_scaling )\n        {\n            preSmooth = this->amg->getNumPostsweeps();\n        }\n        else\n        {\n            preSmooth = this->scaling_smoother_steps;\n        }\n\n        //smooth error\n        this->smoother->setTolerance( 0.0 );\n        this->smoother->set_max_iters( preSmooth );\n\n        if ( vanek_scaling )\n        {\n            thrust_wrapper::fill<TConfig::memSpace>( Aef.begin(), Aef.end(), types::util<ValueTypeB>::get_zero() );\n            cudaCheckError();\n            this->smoother->solve( Aef, ef, false ); //smooth correction with rhs 0\n            this->smoother->solve( bf, xf, false ); // smooth x with rhs residual\n            //recompute residual\n            int offset, size;\n            this->getA().getOffsetAndSizeForView(OWNED, &offset, &size);\n            axmb( this->getA(), xf, bf, rf, offset, size );\n        }\n        else\n        {\n            this->smoother->solve( rf, ef, false ); //smooth correction with rhs residual\n        }\n\n        // multiply for lambda computation\n        multiply(this->getA(), ef, Aef, OWNED);\n        ValueTypeB nominator, denominator;\n        int offset = 0, size = 0;\n        this->A->getOffsetAndSizeForView(OWNED, &offset, &size);\n\n        if ( this->m_error_scaling == 2 || this->m_error_scaling == 4 )\n        {\n            // compute lambda=<rf,Aef>/<Aef,Aef>\n            nominator = amgx::thrust::inner_product( rf.begin(), rf.end(), Aef.begin(), types::util<ValueTypeB>::get_zero() );\n            denominator = amgx::thrust::inner_product( Aef.begin(), Aef.end(), Aef.begin(), types::util<ValueTypeB>::get_zero() );\n            cudaCheckError();\n        }\n\n        if ( this->m_error_scaling == 3 || this->m_error_scaling == 5)\n        {\n            // compute lambda=<rf,ef>/<ef,Aef>\n            nominator = amgx::thrust::inner_product( rf.begin(), rf.begin() + size * blockdim, ef.begin(), types::util<ValueTypeB>::get_zero() );\n            denominator = amgx::thrust::inner_product( ef.begin(), ef.begin() + size * blockdim, Aef.begin(), types::util<ValueTypeB>::get_zero() );\n\n            if (!this->A->is_matrix_singleGPU())\n            {\n                this->A->getManager()->global_reduce_sum(&nominator);\n                this->A->getManager()->global_reduce_sum(&denominator);\n            }\n\n            cudaCheckError();\n        }\n\n        if (types::util<ValueTypeB>::abs(denominator) == 0.0)\n        {\n            nominator = denominator = types::util<ValueTypeB>::get_one();\n        }\n\n        // apply correction x <- x + lambda*e\n        const int num_block_fine = std::min( AMGX_GRID_MAX_SIZE, (numRowsFine * blockdim - 1) / threads_per_block + 1 );\n        ValueTypeB alpha = nominator / denominator;\n\n        if ( types::util<ValueTypeB>::abs(alpha) < .3 )\n        {\n            alpha = (alpha / types::util<ValueTypeB>::abs(alpha)) * .3;    // it was this before: alpha = .3, which is not 100% equal\n        }\n\n        if ( types::util<ValueTypeB>::abs(alpha) > 10 )\n        {\n            alpha = (alpha / types::util<ValueTypeB>::abs(alpha)) * 10.;    // it was this before: alpha = 10., which is not 100% equal\n        }\n\n        applyCorrection <<< num_block_fine, threads_per_block, 0, stream>>>( alpha, ef.raw(), xf.raw(), numRowsFine * blockdim );\n        cudaCheckError();\n        this->scale_counter = this->reuse_scale; //reuse this scale scale_counter times\n        this->scale = alpha;\n        return;\n    }\n\n    ValueTypeB alpha = types::util<ValueTypeB>::get_one();\n    const int block_size = 128;\n    const int num_blocks = this->A->get_num_rows() / block_size + 1;\n    const IndexType *aggregates_ptr = this->m_aggregates.raw();\n    ValueTypeB *x_ptr = xf.raw();\n    const ValueTypeB *e_ptr = ec.raw();\n\n    if (this->m_error_scaling == 1)\n    {\n        FatalError(\"error_scaling=1 is deprecated\", AMGX_ERR_NOT_IMPLEMENTED );\n    }\n\n    prolongateAndApplyCorrectionBlockDiaCsrKernel <<< num_blocks, block_size>>>(alpha, (int)this->A->get_num_rows(), x_ptr, e_ptr, aggregates_ptr, this->m_num_aggregates, this->A->get_block_dimy());\n    cudaCheckError();\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config >::prolongateAndApplyCorrection(VVector &e, VVector &bf, VVector &x, VVector &tmp)\n{\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    //this is dirty, but error scaling 2 and 3 do not have a specialized version. Instead, the general version sits in the 4x4 function\n    if ( this->m_error_scaling >= 2 )\n    {\n        prolongateAndApplyCorrection_4x4(e, bf, x, tmp);\n    }\n    else if (this->A->get_block_size() == 1)\n    {\n        prolongateAndApplyCorrection_1x1(e, bf, x, tmp);\n    }\n    else if (this->A->get_block_dimx() == this->A->get_block_dimy() )\n    {\n        prolongateAndApplyCorrection_4x4(e, bf, x, tmp);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    x.dirtybit = 1;\n\n    if (!this->A->is_matrix_singleGPU() && x.delayed_send == 0)\n    {\n        if (x.in_transfer & RECEIVING) { this->A->manager->exchange_halo_wait(x, x.tag); }\n\n        this->A->manager->exchange_halo_async(x, x.tag);\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::restrictResidual(VVector &r, VVector &rr)\n{\n    if (this->A->get_block_size() == 1)\n    {\n        restrictResidual_1x1(r, rr);\n    }\n    else if (this->A->get_block_dimx() == this->A->get_block_dimy() )\n    {\n        restrictResidual_4x4(r, rr);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimension for aggregation amg level\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    //TODO: check level transfer between host and device for multiGPU\n    if (!this->A->is_matrix_singleGPU())\n    {\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n        rr.dirtybit = 1;\n\n        if (!Ac.is_matrix_singleGPU() && !this->isConsolidationLevel() && rr.delayed_send == 0)\n        {\n            Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA(); //TODO problem in memoryspace transfer is here\n\n            if (rr.in_transfer & RECEIVING) { Ac.manager->exchange_halo_wait(rr, rr.tag); }\n\n            Ac.manager->exchange_halo_async(rr, rr.tag);\n        }\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::computeRestrictionOperator()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeRestrictionOperator_1x1();\n    }\n    else if (this->A->get_block_dimx() == 4 && this->A->get_block_dimy() == 4)\n    {\n        computeRestrictionOperator_4x4();\n    }\n    else\n    {\n        this->computeRestrictionOperator_common();\n    }\n}\n\ntemplate <typename IndexType>\n__global__ void coarse_to_global(IndexType *aggregates, IndexType *aggregates_global, IndexType *renumbering, IndexType num_elements, int64_t offset)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < num_elements)\n    {\n        renumbering[aggregates[element]] = aggregates_global[element] + offset; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename T, typename IndexType>\n__global__ void export_matrix_elements(IndexType *row_offsets, IndexType *col_indices, T *values, IndexType *maps, IndexType *renumbering, IndexType *new_row_offsets, IndexType *new_col_indices, T *new_values, IndexType bsize, IndexType size)\n{\n    int idx = blockIdx.x * blockDim.x / 32 + threadIdx.x / 32;\n    int coopIdx = threadIdx.x % 32;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row_offsets[row];\n        INDEX_TYPE dst_base = new_row_offsets[idx];\n\n        for (int m = coopIdx; m < row_offsets[row + 1]*bsize - src_base * bsize; m += 32)\n        {\n            new_values[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        for (int m = coopIdx; m < row_offsets[row + 1] - src_base; m += 32)\n        {\n            new_col_indices[dst_base + m] = renumbering[col_indices[src_base + m]];\n        }\n\n        idx += gridDim.x * blockDim.x / 32;\n    }\n}\n\ntemplate <class T>\n__global__ void export_matrix_diagonal(T *values, INDEX_TYPE bsize, INDEX_TYPE *maps, T *output, INDEX_TYPE size)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int row = maps[idx];\n        INDEX_TYPE src_base = row;\n        INDEX_TYPE dst_base = idx;\n\n        for (int m = 0; m < bsize; m++)\n        {\n            output[dst_base * bsize + m] = values[src_base * bsize + m];\n        }\n\n        idx += gridDim.x * blockDim.x;\n    }\n}\n\n__global__ void remove_boundary(INDEX_TYPE *flags, INDEX_TYPE *maps, INDEX_TYPE size)\n{\n    int element = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (element < size)\n    {\n        flags[maps[element]] = 0; //this won't be a problem, because we are overwriting the same thing\n        element += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_inverse_renumbering(INDEX_TYPE *renum, INDEX_TYPE *irenum, INDEX_TYPE *renum_gbl, INDEX_TYPE base_index, INDEX_TYPE max_element)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < max_element)\n    {\n        irenum[renum[idx]] = renum_gbl[idx] - base_index;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void create_halo_mapping(INDEX_TYPE *mapping, INDEX_TYPE *node_list, INDEX_TYPE base_index, INDEX_TYPE map_offset, INDEX_TYPE size)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < size)\n    {\n        int idx = node_list[row] - base_index;\n        mapping[idx] = map_offset + row;\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void map_col_indices_and_count_rowlen(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *row_length, INDEX_TYPE *renumbering, INDEX_TYPE *mapping, INDEX_TYPE *map_offsets, int64_t *index_ranges, INDEX_TYPE part_id, INDEX_TYPE my_id, INDEX_TYPE base_index, INDEX_TYPE my_range, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows)\n{\n    extern __shared__ volatile int reduction[];\n    int row = blockIdx.x * blockDim.x / 4 + threadIdx.x / 4;\n    int coopIdx = threadIdx.x % 4;\n\n    while (row < num_rows)\n    {\n        int valid = 0;\n\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += 4) //this may look horrible, but I expect low branch divergence, because col indices in a row usually belong to the same partition (or at most one more)\n        {\n            int colIdx = col_indices[idx];\n            int part = -2;\n\n            if (colIdx >= index_ranges[2 * part_id] && colIdx < index_ranges[2 * part_id + 1]) //the col index probably belongs to the partition I am working on\n            {\n                part = part_id;\n            }\n            else if (colIdx >= base_index && colIdx < base_index + my_range)     //or points back to the owned partition\n            {\n                part = -1;\n            }\n            else        //or else it points to a third partition\n            {\n                for (int i = 0; i < num_neighbors; i++)\n                {\n                    if (colIdx >= index_ranges[2 * i] && colIdx < index_ranges[2 * i + 1])\n                    {\n                        part = i;\n                    }\n                }\n            }\n\n            if (part == -2)\n            {\n                col_indices[idx] = -1;\n#ifdef DEBUG\n                printf(\"Column index encountered that does not belong to any of my neighbors!! %d\\n\", colIdx);\n#endif\n            }\n            else\n            {\n                if (part == -1)\n                {\n                    col_indices[idx] = renumbering[colIdx - base_index];\n                    valid++;\n                }\n                else\n                {\n                    int new_col_idx = mapping[map_offsets[part] + colIdx - index_ranges[2 * part]];\n\n                    if (new_col_idx >= 0)\n                    {\n                        valid++;\n                        col_indices[idx] = new_col_idx;\n                    }\n                    else\n                    {\n                        col_indices[idx] = -1;\n                    }\n                }\n            }\n        }\n\n        reduction[threadIdx.x] = valid;\n\n        for (int s = 2; s > 0; s >>= 1)\n        {\n            if (coopIdx < s)\n            {\n                reduction[threadIdx.x] += reduction[threadIdx.x + s];\n            }\n\n            __syncthreads();\n        }\n\n        if (coopIdx == 0)\n        {\n            row_length[row] = reduction[threadIdx.x];\n        }\n\n        row += gridDim.x * blockDim.x / 4;\n    }\n}\n\n__global__ void map_col_indices(INDEX_TYPE *row_offsets, INDEX_TYPE *col_indices, int64_t *halo_ranges, INDEX_TYPE *halo_renumbering, INDEX_TYPE *halo_rows, INDEX_TYPE *global_renumbering, INDEX_TYPE num_neighbors, INDEX_TYPE num_rows, INDEX_TYPE num_rows_processed)\n{\n    int row = blockIdx.x * blockDim.x / 4 + threadIdx.x / 4;\n    int coopIdx = threadIdx.x % 4;\n\n    while (row < num_rows_processed)\n    {\n        for (int idx = row_offsets[row] + coopIdx; idx < row_offsets[row + 1]; idx += 4)\n        {\n            int colIdx = col_indices[idx];\n            int part = 0;\n\n            if (colIdx < num_rows)\n            {\n                part = -1;\n            }\n            else\n            {\n                colIdx = global_renumbering[colIdx];\n\n                for (int i = 0; i < num_neighbors; i++)\n                {\n                    if (colIdx >= halo_ranges[2 * i] && colIdx < halo_ranges[2 * i + 1])\n                    {\n                        part = i;\n                        break;\n                    }\n                }\n            }\n\n            if (part == -1)\n            {\n                col_indices[idx] = colIdx;\n            }\n            else\n            {\n                col_indices[idx] = halo_renumbering[halo_rows[part] + colIdx - halo_ranges[2 * part]];\n            }\n        }\n\n        row += gridDim.x * blockDim.x / 4;\n    }\n}\n\ntemplate <class T>\n__global__ void reorder_whole_matrix(INDEX_TYPE *old_rows, INDEX_TYPE *old_cols, T *old_vals, INDEX_TYPE *rows, INDEX_TYPE *cols, T *vals, INDEX_TYPE bsize, INDEX_TYPE num_rows)\n{\n    int row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    while (row < num_rows)\n    {\n        INDEX_TYPE dst_row = row;\n        INDEX_TYPE src_base = old_rows[row];\n        INDEX_TYPE dst = rows[dst_row];\n\n        for (int i = 0; i < old_rows[row + 1] - src_base; i++)\n        {\n            INDEX_TYPE colIdx = old_cols[src_base + i];\n\n            if (colIdx >= 0)\n            {\n                cols[dst] = colIdx;\n\n                for (int j = 0; j < bsize; j++) { vals[dst * bsize + j] = old_vals[(src_base + i) * bsize + j]; }\n\n                dst++;\n            }\n        }\n\n        row += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void calc_gbl_renumbering(INDEX_TYPE *inv_renum, INDEX_TYPE *gbl_renum, INDEX_TYPE size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        gbl_renum[inv_renum[idx]] = idx;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename ValueType>\n__global__ void write_diagonals(ValueType *values, INDEX_TYPE *diag, INDEX_TYPE *map, ValueType *output, INDEX_TYPE bsize, INDEX_TYPE size)\n{\n    int nzPerBlock = blockDim.x / bsize;\n    int row = blockIdx.x * nzPerBlock + threadIdx.x / bsize;\n    int vecIdx = threadIdx.x % bsize;\n\n    if (threadIdx.x >= (blockDim.x / bsize)*bsize) { return; }\n\n    while (row < size)\n    {\n        output[row * bsize + vecIdx] = values[diag[map[row]] * bsize + vecIdx];\n        row += gridDim.x * nzPerBlock;\n    }\n}\n\ntemplate <typename ValueType>\n__global__ void write_diagonals_back(ValueType *values, INDEX_TYPE *diag, ValueType *source, INDEX_TYPE bsize, INDEX_TYPE size)\n{\n    int nzPerBlock = blockDim.x / bsize;\n    int row = blockIdx.x * nzPerBlock + threadIdx.x / bsize;\n    int vecIdx = threadIdx.x % bsize;\n\n    if (threadIdx.x >= (blockDim.x / bsize)*bsize) { return; }\n\n    while (row < size)\n    {\n        values[diag[row]*bsize + vecIdx] = source[row * bsize + vecIdx];\n        row += gridDim.x * nzPerBlock;\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_full(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.get_num_rows();\n        int f_size = A.get_num_rows();\n        int diag = Ac.hasProps(DIAG);\n\n        if (A.manager->B2L_rings[0].size() > 2) { FatalError(\"Aggregation_AMG_Level prepareNextLevelMatrix not implemented >1 halo rings\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE); }\n\n        //get coarse -> fine global renumbering\n        IVector renumbering(c_size);\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), renumbering.raw(), f_size, 0);\n        cudaCheckError();\n        //\n        // Step 0 - form halo matrices that are exported to neighbors\n        //\n        std::vector<Matrix<TConfig> > halo_rows(num_neighbors);\n        std::vector<DistributedManager<TConfig> > halo_btl(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++ )\n        {\n            int num_unique = Ac.manager->B2L_rings[i][1];\n            //prepare export halo matrices\n            halo_btl[i].resize(1, 1);\n            halo_btl[i].set_global_id(Ac.manager->global_id());\n            halo_btl[i].B2L_maps[0].resize(num_unique);\n            halo_btl[i].B2L_rings[0].resize(2);\n            halo_btl[i].B2L_rings[0][0] = 0;\n            halo_btl[i].B2L_rings[0][1] = num_unique;\n            halo_btl[i].set_index_range(A.manager->index_range());\n            halo_btl[i].set_base_index(A.manager->base_index());\n            //global indices of rows of the halo matrix\n            amgx::thrust::copy(amgx::thrust::make_permutation_iterator( renumbering.begin(), Ac.manager->B2L_maps[i].begin()),\n                         amgx::thrust::make_permutation_iterator( renumbering.begin(), Ac.manager->B2L_maps[i].begin() + num_unique),\n                         halo_btl[i].B2L_maps[0].begin());\n            cudaCheckError();\n            halo_rows[i].addProps(CSR);\n\n            if (diag) { halo_rows[i].addProps(DIAG); }\n\n            //calculate row length and row_offsets\n            halo_rows[i].row_offsets.resize(num_unique + 1);\n            thrust_wrapper::transform<TConfig::memSpace>(amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin() + 1, Ac.manager->B2L_maps[i].begin()),\n                              amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin() + 1, Ac.manager->B2L_maps[i].end()),\n                              amgx::thrust::make_permutation_iterator(Ac.row_offsets.begin(), Ac.manager->B2L_maps[i].begin()),\n                              halo_rows[i].row_offsets.begin(),\n                              amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            thrust_wrapper::exclusive_scan<TConfig::memSpace>(halo_rows[i].row_offsets.begin(), halo_rows[i].row_offsets.end(), halo_rows[i].row_offsets.begin());\n            cudaCheckError();\n            //resize halo matrix\n            IndexType num_nz = halo_rows[i].row_offsets[num_unique];\n            halo_rows[i].resize(num_unique, num_unique, num_nz, Ac.get_block_dimy(), Ac.get_block_dimx(), 1);\n            //copy relevant rows and renumber their column indices\n            num_blocks = std::min(4096, (num_unique + 127) / 128);\n            export_matrix_elements <<< num_blocks, 128>>>(Ac.row_offsets.raw(), Ac.col_indices.raw(), Ac.values.raw(), Ac.manager->B2L_maps[i].raw(), renumbering.raw(), halo_rows[i].row_offsets.raw(), halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(), A.get_block_size(), num_unique);\n            cudaCheckError();\n\n            if (diag)\n            {\n                export_matrix_diagonal <<< num_blocks, 128>>>(Ac.values.raw() + Ac.row_offsets[Ac.get_num_rows()]*Ac.get_block_size(), Ac.get_block_size(), Ac.manager->B2L_maps[i].raw(), halo_rows[i].values.raw() + halo_rows[i].row_offsets[halo_rows[i].get_num_rows()]*Ac.get_block_size(), num_unique);\n                cudaCheckError();\n            }\n        }\n\n        Ac.manager->getComms()->exchange_matrix_halo(halo_rows, halo_btl, Ac);\n        //--------------------- renumbering/reordering matrix, integrating halo -----------------------------\n        Ac.set_initialized(0);\n        //number of owned rows\n        c_size = Ac.manager->halo_offsets[0];\n        f_size = A.manager->halo_offsets[0];\n        num_blocks = std::min(4096, (c_size + 511) / 512);\n        int rings = 1;\n        //\n        // Step 1 - calculate inverse renumbering (to global indices - base_index)\n        //\n        Ac.manager->inverse_renumbering.resize(c_size);\n        thrust_wrapper::transform<TConfig::memSpace>(renumbering.begin(),\n                          renumbering.begin() + c_size,\n                          amgx::thrust::constant_iterator<IndexType>(A.manager->base_index()),\n                          Ac.manager->inverse_renumbering.begin(),\n                          amgx::thrust::minus<IndexType>());\n        cudaCheckError();\n        //big renumbering table for going from global index to owned local index\n        IVector global_to_coarse_local(Ac.manager->index_range());\n        thrust_wrapper::fill<TConfig::memSpace>(global_to_coarse_local.begin(), global_to_coarse_local.begin() + Ac.manager->index_range(), -1);\n        cudaCheckError();\n        calc_gbl_renumbering <<< num_blocks, 512>>>(Ac.manager->inverse_renumbering.raw(), global_to_coarse_local.raw(), c_size);\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n        cudaCheckError();\n        //\n        // Step 2 - create big mapping table of all halo indices we received (this may use a little too much memory sum(fine nodes per neighbor)\n        //\n        amgx::thrust::host_vector<INDEX_TYPE> neighbor_rows(num_neighbors + 1);\n        int max_num_rows = 0;\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            neighbor_rows[i] = halo_rows[i].manager->index_range();\n            max_num_rows = max_num_rows > halo_rows[i].get_num_rows() ? max_num_rows : halo_rows[i].get_num_rows();\n        }\n\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows.begin());\n        cudaCheckError();\n        int total_rows_of_neighbors = neighbor_rows[num_neighbors];\n        IVector halo_mapping(total_rows_of_neighbors);\n        thrust_wrapper::fill<TConfig::memSpace>(halo_mapping.begin(), halo_mapping.end(), -1);\n        cudaCheckError();\n\n        for (int ring = 0; ring < rings; ring++)\n        {\n            for (int i = 0; i < num_neighbors; i++)\n            {\n                int size = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n                int num_blocks = std::min(4096, (size + 127) / 128);\n                create_halo_mapping <<< num_blocks, 128>>>(halo_mapping.raw() + neighbor_rows[i],\n                        halo_btl[i].B2L_maps[0].raw() + halo_btl[i].B2L_rings[0][ring],\n                        halo_btl[i].base_index(),\n                        Ac.manager->halo_offsets[ring * num_neighbors + i], size);\n                        cudaCheckError();\n            }\n        }\n\n        cudaCheckError();\n        //\n        // Step 3 - renumber halo matrices and calculate row length (to eventually append to the big matrix)\n        //\n        INDEX_TYPE owned_nnz = Ac.row_offsets[c_size];\n        IVector neighbor_rows_d(num_neighbors + 1);\n        amgx::thrust::copy(neighbor_rows.begin(), neighbor_rows.end(), neighbor_rows_d.begin());\n        cudaCheckError();\n        //map column indices of my own matrix (the ones that point outward)\n        map_col_indices <<< num_blocks, 512>>>(Ac.row_offsets.raw() + Ac.manager->num_interior_nodes(),\n                                               Ac.col_indices.raw(),\n                                               Ac.manager->halo_ranges.raw(),\n                                               halo_mapping.raw(),\n                                               neighbor_rows_d.raw(),\n                                               renumbering.raw(),\n                                               num_neighbors, c_size, c_size - Ac.manager->num_interior_nodes());\n        cudaCheckError();\n        IVector temp_row_len(max_num_rows);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            //map column indices of halo matrices\n            int size = halo_rows[i].get_num_rows();\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            map_col_indices_and_count_rowlen <<< num_blocks, 128, 128 * sizeof(INDEX_TYPE)>>>(\n                halo_rows[i].row_offsets.raw(),\n                halo_rows[i].col_indices.raw(),\n                temp_row_len.raw(),\n                global_to_coarse_local.raw(),\n                halo_mapping.raw(),\n                neighbor_rows_d.raw(),\n                Ac.manager->halo_ranges.raw(),\n                i,\n                Ac.manager->global_id(),\n                Ac.manager->base_index(),\n                Ac.manager->index_range(),\n                num_neighbors,\n                size);\n                cudaCheckError();\n\n            for (int ring = 0; ring < rings; ring++)\n            {\n                amgx::thrust::copy(temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring], temp_row_len.begin() + halo_btl[i].B2L_rings[0][ring + 1], Ac.row_offsets.begin() + Ac.manager->halo_offsets[ring * num_neighbors + i]);\n            }\n        }\n\n        cudaCheckError();\n        INDEX_TYPE old_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(Ac.row_offsets.begin() + c_size, Ac.row_offsets.end(), Ac.row_offsets.begin() + c_size, owned_nnz);\n        cudaCheckError();\n        //\n        // Step 4 - consolidate column indices and values\n        //\n        int new_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n\n        Ac.col_indices.resize(new_nnz);\n        Ac.values.resize((new_nnz + 1 + diag * (Ac.row_offsets.size() - 2)) * A.get_block_size());\n\n        if (diag)\n        {\n            MVector diags(c_size * Ac.get_block_size());\n            amgx::thrust::copy(Ac.values.begin() + old_nnz * Ac.get_block_size(),\n                         Ac.values.begin() + old_nnz * Ac.get_block_size() + c_size * Ac.get_block_size(),\n                         diags.begin());\n            amgx::thrust::copy(diags.begin(), diags.begin() + c_size * Ac.get_block_size(),\n                         Ac.values.begin() + Ac.row_offsets[Ac.get_num_rows()]*Ac.get_block_size());\n            cudaCheckError();\n        }\n\n        int cumulative_num_rows = c_size;\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            for (int ring = 0; ring < rings; ring++)\n            {\n                int num_rows = halo_btl[i].B2L_rings[0][ring + 1] - halo_btl[i].B2L_rings[0][ring];\n                int num_blocks = std::min(4096, (num_rows + 127) / 128);\n                reorder_whole_matrix <<< num_blocks, 128>>>(halo_rows[i].row_offsets.raw() + halo_btl[i].B2L_rings[0][ring], halo_rows[i].col_indices.raw(), halo_rows[i].values.raw(), Ac.row_offsets.raw() + Ac.manager->halo_offsets[ring * num_neighbors + i], Ac.col_indices.raw(), Ac.values.raw(), Ac.get_block_size(), num_rows);\n                cudaCheckError();\n\n                if (diag)\n                {\n                    amgx::thrust::copy(halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring])*Ac.get_block_size(),\n                                 halo_rows[i].values.begin() + (halo_rows[i].row_offsets[halo_rows[i].get_num_rows()] + halo_btl[i].B2L_rings[0][ring + 1])*Ac.get_block_size(),\n                                 Ac.values.begin() + (Ac.row_offsets[Ac.get_num_rows()] + cumulative_num_rows)*Ac.get_block_size());\n                    cumulative_num_rows += num_rows;\n                }\n            }\n        }\n\n        cudaCheckError();\n        Ac.set_num_cols(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1]);\n        Ac.set_num_rows(Ac.get_num_cols());\n        Ac.set_num_nz(new_nnz);\n        Ac.delProps(COO);\n        Ac.set_initialized(1);\n        Ac.computeDiagonal();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_diag(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.manager->halo_offsets[0];\n        int f_size = A.manager->halo_offsets[0];\n        int diag = Ac.hasProps(DIAG);\n        Ac.manager->inverse_renumbering.resize(c_size);\n        //get coarse -> fine renumbering\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), Ac.manager->inverse_renumbering.raw(), f_size, -1 * A.manager->base_index());\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n\n        if (!diag) { Ac.computeDiagonal(); }\n\n        Ac.set_initialized(1);\n        std::vector<MVector> diagonals(num_neighbors);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = Ac.manager->B2L_rings[i][Ac.manager->B2L_rings.size() - 1];\n            diagonals[i].resize(Ac.get_block_size()*size);\n            int num_blocks = std::min(4096, (size + 127) / 128);\n            write_diagonals <<< num_blocks, 128>>>(Ac.values.raw(), Ac.diag.raw(), Ac.manager->B2L_maps[i].raw(), diagonals[i].raw(), Ac.get_block_size(), size);\n        }\n\n        cudaCheckError();\n        Ac.manager->getComms()->exchange_vectors(diagonals, Ac, this->tag * 100 + 10 + 2);\n\n        for (int i = 0; i < num_neighbors; i++)\n        {\n            int size = Ac.manager->halo_offsets[i + 1] - Ac.manager->halo_offsets[i];\n\n            if (Ac.hasProps(DIAG)) { amgx::thrust::copy(diagonals[i].begin(), diagonals[i].begin() + Ac.get_block_size()*size, Ac.values.begin() + Ac.get_block_size() * (Ac.diagOffset() + Ac.manager->halo_offsets[i])); }\n            else\n            {\n                int num_blocks = std::min(4096, (size + 127) / 128);\n                write_diagonals_back <<< num_blocks, 128>>>(Ac.values.raw(), Ac.diag.raw() + Ac.manager->halo_offsets[i], diagonals[i].raw(), Ac.get_block_size(), size);\n                cudaCheckError();\n            }\n        }\n\n        cudaCheckError();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix_none(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (A.is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    if (TConfig::memSpace == AMGX_host)\n    {\n        FatalError(\"Aggregation AMG Not implemented for host\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n    else\n    {\n        int c_size = Ac.manager->halo_offsets[0];\n        int f_size = A.manager->halo_offsets[0];\n        int diag = Ac.hasProps(DIAG);\n        Ac.manager->inverse_renumbering.resize(c_size);\n        //get coarse -> fine renumbering\n        int num_blocks = std::min(4096, (c_size + 127) / 128);\n        coarse_to_global <<< num_blocks, 128>>>(this->m_aggregates.raw(), this->m_aggregates_fine_idx.raw(), Ac.manager->inverse_renumbering.raw(), f_size, 0);\n        cudaCheckError();\n        Ac.manager->set_num_halo_rows(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1] - c_size);\n        Ac.set_initialized(1);\n\n        if (!diag) { Ac.computeDiagonal(); }\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::prepareNextLevelMatrix(const Matrix<TConfig> &A, Matrix<TConfig> &Ac)\n{\n    if (m_matrix_halo_exchange == 0)\n    {\n        this->prepareNextLevelMatrix_none(A, Ac);\n    }\n    else if (m_matrix_halo_exchange == 1)\n    {\n        this->prepareNextLevelMatrix_diag(A, Ac);\n    }\n    else if (m_matrix_halo_exchange == 2)\n    {\n        this->prepareNextLevelMatrix_full(A, Ac);\n    }\n    else\n    {\n        FatalError(\"Invalid Aggregation matrix_halo_exchange parameter\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\n__global__ void set_halo_rowlen(INDEX_TYPE *work, INDEX_TYPE *output, INDEX_TYPE  size, INDEX_TYPE diag)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        if (work[idx + 1] - work[idx] > 0)\n        {\n            output[idx] += work[idx + 1] - work[idx] - (1 - diag);\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <typename T>\n__global__ void append_halo_nz(INDEX_TYPE *row_offsets, INDEX_TYPE *new_row_offsets, INDEX_TYPE *col_indices, INDEX_TYPE *new_col_indices, T *values, T *new_values, INDEX_TYPE size, INDEX_TYPE diag, INDEX_TYPE halo_offset, INDEX_TYPE block_size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        int add_diag = !diag;\n\n        if (!diag && new_col_indices[new_row_offsets[idx]] != -1) { add_diag = 0; } //if diag or there is already soimething in the row, then don't add diagonal nonzero (inside diag)\n\n        int append_offset = -1;\n\n        for (int i = new_row_offsets[idx]; i < new_row_offsets[idx + 1]; i++)\n        {\n            if (new_col_indices[i] == -1) {append_offset = i; break;}\n        }\n\n        for (int i = row_offsets[idx]; i < row_offsets[idx + 1]; i++)\n        {\n            if (diag && i == row_offsets[idx])   //if outside diag and this is the first nonzero in a non-empty row, overwrite diagonal value\n            {\n                for (int j = 0; j < block_size; j++)\n                {\n                    new_values[(new_row_offsets[size] + halo_offset + idx)*block_size + j] = values[(row_offsets[size] + halo_offset + idx) * block_size + j];\n                }\n            }\n\n            int col_idx = col_indices[i];\n\n            if (append_offset == -1 && (col_idx != halo_offset + idx)) {printf(\"ERROR: append offset is -1 but row has nonzeros in it old %d to %d new %d to %d\\n\", row_offsets[idx], row_offsets[idx + 1], new_row_offsets[idx], new_row_offsets[idx + 1]); append_offset = 0;}\n\n            if (col_idx != halo_offset + idx || add_diag)\n            {\n                new_col_indices[append_offset] = col_idx;\n\n                for (int j = 0; j < block_size; j++)\n                {\n                    new_values[append_offset * block_size + j] = values[i * block_size + j];\n                }\n\n                append_offset++;\n            }\n        }\n\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseB2LMaps(std::vector<IVector> &in_coarse_B2L_maps)\n{\n    Matrix<TConfig> &A = this->getA();\n    m_num_all_aggregates = m_num_aggregates;\n    int num_neighbors = A.manager->neighbors.size();\n    IndexType max_b2l = 0;\n\n    for (int i = 0; i < num_neighbors; i++ ) { max_b2l = max_b2l > A.manager->B2L_rings[i][1] ? max_b2l : A.manager->B2L_rings[i][1]; }\n\n    IVector B2L_aggregates(max_b2l);\n    IVector indices(max_b2l);\n\n    for (int i = 0; i < num_neighbors; i++ )\n    {\n        int size = A.manager->B2L_rings[i][1];\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + size, 0);\n        thrust_wrapper::sequence<TConfig::memSpace>(indices.begin(), indices.begin() + size);\n        //substitute coarse aggregate indices for fine boundary nodes\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin()),\n                     amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin() + size),\n                     B2L_aggregates.begin());\n        //find the unique ones\n        amgx::thrust::sort_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin());\n        IndexType num_unique = amgx::thrust::unique_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin()).first - B2L_aggregates.begin();\n        in_coarse_B2L_maps[i].resize(num_unique);\n        //sort it back so we have the original ordering\n        amgx::thrust::sort_by_key(indices.begin(), indices.begin() + num_unique, B2L_aggregates.begin());\n        amgx::thrust::copy(B2L_aggregates.begin(), B2L_aggregates.begin() + num_unique, in_coarse_B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n}\n\n\n__global__ void populate_coarse_boundary(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE *output, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        output[flags[maps[indices[idx]]]] = maps[indices[idx]];\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void flag_coarse_boundary(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *maps, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[maps[indices[idx]]] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void flag_halo_indices(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE  offset, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        flags[indices[idx] - offset] = 1;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n__global__ void apply_halo_aggregate_indices(INDEX_TYPE *flags, INDEX_TYPE *indices, INDEX_TYPE *output, INDEX_TYPE offset, INDEX_TYPE aggregates_offset, INDEX_TYPE  size)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n\n    while (idx < size)\n    {\n        output[idx] = flags[indices[idx] - offset] + aggregates_offset;\n        idx += blockDim.x * gridDim.x;\n    }\n}\n\n// renumbering the aggregates/communicationg with neighbors\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::setNeighborAggregates()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    m_num_all_aggregates = m_num_aggregates;\n\n    /* WARNING: the matrix reordering always happens inside createRenumbering routine. There are three ways to get to this routine\n       1. matrix_upload_all -> uploadMatrix -> initializeUploadReorderAll -> reorder_matrix -> createRenumbering\n       2. read_system_distributed -> renumberMatrixOneRing -> reorder_matrix_owned -> createRenumbering\n       3. solver_setup -> ... -> AMG_Level::setup -> createCoarseMatrices -> setNeighborAggregates -> createRenumbering\n       If you are reading the renumbering from file you might need to add intercept code in if statement below,\n       otherwise this routine will exit before calling createRenumbering routine (in case of single or disjoint partitions).\n    */\n    if (this->getA().is_matrix_singleGPU()) { return; }\n\n    int num_neighbors = A.manager->neighbors.size();\n\n    //\n    // Step 0 - set up coarse matrix metadata\n    //\n    if (Ac.manager == NULL) { Ac.manager = new DistributedManager<T_Config>(); }\n\n    Ac.manager->resize(A.manager->neighbors.size(), 1);\n    Ac.manager->A = &Ac;\n    int f_size = A.get_num_rows();\n    Ac.manager->setComms(A.manager->getComms());\n    Ac.manager->set_global_id(A.manager->global_id());\n    Ac.manager->neighbors = A.manager->neighbors;\n    Ac.manager->set_base_index(A.manager->base_index());\n    Ac.manager->halo_ranges = A.manager->halo_ranges;\n    Ac.manager->set_index_range(A.manager->index_range());\n    //-------------------------------------- Section 1 - renumbering -----------------------------------------------------------\n    //\n    // Step 1 - calculate coarse level B2L maps - any aggregate that has a fine boundary node, becomes a coarse boundary node\n    //\n    m_num_all_aggregates = m_num_aggregates;\n    int vec_size = m_num_aggregates + 1; //A.manager->num_boundary_nodes()+1;\n    IVector B2L_aggregates(vec_size);\n\n    for (int i = 0; i < A.manager->neighbors.size(); i++)\n    {\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + vec_size, 0);\n        int size = A.manager->B2L_rings[i][1];\n        int block_size = 128;\n        int grid_size = std::min( 4096, ( size + block_size - 1 ) / block_size);\n        flag_coarse_boundary <<< grid_size, block_size>>>(B2L_aggregates.raw(), A.manager->B2L_maps[i].raw(), this->m_aggregates.raw(), size);\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + vec_size, B2L_aggregates.begin());\n        (Ac.manager->B2L_maps)[i].resize(B2L_aggregates[vec_size - 1]);\n        populate_coarse_boundary <<< grid_size, block_size>>>(B2L_aggregates.raw(), A.manager->B2L_maps[i].raw(), this->m_aggregates.raw(), Ac.manager->B2L_maps[i].raw(), size);\n    }\n\n    cudaCheckError();\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        Ac.manager->B2L_rings[i].resize(2);\n        Ac.manager->B2L_rings[i][0] = 0;\n        Ac.manager->B2L_rings[i][1] = Ac.manager->B2L_maps[i].size();\n    }\n\n    DistributedArranger<T_Config> *prep = new DistributedArranger<T_Config>;\n    prep->initialize_B2L_maps_offsets(Ac, 1);\n    delete prep;\n    Ac.set_num_rows(m_num_aggregates);\n    IVector renumbering(m_num_aggregates + 1); /* +1 is actually not needed, it will be resized in createRenumbering */\n    Ac.manager->createRenumbering(renumbering);\n    //\n    // Step 2 - renumber aggregates, so boundary nodes will have higher index than interior ones (based on the renumberiong we have been calculating)\n    //\n    /* WARNING: 1. Thrust scatter and gather routines seem more appropriate here, but they implicitly assume that the input\n                and output have certain size correlation, which is not matched by vectors in our case. The only remaining option\n                is to use make_permutation as is done below. Example of Thrust scatter and gather calls\n                IVector ttt(f_size,-1);\n                amgx::thrust::scatter(this->m_aggregates.begin(), this->m_aggregates.begin()+f_size, renumbering.begin(), ttt.begin());\n                amgx::thrust::gather(renumbering.begin(), renumbering.end(), this->m_aggregates.begin(), ttt.begin());\n                amgx::thrust::copy(ttt.begin(), ttt.end(), this->m_aggregates.begin());\n\n                2. The original thrust composite call is illegal because it uses the same array (m_aggregates) for input and output.\n                amgx::thrust::copy(amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()),\n                amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()+f_size),\n                             this->m_aggregates.begin());\n                Although it somehow still works, it is much safer to use explicit temporary storage for the intermediate result.\n    */\n    /* WARNING: must save unreordered aggregates for later use before reordering them. */\n    IVector unreordered_aggregates(this->m_aggregates);\n    /* WARNING: change Thrust call to explicitly use temporary storage for the intermediate result. The earlier version is illegal, but somehow still works. */\n    IVector ttt(f_size, -1);\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(renumbering.begin(), this->m_aggregates.begin() + f_size),\n                 ttt.begin());\n    amgx::thrust::copy(ttt.begin(), ttt.end(), this->m_aggregates.begin());\n    cudaCheckError();\n\n    //we don't need renumbering anymore, it will be identity on the coarse level\n\n    //-------------------------------------- Section 2 - communication -----------------------------------------------------------\n\n    //\n    // Step 3 - populate aggregates_fine_idx, which stores for every fine node the original global index of the aggregate (which is lowest global index of nodes aggregated together)\n    //\n\n    //\n    // These are different when we do /don't do matrix halo exchanges - when we do we need global indices to match nodes,\n    // and in this case Ac after computeA will not have the same ordering of halo nodes as after prepareNextLevel_full.\n    // However when we do not do matrix halo exchange we are only interested in the ordering of halo nodes on the coarse level,\n    // and we can get that by exchanging the (already renumbered) aggregates vector.\n    //\n    if (m_matrix_halo_exchange == 2)\n    {\n        //Find original global indices of nodes that have the minimum id in the aggregates.\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(A.manager->inverse_renumbering.begin(), this->m_aggregates_fine_idx.begin()),\n                     amgx::thrust::make_permutation_iterator(A.manager->inverse_renumbering.begin(), this->m_aggregates_fine_idx.begin() + f_size),\n                     this->m_aggregates_fine_idx.begin());\n        thrust_wrapper::transform<TConfig::memSpace>(this->m_aggregates_fine_idx.begin(),\n                          this->m_aggregates_fine_idx.begin() + f_size,\n                          amgx::thrust::constant_iterator<IndexType>(A.manager->base_index()),\n                          this->m_aggregates_fine_idx.begin(),\n                          amgx::thrust::plus<IndexType>());\n        //communicate\n        this->m_aggregates_fine_idx.set_block_dimx(1);\n        this->m_aggregates_fine_idx.set_block_dimy(1);\n        m_aggregates_fine_idx.dirtybit = 1;\n        A.manager->exchange_halo(m_aggregates_fine_idx, this->tag * 100 + 1 * 10 + 0);\n    }\n    else\n    {\n        //communicate\n        this->m_aggregates.set_block_dimx(1);\n        this->m_aggregates.set_block_dimy(1);\n        m_aggregates.dirtybit = 1;\n        /* WARNING: you should exchange unreordered aggregates, and append them to your own reordered aggregates, to conform to asusmptions done by distributed_mamanger. */\n        //A.manager->exchange_halo(m_aggregates, this->tag*100+1*10+0); //wrong\n        A.manager->exchange_halo(unreordered_aggregates, this->tag * 100 + 1 * 10 + 0);\n        amgx::thrust::copy(unreordered_aggregates.begin() + f_size, unreordered_aggregates.end(), this->m_aggregates.begin() + f_size);\n    }\n\n    cudaCheckError();\n    //\n    // Step 4 - consolidate neighbors' aggregates into own list to be able to perform Galerkin product with the n-ring halo\n    //\n    IVector &exchanged_aggregates = m_matrix_halo_exchange == 2 ? this->m_aggregates_fine_idx : this->m_aggregates;\n    int min_index = amgx::thrust::reduce(exchanged_aggregates.begin() + A.manager->halo_offsets[0], exchanged_aggregates.begin() + A.manager->halo_offsets[num_neighbors], (int)0xFFFFFFF, amgx::thrust::minimum<int>());\n    int max_index = amgx::thrust::reduce(exchanged_aggregates.begin() + A.manager->halo_offsets[0], exchanged_aggregates.begin() + A.manager->halo_offsets[num_neighbors], (int)0, amgx::thrust::maximum<int>());\n    cudaCheckError();\n    int s_size = max_index - min_index + 2;\n    IVector scratch(s_size);\n\n    for (int i = 0; i < num_neighbors; i++)\n    {\n        int size = A.manager->halo_offsets[i + 1] - A.manager->halo_offsets[i];\n        //Could also use local minimums to perform the same operation. The results are the same.\n        //int min_local = amgx::thrust::reduce(exchanged_aggregates.begin()+A.manager->halo_offsets[i], exchanged_aggregates.begin()+A.manager->halo_offsets[i+1], (int)0xFFFFFFF, amgx::thrust::minimum<int>());\n        thrust_wrapper::fill<TConfig::memSpace>(scratch.begin(), scratch.begin() + s_size, 0);\n        int block_size = 128;\n        int grid_size = std::min( 4096, ( size + block_size - 1 ) / block_size);\n        flag_halo_indices <<< grid_size, block_size>>>(scratch.raw(), exchanged_aggregates.raw() + A.manager->halo_offsets[i], min_index /*min_local*/, size);\n        cudaCheckError();\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(scratch.begin(), scratch.begin() + s_size, scratch.begin());\n        apply_halo_aggregate_indices <<< grid_size, block_size>>>(scratch.raw(), exchanged_aggregates.raw() + A.manager->halo_offsets[i], this->m_aggregates.raw() + A.manager->halo_offsets[i], min_index /*min_local*/, m_num_all_aggregates, size);\n        cudaCheckError();\n        Ac.manager->halo_offsets[i] = m_num_all_aggregates;\n        m_num_all_aggregates += scratch[s_size - 1];\n    }\n\n    cudaCheckError();\n    Ac.manager->halo_offsets[num_neighbors] = m_num_all_aggregates;\n}\n\n//TODO: The consolidate and unconsolidate parts could be made more efficient by only sending the\n//      nonzero values\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidateVector(VVector &x)\n{\n    int my_id = this->getA().manager->global_id();\n\n    if (this->getA().manager->isRootPartition())\n    {\n        // Here all partitions being consolidated should have same vector size, see TODO above\n        INDEX_TYPE num_parts = this->getA().manager->getNumPartsToConsolidate();\n\n        for (int i = 0; i < num_parts; i++)\n        {\n            int current_part = this->getA().manager->getPartsToConsolidate()[i];\n\n            // Vector has been set to correct size\n            if (current_part != my_id)\n            {\n                //printf(\"Root partition %d receiving %d -> %d and %d -> %d (total %d)\\n\", this->getA().manager->global_id(), this->getA().manager->getConsolidationArrayOffsets()[i], this->getA().manager->getConsolidationArrayOffsets()[i+1], this->getA().manager->getConsolidationArrayOffsets()[num_parts+i], this->getA().manager->getConsolidationArrayOffsets()[num_parts+i+1], (int)x.size()/x.get_block_size());\n                this->getA().manager->getComms()->recv_vector(x, current_part, 10000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[i + 1] - this->getA().manager->getConsolidationArrayOffsets()[i]));\n                this->getA().manager->getComms()->recv_vector(x, current_part, 20000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[num_parts + i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[num_parts + i + 1] - this->getA().manager->getConsolidationArrayOffsets()[num_parts + i]));\n            }\n        }\n    }\n    else\n    {\n        int my_destination_part = this->getA().manager->getMyDestinationPartition();\n        int i_off, i_size, b_off, b_size;\n        this->getA().manager->getConsolidationOffsets(&i_off, &i_size, &b_off, &b_size);\n        // Here all partitions being consolidated should have same vector size, see TODO above\n        this->getA().manager->getComms()->send_vector_async(x, my_destination_part, 10000 + my_id, i_off * x.get_block_size(), i_size * x.get_block_size());\n        this->getA().manager->getComms()->send_vector_async(x, my_destination_part, 20000 + my_id, b_off * x.get_block_size(), b_size * x.get_block_size());\n    }\n}\n\n//TODO: The consolidate and unconsolidate parts could be made more efficient by only sending the\n//      nonzero values\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::unconsolidateVector(VVector &x)\n{\n    if (this->getA().manager->isRootPartition())\n    {\n        INDEX_TYPE num_parts = this->getA().manager->getNumPartsToConsolidate();\n\n        for (int i = 0; i < num_parts; i++)\n        {\n            int current_part = this->getA().manager->getPartsToConsolidate()[i];\n\n            // Vector has been set to correct size\n            if (current_part != this->getA().manager->global_id())\n            {\n                this->getA().manager->getComms()->send_vector_async(x, current_part, 30000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[i + 1] - this->getA().manager->getConsolidationArrayOffsets()[i]));\n                this->getA().manager->getComms()->send_vector_async(x, current_part, 40000 + current_part, x.get_block_size()*this->getA().manager->getConsolidationArrayOffsets()[num_parts + i], x.get_block_size() * (this->getA().manager->getConsolidationArrayOffsets()[num_parts + i + 1] - this->getA().manager->getConsolidationArrayOffsets()[num_parts + i]));\n            }\n        }\n    }\n    else\n    {\n        int my_destination_part = this->getA().manager->getMyDestinationPartition();\n        // Vector x is of unknown size\n        int i_off, i_size, b_off, b_size;\n        this->getA().manager->getConsolidationOffsets(&i_off, &i_size, &b_off, &b_size);\n        this->getA().manager->getComms()->recv_vector(x, my_destination_part, 30000 + this->getA().manager->global_id(), i_off * x.get_block_size(), i_size * x.get_block_size());\n        this->getA().manager->getComms()->recv_vector(x, my_destination_part, 40000 + this->getA().manager->global_id(), b_off * x.get_block_size(), b_size * x.get_block_size());\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseVertices()\n{\n    //Set the aggregates\n    this->m_selector->setAggregates(this->getA(), this->m_aggregates, this->m_aggregates_fine_idx, this->m_num_aggregates);\n\n    if ( this->m_print_aggregation_info )\n    {\n        this->m_selector->printAggregationInfo( this->m_aggregates, this->m_aggregates_fine_idx, this->m_num_aggregates );\n    }\n\n    this->getA().template setParameter< int > (\"aggregates_num\", this->m_num_aggregates); // ptr to aaggregates\n}\n\n//  Creating the next level\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::createCoarseMatrices()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0).\n                Notice that in aggregation path, prolongation P is implicit,\n                and is used through the aggregates array. */\n\n    bool const consolidation_level = !A.is_matrix_singleGPU() && this->isConsolidationLevel();\n\n    // bookkeeping for the coarse grid: renumber aggregates,\n    // if consolidation compute consolidated halo-offsets, etc\n    if (!this->isReuseLevel())\n    {\n        if (consolidation_level)\n        {\n            // Consolidation-path steps 1-9\n            this->consolidationBookKeeping();\n        }\n        else\n        {\n            this->setNeighborAggregates();\n        }\n    }\n\n    this->getA().setView(ALL);\n\n    // Compute restriction operator\n    // TODO: computing the restriction operator could be merged with the selector to save some work\n    // If we reuse the level we keep the previous restriction operator\n    if (this->isReuseLevel() == false)\n    {\n        computeRestrictionOperator();\n    }\n\n    Ac.set_initialized(0);\n    Ac.copyAuxData(&A);\n    this->m_coarseAGenerator->computeAOperator(A, Ac, this->m_aggregates, this->m_R_row_offsets, this->m_R_column_indices, this->m_num_all_aggregates);\n    Ac.setColsReorderedByColor(false);\n    Ac.setView(FULL);\n\n    if (consolidation_level)\n    {\n        // Consolidation-path Steps 11-12, send matrices to root, consolidate, final bookkeeping\n        this->consolidateCoarseGridMatrix();\n    }\n    else\n    {\n        this->prepareNextLevelMatrix(A, Ac);\n    }\n\n    A.setView(OWNED);\n    Ac.setView(OWNED);\n\n    this->m_next_level_size = this->m_num_all_aggregates * Ac.get_block_dimy();\n\n    if (this->m_print_aggregation_info)\n    {\n        MatrixAnalysis<TConfig> ana(&Ac);\n        ana.aggregatesQuality2(this->m_aggregates, this->m_num_aggregates, A);\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidationBookKeeping()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n\n    int num_parts, num_fine_neighbors, my_id;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        num_parts = A.manager->getComms()->get_num_partitions();\n        num_fine_neighbors = A.manager->neighbors.size();\n        my_id = A.manager->global_id();\n    }\n    else\n    {\n        num_parts = 1;\n        num_fine_neighbors = 0;\n        my_id = 0;\n    }\n\n    // ----------------------------------------------------\n    // Consolidate multiple fine matrices into one coarse matrix\n    // ----------------------------------------------------\n    // ----------------\n    // Step 1\n    // Decide which partitions should be merged together, store in destination_partitions vector\n    // ---------------\n    IVector_h &destination_part = A.manager->getDestinationPartitions();\n    int my_destination_part = A.manager->getMyDestinationPartition();\n\n    if (my_destination_part >= num_parts)\n    {\n        FatalError(\"During consolidation, sending data to partition that doesn't exist\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // Create mapping from coarse partition indices (ranks on the coarse consolidated level) to partition indices on the fine level (ranks on the fine level)\n    IVector_h coarse_part_to_fine_part = destination_part;\n    amgx::thrust::sort(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end());\n    cudaCheckError();\n    coarse_part_to_fine_part.erase(thrust::unique(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end()), coarse_part_to_fine_part.end());\n    cudaCheckError();\n    //Then, the number of coarse partitions is simply the size of this vector\n    int num_coarse_partitions = coarse_part_to_fine_part.size();\n    // Create mapping from fine partition indices to coarse partition indices, with fine partitions that are merging together having the same coarse indices\n    IVector_h fine_part_to_coarse_part(num_parts);\n    amgx::thrust::lower_bound(coarse_part_to_fine_part.begin(), coarse_part_to_fine_part.end(), destination_part.begin(), destination_part.end(), fine_part_to_coarse_part.begin());\n    cudaCheckError();\n    // Create mapping from this specific partition's neighbors to consolidated coarse neighbors, but using their fine index (aka. destination partition indices for my neighbors)\n    IVector_h fine_neigh_to_fine_part;\n    A.manager->createNeighToDestPartMap(fine_neigh_to_fine_part, A.manager->neighbors, destination_part, num_fine_neighbors);\n    // Create mapping from consolidated coarse neighbors to fine partition indices (even if the current partition is not going to be a root)\n    IVector_h coarse_neigh_to_fine_part;\n    int num_coarse_neighbors;\n    A.manager->createConsolidatedNeighToPartMap(coarse_neigh_to_fine_part, fine_neigh_to_fine_part, my_destination_part, destination_part, num_coarse_neighbors);\n    // Create mapping from fine neighbors to coarse neighbors, with fine neighbors this partition is merging with labeled with -1\n    IVector_h fine_neigh_to_coarse_neigh;\n    A.manager->createNeighToConsNeigh(fine_neigh_to_coarse_neigh, coarse_neigh_to_fine_part, fine_neigh_to_fine_part, my_destination_part, num_fine_neighbors);\n    /*\n        EXAMPLE\n        Take the following partition graph (that describes connections between partitions, vertices are the partitions themselves), this is the same graph that is used in the setup example\n        number of partitions num_parts=12\n        CSR row_offsets [0 4 8 13 21 25 32 36 41 46 50 57 61]\n        CSR col_indices [0 1 3 8\n                    0 1 2 3\n                    1 2 3 4 5\n                    0 1 2 3 4 5 8 10\n                    2 4 5 6\n                    2 3 4 5 6 7 10\n                    4 5 6 7\n                    5 6 7 9 10\n                    0 3 8 10 11\n                    7 9 10 11\n                    3 5 7 8 9 10 11\n                    8 9 10 11]\n        destination_part = [0 0 0 0 4 4 4 4 8 8 8 8]\n        coarse_part_to_fine_part = [0 4 8] num_coarse_partitions = 3\n        fine_part_to_coarse_part = [0 0 0 0 1 1 1 1 2 2 2 2]\n        original neighbor lists correspond to the rows of the matrix, minus the diagonal elements: (part 0)[1 3 8] (part 3)[0 1 2 4 5 8 10] (part 10)[3 5 7 8 9 11]\n        fine_neigh_to_fine_part (part 0)[0 0 2] (part 3)[0 0 0 0 1 2 2] (part 10)[0 1 1 2 2 2]\n        coarse_neigh_to_fine_part (part 0)[8] (part 3)[4 8] (part 10)[0 4]\n        fine_neigh_to_coarse_neigh (part 0)[-1 -1 0] (part 3)[-1 -1 -1 0 0 1 1] (part 10)[0 1 1 -1 -1 -1]\n        */\n    // --------------------------\n    // Step 2\n    // Create coarse B2L_maps, by mapping fine B2L maps to coarse indices using this->m_aggregates and eliminating duplicates\n    // --------------------------\n    std::vector<IVector> coarse_B2L_maps(num_fine_neighbors);\n    m_num_all_aggregates = m_num_aggregates;\n    int num_neighbors_temp = A.manager->neighbors.size();\n    int num_rings = A.manager->B2L_rings[0].size() - 1;\n\n    if (num_rings != 1)\n    {\n        FatalError(\"num_rings > 1 not supported in consolidation\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IndexType max_b2l = 0;\n\n\n    for (int i = 0; i < num_neighbors_temp; i++ ) { max_b2l = max_b2l > A.manager->B2L_rings[i][1] ? max_b2l : A.manager->B2L_rings[i][1]; }\n\n    IVector B2L_aggregates(max_b2l);\n    IVector indices(max_b2l);\n\n    //TODO: use the algorithm from setNeighborAggregates()\n    for (int i = 0; i < num_neighbors_temp; i++ )\n    {\n        int size = A.manager->B2L_rings[i][1];\n        thrust_wrapper::fill<TConfig::memSpace>(B2L_aggregates.begin(), B2L_aggregates.begin() + size, 0);\n        thrust_wrapper::sequence<TConfig::memSpace>(indices.begin(), indices.begin() + size);\n        //substitute coarse aggregate indices for fine boundary nodes\n        amgx::thrust::copy(amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin()),\n                        amgx::thrust::make_permutation_iterator(this->m_aggregates.begin(), A.manager->B2L_maps[i].begin() + size),\n                        B2L_aggregates.begin());\n        //find the unique ones\n        amgx::thrust::sort_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin());\n        IndexType num_unique = amgx::thrust::unique_by_key(B2L_aggregates.begin(), B2L_aggregates.begin() + size, indices.begin()).first - B2L_aggregates.begin();\n        coarse_B2L_maps[i].resize(num_unique);\n        //sort it back so we have the original ordering\n        amgx::thrust::sort_by_key(indices.begin(), indices.begin() + num_unique, B2L_aggregates.begin());\n        amgx::thrust::copy(B2L_aggregates.begin(), B2L_aggregates.begin() + num_unique, coarse_B2L_maps[i].begin());\n    }\n\n    cudaCheckError();\n    /*\n        * EXAMPLE\n        say, partition 3 has the following coarse B2L_maps:\n        neighbors [0 1 2 4 5 8 10]\n        B2L_maps[0(=0)] = [6 7 8]\n        B2L_maps[1(=1)] = [8 9 10]\n        B2L_maps[2(=2)] = [10 11 12 13]\n        B2L_maps[3(=4)] = [13 14 15]\n        B2L_maps[4(=5)] = [15 16 17]\n        B2L_maps[5(=8)] = [6 18 19]\n        B2L_maps[6(=10)] = [17 20 19]\n        */\n    // ---------------------------------------------------\n    // Step 3\n    // create new B2L maps for each merged destination neighbor and drop B2L maps to neighbors we are merging with\n    // ---------------------------------------------------\n    std::vector<IVector> dest_coarse_B2L_maps;\n    A.manager->consolidateB2Lmaps(dest_coarse_B2L_maps, coarse_B2L_maps, fine_neigh_to_coarse_neigh, num_coarse_neighbors, num_fine_neighbors);\n    /*\n        * EXAMPLE\n        Then, merging the coarse B2L maps on partition 3, we get:\n        coarse_neigh_to_fine_part [4 8]\n        dest_coarse_B2L_maps[0(=4)] = [13 14 15 16 17]\n        dest_coarse_B2L_maps[1(=8)] = [6 17 18 19 20]\n        */\n    // -----------------------\n    // Step 4\n    // Create interior-boundary renumbering of aggregates according to dest_coarse_B2L_maps\n    // -----------------------\n    // Now renumber the aggregates with all interior aggregates first, boundary aggregates second\n    int num_interior_aggregates; //returned by createAggregatesRenumbering\n    int num_boundary_aggregates; //returned by createAggregatesRenumbering\n    IVector renumbering; //returned by createAggregatesRenumbering\n    // Following calls create renumbering array and modifies B2L_maps\n    A.manager->createAggregatesRenumbering(renumbering, dest_coarse_B2L_maps, this->m_num_aggregates, num_coarse_neighbors, num_interior_aggregates, num_boundary_aggregates, num_rings);\n    /*\n        * EXAMPLE\n        Partition 3 will get a renumbering vector of size 21, for the 21 owned agggregates:\n        [0 1 2 3 4 5 17 6 7 8 9 10 11 12 13 14 15 16 18 19 20]\n        num_interior_aggregates = 12\n        num_boundary_aggregates = 9\n        */\n    // -------------------------------------------------\n    // Step 5\n    // Determine whether root partition, make list of partitions merged into one\n    // ------------------------------------------------\n    // Check if I'm root partition and how fine partitions (including myself) are merging into me\n    // bool is_root_partition = false;\n    bool &is_root_partition = this->m_is_root_partition;\n    is_root_partition = false; \n    int num_fine_parts_to_consolidate = 0;\n    // IVector_h fine_parts_to_consolidate;\n    IVector_h &fine_parts_to_consolidate = this->m_fine_parts_to_consolidate;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            is_root_partition = true;\n            num_fine_parts_to_consolidate++;\n        }\n    }\n\n    fine_parts_to_consolidate.resize(num_fine_parts_to_consolidate);\n    int count = 0;\n\n    for (int i = 0; i < num_parts; i++)\n    {\n        if (destination_part[i] == my_id)\n        {\n            fine_parts_to_consolidate[count] = i;\n            count++;\n        }\n    }\n\n    //save this information as state, as this will also be required during solve for restriction/prolongation\n    A.manager->setIsRootPartition(is_root_partition);\n    A.manager->setNumPartsToConsolidate(num_fine_parts_to_consolidate);\n    A.manager->setPartsToConsolidate(fine_parts_to_consolidate);\n\n    // Create a new distributed communicator for coarse levels that only contains active partitions\n    if (Ac.manager == NULL)\n    {\n        Ac.manager = new DistributedManager<TConfig>();\n    }\n\n    Ac.manager->setComms(A.manager->getComms()->Clone());\n    Ac.manager->getComms()->createSubComm(coarse_part_to_fine_part, is_root_partition);\n\n\n    /*\n        * EXAMPLE\n        isRootPartition is true for partitions 0,4,8 false for others\n        num_fine_parts_to_consolidate = 4 for partitions 0,4,8\n        fine_parts_to_consolidate (part 0)[0 1 2 3] (part 4)[4 5 6 7] (part 8)[8 9 10 11]\n        */\n    // ----------------------\n    // Step 6\n    // Compute number of interior, boundary and total nodes in the consolidated coarse matrix. Create offsets so that partitions being merged together will have their aggregate indices ordered like this:\n    // [num_interior(fine_parts_to_consolidate[0]] num_interior(fine_parts_to_consolidate[1]] ... num_interior(fine_parts_to_consolidate[num_fine_parts_to_consolidate]\n    //        num_boundary(fine_parts_to_consolidate[0]] num_boundary(fine_parts_to_consolidate[1]] ... num_boundary(fine_parts_to_consolidate[num_fine_parts_to_consolidate] ]\n    // ----------------------\n    // Gather to get number of interior/boundary aggregates of neighbors I will merge with\n    // std::vector<IVector_h> vertex_counts;\n    std::vector<IVector_h> &vertex_counts = this->m_vertex_counts;\n    // int interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged;\n    int interior_offset, boundary_offset;\n    int &total_interior_rows_in_merged = this->m_total_interior_rows_in_merged;\n    int &total_boundary_rows_in_merged = this->m_total_boundary_rows_in_merged;\n    int total_rows_in_merged;\n    //Computes these offsets on the root, sends them back\n    A.manager->computeConsolidatedOffsets(my_id, my_destination_part, is_root_partition, num_interior_aggregates, num_boundary_aggregates, vertex_counts, fine_parts_to_consolidate, num_fine_parts_to_consolidate, interior_offset, boundary_offset, total_interior_rows_in_merged, total_boundary_rows_in_merged, total_rows_in_merged, A.manager->getComms());\n    //Partitions save these offsets, as it will be required during solve restriction/prolongation\n    A.manager->setConsolidationOffsets(interior_offset, num_interior_aggregates, boundary_offset + num_interior_aggregates, num_boundary_aggregates);\n    /*\n        * EXAMPLE\n        For root partition 0, say we have the following interior/boundary counts (note that partition 1 has 0 boundary, as it is only connected to partitions it is merging with)\n        part 0 - interior: 10 boundary 3\n        part 1 - interior: 18\n        part 2 - interior: 10 boundary 16\n        part 3 - interior: 12 boundary 9\n        interior_offset for partitions 0,1,2,3: 0 10 28 38 (total_interior_rows_in_merged 50)\n        boundary_offset for partitions 0,1,2,3: 0 3 3 19 (total_boundary_rows_in_merged 28)\n        */\n    // ----------------------\n    // Step 7\n    // Each partition renumbers its aggregates and dest_coarse_B2L_maps using offsets computed in Step 6 and permutation in Step 4\n    // ----------------------\n    // Kernel to renumber the aggregates\n    int block_size = 128;\n    int grid_size = std::min( 4096, ( A.manager->halo_offsets[0] + block_size - 1 ) / block_size);\n    renumberAggregatesKernel <<< grid_size, block_size >>>(renumbering.raw(), interior_offset, boundary_offset, this->m_aggregates.raw(), A.manager->halo_offsets[0], num_interior_aggregates, renumbering.size());\n    cudaCheckError();\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        thrust_wrapper::transform<TConfig::memSpace>(dest_coarse_B2L_maps[i].begin(),\n                            dest_coarse_B2L_maps[i].end(),\n                            amgx::thrust::constant_iterator<IndexType>(boundary_offset),\n                            dest_coarse_B2L_maps[i].begin(),\n                            amgx::thrust::plus<IndexType>());\n    }\n\n    cudaCheckError();\n    /*\n        * EXAMPLE\n        Partition 3 had a renumbering vector:\n        [0 1 2 3 4 5 17 6 7 8 9 10 11 12 13 14 15 16 18 19 20]\n        which is now adjusted to account for the consolidated coarse matrices' indices:\n        [38 39 40 41 42 43 74 44 45 46 47 48 49 69 70 71 72 73 75 76 77]\n        And the dest_coarse_B2L_maps, which looked like:\n        dest_coarse_B2L_maps[0(=4)] = [13 14 15 16 17]\n        dest_coarse_B2L_maps[1(=8)] = [6 17 18 19 20]\n        is now:\n        dest_coarse_B2L_maps[0(=4)] = [69 70 71 72 73]\n        dest_coarse_B2L_maps[1(=8)] = [74 73 75 76 77]\n        */\n    // -------------------------------------------------\n    // Step 8\n    // Send dest_coarse_B2L_maps to root partitions\n    // ------------------------------------------------\n    // Each fine partition sends to its root the number of coarse neighbors it has, their ids, and the number of boundary nodes for each coarse neighbor\n    IVector_h num_bdy_per_coarse_neigh(num_coarse_neighbors);\n\n    for (int i = 0; i < num_coarse_neighbors; i++)\n    {\n        num_bdy_per_coarse_neigh[i] = dest_coarse_B2L_maps[i].size();\n    }\n\n    IVector_h consolidated_coarse_neigh_to_fine_part; //consolidated list of coarse neighbors for the root partition, using fine partition indices\n    int num_consolidated_neighbors = 0;\n    // std::vector<IVector> consolidated_B2L_maps; //concatenates dest_coarse_B2L_maps received from partitions that are merging into the same root and pointing to the same destination coarse neighbor\n    std::vector<IVector> &consolidated_B2L_maps = this->m_consolidated_B2L_maps;\n    A.manager->consolidateB2LmapsOnRoot(num_consolidated_neighbors, consolidated_B2L_maps, consolidated_coarse_neigh_to_fine_part, dest_coarse_B2L_maps, coarse_neigh_to_fine_part, num_bdy_per_coarse_neigh, fine_parts_to_consolidate, num_fine_parts_to_consolidate, my_id, my_destination_part, is_root_partition, num_coarse_neighbors, A.manager->getComms());\n    //\n    // Step 9 - figuring out halo aggregate IDs\n    //\n    //Now we need to update halo aggregate IDs - this is just a halo exchange on this->m_aggregates between partitions\n    //that are being merged together, but we need to send other halos to the root to come up with the halo renumbering\n    //TODO: separate transactions, send \"real halo\" to the root nodes (coarse neighbors) immediately\n    //Step 9.1: takes care of synchronizing the aggregate IDs between partitions we are merging together and got consistent halo aggregate IDs for neighbor we are not merging with (which are going to be sent to the root in 9.2)\n    A.manager->exchange_halo(this->m_aggregates, 6666);\n    /*\n        * EXAMPLE 2\n        This example is independent from the previous ones.\n        Say partition 0 and 1 are merging (into 0) partition 0 is neighbors with 1,2,3 and partition 1 is neighbors with 0,3,4\n        Partitions 3 and 4 are merging (into partition 3) and partition 2 is not merging with anyone.\n        This example details the renumbering of halo indices on partition 0 and partition 1.\n        After the exchange halo, we have:\n        this->m_aggregates on partition 0:\n        [(fine interior nodes) (fine boundary nodes) (fine halo from part 1) (fine halo from part 2) (fine halo from part 3)]\n        [(fine interior nodes) (fine boundary nodes) (13 13 15) (12 15 17) (14 16 18)]\n        aggregates on partition 1:\n        [(fine interior nodes) (fine boundary nodes) (fine halo from part 0) (fine halo from part 3) (fine halo from part 4)]\n        [(fine interior nodes) (fine boundary nodes) (14 16 17) (18 19 19) (15 15 17)]\n        indices in  (fine halo from part 0) and (fine halo from part 1) actually contain interior aggregate indices (if they are not connected to partitions 2,3 or 4), because the boundary is disappearing there.\n        Indices in halo regions contain remote-local indices.\n\n        This example is used throughout consolidateAndRenumberHalos\n        */\n    //Step 9.2 - 9.5\n    // IVector_h halo_offsets(num_consolidated_neighbors + 1, 0);\n    IVector_h &halo_offsets = this->m_consolidated_halo_offsets;\n    halo_offsets = IVector_h(num_consolidated_neighbors + 1, 0);\n    A.manager->consolidateAndRenumberHalos(this->m_aggregates, A.manager->halo_offsets, halo_offsets, A.manager->neighbors, num_fine_neighbors, consolidated_coarse_neigh_to_fine_part, num_consolidated_neighbors, destination_part, my_destination_part, is_root_partition, fine_parts_to_consolidate, num_fine_parts_to_consolidate, num_parts, my_id, total_rows_in_merged, this->m_num_all_aggregates, A.manager->getComms());\n\n    if (is_root_partition)\n    {\n        for (int i = 0; i < consolidated_B2L_maps.size(); i++)\n        {\n            amgx::thrust::sort(consolidated_B2L_maps[i].begin(), consolidated_B2L_maps[i].end());\n        }\n\n        this->m_consolidated_neighbors.resize(num_consolidated_neighbors);\n        for (int i = 0; i < num_consolidated_neighbors; i++)\n        {\n            this->m_consolidated_neighbors[i] = fine_part_to_coarse_part[consolidated_coarse_neigh_to_fine_part[i]];\n        }\n            \n        cudaCheckError();\n    }\n}\n\ntemplate <class T_Config>\nvoid Aggregation_AMG_Level_Base<T_Config>::consolidateCoarseGridMatrix()\n{\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n\n    int my_id = A.manager->global_id();\n        \n    IVector_h &destination_part = A.manager->getDestinationPartitions();\n    int my_destination_part = A.manager->getMyDestinationPartition();\n\n    // bookkeeping stored in AMG_Level_Base\n    std::vector<IVector_h> &vertex_counts = this->m_vertex_counts;\n    IVector_h &fine_parts_to_consolidate  = this->m_fine_parts_to_consolidate;\n\n    // bookkeeping stored in either AMG_Level_Base or Acs' DistributedManager\n    IVector_h &halo_offsets                     = this->isReuseLevel() ? Ac.manager->getHaloOffsets() : this->m_consolidated_halo_offsets;\n    std::vector<IVector> &consolidated_B2L_maps = this->isReuseLevel() ? Ac.manager->getB2Lmaps()     : this->m_consolidated_B2L_maps;\n\n    int num_consolidated_neighbors = this->isRootPartition() ? this->m_consolidated_neighbors.size() : 0;\n\n    if (!this->isRootPartition())\n    {\n        A.manager->getComms()->send_vector_async(Ac.row_offsets, my_destination_part, 1111);\n        A.manager->getComms()->send_vector_async(Ac.col_indices, my_destination_part, 1112);\n        A.manager->getComms()->send_vector_async(Ac.values, my_destination_part, 1113);\n    }\n    else\n    {\n        int num_fine_parts_to_consolidate = fine_parts_to_consolidate.size();\n\n        int total_num_rows = this->m_num_all_aggregates;\n        IVector new_row_offsets(total_num_rows + 1, 0);\n\n        //if diags are inside then we won't be counting those twice when computing halo row length\n        if (!Ac.hasProps(DIAG))\n        {\n            thrust_wrapper::fill<TConfig::memSpace>(new_row_offsets.begin() + halo_offsets[0], new_row_offsets.begin() + halo_offsets[num_consolidated_neighbors], 1);\n            cudaCheckError();\n        }\n\n        std::vector<IVector> recv_row_offsets(num_fine_parts_to_consolidate);\n        std::vector<VecInt_t> num_nz(num_fine_parts_to_consolidate);\n        IVector *work_row_offsets;\n        std::vector<VecInt_t> index_offset_array(2 * num_fine_parts_to_consolidate + 1);\n        int interior_offset = 0;\n        int boundary_offset = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            boundary_offset += vertex_counts[i][0];\n        }\n\n        int max_num_nz = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            //receive row offsets\n            if (current_part != my_id)\n            {\n                recv_row_offsets[i].resize(total_num_rows + 1);\n                A.manager->getComms()->recv_vector(recv_row_offsets[i], current_part, 1111);\n                work_row_offsets = &(recv_row_offsets[i]);\n                num_nz[i] = (*work_row_offsets)[work_row_offsets->size() - 1];\n                max_num_nz = max_num_nz > num_nz[i] ? max_num_nz : num_nz[i];\n            }\n            else\n            {\n                work_row_offsets = &(Ac.row_offsets);\n                num_nz[i] = Ac.get_num_nz();\n            }\n\n            //Get interior row length\n            thrust_wrapper::transform<TConfig::memSpace>(work_row_offsets->begin() + interior_offset + 1,\n                                work_row_offsets->begin() + interior_offset + vertex_counts[i][0] + 1,\n                                work_row_offsets->begin() + interior_offset,\n                                new_row_offsets.begin() + interior_offset,\n                                amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            //Get boundary row length\n            thrust_wrapper::transform<TConfig::memSpace>(work_row_offsets->begin() + boundary_offset + 1,\n                                work_row_offsets->begin() + boundary_offset + vertex_counts[i][1] + 1,\n                                work_row_offsets->begin() + boundary_offset,\n                                new_row_offsets.begin() + boundary_offset,\n                                amgx::thrust::minus<IndexType>());\n            cudaCheckError();\n            //Increment halo row length by one for every nonzero that is an edge from the halo into this partition\n            int size = halo_offsets[num_consolidated_neighbors] - halo_offsets[0];\n            const int block_size = 128;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n            set_halo_rowlen <<< num_blocks, block_size>>>(work_row_offsets->raw() + halo_offsets[0], new_row_offsets.raw() + halo_offsets[0], size, Ac.hasProps(DIAG));\n            cudaCheckError();\n            index_offset_array[i] = interior_offset;\n            index_offset_array[num_fine_parts_to_consolidate + i] = boundary_offset;\n            interior_offset += vertex_counts[i][0];\n            boundary_offset += vertex_counts[i][1];\n            index_offset_array[i + 1] = interior_offset;\n            index_offset_array[num_fine_parts_to_consolidate + i + 1] = boundary_offset;\n        }\n\n        A.manager->setConsolidationArrayOffsets(index_offset_array);\n        //Exclusive scan row length array to get row offsets\n        thrust_wrapper::exclusive_scan<TConfig::memSpace>(new_row_offsets.begin(), new_row_offsets.end(), new_row_offsets.begin());\n        cudaCheckError();\n        //Prepare to receive column indices and values\n        int num_nz_consolidated = new_row_offsets[new_row_offsets.size() - 1];\n        IVector recv_col_indices(max_num_nz);\n        IVector new_col_indices(num_nz_consolidated);\n        MVector recv_values((max_num_nz + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n        MVector new_values((num_nz_consolidated + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n        thrust_wrapper::fill<TConfig::memSpace>(new_col_indices.begin() + new_row_offsets[halo_offsets[0]], new_col_indices.end(), -1); //Set all the halo col indices to -1\n\n\n        if (!Ac.hasProps(DIAG)) { thrust_wrapper::fill<TConfig::memSpace>(new_values.begin() + num_nz_consolidated * Ac.get_block_size(), new_values.end(), types::util<ValueTypeA>::get_zero()); }\n\n        cudaCheckError();\n        IVector *work_col_indices;\n        MVector *work_values;\n        interior_offset = 0;\n        boundary_offset = 0;\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n            boundary_offset += vertex_counts[i][0];\n        }\n\n        for (int i = 0; i < num_fine_parts_to_consolidate; i++)\n        {\n            int current_part = fine_parts_to_consolidate[i];\n\n            if (current_part != my_id)\n            {\n                A.manager->getComms()->recv_vector(recv_col_indices, current_part, 1112, 0, num_nz[i]);\n                A.manager->getComms()->recv_vector(recv_values, current_part, 1113, 0, (num_nz[i] + 1 + Ac.hasProps(DIAG) * (halo_offsets[num_consolidated_neighbors] - 1))*Ac.get_block_size());\n                work_col_indices = &(recv_col_indices);\n                work_row_offsets = &(recv_row_offsets[i]);\n                work_values = &(recv_values);\n            }\n            else\n            {\n                work_row_offsets = &(Ac.row_offsets);\n                work_col_indices = &(Ac.col_indices);\n                work_values = &(Ac.values);\n            }\n\n            //Put interior rows in place\n            amgx::thrust::copy(work_col_indices->begin() + (*work_row_offsets)[interior_offset],\n                            work_col_indices->begin() + (*work_row_offsets)[interior_offset + vertex_counts[i][0]],\n                            new_col_indices.begin() + new_row_offsets[interior_offset]);\n            cudaCheckError();\n            amgx::thrust::copy(work_values->begin() + (*work_row_offsets)[interior_offset]*Ac.get_block_size(),\n                            work_values->begin() + ((*work_row_offsets)[interior_offset + vertex_counts[i][0]])*Ac.get_block_size(),\n                            new_values.begin() + new_row_offsets[interior_offset]*Ac.get_block_size());\n            cudaCheckError();\n            //Put boundary rows in place\n            amgx::thrust::copy(work_col_indices->begin() + (*work_row_offsets)[boundary_offset],\n                            work_col_indices->begin() + (*work_row_offsets)[boundary_offset + vertex_counts[i][1]],\n                            new_col_indices.begin() + new_row_offsets[boundary_offset]);\n            cudaCheckError();\n            amgx::thrust::copy(work_values->begin() + (*work_row_offsets)[boundary_offset]*Ac.get_block_size(),\n                            work_values->begin() + ((*work_row_offsets)[boundary_offset + vertex_counts[i][1]])*Ac.get_block_size(),\n                            new_values.begin() + new_row_offsets[boundary_offset]*Ac.get_block_size());\n            cudaCheckError();\n            //Process halo rows (merge)\n            int size = halo_offsets[num_consolidated_neighbors] - halo_offsets[0];\n            const int block_size = 128;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (size - 1) / block_size + 1);\n            //TODO: vectorise this kernel, will be inefficient for larger block sizes\n            append_halo_nz <<< num_blocks, block_size>>>(work_row_offsets->raw() + halo_offsets[0],\n                    new_row_offsets.raw() + halo_offsets[0],\n                    work_col_indices->raw(),\n                    new_col_indices.raw(),\n                    work_values->raw(),\n                    new_values.raw(),\n                    size, Ac.hasProps(DIAG), halo_offsets[0], Ac.get_block_size());\n            cudaCheckError();\n\n            // Diagonals\n            if (Ac.hasProps(DIAG))\n            {\n                // Diagonal corresponding to interior rows\n                amgx::thrust::copy(work_values->begin() + (num_nz[i] + interior_offset)*Ac.get_block_size(),\n                                work_values->begin() + (num_nz[i] + interior_offset + vertex_counts[i][0])*Ac.get_block_size(),\n                                new_values.begin() + (new_row_offsets[halo_offsets[halo_offsets.size() - 1]] + interior_offset)*Ac.get_block_size());\n                // Diagonal corresponding to boundary rows\n                amgx::thrust::copy(work_values->begin() + (num_nz[i] + boundary_offset)*Ac.get_block_size(),\n                                work_values->begin() + (num_nz[i] + boundary_offset + vertex_counts[i][1])*Ac.get_block_size(),\n                                new_values.begin() + (new_row_offsets[halo_offsets[halo_offsets.size() - 1]] + boundary_offset)*Ac.get_block_size());\n                cudaCheckError();\n            }\n\n            interior_offset += vertex_counts[i][0];\n            boundary_offset += vertex_counts[i][1];\n        }\n\n        Ac.set_initialized(0);\n        Ac.row_offsets = new_row_offsets;\n        Ac.col_indices = new_col_indices;\n        Ac.values = new_values;\n    }\n\n    // A new distributed communicator for coarse levels that only contains active partitions\n    // has already been created in consolidatedBookKeeping!\n\n    //\n    // Step 12 - finalizing, bookkeping\n    //\n    if (this->isRootPartition())\n    {\n        // int my_consolidated_id = fine_part_to_coarse_part[my_id];\n        int my_consolidated_id = Ac.manager->getComms()->get_global_id();\n\n        if (!this->isReuseLevel())\n        {\n             Ac.manager->initializeAfterConsolidation(\n                 my_consolidated_id,\n                 Ac,\n                this->m_consolidated_neighbors,\n                this->m_total_interior_rows_in_merged,\n                this->m_total_boundary_rows_in_merged,\n                this->m_num_all_aggregates,\n                this->m_consolidated_halo_offsets,\n                this->m_consolidated_B2L_maps,\n                1,\n                true);\n\n            // this is now stored in Acs DistributedManager\n            this->m_consolidated_neighbors.resize(0);\n            this->m_consolidated_halo_offsets.resize(0);\n            this->m_consolidated_B2L_maps.resize(0);\n\n            Ac.manager->B2L_rings.resize(num_consolidated_neighbors + 1);\n\n            for (int i = 0; i < num_consolidated_neighbors; i++)\n            {\n                Ac.manager->B2L_rings[i].resize(2);\n                Ac.manager->B2L_rings[i][0] = 0;\n                Ac.manager->B2L_rings[i][1] = consolidated_B2L_maps[i].size();\n            }\n        }\n\n        Ac.manager->set_initialized(Ac.row_offsets);\n        Ac.manager->getComms()->set_neighbors(num_consolidated_neighbors);\n        int new_nnz = Ac.row_offsets[Ac.row_offsets.size() - 1];\n\n        Ac.set_num_nz(new_nnz);\n        Ac.set_num_cols(Ac.manager->halo_offsets[Ac.manager->halo_offsets.size() - 1]);\n        Ac.set_num_rows(Ac.get_num_cols());\n\n        if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n        Ac.computeDiagonal();\n        Ac.set_initialized(1);\n    }\n    else\n    {\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.row_offsets);\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.col_indices);\n        this->getA().manager->getComms()->send_vector_wait_all(Ac.values);\n\n        Ac.set_initialized(0);\n        // set size of Ac to be zero\n        Ac.resize(0, 0, 0, 1);\n        Ac.set_initialized(1);\n    }\n}\n\n// -------------------------------------------------------------\n// Explicit instantiations\n// -------------------------------------------------------------\n\n#define AMGX_CASE_LINE(CASE) template class Aggregation_AMG_Level<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n\n}\n", "source": "/kaggle/working/amgx/src/aggregation/aggregation_amg_level.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/selectors/size2_selector.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n\n#include <async_event.h>\n#include <determinism_checker.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\n#define EXPERIMENTAL_ITERATIVE_MATCHING\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace size2_selector\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n#ifndef DELETE\n// Kernel to compute the weight of the edges with block_dia_csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeEdgeWeightsBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                   const ValueType *dia_values, const ValueType *nonzero_values, const IndexType num_block_rows, float *edge_weights, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            jcol = column_indices[j];\n\n            if (jcol >= num_block_rows) { continue; }\n\n            // Compute edge weight\n            for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n            {\n                if (column_indices[k] == tid)\n                {\n                    edge_weights[j] =  (float) 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                       / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n                    break;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n#endif\n\n// findStrongestNeighbour kernel for csr_matrix format\n// computes weight on the fly\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid findStrongestNeighbourCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                               const ValueType *values, const ValueType *diag, const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    ValueType weight;\n    int jcol;\n\n    while (tid < num_rows)\n    {\n        ValueType max_weight_unaggregated = 0.;\n        ValueType max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_rows) { continue; }\n\n                // Compute edge weight\n                //weight = fabs(values[j])/max( fabs(diag[tid]),fabs(diag[jcol]));\n                for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n                {\n                    if (column_indices[k] == tid)\n                    {\n                        weight = 0.5 * (fabs(values[j]) + fabs(values[k])) / max( fabs(diag[tid]), fabs(diag[jcol]));\n                        break;\n                    }\n                }\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                aggregates[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n            else\n            {\n                strongest_neighbour[tid] = tid;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// computes weight on the fly\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                       const ValueType *dia_values, const ValueType *nonzero_values, const IndexType num_block_rows, IndexType *aggregates, int *strongest_neighbour, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    ValueType weight;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        ValueType max_weight_unaggregated = 0.;\n        ValueType max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol >= num_block_rows) { continue; }\n\n                // Compute edge weight\n                for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n                {\n                    if (column_indices[k] == tid)\n                    {\n                        weight = 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                 / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n                        break;\n                    }\n                }\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n                // Put in same aggregate as strongest neighbour\n            {\n                aggregates[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n            else\n            {\n                strongest_neighbour[tid] = tid;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n__device__\nfloat random_weight2(int i, int j)\n{\n#define RAND_MULTIPLIER         1145637293\n    unsigned long i_min = (min(i, j) * RAND_MULTIPLIER);\n    unsigned long i_max = (max(i, j) * RAND_MULTIPLIER);\n    return ((float)i_min / i_max);\n}\n\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices,\n        float *edge_weights, IndexType num_block_rows, IndexType *aggregates,\n        IndexType *strongest_neighbour_1phase, IndexType *strongest_neighbour,\n        const size_t bsize, int phase, bool merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    float   weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        float   max_weight_unaggregated = 0.;\n        float   max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (phase == 1) { weight = edge_weights[j]; }\n                else { weight = random_weight2(tid, jcol); }\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                if (phase == 2 && strongest_neighbour_1phase[jcol] != tid) { continue; } // if 2nd phase only accept those who gave a hand on the 1st phase\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                if ( merge_singletons )\n                    // Put in same aggregate as strongest neighbour\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                if (phase == 2)\n                {\n                    float rand_w1 = random_weight2(tid, strongest_neighbour_1phase[tid]);\n                    strongest_neighbour[tid] = max_weight_unaggregated > rand_w1 ? strongest_unaggregated : strongest_neighbour_1phase[tid];\n                }\n                else { strongest_neighbour_1phase[tid] = strongest_unaggregated; }\n            }\n            else\n            {\n                if (phase == 2) { strongest_neighbour[tid] = strongest_neighbour_1phase[tid]; }\n                else { strongest_neighbour_1phase[tid] = tid; }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchEdges(const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n            potential_match_neighbour = strongest_neighbour[potential_match];\n\n            if (potential_match != -1 && potential_match_neighbour == tid) // we have a match\n            {\n                aggregates[tid] = ( potential_match > tid ) ? tid : potential_match;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, int block_size>\n__global__\nvoid countAggregates(const IndexType num_rows, IndexType *aggregates, int *num_unaggregated)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int c = 0;\n    int i = tid;\n\n    while ( i < num_rows )\n    {\n        c += ( aggregates[i] == -1 );\n        i += gridDim.x * blockDim.x;\n    }\n\n    __shared__ int smem[block_size];\n    smem[threadIdx.x] = c;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 1; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n        __syncthreads();\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        atomicAdd(num_unaggregated, smem[0]);\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// For csr_matrix_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeWithExistingAggregatesCsr(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *values,\n                                    const ValueType *diag, const int num_rows, IndexType *aggregates, int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    ValueType weight;\n\n    while (tid < num_rows)\n    {\n        ValueType max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_rows) { continue; }\n\n                // Compute edge weight\n                weight = fabs(values[j]) / max( fabs(diag[tid]), fabs(diag[jcol]));\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid joinExistingAggregates(IndexType num_rows, IndexType *aggregates, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n        {\n            aggregates[tid] = aggregates_candidate[tid];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Edge weights are computed on the fly\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *dia_values, const ValueType *nonzero_values,\n        const int num_block_rows, IndexType *aggregates, int bsize, int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    ValueType weight;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_aggregated = -1;\n        ValueType max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol >= num_block_rows) { continue; }\n\n                // Compute edge weight\n                weight = fabs(nonzero_values[j * bsize_sq]) / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Weights are read from edge_weights array\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices, const float *edge_weights,\n        const int num_block_rows, IndexType *aggregates, int bsize, const int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    float weight;\n\n    while (tid < num_block_rows)\n    {\n        float max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                // Compute edge weight\n                weight = edge_weights[j];\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }    // skip diagonal\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) //\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernelNoDiaProp(const IndexType *dia_idx, const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        diagonal[tIdx] = values[dia_idx[tIdx]];\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize2SelectorBase<T_Config>::Size2SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    two_phase = cfg.AMG_Config::template getParameter<int>(\"handshaking_phases\", cfg_scope) == 2;\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    merge_singletons = cfg.AMG_Config::template getParameter<int>(\"merge_singletons\", cfg_scope) == 1;\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\n// setAggregates for csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1(const Matrix_h &A,\n        typename Matrix_h::IVector &aggregates,  typename Matrix_h::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size2 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_h &A,\n        typename Matrix_h::IVector &aggregates, typename Matrix_h::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size2 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#ifndef DELETE\n// setAggregates for csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1(\n    const Matrix_d &A, typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(A.get_num_rows());\n    }\n\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    //typedef typename csr_matrix_d::index_type IndexType;\n    //typedef typename csr_matrix_d::value_type ValueType;\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueType *A_values_ptr = A.values.raw();\n    const IndexType *A_dia_ptr = A.diag.raw();\n    IndexType num_rows = (int)A.get_num_rows();\n    typename Matrix_d::IVector strongest_neighbour(num_rows, -1);\n    typename Matrix_d::MVector diag(num_rows, 0);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    ValueType *diag_ptr = diag.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows - 1) / threads_per_block + 1 );\n    getDiagonalKernelNoDiaProp <<< num_blocks, threads_per_block>>>(A_dia_ptr, A_values_ptr, num_rows, diag_ptr);\n    cudaCheckError();\n    int numUnassigned = num_rows;\n    int numUnassigned_previous = numUnassigned;\n    int icount = 0;\n\n    do\n    {\n        // For each row, find the strongest neighbour who hasn't been assigned\n        findStrongestNeighbourCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr, diag_ptr, num_rows, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n        matchEdges <<< num_blocks, threads_per_block>>>(num_rows, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    // Merge remaining vertices with current aggregates\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0 )\n        {\n            mergeWithExistingAggregatesCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr,\n                    diag_ptr, num_rows, aggregates_ptr, this->deterministic, (IndexType *) NULL);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n            cudaCheckError();\n        };\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_rows, -1);\n\n        while (numUnassigned != 0 )\n        {\n            mergeWithExistingAggregatesCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr,\n                    diag_ptr, num_rows, aggregates_ptr, this->deterministic, aggregates_candidate.raw());\n                    cudaCheckError();\n            // Sync here\n            joinExistingAggregates <<< num_blocks, threads_per_block>>>(num_rows, aggregates_ptr, aggregates_candidate.raw());\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n            cudaCheckError();\n        };\n\n        aggregates_candidate.resize(0);\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_rows, num_aggregates);\n}\n#endif\n\n// setAggregates for block_dia_csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(\n    const Matrix_d &A, typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    IndexType num_block_rows = (int) A.get_num_rows();\n    IndexType num_nonzero_blocks = (int) A.get_num_nz();\n    // both ways are supported\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    IndexType total_rows = (A.is_matrix_singleGPU()) ? A.get_num_rows() : A.manager->num_rows_all();\n    aggregates.resize(total_rows);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_1phase(num_block_rows, -1);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_block_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *strongest_neighbour_1phase_ptr = strongest_neighbour_1phase.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1 );\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks, -1);\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;//(this->two_phase ? rand_edge_weights.raw() : NULL);\n    // Compute the edge weights\n    cudaFuncSetCacheConfig(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>, cudaFuncCachePreferL1);\n    computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n    cudaCheckError();\n    cudaStream_t str = amgx::thrust::global_thread_handle::get_stream();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n    AsyncEvent *throttle_event = new AsyncEvent;\n    throttle_event->create();\n    // TODO: pinned host memory\n    typename Matrix_h::IVector h_unagg_vec(1);\n    typename Matrix_d::IVector d_unagg_vec(1);\n    int *unaggregated = h_unagg_vec.raw();\n    int *d_unaggregated = d_unagg_vec.raw();\n#endif\n    int icount, s = 1;\n    {\n        icount = 0;\n        float *weights_ptr = edge_weights_ptr;\n\n        do\n        {\n            if ( !this->two_phase )\n            {\n                // 1-phase handshaking\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                cudaCheckError();\n            }\n            else\n            {\n                // 2-phase handshaking\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                cudaCheckError();\n                // 2nd phase: for each block_row, find the strongest neighbour among those who gave hand on 1st phase\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 2, this->merge_singletons);\n                cudaCheckError();\n            }\n\n            // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n            matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr);\n            cudaCheckError();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n            s = (icount & 1);\n\n            if ( s == 0 )\n            {\n                // count unaggregated vertices\n                cudaError_t cuda_rc = cudaMemsetAsync(d_unaggregated, 0, sizeof(int), str);\n                if (cuda_rc != cudaSuccess)\n                {\n                    FatalError(\"cudaMemsetAsync failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n                }\n                countAggregates<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, d_unaggregated);\n                cudaCheckError();\n                cuda_rc = cudaMemcpyAsync(unaggregated, d_unaggregated, sizeof(int), cudaMemcpyDeviceToHost, str);\n                if (cuda_rc != cudaSuccess)\n                {\n                    FatalError(\"cudaMemcpyAsync failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n                }\n                throttle_event->record(str);\n            }\n            else\n            {\n                throttle_event->sync();\n                numUnassigned_previous = numUnassigned;\n                numUnassigned = *unaggregated;\n            }\n\n#else\n            cudaError_t cuda_rc = cudaStreamSynchronize(str);\n            if (cuda_rc != cudaSuccess)\n            {\n                FatalError(\"cudaStreamSynchronize failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n            }\n            numUnassigned_previous = numUnassigned;\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n            cudaCheckError();\n#endif\n            icount++;\n        }\n        while ( (s == 0) || !(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous));\n    }\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n    delete throttle_event;\n#endif\n\n    if ( this->merge_singletons )\n    {\n        // Merge remaining vertices with current aggregates\n        if (!this->deterministic)\n        {\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, (IndexType *) NULL);\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, aggregates_candidate.raw());\n                cudaCheckError();\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n\n            aggregates_candidate.resize(0);\n        }\n    }\n    else\n    {\n        //make singletons\n        aggregateSingletons <<< num_blocks, threads_per_block, 0, str>>>( aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n}\n\ntemplate<class T_Config>\nvoid Size2SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblocks( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size2\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size2SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size2Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/selectors/size2_selector.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n\n#include <async_event.h>\n#include <determinism_checker.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\n#define EXPERIMENTAL_ITERATIVE_MATCHING\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace size2_selector\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n#ifndef DELETE\n// Kernel to compute the weight of the edges with block_dia_csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeEdgeWeightsBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                   const ValueType *dia_values, const ValueType *nonzero_values, const IndexType num_block_rows, float *edge_weights, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            jcol = column_indices[j];\n\n            if (jcol >= num_block_rows) { continue; }\n\n            // Compute edge weight\n            for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n            {\n                if (column_indices[k] == tid)\n                {\n                    edge_weights[j] =  (float) 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                       / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n                    break;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n#endif\n\n// findStrongestNeighbour kernel for csr_matrix format\n// computes weight on the fly\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid findStrongestNeighbourCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                               const ValueType *values, const ValueType *diag, const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    ValueType weight;\n    int jcol;\n\n    while (tid < num_rows)\n    {\n        ValueType max_weight_unaggregated = 0.;\n        ValueType max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_rows) { continue; }\n\n                // Compute edge weight\n                //weight = fabs(values[j])/max( fabs(diag[tid]),fabs(diag[jcol]));\n                for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n                {\n                    if (column_indices[k] == tid)\n                    {\n                        weight = 0.5 * (fabs(values[j]) + fabs(values[k])) / max( fabs(diag[tid]), fabs(diag[jcol]));\n                        break;\n                    }\n                }\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                aggregates[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n            else\n            {\n                strongest_neighbour[tid] = tid;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// computes weight on the fly\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                       const ValueType *dia_values, const ValueType *nonzero_values, const IndexType num_block_rows, IndexType *aggregates, int *strongest_neighbour, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    ValueType weight;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        ValueType max_weight_unaggregated = 0.;\n        ValueType max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol >= num_block_rows) { continue; }\n\n                // Compute edge weight\n                for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n                {\n                    if (column_indices[k] == tid)\n                    {\n                        weight = 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                 / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n                        break;\n                    }\n                }\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n                // Put in same aggregate as strongest neighbour\n            {\n                aggregates[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n            else\n            {\n                strongest_neighbour[tid] = tid;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n__device__\nfloat random_weight2(int i, int j)\n{\n#define RAND_MULTIPLIER         1145637293\n    unsigned long i_min = (min(i, j) * RAND_MULTIPLIER);\n    unsigned long i_max = (max(i, j) * RAND_MULTIPLIER);\n    return ((float)i_min / i_max);\n}\n\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices,\n        float *edge_weights, IndexType num_block_rows, IndexType *aggregates,\n        IndexType *strongest_neighbour_1phase, IndexType *strongest_neighbour,\n        const size_t bsize, int phase, bool merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    float   weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        float   max_weight_unaggregated = 0.;\n        float   max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (phase == 1) { weight = edge_weights[j]; }\n                else { weight = random_weight2(tid, jcol); }\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                if (phase == 2 && strongest_neighbour_1phase[jcol] != tid) { continue; } // if 2nd phase only accept those who gave a hand on the 1st phase\n\n                // Identify strongest aggregated and unaggregated neighbours\n                if (aggregates[jcol] == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                if ( merge_singletons )\n                    // Put in same aggregate as strongest neighbour\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                if (phase == 2)\n                {\n                    float rand_w1 = random_weight2(tid, strongest_neighbour_1phase[tid]);\n                    strongest_neighbour[tid] = max_weight_unaggregated > rand_w1 ? strongest_unaggregated : strongest_neighbour_1phase[tid];\n                }\n                else { strongest_neighbour_1phase[tid] = strongest_unaggregated; }\n            }\n            else\n            {\n                if (phase == 2) { strongest_neighbour[tid] = strongest_neighbour_1phase[tid]; }\n                else { strongest_neighbour_1phase[tid] = tid; }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchEdges(const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n            potential_match_neighbour = strongest_neighbour[potential_match];\n\n            if (potential_match != -1 && potential_match_neighbour == tid) // we have a match\n            {\n                aggregates[tid] = ( potential_match > tid ) ? tid : potential_match;\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, int block_size>\n__global__\nvoid countAggregates(const IndexType num_rows, IndexType *aggregates, int *num_unaggregated)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int c = 0;\n    int i = tid;\n\n    while ( i < num_rows )\n    {\n        c += ( aggregates[i] == -1 );\n        i += gridDim.x * blockDim.x;\n    }\n\n    __shared__ int smem[block_size];\n    smem[threadIdx.x] = c;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 1; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n        __syncthreads();\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        atomicAdd(num_unaggregated, smem[0]);\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// For csr_matrix_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeWithExistingAggregatesCsr(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *values,\n                                    const ValueType *diag, const int num_rows, IndexType *aggregates, int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    ValueType weight;\n\n    while (tid < num_rows)\n    {\n        ValueType max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_rows) { continue; }\n\n                // Compute edge weight\n                weight = fabs(values[j]) / max( fabs(diag[tid]), fabs(diag[jcol]));\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid joinExistingAggregates(IndexType num_rows, IndexType *aggregates, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n        {\n            aggregates[tid] = aggregates_candidate[tid];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Edge weights are computed on the fly\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices, const ValueType *dia_values, const ValueType *nonzero_values,\n        const int num_block_rows, IndexType *aggregates, int bsize, int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    ValueType weight;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_aggregated = -1;\n        ValueType max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol >= num_block_rows) { continue; }\n\n                // Compute edge weight\n                weight = fabs(nonzero_values[j * bsize_sq]) / max( fabs(dia_values[tid * bsize_sq]), fabs(dia_values[jcol * bsize_sq]));\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Weights are read from edge_weights array\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices, const float *edge_weights,\n        const int num_block_rows, IndexType *aggregates, int bsize, const int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    float weight;\n\n    while (tid < num_block_rows)\n    {\n        float max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                // Compute edge weight\n                weight = edge_weights[j];\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }    // skip diagonal\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) //\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernelNoDiaProp(const IndexType *dia_idx, const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        diagonal[tIdx] = values[dia_idx[tIdx]];\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize2SelectorBase<T_Config>::Size2SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    two_phase = cfg.AMG_Config::template getParameter<int>(\"handshaking_phases\", cfg_scope) == 2;\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    merge_singletons = cfg.AMG_Config::template getParameter<int>(\"merge_singletons\", cfg_scope) == 1;\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\n// setAggregates for csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1(const Matrix_h &A,\n        typename Matrix_h::IVector &aggregates,  typename Matrix_h::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size2 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_h &A,\n        typename Matrix_h::IVector &aggregates, typename Matrix_h::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size2 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n#ifndef DELETE\n// setAggregates for csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1(\n    const Matrix_d &A, typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(A.get_num_rows());\n    }\n\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    //typedef typename csr_matrix_d::index_type IndexType;\n    //typedef typename csr_matrix_d::value_type ValueType;\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const ValueType *A_values_ptr = A.values.raw();\n    const IndexType *A_dia_ptr = A.diag.raw();\n    IndexType num_rows = (int)A.get_num_rows();\n    typename Matrix_d::IVector strongest_neighbour(num_rows, -1);\n    typename Matrix_d::MVector diag(num_rows, 0);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    ValueType *diag_ptr = diag.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_rows - 1) / threads_per_block + 1 );\n    getDiagonalKernelNoDiaProp <<< num_blocks, threads_per_block>>>(A_dia_ptr, A_values_ptr, num_rows, diag_ptr);\n    cudaCheckError();\n    int numUnassigned = num_rows;\n    int numUnassigned_previous = numUnassigned;\n    int icount = 0;\n\n    do\n    {\n        // For each row, find the strongest neighbour who hasn't been assigned\n        findStrongestNeighbourCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr, diag_ptr, num_rows, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n        matchEdges <<< num_blocks, threads_per_block>>>(num_rows, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    // Merge remaining vertices with current aggregates\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0 )\n        {\n            mergeWithExistingAggregatesCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr,\n                    diag_ptr, num_rows, aggregates_ptr, this->deterministic, (IndexType *) NULL);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n            cudaCheckError();\n        };\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_rows, -1);\n\n        while (numUnassigned != 0 )\n        {\n            mergeWithExistingAggregatesCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_values_ptr,\n                    diag_ptr, num_rows, aggregates_ptr, this->deterministic, aggregates_candidate.raw());\n                    cudaCheckError();\n            // Sync here\n            joinExistingAggregates <<< num_blocks, threads_per_block>>>(num_rows, aggregates_ptr, aggregates_candidate.raw());\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_rows, -1);\n            cudaCheckError();\n        };\n\n        aggregates_candidate.resize(0);\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_rows, num_aggregates);\n}\n#endif\n\n// setAggregates for block_dia_csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size2Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(\n    const Matrix_d &A, typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    IndexType num_block_rows = (int) A.get_num_rows();\n    IndexType num_nonzero_blocks = (int) A.get_num_nz();\n    // both ways are supported\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    IndexType total_rows = (A.is_matrix_singleGPU()) ? A.get_num_rows() : A.manager->num_rows_all();\n    aggregates.resize(total_rows);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_1phase(num_block_rows, -1);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_block_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *strongest_neighbour_1phase_ptr = strongest_neighbour_1phase.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1 );\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks, -1);\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;//(this->two_phase ? rand_edge_weights.raw() : NULL);\n    // Compute the edge weights\n    hipFuncSetCacheConfig(reinterpret_cast<const void*>(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>), hipFuncCachePreferL1);\n    computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, amgx::thrust::global_thread_handle::get_stream()>>>(A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n    cudaCheckError();\n    hipStream_t str = amgx::thrust::global_thread_handle::get_stream();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n    AsyncEvent *throttle_event = new AsyncEvent;\n    throttle_event->create();\n    // TODO: pinned host memory\n    typename Matrix_h::IVector h_unagg_vec(1);\n    typename Matrix_d::IVector d_unagg_vec(1);\n    int *unaggregated = h_unagg_vec.raw();\n    int *d_unaggregated = d_unagg_vec.raw();\n#endif\n    int icount, s = 1;\n    {\n        icount = 0;\n        float *weights_ptr = edge_weights_ptr;\n\n        do\n        {\n            if ( !this->two_phase )\n            {\n                // 1-phase handshaking\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                cudaCheckError();\n            }\n            else\n            {\n                // 2-phase handshaking\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                cudaCheckError();\n                // 2nd phase: for each block_row, find the strongest neighbour among those who gave hand on 1st phase\n                findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 2, this->merge_singletons);\n                cudaCheckError();\n            }\n\n            // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n            matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr);\n            cudaCheckError();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n            s = (icount & 1);\n\n            if ( s == 0 )\n            {\n                // count unaggregated vertices\n                hipError_t cuda_rc = hipMemsetAsync(d_unaggregated, 0, sizeof(int), str);\n                if (cuda_rc != hipSuccess)\n                {\n                    FatalError(\"hipMemsetAsync failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n                }\n                countAggregates<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, d_unaggregated);\n                cudaCheckError();\n                cuda_rc = hipMemcpyAsync(unaggregated, d_unaggregated, sizeof(int), hipMemcpyDeviceToHost, str);\n                if (cuda_rc != hipSuccess)\n                {\n                    FatalError(\"hipMemcpyAsync failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n                }\n                throttle_event->record(str);\n            }\n            else\n            {\n                throttle_event->sync();\n                numUnassigned_previous = numUnassigned;\n                numUnassigned = *unaggregated;\n            }\n\n#else\n            hipError_t cuda_rc = hipStreamSynchronize(str);\n            if (cuda_rc != hipSuccess)\n            {\n                FatalError(\"hipStreamSynchronize failed in size2_selector\", AMGX_ERR_CUDA_FAILURE);\n            }\n            numUnassigned_previous = numUnassigned;\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n            cudaCheckError();\n#endif\n            icount++;\n        }\n        while ( (s == 0) || !(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous));\n    }\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n    delete throttle_event;\n#endif\n\n    if ( this->merge_singletons )\n    {\n        // Merge remaining vertices with current aggregates\n        if (!this->deterministic)\n        {\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, (IndexType *) NULL);\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, aggregates_candidate.raw());\n                cudaCheckError();\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n\n            aggregates_candidate.resize(0);\n        }\n    }\n    else\n    {\n        //make singletons\n        aggregateSingletons <<< num_blocks, threads_per_block, 0, str>>>( aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n}\n\ntemplate<class T_Config>\nvoid Size2SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblocks( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size2\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size2SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size2Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/size2_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <thrust/count.h>\n#include <aggregation/selectors/parallel_greedy_selector.h>\n#include <sm_utils.inl>\n\nnamespace amgx\n{\nnamespace aggregation\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n__host__ __device__ int hash_function(int a, int seed = 17)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid compute_edge_weights_diag( const int  A_num_rows,\n                                const int  block_size,\n                                const int  num_owned,\n                                const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const int *__restrict A_diag,\n                                const Value_type *__restrict A_vals,\n                                Value_type *__restrict A_edge_weights )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Shared memory to broadcast columns.\n    __shared__ volatile int s_bcast_col[CTA_SIZE];\n    // Shared memory to store columns ji.\n    __shared__ volatile Value_type s_bcast_ji[CTA_SIZE];\n    // Constants.\n    // const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    // const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // The diagonal of (i,i).\n        Value_type row_diag = A_vals[block_size * A_diag[a_row_id]];\n        // Load the range of the row.\n        int a_col_begin = A_rows[a_row_id + 0];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Iterate over the columns of A.\n        for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n        {\n            // Each lane works on a different column.\n            const int a_col_it = a_col_begin + lane_id;\n            // Is it an active thread.\n            int is_active = a_col_it < a_col_end;\n            // Active nodes load column ids.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            s_bcast_col[threadIdx.x] = a_col_id;\n            // Reset ji coefficients.\n            s_bcast_ji[threadIdx.x] = Value_type(0);\n\n            // Iterate over columns to find ji coefficients if they exist.\n            for ( int k = 0, num_cols = __popc(utils::ballot(is_active)) ; k < num_cols ; ++k )\n            {\n                const int uniform_col_id = s_bcast_col[warp_id * WARP_SIZE + k];\n\n                // Skip columns whose id is greater than num_owned.\n                if ( uniform_col_id == -1 || uniform_col_id == a_row_id || uniform_col_id >= num_owned )\n                {\n                    continue;\n                }\n\n                // The bounds of the row.\n                int b_row_begin = A_rows[uniform_col_id + 0];\n                int b_row_end   = A_rows[uniform_col_id + 1];\n\n                // Iterate over the row.\n                for ( int not_found = 1 ; not_found && b_row_begin < b_row_end ; b_row_begin += WARP_SIZE )\n                {\n                    const int b_row_it = b_row_begin + lane_id;\n                    // Load the column id.\n                    int b_col_id = -1;\n\n                    if ( b_row_it < b_row_end )\n                    {\n                        b_col_id = A_cols[b_row_it];\n                    }\n\n                    // Has anyone found the column.\n                    not_found = utils::all( b_col_id != a_row_id );\n\n                    // If someone found the column, ask it to load the value.\n                    if ( b_col_id == a_row_id )\n                    {\n                        s_bcast_ji[warp_id * WARP_SIZE + k] = A_vals[block_size * b_row_it];\n                    }\n                }\n            }\n\n            // Deactivate \"invalid\" threads.\n            is_active = is_active && a_col_id < num_owned;\n            // The diagonal value associated with the column.\n            Value_type col_diag(0);\n\n            if ( is_active )\n            {\n                col_diag = A_vals[block_size * A_diag[a_col_id]];\n            }\n\n            // The value of the column.\n            Value_type col_val(0);\n\n            if ( is_active )\n            {\n                col_val = A_vals[block_size * a_col_it];\n            }\n\n            // Compute the denominator.\n            Value_type den = max( abs(row_diag), abs(col_diag) );\n            // Compute the weight of the edge.\n            Value_type weight(0);\n\n            if ( den != Value_type(0) )\n            {\n                weight = Value_type(0.5) * (abs(col_val) + abs(s_bcast_ji[threadIdx.x])) / den;\n            }\n\n            if ( is_active )\n            {\n                A_edge_weights[a_col_it] = weight;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid compute_ring_leader( const int A_num_rows,\n                          const int num_owned,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const int *__restrict is_aggregated,\n                          const int *__restrict in_leader_id,\n                          const int *__restrict in_leader_hash,\n                          int *__restrict out_leader_id,\n                          int *__restrict out_leader_hash )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // Skip already aggregated vertices.\n        if ( is_aggregated[a_row_id] )\n        {\n            continue;\n        }\n\n        // Load the range of the row.\n        int a_col_begin = A_rows[a_row_id + 0];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The max hash in my ith ring.\n        int my_min_id   = A_num_rows;\n        int my_max_hash = INT_MIN;\n\n        if ( in_leader_id )\n        {\n            my_min_id   = in_leader_id  [a_row_id];\n            my_max_hash = in_leader_hash[a_row_id];\n        }\n        else\n        {\n            my_min_id   = a_row_id;\n            my_max_hash = hash_function(a_row_id);\n        }\n\n        // Iterate over the columns of A.\n        for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n        {\n            // Each lane works on a different column.\n            const int a_col_it = a_col_begin + lane_id;\n            // Is it an active thread.\n            int is_active = a_col_it < a_col_end;\n            // Active nodes load column ids.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Compute the hash value if needed.\n            int col_min_id   = A_num_rows;\n            int col_max_hash = INT_MIN;\n\n            if ( in_leader_id )\n            {\n                if ( is_active && !is_aggregated[a_col_id] )\n                {\n                    col_min_id   = in_leader_id  [a_col_id];\n                    col_max_hash = in_leader_hash[a_col_id];\n                }\n            }\n            else\n            {\n                if ( is_active && !is_aggregated[a_col_id] )\n                {\n                    col_min_id   = a_col_id;\n                    col_max_hash = hash_function(a_col_id);\n                }\n            }\n\n            // Update the max_hash if needed.\n            if ( is_active && (my_max_hash < col_max_hash || (my_max_hash == col_max_hash && my_min_id >= col_min_id)) )\n            {\n                my_min_id   = col_min_id;\n                my_max_hash = col_max_hash;\n            }\n        }\n\n        // Reduce the max hash.\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            int other_min_id   = utils::shfl_xor(my_min_id, mask);\n            int other_max_hash = utils::shfl_xor(my_max_hash, mask);\n\n            if ( other_max_hash > my_max_hash || (other_max_hash == my_max_hash && other_min_id < my_min_id) )\n            {\n                my_min_id   = other_min_id;\n                my_max_hash = other_max_hash;\n            }\n        }\n\n        // The 1st thread stores the result.\n        if ( lane_id == 0 )\n        {\n            out_leader_id  [a_row_id] = my_min_id;\n            out_leader_hash[a_row_id] = my_max_hash;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid build_aggregates( const int num_rings,\n                       const int A_num_rows,\n                       const int num_owned,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       int *__restrict is_aggregated,\n                       const int *__restrict in_leader_id,\n                       const int *__restrict in_leader_hash,\n                       const Value_type *__restrict edge_weights,\n                       int *__restrict num_aggregates,\n                       int *__restrict aggregates,\n                       int *__restrict num_unaggregated)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // Skip already aggregated vertices.\n        if ( is_aggregated[a_row_id] )\n        {\n            continue;\n        }\n\n        // The max hash in my ith ring.\n        int my_hash = hash_function(a_row_id), my_max_hash = in_leader_hash[a_row_id];\n\n        // Skip if I'm not a local king.\n        if ( my_hash < my_max_hash || (my_hash == my_max_hash && a_row_id > in_leader_id[a_row_id]) )\n        {\n            continue;\n        }\n\n        // We start at the row.\n        int curr_row = a_row_id;\n\n        // Iterate to form the aggregate.\n        for ( int aggregate_size = 1, aggregate_id = -1 ; aggregate_size <= num_rings ; ++aggregate_size )\n        {\n            // Load the range of the row.\n            int a_col_begin = A_rows[curr_row + 0];\n            int a_col_end   = A_rows[curr_row + 1];\n            // The max weight.\n            int max_id = -1;\n            float max_weight = -1.0f;\n            // The max aggregated weight.\n            int max_aggregated_id = -1;\n            float max_aggregated_weight = -1.0f;\n\n            // Iterate over the columns of A.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n            {\n                // Each lane works on a different column.\n                const int a_col_it = a_col_begin + lane_id;\n                // Is it an active thread.\n                int is_active = a_col_it < a_col_end;\n                // Active nodes load column ids.\n                int a_col_id = -1;\n                float a_col_weight = -1.0f;\n\n                if ( is_active )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_col_weight = static_cast<float>(edge_weights[a_col_it]);\n                }\n\n                // Is the column aggregated?\n                int is_col_aggregated = is_active && is_aggregated[a_col_id];\n\n                // Select the column if it's not aggregated and its weight is better.\n                if ( is_active && !is_col_aggregated && a_col_weight > max_weight )\n                {\n                    max_id     = a_col_id;\n                    max_weight = a_col_weight;\n                }\n\n                // Update max aggregated weight if needed.\n                if ( is_active && is_col_aggregated && a_col_weight > max_aggregated_weight )\n                {\n                    max_aggregated_id     = a_col_id;\n                    max_aggregated_weight = a_col_weight;\n                }\n            }\n\n            // Is there a valid max_id.\n            int valid_max_id = utils::any(max_id != -1, utils::activemask());\n\n            // No valid max id?\n            if ( !valid_max_id && aggregate_size > 1 )\n            {\n                break;\n            }\n\n            // Find the max id.\n            if ( !valid_max_id )\n            {\n                max_id     = max_aggregated_id;\n                max_weight = max_aggregated_weight;\n            }\n\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                int   other_max_id     = utils::shfl_xor(max_id, mask);\n                float other_max_weight = utils::shfl_xor(max_weight, mask);\n\n                if ( other_max_weight > max_weight )\n                {\n                    max_id     = other_max_id;\n                    max_weight = other_max_weight;\n                }\n            }\n\n            // We know it's a singleton so merge with an existing aggregate.\n            if ( !valid_max_id )\n            {\n                if ( lane_id == 0 && max_id != -1 )\n                {\n                    is_aggregated[curr_row] = 1;\n                    aggregates[curr_row] = aggregates[max_id];\n                    atomicAdd(num_unaggregated, -1);\n                }\n\n                break;\n            }\n\n            // It's not a singleton but the 1st vertex in the aggregate.\n            if ( lane_id == 0 && aggregate_size == 1 )\n            {\n                aggregate_id = atomicAdd(num_aggregates, 1);\n                is_aggregated[a_row_id] = 1;\n                aggregates[a_row_id] = aggregate_id;\n                atomicAdd(num_unaggregated, -1);\n            }\n\n            // Set the aggregate of the winner.\n            if ( lane_id == 0 )\n            {\n                aggregates[max_id] = aggregate_id;\n                is_aggregated[max_id] = 1;\n                atomicAdd(num_unaggregated, -1);\n            }\n\n            // Set the next row to consider.\n            curr_row = max_id;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32 };\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_device, V, M, I> >::setAggregates_1x1( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_device, V, M, I> >::setAggregates_common_sqblocks( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{\n    const int num_rings = 4;\n    // The number of rows of A.\n    const int num_rows = A.get_num_rows();\n    // The size of the block.\n    const int block_size = A.get_block_dimx() * A.get_block_dimy();\n    // The number of threads per CTA.\n    const int CTA_SIZE = 128;\n    // The number of warps per CTA.\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // The number of CTAs in a grid where each thread is independent.\n    const int THREAD_GRID_SIZE = (A.get_num_rows() + CTA_SIZE - 1) / CTA_SIZE;\n    // The number of CTAs in a grid where each warp proceeds a row.\n    const int WARP_GRID_SIZE = (A.get_num_rows() + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA;\n    // Edge weights.\n    device_vector_alloc<ValueType> edge_weights(A.get_num_nz());\n    // Compute edge weights.\n    compute_edge_weights_diag<ValueType, 32, CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        block_size,\n        num_rows, // It should be num_owned!!!\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        A.diag.raw(),\n        A.values.raw(),\n        amgx::thrust::raw_pointer_cast( &edge_weights.front() ) );\n    cudaCheckError();\n    // Make sure there's enough room to store aggregates.\n    aggregates.resize(num_rows);\n    // The number of aggregates.\n    device_vector_alloc<int> dev_num_aggregates(1, 0), dev_num_unaggregated(1, num_rows);\n    // Is a vertex already aggregated.\n    device_vector_alloc<int> is_aggregated(num_rows, 0);\n    // Hash values.\n    device_vector_alloc<int> ring_leader_id0_array(num_rows);\n    device_vector_alloc<int> ring_leader_id1_array(num_rows);\n    device_vector_alloc<int> ring_leader_hash0_array(num_rows);\n    device_vector_alloc<int> ring_leader_hash1_array(num_rows);\n    // Is there any unaggregated vertex?\n    int num_unaggregated = num_rows;\n\n    // Iterate until all vertices are aggregated.\n    while ( num_unaggregated > 0 )\n    {\n        int *ring_leader_id0 = amgx::thrust::raw_pointer_cast(&ring_leader_id0_array.front());\n        int *ring_leader_id1 = amgx::thrust::raw_pointer_cast(&ring_leader_id1_array.front());\n        int *ring_leader_hash0 = amgx::thrust::raw_pointer_cast(&ring_leader_hash0_array.front());\n        int *ring_leader_hash1 = amgx::thrust::raw_pointer_cast(&ring_leader_hash1_array.front());\n\n        // Count N-ring of vertices.\n        for ( int i = 0 ; i < 2 * num_rings ; ++i )\n        {\n            compute_ring_leader<CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                num_rows, // It should be num_owned!!!\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast(&is_aggregated.front()),\n                i == 0 ? NULL : ring_leader_id0,\n                i == 0 ? NULL : ring_leader_hash0,\n                ring_leader_id1,\n                ring_leader_hash1);\n            cudaCheckError();\n            std::swap(ring_leader_id0, ring_leader_id1);\n            std::swap(ring_leader_hash0, ring_leader_hash1);\n        }\n\n        // Perform the assignments to aggregates.\n        build_aggregates<ValueType, CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n            num_rings,\n            num_rows,\n            num_rows, // It should be num_owned!!!\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast(&is_aggregated.front()),\n            ring_leader_id0,\n            ring_leader_hash0,\n            amgx::thrust::raw_pointer_cast(&edge_weights.front()),\n            amgx::thrust::raw_pointer_cast(&dev_num_aggregates.front()),\n            aggregates.raw(),\n            amgx::thrust::raw_pointer_cast(&dev_num_unaggregated.front()));\n        cudaCheckError();\n        // Number of aggregated vertices.\n        num_unaggregated = dev_num_unaggregated[0];\n    }\n\n    // The number of aggregates.\n    num_aggregates = dev_num_aggregates[0];\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_host, V, M, I> >::setAggregates_1x1( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_host, V, M, I> >::setAggregates_common_sqblocks( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< class T_Config >\nParallelGreedySelector_Base<T_Config>::ParallelGreedySelector_Base( AMG_Config &cfg, const std::string &cfg_scope ) :\n    Selector<T_Config>()\n{\n}\n\ntemplate< class T_Config >\nvoid ParallelGreedySelector_Base<T_Config>::setAggregates( MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{\n    setAggregates_common_sqblocks( A, aggregates, aggregates_global, num_aggregates );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class ParallelGreedySelector_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class ParallelGreedySelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace aggregation\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n#include <aggregation/selectors/parallel_greedy_selector.h>\n#include <sm_utils.inl>\n\nnamespace amgx\n{\nnamespace aggregation\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n__host__ __device__ int hash_function(int a, int seed = 17)\n{\n    a ^= seed;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid compute_edge_weights_diag( const int  A_num_rows,\n                                const int  block_size,\n                                const int  num_owned,\n                                const int *__restrict A_rows,\n                                const int *__restrict A_cols,\n                                const int *__restrict A_diag,\n                                const Value_type *__restrict A_vals,\n                                Value_type *__restrict A_edge_weights )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Shared memory to broadcast columns.\n    __shared__ volatile int s_bcast_col[CTA_SIZE];\n    // Shared memory to store columns ji.\n    __shared__ volatile Value_type s_bcast_ji[CTA_SIZE];\n    // Constants.\n    // const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    // const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // The diagonal of (i,i).\n        Value_type row_diag = A_vals[block_size * A_diag[a_row_id]];\n        // Load the range of the row.\n        int a_col_begin = A_rows[a_row_id + 0];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Iterate over the columns of A.\n        for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n        {\n            // Each lane works on a different column.\n            const int a_col_it = a_col_begin + lane_id;\n            // Is it an active thread.\n            int is_active = a_col_it < a_col_end;\n            // Active nodes load column ids.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            s_bcast_col[threadIdx.x] = a_col_id;\n            // Reset ji coefficients.\n            s_bcast_ji[threadIdx.x] = Value_type(0);\n\n            // Iterate over columns to find ji coefficients if they exist.\n            for ( int k = 0, num_cols = __popc(utils::ballot(is_active)) ; k < num_cols ; ++k )\n            {\n                const int uniform_col_id = s_bcast_col[warp_id * WARP_SIZE + k];\n\n                // Skip columns whose id is greater than num_owned.\n                if ( uniform_col_id == -1 || uniform_col_id == a_row_id || uniform_col_id >= num_owned )\n                {\n                    continue;\n                }\n\n                // The bounds of the row.\n                int b_row_begin = A_rows[uniform_col_id + 0];\n                int b_row_end   = A_rows[uniform_col_id + 1];\n\n                // Iterate over the row.\n                for ( int not_found = 1 ; not_found && b_row_begin < b_row_end ; b_row_begin += WARP_SIZE )\n                {\n                    const int b_row_it = b_row_begin + lane_id;\n                    // Load the column id.\n                    int b_col_id = -1;\n\n                    if ( b_row_it < b_row_end )\n                    {\n                        b_col_id = A_cols[b_row_it];\n                    }\n\n                    // Has anyone found the column.\n                    not_found = utils::all( b_col_id != a_row_id );\n\n                    // If someone found the column, ask it to load the value.\n                    if ( b_col_id == a_row_id )\n                    {\n                        s_bcast_ji[warp_id * WARP_SIZE + k] = A_vals[block_size * b_row_it];\n                    }\n                }\n            }\n\n            // Deactivate \"invalid\" threads.\n            is_active = is_active && a_col_id < num_owned;\n            // The diagonal value associated with the column.\n            Value_type col_diag(0);\n\n            if ( is_active )\n            {\n                col_diag = A_vals[block_size * A_diag[a_col_id]];\n            }\n\n            // The value of the column.\n            Value_type col_val(0);\n\n            if ( is_active )\n            {\n                col_val = A_vals[block_size * a_col_it];\n            }\n\n            // Compute the denominator.\n            Value_type den = max( abs(row_diag), abs(col_diag) );\n            // Compute the weight of the edge.\n            Value_type weight(0);\n\n            if ( den != Value_type(0) )\n            {\n                weight = Value_type(0.5) * (abs(col_val) + abs(s_bcast_ji[threadIdx.x])) / den;\n            }\n\n            if ( is_active )\n            {\n                A_edge_weights[a_col_it] = weight;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid compute_ring_leader( const int A_num_rows,\n                          const int num_owned,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const int *__restrict is_aggregated,\n                          const int *__restrict in_leader_id,\n                          const int *__restrict in_leader_hash,\n                          int *__restrict out_leader_id,\n                          int *__restrict out_leader_hash )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // Skip already aggregated vertices.\n        if ( is_aggregated[a_row_id] )\n        {\n            continue;\n        }\n\n        // Load the range of the row.\n        int a_col_begin = A_rows[a_row_id + 0];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The max hash in my ith ring.\n        int my_min_id   = A_num_rows;\n        int my_max_hash = INT_MIN;\n\n        if ( in_leader_id )\n        {\n            my_min_id   = in_leader_id  [a_row_id];\n            my_max_hash = in_leader_hash[a_row_id];\n        }\n        else\n        {\n            my_min_id   = a_row_id;\n            my_max_hash = hash_function(a_row_id);\n        }\n\n        // Iterate over the columns of A.\n        for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n        {\n            // Each lane works on a different column.\n            const int a_col_it = a_col_begin + lane_id;\n            // Is it an active thread.\n            int is_active = a_col_it < a_col_end;\n            // Active nodes load column ids.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Compute the hash value if needed.\n            int col_min_id   = A_num_rows;\n            int col_max_hash = INT_MIN;\n\n            if ( in_leader_id )\n            {\n                if ( is_active && !is_aggregated[a_col_id] )\n                {\n                    col_min_id   = in_leader_id  [a_col_id];\n                    col_max_hash = in_leader_hash[a_col_id];\n                }\n            }\n            else\n            {\n                if ( is_active && !is_aggregated[a_col_id] )\n                {\n                    col_min_id   = a_col_id;\n                    col_max_hash = hash_function(a_col_id);\n                }\n            }\n\n            // Update the max_hash if needed.\n            if ( is_active && (my_max_hash < col_max_hash || (my_max_hash == col_max_hash && my_min_id >= col_min_id)) )\n            {\n                my_min_id   = col_min_id;\n                my_max_hash = col_max_hash;\n            }\n        }\n\n        // Reduce the max hash.\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            int other_min_id   = utils::shfl_xor(my_min_id, mask);\n            int other_max_hash = utils::shfl_xor(my_max_hash, mask);\n\n            if ( other_max_hash > my_max_hash || (other_max_hash == my_max_hash && other_min_id < my_min_id) )\n            {\n                my_min_id   = other_min_id;\n                my_max_hash = other_max_hash;\n            }\n        }\n\n        // The 1st thread stores the result.\n        if ( lane_id == 0 )\n        {\n            out_leader_id  [a_row_id] = my_min_id;\n            out_leader_hash[a_row_id] = my_max_hash;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int WARP_SIZE >\n__global__\nvoid build_aggregates( const int num_rings,\n                       const int A_num_rows,\n                       const int num_owned,\n                       const int *__restrict A_rows,\n                       const int *__restrict A_cols,\n                       int *__restrict is_aggregated,\n                       const int *__restrict in_leader_id,\n                       const int *__restrict in_leader_hash,\n                       const Value_type *__restrict edge_weights,\n                       int *__restrict num_aggregates,\n                       int *__restrict aggregates,\n                       int *__restrict num_unaggregated)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE  / WARP_SIZE;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS_PER_CTA )\n    {\n        // Skip already aggregated vertices.\n        if ( is_aggregated[a_row_id] )\n        {\n            continue;\n        }\n\n        // The max hash in my ith ring.\n        int my_hash = hash_function(a_row_id), my_max_hash = in_leader_hash[a_row_id];\n\n        // Skip if I'm not a local king.\n        if ( my_hash < my_max_hash || (my_hash == my_max_hash && a_row_id > in_leader_id[a_row_id]) )\n        {\n            continue;\n        }\n\n        // We start at the row.\n        int curr_row = a_row_id;\n\n        // Iterate to form the aggregate.\n        for ( int aggregate_size = 1, aggregate_id = -1 ; aggregate_size <= num_rings ; ++aggregate_size )\n        {\n            // Load the range of the row.\n            int a_col_begin = A_rows[curr_row + 0];\n            int a_col_end   = A_rows[curr_row + 1];\n            // The max weight.\n            int max_id = -1;\n            float max_weight = -1.0f;\n            // The max aggregated weight.\n            int max_aggregated_id = -1;\n            float max_aggregated_weight = -1.0f;\n\n            // Iterate over the columns of A.\n            for ( ; a_col_begin < a_col_end ; a_col_begin += WARP_SIZE )\n            {\n                // Each lane works on a different column.\n                const int a_col_it = a_col_begin + lane_id;\n                // Is it an active thread.\n                int is_active = a_col_it < a_col_end;\n                // Active nodes load column ids.\n                int a_col_id = -1;\n                float a_col_weight = -1.0f;\n\n                if ( is_active )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_col_weight = static_cast<float>(edge_weights[a_col_it]);\n                }\n\n                // Is the column aggregated?\n                int is_col_aggregated = is_active && is_aggregated[a_col_id];\n\n                // Select the column if it's not aggregated and its weight is better.\n                if ( is_active && !is_col_aggregated && a_col_weight > max_weight )\n                {\n                    max_id     = a_col_id;\n                    max_weight = a_col_weight;\n                }\n\n                // Update max aggregated weight if needed.\n                if ( is_active && is_col_aggregated && a_col_weight > max_aggregated_weight )\n                {\n                    max_aggregated_id     = a_col_id;\n                    max_aggregated_weight = a_col_weight;\n                }\n            }\n\n            // Is there a valid max_id.\n            int valid_max_id = utils::any(max_id != -1, utils::activemask());\n\n            // No valid max id?\n            if ( !valid_max_id && aggregate_size > 1 )\n            {\n                break;\n            }\n\n            // Find the max id.\n            if ( !valid_max_id )\n            {\n                max_id     = max_aggregated_id;\n                max_weight = max_aggregated_weight;\n            }\n\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                int   other_max_id     = utils::shfl_xor(max_id, mask);\n                float other_max_weight = utils::shfl_xor(max_weight, mask);\n\n                if ( other_max_weight > max_weight )\n                {\n                    max_id     = other_max_id;\n                    max_weight = other_max_weight;\n                }\n            }\n\n            // We know it's a singleton so merge with an existing aggregate.\n            if ( !valid_max_id )\n            {\n                if ( lane_id == 0 && max_id != -1 )\n                {\n                    is_aggregated[curr_row] = 1;\n                    aggregates[curr_row] = aggregates[max_id];\n                    atomicAdd(num_unaggregated, -1);\n                }\n\n                break;\n            }\n\n            // It's not a singleton but the 1st vertex in the aggregate.\n            if ( lane_id == 0 && aggregate_size == 1 )\n            {\n                aggregate_id = atomicAdd(num_aggregates, 1);\n                is_aggregated[a_row_id] = 1;\n                aggregates[a_row_id] = aggregate_id;\n                atomicAdd(num_unaggregated, -1);\n            }\n\n            // Set the aggregate of the winner.\n            if ( lane_id == 0 )\n            {\n                aggregates[max_id] = aggregate_id;\n                is_aggregated[max_id] = 1;\n                atomicAdd(num_unaggregated, -1);\n            }\n\n            // Set the next row to consider.\n            curr_row = max_id;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32 };\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_device, V, M, I> >::setAggregates_1x1( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_device, V, M, I> >::setAggregates_common_sqblocks( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{\n    const int num_rings = 4;\n    // The number of rows of A.\n    const int num_rows = A.get_num_rows();\n    // The size of the block.\n    const int block_size = A.get_block_dimx() * A.get_block_dimy();\n    // The number of threads per CTA.\n    const int CTA_SIZE = 128;\n    // The number of warps per CTA.\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // The number of CTAs in a grid where each thread is independent.\n    const int THREAD_GRID_SIZE = (A.get_num_rows() + CTA_SIZE - 1) / CTA_SIZE;\n    // The number of CTAs in a grid where each warp proceeds a row.\n    const int WARP_GRID_SIZE = (A.get_num_rows() + NUM_WARPS_PER_CTA - 1) / NUM_WARPS_PER_CTA;\n    // Edge weights.\n    device_vector_alloc<ValueType> edge_weights(A.get_num_nz());\n    // Compute edge weights.\n    compute_edge_weights_diag<ValueType, 32, CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n        num_rows,\n        block_size,\n        num_rows, // It should be num_owned!!!\n        A.row_offsets.raw(),\n        A.col_indices.raw(),\n        A.diag.raw(),\n        A.values.raw(),\n        amgx::thrust::raw_pointer_cast( &edge_weights.front() ) );\n    cudaCheckError();\n    // Make sure there's enough room to store aggregates.\n    aggregates.resize(num_rows);\n    // The number of aggregates.\n    device_vector_alloc<int> dev_num_aggregates(1, 0), dev_num_unaggregated(1, num_rows);\n    // Is a vertex already aggregated.\n    device_vector_alloc<int> is_aggregated(num_rows, 0);\n    // Hash values.\n    device_vector_alloc<int> ring_leader_id0_array(num_rows);\n    device_vector_alloc<int> ring_leader_id1_array(num_rows);\n    device_vector_alloc<int> ring_leader_hash0_array(num_rows);\n    device_vector_alloc<int> ring_leader_hash1_array(num_rows);\n    // Is there any unaggregated vertex?\n    int num_unaggregated = num_rows;\n\n    // Iterate until all vertices are aggregated.\n    while ( num_unaggregated > 0 )\n    {\n        int *ring_leader_id0 = amgx::thrust::raw_pointer_cast(&ring_leader_id0_array.front());\n        int *ring_leader_id1 = amgx::thrust::raw_pointer_cast(&ring_leader_id1_array.front());\n        int *ring_leader_hash0 = amgx::thrust::raw_pointer_cast(&ring_leader_hash0_array.front());\n        int *ring_leader_hash1 = amgx::thrust::raw_pointer_cast(&ring_leader_hash1_array.front());\n\n        // Count N-ring of vertices.\n        for ( int i = 0 ; i < 2 * num_rings ; ++i )\n        {\n            compute_ring_leader<CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n                num_rows,\n                num_rows, // It should be num_owned!!!\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                amgx::thrust::raw_pointer_cast(&is_aggregated.front()),\n                i == 0 ? NULL : ring_leader_id0,\n                i == 0 ? NULL : ring_leader_hash0,\n                ring_leader_id1,\n                ring_leader_hash1);\n            cudaCheckError();\n            std::swap(ring_leader_id0, ring_leader_id1);\n            std::swap(ring_leader_hash0, ring_leader_hash1);\n        }\n\n        // Perform the assignments to aggregates.\n        build_aggregates<ValueType, CTA_SIZE, WARP_SIZE> <<< WARP_GRID_SIZE, CTA_SIZE>>>(\n            num_rings,\n            num_rows,\n            num_rows, // It should be num_owned!!!\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast(&is_aggregated.front()),\n            ring_leader_id0,\n            ring_leader_hash0,\n            amgx::thrust::raw_pointer_cast(&edge_weights.front()),\n            amgx::thrust::raw_pointer_cast(&dev_num_aggregates.front()),\n            aggregates.raw(),\n            amgx::thrust::raw_pointer_cast(&dev_num_unaggregated.front()));\n        cudaCheckError();\n        // Number of aggregated vertices.\n        num_unaggregated = dev_num_unaggregated[0];\n    }\n\n    // The number of aggregates.\n    num_aggregates = dev_num_aggregates[0];\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_host, V, M, I> >::setAggregates_1x1( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nParallelGreedySelector<TemplateConfig<AMGX_host, V, M, I> >::setAggregates_common_sqblocks( const MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< class T_Config >\nParallelGreedySelector_Base<T_Config>::ParallelGreedySelector_Base( AMG_Config &cfg, const std::string &cfg_scope ) :\n    Selector<T_Config>()\n{\n}\n\ntemplate< class T_Config >\nvoid ParallelGreedySelector_Base<T_Config>::setAggregates( MatrixType &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates )\n{\n    setAggregates_common_sqblocks( A, aggregates, aggregates_global, num_aggregates );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class ParallelGreedySelector_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class ParallelGreedySelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace aggregation\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/parallel_greedy_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n#include <aggregation/selectors/size8_selector.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/unique.h> //unique\n#include <thrust/remove.h> //remove\n#include <thrust/transform_scan.h> //transform_inclusive_scan\n\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\n#include <iostream>\n#include <strided_reduction.h>\nnamespace amgx\n{\n\n\nnamespace strided_reduction\n{\ntemplate<int STRIDE, class scalar_t, class OP>\nvoid count_block_results_pinned_memory(scalar_t *out_host, const int n_blocks, scalar_t *out_d, const OP &op = OP(), cudaStream_t stream = 0)\n{\n    strided_reduction_collect_partials<scalar_t, STRIDE, 32, OP> <<< 1, 32, 0, stream>>>(out_host, out_d, n_blocks);\n    cudaCheckError();\n}\n\n\ntemplate<class scalar_t, class OP>\nscalar_t count_block_results_pinned_memory(const int a, const int i, const int n_blocks, scalar_t *out_d, const OP &op = OP(), cudaStream_t stream = 0) //STRIDE=1 case\n{\n    static scalar_t *ret = 0;\n    static cudaEvent_t  throttle_event = 0;\n    const int buffers = 1;\n\n    if (ret == 0)\n    {\n        cudaError_t cuda_rc = amgx::memory::cudaMallocHost((void **)&ret, buffers * sizeof(scalar_t));\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaMallocHost failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        ret[0] = 0;\n        cudaEventCreateWithFlags(&throttle_event, cudaEventDisableTiming);\n    }\n\n    int ib = i % buffers;\n    count_block_results_pinned_memory<1, scalar_t, OP>(ret + ib, n_blocks, out_d, op, stream);\n\n    if (ib == buffers - 1)\n    {\n        cudaError_t cuda_rc = cudaEventRecord(throttle_event);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaEventRecord failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        cuda_rc = cudaEventSynchronize(throttle_event);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaEventSynchronize failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        scalar_t tot = 0;\n\n        for (int j = 0; j < buffers; j++)\n        {\n            tot += ret[j];\n        }\n\n        return tot + buffers - 1;\n    }\n    else\n    {\n        return -1;\n    }\n}\n\n\n}\n\nvoid analyze_coloring(device_vector_alloc<int> aggregates_d, device_vector_alloc<int> colors_d);\n\nnamespace aggregation\n{\nnamespace size8_selector\n{\n\n#include <aggregation/selectors/common_selector.h>\n\ntemplate <int NUM_COLS, typename IndexType>\n__global__  __launch_bounds__(256, 4)\nvoid my_findStrongestNeighbourBlockDiaCsr_NoMergeClean(\n    const IndexType *row_offsets, const IndexType *column_indices,\n    const float *edge_weights, const IndexType num_block_rows, const IndexType num_nonzero,\n    IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour,\n    IndexType *partner_index, float *weight_strongest_neighbour, int deterministic,\n    const IndexType *unassigned_rows,\n    const int num_unassigned_row)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    const int lane_id = utils::lane_id();\n    bool valid_tid = false;\n\n    for (; utils::any(valid_tid = tid < num_block_rows); tid += gridDim.x * blockDim.x)\n    {\n        int jmin = -NUM_COLS * 2, jmax = -NUM_COLS * 4;\n        float weight;\n        int jcol;\n        float max_weight_unaggregated = 0;\n        int strongest_unaggregated = -1;\n        bool is_unassigned = false;\n\n        if (valid_tid)\n        {\n            is_unassigned = (__ldcs(partner_index + tid) == -1);\n        }\n\n        if (is_unassigned) // Unaggregated row\n        {\n            jmin = __ldca(row_offsets + tid);\n            jmax = __ldca(row_offsets + tid + 1);\n        }\n\n        if (utils::any(is_unassigned))\n        {\n            int jj = jmin - amgx::strided_reduction::warp_loader<int, NUM_COLS>::align_shift(jmin);\n\n            for (; utils::any(jj < jmax, utils::activemask()); jj += NUM_COLS)\n            {\n                int   I[NUM_COLS];\n                float W[NUM_COLS];\n                int   P[NUM_COLS];\n                amgx::strided_reduction::warp_loader<int, NUM_COLS>::load(column_indices, jj, num_nonzero, I);\n\n#pragma unroll\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    int j = jj + i;\n                    jcol = I[i];\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        P[i] = __ldg(partner_index + jcol); //make this load ASAP\n                    }\n                }\n\n                amgx::strided_reduction::warp_loader<float, NUM_COLS>::load(edge_weights, jj, num_nonzero, W);\n\n#pragma unroll\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    weight = W[i];\n                    jcol = I[i];\n                    int j = jj + i;\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated)) // unaggregated\n                        {\n                            if (tid != jcol && P[i] == -1)\n                            {\n                                max_weight_unaggregated = weight;\n                                strongest_unaggregated = jcol;\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (strongest_unaggregated == -1) // All neighbours are aggregated\n            {\n                // Put in its own aggregate\n                if (!deterministic && is_unassigned)\n                {\n                    partner_index[tid] = tid;\n                }\n            }\n            else\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n        }\n    }\n}\n\n\n\n#define ALGORITHM_NOMERGE 0\n#define ALGORITHM_STOREWEIGHTS 1\n#define ALGORITHM_STOREWEIGHTS_2 2\n\n\ntemplate <int NUM_COLS, int ALGORITHM, int ASSUME_ALL_UNASSIGNED, int LOAD_ONLY_UNASSIGNED, typename IndexType>\n__global__  __launch_bounds__(256, 4)\nvoid my_findStrongestNeighbourBlockDiaCsr_NoMerge(\n    const IndexType *row_offsets, const IndexType *column_indices,\n    const float *edge_weights, const IndexType num_block_rows, const IndexType num_nonzero,\n    IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour,\n    IndexType *partner_index, float *weight_strongest_neighbour, int deterministic,\n    const IndexType *n_unassigned_per_block, const IndexType *unassigned_per_block\n    //const int num_unassigned_row\n    //const IndexType *unassigned_rows,\n)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bid = blockIdx.x;\n    const int lane_id = utils::lane_id();\n    bool valid_tid = false;\n\n    for (; utils::any( valid_tid = tid < num_block_rows); tid += gridDim.x * blockDim.x)\n    {\n        int jmin = -NUM_COLS * 2, jmax = -NUM_COLS * 4;\n        float weight;\n        int jcol;\n        float max_weight_unaggregated = 0;\n        int strongest_unaggregated = -1;\n        float max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n        int partner = -1;\n        int partner0, partner1, partner2;\n        int agg_jcol;\n        bool is_unassigned = false;\n        int  rowi = -1;\n\n        if (LOAD_ONLY_UNASSIGNED)\n        {\n            if (valid_tid)\n            {\n                rowi  = unassigned_per_block[tid];//unassigned_per_block+bid*256+threadIdx.x);\n                is_unassigned = (__ldg(partner_index + rowi) == -1);\n            }\n\n            if (is_unassigned)\n            {\n                jmin = __ldg(row_offsets + rowi);\n                jmax = __ldg(row_offsets + rowi + 1);\n            }\n        }\n        else\n        {\n            rowi = tid;\n\n            if (ALGORITHM == ALGORITHM_NOMERGE)\n            {\n                if (valid_tid) { is_unassigned = (__ldcs(partner_index + tid) == -1); }\n            }\n            else //ALGORITHM_STOREWEIGHTS or ALGORITHM_STOREWEIGHTS_2\n            {\n                if (valid_tid) { is_unassigned = (__ldcs(aggregated + tid) == -1); }\n            }\n\n            if (is_unassigned) // mind the else above\n            {\n                jmin = __ldg(row_offsets + rowi);\n                jmax = __ldlu(row_offsets + rowi + 1);\n            }\n        }\n\n        if (utils::any(is_unassigned))\n        {\n            if (is_unassigned) // Unaggregated row\n            {\n                if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                {\n                    partner  = partner_index[rowi];\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                {\n                    partner0 = partner_index[rowi];\n                    partner1 = partner_index[num_block_rows + rowi];\n                    partner2 = partner_index[2 * num_block_rows + rowi];\n                }\n            }\n\n            int jj = jmin - amgx::strided_reduction::warp_loader<int, NUM_COLS>::align_shift(jmin);\n\n            for (; utils::any(jj < jmax && jmax >= 0); jj += NUM_COLS)\n            {\n                int   I[NUM_COLS];\n                float W[NUM_COLS];\n                int   P[NUM_COLS];\n                int jj_ok = (jj >= 0 && jj < jmax && jmax >= 0) ? jj : 0;\n\n                amgx::strided_reduction::warp_loader<int, NUM_COLS>::load(column_indices, jj_ok, num_nonzero, I);\n#pragma unroll\n\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    int j = jj + i;\n                    jcol = I[i];\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (ALGORITHM == ALGORITHM_NOMERGE)\n                        {\n                            P[i] = __ldg(partner_index + jcol);    //make this load ASAP\n                        }\n                        else\n                        {\n                            P[i] = __ldg(aggregated + jcol);\n                        }\n                    }\n                }\n\n                amgx::strided_reduction::warp_loader<float, NUM_COLS>::load(edge_weights, jj_ok, num_nonzero, W);\n#pragma unroll\n\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    weight = W[i];\n                    jcol = I[i];\n                    int j = jj + i;\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (ALGORITHM == ALGORITHM_NOMERGE)\n                        {\n                            if (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated)) // unaggregated\n                            {\n                                if (rowi != jcol && P[i] == -1)\n                                {\n                                    max_weight_unaggregated = weight;\n                                    strongest_unaggregated = jcol;\n                                }\n                            }\n                        }\n                        else\n                        {\n                            bool partner_condition;\n\n                            if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                            {\n                                partner_condition = jcol != partner;\n                            }\n                            else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                            {\n                                partner_condition = jcol != partner0 && jcol != partner1 && jcol != partner2;\n                            }\n\n                            agg_jcol = P[i];\n\n                            if (partner_condition && rowi != jcol)\n                            {\n                                if (agg_jcol == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                                {\n                                    max_weight_unaggregated = weight;\n                                    strongest_unaggregated = jcol;\n                                }\n                                else if (agg_jcol != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // unaggregated\n                                {\n                                    max_weight_aggregated = weight;\n                                    strongest_aggregated = jcol;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (valid_tid && is_unassigned)\n            {\n                if (ALGORITHM == ALGORITHM_NOMERGE)\n                {\n                    if (strongest_unaggregated == -1) // All neighbours are aggregated\n                    {\n                        // Put in its own aggregate\n                        if (!deterministic)\n                        {\n                            partner_index[rowi] = rowi;\n                        }\n                    }\n                    else\n                    {\n                        strongest_neighbour[rowi] = strongest_unaggregated;\n                    }\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                {\n                    if (strongest_unaggregated == -1) // All neighbours are aggregated\n                    {\n                        if (!deterministic)\n                        {\n                            if (strongest_aggregated != -1)\n                            {\n                                aggregates[tid] = aggregates[strongest_aggregated];\n                                aggregated[tid] = 1;\n                                aggregates[partner] = aggregates[strongest_aggregated];\n                                aggregated[partner] = 1;\n                            }\n                            else  // leave in its own aggregate\n                            {\n                                aggregated[partner] = 1;\n                                aggregated[tid] = 1;\n                            }\n                        }\n                    }\n                    else // Found an unaggregated aggregate\n                    {\n                        weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n                    }\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                {\n                    if (strongest_unaggregated == -1 && strongest_aggregated != -1) // all neighbours are aggregated, store the strongest aggregated\n                    {\n                        weight_strongest_neighbour[tid] = -max_weight_aggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_aggregated];\n                    }\n                    else if (strongest_unaggregated != -1)\n                    {\n                        weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n                    }\n                }\n            }\n        }\n\n        bid += gridDim.x;\n    }\n}\n\n\n\n\n#define INST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(numcols, algo,assume,c) template __global__ void my_findStrongestNeighbourBlockDiaCsr_NoMerge<numcols,algo,assume,c>(\\\nconst int *row_offsets, const int *column_indices,\\\nconst float *edge_weights, const int num_block_rows,const int num_nonzero,\\\nint *aggregated, int *aggregates, int *strongest_neighbour,\\\nint *partner_index, float *weight_strongest_neighbour, int deterministic,\\\nconst int* n_unassigned_per_block, const int * unassigned_per_block);\n\n#define INSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(numcols) template __global__ void my_findStrongestNeighbourBlockDiaCsr_NoMergeClean<numcols,int>(\\\nconst int *row_offsets, const int *column_indices,\\\nconst float *edge_weights, const int num_block_rows,const int num_nonzero,\\\nint *aggregated, int *aggregates, int *strongest_neighbour,\\\nint *partner_index, float *weight_strongest_neighbour, int deterministic,\\\nconst int *unassigned_rows,\\\nconst int num_unassigned_row);\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\n\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(1)\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(2)\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(4)\n\ntemplate<int ALREADY_COMPACT>\n__global__ void my_blockCompact(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block)\n{\n    int bid = blockIdx.x; //RMV\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    for (; bid < num_rows / 256 + 1/*__any(tid < num_rows)*/; tid += gridDim.x * blockDim.x)\n    {\n        int row = tid;\n        bool no_partner = 0; //RMV\n\n        if (tid < num_rows)\n        {\n            if (ALREADY_COMPACT)\n            {\n                row = unassigned_per_block_in[tid];\n            }\n\n            if (partner_index[row] == -1) // Unaggregated row\n            {\n                no_partner = 1;\n            }\n        }\n\n        amgx::strided_reduction::block_binary_compaction<256, 32, 1>(\n            n_unassigned_per_block, unassigned_per_block, bid,\n            no_partner, row);\n        bid += gridDim.x;\n    }\n}\n\ntemplate __global__ void my_blockCompact<0>(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block);\ntemplate __global__ void my_blockCompact<1>(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block);\n\n__global__ void my_MatchEdgesWithCompaction(const int num_rows, int *partner_index, int *aggregates, const int *strongest_neighbour, int *sets_per_block,\n        int *unassigned_per_block_in, int *n_unassigned_per_block, int *unassigned_per_block\n                                           )\n{\n    int potential_match, potential_match_neighbour;\n    int warp_count = 0;\n    int bid = blockIdx.x; //RMV\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    for (; bid < num_rows / 1024 + 1/*__any(tid < num_rows)*/; tid += gridDim.x * blockDim.x)\n    {\n        int row = tid;\n        bool no_partner = 0;\n\n        if (tid < num_rows)\n        {\n            if (partner_index[row] == -1) // Unaggregated row\n            {\n                no_partner = 1;\n                potential_match = strongest_neighbour[row];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n\n                    if ( potential_match_neighbour == row ) // we have a match\n                    {\n                        no_partner = 0;\n                        //partner_notnull = 1;//RMV\n                        partner_index[row] = potential_match;\n                        aggregates[row] = ( potential_match > row ) ? row : potential_match;\n                    }\n                }\n            }\n        }\n\n        amgx::strided_reduction::block_binary_compaction<1024, 32, 1>(\n            n_unassigned_per_block, unassigned_per_block, bid,\n            no_partner, row);\n        warp_count += amgx::strided_reduction::warp_binary_count(no_partner);\n        bid += gridDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n__global__ void my_MatchEdges(const int num_rows, int *partner_index, int *aggregates, const int *strongest_neighbour, int *sets_per_block)\n{\n    int potential_match, potential_match_neighbour;\n    int warp_count = 0;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; utils::any(tid < num_rows); tid += gridDim.x * blockDim.x)\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows)\n        {\n            if (partner_index[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n\n                    if ( potential_match_neighbour == tid ) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        partner_index[tid] = potential_match;\n                        aggregates[tid] = ( potential_match > tid) ? tid : potential_match;\n                    }\n                }\n            }\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// matchEdges\n__global__ void my_joinExistingAggregates(int num_rows, int *aggregates, int *aggregated, int *aggregates_candidate, int *sets_per_block)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int warp_count = 0;\n\n    while  (utils::any(tid < num_rows))\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows)\n        {\n            if (aggregated[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n            {\n                aggregates[tid] = aggregates_candidate[tid];\n                aggregated[tid] = 1;\n                has_set_partner_index = 1;\n            }\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n        tid += gridDim.x * blockDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// Kernel that checks if perfect matchs exist\n__global__ void my_matchAggregates(int *aggregates, int *aggregated, int *strongest_neighbour, const int num_rows, int *sets_per_block)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour, my_aggregate;\n    int warp_count = 0;\n\n    while (utils::any(tid < num_rows))\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows) if (aggregated[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match); //or global\n                    my_aggregate = aggregates[tid];\n\n                    if (potential_match_neighbour == my_aggregate) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        aggregated[tid] = 1;\n                        aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                    }\n                }\n            }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n        tid += gridDim.x * blockDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// Kernel that checks if perfect matchs exist\n__global__ void my_matchAggregatesSize4(int *aggregates, int *aggregated, int *strongest_neighbour, int *partner_index, const int num_rows, int *sets_per_block)\n{\n    int potential_match, potential_match_neighbour, my_aggregate;\n    int warp_count = 0;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; utils::any(tid < num_rows); tid += blockDim.x * gridDim.x)\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows) if (aggregated[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n                    my_aggregate = aggregates[tid];\n\n                    if (potential_match_neighbour == my_aggregate) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        aggregated[tid] = 1;\n                        aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                        partner_index[tid + num_rows] = potential_match;\n                        partner_index[tid + 2 * num_rows] = partner_index[potential_match];\n                    }\n                }\n            }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_StoreWeight_2(const IndexType *row_offsets, const IndexType *column_indices,\n        const float *edge_weights, const IndexType num_block_rows, IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour, IndexType *partner_index, float *weight_strongest_neighbour)\n{\n    float weight;\n    int jcol, jmin, jmax;\n    int partner0, partner1, partner2;\n    int agg_jcol;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        float max_weight_unaggregated = 0.;\n        float max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregated[tid] == -1) // Unaggregated row\n        {\n            partner0 = partner_index[tid];\n            partner1 = partner_index[num_block_rows + tid];\n            partner2 = partner_index[2 * num_block_rows + tid];\n            jmin = row_offsets[tid];\n            jmax = row_offsets[tid + 1];\n\n            for (int j = jmin; j < jmax; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }\n\n                weight = edge_weights[j];\n                agg_jcol = aggregated[jcol];\n\n                if (jcol != partner0 && jcol != partner1 && jcol != partner2)\n                {\n                    if (agg_jcol == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                    {\n                        max_weight_unaggregated = weight;\n                        strongest_unaggregated = jcol;\n                    }\n                    else if (agg_jcol != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // unaggregated\n                    {\n                        max_weight_aggregated = weight;\n                        strongest_aggregated = jcol;\n                    }\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // all neighbours are aggregated, store the strongest aggregated\n            {\n                weight_strongest_neighbour[tid] = -max_weight_aggregated;\n                strongest_neighbour[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n            }\n        }\n    }\n}\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid agreeOnProposal_2(const IndexType *row_offsets, const IndexType *column_indices,\n                       IndexType num_block_rows, IndexType *aggregated, int *strongest_neighbour, float *weight_strongest_neighbour, IndexType *partner_index, int *aggregates, int deterministic)\n{\n    int partner[3];\n    float weight[3];\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        int strongest_partner = -1;\n        float my_weight = 0.;\n\n        if (aggregated[tid] == -1)\n        {\n            my_weight = weight_strongest_neighbour[tid];\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                partner[m] = partner_index[tid + m * num_block_rows];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                weight[m] = weight_strongest_neighbour[partner[m]];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                //if (weight[m] > my_weight && weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                if (weight[m] > my_weight)\n                {\n                    if (weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n                else if (weight[m] < my_weight)   // there is a partner without an unaggregated neighbour, whose neighbour is stronger than mine\n                {\n                    if (my_weight < 0.)\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n            }\n\n            if (my_weight < 0.) // means all neighbours of vertices in aggregate are aggregated, merge to another aggregate\n            {\n                if (!deterministic)\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = strongest_partner != -1 ? strongest_neighbour[partner[strongest_partner]] : strongest_neighbour[tid];\n                }\n            }\n            else if (strongest_partner != -1)   // store my partner's pick\n            {\n                strongest_neighbour[tid] = strongest_neighbour[partner[strongest_partner]];\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid agreeOnProposal_2_deterministic(int *strongest_neighbour_out, const IndexType *row_offsets, const IndexType *column_indices,\n                                     IndexType num_block_rows, IndexType *aggregated, int *strongest_neighbour, float *weight_strongest_neighbour, IndexType *partner_index, int *aggregates, int deterministic)\n{\n    int partner[3];\n    float weight[3];\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        int strongest_partner = -1;\n        float my_weight = 0.;\n        //copy here to avoid redundant copies before launching the kernel\n        int new_strongest_neighbour_out = strongest_neighbour[tid];\n\n        if (aggregated[tid] == -1)\n        {\n            my_weight = weight_strongest_neighbour[tid];\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                partner[m] = partner_index[tid + m * num_block_rows];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                weight[m] = weight_strongest_neighbour[partner[m]];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                if (weight[m] > my_weight)\n                {\n                    if (weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n                else if (weight[m] < my_weight)   // there is a partner without an unaggregated neighbour, whose neighbour is stronger than mine\n                {\n                    if (my_weight < 0.)\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n            }\n\n            if (my_weight < 0.) // means all neighbours of vertices in aggregate are aggregated, merge to another aggregate\n            {\n                if (!deterministic)\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = strongest_partner != -1 ? strongest_neighbour[partner[strongest_partner]] : strongest_neighbour[tid];\n                }\n            }\n            else if (strongest_partner != -1)   // store my partner's pick\n            {\n                new_strongest_neighbour_out = strongest_neighbour[partner[strongest_partner]];\n            }\n        }\n\n        //copy here to avoid redundant copies before launching the kernel\n        strongest_neighbour_out[tid] = new_strongest_neighbour_out;\n    }\n}\n\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchAggregatesSize4(IndexType *aggregates, IndexType *aggregated, IndexType *strongest_neighbour, IndexType *partner_index, const IndexType num_rows)\n{\n    int potential_match, potential_match_neighbour, my_aggregate;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_rows; tid += blockDim.x * gridDim.x)\n    {\n        if (aggregated[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n\n            if (potential_match != -1)\n            {\n                potential_match_neighbour = strongest_neighbour[potential_match];\n                my_aggregate = aggregates[tid];\n\n                if (potential_match_neighbour == my_aggregate) // we have a match\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                    partner_index[tid + num_rows] = potential_match;\n                    partner_index[tid + 2 * num_rows] = partner_index[potential_match];\n                }\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid assignUnassignedVertices_2(IndexType *partner_index, const IndexType num_rows)\n{\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_rows; tid += blockDim.x * gridDim.x)\n    {\n        if (partner_index[num_rows + tid] == -1) // Unaggregated row\n        {\n            partner_index[num_rows + tid] = tid;\n        }\n\n        if (partner_index[2 * num_rows + tid] == -1) // Unaggregated row\n        {\n            partner_index[2 * num_rows + tid] = tid;\n        }\n    }\n}\n\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize8SelectorBase<T_Config>::Size8SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size8Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_h &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size8 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <int NUM_COLS, typename IndexType, typename ValueType>\n__global__ //__launch_bounds__(256,2)\nvoid computeEdgeWeightsBlockDiaCsr_V2_1(\n    const IndexType *row_offsets,\n    //const int* __myrestrict row_offsets,\n    const IndexType *row_indices,\n    const IndexType *column_indices,\n    const IndexType *dia_values,\n    const ValueType *nonzero_values,\n    //const ValueType* __restrict nonzero_values,\n    const IndexType num_nonzero_blocks,\n    float *str_edge_weights, float *rand_edge_weights, int num_owned, int bsize, int component)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int i, j;\n    int bsize_sq = bsize * bsize;\n    float kvalue;\n    int matrix_weight_entry = component * bsize + component;\n    bool valid_tid;\n\n    while (utils::any(valid_tid = tid < num_nonzero_blocks))\n    {\n        i = -1;\n        double d1, d2, w1;\n\n        if (valid_tid)\n        {\n            if ( rand_edge_weights != NULL )\n            {\n                rand_edge_weights[tid] = random_weight(i, j, num_owned);\n            }\n\n            i  = row_indices[tid];\n            j  = column_indices[tid];\n            d1 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[__ldg(dia_values + i) * bsize_sq + matrix_weight_entry]));\n            d2 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[__ldg(dia_values + j) * bsize_sq + matrix_weight_entry]));\n        }\n\n        const bool valid_j = valid_tid && i != j && j < num_owned;\n        int ki = -1; //my transpose index, initialized to not found\n        //int diag_j = -1; //j diagonal index\n\n        if (!utils::any(valid_j))\n        {\n            continue;\n        }\n\n        int kmin = 0, kmax = 0;\n\n        if (valid_j)\n        {\n            kmin = __cachingLoad(&row_offsets[j  ]);\n            kmax = __cachingLoad(&row_offsets[j + 1]);\n        }\n\n        for ( int k = kmin ; k < kmax ; ++k )\n        {\n            const int idx = __ldg(column_indices + k);\n\n            if (idx == i)\n            {\n                ki = k;    //find the transpose ji\n            }\n        }\n\n        kvalue = 0.0f;\n\n        if (ki > -1)\n        {\n            kvalue = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[ki * bsize_sq + matrix_weight_entry]));\n        }\n\n        if (valid_tid)\n        {\n            w1 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[tid * bsize_sq + matrix_weight_entry]));\n            str_edge_weights[tid] = 0.5 * (w1 + kvalue) / ( (float) max(d1, d2) ) * valid_j;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n\ntemplate<class T, class M, class V>\nstruct async_size8_task : public task_cuda\n{\n    T *size8;\n    M *A;\n    V *aggregates;\n    V *aggregates_global;\n    int *num_aggregates;\n\n    typedef typename T::IndexType IndexType;\n\n    void run()\n    {\n        const IndexType nnz_per_row = (*A).get_num_nz() / (*A).get_num_rows();\n\n        if (0)\n        {\n        }\n        else if (nnz_per_row > 2)\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<4>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n        else if (nnz_per_row > 1)\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<4>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n        else\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<2>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n    }\n};\n\n\ntemplate<class T, class M, class V>\nasync_size8_task<T, M, V> *make_async_size8_task(T *size8, M &A, V &aggregates, V &aggregates_global, int &num_aggregates)\n{\n    async_size8_task<T, M, V> *ret = new async_size8_task<T, M, V>;\n    ret->size8 = size8;\n    ret->A = &A;\n    ret->aggregates = &aggregates;\n    ret->aggregates_global = &aggregates_global;\n    ret->num_aggregates = &num_aggregates;\n    static task_chain_cuda_streamset *ss = new task_chain_cuda_streamset(1);\n    task_chain_cuda *cr = new task_chain_cuda(ss);\n    cr->append(ret, asyncmanager::singleton()->main_thread_queue(2));\n    return ret;\n}\n\n#endif\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size8Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates,\n        typename Matrix_d::IVector &aggregates_global,\n        int &num_aggregates)\n{\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    bool task = false;\n    bool push = false;\n\n    if (task)\n    {\n        task_cuda *t = make_async_size8_task(this, A, aggregates, aggregates_global, num_aggregates);\n        enqueue_async_get_receipt(asyncmanager::singleton()->global_parallel_queue, t)->wait();\n        return;\n    }\n\n    cudaStream_t stream_old;\n    static cudaStream_t stream = 0;\n\n    if (push)\n    {\n        stream_old = amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()];\n        cudaError_t cuda_rc = cudaStreamSynchronize(amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()]);\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaStreamSynchronize failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n\n        if (stream == 0) { cudaStreamCreate(&stream); cudaCheckError(); }\n\n        amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()] = stream;\n    }\n\n#endif\n    const IndexType nnz_per_row = A.get_num_nz() / A.get_num_rows();\n\n    if (0)\n    {\n    }\n    else if (nnz_per_row > 2)\n    {\n        setAggregates_common_sqblock_avg_specialized<4>(A, aggregates, aggregates_global, num_aggregates);\n    }\n    else if (nnz_per_row > 1)\n    {\n        setAggregates_common_sqblock_avg_specialized<4>(A, aggregates, aggregates_global, num_aggregates);\n    }\n    else\n    {\n        setAggregates_common_sqblock_avg_specialized<2>(A, aggregates, aggregates_global, num_aggregates);\n    }\n\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n\n    if (push)\n    {\n        //cudaStreamSynchronize(stream);\n        cudaCheckError();\n        //amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()] = stream_old;\n    }\n\n#endif\n}\n\n// setAggregates for block_dia_csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate<int AVG_NNZ>\nvoid Size8Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >:: setAggregates_common_sqblock_avg_specialized(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    cudaStream_t stream = amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()];\n#else\n    cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n#endif\n    const IndexType num_block_rows = A.get_num_rows();\n    const IndexType num_nonzero_blocks = A.get_num_nz();\n\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(num_block_rows);\n    }\n\n    // Initially, put each vertex in its own aggregate\n    thrust_wrapper::sequence<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows);\n    cudaCheckError();\n    IndexType *aggregates_ptr = aggregates.raw();\n    // Create row_indices array\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    //const ValueType *A_dia_val_ptr = amgx::thrust::raw_pointer_cast(&A.values[A.get_block_size()*A.diagOffset()]);\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector partner_index(3 * num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_tmp(num_block_rows);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *partner_index_ptr = partner_index.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks + 8); //8-padded\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n    // Compute the edge weights\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    int avoid_thrust_count = 1;//0;\n    int newFindStrongest = 1;//0;\n    int newWeights = 1;//0;\n#else\n    int avoid_thrust_count = 0;//0;\n    int newFindStrongest = 0;//0;\n    int newWeights = 0;//0;\n#endif\n    int usenumassignedassumption = false;\n\n    if (newWeights == 0)\n    {\n        cudaFuncSetCacheConfig(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>, cudaFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, stream>>>(\n            A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n        cudaCheckError();\n    }\n    else\n    {\n        cudaFuncSetCacheConfig(computeEdgeWeightsBlockDiaCsr_V2_1<AVG_NNZ, IndexType, ValueType>, cudaFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2_1<AVG_NNZ, IndexType, ValueType> <<< num_blocks_V2, threads_per_block, 0, stream>>>(\n            A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component);\n        cudaCheckError();\n    }\n\n    // -------------------------------------------------\n    // First create aggregates of size 2\n    // -------------------------------------------------\n    int icount = 0;\n    const int num_blocks_1024 = std::min( 13 * 2, (num_block_rows - 1) / 1024 + 1);\n    device_vector_alloc<int> sets_per_block_t(num_blocks_1024);\n    int *sets_per_block = amgx::thrust::raw_pointer_cast(sets_per_block_t.data());\n    cudaCheckError();\n\n    do\n    {\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 1, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(\n                    A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  0, 0, strongest_neighbour_ptr, partner_index_ptr, 0, this->deterministic, 0, 0);\n                    cudaCheckError();\n            }\n            else\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 0, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(\n                    A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, 0, 0, strongest_neighbour_ptr, partner_index_ptr, 0, this->deterministic, 0, 0);\n                    cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_NoMerge <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, partner_index_ptr, strongest_neighbour_ptr, this->deterministic);\n        }\n\n        cudaCheckError();\n        // Look for perfect matches\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchEdges <<< num_blocks, threads_per_block, 0, stream>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(partner_index.begin(), partner_index.begin() + num_block_rows, -1);\n            cudaCheckError();\n        }\n        else\n        {\n            my_MatchEdges <<< num_blocks_1024, 1024, 0, stream>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(0, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    assignUnassignedVertices <<< num_blocks, threads_per_block, 0, stream>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 4\n    // -------------------------------------------------\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > weight_strongest_neighbour(num_block_rows, -1);\n    float *weight_strongest_neighbour_ptr = weight_strongest_neighbour.raw();\n    // At this point, partner index contain either your index or your neighbours index, depending on weither you're matched or not\n    // aggregates contain the largest vertex index of vertices in aggregate\n    typename Matrix_d::IVector aggregated(num_block_rows, -1);\n    IndexType *aggregated_ptr = aggregated.raw();\n    // now used as flag to check if aggregated or not\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 1, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n            else\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 0, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_StoreWeight <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic);\n        }\n\n        cudaCheckError();\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        agreeOnProposal <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchAggregatesSize4 <IndexType> <<< num_blocks, threads_per_block, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, partner_index_ptr, num_block_rows);\n            cudaCheckError();\n            numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        }\n        else\n        {\n            my_matchAggregatesSize4 <<< num_blocks_1024, 1024, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, partner_index_ptr, num_block_rows, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(1, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous) );\n\n    assignUnassignedVertices_2 <<< num_blocks, threads_per_block, 0, stream>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 8\n    // -------------------------------------------------\n    thrust_wrapper::fill<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n    cudaCheckError();\n    thrust_wrapper::fill<AMGX_device>(weight_strongest_neighbour.begin(), weight_strongest_neighbour.end(), -1.);\n    cudaCheckError();\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 1, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n            else\n            {\n                cudaFuncSetCacheConfig(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 0, 0, int>, cudaFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_StoreWeight_2 <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr);\n        }\n\n        cudaCheckError();\n\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        if (!this->deterministic)\n        {\n            agreeOnProposal_2 <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr, this->deterministic);\n            cudaCheckError();\n        }\n        else\n        {\n            //strongest_neighbour_tmp = strongest_neighbour; // copied that directly in the kernel\n            agreeOnProposal_2_deterministic <<< num_blocks, threads_per_block, 0, stream>>>(\n                strongest_neighbour_tmp.raw(),\n                A_row_offsets_ptr,\n                A_column_indices_ptr, num_block_rows,\n                aggregated_ptr, strongest_neighbour_ptr,\n                weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr, this->deterministic);\n                cudaCheckError();\n            strongest_neighbour_tmp.swap(strongest_neighbour);\n            strongest_neighbour_ptr = strongest_neighbour.raw(); //re-saving the correct pointer..\n        }\n\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchAggregates <IndexType> <<< num_blocks, threads_per_block, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows);\n            cudaCheckError();\n            numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        }\n        else\n        {\n            my_matchAggregates <<< num_blocks_1024, 1024, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(2, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned) );\n\n    // Merge remaining vertices with current aggregates\n    int local_iter = 0;\n\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, (IndexType *) NULL, local_iter > 1);\n            cudaCheckError();\n            numUnassigned_previous = numUnassigned;\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n            local_iter++;\n        }\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n        while (numUnassigned != 0)\n        {\n            // allow singletons only from the 2nd local iteration\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, aggregates_candidate.raw(), local_iter > 1);\n            cudaCheckError();\n            numUnassigned_previous = numUnassigned;\n\n            if (avoid_thrust_count == 0)\n            {\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, stream>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            }\n            else\n            {\n                my_joinExistingAggregates <<< num_blocks_1024, 1024, 0, stream>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw(), sets_per_block);\n                cudaCheckError();\n                numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(3, local_iter, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n            }\n\n            cudaCheckError();\n            local_iter++;\n        }\n\n        aggregates_candidate.resize(0);\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n    //analyze_coloring(aggregates, A.getMatrixColoring().getRowColors());\n}\n\ntemplate <class T_Config>\nvoid Size8SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblock( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size8Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size8SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size8Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n#include <aggregation/selectors/size8_selector.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/unique.h> //unique\n#include <thrust/remove.h> //remove\n#include <thrust/transform_scan.h> //transform_inclusive_scan\n\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\n#include <iostream>\n#include <strided_reduction.h>\nnamespace amgx\n{\n\n\nnamespace strided_reduction\n{\ntemplate<int STRIDE, class scalar_t, class OP>\nvoid count_block_results_pinned_memory(scalar_t *out_host, const int n_blocks, scalar_t *out_d, const OP &op = OP(), hipStream_t stream = 0)\n{\n    strided_reduction_collect_partials<scalar_t, STRIDE, 32, OP> <<< 1, 32, 0, stream>>>(out_host, out_d, n_blocks);\n    cudaCheckError();\n}\n\n\ntemplate<class scalar_t, class OP>\nscalar_t count_block_results_pinned_memory(const int a, const int i, const int n_blocks, scalar_t *out_d, const OP &op = OP(), hipStream_t stream = 0) //STRIDE=1 case\n{\n    static scalar_t *ret = 0;\n    static hipEvent_t  throttle_event = 0;\n    const int buffers = 1;\n\n    if (ret == 0)\n    {\n        hipError_t cuda_rc = amgx::memory::hipHostMalloc((void **)&ret, buffers * sizeof(scalar_t));\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipHostMalloc failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        ret[0] = 0;\n        hipEventCreateWithFlags(&throttle_event, hipEventDisableTiming);\n    }\n\n    int ib = i % buffers;\n    count_block_results_pinned_memory<1, scalar_t, OP>(ret + ib, n_blocks, out_d, op, stream);\n\n    if (ib == buffers - 1)\n    {\n        hipError_t cuda_rc = hipEventRecord(throttle_event);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipEventRecord failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        cuda_rc = hipEventSynchronize(throttle_event);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipEventSynchronize failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n        scalar_t tot = 0;\n\n        for (int j = 0; j < buffers; j++)\n        {\n            tot += ret[j];\n        }\n\n        return tot + buffers - 1;\n    }\n    else\n    {\n        return -1;\n    }\n}\n\n\n}\n\nvoid analyze_coloring(device_vector_alloc<int> aggregates_d, device_vector_alloc<int> colors_d);\n\nnamespace aggregation\n{\nnamespace size8_selector\n{\n\n#include <aggregation/selectors/common_selector.h>\n\ntemplate <int NUM_COLS, typename IndexType>\n__global__  __launch_bounds__(256, 4)\nvoid my_findStrongestNeighbourBlockDiaCsr_NoMergeClean(\n    const IndexType *row_offsets, const IndexType *column_indices,\n    const float *edge_weights, const IndexType num_block_rows, const IndexType num_nonzero,\n    IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour,\n    IndexType *partner_index, float *weight_strongest_neighbour, int deterministic,\n    const IndexType *unassigned_rows,\n    const int num_unassigned_row)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    const int lane_id = utils::lane_id();\n    bool valid_tid = false;\n\n    for (; utils::any(valid_tid = tid < num_block_rows); tid += gridDim.x * blockDim.x)\n    {\n        int jmin = -NUM_COLS * 2, jmax = -NUM_COLS * 4;\n        float weight;\n        int jcol;\n        float max_weight_unaggregated = 0;\n        int strongest_unaggregated = -1;\n        bool is_unassigned = false;\n\n        if (valid_tid)\n        {\n            is_unassigned = (__ldcs(partner_index + tid) == -1);\n        }\n\n        if (is_unassigned) // Unaggregated row\n        {\n            jmin = __ldca(row_offsets + tid);\n            jmax = __ldca(row_offsets + tid + 1);\n        }\n\n        if (utils::any(is_unassigned))\n        {\n            int jj = jmin - amgx::strided_reduction::warp_loader<int, NUM_COLS>::align_shift(jmin);\n\n            for (; utils::any(jj < jmax, utils::activemask()); jj += NUM_COLS)\n            {\n                int   I[NUM_COLS];\n                float W[NUM_COLS];\n                int   P[NUM_COLS];\n                amgx::strided_reduction::warp_loader<int, NUM_COLS>::load(column_indices, jj, num_nonzero, I);\n\n#pragma unroll\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    int j = jj + i;\n                    jcol = I[i];\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        P[i] = __ldg(partner_index + jcol); //make this load ASAP\n                    }\n                }\n\n                amgx::strided_reduction::warp_loader<float, NUM_COLS>::load(edge_weights, jj, num_nonzero, W);\n\n#pragma unroll\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    weight = W[i];\n                    jcol = I[i];\n                    int j = jj + i;\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated)) // unaggregated\n                        {\n                            if (tid != jcol && P[i] == -1)\n                            {\n                                max_weight_unaggregated = weight;\n                                strongest_unaggregated = jcol;\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (strongest_unaggregated == -1) // All neighbours are aggregated\n            {\n                // Put in its own aggregate\n                if (!deterministic && is_unassigned)\n                {\n                    partner_index[tid] = tid;\n                }\n            }\n            else\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;\n            }\n        }\n    }\n}\n\n\n\n#define ALGORITHM_NOMERGE 0\n#define ALGORITHM_STOREWEIGHTS 1\n#define ALGORITHM_STOREWEIGHTS_2 2\n\n\ntemplate <int NUM_COLS, int ALGORITHM, int ASSUME_ALL_UNASSIGNED, int LOAD_ONLY_UNASSIGNED, typename IndexType>\n__global__  __launch_bounds__(256, 4)\nvoid my_findStrongestNeighbourBlockDiaCsr_NoMerge(\n    const IndexType *row_offsets, const IndexType *column_indices,\n    const float *edge_weights, const IndexType num_block_rows, const IndexType num_nonzero,\n    IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour,\n    IndexType *partner_index, float *weight_strongest_neighbour, int deterministic,\n    const IndexType *n_unassigned_per_block, const IndexType *unassigned_per_block\n    //const int num_unassigned_row\n    //const IndexType *unassigned_rows,\n)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int bid = blockIdx.x;\n    const int lane_id = utils::lane_id();\n    bool valid_tid = false;\n\n    for (; utils::any( valid_tid = tid < num_block_rows); tid += gridDim.x * blockDim.x)\n    {\n        int jmin = -NUM_COLS * 2, jmax = -NUM_COLS * 4;\n        float weight;\n        int jcol;\n        float max_weight_unaggregated = 0;\n        int strongest_unaggregated = -1;\n        float max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n        int partner = -1;\n        int partner0, partner1, partner2;\n        int agg_jcol;\n        bool is_unassigned = false;\n        int  rowi = -1;\n\n        if (LOAD_ONLY_UNASSIGNED)\n        {\n            if (valid_tid)\n            {\n                rowi  = unassigned_per_block[tid];//unassigned_per_block+bid*256+threadIdx.x);\n                is_unassigned = (__ldg(partner_index + rowi) == -1);\n            }\n\n            if (is_unassigned)\n            {\n                jmin = __ldg(row_offsets + rowi);\n                jmax = __ldg(row_offsets + rowi + 1);\n            }\n        }\n        else\n        {\n            rowi = tid;\n\n            if (ALGORITHM == ALGORITHM_NOMERGE)\n            {\n                if (valid_tid) { is_unassigned = (__ldcs(partner_index + tid) == -1); }\n            }\n            else //ALGORITHM_STOREWEIGHTS or ALGORITHM_STOREWEIGHTS_2\n            {\n                if (valid_tid) { is_unassigned = (__ldcs(aggregated + tid) == -1); }\n            }\n\n            if (is_unassigned) // mind the else above\n            {\n                jmin = __ldg(row_offsets + rowi);\n                jmax = __ldlu(row_offsets + rowi + 1);\n            }\n        }\n\n        if (utils::any(is_unassigned))\n        {\n            if (is_unassigned) // Unaggregated row\n            {\n                if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                {\n                    partner  = partner_index[rowi];\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                {\n                    partner0 = partner_index[rowi];\n                    partner1 = partner_index[num_block_rows + rowi];\n                    partner2 = partner_index[2 * num_block_rows + rowi];\n                }\n            }\n\n            int jj = jmin - amgx::strided_reduction::warp_loader<int, NUM_COLS>::align_shift(jmin);\n\n            for (; utils::any(jj < jmax && jmax >= 0); jj += NUM_COLS)\n            {\n                int   I[NUM_COLS];\n                float W[NUM_COLS];\n                int   P[NUM_COLS];\n                int jj_ok = (jj >= 0 && jj < jmax && jmax >= 0) ? jj : 0;\n\n                amgx::strided_reduction::warp_loader<int, NUM_COLS>::load(column_indices, jj_ok, num_nonzero, I);\n#pragma unroll\n\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    int j = jj + i;\n                    jcol = I[i];\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (ALGORITHM == ALGORITHM_NOMERGE)\n                        {\n                            P[i] = __ldg(partner_index + jcol);    //make this load ASAP\n                        }\n                        else\n                        {\n                            P[i] = __ldg(aggregated + jcol);\n                        }\n                    }\n                }\n\n                amgx::strided_reduction::warp_loader<float, NUM_COLS>::load(edge_weights, jj_ok, num_nonzero, W);\n#pragma unroll\n\n                for (int i = 0; i < NUM_COLS; i++)\n                {\n                    weight = W[i];\n                    jcol = I[i];\n                    int j = jj + i;\n\n                    if (j >= jmin && j < jmax)\n                    {\n                        if (ALGORITHM == ALGORITHM_NOMERGE)\n                        {\n                            if (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated)) // unaggregated\n                            {\n                                if (rowi != jcol && P[i] == -1)\n                                {\n                                    max_weight_unaggregated = weight;\n                                    strongest_unaggregated = jcol;\n                                }\n                            }\n                        }\n                        else\n                        {\n                            bool partner_condition;\n\n                            if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                            {\n                                partner_condition = jcol != partner;\n                            }\n                            else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                            {\n                                partner_condition = jcol != partner0 && jcol != partner1 && jcol != partner2;\n                            }\n\n                            agg_jcol = P[i];\n\n                            if (partner_condition && rowi != jcol)\n                            {\n                                if (agg_jcol == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                                {\n                                    max_weight_unaggregated = weight;\n                                    strongest_unaggregated = jcol;\n                                }\n                                else if (agg_jcol != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // unaggregated\n                                {\n                                    max_weight_aggregated = weight;\n                                    strongest_aggregated = jcol;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (valid_tid && is_unassigned)\n            {\n                if (ALGORITHM == ALGORITHM_NOMERGE)\n                {\n                    if (strongest_unaggregated == -1) // All neighbours are aggregated\n                    {\n                        // Put in its own aggregate\n                        if (!deterministic)\n                        {\n                            partner_index[rowi] = rowi;\n                        }\n                    }\n                    else\n                    {\n                        strongest_neighbour[rowi] = strongest_unaggregated;\n                    }\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS)\n                {\n                    if (strongest_unaggregated == -1) // All neighbours are aggregated\n                    {\n                        if (!deterministic)\n                        {\n                            if (strongest_aggregated != -1)\n                            {\n                                aggregates[tid] = aggregates[strongest_aggregated];\n                                aggregated[tid] = 1;\n                                aggregates[partner] = aggregates[strongest_aggregated];\n                                aggregated[partner] = 1;\n                            }\n                            else  // leave in its own aggregate\n                            {\n                                aggregated[partner] = 1;\n                                aggregated[tid] = 1;\n                            }\n                        }\n                    }\n                    else // Found an unaggregated aggregate\n                    {\n                        weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n                    }\n                }\n                else if (ALGORITHM == ALGORITHM_STOREWEIGHTS_2)\n                {\n                    if (strongest_unaggregated == -1 && strongest_aggregated != -1) // all neighbours are aggregated, store the strongest aggregated\n                    {\n                        weight_strongest_neighbour[tid] = -max_weight_aggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_aggregated];\n                    }\n                    else if (strongest_unaggregated != -1)\n                    {\n                        weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                        strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n                    }\n                }\n            }\n        }\n\n        bid += gridDim.x;\n    }\n}\n\n\n\n\n#define INST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(numcols, algo,assume,c) template __global__ void my_findStrongestNeighbourBlockDiaCsr_NoMerge<numcols,algo,assume,c>(\\\nconst int *row_offsets, const int *column_indices,\\\nconst float *edge_weights, const int num_block_rows,const int num_nonzero,\\\nint *aggregated, int *aggregates, int *strongest_neighbour,\\\nint *partner_index, float *weight_strongest_neighbour, int deterministic,\\\nconst int* n_unassigned_per_block, const int * unassigned_per_block);\n\n#define INSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(numcols) template __global__ void my_findStrongestNeighbourBlockDiaCsr_NoMergeClean<numcols,int>(\\\nconst int *row_offsets, const int *column_indices,\\\nconst float *edge_weights, const int num_block_rows,const int num_nonzero,\\\nint *aggregated, int *aggregates, int *strongest_neighbour,\\\nint *partner_index, float *weight_strongest_neighbour, int deterministic,\\\nconst int *unassigned_rows,\\\nconst int num_unassigned_row);\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(1, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(2, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS_2, 0, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_STOREWEIGHTS_2, 1, 0)\nINST_my_findStrongestNeighbourBlockDiaCsr_NoMerge(4, ALGORITHM_NOMERGE, 0, 1) //load only unassigned\n\n\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(1)\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(2)\nINSTmy_findStrongestNeighbourBlockDiaCsr_NoMergeClean(4)\n\ntemplate<int ALREADY_COMPACT>\n__global__ void my_blockCompact(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block)\n{\n    int bid = blockIdx.x; //RMV\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    for (; bid < num_rows / 256 + 1/*__any(tid < num_rows)*/; tid += gridDim.x * blockDim.x)\n    {\n        int row = tid;\n        bool no_partner = 0; //RMV\n\n        if (tid < num_rows)\n        {\n            if (ALREADY_COMPACT)\n            {\n                row = unassigned_per_block_in[tid];\n            }\n\n            if (partner_index[row] == -1) // Unaggregated row\n            {\n                no_partner = 1;\n            }\n        }\n\n        amgx::strided_reduction::block_binary_compaction<256, 32, 1>(\n            n_unassigned_per_block, unassigned_per_block, bid,\n            no_partner, row);\n        bid += gridDim.x;\n    }\n}\n\ntemplate __global__ void my_blockCompact<0>(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block);\ntemplate __global__ void my_blockCompact<1>(\n    int *partner_index, const int num_rows,\n    int *unassigned_per_block_in,\n    int *n_unassigned_per_block, int *unassigned_per_block);\n\n__global__ void my_MatchEdgesWithCompaction(const int num_rows, int *partner_index, int *aggregates, const int *strongest_neighbour, int *sets_per_block,\n        int *unassigned_per_block_in, int *n_unassigned_per_block, int *unassigned_per_block\n                                           )\n{\n    int potential_match, potential_match_neighbour;\n    int warp_count = 0;\n    int bid = blockIdx.x; //RMV\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    for (; bid < num_rows / 1024 + 1/*__any(tid < num_rows)*/; tid += gridDim.x * blockDim.x)\n    {\n        int row = tid;\n        bool no_partner = 0;\n\n        if (tid < num_rows)\n        {\n            if (partner_index[row] == -1) // Unaggregated row\n            {\n                no_partner = 1;\n                potential_match = strongest_neighbour[row];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n\n                    if ( potential_match_neighbour == row ) // we have a match\n                    {\n                        no_partner = 0;\n                        //partner_notnull = 1;//RMV\n                        partner_index[row] = potential_match;\n                        aggregates[row] = ( potential_match > row ) ? row : potential_match;\n                    }\n                }\n            }\n        }\n\n        amgx::strided_reduction::block_binary_compaction<1024, 32, 1>(\n            n_unassigned_per_block, unassigned_per_block, bid,\n            no_partner, row);\n        warp_count += amgx::strided_reduction::warp_binary_count(no_partner);\n        bid += gridDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n__global__ void my_MatchEdges(const int num_rows, int *partner_index, int *aggregates, const int *strongest_neighbour, int *sets_per_block)\n{\n    int potential_match, potential_match_neighbour;\n    int warp_count = 0;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; utils::any(tid < num_rows); tid += gridDim.x * blockDim.x)\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows)\n        {\n            if (partner_index[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n\n                    if ( potential_match_neighbour == tid ) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        partner_index[tid] = potential_match;\n                        aggregates[tid] = ( potential_match > tid) ? tid : potential_match;\n                    }\n                }\n            }\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// matchEdges\n__global__ void my_joinExistingAggregates(int num_rows, int *aggregates, int *aggregated, int *aggregates_candidate, int *sets_per_block)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int warp_count = 0;\n\n    while  (utils::any(tid < num_rows))\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows)\n        {\n            if (aggregated[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n            {\n                aggregates[tid] = aggregates_candidate[tid];\n                aggregated[tid] = 1;\n                has_set_partner_index = 1;\n            }\n        }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n        tid += gridDim.x * blockDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// Kernel that checks if perfect matchs exist\n__global__ void my_matchAggregates(int *aggregates, int *aggregated, int *strongest_neighbour, const int num_rows, int *sets_per_block)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour, my_aggregate;\n    int warp_count = 0;\n\n    while (utils::any(tid < num_rows))\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows) if (aggregated[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match); //or global\n                    my_aggregate = aggregates[tid];\n\n                    if (potential_match_neighbour == my_aggregate) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        aggregated[tid] = 1;\n                        aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                    }\n                }\n            }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n        tid += gridDim.x * blockDim.x;\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n\n// Kernel that checks if perfect matchs exist\n__global__ void my_matchAggregatesSize4(int *aggregates, int *aggregated, int *strongest_neighbour, int *partner_index, const int num_rows, int *sets_per_block)\n{\n    int potential_match, potential_match_neighbour, my_aggregate;\n    int warp_count = 0;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; utils::any(tid < num_rows); tid += blockDim.x * gridDim.x)\n    {\n        bool has_set_partner_index = 0;\n\n        if (tid < num_rows) if (aggregated[tid] == -1) // Unaggregated row\n            {\n                potential_match = strongest_neighbour[tid];\n\n                if (potential_match != -1)\n                {\n                    potential_match_neighbour = __ldcs(strongest_neighbour + potential_match);\n                    my_aggregate = aggregates[tid];\n\n                    if (potential_match_neighbour == my_aggregate) // we have a match\n                    {\n                        has_set_partner_index = 1;\n                        aggregated[tid] = 1;\n                        aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                        partner_index[tid + num_rows] = potential_match;\n                        partner_index[tid + 2 * num_rows] = partner_index[potential_match];\n                    }\n                }\n            }\n\n        warp_count += amgx::strided_reduction::warp_binary_count(has_set_partner_index);\n    }\n\n    amgx::strided_reduction::block_count<1, 1024, 32, int>(warp_count, sets_per_block);\n}\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_StoreWeight_2(const IndexType *row_offsets, const IndexType *column_indices,\n        const float *edge_weights, const IndexType num_block_rows, IndexType *aggregated, IndexType *aggregates, int *strongest_neighbour, IndexType *partner_index, float *weight_strongest_neighbour)\n{\n    float weight;\n    int jcol, jmin, jmax;\n    int partner0, partner1, partner2;\n    int agg_jcol;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        float max_weight_unaggregated = 0.;\n        float max_weight_aggregated = 0.;\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n\n        if (aggregated[tid] == -1) // Unaggregated row\n        {\n            partner0 = partner_index[tid];\n            partner1 = partner_index[num_block_rows + tid];\n            partner2 = partner_index[2 * num_block_rows + tid];\n            jmin = row_offsets[tid];\n            jmax = row_offsets[tid + 1];\n\n            for (int j = jmin; j < jmax; j++)\n            {\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }\n\n                weight = edge_weights[j];\n                agg_jcol = aggregated[jcol];\n\n                if (jcol != partner0 && jcol != partner1 && jcol != partner2)\n                {\n                    if (agg_jcol == -1 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && jcol > strongest_unaggregated))) // unaggregated\n                    {\n                        max_weight_unaggregated = weight;\n                        strongest_unaggregated = jcol;\n                    }\n                    else if (agg_jcol != -1 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && jcol > strongest_aggregated))) // unaggregated\n                    {\n                        max_weight_aggregated = weight;\n                        strongest_aggregated = jcol;\n                    }\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // all neighbours are aggregated, store the strongest aggregated\n            {\n                weight_strongest_neighbour[tid] = -max_weight_aggregated;\n                strongest_neighbour[tid] = aggregates[strongest_aggregated];\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                weight_strongest_neighbour[tid] = max_weight_unaggregated;\n                strongest_neighbour[tid] = aggregates[strongest_unaggregated];\n            }\n        }\n    }\n}\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType>\n__global__\nvoid agreeOnProposal_2(const IndexType *row_offsets, const IndexType *column_indices,\n                       IndexType num_block_rows, IndexType *aggregated, int *strongest_neighbour, float *weight_strongest_neighbour, IndexType *partner_index, int *aggregates, int deterministic)\n{\n    int partner[3];\n    float weight[3];\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        int strongest_partner = -1;\n        float my_weight = 0.;\n\n        if (aggregated[tid] == -1)\n        {\n            my_weight = weight_strongest_neighbour[tid];\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                partner[m] = partner_index[tid + m * num_block_rows];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                weight[m] = weight_strongest_neighbour[partner[m]];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                //if (weight[m] > my_weight && weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                if (weight[m] > my_weight)\n                {\n                    if (weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n                else if (weight[m] < my_weight)   // there is a partner without an unaggregated neighbour, whose neighbour is stronger than mine\n                {\n                    if (my_weight < 0.)\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n            }\n\n            if (my_weight < 0.) // means all neighbours of vertices in aggregate are aggregated, merge to another aggregate\n            {\n                if (!deterministic)\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = strongest_partner != -1 ? strongest_neighbour[partner[strongest_partner]] : strongest_neighbour[tid];\n                }\n            }\n            else if (strongest_partner != -1)   // store my partner's pick\n            {\n                strongest_neighbour[tid] = strongest_neighbour[partner[strongest_partner]];\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid agreeOnProposal_2_deterministic(int *strongest_neighbour_out, const IndexType *row_offsets, const IndexType *column_indices,\n                                     IndexType num_block_rows, IndexType *aggregated, int *strongest_neighbour, float *weight_strongest_neighbour, IndexType *partner_index, int *aggregates, int deterministic)\n{\n    int partner[3];\n    float weight[3];\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_block_rows; tid += blockDim.x * gridDim.x)\n    {\n        int strongest_partner = -1;\n        float my_weight = 0.;\n        //copy here to avoid redundant copies before launching the kernel\n        int new_strongest_neighbour_out = strongest_neighbour[tid];\n\n        if (aggregated[tid] == -1)\n        {\n            my_weight = weight_strongest_neighbour[tid];\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                partner[m] = partner_index[tid + m * num_block_rows];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                weight[m] = weight_strongest_neighbour[partner[m]];\n            }\n\n#pragma unroll\n\n            for (int m = 0; m < 3; m++)\n            {\n                if (weight[m] > my_weight)\n                {\n                    if (weight[m] > 0.) // there is a partner that has an unaggregated neighbour\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n                else if (weight[m] < my_weight)   // there is a partner without an unaggregated neighbour, whose neighbour is stronger than mine\n                {\n                    if (my_weight < 0.)\n                    {\n                        strongest_partner = m;\n                        my_weight = weight[m];\n                    }\n                }\n            }\n\n            if (my_weight < 0.) // means all neighbours of vertices in aggregate are aggregated, merge to another aggregate\n            {\n                if (!deterministic)\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = strongest_partner != -1 ? strongest_neighbour[partner[strongest_partner]] : strongest_neighbour[tid];\n                }\n            }\n            else if (strongest_partner != -1)   // store my partner's pick\n            {\n                new_strongest_neighbour_out = strongest_neighbour[partner[strongest_partner]];\n            }\n        }\n\n        //copy here to avoid redundant copies before launching the kernel\n        strongest_neighbour_out[tid] = new_strongest_neighbour_out;\n    }\n}\n\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchAggregatesSize4(IndexType *aggregates, IndexType *aggregated, IndexType *strongest_neighbour, IndexType *partner_index, const IndexType num_rows)\n{\n    int potential_match, potential_match_neighbour, my_aggregate;\n\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_rows; tid += blockDim.x * gridDim.x)\n    {\n        if (aggregated[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n\n            if (potential_match != -1)\n            {\n                potential_match_neighbour = strongest_neighbour[potential_match];\n                my_aggregate = aggregates[tid];\n\n                if (potential_match_neighbour == my_aggregate) // we have a match\n                {\n                    aggregated[tid] = 1;\n                    aggregates[tid] = ( potential_match > my_aggregate) ? my_aggregate : potential_match;\n                    partner_index[tid + num_rows] = potential_match;\n                    partner_index[tid + 2 * num_rows] = partner_index[potential_match];\n                }\n            }\n        }\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid assignUnassignedVertices_2(IndexType *partner_index, const IndexType num_rows)\n{\n    for (int tid = threadIdx.x + blockDim.x * blockIdx.x; tid < num_rows; tid += blockDim.x * gridDim.x)\n    {\n        if (partner_index[num_rows + tid] == -1) // Unaggregated row\n        {\n            partner_index[num_rows + tid] = tid;\n        }\n\n        if (partner_index[2 * num_rows + tid] == -1) // Unaggregated row\n        {\n            partner_index[2 * num_rows + tid] = tid;\n        }\n    }\n}\n\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize8SelectorBase<T_Config>::Size8SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size8Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_h &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"Size8 selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <int NUM_COLS, typename IndexType, typename ValueType>\n__global__ //__launch_bounds__(256,2)\nvoid computeEdgeWeightsBlockDiaCsr_V2_1(\n    const IndexType *row_offsets,\n    //const int* __myrestrict row_offsets,\n    const IndexType *row_indices,\n    const IndexType *column_indices,\n    const IndexType *dia_values,\n    const ValueType *nonzero_values,\n    //const ValueType* __restrict nonzero_values,\n    const IndexType num_nonzero_blocks,\n    float *str_edge_weights, float *rand_edge_weights, int num_owned, int bsize, int component)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int i, j;\n    int bsize_sq = bsize * bsize;\n    float kvalue;\n    int matrix_weight_entry = component * bsize + component;\n    bool valid_tid;\n\n    while (utils::any(valid_tid = tid < num_nonzero_blocks))\n    {\n        i = -1;\n        double d1, d2, w1;\n\n        if (valid_tid)\n        {\n            if ( rand_edge_weights != NULL )\n            {\n                rand_edge_weights[tid] = random_weight(i, j, num_owned);\n            }\n\n            i  = row_indices[tid];\n            j  = column_indices[tid];\n            d1 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[__ldg(dia_values + i) * bsize_sq + matrix_weight_entry]));\n            d2 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[__ldg(dia_values + j) * bsize_sq + matrix_weight_entry]));\n        }\n\n        const bool valid_j = valid_tid && i != j && j < num_owned;\n        int ki = -1; //my transpose index, initialized to not found\n        //int diag_j = -1; //j diagonal index\n\n        if (!utils::any(valid_j))\n        {\n            continue;\n        }\n\n        int kmin = 0, kmax = 0;\n\n        if (valid_j)\n        {\n            kmin = __cachingLoad(&row_offsets[j  ]);\n            kmax = __cachingLoad(&row_offsets[j + 1]);\n        }\n\n        for ( int k = kmin ; k < kmax ; ++k )\n        {\n            const int idx = __ldg(column_indices + k);\n\n            if (idx == i)\n            {\n                ki = k;    //find the transpose ji\n            }\n        }\n\n        kvalue = 0.0f;\n\n        if (ki > -1)\n        {\n            kvalue = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[ki * bsize_sq + matrix_weight_entry]));\n        }\n\n        if (valid_tid)\n        {\n            w1 = types::util<ValueType>::abs(__cachingLoad(&nonzero_values[tid * bsize_sq + matrix_weight_entry]));\n            str_edge_weights[tid] = 0.5 * (w1 + kvalue) / ( (float) max(d1, d2) ) * valid_j;\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n\ntemplate<class T, class M, class V>\nstruct async_size8_task : public task_cuda\n{\n    T *size8;\n    M *A;\n    V *aggregates;\n    V *aggregates_global;\n    int *num_aggregates;\n\n    typedef typename T::IndexType IndexType;\n\n    void run()\n    {\n        const IndexType nnz_per_row = (*A).get_num_nz() / (*A).get_num_rows();\n\n        if (0)\n        {\n        }\n        else if (nnz_per_row > 2)\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<4>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n        else if (nnz_per_row > 1)\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<4>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n        else\n        {\n            size8->template setAggregates_common_sqblock_avg_specialized<2>(*A, *aggregates, *aggregates_global, *num_aggregates);\n        }\n    }\n};\n\n\ntemplate<class T, class M, class V>\nasync_size8_task<T, M, V> *make_async_size8_task(T *size8, M &A, V &aggregates, V &aggregates_global, int &num_aggregates)\n{\n    async_size8_task<T, M, V> *ret = new async_size8_task<T, M, V>;\n    ret->size8 = size8;\n    ret->A = &A;\n    ret->aggregates = &aggregates;\n    ret->aggregates_global = &aggregates_global;\n    ret->num_aggregates = &num_aggregates;\n    static task_chain_cuda_streamset *ss = new task_chain_cuda_streamset(1);\n    task_chain_cuda *cr = new task_chain_cuda(ss);\n    cr->append(ret, asyncmanager::singleton()->main_thread_queue(2));\n    return ret;\n}\n\n#endif\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size8Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates,\n        typename Matrix_d::IVector &aggregates_global,\n        int &num_aggregates)\n{\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    bool task = false;\n    bool push = false;\n\n    if (task)\n    {\n        task_cuda *t = make_async_size8_task(this, A, aggregates, aggregates_global, num_aggregates);\n        enqueue_async_get_receipt(asyncmanager::singleton()->global_parallel_queue, t)->wait();\n        return;\n    }\n\n    hipStream_t stream_old;\n    static hipStream_t stream = 0;\n\n    if (push)\n    {\n        stream_old = amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()];\n        hipError_t cuda_rc = hipStreamSynchronize(amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()]);\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipStreamSynchronize failed in size8_selector\", AMGX_ERR_CUDA_FAILURE);\n        }\n\n        if (stream == 0) { hipStreamCreate(&stream); cudaCheckError(); }\n\n        amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()] = stream;\n    }\n\n#endif\n    const IndexType nnz_per_row = A.get_num_nz() / A.get_num_rows();\n\n    if (0)\n    {\n    }\n    else if (nnz_per_row > 2)\n    {\n        setAggregates_common_sqblock_avg_specialized<4>(A, aggregates, aggregates_global, num_aggregates);\n    }\n    else if (nnz_per_row > 1)\n    {\n        setAggregates_common_sqblock_avg_specialized<4>(A, aggregates, aggregates_global, num_aggregates);\n    }\n    else\n    {\n        setAggregates_common_sqblock_avg_specialized<2>(A, aggregates, aggregates_global, num_aggregates);\n    }\n\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n\n    if (push)\n    {\n        //cudaStreamSynchronize(stream);\n        cudaCheckError();\n        //amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()] = stream_old;\n    }\n\n#endif\n}\n\n// setAggregates for block_dia_csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\ntemplate<int AVG_NNZ>\nvoid Size8Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >:: setAggregates_common_sqblock_avg_specialized(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    hipStream_t stream = amgx::thrust::global_thread_handle::threadStream[getCurrentThreadId()];\n#else\n    hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n#endif\n    const IndexType num_block_rows = A.get_num_rows();\n    const IndexType num_nonzero_blocks = A.get_num_nz();\n\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(num_block_rows);\n    }\n\n    // Initially, put each vertex in its own aggregate\n    thrust_wrapper::sequence<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows);\n    cudaCheckError();\n    IndexType *aggregates_ptr = aggregates.raw();\n    // Create row_indices array\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    //const ValueType *A_dia_val_ptr = amgx::thrust::raw_pointer_cast(&A.values[A.get_block_size()*A.diagOffset()]);\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector partner_index(3 * num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_tmp(num_block_rows);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *partner_index_ptr = partner_index.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks + 8); //8-padded\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n    // Compute the edge weights\n#if AMGX_ASYNCCPU_PROOF_OF_CONCEPT\n    int avoid_thrust_count = 1;//0;\n    int newFindStrongest = 1;//0;\n    int newWeights = 1;//0;\n#else\n    int avoid_thrust_count = 0;//0;\n    int newFindStrongest = 0;//0;\n    int newWeights = 0;//0;\n#endif\n    int usenumassignedassumption = false;\n\n    if (newWeights == 0)\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>), hipFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, stream>>>(\n            A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n        cudaCheckError();\n    }\n    else\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(computeEdgeWeightsBlockDiaCsr_V2_1<AVG_NNZ, IndexType, ValueType>), hipFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2_1<AVG_NNZ, IndexType, ValueType> <<< num_blocks_V2, threads_per_block, 0, stream>>>(\n            A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component);\n        cudaCheckError();\n    }\n\n    // -------------------------------------------------\n    // First create aggregates of size 2\n    // -------------------------------------------------\n    int icount = 0;\n    const int num_blocks_1024 = std::min( 13 * 2, (num_block_rows - 1) / 1024 + 1);\n    device_vector_alloc<int> sets_per_block_t(num_blocks_1024);\n    int *sets_per_block = amgx::thrust::raw_pointer_cast(sets_per_block_t.data());\n    cudaCheckError();\n\n    do\n    {\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 1, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(\n                    A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  0, 0, strongest_neighbour_ptr, partner_index_ptr, 0, this->deterministic, 0, 0);\n                    cudaCheckError();\n            }\n            else\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 0, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_NOMERGE, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(\n                    A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, 0, 0, strongest_neighbour_ptr, partner_index_ptr, 0, this->deterministic, 0, 0);\n                    cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_NoMerge <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, partner_index_ptr, strongest_neighbour_ptr, this->deterministic);\n        }\n\n        cudaCheckError();\n        // Look for perfect matches\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchEdges <<< num_blocks, threads_per_block, 0, stream>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(partner_index.begin(), partner_index.begin() + num_block_rows, -1);\n            cudaCheckError();\n        }\n        else\n        {\n            my_MatchEdges <<< num_blocks_1024, 1024, 0, stream>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(0, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    assignUnassignedVertices <<< num_blocks, threads_per_block, 0, stream>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 4\n    // -------------------------------------------------\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > weight_strongest_neighbour(num_block_rows, -1);\n    float *weight_strongest_neighbour_ptr = weight_strongest_neighbour.raw();\n    // At this point, partner index contain either your index or your neighbours index, depending on weither you're matched or not\n    // aggregates contain the largest vertex index of vertices in aggregate\n    typename Matrix_d::IVector aggregated(num_block_rows, -1);\n    IndexType *aggregated_ptr = aggregated.raw();\n    // now used as flag to check if aggregated or not\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 1, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n            else\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 0, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_StoreWeight <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic);\n        }\n\n        cudaCheckError();\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        agreeOnProposal <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchAggregatesSize4 <IndexType> <<< num_blocks, threads_per_block, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, partner_index_ptr, num_block_rows);\n            cudaCheckError();\n            numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        }\n        else\n        {\n            my_matchAggregatesSize4 <<< num_blocks_1024, 1024, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, partner_index_ptr, num_block_rows, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(1, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous) );\n\n    assignUnassignedVertices_2 <<< num_blocks, threads_per_block, 0, stream>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 8\n    // -------------------------------------------------\n    thrust_wrapper::fill<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n    cudaCheckError();\n    thrust_wrapper::fill<AMGX_device>(weight_strongest_neighbour.begin(), weight_strongest_neighbour.end(), -1.);\n    cudaCheckError();\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        if (newFindStrongest)\n        {\n            if (numUnassigned == num_block_rows && usenumassignedassumption)\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 1, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 1, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks,  aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n            else\n            {\n                hipFuncSetCacheConfig(reinterpret_cast<const void*>(my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 0, 0, int>), hipFuncCachePreferL1);\n                my_findStrongestNeighbourBlockDiaCsr_NoMerge<AVG_NNZ, ALGORITHM_STOREWEIGHTS_2, 0, 0, int> <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, num_nonzero_blocks, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic, 0, 0);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            findStrongestNeighbourBlockDiaCsr_StoreWeight_2 <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr);\n        }\n\n        cudaCheckError();\n\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        if (!this->deterministic)\n        {\n            agreeOnProposal_2 <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr, this->deterministic);\n            cudaCheckError();\n        }\n        else\n        {\n            //strongest_neighbour_tmp = strongest_neighbour; // copied that directly in the kernel\n            agreeOnProposal_2_deterministic <<< num_blocks, threads_per_block, 0, stream>>>(\n                strongest_neighbour_tmp.raw(),\n                A_row_offsets_ptr,\n                A_column_indices_ptr, num_block_rows,\n                aggregated_ptr, strongest_neighbour_ptr,\n                weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr, this->deterministic);\n                cudaCheckError();\n            strongest_neighbour_tmp.swap(strongest_neighbour);\n            strongest_neighbour_ptr = strongest_neighbour.raw(); //re-saving the correct pointer..\n        }\n\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n\n        if (avoid_thrust_count == 0)\n        {\n            matchAggregates <IndexType> <<< num_blocks, threads_per_block, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows);\n            cudaCheckError();\n            numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        }\n        else\n        {\n            my_matchAggregates <<< num_blocks_1024, 1024, 0, stream>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows, sets_per_block);\n            cudaCheckError();\n            numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(2, icount, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n        }\n\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned) );\n\n    // Merge remaining vertices with current aggregates\n    int local_iter = 0;\n\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, (IndexType *) NULL, local_iter > 1);\n            cudaCheckError();\n            numUnassigned_previous = numUnassigned;\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n            local_iter++;\n        }\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n        while (numUnassigned != 0)\n        {\n            // allow singletons only from the 2nd local iteration\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block, 0, stream>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, aggregates_candidate.raw(), local_iter > 1);\n            cudaCheckError();\n            numUnassigned_previous = numUnassigned;\n\n            if (avoid_thrust_count == 0)\n            {\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, stream>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            }\n            else\n            {\n                my_joinExistingAggregates <<< num_blocks_1024, 1024, 0, stream>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw(), sets_per_block);\n                cudaCheckError();\n                numUnassigned = numUnassigned_previous - amgx::strided_reduction::count_block_results_pinned_memory(3, local_iter, num_blocks_1024, sets_per_block, amgx::strided_reduction::op_sum(), stream);\n            }\n\n            cudaCheckError();\n            local_iter++;\n        }\n\n        aggregates_candidate.resize(0);\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n    //analyze_coloring(aggregates, A.getMatrixColoring().getRowColors());\n}\n\ntemplate <class T_Config>\nvoid Size8SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblock( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size8Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size8SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size8Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/size8_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/selectors/adaptive.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <matrix_analysis.h>\n#include <transpose.h>\n\n#include <async_event.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <thrust/host_vector.h>\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n#include <determinism_checker.h>\n#include <curand.h>\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace adaptive\n{\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid checkDiagonalDominance( const IndexType *row_offsets, const IndexType *col_indices, const ValueType *values, const IndexType *diag, IndexType *aggregates, int num_owned, int bsize, double diag_dom )\n{\n    //this time tid = i\n    int i = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while ( i < num_owned )\n    {\n        bool dd = false;\n\n        for ( int row = 0; row < bsize; row++ )\n        {\n            ValueType sum = 0.0;\n            int iimax = row_offsets[i + 1];\n\n            for ( int ii = row_offsets[i]; ii < iimax; ii++ )\n            {\n                //dont add the diagonal\n                if ( col_indices[ii] == i )\n                {\n                    for ( int col = 0; col < bsize; col++ )\n                    {\n                        sum += fabs( values[ii * bsize_sq + bsize * row + col] );\n\n                        if ( col == row )\n                        {\n                            continue;\n                        }\n                    }\n                }\n                else\n                {\n                    for ( int col = 0; col < bsize; col++ )\n                    {\n                        sum += fabs( values[ii * bsize_sq + bsize * row + col] );\n                    }\n                }\n            }\n\n            //check for DD condition and sort out\n            dd = sum <= diag_dom * fabs( values[diag[i] * bsize_sq + row * bsize + row] );\n\n            if ( !dd )\n            {\n                break;\n            }\n        }\n\n        if ( dd )\n        {\n            aggregates[i] = num_owned;\n        }\n\n        i += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid assignNodeToBin( const ValueType *x, IndexType *aggregates, ValueType min, ValueType max, IndexType numRows, IndexType numAggregates )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    double range = (max - min) / numAggregates;\n\n    while ( tid < numRows )\n    {\n        aggregates[tid] = floor( (x[tid] - min) / range );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename ValueType, typename IndexType>\n__global__\nvoid rescaleVector( ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = 2 * x[tid] - 1;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//    Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nAdaptiveSelectorBase<T_Config>::AdaptiveSelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    smoother = SolverFactory<T_Config>::allocate( cfg, cfg_scope, \"smoother\" );\n}\n/*\ntemplate <typename ValueType>\nvoid curandGenerateUniformWrapper( curandGenerator_t generator, ValueType *outputPtr, size_t num );\ntemplate<> void curandGenerateUniformWrapper<float>( curandGenerator_t generator, float *outputPtr, size_t num )\n{\n    curandGenerateUniform( generator, outputPtr, num);\n}\ntemplate<> void curandGenerateUniformWrapper<double>( curandGenerator_t generator, double *outputPtr, size_t num )\n{\n    curandGenerateUniformDouble( generator, outputPtr, num);\n}\n*/\n\ntemplate<class T_Config>\nvoid AdaptiveSelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &numAggregates)\n{\n    FatalError(\"Adaptive selector is still in development\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*\n        if (A.get_block_dimx() == A.get_block_dimy())\n        {\n            IndexType numRows = A.get_num_rows();\n\n\n            // This is how adaptive aggregation works:\n            // 1. init random x vector (random values would preferably be between -1 and 1) and 0 rhs\n            // 2. apply smoother a certain number of steps\n            // 3. solve \"binning problem\":\n            //      1. find min and max of x\n            //      2. create N*coarseningrate bins with linear range\n            //      3. assign nodes to bin, this is the final aggregates vector\n\n            // allocate\n            VVector& x = *Allocator<VVector>::allocate( numRows );\n            VVector& rhs = *Allocator<VVector>::allocate( numRows );\n\n            // initialize rhs\n            thrust_wrapper::fill( rhs.begin(), rhs.end(), 0.0 );\n\n            //compute random numbers\n            curandGenerator_t generator;\n            curandCreateGenerator(&generator, CURAND_RNG_PSEUDO_DEFAULT);\n            curandGenerateUniformWrapper(generator, x.raw(), numRows);\n\n            cudaStream_t str = 0;\n            const int threads_per_block = 256;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (numRows-1)/threads_per_block + 1 );\n            rescaleVector<<<num_blocks, threads_per_block, 0, str>>>( x.raw(), numRows );\n            cudaCheckError();\n\n\n            // smooth\n            smoother->setup( A, false );\n            smoother->set_max_iters( 15 ); //TODO: make this a parameter\n            smoother->solve( x, rhs, false );\n\n            //free rhs\n            Allocator<VVector>::free( &rhs, numRows );\n\n            //find min, max\n            ValueType min = amgx::thrust::reduce( x.begin(), x.end(), 0.0, amgx::thrust::minimum<ValueType>() );\n            ValueType max = amgx::thrust::reduce( x.begin(), x.end(), 0.0, amgx::thrust::maximum<ValueType>() );\n\n            //bin\n            aggregates.resize( numRows );\n            numAggregates = numRows / 4;\n            assignNodeToBin<<<num_blocks, threads_per_block, 0, str>>>( x.raw(), aggregates.raw(), min, max, numRows, numAggregates );\n            cudaCheckError();\n\n            //sync\n            cudaStreamSynchronize( str );\n            cudaCheckError();\n\n            //free\n            Allocator<VVector>::free( &x, numRows );\n\n            //fill \"gaps\" of empty bins\n            this->renumberAndCountAggregates(aggregates, aggregates_global, numRows, numAggregates);\n        }\n        else\n            FatalError(\"Unsupported block size for Adaptive Aggregation\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        */\n}\n\n// -------------------------\n//    Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class AdaptiveSelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class AdaptiveSelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/selectors/adaptive.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <matrix_analysis.h>\n#include <transpose.h>\n\n#include <async_event.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <thrust/host_vector.h>\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n#include <determinism_checker.h>\n#include <hiprand/hiprand.h>\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace adaptive\n{\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid checkDiagonalDominance( const IndexType *row_offsets, const IndexType *col_indices, const ValueType *values, const IndexType *diag, IndexType *aggregates, int num_owned, int bsize, double diag_dom )\n{\n    //this time tid = i\n    int i = threadIdx.x + blockDim.x * blockIdx.x;\n    int bsize_sq = bsize * bsize;\n\n    while ( i < num_owned )\n    {\n        bool dd = false;\n\n        for ( int row = 0; row < bsize; row++ )\n        {\n            ValueType sum = 0.0;\n            int iimax = row_offsets[i + 1];\n\n            for ( int ii = row_offsets[i]; ii < iimax; ii++ )\n            {\n                //dont add the diagonal\n                if ( col_indices[ii] == i )\n                {\n                    for ( int col = 0; col < bsize; col++ )\n                    {\n                        sum += fabs( values[ii * bsize_sq + bsize * row + col] );\n\n                        if ( col == row )\n                        {\n                            continue;\n                        }\n                    }\n                }\n                else\n                {\n                    for ( int col = 0; col < bsize; col++ )\n                    {\n                        sum += fabs( values[ii * bsize_sq + bsize * row + col] );\n                    }\n                }\n            }\n\n            //check for DD condition and sort out\n            dd = sum <= diag_dom * fabs( values[diag[i] * bsize_sq + row * bsize + row] );\n\n            if ( !dd )\n            {\n                break;\n            }\n        }\n\n        if ( dd )\n        {\n            aggregates[i] = num_owned;\n        }\n\n        i += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid assignNodeToBin( const ValueType *x, IndexType *aggregates, ValueType min, ValueType max, IndexType numRows, IndexType numAggregates )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    double range = (max - min) / numAggregates;\n\n    while ( tid < numRows )\n    {\n        aggregates[tid] = floor( (x[tid] - min) / range );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename ValueType, typename IndexType>\n__global__\nvoid rescaleVector( ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = 2 * x[tid] - 1;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//    Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nAdaptiveSelectorBase<T_Config>::AdaptiveSelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    smoother = SolverFactory<T_Config>::allocate( cfg, cfg_scope, \"smoother\" );\n}\n/*\ntemplate <typename ValueType>\nvoid curandGenerateUniformWrapper( curandGenerator_t generator, ValueType *outputPtr, size_t num );\ntemplate<> void curandGenerateUniformWrapper<float>( curandGenerator_t generator, float *outputPtr, size_t num )\n{\n    curandGenerateUniform( generator, outputPtr, num);\n}\ntemplate<> void curandGenerateUniformWrapper<double>( curandGenerator_t generator, double *outputPtr, size_t num )\n{\n    curandGenerateUniformDouble( generator, outputPtr, num);\n}\n*/\n\ntemplate<class T_Config>\nvoid AdaptiveSelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &numAggregates)\n{\n    FatalError(\"Adaptive selector is still in development\", AMGX_ERR_NOT_IMPLEMENTED);\n    /*\n        if (A.get_block_dimx() == A.get_block_dimy())\n        {\n            IndexType numRows = A.get_num_rows();\n\n\n            // This is how adaptive aggregation works:\n            // 1. init random x vector (random values would preferably be between -1 and 1) and 0 rhs\n            // 2. apply smoother a certain number of steps\n            // 3. solve \"binning problem\":\n            //      1. find min and max of x\n            //      2. create N*coarseningrate bins with linear range\n            //      3. assign nodes to bin, this is the final aggregates vector\n\n            // allocate\n            VVector& x = *Allocator<VVector>::allocate( numRows );\n            VVector& rhs = *Allocator<VVector>::allocate( numRows );\n\n            // initialize rhs\n            thrust_wrapper::fill( rhs.begin(), rhs.end(), 0.0 );\n\n            //compute random numbers\n            curandGenerator_t generator;\n            curandCreateGenerator(&generator, CURAND_RNG_PSEUDO_DEFAULT);\n            curandGenerateUniformWrapper(generator, x.raw(), numRows);\n\n            cudaStream_t str = 0;\n            const int threads_per_block = 256;\n            const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (numRows-1)/threads_per_block + 1 );\n            rescaleVector<<<num_blocks, threads_per_block, 0, str>>>( x.raw(), numRows );\n            cudaCheckError();\n\n\n            // smooth\n            smoother->setup( A, false );\n            smoother->set_max_iters( 15 ); //TODO: make this a parameter\n            smoother->solve( x, rhs, false );\n\n            //free rhs\n            Allocator<VVector>::free( &rhs, numRows );\n\n            //find min, max\n            ValueType min = amgx::thrust::reduce( x.begin(), x.end(), 0.0, amgx::thrust::minimum<ValueType>() );\n            ValueType max = amgx::thrust::reduce( x.begin(), x.end(), 0.0, amgx::thrust::maximum<ValueType>() );\n\n            //bin\n            aggregates.resize( numRows );\n            numAggregates = numRows / 4;\n            assignNodeToBin<<<num_blocks, threads_per_block, 0, str>>>( x.raw(), aggregates.raw(), min, max, numRows, numAggregates );\n            cudaCheckError();\n\n            //sync\n            cudaStreamSynchronize( str );\n            cudaCheckError();\n\n            //free\n            Allocator<VVector>::free( &x, numRows );\n\n            //fill \"gaps\" of empty bins\n            this->renumberAndCountAggregates(aggregates, aggregates_global, numRows, numAggregates);\n        }\n        else\n            FatalError(\"Unsupported block size for Adaptive Aggregation\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n        */\n}\n\n// -------------------------\n//    Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class AdaptiveSelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class AdaptiveSelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/adaptive.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <basic_types.h>\n#include <algorithm>\n#include <list>\n#include <cmath>\n#include <aggregation/selectors/geo_selector.h>\n#include <error.h>\n#include <types.h>\n#include <math.h>\n#include \"util.h\"\n\n#include <cutil.h>\n#include <util.h>\n#include <blas.h>\n#include <multiply.h>\n\n#include <matrix_analysis.h>\n\n#include<thrust/count.h> //count\n#include<thrust/sort.h> //sort\n#include<thrust/binary_search.h> //lower_bound\n#include<thrust/unique.h> //unique\n#include<cusp/detail/format_utils.h> //offsets_to_indices\n\n#define epsilon 1e-10\nnamespace amgx\n{\nnamespace aggregation\n{\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n//generate the aggregates labels\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid generatelabel1d(IndexType *aggregation, const ValueType *points, const double cord_min, const double cord_max, const int npoint, const int nlevel)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    double distance = 1.01 * (cord_max - cord_min);\n\n    for (; idx < npoint; idx += gridDim.x * blockDim.x)\n    {\n        aggregation[idx] = (int)((points[idx] - cord_min) / distance * nlevel);\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid generatelabel1dpxb(IndexType *aggregation, const int alpha, const ValueType *points, const double cord_min, const double cord_max, const int npoint, const int nlevel)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    double distance = 1.01 * (cord_max - cord_min);\n\n    for (; idx < npoint; idx += gridDim.x * blockDim.x)\n    {\n        aggregation[idx] = aggregation[idx] + alpha * (int)((points[idx] - cord_min) / distance * nlevel);\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid generatelabel3d(IndexType *aggregation, const int n, const int n1d, const int n1d_orig)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n1d2 = n1d_orig * n1d_orig;\n    int x, y, z;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        int label = idx % n1d2;\n        x = (label % n1d_orig) / 2;\n        y = (label / n1d_orig) / 2;\n        z = idx / n1d2 / 2;\n        aggregation[idx] = z * n1d * n1d + y * n1d + x;\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid generatelabel2d(IndexType *aggregation, const int n, const int n1d, const int n1d_orig)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int x, y;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        x = (idx % n1d_orig) / 2;\n        y = (idx / n1d_orig) / 2;\n        aggregation[idx] = y * n1d + x;\n    }\n}\n\n\ntemplate <typename IndexType >\n__global__\nvoid aggregateLevel(IndexType *indices, const IndexType *next_level, const int num_rows)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; idx < num_rows; idx += gridDim.x * blockDim.x)\n    {\n        indices[idx] = next_level[indices[idx]];\n    }\n}\n\n\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nGEO_SelectorBase<T_Config>::GEO_SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n}\n\ntemplate <class T_Config>\nbool compx (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.x < j.x); }\n\ntemplate <class T_Config>\nbool compy (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.y < j.y); }\n\ntemplate <class T_Config>\nbool compz (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.z < j.z); }\n\ntemplate <typename Tuple, typename GeoType, typename IndexType>\nstruct reduce_functor2\n{\n    __host__ __device__\n    Tuple operator()(const Tuple x, const Tuple y)\n    {\n        return amgx::thrust::make_tuple(amgx::thrust::get<0>(x) + amgx::thrust::get<0>(y),\n                                  amgx::thrust::get<1>(x) + amgx::thrust::get<1>(y),\n                                  amgx::thrust::get<2>(x) + amgx::thrust::get<2>(y)\n                                 );\n    }\n};\n\ntemplate <typename Tuple, typename GeoType, typename IndexType>\nstruct reduce_functor3\n{\n    __host__ __device__\n    Tuple operator()(const Tuple x, const Tuple y)\n    {\n        return amgx::thrust::make_tuple(amgx::thrust::get<0>(x) + amgx::thrust::get<0>(y),\n                                  amgx::thrust::get<1>(x) + amgx::thrust::get<1>(y),\n                                  amgx::thrust::get<2>(x) + amgx::thrust::get<2>(y),\n                                  amgx::thrust::get<3>(x) + amgx::thrust::get<3>(y)\n                                 );\n    }\n};\n\n// interpolates geometric info from the matrix using the matrix aggregation\ntemplate<class T_Config>\nvoid GEO_SelectorBase<T_Config>::interpolateGeoinfo( Matrix<T_Config> &A )\n{\n    const int threads_per_block = 512;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (A.get_num_rows() - 1) / threads_per_block + 1 );\n    ngeo_x.resize(A.get_num_rows());\n    ngeo_y.resize(A.get_num_rows());\n\n    if (this->dimension == 3)\n    {\n        ngeo_z.resize(A.get_num_rows());\n    }\n\n    if (!(A.hasParameter(\"aggregates_info\")) )\n    {\n        FatalError(\"Cannot find information about previous aggregations for GEO selector\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    std::vector<IVector *> *agg_info = A.template getParameterPtr< std::vector<IVector *> >(\"aggregates_info\");\n    IVector cur_geo_idx = *(agg_info->at(0));\n    IVector v_counter(this->num_origonal, 1), v_new_counter(A.get_num_rows(), 1), r_coord(A.get_num_rows());\n\n    if ( this->num_origonal != cur_geo_idx.size() )\n    {\n        FatalError(\"GEO size doesn't match original matrix dimension\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    for (size_t i = 1; i < agg_info->size(); i++)\n    {\n        aggregateLevel <<< num_blocks, threads_per_block>>>(cur_geo_idx.raw(), agg_info->at(i)->raw(), this->num_origonal);\n        cudaCheckError();\n    }\n\n    if (this->dimension == 2)\n    {\n        amgx::thrust::sort_by_key(cur_geo_idx.begin(), cur_geo_idx.end(), amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin())));\n        amgx::thrust::reduce_by_key(\n            cur_geo_idx.begin(),\n            cur_geo_idx.end(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), v_counter.begin())),\n            r_coord.begin(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(ngeo_x.begin(), ngeo_y.begin(), v_new_counter.begin())),\n            amgx::thrust::equal_to<int>(),\n            reduce_functor2< amgx::thrust::tuple< ValueType, ValueType, IndexType>, ValueType, IndexType > ()\n        );\n    }\n    else\n    {\n        amgx::thrust::sort_by_key(cur_geo_idx.begin(), cur_geo_idx.end(), amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), this->cord_z->begin())));\n        amgx::thrust::reduce_by_key(\n            cur_geo_idx.begin(),\n            cur_geo_idx.end(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), this->cord_z->begin(), v_counter.begin())),\n            r_coord.begin(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(ngeo_x.begin(), ngeo_y.begin(), ngeo_z.begin(), v_new_counter.begin())),\n            amgx::thrust::equal_to<int>(),\n            reduce_functor3< amgx::thrust::tuple< ValueType, ValueType, ValueType, IndexType>, ValueType, IndexType > ()\n        );\n    }\n\n    cudaCheckError();\n}\n\n// Choose the aggregates for csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1( Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    if (this->dimension == 0)\n    {\n        FatalError(\"No input geometric information, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    //initialize the aggregates vector\n    int n = A.get_num_rows();\n    aggregates.resize(n);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    int nlevel;\n\n    if (this->dimension == 2) { nlevel = floor(log(sqrt((double)n)) / log(2.0)); }\n\n    if (this->dimension == 3) { nlevel = ceil(log(cbrt((double)n)) / log(2.0)); }\n\n    int num_per_row = (int)std::pow(2, nlevel - 1);\n    const int threads_per_block = 512;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (n - 1) / threads_per_block + 1 );\n    // generate aggregation index in 1d\n    IndexType *aggregation_ptr = amgx::thrust::raw_pointer_cast(&aggregates[0]);\n\n    if (A.hasParameter(\"uniform_based\"))\n    {\n        printf(\"GEOAggregating %d-rows matrix as uniform\\n\", A.get_num_rows());\n        int npr_orig = 2 * num_per_row;\n\n        if (this->dimension == 3)\n        {\n            generatelabel3d <<< num_blocks, threads_per_block>>>(aggregation_ptr, n, num_per_row, npr_orig);\n            cudaCheckError();\n        }\n        else\n        {\n            generatelabel2d <<< num_blocks, threads_per_block>>>(aggregation_ptr, n, num_per_row, npr_orig);\n            cudaCheckError();\n        }\n    }\n    else\n    {\n        std::vector<VVector *> geo_ptrs(3);\n        // real geometry source\n        geo_ptrs[0] = this->cord_x;\n        geo_ptrs[1] = this->cord_y;\n        geo_ptrs[2] = this->cord_z;\n\n        // do we need to interpolate from the finest level?\n        if (A.get_num_rows() != this->num_origonal)\n        {\n            this->interpolateGeoinfo(A);\n\n            if (this->dimension == 3)\n            {\n                geo_ptrs[2] = &this->ngeo_z;\n            }\n\n            geo_ptrs[0] = &this->ngeo_x;\n            geo_ptrs[1] = &this->ngeo_y;\n        }\n\n        //Find the boundary coordinates\n        this->xmax = *amgx::thrust::max_element(geo_ptrs[0]->begin(), geo_ptrs[0]->end());\n        this->xmin = *amgx::thrust::min_element(geo_ptrs[0]->begin(), geo_ptrs[0]->end());\n        this->ymax = *amgx::thrust::max_element(geo_ptrs[1]->begin(), geo_ptrs[1]->end());\n        this->ymin = *amgx::thrust::min_element(geo_ptrs[1]->begin(), geo_ptrs[1]->end());\n\n        if (this->dimension == 3)\n        {\n            this->zmax = *amgx::thrust::max_element(geo_ptrs[2]->begin(), geo_ptrs[2]->end());\n            this->zmin = *amgx::thrust::min_element(geo_ptrs[2]->begin(), geo_ptrs[2]->end());\n        }\n\n        cudaCheckError();\n        ValueType *point_ptr = geo_ptrs[0]->raw();\n        generatelabel1d <<< num_blocks, threads_per_block>>>(aggregation_ptr, point_ptr, this->xmin, this->xmax, n, num_per_row);\n        cudaCheckError();\n        point_ptr = geo_ptrs[1]->raw();\n        generatelabel1dpxb <<< num_blocks, threads_per_block>>>(aggregation_ptr, num_per_row, point_ptr, this->ymin, this->ymax, n, num_per_row);\n        cudaCheckError();\n\n        if (this->dimension > 2)\n        {\n            point_ptr = geo_ptrs[2]->raw();\n            generatelabel1dpxb <<< num_blocks, threads_per_block>>>(aggregation_ptr, num_per_row * num_per_row, point_ptr, this->zmin, this->zmax, n, num_per_row);\n            cudaCheckError();\n        }\n    }\n\n    num_aggregates = num_per_row * num_per_row;\n\n    if (this->dimension == 3) { num_aggregates *= num_per_row; }\n\n    A.template setParameter < int > (\"uniform_based\", 1); // not exactly correct. But this is workarond for now, since parameters are instanlty copied for the coarser matrix in aggregation level\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"GEO_Selector not implemented for non square blocks, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n// Choose aggregates on host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1( Matrix_h &A,\n        IVector &aggregates,  IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// Choose aggregates on host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_h &A,\n        IVector &aggregates,  IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <class T_Config>\nvoid GEO_SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    //printf(\"Selector: %d processing with the GEO\\n\", A.get_num_rows());\n    this->dimension = A.template getParameter<int>(\"dim\");\n    cord_x = A.template getParameterPtr< VVector >(\"geo.x\");\n    cord_y = A.template getParameterPtr< VVector >(\"geo.y\");\n    num_origonal = A.template getParameter<int>(\"geo_size\");\n\n    if (this->dimension == 3)\n    {\n        cord_z = A.template getParameterPtr< VVector >(\"geo.z\");\n    }\n\n    if (this->dimension == 0) { FatalError(\"GEO_SELECTOR: Problem dimension is not valid.\", AMGX_ERR_BAD_PARAMETERS); }\n\n    if (num_origonal == 0) { FatalError(\"Problem size is not valid.\", AMGX_ERR_BAD_PARAMETERS); }\n\n    if (A.get_block_size() == 1 && !A.hasProps(DIAG) )\n    {\n        setAggregates_1x1( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_1x1( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GEO_Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class GEO_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class GEO_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <basic_types.h>\n#include <algorithm>\n#include <list>\n#include <cmath>\n#include <aggregation/selectors/geo_selector.h>\n#include <error.h>\n#include <types.h>\n#include <math.h>\n#include \"util.h\"\n\n#include <cutil.h>\n#include <util.h>\n#include <blas.h>\n#include <multiply.h>\n\n#include <matrix_analysis.h>\n\n#include<thrust/count.h> //count\n#include<thrust/sort.h> //sort\n#include<thrust/binary_search.h> //lower_bound\n#include<thrust/unique.h> //unique\n#include<cusp/detail/format_utils.h> //offsets_to_indices\n\n#define epsilon 1e-10\nnamespace amgx\n{\nnamespace aggregation\n{\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n//generate the aggregates labels\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid generatelabel1d(IndexType *aggregation, const ValueType *points, const double cord_min, const double cord_max, const int npoint, const int nlevel)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    double distance = 1.01 * (cord_max - cord_min);\n\n    for (; idx < npoint; idx += gridDim.x * blockDim.x)\n    {\n        aggregation[idx] = (int)((points[idx] - cord_min) / distance * nlevel);\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid generatelabel1dpxb(IndexType *aggregation, const int alpha, const ValueType *points, const double cord_min, const double cord_max, const int npoint, const int nlevel)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    double distance = 1.01 * (cord_max - cord_min);\n\n    for (; idx < npoint; idx += gridDim.x * blockDim.x)\n    {\n        aggregation[idx] = aggregation[idx] + alpha * (int)((points[idx] - cord_min) / distance * nlevel);\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid generatelabel3d(IndexType *aggregation, const int n, const int n1d, const int n1d_orig)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n1d2 = n1d_orig * n1d_orig;\n    int x, y, z;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        int label = idx % n1d2;\n        x = (label % n1d_orig) / 2;\n        y = (label / n1d_orig) / 2;\n        z = idx / n1d2 / 2;\n        aggregation[idx] = z * n1d * n1d + y * n1d + x;\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid generatelabel2d(IndexType *aggregation, const int n, const int n1d, const int n1d_orig)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int x, y;\n\n    for (; idx < n; idx += gridDim.x * blockDim.x)\n    {\n        x = (idx % n1d_orig) / 2;\n        y = (idx / n1d_orig) / 2;\n        aggregation[idx] = y * n1d + x;\n    }\n}\n\n\ntemplate <typename IndexType >\n__global__\nvoid aggregateLevel(IndexType *indices, const IndexType *next_level, const int num_rows)\n{\n    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    for (; idx < num_rows; idx += gridDim.x * blockDim.x)\n    {\n        indices[idx] = next_level[indices[idx]];\n    }\n}\n\n\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nGEO_SelectorBase<T_Config>::GEO_SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n}\n\ntemplate <class T_Config>\nbool compx (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.x < j.x); }\n\ntemplate <class T_Config>\nbool compy (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.y < j.y); }\n\ntemplate <class T_Config>\nbool compz (typename GEO_SelectorBase<T_Config>::p3d i, typename GEO_SelectorBase<T_Config>::p3d j) { return (i.z < j.z); }\n\ntemplate <typename Tuple, typename GeoType, typename IndexType>\nstruct reduce_functor2\n{\n    __host__ __device__\n    Tuple operator()(const Tuple x, const Tuple y)\n    {\n        return amgx::thrust::make_tuple(amgx::thrust::get<0>(x) + amgx::thrust::get<0>(y),\n                                  amgx::thrust::get<1>(x) + amgx::thrust::get<1>(y),\n                                  amgx::thrust::get<2>(x) + amgx::thrust::get<2>(y)\n                                 );\n    }\n};\n\ntemplate <typename Tuple, typename GeoType, typename IndexType>\nstruct reduce_functor3\n{\n    __host__ __device__\n    Tuple operator()(const Tuple x, const Tuple y)\n    {\n        return amgx::thrust::make_tuple(amgx::thrust::get<0>(x) + amgx::thrust::get<0>(y),\n                                  amgx::thrust::get<1>(x) + amgx::thrust::get<1>(y),\n                                  amgx::thrust::get<2>(x) + amgx::thrust::get<2>(y),\n                                  amgx::thrust::get<3>(x) + amgx::thrust::get<3>(y)\n                                 );\n    }\n};\n\n// interpolates geometric info from the matrix using the matrix aggregation\ntemplate<class T_Config>\nvoid GEO_SelectorBase<T_Config>::interpolateGeoinfo( Matrix<T_Config> &A )\n{\n    const int threads_per_block = 512;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (A.get_num_rows() - 1) / threads_per_block + 1 );\n    ngeo_x.resize(A.get_num_rows());\n    ngeo_y.resize(A.get_num_rows());\n\n    if (this->dimension == 3)\n    {\n        ngeo_z.resize(A.get_num_rows());\n    }\n\n    if (!(A.hasParameter(\"aggregates_info\")) )\n    {\n        FatalError(\"Cannot find information about previous aggregations for GEO selector\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    std::vector<IVector *> *agg_info = A.template getParameterPtr< std::vector<IVector *> >(\"aggregates_info\");\n    IVector cur_geo_idx = *(agg_info->at(0));\n    IVector v_counter(this->num_origonal, 1), v_new_counter(A.get_num_rows(), 1), r_coord(A.get_num_rows());\n\n    if ( this->num_origonal != cur_geo_idx.size() )\n    {\n        FatalError(\"GEO size doesn't match original matrix dimension\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    for (size_t i = 1; i < agg_info->size(); i++)\n    {\n        aggregateLevel <<< num_blocks, threads_per_block>>>(cur_geo_idx.raw(), agg_info->at(i)->raw(), this->num_origonal);\n        cudaCheckError();\n    }\n\n    if (this->dimension == 2)\n    {\n        amgx::thrust::sort_by_key(cur_geo_idx.begin(), cur_geo_idx.end(), amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin())));\n        amgx::thrust::reduce_by_key(\n            cur_geo_idx.begin(),\n            cur_geo_idx.end(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), v_counter.begin())),\n            r_coord.begin(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(ngeo_x.begin(), ngeo_y.begin(), v_new_counter.begin())),\n            amgx::thrust::equal_to<int>(),\n            reduce_functor2< amgx::thrust::tuple< ValueType, ValueType, IndexType>, ValueType, IndexType > ()\n        );\n    }\n    else\n    {\n        amgx::thrust::sort_by_key(cur_geo_idx.begin(), cur_geo_idx.end(), amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), this->cord_z->begin())));\n        amgx::thrust::reduce_by_key(\n            cur_geo_idx.begin(),\n            cur_geo_idx.end(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(this->cord_x->begin(), this->cord_y->begin(), this->cord_z->begin(), v_counter.begin())),\n            r_coord.begin(),\n            amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(ngeo_x.begin(), ngeo_y.begin(), ngeo_z.begin(), v_new_counter.begin())),\n            amgx::thrust::equal_to<int>(),\n            reduce_functor3< amgx::thrust::tuple< ValueType, ValueType, ValueType, IndexType>, ValueType, IndexType > ()\n        );\n    }\n\n    cudaCheckError();\n}\n\n// Choose the aggregates for csr_matrix_d format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1( Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    if (this->dimension == 0)\n    {\n        FatalError(\"No input geometric information, exiting\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    //initialize the aggregates vector\n    int n = A.get_num_rows();\n    aggregates.resize(n);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n    int nlevel;\n\n    if (this->dimension == 2) { nlevel = floor(log(sqrt((double)n)) / log(2.0)); }\n\n    if (this->dimension == 3) { nlevel = ceil(log(cbrt((double)n)) / log(2.0)); }\n\n    int num_per_row = (int)std::pow(2, nlevel - 1);\n    const int threads_per_block = 512;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (n - 1) / threads_per_block + 1 );\n    // generate aggregation index in 1d\n    IndexType *aggregation_ptr = amgx::thrust::raw_pointer_cast(&aggregates[0]);\n\n    if (A.hasParameter(\"uniform_based\"))\n    {\n        printf(\"GEOAggregating %d-rows matrix as uniform\\n\", A.get_num_rows());\n        int npr_orig = 2 * num_per_row;\n\n        if (this->dimension == 3)\n        {\n            generatelabel3d <<< num_blocks, threads_per_block>>>(aggregation_ptr, n, num_per_row, npr_orig);\n            cudaCheckError();\n        }\n        else\n        {\n            generatelabel2d <<< num_blocks, threads_per_block>>>(aggregation_ptr, n, num_per_row, npr_orig);\n            cudaCheckError();\n        }\n    }\n    else\n    {\n        std::vector<VVector *> geo_ptrs(3);\n        // real geometry source\n        geo_ptrs[0] = this->cord_x;\n        geo_ptrs[1] = this->cord_y;\n        geo_ptrs[2] = this->cord_z;\n\n        // do we need to interpolate from the finest level?\n        if (A.get_num_rows() != this->num_origonal)\n        {\n            this->interpolateGeoinfo(A);\n\n            if (this->dimension == 3)\n            {\n                geo_ptrs[2] = &this->ngeo_z;\n            }\n\n            geo_ptrs[0] = &this->ngeo_x;\n            geo_ptrs[1] = &this->ngeo_y;\n        }\n\n        //Find the boundary coordinates\n        this->xmax = *amgx::thrust::max_element(geo_ptrs[0]->begin(), geo_ptrs[0]->end());\n        this->xmin = *amgx::thrust::min_element(geo_ptrs[0]->begin(), geo_ptrs[0]->end());\n        this->ymax = *amgx::thrust::max_element(geo_ptrs[1]->begin(), geo_ptrs[1]->end());\n        this->ymin = *amgx::thrust::min_element(geo_ptrs[1]->begin(), geo_ptrs[1]->end());\n\n        if (this->dimension == 3)\n        {\n            this->zmax = *amgx::thrust::max_element(geo_ptrs[2]->begin(), geo_ptrs[2]->end());\n            this->zmin = *amgx::thrust::min_element(geo_ptrs[2]->begin(), geo_ptrs[2]->end());\n        }\n\n        cudaCheckError();\n        ValueType *point_ptr = geo_ptrs[0]->raw();\n        generatelabel1d <<< num_blocks, threads_per_block>>>(aggregation_ptr, point_ptr, this->xmin, this->xmax, n, num_per_row);\n        cudaCheckError();\n        point_ptr = geo_ptrs[1]->raw();\n        generatelabel1dpxb <<< num_blocks, threads_per_block>>>(aggregation_ptr, num_per_row, point_ptr, this->ymin, this->ymax, n, num_per_row);\n        cudaCheckError();\n\n        if (this->dimension > 2)\n        {\n            point_ptr = geo_ptrs[2]->raw();\n            generatelabel1dpxb <<< num_blocks, threads_per_block>>>(aggregation_ptr, num_per_row * num_per_row, point_ptr, this->zmin, this->zmax, n, num_per_row);\n            cudaCheckError();\n        }\n    }\n\n    num_aggregates = num_per_row * num_per_row;\n\n    if (this->dimension == 3) { num_aggregates *= num_per_row; }\n\n    A.template setParameter < int > (\"uniform_based\", 1); // not exactly correct. But this is workarond for now, since parameters are instanlty copied for the coarser matrix in aggregation level\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_d &A,\n        typename Matrix_d::IVector &aggregates, typename Matrix_d::IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"GEO_Selector not implemented for non square blocks, exiting\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n// Choose aggregates on host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_1x1( Matrix_h &A,\n        IVector &aggregates,  IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// Choose aggregates on host\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid GEO_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(const Matrix_h &A,\n        IVector &aggregates,  IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <class T_Config>\nvoid GEO_SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A, IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    //printf(\"Selector: %d processing with the GEO\\n\", A.get_num_rows());\n    this->dimension = A.template getParameter<int>(\"dim\");\n    cord_x = A.template getParameterPtr< VVector >(\"geo.x\");\n    cord_y = A.template getParameterPtr< VVector >(\"geo.y\");\n    num_origonal = A.template getParameter<int>(\"geo_size\");\n\n    if (this->dimension == 3)\n    {\n        cord_z = A.template getParameterPtr< VVector >(\"geo.z\");\n    }\n\n    if (this->dimension == 0) { FatalError(\"GEO_SELECTOR: Problem dimension is not valid.\", AMGX_ERR_BAD_PARAMETERS); }\n\n    if (num_origonal == 0) { FatalError(\"Problem size is not valid.\", AMGX_ERR_BAD_PARAMETERS); }\n\n    if (A.get_block_size() == 1 && !A.hasProps(DIAG) )\n    {\n        setAggregates_1x1( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_1x1( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for GEO_Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class GEO_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class GEO_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/geo_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/selectors/size4_selector.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/unique.h> //unique\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace size4_selector\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n#ifndef DELETE\n// Kernel to compute the weight of the edges with block_dia_csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeEdgeWeightsBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                   const IndexType *dia_idx, const ValueType *nonzero_values, const IndexType num_block_rows, float *edge_weights, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            jcol = column_indices[j];\n\n            if (jcol >= num_block_rows) { continue; }\n\n            // Compute edge weight\n            for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n            {\n                if (column_indices[k] == tid)\n                {\n                    edge_weights[j] = 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                      / max( fabs(nonzero_values[bsize_sq * dia_idx[tid]]), fabs(nonzero_values[bsize_sq * dia_idx[jcol]]));\n                    break;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n#endif\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize4SelectorBase<T_Config>::Size4SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size4Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_h &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"OnePhaseHandshaking selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size4Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_d &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    // both ways are supported\n    const IndexType num_block_rows = A.get_num_rows();\n    const IndexType num_nonzero_blocks = A.get_num_nz();\n\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(num_block_rows);\n    }\n\n    // Initially, put each vertex in its own aggregate\n    thrust_wrapper::sequence<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows);\n    cudaCheckError();\n    IndexType *aggregates_ptr = aggregates.raw();\n    // Create row_indices array\n    IndexType total_nz = A.is_matrix_singleGPU() ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n\n    if (total_nz > 0)\n    {\n        cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector partner_index(num_block_rows * 3, -1);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *partner_index_ptr = partner_index.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks, -1);\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;\n    // Compute the edge weights\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n\n    if (num_nonzero_blocks > 0)\n    {\n        cudaFuncSetCacheConfig(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>, cudaFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block>>>(A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n        cudaCheckError();\n    }\n\n    // -------------------------------------------------\n    // First create aggregates of size 2\n    // -------------------------------------------------\n    int icount = 0;\n\n    do\n    {\n        // For each block_row, find the strongest neighbour who hasn't been assigned\n        findStrongestNeighbourBlockDiaCsr_NoMerge <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, partner_index_ptr, strongest_neighbour_ptr, this->deterministic);\n        cudaCheckError();\n        // Look for perfect matches\n        matchEdges <<< num_blocks, threads_per_block>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = (int)thrust_wrapper::count<AMGX_device>(partner_index.begin(), partner_index.end(), -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    assignUnassignedVertices <<< num_blocks, threads_per_block>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 4\n    // -------------------------------------------------\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > weight_strongest_neighbour(num_block_rows, -1);\n    float *weight_strongest_neighbour_ptr = weight_strongest_neighbour.raw();\n    // now used as flag to check if aggregated or not\n    typename Matrix_d::IVector aggregated(num_block_rows, -1);\n    IndexType *aggregated_ptr = aggregated.raw();\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        findStrongestNeighbourBlockDiaCsr_StoreWeight <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic);\n        cudaCheckError();\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        agreeOnProposal <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr);\n        cudaCheckError();\n        matchAggregates <IndexType> <<< num_blocks, threads_per_block>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    // Merge remaining vertices with current aggregates\n\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, (IndexType *) NULL);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n        };\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, aggregates_candidate.raw());\n            cudaCheckError();\n            joinExistingAggregates <<< num_blocks, threads_per_block>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw());\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n        };\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n}\n\ntemplate <class T_Config>\nvoid Size4SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblock( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size4Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size4SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size4Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/selectors/size4_selector.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/unique.h> //unique\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace size4_selector\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//  Kernels\n// ------------------------\n\n#ifndef DELETE\n// Kernel to compute the weight of the edges with block_dia_csr format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeEdgeWeightsBlockDiaCsr(const IndexType *row_offsets, const IndexType *column_indices,\n                                   const IndexType *dia_idx, const ValueType *nonzero_values, const IndexType num_block_rows, float *edge_weights, int bsize)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    int bsize_sq = bsize * bsize;\n\n    while (tid < num_block_rows)\n    {\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            jcol = column_indices[j];\n\n            if (jcol >= num_block_rows) { continue; }\n\n            // Compute edge weight\n            for (int k = row_offsets[jcol]; k < row_offsets[jcol + 1]; k++)\n            {\n                if (column_indices[k] == tid)\n                {\n                    edge_weights[j] = 0.5 * (fabs(nonzero_values[j * bsize_sq]) + fabs(nonzero_values[k * bsize_sq]))\n                                      / max( fabs(nonzero_values[bsize_sq * dia_idx[tid]]), fabs(nonzero_values[bsize_sq * dia_idx[jcol]]));\n                    break;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n#endif\n\n// -----------------\n//  Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nSize4SelectorBase<T_Config>::Size4SelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    weight_formula = cfg.AMG_Config::template getParameter<int>(\"weight_formula\", cfg_scope);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size4Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_h &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    FatalError(\"OnePhaseHandshaking selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Size4Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblock(const Matrix_d &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    // both ways are supported\n    const IndexType num_block_rows = A.get_num_rows();\n    const IndexType num_nonzero_blocks = A.get_num_nz();\n\n    if (!A.is_matrix_singleGPU())\n    {\n        aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n    }\n    else\n    {\n        aggregates.resize(num_block_rows);\n    }\n\n    // Initially, put each vertex in its own aggregate\n    thrust_wrapper::sequence<AMGX_device>(aggregates.begin(), aggregates.begin() + num_block_rows);\n    cudaCheckError();\n    IndexType *aggregates_ptr = aggregates.raw();\n    // Create row_indices array\n    IndexType total_nz = A.is_matrix_singleGPU() ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector row_indices(total_nz);\n\n    if (total_nz > 0)\n    {\n        cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector partner_index(num_block_rows * 3, -1);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *partner_index_ptr = partner_index.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1);\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > edge_weights(num_nonzero_blocks, -1);\n    float *edge_weights_ptr = edge_weights.raw();\n    float *rand_edge_weights_ptr = NULL;\n    // Compute the edge weights\n    const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n\n    if (num_nonzero_blocks > 0)\n    {\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, float>), hipFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block>>>(A_row_offsets_ptr, A_row_indices_ptr, A_column_indices_ptr, A_dia_idx_ptr, A_nonzero_values_ptr, num_nonzero_blocks, edge_weights_ptr, rand_edge_weights_ptr, num_block_rows, A.get_block_dimy(), this->m_aggregation_edge_weight_component, this->weight_formula);\n        cudaCheckError();\n    }\n\n    // -------------------------------------------------\n    // First create aggregates of size 2\n    // -------------------------------------------------\n    int icount = 0;\n\n    do\n    {\n        // For each block_row, find the strongest neighbour who hasn't been assigned\n        findStrongestNeighbourBlockDiaCsr_NoMerge <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, partner_index_ptr, strongest_neighbour_ptr, this->deterministic);\n        cudaCheckError();\n        // Look for perfect matches\n        matchEdges <<< num_blocks, threads_per_block>>>(num_block_rows, partner_index_ptr, aggregates_ptr, strongest_neighbour_ptr);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = (int)thrust_wrapper::count<AMGX_device>(partner_index.begin(), partner_index.end(), -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    assignUnassignedVertices <<< num_blocks, threads_per_block>>>(partner_index_ptr, num_block_rows);\n    cudaCheckError();\n    // -------------------------------------------------\n    // Merge aggregates to create aggregates of size 4\n    // -------------------------------------------------\n    Vector<TemplateConfig<AMGX_device, AMGX_vecFloat, t_matPrec, t_indPrec> > weight_strongest_neighbour(num_block_rows, -1);\n    float *weight_strongest_neighbour_ptr = weight_strongest_neighbour.raw();\n    // now used as flag to check if aggregated or not\n    typename Matrix_d::IVector aggregated(num_block_rows, -1);\n    IndexType *aggregated_ptr = aggregated.raw();\n    icount = 0;\n    numUnassigned = num_block_rows;\n    numUnassigned_previous = numUnassigned;\n\n    do\n    {\n        // Each vertex stores in strongest_neighbour the aggregates number of strongest neighbour and the weight of connection\n        findStrongestNeighbourBlockDiaCsr_StoreWeight <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregated_ptr, aggregates_ptr, strongest_neighbour_ptr, partner_index_ptr, weight_strongest_neighbour_ptr, this->deterministic);\n        cudaCheckError();\n        // Each vertex in same aggregates will agree on aggregates to propose too, and both store the aggregate number they want to match with\n        agreeOnProposal <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, num_block_rows, aggregated_ptr, strongest_neighbour_ptr, weight_strongest_neighbour_ptr, partner_index_ptr, aggregates_ptr);\n        cudaCheckError();\n        matchAggregates <IndexType> <<< num_blocks, threads_per_block>>>(aggregates_ptr, aggregated_ptr, strongest_neighbour_ptr, num_block_rows);\n        cudaCheckError();\n        numUnassigned_previous = numUnassigned;\n        numUnassigned = thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n        cudaCheckError();\n        icount++;\n    }\n    while (!(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned_previous == numUnassigned));\n\n    // Merge remaining vertices with current aggregates\n\n    if (!this->deterministic)\n    {\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, (IndexType *) NULL);\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n        };\n    }\n    else\n    {\n        typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n        while (numUnassigned != 0)\n        {\n            mergeWithExistingAggregatesBlockDiaCsr <<< num_blocks, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, aggregated_ptr, this->deterministic, aggregates_candidate.raw());\n            cudaCheckError();\n            joinExistingAggregates <<< num_blocks, threads_per_block>>>(num_block_rows, aggregates_ptr, aggregated_ptr, aggregates_candidate.raw());\n            cudaCheckError();\n            numUnassigned = (int)thrust_wrapper::count<AMGX_device>(aggregated.begin(), aggregated.end(), -1);\n            cudaCheckError();\n        };\n    }\n\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n}\n\ntemplate <class T_Config>\nvoid Size4SelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        setAggregates_common_sqblock( A, aggregates, aggregates_global, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for Size4Selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//  Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class Size4SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class Size4Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n}\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/size4_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <types.h>\n#include <aggregation/selectors/agg_selector.h>\n#include <transpose.h>\n\n#include <amgx_types/util.h>\n\n#include <assert.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntemplate<class T_Config>\nvoid Selector<T_Config>::renumberAndCountAggregates(IVector &aggregates, IVector &aggregates_global, const IndexType num_block_rows, IndexType &num_aggregates)\n{\n    // renumber aggregates\n    IVector scratch(num_block_rows + 1);\n\n    if ( num_block_rows != aggregates.size() )\n    {\n        // we are in a distributed environment\n        aggregates_global.resize(aggregates.size());\n        amgx::thrust::copy(aggregates.begin(), aggregates.begin() + num_block_rows, aggregates_global.begin());\n    }\n\n    // set scratch[aggregates[i]] = 1\n    thrust::fill(amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin() + num_block_rows), 1);\n    // do prefix sum on scratch\n    thrust_wrapper::exclusive_scan<T_Config::memSpace>(scratch.begin(), scratch.end(), scratch.begin());\n    // aggregates[i] = scratch[aggregates[i]]\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin() + num_block_rows),\n                 aggregates.begin());\n    // update number of aggregates\n    num_aggregates = scratch[scratch.size() - 1];\n    cudaCheckError();\n}\n\n\ntemplate<typename IndexType>\n__global__\nvoid countRowSizes( const IndexType *R_row_offsets, IndexType *size_array, int num_aggregates)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < num_aggregates )\n    {\n        int size = R_row_offsets[tid + 1] - R_row_offsets[tid];\n        atomicAdd( &size_array[size], 1 );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n//prints aggregate info:\n//counts aggregates grouped by size\ntemplate<class TConfig>\nvoid Selector<TConfig>::printAggregationInfo(const IVector &aggregates, const IVector &aggregates_global, const IndexType num_aggregates) const\n{\n    //build restriction row offsets\n    Matrix<TConfig> P;\n    P.set_initialized(0);\n    P.addProps(CSR);\n    P.delProps(COO);\n    P.delProps(DIAG);\n    P.setColsReorderedByColor(false);\n    P.resize( 0, 0, 0, 1, 1); //make sure the matrix is empty\n    Matrix<TConfig> R;\n    R.set_initialized(0);\n    R.addProps(CSR);\n    R.delProps(COO);\n    R.delProps(DIAG);\n    R.setColsReorderedByColor(false);\n    R.resize( 0, 0, 0, 1, 1);\n    int num_rows = aggregates.size();\n    //set correct sizes.\n    P.row_offsets.resize( num_rows + 1 );\n    P.values.resize( num_rows, types::util<typename MVector::value_type>::get_one());\n    P.col_indices.resize( num_rows );\n    //setup offset array.\n    thrust_wrapper::sequence<TConfig::memSpace>( P.row_offsets.begin(), P.row_offsets.end() );\n    //swap in aggregates\n    amgx::thrust::copy( aggregates.begin(), aggregates.end(), P.col_indices.begin() );\n    cudaCheckError();\n    //inform P about its size\n    P.resize( num_rows, num_aggregates, num_rows, 1, 1, false ); //declare scalar, otherwise transpose won't work\n    //P is ready\n    P.set_initialized(1);\n    //do the transpose\n    R.set_initialized(0);\n    transpose( P, R ); //note: this won't change P\n    //ok now we have row offsets.\n    //we are ready to count the row sizes\n    IVector size_array;\n    size_array.resize(num_rows + 1, 0 );\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( (int)AMGX_GRID_MAX_SIZE, (int)(num_rows - 1) / threads_per_block + 1 );\n    //count\n    countRowSizes <<< num_blocks, threads_per_block, 0, 0>>>( R.row_offsets.raw(), size_array.raw(), num_aggregates );\n    cudaCheckError();\n    cudaDeviceSynchronize();\n    cudaCheckError();\n    //copy to host and print\n    amgx::thrust::host_vector<int> size_array_host;\n    size_array_host.resize(size_array.size() );\n    amgx::thrust::copy( size_array.begin(), size_array.end(), size_array_host.begin() );\n    std::cout << \"number of nodes \" << num_rows << std::endl;\n    std::cout << \"number of aggregates by size\" << std::endl;\n\n    for (int i = 1; i < size_array_host.size(); i++)\n    {\n        if ( size_array_host[i] > 0 )\n        {\n            std::cout << \"size \" << i << \": \" << size_array_host[i] << std::endl;\n        }\n    }\n\n    std::cout << \"total \" << (num_aggregates - size_array_host[0]) << std::endl;\n}\n\ntemplate<class TConfig>\nvoid Selector<TConfig>::assertAggregates( const IVector &aggregates, int numAggregates )\n{\n    int *res_sizes = new int[numAggregates];\n\n    for ( int i = 0; i < numAggregates; i++ )\n    {\n        res_sizes[i] = 0;\n    }\n\n    int *h_aggregates = new int[aggregates.size()];\n    cudaMemcpy( h_aggregates, aggregates.raw(), sizeof(int)*aggregates.size(), cudaMemcpyDeviceToHost );\n    cudaCheckError();\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        if ( h_aggregates[i] < 0 || h_aggregates[i] >= numAggregates )\n        {\n            std::cout << \"Error: aggregates[\" << i << \"]=\" << h_aggregates[i] << \" but numAggregates=\" << numAggregates << std::endl;\n        }\n        else\n        {\n            res_sizes[h_aggregates[i]]++;\n        }\n    }\n\n    for ( int i = 0; i < numAggregates; i++ )\n    {\n        if ( res_sizes[i] == 0 )\n        {\n            std::cout << \"Error: aggregate \" << i << \" is empty\" << std::endl;\n        }\n    }\n\n    std::cout << \"assertAggregates done.\" << std::endl;\n}\n\ntemplate<class TConfig>\nvoid Selector<TConfig>::assertRestriction( const IVector &R_row_offsets, const IVector &R_col_indices, const IVector &aggregates )\n{\n    int *r_ia = new int[R_row_offsets.size()];\n    int *r_ja = new int[R_col_indices.size()];\n    int *agg = new int[aggregates.size()];\n    int *used_col = new int[aggregates.size()];\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        used_col[i] = 0;\n    }\n\n    cudaError_t cuda_rc = cudaMemcpy( r_ia, R_row_offsets.raw(), sizeof(int)*R_row_offsets.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy R_row_offsets D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( r_ja, R_col_indices.raw(), sizeof(int)*R_col_indices.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy R_col_indices D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( agg, aggregates.raw(), sizeof(int)*aggregates.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy aggregates D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for ( int i = 0; i < R_row_offsets.size() - 1; i++ )\n    {\n        for ( int ii = r_ia[i]; ii < r_ia[i + 1]; ii++ )\n        {\n            int j = r_ja[ii];\n            used_col[j]++;\n\n            if ( used_col[j] > 1 )\n            {\n                std::cout << \"column \" << j << \" is present at least \" << used_col[j] << \" times\" << std::endl;\n            }\n\n            if ( j < 0 || j >= aggregates.size() )\n            {\n                std::cout << \"Error: j out of bounds, j = \" << j << \" and numRows = \" << aggregates.size() << std::endl;\n            }\n            else if  ( agg[j] != i )\n            {\n                std::cout << \"Error: agg[\" << j << \"] = \" << agg[j] << \" != \" << i << std::endl;\n            }\n        }\n    }\n\n    std::cout << \"assert restriction done\" << std::endl;\n}\n\n\n\ntemplate<class T_Config>\nstd::map<std::string, SelectorFactory<T_Config>*> &\nSelectorFactory<T_Config>::getFactories( )\n{\n    static std::map<std::string, SelectorFactory<T_Config>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::registerFactory(std::string name, SelectorFactory<T_Config> *f)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::unregisterFactory(std::string name)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    SelectorFactory<T_Config> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::unregisterFactories( )\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        SelectorFactory<T_Config> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class T_Config>\nSelector<T_Config> *SelectorFactory<T_Config>::allocate(AMG_Config &cfg, const std::string &current_scope)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    int agg_lvl_change = cfg.AMG_Config::template getParameter<int>(\"fine_levels\", current_scope);\n    std::string selector;\n    selector = cfg.getParameter<std::string>(\"selector\", current_scope);\n    typename std::map<std::string, SelectorFactory<T_Config> *>::const_iterator it = factories.find(selector);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + selector + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, current_scope);\n};\n\n\n\n//****************************************\n// Explict instantiations\n// **************************************\n#define AMGX_CASE_LINE(CASE) template class Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class SelectorFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <types.h>\n#include <aggregation/selectors/agg_selector.h>\n#include <transpose.h>\n\n#include <amgx_types/util.h>\n\n#include <assert.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntemplate<class T_Config>\nvoid Selector<T_Config>::renumberAndCountAggregates(IVector &aggregates, IVector &aggregates_global, const IndexType num_block_rows, IndexType &num_aggregates)\n{\n    // renumber aggregates\n    IVector scratch(num_block_rows + 1);\n\n    if ( num_block_rows != aggregates.size() )\n    {\n        // we are in a distributed environment\n        aggregates_global.resize(aggregates.size());\n        amgx::thrust::copy(aggregates.begin(), aggregates.begin() + num_block_rows, aggregates_global.begin());\n    }\n\n    // set scratch[aggregates[i]] = 1\n    thrust::fill(amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin() + num_block_rows), 1);\n    // do prefix sum on scratch\n    thrust_wrapper::exclusive_scan<T_Config::memSpace>(scratch.begin(), scratch.end(), scratch.begin());\n    // aggregates[i] = scratch[aggregates[i]]\n    amgx::thrust::copy(amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin()),\n                 amgx::thrust::make_permutation_iterator(scratch.begin(), aggregates.begin() + num_block_rows),\n                 aggregates.begin());\n    // update number of aggregates\n    num_aggregates = scratch[scratch.size() - 1];\n    cudaCheckError();\n}\n\n\ntemplate<typename IndexType>\n__global__\nvoid countRowSizes( const IndexType *R_row_offsets, IndexType *size_array, int num_aggregates)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < num_aggregates )\n    {\n        int size = R_row_offsets[tid + 1] - R_row_offsets[tid];\n        atomicAdd( &size_array[size], 1 );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n//prints aggregate info:\n//counts aggregates grouped by size\ntemplate<class TConfig>\nvoid Selector<TConfig>::printAggregationInfo(const IVector &aggregates, const IVector &aggregates_global, const IndexType num_aggregates) const\n{\n    //build restriction row offsets\n    Matrix<TConfig> P;\n    P.set_initialized(0);\n    P.addProps(CSR);\n    P.delProps(COO);\n    P.delProps(DIAG);\n    P.setColsReorderedByColor(false);\n    P.resize( 0, 0, 0, 1, 1); //make sure the matrix is empty\n    Matrix<TConfig> R;\n    R.set_initialized(0);\n    R.addProps(CSR);\n    R.delProps(COO);\n    R.delProps(DIAG);\n    R.setColsReorderedByColor(false);\n    R.resize( 0, 0, 0, 1, 1);\n    int num_rows = aggregates.size();\n    //set correct sizes.\n    P.row_offsets.resize( num_rows + 1 );\n    P.values.resize( num_rows, types::util<typename MVector::value_type>::get_one());\n    P.col_indices.resize( num_rows );\n    //setup offset array.\n    thrust_wrapper::sequence<TConfig::memSpace>( P.row_offsets.begin(), P.row_offsets.end() );\n    //swap in aggregates\n    amgx::thrust::copy( aggregates.begin(), aggregates.end(), P.col_indices.begin() );\n    cudaCheckError();\n    //inform P about its size\n    P.resize( num_rows, num_aggregates, num_rows, 1, 1, false ); //declare scalar, otherwise transpose won't work\n    //P is ready\n    P.set_initialized(1);\n    //do the transpose\n    R.set_initialized(0);\n    transpose( P, R ); //note: this won't change P\n    //ok now we have row offsets.\n    //we are ready to count the row sizes\n    IVector size_array;\n    size_array.resize(num_rows + 1, 0 );\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( (int)AMGX_GRID_MAX_SIZE, (int)(num_rows - 1) / threads_per_block + 1 );\n    //count\n    countRowSizes <<< num_blocks, threads_per_block, 0, 0>>>( R.row_offsets.raw(), size_array.raw(), num_aggregates );\n    cudaCheckError();\n    hipDeviceSynchronize();\n    cudaCheckError();\n    //copy to host and print\n    amgx::thrust::host_vector<int> size_array_host;\n    size_array_host.resize(size_array.size() );\n    amgx::thrust::copy( size_array.begin(), size_array.end(), size_array_host.begin() );\n    std::cout << \"number of nodes \" << num_rows << std::endl;\n    std::cout << \"number of aggregates by size\" << std::endl;\n\n    for (int i = 1; i < size_array_host.size(); i++)\n    {\n        if ( size_array_host[i] > 0 )\n        {\n            std::cout << \"size \" << i << \": \" << size_array_host[i] << std::endl;\n        }\n    }\n\n    std::cout << \"total \" << (num_aggregates - size_array_host[0]) << std::endl;\n}\n\ntemplate<class TConfig>\nvoid Selector<TConfig>::assertAggregates( const IVector &aggregates, int numAggregates )\n{\n    int *res_sizes = new int[numAggregates];\n\n    for ( int i = 0; i < numAggregates; i++ )\n    {\n        res_sizes[i] = 0;\n    }\n\n    int *h_aggregates = new int[aggregates.size()];\n    hipMemcpy( h_aggregates, aggregates.raw(), sizeof(int)*aggregates.size(), hipMemcpyDeviceToHost );\n    cudaCheckError();\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        if ( h_aggregates[i] < 0 || h_aggregates[i] >= numAggregates )\n        {\n            std::cout << \"Error: aggregates[\" << i << \"]=\" << h_aggregates[i] << \" but numAggregates=\" << numAggregates << std::endl;\n        }\n        else\n        {\n            res_sizes[h_aggregates[i]]++;\n        }\n    }\n\n    for ( int i = 0; i < numAggregates; i++ )\n    {\n        if ( res_sizes[i] == 0 )\n        {\n            std::cout << \"Error: aggregate \" << i << \" is empty\" << std::endl;\n        }\n    }\n\n    std::cout << \"assertAggregates done.\" << std::endl;\n}\n\ntemplate<class TConfig>\nvoid Selector<TConfig>::assertRestriction( const IVector &R_row_offsets, const IVector &R_col_indices, const IVector &aggregates )\n{\n    int *r_ia = new int[R_row_offsets.size()];\n    int *r_ja = new int[R_col_indices.size()];\n    int *agg = new int[aggregates.size()];\n    int *used_col = new int[aggregates.size()];\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        used_col[i] = 0;\n    }\n\n    hipError_t cuda_rc = hipMemcpy( r_ia, R_row_offsets.raw(), sizeof(int)*R_row_offsets.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy R_row_offsets D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( r_ja, R_col_indices.raw(), sizeof(int)*R_col_indices.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy R_col_indices D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( agg, aggregates.raw(), sizeof(int)*aggregates.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy aggregates D2H failed in agg_selector\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for ( int i = 0; i < R_row_offsets.size() - 1; i++ )\n    {\n        for ( int ii = r_ia[i]; ii < r_ia[i + 1]; ii++ )\n        {\n            int j = r_ja[ii];\n            used_col[j]++;\n\n            if ( used_col[j] > 1 )\n            {\n                std::cout << \"column \" << j << \" is present at least \" << used_col[j] << \" times\" << std::endl;\n            }\n\n            if ( j < 0 || j >= aggregates.size() )\n            {\n                std::cout << \"Error: j out of bounds, j = \" << j << \" and numRows = \" << aggregates.size() << std::endl;\n            }\n            else if  ( agg[j] != i )\n            {\n                std::cout << \"Error: agg[\" << j << \"] = \" << agg[j] << \" != \" << i << std::endl;\n            }\n        }\n    }\n\n    std::cout << \"assert restriction done\" << std::endl;\n}\n\n\n\ntemplate<class T_Config>\nstd::map<std::string, SelectorFactory<T_Config>*> &\nSelectorFactory<T_Config>::getFactories( )\n{\n    static std::map<std::string, SelectorFactory<T_Config>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::registerFactory(std::string name, SelectorFactory<T_Config> *f)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::unregisterFactory(std::string name)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    SelectorFactory<T_Config> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class T_Config>\nvoid SelectorFactory<T_Config>::unregisterFactories( )\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<T_Config> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        SelectorFactory<T_Config> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class T_Config>\nSelector<T_Config> *SelectorFactory<T_Config>::allocate(AMG_Config &cfg, const std::string &current_scope)\n{\n    std::map<std::string, SelectorFactory<T_Config>*> &factories = getFactories( );\n    int agg_lvl_change = cfg.AMG_Config::template getParameter<int>(\"fine_levels\", current_scope);\n    std::string selector;\n    selector = cfg.getParameter<std::string>(\"selector\", current_scope);\n    typename std::map<std::string, SelectorFactory<T_Config> *>::const_iterator it = factories.find(selector);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + selector + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, current_scope);\n};\n\n\n\n//****************************************\n// Explict instantiations\n// **************************************\n#define AMGX_CASE_LINE(CASE) template class Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class SelectorFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/agg_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/selectors/multi_pairwise.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n#include <transpose.h>\n\n#include <async_event.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <thrust/host_vector.h>\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n#include <determinism_checker.h>\n#include <solvers/solver.h>\n\n#include <aggregation/coarseAgenerators/thrust_coarse_A_generator.h>\n#include <aggregation/coarseAgenerators/low_deg_coarse_A_generator.h>\n\n#define EXPERIMENTAL_ITERATIVE_MATCHING\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace multi_pairwise\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//    Kernels\n// ------------------------\n\n__device__\nfloat random_weight2(int i, int j)\n{\n#define RAND_MULTIPLIER                 1145637293\n    unsigned long i_min = (min(i, j) * RAND_MULTIPLIER);\n    unsigned long i_max = (max(i, j) * RAND_MULTIPLIER);\n    return ((float)i_min / i_max);\n}\n\n__device__\nunsigned long random_weight3(int i, int j)\n{\n    unsigned long a;\n    a = (i + j) ^ 8;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType, typename MatrixValueType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices,\n        MatrixValueType *edge_weights, IndexType num_block_rows, IndexType *aggregates,\n        IndexType *strongest_neighbour_1phase, IndexType *strongest_neighbour,\n        const size_t bsize, int phase, int merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    MatrixValueType weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        MatrixValueType     max_weight_unaggregated = 0.;\n        MatrixValueType     max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (phase == 1) { weight = edge_weights[j]; }\n                else { weight = random_weight2(tid, jcol); }\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                if (phase == 2 && strongest_neighbour_1phase[jcol] != tid) { continue; } // if 2nd phase only accept those who gave a hand on the 1st phase\n\n                // Identify strongest aggregated and unaggregated neighbours (method by multi_pairwise)\n                if (aggregates[jcol] == -1 && weight > 0.0 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && random_weight3(tid, jcol) > random_weight3(tid, strongest_unaggregated)))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && weight > 0.0 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && random_weight3(tid, jcol) > random_weight3(tid, strongest_aggregated)))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                if ( merge_singletons == 1 )\n                    // Put in same aggregate as strongest neighbour\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                    // create singleton\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                if (phase == 2)\n                {\n                    MatrixValueType rand_w1 = random_weight2(tid, strongest_neighbour_1phase[tid]);\n                    strongest_neighbour[tid] = max_weight_unaggregated > rand_w1 ? strongest_unaggregated : strongest_neighbour_1phase[tid];\n                }\n                else { strongest_neighbour_1phase[tid] = strongest_unaggregated; }\n            }\n            else\n            {\n                if (phase == 2) { strongest_neighbour[tid] = strongest_neighbour_1phase[tid]; }\n                else { strongest_neighbour_1phase[tid] = tid; }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__device__\nbool atomicJoin( IndexType node, IndexType aggregate, IndexType *aggregates, int *sizes, int allowed )\n{\n    int mySize = sizes[node];\n    int theirSize = sizes[aggregate];\n    int theirSizeOld = theirSize;\n\n    do\n    {\n        int newSize = mySize + theirSize;\n\n        if ( newSize > allowed )\n        {\n            return false;\n        }\n\n        theirSizeOld = theirSize;\n        theirSize = atomicCAS( &sizes[aggregate], theirSize, newSize );\n    }\n    while ( theirSize != theirSizeOld );\n\n    aggregates[node] = aggregate;\n    return true;\n}\n\ntemplate <typename IndexType, typename MatrixValueType, bool use_degree>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V3(const IndexType *row_offsets,\n        const IndexType *column_indices,\n        MatrixValueType *edge_weights,\n        IndexType num_block_rows,\n        IndexType *aggregates,\n        IndexType *strongest_neighbour,\n        int *sizes,\n        int *degree,\n        const size_t bsize,\n        int max_aggregate_size,\n        int merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    MatrixValueType weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        int lowest_degree;\n\n        if ( use_degree )\n        {\n            lowest_degree = degree[tid];    //only interested in finding lower degree than self\n        }\n        else\n        {\n            lowest_degree = 0;    //if we decide to not use degree than just propose to the strongest edge\n        }\n\n        int lowest_degree_neighbor = tid;\n        MatrixValueType     lowest_degree_weight = 1e100; //high start value, so that same degree neighbor won't become lowest degree neighbor\n        MatrixValueType     max_weight_unaggregated = 0.;\n        MatrixValueType     max_weight_aggregated = 0.;\n        int mySize;\n\n        if ( merge_singletons == 2 )\n        {\n            mySize = sizes[tid];\n        }\n        else\n        {\n            mySize = 0;\n        }\n\n        if ( merge_singletons != 2 )\n        {\n            max_aggregate_size = 100000;\n        }\n\n        //this aggregate is already full\n        if (mySize == max_aggregate_size)\n        {\n            aggregates[tid] = tid;\n        }\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                weight = edge_weights[j];\n\n                if (weight <= 0.0) { continue; }\n\n                if ( aggregates[jcol] != -1 ) //aggregated neighbor\n                {\n                    int theirSize;\n\n                    if ( merge_singletons == 2 )\n                    {\n                        theirSize = aggregates[sizes[jcol]];\n                    }\n                    else\n                    {\n                        theirSize = 0;\n                    }\n\n                    //if all neighbors are aggregated, find the strongest edge to neighbor aggregate that is not full yet\n                    if (mySize + theirSize <= max_aggregate_size &&\n                            (weight > max_weight_aggregated)) // aggregated\n                    {\n                        max_weight_aggregated = weight;\n                        strongest_aggregated = jcol;\n                    }\n                }\n                else //unaggregated neighbor\n                {\n                    if ( use_degree && merge_singletons == 2 )\n                    {\n                        int theirSize = sizes[jcol];\n                        //get lowest degree neighbor or find out that there is no lower degree neighbor\n                        int current_degree = degree[jcol];\n\n                        if ( mySize + theirSize <= max_aggregate_size && (current_degree < lowest_degree || current_degree == lowest_degree && weight > lowest_degree_weight) )\n                        {\n                            lowest_degree = current_degree;\n                            lowest_degree_weight = weight;\n                            lowest_degree_neighbor = jcol;\n                        }\n\n                        //get highest weight neighbor\n                        if ( mySize + theirSize <= max_aggregate_size && (weight > max_weight_unaggregated) )\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( use_degree && merge_singletons != 2 ) //same as above but ignore sizes\n                    {\n                        //get lowest degree neighbor or find out that there is no lower degree neighbor\n                        int current_degree = degree[jcol];\n\n                        if ( current_degree < lowest_degree || current_degree == lowest_degree && weight > lowest_degree_weight)\n                        {\n                            lowest_degree = current_degree;\n                            lowest_degree_weight = weight;\n                            lowest_degree_neighbor = jcol;\n                        }\n\n                        //get highest weight neighbor\n                        if (weight > max_weight_unaggregated)\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( !use_degree && merge_singletons == 2 )\n                    {\n                        //get highest weight neighbor only but pay attention to the aggregate sizes\n                        int theirSize = sizes[jcol];                        //get highest weight neighbor\n\n                        if ( mySize + theirSize <= max_aggregate_size && (weight > max_weight_unaggregated) )\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( !use_degree && merge_singletons != 2 )\n                    {\n                        //just highest weight\n                        if (weight > max_weight_unaggregated)\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n                }\n            }\n\n            //prefer lowest degree neighbor\n            if ( lowest_degree_neighbor != tid )\n            {\n                strongest_unaggregated = lowest_degree_neighbor;\n            }\n\n            if (strongest_unaggregated != -1) //Unaggregated neighbor exists\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;    //assign strongest aggregated\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated but small enough aggregated neighbors exist\n            {\n                if ( merge_singletons == 0 )\n                {\n                    aggregates[tid] = tid;\n                }\n\n                if ( merge_singletons == 1 )\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n\n                if ( merge_singletons == 2)\n                {\n                    atomicJoin( tid, aggregates[strongest_aggregated], aggregates, sizes, max_aggregate_size );    //try to join, can fail. maybe it works next round.\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated == -1) //no feasable neighbor at all, become singleton\n            {\n                strongest_neighbour[tid] = tid;    //become singleton\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeDegree( const IndexType *ia, const IndexType *ja, const ValueType *weights, IndexType *aggregates, IndexType *sizes, IndexType *degree, IndexType numRows, IndexType max_aggregate_size)\n{\n    int i = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( i < numRows )\n    {\n        int myDegree = 0;\n        IndexType ia_ip1 = ia[i + 1];\n\n        for ( IndexType ii = ia[i]; ii < ia_ip1; ii++ )\n        {\n            IndexType j = ja[ii];\n\n            if ( j == i )\n            {\n                continue;\n            }\n\n            int mySize, theirSize;\n\n            if ( sizes != NULL )\n            {\n                mySize = sizes[i];\n                theirSize = sizes[j];\n            }\n            else\n            {\n                mySize = theirSize = 0;\n            }\n\n            if ( weights[ii] > 0.0 && aggregates[j] == -1 && mySize + theirSize <= max_aggregate_size )\n            {\n                myDegree++;\n            }\n        }\n\n        degree[i] = myDegree;\n        i += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeSingletonsSmart(const IndexType *ia, const IndexType *ja, const ValueType *weights, IndexType *aggregates, IndexType *sizes, IndexType numRows, int max_aggregate_size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        //unaggregated nodes try to join or create their own aggregate\n        if ( aggregates[tid] == -1 )\n        {\n            bool joined = false;\n\n            while ( !joined )\n            {\n                int neighbor_aggregate = -1;\n                ValueType max_weight = 0.0;\n                IndexType mySize = sizes[tid];\n\n                for (IndexType ii = ia[tid]; ii < ia[tid + 1]; ii++)\n                {\n                    IndexType j = ja[ii];\n\n                    if (j == tid || j >= numRows) { continue; }\n\n                    if ( aggregates[j] != -1 && sizes[aggregates[j]] + mySize <= max_aggregate_size && weights[ii] > max_weight )\n                    {\n                        neighbor_aggregate = aggregates[j];\n                        max_weight = weights[ii];\n                    }\n                }\n\n                //no possible neighbor found\n                if ( neighbor_aggregate == -1 )\n                {\n                    //create own aggregate\n                    aggregates[tid] = tid;\n                    joined = true;\n                }\n                else\n                {\n                    //try to join\n                    joined = atomicJoin( tid, neighbor_aggregate, aggregates, sizes, max_aggregate_size );\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid updateAggregateSizes( IndexType *sizesSource, IndexType *sizes, IndexType *aggregates, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        IndexType agg = aggregates[tid];\n        IndexType aggregateSize = sizes[agg];\n        IndexType mySize = sizesSource[tid];\n\n        while ( mySize > aggregateSize )\n        {\n            aggregateSize = atomicCAS( &sizes[agg], aggregateSize, mySize );\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchEdges(const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour, IndexType *sizes)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n            potential_match_neighbour = strongest_neighbour[potential_match];\n\n            if ( potential_match == tid )\n            {\n                aggregates[tid] = tid;\n            }\n            else if (potential_match != -1 && potential_match_neighbour == tid && tid < potential_match) // we have a match\n            {\n                aggregates[tid] = tid;\n                aggregates[potential_match] = tid;\n\n                if ( sizes != NULL)\n                {\n                    sizes[tid] += sizes[potential_match];\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, int block_size>\n__global__\nvoid countAggregates(const IndexType num_rows, IndexType *aggregates, int *num_unaggregated)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int c = 0;\n    int i = tid;\n\n    while ( i < num_rows )\n    {\n        c += ( aggregates[i] == -1 );\n        i += gridDim.x * blockDim.x;\n    }\n\n    __shared__ volatile int smem[block_size];\n    smem[threadIdx.x] = c;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        atomicAdd(num_unaggregated, smem[0]);\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid joinExistingAggregates(IndexType num_rows, IndexType *aggregates, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n        {\n            aggregates[tid] = aggregates_candidate[tid];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Weights are read from edge_weights array\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType, typename MatrixValueType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices, const MatrixValueType *edge_weights,\n        const int num_block_rows, IndexType *aggregates, int bsize, const int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    MatrixValueType weight;\n\n    while (tid < num_block_rows)\n    {\n        MatrixValueType max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                // Compute edge weight\n                weight = edge_weights[j];\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }    // skip diagonal\n\n                if ( aggregates[jcol] == num_block_rows ) { continue; } // skip dd rows\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && weight > 0 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && random_weight3( tid, jcol ) > random_weight3( tid, strongest_aggregated )))) //\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic == 1)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic == 1)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernelNoDiaProp(const IndexType *dia_idx, const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        diagonal[tIdx] = values[dia_idx[tIdx]];\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// filter edge weights like this:\n// set w_ij = 0 iff\n// w_ij < alpha * sqrt( max_k{w_ik} * max_l{w_jl} )\n// alpha is some constant, 0.25 or 0.5 should work fine\ntemplate<typename IndexType, typename ValueType>\n__global__\nvoid filterWeights( const IndexType *row_offsets, const IndexType *row_indices, const IndexType *col_indices, const IndexType *diag, const ValueType *old_weights, ValueType *new_weights, IndexType num_nonzero_blocks, IndexType num_owned, ValueType alpha )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int i, j, kmin, kmax;\n    ValueType max_ik, max_jl;\n\n    while ( tid < num_nonzero_blocks )\n    {\n        i = row_indices[tid];\n        j = col_indices[tid];\n\n        if ( i != j && j < num_owned )\n        {\n            //find max_k{w_ik}\n            kmin = row_offsets[i];\n            kmax = row_offsets[i + 1];\n            max_ik = 0.0;\n\n            for (int k = kmin; k < kmax; k++)\n            {\n                if ( col_indices[k] != i && old_weights[k] > max_ik )\n                {\n                    max_ik = old_weights[k];\n                }\n            }\n\n            //find max_l{w_jl}\n            kmin = row_offsets[j];\n            kmax = row_offsets[j + 1];\n            max_jl = 0.0;\n\n            for (int l = kmin; l < kmax; l++)\n            {\n                if ( col_indices[l] != j && old_weights[l] > max_jl )\n                {\n                    max_jl = old_weights[l];\n                }\n            }\n\n            //test squared inequality\n            if ( old_weights[tid] * old_weights[tid] < alpha * alpha * max_ik * max_jl )\n            {\n                new_weights[tid] = 0.0;\n            }\n            else //rescale to relative importance. this should also increase the chance of a handshake\n            {\n                new_weights[tid] = old_weights[tid];\n            }\n\n            //              new_weights[tid] = old_weights[tid] / sqrt(max_ik*max_jl);\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueType>\n__global__\nvoid gatherValuesInterleaved( const ValueType *inValues, ValueType *outValues, IndexType nnz, int sq_blocksize, int index_offset )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < nnz )\n    {\n        //at least the write is coalesced\n        outValues[tid] = inValues[tid * sq_blocksize + index_offset];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeV, typename ValueTypeM>\n__global__\nvoid addToWeights( ValueTypeM *edge_weights, const ValueTypeV *x, const IndexType *row_indices, IndexType *col_indices, IndexType nnz, double scale )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < nnz )\n    {\n        int i = row_indices[tid];\n        int j = col_indices[tid];\n        edge_weights[tid] -= static_cast<ValueTypeM>( scale * fabs( x[i] - x[j] ) );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename ValueType, typename IndexType>\n__global__\nvoid rescaleVector( ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = 2 * x[tid] - 1;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//    Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nMultiPairwiseSelectorBase<T_Config>::MultiPairwiseSelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    two_phase = cfg.AMG_Config::template getParameter<int>(\"handshaking_phases\", cfg_scope) == 2;\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    aggregation_passes = cfg.AMG_Config::template getParameter<int>(\"aggregation_passes\", cfg_scope); //default to size 8 aggregates. maybe its more convenient to have that as a config parameter\n    filter_weights = cfg.AMG_Config::template getParameter<int>(\"filter_weights\", cfg_scope); //by default: no filtering\n    filter_weights_alpha = cfg.AMG_Config::template getParameter<double>( \"filter_weights_alpha\", cfg_scope ); //default to 0.25\n    full_ghost_level = cfg.AMG_Config::template getParameter<int>( \"full_ghost_level\", cfg_scope ); //defaults to 0\n    notay_weights = cfg.AMG_Config::template getParameter<int>( \"notay_weights\", cfg_scope ); //defaults to 0\n    ghost_offdiag_limit = cfg.AMG_Config::template getParameter<int>( \"ghost_offdiag_limit\", cfg_scope ); //defaults to 0\n    merge_singletons = cfg.AMG_Config::template getParameter<int>( \"merge_singletons\", cfg_scope ); //defaults to 1\n    weight_formula = cfg.AMG_Config::template getParameter<int>( \"weight_formula\", cfg_scope ); //wheight formula defaults to 0\n    serial_matching = cfg.AMG_Config::template getParameter<int>( \"serial_matching\", cfg_scope ) != 0; //will use a serial matching algorithm instead of handshake\n    modified_handshake = cfg.AMG_Config::template getParameter<int>(\"modified_handshake\", cfg_scope ) == 1;\n    //passes = 1 -> max = 3\n    //passes = 2 -> max = 5\n    //passes = 3 -> max = 10\n    //passes = 4 -> max = 18\n    max_aggregate_size = 2;\n\n    for (int i = 1; i < aggregation_passes; i ++)\n    {\n        max_aggregate_size *= 2;\n    }\n\n    max_aggregate_size += aggregation_passes - (aggregation_passes / 2);\n    mCfg = cfg;\n    mCfg_scope = cfg_scope;\n}\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(Matrix_h &A,\n        typename Matrix_h::IVector &aggregates, typename Matrix_h::IVector &aggregates_global, int &num_aggregates, MVector &edge_weights, IVector &sizes)\n{\n    FatalError(\"MultiPairwise selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// device specialization\n\n//edge_weights is an in/out parameter:\n//if its size is zero, the edge_weights will be computed from A and stored into edge_weights\n//else the edge_weights will not be computed and assumed to be valid for the given A. the value array of A is not used in this case\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(Matrix_d &A,\n        typename Matrix_d::IVector &aggregates,\n        typename Matrix_d::IVector &aggregates_global,\n        int &num_aggregates,\n        MVector &edge_weights,\n        IVector &sizes)\n{\n    IndexType num_block_rows = (int) A.get_num_rows();\n    IndexType num_nonzero_blocks = (int) A.get_num_nz();\n    // both ways are supported\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector &row_indices = A.row_indices;\n    row_indices.resize( total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    IndexType total_rows = (A.is_matrix_singleGPU()) ? A.get_num_rows() : A.manager->num_rows_all();\n    aggregates.resize(total_rows);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n\n    if ( this->merge_singletons == 2 && sizes.size() == 0 )\n    {\n        sizes.resize( total_rows, 1 );    //init with all ones\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_1phase(num_block_rows, -1);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_block_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *strongest_neighbour_1phase_ptr = strongest_neighbour_1phase.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1 );\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    bool computeWeights = ( edge_weights.size() == 0 );\n\n    if (computeWeights)\n    {\n        if ( A.hasProps( DIAG ) )\n        {\n            edge_weights.resize( num_nonzero_blocks + num_block_rows, 0.0 );\n        }\n        else\n        {\n            edge_weights.resize( num_nonzero_blocks + 1, -1 );    //+1 is important to some algorithms\n        }\n    }\n\n    ValueType *edge_weights_ptr = edge_weights.raw();\n    ValueType *rand_edge_weights_ptr = NULL;\n    cudaStream_t str = amgx::thrust::global_thread_handle::get_stream();\n\n    // Compute the edge weights\n    if ( computeWeights )\n    {\n        const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n        //compute with std formula\n        cudaFuncSetCacheConfig(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, ValueType>, cudaFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                A_row_indices_ptr,\n                A_column_indices_ptr,\n                A_dia_idx_ptr,\n                A_nonzero_values_ptr,\n                num_nonzero_blocks,\n                edge_weights_ptr,\n                rand_edge_weights_ptr,\n                num_block_rows,\n                A.get_block_dimy(),\n                this->m_aggregation_edge_weight_component,\n                this->weight_formula);\n        cudaCheckError();\n    }\n\n    //filter weights if desired\n    if ( this->filter_weights == 1 )\n    {\n        MVector tmp( edge_weights.size() );\n        const int num_blocks_filter = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n        cudaStreamSynchronize(str);\n        cudaCheckError();\n        filterWeights <<< num_blocks_filter, threads_per_block, 0, str>>>( A_row_offsets_ptr,\n                A_row_indices_ptr,\n                A_column_indices_ptr,\n                A_dia_idx_ptr,\n                edge_weights_ptr,\n                tmp.raw(),\n                num_nonzero_blocks,\n                num_block_rows,\n                this->filter_weights_alpha);\n                cudaCheckError();\n        cudaStreamSynchronize(str);\n        cudaCheckError();\n        tmp.swap( edge_weights );\n        edge_weights_ptr = edge_weights.raw();\n    }\n\n// compute matching\n    if ( !this->serial_matching )\n    {\n        IVector degree;\n\n        if ( this->modified_handshake )\n        {\n            degree.resize( num_block_rows );\n        }\n\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n        // TODO: allocate host pinned memory\n        AsyncEvent *throttle_event = new AsyncEvent;\n        throttle_event->create();\n        typename Matrix_h::IVector h_unagg_vec(1);\n        typename Matrix_d::IVector d_unagg_vec(1);\n        int *unaggregated = h_unagg_vec.raw();\n        int *d_unaggregated = d_unagg_vec.raw();\n#endif\n        int icount, s = 1;\n        {\n            icount = 0;\n            ValueType *weights_ptr = edge_weights_ptr;\n\n            do\n            {\n                if ( !this->two_phase )\n                {\n                    if ( this->modified_handshake )\n                        computeDegree <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                aggregates_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                num_block_rows,\n                                this->max_aggregate_size );\n                                cudaCheckError();\n\n                    // 1-phase handshaking\n                    if ( this->modified_handshake )\n                        findStrongestNeighbourBlockDiaCsr_V3<IndexType, ValueType, true>\n                        <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                num_block_rows,\n                                aggregates_ptr,\n                                strongest_neighbour_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                A.get_block_dimy(),\n                                this->max_aggregate_size,\n                                this->merge_singletons);\n                    else\n                        findStrongestNeighbourBlockDiaCsr_V3<IndexType, ValueType, false>\n                        <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                num_block_rows,\n                                aggregates_ptr,\n                                strongest_neighbour_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                A.get_block_dimy(),\n                                this->max_aggregate_size,\n                                this->merge_singletons);\n\n                    cudaCheckError();\n                }\n                else\n                {\n                    // 2-phase handshaking\n                    findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                    cudaCheckError();\n                    // 2nd phase: for each block_row, find the strongest neighbour among those who gave hand on 1st phase\n                    findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 2, this->merge_singletons);\n                    cudaCheckError();\n                }\n\n                // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n                if ( this->merge_singletons == 2 )\n                {\n                    matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr, sizes.raw());\n                    cudaCheckError();\n                }\n                else\n                {\n                    matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr, (int *)NULL);\n                }\n\n                cudaCheckError();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n                s = (icount & 1);\n\n                if ( s == 0 )\n                {\n                    // count unaggregated vertices\n                    cudaError_t cuda_rc = cudaMemsetAsync(d_unaggregated, 0, sizeof(int), str);\n                    if (cuda_rc != cudaSuccess)\n                    {\n                        FatalError(\"cudaMemsetAsync failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                    }\n                    countAggregates<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, d_unaggregated);\n                    cudaCheckError();\n                    cuda_rc = cudaMemcpyAsync(unaggregated, d_unaggregated, sizeof(int), cudaMemcpyDeviceToHost, str);\n                    if (cuda_rc != cudaSuccess)\n                    {\n                        FatalError(\"cudaMemcpyAsync failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                    }\n                    throttle_event->record(str);\n                }\n                else\n                {\n                    throttle_event->sync();\n                    numUnassigned_previous = numUnassigned;\n                    numUnassigned = *unaggregated;\n                }\n\n#else\n                cudaError_t cuda_rc = cudaStreamSynchronize(str);\n                if (cuda_rc != cudaSuccess)\n                {\n                    FatalError(\"cudaStreamSynchronize failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                }\n                numUnassigned_previous = numUnassigned;\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n#endif\n                icount++;\n            }\n            while ( (s == 0) || !(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous));\n\n        }\n        //    printf(\"%i,\\n\", icount);\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n        delete throttle_event;\n#endif\n    }\n    else\n    {\n        computeMatchingSerialGreedy( A, aggregates, num_aggregates, edge_weights );\n    }\n\n    if ( this->merge_singletons == 1 )\n    {\n        // Merge remaining vertices with current aggregates\n        if (this->deterministic != 1)\n        {\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, (IndexType *) NULL);\n                cudaCheckError();\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, aggregates_candidate.raw());\n                cudaCheckError();\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n\n            aggregates_candidate.resize(0);\n        }\n    }\n    else if (this->merge_singletons == 0 )\n    {\n        //make singletons\n        aggregateSingletons <<< num_blocks, threads_per_block, 0, str>>>( aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n    else if ( this->merge_singletons == 2 )\n    {\n        //merges all remaining singletons into adequate neighbors if possible\n        mergeSingletonsSmart <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                A_column_indices_ptr,\n                edge_weights_ptr,\n                aggregates_ptr,\n                sizes.raw(),\n                num_block_rows,\n                this->max_aggregate_size);\n        cudaCheckError();\n    }\n\n    //This will assign num_aggregates to the pseudo aggregate without counting it. Perfect!\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n\n    if ( this->merge_singletons == 2 )\n    {\n        //udpate the sizes vector, so it matches the renumbered aggregates size\n        IVector sizesSource;\n        sizesSource.swap( sizes );\n        sizes.resize( num_aggregates, 1 );\n        updateAggregateSizes <<< num_blocks, threads_per_block, 0, str>>>( sizesSource.raw(), sizes.raw(), aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n\n}\n\n\n//instead of a handshake, we use a serial greedy algorithm to compute a better matching\n//the algorithm:\n// 1. compute degree of every node and sort nodes by degree into double linked list\n// 2. while non-isolated nodes left:\n//        take node with minimum degree > 0\n//        find strongest edge to unaggregated node and assign to new aggregate\n//        remove both nodes from linked list\n//        decrease degree of each neighbor by one for each of the two nodes\n//        update list\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeMatchingSerialGreedy( const Matrix_d &A, IVector &aggregates, int &numAggregates, MVector &edge_weights)\n{\n    IndexType numRows = A.row_offsets.size() - 1;\n    IndexType nnz = A.col_indices.size();\n    //allocate memory on host\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[nnz];\n    ValueType *w = new ValueType[nnz];\n    IndexType *agg = new IndexType[numRows];\n    IndexType *deg = new IndexType[numRows];\n    //copy\n    cudaError_t cuda_rc = cudaMemcpy( ia, A.row_offsets.raw(), sizeof(IndexType) * (numRows + 1), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy row_offsets D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( ja, A.col_indices.raw(), sizeof(IndexType)*nnz, cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy col_indices D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( w, edge_weights.raw(), sizeof(ValueType)*nnz, cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy edge_weights D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    //init agg and compute the degree of each aggregate\n    int max_degree = 0;\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        agg[i] = -1;\n        int degree = 0;\n\n        for (IndexType ii = ia[i]; ii < ia[i + 1]; ii++) //only care for positive weights\n        {\n            if ( ja[ii] != i && w[ii] > 0.0 )\n            {\n                degree++;\n            }\n        }\n\n        if ( degree > max_degree )\n        {\n            max_degree = degree;\n        }\n\n        deg[i] = degree;\n    }\n\n    if ( max_degree >= numRows )\n    {\n        FatalError( \"max degree is greater than numRows.\", AMGX_ERR_UNKNOWN );\n    }\n\n    //init double linked list\n    IndexType *fwd = new IndexType[numRows + max_degree + 1];\n    IndexType *bwd = new IndexType[numRows + max_degree + 1];\n\n    for (IndexType i = 0; i < numRows + max_degree + 1; i++)\n    {\n        fwd[i] = i;\n        bwd[i] = i;\n    }\n\n    IndexType nodesLeft = numRows;\n    numAggregates = 0;\n\n    //insert nodes into list\n    for (IndexType i = numRows - 1; i >= 0; i--) //inserting in backward order the nodes will be sorted by index in case of same degree\n    {\n        //insert forward following root\n        fwd[i] = fwd[numRows + deg[i]];\n        fwd[numRows + deg[i]] = i;\n        //insert backward\n        bwd[i] = numRows + deg[i];\n        bwd[fwd[i]] = i;\n\n        //isolated nodes cannot be aggregated\n        if ( deg[i] == 0 )\n        {\n            nodesLeft--;\n        }\n    }\n\n    while ( nodesLeft > 0 )\n    {\n        IndexType node = numRows;\n        int degree;\n\n        for (degree = 1; degree <= max_degree; degree++)\n        {\n            //list not empty -> select node\n            if ( fwd[numRows + degree] < numRows ) //selecting the first node will select the most recently inserted one or the one with lowest index. both is preferable\n            {\n                node = fwd[numRows + degree];\n            }\n\n            if ( node < numRows )\n            {\n                break;\n            }\n        }\n\n        //no node with degree > 1 found even though nodesLeft > 0\n        if ( node == numRows )\n        {\n            FatalError(\"nodeLeft counting or list invalid\", AMGX_ERR_UNKNOWN );\n        }\n\n        if ( agg[node] != -1 )\n        {\n            FatalError(\"node is already aggregated\", AMGX_ERR_UNKNOWN );\n        }\n\n        //find strongest edge\n        ValueType max_weight = 0.0;\n        IndexType max_node = numRows; //use this as gatekeeper, so if weight == 0 the node index will not be greater than this\n\n        for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1 || j == node)\n            {\n                continue;\n            }\n\n            if ( w[ii] > 0.0 )\n            {\n                degree--;\n            }\n\n            //deterministic, doesn't selects 0 weight.\n            if ( w[ii] > max_weight || (w[ii] == max_weight && j > max_node) ) //always taking the edge pointing to the max node can give good alignment if numbering is structured\n            {\n                max_node = j;\n                max_weight = w[ii];\n            }\n        } //Note that there has to be at least one neighbor node because degree of node is at least 1.\n\n        if ( max_node == numRows )\n        {\n            FatalError( \"node  has no neighbor although degree of node is at least 1\", AMGX_ERR_UNKNOWN );\n        }\n\n        if ( degree != 0 )\n        {\n            FatalError( \"node degree corrupted\", AMGX_ERR_UNKNOWN );\n        }\n\n        //aggregate\n        agg[node] = node;\n        agg[max_node] = node;\n        numAggregates++;\n        nodesLeft -= 2;\n        //remove from list\n        fwd[bwd[node]] = fwd[node];\n        bwd[fwd[node]] = bwd[node];\n        fwd[bwd[max_node]] = fwd[max_node];\n        bwd[fwd[max_node]] = bwd[max_node];\n\n        //update neighbors and list\n        //max_node first\n        for (IndexType ii = ia[max_node]; ii < ia[max_node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1  || w[ii] <= 0.0)\n            {\n                continue;\n            }\n\n            //remove j from list\n            fwd[bwd[j]] = fwd[j];\n            bwd[fwd[j]] = bwd[j];\n            //update degree of j\n            deg[j]--;\n            //add j back to start of the list\n            fwd[j] = fwd[numRows + deg[j]];\n            bwd[j] = numRows + deg[j];\n            bwd[fwd[j]] = j;\n            fwd[bwd[j]] = j;\n\n            if (deg[j] == 0)\n            {\n                nodesLeft--;\n            }\n        }\n\n        //node second, this will prefer nodes neighbors over max_nodes neighbors when choosing the next node\n        for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1  || w[ii] <= 0.0)\n            {\n                continue;\n            }\n\n            //remove j from list\n            fwd[bwd[j]] = fwd[j];\n            bwd[fwd[j]] = bwd[j];\n            //update degree of j\n            deg[j]--;\n            //add j back to start of the list\n            fwd[j] = fwd[numRows + deg[j]];\n            bwd[j] = numRows + deg[j];\n            bwd[fwd[j]] = j;\n            fwd[bwd[j]] = j;\n\n            if (deg[j] == 0)\n            {\n                nodesLeft--;\n            }\n        }\n    }\n\n    //copy result back to device\n    cuda_rc = cudaMemcpy( aggregates.raw(), agg, sizeof(IndexType)*numRows, cudaMemcpyHostToDevice );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy aggregates H2D failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    //assert matching\n    for (IndexType node = 0; node < numRows; node++)\n    {\n        if ( agg[node] == -1 )\n        {\n            continue;\n        }\n\n        for ( IndexType partner = 0; partner < numRows; partner++)\n        {\n            if ( agg[partner] == agg[node] )\n            {\n                if ( partner == node )\n                {\n                    continue;\n                }\n\n                bool neighbor = false;\n\n                for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                    if ( ja[ii] == partner )\n                    {\n                        neighbor = true;\n                        break;\n                    }\n\n                if ( !neighbor )\n                {\n                    for (IndexType ii = ia[partner]; ii < ia[partner + 1]; ii++)\n                        if ( ja[ii] == node )\n                        {\n                            neighbor = true;\n                            break;\n                        }\n                }\n\n                if ( !neighbor )\n                {\n                    FatalError(\"Internal error in aggregation selector\", AMGX_ERR_INTERNAL);\n                }\n            }\n        }\n    }\n\n    //you shall not leak memory\n    delete[] ia;\n    delete[] ja;\n    delete[] w;\n    delete[] agg;\n    delete[] deg;\n    delete[] fwd;\n    delete[] bwd;\n}\n\n\n\n\n//this kernel merges aggregate2 into aggregate1\ntemplate<typename IndexType>\n__global__\nvoid mergeAggregates(IndexType *aggregate1, const IndexType *aggregate2, IndexType sizeAggregate1, IndexType sizeAggregate2, IndexType sizeAggregate3)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < sizeAggregate1 )\n    {\n        if ( aggregate1[tid] == sizeAggregate2 )\n        {\n            aggregate1[tid] = sizeAggregate3;\n        }\n        else\n        {\n            aggregate1[tid] = aggregate2[aggregate1[tid]];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeIncompleteGalerkin( const Matrix_h &A,\n        Matrix_h &Ac,\n        const typename Matrix_h::IVector &aggregates,\n        const typename Matrix_h::IVector &R_row_offsets,\n        const typename Matrix_h::IVector &R_column_indices,\n        const int num_aggregates )\n{\n    FatalError(\"computeIncomlpetegalerkin is not supported on host. Run with ghost_offdiag_limit=0 instead.\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeIncompleteGalerkin( const Matrix_d &A,\n        Matrix_d &Ac,\n        const typename Matrix_d::IVector &aggregates,\n        const typename Matrix_d::IVector &R_row_offsets,\n        const typename Matrix_d::IVector &R_column_indices,\n        const int num_aggregates )\n{\n    FatalError(\"computeIncomlpetegalerkin is not implemented yet. run with ghost_offdiag_limit=0 instead.\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate<class TConfig>\nvoid MultiPairwiseSelectorBase<TConfig>::assertRestriction( const IVector &R_row_offsets, const IVector &R_col_indices, const IVector &aggregates )\n{\n    int *r_ia = new int[R_row_offsets.size()];\n    int *r_ja = new int[R_col_indices.size()];\n    int *agg = new int[aggregates.size()];\n    int *used_col = new int[aggregates.size()];\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        used_col[i] = 0;\n    }\n\n    cudaError_t cuda_rc = cudaMemcpy( r_ia, R_row_offsets.raw(), sizeof(int)*R_row_offsets.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy R_row_offsets D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( r_ja, R_col_indices.raw(), sizeof(int)*R_col_indices.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy R_col_indices D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = cudaMemcpy( agg, aggregates.raw(), sizeof(int)*aggregates.size(), cudaMemcpyDeviceToHost );\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMemcpy aggregates D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for ( int i = 0; i < R_row_offsets.size() - 1; i++ )\n    {\n        for ( int ii = r_ia[i]; ii < r_ia[i + 1]; ii++ )\n        {\n            int j = r_ja[ii];\n            used_col[j]++;\n\n            if ( used_col[j] > 1 )\n            {\n                std::cout << \"column \" << j << \" is present at least \" << used_col[j] << \" times\" << std::endl;\n            }\n\n            if ( j < 0 || j >= aggregates.size() )\n            {\n                std::cout << \"Error: j out of bounds, j = \" << j << \" and numRows = \" << aggregates.size() << std::endl;\n            }\n            else if  ( agg[j] != i )\n            {\n                std::cout << \"Error: agg[\" << j << \"] = \" << agg[j] << \" != \" << i << std::endl;\n            }\n        }\n    }\n\n    std::cout << \"assert restriction done\" << std::endl;\n}\n\ntemplate<class T_Config>\nvoid MultiPairwiseSelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        //ghost level matrix. this is a probably a weight matrix\n        Matrix<TConfig> ghostA;\n        ghostA.values.resize(0);\n        //prolongation and restriction operator. this is only needed in when LowDegCoarseAGenerator is used\n        IVector R_row_offsets;\n        IVector R_col_indices;\n        //holds the size of each aggregate\n        IVector sizes;\n        sizes.resize(0);\n        //aggregates for ghost level\n        IVector aggregates_current;\n        IVector aggregates_global_current;\n        bool aggregates_initialized = true;\n\n        if (aggregates.size() == 0)\n        {\n            aggregates_initialized = false;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n            }\n            else\n            {\n                aggregates.resize(A.get_num_rows());\n            }\n        }\n\n        //for mergeAggregates kernel\n        const int threads_per_block = 256;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (A.get_num_rows() - 1) / threads_per_block + 1 );\n        cudaStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n        //initialize and prepare weight matrix\n        Matrix<TConfig> w;\n        w.set_initialized(0);\n        w.addProps(CSR);\n        w.delProps(COO);\n        w.setColsReorderedByColor(false);\n        w.resize( 0, 0, 0, 1, 1, true ); //empty scalar 0x0 matrix\n        w.values.resize(0); //matrix resize sets the values array to nnz+1 for no apparent reason\n        IndexType targetSize = 1;\n        //initialize coarse A generator\n        CoarseAGenerator<TConfig> *cag;\n        const bool use_restriction = true;\n        const bool shrink_ghost_level = false;\n        cag = new LowDegCoarseAGenerator<TConfig>(mCfg, mCfg_scope);\n        // This will make coarseAGenerator to allocate more memory inside of  galerkin\n        ghostA.manager = new DistributedManager<TConfig>();\n        w.manager = new DistributedManager<TConfig>();\n        Matrix<TConfig> *curA = &A;\n\n        //foreach pass do:\n        // 1. build aggregates and weights\n        // 2. create weight matrix (in full_ghost_level mode this is the input matrix or the last ghostlevel matrix)\n        // 3. if in full ghost level mode, build R\n        // 4. compute next level\n        for (int current_pass = 1; true; current_pass++)\n        {\n            const IndexType numRows = curA->get_num_rows();\n            const IndexType nnz = curA->get_num_nz();\n            targetSize *= 2;\n\n            if ( full_ghost_level )\n            {\n                w.values.resize(0);    //compute weights from curA\n            }\n            else\n            {\n                w.values.swap( ghostA.values );    //use the weights computed with the galerkin operator (this will do nothing in the first pass, both values have size 0)\n            }\n\n            // create aggregates from correct input matrix\n            setAggregates_common_sqblocks( *curA, aggregates_current, aggregates_global_current, num_aggregates, w.values, sizes );\n\n\n            if ( current_pass > 1 )\n            {\n                //merge original aggregate with the newly created ones\n                mergeAggregates <<< num_blocks, threads_per_block, 0, stream >>>( aggregates.raw(), aggregates_current.raw(), A.get_num_rows(), numRows, num_aggregates );\n                cudaCheckError();\n                //mergeAggregates<<< num_blocks, threads_per_block, 0, stream >>>( aggregates_global.raw(), aggregates_global_current.raw(), A.get_num_rows() );\n                //cudaCheckError();\n            }\n\n            //try to free memory\n            if ( full_ghost_level )\n            {\n                //then we don't need to save the weights, only for original level to do post processing\n                w.values.resize(0);\n            }\n            else\n            {\n                //save edge weights for original level later\n                //in that case we can throw away the values of ghostA as we will use the values to compute the next ghost level\n                ghostA.values.resize(0);\n            }\n\n            // this is the break condition for the loop\n            if ( current_pass >= aggregation_passes || num_aggregates <= 1 || num_aggregates == numRows)\n            {\n                //this means, aggregates has not been initialized yet\n                if ( !aggregates_initialized )\n                {\n                    aggregates.swap( aggregates_current );\n                }\n\n                if ( !aggregates_initialized )\n                {\n                    aggregates_global.swap( aggregates_global_current );\n                }\n\n                cudaStreamSynchronize( stream );\n                cudaCheckError();\n\n                break;\n            }\n\n            //prepare A to be corrupted\n            curA->set_initialized(0);\n            //swap in ia, ja from curA\n            w.row_offsets.swap( curA->row_offsets );\n            w.col_indices.swap( curA->col_indices );\n\n            if ( full_ghost_level )\n            {\n                if ( shrink_ghost_level && curA->get_block_dimx() > 1)\n                {\n                    //set w to correct size\n                    w.values.resize( nnz );\n                    //define grid and offsets\n                    const int num_blocks_inter = std::min( (int)AMGX_GRID_MAX_SIZE, (int)(nnz - 1) / threads_per_block + 1 );\n                    const int sq_blocksize = A.get_block_dimx() * A.get_block_dimy();\n                    const int index_offset = A.get_block_dimy() * m_aggregation_edge_weight_component + m_aggregation_edge_weight_component;\n                    //do the interleaved copy\n                    gatherValuesInterleaved <<< num_blocks_inter, threads_per_block, 0, stream>>>( A.values.raw(), w.values.raw(), nnz, sq_blocksize, index_offset );\n                    cudaCheckError();\n                    cudaStreamSynchronize( stream );\n                    cudaCheckError();\n                }\n                else\n                {\n                    w.values.swap( curA->values );\n                }\n            }\n\n            w.diag.swap( curA->diag );\n\n            //resize to inform the matrix of its new size\n            if ( full_ghost_level && !shrink_ghost_level )\n            {\n                w.set_block_dimx( A.get_block_dimx() );\n                w.set_block_dimy( A.get_block_dimy() );\n            }\n            else\n            {\n                w.set_block_dimx( 1 );\n                w.set_block_dimy( 1 );\n            }\n\n            w.set_num_rows( numRows );\n            w.set_num_cols( numRows );\n            w.set_num_nz( nnz );\n            w.set_allow_recompute_diag( false );\n\n            if ( curA->hasProps( DIAG ) )\n            {\n                w.addProps( DIAG );\n            }\n\n            //ready to use\n            w.set_initialized(1);\n\n            //compute restriction operator\n            if ( use_restriction )\n            {\n                IVector R_row_indices(aggregates_current);\n                R_row_offsets.resize(num_aggregates + 2);\n                R_col_indices.resize(numRows);\n                thrust_wrapper::sequence<TConfig::memSpace>(R_col_indices.begin(), R_col_indices.end());\n                cudaCheckError();\n                amgx::thrust::sort_by_key(R_row_indices.begin(), R_row_indices.end(), R_col_indices.begin());\n                cudaCheckError();\n                cusp::detail::indices_to_offsets(R_row_indices, R_row_offsets);\n                cudaCheckError();\n                //delete last row, which holds the pseudo aggregate\n                R_row_offsets.resize( num_aggregates + 1);\n                R_col_indices.resize( R_row_offsets[num_aggregates] );\n            }\n\n            // 3. compute galerkin ghost level\n            if ( ghost_offdiag_limit == 0 )\n            {\n                //compute full galerkin\n                cag->computeAOperator(w,\n                                      ghostA,\n                                      aggregates_current,\n                                      R_row_offsets,\n                                      R_col_indices,\n                                      num_aggregates);\n            }\n            else\n            {\n                //compute incomplete galerkin\n                computeIncompleteGalerkin(w,\n                                          ghostA,\n                                          aggregates_current,\n                                          R_row_offsets,\n                                          R_col_indices,\n                                          num_aggregates);\n            }\n\n            //from now on w will be destroyed again.\n            w.set_initialized(0);\n\n            //repair the original A matrix. its ia and ja are in w\n            if ( current_pass == 1 )\n            {\n                //swap back\n                w.row_offsets.swap( A.row_offsets );\n                w.col_indices.swap( A.col_indices );\n\n                //only in that case we have swapped the values\n                if ( full_ghost_level && !shrink_ghost_level )\n                {\n                    w.values.swap( A.values );\n                }\n\n                //save the edge weights of the original level\n                A.diag.swap( w.diag );\n                A.set_initialized(1); //A is repaired now\n                //save the first aggregates into the original aggregate vector so we can merge them later\n                aggregates.swap( aggregates_current );\n                aggregates_global.swap( aggregates_global_current );\n                aggregates_initialized = true;\n                curA = &ghostA;\n            }\n        }\n\n        delete cag;\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MultiPairwise\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//    Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class MultiPairwiseSelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class MultiPairwiseSelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/selectors/multi_pairwise.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <basic_types.h>\n#include <texture.h>\n#include <matrix_analysis.h>\n#include <transpose.h>\n\n#include <async_event.h>\n\n#include <thrust/count.h> //count\n#include <thrust/sort.h> //sort\n#include <thrust/binary_search.h> //lower_bound\n#include <thrust/unique.h> //unique\n#include <thrust/host_vector.h>\n#include <cusp/detail/format_utils.h> //offsets_to_indices\n#include <determinism_checker.h>\n#include <solvers/solver.h>\n\n#include <aggregation/coarseAgenerators/thrust_coarse_A_generator.h>\n#include <aggregation/coarseAgenerators/low_deg_coarse_A_generator.h>\n\n#define EXPERIMENTAL_ITERATIVE_MATCHING\n\nnamespace amgx\n{\nnamespace aggregation\n{\nnamespace multi_pairwise\n{\n\n// include common routines for all selectors\n#include <aggregation/selectors/common_selector.h>\n\n// ------------------------\n//    Kernels\n// ------------------------\n\n__device__\nfloat random_weight2(int i, int j)\n{\n#define RAND_MULTIPLIER                 1145637293\n    unsigned long i_min = (min(i, j) * RAND_MULTIPLIER);\n    unsigned long i_max = (max(i, j) * RAND_MULTIPLIER);\n    return ((float)i_min / i_max);\n}\n\n__device__\nunsigned long random_weight3(int i, int j)\n{\n    unsigned long a;\n    a = (i + j) ^ 8;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return a;\n}\n\n\n// findStrongestNeighbour kernel for block_dia_csr_matrix format\n// Reads the weight from edge_weights array\ntemplate <typename IndexType, typename MatrixValueType>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices,\n        MatrixValueType *edge_weights, IndexType num_block_rows, IndexType *aggregates,\n        IndexType *strongest_neighbour_1phase, IndexType *strongest_neighbour,\n        const size_t bsize, int phase, int merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    MatrixValueType weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        MatrixValueType     max_weight_unaggregated = 0.;\n        MatrixValueType     max_weight_aggregated = 0.;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (phase == 1) { weight = edge_weights[j]; }\n                else { weight = random_weight2(tid, jcol); }\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                if (phase == 2 && strongest_neighbour_1phase[jcol] != tid) { continue; } // if 2nd phase only accept those who gave a hand on the 1st phase\n\n                // Identify strongest aggregated and unaggregated neighbours (method by multi_pairwise)\n                if (aggregates[jcol] == -1 && weight > 0.0 && (weight > max_weight_unaggregated || (weight == max_weight_unaggregated && random_weight3(tid, jcol) > random_weight3(tid, strongest_unaggregated)))) // unaggregated\n                {\n                    max_weight_unaggregated = weight;\n                    strongest_unaggregated = jcol;\n                }\n                else if (aggregates[jcol] != -1 && weight > 0.0 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && random_weight3(tid, jcol) > random_weight3(tid, strongest_aggregated)))) // aggregated\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated\n            {\n                if ( merge_singletons == 1 )\n                    // Put in same aggregate as strongest neighbour\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                    // create singleton\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n            else if (strongest_unaggregated != -1)\n            {\n                if (phase == 2)\n                {\n                    MatrixValueType rand_w1 = random_weight2(tid, strongest_neighbour_1phase[tid]);\n                    strongest_neighbour[tid] = max_weight_unaggregated > rand_w1 ? strongest_unaggregated : strongest_neighbour_1phase[tid];\n                }\n                else { strongest_neighbour_1phase[tid] = strongest_unaggregated; }\n            }\n            else\n            {\n                if (phase == 2) { strongest_neighbour[tid] = strongest_neighbour_1phase[tid]; }\n                else { strongest_neighbour_1phase[tid] = tid; }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__device__\nbool atomicJoin( IndexType node, IndexType aggregate, IndexType *aggregates, int *sizes, int allowed )\n{\n    int mySize = sizes[node];\n    int theirSize = sizes[aggregate];\n    int theirSizeOld = theirSize;\n\n    do\n    {\n        int newSize = mySize + theirSize;\n\n        if ( newSize > allowed )\n        {\n            return false;\n        }\n\n        theirSizeOld = theirSize;\n        theirSize = atomicCAS( &sizes[aggregate], theirSize, newSize );\n    }\n    while ( theirSize != theirSizeOld );\n\n    aggregates[node] = aggregate;\n    return true;\n}\n\ntemplate <typename IndexType, typename MatrixValueType, bool use_degree>\n__global__\nvoid findStrongestNeighbourBlockDiaCsr_V3(const IndexType *row_offsets,\n        const IndexType *column_indices,\n        MatrixValueType *edge_weights,\n        IndexType num_block_rows,\n        IndexType *aggregates,\n        IndexType *strongest_neighbour,\n        int *sizes,\n        int *degree,\n        const size_t bsize,\n        int max_aggregate_size,\n        int merge_singletons)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    MatrixValueType weight;\n    int jcol;\n\n    while (tid < num_block_rows)\n    {\n        int strongest_unaggregated = -1;\n        int strongest_aggregated = -1;\n        int lowest_degree;\n\n        if ( use_degree )\n        {\n            lowest_degree = degree[tid];    //only interested in finding lower degree than self\n        }\n        else\n        {\n            lowest_degree = 0;    //if we decide to not use degree than just propose to the strongest edge\n        }\n\n        int lowest_degree_neighbor = tid;\n        MatrixValueType     lowest_degree_weight = 1e100; //high start value, so that same degree neighbor won't become lowest degree neighbor\n        MatrixValueType     max_weight_unaggregated = 0.;\n        MatrixValueType     max_weight_aggregated = 0.;\n        int mySize;\n\n        if ( merge_singletons == 2 )\n        {\n            mySize = sizes[tid];\n        }\n        else\n        {\n            mySize = 0;\n        }\n\n        if ( merge_singletons != 2 )\n        {\n            max_aggregate_size = 100000;\n        }\n\n        //this aggregate is already full\n        if (mySize == max_aggregate_size)\n        {\n            aggregates[tid] = tid;\n        }\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                jcol = column_indices[j];\n\n                if (tid == jcol || jcol >= num_block_rows) { continue; }    // skip diagonal and halo\n\n                weight = edge_weights[j];\n\n                if (weight <= 0.0) { continue; }\n\n                if ( aggregates[jcol] != -1 ) //aggregated neighbor\n                {\n                    int theirSize;\n\n                    if ( merge_singletons == 2 )\n                    {\n                        theirSize = aggregates[sizes[jcol]];\n                    }\n                    else\n                    {\n                        theirSize = 0;\n                    }\n\n                    //if all neighbors are aggregated, find the strongest edge to neighbor aggregate that is not full yet\n                    if (mySize + theirSize <= max_aggregate_size &&\n                            (weight > max_weight_aggregated)) // aggregated\n                    {\n                        max_weight_aggregated = weight;\n                        strongest_aggregated = jcol;\n                    }\n                }\n                else //unaggregated neighbor\n                {\n                    if ( use_degree && merge_singletons == 2 )\n                    {\n                        int theirSize = sizes[jcol];\n                        //get lowest degree neighbor or find out that there is no lower degree neighbor\n                        int current_degree = degree[jcol];\n\n                        if ( mySize + theirSize <= max_aggregate_size && (current_degree < lowest_degree || current_degree == lowest_degree && weight > lowest_degree_weight) )\n                        {\n                            lowest_degree = current_degree;\n                            lowest_degree_weight = weight;\n                            lowest_degree_neighbor = jcol;\n                        }\n\n                        //get highest weight neighbor\n                        if ( mySize + theirSize <= max_aggregate_size && (weight > max_weight_unaggregated) )\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( use_degree && merge_singletons != 2 ) //same as above but ignore sizes\n                    {\n                        //get lowest degree neighbor or find out that there is no lower degree neighbor\n                        int current_degree = degree[jcol];\n\n                        if ( current_degree < lowest_degree || current_degree == lowest_degree && weight > lowest_degree_weight)\n                        {\n                            lowest_degree = current_degree;\n                            lowest_degree_weight = weight;\n                            lowest_degree_neighbor = jcol;\n                        }\n\n                        //get highest weight neighbor\n                        if (weight > max_weight_unaggregated)\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( !use_degree && merge_singletons == 2 )\n                    {\n                        //get highest weight neighbor only but pay attention to the aggregate sizes\n                        int theirSize = sizes[jcol];                        //get highest weight neighbor\n\n                        if ( mySize + theirSize <= max_aggregate_size && (weight > max_weight_unaggregated) )\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n\n                    if ( !use_degree && merge_singletons != 2 )\n                    {\n                        //just highest weight\n                        if (weight > max_weight_unaggregated)\n                        {\n                            strongest_unaggregated = jcol;\n                            max_weight_unaggregated = weight;\n                        }\n                    }\n                }\n            }\n\n            //prefer lowest degree neighbor\n            if ( lowest_degree_neighbor != tid )\n            {\n                strongest_unaggregated = lowest_degree_neighbor;\n            }\n\n            if (strongest_unaggregated != -1) //Unaggregated neighbor exists\n            {\n                strongest_neighbour[tid] = strongest_unaggregated;    //assign strongest aggregated\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated != -1) // All neighbours are aggregated but small enough aggregated neighbors exist\n            {\n                if ( merge_singletons == 0 )\n                {\n                    aggregates[tid] = tid;\n                }\n\n                if ( merge_singletons == 1 )\n                {\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n\n                if ( merge_singletons == 2)\n                {\n                    atomicJoin( tid, aggregates[strongest_aggregated], aggregates, sizes, max_aggregate_size );    //try to join, can fail. maybe it works next round.\n                }\n            }\n\n            if (strongest_unaggregated == -1 && strongest_aggregated == -1) //no feasable neighbor at all, become singleton\n            {\n                strongest_neighbour[tid] = tid;    //become singleton\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid computeDegree( const IndexType *ia, const IndexType *ja, const ValueType *weights, IndexType *aggregates, IndexType *sizes, IndexType *degree, IndexType numRows, IndexType max_aggregate_size)\n{\n    int i = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( i < numRows )\n    {\n        int myDegree = 0;\n        IndexType ia_ip1 = ia[i + 1];\n\n        for ( IndexType ii = ia[i]; ii < ia_ip1; ii++ )\n        {\n            IndexType j = ja[ii];\n\n            if ( j == i )\n            {\n                continue;\n            }\n\n            int mySize, theirSize;\n\n            if ( sizes != NULL )\n            {\n                mySize = sizes[i];\n                theirSize = sizes[j];\n            }\n            else\n            {\n                mySize = theirSize = 0;\n            }\n\n            if ( weights[ii] > 0.0 && aggregates[j] == -1 && mySize + theirSize <= max_aggregate_size )\n            {\n                myDegree++;\n            }\n        }\n\n        degree[i] = myDegree;\n        i += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid mergeSingletonsSmart(const IndexType *ia, const IndexType *ja, const ValueType *weights, IndexType *aggregates, IndexType *sizes, IndexType numRows, int max_aggregate_size)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        //unaggregated nodes try to join or create their own aggregate\n        if ( aggregates[tid] == -1 )\n        {\n            bool joined = false;\n\n            while ( !joined )\n            {\n                int neighbor_aggregate = -1;\n                ValueType max_weight = 0.0;\n                IndexType mySize = sizes[tid];\n\n                for (IndexType ii = ia[tid]; ii < ia[tid + 1]; ii++)\n                {\n                    IndexType j = ja[ii];\n\n                    if (j == tid || j >= numRows) { continue; }\n\n                    if ( aggregates[j] != -1 && sizes[aggregates[j]] + mySize <= max_aggregate_size && weights[ii] > max_weight )\n                    {\n                        neighbor_aggregate = aggregates[j];\n                        max_weight = weights[ii];\n                    }\n                }\n\n                //no possible neighbor found\n                if ( neighbor_aggregate == -1 )\n                {\n                    //create own aggregate\n                    aggregates[tid] = tid;\n                    joined = true;\n                }\n                else\n                {\n                    //try to join\n                    joined = atomicJoin( tid, neighbor_aggregate, aggregates, sizes, max_aggregate_size );\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid updateAggregateSizes( IndexType *sizesSource, IndexType *sizes, IndexType *aggregates, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        IndexType agg = aggregates[tid];\n        IndexType aggregateSize = sizes[agg];\n        IndexType mySize = sizesSource[tid];\n\n        while ( mySize > aggregateSize )\n        {\n            aggregateSize = atomicCAS( &sizes[agg], aggregateSize, mySize );\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n// Kernel that checks if perfect matchs exist\ntemplate <typename IndexType>\n__global__\nvoid matchEdges(const IndexType num_rows, IndexType *aggregates, int *strongest_neighbour, IndexType *sizes)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int potential_match, potential_match_neighbour;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            potential_match = strongest_neighbour[tid];\n            potential_match_neighbour = strongest_neighbour[potential_match];\n\n            if ( potential_match == tid )\n            {\n                aggregates[tid] = tid;\n            }\n            else if (potential_match != -1 && potential_match_neighbour == tid && tid < potential_match) // we have a match\n            {\n                aggregates[tid] = tid;\n                aggregates[potential_match] = tid;\n\n                if ( sizes != NULL)\n                {\n                    sizes[tid] += sizes[potential_match];\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename IndexType, int block_size>\n__global__\nvoid countAggregates(const IndexType num_rows, IndexType *aggregates, int *num_unaggregated)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int c = 0;\n    int i = tid;\n\n    while ( i < num_rows )\n    {\n        c += ( aggregates[i] == -1 );\n        i += gridDim.x * blockDim.x;\n    }\n\n    __shared__ volatile int smem[block_size];\n    smem[threadIdx.x] = c;\n    __syncthreads();\n\n    for ( int off = blockDim.x / 2; off >= 32; off = off / 2 )\n    {\n        if ( threadIdx.x < off )\n        {\n            smem[threadIdx.x] += smem[threadIdx.x + off];\n        }\n\n        __syncthreads();\n    }\n\n    // warp reduce\n    if ( threadIdx.x < 32 )\n    {\n        smem[threadIdx.x] += smem[threadIdx.x + 16];\n        smem[threadIdx.x] += smem[threadIdx.x + 8];\n        smem[threadIdx.x] += smem[threadIdx.x + 4];\n        smem[threadIdx.x] += smem[threadIdx.x + 2];\n        smem[threadIdx.x] += smem[threadIdx.x + 1];\n    }\n\n    if ( threadIdx.x == 0 )\n    {\n        atomicAdd(num_unaggregated, smem[0]);\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid joinExistingAggregates(IndexType num_rows, IndexType *aggregates, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tid < num_rows)\n    {\n        if (aggregates[tid] == -1 && aggregates_candidate[tid] != -1) // Unaggregated row\n        {\n            aggregates[tid] = aggregates_candidate[tid];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel that merges unaggregated vertices its strongest aggregated neighbour\n// Weights are read from edge_weights array\n// For block_dia_csr_matrix_format\ntemplate <typename IndexType, typename MatrixValueType>\n__global__\nvoid mergeWithExistingAggregatesBlockDiaCsr_V2(const IndexType *row_offsets, const IndexType *column_indices, const MatrixValueType *edge_weights,\n        const int num_block_rows, IndexType *aggregates, int bsize, const int deterministic, IndexType *aggregates_candidate)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int jcol;\n    MatrixValueType weight;\n\n    while (tid < num_block_rows)\n    {\n        MatrixValueType max_weight_aggregated = 0.;\n        int strongest_aggregated = -1;\n\n        if (aggregates[tid] == -1) // Unaggregated row\n        {\n            for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n            {\n                // Compute edge weight\n                weight = edge_weights[j];\n                jcol = column_indices[j];\n\n                if (jcol == tid || jcol >= num_block_rows) { continue; }    // skip diagonal\n\n                if ( aggregates[jcol] == num_block_rows ) { continue; } // skip dd rows\n\n                // Identify strongest aggregated neighbour\n                if (aggregates[jcol] != -1 && weight > 0 && (weight > max_weight_aggregated || (weight == max_weight_aggregated && random_weight3( tid, jcol ) > random_weight3( tid, strongest_aggregated )))) //\n                {\n                    max_weight_aggregated = weight;\n                    strongest_aggregated = jcol;\n                }\n            }\n\n            if (strongest_aggregated != -1) // Found a neighbour to aggregate to\n            {\n                if (deterministic == 1)\n                {\n                    aggregates_candidate[tid] = aggregates[strongest_aggregated];\n                }\n                else\n                {\n                    // Put in same aggregate as strongest neighbour\n                    aggregates[tid] = aggregates[strongest_aggregated];\n                }\n            }\n            else // All neighbours are unaggregated, leave alone\n            {\n                if (deterministic == 1)\n                {\n                    aggregates_candidate[tid] = tid;\n                }\n                else\n                {\n                    aggregates[tid] = tid;\n                }\n            }\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// Kernel to extract diagonal for csr_matrix format\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernelNoDiaProp(const IndexType *dia_idx, const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    int tIdx = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while (tIdx < numRows)\n    {\n        diagonal[tIdx] = values[dia_idx[tIdx]];\n        tIdx += gridDim.x * blockDim.x;\n    }\n}\n\n// filter edge weights like this:\n// set w_ij = 0 iff\n// w_ij < alpha * sqrt( max_k{w_ik} * max_l{w_jl} )\n// alpha is some constant, 0.25 or 0.5 should work fine\ntemplate<typename IndexType, typename ValueType>\n__global__\nvoid filterWeights( const IndexType *row_offsets, const IndexType *row_indices, const IndexType *col_indices, const IndexType *diag, const ValueType *old_weights, ValueType *new_weights, IndexType num_nonzero_blocks, IndexType num_owned, ValueType alpha )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n    int i, j, kmin, kmax;\n    ValueType max_ik, max_jl;\n\n    while ( tid < num_nonzero_blocks )\n    {\n        i = row_indices[tid];\n        j = col_indices[tid];\n\n        if ( i != j && j < num_owned )\n        {\n            //find max_k{w_ik}\n            kmin = row_offsets[i];\n            kmax = row_offsets[i + 1];\n            max_ik = 0.0;\n\n            for (int k = kmin; k < kmax; k++)\n            {\n                if ( col_indices[k] != i && old_weights[k] > max_ik )\n                {\n                    max_ik = old_weights[k];\n                }\n            }\n\n            //find max_l{w_jl}\n            kmin = row_offsets[j];\n            kmax = row_offsets[j + 1];\n            max_jl = 0.0;\n\n            for (int l = kmin; l < kmax; l++)\n            {\n                if ( col_indices[l] != j && old_weights[l] > max_jl )\n                {\n                    max_jl = old_weights[l];\n                }\n            }\n\n            //test squared inequality\n            if ( old_weights[tid] * old_weights[tid] < alpha * alpha * max_ik * max_jl )\n            {\n                new_weights[tid] = 0.0;\n            }\n            else //rescale to relative importance. this should also increase the chance of a handshake\n            {\n                new_weights[tid] = old_weights[tid];\n            }\n\n            //              new_weights[tid] = old_weights[tid] / sqrt(max_ik*max_jl);\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueType>\n__global__\nvoid gatherValuesInterleaved( const ValueType *inValues, ValueType *outValues, IndexType nnz, int sq_blocksize, int index_offset )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < nnz )\n    {\n        //at least the write is coalesced\n        outValues[tid] = inValues[tid * sq_blocksize + index_offset];\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\ntemplate<typename IndexType, typename ValueTypeV, typename ValueTypeM>\n__global__\nvoid addToWeights( ValueTypeM *edge_weights, const ValueTypeV *x, const IndexType *row_indices, IndexType *col_indices, IndexType nnz, double scale )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < nnz )\n    {\n        int i = row_indices[tid];\n        int j = col_indices[tid];\n        edge_weights[tid] -= static_cast<ValueTypeM>( scale * fabs( x[i] - x[j] ) );\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\ntemplate <typename ValueType, typename IndexType>\n__global__\nvoid rescaleVector( ValueType *x, IndexType numRows )\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < numRows )\n    {\n        x[tid] = 2 * x[tid] - 1;\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n// -----------------\n//    Methods\n// ----------------\n\n// Constructor\ntemplate<class T_Config>\nMultiPairwiseSelectorBase<T_Config>::MultiPairwiseSelectorBase(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    deterministic = cfg.AMG_Config::template getParameter<IndexType>(\"determinism_flag\", \"default\");\n    max_iterations = cfg.AMG_Config::template getParameter<IndexType>(\"max_matching_iterations\", cfg_scope);\n    numUnassigned_tol = cfg.AMG_Config::template getParameter<double>(\"max_unassigned_percentage\", cfg_scope);\n    two_phase = cfg.AMG_Config::template getParameter<int>(\"handshaking_phases\", cfg_scope) == 2;\n    m_aggregation_edge_weight_component = cfg.AMG_Config::template getParameter<int>(\"aggregation_edge_weight_component\", cfg_scope);\n    aggregation_passes = cfg.AMG_Config::template getParameter<int>(\"aggregation_passes\", cfg_scope); //default to size 8 aggregates. maybe its more convenient to have that as a config parameter\n    filter_weights = cfg.AMG_Config::template getParameter<int>(\"filter_weights\", cfg_scope); //by default: no filtering\n    filter_weights_alpha = cfg.AMG_Config::template getParameter<double>( \"filter_weights_alpha\", cfg_scope ); //default to 0.25\n    full_ghost_level = cfg.AMG_Config::template getParameter<int>( \"full_ghost_level\", cfg_scope ); //defaults to 0\n    notay_weights = cfg.AMG_Config::template getParameter<int>( \"notay_weights\", cfg_scope ); //defaults to 0\n    ghost_offdiag_limit = cfg.AMG_Config::template getParameter<int>( \"ghost_offdiag_limit\", cfg_scope ); //defaults to 0\n    merge_singletons = cfg.AMG_Config::template getParameter<int>( \"merge_singletons\", cfg_scope ); //defaults to 1\n    weight_formula = cfg.AMG_Config::template getParameter<int>( \"weight_formula\", cfg_scope ); //wheight formula defaults to 0\n    serial_matching = cfg.AMG_Config::template getParameter<int>( \"serial_matching\", cfg_scope ) != 0; //will use a serial matching algorithm instead of handshake\n    modified_handshake = cfg.AMG_Config::template getParameter<int>(\"modified_handshake\", cfg_scope ) == 1;\n    //passes = 1 -> max = 3\n    //passes = 2 -> max = 5\n    //passes = 3 -> max = 10\n    //passes = 4 -> max = 18\n    max_aggregate_size = 2;\n\n    for (int i = 1; i < aggregation_passes; i ++)\n    {\n        max_aggregate_size *= 2;\n    }\n\n    max_aggregate_size += aggregation_passes - (aggregation_passes / 2);\n    mCfg = cfg;\n    mCfg_scope = cfg_scope;\n}\n\n// setAggregates for block_dia_csr_matrix_h format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(Matrix_h &A,\n        typename Matrix_h::IVector &aggregates, typename Matrix_h::IVector &aggregates_global, int &num_aggregates, MVector &edge_weights, IVector &sizes)\n{\n    FatalError(\"MultiPairwise selector: setAggregates not implemented on CPU, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// device specialization\n\n//edge_weights is an in/out parameter:\n//if its size is zero, the edge_weights will be computed from A and stored into edge_weights\n//else the edge_weights will not be computed and assumed to be valid for the given A. the value array of A is not used in this case\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setAggregates_common_sqblocks(Matrix_d &A,\n        typename Matrix_d::IVector &aggregates,\n        typename Matrix_d::IVector &aggregates_global,\n        int &num_aggregates,\n        MVector &edge_weights,\n        IVector &sizes)\n{\n    IndexType num_block_rows = (int) A.get_num_rows();\n    IndexType num_nonzero_blocks = (int) A.get_num_nz();\n    // both ways are supported\n    IndexType total_nz = (A.is_matrix_singleGPU()) ? num_nonzero_blocks : A.manager->num_nz_all();\n    typename Matrix_d::IVector &row_indices = A.row_indices;\n    row_indices.resize( total_nz);\n    cusp::detail::offsets_to_indices(A.row_offsets, row_indices);\n    IndexType total_rows = (A.is_matrix_singleGPU()) ? A.get_num_rows() : A.manager->num_rows_all();\n    aggregates.resize(total_rows);\n    thrust_wrapper::fill<AMGX_device>(aggregates.begin(), aggregates.end(), -1);\n    cudaCheckError();\n\n    if ( this->merge_singletons == 2 && sizes.size() == 0 )\n    {\n        sizes.resize( total_rows, 1 );    //init with all ones\n    }\n\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_row_indices_ptr = row_indices.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_idx_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    typename Matrix_d::IVector strongest_neighbour(num_block_rows, -1);\n    typename Matrix_d::IVector strongest_neighbour_1phase(num_block_rows, -1);\n    Vector<TemplateConfig<AMGX_device, AMGX_vecUInt, t_matPrec, t_indPrec> > aggregated(num_block_rows, 0);\n    IndexType *strongest_neighbour_ptr = strongest_neighbour.raw();\n    IndexType *strongest_neighbour_1phase_ptr = strongest_neighbour_1phase.raw();\n    IndexType *aggregates_ptr = aggregates.raw();\n    const int threads_per_block = 256;\n    const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (num_block_rows - 1) / threads_per_block + 1 );\n    int numUnassigned = num_block_rows;\n    int numUnassigned_previous = numUnassigned;\n    bool computeWeights = ( edge_weights.size() == 0 );\n\n    if (computeWeights)\n    {\n        if ( A.hasProps( DIAG ) )\n        {\n            edge_weights.resize( num_nonzero_blocks + num_block_rows, 0.0 );\n        }\n        else\n        {\n            edge_weights.resize( num_nonzero_blocks + 1, -1 );    //+1 is important to some algorithms\n        }\n    }\n\n    ValueType *edge_weights_ptr = edge_weights.raw();\n    ValueType *rand_edge_weights_ptr = NULL;\n    hipStream_t str = amgx::thrust::global_thread_handle::get_stream();\n\n    // Compute the edge weights\n    if ( computeWeights )\n    {\n        const int num_blocks_V2 = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n        //compute with std formula\n        hipFuncSetCacheConfig(reinterpret_cast<const void*>(computeEdgeWeightsBlockDiaCsr_V2<IndexType, ValueType, ValueType>), hipFuncCachePreferL1);\n        computeEdgeWeightsBlockDiaCsr_V2 <<< num_blocks_V2, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                A_row_indices_ptr,\n                A_column_indices_ptr,\n                A_dia_idx_ptr,\n                A_nonzero_values_ptr,\n                num_nonzero_blocks,\n                edge_weights_ptr,\n                rand_edge_weights_ptr,\n                num_block_rows,\n                A.get_block_dimy(),\n                this->m_aggregation_edge_weight_component,\n                this->weight_formula);\n        cudaCheckError();\n    }\n\n    //filter weights if desired\n    if ( this->filter_weights == 1 )\n    {\n        MVector tmp( edge_weights.size() );\n        const int num_blocks_filter = std::min( AMGX_GRID_MAX_SIZE, (num_nonzero_blocks - 1) / threads_per_block + 1);\n        hipStreamSynchronize(str);\n        cudaCheckError();\n        filterWeights <<< num_blocks_filter, threads_per_block, 0, str>>>( A_row_offsets_ptr,\n                A_row_indices_ptr,\n                A_column_indices_ptr,\n                A_dia_idx_ptr,\n                edge_weights_ptr,\n                tmp.raw(),\n                num_nonzero_blocks,\n                num_block_rows,\n                this->filter_weights_alpha);\n                cudaCheckError();\n        hipStreamSynchronize(str);\n        cudaCheckError();\n        tmp.swap( edge_weights );\n        edge_weights_ptr = edge_weights.raw();\n    }\n\n// compute matching\n    if ( !this->serial_matching )\n    {\n        IVector degree;\n\n        if ( this->modified_handshake )\n        {\n            degree.resize( num_block_rows );\n        }\n\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n        // TODO: allocate host pinned memory\n        AsyncEvent *throttle_event = new AsyncEvent;\n        throttle_event->create();\n        typename Matrix_h::IVector h_unagg_vec(1);\n        typename Matrix_d::IVector d_unagg_vec(1);\n        int *unaggregated = h_unagg_vec.raw();\n        int *d_unaggregated = d_unagg_vec.raw();\n#endif\n        int icount, s = 1;\n        {\n            icount = 0;\n            ValueType *weights_ptr = edge_weights_ptr;\n\n            do\n            {\n                if ( !this->two_phase )\n                {\n                    if ( this->modified_handshake )\n                        computeDegree <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                aggregates_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                num_block_rows,\n                                this->max_aggregate_size );\n                                cudaCheckError();\n\n                    // 1-phase handshaking\n                    if ( this->modified_handshake )\n                        findStrongestNeighbourBlockDiaCsr_V3<IndexType, ValueType, true>\n                        <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                num_block_rows,\n                                aggregates_ptr,\n                                strongest_neighbour_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                A.get_block_dimy(),\n                                this->max_aggregate_size,\n                                this->merge_singletons);\n                    else\n                        findStrongestNeighbourBlockDiaCsr_V3<IndexType, ValueType, false>\n                        <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                                A_column_indices_ptr,\n                                weights_ptr,\n                                num_block_rows,\n                                aggregates_ptr,\n                                strongest_neighbour_ptr,\n                                sizes.raw(),\n                                degree.raw(),\n                                A.get_block_dimy(),\n                                this->max_aggregate_size,\n                                this->merge_singletons);\n\n                    cudaCheckError();\n                }\n                else\n                {\n                    // 2-phase handshaking\n                    findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 1, this->merge_singletons);\n                    cudaCheckError();\n                    // 2nd phase: for each block_row, find the strongest neighbour among those who gave hand on 1st phase\n                    findStrongestNeighbourBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, weights_ptr, num_block_rows, aggregates_ptr, strongest_neighbour_1phase_ptr, strongest_neighbour_ptr, A.get_block_dimy(), 2, this->merge_singletons);\n                    cudaCheckError();\n                }\n\n                // Look for perfect matches. Also, for nodes without unaggregated neighbours, merge with aggregate containing strongest neighbour\n                if ( this->merge_singletons == 2 )\n                {\n                    matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr, sizes.raw());\n                    cudaCheckError();\n                }\n                else\n                {\n                    matchEdges <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, strongest_neighbour_ptr, (int *)NULL);\n                }\n\n                cudaCheckError();\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n                s = (icount & 1);\n\n                if ( s == 0 )\n                {\n                    // count unaggregated vertices\n                    hipError_t cuda_rc = hipMemsetAsync(d_unaggregated, 0, sizeof(int), str);\n                    if (cuda_rc != hipSuccess)\n                    {\n                        FatalError(\"hipMemsetAsync failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                    }\n                    countAggregates<IndexType, threads_per_block> <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, d_unaggregated);\n                    cudaCheckError();\n                    cuda_rc = hipMemcpyAsync(unaggregated, d_unaggregated, sizeof(int), hipMemcpyDeviceToHost, str);\n                    if (cuda_rc != hipSuccess)\n                    {\n                        FatalError(\"hipMemcpyAsync failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                    }\n                    throttle_event->record(str);\n                }\n                else\n                {\n                    throttle_event->sync();\n                    numUnassigned_previous = numUnassigned;\n                    numUnassigned = *unaggregated;\n                }\n\n#else\n                hipError_t cuda_rc = hipStreamSynchronize(str);\n                if (cuda_rc != hipSuccess)\n                {\n                    FatalError(\"hipStreamSynchronize failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n                }\n                numUnassigned_previous = numUnassigned;\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n#endif\n                icount++;\n            }\n            while ( (s == 0) || !(numUnassigned == 0 || icount > this->max_iterations || 1.0 * numUnassigned / num_block_rows < this->numUnassigned_tol || numUnassigned == numUnassigned_previous));\n\n        }\n        //    printf(\"%i,\\n\", icount);\n#ifdef EXPERIMENTAL_ITERATIVE_MATCHING\n        delete throttle_event;\n#endif\n    }\n    else\n    {\n        computeMatchingSerialGreedy( A, aggregates, num_aggregates, edge_weights );\n    }\n\n    if ( this->merge_singletons == 1 )\n    {\n        // Merge remaining vertices with current aggregates\n        if (this->deterministic != 1)\n        {\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, (IndexType *) NULL);\n                cudaCheckError();\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            typename Matrix_d::IVector aggregates_candidate(num_block_rows, -1);\n\n            while (numUnassigned != 0)\n            {\n                mergeWithExistingAggregatesBlockDiaCsr_V2 <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr, A_column_indices_ptr, edge_weights_ptr, num_block_rows, aggregates_ptr, A.get_block_dimy(), this->deterministic, aggregates_candidate.raw());\n                cudaCheckError();\n                joinExistingAggregates <<< num_blocks, threads_per_block, 0, str>>>(num_block_rows, aggregates_ptr, aggregates_candidate.raw());\n                cudaCheckError();\n                numUnassigned = (int)amgx::thrust::count(aggregates.begin(), aggregates.begin() + num_block_rows, -1);\n                cudaCheckError();\n            }\n\n            aggregates_candidate.resize(0);\n        }\n    }\n    else if (this->merge_singletons == 0 )\n    {\n        //make singletons\n        aggregateSingletons <<< num_blocks, threads_per_block, 0, str>>>( aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n    else if ( this->merge_singletons == 2 )\n    {\n        //merges all remaining singletons into adequate neighbors if possible\n        mergeSingletonsSmart <<< num_blocks, threads_per_block, 0, str>>>(A_row_offsets_ptr,\n                A_column_indices_ptr,\n                edge_weights_ptr,\n                aggregates_ptr,\n                sizes.raw(),\n                num_block_rows,\n                this->max_aggregate_size);\n        cudaCheckError();\n    }\n\n    //This will assign num_aggregates to the pseudo aggregate without counting it. Perfect!\n    this->renumberAndCountAggregates(aggregates, aggregates_global, num_block_rows, num_aggregates);\n\n    if ( this->merge_singletons == 2 )\n    {\n        //udpate the sizes vector, so it matches the renumbered aggregates size\n        IVector sizesSource;\n        sizesSource.swap( sizes );\n        sizes.resize( num_aggregates, 1 );\n        updateAggregateSizes <<< num_blocks, threads_per_block, 0, str>>>( sizesSource.raw(), sizes.raw(), aggregates_ptr, num_block_rows );\n        cudaCheckError();\n    }\n\n}\n\n\n//instead of a handshake, we use a serial greedy algorithm to compute a better matching\n//the algorithm:\n// 1. compute degree of every node and sort nodes by degree into double linked list\n// 2. while non-isolated nodes left:\n//        take node with minimum degree > 0\n//        find strongest edge to unaggregated node and assign to new aggregate\n//        remove both nodes from linked list\n//        decrease degree of each neighbor by one for each of the two nodes\n//        update list\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeMatchingSerialGreedy( const Matrix_d &A, IVector &aggregates, int &numAggregates, MVector &edge_weights)\n{\n    IndexType numRows = A.row_offsets.size() - 1;\n    IndexType nnz = A.col_indices.size();\n    //allocate memory on host\n    IndexType *ia = new IndexType[numRows + 1];\n    IndexType *ja = new IndexType[nnz];\n    ValueType *w = new ValueType[nnz];\n    IndexType *agg = new IndexType[numRows];\n    IndexType *deg = new IndexType[numRows];\n    //copy\n    hipError_t cuda_rc = hipMemcpy( ia, A.row_offsets.raw(), sizeof(IndexType) * (numRows + 1), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy row_offsets D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( ja, A.col_indices.raw(), sizeof(IndexType)*nnz, hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy col_indices D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( w, edge_weights.raw(), sizeof(ValueType)*nnz, hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy edge_weights D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    //init agg and compute the degree of each aggregate\n    int max_degree = 0;\n\n    for (IndexType i = 0; i < numRows; i++)\n    {\n        agg[i] = -1;\n        int degree = 0;\n\n        for (IndexType ii = ia[i]; ii < ia[i + 1]; ii++) //only care for positive weights\n        {\n            if ( ja[ii] != i && w[ii] > 0.0 )\n            {\n                degree++;\n            }\n        }\n\n        if ( degree > max_degree )\n        {\n            max_degree = degree;\n        }\n\n        deg[i] = degree;\n    }\n\n    if ( max_degree >= numRows )\n    {\n        FatalError( \"max degree is greater than numRows.\", AMGX_ERR_UNKNOWN );\n    }\n\n    //init double linked list\n    IndexType *fwd = new IndexType[numRows + max_degree + 1];\n    IndexType *bwd = new IndexType[numRows + max_degree + 1];\n\n    for (IndexType i = 0; i < numRows + max_degree + 1; i++)\n    {\n        fwd[i] = i;\n        bwd[i] = i;\n    }\n\n    IndexType nodesLeft = numRows;\n    numAggregates = 0;\n\n    //insert nodes into list\n    for (IndexType i = numRows - 1; i >= 0; i--) //inserting in backward order the nodes will be sorted by index in case of same degree\n    {\n        //insert forward following root\n        fwd[i] = fwd[numRows + deg[i]];\n        fwd[numRows + deg[i]] = i;\n        //insert backward\n        bwd[i] = numRows + deg[i];\n        bwd[fwd[i]] = i;\n\n        //isolated nodes cannot be aggregated\n        if ( deg[i] == 0 )\n        {\n            nodesLeft--;\n        }\n    }\n\n    while ( nodesLeft > 0 )\n    {\n        IndexType node = numRows;\n        int degree;\n\n        for (degree = 1; degree <= max_degree; degree++)\n        {\n            //list not empty -> select node\n            if ( fwd[numRows + degree] < numRows ) //selecting the first node will select the most recently inserted one or the one with lowest index. both is preferable\n            {\n                node = fwd[numRows + degree];\n            }\n\n            if ( node < numRows )\n            {\n                break;\n            }\n        }\n\n        //no node with degree > 1 found even though nodesLeft > 0\n        if ( node == numRows )\n        {\n            FatalError(\"nodeLeft counting or list invalid\", AMGX_ERR_UNKNOWN );\n        }\n\n        if ( agg[node] != -1 )\n        {\n            FatalError(\"node is already aggregated\", AMGX_ERR_UNKNOWN );\n        }\n\n        //find strongest edge\n        ValueType max_weight = 0.0;\n        IndexType max_node = numRows; //use this as gatekeeper, so if weight == 0 the node index will not be greater than this\n\n        for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1 || j == node)\n            {\n                continue;\n            }\n\n            if ( w[ii] > 0.0 )\n            {\n                degree--;\n            }\n\n            //deterministic, doesn't selects 0 weight.\n            if ( w[ii] > max_weight || (w[ii] == max_weight && j > max_node) ) //always taking the edge pointing to the max node can give good alignment if numbering is structured\n            {\n                max_node = j;\n                max_weight = w[ii];\n            }\n        } //Note that there has to be at least one neighbor node because degree of node is at least 1.\n\n        if ( max_node == numRows )\n        {\n            FatalError( \"node  has no neighbor although degree of node is at least 1\", AMGX_ERR_UNKNOWN );\n        }\n\n        if ( degree != 0 )\n        {\n            FatalError( \"node degree corrupted\", AMGX_ERR_UNKNOWN );\n        }\n\n        //aggregate\n        agg[node] = node;\n        agg[max_node] = node;\n        numAggregates++;\n        nodesLeft -= 2;\n        //remove from list\n        fwd[bwd[node]] = fwd[node];\n        bwd[fwd[node]] = bwd[node];\n        fwd[bwd[max_node]] = fwd[max_node];\n        bwd[fwd[max_node]] = bwd[max_node];\n\n        //update neighbors and list\n        //max_node first\n        for (IndexType ii = ia[max_node]; ii < ia[max_node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1  || w[ii] <= 0.0)\n            {\n                continue;\n            }\n\n            //remove j from list\n            fwd[bwd[j]] = fwd[j];\n            bwd[fwd[j]] = bwd[j];\n            //update degree of j\n            deg[j]--;\n            //add j back to start of the list\n            fwd[j] = fwd[numRows + deg[j]];\n            bwd[j] = numRows + deg[j];\n            bwd[fwd[j]] = j;\n            fwd[bwd[j]] = j;\n\n            if (deg[j] == 0)\n            {\n                nodesLeft--;\n            }\n        }\n\n        //node second, this will prefer nodes neighbors over max_nodes neighbors when choosing the next node\n        for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n        {\n            IndexType j = ja[ii];\n\n            if ( agg[j] != -1  || w[ii] <= 0.0)\n            {\n                continue;\n            }\n\n            //remove j from list\n            fwd[bwd[j]] = fwd[j];\n            bwd[fwd[j]] = bwd[j];\n            //update degree of j\n            deg[j]--;\n            //add j back to start of the list\n            fwd[j] = fwd[numRows + deg[j]];\n            bwd[j] = numRows + deg[j];\n            bwd[fwd[j]] = j;\n            fwd[bwd[j]] = j;\n\n            if (deg[j] == 0)\n            {\n                nodesLeft--;\n            }\n        }\n    }\n\n    //copy result back to device\n    cuda_rc = hipMemcpy( aggregates.raw(), agg, sizeof(IndexType)*numRows, hipMemcpyHostToDevice );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy aggregates H2D failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    //assert matching\n    for (IndexType node = 0; node < numRows; node++)\n    {\n        if ( agg[node] == -1 )\n        {\n            continue;\n        }\n\n        for ( IndexType partner = 0; partner < numRows; partner++)\n        {\n            if ( agg[partner] == agg[node] )\n            {\n                if ( partner == node )\n                {\n                    continue;\n                }\n\n                bool neighbor = false;\n\n                for (IndexType ii = ia[node]; ii < ia[node + 1]; ii++)\n                    if ( ja[ii] == partner )\n                    {\n                        neighbor = true;\n                        break;\n                    }\n\n                if ( !neighbor )\n                {\n                    for (IndexType ii = ia[partner]; ii < ia[partner + 1]; ii++)\n                        if ( ja[ii] == node )\n                        {\n                            neighbor = true;\n                            break;\n                        }\n                }\n\n                if ( !neighbor )\n                {\n                    FatalError(\"Internal error in aggregation selector\", AMGX_ERR_INTERNAL);\n                }\n            }\n        }\n    }\n\n    //you shall not leak memory\n    delete[] ia;\n    delete[] ja;\n    delete[] w;\n    delete[] agg;\n    delete[] deg;\n    delete[] fwd;\n    delete[] bwd;\n}\n\n\n\n\n//this kernel merges aggregate2 into aggregate1\ntemplate<typename IndexType>\n__global__\nvoid mergeAggregates(IndexType *aggregate1, const IndexType *aggregate2, IndexType sizeAggregate1, IndexType sizeAggregate2, IndexType sizeAggregate3)\n{\n    int tid = threadIdx.x + blockDim.x * blockIdx.x;\n\n    while ( tid < sizeAggregate1 )\n    {\n        if ( aggregate1[tid] == sizeAggregate2 )\n        {\n            aggregate1[tid] = sizeAggregate3;\n        }\n        else\n        {\n            aggregate1[tid] = aggregate2[aggregate1[tid]];\n        }\n\n        tid += gridDim.x * blockDim.x;\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeIncompleteGalerkin( const Matrix_h &A,\n        Matrix_h &Ac,\n        const typename Matrix_h::IVector &aggregates,\n        const typename Matrix_h::IVector &R_row_offsets,\n        const typename Matrix_h::IVector &R_column_indices,\n        const int num_aggregates )\n{\n    FatalError(\"computeIncomlpetegalerkin is not supported on host. Run with ghost_offdiag_limit=0 instead.\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid MultiPairwiseSelector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeIncompleteGalerkin( const Matrix_d &A,\n        Matrix_d &Ac,\n        const typename Matrix_d::IVector &aggregates,\n        const typename Matrix_d::IVector &R_row_offsets,\n        const typename Matrix_d::IVector &R_column_indices,\n        const int num_aggregates )\n{\n    FatalError(\"computeIncomlpetegalerkin is not implemented yet. run with ghost_offdiag_limit=0 instead.\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate<class TConfig>\nvoid MultiPairwiseSelectorBase<TConfig>::assertRestriction( const IVector &R_row_offsets, const IVector &R_col_indices, const IVector &aggregates )\n{\n    int *r_ia = new int[R_row_offsets.size()];\n    int *r_ja = new int[R_col_indices.size()];\n    int *agg = new int[aggregates.size()];\n    int *used_col = new int[aggregates.size()];\n\n    for ( int i = 0; i < aggregates.size(); i++ )\n    {\n        used_col[i] = 0;\n    }\n\n    hipError_t cuda_rc = hipMemcpy( r_ia, R_row_offsets.raw(), sizeof(int)*R_row_offsets.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy R_row_offsets D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( r_ja, R_col_indices.raw(), sizeof(int)*R_col_indices.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy R_col_indices D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = hipMemcpy( agg, aggregates.raw(), sizeof(int)*aggregates.size(), hipMemcpyDeviceToHost );\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipMemcpy aggregates D2H failed in multi_pairwise\", AMGX_ERR_CUDA_FAILURE);\n    }\n\n    for ( int i = 0; i < R_row_offsets.size() - 1; i++ )\n    {\n        for ( int ii = r_ia[i]; ii < r_ia[i + 1]; ii++ )\n        {\n            int j = r_ja[ii];\n            used_col[j]++;\n\n            if ( used_col[j] > 1 )\n            {\n                std::cout << \"column \" << j << \" is present at least \" << used_col[j] << \" times\" << std::endl;\n            }\n\n            if ( j < 0 || j >= aggregates.size() )\n            {\n                std::cout << \"Error: j out of bounds, j = \" << j << \" and numRows = \" << aggregates.size() << std::endl;\n            }\n            else if  ( agg[j] != i )\n            {\n                std::cout << \"Error: agg[\" << j << \"] = \" << agg[j] << \" != \" << i << std::endl;\n            }\n        }\n    }\n\n    std::cout << \"assert restriction done\" << std::endl;\n}\n\ntemplate<class T_Config>\nvoid MultiPairwiseSelectorBase<T_Config>::setAggregates(Matrix<T_Config> &A,\n        IVector &aggregates, IVector &aggregates_global, int &num_aggregates)\n{\n    if (A.get_block_dimx() == A.get_block_dimy())\n    {\n        //ghost level matrix. this is a probably a weight matrix\n        Matrix<TConfig> ghostA;\n        ghostA.values.resize(0);\n        //prolongation and restriction operator. this is only needed in when LowDegCoarseAGenerator is used\n        IVector R_row_offsets;\n        IVector R_col_indices;\n        //holds the size of each aggregate\n        IVector sizes;\n        sizes.resize(0);\n        //aggregates for ghost level\n        IVector aggregates_current;\n        IVector aggregates_global_current;\n        bool aggregates_initialized = true;\n\n        if (aggregates.size() == 0)\n        {\n            aggregates_initialized = false;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                aggregates.resize(A.manager->halo_offset(A.manager->num_neighbors()));\n            }\n            else\n            {\n                aggregates.resize(A.get_num_rows());\n            }\n        }\n\n        //for mergeAggregates kernel\n        const int threads_per_block = 256;\n        const int num_blocks = std::min( AMGX_GRID_MAX_SIZE, (A.get_num_rows() - 1) / threads_per_block + 1 );\n        hipStream_t stream = amgx::thrust::global_thread_handle::get_stream();\n        //initialize and prepare weight matrix\n        Matrix<TConfig> w;\n        w.set_initialized(0);\n        w.addProps(CSR);\n        w.delProps(COO);\n        w.setColsReorderedByColor(false);\n        w.resize( 0, 0, 0, 1, 1, true ); //empty scalar 0x0 matrix\n        w.values.resize(0); //matrix resize sets the values array to nnz+1 for no apparent reason\n        IndexType targetSize = 1;\n        //initialize coarse A generator\n        CoarseAGenerator<TConfig> *cag;\n        const bool use_restriction = true;\n        const bool shrink_ghost_level = false;\n        cag = new LowDegCoarseAGenerator<TConfig>(mCfg, mCfg_scope);\n        // This will make coarseAGenerator to allocate more memory inside of  galerkin\n        ghostA.manager = new DistributedManager<TConfig>();\n        w.manager = new DistributedManager<TConfig>();\n        Matrix<TConfig> *curA = &A;\n\n        //foreach pass do:\n        // 1. build aggregates and weights\n        // 2. create weight matrix (in full_ghost_level mode this is the input matrix or the last ghostlevel matrix)\n        // 3. if in full ghost level mode, build R\n        // 4. compute next level\n        for (int current_pass = 1; true; current_pass++)\n        {\n            const IndexType numRows = curA->get_num_rows();\n            const IndexType nnz = curA->get_num_nz();\n            targetSize *= 2;\n\n            if ( full_ghost_level )\n            {\n                w.values.resize(0);    //compute weights from curA\n            }\n            else\n            {\n                w.values.swap( ghostA.values );    //use the weights computed with the galerkin operator (this will do nothing in the first pass, both values have size 0)\n            }\n\n            // create aggregates from correct input matrix\n            setAggregates_common_sqblocks( *curA, aggregates_current, aggregates_global_current, num_aggregates, w.values, sizes );\n\n\n            if ( current_pass > 1 )\n            {\n                //merge original aggregate with the newly created ones\n                mergeAggregates <<< num_blocks, threads_per_block, 0, stream >>>( aggregates.raw(), aggregates_current.raw(), A.get_num_rows(), numRows, num_aggregates );\n                cudaCheckError();\n                //mergeAggregates<<< num_blocks, threads_per_block, 0, stream >>>( aggregates_global.raw(), aggregates_global_current.raw(), A.get_num_rows() );\n                //cudaCheckError();\n            }\n\n            //try to free memory\n            if ( full_ghost_level )\n            {\n                //then we don't need to save the weights, only for original level to do post processing\n                w.values.resize(0);\n            }\n            else\n            {\n                //save edge weights for original level later\n                //in that case we can throw away the values of ghostA as we will use the values to compute the next ghost level\n                ghostA.values.resize(0);\n            }\n\n            // this is the break condition for the loop\n            if ( current_pass >= aggregation_passes || num_aggregates <= 1 || num_aggregates == numRows)\n            {\n                //this means, aggregates has not been initialized yet\n                if ( !aggregates_initialized )\n                {\n                    aggregates.swap( aggregates_current );\n                }\n\n                if ( !aggregates_initialized )\n                {\n                    aggregates_global.swap( aggregates_global_current );\n                }\n\n                hipStreamSynchronize( stream );\n                cudaCheckError();\n\n                break;\n            }\n\n            //prepare A to be corrupted\n            curA->set_initialized(0);\n            //swap in ia, ja from curA\n            w.row_offsets.swap( curA->row_offsets );\n            w.col_indices.swap( curA->col_indices );\n\n            if ( full_ghost_level )\n            {\n                if ( shrink_ghost_level && curA->get_block_dimx() > 1)\n                {\n                    //set w to correct size\n                    w.values.resize( nnz );\n                    //define grid and offsets\n                    const int num_blocks_inter = std::min( (int)AMGX_GRID_MAX_SIZE, (int)(nnz - 1) / threads_per_block + 1 );\n                    const int sq_blocksize = A.get_block_dimx() * A.get_block_dimy();\n                    const int index_offset = A.get_block_dimy() * m_aggregation_edge_weight_component + m_aggregation_edge_weight_component;\n                    //do the interleaved copy\n                    gatherValuesInterleaved <<< num_blocks_inter, threads_per_block, 0, stream>>>( A.values.raw(), w.values.raw(), nnz, sq_blocksize, index_offset );\n                    cudaCheckError();\n                    hipStreamSynchronize( stream );\n                    cudaCheckError();\n                }\n                else\n                {\n                    w.values.swap( curA->values );\n                }\n            }\n\n            w.diag.swap( curA->diag );\n\n            //resize to inform the matrix of its new size\n            if ( full_ghost_level && !shrink_ghost_level )\n            {\n                w.set_block_dimx( A.get_block_dimx() );\n                w.set_block_dimy( A.get_block_dimy() );\n            }\n            else\n            {\n                w.set_block_dimx( 1 );\n                w.set_block_dimy( 1 );\n            }\n\n            w.set_num_rows( numRows );\n            w.set_num_cols( numRows );\n            w.set_num_nz( nnz );\n            w.set_allow_recompute_diag( false );\n\n            if ( curA->hasProps( DIAG ) )\n            {\n                w.addProps( DIAG );\n            }\n\n            //ready to use\n            w.set_initialized(1);\n\n            //compute restriction operator\n            if ( use_restriction )\n            {\n                IVector R_row_indices(aggregates_current);\n                R_row_offsets.resize(num_aggregates + 2);\n                R_col_indices.resize(numRows);\n                thrust_wrapper::sequence<TConfig::memSpace>(R_col_indices.begin(), R_col_indices.end());\n                cudaCheckError();\n                amgx::thrust::sort_by_key(R_row_indices.begin(), R_row_indices.end(), R_col_indices.begin());\n                cudaCheckError();\n                cusp::detail::indices_to_offsets(R_row_indices, R_row_offsets);\n                cudaCheckError();\n                //delete last row, which holds the pseudo aggregate\n                R_row_offsets.resize( num_aggregates + 1);\n                R_col_indices.resize( R_row_offsets[num_aggregates] );\n            }\n\n            // 3. compute galerkin ghost level\n            if ( ghost_offdiag_limit == 0 )\n            {\n                //compute full galerkin\n                cag->computeAOperator(w,\n                                      ghostA,\n                                      aggregates_current,\n                                      R_row_offsets,\n                                      R_col_indices,\n                                      num_aggregates);\n            }\n            else\n            {\n                //compute incomplete galerkin\n                computeIncompleteGalerkin(w,\n                                          ghostA,\n                                          aggregates_current,\n                                          R_row_offsets,\n                                          R_col_indices,\n                                          num_aggregates);\n            }\n\n            //from now on w will be destroyed again.\n            w.set_initialized(0);\n\n            //repair the original A matrix. its ia and ja are in w\n            if ( current_pass == 1 )\n            {\n                //swap back\n                w.row_offsets.swap( A.row_offsets );\n                w.col_indices.swap( A.col_indices );\n\n                //only in that case we have swapped the values\n                if ( full_ghost_level && !shrink_ghost_level )\n                {\n                    w.values.swap( A.values );\n                }\n\n                //save the edge weights of the original level\n                A.diag.swap( w.diag );\n                A.set_initialized(1); //A is repaired now\n                //save the first aggregates into the original aggregate vector so we can merge them later\n                aggregates.swap( aggregates_current );\n                aggregates_global.swap( aggregates_global_current );\n                aggregates_initialized = true;\n                curA = &ghostA;\n            }\n        }\n\n        delete cag;\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for MultiPairwise\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n}\n\n// -------------------------\n//    Explict instantiations\n// -------------------------\n#define AMGX_CASE_LINE(CASE) template class MultiPairwiseSelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class MultiPairwiseSelector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/selectors/multi_pairwise.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n// The number of non-zeros per row is computed using Thrust\n// The coarse matrix is created by custom kernel\n// --------------------------------------------------------\n\n#include <aggregation/coarseAgenerators/hybrid_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <cusp/detail/format_utils.h> //indices_to_offsets\n#include <thrust/remove.h>\n#include <thrust/extrema.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/unique.h>\n#include <error.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntypedef amgx::thrust::tuple<int, int> tuple_t;\n\nstruct isDiagonal\n{\n    __host__ __device__\n    bool operator()(tuple_t IJ)\n    {\n        return ( amgx::thrust::get<0>(IJ) == amgx::thrust::get<1>(IJ) );\n    }\n\n};\n\n// --------------------\n// Kernels\n// --------------------\n\n// Kernel to store aggregate I of each fine point index i\ntemplate <typename IndexType>\n__global__\nvoid iToIKernel(const IndexType *row_offsets, const IndexType *aggregates, IndexType *I, const int num_rows)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        int agg = aggregates[tid];\n\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            I[j] = agg;\n        }\n    }\n}\n\n// Kernel to store aggregate J of each fine point index j\ntemplate <typename IndexType>\n__global__\nvoid jToJKernel(const IndexType *column_indices, const IndexType *aggregates, IndexType *J, const int num_entries)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_entries; tid += gridDim.x * blockDim.x)\n    {\n        int j = column_indices[tid];\n        J[tid] = aggregates[j];\n    }\n}\n\n// Kernel to fill matrix Ac for 4x4 block matrices\ntemplate <typename IndexType, typename ValueType, int max_nonzeros_per_row, int threads_per_block, int bsize_sq, int log_bsize_sq>\n__global__\nvoid fillA_4by4Blocks_Thrust_Kernel(const IndexType *A_row_offsets, const IndexType *A_column_indices, const IndexType *A_dia_values, const ValueType *A_nonzero_values, const IndexType *R_row_offsets, const IndexType *R_column_indices, const IndexType *Ac_row_offsets, const IndexType *Ac_column_indices, IndexType *Ac_dia_values, ValueType *Ac_nonzero_values, const IndexType *aggregates, const int num_aggregates, const int num_threads)\n{\n    const int tid = threadIdx.x;\n    const int I = blockIdx.x * threads_per_block + tid;\n    const int halfwarp_id = tid >> log_bsize_sq;\n    const int mat_entry_index = tid  & (bsize_sq - 1);\n    volatile __shared__ IndexType current_cell_to_read[threads_per_block / bsize_sq];\n    volatile __shared__ IndexType current_cell_to_write[threads_per_block / bsize_sq];\n    volatile __shared__ IndexType max_nonzeros_i[threads_per_block];\n    volatile __shared__ IndexType max_nonzeros_j[threads_per_block];\n    const int size_hash_reg = (2. / 3.) * max_nonzeros_per_row; // 2/3 is arbitrary\n    const int size_hash_shared = max_nonzeros_per_row - size_hash_reg;\n    IndexType shared_offset = threadIdx.x * size_hash_shared;\n    volatile __shared__ IndexType hash_shared[size_hash_shared * threads_per_block];\n    IndexType hash_reg[size_hash_reg];\n    int Ac_offset_min;\n    int Ac_offset_max;\n#pragma unroll\n\n    for (int i = 0; i < size_hash_reg; i++)\n    {\n        hash_reg[i] = -1;\n    }\n\n    for (int i = 0; i < size_hash_shared; i++)\n    {\n        hash_shared[shared_offset + i] = -1;\n    }\n\n    // Find maximum number of nonzeros for threads in same halfwarp\n    int i_offset_min, i_offset_max;\n\n    if (I < num_aggregates)\n    {\n        i_offset_min = R_row_offsets[I];\n        i_offset_max = R_row_offsets[I + 1];\n        max_nonzeros_i[tid] = i_offset_max - i_offset_min;\n        Ac_offset_min = Ac_row_offsets[I];\n        Ac_offset_max = Ac_row_offsets[I + 1];\n\n        for (int k = Ac_offset_min, icount = 0; k < Ac_offset_max; k++, icount++)\n        {\n            if (icount < size_hash_reg)\n            {\n                hash_reg[icount] = Ac_column_indices[k];\n            }\n            else\n            {\n                hash_shared[shared_offset + icount - size_hash_reg] = Ac_column_indices[k];\n            }\n        }\n    }\n    else\n    {\n        i_offset_min = 0;\n        i_offset_max = 0;\n        max_nonzeros_i[tid] = 0;\n    }\n\n    int max_loop_i = 0;\n\n    for (int m = 0; m < bsize_sq; m++)\n    {\n        int max_nonzeros = max_nonzeros_i[halfwarp_id * bsize_sq + m];\n\n        if (max_nonzeros > max_loop_i)\n        {\n            max_loop_i = max_nonzeros;\n        }\n    }\n\n    // For all threads that could do useful work\n    if (I < num_threads)\n    {\n        for (int iloop = 0, i_offset = i_offset_min; iloop < max_loop_i; iloop++, i_offset++)\n        {\n            // Contribution from A_dia_values of fine point i\n            int i = (i_offset < i_offset_max)  ? R_column_indices[i_offset] : -1 ;\n\n            // Have threads collaborate to load in coalesced fashion\n            for (int m = 0; m < bsize_sq; m++)\n            {\n                // Write which cell to load in shared memory buffer\n                if (mat_entry_index == m)\n                {\n                    current_cell_to_read[halfwarp_id] = i;\n                    current_cell_to_write[halfwarp_id] = I;\n                }\n\n                // All threads read from shared which cell to read and write\n                int cell_to_read = current_cell_to_read[halfwarp_id];\n                int cell_to_write = current_cell_to_write[halfwarp_id];\n\n                // Here all threads in half-warp will take same path\n                if (cell_to_read != -1)\n                {\n                    Ac_nonzero_values[ bsize_sq * Ac_dia_values[cell_to_write] + mat_entry_index] += A_nonzero_values[bsize_sq * A_dia_values[cell_to_read] + mat_entry_index];\n                }\n            }\n\n            // Contribution from A_nonzero_values of fine point i\n            // Find maximum number of nonzeros for threads in same halfwarp_id\n            int j_offset_min, j_offset_max;\n\n            if (i != -1)\n            {\n                j_offset_min = A_row_offsets[i];\n                j_offset_max = A_row_offsets[i + 1];\n                max_nonzeros_j[tid] = j_offset_max - j_offset_min;\n            }\n            else\n            {\n                j_offset_min = 0;\n                j_offset_max = 0;\n                max_nonzeros_j[tid] = 0;\n            }\n\n            int max_loop_j = 0;\n\n            for (int m = 0; m < bsize_sq; m++)\n            {\n                int max_nonzeros = max_nonzeros_j[halfwarp_id * bsize_sq + m];\n\n                if (max_nonzeros > max_loop_j)\n                {\n                    max_loop_j = max_nonzeros;\n                }\n            }\n\n            // Add contribution from nonzero_values of A\n            int j_offset_to_read;\n\n            for (int jloop = 0, j_offset = j_offset_min; jloop < max_loop_j; jloop++, j_offset++)\n            {\n                int J, j, k;\n\n                if (j_offset < j_offset_max)\n                {\n                    j_offset_to_read = j_offset;\n                    j = A_column_indices[j_offset];\n                    J = aggregates[j];\n\n                    // Find index k where to store the data and create A_column_indices\n                    if (I != J)\n                    {\n                        // This weird construct is to allow loop unrolling and avoid register spilling (see original version below)\n                        int found = 0;\n                        k = Ac_offset_min;\n#pragma unroll\n\n                        for (int icount = 0; icount < max_nonzeros_per_row; icount++)\n                        {\n                            if (k < Ac_offset_max)\n                            {\n                                if (found == 0)\n                                {\n                                    int Jtemp = (icount < size_hash_reg)  ? hash_reg[icount] : hash_shared[shared_offset + icount - size_hash_reg];\n\n                                    if (J == Jtemp)\n                                    {\n                                        found = 1;\n                                    }\n\n                                    if (found == 0) { k++; }\n                                }\n                            }\n                        }\n                    } // if I != J\n                } // if j_offset < j_offset_max\n                else\n                {\n                    j_offset_to_read = -1;\n                }\n\n                // Have threads collaborate to load in coalesced fashion\n                for (int m = 0; m < bsize_sq; m++)\n                {\n                    // Write which cell to load in shared memory buffer\n                    if (mat_entry_index == m)\n                    {\n                        current_cell_to_read[halfwarp_id] = j_offset_to_read;\n\n                        if (I != J)\n                        {\n                            current_cell_to_write[halfwarp_id] = -k;\n                        }\n                        else\n                        {\n                            current_cell_to_write[halfwarp_id] = I + 1;\n                        }\n                    }\n\n                    // All threads read from shared which cell to read and write\n                    int cell_to_read = current_cell_to_read[halfwarp_id];\n                    int cell_to_write = current_cell_to_write[halfwarp_id];\n\n                    if (cell_to_read != -1)\n                    {\n                        if (cell_to_write <= 0)\n                        {\n                            Ac_nonzero_values[(-cell_to_write)*bsize_sq + mat_entry_index] += A_nonzero_values[cell_to_read * bsize_sq + mat_entry_index];\n                        }\n                        else\n                        {\n                            Ac_nonzero_values[ Ac_dia_values[ cell_to_write - 1 ] * bsize_sq + mat_entry_index] += A_nonzero_values[cell_to_read * bsize_sq + mat_entry_index];\n                        }\n                    }\n                }\n            } // j_offset_loop\n        } // i_offset_loop\n    } // if tid < num_threads\n}\n\n// Constructor\ntemplate<class T_Config>\nHybridCoarseAGeneratorBase<T_Config>::HybridCoarseAGeneratorBase()\n{\n}\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n// The number of non-zeros per row is computed using Thrust\n// The coarse matrix is created by custom kernel\n//-----------------------------------------------------\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid HybridCoarseAGenerator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_4x4(const Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &A, Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &Ac, const typename Matrix_d::IVector &aggregates, const typename Matrix_d::IVector &R_row_offsets, const typename Matrix_d::IVector &R_column_indices, const int num_aggregates)\n{\n// supports both DIA properties\n    if (!A.hasProps(DIAG))\n    {\n        FatalError(\"Hybryd coarser does not support inside diagonal yet\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_num_nz() == 0)\n    {\n        FatalError(\"Hybryd coarser does not work correctly with diagonal matrices\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typename Matrix_d::IVector I(A.get_num_nz(), -1);\n    typename Matrix_d::IVector J(A.get_num_nz(), -1);\n    typedef device_vector_alloc<IndexType> IntVector;\n    typedef typename IntVector::iterator IntIterator;\n    typedef amgx::thrust::tuple< IntIterator, IntIterator> IntIteratorTuple;\n    typedef amgx::thrust::zip_iterator<IntIteratorTuple> ZipIterator;\n    ZipIterator new_end;\n    const int block_size_I = 128;\n    const int block_size_J = 256;\n    const int num_blocks_I = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() - 1) / block_size_I + 1));\n    const int num_blocks_J = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_nz() - 1) / block_size_J + 1));\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_values_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n    // Kernel to fill array I with aggregates number for fine points i\n    iToIKernel <<< num_blocks_I, block_size_I>>>(A_row_offsets_ptr, aggregates_ptr, I_ptr, (int) A.get_num_rows());\n    cudaCheckError();\n    // Kernel to fill array J with aggregates number for fine points j\n    jToJKernel <<< num_blocks_J, block_size_J>>>(A_column_indices_ptr, aggregates_ptr, J_ptr, (int) A.get_num_nz());\n    cudaCheckError();\n    // Sort (I,J) by rows and columns (I,J)\n    IVector permutation(A.get_num_nz());\n    thrust_wrapper::sequence<AMGX_device>(permutation.begin(), permutation.end());\n    cudaCheckError();\n    // compute permutation and sort by (I,J)\n    {\n        IVector temp(J);\n        amgx::thrust::stable_sort_by_key(temp.begin(), temp.end(), permutation.begin());\n        cudaCheckError();\n        temp = I;\n        //I = temp;\n        thrust_wrapper::gather<AMGX_device>(permutation.begin(), permutation.end(), temp.begin(), I.begin());\n        cudaCheckError();\n        amgx::thrust::stable_sort_by_key(I.begin(), I.end(), permutation.begin());\n        cudaCheckError();\n        temp = J;\n        //J = temp;\n        thrust_wrapper::gather<AMGX_device>(permutation.begin(), permutation.end(), temp.begin(), J.begin());\n        cudaCheckError();\n    }\n    // Remove duplicate tuples\n    new_end = amgx::thrust::unique(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                             amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(), J.end())), amgx::thrust::equal_to < amgx::thrust::tuple<IndexType, IndexType> >());\n    cudaCheckError();\n    IntIteratorTuple endTuple = new_end.get_iterator_tuple();\n    I.erase(amgx::thrust::get<0>(endTuple), I.end());\n    J.erase(amgx::thrust::get<1>(endTuple), J.end());\n    // Remove diagonal terms\n    new_end = amgx::thrust::remove_if(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(), J.end())), isDiagonal() );\n    cudaCheckError();\n    endTuple = new_end.get_iterator_tuple();\n    I.erase(amgx::thrust::get<0>(endTuple), I.end());\n    J.erase(amgx::thrust::get<1>(endTuple), J.end());\n    int nonzero_blocks = J.size();\n    // Resize Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    Ac.resize(num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx());\n    //Ac.resize(num_aggregates,num_aggregates,nonzero_blocks,A.get_block_dimy(),A.get_block_dimx(), 1);\n    // Ac.column_indices\n    Ac.col_indices = J;\n    J.clear();\n    J.shrink_to_fit();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(I, Ac.row_offsets);\n    cudaCheckError();\n    I.resize(Ac.row_offsets.size());\n    // Compute the maximum number of nonzeros\n    amgx::thrust::adjacent_difference(Ac.row_offsets.begin(), Ac.row_offsets.end(), I.begin());\n    cudaCheckError();\n    const IndexType max_nonzero_per_row = *amgx::thrust::max_element(I.begin() + 1, I.end());\n    cudaCheckError();\n    //std::cout << \"max_nonzero_per_row\" << max_nonzero_per_row << std::endl;\n    I.clear();\n    I.shrink_to_fit();\n    const IndexType *R_row_offsets_ptr = R_row_offsets.raw();\n    const IndexType *R_column_indices_ptr = R_column_indices.raw();\n    // Get max_nonzero_per_row\n    //const IndexType max_nonzero_per_row = 32;\n    const int threads_per_block = 128;\n    // Store the column_indices in a register\n    // Option to print the number of nonzeros distribution\n    //printNonzeroStats(Ac_row_offsets_temp,num_aggregates);\n    // Resize Ac and doing exclusive scan on Ac_row_offsets_temp\n    IndexType *Ac_row_offsets_ptr = Ac.row_offsets.raw();\n    IndexType *Ac_column_indices_ptr = Ac.col_indices.raw();\n    IndexType *Ac_dia_values_ptr = Ac.diag.raw();\n    ValueType *Ac_nonzero_values_ptr = Ac.values.raw();\n    // Now create Ac.dia_values and Ac.nonzero_values\n    //thrust_wrapper::fill<AMGX_device>(Ac.diag.begin(),Ac.diag.end(),0.);\n    thrust_wrapper::fill<AMGX_device>(Ac.values.begin(), Ac.values.end(), 0.);\n    cudaCheckError();\n    // Coalesced version of kernel to fill A\n    const int num_threads = ( ( num_aggregates + 15) / 16 ) * 16;\n    const int num_blocks2 = ( ( num_threads + threads_per_block - 1) / threads_per_block );\n\n    if (max_nonzero_per_row < 16)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 16, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 20)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 20, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 24)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 24, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 28)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 28, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 32)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 32, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 36)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 36, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 40)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 40, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 44)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 44, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 48)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 48, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 52)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 52, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 56)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 56, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 60)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 60, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 64)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 64, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 128)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 128, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 256)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 256, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 512)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 256, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else\n    {\n        FatalError(\"Maximum number of nonzeros is too large\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid HybridCoarseAGenerator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_4x4(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A, Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    FatalError(\"Host is unsupported for HybridCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// ------------------------------------------------\ntemplate <class T_Config>\nvoid HybridCoarseAGeneratorBase<T_Config>::computeAOperator(const Matrix<T_Config> &A, Matrix<T_Config> &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    Ac.set_initialized(0);\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeAOperator_4x4( A, Ac, aggregates, R_row_offsets, R_column_indices, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for HybridCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    Ac.set_initialized(1);\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class HybridCoarseAGeneratorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class HybridCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n// The number of non-zeros per row is computed using Thrust\n// The coarse matrix is created by custom kernel\n// --------------------------------------------------------\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/coarseAgenerators/hybrid_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <cusp/detail/format_utils.h> //indices_to_offsets\n#include <thrust/remove.h>\n#include <thrust/extrema.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/unique.h>\n#include <error.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntypedef amgx::thrust::tuple<int, int> tuple_t;\n\nstruct isDiagonal\n{\n    __host__ __device__\n    bool operator()(tuple_t IJ)\n    {\n        return ( amgx::thrust::get<0>(IJ) == amgx::thrust::get<1>(IJ) );\n    }\n\n};\n\n// --------------------\n// Kernels\n// --------------------\n\n// Kernel to store aggregate I of each fine point index i\ntemplate <typename IndexType>\n__global__\nvoid iToIKernel(const IndexType *row_offsets, const IndexType *aggregates, IndexType *I, const int num_rows)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        int agg = aggregates[tid];\n\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            I[j] = agg;\n        }\n    }\n}\n\n// Kernel to store aggregate J of each fine point index j\ntemplate <typename IndexType>\n__global__\nvoid jToJKernel(const IndexType *column_indices, const IndexType *aggregates, IndexType *J, const int num_entries)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_entries; tid += gridDim.x * blockDim.x)\n    {\n        int j = column_indices[tid];\n        J[tid] = aggregates[j];\n    }\n}\n\n// Kernel to fill matrix Ac for 4x4 block matrices\ntemplate <typename IndexType, typename ValueType, int max_nonzeros_per_row, int threads_per_block, int bsize_sq, int log_bsize_sq>\n__global__\nvoid fillA_4by4Blocks_Thrust_Kernel(const IndexType *A_row_offsets, const IndexType *A_column_indices, const IndexType *A_dia_values, const ValueType *A_nonzero_values, const IndexType *R_row_offsets, const IndexType *R_column_indices, const IndexType *Ac_row_offsets, const IndexType *Ac_column_indices, IndexType *Ac_dia_values, ValueType *Ac_nonzero_values, const IndexType *aggregates, const int num_aggregates, const int num_threads)\n{\n    const int tid = threadIdx.x;\n    const int I = blockIdx.x * threads_per_block + tid;\n    const int halfwarp_id = tid >> log_bsize_sq;\n    const int mat_entry_index = tid  & (bsize_sq - 1);\n    volatile __shared__ IndexType current_cell_to_read[threads_per_block / bsize_sq];\n    volatile __shared__ IndexType current_cell_to_write[threads_per_block / bsize_sq];\n    volatile __shared__ IndexType max_nonzeros_i[threads_per_block];\n    volatile __shared__ IndexType max_nonzeros_j[threads_per_block];\n    const int size_hash_reg = (2. / 3.) * max_nonzeros_per_row; // 2/3 is arbitrary\n    const int size_hash_shared = max_nonzeros_per_row - size_hash_reg;\n    IndexType shared_offset = threadIdx.x * size_hash_shared;\n    volatile __shared__ IndexType hash_shared[size_hash_shared * threads_per_block];\n    IndexType hash_reg[size_hash_reg];\n    int Ac_offset_min;\n    int Ac_offset_max;\n#pragma unroll\n\n    for (int i = 0; i < size_hash_reg; i++)\n    {\n        hash_reg[i] = -1;\n    }\n\n    for (int i = 0; i < size_hash_shared; i++)\n    {\n        hash_shared[shared_offset + i] = -1;\n    }\n\n    // Find maximum number of nonzeros for threads in same halfwarp\n    int i_offset_min, i_offset_max;\n\n    if (I < num_aggregates)\n    {\n        i_offset_min = R_row_offsets[I];\n        i_offset_max = R_row_offsets[I + 1];\n        max_nonzeros_i[tid] = i_offset_max - i_offset_min;\n        Ac_offset_min = Ac_row_offsets[I];\n        Ac_offset_max = Ac_row_offsets[I + 1];\n\n        for (int k = Ac_offset_min, icount = 0; k < Ac_offset_max; k++, icount++)\n        {\n            if (icount < size_hash_reg)\n            {\n                hash_reg[icount] = Ac_column_indices[k];\n            }\n            else\n            {\n                hash_shared[shared_offset + icount - size_hash_reg] = Ac_column_indices[k];\n            }\n        }\n    }\n    else\n    {\n        i_offset_min = 0;\n        i_offset_max = 0;\n        max_nonzeros_i[tid] = 0;\n    }\n\n    int max_loop_i = 0;\n\n    for (int m = 0; m < bsize_sq; m++)\n    {\n        int max_nonzeros = max_nonzeros_i[halfwarp_id * bsize_sq + m];\n\n        if (max_nonzeros > max_loop_i)\n        {\n            max_loop_i = max_nonzeros;\n        }\n    }\n\n    // For all threads that could do useful work\n    if (I < num_threads)\n    {\n        for (int iloop = 0, i_offset = i_offset_min; iloop < max_loop_i; iloop++, i_offset++)\n        {\n            // Contribution from A_dia_values of fine point i\n            int i = (i_offset < i_offset_max)  ? R_column_indices[i_offset] : -1 ;\n\n            // Have threads collaborate to load in coalesced fashion\n            for (int m = 0; m < bsize_sq; m++)\n            {\n                // Write which cell to load in shared memory buffer\n                if (mat_entry_index == m)\n                {\n                    current_cell_to_read[halfwarp_id] = i;\n                    current_cell_to_write[halfwarp_id] = I;\n                }\n\n                // All threads read from shared which cell to read and write\n                int cell_to_read = current_cell_to_read[halfwarp_id];\n                int cell_to_write = current_cell_to_write[halfwarp_id];\n\n                // Here all threads in half-warp will take same path\n                if (cell_to_read != -1)\n                {\n                    Ac_nonzero_values[ bsize_sq * Ac_dia_values[cell_to_write] + mat_entry_index] += A_nonzero_values[bsize_sq * A_dia_values[cell_to_read] + mat_entry_index];\n                }\n            }\n\n            // Contribution from A_nonzero_values of fine point i\n            // Find maximum number of nonzeros for threads in same halfwarp_id\n            int j_offset_min, j_offset_max;\n\n            if (i != -1)\n            {\n                j_offset_min = A_row_offsets[i];\n                j_offset_max = A_row_offsets[i + 1];\n                max_nonzeros_j[tid] = j_offset_max - j_offset_min;\n            }\n            else\n            {\n                j_offset_min = 0;\n                j_offset_max = 0;\n                max_nonzeros_j[tid] = 0;\n            }\n\n            int max_loop_j = 0;\n\n            for (int m = 0; m < bsize_sq; m++)\n            {\n                int max_nonzeros = max_nonzeros_j[halfwarp_id * bsize_sq + m];\n\n                if (max_nonzeros > max_loop_j)\n                {\n                    max_loop_j = max_nonzeros;\n                }\n            }\n\n            // Add contribution from nonzero_values of A\n            int j_offset_to_read;\n\n            for (int jloop = 0, j_offset = j_offset_min; jloop < max_loop_j; jloop++, j_offset++)\n            {\n                int J, j, k;\n\n                if (j_offset < j_offset_max)\n                {\n                    j_offset_to_read = j_offset;\n                    j = A_column_indices[j_offset];\n                    J = aggregates[j];\n\n                    // Find index k where to store the data and create A_column_indices\n                    if (I != J)\n                    {\n                        // This weird construct is to allow loop unrolling and avoid register spilling (see original version below)\n                        int found = 0;\n                        k = Ac_offset_min;\n#pragma unroll\n\n                        for (int icount = 0; icount < max_nonzeros_per_row; icount++)\n                        {\n                            if (k < Ac_offset_max)\n                            {\n                                if (found == 0)\n                                {\n                                    int Jtemp = (icount < size_hash_reg)  ? hash_reg[icount] : hash_shared[shared_offset + icount - size_hash_reg];\n\n                                    if (J == Jtemp)\n                                    {\n                                        found = 1;\n                                    }\n\n                                    if (found == 0) { k++; }\n                                }\n                            }\n                        }\n                    } // if I != J\n                } // if j_offset < j_offset_max\n                else\n                {\n                    j_offset_to_read = -1;\n                }\n\n                // Have threads collaborate to load in coalesced fashion\n                for (int m = 0; m < bsize_sq; m++)\n                {\n                    // Write which cell to load in shared memory buffer\n                    if (mat_entry_index == m)\n                    {\n                        current_cell_to_read[halfwarp_id] = j_offset_to_read;\n\n                        if (I != J)\n                        {\n                            current_cell_to_write[halfwarp_id] = -k;\n                        }\n                        else\n                        {\n                            current_cell_to_write[halfwarp_id] = I + 1;\n                        }\n                    }\n\n                    // All threads read from shared which cell to read and write\n                    int cell_to_read = current_cell_to_read[halfwarp_id];\n                    int cell_to_write = current_cell_to_write[halfwarp_id];\n\n                    if (cell_to_read != -1)\n                    {\n                        if (cell_to_write <= 0)\n                        {\n                            Ac_nonzero_values[(-cell_to_write)*bsize_sq + mat_entry_index] += A_nonzero_values[cell_to_read * bsize_sq + mat_entry_index];\n                        }\n                        else\n                        {\n                            Ac_nonzero_values[ Ac_dia_values[ cell_to_write - 1 ] * bsize_sq + mat_entry_index] += A_nonzero_values[cell_to_read * bsize_sq + mat_entry_index];\n                        }\n                    }\n                }\n            } // j_offset_loop\n        } // i_offset_loop\n    } // if tid < num_threads\n}\n\n// Constructor\ntemplate<class T_Config>\nHybridCoarseAGeneratorBase<T_Config>::HybridCoarseAGeneratorBase()\n{\n}\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n// The number of non-zeros per row is computed using Thrust\n// The coarse matrix is created by custom kernel\n//-----------------------------------------------------\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid HybridCoarseAGenerator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_4x4(const Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &A, Matrix<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> > &Ac, const typename Matrix_d::IVector &aggregates, const typename Matrix_d::IVector &R_row_offsets, const typename Matrix_d::IVector &R_column_indices, const int num_aggregates)\n{\n// supports both DIA properties\n    if (!A.hasProps(DIAG))\n    {\n        FatalError(\"Hybryd coarser does not support inside diagonal yet\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    if (A.get_num_nz() == 0)\n    {\n        FatalError(\"Hybryd coarser does not work correctly with diagonal matrices\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typename Matrix_d::IVector I(A.get_num_nz(), -1);\n    typename Matrix_d::IVector J(A.get_num_nz(), -1);\n    typedef device_vector_alloc<IndexType> IntVector;\n    typedef typename IntVector::iterator IntIterator;\n    typedef amgx::thrust::tuple< IntIterator, IntIterator> IntIteratorTuple;\n    typedef amgx::thrust::zip_iterator<IntIteratorTuple> ZipIterator;\n    ZipIterator new_end;\n    const int block_size_I = 128;\n    const int block_size_J = 256;\n    const int num_blocks_I = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() - 1) / block_size_I + 1));\n    const int num_blocks_J = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_nz() - 1) / block_size_J + 1));\n    const IndexType *A_row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *A_column_indices_ptr = A.col_indices.raw();\n    const IndexType *A_dia_values_ptr = A.diag.raw();\n    const ValueType *A_nonzero_values_ptr = A.values.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n    // Kernel to fill array I with aggregates number for fine points i\n    iToIKernel <<< num_blocks_I, block_size_I>>>(A_row_offsets_ptr, aggregates_ptr, I_ptr, (int) A.get_num_rows());\n    cudaCheckError();\n    // Kernel to fill array J with aggregates number for fine points j\n    jToJKernel <<< num_blocks_J, block_size_J>>>(A_column_indices_ptr, aggregates_ptr, J_ptr, (int) A.get_num_nz());\n    cudaCheckError();\n    // Sort (I,J) by rows and columns (I,J)\n    IVector permutation(A.get_num_nz());\n    thrust_wrapper::sequence<AMGX_device>(permutation.begin(), permutation.end());\n    cudaCheckError();\n    // compute permutation and sort by (I,J)\n    {\n        IVector temp(J);\n        amgx::thrust::stable_sort_by_key(temp.begin(), temp.end(), permutation.begin());\n        cudaCheckError();\n        temp = I;\n        //I = temp;\n        thrust_wrapper::gather<AMGX_device>(permutation.begin(), permutation.end(), temp.begin(), I.begin());\n        cudaCheckError();\n        amgx::thrust::stable_sort_by_key(I.begin(), I.end(), permutation.begin());\n        cudaCheckError();\n        temp = J;\n        //J = temp;\n        thrust_wrapper::gather<AMGX_device>(permutation.begin(), permutation.end(), temp.begin(), J.begin());\n        cudaCheckError();\n    }\n    // Remove duplicate tuples\n    new_end = amgx::thrust::unique(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                             amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(), J.end())), amgx::thrust::equal_to < amgx::thrust::tuple<IndexType, IndexType> >());\n    cudaCheckError();\n    IntIteratorTuple endTuple = new_end.get_iterator_tuple();\n    I.erase(amgx::thrust::get<0>(endTuple), I.end());\n    J.erase(amgx::thrust::get<1>(endTuple), J.end());\n    // Remove diagonal terms\n    new_end = amgx::thrust::remove_if(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(), J.end())), isDiagonal() );\n    cudaCheckError();\n    endTuple = new_end.get_iterator_tuple();\n    I.erase(amgx::thrust::get<0>(endTuple), I.end());\n    J.erase(amgx::thrust::get<1>(endTuple), J.end());\n    int nonzero_blocks = J.size();\n    // Resize Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    Ac.resize(num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx());\n    //Ac.resize(num_aggregates,num_aggregates,nonzero_blocks,A.get_block_dimy(),A.get_block_dimx(), 1);\n    // Ac.column_indices\n    Ac.col_indices = J;\n    J.clear();\n    J.shrink_to_fit();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(I, Ac.row_offsets);\n    cudaCheckError();\n    I.resize(Ac.row_offsets.size());\n    // Compute the maximum number of nonzeros\n    amgx::thrust::adjacent_difference(Ac.row_offsets.begin(), Ac.row_offsets.end(), I.begin());\n    cudaCheckError();\n    const IndexType max_nonzero_per_row = *amgx::thrust::max_element(I.begin() + 1, I.end());\n    cudaCheckError();\n    //std::cout << \"max_nonzero_per_row\" << max_nonzero_per_row << std::endl;\n    I.clear();\n    I.shrink_to_fit();\n    const IndexType *R_row_offsets_ptr = R_row_offsets.raw();\n    const IndexType *R_column_indices_ptr = R_column_indices.raw();\n    // Get max_nonzero_per_row\n    //const IndexType max_nonzero_per_row = 32;\n    const int threads_per_block = 128;\n    // Store the column_indices in a register\n    // Option to print the number of nonzeros distribution\n    //printNonzeroStats(Ac_row_offsets_temp,num_aggregates);\n    // Resize Ac and doing exclusive scan on Ac_row_offsets_temp\n    IndexType *Ac_row_offsets_ptr = Ac.row_offsets.raw();\n    IndexType *Ac_column_indices_ptr = Ac.col_indices.raw();\n    IndexType *Ac_dia_values_ptr = Ac.diag.raw();\n    ValueType *Ac_nonzero_values_ptr = Ac.values.raw();\n    // Now create Ac.dia_values and Ac.nonzero_values\n    //thrust_wrapper::fill<AMGX_device>(Ac.diag.begin(),Ac.diag.end(),0.);\n    thrust_wrapper::fill<AMGX_device>(Ac.values.begin(), Ac.values.end(), 0.);\n    cudaCheckError();\n    // Coalesced version of kernel to fill A\n    const int num_threads = ( ( num_aggregates + 15) / 16 ) * 16;\n    const int num_blocks2 = ( ( num_threads + threads_per_block - 1) / threads_per_block );\n\n    if (max_nonzero_per_row < 16)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 16, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 20)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 20, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 24)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 24, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 28)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 28, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 32)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 32, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 36)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 36, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 40)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 40, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 44)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 44, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 48)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 48, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 52)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 52, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 56)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 56, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 60)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 60, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 64)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 64, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 128)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 128, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 256)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 256, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else if (max_nonzero_per_row < 512)\n    {\n        fillA_4by4Blocks_Thrust_Kernel<IndexType, ValueType, 256, threads_per_block, 16, 4> <<< num_blocks2, threads_per_block>>>(A_row_offsets_ptr, A_column_indices_ptr, A_dia_values_ptr, A_nonzero_values_ptr, R_row_offsets_ptr, R_column_indices_ptr, Ac_row_offsets_ptr, Ac_column_indices_ptr, Ac_dia_values_ptr, Ac_nonzero_values_ptr, aggregates_ptr, num_aggregates, num_threads);\n    }\n    else\n    {\n        FatalError(\"Maximum number of nonzeros is too large\", AMGX_ERR_BAD_PARAMETERS);\n    }\n\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid HybridCoarseAGenerator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_4x4(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A, Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    FatalError(\"Host is unsupported for HybridCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n}\n\n// ------------------------------------------------\ntemplate <class T_Config>\nvoid HybridCoarseAGeneratorBase<T_Config>::computeAOperator(const Matrix<T_Config> &A, Matrix<T_Config> &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    Ac.set_initialized(0);\n\n    if (A.get_block_dimx() == 4 && A.get_block_dimy() == 4)\n    {\n        computeAOperator_4x4( A, Ac, aggregates, R_row_offsets, R_column_indices, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for HybridCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    Ac.set_initialized(1);\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class HybridCoarseAGeneratorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class HybridCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/coarseAgenerators/hybrid_coarse_A_generator.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n// --------------------------------------------------------\n// --------------------------------------------------------\n#include <aggregation/coarseAgenerators/thrust_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <thrust/remove.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <error.h>\n#include <cutil.h>\n#include <types.h>\n#include <cusp/detail/format_utils.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntypedef amgx::thrust::tuple<int, int> tuple_t;\n\n// --------------------\n// Kernels\n// --------------------\n\n// Kernel to store aggregate I of each fine point index i\ntemplate <typename IndexType>\n__global__\nvoid iToIKernel(const IndexType *row_offsets, const IndexType *aggregates, IndexType *I, const int num_rows)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        int agg = aggregates[tid];\n\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            I[j] = agg;\n        }\n    }\n}\n\n// Kernel to store aggregate J of each fine point index j\ntemplate <typename IndexType>\n__global__\nvoid jToJKernel(const IndexType *column_indices, const IndexType *aggregates, IndexType *J, const int num_entries)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_entries; tid += gridDim.x * blockDim.x)\n    {\n        int j = column_indices[tid];\n        J[tid] = aggregates[j];\n    }\n}\n\n// Constructor\ntemplate<class T_Config>\nThrustCoarseAGeneratorBase<T_Config>::ThrustCoarseAGeneratorBase()\n{\n}\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n//-----------------------------------------------------\n\n// Method to compute A on DEVICE using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ThrustCoarseAGenerator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1(const Matrix_d &A, Matrix_d &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"ThrustCoarseAGenerator: unsupported diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n    IVector I(A.get_num_nz(), -1);\n    IVector J(A.get_num_nz(), -1);\n    VVector V(A.get_num_nz(), -1);\n    const int block_size_I = 128;\n    const int block_size_J = 256;\n    const int num_blocks_I = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() - 1) / block_size_I + 1) );\n    const int num_blocks_J = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_nz() - 1) / block_size_J + 1) );\n    const IndexType *row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n    // Kernel to fill array I with aggregates number for fine points i\n    iToIKernel <<< num_blocks_I, block_size_I>>>(row_offsets_ptr, aggregates_ptr, I_ptr, (int)A.get_num_rows());\n    cudaCheckError();\n    // Kernel to fill array J with aggregates number for fine points j\n    jToJKernel <<< num_blocks_J, block_size_J>>>(column_indices_ptr, aggregates_ptr, J_ptr, (int)A.get_num_nz());\n    cudaCheckError();\n    // Copy A.values to V array\n    amgx::thrust::copy(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), V.begin());\n    cudaCheckError();\n    // Sort (I,J,V) by rows and columns (I,J)\n    cusp::detail::sort_by_row_and_column(I, J, V);\n    cudaCheckError();\n    // compute unique number of nonzeros in the output\n    IndexType NNZ = amgx::thrust::inner_product(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end (),  J.end()))   - 1,\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())) + 1,\n                                          IndexType(0),\n                                          amgx::thrust::plus<IndexType>(),\n                                          amgx::thrust::not_equal_to< amgx::thrust::tuple<IndexType, IndexType> >()) + 1;\n    cudaCheckError();\n    // allocate space for coarse matrix Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    if (A.is_matrix_singleGPU())\n    {\n        Ac.resize(num_aggregates, num_aggregates, NNZ, 1);\n    }\n    else\n    {\n        Ac.resize_spare(num_aggregates, num_aggregates, NNZ, A.get_block_dimy(), A.get_block_dimx(), 1.0);\n\n        if (A.hasProps(DIAG)) { Ac.computeDiagonal(); }\n    }\n\n    // Reduce by key to fill in Ac.column_indices and Ac.values\n    IVector new_row_indices(NNZ, 0);\n    amgx::thrust::reduce_by_key(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(),   J.end())),\n        V.begin(),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(new_row_indices.begin(), Ac.col_indices.begin())),\n        Ac.values.begin(),\n        amgx::thrust::equal_to< amgx::thrust::tuple<IndexType, IndexType> >(),\n        amgx::thrust::plus<ValueType>());\n    cudaCheckError();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(new_row_indices, Ac.row_offsets);\n    cudaCheckError();\n    I.clear();\n    I.shrink_to_fit();\n    J.clear();\n    J.shrink_to_fit();\n    V.clear();\n    V.shrink_to_fit();\n}\n\n// Method to compute A on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ThrustCoarseAGenerator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1(const Matrix_h &A, Matrix_h &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"ThrustCoarseAGenerator: unsupported diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IVector I(A.get_num_nz(), -1);\n    IVector J(A.get_num_nz(), -1);\n    VVector V(A.get_num_nz(), -1);\n    const IndexType *row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n\n    // Kernel to fill array I with aggregates number for fine points i\n    for ( int tid = 0; tid < (int)A.get_num_rows(); tid++ )\n    {\n        int agg = aggregates_ptr[tid];\n\n        for (int j = row_offsets_ptr[tid]; j < row_offsets_ptr[tid + 1]; j++)\n        {\n            I_ptr[j] = agg;\n        }\n    }\n\n    // Kernel to fill array J with aggregates number for fine points j\n    for ( int tid = 0; tid < (int)A.get_num_nz(); tid++ )\n    {\n        int j = column_indices_ptr[tid];\n        J_ptr[tid] = aggregates_ptr[j];\n    }\n\n    // Copy A.values to V array\n    amgx::thrust::copy(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), V.begin());\n    cudaCheckError();\n    // Sort (I,J,V) by rows and columns (I,J)\n    cusp::detail::sort_by_row_and_column(I, J, V);\n    cudaCheckError();\n    // compute unique number of nonzeros in the output\n    IndexType NNZ = amgx::thrust::inner_product(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end (),  J.end()))   - 1,\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())) + 1,\n                                          IndexType(0),\n                                          amgx::thrust::plus<IndexType>(),\n                                          amgx::thrust::not_equal_to< amgx::thrust::tuple<IndexType, IndexType> >()) + 1;\n    cudaCheckError();\n    // allocate space for coarse matrix Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    if (A.is_matrix_singleGPU())\n    {\n        Ac.resize(num_aggregates, num_aggregates, NNZ, 1);\n    }\n    else\n    {\n        Ac.resize_spare(num_aggregates, num_aggregates, NNZ, A.get_block_dimy(), A.get_block_dimx(), 1.0);\n\n        if (A.hasProps(DIAG)) { Ac.computeDiagonal(); }\n    }\n\n    // Reduce by key to fill in Ac.column_indices and Ac.values\n    typename Matrix_h::IVector new_row_indices(NNZ, 0);\n    amgx::thrust::reduce_by_key(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(),   J.end())),\n        V.begin(),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(new_row_indices.begin(), Ac.col_indices.begin())),\n        Ac.values.begin(),\n        amgx::thrust::equal_to< amgx::thrust::tuple<IndexType, IndexType> >(),\n        amgx::thrust::plus<ValueType>());\n    cudaCheckError();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(new_row_indices, Ac.row_offsets);\n    cudaCheckError();\n    I.clear();\n    I.shrink_to_fit();\n    J.clear();\n    J.shrink_to_fit();\n    V.clear();\n    V.shrink_to_fit();\n}\n\n// ------------------------------------------------\ntemplate <class T_Config>\nvoid ThrustCoarseAGeneratorBase<T_Config>::computeAOperator(const Matrix<T_Config> &A, Matrix<T_Config> &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    Ac.set_initialized(0);\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeAOperator_1x1( A, Ac, aggregates, R_row_offsets, R_column_indices, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for ThrustCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (Ac.is_matrix_singleGPU()) { Ac.computeDiagonal(); }\n\n    Ac.set_initialized(1);\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class ThrustCoarseAGeneratorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class ThrustCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n// --------------------------------------------------------\n// --------------------------------------------------------\n#include \"hip/hip_runtime.h\"\n#include <aggregation/coarseAgenerators/thrust_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <thrust/remove.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <error.h>\n#include <cutil.h>\n#include <types.h>\n#include <cusp/detail/format_utils.h>\n\nnamespace amgx\n{\n\nnamespace aggregation\n{\n\ntypedef amgx::thrust::tuple<int, int> tuple_t;\n\n// --------------------\n// Kernels\n// --------------------\n\n// Kernel to store aggregate I of each fine point index i\ntemplate <typename IndexType>\n__global__\nvoid iToIKernel(const IndexType *row_offsets, const IndexType *aggregates, IndexType *I, const int num_rows)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_rows; tid += gridDim.x * blockDim.x)\n    {\n        int agg = aggregates[tid];\n\n        for (int j = row_offsets[tid]; j < row_offsets[tid + 1]; j++)\n        {\n            I[j] = agg;\n        }\n    }\n}\n\n// Kernel to store aggregate J of each fine point index j\ntemplate <typename IndexType>\n__global__\nvoid jToJKernel(const IndexType *column_indices, const IndexType *aggregates, IndexType *J, const int num_entries)\n{\n    for (int tid = blockDim.x * blockIdx.x + threadIdx.x; tid < num_entries; tid += gridDim.x * blockDim.x)\n    {\n        int j = column_indices[tid];\n        J[tid] = aggregates[j];\n    }\n}\n\n// Constructor\ntemplate<class T_Config>\nThrustCoarseAGeneratorBase<T_Config>::ThrustCoarseAGeneratorBase()\n{\n}\n\n//-----------------------------------------------------\n// Method to compute the Galerkin product: A_c=R*A*P\n//-----------------------------------------------------\n\n// Method to compute A on DEVICE using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ThrustCoarseAGenerator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1(const Matrix_d &A, Matrix_d &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"ThrustCoarseAGenerator: unsupported diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cudaCheckError();\n    IVector I(A.get_num_nz(), -1);\n    IVector J(A.get_num_nz(), -1);\n    VVector V(A.get_num_nz(), -1);\n    const int block_size_I = 128;\n    const int block_size_J = 256;\n    const int num_blocks_I = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() - 1) / block_size_I + 1) );\n    const int num_blocks_J = std::min( AMGX_GRID_MAX_SIZE, (int) ((A.get_num_nz() - 1) / block_size_J + 1) );\n    const IndexType *row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n    // Kernel to fill array I with aggregates number for fine points i\n    iToIKernel <<< num_blocks_I, block_size_I>>>(row_offsets_ptr, aggregates_ptr, I_ptr, (int)A.get_num_rows());\n    cudaCheckError();\n    // Kernel to fill array J with aggregates number for fine points j\n    jToJKernel <<< num_blocks_J, block_size_J>>>(column_indices_ptr, aggregates_ptr, J_ptr, (int)A.get_num_nz());\n    cudaCheckError();\n    // Copy A.values to V array\n    amgx::thrust::copy(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), V.begin());\n    cudaCheckError();\n    // Sort (I,J,V) by rows and columns (I,J)\n    cusp::detail::sort_by_row_and_column(I, J, V);\n    cudaCheckError();\n    // compute unique number of nonzeros in the output\n    IndexType NNZ = amgx::thrust::inner_product(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end (),  J.end()))   - 1,\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())) + 1,\n                                          IndexType(0),\n                                          amgx::thrust::plus<IndexType>(),\n                                          amgx::thrust::not_equal_to< amgx::thrust::tuple<IndexType, IndexType> >()) + 1;\n    cudaCheckError();\n    // allocate space for coarse matrix Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    if (A.is_matrix_singleGPU())\n    {\n        Ac.resize(num_aggregates, num_aggregates, NNZ, 1);\n    }\n    else\n    {\n        Ac.resize_spare(num_aggregates, num_aggregates, NNZ, A.get_block_dimy(), A.get_block_dimx(), 1.0);\n\n        if (A.hasProps(DIAG)) { Ac.computeDiagonal(); }\n    }\n\n    // Reduce by key to fill in Ac.column_indices and Ac.values\n    IVector new_row_indices(NNZ, 0);\n    amgx::thrust::reduce_by_key(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(),   J.end())),\n        V.begin(),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(new_row_indices.begin(), Ac.col_indices.begin())),\n        Ac.values.begin(),\n        amgx::thrust::equal_to< amgx::thrust::tuple<IndexType, IndexType> >(),\n        amgx::thrust::plus<ValueType>());\n    cudaCheckError();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(new_row_indices, Ac.row_offsets);\n    cudaCheckError();\n    I.clear();\n    I.shrink_to_fit();\n    J.clear();\n    J.shrink_to_fit();\n    V.clear();\n    V.shrink_to_fit();\n}\n\n// Method to compute A on HOST using csr format\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid ThrustCoarseAGenerator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1(const Matrix_h &A, Matrix_h &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"ThrustCoarseAGenerator: unsupported diagonal\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    IVector I(A.get_num_nz(), -1);\n    IVector J(A.get_num_nz(), -1);\n    VVector V(A.get_num_nz(), -1);\n    const IndexType *row_offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const IndexType *aggregates_ptr = aggregates.raw();\n    IndexType *I_ptr = I.raw();\n    IndexType *J_ptr = J.raw();\n\n    // Kernel to fill array I with aggregates number for fine points i\n    for ( int tid = 0; tid < (int)A.get_num_rows(); tid++ )\n    {\n        int agg = aggregates_ptr[tid];\n\n        for (int j = row_offsets_ptr[tid]; j < row_offsets_ptr[tid + 1]; j++)\n        {\n            I_ptr[j] = agg;\n        }\n    }\n\n    // Kernel to fill array J with aggregates number for fine points j\n    for ( int tid = 0; tid < (int)A.get_num_nz(); tid++ )\n    {\n        int j = column_indices_ptr[tid];\n        J_ptr[tid] = aggregates_ptr[j];\n    }\n\n    // Copy A.values to V array\n    amgx::thrust::copy(A.values.begin(), A.values.begin() + A.get_num_nz()*A.get_block_size(), V.begin());\n    cudaCheckError();\n    // Sort (I,J,V) by rows and columns (I,J)\n    cusp::detail::sort_by_row_and_column(I, J, V);\n    cudaCheckError();\n    // compute unique number of nonzeros in the output\n    IndexType NNZ = amgx::thrust::inner_product(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end (),  J.end()))   - 1,\n                                          amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())) + 1,\n                                          IndexType(0),\n                                          amgx::thrust::plus<IndexType>(),\n                                          amgx::thrust::not_equal_to< amgx::thrust::tuple<IndexType, IndexType> >()) + 1;\n    cudaCheckError();\n    // allocate space for coarse matrix Ac\n    Ac.addProps(CSR);\n\n    if (A.hasProps(DIAG)) { Ac.addProps(DIAG); }\n\n    if (A.is_matrix_singleGPU())\n    {\n        Ac.resize(num_aggregates, num_aggregates, NNZ, 1);\n    }\n    else\n    {\n        Ac.resize_spare(num_aggregates, num_aggregates, NNZ, A.get_block_dimy(), A.get_block_dimx(), 1.0);\n\n        if (A.hasProps(DIAG)) { Ac.computeDiagonal(); }\n    }\n\n    // Reduce by key to fill in Ac.column_indices and Ac.values\n    typename Matrix_h::IVector new_row_indices(NNZ, 0);\n    amgx::thrust::reduce_by_key(amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.begin(), J.begin())),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(I.end(),   J.end())),\n        V.begin(),\n        amgx::thrust::make_zip_iterator(amgx::thrust::make_tuple(new_row_indices.begin(), Ac.col_indices.begin())),\n        Ac.values.begin(),\n        amgx::thrust::equal_to< amgx::thrust::tuple<IndexType, IndexType> >(),\n        amgx::thrust::plus<ValueType>());\n    cudaCheckError();\n    // Convert array new_row_indices to offsets\n    cusp::detail::indices_to_offsets(new_row_indices, Ac.row_offsets);\n    cudaCheckError();\n    I.clear();\n    I.shrink_to_fit();\n    J.clear();\n    J.shrink_to_fit();\n    V.clear();\n    V.shrink_to_fit();\n}\n\n// ------------------------------------------------\ntemplate <class T_Config>\nvoid ThrustCoarseAGeneratorBase<T_Config>::computeAOperator(const Matrix<T_Config> &A, Matrix<T_Config> &Ac, const IVector &aggregates, const IVector &R_row_offsets, const IVector &R_column_indices, const int num_aggregates)\n{\n    Ac.set_initialized(0);\n\n    if (A.get_block_dimx() == 1 && A.get_block_dimy() == 1)\n    {\n        computeAOperator_1x1( A, Ac, aggregates, R_row_offsets, R_column_indices, num_aggregates );\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for ThrustCoarseAGenerator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    if (Ac.is_matrix_singleGPU()) { Ac.computeDiagonal(); }\n\n    Ac.set_initialized(1);\n}\n\n// ---------------------------\n// Explict instantiations\n// ---------------------------\n#define AMGX_CASE_LINE(CASE) template class ThrustCoarseAGeneratorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n#define AMGX_CASE_LINE(CASE) template class ThrustCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n}\n}\n", "source": "/kaggle/working/amgx/src/aggregation/coarseAgenerators/thrust_coarse_A_generator.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <aggregation/coarseAgenerators/low_deg_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <thrust/scan.h>\n#include <thrust/remove.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust_wrapper.h>\n#include <error.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <misc.h>\n#include <hash_workspace.h>\n#include <matrix_io.h>\n#include <device_properties.h>\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\nnamespace aggregation\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl>\n\nstatic __device__ __forceinline__ int get_work( int *queue, int warp_id, int count = 1 )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, count );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_sparsity_kernel( const int  R_num_rows, // same as num_aggregates.\n                         const int *R_rows,\n                         const int *R_cols,\n                         const int *A_rows,\n                         const int *A_cols,\n                         const int *aggregates,\n                         int *Ac_rows,\n                         int *Ac_cols,\n                         int *Ac_pos,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS       = CTA_SIZE  / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id ; utils::any(r_col_it < r_col_end) ; r_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_a_row_id = utils::shfl( a_row_id, local_k );\n                // Load the range of the row of B.\n                int a_col_it = 0, a_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    a_col_it  = A_rows[uniform_a_row_id + 0];\n                    a_col_end = A_rows[uniform_a_row_id + 1];\n                }\n\n                // Iterate over the range of columns of B.\n                for ( a_col_it += lane_id_mod_num_threads ; utils::any(a_col_it < a_col_end) ; a_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int a_col_id = -1, a_agg_id = -1;\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_col_id = A_cols[a_col_it];\n                        a_agg_id = aggregates[a_col_id];\n                    }\n\n                    if ( HAS_DIAG && a_agg_id == r_row_id )\n                    {\n                        a_agg_id = -1;\n                    }\n\n                    set.insert( a_agg_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                Ac_rows[r_row_id] = count;\n            }\n        }\n        else\n        {\n            int ac_col_it = Ac_rows[r_row_id];\n            set.store_with_positions( &Ac_cols[ac_col_it], &Ac_pos[ac_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 8 )\nvoid fill_A_kernel_1x1( const int  R_num_rows,\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        int gmem_size,\n                        int *g_keys,\n                        Value_type *g_vals,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE]; \n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE  ],\n            &g_keys[r_row_id * gmem_size ],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[r_row_id * gmem_size ], gmem_size );\n    // Loop over rows of A.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the output row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first. TODO: Make sure it's needed. I don't think it is!!!!\n        map.clear();\n        // Populate the map.\n        map.load( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it] );\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // The diagonal.\n        Value_type r_diag(types::util<Value_type>::get_zero());\n\n        // _iterate over the columns of A to build C_hat.\n        for ( r_col_it += lane_id ; utils::any(r_col_it < r_col_end) ; r_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal (if needed).\n            if ( HAS_DIAG && is_active )\n            {\n                r_diag = r_diag + A_vals[A_diag[a_row_id]];\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_a_row_id = utils::shfl( a_row_id, local_k );\n                // The range of the row of B.\n                int a_col_it = 0, a_col_end = 0;\n\n                if ( local_k < num_rows )\n                {\n                    a_col_it  = utils::Ld<utils::LD_CG>::load( &A_rows[uniform_a_row_id + 0] );\n                    a_col_end = utils::Ld<utils::LD_CG>::load( &A_rows[uniform_a_row_id + 1] );\n                }\n\n                // Iterate over the range of columns of B.\n                for ( a_col_it += lane_id_mod_num_threads ; utils::any(a_col_it < a_col_end) ; a_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // Load columns and values.\n                    int a_col_id = -1;\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_col_id = A_cols[a_col_it];\n                        a_value  = A_vals[a_col_it];\n                    }\n\n                    // Find the aggregate.\n                    int a_agg_id = -1;\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_agg_id = aggregates[a_col_id];\n                    }\n\n                    // Update the diag/hash map.\n                    if ( HAS_DIAG && a_agg_id == r_row_id )\n                    {\n                        r_diag = r_diag + a_value;\n                        a_agg_id = -1;\n                    }\n\n                    map.insert( a_agg_id, a_value, NULL );  // It won't insert. Only update.\n                }\n            }\n        }\n\n        // Update the diagonal.\n        if ( HAS_DIAG )\n        {\n            r_diag = utils::warp_reduce<1, utils::Add>( r_diag );\n\n            if ( lane_id == 0 )\n            {\n                Ac_vals[Ac_diag[r_row_id]] = r_diag;\n            }\n        }\n\n        // Store the results.\n        int count = ac_col_end - ac_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &Ac_vals[ac_col_it] );\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG >\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else \n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_4x4( const int  R_num_rows, // same as num_aggregates.\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        const int gmem_size,\n                        int *g_keys,\n                        int *g_idx,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_16 = lane_id / 16;\n    const int lane_id_mod_16 = lane_id % 16;\n    const int warp_offset = 16 * lane_id_div_16;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id_div_16 ; utils::any(r_col_it < r_col_end) ; r_col_it += 2 )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG && is_active )\n            {\n                ac_diag = ac_diag + A_vals[16 * A_diag[a_row_id] + lane_id_mod_16];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = 0, a_col_end = 0;\n\n            if ( is_active )\n            {\n                a_col_begin = A_rows[a_row_id + 0];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += 16 )\n            {\n                int a_col_it = a_col_begin + lane_id_mod_16;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the 16 items.\n                for ( int k = 0 ; k < 16 ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, warp_offset + k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    warp_offset + k );\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // Load the value if possible.\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( uniform_a_col_it < a_col_end )\n                    {\n                        a_value = A_vals[16 * uniform_a_col_it + lane_id_mod_16];\n                    }\n\n                    // Proceed diagonal if needed.\n                    if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                    {\n                        ac_diag = ac_diag + a_value;\n                        uniform_ac_col = -1;\n                    }\n\n                    // Get the id of the column computed by the other half warp.\n                    int other_ac_col = utils::shfl_xor( uniform_ac_col, 16 );\n                    // If both half warps want to write to the same location, we have a conflict!!!\n                    int are_fighting = uniform_ac_col == other_ac_col;\n\n                    // Reduce the two values to a single one.\n                    if ( uniform_ac_col != -1 && are_fighting )\n                    {\n                        a_value = a_value + utils::shfl_xor( a_value, 16 );\n                    }\n\n                    // If the two half warps fight, only one can be the winner... It's the 1st half!!!\n                    int is_winner = !are_fighting || lane_id_div_16 == 0;\n\n                    // Update the value.\n                    if ( uniform_ac_col != -1 && is_winner )\n                    {\n                        Ac_vals[16 * uniform_ac_idx + lane_id_mod_16] = Ac_vals[16 * uniform_ac_idx + lane_id_mod_16] + a_value;\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            ac_diag = ac_diag + utils::shfl_xor( ac_diag, 16 );\n\n            if ( lane_id_div_16 == 0 )\n            {\n                Ac_vals[16 * Ac_diag[r_row_id] + lane_id_mod_16] = ac_diag;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int N, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool FORCE_DETERMINISM >\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_NxN( const int  R_num_rows, // same as num_aggregates.\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        const int gmem_size,\n                        int *g_keys,\n                        int *g_idx,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int T_WARP = FORCE_DETERMINISM ? 1 : WARP_SIZE / NxN;\n    const int NUM_ITEMS_PER_WARP = T_WARP == 0 ? 1 : T_WARP;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    const int warp_offset = NxN * lane_id_div_NxN;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id_div_NxN ; utils::any(r_col_it < r_col_end) ; r_col_it += NUM_ITEMS_PER_WARP )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG && is_active )\n            {\n                ac_diag = ac_diag + A_vals[NxN * A_diag[a_row_id] + lane_id_mod_NxN];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = 0, a_col_end = 0;\n\n            if ( is_active )\n            {\n                a_col_begin = A_rows[a_row_id + 0];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += NxN )\n            {\n                int a_col_it = a_col_begin + lane_id_mod_NxN;\n                // Is it active.\n                const bool is_active_k = a_col_it < a_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( is_active_k )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the NxN items.\n                #pragma unroll 1\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, warp_offset + k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    warp_offset + k );\n\n                    if ( lane_id_div_NxN >= NUM_ITEMS_PER_WARP )\n                    {\n                        uniform_ac_col = -1;\n                        uniform_ac_idx = -1;\n                    }\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // Load the value if possible.\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( uniform_a_col_it < a_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP )\n                    {\n                        a_value = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    // Update the diagonal if it is a diagonal term.\n                    if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                    {\n                        ac_diag = ac_diag + a_value;\n                        uniform_ac_col = -1;\n                    }\n\n                    // Update the value.\n                    if ( uniform_ac_col != -1 )\n                    {\n                        utils::atomic_add( &Ac_vals[NxN * uniform_ac_idx + lane_id_mod_NxN], a_value );\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            if ( !FORCE_DETERMINISM )\n            {\n                ac_diag = utils::warp_reduce<NxN, utils::Add>( ac_diag );\n            }\n\n            if ( lane_id_div_NxN == 0 )\n            {\n                Ac_vals[NxN * Ac_diag[r_row_id] + lane_id_mod_NxN] = ac_diag;\n            }\n        }\n    }\n}\n\n// when blocksize is larger than warp size\ntemplate< typename Value_type, int N, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool FORCE_DETERMINISM, int NUM_BLOCK_ITERS_PER_WARP>\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_NxN_large( const int  R_num_rows, // same as num_aggregates.\n                              const int *R_rows,\n                              const int *R_cols,\n                              const int *A_rows,\n                              const int *A_cols,\n                              const int *A_diag,\n                              const Value_type *A_vals,\n                              const int *aggregates,\n                              const int *Ac_rows,\n                              const int *Ac_cols,\n                              const int *Ac_pos,\n                              const int *Ac_diag,\n                              Value_type *Ac_vals,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *g_idx,\n                              int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp. Let's be chill here and take 1 per warp for large blocks\n    const int NUM_ITEMS_PER_WARP = 1;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( ; utils::any(r_col_it < r_col_end) ; r_col_it += NUM_ITEMS_PER_WARP )\n        {\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = R_cols[r_col_it];\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG )\n            {\n                ac_diag = ac_diag + A_vals[NxN * A_diag[a_row_id] + lane_id];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = A_rows[a_row_id + 0];\n            int a_col_end   = A_rows[a_row_id + 1];\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += NxN )\n            {\n                int a_col_it = a_col_begin + lane_id;\n                // Is it active.\n                const bool is_active_k = a_col_it < a_col_end;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( is_active_k )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the NxN items.\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    k );\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // iterate through the block\n#pragma unroll\n\n                    for (int i = 0; i < NUM_BLOCK_ITERS_PER_WARP; i++)\n                    {\n                        // Load the value if possible.\n                        Value_type a_value(types::util<Value_type>::get_zero());\n\n                        if ( uniform_a_col_it < a_col_end && (WARP_SIZE * i + lane_id) < NxN )\n                        {\n                            a_value = A_vals[NxN * uniform_a_col_it + WARP_SIZE * i + lane_id];\n                        }\n\n                        // Update the diagonal if it is a diagonal term.\n                        if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                        {\n                            ac_diag = ac_diag + a_value;\n                            uniform_ac_col = -1;\n                        }\n\n                        // Update the value.\n                        if ( uniform_ac_col != -1 && (WARP_SIZE * i + lane_id) < NxN)\n                        {\n                            utils::atomic_add( &Ac_vals[NxN * uniform_ac_idx + WARP_SIZE * i + lane_id], a_value );\n                        }\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            if ( !FORCE_DETERMINISM )\n            {\n                ac_diag = utils::warp_reduce<NxN, utils::Add>( ac_diag );\n            }\n\n            Ac_vals[NxN * Ac_diag[r_row_id] + lane_id] = ac_diag;\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\ntemplate< int CTA_SIZE, bool HAS_DIAG, bool COUNT_ONLY, typename Workspace >\nstatic\nvoid compute_sparsity_dispatch( Workspace &hash_wk,\n                                const int  R_num_rows,\n                                const int *R_rows,\n                                const int *R_cols,\n                                const int *A_rows,\n                                const int *A_cols,\n                                const int *aggregates,\n                                int *Ac_rows,\n                                int *Ac_cols,\n                                int *Ac_pos )\n{\n    cudaDeviceProp props = getDeviceProperties();\n    int GRID_SIZE = (props.major >= 7) ? 1024 : 128;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int *h_status;\n    cudaError_t cuda_rc = amgx::memory::cudaMallocHost((void **) &h_status, sizeof(int));\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMallocHost failed for h_status in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    int *h_work_offset;\n    cuda_rc = amgx::memory::cudaMallocHost((void **) &h_work_offset, sizeof(int));\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaMallocHost failed for h_work_offset in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    int attempt = 0;\n    bool warning_printed = 0;\n\n    for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n    {\n        // Double the amount of GMEM (if needed).\n        if ( attempt > 0 )\n        {\n            if (!warning_printed)\n            {\n                amgx_printf(\"WARNING: Used settings might result in degraded performance for the MG coarsener for this matrix.\\n\");\n                amgx_printf(\"WARNING: You might want to try different selector or MG algorithm for better performance.\\n\");\n                warning_printed = 1;\n            }\n\n            hash_wk.expand();\n        }\n\n        // Reset the status.\n        int *p_status = h_status;\n        *p_status = 0;\n        cudaMemcpyAsync( hash_wk.get_status(), p_status, sizeof(int), cudaMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        // Reset the work queue.\n        int *p_work_offset = h_work_offset;\n        *p_work_offset = GRID_SIZE * NUM_WARPS;\n        cudaMemcpyAsync( hash_wk.get_work_queue(), p_work_offset, sizeof(int), cudaMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        // Launch the kernel.\n        compute_sparsity_kernel<8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, HAS_DIAG, COUNT_ONLY> <<< GRID_SIZE, CTA_SIZE, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n            R_num_rows,\n            R_rows,\n            R_cols,\n            A_rows,\n            A_cols,\n            aggregates,\n            Ac_rows,\n            Ac_cols,\n            Ac_pos,\n            hash_wk.get_gmem_size(),\n            hash_wk.get_keys(),\n            hash_wk.get_work_queue(),\n            hash_wk.get_status() );\n        cudaCheckError();\n        // Read the result from count_non_zeroes.\n        cudaMemcpyAsync( p_status, hash_wk.get_status(), sizeof(int), cudaMemcpyDeviceToHost, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        cuda_rc = cudaStreamSynchronize(amgx::thrust::global_thread_handle::get_stream());\n        if (cuda_rc != cudaSuccess)\n        {\n            FatalError(\"cudaStreamSynchronize failed in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n        }\n        done = (*p_status == 0);\n        cudaCheckError();\n    }\n\n    cuda_rc = amgx::memory::cudaFreeHost(h_status);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaFreeHost failed for h_status in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = amgx::memory::cudaFreeHost(h_work_offset);\n    if (cuda_rc != cudaSuccess)\n    {\n        FatalError(\"cudaFreeHost failed for h_work_offset in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, bool HAS_DIAG, typename Workspace, typename Value_type >\nstatic\nvoid fill_A_dispatch( Workspace &hash_wk,\n                      const int  block_size,\n                      const int  R_num_rows, // same as num_aggregates.\n                      const int *R_rows,\n                      const int *R_cols,\n                      const int *A_rows,\n                      const int *A_cols,\n                      const int *A_diag,\n                      const Value_type *A_vals,\n                      const int *aggregates,\n                      const int *Ac_rows,\n                      const int *Ac_cols,\n                      const int *Ac_pos,\n                      const int *Ac_diag,\n                      Value_type *Ac_vals,\n                      bool force_determinism )\n{\n    cudaDeviceProp props = getDeviceProperties();\n    int GRID_SIZE = (props.major >= 7) ? 1024 : 128;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    cudaMemcpyAsync( hash_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n    cudaCheckError();\n\n    // Launch the kernel.\n    switch ( block_size )\n    {\n        case 1:\n            fill_A_kernel_1x1<Value_type, 8, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                hash_wk.get_vals(),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 2:\n            fill_A_kernel_NxN<Value_type, 2, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 3:\n            fill_A_kernel_NxN<Value_type, 3, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 4:\n            if ( force_determinism )\n                fill_A_kernel_NxN<Value_type, 4, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                    R_num_rows,\n                    R_rows,\n                    R_cols,\n                    A_rows,\n                    A_cols,\n                    A_diag,\n                    A_vals,\n                    aggregates,\n                    Ac_rows,\n                    Ac_cols,\n                    Ac_pos,\n                    Ac_diag,\n                    Ac_vals,\n                    hash_wk.get_gmem_size(),\n                    hash_wk.get_keys(),\n                    reinterpret_cast<int *>( hash_wk.get_vals() ),\n                    hash_wk.get_work_queue() );\n            else\n                fill_A_kernel_4x4<Value_type, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG> <<< GRID_SIZE, CTA_SIZE, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n                    R_num_rows,\n                    R_rows,\n                    R_cols,\n                    A_rows,\n                    A_cols,\n                    A_diag,\n                    A_vals,\n                    aggregates,\n                    Ac_rows,\n                    Ac_cols,\n                    Ac_pos,\n                    Ac_diag,\n                    Ac_vals,\n                    hash_wk.get_gmem_size(),\n                    hash_wk.get_keys(),\n                    reinterpret_cast<int *>( hash_wk.get_vals() ),\n                    hash_wk.get_work_queue() );\n            \n            cudaCheckError();\n            break;\n\n        case 5:\n            fill_A_kernel_NxN<Value_type, 5, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 8:\n            fill_A_kernel_NxN_large<Value_type, 8, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false, 2> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 10:\n            fill_A_kernel_NxN_large<Value_type, 10, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false, 4> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"LOW_DEG not implemented for this block size\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nLowDegCoarseAGenerator<TemplateConfig<AMGX_device, V, M, I> >::computeAOperator( const Matrix_d &A,\n        Matrix_d &Ac,\n        const IVector &aggregates,\n        const IVector &R_row_offsets,\n        const IVector &R_column_indices,\n        const int num_aggregates )\n{\n    if ( A.get_block_dimx() != A.get_block_dimy() )\n    {\n        FatalError( \"LowDegCoarseAGenerator implemented for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    // The matrix Ac will be modified.\n    Ac.set_initialized(0);\n    // Is the diagonal stored separately??\n    const int diag_prop = A.hasProps(DIAG);\n    // Allocate a workspace for hashing.\n    typedef TemplateConfig<AMGX_device, V, M, I> TConfig_d;\n\n    cudaDeviceProp props = getDeviceProperties();\n    int grid_size = (props.major >= 7) ? 1024 : 128;\n\n    Hash_Workspace<TConfig_d, int> hash_wk(true, grid_size);\n\n    // Compute row offsets of Ac.\n    Ac.addProps(CSR);\n    Ac.set_num_rows( num_aggregates );\n    Ac.set_num_cols( num_aggregates );\n    Ac.row_offsets.resize( num_aggregates + 1 );\n    // Compute the number of non-zero elements per row of Ac.\n    const int CTA_SIZE  = 128;\n\n    if ( diag_prop )\n        compute_sparsity_dispatch<CTA_SIZE, true, true>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            NULL,\n            NULL );\n    else\n        compute_sparsity_dispatch<CTA_SIZE, false, true>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            NULL,\n            NULL );\n\n    cudaCheckError();\n    // Compute the number of non-zeroes.\n    thrust_wrapper::exclusive_scan<AMGX_device>( Ac.row_offsets.begin(), Ac.row_offsets.end(), Ac.row_offsets.begin() );\n    cudaCheckError();\n    int nonzero_blocks = Ac.row_offsets[num_aggregates];\n\n    if ( diag_prop )\n    {\n        Ac.addProps(DIAG);\n    }\n\n    if ( A.is_matrix_singleGPU() )\n    {\n        Ac.resize( num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx(), !diag_prop );\n    }\n    else\n    {\n        //have 3% more nz for storage\n        Ac.resize_spare( num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx(), 1.0 );\n\n        if ( diag_prop )\n        {\n            Ac.computeDiagonal();\n        }\n    }\n\n    // Vector to store the positions in the hash table.\n    device_vector_alloc<int> Ac_pos(nonzero_blocks);\n\n    // Compute the sparsity pattern of the rows of Ac.\n    if ( diag_prop )\n        compute_sparsity_dispatch<CTA_SIZE, true, false>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ));\n    else\n        compute_sparsity_dispatch<CTA_SIZE, false, false>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ));\n\n    cudaCheckError();\n\n    // Reset values if needed.\n    if ( A.get_block_dimy() != 1 )\n    {\n        thrust_wrapper::fill<AMGX_device>( Ac.values.begin(), Ac.values.end(), types::util<ValueType>::get_zero() );\n        cudaCheckError();\n    }\n\n    // Compute values.\n    if ( diag_prop )\n    {\n        fill_A_dispatch<CTA_SIZE, true>(\n            hash_wk,\n            A.get_block_dimy(),\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ),\n            Ac.diag.raw(),\n            Ac.values.raw(),\n            this->m_force_determinism );\n    }\n    else\n    {\n        fill_A_dispatch<CTA_SIZE, false>(\n            hash_wk,\n            A.get_block_dimy(),\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ),\n            Ac.diag.raw(),\n            Ac.values.raw(),\n            this->m_force_determinism );\n    }\n\n    cudaCheckError();\n\n    // Update the diagonal if needed.\n    if ( Ac.is_matrix_singleGPU() )\n    {\n        Ac.computeDiagonal();\n    }\n\n    cudaCheckError();\n    // Finalize the modification.\n    Ac.set_initialized(1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nLowDegCoarseAGenerator<TemplateConfig<AMGX_host, V, M, I> >::computeAOperator( const Matrix_h &h_A,\n        Matrix_h &h_Ac,\n        const IVector &h_aggregates,\n        const IVector &h_R_row_offsets,\n        const IVector &h_R_column_indices,\n        const int num_aggregates )\n{\n    h_Ac.set_initialized(0);\n    IVector rows;\n    IVector inds;\n    typename Matrix_h::MVector vals;\n    typename Matrix_h::MVector diag;\n    int num_nnz = 0;\n    int diag_prop = h_A.hasProps(DIAG);\n\n    for ( int row = 0; row < num_aggregates; row++ )\n    {\n        for ( int col = 0; col < num_aggregates; col++ )\n        {\n            int fill = 0;\n            typename Matrix_h::MVector cur(h_A.get_block_size(), types::util<typename Matrix_h::value_type>::get_zero());\n\n            for ( int rc = h_R_row_offsets[row]; rc < h_R_row_offsets[row + 1]; rc++ )\n            {\n                int j = h_R_column_indices[rc];\n\n                for ( int ac = h_A.row_offsets[j]; ac < h_A.row_offsets[j + 1] + diag_prop; ac++ )\n                {\n                    int k = (ac == h_A.row_offsets[j + 1]) ? j : h_A.col_indices[ac];\n\n                    for ( int q = h_R_row_offsets[col]; q < h_R_row_offsets[col + 1]; q++ )\n                        if ( k == h_R_column_indices[q] )\n                        {\n                            fill = 1;\n                            int val_idx = (ac == h_A.row_offsets[j + 1]) ? h_A.get_num_nz() + j : ac;\n\n                            for ( int v = 0; v < h_A.get_block_size(); v++)\n                            {\n                                cur[v] = cur[v] + h_A.values[val_idx * h_A.get_block_size() + v];\n                            }\n                        }\n                }\n            }\n\n            if ( fill )\n            {\n                if ( row != col || !diag_prop )\n                {\n                    inds.push_back(col);\n                    rows.push_back(row);\n                    num_nnz++;\n\n                    for ( int v = 0; v < h_A.get_block_size(); v++ )\n                    {\n                        vals.push_back(cur[v]);\n                    }\n                }\n                else\n                {\n                    for ( int v = 0; v < h_A.get_block_size(); v++ )\n                    {\n                        diag.push_back(cur[v]);\n                    }\n                }\n            }\n        }\n    }\n\n    rows.push_back(-1);\n\n    // add diagonal to the end\n    if ( diag_prop )\n    {\n        for ( int v = 0; v < num_aggregates * h_A.get_block_size(); v++ )\n        {\n            vals.push_back(diag[v]);\n        }\n    }\n    else\n    {\n        // Add a zero at the end\n        for (int v = 0; v < h_A.get_block_size(); v++)\n        {\n            vals.push_back(types::util<typename Matrix_h::value_type>::get_zero());\n        }\n    }\n\n    h_Ac.resize(num_aggregates, num_aggregates, num_nnz, h_A.get_block_dimx(), h_A.get_block_dimy(), 1);\n    h_Ac.row_indices = rows;\n    h_Ac.col_indices = inds;\n    h_Ac.values = vals;\n    h_Ac.addProps( CSR | ( diag_prop ? DIAG : 0 ) );\n    h_Ac.computeDiagonal();\n    h_Ac.set_initialized(1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class LowDegCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace aggregation\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <aggregation/coarseAgenerators/low_deg_coarse_A_generator.h>\n#include <thrust/system/detail/generic/reduce_by_key.h>\n#include <thrust/scan.h>\n#include <thrust/remove.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust_wrapper.h>\n#include <error.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <misc.h>\n#include <hash_workspace.h>\n#include <matrix_io.h>\n#include <device_properties.h>\n#include <amgx_types/util.h>\n\nnamespace amgx\n{\nnamespace aggregation\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl>\n\nstatic __device__ __forceinline__ int get_work( int *queue, int warp_id, int count = 1 )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, count );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool COUNT_ONLY >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_sparsity_kernel( const int  R_num_rows, // same as num_aggregates.\n                         const int *R_rows,\n                         const int *R_cols,\n                         const int *A_rows,\n                         const int *A_cols,\n                         const int *aggregates,\n                         int *Ac_rows,\n                         int *Ac_cols,\n                         int *Ac_pos,\n                         const int gmem_size,\n                         int *g_keys,\n                         int *wk_work_queue,\n                         int *wk_status )\n{\n    const int NUM_WARPS       = CTA_SIZE  / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Make sure we have to proceed.\n        if ( COUNT_ONLY )\n        {\n            volatile int *status = reinterpret_cast<volatile int *>( wk_status );\n\n            if ( set.has_failed() || *status != 0 )\n            {\n                return;\n            }\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id ; utils::any(r_col_it < r_col_end) ; r_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_a_row_id = utils::shfl( a_row_id, local_k );\n                // Load the range of the row of B.\n                int a_col_it = 0, a_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    a_col_it  = A_rows[uniform_a_row_id + 0];\n                    a_col_end = A_rows[uniform_a_row_id + 1];\n                }\n\n                // Iterate over the range of columns of B.\n                for ( a_col_it += lane_id_mod_num_threads ; utils::any(a_col_it < a_col_end) ; a_col_it += NUM_THREADS_PER_ROW )\n                {\n                    int a_col_id = -1, a_agg_id = -1;\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_col_id = A_cols[a_col_it];\n                        a_agg_id = aggregates[a_col_id];\n                    }\n\n                    if ( HAS_DIAG && a_agg_id == r_row_id )\n                    {\n                        a_agg_id = -1;\n                    }\n\n                    set.insert( a_agg_id, COUNT_ONLY ? wk_status : NULL );\n                }\n            }\n        }\n\n        // Store the results.\n        if ( COUNT_ONLY )\n        {\n            int count = set.compute_size();\n\n            if ( lane_id == 0 )\n            {\n                Ac_rows[r_row_id] = count;\n            }\n        }\n        else\n        {\n            int ac_col_it = Ac_rows[r_row_id];\n            set.store_with_positions( &Ac_cols[ac_col_it], &Ac_pos[ac_col_it] );\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG >\n__global__\n__launch_bounds__( CTA_SIZE, 8 )\nvoid fill_A_kernel_1x1( const int  R_num_rows,\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        int gmem_size,\n                        int *g_keys,\n                        Value_type *g_vals,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE]; \n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE  ],\n            &g_keys[r_row_id * gmem_size ],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[r_row_id * gmem_size ], gmem_size );\n    // Loop over rows of A.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the output row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first. TODO: Make sure it's needed. I don't think it is!!!!\n        map.clear();\n        // Populate the map.\n        map.load( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it] );\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // The diagonal.\n        Value_type r_diag(types::util<Value_type>::get_zero());\n\n        // _iterate over the columns of A to build C_hat.\n        for ( r_col_it += lane_id ; utils::any(r_col_it < r_col_end) ; r_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal (if needed).\n            if ( HAS_DIAG && is_active )\n            {\n                r_diag = r_diag + A_vals[A_diag[a_row_id]];\n            }\n\n            const int num_rows = __popc( utils::ballot(is_active) );\n\n            // Uniform loop: threads collaborate to load other elements.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                const int uniform_a_row_id = utils::shfl( a_row_id, local_k );\n                // The range of the row of B.\n                int a_col_it = 0, a_col_end = 0;\n\n                if ( local_k < num_rows )\n                {\n                    a_col_it  = utils::Ld<utils::LD_CG>::load( &A_rows[uniform_a_row_id + 0] );\n                    a_col_end = utils::Ld<utils::LD_CG>::load( &A_rows[uniform_a_row_id + 1] );\n                }\n\n                // Iterate over the range of columns of B.\n                for ( a_col_it += lane_id_mod_num_threads ; utils::any(a_col_it < a_col_end) ; a_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // Load columns and values.\n                    int a_col_id = -1;\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_col_id = A_cols[a_col_it];\n                        a_value  = A_vals[a_col_it];\n                    }\n\n                    // Find the aggregate.\n                    int a_agg_id = -1;\n\n                    if ( a_col_it < a_col_end )\n                    {\n                        a_agg_id = aggregates[a_col_id];\n                    }\n\n                    // Update the diag/hash map.\n                    if ( HAS_DIAG && a_agg_id == r_row_id )\n                    {\n                        r_diag = r_diag + a_value;\n                        a_agg_id = -1;\n                    }\n\n                    map.insert( a_agg_id, a_value, NULL );  // It won't insert. Only update.\n                }\n            }\n        }\n\n        // Update the diagonal.\n        if ( HAS_DIAG )\n        {\n            r_diag = utils::warp_reduce<1, utils::Add>( r_diag );\n\n            if ( lane_id == 0 )\n            {\n                Ac_vals[Ac_diag[r_row_id]] = r_diag;\n            }\n        }\n\n        // Store the results.\n        int count = ac_col_end - ac_col_it;\n\n        if ( count == 0 )\n        {\n            continue;\n        }\n\n        map.store( count, &Ac_vals[ac_col_it] );\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG >\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else \n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_4x4( const int  R_num_rows, // same as num_aggregates.\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        const int gmem_size,\n                        int *g_keys,\n                        int *g_idx,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_16 = lane_id / 16;\n    const int lane_id_mod_16 = lane_id % 16;\n    const int warp_offset = 16 * lane_id_div_16;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id_div_16 ; utils::any(r_col_it < r_col_end) ; r_col_it += 2 )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG && is_active )\n            {\n                ac_diag = ac_diag + A_vals[16 * A_diag[a_row_id] + lane_id_mod_16];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = 0, a_col_end = 0;\n\n            if ( is_active )\n            {\n                a_col_begin = A_rows[a_row_id + 0];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += 16 )\n            {\n                int a_col_it = a_col_begin + lane_id_mod_16;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the 16 items.\n                for ( int k = 0 ; k < 16 ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, warp_offset + k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    warp_offset + k );\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // Load the value if possible.\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( uniform_a_col_it < a_col_end )\n                    {\n                        a_value = A_vals[16 * uniform_a_col_it + lane_id_mod_16];\n                    }\n\n                    // Proceed diagonal if needed.\n                    if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                    {\n                        ac_diag = ac_diag + a_value;\n                        uniform_ac_col = -1;\n                    }\n\n                    // Get the id of the column computed by the other half warp.\n                    int other_ac_col = utils::shfl_xor( uniform_ac_col, 16 );\n                    // If both half warps want to write to the same location, we have a conflict!!!\n                    int are_fighting = uniform_ac_col == other_ac_col;\n\n                    // Reduce the two values to a single one.\n                    if ( uniform_ac_col != -1 && are_fighting )\n                    {\n                        a_value = a_value + utils::shfl_xor( a_value, 16 );\n                    }\n\n                    // If the two half warps fight, only one can be the winner... It's the 1st half!!!\n                    int is_winner = !are_fighting || lane_id_div_16 == 0;\n\n                    // Update the value.\n                    if ( uniform_ac_col != -1 && is_winner )\n                    {\n                        Ac_vals[16 * uniform_ac_idx + lane_id_mod_16] = Ac_vals[16 * uniform_ac_idx + lane_id_mod_16] + a_value;\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            ac_diag = ac_diag + utils::shfl_xor( ac_diag, 16 );\n\n            if ( lane_id_div_16 == 0 )\n            {\n                Ac_vals[16 * Ac_diag[r_row_id] + lane_id_mod_16] = ac_diag;\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int N, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool FORCE_DETERMINISM >\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_NxN( const int  R_num_rows, // same as num_aggregates.\n                        const int *R_rows,\n                        const int *R_cols,\n                        const int *A_rows,\n                        const int *A_cols,\n                        const int *A_diag,\n                        const Value_type *A_vals,\n                        const int *aggregates,\n                        const int *Ac_rows,\n                        const int *Ac_cols,\n                        const int *Ac_pos,\n                        const int *Ac_diag,\n                        Value_type *Ac_vals,\n                        const int gmem_size,\n                        int *g_keys,\n                        int *g_idx,\n                        int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp.\n    const int T_WARP = FORCE_DETERMINISM ? 1 : WARP_SIZE / NxN;\n    const int NUM_ITEMS_PER_WARP = T_WARP == 0 ? 1 : T_WARP;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_NxN = lane_id / NxN;\n    const int lane_id_mod_NxN = lane_id % NxN;\n    const int warp_offset = NxN * lane_id_div_NxN;\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( r_col_it += lane_id_div_NxN ; utils::any(r_col_it < r_col_end) ; r_col_it += NUM_ITEMS_PER_WARP )\n        {\n            // Is it an active thread.\n            const bool is_active = r_col_it < r_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = -1;\n\n            if ( is_active )\n            {\n                a_row_id = R_cols[r_col_it];\n            }\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG && is_active )\n            {\n                ac_diag = ac_diag + A_vals[NxN * A_diag[a_row_id] + lane_id_mod_NxN];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = 0, a_col_end = 0;\n\n            if ( is_active )\n            {\n                a_col_begin = A_rows[a_row_id + 0];\n                a_col_end   = A_rows[a_row_id + 1];\n            }\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += NxN )\n            {\n                int a_col_it = a_col_begin + lane_id_mod_NxN;\n                // Is it active.\n                const bool is_active_k = a_col_it < a_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( is_active_k )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the NxN items.\n                #pragma unroll 1\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, warp_offset + k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    warp_offset + k );\n\n                    if ( lane_id_div_NxN >= NUM_ITEMS_PER_WARP )\n                    {\n                        uniform_ac_col = -1;\n                        uniform_ac_idx = -1;\n                    }\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // Load the value if possible.\n                    Value_type a_value(types::util<Value_type>::get_zero());\n\n                    if ( uniform_a_col_it < a_col_end && lane_id_div_NxN < NUM_ITEMS_PER_WARP )\n                    {\n                        a_value = A_vals[NxN * uniform_a_col_it + lane_id_mod_NxN];\n                    }\n\n                    // Update the diagonal if it is a diagonal term.\n                    if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                    {\n                        ac_diag = ac_diag + a_value;\n                        uniform_ac_col = -1;\n                    }\n\n                    // Update the value.\n                    if ( uniform_ac_col != -1 )\n                    {\n                        utils::atomic_add( &Ac_vals[NxN * uniform_ac_idx + lane_id_mod_NxN], a_value );\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            if ( !FORCE_DETERMINISM )\n            {\n                ac_diag = utils::warp_reduce<NxN, utils::Add>( ac_diag );\n            }\n\n            if ( lane_id_div_NxN == 0 )\n            {\n                Ac_vals[NxN * Ac_diag[r_row_id] + lane_id_mod_NxN] = ac_diag;\n            }\n        }\n    }\n}\n\n// when blocksize is larger than warp size\ntemplate< typename Value_type, int N, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, bool HAS_DIAG, bool FORCE_DETERMINISM, int NUM_BLOCK_ITERS_PER_WARP>\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 700\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#else\n__global__ __launch_bounds__( CTA_SIZE, 8 )\n#endif\nvoid fill_A_kernel_NxN_large( const int  R_num_rows, // same as num_aggregates.\n                              const int *R_rows,\n                              const int *R_cols,\n                              const int *A_rows,\n                              const int *A_cols,\n                              const int *A_diag,\n                              const Value_type *A_vals,\n                              const int *aggregates,\n                              const int *Ac_rows,\n                              const int *Ac_cols,\n                              const int *Ac_pos,\n                              const int *Ac_diag,\n                              Value_type *Ac_vals,\n                              const int gmem_size,\n                              int *g_keys,\n                              int *g_idx,\n                              int *wk_work_queue )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Squared N.\n    const int NxN = N * N;\n    // Number of items per warp. Let's be chill here and take 1 per warp for large blocks\n    const int NUM_ITEMS_PER_WARP = 1;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int r_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // My index.\n    Hash_index<int, SMEM_SIZE, WARP_SIZE> index( &g_idx[r_row_id * gmem_size] );\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[r_row_id * gmem_size], gmem_size );\n    // Loop over rows of R.\n    for ( ; r_row_id < R_num_rows ; r_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // The indices of the row.\n        int ac_col_it  = Ac_rows[r_row_id + 0];\n        int ac_col_end = Ac_rows[r_row_id + 1];\n        // Clear the set first.\n        set.clear(true);\n        // Populate the index.\n        set.load_index( ac_col_end - ac_col_it, &Ac_cols[ac_col_it], &Ac_pos[ac_col_it], index, false );\n        // Load the range of the row.\n        int r_col_it  = R_rows[r_row_id + 0];\n        int r_col_end = R_rows[r_row_id + 1];\n        // Diagonal value (each half warp stores a diagonal element).\n        Value_type ac_diag(types::util<Value_type>::get_zero());\n\n        // Iterate over the columns of R.\n        for ( ; utils::any(r_col_it < r_col_end) ; r_col_it += NUM_ITEMS_PER_WARP )\n        {\n            // Columns of R map to rows of A. Each thread of the warp loads its R-col/A-row ID.\n            int a_row_id = R_cols[r_col_it];\n\n            // Update the diagonal if needed.\n            if ( HAS_DIAG )\n            {\n                ac_diag = ac_diag + A_vals[NxN * A_diag[a_row_id] + lane_id];\n            }\n\n            // Load the range of the row of A.\n            int a_col_begin = A_rows[a_row_id + 0];\n            int a_col_end   = A_rows[a_row_id + 1];\n\n            // Iterate over the range of columns of B.\n            for ( ; utils::any(a_col_begin < a_col_end) ; a_col_begin += NxN )\n            {\n                int a_col_it = a_col_begin + lane_id;\n                // Is it active.\n                const bool is_active_k = a_col_it < a_col_end;\n                // Each thread loads a column-ID and an aggregate.\n                int a_col_id = -1, ac_col_id = -1;\n\n                if ( is_active_k )\n                {\n                    a_col_id  = A_cols    [a_col_it];\n                    ac_col_id = aggregates[a_col_id];\n                }\n\n                // Each thread uses the hashed index to find the position associated with the aggregate.\n                int key = ac_col_id;\n\n                if ( HAS_DIAG && ac_col_id == r_row_id )\n                {\n                    key = -1;\n                }\n\n                int ac_idx = ac_col_it + set.find_index( key, index, false );\n\n                // Iterate over the NxN items.\n                for ( int k = 0 ; k < NxN ; ++k )\n                {\n                    int uniform_ac_col = utils::shfl( ac_col_id, k );\n                    int uniform_ac_idx = utils::shfl( ac_idx,    k );\n\n                    // Early loop exit.\n                    if ( utils::all( uniform_ac_col == -1 ) )\n                    {\n                        break;\n                    }\n\n                    // The index of the item.\n                    const int uniform_a_col_it = a_col_begin + k;\n                    // iterate through the block\n#pragma unroll\n\n                    for (int i = 0; i < NUM_BLOCK_ITERS_PER_WARP; i++)\n                    {\n                        // Load the value if possible.\n                        Value_type a_value(types::util<Value_type>::get_zero());\n\n                        if ( uniform_a_col_it < a_col_end && (WARP_SIZE * i + lane_id) < NxN )\n                        {\n                            a_value = A_vals[NxN * uniform_a_col_it + WARP_SIZE * i + lane_id];\n                        }\n\n                        // Update the diagonal if it is a diagonal term.\n                        if ( HAS_DIAG && uniform_ac_col == r_row_id )\n                        {\n                            ac_diag = ac_diag + a_value;\n                            uniform_ac_col = -1;\n                        }\n\n                        // Update the value.\n                        if ( uniform_ac_col != -1 && (WARP_SIZE * i + lane_id) < NxN)\n                        {\n                            utils::atomic_add( &Ac_vals[NxN * uniform_ac_idx + WARP_SIZE * i + lane_id], a_value );\n                        }\n                    }\n                }\n            }\n        }\n\n        if ( HAS_DIAG )\n        {\n            if ( !FORCE_DETERMINISM )\n            {\n                ac_diag = utils::warp_reduce<NxN, utils::Add>( ac_diag );\n            }\n\n            Ac_vals[NxN * Ac_diag[r_row_id] + lane_id] = ac_diag;\n        }\n    }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\ntemplate< int CTA_SIZE, bool HAS_DIAG, bool COUNT_ONLY, typename Workspace >\nstatic\nvoid compute_sparsity_dispatch( Workspace &hash_wk,\n                                const int  R_num_rows,\n                                const int *R_rows,\n                                const int *R_cols,\n                                const int *A_rows,\n                                const int *A_cols,\n                                const int *aggregates,\n                                int *Ac_rows,\n                                int *Ac_cols,\n                                int *Ac_pos )\n{\n    hipDeviceProp_t props = getDeviceProperties();\n    int GRID_SIZE = (props.major >= 7) ? 1024 : 128;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int *h_status;\n    hipError_t cuda_rc = amgx::memory::hipHostMalloc((void **) &h_status, sizeof(int));\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipHostMalloc failed for h_status in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    int *h_work_offset;\n    cuda_rc = amgx::memory::hipHostMalloc((void **) &h_work_offset, sizeof(int));\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipHostMalloc failed for h_work_offset in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    int attempt = 0;\n    bool warning_printed = 0;\n\n    for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n    {\n        // Double the amount of GMEM (if needed).\n        if ( attempt > 0 )\n        {\n            if (!warning_printed)\n            {\n                amgx_printf(\"WARNING: Used settings might result in degraded performance for the MG coarsener for this matrix.\\n\");\n                amgx_printf(\"WARNING: You might want to try different selector or MG algorithm for better performance.\\n\");\n                warning_printed = 1;\n            }\n\n            hash_wk.expand();\n        }\n\n        // Reset the status.\n        int *p_status = h_status;\n        *p_status = 0;\n        hipMemcpyAsync( hash_wk.get_status(), p_status, sizeof(int), hipMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        // Reset the work queue.\n        int *p_work_offset = h_work_offset;\n        *p_work_offset = GRID_SIZE * NUM_WARPS;\n        hipMemcpyAsync( hash_wk.get_work_queue(), p_work_offset, sizeof(int), hipMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        // Launch the kernel.\n        compute_sparsity_kernel<8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, HAS_DIAG, COUNT_ONLY> <<< GRID_SIZE, CTA_SIZE, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n            R_num_rows,\n            R_rows,\n            R_cols,\n            A_rows,\n            A_cols,\n            aggregates,\n            Ac_rows,\n            Ac_cols,\n            Ac_pos,\n            hash_wk.get_gmem_size(),\n            hash_wk.get_keys(),\n            hash_wk.get_work_queue(),\n            hash_wk.get_status() );\n        cudaCheckError();\n        // Read the result from count_non_zeroes.\n        hipMemcpyAsync( p_status, hash_wk.get_status(), sizeof(int), hipMemcpyDeviceToHost, amgx::thrust::global_thread_handle::get_stream() );\n        cudaCheckError();\n        cuda_rc = hipStreamSynchronize(amgx::thrust::global_thread_handle::get_stream());\n        if (cuda_rc != hipSuccess)\n        {\n            FatalError(\"hipStreamSynchronize failed in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n        }\n        done = (*p_status == 0);\n        cudaCheckError();\n    }\n\n    cuda_rc = amgx::memory::hipHostFree(h_status);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipHostFree failed for h_status in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n    cuda_rc = amgx::memory::hipHostFree(h_work_offset);\n    if (cuda_rc != hipSuccess)\n    {\n        FatalError(\"hipHostFree failed for h_work_offset in low_deg_coarse_A_generator\", AMGX_ERR_CUDA_FAILURE);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, bool HAS_DIAG, typename Workspace, typename Value_type >\nstatic\nvoid fill_A_dispatch( Workspace &hash_wk,\n                      const int  block_size,\n                      const int  R_num_rows, // same as num_aggregates.\n                      const int *R_rows,\n                      const int *R_cols,\n                      const int *A_rows,\n                      const int *A_cols,\n                      const int *A_diag,\n                      const Value_type *A_vals,\n                      const int *aggregates,\n                      const int *Ac_rows,\n                      const int *Ac_cols,\n                      const int *Ac_pos,\n                      const int *Ac_diag,\n                      Value_type *Ac_vals,\n                      bool force_determinism )\n{\n    hipDeviceProp_t props = getDeviceProperties();\n    int GRID_SIZE = (props.major >= 7) ? 1024 : 128;\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    hipMemcpyAsync( hash_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice, amgx::thrust::global_thread_handle::get_stream() );\n    cudaCheckError();\n\n    // Launch the kernel.\n    switch ( block_size )\n    {\n        case 1:\n            fill_A_kernel_1x1<Value_type, 8, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                hash_wk.get_vals(),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 2:\n            fill_A_kernel_NxN<Value_type, 2, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 3:\n            fill_A_kernel_NxN<Value_type, 3, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 4:\n            if ( force_determinism )\n                fill_A_kernel_NxN<Value_type, 4, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, true> <<< GRID_SIZE, CTA_SIZE>>>(\n                    R_num_rows,\n                    R_rows,\n                    R_cols,\n                    A_rows,\n                    A_cols,\n                    A_diag,\n                    A_vals,\n                    aggregates,\n                    Ac_rows,\n                    Ac_cols,\n                    Ac_pos,\n                    Ac_diag,\n                    Ac_vals,\n                    hash_wk.get_gmem_size(),\n                    hash_wk.get_keys(),\n                    reinterpret_cast<int *>( hash_wk.get_vals() ),\n                    hash_wk.get_work_queue() );\n            else\n                fill_A_kernel_4x4<Value_type, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG> <<< GRID_SIZE, CTA_SIZE, 0, amgx::thrust::global_thread_handle::get_stream()>>>(\n                    R_num_rows,\n                    R_rows,\n                    R_cols,\n                    A_rows,\n                    A_cols,\n                    A_diag,\n                    A_vals,\n                    aggregates,\n                    Ac_rows,\n                    Ac_cols,\n                    Ac_pos,\n                    Ac_diag,\n                    Ac_vals,\n                    hash_wk.get_gmem_size(),\n                    hash_wk.get_keys(),\n                    reinterpret_cast<int *>( hash_wk.get_vals() ),\n                    hash_wk.get_work_queue() );\n            \n            cudaCheckError();\n            break;\n\n        case 5:\n            fill_A_kernel_NxN<Value_type, 5, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 8:\n            fill_A_kernel_NxN_large<Value_type, 8, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false, 2> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        case 10:\n            fill_A_kernel_NxN_large<Value_type, 10, CTA_SIZE, SMEM_SIZE, 32, HAS_DIAG, false, 4> <<< GRID_SIZE, CTA_SIZE>>>(\n                R_num_rows,\n                R_rows,\n                R_cols,\n                A_rows,\n                A_cols,\n                A_diag,\n                A_vals,\n                aggregates,\n                Ac_rows,\n                Ac_cols,\n                Ac_pos,\n                Ac_diag,\n                Ac_vals,\n                hash_wk.get_gmem_size(),\n                hash_wk.get_keys(),\n                reinterpret_cast<int *>( hash_wk.get_vals() ),\n                hash_wk.get_work_queue() );\n                cudaCheckError();\n            break;\n\n        default:\n            FatalError( \"LOW_DEG not implemented for this block size\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    cudaCheckError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nLowDegCoarseAGenerator<TemplateConfig<AMGX_device, V, M, I> >::computeAOperator( const Matrix_d &A,\n        Matrix_d &Ac,\n        const IVector &aggregates,\n        const IVector &R_row_offsets,\n        const IVector &R_column_indices,\n        const int num_aggregates )\n{\n    if ( A.get_block_dimx() != A.get_block_dimy() )\n    {\n        FatalError( \"LowDegCoarseAGenerator implemented for squared blocks\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE );\n    }\n\n    // The matrix Ac will be modified.\n    Ac.set_initialized(0);\n    // Is the diagonal stored separately??\n    const int diag_prop = A.hasProps(DIAG);\n    // Allocate a workspace for hashing.\n    typedef TemplateConfig<AMGX_device, V, M, I> TConfig_d;\n\n    hipDeviceProp_t props = getDeviceProperties();\n    int grid_size = (props.major >= 7) ? 1024 : 128;\n\n    Hash_Workspace<TConfig_d, int> hash_wk(true, grid_size);\n\n    // Compute row offsets of Ac.\n    Ac.addProps(CSR);\n    Ac.set_num_rows( num_aggregates );\n    Ac.set_num_cols( num_aggregates );\n    Ac.row_offsets.resize( num_aggregates + 1 );\n    // Compute the number of non-zero elements per row of Ac.\n    const int CTA_SIZE  = 128;\n\n    if ( diag_prop )\n        compute_sparsity_dispatch<CTA_SIZE, true, true>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            NULL,\n            NULL );\n    else\n        compute_sparsity_dispatch<CTA_SIZE, false, true>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            NULL,\n            NULL );\n\n    cudaCheckError();\n    // Compute the number of non-zeroes.\n    thrust_wrapper::exclusive_scan<AMGX_device>( Ac.row_offsets.begin(), Ac.row_offsets.end(), Ac.row_offsets.begin() );\n    cudaCheckError();\n    int nonzero_blocks = Ac.row_offsets[num_aggregates];\n\n    if ( diag_prop )\n    {\n        Ac.addProps(DIAG);\n    }\n\n    if ( A.is_matrix_singleGPU() )\n    {\n        Ac.resize( num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx(), !diag_prop );\n    }\n    else\n    {\n        //have 3% more nz for storage\n        Ac.resize_spare( num_aggregates, num_aggregates, nonzero_blocks, A.get_block_dimy(), A.get_block_dimx(), 1.0 );\n\n        if ( diag_prop )\n        {\n            Ac.computeDiagonal();\n        }\n    }\n\n    // Vector to store the positions in the hash table.\n    device_vector_alloc<int> Ac_pos(nonzero_blocks);\n\n    // Compute the sparsity pattern of the rows of Ac.\n    if ( diag_prop )\n        compute_sparsity_dispatch<CTA_SIZE, true, false>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ));\n    else\n        compute_sparsity_dispatch<CTA_SIZE, false, false>(\n            hash_wk,\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ));\n\n    cudaCheckError();\n\n    // Reset values if needed.\n    if ( A.get_block_dimy() != 1 )\n    {\n        thrust_wrapper::fill<AMGX_device>( Ac.values.begin(), Ac.values.end(), types::util<ValueType>::get_zero() );\n        cudaCheckError();\n    }\n\n    // Compute values.\n    if ( diag_prop )\n    {\n        fill_A_dispatch<CTA_SIZE, true>(\n            hash_wk,\n            A.get_block_dimy(),\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ),\n            Ac.diag.raw(),\n            Ac.values.raw(),\n            this->m_force_determinism );\n    }\n    else\n    {\n        fill_A_dispatch<CTA_SIZE, false>(\n            hash_wk,\n            A.get_block_dimy(),\n            num_aggregates,\n            R_row_offsets.raw(),\n            R_column_indices.raw(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.diag.raw(),\n            A.values.raw(),\n            aggregates.raw(),\n            Ac.row_offsets.raw(),\n            Ac.col_indices.raw(),\n            amgx::thrust::raw_pointer_cast( &Ac_pos.front() ),\n            Ac.diag.raw(),\n            Ac.values.raw(),\n            this->m_force_determinism );\n    }\n\n    cudaCheckError();\n\n    // Update the diagonal if needed.\n    if ( Ac.is_matrix_singleGPU() )\n    {\n        Ac.computeDiagonal();\n    }\n\n    cudaCheckError();\n    // Finalize the modification.\n    Ac.set_initialized(1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid\nLowDegCoarseAGenerator<TemplateConfig<AMGX_host, V, M, I> >::computeAOperator( const Matrix_h &h_A,\n        Matrix_h &h_Ac,\n        const IVector &h_aggregates,\n        const IVector &h_R_row_offsets,\n        const IVector &h_R_column_indices,\n        const int num_aggregates )\n{\n    h_Ac.set_initialized(0);\n    IVector rows;\n    IVector inds;\n    typename Matrix_h::MVector vals;\n    typename Matrix_h::MVector diag;\n    int num_nnz = 0;\n    int diag_prop = h_A.hasProps(DIAG);\n\n    for ( int row = 0; row < num_aggregates; row++ )\n    {\n        for ( int col = 0; col < num_aggregates; col++ )\n        {\n            int fill = 0;\n            typename Matrix_h::MVector cur(h_A.get_block_size(), types::util<typename Matrix_h::value_type>::get_zero());\n\n            for ( int rc = h_R_row_offsets[row]; rc < h_R_row_offsets[row + 1]; rc++ )\n            {\n                int j = h_R_column_indices[rc];\n\n                for ( int ac = h_A.row_offsets[j]; ac < h_A.row_offsets[j + 1] + diag_prop; ac++ )\n                {\n                    int k = (ac == h_A.row_offsets[j + 1]) ? j : h_A.col_indices[ac];\n\n                    for ( int q = h_R_row_offsets[col]; q < h_R_row_offsets[col + 1]; q++ )\n                        if ( k == h_R_column_indices[q] )\n                        {\n                            fill = 1;\n                            int val_idx = (ac == h_A.row_offsets[j + 1]) ? h_A.get_num_nz() + j : ac;\n\n                            for ( int v = 0; v < h_A.get_block_size(); v++)\n                            {\n                                cur[v] = cur[v] + h_A.values[val_idx * h_A.get_block_size() + v];\n                            }\n                        }\n                }\n            }\n\n            if ( fill )\n            {\n                if ( row != col || !diag_prop )\n                {\n                    inds.push_back(col);\n                    rows.push_back(row);\n                    num_nnz++;\n\n                    for ( int v = 0; v < h_A.get_block_size(); v++ )\n                    {\n                        vals.push_back(cur[v]);\n                    }\n                }\n                else\n                {\n                    for ( int v = 0; v < h_A.get_block_size(); v++ )\n                    {\n                        diag.push_back(cur[v]);\n                    }\n                }\n            }\n        }\n    }\n\n    rows.push_back(-1);\n\n    // add diagonal to the end\n    if ( diag_prop )\n    {\n        for ( int v = 0; v < num_aggregates * h_A.get_block_size(); v++ )\n        {\n            vals.push_back(diag[v]);\n        }\n    }\n    else\n    {\n        // Add a zero at the end\n        for (int v = 0; v < h_A.get_block_size(); v++)\n        {\n            vals.push_back(types::util<typename Matrix_h::value_type>::get_zero());\n        }\n    }\n\n    h_Ac.resize(num_aggregates, num_aggregates, num_nnz, h_A.get_block_dimx(), h_A.get_block_dimy(), 1);\n    h_Ac.row_indices = rows;\n    h_Ac.col_indices = inds;\n    h_Ac.values = vals;\n    h_Ac.addProps( CSR | ( diag_prop ? DIAG : 0 ) );\n    h_Ac.computeDiagonal();\n    h_Ac.set_initialized(1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define AMGX_CASE_LINE(CASE) template class LowDegCoarseAGenerator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace aggregation\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/aggregation/coarseAgenerators/low_deg_coarse_A_generator.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#define COARSE_CLA_CONSO 0\n\n#include <classical/classical_amg_level.h>\n#include <amg_level.h>\n\n#include <basic_types.h>\n#include <cutil.h>\n#include <multiply.h>\n#include <transpose.h>\n#include <truncate.h>\n#include <blas.h>\n#include <util.h>\n#include <thrust/logical.h>\n#include <thrust/remove.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/extrema.h> // for minmax_element\n\n#include <algorithm>\n#include <assert.h>\n#include <matrix_io.h>\n\n#include <csr_multiply.h>\n\n#include <thrust/logical.h>\n#include <thrust/count.h>\n#include <thrust/sort.h>\n\n#include <distributed/glue.h>\nnamespace amgx\n{\n\nnamespace classical\n{\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(const double &v)\n    {\n        return fabs(v) < 1e-10;\n    }\n};\n\n#define AMGX_CAL_BLOCK_SIZE 256\n\n/* There might be a situation where not all local_to_global_map columns are present in the matrix (because some rows were removed\n   and the columns in these rows are therefore no longer present. This kernel creates the flags array that marks existing columns. */\ntemplate<typename ind_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid flag_existing_local_to_global_columns(ind_t n, ind_t *row_offsets, ind_t *col_indices, ind_t *flags)\n{\n    ind_t i, j, s, e, col;\n\n    //go through the matrix\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        s = row_offsets[i];\n        e = row_offsets[i + 1];\n\n        for (j = s; j < e; j++)\n        {\n            col = col_indices[j];\n\n            //flag columns outside of the square part (which correspond to local_to_global_map)\n            if (col >= n)\n            {\n                flags[col - n] = 1;\n            }\n        }\n    }\n}\n\n/* Renumber the indices based on the prefix-scan/sum of the flags array */\ntemplate<typename ind_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid compress_existing_local_columns(ind_t n, ind_t *row_offsets, ind_t *col_indices, ind_t *flags)\n{\n    ind_t i, j, s, e, col;\n\n    //go through the matrix\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        s = row_offsets[i];\n        e = row_offsets[i + 1];\n\n        for (j = s; j < e; j++)\n        {\n            col = col_indices[j];\n\n            //flag columns outside of the square part (which correspond to local_to_global_map)\n            if (col >= n)\n            {\n                col_indices[j] = n + flags[col - n];\n            }\n        }\n    }\n}\n\n/* compress the local to global columns indices based on the prefix-scan/sum of the flags array */\ntemplate<typename ind_t, typename ind64_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid compress_existing_local_to_global_columns(ind_t n, ind64_t *l2g_in, ind64_t *l2g_out, ind_t *flags)\n{\n    ind_t i;\n\n    //go through the arrays (and copy the updated indices when needed)\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        if (flags[i] != flags[i + 1])\n        {\n            l2g_out[flags[i]] = l2g_in[i];\n        }\n    }\n}\n\n\ntemplate <class T_Config>\nSelector<T_Config> *chooseAggressiveSelector(AMG_Config *m_cfg, std::string std_scope)\n{\n    AMG_Config cfg;\n    std::string cfg_string(\"\");\n    cfg_string += \"default:\";\n    // if necessary, allocate aggressive selector + interpolator\n    bool use_pmis = false, use_hmis = false;\n    // default argument - use the same selector as normal coarsening\n    std::string agg_selector = m_cfg->AMG_Config::template getParameter<std::string>(\"aggressive_selector\", std_scope);\n\n    if (agg_selector == \"DEFAULT\")\n    {\n        std::string std_selector = m_cfg->AMG_Config::template getParameter<std::string>(\"selector\", std_scope);\n\n        if      (std_selector == \"PMIS\") { cfg_string += \"selector=AGGRESSIVE_PMIS\"; use_pmis = true; }\n        else if (std_selector == \"HMIS\") { cfg_string += \"selector=AGGRESSIVE_HMIS\"; use_hmis = true; }\n        else\n        {\n            FatalError(\"Must use either PMIS or HMIS algorithms with aggressive coarsening\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n    }\n    // otherwise use specified selector\n    else if (agg_selector == \"PMIS\") { cfg_string += \"selector=AGGRESSIVE_PMIS\"; use_pmis = true; }\n    else if (agg_selector == \"HMIS\") { cfg_string += \"selector=AGGRESSIVE_HMIS\"; use_hmis = true; }\n    else\n    {\n        FatalError(\"Invalid aggressive coarsener selected\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // check a selector has been selected\n    if (!use_pmis && !use_hmis)\n    {\n        FatalError(\"No aggressive selector chosen\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cfg.parseParameterString(cfg_string.c_str());\n    // now allocate the selector and interpolator\n    return classical::SelectorFactory<T_Config>::allocate(cfg, \"default\" /*std_scope*/);\n}\n\ntemplate <class T_Config>\nInterpolator<T_Config> *chooseAggressiveInterpolator(AMG_Config *m_cfg, std::string std_scope)\n{\n    // temporary config and pointer to main config\n    AMG_Config cfg(*m_cfg);\n    std::string cfg_string(\"default:\");\n    // Set the interpolator\n    cfg_string += \"interpolator=\";\n    cfg_string += m_cfg->AMG_Config::template getParameter<std::string>(\"aggressive_interpolator\", std_scope);\n    cfg.parseParameterString(cfg_string.c_str());\n    // now allocate the selector and interpolator\n    return InterpolatorFactory<T_Config>::allocate(cfg, \"default\");\n}\n\ntemplate <class T_Config>\nClassical_AMG_Level_Base<T_Config>::Classical_AMG_Level_Base(AMG_Class *amg) : AMG_Level<T_Config>(amg)\n{\n    strength = StrengthFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    selector = classical::SelectorFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    interpolator = InterpolatorFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    trunc_factor = amg->m_cfg->AMG_Config::template getParameter<double>(\"interp_truncation_factor\", amg->m_cfg_scope);\n    max_elmts = amg->m_cfg->AMG_Config::template getParameter<int>(\"interp_max_elements\", amg->m_cfg_scope);\n    max_row_sum = amg->m_cfg->AMG_Config::template getParameter<double>(\"max_row_sum\", amg->m_cfg_scope);\n    num_aggressive_levels = amg->m_cfg->AMG_Config::template getParameter<int>(\"aggressive_levels\", amg->m_cfg_scope);\n}\n\ntemplate <class T_Config>\nClassical_AMG_Level_Base<T_Config>::~Classical_AMG_Level_Base()\n{\n    delete strength;\n    delete selector;\n    delete interpolator;\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::transfer_level(AMG_Level<TConfig1> *ref_lvl)\n{\n    Classical_AMG_Level_Base<TConfig1> *ref_cla_lvl = dynamic_cast<Classical_AMG_Level_Base<TConfig1>*>(ref_lvl);\n    this->P.copy(ref_cla_lvl->P);\n    this->R.copy(ref_cla_lvl->R);\n    this->m_s_con.copy(ref_cla_lvl->m_s_con);\n    this->m_scratch.copy(ref_cla_lvl->m_scratch);\n    this->m_cf_map.copy(ref_cla_lvl->m_cf_map);\n}\n\n/****************************************\n * Computes the A, P, and R operators\n ***************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::createCoarseVertices()\n{\n    if (AMG_Level<T_Config>::getLevelIndex() < this->num_aggressive_levels)\n    {\n        if (selector) { delete selector; }\n\n        selector = chooseAggressiveSelector<T_Config>(AMG_Level<T_Config>::amg->m_cfg, AMG_Level<T_Config>::amg->m_cfg_scope);\n    }\n\n    Matrix<T_Config> &RAP = this->getNextLevel( typename Matrix<T_Config>::memory_space( ) )->getA( );\n    Matrix<T_Config> &A = this->getA();\n    int size_all, size_full, nnz_full;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int offset;\n        // Need to get number of 2-ring rows\n        A.getOffsetAndSizeForView(ALL, &offset, &size_all);\n        A.getOffsetAndSizeForView(FULL, &offset, &size_full);\n        A.getNnzForView(FULL, &nnz_full);\n    }\n    else\n    {\n        size_all = A.get_num_rows();\n        size_full = A.get_num_rows();\n        nnz_full = A.get_num_nz();\n    }\n\n    this->m_cf_map.resize(size_all);\n    this->m_s_con.resize(nnz_full);\n    this->m_scratch.resize(size_full);\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_cf_map.begin(), this->m_cf_map.end(), 0);\n    cudaCheckError();\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_s_con.begin(), this->m_s_con.end(), false);\n    cudaCheckError();\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_scratch.begin(), this->m_scratch.end(), 0);\n    cudaCheckError();\n    markCoarseFinePoints();\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::createCoarseMatrices()\n{\n    // allocate aggressive interpolator if needed\n    if (AMG_Level<T_Config>::getLevelIndex() < this->num_aggressive_levels)\n    {\n        if (interpolator) { delete interpolator; }\n\n        interpolator = chooseAggressiveInterpolator<T_Config>(AMG_Level<T_Config>::amg->m_cfg, AMG_Level<T_Config>::amg->m_cfg_scope);\n    }\n\n    Matrix<T_Config> &RAP = this->getNextLevel( typename Matrix<T_Config>::memory_space( ) )->getA( );\n    Matrix<T_Config> &A = this->getA();\n    /* WARNING: exit if D1 interpolator is selected in distributed setting */\n    std::string s(\"\");\n    s += AMG_Level<T_Config>::amg->m_cfg->AMG_Config::template getParameter<std::string>(\"interpolator\", AMG_Level<T_Config>::amg->m_cfg_scope);\n\n    if (A.is_matrix_distributed() && (s.compare(\"D1\") == 0))\n    {\n        FatalError(\"D1 interpolation is not supported in distributed settings\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0) */\n    if (this->isReuseLevel() == false)\n    {\n        computeProlongationOperator();\n    }\n\n    // Compute Restriction operator and coarse matrix Ac\n    if (!this->A->is_matrix_distributed() || this->A->manager->get_num_partitions() == 1)\n    {\n        /* WARNING: see above warning. */\n        if (this->isReuseLevel() == false)\n        {\n            computeRestrictionOperator();\n        }\n\n        computeAOperator();\n    }\n    else\n    {\n        /* WARNING: notice that in this case the computeRestructionOperator() is called\n                    inside computeAOperator_distributed() routine. */\n        computeAOperator_distributed();\n    }\n\n// we also need to renumber columns of P and rows or R correspondingly since we changed RAP halo columns\n// for R we just keep track of renumbering in and exchange proper vectors in restriction\n// for P we actually need to renumber columns for prolongation:\n    if (A.is_matrix_distributed() && this->A->manager->get_num_partitions() > 1)\n    {\n        RAP.set_initialized(0);\n        // Renumber the owned nodes as interior and boundary (renumber rows and columns)\n        // We are passing reuse flag to not create neighbours list from scratch, but rather update based on new halos\n        RAP.manager->renumberMatrixOneRing(this->isReuseLevel());\n        // Renumber the column indices of P and shuffle rows of P\n        RAP.manager->renumber_P_R(this->P, this->R, A);\n        // Create the B2L_maps for RAP\n        RAP.manager->createOneRingHaloRows();\n        RAP.manager->getComms()->set_neighbors(RAP.manager->num_neighbors());\n        RAP.setView(OWNED);\n        RAP.set_initialized(1);\n        // update # of columns in P - this is necessary for correct CSR multiply\n        P.set_initialized(0);\n        int new_num_cols = thrust_wrapper::reduce<TConfig::memSpace>(P.col_indices.begin(), P.col_indices.end(), int(0), amgx::thrust::maximum<int>()) + 1;\n        cudaCheckError();\n        P.set_num_cols(new_num_cols);\n        P.set_initialized(1);\n    }\n\n    RAP.copyAuxData(&A);\n\n    if (!A.is_matrix_singleGPU() && RAP.manager == NULL)\n    {\n        RAP.manager = new DistributedManager<TConfig>();\n    }\n\n    if (this->getA().is_matrix_singleGPU())\n    {\n        this->m_next_level_size = this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_num_rows() * this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_block_dimy();\n    }\n    else\n    {\n        // m_next_level_size is the size that will be used to allocate xc, bc vectors\n        int size, offset;\n        this->getNextLevel(typename Matrix<TConfig>::memory_space())->getA().getOffsetAndSizeForView(FULL, &offset, &size);\n        this->m_next_level_size = size * this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_block_dimy();\n    }\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::markCoarseFinePoints()\n{\n    Matrix<T_Config> &A = this->getA();\n    //allocate necessary memory\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecBool>::Type> BVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecFloat>::Type> FVector;\n    FVector weights;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int size, offset;\n        A.getOffsetAndSizeForView(FULL, &offset, &size);\n        // size should now contain the number of 1-ring rows\n        weights.resize(size);\n    }\n    else\n    {\n        weights.resize(A.get_num_rows());\n    }\n\n    thrust_wrapper::fill<TConfig::memSpace>(weights.begin(), weights.end(), 0.0);\n    cudaCheckError();\n\n    // extend A to include 1st ring nodes\n    // compute strong connections and weights\n    if (!A.is_matrix_singleGPU())\n    {\n        ViewType oldView = A.currentView();\n        A.setView(FULL);\n        strength->computeStrongConnectionsAndWeights(A, this->m_s_con, weights, this->max_row_sum);\n        A.setView(oldView);\n    }\n    else\n    {\n        strength->computeStrongConnectionsAndWeights(A, this->m_s_con, weights, this->max_row_sum);\n    }\n\n    // Exchange the one-ring of the weights\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(weights, weights.tag);\n    }\n\n    //mark coarse and fine points\n    selector->markCoarseFinePoints(A, weights, this->m_s_con, this->m_cf_map, this->m_scratch);\n    // we do resize cf_map to zero later, so we are saving separate copy\n    this->m_cf_map.dirtybit = 1;\n\n    // Do a two ring exchange of cf_map\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo_2ring(this->m_cf_map, m_cf_map.tag);\n    }\n\n    // Modify cf_map array such that coarse points are assigned a local index, while fine points entries are not touched\n    selector->renumberAndCountCoarsePoints(this->m_cf_map, this->m_num_coarse_vertices, A.get_num_rows());\n}\n\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeProlongationOperator()\n{\n    this->Profile.tic(\"computeP\");\n    Matrix<T_Config> &A = this->getA();\n    //allocate necessary memory\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecBool>::Type> BVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecFloat>::Type> FVector;\n    //generate the interpolation matrix\n    interpolator->generateInterpolationMatrix(A, this->m_cf_map, this->m_s_con, this->m_scratch, P);\n    this->m_cf_map.clear();\n    this->m_cf_map.shrink_to_fit();\n    this->m_scratch.clear();\n    this->m_scratch.shrink_to_fit();\n    this->m_s_con.clear();\n    this->m_s_con.shrink_to_fit();\n\n    // truncate based on max # of elements if desired\n    if (this->max_elmts > 0 && P.get_num_rows() > 0)\n    {\n        Truncate<TConfig>::truncateByMaxElements(P, this->max_elmts);\n    }\n\n    if (!P.isLatencyHidingEnabled(*this->amg->m_cfg))\n    {\n        // This will cause bsrmv_with_mask to not do latency hiding\n        P.setInteriorView(OWNED);\n        P.setExteriorView(OWNED);\n    }\n}\n\n/**********************************************\n * computes R=P^T\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeRestrictionOperator()\n{\n    this->Profile.tic(\"computeR\");\n    R.set_initialized(0);\n    P.setView(OWNED);\n    transpose(P, R, P.get_num_rows());\n\n    if (!R.isLatencyHidingEnabled(*this->amg->m_cfg))\n    {\n        // This will cause bsrmv_with_mask_restriction to not do latency hiding\n        R.setInteriorView(OWNED);\n        R.setExteriorView(OWNED);\n    }\n\n    if(P.is_matrix_distributed())\n    {\n        // Setup the number of non-zeros in R using stub DistributedManager\n        R.manager = new DistributedManager<T_Config>();\n        int nrows_owned = P.manager->halo_offsets[0];\n        int nrows_full = P.manager->halo_offsets[P.manager->neighbors.size()];\n        int nz_full = R.row_offsets[nrows_full];\n        int nz_owned = R.row_offsets[nrows_owned];\n        R.manager->setViewSizes(nrows_owned, nz_owned, nrows_owned, nz_owned, nrows_full, nz_full, R.get_num_rows(), R.get_num_nz());\n    }\n\n    R.set_initialized(1);\n    this->Profile.toc(\"computeR\");\n}\n\n/**********************************************\n * computes the Galerkin product: A_c=R*A*P\n **********************************************/\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1()\n{\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_h> RA;\n    RA.addProps(CSR);\n    RA.set_block_dimx(this->getA().get_block_dimx());\n    RA.set_block_dimy(this->getA().get_block_dimy());\n    Matrix<TConfig_h> &RAP = this->getNextLevel( typename Matrix<TConfig_h>::memory_space( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    Matrix<TConfig_h> &Atmp = this->getA();\n    multiplyMM(this->R, this->getA(), RA);\n    multiplyMM(RA, this->P, RAP);\n    RAP.sortByRowAndColumn();\n    RAP.set_initialized(1);\n    this->Profile.toc(\"computeA\");\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1_distributed()\n{\n    FatalError(\"Distributed classical AMG not implemented for host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1()\n{\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_d> &RAP = this->getNextLevel( device_memory( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    this->R.set_initialized( 0 );\n    this->R.addProps( CSR );\n    this->R.set_initialized( 1 );\n    this->P.set_initialized( 0 );\n    this->P.addProps( CSR );\n    this->P.set_initialized( 1 );\n    void *wk = AMG_Level<TConfig_d>::amg->getCsrWorkspace();\n\n    if ( wk == NULL )\n    {\n        wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *(AMG_Level<TConfig_d>::amg->m_cfg), AMG_Level<TConfig_d>::amg->m_cfg_scope );\n        AMG_Level<TConfig_d>::amg->setCsrWorkspace( wk );\n    }\n\n    int spmm_verbose = this->amg->m_cfg->AMG_Config::template getParameter<int>(\"spmm_verbose\", this->amg->m_cfg_scope);\n\n    if ( spmm_verbose )\n    {\n        typedef typename Matrix<TConfig_d>::IVector::const_iterator Iterator;\n        typedef amgx::thrust::pair<Iterator, Iterator> Result;\n        std::ostringstream buffer;\n        buffer << \"SPMM: Level \" << this->getLevelIndex() << std::endl;\n\n        if ( this->getLevelIndex() == 0 )\n        {\n            device_vector_alloc<int> num_nz( this->getA().row_offsets.size() );\n            amgx::thrust::adjacent_difference( this->getA().row_offsets.begin(), this->getA().row_offsets.end(), num_nz.begin() );\n            cudaCheckError();\n            Result result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n            cudaCheckError();\n            int min_size = *result.first;\n            int max_size = *result.second;\n            int sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n            cudaCheckError();\n            double avg_size = double(sum) / this->getA().get_num_rows();\n            buffer << \"SPMM: A: \" << std::endl;\n            buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n            buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n            buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        }\n\n        device_vector_alloc<int> num_nz( this->P.row_offsets.size() );\n        amgx::thrust::adjacent_difference( this->P.row_offsets.begin(), this->P.row_offsets.end(), num_nz.begin() );\n        cudaCheckError();\n        Result result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        int min_size = *result.first;\n        int max_size = *result.second;\n        int sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        double avg_size = double(sum) / this->P.get_num_rows();\n        buffer << \"SPMM: P: \" << std::endl;\n        buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n        buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n        buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        num_nz.resize( this->R.row_offsets.size() );\n        amgx::thrust::adjacent_difference( this->R.row_offsets.begin(), this->R.row_offsets.end(), num_nz.begin() );\n        cudaCheckError();\n        result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        min_size = *result.first;\n        max_size = *result.second;\n        sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        avg_size = double(sum) / this->R.get_num_rows();\n        buffer << \"SPMM: R: \" << std::endl;\n        buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n        buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n        buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        amgx_output( buffer.str().c_str(), static_cast<int>( buffer.str().length() ) );\n    }\n\n    RAP.set_initialized( 0 );\n    CSR_Multiply<TConfig_d>::csr_galerkin_product( this->R, this->getA(), this->P, RAP, NULL, NULL, NULL, NULL, NULL, NULL, wk );\n    RAP.set_initialized( 1 );\n    int spmm_no_sort = this->amg->m_cfg->AMG_Config::template getParameter<int>(\"spmm_no_sort\", this->amg->m_cfg_scope);\n    this->Profile.toc(\"computeA\");\n}\n/**********************************************\n * computes the restriction: rr=R*r\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::restrictResidual(VVector &r, VVector &rr)\n{\n// we need to resize residual vector to make sure it can store halo rows to be sent\n    if (!P.is_matrix_singleGPU())\n    {\n        typedef typename TConfig::MemSpace MemorySpace;\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n#if COARSE_CLA_CONSO\n        int desired_size ;\n\n        if (this->getNextLevel(MemorySpace())->isConsolidationLevel())\n        {\n            desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets_before_glue[Ac.manager->neighbors_before_glue.size()] * rr.get_block_size());\n        }\n        else\n        {\n            desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()] * rr.get_block_size());\n        }\n\n#else\n        int desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()] * rr.get_block_size());\n#endif\n        rr.resize(desired_size);\n    }\n\n#if 1\n    this->Profile.tic(\"restrictRes\");\n\n    // Disable speculative send of rr\n    if (P.is_matrix_singleGPU())\n    {\n        multiply( R, r, rr);\n    }\n    else\n    {\n        multiply_with_mask_restriction( R, r, rr, P);\n    }\n\n#endif\n    // exchange halo residuals & add residual contribution from neighbors\n    rr.dirtybit = 1;\n\n    if (!P.is_matrix_singleGPU())\n    {\n        int desired_size = P.manager->halo_offsets[P.manager->neighbors.size()] * rr.get_block_size();\n\n        if (rr.size() < desired_size)\n        {\n            rr.resize(desired_size);\n        }\n    }\n\n    this->Profile.toc(\"restrictRes\");\n}\n\nstruct is_minus_one\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x == -1;\n    }\n};\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1_distributed()\n{\n    Matrix<TConfig_d> &A = this->getA();\n    Matrix<TConfig_d> &P = this->P;\n    Matrix<TConfig_d> &RAP = this->getNextLevel( device_memory( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    IndexType num_parts = A.manager->get_num_partitions();\n    IndexType num_neighbors = A.manager->num_neighbors();\n    IndexType my_rank = A.manager->global_id();\n    // OWNED includes interior and boundary\n    A.setView(OWNED);\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    int num_owned_fine_pts = A.manager->halo_offsets[0];\n\n    // Initialize RAP.manager\n    if (RAP.manager == NULL)\n    {\n        RAP.manager = new DistributedManager<TConfig_d>();\n    }\n\n    RAP.manager->A = &RAP;\n    RAP.manager->setComms(A.manager->getComms());\n    RAP.manager->set_global_id(my_rank);\n    RAP.manager->set_num_partitions(num_parts);\n    RAP.manager->part_offsets_h = P.manager->part_offsets_h;\n    RAP.manager->part_offsets = P.manager->part_offsets;\n    RAP.manager->set_base_index(RAP.manager->part_offsets_h[my_rank]);\n    RAP.manager->set_index_range(num_owned_coarse_pts);\n    RAP.manager->num_rows_global = RAP.manager->part_offsets_h[num_parts];\n    // --------------------------------------------------------------------\n    // Using the B2L_maps of matrix A, identify the rows of P that need to be sent to neighbors,\n    // so that they can compute A*P\n    // Once rows of P are identified, convert the column indices to global indices, and send them to neighbors\n    //  ---------------------------------------------------------------------------\n    // Copy some information about the manager of P, since we don't want to modify those\n    IVector_h P_neighbors = P.manager->neighbors;\n    I64Vector_h P_halo_ranges_h = P.manager->halo_ranges_h;\n    I64Vector_d P_halo_ranges = P.manager->halo_ranges;\n    RAP.manager->local_to_global_map = P.manager->local_to_global_map;\n    IVector_h P_halo_offsets = P.manager->halo_offsets;\n    // Create a temporary distributed arranger\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->exchange_halo_rows_P(A, this->P, RAP.manager->local_to_global_map, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, RAP.manager->part_offsets_h, RAP.manager->part_offsets, num_owned_coarse_pts, RAP.manager->part_offsets_h[my_rank]);\n    cudaCheckError();\n    // At this point, we can compute RAP_full which contains some rows that will need to be sent to neighbors\n    // i.e. RAP_full = [ RAP_int ]\n    //                 [ RAP_ext ]\n    // RAP is [ RAP_int ] + [RAP_ext_received_from_neighbors]\n    // We can reuse the serial galerkin product since R, A and P use local indices\n    // TODO: latency hiding (i.e. compute RAP_ext, exchange_matrix_halo, then do RAP_int)\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0) */\n    /* We force for matrix P to have only owned rows to be seen for the correct galerkin product computation*/\n    this->P.set_initialized(0);\n    this->P.set_num_rows(num_owned_fine_pts);\n    this->P.addProps( CSR );\n    this->P.set_initialized(1);\n\n    if (this->isReuseLevel() == false)\n    {\n        this->R.set_initialized( 0 );\n        this->R.addProps( CSR );\n        // Take the tranpose of P to get R\n        // Single-GPU transpose, no mpi exchange\n        this->computeRestrictionOperator();\n        this->R.set_initialized( 1 );\n    }\n\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_d> RAP_full;\n    // Initialize the workspace needed for galerkin product\n    void *wk = AMG_Level<TConfig_d>::amg->getCsrWorkspace();\n\n    if ( wk == NULL )\n    {\n        wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *(AMG_Level<TConfig_d>::amg->m_cfg), AMG_Level<TConfig_d>::amg->m_cfg_scope );\n        AMG_Level<TConfig_d>::amg->setCsrWorkspace( wk );\n    }\n\n    // Single-GPU RAP, no mpi exchange\n    RAP_full.set_initialized( 0 );\n    /* WARNING: Since A is reordered (into interior and boundary nodes), while R and P are not reordered,\n                you must unreorder A when performing R*A*P product in ordre to obtain the correct result. */\n    CSR_Multiply<TConfig_d>::csr_galerkin_product( this->R, this->getA(), this->P, RAP_full,\n            /* permutation for rows of R, A and P */       NULL, NULL /*&(this->getA().manager->renumbering)*/,        NULL,\n            /* permutation for cols of R, A and P */       NULL, NULL /*&(this->getA().manager->inverse_renumbering)*/, NULL,\n            wk );\n    RAP_full.set_initialized( 1 );\n    this->Profile.toc(\"computeA\");\n    // ----------------------------------------------------------------------------------------------\n    // Now, send rows of RAP_full requireq by neighbors, received rows from neighbors and create RAP\n    // ----------------------------------------------------------------------------------------------\n    prep->exchange_RAP_ext(RAP, RAP_full, A, this->P, P_halo_offsets, RAP.manager->local_to_global_map, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP.manager->part_offsets_h, RAP.manager->part_offsets, num_owned_coarse_pts, RAP.manager->part_offsets_h[my_rank], wk);\n    // Delete temporary distributed arranger\n    delete prep;\n    /* WARNING: The RAP matrix generated at this point contains extra rows (that correspond to rows of R,\n       that was obtained by locally transposing P). This rows are ignored by setting the # of matrix rows\n       to be smaller, so that they correspond to number of owned coarse nodes. This should be fine, but\n       it leaves holes in the matrix as there might be columns that belong to the extra rows that now do not\n       belong to the smaller matrix with number of owned coarse nodes rows. The same is trued about the\n       local_to_global_map. These two data structures match at this point. However, in the next calls\n       local_to_global (exclusively) will be used to geberate B2L_maps (wihtout going through column indices)\n       which creates extra elements in the B2L that simply do not exist in the new matrices. I strongly suspect\n       this is the reason fore the bug. The below fix simply compresses the matrix so that there are no holes\n       in it, or in the local_2_global_map. */\n    //mark local_to_global_columns that exist in the owned coarse nodes rows.\n    IndexType nrow = RAP.get_num_rows();\n    IndexType ncol = RAP.get_num_cols();\n    IndexType nl2g = ncol - nrow;\n\n    if (nl2g > 0)\n    {\n        IVector   l2g_p(nl2g + 1, 0); //+1 is needed for prefix_sum/exclusive_scan\n        I64Vector l2g_t(nl2g, 0);\n        IndexType nblocks = (nrow + AMGX_CAL_BLOCK_SIZE - 1) / AMGX_CAL_BLOCK_SIZE;\n\n        if (nblocks > 0)\n            flag_existing_local_to_global_columns<int> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nrow, RAP.row_offsets.raw(), RAP.col_indices.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        /*\n        //slow version of the above kernel\n        for(int ii=0; ii<nrow; ii++){\n            int s = RAP.row_offsets[ii];\n            int e = RAP.row_offsets[ii+1];\n            for (int jj=s; jj<e; jj++) {\n                int col = RAP.col_indices[jj];\n                if (col>=nrow){\n                    int kk = col-RAP.get_num_rows();\n                    l2g_p[kk] = 1;\n                }\n            }\n        }\n        cudaCheckError();\n        */\n        //create a pointer map for their location using prefix sum\n        thrust_wrapper::exclusive_scan<AMGX_device>(l2g_p.begin(), l2g_p.end(), l2g_p.begin());\n        int new_nl2g = l2g_p[nl2g];\n\n        //compress the columns using the pointer map\n        if (nblocks > 0)\n            compress_existing_local_columns<int> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nrow, RAP.row_offsets.raw(), RAP.col_indices.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        /*\n        //slow version of the above kernel\n        for(int ii=0; ii<nrow; ii++){\n            int s = RAP.row_offsets[ii];\n            int e = RAP.row_offsets[ii+1];\n            for (int jj=s; jj<e; jj++) {\n                int col = RAP.col_indices[jj];\n                if (col>=nrow){\n                    int kk = col-RAP.get_num_rows();\n                    RAP.col_indices[jj] = nrow+l2g_p[kk];\n                }\n            }\n        }\n        cudaCheckError();\n        */\n        //adjust matrix size (number of columns) accordingly\n        RAP.set_initialized(0);\n        RAP.set_num_cols(nrow + new_nl2g);\n        RAP.set_initialized(1);\n        //compress local_to_global_map using the pointer map\n        nblocks = (nl2g + AMGX_CAL_BLOCK_SIZE - 1) / AMGX_CAL_BLOCK_SIZE;\n\n        if (nblocks > 0)\n            compress_existing_local_to_global_columns<int, int64_t> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nl2g, RAP.manager->local_to_global_map.raw(), l2g_t.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        amgx::thrust::copy(l2g_t.begin(), l2g_t.begin() + new_nl2g, RAP.manager->local_to_global_map.begin());\n        cudaCheckError();\n        /*\n        //slow version of the above kernel (through Thrust)\n        for(int ii=0; ii<(l2g_p.size()-1); ii++){\n            if (l2g_p[ii] != l2g_p[ii+1]){\n                RAP.manager->local_to_global_map[l2g_p[ii]] = RAP.manager->local_to_global_map[ii];\n            }\n        }\n        cudaCheckError();\n        */\n        //adjust local_to_global_map size accordingly\n        RAP.manager->local_to_global_map.resize(new_nl2g);\n    }\n}\n\n/**********************************************\n * prolongates the error: x+=P*e\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::prolongateAndApplyCorrection(VVector &e, VVector &bc, VVector &x, VVector &tmp)\n{\n    this->Profile.tic(\"proCorr\");\n    // Use P.manager to exchange halo of e before doing P\n    // (since P has columns belonging to one of P.neighbors)\n    e.dirtybit = 1;\n\n    if (!P.is_matrix_singleGPU())\n    {\n        // get coarse matrix\n        typedef typename TConfig::MemSpace MemorySpace;\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n#if COARSE_CLA_CONSO\n        int e_size;\n\n        if (this->getNextLevel(MemorySpace())->isConsolidationLevel())\n        {\n            e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets_before_glue[Ac.manager->neighbors_before_glue.size()]) * e.get_block_size();\n        }\n        else\n        {\n            e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()]) * e.get_block_size();\n        }\n\n        if (e.size() < e_size) { e.resize(e_size); }\n\n#else\n        int e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()]) * e.get_block_size();\n        e.resize(e_size);\n#endif\n    }\n\n    if (P.is_matrix_singleGPU())\n    {\n        if (e.size() > 0)\n        {\n            multiply( P, e, tmp);\n        }\n    }\n    else\n    {\n        multiply_with_mask( P, e, tmp);\n    }\n\n    // get owned num rows for fine matrix\n    int owned_size;\n\n    if (this->A->is_matrix_distributed())\n    {\n        int owned_offset;\n        P.manager->getOffsetAndSizeForView(OWNED, &owned_offset, &owned_size);\n    }\n    else\n    {\n        owned_size = x.size();\n    }\n\n    //apply\n    axpby(x, tmp, x, ValueType(1), ValueType(1), 0, owned_size);\n    this->Profile.toc(\"proCorr\");\n    x.dirtybit = 1;\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeAOperator()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeAOperator_1x1();\n    }\n    else\n    {\n        FatalError(\"Classical AMG not implemented for block_size != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeAOperator_distributed()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeAOperator_1x1_distributed();\n    }\n    else\n    {\n        FatalError(\"Classical AMG not implemented for block_size != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::consolidateVector(VVector &x)\n{\n#ifdef AMGX_WITH_MPI\n#if COARSE_CLA_CONSO\n    typedef typename TConfig::MemSpace MemorySpace;\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    MPI_Comm comm, temp_com;\n    comm = Ac.manager->getComms()->get_mpi_comm();\n    temp_com = compute_glue_matrices_communicator(Ac);\n    glue_vector(Ac, comm, x, temp_com);\n#endif\n#endif\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::unconsolidateVector(VVector &x)\n{\n#ifdef AMGX_WITH_MPI\n#if COARSE_CLA_CONSO\n    typedef typename TConfig::MemSpace MemorySpace;\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    MPI_Comm comm, temp_com;\n    comm = Ac.manager->getComms()->get_mpi_comm();\n    temp_com = compute_glue_matrices_communicator(Ac);\n    unglue_vector(Ac, comm, x, temp_com, x);\n#endif\n#endif\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Classical_AMG_Level_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Classical_AMG_Level<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#define COARSE_CLA_CONSO 0\n\n#include <classical/classical_amg_level.h>\n#include <amg_level.h>\n\n#include <basic_types.h>\n#include <cutil.h>\n#include <multiply.h>\n#include <transpose.h>\n#include <truncate.h>\n#include <blas.h>\n#include <util.h>\n#include <thrust/logical.h>\n#include <thrust/remove.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/extrema.h> // for minmax_element\n\n#include <algorithm>\n#include <assert.h>\n#include <matrix_io.h>\n\n#include <csr_multiply.h>\n\n#include <thrust/logical.h>\n#include <thrust/count.h>\n#include <thrust/sort.h>\n\n#include <distributed/glue.h>\nnamespace amgx\n{\n\nnamespace classical\n{\n\nstruct is_zero\n{\n    __host__ __device__\n    bool operator()(const double &v)\n    {\n        return fabs(v) < 1e-10;\n    }\n};\n\n#define AMGX_CAL_BLOCK_SIZE 256\n\n/* There might be a situation where not all local_to_global_map columns are present in the matrix (because some rows were removed\n   and the columns in these rows are therefore no longer present. This kernel creates the flags array that marks existing columns. */\ntemplate<typename ind_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid flag_existing_local_to_global_columns(ind_t n, ind_t *row_offsets, ind_t *col_indices, ind_t *flags)\n{\n    ind_t i, j, s, e, col;\n\n    //go through the matrix\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        s = row_offsets[i];\n        e = row_offsets[i + 1];\n\n        for (j = s; j < e; j++)\n        {\n            col = col_indices[j];\n\n            //flag columns outside of the square part (which correspond to local_to_global_map)\n            if (col >= n)\n            {\n                flags[col - n] = 1;\n            }\n        }\n    }\n}\n\n/* Renumber the indices based on the prefix-scan/sum of the flags array */\ntemplate<typename ind_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid compress_existing_local_columns(ind_t n, ind_t *row_offsets, ind_t *col_indices, ind_t *flags)\n{\n    ind_t i, j, s, e, col;\n\n    //go through the matrix\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        s = row_offsets[i];\n        e = row_offsets[i + 1];\n\n        for (j = s; j < e; j++)\n        {\n            col = col_indices[j];\n\n            //flag columns outside of the square part (which correspond to local_to_global_map)\n            if (col >= n)\n            {\n                col_indices[j] = n + flags[col - n];\n            }\n        }\n    }\n}\n\n/* compress the local to global columns indices based on the prefix-scan/sum of the flags array */\ntemplate<typename ind_t, typename ind64_t>\n__global__ __launch_bounds__( AMGX_CAL_BLOCK_SIZE )\nvoid compress_existing_local_to_global_columns(ind_t n, ind64_t *l2g_in, ind64_t *l2g_out, ind_t *flags)\n{\n    ind_t i;\n\n    //go through the arrays (and copy the updated indices when needed)\n    for (i = threadIdx.x + blockIdx.x * blockDim.x; i < n; i += blockDim.x * gridDim.x)\n    {\n        if (flags[i] != flags[i + 1])\n        {\n            l2g_out[flags[i]] = l2g_in[i];\n        }\n    }\n}\n\n\ntemplate <class T_Config>\nSelector<T_Config> *chooseAggressiveSelector(AMG_Config *m_cfg, std::string std_scope)\n{\n    AMG_Config cfg;\n    std::string cfg_string(\"\");\n    cfg_string += \"default:\";\n    // if necessary, allocate aggressive selector + interpolator\n    bool use_pmis = false, use_hmis = false;\n    // default argument - use the same selector as normal coarsening\n    std::string agg_selector = m_cfg->AMG_Config::template getParameter<std::string>(\"aggressive_selector\", std_scope);\n\n    if (agg_selector == \"DEFAULT\")\n    {\n        std::string std_selector = m_cfg->AMG_Config::template getParameter<std::string>(\"selector\", std_scope);\n\n        if      (std_selector == \"PMIS\") { cfg_string += \"selector=AGGRESSIVE_PMIS\"; use_pmis = true; }\n        else if (std_selector == \"HMIS\") { cfg_string += \"selector=AGGRESSIVE_HMIS\"; use_hmis = true; }\n        else\n        {\n            FatalError(\"Must use either PMIS or HMIS algorithms with aggressive coarsening\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n    }\n    // otherwise use specified selector\n    else if (agg_selector == \"PMIS\") { cfg_string += \"selector=AGGRESSIVE_PMIS\"; use_pmis = true; }\n    else if (agg_selector == \"HMIS\") { cfg_string += \"selector=AGGRESSIVE_HMIS\"; use_hmis = true; }\n    else\n    {\n        FatalError(\"Invalid aggressive coarsener selected\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    // check a selector has been selected\n    if (!use_pmis && !use_hmis)\n    {\n        FatalError(\"No aggressive selector chosen\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    cfg.parseParameterString(cfg_string.c_str());\n    // now allocate the selector and interpolator\n    return classical::SelectorFactory<T_Config>::allocate(cfg, \"default\" /*std_scope*/);\n}\n\ntemplate <class T_Config>\nInterpolator<T_Config> *chooseAggressiveInterpolator(AMG_Config *m_cfg, std::string std_scope)\n{\n    // temporary config and pointer to main config\n    AMG_Config cfg(*m_cfg);\n    std::string cfg_string(\"default:\");\n    // Set the interpolator\n    cfg_string += \"interpolator=\";\n    cfg_string += m_cfg->AMG_Config::template getParameter<std::string>(\"aggressive_interpolator\", std_scope);\n    cfg.parseParameterString(cfg_string.c_str());\n    // now allocate the selector and interpolator\n    return InterpolatorFactory<T_Config>::allocate(cfg, \"default\");\n}\n\ntemplate <class T_Config>\nClassical_AMG_Level_Base<T_Config>::Classical_AMG_Level_Base(AMG_Class *amg) : AMG_Level<T_Config>(amg)\n{\n    strength = StrengthFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    selector = classical::SelectorFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    interpolator = InterpolatorFactory<T_Config>::allocate(*(amg->m_cfg), amg->m_cfg_scope);\n    trunc_factor = amg->m_cfg->AMG_Config::template getParameter<double>(\"interp_truncation_factor\", amg->m_cfg_scope);\n    max_elmts = amg->m_cfg->AMG_Config::template getParameter<int>(\"interp_max_elements\", amg->m_cfg_scope);\n    max_row_sum = amg->m_cfg->AMG_Config::template getParameter<double>(\"max_row_sum\", amg->m_cfg_scope);\n    num_aggressive_levels = amg->m_cfg->AMG_Config::template getParameter<int>(\"aggressive_levels\", amg->m_cfg_scope);\n}\n\ntemplate <class T_Config>\nClassical_AMG_Level_Base<T_Config>::~Classical_AMG_Level_Base()\n{\n    delete strength;\n    delete selector;\n    delete interpolator;\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::transfer_level(AMG_Level<TConfig1> *ref_lvl)\n{\n    Classical_AMG_Level_Base<TConfig1> *ref_cla_lvl = dynamic_cast<Classical_AMG_Level_Base<TConfig1>*>(ref_lvl);\n    this->P.copy(ref_cla_lvl->P);\n    this->R.copy(ref_cla_lvl->R);\n    this->m_s_con.copy(ref_cla_lvl->m_s_con);\n    this->m_scratch.copy(ref_cla_lvl->m_scratch);\n    this->m_cf_map.copy(ref_cla_lvl->m_cf_map);\n}\n\n/****************************************\n * Computes the A, P, and R operators\n ***************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::createCoarseVertices()\n{\n    if (AMG_Level<T_Config>::getLevelIndex() < this->num_aggressive_levels)\n    {\n        if (selector) { delete selector; }\n\n        selector = chooseAggressiveSelector<T_Config>(AMG_Level<T_Config>::amg->m_cfg, AMG_Level<T_Config>::amg->m_cfg_scope);\n    }\n\n    Matrix<T_Config> &RAP = this->getNextLevel( typename Matrix<T_Config>::memory_space( ) )->getA( );\n    Matrix<T_Config> &A = this->getA();\n    int size_all, size_full, nnz_full;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int offset;\n        // Need to get number of 2-ring rows\n        A.getOffsetAndSizeForView(ALL, &offset, &size_all);\n        A.getOffsetAndSizeForView(FULL, &offset, &size_full);\n        A.getNnzForView(FULL, &nnz_full);\n    }\n    else\n    {\n        size_all = A.get_num_rows();\n        size_full = A.get_num_rows();\n        nnz_full = A.get_num_nz();\n    }\n\n    this->m_cf_map.resize(size_all);\n    this->m_s_con.resize(nnz_full);\n    this->m_scratch.resize(size_full);\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_cf_map.begin(), this->m_cf_map.end(), 0);\n    cudaCheckError();\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_s_con.begin(), this->m_s_con.end(), false);\n    cudaCheckError();\n    thrust_wrapper::fill<T_Config::memSpace>(this->m_scratch.begin(), this->m_scratch.end(), 0);\n    cudaCheckError();\n    markCoarseFinePoints();\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::createCoarseMatrices()\n{\n    // allocate aggressive interpolator if needed\n    if (AMG_Level<T_Config>::getLevelIndex() < this->num_aggressive_levels)\n    {\n        if (interpolator) { delete interpolator; }\n\n        interpolator = chooseAggressiveInterpolator<T_Config>(AMG_Level<T_Config>::amg->m_cfg, AMG_Level<T_Config>::amg->m_cfg_scope);\n    }\n\n    Matrix<T_Config> &RAP = this->getNextLevel( typename Matrix<T_Config>::memory_space( ) )->getA( );\n    Matrix<T_Config> &A = this->getA();\n    /* WARNING: exit if D1 interpolator is selected in distributed setting */\n    std::string s(\"\");\n    s += AMG_Level<T_Config>::amg->m_cfg->AMG_Config::template getParameter<std::string>(\"interpolator\", AMG_Level<T_Config>::amg->m_cfg_scope);\n\n    if (A.is_matrix_distributed() && (s.compare(\"D1\") == 0))\n    {\n        FatalError(\"D1 interpolation is not supported in distributed settings\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0) */\n    if (this->isReuseLevel() == false)\n    {\n        computeProlongationOperator();\n    }\n\n    // Compute Restriction operator and coarse matrix Ac\n    if (!this->A->is_matrix_distributed() || this->A->manager->get_num_partitions() == 1)\n    {\n        /* WARNING: see above warning. */\n        if (this->isReuseLevel() == false)\n        {\n            computeRestrictionOperator();\n        }\n\n        computeAOperator();\n    }\n    else\n    {\n        /* WARNING: notice that in this case the computeRestructionOperator() is called\n                    inside computeAOperator_distributed() routine. */\n        computeAOperator_distributed();\n    }\n\n// we also need to renumber columns of P and rows or R correspondingly since we changed RAP halo columns\n// for R we just keep track of renumbering in and exchange proper vectors in restriction\n// for P we actually need to renumber columns for prolongation:\n    if (A.is_matrix_distributed() && this->A->manager->get_num_partitions() > 1)\n    {\n        RAP.set_initialized(0);\n        // Renumber the owned nodes as interior and boundary (renumber rows and columns)\n        // We are passing reuse flag to not create neighbours list from scratch, but rather update based on new halos\n        RAP.manager->renumberMatrixOneRing(this->isReuseLevel());\n        // Renumber the column indices of P and shuffle rows of P\n        RAP.manager->renumber_P_R(this->P, this->R, A);\n        // Create the B2L_maps for RAP\n        RAP.manager->createOneRingHaloRows();\n        RAP.manager->getComms()->set_neighbors(RAP.manager->num_neighbors());\n        RAP.setView(OWNED);\n        RAP.set_initialized(1);\n        // update # of columns in P - this is necessary for correct CSR multiply\n        P.set_initialized(0);\n        int new_num_cols = thrust_wrapper::reduce<TConfig::memSpace>(P.col_indices.begin(), P.col_indices.end(), int(0), amgx::thrust::maximum<int>()) + 1;\n        cudaCheckError();\n        P.set_num_cols(new_num_cols);\n        P.set_initialized(1);\n    }\n\n    RAP.copyAuxData(&A);\n\n    if (!A.is_matrix_singleGPU() && RAP.manager == NULL)\n    {\n        RAP.manager = new DistributedManager<TConfig>();\n    }\n\n    if (this->getA().is_matrix_singleGPU())\n    {\n        this->m_next_level_size = this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_num_rows() * this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_block_dimy();\n    }\n    else\n    {\n        // m_next_level_size is the size that will be used to allocate xc, bc vectors\n        int size, offset;\n        this->getNextLevel(typename Matrix<TConfig>::memory_space())->getA().getOffsetAndSizeForView(FULL, &offset, &size);\n        this->m_next_level_size = size * this->getNextLevel(typename Matrix<TConfig>::memory_space() )->getA().get_block_dimy();\n    }\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::markCoarseFinePoints()\n{\n    Matrix<T_Config> &A = this->getA();\n    //allocate necessary memory\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecBool>::Type> BVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecFloat>::Type> FVector;\n    FVector weights;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int size, offset;\n        A.getOffsetAndSizeForView(FULL, &offset, &size);\n        // size should now contain the number of 1-ring rows\n        weights.resize(size);\n    }\n    else\n    {\n        weights.resize(A.get_num_rows());\n    }\n\n    thrust_wrapper::fill<TConfig::memSpace>(weights.begin(), weights.end(), 0.0);\n    cudaCheckError();\n\n    // extend A to include 1st ring nodes\n    // compute strong connections and weights\n    if (!A.is_matrix_singleGPU())\n    {\n        ViewType oldView = A.currentView();\n        A.setView(FULL);\n        strength->computeStrongConnectionsAndWeights(A, this->m_s_con, weights, this->max_row_sum);\n        A.setView(oldView);\n    }\n    else\n    {\n        strength->computeStrongConnectionsAndWeights(A, this->m_s_con, weights, this->max_row_sum);\n    }\n\n    // Exchange the one-ring of the weights\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(weights, weights.tag);\n    }\n\n    //mark coarse and fine points\n    selector->markCoarseFinePoints(A, weights, this->m_s_con, this->m_cf_map, this->m_scratch);\n    // we do resize cf_map to zero later, so we are saving separate copy\n    this->m_cf_map.dirtybit = 1;\n\n    // Do a two ring exchange of cf_map\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo_2ring(this->m_cf_map, m_cf_map.tag);\n    }\n\n    // Modify cf_map array such that coarse points are assigned a local index, while fine points entries are not touched\n    selector->renumberAndCountCoarsePoints(this->m_cf_map, this->m_num_coarse_vertices, A.get_num_rows());\n}\n\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeProlongationOperator()\n{\n    this->Profile.tic(\"computeP\");\n    Matrix<T_Config> &A = this->getA();\n    //allocate necessary memory\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecInt>::Type> IVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecBool>::Type> BVector;\n    typedef Vector<typename TConfig::template setVecPrec<AMGX_vecFloat>::Type> FVector;\n    //generate the interpolation matrix\n    interpolator->generateInterpolationMatrix(A, this->m_cf_map, this->m_s_con, this->m_scratch, P);\n    this->m_cf_map.clear();\n    this->m_cf_map.shrink_to_fit();\n    this->m_scratch.clear();\n    this->m_scratch.shrink_to_fit();\n    this->m_s_con.clear();\n    this->m_s_con.shrink_to_fit();\n\n    // truncate based on max # of elements if desired\n    if (this->max_elmts > 0 && P.get_num_rows() > 0)\n    {\n        Truncate<TConfig>::truncateByMaxElements(P, this->max_elmts);\n    }\n\n    if (!P.isLatencyHidingEnabled(*this->amg->m_cfg))\n    {\n        // This will cause bsrmv_with_mask to not do latency hiding\n        P.setInteriorView(OWNED);\n        P.setExteriorView(OWNED);\n    }\n}\n\n/**********************************************\n * computes R=P^T\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeRestrictionOperator()\n{\n    this->Profile.tic(\"computeR\");\n    R.set_initialized(0);\n    P.setView(OWNED);\n    transpose(P, R, P.get_num_rows());\n\n    if (!R.isLatencyHidingEnabled(*this->amg->m_cfg))\n    {\n        // This will cause bsrmv_with_mask_restriction to not do latency hiding\n        R.setInteriorView(OWNED);\n        R.setExteriorView(OWNED);\n    }\n\n    if(P.is_matrix_distributed())\n    {\n        // Setup the number of non-zeros in R using stub DistributedManager\n        R.manager = new DistributedManager<T_Config>();\n        int nrows_owned = P.manager->halo_offsets[0];\n        int nrows_full = P.manager->halo_offsets[P.manager->neighbors.size()];\n        int nz_full = R.row_offsets[nrows_full];\n        int nz_owned = R.row_offsets[nrows_owned];\n        R.manager->setViewSizes(nrows_owned, nz_owned, nrows_owned, nz_owned, nrows_full, nz_full, R.get_num_rows(), R.get_num_nz());\n    }\n\n    R.set_initialized(1);\n    this->Profile.toc(\"computeR\");\n}\n\n/**********************************************\n * computes the Galerkin product: A_c=R*A*P\n **********************************************/\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1()\n{\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_h> RA;\n    RA.addProps(CSR);\n    RA.set_block_dimx(this->getA().get_block_dimx());\n    RA.set_block_dimy(this->getA().get_block_dimy());\n    Matrix<TConfig_h> &RAP = this->getNextLevel( typename Matrix<TConfig_h>::memory_space( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    Matrix<TConfig_h> &Atmp = this->getA();\n    multiplyMM(this->R, this->getA(), RA);\n    multiplyMM(RA, this->P, RAP);\n    RAP.sortByRowAndColumn();\n    RAP.set_initialized(1);\n    this->Profile.toc(\"computeA\");\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1_distributed()\n{\n    FatalError(\"Distributed classical AMG not implemented for host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1()\n{\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_d> &RAP = this->getNextLevel( device_memory( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    this->R.set_initialized( 0 );\n    this->R.addProps( CSR );\n    this->R.set_initialized( 1 );\n    this->P.set_initialized( 0 );\n    this->P.addProps( CSR );\n    this->P.set_initialized( 1 );\n    void *wk = AMG_Level<TConfig_d>::amg->getCsrWorkspace();\n\n    if ( wk == NULL )\n    {\n        wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *(AMG_Level<TConfig_d>::amg->m_cfg), AMG_Level<TConfig_d>::amg->m_cfg_scope );\n        AMG_Level<TConfig_d>::amg->setCsrWorkspace( wk );\n    }\n\n    int spmm_verbose = this->amg->m_cfg->AMG_Config::template getParameter<int>(\"spmm_verbose\", this->amg->m_cfg_scope);\n\n    if ( spmm_verbose )\n    {\n        typedef typename Matrix<TConfig_d>::IVector::const_iterator Iterator;\n        typedef amgx::thrust::pair<Iterator, Iterator> Result;\n        std::ostringstream buffer;\n        buffer << \"SPMM: Level \" << this->getLevelIndex() << std::endl;\n\n        if ( this->getLevelIndex() == 0 )\n        {\n            device_vector_alloc<int> num_nz( this->getA().row_offsets.size() );\n            amgx::thrust::adjacent_difference( this->getA().row_offsets.begin(), this->getA().row_offsets.end(), num_nz.begin() );\n            cudaCheckError();\n            Result result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n            cudaCheckError();\n            int min_size = *result.first;\n            int max_size = *result.second;\n            int sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n            cudaCheckError();\n            double avg_size = double(sum) / this->getA().get_num_rows();\n            buffer << \"SPMM: A: \" << std::endl;\n            buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n            buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n            buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        }\n\n        device_vector_alloc<int> num_nz( this->P.row_offsets.size() );\n        amgx::thrust::adjacent_difference( this->P.row_offsets.begin(), this->P.row_offsets.end(), num_nz.begin() );\n        cudaCheckError();\n        Result result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        int min_size = *result.first;\n        int max_size = *result.second;\n        int sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        double avg_size = double(sum) / this->P.get_num_rows();\n        buffer << \"SPMM: P: \" << std::endl;\n        buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n        buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n        buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        num_nz.resize( this->R.row_offsets.size() );\n        amgx::thrust::adjacent_difference( this->R.row_offsets.begin(), this->R.row_offsets.end(), num_nz.begin() );\n        cudaCheckError();\n        result = amgx::thrust::minmax_element( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        min_size = *result.first;\n        max_size = *result.second;\n        sum = thrust_wrapper::reduce<AMGX_device>( num_nz.begin() + 1, num_nz.end() );\n        cudaCheckError();\n        avg_size = double(sum) / this->R.get_num_rows();\n        buffer << \"SPMM: R: \" << std::endl;\n        buffer << \"SPMM: Matrix avg row size: \" << avg_size << std::endl;\n        buffer << \"SPMM: Matrix min row size: \" << min_size << std::endl;\n        buffer << \"SPMM: Matrix max row size: \" << max_size << std::endl;\n        amgx_output( buffer.str().c_str(), static_cast<int>( buffer.str().length() ) );\n    }\n\n    RAP.set_initialized( 0 );\n    CSR_Multiply<TConfig_d>::csr_galerkin_product( this->R, this->getA(), this->P, RAP, NULL, NULL, NULL, NULL, NULL, NULL, wk );\n    RAP.set_initialized( 1 );\n    int spmm_no_sort = this->amg->m_cfg->AMG_Config::template getParameter<int>(\"spmm_no_sort\", this->amg->m_cfg_scope);\n    this->Profile.toc(\"computeA\");\n}\n/**********************************************\n * computes the restriction: rr=R*r\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::restrictResidual(VVector &r, VVector &rr)\n{\n// we need to resize residual vector to make sure it can store halo rows to be sent\n    if (!P.is_matrix_singleGPU())\n    {\n        typedef typename TConfig::MemSpace MemorySpace;\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n#if COARSE_CLA_CONSO\n        int desired_size ;\n\n        if (this->getNextLevel(MemorySpace())->isConsolidationLevel())\n        {\n            desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets_before_glue[Ac.manager->neighbors_before_glue.size()] * rr.get_block_size());\n        }\n        else\n        {\n            desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()] * rr.get_block_size());\n        }\n\n#else\n        int desired_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()] * rr.get_block_size());\n#endif\n        rr.resize(desired_size);\n    }\n\n#if 1\n    this->Profile.tic(\"restrictRes\");\n\n    // Disable speculative send of rr\n    if (P.is_matrix_singleGPU())\n    {\n        multiply( R, r, rr);\n    }\n    else\n    {\n        multiply_with_mask_restriction( R, r, rr, P);\n    }\n\n#endif\n    // exchange halo residuals & add residual contribution from neighbors\n    rr.dirtybit = 1;\n\n    if (!P.is_matrix_singleGPU())\n    {\n        int desired_size = P.manager->halo_offsets[P.manager->neighbors.size()] * rr.get_block_size();\n\n        if (rr.size() < desired_size)\n        {\n            rr.resize(desired_size);\n        }\n    }\n\n    this->Profile.toc(\"restrictRes\");\n}\n\nstruct is_minus_one\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x == -1;\n    }\n};\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Classical_AMG_Level<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::computeAOperator_1x1_distributed()\n{\n    Matrix<TConfig_d> &A = this->getA();\n    Matrix<TConfig_d> &P = this->P;\n    Matrix<TConfig_d> &RAP = this->getNextLevel( device_memory( ) )->getA( );\n    RAP.addProps(CSR);\n    RAP.set_block_dimx(this->getA().get_block_dimx());\n    RAP.set_block_dimy(this->getA().get_block_dimy());\n    IndexType num_parts = A.manager->get_num_partitions();\n    IndexType num_neighbors = A.manager->num_neighbors();\n    IndexType my_rank = A.manager->global_id();\n    // OWNED includes interior and boundary\n    A.setView(OWNED);\n    int num_owned_coarse_pts = P.manager->halo_offsets[0];\n    int num_owned_fine_pts = A.manager->halo_offsets[0];\n\n    // Initialize RAP.manager\n    if (RAP.manager == NULL)\n    {\n        RAP.manager = new DistributedManager<TConfig_d>();\n    }\n\n    RAP.manager->A = &RAP;\n    RAP.manager->setComms(A.manager->getComms());\n    RAP.manager->set_global_id(my_rank);\n    RAP.manager->set_num_partitions(num_parts);\n    RAP.manager->part_offsets_h = P.manager->part_offsets_h;\n    RAP.manager->part_offsets = P.manager->part_offsets;\n    RAP.manager->set_base_index(RAP.manager->part_offsets_h[my_rank]);\n    RAP.manager->set_index_range(num_owned_coarse_pts);\n    RAP.manager->num_rows_global = RAP.manager->part_offsets_h[num_parts];\n    // --------------------------------------------------------------------\n    // Using the B2L_maps of matrix A, identify the rows of P that need to be sent to neighbors,\n    // so that they can compute A*P\n    // Once rows of P are identified, convert the column indices to global indices, and send them to neighbors\n    //  ---------------------------------------------------------------------------\n    // Copy some information about the manager of P, since we don't want to modify those\n    IVector_h P_neighbors = P.manager->neighbors;\n    I64Vector_h P_halo_ranges_h = P.manager->halo_ranges_h;\n    I64Vector_d P_halo_ranges = P.manager->halo_ranges;\n    RAP.manager->local_to_global_map = P.manager->local_to_global_map;\n    IVector_h P_halo_offsets = P.manager->halo_offsets;\n    // Create a temporary distributed arranger\n    DistributedArranger<TConfig_d> *prep = new DistributedArranger<TConfig_d>;\n    prep->exchange_halo_rows_P(A, this->P, RAP.manager->local_to_global_map, P_neighbors, P_halo_ranges_h, P_halo_ranges, P_halo_offsets, RAP.manager->part_offsets_h, RAP.manager->part_offsets, num_owned_coarse_pts, RAP.manager->part_offsets_h[my_rank]);\n    cudaCheckError();\n    // At this point, we can compute RAP_full which contains some rows that will need to be sent to neighbors\n    // i.e. RAP_full = [ RAP_int ]\n    //                 [ RAP_ext ]\n    // RAP is [ RAP_int ] + [RAP_ext_received_from_neighbors]\n    // We can reuse the serial galerkin product since R, A and P use local indices\n    // TODO: latency hiding (i.e. compute RAP_ext, exchange_matrix_halo, then do RAP_int)\n    /* WARNING: do not recompute prolongation (P) and restriction (R) when you\n                are reusing the level structure (structure_reuse_levels > 0) */\n    /* We force for matrix P to have only owned rows to be seen for the correct galerkin product computation*/\n    this->P.set_initialized(0);\n    this->P.set_num_rows(num_owned_fine_pts);\n    this->P.addProps( CSR );\n    this->P.set_initialized(1);\n\n    if (this->isReuseLevel() == false)\n    {\n        this->R.set_initialized( 0 );\n        this->R.addProps( CSR );\n        // Take the tranpose of P to get R\n        // Single-GPU transpose, no mpi exchange\n        this->computeRestrictionOperator();\n        this->R.set_initialized( 1 );\n    }\n\n    this->Profile.tic(\"computeA\");\n    Matrix<TConfig_d> RAP_full;\n    // Initialize the workspace needed for galerkin product\n    void *wk = AMG_Level<TConfig_d>::amg->getCsrWorkspace();\n\n    if ( wk == NULL )\n    {\n        wk = CSR_Multiply<TConfig_d>::csr_workspace_create( *(AMG_Level<TConfig_d>::amg->m_cfg), AMG_Level<TConfig_d>::amg->m_cfg_scope );\n        AMG_Level<TConfig_d>::amg->setCsrWorkspace( wk );\n    }\n\n    // Single-GPU RAP, no mpi exchange\n    RAP_full.set_initialized( 0 );\n    /* WARNING: Since A is reordered (into interior and boundary nodes), while R and P are not reordered,\n                you must unreorder A when performing R*A*P product in ordre to obtain the correct result. */\n    CSR_Multiply<TConfig_d>::csr_galerkin_product( this->R, this->getA(), this->P, RAP_full,\n            /* permutation for rows of R, A and P */       NULL, NULL /*&(this->getA().manager->renumbering)*/,        NULL,\n            /* permutation for cols of R, A and P */       NULL, NULL /*&(this->getA().manager->inverse_renumbering)*/, NULL,\n            wk );\n    RAP_full.set_initialized( 1 );\n    this->Profile.toc(\"computeA\");\n    // ----------------------------------------------------------------------------------------------\n    // Now, send rows of RAP_full requireq by neighbors, received rows from neighbors and create RAP\n    // ----------------------------------------------------------------------------------------------\n    prep->exchange_RAP_ext(RAP, RAP_full, A, this->P, P_halo_offsets, RAP.manager->local_to_global_map, P_neighbors, P_halo_ranges_h, P_halo_ranges, RAP.manager->part_offsets_h, RAP.manager->part_offsets, num_owned_coarse_pts, RAP.manager->part_offsets_h[my_rank], wk);\n    // Delete temporary distributed arranger\n    delete prep;\n    /* WARNING: The RAP matrix generated at this point contains extra rows (that correspond to rows of R,\n       that was obtained by locally transposing P). This rows are ignored by setting the # of matrix rows\n       to be smaller, so that they correspond to number of owned coarse nodes. This should be fine, but\n       it leaves holes in the matrix as there might be columns that belong to the extra rows that now do not\n       belong to the smaller matrix with number of owned coarse nodes rows. The same is trued about the\n       local_to_global_map. These two data structures match at this point. However, in the next calls\n       local_to_global (exclusively) will be used to geberate B2L_maps (wihtout going through column indices)\n       which creates extra elements in the B2L that simply do not exist in the new matrices. I strongly suspect\n       this is the reason fore the bug. The below fix simply compresses the matrix so that there are no holes\n       in it, or in the local_2_global_map. */\n    //mark local_to_global_columns that exist in the owned coarse nodes rows.\n    IndexType nrow = RAP.get_num_rows();\n    IndexType ncol = RAP.get_num_cols();\n    IndexType nl2g = ncol - nrow;\n\n    if (nl2g > 0)\n    {\n        IVector   l2g_p(nl2g + 1, 0); //+1 is needed for prefix_sum/exclusive_scan\n        I64Vector l2g_t(nl2g, 0);\n        IndexType nblocks = (nrow + AMGX_CAL_BLOCK_SIZE - 1) / AMGX_CAL_BLOCK_SIZE;\n\n        if (nblocks > 0)\n            flag_existing_local_to_global_columns<int> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nrow, RAP.row_offsets.raw(), RAP.col_indices.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        /*\n        //slow version of the above kernel\n        for(int ii=0; ii<nrow; ii++){\n            int s = RAP.row_offsets[ii];\n            int e = RAP.row_offsets[ii+1];\n            for (int jj=s; jj<e; jj++) {\n                int col = RAP.col_indices[jj];\n                if (col>=nrow){\n                    int kk = col-RAP.get_num_rows();\n                    l2g_p[kk] = 1;\n                }\n            }\n        }\n        cudaCheckError();\n        */\n        //create a pointer map for their location using prefix sum\n        thrust_wrapper::exclusive_scan<AMGX_device>(l2g_p.begin(), l2g_p.end(), l2g_p.begin());\n        int new_nl2g = l2g_p[nl2g];\n\n        //compress the columns using the pointer map\n        if (nblocks > 0)\n            compress_existing_local_columns<int> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nrow, RAP.row_offsets.raw(), RAP.col_indices.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        /*\n        //slow version of the above kernel\n        for(int ii=0; ii<nrow; ii++){\n            int s = RAP.row_offsets[ii];\n            int e = RAP.row_offsets[ii+1];\n            for (int jj=s; jj<e; jj++) {\n                int col = RAP.col_indices[jj];\n                if (col>=nrow){\n                    int kk = col-RAP.get_num_rows();\n                    RAP.col_indices[jj] = nrow+l2g_p[kk];\n                }\n            }\n        }\n        cudaCheckError();\n        */\n        //adjust matrix size (number of columns) accordingly\n        RAP.set_initialized(0);\n        RAP.set_num_cols(nrow + new_nl2g);\n        RAP.set_initialized(1);\n        //compress local_to_global_map using the pointer map\n        nblocks = (nl2g + AMGX_CAL_BLOCK_SIZE - 1) / AMGX_CAL_BLOCK_SIZE;\n\n        if (nblocks > 0)\n            compress_existing_local_to_global_columns<int, int64_t> <<< nblocks, AMGX_CAL_BLOCK_SIZE>>>\n            (nl2g, RAP.manager->local_to_global_map.raw(), l2g_t.raw(), l2g_p.raw());\n\n        cudaCheckError();\n        amgx::thrust::copy(l2g_t.begin(), l2g_t.begin() + new_nl2g, RAP.manager->local_to_global_map.begin());\n        cudaCheckError();\n        /*\n        //slow version of the above kernel (through Thrust)\n        for(int ii=0; ii<(l2g_p.size()-1); ii++){\n            if (l2g_p[ii] != l2g_p[ii+1]){\n                RAP.manager->local_to_global_map[l2g_p[ii]] = RAP.manager->local_to_global_map[ii];\n            }\n        }\n        cudaCheckError();\n        */\n        //adjust local_to_global_map size accordingly\n        RAP.manager->local_to_global_map.resize(new_nl2g);\n    }\n}\n\n/**********************************************\n * prolongates the error: x+=P*e\n **********************************************/\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::prolongateAndApplyCorrection(VVector &e, VVector &bc, VVector &x, VVector &tmp)\n{\n    this->Profile.tic(\"proCorr\");\n    // Use P.manager to exchange halo of e before doing P\n    // (since P has columns belonging to one of P.neighbors)\n    e.dirtybit = 1;\n\n    if (!P.is_matrix_singleGPU())\n    {\n        // get coarse matrix\n        typedef typename TConfig::MemSpace MemorySpace;\n        Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n#if COARSE_CLA_CONSO\n        int e_size;\n\n        if (this->getNextLevel(MemorySpace())->isConsolidationLevel())\n        {\n            e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets_before_glue[Ac.manager->neighbors_before_glue.size()]) * e.get_block_size();\n        }\n        else\n        {\n            e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()]) * e.get_block_size();\n        }\n\n        if (e.size() < e_size) { e.resize(e_size); }\n\n#else\n        int e_size = std::max(P.manager->halo_offsets[P.manager->neighbors.size()], Ac.manager->halo_offsets[Ac.manager->neighbors.size()]) * e.get_block_size();\n        e.resize(e_size);\n#endif\n    }\n\n    if (P.is_matrix_singleGPU())\n    {\n        if (e.size() > 0)\n        {\n            multiply( P, e, tmp);\n        }\n    }\n    else\n    {\n        multiply_with_mask( P, e, tmp);\n    }\n\n    // get owned num rows for fine matrix\n    int owned_size;\n\n    if (this->A->is_matrix_distributed())\n    {\n        int owned_offset;\n        P.manager->getOffsetAndSizeForView(OWNED, &owned_offset, &owned_size);\n    }\n    else\n    {\n        owned_size = x.size();\n    }\n\n    //apply\n    axpby(x, tmp, x, ValueType(1), ValueType(1), 0, owned_size);\n    this->Profile.toc(\"proCorr\");\n    x.dirtybit = 1;\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeAOperator()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeAOperator_1x1();\n    }\n    else\n    {\n        FatalError(\"Classical AMG not implemented for block_size != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::computeAOperator_distributed()\n{\n    if (this->A->get_block_size() == 1)\n    {\n        computeAOperator_1x1_distributed();\n    }\n    else\n    {\n        FatalError(\"Classical AMG not implemented for block_size != 1\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n}\n\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::consolidateVector(VVector &x)\n{\n#ifdef AMGX_WITH_MPI\n#if COARSE_CLA_CONSO\n    typedef typename TConfig::MemSpace MemorySpace;\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    MPI_Comm comm, temp_com;\n    comm = Ac.manager->getComms()->get_mpi_comm();\n    temp_com = compute_glue_matrices_communicator(Ac);\n    glue_vector(Ac, comm, x, temp_com);\n#endif\n#endif\n}\n\ntemplate <class T_Config>\nvoid Classical_AMG_Level_Base<T_Config>::unconsolidateVector(VVector &x)\n{\n#ifdef AMGX_WITH_MPI\n#if COARSE_CLA_CONSO\n    typedef typename TConfig::MemSpace MemorySpace;\n    Matrix<TConfig> &A = this->getA();\n    Matrix<TConfig> &Ac = this->getNextLevel( MemorySpace( ) )->getA();\n    MPI_Comm comm, temp_com;\n    comm = Ac.manager->getComms()->get_mpi_comm();\n    temp_com = compute_glue_matrices_communicator(Ac);\n    unglue_vector(Ac, comm, x, temp_com, x);\n#endif\n#endif\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Classical_AMG_Level_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Classical_AMG_Level<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/classical_amg_level.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"solvers/solver.h\"\n#include <classical/selectors/cr.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n\n#include <thrust/count.h>\n#include <thrust/inner_product.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/extrema.h>\n#include <thrust/reduce.h>\n#include <thrust/functional.h>\n#include <thrust/random.h>\n#include <thrust/transform.h>\n#include <thrust_wrapper.h>\n#include <solvers/block_common_solver.h>\n\n\nnamespace amgx\n{\n\nnamespace classical\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*************************************************************************\n * Generate uniform random vector\n ************************************************************************/\nstruct prg\n{\n    float a, b;\n\n    __host__ __device__\n    prg(float _a = 0.f, float _b = 1.f) : a(_a), b(_b) {};\n\n    __host__ __device__\n    float operator()(const unsigned int n) const\n    {\n        amgx::thrust::default_random_engine rng;\n        amgx::thrust::uniform_real_distribution<float> dist(a, b);\n        rng.discard(n);\n        return dist(rng);\n    }\n};\n\n\n// Initialization of test error vector with random entries\ntemplate <class Vector>\nvoid initRandom(Vector &vec)\n{\n    const unsigned int size = vec.size();\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    thrust_wrapper::transform<Vector::TConfig::memSpace>(index_sequence_begin, index_sequence_begin + size,\n                      vec.begin(), prg(0.f, 1.f));\n}\n\n// Zero out the entries of a vector corresponding to the coarse points\ntemplate <typename ValueType>\n__global__\nvoid zero_coarse_points_kernel(const int *cf_map, const int numRows, ValueType *vec)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == COARSE)\n        {\n            vec[tIdx] = 0.;\n        }\n    }\n}\n\n// Create a vector which extracts fine points from the error vector and is\n// zero at the coarse points\ntemplate <typename ValueType>\n__global__\nvoid get_fine_error_kernel( const int *cf_map, const int numRows,\n                            const ValueType *v_err, ValueType *vec )\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == COARSE)\n        {\n            vec[tIdx] = 0.;\n        }\n        else\n        {\n            vec[tIdx] = v_err[tIdx];\n        }\n    }\n}\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename ValueType>\n__global__\nvoid update_fine_zero_coarse_error_kernel(const int *cf_map, const int numRows,\n        ValueType *v_err, const ValueType *vec)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == FINE)\n        {\n            v_err[tIdx] = vec[tIdx];\n        }\n        else\n        {\n            v_err[tIdx] = (ValueType) 0;\n        }\n    }\n}\n\n\n// Update coarse and fine points from the matrix coloring taking the first\n// levels colors to form the independent set.\ntemplate <typename IndexType>\n__global__\nvoid update_cf_map_kernel(IndexType *cf_map, const int numRows, const int levels, const IndexType *A_row_colors)\n{\n    //coarse = [coarse; find(independent)];\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // go over each row\n        if (A_row_colors[tIdx] <= levels) // double check that this is what we want\n        {\n            cf_map[tIdx] = COARSE;\n        }\n    }\n}\n\n\n\n#define EXPERIMENTAL_CR\n\n#ifdef EXPERIMENTAL_CR\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_Asc_nnzPerRow_kernel2(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                                   const ValueType *Avalues, const ValueType *AdiagValues,\n                                   const IndexType AnumRows, const IndexType *cf_map,\n                                   IndexType *Asc_nnzPerRow, ValueType *row_thresh, const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"fine\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE)\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            //  Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            // but this thresh is dynamic\n            ValueType fine_avg_thresh = 0.;\n            int fine_count = 0;\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                int AjColIndex = AcolInd[j];\n\n                if ((AjColIndex != tIdx) && (cf_map[AjColIndex] == FINE))\n                {\n                    fine_avg_thresh += Avalues[j];\n                    fine_count ++;\n                }\n            }\n\n            fine_avg_thresh *= thresh / fine_count;\n            row_thresh[tIdx] = fine_avg_thresh;\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx)\n                {\n                    /*Asc_nnzPerRow[tIdx]++;*/\n                    continue;\n                }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (Avalues[j] / AdiagValues[tIdx] < fine_avg_thresh / AdiagValues[tIdx] || Avalues[j] / AdiagValues[AjColIndex] < fine_avg_thresh / AdiagValues[AjColIndex])\n                    {\n                        Asc_nnzPerRow[tIdx]++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_AscColInd_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                              const ValueType *Avalues, const ValueType *AdiagValues,\n                              const IndexType AnumRows, const IndexType *cf_map,\n                              const IndexType *AscRowOffsets, IndexType *AscColInd,\n                              const ValueType *row_thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n        const int AscRowEnd = AscRowOffsets[tIdx + 1];\n        ValueType rowMax(0);\n\n        if (cf_map[tIdx] == FINE) // tIdx = j (in matlab) i.e. row of A\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            // Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            int AscEntryNum = AscRowOffsets[tIdx];  // starting location of the current column of P\n\n            // calculate max(-rowentries)\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                rowMax = max( rowMax, -Avalues[j]);\n            }\n\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx) { /*AscColInd[AscEntryNum] = AjColIndex; AscEntryNum++;*/ continue; }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (-Avalues[j] >= row_thresh[tIdx]*rowMax)\n                    {\n                        AscColInd[AscEntryNum] = AjColIndex;\n                        AscEntryNum++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n#else\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_Asc_nnzPerRow_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                                  const ValueType *Avalues, const ValueType *AdiagValues,\n                                  const IndexType AnumRows, const IndexType *cf_map,\n                                  IndexType *Asc_nnzPerRow, const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"fine\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE)\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            //  Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx)\n                {\n                    /*Asc_nnzPerRow[tIdx]++;*/\n                    continue;\n                }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (Avalues[j] / AdiagValues[tIdx] < -thresh || Avalues[j] / AdiagValues[AjColIndex] < -thresh)\n                    {\n                        Asc_nnzPerRow[tIdx]++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_AscColInd_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                              const ValueType *Avalues, const ValueType *AdiagValues,\n                              const IndexType AnumRows, const IndexType *cf_map,\n                              const IndexType *AscRowOffsets, IndexType *AscColInd,\n                              const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n        const int AscRowEnd = AscRowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE) // tIdx = j (in matlab) i.e. row of A\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            // Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            int AscEntryNum = AscRowOffsets[tIdx];  // starting location of the current column of P\n\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx) { /*AscColInd[AscEntryNum] = AjColIndex; AscEntryNum++;*/ continue; }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if ((Avalues[j] / AdiagValues[tIdx] < -thresh) || (Avalues[j] / AdiagValues[AjColIndex] < -thresh))\n                    {\n                        AscColInd[AscEntryNum] = AjColIndex;\n                        AscEntryNum++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n#endif\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nCR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\nCR_Selector(AMG_Config &cfg, const std::string &cfg_scope) : \n  CR_SelectorBase<TConfig_d>(cfg, cfg_scope),\n    m_smoother(0) {}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nCR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\n~CR_Selector()\n{\n    if (m_smoother) { delete m_smoother; }\n}\n\n\n// Private functions for the implementation on device\n\n// Presmooth error\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::presmoothFineError( Matrix_d &A, const VVector &AdiagValues, const IVector &cf_map,\n                      Vector_d &v_u, Vector_d &v_tmp, Vector_d &v_z,\n                      Solver<TConfig_d> *smoother, ValueType &norm0,\n                      const ValueType rho_thresh, const int pre )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Coarse/Fine map\n    const int *cf_map_ptr = cf_map.raw();\n    // Vector with the initial 'fine' error\n    ValueTypeB *v_u_ptr   = v_u.raw();\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Compute 'energy norm' of v_u (normalize error before smoothing)\n    // v_tmp = diag(Aff).*v_u\n    thrust_wrapper::transform<AMGX_device>(AdiagValues.begin(), AdiagValues.end(),\n                      v_u.begin(), v_tmp.begin(),\n                      amgx::thrust::multiplies<ValueTypeB>());\n    cudaCheckError();\n    // norm0 = sqrt(v_tmp' * v_u)\n    norm0 = sqrt( amgx::thrust::inner_product(v_u.begin(), v_u.end(),\n                                        v_tmp.begin(), ValueTypeB(0.)) );\n    cudaCheckError();\n    // normalize: v_u = v_u / (norm0 + 1e-12);\n    thrust_wrapper::transform<AMGX_device>(v_u.begin(), v_u.end(),\n                      amgx::thrust::make_constant_iterator(norm0 + 1.0e-12), v_u.begin(),\n                      amgx::thrust::divides<ValueTypeB>());\n    cudaCheckError();\n\n    // ---------------------------- begin error presmoothing --------------------------\n    for (int k = 1; k <= 5; k++)\n    {\n        // Presmooth with Symmetric Gauss-Seidel (or ilu) for pre number of sm(ooth) s(teps)\n        // WARNING: zeroing out technique used here is not equivalent to the fine matrix pre-smoothing!\n        for (int sms = 1; sms <= pre; sms++)\n        {\n            smoother->solve_iteration( v_z, v_u, false );\n            zero_coarse_points_kernel<ValueTypeB>\n            <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_u_ptr);\n            cudaCheckError();\n        }\n\n        // -------------------- compute 'energy norm' of v_u ------------------------------\n        // v_tmp = diag(Aff).*v_u\n        thrust_wrapper::transform<AMGX_device>(AdiagValues.begin(), AdiagValues.end(),\n                          v_u.begin(), v_tmp.begin(),\n                          amgx::thrust::multiplies<ValueTypeB>());\n        cudaCheckError();\n        // norm0 = sqrt(v_tmp' * v_u)\n        norm0 = sqrt( amgx::thrust::inner_product(v_u.begin(), v_u.end(),\n                                            v_tmp.begin(), ValueTypeB(0.)) );\n        cudaCheckError();\n        // -------------------- done with 'energy norm' of v_u ----------------------------\n        // normalize: v_u = v_u / (norm0 + 1e-12);\n        thrust_wrapper::transform<AMGX_device>(v_u.begin(), v_u.end(),\n                          amgx::thrust::make_constant_iterator(norm0 + 1.0e-12), v_u.begin(),\n                          amgx::thrust::divides<ValueTypeB>());\n        cudaCheckError();\n\n        // norm0 := rho\n        if (norm0 > 5 || norm0 < rho_thresh) { break; }\n    }\n\n    // ---------------------------- end error presmoothing ------------------------------\n}\n\n\n// A single iteration of CR while loop.\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::CR_iteration( Matrix_d &A, IVector &cf_map, int &numFine,\n                Vector_d &v_err, ValueType &norm0,\n                const ValueType maxit )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Coarse/Fine map\n    IndexType *cf_map_ptr = cf_map.raw();\n    // Raw ptr for the row colors from the multi-color smoother\n    const IndexType *A_row_colors_ptr;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    const int pre   = 10;\n    const ValueType alpha = 0.7;\n    const ValueType rho_thresh = 1.0e-2;\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    // temporary vectors - diagonal and non-zero offsets\n    VVector AdiagValues(AnumRows, 0.0);\n    // Extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(AnumRows, A.diag.raw(), Avalues_ptr, AdiagValues.raw());\n    cudaCheckError();\n    // ----------------------------------- define smoother ------------------------------------------\n    // Set the smoother to Gauss-Seidel (json config format)\n    AMG_Config cfg;\n    std::string cfg_string = \"{ \\\"config_version\\\": 2\";\n    cfg_string += \", \\\"solver\\\": { \\\"scope\\\": \\\"presmoothError\\\", \\\"solver\\\": \\\"MULTICOLOR_GS\\\"\";\n    cfg_string += \" ,\\\"matrix_coloring_scheme\\\": \\\"PARALLEL_GREEDY\\\"\";\n    cfg_string += \" } }\";\n    cfg.parseParameterString(cfg_string.c_str());\n    // Allocate smoother\n    m_smoother = SolverFactory<TConfig_d>::allocate(cfg, \"default\", \"solver\");\n    // --------------------------------- end define smoother ----------------------------------------\n    Vector_d v_z(AnumRows, 0.0);\n    Vector_d v_u(AnumRows);\n    Vector_d v_tmp(v_u.size());\n    ValueTypeB *v_err_ptr = v_err.raw();\n    ValueTypeB *v_u_ptr = v_u.raw();\n    // Store the number of non-zeros in each column of the interpolation matrix P\n    IVector Asc_nnzPerRow(AnumRows, 0);\n#ifdef EXPERIMENTAL_CR\n    VVector Asc_thresh_per_row(AnumRows, 0);\n    ValueTypeB CR_P_trunc = 0.9; // truncation coefficient for CR P matrix, [0.0 ... 1.0]\n    ValueTypeB Asc_thresh = (CR_P_trunc < epsilon(CR_P_trunc)) ? (-1. / epsilon(CR_P_trunc)) : (-1. / CR_P_trunc + 1);\n    compute_Asc_nnzPerRow_kernel2<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc_nnzPerRow.raw(), Asc_thresh_per_row.raw(), Asc_thresh);\n                                cudaCheckError();\n#else\n    compute_Asc_nnzPerRow_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc_nnzPerRow.raw(), 0.1);\n                                cudaCheckError();\n#endif\n    cudaCheckError();\n    // get the offsets in Asc with an inclusive scan\n    thrust_wrapper::inclusive_scan<AMGX_device>(Asc_nnzPerRow.begin(), Asc_nnzPerRow.end(), Asc_nnzPerRow.begin());\n    cudaCheckError();\n    // get total num of non-zeros in P\n    const int Asc_nnz = Asc_nnzPerRow[AnumRows - 1];\n    // Declare Asc (strong connections of A)\n    Matrix_d Asc;\n    // resize Asc\n    Asc.resize(0, 0, 0, 1);\n    Asc.addProps(CSR);\n    Asc.resize(AnumRows, AnumRows, Asc_nnz, 1);\n    Asc.row_offsets[0] = 0;\n    // set P offsets (P column offsets or P^T row offsets)\n    amgx::thrust::copy(Asc_nnzPerRow.begin(), Asc_nnzPerRow.end(), Asc.row_offsets.begin() + 1);\n    cudaCheckError();\n#ifdef EXPERIMENTAL_CR\n    compute_AscColInd_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc.row_offsets.raw(), Asc.col_indices.raw(), Asc_thresh_per_row.raw());\n                                cudaCheckError();\n#else\n    compute_AscColInd_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc.row_offsets.raw(), Asc.col_indices.raw(), 0.25);\n                                cudaCheckError();\n#endif\n    cudaCheckError();\n    Asc.set_initialized(0);\n    Asc.colorMatrix(cfg, \"presmoothError\");\n    Asc.set_initialized(1);\n    // get the coloring of matrix\n    A_row_colors_ptr = Asc.getMatrixColoring().getRowColors().raw();\n    m_smoother->setup(A, false);\n    int levels = 1;     // number of CR iterations\n\n    while (levels < 5)\n    {\n        // Here, we normally (i.e. as is done in Brannick's matlab code) we need to smooth using Aff,\n        // however, since we are using Gauss-Seidel (symmetric or not) we can instead use A and\n        // simply zero out v_err at the coarse points of each iteration.\n        // v_u = v_err(fine)\n        get_fine_error_kernel<ValueTypeB>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_err_ptr, v_u_ptr);\n        cudaCheckError();\n        // Presmooth error at the current FINE points.\n        presmoothFineError( A, AdiagValues, cf_map, v_u, v_tmp, v_z,\n                            m_smoother, norm0, rho_thresh, pre );\n\n        // check for convergence and break if it's fast enough\n        if (norm0 <= alpha && levels > 1)\n        {\n            break;\n        }\n\n        // v_err(fine) = v_u;\n        update_fine_zero_coarse_error_kernel<ValueTypeB>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_err_ptr, v_u_ptr);\n        cudaCheckError();\n        // TODO: add aggressive coarsening here.\n        // if (AggressiveCoarsening) { A2 = Al*Al; }\n        // else                      { A2 = Al; }\n        // Add nodes with lowest colors (<=levels) to the coarse set.\n        update_cf_map_kernel<IndexType>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, levels - 1, A_row_colors_ptr);\n        cudaCheckError();\n        // Get the new FINE points count.\n        numFine = (int) amgx::thrust::count(cf_map.begin(), cf_map.end(), (int)FINE);\n        cudaCheckError();\n\n        if (numFine == 0) { break; }  // no fine points left, just exit\n\n        levels++;\n    } // end while (levels < maxit)\n} // end CR_iteration()\n\n\n/*************************************************************************\n * Implementing the CR algorithm\n ************************************************************************/\n// ----------------------------\n//  specialization for device\n// ----------------------------\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1( Matrix_d &A,\n                            IVector &cf_map)\n{\n    if (A.hasProps(DIAG)) { FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Dimension of A.\n    const int AnumRows  = (int) A.get_num_rows();\n\n    if (AnumRows == 0) { return; }\n\n    // Choose blocksize. Using 1 thread / row for now\n    const int blocksize = 256;\n    const int numBlocks = std::min(AMGX_GRID_MAX_SIZE, (int) ((AnumRows + blocksize - 1) / blocksize));\n    const int maxit = 5;  // Max num of CR iterations.\n    ValueType norm0;      // Stores the energy norm of the smoothed error in CR iteration.\n    int *cf_map_ptr = cf_map.raw();\n    // Initially all points are set to FINE - this is done in the level\n    // Initial number of fine points (all points are fine).\n    int numFine = AnumRows;\n    // Randomly initialize error vector v_err: AnumRows x 1 vector.\n    Vector_d v_err(AnumRows);\n    initRandom(v_err);\n    // Perform Compatible Relaxation (CR) iteration.\n    CR_iteration(A, cf_map, numFine, v_err, norm0, maxit);\n    int numCoarse = AnumRows - numFine;\n    printf(\"CR: numrows = %d, numCoarse = %d\\n\", AnumRows, numCoarse);\n} // end markCoarseFinePoints_1x1 (device specialization)\n\n\ntemplate <class T_Config>\nvoid CR_SelectorBase<T_Config>::markCoarseFinePoints( Matrix<TConfig> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, cf_map);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size CR selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n\n\n/****************************************\n * Explicit instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CR_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CR_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include \"solvers/solver.h\"\n#include <classical/selectors/cr.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n\n#include <thrust/count.h>\n#include <thrust/inner_product.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/extrema.h>\n#include <thrust/reduce.h>\n#include <thrust/functional.h>\n#include <thrust/random.h>\n#include <thrust/transform.h>\n#include <thrust_wrapper.h>\n#include <solvers/block_common_solver.h>\n\n\nnamespace amgx\n{\n\nnamespace classical\n{\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*************************************************************************\n * Generate uniform random vector\n ************************************************************************/\nstruct prg\n{\n    float a, b;\n\n    __host__ __device__\n    prg(float _a = 0.f, float _b = 1.f) : a(_a), b(_b) {};\n\n    __host__ __device__\n    float operator()(const unsigned int n) const\n    {\n        amgx::thrust::default_random_engine rng;\n        amgx::thrust::uniform_real_distribution<float> dist(a, b);\n        rng.discard(n);\n        return dist(rng);\n    }\n};\n\n\n// Initialization of test error vector with random entries\ntemplate <class Vector>\nvoid initRandom(Vector &vec)\n{\n    const unsigned int size = vec.size();\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    thrust_wrapper::transform<Vector::TConfig::memSpace>(index_sequence_begin, index_sequence_begin + size,\n                      vec.begin(), prg(0.f, 1.f));\n}\n\n// Zero out the entries of a vector corresponding to the coarse points\ntemplate <typename ValueType>\n__global__\nvoid zero_coarse_points_kernel(const int *cf_map, const int numRows, ValueType *vec)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == COARSE)\n        {\n            vec[tIdx] = 0.;\n        }\n    }\n}\n\n// Create a vector which extracts fine points from the error vector and is\n// zero at the coarse points\ntemplate <typename ValueType>\n__global__\nvoid get_fine_error_kernel( const int *cf_map, const int numRows,\n                            const ValueType *v_err, ValueType *vec )\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == COARSE)\n        {\n            vec[tIdx] = 0.;\n        }\n        else\n        {\n            vec[tIdx] = v_err[tIdx];\n        }\n    }\n}\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename ValueType>\n__global__\nvoid update_fine_zero_coarse_error_kernel(const int *cf_map, const int numRows,\n        ValueType *v_err, const ValueType *vec)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // if point is coarse, zero out the corresponding entry of vec\n        if (cf_map[tIdx] == FINE)\n        {\n            v_err[tIdx] = vec[tIdx];\n        }\n        else\n        {\n            v_err[tIdx] = (ValueType) 0;\n        }\n    }\n}\n\n\n// Update coarse and fine points from the matrix coloring taking the first\n// levels colors to form the independent set.\ntemplate <typename IndexType>\n__global__\nvoid update_cf_map_kernel(IndexType *cf_map, const int numRows, const int levels, const IndexType *A_row_colors)\n{\n    //coarse = [coarse; find(independent)];\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // go over each row\n        if (A_row_colors[tIdx] <= levels) // double check that this is what we want\n        {\n            cf_map[tIdx] = COARSE;\n        }\n    }\n}\n\n\n\n#define EXPERIMENTAL_CR\n\n#ifdef EXPERIMENTAL_CR\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_Asc_nnzPerRow_kernel2(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                                   const ValueType *Avalues, const ValueType *AdiagValues,\n                                   const IndexType AnumRows, const IndexType *cf_map,\n                                   IndexType *Asc_nnzPerRow, ValueType *row_thresh, const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"fine\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE)\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            //  Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            // but this thresh is dynamic\n            ValueType fine_avg_thresh = 0.;\n            int fine_count = 0;\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                int AjColIndex = AcolInd[j];\n\n                if ((AjColIndex != tIdx) && (cf_map[AjColIndex] == FINE))\n                {\n                    fine_avg_thresh += Avalues[j];\n                    fine_count ++;\n                }\n            }\n\n            fine_avg_thresh *= thresh / fine_count;\n            row_thresh[tIdx] = fine_avg_thresh;\n\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx)\n                {\n                    /*Asc_nnzPerRow[tIdx]++;*/\n                    continue;\n                }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (Avalues[j] / AdiagValues[tIdx] < fine_avg_thresh / AdiagValues[tIdx] || Avalues[j] / AdiagValues[AjColIndex] < fine_avg_thresh / AdiagValues[AjColIndex])\n                    {\n                        Asc_nnzPerRow[tIdx]++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_AscColInd_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                              const ValueType *Avalues, const ValueType *AdiagValues,\n                              const IndexType AnumRows, const IndexType *cf_map,\n                              const IndexType *AscRowOffsets, IndexType *AscColInd,\n                              const ValueType *row_thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n        const int AscRowEnd = AscRowOffsets[tIdx + 1];\n        ValueType rowMax(0);\n\n        if (cf_map[tIdx] == FINE) // tIdx = j (in matlab) i.e. row of A\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            // Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            int AscEntryNum = AscRowOffsets[tIdx];  // starting location of the current column of P\n\n            // calculate max(-rowentries)\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                rowMax = max( rowMax, -Avalues[j]);\n            }\n\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx) { /*AscColInd[AscEntryNum] = AjColIndex; AscEntryNum++;*/ continue; }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (-Avalues[j] >= row_thresh[tIdx]*rowMax)\n                    {\n                        AscColInd[AscEntryNum] = AjColIndex;\n                        AscEntryNum++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n#else\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_Asc_nnzPerRow_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                                  const ValueType *Avalues, const ValueType *AdiagValues,\n                                  const IndexType AnumRows, const IndexType *cf_map,\n                                  IndexType *Asc_nnzPerRow, const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"fine\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE)\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            //  Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            for (int j = ArowBegin; j < ArowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx)\n                {\n                    /*Asc_nnzPerRow[tIdx]++;*/\n                    continue;\n                }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if (Avalues[j] / AdiagValues[tIdx] < -thresh || Avalues[j] / AdiagValues[AjColIndex] < -thresh)\n                    {\n                        Asc_nnzPerRow[tIdx]++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n\n// Update only the fine points of the error vector (v_err) based on input vec.\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid compute_AscColInd_kernel(const IndexType *ArowOffsets, const IndexType *AcolInd,\n                              const ValueType *Avalues, const ValueType *AdiagValues,\n                              const IndexType AnumRows, const IndexType *cf_map,\n                              const IndexType *AscRowOffsets, IndexType *AscColInd,\n                              const ValueType thresh)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < AnumRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // each thread works on one \"coarse\" row of A at a time\n        const int ArowBegin = ArowOffsets[tIdx];\n        const int ArowEnd   = ArowOffsets[tIdx + 1];\n        const int AscRowEnd = AscRowOffsets[tIdx + 1];\n\n        if (cf_map[tIdx] == FINE) // tIdx = j (in matlab) i.e. row of A\n        {\n            // work with fine pts only\n            // Matlab pseudo-code:\n            // Asc(:,i) = A(:,i)/A(i,i)<-thresh;\n            int AscEntryNum = AscRowOffsets[tIdx];  // starting location of the current column of P\n\n            for (int j = ArowBegin; j < ArowEnd && AscEntryNum < AscRowEnd; j++)\n            {\n                // go thru each \"fine\" column of A\n                int AjColIndex = AcolInd[j];\n\n                if (AjColIndex == tIdx) { /*AscColInd[AscEntryNum] = AjColIndex; AscEntryNum++;*/ continue; }\n\n                if (cf_map[AjColIndex] == FINE)\n                {\n                    if ((Avalues[j] / AdiagValues[tIdx] < -thresh) || (Avalues[j] / AdiagValues[AjColIndex] < -thresh))\n                    {\n                        AscColInd[AscEntryNum] = AjColIndex;\n                        AscEntryNum++;\n                    }\n                }\n            }\n        }\n    }\n}\n\n#endif\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nCR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\nCR_Selector(AMG_Config &cfg, const std::string &cfg_scope) : \n  CR_SelectorBase<TConfig_d>(cfg, cfg_scope),\n    m_smoother(0) {}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nCR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\n~CR_Selector()\n{\n    if (m_smoother) { delete m_smoother; }\n}\n\n\n// Private functions for the implementation on device\n\n// Presmooth error\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::presmoothFineError( Matrix_d &A, const VVector &AdiagValues, const IVector &cf_map,\n                      Vector_d &v_u, Vector_d &v_tmp, Vector_d &v_z,\n                      Solver<TConfig_d> *smoother, ValueType &norm0,\n                      const ValueType rho_thresh, const int pre )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Coarse/Fine map\n    const int *cf_map_ptr = cf_map.raw();\n    // Vector with the initial 'fine' error\n    ValueTypeB *v_u_ptr   = v_u.raw();\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    // Compute 'energy norm' of v_u (normalize error before smoothing)\n    // v_tmp = diag(Aff).*v_u\n    thrust_wrapper::transform<AMGX_device>(AdiagValues.begin(), AdiagValues.end(),\n                      v_u.begin(), v_tmp.begin(),\n                      amgx::thrust::multiplies<ValueTypeB>());\n    cudaCheckError();\n    // norm0 = sqrt(v_tmp' * v_u)\n    norm0 = sqrt( amgx::thrust::inner_product(v_u.begin(), v_u.end(),\n                                        v_tmp.begin(), ValueTypeB(0.)) );\n    cudaCheckError();\n    // normalize: v_u = v_u / (norm0 + 1e-12);\n    thrust_wrapper::transform<AMGX_device>(v_u.begin(), v_u.end(),\n                      amgx::thrust::make_constant_iterator(norm0 + 1.0e-12), v_u.begin(),\n                      amgx::thrust::divides<ValueTypeB>());\n    cudaCheckError();\n\n    // ---------------------------- begin error presmoothing --------------------------\n    for (int k = 1; k <= 5; k++)\n    {\n        // Presmooth with Symmetric Gauss-Seidel (or ilu) for pre number of sm(ooth) s(teps)\n        // WARNING: zeroing out technique used here is not equivalent to the fine matrix pre-smoothing!\n        for (int sms = 1; sms <= pre; sms++)\n        {\n            smoother->solve_iteration( v_z, v_u, false );\n            zero_coarse_points_kernel<ValueTypeB>\n            <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_u_ptr);\n            cudaCheckError();\n        }\n\n        // -------------------- compute 'energy norm' of v_u ------------------------------\n        // v_tmp = diag(Aff).*v_u\n        thrust_wrapper::transform<AMGX_device>(AdiagValues.begin(), AdiagValues.end(),\n                          v_u.begin(), v_tmp.begin(),\n                          amgx::thrust::multiplies<ValueTypeB>());\n        cudaCheckError();\n        // norm0 = sqrt(v_tmp' * v_u)\n        norm0 = sqrt( amgx::thrust::inner_product(v_u.begin(), v_u.end(),\n                                            v_tmp.begin(), ValueTypeB(0.)) );\n        cudaCheckError();\n        // -------------------- done with 'energy norm' of v_u ----------------------------\n        // normalize: v_u = v_u / (norm0 + 1e-12);\n        thrust_wrapper::transform<AMGX_device>(v_u.begin(), v_u.end(),\n                          amgx::thrust::make_constant_iterator(norm0 + 1.0e-12), v_u.begin(),\n                          amgx::thrust::divides<ValueTypeB>());\n        cudaCheckError();\n\n        // norm0 := rho\n        if (norm0 > 5 || norm0 < rho_thresh) { break; }\n    }\n\n    // ---------------------------- end error presmoothing ------------------------------\n}\n\n\n// A single iteration of CR while loop.\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::CR_iteration( Matrix_d &A, IVector &cf_map, int &numFine,\n                Vector_d &v_err, ValueType &norm0,\n                const ValueType maxit )\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    typedef typename Vector_d::value_type ValueTypeB;\n    // Matrix A\n    const IndexType AnumRows = (IndexType) A.get_num_rows();\n    // Coarse/Fine map\n    IndexType *cf_map_ptr = cf_map.raw();\n    // Raw ptr for the row colors from the multi-color smoother\n    const IndexType *A_row_colors_ptr;\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (AnumRows / blocksize + 1));\n    const int pre   = 10;\n    const ValueType alpha = 0.7;\n    const ValueType rho_thresh = 1.0e-2;\n    const IndexType *ArowOffsets_ptr = A.row_offsets.raw();\n    const IndexType *AcolInd_ptr     = A.col_indices.raw();\n    const ValueType *Avalues_ptr     = A.values.raw();\n    // temporary vectors - diagonal and non-zero offsets\n    VVector AdiagValues(AnumRows, 0.0);\n    // Extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(AnumRows, A.diag.raw(), Avalues_ptr, AdiagValues.raw());\n    cudaCheckError();\n    // ----------------------------------- define smoother ------------------------------------------\n    // Set the smoother to Gauss-Seidel (json config format)\n    AMG_Config cfg;\n    std::string cfg_string = \"{ \\\"config_version\\\": 2\";\n    cfg_string += \", \\\"solver\\\": { \\\"scope\\\": \\\"presmoothError\\\", \\\"solver\\\": \\\"MULTICOLOR_GS\\\"\";\n    cfg_string += \" ,\\\"matrix_coloring_scheme\\\": \\\"PARALLEL_GREEDY\\\"\";\n    cfg_string += \" } }\";\n    cfg.parseParameterString(cfg_string.c_str());\n    // Allocate smoother\n    m_smoother = SolverFactory<TConfig_d>::allocate(cfg, \"default\", \"solver\");\n    // --------------------------------- end define smoother ----------------------------------------\n    Vector_d v_z(AnumRows, 0.0);\n    Vector_d v_u(AnumRows);\n    Vector_d v_tmp(v_u.size());\n    ValueTypeB *v_err_ptr = v_err.raw();\n    ValueTypeB *v_u_ptr = v_u.raw();\n    // Store the number of non-zeros in each column of the interpolation matrix P\n    IVector Asc_nnzPerRow(AnumRows, 0);\n#ifdef EXPERIMENTAL_CR\n    VVector Asc_thresh_per_row(AnumRows, 0);\n    ValueTypeB CR_P_trunc = 0.9; // truncation coefficient for CR P matrix, [0.0 ... 1.0]\n    ValueTypeB Asc_thresh = (CR_P_trunc < epsilon(CR_P_trunc)) ? (-1. / epsilon(CR_P_trunc)) : (-1. / CR_P_trunc + 1);\n    compute_Asc_nnzPerRow_kernel2<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc_nnzPerRow.raw(), Asc_thresh_per_row.raw(), Asc_thresh);\n                                cudaCheckError();\n#else\n    compute_Asc_nnzPerRow_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc_nnzPerRow.raw(), 0.1);\n                                cudaCheckError();\n#endif\n    cudaCheckError();\n    // get the offsets in Asc with an inclusive scan\n    thrust_wrapper::inclusive_scan<AMGX_device>(Asc_nnzPerRow.begin(), Asc_nnzPerRow.end(), Asc_nnzPerRow.begin());\n    cudaCheckError();\n    // get total num of non-zeros in P\n    const int Asc_nnz = Asc_nnzPerRow[AnumRows - 1];\n    // Declare Asc (strong connections of A)\n    Matrix_d Asc;\n    // resize Asc\n    Asc.resize(0, 0, 0, 1);\n    Asc.addProps(CSR);\n    Asc.resize(AnumRows, AnumRows, Asc_nnz, 1);\n    Asc.row_offsets[0] = 0;\n    // set P offsets (P column offsets or P^T row offsets)\n    amgx::thrust::copy(Asc_nnzPerRow.begin(), Asc_nnzPerRow.end(), Asc.row_offsets.begin() + 1);\n    cudaCheckError();\n#ifdef EXPERIMENTAL_CR\n    compute_AscColInd_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc.row_offsets.raw(), Asc.col_indices.raw(), Asc_thresh_per_row.raw());\n                                cudaCheckError();\n#else\n    compute_AscColInd_kernel<IndexType, ValueType>\n    <<< numBlocks, blocksize>>>(ArowOffsets_ptr, AcolInd_ptr,\n                                Avalues_ptr, AdiagValues.raw(),\n                                AnumRows, cf_map_ptr,\n                                Asc.row_offsets.raw(), Asc.col_indices.raw(), 0.25);\n                                cudaCheckError();\n#endif\n    cudaCheckError();\n    Asc.set_initialized(0);\n    Asc.colorMatrix(cfg, \"presmoothError\");\n    Asc.set_initialized(1);\n    // get the coloring of matrix\n    A_row_colors_ptr = Asc.getMatrixColoring().getRowColors().raw();\n    m_smoother->setup(A, false);\n    int levels = 1;     // number of CR iterations\n\n    while (levels < 5)\n    {\n        // Here, we normally (i.e. as is done in Brannick's matlab code) we need to smooth using Aff,\n        // however, since we are using Gauss-Seidel (symmetric or not) we can instead use A and\n        // simply zero out v_err at the coarse points of each iteration.\n        // v_u = v_err(fine)\n        get_fine_error_kernel<ValueTypeB>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_err_ptr, v_u_ptr);\n        cudaCheckError();\n        // Presmooth error at the current FINE points.\n        presmoothFineError( A, AdiagValues, cf_map, v_u, v_tmp, v_z,\n                            m_smoother, norm0, rho_thresh, pre );\n\n        // check for convergence and break if it's fast enough\n        if (norm0 <= alpha && levels > 1)\n        {\n            break;\n        }\n\n        // v_err(fine) = v_u;\n        update_fine_zero_coarse_error_kernel<ValueTypeB>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, v_err_ptr, v_u_ptr);\n        cudaCheckError();\n        // TODO: add aggressive coarsening here.\n        // if (AggressiveCoarsening) { A2 = Al*Al; }\n        // else                      { A2 = Al; }\n        // Add nodes with lowest colors (<=levels) to the coarse set.\n        update_cf_map_kernel<IndexType>\n        <<< numBlocks, blocksize>>>(cf_map_ptr, AnumRows, levels - 1, A_row_colors_ptr);\n        cudaCheckError();\n        // Get the new FINE points count.\n        numFine = (int) amgx::thrust::count(cf_map.begin(), cf_map.end(), (int)FINE);\n        cudaCheckError();\n\n        if (numFine == 0) { break; }  // no fine points left, just exit\n\n        levels++;\n    } // end while (levels < maxit)\n} // end CR_iteration()\n\n\n/*************************************************************************\n * Implementing the CR algorithm\n ************************************************************************/\n// ----------------------------\n//  specialization for device\n// ----------------------------\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid CR_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1( Matrix_d &A,\n                            IVector &cf_map)\n{\n    if (A.hasProps(DIAG)) { FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED); }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Dimension of A.\n    const int AnumRows  = (int) A.get_num_rows();\n\n    if (AnumRows == 0) { return; }\n\n    // Choose blocksize. Using 1 thread / row for now\n    const int blocksize = 256;\n    const int numBlocks = std::min(AMGX_GRID_MAX_SIZE, (int) ((AnumRows + blocksize - 1) / blocksize));\n    const int maxit = 5;  // Max num of CR iterations.\n    ValueType norm0;      // Stores the energy norm of the smoothed error in CR iteration.\n    int *cf_map_ptr = cf_map.raw();\n    // Initially all points are set to FINE - this is done in the level\n    // Initial number of fine points (all points are fine).\n    int numFine = AnumRows;\n    // Randomly initialize error vector v_err: AnumRows x 1 vector.\n    Vector_d v_err(AnumRows);\n    initRandom(v_err);\n    // Perform Compatible Relaxation (CR) iteration.\n    CR_iteration(A, cf_map, numFine, v_err, norm0, maxit);\n    int numCoarse = AnumRows - numFine;\n    printf(\"CR: numrows = %d, numCoarse = %d\\n\", AnumRows, numCoarse);\n} // end markCoarseFinePoints_1x1 (device specialization)\n\n\ntemplate <class T_Config>\nvoid CR_SelectorBase<T_Config>::markCoarseFinePoints( Matrix<TConfig> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, cf_map);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size CR selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n\n\n/****************************************\n * Explicit instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class CR_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class CR_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/selectors/cr.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <classical/selectors/dummy_selector.h>\n#include <classical/interpolators/common.h>\n#include <cutil.h>\n#include <types.h>\n\n#include<thrust/count.h>\n\nnamespace amgx\n{\n\nnamespace classical\n{\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingKernel(IndexType *cf_map, const IndexType numRows)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        cf_map[tIdx] = (tIdx % 2) ? COARSE : FINE;\n    }\n}\n\n\n\n/*************************************************************************\n * mark coarse and fine points\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Dummy_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1(Matrix_h &A,\n                           FVector &weights,\n                           const BVector &s_con,\n                           IVector &cf_map,\n                           IVector &scratch,\n                           int cf_map_init)\n{\n    FatalError(\"DummySelector not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Dummy_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::markCoarseFinePoints_1x1(Matrix_d &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Choose blocksize. Using 1 thread / row for now\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() + blockSize - 1) / blockSize));\n    const int numRows = (int) A.get_num_rows();\n    // raw pointers from the cusp arrays\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const ValueType *values_ptr = A.values.raw();\n    float *weights_ptr = weights.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *scratch_ptr = scratch.raw();\n    // do the initial marking of points\n    initialMarkingKernel <<< numBlocks, blockSize>>>(cf_map.raw(), A.get_num_rows());\n    cudaCheckError();\n    cf_map.dirtybit = 1;\n\n    // Do a two ring exchange before\n    // TODO: This should query the interpolator to figure out what is needed in more general case\n    // By default we do 2 rings in classical\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo_2ring(cf_map, cf_map.tag);\n    }\n}\n\ntemplate <class T_Config>\nvoid Dummy_SelectorBase< T_Config>::markCoarseFinePoints(Matrix< T_Config> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, weights, s_con, cf_map, scratch);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size Dummy selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    // Mark cf_map as dirty\n    cf_map.dirtybit = 1;\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Dummy_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Dummy_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <classical/selectors/dummy_selector.h>\n#include <classical/interpolators/common.h>\n#include <cutil.h>\n#include <types.h>\n\n#include<thrust/count.h>\n\nnamespace amgx\n{\n\nnamespace classical\n{\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingKernel(IndexType *cf_map, const IndexType numRows)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        cf_map[tIdx] = (tIdx % 2) ? COARSE : FINE;\n    }\n}\n\n\n\n/*************************************************************************\n * mark coarse and fine points\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Dummy_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1(Matrix_h &A,\n                           FVector &weights,\n                           const BVector &s_con,\n                           IVector &cf_map,\n                           IVector &scratch,\n                           int cf_map_init)\n{\n    FatalError(\"DummySelector not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Dummy_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::markCoarseFinePoints_1x1(Matrix_d &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Choose blocksize. Using 1 thread / row for now\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() + blockSize - 1) / blockSize));\n    const int numRows = (int) A.get_num_rows();\n    // raw pointers from the cusp arrays\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const ValueType *values_ptr = A.values.raw();\n    float *weights_ptr = weights.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *scratch_ptr = scratch.raw();\n    // do the initial marking of points\n    initialMarkingKernel <<< numBlocks, blockSize>>>(cf_map.raw(), A.get_num_rows());\n    cudaCheckError();\n    cf_map.dirtybit = 1;\n\n    // Do a two ring exchange before\n    // TODO: This should query the interpolator to figure out what is needed in more general case\n    // By default we do 2 rings in classical\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo_2ring(cf_map, cf_map.tag);\n    }\n}\n\ntemplate <class T_Config>\nvoid Dummy_SelectorBase< T_Config>::markCoarseFinePoints(Matrix< T_Config> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, weights, s_con, cf_map, scratch);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size Dummy selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    // Mark cf_map as dirty\n    cf_map.dirtybit = 1;\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Dummy_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Dummy_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/selectors/dummy_selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <types.h>\n#include <classical/selectors/selector.h>\n#include <thrust/count.h>\n#include <hash_workspace.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n#include <assert.h>\n\n#include <device_properties.h>\n\nnamespace amgx\n{\nnamespace classical\n{\nstruct is_non_neg\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x >= 0;\n    }\n};\n\n__global__\nvoid coarseMarkKernel(int *cf_map, int *mark, const int numEntries)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < numEntries; tIdx += gridDim.x * blockDim.x)\n    {\n        if (cf_map[tIdx] == COARSE)\n        {\n            mark[tIdx] = 1;\n        }\n    }\n}\n\n__global__\nvoid modifyCoarseMapKernel(int *cf_map, int *mark, const int numEntries)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < numEntries; tIdx += gridDim.x * blockDim.x)\n    {\n        // entry mark[i] is 0 if fine, desired #+1 if coarse\n        if (cf_map[tIdx] == COARSE)\n        {\n            cf_map[tIdx] = mark[tIdx] - 1;\n        }\n    }\n}\n\n/*\n * Count the # of non-zeros per row\n */\n__global__\nvoid nonZerosPerCoarseRowKernel(const int num_rows, const int *cf_map, const int *C_hat_start,\n                                const int *C_hat_end, int *nonZerosPerCoarseRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        // If coarse point\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZerosPerCoarseRow[cf_map[tIdx]] = C_hat_end[tIdx] - C_hat_start[tIdx];\n        }\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid resolve_boundary(const IndexType *offsets, const IndexType *column_indices, bool *s_con,\n                      const int *cf_map_in, const int base_offset, const int numRows)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        if (cf_map_in[base_offset + tIdx] == COARSE)\n        {\n            // for each column\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // skip weakly connected points\n\n                int jcol = column_indices[j];\n\n                if (cf_map_in[jcol] == COARSE) // coarse neighbour\n                {\n                    s_con[j] = false; //make it weak\n                }\n            }\n        }\n    }\n}\n\nnamespace selector\n{\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncthreads();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of B.\n                int b_col_it  = A_rows[b_row_id + 0];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id + 0];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel_opt( const int nunfine,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets,\n                            int* unfine_set )\n{\n    // First threads load the row IDs of A needed by the CTA...\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Loop over rows of A.\n    if (gid >= nunfine) return;\n\n    int a_row_id = unfine_set[gid];\n\n    int count = 0;\n    // Iterate over the columns of A to build C_hat.\n    for ( int a_col_it = A_rows[a_row_id  ]; a_col_it < A_rows[a_row_id + 1]; ++a_col_it )\n    {\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int a_col_id = A_cols[a_col_it];\n        // Is it an off-diagonal element.\n        bool is_off_diagonal = a_col_id != a_row_id;\n        // Is it strongly connected ?\n        bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n        // Is it fine.\n        bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n        // Is it isolated.\n        bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n        // Is it a coarse and strongly-connected column.\n        bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n\n        if(is_coarse_strongly_connected) \n        {\n            count++;\n        }\n\n        // Is it a fine and strongly-connected column.\n        bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n\n        if (!is_fine_strongly_connected ) continue;\n\n        int b_col_beg = A_rows[a_col_id];\n\n        // _iterate over the range of columns of B.\n        for (int b_col_it = b_col_beg; b_col_it < A_rows[a_col_id+1]; ++b_col_it)\n        {\n            // The ID of the column.\n            int b_col_id = A_cols[b_col_it];\n\n            // Is it an off-diagonal element.\n            is_off_diagonal = b_col_id != a_col_id;\n            // Is it a coarse and strongly-connected column.\n            is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n            // Push coarse and strongly connected nodes in the set.\n            if(is_coarse_strongly_connected)\n            {\n                count++;\n            }\n        }\n    }\n\n    C_hat_offsets[a_row_id] = count;\n}\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        __syncthreads();\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n        __syncthreads();\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            //if (a_row_id == 7) printf(\"inserting %d\\n\",item);\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of the row of B.\n                int b_col_it  = A_rows[uniform_b_row_id + 0];\n                int b_col_end = A_rows[uniform_b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                    //if (a_row_id == 7) printf(\"inserting %d\\n\",b_item);\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store(&C_hat[c_col_it]);\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            //if (a_row_id == 7) printf(\"inserting item %d, is_off_diagonal = %d, \\n\",item,is_off_diagonal);\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[uniform_b_row_id + 0];\n                    b_col_end = A_rows[uniform_b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id && b_col_id != a_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                    //if (a_row_id == 7) printf(\"inserting b_item %d, is_off_diagonal = %d, \\n\",b_item,is_off_diagonal);\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store(&C_hat[c_col_it]) ;\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE>\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel_opt( int unfine_set_size,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_end,\n                      int *__restrict cf_map,\n                      int *__restrict unfine_set)\n{\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    const int gid = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Hash container storage\n    int* key_s = (int*)s;\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n    }\n\n    int* count_s = (int*)&key_s[ngroups*HASH_SIZE];\n    if(lane_id == 0)\n    {\n        count_s[group_id] = 0;\n    }\n\n    __syncthreads();\n\n    int a_row_id = -1;\n\n    if(gid < unfine_set_size)\n    {\n        // One row of A per group\n        a_row_id = unfine_set[gid];\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = A_rows[a_row_id] + lane_id ; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE )\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n\n            if(is_coarse_strongly_connected) \n            {\n                int hash = a_col_id % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == a_col_id) break;\n\n                    if(curr_key == SLOT_VACANT)\n                    {\n                        int old_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, a_col_id);\n                        if(old_key == SLOT_VACANT || old_key == a_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n\n            if(!is_fine_strongly_connected) continue;\n\n            // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n            int b_col_beg = A_rows[a_col_id];\n\n            // Iterate over the range of columns of B.\n            for (int b_col_it = b_col_beg; b_col_it < A_rows[a_col_id+1]; ++b_col_it)\n            {\n                int b_col_id = A_cols[b_col_it];\n\n                // Is it an off-diagonal element.\n                is_off_diagonal = b_col_id != a_col_id && b_col_id != a_row_id;\n                // Is it a strongly connected node.\n                is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                // Is it a coarse and strongly-connected column.\n                is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n\n                if(!is_coarse_strongly_connected) continue;\n\n                int hash = b_col_id % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == b_col_id) break;\n                    \n                    if(curr_key == SLOT_VACANT)\n                    {\n                        int old_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n                        if(old_key == SLOT_VACANT || old_key == b_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // XXX Could change to poll by ballot style compress\n    if(gid < unfine_set_size)\n    {\n        // Store the results.\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            int key = key_group_s[i];\n            if(key != SLOT_VACANT)\n            {\n                int ind = atomicAdd(&count_s[group_id], 1);\n                C_hat[C_hat_start[a_row_id] + ind] = key;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(gid < unfine_set_size)\n    {\n        // Store the results.\n        if(lane_id == 0)\n        {\n            C_hat_end[a_row_id] = C_hat_start[a_row_id] + count_s[group_id];\n        }\n    }\n}\n} // namespace selector\n\ntemplate <typename IndexType>\nvoid __global__  createCfMapGlobal(const IndexType *cf_map, int64_t *cf_map_global, const int64_t my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (int64_t) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\n#include <sm_utils.inl>\n\nenum { WARP_SIZE = 32, GRID_SIZE = 1024, SMEM_SIZE = 128 };\n\ntemplate< typename Value_type, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nfillS2ColIndices( const int A_num_rows,\n                  const int *__restrict cf_map,\n                  const int *__restrict C_hat,\n                  const int *__restrict C_hat_start,\n                  const int *__restrict C_hat_end,\n                  const int *__restrict S2_rows,\n                  int *S2_cols,\n                  int64_t *S2_global_cols,\n                  int64_t *cf_map_global)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows; a_row_id += NUM_WARPS_PER_GRID )\n    {\n        int coarse_fine_id = cf_map[a_row_id];\n\n        // Skip fine rows.\n        if ( coarse_fine_id < 0 )\n        {\n            continue;\n        }\n\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        int s2_col_it  = S2_rows[coarse_fine_id];\n\n        // Iterate over the columns of A.\n        for ( c_hat_it += lane_id ; utils::any( c_hat_it < c_hat_end ) ; c_hat_it += WARP_SIZE, s2_col_it += WARP_SIZE)\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int s2_col_id(-1);\n\n            if ( c_hat_it < c_hat_end )\n            {\n                s2_col_id = C_hat[c_hat_it];\n            }\n\n            if (s2_col_id != -1)\n            {\n                S2_cols[s2_col_it + lane_id] = cf_map[s2_col_id];\n\n                if (S2_global_cols != NULL)\n                {\n                    S2_global_cols[s2_col_it + lane_id] = cf_map_global[s2_col_id];\n                }\n            }\n        }\n    }\n}\n\n\n\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid Selector<TemplateConfig<AMGX_device, V, M, I> >::renumberAndCountCoarsePoints( IVector &cf_map,\n        int &num_coarse_points,\n        int num_rows)\n{\n    IVector mark(cf_map.size(), 0);\n    int blockSize = 128;\n    int num_blocks = std::min( 4096, (int) (cf_map.size() + blockSize - 1) / blockSize );\n\n    if (cf_map.size() > 0)\n    {\n        coarseMarkKernel <<< num_blocks, blockSize>>>(cf_map.raw(), mark.raw(), cf_map.size());\n        cudaCheckError();\n    }\n\n    // get the sequence of values\n    thrust_wrapper::inclusive_scan<AMGX_device>(mark.begin(), mark.end(), mark.begin());\n    cudaCheckError();\n\n    // assign to cf_map\n    if (cf_map.size() > 0)\n    {\n        modifyCoarseMapKernel <<< num_blocks, blockSize>>>(cf_map.raw(), mark.raw(), cf_map.size());\n        cudaCheckError();\n    }\n\n\n    num_coarse_points = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_rows, is_non_neg());\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid Selector<TemplateConfig<AMGX_host, V, M, I> >::renumberAndCountCoarsePoints( IVector &cf_map,\n        int &num_coarse_points,\n        int num_rows)\n{\n    num_coarse_points = 0;\n\n    for (int i = 0; i < num_rows; i++)\n    {\n        // if coarse\n        if (cf_map[i] == COARSE)\n        {\n            cf_map[i] = num_coarse_points++;\n        }\n    }\n}\n\n__global__\nvoid correctCfMapKernel(int *cf_map, const int *cf_map_scanned, const int *cf_map_s2, int size)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < size; tIdx += gridDim.x * blockDim.x)\n    {\n        if (cf_map[tIdx] == COARSE) // Coarse point\n        {\n            int coarse_id = cf_map_scanned[tIdx];\n            int coarse_id_s2 = cf_map_s2[coarse_id];\n            // if cf_map_s2 is STRONG_FINE, mark as COARSE\n            cf_map[tIdx] = (coarse_id_s2 == STRONG_FINE) ? COARSE : coarse_id_s2;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::correctCfMap(IVector &cf_map, IVector &cf_map_scanned, IVector &cf_map_S2)\n{\n    FatalError(\"CorrectCFMap not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::correctCfMap(IVector &cf_map, IVector &cf_map_scanned, IVector &cf_map_S2)\n{\n    // One thread per row of A\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const int cta_size = 128;\n    const int grid_size = std::min( 4096, ((int) cf_map.size() + cta_size - 1) / cta_size);\n    correctCfMapKernel <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_scanned.raw(), cf_map_S2.raw(), cf_map.size());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::createS2(Matrix_h &A,\n           Matrix_h &S2,\n           const BVector &s_con,\n           IVector &cf_map)\n{\n    FatalError(\"Aggressive PMIS selector not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size>\nvoid Selector<TemplateConfig<AMGX_device, V, M, I>>::compute_c_hat_opt_dispatch(\n        const Matrix_d &A,\n        const bool *s_con,\n        const int *C_hat_start,\n        int *C_hat,\n        int *C_hat_end,\n        int *cf_map,\n        IntVector &unfine_set)\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = unfine_set.size() / ngroups + 1;\n\n    cudaDeviceProp deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(int)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    cudaFuncSetAttribute(amgx::classical::selector::compute_c_hat_kernel_opt \n            <group_size, cta_size, hash_size>, \n            cudaFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    amgx::classical::selector::compute_c_hat_kernel_opt\n        <group_size, cta_size, hash_size> \n        <<<grid_size, cta_size, shmem_size>>>(\n            unfine_set.size(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            s_con,\n            C_hat_start,\n            C_hat,\n            C_hat_end,\n            cf_map,\n            unfine_set.raw());\n            cudaCheckError();\n}\n\n// Determines which nodes are not fine, hence excluded\n__global__ void set_unfine(int n, int* cf_map, int* unfine, int* unfine_offs)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    unfine[i] = (cf_map[i] >= 0) ? 1 : 0;\n\n    if(i == 0) \n    {\n        unfine_offs[0] = 0;\n    }\n}\n\n// Compresses indirection\n__global__ void unfine_set_fill(int n, int* unfine, int* unfine_offs, int* unfine_set)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    if(unfine[i]) \n    { \n        unfine_set[unfine_offs[i]] = i; \n    } \n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createS2(Matrix_d &A,\n        Matrix_d &S2,\n        const BVector &s_con,\n        IVector &cf_map)\n{\n    const int blockSize = 256;\n    // One thread per row of A\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n    int *cf_map_ptr = cf_map.raw();\n    Hash_Workspace<TConfig_d, int> exp_wk(true, GRID_SIZE);\n    IntVector C_hat_start( A.get_num_rows() + 1, 0 ), C_hat_end( A.get_num_rows() + 1, 0 );\n\n    int max_c_hat_size = 0;\n    int nunfine = 0;\n    IntVector unfine_set;\n\n    if(this->m_use_opt_kernels)\n    {\n        IntVector unfine(A.get_num_rows());\n        IntVector unfine_offs(A.get_num_rows()+1);\n        constexpr int nthreads = 128;\n        const int nblocks = A.get_num_rows() / nthreads + 1;\n        set_unfine<<<nblocks, nthreads>>>(A.get_num_rows(), cf_map.raw(), unfine.raw(), unfine_offs.raw());\n        cudaCheckError();\n        thrust_wrapper::inclusive_scan<AMGX_device>(unfine.begin(), unfine.end(), unfine_offs.begin()+1);\n\n        nunfine = unfine_offs[unfine_offs.size()-1];\n        if(nunfine > 0)\n        {\n            unfine_set.resize(nunfine);\n            unfine_set_fill<<<nblocks, nthreads>>>(A.get_num_rows(), unfine.raw(), unfine_offs.raw(), unfine_set.raw());\n            cudaCheckError();\n        }\n\n        const int CTA_SIZE  = 128;\n        int grid_size = nunfine/CTA_SIZE + 1;\n\n        amgx::classical::selector::estimate_c_hat_size_kernel_opt< CTA_SIZE > <<< grid_size, CTA_SIZE>>>(\n                nunfine,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw(),\n                unfine_set.raw());\n        cudaCheckError();\n\n        max_c_hat_size = thrust::reduce(C_hat_start.begin(), C_hat_start.end(), -1, thrust::maximum<int>());\n    }\n    else\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        int grid_size = A.get_num_rows()/NUM_WARPS + 1;\n\n        if ( avg_nz_per_row < 16 )\n        {\n            amgx::classical::selector::estimate_c_hat_size_kernel< 8, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n                cudaCheckError();\n        }\n        else\n        {\n            amgx::classical::selector::estimate_c_hat_size_kernel<CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n        }\n\n        cudaCheckError();\n    }\n\n    // Compute row offsets.\n    thrust_wrapper::exclusive_scan<AMGX_device>(C_hat_start.begin(), C_hat_start.end(), C_hat_start.begin());\n    cudaCheckError();\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    IntVector C_hat( nVals );\n\n    if(this->m_use_opt_kernels)\n    {\n        int hash_size = pow(2, ceil(log2(max_c_hat_size)));\n\n        if(nunfine > 0)\n        {\n            switch(hash_size)\n            {\n                case 1:\n                case 2:\n                case 4:\n                case 8:\n                case 16:\n                case 32:\n                    compute_c_hat_opt_dispatch<32, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 64:\n                    compute_c_hat_opt_dispatch<64, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 128:\n                    compute_c_hat_opt_dispatch<128, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 256:\n                    compute_c_hat_opt_dispatch<256, 16>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 512:\n                    compute_c_hat_opt_dispatch<512, 32>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                default:\n                    FatalError(\"Size of C_hat in a single row too large, solve with original solvers.\", AMGX_ERR_INTERNAL);\n            }\n        }\n    }\n    else\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        int attempt = 0;\n\n        for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n        {\n            // Double the amount of GMEM (if needed).\n            if ( attempt > 0 )\n            {\n                exp_wk.expand();\n            }\n\n            // Reset the status. \n            // TODO: Optimize with async copies.\n            int status = 0;\n            cudaMemcpy( exp_wk.get_status(), &status, sizeof(int), cudaMemcpyHostToDevice );\n            cudaCheckError();\n            // Compute the set C_hat.\n            int work_offset = GRID_SIZE * NUM_WARPS;\n            cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n            cudaCheckError();\n\n            // Run the computation.\n            if ( avg_nz_per_row < 16 )\n            {\n                amgx::classical::selector::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n                    cudaCheckError();\n            }\n            else\n            {\n                amgx::classical::selector::compute_c_hat_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n            }\n\n            cudaCheckError();\n            // Read the result from count_non_zeroes.\n            cudaMemcpy( &status, exp_wk.get_status(), sizeof(int), cudaMemcpyDeviceToHost );\n            cudaCheckError();\n            done = status == 0;\n        }\n    }\n    // get pointers to data\n    int *C_hat_ptr = C_hat.raw();\n    int *C_hat_start_ptr = C_hat_start.raw();\n    int *C_hat_end_ptr = C_hat_end.raw();\n    int S2_num_rows;;\n    DistributedArranger<TConfig_d> *prep = NULL;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        S2_num_rows = num_owned_coarse_pts;\n        prep->initialize_manager(A, S2, num_owned_coarse_pts);\n    }\n    else\n    {\n        S2_num_rows = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n    }\n\n    // count the number of non-zeros in the interpolation matrix\n    int numBlocks = (int) (A.get_num_rows() + blockSize - 1) / blockSize;\n    IntVector nonZeroOffsets(S2_num_rows + 1);\n    IntVector nonZerosPerRow(S2_num_rows);\n    // Updating the number of nonZeros for your own coarse rows\n    int *nonZerosPerRow_ptr = nonZerosPerRow.raw();\n    nonZerosPerCoarseRowKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map.raw(), C_hat_start_ptr, C_hat_end_ptr,\n            nonZerosPerRow_ptr);\n    cudaCheckError();\n    // get total with a reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[S2_num_rows] = nonZeros;\n    // resize S2\n    S2.resize(0, 0, 0, 1);\n    S2.addProps(CSR);\n    S2.set_num_rows(S2_num_rows);\n    S2.set_num_cols(S2_num_rows);\n    S2.set_num_nz(nonZeros);\n    S2.row_offsets.resize(S2_num_rows + 1);\n    S2.col_indices.resize(nonZeros);\n    S2.diag.resize(S2_num_rows);\n    S2.set_block_dimx(A.get_block_dimx());\n    S2.set_block_dimy(A.get_block_dimy());\n    cudaCheckError();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), S2.row_offsets.begin());\n    cudaCheckError();\n    {\n        const int CTA_SIZE  = 256;\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        if (!A.is_matrix_distributed())\n        {\n            int grid_size = (A.get_num_rows() / NUM_WARPS) + 1;\n\n            fillS2ColIndices<Value_type, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                cf_map.raw(),\n                C_hat.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                S2.row_offsets.raw(),\n                S2.col_indices.raw(),\n                NULL,\n                NULL);\n                cudaCheckError();\n        }\n        else\n        {\n            I64Vector_d cf_map_global(cf_map.size());\n            int num_owned_fine_pts = A.get_num_rows();\n            int my_rank = A.manager->global_id();\n            const int cta_size = 128;\n            int grid_size = (num_owned_fine_pts + cta_size - 1) / cta_size;\n            createCfMapGlobal <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), S2.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n            cudaCheckError();\n\n            // Exchange the cf_map_global so that we know the coarse global id of halo nodes\n            cf_map_global.dirtybit = 1;\n            A.manager->exchange_halo_2ring(cf_map_global, cf_map_global.tag);\n            I64Vector_d S2_col_indices_global(S2.col_indices.size());\n\n            grid_size = (A.get_num_rows() / NUM_WARPS) + 1;\n            fillS2ColIndices<Value_type, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                cf_map.raw(),\n                C_hat.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                S2.row_offsets.raw(),\n                S2.col_indices.raw(),\n                S2_col_indices_global.raw(),\n                cf_map_global.raw());\n            cudaCheckError();\n            prep->initialize_manager_from_global_col_indices(S2, S2_col_indices_global);\n        }\n\n        if (prep != NULL) { delete prep; }\n\n        cudaCheckError();\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::demoteStrongEdges(const Matrix_h &A,\n        const FVector &weights,\n        BVector &s_con,\n        const IVector &cf_map,\n        const IndexType base_offset)\n{\n    for ( int tIdx = 0; tIdx < A.get_num_rows() - base_offset; tIdx++)\n    {\n        const int offset = A.row_offsets[tIdx];\n        const int numj = A.row_offsets[tIdx + 1] - offset;\n\n        if (cf_map[base_offset + tIdx] == COARSE)\n        {\n            // for each column\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // skip weakly connected points\n\n                int jcol = A.col_indices[j];\n\n                if (cf_map[jcol] == COARSE) // coarse neighbour\n                {\n                    s_con[j] = false; //make it weak\n                }\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::demoteStrongEdges(const Matrix_d &A,\n        const FVector &weights,\n        BVector &s_con,\n        const IVector &cf_map,\n        const IndexType offset)\n{\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (((A.get_num_rows() - offset) + blockSize - 1) / blockSize));\n    const int numRows = (int)(A.get_num_rows() - offset);\n    resolve_boundary <<< blockSize, numBlocks>>>(A.row_offsets.raw() + offset, A.col_indices.raw(), s_con.raw(), cf_map.raw(), offset, A.get_num_rows() - offset);\n    cudaCheckError();\n}\n\n\n\ntemplate<class TConfig>\nstd::map<std::string, SelectorFactory<TConfig>*> &\nSelectorFactory<TConfig>::getFactories( )\n{\n    static std::map<std::string, SelectorFactory<TConfig>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::registerFactory(std::string name, SelectorFactory<TConfig> *f)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::unregisterFactory(std::string name)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    SelectorFactory<TConfig> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::unregisterFactories( )\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        SelectorFactory<TConfig> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class TConfig>\nSelector<TConfig> *SelectorFactory<TConfig>::allocate(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    std::string selector = cfg.getParameter<std::string>(\"selector\", cfg_scope);\n    typename std::map<std::string, SelectorFactory<TConfig> *>::const_iterator it = factories.find(selector);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + selector + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, cfg_scope);\n};\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class SelectorFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace classical\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <types.h>\n#include <classical/selectors/selector.h>\n#include <thrust/count.h>\n#include <hash_workspace.h>\n#include <thrust_wrapper.h>\n\n#include <algorithm>\n#include <assert.h>\n\n#include <device_properties.h>\n\nnamespace amgx\n{\nnamespace classical\n{\nstruct is_non_neg\n{\n    __host__ __device__\n    bool operator()(const int &x)\n    {\n        return x >= 0;\n    }\n};\n\n__global__\nvoid coarseMarkKernel(int *cf_map, int *mark, const int numEntries)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < numEntries; tIdx += gridDim.x * blockDim.x)\n    {\n        if (cf_map[tIdx] == COARSE)\n        {\n            mark[tIdx] = 1;\n        }\n    }\n}\n\n__global__\nvoid modifyCoarseMapKernel(int *cf_map, int *mark, const int numEntries)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < numEntries; tIdx += gridDim.x * blockDim.x)\n    {\n        // entry mark[i] is 0 if fine, desired #+1 if coarse\n        if (cf_map[tIdx] == COARSE)\n        {\n            cf_map[tIdx] = mark[tIdx] - 1;\n        }\n    }\n}\n\n/*\n * Count the # of non-zeros per row\n */\n__global__\nvoid nonZerosPerCoarseRowKernel(const int num_rows, const int *cf_map, const int *C_hat_start,\n                                const int *C_hat_end, int *nonZerosPerCoarseRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        // If coarse point\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZerosPerCoarseRow[cf_map[tIdx]] = C_hat_end[tIdx] - C_hat_start[tIdx];\n        }\n    }\n}\n\n\ntemplate <typename IndexType>\n__global__\nvoid resolve_boundary(const IndexType *offsets, const IndexType *column_indices, bool *s_con,\n                      const int *cf_map_in, const int base_offset, const int numRows)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        if (cf_map_in[base_offset + tIdx] == COARSE)\n        {\n            // for each column\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // skip weakly connected points\n\n                int jcol = column_indices[j];\n\n                if (cf_map_in[jcol] == COARSE) // coarse neighbour\n                {\n                    s_con[j] = false; //make it weak\n                }\n            }\n        }\n    }\n}\n\nnamespace selector\n{\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncthreads();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of B.\n                int b_col_it  = A_rows[b_row_id + 0];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id + 0];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n\ntemplate< int CTA_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel_opt( const int nunfine,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets,\n                            int* unfine_set )\n{\n    // First threads load the row IDs of A needed by the CTA...\n    int gid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // Loop over rows of A.\n    if (gid >= nunfine) return;\n\n    int a_row_id = unfine_set[gid];\n\n    int count = 0;\n    // Iterate over the columns of A to build C_hat.\n    for ( int a_col_it = A_rows[a_row_id  ]; a_col_it < A_rows[a_row_id + 1]; ++a_col_it )\n    {\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int a_col_id = A_cols[a_col_it];\n        // Is it an off-diagonal element.\n        bool is_off_diagonal = a_col_id != a_row_id;\n        // Is it strongly connected ?\n        bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n        // Is it fine.\n        bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n        // Is it isolated.\n        bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n        // Is it a coarse and strongly-connected column.\n        bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n\n        if(is_coarse_strongly_connected) \n        {\n            count++;\n        }\n\n        // Is it a fine and strongly-connected column.\n        bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n\n        if (!is_fine_strongly_connected ) continue;\n\n        int b_col_beg = A_rows[a_col_id];\n\n        // _iterate over the range of columns of B.\n        for (int b_col_it = b_col_beg; b_col_it < A_rows[a_col_id+1]; ++b_col_it)\n        {\n            // The ID of the column.\n            int b_col_id = A_cols[b_col_it];\n\n            // Is it an off-diagonal element.\n            is_off_diagonal = b_col_id != a_col_id;\n            // Is it a coarse and strongly-connected column.\n            is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n            // Push coarse and strongly connected nodes in the set.\n            if(is_coarse_strongly_connected)\n            {\n                count++;\n            }\n        }\n    }\n\n    C_hat_offsets[a_row_id] = count;\n}\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        __syncthreads();\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n        __syncthreads();\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            //if (a_row_id == 7) printf(\"inserting %d\\n\",item);\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of the row of B.\n                int b_col_it  = A_rows[uniform_b_row_id + 0];\n                int b_col_end = A_rows[uniform_b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                    //if (a_row_id == 7) printf(\"inserting %d\\n\",b_item);\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store(&C_hat[c_col_it]);\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id < 0)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            //if (a_row_id == 7) printf(\"inserting item %d, is_off_diagonal = %d, \\n\",item,is_off_diagonal);\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[uniform_b_row_id + 0];\n                    b_col_end = A_rows[uniform_b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id && b_col_id != a_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                    //if (a_row_id == 7) printf(\"inserting b_item %d, is_off_diagonal = %d, \\n\",b_item,is_off_diagonal);\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store(&C_hat[c_col_it]) ;\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE>\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel_opt( int unfine_set_size,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_end,\n                      int *__restrict cf_map,\n                      int *__restrict unfine_set)\n{\n    constexpr int SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    const int gid = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Hash container storage\n    int* key_s = (int*)s;\n    int* key_group_s = &key_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n    }\n\n    int* count_s = (int*)&key_s[ngroups*HASH_SIZE];\n    if(lane_id == 0)\n    {\n        count_s[group_id] = 0;\n    }\n\n    __syncthreads();\n\n    int a_row_id = -1;\n\n    if(gid < unfine_set_size)\n    {\n        // One row of A per group\n        a_row_id = unfine_set[gid];\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = A_rows[a_row_id] + lane_id ; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE )\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n\n            if(is_coarse_strongly_connected) \n            {\n                int hash = a_col_id % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == a_col_id) break;\n\n                    if(curr_key == SLOT_VACANT)\n                    {\n                        int old_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, a_col_id);\n                        if(old_key == SLOT_VACANT || old_key == a_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n\n            if(!is_fine_strongly_connected) continue;\n\n            // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n            int b_col_beg = A_rows[a_col_id];\n\n            // Iterate over the range of columns of B.\n            for (int b_col_it = b_col_beg; b_col_it < A_rows[a_col_id+1]; ++b_col_it)\n            {\n                int b_col_id = A_cols[b_col_it];\n\n                // Is it an off-diagonal element.\n                is_off_diagonal = b_col_id != a_col_id && b_col_id != a_row_id;\n                // Is it a strongly connected node.\n                is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                // Is it a coarse and strongly-connected column.\n                is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n\n                if(!is_coarse_strongly_connected) continue;\n\n                int hash = b_col_id % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == b_col_id) break;\n                    \n                    if(curr_key == SLOT_VACANT)\n                    {\n                        int old_key = atomicCAS(&key_group_s[hash], SLOT_VACANT, b_col_id);\n                        if(old_key == SLOT_VACANT || old_key == b_col_id)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // XXX Could change to poll by ballot style compress\n    if(gid < unfine_set_size)\n    {\n        // Store the results.\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            int key = key_group_s[i];\n            if(key != SLOT_VACANT)\n            {\n                int ind = atomicAdd(&count_s[group_id], 1);\n                C_hat[C_hat_start[a_row_id] + ind] = key;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(gid < unfine_set_size)\n    {\n        // Store the results.\n        if(lane_id == 0)\n        {\n            C_hat_end[a_row_id] = C_hat_start[a_row_id] + count_s[group_id];\n        }\n    }\n}\n} // namespace selector\n\ntemplate <typename IndexType>\nvoid __global__  createCfMapGlobal(const IndexType *cf_map, int64_t *cf_map_global, const int64_t my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (int64_t) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\n#include <sm_utils.inl>\n\nenum { WARP_SIZE = 32, GRID_SIZE = 1024, SMEM_SIZE = 128 };\n\ntemplate< typename Value_type, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nfillS2ColIndices( const int A_num_rows,\n                  const int *__restrict cf_map,\n                  const int *__restrict C_hat,\n                  const int *__restrict C_hat_start,\n                  const int *__restrict C_hat_end,\n                  const int *__restrict S2_rows,\n                  int *S2_cols,\n                  int64_t *S2_global_cols,\n                  int64_t *cf_map_global)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows; a_row_id += NUM_WARPS_PER_GRID )\n    {\n        int coarse_fine_id = cf_map[a_row_id];\n\n        // Skip fine rows.\n        if ( coarse_fine_id < 0 )\n        {\n            continue;\n        }\n\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        int s2_col_it  = S2_rows[coarse_fine_id];\n\n        // Iterate over the columns of A.\n        for ( c_hat_it += lane_id ; utils::any( c_hat_it < c_hat_end ) ; c_hat_it += WARP_SIZE, s2_col_it += WARP_SIZE)\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int s2_col_id(-1);\n\n            if ( c_hat_it < c_hat_end )\n            {\n                s2_col_id = C_hat[c_hat_it];\n            }\n\n            if (s2_col_id != -1)\n            {\n                S2_cols[s2_col_it + lane_id] = cf_map[s2_col_id];\n\n                if (S2_global_cols != NULL)\n                {\n                    S2_global_cols[s2_col_it + lane_id] = cf_map_global[s2_col_id];\n                }\n            }\n        }\n    }\n}\n\n\n\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid Selector<TemplateConfig<AMGX_device, V, M, I> >::renumberAndCountCoarsePoints( IVector &cf_map,\n        int &num_coarse_points,\n        int num_rows)\n{\n    IVector mark(cf_map.size(), 0);\n    int blockSize = 128;\n    int num_blocks = std::min( 4096, (int) (cf_map.size() + blockSize - 1) / blockSize );\n\n    if (cf_map.size() > 0)\n    {\n        coarseMarkKernel <<< num_blocks, blockSize>>>(cf_map.raw(), mark.raw(), cf_map.size());\n        cudaCheckError();\n    }\n\n    // get the sequence of values\n    thrust_wrapper::inclusive_scan<AMGX_device>(mark.begin(), mark.end(), mark.begin());\n    cudaCheckError();\n\n    // assign to cf_map\n    if (cf_map.size() > 0)\n    {\n        modifyCoarseMapKernel <<< num_blocks, blockSize>>>(cf_map.raw(), mark.raw(), cf_map.size());\n        cudaCheckError();\n    }\n\n\n    num_coarse_points = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_rows, is_non_neg());\n    cudaCheckError();\n}\n\n\ntemplate< AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I >\nvoid Selector<TemplateConfig<AMGX_host, V, M, I> >::renumberAndCountCoarsePoints( IVector &cf_map,\n        int &num_coarse_points,\n        int num_rows)\n{\n    num_coarse_points = 0;\n\n    for (int i = 0; i < num_rows; i++)\n    {\n        // if coarse\n        if (cf_map[i] == COARSE)\n        {\n            cf_map[i] = num_coarse_points++;\n        }\n    }\n}\n\n__global__\nvoid correctCfMapKernel(int *cf_map, const int *cf_map_scanned, const int *cf_map_s2, int size)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < size; tIdx += gridDim.x * blockDim.x)\n    {\n        if (cf_map[tIdx] == COARSE) // Coarse point\n        {\n            int coarse_id = cf_map_scanned[tIdx];\n            int coarse_id_s2 = cf_map_s2[coarse_id];\n            // if cf_map_s2 is STRONG_FINE, mark as COARSE\n            cf_map[tIdx] = (coarse_id_s2 == STRONG_FINE) ? COARSE : coarse_id_s2;\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::correctCfMap(IVector &cf_map, IVector &cf_map_scanned, IVector &cf_map_S2)\n{\n    FatalError(\"CorrectCFMap not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::correctCfMap(IVector &cf_map, IVector &cf_map_scanned, IVector &cf_map_S2)\n{\n    // One thread per row of A\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const int cta_size = 128;\n    const int grid_size = std::min( 4096, ((int) cf_map.size() + cta_size - 1) / cta_size);\n    correctCfMapKernel <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_scanned.raw(), cf_map_S2.raw(), cf_map.size());\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::createS2(Matrix_h &A,\n           Matrix_h &S2,\n           const BVector &s_con,\n           IVector &cf_map)\n{\n    FatalError(\"Aggressive PMIS selector not implemented on host\", AMGX_ERR_NOT_IMPLEMENTED);\n}\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size>\nvoid Selector<TemplateConfig<AMGX_device, V, M, I>>::compute_c_hat_opt_dispatch(\n        const Matrix_d &A,\n        const bool *s_con,\n        const int *C_hat_start,\n        int *C_hat,\n        int *C_hat_end,\n        int *cf_map,\n        IntVector &unfine_set)\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = unfine_set.size() / ngroups + 1;\n\n    hipDeviceProp_t deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(int)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    hipFuncSetAttribute(reinterpret_cast<const void*>(amgx::classical::selector::compute_c_hat_kernel_opt \n            <group_size, cta_size, hash_size>), \n            hipFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    amgx::classical::selector::compute_c_hat_kernel_opt\n        <group_size, cta_size, hash_size> \n        <<<grid_size, cta_size, shmem_size>>>(\n            unfine_set.size(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            s_con,\n            C_hat_start,\n            C_hat,\n            C_hat_end,\n            cf_map,\n            unfine_set.raw());\n            cudaCheckError();\n}\n\n// Determines which nodes are not fine, hence excluded\n__global__ void set_unfine(int n, int* cf_map, int* unfine, int* unfine_offs)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    unfine[i] = (cf_map[i] >= 0) ? 1 : 0;\n\n    if(i == 0) \n    {\n        unfine_offs[0] = 0;\n    }\n}\n\n// Compresses indirection\n__global__ void unfine_set_fill(int n, int* unfine, int* unfine_offs, int* unfine_set)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    if(unfine[i]) \n    { \n        unfine_set[unfine_offs[i]] = i; \n    } \n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::createS2(Matrix_d &A,\n        Matrix_d &S2,\n        const BVector &s_con,\n        IVector &cf_map)\n{\n    const int blockSize = 256;\n    // One thread per row of A\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n    int *cf_map_ptr = cf_map.raw();\n    Hash_Workspace<TConfig_d, int> exp_wk(true, GRID_SIZE);\n    IntVector C_hat_start( A.get_num_rows() + 1, 0 ), C_hat_end( A.get_num_rows() + 1, 0 );\n\n    int max_c_hat_size = 0;\n    int nunfine = 0;\n    IntVector unfine_set;\n\n    if(this->m_use_opt_kernels)\n    {\n        IntVector unfine(A.get_num_rows());\n        IntVector unfine_offs(A.get_num_rows()+1);\n        constexpr int nthreads = 128;\n        const int nblocks = A.get_num_rows() / nthreads + 1;\n        set_unfine<<<nblocks, nthreads>>>(A.get_num_rows(), cf_map.raw(), unfine.raw(), unfine_offs.raw());\n        cudaCheckError();\n        thrust_wrapper::inclusive_scan<AMGX_device>(unfine.begin(), unfine.end(), unfine_offs.begin()+1);\n\n        nunfine = unfine_offs[unfine_offs.size()-1];\n        if(nunfine > 0)\n        {\n            unfine_set.resize(nunfine);\n            unfine_set_fill<<<nblocks, nthreads>>>(A.get_num_rows(), unfine.raw(), unfine_offs.raw(), unfine_set.raw());\n            cudaCheckError();\n        }\n\n        const int CTA_SIZE  = 128;\n        int grid_size = nunfine/CTA_SIZE + 1;\n\n        amgx::classical::selector::estimate_c_hat_size_kernel_opt< CTA_SIZE > <<< grid_size, CTA_SIZE>>>(\n                nunfine,\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw(),\n                unfine_set.raw());\n        cudaCheckError();\n\n        max_c_hat_size = thrust::reduce(C_hat_start.begin(), C_hat_start.end(), -1, thrust::maximum<int>());\n    }\n    else\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        int grid_size = A.get_num_rows()/NUM_WARPS + 1;\n\n        if ( avg_nz_per_row < 16 )\n        {\n            amgx::classical::selector::estimate_c_hat_size_kernel< 8, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n                cudaCheckError();\n        }\n        else\n        {\n            amgx::classical::selector::estimate_c_hat_size_kernel<CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n        }\n\n        cudaCheckError();\n    }\n\n    // Compute row offsets.\n    thrust_wrapper::exclusive_scan<AMGX_device>(C_hat_start.begin(), C_hat_start.end(), C_hat_start.begin());\n    cudaCheckError();\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    IntVector C_hat( nVals );\n\n    if(this->m_use_opt_kernels)\n    {\n        int hash_size = pow(2, ceil(log2(max_c_hat_size)));\n\n        if(nunfine > 0)\n        {\n            switch(hash_size)\n            {\n                case 1:\n                case 2:\n                case 4:\n                case 8:\n                case 16:\n                case 32:\n                    compute_c_hat_opt_dispatch<32, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 64:\n                    compute_c_hat_opt_dispatch<64, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 128:\n                    compute_c_hat_opt_dispatch<128, 8>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 256:\n                    compute_c_hat_opt_dispatch<256, 16>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                case 512:\n                    compute_c_hat_opt_dispatch<512, 32>(A, s_con.raw(), C_hat_start.raw(), C_hat.raw(), C_hat_end.raw(), cf_map.raw(), unfine_set);\n                    break;\n                default:\n                    FatalError(\"Size of C_hat in a single row too large, solve with original solvers.\", AMGX_ERR_INTERNAL);\n            }\n        }\n    }\n    else\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        int attempt = 0;\n\n        for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n        {\n            // Double the amount of GMEM (if needed).\n            if ( attempt > 0 )\n            {\n                exp_wk.expand();\n            }\n\n            // Reset the status. \n            // TODO: Optimize with async copies.\n            int status = 0;\n            hipMemcpy( exp_wk.get_status(), &status, sizeof(int), hipMemcpyHostToDevice );\n            cudaCheckError();\n            // Compute the set C_hat.\n            int work_offset = GRID_SIZE * NUM_WARPS;\n            hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n            cudaCheckError();\n\n            // Run the computation.\n            if ( avg_nz_per_row < 16 )\n            {\n                amgx::classical::selector::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n                    cudaCheckError();\n            }\n            else\n            {\n                amgx::classical::selector::compute_c_hat_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n            }\n\n            cudaCheckError();\n            // Read the result from count_non_zeroes.\n            hipMemcpy( &status, exp_wk.get_status(), sizeof(int), hipMemcpyDeviceToHost );\n            cudaCheckError();\n            done = status == 0;\n        }\n    }\n    // get pointers to data\n    int *C_hat_ptr = C_hat.raw();\n    int *C_hat_start_ptr = C_hat_start.raw();\n    int *C_hat_end_ptr = C_hat_end.raw();\n    int S2_num_rows;;\n    DistributedArranger<TConfig_d> *prep = NULL;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        S2_num_rows = num_owned_coarse_pts;\n        prep->initialize_manager(A, S2, num_owned_coarse_pts);\n    }\n    else\n    {\n        S2_num_rows = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n    }\n\n    // count the number of non-zeros in the interpolation matrix\n    int numBlocks = (int) (A.get_num_rows() + blockSize - 1) / blockSize;\n    IntVector nonZeroOffsets(S2_num_rows + 1);\n    IntVector nonZerosPerRow(S2_num_rows);\n    // Updating the number of nonZeros for your own coarse rows\n    int *nonZerosPerRow_ptr = nonZerosPerRow.raw();\n    nonZerosPerCoarseRowKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map.raw(), C_hat_start_ptr, C_hat_end_ptr,\n            nonZerosPerRow_ptr);\n    cudaCheckError();\n    // get total with a reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[S2_num_rows] = nonZeros;\n    // resize S2\n    S2.resize(0, 0, 0, 1);\n    S2.addProps(CSR);\n    S2.set_num_rows(S2_num_rows);\n    S2.set_num_cols(S2_num_rows);\n    S2.set_num_nz(nonZeros);\n    S2.row_offsets.resize(S2_num_rows + 1);\n    S2.col_indices.resize(nonZeros);\n    S2.diag.resize(S2_num_rows);\n    S2.set_block_dimx(A.get_block_dimx());\n    S2.set_block_dimy(A.get_block_dimy());\n    cudaCheckError();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), S2.row_offsets.begin());\n    cudaCheckError();\n    {\n        const int CTA_SIZE  = 256;\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        if (!A.is_matrix_distributed())\n        {\n            int grid_size = (A.get_num_rows() / NUM_WARPS) + 1;\n\n            fillS2ColIndices<Value_type, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                cf_map.raw(),\n                C_hat.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                S2.row_offsets.raw(),\n                S2.col_indices.raw(),\n                NULL,\n                NULL);\n                cudaCheckError();\n        }\n        else\n        {\n            I64Vector_d cf_map_global(cf_map.size());\n            int num_owned_fine_pts = A.get_num_rows();\n            int my_rank = A.manager->global_id();\n            const int cta_size = 128;\n            int grid_size = (num_owned_fine_pts + cta_size - 1) / cta_size;\n            createCfMapGlobal <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), S2.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n            cudaCheckError();\n\n            // Exchange the cf_map_global so that we know the coarse global id of halo nodes\n            cf_map_global.dirtybit = 1;\n            A.manager->exchange_halo_2ring(cf_map_global, cf_map_global.tag);\n            I64Vector_d S2_col_indices_global(S2.col_indices.size());\n\n            grid_size = (A.get_num_rows() / NUM_WARPS) + 1;\n            fillS2ColIndices<Value_type, CTA_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                cf_map.raw(),\n                C_hat.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                S2.row_offsets.raw(),\n                S2.col_indices.raw(),\n                S2_col_indices_global.raw(),\n                cf_map_global.raw());\n            cudaCheckError();\n            prep->initialize_manager_from_global_col_indices(S2, S2_col_indices_global);\n        }\n\n        if (prep != NULL) { delete prep; }\n\n        cudaCheckError();\n    }\n}\n\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::demoteStrongEdges(const Matrix_h &A,\n        const FVector &weights,\n        BVector &s_con,\n        const IVector &cf_map,\n        const IndexType base_offset)\n{\n    for ( int tIdx = 0; tIdx < A.get_num_rows() - base_offset; tIdx++)\n    {\n        const int offset = A.row_offsets[tIdx];\n        const int numj = A.row_offsets[tIdx + 1] - offset;\n\n        if (cf_map[base_offset + tIdx] == COARSE)\n        {\n            // for each column\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // skip weakly connected points\n\n                int jcol = A.col_indices[j];\n\n                if (cf_map[jcol] == COARSE) // coarse neighbour\n                {\n                    s_con[j] = false; //make it weak\n                }\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::demoteStrongEdges(const Matrix_d &A,\n        const FVector &weights,\n        BVector &s_con,\n        const IVector &cf_map,\n        const IndexType offset)\n{\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (((A.get_num_rows() - offset) + blockSize - 1) / blockSize));\n    const int numRows = (int)(A.get_num_rows() - offset);\n    resolve_boundary <<< blockSize, numBlocks>>>(A.row_offsets.raw() + offset, A.col_indices.raw(), s_con.raw(), cf_map.raw(), offset, A.get_num_rows() - offset);\n    cudaCheckError();\n}\n\n\n\ntemplate<class TConfig>\nstd::map<std::string, SelectorFactory<TConfig>*> &\nSelectorFactory<TConfig>::getFactories( )\n{\n    static std::map<std::string, SelectorFactory<TConfig>*> s_factories;\n    return s_factories;\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::registerFactory(std::string name, SelectorFactory<TConfig> *f)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::const_iterator it = factories.find(name);\n\n    if (it != factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has already been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    factories[name] = f;\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::unregisterFactory(std::string name)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::iterator it = factories.find(name);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + name + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    SelectorFactory<TConfig> *factory = it->second;\n    assert( factory != NULL );\n    delete factory;\n    factories.erase(it);\n}\n\ntemplate<class TConfig>\nvoid SelectorFactory<TConfig>::unregisterFactories( )\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    typename std::map<std::string, SelectorFactory<TConfig> *>::iterator it = factories.begin( );\n\n    for ( ; it != factories.end( ) ; )\n    {\n        SelectorFactory<TConfig> *factory = it->second;\n        assert( factory != NULL );\n        it++;\n        delete factory;\n    }\n\n    factories.clear( );\n}\n\ntemplate<class TConfig>\nSelector<TConfig> *SelectorFactory<TConfig>::allocate(AMG_Config &cfg, const std::string &cfg_scope)\n{\n    std::map<std::string, SelectorFactory<TConfig>*> &factories = getFactories( );\n    std::string selector = cfg.getParameter<std::string>(\"selector\", cfg_scope);\n    typename std::map<std::string, SelectorFactory<TConfig> *>::const_iterator it = factories.find(selector);\n\n    if (it == factories.end())\n    {\n        std::string error = \"SelectorFactory '\" + selector + \"' has not been registered\\n\";\n        FatalError(error.c_str(), AMGX_ERR_CORE);\n    }\n\n    return it->second->create(cfg, cfg_scope);\n};\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class SelectorFactory<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\nAMGX_FORCOMPLEX_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n} // namespace classical\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/selectors/selector.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <classical/selectors/pmis.h>\n#include <classical/interpolators/common.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\nnamespace amgx\n{\n\nnamespace classical\n{\n/*************************************************************************\n * marks the strongest connected (and indepent) points as coarse\n ************************************************************************/\n\ntemplate< AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec >\nstruct MarkAdditionalPoints\n{\n    static const AMGX_VecPrecision vecPrec = (AMGX_VecPrecision)t_indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, vecPrec, t_matPrec, t_indPrec> > IndVector;\n    static int markAdditionalCoarsePoints(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A,\n                                          const Vector<TemplateConfig<AMGX_host, AMGX_vecFloat, t_matPrec, t_indPrec> > &weights,\n                                          const Vector<TemplateConfig<AMGX_host, AMGX_vecBool, t_matPrec, t_indPrec> > &s_con,\n                                          IndVector &cf_map_in,\n                                          IndVector &cf_map_out,\n                                          IndVector &mark)\n    {\n        int num_coarse = 0;\n\n        //deep copy cf_map_in into cf_map_out\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            cf_map_out[i] = cf_map_in[i];\n        }\n\n        // set all unassigned points to be coarse\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            mark[i] = 0;\n\n            if (cf_map_in[i] == UNASSIGNED)\n            {\n                cf_map_out[i] = COARSE;\n                mark[i] = 1;\n            }\n        }\n\n        // now run through newly marked coarse points and make them unassigned again if necessary\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            if (mark[i] > 0) // newly marked C point\n            {\n                for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n                {\n                    if (!s_con[j]) { continue; } // skip weak connections\n\n                    int jcol = A.col_indices[j];\n\n                    if (mark[jcol] && weights[jcol] > 1) // other point is also newly coarse\n                    {\n                        // if my weight is larger, they can't be a coarse point\n                        if (weights[i] > weights[jcol])\n                        {\n                            cf_map_out[jcol] = UNASSIGNED;\n                        }\n                        // if their weight is large, I can't be a coarse point\n                        else if (weights[jcol] > weights[i])\n                        {\n                            cf_map_out[i] = UNASSIGNED;\n                        }\n                    }\n                }\n            }\n        }\n\n        // finally count the number of coarse points\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            num_coarse += static_cast<int>(cf_map_out[i] == COARSE);\n        }\n\n        return num_coarse;\n    }\n\n    /*************************************************************************\n     * marks all neighbors of coarse points as fine\n     ************************************************************************/\n    static int markAdditionalFinePoints(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A,\n                                        const Vector<TemplateConfig<AMGX_host, AMGX_vecBool, t_matPrec, t_indPrec> > &s_con,\n                                        IndVector &cf_map_in,\n                                        IndVector &cf_map_out)\n    {\n        int num_fine = 0;\n\n        //deep copy cf_map_in into cf_map_out\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            cf_map_out[i] = cf_map_in[i];\n        }\n\n        //for each row of the matrix\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            //if already fine add it to the count\n            num_fine += static_cast<int>(cf_map_in[i] == FINE);\n\n            if (cf_map_in[i] == UNASSIGNED)\n            {\n                // for each off diagonal, if I have a new strongly connected coarse neighbour\n                // set to fine\n                for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n                {\n                    if (!s_con[j]) { continue; } //skip weakly connected points\n\n                    int jcol = A.col_indices[j];\n\n                    if (cf_map_in[jcol] == COARSE)\n                    {\n                        cf_map_out[i] = FINE;\n                        num_fine++;\n                        break;\n                    }\n                }\n            }\n        }\n\n        return num_fine;\n    }\n};\n\n/*************************************************************************\n * mark coarse and fine points\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PMIS_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1(Matrix_h &A,\n                           FVector &weights,\n                           const BVector &s_con,\n                           IVector &cf_map,\n                           IVector &scratch,\n                           int cf_map_init)\n{\n    int num_coarse = 0, num_fine = 0;\n    int num_strong_fine = 0;\n\n    //mark all unconnected points as fine\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int numj = A.row_offsets[i + 1] - A.row_offsets[i];\n\n        if (numj == 0) //if there are no columns\n        {\n            cf_map[i] = FINE;\n        }\n        else if (numj == 1 && A.col_indices[A.row_offsets[i]] == i) //if there is only 1 column and it is the diagonal\n        {\n            cf_map[i] = FINE;\n        }\n        else if (weights[i] < 1) // no point strongly depends on point\n        {\n            cf_map[i] = FINE;\n        }\n        else\n        {\n            cf_map[i] = UNASSIGNED;\n        }\n\n        // check to see if a point has no strong connections to anyone\n        // needs more justification / knowledge of everywhere this condition is applied\n        bool isolated = true;\n\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[i] = STRONG_FINE;\n            num_strong_fine++;\n            weights[i] = 0.F;\n        }\n    }\n\n    // temp storage for marking coarse points\n    IntVector mark(A.get_num_rows(), 0);\n    int iterations = 0;\n\n    do\n    {\n        ++iterations;\n        num_coarse = MarkAdditionalPoints<t_vecPrec, t_matPrec, t_indPrec>::markAdditionalCoarsePoints(A, weights, s_con, cf_map, scratch, mark);\n        num_fine = MarkAdditionalPoints<t_vecPrec, t_matPrec, t_indPrec>::markAdditionalFinePoints(A, s_con, scratch, cf_map);\n    }\n    while (num_coarse + num_fine + num_strong_fine < A.get_num_rows());\n\n    // now we have \\hat{C} and the diagonals, we can start calculating the weights\n    // remap cf_map (to be like in distance1)\n}\n\n/*************************************************************************\n * device kernels\n ************************************************************************/\n\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingKernel(const IndexType *offsets, const IndexType *column_indices,\n                          const int numRows, int *cf_map, float *weights, const bool *s_con)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else\n        {\n            cf_map[tIdx] = UNASSIGNED;\n        }\n\n        bool isolated = true;\n\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[tIdx] = STRONG_FINE;\n            weights[tIdx] = 0.F;\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingCFInit3Kernel(const IndexType *offsets, const IndexType *column_indices,\n                                 const int numRows, int *cf_map, float *weights, const bool *s_con)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else\n        {\n            cf_map[tIdx] = UNASSIGNED;\n        }\n\n        bool isolated = true;\n\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[tIdx] = COARSE;\n            weights[tIdx] = 0.F;\n        }\n    }\n}\n\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingCfInitKernel(const IndexType *offsets, const IndexType *column_indices,\n                                const int numRows, int *cf_map, float *weights, const bool *s_con, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // Check if on boundary, this is unmarking even coarse points\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (column_indices[j] > numRows)\n            {\n                cf_map[tIdx] = UNASSIGNED;\n                mark[tIdx] = 1;\n                break;\n            }\n        }\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            // Do nothing, leave as strong fine\n            weights[tIdx] = 0.F;\n        }\n        else if (cf_map[tIdx] == FINE)\n        {\n            // Fine point, mark as unassigned\n            cf_map[tIdx] = UNASSIGNED;\n            mark[tIdx] = 1;\n        }\n    }\n}\n\n\n/*************************************************************************\n * Mark every currently unassigned point as coarse\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markUnassignedAsCoarseKernel(const int numRows,\n                                  const float *weights,\n                                  IndexType *cf_map_in,\n                                  IndexType *cf_map_out, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const IndexType map_in = cf_map_in[tIdx];\n        const int unassigned = map_in == UNASSIGNED ? 1 : 0;\n        mark[tIdx] = unassigned;\n\n        // Mark unassigned piont as coarse\n        if (weights[tIdx] > 1.)\n        {\n            cf_map_out[tIdx] = unassigned ? COARSE : map_in;\n        }\n        else\n        {\n            cf_map_out[tIdx] = map_in;\n        }\n    }\n}\n\n/*************************************************************************\n * Unset all newly set coarse points who shouldn't be\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markAdditionalCoarsePointsKernel(const IndexType *offsets,\n                                      const IndexType *column_indices,\n                                      const int numRows, const float *weights,\n                                      const bool *s_con,\n                                      IndexType *cf_map_out, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // run through newly marked coarse points & set to unassigned again if necessary\n        if (mark[tIdx] > 0)\n        {\n            for ( int j = offsets[tIdx], j_end = offsets[tIdx + 1] ; j < j_end; j++ )\n            {\n                if (!s_con[j]) { continue; } //these are always false for edges pointing outside of the 1-ring halo\n\n                int jcol = column_indices[j];\n                float w_col = weights[jcol];\n                float w_row = weights[tIdx];\n\n                if ( mark[jcol] && w_col > 1.0f )\n                {\n                    // If my weight is bigger, they can't be a coarse point\n                    if ( w_row > w_col )\n                    {\n                        cf_map_out[jcol] = UNASSIGNED;\n                    }\n                    // If their weight is bigger, I can't be a coarse point\n                    else if ( w_col > w_row )\n                    {\n                        cf_map_out[tIdx] = UNASSIGNED;\n                    }\n                }\n            }\n        }\n    }\n}\n\n/*************************************************************************\n * mark every neighbour of a newly assigned coarse point to be fine\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markAdditionalFinePointsKernel(const IndexType *offsets, const IndexType *column_indices,\n                                    const int numRows, const bool *s_con,\n                                    int *cf_map_in, int *cf_map_out)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x ; tIdx < numRows ; tIdx += blockDim.x * gridDim.x )\n    {\n        const int map_in = cf_map_in[tIdx];\n        // go over each row\n        bool fine = false;\n\n        if ( map_in == UNASSIGNED )\n        {\n            // for each column\n            for ( int j = offsets[tIdx], j_end = offsets[tIdx + 1] ; !fine && j < j_end ; ++j )\n            {\n                if ( !s_con[j] )\n                {\n                    continue;    // skip weakly connected points\n                }\n\n                int jcol = column_indices[j];\n                fine = cf_map_in[jcol] == COARSE;\n            }\n        }\n\n        cf_map_out[tIdx] = fine ? FINE : map_in;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PMIS_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::markCoarseFinePoints_1x1(Matrix_d &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Choose blocksize. Using 1 thread / row for now\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() + blockSize - 1) / blockSize));\n    const int numRows = (int) A.get_num_rows();\n    // raw pointers from the cusp arrays\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    float *weights_ptr = weights.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *cf_map_ptr = cf_map.raw();\n    int *scratch_ptr = scratch.raw();\n    IVector mark(cf_map.size());\n    // do the initial marking of points\n    thrust_wrapper::fill<AMGX_device>(mark.begin(), mark.end(), 0);\n    cudaCheckError();\n\n    if (numRows > 0)\n    {\n        if (cf_map_init == 0)\n        {\n            initialMarkingKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr);\n            cudaCheckError();\n        }\n        else if (cf_map_init == 1)\n        {\n            initialMarkingCfInitKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr, mark.raw());\n            cudaCheckError();\n        }\n        else if (cf_map_init == 3)\n        {\n            initialMarkingCFInit3Kernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr);\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"invalid value of cf_map_init in pmis selector, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n    }\n\n    cudaCheckError();\n    // exchange initial cf_map for the 1st ring halos\n    cf_map.dirtybit = 1;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(cf_map, cf_map.tag);\n    }\n\n    // count the # of coarse and fine points we have now\n    int numUnassigned = 0;\n    // array needed for marking of additional coarse points\n    int one_ring_size, offset;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        A.getOffsetAndSizeForView(FULL, &offset, &one_ring_size);\n    }\n    else\n    {\n        one_ring_size = numRows;\n    }\n\n    const int numBlocks1Ring = std::min (AMGX_GRID_MAX_SIZE, (int) ((one_ring_size + blockSize - 1) / blockSize));\n    // iterate until all points have been classified\n    int numUnassignedMax = numRows;\n    //int numUnassignedMaxPrevious;\n    int iter = 0;\n\n    do\n    {\n        //numUnassignedMaxPrevious = numUnassignedMax;\n        if (numRows > 0)\n        {\n            if (iter || !cf_map_init)\n            {\n                // Laucnhed for 1-ring\n                markUnassignedAsCoarseKernel <<< numBlocks1Ring, blockSize>>>(one_ring_size, weights_ptr, cf_map_ptr,\n                        scratch_ptr, mark.raw());\n                cudaCheckError();\n                markAdditionalCoarsePointsKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr,\n                        numRows, weights_ptr, s_con_ptr,\n                        scratch_ptr, mark.raw());\n                cudaCheckError();\n            }\n            else\n            {\n                amgx::thrust::copy(cf_map.begin(), cf_map.begin() + one_ring_size, scratch.begin());\n                cudaCheckError();\n            }\n\n            // If a neighbor marked one of my node as unassigned, need to mark it\n            // Taking the min (unassigned == -4, coarse = -1)\n            scratch.dirtybit = 1;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->min_from_halo(scratch, scratch.tag);\n            }\n\n            cudaCheckError();\n            // Now exchange with neighbors scratch for my own nodes\n            scratch.dirtybit = 1;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                // exchange updated CF map for halos (stored in scratch)\n                A.manager->exchange_halo(scratch, scratch.tag);\n            }\n\n            cudaCheckError();\n            markAdditionalFinePointsKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr,\n                    numRows, s_con_ptr, scratch_ptr,\n                    cf_map_ptr);\n            cudaCheckError();\n        }  // num rows > 0\n\n        // count # of points still unassigned\n        numUnassigned = (int) thrust_wrapper::count<AMGX_device>(cf_map.begin(), cf_map.begin() + numRows, (int)UNASSIGNED);\n        cudaCheckError();\n        numUnassignedMax = numUnassigned;\n        cudaCheckError();\n        cf_map.dirtybit = 1;\n\n        if (A.is_matrix_distributed())\n        {\n            // exchange CF map for halos\n            A.manager->exchange_halo(cf_map, cf_map.tag);\n            // get max across all processes\n            A.manager->getComms()->all_reduce_max(numUnassigned, numUnassignedMax);\n        }\n\n        iter++;\n    }\n    while ( numUnassignedMax != 0 );\n}\n\ntemplate <class T_Config>\nvoid PMIS_SelectorBase< T_Config>::markCoarseFinePoints(Matrix< T_Config> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, weights, s_con, cf_map, scratch, cf_map_init);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size PMIS selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class PMIS_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class PMIS_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <classical/selectors/pmis.h>\n#include <classical/interpolators/common.h>\n#include <cutil.h>\n#include <util.h>\n#include <types.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\nnamespace amgx\n{\n\nnamespace classical\n{\n/*************************************************************************\n * marks the strongest connected (and indepent) points as coarse\n ************************************************************************/\n\ntemplate< AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec >\nstruct MarkAdditionalPoints\n{\n    static const AMGX_VecPrecision vecPrec = (AMGX_VecPrecision)t_indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, vecPrec, t_matPrec, t_indPrec> > IndVector;\n    static int markAdditionalCoarsePoints(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A,\n                                          const Vector<TemplateConfig<AMGX_host, AMGX_vecFloat, t_matPrec, t_indPrec> > &weights,\n                                          const Vector<TemplateConfig<AMGX_host, AMGX_vecBool, t_matPrec, t_indPrec> > &s_con,\n                                          IndVector &cf_map_in,\n                                          IndVector &cf_map_out,\n                                          IndVector &mark)\n    {\n        int num_coarse = 0;\n\n        //deep copy cf_map_in into cf_map_out\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            cf_map_out[i] = cf_map_in[i];\n        }\n\n        // set all unassigned points to be coarse\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            mark[i] = 0;\n\n            if (cf_map_in[i] == UNASSIGNED)\n            {\n                cf_map_out[i] = COARSE;\n                mark[i] = 1;\n            }\n        }\n\n        // now run through newly marked coarse points and make them unassigned again if necessary\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            if (mark[i] > 0) // newly marked C point\n            {\n                for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n                {\n                    if (!s_con[j]) { continue; } // skip weak connections\n\n                    int jcol = A.col_indices[j];\n\n                    if (mark[jcol] && weights[jcol] > 1) // other point is also newly coarse\n                    {\n                        // if my weight is larger, they can't be a coarse point\n                        if (weights[i] > weights[jcol])\n                        {\n                            cf_map_out[jcol] = UNASSIGNED;\n                        }\n                        // if their weight is large, I can't be a coarse point\n                        else if (weights[jcol] > weights[i])\n                        {\n                            cf_map_out[i] = UNASSIGNED;\n                        }\n                    }\n                }\n            }\n        }\n\n        // finally count the number of coarse points\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            num_coarse += static_cast<int>(cf_map_out[i] == COARSE);\n        }\n\n        return num_coarse;\n    }\n\n    /*************************************************************************\n     * marks all neighbors of coarse points as fine\n     ************************************************************************/\n    static int markAdditionalFinePoints(const Matrix<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> > &A,\n                                        const Vector<TemplateConfig<AMGX_host, AMGX_vecBool, t_matPrec, t_indPrec> > &s_con,\n                                        IndVector &cf_map_in,\n                                        IndVector &cf_map_out)\n    {\n        int num_fine = 0;\n\n        //deep copy cf_map_in into cf_map_out\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            cf_map_out[i] = cf_map_in[i];\n        }\n\n        //for each row of the matrix\n        for (int i = 0; i < A.get_num_rows(); i++)\n        {\n            //if already fine add it to the count\n            num_fine += static_cast<int>(cf_map_in[i] == FINE);\n\n            if (cf_map_in[i] == UNASSIGNED)\n            {\n                // for each off diagonal, if I have a new strongly connected coarse neighbour\n                // set to fine\n                for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n                {\n                    if (!s_con[j]) { continue; } //skip weakly connected points\n\n                    int jcol = A.col_indices[j];\n\n                    if (cf_map_in[jcol] == COARSE)\n                    {\n                        cf_map_out[i] = FINE;\n                        num_fine++;\n                        break;\n                    }\n                }\n            }\n        }\n\n        return num_fine;\n    }\n};\n\n/*************************************************************************\n * mark coarse and fine points\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PMIS_Selector<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >\n::markCoarseFinePoints_1x1(Matrix_h &A,\n                           FVector &weights,\n                           const BVector &s_con,\n                           IVector &cf_map,\n                           IVector &scratch,\n                           int cf_map_init)\n{\n    int num_coarse = 0, num_fine = 0;\n    int num_strong_fine = 0;\n\n    //mark all unconnected points as fine\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int numj = A.row_offsets[i + 1] - A.row_offsets[i];\n\n        if (numj == 0) //if there are no columns\n        {\n            cf_map[i] = FINE;\n        }\n        else if (numj == 1 && A.col_indices[A.row_offsets[i]] == i) //if there is only 1 column and it is the diagonal\n        {\n            cf_map[i] = FINE;\n        }\n        else if (weights[i] < 1) // no point strongly depends on point\n        {\n            cf_map[i] = FINE;\n        }\n        else\n        {\n            cf_map[i] = UNASSIGNED;\n        }\n\n        // check to see if a point has no strong connections to anyone\n        // needs more justification / knowledge of everywhere this condition is applied\n        bool isolated = true;\n\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[i] = STRONG_FINE;\n            num_strong_fine++;\n            weights[i] = 0.F;\n        }\n    }\n\n    // temp storage for marking coarse points\n    IntVector mark(A.get_num_rows(), 0);\n    int iterations = 0;\n\n    do\n    {\n        ++iterations;\n        num_coarse = MarkAdditionalPoints<t_vecPrec, t_matPrec, t_indPrec>::markAdditionalCoarsePoints(A, weights, s_con, cf_map, scratch, mark);\n        num_fine = MarkAdditionalPoints<t_vecPrec, t_matPrec, t_indPrec>::markAdditionalFinePoints(A, s_con, scratch, cf_map);\n    }\n    while (num_coarse + num_fine + num_strong_fine < A.get_num_rows());\n\n    // now we have \\hat{C} and the diagonals, we can start calculating the weights\n    // remap cf_map (to be like in distance1)\n}\n\n/*************************************************************************\n * device kernels\n ************************************************************************/\n\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingKernel(const IndexType *offsets, const IndexType *column_indices,\n                          const int numRows, int *cf_map, float *weights, const bool *s_con)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else\n        {\n            cf_map[tIdx] = UNASSIGNED;\n        }\n\n        bool isolated = true;\n\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[tIdx] = STRONG_FINE;\n            weights[tIdx] = 0.F;\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingCFInit3Kernel(const IndexType *offsets, const IndexType *column_indices,\n                                 const int numRows, int *cf_map, float *weights, const bool *s_con)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else\n        {\n            cf_map[tIdx] = UNASSIGNED;\n        }\n\n        bool isolated = true;\n\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (s_con[j])\n            {\n                isolated = false;\n                break;\n            }\n        }\n\n        if (isolated)\n        {\n            cf_map[tIdx] = COARSE;\n            weights[tIdx] = 0.F;\n        }\n    }\n}\n\n/*************************************************************************\n * Perform the initial marking for each row\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid initialMarkingCfInitKernel(const IndexType *offsets, const IndexType *column_indices,\n                                const int numRows, int *cf_map, float *weights, const bool *s_con, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        // Check if on boundary, this is unmarking even coarse points\n        for (int j = offsets[tIdx]; j < offsets[tIdx + 1]; j++)\n        {\n            if (column_indices[j] > numRows)\n            {\n                cf_map[tIdx] = UNASSIGNED;\n                mark[tIdx] = 1;\n                break;\n            }\n        }\n\n        // set initial C/F/U\n        if (numj == 0) // no points\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (numj == 1 && column_indices[offset] == tIdx) // only the diagonal\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (weights[tIdx] < 1) // no strong dependancies\n        {\n            cf_map[tIdx] = FINE;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            // Do nothing, leave as strong fine\n            weights[tIdx] = 0.F;\n        }\n        else if (cf_map[tIdx] == FINE)\n        {\n            // Fine point, mark as unassigned\n            cf_map[tIdx] = UNASSIGNED;\n            mark[tIdx] = 1;\n        }\n    }\n}\n\n\n/*************************************************************************\n * Mark every currently unassigned point as coarse\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markUnassignedAsCoarseKernel(const int numRows,\n                                  const float *weights,\n                                  IndexType *cf_map_in,\n                                  IndexType *cf_map_out, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        const IndexType map_in = cf_map_in[tIdx];\n        const int unassigned = map_in == UNASSIGNED ? 1 : 0;\n        mark[tIdx] = unassigned;\n\n        // Mark unassigned piont as coarse\n        if (weights[tIdx] > 1.)\n        {\n            cf_map_out[tIdx] = unassigned ? COARSE : map_in;\n        }\n        else\n        {\n            cf_map_out[tIdx] = map_in;\n        }\n    }\n}\n\n/*************************************************************************\n * Unset all newly set coarse points who shouldn't be\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markAdditionalCoarsePointsKernel(const IndexType *offsets,\n                                      const IndexType *column_indices,\n                                      const int numRows, const float *weights,\n                                      const bool *s_con,\n                                      IndexType *cf_map_out, IndexType *mark)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x )\n    {\n        // run through newly marked coarse points & set to unassigned again if necessary\n        if (mark[tIdx] > 0)\n        {\n            for ( int j = offsets[tIdx], j_end = offsets[tIdx + 1] ; j < j_end; j++ )\n            {\n                if (!s_con[j]) { continue; } //these are always false for edges pointing outside of the 1-ring halo\n\n                int jcol = column_indices[j];\n                float w_col = weights[jcol];\n                float w_row = weights[tIdx];\n\n                if ( mark[jcol] && w_col > 1.0f )\n                {\n                    // If my weight is bigger, they can't be a coarse point\n                    if ( w_row > w_col )\n                    {\n                        cf_map_out[jcol] = UNASSIGNED;\n                    }\n                    // If their weight is bigger, I can't be a coarse point\n                    else if ( w_col > w_row )\n                    {\n                        cf_map_out[tIdx] = UNASSIGNED;\n                    }\n                }\n            }\n        }\n    }\n}\n\n/*************************************************************************\n * mark every neighbour of a newly assigned coarse point to be fine\n ************************************************************************/\ntemplate <typename IndexType>\n__global__\nvoid markAdditionalFinePointsKernel(const IndexType *offsets, const IndexType *column_indices,\n                                    const int numRows, const bool *s_con,\n                                    int *cf_map_in, int *cf_map_out)\n{\n    for ( int tIdx = threadIdx.x + blockDim.x * blockIdx.x ; tIdx < numRows ; tIdx += blockDim.x * gridDim.x )\n    {\n        const int map_in = cf_map_in[tIdx];\n        // go over each row\n        bool fine = false;\n\n        if ( map_in == UNASSIGNED )\n        {\n            // for each column\n            for ( int j = offsets[tIdx], j_end = offsets[tIdx + 1] ; !fine && j < j_end ; ++j )\n            {\n                if ( !s_con[j] )\n                {\n                    continue;    // skip weakly connected points\n                }\n\n                int jcol = column_indices[j];\n                fine = cf_map_in[jcol] == COARSE;\n            }\n        }\n\n        cf_map_out[tIdx] = fine ? FINE : map_in;\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid PMIS_Selector<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::markCoarseFinePoints_1x1(Matrix_d &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    if (A.hasProps(DIAG))\n    {\n        FatalError(\"Unsupported separate diag\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Choose blocksize. Using 1 thread / row for now\n    const int blockSize = 256;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) ((A.get_num_rows() + blockSize - 1) / blockSize));\n    const int numRows = (int) A.get_num_rows();\n    // raw pointers from the cusp arrays\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    float *weights_ptr = weights.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *cf_map_ptr = cf_map.raw();\n    int *scratch_ptr = scratch.raw();\n    IVector mark(cf_map.size());\n    // do the initial marking of points\n    thrust_wrapper::fill<AMGX_device>(mark.begin(), mark.end(), 0);\n    cudaCheckError();\n\n    if (numRows > 0)\n    {\n        if (cf_map_init == 0)\n        {\n            initialMarkingKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr);\n            cudaCheckError();\n        }\n        else if (cf_map_init == 1)\n        {\n            initialMarkingCfInitKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr, mark.raw());\n            cudaCheckError();\n        }\n        else if (cf_map_init == 3)\n        {\n            initialMarkingCFInit3Kernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr, numRows, cf_map_ptr, weights_ptr, s_con_ptr);\n            cudaCheckError();\n        }\n        else\n        {\n            FatalError(\"invalid value of cf_map_init in pmis selector, exiting\", AMGX_ERR_NOT_IMPLEMENTED);\n        }\n    }\n\n    cudaCheckError();\n    // exchange initial cf_map for the 1st ring halos\n    cf_map.dirtybit = 1;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        A.manager->exchange_halo(cf_map, cf_map.tag);\n    }\n\n    // count the # of coarse and fine points we have now\n    int numUnassigned = 0;\n    // array needed for marking of additional coarse points\n    int one_ring_size, offset;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        A.getOffsetAndSizeForView(FULL, &offset, &one_ring_size);\n    }\n    else\n    {\n        one_ring_size = numRows;\n    }\n\n    const int numBlocks1Ring = std::min (AMGX_GRID_MAX_SIZE, (int) ((one_ring_size + blockSize - 1) / blockSize));\n    // iterate until all points have been classified\n    int numUnassignedMax = numRows;\n    //int numUnassignedMaxPrevious;\n    int iter = 0;\n\n    do\n    {\n        //numUnassignedMaxPrevious = numUnassignedMax;\n        if (numRows > 0)\n        {\n            if (iter || !cf_map_init)\n            {\n                // Laucnhed for 1-ring\n                markUnassignedAsCoarseKernel <<< numBlocks1Ring, blockSize>>>(one_ring_size, weights_ptr, cf_map_ptr,\n                        scratch_ptr, mark.raw());\n                cudaCheckError();\n                markAdditionalCoarsePointsKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr,\n                        numRows, weights_ptr, s_con_ptr,\n                        scratch_ptr, mark.raw());\n                cudaCheckError();\n            }\n            else\n            {\n                amgx::thrust::copy(cf_map.begin(), cf_map.begin() + one_ring_size, scratch.begin());\n                cudaCheckError();\n            }\n\n            // If a neighbor marked one of my node as unassigned, need to mark it\n            // Taking the min (unassigned == -4, coarse = -1)\n            scratch.dirtybit = 1;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                A.manager->min_from_halo(scratch, scratch.tag);\n            }\n\n            cudaCheckError();\n            // Now exchange with neighbors scratch for my own nodes\n            scratch.dirtybit = 1;\n\n            if (!A.is_matrix_singleGPU())\n            {\n                // exchange updated CF map for halos (stored in scratch)\n                A.manager->exchange_halo(scratch, scratch.tag);\n            }\n\n            cudaCheckError();\n            markAdditionalFinePointsKernel <<< numBlocks, blockSize>>>(offsets_ptr, column_indices_ptr,\n                    numRows, s_con_ptr, scratch_ptr,\n                    cf_map_ptr);\n            cudaCheckError();\n        }  // num rows > 0\n\n        // count # of points still unassigned\n        numUnassigned = (int) thrust_wrapper::count<AMGX_device>(cf_map.begin(), cf_map.begin() + numRows, (int)UNASSIGNED);\n        cudaCheckError();\n        numUnassignedMax = numUnassigned;\n        cudaCheckError();\n        cf_map.dirtybit = 1;\n\n        if (A.is_matrix_distributed())\n        {\n            // exchange CF map for halos\n            A.manager->exchange_halo(cf_map, cf_map.tag);\n            // get max across all processes\n            A.manager->getComms()->all_reduce_max(numUnassigned, numUnassignedMax);\n        }\n\n        iter++;\n    }\n    while ( numUnassignedMax != 0 );\n}\n\ntemplate <class T_Config>\nvoid PMIS_SelectorBase< T_Config>::markCoarseFinePoints(Matrix< T_Config> &A,\n        FVector &weights,\n        const BVector &s_con,\n        IVector &cf_map,\n        IVector &scratch,\n        int cf_map_init)\n{\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        markCoarseFinePoints_1x1(A, weights, s_con, cf_map, scratch, cf_map_init);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size PMIS selector\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n}\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class PMIS_SelectorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class PMIS_Selector<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace classical\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/selectors/pmis.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <types.h>\n#include <classical/interpolators/common.h>\n\nnamespace amgx\n{\n\n/*\n * Count the # of non-zeros per row\n */\n__global__\nvoid nonZerosPerRowKernel(const int num_rows, const int *cf_map, const int *C_hat_start,\n                          const int *C_hat_end, int *nonZerosPerRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        int nonZeros = 0;\n\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZeros = 1;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            nonZeros = 0;\n        }\n        else\n        {\n            nonZeros = C_hat_end[tIdx] - C_hat_start[tIdx];\n        }\n\n        nonZerosPerRow[tIdx] = nonZeros;\n    }\n}\n\n__global__\nvoid nonZerosPerRowSizeKernel(const int num_rows, const int *cf_map,\n                              const int *C_hat_size, int *nonZerosPerRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        int nonZeros = 0;\n\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZeros = 1;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            nonZeros = 0;\n        }\n        else\n        {\n            nonZeros = C_hat_size[tIdx];\n        }\n\n        nonZerosPerRow[tIdx] = nonZeros;\n    }\n}\n\n\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <types.h>\n#include <classical/interpolators/common.h>\n\nnamespace amgx\n{\n\n/*\n * Count the # of non-zeros per row\n */\n__global__\nvoid nonZerosPerRowKernel(const int num_rows, const int *cf_map, const int *C_hat_start,\n                          const int *C_hat_end, int *nonZerosPerRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        int nonZeros = 0;\n\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZeros = 1;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            nonZeros = 0;\n        }\n        else\n        {\n            nonZeros = C_hat_end[tIdx] - C_hat_start[tIdx];\n        }\n\n        nonZerosPerRow[tIdx] = nonZeros;\n    }\n}\n\n__global__\nvoid nonZerosPerRowSizeKernel(const int num_rows, const int *cf_map,\n                              const int *C_hat_size, int *nonZerosPerRow)\n{\n    for (int tIdx = threadIdx.x + blockIdx.x * blockDim.x; tIdx < num_rows; tIdx += gridDim.x * blockDim.x)\n    {\n        int nonZeros = 0;\n\n        if (cf_map[tIdx] >= 0)\n        {\n            nonZeros = 1;\n        }\n        else if (cf_map[tIdx] == STRONG_FINE)\n        {\n            nonZeros = 0;\n        }\n        else\n        {\n            nonZeros = C_hat_size[tIdx];\n        }\n\n        nonZerosPerRow[tIdx] = nonZeros;\n    }\n}\n\n\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/interpolators/common.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <classical/interpolators/multipass.h>\n#include <classical/interpolators/common.h>\n#include <device_properties.h>\n#include <amgx_timer.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <sort.h>\n#include <assert.h>\n#include <misc.h>\n#include <sstream>\n#include <csr_multiply.h>\n#include <hash_workspace.h>\n#include <thrust/count.h>\n#include <thrust/scan.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nusing std::set;\nusing std::vector;\n\ntemplate < typename IndexType, typename ValueType >\n__global__\nvoid check_P_values(IndexType *offsets, ValueType *val, int size, int flag)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        for (int i = offsets[tidx]; i < offsets[tidx + 1]; i++)\n        {\n            if (isnan(val[i])) { printf(\"%d, row = %d, i=%d\\n\", flag, tidx, i); }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid fill_cf_map_global(const IndexType *cf_map, KeyType *cf_map_global, IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        cf_map_global[tidx] = (KeyType) cf_map[tidx];\n    }\n}\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid fill_P_col_indices(const KeyType *global_cols, IndexType *cols, IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        cols[tidx] = (IndexType) global_cols[tidx];\n    }\n}\n\n\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid createCfMapGlobal(const IndexType *cf_map, KeyType *cf_map_global, const KeyType my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (KeyType) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid initializeAssignedArray(const IndexType *cf_map, IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, IndexType *C_hat_offsets, int A_num_rows)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        int coarse_fine_id = cf_map[aRowId];\n\n        if ( coarse_fine_id >= 0)\n        {\n            assigned[aRowId] = 0;\n        }\n        else if (coarse_fine_id == FINE)\n        {\n            // Load A row IDs.\n            int aColIt  = A_rows[aRowId  ];\n            int aColEnd = A_rows[aRowId + 1];\n            int coarse_count = 0;\n\n            // Iterate over the columns of A.\n            for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n                // Is it fine.\n                bool is_coarse  = is_off_diagonal && cf_map[aColId] >= 0;\n                // Is it strongly connected ?\n                bool is_coarse_strongly_connected = aColIt < aColEnd && s_con[aColIt] && is_coarse;\n                // Push coarse and strongly connected nodes in the set.\n                coarse_count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            }\n\n            // If fine point connected to coarse point, assigned = 1,\n            if (coarse_count)\n            {\n                assigned[aRowId] = 1;\n                C_hat_offsets[aRowId] = coarse_count;\n            }\n        }\n        else // Strong fine\n        {\n            assigned[aRowId] = -1;\n        }\n    }\n}\n\ntemplate <typename IndexType, int cta_size, int group_size>\n__global__\nvoid initializeAssignedArray_opt(const IndexType *cf_map, IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, IndexType *C_hat_offsets, int A_num_rows)\n{\n    const int ngroups = cta_size / group_size;\n\n    // The coordinates of the thread inside the CTA/group.\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    bool active = true;\n    if(a_row_id > A_num_rows)\n    {\n        active = false;\n    }\n\n    int coarse_fine_id = UNASSIGNED;\n\n    if(active)\n    {\n        coarse_fine_id = cf_map[a_row_id];\n\n        if(coarse_fine_id >= 0)\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = 0;\n            }\n\n            active = false;\n        }\n        else if(coarse_fine_id != FINE) // Strong fine\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = -1;\n            }\n\n            active = false;\n        }\n    }\n\n    active = (active && coarse_fine_id == FINE);\n\n    constexpr int warp_size = 32;\n    unsigned int base_mask = 0xFFFFFFFF >> (warp_size - group_size);\n    int ngroups_in_warp = warp_size / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // Load A row IDs.\n    int aColIt  = A_rows[a_row_id  ];\n    int aColEnd = A_rows[a_row_id + 1];\n\n    // Iterate over the columns of A.\n    int coarse_count = 0;\n    for ( aColIt += lane_id ; utils::any( active && aColIt < aColEnd ) ; aColIt += group_size )\n    {\n        bool is_coarse_strongly_connected = false;\n\n        if(active)\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = aColIt < aColEnd && aColId != a_row_id;\n\n            // Is it fine.\n            bool is_coarse  = is_off_diagonal && cf_map[aColId] >= 0;\n\n            // Is it strongly connected ?\n            is_coarse_strongly_connected = s_con[aColIt] && is_coarse;\n        }\n\n        // Count any of the threads in the group that had strong connections\n        int coarse_exist_flags = utils::ballot(is_coarse_strongly_connected);\n        if(lane_id == 0)\n        {\n            coarse_count += __popc(coarse_exist_flags & group_mask);\n        }\n    }\n\n    // If fine point connected to coarse point, assigned = 1,\n    if (active && coarse_count > 0)\n    {\n        if(lane_id == 0)\n        {\n            assigned[a_row_id] = 1;\n            C_hat_offsets[a_row_id] = coarse_count;\n        }\n    }\n}\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid fillAssignedArray(IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, int A_num_rows, int pass)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        if ( assigned[aRowId] == -1)\n        {\n            // Load A row IDs.\n            int aColIt  = A_rows[aRowId  ];\n            int aColEnd = A_rows[aRowId + 1];\n            int assigned_count = 0;\n\n            // Iterate over the columns of A.\n            for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n                // Is it assigned\n                bool is_assigned = is_off_diagonal && assigned[aColId] == pass - 1;\n                // Is it strongly connected ?\n                bool is_assigned_strongly_connected = aColIt < aColEnd && s_con[aColIt] && is_assigned;\n                // Push coarse and strongly connected nodes in the set.\n                assigned_count += __popc( utils::ballot( is_assigned_strongly_connected ) );\n\n                if (assigned_count) { break; }\n            }\n\n            // If one of the strong neighbor has been assigned, set assigned to current pass number\n            if (assigned_count)\n            {\n                assigned[aRowId] = pass;\n            }\n        }\n    }\n}\n\ntemplate < typename IndexType, int cta_size, int group_size >\n__global__\nvoid fillAssignedArray_opt(IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, int A_num_rows, int pass)\n{\n    // The coordinates of the thread inside the CTA/groups\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n    const int ngroups_per_block = cta_size / group_size;\n\n    int a_row_id = blockIdx.x * ngroups_per_block + group_id;\n\n    bool active = true;\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != -1)\n    {\n        active = false;\n    }\n\n    // Mask for the subgroup\n    constexpr int warp_size = 32;\n    unsigned int base_mask = 0xFFFFFFFF >> (warp_size - group_size);\n    int ngroups_in_warp = warp_size / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // Load A row IDs.\n    int a_col_begin = 0;\n    int a_col_end   = 0;\n\n    if(active)\n    {\n        a_col_begin = A_rows[a_row_id  ];\n        a_col_end   = A_rows[a_row_id + 1];\n    }\n\n    // Iterate over the columns of A.\n    for (int a_col_it = a_col_begin + lane_id ; utils::any(active && a_col_it < a_col_end); a_col_it += group_size)\n    {\n        bool is_assigned_strongly_connected = false;\n\n        if(active)\n        {\n            // Columns of A maps to rows of B. Each thread of the group loads its A-col/B-row ID.\n            int a_col_id = a_col_it < a_col_end ? A_cols[a_col_it] : -1;\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n\n            // Is it assigned\n            bool is_assigned = is_off_diagonal && assigned[a_col_id] == pass - 1;\n\n            // Is it strongly connected ?\n            is_assigned_strongly_connected = s_con[a_col_it] && is_assigned;\n        }\n\n        int group_strongly_connected = utils::ballot(is_assigned_strongly_connected);\n\n        // Push coarse and strongly connected nodes in the set.\n        if(active && (group_strongly_connected & group_mask))\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = pass;\n            }\n\n            active = false;\n        }\n    }\n}\n\n\n/*\n * remove all duplicates from a vector\n */\ntemplate <typename T>\nvoid removeDuplicates(vector<T> &vec)\n{\n    std::sort(vec.begin(), vec.end());\n    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());\n}\n\n/*\n * find the value at a particular column of a row if it exists\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type findValue(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const int column_needed)\n{\n    typename VVector::value_type v = 0;\n\n    // NAIVE\n    for (int k = row_begin; k < row_end; k++)\n    {\n        int kcol = columns[k];\n\n        if (kcol == column_needed)\n        {\n            v = values[k];\n        }\n    }\n\n    return v;\n}\n\n\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_h &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_h &P)\n{\n    FatalError(\"multipass interpolation not implemented on host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n} // end multipass interpolator\n\n\n///////////////////////////////////////////////////////////////////////////////\n\n// DEVICE CODE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace multipass\n{\n\n#include <sm_utils.inl>\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const bool *s_con,\n                            const int *assigned,\n                            int *C_hat_offsets,\n                            int pass )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per grid.\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    volatile int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID )\n    {\n        // Skip coarse rows and fine rows touching a coarse point\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int my_count = 0;\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // assigned == pass-1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n            // Is it a coarse, strongly-connected column and already assigned?\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n            // If so, add their C_hat_offset value to my_count\n            my_count += (is_assigned_strongly_connected ? C_hat_offsets[a_col_id] : 0);\n        }\n\n        // Do reduction\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_count += utils::shfl_xor( my_count, mask );\n        }\n\n        // Write result -- RACE CONDITION!\n        if (lane_id == 0)\n        {\n            C_hat_offsets[a_row_id] = my_count;\n        }\n    }\n}\n\ntemplate< int CTA_SIZE, int group_size >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel_opt( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const bool *s_con,\n                            const int *assigned,\n                            int *C_hat_offsets,\n                            int pass )\n{\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    int ngroups = CTA_SIZE / group_size;\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Skip coarse rows and fine rows touching a coarse point\n    bool active = true;\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != pass)\n    {\n        active = false;\n    }\n\n    int my_count = 0;\n\n    // Load A row IDs.\n    if(active)\n    {\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Iterate over the columns of A to build C_hat.\n        for (int a_col_it = a_col_begin + lane_id; a_col_it < a_col_end; a_col_it += group_size )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // assigned == pass-1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n\n            // Is it a coarse, strongly-connected column and already assigned?\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n\n            // If so, add their C_hat_offset value to my_count\n            my_count += (is_assigned_strongly_connected ? C_hat_offsets[a_col_id] : 0);\n        }\n    }\n\n#pragma unroll\n    for(int i = group_size / 2; i > 0; i /= 2)\n    {\n        my_count += utils::shfl_down(my_count, i);\n    }\n\n    if (active && lane_id == 0)\n    {\n        C_hat_offsets[a_row_id] = my_count;\n    }\n}\n\n    template< int CTA_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\n    void\ncompute_c_hat_first_pass_kernel( int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const bool *__restrict s_con,\n        const KeyType *__restrict cf_map_global,\n        const int *__restrict C_hat_start,\n        int *__restrict C_hat_size,\n        KeyType *__restrict C_hat,\n        int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Skip if not in current pass\n        if (assigned[a_row_id] != 1)\n        {\n            continue;\n        }\n\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        int count = 0;\n        int c_col_it = C_hat_start[a_row_id];\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == 0);\n            // Is it an assigned==pass -1 and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                //if (cf_map[a_col_id] < 0)\n                //  printf(\"error in multipass 1\\n\");\n                C_hat[c_col_it + count + dest] = cf_map_global[a_col_id];\n            }\n\n            count += __popc( vote );\n        }\n\n        if ( lane_id == 0 )\n        {\n            C_hat_size[a_row_id] = count;\n        }\n    }\n}\n\n    template< int CTA_SIZE, int WARP_SIZE, int group_size, typename KeyType>\n__global__ __launch_bounds__( CTA_SIZE )\n    void\ncompute_c_hat_first_pass_kernel_opt( int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const bool *__restrict s_con,\n        const KeyType *__restrict cf_map_global,\n        const int *__restrict C_hat_start,\n        int *__restrict C_hat_size,\n        KeyType *__restrict C_hat,\n        int *__restrict assigned)\n{\n    const int ngroups = CTA_SIZE / group_size;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    bool active = true;\n\n    // Skip if not in current pass\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != 1)\n    {\n        active = false;\n    }\n\n    int a_col_begin = 0;\n    int a_col_end   = 0;\n    int c_col_it = 0;\n\n    if(active)\n    {\n        a_col_begin = A_rows[a_row_id];\n        a_col_end   = A_rows[a_row_id + 1];\n        c_col_it = C_hat_start[a_row_id];\n    }\n\n    unsigned int base_mask = 0xFFFFFFFF >> (WARP_SIZE - group_size);\n    int ngroups_in_warp = WARP_SIZE / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // _iterate over the columns of A to build C_hat.\n    int count = 0;\n    for ( int a_col_it = a_col_begin + lane_id ; utils::any(active && a_col_it < a_col_end) ; a_col_it += group_size )\n    {\n        bool is_assigned_strongly_connected = false;\n\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int a_col_id = -1;\n\n        if(active)\n        {\n            if (a_col_it < a_col_end)\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = (a_col_it < a_col_end) && (a_col_id != a_row_id);\n\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == 0);\n\n            // Is it an assigned==pass -1 and strongly-connected column.\n            is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n        }\n\n        // We collect fine and strongly-collected columns.\n        int all_vote = utils::ballot( is_assigned_strongly_connected );\n        int group_vote = all_vote & group_mask;\n        int dest = __popc( group_vote & utils::lane_mask_lt() );\n\n        if ( active && is_assigned_strongly_connected )\n        {\n            C_hat[c_col_it + count + dest] = cf_map_global[a_col_id];\n        }\n\n        count += __popc( group_vote );\n    }\n\n    if ( active && lane_id == 0 )\n    {\n        C_hat_size[a_row_id] = count;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_size,\n                      KeyType *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int *__restrict assigned,\n                      int gmem_size,\n                      KeyType *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status,\n                      int pass )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<KeyType, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip if not in current pass\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Check if assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = is_active && s_con[a_col_it];\n            // Check if strong connection to assigned == pass-1\n            bool is_assigned_strongly_connected = is_assigned && is_strongly_connected;\n            // We collect columns for which assigned == pass - 1\n            int vote = utils::ballot( is_assigned_strongly_connected);\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected)\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    // this loops over elements of C_hat for row uniform_b_row_id\n                    b_col_it  = C_hat_start[uniform_b_row_id];\n                    b_col_end = b_col_it + C_hat_size[uniform_b_row_id];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the interpolatory point\n                    KeyType  b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = C_hat[b_col_it];\n                    }\n\n                    // Is it valid\n                    is_off_diagonal = b_col_it < b_col_end; //  && b_col_id != local_k;\n                    // Push node to set\n                    KeyType b_item = -1;\n\n                    if ( is_off_diagonal )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    // Insert the interpolatory point to the set\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_size[a_row_id] = count;\n        }\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE, class KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel_opt( int assigned_set_size,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_size,\n                      KeyType *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int *__restrict assigned,\n                      int *__restrict assigned_set,\n                      int pass )\n{\n    constexpr KeyType SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    const int gid = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Block-level hash container storage\n    KeyType* key_s = (KeyType*)s;\n\n    //KeyType* data_s = (KeyType*)&key_s[ngroups*HASH_SIZE];\n    int* col_ind_s = (int*)&key_s[ngroups*HASH_SIZE];\n\n    // Group-level hash containers\n    KeyType* key_group_s = &key_s[group_id*HASH_SIZE];\n    //KeyType* data_group_s = &data_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n        //data_s[i] = 0; // We will sum into values\n    }\n\n    if(lane_id == 0)\n    {\n        col_ind_s[group_id] = 0;\n    }\n\n    __syncthreads();\n\n    int a_row_id = -1;\n\n    if(gid < assigned_set_size)\n    {\n        // One row of A per group\n        a_row_id = assigned_set[gid];\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = A_rows[a_row_id] + lane_id ; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE )\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Check if assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = s_con[a_col_it];\n\n            // Check if strong connection to assigned == pass-1\n            bool is_assigned_strongly_connected = is_assigned && is_strongly_connected;\n\n            if(!is_assigned_strongly_connected) continue;\n\n            // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n            int b_col_beg  = C_hat_start[a_col_id];\n\n            // Iterate over the range of columns of B.\n            for (int b_col_it = b_col_beg; b_col_it < b_col_beg + C_hat_size[a_col_id]; ++b_col_it)\n            {\n                KeyType key = C_hat[b_col_it];\n\n                int hash = key % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == key) break;\n                    \n                    if(curr_key == SLOT_VACANT)\n                    {\n                        KeyType old_key = (KeyType)atomicCAS((unsigned long long*)&key_group_s[hash], (unsigned long long)SLOT_VACANT, (unsigned long long)key);\n                        if(old_key == SLOT_VACANT || old_key == key)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // XXX Could change to poll by ballot style compress\n    if(gid < assigned_set_size)\n    {\n        // Store the results.\n        int c_col_it = C_hat_start[a_row_id];\n\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            KeyType key = key_group_s[i];\n            if(key != SLOT_VACANT)\n            {\n                int ind = atomicAdd(&col_ind_s[group_id], 1);\n                C_hat[c_col_it + ind] = key;\n                //C_hat_pos[c_col_it + ind] = i;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(gid < assigned_set_size)\n    {\n        if(lane_id == 0)\n        {\n            C_hat_size[a_row_id] = col_ind_s[group_id];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_first_pass_kernel( const int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Value_type *__restrict A_vals,\n        const KeyType *__restrict cf_map_global,\n        const bool *__restrict s_con,\n        const Value_type *__restrict diag,\n        const int *__restrict P_rows,\n        KeyType *P_cols,\n        Value_type *P_vals,\n        const int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Only do work if assigned[row_id] == pass\n        KeyType coarse_fine_id = cf_map_global[a_row_id];\n\n        // If coarse rows\n        if ( assigned[a_row_id] == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n        }\n        // If strong fine\n        else if ( assigned[a_row_id] == -1 )\n        {\n            // Do nothing\n        }\n        // strongly connected to a coarse point\n        else if (assigned[a_row_id] == 1)\n        {\n            // Load A row IDs.\n            int a_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                a_col_tmp = A_rows[a_row_id + lane_id];\n            }\n\n            int a_col_it  = utils::shfl( a_col_tmp, 0 );\n            int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n            int p_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                p_col_tmp = P_rows[a_row_id + lane_id];\n            }\n\n            int p_col_it  = utils::shfl( p_col_tmp, 0 );\n            int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n            // Weak value.\n            Value_type sum_N(0), sum_C(0), alfa(0);\n            int count = 0;\n\n            // Iterate over the columns of A.\n            for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int a_col_id(-1);\n                Value_type a_value(0);\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_value  = A_vals[a_col_it];\n                }\n\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n                // Is it a strongly-connected column.\n                bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n                // Is it a weakly connected node.\n                //bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n                // Is it isolated.\n                bool is_strong_fine = is_off_diagonal && cf_map_global[a_col_id] == STRONG_FINE;\n                // Is it a fine and strongly-connected column.\n                bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == 0;\n\n                // Update the weak value.\n                if ( is_off_diagonal && !is_strong_fine)\n                {\n                    sum_N += a_value;\n                }\n\n                if ( is_assigned_strongly_connected )\n                {\n                    sum_C += a_value;\n                }\n\n                // We collect fine and strongly-collected columns.\n                int vote = utils::ballot( is_assigned_strongly_connected );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( is_assigned_strongly_connected )\n                {\n                    P_cols[p_col_it + count + dest] = cf_map_global[a_col_id];\n                    P_vals[p_col_it + count + dest] = a_value;\n                }\n\n                count += __popc( vote );\n            }\n\n            // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                sum_C += utils::shfl_xor( sum_C, mask );\n                sum_N += utils::shfl_xor( sum_N, mask );\n            }\n\n            if ( lane_id == 0 )\n            {\n                // NOTE this matches the check for zero in HYPRE\n                double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n                alfa = -sum_N / div;\n            }\n\n            alfa = utils::shfl( alfa, 0 );\n\n            // Scale the value of P\n            for ( p_col_it += lane_id ; utils::any( p_col_it < p_col_end ) ; p_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                if ( p_col_it < p_col_end )\n                {\n                    P_vals[p_col_it] *= alfa;\n                }\n            }\n        } // if (assigned[a_row_id] == 1)\n    } // Loop over rows\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_first_pass_kernel_opt( const int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Value_type *__restrict A_vals,\n        const KeyType *__restrict cf_map_global,\n        const bool *__restrict s_con,\n        const Value_type *__restrict diag,\n        const int *__restrict P_rows,\n        KeyType *P_cols,\n        Value_type *P_vals,\n        const int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Only do work if assigned[row_id] == pass\n        KeyType coarse_fine_id = cf_map_global[a_row_id];\n\n        // If coarse rows\n        if ( assigned[a_row_id] == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n        }\n        // If strong fine\n        else if ( assigned[a_row_id] == -1 )\n        {\n            // Do nothing\n        }\n        // strongly connected to a coarse point\n        else if (assigned[a_row_id] == 1)\n        {\n            // Load A row IDs.\n            int a_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                a_col_tmp = A_rows[a_row_id + lane_id];\n            }\n\n            int a_col_it  = utils::shfl( a_col_tmp, 0 );\n            int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n            int p_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                p_col_tmp = P_rows[a_row_id + lane_id];\n            }\n\n            int p_col_it  = utils::shfl( p_col_tmp, 0 );\n            int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n            // Weak value.\n            Value_type sum_N(0), sum_C(0), alfa(0);\n            int count = 0;\n\n            // Iterate over the columns of A.\n            for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int a_col_id(-1);\n                Value_type a_value(0);\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_value  = A_vals[a_col_it];\n                }\n\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n                // Is it a strongly-connected column.\n                bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n                // Is it a weakly connected node.\n                //bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n                // Is it isolated.\n                bool is_strong_fine = is_off_diagonal && cf_map_global[a_col_id] == STRONG_FINE;\n                // Is it a fine and strongly-connected column.\n                bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == 0;\n\n                // Update the weak value.\n                if ( is_off_diagonal && !is_strong_fine)\n                {\n                    sum_N += a_value;\n                }\n\n                if ( is_assigned_strongly_connected )\n                {\n                    sum_C += a_value;\n                }\n\n                // We collect fine and strongly-collected columns.\n                int vote = utils::ballot( is_assigned_strongly_connected );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( is_assigned_strongly_connected )\n                {\n                    P_cols[p_col_it + count + dest] = cf_map_global[a_col_id];\n                    P_vals[p_col_it + count + dest] = a_value;\n                }\n\n                count += __popc( vote );\n            }\n\n            // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                sum_C += utils::shfl_xor( sum_C, mask );\n                sum_N += utils::shfl_xor( sum_N, mask );\n            }\n\n            if ( lane_id == 0 )\n            {\n                // NOTE this matches the check for zero in HYPRE\n                double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n                alfa = -sum_N / div;\n            }\n\n            alfa = utils::shfl( alfa, 0 );\n\n            // Scale the value of P\n            for ( p_col_it += lane_id ; utils::any( p_col_it < p_col_end ) ; p_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                if ( p_col_it < p_col_end )\n                {\n                    P_vals[p_col_it] *= alfa;\n                }\n            }\n        } // if (assigned[a_row_id] == 1)\n    } // Loop over rows\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel_opt( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const KeyType *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_size,\n                              const Value_type *__restrict diag,\n                              const int *__restrict P_rows,\n                              KeyType *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              KeyType *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue,\n                              const int *__restrict assigned,\n                              const int *__restrict assigned_set,\n                              int pass)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads store a value of B to load.\n    __shared__ Value_type s_b_values[CTA_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<KeyType, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a * gmem_size],\n            gmem_size );\n    // Loop over rows of A.\n    for ( ; a < A_num_rows ; a += gridDim.x*NUM_WARPS )\n    {\n        int a_row_id = assigned_set[a];\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = c_hat_it + C_hat_size[a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // sums\n        Value_type sum_N(0), sum_C(0), alfa(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a fine and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == pass - 1;\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_assigned_strongly_connected;\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_strong_fine)\n            {\n                sum_N += a_value;\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_b_values[warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                Value_type uniform_val = s_b_values[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = P_rows[b_row_id  ];\n                int b_col_end   = P_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    KeyType b_col_id(-1);\n                    Value_type tmp(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = P_cols[b_col_it];\n                        tmp = P_vals[b_col_it] * uniform_val;\n                        sum_C += tmp;\n                        sum_N += tmp;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    // Need to get fine level id for b_col_id\n                    // if (a_row_id == 8 && b_col_id == 3) printf(\"updating 8,3: %lg\\n\",tmp);\n                    bool found = map.update( b_col_id, tmp );\n                    bool any_found = (__popc(utils::ballot(found)));\n                }\n            }\n        }\n\n        // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            sum_C += utils::shfl_xor( sum_C, mask );\n            sum_N += utils::shfl_xor( sum_N, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            // NOTE this matches the check for zero in HYPRE\n            double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n            alfa = -sum_N / div;\n            // alfa = -sum_N/(sum_C*diag[a_row_id]);\n        }\n\n        alfa = utils::shfl( alfa, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_keys_scale_values( p_col_end - p_col_it, &P_cols[p_col_it], alfa, &P_vals[p_col_it] );\n    }\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const KeyType *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_size,\n                              const Value_type *__restrict diag,\n                              const int *__restrict P_rows,\n                              KeyType *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              KeyType *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue,\n                              const int *__restrict assigned,\n                              int pass)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads store a value of B to load.\n    __shared__ Value_type s_b_values[CTA_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<KeyType, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Only do work if assigned[row_id] == pass\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = c_hat_it + C_hat_size[a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // sums\n        Value_type sum_N(0), sum_C(0), alfa(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a fine and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == pass - 1;\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_assigned_strongly_connected;\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_strong_fine)\n            {\n                sum_N += a_value;\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_b_values[warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            __syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                Value_type uniform_val = s_b_values[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = P_rows[b_row_id  ];\n                int b_col_end   = P_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    KeyType b_col_id(-1);\n                    Value_type tmp(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = P_cols[b_col_it];\n                        tmp = P_vals[b_col_it] * uniform_val;\n                        sum_C += tmp;\n                        sum_N += tmp;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    // Need to get fine level id for b_col_id\n                    // if (a_row_id == 8 && b_col_id == 3) printf(\"updating 8,3: %lg\\n\",tmp);\n                    bool found = map.update( b_col_id, tmp );\n                    bool any_found = (__popc(utils::ballot(found)));\n                }\n            }\n        }\n\n        // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            sum_C += utils::shfl_xor( sum_C, mask );\n            sum_N += utils::shfl_xor( sum_N, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            // NOTE this matches the check for zero in HYPRE\n            double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n            alfa = -sum_N / div;\n            // alfa = -sum_N/(sum_C*diag[a_row_id]);\n        }\n\n        alfa = utils::shfl( alfa, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_keys_scale_values( p_col_end - p_col_it, &P_cols[p_col_it], alfa, &P_vals[p_col_it] );\n    }\n}\n\n\n} // namespace multipass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*\n * grab the diagonal of the matrix\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n    }\n}\n/*************************************************************************\n * Implementing Extended+i algorithm from \\S 4.5 of:\n * \"Distance-two interpolation for parallel algebraic multigrid\"\n * Reference [4] on wiki\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nMultipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Multipass_Interpolator(AMG_Config &cfg, const std::string &cfg_scope) : Base(cfg, cfg_scope)\n{\n    this->m_use_opt_kernels = cfg.getParameter<int>(\"use_opt_kernels\", \"default\");\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nMultipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~Multipass_Interpolator()\n{}\n\nenum { WARP_SIZE = 32, GRID_SIZE = 1024, SMEM_SIZE = 128 };\n\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x < 0;\n    }\n};\n\nstruct is_strong_fine\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == -3;\n    }\n};\n\n__global__ void set_assigned_in_pass(int n, int pass, int* assigned, int* assigned_in_pass, int* assigned_offs)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    assigned_in_pass[i] = (assigned[i] == pass) ? 1 : 0;\n\n    if(i == 0) \n    {\n        assigned_offs[0] = 0;\n    }\n}\n\n__global__ void assigned_set_fill(int n, int* assigned_in_pass, int* assigned_offs, int* assigned_set)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    if(assigned_in_pass[i]) \n    { \n        assigned_set[assigned_offs[i]] = i; \n    } \n}\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size, class KeyType>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_device, V, M, I>>::compute_c_hat_opt_dispatch(\n        const Matrix_d &A,\n        const bool *s_con,\n        const int *C_hat_start,\n        int *C_hat_size,\n        KeyType *C_hat,\n        int *C_hat_pos,\n        int *assigned,\n        IntVector &assigned_set,\n        int pass )\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = assigned_set.size() / ngroups + 1;\n\n    cudaDeviceProp deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(KeyType)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    cudaFuncSetAttribute(multipass::compute_c_hat_kernel_opt \n            <group_size, cta_size, hash_size, KeyType>, \n            cudaFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    multipass::compute_c_hat_kernel_opt\n        <group_size, cta_size, hash_size, KeyType> \n        <<<grid_size, cta_size, shmem_size>>>(\n            assigned_set.size(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            s_con,\n            C_hat_start,\n            C_hat_size,\n            C_hat,\n            C_hat_pos,\n            assigned,\n            assigned_set.raw(),\n            pass);\n            cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    nvtxRange nvr_gim(__func__);\n\n    // Implementation based on paper \"On long range interpolation operators for aggressive coarsening\" by Ulrike Meier Yang, section 4.3\n    const int blockSize = 256;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n    int *cf_map_ptr = cf_map.raw();\n    bool *s_con_ptr = s_con.raw();\n    int size_two_ring;\n    int size_one_ring;\n\n    if (A.is_matrix_distributed())\n    {\n        int offset;\n        A.getOffsetAndSizeForView(ALL, &offset, &size_two_ring);\n        A.getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    }\n    else\n    {\n        size_one_ring = A.get_num_rows();\n        size_two_ring = A.get_num_rows();\n    }\n\n    int numBlocksOneRing = std::min( 4096, (int) (size_one_ring + blockSize - 1) / blockSize );\n    int numBlocksTwoRing = std::min( 4096, (int) (size_two_ring + blockSize - 1) / blockSize );\n    int numBlocks = std::min( 4096, (int) (A.get_num_rows() + blockSize - 1) / blockSize );\n    // ----------------------------------------------------------\n    // First fill out the assigned array and count # of passes\n    // ----------------------------------------------------------\n    IntVector assigned( size_one_ring, -1);\n    IntVector C_hat_start( size_one_ring + 1, 0 );\n    // assigned[i] = -1: unassigned point\n    // assigned[i] = 0: coarse point\n    // assigned[i] = 1: fine point directly connected to coarse point\n    // assigned[i] = 2: fine point distance 2 away from strongly connected coarse point\n    // assigned[i] = ...\n    // Initialize assigned array by marking the coarse and fine point directly connected to coarse points\n    const int cta_size = 256;\n    const int nWarps = cta_size / 32;\n    int grid_size = std::min( 4096, (int) (A.get_num_rows() + nWarps - 1) / nWarps);\n    if(this->m_use_opt_kernels)\n    {\n        constexpr int group_size = 8;\n        int ngroups_per_block = cta_size / group_size;\n        int grid_size = A.get_num_rows() / ngroups_per_block + 1;\n        initializeAssignedArray_opt<IndexType, cta_size, group_size> <<< grid_size, cta_size>>>(cf_map.raw(), assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), C_hat_start.raw(), Anum_rows);\n        cudaCheckError();\n    }\n    else\n    {\n        initializeAssignedArray<IndexType, cta_size> <<< grid_size, cta_size>>>(cf_map.raw(), assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), C_hat_start.raw(), Anum_rows);\n    }\n    cudaCheckError();\n    // Count the number of passes and fill assigned for pass > 1\n    int pass = 2;\n    int max_num_passes = 10;\n    // Count the number of unassigned nodes by checking which nodes have assigned[i] = -1\n    IndexType num_unassigned = thrust_wrapper::count_if<AMGX_device>(assigned.begin(), assigned.end(), is_less_than_zero());\n    cudaCheckError();\n    IndexType num_strong_fine = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_strong_fine());\n    cudaCheckError();\n    IndexType num_unassigned_max = num_unassigned - num_strong_fine;\n\n    // Exchange 1 ring of assigned array\n    if (A.is_matrix_distributed())\n    {\n        assigned.dirtybit = 1;\n        A.manager->exchange_halo(assigned, assigned.tag);\n        // get max across all processes\n        A.manager->getComms()->all_reduce_max(num_unassigned, num_unassigned_max);\n    }\n\n    // Fill the assigned array for the other nodes, going one pass at the time\n    while (num_unassigned_max && pass < max_num_passes)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            constexpr int group_size = 8;\n            int ngroups_per_block = cta_size / group_size;\n            int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n            fillAssignedArray_opt<IndexType, cta_size, group_size> <<< nblocks, cta_size>>>(assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), Anum_rows, pass);\n            cudaCheckError();\n        }\n        else\n        {\n            fillAssignedArray<IndexType, cta_size> <<< grid_size, cta_size>>>(assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), Anum_rows, pass);\n            cudaCheckError();\n        }\n        num_unassigned = thrust_wrapper::count_if<AMGX_device>(assigned.begin(), assigned.begin() + A.get_num_rows(), is_less_than_zero());\n        cudaCheckError();\n        num_unassigned_max = num_unassigned - num_strong_fine;\n\n        if (A.is_matrix_distributed())\n        {\n            assigned.dirtybit = 1;\n            A.manager->exchange_halo(assigned, assigned.tag);\n            // get max across all processes\n            A.manager->getComms()->all_reduce_max(num_unassigned, num_unassigned_max);\n        }\n\n        // printf(\"after pass %d, %d unassigned\\n\",pass, num_unassigned_max);\n        pass++;\n    }\n\n    cudaCheckError();\n    // Check that we haven't reached maximum number of passes\n    //if (pass == max_num_passes)\n    //FatalError(\"Multipass intepolator reached maximum number of passes, exiting\\n\",AMGX_ERR_NOT_IMPLEMENTED);\n    int num_passes = pass;\n    // -------------------------------------------------------------------------------------\n    // Create array cf_map_global, which assigns a global index to all owned coarse points\n    // -------------------------------------------------------------------------------------\n    I64Vector_d cf_map_global(size_one_ring);\n    DistributedArranger<TConfig_d> *prep = NULL;\n    I64Vector_d P_col_indices_global;\n    int coarsePoints;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        cudaCheckError();\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        coarsePoints = num_owned_coarse_pts + num_halo_coarse_pts;\n        // Partially initialize the distributed manager of matrix P, using num_owned_coarse_pts\n        prep->initialize_manager(A, P, num_owned_coarse_pts);\n        int my_rank = A.manager->global_id();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (num_owned_fine_pts + cta_size - 1) / cta_size);\n        // Fill the cf_map_global array, which will assign for each coarse pt, the global index of that coarse pt\n        createCfMapGlobal<IndexType, int64_t> <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), P.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n        cudaCheckError();\n        // Exchange the cf_map_global so that we know the coarse global id of halo nodes in one ring\n        cf_map_global.dirtybit = 1;\n        A.manager->exchange_halo(cf_map_global, cf_map_global.tag);\n    }\n    else\n    {\n        coarsePoints = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (A.get_num_rows() + cta_size - 1) / cta_size);\n        fill_cf_map_global <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), A.get_num_rows());\n        cudaCheckError();\n    }\n\n    // ----------------------------------------------------------\n    // Create an upper bound for the length of each row P\n    // ----------------------------------------------------------\n    Hash_Workspace<TConfig_d, int64_t> exp_wk(true, GRID_SIZE);\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int work_offset = GRID_SIZE * NUM_WARPS;\n        cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n        cudaCheckError();\n\n        // Do one ring exchange of C_hat_start, which have been filled for nodes with assigned=1\n        if (A.is_matrix_distributed())\n        {\n            C_hat_start.dirtybit = 1;\n            A.manager->exchange_halo(C_hat_start, C_hat_start.tag);\n        }\n\n        for (int i = 2; i < num_passes; i++)\n        {\n            if(this->m_use_opt_kernels)\n            {\n                constexpr int group_size = 8;\n                int ngroups_per_block = CTA_SIZE / group_size;\n                int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n                multipass::estimate_c_hat_size_kernel_opt< CTA_SIZE, group_size> <<< nblocks, CTA_SIZE>>>(\n                        A.get_num_rows(),\n                        A.row_offsets.raw(),\n                        A.col_indices.raw(),\n                        s_con.raw(),\n                        assigned.raw(),\n                        C_hat_start.raw(),\n                        i);\n                        cudaCheckError();\n            }\n            else\n            {\n                multipass::estimate_c_hat_size_kernel< CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                        A.get_num_rows(),\n                        A.row_offsets.raw(),\n                        A.col_indices.raw(),\n                        s_con.raw(),\n                        assigned.raw(),\n                        C_hat_start.raw(),\n                        i);\n                        cudaCheckError();\n            }\n\n            if (A.is_matrix_distributed())\n            {\n                // TODO: This could be improved by only doing the exchange for points in current pass\n                C_hat_start.dirtybit = 1;\n                A.manager->exchange_halo(C_hat_start, C_hat_start.tag);\n            }\n        }\n\n        cudaCheckError();\n    }\n\n    // Do an exclusive scan on C_hat_start to compute compute upper bound on row offsets\n    thrust_wrapper::exclusive_scan<AMGX_device>( C_hat_start.begin( ), C_hat_start.end( ), C_hat_start.begin( ) );\n    cudaCheckError();\n    // Now C_hat_start contains the offsets\n    // Create a temporary manager that can be used to exchange halo information on values and/or column indices, where there's more than 1 value associated with each node\n    Matrix<TConfig_d> C_hat_matrix(1, 1, 1, 0);\n\n    // Create manager to exchange C_hat data\n    if (A.is_matrix_distributed())\n    {\n        prep->createTempManager(C_hat_matrix, A, C_hat_start);\n    }\n\n    // ----------------------------------------------------------\n    // Count the actual number of nonzeroes in each row of P\n    // ----------------------------------------------------------\n    // Allocate memory to store columns/values.\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    // C_hat stores the global indices of the coarse nodes that each fine point depends on\n    I64Vector_d C_hat( nVals, -1 );\n    // C_hat_pos is an array used by the hash set structure (check with Julien for more details)\n    IntVector C_hat_pos( nVals );\n    // C_hat_size stores the number of coarse points on which each fine point depends on\n    IntVector C_hat_size( size_one_ring + 1, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        if(this->m_use_opt_kernels)\n        {\n            constexpr int group_size = 8;\n            int ngroups_per_block = CTA_SIZE / group_size;\n            int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n\n            // This will fill C_hat for nodes with assigned = 1 (for fine points strongly connected to coarse points)\n            multipass::compute_c_hat_first_pass_kernel_opt< CTA_SIZE, WARP_SIZE, group_size, int64_t> <<< nblocks, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                s_con.raw(),\n                cf_map_global.raw(),\n                C_hat_start.raw(),\n                C_hat_size.raw(),\n                C_hat.raw(),\n                assigned.raw());\n\n            cudaCheckError();\n        }\n        else\n        {\n            // This will fill C_hat for nodes with assigned = 1 (for fine points strongly connected to coarse points)\n            multipass::compute_c_hat_first_pass_kernel< CTA_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                s_con.raw(),\n                cf_map_global.raw(),\n                C_hat_start.raw(),\n                C_hat_size.raw(),\n                C_hat.raw(),\n                assigned.raw()\n                );\n            cudaCheckError();\n        }\n\n        // Here exchange C_hat_size and C_hat for one ring neighbors\n        if (A.is_matrix_distributed())\n        {\n            //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n            C_hat.dirtybit = 1;\n            C_hat_matrix.manager->exchange_halo(C_hat, C_hat.tag);\n            C_hat_size.dirtybit = 1;\n            A.manager->exchange_halo(C_hat_size, C_hat_size.tag);\n        }\n\n        for (int i = 2; i < num_passes; i++)\n        {\n            if(false && this->m_use_opt_kernels)\n            {\n                I64Vector_d C_hat_(C_hat);\n                IntVector C_hat_pos_(C_hat_pos);\n                IntVector C_hat_size_(C_hat_size);\n\n                IntVector assigned_in_pass(assigned.size());\n                IntVector assigned_offs(assigned.size()+1);\n\n                int nthreads = 128;\n                int nblocks = assigned.size() / nthreads + 1;\n                set_assigned_in_pass<<<nblocks, nthreads>>>(assigned.size(), i, assigned.raw(), assigned_in_pass.raw(), assigned_offs.raw());\n                cudaCheckError();\n\n                thrust_wrapper::inclusive_scan<AMGX_device>(assigned_in_pass.begin(), assigned_in_pass.end(), assigned_offs.begin()+1);\n\n                int nassigned_in_pass = assigned_offs[assigned_offs.size()-1] ;\n                if(nassigned_in_pass > 0)\n                {\n                    IntVector assigned_set(nassigned_in_pass);\n                    assigned_set_fill<<<nblocks, nthreads>>>(assigned.size(), assigned_in_pass.raw(), assigned_offs.raw(), assigned_set.raw());\n                    cudaCheckError();\n\n                    int max_c_hat_size = thrust::reduce(C_hat_size_.begin(), C_hat_size_.end(), -1, thrust::maximum<int>());\n\n                    int hash_size = pow(2, ceil(log2(max_c_hat_size)));\n\n                    switch(hash_size)\n                    {\n                        case 1:\n                        case 2:\n                        case 4:\n                        case 8:\n                        case 16:\n                        case 32:\n                            compute_c_hat_opt_dispatch<32, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 64:\n                            compute_c_hat_opt_dispatch<64, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 128:\n                            compute_c_hat_opt_dispatch<128, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 256:\n                            compute_c_hat_opt_dispatch<256, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 512:\n                            compute_c_hat_opt_dispatch<512, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        default:\n                            FatalError(\"Size of C_hat in a single row too large, solve with original solvers.\", AMGX_ERR_INTERNAL);\n                    }\n                }\n\n                cudaDeviceSynchronize();\n                cudaCheckError();\n\n#if 0\n                if (A.is_matrix_distributed())\n                {\n                    //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n                    C_hat_.dirtybit = 1;\n                    C_hat_matrix.manager->exchange_halo(C_hat_, C_hat_.tag);\n                    C_hat_size_.dirtybit = 1;\n                    A.manager->exchange_halo(C_hat_size_, C_hat_size_.tag);\n                }\n#endif\n            }\n            else\n            {\n            }\n\n            // In case the hash table is not big enough, run multiple attemps\n            int attempt = 0;\n\n            for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n            {\n                // Double the amount of GMEM (if needed).\n                if ( attempt > 0 )\n                {\n                    exp_wk.expand();\n                }\n\n                // Reset the status. TODO: Launch async copies.\n                int status = 0;\n                cudaMemcpy( exp_wk.get_status(), &status, sizeof(int), cudaMemcpyHostToDevice );\n                cudaCheckError();\n                // Compute the set C_hat.\n                int work_offset = GRID_SIZE * NUM_WARPS;\n                cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n                cudaCheckError();\n                // Run the computation.\n                multipass::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    assigned.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status(),\n                    i);\n                    cudaCheckError();\n\n                if (A.is_matrix_distributed())\n                {\n                    //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n                    C_hat.dirtybit = 1;\n                    C_hat_matrix.manager->exchange_halo(C_hat, C_hat.tag);\n                    C_hat_size.dirtybit = 1;\n                    A.manager->exchange_halo(C_hat_size, C_hat_size.tag);\n                }\n\n                cudaCheckError();\n                // Read the result from count_non_zeroes.\n                cudaMemcpy( &status, exp_wk.get_status(), sizeof(int), cudaMemcpyDeviceToHost );\n                cudaCheckError();\n                done = status == 0;\n            }\n        }\n    }\n    // At this stage, for each fine node (even in 1 ring halo), we have the global indices\n    // of the interpolatory coarse points\n    // count the number of non-zeros in the interpolation matrix,\n    // For coarse point, nonzeros = 1\n    // for strong fine, nonzeros = 0\n    // for other points, nonzeros[i] = C_hat_size[i]\n    IntVector nonZeroOffsets(size_one_ring + 1);\n    IntVector nonZerosPerRow(size_one_ring);\n    nonZerosPerRowSizeKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map_ptr, C_hat_size.raw(),\n            nonZerosPerRow.raw());\n    cudaCheckError();\n\n    // Do a one ring halo exchange to know the lengths of the neighbors row\n    if (A.is_matrix_distributed())\n    {\n        nonZerosPerRow.dirtybit = 1;\n        A.manager->exchange_halo(nonZerosPerRow, nonZerosPerRow.tag);\n    }\n\n    // get total number of non-zeros with reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[nonZeroOffsets.size() - 1] = nonZeros;\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);\n    // TODO: is the number of columns used anywhere?\n    P.resize(size_one_ring, coarsePoints, nonZeros, 1);\n    cudaCheckError();\n    P_col_indices_global.resize(P.col_indices.size());\n    Matrix<TConfig_d> P_temp(1, 1, 1, 0);\n\n    if (A.is_matrix_distributed())\n    {\n        prep->createTempManager(P_temp, A, nonZeroOffsets);\n    }\n\n    IndexType *Poffsets = P.row_offsets.raw();\n    IndexType *Pcolumn_indices = P.col_indices.raw();\n    ValueType *Pvalues = P.values.raw();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // grab the diagonal terms\n    VVector diag(size_one_ring);\n    ValueType *diag_ptr = diag.raw();\n    {\n        find_diag_kernel_indexed_dia <<< numBlocksOneRing, blockSize>>>(\n            size_one_ring,\n            A.diag.raw(),\n            A.values.raw(),\n            diag.raw());\n    }\n    cudaCheckError();\n\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n\n    // Fill P.col_indices_global, P.values\n    if(this->m_use_opt_kernels)\n    {\n        int grid_size = A.get_num_rows() / NUM_WARPS + 1;\n\n        multipass::compute_interp_weight_first_pass_kernel_opt\n            <Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t>\n            <<< grid_size, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map_global.raw(),\n            s_con.raw(),\n            diag.raw(),\n            P.row_offsets.raw(),\n            P_col_indices_global.raw(),\n            P.values.raw(),\n            assigned.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n        cudaCheckError();\n\n        // Compute the set C_hat.\n        multipass::compute_interp_weight_first_pass_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map_global.raw(),\n            s_con.raw(),\n            diag.raw(),\n            P.row_offsets.raw(),\n            P_col_indices_global.raw(),\n            P.values.raw(),\n            assigned.raw());\n        cudaCheckError();\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n        P_col_indices_global.dirtybit = 1;\n        P_temp.manager->exchange_halo(P_col_indices_global, P_col_indices_global.tag);\n        P.values.dirtybit = 1;\n        P_temp.manager->exchange_halo(P.values, P.values.tag);\n    }\n\n    for (int i = 2; i < num_passes; i++)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            IntVector assigned_in_pass(assigned.size());\n            IntVector assigned_offs(assigned.size() + 1);\n\n            int nthreads = 128;\n            int nblocks = assigned.size() / nthreads + 1;\n            set_assigned_in_pass<<<nblocks, nthreads>>>(assigned.size(), i, assigned.raw(), assigned_in_pass.raw(), assigned_offs.raw());\n            cudaCheckError();\n            thrust_wrapper::inclusive_scan<AMGX_device>(assigned_in_pass.begin(), assigned_in_pass.end(), assigned_offs.begin()+1);\n\n            IntVector assigned_set(assigned_offs[assigned_offs.size()-1]);\n            assigned_set_fill<<<nblocks, nthreads>>>(assigned.size(), assigned_in_pass.raw(), assigned_offs.raw(), assigned_set.raw());\n            cudaCheckError();\n\n            Hash_Workspace<TConfig_d, int64_t> exp_wk2(true, 4096);\n            multipass::compute_interp_weight_kernel_opt<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< 4096, CTA_SIZE>>>(\n                    assigned_set.size(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.values.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    diag.raw(),\n                    P.row_offsets.raw(),\n                    P_col_indices_global.raw(),\n                    P.values.raw(),\n                    exp_wk2.get_gmem_size(),\n                    exp_wk2.get_keys(),\n                    exp_wk2.get_vals(),\n                    exp_wk2.get_work_queue(),\n                    assigned.raw(),\n                    assigned_set.raw(),\n                    i);\n                    cudaCheckError();\n        }\n        else\n        {\n            cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n            cudaCheckError();\n            multipass::compute_interp_weight_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.values.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    diag.raw(),\n                    P.row_offsets.raw(),\n                    P_col_indices_global.raw(),\n                    P.values.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_vals(),\n                    exp_wk.get_work_queue(),\n                    assigned.raw(),\n                    i);\n                    cudaCheckError();\n        }\n\n        if (A.is_matrix_distributed())\n        {\n            //TODO: This is exchanging too much data\n            P_col_indices_global.dirtybit = 1;\n            P_temp.manager->exchange_halo(P_col_indices_global, P_col_indices_global.tag);\n            P.values.dirtybit = 1;\n            P_temp.manager->exchange_halo(P.values, P.values.tag);\n        }\n\n        cudaCheckError();\n    }\n\n    if (!A.is_matrix_distributed())\n    {\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, ( (int) P.col_indices.size() + cta_size - 1) / cta_size);\n        // Simply cast from int64 to int\n        fill_P_col_indices <<< grid_size, cta_size>>>(P_col_indices_global.raw(), P.col_indices.raw(), (int) P.col_indices.size());\n        cudaCheckError();\n    }\n    else\n    {\n        // Resize P to only include the owned rows and columns\n        int P_num_nnz = P.row_offsets[P.get_num_rows()];\n        P.resize(A.get_num_rows(), P.manager->halo_offsets[0], P_num_nnz, 1);\n        // Using the global col indices, identify neighbors for P.manager, fill B2L_maps, etc...\n        prep->initialize_manager_from_global_col_indices(P, P_col_indices_global);\n        // Create the list of interior and boundary nodes (this is necessary if doing masked SpMV operations)\n        prep->createRowsLists(P, true);\n    }\n\n    // Delete the temporary distributed arranger\n    if (prep != NULL) { delete prep; }\n}\n\ntemplate< class T_Config>\nvoid Multipass_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimensions for multipass interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    //P.computeDiagonal();\n    P.set_initialized(1);\n#if 0\n    P.printToFile(\"P.out\", \"\", -1, -1);\n#endif\n}\n\n#define AMGX_CASE_LINE(CASE) template class Multipass_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Multipass_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <classical/interpolators/multipass.h>\n#include <classical/interpolators/common.h>\n#include <device_properties.h>\n#include <amgx_timer.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <sort.h>\n#include <assert.h>\n#include <misc.h>\n#include <sstream>\n#include <csr_multiply.h>\n#include <hash_workspace.h>\n#include <thrust/count.h>\n#include <thrust/scan.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nusing std::set;\nusing std::vector;\n\ntemplate < typename IndexType, typename ValueType >\n__global__\nvoid check_P_values(IndexType *offsets, ValueType *val, int size, int flag)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < size; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        for (int i = offsets[tidx]; i < offsets[tidx + 1]; i++)\n        {\n            if (isnan(val[i])) { printf(\"%d, row = %d, i=%d\\n\", flag, tidx, i); }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid fill_cf_map_global(const IndexType *cf_map, KeyType *cf_map_global, IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        cf_map_global[tidx] = (KeyType) cf_map[tidx];\n    }\n}\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid fill_P_col_indices(const KeyType *global_cols, IndexType *cols, IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Simply cast\n        cols[tidx] = (IndexType) global_cols[tidx];\n    }\n}\n\n\n\ntemplate <typename IndexType, typename KeyType>\n__global__\nvoid createCfMapGlobal(const IndexType *cf_map, KeyType *cf_map_global, const KeyType my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (KeyType) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid initializeAssignedArray(const IndexType *cf_map, IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, IndexType *C_hat_offsets, int A_num_rows)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        int coarse_fine_id = cf_map[aRowId];\n\n        if ( coarse_fine_id >= 0)\n        {\n            assigned[aRowId] = 0;\n        }\n        else if (coarse_fine_id == FINE)\n        {\n            // Load A row IDs.\n            int aColIt  = A_rows[aRowId  ];\n            int aColEnd = A_rows[aRowId + 1];\n            int coarse_count = 0;\n\n            // Iterate over the columns of A.\n            for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n                // Is it fine.\n                bool is_coarse  = is_off_diagonal && cf_map[aColId] >= 0;\n                // Is it strongly connected ?\n                bool is_coarse_strongly_connected = aColIt < aColEnd && s_con[aColIt] && is_coarse;\n                // Push coarse and strongly connected nodes in the set.\n                coarse_count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            }\n\n            // If fine point connected to coarse point, assigned = 1,\n            if (coarse_count)\n            {\n                assigned[aRowId] = 1;\n                C_hat_offsets[aRowId] = coarse_count;\n            }\n        }\n        else // Strong fine\n        {\n            assigned[aRowId] = -1;\n        }\n    }\n}\n\ntemplate <typename IndexType, int cta_size, int group_size>\n__global__\nvoid initializeAssignedArray_opt(const IndexType *cf_map, IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, IndexType *C_hat_offsets, int A_num_rows)\n{\n    const int ngroups = cta_size / group_size;\n\n    // The coordinates of the thread inside the CTA/group.\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    bool active = true;\n    if(a_row_id > A_num_rows)\n    {\n        active = false;\n    }\n\n    int coarse_fine_id = UNASSIGNED;\n\n    if(active)\n    {\n        coarse_fine_id = cf_map[a_row_id];\n\n        if(coarse_fine_id >= 0)\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = 0;\n            }\n\n            active = false;\n        }\n        else if(coarse_fine_id != FINE) // Strong fine\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = -1;\n            }\n\n            active = false;\n        }\n    }\n\n    active = (active && coarse_fine_id == FINE);\n\n    constexpr int warp_size = 32;\n    unsigned int base_mask = 0xFFFFFFFF >> (warp_size - group_size);\n    int ngroups_in_warp = warp_size / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // Load A row IDs.\n    int aColIt  = A_rows[a_row_id  ];\n    int aColEnd = A_rows[a_row_id + 1];\n\n    // Iterate over the columns of A.\n    int coarse_count = 0;\n    for ( aColIt += lane_id ; utils::any( active && aColIt < aColEnd ) ; aColIt += group_size )\n    {\n        bool is_coarse_strongly_connected = false;\n\n        if(active)\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = aColIt < aColEnd && aColId != a_row_id;\n\n            // Is it fine.\n            bool is_coarse  = is_off_diagonal && cf_map[aColId] >= 0;\n\n            // Is it strongly connected ?\n            is_coarse_strongly_connected = s_con[aColIt] && is_coarse;\n        }\n\n        // Count any of the threads in the group that had strong connections\n        int coarse_exist_flags = utils::ballot(is_coarse_strongly_connected);\n        if(lane_id == 0)\n        {\n            coarse_count += __popc(coarse_exist_flags & group_mask);\n        }\n    }\n\n    // If fine point connected to coarse point, assigned = 1,\n    if (active && coarse_count > 0)\n    {\n        if(lane_id == 0)\n        {\n            assigned[a_row_id] = 1;\n            C_hat_offsets[a_row_id] = coarse_count;\n        }\n    }\n}\n\ntemplate<typename IndexType, int cta_size >\n__global__\nvoid fillAssignedArray(IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, int A_num_rows, int pass)\n{\n    const int nWarps = cta_size / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        if ( assigned[aRowId] == -1)\n        {\n            // Load A row IDs.\n            int aColIt  = A_rows[aRowId  ];\n            int aColEnd = A_rows[aRowId + 1];\n            int assigned_count = 0;\n\n            // Iterate over the columns of A.\n            for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n                // Is it assigned\n                bool is_assigned = is_off_diagonal && assigned[aColId] == pass - 1;\n                // Is it strongly connected ?\n                bool is_assigned_strongly_connected = aColIt < aColEnd && s_con[aColIt] && is_assigned;\n                // Push coarse and strongly connected nodes in the set.\n                assigned_count += __popc( utils::ballot( is_assigned_strongly_connected ) );\n\n                if (assigned_count) { break; }\n            }\n\n            // If one of the strong neighbor has been assigned, set assigned to current pass number\n            if (assigned_count)\n            {\n                assigned[aRowId] = pass;\n            }\n        }\n    }\n}\n\ntemplate < typename IndexType, int cta_size, int group_size >\n__global__\nvoid fillAssignedArray_opt(IndexType *assigned, const IndexType *A_rows, const IndexType *A_cols, const bool *s_con, int A_num_rows, int pass)\n{\n    // The coordinates of the thread inside the CTA/groups\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n    const int ngroups_per_block = cta_size / group_size;\n\n    int a_row_id = blockIdx.x * ngroups_per_block + group_id;\n\n    bool active = true;\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != -1)\n    {\n        active = false;\n    }\n\n    // Mask for the subgroup\n    constexpr int warp_size = 32;\n    unsigned int base_mask = 0xFFFFFFFF >> (warp_size - group_size);\n    int ngroups_in_warp = warp_size / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // Load A row IDs.\n    int a_col_begin = 0;\n    int a_col_end   = 0;\n\n    if(active)\n    {\n        a_col_begin = A_rows[a_row_id  ];\n        a_col_end   = A_rows[a_row_id + 1];\n    }\n\n    // Iterate over the columns of A.\n    for (int a_col_it = a_col_begin + lane_id ; utils::any(active && a_col_it < a_col_end); a_col_it += group_size)\n    {\n        bool is_assigned_strongly_connected = false;\n\n        if(active)\n        {\n            // Columns of A maps to rows of B. Each thread of the group loads its A-col/B-row ID.\n            int a_col_id = a_col_it < a_col_end ? A_cols[a_col_it] : -1;\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n\n            // Is it assigned\n            bool is_assigned = is_off_diagonal && assigned[a_col_id] == pass - 1;\n\n            // Is it strongly connected ?\n            is_assigned_strongly_connected = s_con[a_col_it] && is_assigned;\n        }\n\n        int group_strongly_connected = utils::ballot(is_assigned_strongly_connected);\n\n        // Push coarse and strongly connected nodes in the set.\n        if(active && (group_strongly_connected & group_mask))\n        {\n            if(lane_id == 0)\n            {\n                assigned[a_row_id] = pass;\n            }\n\n            active = false;\n        }\n    }\n}\n\n\n/*\n * remove all duplicates from a vector\n */\ntemplate <typename T>\nvoid removeDuplicates(vector<T> &vec)\n{\n    std::sort(vec.begin(), vec.end());\n    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());\n}\n\n/*\n * find the value at a particular column of a row if it exists\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type findValue(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const int column_needed)\n{\n    typename VVector::value_type v = 0;\n\n    // NAIVE\n    for (int k = row_begin; k < row_end; k++)\n    {\n        int kcol = columns[k];\n\n        if (kcol == column_needed)\n        {\n            v = values[k];\n        }\n    }\n\n    return v;\n}\n\n\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_h &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_h &P)\n{\n    FatalError(\"multipass interpolation not implemented on host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n} // end multipass interpolator\n\n\n///////////////////////////////////////////////////////////////////////////////\n\n// DEVICE CODE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace multipass\n{\n\n#include <sm_utils.inl>\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const bool *s_con,\n                            const int *assigned,\n                            int *C_hat_offsets,\n                            int pass )\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    // Number of items per grid.\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    volatile int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID )\n    {\n        // Skip coarse rows and fine rows touching a coarse point\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int my_count = 0;\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // assigned == pass-1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n            // Is it a coarse, strongly-connected column and already assigned?\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n            // If so, add their C_hat_offset value to my_count\n            my_count += (is_assigned_strongly_connected ? C_hat_offsets[a_col_id] : 0);\n        }\n\n        // Do reduction\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            my_count += utils::shfl_xor( my_count, mask );\n        }\n\n        // Write result -- RACE CONDITION!\n        if (lane_id == 0)\n        {\n            C_hat_offsets[a_row_id] = my_count;\n        }\n    }\n}\n\ntemplate< int CTA_SIZE, int group_size >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel_opt( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const bool *s_con,\n                            const int *assigned,\n                            int *C_hat_offsets,\n                            int pass )\n{\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    int ngroups = CTA_SIZE / group_size;\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    // Skip coarse rows and fine rows touching a coarse point\n    bool active = true;\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != pass)\n    {\n        active = false;\n    }\n\n    int my_count = 0;\n\n    // Load A row IDs.\n    if(active)\n    {\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n\n        // Iterate over the columns of A to build C_hat.\n        for (int a_col_it = a_col_begin + lane_id; a_col_it < a_col_end; a_col_it += group_size )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // assigned == pass-1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n\n            // Is it a coarse, strongly-connected column and already assigned?\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n\n            // If so, add their C_hat_offset value to my_count\n            my_count += (is_assigned_strongly_connected ? C_hat_offsets[a_col_id] : 0);\n        }\n    }\n\n#pragma unroll\n    for(int i = group_size / 2; i > 0; i /= 2)\n    {\n        my_count += utils::shfl_down(my_count, i);\n    }\n\n    if (active && lane_id == 0)\n    {\n        C_hat_offsets[a_row_id] = my_count;\n    }\n}\n\n    template< int CTA_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\n    void\ncompute_c_hat_first_pass_kernel( int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const bool *__restrict s_con,\n        const KeyType *__restrict cf_map_global,\n        const int *__restrict C_hat_start,\n        int *__restrict C_hat_size,\n        KeyType *__restrict C_hat,\n        int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / WARP_SIZE;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Skip if not in current pass\n        if (assigned[a_row_id] != 1)\n        {\n            continue;\n        }\n\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        int count = 0;\n        int c_col_it = C_hat_start[a_row_id];\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == 0);\n            // Is it an assigned==pass -1 and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                //if (cf_map[a_col_id] < 0)\n                //  printf(\"error in multipass 1\\n\");\n                C_hat[c_col_it + count + dest] = cf_map_global[a_col_id];\n            }\n\n            count += __popc( vote );\n        }\n\n        if ( lane_id == 0 )\n        {\n            C_hat_size[a_row_id] = count;\n        }\n    }\n}\n\n    template< int CTA_SIZE, int WARP_SIZE, int group_size, typename KeyType>\n__global__ __launch_bounds__( CTA_SIZE )\n    void\ncompute_c_hat_first_pass_kernel_opt( int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const bool *__restrict s_con,\n        const KeyType *__restrict cf_map_global,\n        const int *__restrict C_hat_start,\n        int *__restrict C_hat_size,\n        KeyType *__restrict C_hat,\n        int *__restrict assigned)\n{\n    const int ngroups = CTA_SIZE / group_size;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int group_id = threadIdx.x / group_size;\n    const int lane_id = threadIdx.x % group_size;\n\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * ngroups + group_id;\n\n    bool active = true;\n\n    // Skip if not in current pass\n    if (a_row_id >= A_num_rows || assigned[a_row_id] != 1)\n    {\n        active = false;\n    }\n\n    int a_col_begin = 0;\n    int a_col_end   = 0;\n    int c_col_it = 0;\n\n    if(active)\n    {\n        a_col_begin = A_rows[a_row_id];\n        a_col_end   = A_rows[a_row_id + 1];\n        c_col_it = C_hat_start[a_row_id];\n    }\n\n    unsigned int base_mask = 0xFFFFFFFF >> (WARP_SIZE - group_size);\n    int ngroups_in_warp = WARP_SIZE / group_size;\n    int group_id_in_warp = group_id % ngroups_in_warp;\n    unsigned int group_mask = base_mask << (group_id_in_warp*group_size);\n\n    // _iterate over the columns of A to build C_hat.\n    int count = 0;\n    for ( int a_col_it = a_col_begin + lane_id ; utils::any(active && a_col_it < a_col_end) ; a_col_it += group_size )\n    {\n        bool is_assigned_strongly_connected = false;\n\n        // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n        int a_col_id = -1;\n\n        if(active)\n        {\n            if (a_col_it < a_col_end)\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = (a_col_it < a_col_end) && (a_col_id != a_row_id);\n\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n\n            // assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == 0);\n\n            // Is it an assigned==pass -1 and strongly-connected column.\n            is_assigned_strongly_connected = is_strongly_connected && is_assigned;\n        }\n\n        // We collect fine and strongly-collected columns.\n        int all_vote = utils::ballot( is_assigned_strongly_connected );\n        int group_vote = all_vote & group_mask;\n        int dest = __popc( group_vote & utils::lane_mask_lt() );\n\n        if ( active && is_assigned_strongly_connected )\n        {\n            C_hat[c_col_it + count + dest] = cf_map_global[a_col_id];\n        }\n\n        count += __popc( group_vote );\n    }\n\n    if ( active && lane_id == 0 )\n    {\n        C_hat_size[a_row_id] = count;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_size,\n                      KeyType *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int *__restrict assigned,\n                      int gmem_size,\n                      KeyType *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status,\n                      int pass )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<KeyType, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip if not in current pass\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Check if assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = is_active && s_con[a_col_it];\n            // Check if strong connection to assigned == pass-1\n            bool is_assigned_strongly_connected = is_assigned && is_strongly_connected;\n            // We collect columns for which assigned == pass - 1\n            int vote = utils::ballot( is_assigned_strongly_connected);\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected)\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    // this loops over elements of C_hat for row uniform_b_row_id\n                    b_col_it  = C_hat_start[uniform_b_row_id];\n                    b_col_end = b_col_it + C_hat_size[uniform_b_row_id];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the interpolatory point\n                    KeyType  b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = C_hat[b_col_it];\n                    }\n\n                    // Is it valid\n                    is_off_diagonal = b_col_it < b_col_end; //  && b_col_id != local_k;\n                    // Push node to set\n                    KeyType b_item = -1;\n\n                    if ( is_off_diagonal )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    // Insert the interpolatory point to the set\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_size[a_row_id] = count;\n        }\n    }\n}\n\ntemplate <int GROUP_SIZE, int CTA_SIZE, int HASH_SIZE, class KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel_opt( int assigned_set_size,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_size,\n                      KeyType *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int *__restrict assigned,\n                      int *__restrict assigned_set,\n                      int pass )\n{\n    constexpr KeyType SLOT_VACANT = -1;\n\n    // Group indices\n    constexpr int ngroups = CTA_SIZE / GROUP_SIZE;\n    const int group_id = threadIdx.x / GROUP_SIZE;\n    const int lane_id = threadIdx.x % GROUP_SIZE;\n\n    const int gid = blockIdx.x * ngroups + group_id;\n\n    // Dynamic sized shared memory\n    extern __shared__ int s[];\n\n    // Block-level hash container storage\n    KeyType* key_s = (KeyType*)s;\n\n    //KeyType* data_s = (KeyType*)&key_s[ngroups*HASH_SIZE];\n    int* col_ind_s = (int*)&key_s[ngroups*HASH_SIZE];\n\n    // Group-level hash containers\n    KeyType* key_group_s = &key_s[group_id*HASH_SIZE];\n    //KeyType* data_group_s = &data_s[group_id*HASH_SIZE];\n\n    // Initialise the keys and values.\n#pragma unroll\n    for(int i = threadIdx.x; i < ngroups*HASH_SIZE; i += CTA_SIZE)\n    {\n        key_s[i] = SLOT_VACANT; // Inserted keys will be in range [0,N]\n        //data_s[i] = 0; // We will sum into values\n    }\n\n    if(lane_id == 0)\n    {\n        col_ind_s[group_id] = 0;\n    }\n\n    __syncthreads();\n\n    int a_row_id = -1;\n\n    if(gid < assigned_set_size)\n    {\n        // One row of A per group\n        a_row_id = assigned_set[gid];\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = A_rows[a_row_id] + lane_id ; a_col_it < A_rows[a_row_id + 1]; a_col_it += GROUP_SIZE )\n        {\n            int a_col_id = A_cols[a_col_it];\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_id != a_row_id;\n\n            // Check if assigned == pass -1\n            bool is_assigned = is_off_diagonal && (assigned[a_col_id] == pass - 1);\n\n            // Check if we have a strong connection to the point\n            bool is_strongly_connected = s_con[a_col_it];\n\n            // Check if strong connection to assigned == pass-1\n            bool is_assigned_strongly_connected = is_assigned && is_strongly_connected;\n\n            if(!is_assigned_strongly_connected) continue;\n\n            // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n            int b_col_beg  = C_hat_start[a_col_id];\n\n            // Iterate over the range of columns of B.\n            for (int b_col_it = b_col_beg; b_col_it < b_col_beg + C_hat_size[a_col_id]; ++b_col_it)\n            {\n                KeyType key = C_hat[b_col_it];\n\n                int hash = key % HASH_SIZE;\n\n                while(true)\n                {\n                    // XXX You can just avoid this and directly atomicCAS, which is faster?\n                    int curr_key = key_group_s[hash];\n                    if(curr_key == key) break;\n                    \n                    if(curr_key == SLOT_VACANT)\n                    {\n                        KeyType old_key = (KeyType)atomicCAS((unsigned long long*)&key_group_s[hash], (unsigned long long)SLOT_VACANT, (unsigned long long)key);\n                        if(old_key == SLOT_VACANT || old_key == key)\n                        {\n                            break;\n                        }\n                    }\n\n                    // We did not secure a slot, so linear probe to next slot\n                    hash = (hash + 1) % HASH_SIZE;\n                }\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // XXX Could change to poll by ballot style compress\n    if(gid < assigned_set_size)\n    {\n        // Store the results.\n        int c_col_it = C_hat_start[a_row_id];\n\n#pragma unroll\n        for(int i = lane_id; i < HASH_SIZE; i += GROUP_SIZE)\n        {\n            KeyType key = key_group_s[i];\n            if(key != SLOT_VACANT)\n            {\n                int ind = atomicAdd(&col_ind_s[group_id], 1);\n                C_hat[c_col_it + ind] = key;\n                //C_hat_pos[c_col_it + ind] = i;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if(gid < assigned_set_size)\n    {\n        if(lane_id == 0)\n        {\n            C_hat_size[a_row_id] = col_ind_s[group_id];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_first_pass_kernel( const int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Value_type *__restrict A_vals,\n        const KeyType *__restrict cf_map_global,\n        const bool *__restrict s_con,\n        const Value_type *__restrict diag,\n        const int *__restrict P_rows,\n        KeyType *P_cols,\n        Value_type *P_vals,\n        const int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Only do work if assigned[row_id] == pass\n        KeyType coarse_fine_id = cf_map_global[a_row_id];\n\n        // If coarse rows\n        if ( assigned[a_row_id] == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n        }\n        // If strong fine\n        else if ( assigned[a_row_id] == -1 )\n        {\n            // Do nothing\n        }\n        // strongly connected to a coarse point\n        else if (assigned[a_row_id] == 1)\n        {\n            // Load A row IDs.\n            int a_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                a_col_tmp = A_rows[a_row_id + lane_id];\n            }\n\n            int a_col_it  = utils::shfl( a_col_tmp, 0 );\n            int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n            int p_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                p_col_tmp = P_rows[a_row_id + lane_id];\n            }\n\n            int p_col_it  = utils::shfl( p_col_tmp, 0 );\n            int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n            // Weak value.\n            Value_type sum_N(0), sum_C(0), alfa(0);\n            int count = 0;\n\n            // Iterate over the columns of A.\n            for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int a_col_id(-1);\n                Value_type a_value(0);\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_value  = A_vals[a_col_it];\n                }\n\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n                // Is it a strongly-connected column.\n                bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n                // Is it a weakly connected node.\n                //bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n                // Is it isolated.\n                bool is_strong_fine = is_off_diagonal && cf_map_global[a_col_id] == STRONG_FINE;\n                // Is it a fine and strongly-connected column.\n                bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == 0;\n\n                // Update the weak value.\n                if ( is_off_diagonal && !is_strong_fine)\n                {\n                    sum_N += a_value;\n                }\n\n                if ( is_assigned_strongly_connected )\n                {\n                    sum_C += a_value;\n                }\n\n                // We collect fine and strongly-collected columns.\n                int vote = utils::ballot( is_assigned_strongly_connected );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( is_assigned_strongly_connected )\n                {\n                    P_cols[p_col_it + count + dest] = cf_map_global[a_col_id];\n                    P_vals[p_col_it + count + dest] = a_value;\n                }\n\n                count += __popc( vote );\n            }\n\n            // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                sum_C += utils::shfl_xor( sum_C, mask );\n                sum_N += utils::shfl_xor( sum_N, mask );\n            }\n\n            if ( lane_id == 0 )\n            {\n                // NOTE this matches the check for zero in HYPRE\n                double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n                alfa = -sum_N / div;\n            }\n\n            alfa = utils::shfl( alfa, 0 );\n\n            // Scale the value of P\n            for ( p_col_it += lane_id ; utils::any( p_col_it < p_col_end ) ; p_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                if ( p_col_it < p_col_end )\n                {\n                    P_vals[p_col_it] *= alfa;\n                }\n            }\n        } // if (assigned[a_row_id] == 1)\n    } // Loop over rows\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_first_pass_kernel_opt( const int A_num_rows,\n        const int *__restrict A_rows,\n        const int *__restrict A_cols,\n        const Value_type *__restrict A_vals,\n        const KeyType *__restrict cf_map_global,\n        const bool *__restrict s_con,\n        const Value_type *__restrict diag,\n        const int *__restrict P_rows,\n        KeyType *P_cols,\n        Value_type *P_vals,\n        const int *__restrict assigned)\n{\n    const int NUM_WARPS_PER_CTA = CTA_SIZE / 32;\n    const int NUM_WARPS_PER_GRID = gridDim.x * NUM_WARPS_PER_CTA;\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS_PER_CTA + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += NUM_WARPS_PER_GRID)\n    {\n        // Only do work if assigned[row_id] == pass\n        KeyType coarse_fine_id = cf_map_global[a_row_id];\n\n        // If coarse rows\n        if ( assigned[a_row_id] == 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n        }\n        // If strong fine\n        else if ( assigned[a_row_id] == -1 )\n        {\n            // Do nothing\n        }\n        // strongly connected to a coarse point\n        else if (assigned[a_row_id] == 1)\n        {\n            // Load A row IDs.\n            int a_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                a_col_tmp = A_rows[a_row_id + lane_id];\n            }\n\n            int a_col_it  = utils::shfl( a_col_tmp, 0 );\n            int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n            int p_col_tmp = -1;\n\n            if ( lane_id < 2 )\n            {\n                p_col_tmp = P_rows[a_row_id + lane_id];\n            }\n\n            int p_col_it  = utils::shfl( p_col_tmp, 0 );\n            int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n            // Weak value.\n            Value_type sum_N(0), sum_C(0), alfa(0);\n            int count = 0;\n\n            // Iterate over the columns of A.\n            for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                int a_col_id(-1);\n                Value_type a_value(0);\n\n                if ( a_col_it < a_col_end )\n                {\n                    a_col_id = A_cols[a_col_it];\n                    a_value  = A_vals[a_col_it];\n                }\n\n                // Is it an off-diagonal element.\n                bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n                // Is it a strongly-connected column.\n                bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n                // Is it a weakly connected node.\n                //bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n                // Is it isolated.\n                bool is_strong_fine = is_off_diagonal && cf_map_global[a_col_id] == STRONG_FINE;\n                // Is it a fine and strongly-connected column.\n                bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == 0;\n\n                // Update the weak value.\n                if ( is_off_diagonal && !is_strong_fine)\n                {\n                    sum_N += a_value;\n                }\n\n                if ( is_assigned_strongly_connected )\n                {\n                    sum_C += a_value;\n                }\n\n                // We collect fine and strongly-collected columns.\n                int vote = utils::ballot( is_assigned_strongly_connected );\n                int dest = __popc( vote & utils::lane_mask_lt() );\n\n                if ( is_assigned_strongly_connected )\n                {\n                    P_cols[p_col_it + count + dest] = cf_map_global[a_col_id];\n                    P_vals[p_col_it + count + dest] = a_value;\n                }\n\n                count += __popc( vote );\n            }\n\n            // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n            for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n            {\n                sum_C += utils::shfl_xor( sum_C, mask );\n                sum_N += utils::shfl_xor( sum_N, mask );\n            }\n\n            if ( lane_id == 0 )\n            {\n                // NOTE this matches the check for zero in HYPRE\n                double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n                alfa = -sum_N / div;\n            }\n\n            alfa = utils::shfl( alfa, 0 );\n\n            // Scale the value of P\n            for ( p_col_it += lane_id ; utils::any( p_col_it < p_col_end ) ; p_col_it += WARP_SIZE )\n            {\n                // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n                if ( p_col_it < p_col_end )\n                {\n                    P_vals[p_col_it] *= alfa;\n                }\n            }\n        } // if (assigned[a_row_id] == 1)\n    } // Loop over rows\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel_opt( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const KeyType *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_size,\n                              const Value_type *__restrict diag,\n                              const int *__restrict P_rows,\n                              KeyType *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              KeyType *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue,\n                              const int *__restrict assigned,\n                              const int *__restrict assigned_set,\n                              int pass)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads store a value of B to load.\n    __shared__ Value_type s_b_values[CTA_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<KeyType, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a * gmem_size],\n            gmem_size );\n    // Loop over rows of A.\n    for ( ; a < A_num_rows ; a += gridDim.x*NUM_WARPS )\n    {\n        int a_row_id = assigned_set[a];\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = c_hat_it + C_hat_size[a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // sums\n        Value_type sum_N(0), sum_C(0), alfa(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a fine and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == pass - 1;\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_assigned_strongly_connected;\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_strong_fine)\n            {\n                sum_N += a_value;\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_b_values[warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                Value_type uniform_val = s_b_values[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = P_rows[b_row_id  ];\n                int b_col_end   = P_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    KeyType b_col_id(-1);\n                    Value_type tmp(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = P_cols[b_col_it];\n                        tmp = P_vals[b_col_it] * uniform_val;\n                        sum_C += tmp;\n                        sum_N += tmp;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    // Need to get fine level id for b_col_id\n                    // if (a_row_id == 8 && b_col_id == 3) printf(\"updating 8,3: %lg\\n\",tmp);\n                    bool found = map.update( b_col_id, tmp );\n                    bool any_found = (__popc(utils::ballot(found)));\n                }\n            }\n        }\n\n        // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            sum_C += utils::shfl_xor( sum_C, mask );\n            sum_N += utils::shfl_xor( sum_N, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            // NOTE this matches the check for zero in HYPRE\n            double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n            alfa = -sum_N / div;\n            // alfa = -sum_N/(sum_C*diag[a_row_id]);\n        }\n\n        alfa = utils::shfl( alfa, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_keys_scale_values( p_col_end - p_col_it, &P_cols[p_col_it], alfa, &P_vals[p_col_it] );\n    }\n}\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE, typename KeyType >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const KeyType *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_size,\n                              const Value_type *__restrict diag,\n                              const int *__restrict P_rows,\n                              KeyType *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              KeyType *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue,\n                              const int *__restrict assigned,\n                              int pass)\n{\n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ KeyType s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads store a value of B to load.\n    __shared__ Value_type s_b_values[CTA_SIZE];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<KeyType, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Only do work if assigned[row_id] == pass\n        if (assigned[a_row_id] != pass)\n        {\n            continue;\n        }\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = c_hat_it + C_hat_size[a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // sums\n        Value_type sum_N(0), sum_C(0), alfa(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a fine and strongly-connected column.\n            bool is_assigned_strongly_connected = is_strongly_connected && assigned[a_col_id] == pass - 1;\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_assigned_strongly_connected;\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_strong_fine)\n            {\n                sum_N += a_value;\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_assigned_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_assigned_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_b_values[warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            __syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                Value_type uniform_val = s_b_values[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = P_rows[b_row_id  ];\n                int b_col_end   = P_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    KeyType b_col_id(-1);\n                    Value_type tmp(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = P_cols[b_col_it];\n                        tmp = P_vals[b_col_it] * uniform_val;\n                        sum_C += tmp;\n                        sum_N += tmp;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    // Need to get fine level id for b_col_id\n                    // if (a_row_id == 8 && b_col_id == 3) printf(\"updating 8,3: %lg\\n\",tmp);\n                    bool found = map.update( b_col_id, tmp );\n                    bool any_found = (__popc(utils::ballot(found)));\n                }\n            }\n        }\n\n        // We're done with that row of A, now reduce sum_N and sum_C\n#pragma unroll\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            sum_C += utils::shfl_xor( sum_C, mask );\n            sum_N += utils::shfl_xor( sum_N, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            // NOTE this matches the check for zero in HYPRE\n            double div = (fabs(sum_C * diag[a_row_id]) == 0.0) ? 1. : sum_C * diag[a_row_id];\n            alfa = -sum_N / div;\n            // alfa = -sum_N/(sum_C*diag[a_row_id]);\n        }\n\n        alfa = utils::shfl( alfa, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_keys_scale_values( p_col_end - p_col_it, &P_cols[p_col_it], alfa, &P_vals[p_col_it] );\n    }\n}\n\n\n} // namespace multipass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*\n * grab the diagonal of the matrix\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n    }\n}\n/*************************************************************************\n * Implementing Extended+i algorithm from \\S 4.5 of:\n * \"Distance-two interpolation for parallel algebraic multigrid\"\n * Reference [4] on wiki\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nMultipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Multipass_Interpolator(AMG_Config &cfg, const std::string &cfg_scope) : Base(cfg, cfg_scope)\n{\n    this->m_use_opt_kernels = cfg.getParameter<int>(\"use_opt_kernels\", \"default\");\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nMultipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~Multipass_Interpolator()\n{}\n\nenum { WARP_SIZE = 32, GRID_SIZE = 1024, SMEM_SIZE = 128 };\n\n\nstruct is_less_than_zero\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x < 0;\n    }\n};\n\nstruct is_strong_fine\n{\n    __host__ __device__\n    bool operator()(int x)\n    {\n        return x == -3;\n    }\n};\n\n__global__ void set_assigned_in_pass(int n, int pass, int* assigned, int* assigned_in_pass, int* assigned_offs)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    assigned_in_pass[i] = (assigned[i] == pass) ? 1 : 0;\n\n    if(i == 0) \n    {\n        assigned_offs[0] = 0;\n    }\n}\n\n__global__ void assigned_set_fill(int n, int* assigned_in_pass, int* assigned_offs, int* assigned_set)\n{\n    int i = blockIdx.x*blockDim.x + threadIdx.x;\n    if(i >= n) return;\n\n    if(assigned_in_pass[i]) \n    { \n        assigned_set[assigned_offs[i]] = i; \n    } \n}\n\ntemplate <AMGX_VecPrecision V, AMGX_MatPrecision M, AMGX_IndPrecision I>\ntemplate <int hash_size, int group_size, class KeyType>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_device, V, M, I>>::compute_c_hat_opt_dispatch(\n        const Matrix_d &A,\n        const bool *s_con,\n        const int *C_hat_start,\n        int *C_hat_size,\n        KeyType *C_hat,\n        int *C_hat_pos,\n        int *assigned,\n        IntVector &assigned_set,\n        int pass )\n{\n    typedef typename IndPrecisionMap<I>::Type Index_type;\n\n    constexpr int cta_size = 128;\n    constexpr int ngroups = cta_size / group_size;\n\n    const int grid_size = assigned_set.size() / ngroups + 1;\n\n    hipDeviceProp_t deviceProps = getDeviceProperties();\n    size_t max_shmem_size = deviceProps.sharedMemPerMultiprocessor;\n\n    constexpr int shmem_size =\n        (sizeof(KeyType)+sizeof(Index_type))*ngroups*hash_size + group_size; \n\n    if(shmem_size > max_shmem_size) \n    { \n        FatalError(\"In compute_values_opt the requested hash size is larger than max.\\n\", \n                AMGX_ERR_NOT_IMPLEMENTED); \n    } \n\n    hipFuncSetAttribute(reinterpret_cast<const void*>(multipass::compute_c_hat_kernel_opt \n            <group_size, cta_size, hash_size, KeyType>), \n            hipFuncAttributeMaxDynamicSharedMemorySize, shmem_size); \n\n    multipass::compute_c_hat_kernel_opt\n        <group_size, cta_size, hash_size, KeyType> \n        <<<grid_size, cta_size, shmem_size>>>(\n            assigned_set.size(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            s_con,\n            C_hat_start,\n            C_hat_size,\n            C_hat,\n            C_hat_pos,\n            assigned,\n            assigned_set.raw(),\n            pass);\n            cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Multipass_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    nvtxRange nvr_gim(__func__);\n\n    // Implementation based on paper \"On long range interpolation operators for aggressive coarsening\" by Ulrike Meier Yang, section 4.3\n    const int blockSize = 256;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n    int *cf_map_ptr = cf_map.raw();\n    bool *s_con_ptr = s_con.raw();\n    int size_two_ring;\n    int size_one_ring;\n\n    if (A.is_matrix_distributed())\n    {\n        int offset;\n        A.getOffsetAndSizeForView(ALL, &offset, &size_two_ring);\n        A.getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    }\n    else\n    {\n        size_one_ring = A.get_num_rows();\n        size_two_ring = A.get_num_rows();\n    }\n\n    int numBlocksOneRing = std::min( 4096, (int) (size_one_ring + blockSize - 1) / blockSize );\n    int numBlocksTwoRing = std::min( 4096, (int) (size_two_ring + blockSize - 1) / blockSize );\n    int numBlocks = std::min( 4096, (int) (A.get_num_rows() + blockSize - 1) / blockSize );\n    // ----------------------------------------------------------\n    // First fill out the assigned array and count # of passes\n    // ----------------------------------------------------------\n    IntVector assigned( size_one_ring, -1);\n    IntVector C_hat_start( size_one_ring + 1, 0 );\n    // assigned[i] = -1: unassigned point\n    // assigned[i] = 0: coarse point\n    // assigned[i] = 1: fine point directly connected to coarse point\n    // assigned[i] = 2: fine point distance 2 away from strongly connected coarse point\n    // assigned[i] = ...\n    // Initialize assigned array by marking the coarse and fine point directly connected to coarse points\n    const int cta_size = 256;\n    const int nWarps = cta_size / 32;\n    int grid_size = std::min( 4096, (int) (A.get_num_rows() + nWarps - 1) / nWarps);\n    if(this->m_use_opt_kernels)\n    {\n        constexpr int group_size = 8;\n        int ngroups_per_block = cta_size / group_size;\n        int grid_size = A.get_num_rows() / ngroups_per_block + 1;\n        initializeAssignedArray_opt<IndexType, cta_size, group_size> <<< grid_size, cta_size>>>(cf_map.raw(), assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), C_hat_start.raw(), Anum_rows);\n        cudaCheckError();\n    }\n    else\n    {\n        initializeAssignedArray<IndexType, cta_size> <<< grid_size, cta_size>>>(cf_map.raw(), assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), C_hat_start.raw(), Anum_rows);\n    }\n    cudaCheckError();\n    // Count the number of passes and fill assigned for pass > 1\n    int pass = 2;\n    int max_num_passes = 10;\n    // Count the number of unassigned nodes by checking which nodes have assigned[i] = -1\n    IndexType num_unassigned = thrust_wrapper::count_if<AMGX_device>(assigned.begin(), assigned.end(), is_less_than_zero());\n    cudaCheckError();\n    IndexType num_strong_fine = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_strong_fine());\n    cudaCheckError();\n    IndexType num_unassigned_max = num_unassigned - num_strong_fine;\n\n    // Exchange 1 ring of assigned array\n    if (A.is_matrix_distributed())\n    {\n        assigned.dirtybit = 1;\n        A.manager->exchange_halo(assigned, assigned.tag);\n        // get max across all processes\n        A.manager->getComms()->all_reduce_max(num_unassigned, num_unassigned_max);\n    }\n\n    // Fill the assigned array for the other nodes, going one pass at the time\n    while (num_unassigned_max && pass < max_num_passes)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            constexpr int group_size = 8;\n            int ngroups_per_block = cta_size / group_size;\n            int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n            fillAssignedArray_opt<IndexType, cta_size, group_size> <<< nblocks, cta_size>>>(assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), Anum_rows, pass);\n            cudaCheckError();\n        }\n        else\n        {\n            fillAssignedArray<IndexType, cta_size> <<< grid_size, cta_size>>>(assigned.raw(), A.row_offsets.raw(), A.col_indices.raw(), s_con.raw(), Anum_rows, pass);\n            cudaCheckError();\n        }\n        num_unassigned = thrust_wrapper::count_if<AMGX_device>(assigned.begin(), assigned.begin() + A.get_num_rows(), is_less_than_zero());\n        cudaCheckError();\n        num_unassigned_max = num_unassigned - num_strong_fine;\n\n        if (A.is_matrix_distributed())\n        {\n            assigned.dirtybit = 1;\n            A.manager->exchange_halo(assigned, assigned.tag);\n            // get max across all processes\n            A.manager->getComms()->all_reduce_max(num_unassigned, num_unassigned_max);\n        }\n\n        // printf(\"after pass %d, %d unassigned\\n\",pass, num_unassigned_max);\n        pass++;\n    }\n\n    cudaCheckError();\n    // Check that we haven't reached maximum number of passes\n    //if (pass == max_num_passes)\n    //FatalError(\"Multipass intepolator reached maximum number of passes, exiting\\n\",AMGX_ERR_NOT_IMPLEMENTED);\n    int num_passes = pass;\n    // -------------------------------------------------------------------------------------\n    // Create array cf_map_global, which assigns a global index to all owned coarse points\n    // -------------------------------------------------------------------------------------\n    I64Vector_d cf_map_global(size_one_ring);\n    DistributedArranger<TConfig_d> *prep = NULL;\n    I64Vector_d P_col_indices_global;\n    int coarsePoints;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        cudaCheckError();\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        coarsePoints = num_owned_coarse_pts + num_halo_coarse_pts;\n        // Partially initialize the distributed manager of matrix P, using num_owned_coarse_pts\n        prep->initialize_manager(A, P, num_owned_coarse_pts);\n        int my_rank = A.manager->global_id();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (num_owned_fine_pts + cta_size - 1) / cta_size);\n        // Fill the cf_map_global array, which will assign for each coarse pt, the global index of that coarse pt\n        createCfMapGlobal<IndexType, int64_t> <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), P.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n        cudaCheckError();\n        // Exchange the cf_map_global so that we know the coarse global id of halo nodes in one ring\n        cf_map_global.dirtybit = 1;\n        A.manager->exchange_halo(cf_map_global, cf_map_global.tag);\n    }\n    else\n    {\n        coarsePoints = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (A.get_num_rows() + cta_size - 1) / cta_size);\n        fill_cf_map_global <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), A.get_num_rows());\n        cudaCheckError();\n    }\n\n    // ----------------------------------------------------------\n    // Create an upper bound for the length of each row P\n    // ----------------------------------------------------------\n    Hash_Workspace<TConfig_d, int64_t> exp_wk(true, GRID_SIZE);\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int work_offset = GRID_SIZE * NUM_WARPS;\n        hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n        cudaCheckError();\n\n        // Do one ring exchange of C_hat_start, which have been filled for nodes with assigned=1\n        if (A.is_matrix_distributed())\n        {\n            C_hat_start.dirtybit = 1;\n            A.manager->exchange_halo(C_hat_start, C_hat_start.tag);\n        }\n\n        for (int i = 2; i < num_passes; i++)\n        {\n            if(this->m_use_opt_kernels)\n            {\n                constexpr int group_size = 8;\n                int ngroups_per_block = CTA_SIZE / group_size;\n                int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n                multipass::estimate_c_hat_size_kernel_opt< CTA_SIZE, group_size> <<< nblocks, CTA_SIZE>>>(\n                        A.get_num_rows(),\n                        A.row_offsets.raw(),\n                        A.col_indices.raw(),\n                        s_con.raw(),\n                        assigned.raw(),\n                        C_hat_start.raw(),\n                        i);\n                        cudaCheckError();\n            }\n            else\n            {\n                multipass::estimate_c_hat_size_kernel< CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                        A.get_num_rows(),\n                        A.row_offsets.raw(),\n                        A.col_indices.raw(),\n                        s_con.raw(),\n                        assigned.raw(),\n                        C_hat_start.raw(),\n                        i);\n                        cudaCheckError();\n            }\n\n            if (A.is_matrix_distributed())\n            {\n                // TODO: This could be improved by only doing the exchange for points in current pass\n                C_hat_start.dirtybit = 1;\n                A.manager->exchange_halo(C_hat_start, C_hat_start.tag);\n            }\n        }\n\n        cudaCheckError();\n    }\n\n    // Do an exclusive scan on C_hat_start to compute compute upper bound on row offsets\n    thrust_wrapper::exclusive_scan<AMGX_device>( C_hat_start.begin( ), C_hat_start.end( ), C_hat_start.begin( ) );\n    cudaCheckError();\n    // Now C_hat_start contains the offsets\n    // Create a temporary manager that can be used to exchange halo information on values and/or column indices, where there's more than 1 value associated with each node\n    Matrix<TConfig_d> C_hat_matrix(1, 1, 1, 0);\n\n    // Create manager to exchange C_hat data\n    if (A.is_matrix_distributed())\n    {\n        prep->createTempManager(C_hat_matrix, A, C_hat_start);\n    }\n\n    // ----------------------------------------------------------\n    // Count the actual number of nonzeroes in each row of P\n    // ----------------------------------------------------------\n    // Allocate memory to store columns/values.\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    // C_hat stores the global indices of the coarse nodes that each fine point depends on\n    I64Vector_d C_hat( nVals, -1 );\n    // C_hat_pos is an array used by the hash set structure (check with Julien for more details)\n    IntVector C_hat_pos( nVals );\n    // C_hat_size stores the number of coarse points on which each fine point depends on\n    IntVector C_hat_size( size_one_ring + 1, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = A.get_num_nz() / A.get_num_rows();\n        if(this->m_use_opt_kernels)\n        {\n            constexpr int group_size = 8;\n            int ngroups_per_block = CTA_SIZE / group_size;\n            int nblocks = A.get_num_rows() / ngroups_per_block + 1;\n\n            // This will fill C_hat for nodes with assigned = 1 (for fine points strongly connected to coarse points)\n            multipass::compute_c_hat_first_pass_kernel_opt< CTA_SIZE, WARP_SIZE, group_size, int64_t> <<< nblocks, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                s_con.raw(),\n                cf_map_global.raw(),\n                C_hat_start.raw(),\n                C_hat_size.raw(),\n                C_hat.raw(),\n                assigned.raw());\n\n            cudaCheckError();\n        }\n        else\n        {\n            // This will fill C_hat for nodes with assigned = 1 (for fine points strongly connected to coarse points)\n            multipass::compute_c_hat_first_pass_kernel< CTA_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                s_con.raw(),\n                cf_map_global.raw(),\n                C_hat_start.raw(),\n                C_hat_size.raw(),\n                C_hat.raw(),\n                assigned.raw()\n                );\n            cudaCheckError();\n        }\n\n        // Here exchange C_hat_size and C_hat for one ring neighbors\n        if (A.is_matrix_distributed())\n        {\n            //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n            C_hat.dirtybit = 1;\n            C_hat_matrix.manager->exchange_halo(C_hat, C_hat.tag);\n            C_hat_size.dirtybit = 1;\n            A.manager->exchange_halo(C_hat_size, C_hat_size.tag);\n        }\n\n        for (int i = 2; i < num_passes; i++)\n        {\n            if(false && this->m_use_opt_kernels)\n            {\n                I64Vector_d C_hat_(C_hat);\n                IntVector C_hat_pos_(C_hat_pos);\n                IntVector C_hat_size_(C_hat_size);\n\n                IntVector assigned_in_pass(assigned.size());\n                IntVector assigned_offs(assigned.size()+1);\n\n                int nthreads = 128;\n                int nblocks = assigned.size() / nthreads + 1;\n                set_assigned_in_pass<<<nblocks, nthreads>>>(assigned.size(), i, assigned.raw(), assigned_in_pass.raw(), assigned_offs.raw());\n                cudaCheckError();\n\n                thrust_wrapper::inclusive_scan<AMGX_device>(assigned_in_pass.begin(), assigned_in_pass.end(), assigned_offs.begin()+1);\n\n                int nassigned_in_pass = assigned_offs[assigned_offs.size()-1] ;\n                if(nassigned_in_pass > 0)\n                {\n                    IntVector assigned_set(nassigned_in_pass);\n                    assigned_set_fill<<<nblocks, nthreads>>>(assigned.size(), assigned_in_pass.raw(), assigned_offs.raw(), assigned_set.raw());\n                    cudaCheckError();\n\n                    int max_c_hat_size = thrust::reduce(C_hat_size_.begin(), C_hat_size_.end(), -1, thrust::maximum<int>());\n\n                    int hash_size = pow(2, ceil(log2(max_c_hat_size)));\n\n                    switch(hash_size)\n                    {\n                        case 1:\n                        case 2:\n                        case 4:\n                        case 8:\n                        case 16:\n                        case 32:\n                            compute_c_hat_opt_dispatch<32, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 64:\n                            compute_c_hat_opt_dispatch<64, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 128:\n                            compute_c_hat_opt_dispatch<128, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 256:\n                            compute_c_hat_opt_dispatch<256, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        case 512:\n                            compute_c_hat_opt_dispatch<512, 32>(\n                                    A, s_con.raw(), C_hat_start.raw(),\n                                    C_hat_size_.raw(), C_hat_.raw(), C_hat_pos_.raw(), assigned.raw(),\n                                    assigned_set, i);\n                            break;\n                        default:\n                            FatalError(\"Size of C_hat in a single row too large, solve with original solvers.\", AMGX_ERR_INTERNAL);\n                    }\n                }\n\n                hipDeviceSynchronize();\n                cudaCheckError();\n\n#if 0\n                if (A.is_matrix_distributed())\n                {\n                    //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n                    C_hat_.dirtybit = 1;\n                    C_hat_matrix.manager->exchange_halo(C_hat_, C_hat_.tag);\n                    C_hat_size_.dirtybit = 1;\n                    A.manager->exchange_halo(C_hat_size_, C_hat_size_.tag);\n                }\n#endif\n            }\n            else\n            {\n            }\n\n            // In case the hash table is not big enough, run multiple attemps\n            int attempt = 0;\n\n            for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n            {\n                // Double the amount of GMEM (if needed).\n                if ( attempt > 0 )\n                {\n                    exp_wk.expand();\n                }\n\n                // Reset the status. TODO: Launch async copies.\n                int status = 0;\n                hipMemcpy( exp_wk.get_status(), &status, sizeof(int), hipMemcpyHostToDevice );\n                cudaCheckError();\n                // Compute the set C_hat.\n                int work_offset = GRID_SIZE * NUM_WARPS;\n                hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n                cudaCheckError();\n                // Run the computation.\n                multipass::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    assigned.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status(),\n                    i);\n                    cudaCheckError();\n\n                if (A.is_matrix_distributed())\n                {\n                    //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n                    C_hat.dirtybit = 1;\n                    C_hat_matrix.manager->exchange_halo(C_hat, C_hat.tag);\n                    C_hat_size.dirtybit = 1;\n                    A.manager->exchange_halo(C_hat_size, C_hat_size.tag);\n                }\n\n                cudaCheckError();\n                // Read the result from count_non_zeroes.\n                hipMemcpy( &status, exp_wk.get_status(), sizeof(int), hipMemcpyDeviceToHost );\n                cudaCheckError();\n                done = status == 0;\n            }\n        }\n    }\n    // At this stage, for each fine node (even in 1 ring halo), we have the global indices\n    // of the interpolatory coarse points\n    // count the number of non-zeros in the interpolation matrix,\n    // For coarse point, nonzeros = 1\n    // for strong fine, nonzeros = 0\n    // for other points, nonzeros[i] = C_hat_size[i]\n    IntVector nonZeroOffsets(size_one_ring + 1);\n    IntVector nonZerosPerRow(size_one_ring);\n    nonZerosPerRowSizeKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map_ptr, C_hat_size.raw(),\n            nonZerosPerRow.raw());\n    cudaCheckError();\n\n    // Do a one ring halo exchange to know the lengths of the neighbors row\n    if (A.is_matrix_distributed())\n    {\n        nonZerosPerRow.dirtybit = 1;\n        A.manager->exchange_halo(nonZerosPerRow, nonZerosPerRow.tag);\n    }\n\n    // get total number of non-zeros with reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[nonZeroOffsets.size() - 1] = nonZeros;\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);\n    // TODO: is the number of columns used anywhere?\n    P.resize(size_one_ring, coarsePoints, nonZeros, 1);\n    cudaCheckError();\n    P_col_indices_global.resize(P.col_indices.size());\n    Matrix<TConfig_d> P_temp(1, 1, 1, 0);\n\n    if (A.is_matrix_distributed())\n    {\n        prep->createTempManager(P_temp, A, nonZeroOffsets);\n    }\n\n    IndexType *Poffsets = P.row_offsets.raw();\n    IndexType *Pcolumn_indices = P.col_indices.raw();\n    ValueType *Pvalues = P.values.raw();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // grab the diagonal terms\n    VVector diag(size_one_ring);\n    ValueType *diag_ptr = diag.raw();\n    {\n        find_diag_kernel_indexed_dia <<< numBlocksOneRing, blockSize>>>(\n            size_one_ring,\n            A.diag.raw(),\n            A.values.raw(),\n            diag.raw());\n    }\n    cudaCheckError();\n\n    const int CTA_SIZE  = 256;\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    int work_offset = GRID_SIZE * NUM_WARPS;\n    typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n\n    // Fill P.col_indices_global, P.values\n    if(this->m_use_opt_kernels)\n    {\n        int grid_size = A.get_num_rows() / NUM_WARPS + 1;\n\n        multipass::compute_interp_weight_first_pass_kernel_opt\n            <Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t>\n            <<< grid_size, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map_global.raw(),\n            s_con.raw(),\n            diag.raw(),\n            P.row_offsets.raw(),\n            P_col_indices_global.raw(),\n            P.values.raw(),\n            assigned.raw());\n        cudaCheckError();\n    }\n    else\n    {\n        hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n        cudaCheckError();\n\n        // Compute the set C_hat.\n        multipass::compute_interp_weight_first_pass_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map_global.raw(),\n            s_con.raw(),\n            diag.raw(),\n            P.row_offsets.raw(),\n            P_col_indices_global.raw(),\n            P.values.raw(),\n            assigned.raw());\n        cudaCheckError();\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        //TODO: We could probably reduce the amount of data exchanged here, since we only need to exchange for recently updated nodes\n        P_col_indices_global.dirtybit = 1;\n        P_temp.manager->exchange_halo(P_col_indices_global, P_col_indices_global.tag);\n        P.values.dirtybit = 1;\n        P_temp.manager->exchange_halo(P.values, P.values.tag);\n    }\n\n    for (int i = 2; i < num_passes; i++)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            IntVector assigned_in_pass(assigned.size());\n            IntVector assigned_offs(assigned.size() + 1);\n\n            int nthreads = 128;\n            int nblocks = assigned.size() / nthreads + 1;\n            set_assigned_in_pass<<<nblocks, nthreads>>>(assigned.size(), i, assigned.raw(), assigned_in_pass.raw(), assigned_offs.raw());\n            cudaCheckError();\n            thrust_wrapper::inclusive_scan<AMGX_device>(assigned_in_pass.begin(), assigned_in_pass.end(), assigned_offs.begin()+1);\n\n            IntVector assigned_set(assigned_offs[assigned_offs.size()-1]);\n            assigned_set_fill<<<nblocks, nthreads>>>(assigned.size(), assigned_in_pass.raw(), assigned_offs.raw(), assigned_set.raw());\n            cudaCheckError();\n\n            Hash_Workspace<TConfig_d, int64_t> exp_wk2(true, 4096);\n            multipass::compute_interp_weight_kernel_opt<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< 4096, CTA_SIZE>>>(\n                    assigned_set.size(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.values.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    diag.raw(),\n                    P.row_offsets.raw(),\n                    P_col_indices_global.raw(),\n                    P.values.raw(),\n                    exp_wk2.get_gmem_size(),\n                    exp_wk2.get_keys(),\n                    exp_wk2.get_vals(),\n                    exp_wk2.get_work_queue(),\n                    assigned.raw(),\n                    assigned_set.raw(),\n                    i);\n                    cudaCheckError();\n        }\n        else\n        {\n            hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n            cudaCheckError();\n            multipass::compute_interp_weight_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE, int64_t> <<< GRID_SIZE, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    A.values.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    C_hat_start.raw(),\n                    C_hat_size.raw(),\n                    diag.raw(),\n                    P.row_offsets.raw(),\n                    P_col_indices_global.raw(),\n                    P.values.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_vals(),\n                    exp_wk.get_work_queue(),\n                    assigned.raw(),\n                    i);\n                    cudaCheckError();\n        }\n\n        if (A.is_matrix_distributed())\n        {\n            //TODO: This is exchanging too much data\n            P_col_indices_global.dirtybit = 1;\n            P_temp.manager->exchange_halo(P_col_indices_global, P_col_indices_global.tag);\n            P.values.dirtybit = 1;\n            P_temp.manager->exchange_halo(P.values, P.values.tag);\n        }\n\n        cudaCheckError();\n    }\n\n    if (!A.is_matrix_distributed())\n    {\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, ( (int) P.col_indices.size() + cta_size - 1) / cta_size);\n        // Simply cast from int64 to int\n        fill_P_col_indices <<< grid_size, cta_size>>>(P_col_indices_global.raw(), P.col_indices.raw(), (int) P.col_indices.size());\n        cudaCheckError();\n    }\n    else\n    {\n        // Resize P to only include the owned rows and columns\n        int P_num_nnz = P.row_offsets[P.get_num_rows()];\n        P.resize(A.get_num_rows(), P.manager->halo_offsets[0], P_num_nnz, 1);\n        // Using the global col indices, identify neighbors for P.manager, fill B2L_maps, etc...\n        prep->initialize_manager_from_global_col_indices(P, P_col_indices_global);\n        // Create the list of interior and boundary nodes (this is necessary if doing masked SpMV operations)\n        prep->createRowsLists(P, true);\n    }\n\n    // Delete the temporary distributed arranger\n    if (prep != NULL) { delete prep; }\n}\n\ntemplate< class T_Config>\nvoid Multipass_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimensions for multipass interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    //P.computeDiagonal();\n    P.set_initialized(1);\n#if 0\n    P.printToFile(\"P.out\", \"\", -1, -1);\n#endif\n}\n\n#define AMGX_CASE_LINE(CASE) template class Multipass_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Multipass_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/interpolators/multipass.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <thrust/transform.h>\n#include <thrust/transform_scan.h>\n#include <thrust_wrapper.h>\n#include <classical/interpolators/distance1.h>\n#include <classical/interpolators/common.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <assert.h>\n\n#include <thrust/count.h>\n\nnamespace amgx\n{\n\nnamespace detail\n{\n\ntemplate< class CsrMatrixType >\nstruct find_row_diagonal\n{\n//    typedef typename CsrMatrixType::values_array_type::value_type value_type;\n    typedef typename CsrMatrixType::value_type value_type;\n    const CsrMatrixType &my_A;\n    find_row_diagonal( const CsrMatrixType &A ) : my_A( A ) {}\n\n    // Operator to extract the diagonal value.\n    value_type operator()( int irow ) const\n    {\n        for ( int i = my_A.row_offsets[irow], end = my_A.row_offsets[irow + 1] ; i < end ; ++i )\n            if ( my_A.col_indices[i] == irow )\n            {\n                return my_A.values[i];\n            }\n\n        // assert( false );\n        return value_type( 0 );\n    }\n};\n\ntemplate< class CsrMatrixType >\nstruct count_nnz_per_row\n{\n    const CsrMatrixType &my_A;\n    static const AMGX_VecPrecision vecPrec = CsrMatrixType::TConfig::vecPrec;\n    static const AMGX_MatPrecision matPrec = CsrMatrixType::TConfig::matPrec;\n    static const AMGX_IndPrecision indPrec = CsrMatrixType::TConfig::indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecBool, matPrec, indPrec> > BVector;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecInt, matPrec, indPrec> > IntVector;\n\n    const  BVector &my_are_sc;\n    const  IntVector &my_cf_map;\n    count_nnz_per_row( const CsrMatrixType &A,\n                       const BVector &are_sc,\n                       const IntVector &cf_map ) :\n        my_A( A ),\n        my_are_sc( are_sc ),\n        my_cf_map( cf_map )\n    {}\n\n    // Operator to extract the diagonal value.\n    int operator()( int i ) const\n    {\n        if ( my_cf_map[i] >= 0 ) // If it's a coarse node, only one NZ element.\n        {\n            return 1;\n        }\n\n        // Count the number of strongly connected coarse nodes.\n        int count = 0;\n\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            int j = my_A.col_indices[itj];\n\n            if ( my_cf_map[j] >= 0 && my_are_sc[itj] )\n            {\n                count++;\n            }\n        }\n\n        return count;\n    }\n};\n\ntemplate< class CsrMatrixType >\nstruct compute_weights\n{\n    typedef typename CsrMatrixType::value_type value_type;\n    typedef typename CsrMatrixType::MVector VVector;\n    static const AMGX_VecPrecision vecPrec = CsrMatrixType::TConfig::vecPrec;\n    static const AMGX_MatPrecision matPrec = CsrMatrixType::TConfig::matPrec;\n    static const AMGX_IndPrecision indPrec = CsrMatrixType::TConfig::indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecBool, matPrec, indPrec> > BVector;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecInt, matPrec, indPrec> > IntVector;\n    const CsrMatrixType &my_A;\n    const VVector &my_diag;\n    const BVector &my_are_sc;\n    const IntVector &my_cf_map;\n    IntVector *my_edges_markers;\n    CsrMatrixType &my_P;\n\n    // Constructor.\n    compute_weights( const CsrMatrixType &A,\n                     const VVector &diag,\n                     const BVector &are_sc,\n                     const IntVector &cf_map,\n                     IntVector *edges_markers,\n                     CsrMatrixType &P ) :\n        my_A( A ),\n        my_diag( diag ),\n        my_are_sc( are_sc ),\n        my_cf_map( cf_map ),\n        my_edges_markers( edges_markers ),\n        my_P( P )\n    { }\n\n    // Get the thread index. Always 0 if OpenMP is off.\n    inline\n    int get_thread_id( ) const\n    {\n        return 0;\n    }\n\n    // Operator to find the weights.\n    void operator()( int i )\n    {\n        // Skip coarse nodes.\n        int nnz = my_P.row_offsets[i];\n        int id = my_cf_map[i];\n\n        if ( id >= 0 )\n        {\n            my_P.col_indices[nnz] = id;\n            my_P.values[nnz] = value_type( 1 );\n            return;\n        }\n\n        assert( my_A.row_offsets[i] < my_A.row_offsets[i + 1] );\n        const int strong_marker = -2;\n\n        // Initialize the F-C connections to 0 and mark F-F connections.\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            if ( !my_are_sc[itj] )\n            {\n                continue;\n            }\n\n            int j = my_A.col_indices[itj];\n\n            if ( my_cf_map[j] == FINE ) // It's a F-F connection, mark it.\n            {\n                assert( my_cf_map[j] < 0 );\n                my_edges_markers[get_thread_id( )][j] = strong_marker;\n                continue;\n            }\n\n            assert( nnz < my_P.row_offsets[i + 1] );\n            my_edges_markers[get_thread_id( )][j] = nnz; // It's a F-C connection, mark the coarse end point.\n            my_P.col_indices[nnz] = my_cf_map[j];\n            my_P.values[nnz] = value_type( 0 );\n            nnz++;\n        }\n\n        // Make sure we exhausted all the indices.\n        assert( nnz == my_P.row_offsets[i + 1] );\n        // Reset NNZ variable.\n        nnz = my_P.row_offsets[i];\n        // The diagonal element a_ii.\n        value_type diag = my_diag[i];\n\n        // Loop over the neighbours of the node n_i to add contributions.\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            int j = my_A.col_indices[itj];\n\n            if ( i == j ) // Skip the diagonal.\n            {\n                continue;\n            }\n\n            // 1st case: j is a coarse node and it strongly influences i, so we accumulate a_ij.\n            if ( my_edges_markers[get_thread_id( )][j] >= nnz )\n            {\n                int itk = my_edges_markers[get_thread_id( )][j];\n                my_P.values[itk] += my_A.values[itj];\n                continue;\n            }\n            // 2nd case: j is a fine node and strongly influences i. We distribute a_ij to the coarse\n            // nodes that strongly influence i (not the diagonal).\n            else if ( my_edges_markers[get_thread_id( )][j] == strong_marker )\n            {\n                value_type sum( 0 );\n                int diag_sign = std::signbit( my_diag[i] );\n\n                for ( int itk = my_A.row_offsets[j] ; itk < my_A.row_offsets[j + 1] ; ++itk )\n                {\n                    int k = my_A.col_indices[itk];\n\n                    if ( my_edges_markers[get_thread_id( )][k] >= nnz && std::signbit( my_A.values[itk] ) != diag_sign )\n                    {\n                        sum += my_A.values[itk];\n                    }\n                }\n\n                if ( sum == value_type( 0 ) )\n                {\n                    diag += my_A.values[itj];\n                    continue;\n                }\n\n                value_type d = my_A.values[itj] / sum;\n\n                for ( int itk = my_A.row_offsets[j] ; itk < my_A.row_offsets[j + 1] ; ++itk )\n                {\n                    int it = my_edges_markers[get_thread_id( )][my_A.col_indices[itk]];\n\n                    if ( it >= nnz && std::signbit( my_A.values[itk] ) != diag_sign )\n                    {\n                        my_P.values[it] += d * my_A.values[itk];\n                    }\n                }\n            }\n            else if ( my_cf_map[j] == FINE )\n            {\n                diag += my_A.values[itj];\n            }\n        }\n\n        if ( diag == value_type( 0 ) )\n        {\n            diag = my_diag[i];\n\n            if ( diag == value_type( 0 ) ) // REALLY ?\n            {\n                diag = value_type( 1 );\n            }\n        }\n\n        assert( diag != value_type( 0 ) );\n        diag = value_type( -1.0 ) / diag;\n\n        for ( ; nnz < my_P.row_offsets[i + 1] ; ++nnz )\n        {\n            my_P.values[nnz] *= diag;\n        }\n\n        // strong_marker--;\n    }\n};\n\n}\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(\n    Matrix_h &A,\n    IntVector &cf_map,\n    BVector &are_sc,\n    IntVector &wk,\n    Matrix_h &P)\n{\n    // The diagonal of A.\n    VVector diag( A.get_num_rows() );\n    thrust_wrapper::transform<AMGX_host>( amgx::thrust::make_counting_iterator<int>( 0 ),\n                       amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                       diag.begin( ),\n                       detail::find_row_diagonal<Matrix_h>( A ) );\n    cudaCheckError();\n    // Count the number of coarse points.\n    int n_coarse = 0;\n\n    for ( int i = 0 ; i < A.get_num_rows() ; ++i )\n        if ( cf_map[i] >= 0) { n_coarse++; }\n\n    // Count the number of non-zero elements per row of P.\n    IntVector nnz_per_row( A.get_num_rows() + 1 );\n    amgx::thrust::transform( amgx::thrust::make_counting_iterator<int>( 0 ),\n                       amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                       nnz_per_row.begin( ),\n                       detail::count_nnz_per_row<Matrix_h>( A, are_sc, cf_map ) );\n    cudaCheckError();\n    nnz_per_row[A.get_num_rows()] = 0;\n    // Find the total number of non-zeroes. \n    // TODO: merge with the exclusive scan.\n    int nnz = amgx::thrust::reduce( nnz_per_row.begin( ), nnz_per_row.end( ) );\n    cudaCheckError();\n    // Resize P so that there is enough space to store the non-zero elements.\n    P.addProps(CSR);\n    P.resize( A.get_num_rows(), n_coarse, nnz );\n    // Compute row offsets of P.\n    thrust_wrapper::exclusive_scan<AMGX_host>( nnz_per_row.begin( ),\n                            nnz_per_row.end( ),\n                            P.row_offsets.begin( ) );\n    cudaCheckError();\n    // For each row we compute the weights.\n    IntVector edges_markers_on_stack( A.get_num_rows(), -1 );\n    IntVector *edges_markers = &edges_markers_on_stack;\n    detail::compute_weights<Matrix_h> compute_fct( A, diag, are_sc, cf_map, edges_markers, P );\n\n    amgx::thrust::for_each( amgx::thrust::host,\n                      amgx::thrust::make_counting_iterator<int>( 0 ),\n                      amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                      compute_fct );\n    cudaCheckError();\n}\n\n/*************************************************************************\n* create the interpolation matrix P on GPU\n************************************************************************/\n\nenum SETS {SET_STRONG_COARSE = 1,\n           SET_WEAK_COARSE = 2,\n           SET_STRONG_FINE = 4,\n           SET_STRONG_FINE_NO_COMMON = 8,\n           SET_WEAK_FINE = 16,\n           SET_DIAGONAL = 32\n          };\n\n/*\n * grab the diagonal of the matrix\n */\n/*template <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n  const int tIdx = threadIdx.x + blockDim.x*blockIdx.x;\n\n  if (tIdx >= numRows) return;\n\n  const int offset = offsets[tIdx];\n  const int numj = offsets[tIdx+1]-offset;\n\n  for (int j=offset; j < offset+numj; j++)\n  {\n    int jcol = column_indices[j];\n    if (tIdx == jcol)\n    {\n      diagonal[tIdx] = values[j];\n    }\n  }\n}*/\n\n/*\n * Count the non-zeros in final output\n */\ntemplate <typename IndexType>\n__global__\nvoid numNonZerosVecKernel(const IndexType *offsets, const IndexType *column_indices,\n                          const IndexType numRows, int *cf_map, const bool *s_con,\n                          int *nonZerosVec)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int numOnRow = 0;\n\n        // count strong coarse influences\n        if (cf_map[tIdx] == FINE)\n        {\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // if weakly connected, ignore\n\n                int jcol = column_indices[j];\n\n                if (cf_map[jcol] >= 0) // if coarse\n                {\n                    numOnRow++;\n                }\n            }\n        }\n        else\n        {\n            numOnRow++;\n        }\n\n        nonZerosVec[tIdx] = numOnRow;\n    }\n}\n\n/*\n * Calculate D_i vector\n * - Weakly influencing and strong fine with no common C-points to be added to diagonal term\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateDKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal,\n                      int *set_fields, ValueType *D)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numk = offsets[tIdx + 1] - offset;\n        ValueType sum = 0;\n        int mask = SET_WEAK_FINE;\n\n        for (int k = offset; k < offset + numk; k++)\n        {\n            if (set_fields[k] & mask)\n            {\n                sum += values[k];\n            }\n        }\n\n        D[tIdx] += sum;\n    }\n}\n\n/*\n * Calculate the k dependent term in w_i,j\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateBKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal, ValueType *diag_inc,\n                      int *set_fields, int *cf_map, int *nonZerosVec, ValueType *B)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localNonZeros = 0;\n        const int nonZerosOffset = nonZerosVec[tIdx];\n        ValueType sum = 0.0, top = 0.0, bottom_sum = 0.0, a_ik;\n\n        if (cf_map[tIdx] >= 0)   // If I am a coarse row\n        {\n            B[nonZerosOffset] = 1;\n            return;\n        }\n\n        diag_inc[tIdx] = 0;\n        int flag = 0;\n        int first_j_loop = 0;\n        ValueType tol = 1e-10;\n\n        // otherwise\n        for (int j = offset; j < offset + numj; j++)\n        {\n            sum = 0.0;\n            bottom_sum = 0.0;\n\n            if (set_fields[j] & SET_STRONG_COARSE) // we actually care about this j\n            {\n                int jcol = column_indices[j]; // this is the only part of j I need / care about.\n\n                if (flag == 0)\n                {\n                    first_j_loop = 1;\n                    flag = 1;\n                }\n                else\n                {\n                    first_j_loop = 0;\n                }\n\n                // run through the columns of row i, summing over all k\n                for (int k = offset; k < offset + numj; k++)\n                {\n                    top = 0.0;\n                    bottom_sum = 0.0;\n\n                    if ( (set_fields[k] & SET_STRONG_FINE) || (set_fields[k] & SET_STRONG_FINE_NO_COMMON))   // k we're interested in\n                    {\n                        // part of the desired set - get column, diagonal and a_ik\n                        int kcol = column_indices[k];\n                        a_ik = values[k]; // get a_i,k term\n                        // now find a_k,j\n                        ValueType diag = diagonal[kcol];\n                        int sgn = diag < 0.0 ? -1 : 1;\n\n                        // now I need to find the j-th column on the k-th row. another loop (sigh)\n                        for (int n = offsets[kcol]; n < offsets[kcol + 1]; n++)\n                        {\n                            int ncol = column_indices[n];\n\n                            if (ncol == jcol)   // this is the entry I need\n                            {\n                                if (sgn * values[n] < 0)\n                                {\n                                    // add to the sum\n                                    top = a_ik * values[n]; //  discard diagonal\n                                }\n                            }\n                        }\n\n                        // calculate the bottom sum - loop through columns of i\n                        for (int m = offset; m < offset + numj; m++)\n                        {\n                            if (set_fields[m] & SET_STRONG_COARSE)\n                            {\n                                // this is a point I care about\n                                int mcol = column_indices[m];\n\n                                // now find the m-th column of the k-th row and add to sum\n                                for (int n = offsets[kcol]; n < offsets[kcol + 1]; n++)\n                                {\n                                    int ncol = column_indices[n];\n\n                                    if (ncol == mcol)\n                                    {\n                                        if (sgn * values[n] < 0)\n                                        {\n                                            bottom_sum += values[n];\n                                        }\n                                    }\n                                }\n                            }\n                        }\n\n                        if (abs(bottom_sum) < tol)\n                        {\n                            if (first_j_loop == 1)\n                            {\n                                //add it to the diagonal....\n                                diag_inc[tIdx] += values[k];\n                            }\n                        }\n                        else\n                        {\n                            sum += top / bottom_sum;\n                        }\n                    }\n                }\n\n                B[nonZerosOffset + localNonZeros] = sum;\n                localNonZeros++;\n            }\n        }\n    }\n}\n\n/*\n * count the number of strong coarse points per row\n * - was used for degugging, might be useful in the future\n */\ntemplate <typename IndexType>\n__global__\nvoid coarsePerRow(const IndexType *offsets, int num_rows, int *set_fields, int *output)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localCoarse = 0;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            if (set_fields[j] & SET_STRONG_COARSE)\n            {\n                localCoarse++;\n            }\n        }\n\n        output[tIdx] = localCoarse;\n    }\n}\n\n/*\n * Assemble final w_ij terms\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateWKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal,\n                      int *set_fields, int *nonZerosVec, ValueType *D, ValueType *B,\n                      ValueType *w, IndexType *Pcolumn_indices, int *cf_map)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localNonZeros = 0;\n        const int nonZerosOffset = nonZerosVec[tIdx];\n        ValueType diag = diagonal[tIdx];\n\n        if (cf_map[tIdx] >= 0)\n        {\n            w[nonZerosOffset] = 1.0;\n            Pcolumn_indices[nonZerosOffset] = cf_map[tIdx];\n            continue;\n        }\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            if (set_fields[j] & SET_STRONG_COARSE)\n            {\n                ValueType aij = values[j];\n                ValueType Bij = B[nonZerosOffset + localNonZeros];\n                ValueType Di  = D[tIdx];\n                Pcolumn_indices[nonZerosOffset + localNonZeros] = cf_map[column_indices[j]];\n                double bottom_sum = (fabs(diag + Di) < 1e-10) ? 1. : diag + Di;\n                w[nonZerosOffset + localNonZeros] = -1.0 / bottom_sum * (aij + Bij);\n                localNonZeros++;\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid markCoarseEdgesKernel(const IndexType *offsets, const IndexType *column_indices,\n                           const size_t numRows, const int *cf_map, const bool *s_con,\n                           bool *coarseMark)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int rowBegin = offsets[tIdx];\n        const int rowEnd = offsets[tIdx + 1];\n\n        // loop over columns\n        for (int j = rowBegin; j < rowEnd; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol) { continue; } // skip the diagonal\n\n            if (cf_map[jcol] >= 0)\n            {\n                // mark this point\n                coarseMark[j] = true;\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__device__\nbool sparseIntersectDevice(const IndexType *column_indices, ValueType *values,\n                           int begin1, int end1, int begin2, int end2)\n{\n    int i1 = begin1;\n    int i2 = begin2;\n\n    // check arrays non zero length\n    if (begin1 >= end1 || begin2 >= end2) { return false; }\n\n    bool r = false;\n\n    while ( true )\n    {\n        int col1 = column_indices[i1];\n        int col2 = column_indices[i2];\n\n        if (col1 == col2) // this is a match\n        {\n            if (values[i1] && values[i2])\n            {\n                r = true;\n                break;\n            }\n\n            i1++;\n            i2++;\n\n            if ( i1 >= end1 || i2 >= end2 ) { break; }\n        }\n        else if (col1 > col2)\n        {\n            i2++;\n\n            if (i2 >= end2) { break; }\n        }\n        else\n        {\n            i1++;\n\n            if (i1 >= end1) { break; }\n        }\n    }\n\n    return r;\n}\n\ntemplate <typename IndexType>\n__global__\nvoid categoriseEdgesKernel(const IndexType *offsets, const IndexType *column_indices,\n                           const size_t numRows, const int *cf_map, const bool *s_con,\n                           bool *coarse_mark, int *set_fields)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // set_fields[tIdx] = 1;\n        if (cf_map[tIdx] >= 0) { continue; } // we don't need to categorise C-? edges\n\n        const int rowBegin = offsets[tIdx];\n        const int rowEnd = offsets[tIdx + 1];\n\n        // we know I'm a fine row now - loop over non-zeros\n        for (int j = rowBegin; j < rowEnd; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (jcol == tIdx) { continue; }\n\n            // check F-C edges\n            if (cf_map[jcol] >= 0)\n            {\n                if (s_con[j])\n                {\n                    set_fields[j] |= SET_STRONG_COARSE;\n                }\n                else\n                {\n                    set_fields[j] |= SET_WEAK_COARSE;\n                }\n            }\n            else // F-F edges\n            {\n                if (!s_con[j])\n                {\n                    set_fields[j] |= SET_WEAK_FINE;\n                }\n                else // check if there exists a common C-point\n                {\n                    // intersect i and jcol\n                    bool common_C = true;\n                    common_C = sparseIntersectDevice(column_indices, coarse_mark, rowBegin, rowEnd,\n                                                     offsets[jcol], offsets[jcol + 1]);\n\n                    if (common_C)\n                    {\n                        set_fields[j] |= SET_STRONG_FINE;\n                    }\n                    else\n                    {\n                        set_fields[j] |= SET_STRONG_FINE_NO_COMMON;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setSetsGPU(const Matrix_d &A, const BVector &s_con,\n        const IntVector &cf_map,\n        IntVector &set_fields)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    const int blocksize = 64;\n    const int numBlocks = std::min( AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1) );\n    // raw pointers for matrix structure\n    const IndexType *offsets = A.row_offsets.raw();\n    const IndexType *column_indices = A.col_indices.raw();\n    // raw pointers for coarse/fine map and strength of connection array\n    const bool *s_con_ptr = s_con.raw();\n    const int *cf_map_ptr = cf_map.raw();\n    int *set_fields_ptr = set_fields.raw();\n    // memory for CoarseMark array\n    BVector coarseMark(A.get_num_nz(), false);\n    bool *coarseMark_ptr = coarseMark.raw();\n    markCoarseEdgesKernel <<< numBlocks, blocksize>>>(offsets, column_indices, A.get_num_rows(),\n            cf_map_ptr, s_con_ptr, coarseMark_ptr);\n    cudaCheckError();\n    // now we have the strong coarse edges marked, categorise all edges\n    categoriseEdgesKernel <<< numBlocks, blocksize>>>(offsets, column_indices, A.get_num_rows(),\n            cf_map_ptr, s_con_ptr, coarseMark_ptr,\n            set_fields_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *Aoffsets_ptr = A.row_offsets.raw();\n    const IndexType *Acolumn_indices_ptr = A.col_indices.raw();\n    const ValueType *Avalues_ptr = A.values.raw();\n    // Coarse/Fine map, strength of connection, scratch\n    int *cf_map_ptr = cf_map.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *scratch_ptr = scratch.raw();\n    // temporary vectors - diagonal and non-zero offsets\n    VVector diag(A.get_num_rows(), 0.0);\n    IntVector nonZerosVec(A.get_num_rows() + 1, 0);\n    ValueType *diag_ptr = diag.raw();\n    int *nonZerosVec_ptr = nonZerosVec.raw();\n    // NB - will get pointers for P *after* it has been initialised\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1));\n    const IndexType numRows = (IndexType) A.get_num_rows();\n    // extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(A.get_num_rows(),\n            A.diag.raw(),\n            A.values.raw(),\n            diag_ptr);\n            cudaCheckError();\n    /*  getDiagonalKernel<<<numBlocks,blocksize>>>(Aoffsets_ptr,Acolumn_indices_ptr,Avalues_ptr,\n                                                 numRows,diag_ptr);*/\n    cudaCheckError();\n    // count the number of non-zeros in final output per row\n    numNonZerosVecKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, numRows,\n            cf_map_ptr, s_con_ptr, nonZerosVec_ptr);\n    cudaCheckError();\n    // now I have the non-zeros per row for matrix P, count the non-zeros on each row\n    // to get total NNZ\n    // sum non-zeros per row\n    int NNZidx = thrust_wrapper::reduce<AMGX_device>(nonZerosVec.begin(), nonZerosVec.end());\n    cudaCheckError();\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosVec.begin(), nonZerosVec.end(), nonZerosVec.begin());\n    cudaCheckError();\n    nonZerosVec[A.get_num_rows()] = NNZidx;\n    // generate sets on the device\n    IntVector set_fields(A.get_num_nz(), 0);\n    setSetsGPU(A, s_con, cf_map, set_fields);\n    int *set_field_ptr = set_fields.raw();\n    // count the coarse points first\n    // (no need to worry about >4B coarse points)\n    int coarsePoints = (int) amgx::thrust::count_if(cf_map.begin(), cf_map.end(), is_non_neg());\n    cudaCheckError();\n    // I have all the information needed - create P and get the pointers to its data\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, NNZidx, 1);\n    IndexType *Pcolumn_indices_ptr = P.col_indices.raw();\n    ValueType *Pvalues_ptr = P.values.raw();\n    // now P is initialised, get row offsets from nonZerosVec with an exclusive prefix sum\n    // set final value\n    amgx::thrust::copy(nonZerosVec.begin(), nonZerosVec.end(), P.row_offsets.begin());\n    cudaCheckError();\n    IndexType *Poffsets_ptr = P.row_offsets.raw();\n    // assign temp memory\n    VVector B(NNZidx, 0.0);\n    VVector D(A.get_num_rows(), 0.0);\n    VVector W(NNZidx, 0.0);\n    // get the raw pointers for temp memory\n    ValueType *D_ptr = D.raw();\n    ValueType *B_ptr = B.raw();\n    ValueType *W_ptr = W.raw();\n    // call each kernel in turn\n    calculateBKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, D_ptr, set_field_ptr, cf_map_ptr,\n            nonZerosVec_ptr, B_ptr);\n    cudaCheckError();\n    calculateDKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, set_field_ptr, D_ptr);\n    cudaCheckError();\n    calculateWKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, set_field_ptr, nonZerosVec_ptr,\n            D_ptr, B_ptr, Pvalues_ptr, Pcolumn_indices_ptr,\n            cf_map_ptr);\n    cudaCheckError();\n}\n\ntemplate< class T_Config>\nvoid Distance1_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for distance1 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    P.computeDiagonal();\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class Distance1_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Distance1_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/transform.h>\n#include <thrust/transform_scan.h>\n#include <thrust_wrapper.h>\n#include <classical/interpolators/distance1.h>\n#include <classical/interpolators/common.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <assert.h>\n\n#include <thrust/count.h>\n\nnamespace amgx\n{\n\nnamespace detail\n{\n\ntemplate< class CsrMatrixType >\nstruct find_row_diagonal\n{\n//    typedef typename CsrMatrixType::values_array_type::value_type value_type;\n    typedef typename CsrMatrixType::value_type value_type;\n    const CsrMatrixType &my_A;\n    find_row_diagonal( const CsrMatrixType &A ) : my_A( A ) {}\n\n    // Operator to extract the diagonal value.\n    value_type operator()( int irow ) const\n    {\n        for ( int i = my_A.row_offsets[irow], end = my_A.row_offsets[irow + 1] ; i < end ; ++i )\n            if ( my_A.col_indices[i] == irow )\n            {\n                return my_A.values[i];\n            }\n\n        // assert( false );\n        return value_type( 0 );\n    }\n};\n\ntemplate< class CsrMatrixType >\nstruct count_nnz_per_row\n{\n    const CsrMatrixType &my_A;\n    static const AMGX_VecPrecision vecPrec = CsrMatrixType::TConfig::vecPrec;\n    static const AMGX_MatPrecision matPrec = CsrMatrixType::TConfig::matPrec;\n    static const AMGX_IndPrecision indPrec = CsrMatrixType::TConfig::indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecBool, matPrec, indPrec> > BVector;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecInt, matPrec, indPrec> > IntVector;\n\n    const  BVector &my_are_sc;\n    const  IntVector &my_cf_map;\n    count_nnz_per_row( const CsrMatrixType &A,\n                       const BVector &are_sc,\n                       const IntVector &cf_map ) :\n        my_A( A ),\n        my_are_sc( are_sc ),\n        my_cf_map( cf_map )\n    {}\n\n    // Operator to extract the diagonal value.\n    int operator()( int i ) const\n    {\n        if ( my_cf_map[i] >= 0 ) // If it's a coarse node, only one NZ element.\n        {\n            return 1;\n        }\n\n        // Count the number of strongly connected coarse nodes.\n        int count = 0;\n\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            int j = my_A.col_indices[itj];\n\n            if ( my_cf_map[j] >= 0 && my_are_sc[itj] )\n            {\n                count++;\n            }\n        }\n\n        return count;\n    }\n};\n\ntemplate< class CsrMatrixType >\nstruct compute_weights\n{\n    typedef typename CsrMatrixType::value_type value_type;\n    typedef typename CsrMatrixType::MVector VVector;\n    static const AMGX_VecPrecision vecPrec = CsrMatrixType::TConfig::vecPrec;\n    static const AMGX_MatPrecision matPrec = CsrMatrixType::TConfig::matPrec;\n    static const AMGX_IndPrecision indPrec = CsrMatrixType::TConfig::indPrec;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecBool, matPrec, indPrec> > BVector;\n    typedef Vector<TemplateConfig<AMGX_host, AMGX_vecInt, matPrec, indPrec> > IntVector;\n    const CsrMatrixType &my_A;\n    const VVector &my_diag;\n    const BVector &my_are_sc;\n    const IntVector &my_cf_map;\n    IntVector *my_edges_markers;\n    CsrMatrixType &my_P;\n\n    // Constructor.\n    compute_weights( const CsrMatrixType &A,\n                     const VVector &diag,\n                     const BVector &are_sc,\n                     const IntVector &cf_map,\n                     IntVector *edges_markers,\n                     CsrMatrixType &P ) :\n        my_A( A ),\n        my_diag( diag ),\n        my_are_sc( are_sc ),\n        my_cf_map( cf_map ),\n        my_edges_markers( edges_markers ),\n        my_P( P )\n    { }\n\n    // Get the thread index. Always 0 if OpenMP is off.\n    inline\n    int get_thread_id( ) const\n    {\n        return 0;\n    }\n\n    // Operator to find the weights.\n    void operator()( int i )\n    {\n        // Skip coarse nodes.\n        int nnz = my_P.row_offsets[i];\n        int id = my_cf_map[i];\n\n        if ( id >= 0 )\n        {\n            my_P.col_indices[nnz] = id;\n            my_P.values[nnz] = value_type( 1 );\n            return;\n        }\n\n        assert( my_A.row_offsets[i] < my_A.row_offsets[i + 1] );\n        const int strong_marker = -2;\n\n        // Initialize the F-C connections to 0 and mark F-F connections.\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            if ( !my_are_sc[itj] )\n            {\n                continue;\n            }\n\n            int j = my_A.col_indices[itj];\n\n            if ( my_cf_map[j] == FINE ) // It's a F-F connection, mark it.\n            {\n                assert( my_cf_map[j] < 0 );\n                my_edges_markers[get_thread_id( )][j] = strong_marker;\n                continue;\n            }\n\n            assert( nnz < my_P.row_offsets[i + 1] );\n            my_edges_markers[get_thread_id( )][j] = nnz; // It's a F-C connection, mark the coarse end point.\n            my_P.col_indices[nnz] = my_cf_map[j];\n            my_P.values[nnz] = value_type( 0 );\n            nnz++;\n        }\n\n        // Make sure we exhausted all the indices.\n        assert( nnz == my_P.row_offsets[i + 1] );\n        // Reset NNZ variable.\n        nnz = my_P.row_offsets[i];\n        // The diagonal element a_ii.\n        value_type diag = my_diag[i];\n\n        // Loop over the neighbours of the node n_i to add contributions.\n        for ( int itj = my_A.row_offsets[i] ; itj < my_A.row_offsets[i + 1] ; ++itj )\n        {\n            int j = my_A.col_indices[itj];\n\n            if ( i == j ) // Skip the diagonal.\n            {\n                continue;\n            }\n\n            // 1st case: j is a coarse node and it strongly influences i, so we accumulate a_ij.\n            if ( my_edges_markers[get_thread_id( )][j] >= nnz )\n            {\n                int itk = my_edges_markers[get_thread_id( )][j];\n                my_P.values[itk] += my_A.values[itj];\n                continue;\n            }\n            // 2nd case: j is a fine node and strongly influences i. We distribute a_ij to the coarse\n            // nodes that strongly influence i (not the diagonal).\n            else if ( my_edges_markers[get_thread_id( )][j] == strong_marker )\n            {\n                value_type sum( 0 );\n                int diag_sign = std::signbit( my_diag[i] );\n\n                for ( int itk = my_A.row_offsets[j] ; itk < my_A.row_offsets[j + 1] ; ++itk )\n                {\n                    int k = my_A.col_indices[itk];\n\n                    if ( my_edges_markers[get_thread_id( )][k] >= nnz && std::signbit( my_A.values[itk] ) != diag_sign )\n                    {\n                        sum += my_A.values[itk];\n                    }\n                }\n\n                if ( sum == value_type( 0 ) )\n                {\n                    diag += my_A.values[itj];\n                    continue;\n                }\n\n                value_type d = my_A.values[itj] / sum;\n\n                for ( int itk = my_A.row_offsets[j] ; itk < my_A.row_offsets[j + 1] ; ++itk )\n                {\n                    int it = my_edges_markers[get_thread_id( )][my_A.col_indices[itk]];\n\n                    if ( it >= nnz && std::signbit( my_A.values[itk] ) != diag_sign )\n                    {\n                        my_P.values[it] += d * my_A.values[itk];\n                    }\n                }\n            }\n            else if ( my_cf_map[j] == FINE )\n            {\n                diag += my_A.values[itj];\n            }\n        }\n\n        if ( diag == value_type( 0 ) )\n        {\n            diag = my_diag[i];\n\n            if ( diag == value_type( 0 ) ) // REALLY ?\n            {\n                diag = value_type( 1 );\n            }\n        }\n\n        assert( diag != value_type( 0 ) );\n        diag = value_type( -1.0 ) / diag;\n\n        for ( ; nnz < my_P.row_offsets[i + 1] ; ++nnz )\n        {\n            my_P.values[nnz] *= diag;\n        }\n\n        // strong_marker--;\n    }\n};\n\n}\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(\n    Matrix_h &A,\n    IntVector &cf_map,\n    BVector &are_sc,\n    IntVector &wk,\n    Matrix_h &P)\n{\n    // The diagonal of A.\n    VVector diag( A.get_num_rows() );\n    thrust_wrapper::transform<AMGX_host>( amgx::thrust::make_counting_iterator<int>( 0 ),\n                       amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                       diag.begin( ),\n                       detail::find_row_diagonal<Matrix_h>( A ) );\n    cudaCheckError();\n    // Count the number of coarse points.\n    int n_coarse = 0;\n\n    for ( int i = 0 ; i < A.get_num_rows() ; ++i )\n        if ( cf_map[i] >= 0) { n_coarse++; }\n\n    // Count the number of non-zero elements per row of P.\n    IntVector nnz_per_row( A.get_num_rows() + 1 );\n    amgx::thrust::transform( amgx::thrust::make_counting_iterator<int>( 0 ),\n                       amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                       nnz_per_row.begin( ),\n                       detail::count_nnz_per_row<Matrix_h>( A, are_sc, cf_map ) );\n    cudaCheckError();\n    nnz_per_row[A.get_num_rows()] = 0;\n    // Find the total number of non-zeroes. \n    // TODO: merge with the exclusive scan.\n    int nnz = amgx::thrust::reduce( nnz_per_row.begin( ), nnz_per_row.end( ) );\n    cudaCheckError();\n    // Resize P so that there is enough space to store the non-zero elements.\n    P.addProps(CSR);\n    P.resize( A.get_num_rows(), n_coarse, nnz );\n    // Compute row offsets of P.\n    thrust_wrapper::exclusive_scan<AMGX_host>( nnz_per_row.begin( ),\n                            nnz_per_row.end( ),\n                            P.row_offsets.begin( ) );\n    cudaCheckError();\n    // For each row we compute the weights.\n    IntVector edges_markers_on_stack( A.get_num_rows(), -1 );\n    IntVector *edges_markers = &edges_markers_on_stack;\n    detail::compute_weights<Matrix_h> compute_fct( A, diag, are_sc, cf_map, edges_markers, P );\n\n    amgx::thrust::for_each( amgx::thrust::host,\n                      amgx::thrust::make_counting_iterator<int>( 0 ),\n                      amgx::thrust::make_counting_iterator<int>( A.get_num_rows() ),\n                      compute_fct );\n    cudaCheckError();\n}\n\n/*************************************************************************\n* create the interpolation matrix P on GPU\n************************************************************************/\n\nenum SETS {SET_STRONG_COARSE = 1,\n           SET_WEAK_COARSE = 2,\n           SET_STRONG_FINE = 4,\n           SET_STRONG_FINE_NO_COMMON = 8,\n           SET_WEAK_FINE = 16,\n           SET_DIAGONAL = 32\n          };\n\n/*\n * grab the diagonal of the matrix\n */\n/*template <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n  const int tIdx = threadIdx.x + blockDim.x*blockIdx.x;\n\n  if (tIdx >= numRows) return;\n\n  const int offset = offsets[tIdx];\n  const int numj = offsets[tIdx+1]-offset;\n\n  for (int j=offset; j < offset+numj; j++)\n  {\n    int jcol = column_indices[j];\n    if (tIdx == jcol)\n    {\n      diagonal[tIdx] = values[j];\n    }\n  }\n}*/\n\n/*\n * Count the non-zeros in final output\n */\ntemplate <typename IndexType>\n__global__\nvoid numNonZerosVecKernel(const IndexType *offsets, const IndexType *column_indices,\n                          const IndexType numRows, int *cf_map, const bool *s_con,\n                          int *nonZerosVec)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int numOnRow = 0;\n\n        // count strong coarse influences\n        if (cf_map[tIdx] == FINE)\n        {\n            for (int j = offset; j < offset + numj; j++)\n            {\n                if (!s_con[j]) { continue; } // if weakly connected, ignore\n\n                int jcol = column_indices[j];\n\n                if (cf_map[jcol] >= 0) // if coarse\n                {\n                    numOnRow++;\n                }\n            }\n        }\n        else\n        {\n            numOnRow++;\n        }\n\n        nonZerosVec[tIdx] = numOnRow;\n    }\n}\n\n/*\n * Calculate D_i vector\n * - Weakly influencing and strong fine with no common C-points to be added to diagonal term\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateDKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal,\n                      int *set_fields, ValueType *D)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numk = offsets[tIdx + 1] - offset;\n        ValueType sum = 0;\n        int mask = SET_WEAK_FINE;\n\n        for (int k = offset; k < offset + numk; k++)\n        {\n            if (set_fields[k] & mask)\n            {\n                sum += values[k];\n            }\n        }\n\n        D[tIdx] += sum;\n    }\n}\n\n/*\n * Calculate the k dependent term in w_i,j\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateBKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal, ValueType *diag_inc,\n                      int *set_fields, int *cf_map, int *nonZerosVec, ValueType *B)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localNonZeros = 0;\n        const int nonZerosOffset = nonZerosVec[tIdx];\n        ValueType sum = 0.0, top = 0.0, bottom_sum = 0.0, a_ik;\n\n        if (cf_map[tIdx] >= 0)   // If I am a coarse row\n        {\n            B[nonZerosOffset] = 1;\n            return;\n        }\n\n        diag_inc[tIdx] = 0;\n        int flag = 0;\n        int first_j_loop = 0;\n        ValueType tol = 1e-10;\n\n        // otherwise\n        for (int j = offset; j < offset + numj; j++)\n        {\n            sum = 0.0;\n            bottom_sum = 0.0;\n\n            if (set_fields[j] & SET_STRONG_COARSE) // we actually care about this j\n            {\n                int jcol = column_indices[j]; // this is the only part of j I need / care about.\n\n                if (flag == 0)\n                {\n                    first_j_loop = 1;\n                    flag = 1;\n                }\n                else\n                {\n                    first_j_loop = 0;\n                }\n\n                // run through the columns of row i, summing over all k\n                for (int k = offset; k < offset + numj; k++)\n                {\n                    top = 0.0;\n                    bottom_sum = 0.0;\n\n                    if ( (set_fields[k] & SET_STRONG_FINE) || (set_fields[k] & SET_STRONG_FINE_NO_COMMON))   // k we're interested in\n                    {\n                        // part of the desired set - get column, diagonal and a_ik\n                        int kcol = column_indices[k];\n                        a_ik = values[k]; // get a_i,k term\n                        // now find a_k,j\n                        ValueType diag = diagonal[kcol];\n                        int sgn = diag < 0.0 ? -1 : 1;\n\n                        // now I need to find the j-th column on the k-th row. another loop (sigh)\n                        for (int n = offsets[kcol]; n < offsets[kcol + 1]; n++)\n                        {\n                            int ncol = column_indices[n];\n\n                            if (ncol == jcol)   // this is the entry I need\n                            {\n                                if (sgn * values[n] < 0)\n                                {\n                                    // add to the sum\n                                    top = a_ik * values[n]; //  discard diagonal\n                                }\n                            }\n                        }\n\n                        // calculate the bottom sum - loop through columns of i\n                        for (int m = offset; m < offset + numj; m++)\n                        {\n                            if (set_fields[m] & SET_STRONG_COARSE)\n                            {\n                                // this is a point I care about\n                                int mcol = column_indices[m];\n\n                                // now find the m-th column of the k-th row and add to sum\n                                for (int n = offsets[kcol]; n < offsets[kcol + 1]; n++)\n                                {\n                                    int ncol = column_indices[n];\n\n                                    if (ncol == mcol)\n                                    {\n                                        if (sgn * values[n] < 0)\n                                        {\n                                            bottom_sum += values[n];\n                                        }\n                                    }\n                                }\n                            }\n                        }\n\n                        if (abs(bottom_sum) < tol)\n                        {\n                            if (first_j_loop == 1)\n                            {\n                                //add it to the diagonal....\n                                diag_inc[tIdx] += values[k];\n                            }\n                        }\n                        else\n                        {\n                            sum += top / bottom_sum;\n                        }\n                    }\n                }\n\n                B[nonZerosOffset + localNonZeros] = sum;\n                localNonZeros++;\n            }\n        }\n    }\n}\n\n/*\n * count the number of strong coarse points per row\n * - was used for degugging, might be useful in the future\n */\ntemplate <typename IndexType>\n__global__\nvoid coarsePerRow(const IndexType *offsets, int num_rows, int *set_fields, int *output)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localCoarse = 0;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            if (set_fields[j] & SET_STRONG_COARSE)\n            {\n                localCoarse++;\n            }\n        }\n\n        output[tIdx] = localCoarse;\n    }\n}\n\n/*\n * Assemble final w_ij terms\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid calculateWKernel(const IndexType *offsets, const IndexType *column_indices,\n                      const ValueType *values, int num_rows, ValueType *diagonal,\n                      int *set_fields, int *nonZerosVec, ValueType *D, ValueType *B,\n                      ValueType *w, IndexType *Pcolumn_indices, int *cf_map)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < num_rows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n        int localNonZeros = 0;\n        const int nonZerosOffset = nonZerosVec[tIdx];\n        ValueType diag = diagonal[tIdx];\n\n        if (cf_map[tIdx] >= 0)\n        {\n            w[nonZerosOffset] = 1.0;\n            Pcolumn_indices[nonZerosOffset] = cf_map[tIdx];\n            continue;\n        }\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            if (set_fields[j] & SET_STRONG_COARSE)\n            {\n                ValueType aij = values[j];\n                ValueType Bij = B[nonZerosOffset + localNonZeros];\n                ValueType Di  = D[tIdx];\n                Pcolumn_indices[nonZerosOffset + localNonZeros] = cf_map[column_indices[j]];\n                double bottom_sum = (fabs(diag + Di) < 1e-10) ? 1. : diag + Di;\n                w[nonZerosOffset + localNonZeros] = -1.0 / bottom_sum * (aij + Bij);\n                localNonZeros++;\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType>\n__global__\nvoid markCoarseEdgesKernel(const IndexType *offsets, const IndexType *column_indices,\n                           const size_t numRows, const int *cf_map, const bool *s_con,\n                           bool *coarseMark)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int rowBegin = offsets[tIdx];\n        const int rowEnd = offsets[tIdx + 1];\n\n        // loop over columns\n        for (int j = rowBegin; j < rowEnd; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol) { continue; } // skip the diagonal\n\n            if (cf_map[jcol] >= 0)\n            {\n                // mark this point\n                coarseMark[j] = true;\n            }\n        }\n    }\n}\n\ntemplate <typename IndexType, typename ValueType>\n__device__\nbool sparseIntersectDevice(const IndexType *column_indices, ValueType *values,\n                           int begin1, int end1, int begin2, int end2)\n{\n    int i1 = begin1;\n    int i2 = begin2;\n\n    // check arrays non zero length\n    if (begin1 >= end1 || begin2 >= end2) { return false; }\n\n    bool r = false;\n\n    while ( true )\n    {\n        int col1 = column_indices[i1];\n        int col2 = column_indices[i2];\n\n        if (col1 == col2) // this is a match\n        {\n            if (values[i1] && values[i2])\n            {\n                r = true;\n                break;\n            }\n\n            i1++;\n            i2++;\n\n            if ( i1 >= end1 || i2 >= end2 ) { break; }\n        }\n        else if (col1 > col2)\n        {\n            i2++;\n\n            if (i2 >= end2) { break; }\n        }\n        else\n        {\n            i1++;\n\n            if (i1 >= end1) { break; }\n        }\n    }\n\n    return r;\n}\n\ntemplate <typename IndexType>\n__global__\nvoid categoriseEdgesKernel(const IndexType *offsets, const IndexType *column_indices,\n                           const size_t numRows, const int *cf_map, const bool *s_con,\n                           bool *coarse_mark, int *set_fields)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        // set_fields[tIdx] = 1;\n        if (cf_map[tIdx] >= 0) { continue; } // we don't need to categorise C-? edges\n\n        const int rowBegin = offsets[tIdx];\n        const int rowEnd = offsets[tIdx + 1];\n\n        // we know I'm a fine row now - loop over non-zeros\n        for (int j = rowBegin; j < rowEnd; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (jcol == tIdx) { continue; }\n\n            // check F-C edges\n            if (cf_map[jcol] >= 0)\n            {\n                if (s_con[j])\n                {\n                    set_fields[j] |= SET_STRONG_COARSE;\n                }\n                else\n                {\n                    set_fields[j] |= SET_WEAK_COARSE;\n                }\n            }\n            else // F-F edges\n            {\n                if (!s_con[j])\n                {\n                    set_fields[j] |= SET_WEAK_FINE;\n                }\n                else // check if there exists a common C-point\n                {\n                    // intersect i and jcol\n                    bool common_C = true;\n                    common_C = sparseIntersectDevice(column_indices, coarse_mark, rowBegin, rowEnd,\n                                                     offsets[jcol], offsets[jcol + 1]);\n\n                    if (common_C)\n                    {\n                        set_fields[j] |= SET_STRONG_FINE;\n                    }\n                    else\n                    {\n                        set_fields[j] |= SET_STRONG_FINE_NO_COMMON;\n                    }\n                }\n            }\n        }\n    }\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::setSetsGPU(const Matrix_d &A, const BVector &s_con,\n        const IntVector &cf_map,\n        IntVector &set_fields)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    const int blocksize = 64;\n    const int numBlocks = std::min( AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1) );\n    // raw pointers for matrix structure\n    const IndexType *offsets = A.row_offsets.raw();\n    const IndexType *column_indices = A.col_indices.raw();\n    // raw pointers for coarse/fine map and strength of connection array\n    const bool *s_con_ptr = s_con.raw();\n    const int *cf_map_ptr = cf_map.raw();\n    int *set_fields_ptr = set_fields.raw();\n    // memory for CoarseMark array\n    BVector coarseMark(A.get_num_nz(), false);\n    bool *coarseMark_ptr = coarseMark.raw();\n    markCoarseEdgesKernel <<< numBlocks, blocksize>>>(offsets, column_indices, A.get_num_rows(),\n            cf_map_ptr, s_con_ptr, coarseMark_ptr);\n    cudaCheckError();\n    // now we have the strong coarse edges marked, categorise all edges\n    categoriseEdgesKernel <<< numBlocks, blocksize>>>(offsets, column_indices, A.get_num_rows(),\n            cf_map_ptr, s_con_ptr, coarseMark_ptr,\n            set_fields_ptr);\n    cudaCheckError();\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance1_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // Assemble all the raw pointers needed\n    // Matrix A\n    const IndexType *Aoffsets_ptr = A.row_offsets.raw();\n    const IndexType *Acolumn_indices_ptr = A.col_indices.raw();\n    const ValueType *Avalues_ptr = A.values.raw();\n    // Coarse/Fine map, strength of connection, scratch\n    int *cf_map_ptr = cf_map.raw();\n    const bool *s_con_ptr = s_con.raw();\n    int *scratch_ptr = scratch.raw();\n    // temporary vectors - diagonal and non-zero offsets\n    VVector diag(A.get_num_rows(), 0.0);\n    IntVector nonZerosVec(A.get_num_rows() + 1, 0);\n    ValueType *diag_ptr = diag.raw();\n    int *nonZerosVec_ptr = nonZerosVec.raw();\n    // NB - will get pointers for P *after* it has been initialised\n    // choose blocksize. Using 1 thread / row for now\n    const int blocksize = 64;\n    const int numBlocks = std::min (AMGX_GRID_MAX_SIZE, (int) (A.get_num_rows() / blocksize + 1));\n    const IndexType numRows = (IndexType) A.get_num_rows();\n    // extract the diagonal of A\n    find_diag_kernel_indexed_dia <<< numBlocks, blocksize>>>(A.get_num_rows(),\n            A.diag.raw(),\n            A.values.raw(),\n            diag_ptr);\n            cudaCheckError();\n    /*  getDiagonalKernel<<<numBlocks,blocksize>>>(Aoffsets_ptr,Acolumn_indices_ptr,Avalues_ptr,\n                                                 numRows,diag_ptr);*/\n    cudaCheckError();\n    // count the number of non-zeros in final output per row\n    numNonZerosVecKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, numRows,\n            cf_map_ptr, s_con_ptr, nonZerosVec_ptr);\n    cudaCheckError();\n    // now I have the non-zeros per row for matrix P, count the non-zeros on each row\n    // to get total NNZ\n    // sum non-zeros per row\n    int NNZidx = thrust_wrapper::reduce<AMGX_device>(nonZerosVec.begin(), nonZerosVec.end());\n    cudaCheckError();\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosVec.begin(), nonZerosVec.end(), nonZerosVec.begin());\n    cudaCheckError();\n    nonZerosVec[A.get_num_rows()] = NNZidx;\n    // generate sets on the device\n    IntVector set_fields(A.get_num_nz(), 0);\n    setSetsGPU(A, s_con, cf_map, set_fields);\n    int *set_field_ptr = set_fields.raw();\n    // count the coarse points first\n    // (no need to worry about >4B coarse points)\n    int coarsePoints = (int) amgx::thrust::count_if(cf_map.begin(), cf_map.end(), is_non_neg());\n    cudaCheckError();\n    // I have all the information needed - create P and get the pointers to its data\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, NNZidx, 1);\n    IndexType *Pcolumn_indices_ptr = P.col_indices.raw();\n    ValueType *Pvalues_ptr = P.values.raw();\n    // now P is initialised, get row offsets from nonZerosVec with an exclusive prefix sum\n    // set final value\n    amgx::thrust::copy(nonZerosVec.begin(), nonZerosVec.end(), P.row_offsets.begin());\n    cudaCheckError();\n    IndexType *Poffsets_ptr = P.row_offsets.raw();\n    // assign temp memory\n    VVector B(NNZidx, 0.0);\n    VVector D(A.get_num_rows(), 0.0);\n    VVector W(NNZidx, 0.0);\n    // get the raw pointers for temp memory\n    ValueType *D_ptr = D.raw();\n    ValueType *B_ptr = B.raw();\n    ValueType *W_ptr = W.raw();\n    // call each kernel in turn\n    calculateBKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, D_ptr, set_field_ptr, cf_map_ptr,\n            nonZerosVec_ptr, B_ptr);\n    cudaCheckError();\n    calculateDKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, set_field_ptr, D_ptr);\n    cudaCheckError();\n    calculateWKernel <<< numBlocks, blocksize>>>(Aoffsets_ptr, Acolumn_indices_ptr, Avalues_ptr,\n            (int) A.get_num_rows(), diag_ptr, set_field_ptr, nonZerosVec_ptr,\n            D_ptr, B_ptr, Pvalues_ptr, Pcolumn_indices_ptr,\n            cf_map_ptr);\n    cudaCheckError();\n}\n\ntemplate< class T_Config>\nvoid Distance1_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported block size for distance1 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    P.computeDiagonal();\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class Distance1_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Distance1_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/classical/interpolators/distance1.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <classical/interpolators/distance2.h>\n#include <classical/interpolators/common.h>\n#include <amgx_timer.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <sort.h>\n#include <assert.h>\n#include <misc.h>\n#include <sstream>\n#include <csr_multiply.h>\n#include <hash_workspace.h>\n#include <thrust/count.h>\n#include <thrust/scan.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <device_properties.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nusing std::set;\nusing std::vector;\n\n/*\n * remove all duplicates from a vector\n */\ntemplate <typename T>\nvoid removeDuplicates(vector<T> &vec)\n{\n    std::sort(vec.begin(), vec.end());\n    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());\n}\n\n/*\n * find the value at a particular column of a row if it exists\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type findValue(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const int column_needed)\n{\n    typename VVector::value_type v = 0;\n\n    // NAIVE\n    for (int k = row_begin; k < row_end; k++)\n    {\n        int kcol = columns[k];\n\n        if (kcol == column_needed)\n        {\n            v = values[k];\n        }\n    }\n\n    return v;\n}\n\n\ntemplate <typename IndexType>\nvoid __global__  createCfMapGlobal(const IndexType *cf_map, int64_t *cf_map_global, const int64_t my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (int64_t) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\ntemplate <int coop, typename index_type>\n__global__ void fill_P_global_col_indices_kernel(index_type *row_offsets, index_type *C_hat_start, index_type *C_hat_end, index_type *C_hat, int64_t *cf_map_global, int64_t *global_col_indices, index_type num_rows)\n{\n    int coopIdx = threadIdx.x % coop;\n\n    for (int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop; row < num_rows ; row += blockDim.x * gridDim.x / coop)\n    {\n        int64_t coarse_fine_id = cf_map_global[row];\n        int j = row_offsets[row]  + coopIdx;\n\n        // coarse row\n        if (coarse_fine_id >= 0)\n        {\n            if (coopIdx == 0)\n            {\n                global_col_indices[j] = coarse_fine_id;\n            }\n\n            continue;\n        }\n\n        // strong fine row\n        if (coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // fine row\n        for (int i = C_hat_start[row] + coopIdx; i < C_hat_end[row]; i += coop, j += coop)\n        {\n            index_type fine_col = C_hat[i];\n            int64_t global_col = cf_map_global[fine_col];\n            global_col_indices[j] = global_col;\n        }\n    }\n}\n\n\n/*\n * calculate \\tilde{a}_{ii} from eqn. 4.11 in [4]\n */\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::calculateD( const Matrix_h &A, IVector &cf_map, BVector &s_con,\n        vector<int> *C_hat, VVector &diag, VVector &D,\n        set<int> *weak_lists, VVector &innerSum,\n        IVector &innerSumOffsets)\n{\n    // only do the non-weak part for now\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int outerOffset = innerSumOffsets[i], innerOffset = 0;\n        ValueType sum = 0;\n\n        // loop over all non-zeros\n        for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n        {\n            int kcol = A.col_indices[k];\n\n            if (cf_map[kcol] == FINE && s_con[k]) // strongly connected fine, ignore diagonal\n            {\n                int sgn = diag[kcol] < 0 ? -1 : 1;\n                // find a_ki\n                ValueType temp = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], i);\n                ValueType a_ki = (sgn * temp < 0) * temp;\n                sum += a_ki * innerSum[outerOffset + innerOffset];\n                innerOffset++;\n            }\n        }\n\n        // now calculate the weak part\n        set<int> difference;\n\n        for (set<int>::iterator it = weak_lists[i].begin(); it != weak_lists[i].end(); ++it)\n        {\n            difference.insert(*it);\n        }\n\n        for (int l = 0; l < C_hat[i].size(); l++)\n        {\n            difference.erase(C_hat[i][l]);\n        }\n\n        ValueType weak = 0;\n\n        for (set<int>::iterator it = difference.begin(); it != difference.end(); ++it)\n        {\n            weak += findValue(A.col_indices, A.values, A.row_offsets[i], A.row_offsets[i + 1], *it);\n        }\n\n        D[i] = diag[i] + weak + sum;\n    }\n}\n\n/*\n * Generate the lower sum from eqns. 4.10 and 4.11 of [4]\n * strongly resembles a sparse dot product\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type sparseSum(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const std::vector<int> &C_hat_row, const int sgn)\n{\n    int i1 = row_begin, i2 = 0;\n\n    // check lengths are non-zero\n    if (row_begin >= row_end || C_hat_row.size() == 0) { return 0; }\n\n    typename VVector::value_type sum = 0;\n\n    // if column exists in both C_hat_row and row of matrix, add matrix value\n    while (true)\n    {\n        int col1 = columns[i1];\n        int col2 = C_hat_row[i2];\n\n        if (col1 == col2) // match\n        {\n            if (sgn * values[i1] < 0)\n            {\n                sum += values[i1];\n            }\n\n            i1++;\n            i2++;\n\n            if (i1 >= row_end || i2 >= C_hat_row.size()) { break; }\n        }\n        else if (col1 > col2)\n        {\n            // instead of linear increase, binary search\n            /*      int min = i2++, max = C_hat_row.size();\n                  while(min <= max)\n                  {\n                    int mid = (min+max)/2;\n                    col2 = C_hat_row[mid];\n                    if (col1 > col2)\n                      min = mid+1;\n                    else if (col1 < col2)\n                      max = mid-1;\n                    else\n                    {\n                      i2 = mid;\n                      break;\n                    }\n                  }*/\n            i2++;\n\n            if (i2 >= C_hat_row.size()) { break; }\n        }\n        else\n        {\n            i1++;\n\n            if (i1 >= row_end) { break; }\n        }\n    }\n\n    return sum;\n}\n\n/*\n * Generate the repeatedly reused inner sum from eqns 4.10, 4.11 in [4]\n * \\sum_{k\\in F^s_i} a_{ik}\\frac{1}{\\sum_{l\\in\\hat{C}_i\\cup\\{i\\}} \\bar{a}_{kl}}\n */\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInnerSum(Matrix_h &A, const IntVector &cf_map,\n        const BVector &s_con,\n        vector<int> *C_hat, VVector &diag,\n        VVector &innerSum,\n        IntVector &innerSumOffsets)\n{\n    // calculate necessary storage for innerSum\n    int connections = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            int jcol = A.col_indices[j];\n\n            if (cf_map[jcol] == FINE && s_con[j] && jcol != i)\n            {\n                connections++;\n            }\n        }\n    }\n\n    innerSum.resize(connections);\n    innerSumOffsets.resize(A.get_num_rows() + 1);\n    innerSumOffsets[0] = 0;\n    int innerOffset = 0;\n\n    // generate the values\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n        {\n            int kcol = A.col_indices[k];\n\n            if (cf_map[kcol] == FINE && s_con[k] && kcol != i)\n            {\n                ValueType bottom_sum = 0;\n                ValueType a_ik = A.values[k];\n                int sgn = diag[kcol] < 0 ? -1 : 1;\n                // generate the bottom sum - looks similar to sparse dot product\n                bottom_sum = sparseSum(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1],\n                                       C_hat[i], sgn);\n                // add \\union i\n                ValueType iTerm = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], i);\n                bottom_sum += (sgn * iTerm < 0) * iTerm;\n\n                if (bottom_sum == 0 && cf_map[i] == FINE)\n                {\n                    char buf[255];\n                    sprintf(buf, \"DIVISION BY ZERO: row: %d, col: %d\\n\", i, kcol);\n                    amgx_output(buf, strlen(buf));\n                }\n\n                innerSum[innerOffset] = a_ik / bottom_sum;\n                innerOffset++;\n            }\n        }\n\n        innerSumOffsets[i + 1] = innerOffset;\n    }\n}\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_h &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_h &P)\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        FatalError(\"Distributed classical AMG not implemented on host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_h::value_type ValueType;\n    // implementation begins here\n    set<int> *coarse_lists = new set<int>[A.get_num_rows()];\n    set<int> *weak_lists = new set<int>[A.get_num_rows()];\n    vector<int> *C_hat = new vector<int>[A.get_num_rows()]; // hold full \\hat{C} set for each row\n    set<int> *C_hat_set = new set<int>[A.get_num_rows()];\n    set<int>::iterator set_it;\n    VVector diag(A.get_num_rows(), 0);\n\n    // grab the diagonal\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                diag[i] = A.values[j];\n            }\n        }\n    }\n\n    // generate coarse sets\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // loop over non-zero elements\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            int jcol = A.col_indices[j];\n\n            if (jcol == i) { continue; } // skip the diagonal\n\n            // Coarse and strongly connected\n            if (cf_map[jcol] >= 0 && s_con[j])\n            {\n                coarse_lists[i].insert(jcol);\n            }\n            else if (!s_con[j] && jcol != i && cf_map[jcol] != STRONG_FINE)\n            {\n                weak_lists[i].insert(jcol);\n            }\n        }\n    }\n\n    int numWTotal = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        numWTotal += (int) weak_lists[i].size();\n    }\n\n    typedef typename set<int>::iterator iSetIter;\n\n    // generate \\hat{C} for each row, i\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        C_hat[i].clear();\n\n        if (cf_map[i] == FINE)\n        {\n            // copy coarse_lists[i] to C_hat_set[i]\n            for (iSetIter it = coarse_lists[i].begin(); it != coarse_lists[i].end(); ++it)\n            {\n                C_hat_set[i].insert(*it);\n            }\n\n            // loop over non-zeros\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                int jcol = A.col_indices[j];\n\n                if (jcol == i) { continue; } // don't want to take into account myself\n\n                // if this is a strong-fine connection, union of the sets\n                if (cf_map[jcol] == FINE && s_con[j])\n                {\n                    for (iSetIter it = coarse_lists[jcol].begin(); it != coarse_lists[jcol].end(); ++it)\n                    {\n                        C_hat_set[i].insert(*it);\n                    }\n                }\n            }\n\n            removeDuplicates(C_hat[i]);\n        }\n\n        // copy C_hat_set[i] into C_hat[i]\n        for (iSetIter it = C_hat_set[i].begin(); it != C_hat_set[i].end(); ++it)\n        {\n            C_hat[i].push_back(*it);\n        }\n    }\n\n    // check that \\hat{C_i} for fine points does not contain i\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if (cf_map[i] == FINE)\n        {\n            vector<int>::iterator it = std::find(C_hat[i].begin(), C_hat[i].end(), i);\n            {\n                assert(it == C_hat[i].end());\n            }\n        }\n    }\n\n    // generate the inner sum for all i, k\n    VVector innerSum;\n    IntVector innerSumOffsets;\n    generateInnerSum(A, cf_map, s_con, C_hat, diag, innerSum, innerSumOffsets);\n    // calculate the ~a_ii term\n    VVector D(A.get_num_rows(), 0);\n    calculateD(A, cf_map, s_con, C_hat, diag, D, weak_lists, innerSum, innerSumOffsets);\n    // delete all storage we have no more need for\n    delete [] coarse_lists;\n    delete [] weak_lists;\n    delete [] C_hat_set;\n    // now we have \\hat{C} and the diagonals, we can start calculating the weights\n    // remap cf_map (to be like in distance1)\n    int coarsePoints = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // if coarse\n        if (cf_map[i] >= 0) { coarsePoints++; }\n    }\n\n    // count the non-zeros - 1 per row if coarse, len(C_hat[i]) otherwise\n    IntVector nonZeroOffsets(A.get_num_rows() + 1);\n    IntVector nonZerosPerRow(A.get_num_rows());\n    int nonZeros = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if (cf_map[i] >= 0)\n        {\n            nonZerosPerRow[i] = 1;\n            nonZeros++;\n        }\n        else if (cf_map[i] == STRONG_FINE)\n        {\n            nonZerosPerRow[i] = 0;\n        }\n        else\n        {\n            nonZerosPerRow[i] = (int) C_hat[i].size();\n            nonZeros += (int) C_hat[i].size();\n        }\n    }\n\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_host>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[A.get_num_rows()] = nonZeros;\n    // resize P\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, nonZeros, 1);\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // main loop\n    int outerOffset = 0, innerOffset;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int nonZeroOffset = nonZeroOffsets[i];\n        int localNonZeros = 0;\n        outerOffset = innerSumOffsets[i];\n\n        if (cf_map[i] >= 0)\n        {\n            // will be setting here & continuing\n            P.values[nonZeroOffset + localNonZeros] = 1.0;\n            P.col_indices[nonZeroOffset + localNonZeros] = cf_map[i];\n            continue;\n        }\n        else if (cf_map[i] != STRONG_FINE) // fine row\n        {\n            //\n            for (int j = 0; j < C_hat[i].size(); j++)\n            {\n                innerOffset = 0; // set to 'row' offset\n                int jcol = C_hat[i][j]; // j can potentially not exist on row i\n\n                if (jcol == i) // skip diagonal\n                {\n                    FatalError(\"Error - Diagonal should never be in C_hat[i]\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                ValueType value = 0;\n                // loop over i and find a_ij - if not found, doesn't exist, keep as 0\n                ValueType a_ij = 0;\n                a_ij = findValue(A.col_indices, A.values, A.row_offsets[i], A.row_offsets[i + 1], jcol);\n\n                // now loop over and find strong-fine connections\n                for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n                {\n                    int kcol = A.col_indices[k];\n\n                    if (cf_map[kcol] == FINE && s_con[k]) // Strong-Fine edge, ignore diagonal\n                    {\n                        if (kcol == i)\n                        {\n                            FatalError(\"Error - point should never to strongly connected to itself\", AMGX_ERR_BAD_PARAMETERS);\n                        }\n\n                        // point we care about\n                        int sgn = diag[kcol] < 0 ? -1 : 1;\n                        ValueType a_kj = 0;\n                        // get \\bar{a_kj}\n                        ValueType temp = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], jcol);\n                        a_kj = (sgn * temp < 0) * temp;\n                        // see if we need to add \\bar{a_ki} in\n                        vector<int>::iterator it = std::find(C_hat[i].begin(), C_hat[i].end(), i);\n\n                        if (it != C_hat[i].end())\n                        {\n                            FatalError(\"Error - point i should never be in\\\\hat{C_i}\", AMGX_ERR_BAD_PARAMETERS);\n                        }\n                        else\n                        {\n                            // we now have everything we need - add to sum\n                            // not valid in non-symmetric matrices\n                            value += innerSum[outerOffset + innerOffset] * a_kj;\n                            innerOffset++;\n                        }\n                    } // strong fine connection end\n                } // k loop (find strong fine connections) end\n\n                ValueType diagonal = (D[i]);\n                // now set column & value for P\n                P.col_indices[nonZeroOffset + localNonZeros] = cf_map[jcol];\n                P.values[nonZeroOffset + localNonZeros] = -1.0 / (diagonal) * (a_ij + value);\n                localNonZeros++;\n            } // end j loop\n        } // fine connection loop\n\n        outerOffset += innerOffset;\n    } // end i loop\n\n    delete [] C_hat;\n} // end distance2 interpolator\n\n\n///////////////////////////////////////////////////////////////////////////////\n\n// DEVICE CODE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace distance2\n{\n\n#include <sm_utils.inl>\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip coarse and strong fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncthreads();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of B.\n                int b_col_it  = A_rows[b_row_id + 0];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    s_b_row_ids[threadIdx.x] = 0;\n    __syncthreads();\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n\n            int num_rows = __popc(vote);\n            for ( int k = 0; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id + 0];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Shared memory to vote.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        __syncthreads();\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n        __syncthreads();\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of the row of B.\n                int b_col_it  = A_rows[uniform_b_row_id + 0];\n                int b_col_end = A_rows[uniform_b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[uniform_b_row_id + 0];\n                    b_col_end = A_rows[uniform_b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_inner_sum_kernel( const int A_num_rows,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const Value_type *__restrict A_vals,\n                          const int *__restrict cf_map,\n                          const bool *__restrict s_con,\n                          const int *__restrict C_hat,\n                          const int *__restrict C_hat_pos,\n                          const int *__restrict C_hat_start,\n                          const int *__restrict C_hat_end,\n                          const Value_type *__restrict diag,\n                          const int *__restrict inner_sum_offsets,\n                          Value_type *inner_sum,\n                          const int gmem_size,\n                          int *g_keys,\n                          int *wk_work_queue )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_a_values[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        set.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // And share the value of the diagonal.\n        bool sign_diag = false;\n\n        if ( lane_id == 0 )\n        {\n            sign_diag = sign( diag[a_row_id] );\n        }\n\n        sign_diag = utils::shfl( sign_diag, 0 );\n\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // _columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[a_col_it] && cf_map[a_col_id] == FINE;\n            // Read the associated value from A.\n            Value_type a_value(0);\n\n            if ( is_fine_strongly_connected )\n            {\n                a_value = A_vals[a_col_it];\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_a_values [warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n            // First n_rows threads reload the correct value.\n            a_value = s_a_values[warp_id * WARP_SIZE + lane_id];\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Each thread keeps its own sum.\n                Value_type bottom_sum(0);\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses.\n                int b_col_it  = A_rows[b_row_id  ];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it needed to compute the bottom sum.\n                    bool is_needed = set.contains( b_col_id ) || b_col_id == a_row_id;\n                    // Read the associated value from A.\n                    Value_type b_value(0);\n\n                    if ( is_needed )\n                    {\n                        b_value = A_vals[b_col_it];\n                    }\n\n                    // Update the sum if needed.\n                    if ( sign_diag != sign( b_value ) )\n                    {\n                        bottom_sum += b_value;\n                    }\n                }\n\n                // Reduce the row to a single value.\n#pragma unroll\n                for ( int offset = WARP_SIZE / 2 ; offset > 0 ; offset >>= 1 )\n                {\n                    int hi = __double2hiint(bottom_sum);\n                    hi = utils::shfl_xor( hi, offset );\n                    int lo = __double2loint(bottom_sum);\n                    lo = utils::shfl_xor( lo, offset );\n                    bottom_sum += __hiloint2double(hi, lo);\n                }\n\n                bottom_sum = utils::shfl( bottom_sum, 0 );\n\n                if ( lane_id == k && bottom_sum != Value_type(0) )\n                {\n                    a_value /= bottom_sum;\n                }\n            }\n\n            // Write the results.\n            if ( lane_id < num_rows )\n            {\n                inner_sum[inner_sum_offset + lane_id] = a_value;\n            }\n\n            inner_sum_offset += num_rows;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_inner_sum_kernel( const int A_num_rows,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const Value_type *__restrict A_vals,\n                          const int *__restrict cf_map,\n                          const bool *__restrict s_con,\n                          const int *__restrict C_hat,\n                          const int *__restrict C_hat_pos,\n                          const int *__restrict C_hat_start,\n                          const int *__restrict C_hat_end,\n                          const Value_type *__restrict diag,\n                          const int *__restrict inner_sum_offsets,\n                          Value_type *inner_sum,\n                          const int gmem_size,\n                          int *g_keys,\n                          int *wk_work_queue )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_a_values[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        set.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // And share the value of the diagonal.\n        bool sign_diag = false;\n\n        if ( lane_id == 0 )\n        {\n            sign_diag = sign( diag[a_row_id] );\n        }\n\n        sign_diag = utils::shfl( sign_diag, 0 );\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // _columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[a_col_it] && cf_map[a_col_id] == FINE;\n            // Read the associated value from A.\n            Value_type a_value(0);\n\n            if ( is_fine_strongly_connected )\n            {\n                a_value = A_vals[a_col_it];\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_a_values [warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            int num_rows = __popc( vote );\n            // First n_rows threads reload the correct value.\n            a_value = s_a_values[warp_id * WARP_SIZE + lane_id];\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                // Each thread keeps its own sum.\n                Value_type bottom_sum(0);\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k + lane_id_div_num_threads];\n                }\n\n                // TODO: make sure we have better memory accesses.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id  ];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any( b_col_it < b_col_end ) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it needed to compute the bottom sum.\n                    bool is_needed = set.contains( b_col_id ) || b_col_id == a_row_id;\n                    // Read the associated value from A.\n                    Value_type b_value(0);\n\n                    if ( is_needed )\n                    {\n                        b_value = A_vals[b_col_it];\n                    }\n\n                    // Update the sum if needed.\n                    if ( sign_diag != sign( b_value ) )\n                    {\n                        bottom_sum += b_value;\n                    }\n                }\n\n                // Reduce the row to a single value.\n#pragma unroll\n                for ( int offset = NUM_THREADS_PER_ROW / 2 ; offset > 0 ; offset >>= 1 )\n                {\n                    int hi = __double2hiint(bottom_sum);\n                    hi = utils::shfl_down( hi, offset, NUM_THREADS_PER_ROW );\n                    int lo = __double2loint(bottom_sum);\n                    lo = utils::shfl_down( lo, offset, NUM_THREADS_PER_ROW );\n                    bottom_sum += __hiloint2double(hi, lo);\n                }\n\n                bottom_sum = utils::shfl( bottom_sum, lane_id_mod_num_threads * NUM_THREADS_PER_ROW );\n\n                if ( lane_id >= k && lane_id < k + NUM_THREADS_PER_ROW && bottom_sum != Value_type(0) )\n                {\n                    a_value /= bottom_sum;\n                }\n            }\n\n            // Write the results.\n            if ( lane_id < num_rows )\n            {\n                inner_sum[inner_sum_offset + lane_id] = a_value;\n            }\n\n            inner_sum_offset += num_rows;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const int *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_end,\n                              const Value_type *__restrict diag,\n                              const int *__restrict inner_sum_offsets,\n                              const Value_type *__restrict inner_sum,\n                              const int *__restrict P_rows,\n                              int *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              int *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue )\n{\n    \n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_aki[NUM_WARPS];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int coarse_fine_id = cf_map[a_row_id];\n\n        // Skip coarse rows.\n        if ( coarse_fine_id >= 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n\n            continue;\n        }\n\n        if ( coarse_fine_id == STRONG_FINE )\n        {\n            continue;\n        }\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // Weak value.\n        Value_type weak(0), sum(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n            // Is isolated\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n            bool is_in_c_hat = map.update( a_col_id, a_value );\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_in_c_hat && !is_strong_fine)\n            {\n                weak += a_value;\n            }\n\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && cf_map[a_col_id] == FINE;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n            // We pre-load inner sums.\n            sum = Value_type(0);\n\n            if ( lane_id < num_rows )\n            {\n                sum = inner_sum[inner_sum_offset + lane_id];\n            }\n\n            inner_sum_offset += num_rows;\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = A_rows[b_row_id  ];\n                int b_col_end   = A_rows[b_row_id + 1];\n                // Diagonal element.\n                Value_type b_diag = diag[b_row_id];\n\n                // Set s_aki to 0 because a_row_id may not appear in a row.\n                if ( lane_id == 0 )\n                {\n                    s_aki[warp_id] = Value_type(0);\n                }\n\n                utils::syncwarp();\n\n                // Load the kth inner sum.\n                Value_type uniform_val = utils::shfl( sum, k );\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id(-1);\n                    Value_type b_value(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                        b_value  = A_vals[b_col_it];\n                    }\n\n                    // Update bValue if needed.\n                    if ( sign( b_diag ) == sign( b_value ) )\n                    {\n                        b_value = Value_type(0);\n                    }\n\n                    // See if we have found a_row_id in the row. Only one thread can evaluate to true.\n                    if ( a_row_id == b_col_id )\n                    {\n                        s_aki[warp_id] = b_value;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    map.update( b_col_id, b_value * uniform_val );\n                }\n\n                utils::syncwarp();\n\n                // The thread k updates the sum.\n                if ( lane_id == 0 )\n                {\n                    weak += s_aki[warp_id] * uniform_val;\n                }\n            }\n        }\n\n        // We're done with that row of A. We compute D.\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            weak += utils::shfl_xor( weak, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            weak += diag[a_row_id];\n            sum   = Value_type(-1) / weak;\n        }\n\n        sum = utils::shfl( sum, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_map_keys_scale_values( p_col_end - p_col_it, cf_map, &P_cols[p_col_it], sum, &P_vals[p_col_it] );\n    }\n}\n\n\n} // namespace distance2\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename IndexType, int kCtaSize >\n__global__\nvoid\ncalculateInnerSumStorageKernel( const IndexType *A_rows,\n                                const IndexType *A_cols,\n                                const int A_num_rows,\n                                const int *cf_map,\n                                const bool *s_con,\n                                int *innerSum_offsets )\n{\n    const int nWarps = kCtaSize / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[aRowId];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // The sum for each thread.\n        int sum = 0;\n        // Load A row IDs.\n        int aColIt  = A_rows[aRowId  ];\n        int aColEnd = A_rows[aRowId + 1];\n\n        // Iterate over the columns of A.\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[aColIt] && cf_map[aColId] == FINE;\n            // Update the sum.\n            sum += __popc( utils::ballot( is_fine_strongly_connected ) );\n        }\n\n        // Update the value.\n        if ( laneId == 0 )\n        {\n            innerSum_offsets[aRowId] = sum;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*\n * grab the diagonal of the matrix\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n    }\n}\n\n/*************************************************************************\n * Implementing Extended+i algorithm from \\S 4.5 of:\n * \"Distance-two interpolation for parallel algebraic multigrid\"\n * Reference [4] on wiki\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Distance2_Interpolator(AMG_Config &cfg, const std::string &cfg_scope) : Base(cfg, cfg_scope)\n{}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~Distance2_Interpolator()\n{}\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    nvtxRange gim_nr(__func__);\n\n    const int blockSize = 256;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n\n    cudaDeviceProp props = getDeviceProperties();\n    int grid_size = (props.major >= 7) ? 1024 : 128;\n    int gmem_size = (props.major >= 7) ? 512 : 2048;\n\n    Hash_Workspace<TConfig_d, int> exp_wk(true, grid_size, 8, gmem_size);\n\n    IntVector C_hat_start( A.get_num_rows() + 1, 0 ), C_hat_end( A.get_num_rows() + 1, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int work_offset = grid_size * NUM_WARPS;\n        cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n        cudaCheckError();\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n\n        if ( avg_nz_per_row < 16 )\n        {\n            distance2::estimate_c_hat_size_kernel< 8, CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n                cudaCheckError();\n        }\n        else\n        {\n            distance2::estimate_c_hat_size_kernel<CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n        }\n\n        cudaCheckError();\n    }\n    // Compute row offsets.\n    thrust_wrapper::exclusive_scan<AMGX_device>( C_hat_start.begin( ), C_hat_start.end( ), C_hat_start.begin( ) );\n    cudaCheckError();\n    // Allocate memory to store columns/values.\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    IntVector C_hat( nVals ), C_hat_pos( nVals );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        int attempt = 0;\n\n        for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n        {\n            // Double the amount of GMEM (if needed).\n            if ( attempt > 0 )\n            {\n                exp_wk.expand();\n            }\n\n            // Reset the status. TODO: Launch async copies.\n            int status = 0;\n            cudaMemcpy( exp_wk.get_status(), &status, sizeof(int), cudaMemcpyHostToDevice );\n            cudaCheckError();\n            // Compute the set C_hat.\n            int work_offset = grid_size * NUM_WARPS;\n            cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n            cudaCheckError();\n\n            // Run the computation.\n            if ( avg_nz_per_row < 16 )\n            {\n                distance2::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n                    cudaCheckError();\n            }\n            else\n            {\n                distance2::compute_c_hat_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n            }\n\n            cudaCheckError();\n            // Read the result from count_non_zeroes.\n            cudaMemcpy( &status, exp_wk.get_status(), sizeof(int), cudaMemcpyDeviceToHost );\n            cudaCheckError();\n            done = status == 0;\n        }\n    }\n    // get pointers to data\n    int *C_hat_ptr = C_hat.raw();\n    int *C_hat_start_ptr = C_hat_start.raw();\n    int *C_hat_end_ptr = C_hat_end.raw();\n    // modify Coarse/Fine map array to have the column index of the new interpolation matrix\n    // for evry coarse point\n    int coarsePoints;\n    DistributedArranger<TConfig_d> *prep;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        cudaCheckError();\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        coarsePoints = num_owned_coarse_pts + num_halo_coarse_pts;\n        // Using the number of owned_coarse_pts (number of rows of Ac), initialize P manager\n        // This will set the part_offsets array, index_range, halo_offsets[0] value\n        prep->initialize_manager(A, P, num_owned_coarse_pts);\n    }\n    else\n    {\n        coarsePoints = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n    }\n\n    // mark as 1 if a coarse point\n    int size_one_ring;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int offset;\n        A.getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    }\n    else\n    {\n        size_one_ring = A.get_num_rows();\n    }\n\n    int numBlocksOneRing = std::min( 4096, (int) (size_one_ring + blockSize - 1) / blockSize );\n    int numBlocks = std::min( 4096, (int) (A.get_num_rows() + blockSize - 1) / blockSize );\n    // count the number of non-zeros in the interpolation matrix\n    IntVector nonZeroOffsets(A.get_num_rows() + 1);\n    IntVector nonZerosPerRow(A.get_num_rows());\n    int *nonZerosPerRow_ptr = nonZerosPerRow.raw();\n\n    if (A.get_num_rows() > 0)\n    {\n        nonZerosPerRowKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map.raw(), C_hat_start_ptr, C_hat_end_ptr,\n                nonZerosPerRow_ptr);\n        cudaCheckError();\n    }\n\n    // get total with a reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[A.get_num_rows()] = nonZeros;\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, nonZeros, 1);\n    IndexType *Poffsets = P.row_offsets.raw();\n    IndexType *Pcolumn_indices = P.col_indices.raw();\n    ValueType *Pvalues = P.values.raw();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // grab the diagonal terms\n    VVector diag(size_one_ring);\n    ValueType *diag_ptr = diag.raw();\n\n    if (A.get_num_rows() > 0)\n    {\n        find_diag_kernel_indexed_dia <<< numBlocksOneRing, blockSize>>>(\n            size_one_ring,\n            A.diag.raw(),\n            A.values.raw(),\n            diag.raw());\n    }\n\n    cudaCheckError();\n    IntVector innerSumOffset( A.get_num_rows() + 1, 0 );\n\n    if (A.get_num_rows() > 0)\n    {\n        const int kCtaSize = 256;\n        const int nWarps = kCtaSize / 32;\n        const int grid_size = std::min( 4096, ( Anum_rows + nWarps - 1 ) / nWarps );\n        calculateInnerSumStorageKernel<IndexType, kCtaSize> <<< grid_size, kCtaSize>>>(\n            Aoffsets,\n            Acolumn_indices,\n            Anum_rows,\n            cf_map.raw(),\n            s_con.raw(),\n            innerSumOffset.raw() );\n        cudaCheckError();\n    }\n\n    // get the offsets\n    thrust_wrapper::exclusive_scan<AMGX_device>( innerSumOffset.begin(), innerSumOffset.end(), innerSumOffset.begin() );\n    cudaCheckError();\n    // assign memory & get pointer\n    int numInnerSum = innerSumOffset[A.get_num_rows()];\n    VVector innerSum( numInnerSum, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        // Compute the set C_hat.\n        int work_offset = grid_size * NUM_WARPS;\n        cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n        cudaCheckError();\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n        {\n            distance2::compute_inner_sum_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat.raw(),\n                C_hat_pos.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                diag.raw(),\n                innerSumOffset.raw(),\n                innerSum.raw(),\n                exp_wk.get_gmem_size(),\n                exp_wk.get_keys(),\n                exp_wk.get_work_queue());\n        }\n        cudaCheckError();\n    }\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        // Compute the set C_hat.\n        int work_offset = grid_size * NUM_WARPS;\n        cudaMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), cudaMemcpyHostToDevice );\n        cudaCheckError();\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n        distance2::compute_interp_weight_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map.raw(),\n            s_con.raw(),\n            C_hat.raw(),\n            C_hat_pos.raw(),\n            C_hat_start.raw(),\n            C_hat_end.raw(),\n            diag.raw(),\n            innerSumOffset.raw(),\n            innerSum.raw(),\n            P.row_offsets.raw(),\n            P.col_indices.raw(),\n            P.values.raw(),\n            exp_wk.get_gmem_size(),\n            exp_wk.get_keys(),\n            exp_wk.get_vals(),\n            exp_wk.get_work_queue());\n        cudaCheckError();\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        //TODO: cf_map_global could be smaller (only contain halo nodes)\n        I64Vector_d cf_map_global(cf_map.size());\n        int num_owned_fine_pts = P.get_num_rows();\n        int my_rank = A.manager->global_id();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (num_owned_fine_pts + cta_size - 1) / cta_size);\n\n        // Write the global coarse index of all the owned fine points (-1 if not a coarse point)\n        if (num_owned_fine_pts > 0)\n        {\n            createCfMapGlobal <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), P.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n            cudaCheckError();\n        }\n\n        // Exchange the cf_map_global so that we know the coarse global id of halo nodes\n        cf_map_global.dirtybit = 1;\n        A.manager->exchange_halo_2ring(cf_map_global, cf_map_global.tag);\n        I64Vector_d P_col_indices_global(P.col_indices.size());\n\n        if (num_owned_fine_pts > 0)\n        {\n            fill_P_global_col_indices_kernel<16, int> <<< grid_size, cta_size>>>(P.row_offsets.raw(), C_hat_start.raw(), C_hat_end.raw(), C_hat.raw(), cf_map_global.raw(), P_col_indices_global.raw(), num_owned_fine_pts);\n            cudaCheckError();\n        }\n\n        // Now that we know the global col indices of P, we can find:\n        // the list of neighbors, create the B2L_maps for P, create local numbering for halo nodes, create local_to_global_map array, etc...\n        prep->initialize_manager_from_global_col_indices(P, P_col_indices_global);\n        prep->createRowsLists(P, true);\n        delete prep;\n    }\n}\n\ntemplate< class T_Config>\nvoid Distance2_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    // If multi-GPU do a 2-ring exchange of cf_map since this is a distance two interpolator\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimensions for distance2 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class Distance2_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Distance2_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <classical/interpolators/distance2.h>\n#include <classical/interpolators/common.h>\n#include <amgx_timer.h>\n#include <basic_types.h>\n#include <types.h>\n#include <cutil.h>\n#include <util.h>\n#include <sm_utils.inl>\n#include <fstream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <sort.h>\n#include <assert.h>\n#include <misc.h>\n#include <sstream>\n#include <csr_multiply.h>\n#include <hash_workspace.h>\n#include <thrust/count.h>\n#include <thrust/scan.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <device_properties.h>\n#include <thrust_wrapper.h>\n\nnamespace amgx\n{\n\nusing std::set;\nusing std::vector;\n\n/*\n * remove all duplicates from a vector\n */\ntemplate <typename T>\nvoid removeDuplicates(vector<T> &vec)\n{\n    std::sort(vec.begin(), vec.end());\n    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());\n}\n\n/*\n * find the value at a particular column of a row if it exists\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type findValue(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const int column_needed)\n{\n    typename VVector::value_type v = 0;\n\n    // NAIVE\n    for (int k = row_begin; k < row_end; k++)\n    {\n        int kcol = columns[k];\n\n        if (kcol == column_needed)\n        {\n            v = values[k];\n        }\n    }\n\n    return v;\n}\n\n\ntemplate <typename IndexType>\nvoid __global__  createCfMapGlobal(const IndexType *cf_map, int64_t *cf_map_global, const int64_t my_part_offset, const IndexType num_owned_fine_pts)\n{\n    for (int tidx = threadIdx.x + blockIdx.x * blockDim.x; tidx < num_owned_fine_pts ; tidx += blockDim.x * gridDim.x)\n    {\n        // Only renumber the interior points\n        if (cf_map[tidx] >= 0 && tidx < num_owned_fine_pts)\n        {\n            // Simply shift\n            cf_map_global[tidx] = (int64_t) cf_map[tidx] /* my_local_id */ + my_part_offset;\n        }\n        else\n        {\n            cf_map_global[tidx] = -1;\n        }\n    }\n}\n\ntemplate <int coop, typename index_type>\n__global__ void fill_P_global_col_indices_kernel(index_type *row_offsets, index_type *C_hat_start, index_type *C_hat_end, index_type *C_hat, int64_t *cf_map_global, int64_t *global_col_indices, index_type num_rows)\n{\n    int coopIdx = threadIdx.x % coop;\n\n    for (int row = blockIdx.x * blockDim.x / coop + threadIdx.x / coop; row < num_rows ; row += blockDim.x * gridDim.x / coop)\n    {\n        int64_t coarse_fine_id = cf_map_global[row];\n        int j = row_offsets[row]  + coopIdx;\n\n        // coarse row\n        if (coarse_fine_id >= 0)\n        {\n            if (coopIdx == 0)\n            {\n                global_col_indices[j] = coarse_fine_id;\n            }\n\n            continue;\n        }\n\n        // strong fine row\n        if (coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // fine row\n        for (int i = C_hat_start[row] + coopIdx; i < C_hat_end[row]; i += coop, j += coop)\n        {\n            index_type fine_col = C_hat[i];\n            int64_t global_col = cf_map_global[fine_col];\n            global_col_indices[j] = global_col;\n        }\n    }\n}\n\n\n/*\n * calculate \\tilde{a}_{ii} from eqn. 4.11 in [4]\n */\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::calculateD( const Matrix_h &A, IVector &cf_map, BVector &s_con,\n        vector<int> *C_hat, VVector &diag, VVector &D,\n        set<int> *weak_lists, VVector &innerSum,\n        IVector &innerSumOffsets)\n{\n    // only do the non-weak part for now\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int outerOffset = innerSumOffsets[i], innerOffset = 0;\n        ValueType sum = 0;\n\n        // loop over all non-zeros\n        for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n        {\n            int kcol = A.col_indices[k];\n\n            if (cf_map[kcol] == FINE && s_con[k]) // strongly connected fine, ignore diagonal\n            {\n                int sgn = diag[kcol] < 0 ? -1 : 1;\n                // find a_ki\n                ValueType temp = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], i);\n                ValueType a_ki = (sgn * temp < 0) * temp;\n                sum += a_ki * innerSum[outerOffset + innerOffset];\n                innerOffset++;\n            }\n        }\n\n        // now calculate the weak part\n        set<int> difference;\n\n        for (set<int>::iterator it = weak_lists[i].begin(); it != weak_lists[i].end(); ++it)\n        {\n            difference.insert(*it);\n        }\n\n        for (int l = 0; l < C_hat[i].size(); l++)\n        {\n            difference.erase(C_hat[i][l]);\n        }\n\n        ValueType weak = 0;\n\n        for (set<int>::iterator it = difference.begin(); it != difference.end(); ++it)\n        {\n            weak += findValue(A.col_indices, A.values, A.row_offsets[i], A.row_offsets[i + 1], *it);\n        }\n\n        D[i] = diag[i] + weak + sum;\n    }\n}\n\n/*\n * Generate the lower sum from eqns. 4.10 and 4.11 of [4]\n * strongly resembles a sparse dot product\n */\ntemplate <typename IVector, typename VVector>\ntypename VVector::value_type sparseSum(const IVector &columns,\n                                       const VVector &values,\n                                       const int row_begin, const int row_end, const std::vector<int> &C_hat_row, const int sgn)\n{\n    int i1 = row_begin, i2 = 0;\n\n    // check lengths are non-zero\n    if (row_begin >= row_end || C_hat_row.size() == 0) { return 0; }\n\n    typename VVector::value_type sum = 0;\n\n    // if column exists in both C_hat_row and row of matrix, add matrix value\n    while (true)\n    {\n        int col1 = columns[i1];\n        int col2 = C_hat_row[i2];\n\n        if (col1 == col2) // match\n        {\n            if (sgn * values[i1] < 0)\n            {\n                sum += values[i1];\n            }\n\n            i1++;\n            i2++;\n\n            if (i1 >= row_end || i2 >= C_hat_row.size()) { break; }\n        }\n        else if (col1 > col2)\n        {\n            // instead of linear increase, binary search\n            /*      int min = i2++, max = C_hat_row.size();\n                  while(min <= max)\n                  {\n                    int mid = (min+max)/2;\n                    col2 = C_hat_row[mid];\n                    if (col1 > col2)\n                      min = mid+1;\n                    else if (col1 < col2)\n                      max = mid-1;\n                    else\n                    {\n                      i2 = mid;\n                      break;\n                    }\n                  }*/\n            i2++;\n\n            if (i2 >= C_hat_row.size()) { break; }\n        }\n        else\n        {\n            i1++;\n\n            if (i1 >= row_end) { break; }\n        }\n    }\n\n    return sum;\n}\n\n/*\n * Generate the repeatedly reused inner sum from eqns 4.10, 4.11 in [4]\n * \\sum_{k\\in F^s_i} a_{ik}\\frac{1}{\\sum_{l\\in\\hat{C}_i\\cup\\{i\\}} \\bar{a}_{kl}}\n */\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInnerSum(Matrix_h &A, const IntVector &cf_map,\n        const BVector &s_con,\n        vector<int> *C_hat, VVector &diag,\n        VVector &innerSum,\n        IntVector &innerSumOffsets)\n{\n    // calculate necessary storage for innerSum\n    int connections = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            int jcol = A.col_indices[j];\n\n            if (cf_map[jcol] == FINE && s_con[j] && jcol != i)\n            {\n                connections++;\n            }\n        }\n    }\n\n    innerSum.resize(connections);\n    innerSumOffsets.resize(A.get_num_rows() + 1);\n    innerSumOffsets[0] = 0;\n    int innerOffset = 0;\n\n    // generate the values\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n        {\n            int kcol = A.col_indices[k];\n\n            if (cf_map[kcol] == FINE && s_con[k] && kcol != i)\n            {\n                ValueType bottom_sum = 0;\n                ValueType a_ik = A.values[k];\n                int sgn = diag[kcol] < 0 ? -1 : 1;\n                // generate the bottom sum - looks similar to sparse dot product\n                bottom_sum = sparseSum(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1],\n                                       C_hat[i], sgn);\n                // add \\union i\n                ValueType iTerm = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], i);\n                bottom_sum += (sgn * iTerm < 0) * iTerm;\n\n                if (bottom_sum == 0 && cf_map[i] == FINE)\n                {\n                    char buf[255];\n                    sprintf(buf, \"DIVISION BY ZERO: row: %d, col: %d\\n\", i, kcol);\n                    amgx_output(buf, strlen(buf));\n                }\n\n                innerSum[innerOffset] = a_ik / bottom_sum;\n                innerOffset++;\n            }\n        }\n\n        innerSumOffsets[i + 1] = innerOffset;\n    }\n}\n\n/*************************************************************************\n* create the interpolation matrix P\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_h &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_h &P)\n{\n    if (!A.is_matrix_singleGPU())\n    {\n        FatalError(\"Distributed classical AMG not implemented on host\\n\", AMGX_ERR_NOT_IMPLEMENTED);\n    }\n\n    typedef typename Matrix_h::value_type ValueType;\n    // implementation begins here\n    set<int> *coarse_lists = new set<int>[A.get_num_rows()];\n    set<int> *weak_lists = new set<int>[A.get_num_rows()];\n    vector<int> *C_hat = new vector<int>[A.get_num_rows()]; // hold full \\hat{C} set for each row\n    set<int> *C_hat_set = new set<int>[A.get_num_rows()];\n    set<int>::iterator set_it;\n    VVector diag(A.get_num_rows(), 0);\n\n    // grab the diagonal\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            if (A.col_indices[j] == i)\n            {\n                diag[i] = A.values[j];\n            }\n        }\n    }\n\n    // generate coarse sets\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // loop over non-zero elements\n        for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n        {\n            int jcol = A.col_indices[j];\n\n            if (jcol == i) { continue; } // skip the diagonal\n\n            // Coarse and strongly connected\n            if (cf_map[jcol] >= 0 && s_con[j])\n            {\n                coarse_lists[i].insert(jcol);\n            }\n            else if (!s_con[j] && jcol != i && cf_map[jcol] != STRONG_FINE)\n            {\n                weak_lists[i].insert(jcol);\n            }\n        }\n    }\n\n    int numWTotal = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        numWTotal += (int) weak_lists[i].size();\n    }\n\n    typedef typename set<int>::iterator iSetIter;\n\n    // generate \\hat{C} for each row, i\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        C_hat[i].clear();\n\n        if (cf_map[i] == FINE)\n        {\n            // copy coarse_lists[i] to C_hat_set[i]\n            for (iSetIter it = coarse_lists[i].begin(); it != coarse_lists[i].end(); ++it)\n            {\n                C_hat_set[i].insert(*it);\n            }\n\n            // loop over non-zeros\n            for (int j = A.row_offsets[i]; j < A.row_offsets[i + 1]; j++)\n            {\n                int jcol = A.col_indices[j];\n\n                if (jcol == i) { continue; } // don't want to take into account myself\n\n                // if this is a strong-fine connection, union of the sets\n                if (cf_map[jcol] == FINE && s_con[j])\n                {\n                    for (iSetIter it = coarse_lists[jcol].begin(); it != coarse_lists[jcol].end(); ++it)\n                    {\n                        C_hat_set[i].insert(*it);\n                    }\n                }\n            }\n\n            removeDuplicates(C_hat[i]);\n        }\n\n        // copy C_hat_set[i] into C_hat[i]\n        for (iSetIter it = C_hat_set[i].begin(); it != C_hat_set[i].end(); ++it)\n        {\n            C_hat[i].push_back(*it);\n        }\n    }\n\n    // check that \\hat{C_i} for fine points does not contain i\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if (cf_map[i] == FINE)\n        {\n            vector<int>::iterator it = std::find(C_hat[i].begin(), C_hat[i].end(), i);\n            {\n                assert(it == C_hat[i].end());\n            }\n        }\n    }\n\n    // generate the inner sum for all i, k\n    VVector innerSum;\n    IntVector innerSumOffsets;\n    generateInnerSum(A, cf_map, s_con, C_hat, diag, innerSum, innerSumOffsets);\n    // calculate the ~a_ii term\n    VVector D(A.get_num_rows(), 0);\n    calculateD(A, cf_map, s_con, C_hat, diag, D, weak_lists, innerSum, innerSumOffsets);\n    // delete all storage we have no more need for\n    delete [] coarse_lists;\n    delete [] weak_lists;\n    delete [] C_hat_set;\n    // now we have \\hat{C} and the diagonals, we can start calculating the weights\n    // remap cf_map (to be like in distance1)\n    int coarsePoints = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        // if coarse\n        if (cf_map[i] >= 0) { coarsePoints++; }\n    }\n\n    // count the non-zeros - 1 per row if coarse, len(C_hat[i]) otherwise\n    IntVector nonZeroOffsets(A.get_num_rows() + 1);\n    IntVector nonZerosPerRow(A.get_num_rows());\n    int nonZeros = 0;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        if (cf_map[i] >= 0)\n        {\n            nonZerosPerRow[i] = 1;\n            nonZeros++;\n        }\n        else if (cf_map[i] == STRONG_FINE)\n        {\n            nonZerosPerRow[i] = 0;\n        }\n        else\n        {\n            nonZerosPerRow[i] = (int) C_hat[i].size();\n            nonZeros += (int) C_hat[i].size();\n        }\n    }\n\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_host>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[A.get_num_rows()] = nonZeros;\n    // resize P\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, nonZeros, 1);\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // main loop\n    int outerOffset = 0, innerOffset;\n\n    for (int i = 0; i < A.get_num_rows(); i++)\n    {\n        int nonZeroOffset = nonZeroOffsets[i];\n        int localNonZeros = 0;\n        outerOffset = innerSumOffsets[i];\n\n        if (cf_map[i] >= 0)\n        {\n            // will be setting here & continuing\n            P.values[nonZeroOffset + localNonZeros] = 1.0;\n            P.col_indices[nonZeroOffset + localNonZeros] = cf_map[i];\n            continue;\n        }\n        else if (cf_map[i] != STRONG_FINE) // fine row\n        {\n            //\n            for (int j = 0; j < C_hat[i].size(); j++)\n            {\n                innerOffset = 0; // set to 'row' offset\n                int jcol = C_hat[i][j]; // j can potentially not exist on row i\n\n                if (jcol == i) // skip diagonal\n                {\n                    FatalError(\"Error - Diagonal should never be in C_hat[i]\", AMGX_ERR_BAD_PARAMETERS);\n                }\n\n                ValueType value = 0;\n                // loop over i and find a_ij - if not found, doesn't exist, keep as 0\n                ValueType a_ij = 0;\n                a_ij = findValue(A.col_indices, A.values, A.row_offsets[i], A.row_offsets[i + 1], jcol);\n\n                // now loop over and find strong-fine connections\n                for (int k = A.row_offsets[i]; k < A.row_offsets[i + 1]; k++)\n                {\n                    int kcol = A.col_indices[k];\n\n                    if (cf_map[kcol] == FINE && s_con[k]) // Strong-Fine edge, ignore diagonal\n                    {\n                        if (kcol == i)\n                        {\n                            FatalError(\"Error - point should never to strongly connected to itself\", AMGX_ERR_BAD_PARAMETERS);\n                        }\n\n                        // point we care about\n                        int sgn = diag[kcol] < 0 ? -1 : 1;\n                        ValueType a_kj = 0;\n                        // get \\bar{a_kj}\n                        ValueType temp = findValue(A.col_indices, A.values, A.row_offsets[kcol], A.row_offsets[kcol + 1], jcol);\n                        a_kj = (sgn * temp < 0) * temp;\n                        // see if we need to add \\bar{a_ki} in\n                        vector<int>::iterator it = std::find(C_hat[i].begin(), C_hat[i].end(), i);\n\n                        if (it != C_hat[i].end())\n                        {\n                            FatalError(\"Error - point i should never be in\\\\hat{C_i}\", AMGX_ERR_BAD_PARAMETERS);\n                        }\n                        else\n                        {\n                            // we now have everything we need - add to sum\n                            // not valid in non-symmetric matrices\n                            value += innerSum[outerOffset + innerOffset] * a_kj;\n                            innerOffset++;\n                        }\n                    } // strong fine connection end\n                } // k loop (find strong fine connections) end\n\n                ValueType diagonal = (D[i]);\n                // now set column & value for P\n                P.col_indices[nonZeroOffset + localNonZeros] = cf_map[jcol];\n                P.values[nonZeroOffset + localNonZeros] = -1.0 / (diagonal) * (a_ij + value);\n                localNonZeros++;\n            } // end j loop\n        } // fine connection loop\n\n        outerOffset += innerOffset;\n    } // end i loop\n\n    delete [] C_hat;\n} // end distance2 interpolator\n\n\n///////////////////////////////////////////////////////////////////////////////\n\n// DEVICE CODE\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <sm_utils.inl>\n#include <hash_containers_detail.inl> // Included inside the namespace to solve name colisions.\n\n__device__ __forceinline__ int get_work( int *queue, int warp_id )\n{\n    int offset = -1;\n\n    if ( utils::lane_id() == 0 )\n    {\n        offset = atomicAdd( queue, 1 );\n    }\n\n    return utils::shfl( offset, 0 );\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace distance2\n{\n\n#include <sm_utils.inl>\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip coarse and strong fine rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            __syncthreads();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0, num_rows = __popc(vote) ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of B.\n                int b_col_it  = A_rows[b_row_id + 0];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\nestimate_c_hat_size_kernel( const int A_num_rows,\n                            const int *A_rows,\n                            const int *A_cols,\n                            const int *cf_map,\n                            const bool *s_con,\n                            int *C_hat_offsets )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    s_b_row_ids[threadIdx.x] = 0;\n    __syncthreads();\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id += gridDim.x * NUM_WARPS )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Load A row IDs.\n        int a_col_begin = A_rows[a_row_id  ];\n        int a_col_end   = A_rows[a_row_id + 1];\n        // The number of elements.\n        int count(0);\n\n        // Iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            // For each warp, we have up to 32 rows of B to proceed.\n\n            int num_rows = __popc(vote);\n            for ( int k = 0; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id + 0];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != b_row_id;\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_off_diagonal && s_con[b_col_it] && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    count += __popc( utils::ballot( is_coarse_strongly_connected ) );\n                }\n            }\n        }\n\n        // Store the number of columns in each row.\n        if ( lane_id == 0 )\n        {\n            C_hat_offsets[a_row_id] = count;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // Shared memory to vote.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Load the range of the row.\n        __syncthreads();\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n        __syncthreads();\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // Load the range of the row of B.\n                int b_col_it  = A_rows[uniform_b_row_id + 0];\n                int b_col_end = A_rows[uniform_b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any(b_col_it < b_col_end) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_c_hat_kernel( int A_num_rows,\n                      const int *__restrict A_rows,\n                      const int *__restrict A_cols,\n                      const int *__restrict cf_map,\n                      const bool *__restrict s_con,\n                      const int *__restrict C_hat_start,\n                      int *__restrict C_hat_end,\n                      int *__restrict C_hat,\n                      int *__restrict C_hat_pos,\n                      int gmem_size,\n                      int *g_keys,\n                      int *wk_work_queue,\n                      int *wk_status )\n{\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // Shared memory to vote.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id( );\n    const int lane_id = utils::lane_id( );\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n\n        // Load the range of the row.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_begin = utils::shfl( a_col_tmp, 0 );\n        int a_col_end   = utils::shfl( a_col_tmp, 1 );\n\n        // _iterate over the columns of A to build C_hat.\n        for ( int a_col_it = a_col_begin + lane_id ; utils::any(a_col_it < a_col_end) ; a_col_it += WARP_SIZE )\n        {\n            // Is it an active thread.\n            const bool is_active = a_col_it < a_col_end;\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( is_active )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = is_active && a_col_id != a_row_id;\n            // Is it strongly connected ?\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it fine.\n            bool is_fine = is_off_diagonal && cf_map[a_col_id] == FINE;\n            // Is it isolated.\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Is it a coarse and strongly-connected column.\n            bool is_coarse_strongly_connected = is_strongly_connected && !is_fine && !is_strong_fine;\n            // Push coarse and strongly connected nodes in the set.\n            int item = -1;\n\n            if ( is_coarse_strongly_connected )\n            {\n                item = a_col_id;\n            }\n\n            set.insert( item, wk_status );\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && is_fine;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [bColIt, bColEnd).\n                int uniform_b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    uniform_b_row_id = s_b_row_ids[warp_id * WARP_SIZE + local_k];\n                }\n\n                // Load the range of the row of B.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[uniform_b_row_id + 0];\n                    b_col_end = A_rows[uniform_b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any(b_col_it < b_col_end) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it an off-diagonal element.\n                    is_off_diagonal = b_col_it < b_col_end && b_col_id != uniform_b_row_id;\n                    // Is it a strongly connected node.\n                    is_strongly_connected = is_off_diagonal && s_con[b_col_it];\n                    // Is it a coarse and strongly-connected column.\n                    is_coarse_strongly_connected = is_strongly_connected && (cf_map[b_col_id] != FINE && cf_map[b_col_id] != STRONG_FINE);\n                    // Push coarse and strongly connected nodes in the set.\n                    int b_item = -1;\n\n                    if ( is_coarse_strongly_connected )\n                    {\n                        b_item = b_col_id;\n                    }\n\n                    set.insert( b_item, wk_status );\n                }\n            }\n        }\n\n        int c_col_it = C_hat_start[a_row_id];\n        int count = set.store_with_positions( &C_hat[c_col_it], &C_hat_pos[c_col_it] );\n\n        if ( lane_id == 0 )\n        {\n            C_hat_end[a_row_id] = c_col_it + count;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_inner_sum_kernel( const int A_num_rows,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const Value_type *__restrict A_vals,\n                          const int *__restrict cf_map,\n                          const bool *__restrict s_con,\n                          const int *__restrict C_hat,\n                          const int *__restrict C_hat_pos,\n                          const int *__restrict C_hat_start,\n                          const int *__restrict C_hat_end,\n                          const Value_type *__restrict diag,\n                          const int *__restrict inner_sum_offsets,\n                          Value_type *inner_sum,\n                          const int gmem_size,\n                          int *g_keys,\n                          int *wk_work_queue )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_a_values[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        set.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // And share the value of the diagonal.\n        bool sign_diag = false;\n\n        if ( lane_id == 0 )\n        {\n            sign_diag = sign( diag[a_row_id] );\n        }\n\n        sign_diag = utils::shfl( sign_diag, 0 );\n\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // _columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[a_col_it] && cf_map[a_col_id] == FINE;\n            // Read the associated value from A.\n            Value_type a_value(0);\n\n            if ( is_fine_strongly_connected )\n            {\n                a_value = A_vals[a_col_it];\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_a_values [warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n            // First n_rows threads reload the correct value.\n            a_value = s_a_values[warp_id * WARP_SIZE + lane_id];\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Each thread keeps its own sum.\n                Value_type bottom_sum(0);\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses.\n                int b_col_it  = A_rows[b_row_id  ];\n                int b_col_end = A_rows[b_row_id + 1];\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it needed to compute the bottom sum.\n                    bool is_needed = set.contains( b_col_id ) || b_col_id == a_row_id;\n                    // Read the associated value from A.\n                    Value_type b_value(0);\n\n                    if ( is_needed )\n                    {\n                        b_value = A_vals[b_col_it];\n                    }\n\n                    // Update the sum if needed.\n                    if ( sign_diag != sign( b_value ) )\n                    {\n                        bottom_sum += b_value;\n                    }\n                }\n\n                // Reduce the row to a single value.\n#pragma unroll\n                for ( int offset = WARP_SIZE / 2 ; offset > 0 ; offset >>= 1 )\n                {\n                    int hi = __double2hiint(bottom_sum);\n                    hi = utils::shfl_xor( hi, offset );\n                    int lo = __double2loint(bottom_sum);\n                    lo = utils::shfl_xor( lo, offset );\n                    bottom_sum += __hiloint2double(hi, lo);\n                }\n\n                bottom_sum = utils::shfl( bottom_sum, 0 );\n\n                if ( lane_id == k && bottom_sum != Value_type(0) )\n                {\n                    a_value /= bottom_sum;\n                }\n            }\n\n            // Write the results.\n            if ( lane_id < num_rows )\n            {\n                inner_sum[inner_sum_offset + lane_id] = a_value;\n            }\n\n            inner_sum_offset += num_rows;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< int NUM_THREADS_PER_ROW, typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_inner_sum_kernel( const int A_num_rows,\n                          const int *__restrict A_rows,\n                          const int *__restrict A_cols,\n                          const Value_type *__restrict A_vals,\n                          const int *__restrict cf_map,\n                          const bool *__restrict s_con,\n                          const int *__restrict C_hat,\n                          const int *__restrict C_hat_pos,\n                          const int *__restrict C_hat_start,\n                          const int *__restrict C_hat_end,\n                          const Value_type *__restrict diag,\n                          const int *__restrict inner_sum_offsets,\n                          Value_type *inner_sum,\n                          const int gmem_size,\n                          int *g_keys,\n                          int *wk_work_queue )\n{\n\n    const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n    const int NUM_LOADED_ROWS = WARP_SIZE / NUM_THREADS_PER_ROW;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_a_values[CTA_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // Constants.\n    const int lane_id_div_num_threads = lane_id / NUM_THREADS_PER_ROW;\n    const int lane_id_mod_num_threads = lane_id % NUM_THREADS_PER_ROW;\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_set<int, SMEM_SIZE, 4, WARP_SIZE> set( &s_keys[warp_id * SMEM_SIZE], &g_keys[a_row_id * gmem_size], gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[a_row_id];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // Clear the set.\n        set.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        set.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // And share the value of the diagonal.\n        bool sign_diag = false;\n\n        if ( lane_id == 0 )\n        {\n            sign_diag = sign( diag[a_row_id] );\n        }\n\n        sign_diag = utils::shfl( sign_diag, 0 );\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // _columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id = -1;\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[a_col_it] && cf_map[a_col_id] == FINE;\n            // Read the associated value from A.\n            Value_type a_value(0);\n\n            if ( is_fine_strongly_connected )\n            {\n                a_value = A_vals[a_col_it];\n            }\n\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n                s_a_values [warp_id * WARP_SIZE + dest] = a_value;\n            }\n\n            int num_rows = __popc( vote );\n            // First n_rows threads reload the correct value.\n            a_value = s_a_values[warp_id * WARP_SIZE + lane_id];\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; k += NUM_LOADED_ROWS )\n            {\n                // Each thread keeps its own sum.\n                Value_type bottom_sum(0);\n                int local_k = k + lane_id_div_num_threads;\n                // Is it an active thread.\n                bool is_active_k = local_k < num_rows;\n                // Threads in the warp proceeds columns of B in the range [b_col_it, b_col_end).\n                int b_row_id = -1;\n\n                if ( is_active_k )\n                {\n                    b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k + lane_id_div_num_threads];\n                }\n\n                // TODO: make sure we have better memory accesses.\n                int b_col_it = 0, b_col_end = 0;\n\n                if ( is_active_k )\n                {\n                    b_col_it  = A_rows[b_row_id  ];\n                    b_col_end = A_rows[b_row_id + 1];\n                }\n\n                // _iterate over the range of columns of B.\n                for ( b_col_it += lane_id_mod_num_threads ; utils::any( b_col_it < b_col_end ) ; b_col_it += NUM_THREADS_PER_ROW )\n                {\n                    // The ID of the column.\n                    int b_col_id = -1;\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                    }\n\n                    // Is it needed to compute the bottom sum.\n                    bool is_needed = set.contains( b_col_id ) || b_col_id == a_row_id;\n                    // Read the associated value from A.\n                    Value_type b_value(0);\n\n                    if ( is_needed )\n                    {\n                        b_value = A_vals[b_col_it];\n                    }\n\n                    // Update the sum if needed.\n                    if ( sign_diag != sign( b_value ) )\n                    {\n                        bottom_sum += b_value;\n                    }\n                }\n\n                // Reduce the row to a single value.\n#pragma unroll\n                for ( int offset = NUM_THREADS_PER_ROW / 2 ; offset > 0 ; offset >>= 1 )\n                {\n                    int hi = __double2hiint(bottom_sum);\n                    hi = utils::shfl_down( hi, offset, NUM_THREADS_PER_ROW );\n                    int lo = __double2loint(bottom_sum);\n                    lo = utils::shfl_down( lo, offset, NUM_THREADS_PER_ROW );\n                    bottom_sum += __hiloint2double(hi, lo);\n                }\n\n                bottom_sum = utils::shfl( bottom_sum, lane_id_mod_num_threads * NUM_THREADS_PER_ROW );\n\n                if ( lane_id >= k && lane_id < k + NUM_THREADS_PER_ROW && bottom_sum != Value_type(0) )\n                {\n                    a_value /= bottom_sum;\n                }\n            }\n\n            // Write the results.\n            if ( lane_id < num_rows )\n            {\n                inner_sum[inner_sum_offset + lane_id] = a_value;\n            }\n\n            inner_sum_offset += num_rows;\n        }\n    }\n\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename Value_type, int CTA_SIZE, int SMEM_SIZE, int WARP_SIZE >\n__global__ __launch_bounds__( CTA_SIZE )\nvoid\ncompute_interp_weight_kernel( const int A_num_rows,\n                              const int *__restrict A_rows,\n                              const int *__restrict A_cols,\n                              const Value_type *__restrict A_vals,\n                              const int *__restrict cf_map,\n                              const bool *__restrict s_con,\n                              const int *__restrict C_hat,\n                              const int *__restrict C_hat_pos,\n                              const int *__restrict C_hat_start,\n                              const int *__restrict C_hat_end,\n                              const Value_type *__restrict diag,\n                              const int *__restrict inner_sum_offsets,\n                              const Value_type *__restrict inner_sum,\n                              const int *__restrict P_rows,\n                              int *P_cols,\n                              Value_type *P_vals,\n                              const int gmem_size,\n                              int *g_keys,\n                              Value_type *g_vals,\n                              int *wk_work_queue )\n{\n    \n    const int NUM_WARPS = CTA_SIZE / 32;\n    // The hash keys stored in shared memory.\n    __shared__ int s_keys[NUM_WARPS * SMEM_SIZE];\n    // A shared location where threads propose a row of B to load.\n    __shared__ volatile int s_b_row_ids[CTA_SIZE];\n    // A shared location where threads propose a value.\n    __shared__ volatile Value_type s_aki[NUM_WARPS];\n    // The hash values stored in shared memory.\n    __shared__ Value_type s_vals[NUM_WARPS * SMEM_SIZE];\n    // The coordinates of the thread inside the CTA/warp.\n    const int warp_id = utils::warp_id();\n    const int lane_id = utils::lane_id();\n    // First threads load the row IDs of A needed by the CTA...\n    int a_row_id = blockIdx.x * NUM_WARPS + warp_id;\n    // Create local storage for the set.\n    Hash_map<int, Value_type, SMEM_SIZE, 4, WARP_SIZE> map( &s_keys[warp_id * SMEM_SIZE],\n            &g_keys[a_row_id * gmem_size],\n            &s_vals[warp_id * SMEM_SIZE],\n            &g_vals[a_row_id * gmem_size],\n            gmem_size );\n    // Loop over rows of A.\n    for ( ; a_row_id < A_num_rows ; a_row_id = get_work( wk_work_queue, warp_id ) )\n    {\n        int coarse_fine_id = cf_map[a_row_id];\n\n        // Skip coarse rows.\n        if ( coarse_fine_id >= 0 )\n        {\n            if ( lane_id == 0 )\n            {\n                int p_row_it = P_rows[a_row_id];\n                P_cols[p_row_it] = coarse_fine_id;\n                P_vals[p_row_it] = Value_type( 1 );\n            }\n\n            continue;\n        }\n\n        if ( coarse_fine_id == STRONG_FINE )\n        {\n            continue;\n        }\n\n        // Clear the table.\n        map.clear();\n        // Rebuild C_hat.\n        int c_hat_it  = C_hat_start[a_row_id];\n        int c_hat_end = C_hat_end  [a_row_id];\n        map.load( c_hat_end - c_hat_it, &C_hat[c_hat_it], &C_hat_pos[c_hat_it] );\n        // Load A row IDs.\n        int a_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            a_col_tmp = A_rows[a_row_id + lane_id];\n        }\n\n        int a_col_it  = utils::shfl( a_col_tmp, 0 );\n        int a_col_end = utils::shfl( a_col_tmp, 1 );\n\n        // The offset in the inner sum table.\n        int inner_sum_offset = inner_sum_offsets[a_row_id];\n        // Weak value.\n        Value_type weak(0), sum(0);\n\n        // Iterate over the columns of A.\n        for ( a_col_it += lane_id ; utils::any( a_col_it < a_col_end ) ; a_col_it += WARP_SIZE )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int a_col_id(-1);\n            Value_type a_value(0);\n\n            if ( a_col_it < a_col_end )\n            {\n                a_col_id = A_cols[a_col_it];\n                a_value  = A_vals[a_col_it];\n            }\n\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = a_col_it < a_col_end && a_col_id != a_row_id;\n            // Is it a strongly-connected column.\n            bool is_strongly_connected = is_off_diagonal && s_con[a_col_it];\n            // Is it a weakly connected node.\n            bool is_weakly_connected = is_off_diagonal && !is_strongly_connected;\n            // Is isolated\n            bool is_strong_fine = is_off_diagonal && cf_map[a_col_id] == STRONG_FINE;\n            // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n            bool is_in_c_hat = map.update( a_col_id, a_value );\n\n            // Update the weak value.\n            if ( is_weakly_connected && !is_in_c_hat && !is_strong_fine)\n            {\n                weak += a_value;\n            }\n\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_strongly_connected && cf_map[a_col_id] == FINE;\n            // We collect fine and strongly-collected columns.\n            int vote = utils::ballot( is_fine_strongly_connected );\n            int dest = __popc( vote & utils::lane_mask_lt() );\n\n            if ( is_fine_strongly_connected )\n            {\n                s_b_row_ids[warp_id * WARP_SIZE + dest] = a_col_id;\n            }\n\n            utils::syncwarp();\n\n            int num_rows = __popc( vote );\n            // We pre-load inner sums.\n            sum = Value_type(0);\n\n            if ( lane_id < num_rows )\n            {\n                sum = inner_sum[inner_sum_offset + lane_id];\n            }\n\n            inner_sum_offset += num_rows;\n\n            // For each warp, we have up to 32 rows of B to proceed.\n            for ( int k = 0 ; k < num_rows ; ++k )\n            {\n                // Threads in the warp proceeds columns of B in the range [b_col_it, bCol_end).\n                int b_row_id = s_b_row_ids[warp_id * WARP_SIZE + k];\n                // TODO: make sure we have better memory accesses. b_colBegin rather than bCol_it because we iterate twice.\n                int b_col_begin = A_rows[b_row_id  ];\n                int b_col_end   = A_rows[b_row_id + 1];\n                // Diagonal element.\n                Value_type b_diag = diag[b_row_id];\n\n                // Set s_aki to 0 because a_row_id may not appear in a row.\n                if ( lane_id == 0 )\n                {\n                    s_aki[warp_id] = Value_type(0);\n                }\n\n                utils::syncwarp();\n\n                // Load the kth inner sum.\n                Value_type uniform_val = utils::shfl( sum, k );\n\n                // _iterate over the range of columns of B.\n                for ( int b_col_it = b_col_begin + lane_id ; utils::any( b_col_it < b_col_end ) ; b_col_it += WARP_SIZE )\n                {\n                    // The ID of the column.\n                    int b_col_id(-1);\n                    Value_type b_value(0);\n\n                    if ( b_col_it < b_col_end )\n                    {\n                        b_col_id = A_cols[b_col_it];\n                        b_value  = A_vals[b_col_it];\n                    }\n\n                    // Update bValue if needed.\n                    if ( sign( b_diag ) == sign( b_value ) )\n                    {\n                        b_value = Value_type(0);\n                    }\n\n                    // See if we have found a_row_id in the row. Only one thread can evaluate to true.\n                    if ( a_row_id == b_col_id )\n                    {\n                        s_aki[warp_id] = b_value;\n                    }\n\n                    // Update C_hat values. If the value is not in C_hat it will be skipped (true parameter).\n                    map.update( b_col_id, b_value * uniform_val );\n                }\n\n                utils::syncwarp();\n\n                // The thread k updates the sum.\n                if ( lane_id == 0 )\n                {\n                    weak += s_aki[warp_id] * uniform_val;\n                }\n            }\n        }\n\n        // We're done with that row of A. We compute D.\n#pragma unroll\n\n        for ( int mask = WARP_SIZE / 2 ; mask > 0 ; mask >>= 1 )\n        {\n            weak += utils::shfl_xor( weak, mask );\n        }\n\n        if ( lane_id == 0 )\n        {\n            weak += diag[a_row_id];\n            sum   = Value_type(-1) / weak;\n        }\n\n        sum = utils::shfl( sum, 0 );\n\n        int p_col_tmp = -1;\n\n        if ( lane_id < 2 )\n        {\n            p_col_tmp = P_rows[a_row_id + lane_id];\n        }\n\n        int p_col_it  = utils::shfl( p_col_tmp, 0 );\n        int p_col_end = utils::shfl( p_col_tmp, 1 );\n\n        map.store_map_keys_scale_values( p_col_end - p_col_it, cf_map, &P_cols[p_col_it], sum, &P_vals[p_col_it] );\n    }\n}\n\n\n} // namespace distance2\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate< typename IndexType, int kCtaSize >\n__global__\nvoid\ncalculateInnerSumStorageKernel( const IndexType *A_rows,\n                                const IndexType *A_cols,\n                                const int A_num_rows,\n                                const int *cf_map,\n                                const bool *s_con,\n                                int *innerSum_offsets )\n{\n    const int nWarps = kCtaSize / 32;\n    // The coordinates of the thread inside the CTA/warp.\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    // Loop over rows of A.\n    for ( int aRowId = blockIdx.x * nWarps + warpId ; aRowId < A_num_rows ; aRowId += gridDim.x * nWarps )\n    {\n        // Skip coarse rows.\n        int coarse_fine_id = cf_map[aRowId];\n\n        if ( coarse_fine_id >= 0 || coarse_fine_id == STRONG_FINE)\n        {\n            continue;\n        }\n\n        // The sum for each thread.\n        int sum = 0;\n        // Load A row IDs.\n        int aColIt  = A_rows[aRowId  ];\n        int aColEnd = A_rows[aRowId + 1];\n\n        // Iterate over the columns of A.\n        for ( aColIt += laneId ; utils::any( aColIt < aColEnd ) ; aColIt += 32 )\n        {\n            // Columns of A maps to rows of B. Each thread of the warp loads its A-col/B-row ID.\n            int aColId = aColIt < aColEnd ? A_cols[aColIt] : -1;\n            // Is it an off-diagonal element.\n            bool is_off_diagonal = aColIt < aColEnd && aColId != aRowId;\n            // Is it a fine and strongly-connected column.\n            bool is_fine_strongly_connected = is_off_diagonal && s_con[aColIt] && cf_map[aColId] == FINE;\n            // Update the sum.\n            sum += __popc( utils::ballot( is_fine_strongly_connected ) );\n        }\n\n        // Update the value.\n        if ( laneId == 0 )\n        {\n            innerSum_offsets[aRowId] = sum;\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*\n * grab the diagonal of the matrix\n */\ntemplate <typename IndexType, typename ValueType>\n__global__\nvoid getDiagonalKernel(const IndexType *offsets, const IndexType *column_indices,\n                       const ValueType *values, const IndexType numRows, ValueType *diagonal)\n{\n    for (int tIdx = threadIdx.x + blockDim.x * blockIdx.x; tIdx < numRows; tIdx += blockDim.x * gridDim.x)\n    {\n        const int offset = offsets[tIdx];\n        const int numj = offsets[tIdx + 1] - offset;\n\n        for (int j = offset; j < offset + numj; j++)\n        {\n            int jcol = column_indices[j];\n\n            if (tIdx == jcol)\n            {\n                diagonal[tIdx] = values[j];\n            }\n        }\n    }\n}\n\n/*************************************************************************\n * Implementing Extended+i algorithm from \\S 4.5 of:\n * \"Distance-two interpolation for parallel algebraic multigrid\"\n * Reference [4] on wiki\n ************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Distance2_Interpolator(AMG_Config &cfg, const std::string &cfg_scope) : Base(cfg, cfg_scope)\n{}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nDistance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::~Distance2_Interpolator()\n{}\n\nenum { WARP_SIZE = 32, SMEM_SIZE = 128 };\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nvoid Distance2_Interpolator<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::generateInterpolationMatrix_1x1(Matrix_d &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix_d &P)\n{\n    nvtxRange gim_nr(__func__);\n\n    const int blockSize = 256;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get raw pointers to data\n    const IndexType *Aoffsets = A.row_offsets.raw();\n    const IndexType *Acolumn_indices = A.col_indices.raw();\n    const ValueType *Avalues = A.values.raw();\n    const IndexType Anum_rows = (int) A.get_num_rows();\n\n    hipDeviceProp_t props = getDeviceProperties();\n    int grid_size = (props.major >= 7) ? 1024 : 128;\n    int gmem_size = (props.major >= 7) ? 512 : 2048;\n\n    Hash_Workspace<TConfig_d, int> exp_wk(true, grid_size, 8, gmem_size);\n\n    IntVector C_hat_start( A.get_num_rows() + 1, 0 ), C_hat_end( A.get_num_rows() + 1, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int work_offset = grid_size * NUM_WARPS;\n        hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n        cudaCheckError();\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n\n        if ( avg_nz_per_row < 16 )\n        {\n            distance2::estimate_c_hat_size_kernel< 8, CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n                cudaCheckError();\n        }\n        else\n        {\n            distance2::estimate_c_hat_size_kernel<CTA_SIZE, WARP_SIZE> <<< 2048, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat_start.raw());\n        }\n\n        cudaCheckError();\n    }\n    // Compute row offsets.\n    thrust_wrapper::exclusive_scan<AMGX_device>( C_hat_start.begin( ), C_hat_start.end( ), C_hat_start.begin( ) );\n    cudaCheckError();\n    // Allocate memory to store columns/values.\n    int nVals = C_hat_start[C_hat_start.size() - 1];\n    IntVector C_hat( nVals ), C_hat_pos( nVals );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        int attempt = 0;\n\n        for ( bool done = false ; !done && attempt < 10 ; ++attempt )\n        {\n            // Double the amount of GMEM (if needed).\n            if ( attempt > 0 )\n            {\n                exp_wk.expand();\n            }\n\n            // Reset the status. TODO: Launch async copies.\n            int status = 0;\n            hipMemcpy( exp_wk.get_status(), &status, sizeof(int), hipMemcpyHostToDevice );\n            cudaCheckError();\n            // Compute the set C_hat.\n            int work_offset = grid_size * NUM_WARPS;\n            hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n            cudaCheckError();\n\n            // Run the computation.\n            if ( avg_nz_per_row < 16 )\n            {\n                distance2::compute_c_hat_kernel< 8, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n                    cudaCheckError();\n            }\n            else\n            {\n                distance2::compute_c_hat_kernel<CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                    A.get_num_rows(),\n                    A.row_offsets.raw(),\n                    A.col_indices.raw(),\n                    cf_map.raw(),\n                    s_con.raw(),\n                    C_hat_start.raw(),\n                    C_hat_end.raw(),\n                    C_hat.raw(),\n                    C_hat_pos.raw(),\n                    exp_wk.get_gmem_size(),\n                    exp_wk.get_keys(),\n                    exp_wk.get_work_queue(),\n                    exp_wk.get_status() );\n            }\n\n            cudaCheckError();\n            // Read the result from count_non_zeroes.\n            hipMemcpy( &status, exp_wk.get_status(), sizeof(int), hipMemcpyDeviceToHost );\n            cudaCheckError();\n            done = status == 0;\n        }\n    }\n    // get pointers to data\n    int *C_hat_ptr = C_hat.raw();\n    int *C_hat_start_ptr = C_hat_start.raw();\n    int *C_hat_end_ptr = C_hat_end.raw();\n    // modify Coarse/Fine map array to have the column index of the new interpolation matrix\n    // for evry coarse point\n    int coarsePoints;\n    DistributedArranger<TConfig_d> *prep;\n\n    if (A.is_matrix_distributed())\n    {\n        prep = new DistributedArranger<TConfig_d>;\n        int num_owned_fine_pts = A.get_num_rows();\n        int num_owned_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.begin() + num_owned_fine_pts, is_non_neg());\n        cudaCheckError();\n        int num_halo_coarse_pts = thrust_wrapper::count_if<AMGX_device>(cf_map.begin() + num_owned_fine_pts, cf_map.end(), is_non_neg());\n        cudaCheckError();\n        coarsePoints = num_owned_coarse_pts + num_halo_coarse_pts;\n        // Using the number of owned_coarse_pts (number of rows of Ac), initialize P manager\n        // This will set the part_offsets array, index_range, halo_offsets[0] value\n        prep->initialize_manager(A, P, num_owned_coarse_pts);\n    }\n    else\n    {\n        coarsePoints = (int) thrust_wrapper::count_if<AMGX_device>(cf_map.begin(), cf_map.end(), is_non_neg());\n        cudaCheckError();\n    }\n\n    // mark as 1 if a coarse point\n    int size_one_ring;\n\n    if (!A.is_matrix_singleGPU())\n    {\n        int offset;\n        A.getOffsetAndSizeForView(FULL, &offset, &size_one_ring);\n    }\n    else\n    {\n        size_one_ring = A.get_num_rows();\n    }\n\n    int numBlocksOneRing = std::min( 4096, (int) (size_one_ring + blockSize - 1) / blockSize );\n    int numBlocks = std::min( 4096, (int) (A.get_num_rows() + blockSize - 1) / blockSize );\n    // count the number of non-zeros in the interpolation matrix\n    IntVector nonZeroOffsets(A.get_num_rows() + 1);\n    IntVector nonZerosPerRow(A.get_num_rows());\n    int *nonZerosPerRow_ptr = nonZerosPerRow.raw();\n\n    if (A.get_num_rows() > 0)\n    {\n        nonZerosPerRowKernel <<< numBlocks, blockSize>>>(Anum_rows, cf_map.raw(), C_hat_start_ptr, C_hat_end_ptr,\n                nonZerosPerRow_ptr);\n        cudaCheckError();\n    }\n\n    // get total with a reduction\n    int nonZeros = thrust_wrapper::reduce<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end());\n    cudaCheckError();\n    // get the offsets with an exclusive scan\n    thrust_wrapper::exclusive_scan<AMGX_device>(nonZerosPerRow.begin(), nonZerosPerRow.end(), nonZeroOffsets.begin());\n    cudaCheckError();\n    nonZeroOffsets[A.get_num_rows()] = nonZeros;\n    // resize P\n    P.resize(0, 0, 0, 1);\n    P.addProps(CSR);\n    P.resize(A.get_num_rows(), coarsePoints, nonZeros, 1);\n    IndexType *Poffsets = P.row_offsets.raw();\n    IndexType *Pcolumn_indices = P.col_indices.raw();\n    ValueType *Pvalues = P.values.raw();\n    // copy nonzero offsets to the P matrix\n    amgx::thrust::copy(nonZeroOffsets.begin(), nonZeroOffsets.end(), P.row_offsets.begin());\n    cudaCheckError();\n    // grab the diagonal terms\n    VVector diag(size_one_ring);\n    ValueType *diag_ptr = diag.raw();\n\n    if (A.get_num_rows() > 0)\n    {\n        find_diag_kernel_indexed_dia <<< numBlocksOneRing, blockSize>>>(\n            size_one_ring,\n            A.diag.raw(),\n            A.values.raw(),\n            diag.raw());\n    }\n\n    cudaCheckError();\n    IntVector innerSumOffset( A.get_num_rows() + 1, 0 );\n\n    if (A.get_num_rows() > 0)\n    {\n        const int kCtaSize = 256;\n        const int nWarps = kCtaSize / 32;\n        const int grid_size = std::min( 4096, ( Anum_rows + nWarps - 1 ) / nWarps );\n        calculateInnerSumStorageKernel<IndexType, kCtaSize> <<< grid_size, kCtaSize>>>(\n            Aoffsets,\n            Acolumn_indices,\n            Anum_rows,\n            cf_map.raw(),\n            s_con.raw(),\n            innerSumOffset.raw() );\n        cudaCheckError();\n    }\n\n    // get the offsets\n    thrust_wrapper::exclusive_scan<AMGX_device>( innerSumOffset.begin(), innerSumOffset.end(), innerSumOffset.begin() );\n    cudaCheckError();\n    // assign memory & get pointer\n    int numInnerSum = innerSumOffset[A.get_num_rows()];\n    VVector innerSum( numInnerSum, 0 );\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        // Compute the set C_hat.\n        int work_offset = grid_size * NUM_WARPS;\n        hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n        cudaCheckError();\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n        {\n            distance2::compute_inner_sum_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n                A.get_num_rows(),\n                A.row_offsets.raw(),\n                A.col_indices.raw(),\n                A.values.raw(),\n                cf_map.raw(),\n                s_con.raw(),\n                C_hat.raw(),\n                C_hat_pos.raw(),\n                C_hat_start.raw(),\n                C_hat_end.raw(),\n                diag.raw(),\n                innerSumOffset.raw(),\n                innerSum.raw(),\n                exp_wk.get_gmem_size(),\n                exp_wk.get_keys(),\n                exp_wk.get_work_queue());\n        }\n        cudaCheckError();\n    }\n    {\n        const int CTA_SIZE  = 256;\n        const int NUM_WARPS = CTA_SIZE / WARP_SIZE;\n        int avg_nz_per_row = (A.get_num_rows() == 0) ? 0 : A.get_num_nz() / A.get_num_rows();\n        // Compute the set C_hat.\n        int work_offset = grid_size * NUM_WARPS;\n        hipMemcpy( exp_wk.get_work_queue(), &work_offset, sizeof(int), hipMemcpyHostToDevice );\n        cudaCheckError();\n        // Run the computation.\n        typedef typename MatPrecisionMap<t_matPrec>::Type Value_type;\n        distance2::compute_interp_weight_kernel<Value_type, CTA_SIZE, SMEM_SIZE, WARP_SIZE> <<< grid_size, CTA_SIZE>>>(\n            A.get_num_rows(),\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            A.values.raw(),\n            cf_map.raw(),\n            s_con.raw(),\n            C_hat.raw(),\n            C_hat_pos.raw(),\n            C_hat_start.raw(),\n            C_hat_end.raw(),\n            diag.raw(),\n            innerSumOffset.raw(),\n            innerSum.raw(),\n            P.row_offsets.raw(),\n            P.col_indices.raw(),\n            P.values.raw(),\n            exp_wk.get_gmem_size(),\n            exp_wk.get_keys(),\n            exp_wk.get_vals(),\n            exp_wk.get_work_queue());\n        cudaCheckError();\n    }\n\n    if (A.is_matrix_distributed())\n    {\n        //TODO: cf_map_global could be smaller (only contain halo nodes)\n        I64Vector_d cf_map_global(cf_map.size());\n        int num_owned_fine_pts = P.get_num_rows();\n        int my_rank = A.manager->global_id();\n        const int cta_size = 128;\n        const int grid_size = std::min( 4096, (num_owned_fine_pts + cta_size - 1) / cta_size);\n\n        // Write the global coarse index of all the owned fine points (-1 if not a coarse point)\n        if (num_owned_fine_pts > 0)\n        {\n            createCfMapGlobal <<< grid_size, cta_size>>>(cf_map.raw(), cf_map_global.raw(), P.manager->part_offsets_h[my_rank], num_owned_fine_pts);\n            cudaCheckError();\n        }\n\n        // Exchange the cf_map_global so that we know the coarse global id of halo nodes\n        cf_map_global.dirtybit = 1;\n        A.manager->exchange_halo_2ring(cf_map_global, cf_map_global.tag);\n        I64Vector_d P_col_indices_global(P.col_indices.size());\n\n        if (num_owned_fine_pts > 0)\n        {\n            fill_P_global_col_indices_kernel<16, int> <<< grid_size, cta_size>>>(P.row_offsets.raw(), C_hat_start.raw(), C_hat_end.raw(), C_hat.raw(), cf_map_global.raw(), P_col_indices_global.raw(), num_owned_fine_pts);\n            cudaCheckError();\n        }\n\n        // Now that we know the global col indices of P, we can find:\n        // the list of neighbors, create the B2L_maps for P, create local numbering for halo nodes, create local_to_global_map array, etc...\n        prep->initialize_manager_from_global_col_indices(P, P_col_indices_global);\n        prep->createRowsLists(P, true);\n        delete prep;\n    }\n}\n\ntemplate< class T_Config>\nvoid Distance2_InterpolatorBase<T_Config>::generateInterpolationMatrix(Matrix<T_Config> &A,\n        IntVector &cf_map,\n        BVector &s_con,\n        IntVector &scratch,\n        Matrix<T_Config> &P)\n{\n    P.set_initialized(0);\n    ViewType oldView = A.currentView();\n    A.setView(OWNED);\n\n    // If multi-GPU do a 2-ring exchange of cf_map since this is a distance two interpolator\n    if (A.get_block_size() == 1)\n    {\n        generateInterpolationMatrix_1x1(A, cf_map, s_con, scratch, P);\n    }\n    else\n    {\n        FatalError(\"Unsupported dimensions for distance2 interpolator\", AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n    }\n\n    A.setView(oldView);\n    P.set_initialized(1);\n}\n\n#define AMGX_CASE_LINE(CASE) template class Distance2_InterpolatorBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Distance2_Interpolator<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n", "source": "/kaggle/working/amgx/src/classical/interpolators/distance2.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <fstream>\n#include <cutil.h>\n#include <types.h>\n#include <classical/strength/ahat.h>\n#include <classical/strength/all.h>\n#include <float.h>\n#include <specific_spmv.h>\n#include <sm_utils.inl>\n\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/transform.h>\n#include <thrust/logical.h>\n\n/*\n* Note:\n* This implementation assumes that off-diag entries all have the opposite sign\n* comparing to the diag entry. This is true for most practical cases.\n* It would even work if the offending off-diag entries are just a few.\n* But if there are many off-diag entries violate this assumption,\n* the interpolation based on this strength would be inaccurate.\n* This is explained in \"Intro to Algebraic multigrid\" by K. Stuben.\n*/\n\n\nnamespace amgx\n{\n\ntemplate< typename T_Config >\nStrength_BaseBase<T_Config>::Strength_BaseBase(AMG_Config &cfg,\n        const std::string &cfg_scope)\n{\n    alpha = cfg.AMG_Config::template getParameter<double>(\"strength_threshold\", cfg_scope);\n    m_use_opt_kernels = cfg.AMG_Config::template getParameter<int>(\"use_opt_kernels\", \"default\");\n}\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ __forceinline__\nstatic float ourHash(const int i)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return (a ^ 0x4a51e590) / (float)UINT_MAX;\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights\n* Described in \\S 4.1 of:\n* \"Reducing complexity in parallel algebraic multigrid preconditioners\"\n*\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid\nStrength_Base<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_h &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    bool compute_row_sum = (max_row_sum < 1.0);\n    VVector sums_ptr;\n\n    // get the (normalised) row sums\n    if (compute_row_sum)\n    {\n        sums_ptr.resize(A.get_num_rows());\n        weightedRowSum(A, sums_ptr);\n        cudaCheckError();\n    }\n\n    // get min/max off-diag, depending on sign of diagonal\n    for (int row = 0; row < A.get_num_rows(); row++)\n    {\n        ValueType diag(0), minVal(0), maxVal(0);\n        ValueType row_sum = compute_row_sum ? sums_ptr[row] : -1;\n        int rowEnd = A.row_offsets[row + 1];\n\n        for (int j = A.row_offsets[row]; j < rowEnd; j++)\n        {\n            int col = A.col_indices[j];\n            ValueType val = A.values[j];\n\n            if (col == row)\n            {\n                diag = val;\n            }\n            else\n            {\n                minVal = std::min(minVal, val);\n                maxVal = std::max(maxVal, val);\n            }\n        }\n\n        //set the threshold for being strongly connected\n        ValueType threshold = (diag < 0) ? maxVal : minVal;\n        threshold *= this->alpha;\n\n        //initialize the weight to a small random number\n        if (A.is_matrix_singleGPU())\n        {\n            weights[row] += ourHash(row);\n        }\n        else\n        {\n            weights[row] += ourHash((int)A.manager->base_index() + row);\n        }\n\n        // sum the column of S - # of points that strongly connect to me\n        for (int j = A.row_offsets[row]; j < rowEnd; j++)\n        {\n            bool is_strongly_connected = false;\n\n            if (compute_row_sum && row_sum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n                is_strongly_connected =\n                    this->strongly_connected(A.values[j], threshold, diag);\n\n            int col = A.col_indices[j];\n            s_con[j] = (col != row) && is_strongly_connected;\n            weights[A.col_indices[j]] += s_con[j] ? 1. : 0.;\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid\nStrength_Base<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeWeights_1x1(Matrix_h &S,\n                   FVector &weights)\n{\n    for (int row = 0; row < S.get_num_rows(); row++)\n    {\n        //initialize the weight to a small random number\n        if (S.is_matrix_singleGPU())\n        {\n            weights[row] += ourHash(row);\n        }\n        else\n        {\n            weights[row] += ourHash(S.manager->base_index() + row);\n        }\n\n        int rowEnd = S.row_offsets[row + 1];\n\n        for (int j = S.row_offsets[row]; j < rowEnd; j++)\n        {\n            int col = S.col_indices[j];\n\n            if (col != row) { weights[col] += 1.; }\n        }\n    }\n}\n\n\ntemplate <typename ValueType>\n__device__\nbool stronglyConnectedAHat(ValueType value, ValueType threshold, ValueType diag)\n{\n    return (diag < ValueType(0)) ? value > threshold : value < threshold;\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsKernel( const IndexType *A_rows,\n        const IndexType *A_cols,\n        const ValueType *A_vals,\n        int A_num_rows,\n        bool *s_con,\n        float *weights,\n        ValueType alpha,\n        ValueType *row_sum,\n        const double max_row_sum,\n        int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    __shared__ volatile ValueType smem[kCtaSize];\n    __shared__ volatile ValueType s_diag[num_warps];\n    __shared__ volatile ValueType s_threshold[num_warps];\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        ValueType minVal(0), maxVal(0);\n\n        if ( laneId == 0 ) // Reset the diagonal\n        {\n            s_diag[warpId] = ValueType(0);\n        }\n\n        utils::syncwarp();\n\n        // Row sum\n        ValueType rowSum = -1.0;\n\n        if (max_row_sum < 1.0) { rowSum = row_sum[aRowId]; }\n\n        // get diagonal, min/max off-diagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n\n            if ( aColId == aRowId ) // only one thread evaluates to true.\n            {\n                s_diag[warpId] = aValue;\n            }\n\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if ( is_off_diagonal )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        // init weights[] with a random number\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        utils::syncwarp();\n\n        // Big assumption: diag and off-diag always have the opposite sign.\n        // If diag entry is negative, then all off-diag entries must be positive.\n        // This means max off-diag is to be used to compute the threshold.\n        // If diag entry is positve, the min off-diag is used instead.\n        if ( s_diag[warpId] < ValueType(0) )\n        {\n            smem[threadIdx.x] = maxVal;\n\n            utils::syncwarp();\n\n#pragma unroll\n            for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            {\n                if ( laneId < offset )\n                {\n                    smem[threadIdx.x] = maxVal = max( maxVal, smem[threadIdx.x + offset] );\n                }\n                utils::syncwarp();\n            }\n        }\n        else\n        {\n            smem[threadIdx.x] = minVal;\n\n            utils::syncwarp();\n\n#pragma unroll\n            for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            {\n                if ( laneId < offset )\n                {\n                    smem[threadIdx.x] = minVal = min( minVal, smem[threadIdx.x + offset] );\n                }\n                utils::syncwarp();\n            }\n        }\n\n        if ( laneId == 0 )\n        {\n            // If laneId=0, then maxVal or minVal is in smem[threadIdx.x].\n            s_threshold[warpId] = smem[threadIdx.x] * alpha;\n        }\n\n        utils::syncwarp();\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_strongly_connected = false;\n\n            if (max_row_sum < 1.0 && rowSum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n            {\n                bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n                is_strongly_connected = is_off_diagonal &&\n                                        stronglyConnectedAHat( aValue, s_threshold[warpId], s_diag[warpId] );\n            }\n\n            if ( is_strongly_connected && aRowIt < aRowEnd && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            if ( aRowIt < aRowEnd )\n            {\n                s_con[aRowIt] = is_strongly_connected;\n            }\n        }\n    }\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsKernel_opt( const IndexType *A_rows,\n        const IndexType *A_cols,\n        const ValueType *A_vals,\n        int A_num_rows,\n        bool *s_con,\n        float *weights,\n        ValueType alpha,\n        ValueType *row_sum,\n        const double max_row_sum,\n        int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    int aRowId = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (aRowId < A_num_rows)\n    {\n        ValueType minVal(0), maxVal(0);\n\n        ValueType diag = ValueType(0);\n\n        utils::syncwarp();\n\n        // Row sum\n        ValueType rowSum = -1.0;\n\n        if (max_row_sum < 1.0) { rowSum = row_sum[aRowId]; }\n\n        // get diagonal, min/max off-diagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin; aRowIt < aRowEnd ; ++aRowIt)\n        {\n            IndexType aColId = A_cols[aRowIt];\n            ValueType aValue = A_vals[aRowIt];\n\n            if ( aColId == aRowId ) // only one thread evaluates to true.\n            {\n                diag = aValue;\n            }\n\n            if ( aColId != aRowId )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        if ( singleGPU )\n        {\n            atomicAdd(&weights[aRowId], ourHash(aRowId));\n        }\n        else\n        {\n            atomicAdd(&weights[aRowId], ourHash( (int) base_index + aRowId));\n        }\n\n        utils::syncwarp();\n\n        // Big assumption: diag and off-diag always have the opposite sign.\n        // If diag entry is negative, then all off-diag entries must be positive.\n        // This means max off-diag is to be used to compute the threshold.\n        // If diag entry is positve, the min off-diag is used instead.\n        ValueType threshold;\n        if ( diag < ValueType(0) )\n        {\n            threshold = maxVal*alpha;\n        }\n        else\n        {\n            threshold = minVal*alpha;\n        }\n\n        utils::syncwarp();\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin;  aRowIt < aRowEnd ; ++aRowIt)\n        {\n            IndexType aColId = A_cols[aRowIt];\n            ValueType aValue = A_vals[aRowIt];\n            bool is_strongly_connected = false;\n\n            if (max_row_sum < 1.0 && rowSum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n            {\n                bool is_off_diagonal = aColId != aRowId;\n                is_strongly_connected = is_off_diagonal &&\n                                        stronglyConnectedAHat( aValue, threshold, diag );\n            }\n\n            if ( is_strongly_connected && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            s_con[aRowIt] = is_strongly_connected;\n        }\n    }\n}\n\n\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeWeightsKernel( const IndexType *A_rows,\n                           const IndexType *A_cols,\n                           int A_num_rows,\n                           float *weights,\n                           int64_t base_index)\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if (is_off_diagonal)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n        }\n    }\n}\n\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Base<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_d &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const ValueType *values_ptr = A.values.raw();\n    bool *s_con_ptr = s_con.raw();\n    float *weights_ptr = weights.raw();\n    bool compute_row_sum = (max_row_sum < 1.0);\n\n    if (A.get_num_rows() == 0) { compute_row_sum = false; }\n\n    VVector sums_ptr;\n\n    if (compute_row_sum)\n    {\n        sums_ptr.resize(A.get_num_rows());\n        weightedRowSum(A, sums_ptr);\n        cudaCheckError();\n    }\n\n    // choose a blocksize. Use 1 warp per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = std::min( 4096, (int) (A.get_num_rows() + numWarps - 1) / numWarps );\n\n    if (A.get_num_rows() > 0)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            // One row per thread\n            const int blockSize = 128;\n            const int numBlocks = A.get_num_rows() / blockSize + 1;\n\n            if (A.is_matrix_singleGPU()) {\n                computeStrongConnectionsAndWeightsKernel_opt<IndexType, ValueType, blockSize, true>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            0);\n                cudaCheckError();\n            }\n            else {\n                computeStrongConnectionsAndWeightsKernel_opt<IndexType, ValueType, blockSize, false>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            A.manager->base_index());\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            const int blockSize = 256;\n            const int numWarps  = blockSize / 32;\n            const int numBlocks = min( 4096, (int) (A.get_num_rows() + numWarps - 1) / numWarps );\n\n            if (A.is_matrix_singleGPU()) {\n                computeStrongConnectionsAndWeightsKernel<IndexType, ValueType, blockSize, true>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            0);\n                cudaCheckError();\n            }\n            else {\n                computeStrongConnectionsAndWeightsKernel<IndexType, ValueType, blockSize, false>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            A.manager->base_index());\n                cudaCheckError();\n            }\n        }\n    }\n\n    if (!A.is_matrix_singleGPU() && A.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        A.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Base<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeWeights_1x1(Matrix_d &S,\n                   FVector &weights)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = S.row_offsets.raw();\n    const IndexType *column_indices_ptr = S.col_indices.raw();\n    float *weights_ptr = weights.raw();\n    // choose a blocksize. Use 1 thread per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = std::min( 4096, (int) (S.get_num_rows() + numWarps - 1) / numWarps );\n    cudaDeviceSynchronize();\n    cudaCheckError();\n\n    // call the CUDA kernel\n    if (S.is_matrix_singleGPU())\n        computeWeightsKernel<IndexType, ValueType, blockSize, true>\n        <<< numBlocks, blockSize>>>(\n            S.row_offsets.raw(),\n            S.col_indices.raw(),\n            S.get_num_rows(),\n            weights.raw(),\n            0);\n    else\n        computeWeightsKernel<IndexType, ValueType, blockSize, false>\n        <<< numBlocks, blockSize>>>(\n            S.row_offsets.raw(),\n            S.col_indices.raw(),\n            S.get_num_rows(),\n            weights.raw(),\n            S.manager->base_index());\n\n    cudaCheckError();\n\n    if (!S.is_matrix_singleGPU() && S.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        S.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\ntemplate<class T_Config>\nvoid Strength_BaseBase<T_Config>::\ncomputeStrongConnectionsAndWeights(Matrix<T_Config> &A,\n                                   BVector &s_con,\n                                   FVector &weights,\n                                   const double max_row_sum\n                                  )\n{\n    if (A.get_block_size() == 1)\n    {\n        computeStrongConnectionsAndWeights_1x1(A, s_con, weights, max_row_sum);\n    }\n    else\n        FatalError(\"Unsupported block size for strong connections\",\n                   AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate<class T_Config>\nvoid Strength_BaseBase<T_Config>::computeWeights(Matrix<T_Config> &S,\n        FVector &weights\n                                                )\n{\n    if (S.get_block_size() == 1)\n    {\n        computeWeights_1x1(S, weights);\n    }\n    else\n        FatalError(\"Unsupported block size for strong connections\",\n                   AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Strength_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Strength_BaseBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <fstream>\n#include <cutil.h>\n#include <types.h>\n#include <classical/strength/ahat.h>\n#include <classical/strength/all.h>\n#include <float.h>\n#include <specific_spmv.h>\n#include <sm_utils.inl>\n\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/transform.h>\n#include <thrust/logical.h>\n\n/*\n* Note:\n* This implementation assumes that off-diag entries all have the opposite sign\n* comparing to the diag entry. This is true for most practical cases.\n* It would even work if the offending off-diag entries are just a few.\n* But if there are many off-diag entries violate this assumption,\n* the interpolation based on this strength would be inaccurate.\n* This is explained in \"Intro to Algebraic multigrid\" by K. Stuben.\n*/\n\n\nnamespace amgx\n{\n\ntemplate< typename T_Config >\nStrength_BaseBase<T_Config>::Strength_BaseBase(AMG_Config &cfg,\n        const std::string &cfg_scope)\n{\n    alpha = cfg.AMG_Config::template getParameter<double>(\"strength_threshold\", cfg_scope);\n    m_use_opt_kernels = cfg.AMG_Config::template getParameter<int>(\"use_opt_kernels\", \"default\");\n}\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ __forceinline__\nstatic float ourHash(const int i)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return (a ^ 0x4a51e590) / (float)UINT_MAX;\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights\n* Described in \\S 4.1 of:\n* \"Reducing complexity in parallel algebraic multigrid preconditioners\"\n*\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid\nStrength_Base<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_h &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    bool compute_row_sum = (max_row_sum < 1.0);\n    VVector sums_ptr;\n\n    // get the (normalised) row sums\n    if (compute_row_sum)\n    {\n        sums_ptr.resize(A.get_num_rows());\n        weightedRowSum(A, sums_ptr);\n        cudaCheckError();\n    }\n\n    // get min/max off-diag, depending on sign of diagonal\n    for (int row = 0; row < A.get_num_rows(); row++)\n    {\n        ValueType diag(0), minVal(0), maxVal(0);\n        ValueType row_sum = compute_row_sum ? sums_ptr[row] : -1;\n        int rowEnd = A.row_offsets[row + 1];\n\n        for (int j = A.row_offsets[row]; j < rowEnd; j++)\n        {\n            int col = A.col_indices[j];\n            ValueType val = A.values[j];\n\n            if (col == row)\n            {\n                diag = val;\n            }\n            else\n            {\n                minVal = std::min(minVal, val);\n                maxVal = std::max(maxVal, val);\n            }\n        }\n\n        //set the threshold for being strongly connected\n        ValueType threshold = (diag < 0) ? maxVal : minVal;\n        threshold *= this->alpha;\n\n        //initialize the weight to a small random number\n        if (A.is_matrix_singleGPU())\n        {\n            weights[row] += ourHash(row);\n        }\n        else\n        {\n            weights[row] += ourHash((int)A.manager->base_index() + row);\n        }\n\n        // sum the column of S - # of points that strongly connect to me\n        for (int j = A.row_offsets[row]; j < rowEnd; j++)\n        {\n            bool is_strongly_connected = false;\n\n            if (compute_row_sum && row_sum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n                is_strongly_connected =\n                    this->strongly_connected(A.values[j], threshold, diag);\n\n            int col = A.col_indices[j];\n            s_con[j] = (col != row) && is_strongly_connected;\n            weights[A.col_indices[j]] += s_con[j] ? 1. : 0.;\n        }\n    }\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid\nStrength_Base<TemplateConfig<AMGX_host, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeWeights_1x1(Matrix_h &S,\n                   FVector &weights)\n{\n    for (int row = 0; row < S.get_num_rows(); row++)\n    {\n        //initialize the weight to a small random number\n        if (S.is_matrix_singleGPU())\n        {\n            weights[row] += ourHash(row);\n        }\n        else\n        {\n            weights[row] += ourHash(S.manager->base_index() + row);\n        }\n\n        int rowEnd = S.row_offsets[row + 1];\n\n        for (int j = S.row_offsets[row]; j < rowEnd; j++)\n        {\n            int col = S.col_indices[j];\n\n            if (col != row) { weights[col] += 1.; }\n        }\n    }\n}\n\n\ntemplate <typename ValueType>\n__device__\nbool stronglyConnectedAHat(ValueType value, ValueType threshold, ValueType diag)\n{\n    return (diag < ValueType(0)) ? value > threshold : value < threshold;\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsKernel( const IndexType *A_rows,\n        const IndexType *A_cols,\n        const ValueType *A_vals,\n        int A_num_rows,\n        bool *s_con,\n        float *weights,\n        ValueType alpha,\n        ValueType *row_sum,\n        const double max_row_sum,\n        int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    __shared__ volatile ValueType smem[kCtaSize];\n    __shared__ volatile ValueType s_diag[num_warps];\n    __shared__ volatile ValueType s_threshold[num_warps];\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        ValueType minVal(0), maxVal(0);\n\n        if ( laneId == 0 ) // Reset the diagonal\n        {\n            s_diag[warpId] = ValueType(0);\n        }\n\n        utils::syncwarp();\n\n        // Row sum\n        ValueType rowSum = -1.0;\n\n        if (max_row_sum < 1.0) { rowSum = row_sum[aRowId]; }\n\n        // get diagonal, min/max off-diagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n\n            if ( aColId == aRowId ) // only one thread evaluates to true.\n            {\n                s_diag[warpId] = aValue;\n            }\n\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if ( is_off_diagonal )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        // init weights[] with a random number\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        utils::syncwarp();\n\n        // Big assumption: diag and off-diag always have the opposite sign.\n        // If diag entry is negative, then all off-diag entries must be positive.\n        // This means max off-diag is to be used to compute the threshold.\n        // If diag entry is positve, the min off-diag is used instead.\n        if ( s_diag[warpId] < ValueType(0) )\n        {\n            smem[threadIdx.x] = maxVal;\n\n            utils::syncwarp();\n\n#pragma unroll\n            for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            {\n                if ( laneId < offset )\n                {\n                    smem[threadIdx.x] = maxVal = max( maxVal, smem[threadIdx.x + offset] );\n                }\n                utils::syncwarp();\n            }\n        }\n        else\n        {\n            smem[threadIdx.x] = minVal;\n\n            utils::syncwarp();\n\n#pragma unroll\n            for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            {\n                if ( laneId < offset )\n                {\n                    smem[threadIdx.x] = minVal = min( minVal, smem[threadIdx.x + offset] );\n                }\n                utils::syncwarp();\n            }\n        }\n\n        if ( laneId == 0 )\n        {\n            // If laneId=0, then maxVal or minVal is in smem[threadIdx.x].\n            s_threshold[warpId] = smem[threadIdx.x] * alpha;\n        }\n\n        utils::syncwarp();\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_strongly_connected = false;\n\n            if (max_row_sum < 1.0 && rowSum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n            {\n                bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n                is_strongly_connected = is_off_diagonal &&\n                                        stronglyConnectedAHat( aValue, s_threshold[warpId], s_diag[warpId] );\n            }\n\n            if ( is_strongly_connected && aRowIt < aRowEnd && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            if ( aRowIt < aRowEnd )\n            {\n                s_con[aRowIt] = is_strongly_connected;\n            }\n        }\n    }\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsKernel_opt( const IndexType *A_rows,\n        const IndexType *A_cols,\n        const ValueType *A_vals,\n        int A_num_rows,\n        bool *s_con,\n        float *weights,\n        ValueType alpha,\n        ValueType *row_sum,\n        const double max_row_sum,\n        int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    int aRowId = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (aRowId < A_num_rows)\n    {\n        ValueType minVal(0), maxVal(0);\n\n        ValueType diag = ValueType(0);\n\n        utils::syncwarp();\n\n        // Row sum\n        ValueType rowSum = -1.0;\n\n        if (max_row_sum < 1.0) { rowSum = row_sum[aRowId]; }\n\n        // get diagonal, min/max off-diagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin; aRowIt < aRowEnd ; ++aRowIt)\n        {\n            IndexType aColId = A_cols[aRowIt];\n            ValueType aValue = A_vals[aRowIt];\n\n            if ( aColId == aRowId ) // only one thread evaluates to true.\n            {\n                diag = aValue;\n            }\n\n            if ( aColId != aRowId )\n            {\n                minVal = min( minVal, aValue );\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        if ( singleGPU )\n        {\n            atomicAdd(&weights[aRowId], ourHash(aRowId));\n        }\n        else\n        {\n            atomicAdd(&weights[aRowId], ourHash( (int) base_index + aRowId));\n        }\n\n        utils::syncwarp();\n\n        // Big assumption: diag and off-diag always have the opposite sign.\n        // If diag entry is negative, then all off-diag entries must be positive.\n        // This means max off-diag is to be used to compute the threshold.\n        // If diag entry is positve, the min off-diag is used instead.\n        ValueType threshold;\n        if ( diag < ValueType(0) )\n        {\n            threshold = maxVal*alpha;\n        }\n        else\n        {\n            threshold = minVal*alpha;\n        }\n\n        utils::syncwarp();\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin;  aRowIt < aRowEnd ; ++aRowIt)\n        {\n            IndexType aColId = A_cols[aRowIt];\n            ValueType aValue = A_vals[aRowIt];\n            bool is_strongly_connected = false;\n\n            if (max_row_sum < 1.0 && rowSum > max_row_sum)\n            {\n                is_strongly_connected = false;\n            }\n            else\n            {\n                bool is_off_diagonal = aColId != aRowId;\n                is_strongly_connected = is_off_diagonal &&\n                                        stronglyConnectedAHat( aValue, threshold, diag );\n            }\n\n            if ( is_strongly_connected && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            s_con[aRowIt] = is_strongly_connected;\n        }\n    }\n}\n\n\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeWeightsKernel( const IndexType *A_rows,\n                           const IndexType *A_cols,\n                           int A_num_rows,\n                           float *weights,\n                           int64_t base_index)\n{\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if (is_off_diagonal)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n        }\n    }\n}\n\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Base<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_d &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = A.row_offsets.raw();\n    const IndexType *column_indices_ptr = A.col_indices.raw();\n    const ValueType *values_ptr = A.values.raw();\n    bool *s_con_ptr = s_con.raw();\n    float *weights_ptr = weights.raw();\n    bool compute_row_sum = (max_row_sum < 1.0);\n\n    if (A.get_num_rows() == 0) { compute_row_sum = false; }\n\n    VVector sums_ptr;\n\n    if (compute_row_sum)\n    {\n        sums_ptr.resize(A.get_num_rows());\n        weightedRowSum(A, sums_ptr);\n        cudaCheckError();\n    }\n\n    // choose a blocksize. Use 1 warp per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = std::min( 4096, (int) (A.get_num_rows() + numWarps - 1) / numWarps );\n\n    if (A.get_num_rows() > 0)\n    {\n        if(this->m_use_opt_kernels)\n        {\n            // One row per thread\n            const int blockSize = 128;\n            const int numBlocks = A.get_num_rows() / blockSize + 1;\n\n            if (A.is_matrix_singleGPU()) {\n                computeStrongConnectionsAndWeightsKernel_opt<IndexType, ValueType, blockSize, true>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            0);\n                cudaCheckError();\n            }\n            else {\n                computeStrongConnectionsAndWeightsKernel_opt<IndexType, ValueType, blockSize, false>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            A.manager->base_index());\n                cudaCheckError();\n            }\n        }\n        else\n        {\n            const int blockSize = 256;\n            const int numWarps  = blockSize / 32;\n            const int numBlocks = min( 4096, (int) (A.get_num_rows() + numWarps - 1) / numWarps );\n\n            if (A.is_matrix_singleGPU()) {\n                computeStrongConnectionsAndWeightsKernel<IndexType, ValueType, blockSize, true>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            0);\n                cudaCheckError();\n            }\n            else {\n                computeStrongConnectionsAndWeightsKernel<IndexType, ValueType, blockSize, false>\n                    <<< numBlocks, blockSize>>>(\n                            A.row_offsets.raw(),\n                            A.col_indices.raw(),\n                            A.values.raw(),\n                            A.get_num_rows(),\n                            s_con.raw(),\n                            weights.raw(),\n                            this->alpha,\n                            compute_row_sum ? sums_ptr.raw() : NULL,\n                            max_row_sum,\n                            A.manager->base_index());\n                cudaCheckError();\n            }\n        }\n    }\n\n    if (!A.is_matrix_singleGPU() && A.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        A.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Base<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeWeights_1x1(Matrix_d &S,\n                   FVector &weights)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueType;\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = S.row_offsets.raw();\n    const IndexType *column_indices_ptr = S.col_indices.raw();\n    float *weights_ptr = weights.raw();\n    // choose a blocksize. Use 1 thread per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = std::min( 4096, (int) (S.get_num_rows() + numWarps - 1) / numWarps );\n    hipDeviceSynchronize();\n    cudaCheckError();\n\n    // call the CUDA kernel\n    if (S.is_matrix_singleGPU())\n        computeWeightsKernel<IndexType, ValueType, blockSize, true>\n        <<< numBlocks, blockSize>>>(\n            S.row_offsets.raw(),\n            S.col_indices.raw(),\n            S.get_num_rows(),\n            weights.raw(),\n            0);\n    else\n        computeWeightsKernel<IndexType, ValueType, blockSize, false>\n        <<< numBlocks, blockSize>>>(\n            S.row_offsets.raw(),\n            S.col_indices.raw(),\n            S.get_num_rows(),\n            weights.raw(),\n            S.manager->base_index());\n\n    cudaCheckError();\n\n    if (!S.is_matrix_singleGPU() && S.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        S.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\ntemplate<class T_Config>\nvoid Strength_BaseBase<T_Config>::\ncomputeStrongConnectionsAndWeights(Matrix<T_Config> &A,\n                                   BVector &s_con,\n                                   FVector &weights,\n                                   const double max_row_sum\n                                  )\n{\n    if (A.get_block_size() == 1)\n    {\n        computeStrongConnectionsAndWeights_1x1(A, s_con, weights, max_row_sum);\n    }\n    else\n        FatalError(\"Unsupported block size for strong connections\",\n                   AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\ntemplate<class T_Config>\nvoid Strength_BaseBase<T_Config>::computeWeights(Matrix<T_Config> &S,\n        FVector &weights\n                                                )\n{\n    if (S.get_block_size() == 1)\n    {\n        computeWeights_1x1(S, weights);\n    }\n    else\n        FatalError(\"Unsupported block size for strong connections\",\n                   AMGX_ERR_NOT_SUPPORTED_BLOCKSIZE);\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n#define AMGX_CASE_LINE(CASE) template class Strength_Base<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Strength_BaseBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n", "source": "/kaggle/working/amgx/src/classical/strength/strength_base.cu"}
{"input": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <fstream>\n#include <cutil.h>\n#include <types.h>\n//#include <classical/strength/ahat.h>\n//#include <classical/strength/all.h>\n#include <classical/strength/affinity.h>\n#include <float.h>\n#include <specific_spmv.h>\n\n#include <util.h>\n\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/transform.h>\n#include <thrust/logical.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/random.h>\n\n#include <sm_utils.inl>\n/*\n* Note:\n* This implementation assumes that off-diag entries all have the opposite sign\n* comparing to the diag entry. This is true for most practical cases.\n* It would even work if the offending off-diag entries are just a few.\n* But if there are many off-diag entries violate this assumption,\n* the interpolation based on this strength would be inaccurate.\n* This is explained in \"Intro to Algebraic multigrid\" by K. Stuben.\n*/\n\n\nnamespace amgx\n{\n\nnamespace\n{\nint level_counter = 0;\nbool is_random_initialized = false;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nStrength_Affinity<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Strength_Affinity(AMG_Config &cfg,\n        const std::string &cfg_scope)  : Strength_AffinityBase<TConfig_d>(cfg, cfg_scope)\n{\n    AMG_Config default_cfg;\n    std::string default_cfg_scope = \"default\";\n    affinity_iters = cfg.AMG_Config::template getParameter<int>(\"affinity_iterations\", cfg_scope);\n    n_TV = cfg.AMG_Config::template getParameter<int>(\"affinity_vectors\", cfg_scope);\n\n    if (n_TV > 32)\n    {\n        FatalError(\"Strength affinity: Test vectors > 32 is not supported, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n\n    solver = new MulticolorGaussSeidelSolver<TConfig_d>(cfg, cfg_scope);\n    this->solver->set_max_iters(affinity_iters);\n}\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ __forceinline__\nstatic float ourHash(const int i)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return (a ^ 0x4a51e590) / (float)UINT_MAX;\n}\n\nstruct prg\n{\n    float a, b;\n\n    __host__ __device__\n    prg(float _a = -1.f, float _b = 1.f) : a(_a), b(_b) {};\n\n    __host__ __device__\n    float operator()(const unsigned int n) const\n    {\n#if 1\n        amgx::thrust::default_random_engine rng;\n        amgx::thrust::uniform_real_distribution<float> dist(a, b);\n        rng.discard(n);\n        return dist(rng);\n#else\n        float ru = ourHash(n);\n        return (ru - 0.5f) * 2.0f;\n#endif\n    }\n};\n\n\ntemplate <class Vector>\nvoid initRandom(Vector &vec, int size)\n{\n    vec.resize(size);\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    thrust_wrapper::transform<Vector::TConfig::memSpace>(index_sequence_begin,\n                      index_sequence_begin + size,\n                      vec.begin(),\n                      prg(-1.f, 1.f));\n}\n\n// Device utils\ntemplate <class T>\nstatic __device__ __inline__ T cahedRead (const T *ar)\n{\n    return utils::Ld<utils::LD_NC>::load(ar);\n}\n\n// ! shfl is defined for int/float only in arch 600\ntemplate< typename ValueType >\n__forceinline__ __device__ void reduce_inwarp_mul(\n    const ValueType vValueA,\n    const ValueType vValueB,\n    const int vecId,\n    const int n_per_warp,\n    volatile ValueType *smem,\n    double *s_xy)\n{\n    ValueType sum = vValueA * vValueB;\n#pragma unroll\n\n    for ( int offset = 16 / n_per_warp ; offset > 0 ; offset /= 2 )\n    {\n        sum += utils::shfl_xor(sum, offset, 16);\n    }\n\n    *s_xy = sum;\n}\n\ntemplate< typename ValueType >\n__forceinline__ __device__ void reduce_inblock_mul(\n    const int blockSize,\n    const ValueType vValueA,\n    const ValueType vValueB,\n    const int vecId,\n    const int n_per_block,\n    volatile ValueType *smem,\n    double *s_xy)\n{\n    ValueType sum = vValueA * vValueB;\n    smem[threadIdx.x] = sum;\n    __syncthreads();\n\n    if (blockSize >= 512)\n    {\n        if (vecId < 256 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + 256 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n    if (blockSize >= 256)\n    {\n        if (vecId < 128 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + 128 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n    if (blockSize >= 128)\n    {\n        if (vecId <  64 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x +  64 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n#pragma unroll\n\n    for ( int offset = 16  ; offset > 0 ; offset /= 2 )\n        if ( vecId < offset )\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + offset];\n        }\n\n    if ( vecId == 0 )\n    {\n        // If laneId=0, then sum is in smem[threadIdx.x].\n        *s_xy = sum;\n    }\n}\n\n/*************************************************************************\n* Computes affinity matrix (device)\n************************************************************************/\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize >\n__global__\nvoid computeAffinity_1x1_Kernel(const int *A_rows,\n                                const int *A_cols,\n                                const ValueTypeB *X,\n                                const int nTV,\n                                const int A_num_rows,\n                                ValueTypeA *affinity\n                               )\n{\n    const ValueTypeB epsilon = 1.e-12;\n    const int tid =  blockDim.x * blockIdx.x + threadIdx.x;\n    const int vid = tid % nTV;\n    const int num_vecs_per_warp = 32 / nTV;\n    const int num_rows_per_iter = gridDim.x * blockDim.x / nTV;\n    //const int num_vecs_per_block = blockDim.x / nTV;\n    ValueTypeB *smem = NULL;\n    double s_xx, s_xy, s_yy;\n\n    for ( int aRowId = tid / nTV ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        const int aRowBegin = A_rows[aRowId];\n        const int aRowEnd   = A_rows[aRowId + 1];\n        ValueTypeB vValueA = X[aRowId * nTV + vid];\n        reduce_inwarp_mul(vValueA, vValueA, vid, num_vecs_per_warp, smem, &s_xx);\n#if 0\n\n        if (num_vecs_per_warp > 0)\n        {\n            reduce_inwarp_mul(vValueA, vValueA, vid, num_vecs_per_warp, smem, &s_xx);\n        }\n        else\n        {\n            reduce_inblock_mul(kCtaSize, vValueA, vValueA, vid, num_vecs_per_block, smem, &s_xx);\n        }\n\n#endif\n\n        for ( IndexType aRowIt = aRowBegin ; aRowIt <  aRowEnd;\n                aRowIt++ )\n        {\n            IndexType aColId = A_cols[aRowIt];\n\n            if (aColId != aRowId)\n            {\n                ValueTypeB vValueB = cahedRead(X + aColId * nTV + vid);\n                reduce_inwarp_mul(vValueA, vValueB, vid, num_vecs_per_warp, smem, &s_xy);\n                reduce_inwarp_mul(vValueB, vValueB, vid, num_vecs_per_warp, smem, &s_yy);\n\n                if (vid == 0)\n                {\n                    affinity[aRowIt] = (s_xy * s_xy / (s_xx * s_yy + epsilon));\n                }\n            }\n        }\n    }\n}\n\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\n\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsFromAffinityKernel(\n    const int *A_rows,\n    const int *A_cols,\n    const ValueType *A_vals,\n    int A_num_rows,\n    bool *s_con,\n    float *weights,\n    ValueType alpha,\n    int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    __shared__ volatile ValueType smem[kCtaSize];\n    __shared__ volatile ValueType s_threshold[num_warps];\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        ValueType maxVal(0);\n        // get diagonal, min/max off-idagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if ( is_off_diagonal )\n            {\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        // init weights[] with a random number\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        smem[threadIdx.x] = maxVal;\n#pragma unroll\n\n        for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            if ( laneId < offset )\n            {\n                smem[threadIdx.x] = maxVal = max( maxVal, smem[threadIdx.x + offset] );\n            }\n\n        if ( laneId == 0 )\n        {\n            // If laneId=0, then maxVal or minVal is in smem[threadIdx.x].\n            s_threshold[warpId] = smem[threadIdx.x] * alpha;\n        }\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n            bool is_strongly_connected = is_off_diagonal && (aValue > s_threshold[warpId]);\n\n            if ( is_strongly_connected && aRowIt < aRowEnd && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            if ( aRowIt < aRowEnd )\n            {\n                s_con[aRowIt] = is_strongly_connected;\n            }\n        }\n    }\n}\n\n// Save to file\ntemplate <class T_Config>\nvoid dump_matrix_vector(const char *fname, const Matrix<T_Config> &A, const Vector<T_Config> &solution)\n{\n    unsigned int num_nz = A.get_num_nz();\n    unsigned int num_rows = A.get_num_rows();\n    unsigned int vec_sz = solution.size();\n    std::ofstream fout;\n    fout.open(fname, std::ofstream::out | std::ofstream::app);\n    fout << num_rows << \" \" << num_nz << \"  \" << vec_sz << std::endl;\n    typename Vector<T_Config>::value_type val;\n\n    for (int i = 0; i < num_rows + 1; i++)\n    {\n        fout << A.row_offsets[i] << std::endl;\n    }\n\n    for (int i = 0; i < num_nz; i++)\n    {\n        fout << A.col_indices[i] << std::endl;\n    }\n\n    fout.precision(14);\n    fout.width(16);\n\n    for (int i = 0; i < num_nz; i++)\n    {\n        val = A.values[i];\n        fout << val << std::endl;\n    }\n\n    for (int i = 0; i < vec_sz; i++)\n    {\n        val = solution[i];\n        fout << val << std::endl;\n    }\n\n    fout << std::endl;\n    fout.close();\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Affinity<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_d &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename Vector<TConfig_d>::value_type ValueTypeB;\n    bool level_opt_flag = ((int) max_row_sum != -1);\n    m_aff.copy(A);\n\n    if (!is_random_initialized)\n    {\n        unsigned int nTV_num_rows = this->n_TV * A.get_num_rows();\n        m_rhs.resize(nTV_num_rows, 0.0);\n        initRandom(m_x, nTV_num_rows);\n\n        if (level_opt_flag) { is_random_initialized = true; }\n\n        level_counter = n_TV;\n    }\n    else\n    {\n        this->n_TV = min(level_counter, 32);\n        unsigned int nTV_num_rows = this->n_TV * A.get_num_rows();\n        m_rhs.resize(nTV_num_rows, 0.0);\n\n        if (m_x.size() < nTV_num_rows)\n        {\n            initRandom(m_x, nTV_num_rows);\n        }\n    }\n\n    level_counter *= 2;\n    this->solver->setup_no_throw(m_aff, false);\n    TimerCPU timer(true);\n    timer.start();\n\n    for (int iter = 0; iter < affinity_iters; iter ++)\n    {\n        this->solver->batch_smooth_1x1_fast(m_aff, this->n_TV, m_rhs, m_x);\n    }\n\n    cudaDeviceSynchronize();\n    cudaCheckError();\n    double elapsed = timer.elapsed();\n// End of GS check\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = m_aff.row_offsets.raw();\n    const IndexType *column_indices_ptr = m_aff.col_indices.raw();\n    bool *s_con_ptr = s_con.raw();\n    float *weights_ptr = weights.raw();\n    ValueTypeA *m_aff_values_ptr = m_aff.values.raw();\n    //m_aff_values.raw();\n    ValueTypeB *m_x_ptr = m_x.raw();\n    // choose a blocksize. Use 1 warp per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = (int) (A.get_num_rows() + numWarps - 1) / numWarps;\n\n    if (A.get_num_rows() > 0)\n    {\n        computeAffinity_1x1_Kernel<IndexType, ValueTypeA, ValueTypeB, blockSize>\n        <<< numBlocks, blockSize>>>(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            m_x_ptr,\n            this->n_TV,\n            A.get_num_rows(),\n            m_aff_values_ptr\n        );\n        cudaCheckError();\n\n        if (A.is_matrix_singleGPU())\n            computeStrongConnectionsAndWeightsFromAffinityKernel<IndexType, ValueTypeA, blockSize, true>\n            <<< numBlocks, blockSize>>>(\n                m_aff.row_offsets.raw(),\n                m_aff.col_indices.raw(),\n                m_aff_values_ptr,\n                m_aff.get_num_rows(),\n                s_con.raw(),\n                weights.raw(),\n                this->alpha,\n                int64_t(0));\n        else\n            computeStrongConnectionsAndWeightsFromAffinityKernel<IndexType, ValueTypeA, blockSize, false>\n            <<< numBlocks, blockSize>>>(\n                m_aff.row_offsets.raw(),\n                m_aff.col_indices.raw(),\n                m_aff_values_ptr,\n                m_aff.get_num_rows(),\n                s_con.raw(),\n                weights.raw(),\n                this->alpha,\n                A.manager->base_index());\n        \n        cudaCheckError();\n    }\n\n    if (!A.is_matrix_singleGPU() && A.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        A.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n\n#define AMGX_CASE_LINE(CASE) template class Strength_AffinityBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Strength_Affinity<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n\n", "output": "// SPDX-FileCopyrightText: 2011 - 2025 NVIDIA CORPORATION. All Rights Reserved.\n//\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <fstream>\n#include <cutil.h>\n#include <types.h>\n//#include <classical/strength/ahat.h>\n//#include <classical/strength/all.h>\n#include <classical/strength/affinity.h>\n#include <float.h>\n#include <specific_spmv.h>\n\n#include <util.h>\n\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/transform.h>\n#include <thrust/logical.h>\n#include <thrust_wrapper.h>\n\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/random.h>\n\n#include <sm_utils.inl>\n/*\n* Note:\n* This implementation assumes that off-diag entries all have the opposite sign\n* comparing to the diag entry. This is true for most practical cases.\n* It would even work if the offending off-diag entries are just a few.\n* But if there are many off-diag entries violate this assumption,\n* the interpolation based on this strength would be inaccurate.\n* This is explained in \"Intro to Algebraic multigrid\" by K. Stuben.\n*/\n\n\nnamespace amgx\n{\n\nnamespace\n{\nint level_counter = 0;\nbool is_random_initialized = false;\n}\n\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec, AMGX_IndPrecision t_indPrec>\nStrength_Affinity<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::Strength_Affinity(AMG_Config &cfg,\n        const std::string &cfg_scope)  : Strength_AffinityBase<TConfig_d>(cfg, cfg_scope)\n{\n    AMG_Config default_cfg;\n    std::string default_cfg_scope = \"default\";\n    affinity_iters = cfg.AMG_Config::template getParameter<int>(\"affinity_iterations\", cfg_scope);\n    n_TV = cfg.AMG_Config::template getParameter<int>(\"affinity_vectors\", cfg_scope);\n\n    if (n_TV > 32)\n    {\n        FatalError(\"Strength affinity: Test vectors > 32 is not supported, exiting\", AMGX_ERR_NOT_SUPPORTED_TARGET);\n    }\n\n    solver = new MulticolorGaussSeidelSolver<TConfig_d>(cfg, cfg_scope);\n    this->solver->set_max_iters(affinity_iters);\n}\n\n/*************************************************************************\n* \"random\" hash function for both device and host\n************************************************************************/\n__host__ __device__ __forceinline__\nstatic float ourHash(const int i)\n{\n    unsigned int a = i;\n    a = (a + 0x7ed55d16) + (a << 12);\n    a = (a ^ 0xc761c23c) + (a >> 19);\n    a = (a + 0x165667b1) + (a << 5);\n    a = (a ^ 0xd3a2646c) + (a << 9);\n    a = (a + 0xfd7046c5) + (a << 3);\n    a = (a ^ 0xb55a4f09) + (a >> 16);\n    return (a ^ 0x4a51e590) / (float)UINT_MAX;\n}\n\nstruct prg\n{\n    float a, b;\n\n    __host__ __device__\n    prg(float _a = -1.f, float _b = 1.f) : a(_a), b(_b) {};\n\n    __host__ __device__\n    float operator()(const unsigned int n) const\n    {\n#if 1\n        amgx::thrust::default_random_engine rng;\n        amgx::thrust::uniform_real_distribution<float> dist(a, b);\n        rng.discard(n);\n        return dist(rng);\n#else\n        float ru = ourHash(n);\n        return (ru - 0.5f) * 2.0f;\n#endif\n    }\n};\n\n\ntemplate <class Vector>\nvoid initRandom(Vector &vec, int size)\n{\n    vec.resize(size);\n    amgx::thrust::counting_iterator<unsigned int> index_sequence_begin(0);\n    thrust_wrapper::transform<Vector::TConfig::memSpace>(index_sequence_begin,\n                      index_sequence_begin + size,\n                      vec.begin(),\n                      prg(-1.f, 1.f));\n}\n\n// Device utils\ntemplate <class T>\nstatic __device__ __inline__ T cahedRead (const T *ar)\n{\n    return utils::Ld<utils::LD_NC>::load(ar);\n}\n\n// ! shfl is defined for int/float only in arch 600\ntemplate< typename ValueType >\n__forceinline__ __device__ void reduce_inwarp_mul(\n    const ValueType vValueA,\n    const ValueType vValueB,\n    const int vecId,\n    const int n_per_warp,\n    volatile ValueType *smem,\n    double *s_xy)\n{\n    ValueType sum = vValueA * vValueB;\n#pragma unroll\n\n    for ( int offset = 16 / n_per_warp ; offset > 0 ; offset /= 2 )\n    {\n        sum += utils::shfl_xor(sum, offset, 16);\n    }\n\n    *s_xy = sum;\n}\n\ntemplate< typename ValueType >\n__forceinline__ __device__ void reduce_inblock_mul(\n    const int blockSize,\n    const ValueType vValueA,\n    const ValueType vValueB,\n    const int vecId,\n    const int n_per_block,\n    volatile ValueType *smem,\n    double *s_xy)\n{\n    ValueType sum = vValueA * vValueB;\n    smem[threadIdx.x] = sum;\n    __syncthreads();\n\n    if (blockSize >= 512)\n    {\n        if (vecId < 256 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + 256 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n    if (blockSize >= 256)\n    {\n        if (vecId < 128 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + 128 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n    if (blockSize >= 128)\n    {\n        if (vecId <  64 / n_per_block)\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x +  64 / n_per_block];\n        }\n\n        __syncthreads();\n    }\n\n#pragma unroll\n\n    for ( int offset = 16  ; offset > 0 ; offset /= 2 )\n        if ( vecId < offset )\n        {\n            smem[threadIdx.x] = sum = sum + smem[threadIdx.x + offset];\n        }\n\n    if ( vecId == 0 )\n    {\n        // If laneId=0, then sum is in smem[threadIdx.x].\n        *s_xy = sum;\n    }\n}\n\n/*************************************************************************\n* Computes affinity matrix (device)\n************************************************************************/\n\ntemplate< typename IndexType, typename ValueTypeA, typename ValueTypeB, int kCtaSize >\n__global__\nvoid computeAffinity_1x1_Kernel(const int *A_rows,\n                                const int *A_cols,\n                                const ValueTypeB *X,\n                                const int nTV,\n                                const int A_num_rows,\n                                ValueTypeA *affinity\n                               )\n{\n    const ValueTypeB epsilon = 1.e-12;\n    const int tid =  blockDim.x * blockIdx.x + threadIdx.x;\n    const int vid = tid % nTV;\n    const int num_vecs_per_warp = 32 / nTV;\n    const int num_rows_per_iter = gridDim.x * blockDim.x / nTV;\n    //const int num_vecs_per_block = blockDim.x / nTV;\n    ValueTypeB *smem = NULL;\n    double s_xx, s_xy, s_yy;\n\n    for ( int aRowId = tid / nTV ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        const int aRowBegin = A_rows[aRowId];\n        const int aRowEnd   = A_rows[aRowId + 1];\n        ValueTypeB vValueA = X[aRowId * nTV + vid];\n        reduce_inwarp_mul(vValueA, vValueA, vid, num_vecs_per_warp, smem, &s_xx);\n#if 0\n\n        if (num_vecs_per_warp > 0)\n        {\n            reduce_inwarp_mul(vValueA, vValueA, vid, num_vecs_per_warp, smem, &s_xx);\n        }\n        else\n        {\n            reduce_inblock_mul(kCtaSize, vValueA, vValueA, vid, num_vecs_per_block, smem, &s_xx);\n        }\n\n#endif\n\n        for ( IndexType aRowIt = aRowBegin ; aRowIt <  aRowEnd;\n                aRowIt++ )\n        {\n            IndexType aColId = A_cols[aRowIt];\n\n            if (aColId != aRowId)\n            {\n                ValueTypeB vValueB = cahedRead(X + aColId * nTV + vid);\n                reduce_inwarp_mul(vValueA, vValueB, vid, num_vecs_per_warp, smem, &s_xy);\n                reduce_inwarp_mul(vValueB, vValueB, vid, num_vecs_per_warp, smem, &s_yy);\n\n                if (vid == 0)\n                {\n                    affinity[aRowIt] = (s_xy * s_xy / (s_xx * s_yy + epsilon));\n                }\n            }\n        }\n    }\n}\n\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\n\ntemplate< typename IndexType, typename ValueType, int kCtaSize, bool singleGPU >\n__global__\nvoid computeStrongConnectionsAndWeightsFromAffinityKernel(\n    const int *A_rows,\n    const int *A_cols,\n    const ValueType *A_vals,\n    int A_num_rows,\n    bool *s_con,\n    float *weights,\n    ValueType alpha,\n    int64_t base_index)\n{\n    // One warp works on each row and hence one iteration handles\n    // num_warps*numBlock rows. This means atomicAdd() is inevitable.\n    const int num_warps = kCtaSize / 32;\n    const int num_rows_per_iter = num_warps * gridDim.x;\n    __shared__ volatile ValueType smem[kCtaSize];\n    __shared__ volatile ValueType s_threshold[num_warps];\n    const int warpId = threadIdx.x / 32;\n    const int laneId = threadIdx.x % 32;\n\n    for ( int aRowId = blockIdx.x * num_warps + warpId ; aRowId < A_num_rows ;\n            aRowId += num_rows_per_iter )\n    {\n        ValueType maxVal(0);\n        // get diagonal, min/max off-idagonals\n        const int aRowBegin = A_rows[aRowId  ];\n        const int aRowEnd   = A_rows[aRowId + 1];\n\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n\n            if ( is_off_diagonal )\n            {\n                maxVal = max( maxVal, aValue );\n            }\n        }\n\n        // init weights[] with a random number\n        if ( laneId == 0 )\n        {\n            if ( singleGPU )\n            {\n                atomicAdd( &weights[aRowId], ourHash(aRowId) );\n            }\n            else\n            {\n                atomicAdd( &weights[aRowId], ourHash( (int) base_index + aRowId) );\n            }\n        }\n\n        smem[threadIdx.x] = maxVal;\n#pragma unroll\n\n        for ( int offset = 16 ; offset > 0 ; offset /= 2 )\n            if ( laneId < offset )\n            {\n                smem[threadIdx.x] = maxVal = max( maxVal, smem[threadIdx.x + offset] );\n            }\n\n        if ( laneId == 0 )\n        {\n            // If laneId=0, then maxVal or minVal is in smem[threadIdx.x].\n            s_threshold[warpId] = smem[threadIdx.x] * alpha;\n        }\n\n        // sum of the column of S\n        for ( IndexType aRowIt = aRowBegin + laneId ; utils::any( aRowIt < aRowEnd ) ;\n                aRowIt += 32 )\n        {\n            IndexType aColId = aRowIt < aRowEnd ? A_cols[aRowIt] : -1;\n            ValueType aValue = aRowIt < aRowEnd ? A_vals[aRowIt] : ValueType(0);\n            bool is_off_diagonal = aRowIt < aRowEnd && aColId != aRowId;\n            bool is_strongly_connected = is_off_diagonal && (aValue > s_threshold[warpId]);\n\n            if ( is_strongly_connected && aRowIt < aRowEnd && aColId < A_num_rows)\n            {\n                atomicAdd( &weights[aColId], 1.0f );\n            }\n\n            if ( aRowIt < aRowEnd )\n            {\n                s_con[aRowIt] = is_strongly_connected;\n            }\n        }\n    }\n}\n\n// Save to file\ntemplate <class T_Config>\nvoid dump_matrix_vector(const char *fname, const Matrix<T_Config> &A, const Vector<T_Config> &solution)\n{\n    unsigned int num_nz = A.get_num_nz();\n    unsigned int num_rows = A.get_num_rows();\n    unsigned int vec_sz = solution.size();\n    std::ofstream fout;\n    fout.open(fname, std::ofstream::out | std::ofstream::app);\n    fout << num_rows << \" \" << num_nz << \"  \" << vec_sz << std::endl;\n    typename Vector<T_Config>::value_type val;\n\n    for (int i = 0; i < num_rows + 1; i++)\n    {\n        fout << A.row_offsets[i] << std::endl;\n    }\n\n    for (int i = 0; i < num_nz; i++)\n    {\n        fout << A.col_indices[i] << std::endl;\n    }\n\n    fout.precision(14);\n    fout.width(16);\n\n    for (int i = 0; i < num_nz; i++)\n    {\n        val = A.values[i];\n        fout << val << std::endl;\n    }\n\n    for (int i = 0; i < vec_sz; i++)\n    {\n        val = solution[i];\n        fout << val << std::endl;\n    }\n\n    fout << std::endl;\n    fout.close();\n}\n\n/*************************************************************************\n* Computes the strength matrix and the connection weights (device)\n************************************************************************/\ntemplate <AMGX_VecPrecision t_vecPrec, AMGX_MatPrecision t_matPrec,\n          AMGX_IndPrecision t_indPrec>\nvoid Strength_Affinity<TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> >::\ncomputeStrongConnectionsAndWeights_1x1(Matrix_d &A,\n                                       BVector &s_con,\n                                       FVector &weights,\n                                       const double max_row_sum)\n{\n    typedef TemplateConfig<AMGX_device, t_vecPrec, t_matPrec, t_indPrec> TConfig_d;\n    typedef typename Matrix_d::index_type IndexType;\n    typedef typename Matrix_d::value_type ValueTypeA;\n    typedef typename Vector<TConfig_d>::value_type ValueTypeB;\n    bool level_opt_flag = ((int) max_row_sum != -1);\n    m_aff.copy(A);\n\n    if (!is_random_initialized)\n    {\n        unsigned int nTV_num_rows = this->n_TV * A.get_num_rows();\n        m_rhs.resize(nTV_num_rows, 0.0);\n        initRandom(m_x, nTV_num_rows);\n\n        if (level_opt_flag) { is_random_initialized = true; }\n\n        level_counter = n_TV;\n    }\n    else\n    {\n        this->n_TV = min(level_counter, 32);\n        unsigned int nTV_num_rows = this->n_TV * A.get_num_rows();\n        m_rhs.resize(nTV_num_rows, 0.0);\n\n        if (m_x.size() < nTV_num_rows)\n        {\n            initRandom(m_x, nTV_num_rows);\n        }\n    }\n\n    level_counter *= 2;\n    this->solver->setup_no_throw(m_aff, false);\n    TimerCPU timer(true);\n    timer.start();\n\n    for (int iter = 0; iter < affinity_iters; iter ++)\n    {\n        this->solver->batch_smooth_1x1_fast(m_aff, this->n_TV, m_rhs, m_x);\n    }\n\n    hipDeviceSynchronize();\n    cudaCheckError();\n    double elapsed = timer.elapsed();\n// End of GS check\n    // get the raw pointers for everything I need\n    const IndexType *offsets_ptr = m_aff.row_offsets.raw();\n    const IndexType *column_indices_ptr = m_aff.col_indices.raw();\n    bool *s_con_ptr = s_con.raw();\n    float *weights_ptr = weights.raw();\n    ValueTypeA *m_aff_values_ptr = m_aff.values.raw();\n    //m_aff_values.raw();\n    ValueTypeB *m_x_ptr = m_x.raw();\n    // choose a blocksize. Use 1 warp per row\n    const int blockSize = 256;\n    const int numWarps  = blockSize / 32;\n    const int numBlocks = (int) (A.get_num_rows() + numWarps - 1) / numWarps;\n\n    if (A.get_num_rows() > 0)\n    {\n        computeAffinity_1x1_Kernel<IndexType, ValueTypeA, ValueTypeB, blockSize>\n        <<< numBlocks, blockSize>>>(\n            A.row_offsets.raw(),\n            A.col_indices.raw(),\n            m_x_ptr,\n            this->n_TV,\n            A.get_num_rows(),\n            m_aff_values_ptr\n        );\n        cudaCheckError();\n\n        if (A.is_matrix_singleGPU())\n            computeStrongConnectionsAndWeightsFromAffinityKernel<IndexType, ValueTypeA, blockSize, true>\n            <<< numBlocks, blockSize>>>(\n                m_aff.row_offsets.raw(),\n                m_aff.col_indices.raw(),\n                m_aff_values_ptr,\n                m_aff.get_num_rows(),\n                s_con.raw(),\n                weights.raw(),\n                this->alpha,\n                int64_t(0));\n        else\n            computeStrongConnectionsAndWeightsFromAffinityKernel<IndexType, ValueTypeA, blockSize, false>\n            <<< numBlocks, blockSize>>>(\n                m_aff.row_offsets.raw(),\n                m_aff.col_indices.raw(),\n                m_aff_values_ptr,\n                m_aff.get_num_rows(),\n                s_con.raw(),\n                weights.raw(),\n                this->alpha,\n                A.manager->base_index());\n        \n        cudaCheckError();\n    }\n\n    if (!A.is_matrix_singleGPU() && A.currentView() == OWNED)\n    {\n        // Need to add neighbors contribution to my weights\n        weights.dirtybit = 1;\n        A.manager->add_from_halo(weights, weights.tag);\n    }\n\n    cudaCheckError();\n}\n\n\n/****************************************\n * Explict instantiations\n ***************************************/\n\n#define AMGX_CASE_LINE(CASE) template class Strength_AffinityBase<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n#define AMGX_CASE_LINE(CASE) template class Strength_Affinity<TemplateMode<CASE>::Type>;\nAMGX_FORALL_BUILDS(AMGX_CASE_LINE)\n#undef AMGX_CASE_LINE\n\n} // namespace amgx\n\n\n", "source": "/kaggle/working/amgx/src/classical/strength/affinity.cu"}
{"input": "\n#include <cuda_runtime.h>\n\n// CUDA-compatible types (no C++ standard library headers for NVRTC)\ntypedef unsigned char uint8_t;\ntypedef unsigned long long uint64_t;\ntypedef uint64_t uintptr_t;\n\n// ============================================================================\n// Kernel Configuration Constants (from ChunkedKernel.h)\n// ============================================================================\n\nconstexpr int CHUNK_SIZE = 128 * 1024;       // 128KB per chunk\nconstexpr int NUM_BLOCKS = 75;               // Fixed grid size\nconstexpr int THREADS_PER_BLOCK = 1024;      // Fixed block size\nconstexpr int FLOAT4_SIZE = 16;              // 16 bytes per float4\nconstexpr int MAX_CHUNKS_PER_BLOCK = 512;    // Max chunks per block for shared memory\n\nextern \"C\" {\n\n/**\n * Chunked batched copy kernel implementation\n *\n * This kernel performs efficient batched memory copies using:\n * 1. Contiguous block assignment for better load balancing\n * 2. Shared memory prefetching of chunk metadata\n * 3. Vectorized float4 (16-byte) copies for aligned data\n * 4. Byte-by-byte fallback for unaligned or small data\n */\n__global__ void chunked_batched_copy_kernel(\n    uint8_t** src_addrs,\n    uint8_t** dst_addrs,\n    size_t* sizes,\n    int total_chunks\n) {\n    // Shared memory for metadata prefetching\n    __shared__ uint8_t* s_src_addrs[MAX_CHUNKS_PER_BLOCK];\n    __shared__ uint8_t* s_dst_addrs[MAX_CHUNKS_PER_BLOCK];\n    __shared__ size_t s_sizes[MAX_CHUNKS_PER_BLOCK];\n\n    // Contiguous block assignment: block i processes chunks [start_chunk, end_chunk)\n    int chunks_per_block = (total_chunks + gridDim.x - 1) / gridDim.x;  // Ceiling division\n    int start_chunk = blockIdx.x * chunks_per_block;\n    int end_chunk = start_chunk + chunks_per_block;\n    if (end_chunk > total_chunks) {\n        end_chunk = total_chunks;\n    }\n    int num_chunks_this_block = end_chunk - start_chunk;\n\n    // Phase 1: Cooperative loading of metadata to shared memory\n    // All 1024 threads cooperate to load metadata from global memory\n    for (int i = threadIdx.x; i < num_chunks_this_block; i += blockDim.x) {\n        int global_chunk_id = start_chunk + i;\n        s_src_addrs[i] = src_addrs[global_chunk_id];\n        s_dst_addrs[i] = dst_addrs[global_chunk_id];\n        s_sizes[i] = sizes[global_chunk_id];\n    }\n    __syncthreads();\n\n    // Phase 2: Process each chunk using metadata from shared memory\n    for (int chunk_id = 0; chunk_id < num_chunks_this_block; chunk_id++) {\n        uint8_t* src = s_src_addrs[chunk_id];\n        uint8_t* dst = s_dst_addrs[chunk_id];\n        size_t size = s_sizes[chunk_id];\n\n        // Check if both src and dst are aligned to 16 bytes for float4 access\n        uintptr_t src_addr = (uintptr_t)src;\n        uintptr_t dst_addr = (uintptr_t)dst;\n        bool is_aligned = ((src_addr % FLOAT4_SIZE) == 0) && ((dst_addr % FLOAT4_SIZE) == 0);\n\n        if (is_aligned && size >= FLOAT4_SIZE) {\n            // Fast path: vectorized float4 copies\n            size_t aligned_size = (size / FLOAT4_SIZE) * FLOAT4_SIZE;\n\n            // All 1024 threads cooperate on float4 copies\n            #pragma unroll 4\n            for (size_t offset = threadIdx.x * FLOAT4_SIZE;\n                 offset < aligned_size;\n                 offset += blockDim.x * FLOAT4_SIZE) {\n                // Vectorized 16-byte load and store\n                float4 data = *((float4*)(src + offset));\n                *((float4*)(dst + offset)) = data;\n            }\n\n            // Handle remaining bytes (< 16 bytes) with byte-by-byte copy\n            for (size_t offset = aligned_size + threadIdx.x;\n                 offset < size;\n                 offset += blockDim.x) {\n                dst[offset] = src[offset];\n            }\n        } else {\n            // Fallback path: byte-by-byte copy for unaligned addresses\n            // Still use all threads for parallelism\n            for (size_t offset = threadIdx.x; offset < size; offset += blockDim.x) {\n                dst[offset] = src[offset];\n            }\n        }\n    }\n\n    // System-wide memory fence: ensures all writes from this thread are visible\n    // to system agents (NIC DMA engines, other GPUs via PCIe/NVLink).\n    // Required so that NVSHMEM put() on another CUDA stream can read correct\n    // pack data from the send buffer. Without this, writes may remain in L2\n    // cache, invisible to the NIC's DMA engine.\n    __threadfence_system();\n}\n\n}\n\n\n", "output": "\n#include <hip/hip_runtime.h>\n\n// CUDA-compatible types (no C++ standard library headers for NVRTC)\ntypedef unsigned char uint8_t;\ntypedef unsigned long long uint64_t;\ntypedef uint64_t uintptr_t;\n\n// ============================================================================\n// Kernel Configuration Constants (from ChunkedKernel.h)\n// ============================================================================\n\nconstexpr int CHUNK_SIZE = 128 * 1024;       // 128KB per chunk\nconstexpr int NUM_BLOCKS = 75;               // Fixed grid size\nconstexpr int THREADS_PER_BLOCK = 1024;      // Fixed block size\nconstexpr int FLOAT4_SIZE = 16;              // 16 bytes per float4\nconstexpr int MAX_CHUNKS_PER_BLOCK = 512;    // Max chunks per block for shared memory\n\nextern \"C\" {\n\n/**\n * Chunked batched copy kernel implementation\n *\n * This kernel performs efficient batched memory copies using:\n * 1. Contiguous block assignment for better load balancing\n * 2. Shared memory prefetching of chunk metadata\n * 3. Vectorized float4 (16-byte) copies for aligned data\n * 4. Byte-by-byte fallback for unaligned or small data\n */\n__global__ void chunked_batched_copy_kernel(\n    uint8_t** src_addrs,\n    uint8_t** dst_addrs,\n    size_t* sizes,\n    int total_chunks\n) {\n    // Shared memory for metadata prefetching\n    __shared__ uint8_t* s_src_addrs[MAX_CHUNKS_PER_BLOCK];\n    __shared__ uint8_t* s_dst_addrs[MAX_CHUNKS_PER_BLOCK];\n    __shared__ size_t s_sizes[MAX_CHUNKS_PER_BLOCK];\n\n    // Contiguous block assignment: block i processes chunks [start_chunk, end_chunk)\n    int chunks_per_block = (total_chunks + gridDim.x - 1) / gridDim.x;  // Ceiling division\n    int start_chunk = blockIdx.x * chunks_per_block;\n    int end_chunk = start_chunk + chunks_per_block;\n    if (end_chunk > total_chunks) {\n        end_chunk = total_chunks;\n    }\n    int num_chunks_this_block = end_chunk - start_chunk;\n\n    // Phase 1: Cooperative loading of metadata to shared memory\n    // All 1024 threads cooperate to load metadata from global memory\n    for (int i = threadIdx.x; i < num_chunks_this_block; i += blockDim.x) {\n        int global_chunk_id = start_chunk + i;\n        s_src_addrs[i] = src_addrs[global_chunk_id];\n        s_dst_addrs[i] = dst_addrs[global_chunk_id];\n        s_sizes[i] = sizes[global_chunk_id];\n    }\n    __syncthreads();\n\n    // Phase 2: Process each chunk using metadata from shared memory\n    for (int chunk_id = 0; chunk_id < num_chunks_this_block; chunk_id++) {\n        uint8_t* src = s_src_addrs[chunk_id];\n        uint8_t* dst = s_dst_addrs[chunk_id];\n        size_t size = s_sizes[chunk_id];\n\n        // Check if both src and dst are aligned to 16 bytes for float4 access\n        uintptr_t src_addr = (uintptr_t)src;\n        uintptr_t dst_addr = (uintptr_t)dst;\n        bool is_aligned = ((src_addr % FLOAT4_SIZE) == 0) && ((dst_addr % FLOAT4_SIZE) == 0);\n\n        if (is_aligned && size >= FLOAT4_SIZE) {\n            // Fast path: vectorized float4 copies\n            size_t aligned_size = (size / FLOAT4_SIZE) * FLOAT4_SIZE;\n\n            // All 1024 threads cooperate on float4 copies\n            #pragma unroll 4\n            for (size_t offset = threadIdx.x * FLOAT4_SIZE;\n                 offset < aligned_size;\n                 offset += blockDim.x * FLOAT4_SIZE) {\n                // Vectorized 16-byte load and store\n                float4 data = *((float4*)(src + offset));\n                *((float4*)(dst + offset)) = data;\n            }\n\n            // Handle remaining bytes (< 16 bytes) with byte-by-byte copy\n            for (size_t offset = aligned_size + threadIdx.x;\n                 offset < size;\n                 offset += blockDim.x) {\n                dst[offset] = src[offset];\n            }\n        } else {\n            // Fallback path: byte-by-byte copy for unaligned addresses\n            // Still use all threads for parallelism\n            for (size_t offset = threadIdx.x; offset < size; offset += blockDim.x) {\n                dst[offset] = src[offset];\n            }\n        }\n    }\n\n    // System-wide memory fence: ensures all writes from this thread are visible\n    // to system agents (NIC DMA engines, other GPUs via PCIe/NVLink).\n    // Required so that NVSHMEM put() on another CUDA stream can read correct\n    // pack data from the send buffer. Without this, writes may remain in L2\n    // cache, invisible to the NIC's DMA engine.\n    __threadfence_system();\n}\n\n}\n\n\n", "source": "/kaggle/working/Megatron-LM/megatron/core/resharding/nvshmem_copy_service/kernels/chunked_kernel.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <cuml/prims/opg/comm_utils.h>\n#include <cuml/prims/opg/stats/stddev.hpp>\n\n#include <raft/linalg/divide.cuh>\n#include <raft/linalg/multiply.cuh>\n#include <raft/linalg/transpose.cuh>\n#include <raft/stats/sum.cuh>\n\n#include <rmm/device_uvector.hpp>\n\nnamespace MLCommon {\nnamespace Stats {\nnamespace opg {\n\ntemplate <typename Type, typename IdxType, int TPB>\nstatic __global__ void varsPartitionKernelColMajor(\n  Type* var, const Type* data, const Type* mu, IdxType D, IdxType N, IdxType K)\n{\n  typedef cub::BlockReduce<Type, TPB> BlockReduce;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n  Type thread_data = Type(0);\n  IdxType colStart = N * blockIdx.x;\n  Type m           = mu[blockIdx.x];\n  for (IdxType i = threadIdx.x; i < N; i += TPB) {\n    IdxType idx = colStart + i;\n    Type diff   = data[idx] - m;\n    thread_data += diff * diff;\n  }\n  Type acc = BlockReduce(temp_storage).Sum(thread_data);\n  if (threadIdx.x == 0) { var[blockIdx.x] = acc / K; }\n}\n\ntemplate <typename Type, typename IdxType = int>\nvoid varPartition(\n  Type* std, const Type* data, const Type* mu, IdxType D, IdxType N, IdxType K, cudaStream_t stream)\n{\n  static const int TPB = 256;\n  varsPartitionKernelColMajor<Type, IdxType, TPB><<<D, TPB, 0, stream>>>(std, data, mu, D, N, K);\n\n  RAFT_CUDA_TRY(cudaPeekAtLastError());\n}\n\ntemplate <typename T, int TPB = 256>\nvoid var_impl(const raft::handle_t& handle,\n              Matrix::Data<T>& out,\n              const std::vector<Matrix::Data<T>*>& in,\n              const Matrix::PartDescriptor& inDesc,\n              const T* mu,\n              cudaStream_t* streams,\n              int n_streams)\n{\n  auto& comm = handle.get_comms();\n\n  rmm::device_uvector<T> local_vars_tmp(in.size() * inDesc.N, streams[0]);\n  rmm::device_uvector<T> local_vars_tmp_t(in.size() * inDesc.N, streams[0]);\n\n  std::vector<Matrix::RankSizePair*> localBlocks = inDesc.blocksOwnedBy(comm.get_rank());\n\n  for (size_t i = 0; i < localBlocks.size(); i++) {\n    T* loc = local_vars_tmp.data() + (i * inDesc.N);\n    Stats::opg::varPartition(\n      loc, in[i]->ptr, mu, inDesc.N, localBlocks[i]->size, inDesc.M, streams[i % n_streams]);\n  }\n\n  for (int i = 0; i < n_streams; i++) {\n    RAFT_CUDA_TRY(cudaStreamSynchronize(streams[i]));\n  }\n\n  raft::linalg::transpose(handle,\n                          local_vars_tmp.data(),\n                          local_vars_tmp_t.data(),\n                          inDesc.N,\n                          localBlocks.size(),\n                          streams[0]);\n\n  raft::stats::sum<false>(\n    local_vars_tmp.data(), local_vars_tmp_t.data(), inDesc.N, localBlocks.size(), streams[0]);\n\n  comm.allreduce(local_vars_tmp.data(), out.ptr, inDesc.N, raft::comms::op_t::SUM, streams[0]);\n\n  comm.sync_stream(streams[0]);\n}\n\nvoid var(const raft::handle_t& handle,\n         Matrix::Data<double>& out,\n         const std::vector<Matrix::Data<double>*>& in,\n         const Matrix::PartDescriptor& inDesc,\n         const double* mu,\n         cudaStream_t* streams,\n         int n_streams)\n{\n  var_impl<double>(handle, out, in, inDesc, mu, streams, n_streams);\n}\n\nvoid var(const raft::handle_t& handle,\n         Matrix::Data<float>& out,\n         const std::vector<Matrix::Data<float>*>& in,\n         const Matrix::PartDescriptor& inDesc,\n         const float* mu,\n         cudaStream_t* streams,\n         int n_streams)\n{\n  var_impl<float>(handle, out, in, inDesc, mu, streams, n_streams);\n}\n\n};  // namespace opg\n};  // namespace Stats\n};  // namespace MLCommon\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuml/prims/opg/comm_utils.h>\n#include <cuml/prims/opg/stats/stddev.hpp>\n\n#include <raft/linalg/divide.cuh>\n#include <raft/linalg/multiply.cuh>\n#include <raft/linalg/transpose.cuh>\n#include <raft/stats/sum.cuh>\n\n#include <rmm/device_uvector.hpp>\n\nnamespace MLCommon {\nnamespace Stats {\nnamespace opg {\n\ntemplate <typename Type, typename IdxType, int TPB>\nstatic __global__ void varsPartitionKernelColMajor(\n  Type* var, const Type* data, const Type* mu, IdxType D, IdxType N, IdxType K)\n{\n  typedef hipcub::BlockReduce<Type, TPB> BlockReduce;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n  Type thread_data = Type(0);\n  IdxType colStart = N * blockIdx.x;\n  Type m           = mu[blockIdx.x];\n  for (IdxType i = threadIdx.x; i < N; i += TPB) {\n    IdxType idx = colStart + i;\n    Type diff   = data[idx] - m;\n    thread_data += diff * diff;\n  }\n  Type acc = BlockReduce(temp_storage).Sum(thread_data);\n  if (threadIdx.x == 0) { var[blockIdx.x] = acc / K; }\n}\n\ntemplate <typename Type, typename IdxType = int>\nvoid varPartition(\n  Type* std, const Type* data, const Type* mu, IdxType D, IdxType N, IdxType K, hipStream_t stream)\n{\n  static const int TPB = 256;\n  varsPartitionKernelColMajor<Type, IdxType, TPB><<<D, TPB, 0, stream>>>(std, data, mu, D, N, K);\n\n  RAFT_CUDA_TRY(hipPeekAtLastError());\n}\n\ntemplate <typename T, int TPB = 256>\nvoid var_impl(const raft::handle_t& handle,\n              Matrix::Data<T>& out,\n              const std::vector<Matrix::Data<T>*>& in,\n              const Matrix::PartDescriptor& inDesc,\n              const T* mu,\n              hipStream_t* streams,\n              int n_streams)\n{\n  auto& comm = handle.get_comms();\n\n  rmm::device_uvector<T> local_vars_tmp(in.size() * inDesc.N, streams[0]);\n  rmm::device_uvector<T> local_vars_tmp_t(in.size() * inDesc.N, streams[0]);\n\n  std::vector<Matrix::RankSizePair*> localBlocks = inDesc.blocksOwnedBy(comm.get_rank());\n\n  for (size_t i = 0; i < localBlocks.size(); i++) {\n    T* loc = local_vars_tmp.data() + (i * inDesc.N);\n    Stats::opg::varPartition(\n      loc, in[i]->ptr, mu, inDesc.N, localBlocks[i]->size, inDesc.M, streams[i % n_streams]);\n  }\n\n  for (int i = 0; i < n_streams; i++) {\n    RAFT_CUDA_TRY(hipStreamSynchronize(streams[i]));\n  }\n\n  raft::linalg::transpose(handle,\n                          local_vars_tmp.data(),\n                          local_vars_tmp_t.data(),\n                          inDesc.N,\n                          localBlocks.size(),\n                          streams[0]);\n\n  raft::stats::sum<false>(\n    local_vars_tmp.data(), local_vars_tmp_t.data(), inDesc.N, localBlocks.size(), streams[0]);\n\n  comm.allreduce(local_vars_tmp.data(), out.ptr, inDesc.N, raft::comms::op_t::SUM, streams[0]);\n\n  comm.sync_stream(streams[0]);\n}\n\nvoid var(const raft::handle_t& handle,\n         Matrix::Data<double>& out,\n         const std::vector<Matrix::Data<double>*>& in,\n         const Matrix::PartDescriptor& inDesc,\n         const double* mu,\n         hipStream_t* streams,\n         int n_streams)\n{\n  var_impl<double>(handle, out, in, inDesc, mu, streams, n_streams);\n}\n\nvoid var(const raft::handle_t& handle,\n         Matrix::Data<float>& out,\n         const std::vector<Matrix::Data<float>*>& in,\n         const Matrix::PartDescriptor& inDesc,\n         const float* mu,\n         hipStream_t* streams,\n         int n_streams)\n{\n  var_impl<float>(handle, out, in, inDesc, mu, streams, n_streams);\n}\n\n};  // namespace opg\n};  // namespace Stats\n};  // namespace MLCommon\n", "source": "/kaggle/working/cuml/cpp/src_prims/opg/stats/stddev.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n#include <cuml/prims/opg/matrix/matrix_utils.hpp>\n\n#include <raft/linalg/transpose.cuh>\n#include <raft/util/cudart_utils.hpp>\n\n#include <rmm/device_uvector.hpp>\n#include <rmm/mr/per_device_resource.hpp>\n#include <rmm/resource_ref.hpp>\n\nnamespace MLCommon {\nnamespace Matrix {\nnamespace opg {\n\ntemplate <typename T>\nvoid gatherPart(const raft::handle_t& h,\n                T* gatheredPart,\n                std::vector<Matrix::Data<T>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                cudaStream_t stream)\n{\n  ASSERT(partIndex < desc.partsToRanks.size(),\n         \"MLCommon::Matrix::opg::gatherPart: Part index is out of range\");\n  const auto& comm = h.get_comms();\n  std::vector<raft::comms::request_t> requests;\n  int items = desc.partsToRanks[partIndex]->size * desc.N;\n  comm.sync_stream(stream);\n  if (desc.partsToRanks[partIndex]->rank == myRank) {\n    requests.resize(requests.size() + 1);\n    int localIndex = 0;\n    for (int i = 0; i < partIndex; i++) {\n      if (desc.partsToRanks[i]->rank == myRank) { localIndex++; }\n    }\n\n    comm.isend(parts[localIndex]->ptr, items, rootRank, 0, &requests.back());\n  }\n  if (myRank == rootRank) {\n    requests.resize(requests.size() + 1);\n    comm.irecv(gatheredPart, items, desc.partsToRanks[partIndex]->rank, 0, &requests.back());\n  }\n  comm.waitall(requests.size(), requests.data());\n}\n\ntemplate <typename T>\nvoid allGatherPart(const raft::handle_t& h,\n                   T* gatheredPart,\n                   std::vector<Matrix::Data<T>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   cudaStream_t stream)\n{\n  ASSERT(partIndex < desc.partsToRanks.size(),\n         \"MLCommon::Matrix::opg::allGatherPart: Part index is out of range\");\n\n  const auto& comm = h.get_comms();\n\n  int partLength = desc.partsToRanks[partIndex]->size * desc.N;\n\n  if (desc.partsToRanks[partIndex]->rank == myRank) {\n    int localIndex = 0;\n    for (int i = 0; i < partIndex; i++) {\n      if (desc.partsToRanks[i]->rank == myRank) { localIndex++; }\n    }\n    raft::copy(gatheredPart, parts[localIndex]->ptr, partLength, stream);\n    comm.sync_stream(stream);\n  }\n  comm.bcast(gatheredPart, partLength, desc.partsToRanks[partIndex]->rank, stream);\n}\n\ntemplate <typename T>\nvoid gather(const raft::handle_t& h,\n            T* gatheredMatrix,\n            std::vector<Matrix::Data<T>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            cudaStream_t stream)\n{\n  size_t maxPartSize = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (maxPartSize < desc.partsToRanks[i]->size) { maxPartSize = desc.partsToRanks[i]->size; }\n  }\n\n  rmm::device_uvector<T> partBuffer(0, stream);\n  if (myRank == rootRank) { partBuffer.resize(maxPartSize * desc.N, stream); }\n  size_t offset = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    gatherPart(h, partBuffer.data(), parts, desc, i, rootRank, myRank, stream);\n\n    if (myRank == rootRank) {\n      if (desc.layout == Matrix::LayoutRowMajor) {\n        raft::copy(\n          gatheredMatrix + offset, partBuffer.data(), desc.partsToRanks[i]->size * desc.N, stream);\n      } else {\n        raft::linalg::transpose(h,\n                                partBuffer.data(),\n                                gatheredMatrix + offset,\n                                (int)desc.partsToRanks[i]->size,\n                                (int)desc.N,\n                                stream);\n      }\n    }\n    offset += desc.partsToRanks[i]->size * desc.N;\n  }\n  RAFT_CUDA_TRY(cudaStreamSynchronize(stream));\n}\n\ntemplate <typename T>\nvoid allGather(const raft::handle_t& h,\n               T* gatheredMatrix,\n               std::vector<Matrix::Data<T>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream)\n{\n  size_t maxPartSize = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (maxPartSize < desc.partsToRanks[i]->size) { maxPartSize = desc.partsToRanks[i]->size; }\n  }\n\n  rmm::device_uvector<T> partBuffer(maxPartSize * desc.N, stream);\n  size_t offset = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    allGatherPart(h, partBuffer.data(), parts, desc, i, myRank, stream);\n    if (desc.layout == Matrix::LayoutRowMajor) {\n      raft::copy(\n        gatheredMatrix + offset, partBuffer.data(), desc.partsToRanks[i]->size * desc.N, stream);\n    } else {\n      raft::linalg::transpose(h,\n                              partBuffer.data(),\n                              gatheredMatrix + offset,\n                              (int)desc.partsToRanks[i]->size,\n                              (int)desc.N,\n                              stream);\n    }\n    offset += desc.partsToRanks[i]->size * desc.N;\n  }\n}\n\ntemplate <typename T>\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<T>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              cudaStream_t stream)\n{\n  auto allocator = rmm::mr::get_current_device_resource_ref();\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize          = desc.partsToRanks[i]->size * desc.N;\n      T* partMem            = (T*)allocator.allocate(stream, partSize * sizeof(T));\n      Matrix::Data<T>* part = new Matrix::Data<T>(partMem, partSize);\n      parts.push_back(part);\n    }\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<T>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                cudaStream_t stream)\n{\n  auto allocator = rmm::mr::get_current_device_resource_ref();\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize = desc.partsToRanks[i]->size * desc.N;\n      allocator.deallocate(stream, parts[localIndex]->ptr, partSize * sizeof(T));\n      localIndex++;\n    }\n  }\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      delete parts[localIndex];\n      localIndex++;\n    }\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<T>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream,\n               T low  = T(-1.0),\n               T high = T(1.0))\n{\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize = desc.partsToRanks[i]->size * desc.N;\n      r.uniform(parts[localIndex]->ptr, partSize, low, high, stream);\n      localIndex++;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<T>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           cudaStream_t stream)\n{\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      size_t partSize = desc.partsToRanks[i]->size * desc.N * sizeof(T);\n      RAFT_CUDA_TRY(cudaMemsetAsync(parts[localIndex]->ptr, 0, partSize, stream));\n      localIndex++;\n    }\n  }\n}\n\ntemplate <typename T>\nstatic __global__ void printRaw2DKernel(T* buffer, int rows, int cols, bool isColMajor)\n{\n  for (int r = 0; r < rows; r++) {\n    for (int c = 0; c < cols; c++) {\n      if (isColMajor) {\n        printf(\"%8.6f \", buffer[c * rows + r]);\n      } else {\n        printf(\"%8.6f \", buffer[r * cols + c]);\n      }\n    }\n    printf(\"\\n\");\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid printRaw2D(T* buffer, int rows, int cols, bool isColMajor, cudaStream_t stream)\n{\n  printRaw2DKernel<<<1, 1, 0, stream>>>(buffer, rows, cols, isColMajor);\n}\n\ntemplate <typename T>\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<T>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           cudaStream_t stream)\n{\n  rmm::device_uvector<T> buffer(desc.M * desc.N, stream);\n  if (myRank == 0) { buffer.resize(desc.M * desc.N, stream); }\n  Matrix::opg::gather(h, buffer.data(), parts, desc, 0, myRank, stream);\n  if (myRank == 0) {\n    printf(\"%s = [\", matrixName);\n    printRaw2D(buffer.data(), desc.M, desc.N, false, stream);\n    RAFT_CUDA_TRY(cudaStreamSynchronize(stream));\n    printf(\"];\\n#-------------------------------\\n\");\n  }\n}\n\n// Instantiations\n\nvoid gatherPart(const raft::handle_t& h,\n                float* gatheredPart,\n                std::vector<Matrix::Data<float>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                cudaStream_t stream)\n{\n  gatherPart<float>(h, gatheredPart, parts, desc, partIndex, rootRank, myRank, stream);\n}\n\nvoid allGatherPart(const raft::handle_t& h,\n                   float* gatheredPart,\n                   std::vector<Matrix::Data<float>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   cudaStream_t stream)\n{\n  allGatherPart<float>(h, gatheredPart, parts, desc, partIndex, myRank, stream);\n}\n\nvoid gather(const raft::handle_t& h,\n            float* gatheredMatrix,\n            std::vector<Matrix::Data<float>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            cudaStream_t stream)\n{\n  gather<float>(h, gatheredMatrix, parts, desc, rootRank, myRank, stream);\n}\n\nvoid allGather(const raft::handle_t& h,\n               float* gatheredMatrix,\n               std::vector<Matrix::Data<float>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream)\n{\n  allGather<float>(h, gatheredMatrix, parts, desc, myRank, stream);\n}\n\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<float>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              cudaStream_t stream)\n{\n  allocate<float>(h, parts, desc, myRank, stream);\n  return;\n}\n\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<float>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                cudaStream_t stream)\n{\n  deallocate<float>(h, parts, desc, myRank, stream);\n}\n\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<float>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream,\n               float low,\n               float high)\n{\n  randomize<float>(h, r, parts, desc, myRank, stream, low, high);\n}\n\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<float>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           cudaStream_t stream)\n{\n  reset<float>(h, parts, desc, myRank, stream);\n}\n\nvoid printRaw2D(float* buffer, int rows, int cols, bool isColMajor, cudaStream_t stream)\n{\n  printRaw2D<float>(buffer, rows, cols, isColMajor, stream);\n}\n\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<float>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           cudaStream_t stream)\n{\n  print<float>(h, parts, desc, matrixName, myRank, stream);\n}\n\nvoid gatherPart(const raft::handle_t& h,\n                double* gatheredPart,\n                std::vector<Matrix::Data<double>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                cudaStream_t stream)\n{\n  gatherPart<double>(h, gatheredPart, parts, desc, partIndex, rootRank, myRank, stream);\n}\n\nvoid allGatherPart(const raft::handle_t& h,\n                   double* gatheredPart,\n                   std::vector<Matrix::Data<double>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   cudaStream_t stream)\n{\n  allGatherPart<double>(h, gatheredPart, parts, desc, partIndex, myRank, stream);\n}\n\nvoid gather(const raft::handle_t& h,\n            double* gatheredMatrix,\n            std::vector<Matrix::Data<double>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            cudaStream_t stream)\n{\n  gather<double>(h, gatheredMatrix, parts, desc, rootRank, myRank, stream);\n}\n\nvoid allGather(const raft::handle_t& h,\n               double* gatheredMatrix,\n               std::vector<Matrix::Data<double>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream)\n{\n  allGather<double>(h, gatheredMatrix, parts, desc, myRank, stream);\n}\n\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<double>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              cudaStream_t stream)\n{\n  allocate<double>(h, parts, desc, myRank, stream);\n}\n\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<double>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                cudaStream_t stream)\n{\n  deallocate<double>(h, parts, desc, myRank, stream);\n}\n\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<double>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               cudaStream_t stream,\n               double low,\n               double high)\n{\n  randomize<double>(h, r, parts, desc, myRank, stream, low, high);\n}\n\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<double>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           cudaStream_t stream)\n{\n  reset<double>(h, parts, desc, myRank, stream);\n}\n\nvoid printRaw2D(double* buffer, int rows, int cols, bool isColMajor, cudaStream_t stream)\n{\n  printRaw2D<double>(buffer, rows, cols, isColMajor, stream);\n}\n\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<double>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           cudaStream_t stream)\n{\n  print<double>(h, parts, desc, matrixName, myRank, stream);\n}\n\n}  // end namespace opg\n}  // end namespace Matrix\n}  // end namespace MLCommon\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n#include \"hip/hip_runtime.h\"\n#include <cuml/prims/opg/matrix/matrix_utils.hpp>\n\n#include <raft/linalg/transpose.cuh>\n#include <raft/util/cudart_utils.hpp>\n\n#include <rmm/device_uvector.hpp>\n#include <rmm/mr/per_device_resource.hpp>\n#include <rmm/resource_ref.hpp>\n\nnamespace MLCommon {\nnamespace Matrix {\nnamespace opg {\n\ntemplate <typename T>\nvoid gatherPart(const raft::handle_t& h,\n                T* gatheredPart,\n                std::vector<Matrix::Data<T>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                hipStream_t stream)\n{\n  ASSERT(partIndex < desc.partsToRanks.size(),\n         \"MLCommon::Matrix::opg::gatherPart: Part index is out of range\");\n  const auto& comm = h.get_comms();\n  std::vector<raft::comms::request_t> requests;\n  int items = desc.partsToRanks[partIndex]->size * desc.N;\n  comm.sync_stream(stream);\n  if (desc.partsToRanks[partIndex]->rank == myRank) {\n    requests.resize(requests.size() + 1);\n    int localIndex = 0;\n    for (int i = 0; i < partIndex; i++) {\n      if (desc.partsToRanks[i]->rank == myRank) { localIndex++; }\n    }\n\n    comm.isend(parts[localIndex]->ptr, items, rootRank, 0, &requests.back());\n  }\n  if (myRank == rootRank) {\n    requests.resize(requests.size() + 1);\n    comm.irecv(gatheredPart, items, desc.partsToRanks[partIndex]->rank, 0, &requests.back());\n  }\n  comm.waitall(requests.size(), requests.data());\n}\n\ntemplate <typename T>\nvoid allGatherPart(const raft::handle_t& h,\n                   T* gatheredPart,\n                   std::vector<Matrix::Data<T>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   hipStream_t stream)\n{\n  ASSERT(partIndex < desc.partsToRanks.size(),\n         \"MLCommon::Matrix::opg::allGatherPart: Part index is out of range\");\n\n  const auto& comm = h.get_comms();\n\n  int partLength = desc.partsToRanks[partIndex]->size * desc.N;\n\n  if (desc.partsToRanks[partIndex]->rank == myRank) {\n    int localIndex = 0;\n    for (int i = 0; i < partIndex; i++) {\n      if (desc.partsToRanks[i]->rank == myRank) { localIndex++; }\n    }\n    raft::copy(gatheredPart, parts[localIndex]->ptr, partLength, stream);\n    comm.sync_stream(stream);\n  }\n  comm.bcast(gatheredPart, partLength, desc.partsToRanks[partIndex]->rank, stream);\n}\n\ntemplate <typename T>\nvoid gather(const raft::handle_t& h,\n            T* gatheredMatrix,\n            std::vector<Matrix::Data<T>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            hipStream_t stream)\n{\n  size_t maxPartSize = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (maxPartSize < desc.partsToRanks[i]->size) { maxPartSize = desc.partsToRanks[i]->size; }\n  }\n\n  rmm::device_uvector<T> partBuffer(0, stream);\n  if (myRank == rootRank) { partBuffer.resize(maxPartSize * desc.N, stream); }\n  size_t offset = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    gatherPart(h, partBuffer.data(), parts, desc, i, rootRank, myRank, stream);\n\n    if (myRank == rootRank) {\n      if (desc.layout == Matrix::LayoutRowMajor) {\n        raft::copy(\n          gatheredMatrix + offset, partBuffer.data(), desc.partsToRanks[i]->size * desc.N, stream);\n      } else {\n        raft::linalg::transpose(h,\n                                partBuffer.data(),\n                                gatheredMatrix + offset,\n                                (int)desc.partsToRanks[i]->size,\n                                (int)desc.N,\n                                stream);\n      }\n    }\n    offset += desc.partsToRanks[i]->size * desc.N;\n  }\n  RAFT_CUDA_TRY(hipStreamSynchronize(stream));\n}\n\ntemplate <typename T>\nvoid allGather(const raft::handle_t& h,\n               T* gatheredMatrix,\n               std::vector<Matrix::Data<T>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream)\n{\n  size_t maxPartSize = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (maxPartSize < desc.partsToRanks[i]->size) { maxPartSize = desc.partsToRanks[i]->size; }\n  }\n\n  rmm::device_uvector<T> partBuffer(maxPartSize * desc.N, stream);\n  size_t offset = 0;\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    allGatherPart(h, partBuffer.data(), parts, desc, i, myRank, stream);\n    if (desc.layout == Matrix::LayoutRowMajor) {\n      raft::copy(\n        gatheredMatrix + offset, partBuffer.data(), desc.partsToRanks[i]->size * desc.N, stream);\n    } else {\n      raft::linalg::transpose(h,\n                              partBuffer.data(),\n                              gatheredMatrix + offset,\n                              (int)desc.partsToRanks[i]->size,\n                              (int)desc.N,\n                              stream);\n    }\n    offset += desc.partsToRanks[i]->size * desc.N;\n  }\n}\n\ntemplate <typename T>\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<T>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              hipStream_t stream)\n{\n  auto allocator = rmm::mr::get_current_device_resource_ref();\n  for (size_t i = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize          = desc.partsToRanks[i]->size * desc.N;\n      T* partMem            = (T*)allocator.allocate(stream, partSize * sizeof(T));\n      Matrix::Data<T>* part = new Matrix::Data<T>(partMem, partSize);\n      parts.push_back(part);\n    }\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<T>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                hipStream_t stream)\n{\n  auto allocator = rmm::mr::get_current_device_resource_ref();\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize = desc.partsToRanks[i]->size * desc.N;\n      allocator.deallocate(stream, parts[localIndex]->ptr, partSize * sizeof(T));\n      localIndex++;\n    }\n  }\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      delete parts[localIndex];\n      localIndex++;\n    }\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<T>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream,\n               T low  = T(-1.0),\n               T high = T(1.0))\n{\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      int partSize = desc.partsToRanks[i]->size * desc.N;\n      r.uniform(parts[localIndex]->ptr, partSize, low, high, stream);\n      localIndex++;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<T>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           hipStream_t stream)\n{\n  for (size_t i = 0, localIndex = 0; i < desc.partsToRanks.size(); i++) {\n    if (myRank == desc.partsToRanks[i]->rank) {\n      size_t partSize = desc.partsToRanks[i]->size * desc.N * sizeof(T);\n      RAFT_CUDA_TRY(hipMemsetAsync(parts[localIndex]->ptr, 0, partSize, stream));\n      localIndex++;\n    }\n  }\n}\n\ntemplate <typename T>\nstatic __global__ void printRaw2DKernel(T* buffer, int rows, int cols, bool isColMajor)\n{\n  for (int r = 0; r < rows; r++) {\n    for (int c = 0; c < cols; c++) {\n      if (isColMajor) {\n        printf(\"%8.6f \", buffer[c * rows + r]);\n      } else {\n        printf(\"%8.6f \", buffer[r * cols + c]);\n      }\n    }\n    printf(\"\\n\");\n  }\n  return;\n}\n\ntemplate <typename T>\nvoid printRaw2D(T* buffer, int rows, int cols, bool isColMajor, hipStream_t stream)\n{\n  printRaw2DKernel<<<1, 1, 0, stream>>>(buffer, rows, cols, isColMajor);\n}\n\ntemplate <typename T>\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<T>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           hipStream_t stream)\n{\n  rmm::device_uvector<T> buffer(desc.M * desc.N, stream);\n  if (myRank == 0) { buffer.resize(desc.M * desc.N, stream); }\n  Matrix::opg::gather(h, buffer.data(), parts, desc, 0, myRank, stream);\n  if (myRank == 0) {\n    printf(\"%s = [\", matrixName);\n    printRaw2D(buffer.data(), desc.M, desc.N, false, stream);\n    RAFT_CUDA_TRY(hipStreamSynchronize(stream));\n    printf(\"];\\n#-------------------------------\\n\");\n  }\n}\n\n// Instantiations\n\nvoid gatherPart(const raft::handle_t& h,\n                float* gatheredPart,\n                std::vector<Matrix::Data<float>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                hipStream_t stream)\n{\n  gatherPart<float>(h, gatheredPart, parts, desc, partIndex, rootRank, myRank, stream);\n}\n\nvoid allGatherPart(const raft::handle_t& h,\n                   float* gatheredPart,\n                   std::vector<Matrix::Data<float>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   hipStream_t stream)\n{\n  allGatherPart<float>(h, gatheredPart, parts, desc, partIndex, myRank, stream);\n}\n\nvoid gather(const raft::handle_t& h,\n            float* gatheredMatrix,\n            std::vector<Matrix::Data<float>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            hipStream_t stream)\n{\n  gather<float>(h, gatheredMatrix, parts, desc, rootRank, myRank, stream);\n}\n\nvoid allGather(const raft::handle_t& h,\n               float* gatheredMatrix,\n               std::vector<Matrix::Data<float>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream)\n{\n  allGather<float>(h, gatheredMatrix, parts, desc, myRank, stream);\n}\n\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<float>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              hipStream_t stream)\n{\n  allocate<float>(h, parts, desc, myRank, stream);\n  return;\n}\n\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<float>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                hipStream_t stream)\n{\n  deallocate<float>(h, parts, desc, myRank, stream);\n}\n\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<float>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream,\n               float low,\n               float high)\n{\n  randomize<float>(h, r, parts, desc, myRank, stream, low, high);\n}\n\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<float>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           hipStream_t stream)\n{\n  reset<float>(h, parts, desc, myRank, stream);\n}\n\nvoid printRaw2D(float* buffer, int rows, int cols, bool isColMajor, hipStream_t stream)\n{\n  printRaw2D<float>(buffer, rows, cols, isColMajor, stream);\n}\n\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<float>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           hipStream_t stream)\n{\n  print<float>(h, parts, desc, matrixName, myRank, stream);\n}\n\nvoid gatherPart(const raft::handle_t& h,\n                double* gatheredPart,\n                std::vector<Matrix::Data<double>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int partIndex,\n                int rootRank,\n                int myRank,\n                hipStream_t stream)\n{\n  gatherPart<double>(h, gatheredPart, parts, desc, partIndex, rootRank, myRank, stream);\n}\n\nvoid allGatherPart(const raft::handle_t& h,\n                   double* gatheredPart,\n                   std::vector<Matrix::Data<double>*>& parts,\n                   Matrix::PartDescriptor& desc,\n                   int partIndex,\n                   int myRank,\n                   hipStream_t stream)\n{\n  allGatherPart<double>(h, gatheredPart, parts, desc, partIndex, myRank, stream);\n}\n\nvoid gather(const raft::handle_t& h,\n            double* gatheredMatrix,\n            std::vector<Matrix::Data<double>*>& parts,\n            Matrix::PartDescriptor& desc,\n            int rootRank,\n            int myRank,\n            hipStream_t stream)\n{\n  gather<double>(h, gatheredMatrix, parts, desc, rootRank, myRank, stream);\n}\n\nvoid allGather(const raft::handle_t& h,\n               double* gatheredMatrix,\n               std::vector<Matrix::Data<double>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream)\n{\n  allGather<double>(h, gatheredMatrix, parts, desc, myRank, stream);\n}\n\nvoid allocate(const raft::handle_t& h,\n              std::vector<Matrix::Data<double>*>& parts,\n              Matrix::PartDescriptor& desc,\n              int myRank,\n              hipStream_t stream)\n{\n  allocate<double>(h, parts, desc, myRank, stream);\n}\n\nvoid deallocate(const raft::handle_t& h,\n                std::vector<Matrix::Data<double>*>& parts,\n                Matrix::PartDescriptor& desc,\n                int myRank,\n                hipStream_t stream)\n{\n  deallocate<double>(h, parts, desc, myRank, stream);\n}\n\nvoid randomize(const raft::handle_t& h,\n               raft::random::Rng& r,\n               std::vector<Matrix::Data<double>*>& parts,\n               Matrix::PartDescriptor& desc,\n               int myRank,\n               hipStream_t stream,\n               double low,\n               double high)\n{\n  randomize<double>(h, r, parts, desc, myRank, stream, low, high);\n}\n\nvoid reset(const raft::handle_t& h,\n           std::vector<Matrix::Data<double>*>& parts,\n           Matrix::PartDescriptor& desc,\n           int myRank,\n           hipStream_t stream)\n{\n  reset<double>(h, parts, desc, myRank, stream);\n}\n\nvoid printRaw2D(double* buffer, int rows, int cols, bool isColMajor, hipStream_t stream)\n{\n  printRaw2D<double>(buffer, rows, cols, isColMajor, stream);\n}\n\nvoid print(const raft::handle_t& h,\n           std::vector<Matrix::Data<double>*>& parts,\n           Matrix::PartDescriptor& desc,\n           const char* matrixName,\n           int myRank,\n           hipStream_t stream)\n{\n  print<double>(h, parts, desc, matrixName, myRank, stream);\n}\n\n}  // end namespace opg\n}  // end namespace Matrix\n}  // end namespace MLCommon\n", "source": "/kaggle/working/cuml/cpp/src_prims/opg/matrix/matrix_utils.cu"}
{"input": "#include <thrust/detail/config.h>\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/system/cuda/execution_policy.h>\n#include <cassert>\n\n#if THRUST_CPP_DIALECT >= 2011\n#include <future>\n#endif\n\n// This example demonstrates two ways to achieve algorithm invocations that are asynchronous with\n// the calling thread.\n//\n// The first method wraps a call to thrust::reduce inside a __global__ function. Since __global__ function\n// launches are asynchronous with the launching thread, this achieves asynchrony. The result of the reduction\n// is stored to a pointer to CUDA global memory. The calling thread waits for the result of the reduction to \n// be ready by synchronizing with the CUDA stream on which the __global__ function is launched.\n//\n// The second method uses the C++11 library function, std::async, to create concurrency. The lambda function\n// given to std::async returns the result of thrust::reduce to a std::future. The calling thread can use the\n// std::future to wait for the result of the reduction. This method requires a compiler which supports\n// C++11-capable language and library constructs.\n\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\ntemplate<typename Iterator, typename T, typename BinaryOperation, typename Pointer>\n__global__ void reduce_kernel(Iterator first, Iterator last, T init, BinaryOperation binary_op, Pointer result)\n{\n  *result = thrust::reduce(thrust::cuda::par, first, last, init, binary_op);\n}\n#endif\n\nint main()\n{\n  size_t n = 1 << 20;\n  thrust::device_vector<unsigned int> data(n, 1);\n  thrust::device_vector<unsigned int> result(1, 0);\n\n  // method 1: call thrust::reduce from an asynchronous CUDA kernel launch\n\n  // create a CUDA stream \n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // launch a CUDA kernel with only 1 thread on our stream\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\n  reduce_kernel<<<1,1,0,s>>>(data.begin(), data.end(), 0, thrust::plus<int>(), result.data());\n#else\n  result[0] = thrust::reduce(thrust::cuda::par, data.begin(), data.end(), 0, thrust::plus<int>());\n#endif\n\n  // wait for the stream to finish\n  cudaStreamSynchronize(s);\n\n  // our result should be ready\n  assert(result[0] == n);\n\n  cudaStreamDestroy(s);\n\n  // reset the result\n  result[0] = 0;\n\n#if THRUST_CPP_DIALECT >= 2011\n  // method 2: use std::async to create asynchrony\n\n  // copy all the algorithm parameters\n  auto begin        = data.begin();\n  auto end          = data.end();\n  unsigned int init = 0;\n  auto binary_op    = thrust::plus<unsigned int>();\n\n  // std::async captures the algorithm parameters by value\n  // use std::launch::async to ensure the creation of a new thread\n  std::future<unsigned int> future_result = std::async(std::launch::async, [=]\n  {\n    return thrust::reduce(begin, end, init, binary_op);\n  });\n\n  // wait on the result and check that it is correct\n  assert(future_result.get() == n);\n#endif\n\n  return 0;\n}\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/detail/config.h>\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/system/cuda/execution_policy.h>\n#include <cassert>\n\n#if THRUST_CPP_DIALECT >= 2011\n#include <future>\n#endif\n\n// This example demonstrates two ways to achieve algorithm invocations that are asynchronous with\n// the calling thread.\n//\n// The first method wraps a call to thrust::reduce inside a __global__ function. Since __global__ function\n// launches are asynchronous with the launching thread, this achieves asynchrony. The result of the reduction\n// is stored to a pointer to CUDA global memory. The calling thread waits for the result of the reduction to \n// be ready by synchronizing with the CUDA stream on which the __global__ function is launched.\n//\n// The second method uses the C++11 library function, std::async, to create concurrency. The lambda function\n// given to std::async returns the result of thrust::reduce to a std::future. The calling thread can use the\n// std::future to wait for the result of the reduction. This method requires a compiler which supports\n// C++11-capable language and library constructs.\n\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\ntemplate<typename Iterator, typename T, typename BinaryOperation, typename Pointer>\n__global__ void reduce_kernel(Iterator first, Iterator last, T init, BinaryOperation binary_op, Pointer result)\n{\n  *result = thrust::reduce(thrust::cuda::par, first, last, init, binary_op);\n}\n#endif\n\nint main()\n{\n  size_t n = 1 << 20;\n  thrust::device_vector<unsigned int> data(n, 1);\n  thrust::device_vector<unsigned int> result(1, 0);\n\n  // method 1: call thrust::reduce from an asynchronous CUDA kernel launch\n\n  // create a CUDA stream \n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // launch a CUDA kernel with only 1 thread on our stream\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\n  reduce_kernel<<<1,1,0,s>>>(data.begin(), data.end(), 0, thrust::plus<int>(), result.data());\n#else\n  result[0] = thrust::reduce(thrust::cuda::par, data.begin(), data.end(), 0, thrust::plus<int>());\n#endif\n\n  // wait for the stream to finish\n  hipStreamSynchronize(s);\n\n  // our result should be ready\n  assert(result[0] == n);\n\n  hipStreamDestroy(s);\n\n  // reset the result\n  result[0] = 0;\n\n#if THRUST_CPP_DIALECT >= 2011\n  // method 2: use std::async to create asynchrony\n\n  // copy all the algorithm parameters\n  auto begin        = data.begin();\n  auto end          = data.end();\n  unsigned int init = 0;\n  auto binary_op    = thrust::plus<unsigned int>();\n\n  // std::async captures the algorithm parameters by value\n  // use std::launch::async to ensure the creation of a new thread\n  std::future<unsigned int> future_result = std::async(std::launch::async, [=]\n  {\n    return thrust::reduce(begin, end, init, binary_op);\n  });\n\n  // wait on the result and check that it is correct\n  assert(future_result.get() == n);\n#endif\n\n  return 0;\n}\n\n", "source": "/kaggle/working/thrust/examples/cuda/async_reduce.cu"}
{"input": "#include <unittest/unittest.h>\n\n#include <thrust/device_vector.h>\n#include <thrust/device_ptr.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n\n\nTHRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_BEGIN\n\n\ntemplate <typename Iterator1, typename Iterator2>\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n__global__\n#endif \nvoid simple_copy_on_device(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n    while(first1 != last1)\n        *(first2++) = *(first1++);\n}\n\ntemplate <typename Iterator1, typename Iterator2>\nvoid simple_copy(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n    simple_copy_on_device<<<1,1>>>(first1, last1, first2);\n#else\n    simple_copy_on_device(first1, last1, first2);\n#endif\n}\n\n\nvoid TestDeviceDereferenceDeviceVectorIterator(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    simple_copy(input.begin(), input.end(), output.begin());\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDeviceVectorIterator);\n\nvoid TestDeviceDereferenceDevicePtr(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    thrust::device_ptr<int> _first1 = &input[0];\n    thrust::device_ptr<int> _last1  = _first1 + input.size();\n    thrust::device_ptr<int> _first2 = &output[0];\n\n    simple_copy(_first1, _last1, _first2);\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDevicePtr);\n\nvoid TestDeviceDereferenceTransformIterator(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    simple_copy(thrust::make_transform_iterator(input.begin(), thrust::identity<int>()),\n                thrust::make_transform_iterator(input.end (),  thrust::identity<int>()),\n                output.begin());\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIterator);\n\nvoid TestDeviceDereferenceCountingIterator(void)\n{\n    thrust::counting_iterator<int> first(1);\n    thrust::counting_iterator<int> last(6);\n\n    thrust::device_vector<int> output(5);\n\n    simple_copy(first, last, output.begin());\n\n    ASSERT_EQUAL(output[0], 1);\n    ASSERT_EQUAL(output[1], 2);\n    ASSERT_EQUAL(output[2], 3);\n    ASSERT_EQUAL(output[3], 4);\n    ASSERT_EQUAL(output[4], 5);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceCountingIterator);\n\nvoid TestDeviceDereferenceTransformedCountingIterator(void)\n{\n    thrust::counting_iterator<int> first(1);\n    thrust::counting_iterator<int> last(6);\n\n    thrust::device_vector<int> output(5);\n\n    simple_copy(thrust::make_transform_iterator(first, thrust::negate<int>()),\n                thrust::make_transform_iterator(last,  thrust::negate<int>()),\n                output.begin());\n\n    ASSERT_EQUAL(output[0], -1);\n    ASSERT_EQUAL(output[1], -2);\n    ASSERT_EQUAL(output[2], -3);\n    ASSERT_EQUAL(output[3], -4);\n    ASSERT_EQUAL(output[4], -5);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformedCountingIterator);\n\nTHRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_END\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n\n#include <thrust/device_vector.h>\n#include <thrust/device_ptr.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/iterator/counting_iterator.h>\n\n\nTHRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_BEGIN\n\n\ntemplate <typename Iterator1, typename Iterator2>\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n__global__\n#endif \nvoid simple_copy_on_device(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n    while(first1 != last1)\n        *(first2++) = *(first1++);\n}\n\ntemplate <typename Iterator1, typename Iterator2>\nvoid simple_copy(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n    simple_copy_on_device<<<1,1>>>(first1, last1, first2);\n#else\n    simple_copy_on_device(first1, last1, first2);\n#endif\n}\n\n\nvoid TestDeviceDereferenceDeviceVectorIterator(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    simple_copy(input.begin(), input.end(), output.begin());\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDeviceVectorIterator);\n\nvoid TestDeviceDereferenceDevicePtr(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    thrust::device_ptr<int> _first1 = &input[0];\n    thrust::device_ptr<int> _last1  = _first1 + input.size();\n    thrust::device_ptr<int> _first2 = &output[0];\n\n    simple_copy(_first1, _last1, _first2);\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDevicePtr);\n\nvoid TestDeviceDereferenceTransformIterator(void)\n{\n    thrust::device_vector<int> input = unittest::random_integers<int>(100); \n    thrust::device_vector<int> output(input.size(), 0);\n\n    simple_copy(thrust::make_transform_iterator(input.begin(), thrust::identity<int>()),\n                thrust::make_transform_iterator(input.end (),  thrust::identity<int>()),\n                output.begin());\n\n    ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIterator);\n\nvoid TestDeviceDereferenceCountingIterator(void)\n{\n    thrust::counting_iterator<int> first(1);\n    thrust::counting_iterator<int> last(6);\n\n    thrust::device_vector<int> output(5);\n\n    simple_copy(first, last, output.begin());\n\n    ASSERT_EQUAL(output[0], 1);\n    ASSERT_EQUAL(output[1], 2);\n    ASSERT_EQUAL(output[2], 3);\n    ASSERT_EQUAL(output[3], 4);\n    ASSERT_EQUAL(output[4], 5);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceCountingIterator);\n\nvoid TestDeviceDereferenceTransformedCountingIterator(void)\n{\n    thrust::counting_iterator<int> first(1);\n    thrust::counting_iterator<int> last(6);\n\n    thrust::device_vector<int> output(5);\n\n    simple_copy(thrust::make_transform_iterator(first, thrust::negate<int>()),\n                thrust::make_transform_iterator(last,  thrust::negate<int>()),\n                output.begin());\n\n    ASSERT_EQUAL(output[0], -1);\n    ASSERT_EQUAL(output[1], -2);\n    ASSERT_EQUAL(output[2], -3);\n    ASSERT_EQUAL(output[3], -4);\n    ASSERT_EQUAL(output[4], -5);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformedCountingIterator);\n\nTHRUST_DISABLE_MSVC_POSSIBLE_LOSS_OF_DATA_WARNING_END\n", "source": "/kaggle/working/thrust/testing/dereference.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/unique.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_equal_div_10_unique\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\ntemplate<typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\n\ntemplate<typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0; \n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid unique_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid unique_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector keys;\n  Vector values;\n  \n  typedef thrust::pair<typename Vector::iterator, typename Vector::iterator> iter_pair;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 20);\n  ASSERT_EQUAL(keys[3], 21);\n  ASSERT_EQUAL(keys[4], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 3);\n  ASSERT_EQUAL(values[3], 4);\n  ASSERT_EQUAL(values[4], 7);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 7);\n}\n\nvoid TestUniqueByKeyDeviceSeq()\n{\n  TestUniqueByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceSeq);\n\n\nvoid TestUniqueByKeyDeviceDevice()\n{\n  TestUniqueByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceDevice);\n\n\nvoid TestUniqueByKeyDeviceNoSync()\n{\n  TestUniqueByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector keys;\n  Vector values;\n  \n  typedef thrust::pair<Vector::iterator, Vector::iterator> iter_pair;\n  iter_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 20);\n  ASSERT_EQUAL(keys[3], 21);\n  ASSERT_EQUAL(keys[4], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 3);\n  ASSERT_EQUAL(values[3], 4);\n  ASSERT_EQUAL(values[4], 7);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>());\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 7);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueByKeyCudaStreamsSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsSync);\n\n\nvoid TestUniqueByKeyCudaStreamsNoSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5>\n__global__\nvoid unique_by_key_copy_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 keys_result, Iterator4 values_result, Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename Iterator5>\n__global__\nvoid unique_by_key_copy_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 keys_result, Iterator4 values_result, BinaryPredicate pred, Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  typedef thrust::pair<typename Vector::iterator, typename Vector::iterator> iter_pair;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  unique_by_key_copy_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 4);\n  ASSERT_EQUAL(output_values[4], 7);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_copy_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 7);\n}\n\n\nvoid TestUniqueCopyByKeyDeviceSeq()\n{\n  TestUniqueCopyByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceSeq);\n\n\nvoid TestUniqueCopyByKeyDeviceDevice()\n{\n  TestUniqueCopyByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceDevice);\n\n\nvoid TestUniqueCopyByKeyDeviceNoSync()\n{\n  TestUniqueCopyByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  typedef thrust::pair<Vector::iterator, Vector::iterator> iter_pair;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key_copy(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 4);\n  ASSERT_EQUAL(output_values[4], 7);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::unique_by_key_copy(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_unique<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 7);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCopyByKeyCudaStreamsSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsSync);\n\n\nvoid TestUniqueCopyByKeyCudaStreamsNoSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/unique.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_equal_div_10_unique\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\ntemplate<typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\n\ntemplate<typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0; \n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid unique_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid unique_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector keys;\n  Vector values;\n  \n  typedef thrust::pair<typename Vector::iterator, typename Vector::iterator> iter_pair;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 20);\n  ASSERT_EQUAL(keys[3], 21);\n  ASSERT_EQUAL(keys[4], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 3);\n  ASSERT_EQUAL(values[3], 4);\n  ASSERT_EQUAL(values[4], 7);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 7);\n}\n\nvoid TestUniqueByKeyDeviceSeq()\n{\n  TestUniqueByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceSeq);\n\n\nvoid TestUniqueByKeyDeviceDevice()\n{\n  TestUniqueByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceDevice);\n\n\nvoid TestUniqueByKeyDeviceNoSync()\n{\n  TestUniqueByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector keys;\n  Vector values;\n  \n  typedef thrust::pair<Vector::iterator, Vector::iterator> iter_pair;\n  iter_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 20);\n  ASSERT_EQUAL(keys[3], 21);\n  ASSERT_EQUAL(keys[4], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 3);\n  ASSERT_EQUAL(values[3], 4);\n  ASSERT_EQUAL(values[4], 7);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>());\n  \n  ASSERT_EQUAL(new_last.first  - keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  ASSERT_EQUAL(keys[0], 11);\n  ASSERT_EQUAL(keys[1], 21);\n  ASSERT_EQUAL(keys[2], 37);\n  \n  ASSERT_EQUAL(values[0], 0);\n  ASSERT_EQUAL(values[1], 2);\n  ASSERT_EQUAL(values[2], 7);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueByKeyCudaStreamsSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsSync);\n\n\nvoid TestUniqueByKeyCudaStreamsNoSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5>\n__global__\nvoid unique_by_key_copy_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 keys_result, Iterator4 values_result, Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename Iterator5>\n__global__\nvoid unique_by_key_copy_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 keys_result, Iterator4 values_result, BinaryPredicate pred, Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  typedef thrust::pair<typename Vector::iterator, typename Vector::iterator> iter_pair;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  unique_by_key_copy_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 4);\n  ASSERT_EQUAL(output_values[4], 7);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  unique_by_key_copy_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 7);\n}\n\n\nvoid TestUniqueCopyByKeyDeviceSeq()\n{\n  TestUniqueCopyByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceSeq);\n\n\nvoid TestUniqueCopyByKeyDeviceDevice()\n{\n  TestUniqueCopyByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceDevice);\n\n\nvoid TestUniqueCopyByKeyDeviceNoSync()\n{\n  TestUniqueCopyByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  typedef thrust::pair<Vector::iterator, Vector::iterator> iter_pair;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key_copy(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 4);\n  ASSERT_EQUAL(output_values[4], 7);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::unique_by_key_copy(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_unique<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0], 0);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 7);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCopyByKeyCudaStreamsSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsSync);\n\n\nvoid TestUniqueCopyByKeyCudaStreamsNoSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/unique_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/mismatch.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void mismatch_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::mismatch(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMismatchDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a(4);\n  thrust::device_vector<int> b(4);\n  a[0] = 1; b[0] = 1;\n  a[1] = 2; b[1] = 2;\n  a[2] = 3; b[2] = 4;\n  a[3] = 4; b[3] = 3;\n\n  typedef thrust::pair<\n    typename thrust::device_vector<int>::iterator,\n    typename thrust::device_vector<int>::iterator\n  > pair_type;\n\n  thrust::device_vector<pair_type> d_result(1);\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(2, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(2, ((pair_type)d_result[0]).second - b.begin());\n  \n  b[2] = 3;\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(3, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(3, ((pair_type)d_result[0]).second - b.begin());\n  \n  b[3] = 4;\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(4, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(4, ((pair_type)d_result[0]).second - b.begin());\n}\n\n\nvoid TestMismatchDeviceSeq()\n{\n  TestMismatchDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMismatchDeviceSeq);\n\n\nvoid TestMismatchDeviceDevice()\n{\n  TestMismatchDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMismatchDeviceDevice);\n#endif\n\n\nvoid TestMismatchCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector a(4); Vector b(4);\n  a[0] = 1; b[0] = 1;\n  a[1] = 2; b[1] = 2;\n  a[2] = 3; b[2] = 4;\n  a[3] = 4; b[3] = 3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 2);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 2);\n\n  b[2] = 3;\n  \n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 3);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 3);\n  \n  b[3] = 4;\n  \n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 4);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMismatchCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/mismatch.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void mismatch_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::mismatch(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMismatchDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a(4);\n  thrust::device_vector<int> b(4);\n  a[0] = 1; b[0] = 1;\n  a[1] = 2; b[1] = 2;\n  a[2] = 3; b[2] = 4;\n  a[3] = 4; b[3] = 3;\n\n  typedef thrust::pair<\n    typename thrust::device_vector<int>::iterator,\n    typename thrust::device_vector<int>::iterator\n  > pair_type;\n\n  thrust::device_vector<pair_type> d_result(1);\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(2, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(2, ((pair_type)d_result[0]).second - b.begin());\n  \n  b[2] = 3;\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(3, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(3, ((pair_type)d_result[0]).second - b.begin());\n  \n  b[3] = 4;\n  \n  mismatch_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(4, ((pair_type)d_result[0]).first  - a.begin());\n  ASSERT_EQUAL(4, ((pair_type)d_result[0]).second - b.begin());\n}\n\n\nvoid TestMismatchDeviceSeq()\n{\n  TestMismatchDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMismatchDeviceSeq);\n\n\nvoid TestMismatchDeviceDevice()\n{\n  TestMismatchDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMismatchDeviceDevice);\n#endif\n\n\nvoid TestMismatchCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector a(4); Vector b(4);\n  a[0] = 1; b[0] = 1;\n  a[1] = 2; b[1] = 2;\n  a[2] = 3; b[2] = 4;\n  a[3] = 4; b[3] = 3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 2);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 2);\n\n  b[2] = 3;\n  \n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 3);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 3);\n  \n  b[3] = 4;\n  \n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first  - a.begin(), 4);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMismatchCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/mismatch.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/inner_product.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__\nvoid inner_product_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, T init, Iterator3 result)\n{\n  *result = thrust::inner_product(exec, first1, last1, first2, init);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestInnerProductDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_v1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_v2 = unittest::random_integers<int>(n);\n  \n  thrust::device_vector<int> d_v1 = h_v1;\n  thrust::device_vector<int> d_v2 = h_v2;\n  \n  thrust::device_vector<int> result(1);\n  \n  int init = 13;\n  \n  int expected = thrust::inner_product(h_v1.begin(), h_v1.end(), h_v2.begin(), init);\n\n  inner_product_kernel<<<1,1>>>(exec, d_v1.begin(), d_v1.end(), d_v2.begin(), init, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(expected, result[0]);\n}\n\n\nvoid TestInnerProductDeviceSeq()\n{\n  TestInnerProductDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceSeq);\n\n\nvoid TestInnerProductDeviceDevice()\n{\n  TestInnerProductDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceDevice);\n#endif\n\n\nvoid TestInnerProductCudaStreams()\n{\n  thrust::device_vector<int> v1(3);\n  thrust::device_vector<int> v2(3);\n  v1[0] =  1; v1[1] = -2; v1[2] =  3;\n  v2[0] = -4; v2[1] =  5; v2[2] =  6;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  int init = 3;\n  int result = thrust::inner_product(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), init);\n  ASSERT_EQUAL(result, 7);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInnerProductCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/inner_product.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__\nvoid inner_product_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, T init, Iterator3 result)\n{\n  *result = thrust::inner_product(exec, first1, last1, first2, init);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestInnerProductDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_v1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_v2 = unittest::random_integers<int>(n);\n  \n  thrust::device_vector<int> d_v1 = h_v1;\n  thrust::device_vector<int> d_v2 = h_v2;\n  \n  thrust::device_vector<int> result(1);\n  \n  int init = 13;\n  \n  int expected = thrust::inner_product(h_v1.begin(), h_v1.end(), h_v2.begin(), init);\n\n  inner_product_kernel<<<1,1>>>(exec, d_v1.begin(), d_v1.end(), d_v2.begin(), init, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(expected, result[0]);\n}\n\n\nvoid TestInnerProductDeviceSeq()\n{\n  TestInnerProductDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceSeq);\n\n\nvoid TestInnerProductDeviceDevice()\n{\n  TestInnerProductDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceDevice);\n#endif\n\n\nvoid TestInnerProductCudaStreams()\n{\n  thrust::device_vector<int> v1(3);\n  thrust::device_vector<int> v2(3);\n  v1[0] =  1; v1[1] = -2; v1[2] =  3;\n  v2[0] = -4; v2[1] =  5; v2[2] =  6;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  int init = 3;\n  int result = thrust::inner_product(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), init);\n  ASSERT_EQUAL(result, 7);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInnerProductCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/inner_product.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/unique.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_equal_div_10_unique\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n  \n  unique_kernel<<<1,1>>>(exec, data.begin(), data.end(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 12);\n  ASSERT_EQUAL(data[2], 20);\n  ASSERT_EQUAL(data[3], 29);\n  ASSERT_EQUAL(data[4], 21);\n  ASSERT_EQUAL(data[5], 31);\n  ASSERT_EQUAL(data[6], 37);\n\n  unique_kernel<<<1,1>>>(exec, data.begin(), new_last, is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n}\n\n\nvoid TestUniqueDeviceSeq()\n{\n  TestUniqueDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueDeviceSeq);\n\n\nvoid TestUniqueDeviceDevice()\n{\n  TestUniqueDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueDeviceDevice);\n\n\nvoid TestUniqueDeviceNoSync()\n{\n  TestUniqueDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique(streampolicy, data.begin(), data.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 12);\n  ASSERT_EQUAL(data[2], 20);\n  ASSERT_EQUAL(data[3], 29);\n  ASSERT_EQUAL(data[4], 21);\n  ASSERT_EQUAL(data[5], 31);\n  ASSERT_EQUAL(data[6], 37);\n\n  new_last = thrust::unique(streampolicy, data.begin(), new_last, is_equal_div_10_unique<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCudaStreamsSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsSync);\n\n\nvoid TestUniqueCudaStreamsNoSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid unique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid unique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, BinaryPredicate pred, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n  \n  unique_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), output.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  ASSERT_EQUAL(output[0], 11);\n  ASSERT_EQUAL(output[1], 12);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 29);\n  ASSERT_EQUAL(output[4], 21);\n  ASSERT_EQUAL(output[5], 31);\n  ASSERT_EQUAL(output[6], 37);\n\n  unique_copy_kernel<<<1,1>>>(exec, output.begin(), new_last, data.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n}\n\n\nvoid TestUniqueCopyDeviceSeq()\n{\n  TestUniqueCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceSeq);\n\n\nvoid TestUniqueCopyDeviceDevice()\n{\n  TestUniqueCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceDevice);\n\n\nvoid TestUniqueCopyDeviceNoSync()\n{\n  TestUniqueCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique_copy(streampolicy, data.begin(), data.end(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  ASSERT_EQUAL(output[0], 11);\n  ASSERT_EQUAL(output[1], 12);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 29);\n  ASSERT_EQUAL(output[4], 21);\n  ASSERT_EQUAL(output[5], 31);\n  ASSERT_EQUAL(output[6], 37);\n\n  new_last = thrust::unique_copy(streampolicy, output.begin(), new_last, data.begin(), is_equal_div_10_unique<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCopyCudaStreamsSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsSync);\n\n\nvoid TestUniqueCopyCudaStreamsNoSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCountDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(1, -1);\n  \n  unique_count_kernel<<<1,1>>>(exec, data.begin(), data.end(), output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 7);\n\n  unique_count_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_equal_div_10_unique<T>(), output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 3);\n}\n\n\nvoid TestUniqueCountDeviceSeq()\n{\n  TestUniqueCountDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceSeq);\n\n\nvoid TestUniqueCountDeviceDevice()\n{\n  TestUniqueCountDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceDevice);\n\n\nvoid TestUniqueCountDeviceNoSync()\n{\n  TestUniqueCountDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCountCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  int result = thrust::unique_count(streampolicy, data.begin(), data.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 7);\n\n  result = thrust::unique_count(streampolicy, data.begin(), data.end(), is_equal_div_10_unique<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 3);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCountCudaStreamsSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsSync);\n\n\nvoid TestUniqueCountCudaStreamsNoSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/unique.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_equal_div_10_unique\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n  \n  unique_kernel<<<1,1>>>(exec, data.begin(), data.end(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 12);\n  ASSERT_EQUAL(data[2], 20);\n  ASSERT_EQUAL(data[3], 29);\n  ASSERT_EQUAL(data[4], 21);\n  ASSERT_EQUAL(data[5], 31);\n  ASSERT_EQUAL(data[6], 37);\n\n  unique_kernel<<<1,1>>>(exec, data.begin(), new_last, is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n}\n\n\nvoid TestUniqueDeviceSeq()\n{\n  TestUniqueDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueDeviceSeq);\n\n\nvoid TestUniqueDeviceDevice()\n{\n  TestUniqueDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueDeviceDevice);\n\n\nvoid TestUniqueDeviceNoSync()\n{\n  TestUniqueDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique(streampolicy, data.begin(), data.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 12);\n  ASSERT_EQUAL(data[2], 20);\n  ASSERT_EQUAL(data[3], 29);\n  ASSERT_EQUAL(data[4], 21);\n  ASSERT_EQUAL(data[5], 31);\n  ASSERT_EQUAL(data[6], 37);\n\n  new_last = thrust::unique(streampolicy, data.begin(), new_last, is_equal_div_10_unique<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCudaStreamsSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsSync);\n\n\nvoid TestUniqueCudaStreamsNoSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid unique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid unique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, BinaryPredicate pred, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n  \n  unique_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), output.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  ASSERT_EQUAL(output[0], 11);\n  ASSERT_EQUAL(output[1], 12);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 29);\n  ASSERT_EQUAL(output[4], 21);\n  ASSERT_EQUAL(output[5], 31);\n  ASSERT_EQUAL(output[6], 37);\n\n  unique_copy_kernel<<<1,1>>>(exec, output.begin(), new_last, data.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n}\n\n\nvoid TestUniqueCopyDeviceSeq()\n{\n  TestUniqueCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceSeq);\n\n\nvoid TestUniqueCopyDeviceDevice()\n{\n  TestUniqueCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceDevice);\n\n\nvoid TestUniqueCopyDeviceNoSync()\n{\n  TestUniqueCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCopyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  new_last = thrust::unique_copy(streampolicy, data.begin(), data.end(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  ASSERT_EQUAL(output[0], 11);\n  ASSERT_EQUAL(output[1], 12);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 29);\n  ASSERT_EQUAL(output[4], 21);\n  ASSERT_EQUAL(output[5], 31);\n  ASSERT_EQUAL(output[6], 37);\n\n  new_last = thrust::unique_copy(streampolicy, output.begin(), new_last, data.begin(), is_equal_div_10_unique<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  ASSERT_EQUAL(data[0], 11);\n  ASSERT_EQUAL(data[1], 20);\n  ASSERT_EQUAL(data[2], 31);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCopyCudaStreamsSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsSync);\n\n\nvoid TestUniqueCopyCudaStreamsNoSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCountDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37; \n  \n  Vector output(1, -1);\n  \n  unique_count_kernel<<<1,1>>>(exec, data.begin(), data.end(), output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 7);\n\n  unique_count_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_equal_div_10_unique<T>(), output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 3);\n}\n\n\nvoid TestUniqueCountDeviceSeq()\n{\n  TestUniqueCountDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceSeq);\n\n\nvoid TestUniqueCountDeviceDevice()\n{\n  TestUniqueCountDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceDevice);\n\n\nvoid TestUniqueCountDeviceNoSync()\n{\n  TestUniqueCountDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUniqueCountCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(10);\n  data[0] = 11; \n  data[1] = 11; \n  data[2] = 12;\n  data[3] = 20; \n  data[4] = 29; \n  data[5] = 21; \n  data[6] = 21; \n  data[7] = 31; \n  data[8] = 31; \n  data[9] = 37;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  int result = thrust::unique_count(streampolicy, data.begin(), data.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 7);\n\n  result = thrust::unique_count(streampolicy, data.begin(), data.end(), is_equal_div_10_unique<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 3);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCountCudaStreamsSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsSync);\n\n\nvoid TestUniqueCountCudaStreamsNoSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/unique.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_symmetric_difference_kernel(ExecutionPolicy exec,\n                                     Iterator1 first1, Iterator1 last1,\n                                     Iterator2 first2, Iterator2 last2,\n                                     Iterator3 result1,\n                                     Iterator4 result2)\n{\n  *result2 = thrust::set_symmetric_difference(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 7;\n\n  Vector ref(5);\n  ref[0] = 2; ref[1] = 3; ref[2] = 3; ref[3] = 6; ref[4] = 7;\n\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_symmetric_difference_kernel<<<1,1>>>(exec,\n                                           a.begin(), a.end(),\n                                           b.begin(), b.end(),\n                                           result.begin(),\n                                           end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetSymmetricDifferenceDeviceSeq()\n{\n  TestSetSymmetricDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceSeq);\n\n\nvoid TestSetSymmetricDifferenceDeviceDevice()\n{\n  TestSetSymmetricDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceDevice);\n#endif\n\n\nvoid TestSetSymmetricDifferenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 7;\n\n  Vector ref(5);\n  ref[0] = 2; ref[1] = 3; ref[2] = 3; ref[3] = 6; ref[4] = 7;\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end = thrust::set_symmetric_difference(thrust::cuda::par.on(s),\n                                                  a.begin(), a.end(),\n                                                  b.begin(), b.end(),\n                                                  result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_symmetric_difference_kernel(ExecutionPolicy exec,\n                                     Iterator1 first1, Iterator1 last1,\n                                     Iterator2 first2, Iterator2 last2,\n                                     Iterator3 result1,\n                                     Iterator4 result2)\n{\n  *result2 = thrust::set_symmetric_difference(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 7;\n\n  Vector ref(5);\n  ref[0] = 2; ref[1] = 3; ref[2] = 3; ref[3] = 6; ref[4] = 7;\n\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_symmetric_difference_kernel<<<1,1>>>(exec,\n                                           a.begin(), a.end(),\n                                           b.begin(), b.end(),\n                                           result.begin(),\n                                           end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetSymmetricDifferenceDeviceSeq()\n{\n  TestSetSymmetricDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceSeq);\n\n\nvoid TestSetSymmetricDifferenceDeviceDevice()\n{\n  TestSetSymmetricDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceDevice);\n#endif\n\n\nvoid TestSetSymmetricDifferenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 7;\n\n  Vector ref(5);\n  ref[0] = 2; ref[1] = 3; ref[2] = 3; ref[3] = 6; ref[4] = 7;\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end = thrust::set_symmetric_difference(thrust::cuda::par.on(s),\n                                                  a.begin(), a.end(),\n                                                  b.begin(), b.end(),\n                                                  result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_symmetric_difference.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::copy(exec, first, last, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCopyDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T>   h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n  \n  thrust::copy(h_src.begin(), h_src.end(), h_dst.begin());\n  copy_kernel<<<1,1>>>(exec, d_src.begin(), d_src.end(), d_dst.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\n\ntemplate<typename T>\nvoid TestCopyDeviceSeq(size_t n)\n{\n  TestCopyDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCopyDeviceDevice(size_t n)\n{\n  TestCopyDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__\nvoid copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::copy_n(exec, first, n, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCopyNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T>   h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n  \n  thrust::copy_n(h_src.begin(), h_src.size(), h_dst.begin());\n  copy_n_kernel<<<1,1>>>(exec, d_src.begin(), d_src.size(), d_dst.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\n\ntemplate<typename T>\nvoid TestCopyNDeviceSeq(size_t n)\n{\n  TestCopyNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCopyNDeviceDevice(size_t n)\n{\n  TestCopyNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceDevice);\n#endif\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::copy(exec, first, last, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCopyDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T>   h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n  \n  thrust::copy(h_src.begin(), h_src.end(), h_dst.begin());\n  copy_kernel<<<1,1>>>(exec, d_src.begin(), d_src.end(), d_dst.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\n\ntemplate<typename T>\nvoid TestCopyDeviceSeq(size_t n)\n{\n  TestCopyDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCopyDeviceDevice(size_t n)\n{\n  TestCopyDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__\nvoid copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::copy_n(exec, first, n, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCopyNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T>   h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n  \n  thrust::copy_n(h_src.begin(), h_src.size(), h_dst.begin());\n  copy_n_kernel<<<1,1>>>(exec, d_src.begin(), d_src.size(), d_dst.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\n\ntemplate<typename T>\nvoid TestCopyNDeviceSeq(size_t n)\n{\n  TestCopyNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCopyNDeviceDevice(size_t n)\n{\n  TestCopyNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceDevice);\n#endif\n\n", "source": "/kaggle/working/thrust/testing/cuda/copy.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/scatter.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid scatter_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 result)\n{\n  thrust::scatter(exec, first, last, map_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScatterDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n  \n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n  {\n    h_map[i] =  h_map[i] % output_size;\n  }\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  thrust::host_vector<int>   h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n  \n  thrust::scatter(h_input.begin(), h_input.end(), h_map.begin(), h_output.begin());\n\n  scatter_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_output.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterDeviceSeq()\n{\n  TestScatterDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterDeviceSeq);\n\nvoid TestScatterDeviceDevice()\n{\n  TestScatterDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Function>\n__global__\nvoid scatter_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 stencil_first, Iterator4 result, Function f)\n{\n  thrust::scatter_if(exec, first, last, map_first, stencil_first, result, f);\n}\n\n\ntemplate<typename T>\nstruct is_even_scatter_if\n{\n  __host__ __device__ bool operator()(const T i) const { return (i % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScatterIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n  \n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n  {\n    h_map[i] =  h_map[i] % output_size;\n  }\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  thrust::host_vector<int>   h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n  \n  thrust::scatter_if(h_input.begin(), h_input.end(), h_map.begin(), h_map.begin(), h_output.begin(), is_even_scatter_if<unsigned int>());\n\n  scatter_if_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_map.begin(), d_output.begin(), is_even_scatter_if<unsigned int>());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\n\nvoid TestScatterIfDeviceSeq()\n{\n  TestScatterIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceSeq);\n\n\nvoid TestScatterIfDeviceDevice()\n{\n  TestScatterIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceDevice);\n#endif\n\n\nvoid TestScatterCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector map(5);  // scatter indices\n  Vector src(5);  // source vector\n  Vector dst(8);  // destination vector\n\n  map[0] = 6; map[1] = 3; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0; dst[5] = 0; dst[6] = 0; dst[7] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::scatter(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), dst.begin());\n\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 4);\n  ASSERT_EQUAL(dst[3], 1);\n  ASSERT_EQUAL(dst[4], 0);\n  ASSERT_EQUAL(dst[5], 0);\n  ASSERT_EQUAL(dst[6], 0);\n  ASSERT_EQUAL(dst[7], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterCudaStreams);\n\n\nvoid TestScatterIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector flg(5);  // predicate array\n  Vector map(5);  // scatter indices\n  Vector src(5);  // source vector\n  Vector dst(8);  // destination vector\n  \n  flg[0] = 0; flg[1] = 1; flg[2] = 0; flg[3] = 1; flg[4] = 0;\n  map[0] = 6; map[1] = 3; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0; dst[5] = 0; dst[6] = 0; dst[7] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::scatter_if(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), flg.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 0);\n  ASSERT_EQUAL(dst[2], 0);\n  ASSERT_EQUAL(dst[3], 1);\n  ASSERT_EQUAL(dst[4], 0);\n  ASSERT_EQUAL(dst[5], 0);\n  ASSERT_EQUAL(dst[6], 0);\n  ASSERT_EQUAL(dst[7], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterIfCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/scatter.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid scatter_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 result)\n{\n  thrust::scatter(exec, first, last, map_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScatterDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n  \n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n  {\n    h_map[i] =  h_map[i] % output_size;\n  }\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  thrust::host_vector<int>   h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n  \n  thrust::scatter(h_input.begin(), h_input.end(), h_map.begin(), h_output.begin());\n\n  scatter_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_output.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterDeviceSeq()\n{\n  TestScatterDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterDeviceSeq);\n\nvoid TestScatterDeviceDevice()\n{\n  TestScatterDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Function>\n__global__\nvoid scatter_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 stencil_first, Iterator4 result, Function f)\n{\n  thrust::scatter_if(exec, first, last, map_first, stencil_first, result, f);\n}\n\n\ntemplate<typename T>\nstruct is_even_scatter_if\n{\n  __host__ __device__ bool operator()(const T i) const { return (i % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScatterIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n  \n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n  {\n    h_map[i] =  h_map[i] % output_size;\n  }\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  thrust::host_vector<int>   h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n  \n  thrust::scatter_if(h_input.begin(), h_input.end(), h_map.begin(), h_map.begin(), h_output.begin(), is_even_scatter_if<unsigned int>());\n\n  scatter_if_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_map.begin(), d_output.begin(), is_even_scatter_if<unsigned int>());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\n\nvoid TestScatterIfDeviceSeq()\n{\n  TestScatterIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceSeq);\n\n\nvoid TestScatterIfDeviceDevice()\n{\n  TestScatterIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceDevice);\n#endif\n\n\nvoid TestScatterCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector map(5);  // scatter indices\n  Vector src(5);  // source vector\n  Vector dst(8);  // destination vector\n\n  map[0] = 6; map[1] = 3; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0; dst[5] = 0; dst[6] = 0; dst[7] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::scatter(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), dst.begin());\n\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 4);\n  ASSERT_EQUAL(dst[3], 1);\n  ASSERT_EQUAL(dst[4], 0);\n  ASSERT_EQUAL(dst[5], 0);\n  ASSERT_EQUAL(dst[6], 0);\n  ASSERT_EQUAL(dst[7], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterCudaStreams);\n\n\nvoid TestScatterIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector flg(5);  // predicate array\n  Vector map(5);  // scatter indices\n  Vector src(5);  // source vector\n  Vector dst(8);  // destination vector\n  \n  flg[0] = 0; flg[1] = 1; flg[2] = 0; flg[3] = 1; flg[4] = 0;\n  map[0] = 6; map[1] = 3; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0; dst[5] = 0; dst[6] = 0; dst[7] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::scatter_if(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), flg.begin(), dst.begin());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 0);\n  ASSERT_EQUAL(dst[2], 0);\n  ASSERT_EQUAL(dst[3], 1);\n  ASSERT_EQUAL(dst[4], 0);\n  ASSERT_EQUAL(dst[5], 0);\n  ASSERT_EQUAL(dst[6], 0);\n  ASSERT_EQUAL(dst[7], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterIfCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/scatter.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_difference_by_key_kernel(ExecutionPolicy exec,\n                                  Iterator1 keys_first1, Iterator1 keys_last1,\n                                  Iterator2 keys_first2, Iterator2 keys_last2,\n                                  Iterator3 values_first1,\n                                  Iterator4 values_first2,\n                                  Iterator5 keys_result,\n                                  Iterator6 values_result,\n                                  Iterator7 result)\n{\n  *result = thrust::set_difference_by_key(exec,\n                                          keys_first1, keys_last1,\n                                          keys_first2, keys_last2,\n                                          values_first1,\n                                          values_first2,\n                                          keys_result,\n                                          values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 5;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 6;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 2; ref_key[1] = 5;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_difference_by_key_kernel<<<1,1>>>(exec,\n                                        a_key.begin(), a_key.end(),\n                                        b_key.begin(), b_key.end(),\n                                        a_val.begin(),\n                                        b_val.begin(),\n                                        result_key.begin(),\n                                        result_val.begin(),\n                                        end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter_pair end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetDifferenceByKeyDeviceSeq()\n{\n  TestSetDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceSeq);\n\n\nvoid TestSetDifferenceByKeyDeviceDevice()\n{\n  TestSetDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetDifferenceByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 5;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 6;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 2; ref_key[1] = 5;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_difference_by_key(thrust::cuda::par.on(s),\n                                  a_key.begin(), a_key.end(),\n                                  b_key.begin(), b_key.end(),\n                                  a_val.begin(),\n                                  b_val.begin(),\n                                  result_key.begin(),\n                                  result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_difference_by_key_kernel(ExecutionPolicy exec,\n                                  Iterator1 keys_first1, Iterator1 keys_last1,\n                                  Iterator2 keys_first2, Iterator2 keys_last2,\n                                  Iterator3 values_first1,\n                                  Iterator4 values_first2,\n                                  Iterator5 keys_result,\n                                  Iterator6 values_result,\n                                  Iterator7 result)\n{\n  *result = thrust::set_difference_by_key(exec,\n                                          keys_first1, keys_last1,\n                                          keys_first2, keys_last2,\n                                          values_first1,\n                                          values_first2,\n                                          keys_result,\n                                          values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 5;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 6;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 2; ref_key[1] = 5;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_difference_by_key_kernel<<<1,1>>>(exec,\n                                        a_key.begin(), a_key.end(),\n                                        b_key.begin(), b_key.end(),\n                                        a_val.begin(),\n                                        b_val.begin(),\n                                        result_key.begin(),\n                                        result_val.begin(),\n                                        end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter_pair end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetDifferenceByKeyDeviceSeq()\n{\n  TestSetDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceSeq);\n\n\nvoid TestSetDifferenceByKeyDeviceDevice()\n{\n  TestSetDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetDifferenceByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 5;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 6;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 2; ref_key[1] = 5;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_difference_by_key(thrust::cuda::par.on(s),\n                                  a_key.begin(), a_key.end(),\n                                  b_key.begin(), b_key.end(),\n                                  a_val.begin(),\n                                  b_val.begin(),\n                                  result_key.begin(),\n                                  result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_difference_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/transform_scan.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function1, typename Function2, typename Iterator3>\n__global__\nvoid transform_inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function1 f1, Function2 f2, Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, f2);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function1, typename T, typename Function2, typename Iterator3>\n__global__\nvoid transform_exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Function1 f1, T init, Function2 f2, Iterator3 result2)\n{\n  *result2 = thrust::transform_exclusive_scan(exec, first, last, result, f1, init, f2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformScanDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(5);\n  Vector ref(5);\n  Vector output(5);\n  \n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n  \n  Vector input_copy(input);\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  // inclusive scan\n  transform_inclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), thrust::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = -1; ref[1] = -4; ref[2] = -2; ref[3] = -6; ref[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // exclusive scan with 0 init\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), 0, thrust::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref[0] = 0; ref[1] = -1; ref[2] = -4; ref[3] = -2; ref[4] = -6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // exclusive scan with nonzero init\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), 3, thrust::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = 3; ref[1] = 2; ref[2] = -1; ref[3] = 1; ref[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // inplace inclusive scan\n  input = input_copy;\n  transform_inclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), input.begin(), thrust::negate<T>(), thrust::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = -1; ref[1] = -4; ref[2] = -2; ref[3] = -6; ref[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n  \n  // inplace exclusive scan with init\n  input = input_copy;\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), input.begin(), thrust::negate<T>(), 3, thrust::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = 3; ref[1] = 2; ref[2] = -1; ref[3] = 1; ref[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n}\n\n\nvoid TestTransformScanDeviceSeq()\n{\n  TestTransformScanDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceSeq);\n\n\nvoid TestTransformScanDeviceDevice()\n{\n  TestTransformScanDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceDevice);\n#endif\n\n\nvoid TestTransformScanCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector result(5);\n  Vector output(5);\n\n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n\n  Vector input_copy(input);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::transform_inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = -1; result[1] = -4; result[2] = -2; result[3] = -6; result[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with 0 init\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), 0, thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = 0; result[1] = -1; result[2] = -4; result[3] = -2; result[4] = -6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with nonzero init\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), 3, thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 2; result[2] = -1; result[3] = 1; result[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // inplace inclusive scan\n  input = input_copy;\n  iter = thrust::transform_inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), thrust::negate<T>(), thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = -1; result[1] = -4; result[2] = -2; result[3] = -6; result[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), thrust::negate<T>(), 3, thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 2; result[2] = -1; result[3] = 1; result[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformScanCudaStreams);\n\nvoid TestTransformScanConstAccumulator()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector reference(5);\n  Vector output(5);\n\n  input[0] = 1;\n  input[1] = 3;\n  input[2] = -2;\n  input[3] = 4;\n  input[4] = -5;\n\n  thrust::transform_inclusive_scan(input.begin(),\n                                   input.end(),\n                                   output.begin(),\n                                   thrust::identity<T>(),\n                                   thrust::plus<T>());\n  thrust::inclusive_scan(input.begin(), input.end(), reference.begin(), thrust::plus<T>());\n\n  ASSERT_EQUAL(output, reference);\n}\nDECLARE_UNITTEST(TestTransformScanConstAccumulator);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/transform_scan.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function1, typename Function2, typename Iterator3>\n__global__\nvoid transform_inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function1 f1, Function2 f2, Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, f2);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function1, typename T, typename Function2, typename Iterator3>\n__global__\nvoid transform_exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Function1 f1, T init, Function2 f2, Iterator3 result2)\n{\n  *result2 = thrust::transform_exclusive_scan(exec, first, last, result, f1, init, f2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformScanDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(5);\n  Vector ref(5);\n  Vector output(5);\n  \n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n  \n  Vector input_copy(input);\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  // inclusive scan\n  transform_inclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), thrust::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = -1; ref[1] = -4; ref[2] = -2; ref[3] = -6; ref[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // exclusive scan with 0 init\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), 0, thrust::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref[0] = 0; ref[1] = -1; ref[2] = -4; ref[3] = -2; ref[4] = -6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // exclusive scan with nonzero init\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), 3, thrust::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = 3; ref[1] = 2; ref[2] = -1; ref[3] = 1; ref[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(ref, output);\n  \n  // inplace inclusive scan\n  input = input_copy;\n  transform_inclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), input.begin(), thrust::negate<T>(), thrust::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = -1; ref[1] = -4; ref[2] = -2; ref[3] = -6; ref[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n  \n  // inplace exclusive scan with init\n  input = input_copy;\n  transform_exclusive_scan_kernel<<<1,1>>>(exec, input.begin(), input.end(), input.begin(), thrust::negate<T>(), 3, thrust::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref[0] = 3; ref[1] = 2; ref[2] = -1; ref[3] = 1; ref[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n}\n\n\nvoid TestTransformScanDeviceSeq()\n{\n  TestTransformScanDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceSeq);\n\n\nvoid TestTransformScanDeviceDevice()\n{\n  TestTransformScanDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceDevice);\n#endif\n\n\nvoid TestTransformScanCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector result(5);\n  Vector output(5);\n\n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n\n  Vector input_copy(input);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::transform_inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = -1; result[1] = -4; result[2] = -2; result[3] = -6; result[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with 0 init\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), 0, thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = 0; result[1] = -1; result[2] = -4; result[3] = -2; result[4] = -6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with nonzero init\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>(), 3, thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 2; result[2] = -1; result[3] = 1; result[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // inplace inclusive scan\n  input = input_copy;\n  iter = thrust::transform_inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), thrust::negate<T>(), thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = -1; result[1] = -4; result[2] = -2; result[3] = -6; result[4] = -1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter = thrust::transform_exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), thrust::negate<T>(), 3, thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 2; result[2] = -1; result[3] = 1; result[4] = -3;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformScanCudaStreams);\n\nvoid TestTransformScanConstAccumulator()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector reference(5);\n  Vector output(5);\n\n  input[0] = 1;\n  input[1] = 3;\n  input[2] = -2;\n  input[3] = 4;\n  input[4] = -5;\n\n  thrust::transform_inclusive_scan(input.begin(),\n                                   input.end(),\n                                   output.begin(),\n                                   thrust::identity<T>(),\n                                   thrust::plus<T>());\n  thrust::inclusive_scan(input.begin(), input.end(), reference.begin(), thrust::plus<T>());\n\n  ASSERT_EQUAL(output, reference);\n}\nDECLARE_UNITTEST(TestTransformScanConstAccumulator);\n", "source": "/kaggle/working/thrust/testing/cuda/transform_scan.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid partition_point_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition_point(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionPointDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n  typedef typename thrust::device_vector<int>::iterator iterator;\n\n  iterator ref = thrust::stable_partition(v.begin(), v.end(), is_even<int>());\n\n  thrust::device_vector<iterator> result(1);\n  partition_point_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(ref - v.begin(), (iterator)result[0] - v.begin());\n}\n\n\nvoid TestPartitionPointDeviceSeq()\n{\n  TestPartitionPointDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceSeq);\n\n\nvoid TestPartitionPointDeviceDevice()\n{\n  TestPartitionPointDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceDevice);\n#endif\n\n\nvoid TestPartitionPointCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator Iterator;\n\n  Vector v(4);\n  v[0] = 1; v[1] = 1; v[2] = 1; v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 4;\n  Iterator ref = first + 3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, thrust::identity<T>()));\n\n  last = v.begin() + 3;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, thrust::identity<T>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestPartitionPointCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid partition_point_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition_point(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionPointDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n  typedef typename thrust::device_vector<int>::iterator iterator;\n\n  iterator ref = thrust::stable_partition(v.begin(), v.end(), is_even<int>());\n\n  thrust::device_vector<iterator> result(1);\n  partition_point_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(ref - v.begin(), (iterator)result[0] - v.begin());\n}\n\n\nvoid TestPartitionPointDeviceSeq()\n{\n  TestPartitionPointDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceSeq);\n\n\nvoid TestPartitionPointDeviceDevice()\n{\n  TestPartitionPointDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceDevice);\n#endif\n\n\nvoid TestPartitionPointCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator Iterator;\n\n  Vector v(4);\n  v[0] = 1; v[1] = 1; v[2] = 1; v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 4;\n  Iterator ref = first + 3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, thrust::identity<T>()));\n\n  last = v.begin() + 3;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, thrust::identity<T>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestPartitionPointCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/partition_point.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/merge.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid merge_kernel(ExecutionPolicy exec,\n                  Iterator1 first1, Iterator1 last1,\n                  Iterator2 first2, Iterator2 last2,\n                  Iterator3 result1,\n                  Iterator4 result2)\n{\n  *result2 = thrust::merge(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMergeDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  size_t sizes[]   = {0, 1, n / 2, n, n + 1, 2 * n};\n  size_t num_sizes = sizeof(sizes) / sizeof(size_t);\n\n  thrust::host_vector<int> random = unittest::random_integers<unittest::int8_t>(n + *thrust::max_element(sizes, sizes + num_sizes));\n\n  thrust::host_vector<int> h_a(random.begin(), random.begin() + n);\n  thrust::host_vector<int> h_b(random.begin() + n, random.end());\n\n  thrust::stable_sort(h_a.begin(), h_a.end());\n  thrust::stable_sort(h_b.begin(), h_b.end());\n  \n  thrust::device_vector<int> d_a = h_a;\n  thrust::device_vector<int> d_b = h_b;\n\n  for(size_t i = 0; i < num_sizes; i++)\n  {\n    size_t size = sizes[i];\n    \n    thrust::host_vector<int>   h_result(n + size);\n    thrust::device_vector<int> d_result(n + size);\n\n    typename thrust::host_vector<int>::iterator   h_end;\n\n    typedef typename thrust::device_vector<int>::iterator iter_type;\n    thrust::device_vector<iter_type> d_end(1);\n    \n    h_end = thrust::merge(h_a.begin(), h_a.end(),\n                          h_b.begin(), h_b.begin() + size,\n                          h_result.begin());\n    h_result.resize(h_end - h_result.begin());\n\n    merge_kernel<<<1,1>>>(exec,\n                          d_a.begin(), d_a.end(),\n                          d_b.begin(), d_b.begin() + size,\n                          d_result.begin(),\n                          d_end.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_result.resize((iter_type)d_end[0] - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestMergeDeviceSeq()\n{\n  TestMergeDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeDeviceSeq);\n\n\nvoid TestMergeDeviceDevice()\n{\n  TestMergeDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeDeviceDevice);\n#endif\n\n\nvoid TestMergeCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(7);\n  ref[0] = 0;\n  ref[1] = 0;\n  ref[2] = 2;\n  ref[3] = 3;\n  ref[4] = 3;\n  ref[5] = 4;\n  ref[6] = 4;\n\n  Vector result(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end = thrust::merge(thrust::cuda::par.on(s),\n                               a.begin(), a.end(),\n                               b.begin(), b.end(),\n                               result.begin());\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/merge.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid merge_kernel(ExecutionPolicy exec,\n                  Iterator1 first1, Iterator1 last1,\n                  Iterator2 first2, Iterator2 last2,\n                  Iterator3 result1,\n                  Iterator4 result2)\n{\n  *result2 = thrust::merge(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMergeDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  size_t sizes[]   = {0, 1, n / 2, n, n + 1, 2 * n};\n  size_t num_sizes = sizeof(sizes) / sizeof(size_t);\n\n  thrust::host_vector<int> random = unittest::random_integers<unittest::int8_t>(n + *thrust::max_element(sizes, sizes + num_sizes));\n\n  thrust::host_vector<int> h_a(random.begin(), random.begin() + n);\n  thrust::host_vector<int> h_b(random.begin() + n, random.end());\n\n  thrust::stable_sort(h_a.begin(), h_a.end());\n  thrust::stable_sort(h_b.begin(), h_b.end());\n  \n  thrust::device_vector<int> d_a = h_a;\n  thrust::device_vector<int> d_b = h_b;\n\n  for(size_t i = 0; i < num_sizes; i++)\n  {\n    size_t size = sizes[i];\n    \n    thrust::host_vector<int>   h_result(n + size);\n    thrust::device_vector<int> d_result(n + size);\n\n    typename thrust::host_vector<int>::iterator   h_end;\n\n    typedef typename thrust::device_vector<int>::iterator iter_type;\n    thrust::device_vector<iter_type> d_end(1);\n    \n    h_end = thrust::merge(h_a.begin(), h_a.end(),\n                          h_b.begin(), h_b.begin() + size,\n                          h_result.begin());\n    h_result.resize(h_end - h_result.begin());\n\n    merge_kernel<<<1,1>>>(exec,\n                          d_a.begin(), d_a.end(),\n                          d_b.begin(), d_b.begin() + size,\n                          d_result.begin(),\n                          d_end.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_result.resize((iter_type)d_end[0] - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestMergeDeviceSeq()\n{\n  TestMergeDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeDeviceSeq);\n\n\nvoid TestMergeDeviceDevice()\n{\n  TestMergeDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeDeviceDevice);\n#endif\n\n\nvoid TestMergeCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(7);\n  ref[0] = 0;\n  ref[1] = 0;\n  ref[2] = 2;\n  ref[3] = 3;\n  ref[4] = 3;\n  ref[5] = 4;\n  ref[6] = 4;\n\n  Vector result(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end = thrust::merge(thrust::cuda::par.on(s),\n                               a.begin(), a.end(),\n                               b.begin(), b.end(),\n                               result.begin());\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/merge.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/swap.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid swap_ranges_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  thrust::swap_ranges(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSwapRangesDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n\n  Vector v2(5);\n  v2[0] = 5; v2[1] = 6; v2[2] = 7; v2[3] = 8; v2[4] = 9;\n\n  swap_ranges_kernel<<<1,1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(v1[0], 5);\n  ASSERT_EQUAL(v1[1], 6);\n  ASSERT_EQUAL(v1[2], 7);\n  ASSERT_EQUAL(v1[3], 8);\n  ASSERT_EQUAL(v1[4], 9);\n  \n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\nvoid TestSwapRangesDeviceSeq()\n{\n  TestSwapRangesDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceSeq);\n\nvoid TestSwapRangesDeviceDevice()\n{\n  TestSwapRangesDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceDevice);\n#endif\n\nvoid TestSwapRangesCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n\n  Vector v2(5);\n  v2[0] = 5; v2[1] = 6; v2[2] = 7; v2[3] = 8; v2[4] = 9;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::swap_ranges(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v1[0], 5);\n  ASSERT_EQUAL(v1[1], 6);\n  ASSERT_EQUAL(v1[2], 7);\n  ASSERT_EQUAL(v1[3], 8);\n  ASSERT_EQUAL(v1[4], 9);\n  \n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSwapRangesCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/swap.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid swap_ranges_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  thrust::swap_ranges(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSwapRangesDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n\n  Vector v2(5);\n  v2[0] = 5; v2[1] = 6; v2[2] = 7; v2[3] = 8; v2[4] = 9;\n\n  swap_ranges_kernel<<<1,1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(v1[0], 5);\n  ASSERT_EQUAL(v1[1], 6);\n  ASSERT_EQUAL(v1[2], 7);\n  ASSERT_EQUAL(v1[3], 8);\n  ASSERT_EQUAL(v1[4], 9);\n  \n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\nvoid TestSwapRangesDeviceSeq()\n{\n  TestSwapRangesDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceSeq);\n\nvoid TestSwapRangesDeviceDevice()\n{\n  TestSwapRangesDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceDevice);\n#endif\n\nvoid TestSwapRangesCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n\n  Vector v2(5);\n  v2[0] = 5; v2[1] = 6; v2[2] = 7; v2[3] = 8; v2[4] = 9;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::swap_ranges(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v1[0], 5);\n  ASSERT_EQUAL(v1[1], 6);\n  ASSERT_EQUAL(v1[2], 7);\n  ASSERT_EQUAL(v1[3], 8);\n  ASSERT_EQUAL(v1[4], 9);\n  \n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSwapRangesCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/swap_ranges.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/transform.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Iterator3>\n__global__\nvoid transform_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Iterator3 result2)\n{\n  *result2 = thrust::transform(exec, first, last, result1, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformUnaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  input[0]  =  1; input[1]  = -2; input[2]  =  3;\n  result[0] = -1; result[1] =  2; result[2] = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformUnaryDeviceSeq()\n{\n  TestTransformUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceSeq);\n\nvoid TestTransformUnaryDeviceDevice()\n{\n  TestTransformUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Predicate, typename Iterator3>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfUnaryNoStencilDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input[0]   =  0; input[1]   = -2; input[2]   =  0;\n  output[0]  = -1; output[1]  = -2; output[2]  = -3; \n  result[0]  = -1; result[1]  =  2; result[2]  = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input.begin(), input.end(),\n                               output.begin(),\n                               thrust::negate<T>(),\n                               thrust::identity<T>(),\n                               iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryNoStencilDeviceSeq()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceSeq);\n\nvoid TestTransformIfUnaryNoStencilDeviceDevice()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Function, typename Predicate, typename Iterator4>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result1, Function f, Predicate pred, Iterator4 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, stencil_first, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfUnaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector stencil(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input[0]   =  1; input[1]   = -2; input[2]   =  3;\n  output[0]  =  1; output[1]  =  2; output[2]  =  3; \n  stencil[0] =  1; stencil[1] =  0; stencil[2] =  1;\n  result[0]  = -1; result[1]  =  2; result[2]  = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input.begin(), input.end(),\n                               stencil.begin(),\n                               output.begin(),\n                               thrust::negate<T>(),\n                               thrust::identity<T>(),\n                               iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryDeviceSeq()\n{\n  TestTransformIfUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceSeq);\n\nvoid TestTransformIfUnaryDeviceDevice()\n{\n  TestTransformIfUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Function, typename Iterator4>\n__global__\nvoid transform_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result1, Function f, Iterator4 result2)\n{\n  *result2 = thrust::transform(exec, first1, last1, first2, result1, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformBinaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input1(3);\n  Vector input2(3);\n  Vector output(3);\n  Vector result(3);\n  input1[0] =  1; input1[1] = -2; input1[2] =  3;\n  input2[0] = -4; input2[1] =  5; input2[2] =  6;\n  result[0] =  5; result[1] = -7; result[2] = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_kernel<<<1,1>>>(exec, input1.begin(), input1.end(), input2.begin(), output.begin(), thrust::minus<T>(), iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformBinaryDeviceSeq()\n{\n  TestTransformBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceSeq);\n\nvoid TestTransformBinaryDeviceDevice()\n{\n  TestTransformBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Function, typename Predicate, typename Iterator5>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 stencil_first, Iterator4 result1, Function f, Predicate pred, Iterator5 result2)\n{\n  *result2 = thrust::transform_if(exec, first1, last1, first2, stencil_first, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfBinaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input1(3);\n  Vector input2(3);\n  Vector stencil(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input1[0]  =  1; input1[1]  = -2; input1[2]  =  3;\n  input2[0]  = -4; input2[1]  =  5; input2[2]  =  6;\n  stencil[0] =  0; stencil[1] =  1; stencil[2] =  0;\n  output[0]  =  1; output[1]  =  2; output[2]  =  3;\n  result[0]  =  5; result[1]  =  2; result[2]  = -3;\n  \n  thrust::identity<T> identity;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input1.begin(), input1.end(),\n                               input2.begin(),\n                               stencil.begin(),\n                               output.begin(),\n                               thrust::minus<T>(),\n                               thrust::not1(identity),\n                               iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfBinaryDeviceSeq()\n{\n  TestTransformIfBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceSeq);\n\nvoid TestTransformIfBinaryDeviceDevice()\n{\n  TestTransformIfBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceDevice);\n#endif\n\nvoid TestTransformUnaryCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector::iterator iter;\n\n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  input[0]  =  1; input[1]  = -2; input[2]  =  3;\n  result[0] = -1; result[1] =  2; result[2] = -3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  iter = thrust::transform(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformUnaryCudaStreams);\n\n\nvoid TestTransformBinaryCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input1(3);\n  Vector input2(3);\n  Vector output(3);\n  Vector result(3);\n  input1[0] =  1; input1[1] = -2; input1[2] =  3;\n  input2[0] = -4; input2[1] =  5; input2[2] =  6;\n  result[0] =  5; result[1] = -7; result[2] = -3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  iter = thrust::transform(thrust::cuda::par.on(s), input1.begin(), input1.end(), input2.begin(), output.begin(), thrust::minus<T>());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformBinaryCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/transform.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Iterator3>\n__global__\nvoid transform_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Iterator3 result2)\n{\n  *result2 = thrust::transform(exec, first, last, result1, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformUnaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  input[0]  =  1; input[1]  = -2; input[2]  =  3;\n  result[0] = -1; result[1] =  2; result[2] = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_kernel<<<1,1>>>(exec, input.begin(), input.end(), output.begin(), thrust::negate<T>(), iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformUnaryDeviceSeq()\n{\n  TestTransformUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceSeq);\n\nvoid TestTransformUnaryDeviceDevice()\n{\n  TestTransformUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Predicate, typename Iterator3>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfUnaryNoStencilDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input[0]   =  0; input[1]   = -2; input[2]   =  0;\n  output[0]  = -1; output[1]  = -2; output[2]  = -3; \n  result[0]  = -1; result[1]  =  2; result[2]  = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input.begin(), input.end(),\n                               output.begin(),\n                               thrust::negate<T>(),\n                               thrust::identity<T>(),\n                               iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryNoStencilDeviceSeq()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceSeq);\n\nvoid TestTransformIfUnaryNoStencilDeviceDevice()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Function, typename Predicate, typename Iterator4>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result1, Function f, Predicate pred, Iterator4 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, stencil_first, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfUnaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input(3);\n  Vector stencil(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input[0]   =  1; input[1]   = -2; input[2]   =  3;\n  output[0]  =  1; output[1]  =  2; output[2]  =  3; \n  stencil[0] =  1; stencil[1] =  0; stencil[2] =  1;\n  result[0]  = -1; result[1]  =  2; result[2]  = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input.begin(), input.end(),\n                               stencil.begin(),\n                               output.begin(),\n                               thrust::negate<T>(),\n                               thrust::identity<T>(),\n                               iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryDeviceSeq()\n{\n  TestTransformIfUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceSeq);\n\nvoid TestTransformIfUnaryDeviceDevice()\n{\n  TestTransformIfUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Function, typename Iterator4>\n__global__\nvoid transform_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result1, Function f, Iterator4 result2)\n{\n  *result2 = thrust::transform(exec, first1, last1, first2, result1, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformBinaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input1(3);\n  Vector input2(3);\n  Vector output(3);\n  Vector result(3);\n  input1[0] =  1; input1[1] = -2; input1[2] =  3;\n  input2[0] = -4; input2[1] =  5; input2[2] =  6;\n  result[0] =  5; result[1] = -7; result[2] = -3;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_kernel<<<1,1>>>(exec, input1.begin(), input1.end(), input2.begin(), output.begin(), thrust::minus<T>(), iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformBinaryDeviceSeq()\n{\n  TestTransformBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceSeq);\n\nvoid TestTransformBinaryDeviceDevice()\n{\n  TestTransformBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Function, typename Predicate, typename Iterator5>\n__global__\nvoid transform_if_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 stencil_first, Iterator4 result1, Function f, Predicate pred, Iterator5 result2)\n{\n  *result2 = thrust::transform_if(exec, first1, last1, first2, stencil_first, result1, f, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformIfBinaryDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  typename Vector::iterator iter;\n  \n  Vector input1(3);\n  Vector input2(3);\n  Vector stencil(3);\n  Vector output(3);\n  Vector result(3);\n  \n  input1[0]  =  1; input1[1]  = -2; input1[2]  =  3;\n  input2[0]  = -4; input2[1]  =  5; input2[2]  =  6;\n  stencil[0] =  0; stencil[1] =  1; stencil[2] =  0;\n  output[0]  =  1; output[1]  =  2; output[2]  =  3;\n  result[0]  =  5; result[1]  =  2; result[2]  = -3;\n  \n  thrust::identity<T> identity;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n  \n  transform_if_kernel<<<1,1>>>(exec,\n                               input1.begin(), input1.end(),\n                               input2.begin(),\n                               stencil.begin(),\n                               output.begin(),\n                               thrust::minus<T>(),\n                               thrust::not1(identity),\n                               iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfBinaryDeviceSeq()\n{\n  TestTransformIfBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceSeq);\n\nvoid TestTransformIfBinaryDeviceDevice()\n{\n  TestTransformIfBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceDevice);\n#endif\n\nvoid TestTransformUnaryCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector::iterator iter;\n\n  Vector input(3);\n  Vector output(3);\n  Vector result(3);\n  input[0]  =  1; input[1]  = -2; input[2]  =  3;\n  result[0] = -1; result[1] =  2; result[2] = -3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  iter = thrust::transform(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::negate<T>());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformUnaryCudaStreams);\n\n\nvoid TestTransformBinaryCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector::iterator iter;\n\n  Vector input1(3);\n  Vector input2(3);\n  Vector output(3);\n  Vector result(3);\n  input1[0] =  1; input1[1] = -2; input1[2] =  3;\n  input2[0] = -4; input2[1] =  5; input2[2] =  6;\n  result[0] =  5; result[1] = -7; result[2] = -3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  iter = thrust::transform(thrust::cuda::par.on(s), input1.begin(), input1.end(), input2.begin(), output.begin(), thrust::minus<T>());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformBinaryCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/transform.cu"}
{"input": "#include <cstdio>\n#include <unittest/unittest.h>\n#include <thrust/scan.h>\n#include <thrust/functional.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::inclusive_scan(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::exclusive_scan(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T>\n__global__\nvoid exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init)\n{\n  thrust::exclusive_scan(exec, first, last, result, init);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestScanDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_input = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11);\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  // in-place scans\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::inclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1,1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  h_output = h_input;\n  d_output = d_input;\n  \n  thrust::exclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(d_output, h_output);\n}\n\n\ntemplate<typename T>\nstruct TestScanDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestScanDeviceSeq, IntegralTypes> TestScanDeviceSeqInstance;\n\n\ntemplate<typename T>\nstruct TestScanDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestScanDeviceDevice, IntegralTypes> TestScanDeviceDeviceInstance;\n#endif\n\n\nvoid TestScanCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector result(5);\n  Vector output(5);\n\n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n\n  Vector input_copy(input);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n  cudaStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 0);\n  cudaStreamSynchronize(s);\n\n  result[0] = 0; result[1] = 1; result[2] = 4; result[3] = 2; result[4] = 6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with init\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3);\n  cudaStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // inclusive scan with op\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init and op\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, thrust::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  cudaStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), 3);\n  cudaStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with implicit init=0\n  input = input_copy;\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  cudaStreamSynchronize(s);\n\n  result[0] = 0; result[1] = 1; result[2] = 4; result[3] = 2; result[4] = 6;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScanCudaStreams);\n\ntemplate <typename T>\nstruct const_ref_plus_mod3\n{\n    T * table;\n\n    const_ref_plus_mod3(T * table) : table(table) {}\n\n    __host__ __device__\n    const T& operator()(T a, T b)\n    {\n        return table[(int) (a + b)];\n    }\n};\n\nstatic void TestInclusiveScanWithConstAccumulator(void)\n{\n    // add numbers modulo 3 with external lookup table\n    thrust::device_vector<int> data(7);\n    data[0] = 0;\n    data[1] = 1;\n    data[2] = 2;\n    data[3] = 1;\n    data[4] = 2;\n    data[5] = 0;\n    data[6] = 1;\n\n    thrust::device_vector<int> table(6);\n    table[0] = 0;\n    table[1] = 1;\n    table[2] = 2;\n    table[3] = 0;\n    table[4] = 1;\n    table[5] = 2;\n\n    thrust::inclusive_scan(data.begin(), data.end(), data.begin(), const_ref_plus_mod3<int>(thrust::raw_pointer_cast(&table[0])));\n    \n    ASSERT_EQUAL(data[0], 0);\n    ASSERT_EQUAL(data[1], 1);\n    ASSERT_EQUAL(data[2], 0);\n    ASSERT_EQUAL(data[3], 1);\n    ASSERT_EQUAL(data[4], 0);\n    ASSERT_EQUAL(data[5], 0);\n    ASSERT_EQUAL(data[6], 1);\n}\nDECLARE_UNITTEST(TestInclusiveScanWithConstAccumulator);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cstdio>\n#include <unittest/unittest.h>\n#include <thrust/scan.h>\n#include <thrust/functional.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::inclusive_scan(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::exclusive_scan(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T>\n__global__\nvoid exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init)\n{\n  thrust::exclusive_scan(exec, first, last, result, init);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestScanDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_input = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11);\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  // in-place scans\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::inclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1,1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n  \n  h_output = h_input;\n  d_output = d_input;\n  \n  thrust::exclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1,1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(d_output, h_output);\n}\n\n\ntemplate<typename T>\nstruct TestScanDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestScanDeviceSeq, IntegralTypes> TestScanDeviceSeqInstance;\n\n\ntemplate<typename T>\nstruct TestScanDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestScanDeviceDevice, IntegralTypes> TestScanDeviceDeviceInstance;\n#endif\n\n\nvoid TestScanCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector::iterator iter;\n\n  Vector input(5);\n  Vector result(5);\n  Vector output(5);\n\n  input[0] = 1; input[1] = 3; input[2] = -2; input[3] = 4; input[4] = -5;\n\n  Vector input_copy(input);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n  hipStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 0);\n  hipStreamSynchronize(s);\n\n  result[0] = 0; result[1] = 1; result[2] = 4; result[3] = 2; result[4] = 6;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // exclusive scan with init\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3);\n  hipStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n  \n  // inclusive scan with op\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init and op\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, thrust::plus<T>());\n  hipStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input,  input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  hipStreamSynchronize(s);\n\n  result[0] = 1; result[1] = 4; result[2] = 2; result[3] = 6; result[4] = 1;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), 3);\n  hipStreamSynchronize(s);\n\n  result[0] = 3; result[1] = 4; result[2] = 7; result[3] = 5; result[4] = 9;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with implicit init=0\n  input = input_copy;\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  hipStreamSynchronize(s);\n\n  result[0] = 0; result[1] = 1; result[2] = 4; result[3] = 2; result[4] = 6;\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScanCudaStreams);\n\ntemplate <typename T>\nstruct const_ref_plus_mod3\n{\n    T * table;\n\n    const_ref_plus_mod3(T * table) : table(table) {}\n\n    __host__ __device__\n    const T& operator()(T a, T b)\n    {\n        return table[(int) (a + b)];\n    }\n};\n\nstatic void TestInclusiveScanWithConstAccumulator(void)\n{\n    // add numbers modulo 3 with external lookup table\n    thrust::device_vector<int> data(7);\n    data[0] = 0;\n    data[1] = 1;\n    data[2] = 2;\n    data[3] = 1;\n    data[4] = 2;\n    data[5] = 0;\n    data[6] = 1;\n\n    thrust::device_vector<int> table(6);\n    table[0] = 0;\n    table[1] = 1;\n    table[2] = 2;\n    table[3] = 0;\n    table[4] = 1;\n    table[5] = 2;\n\n    thrust::inclusive_scan(data.begin(), data.end(), data.begin(), const_ref_plus_mod3<int>(thrust::raw_pointer_cast(&table[0])));\n    \n    ASSERT_EQUAL(data[0], 0);\n    ASSERT_EQUAL(data[1], 1);\n    ASSERT_EQUAL(data[2], 0);\n    ASSERT_EQUAL(data[3], 1);\n    ASSERT_EQUAL(data[4], 0);\n    ASSERT_EQUAL(data[5], 0);\n    ASSERT_EQUAL(data[6], 1);\n}\nDECLARE_UNITTEST(TestInclusiveScanWithConstAccumulator);\n", "source": "/kaggle/working/thrust/testing/cuda/scan.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/pair.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid stable_sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::stable_sort(exec, first, last);\n}\n\n\nstruct make_pair_functor\n{\n  template<typename T1, typename T2>\n  __host__ __device__\n    thrust::pair<T1,T2> operator()(const T1 &x, const T2 &y)\n  {\n    return thrust::make_pair(x,y);\n  } // end operator()()\n}; // end make_pair_functor\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPairStableSortDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  typedef thrust::pair<int,int> P;\n\n  thrust::host_vector<int>   h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int>   h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P>   h_pairs(n);\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  thrust::device_vector<P> d_pairs = h_pairs;\n\n  stable_sort_kernel<<<1,1>>>(exec, d_pairs.begin(), d_pairs.end());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort(h_pairs.begin(), h_pairs.end());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n};\n\n\nvoid TestPairStableSortDeviceSeq()\n{\n  TestPairStableSortDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceSeq);\n\n\nvoid TestPairStableSortDeviceDevice()\n{\n  TestPairStableSortDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceDevice);\n#endif\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/pair.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid stable_sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::stable_sort(exec, first, last);\n}\n\n\nstruct make_pair_functor\n{\n  template<typename T1, typename T2>\n  __host__ __device__\n    thrust::pair<T1,T2> operator()(const T1 &x, const T2 &y)\n  {\n    return thrust::make_pair(x,y);\n  } // end operator()()\n}; // end make_pair_functor\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPairStableSortDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  typedef thrust::pair<int,int> P;\n\n  thrust::host_vector<int>   h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int>   h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P>   h_pairs(n);\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  thrust::device_vector<P> d_pairs = h_pairs;\n\n  stable_sort_kernel<<<1,1>>>(exec, d_pairs.begin(), d_pairs.end());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort(h_pairs.begin(), h_pairs.end());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n};\n\n\nvoid TestPairStableSortDeviceSeq()\n{\n  TestPairStableSortDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceSeq);\n\n\nvoid TestPairStableSortDeviceDevice()\n{\n  TestPairStableSortDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceDevice);\n#endif\n\n", "source": "/kaggle/working/thrust/testing/cuda/pair_sort.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct my_less\n{\n  __host__ __device__\n  bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Compare>\n__global__\nvoid sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Compare comp)\n{\n  thrust::sort(exec, first, last, comp);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  sort_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), comp);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n\n  thrust::sort(h_data.begin(), h_data.end(), comp);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\n\ntemplate<typename T>\n  struct TestComparisonSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestComparisonSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortDeviceDeviceDeviceInstance;\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestSortDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortDevice<T>(exec, n, thrust::less<T>());\n};\n\n\ntemplate<typename T>\n  struct TestSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<\n  TestSortDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<\n  TestSortDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortDeviceDeviceInstance;\n#endif\n\n\nvoid TestSortCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n\n  keys[0] = 9;\n  keys[1] = 3;\n  keys[2] = 2;\n  keys[3] = 0;\n  keys[4] = 4;\n  keys[5] = 7;\n  keys[6] = 8;\n  keys[7] = 1;\n  keys[8] = 5;\n  keys[9] = 6;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n                      \n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortCudaStreams);\n\n\nvoid TestComparisonSortCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n\n  keys[0] = 9;\n  keys[1] = 3;\n  keys[2] = 2;\n  keys[3] = 0;\n  keys[4] = 4;\n  keys[5] = 7;\n  keys[6] = 8;\n  keys[7] = 1;\n  keys[8] = 5;\n  keys[9] = 6;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end(), my_less<int>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end(), my_less<int>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct my_less\n{\n  __host__ __device__\n  bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Compare>\n__global__\nvoid sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Compare comp)\n{\n  thrust::sort(exec, first, last, comp);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  sort_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), comp);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n\n  thrust::sort(h_data.begin(), h_data.end(), comp);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\n\ntemplate<typename T>\n  struct TestComparisonSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestComparisonSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortDeviceDeviceDeviceInstance;\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestSortDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortDevice<T>(exec, n, thrust::less<T>());\n};\n\n\ntemplate<typename T>\n  struct TestSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<\n  TestSortDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<\n  TestSortDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortDeviceDeviceInstance;\n#endif\n\n\nvoid TestSortCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n\n  keys[0] = 9;\n  keys[1] = 3;\n  keys[2] = 2;\n  keys[3] = 0;\n  keys[4] = 4;\n  keys[5] = 7;\n  keys[6] = 8;\n  keys[7] = 1;\n  keys[8] = 5;\n  keys[9] = 6;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n                      \n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortCudaStreams);\n\n\nvoid TestComparisonSortCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n\n  keys[0] = 9;\n  keys[1] = 3;\n  keys[2] = 2;\n  keys[3] = 0;\n  keys[4] = 4;\n  keys[5] = 7;\n  keys[6] = 8;\n  keys[7] = 1;\n  keys[8] = 5;\n  keys[9] = 6;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end(), my_less<int>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end(), my_less<int>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/sort.cu"}
{"input": "#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n#include <unittest/unittest.h>\n\n#include <cstdint>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          BinaryPredicate pred,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename BinaryFunction, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          BinaryPredicate pred,\n                          BinaryFunction binary_op,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred, binary_op);\n}\n#endif\n\n\ntemplate<typename T>\nstruct is_equal_div_10_reduce\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\ntemplate<typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\n\ntemplate<typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0; \n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy>\nvoid TestReduceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> keys;\n  thrust::device_vector<T> values;\n\n  typedef typename thrust::pair<\n    typename thrust::device_vector<T>::iterator,\n    typename thrust::device_vector<T>::iterator\n  > iterator_pair;\n\n  thrust::device_vector<iterator_pair> new_last_vec(1);\n  iterator_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n  \n  thrust::device_vector<T> output_keys(keys.size());\n  thrust::device_vector<T> output_values(values.size());\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_reduce<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n  \n  // test BinaryFunction\n  initialize_keys(keys);  initialize_values(values);\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), thrust::equal_to<T>(), thrust::plus<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n}\n\n\nvoid TestReduceByKeyDeviceSeq()\n{\n  TestReduceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceSeq);\n\n\nvoid TestReduceByKeyDeviceDevice()\n{\n  TestReduceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceDevice);\n\n\nvoid TestReduceByKeyDeviceNoSync()\n{\n  TestReduceByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReduceByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  thrust::pair<Vector::iterator, Vector::iterator> new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_reduce<T>());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), thrust::equal_to<T>(), thrust::plus<T>());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestReduceByKeyCudaStreamsSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsSync);\n\n\nvoid TestReduceByKeyCudaStreamsNoSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsNoSync);\n\n\n// Maps indices to key ids\nclass div_op : public thrust::unary_function<std::int64_t, std::int64_t>\n{\n  std::int64_t m_divisor;\n\npublic:\n  __host__ div_op(std::int64_t divisor)\n    : m_divisor(divisor)\n  {}\n\n  __host__ __device__\n  std::int64_t operator()(std::int64_t x) const\n  {\n    return x / m_divisor;\n  }\n};\n\n// Produces unique sequence for key\nclass mod_op : public thrust::unary_function<std::int64_t, std::int64_t>\n{\n  std::int64_t m_divisor;\n\npublic:\n  __host__ mod_op(std::int64_t divisor)\n    : m_divisor(divisor)\n  {}\n\n  __host__ __device__\n  std::int64_t operator()(std::int64_t x) const\n  {\n    // div: 2          \n    // idx: 0 1   2 3   4 5 \n    // key: 0 0 | 1 1 | 2 2 \n    // mod: 0 1 | 0 1 | 0 1\n    // ret: 0 1   1 2   2 3\n    return (x % m_divisor) + (x / m_divisor);\n  }\n};\n\n\nvoid TestReduceByKeyWithBigIndexesHelper(int magnitude)\n{\n  const std::int64_t key_size_magnitude = 8;\n  ASSERT_EQUAL(true, key_size_magnitude < magnitude);\n\n  const std::int64_t num_items       = 1ll << magnitude;\n  const std::int64_t num_unique_keys = 1ll << key_size_magnitude;\n\n  // Size of each key group\n  const std::int64_t key_size = num_items / num_unique_keys;\n\n  using counting_it      = thrust::counting_iterator<std::int64_t>;\n  using transform_key_it = thrust::transform_iterator<div_op, counting_it>;\n  using transform_val_it = thrust::transform_iterator<mod_op, counting_it>;\n\n  counting_it count_begin(0ll);\n  counting_it count_end = count_begin + num_items;\n  ASSERT_EQUAL(static_cast<std::int64_t>(thrust::distance(count_begin, count_end)),\n               num_items);\n\n  transform_key_it keys_begin(count_begin, div_op{key_size});\n  transform_key_it keys_end(count_end, div_op{key_size});\n\n  transform_val_it values_begin(count_begin, mod_op{key_size});\n\n  thrust::device_vector<std::int64_t> output_keys(num_unique_keys);\n  thrust::device_vector<std::int64_t> output_values(num_unique_keys);\n\n  // example:\n  //  items:        6\n  //  unique_keys:  2\n  //  key_size:     3\n  //  keys:         0 0 0 | 1 1 1 \n  //  values:       0 1 2 | 1 2 3\n  //  result:       3       6     = sum(range(key_size)) + key_size * key_id\n  thrust::reduce_by_key(keys_begin,\n                        keys_end,\n                        values_begin,\n                        output_keys.begin(),\n                        output_values.begin());\n\n  ASSERT_EQUAL(\n    true,\n    thrust::equal(output_keys.begin(), output_keys.end(), count_begin));\n\n  thrust::host_vector<std::int64_t> result = output_values;\n\n  const std::int64_t sum = (key_size - 1) * key_size / 2;\n  for (std::int64_t key_id = 0; key_id < num_unique_keys; key_id++)\n  {\n    ASSERT_EQUAL(result[key_id], sum + key_id * key_size);\n  }\n}\n\nvoid TestReduceByKeyWithBigIndexes()\n{\n  TestReduceByKeyWithBigIndexesHelper(30);\n  TestReduceByKeyWithBigIndexesHelper(31);\n  TestReduceByKeyWithBigIndexesHelper(32);\n  TestReduceByKeyWithBigIndexesHelper(33);\n}\nDECLARE_UNITTEST(TestReduceByKeyWithBigIndexes);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n#include <unittest/unittest.h>\n\n#include <cstdint>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          BinaryPredicate pred,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename BinaryPredicate, typename BinaryFunction, typename Iterator5>\n__global__\nvoid reduce_by_key_kernel(ExecutionPolicy exec,\n                          Iterator1 keys_first, Iterator1 keys_last,\n                          Iterator2 values_first,\n                          Iterator3 keys_result,\n                          Iterator4 values_result,\n                          BinaryPredicate pred,\n                          BinaryFunction binary_op,\n                          Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred, binary_op);\n}\n#endif\n\n\ntemplate<typename T>\nstruct is_equal_div_10_reduce\n{\n  __host__ __device__\n  bool operator()(const T x, const T& y) const { return ((int) x / 10) == ((int) y / 10); }\n};\n\n\ntemplate<typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\n\ntemplate<typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0; \n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy>\nvoid TestReduceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> keys;\n  thrust::device_vector<T> values;\n\n  typedef typename thrust::pair<\n    typename thrust::device_vector<T>::iterator,\n    typename thrust::device_vector<T>::iterator\n  > iterator_pair;\n\n  thrust::device_vector<iterator_pair> new_last_vec(1);\n  iterator_pair new_last;\n  \n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n  \n  thrust::device_vector<T> output_keys(keys.size());\n  thrust::device_vector<T> output_values(values.size());\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n  \n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_reduce<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n  \n  // test BinaryFunction\n  initialize_keys(keys);  initialize_values(values);\n  \n  reduce_by_key_kernel<<<1,1>>>(exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), thrust::equal_to<T>(), thrust::plus<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n  \n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n}\n\n\nvoid TestReduceByKeyDeviceSeq()\n{\n  TestReduceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceSeq);\n\n\nvoid TestReduceByKeyDeviceDevice()\n{\n  TestReduceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceDevice);\n\n\nvoid TestReduceByKeyDeviceNoSync()\n{\n  TestReduceByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReduceByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector keys;\n  Vector values;\n\n  thrust::pair<Vector::iterator, Vector::iterator> new_last;\n\n  // basic test\n  initialize_keys(keys);  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);  initialize_values(values);\n  \n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), is_equal_div_10_reduce<T>());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), thrust::equal_to<T>(), thrust::plus<T>());\n\n  ASSERT_EQUAL(new_last.first  - output_keys.begin(),   5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n  \n  ASSERT_EQUAL(output_values[0],  1);\n  ASSERT_EQUAL(output_values[1],  2);\n  ASSERT_EQUAL(output_values[2],  3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestReduceByKeyCudaStreamsSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsSync);\n\n\nvoid TestReduceByKeyCudaStreamsNoSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsNoSync);\n\n\n// Maps indices to key ids\nclass div_op : public thrust::unary_function<std::int64_t, std::int64_t>\n{\n  std::int64_t m_divisor;\n\npublic:\n  __host__ div_op(std::int64_t divisor)\n    : m_divisor(divisor)\n  {}\n\n  __host__ __device__\n  std::int64_t operator()(std::int64_t x) const\n  {\n    return x / m_divisor;\n  }\n};\n\n// Produces unique sequence for key\nclass mod_op : public thrust::unary_function<std::int64_t, std::int64_t>\n{\n  std::int64_t m_divisor;\n\npublic:\n  __host__ mod_op(std::int64_t divisor)\n    : m_divisor(divisor)\n  {}\n\n  __host__ __device__\n  std::int64_t operator()(std::int64_t x) const\n  {\n    // div: 2          \n    // idx: 0 1   2 3   4 5 \n    // key: 0 0 | 1 1 | 2 2 \n    // mod: 0 1 | 0 1 | 0 1\n    // ret: 0 1   1 2   2 3\n    return (x % m_divisor) + (x / m_divisor);\n  }\n};\n\n\nvoid TestReduceByKeyWithBigIndexesHelper(int magnitude)\n{\n  const std::int64_t key_size_magnitude = 8;\n  ASSERT_EQUAL(true, key_size_magnitude < magnitude);\n\n  const std::int64_t num_items       = 1ll << magnitude;\n  const std::int64_t num_unique_keys = 1ll << key_size_magnitude;\n\n  // Size of each key group\n  const std::int64_t key_size = num_items / num_unique_keys;\n\n  using counting_it      = thrust::counting_iterator<std::int64_t>;\n  using transform_key_it = thrust::transform_iterator<div_op, counting_it>;\n  using transform_val_it = thrust::transform_iterator<mod_op, counting_it>;\n\n  counting_it count_begin(0ll);\n  counting_it count_end = count_begin + num_items;\n  ASSERT_EQUAL(static_cast<std::int64_t>(thrust::distance(count_begin, count_end)),\n               num_items);\n\n  transform_key_it keys_begin(count_begin, div_op{key_size});\n  transform_key_it keys_end(count_end, div_op{key_size});\n\n  transform_val_it values_begin(count_begin, mod_op{key_size});\n\n  thrust::device_vector<std::int64_t> output_keys(num_unique_keys);\n  thrust::device_vector<std::int64_t> output_values(num_unique_keys);\n\n  // example:\n  //  items:        6\n  //  unique_keys:  2\n  //  key_size:     3\n  //  keys:         0 0 0 | 1 1 1 \n  //  values:       0 1 2 | 1 2 3\n  //  result:       3       6     = sum(range(key_size)) + key_size * key_id\n  thrust::reduce_by_key(keys_begin,\n                        keys_end,\n                        values_begin,\n                        output_keys.begin(),\n                        output_values.begin());\n\n  ASSERT_EQUAL(\n    true,\n    thrust::equal(output_keys.begin(), output_keys.end(), count_begin));\n\n  thrust::host_vector<std::int64_t> result = output_values;\n\n  const std::int64_t sum = (key_size - 1) * key_size / 2;\n  for (std::int64_t key_id = 0; key_id < num_unique_keys; key_id++)\n  {\n    ASSERT_EQUAL(result[key_id], sum + key_id * key_size);\n  }\n}\n\nvoid TestReduceByKeyWithBigIndexes()\n{\n  TestReduceByKeyWithBigIndexesHelper(30);\n  TestReduceByKeyWithBigIndexesHelper(31);\n  TestReduceByKeyWithBigIndexesHelper(32);\n  TestReduceByKeyWithBigIndexesHelper(33);\n}\nDECLARE_UNITTEST(TestReduceByKeyWithBigIndexes);\n", "source": "/kaggle/working/thrust/testing/cuda/reduce_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/remove.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid remove_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val, Iterator2 result)\n{\n  *result = thrust::remove(exec, first, last, val);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid remove_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::remove_if(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid remove_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::remove_if(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__\nvoid remove_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, T val, Iterator3 result2)\n{\n  *result2 = thrust::remove_copy(exec, first, last, result1, val);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid remove_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, Iterator3 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid remove_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result, Predicate pred, Iterator4 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, stencil_first, result, pred);\n}\n#endif\n\n\ntemplate<typename T>\nstruct is_even\n  : thrust::unary_function<T,bool>\n{\n  __host__ __device__\n  bool operator()(T x) { return (static_cast<unsigned int>(x) & 1) == 0; }\n};\n\n\ntemplate<typename T>\nstruct is_true\n  : thrust::unary_function<T,bool>\n{\n  __host__ __device__\n  bool operator()(T x) { return x ? true : false; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  size_t h_size = thrust::remove(h_data.begin(), h_data.end(), 0) - h_data.begin();\n\n  remove_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), 0, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveDeviceSeq()\n{\n  TestRemoveDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveDeviceSeq);\n\n\nvoid TestRemoveDeviceDevice()\n{\n  TestRemoveDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), is_true<int>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveIfDeviceSeq()\n{\n  TestRemoveIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceSeq);\n\n\nvoid TestRemoveIfDeviceDevice()\n{\n  TestRemoveIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  thrust::host_vector<bool>   h_stencil = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n  \n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), h_stencil.begin(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), is_true<int>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveIfStencilDeviceSeq()\n{\n  TestRemoveIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceSeq);\n\n\nvoid TestRemoveIfStencilDeviceDevice()\n{\n  TestRemoveIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n  \n  size_t h_size = thrust::remove_copy(h_data.begin(), h_data.end(), h_result.begin(), 0) - h_result.begin();\n\n  remove_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), 0, d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyDeviceSeq()\n{\n  TestRemoveCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceSeq);\n\n\nvoid TestRemoveCopyDeviceDevice()\n{\n  TestRemoveCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n  \n  size_t h_size = thrust::remove_copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyIfDeviceSeq()\n{\n  TestRemoveCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceSeq);\n\n\nvoid TestRemoveCopyIfDeviceDevice()\n{\n  TestRemoveCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  thrust::host_vector<bool>   h_stencil = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n  \n  size_t h_size = thrust::remove_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyIfStencilDeviceSeq()\n{\n  TestRemoveCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceSeq);\n\n\nvoid TestRemoveCopyIfStencilDeviceDevice()\n{\n  TestRemoveCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceDevice);\n#endif\n\n\nvoid TestRemoveCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove(thrust::cuda::par.on(s),\n                                        data.begin(), \n                                        data.end(), \n                                        (T) 2);\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCudaStreams);\n\n\nvoid TestRemoveCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy(thrust::cuda::par.on(s),\n                                             data.begin(), \n                                             data.end(), \n                                             result.begin(), \n                                             (T) 2);\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyCudaStreams);\n\n\nvoid TestRemoveIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s),\n                                           data.begin(), \n                                           data.end(), \n                                           is_even<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfCudaStreams);\n\n\nvoid TestRemoveIfStencilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s),\n                                           data.begin(), \n                                           data.end(),\n                                           stencil.begin(),\n                                           thrust::identity<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilCudaStreams);\n\n\nvoid TestRemoveCopyIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(thrust::cuda::par.on(s),\n                                                data.begin(), \n                                                data.end(), \n                                                result.begin(), \n                                                is_even<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfCudaStreams);\n\n\nvoid TestRemoveCopyIfStencilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(thrust::cuda::par.on(s),\n                                                data.begin(), \n                                                data.end(), \n                                                stencil.begin(),\n                                                result.begin(), \n                                                thrust::identity<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/remove.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid remove_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val, Iterator2 result)\n{\n  *result = thrust::remove(exec, first, last, val);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid remove_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::remove_if(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid remove_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::remove_if(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__\nvoid remove_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, T val, Iterator3 result2)\n{\n  *result2 = thrust::remove_copy(exec, first, last, result1, val);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid remove_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, Iterator3 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid remove_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result, Predicate pred, Iterator4 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, stencil_first, result, pred);\n}\n#endif\n\n\ntemplate<typename T>\nstruct is_even\n  : thrust::unary_function<T,bool>\n{\n  __host__ __device__\n  bool operator()(T x) { return (static_cast<unsigned int>(x) & 1) == 0; }\n};\n\n\ntemplate<typename T>\nstruct is_true\n  : thrust::unary_function<T,bool>\n{\n  __host__ __device__\n  bool operator()(T x) { return x ? true : false; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  size_t h_size = thrust::remove(h_data.begin(), h_data.end(), 0) - h_data.begin();\n\n  remove_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), 0, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveDeviceSeq()\n{\n  TestRemoveDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveDeviceSeq);\n\n\nvoid TestRemoveDeviceDevice()\n{\n  TestRemoveDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), is_true<int>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveIfDeviceSeq()\n{\n  TestRemoveIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceSeq);\n\n\nvoid TestRemoveIfDeviceDevice()\n{\n  TestRemoveIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_result(1);\n  \n  thrust::host_vector<bool>   h_stencil = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n  \n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), h_stencil.begin(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), is_true<int>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_result[0] - d_data.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_data.resize(h_size);\n  d_data.resize(d_size);\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\n\nvoid TestRemoveIfStencilDeviceSeq()\n{\n  TestRemoveIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceSeq);\n\n\nvoid TestRemoveIfStencilDeviceDevice()\n{\n  TestRemoveIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n  \n  size_t h_size = thrust::remove_copy(h_data.begin(), h_data.end(), h_result.begin(), 0) - h_result.begin();\n\n  remove_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), 0, d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyDeviceSeq()\n{\n  TestRemoveCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceSeq);\n\n\nvoid TestRemoveCopyDeviceDevice()\n{\n  TestRemoveCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n  \n  size_t h_size = thrust::remove_copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyIfDeviceSeq()\n{\n  TestRemoveCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceSeq);\n\n\nvoid TestRemoveCopyIfDeviceDevice()\n{\n  TestRemoveCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestRemoveCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  thrust::host_vector<bool>   h_stencil = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n  \n  size_t h_size = thrust::remove_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator)d_new_end[0] - d_result.begin();\n  \n  ASSERT_EQUAL(h_size, d_size);\n  \n  h_result.resize(h_size);\n  d_result.resize(d_size);\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\nvoid TestRemoveCopyIfStencilDeviceSeq()\n{\n  TestRemoveCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceSeq);\n\n\nvoid TestRemoveCopyIfStencilDeviceDevice()\n{\n  TestRemoveCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceDevice);\n#endif\n\n\nvoid TestRemoveCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove(thrust::cuda::par.on(s),\n                                        data.begin(), \n                                        data.end(), \n                                        (T) 2);\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCudaStreams);\n\n\nvoid TestRemoveCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy(thrust::cuda::par.on(s),\n                                             data.begin(), \n                                             data.end(), \n                                             result.begin(), \n                                             (T) 2);\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyCudaStreams);\n\n\nvoid TestRemoveIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s),\n                                           data.begin(), \n                                           data.end(), \n                                           is_even<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfCudaStreams);\n\n\nvoid TestRemoveIfStencilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s),\n                                           data.begin(), \n                                           data.end(),\n                                           stencil.begin(),\n                                           thrust::identity<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n\n  ASSERT_EQUAL(data[0], 1);\n  ASSERT_EQUAL(data[1], 1);\n  ASSERT_EQUAL(data[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilCudaStreams);\n\n\nvoid TestRemoveCopyIfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(thrust::cuda::par.on(s),\n                                                data.begin(), \n                                                data.end(), \n                                                result.begin(), \n                                                is_even<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfCudaStreams);\n\n\nvoid TestRemoveCopyIfStencilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(thrust::cuda::par.on(s),\n                                                data.begin(), \n                                                data.end(), \n                                                stencil.begin(),\n                                                result.begin(), \n                                                thrust::identity<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n\n  ASSERT_EQUAL(result[0], 1);\n  ASSERT_EQUAL(result[1], 1);\n  ASSERT_EQUAL(result[2], 3);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/remove.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> d_result(1);\n  \n  typename thrust::host_vector<int>::iterator   h_max = thrust::max_element(h_data.begin(), h_data.end());\n\n  max_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n\n  \n  typename thrust::host_vector<int>::iterator   h_min = thrust::max_element(h_data.begin(), h_data.end(), thrust::greater<int>());\n\n  max_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n}\n\n\nvoid TestMaxElementDeviceSeq()\n{\n  TestMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceSeq);\n\n\nvoid TestMaxElementDeviceDevice()\n{\n  TestMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceDevice);\n\n\nvoid TestMaxElementDeviceNoSync()\n{\n  TestMaxElementDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMaxElementCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  ASSERT_EQUAL( *thrust::max_element(streampolicy, data.begin(), data.end()), 5);\n  ASSERT_EQUAL( thrust::max_element(streampolicy, data.begin(), data.end()) - data.begin(), 1);\n  \n  ASSERT_EQUAL( *thrust::max_element(streampolicy, data.begin(), data.end(), thrust::greater<T>()), 1);\n  ASSERT_EQUAL( thrust::max_element(streampolicy, data.begin(), data.end(), thrust::greater<T>()) - data.begin(), 2);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestMaxElementCudaStreamsSync(){\n  TestMaxElementCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsSync);\n\n\nvoid TestMaxElementCudaStreamsNoSync(){\n  TestMaxElementCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsNoSync);\n\n\nvoid TestMaxElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::max_element(thrust::device, raw_ptr, raw_ptr+n) - raw_ptr, 1);\n  ASSERT_EQUAL( thrust::max_element(thrust::device, raw_ptr, raw_ptr+n, thrust::greater<T>()) - raw_ptr, 2);\n}\nDECLARE_UNITTEST(TestMaxElementDevicePointer);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> d_result(1);\n  \n  typename thrust::host_vector<int>::iterator   h_max = thrust::max_element(h_data.begin(), h_data.end());\n\n  max_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n\n  \n  typename thrust::host_vector<int>::iterator   h_min = thrust::max_element(h_data.begin(), h_data.end(), thrust::greater<int>());\n\n  max_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n}\n\n\nvoid TestMaxElementDeviceSeq()\n{\n  TestMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceSeq);\n\n\nvoid TestMaxElementDeviceDevice()\n{\n  TestMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceDevice);\n\n\nvoid TestMaxElementDeviceNoSync()\n{\n  TestMaxElementDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMaxElementCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  ASSERT_EQUAL( *thrust::max_element(streampolicy, data.begin(), data.end()), 5);\n  ASSERT_EQUAL( thrust::max_element(streampolicy, data.begin(), data.end()) - data.begin(), 1);\n  \n  ASSERT_EQUAL( *thrust::max_element(streampolicy, data.begin(), data.end(), thrust::greater<T>()), 1);\n  ASSERT_EQUAL( thrust::max_element(streampolicy, data.begin(), data.end(), thrust::greater<T>()) - data.begin(), 2);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestMaxElementCudaStreamsSync(){\n  TestMaxElementCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsSync);\n\n\nvoid TestMaxElementCudaStreamsNoSync(){\n  TestMaxElementCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsNoSync);\n\n\nvoid TestMaxElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::max_element(thrust::device, raw_ptr, raw_ptr+n) - raw_ptr, 1);\n  ASSERT_EQUAL( thrust::max_element(thrust::device, raw_ptr, raw_ptr+n, thrust::greater<T>()) - raw_ptr, 2);\n}\nDECLARE_UNITTEST(TestMaxElementDevicePointer);\n", "source": "/kaggle/working/thrust/testing/cuda/max_element.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n\n\ntemplate<typename T>\nstruct my_less\n{\n  __host__ __device__\n  bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Compare>\n__global__\nvoid sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Compare comp)\n{\n  thrust::sort_by_key(exec, keys_first, keys_last, values_first, comp);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortByKeyDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T>   h_keys = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_keys = h_keys;\n\n  thrust::host_vector<T>   h_values = h_keys;\n  thrust::device_vector<T> d_values = d_keys;\n\n  sort_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_values.begin(), comp);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::sort_by_key(h_keys.begin(), h_keys.end(), h_values.begin(), comp);\n\n  ASSERT_EQUAL(h_keys, d_keys);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\n\ntemplate<typename T>\n  struct TestComparisonSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortByKeyDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortByKeyDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestComparisonSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortByKeyDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortByKeyDeviceDeviceDeviceInstance;\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestSortByKeyDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortByKeyDevice<T>(exec, n, thrust::less<T>());\n};\n\n\ntemplate<typename T>\n  struct TestSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<\n  TestSortByKeyDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortByKeyDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<\n  TestSortByKeyDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortByKeyDeviceDeviceInstance;\n#endif\n\n\nvoid TestComparisonSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n  thrust::device_vector<int> vals(10);\n\n  keys[0] = 9; vals[0] = 9;\n  keys[1] = 3; vals[1] = 3;\n  keys[2] = 2; vals[2] = 2;\n  keys[3] = 0; vals[3] = 0;\n  keys[4] = 4; vals[4] = 4;\n  keys[5] = 7; vals[5] = 7;\n  keys[6] = 8; vals[6] = 8;\n  keys[7] = 1; vals[7] = 1;\n  keys[8] = 5; vals[8] = 5;\n  keys[9] = 6; vals[9] = 6;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), my_less<int>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortByKeyCudaStreams);\n\n\nvoid TestSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n  thrust::device_vector<int> vals(10);\n\n  keys[0] = 9; vals[0] = 9;\n  keys[1] = 3; vals[1] = 3;\n  keys[2] = 2; vals[2] = 2;\n  keys[3] = 0; vals[3] = 0;\n  keys[4] = 4; vals[4] = 4;\n  keys[5] = 7; vals[5] = 7;\n  keys[6] = 8; vals[6] = 8;\n  keys[7] = 1; vals[7] = 1;\n  keys[8] = 5; vals[8] = 5;\n  keys[9] = 6; vals[9] = 6;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n\n\ntemplate<typename T>\nstruct my_less\n{\n  __host__ __device__\n  bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Compare>\n__global__\nvoid sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Compare comp)\n{\n  thrust::sort_by_key(exec, keys_first, keys_last, values_first, comp);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortByKeyDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T>   h_keys = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_keys = h_keys;\n\n  thrust::host_vector<T>   h_values = h_keys;\n  thrust::device_vector<T> d_values = d_keys;\n\n  sort_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_values.begin(), comp);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::sort_by_key(h_keys.begin(), h_keys.end(), h_values.begin(), comp);\n\n  ASSERT_EQUAL(h_keys, d_keys);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\n\ntemplate<typename T>\n  struct TestComparisonSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortByKeyDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortByKeyDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestComparisonSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<\n  TestComparisonSortByKeyDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestComparisonSortByKeyDeviceDeviceDeviceInstance;\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestSortByKeyDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortByKeyDevice<T>(exec, n, thrust::less<T>());\n};\n\n\ntemplate<typename T>\n  struct TestSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<\n  TestSortByKeyDeviceSeq,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortByKeyDeviceSeqInstance;\n\n\ntemplate<typename T>\n  struct TestSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<\n  TestSortByKeyDeviceDevice,\n  unittest::type_list<unittest::int8_t,unittest::int32_t>\n> TestSortByKeyDeviceDeviceInstance;\n#endif\n\n\nvoid TestComparisonSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n  thrust::device_vector<int> vals(10);\n\n  keys[0] = 9; vals[0] = 9;\n  keys[1] = 3; vals[1] = 3;\n  keys[2] = 2; vals[2] = 2;\n  keys[3] = 0; vals[3] = 0;\n  keys[4] = 4; vals[4] = 4;\n  keys[5] = 7; vals[5] = 7;\n  keys[6] = 8; vals[6] = 8;\n  keys[7] = 1; vals[7] = 1;\n  keys[8] = 5; vals[8] = 5;\n  keys[9] = 6; vals[9] = 6;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), my_less<int>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortByKeyCudaStreams);\n\n\nvoid TestSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys(10);\n  thrust::device_vector<int> vals(10);\n\n  keys[0] = 9; vals[0] = 9;\n  keys[1] = 3; vals[1] = 3;\n  keys[2] = 2; vals[2] = 2;\n  keys[3] = 0; vals[3] = 0;\n  keys[4] = 4; vals[4] = 4;\n  keys[5] = 7; vals[5] = 7;\n  keys[6] = 8; vals[6] = 8;\n  keys[7] = 1; vals[7] = 1;\n  keys[8] = 5; vals[8] = 5;\n  keys[9] = 6; vals[9] = 6;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/sort_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid count_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::count(exec, first, last, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCountDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n  \n  size_t h_result = thrust::count(h_data.begin(), h_data.end(), T(5));\n\n  count_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), T(5), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nvoid TestCountDeviceSeq(const size_t n)\n{\n  TestCountDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCountDeviceDevice(const size_t n)\n{\n  TestCountDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid count_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::count_if(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct greater_than_five\n{\n  __host__ __device__ bool operator()(const T &x) const {return x > 5;}\n};\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCountIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n  \n  size_t h_result = thrust::count_if(h_data.begin(), h_data.end(), greater_than_five<T>());\n  count_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), greater_than_five<T>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nvoid TestCountIfDeviceSeq(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCountIfDeviceDevice(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceDevice);\n#endif\n\n\nvoid TestCountCudaStreams()\n{\n  thrust::device_vector<int> data(5);\n  data[0] = 1; data[1] = 1; data[2] = 0; data[3] = 0; data[4] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 0), 2);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 1), 3);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 2), 0);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestCountCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid count_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::count(exec, first, last, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCountDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n  \n  size_t h_result = thrust::count(h_data.begin(), h_data.end(), T(5));\n\n  count_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), T(5), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nvoid TestCountDeviceSeq(const size_t n)\n{\n  TestCountDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCountDeviceDevice(const size_t n)\n{\n  TestCountDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid count_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::count_if(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct greater_than_five\n{\n  __host__ __device__ bool operator()(const T &x) const {return x > 5;}\n};\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestCountIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n  \n  size_t h_result = thrust::count_if(h_data.begin(), h_data.end(), greater_than_five<T>());\n  count_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), greater_than_five<T>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nvoid TestCountIfDeviceSeq(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestCountIfDeviceDevice(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceDevice);\n#endif\n\n\nvoid TestCountCudaStreams()\n{\n  thrust::device_vector<int> data(5);\n  data[0] = 1; data[1] = 1; data[2] = 0; data[3] = 0; data[4] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 0), 2);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 1), 3);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 2), 0);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestCountCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/count.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid is_sorted_until_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::is_sorted_until(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsSortedUntilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n  \n  is_sorted_until_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.begin() + 1, (iter_type)result[0]);\n  \n  thrust::sort(v.begin(), v.end());\n  \n  is_sorted_until_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.end(), (iter_type)result[0]);\n}\n\n\nvoid TestIsSortedUntilDeviceSeq()\n{\n  TestIsSortedUntilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceSeq);\n\n\nvoid TestIsSortedUntilDeviceDevice()\n{\n  TestIsSortedUntilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceDevice);\n#endif\n\n\nvoid TestIsSortedUntilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  typedef Vector::value_type T;\n  typedef Vector::iterator Iterator;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector v(4);\n  v[0] = 0; v[1] = 5; v[2] = 8; v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last  = v.begin() + 0;\n  Iterator ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 1;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 2;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::less<T>()));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::less<T>()));\n\n  last = v.begin() + 1;\n  ref = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  first = v.begin() + 2;\n  last = v.begin() + 4;\n  ref = v.begin() + 4;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedUntilCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid is_sorted_until_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::is_sorted_until(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsSortedUntilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n  \n  is_sorted_until_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.begin() + 1, (iter_type)result[0]);\n  \n  thrust::sort(v.begin(), v.end());\n  \n  is_sorted_until_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.end(), (iter_type)result[0]);\n}\n\n\nvoid TestIsSortedUntilDeviceSeq()\n{\n  TestIsSortedUntilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceSeq);\n\n\nvoid TestIsSortedUntilDeviceDevice()\n{\n  TestIsSortedUntilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceDevice);\n#endif\n\n\nvoid TestIsSortedUntilCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  typedef Vector::value_type T;\n  typedef Vector::iterator Iterator;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector v(4);\n  v[0] = 0; v[1] = 5; v[2] = 8; v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last  = v.begin() + 0;\n  Iterator ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 1;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 2;\n  ref = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::less<T>()));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::less<T>()));\n\n  last = v.begin() + 1;\n  ref = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  last = v.begin() + 4;\n  ref = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  first = v.begin() + 2;\n  last = v.begin() + 4;\n  ref = v.begin() + 4;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, thrust::greater<T>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedUntilCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/is_sorted_until.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate>\n__global__\nvoid minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMinMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_min;\n  typename thrust::host_vector<int>::iterator   h_max;\n  typename thrust::device_vector<int>::iterator d_min;\n  typename thrust::device_vector<int>::iterator d_max;\n\n  typedef thrust::pair<\n    typename thrust::device_vector<int>::iterator,\n    typename thrust::device_vector<int>::iterator\n  > pair_type;\n\n  thrust::device_vector<pair_type> d_result(1);\n  \n  h_min = thrust::minmax_element(h_data.begin(), h_data.end()).first;\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end()).second;\n\n  d_min = thrust::minmax_element(d_data.begin(), d_data.end()).first;\n  d_max = thrust::minmax_element(d_data.begin(), d_data.end()).second;\n\n  minmax_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  d_min = ((pair_type)d_result[0]).first;\n  d_max = ((pair_type)d_result[0]).second;\n  \n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n  \n  h_max = thrust::minmax_element(h_data.begin(), h_data.end(), thrust::greater<int>()).first;\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end(), thrust::greater<int>()).second;\n\n  minmax_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  d_max = ((pair_type)d_result[0]).first;\n  d_min = ((pair_type)d_result[0]).second;\n  \n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n}\n\n\nvoid TestMinMaxElementDeviceSeq()\n{\n  TestMinMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceSeq);\n\n\nvoid TestMinMaxElementDeviceDevice()\n{\n  TestMinMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceDevice);\n#endif\n\n\nvoid TestMinMaxElementCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL( *thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first,  1);\n  ASSERT_EQUAL( *thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second, 5);\n  ASSERT_EQUAL(  thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first  - data.begin(), 2);\n  ASSERT_EQUAL(  thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second - data.begin(), 1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinMaxElementCudaStreams);\n\nvoid TestMinMaxElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::minmax_element(thrust::device, raw_ptr, raw_ptr+n).first - raw_ptr,  2);\n  ASSERT_EQUAL( thrust::minmax_element(thrust::device, raw_ptr, raw_ptr+n).second - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinMaxElementDevicePointer);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate>\n__global__\nvoid minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMinMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_min;\n  typename thrust::host_vector<int>::iterator   h_max;\n  typename thrust::device_vector<int>::iterator d_min;\n  typename thrust::device_vector<int>::iterator d_max;\n\n  typedef thrust::pair<\n    typename thrust::device_vector<int>::iterator,\n    typename thrust::device_vector<int>::iterator\n  > pair_type;\n\n  thrust::device_vector<pair_type> d_result(1);\n  \n  h_min = thrust::minmax_element(h_data.begin(), h_data.end()).first;\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end()).second;\n\n  d_min = thrust::minmax_element(d_data.begin(), d_data.end()).first;\n  d_max = thrust::minmax_element(d_data.begin(), d_data.end()).second;\n\n  minmax_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  d_min = ((pair_type)d_result[0]).first;\n  d_max = ((pair_type)d_result[0]).second;\n  \n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n  \n  h_max = thrust::minmax_element(h_data.begin(), h_data.end(), thrust::greater<int>()).first;\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end(), thrust::greater<int>()).second;\n\n  minmax_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  d_max = ((pair_type)d_result[0]).first;\n  d_min = ((pair_type)d_result[0]).second;\n  \n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n}\n\n\nvoid TestMinMaxElementDeviceSeq()\n{\n  TestMinMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceSeq);\n\n\nvoid TestMinMaxElementDeviceDevice()\n{\n  TestMinMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceDevice);\n#endif\n\n\nvoid TestMinMaxElementCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL( *thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first,  1);\n  ASSERT_EQUAL( *thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second, 5);\n  ASSERT_EQUAL(  thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first  - data.begin(), 2);\n  ASSERT_EQUAL(  thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second - data.begin(), 1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinMaxElementCudaStreams);\n\nvoid TestMinMaxElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::minmax_element(thrust::device, raw_ptr, raw_ptr+n).first - raw_ptr,  2);\n  ASSERT_EQUAL( thrust::minmax_element(thrust::device, raw_ptr, raw_ptr+n).second - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinMaxElementDevicePointer);\n\n", "source": "/kaggle/working/thrust/testing/cuda/minmax_element.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_symmetric_difference_by_key_kernel(ExecutionPolicy exec,\n                                            Iterator1 keys_first1, Iterator1 keys_last1,\n                                            Iterator2 keys_first2, Iterator2 keys_last2,\n                                            Iterator3 values_first1,\n                                            Iterator4 values_first2,\n                                            Iterator5 keys_result,\n                                            Iterator6 values_result,\n                                            Iterator7 result)\n{\n  *result = thrust::set_symmetric_difference_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 6;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 7;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 2; ref_key[1] = 3; ref_key[2] = 3; ref_key[3] = 6; ref_key[4] = 7;\n  ref_val[0] = 0; ref_val[1] = 1; ref_val[2] = 1; ref_val[3] = 0; ref_val[4] = 1;\n\n  Vector result_key(5), result_val(5);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_symmetric_difference_by_key_kernel<<<1,1>>>(exec,\n                                                  a_key.begin(), a_key.end(),\n                                                  b_key.begin(), b_key.end(),\n                                                  a_val.begin(),\n                                                  b_val.begin(),\n                                                  result_key.begin(),\n                                                  result_val.begin(),\n                                                  end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter_pair end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetSymmetricDifferenceByKeyDeviceSeq()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceSeq);\n\n\nvoid TestSetSymmetricDifferenceByKeyDeviceDevice()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetSymmetricDifferenceByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 6;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 7;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 2; ref_key[1] = 3; ref_key[2] = 3; ref_key[3] = 6; ref_key[4] = 7;\n  ref_val[0] = 0; ref_val[1] = 1; ref_val[2] = 1; ref_val[3] = 0; ref_val[4] = 1;\n\n  Vector result_key(5), result_val(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_symmetric_difference_by_key(thrust::cuda::par.on(s),\n                                            a_key.begin(), a_key.end(),\n                                            b_key.begin(), b_key.end(),\n                                            a_val.begin(),\n                                            b_val.begin(),\n                                            result_key.begin(),\n                                            result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_symmetric_difference_by_key_kernel(ExecutionPolicy exec,\n                                            Iterator1 keys_first1, Iterator1 keys_last1,\n                                            Iterator2 keys_first2, Iterator2 keys_last2,\n                                            Iterator3 values_first1,\n                                            Iterator4 values_first2,\n                                            Iterator5 keys_result,\n                                            Iterator6 values_result,\n                                            Iterator7 result)\n{\n  *result = thrust::set_symmetric_difference_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 6;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 7;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 2; ref_key[1] = 3; ref_key[2] = 3; ref_key[3] = 6; ref_key[4] = 7;\n  ref_val[0] = 0; ref_val[1] = 1; ref_val[2] = 1; ref_val[3] = 0; ref_val[4] = 1;\n\n  Vector result_key(5), result_val(5);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_symmetric_difference_by_key_kernel<<<1,1>>>(exec,\n                                                  a_key.begin(), a_key.end(),\n                                                  b_key.begin(), b_key.end(),\n                                                  a_val.begin(),\n                                                  b_val.begin(),\n                                                  result_key.begin(),\n                                                  result_val.begin(),\n                                                  end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter_pair end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetSymmetricDifferenceByKeyDeviceSeq()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceSeq);\n\n\nvoid TestSetSymmetricDifferenceByKeyDeviceDevice()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetSymmetricDifferenceByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(4), b_key(5);\n  Vector a_val(4), b_val(5);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4; a_key[3] = 6;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0; a_val[3] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4; b_key[4] = 7;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1; b_val[4] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 2; ref_key[1] = 3; ref_key[2] = 3; ref_key[3] = 6; ref_key[4] = 7;\n  ref_val[0] = 0; ref_val[1] = 1; ref_val[2] = 1; ref_val[3] = 0; ref_val[4] = 1;\n\n  Vector result_key(5), result_val(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_symmetric_difference_by_key(thrust::cuda::par.on(s),\n                                            a_key.begin(), a_key.end(),\n                                            b_key.begin(), b_key.end(),\n                                            a_val.begin(),\n                                            b_val.begin(),\n                                            result_key.begin(),\n                                            result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_symmetric_difference_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/scan.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid inclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::inclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid exclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename T>\n__global__\nvoid exclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result, T init)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result, init);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScanByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_keys(n);\n  for(size_t i = 0, k = 0; i < n; i++)\n  {\n    h_keys[i] = static_cast<int>(k);\n    if(rand() % 10 == 0)\n    {\n      k++;\n    }\n  }\n  thrust::device_vector<int> d_keys = h_keys;\n  \n  thrust::host_vector<int>   h_vals = unittest::random_integers<int>(n);\n  for(size_t i = 0; i < n; i++)\n  {\n    h_vals[i] = i % 10;\n  }\n  thrust::device_vector<int> d_vals = h_vals;\n  \n  thrust::host_vector<int>   h_output(n);\n  thrust::device_vector<int> d_output(n);\n  \n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  // in-place scans: in/out values aliasing\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec,d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: keys/values aliasing\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n\n  d_keys = h_keys;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n}\n\n\nvoid TestScanByKeyDeviceSeq()\n{\n  TestScanByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceSeq);\n\n\nvoid TestScanByKeyDeviceDevice()\n{\n  TestScanByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceDevice);\n#endif\n\n\nvoid TestInclusiveScanByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n\n  Vector keys(7);\n  Vector vals(7);\n\n  Vector output(7, 0);\n\n  keys[0] = 0; vals[0] = 1;\n  keys[1] = 1; vals[1] = 2;\n  keys[2] = 1; vals[2] = 3;\n  keys[3] = 1; vals[3] = 4;\n  keys[4] = 2; vals[4] = 5;\n  keys[5] = 3; vals[5] = 6;\n  keys[6] = 3; vals[6] = 7;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator iter = thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  5);\n  ASSERT_EQUAL(output[3],  9);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 13);\n  \n  thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), thrust::equal_to<T>(), thrust::multiplies<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  6);\n  ASSERT_EQUAL(output[3], 24);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 42);\n  \n  thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), thrust::equal_to<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  5);\n  ASSERT_EQUAL(output[3],  9);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 13);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInclusiveScanByKeyCudaStreams);\n\n\nvoid TestExclusiveScanByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n\n  Vector keys(7);\n  Vector vals(7);\n\n  Vector output(7, 0);\n\n  keys[0] = 0; vals[0] = 1;\n  keys[1] = 1; vals[1] = 2;\n  keys[2] = 1; vals[2] = 3;\n  keys[3] = 1; vals[3] = 4;\n  keys[4] = 2; vals[4] = 5;\n  keys[5] = 3; vals[5] = 6;\n  keys[6] = 3; vals[6] = 7;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  Iterator iter = thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  ASSERT_EQUAL(output[0], 0);\n  ASSERT_EQUAL(output[1], 0);\n  ASSERT_EQUAL(output[2], 2);\n  ASSERT_EQUAL(output[3], 5);\n  ASSERT_EQUAL(output[4], 0);\n  ASSERT_EQUAL(output[5], 0);\n  ASSERT_EQUAL(output[6], 6);\n\n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10));\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 12);\n  ASSERT_EQUAL(output[3], 15);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 16);\n  \n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), thrust::equal_to<T>(), thrust::multiplies<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 60);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 60);\n  \n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), thrust::equal_to<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 12);\n  ASSERT_EQUAL(output[3], 15);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 16);\n}\nDECLARE_UNITTEST(TestExclusiveScanByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/scan.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid inclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::inclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid exclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename T>\n__global__\nvoid exclusive_scan_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result, T init)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result, init);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestScanByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_keys(n);\n  for(size_t i = 0, k = 0; i < n; i++)\n  {\n    h_keys[i] = static_cast<int>(k);\n    if(rand() % 10 == 0)\n    {\n      k++;\n    }\n  }\n  thrust::device_vector<int> d_keys = h_keys;\n  \n  thrust::host_vector<int>   h_vals = unittest::random_integers<int>(n);\n  for(size_t i = 0; i < n; i++)\n  {\n    h_vals[i] = i % 10;\n  }\n  thrust::device_vector<int> d_vals = h_vals;\n  \n  thrust::host_vector<int>   h_output(n);\n  thrust::device_vector<int> d_output(n);\n  \n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  // in-place scans: in/out values aliasing\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec,d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n  \n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: keys/values aliasing\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n\n  d_keys = h_keys;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1,1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n}\n\n\nvoid TestScanByKeyDeviceSeq()\n{\n  TestScanByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceSeq);\n\n\nvoid TestScanByKeyDeviceDevice()\n{\n  TestScanByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceDevice);\n#endif\n\n\nvoid TestInclusiveScanByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n\n  Vector keys(7);\n  Vector vals(7);\n\n  Vector output(7, 0);\n\n  keys[0] = 0; vals[0] = 1;\n  keys[1] = 1; vals[1] = 2;\n  keys[2] = 1; vals[2] = 3;\n  keys[3] = 1; vals[3] = 4;\n  keys[4] = 2; vals[4] = 5;\n  keys[5] = 3; vals[5] = 6;\n  keys[6] = 3; vals[6] = 7;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator iter = thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  5);\n  ASSERT_EQUAL(output[3],  9);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 13);\n  \n  thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), thrust::equal_to<T>(), thrust::multiplies<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  6);\n  ASSERT_EQUAL(output[3], 24);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 42);\n  \n  thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), thrust::equal_to<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0],  1);\n  ASSERT_EQUAL(output[1],  2);\n  ASSERT_EQUAL(output[2],  5);\n  ASSERT_EQUAL(output[3],  9);\n  ASSERT_EQUAL(output[4],  5);\n  ASSERT_EQUAL(output[5],  6);\n  ASSERT_EQUAL(output[6], 13);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInclusiveScanByKeyCudaStreams);\n\n\nvoid TestExclusiveScanByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n\n  Vector keys(7);\n  Vector vals(7);\n\n  Vector output(7, 0);\n\n  keys[0] = 0; vals[0] = 1;\n  keys[1] = 1; vals[1] = 2;\n  keys[2] = 1; vals[2] = 3;\n  keys[3] = 1; vals[3] = 4;\n  keys[4] = 2; vals[4] = 5;\n  keys[5] = 3; vals[5] = 6;\n  keys[6] = 3; vals[6] = 7;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  Iterator iter = thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  ASSERT_EQUAL(output[0], 0);\n  ASSERT_EQUAL(output[1], 0);\n  ASSERT_EQUAL(output[2], 2);\n  ASSERT_EQUAL(output[3], 5);\n  ASSERT_EQUAL(output[4], 0);\n  ASSERT_EQUAL(output[5], 0);\n  ASSERT_EQUAL(output[6], 6);\n\n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10));\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 12);\n  ASSERT_EQUAL(output[3], 15);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 16);\n  \n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), thrust::equal_to<T>(), thrust::multiplies<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 20);\n  ASSERT_EQUAL(output[3], 60);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 60);\n  \n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), thrust::equal_to<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 10);\n  ASSERT_EQUAL(output[1], 10);\n  ASSERT_EQUAL(output[2], 12);\n  ASSERT_EQUAL(output[3], 15);\n  ASSERT_EQUAL(output[4], 10);\n  ASSERT_EQUAL(output[5], 10);\n  ASSERT_EQUAL(output[6], 16);\n}\nDECLARE_UNITTEST(TestExclusiveScanByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/scan_by_key.cu"}
{"input": "#include <thrust/universal_vector.h>\n\n#include <unittest/unittest.h>\n\ntemplate <class VecT>\n__host__ __device__ void universal_vector_access(VecT &in, thrust::universal_vector<bool> &out)\n{\n  const int expected_front  = 4;\n  const int expected_back   = 2;\n\n  out[0] = in.size() == 2 &&               //\n           in[0] == expected_front &&      //\n           in.front() == expected_front && //\n           *in.data() == expected_front && //\n           in[1] == expected_back &&       //\n           in.back() == expected_back;\n}\n\n#if defined(THRUST_TEST_DEVICE_SIDE)\ntemplate <class VecT>\n__global__ void universal_vector_device_access_kernel(VecT &vec,\n                                                      thrust::universal_vector<bool> &out)\n{\n  universal_vector_access(vec, out);\n}\n\ntemplate <class VecT>\nvoid test_universal_vector_access(VecT &vec, thrust::universal_vector<bool> &out)\n{\n  universal_vector_device_access_kernel<<<1, 1>>>(vec, out);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  ASSERT_EQUAL(out[0], true);\n}\n#else\ntemplate <class VecT>\nvoid test_universal_vector_access(VecT &vec, thrust::universal_vector<bool> &out)\n{\n  universal_vector_access(vec, out);\n  ASSERT_EQUAL(out[0], true);\n}\n#endif\n\nvoid TestUniversalVectorDeviceAccess()\n{\n  thrust::universal_vector<thrust::universal_vector<int>> in_storage(1);\n  thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n  in.resize(2);\n  in[0] = 4;\n  in[1] = 2;\n\n  thrust::universal_vector<thrust::universal_vector<bool>> out_storage(1);\n  thrust::universal_vector<bool> &out = *thrust::raw_pointer_cast(out_storage.data());\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n}\nDECLARE_UNITTEST(TestUniversalVectorDeviceAccess);\n\nvoid TestConstUniversalVectorDeviceAccess()\n{\n  thrust::universal_vector<thrust::universal_vector<int>> in_storage(1);\n\n  {\n    thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n    in.resize(2);\n    in[0] = 4;\n    in[1] = 2;\n  }\n\n  const thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n  thrust::universal_vector<thrust::universal_vector<bool>> out_storage(1);\n  thrust::universal_vector<bool> &out = *thrust::raw_pointer_cast(out_storage.data());\n\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n}\nDECLARE_UNITTEST(TestConstUniversalVectorDeviceAccess);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/universal_vector.h>\n\n#include <unittest/unittest.h>\n\ntemplate <class VecT>\n__host__ __device__ void universal_vector_access(VecT &in, thrust::universal_vector<bool> &out)\n{\n  const int expected_front  = 4;\n  const int expected_back   = 2;\n\n  out[0] = in.size() == 2 &&               //\n           in[0] == expected_front &&      //\n           in.front() == expected_front && //\n           *in.data() == expected_front && //\n           in[1] == expected_back &&       //\n           in.back() == expected_back;\n}\n\n#if defined(THRUST_TEST_DEVICE_SIDE)\ntemplate <class VecT>\n__global__ void universal_vector_device_access_kernel(VecT &vec,\n                                                      thrust::universal_vector<bool> &out)\n{\n  universal_vector_access(vec, out);\n}\n\ntemplate <class VecT>\nvoid test_universal_vector_access(VecT &vec, thrust::universal_vector<bool> &out)\n{\n  universal_vector_device_access_kernel<<<1, 1>>>(vec, out);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  ASSERT_EQUAL(out[0], true);\n}\n#else\ntemplate <class VecT>\nvoid test_universal_vector_access(VecT &vec, thrust::universal_vector<bool> &out)\n{\n  universal_vector_access(vec, out);\n  ASSERT_EQUAL(out[0], true);\n}\n#endif\n\nvoid TestUniversalVectorDeviceAccess()\n{\n  thrust::universal_vector<thrust::universal_vector<int>> in_storage(1);\n  thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n  in.resize(2);\n  in[0] = 4;\n  in[1] = 2;\n\n  thrust::universal_vector<thrust::universal_vector<bool>> out_storage(1);\n  thrust::universal_vector<bool> &out = *thrust::raw_pointer_cast(out_storage.data());\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n}\nDECLARE_UNITTEST(TestUniversalVectorDeviceAccess);\n\nvoid TestConstUniversalVectorDeviceAccess()\n{\n  thrust::universal_vector<thrust::universal_vector<int>> in_storage(1);\n\n  {\n    thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n    in.resize(2);\n    in[0] = 4;\n    in[1] = 2;\n  }\n\n  const thrust::universal_vector<int> &in = *thrust::raw_pointer_cast(in_storage.data());\n\n  thrust::universal_vector<thrust::universal_vector<bool>> out_storage(1);\n  thrust::universal_vector<bool> &out = *thrust::raw_pointer_cast(out_storage.data());\n\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n}\nDECLARE_UNITTEST(TestConstUniversalVectorDeviceAccess);\n", "source": "/kaggle/working/thrust/testing/cuda/device_side_universal_vector.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_union_kernel(ExecutionPolicy exec,\n                      Iterator1 first1, Iterator1 last1,\n                      Iterator2 first2, Iterator2 last2,\n                      Iterator3 result1,\n                      Iterator4 result2)\n{\n  *result2 = thrust::set_union(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetUnionDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(5);\n  ref[0] = 0; ref[1] = 2; ref[2] = 3; ref[3] = 3; ref[4] = 4;\n\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_union_kernel<<<1,1>>>(exec,\n                            a.begin(), a.end(),\n                            b.begin(), b.end(),\n                            result.begin(),\n                            end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetUnionDeviceSeq()\n{\n  TestSetUnionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceSeq);\n\n\nvoid TestSetUnionDeviceDevice()\n{\n  TestSetUnionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceDevice);\n#endif\n\n\nvoid TestSetUnionCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(5);\n  ref[0] = 0; ref[1] = 2; ref[2] = 3; ref[3] = 3; ref[4] = 4;\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end = thrust::set_union(thrust::cuda::par.on(s),\n                                   a.begin(), a.end(),\n                                   b.begin(), b.end(),\n                                   result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_union_kernel(ExecutionPolicy exec,\n                      Iterator1 first1, Iterator1 last1,\n                      Iterator2 first2, Iterator2 last2,\n                      Iterator3 result1,\n                      Iterator4 result2)\n{\n  *result2 = thrust::set_union(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetUnionDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(5);\n  ref[0] = 0; ref[1] = 2; ref[2] = 3; ref[3] = 3; ref[4] = 4;\n\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_union_kernel<<<1,1>>>(exec,\n                            a.begin(), a.end(),\n                            b.begin(), b.end(),\n                            result.begin(),\n                            end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetUnionDeviceSeq()\n{\n  TestSetUnionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceSeq);\n\n\nvoid TestSetUnionDeviceDevice()\n{\n  TestSetUnionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceDevice);\n#endif\n\n\nvoid TestSetUnionCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(5);\n  ref[0] = 0; ref[1] = 2; ref[2] = 3; ref[3] = 3; ref[4] = 4;\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end = thrust::set_union(thrust::cuda::par.on(s),\n                                   a.begin(), a.end(),\n                                   b.begin(), b.end(),\n                                   result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_union.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/reverse.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid reverse_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::reverse(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReverseDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::reverse(h_data.begin(), h_data.end());\n\n  reverse_kernel<<<1,1>>>(exec, raw_pointer_cast(d_data.data()), raw_pointer_cast(d_data.data() + d_data.size()));\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_data, d_data);\n};\n\n\nvoid TestReverseDeviceSeq()\n{\n  TestReverseDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseDeviceSeq);\n\n\nvoid TestReverseDeviceDevice()\n{\n  TestReverseDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid reverse_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::reverse_copy(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReverseCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  thrust::reverse_copy(h_data.begin(), h_data.end(), h_result.begin());\n\n  reverse_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n};\n\n\nvoid TestReverseCopyDeviceSeq()\n{\n  TestReverseCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceSeq);\n\n\nvoid TestReverseCopyDeviceDevice()\n{\n  TestReverseCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceDevice);\n#endif\n\n\nvoid TestReverseCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 3;\n  data[3] = 4;\n  data[4] = 5;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::reverse(thrust::cuda::par.on(s), data.begin(), data.end());\n\n  cudaStreamSynchronize(s);\n\n  Vector ref(5);\n  ref[0] = 5;\n  ref[1] = 4;\n  ref[2] = 3;\n  ref[3] = 2;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(ref, data);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCudaStreams);\n\n\nvoid TestReverseCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 3;\n  data[3] = 4;\n  data[4] = 5;\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::reverse_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin());\n\n  cudaStreamSynchronize(s);\n\n  Vector ref(5);\n  ref[0] = 5;\n  ref[1] = 4;\n  ref[2] = 3;\n  ref[3] = 2;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCopyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/reverse.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid reverse_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::reverse(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReverseDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::reverse(h_data.begin(), h_data.end());\n\n  reverse_kernel<<<1,1>>>(exec, raw_pointer_cast(d_data.data()), raw_pointer_cast(d_data.data() + d_data.size()));\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_data, d_data);\n};\n\n\nvoid TestReverseDeviceSeq()\n{\n  TestReverseDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseDeviceSeq);\n\n\nvoid TestReverseDeviceDevice()\n{\n  TestReverseDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid reverse_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::reverse_copy(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReverseCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  thrust::reverse_copy(h_data.begin(), h_data.end(), h_result.begin());\n\n  reverse_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n};\n\n\nvoid TestReverseCopyDeviceSeq()\n{\n  TestReverseCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceSeq);\n\n\nvoid TestReverseCopyDeviceDevice()\n{\n  TestReverseCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceDevice);\n#endif\n\n\nvoid TestReverseCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 3;\n  data[3] = 4;\n  data[4] = 5;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::reverse(thrust::cuda::par.on(s), data.begin(), data.end());\n\n  hipStreamSynchronize(s);\n\n  Vector ref(5);\n  ref[0] = 5;\n  ref[1] = 4;\n  ref[2] = 3;\n  ref[3] = 2;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(ref, data);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCudaStreams);\n\n\nvoid TestReverseCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 3;\n  data[3] = 4;\n  data[4] = 5;\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::reverse_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin());\n\n  hipStreamSynchronize(s);\n\n  Vector ref(5);\n  ref[0] = 5;\n  ref[1] = 4;\n  ref[2] = 3;\n  ref[3] = 2;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCopyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/reverse.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/uninitialized_fill.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid uninitialized_fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val)\n{\n  thrust::uninitialized_fill(exec, first, last, val);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedFillDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 4, exemplar);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 4);\n  \n  exemplar = 8;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 3, exemplar);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  exemplar = 9;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 2, v.end(), exemplar);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 9);\n  \n  exemplar = 1;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin(), v.end(), exemplar);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n}\n\n\nvoid TestUninitializedFillDeviceSeq()\n{\n  TestUninitializedFillDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceSeq);\n\n\nvoid TestUninitializedFillDeviceDevice()\n{\n  TestUninitializedFillDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceDevice);\n#endif\n\n\nvoid TestUninitializedFillCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::uninitialized_fill(thrust::cuda::par.on(s), v.begin(), v.end(), exemplar);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename T, typename Iterator2>\n__global__\nvoid uninitialized_fill_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, T val, Iterator2 result)\n{\n  *result = thrust::uninitialized_fill_n(exec, first, n, val);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedFillNDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  thrust::device_vector<Vector::iterator> iter_vec(1);\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 1, 3, exemplar, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  Vector::iterator iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 4);\n  ASSERT_EQUAL_QUIET(v.begin() + 4, iter);\n  \n  exemplar = 8;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 0, 3, exemplar, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  ASSERT_EQUAL_QUIET(v.begin() + 3, iter);\n  \n  exemplar = 9;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 2, 3, exemplar, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 9);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n  \n  exemplar = 1;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin(), v.size(), exemplar, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n}\n\n\nvoid TestUninitializedFillNDeviceSeq()\n{\n  TestUninitializedFillNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceSeq);\n\n\nvoid TestUninitializedFillNDeviceDevice()\n{\n  TestUninitializedFillNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceDevice);\n#endif\n\n\nvoid TestUninitializedFillNCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::uninitialized_fill_n(thrust::cuda::par.on(s), v.begin(), v.size(), exemplar);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillNCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/uninitialized_fill.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid uninitialized_fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val)\n{\n  thrust::uninitialized_fill(exec, first, last, val);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedFillDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 4, exemplar);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 4);\n  \n  exemplar = 8;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 3, exemplar);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  exemplar = 9;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin() + 2, v.end(), exemplar);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 9);\n  \n  exemplar = 1;\n  \n  uninitialized_fill_kernel<<<1,1>>>(exec, v.begin(), v.end(), exemplar);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n}\n\n\nvoid TestUninitializedFillDeviceSeq()\n{\n  TestUninitializedFillDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceSeq);\n\n\nvoid TestUninitializedFillDeviceDevice()\n{\n  TestUninitializedFillDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceDevice);\n#endif\n\n\nvoid TestUninitializedFillCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::uninitialized_fill(thrust::cuda::par.on(s), v.begin(), v.end(), exemplar);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename T, typename Iterator2>\n__global__\nvoid uninitialized_fill_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, T val, Iterator2 result)\n{\n  *result = thrust::uninitialized_fill_n(exec, first, n, val);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedFillNDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  thrust::device_vector<Vector::iterator> iter_vec(1);\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 1, 3, exemplar, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  Vector::iterator iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 4);\n  ASSERT_EQUAL_QUIET(v.begin() + 4, iter);\n  \n  exemplar = 8;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 0, 3, exemplar, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  ASSERT_EQUAL_QUIET(v.begin() + 3, iter);\n  \n  exemplar = 9;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin() + 2, 3, exemplar, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], 9);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n  \n  exemplar = 1;\n  \n  uninitialized_fill_n_kernel<<<1,1>>>(exec, v.begin(), v.size(), exemplar, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n}\n\n\nvoid TestUninitializedFillNDeviceSeq()\n{\n  TestUninitializedFillNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceSeq);\n\n\nvoid TestUninitializedFillNDeviceDevice()\n{\n  TestUninitializedFillNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceDevice);\n#endif\n\n\nvoid TestUninitializedFillNCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n  \n  T exemplar(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::uninitialized_fill_n(thrust::cuda::par.on(s), v.begin(), v.size(), exemplar);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], exemplar);\n  ASSERT_EQUAL(v[1], exemplar);\n  ASSERT_EQUAL(v[2], exemplar);\n  ASSERT_EQUAL(v[3], exemplar);\n  ASSERT_EQUAL(v[4], exemplar);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillNCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/uninitialized_fill.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/merge.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy,\n         typename Iterator1,\n         typename Iterator2,\n         typename Iterator3,\n         typename Iterator4,\n         typename Iterator5,\n         typename Iterator6,\n         typename Iterator7>\n__global__\nvoid merge_by_key_kernel(ExecutionPolicy exec,\n                         Iterator1 keys_first1, Iterator1 keys_last1,\n                         Iterator2 keys_first2, Iterator2 keys_last2,\n                         Iterator3 values_first1,\n                         Iterator4 values_first2,\n                         Iterator5 keys_result,\n                         Iterator6 values_result,\n                         Iterator7 result)\n{\n  *result = thrust::merge_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMergeByKeyDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a_key(3), a_val(3), b_key(4), b_val(4);\n\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  thrust::device_vector<int> ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n\n  thrust::device_vector<int> result_key(7), result_val(7);\n\n  typedef typename thrust::device_vector<int>::iterator Iterator;\n\n  thrust::device_vector<thrust::pair<Iterator,Iterator> > result_ends(1);\n\n  merge_by_key_kernel<<<1,1>>>(exec,\n                               a_key.begin(), a_key.end(),\n                               b_key.begin(), b_key.end(),\n                               a_val.begin(), b_val.begin(),\n                               result_key.begin(),\n                               result_val.begin(),\n                               result_ends.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::pair<Iterator,Iterator> ends = result_ends[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestMergeByKeyDeviceSeq()\n{\n  TestMergeByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceSeq);\n\n\nvoid TestMergeByKeyDeviceDevice()\n{\n  TestMergeByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceDevice);\n#endif\n\n\nvoid TestMergeByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), a_val(3), b_key(4), b_val(4);\n\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  Vector ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n\n  Vector result_key(7), result_val(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> ends =\n    thrust::merge_by_key(thrust::cuda::par.on(s),\n                         a_key.begin(), a_key.end(),\n                         b_key.begin(), b_key.end(),\n                         a_val.begin(), b_val.begin(),\n                         result_key.begin(),\n                         result_val.begin());\n\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/merge.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy,\n         typename Iterator1,\n         typename Iterator2,\n         typename Iterator3,\n         typename Iterator4,\n         typename Iterator5,\n         typename Iterator6,\n         typename Iterator7>\n__global__\nvoid merge_by_key_kernel(ExecutionPolicy exec,\n                         Iterator1 keys_first1, Iterator1 keys_last1,\n                         Iterator2 keys_first2, Iterator2 keys_last2,\n                         Iterator3 values_first1,\n                         Iterator4 values_first2,\n                         Iterator5 keys_result,\n                         Iterator6 values_result,\n                         Iterator7 result)\n{\n  *result = thrust::merge_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMergeByKeyDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a_key(3), a_val(3), b_key(4), b_val(4);\n\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  thrust::device_vector<int> ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n\n  thrust::device_vector<int> result_key(7), result_val(7);\n\n  typedef typename thrust::device_vector<int>::iterator Iterator;\n\n  thrust::device_vector<thrust::pair<Iterator,Iterator> > result_ends(1);\n\n  merge_by_key_kernel<<<1,1>>>(exec,\n                               a_key.begin(), a_key.end(),\n                               b_key.begin(), b_key.end(),\n                               a_val.begin(), b_val.begin(),\n                               result_key.begin(),\n                               result_val.begin(),\n                               result_ends.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::pair<Iterator,Iterator> ends = result_ends[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestMergeByKeyDeviceSeq()\n{\n  TestMergeByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceSeq);\n\n\nvoid TestMergeByKeyDeviceDevice()\n{\n  TestMergeByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceDevice);\n#endif\n\n\nvoid TestMergeByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), a_val(3), b_key(4), b_val(4);\n\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  Vector ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n\n  Vector result_key(7), result_val(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> ends =\n    thrust::merge_by_key(thrust::cuda::par.on(s),\n                         a_key.begin(), a_key.end(),\n                         b_key.begin(), b_key.end(),\n                         a_val.begin(), b_val.begin(),\n                         result_key.begin(),\n                         result_val.begin());\n\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/merge_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_union_by_key_kernel(ExecutionPolicy exec,\n                             Iterator1 keys_first1, Iterator1 keys_last1,\n                             Iterator2 keys_first2, Iterator2 keys_last2,\n                             Iterator3 values_first1,\n                             Iterator4 values_first2,\n                             Iterator5 keys_result,\n                             Iterator6 values_result,\n                             Iterator7 result)\n{\n  *result = thrust::set_union_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetUnionByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3), b_val(4);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 0; ref_key[1] = 2; ref_key[2] = 3; ref_key[3] = 3; ref_key[4] = 4;\n  ref_val[0] = 0; ref_val[1] = 0; ref_val[2] = 1; ref_val[3] = 1; ref_val[4] = 0;\n\n  Vector result_key(5), result_val(5);\n\n  thrust::device_vector<thrust::pair<Iterator,Iterator> > end_vec(1);\n\n  set_union_by_key_kernel<<<1,1>>>(exec,\n                                   a_key.begin(), a_key.end(),\n                                   b_key.begin(), b_key.end(),\n                                   a_val.begin(),\n                                   b_val.begin(),\n                                   result_key.begin(),\n                                   result_val.begin(),\n                                   end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::pair<Iterator,Iterator> end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetUnionByKeyDeviceSeq()\n{\n  TestSetUnionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceSeq);\n\n\nvoid TestSetUnionByKeyDeviceDevice()\n{\n  TestSetUnionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetUnionByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3), b_val(4);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 0; ref_key[1] = 2; ref_key[2] = 3; ref_key[3] = 3; ref_key[4] = 4;\n  ref_val[0] = 0; ref_val[1] = 0; ref_val[2] = 1; ref_val[3] = 1; ref_val[4] = 0;\n\n  Vector result_key(5), result_val(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_union_by_key(thrust::cuda::par.on(s),\n                             a_key.begin(), a_key.end(),\n                             b_key.begin(), b_key.end(),\n                             a_val.begin(),\n                             b_val.begin(),\n                             result_key.begin(),\n                             result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6, typename Iterator7>\n__global__\nvoid set_union_by_key_kernel(ExecutionPolicy exec,\n                             Iterator1 keys_first1, Iterator1 keys_last1,\n                             Iterator2 keys_first2, Iterator2 keys_last2,\n                             Iterator3 values_first1,\n                             Iterator4 values_first2,\n                             Iterator5 keys_result,\n                             Iterator6 values_result,\n                             Iterator7 result)\n{\n  *result = thrust::set_union_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetUnionByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3), b_val(4);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 0; ref_key[1] = 2; ref_key[2] = 3; ref_key[3] = 3; ref_key[4] = 4;\n  ref_val[0] = 0; ref_val[1] = 0; ref_val[2] = 1; ref_val[3] = 1; ref_val[4] = 0;\n\n  Vector result_key(5), result_val(5);\n\n  thrust::device_vector<thrust::pair<Iterator,Iterator> > end_vec(1);\n\n  set_union_by_key_kernel<<<1,1>>>(exec,\n                                   a_key.begin(), a_key.end(),\n                                   b_key.begin(), b_key.end(),\n                                   a_val.begin(),\n                                   b_val.begin(),\n                                   result_key.begin(),\n                                   result_val.begin(),\n                                   end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::pair<Iterator,Iterator> end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetUnionByKeyDeviceSeq()\n{\n  TestSetUnionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceSeq);\n\n\nvoid TestSetUnionByKeyDeviceDevice()\n{\n  TestSetUnionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceDevice);\n#endif\n\n\nvoid TestSetUnionByKeyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3), b_val(4);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 1; b_val[1] = 1; b_val[2] = 1; b_val[3] = 1;\n\n  Vector ref_key(5), ref_val(5);\n  ref_key[0] = 0; ref_key[1] = 2; ref_key[2] = 3; ref_key[3] = 3; ref_key[4] = 4;\n  ref_val[0] = 0; ref_val[1] = 0; ref_val[2] = 1; ref_val[3] = 1; ref_val[4] = 0;\n\n  Vector result_key(5), result_val(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_union_by_key(thrust::cuda::par.on(s),\n                             a_key.begin(), a_key.end(),\n                             b_key.begin(), b_key.end(),\n                             a_val.begin(),\n                             b_val.begin(),\n                             result_key.begin(),\n                             result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_union_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMinElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> d_result(1);\n  \n  typename thrust::host_vector<int>::iterator   h_min = thrust::min_element(h_data.begin(), h_data.end());\n\n  min_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator   h_max = thrust::min_element(h_data.begin(), h_data.end(), thrust::greater<int>());\n\n  min_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n}\n\n\nvoid TestMinElementDeviceSeq()\n{\n  TestMinElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinElementDeviceSeq);\n\n\nvoid TestMinElementDeviceDevice()\n{\n  TestMinElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinElementDeviceDevice);\n#endif\n\n\nvoid TestMinElementCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL( *thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()), 1);\n  ASSERT_EQUAL( thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()) - data.begin(), 2);\n  \n  ASSERT_EQUAL( *thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::greater<T>()), 5);\n  ASSERT_EQUAL( thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::greater<T>()) - data.begin(), 1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinElementCudaStreams);\n\nvoid TestMinElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::min_element(thrust::device, raw_ptr, raw_ptr+n) - raw_ptr, 2);\n  ASSERT_EQUAL( thrust::min_element(thrust::device, raw_ptr, raw_ptr+n, thrust::greater<T>()) - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinElementDevicePointer);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/extrema.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__\nvoid min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestMinElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typedef typename thrust::device_vector<int>::iterator iter_type;\n\n  thrust::device_vector<iter_type> d_result(1);\n  \n  typename thrust::host_vector<int>::iterator   h_min = thrust::min_element(h_data.begin(), h_data.end());\n\n  min_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator   h_max = thrust::min_element(h_data.begin(), h_data.end(), thrust::greater<int>());\n\n  min_element_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), thrust::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n}\n\n\nvoid TestMinElementDeviceSeq()\n{\n  TestMinElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinElementDeviceSeq);\n\n\nvoid TestMinElementDeviceDevice()\n{\n  TestMinElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinElementDeviceDevice);\n#endif\n\n\nvoid TestMinElementCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL( *thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()), 1);\n  ASSERT_EQUAL( thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()) - data.begin(), 2);\n  \n  ASSERT_EQUAL( *thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::greater<T>()), 5);\n  ASSERT_EQUAL( thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::greater<T>()) - data.begin(), 1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinElementCudaStreams);\n\nvoid TestMinElementDevicePointer()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n = data.size();\n  ASSERT_EQUAL( thrust::min_element(thrust::device, raw_ptr, raw_ptr+n) - raw_ptr, 2);\n  ASSERT_EQUAL( thrust::min_element(thrust::device, raw_ptr, raw_ptr+n, thrust::greater<T>()) - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinElementDevicePointer);\n", "source": "/kaggle/working/thrust/testing/cuda/min_element.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/extrema.h>\n#include <thrust/iterator/discard_iterator.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_intersection_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1,\n                             Iterator2 first2, Iterator2 last2,\n                             Iterator3 result1,\n                             Iterator4 result2)\n{\n  *result2 = thrust::set_intersection(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(2);\n  ref[0] = 0; ref[1] = 4;\n\n  Vector result(2);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_intersection_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetIntersectionDeviceSeq()\n{\n  TestSetIntersectionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceSeq);\n\n\nvoid TestSetIntersectionDeviceDevice()\n{\n  TestSetIntersectionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceDevice);\n\n\nvoid TestSetIntersectionDeviceNoSync()\n{\n  TestSetIntersectionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(2);\n  ref[0] = 0; ref[1] = 4;\n\n  Vector result(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator end = thrust::set_intersection(streampolicy,\n                                          a.begin(), a.end(),\n                                          b.begin(), b.end(),\n                                          result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestSetIntersectionCudaStreamsSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsSync);\n\n\nvoid TestSetIntersectionCudaStreamsNoSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n#include <thrust/extrema.h>\n#include <thrust/iterator/discard_iterator.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_intersection_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1,\n                             Iterator2 first2, Iterator2 last2,\n                             Iterator3 result1,\n                             Iterator4 result2)\n{\n  *result2 = thrust::set_intersection(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(2);\n  ref[0] = 0; ref[1] = 4;\n\n  Vector result(2);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_intersection_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetIntersectionDeviceSeq()\n{\n  TestSetIntersectionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceSeq);\n\n\nvoid TestSetIntersectionDeviceDevice()\n{\n  TestSetIntersectionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceDevice);\n\n\nvoid TestSetIntersectionDeviceNoSync()\n{\n  TestSetIntersectionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(3), b(4);\n\n  a[0] = 0; a[1] = 2; a[2] = 4;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4;\n\n  Vector ref(2);\n  ref[0] = 0; ref[1] = 4;\n\n  Vector result(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator end = thrust::set_intersection(streampolicy,\n                                          a.begin(), a.end(),\n                                          b.begin(), b.end(),\n                                          result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestSetIntersectionCudaStreamsSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsSync);\n\n\nvoid TestSetIntersectionCudaStreamsNoSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_intersection.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6>\n__global__\nvoid set_intersection_by_key_kernel(ExecutionPolicy exec,\n                                    Iterator1 keys_first1, Iterator1 keys_last1,\n                                    Iterator2 keys_first2, Iterator2 keys_last2,\n                                    Iterator3 values_first1,\n                                    Iterator4 keys_result,\n                                    Iterator5 values_result,\n                                    Iterator6 result)\n{\n  *result = thrust::set_intersection_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 0; ref_key[1] = 4;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_intersection_by_key_kernel<<<1,1>>>(exec,\n                                          a_key.begin(), a_key.end(),\n                                          b_key.begin(), b_key.end(),\n                                          a_val.begin(),\n                                          result_key.begin(),\n                                          result_val.begin(),\n                                          end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::pair<Iterator,Iterator> end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetIntersectionByKeyDeviceSeq()\n{\n  TestSetIntersectionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceSeq);\n\n\nvoid TestSetIntersectionByKeyDeviceDevice()\n{\n  TestSetIntersectionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceDevice);\n\n\nvoid TestSetIntersectionByKeyDeviceNoSync()\n{\n  TestSetIntersectionByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 0; ref_key[1] = 4;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_intersection_by_key(streampolicy,\n                                    a_key.begin(), a_key.end(),\n                                    b_key.begin(), b_key.end(),\n                                    a_val.begin(),\n                                    result_key.begin(),\n                                    result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestSetIntersectionByKeyCudaStreamsSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsSync);\n\n\nvoid TestSetIntersectionByKeyCudaStreamsNoSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Iterator5, typename Iterator6>\n__global__\nvoid set_intersection_by_key_kernel(ExecutionPolicy exec,\n                                    Iterator1 keys_first1, Iterator1 keys_last1,\n                                    Iterator2 keys_first2, Iterator2 keys_last2,\n                                    Iterator3 values_first1,\n                                    Iterator4 keys_result,\n                                    Iterator5 values_result,\n                                    Iterator6 result)\n{\n  *result = thrust::set_intersection_by_key(exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, keys_result, values_result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 0; ref_key[1] = 4;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  typedef thrust::pair<Iterator,Iterator> iter_pair;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_intersection_by_key_kernel<<<1,1>>>(exec,\n                                          a_key.begin(), a_key.end(),\n                                          b_key.begin(), b_key.end(),\n                                          a_val.begin(),\n                                          result_key.begin(),\n                                          result_val.begin(),\n                                          end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::pair<Iterator,Iterator> end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\n\nvoid TestSetIntersectionByKeyDeviceSeq()\n{\n  TestSetIntersectionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceSeq);\n\n\nvoid TestSetIntersectionByKeyDeviceDevice()\n{\n  TestSetIntersectionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceDevice);\n\n\nvoid TestSetIntersectionByKeyDeviceNoSync()\n{\n  TestSetIntersectionByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a_key(3), b_key(4);\n  Vector a_val(3);\n\n  a_key[0] = 0; a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 0; a_val[1] = 0; a_val[2] = 0;\n\n  b_key[0] = 0; b_key[1] = 3; b_key[2] = 3; b_key[3] = 4;\n\n  Vector ref_key(2), ref_val(2);\n  ref_key[0] = 0; ref_key[1] = 4;\n  ref_val[0] = 0; ref_val[1] = 0;\n\n  Vector result_key(2), result_val(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  thrust::pair<Iterator,Iterator> end =\n    thrust::set_intersection_by_key(streampolicy,\n                                    a_key.begin(), a_key.end(),\n                                    b_key.begin(), b_key.end(),\n                                    a_val.begin(),\n                                    result_key.begin(),\n                                    result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestSetIntersectionByKeyCudaStreamsSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsSync);\n\n\nvoid TestSetIntersectionByKeyCudaStreamsNoSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_intersection_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/reduce.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/constant_iterator.h>\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid reduce_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, Iterator2 result)\n{\n  *result = thrust::reduce(exec, first, last, init);\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestReduceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  thrust::device_vector<T> d_result(1);\n  \n  T init = 13;\n  \n  T h_result = thrust::reduce(h_data.begin(), h_data.end(), init);\n  \n  reduce_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), init, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nstruct TestReduceDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceSeq, IntegralTypes> TestReduceDeviceSeqInstance;\n\n\ntemplate<typename T>\nstruct TestReduceDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceDevice, IntegralTypes> TestReduceDeviceDeviceInstance;\n\n\ntemplate<typename T>\nstruct TestReduceDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceNoSync, IntegralTypes> TestReduceDeviceNoSyncInstance;\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReduceCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v(3);\n  v[0] = 1; v[1] = -2; v[2] = 3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end()), 2);\n\n  // with initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end(), 10), 12);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestReduceCudaStreamsSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsSync);\n\n\nvoid TestReduceCudaStreamsNoSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceLargeInput()\n{\n  using T = unsigned long long;\n  using OffsetT = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  thrust::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_kernel<<<1,1>>>(thrust::device, d_data, d_data + num_items, T{}, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceLargeInput);\n#endif\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/reduce.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/constant_iterator.h>\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__\nvoid reduce_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, Iterator2 result)\n{\n  *result = thrust::reduce(exec, first, last, init);\n}\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestReduceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  thrust::device_vector<T> d_result(1);\n  \n  T init = 13;\n  \n  T h_result = thrust::reduce(h_data.begin(), h_data.end(), init);\n  \n  reduce_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), init, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\n\ntemplate<typename T>\nstruct TestReduceDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceSeq, IntegralTypes> TestReduceDeviceSeqInstance;\n\n\ntemplate<typename T>\nstruct TestReduceDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceDevice, IntegralTypes> TestReduceDeviceDeviceInstance;\n\n\ntemplate<typename T>\nstruct TestReduceDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceNoSync, IntegralTypes> TestReduceDeviceNoSyncInstance;\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReduceCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector v(3);\n  v[0] = 1; v[1] = -2; v[2] = 3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end()), 2);\n\n  // with initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end(), 10), 12);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestReduceCudaStreamsSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsSync);\n\n\nvoid TestReduceCudaStreamsNoSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceLargeInput()\n{\n  using T = unsigned long long;\n  using OffsetT = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  thrust::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_kernel<<<1,1>>>(thrust::device, d_data, d_data + num_items, T{}, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceLargeInput);\n#endif\n\n", "source": "/kaggle/working/thrust/testing/cuda/reduce.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/generate.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct return_value\n{\n  T val;\n  \n  return_value(void){}\n  return_value(T v):val(v){}\n  \n  __host__ __device__\n  T operator()(void){ return val; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__\nvoid generate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::generate(exec, first, last, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGenerateDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n  \n  T value = 13;\n  return_value<T> f(value);\n  \n  thrust::generate(h_result.begin(), h_result.end(), f);\n\n  generate_kernel<<<1,1>>>(exec, d_result.begin(), d_result.end(), f);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\ntemplate<typename T>\nvoid TestGenerateDeviceSeq(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestGenerateDeviceDevice(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceDevice);\n#endif\n\n\nvoid TestGenerateCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n  \n  int value = 13;\n  \n  return_value<int> f(value);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::generate(thrust::cuda::par.on(s), result.begin(), result.end(), f);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__\nvoid generate_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::generate_n(exec, first, n, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGenerateNDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n  \n  T value = 13;\n  return_value<T> f(value);\n  \n  thrust::generate_n(h_result.begin(), h_result.size(), f);\n\n  generate_n_kernel<<<1,1>>>(exec, d_result.begin(), d_result.size(), f);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\ntemplate<typename T>\nvoid TestGenerateNDeviceSeq(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestGenerateNDeviceDevice(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceDevice);\n#endif\n\n\nvoid TestGenerateNCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n  \n  int value = 13;\n  \n  return_value<int> f(value);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::generate_n(thrust::cuda::par.on(s), result.begin(), result.size(), f);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateNCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/generate.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct return_value\n{\n  T val;\n  \n  return_value(void){}\n  return_value(T v):val(v){}\n  \n  __host__ __device__\n  T operator()(void){ return val; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__\nvoid generate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::generate(exec, first, last, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGenerateDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n  \n  T value = 13;\n  return_value<T> f(value);\n  \n  thrust::generate(h_result.begin(), h_result.end(), f);\n\n  generate_kernel<<<1,1>>>(exec, d_result.begin(), d_result.end(), f);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\ntemplate<typename T>\nvoid TestGenerateDeviceSeq(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestGenerateDeviceDevice(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceDevice);\n#endif\n\n\nvoid TestGenerateCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n  \n  int value = 13;\n  \n  return_value<int> f(value);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::generate(thrust::cuda::par.on(s), result.begin(), result.end(), f);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__\nvoid generate_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::generate_n(exec, first, n, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGenerateNDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n  \n  T value = 13;\n  return_value<T> f(value);\n  \n  thrust::generate_n(h_result.begin(), h_result.size(), f);\n\n  generate_n_kernel<<<1,1>>>(exec, d_result.begin(), d_result.size(), f);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_result, d_result);\n}\n\n\ntemplate<typename T>\nvoid TestGenerateNDeviceSeq(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestGenerateNDeviceDevice(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceDevice);\n#endif\n\n\nvoid TestGenerateNCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n  \n  int value = 13;\n  \n  return_value<int> f(value);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::generate_n(thrust::cuda::par.on(s), result.begin(), result.size(), f);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateNCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/generate.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/uninitialized_copy.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid uninitialized_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::uninitialized_copy(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedCopyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_kernel<<<1,1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\n\nvoid TestUninitializedCopyDeviceSeq()\n{\n  TestUninitializedCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceSeq);\n\n\nvoid TestUninitializedCopyDeviceDevice()\n{\n  TestUninitializedCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceDevice);\n#endif\n\n\nvoid TestUninitializedCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_copy(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__\nvoid uninitialized_copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::uninitialized_copy_n(exec, first, n, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedCopyNDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_n_kernel<<<1,1>>>(exec, v1.begin(), v1.size(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\n\nvoid TestUninitializedCopyNDeviceSeq()\n{\n  TestUninitializedCopyNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceSeq);\n\n\nvoid TestUninitializedCopyNDeviceDevice()\n{\n  TestUninitializedCopyNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceDevice);\n#endif\n\n\nvoid TestUninitializedCopyNCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_copy_n(thrust::cuda::par.on(s), v1.begin(), v1.size(), v2.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/uninitialized_copy.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid uninitialized_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::uninitialized_copy(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedCopyDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_kernel<<<1,1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\n\nvoid TestUninitializedCopyDeviceSeq()\n{\n  TestUninitializedCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceSeq);\n\n\nvoid TestUninitializedCopyDeviceDevice()\n{\n  TestUninitializedCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceDevice);\n#endif\n\n\nvoid TestUninitializedCopyCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_copy(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__\nvoid uninitialized_copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::uninitialized_copy_n(exec, first, n, result);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestUninitializedCopyNDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_n_kernel<<<1,1>>>(exec, v1.begin(), v1.size(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n}\n\n\nvoid TestUninitializedCopyNDeviceSeq()\n{\n  TestUninitializedCopyNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceSeq);\n\n\nvoid TestUninitializedCopyNDeviceDevice()\n{\n  TestUninitializedCopyNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceDevice);\n#endif\n\n\nvoid TestUninitializedCopyNCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v1(5);\n  v1[0] = 0; v1[1] = 1; v1[2] = 2; v1[3] = 3; v1[4] = 4;\n  \n  // copy to Vector\n  Vector v2(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_copy_n(thrust::cuda::par.on(s), v1.begin(), v1.size(), v2.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2[0], 0);\n  ASSERT_EQUAL(v2[1], 1);\n  ASSERT_EQUAL(v2[2], 2);\n  ASSERT_EQUAL(v2[3], 3);\n  ASSERT_EQUAL(v2[4], 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/uninitialized_copy.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/pair.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid stable_sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first)\n{\n  thrust::stable_sort_by_key(exec, keys_first, keys_last, values_first);\n}\n\n\nstruct make_pair_functor\n{\n  template<typename T1, typename T2>\n  __host__ __device__\n    thrust::pair<T1,T2> operator()(const T1 &x, const T2 &y)\n  {\n    return thrust::make_pair(x,y);\n  } // end operator()()\n}; // end make_pair_functor\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPairStableSortByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  typedef thrust::pair<int,int> P;\n\n  // host arrays\n  thrust::host_vector<int>   h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int>   h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P>   h_pairs(n);\n\n  thrust::host_vector<int> h_values(n);\n  thrust::sequence(h_values.begin(), h_values.end());\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  // device arrays\n  thrust::device_vector<P>   d_pairs = h_pairs;\n  thrust::device_vector<int> d_values = h_values;\n\n  // sort on the device\n  stable_sort_by_key_kernel<<<1,1>>>(exec, d_pairs.begin(), d_pairs.end(), d_values.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort_by_key(h_pairs.begin(), h_pairs.end(), h_values.begin());\n\n  ASSERT_EQUAL_QUIET(h_pairs,  d_pairs);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\n\nvoid TestPairStableSortByKeyDeviceSeq()\n{\n  TestPairStableSortByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceSeq);\n\n\nvoid TestPairStableSortByKeyDeviceDevice()\n{\n  TestPairStableSortByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceDevice);\n#endif\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/pair.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__\nvoid stable_sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first)\n{\n  thrust::stable_sort_by_key(exec, keys_first, keys_last, values_first);\n}\n\n\nstruct make_pair_functor\n{\n  template<typename T1, typename T2>\n  __host__ __device__\n    thrust::pair<T1,T2> operator()(const T1 &x, const T2 &y)\n  {\n    return thrust::make_pair(x,y);\n  } // end operator()()\n}; // end make_pair_functor\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPairStableSortByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  typedef thrust::pair<int,int> P;\n\n  // host arrays\n  thrust::host_vector<int>   h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int>   h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P>   h_pairs(n);\n\n  thrust::host_vector<int> h_values(n);\n  thrust::sequence(h_values.begin(), h_values.end());\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  // device arrays\n  thrust::device_vector<P>   d_pairs = h_pairs;\n  thrust::device_vector<int> d_values = h_values;\n\n  // sort on the device\n  stable_sort_by_key_kernel<<<1,1>>>(exec, d_pairs.begin(), d_pairs.end(), d_values.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort_by_key(h_pairs.begin(), h_pairs.end(), h_values.begin());\n\n  ASSERT_EQUAL_QUIET(h_pairs,  d_pairs);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\n\nvoid TestPairStableSortByKeyDeviceSeq()\n{\n  TestPairStableSortByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceSeq);\n\n\nvoid TestPairStableSortByKeyDeviceDevice()\n{\n  TestPairStableSortByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceDevice);\n#endif\n\n", "source": "/kaggle/working/thrust/testing/cuda/pair_sort_by_key.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Function1, typename T, typename Function2, typename Iterator2>\n__global__\nvoid transform_reduce_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Function1 f1, T init, Function2 f2, Iterator2 result)\n{\n  *result = thrust::transform_reduce(exec, first, last, f1, init, f2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformReduceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  Vector data(3);\n  data[0] = 1; data[1] = -2; data[2] = 3;\n  \n  T init = 10;\n\n  thrust::device_vector<T> result(1);\n\n  transform_reduce_kernel<<<1,1>>>(exec, data.begin(), data.end(), thrust::negate<T>(), init, thrust::plus<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(8, (T)result[0]);\n}\n\n\nvoid TestTransformReduceDeviceSeq()\n{\n  TestTransformReduceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceSeq);\n\n\nvoid TestTransformReduceDeviceDevice()\n{\n  TestTransformReduceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceDevice);\n#endif\n\n\nvoid TestTransformReduceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector data(3);\n  data[0] = 1; data[1] = -2; data[2] = 3;\n  \n  T init = 10;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  T result = thrust::transform_reduce(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::negate<T>(), init, thrust::plus<T>());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(8, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformReduceCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/transform_reduce.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Function1, typename T, typename Function2, typename Iterator2>\n__global__\nvoid transform_reduce_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Function1 f1, T init, Function2 f2, Iterator2 result)\n{\n  *result = thrust::transform_reduce(exec, first, last, f1, init, f2);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTransformReduceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::value_type T;\n  \n  Vector data(3);\n  data[0] = 1; data[1] = -2; data[2] = 3;\n  \n  T init = 10;\n\n  thrust::device_vector<T> result(1);\n\n  transform_reduce_kernel<<<1,1>>>(exec, data.begin(), data.end(), thrust::negate<T>(), init, thrust::plus<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(8, (T)result[0]);\n}\n\n\nvoid TestTransformReduceDeviceSeq()\n{\n  TestTransformReduceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceSeq);\n\n\nvoid TestTransformReduceDeviceDevice()\n{\n  TestTransformReduceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceDevice);\n#endif\n\n\nvoid TestTransformReduceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector data(3);\n  data[0] = 1; data[1] = -2; data[2] = 3;\n  \n  T init = 10;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  T result = thrust::transform_reduce(thrust::cuda::par.on(s), data.begin(), data.end(), thrust::negate<T>(), init, thrust::plus<T>());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(8, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformReduceCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/transform_reduce.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_difference_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, Iterator3 result1, Iterator4 result2)\n{\n  *result2 = thrust::set_difference(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetDifferenceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 5;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 6;\n\n  Vector ref(2);\n  ref[0] = 2; ref[1] = 5;\n\n  Vector result(2);\n\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_difference_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetDifferenceDeviceSeq()\n{\n  TestSetDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceSeq);\n\n\nvoid TestSetDifferenceDeviceDevice()\n{\n  TestSetDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceDevice);\n#endif\n\n\nvoid TestSetDifferenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 5;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 6;\n\n  Vector ref(2);\n  ref[0] = 2; ref[1] = 5;\n\n  Vector result(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end = thrust::set_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/set_operations.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__\nvoid set_difference_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, Iterator3 result1, Iterator4 result2)\n{\n  *result2 = thrust::set_difference(exec, first1, last1, first2, last2, result1);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSetDifferenceDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef typename Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 5;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 6;\n\n  Vector ref(2);\n  ref[0] = 2; ref[1] = 5;\n\n  Vector result(2);\n\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_difference_kernel<<<1,1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\n\nvoid TestSetDifferenceDeviceSeq()\n{\n  TestSetDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceSeq);\n\n\nvoid TestSetDifferenceDeviceDevice()\n{\n  TestSetDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceDevice);\n#endif\n\n\nvoid TestSetDifferenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::iterator Iterator;\n\n  Vector a(4), b(5);\n\n  a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 5;\n  b[0] = 0; b[1] = 3; b[2] = 3; b[3] = 4; b[4] = 6;\n\n  Vector ref(2);\n  ref[0] = 2; ref[1] = 5;\n\n  Vector result(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end = thrust::set_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/set_difference.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/execution_policy.h>\n#include <thrust/device_malloc.h>\n#include <thrust/device_free.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::adjacent_difference(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryFunction>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, BinaryFunction f)\n{\n  thrust::adjacent_difference(exec, first, last, result, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestAdjacentDifferenceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_input = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin(), thrust::plus<T>());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), thrust::plus<T>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  // in-place operation\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_input.begin(), thrust::plus<T>());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_input.begin(), thrust::plus<T>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_input, h_output); //computed previously\n  ASSERT_EQUAL(d_input, d_output); //computed previously\n}\n\n\ntemplate<typename T>\nvoid TestAdjacentDifferenceDeviceSeq(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestAdjacentDifferenceDeviceDevice(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceDevice);\n#endif\n\n\nvoid TestAdjacentDifferenceCudaStreams()\n{\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::device_vector<int> input(3);\n  thrust::device_vector<int> output(3);\n  input[0] = 1; input[1] = 4; input[2] = 6;\n\n  thrust::adjacent_difference(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 1);\n  ASSERT_EQUAL(output[1], 3);\n  ASSERT_EQUAL(output[2], 2);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceCudaStreams);\n\nstruct detect_wrong_difference\n{\n    using difference_type = void;\n    using value_type = void;\n    using pointer = void;\n    using reference = void;\n    using iterator_category = std::output_iterator_tag;\n\n    bool * flag;\n\n    __host__ __device__ detect_wrong_difference operator++() const { return *this; }\n    __host__ __device__ detect_wrong_difference operator*() const { return *this; }\n    template<typename Difference>\n    __host__ __device__ detect_wrong_difference operator+(Difference) const { return *this; }\n    template<typename Index>\n    __host__ __device__ detect_wrong_difference operator[](Index) const { return *this; }\n\n    __device__\n    void operator=(long long difference) const\n    {\n        if (difference != 1)\n        {\n            *flag = false;\n        }\n    }\n};\n\nvoid TestAdjacentDifferenceWithBigIndexesHelper(int magnitude)\n{\n    thrust::counting_iterator<long long> begin(1);\n    thrust::counting_iterator<long long> end = begin + (1ll << magnitude);\n    ASSERT_EQUAL(thrust::distance(begin, end), 1ll << magnitude);\n\n    thrust::device_ptr<bool> all_differences_correct = thrust::device_malloc<bool>(1);\n    *all_differences_correct = true;\n\n    detect_wrong_difference out = { thrust::raw_pointer_cast(all_differences_correct) };\n\n    thrust::adjacent_difference(thrust::device, begin, end, out);\n\n    bool all_differences_correct_h = *all_differences_correct;\n    thrust::device_free(all_differences_correct);\n\n    ASSERT_EQUAL(all_differences_correct_h, true);\n}\n\nvoid TestAdjacentDifferenceWithBigIndexes()\n{\n    TestAdjacentDifferenceWithBigIndexesHelper(30);\n    TestAdjacentDifferenceWithBigIndexesHelper(31);\n    TestAdjacentDifferenceWithBigIndexesHelper(32);\n    TestAdjacentDifferenceWithBigIndexesHelper(33);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceWithBigIndexes);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/adjacent_difference.h>\n#include <thrust/execution_policy.h>\n#include <thrust/device_malloc.h>\n#include <thrust/device_free.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::adjacent_difference(exec, first, last, result);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryFunction>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, BinaryFunction f)\n{\n  thrust::adjacent_difference(exec, first, last, result, f);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestAdjacentDifferenceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_input = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin(), thrust::plus<T>());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), thrust::plus<T>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  // in-place operation\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_input.begin(), thrust::plus<T>());\n  adjacent_difference_kernel<<<1,1>>>(exec, d_input.begin(), d_input.end(), d_input.begin(), thrust::plus<T>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_input, h_output); //computed previously\n  ASSERT_EQUAL(d_input, d_output); //computed previously\n}\n\n\ntemplate<typename T>\nvoid TestAdjacentDifferenceDeviceSeq(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestAdjacentDifferenceDeviceDevice(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceDevice);\n#endif\n\n\nvoid TestAdjacentDifferenceCudaStreams()\n{\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::device_vector<int> input(3);\n  thrust::device_vector<int> output(3);\n  input[0] = 1; input[1] = 4; input[2] = 6;\n\n  thrust::adjacent_difference(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(output[0], 1);\n  ASSERT_EQUAL(output[1], 3);\n  ASSERT_EQUAL(output[2], 2);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceCudaStreams);\n\nstruct detect_wrong_difference\n{\n    using difference_type = void;\n    using value_type = void;\n    using pointer = void;\n    using reference = void;\n    using iterator_category = std::output_iterator_tag;\n\n    bool * flag;\n\n    __host__ __device__ detect_wrong_difference operator++() const { return *this; }\n    __host__ __device__ detect_wrong_difference operator*() const { return *this; }\n    template<typename Difference>\n    __host__ __device__ detect_wrong_difference operator+(Difference) const { return *this; }\n    template<typename Index>\n    __host__ __device__ detect_wrong_difference operator[](Index) const { return *this; }\n\n    __device__\n    void operator=(long long difference) const\n    {\n        if (difference != 1)\n        {\n            *flag = false;\n        }\n    }\n};\n\nvoid TestAdjacentDifferenceWithBigIndexesHelper(int magnitude)\n{\n    thrust::counting_iterator<long long> begin(1);\n    thrust::counting_iterator<long long> end = begin + (1ll << magnitude);\n    ASSERT_EQUAL(thrust::distance(begin, end), 1ll << magnitude);\n\n    thrust::device_ptr<bool> all_differences_correct = thrust::device_malloc<bool>(1);\n    *all_differences_correct = true;\n\n    detect_wrong_difference out = { thrust::raw_pointer_cast(all_differences_correct) };\n\n    thrust::adjacent_difference(thrust::device, begin, end, out);\n\n    bool all_differences_correct_h = *all_differences_correct;\n    thrust::device_free(all_differences_correct);\n\n    ASSERT_EQUAL(all_differences_correct_h, true);\n}\n\nvoid TestAdjacentDifferenceWithBigIndexes()\n{\n    TestAdjacentDifferenceWithBigIndexesHelper(30);\n    TestAdjacentDifferenceWithBigIndexesHelper(31);\n    TestAdjacentDifferenceWithBigIndexesHelper(32);\n    TestAdjacentDifferenceWithBigIndexesHelper(33);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceWithBigIndexes);\n", "source": "/kaggle/working/thrust/testing/cuda/adjacent_difference.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/replace.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate <typename T>\nstruct less_than_five\n{\n  __host__ __device__ bool operator()(const T &val) const {return val < 5;}\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T1, typename T2>\n__global__\nvoid replace_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T1 old_value, T2 new_value)\n{\n  thrust::replace(exec, first, last, old_value, new_value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestReplaceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  T old_value = 0;\n  T new_value = 1;\n  \n  thrust::replace(h_data.begin(), h_data.end(), old_value, new_value);\n\n  replace_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), old_value, new_value);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\n\ntemplate<typename T>\nvoid TestReplaceDeviceSeq(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceSeq);\n\ntemplate<typename T>\nvoid TestReplaceDeviceDevice(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T1, typename T2>\n__global__\nvoid replace_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T1 old_value, T2 new_value)\n{\n  thrust::replace_copy(exec, first, last, result, old_value, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  int old_value = 0;\n  int new_value = 1;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy(h_data.begin(), h_data.end(), h_dest.begin(), old_value, new_value);\n\n  replace_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), old_value, new_value);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyDeviceSeq()\n{\n  TestReplaceCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceSeq);\n\nvoid TestReplaceCopyDeviceDevice()\n{\n  TestReplaceCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename T>\n__global__\nvoid replace_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::replace_if(h_data.begin(), h_data.end(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfDeviceSeq()\n{\n  TestReplaceIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceSeq);\n\nvoid TestReplaceIfDeviceDevice()\n{\n  TestReplaceIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__\nvoid replace_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, stencil_first, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_stencil = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n  \n  thrust::replace_if(h_data.begin(), h_data.end(), h_stencil.begin(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfStencilDeviceSeq()\n{\n  TestReplaceIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceSeq);\n\nvoid TestReplaceIfStencilDeviceDevice()\n{\n  TestReplaceIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__\nvoid replace_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, result, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfDeviceSeq()\n{\n  TestReplaceCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceSeq);\n\nvoid TestReplaceCopyIfDeviceDevice()\n{\n  TestReplaceCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename T>\n__global__\nvoid replace_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, stencil_first, result, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_stencil = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_dest.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\n\nvoid TestReplaceCopyIfStencilDeviceSeq()\n{\n  TestReplaceCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceSeq);\n\n\nvoid TestReplaceCopyIfStencilDeviceDevice()\n{\n  TestReplaceCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceDevice);\n#endif\n\n\nvoid TestReplaceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 1, (T) 4);\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2, (T) 5);\n\n  cudaStreamSynchronize(s);\n\n  Vector result(5);\n  result[0] =  4; \n  result[1] =  5; \n  result[2] =  4;\n  result[3] =  3; \n  result[4] =  5; \n\n  ASSERT_EQUAL(data, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReplaceCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/replace.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate <typename T>\nstruct less_than_five\n{\n  __host__ __device__ bool operator()(const T &val) const {return val < 5;}\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T1, typename T2>\n__global__\nvoid replace_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T1 old_value, T2 new_value)\n{\n  thrust::replace(exec, first, last, old_value, new_value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestReplaceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  T old_value = 0;\n  T new_value = 1;\n  \n  thrust::replace(h_data.begin(), h_data.end(), old_value, new_value);\n\n  replace_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), old_value, new_value);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\n\ntemplate<typename T>\nvoid TestReplaceDeviceSeq(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceSeq);\n\ntemplate<typename T>\nvoid TestReplaceDeviceDevice(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T1, typename T2>\n__global__\nvoid replace_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T1 old_value, T2 new_value)\n{\n  thrust::replace_copy(exec, first, last, result, old_value, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  int old_value = 0;\n  int new_value = 1;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy(h_data.begin(), h_data.end(), h_dest.begin(), old_value, new_value);\n\n  replace_copy_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), old_value, new_value);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyDeviceSeq()\n{\n  TestReplaceCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceSeq);\n\nvoid TestReplaceCopyDeviceDevice()\n{\n  TestReplaceCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename T>\n__global__\nvoid replace_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::replace_if(h_data.begin(), h_data.end(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfDeviceSeq()\n{\n  TestReplaceIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceSeq);\n\nvoid TestReplaceIfDeviceDevice()\n{\n  TestReplaceIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__\nvoid replace_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, stencil_first, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_stencil = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n  \n  thrust::replace_if(h_data.begin(), h_data.end(), h_stencil.begin(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfStencilDeviceSeq()\n{\n  TestReplaceIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceSeq);\n\nvoid TestReplaceIfStencilDeviceDevice()\n{\n  TestReplaceIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__\nvoid replace_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, result, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfDeviceSeq()\n{\n  TestReplaceCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceSeq);\n\nvoid TestReplaceCopyIfDeviceDevice()\n{\n  TestReplaceCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename T>\n__global__\nvoid replace_copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, stencil_first, result, pred, new_value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestReplaceCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  thrust::host_vector<int>   h_stencil = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n  \n  thrust::host_vector<int>   h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n  \n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_dest.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\n\nvoid TestReplaceCopyIfStencilDeviceSeq()\n{\n  TestReplaceCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceSeq);\n\n\nvoid TestReplaceCopyIfStencilDeviceDevice()\n{\n  TestReplaceCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceDevice);\n#endif\n\n\nvoid TestReplaceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 1, (T) 4);\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2, (T) 5);\n\n  hipStreamSynchronize(s);\n\n  Vector result(5);\n  result[0] =  4; \n  result[1] =  5; \n  result[2] =  4;\n  result[3] =  3; \n  result[4] =  5; \n\n  ASSERT_EQUAL(data, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReplaceCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/replace.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/tabulate.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__\nvoid tabulate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::tabulate(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTabulateDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  using namespace thrust::placeholders;\n  typedef typename Vector::value_type T;\n  \n  Vector v(5);\n\n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), -_1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0],  0);\n  ASSERT_EQUAL(v[1], -1);\n  ASSERT_EQUAL(v[2], -2);\n  ASSERT_EQUAL(v[3], -3);\n  ASSERT_EQUAL(v[4], -4);\n  \n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), _1 * _1 * _1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 27);\n  ASSERT_EQUAL(v[4], 64);\n}\n\nvoid TestTabulateDeviceSeq()\n{\n  TestTabulateDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTabulateDeviceSeq);\n\nvoid TestTabulateDeviceDevice()\n{\n  TestTabulateDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTabulateDeviceDevice);\n#endif\n\nvoid TestTabulateCudaStreams()\n{\n  using namespace thrust::placeholders;\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), -_1);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0],  0);\n  ASSERT_EQUAL(v[1], -1);\n  ASSERT_EQUAL(v[2], -2);\n  ASSERT_EQUAL(v[3], -3);\n  ASSERT_EQUAL(v[4], -4);\n  \n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), _1 * _1 * _1);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 27);\n  ASSERT_EQUAL(v[4], 64);\n\n  cudaStreamSynchronize(s);\n}\nDECLARE_UNITTEST(TestTabulateCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/tabulate.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__\nvoid tabulate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::tabulate(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestTabulateDevice(ExecutionPolicy exec)\n{\n  typedef thrust::device_vector<int> Vector;\n  using namespace thrust::placeholders;\n  typedef typename Vector::value_type T;\n  \n  Vector v(5);\n\n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), -_1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0],  0);\n  ASSERT_EQUAL(v[1], -1);\n  ASSERT_EQUAL(v[2], -2);\n  ASSERT_EQUAL(v[3], -3);\n  ASSERT_EQUAL(v[4], -4);\n  \n  tabulate_kernel<<<1,1>>>(exec, v.begin(), v.end(), _1 * _1 * _1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 27);\n  ASSERT_EQUAL(v[4], 64);\n}\n\nvoid TestTabulateDeviceSeq()\n{\n  TestTabulateDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTabulateDeviceSeq);\n\nvoid TestTabulateDeviceDevice()\n{\n  TestTabulateDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTabulateDeviceDevice);\n#endif\n\nvoid TestTabulateCudaStreams()\n{\n  using namespace thrust::placeholders;\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), -_1);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0],  0);\n  ASSERT_EQUAL(v[1], -1);\n  ASSERT_EQUAL(v[2], -2);\n  ASSERT_EQUAL(v[3], -3);\n  ASSERT_EQUAL(v[4], -4);\n  \n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), _1 * _1 * _1);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 27);\n  ASSERT_EQUAL(v[4], 64);\n\n  hipStreamSynchronize(s);\n}\nDECLARE_UNITTEST(TestTabulateCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/tabulate.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/find.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct equal_to_value_pred\n{\n    T value;\n\n    equal_to_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v == value; }\n};\n\n\ntemplate<typename T>\nstruct not_equal_to_value_pred\n{\n    T value;\n\n    not_equal_to_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v != value; }\n};\n\n\ntemplate<typename T>\nstruct less_than_value_pred\n{\n    T value;\n\n    less_than_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v < value; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void find_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::find(exec, first, last, value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find(h_data.begin(), h_data.end(), int(0));\n\n  find_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), int(0), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for(size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find(h_data.begin(), h_data.end(), sample);\n\n    find_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), sample, d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindDeviceSeq()\n{\n  TestFindDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindDeviceSeq);\n\n\nvoid TestFindDeviceDevice()\n{\n  TestFindDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find_if(h_data.begin(), h_data.end(), equal_to_value_pred<int>(0));\n\n  find_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), equal_to_value_pred<int>(0), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if(h_data.begin(), h_data.end(), equal_to_value_pred<int>(sample));\n\n    find_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), equal_to_value_pred<int>(sample), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindIfDeviceSeq()\n{\n  TestFindIfDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfDeviceSeq);\n\n\nvoid TestFindIfDeviceDevice()\n{\n  TestFindIfDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_not_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if_not(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindIfNotDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), not_equal_to_value_pred<int>(0));\n\n  find_if_not_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), not_equal_to_value_pred<int>(0), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for(size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), not_equal_to_value_pred<int>(sample));\n\n    find_if_not_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), not_equal_to_value_pred<int>(sample), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindIfNotDeviceSeq()\n{\n  TestFindIfNotDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceSeq);\n\n\nvoid TestFindIfNotDeviceDevice()\n{\n  TestFindIfNotDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceDevice);\n#endif\n\n\nvoid TestFindCudaStreams()\n{\n  thrust::device_vector<int> vec(5);\n  vec[0] = 1;\n  vec[1] = 2;\n  vec[2] = 3;\n  vec[3] = 3;\n  vec[4] = 5;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 0) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 1) - vec.begin(), 0);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 2) - vec.begin(), 1);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 3) - vec.begin(), 2);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 4) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 5) - vec.begin(), 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFindCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/find.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct equal_to_value_pred\n{\n    T value;\n\n    equal_to_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v == value; }\n};\n\n\ntemplate<typename T>\nstruct not_equal_to_value_pred\n{\n    T value;\n\n    not_equal_to_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v != value; }\n};\n\n\ntemplate<typename T>\nstruct less_than_value_pred\n{\n    T value;\n\n    less_than_value_pred(T value) : value(value) {}\n\n    __host__ __device__\n    bool operator()(T v) const { return v < value; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void find_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::find(exec, first, last, value);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find(h_data.begin(), h_data.end(), int(0));\n\n  find_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), int(0), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for(size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find(h_data.begin(), h_data.end(), sample);\n\n    find_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), sample, d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindDeviceSeq()\n{\n  TestFindDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindDeviceSeq);\n\n\nvoid TestFindDeviceDevice()\n{\n  TestFindDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find_if(h_data.begin(), h_data.end(), equal_to_value_pred<int>(0));\n\n  find_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), equal_to_value_pred<int>(0), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if(h_data.begin(), h_data.end(), equal_to_value_pred<int>(sample));\n\n    find_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), equal_to_value_pred<int>(sample), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindIfDeviceSeq()\n{\n  TestFindIfDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfDeviceSeq);\n\n\nvoid TestFindIfDeviceDevice()\n{\n  TestFindIfDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_not_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if_not(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestFindIfNotDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_iter;\n  \n  typedef typename thrust::device_vector<int>::iterator iter_type;\n  thrust::device_vector<iter_type> d_result(1);\n  \n  h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), not_equal_to_value_pred<int>(0));\n\n  find_if_not_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), not_equal_to_value_pred<int>(0), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  \n  for(size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), not_equal_to_value_pred<int>(sample));\n\n    find_if_not_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), not_equal_to_value_pred<int>(sample), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type)d_result[0] - d_data.begin());\n  }\n}\n\n\nvoid TestFindIfNotDeviceSeq()\n{\n  TestFindIfNotDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceSeq);\n\n\nvoid TestFindIfNotDeviceDevice()\n{\n  TestFindIfNotDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceDevice);\n#endif\n\n\nvoid TestFindCudaStreams()\n{\n  thrust::device_vector<int> vec(5);\n  vec[0] = 1;\n  vec[1] = 2;\n  vec[2] = 3;\n  vec[3] = 3;\n  vec[4] = 5;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 0) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 1) - vec.begin(), 0);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 2) - vec.begin(), 1);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 3) - vec.begin(), 2);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 4) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 5) - vec.begin(), 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFindCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/find.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/for_each.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\nstatic const size_t NUM_REGISTERS = 64;\n\ntemplate <size_t N> __host__ __device__ void f   (int * x) { int temp = *x; f<N - 1>(x + 1); *x = temp;};\ntemplate <>         __host__ __device__ void f<0>(int * /*x*/) { }\ntemplate <size_t N>\nstruct CopyFunctorWithManyRegisters\n{\n  __host__ __device__\n  void operator()(int * ptr)\n  {\n      f<N>(ptr);\n  }\n};\n\n\nvoid TestForEachLargeRegisterFootprint()\n{\n  int current_device = -1;\n  cudaGetDevice(&current_device);\n  cudaDeviceProp prop;\n  cudaGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int *> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n  \n  thrust::for_each(input.begin(), input.end(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachLargeRegisterFootprint);\n\n\nvoid TestForEachNLargeRegisterFootprint()\n{\n  int current_device = -1;\n  cudaGetDevice(&current_device);\n  cudaDeviceProp prop;\n  cudaGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int *> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n  \n  thrust::for_each_n(input.begin(), input.size(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachNLargeRegisterFootprint);\n\n\ntemplate <typename T>\nstruct mark_present_for_each\n{\n  T * ptr;\n  __host__ __device__ void\n  operator()(T x){ ptr[(int) x] = 1; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void for_each_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::for_each(exec, first, last, f);\n}\n\n\ntemplate<typename T>\nvoid TestForEachDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  ((size_t) h_input[i]) % output_size;\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n  \n  for_each_kernel<<<1,1>>>(thrust::seq, d_input.begin(), d_input.end(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestForEachDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n  \n  for_each_kernel<<<1,1>>>(thrust::device, d_input.begin(), d_input.end(), d_f);\n  {\n    cudaError_t const err = cudaGetLastError();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__\nvoid for_each_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::for_each_n(exec, first, n, f);\n}\n\n\ntemplate<typename T>\nvoid TestForEachNDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  static_cast<T>(((size_t) h_input[i]) % output_size);\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n  \n  for_each_n_kernel<<<1,1>>>(thrust::seq, d_input.begin(), d_input.size(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestForEachNDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  static_cast<T>(((size_t) h_input[i]) % output_size);\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n  \n  for_each_n_kernel<<<1,1>>>(thrust::device, d_input.begin(), d_input.size(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceDevice);\n#endif\n\n\nvoid TestForEachCudaStreams()\n{\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::device_vector<int> input(5);\n  thrust::device_vector<int> output(7, 0);\n  \n  input[0] = 3; input[1] = 2; input[2] = 3; input[3] = 4; input[4] = 6;\n  \n  mark_present_for_each<int> f;\n  f.ptr = thrust::raw_pointer_cast(output.data());\n  \n  thrust::for_each(thrust::cuda::par.on(s), input.begin(), input.end(), f);\n\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(output[0], 0);\n  ASSERT_EQUAL(output[1], 0);\n  ASSERT_EQUAL(output[2], 1);\n  ASSERT_EQUAL(output[3], 1);\n  ASSERT_EQUAL(output[4], 1);\n  ASSERT_EQUAL(output[5], 0);\n  ASSERT_EQUAL(output[6], 1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestForEachCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/for_each.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\nstatic const size_t NUM_REGISTERS = 64;\n\ntemplate <size_t N> __host__ __device__ void f   (int * x) { int temp = *x; f<N - 1>(x + 1); *x = temp;};\ntemplate <>         __host__ __device__ void f<0>(int * /*x*/) { }\ntemplate <size_t N>\nstruct CopyFunctorWithManyRegisters\n{\n  __host__ __device__\n  void operator()(int * ptr)\n  {\n      f<N>(ptr);\n  }\n};\n\n\nvoid TestForEachLargeRegisterFootprint()\n{\n  int current_device = -1;\n  hipGetDevice(&current_device);\n  hipDeviceProp_t prop;\n  hipGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int *> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n  \n  thrust::for_each(input.begin(), input.end(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachLargeRegisterFootprint);\n\n\nvoid TestForEachNLargeRegisterFootprint()\n{\n  int current_device = -1;\n  hipGetDevice(&current_device);\n  hipDeviceProp_t prop;\n  hipGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int *> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n  \n  thrust::for_each_n(input.begin(), input.size(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachNLargeRegisterFootprint);\n\n\ntemplate <typename T>\nstruct mark_present_for_each\n{\n  T * ptr;\n  __host__ __device__ void\n  operator()(T x){ ptr[(int) x] = 1; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void for_each_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::for_each(exec, first, last, f);\n}\n\n\ntemplate<typename T>\nvoid TestForEachDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  ((size_t) h_input[i]) % output_size;\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n  \n  for_each_kernel<<<1,1>>>(thrust::seq, d_input.begin(), d_input.end(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestForEachDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n  \n  for_each_kernel<<<1,1>>>(thrust::device, d_input.begin(), d_input.end(), d_f);\n  {\n    hipError_t const err = hipGetLastError();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__\nvoid for_each_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::for_each_n(exec, first, n, f);\n}\n\n\ntemplate<typename T>\nvoid TestForEachNDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  static_cast<T>(((size_t) h_input[i]) % output_size);\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n  \n  for_each_n_kernel<<<1,1>>>(thrust::seq, d_input.begin(), d_input.size(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestForEachNDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n  \n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_input[i] =  static_cast<T>(((size_t) h_input[i]) % output_size);\n  \n  thrust::device_vector<T> d_input = h_input;\n  \n  thrust::host_vector<T>   h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n  \n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n  \n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n  \n  for_each_n_kernel<<<1,1>>>(thrust::device, d_input.begin(), d_input.size(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceDevice);\n#endif\n\n\nvoid TestForEachCudaStreams()\n{\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::device_vector<int> input(5);\n  thrust::device_vector<int> output(7, 0);\n  \n  input[0] = 3; input[1] = 2; input[2] = 3; input[3] = 4; input[4] = 6;\n  \n  mark_present_for_each<int> f;\n  f.ptr = thrust::raw_pointer_cast(output.data());\n  \n  thrust::for_each(thrust::cuda::par.on(s), input.begin(), input.end(), f);\n\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(output[0], 0);\n  ASSERT_EQUAL(output[1], 0);\n  ASSERT_EQUAL(output[2], 1);\n  ASSERT_EQUAL(output[3], 1);\n  ASSERT_EQUAL(output[4], 1);\n  ASSERT_EQUAL(output[5], 0);\n  ASSERT_EQUAL(output[6], 1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestForEachCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/for_each.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/gather.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid gather_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 elements_first, Iterator3 result)\n{\n  thrust::gather(exec, map_first, map_last, elements_first, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGatherDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n  \n  // source vectors to gather from\n  thrust::host_vector<T>   h_source = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n  \n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_map[i] =  h_map[i] % source_size;\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  // gather destination\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::gather(h_map.begin(), h_map.end(), h_source.begin(), h_output.begin());\n\n  gather_kernel<<<1,1>>>(exec, d_map.begin(), d_map.end(), d_source.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate<typename T>\nvoid TestGatherDeviceSeq(const size_t n)\n{\n  TestGatherDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceSeq);\n\ntemplate<typename T>\nvoid TestGatherDeviceDevice(const size_t n)\n{\n  TestGatherDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceDevice);\n#endif\n\n\nvoid TestGatherCudaStreams()\n{\n  thrust::device_vector<int> map(5);  // gather indices\n  thrust::device_vector<int> src(8);  // source vector\n  thrust::device_vector<int> dst(5);  // destination vector\n  \n  map[0] = 6; map[1] = 2; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4; src[5] = 5; src[6] = 6; src[7] = 7;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::gather(thrust::cuda::par.on(s), map.begin(), map.end(), src.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 6);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 1);\n  ASSERT_EQUAL(dst[3], 7);\n  ASSERT_EQUAL(dst[4], 2);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate>\n__global__\nvoid gather_if_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 stencil_first, Iterator3 elements_first, Iterator4 result, Predicate pred)\n{\n  thrust::gather_if(exec, map_first, map_last, stencil_first, elements_first, result, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even_gather_if\n{\n  __host__ __device__\n  bool operator()(const T i) const\n  { \n    return (i % 2) == 0;\n  }\n};\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGatherIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n  \n  // source vectors to gather from\n  thrust::host_vector<T>   h_source = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n  \n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n      h_map[i] = h_map[i] % source_size;\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  // gather stencil\n  thrust::host_vector<unsigned int> h_stencil = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_stencil[i] = h_stencil[i] % 2;\n  \n  thrust::device_vector<unsigned int> d_stencil = h_stencil;\n  \n  // gather destination\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::gather_if(h_map.begin(), h_map.end(), h_stencil.begin(), h_source.begin(), h_output.begin(), is_even_gather_if<unsigned int>());\n\n  gather_if_kernel<<<1,1>>>(exec, d_map.begin(), d_map.end(), d_stencil.begin(), d_source.begin(), d_output.begin(), is_even_gather_if<unsigned int>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate<typename T>\nvoid TestGatherIfDeviceSeq(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceSeq);\n\ntemplate<typename T>\nvoid TestGatherIfDeviceDevice(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceDevice);\n#endif\n\nvoid TestGatherIfCudaStreams(void)\n{\n  thrust::device_vector<int> flg(5);  // predicate array\n  thrust::device_vector<int> map(5);  // gather indices\n  thrust::device_vector<int> src(8);  // source vector\n  thrust::device_vector<int> dst(5);  // destination vector\n  \n  flg[0] = 0; flg[1] = 1; flg[2] = 0; flg[3] = 1; flg[4] = 0;\n  map[0] = 6; map[1] = 2; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4; src[5] = 5; src[6] = 6; src[7] = 7;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::gather_if(thrust::cuda::par.on(s), map.begin(), map.end(), flg.begin(), src.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 0);\n  ASSERT_EQUAL(dst[3], 7);\n  ASSERT_EQUAL(dst[4], 0);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherIfCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/gather.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid gather_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 elements_first, Iterator3 result)\n{\n  thrust::gather(exec, map_first, map_last, elements_first, result);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGatherDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n  \n  // source vectors to gather from\n  thrust::host_vector<T>   h_source = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n  \n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_map[i] =  h_map[i] % source_size;\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  // gather destination\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::gather(h_map.begin(), h_map.end(), h_source.begin(), h_output.begin());\n\n  gather_kernel<<<1,1>>>(exec, d_map.begin(), d_map.end(), d_source.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate<typename T>\nvoid TestGatherDeviceSeq(const size_t n)\n{\n  TestGatherDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceSeq);\n\ntemplate<typename T>\nvoid TestGatherDeviceDevice(const size_t n)\n{\n  TestGatherDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceDevice);\n#endif\n\n\nvoid TestGatherCudaStreams()\n{\n  thrust::device_vector<int> map(5);  // gather indices\n  thrust::device_vector<int> src(8);  // source vector\n  thrust::device_vector<int> dst(5);  // destination vector\n  \n  map[0] = 6; map[1] = 2; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4; src[5] = 5; src[6] = 6; src[7] = 7;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::gather(thrust::cuda::par.on(s), map.begin(), map.end(), src.begin(), dst.begin());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 6);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 1);\n  ASSERT_EQUAL(dst[3], 7);\n  ASSERT_EQUAL(dst[4], 2);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate>\n__global__\nvoid gather_if_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 stencil_first, Iterator3 elements_first, Iterator4 result, Predicate pred)\n{\n  thrust::gather_if(exec, map_first, map_last, stencil_first, elements_first, result, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even_gather_if\n{\n  __host__ __device__\n  bool operator()(const T i) const\n  { \n    return (i % 2) == 0;\n  }\n};\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestGatherIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n  \n  // source vectors to gather from\n  thrust::host_vector<T>   h_source = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n  \n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n      h_map[i] = h_map[i] % source_size;\n  \n  thrust::device_vector<unsigned int> d_map = h_map;\n  \n  // gather stencil\n  thrust::host_vector<unsigned int> h_stencil = unittest::random_integers<unsigned int>(n);\n  \n  for(size_t i = 0; i < n; i++)\n    h_stencil[i] = h_stencil[i] % 2;\n  \n  thrust::device_vector<unsigned int> d_stencil = h_stencil;\n  \n  // gather destination\n  thrust::host_vector<T>   h_output(n);\n  thrust::device_vector<T> d_output(n);\n  \n  thrust::gather_if(h_map.begin(), h_map.end(), h_stencil.begin(), h_source.begin(), h_output.begin(), is_even_gather_if<unsigned int>());\n\n  gather_if_kernel<<<1,1>>>(exec, d_map.begin(), d_map.end(), d_stencil.begin(), d_source.begin(), d_output.begin(), is_even_gather_if<unsigned int>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate<typename T>\nvoid TestGatherIfDeviceSeq(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceSeq);\n\ntemplate<typename T>\nvoid TestGatherIfDeviceDevice(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceDevice);\n#endif\n\nvoid TestGatherIfCudaStreams(void)\n{\n  thrust::device_vector<int> flg(5);  // predicate array\n  thrust::device_vector<int> map(5);  // gather indices\n  thrust::device_vector<int> src(8);  // source vector\n  thrust::device_vector<int> dst(5);  // destination vector\n  \n  flg[0] = 0; flg[1] = 1; flg[2] = 0; flg[3] = 1; flg[4] = 0;\n  map[0] = 6; map[1] = 2; map[2] = 1; map[3] = 7; map[4] = 2;\n  src[0] = 0; src[1] = 1; src[2] = 2; src[3] = 3; src[4] = 4; src[5] = 5; src[6] = 6; src[7] = 7;\n  dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; dst[4] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::gather_if(thrust::cuda::par.on(s), map.begin(), map.end(), flg.begin(), src.begin(), dst.begin());\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(dst[0], 0);\n  ASSERT_EQUAL(dst[1], 2);\n  ASSERT_EQUAL(dst[2], 0);\n  ASSERT_EQUAL(dst[3], 7);\n  ASSERT_EQUAL(dst[4], 0);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherIfCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/gather.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid is_partitioned_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::is_partitioned(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsPartitionedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  n = thrust::max<size_t>(n, 2);\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_partitioned_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::partition(v.begin(), v.end(), is_even<int>());\n\n  is_partitioned_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\n\nvoid TestIsPartitionedDeviceSeq()\n{\n  TestIsPartitionedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceSeq);\n\n\nvoid TestIsPartitionedDeviceDevice()\n{\n  TestIsPartitionedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceDevice);\n#endif\n\n\nvoid TestIsPartitionedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 1; v[1] = 1; v[2] = 1; v[3] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // empty partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin(), thrust::identity<int>()));\n\n  // one element true partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, thrust::identity<int>()));\n\n  // just true partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 2, thrust::identity<int>()));\n\n  // both true & false partitions\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<int>()));\n\n  // one element false partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin() + 3, v.end(), thrust::identity<int>()));\n\n  v[0] = 1; v[1] = 0; v[2] = 1; v[3] = 1;\n\n  // not partitioned\n  ASSERT_EQUAL_QUIET(false, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<int>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsPartitionedCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__\nvoid is_partitioned_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::is_partitioned(exec, first, last, pred);\n}\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsPartitionedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  n = thrust::max<size_t>(n, 2);\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_partitioned_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::partition(v.begin(), v.end(), is_even<int>());\n\n  is_partitioned_kernel<<<1,1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\n\nvoid TestIsPartitionedDeviceSeq()\n{\n  TestIsPartitionedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceSeq);\n\n\nvoid TestIsPartitionedDeviceDevice()\n{\n  TestIsPartitionedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceDevice);\n#endif\n\n\nvoid TestIsPartitionedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 1; v[1] = 1; v[2] = 1; v[3] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // empty partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin(), thrust::identity<int>()));\n\n  // one element true partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, thrust::identity<int>()));\n\n  // just true partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 2, thrust::identity<int>()));\n\n  // both true & false partitions\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<int>()));\n\n  // one element false partition\n  ASSERT_EQUAL_QUIET(true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin() + 3, v.end(), thrust::identity<int>()));\n\n  v[0] = 1; v[1] = 0; v[2] = 1; v[3] = 1;\n\n  // not partitioned\n  ASSERT_EQUAL_QUIET(false, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<int>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsPartitionedCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/is_partitioned.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/equal.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2, pred);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestEqualDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::device_vector<T> d_data1 = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data2 = unittest::random_samples<T>(n);\n  thrust::device_vector<bool> d_result(1, false);\n  \n  //empty ranges\n  equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin(), d_data1.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n  \n  //symmetric cases\n  equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.end(), d_data1.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n  \n  if(n > 0)\n  {\n    d_data1[0] = 0; d_data2[0] = 1;\n    \n    //different vectors\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.end(), d_data2.begin(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n    \n    //different predicates\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), thrust::less<T>(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], true);\n\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), thrust::greater<T>(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n  }\n}\n\n\ntemplate<typename T>\nvoid TestEqualDeviceSeq(const size_t n)\n{\n  TestEqualDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestEqualDeviceDevice(const size_t n)\n{\n  TestEqualDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceDevice);\n#endif\n\n\nvoid TestEqualCudaStreams()\n{\n  thrust::device_vector<int> v1(5);\n  thrust::device_vector<int> v2(5);\n  v1[0] = 5; v1[1] = 2; v1[2] = 0; v1[3] = 0; v1[4] = 0;\n  v2[0] = 5; v2[1] = 2; v2[2] = 0; v2[3] = 6; v2[4] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin()), false);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v2.begin(), v2.end(), v2.begin()), true);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 0, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 1, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 3, v2.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 4, v2.begin()), false);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), thrust::less_equal<int>()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), thrust::greater<int>()),    false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestEqualCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/equal.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__\nvoid equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__\nvoid equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2, pred);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestEqualDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::device_vector<T> d_data1 = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data2 = unittest::random_samples<T>(n);\n  thrust::device_vector<bool> d_result(1, false);\n  \n  //empty ranges\n  equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin(), d_data1.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n  \n  //symmetric cases\n  equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.end(), d_data1.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n  \n  if(n > 0)\n  {\n    d_data1[0] = 0; d_data2[0] = 1;\n    \n    //different vectors\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.end(), d_data2.begin(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n    \n    //different predicates\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), thrust::less<T>(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], true);\n\n    equal_kernel<<<1,1>>>(exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), thrust::greater<T>(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n  }\n}\n\n\ntemplate<typename T>\nvoid TestEqualDeviceSeq(const size_t n)\n{\n  TestEqualDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceSeq);\n\n\ntemplate<typename T>\nvoid TestEqualDeviceDevice(const size_t n)\n{\n  TestEqualDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceDevice);\n#endif\n\n\nvoid TestEqualCudaStreams()\n{\n  thrust::device_vector<int> v1(5);\n  thrust::device_vector<int> v2(5);\n  v1[0] = 5; v1[1] = 2; v1[2] = 0; v1[3] = 0; v1[4] = 0;\n  v2[0] = 5; v2[1] = 2; v2[2] = 0; v2[3] = 6; v2[4] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin()), false);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v2.begin(), v2.end(), v2.begin()), true);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 0, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 1, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 3, v2.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 4, v2.begin()), false);\n  \n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), thrust::less_equal<int>()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), thrust::greater<int>()),    false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestEqualCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/equal.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/copy.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) { return (static_cast<unsigned int>(x) & 1) == 0; }\n};\n\n\ntemplate<typename T>\nstruct mod_3\n{\n  __host__ __device__\n  unsigned int operator()(T x) { return static_cast<unsigned int>(x) % 3; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<\n    typename thrust::device_vector<int>::iterator\n  > d_new_end_vec(1);\n  \n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n  \n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestCopyIfDeviceSeq()\n{\n  TestCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceSeq);\n\n\nvoid TestCopyIfDeviceDevice()\n{\n  TestCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceDevice);\n\n\nvoid TestCopyIfDeviceNoSync()\n{\n  TestCopyIfDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceNoSync);\n#endif\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s),\n                                         data.begin(), \n                                         data.end(), \n                                         result.begin(),\n                                         is_even<int>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n\n  ASSERT_EQUAL(result[0], 2);\n  ASSERT_EQUAL(result[1], 2);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestCopyIfCudaStreamsSync(){\n  TestCopyIfCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsSync);\n\nvoid TestCopyIfCudaStreamsNoSync(){\n  TestCopyIfCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__ void copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result1, Predicate pred, Iterator4 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, stencil_first, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data(n); thrust::sequence(h_data.begin(), h_data.end());\n  thrust::device_vector<int> d_data(n); thrust::sequence(d_data.begin(), d_data.end()); \n  \n  thrust::host_vector<int>   h_stencil = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_stencil = unittest::random_integers<int>(n);\n  \n  typename thrust::host_vector<int>::iterator   h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<\n    typename thrust::device_vector<int>::iterator\n  > d_new_end_vec(1);\n  \n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n  \n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestCopyIfStencilDeviceSeq()\n{\n  TestCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceSeq);\n\n\nvoid TestCopyIfStencilDeviceDevice()\n{\n  TestCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceDevice);\n\n\nvoid TestCopyIfStencilDeviceNoSync()\n{\n  TestCopyIfStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfStencilCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s),\n                                         data.begin(), \n                                         data.end(),\n                                         stencil.begin(),\n                                         result.begin(),\n                                         thrust::identity<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n\n  ASSERT_EQUAL(result[0], 2);\n  ASSERT_EQUAL(result[1], 2);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestCopyIfStencilCudaStreamsSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsSync);\n\n\nvoid TestCopyIfStencilCudaStreamsNoSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/copy.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) { return (static_cast<unsigned int>(x) & 1) == 0; }\n};\n\n\ntemplate<typename T>\nstruct mod_3\n{\n  __host__ __device__\n  unsigned int operator()(T x) { return static_cast<unsigned int>(x) % 3; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n  \n  typename thrust::host_vector<int>::iterator   h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<\n    typename thrust::device_vector<int>::iterator\n  > d_new_end_vec(1);\n  \n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n  \n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestCopyIfDeviceSeq()\n{\n  TestCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceSeq);\n\n\nvoid TestCopyIfDeviceDevice()\n{\n  TestCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceDevice);\n\n\nvoid TestCopyIfDeviceNoSync()\n{\n  TestCopyIfDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceNoSync);\n#endif\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s),\n                                         data.begin(), \n                                         data.end(), \n                                         result.begin(),\n                                         is_even<int>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n\n  ASSERT_EQUAL(result[0], 2);\n  ASSERT_EQUAL(result[1], 2);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestCopyIfCudaStreamsSync(){\n  TestCopyIfCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsSync);\n\nvoid TestCopyIfCudaStreamsNoSync(){\n  TestCopyIfCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsNoSync);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__ void copy_if_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 result1, Predicate pred, Iterator4 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, stencil_first, result1, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int>   h_data(n); thrust::sequence(h_data.begin(), h_data.end());\n  thrust::device_vector<int> d_data(n); thrust::sequence(d_data.begin(), d_data.end()); \n  \n  thrust::host_vector<int>   h_stencil = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_stencil = unittest::random_integers<int>(n);\n  \n  typename thrust::host_vector<int>::iterator   h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<\n    typename thrust::device_vector<int>::iterator\n  > d_new_end_vec(1);\n  \n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n  \n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int>   h_result(n);\n    thrust::device_vector<int> d_result(n);\n    \n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n    \n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n    \n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\n\nvoid TestCopyIfStencilDeviceSeq()\n{\n  TestCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceSeq);\n\n\nvoid TestCopyIfStencilDeviceDevice()\n{\n  TestCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceDevice);\n\n\nvoid TestCopyIfStencilDeviceNoSync()\n{\n  TestCopyIfStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestCopyIfStencilCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  3; \n  data[4] =  2; \n\n  Vector result(5);\n\n  Vector stencil(5);\n  stencil[0] = 0;\n  stencil[1] = 1;\n  stencil[2] = 0;\n  stencil[3] = 0;\n  stencil[4] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s),\n                                         data.begin(), \n                                         data.end(),\n                                         stencil.begin(),\n                                         result.begin(),\n                                         thrust::identity<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n\n  ASSERT_EQUAL(result[0], 2);\n  ASSERT_EQUAL(result[1], 2);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestCopyIfStencilCudaStreamsSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsSync);\n\n\nvoid TestCopyIfStencilCudaStreamsNoSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/copy_if.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid is_sorted_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::is_sorted(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsSortedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsSortedDeviceSeq()\n{\n  TestIsSortedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceSeq);\n\n\nvoid TestIsSortedDeviceDevice()\n{\n  TestIsSortedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceDevice);\n#endif\n\n\nvoid TestIsSortedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 0; v[1] = 5; v[2] = 8; v[3] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 0), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1), true);\n  \n  // the following line crashes gcc 4.3\n#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)\n  // do nothing\n#else\n  // compile this line on other compilers\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 2), true);\n#endif // GCC\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4), false);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3, thrust::less<int>()),    true);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, thrust::greater<int>()), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4, thrust::greater<int>()), false);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.end()), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/sort.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__\nvoid is_sorted_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::is_sorted(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestIsSortedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_kernel<<<1,1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsSortedDeviceSeq()\n{\n  TestIsSortedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceSeq);\n\n\nvoid TestIsSortedDeviceDevice()\n{\n  TestIsSortedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceDevice);\n#endif\n\n\nvoid TestIsSortedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 0; v[1] = 5; v[2] = 8; v[3] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 0), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1), true);\n  \n  // the following line crashes gcc 4.3\n#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)\n  // do nothing\n#else\n  // compile this line on other compilers\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 2), true);\n#endif // GCC\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4), false);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3, thrust::less<int>()),    true);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, thrust::greater<int>()), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4, thrust::greater<int>()), false);\n  \n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.end()), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/is_sorted.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/system/cuda/memory.h>\n#include <thrust/system/cpp/memory.h>\n#include <thrust/memory.h>\n#include <thrust/execution_policy.h>\n#include <thrust/logical.h>\n\n\ntemplate<typename T1, typename T2>\nbool are_same_type(const T1 &, const T2 &)\n{\n  return false;\n}\n\n\ntemplate<typename T>\nbool are_same_type(const T &, const T &)\n{\n  return true;\n}\n\n\nvoid TestSelectSystemCudaToCpp()\n{\n  using thrust::system::detail::generic::select_system;\n\n  thrust::cuda::tag cuda_tag;\n  thrust::cpp::tag cpp_tag;\n  thrust::cuda_cub::cross_system<thrust::cuda::tag,thrust::cpp::tag> cuda_to_cpp(cuda_tag, cpp_tag);\n\n  // select_system(cuda::tag, thrust::host_system_tag) should return cuda_to_cpp\n  bool is_cuda_to_cpp = are_same_type(cuda_to_cpp, select_system(cuda_tag, cpp_tag));\n  ASSERT_EQUAL(true, is_cuda_to_cpp);\n}\nDECLARE_UNITTEST(TestSelectSystemCudaToCpp);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename Iterator>\n__global__ void get_temporary_buffer_kernel(size_t n, Iterator result)\n{\n  *result = thrust::get_temporary_buffer<int>(thrust::seq, n);\n}\n\n\ntemplate<typename Pointer>\n__global__ void return_temporary_buffer_kernel(Pointer ptr, std::ptrdiff_t n)\n{\n  thrust::return_temporary_buffer(thrust::seq, ptr, n);\n}\n\n\nvoid TestGetTemporaryBufferDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  typedef thrust::pointer<int, thrust::detail::seq_t> pointer;\n  typedef thrust::pair<pointer, std::ptrdiff_t> ptr_and_sz_type;\n  thrust::device_vector<ptr_and_sz_type> d_result(1);\n  \n  get_temporary_buffer_kernel<<<1,1>>>(n, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ptr_and_sz_type ptr_and_sz = d_result[0];\n\n  if(ptr_and_sz.second > 0)\n  {\n    ASSERT_EQUAL(ptr_and_sz.second, n);\n\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr_and_sz.first, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr_and_sz.first, ptr_and_sz.first + n, thrust::placeholders::_1 == ref_val));\n\n    return_temporary_buffer_kernel<<<1,1>>>(ptr_and_sz.first, ptr_and_sz.second);\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestGetTemporaryBufferDeviceSeq);\n\n\ntemplate<typename Iterator>\n__global__ void malloc_kernel(size_t n, Iterator result)\n{\n  *result = static_cast<int*>(thrust::malloc(thrust::seq, sizeof(int) * n).get());\n}\n\n\ntemplate<typename Pointer>\n__global__ void free_kernel(Pointer ptr)\n{\n  thrust::free(thrust::seq, ptr);\n}\n\n\nvoid TestMallocDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  typedef thrust::pointer<int, thrust::detail::seq_t> pointer;\n  thrust::device_vector<pointer> d_result(1);\n  \n  malloc_kernel<<<1,1>>>(n, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  pointer ptr = d_result[0];\n\n  if(ptr.get() != 0)\n  {\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr, ptr + n, thrust::placeholders::_1 == ref_val));\n\n    free_kernel<<<1,1>>>(ptr);\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestMallocDeviceSeq);\n#endif\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/system/cuda/memory.h>\n#include <thrust/system/cpp/memory.h>\n#include <thrust/memory.h>\n#include <thrust/execution_policy.h>\n#include <thrust/logical.h>\n\n\ntemplate<typename T1, typename T2>\nbool are_same_type(const T1 &, const T2 &)\n{\n  return false;\n}\n\n\ntemplate<typename T>\nbool are_same_type(const T &, const T &)\n{\n  return true;\n}\n\n\nvoid TestSelectSystemCudaToCpp()\n{\n  using thrust::system::detail::generic::select_system;\n\n  thrust::cuda::tag cuda_tag;\n  thrust::cpp::tag cpp_tag;\n  thrust::cuda_cub::cross_system<thrust::cuda::tag,thrust::cpp::tag> cuda_to_cpp(cuda_tag, cpp_tag);\n\n  // select_system(cuda::tag, thrust::host_system_tag) should return cuda_to_cpp\n  bool is_cuda_to_cpp = are_same_type(cuda_to_cpp, select_system(cuda_tag, cpp_tag));\n  ASSERT_EQUAL(true, is_cuda_to_cpp);\n}\nDECLARE_UNITTEST(TestSelectSystemCudaToCpp);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename Iterator>\n__global__ void get_temporary_buffer_kernel(size_t n, Iterator result)\n{\n  *result = thrust::get_temporary_buffer<int>(thrust::seq, n);\n}\n\n\ntemplate<typename Pointer>\n__global__ void return_temporary_buffer_kernel(Pointer ptr, std::ptrdiff_t n)\n{\n  thrust::return_temporary_buffer(thrust::seq, ptr, n);\n}\n\n\nvoid TestGetTemporaryBufferDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  typedef thrust::pointer<int, thrust::detail::seq_t> pointer;\n  typedef thrust::pair<pointer, std::ptrdiff_t> ptr_and_sz_type;\n  thrust::device_vector<ptr_and_sz_type> d_result(1);\n  \n  get_temporary_buffer_kernel<<<1,1>>>(n, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ptr_and_sz_type ptr_and_sz = d_result[0];\n\n  if(ptr_and_sz.second > 0)\n  {\n    ASSERT_EQUAL(ptr_and_sz.second, n);\n\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr_and_sz.first, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr_and_sz.first, ptr_and_sz.first + n, thrust::placeholders::_1 == ref_val));\n\n    return_temporary_buffer_kernel<<<1,1>>>(ptr_and_sz.first, ptr_and_sz.second);\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestGetTemporaryBufferDeviceSeq);\n\n\ntemplate<typename Iterator>\n__global__ void malloc_kernel(size_t n, Iterator result)\n{\n  *result = static_cast<int*>(thrust::malloc(thrust::seq, sizeof(int) * n).get());\n}\n\n\ntemplate<typename Pointer>\n__global__ void free_kernel(Pointer ptr)\n{\n  thrust::free(thrust::seq, ptr);\n}\n\n\nvoid TestMallocDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  typedef thrust::pointer<int, thrust::detail::seq_t> pointer;\n  thrust::device_vector<pointer> d_result(1);\n  \n  malloc_kernel<<<1,1>>>(n, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  pointer ptr = d_result[0];\n\n  if(ptr.get() != 0)\n  {\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr, ptr + n, thrust::placeholders::_1 == ref_val));\n\n    free_kernel<<<1,1>>>(ptr);\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestMallocDeviceSeq);\n#endif\n\n", "source": "/kaggle/working/thrust/testing/cuda/memory.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::sequence(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init)\n{\n  thrust::sequence(exec, first, last, init);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, T step)\n{\n  thrust::sequence(exec, first, last, init, step);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSequenceDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> v(5);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end(), 10);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 11);\n  ASSERT_EQUAL(v[2], 12);\n  ASSERT_EQUAL(v[3], 13);\n  ASSERT_EQUAL(v[4], 14);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end(), 10, 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 12);\n  ASSERT_EQUAL(v[2], 14);\n  ASSERT_EQUAL(v[3], 16);\n  ASSERT_EQUAL(v[4], 18);\n}\n\nvoid TestSequenceDeviceSeq()\n{\n  TestSequenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSequenceDeviceSeq);\n\nvoid TestSequenceDeviceDevice()\n{\n  TestSequenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSequenceDeviceDevice);\n#endif\n\nvoid TestSequenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 11);\n  ASSERT_EQUAL(v[2], 12);\n  ASSERT_EQUAL(v[3], 13);\n  ASSERT_EQUAL(v[4], 14);\n  \n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10, 2);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 12);\n  ASSERT_EQUAL(v[2], 14);\n  ASSERT_EQUAL(v[3], 16);\n  ASSERT_EQUAL(v[4], 18);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSequenceCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/sequence.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::sequence(exec, first, last);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init)\n{\n  thrust::sequence(exec, first, last, init);\n}\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, T step)\n{\n  thrust::sequence(exec, first, last, init, step);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestSequenceDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> v(5);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end(), 10);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 11);\n  ASSERT_EQUAL(v[2], 12);\n  ASSERT_EQUAL(v[3], 13);\n  ASSERT_EQUAL(v[4], 14);\n  \n  sequence_kernel<<<1,1>>>(exec, v.begin(), v.end(), 10, 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 12);\n  ASSERT_EQUAL(v[2], 14);\n  ASSERT_EQUAL(v[3], 16);\n  ASSERT_EQUAL(v[4], 18);\n}\n\nvoid TestSequenceDeviceSeq()\n{\n  TestSequenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSequenceDeviceSeq);\n\nvoid TestSequenceDeviceDevice()\n{\n  TestSequenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSequenceDeviceDevice);\n#endif\n\nvoid TestSequenceCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  \n  Vector v(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 2);\n  ASSERT_EQUAL(v[3], 3);\n  ASSERT_EQUAL(v[4], 4);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 11);\n  ASSERT_EQUAL(v[2], 12);\n  ASSERT_EQUAL(v[3], 13);\n  ASSERT_EQUAL(v[4], 14);\n  \n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10, 2);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v[0], 10);\n  ASSERT_EQUAL(v[1], 12);\n  ASSERT_EQUAL(v[2], 14);\n  ASSERT_EQUAL(v[3], 16);\n  ASSERT_EQUAL(v[4], 18);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSequenceCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/sequence.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n  \n  partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n  \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestPartitionDeviceSeq()\n{\n  TestPartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionDeviceSeq);\n\n\nvoid TestPartitionDeviceDevice()\n{\n  TestPartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionDeviceDevice);\n\n\nvoid TestPartitionDeviceNoSync()\n{\n  TestPartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::partition(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n  \n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1; \n  stencil[1] = 2; \n  stencil[2] = 1;\n  stencil[3] = 1; \n  stencil[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n  \n  partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n  \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestPartitionStencilDeviceSeq()\n{\n  TestPartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceSeq);\n\n\nvoid TestPartitionStencilDeviceDevice()\n{\n  TestPartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceDevice);\n\n\nvoid TestPartitionStencilDeviceNoSync()\n{\n  TestPartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 true_result, Iterator3 false_result, Predicate pred, Iterator4 result)\n{\n  *result = thrust::partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCopyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  1; \n  data[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n  \n  partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] =  2;\n  true_ref[1] =  2;\n  \n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] =  1;\n  false_ref[1] =  1;\n  false_ref[2] =  1;\n\n  pair_type ends = iterators[0];\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestPartitionCopyDeviceSeq()\n{\n  TestPartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceSeq);\n\n\nvoid TestPartitionCopyDeviceDevice()\n{\n  TestPartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceDevice);\n\n\nvoid TestPartitionCopyDeviceNoSync()\n{\n  TestPartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate, typename Iterator5>\n__global__\nvoid partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 true_result, Iterator4 false_result, Predicate pred, Iterator5 result)\n{\n  *result = thrust::partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<int> data(5);\n  data[0] =  0; \n  data[1] =  1; \n  data[2] =  0;\n  data[3] =  0; \n  data[4] =  1; \n  \n  thrust::device_vector<int> stencil(5);\n  stencil[0] =  1; \n  stencil[1] =  2; \n  stencil[2] =  1;\n  stencil[3] =  1; \n  stencil[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  pair_type ends = iterators[0];\n  \n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] =  1;\n  true_ref[1] =  1;\n  \n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] =  0;\n  false_ref[1] =  0;\n  false_ref[2] =  0;\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestPartitionCopyStencilDeviceSeq()\n{\n  TestPartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceSeq);\n\n\nvoid TestPartitionCopyStencilDeviceDevice()\n{\n  TestPartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceDevice);\n\n\nvoid TestPartitionCopyStencilDeviceNoSync()\n{\n  TestPartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid stable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::stable_partition(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n    \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestStablePartitionDeviceSeq()\n{\n  TestStablePartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceSeq);\n\n\nvoid TestStablePartitionDeviceDevice()\n{\n  TestStablePartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceDevice);\n\n\nvoid TestStablePartitionDeviceNoSync()\n{\n  TestStablePartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid stable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::stable_partition(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n  \n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1; \n  stencil[1] = 2; \n  stencil[2] = 1;\n  stencil[3] = 1; \n  stencil[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n    \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestStablePartitionStencilDeviceSeq()\n{\n  TestStablePartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceSeq);\n\n\nvoid TestStablePartitionStencilDeviceDevice()\n{\n  TestStablePartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceDevice);\n\n\nvoid TestStablePartitionStencilDeviceNoSync()\n{\n  TestStablePartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid stable_partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 true_result, Iterator3 false_result, Predicate pred, Iterator4 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionCopyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  1; \n  data[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n  \n  stable_partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  \n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] =  2;\n  true_ref[1] =  2;\n  \n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] =  1;\n  false_ref[1] =  1;\n  false_ref[2] =  1;\n\n  pair_type ends = iterators[0];\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestStablePartitionCopyDeviceSeq()\n{\n  TestStablePartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceSeq);\n\n\nvoid TestStablePartitionCopyDeviceDevice()\n{\n  TestStablePartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceDevice);\n\n\nvoid TestStablePartitionCopyDeviceNoSync()\n{\n  TestStablePartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate, typename Iterator5>\n__global__\nvoid stable_partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 true_result, Iterator4 false_result, Predicate pred, Iterator5 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<int> data(5);\n  data[0] =  0; \n  data[1] =  1; \n  data[2] =  0;\n  data[3] =  0; \n  data[4] =  1; \n  \n  thrust::device_vector<int> stencil(5);\n  stencil[0] =  1; \n  stencil[1] =  2; \n  stencil[2] =  1;\n  stencil[3] =  1; \n  stencil[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  pair_type ends = iterators[0];\n  \n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] =  1;\n  true_ref[1] =  1;\n  \n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] =  0;\n  false_ref[1] =  0;\n  false_ref[2] =  0;\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestStablePartitionCopyStencilDeviceSeq()\n{\n  TestStablePartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceSeq);\n\n\nvoid TestStablePartitionCopyStencilDeviceDevice()\n{\n  TestStablePartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceDevice);\n\n\nvoid TestStablePartitionCopyStencilDeviceNoSync()\n{\n  TestStablePartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n  \n  Vector data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  Iterator iter = thrust::partition(streampolicy, data.begin(), data.end(), is_even<T>());\n  \n  Vector ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n  \n  ASSERT_EQUAL(iter - data.begin(), 2);\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestPartitionCudaStreamsSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsSync);\n\n\nvoid TestPartitionCudaStreamsNoSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsNoSync);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/partition.h>\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n\ntemplate<typename T>\nstruct is_even\n{\n  __host__ __device__\n  bool operator()(T x) const { return ((int) x % 2) == 0; }\n};\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n  \n  partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n  \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestPartitionDeviceSeq()\n{\n  TestPartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionDeviceSeq);\n\n\nvoid TestPartitionDeviceDevice()\n{\n  TestPartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionDeviceDevice);\n\n\nvoid TestPartitionDeviceNoSync()\n{\n  TestPartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::partition(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n  \n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1; \n  stencil[1] = 2; \n  stencil[2] = 1;\n  stencil[3] = 1; \n  stencil[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n  \n  partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n  \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestPartitionStencilDeviceSeq()\n{\n  TestPartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceSeq);\n\n\nvoid TestPartitionStencilDeviceDevice()\n{\n  TestPartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceDevice);\n\n\nvoid TestPartitionStencilDeviceNoSync()\n{\n  TestPartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 true_result, Iterator3 false_result, Predicate pred, Iterator4 result)\n{\n  *result = thrust::partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCopyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  1; \n  data[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n  \n  partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] =  2;\n  true_ref[1] =  2;\n  \n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] =  1;\n  false_ref[1] =  1;\n  false_ref[2] =  1;\n\n  pair_type ends = iterators[0];\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestPartitionCopyDeviceSeq()\n{\n  TestPartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceSeq);\n\n\nvoid TestPartitionCopyDeviceDevice()\n{\n  TestPartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceDevice);\n\n\nvoid TestPartitionCopyDeviceNoSync()\n{\n  TestPartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate, typename Iterator5>\n__global__\nvoid partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 true_result, Iterator4 false_result, Predicate pred, Iterator5 result)\n{\n  *result = thrust::partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<int> data(5);\n  data[0] =  0; \n  data[1] =  1; \n  data[2] =  0;\n  data[3] =  0; \n  data[4] =  1; \n  \n  thrust::device_vector<int> stencil(5);\n  stencil[0] =  1; \n  stencil[1] =  2; \n  stencil[2] =  1;\n  stencil[3] =  1; \n  stencil[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  pair_type ends = iterators[0];\n  \n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] =  1;\n  true_ref[1] =  1;\n  \n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] =  0;\n  false_ref[1] =  0;\n  false_ref[2] =  0;\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestPartitionCopyStencilDeviceSeq()\n{\n  TestPartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceSeq);\n\n\nvoid TestPartitionCopyStencilDeviceDevice()\n{\n  TestPartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceDevice);\n\n\nvoid TestPartitionCopyStencilDeviceNoSync()\n{\n  TestPartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__\nvoid stable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::stable_partition(exec, first, last, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n    \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestStablePartitionDeviceSeq()\n{\n  TestStablePartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceSeq);\n\n\nvoid TestStablePartitionDeviceDevice()\n{\n  TestStablePartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceDevice);\n\n\nvoid TestStablePartitionDeviceNoSync()\n{\n  TestStablePartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__\nvoid stable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::stable_partition(exec, first, last, stencil_first, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef typename thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n  \n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1; \n  stencil[1] = 2; \n  stencil[2] = 1;\n  stencil[3] = 1; \n  stencil[4] = 2; \n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n    \n  ASSERT_EQUAL(2, (iterator)result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\n\nvoid TestStablePartitionStencilDeviceSeq()\n{\n  TestStablePartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceSeq);\n\n\nvoid TestStablePartitionStencilDeviceDevice()\n{\n  TestStablePartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceDevice);\n\n\nvoid TestStablePartitionStencilDeviceNoSync()\n{\n  TestStablePartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Predicate, typename Iterator4>\n__global__\nvoid stable_partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 true_result, Iterator3 false_result, Predicate pred, Iterator4 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionCopyDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  typedef thrust::device_vector<T>::iterator iterator;\n  \n  thrust::device_vector<T> data(5);\n  data[0] =  1; \n  data[1] =  2; \n  data[2] =  1;\n  data[3] =  1; \n  data[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n  \n  stable_partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  \n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] =  2;\n  true_ref[1] =  2;\n  \n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] =  1;\n  false_ref[1] =  1;\n  false_ref[2] =  1;\n\n  pair_type ends = iterators[0];\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestStablePartitionCopyDeviceSeq()\n{\n  TestStablePartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceSeq);\n\n\nvoid TestStablePartitionCopyDeviceDevice()\n{\n  TestStablePartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceDevice);\n\n\nvoid TestStablePartitionCopyDeviceNoSync()\n{\n  TestStablePartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceNoSync);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4, typename Predicate, typename Iterator5>\n__global__\nvoid stable_partition_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Iterator3 true_result, Iterator4 false_result, Predicate pred, Iterator5 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestStablePartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<int> data(5);\n  data[0] =  0; \n  data[1] =  1; \n  data[2] =  0;\n  data[3] =  0; \n  data[4] =  1; \n  \n  thrust::device_vector<int> stencil(5);\n  stencil[0] =  1; \n  stencil[1] =  2; \n  stencil[2] =  1;\n  stencil[3] =  1; \n  stencil[4] =  2; \n  \n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  typedef typename thrust::device_vector<int>::iterator iterator;\n  typedef thrust::pair<iterator,iterator> pair_type;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1,1>>>(exec, data.begin(), data.end(), stencil.begin(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  pair_type ends = iterators[0];\n  \n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] =  1;\n  true_ref[1] =  1;\n  \n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] =  0;\n  false_ref[1] =  0;\n  false_ref[2] =  0;\n  \n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\n\nvoid TestStablePartitionCopyStencilDeviceSeq()\n{\n  TestStablePartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceSeq);\n\n\nvoid TestStablePartitionCopyStencilDeviceDevice()\n{\n  TestStablePartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceDevice);\n\n\nvoid TestStablePartitionCopyStencilDeviceNoSync()\n{\n  TestStablePartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceNoSync);\n#endif\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestPartitionCudaStreams(ExecutionPolicy policy)\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  typedef Vector::iterator   Iterator;\n  \n  Vector data(5);\n  data[0] = 1; \n  data[1] = 2; \n  data[2] = 1;\n  data[3] = 1; \n  data[4] = 2; \n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n  \n  Iterator iter = thrust::partition(streampolicy, data.begin(), data.end(), is_even<T>());\n  \n  Vector ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n  \n  ASSERT_EQUAL(iter - data.begin(), 2);\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestPartitionCudaStreamsSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsSync);\n\n\nvoid TestPartitionCudaStreamsNoSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsNoSync);\n\n", "source": "/kaggle/working/thrust/testing/cuda/partition.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/logical.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid all_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::all_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestAllOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  v[1] = 0;\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\n\nvoid TestAllOfDeviceSeq()\n{\n  TestAllOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAllOfDeviceSeq);\n\n\nvoid TestAllOfDeviceDevice()\n{\n  TestAllOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAllOfDeviceDevice);\n#endif\n\n\nvoid TestAllOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(3, 1);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n  \n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAllOfCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid any_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::any_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestAnyOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  v[1] = 0;\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\n\nvoid TestAnyOfDeviceSeq()\n{\n  TestAnyOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceSeq);\n\n\nvoid TestAnyOfDeviceDevice()\n{\n  TestAnyOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceDevice);\n#endif\n\n\nvoid TestAnyOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector v(3, 1);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n\n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAnyOfCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid none_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::none_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestNoneOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  v[1] = 0;\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\n\nvoid TestNoneOfDeviceSeq()\n{\n  TestNoneOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceSeq);\n\n\nvoid TestNoneOfDeviceDevice()\n{\n  TestNoneOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceDevice);\n#endif\n\n\nvoid TestNoneOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector v(3, 1);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n\n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), true);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestNoneOfCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/logical.h>\n#include <thrust/functional.h>\n#include <thrust/execution_policy.h>\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid all_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::all_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestAllOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  v[1] = 0;\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\n\nvoid TestAllOfDeviceSeq()\n{\n  TestAllOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAllOfDeviceSeq);\n\n\nvoid TestAllOfDeviceDevice()\n{\n  TestAllOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAllOfDeviceDevice);\n#endif\n\n\nvoid TestAllOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n  \n  Vector v(3, 1);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n  \n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n  \n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAllOfCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid any_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::any_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestAnyOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  v[1] = 0;\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n  \n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\n\nvoid TestAnyOfDeviceSeq()\n{\n  TestAnyOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceSeq);\n\n\nvoid TestAnyOfDeviceDevice()\n{\n  TestAnyOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceDevice);\n#endif\n\n\nvoid TestAnyOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector v(3, 1);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n\n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), true);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAnyOfCudaStreams);\n\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__\nvoid none_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::none_of(exec, first, last, f);\n}\n\n\ntemplate<typename ExecutionPolicy>\nvoid TestNoneOfDevice(ExecutionPolicy exec)\n{\n  typedef int T;\n  \n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  v[1] = 0;\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin(), v.end(), thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n  \n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 0, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 1, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 0, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1,1>>>(exec, v.begin() + 1, v.begin() + 2, thrust::identity<T>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\n\nvoid TestNoneOfDeviceSeq()\n{\n  TestNoneOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceSeq);\n\n\nvoid TestNoneOfDeviceDevice()\n{\n  TestNoneOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceDevice);\n#endif\n\n\nvoid TestNoneOfCudaStreams()\n{\n  typedef thrust::device_vector<int> Vector;\n  typedef Vector::value_type T;\n\n  Vector v(3, 1);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n\n  v[1] = 0;\n  \n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), thrust::identity<T>()), false);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, thrust::identity<T>()), true);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, thrust::identity<T>()), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, thrust::identity<T>()), true);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestNoneOfCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/logical.cu"}
{"input": "#include <unittest/unittest.h>\n#include <thrust/fill.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value)\n{\n  thrust::fill(exec, first, last, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestFillDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  thrust::fill(h_data.begin() + std::min((size_t)1, n), h_data.begin() + std::min((size_t)3, n), (T) 0);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)1, n), d_data.begin() + std::min((size_t)3, n), (T) 0);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)117, n), h_data.begin() + std::min((size_t)367, n), (T) 1);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)117, n), d_data.begin() + std::min((size_t)367, n), (T) 1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)8, n), h_data.begin() + std::min((size_t)259, n), (T) 2);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)8, n), d_data.begin() + std::min((size_t)259, n), (T) 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)3, n), h_data.end(), (T) 3);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)3, n), d_data.end(), (T) 3);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin(), h_data.end(), (T) 4);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), (T) 4);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate<typename T>\nvoid TestFillDeviceSeq(size_t n)\n{\n  TestFillDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceSeq);\n\ntemplate<typename T>\nvoid TestFillDeviceDevice(size_t n)\n{\n  TestFillDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename T>\n__global__\nvoid fill_n_kernel(ExecutionPolicy exec, Iterator first, Size n, T value)\n{\n  thrust::fill_n(exec, first, n, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestFillNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  size_t begin_offset = std::min<size_t>(1,n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)3, n) - begin_offset, (T) 0);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)3, n) - begin_offset, (T) 0);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(117, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)367, n) - begin_offset, (T) 1);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)367, n) - begin_offset, (T) 1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(8, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)259, n) - begin_offset, (T) 2);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)259, n) - begin_offset, (T) 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(3, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, h_data.size() - begin_offset, (T) 3);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, d_data.size() - begin_offset, (T) 3);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill_n(h_data.begin(), h_data.size(), (T) 4);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin(), d_data.size(), (T) 4);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate<typename T>\nvoid TestFillNDeviceSeq(size_t n)\n{\n  TestFillNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceSeq);\n\ntemplate<typename T>\nvoid TestFillNDeviceDevice(size_t n)\n{\n  TestFillNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceDevice);\n#endif\n\nvoid TestFillCudaStreams()\n{\n  thrust::device_vector<int> v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 4, 7);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 7);\n  ASSERT_EQUAL(v[2], 7);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 3, 8);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 2, v.end(), 9);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], 9);\n  ASSERT_EQUAL(v[3], 9);\n  ASSERT_EQUAL(v[4], 9);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin(), v.end(), 1);\n  cudaStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 1);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 1);\n  ASSERT_EQUAL(v[3], 1);\n  ASSERT_EQUAL(v[4], 1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFillCudaStreams);\n\n", "output": "#include \"hip/hip_runtime.h\"\n#include <unittest/unittest.h>\n#include <thrust/fill.h>\n#include <thrust/execution_policy.h>\n#include <algorithm>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate<typename ExecutionPolicy, typename Iterator, typename T>\n__global__\nvoid fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value)\n{\n  thrust::fill(exec, first, last, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestFillDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  thrust::fill(h_data.begin() + std::min((size_t)1, n), h_data.begin() + std::min((size_t)3, n), (T) 0);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)1, n), d_data.begin() + std::min((size_t)3, n), (T) 0);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)117, n), h_data.begin() + std::min((size_t)367, n), (T) 1);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)117, n), d_data.begin() + std::min((size_t)367, n), (T) 1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)8, n), h_data.begin() + std::min((size_t)259, n), (T) 2);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)8, n), d_data.begin() + std::min((size_t)259, n), (T) 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin() + std::min((size_t)3, n), h_data.end(), (T) 3);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin() + std::min((size_t)3, n), d_data.end(), (T) 3);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill(h_data.begin(), h_data.end(), (T) 4);\n\n  fill_kernel<<<1,1>>>(exec, d_data.begin(), d_data.end(), (T) 4);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate<typename T>\nvoid TestFillDeviceSeq(size_t n)\n{\n  TestFillDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceSeq);\n\ntemplate<typename T>\nvoid TestFillDeviceDevice(size_t n)\n{\n  TestFillDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceDevice);\n\n\ntemplate<typename ExecutionPolicy, typename Iterator, typename Size, typename T>\n__global__\nvoid fill_n_kernel(ExecutionPolicy exec, Iterator first, Size n, T value)\n{\n  thrust::fill_n(exec, first, n, value);\n}\n\n\ntemplate<typename T, typename ExecutionPolicy>\nvoid TestFillNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T>   h_data = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n  \n  size_t begin_offset = std::min<size_t>(1,n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)3, n) - begin_offset, (T) 0);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)3, n) - begin_offset, (T) 0);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(117, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)367, n) - begin_offset, (T) 1);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)367, n) - begin_offset, (T) 1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(8, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t)259, n) - begin_offset, (T) 2);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, std::min((size_t)259, n) - begin_offset, (T) 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  begin_offset = std::min<size_t>(3, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, h_data.size() - begin_offset, (T) 3);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin() + begin_offset, d_data.size() - begin_offset, (T) 3);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n  \n  thrust::fill_n(h_data.begin(), h_data.size(), (T) 4);\n\n  fill_n_kernel<<<1,1>>>(exec, d_data.begin(), d_data.size(), (T) 4);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  \n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate<typename T>\nvoid TestFillNDeviceSeq(size_t n)\n{\n  TestFillNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceSeq);\n\ntemplate<typename T>\nvoid TestFillNDeviceDevice(size_t n)\n{\n  TestFillNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceDevice);\n#endif\n\nvoid TestFillCudaStreams()\n{\n  thrust::device_vector<int> v(5);\n  v[0] = 0; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 4;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 4, 7);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 0);\n  ASSERT_EQUAL(v[1], 7);\n  ASSERT_EQUAL(v[2], 7);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 3, 8);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], 8);\n  ASSERT_EQUAL(v[3], 7);\n  ASSERT_EQUAL(v[4], 4);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 2, v.end(), 9);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 8);\n  ASSERT_EQUAL(v[1], 8);\n  ASSERT_EQUAL(v[2], 9);\n  ASSERT_EQUAL(v[3], 9);\n  ASSERT_EQUAL(v[4], 9);\n  \n  thrust::fill(thrust::cuda::par.on(s), v.begin(), v.end(), 1);\n  hipStreamSynchronize(s);\n  \n  ASSERT_EQUAL(v[0], 1);\n  ASSERT_EQUAL(v[1], 1);\n  ASSERT_EQUAL(v[2], 1);\n  ASSERT_EQUAL(v[3], 1);\n  ASSERT_EQUAL(v[4], 1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFillCudaStreams);\n\n", "source": "/kaggle/working/thrust/testing/cuda/fill.cu"}
{"input": "#include <unittest/testframework.h>\n#include <unittest/cuda/testframework.h>\n#include <thrust/system/cuda/memory.h>\n#include <cuda_runtime.h>\n#include <numeric>\n\n__global__ void dummy_kernel() {}\n\nbool binary_exists_for_current_device()\n{\n  // check against the dummy_kernel\n  // if we're unable to get the attributes, then\n  // we didn't compile a binary compatible with the current device\n  cudaFuncAttributes attr;\n  cudaError_t error = cudaFuncGetAttributes(&attr, dummy_kernel);\n\n  // clear the CUDA global error state if we just set it, so that\n  // check_cuda_error doesn't complain\n  if (cudaSuccess != error) (void)cudaGetLastError();\n\n  return cudaSuccess == error;\n}\n\nvoid list_devices(void)\n{\n  int deviceCount;\n  cudaGetDeviceCount(&deviceCount);\n  if(deviceCount == 0)\n  {\n    std::cout << \"There is no device supporting CUDA\" << std::endl;\n  }\n\n  int selected_device;\n  cudaGetDevice(&selected_device);\n\n  for (int dev = 0; dev < deviceCount; ++dev)\n  {\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, dev);\n\n    if(dev == 0)\n    {\n      if(deviceProp.major == 9999 && deviceProp.minor == 9999)\n        std::cout << \"There is no device supporting CUDA.\" << std::endl;\n      else if(deviceCount == 1)\n        std::cout << \"There is 1 device supporting CUDA\" << std:: endl;\n      else\n        std::cout << \"There are \" << deviceCount <<  \" devices supporting CUDA\" << std:: endl;\n    }\n\n    std::cout << \"\\nDevice \" << dev << \": \\\"\" << deviceProp.name << \"\\\"\";\n    if(dev == selected_device)\n      std::cout << \"  [SELECTED]\";\n    std::cout << std::endl;\n\n    std::cout << \"  Major revision number:                         \" << deviceProp.major << std::endl;\n    std::cout << \"  Minor revision number:                         \" << deviceProp.minor << std::endl;\n    std::cout << \"  Total amount of global memory:                 \" << deviceProp.totalGlobalMem << \" bytes\" << std::endl;\n  }\n  std::cout << std::endl;\n}\n\n// provide next, which c++03 doesn't have\ntemplate<typename Iterator> Iterator my_next(Iterator iter)\n{\n  return ++iter;\n}\n\n\nstd::vector<int> CUDATestDriver::target_devices(const ArgumentMap &kwargs)\n{\n  std::vector<int> result;\n\n  // by default, test all devices in the system (device id -1)\n  int device_id = kwargs.count(\"device\") ? atoi(kwargs.find(\"device\")->second.c_str()) : -1;\n\n  if(device_id < 0)\n  {\n    // target all devices in the system\n    int count = 0;\n    cudaGetDeviceCount(&count);\n\n    result.resize(count);\n    std::iota(result.begin(), result.end(), 0);\n  }\n  else\n  {\n    // target the specified device\n    result = std::vector<int>(1,device_id);\n  }\n\n  return result;\n}\n\nbool CUDATestDriver::check_cuda_error(bool concise)\n{\n  cudaError_t const error = cudaGetLastError();\n  if(cudaSuccess != error)\n  {\n    if(!concise)\n    {\n      std::cout << \"[ERROR] CUDA error detected before running tests: [\"\n                << std::string(cudaGetErrorName(error))\n                << \": \"\n                << std::string(cudaGetErrorString(error))\n                << \"]\" << std::endl;\n    }\n  }\n\n  return cudaSuccess != error;\n}\n\nbool CUDATestDriver::post_test_smoke_check(const UnitTest &test, bool concise)\n{\n  cudaError_t const error = cudaDeviceSynchronize();\n  if(cudaSuccess != error)\n  {\n    if(!concise)\n    {\n      std::cout << \"\\t[ERROR] CUDA error detected after running \" << test.name << \": [\"\n                << std::string(cudaGetErrorName(error))\n                << \": \"\n                << std::string(cudaGetErrorString(error))\n                << \"]\" << std::endl;\n    }\n  }\n\n  return cudaSuccess == error;\n}\n\nbool CUDATestDriver::run_tests(const ArgumentSet &args, const ArgumentMap &kwargs)\n{\n  bool verbose = kwargs.count(\"verbose\");\n  bool concise = kwargs.count(\"concise\");\n\n  if(verbose && concise)\n  {\n    std::cout << \"--verbose and --concise cannot be used together\" << std::endl;\n    exit(EXIT_FAILURE);\n  }\n\n  // check error status before doing anything\n  if(check_cuda_error(concise)) return false;\n\n  bool result = true;\n\n  if(kwargs.count(\"verbose\"))\n  {\n    list_devices();\n  }\n\n  // figure out which devices to target\n  std::vector<int> devices = target_devices(kwargs);\n\n  // target each device\n  for(std::vector<int>::iterator device = devices.begin();\n      device != devices.end();\n      ++device)\n  {\n    cudaDeviceSynchronize();\n\n    // set the device\n    cudaSetDevice(*device);\n\n    // check if a binary exists for this device\n    // if none exists, skip the device silently unless this is the only one we're targeting\n    if(devices.size() > 1 && !binary_exists_for_current_device())\n    {\n      // note which device we're skipping\n      cudaDeviceProp deviceProp;\n      cudaGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Skipping Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << std::endl;\n\n      continue;\n    }\n\n    if(!concise)\n    {\n      // note which device we're testing\n      cudaDeviceProp deviceProp;\n      cudaGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Testing Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << std::endl;\n    }\n\n    // check error status before running any tests\n    if(check_cuda_error(concise)) return false;\n\n    // run tests\n    result &= UnitTestDriver::run_tests(args, kwargs);\n\n    if(!concise && my_next(device) != devices.end())\n    {\n      // provide some separation between the output of separate tests\n      std::cout << std::endl;\n    }\n  }\n\n  return result;\n}\n\nint CUDATestDriver::current_device_architecture() const\n{\n  int current = -1;\n  cudaGetDevice(&current);\n  cudaDeviceProp deviceProp;\n  cudaGetDeviceProperties(&deviceProp, current);\n\n  return 100 * deviceProp.major + 10 * deviceProp.minor;\n}\n\nUnitTestDriver &driver_instance(thrust::system::cuda::tag)\n{\n  static CUDATestDriver s_instance;\n  return s_instance;\n}\n\n", "output": "#include <unittest/testframework.h>\n#include <unittest/cuda/testframework.h>\n#include <thrust/system/cuda/memory.h>\n#include <hip/hip_runtime.h>\n#include <numeric>\n\n__global__ void dummy_kernel() {}\n\nbool binary_exists_for_current_device()\n{\n  // check against the dummy_kernel\n  // if we're unable to get the attributes, then\n  // we didn't compile a binary compatible with the current device\n  hipFuncAttributes attr;\n  hipError_t error = hipFuncGetAttributes(&attr,reinterpret_cast<const void*>(dummy_kernel));\n\n  // clear the CUDA global error state if we just set it, so that\n  // check_cuda_error doesn't complain\n  if (hipSuccess != error) (void)hipGetLastError();\n\n  return hipSuccess == error;\n}\n\nvoid list_devices(void)\n{\n  int deviceCount;\n  hipGetDeviceCount(&deviceCount);\n  if(deviceCount == 0)\n  {\n    std::cout << \"There is no device supporting CUDA\" << std::endl;\n  }\n\n  int selected_device;\n  hipGetDevice(&selected_device);\n\n  for (int dev = 0; dev < deviceCount; ++dev)\n  {\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, dev);\n\n    if(dev == 0)\n    {\n      if(deviceProp.major == 9999 && deviceProp.minor == 9999)\n        std::cout << \"There is no device supporting CUDA.\" << std::endl;\n      else if(deviceCount == 1)\n        std::cout << \"There is 1 device supporting CUDA\" << std:: endl;\n      else\n        std::cout << \"There are \" << deviceCount <<  \" devices supporting CUDA\" << std:: endl;\n    }\n\n    std::cout << \"\\nDevice \" << dev << \": \\\"\" << deviceProp.name << \"\\\"\";\n    if(dev == selected_device)\n      std::cout << \"  [SELECTED]\";\n    std::cout << std::endl;\n\n    std::cout << \"  Major revision number:                         \" << deviceProp.major << std::endl;\n    std::cout << \"  Minor revision number:                         \" << deviceProp.minor << std::endl;\n    std::cout << \"  Total amount of global memory:                 \" << deviceProp.totalGlobalMem << \" bytes\" << std::endl;\n  }\n  std::cout << std::endl;\n}\n\n// provide next, which c++03 doesn't have\ntemplate<typename Iterator> Iterator my_next(Iterator iter)\n{\n  return ++iter;\n}\n\n\nstd::vector<int> CUDATestDriver::target_devices(const ArgumentMap &kwargs)\n{\n  std::vector<int> result;\n\n  // by default, test all devices in the system (device id -1)\n  int device_id = kwargs.count(\"device\") ? atoi(kwargs.find(\"device\")->second.c_str()) : -1;\n\n  if(device_id < 0)\n  {\n    // target all devices in the system\n    int count = 0;\n    hipGetDeviceCount(&count);\n\n    result.resize(count);\n    std::iota(result.begin(), result.end(), 0);\n  }\n  else\n  {\n    // target the specified device\n    result = std::vector<int>(1,device_id);\n  }\n\n  return result;\n}\n\nbool CUDATestDriver::check_cuda_error(bool concise)\n{\n  hipError_t const error = hipGetLastError();\n  if(hipSuccess != error)\n  {\n    if(!concise)\n    {\n      std::cout << \"[ERROR] CUDA error detected before running tests: [\"\n                << std::string(hipGetErrorName(error))\n                << \": \"\n                << std::string(hipGetErrorString(error))\n                << \"]\" << std::endl;\n    }\n  }\n\n  return hipSuccess != error;\n}\n\nbool CUDATestDriver::post_test_smoke_check(const UnitTest &test, bool concise)\n{\n  hipError_t const error = hipDeviceSynchronize();\n  if(hipSuccess != error)\n  {\n    if(!concise)\n    {\n      std::cout << \"\\t[ERROR] CUDA error detected after running \" << test.name << \": [\"\n                << std::string(hipGetErrorName(error))\n                << \": \"\n                << std::string(hipGetErrorString(error))\n                << \"]\" << std::endl;\n    }\n  }\n\n  return hipSuccess == error;\n}\n\nbool CUDATestDriver::run_tests(const ArgumentSet &args, const ArgumentMap &kwargs)\n{\n  bool verbose = kwargs.count(\"verbose\");\n  bool concise = kwargs.count(\"concise\");\n\n  if(verbose && concise)\n  {\n    std::cout << \"--verbose and --concise cannot be used together\" << std::endl;\n    exit(EXIT_FAILURE);\n  }\n\n  // check error status before doing anything\n  if(check_cuda_error(concise)) return false;\n\n  bool result = true;\n\n  if(kwargs.count(\"verbose\"))\n  {\n    list_devices();\n  }\n\n  // figure out which devices to target\n  std::vector<int> devices = target_devices(kwargs);\n\n  // target each device\n  for(std::vector<int>::iterator device = devices.begin();\n      device != devices.end();\n      ++device)\n  {\n    hipDeviceSynchronize();\n\n    // set the device\n    hipSetDevice(*device);\n\n    // check if a binary exists for this device\n    // if none exists, skip the device silently unless this is the only one we're targeting\n    if(devices.size() > 1 && !binary_exists_for_current_device())\n    {\n      // note which device we're skipping\n      hipDeviceProp_t deviceProp;\n      hipGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Skipping Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << std::endl;\n\n      continue;\n    }\n\n    if(!concise)\n    {\n      // note which device we're testing\n      hipDeviceProp_t deviceProp;\n      hipGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Testing Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << std::endl;\n    }\n\n    // check error status before running any tests\n    if(check_cuda_error(concise)) return false;\n\n    // run tests\n    result &= UnitTestDriver::run_tests(args, kwargs);\n\n    if(!concise && my_next(device) != devices.end())\n    {\n      // provide some separation between the output of separate tests\n      std::cout << std::endl;\n    }\n  }\n\n  return result;\n}\n\nint CUDATestDriver::current_device_architecture() const\n{\n  int current = -1;\n  hipGetDevice(&current);\n  hipDeviceProp_t deviceProp;\n  hipGetDeviceProperties(&deviceProp, current);\n\n  return 100 * deviceProp.major + 10 * deviceProp.minor;\n}\n\nUnitTestDriver &driver_instance(thrust::system::cuda::tag)\n{\n  static CUDATestDriver s_instance;\n  return s_instance;\n}\n\n", "source": "/kaggle/working/thrust/testing/unittest/cuda/testframework.cu"}
{"input": "#include <stdio.h>\n\nvoid helloCPU()\n{\n  printf(\"Hello from the CPU.\\n\");\n}\n\n/*\n * The addition of `__global__` signifies that this function\n * should be launced on the GPU.\n */\n\n__global__ void helloGPU()\n{\n  printf(\"Hello from the GPU.\\n\");\n}\n\nint main()\n{\n  helloCPU();\n\n\n  /*\n   * Add an execution configuration with the <<<...>>> syntax\n   * will launch this function as a kernel on the GPU.\n   */\n\n  helloGPU<<<1, 1>>>();\n\n  /*\n   * `cudaDeviceSynchronize` will block the CPU stream until\n   * all GPU kernels have completed.\n   */\n\n  cudaDeviceSynchronize();\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\nvoid helloCPU()\n{\n  printf(\"Hello from the CPU.\\n\");\n}\n\n/*\n * The addition of `__global__` signifies that this function\n * should be launced on the GPU.\n */\n\n__global__ void helloGPU()\n{\n  printf(\"Hello from the GPU.\\n\");\n}\n\nint main()\n{\n  helloCPU();\n\n\n  /*\n   * Add an execution configuration with the <<<...>>> syntax\n   * will launch this function as a kernel on the GPU.\n   */\n\n  helloGPU<<<1, 1>>>();\n\n  /*\n   * `cudaDeviceSynchronize` will block the CPU stream until\n   * all GPU kernels have completed.\n   */\n\n  hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-hello-gpu-solution.cu"}
{"input": "#include <stdio.h>\n#include <math.h>\n\n// Simple define to index into a 1D array from 2D space\n#define I2D(num, c, r) ((r)*(num)+(c))\n\n__global__\nvoid step_kernel_mod(int ni, int nj, float fact, float* temp_in, float* temp_out)\n{\n  int i00, im10, ip10, i0m1, i0p1;\n  float d2tdx2, d2tdy2;\n\n  int j = blockIdx.x * blockDim.x + threadIdx.x;\n  int i = blockIdx.y * blockDim.y + threadIdx.y;\n\n  // loop over all points in domain (except boundary)\n  if (j > 0 && i > 0 && j < nj-1 && i < ni-1) {\n    // find indices into linear memory\n    // for central point and neighbours\n    i00 = I2D(ni, i, j);\n    im10 = I2D(ni, i-1, j);\n    ip10 = I2D(ni, i+1, j);\n    i0m1 = I2D(ni, i, j-1);\n    i0p1 = I2D(ni, i, j+1);\n\n    // evaluate derivatives\n    d2tdx2 = temp_in[im10]-2*temp_in[i00]+temp_in[ip10];\n    d2tdy2 = temp_in[i0m1]-2*temp_in[i00]+temp_in[i0p1];\n\n    // update temperatures\n    temp_out[i00] = temp_in[i00]+fact*(d2tdx2 + d2tdy2);\n  }\n}\n\nvoid step_kernel_ref(int ni, int nj, float fact, float* temp_in, float* temp_out)\n{\n  int i00, im10, ip10, i0m1, i0p1;\n  float d2tdx2, d2tdy2;\n\n\n  // loop over all points in domain (except boundary)\n  for ( int j=1; j < nj-1; j++ ) {\n    for ( int i=1; i < ni-1; i++ ) {\n      // find indices into linear memory\n      // for central point and neighbours\n      i00 = I2D(ni, i, j);\n      im10 = I2D(ni, i-1, j);\n      ip10 = I2D(ni, i+1, j);\n      i0m1 = I2D(ni, i, j-1);\n      i0p1 = I2D(ni, i, j+1);\n\n      // evaluate derivatives\n      d2tdx2 = temp_in[im10]-2*temp_in[i00]+temp_in[ip10];\n      d2tdy2 = temp_in[i0m1]-2*temp_in[i00]+temp_in[i0p1];\n\n      // update temperatures\n      temp_out[i00] = temp_in[i00]+fact*(d2tdx2 + d2tdy2);\n    }\n  }\n}\n\nint main()\n{\n  int istep;\n  int nstep = 200; // number of time steps\n\n  // Specify our 2D dimensions\n  const int ni = 200;\n  const int nj = 100;\n  float tfac = 8.418e-5; // thermal diffusivity of silver\n\n  float *temp1_ref, *temp2_ref, *temp1, *temp2, *temp_tmp;\n\n  const int size = ni * nj * sizeof(float);\n\n  temp1_ref = (float*)malloc(size);\n  temp2_ref = (float*)malloc(size);\n  cudaMallocManaged(&temp1, size);\n  cudaMallocManaged(&temp2, size);\n\n  // Initialize with random data\n  for( int i = 0; i < ni*nj; ++i) {\n    temp1_ref[i] = temp2_ref[i] = temp1[i] = temp2[i] = (float)rand()/(float)(RAND_MAX/100.0f);\n  }\n\n  // Execute the CPU-only reference version\n  for (istep=0; istep < nstep; istep++) {\n    step_kernel_ref(ni, nj, tfac, temp1_ref, temp2_ref);\n\n    // swap the temperature pointers\n    temp_tmp = temp1_ref;\n    temp1_ref = temp2_ref;\n    temp2_ref= temp_tmp;\n  }\n\n  dim3 tblocks(32, 16, 1);\n  dim3 grid((nj/tblocks.x)+1, (ni/tblocks.y)+1, 1);\n  cudaError_t ierrSync, ierrAsync;\n\n  // Execute the modified version using same data\n  for (istep=0; istep < nstep; istep++) {\n    step_kernel_mod<<< grid, tblocks >>>(ni, nj, tfac, temp1, temp2);\n\n    ierrSync = cudaGetLastError();\n    ierrAsync = cudaDeviceSynchronize(); // Wait for the GPU to finish\n    if (ierrSync != cudaSuccess) { printf(\"Sync error: %s\\n\", cudaGetErrorString(ierrSync)); }\n    if (ierrAsync != cudaSuccess) { printf(\"Async error: %s\\n\", cudaGetErrorString(ierrAsync)); }\n\n    // swap the temperature pointers\n    temp_tmp = temp1;\n    temp1 = temp2;\n    temp2= temp_tmp;\n  }\n\n  float maxError = 0;\n  // Output should always be stored in the temp1 and temp1_ref at this point\n  for( int i = 0; i < ni*nj; ++i ) {\n    if (abs(temp1[i]-temp1_ref[i]) > maxError) { maxError = abs(temp1[i]-temp1_ref[i]); }\n  }\n\n  // Check and see if our maxError is greater than an error bound\n  if (maxError > 0.0005f)\n    printf(\"Problem! The Max Error of %.5f is NOT within acceptable bounds.\\n\", maxError);\n  else\n    printf(\"The Max Error of %.5f is within acceptable bounds.\\n\", maxError);\n\n  free( temp1_ref );\n  free( temp2_ref );\n  cudaFree( temp1 );\n  cudaFree( temp2 );\n\n  return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <math.h>\n\n// Simple define to index into a 1D array from 2D space\n#define I2D(num, c, r) ((r)*(num)+(c))\n\n__global__\nvoid step_kernel_mod(int ni, int nj, float fact, float* temp_in, float* temp_out)\n{\n  int i00, im10, ip10, i0m1, i0p1;\n  float d2tdx2, d2tdy2;\n\n  int j = blockIdx.x * blockDim.x + threadIdx.x;\n  int i = blockIdx.y * blockDim.y + threadIdx.y;\n\n  // loop over all points in domain (except boundary)\n  if (j > 0 && i > 0 && j < nj-1 && i < ni-1) {\n    // find indices into linear memory\n    // for central point and neighbours\n    i00 = I2D(ni, i, j);\n    im10 = I2D(ni, i-1, j);\n    ip10 = I2D(ni, i+1, j);\n    i0m1 = I2D(ni, i, j-1);\n    i0p1 = I2D(ni, i, j+1);\n\n    // evaluate derivatives\n    d2tdx2 = temp_in[im10]-2*temp_in[i00]+temp_in[ip10];\n    d2tdy2 = temp_in[i0m1]-2*temp_in[i00]+temp_in[i0p1];\n\n    // update temperatures\n    temp_out[i00] = temp_in[i00]+fact*(d2tdx2 + d2tdy2);\n  }\n}\n\nvoid step_kernel_ref(int ni, int nj, float fact, float* temp_in, float* temp_out)\n{\n  int i00, im10, ip10, i0m1, i0p1;\n  float d2tdx2, d2tdy2;\n\n\n  // loop over all points in domain (except boundary)\n  for ( int j=1; j < nj-1; j++ ) {\n    for ( int i=1; i < ni-1; i++ ) {\n      // find indices into linear memory\n      // for central point and neighbours\n      i00 = I2D(ni, i, j);\n      im10 = I2D(ni, i-1, j);\n      ip10 = I2D(ni, i+1, j);\n      i0m1 = I2D(ni, i, j-1);\n      i0p1 = I2D(ni, i, j+1);\n\n      // evaluate derivatives\n      d2tdx2 = temp_in[im10]-2*temp_in[i00]+temp_in[ip10];\n      d2tdy2 = temp_in[i0m1]-2*temp_in[i00]+temp_in[i0p1];\n\n      // update temperatures\n      temp_out[i00] = temp_in[i00]+fact*(d2tdx2 + d2tdy2);\n    }\n  }\n}\n\nint main()\n{\n  int istep;\n  int nstep = 200; // number of time steps\n\n  // Specify our 2D dimensions\n  const int ni = 200;\n  const int nj = 100;\n  float tfac = 8.418e-5; // thermal diffusivity of silver\n\n  float *temp1_ref, *temp2_ref, *temp1, *temp2, *temp_tmp;\n\n  const int size = ni * nj * sizeof(float);\n\n  temp1_ref = (float*)malloc(size);\n  temp2_ref = (float*)malloc(size);\n  hipMallocManaged(&temp1, size);\n  hipMallocManaged(&temp2, size);\n\n  // Initialize with random data\n  for( int i = 0; i < ni*nj; ++i) {\n    temp1_ref[i] = temp2_ref[i] = temp1[i] = temp2[i] = (float)rand()/(float)(RAND_MAX/100.0f);\n  }\n\n  // Execute the CPU-only reference version\n  for (istep=0; istep < nstep; istep++) {\n    step_kernel_ref(ni, nj, tfac, temp1_ref, temp2_ref);\n\n    // swap the temperature pointers\n    temp_tmp = temp1_ref;\n    temp1_ref = temp2_ref;\n    temp2_ref= temp_tmp;\n  }\n\n  dim3 tblocks(32, 16, 1);\n  dim3 grid((nj/tblocks.x)+1, (ni/tblocks.y)+1, 1);\n  hipError_t ierrSync, ierrAsync;\n\n  // Execute the modified version using same data\n  for (istep=0; istep < nstep; istep++) {\n    step_kernel_mod<<< grid, tblocks >>>(ni, nj, tfac, temp1, temp2);\n\n    ierrSync = hipGetLastError();\n    ierrAsync = hipDeviceSynchronize(); // Wait for the GPU to finish\n    if (ierrSync != hipSuccess) { printf(\"Sync error: %s\\n\", hipGetErrorString(ierrSync)); }\n    if (ierrAsync != hipSuccess) { printf(\"Async error: %s\\n\", hipGetErrorString(ierrAsync)); }\n\n    // swap the temperature pointers\n    temp_tmp = temp1;\n    temp1 = temp2;\n    temp2= temp_tmp;\n  }\n\n  float maxError = 0;\n  // Output should always be stored in the temp1 and temp1_ref at this point\n  for( int i = 0; i < ni*nj; ++i ) {\n    if (abs(temp1[i]-temp1_ref[i]) > maxError) { maxError = abs(temp1[i]-temp1_ref[i]); }\n  }\n\n  // Check and see if our maxError is greater than an error bound\n  if (maxError > 0.0005f)\n    printf(\"Problem! The Max Error of %.5f is NOT within acceptable bounds.\\n\", maxError);\n  else\n    printf(\"The Max Error of %.5f is within acceptable bounds.\\n\", maxError);\n\n  free( temp1_ref );\n  free( temp2_ref );\n  hipFree( temp1 );\n  hipFree( temp2 );\n\n  return 0;\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-heat-conduction-solution.cu"}
{"input": "#include <stdio.h>\n\n#define N  64\n\n__global__ void matrixMulGPU( int * a, int * b, int * c )\n{\n  /*\n   * Build out this kernel.\n   */\n}\n\n/*\n * This CPU function already works, and will run to create a solution matrix\n * against which to verify your work building out the matrixMulGPU kernel.\n */\n\nvoid matrixMulCPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      val = 0;\n      for ( int k = 0; k < N; ++k )\n        val += a[row * N + k] * b[k * N + col];\n      c[row * N + col] = val;\n    }\n}\n\nint main()\n{\n  int *a, *b, *c_cpu, *c_gpu; // Allocate a solution matrix for both the CPU and the GPU operations\n\n  int size = N * N * sizeof (int); // Number of bytes of an N x N matrix\n\n  // Allocate memory\n  cudaMallocManaged (&a, size);\n  cudaMallocManaged (&b, size);\n  cudaMallocManaged (&c_cpu, size);\n  cudaMallocManaged (&c_gpu, size);\n\n  // Initialize memory; create 2D matrices\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      a[row*N + col] = row;\n      b[row*N + col] = col+2;\n      c_cpu[row*N + col] = 0;\n      c_gpu[row*N + col] = 0;\n    }\n\n  /*\n   * Assign `threads_per_block` and `number_of_blocks` 2D values\n   * that can be used in matrixMulGPU above.\n   */\n\n  dim3 threads_per_block;\n  dim3 number_of_blocks;\n\n  matrixMulGPU <<< number_of_blocks, threads_per_block >>> ( a, b, c_gpu );\n\n  cudaDeviceSynchronize();\n\n  // Call the CPU version to check our work\n  matrixMulCPU( a, b, c_cpu );\n\n  // Compare the two answers to make sure they are equal\n  bool error = false;\n  for( int row = 0; row < N && !error; ++row )\n    for( int col = 0; col < N && !error; ++col )\n      if (c_cpu[row * N + col] != c_gpu[row * N + col])\n      {\n        printf(\"FOUND ERROR at c[%d][%d]\\n\", row, col);\n        error = true;\n        break;\n      }\n  if (!error)\n    printf(\"Success!\\n\");\n\n  // Free all our allocated memory\n  cudaFree(a); cudaFree(b);\n  cudaFree( c_cpu ); cudaFree( c_gpu );\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#define N  64\n\n__global__ void matrixMulGPU( int * a, int * b, int * c )\n{\n  /*\n   * Build out this kernel.\n   */\n}\n\n/*\n * This CPU function already works, and will run to create a solution matrix\n * against which to verify your work building out the matrixMulGPU kernel.\n */\n\nvoid matrixMulCPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      val = 0;\n      for ( int k = 0; k < N; ++k )\n        val += a[row * N + k] * b[k * N + col];\n      c[row * N + col] = val;\n    }\n}\n\nint main()\n{\n  int *a, *b, *c_cpu, *c_gpu; // Allocate a solution matrix for both the CPU and the GPU operations\n\n  int size = N * N * sizeof (int); // Number of bytes of an N x N matrix\n\n  // Allocate memory\n  hipMallocManaged (&a, size);\n  hipMallocManaged (&b, size);\n  hipMallocManaged (&c_cpu, size);\n  hipMallocManaged (&c_gpu, size);\n\n  // Initialize memory; create 2D matrices\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      a[row*N + col] = row;\n      b[row*N + col] = col+2;\n      c_cpu[row*N + col] = 0;\n      c_gpu[row*N + col] = 0;\n    }\n\n  /*\n   * Assign `threads_per_block` and `number_of_blocks` 2D values\n   * that can be used in matrixMulGPU above.\n   */\n\n  dim3 threads_per_block;\n  dim3 number_of_blocks;\n\n  matrixMulGPU <<< number_of_blocks, threads_per_block >>> ( a, b, c_gpu );\n\n  hipDeviceSynchronize();\n\n  // Call the CPU version to check our work\n  matrixMulCPU( a, b, c_cpu );\n\n  // Compare the two answers to make sure they are equal\n  bool error = false;\n  for( int row = 0; row < N && !error; ++row )\n    for( int col = 0; col < N && !error; ++col )\n      if (c_cpu[row * N + col] != c_gpu[row * N + col])\n      {\n        printf(\"FOUND ERROR at c[%d][%d]\\n\", row, col);\n        error = true;\n        break;\n      }\n  if (!error)\n    printf(\"Success!\\n\");\n\n  // Free all our allocated memory\n  hipFree(a); hipFree(b);\n  hipFree( c_cpu ); hipFree( c_gpu );\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-matrix-multiply-2d.cu"}
{"input": "#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n\n  /*\n   * Use a grid-stride loop so each thread does work\n   * on more than one element in the array.\n   */\n\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n\n  for (int i = idx; i < N; i += stride)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 10000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n  cudaMallocManaged(&a, size);\n\n  init(a, N);\n\n  size_t threads_per_block = 256;\n  size_t number_of_blocks = 32;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n  cudaDeviceSynchronize();\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  cudaFree(a);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n\n  /*\n   * Use a grid-stride loop so each thread does work\n   * on more than one element in the array.\n   */\n\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n\n  for (int i = idx; i < N; i += stride)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 10000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n  hipMallocManaged(&a, size);\n\n  init(a, N);\n\n  size_t threads_per_block = 256;\n  size_t number_of_blocks = 32;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n  hipDeviceSynchronize();\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  hipFree(a);\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/03-grid-stride-double-solution.cu"}
{"input": "#include <stdio.h>\n\n\n/*\n * Notice the absence of the previously expected argument `N`.\n */\n\n__global__ void loop()\n{\n  /*\n   * This kernel does the work of only 1 iteration\n   * of the original for loop. Indication of which\n   * \"iteration\" is being executed by this kernel is\n   * still available via `threadIdx.x`.\n   */\n\n  printf(\"This is iteration number %d\\n\", threadIdx.x);\n}\n\nint main()\n{\n  /*\n   * It is the execution context that sets how many \"iterations\"\n   * of the \"loop\" will be done.\n   */\n\n  loop<<<1, 10>>>();\n  cudaDeviceSynchronize();\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n\n/*\n * Notice the absence of the previously expected argument `N`.\n */\n\n__global__ void loop()\n{\n  /*\n   * This kernel does the work of only 1 iteration\n   * of the original for loop. Indication of which\n   * \"iteration\" is being executed by this kernel is\n   * still available via `threadIdx.x`.\n   */\n\n  printf(\"This is iteration number %d\\n\", threadIdx.x);\n}\n\nint main()\n{\n  /*\n   * It is the execution context that sets how many \"iterations\"\n   * of the \"loop\" will be done.\n   */\n\n  loop<<<1, 10>>>();\n  hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-single-block-loop-solution.cu"}
{"input": "#include <stdio.h>\n\n__global__ void printSuccessForCorrectExecutionConfiguration()\n{\n\n  if(threadIdx.x == 1023 && blockIdx.x == 255)\n  {\n    printf(\"Success!\\n\");\n  }\n}\n\nint main()\n{\n  /*\n   * This is one possible execution context that will make\n   * the kernel launch print its success message.\n   */\n\n  printSuccessForCorrectExecutionConfiguration<<<256, 1024>>>();\n\n  /*\n   * Don't forget kernel execution is asynchronous and you must\n   * sync on its completion.\n   */\n\n  cudaDeviceSynchronize();\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n__global__ void printSuccessForCorrectExecutionConfiguration()\n{\n\n  if(threadIdx.x == 1023 && blockIdx.x == 255)\n  {\n    printf(\"Success!\\n\");\n  }\n}\n\nint main()\n{\n  /*\n   * This is one possible execution context that will make\n   * the kernel launch print its success message.\n   */\n\n  printSuccessForCorrectExecutionConfiguration<<<256, 1024>>>();\n\n  /*\n   * Don't forget kernel execution is asynchronous and you must\n   * sync on its completion.\n   */\n\n  hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-thread-and-block-idx-solution.cu"}
{"input": "#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n  int i;\n  i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < N)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 1000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n\n  /*\n   * Use `cudaMallocManaged` to allocate pointer `a` available\n   * on both the host and the device.\n   */\n\n  cudaMallocManaged(&a, size);\n\n  init(a, N);\n\n  size_t threads_per_block = 256;\n  size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n  cudaDeviceSynchronize();\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  /*\n   * Use `cudaFree` to free memory allocated\n   * with `cudaMallocManaged`.\n   */\n\n  cudaFree(a);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n  int i;\n  i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < N)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 1000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n\n  /*\n   * Use `cudaMallocManaged` to allocate pointer `a` available\n   * on both the host and the device.\n   */\n\n  hipMallocManaged(&a, size);\n\n  init(a, N);\n\n  size_t threads_per_block = 256;\n  size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n  hipDeviceSynchronize();\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  /*\n   * Use `cudaFree` to free memory allocated\n   * with `cudaMallocManaged`.\n   */\n\n  hipFree(a);\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-double-elements-solution.cu"}
{"input": "#include <stdio.h>\n\n#define N  64\n\n__global__ void matrixMulGPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  int row = blockIdx.x * blockDim.x + threadIdx.x;\n  int col = blockIdx.y * blockDim.y + threadIdx.y;\n\n  if (row < N && col < N)\n  {\n    for ( int k = 0; k < N; ++k )\n      val += a[row * N + k] * b[k * N + col];\n    c[row * N + col] = val;\n  }\n}\n\nvoid matrixMulCPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      val = 0;\n      for ( int k = 0; k < N; ++k )\n        val += a[row * N + k] * b[k * N + col];\n      c[row * N + col] = val;\n    }\n}\n\nint main()\n{\n  int *a, *b, *c_cpu, *c_gpu;\n\n  int size = N * N * sizeof (int); // Number of bytes of an N x N matrix\n\n  // Allocate memory\n  cudaMallocManaged (&a, size);\n  cudaMallocManaged (&b, size);\n  cudaMallocManaged (&c_cpu, size);\n  cudaMallocManaged (&c_gpu, size);\n\n  // Initialize memory\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      a[row*N + col] = row;\n      b[row*N + col] = col+2;\n      c_cpu[row*N + col] = 0;\n      c_gpu[row*N + col] = 0;\n    }\n\n  dim3 threads_per_block (16, 16, 1); // A 16 x 16 block threads\n  dim3 number_of_blocks ((N / threads_per_block.x) + 1, (N / threads_per_block.y) + 1, 1);\n\n  matrixMulGPU <<< number_of_blocks, threads_per_block >>> ( a, b, c_gpu );\n\n  cudaDeviceSynchronize(); // Wait for the GPU to finish before proceeding\n\n  // Call the CPU version to check our work\n  matrixMulCPU( a, b, c_cpu );\n\n  // Compare the two answers to make sure they are equal\n  bool error = false;\n  for( int row = 0; row < N && !error; ++row )\n    for( int col = 0; col < N && !error; ++col )\n      if (c_cpu[row * N + col] != c_gpu[row * N + col])\n      {\n        printf(\"FOUND ERROR at c[%d][%d]\\n\", row, col);\n        error = true;\n        break;\n      }\n  if (!error)\n    printf(\"Success!\\n\");\n\n  // Free all our allocated memory\n  cudaFree(a); cudaFree(b);\n  cudaFree( c_cpu ); cudaFree( c_gpu );\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#define N  64\n\n__global__ void matrixMulGPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  int row = blockIdx.x * blockDim.x + threadIdx.x;\n  int col = blockIdx.y * blockDim.y + threadIdx.y;\n\n  if (row < N && col < N)\n  {\n    for ( int k = 0; k < N; ++k )\n      val += a[row * N + k] * b[k * N + col];\n    c[row * N + col] = val;\n  }\n}\n\nvoid matrixMulCPU( int * a, int * b, int * c )\n{\n  int val = 0;\n\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      val = 0;\n      for ( int k = 0; k < N; ++k )\n        val += a[row * N + k] * b[k * N + col];\n      c[row * N + col] = val;\n    }\n}\n\nint main()\n{\n  int *a, *b, *c_cpu, *c_gpu;\n\n  int size = N * N * sizeof (int); // Number of bytes of an N x N matrix\n\n  // Allocate memory\n  hipMallocManaged (&a, size);\n  hipMallocManaged (&b, size);\n  hipMallocManaged (&c_cpu, size);\n  hipMallocManaged (&c_gpu, size);\n\n  // Initialize memory\n  for( int row = 0; row < N; ++row )\n    for( int col = 0; col < N; ++col )\n    {\n      a[row*N + col] = row;\n      b[row*N + col] = col+2;\n      c_cpu[row*N + col] = 0;\n      c_gpu[row*N + col] = 0;\n    }\n\n  dim3 threads_per_block (16, 16, 1); // A 16 x 16 block threads\n  dim3 number_of_blocks ((N / threads_per_block.x) + 1, (N / threads_per_block.y) + 1, 1);\n\n  matrixMulGPU <<< number_of_blocks, threads_per_block >>> ( a, b, c_gpu );\n\n  hipDeviceSynchronize(); // Wait for the GPU to finish before proceeding\n\n  // Call the CPU version to check our work\n  matrixMulCPU( a, b, c_cpu );\n\n  // Compare the two answers to make sure they are equal\n  bool error = false;\n  for( int row = 0; row < N && !error; ++row )\n    for( int col = 0; col < N && !error; ++col )\n      if (c_cpu[row * N + col] != c_gpu[row * N + col])\n      {\n        printf(\"FOUND ERROR at c[%d][%d]\\n\", row, col);\n        error = true;\n        break;\n      }\n  if (!error)\n    printf(\"Success!\\n\");\n\n  // Free all our allocated memory\n  hipFree(a); hipFree(b);\n  hipFree( c_cpu ); hipFree( c_gpu );\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-matrix-multiply-2d-solution.cu"}
{"input": "#include <stdio.h>\n\n__global__ void loop()\n{\n  /*\n   * This idiomatic expression gives each thread\n   * a unique index within the entire grid.\n   */\n\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  printf(\"%d\\n\", i);\n}\n\nint main()\n{\n  /*\n   * Additional execution configurations that would\n   * work and meet the exercises contraints are:\n   *\n   * <<<5, 2>>>\n   * <<<10, 1>>>\n   */\n\n  loop<<<2, 5>>>();\n  cudaDeviceSynchronize();\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n__global__ void loop()\n{\n  /*\n   * This idiomatic expression gives each thread\n   * a unique index within the entire grid.\n   */\n\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  printf(\"%d\\n\", i);\n}\n\nint main()\n{\n  /*\n   * Additional execution configurations that would\n   * work and meet the exercises contraints are:\n   *\n   * <<<5, 2>>>\n   * <<<10, 1>>>\n   */\n\n  loop<<<2, 5>>>();\n  hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/02-multi-block-loop-solution.cu"}
{"input": "#include <stdio.h>\n#include <assert.h>\n\ninline cudaError_t checkCuda(cudaError_t result)\n{\n  if (result != cudaSuccess) {\n    fprintf(stderr, \"CUDA Runtime Error: %s\\n\", cudaGetErrorString(result));\n    assert(result == cudaSuccess);\n  }\n  return result;\n}\n\nvoid initWith(float num, float *a, int N)\n{\n  for(int i = 0; i < N; ++i)\n  {\n    a[i] = num;\n  }\n}\n\n__global__\nvoid addVectorsInto(float *result, float *a, float *b, int N)\n{\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for(int i = index; i < N; i += stride)\n  {\n    result[i] = a[i] + b[i];\n  }\n}\n\nvoid checkElementsAre(float target, float *array, int N)\n{\n  for(int i = 0; i < N; i++)\n  {\n    if(array[i] != target)\n    {\n      printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n      exit(1);\n    }\n  }\n}\n\nint main()\n{\n  const int N = 2<<20;\n  size_t size = N * sizeof(float);\n\n  float *a;\n  float *b;\n  float *c;\n\n  checkCuda( cudaMallocManaged(&a, size) );\n  checkCuda( cudaMallocManaged(&b, size) );\n  checkCuda( cudaMallocManaged(&c, size) );\n\n  initWith(3, a, N);\n  initWith(4, b, N);\n  initWith(0, c, N);\n\n  size_t threadsPerBlock;\n  size_t numberOfBlocks;\n\n  threadsPerBlock = 256;\n  numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n  addVectorsInto<<<numberOfBlocks, threadsPerBlock>>>(c, a, b, N);\n\n  checkCuda( cudaGetLastError() );\n  checkCuda( cudaDeviceSynchronize() );\n\n  checkElementsAre(7, c, N);\n\n  checkCuda( cudaFree(a) );\n  checkCuda( cudaFree(b) );\n  checkCuda( cudaFree(c) );\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <assert.h>\n\ninline hipError_t checkCuda(hipError_t result)\n{\n  if (result != hipSuccess) {\n    fprintf(stderr, \"CUDA Runtime Error: %s\\n\", hipGetErrorString(result));\n    assert(result == hipSuccess);\n  }\n  return result;\n}\n\nvoid initWith(float num, float *a, int N)\n{\n  for(int i = 0; i < N; ++i)\n  {\n    a[i] = num;\n  }\n}\n\n__global__\nvoid addVectorsInto(float *result, float *a, float *b, int N)\n{\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for(int i = index; i < N; i += stride)\n  {\n    result[i] = a[i] + b[i];\n  }\n}\n\nvoid checkElementsAre(float target, float *array, int N)\n{\n  for(int i = 0; i < N; i++)\n  {\n    if(array[i] != target)\n    {\n      printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n      exit(1);\n    }\n  }\n}\n\nint main()\n{\n  const int N = 2<<20;\n  size_t size = N * sizeof(float);\n\n  float *a;\n  float *b;\n  float *c;\n\n  checkCuda( hipMallocManaged(&a, size) );\n  checkCuda( hipMallocManaged(&b, size) );\n  checkCuda( hipMallocManaged(&c, size) );\n\n  initWith(3, a, N);\n  initWith(4, b, N);\n  initWith(0, c, N);\n\n  size_t threadsPerBlock;\n  size_t numberOfBlocks;\n\n  threadsPerBlock = 256;\n  numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n  addVectorsInto<<<numberOfBlocks, threadsPerBlock>>>(c, a, b, N);\n\n  checkCuda( hipGetLastError() );\n  checkCuda( hipDeviceSynchronize() );\n\n  checkElementsAre(7, c, N);\n\n  checkCuda( hipFree(a) );\n  checkCuda( hipFree(b) );\n  checkCuda( hipFree(c) );\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-add-error-handling-solution (1).cu"}
{"input": "#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n\n  /*\n   * The previous code (now commented out) attempted\n   * to access an element outside the range of `a`.\n   */\n\n  // for (int i = idx; i < N + stride; i += stride)\n  for (int i = idx; i < N; i += stride)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 10000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n  cudaMallocManaged(&a, size);\n\n  init(a, N);\n\n  /*\n   * The previous code (now commented out) attempted to launch\n   * the kernel with more than the maximum number of threads per\n   * block, which is 1024.\n   */\n\n  size_t threads_per_block = 1024;\n  /* size_t threads_per_block = 2048; */\n  size_t number_of_blocks = 32;\n\n  cudaError_t syncErr, asyncErr;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n\n  /*\n   * Catch errors for both the kernel launch above and any\n   * errors that occur during the asynchronous `doubleElements`\n   * kernel execution.\n   */\n\n  syncErr = cudaGetLastError();\n  asyncErr = cudaDeviceSynchronize();\n\n  /*\n   * Print errors should they exist.\n   */\n\n  if (syncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(syncErr));\n  if (asyncErr != cudaSuccess) printf(\"Error: %s\\n\", cudaGetErrorString(asyncErr));\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  cudaFree(a);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\nvoid init(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    a[i] = i;\n  }\n}\n\n__global__\nvoid doubleElements(int *a, int N)\n{\n\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n\n  /*\n   * The previous code (now commented out) attempted\n   * to access an element outside the range of `a`.\n   */\n\n  // for (int i = idx; i < N + stride; i += stride)\n  for (int i = idx; i < N; i += stride)\n  {\n    a[i] *= 2;\n  }\n}\n\nbool checkElementsAreDoubled(int *a, int N)\n{\n  int i;\n  for (i = 0; i < N; ++i)\n  {\n    if (a[i] != i*2) return false;\n  }\n  return true;\n}\n\nint main()\n{\n  int N = 10000;\n  int *a;\n\n  size_t size = N * sizeof(int);\n  hipMallocManaged(&a, size);\n\n  init(a, N);\n\n  /*\n   * The previous code (now commented out) attempted to launch\n   * the kernel with more than the maximum number of threads per\n   * block, which is 1024.\n   */\n\n  size_t threads_per_block = 1024;\n  /* size_t threads_per_block = 2048; */\n  size_t number_of_blocks = 32;\n\n  hipError_t syncErr, asyncErr;\n\n  doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);\n\n  /*\n   * Catch errors for both the kernel launch above and any\n   * errors that occur during the asynchronous `doubleElements`\n   * kernel execution.\n   */\n\n  syncErr = hipGetLastError();\n  asyncErr = hipDeviceSynchronize();\n\n  /*\n   * Print errors should they exist.\n   */\n\n  if (syncErr != hipSuccess) printf(\"Error: %s\\n\", hipGetErrorString(syncErr));\n  if (asyncErr != hipSuccess) printf(\"Error: %s\\n\", hipGetErrorString(asyncErr));\n\n  bool areDoubled = checkElementsAreDoubled(a, N);\n  printf(\"All elements were doubled? %s\\n\", areDoubled ? \"TRUE\" : \"FALSE\");\n\n  hipFree(a);\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-add-error-handling-solution.cu"}
{"input": "#include <stdio.h>\n\n/*\n * Refactor firstParallel so that it can run on the GPU.\n */\n\n__global__ void firstParallel()\n{\n  printf(\"This should be running in parallel.\\n\");\n}\n\nint main()\n{\n  /*\n   * Refactor this call to firstParallel to execute in parallel\n   * on the GPU.\n   */\n\n  firstParallel<<<5,5>>>();\n\n  /*\n   * Some code is needed below so that the CPU will wait\n   * for the GPU kernels to complete before proceeding.\n   */\n    cudaDeviceSynchronize();\n\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n/*\n * Refactor firstParallel so that it can run on the GPU.\n */\n\n__global__ void firstParallel()\n{\n  printf(\"This should be running in parallel.\\n\");\n}\n\nint main()\n{\n  /*\n   * Refactor this call to firstParallel to execute in parallel\n   * on the GPU.\n   */\n\n  firstParallel<<<5,5>>>();\n\n  /*\n   * Some code is needed below so that the CPU will wait\n   * for the GPU kernels to complete before proceeding.\n   */\n    hipDeviceSynchronize();\n\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-basic-parallel.cu"}
{"input": "#include <stdio.h>\n#include <assert.h>\n\ninline cudaError_t checkCuda(cudaError_t result)\n{\n  if (result != cudaSuccess) {\n    fprintf(stderr, \"CUDA Runtime Error: %s\\n\", cudaGetErrorString(result));\n    assert(result == cudaSuccess);\n  }\n  return result;\n}\n\nvoid initWith(float num, float *a, int N)\n{\n  for(int i = 0; i < N; ++i)\n  {\n    a[i] = num;\n  }\n}\n\n__global__\nvoid addVectorsInto(float *result, float *a, float *b, int N)\n{\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for(int i = index; i < N; i += stride)\n  {\n    result[i] = a[i] + b[i];\n  }\n}\n\nvoid checkElementsAre(float target, float *array, int N)\n{\n  for(int i = 0; i < N; i++)\n  {\n    if(array[i] != target)\n    {\n      printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n      exit(1);\n    }\n  }\n}\n\nint main()\n{\n  const int N = 2<<20;\n  size_t size = N * sizeof(float);\n\n  float *a;\n  float *b;\n  float *c;\n\n  checkCuda( cudaMallocManaged(&a, size) );\n  checkCuda( cudaMallocManaged(&b, size) );\n  checkCuda( cudaMallocManaged(&c, size) );\n\n  initWith(3, a, N);\n  initWith(4, b, N);\n  initWith(0, c, N);\n\n  size_t threadsPerBlock;\n  size_t numberOfBlocks;\n\n  threadsPerBlock = 256;\n  numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n  addVectorsInto<<<numberOfBlocks, threadsPerBlock>>>(c, a, b, N);\n\n  checkCuda( cudaGetLastError() );\n  checkCuda( cudaDeviceSynchronize() );\n\n  checkElementsAre(7, c, N);\n\n  checkCuda( cudaFree(a) );\n  checkCuda( cudaFree(b) );\n  checkCuda( cudaFree(c) );\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <assert.h>\n\ninline hipError_t checkCuda(hipError_t result)\n{\n  if (result != hipSuccess) {\n    fprintf(stderr, \"CUDA Runtime Error: %s\\n\", hipGetErrorString(result));\n    assert(result == hipSuccess);\n  }\n  return result;\n}\n\nvoid initWith(float num, float *a, int N)\n{\n  for(int i = 0; i < N; ++i)\n  {\n    a[i] = num;\n  }\n}\n\n__global__\nvoid addVectorsInto(float *result, float *a, float *b, int N)\n{\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for(int i = index; i < N; i += stride)\n  {\n    result[i] = a[i] + b[i];\n  }\n}\n\nvoid checkElementsAre(float target, float *array, int N)\n{\n  for(int i = 0; i < N; i++)\n  {\n    if(array[i] != target)\n    {\n      printf(\"FAIL: array[%d] - %0.0f does not equal %0.0f\\n\", i, array[i], target);\n      exit(1);\n    }\n  }\n}\n\nint main()\n{\n  const int N = 2<<20;\n  size_t size = N * sizeof(float);\n\n  float *a;\n  float *b;\n  float *c;\n\n  checkCuda( hipMallocManaged(&a, size) );\n  checkCuda( hipMallocManaged(&b, size) );\n  checkCuda( hipMallocManaged(&c, size) );\n\n  initWith(3, a, N);\n  initWith(4, b, N);\n  initWith(0, c, N);\n\n  size_t threadsPerBlock;\n  size_t numberOfBlocks;\n\n  threadsPerBlock = 256;\n  numberOfBlocks = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n  addVectorsInto<<<numberOfBlocks, threadsPerBlock>>>(c, a, b, N);\n\n  checkCuda( hipGetLastError() );\n  checkCuda( hipDeviceSynchronize() );\n\n  checkElementsAre(7, c, N);\n\n  checkCuda( hipFree(a) );\n  checkCuda( hipFree(b) );\n  checkCuda( hipFree(c) );\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/01-vector-add-solution.cu"}
{"input": "#include <stdio.h>\n\n\n__global__ void initializeElementsTo(int initialValue, int *a, int N)\n{\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < N)\n  {\n    a[i] = initialValue;\n  }\n}\n\nint main()\n{\n  /*\n   * Do not modify `N`.\n   */\n\n  int N = 1000;\n\n  int *a;\n  size_t size = N * sizeof(int);\n\n  cudaMallocManaged(&a, size);\n\n  /*\n   * Assume we have reason to want the number of threads\n   * fixed at `256`: do not modify `threads_per_block`.\n   */\n\n  size_t threads_per_block = 256;\n\n  /*\n   * The following is idiomatic CUDA to make sure there are at\n   * least as many threads in the grid as there are `N` elements.\n   */\n\n  size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n\n  int initialValue = 6;\n\n  initializeElementsTo<<<number_of_blocks, threads_per_block>>>(initialValue, a, N);\n  cudaDeviceSynchronize();\n\n  /*\n   * Check to make sure all values in `a`, were initialized.\n   */\n\n  for (int i = 0; i < N; ++i)\n  {\n    if(a[i] != initialValue)\n    {\n      printf(\"FAILURE: target value: %d\\t a[%d]: %d\\n\", initialValue, i, a[i]);\n      exit(1);\n    }\n  }\n  printf(\"SUCCESS!\\n\");\n\n  cudaFree(a);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n\n__global__ void initializeElementsTo(int initialValue, int *a, int N)\n{\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < N)\n  {\n    a[i] = initialValue;\n  }\n}\n\nint main()\n{\n  /*\n   * Do not modify `N`.\n   */\n\n  int N = 1000;\n\n  int *a;\n  size_t size = N * sizeof(int);\n\n  hipMallocManaged(&a, size);\n\n  /*\n   * Assume we have reason to want the number of threads\n   * fixed at `256`: do not modify `threads_per_block`.\n   */\n\n  size_t threads_per_block = 256;\n\n  /*\n   * The following is idiomatic CUDA to make sure there are at\n   * least as many threads in the grid as there are `N` elements.\n   */\n\n  size_t number_of_blocks = (N + threads_per_block - 1) / threads_per_block;\n\n  int initialValue = 6;\n\n  initializeElementsTo<<<number_of_blocks, threads_per_block>>>(initialValue, a, N);\n  hipDeviceSynchronize();\n\n  /*\n   * Check to make sure all values in `a`, were initialized.\n   */\n\n  for (int i = 0; i < N; ++i)\n  {\n    if(a[i] != initialValue)\n    {\n      printf(\"FAILURE: target value: %d\\t a[%d]: %d\\n\", initialValue, i, a[i]);\n      exit(1);\n    }\n  }\n  printf(\"SUCCESS!\\n\");\n\n  hipFree(a);\n}\n", "source": "/kaggle/working/GPU_Acceleration_Using_CUDA_C_CPP/02-mismatched-config-loop-solution.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/transformer_engine.h>\n\n#include <bit>\n\n#include \"./common.h\"\n#include \"./utils.cuh\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/logging.h\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n__global__ void __launch_bounds__(1)\n    update_tensor_scale_inv_kernel(const float *__restrict__ scale_ptr,\n                                   float *__restrict__ scale_inv_ptr) {\n  const float scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  reciprocal<float>(scale_inv_ptr, scale);\n}\n\n}  // namespace\n\ncudaDataType_t get_cuda_dtype(const transformer_engine::DType t) {\n  using namespace transformer_engine;\n  switch (t) {\n    case DType::kFloat16:\n      return CUDA_R_16F;\n    case DType::kFloat32:\n      return CUDA_R_32F;\n    case DType::kBFloat16:\n      return CUDA_R_16BF;\n    case DType::kFloat8E4M3:\n      return CUDA_R_8F_E4M3;\n    case DType::kFloat8E5M2:\n      return CUDA_R_8F_E5M2;\n#if CUDA_VERSION >= 12080\n    case DType::kFloat4E2M1:\n      return CUDA_R_4F_E2M1;\n#endif\n    default:\n      NVTE_ERROR(\"Invalid type\");\n  }\n}\n\nvoid update_tensor_scale_inv(Tensor *t, cudaStream_t stream) {\n  if (is_fp8_dtype(t->data.dtype) && is_tensor_scaling(t->scaling_mode)) {\n    NVTE_CHECK(t->scale_inv.dptr != nullptr, \"Tensor should have allocated scale_inv.\");\n    update_tensor_scale_inv_kernel<<<1, 1, 0, stream>>>(\n        reinterpret_cast<const float *>(t->scale.dptr),\n        reinterpret_cast<float *>(t->scale_inv.dptr));\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nnamespace {\n\nconstexpr size_t kThreadsPerBlock = 256;\ntemplate <typename TVectorized>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    memset_kernel(void *__restrict__ ptr, int value, size_t size_in_bytes) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (idx * sizeof(TVectorized) >= size_in_bytes) {\n    return;  // Out of bounds\n  }\n\n  if ((idx + 1) * sizeof(TVectorized) > size_in_bytes) {\n    // If the buffer size is not an even multiple of the vectorization, manually set the remaining bytes unvectorized.\n    size_t remaining_bytes = size_in_bytes - idx * sizeof(TVectorized);\n    memset(reinterpret_cast<uint8_t *>(ptr) + idx * sizeof(TVectorized), value, remaining_bytes);\n    return;\n  }\n\n  union {\n    TVectorized value;\n    uint8_t data[sizeof(TVectorized)];\n  } data;\n  for (size_t i = 0; i < sizeof(TVectorized); ++i) {\n    data.data[i] = static_cast<uint8_t>(value);\n  }\n  reinterpret_cast<TVectorized *>(ptr)[idx] = data.value;\n}\n\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    splits_to_offsets_kernel(const int64_t *__restrict__ first_dims, int64_t *__restrict__ output,\n                             size_t num_tensors, int64_t logical_last_dim) {\n  __shared__ int64_t block_scan[kThreadsPerBlock];\n  __shared__ int64_t chunk_prefix;\n\n  const size_t tid = threadIdx.x;\n  if (tid == 0) {\n    output[0] = 0;\n    chunk_prefix = 0;\n  }\n  __syncthreads();\n\n  for (size_t chunk_start = 0; chunk_start < num_tensors; chunk_start += kThreadsPerBlock) {\n    const size_t idx = chunk_start + tid;\n    int64_t value = 0;\n    if (idx < num_tensors) {\n      value = first_dims[idx] * logical_last_dim;\n    }\n    block_scan[tid] = value;\n    __syncthreads();\n\n    // Inclusive scan in shared memory.\n    for (size_t offset = 1; offset < kThreadsPerBlock; offset <<= 1) {\n      const int64_t addend = (tid >= offset) ? block_scan[tid - offset] : 0;\n      __syncthreads();\n      block_scan[tid] += addend;\n      __syncthreads();\n    }\n\n    if (idx < num_tensors) {\n      output[idx + 1] = chunk_prefix + block_scan[tid];\n    }\n    __syncthreads();\n\n    if (tid == kThreadsPerBlock - 1) {\n      chunk_prefix += block_scan[tid];\n    }\n    __syncthreads();\n  }\n}\n\n}  // namespace\n\n#define MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, vectorizedType, stream) \\\n  if (size_in_bytes >= sizeof(vectorizedType) &&                                             \\\n      reinterpret_cast<size_t>(ptr) % sizeof(vectorizedType) == 0) {                         \\\n    size_t numBlocks = DIVUP(size_in_bytes, kThreadsPerBlock * sizeof(vectorizedType));      \\\n    dim3 grid(numBlocks, 1, 1);                                                              \\\n    memset_kernel<vectorizedType>                                                            \\\n        <<<grid, kThreadsPerBlock, 0, stream>>>(ptr, value, size_in_bytes);                  \\\n    NVTE_CHECK_CUDA(cudaGetLastError());                                                     \\\n    return;                                                                                  \\\n  }\n\nextern \"C\" {\nvoid nvte_memset(void *ptr, int value, size_t size_in_bytes, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_memset);\n  NVTE_CHECK(ptr != nullptr, \"Pointer for memset must be allocated.\");\n\n  if (size_in_bytes > 4096) {\n    // Use cudaMemsetAsync for larger sizes.\n    NVTE_CHECK_CUDA(cudaMemsetAsync(ptr, value, size_in_bytes, stream));\n    return;\n  }\n\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float4, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float2, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, uint8_t, stream);\n}\n\nvoid nvte_splits_to_offsets(const int64_t *first_dims, int64_t *output, size_t num_tensors,\n                            int64_t logical_last_dim, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_splits_to_offsets);\n  NVTE_CHECK(output != nullptr, \"Output pointer must be allocated.\");\n  NVTE_CHECK(num_tensors > 0, \"num_tensors must be greater than 0.\");\n  NVTE_CHECK(first_dims != nullptr, \"first_dims pointer must be allocated.\");\n  NVTE_CHECK(logical_last_dim > 0, \"logical_last_dim must be greater than 0.\");\n\n  splits_to_offsets_kernel<<<1, kThreadsPerBlock, 0, stream>>>(first_dims, output, num_tensors,\n                                                               logical_last_dim);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n}  // extern \"C\"\n\nvoid checkCuDriverContext(CUstream stream) {\n  // Ensure the thread's \"current\" CUDA context is set.\n  cuda_driver::ensure_context_exists();\n\n  CUcontext ctx;\n  const CUresult driver_status = cuda_driver::call(\"cuStreamGetCtx\", stream, &ctx);\n  switch (driver_status) {\n    case CUDA_SUCCESS:\n      break;\n\n    case CUDA_ERROR_INVALID_CONTEXT:\n      int current_device;\n      NVTE_CHECK_CUDA(cudaGetDevice(&current_device));\n      NVTE_CALL_CHECK_CUDA_DRIVER(cuDevicePrimaryCtxRetain, &ctx, current_device);\n      NVTE_CALL_CHECK_CUDA_DRIVER(cuCtxSetCurrent, ctx);\n      break;\n\n    default:\n      const char *desc_NVTE_CHECK_CUDA_DRIVER;\n      cuda_driver::call(\"cuGetErrorString\", driver_status, &desc_NVTE_CHECK_CUDA_DRIVER);\n      NVTE_ERROR(\"CUDA Error: \", desc_NVTE_CHECK_CUDA_DRIVER);\n  }\n}\n\nCUtensorMapDataType get_CUtensorMapDataType(DType dtype) {\n  static const std::unordered_map<DType, CUtensorMapDataType> dtypeMapping = []() {\n    std::unordered_map<DType, CUtensorMapDataType> typeMapping = {\n        {DType::kByte, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8},\n        {DType::kFloat32, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_FLOAT32},\n        {DType::kFloat16, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_FLOAT16},\n        {DType::kBFloat16, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_BFLOAT16},\n        {DType::kFloat8E4M3, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8},\n        {DType::kFloat8E5M2, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8}};\n#if FP4_TYPE_SUPPORTED\n    typeMapping.insert(\n        {DType::kFloat4E2M1, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_16U4_ALIGN8B});\n#endif\n    return typeMapping;\n  }();\n  return dtypeMapping.at(dtype);\n}\n\n// Set up parameters to create TMA descriptor.\nvoid create_2D_tensor_map(CUtensorMap &tensorMap, const SimpleTensor &tensor,\n                          const uint64_t globalY, const uint64_t globalX, const uint32_t shmemY,\n                          const uint32_t shmemX, const uint32_t stride_elems,\n                          const uint32_t offset_elems, const size_t type_num_bits,\n                          const CUtensorMapSwizzle swizzle) {\n  cuda_driver::ensure_context_exists();\n  // Get a function pointer to the cuTensorMapEncodeTiled driver API\n  // Note: PFN_cuTensorMapEncodeTiled is not defined in cuda13\n  static PFN_cuTensorMapEncodeTiled_v12000 cuDriverTensorMapEncodeTiled = []() {\n    void *driver_ptr = cuda_driver::get_symbol(\"cuTensorMapEncodeTiled\");\n    return reinterpret_cast<PFN_cuTensorMapEncodeTiled_v12000>(driver_ptr);\n  }();\n  // rank is the number of dimensions of the array\n  constexpr uint32_t rank = 2;\n\n  // Dimension for the packed data types must reflect the number of individual U# values.\n  uint64_t size[rank] = {globalX, globalY};\n\n  // The stride is the number of bytes to traverse from the first element of one row to the next\n  uint64_t stride[rank - 1] = {(stride_elems * type_num_bits) / 8};\n\n  // The boxSize is the size of the shared memory buffer that is used as the\n  // source/destination of a TMA transfer\n  uint32_t boxSize[rank] = {shmemX, shmemY};\n\n  // The distance between elements in units of sizeof(element)\n  uint32_t elemStride[rank] = {1, 1};\n\n  const CUtensorMapDataType tensorDataType = get_CUtensorMapDataType(tensor.dtype);\n  void *dataPtr = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(tensor.dptr) +\n                                           (offset_elems * type_num_bits) / 8);\n\n  NVTE_CHECK(is_aligned_ptr(dataPtr, TMA_GMEM_ALIGNMENT),\n             \"Tensor data pointer must be 16B aligned\");\n\n  const int TMA_needed_size = (TMA_GMEM_ALIGNMENT * 8) / type_num_bits;\n  NVTE_CHECK(globalX % TMA_needed_size == 0, \"Shape not supported. For \", type_num_bits,\n             \"-bit data type, expected multiple of \", TMA_needed_size, \", got \", globalX);\n\n  // Create the tensor descriptor.\n  NVTE_CHECK_CUDA_DRIVER(cuDriverTensorMapEncodeTiled(\n      &tensorMap,  // CUtensorMap *tensorMap,\n      tensorDataType,\n      rank,        // cuuint32_t tensorRank,\n      dataPtr,     // void *globalAddress,\n      size,        // const cuuint64_t *globalDim,\n      stride,      // const cuuint64_t *globalStrides,\n      boxSize,     // const cuuint32_t *boxDim,\n      elemStride,  // const cuuint32_t *elementStrides,\n      // Interleave patterns can be used to accelerate loading of values that\n      // are less than 4 bytes long.\n      CUtensorMapInterleave::CU_TENSOR_MAP_INTERLEAVE_NONE,\n\n      // Swizzling can be used to avoid shared memory bank conflicts.\n      swizzle,\n\n      // L2 Promotion can be used to widen the effect of a cache-policy to a wider\n      // set of L2 cache lines.\n      CUtensorMapL2promotion::CU_TENSOR_MAP_L2_PROMOTION_NONE,\n      // CUtensorMapL2promotion::CU_TENSOR_MAP_L2_PROMOTION_L2_256B,\n\n      // Any element that is outside of bounds will be set to zero by the TMA transfer.\n      CUtensorMapFloatOOBfill::CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE));\n}\n\nbool is_supported_by_CC_100() {\n  int deviceComputeCapability = cuda::sm_arch(cuda::current_device());\n\n  return deviceComputeCapability >= 100;\n}\n\nstd::vector<std::vector<Tensor *>> convert_tensor_array(NVTETensor **nvte_tensors,\n                                                        size_t outer_size, size_t inner_size) {\n  std::vector<std::vector<Tensor *>> ret;\n  for (size_t i = 0; i < outer_size; ++i) {\n    ret.emplace_back();\n    for (size_t j = 0; j < inner_size; ++j) {\n      ret.back().push_back(convertNVTETensor(nvte_tensors[i][j]));\n    }\n  }\n  return ret;\n}\n\nsize_t get_buffer_size_bytes(const size_t elements_num, const DType buffer_dtype) {\n  return (elements_num * typeToNumBits(buffer_dtype)) / 8;\n}\n\nsize_t get_buffer_size_bytes(const size_t dim_first, const size_t dim_last,\n                             const DType buffer_dtype) {\n  if (buffer_dtype == DType::kFloat4E2M1) {\n    NVTE_CHECK(dim_last % 2 == 0,\n               \"Last dimension of a tensor with FP4 type of data must be an even number!\");\n  }\n  const size_t elements_num = dim_first * dim_last;\n  return get_buffer_size_bytes(elements_num, buffer_dtype);\n}\n\n}  // namespace transformer_engine\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/transformer_engine.h>\n\n#include <bit>\n\n#include \"./common.h\"\n#include \"./utils.cuh\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/logging.h\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n__global__ void __launch_bounds__(1)\n    update_tensor_scale_inv_kernel(const float *__restrict__ scale_ptr,\n                                   float *__restrict__ scale_inv_ptr) {\n  const float scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  reciprocal<float>(scale_inv_ptr, scale);\n}\n\n}  // namespace\n\nhipDataType get_cuda_dtype(const transformer_engine::DType t) {\n  using namespace transformer_engine;\n  switch (t) {\n    case DType::kFloat16:\n      return HIP_R_16F;\n    case DType::kFloat32:\n      return HIP_R_32F;\n    case DType::kBFloat16:\n      return HIP_R_16BF;\n    case DType::kFloat8E4M3:\n      return HIP_R_8F_E4M3;\n    case DType::kFloat8E5M2:\n      return HIP_R_8F_E5M2;\n#if CUDA_VERSION >= 12080\n    case DType::kFloat4E2M1:\n      return HIP_R_4F_E2M1;\n#endif\n    default:\n      NVTE_ERROR(\"Invalid type\");\n  }\n}\n\nvoid update_tensor_scale_inv(Tensor *t, hipStream_t stream) {\n  if (is_fp8_dtype(t->data.dtype) && is_tensor_scaling(t->scaling_mode)) {\n    NVTE_CHECK(t->scale_inv.dptr != nullptr, \"Tensor should have allocated scale_inv.\");\n    update_tensor_scale_inv_kernel<<<1, 1, 0, stream>>>(\n        reinterpret_cast<const float *>(t->scale.dptr),\n        reinterpret_cast<float *>(t->scale_inv.dptr));\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nnamespace {\n\nconstexpr size_t kThreadsPerBlock = 256;\ntemplate <typename TVectorized>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    memset_kernel(void *__restrict__ ptr, int value, size_t size_in_bytes) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (idx * sizeof(TVectorized) >= size_in_bytes) {\n    return;  // Out of bounds\n  }\n\n  if ((idx + 1) * sizeof(TVectorized) > size_in_bytes) {\n    // If the buffer size is not an even multiple of the vectorization, manually set the remaining bytes unvectorized.\n    size_t remaining_bytes = size_in_bytes - idx * sizeof(TVectorized);\n    memset(reinterpret_cast<uint8_t *>(ptr) + idx * sizeof(TVectorized), value, remaining_bytes);\n    return;\n  }\n\n  union {\n    TVectorized value;\n    uint8_t data[sizeof(TVectorized)];\n  } data;\n  for (size_t i = 0; i < sizeof(TVectorized); ++i) {\n    data.data[i] = static_cast<uint8_t>(value);\n  }\n  reinterpret_cast<TVectorized *>(ptr)[idx] = data.value;\n}\n\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    splits_to_offsets_kernel(const int64_t *__restrict__ first_dims, int64_t *__restrict__ output,\n                             size_t num_tensors, int64_t logical_last_dim) {\n  __shared__ int64_t block_scan[kThreadsPerBlock];\n  __shared__ int64_t chunk_prefix;\n\n  const size_t tid = threadIdx.x;\n  if (tid == 0) {\n    output[0] = 0;\n    chunk_prefix = 0;\n  }\n  __syncthreads();\n\n  for (size_t chunk_start = 0; chunk_start < num_tensors; chunk_start += kThreadsPerBlock) {\n    const size_t idx = chunk_start + tid;\n    int64_t value = 0;\n    if (idx < num_tensors) {\n      value = first_dims[idx] * logical_last_dim;\n    }\n    block_scan[tid] = value;\n    __syncthreads();\n\n    // Inclusive scan in shared memory.\n    for (size_t offset = 1; offset < kThreadsPerBlock; offset <<= 1) {\n      const int64_t addend = (tid >= offset) ? block_scan[tid - offset] : 0;\n      __syncthreads();\n      block_scan[tid] += addend;\n      __syncthreads();\n    }\n\n    if (idx < num_tensors) {\n      output[idx + 1] = chunk_prefix + block_scan[tid];\n    }\n    __syncthreads();\n\n    if (tid == kThreadsPerBlock - 1) {\n      chunk_prefix += block_scan[tid];\n    }\n    __syncthreads();\n  }\n}\n\n}  // namespace\n\n#define MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, vectorizedType, stream) \\\n  if (size_in_bytes >= sizeof(vectorizedType) &&                                             \\\n      reinterpret_cast<size_t>(ptr) % sizeof(vectorizedType) == 0) {                         \\\n    size_t numBlocks = DIVUP(size_in_bytes, kThreadsPerBlock * sizeof(vectorizedType));      \\\n    dim3 grid(numBlocks, 1, 1);                                                              \\\n    memset_kernel<vectorizedType>                                                            \\\n        <<<grid, kThreadsPerBlock, 0, stream>>>(ptr, value, size_in_bytes);                  \\\n    NVTE_CHECK_CUDA(hipGetLastError());                                                     \\\n    return;                                                                                  \\\n  }\n\nextern \"C\" {\nvoid nvte_memset(void *ptr, int value, size_t size_in_bytes, hipStream_t stream) {\n  NVTE_API_CALL(nvte_memset);\n  NVTE_CHECK(ptr != nullptr, \"Pointer for memset must be allocated.\");\n\n  if (size_in_bytes > 4096) {\n    // Use cudaMemsetAsync for larger sizes.\n    NVTE_CHECK_CUDA(hipMemsetAsync(ptr, value, size_in_bytes, stream));\n    return;\n  }\n\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float4, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float2, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, float, stream);\n  MEMSET_VECTORIZED_KERNEL_DISPATCH(ptr, size_in_bytes, value, uint8_t, stream);\n}\n\nvoid nvte_splits_to_offsets(const int64_t *first_dims, int64_t *output, size_t num_tensors,\n                            int64_t logical_last_dim, hipStream_t stream) {\n  NVTE_API_CALL(nvte_splits_to_offsets);\n  NVTE_CHECK(output != nullptr, \"Output pointer must be allocated.\");\n  NVTE_CHECK(num_tensors > 0, \"num_tensors must be greater than 0.\");\n  NVTE_CHECK(first_dims != nullptr, \"first_dims pointer must be allocated.\");\n  NVTE_CHECK(logical_last_dim > 0, \"logical_last_dim must be greater than 0.\");\n\n  splits_to_offsets_kernel<<<1, kThreadsPerBlock, 0, stream>>>(first_dims, output, num_tensors,\n                                                               logical_last_dim);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n}  // extern \"C\"\n\nvoid checkCuDriverContext(hipStream_t stream) {\n  // Ensure the thread's \"current\" CUDA context is set.\n  cuda_driver::ensure_context_exists();\n\n  hipCtx_t ctx;\n  const hipError_t driver_status = cuda_driver::call(\"cuStreamGetCtx\", stream, &ctx);\n  switch (driver_status) {\n    case hipSuccess:\n      break;\n\n    case hipErrorInvalidContext:\n      int current_device;\n      NVTE_CHECK_CUDA(hipGetDevice(&current_device));\n      NVTE_CALL_CHECK_CUDA_DRIVER(hipDevicePrimaryCtxRetain, &ctx, current_device);\n      NVTE_CALL_CHECK_CUDA_DRIVER(hipCtxSetCurrent, ctx);\n      break;\n\n    default:\n      const char *desc_NVTE_CHECK_CUDA_DRIVER;\n      cuda_driver::call(\"hipDrvGetErrorString\", driver_status, &desc_NVTE_CHECK_CUDA_DRIVER);\n      NVTE_ERROR(\"CUDA Error: \", desc_NVTE_CHECK_CUDA_DRIVER);\n  }\n}\n\nCUtensorMapDataType get_CUtensorMapDataType(DType dtype) {\n  static const std::unordered_map<DType, CUtensorMapDataType> dtypeMapping = []() {\n    std::unordered_map<DType, CUtensorMapDataType> typeMapping = {\n        {DType::kByte, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8},\n        {DType::kFloat32, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_FLOAT32},\n        {DType::kFloat16, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_FLOAT16},\n        {DType::kBFloat16, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_BFLOAT16},\n        {DType::kFloat8E4M3, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8},\n        {DType::kFloat8E5M2, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8}};\n#if FP4_TYPE_SUPPORTED\n    typeMapping.insert(\n        {DType::kFloat4E2M1, CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_16U4_ALIGN8B});\n#endif\n    return typeMapping;\n  }();\n  return dtypeMapping.at(dtype);\n}\n\n// Set up parameters to create TMA descriptor.\nvoid create_2D_tensor_map(CUtensorMap &tensorMap, const SimpleTensor &tensor,\n                          const uint64_t globalY, const uint64_t globalX, const uint32_t shmemY,\n                          const uint32_t shmemX, const uint32_t stride_elems,\n                          const uint32_t offset_elems, const size_t type_num_bits,\n                          const CUtensorMapSwizzle swizzle) {\n  cuda_driver::ensure_context_exists();\n  // Get a function pointer to the cuTensorMapEncodeTiled driver API\n  // Note: PFN_cuTensorMapEncodeTiled is not defined in cuda13\n  static PFN_cuTensorMapEncodeTiled_v12000 cuDriverTensorMapEncodeTiled = []() {\n    void *driver_ptr = cuda_driver::get_symbol(\"cuTensorMapEncodeTiled\");\n    return reinterpret_cast<PFN_cuTensorMapEncodeTiled_v12000>(driver_ptr);\n  }();\n  // rank is the number of dimensions of the array\n  constexpr uint32_t rank = 2;\n\n  // Dimension for the packed data types must reflect the number of individual U# values.\n  uint64_t size[rank] = {globalX, globalY};\n\n  // The stride is the number of bytes to traverse from the first element of one row to the next\n  uint64_t stride[rank - 1] = {(stride_elems * type_num_bits) / 8};\n\n  // The boxSize is the size of the shared memory buffer that is used as the\n  // source/destination of a TMA transfer\n  uint32_t boxSize[rank] = {shmemX, shmemY};\n\n  // The distance between elements in units of sizeof(element)\n  uint32_t elemStride[rank] = {1, 1};\n\n  const CUtensorMapDataType tensorDataType = get_CUtensorMapDataType(tensor.dtype);\n  void *dataPtr = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(tensor.dptr) +\n                                           (offset_elems * type_num_bits) / 8);\n\n  NVTE_CHECK(is_aligned_ptr(dataPtr, TMA_GMEM_ALIGNMENT),\n             \"Tensor data pointer must be 16B aligned\");\n\n  const int TMA_needed_size = (TMA_GMEM_ALIGNMENT * 8) / type_num_bits;\n  NVTE_CHECK(globalX % TMA_needed_size == 0, \"Shape not supported. For \", type_num_bits,\n             \"-bit data type, expected multiple of \", TMA_needed_size, \", got \", globalX);\n\n  // Create the tensor descriptor.\n  NVTE_CHECK_CUDA_DRIVER(cuDriverTensorMapEncodeTiled(\n      &tensorMap,  // CUtensorMap *tensorMap,\n      tensorDataType,\n      rank,        // cuuint32_t tensorRank,\n      dataPtr,     // void *globalAddress,\n      size,        // const cuuint64_t *globalDim,\n      stride,      // const cuuint64_t *globalStrides,\n      boxSize,     // const cuuint32_t *boxDim,\n      elemStride,  // const cuuint32_t *elementStrides,\n      // Interleave patterns can be used to accelerate loading of values that\n      // are less than 4 bytes long.\n      CUtensorMapInterleave::CU_TENSOR_MAP_INTERLEAVE_NONE,\n\n      // Swizzling can be used to avoid shared memory bank conflicts.\n      swizzle,\n\n      // L2 Promotion can be used to widen the effect of a cache-policy to a wider\n      // set of L2 cache lines.\n      CUtensorMapL2promotion::CU_TENSOR_MAP_L2_PROMOTION_NONE,\n      // CUtensorMapL2promotion::CU_TENSOR_MAP_L2_PROMOTION_L2_256B,\n\n      // Any element that is outside of bounds will be set to zero by the TMA transfer.\n      CUtensorMapFloatOOBfill::CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE));\n}\n\nbool is_supported_by_CC_100() {\n  int deviceComputeCapability = cuda::sm_arch(cuda::current_device());\n\n  return deviceComputeCapability >= 100;\n}\n\nstd::vector<std::vector<Tensor *>> convert_tensor_array(NVTETensor **nvte_tensors,\n                                                        size_t outer_size, size_t inner_size) {\n  std::vector<std::vector<Tensor *>> ret;\n  for (size_t i = 0; i < outer_size; ++i) {\n    ret.emplace_back();\n    for (size_t j = 0; j < inner_size; ++j) {\n      ret.back().push_back(convertNVTETensor(nvte_tensors[i][j]));\n    }\n  }\n  return ret;\n}\n\nsize_t get_buffer_size_bytes(const size_t elements_num, const DType buffer_dtype) {\n  return (elements_num * typeToNumBits(buffer_dtype)) / 8;\n}\n\nsize_t get_buffer_size_bytes(const size_t dim_first, const size_t dim_last,\n                             const DType buffer_dtype) {\n  if (buffer_dtype == DType::kFloat4E2M1) {\n    NVTE_CHECK(dim_last % 2 == 0,\n               \"Last dimension of a tensor with FP4 type of data must be an even number!\");\n  }\n  const size_t elements_num = dim_first * dim_last;\n  return get_buffer_size_bytes(elements_num, buffer_dtype);\n}\n\n}  // namespace transformer_engine\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/common.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"../common.h\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\nnamespace kv_cache {\n\nconstexpr int block_size = 1024;\n\ntemplate <typename dtype>\n__global__ void reindex_kv_cache_kernel(dtype *k_cache, dtype *v_cache, int *batch_indices,\n                                        int *cu_new_lens, int *cu_cached_lens, int h_kv, int d_k,\n                                        int d_v, int b, int max_seq_len) {\n  // k_cache, v_cache: bshd\n  // batch_indices: [b]; cu_new_lens, cu_cached_lens: [b + 1]\n  int actual_b = b;\n  for (int i = 0; i < b - 1; i++) {\n    if (batch_indices[i + 1] < batch_indices[i]) {\n      actual_b = i + 1;\n    }\n  }\n  bool flag = (batch_indices[0] != 0);\n  for (int batch_idx = 0; batch_idx < actual_b; batch_idx++) {\n    if (flag || ((batch_indices[batch_idx] - batch_indices[0]) != batch_idx)) {\n      int num_tokens = (cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx]) -\n                       (cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx]);\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int num_elts = max(num_elts_k, num_elts_v);\n      for (int token_idx = blockIdx.x; token_idx < num_tokens; token_idx += gridDim.x) {\n        int src_offset = batch_indices[batch_idx] * max_seq_len + token_idx;\n        int des_offset = batch_idx * max_seq_len + token_idx;\n        dtype *k_cache_src_offset = k_cache + src_offset * num_elts_k;\n        dtype *k_cache_des_offset = k_cache + des_offset * num_elts_k;\n        dtype *v_cache_src_offset = v_cache + src_offset * num_elts_v;\n        dtype *v_cache_des_offset = v_cache + des_offset * num_elts_v;\n        for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n          if (i < num_elts_k) {\n            *(k_cache_des_offset + i) = *(k_cache_src_offset + i);\n          }\n          if (i < num_elts_v) {\n            *(v_cache_des_offset + i) = *(v_cache_src_offset + i);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename dtype>\n__global__ void copy_to_kv_cache_kernel(dtype *new_k, dtype *new_v, dtype *k_cache, dtype *v_cache,\n                                        int *page_table, int *cu_new_lens, int *cu_cached_lens,\n                                        NVTE_QKV_Format qkv_format, int h_kv, int d_k, int d_v,\n                                        int b, int max_ctx_len, int max_seq_len,\n                                        int max_pages_per_seq, bool is_non_paged) {\n  // new_k, new_v: qkv_format; k_cache, v_cache: bshd\n  // cu_new_lens, cu_cached_lens: [b + 1]\n  // page_table: [b, max_pages_per_seq]\n  int page_size = max_seq_len / max_pages_per_seq;\n  if (qkv_format == NVTE_QKV_Format::NVTE_BSHD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (batch_idx * max_ctx_len + i) * num_elts_k;\n        dtype *new_token_id_v = new_v + (batch_idx * max_ctx_len + i) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (i * b + batch_idx) * num_elts_k;\n        dtype *new_token_id_v = new_v + (i * b + batch_idx) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (cu_new_lens[batch_idx] + i) * num_elts_k;\n        dtype *new_token_id_v = new_v + (cu_new_lens[batch_idx] + i) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename dtype>\nvoid copy_to_kv_cache_launcher(Tensor new_k, Tensor new_v, Tensor k_cache, Tensor v_cache,\n                               Tensor page_table, Tensor cu_new_lens, Tensor cu_cached_lens,\n                               NVTE_QKV_Format qkv_format, int h_kv, int d_k, int d_v, int b,\n                               int max_ctx_len, int max_seq_len, int max_pages_per_seq,\n                               bool is_non_paged, cudaStream_t stream) {\n  if (new_k.has_data() && new_v.has_data() && k_cache.has_data() && v_cache.has_data()) {\n    if (is_non_paged) {\n      reindex_kv_cache_kernel<<<max_seq_len, block_size, 0, stream>>>(\n          reinterpret_cast<dtype *>(k_cache.data.dptr),\n          reinterpret_cast<dtype *>(v_cache.data.dptr),\n          reinterpret_cast<int *>(page_table.data.dptr),\n          reinterpret_cast<int *>(cu_new_lens.data.dptr),\n          reinterpret_cast<int *>(cu_cached_lens.data.dptr), h_kv, d_k, d_v, b, max_seq_len);\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    }\n    dim3 grid_size(b, max_ctx_len);\n    copy_to_kv_cache_kernel<<<grid_size, block_size, 0, stream>>>(\n        reinterpret_cast<dtype *>(new_k.data.dptr), reinterpret_cast<dtype *>(new_v.data.dptr),\n        reinterpret_cast<dtype *>(k_cache.data.dptr), reinterpret_cast<dtype *>(v_cache.data.dptr),\n        reinterpret_cast<int *>(page_table.data.dptr),\n        reinterpret_cast<int *>(cu_new_lens.data.dptr),\n        reinterpret_cast<int *>(cu_cached_lens.data.dptr), qkv_format, h_kv, d_k, d_v, b,\n        max_ctx_len, max_seq_len, max_pages_per_seq, is_non_paged);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid copy_to_kv_cache(Tensor new_k, Tensor new_v, Tensor k_cache, Tensor v_cache, Tensor page_table,\n                      Tensor cu_new_lens, Tensor cu_cached_lens, NVTE_QKV_Format qkv_format, int b,\n                      int max_ctx_len, int max_seq_len, int max_pages_per_seq, bool is_non_paged,\n                      cudaStream_t stream) {\n  int h_kv = new_k.shape()[new_k.dim() - 2];\n  int d_k = new_k.shape()[new_k.dim() - 1];\n  int d_v = new_v.shape()[new_v.dim() - 1];\n  NVTE_CHECK(k_cache.dtype() == v_cache.dtype() && new_k.dtype() == new_v.dtype() &&\n                 new_k.dtype() == k_cache.dtype(),\n             \"new_k, new_v, k_cache and v_cache must be of the same data type.\");\n  NVTE_CHECK(qkv_format == NVTE_QKV_Format::NVTE_BSHD || qkv_format == NVTE_QKV_Format::NVTE_SBHD ||\n                 qkv_format == NVTE_QKV_Format::NVTE_THD,\n             \"qkv_format must be {BSHD, SBHD, THD}.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      k_cache.dtype(), dtype,\n      copy_to_kv_cache_launcher<dtype>(new_k, new_v, k_cache, v_cache, page_table, cu_new_lens,\n                                       cu_cached_lens, qkv_format, h_kv, d_k, d_v, b, max_ctx_len,\n                                       max_seq_len, max_pages_per_seq, is_non_paged, stream););\n}\n\ntemplate <typename scalar_t>\n__global__ void convert_thd_to_bshd_kernel(scalar_t *tensor, scalar_t *new_tensor, int *cu_seqlens,\n                                           int b, int max_seq_len, int h, int d) {\n  // tensor: thd; new_tensor: bshd\n  // cu_seqlens: [b + 1]\n  for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n    int num_elts = (cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx]) * h * d;\n    int thd_offset = cu_seqlens[batch_idx] * h * d;\n    int bshd_offset = batch_idx * max_seq_len * h * d;\n    scalar_t *thd_token = tensor + thd_offset;\n    scalar_t *bshd_token = new_tensor + bshd_offset;\n    for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n      *(bshd_token + i) = *(thd_token + i);\n    }\n  }\n}\n\ntemplate <typename scalar_t>\nvoid convert_thd_to_bshd_launcher(Tensor tensor, Tensor new_tensor, Tensor cu_seqlens, int b,\n                                  int max_seq_len, int h, int d, cudaStream_t stream) {\n  using namespace transformer_engine;\n  convert_thd_to_bshd_kernel<<<16, 256, 0, stream>>>(\n      reinterpret_cast<scalar_t *>(tensor.data.dptr),\n      reinterpret_cast<scalar_t *>(new_tensor.data.dptr),\n      reinterpret_cast<int *>(cu_seqlens.data.dptr), b, max_seq_len, h, d);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid convert_thd_to_bshd(Tensor tensor, Tensor cu_seqlens, Tensor new_tensor, int b,\n                         int max_seq_len, cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  auto tensor_shape = tensor.shape();\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      new_tensor.dtype(), dtype,\n      convert_thd_to_bshd_launcher<dtype>(tensor, new_tensor, cu_seqlens, b, max_seq_len,\n                                          tensor_shape[1], tensor_shape[2], stream););\n}\n\ntemplate <typename scalar_t>\n__global__ void convert_bshd_to_thd_kernel(scalar_t *tensor, scalar_t *new_tensor, int *cu_seqlens,\n                                           int b, int max_seq_len, int h, int d) {\n  // tensor: bshd; new_tensor: thd\n  // cu_seqlens: [b + 1]\n  for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n    int seqlen = cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx];\n    int num_elts = seqlen * h * d;\n    int bshd_offset = batch_idx * max_seq_len * h * d;\n    int thd_offset = cu_seqlens[batch_idx] * h * d;\n    scalar_t *bshd_token = tensor + bshd_offset;\n    scalar_t *thd_token = new_tensor + thd_offset;\n    for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n      *(thd_token + i) = *(bshd_token + i);\n    }\n  }\n}\n\ntemplate <typename scalar_t>\nvoid convert_bshd_to_thd_launcher(Tensor tensor, Tensor new_tensor, Tensor cu_seqlens, int b,\n                                  int max_seq_len, int h, int d, cudaStream_t stream) {\n  using namespace transformer_engine;\n  convert_bshd_to_thd_kernel<<<16, 256, 0, stream>>>(\n      reinterpret_cast<scalar_t *>(tensor.data.dptr),\n      reinterpret_cast<scalar_t *>(new_tensor.data.dptr),\n      reinterpret_cast<int *>(cu_seqlens.data.dptr), b, max_seq_len, h, d);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid convert_bshd_to_thd(Tensor tensor, Tensor cu_seqlens, Tensor new_tensor, int t,\n                         cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  auto tensor_shape = tensor.shape();\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      tensor.dtype(), dtype,\n      convert_bshd_to_thd_launcher<dtype>(tensor, new_tensor, cu_seqlens, tensor_shape[0],\n                                          tensor_shape[1], tensor_shape[2], tensor_shape[3],\n                                          stream););\n}\n\n}  // namespace kv_cache\n}  // namespace transformer_engine\n\n/***************************************************************************************************\n * KV Cache: Copy new KV tokens to the KV cache\n *   1. new_k and new_v are in qkv_format; k_cache and v_cache are in 'bshd' format\n *   2. cu_new_lens and cu_cached_lens are of shape [b + 1]; cu_cached_lens include the added lens\n *      in current step\n *   3. Non-paged KV cache is a special case of paged KV cache, with page_table = [b, 1] and\n *      max_pages_per_seq = 1. We use the same underlying kernel for both non-paged and paged.\n *      Set is_non_paged = True/False to indicate as such.\n *   4. is_non_paged = True also re-indexes the KV cache, e.g. the initial batch indices [0, 3, 1, 2]\n *      becomes [0, 1, 1, 2]. The page_table = batch_indices.unsqueeze(1) is however unchanged.\n *      batch_indices_post can be used for monotonical indexing, i.e. [0, 1, 2, 3]. batch_indices is\n *      preserved for the next layer in the same iteration.\n *   5. Only supports same page_table for k_cache and v_cache\n *   6. Only pad_between_seqs = False when qkv_format = thd, i.e. there should be no pad tokens\n *      between sequences in new_k and new_v such as [a a a 0..0 b b 0..0 c 0..0].\n **************************************************************************************************/\n\nvoid nvte_copy_to_kv_cache(NVTETensor new_k, NVTETensor new_v, NVTETensor k_cache,\n                           NVTETensor v_cache, NVTETensor page_table, NVTETensor cu_new_lens,\n                           NVTETensor cu_cached_lens, NVTE_QKV_Format qkv_format, int b,\n                           int max_ctx_len, int max_seq_len, int max_pages_per_seq,\n                           int is_non_paged, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_copy_to_kv_cache);\n  using namespace transformer_engine;\n\n  kv_cache::copy_to_kv_cache(*convertNVTETensorCheck(new_k), *convertNVTETensorCheck(new_v),\n                             *convertNVTETensorCheck(k_cache), *convertNVTETensorCheck(v_cache),\n                             *convertNVTETensorCheck(page_table),\n                             *convertNVTETensorCheck(cu_new_lens),\n                             *convertNVTETensorCheck(cu_cached_lens), qkv_format, b, max_ctx_len,\n                             max_seq_len, max_pages_per_seq, is_non_paged, stream);\n}\n\n/***************************************************************************************************\n * KV Cache: Convert a tensor from qkv_format = thd to qkv_format = bshd\n **************************************************************************************************/\n\nvoid nvte_convert_thd_to_bshd(NVTETensor tensor, NVTETensor cu_seqlens, NVTETensor new_tensor,\n                              int b, int max_seq_len, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_convert_thd_to_bshd);\n  using namespace transformer_engine;\n\n  kv_cache::convert_thd_to_bshd(*convertNVTETensorCheck(tensor),\n                                *convertNVTETensorCheck(cu_seqlens),\n                                *convertNVTETensorCheck(new_tensor), b, max_seq_len, stream);\n}\n\n/***************************************************************************************************\n * KV Cache: Convert a tensor from qkv_format = bshd to qkv_format = thd\n **************************************************************************************************/\n\nvoid nvte_convert_bshd_to_thd(NVTETensor tensor, NVTETensor cu_seqlens, NVTETensor new_tensor,\n                              int t, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_convert_bshd_to_thd);\n  using namespace transformer_engine;\n\n  kv_cache::convert_bshd_to_thd(*convertNVTETensorCheck(tensor),\n                                *convertNVTETensorCheck(cu_seqlens),\n                                *convertNVTETensorCheck(new_tensor), t, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common.h\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\nnamespace kv_cache {\n\nconstexpr int block_size = 1024;\n\ntemplate <typename dtype>\n__global__ void reindex_kv_cache_kernel(dtype *k_cache, dtype *v_cache, int *batch_indices,\n                                        int *cu_new_lens, int *cu_cached_lens, int h_kv, int d_k,\n                                        int d_v, int b, int max_seq_len) {\n  // k_cache, v_cache: bshd\n  // batch_indices: [b]; cu_new_lens, cu_cached_lens: [b + 1]\n  int actual_b = b;\n  for (int i = 0; i < b - 1; i++) {\n    if (batch_indices[i + 1] < batch_indices[i]) {\n      actual_b = i + 1;\n    }\n  }\n  bool flag = (batch_indices[0] != 0);\n  for (int batch_idx = 0; batch_idx < actual_b; batch_idx++) {\n    if (flag || ((batch_indices[batch_idx] - batch_indices[0]) != batch_idx)) {\n      int num_tokens = (cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx]) -\n                       (cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx]);\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int num_elts = max(num_elts_k, num_elts_v);\n      for (int token_idx = blockIdx.x; token_idx < num_tokens; token_idx += gridDim.x) {\n        int src_offset = batch_indices[batch_idx] * max_seq_len + token_idx;\n        int des_offset = batch_idx * max_seq_len + token_idx;\n        dtype *k_cache_src_offset = k_cache + src_offset * num_elts_k;\n        dtype *k_cache_des_offset = k_cache + des_offset * num_elts_k;\n        dtype *v_cache_src_offset = v_cache + src_offset * num_elts_v;\n        dtype *v_cache_des_offset = v_cache + des_offset * num_elts_v;\n        for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n          if (i < num_elts_k) {\n            *(k_cache_des_offset + i) = *(k_cache_src_offset + i);\n          }\n          if (i < num_elts_v) {\n            *(v_cache_des_offset + i) = *(v_cache_src_offset + i);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename dtype>\n__global__ void copy_to_kv_cache_kernel(dtype *new_k, dtype *new_v, dtype *k_cache, dtype *v_cache,\n                                        int *page_table, int *cu_new_lens, int *cu_cached_lens,\n                                        NVTE_QKV_Format qkv_format, int h_kv, int d_k, int d_v,\n                                        int b, int max_ctx_len, int max_seq_len,\n                                        int max_pages_per_seq, bool is_non_paged) {\n  // new_k, new_v: qkv_format; k_cache, v_cache: bshd\n  // cu_new_lens, cu_cached_lens: [b + 1]\n  // page_table: [b, max_pages_per_seq]\n  int page_size = max_seq_len / max_pages_per_seq;\n  if (qkv_format == NVTE_QKV_Format::NVTE_BSHD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (batch_idx * max_ctx_len + i) * num_elts_k;\n        dtype *new_token_id_v = new_v + (batch_idx * max_ctx_len + i) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (i * b + batch_idx) * num_elts_k;\n        dtype *new_token_id_v = new_v + (i * b + batch_idx) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n      int *page_list = is_non_paged ? nullptr : page_table + batch_idx * max_pages_per_seq;\n      int cached_len = cu_cached_lens[batch_idx + 1] - cu_cached_lens[batch_idx];\n      int new_len = cu_new_lens[batch_idx + 1] - cu_new_lens[batch_idx];\n      int num_elts_k = h_kv * d_k;\n      int num_elts_v = h_kv * d_v;\n      int hd = h_kv * max(d_k, d_v);\n      for (int i = blockIdx.y; i < new_len; i += gridDim.y) {\n        int page_idx = is_non_paged ? batch_idx : page_list[(cached_len - new_len + i) / page_size];\n        dtype *new_token_id_k = new_k + (cu_new_lens[batch_idx] + i) * num_elts_k;\n        dtype *new_token_id_v = new_v + (cu_new_lens[batch_idx] + i) * num_elts_v;\n        dtype *token_id_k =\n            k_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_k;\n        dtype *token_id_v =\n            v_cache + (page_idx * page_size + (cached_len - new_len + i) % page_size) * num_elts_v;\n        for (int j = threadIdx.x; j < hd; j += blockDim.x) {\n          if (j < num_elts_k) {\n            *(token_id_k + j) = *(new_token_id_k + j);\n          }\n          if (j < num_elts_v) {\n            *(token_id_v + j) = *(new_token_id_v + j);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename dtype>\nvoid copy_to_kv_cache_launcher(Tensor new_k, Tensor new_v, Tensor k_cache, Tensor v_cache,\n                               Tensor page_table, Tensor cu_new_lens, Tensor cu_cached_lens,\n                               NVTE_QKV_Format qkv_format, int h_kv, int d_k, int d_v, int b,\n                               int max_ctx_len, int max_seq_len, int max_pages_per_seq,\n                               bool is_non_paged, hipStream_t stream) {\n  if (new_k.has_data() && new_v.has_data() && k_cache.has_data() && v_cache.has_data()) {\n    if (is_non_paged) {\n      reindex_kv_cache_kernel<<<max_seq_len, block_size, 0, stream>>>(\n          reinterpret_cast<dtype *>(k_cache.data.dptr),\n          reinterpret_cast<dtype *>(v_cache.data.dptr),\n          reinterpret_cast<int *>(page_table.data.dptr),\n          reinterpret_cast<int *>(cu_new_lens.data.dptr),\n          reinterpret_cast<int *>(cu_cached_lens.data.dptr), h_kv, d_k, d_v, b, max_seq_len);\n      NVTE_CHECK_CUDA(hipGetLastError());\n    }\n    dim3 grid_size(b, max_ctx_len);\n    copy_to_kv_cache_kernel<<<grid_size, block_size, 0, stream>>>(\n        reinterpret_cast<dtype *>(new_k.data.dptr), reinterpret_cast<dtype *>(new_v.data.dptr),\n        reinterpret_cast<dtype *>(k_cache.data.dptr), reinterpret_cast<dtype *>(v_cache.data.dptr),\n        reinterpret_cast<int *>(page_table.data.dptr),\n        reinterpret_cast<int *>(cu_new_lens.data.dptr),\n        reinterpret_cast<int *>(cu_cached_lens.data.dptr), qkv_format, h_kv, d_k, d_v, b,\n        max_ctx_len, max_seq_len, max_pages_per_seq, is_non_paged);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid copy_to_kv_cache(Tensor new_k, Tensor new_v, Tensor k_cache, Tensor v_cache, Tensor page_table,\n                      Tensor cu_new_lens, Tensor cu_cached_lens, NVTE_QKV_Format qkv_format, int b,\n                      int max_ctx_len, int max_seq_len, int max_pages_per_seq, bool is_non_paged,\n                      hipStream_t stream) {\n  int h_kv = new_k.shape()[new_k.dim() - 2];\n  int d_k = new_k.shape()[new_k.dim() - 1];\n  int d_v = new_v.shape()[new_v.dim() - 1];\n  NVTE_CHECK(k_cache.dtype() == v_cache.dtype() && new_k.dtype() == new_v.dtype() &&\n                 new_k.dtype() == k_cache.dtype(),\n             \"new_k, new_v, k_cache and v_cache must be of the same data type.\");\n  NVTE_CHECK(qkv_format == NVTE_QKV_Format::NVTE_BSHD || qkv_format == NVTE_QKV_Format::NVTE_SBHD ||\n                 qkv_format == NVTE_QKV_Format::NVTE_THD,\n             \"qkv_format must be {BSHD, SBHD, THD}.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      k_cache.dtype(), dtype,\n      copy_to_kv_cache_launcher<dtype>(new_k, new_v, k_cache, v_cache, page_table, cu_new_lens,\n                                       cu_cached_lens, qkv_format, h_kv, d_k, d_v, b, max_ctx_len,\n                                       max_seq_len, max_pages_per_seq, is_non_paged, stream););\n}\n\ntemplate <typename scalar_t>\n__global__ void convert_thd_to_bshd_kernel(scalar_t *tensor, scalar_t *new_tensor, int *cu_seqlens,\n                                           int b, int max_seq_len, int h, int d) {\n  // tensor: thd; new_tensor: bshd\n  // cu_seqlens: [b + 1]\n  for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n    int num_elts = (cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx]) * h * d;\n    int thd_offset = cu_seqlens[batch_idx] * h * d;\n    int bshd_offset = batch_idx * max_seq_len * h * d;\n    scalar_t *thd_token = tensor + thd_offset;\n    scalar_t *bshd_token = new_tensor + bshd_offset;\n    for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n      *(bshd_token + i) = *(thd_token + i);\n    }\n  }\n}\n\ntemplate <typename scalar_t>\nvoid convert_thd_to_bshd_launcher(Tensor tensor, Tensor new_tensor, Tensor cu_seqlens, int b,\n                                  int max_seq_len, int h, int d, hipStream_t stream) {\n  using namespace transformer_engine;\n  convert_thd_to_bshd_kernel<<<16, 256, 0, stream>>>(\n      reinterpret_cast<scalar_t *>(tensor.data.dptr),\n      reinterpret_cast<scalar_t *>(new_tensor.data.dptr),\n      reinterpret_cast<int *>(cu_seqlens.data.dptr), b, max_seq_len, h, d);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid convert_thd_to_bshd(Tensor tensor, Tensor cu_seqlens, Tensor new_tensor, int b,\n                         int max_seq_len, hipStream_t stream) {\n  using namespace transformer_engine;\n\n  auto tensor_shape = tensor.shape();\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      new_tensor.dtype(), dtype,\n      convert_thd_to_bshd_launcher<dtype>(tensor, new_tensor, cu_seqlens, b, max_seq_len,\n                                          tensor_shape[1], tensor_shape[2], stream););\n}\n\ntemplate <typename scalar_t>\n__global__ void convert_bshd_to_thd_kernel(scalar_t *tensor, scalar_t *new_tensor, int *cu_seqlens,\n                                           int b, int max_seq_len, int h, int d) {\n  // tensor: bshd; new_tensor: thd\n  // cu_seqlens: [b + 1]\n  for (int batch_idx = blockIdx.x; batch_idx < b; batch_idx += gridDim.x) {\n    int seqlen = cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx];\n    int num_elts = seqlen * h * d;\n    int bshd_offset = batch_idx * max_seq_len * h * d;\n    int thd_offset = cu_seqlens[batch_idx] * h * d;\n    scalar_t *bshd_token = tensor + bshd_offset;\n    scalar_t *thd_token = new_tensor + thd_offset;\n    for (int i = threadIdx.x; i < num_elts; i += blockDim.x) {\n      *(thd_token + i) = *(bshd_token + i);\n    }\n  }\n}\n\ntemplate <typename scalar_t>\nvoid convert_bshd_to_thd_launcher(Tensor tensor, Tensor new_tensor, Tensor cu_seqlens, int b,\n                                  int max_seq_len, int h, int d, hipStream_t stream) {\n  using namespace transformer_engine;\n  convert_bshd_to_thd_kernel<<<16, 256, 0, stream>>>(\n      reinterpret_cast<scalar_t *>(tensor.data.dptr),\n      reinterpret_cast<scalar_t *>(new_tensor.data.dptr),\n      reinterpret_cast<int *>(cu_seqlens.data.dptr), b, max_seq_len, h, d);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid convert_bshd_to_thd(Tensor tensor, Tensor cu_seqlens, Tensor new_tensor, int t,\n                         hipStream_t stream) {\n  using namespace transformer_engine;\n\n  auto tensor_shape = tensor.shape();\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FLOAT(\n      tensor.dtype(), dtype,\n      convert_bshd_to_thd_launcher<dtype>(tensor, new_tensor, cu_seqlens, tensor_shape[0],\n                                          tensor_shape[1], tensor_shape[2], tensor_shape[3],\n                                          stream););\n}\n\n}  // namespace kv_cache\n}  // namespace transformer_engine\n\n/***************************************************************************************************\n * KV Cache: Copy new KV tokens to the KV cache\n *   1. new_k and new_v are in qkv_format; k_cache and v_cache are in 'bshd' format\n *   2. cu_new_lens and cu_cached_lens are of shape [b + 1]; cu_cached_lens include the added lens\n *      in current step\n *   3. Non-paged KV cache is a special case of paged KV cache, with page_table = [b, 1] and\n *      max_pages_per_seq = 1. We use the same underlying kernel for both non-paged and paged.\n *      Set is_non_paged = True/False to indicate as such.\n *   4. is_non_paged = True also re-indexes the KV cache, e.g. the initial batch indices [0, 3, 1, 2]\n *      becomes [0, 1, 1, 2]. The page_table = batch_indices.unsqueeze(1) is however unchanged.\n *      batch_indices_post can be used for monotonical indexing, i.e. [0, 1, 2, 3]. batch_indices is\n *      preserved for the next layer in the same iteration.\n *   5. Only supports same page_table for k_cache and v_cache\n *   6. Only pad_between_seqs = False when qkv_format = thd, i.e. there should be no pad tokens\n *      between sequences in new_k and new_v such as [a a a 0..0 b b 0..0 c 0..0].\n **************************************************************************************************/\n\nvoid nvte_copy_to_kv_cache(NVTETensor new_k, NVTETensor new_v, NVTETensor k_cache,\n                           NVTETensor v_cache, NVTETensor page_table, NVTETensor cu_new_lens,\n                           NVTETensor cu_cached_lens, NVTE_QKV_Format qkv_format, int b,\n                           int max_ctx_len, int max_seq_len, int max_pages_per_seq,\n                           int is_non_paged, hipStream_t stream) {\n  NVTE_API_CALL(nvte_copy_to_kv_cache);\n  using namespace transformer_engine;\n\n  kv_cache::copy_to_kv_cache(*convertNVTETensorCheck(new_k), *convertNVTETensorCheck(new_v),\n                             *convertNVTETensorCheck(k_cache), *convertNVTETensorCheck(v_cache),\n                             *convertNVTETensorCheck(page_table),\n                             *convertNVTETensorCheck(cu_new_lens),\n                             *convertNVTETensorCheck(cu_cached_lens), qkv_format, b, max_ctx_len,\n                             max_seq_len, max_pages_per_seq, is_non_paged, stream);\n}\n\n/***************************************************************************************************\n * KV Cache: Convert a tensor from qkv_format = thd to qkv_format = bshd\n **************************************************************************************************/\n\nvoid nvte_convert_thd_to_bshd(NVTETensor tensor, NVTETensor cu_seqlens, NVTETensor new_tensor,\n                              int b, int max_seq_len, hipStream_t stream) {\n  NVTE_API_CALL(nvte_convert_thd_to_bshd);\n  using namespace transformer_engine;\n\n  kv_cache::convert_thd_to_bshd(*convertNVTETensorCheck(tensor),\n                                *convertNVTETensorCheck(cu_seqlens),\n                                *convertNVTETensorCheck(new_tensor), b, max_seq_len, stream);\n}\n\n/***************************************************************************************************\n * KV Cache: Convert a tensor from qkv_format = bshd to qkv_format = thd\n **************************************************************************************************/\n\nvoid nvte_convert_bshd_to_thd(NVTETensor tensor, NVTETensor cu_seqlens, NVTETensor new_tensor,\n                              int t, hipStream_t stream) {\n  NVTE_API_CALL(nvte_convert_bshd_to_thd);\n  using namespace transformer_engine;\n\n  kv_cache::convert_bshd_to_thd(*convertNVTETensorCheck(tensor),\n                                *convertNVTETensorCheck(cu_seqlens),\n                                *convertNVTETensorCheck(new_tensor), t, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_attn/kv_cache.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n\n#include \"../common.h\"\n#include \"../util/cuda_driver.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\n\n// ============================================================================\n// prepare_flash_attn: SBH3D <-> BSHD_BSHD_BSHD for the FlashAttention backend\n// ============================================================================\n\nnamespace flash_attention {\n\n/// Packed vector of N elements of T; alignment matches a single wide load/store of N * sizeof(T) bytes.\ntemplate <typename T, int N>\nstruct alignas(sizeof(T) * N) Vec {\n  T data[N];\n};\n\nconstexpr int warp_size = 32;\nconstexpr int type_size = 2;  // FP16 or BF16\nconstexpr int nvec = sizeof(uint64_t) / type_size;\nconstexpr int load_size = warp_size * nvec;\nconstexpr int block_size = 512;\n\ntemplate <typename T>\n__launch_bounds__(block_size) __global__\n    void prepare_kernel_fwd(const T *qkvi, T *qkv, const size_t B, const size_t S, const size_t Z,\n                            const size_t W) {\n  const int warpid = (blockDim.x * blockIdx.x + threadIdx.x) / warp_size;\n  const int id_in_warp = threadIdx.x % warp_size;\n  const size_t offset_input = blockIdx.y * W + warpid * 3 * W * Z + id_in_warp * nvec;\n  const T *my_input = qkvi + offset_input;\n\n  const size_t s = warpid / B;\n  if (s >= S) return;\n\n  const size_t b = warpid % B;\n\n  const size_t offset_output = blockIdx.y * B * S * Z * W + (s + b * S) * W * Z + id_in_warp * nvec;\n\n  T *my_output = qkv + offset_output;\n\n  for (int i = 0; i < Z; ++i) {\n    Vec<T, nvec> *const out = reinterpret_cast<Vec<T, nvec> *>(my_output + i * load_size);\n    *out = *reinterpret_cast<const Vec<T, nvec> *>(my_input + i * load_size * 3);\n  }\n}\n\ntemplate <typename T>\n__launch_bounds__(block_size) __global__\n    void prepare_kernel_bwd(const T *q, const T *k, const T *v, T *qkv, const size_t B,\n                            const size_t S, const size_t Z, const size_t W) {\n  const T *input = blockIdx.y == 0 ? q : (blockIdx.y == 1 ? k : v);\n\n  const int warpid = (blockDim.x * blockIdx.x + threadIdx.x) / warp_size;\n  const int id_in_warp = threadIdx.x % warp_size;\n  const size_t offset_input = warpid * W * Z + id_in_warp * nvec;\n  const T *my_input = input + offset_input;\n\n  const size_t b = warpid / S;\n  if (b >= B) return;\n\n  const size_t s = warpid % S;\n\n  const size_t offset_output = (b + s * B) * 3 * W * Z + id_in_warp * nvec + blockIdx.y * W;\n\n  T *my_output = qkv + offset_output;\n\n  for (int i = 0; i < Z; ++i) {\n    Vec<T, nvec> *const out = reinterpret_cast<Vec<T, nvec> *>(my_output + i * load_size * 3);\n    *out = *reinterpret_cast<const Vec<T, nvec> *>(my_input + i * load_size);\n  }\n}\n\nvoid prepare_flash_attn_fwd(Tensor qkvi, Tensor qkv, cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  NVTE_CHECK(qkvi.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(qkvi.dtype() == DType::kFloat16 || qkvi.dtype() == DType::kBFloat16);\n\n  auto qkvi_shape = qkvi.shape();\n\n  NVTE_CHECK(qkvi_shape[3] % load_size == 0);\n  NVTE_CHECK(qkvi_shape[3] == load_size);\n\n  // [s, b, n, h * 3] -> [3, b, s, n, h]\n  std::vector<uint64_t> shape = {3, qkvi_shape[1], qkvi_shape[0], qkvi_shape[2], qkvi_shape[3]};\n\n  size_t warps = qkvi_shape[0] * qkvi_shape[1];\n  size_t warps_per_block = block_size / warp_size;\n  size_t blocks = (warps + warps_per_block - 1) / warps_per_block;\n  dim3 grid(blocks, 3);\n  int threads = block_size;\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      qkvi.dtype(), dtype,\n      prepare_kernel_fwd<dtype><<<grid, threads, 0, stream>>>(\n          reinterpret_cast<dtype *>(qkvi.data.dptr), reinterpret_cast<dtype *>(qkv.data.dptr),\n          shape[1], shape[2], shape[3], shape[4]););\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid prepare_flash_attn_bwd(Tensor q, Tensor k, Tensor v, Tensor qkv, cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  NVTE_CHECK(q.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(k.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(v.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(q.dtype() == DType::kFloat16 || q.dtype() == DType::kBFloat16);\n  NVTE_CHECK(k.dtype() == q.dtype());\n  NVTE_CHECK(v.dtype() == q.dtype());\n\n  auto q_shape = q.shape();\n  auto k_shape = k.shape();\n  auto v_shape = v.shape();\n\n  NVTE_CHECK(q_shape[3] % load_size == 0);\n  NVTE_CHECK(q_shape[3] == load_size);\n  NVTE_CHECK(k_shape[3] % load_size == 0);\n  NVTE_CHECK(k_shape[3] == load_size);\n  NVTE_CHECK(v_shape[3] % load_size == 0);\n  NVTE_CHECK(v_shape[3] == load_size);\n\n  // 3 x [s, b, n, h] -> [b, s, n, 3 * h]\n  std::vector<uint64_t> shape = {q_shape[1], q_shape[0], q_shape[2], 3 * q_shape[3]};\n\n  size_t warps = q_shape[0] * q_shape[1];\n  size_t warps_per_block = block_size / warp_size;\n  size_t blocks = (warps + warps_per_block - 1) / warps_per_block;\n  dim3 grid(blocks, 3);\n  int threads = block_size;\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      q.dtype(), dtype,\n      prepare_kernel_bwd<dtype><<<grid, threads, 0, stream>>>(\n          reinterpret_cast<dtype *>(q.data.dptr), reinterpret_cast<dtype *>(k.data.dptr),\n          reinterpret_cast<dtype *>(v.data.dptr), reinterpret_cast<dtype *>(qkv.data.dptr),\n          q_shape[0], q_shape[1], q_shape[2], q_shape[3]););\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace flash_attention\n\n// ============================================================================\n// multi_tensor_transpose_to_bhsd: BSHD/SBHD -> BHSD\n// ============================================================================\n\nnamespace multi_tensor_transpose_to_bhsd {\n\nusing flash_attention::Vec;\n\nconstexpr int kMaxPermuteTensors = 16;\n\nstruct PermuteSlot {\n  const void *input;\n  void *output;\n  size_t S, H, D_in, D_out;\n};\n\nstruct PermuteParams {\n  PermuteSlot slots[kMaxPermuteTensors];\n};\n\nstruct TmaMapParams {\n  CUtensorMap maps[kMaxPermuteTensors];\n};\n\n// ---------- path 3: fallback_not_vec_aligned ----------\n\n__device__ __forceinline__ void copy_row_bytes(const char *__restrict__ src, char *__restrict__ dst,\n                                               size_t D_bytes) {\n  size_t off = 0;\n  for (; off + 16 <= D_bytes; off += 16) {\n    uint4 tmp;\n    memcpy(&tmp, src + off, 16);\n    memcpy(dst + off, &tmp, 16);\n  }\n  for (; off + 8 <= D_bytes; off += 8) {\n    uint2 tmp;\n    memcpy(&tmp, src + off, 8);\n    memcpy(dst + off, &tmp, 8);\n  }\n  for (; off + 4 <= D_bytes; off += 4) {\n    unsigned int tmp;\n    memcpy(&tmp, src + off, 4);\n    memcpy(dst + off, &tmp, 4);\n  }\n  for (; off + 2 <= D_bytes; off += 2) {\n    uint16_t tmp;\n    memcpy(&tmp, src + off, 2);\n    memcpy(dst + off, &tmp, 2);\n  }\n  for (; off < D_bytes; ++off) dst[off] = src[off];\n}\n\n__device__ __forceinline__ void copy_and_pad_row_bytes(const char *__restrict__ src,\n                                                       char *__restrict__ dst, size_t D_bytes,\n                                                       size_t D_out_bytes) {\n  copy_row_bytes(src, dst, D_bytes);\n  for (size_t off = D_bytes; off < D_out_bytes; ++off) dst[off] = 0;\n}\n\nconstexpr int TRANSPOSE_TILE = 32;\nconstexpr int TRANSPOSE_BLOCK = 256;\nconstexpr int TRANSPOSE_WARPS = TRANSPOSE_BLOCK / 32;  // 8\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(TRANSPOSE_BLOCK) __global__\n    void transpose_to_bhsd_fallback_not_vec_aligned_kernel(PermuteParams params, size_t b,\n                                                           unsigned int s_tiles) {\n  const auto &slot = params.slots[blockIdx.z];\n  const T *__restrict__ in = reinterpret_cast<const T *>(slot.input);\n  T *__restrict__ out = reinterpret_cast<T *>(slot.output);\n  const size_t S = slot.S;\n  const size_t H = slot.H;\n  const size_t D = slot.D_in;\n  const size_t D_out = slot.D_out;\n  const size_t D_bytes = D * sizeof(T);\n  const size_t D_out_bytes = D_out * sizeof(T);\n  const size_t D_smem_pad = (D_bytes + 3u) & ~size_t(3);\n\n  const size_t tile_s = static_cast<size_t>(blockIdx.x) % static_cast<size_t>(s_tiles);\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / static_cast<size_t>(s_tiles);\n  if (b_i >= b) return;\n  const size_t tile_h = static_cast<size_t>(blockIdx.y);\n\n  const size_t s_base = tile_s * TRANSPOSE_TILE;\n  const size_t h_base = tile_h * TRANSPOSE_TILE;\n\n  extern __shared__ char smem[];\n  const size_t smem_row = static_cast<size_t>(TRANSPOSE_TILE) * D_smem_pad + 4;\n\n  // ---- Phase 1: global \u2192 smem (sweep consecutive H \u2192 coalesced reads) ----\n  for (unsigned int warp_off = threadIdx.x >> 5; warp_off < TRANSPOSE_TILE;\n       warp_off += TRANSPOSE_WARPS) {\n    const size_t local_s = warp_off;\n    const size_t local_h = threadIdx.x & 31u;\n    const size_t s_i = s_base + local_s;\n    const size_t h_i = h_base + local_h;\n    if (s_i < S && h_i < H) {\n      const char *__restrict__ src;\n      if constexpr (kIsBshd)\n        src = reinterpret_cast<const char *>(in + b_i * S * H * D + s_i * H * D + h_i * D);\n      else\n        src = reinterpret_cast<const char *>(in + s_i * b * H * D + b_i * H * D + h_i * D);\n      copy_row_bytes(src, smem + local_s * smem_row + local_h * D_smem_pad, D_bytes);\n    }\n  }\n\n  __syncthreads();\n\n  // ---- Phase 2: smem \u2192 global (sweep consecutive S \u2192 coalesced writes, with padding) ----\n  for (unsigned int warp_off = threadIdx.x >> 5; warp_off < TRANSPOSE_TILE;\n       warp_off += TRANSPOSE_WARPS) {\n    const size_t local_h = warp_off;\n    const size_t local_s = threadIdx.x & 31u;\n    const size_t s_i = s_base + local_s;\n    const size_t h_i = h_base + local_h;\n    if (s_i < S && h_i < H) {\n      copy_and_pad_row_bytes(\n          smem + local_s * smem_row + local_h * D_smem_pad,\n          reinterpret_cast<char *>(out + b_i * H * S * D_out + h_i * S * D_out + s_i * D_out),\n          D_bytes, D_out_bytes);\n    }\n  }\n}\n\n// ---------- path 2: fallback_vec_aligned ----------\n\nconstexpr int fallback_permute_threads = 1024;\n\ntemplate <typename T, bool kIsBshd, int N>\n__device__ __forceinline__ void permute_vec_loop(const T *__restrict__ in, T *__restrict__ out,\n                                                 size_t b, size_t S, size_t H, size_t D,\n                                                 size_t D_out, size_t b_i, size_t h_i,\n                                                 size_t s_begin, size_t S_chunk) {\n  const size_t out_base = b_i * H * S * D_out + h_i * S * D_out;\n  const size_t d_vec = D / static_cast<size_t>(N);\n  const size_t total_work = S_chunk * d_vec;\n  for (size_t w = static_cast<size_t>(threadIdx.x); w < total_work;\n       w += static_cast<size_t>(blockDim.x)) {\n    const size_t s_local = w / d_vec;\n    const size_t s_i = s_begin + s_local;\n    const size_t d_off = (w % d_vec) * static_cast<size_t>(N);\n    const T *__restrict__ in_ptr;\n    if constexpr (kIsBshd) {\n      in_ptr = in + b_i * (S * H * D) + s_i * (H * D) + h_i * D + d_off;\n    } else {\n      in_ptr = in + s_i * (b * H * D) + b_i * (H * D) + h_i * D + d_off;\n    }\n    T *__restrict__ out_ptr = out + out_base + s_i * D_out + d_off;\n    *reinterpret_cast<Vec<T, N> *>(out_ptr) = *reinterpret_cast<const Vec<T, N> *>(in_ptr);\n  }\n  if (D_out > D) {\n    const size_t pad_elems = D_out - D;\n    const size_t total_pad = S_chunk * pad_elems;\n    for (size_t w = static_cast<size_t>(threadIdx.x); w < total_pad;\n         w += static_cast<size_t>(blockDim.x)) {\n      const size_t s_local = w / pad_elems;\n      const size_t s_i = s_begin + s_local;\n      const size_t d_off = D + (w % pad_elems);\n      out[out_base + s_i * D_out + d_off] = static_cast<T>(0.f);\n    }\n  }\n}\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(fallback_permute_threads) __global__\n    void transpose_to_bhsd_fallback_vec_aligned_kernel(PermuteParams params, size_t b,\n                                                       unsigned int permute_s_splits,\n                                                       size_t h_grid) {\n  const auto &slot = params.slots[blockIdx.z];\n  const T *__restrict__ in = reinterpret_cast<const T *>(slot.input);\n  T *__restrict__ out = reinterpret_cast<T *>(slot.output);\n  const size_t S = slot.S;\n  const size_t H = slot.H;\n  const size_t D = slot.D_in;\n  const size_t D_out = slot.D_out;\n\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / h_grid;\n  const size_t h_i = static_cast<size_t>(blockIdx.x) % h_grid;\n  if (b_i >= b) return;\n  if (h_i >= H) return;\n\n  const unsigned int s_part = blockIdx.y;\n  const size_t s_begin = (S * static_cast<size_t>(s_part)) / static_cast<size_t>(permute_s_splits);\n  const size_t s_end =\n      (S * static_cast<size_t>(s_part + 1)) / static_cast<size_t>(permute_s_splits);\n  if (s_begin >= s_end) return;\n  const size_t S_chunk = s_end - s_begin;\n\n  const size_t D_bytes = D * sizeof(T);\n\n  if (D_bytes % 16 == 0) {\n    constexpr size_t N = 16 / sizeof(T);\n    permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n    return;\n  }\n  if (D_bytes % 8 == 0) {\n    constexpr size_t N = 8 / sizeof(T);\n    permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n    return;\n  }\n  if constexpr (sizeof(T) <= 4) {\n    if (D_bytes % 4 == 0) {\n      constexpr size_t N = 4 / sizeof(T);\n      permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n      return;\n    }\n  }\n}\n\n// ---------- path 1: TMA ----------\n\nconstexpr int tma_permute_threads = 128;\nconstexpr int tma_permute_s_tile_default = 32;\n\n__device__ __forceinline__ void cp_async_bulk_tensor_4d_global_to_shared(\n    void *dst_shmem, const CUtensorMap *tensor_map, uint32_t c0, uint32_t c1, uint32_t c2,\n    uint32_t c3, uint64_t *mbar) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  uint32_t dst = __cvta_generic_to_shared(dst_shmem);\n  uint32_t bar = __cvta_generic_to_shared(mbar);\n  asm volatile(\n      \"cp.async.bulk.tensor.4d.shared::cluster.global.tile\"\n      \".mbarrier::complete_tx::bytes [%0], [%1, {%2, %3, %4, %5}], [%6];\" ::\"r\"(dst),\n      \"l\"(tensor_map), \"r\"(c0), \"r\"(c1), \"r\"(c2), \"r\"(c3), \"r\"(bar)\n      : \"memory\");\n#else\n  NVTE_DEVICE_ERROR(\"cp_async_bulk_tensor_4d_global_to_shared requires SM 10.0+.\");\n#endif\n}\n\n__device__ __forceinline__ void cp_async_bulk_tensor_4d_shared_to_global(\n    const CUtensorMap *tensor_map, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3,\n    void *src_shmem) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  uint32_t src = __cvta_generic_to_shared(src_shmem);\n  asm volatile(\n      \"cp.async.bulk.tensor.4d.global.shared::cta.bulk_group\"\n      \" [%0, {%1, %2, %3, %4}], [%5];\" ::\"l\"(tensor_map),\n      \"r\"(c0), \"r\"(c1), \"r\"(c2), \"r\"(c3), \"r\"(src)\n      : \"memory\");\n#else\n  NVTE_DEVICE_ERROR(\"cp_async_bulk_tensor_4d_shared_to_global requires SM 10.0+.\");\n#endif\n}\n\nstatic void create_4D_tensor_map(CUtensorMap &tensorMap, void *dataPtr, DType dtype, uint64_t dim0,\n                                 uint64_t dim1, uint64_t dim2, uint64_t dim3, uint32_t box0,\n                                 uint32_t box1, uint32_t box2, uint32_t box3) {\n  cuda_driver::ensure_context_exists();\n  static PFN_cuTensorMapEncodeTiled_v12000 cuDriverTensorMapEncodeTiled = []() {\n    void *ptr = cuda_driver::get_symbol(\"cuTensorMapEncodeTiled\");\n    return reinterpret_cast<PFN_cuTensorMapEncodeTiled_v12000>(ptr);\n  }();\n\n  CUtensorMapDataType tma_dtype;\n  size_t elem_bytes;\n  switch (dtype) {\n    case DType::kFloat16:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_FLOAT16;\n      elem_bytes = 2;\n      break;\n    case DType::kBFloat16:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_BFLOAT16;\n      elem_bytes = 2;\n      break;\n    case DType::kFloat8E4M3:\n    case DType::kFloat8E5M2:\n    case DType::kFloat8E8M0:\n    case DType::kByte:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_UINT8;\n      elem_bytes = 1;\n      break;\n    default:\n      NVTE_ERROR(\"create_4D_tensor_map: unsupported dtype \", to_string(static_cast<DType>(dtype)));\n  }\n\n  constexpr uint32_t rank = 4;\n  uint64_t size[rank] = {dim0, dim1, dim2, dim3};\n  uint64_t stride[rank - 1] = {\n      dim0 * elem_bytes,\n      dim0 * dim1 * elem_bytes,\n      dim0 * dim1 * dim2 * elem_bytes,\n  };\n  uint32_t boxSize[rank] = {box0, box1, box2, box3};\n  uint32_t elemStride[rank] = {1, 1, 1, 1};\n\n  const auto oob_fill = (tma_dtype == CU_TENSOR_MAP_DATA_TYPE_UINT8)\n                            ? CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE\n                            : CU_TENSOR_MAP_FLOAT_OOB_FILL_NAN_REQUEST_ZERO_FMA;\n\n  NVTE_CHECK_CUDA_DRIVER(cuDriverTensorMapEncodeTiled(\n      &tensorMap, tma_dtype, rank, dataPtr, size, stride, boxSize, elemStride,\n      CU_TENSOR_MAP_INTERLEAVE_NONE, CU_TENSOR_MAP_SWIZZLE_NONE, CU_TENSOR_MAP_L2_PROMOTION_NONE,\n      oob_fill));\n}\n\ntemplate <typename T, bool kIsBshd>\n__device__ __forceinline__ void issue_tma_load_strided(T *smem_buf, const CUtensorMap *tma,\n                                                       size_t h_i, size_t s_tile, size_t b_i,\n                                                       uint64_t *mbar, size_t tile_bytes) {\n  ptx::mbarrier_arrive_expect_tx(mbar, static_cast<uint32_t>(tile_bytes));\n  if constexpr (kIsBshd) {\n    cp_async_bulk_tensor_4d_global_to_shared(smem_buf, tma, 0, static_cast<uint32_t>(h_i),\n                                             static_cast<uint32_t>(s_tile),\n                                             static_cast<uint32_t>(b_i), mbar);\n  } else {\n    cp_async_bulk_tensor_4d_global_to_shared(smem_buf, tma, 0, static_cast<uint32_t>(h_i),\n                                             static_cast<uint32_t>(b_i),\n                                             static_cast<uint32_t>(s_tile), mbar);\n  }\n}\n\n__device__ __forceinline__ void st_global_cs_uint4(uint4 *ptr, uint4 val) {\n  asm volatile(\"st.global.cs.v4.b32 [%0], {%1, %2, %3, %4};\" ::\"l\"(ptr), \"r\"(val.x), \"r\"(val.y),\n               \"r\"(val.z), \"r\"(val.w)\n               : \"memory\");\n}\n// TMA loads from strided input to smem + non-temporal stores to contiguous output in gmem\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(tma_permute_threads) __global__\n    void transpose_to_bhsd_kernel(const __grid_constant__ TmaMapParams tma_maps,\n                                  PermuteParams params, size_t b, size_t h_grid,\n                                  unsigned int permute_s_splits, size_t s_tile_size) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  const auto &slot = params.slots[blockIdx.z];\n  const CUtensorMap *tma_in = &tma_maps.maps[blockIdx.z];\n  T *__restrict__ tensor_out = reinterpret_cast<T *>(slot.output);\n  const size_t Sdim = slot.S;\n  const size_t Hdim = slot.H;\n  const size_t Ddim = slot.D_in;\n  const size_t Ddim_out = slot.D_out;\n\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / h_grid;\n  const size_t h_i = static_cast<size_t>(blockIdx.x) % h_grid;\n\n  if (b_i >= b) return;\n  if (h_i >= Hdim) return;\n\n  const unsigned int s_part = blockIdx.y;\n  const size_t s_begin =\n      (Sdim * static_cast<size_t>(s_part)) / static_cast<size_t>(permute_s_splits);\n  const size_t s_end =\n      (Sdim * static_cast<size_t>(s_part + 1)) / static_cast<size_t>(permute_s_splits);\n  if (s_begin >= s_end) return;\n\n  const size_t out_base = b_i * Hdim * Sdim * Ddim_out + h_i * Sdim * Ddim_out;\n\n  extern __shared__ __align__(128) char smem_raw[];\n  T *smem = reinterpret_cast<T *>(smem_raw);\n\n  __shared__ __align__(8) uint64_t mbar;\n  const bool is_leader = (threadIdx.x == 0);\n\n  if (is_leader) {\n    ptx::mbarrier_init(&mbar, static_cast<uint32_t>(blockDim.x));\n    ptx::fence_proxy_async_shared_cta();\n  }\n  __syncthreads();\n\n  const size_t S_TILE = s_tile_size;\n  const uint32_t tile_bytes = static_cast<uint32_t>(S_TILE * Ddim * sizeof(T));\n  int parity = 0;\n\n  for (size_t s_tile = s_begin; s_tile < s_end; s_tile += S_TILE) {\n    const size_t tile_rows = min(S_TILE, s_end - s_tile);\n\n    if (is_leader) {\n      issue_tma_load_strided<T, kIsBshd>(smem, tma_in, h_i, s_tile, b_i, &mbar, tile_bytes);\n    } else {\n      ptx::mbarrier_arrive(&mbar);\n    }\n\n    ptx::mbarrier_wait_parity(&mbar, parity);\n    parity ^= 1;\n\n    T *__restrict__ out_ptr = tensor_out + out_base + s_tile * Ddim_out;\n    constexpr size_t vec_elems = sizeof(uint4) / sizeof(T);\n\n    if (Ddim_out == Ddim) {\n      const size_t total_elems = tile_rows * Ddim;\n      for (size_t i = threadIdx.x * vec_elems; i < total_elems;\n           i += static_cast<size_t>(blockDim.x) * vec_elems) {\n        uint4 v = *reinterpret_cast<const uint4 *>(smem + i);\n        st_global_cs_uint4(reinterpret_cast<uint4 *>(out_ptr + i), v);\n      }\n    } else {\n      const size_t total_out_elems = tile_rows * Ddim_out;\n      for (size_t i = threadIdx.x * vec_elems; i < total_out_elems;\n           i += static_cast<size_t>(blockDim.x) * vec_elems) {\n        const size_t row = i / Ddim_out;\n        const size_t col = i % Ddim_out;\n        uint4 v;\n        if (col + vec_elems <= Ddim) {\n          v = *reinterpret_cast<const uint4 *>(smem + row * Ddim + col);\n        } else {\n          memset(&v, 0, sizeof(v));\n          const size_t smem_off = row * Ddim + col;\n          size_t copy_elems = (col < Ddim) ? (Ddim - col) : 0;\n          if (copy_elems > 0) memcpy(&v, smem + smem_off, copy_elems * sizeof(T));\n        }\n        st_global_cs_uint4(reinterpret_cast<uint4 *>(out_ptr + i), v);\n      }\n    }\n\n    __syncthreads();\n  }\n\n  if (is_leader) {\n    ptx::mbarrier_invalid(&mbar);\n  }\n#endif\n}\n\n// 4D TMA descriptor:\n// [B, S, H, D]: TMA dims [D, H, S, B], box [D, 1, S_TILE, 1]\n// [S, B, H, D]: TMA dims [D, H, B, S], box [D, 1, 1, S_TILE]\n\nstatic void create_strided_tensor_map(CUtensorMap &map, void *ptr, DType dtype, size_t b, size_t s,\n                                      size_t h, size_t d, size_t s_tile, bool is_bshd) {\n  if (is_bshd) {\n    create_4D_tensor_map(map, ptr, dtype, static_cast<uint64_t>(d), static_cast<uint64_t>(h),\n                         static_cast<uint64_t>(s), static_cast<uint64_t>(b),\n                         static_cast<uint32_t>(d), 1, static_cast<uint32_t>(s_tile), 1);\n  } else {\n    create_4D_tensor_map(map, ptr, dtype, static_cast<uint64_t>(d), static_cast<uint64_t>(h),\n                         static_cast<uint64_t>(b), static_cast<uint64_t>(s),\n                         static_cast<uint32_t>(d), 1, 1, static_cast<uint32_t>(s_tile));\n  }\n}\n\nvoid multi_tensor_transpose_to_bhsd(Tensor *inputs, Tensor *outputs, size_t num_tensors,\n                                    NVTE_QKV_Format original_format, cudaStream_t stream) {\n  using namespace transformer_engine;\n  if (num_tensors == 0) return;\n  NVTE_CHECK(num_tensors <= static_cast<size_t>(kMaxPermuteTensors), \"num_tensors must be in [1, \",\n             kMaxPermuteTensors, \"], got \", num_tensors, \".\");\n\n  const bool is_bshd = (original_format == NVTE_QKV_Format::NVTE_BSHD);\n  const DType dtype = inputs[0].dtype();\n  const size_t elem_size = typeToSize(dtype);\n  const size_t b = outputs[0].shape()[0];\n\n  PermuteParams params{};\n  size_t s_max = 0, h_max = 0, s_min = SIZE_MAX;\n  size_t d_in_max = 0, d_out_max = 0;\n  bool any_not_vec_aligned = false;\n  bool all_tma_ok = true;\n\n  for (size_t i = 0; i < num_tensors; ++i) {\n    const size_t H = outputs[i].shape()[1];\n    const size_t S = outputs[i].shape()[2];\n    const size_t D_in = inputs[i].shape()[inputs[i].shape().size() - 1];\n    const size_t D_out = outputs[i].shape()[3];\n    params.slots[i] = {inputs[i].data.dptr, outputs[i].data.dptr, S, H, D_in, D_out};\n    s_max = std::max(s_max, S);\n    h_max = std::max(h_max, H);\n    s_min = std::min(s_min, S);\n    d_in_max = std::max(d_in_max, D_in);\n    d_out_max = std::max(d_out_max, D_out);\n    if ((D_in * elem_size) % 4 != 0) any_not_vec_aligned = true;\n    const size_t inner = D_in * elem_size;\n    if (inner < 32 || inner % 16 != 0) all_tma_ok = false;\n  }\n\n  if (all_tma_ok) {\n    const int sm = cuda::sm_arch(cuda::current_device());\n    if (sm < 100) {\n      all_tma_ok = false;\n    } else {\n      switch (dtype) {\n        case DType::kFloat16:\n        case DType::kBFloat16:\n        case DType::kFloat8E4M3:\n        case DType::kFloat8E5M2:\n        case DType::kFloat8E8M0:\n        case DType::kByte:\n          break;\n        default:\n          all_tma_ok = false;\n      }\n    }\n  }\n\n  // Dispatch order:\n  //  1. TMA path: SM 10.0+, D_in*elem >= 32 && 16-aligned, supported dtype,\n  //     and s_tile*D_in*elem is uint4-aligned.\n  //  2. Fallback path (vec-aligned): vectorized loads/stores when D_in*elem % 4 == 0.\n  //  3. Fallback path (not-vec-aligned): shared-memory transpose when D_in*elem % 4 != 0.\n  if (all_tma_ok) {\n    const size_t s_tile = std::min(static_cast<size_t>(tma_permute_s_tile_default), s_min);\n    bool tma_aligned = true;\n    for (size_t i = 0; i < num_tensors && tma_aligned; ++i) {\n      if ((s_tile * params.slots[i].D_in * elem_size) % sizeof(uint4) != 0) tma_aligned = false;\n    }\n\n    if (tma_aligned) {\n      TmaMapParams tma_maps{};\n      for (size_t i = 0; i < num_tensors; ++i) {\n        const auto &slot = params.slots[i];\n        create_strided_tensor_map(tma_maps.maps[i], const_cast<void *>(slot.input), dtype, b,\n                                  slot.S, slot.H, slot.D_in, s_tile, is_bshd);\n      }\n\n      const unsigned int permute_s_splits = std::max(1u, static_cast<unsigned int>(s_min / s_tile));\n      dim3 grid(static_cast<unsigned int>(b * h_max), permute_s_splits,\n                static_cast<unsigned int>(num_tensors));\n      const size_t smem_bytes = s_tile * d_in_max * elem_size;\n\n      if (is_bshd) {\n        TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n            dtype, dtype_t, auto kernel = transpose_to_bhsd_kernel<dtype_t, true>;\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n                kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n            kernel<<<grid, tma_permute_threads, smem_bytes, stream>>>(tma_maps, params, b, h_max,\n                                                                      permute_s_splits, s_tile););\n      } else {\n        TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n            dtype, dtype_t, auto kernel = transpose_to_bhsd_kernel<dtype_t, false>;\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n                kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n            kernel<<<grid, tma_permute_threads, smem_bytes, stream>>>(tma_maps, params, b, h_max,\n                                                                      permute_s_splits, s_tile););\n      }\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      return;\n    }\n  }\n\n  if (!any_not_vec_aligned) {\n    const unsigned int permute_s_splits = std::max(\n        1u, static_cast<unsigned int>(s_min / static_cast<size_t>(fallback_permute_threads)));\n    dim3 grid(static_cast<unsigned int>(b * h_max), permute_s_splits,\n              static_cast<unsigned int>(num_tensors));\n\n    if (is_bshd) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_vec_aligned_kernel<dtype_t, true>\n          <<<grid, fallback_permute_threads, 0, stream>>>(params, b, permute_s_splits, h_max););\n    } else {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_vec_aligned_kernel<dtype_t, false>\n          <<<grid, fallback_permute_threads, 0, stream>>>(params, b, permute_s_splits, h_max););\n    }\n  } else {\n    const unsigned int st =\n        static_cast<unsigned int>((s_max + TRANSPOSE_TILE - 1) / TRANSPOSE_TILE);\n    const unsigned int ht =\n        static_cast<unsigned int>((h_max + TRANSPOSE_TILE - 1) / TRANSPOSE_TILE);\n    dim3 grid(static_cast<unsigned int>(b) * st, ht, static_cast<unsigned int>(num_tensors));\n    const size_t D_pad = (d_in_max * elem_size + 3u) & ~size_t(3);\n    const size_t smem_bytes =\n        static_cast<size_t>(TRANSPOSE_TILE) * (static_cast<size_t>(TRANSPOSE_TILE) * D_pad + 4);\n\n    if (is_bshd) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_not_vec_aligned_kernel<dtype_t, true>\n          <<<grid, TRANSPOSE_BLOCK, smem_bytes, stream>>>(params, b, st););\n    } else {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_not_vec_aligned_kernel<dtype_t, false>\n          <<<grid, TRANSPOSE_BLOCK, smem_bytes, stream>>>(params, b, st););\n    }\n  }\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace multi_tensor_transpose_to_bhsd\n\n// ===================================================================================\n// multi_tensor_pad_last_dim: pad the last dim of multiple tensors to certain alignment\n// ===================================================================================\n\nnamespace multi_tensor_pad_last_dim {\n\nconstexpr int pad_threads_per_block = 256;\nconstexpr int kMaxPadTensors = 16;\n\nstruct PadLastDimArgs {\n  const uint8_t *input;\n  uint32_t *output;\n  size_t n_uint32;\n  uint32_t in_row_bytes;\n  uint32_t out_row_uint32;\n};\n\nstruct MultiPadParams {\n  PadLastDimArgs tensors[kMaxPadTensors];\n};\n\n__launch_bounds__(pad_threads_per_block) __global__\n    void multi_tensor_pad_last_dim_kernel(MultiPadParams params) {\n  const auto &a = params.tensors[blockIdx.y];\n\n  for (size_t idx = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x; idx < a.n_uint32;\n       idx += static_cast<size_t>(gridDim.x) * blockDim.x) {\n    const uint32_t col_byte = (idx % a.out_row_uint32) * 4;\n    const size_t row = idx / a.out_row_uint32;\n    const uint8_t *__restrict__ src = a.input + row * static_cast<size_t>(a.in_row_bytes);\n\n    uint32_t val;\n    if (col_byte + 4 <= a.in_row_bytes) {\n      memcpy(&val, src + col_byte, 4);\n    } else if (col_byte >= a.in_row_bytes) {\n      val = 0;\n    } else {\n      val = 0;\n      memcpy(&val, src + col_byte, a.in_row_bytes - col_byte);\n    }\n    a.output[idx] = val;\n  }\n}\n\nvoid launch_pad_batch(MultiPadParams &params, int kernel_count, size_t max_n_uint32,\n                      cudaStream_t stream) {\n  if (kernel_count == 0) return;\n  constexpr int threads = pad_threads_per_block;\n  const int blocks_x = static_cast<int>(\n      std::min(DIVUP(max_n_uint32, static_cast<size_t>(threads)), static_cast<size_t>(65535)));\n  dim3 grid(blocks_x, kernel_count);\n  multi_tensor_pad_last_dim_kernel<<<grid, threads, 0, stream>>>(params);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid multi_tensor_pad_last_dim(Tensor *inputs, Tensor *outputs, size_t num_tensors,\n                               cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  if (num_tensors == 0) return;\n\n  MultiPadParams params{};\n  size_t max_n_uint32 = 0;\n  int kernel_count = 0;\n\n  for (size_t i = 0; i < num_tensors; ++i) {\n    auto &inp = inputs[i];\n    auto &out = outputs[i];\n\n    NVTE_CHECK(inp.data.shape.size() == 2, \"Expected 2D input tensor at index \", i, \".\");\n    NVTE_CHECK(out.data.shape.size() == 2, \"Expected 2D output tensor at index \", i, \".\");\n    NVTE_CHECK(inp.data.dtype == out.data.dtype, \"Dtype mismatch at index \", i, \".\");\n\n    const size_t rows = inp.data.shape[0];\n    const size_t in_cols = inp.data.shape[1];\n    const size_t out_cols = out.data.shape[1];\n\n    NVTE_CHECK(out.data.shape[0] == rows, \"Row count mismatch at index \", i, \".\");\n    NVTE_CHECK(out_cols >= in_cols, \"out_cols < in_cols at index \", i, \".\");\n\n    if (rows == 0) continue;\n\n    if (in_cols == out_cols) {\n      const size_t total_bytes = rows * in_cols * typeToSize(inp.data.dtype);\n      NVTE_CHECK_CUDA(cudaMemcpyAsync(out.data.dptr, inp.data.dptr, total_bytes,\n                                      cudaMemcpyDeviceToDevice, stream));\n      continue;\n    }\n\n    if (kernel_count == kMaxPadTensors) {\n      launch_pad_batch(params, kernel_count, max_n_uint32, stream);\n      params = MultiPadParams{};\n      kernel_count = 0;\n      max_n_uint32 = 0;\n    }\n\n    const size_t elem_size = typeToSize(inp.data.dtype);\n    const auto in_row_bytes = static_cast<uint32_t>(in_cols * elem_size);\n    const auto out_row_bytes = static_cast<uint32_t>(out_cols * elem_size);\n    NVTE_CHECK(out_row_bytes % 4 == 0, \"Padded row size in bytes (\", out_row_bytes,\n               \") must be a multiple of 4.\");\n\n    const uint32_t out_row_uint32 = out_row_bytes / 4;\n    const size_t n_uint32 = rows * out_row_uint32;\n\n    params.tensors[kernel_count] = {reinterpret_cast<const uint8_t *>(inp.data.dptr),\n                                    reinterpret_cast<uint32_t *>(out.data.dptr), n_uint32,\n                                    in_row_bytes, out_row_uint32};\n    max_n_uint32 = std::max(max_n_uint32, n_uint32);\n    ++kernel_count;\n  }\n\n  launch_pad_batch(params, kernel_count, max_n_uint32, stream);\n}\n\n}  // namespace multi_tensor_pad_last_dim\n}  // namespace transformer_engine\n\nvoid nvte_prepare_flash_attn_fwd(NVTETensor qkvi, NVTETensor qkv, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_prepare_flash_attn_fwd);\n  using namespace transformer_engine;\n\n  flash_attention::prepare_flash_attn_fwd(*convertNVTETensorCheck(qkvi),\n                                          *convertNVTETensorCheck(qkv), stream);\n}\n\nvoid nvte_prepare_flash_attn_bwd(NVTETensor q, NVTETensor k, NVTETensor v, NVTETensor qkv,\n                                 cudaStream_t stream) {\n  NVTE_API_CALL(nvte_prepare_flash_attn_bwd);\n  using namespace transformer_engine;\n\n  flash_attention::prepare_flash_attn_bwd(*convertNVTETensorCheck(q), *convertNVTETensorCheck(k),\n                                          *convertNVTETensorCheck(v), *convertNVTETensorCheck(qkv),\n                                          stream);\n}\n\nvoid nvte_multi_tensor_transpose_to_bhsd(NVTETensor *inputs, NVTETensor *outputs,\n                                         size_t num_tensors, NVTE_QKV_Format original_format,\n                                         cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_transpose_to_bhsd);\n  NVTE_CHECK(original_format == NVTE_QKV_Format::NVTE_BSHD ||\n                 original_format == NVTE_QKV_Format::NVTE_SBHD,\n             \"nvte_multi_tensor_transpose_to_bhsd: only BSHD/SBHD -> BHSD is currently \"\n             \"supported.\");\n  using namespace transformer_engine;\n\n  std::vector<Tensor> in_vec(num_tensors), out_vec(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    in_vec[i] = *convertNVTETensorCheck(inputs[i]);\n    out_vec[i] = *convertNVTETensorCheck(outputs[i]);\n  }\n  constexpr size_t kBatch = multi_tensor_transpose_to_bhsd::kMaxPermuteTensors;\n  for (size_t offset = 0; offset < num_tensors; offset += kBatch) {\n    const size_t batch = std::min(num_tensors - offset, kBatch);\n    multi_tensor_transpose_to_bhsd::multi_tensor_transpose_to_bhsd(\n        in_vec.data() + offset, out_vec.data() + offset, batch, original_format, stream);\n  }\n}\n\nvoid nvte_multi_tensor_pad_last_dim(NVTETensor *inputs, NVTETensor *outputs, size_t num_tensors,\n                                    cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_pad_last_dim);\n  using namespace transformer_engine;\n\n  std::vector<Tensor> in_vec(num_tensors), out_vec(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    in_vec[i] = *convertNVTETensorCheck(inputs[i]);\n    out_vec[i] = *convertNVTETensorCheck(outputs[i]);\n  }\n  multi_tensor_pad_last_dim::multi_tensor_pad_last_dim(in_vec.data(), out_vec.data(), num_tensors,\n                                                       stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n\n#include \"../common.h\"\n#include \"../util/cuda_driver.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\n\n// ============================================================================\n// prepare_flash_attn: SBH3D <-> BSHD_BSHD_BSHD for the FlashAttention backend\n// ============================================================================\n\nnamespace flash_attention {\n\n/// Packed vector of N elements of T; alignment matches a single wide load/store of N * sizeof(T) bytes.\ntemplate <typename T, int N>\nstruct alignas(sizeof(T) * N) Vec {\n  T data[N];\n};\n\nconstexpr int warp_size = 32;\nconstexpr int type_size = 2;  // FP16 or BF16\nconstexpr int nvec = sizeof(uint64_t) / type_size;\nconstexpr int load_size = warp_size * nvec;\nconstexpr int block_size = 512;\n\ntemplate <typename T>\n__launch_bounds__(block_size) __global__\n    void prepare_kernel_fwd(const T *qkvi, T *qkv, const size_t B, const size_t S, const size_t Z,\n                            const size_t W) {\n  const int warpid = (blockDim.x * blockIdx.x + threadIdx.x) / warp_size;\n  const int id_in_warp = threadIdx.x % warp_size;\n  const size_t offset_input = blockIdx.y * W + warpid * 3 * W * Z + id_in_warp * nvec;\n  const T *my_input = qkvi + offset_input;\n\n  const size_t s = warpid / B;\n  if (s >= S) return;\n\n  const size_t b = warpid % B;\n\n  const size_t offset_output = blockIdx.y * B * S * Z * W + (s + b * S) * W * Z + id_in_warp * nvec;\n\n  T *my_output = qkv + offset_output;\n\n  for (int i = 0; i < Z; ++i) {\n    Vec<T, nvec> *const out = reinterpret_cast<Vec<T, nvec> *>(my_output + i * load_size);\n    *out = *reinterpret_cast<const Vec<T, nvec> *>(my_input + i * load_size * 3);\n  }\n}\n\ntemplate <typename T>\n__launch_bounds__(block_size) __global__\n    void prepare_kernel_bwd(const T *q, const T *k, const T *v, T *qkv, const size_t B,\n                            const size_t S, const size_t Z, const size_t W) {\n  const T *input = blockIdx.y == 0 ? q : (blockIdx.y == 1 ? k : v);\n\n  const int warpid = (blockDim.x * blockIdx.x + threadIdx.x) / warp_size;\n  const int id_in_warp = threadIdx.x % warp_size;\n  const size_t offset_input = warpid * W * Z + id_in_warp * nvec;\n  const T *my_input = input + offset_input;\n\n  const size_t b = warpid / S;\n  if (b >= B) return;\n\n  const size_t s = warpid % S;\n\n  const size_t offset_output = (b + s * B) * 3 * W * Z + id_in_warp * nvec + blockIdx.y * W;\n\n  T *my_output = qkv + offset_output;\n\n  for (int i = 0; i < Z; ++i) {\n    Vec<T, nvec> *const out = reinterpret_cast<Vec<T, nvec> *>(my_output + i * load_size * 3);\n    *out = *reinterpret_cast<const Vec<T, nvec> *>(my_input + i * load_size);\n  }\n}\n\nvoid prepare_flash_attn_fwd(Tensor qkvi, Tensor qkv, hipStream_t stream) {\n  using namespace transformer_engine;\n\n  NVTE_CHECK(qkvi.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(qkvi.dtype() == DType::kFloat16 || qkvi.dtype() == DType::kBFloat16);\n\n  auto qkvi_shape = qkvi.shape();\n\n  NVTE_CHECK(qkvi_shape[3] % load_size == 0);\n  NVTE_CHECK(qkvi_shape[3] == load_size);\n\n  // [s, b, n, h * 3] -> [3, b, s, n, h]\n  std::vector<uint64_t> shape = {3, qkvi_shape[1], qkvi_shape[0], qkvi_shape[2], qkvi_shape[3]};\n\n  size_t warps = qkvi_shape[0] * qkvi_shape[1];\n  size_t warps_per_block = block_size / warp_size;\n  size_t blocks = (warps + warps_per_block - 1) / warps_per_block;\n  dim3 grid(blocks, 3);\n  int threads = block_size;\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      qkvi.dtype(), dtype,\n      prepare_kernel_fwd<dtype><<<grid, threads, 0, stream>>>(\n          reinterpret_cast<dtype *>(qkvi.data.dptr), reinterpret_cast<dtype *>(qkv.data.dptr),\n          shape[1], shape[2], shape[3], shape[4]););\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid prepare_flash_attn_bwd(Tensor q, Tensor k, Tensor v, Tensor qkv, hipStream_t stream) {\n  using namespace transformer_engine;\n\n  NVTE_CHECK(q.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(k.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(v.dim() == 4, \"Expected 4-dim tensor.\");\n  NVTE_CHECK(q.dtype() == DType::kFloat16 || q.dtype() == DType::kBFloat16);\n  NVTE_CHECK(k.dtype() == q.dtype());\n  NVTE_CHECK(v.dtype() == q.dtype());\n\n  auto q_shape = q.shape();\n  auto k_shape = k.shape();\n  auto v_shape = v.shape();\n\n  NVTE_CHECK(q_shape[3] % load_size == 0);\n  NVTE_CHECK(q_shape[3] == load_size);\n  NVTE_CHECK(k_shape[3] % load_size == 0);\n  NVTE_CHECK(k_shape[3] == load_size);\n  NVTE_CHECK(v_shape[3] % load_size == 0);\n  NVTE_CHECK(v_shape[3] == load_size);\n\n  // 3 x [s, b, n, h] -> [b, s, n, 3 * h]\n  std::vector<uint64_t> shape = {q_shape[1], q_shape[0], q_shape[2], 3 * q_shape[3]};\n\n  size_t warps = q_shape[0] * q_shape[1];\n  size_t warps_per_block = block_size / warp_size;\n  size_t blocks = (warps + warps_per_block - 1) / warps_per_block;\n  dim3 grid(blocks, 3);\n  int threads = block_size;\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      q.dtype(), dtype,\n      prepare_kernel_bwd<dtype><<<grid, threads, 0, stream>>>(\n          reinterpret_cast<dtype *>(q.data.dptr), reinterpret_cast<dtype *>(k.data.dptr),\n          reinterpret_cast<dtype *>(v.data.dptr), reinterpret_cast<dtype *>(qkv.data.dptr),\n          q_shape[0], q_shape[1], q_shape[2], q_shape[3]););\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace flash_attention\n\n// ============================================================================\n// multi_tensor_transpose_to_bhsd: BSHD/SBHD -> BHSD\n// ============================================================================\n\nnamespace multi_tensor_transpose_to_bhsd {\n\nusing flash_attention::Vec;\n\nconstexpr int kMaxPermuteTensors = 16;\n\nstruct PermuteSlot {\n  const void *input;\n  void *output;\n  size_t S, H, D_in, D_out;\n};\n\nstruct PermuteParams {\n  PermuteSlot slots[kMaxPermuteTensors];\n};\n\nstruct TmaMapParams {\n  CUtensorMap maps[kMaxPermuteTensors];\n};\n\n// ---------- path 3: fallback_not_vec_aligned ----------\n\n__device__ __forceinline__ void copy_row_bytes(const char *__restrict__ src, char *__restrict__ dst,\n                                               size_t D_bytes) {\n  size_t off = 0;\n  for (; off + 16 <= D_bytes; off += 16) {\n    uint4 tmp;\n    memcpy(&tmp, src + off, 16);\n    memcpy(dst + off, &tmp, 16);\n  }\n  for (; off + 8 <= D_bytes; off += 8) {\n    uint2 tmp;\n    memcpy(&tmp, src + off, 8);\n    memcpy(dst + off, &tmp, 8);\n  }\n  for (; off + 4 <= D_bytes; off += 4) {\n    unsigned int tmp;\n    memcpy(&tmp, src + off, 4);\n    memcpy(dst + off, &tmp, 4);\n  }\n  for (; off + 2 <= D_bytes; off += 2) {\n    uint16_t tmp;\n    memcpy(&tmp, src + off, 2);\n    memcpy(dst + off, &tmp, 2);\n  }\n  for (; off < D_bytes; ++off) dst[off] = src[off];\n}\n\n__device__ __forceinline__ void copy_and_pad_row_bytes(const char *__restrict__ src,\n                                                       char *__restrict__ dst, size_t D_bytes,\n                                                       size_t D_out_bytes) {\n  copy_row_bytes(src, dst, D_bytes);\n  for (size_t off = D_bytes; off < D_out_bytes; ++off) dst[off] = 0;\n}\n\nconstexpr int TRANSPOSE_TILE = 32;\nconstexpr int TRANSPOSE_BLOCK = 256;\nconstexpr int TRANSPOSE_WARPS = TRANSPOSE_BLOCK / 32;  // 8\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(TRANSPOSE_BLOCK) __global__\n    void transpose_to_bhsd_fallback_not_vec_aligned_kernel(PermuteParams params, size_t b,\n                                                           unsigned int s_tiles) {\n  const auto &slot = params.slots[blockIdx.z];\n  const T *__restrict__ in = reinterpret_cast<const T *>(slot.input);\n  T *__restrict__ out = reinterpret_cast<T *>(slot.output);\n  const size_t S = slot.S;\n  const size_t H = slot.H;\n  const size_t D = slot.D_in;\n  const size_t D_out = slot.D_out;\n  const size_t D_bytes = D * sizeof(T);\n  const size_t D_out_bytes = D_out * sizeof(T);\n  const size_t D_smem_pad = (D_bytes + 3u) & ~size_t(3);\n\n  const size_t tile_s = static_cast<size_t>(blockIdx.x) % static_cast<size_t>(s_tiles);\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / static_cast<size_t>(s_tiles);\n  if (b_i >= b) return;\n  const size_t tile_h = static_cast<size_t>(blockIdx.y);\n\n  const size_t s_base = tile_s * TRANSPOSE_TILE;\n  const size_t h_base = tile_h * TRANSPOSE_TILE;\n\n  extern __shared__ char smem[];\n  const size_t smem_row = static_cast<size_t>(TRANSPOSE_TILE) * D_smem_pad + 4;\n\n  // ---- Phase 1: global \u2192 smem (sweep consecutive H \u2192 coalesced reads) ----\n  for (unsigned int warp_off = threadIdx.x >> 5; warp_off < TRANSPOSE_TILE;\n       warp_off += TRANSPOSE_WARPS) {\n    const size_t local_s = warp_off;\n    const size_t local_h = threadIdx.x & 31u;\n    const size_t s_i = s_base + local_s;\n    const size_t h_i = h_base + local_h;\n    if (s_i < S && h_i < H) {\n      const char *__restrict__ src;\n      if constexpr (kIsBshd)\n        src = reinterpret_cast<const char *>(in + b_i * S * H * D + s_i * H * D + h_i * D);\n      else\n        src = reinterpret_cast<const char *>(in + s_i * b * H * D + b_i * H * D + h_i * D);\n      copy_row_bytes(src, smem + local_s * smem_row + local_h * D_smem_pad, D_bytes);\n    }\n  }\n\n  __syncthreads();\n\n  // ---- Phase 2: smem \u2192 global (sweep consecutive S \u2192 coalesced writes, with padding) ----\n  for (unsigned int warp_off = threadIdx.x >> 5; warp_off < TRANSPOSE_TILE;\n       warp_off += TRANSPOSE_WARPS) {\n    const size_t local_h = warp_off;\n    const size_t local_s = threadIdx.x & 31u;\n    const size_t s_i = s_base + local_s;\n    const size_t h_i = h_base + local_h;\n    if (s_i < S && h_i < H) {\n      copy_and_pad_row_bytes(\n          smem + local_s * smem_row + local_h * D_smem_pad,\n          reinterpret_cast<char *>(out + b_i * H * S * D_out + h_i * S * D_out + s_i * D_out),\n          D_bytes, D_out_bytes);\n    }\n  }\n}\n\n// ---------- path 2: fallback_vec_aligned ----------\n\nconstexpr int fallback_permute_threads = 1024;\n\ntemplate <typename T, bool kIsBshd, int N>\n__device__ __forceinline__ void permute_vec_loop(const T *__restrict__ in, T *__restrict__ out,\n                                                 size_t b, size_t S, size_t H, size_t D,\n                                                 size_t D_out, size_t b_i, size_t h_i,\n                                                 size_t s_begin, size_t S_chunk) {\n  const size_t out_base = b_i * H * S * D_out + h_i * S * D_out;\n  const size_t d_vec = D / static_cast<size_t>(N);\n  const size_t total_work = S_chunk * d_vec;\n  for (size_t w = static_cast<size_t>(threadIdx.x); w < total_work;\n       w += static_cast<size_t>(blockDim.x)) {\n    const size_t s_local = w / d_vec;\n    const size_t s_i = s_begin + s_local;\n    const size_t d_off = (w % d_vec) * static_cast<size_t>(N);\n    const T *__restrict__ in_ptr;\n    if constexpr (kIsBshd) {\n      in_ptr = in + b_i * (S * H * D) + s_i * (H * D) + h_i * D + d_off;\n    } else {\n      in_ptr = in + s_i * (b * H * D) + b_i * (H * D) + h_i * D + d_off;\n    }\n    T *__restrict__ out_ptr = out + out_base + s_i * D_out + d_off;\n    *reinterpret_cast<Vec<T, N> *>(out_ptr) = *reinterpret_cast<const Vec<T, N> *>(in_ptr);\n  }\n  if (D_out > D) {\n    const size_t pad_elems = D_out - D;\n    const size_t total_pad = S_chunk * pad_elems;\n    for (size_t w = static_cast<size_t>(threadIdx.x); w < total_pad;\n         w += static_cast<size_t>(blockDim.x)) {\n      const size_t s_local = w / pad_elems;\n      const size_t s_i = s_begin + s_local;\n      const size_t d_off = D + (w % pad_elems);\n      out[out_base + s_i * D_out + d_off] = static_cast<T>(0.f);\n    }\n  }\n}\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(fallback_permute_threads) __global__\n    void transpose_to_bhsd_fallback_vec_aligned_kernel(PermuteParams params, size_t b,\n                                                       unsigned int permute_s_splits,\n                                                       size_t h_grid) {\n  const auto &slot = params.slots[blockIdx.z];\n  const T *__restrict__ in = reinterpret_cast<const T *>(slot.input);\n  T *__restrict__ out = reinterpret_cast<T *>(slot.output);\n  const size_t S = slot.S;\n  const size_t H = slot.H;\n  const size_t D = slot.D_in;\n  const size_t D_out = slot.D_out;\n\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / h_grid;\n  const size_t h_i = static_cast<size_t>(blockIdx.x) % h_grid;\n  if (b_i >= b) return;\n  if (h_i >= H) return;\n\n  const unsigned int s_part = blockIdx.y;\n  const size_t s_begin = (S * static_cast<size_t>(s_part)) / static_cast<size_t>(permute_s_splits);\n  const size_t s_end =\n      (S * static_cast<size_t>(s_part + 1)) / static_cast<size_t>(permute_s_splits);\n  if (s_begin >= s_end) return;\n  const size_t S_chunk = s_end - s_begin;\n\n  const size_t D_bytes = D * sizeof(T);\n\n  if (D_bytes % 16 == 0) {\n    constexpr size_t N = 16 / sizeof(T);\n    permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n    return;\n  }\n  if (D_bytes % 8 == 0) {\n    constexpr size_t N = 8 / sizeof(T);\n    permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n    return;\n  }\n  if constexpr (sizeof(T) <= 4) {\n    if (D_bytes % 4 == 0) {\n      constexpr size_t N = 4 / sizeof(T);\n      permute_vec_loop<T, kIsBshd, N>(in, out, b, S, H, D, D_out, b_i, h_i, s_begin, S_chunk);\n      return;\n    }\n  }\n}\n\n// ---------- path 1: TMA ----------\n\nconstexpr int tma_permute_threads = 128;\nconstexpr int tma_permute_s_tile_default = 32;\n\n__device__ __forceinline__ void cp_async_bulk_tensor_4d_global_to_shared(\n    void *dst_shmem, const CUtensorMap *tensor_map, uint32_t c0, uint32_t c1, uint32_t c2,\n    uint32_t c3, uint64_t *mbar) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  uint32_t dst = __cvta_generic_to_shared(dst_shmem);\n  uint32_t bar = __cvta_generic_to_shared(mbar);\n  asm volatile(\n      \"cp.async.bulk.tensor.4d.shared::cluster.global.tile\"\n      \".mbarrier::complete_tx::bytes [%0], [%1, {%2, %3, %4, %5}], [%6];\" ::\"r\"(dst),\n      \"l\"(tensor_map), \"r\"(c0), \"r\"(c1), \"r\"(c2), \"r\"(c3), \"r\"(bar)\n      : \"memory\");\n#else\n  NVTE_DEVICE_ERROR(\"cp_async_bulk_tensor_4d_global_to_shared requires SM 10.0+.\");\n#endif\n}\n\n__device__ __forceinline__ void cp_async_bulk_tensor_4d_shared_to_global(\n    const CUtensorMap *tensor_map, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3,\n    void *src_shmem) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  uint32_t src = __cvta_generic_to_shared(src_shmem);\n  asm volatile(\n      \"cp.async.bulk.tensor.4d.global.shared::cta.bulk_group\"\n      \" [%0, {%1, %2, %3, %4}], [%5];\" ::\"l\"(tensor_map),\n      \"r\"(c0), \"r\"(c1), \"r\"(c2), \"r\"(c3), \"r\"(src)\n      : \"memory\");\n#else\n  NVTE_DEVICE_ERROR(\"cp_async_bulk_tensor_4d_shared_to_global requires SM 10.0+.\");\n#endif\n}\n\nstatic void create_4D_tensor_map(CUtensorMap &tensorMap, void *dataPtr, DType dtype, uint64_t dim0,\n                                 uint64_t dim1, uint64_t dim2, uint64_t dim3, uint32_t box0,\n                                 uint32_t box1, uint32_t box2, uint32_t box3) {\n  cuda_driver::ensure_context_exists();\n  static PFN_cuTensorMapEncodeTiled_v12000 cuDriverTensorMapEncodeTiled = []() {\n    void *ptr = cuda_driver::get_symbol(\"cuTensorMapEncodeTiled\");\n    return reinterpret_cast<PFN_cuTensorMapEncodeTiled_v12000>(ptr);\n  }();\n\n  CUtensorMapDataType tma_dtype;\n  size_t elem_bytes;\n  switch (dtype) {\n    case DType::kFloat16:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_FLOAT16;\n      elem_bytes = 2;\n      break;\n    case DType::kBFloat16:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_BFLOAT16;\n      elem_bytes = 2;\n      break;\n    case DType::kFloat8E4M3:\n    case DType::kFloat8E5M2:\n    case DType::kFloat8E8M0:\n    case DType::kByte:\n      tma_dtype = CU_TENSOR_MAP_DATA_TYPE_UINT8;\n      elem_bytes = 1;\n      break;\n    default:\n      NVTE_ERROR(\"create_4D_tensor_map: unsupported dtype \", to_string(static_cast<DType>(dtype)));\n  }\n\n  constexpr uint32_t rank = 4;\n  uint64_t size[rank] = {dim0, dim1, dim2, dim3};\n  uint64_t stride[rank - 1] = {\n      dim0 * elem_bytes,\n      dim0 * dim1 * elem_bytes,\n      dim0 * dim1 * dim2 * elem_bytes,\n  };\n  uint32_t boxSize[rank] = {box0, box1, box2, box3};\n  uint32_t elemStride[rank] = {1, 1, 1, 1};\n\n  const auto oob_fill = (tma_dtype == CU_TENSOR_MAP_DATA_TYPE_UINT8)\n                            ? CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE\n                            : CU_TENSOR_MAP_FLOAT_OOB_FILL_NAN_REQUEST_ZERO_FMA;\n\n  NVTE_CHECK_CUDA_DRIVER(cuDriverTensorMapEncodeTiled(\n      &tensorMap, tma_dtype, rank, dataPtr, size, stride, boxSize, elemStride,\n      CU_TENSOR_MAP_INTERLEAVE_NONE, CU_TENSOR_MAP_SWIZZLE_NONE, CU_TENSOR_MAP_L2_PROMOTION_NONE,\n      oob_fill));\n}\n\ntemplate <typename T, bool kIsBshd>\n__device__ __forceinline__ void issue_tma_load_strided(T *smem_buf, const CUtensorMap *tma,\n                                                       size_t h_i, size_t s_tile, size_t b_i,\n                                                       uint64_t *mbar, size_t tile_bytes) {\n  ptx::mbarrier_arrive_expect_tx(mbar, static_cast<uint32_t>(tile_bytes));\n  if constexpr (kIsBshd) {\n    cp_async_bulk_tensor_4d_global_to_shared(smem_buf, tma, 0, static_cast<uint32_t>(h_i),\n                                             static_cast<uint32_t>(s_tile),\n                                             static_cast<uint32_t>(b_i), mbar);\n  } else {\n    cp_async_bulk_tensor_4d_global_to_shared(smem_buf, tma, 0, static_cast<uint32_t>(h_i),\n                                             static_cast<uint32_t>(b_i),\n                                             static_cast<uint32_t>(s_tile), mbar);\n  }\n}\n\n__device__ __forceinline__ void st_global_cs_uint4(uint4 *ptr, uint4 val) {\n  asm volatile(\"st.global.cs.v4.b32 [%0], {%1, %2, %3, %4};\" ::\"l\"(ptr), \"r\"(val.x), \"r\"(val.y),\n               \"r\"(val.z), \"r\"(val.w)\n               : \"memory\");\n}\n// TMA loads from strided input to smem + non-temporal stores to contiguous output in gmem\n\ntemplate <typename T, bool kIsBshd>\n__launch_bounds__(tma_permute_threads) __global__\n    void transpose_to_bhsd_kernel(const __grid_constant__ TmaMapParams tma_maps,\n                                  PermuteParams params, size_t b, size_t h_grid,\n                                  unsigned int permute_s_splits, size_t s_tile_size) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n  const auto &slot = params.slots[blockIdx.z];\n  const CUtensorMap *tma_in = &tma_maps.maps[blockIdx.z];\n  T *__restrict__ tensor_out = reinterpret_cast<T *>(slot.output);\n  const size_t Sdim = slot.S;\n  const size_t Hdim = slot.H;\n  const size_t Ddim = slot.D_in;\n  const size_t Ddim_out = slot.D_out;\n\n  const size_t b_i = static_cast<size_t>(blockIdx.x) / h_grid;\n  const size_t h_i = static_cast<size_t>(blockIdx.x) % h_grid;\n\n  if (b_i >= b) return;\n  if (h_i >= Hdim) return;\n\n  const unsigned int s_part = blockIdx.y;\n  const size_t s_begin =\n      (Sdim * static_cast<size_t>(s_part)) / static_cast<size_t>(permute_s_splits);\n  const size_t s_end =\n      (Sdim * static_cast<size_t>(s_part + 1)) / static_cast<size_t>(permute_s_splits);\n  if (s_begin >= s_end) return;\n\n  const size_t out_base = b_i * Hdim * Sdim * Ddim_out + h_i * Sdim * Ddim_out;\n\n  extern __shared__ __align__(128) char smem_raw[];\n  T *smem = reinterpret_cast<T *>(smem_raw);\n\n  __shared__ __align__(8) uint64_t mbar;\n  const bool is_leader = (threadIdx.x == 0);\n\n  if (is_leader) {\n    ptx::mbarrier_init(&mbar, static_cast<uint32_t>(blockDim.x));\n    ptx::fence_proxy_async_shared_cta();\n  }\n  __syncthreads();\n\n  const size_t S_TILE = s_tile_size;\n  const uint32_t tile_bytes = static_cast<uint32_t>(S_TILE * Ddim * sizeof(T));\n  int parity = 0;\n\n  for (size_t s_tile = s_begin; s_tile < s_end; s_tile += S_TILE) {\n    const size_t tile_rows = min(S_TILE, s_end - s_tile);\n\n    if (is_leader) {\n      issue_tma_load_strided<T, kIsBshd>(smem, tma_in, h_i, s_tile, b_i, &mbar, tile_bytes);\n    } else {\n      ptx::mbarrier_arrive(&mbar);\n    }\n\n    ptx::mbarrier_wait_parity(&mbar, parity);\n    parity ^= 1;\n\n    T *__restrict__ out_ptr = tensor_out + out_base + s_tile * Ddim_out;\n    constexpr size_t vec_elems = sizeof(uint4) / sizeof(T);\n\n    if (Ddim_out == Ddim) {\n      const size_t total_elems = tile_rows * Ddim;\n      for (size_t i = threadIdx.x * vec_elems; i < total_elems;\n           i += static_cast<size_t>(blockDim.x) * vec_elems) {\n        uint4 v = *reinterpret_cast<const uint4 *>(smem + i);\n        st_global_cs_uint4(reinterpret_cast<uint4 *>(out_ptr + i), v);\n      }\n    } else {\n      const size_t total_out_elems = tile_rows * Ddim_out;\n      for (size_t i = threadIdx.x * vec_elems; i < total_out_elems;\n           i += static_cast<size_t>(blockDim.x) * vec_elems) {\n        const size_t row = i / Ddim_out;\n        const size_t col = i % Ddim_out;\n        uint4 v;\n        if (col + vec_elems <= Ddim) {\n          v = *reinterpret_cast<const uint4 *>(smem + row * Ddim + col);\n        } else {\n          memset(&v, 0, sizeof(v));\n          const size_t smem_off = row * Ddim + col;\n          size_t copy_elems = (col < Ddim) ? (Ddim - col) : 0;\n          if (copy_elems > 0) memcpy(&v, smem + smem_off, copy_elems * sizeof(T));\n        }\n        st_global_cs_uint4(reinterpret_cast<uint4 *>(out_ptr + i), v);\n      }\n    }\n\n    __syncthreads();\n  }\n\n  if (is_leader) {\n    ptx::mbarrier_invalid(&mbar);\n  }\n#endif\n}\n\n// 4D TMA descriptor:\n// [B, S, H, D]: TMA dims [D, H, S, B], box [D, 1, S_TILE, 1]\n// [S, B, H, D]: TMA dims [D, H, B, S], box [D, 1, 1, S_TILE]\n\nstatic void create_strided_tensor_map(CUtensorMap &map, void *ptr, DType dtype, size_t b, size_t s,\n                                      size_t h, size_t d, size_t s_tile, bool is_bshd) {\n  if (is_bshd) {\n    create_4D_tensor_map(map, ptr, dtype, static_cast<uint64_t>(d), static_cast<uint64_t>(h),\n                         static_cast<uint64_t>(s), static_cast<uint64_t>(b),\n                         static_cast<uint32_t>(d), 1, static_cast<uint32_t>(s_tile), 1);\n  } else {\n    create_4D_tensor_map(map, ptr, dtype, static_cast<uint64_t>(d), static_cast<uint64_t>(h),\n                         static_cast<uint64_t>(b), static_cast<uint64_t>(s),\n                         static_cast<uint32_t>(d), 1, 1, static_cast<uint32_t>(s_tile));\n  }\n}\n\nvoid multi_tensor_transpose_to_bhsd(Tensor *inputs, Tensor *outputs, size_t num_tensors,\n                                    NVTE_QKV_Format original_format, hipStream_t stream) {\n  using namespace transformer_engine;\n  if (num_tensors == 0) return;\n  NVTE_CHECK(num_tensors <= static_cast<size_t>(kMaxPermuteTensors), \"num_tensors must be in [1, \",\n             kMaxPermuteTensors, \"], got \", num_tensors, \".\");\n\n  const bool is_bshd = (original_format == NVTE_QKV_Format::NVTE_BSHD);\n  const DType dtype = inputs[0].dtype();\n  const size_t elem_size = typeToSize(dtype);\n  const size_t b = outputs[0].shape()[0];\n\n  PermuteParams params{};\n  size_t s_max = 0, h_max = 0, s_min = SIZE_MAX;\n  size_t d_in_max = 0, d_out_max = 0;\n  bool any_not_vec_aligned = false;\n  bool all_tma_ok = true;\n\n  for (size_t i = 0; i < num_tensors; ++i) {\n    const size_t H = outputs[i].shape()[1];\n    const size_t S = outputs[i].shape()[2];\n    const size_t D_in = inputs[i].shape()[inputs[i].shape().size() - 1];\n    const size_t D_out = outputs[i].shape()[3];\n    params.slots[i] = {inputs[i].data.dptr, outputs[i].data.dptr, S, H, D_in, D_out};\n    s_max = std::max(s_max, S);\n    h_max = std::max(h_max, H);\n    s_min = std::min(s_min, S);\n    d_in_max = std::max(d_in_max, D_in);\n    d_out_max = std::max(d_out_max, D_out);\n    if ((D_in * elem_size) % 4 != 0) any_not_vec_aligned = true;\n    const size_t inner = D_in * elem_size;\n    if (inner < 32 || inner % 16 != 0) all_tma_ok = false;\n  }\n\n  if (all_tma_ok) {\n    const int sm = cuda::sm_arch(cuda::current_device());\n    if (sm < 100) {\n      all_tma_ok = false;\n    } else {\n      switch (dtype) {\n        case DType::kFloat16:\n        case DType::kBFloat16:\n        case DType::kFloat8E4M3:\n        case DType::kFloat8E5M2:\n        case DType::kFloat8E8M0:\n        case DType::kByte:\n          break;\n        default:\n          all_tma_ok = false;\n      }\n    }\n  }\n\n  // Dispatch order:\n  //  1. TMA path: SM 10.0+, D_in*elem >= 32 && 16-aligned, supported dtype,\n  //     and s_tile*D_in*elem is uint4-aligned.\n  //  2. Fallback path (vec-aligned): vectorized loads/stores when D_in*elem % 4 == 0.\n  //  3. Fallback path (not-vec-aligned): shared-memory transpose when D_in*elem % 4 != 0.\n  if (all_tma_ok) {\n    const size_t s_tile = std::min(static_cast<size_t>(tma_permute_s_tile_default), s_min);\n    bool tma_aligned = true;\n    for (size_t i = 0; i < num_tensors && tma_aligned; ++i) {\n      if ((s_tile * params.slots[i].D_in * elem_size) % sizeof(uint4) != 0) tma_aligned = false;\n    }\n\n    if (tma_aligned) {\n      TmaMapParams tma_maps{};\n      for (size_t i = 0; i < num_tensors; ++i) {\n        const auto &slot = params.slots[i];\n        create_strided_tensor_map(tma_maps.maps[i], const_cast<void *>(slot.input), dtype, b,\n                                  slot.S, slot.H, slot.D_in, s_tile, is_bshd);\n      }\n\n      const unsigned int permute_s_splits = std::max(1u, static_cast<unsigned int>(s_min / s_tile));\n      dim3 grid(static_cast<unsigned int>(b * h_max), permute_s_splits,\n                static_cast<unsigned int>(num_tensors));\n      const size_t smem_bytes = s_tile * d_in_max * elem_size;\n\n      if (is_bshd) {\n        TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n            dtype, dtype_t, auto kernel = transpose_to_bhsd_kernel<dtype_t, true>;\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n            kernel<<<grid, tma_permute_threads, smem_bytes, stream>>>(tma_maps, params, b, h_max,\n                                                                      permute_s_splits, s_tile););\n      } else {\n        TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n            dtype, dtype_t, auto kernel = transpose_to_bhsd_kernel<dtype_t, false>;\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n            kernel<<<grid, tma_permute_threads, smem_bytes, stream>>>(tma_maps, params, b, h_max,\n                                                                      permute_s_splits, s_tile););\n      }\n      NVTE_CHECK_CUDA(hipGetLastError());\n      return;\n    }\n  }\n\n  if (!any_not_vec_aligned) {\n    const unsigned int permute_s_splits = std::max(\n        1u, static_cast<unsigned int>(s_min / static_cast<size_t>(fallback_permute_threads)));\n    dim3 grid(static_cast<unsigned int>(b * h_max), permute_s_splits,\n              static_cast<unsigned int>(num_tensors));\n\n    if (is_bshd) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_vec_aligned_kernel<dtype_t, true>\n          <<<grid, fallback_permute_threads, 0, stream>>>(params, b, permute_s_splits, h_max););\n    } else {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_vec_aligned_kernel<dtype_t, false>\n          <<<grid, fallback_permute_threads, 0, stream>>>(params, b, permute_s_splits, h_max););\n    }\n  } else {\n    const unsigned int st =\n        static_cast<unsigned int>((s_max + TRANSPOSE_TILE - 1) / TRANSPOSE_TILE);\n    const unsigned int ht =\n        static_cast<unsigned int>((h_max + TRANSPOSE_TILE - 1) / TRANSPOSE_TILE);\n    dim3 grid(static_cast<unsigned int>(b) * st, ht, static_cast<unsigned int>(num_tensors));\n    const size_t D_pad = (d_in_max * elem_size + 3u) & ~size_t(3);\n    const size_t smem_bytes =\n        static_cast<size_t>(TRANSPOSE_TILE) * (static_cast<size_t>(TRANSPOSE_TILE) * D_pad + 4);\n\n    if (is_bshd) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_not_vec_aligned_kernel<dtype_t, true>\n          <<<grid, TRANSPOSE_BLOCK, smem_bytes, stream>>>(params, b, st););\n    } else {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          dtype, dtype_t,\n          transpose_to_bhsd_fallback_not_vec_aligned_kernel<dtype_t, false>\n          <<<grid, TRANSPOSE_BLOCK, smem_bytes, stream>>>(params, b, st););\n    }\n  }\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace multi_tensor_transpose_to_bhsd\n\n// ===================================================================================\n// multi_tensor_pad_last_dim: pad the last dim of multiple tensors to certain alignment\n// ===================================================================================\n\nnamespace multi_tensor_pad_last_dim {\n\nconstexpr int pad_threads_per_block = 256;\nconstexpr int kMaxPadTensors = 16;\n\nstruct PadLastDimArgs {\n  const uint8_t *input;\n  uint32_t *output;\n  size_t n_uint32;\n  uint32_t in_row_bytes;\n  uint32_t out_row_uint32;\n};\n\nstruct MultiPadParams {\n  PadLastDimArgs tensors[kMaxPadTensors];\n};\n\n__launch_bounds__(pad_threads_per_block) __global__\n    void multi_tensor_pad_last_dim_kernel(MultiPadParams params) {\n  const auto &a = params.tensors[blockIdx.y];\n\n  for (size_t idx = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x; idx < a.n_uint32;\n       idx += static_cast<size_t>(gridDim.x) * blockDim.x) {\n    const uint32_t col_byte = (idx % a.out_row_uint32) * 4;\n    const size_t row = idx / a.out_row_uint32;\n    const uint8_t *__restrict__ src = a.input + row * static_cast<size_t>(a.in_row_bytes);\n\n    uint32_t val;\n    if (col_byte + 4 <= a.in_row_bytes) {\n      memcpy(&val, src + col_byte, 4);\n    } else if (col_byte >= a.in_row_bytes) {\n      val = 0;\n    } else {\n      val = 0;\n      memcpy(&val, src + col_byte, a.in_row_bytes - col_byte);\n    }\n    a.output[idx] = val;\n  }\n}\n\nvoid launch_pad_batch(MultiPadParams &params, int kernel_count, size_t max_n_uint32,\n                      hipStream_t stream) {\n  if (kernel_count == 0) return;\n  constexpr int threads = pad_threads_per_block;\n  const int blocks_x = static_cast<int>(\n      std::min(DIVUP(max_n_uint32, static_cast<size_t>(threads)), static_cast<size_t>(65535)));\n  dim3 grid(blocks_x, kernel_count);\n  multi_tensor_pad_last_dim_kernel<<<grid, threads, 0, stream>>>(params);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid multi_tensor_pad_last_dim(Tensor *inputs, Tensor *outputs, size_t num_tensors,\n                               hipStream_t stream) {\n  using namespace transformer_engine;\n\n  if (num_tensors == 0) return;\n\n  MultiPadParams params{};\n  size_t max_n_uint32 = 0;\n  int kernel_count = 0;\n\n  for (size_t i = 0; i < num_tensors; ++i) {\n    auto &inp = inputs[i];\n    auto &out = outputs[i];\n\n    NVTE_CHECK(inp.data.shape.size() == 2, \"Expected 2D input tensor at index \", i, \".\");\n    NVTE_CHECK(out.data.shape.size() == 2, \"Expected 2D output tensor at index \", i, \".\");\n    NVTE_CHECK(inp.data.dtype == out.data.dtype, \"Dtype mismatch at index \", i, \".\");\n\n    const size_t rows = inp.data.shape[0];\n    const size_t in_cols = inp.data.shape[1];\n    const size_t out_cols = out.data.shape[1];\n\n    NVTE_CHECK(out.data.shape[0] == rows, \"Row count mismatch at index \", i, \".\");\n    NVTE_CHECK(out_cols >= in_cols, \"out_cols < in_cols at index \", i, \".\");\n\n    if (rows == 0) continue;\n\n    if (in_cols == out_cols) {\n      const size_t total_bytes = rows * in_cols * typeToSize(inp.data.dtype);\n      NVTE_CHECK_CUDA(hipMemcpyAsync(out.data.dptr, inp.data.dptr, total_bytes,\n                                      hipMemcpyDeviceToDevice, stream));\n      continue;\n    }\n\n    if (kernel_count == kMaxPadTensors) {\n      launch_pad_batch(params, kernel_count, max_n_uint32, stream);\n      params = MultiPadParams{};\n      kernel_count = 0;\n      max_n_uint32 = 0;\n    }\n\n    const size_t elem_size = typeToSize(inp.data.dtype);\n    const auto in_row_bytes = static_cast<uint32_t>(in_cols * elem_size);\n    const auto out_row_bytes = static_cast<uint32_t>(out_cols * elem_size);\n    NVTE_CHECK(out_row_bytes % 4 == 0, \"Padded row size in bytes (\", out_row_bytes,\n               \") must be a multiple of 4.\");\n\n    const uint32_t out_row_uint32 = out_row_bytes / 4;\n    const size_t n_uint32 = rows * out_row_uint32;\n\n    params.tensors[kernel_count] = {reinterpret_cast<const uint8_t *>(inp.data.dptr),\n                                    reinterpret_cast<uint32_t *>(out.data.dptr), n_uint32,\n                                    in_row_bytes, out_row_uint32};\n    max_n_uint32 = std::max(max_n_uint32, n_uint32);\n    ++kernel_count;\n  }\n\n  launch_pad_batch(params, kernel_count, max_n_uint32, stream);\n}\n\n}  // namespace multi_tensor_pad_last_dim\n}  // namespace transformer_engine\n\nvoid nvte_prepare_flash_attn_fwd(NVTETensor qkvi, NVTETensor qkv, hipStream_t stream) {\n  NVTE_API_CALL(nvte_prepare_flash_attn_fwd);\n  using namespace transformer_engine;\n\n  flash_attention::prepare_flash_attn_fwd(*convertNVTETensorCheck(qkvi),\n                                          *convertNVTETensorCheck(qkv), stream);\n}\n\nvoid nvte_prepare_flash_attn_bwd(NVTETensor q, NVTETensor k, NVTETensor v, NVTETensor qkv,\n                                 hipStream_t stream) {\n  NVTE_API_CALL(nvte_prepare_flash_attn_bwd);\n  using namespace transformer_engine;\n\n  flash_attention::prepare_flash_attn_bwd(*convertNVTETensorCheck(q), *convertNVTETensorCheck(k),\n                                          *convertNVTETensorCheck(v), *convertNVTETensorCheck(qkv),\n                                          stream);\n}\n\nvoid nvte_multi_tensor_transpose_to_bhsd(NVTETensor *inputs, NVTETensor *outputs,\n                                         size_t num_tensors, NVTE_QKV_Format original_format,\n                                         hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_transpose_to_bhsd);\n  NVTE_CHECK(original_format == NVTE_QKV_Format::NVTE_BSHD ||\n                 original_format == NVTE_QKV_Format::NVTE_SBHD,\n             \"nvte_multi_tensor_transpose_to_bhsd: only BSHD/SBHD -> BHSD is currently \"\n             \"supported.\");\n  using namespace transformer_engine;\n\n  std::vector<Tensor> in_vec(num_tensors), out_vec(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    in_vec[i] = *convertNVTETensorCheck(inputs[i]);\n    out_vec[i] = *convertNVTETensorCheck(outputs[i]);\n  }\n  constexpr size_t kBatch = multi_tensor_transpose_to_bhsd::kMaxPermuteTensors;\n  for (size_t offset = 0; offset < num_tensors; offset += kBatch) {\n    const size_t batch = std::min(num_tensors - offset, kBatch);\n    multi_tensor_transpose_to_bhsd::multi_tensor_transpose_to_bhsd(\n        in_vec.data() + offset, out_vec.data() + offset, batch, original_format, stream);\n  }\n}\n\nvoid nvte_multi_tensor_pad_last_dim(NVTETensor *inputs, NVTETensor *outputs, size_t num_tensors,\n                                    hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_pad_last_dim);\n  using namespace transformer_engine;\n\n  std::vector<Tensor> in_vec(num_tensors), out_vec(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    in_vec[i] = *convertNVTETensorCheck(inputs[i]);\n    out_vec[i] = *convertNVTETensorCheck(outputs[i]);\n  }\n  multi_tensor_pad_last_dim::multi_tensor_pad_last_dim(in_vec.data(), out_vec.data(), num_tensors,\n                                                       stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_attn/flash_attn.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda_bf16.h>\n\n#include \"../common.h\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\nnamespace context_parallel {\n\nstruct LseCorrectionFunctor {\n  __forceinline__ __device__ static void run(float *lse, float *half_lse, size_t idx,\n                                             size_t half_idx) {\n    float val = lse[idx];\n    float val_per_step = half_lse[half_idx];\n    float max_scale = max(val, val_per_step);\n    float min_scale = min(val, val_per_step);\n    lse[idx] = max_scale + log1pf(expf(min_scale - max_scale));\n  }\n};\n\nstruct ReadLseFunctor {\n  __forceinline__ __device__ static void run(float *lse, float *half_lse, size_t idx,\n                                             size_t half_idx) {\n    half_lse[half_idx] = lse[idx];\n  }\n};\n\nstruct EmptyFunctor {\n  __forceinline__ __device__ static void run(void *token, void *token_per_step, int idx) {}\n};\n\nstruct CopyFunctor {\n  __forceinline__ __device__ static void run(void *token, void *token_per_step, int idx) {\n    reinterpret_cast<float4 *>(token)[idx] = reinterpret_cast<float4 *>(token_per_step)[idx];\n  }\n};\n\ntemplate <typename dtype>\nstruct AddFunctor {\n  __forceinline__ __device__ static void run(dtype *token, dtype *token_per_step, int idx) {\n    float4 d_ = reinterpret_cast<float4 *>(token)[idx];\n    dtype *p_ = reinterpret_cast<dtype *>(&d_);\n\n    float4 d = reinterpret_cast<float4 *>(token_per_step)[idx];\n    dtype *p = reinterpret_cast<dtype *>(&d);\n\n#pragma unroll\n    for (int i = 0; i < sizeof(float4) / sizeof(dtype); i++) {\n      p_[i] = p_[i] + p[i];\n    }\n\n    reinterpret_cast<float4 *>(token)[idx] = d_;\n  }\n};\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Binary search an array for a target value\n **************************************************************************************************/\n\n__forceinline__ __device__ int binary_search(int target, int *array, int len) {\n  int left = 1, right = len - 1;\n  while (left < right) {\n    int mid = (left + right) / 2;\n    if (array[mid] <= target) {\n      left = mid + 1;\n    } else {\n      right = mid;\n    }\n  }\n  return left - 1;\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Generate partitioned indices for input tokens\n **************************************************************************************************/\n__global__ void thd_partition_indices_kernel(int *output, int *cu_seqlens, int batch,\n                                             int total_tokens, int world_size, int rank) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    int seqlen = cu_seqlens[i];\n    // Currently we assume that each sequence length is divisible by (world_size*2) since we have\n    // to distribute each sequence evenly to different GPUs.\n    assert(seqlen % (world_size * 2) == 0);\n    cu_seqlens_s[i] = seqlen / world_size;\n  }\n  __syncthreads();\n\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int num_threads = blockDim.x * gridDim.x;\n\n  for (int token_id = tid; token_id < total_tokens / world_size; token_id += num_threads) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n    int index = token_id - cu_seqlens_s[seq_id];\n    int offset = index < seq_len / 2 ? rank : (world_size - 1) * 2 - rank;\n    index += cu_seqlens_s[seq_id] * world_size + seq_len / 2 * offset;\n    output[token_id] = index;\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Read the half of a THD tensor\n **************************************************************************************************/\n\n__global__ void thd_read_half_tensor_kernel(void *half, void *tensor, int *cu_seqlens, int batch,\n                                            int hidden_size_in_bytes, int half_idx,\n                                            int dim_size_of_token) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / 2;\n  }\n  __syncthreads();\n\n  int warpid = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  int laneid = threadIdx.x % 32;\n  int num_warps = (blockDim.x * gridDim.x) / 32;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_float4s_per_token = hidden_size_in_bytes / sizeof(float4);\n\n  size_t offset = static_cast<size_t>(dim_size_of_token) * hidden_size_in_bytes;\n  half = reinterpret_cast<void *>(reinterpret_cast<char *>(half) + offset / 2 * blockIdx.y);\n  tensor = reinterpret_cast<void *>(reinterpret_cast<char *>(tensor) + offset * blockIdx.y);\n\n  for (int token_id = warpid; token_id < num_total_tokens; token_id += num_warps) {\n    int seqid = binary_search(token_id, cu_seqlens_s, batch + 1);\n\n    size_t offset_in_bytes = static_cast<size_t>(token_id) * hidden_size_in_bytes;\n    float4 *cur_half_token =\n        reinterpret_cast<float4 *>(reinterpret_cast<char *>(half) + offset_in_bytes);\n\n    offset_in_bytes =\n        (static_cast<size_t>(token_id) + cu_seqlens_s[seqid + half_idx]) * hidden_size_in_bytes;\n    float4 *cur_token =\n        reinterpret_cast<float4 *>(reinterpret_cast<char *>(tensor) + offset_in_bytes);\n\n    for (int idx = laneid; idx < num_float4s_per_token; idx += 32) {\n      cur_half_token[idx] = cur_token[idx];\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: softmax_lse related operations\n **************************************************************************************************/\n\ntemplate <bool lse_packed, typename Functor>\n__global__ void thd_lse_kernel(float *lse, float *half_lse, int *cu_seqlens, int batch,\n                               int num_heads, int lse_seqlen, int second_half_lse_seqlen) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / 2;\n  }\n  __syncthreads();\n\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int num_threads = blockDim.x * gridDim.x;\n  int num_total_tokens = cu_seqlens_s[batch];\n\n  for (int token_id = tid; token_id < num_total_tokens; token_id += num_threads) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    for (int head_id = blockIdx.y; head_id < num_heads; head_id += gridDim.y) {\n      size_t idx, half_idx;\n      if constexpr (lse_packed) {\n        idx = head_id * lse_seqlen + token_id + cu_seqlens_s[seq_id + 1];\n        half_idx = head_id * second_half_lse_seqlen + token_id;\n      } else {\n        size_t row = static_cast<size_t>(seq_id) * num_heads + head_id;\n        int col = token_id - cu_seqlens_s[seq_id];\n        int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n\n        idx = row * lse_seqlen + col + seq_len;\n        half_idx = row * second_half_lse_seqlen + col;\n      }\n\n      Functor::run(lse, half_lse, idx, half_idx);\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Out correction in forward\n **************************************************************************************************/\n\ntemplate <typename dtype, int only_second_half, int tile_size, bool lse_packed>\n__global__ void thd_out_correction_kernel(dtype *out, dtype *out_per_step, float *lse,\n                                          float *lse_per_step, int *cu_seqlens, int batch,\n                                          int num_heads, int dim_per_head, int lse_seqlen,\n                                          int lse_per_step_seqlen) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / (only_second_half + 1);\n  }\n  __syncthreads();\n\n  int tile_id = (blockIdx.x * blockDim.x + threadIdx.x) / tile_size;\n  int lane_id = threadIdx.x % tile_size;\n  int num_tiles = (blockDim.x * gridDim.x) / tile_size;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_loops_per_head = dim_per_head * sizeof(dtype) / sizeof(float4);\n\n  for (int token_id = tile_id; token_id < num_total_tokens; token_id += num_tiles) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    for (int head_id = blockIdx.y; head_id < num_heads; head_id += gridDim.y) {\n      size_t idx, idx_per_step;\n\n      if constexpr (lse_packed) {\n        idx = head_id * lse_seqlen + token_id + cu_seqlens_s[seq_id + 1] * only_second_half;\n        idx_per_step = head_id * lse_per_step_seqlen + token_id;\n      } else {\n        size_t row = static_cast<size_t>(seq_id) * num_heads + head_id;\n        int col = token_id - cu_seqlens_s[seq_id];\n        int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n        idx = row * lse_seqlen + col + seq_len * only_second_half;\n        idx_per_step = row * lse_per_step_seqlen + col;\n      }\n      float lse_corrected_exp = expf(lse_per_step[idx_per_step] - lse[idx]);\n\n      idx = token_id + cu_seqlens_s[seq_id + 1] * only_second_half;\n      idx = (idx * num_heads + head_id) * dim_per_head;\n      idx_per_step = (static_cast<size_t>(token_id) * num_heads + head_id) * dim_per_head;\n      dtype *cur_out = out + idx;\n      dtype *cur_out_per_step = out_per_step + idx_per_step;\n\n      for (int j = lane_id; j < num_loops_per_head; j += tile_size) {\n        float4 data_per_step = reinterpret_cast<float4 *>(cur_out_per_step)[j];\n        float4 data = reinterpret_cast<float4 *>(cur_out)[j];\n        dtype *p_per_step = reinterpret_cast<dtype *>(&data_per_step);\n        dtype *p = reinterpret_cast<dtype *>(&data);\n        for (int k = 0; k < sizeof(float4) / sizeof(dtype); k++) {\n          p[k] = p[k] +\n                 (p_per_step[k] == static_cast<dtype>(0.f)\n                      ? static_cast<dtype>(0.f)\n                      : static_cast<dtype>(static_cast<float>(p_per_step[k]) * lse_corrected_exp));\n        }\n        reinterpret_cast<float4 *>(cur_out)[j] = data;\n      }\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Gradients correction in backward\n **************************************************************************************************/\n\ntemplate <typename dtype, typename Functor_0, typename Functor_1, int functor_idx, int group_size>\n__global__ void thd_grad_correction_kernel(dtype *grad, dtype *grad_per_step, int *cu_seqlens,\n                                           int batch, int hidden_size, int dim_size_of_token) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    if constexpr (functor_idx < 2) {\n      cu_seqlens_s[i] = cu_seqlens[i] / 2;\n    } else {\n      cu_seqlens_s[i] = cu_seqlens[i];\n    }\n  }\n  __syncthreads();\n\n  int group_id = (blockIdx.x * blockDim.x + threadIdx.x) / group_size;\n  int lane_id = threadIdx.x % group_size;\n  int num_groups = (blockDim.x * gridDim.x) / group_size;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_inner_loops = hidden_size * sizeof(dtype) / sizeof(float4);\n\n  size_t offset = static_cast<size_t>(dim_size_of_token) * hidden_size;\n  if constexpr (functor_idx < 2) {\n    grad_per_step = grad_per_step + offset / 2 * blockIdx.y;\n  } else {\n    grad_per_step = grad_per_step + offset * blockIdx.y;\n  }\n  grad = grad + offset * blockIdx.y;\n\n  for (int token_id = group_id; token_id < num_total_tokens; token_id += num_groups) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n\n    int token_offset;\n    bool is_first_half;\n    if constexpr (functor_idx < 2) {\n      token_offset = cu_seqlens_s[seq_id + functor_idx];\n      is_first_half = (functor_idx == 0);\n    } else {\n      token_offset = 0;\n      int len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n      is_first_half = (token_id - cu_seqlens_s[seq_id]) < (len / 2);\n    }\n\n    dtype *token = &grad[(token_id + token_offset) * static_cast<size_t>(hidden_size)];\n    dtype *token_per_step = &grad_per_step[token_id * static_cast<size_t>(hidden_size)];\n    for (int idx = lane_id; idx < num_inner_loops; idx += group_size) {\n      if (is_first_half) {\n        Functor_0::run(token, token_per_step, idx);\n      } else {\n        Functor_1::run(token, token_per_step, idx);\n      }\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Read the half of a THD tensor\n **************************************************************************************************/\n\nvoid thd_read_half_tensor(const Tensor &tensor, const Tensor &cu_seqlens, Tensor &half,\n                          int half_idx, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(tensor.dim() == 3 || tensor.dim() == 4);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto tensor_shape = tensor.shape();\n\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n  NVTE_CHECK(cu_seqlens_shape[0] >= 2);\n\n  // Shapes of q and dq are [t, h, d], so the dimension of \"t\" is 0\n  // Shapes of kv and dkv are [2, t, h, d], so the dimension of \"t\" is 1\n  int seq_dim = tensor.dim() == 3 ? 0 : 1;\n\n  int batch = cu_seqlens_shape[0] - 1;\n  int num_heads = tensor_shape[seq_dim + 1];\n  int dim_per_head = tensor_shape[seq_dim + 2];\n  int hidden_size_in_bytes = (num_heads * dim_per_head * typeToNumBits(tensor.dtype())) / 8;\n\n  // For 128-bits load/store\n  NVTE_CHECK(hidden_size_in_bytes % 16 == 0);\n\n  // Launch Kernel\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (tensor_shape[seq_dim] / 2 * 32 + block - 1) / block;\n  unsigned int grid_y = 1;\n  for (int i = 0; i < seq_dim; i++) {\n    grid_y *= tensor_shape[i];\n  }\n  dim3 grid = {grid_x, grid_y};\n  thd_read_half_tensor_kernel<<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n      half.data.dptr, tensor.data.dptr, reinterpret_cast<int *>(cu_seqlens.data.dptr), batch,\n      hidden_size_in_bytes, half_idx, tensor_shape[seq_dim]);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: softmax_lse related operations\n **************************************************************************************************/\n\nvoid thd_second_half_lse_correction(Tensor lse, const Tensor &lse_per_step,\n                                    const Tensor &cu_seqlens, bool lse_packed,\n                                    cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(lse_per_step.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  int batch, num_heads, lse_seqlen, second_half_lse_seqlen;\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto lse_shape = lse.shape();\n  auto lse_per_step_shape = lse_per_step.shape();\n\n  if (lse_packed) {\n    NVTE_CHECK(lse.dim() == 2);\n    NVTE_CHECK(lse_per_step.dim() == 2);\n\n    batch = cu_seqlens_shape[0] - 1;\n    num_heads = lse_shape[0];\n    lse_seqlen = lse_shape[1];\n    second_half_lse_seqlen = lse_per_step_shape[1];\n\n    NVTE_CHECK(lse_per_step_shape[0] == num_heads);\n    NVTE_CHECK(second_half_lse_seqlen >= lse_seqlen / 2);\n  } else {\n    NVTE_CHECK(lse.dim() == 3);\n    NVTE_CHECK(lse_per_step.dim() == 3);\n\n    batch = lse_shape[0];\n    num_heads = lse_shape[1];\n    lse_seqlen = lse_shape[2];\n    second_half_lse_seqlen = lse_per_step_shape[2];\n\n    NVTE_CHECK(lse_per_step_shape[0] == batch);\n    NVTE_CHECK(lse_per_step_shape[1] == num_heads);\n    NVTE_CHECK(second_half_lse_seqlen == lse_seqlen / 2);\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n  }\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (lse_seqlen / 2 + block - 1) / block;\n  unsigned int grid_y = num_heads;\n  dim3 grid = {grid_x, grid_y};\n\n  if (lse_packed) {\n    thd_lse_kernel<true, LseCorrectionFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(lse_per_step.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    thd_lse_kernel<false, LseCorrectionFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(lse_per_step.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid thd_read_second_half_lse(const Tensor &lse, const Tensor &cu_seqlens, Tensor &half_lse,\n                              bool lse_packed, int second_half_lse_seqlen, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  int batch, num_heads, lse_seqlen;\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto lse_shape = lse.shape();\n\n  if (lse_packed) {\n    NVTE_CHECK(lse.dim() == 2);\n\n    batch = cu_seqlens_shape[0] - 1;\n    num_heads = lse_shape[0];\n    lse_seqlen = lse_shape[1];\n\n    NVTE_CHECK(second_half_lse_seqlen >= lse_seqlen / 2);\n  } else {\n    NVTE_CHECK(lse.dim() == 3);\n\n    batch = lse_shape[0];\n    num_heads = lse_shape[1];\n    lse_seqlen = lse_shape[2];\n\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n    NVTE_CHECK(second_half_lse_seqlen == lse_seqlen / 2);\n  }\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (lse_seqlen / 2 + block - 1) / block;\n  unsigned int grid_y = num_heads;\n  dim3 grid = {grid_x, grid_y};\n\n  if (lse_packed) {\n    thd_lse_kernel<true, ReadLseFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(half_lse.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    thd_lse_kernel<false, ReadLseFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(half_lse.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Out correction in forward\n **************************************************************************************************/\n\ntemplate <typename dtype, int only_second_half>\nstatic void thd_out_correction_helper(Tensor out, const Tensor &out_per_step, const Tensor &lse,\n                                      const Tensor &lse_per_step, const Tensor &cu_seqlens,\n                                      bool lse_packed, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(out.dtype() == out_per_step.dtype());\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(lse_per_step.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n\n  auto out_shape = out.shape();\n  auto lse_shape = lse.shape();\n  auto out_per_step_shape = out_per_step.shape();\n  auto lse_per_step_shape = lse_per_step.shape();\n  auto cu_seqlens_shape = cu_seqlens.shape();\n\n  int total_tokens = out_shape[0];\n  int num_heads = out_shape[1];\n  int dim_per_head = out_shape[2];\n\n  NVTE_CHECK(out_per_step_shape[0] == total_tokens / (only_second_half + 1));\n  NVTE_CHECK(out_per_step_shape[1] == num_heads);\n  NVTE_CHECK(out_per_step_shape[2] == dim_per_head);\n\n  int batch, lse_seqlen, lse_per_step_seqlen;\n  if (lse_packed) {\n    batch = cu_seqlens_shape[0] - 1;\n    lse_seqlen = lse_shape[1];\n    lse_per_step_seqlen = lse_per_step_shape[1];\n\n    NVTE_CHECK(lse_shape[0] == num_heads);\n    NVTE_CHECK(lse_seqlen >= total_tokens);\n    NVTE_CHECK(lse_per_step_shape[0] == num_heads);\n    NVTE_CHECK(lse_per_step_seqlen >= lse_seqlen / (only_second_half + 1));\n  } else {\n    batch = lse_shape[0];\n    lse_seqlen = lse_shape[2];\n    lse_per_step_seqlen = lse_per_step_shape[2];\n\n    NVTE_CHECK(lse_shape[1] == num_heads);\n    NVTE_CHECK(lse_per_step_shape[0] == batch);\n    NVTE_CHECK(lse_per_step_shape[1] == num_heads);\n    NVTE_CHECK(lse_per_step_seqlen == lse_seqlen / (only_second_half + 1));\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n  }\n\n  constexpr int tile = 16;\n  constexpr int block = 512;\n  unsigned int grid_x =\n      (static_cast<size_t>(total_tokens) / (only_second_half + 1) * tile + block - 1) / block;\n  dim3 grid = {grid_x, (unsigned int)num_heads};\n\n  if (lse_packed) {\n    thd_out_correction_kernel<dtype, only_second_half, tile, true>\n        <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n            reinterpret_cast<dtype *>(out.data.dptr),\n            reinterpret_cast<dtype *>(out_per_step.data.dptr),\n            reinterpret_cast<float *>(lse.data.dptr),\n            reinterpret_cast<float *>(lse_per_step.data.dptr),\n            reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, dim_per_head,\n            lse_seqlen, lse_per_step_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    thd_out_correction_kernel<dtype, only_second_half, tile, false>\n        <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n            reinterpret_cast<dtype *>(out.data.dptr),\n            reinterpret_cast<dtype *>(out_per_step.data.dptr),\n            reinterpret_cast<float *>(lse.data.dptr),\n            reinterpret_cast<float *>(lse_per_step.data.dptr),\n            reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, dim_per_head,\n            lse_seqlen, lse_per_step_seqlen);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid thd_out_correction(Tensor out, const Tensor &out_per_step, const Tensor &lse,\n                        const Tensor &lse_per_step, const Tensor &cu_seqlens, bool only_second_half,\n                        bool lse_packed, cudaStream_t stream) {\n  using namespace transformer_engine;\n  if (only_second_half) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n        out.dtype(), dtype,\n        thd_out_correction_helper<dtype, 1>(out, out_per_step, lse, lse_per_step, cu_seqlens,\n                                            lse_packed, stream););\n  } else {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n        out.dtype(), dtype,\n        thd_out_correction_helper<dtype, 0>(out, out_per_step, lse, lse_per_step, cu_seqlens,\n                                            lse_packed, stream););\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Gradients correction in backward\n **************************************************************************************************/\n\ntemplate <typename dtype, typename Functor_0, typename Functor_1, int functor_idx>\nstatic void thd_grad_correction_helper(Tensor grad, const Tensor &grad_per_step,\n                                       const Tensor &cu_seqlens, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(grad.dim() == 3 || grad.dim() == 4);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  auto grad_shape = grad.shape();\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto grad_per_step_shape = grad_per_step.shape();\n\n  // Shape of dq is [t, h, d], so the dimension of \"t\" is 0\n  // Shape of dkv is [2, t, h, d], so the dimension of \"t\" is 1\n  int seq_dim = grad.dim() == 3 ? 0 : 1;\n\n  int total_tokens = grad_shape[seq_dim];\n  int num_heads = grad_shape[seq_dim + 1];\n  int dim_per_head = grad_shape[seq_dim + 2];\n  int batch = cu_seqlens_shape[0] - 1;\n\n  if constexpr (functor_idx < 2) {\n    NVTE_CHECK(grad_per_step_shape[seq_dim] == total_tokens / 2);\n  } else {\n    NVTE_CHECK(grad_per_step_shape[seq_dim] == total_tokens);\n  }\n  NVTE_CHECK(grad_per_step_shape[seq_dim + 1] == num_heads);\n  NVTE_CHECK(grad_per_step_shape[seq_dim + 2] == dim_per_head);\n\n  size_t hidden_size = num_heads * dim_per_head;\n  NVTE_CHECK(((hidden_size * typeToNumBits(grad.dtype())) / 8) % 16 == 0);\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x;\n  if constexpr (functor_idx < 2) {\n    grid_x = (total_tokens / 2 * 32 + block - 1) / block;\n  } else {\n    grid_x = (total_tokens * 32 + block - 1) / block;\n  }\n  unsigned int grid_y = 1;\n  for (int i = 0; i < seq_dim; i++) {\n    grid_y *= grad_shape[i];\n  }\n  dim3 grid = {grid_x, grid_y};\n\n  thd_grad_correction_kernel<dtype, Functor_0, Functor_1, functor_idx, 32>\n      <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n          reinterpret_cast<dtype *>(grad.data.dptr),\n          reinterpret_cast<dtype *>(grad_per_step.data.dptr),\n          reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, hidden_size, total_tokens);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename dtype>\nstatic void thd_grad_dispatcher(Tensor grad, const Tensor &grad_per_step, const Tensor &cu_seqlens,\n                                const std::string &first_half, const std::string &second_half,\n                                cudaStream_t stream) {\n  using namespace transformer_engine;\n  if (first_half == \"add\" && second_half == \"none\") {\n    thd_grad_correction_helper<dtype, AddFunctor<dtype>, EmptyFunctor, 0>(grad, grad_per_step,\n                                                                          cu_seqlens, stream);\n  } else if (first_half == \"copy\" && second_half == \"none\") {\n    thd_grad_correction_helper<dtype, CopyFunctor, EmptyFunctor, 0>(grad, grad_per_step, cu_seqlens,\n                                                                    stream);\n  } else if (first_half == \"none\" && second_half == \"add\") {\n    thd_grad_correction_helper<dtype, EmptyFunctor, AddFunctor<dtype>, 1>(grad, grad_per_step,\n                                                                          cu_seqlens, stream);\n  } else if (first_half == \"none\" && second_half == \"copy\") {\n    thd_grad_correction_helper<dtype, EmptyFunctor, CopyFunctor, 1>(grad, grad_per_step, cu_seqlens,\n                                                                    stream);\n  } else if (first_half == \"add\" && second_half == \"copy\") {\n    thd_grad_correction_helper<dtype, AddFunctor<dtype>, CopyFunctor, 2>(grad, grad_per_step,\n                                                                         cu_seqlens, stream);\n  } else if (first_half == \"copy\" && second_half == \"add\") {\n    thd_grad_correction_helper<dtype, CopyFunctor, AddFunctor<dtype>, 2>(grad, grad_per_step,\n                                                                         cu_seqlens, stream);\n  } else {\n    NVTE_ERROR(\"Unsupported Functor of first half and second_half\\n\");\n  }\n}\n\nvoid thd_grad_correction(Tensor grad, const Tensor &grad_per_step, const Tensor &cu_seqlens,\n                         const std::string &first_half, const std::string &second_half,\n                         cudaStream_t stream) {\n  using namespace transformer_engine;\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      grad.dtype(), dtype,\n      thd_grad_dispatcher<dtype>(grad, grad_per_step, cu_seqlens, first_half, second_half,\n                                 stream););\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Generate partitioned indices for input tokens\n **************************************************************************************************/\n\nvoid thd_get_partitioned_indices(const Tensor &cu_seqlens, Tensor output, int total_tokens,\n                                 int world_size, int rank, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto output_shape = output.shape();\n\n  NVTE_CHECK(cu_seqlens_shape[0] >= 2);\n  NVTE_CHECK(rank >= 0 && rank < world_size);\n  NVTE_CHECK(world_size > 0);\n  NVTE_CHECK(total_tokens > 0 && total_tokens % (world_size * 2) == 0);\n\n  int batch = cu_seqlens_shape[0] - 1;\n\n  constexpr unsigned int block = 256;\n  unsigned int grid = (output_shape[0] + block - 1) / block;\n  thd_partition_indices_kernel<<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n      reinterpret_cast<int *>(output.data.dptr), reinterpret_cast<int *>(cu_seqlens.data.dptr),\n      batch, total_tokens, world_size, rank);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace context_parallel\n}  // namespace transformer_engine\n\nvoid nvte_cp_thd_read_half_tensor(const NVTETensor &tensor, const NVTETensor &cu_seqlens,\n                                  NVTETensor half, int half_idx, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_read_half_tensor);\n  using namespace transformer_engine;\n\n  context_parallel::thd_read_half_tensor(*convertNVTETensorCheck(tensor),\n                                         *convertNVTETensorCheck(cu_seqlens),\n                                         *convertNVTETensorCheck(half), half_idx, stream);\n}\n\nvoid nvte_cp_thd_second_half_lse_correction(NVTETensor lse, const NVTETensor &lse_per_step,\n                                            const NVTETensor &cu_seqlens, int lse_packed,\n                                            cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_second_half_lse_correction);\n  using namespace transformer_engine;\n\n  context_parallel::thd_second_half_lse_correction(\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(lse_per_step),\n      *convertNVTETensorCheck(cu_seqlens), lse_packed, stream);\n}\n\nvoid nvte_cp_thd_read_second_half_lse(const NVTETensor &lse, const NVTETensor &cu_seqlens,\n                                      NVTETensor half_lse, int lse_packed,\n                                      int second_half_lse_seqlen, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_read_second_half_lse);\n  using namespace transformer_engine;\n\n  context_parallel::thd_read_second_half_lse(\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(cu_seqlens),\n      *convertNVTETensorCheck(half_lse), lse_packed, second_half_lse_seqlen, stream);\n}\n\nvoid nvte_cp_thd_out_correction(NVTETensor out, const NVTETensor &out_per_step,\n                                const NVTETensor &lse, const NVTETensor &lse_per_step,\n                                const NVTETensor &cu_seqlens, int only_second_half, int lse_packed,\n                                cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_out_correction);\n  using namespace transformer_engine;\n\n  context_parallel::thd_out_correction(\n      *convertNVTETensorCheck(out), *convertNVTETensorCheck(out_per_step),\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(lse_per_step),\n      *convertNVTETensorCheck(cu_seqlens), only_second_half, lse_packed, stream);\n}\n\nvoid nvte_cp_thd_grad_correction(NVTETensor grad, const NVTETensor &grad_per_step,\n                                 const NVTETensor &cu_seqlens, const char *first_half,\n                                 const char *second_half, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_grad_correction);\n  using namespace transformer_engine;\n\n  std::string first_half_str(first_half);\n  std::string second_half_str(second_half);\n\n  context_parallel::thd_grad_correction(\n      *convertNVTETensorCheck(grad), *convertNVTETensorCheck(grad_per_step),\n      *convertNVTETensorCheck(cu_seqlens), first_half_str, second_half_str, stream);\n}\n\nvoid nvte_cp_thd_get_partitioned_indices(const NVTETensor &cu_seqlens, NVTETensor output,\n                                         int total_tokens, int world_size, int rank,\n                                         cudaStream_t stream) {\n  NVTE_API_CALL(nvte_thd_get_partitioned_indices);\n  using namespace transformer_engine;\n\n  context_parallel::thd_get_partitioned_indices(*convertNVTETensorCheck(cu_seqlens),\n                                                *convertNVTETensorCheck(output), total_tokens,\n                                                world_size, rank, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_bf16.h>\n\n#include \"../common.h\"\n#include \"transformer_engine/fused_attn.h\"\n\nnamespace transformer_engine {\nnamespace context_parallel {\n\nstruct LseCorrectionFunctor {\n  __forceinline__ __device__ static void run(float *lse, float *half_lse, size_t idx,\n                                             size_t half_idx) {\n    float val = lse[idx];\n    float val_per_step = half_lse[half_idx];\n    float max_scale = max(val, val_per_step);\n    float min_scale = min(val, val_per_step);\n    lse[idx] = max_scale + log1pf(expf(min_scale - max_scale));\n  }\n};\n\nstruct ReadLseFunctor {\n  __forceinline__ __device__ static void run(float *lse, float *half_lse, size_t idx,\n                                             size_t half_idx) {\n    half_lse[half_idx] = lse[idx];\n  }\n};\n\nstruct EmptyFunctor {\n  __forceinline__ __device__ static void run(void *token, void *token_per_step, int idx) {}\n};\n\nstruct CopyFunctor {\n  __forceinline__ __device__ static void run(void *token, void *token_per_step, int idx) {\n    reinterpret_cast<float4 *>(token)[idx] = reinterpret_cast<float4 *>(token_per_step)[idx];\n  }\n};\n\ntemplate <typename dtype>\nstruct AddFunctor {\n  __forceinline__ __device__ static void run(dtype *token, dtype *token_per_step, int idx) {\n    float4 d_ = reinterpret_cast<float4 *>(token)[idx];\n    dtype *p_ = reinterpret_cast<dtype *>(&d_);\n\n    float4 d = reinterpret_cast<float4 *>(token_per_step)[idx];\n    dtype *p = reinterpret_cast<dtype *>(&d);\n\n#pragma unroll\n    for (int i = 0; i < sizeof(float4) / sizeof(dtype); i++) {\n      p_[i] = p_[i] + p[i];\n    }\n\n    reinterpret_cast<float4 *>(token)[idx] = d_;\n  }\n};\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Binary search an array for a target value\n **************************************************************************************************/\n\n__forceinline__ __device__ int binary_search(int target, int *array, int len) {\n  int left = 1, right = len - 1;\n  while (left < right) {\n    int mid = (left + right) / 2;\n    if (array[mid] <= target) {\n      left = mid + 1;\n    } else {\n      right = mid;\n    }\n  }\n  return left - 1;\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Generate partitioned indices for input tokens\n **************************************************************************************************/\n__global__ void thd_partition_indices_kernel(int *output, int *cu_seqlens, int batch,\n                                             int total_tokens, int world_size, int rank) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    int seqlen = cu_seqlens[i];\n    // Currently we assume that each sequence length is divisible by (world_size*2) since we have\n    // to distribute each sequence evenly to different GPUs.\n    assert(seqlen % (world_size * 2) == 0);\n    cu_seqlens_s[i] = seqlen / world_size;\n  }\n  __syncthreads();\n\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int num_threads = blockDim.x * gridDim.x;\n\n  for (int token_id = tid; token_id < total_tokens / world_size; token_id += num_threads) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n    int index = token_id - cu_seqlens_s[seq_id];\n    int offset = index < seq_len / 2 ? rank : (world_size - 1) * 2 - rank;\n    index += cu_seqlens_s[seq_id] * world_size + seq_len / 2 * offset;\n    output[token_id] = index;\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Read the half of a THD tensor\n **************************************************************************************************/\n\n__global__ void thd_read_half_tensor_kernel(void *half, void *tensor, int *cu_seqlens, int batch,\n                                            int hidden_size_in_bytes, int half_idx,\n                                            int dim_size_of_token) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / 2;\n  }\n  __syncthreads();\n\n  int warpid = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  int laneid = threadIdx.x % 32;\n  int num_warps = (blockDim.x * gridDim.x) / 32;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_float4s_per_token = hidden_size_in_bytes / sizeof(float4);\n\n  size_t offset = static_cast<size_t>(dim_size_of_token) * hidden_size_in_bytes;\n  half = reinterpret_cast<void *>(reinterpret_cast<char *>(half) + offset / 2 * blockIdx.y);\n  tensor = reinterpret_cast<void *>(reinterpret_cast<char *>(tensor) + offset * blockIdx.y);\n\n  for (int token_id = warpid; token_id < num_total_tokens; token_id += num_warps) {\n    int seqid = binary_search(token_id, cu_seqlens_s, batch + 1);\n\n    size_t offset_in_bytes = static_cast<size_t>(token_id) * hidden_size_in_bytes;\n    float4 *cur_half_token =\n        reinterpret_cast<float4 *>(reinterpret_cast<char *>(half) + offset_in_bytes);\n\n    offset_in_bytes =\n        (static_cast<size_t>(token_id) + cu_seqlens_s[seqid + half_idx]) * hidden_size_in_bytes;\n    float4 *cur_token =\n        reinterpret_cast<float4 *>(reinterpret_cast<char *>(tensor) + offset_in_bytes);\n\n    for (int idx = laneid; idx < num_float4s_per_token; idx += 32) {\n      cur_half_token[idx] = cur_token[idx];\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: softmax_lse related operations\n **************************************************************************************************/\n\ntemplate <bool lse_packed, typename Functor>\n__global__ void thd_lse_kernel(float *lse, float *half_lse, int *cu_seqlens, int batch,\n                               int num_heads, int lse_seqlen, int second_half_lse_seqlen) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / 2;\n  }\n  __syncthreads();\n\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int num_threads = blockDim.x * gridDim.x;\n  int num_total_tokens = cu_seqlens_s[batch];\n\n  for (int token_id = tid; token_id < num_total_tokens; token_id += num_threads) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    for (int head_id = blockIdx.y; head_id < num_heads; head_id += gridDim.y) {\n      size_t idx, half_idx;\n      if constexpr (lse_packed) {\n        idx = head_id * lse_seqlen + token_id + cu_seqlens_s[seq_id + 1];\n        half_idx = head_id * second_half_lse_seqlen + token_id;\n      } else {\n        size_t row = static_cast<size_t>(seq_id) * num_heads + head_id;\n        int col = token_id - cu_seqlens_s[seq_id];\n        int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n\n        idx = row * lse_seqlen + col + seq_len;\n        half_idx = row * second_half_lse_seqlen + col;\n      }\n\n      Functor::run(lse, half_lse, idx, half_idx);\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Out correction in forward\n **************************************************************************************************/\n\ntemplate <typename dtype, int only_second_half, int tile_size, bool lse_packed>\n__global__ void thd_out_correction_kernel(dtype *out, dtype *out_per_step, float *lse,\n                                          float *lse_per_step, int *cu_seqlens, int batch,\n                                          int num_heads, int dim_per_head, int lse_seqlen,\n                                          int lse_per_step_seqlen) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    cu_seqlens_s[i] = cu_seqlens[i] / (only_second_half + 1);\n  }\n  __syncthreads();\n\n  int tile_id = (blockIdx.x * blockDim.x + threadIdx.x) / tile_size;\n  int lane_id = threadIdx.x % tile_size;\n  int num_tiles = (blockDim.x * gridDim.x) / tile_size;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_loops_per_head = dim_per_head * sizeof(dtype) / sizeof(float4);\n\n  for (int token_id = tile_id; token_id < num_total_tokens; token_id += num_tiles) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n    for (int head_id = blockIdx.y; head_id < num_heads; head_id += gridDim.y) {\n      size_t idx, idx_per_step;\n\n      if constexpr (lse_packed) {\n        idx = head_id * lse_seqlen + token_id + cu_seqlens_s[seq_id + 1] * only_second_half;\n        idx_per_step = head_id * lse_per_step_seqlen + token_id;\n      } else {\n        size_t row = static_cast<size_t>(seq_id) * num_heads + head_id;\n        int col = token_id - cu_seqlens_s[seq_id];\n        int seq_len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n        idx = row * lse_seqlen + col + seq_len * only_second_half;\n        idx_per_step = row * lse_per_step_seqlen + col;\n      }\n      float lse_corrected_exp = expf(lse_per_step[idx_per_step] - lse[idx]);\n\n      idx = token_id + cu_seqlens_s[seq_id + 1] * only_second_half;\n      idx = (idx * num_heads + head_id) * dim_per_head;\n      idx_per_step = (static_cast<size_t>(token_id) * num_heads + head_id) * dim_per_head;\n      dtype *cur_out = out + idx;\n      dtype *cur_out_per_step = out_per_step + idx_per_step;\n\n      for (int j = lane_id; j < num_loops_per_head; j += tile_size) {\n        float4 data_per_step = reinterpret_cast<float4 *>(cur_out_per_step)[j];\n        float4 data = reinterpret_cast<float4 *>(cur_out)[j];\n        dtype *p_per_step = reinterpret_cast<dtype *>(&data_per_step);\n        dtype *p = reinterpret_cast<dtype *>(&data);\n        for (int k = 0; k < sizeof(float4) / sizeof(dtype); k++) {\n          p[k] = p[k] +\n                 (p_per_step[k] == static_cast<dtype>(0.f)\n                      ? static_cast<dtype>(0.f)\n                      : static_cast<dtype>(static_cast<float>(p_per_step[k]) * lse_corrected_exp));\n        }\n        reinterpret_cast<float4 *>(cur_out)[j] = data;\n      }\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Gradients correction in backward\n **************************************************************************************************/\n\ntemplate <typename dtype, typename Functor_0, typename Functor_1, int functor_idx, int group_size>\n__global__ void thd_grad_correction_kernel(dtype *grad, dtype *grad_per_step, int *cu_seqlens,\n                                           int batch, int hidden_size, int dim_size_of_token) {\n  extern __shared__ int cu_seqlens_s[];\n  for (int i = threadIdx.x; i <= batch; i += blockDim.x) {\n    if constexpr (functor_idx < 2) {\n      cu_seqlens_s[i] = cu_seqlens[i] / 2;\n    } else {\n      cu_seqlens_s[i] = cu_seqlens[i];\n    }\n  }\n  __syncthreads();\n\n  int group_id = (blockIdx.x * blockDim.x + threadIdx.x) / group_size;\n  int lane_id = threadIdx.x % group_size;\n  int num_groups = (blockDim.x * gridDim.x) / group_size;\n  int num_total_tokens = cu_seqlens_s[batch];\n  int num_inner_loops = hidden_size * sizeof(dtype) / sizeof(float4);\n\n  size_t offset = static_cast<size_t>(dim_size_of_token) * hidden_size;\n  if constexpr (functor_idx < 2) {\n    grad_per_step = grad_per_step + offset / 2 * blockIdx.y;\n  } else {\n    grad_per_step = grad_per_step + offset * blockIdx.y;\n  }\n  grad = grad + offset * blockIdx.y;\n\n  for (int token_id = group_id; token_id < num_total_tokens; token_id += num_groups) {\n    int seq_id = binary_search(token_id, cu_seqlens_s, batch + 1);\n\n    int token_offset;\n    bool is_first_half;\n    if constexpr (functor_idx < 2) {\n      token_offset = cu_seqlens_s[seq_id + functor_idx];\n      is_first_half = (functor_idx == 0);\n    } else {\n      token_offset = 0;\n      int len = cu_seqlens_s[seq_id + 1] - cu_seqlens_s[seq_id];\n      is_first_half = (token_id - cu_seqlens_s[seq_id]) < (len / 2);\n    }\n\n    dtype *token = &grad[(token_id + token_offset) * static_cast<size_t>(hidden_size)];\n    dtype *token_per_step = &grad_per_step[token_id * static_cast<size_t>(hidden_size)];\n    for (int idx = lane_id; idx < num_inner_loops; idx += group_size) {\n      if (is_first_half) {\n        Functor_0::run(token, token_per_step, idx);\n      } else {\n        Functor_1::run(token, token_per_step, idx);\n      }\n    }\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Read the half of a THD tensor\n **************************************************************************************************/\n\nvoid thd_read_half_tensor(const Tensor &tensor, const Tensor &cu_seqlens, Tensor &half,\n                          int half_idx, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(tensor.dim() == 3 || tensor.dim() == 4);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto tensor_shape = tensor.shape();\n\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n  NVTE_CHECK(cu_seqlens_shape[0] >= 2);\n\n  // Shapes of q and dq are [t, h, d], so the dimension of \"t\" is 0\n  // Shapes of kv and dkv are [2, t, h, d], so the dimension of \"t\" is 1\n  int seq_dim = tensor.dim() == 3 ? 0 : 1;\n\n  int batch = cu_seqlens_shape[0] - 1;\n  int num_heads = tensor_shape[seq_dim + 1];\n  int dim_per_head = tensor_shape[seq_dim + 2];\n  int hidden_size_in_bytes = (num_heads * dim_per_head * typeToNumBits(tensor.dtype())) / 8;\n\n  // For 128-bits load/store\n  NVTE_CHECK(hidden_size_in_bytes % 16 == 0);\n\n  // Launch Kernel\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (tensor_shape[seq_dim] / 2 * 32 + block - 1) / block;\n  unsigned int grid_y = 1;\n  for (int i = 0; i < seq_dim; i++) {\n    grid_y *= tensor_shape[i];\n  }\n  dim3 grid = {grid_x, grid_y};\n  thd_read_half_tensor_kernel<<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n      half.data.dptr, tensor.data.dptr, reinterpret_cast<int *>(cu_seqlens.data.dptr), batch,\n      hidden_size_in_bytes, half_idx, tensor_shape[seq_dim]);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: softmax_lse related operations\n **************************************************************************************************/\n\nvoid thd_second_half_lse_correction(Tensor lse, const Tensor &lse_per_step,\n                                    const Tensor &cu_seqlens, bool lse_packed,\n                                    hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(lse_per_step.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  int batch, num_heads, lse_seqlen, second_half_lse_seqlen;\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto lse_shape = lse.shape();\n  auto lse_per_step_shape = lse_per_step.shape();\n\n  if (lse_packed) {\n    NVTE_CHECK(lse.dim() == 2);\n    NVTE_CHECK(lse_per_step.dim() == 2);\n\n    batch = cu_seqlens_shape[0] - 1;\n    num_heads = lse_shape[0];\n    lse_seqlen = lse_shape[1];\n    second_half_lse_seqlen = lse_per_step_shape[1];\n\n    NVTE_CHECK(lse_per_step_shape[0] == num_heads);\n    NVTE_CHECK(second_half_lse_seqlen >= lse_seqlen / 2);\n  } else {\n    NVTE_CHECK(lse.dim() == 3);\n    NVTE_CHECK(lse_per_step.dim() == 3);\n\n    batch = lse_shape[0];\n    num_heads = lse_shape[1];\n    lse_seqlen = lse_shape[2];\n    second_half_lse_seqlen = lse_per_step_shape[2];\n\n    NVTE_CHECK(lse_per_step_shape[0] == batch);\n    NVTE_CHECK(lse_per_step_shape[1] == num_heads);\n    NVTE_CHECK(second_half_lse_seqlen == lse_seqlen / 2);\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n  }\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (lse_seqlen / 2 + block - 1) / block;\n  unsigned int grid_y = num_heads;\n  dim3 grid = {grid_x, grid_y};\n\n  if (lse_packed) {\n    thd_lse_kernel<true, LseCorrectionFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(lse_per_step.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    thd_lse_kernel<false, LseCorrectionFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(lse_per_step.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid thd_read_second_half_lse(const Tensor &lse, const Tensor &cu_seqlens, Tensor &half_lse,\n                              bool lse_packed, int second_half_lse_seqlen, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  int batch, num_heads, lse_seqlen;\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto lse_shape = lse.shape();\n\n  if (lse_packed) {\n    NVTE_CHECK(lse.dim() == 2);\n\n    batch = cu_seqlens_shape[0] - 1;\n    num_heads = lse_shape[0];\n    lse_seqlen = lse_shape[1];\n\n    NVTE_CHECK(second_half_lse_seqlen >= lse_seqlen / 2);\n  } else {\n    NVTE_CHECK(lse.dim() == 3);\n\n    batch = lse_shape[0];\n    num_heads = lse_shape[1];\n    lse_seqlen = lse_shape[2];\n\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n    NVTE_CHECK(second_half_lse_seqlen == lse_seqlen / 2);\n  }\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x = (lse_seqlen / 2 + block - 1) / block;\n  unsigned int grid_y = num_heads;\n  dim3 grid = {grid_x, grid_y};\n\n  if (lse_packed) {\n    thd_lse_kernel<true, ReadLseFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(half_lse.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    thd_lse_kernel<false, ReadLseFunctor><<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n        reinterpret_cast<float *>(lse.data.dptr), reinterpret_cast<float *>(half_lse.data.dptr),\n        reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, lse_seqlen,\n        second_half_lse_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Out correction in forward\n **************************************************************************************************/\n\ntemplate <typename dtype, int only_second_half>\nstatic void thd_out_correction_helper(Tensor out, const Tensor &out_per_step, const Tensor &lse,\n                                      const Tensor &lse_per_step, const Tensor &cu_seqlens,\n                                      bool lse_packed, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(out.dtype() == out_per_step.dtype());\n  NVTE_CHECK(lse.dtype() == DType::kFloat32);\n  NVTE_CHECK(lse_per_step.dtype() == DType::kFloat32);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n\n  auto out_shape = out.shape();\n  auto lse_shape = lse.shape();\n  auto out_per_step_shape = out_per_step.shape();\n  auto lse_per_step_shape = lse_per_step.shape();\n  auto cu_seqlens_shape = cu_seqlens.shape();\n\n  int total_tokens = out_shape[0];\n  int num_heads = out_shape[1];\n  int dim_per_head = out_shape[2];\n\n  NVTE_CHECK(out_per_step_shape[0] == total_tokens / (only_second_half + 1));\n  NVTE_CHECK(out_per_step_shape[1] == num_heads);\n  NVTE_CHECK(out_per_step_shape[2] == dim_per_head);\n\n  int batch, lse_seqlen, lse_per_step_seqlen;\n  if (lse_packed) {\n    batch = cu_seqlens_shape[0] - 1;\n    lse_seqlen = lse_shape[1];\n    lse_per_step_seqlen = lse_per_step_shape[1];\n\n    NVTE_CHECK(lse_shape[0] == num_heads);\n    NVTE_CHECK(lse_seqlen >= total_tokens);\n    NVTE_CHECK(lse_per_step_shape[0] == num_heads);\n    NVTE_CHECK(lse_per_step_seqlen >= lse_seqlen / (only_second_half + 1));\n  } else {\n    batch = lse_shape[0];\n    lse_seqlen = lse_shape[2];\n    lse_per_step_seqlen = lse_per_step_shape[2];\n\n    NVTE_CHECK(lse_shape[1] == num_heads);\n    NVTE_CHECK(lse_per_step_shape[0] == batch);\n    NVTE_CHECK(lse_per_step_shape[1] == num_heads);\n    NVTE_CHECK(lse_per_step_seqlen == lse_seqlen / (only_second_half + 1));\n    NVTE_CHECK(cu_seqlens_shape[0] == batch + 1);\n  }\n\n  constexpr int tile = 16;\n  constexpr int block = 512;\n  unsigned int grid_x =\n      (static_cast<size_t>(total_tokens) / (only_second_half + 1) * tile + block - 1) / block;\n  dim3 grid = {grid_x, (unsigned int)num_heads};\n\n  if (lse_packed) {\n    thd_out_correction_kernel<dtype, only_second_half, tile, true>\n        <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n            reinterpret_cast<dtype *>(out.data.dptr),\n            reinterpret_cast<dtype *>(out_per_step.data.dptr),\n            reinterpret_cast<float *>(lse.data.dptr),\n            reinterpret_cast<float *>(lse_per_step.data.dptr),\n            reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, dim_per_head,\n            lse_seqlen, lse_per_step_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    thd_out_correction_kernel<dtype, only_second_half, tile, false>\n        <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n            reinterpret_cast<dtype *>(out.data.dptr),\n            reinterpret_cast<dtype *>(out_per_step.data.dptr),\n            reinterpret_cast<float *>(lse.data.dptr),\n            reinterpret_cast<float *>(lse_per_step.data.dptr),\n            reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, num_heads, dim_per_head,\n            lse_seqlen, lse_per_step_seqlen);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid thd_out_correction(Tensor out, const Tensor &out_per_step, const Tensor &lse,\n                        const Tensor &lse_per_step, const Tensor &cu_seqlens, bool only_second_half,\n                        bool lse_packed, hipStream_t stream) {\n  using namespace transformer_engine;\n  if (only_second_half) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n        out.dtype(), dtype,\n        thd_out_correction_helper<dtype, 1>(out, out_per_step, lse, lse_per_step, cu_seqlens,\n                                            lse_packed, stream););\n  } else {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n        out.dtype(), dtype,\n        thd_out_correction_helper<dtype, 0>(out, out_per_step, lse, lse_per_step, cu_seqlens,\n                                            lse_packed, stream););\n  }\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Gradients correction in backward\n **************************************************************************************************/\n\ntemplate <typename dtype, typename Functor_0, typename Functor_1, int functor_idx>\nstatic void thd_grad_correction_helper(Tensor grad, const Tensor &grad_per_step,\n                                       const Tensor &cu_seqlens, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(grad.dim() == 3 || grad.dim() == 4);\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  auto grad_shape = grad.shape();\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto grad_per_step_shape = grad_per_step.shape();\n\n  // Shape of dq is [t, h, d], so the dimension of \"t\" is 0\n  // Shape of dkv is [2, t, h, d], so the dimension of \"t\" is 1\n  int seq_dim = grad.dim() == 3 ? 0 : 1;\n\n  int total_tokens = grad_shape[seq_dim];\n  int num_heads = grad_shape[seq_dim + 1];\n  int dim_per_head = grad_shape[seq_dim + 2];\n  int batch = cu_seqlens_shape[0] - 1;\n\n  if constexpr (functor_idx < 2) {\n    NVTE_CHECK(grad_per_step_shape[seq_dim] == total_tokens / 2);\n  } else {\n    NVTE_CHECK(grad_per_step_shape[seq_dim] == total_tokens);\n  }\n  NVTE_CHECK(grad_per_step_shape[seq_dim + 1] == num_heads);\n  NVTE_CHECK(grad_per_step_shape[seq_dim + 2] == dim_per_head);\n\n  size_t hidden_size = num_heads * dim_per_head;\n  NVTE_CHECK(((hidden_size * typeToNumBits(grad.dtype())) / 8) % 16 == 0);\n\n  constexpr unsigned int block = 256;\n  unsigned int grid_x;\n  if constexpr (functor_idx < 2) {\n    grid_x = (total_tokens / 2 * 32 + block - 1) / block;\n  } else {\n    grid_x = (total_tokens * 32 + block - 1) / block;\n  }\n  unsigned int grid_y = 1;\n  for (int i = 0; i < seq_dim; i++) {\n    grid_y *= grad_shape[i];\n  }\n  dim3 grid = {grid_x, grid_y};\n\n  thd_grad_correction_kernel<dtype, Functor_0, Functor_1, functor_idx, 32>\n      <<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n          reinterpret_cast<dtype *>(grad.data.dptr),\n          reinterpret_cast<dtype *>(grad_per_step.data.dptr),\n          reinterpret_cast<int *>(cu_seqlens.data.dptr), batch, hidden_size, total_tokens);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename dtype>\nstatic void thd_grad_dispatcher(Tensor grad, const Tensor &grad_per_step, const Tensor &cu_seqlens,\n                                const std::string &first_half, const std::string &second_half,\n                                hipStream_t stream) {\n  using namespace transformer_engine;\n  if (first_half == \"add\" && second_half == \"none\") {\n    thd_grad_correction_helper<dtype, AddFunctor<dtype>, EmptyFunctor, 0>(grad, grad_per_step,\n                                                                          cu_seqlens, stream);\n  } else if (first_half == \"copy\" && second_half == \"none\") {\n    thd_grad_correction_helper<dtype, CopyFunctor, EmptyFunctor, 0>(grad, grad_per_step, cu_seqlens,\n                                                                    stream);\n  } else if (first_half == \"none\" && second_half == \"add\") {\n    thd_grad_correction_helper<dtype, EmptyFunctor, AddFunctor<dtype>, 1>(grad, grad_per_step,\n                                                                          cu_seqlens, stream);\n  } else if (first_half == \"none\" && second_half == \"copy\") {\n    thd_grad_correction_helper<dtype, EmptyFunctor, CopyFunctor, 1>(grad, grad_per_step, cu_seqlens,\n                                                                    stream);\n  } else if (first_half == \"add\" && second_half == \"copy\") {\n    thd_grad_correction_helper<dtype, AddFunctor<dtype>, CopyFunctor, 2>(grad, grad_per_step,\n                                                                         cu_seqlens, stream);\n  } else if (first_half == \"copy\" && second_half == \"add\") {\n    thd_grad_correction_helper<dtype, CopyFunctor, AddFunctor<dtype>, 2>(grad, grad_per_step,\n                                                                         cu_seqlens, stream);\n  } else {\n    NVTE_ERROR(\"Unsupported Functor of first half and second_half\\n\");\n  }\n}\n\nvoid thd_grad_correction(Tensor grad, const Tensor &grad_per_step, const Tensor &cu_seqlens,\n                         const std::string &first_half, const std::string &second_half,\n                         hipStream_t stream) {\n  using namespace transformer_engine;\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      grad.dtype(), dtype,\n      thd_grad_dispatcher<dtype>(grad, grad_per_step, cu_seqlens, first_half, second_half,\n                                 stream););\n}\n\n/***************************************************************************************************\n * Support THD format for Context Parallel: Generate partitioned indices for input tokens\n **************************************************************************************************/\n\nvoid thd_get_partitioned_indices(const Tensor &cu_seqlens, Tensor output, int total_tokens,\n                                 int world_size, int rank, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_CHECK(cu_seqlens.dtype() == DType::kInt32);\n  NVTE_CHECK(cu_seqlens.dim() == 1);\n\n  auto cu_seqlens_shape = cu_seqlens.shape();\n  auto output_shape = output.shape();\n\n  NVTE_CHECK(cu_seqlens_shape[0] >= 2);\n  NVTE_CHECK(rank >= 0 && rank < world_size);\n  NVTE_CHECK(world_size > 0);\n  NVTE_CHECK(total_tokens > 0 && total_tokens % (world_size * 2) == 0);\n\n  int batch = cu_seqlens_shape[0] - 1;\n\n  constexpr unsigned int block = 256;\n  unsigned int grid = (output_shape[0] + block - 1) / block;\n  thd_partition_indices_kernel<<<grid, block, sizeof(int) * (batch + 1), stream>>>(\n      reinterpret_cast<int *>(output.data.dptr), reinterpret_cast<int *>(cu_seqlens.data.dptr),\n      batch, total_tokens, world_size, rank);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace context_parallel\n}  // namespace transformer_engine\n\nvoid nvte_cp_thd_read_half_tensor(const NVTETensor &tensor, const NVTETensor &cu_seqlens,\n                                  NVTETensor half, int half_idx, hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_read_half_tensor);\n  using namespace transformer_engine;\n\n  context_parallel::thd_read_half_tensor(*convertNVTETensorCheck(tensor),\n                                         *convertNVTETensorCheck(cu_seqlens),\n                                         *convertNVTETensorCheck(half), half_idx, stream);\n}\n\nvoid nvte_cp_thd_second_half_lse_correction(NVTETensor lse, const NVTETensor &lse_per_step,\n                                            const NVTETensor &cu_seqlens, int lse_packed,\n                                            hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_second_half_lse_correction);\n  using namespace transformer_engine;\n\n  context_parallel::thd_second_half_lse_correction(\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(lse_per_step),\n      *convertNVTETensorCheck(cu_seqlens), lse_packed, stream);\n}\n\nvoid nvte_cp_thd_read_second_half_lse(const NVTETensor &lse, const NVTETensor &cu_seqlens,\n                                      NVTETensor half_lse, int lse_packed,\n                                      int second_half_lse_seqlen, hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_read_second_half_lse);\n  using namespace transformer_engine;\n\n  context_parallel::thd_read_second_half_lse(\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(cu_seqlens),\n      *convertNVTETensorCheck(half_lse), lse_packed, second_half_lse_seqlen, stream);\n}\n\nvoid nvte_cp_thd_out_correction(NVTETensor out, const NVTETensor &out_per_step,\n                                const NVTETensor &lse, const NVTETensor &lse_per_step,\n                                const NVTETensor &cu_seqlens, int only_second_half, int lse_packed,\n                                hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_out_correction);\n  using namespace transformer_engine;\n\n  context_parallel::thd_out_correction(\n      *convertNVTETensorCheck(out), *convertNVTETensorCheck(out_per_step),\n      *convertNVTETensorCheck(lse), *convertNVTETensorCheck(lse_per_step),\n      *convertNVTETensorCheck(cu_seqlens), only_second_half, lse_packed, stream);\n}\n\nvoid nvte_cp_thd_grad_correction(NVTETensor grad, const NVTETensor &grad_per_step,\n                                 const NVTETensor &cu_seqlens, const char *first_half,\n                                 const char *second_half, hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_grad_correction);\n  using namespace transformer_engine;\n\n  std::string first_half_str(first_half);\n  std::string second_half_str(second_half);\n\n  context_parallel::thd_grad_correction(\n      *convertNVTETensorCheck(grad), *convertNVTETensorCheck(grad_per_step),\n      *convertNVTETensorCheck(cu_seqlens), first_half_str, second_half_str, stream);\n}\n\nvoid nvte_cp_thd_get_partitioned_indices(const NVTETensor &cu_seqlens, NVTETensor output,\n                                         int total_tokens, int world_size, int rank,\n                                         hipStream_t stream) {\n  NVTE_API_CALL(nvte_thd_get_partitioned_indices);\n  using namespace transformer_engine;\n\n  context_parallel::thd_get_partitioned_indices(*convertNVTETensorCheck(cu_seqlens),\n                                                *convertNVTETensorCheck(output), total_tokens,\n                                                world_size, rank, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_attn/context_parallel.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <algorithm>\n#include <cmath>\n\n#include \"../common.h\"\n#include \"../cudnn_utils.h\"\n#include \"transformer_engine/fused_attn.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_attn {\n\nusing namespace transformer_engine;\n\n// get matrix strides based on matrix type\nvoid generateMatrixStrides(int64_t b, int64_t h, int64_t s_q, int64_t s_kv, int64_t d,\n                           int64_t *strideA, NVTE_QKV_Layout layout, NVTE_QKV_Matrix matrix) {\n  constexpr int batch_dim_idx = 0;\n  constexpr int head_dim_idx = 1;\n  constexpr int seqlen_dim_idx = 2;\n  constexpr int hidden_dim_idx = 3;\n\n  constexpr int seqlen_transpose_dim_idx = 3;\n  constexpr int hidden_transpose_dim_idx = 2;\n\n  constexpr int seqlen_q_dim_idx = 2;\n  constexpr int seqlen_kv_dim_idx = 3;\n\n  switch (layout) {\n    case NVTE_QKV_Layout::NVTE_SB3HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBH3D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_dim_idx] = b * 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_transpose_dim_idx] = b * 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SB2HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SBH2D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_dim_idx] = b * 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_transpose_dim_idx] = b * 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_SBHD_SBHD_SBHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BS3HD:\n    case NVTE_QKV_Layout::NVTE_T3HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSH3D:\n    case NVTE_QKV_Layout::NVTE_TH3D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_dim_idx] = 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_transpose_dim_idx] = 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BS2HD:\n    case NVTE_QKV_Layout::NVTE_THD_T2HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BSH2D:\n    case NVTE_QKV_Layout::NVTE_THD_TH2D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_dim_idx] = 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_transpose_dim_idx] = 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_THD_THD_THD:\n    case NVTE_QKV_Layout::NVTE_THD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_BSHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_THD_BSHD_BSHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_SBHD_BSHD_BSHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_THD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_BSHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_THD_SBHD_SBHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BHSD_BHSD_BHSD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * s_q * d;\n        strideA[head_dim_idx] = s_q * d;\n        strideA[seqlen_dim_idx] = d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = h * s_kv * d;\n        strideA[head_dim_idx] = s_kv * d;\n        strideA[seqlen_dim_idx] = d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * s_kv * d;\n        strideA[head_dim_idx] = s_kv * d;\n        strideA[seqlen_transpose_dim_idx] = d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n  }\n\n  if (matrix == NVTE_QKV_Matrix::NVTE_S_Matrix) {\n    strideA[seqlen_kv_dim_idx] = 1;\n    strideA[seqlen_q_dim_idx] = s_kv;\n    strideA[head_dim_idx] = s_q * s_kv;\n    strideA[batch_dim_idx] = h * s_q * s_kv;\n  }\n}\n\nbool allowAllConfig(cudnnBackendDescriptor_t engine_config) {\n  (void)engine_config;\n  return false;\n}\n\ncudnn_frontend::Tensor tensor_create(cudnnDataType_t type, int64_t id, int64_t const *dim,\n                                     int64_t const *stride, bool is_virtual, bool is_value) {\n  int nbDims = 4;\n  auto tensor_created =\n      cudnn_frontend::TensorBuilder()\n          .setDim(nbDims, dim)\n          .setStride(nbDims, stride)\n          .setId(id)\n          .setAlignment(16)  // 16B alignment is needed to run a tensor core engine\n          .setDataType(type)\n          .setVirtual(is_virtual)\n          .setByValue(is_value)\n          .build();\n  return tensor_created;\n}\n\ncudnn_frontend::Tensor tensor_create_with_offset(\n    cudnnDataType_t type, int64_t id, int64_t const *dim, int64_t const *stride, bool is_virtual,\n    bool is_value, std::shared_ptr<cudnn_frontend::Tensor> raggedOffset) {\n  int nbDims = 4;\n  auto tensor_created =\n      cudnn_frontend::TensorBuilder()\n          .setDim(nbDims, dim)\n          .setStride(nbDims, stride)\n          .setId(id)\n          .setAlignment(16)  // 16B alignment is needed to run a tensor core engine\n          .setDataType(type)\n          .setVirtual(is_virtual)\n          .setByValue(is_value)\n          .setRaggedOffset(raggedOffset)\n          .build();\n  return tensor_created;\n}\n\ncudnn_frontend::PointWiseDesc pw_desc_create(cudnnDataType_t type, cudnnPointwiseMode_t mode) {\n  auto pw_desc_created =\n      cudnn_frontend::PointWiseDescBuilder().setMode(mode).setComputeType(type).build();\n  return pw_desc_created;\n}\n\ncudnn_frontend::Operation unary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                             cudnn_frontend::Tensor const &yDesc,\n                                             cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\ncudnn_frontend::Operation binary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                              cudnn_frontend::Tensor const &bDesc,\n                                              cudnn_frontend::Tensor const &yDesc,\n                                              cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setbDesc(bDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\ncudnn_frontend::Operation ternary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                               cudnn_frontend::Tensor const &bDesc,\n                                               cudnn_frontend::Tensor const &tDesc,\n                                               cudnn_frontend::Tensor const &yDesc,\n                                               cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setbDesc(bDesc)\n          .settDesc(tDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\n// convert cu_seqlens to actual_seqlens\n__global__ void cu_seqlens_to_actual_seqlens(int64_t actual_b, int64_t max_b,\n                                             int32_t const *const q_cu_seqlens,\n                                             int32_t const *const kv_cu_seqlens, int32_t *q_seqlens,\n                                             int32_t *kv_seqlens) {\n  size_t tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < actual_b) {\n    q_seqlens[tid] = q_cu_seqlens[tid + 1] - q_cu_seqlens[tid];\n    kv_seqlens[tid] = kv_cu_seqlens[tid + 1] - kv_cu_seqlens[tid];\n  } else if (tid < max_b) {\n    q_seqlens[tid] = 0;\n    kv_seqlens[tid] = 0;\n  }\n}\n\n// convert cu_seqlens_padded to offsets\ntemplate <class OFFSETS_T>\n__device__ void cu_seqlens_padded_to_offsets_impl(\n    NVTE_QKV_Layout_Group layout_group, int64_t actual_b, int64_t max_b, int64_t h, int64_t hg,\n    int64_t d_qk, int64_t d_v, const int32_t *cu_seqlens_q_padded,\n    const int32_t *cu_seqlens_kv_padded, OFFSETS_T *offsets_q, OFFSETS_T *offsets_k,\n    OFFSETS_T *offsets_v, OFFSETS_T *offsets_o, OFFSETS_T *offsets_s) {\n  size_t tid = blockIdx.x * blockDim.x + threadIdx.x;\n  auto cu_seqlens_id = min(tid, actual_b);\n  if (tid <= max_b) {\n    if (offsets_s != nullptr) {\n      offsets_s[tid] = h * cu_seqlens_q_padded[cu_seqlens_id];\n    }\n    if (offsets_q != nullptr && offsets_o != nullptr) {\n      offsets_o[tid] = h * d_v * cu_seqlens_q_padded[cu_seqlens_id];\n      switch (layout_group) {\n        case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n        case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n          offsets_q[tid] = h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_3HD:\n        case NVTE_QKV_Layout_Group::NVTE_H3D:\n          offsets_q[tid] = 3 * h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n        case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n          offsets_q[tid] = h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n      }\n    }\n    if (offsets_k != nullptr && offsets_v != nullptr) {\n      switch (layout_group) {\n        case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n        case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n          offsets_k[tid] = hg * d_qk * cu_seqlens_kv_padded[cu_seqlens_id];\n          offsets_v[tid] = hg * d_v * cu_seqlens_kv_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_3HD:\n        case NVTE_QKV_Layout_Group::NVTE_H3D:\n          offsets_k[tid] = 3 * h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          offsets_v[tid] = offsets_k[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n        case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n          offsets_k[tid] = 2 * hg * d_qk * cu_seqlens_kv_padded[cu_seqlens_id];\n          offsets_v[tid] = offsets_k[cu_seqlens_id];\n          break;\n      }\n    }\n  }\n}\n\n__global__ void cu_seqlens_padded_to_offsets(NVTE_QKV_Layout_Group layout_group, int64_t actual_b,\n                                             int64_t max_b, int64_t h, int64_t hg, int64_t d_qk,\n                                             int64_t d_v, const int32_t *cu_seqlens_q_padded,\n                                             const int32_t *cu_seqlens_kv_padded,\n                                             DType offset_dtype, void *offsets_q, void *offsets_k,\n                                             void *offsets_v, void *offsets_o, void *offsets_s) {\n  if (offset_dtype == DType::kInt32) {\n    cu_seqlens_padded_to_offsets_impl<int32_t>(\n        layout_group, actual_b, max_b, h, hg, d_qk, d_v, cu_seqlens_q_padded, cu_seqlens_kv_padded,\n        reinterpret_cast<int32_t *>(offsets_q), reinterpret_cast<int32_t *>(offsets_k),\n        reinterpret_cast<int32_t *>(offsets_v), reinterpret_cast<int32_t *>(offsets_o),\n        reinterpret_cast<int32_t *>(offsets_s));\n  } else {\n    assert(offset_dtype == DType::kInt64 && \"expect int64\");\n    cu_seqlens_padded_to_offsets_impl<int64_t>(\n        layout_group, actual_b, max_b, h, hg, d_qk, d_v, cu_seqlens_q_padded, cu_seqlens_kv_padded,\n        reinterpret_cast<int64_t *>(offsets_q), reinterpret_cast<int64_t *>(offsets_k),\n        reinterpret_cast<int64_t *>(offsets_v), reinterpret_cast<int64_t *>(offsets_o),\n        reinterpret_cast<int64_t *>(offsets_s));\n  }\n}\n\nDType get_ragged_offset_dtype(NVTE_QKV_Layout_Group layout_group, int64_t num_attn_heads,\n                              int64_t num_gqa_groups, int64_t max_seqlen_q, int64_t max_seqlen_kv,\n                              int64_t head_dim_qk, int64_t head_dim_v) {\n  std::array<int64_t, 4> offsets_qkvo{};\n  switch (layout_group) {\n    case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n    case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n      offsets_qkvo[0] = num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = num_gqa_groups * head_dim_qk * max_seqlen_kv;\n      offsets_qkvo[2] = num_gqa_groups * head_dim_v * max_seqlen_kv;\n      break;\n    case NVTE_QKV_Layout_Group::NVTE_3HD:\n    case NVTE_QKV_Layout_Group::NVTE_H3D:\n      offsets_qkvo[0] = 3 * num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = offsets_qkvo[0];\n      offsets_qkvo[2] = offsets_qkvo[0];\n      break;\n    case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n    case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n      offsets_qkvo[0] = num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = 2 * num_gqa_groups * head_dim_qk * max_seqlen_kv;\n      offsets_qkvo[2] = offsets_qkvo[1];\n      break;\n  }\n\n  offsets_qkvo[3] = num_attn_heads * head_dim_qk * max_seqlen_q;\n\n  size_t max_offset = *std::max_element(offsets_qkvo.begin(), offsets_qkvo.end());\n  if (max_offset > std::numeric_limits<int32_t>::max()) {\n    return DType::kInt64;\n  }\n\n  return DType::kInt32;\n}\n\n// quantize batch size\nsize_t get_max_batch_size(size_t batch_size) {\n  size_t max_b = batch_size;\n  size_t log2_b = ceil(log2(batch_size));\n  // batch size is expected to be 10s-100s\n  // b = 1, ..., 32   -> max_b = 32\n  // b = 33, ..., 512 -> max_b = next power of 2\n  // b = 513, ...     -> max_b = increment by 512\n  if (log2_b <= 5) {\n    max_b = 32;\n  } else if (log2_b <= 9) {\n    max_b = pow(2, log2_b);\n  } else {\n    max_b = (batch_size + 511) / 512 * 512;\n  }\n  return max_b;\n}\n\n// quantize token count\nsize_t get_max_tokens(size_t num_tokens) {\n  // token count is expected to be 1k's-100k's\n  // t = 0, ..., 1024   -> max_t = 1024\n  // t = 1025, ..., 32k -> max_t = next power of 2\n  // t = 32k+1, ...     -> max_t = increment by 32k\n  size_t log2_t = ceil(log2(num_tokens));\n  size_t max_t = 0;\n  if (log2_t <= 10) {\n    max_t = 1024;\n  } else if (log2_t <= 15) {\n    max_t = pow(2, log2_t);\n  } else {\n    max_t = (num_tokens + 32767) / 32768 * 32768;\n  }\n  return max_t;\n}\n\n__global__ void populate_rng_state_kernel(int64_t *rng_state_dst, const int64_t *const seed,\n                                          int64_t offset) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid > 0) return;\n  rng_state_dst[0] = seed[0];\n  rng_state_dst[1] = offset;\n}\n\n__global__ void get_runtime_num_segments_kernel(int32_t *cu_seqlen, size_t len, uint32_t *out) {\n  int tid = blockDim.x * blockIdx.x + threadIdx.x;\n  if (tid >= len) return;\n\n  if (cu_seqlen[tid] > 0) {\n    // atomicAdd only support 32 bits dtype\n    atomicAdd(out, 1);\n  }\n}\n\nvoid PopulateRngStateAsync(void *rng_state_dst, const void *seed, size_t q_max_seqlen,\n                           size_t kv_max_seqlen, NVTE_Fused_Attn_Backend backend,\n                           cudaStream_t stream) {\n  size_t increment = 0;\n  if (backend == NVTE_Fused_Attn_Backend::NVTE_F16_arbitrary_seqlen) {\n    increment = 16;\n  } else {\n    constexpr int threads_per_cta = 128;\n    increment = (q_max_seqlen * kv_max_seqlen + threads_per_cta - 1) / threads_per_cta;\n  }\n  auto offset = FusedAttnOffsetManager::Instance().GetAndUpdateOffset(increment);\n  populate_rng_state_kernel<<<1, 1, 0, stream>>>(reinterpret_cast<int64_t *>(rng_state_dst),\n                                                 reinterpret_cast<const int64_t *>(seed), offset);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nuint32_t GetRuntimeNumSegments(void *cu_seqlen, void *workspace, size_t len, cudaStream_t stream) {\n  // workspace size requires 4 bytes\n  uint32_t *dout = static_cast<uint32_t *>(workspace);\n  uint32_t hout{};\n  NVTE_CHECK_CUDA(cudaMemsetAsync(dout, 0, sizeof(uint32_t), stream));\n  constexpr int threads = 128;\n  const int blocks = (len - 1) / threads + 1;\n  get_runtime_num_segments_kernel<<<blocks, threads, 0, stream>>>(static_cast<int32_t *>(cu_seqlen),\n                                                                  len, dout);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n  NVTE_CHECK_CUDA(cudaMemcpyAsync(&hout, dout, sizeof(uint32_t), cudaMemcpyDeviceToHost, stream));\n  NVTE_CHECK_CUDA(cudaStreamSynchronize(stream));\n  return hout;\n}\n\n__global__ void extract_seed_and_offset(int64_t *rng_state_ptr, bool captured, int64_t *seed_ptr,\n                                        uint64_t seed_val, int64_t *offset_ptr, uint64_t offset_val,\n                                        uint32_t offset_intragraph) {\n  if (captured) {\n    rng_state_ptr[0] = *seed_ptr;\n    rng_state_ptr[1] = static_cast<int64_t>(*offset_ptr + static_cast<int64_t>(offset_intragraph));\n  } else {\n    rng_state_ptr[0] = static_cast<int64_t>(seed_val);\n    rng_state_ptr[1] = static_cast<int64_t>(offset_val);\n  }\n}\n\n}  // namespace fused_attn\n}  // namespace transformer_engine\n\nvoid nvte_extract_seed_and_offset(int64_t *rng_state_ptr, int captured, int64_t *seed_ptr,\n                                  uint64_t seed_val, int64_t *offset_ptr, uint64_t offset_val,\n                                  uint32_t offset_intragraph, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_extract_seed_and_offset);\n  using namespace transformer_engine;\n\n  fused_attn::extract_seed_and_offset<<<1, 1, 0, stream>>>(\n      rng_state_ptr, captured, seed_ptr, seed_val, offset_ptr, offset_val, offset_intragraph);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n#include <cmath>\n\n#include \"../common.h\"\n#include \"../cudnn_utils.h\"\n#include \"transformer_engine/fused_attn.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_attn {\n\nusing namespace transformer_engine;\n\n// get matrix strides based on matrix type\nvoid generateMatrixStrides(int64_t b, int64_t h, int64_t s_q, int64_t s_kv, int64_t d,\n                           int64_t *strideA, NVTE_QKV_Layout layout, NVTE_QKV_Matrix matrix) {\n  constexpr int batch_dim_idx = 0;\n  constexpr int head_dim_idx = 1;\n  constexpr int seqlen_dim_idx = 2;\n  constexpr int hidden_dim_idx = 3;\n\n  constexpr int seqlen_transpose_dim_idx = 3;\n  constexpr int hidden_transpose_dim_idx = 2;\n\n  constexpr int seqlen_q_dim_idx = 2;\n  constexpr int seqlen_kv_dim_idx = 3;\n\n  switch (layout) {\n    case NVTE_QKV_Layout::NVTE_SB3HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBH3D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_dim_idx] = b * 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_transpose_dim_idx] = b * 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SB2HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SBH2D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_dim_idx] = b * 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_transpose_dim_idx] = b * 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_SBHD_SBHD_SBHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BS3HD:\n    case NVTE_QKV_Layout::NVTE_T3HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSH3D:\n    case NVTE_QKV_Layout::NVTE_TH3D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_dim_idx] = 3 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_q * 3 * h * d;\n        strideA[head_dim_idx] = 3 * d;\n        strideA[seqlen_transpose_dim_idx] = 3 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BS2HD:\n    case NVTE_QKV_Layout::NVTE_THD_T2HD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BSH2D:\n    case NVTE_QKV_Layout::NVTE_THD_TH2D:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_dim_idx] = 2 * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * 2 * h * d;\n        strideA[head_dim_idx] = 2 * d;\n        strideA[seqlen_transpose_dim_idx] = 2 * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_THD_THD_THD:\n    case NVTE_QKV_Layout::NVTE_THD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_BSHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_THD_BSHD_BSHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_SBHD_BSHD_BSHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_SBHD_BSHD_BSHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = s_kv * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BSHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_THD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_BSHD_SBHD_SBHD:\n    case NVTE_QKV_Layout::NVTE_Paged_KV_THD_SBHD_SBHD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = b * h * d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_transpose_dim_idx] = b * h * d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = s_q * h * d;\n        strideA[head_dim_idx] = d;\n        strideA[seqlen_dim_idx] = h * d;\n        strideA[hidden_dim_idx] = 1;\n      }\n      break;\n    case NVTE_QKV_Layout::NVTE_BHSD_BHSD_BHSD:\n      if ((matrix == NVTE_QKV_Matrix::NVTE_Q_Matrix) ||\n          (matrix == NVTE_QKV_Matrix::NVTE_O_Matrix)) {\n        strideA[batch_dim_idx] = h * s_q * d;\n        strideA[head_dim_idx] = s_q * d;\n        strideA[seqlen_dim_idx] = d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix)) {\n        strideA[batch_dim_idx] = h * s_kv * d;\n        strideA[head_dim_idx] = s_kv * d;\n        strideA[seqlen_dim_idx] = d;\n        strideA[hidden_dim_idx] = 1;\n      } else if ((matrix == NVTE_QKV_Matrix::NVTE_K_Matrix_Transpose) ||\n                 (matrix == NVTE_QKV_Matrix::NVTE_V_Matrix_Transpose)) {\n        strideA[batch_dim_idx] = h * s_kv * d;\n        strideA[head_dim_idx] = s_kv * d;\n        strideA[seqlen_transpose_dim_idx] = d;\n        strideA[hidden_transpose_dim_idx] = 1;\n      }\n      break;\n  }\n\n  if (matrix == NVTE_QKV_Matrix::NVTE_S_Matrix) {\n    strideA[seqlen_kv_dim_idx] = 1;\n    strideA[seqlen_q_dim_idx] = s_kv;\n    strideA[head_dim_idx] = s_q * s_kv;\n    strideA[batch_dim_idx] = h * s_q * s_kv;\n  }\n}\n\nbool allowAllConfig(cudnnBackendDescriptor_t engine_config) {\n  (void)engine_config;\n  return false;\n}\n\ncudnn_frontend::Tensor tensor_create(hipdnnDataType_t type, int64_t id, int64_t const *dim,\n                                     int64_t const *stride, bool is_virtual, bool is_value) {\n  int nbDims = 4;\n  auto tensor_created =\n      cudnn_frontend::TensorBuilder()\n          .setDim(nbDims, dim)\n          .setStride(nbDims, stride)\n          .setId(id)\n          .setAlignment(16)  // 16B alignment is needed to run a tensor core engine\n          .setDataType(type)\n          .setVirtual(is_virtual)\n          .setByValue(is_value)\n          .build();\n  return tensor_created;\n}\n\ncudnn_frontend::Tensor tensor_create_with_offset(\n    hipdnnDataType_t type, int64_t id, int64_t const *dim, int64_t const *stride, bool is_virtual,\n    bool is_value, std::shared_ptr<cudnn_frontend::Tensor> raggedOffset) {\n  int nbDims = 4;\n  auto tensor_created =\n      cudnn_frontend::TensorBuilder()\n          .setDim(nbDims, dim)\n          .setStride(nbDims, stride)\n          .setId(id)\n          .setAlignment(16)  // 16B alignment is needed to run a tensor core engine\n          .setDataType(type)\n          .setVirtual(is_virtual)\n          .setByValue(is_value)\n          .setRaggedOffset(raggedOffset)\n          .build();\n  return tensor_created;\n}\n\ncudnn_frontend::PointWiseDesc pw_desc_create(hipdnnDataType_t type, cudnnPointwiseMode_t mode) {\n  auto pw_desc_created =\n      cudnn_frontend::PointWiseDescBuilder().setMode(mode).setComputeType(type).build();\n  return pw_desc_created;\n}\n\ncudnn_frontend::Operation unary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                             cudnn_frontend::Tensor const &yDesc,\n                                             cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\ncudnn_frontend::Operation binary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                              cudnn_frontend::Tensor const &bDesc,\n                                              cudnn_frontend::Tensor const &yDesc,\n                                              cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setbDesc(bDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\ncudnn_frontend::Operation ternary_pw_op_create(cudnn_frontend::Tensor const &xDesc,\n                                               cudnn_frontend::Tensor const &bDesc,\n                                               cudnn_frontend::Tensor const &tDesc,\n                                               cudnn_frontend::Tensor const &yDesc,\n                                               cudnn_frontend::PointWiseDesc const &pwDesc) {\n  auto pw_op_created =\n      cudnn_frontend::OperationBuilder(CUDNN_BACKEND_OPERATION_POINTWISE_DESCRIPTOR)\n          .setxDesc(xDesc)\n          .setbDesc(bDesc)\n          .settDesc(tDesc)\n          .setyDesc(yDesc)\n          .setpwDesc(pwDesc)\n          .build();\n  return pw_op_created;\n}\n\n// convert cu_seqlens to actual_seqlens\n__global__ void cu_seqlens_to_actual_seqlens(int64_t actual_b, int64_t max_b,\n                                             int32_t const *const q_cu_seqlens,\n                                             int32_t const *const kv_cu_seqlens, int32_t *q_seqlens,\n                                             int32_t *kv_seqlens) {\n  size_t tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < actual_b) {\n    q_seqlens[tid] = q_cu_seqlens[tid + 1] - q_cu_seqlens[tid];\n    kv_seqlens[tid] = kv_cu_seqlens[tid + 1] - kv_cu_seqlens[tid];\n  } else if (tid < max_b) {\n    q_seqlens[tid] = 0;\n    kv_seqlens[tid] = 0;\n  }\n}\n\n// convert cu_seqlens_padded to offsets\ntemplate <class OFFSETS_T>\n__device__ void cu_seqlens_padded_to_offsets_impl(\n    NVTE_QKV_Layout_Group layout_group, int64_t actual_b, int64_t max_b, int64_t h, int64_t hg,\n    int64_t d_qk, int64_t d_v, const int32_t *cu_seqlens_q_padded,\n    const int32_t *cu_seqlens_kv_padded, OFFSETS_T *offsets_q, OFFSETS_T *offsets_k,\n    OFFSETS_T *offsets_v, OFFSETS_T *offsets_o, OFFSETS_T *offsets_s) {\n  size_t tid = blockIdx.x * blockDim.x + threadIdx.x;\n  auto cu_seqlens_id = min(tid, actual_b);\n  if (tid <= max_b) {\n    if (offsets_s != nullptr) {\n      offsets_s[tid] = h * cu_seqlens_q_padded[cu_seqlens_id];\n    }\n    if (offsets_q != nullptr && offsets_o != nullptr) {\n      offsets_o[tid] = h * d_v * cu_seqlens_q_padded[cu_seqlens_id];\n      switch (layout_group) {\n        case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n        case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n          offsets_q[tid] = h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_3HD:\n        case NVTE_QKV_Layout_Group::NVTE_H3D:\n          offsets_q[tid] = 3 * h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n        case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n          offsets_q[tid] = h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          break;\n      }\n    }\n    if (offsets_k != nullptr && offsets_v != nullptr) {\n      switch (layout_group) {\n        case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n        case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n          offsets_k[tid] = hg * d_qk * cu_seqlens_kv_padded[cu_seqlens_id];\n          offsets_v[tid] = hg * d_v * cu_seqlens_kv_padded[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_3HD:\n        case NVTE_QKV_Layout_Group::NVTE_H3D:\n          offsets_k[tid] = 3 * h * d_qk * cu_seqlens_q_padded[cu_seqlens_id];\n          offsets_v[tid] = offsets_k[cu_seqlens_id];\n          break;\n        case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n        case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n          offsets_k[tid] = 2 * hg * d_qk * cu_seqlens_kv_padded[cu_seqlens_id];\n          offsets_v[tid] = offsets_k[cu_seqlens_id];\n          break;\n      }\n    }\n  }\n}\n\n__global__ void cu_seqlens_padded_to_offsets(NVTE_QKV_Layout_Group layout_group, int64_t actual_b,\n                                             int64_t max_b, int64_t h, int64_t hg, int64_t d_qk,\n                                             int64_t d_v, const int32_t *cu_seqlens_q_padded,\n                                             const int32_t *cu_seqlens_kv_padded,\n                                             DType offset_dtype, void *offsets_q, void *offsets_k,\n                                             void *offsets_v, void *offsets_o, void *offsets_s) {\n  if (offset_dtype == DType::kInt32) {\n    cu_seqlens_padded_to_offsets_impl<int32_t>(\n        layout_group, actual_b, max_b, h, hg, d_qk, d_v, cu_seqlens_q_padded, cu_seqlens_kv_padded,\n        reinterpret_cast<int32_t *>(offsets_q), reinterpret_cast<int32_t *>(offsets_k),\n        reinterpret_cast<int32_t *>(offsets_v), reinterpret_cast<int32_t *>(offsets_o),\n        reinterpret_cast<int32_t *>(offsets_s));\n  } else {\n    assert(offset_dtype == DType::kInt64 && \"expect int64\");\n    cu_seqlens_padded_to_offsets_impl<int64_t>(\n        layout_group, actual_b, max_b, h, hg, d_qk, d_v, cu_seqlens_q_padded, cu_seqlens_kv_padded,\n        reinterpret_cast<int64_t *>(offsets_q), reinterpret_cast<int64_t *>(offsets_k),\n        reinterpret_cast<int64_t *>(offsets_v), reinterpret_cast<int64_t *>(offsets_o),\n        reinterpret_cast<int64_t *>(offsets_s));\n  }\n}\n\nDType get_ragged_offset_dtype(NVTE_QKV_Layout_Group layout_group, int64_t num_attn_heads,\n                              int64_t num_gqa_groups, int64_t max_seqlen_q, int64_t max_seqlen_kv,\n                              int64_t head_dim_qk, int64_t head_dim_v) {\n  std::array<int64_t, 4> offsets_qkvo{};\n  switch (layout_group) {\n    case NVTE_QKV_Layout_Group::NVTE_HD_HD_HD:\n    case NVTE_QKV_Layout_Group::NVTE_Paged_KV_HD_HD_HD:\n      offsets_qkvo[0] = num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = num_gqa_groups * head_dim_qk * max_seqlen_kv;\n      offsets_qkvo[2] = num_gqa_groups * head_dim_v * max_seqlen_kv;\n      break;\n    case NVTE_QKV_Layout_Group::NVTE_3HD:\n    case NVTE_QKV_Layout_Group::NVTE_H3D:\n      offsets_qkvo[0] = 3 * num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = offsets_qkvo[0];\n      offsets_qkvo[2] = offsets_qkvo[0];\n      break;\n    case NVTE_QKV_Layout_Group::NVTE_HD_2HD:\n    case NVTE_QKV_Layout_Group::NVTE_HD_H2D:\n      offsets_qkvo[0] = num_attn_heads * head_dim_qk * max_seqlen_q;\n      offsets_qkvo[1] = 2 * num_gqa_groups * head_dim_qk * max_seqlen_kv;\n      offsets_qkvo[2] = offsets_qkvo[1];\n      break;\n  }\n\n  offsets_qkvo[3] = num_attn_heads * head_dim_qk * max_seqlen_q;\n\n  size_t max_offset = *std::max_element(offsets_qkvo.begin(), offsets_qkvo.end());\n  if (max_offset > std::numeric_limits<int32_t>::max()) {\n    return DType::kInt64;\n  }\n\n  return DType::kInt32;\n}\n\n// quantize batch size\nsize_t get_max_batch_size(size_t batch_size) {\n  size_t max_b = batch_size;\n  size_t log2_b = ceil(log2(batch_size));\n  // batch size is expected to be 10s-100s\n  // b = 1, ..., 32   -> max_b = 32\n  // b = 33, ..., 512 -> max_b = next power of 2\n  // b = 513, ...     -> max_b = increment by 512\n  if (log2_b <= 5) {\n    max_b = 32;\n  } else if (log2_b <= 9) {\n    max_b = pow(2, log2_b);\n  } else {\n    max_b = (batch_size + 511) / 512 * 512;\n  }\n  return max_b;\n}\n\n// quantize token count\nsize_t get_max_tokens(size_t num_tokens) {\n  // token count is expected to be 1k's-100k's\n  // t = 0, ..., 1024   -> max_t = 1024\n  // t = 1025, ..., 32k -> max_t = next power of 2\n  // t = 32k+1, ...     -> max_t = increment by 32k\n  size_t log2_t = ceil(log2(num_tokens));\n  size_t max_t = 0;\n  if (log2_t <= 10) {\n    max_t = 1024;\n  } else if (log2_t <= 15) {\n    max_t = pow(2, log2_t);\n  } else {\n    max_t = (num_tokens + 32767) / 32768 * 32768;\n  }\n  return max_t;\n}\n\n__global__ void populate_rng_state_kernel(int64_t *rng_state_dst, const int64_t *const seed,\n                                          int64_t offset) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid > 0) return;\n  rng_state_dst[0] = seed[0];\n  rng_state_dst[1] = offset;\n}\n\n__global__ void get_runtime_num_segments_kernel(int32_t *cu_seqlen, size_t len, uint32_t *out) {\n  int tid = blockDim.x * blockIdx.x + threadIdx.x;\n  if (tid >= len) return;\n\n  if (cu_seqlen[tid] > 0) {\n    // atomicAdd only support 32 bits dtype\n    atomicAdd(out, 1);\n  }\n}\n\nvoid PopulateRngStateAsync(void *rng_state_dst, const void *seed, size_t q_max_seqlen,\n                           size_t kv_max_seqlen, NVTE_Fused_Attn_Backend backend,\n                           hipStream_t stream) {\n  size_t increment = 0;\n  if (backend == NVTE_Fused_Attn_Backend::NVTE_F16_arbitrary_seqlen) {\n    increment = 16;\n  } else {\n    constexpr int threads_per_cta = 128;\n    increment = (q_max_seqlen * kv_max_seqlen + threads_per_cta - 1) / threads_per_cta;\n  }\n  auto offset = FusedAttnOffsetManager::Instance().GetAndUpdateOffset(increment);\n  populate_rng_state_kernel<<<1, 1, 0, stream>>>(reinterpret_cast<int64_t *>(rng_state_dst),\n                                                 reinterpret_cast<const int64_t *>(seed), offset);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nuint32_t GetRuntimeNumSegments(void *cu_seqlen, void *workspace, size_t len, hipStream_t stream) {\n  // workspace size requires 4 bytes\n  uint32_t *dout = static_cast<uint32_t *>(workspace);\n  uint32_t hout{};\n  NVTE_CHECK_CUDA(hipMemsetAsync(dout, 0, sizeof(uint32_t), stream));\n  constexpr int threads = 128;\n  const int blocks = (len - 1) / threads + 1;\n  get_runtime_num_segments_kernel<<<blocks, threads, 0, stream>>>(static_cast<int32_t *>(cu_seqlen),\n                                                                  len, dout);\n  NVTE_CHECK_CUDA(hipGetLastError());\n  NVTE_CHECK_CUDA(hipMemcpyAsync(&hout, dout, sizeof(uint32_t), hipMemcpyDeviceToHost, stream));\n  NVTE_CHECK_CUDA(hipStreamSynchronize(stream));\n  return hout;\n}\n\n__global__ void extract_seed_and_offset(int64_t *rng_state_ptr, bool captured, int64_t *seed_ptr,\n                                        uint64_t seed_val, int64_t *offset_ptr, uint64_t offset_val,\n                                        uint32_t offset_intragraph) {\n  if (captured) {\n    rng_state_ptr[0] = *seed_ptr;\n    rng_state_ptr[1] = static_cast<int64_t>(*offset_ptr + static_cast<int64_t>(offset_intragraph));\n  } else {\n    rng_state_ptr[0] = static_cast<int64_t>(seed_val);\n    rng_state_ptr[1] = static_cast<int64_t>(offset_val);\n  }\n}\n\n}  // namespace fused_attn\n}  // namespace transformer_engine\n\nvoid nvte_extract_seed_and_offset(int64_t *rng_state_ptr, int captured, int64_t *seed_ptr,\n                                  uint64_t seed_val, int64_t *offset_ptr, uint64_t offset_val,\n                                  uint32_t offset_intragraph, hipStream_t stream) {\n  NVTE_API_CALL(nvte_extract_seed_and_offset);\n  using namespace transformer_engine;\n\n  fused_attn::extract_seed_and_offset<<<1, 1, 0, stream>>>(\n      rng_state_ptr, captured, seed_ptr, seed_val, offset_ptr, offset_val, offset_intragraph);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_attn/utils.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/utils.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n\nnamespace {\n\nconstexpr int64_t kMaxKernelAddresses = 256;\n\nstruct HostPointersArgs {\n  uint64_t ptrs[kMaxKernelAddresses];\n};\n\n__global__ void write_pointers_kernel(HostPointersArgs args, uint64_t *out, int64_t count,\n                                      int64_t offset) {\n  const int64_t idx = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (idx < count) {\n    out[offset + idx] = args.ptrs[idx];\n  }\n}\n\n}  // namespace\n\nvoid nvte_convert_pointers_to_tensor(const uint64_t *host_ptrs, NVTETensor output, int64_t count,\n                                     cudaStream_t stream) {\n  NVTE_API_CALL(nvte_convert_pointers_to_tensor);\n  using namespace transformer_engine;\n  Tensor *out_tensor = convertNVTETensorCheck(output);\n  uint64_t *out_ptr = static_cast<uint64_t *>(out_tensor->data.dptr);\n  NVTE_CHECK(out_ptr != nullptr, \"Output tensor data pointer is null.\");\n\n  int64_t offset = 0;\n  while (offset < count) {\n    const int64_t chunk = std::min(kMaxKernelAddresses, count - offset);\n    HostPointersArgs args{};\n    for (int64_t i = 0; i < chunk; ++i) {\n      args.ptrs[i] = host_ptrs[offset + i];\n    }\n    constexpr int threads = kMaxKernelAddresses;\n    write_pointers_kernel<<<1, threads, 0, stream>>>(args, out_ptr, chunk, offset);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n    offset += chunk;\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/utils.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n\nnamespace {\n\nconstexpr int64_t kMaxKernelAddresses = 256;\n\nstruct HostPointersArgs {\n  uint64_t ptrs[kMaxKernelAddresses];\n};\n\n__global__ void write_pointers_kernel(HostPointersArgs args, uint64_t *out, int64_t count,\n                                      int64_t offset) {\n  const int64_t idx = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (idx < count) {\n    out[offset + idx] = args.ptrs[idx];\n  }\n}\n\n}  // namespace\n\nvoid nvte_convert_pointers_to_tensor(const uint64_t *host_ptrs, NVTETensor output, int64_t count,\n                                     hipStream_t stream) {\n  NVTE_API_CALL(nvte_convert_pointers_to_tensor);\n  using namespace transformer_engine;\n  Tensor *out_tensor = convertNVTETensorCheck(output);\n  uint64_t *out_ptr = static_cast<uint64_t *>(out_tensor->data.dptr);\n  NVTE_CHECK(out_ptr != nullptr, \"Output tensor data pointer is null.\");\n\n  int64_t offset = 0;\n  while (offset < count) {\n    const int64_t chunk = std::min(kMaxKernelAddresses, count - offset);\n    HostPointersArgs args{};\n    for (int64_t i = 0; i < chunk; ++i) {\n      args.ptrs[i] = host_ptrs[offset + i];\n    }\n    constexpr int threads = kMaxKernelAddresses;\n    write_pointers_kernel<<<1, threads, 0, stream>>>(args, out_ptr, chunk, offset);\n    NVTE_CHECK_CUDA(hipGetLastError());\n    offset += chunk;\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/util/utils.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/padding.h>\n\n#include <cfloat>\n#include <iostream>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// Parameters to tune\nconstexpr int n_warps_per_tile = 4;\nconstexpr int threads_per_block = THREADS_PER_WARP * n_warps_per_tile;\nconstexpr int desired_load_store_size = 8;\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\n\nstruct MultiPaddingArgs {\n  // (input) Data buffers for input tensors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for cast output tensors\n  void* output_list[kMaxTensorsPerKernel];\n  // Input matrix heights\n  int num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix heights (padded)\n  int padded_num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix widths\n  int row_length_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\ntemplate <int nvec, typename Type>\n__global__ void __launch_bounds__(threads_per_block) multi_padding_kernel(MultiPaddingArgs args) {\n  using Vec = Vec<Type, nvec>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec;\n\n  // Number of nvec x nvec subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const Type* input = reinterpret_cast<const Type*>(args.input_list[tensor_id]);\n  Type* output = reinterpret_cast<Type*>(args.output_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int padded_num_rows = args.padded_num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  Type local_zero = static_cast<Type>(0.f);\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec; ++i2) {\n      const int row = tile_row + i1 * nvec + i2;\n      const int col = tile_col + j1 * nvec;\n      Vec local_input;\n      Vec local_output;\n      local_input.clear();\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[static_cast<size_t>(row) * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec; ++j2) {\n        local_output.data.elt[j2] = local_input.data.elt[j2];\n      }\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_output.data.elt[j2];\n          }\n        }\n      } else if (row < padded_num_rows) {\n        // padding\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_zero;\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <int nvec, typename Type>\n__global__ void __launch_bounds__(threads_per_block) multi_unpadding_kernel(MultiPaddingArgs args) {\n  using Vec = Vec<Type, nvec>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec;\n\n  // Number of nvec x nvec subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const Type* input = reinterpret_cast<const Type*>(args.input_list[tensor_id]);\n  Type* output = reinterpret_cast<Type*>(args.output_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  Type local_zero = static_cast<Type>(0.f);\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec; ++i2) {\n      const int row = tile_row + i1 * nvec + i2;\n      const int col = tile_col + j1 * nvec;\n      Vec local_input;\n      Vec local_output;\n      local_input.clear();\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[static_cast<size_t>(row) * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec; ++j2) {\n        local_output.data.elt[j2] = local_input.data.elt[j2];\n      }\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_output.data.elt[j2];\n          }\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n\nvoid multi_padding(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                   const std::vector<int> padded_num_rows_list, cudaStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType type = input_list[0]->data.dtype;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_padding_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_padding_output_\" + std::to_string(tensor_id));\n\n    NVTE_CHECK(input.data.dtype == type, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == type, \"Output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions.\");\n    NVTE_CHECK(output.data.shape[0] == padded_num_rows_list[tensor_id],\n               \"output tensor shape does not match padded input shape.\");\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_load_store_size * 8 / typeToNumBits(type);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_store_size * 8 / typeToNumBits(type);\n\n  // Add tensors to kernel argument struct\n  MultiPaddingArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n          const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n          multi_padding_kernel<nvec, Type>\n          <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      kernel_args.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = input_list[tensor_id]->data.shape[0];\n    const int padded_num_rows = padded_num_rows_list[tensor_id];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (padded_num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.padded_num_rows_list[pos] = padded_num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n        type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n        const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n        multi_padding_kernel<nvec, Type>\n        <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid multi_unpadding(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                     const std::vector<int> unpadded_num_rows_list, cudaStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType type = input_list[0]->data.dtype;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_unpadding_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_unpadding_output_\" + std::to_string(tensor_id));\n\n    NVTE_CHECK(input.data.dtype == type, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == type, \"Output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions.\");\n    NVTE_CHECK(output.data.shape[0] == unpadded_num_rows_list[tensor_id],\n               \"output tensor shape does not match padded input shape.\");\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_load_store_size / typeToSize(type);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_store_size / typeToSize(type);\n\n  // Add tensors to kernel argument struct\n  MultiPaddingArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n          const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n          multi_unpadding_kernel<nvec, Type>\n          <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      kernel_args.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = unpadded_num_rows_list[tensor_id];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n        type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n        const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n        multi_unpadding_kernel<nvec, Type>\n        <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_multi_padding(size_t num_tensors, const NVTETensor* input_list, NVTETensor* output_list,\n                        const int* padded_num_rows_list, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_padding);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  std::vector<int> padded_num_rows_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n    padded_num_rows_list_.push_back(padded_num_rows_list[i]);\n  }\n  multi_padding(input_list_, output_list_, padded_num_rows_list_, stream);\n}\n\nvoid nvte_multi_unpadding(size_t num_tensors, const NVTETensor* input_list, NVTETensor* output_list,\n                          const int* unpadded_num_rows_list, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_unpadding);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  std::vector<int> unpadded_num_rows_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n    unpadded_num_rows_list_.push_back(unpadded_num_rows_list[i]);\n  }\n  multi_unpadding(input_list_, output_list_, unpadded_num_rows_list_, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/padding.h>\n\n#include <cfloat>\n#include <iostream>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// Parameters to tune\nconstexpr int n_warps_per_tile = 4;\nconstexpr int threads_per_block = THREADS_PER_WARP * n_warps_per_tile;\nconstexpr int desired_load_store_size = 8;\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\n\nstruct MultiPaddingArgs {\n  // (input) Data buffers for input tensors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for cast output tensors\n  void* output_list[kMaxTensorsPerKernel];\n  // Input matrix heights\n  int num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix heights (padded)\n  int padded_num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix widths\n  int row_length_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\ntemplate <int nvec, typename Type>\n__global__ void __launch_bounds__(threads_per_block) multi_padding_kernel(MultiPaddingArgs args) {\n  using Vec = Vec<Type, nvec>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec;\n\n  // Number of nvec x nvec subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const Type* input = reinterpret_cast<const Type*>(args.input_list[tensor_id]);\n  Type* output = reinterpret_cast<Type*>(args.output_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int padded_num_rows = args.padded_num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  Type local_zero = static_cast<Type>(0.f);\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec; ++i2) {\n      const int row = tile_row + i1 * nvec + i2;\n      const int col = tile_col + j1 * nvec;\n      Vec local_input;\n      Vec local_output;\n      local_input.clear();\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[static_cast<size_t>(row) * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec; ++j2) {\n        local_output.data.elt[j2] = local_input.data.elt[j2];\n      }\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_output.data.elt[j2];\n          }\n        }\n      } else if (row < padded_num_rows) {\n        // padding\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_zero;\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <int nvec, typename Type>\n__global__ void __launch_bounds__(threads_per_block) multi_unpadding_kernel(MultiPaddingArgs args) {\n  using Vec = Vec<Type, nvec>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec;\n\n  // Number of nvec x nvec subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const Type* input = reinterpret_cast<const Type*>(args.input_list[tensor_id]);\n  Type* output = reinterpret_cast<Type*>(args.output_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  Type local_zero = static_cast<Type>(0.f);\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec; ++i2) {\n      const int row = tile_row + i1 * nvec + i2;\n      const int col = tile_col + j1 * nvec;\n      Vec local_input;\n      Vec local_output;\n      local_input.clear();\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[static_cast<size_t>(row) * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec; ++j2) {\n        local_output.data.elt[j2] = local_input.data.elt[j2];\n      }\n      if (row < num_rows) {\n        for (int j2 = 0; j2 < nvec; ++j2) {\n          if (col + j2 < row_length) {\n            output[static_cast<size_t>(row) * row_length + col + j2] = local_output.data.elt[j2];\n          }\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n\nvoid multi_padding(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                   const std::vector<int> padded_num_rows_list, hipStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType type = input_list[0]->data.dtype;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_padding_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_padding_output_\" + std::to_string(tensor_id));\n\n    NVTE_CHECK(input.data.dtype == type, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == type, \"Output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions.\");\n    NVTE_CHECK(output.data.shape[0] == padded_num_rows_list[tensor_id],\n               \"output tensor shape does not match padded input shape.\");\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_load_store_size * 8 / typeToNumBits(type);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_store_size * 8 / typeToNumBits(type);\n\n  // Add tensors to kernel argument struct\n  MultiPaddingArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n          const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n          multi_padding_kernel<nvec, Type>\n          <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n      NVTE_CHECK_CUDA(hipGetLastError());\n      kernel_args.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = input_list[tensor_id]->data.shape[0];\n    const int padded_num_rows = padded_num_rows_list[tensor_id];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (padded_num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.padded_num_rows_list[pos] = padded_num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n        type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n        const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n        multi_padding_kernel<nvec, Type>\n        <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid multi_unpadding(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                     const std::vector<int> unpadded_num_rows_list, hipStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType type = input_list[0]->data.dtype;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_unpadding_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_unpadding_output_\" + std::to_string(tensor_id));\n\n    NVTE_CHECK(input.data.dtype == type, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == type, \"Output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions.\");\n    NVTE_CHECK(output.data.shape[0] == unpadded_num_rows_list[tensor_id],\n               \"output tensor shape does not match padded input shape.\");\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec x nvec subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_load_store_size / typeToSize(type);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_store_size / typeToSize(type);\n\n  // Add tensors to kernel argument struct\n  MultiPaddingArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n          type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n          const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n          multi_unpadding_kernel<nvec, Type>\n          <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n      NVTE_CHECK_CUDA(hipGetLastError());\n      kernel_args.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = unpadded_num_rows_list[tensor_id];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(\n        type, Type, constexpr int nvec = desired_load_store_size / sizeof(Type);\n        const int n_blocks = kernel_args.block_range[kernel_args.num_tensors];\n        multi_unpadding_kernel<nvec, Type>\n        <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args););  // NOLINT(*)\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_multi_padding(size_t num_tensors, const NVTETensor* input_list, NVTETensor* output_list,\n                        const int* padded_num_rows_list, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_padding);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  std::vector<int> padded_num_rows_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n    padded_num_rows_list_.push_back(padded_num_rows_list[i]);\n  }\n  multi_padding(input_list_, output_list_, padded_num_rows_list_, stream);\n}\n\nvoid nvte_multi_unpadding(size_t num_tensors, const NVTETensor* input_list, NVTETensor* output_list,\n                          const int* unpadded_num_rows_list, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_unpadding);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  std::vector<int> unpadded_num_rows_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n    unpadded_num_rows_list_.push_back(unpadded_num_rows_list[i]);\n  }\n  multi_unpadding(input_list_, output_list_, unpadded_num_rows_list_, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/util/padding.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda_fp16.h>\n#include <cuda_profiler_api.h>\n#include <cuda_runtime.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <cfloat>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<half, 1>(half *dst, const half *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<half, 4>(half *dst, const half *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((half2 *)dst) = *((half2 *)src);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_BATCH, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_BATCH; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_softmax_warp_forward(output_t *dst, const input_t *src, const acc_t scale,\n                                            int micro_batch_size, int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch =\n      (blockDim.y * (blockIdx.x + gridDim.x * (blockIdx.y + gridDim.y * blockIdx.z)) +\n       threadIdx.y) *\n      WARP_BATCH;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        int itr_idx = i * element_count + it * WARP_SIZE;\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = (acc_t)temp_data[element] * scale;\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n      sum[i] += elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = elements[i][it + element] / sum[i];\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count + it * WARP_SIZE, out);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n * 2) Explicit masking\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                   const uint8_t *mask, const acc_t scale,\n                                                   int micro_batch_size, int element_count,\n                                                   int pad_batches) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch =\n      (blockDim.y * (blockIdx.x + gridDim.x * (blockIdx.y + gridDim.y * blockIdx.z)) +\n       threadIdx.y) *\n      WARP_BATCH;\n  size_t pad_first_batch = 0;\n  if (pad_batches != 1) {  // bert style\n    pad_first_batch =\n        (blockDim.y * (blockIdx.x + gridDim.x * blockIdx.z) + threadIdx.y) * WARP_BATCH;\n  } else {  // gpt2 style\n    pad_first_batch = (blockDim.y * blockIdx.x + threadIdx.y) * WARP_BATCH;\n  }\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset_src_dst = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  size_t thread_offset_mask = pad_first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset_src_dst;\n  dst += thread_offset_src_dst;\n  mask += thread_offset_mask;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n  uint8_t temp_mask[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        int itr_idx = i * element_count + it * WARP_SIZE;\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n        copy_vector<uint8_t, ELEMENTS_PER_LDG_STG>(temp_mask, mask + itr_idx);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (temp_mask[element] != 1) {\n            elements[i][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[i][it + element] = -10000.0;\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  // compute scale value to account for full mask\n  acc_t scale_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    scale_value[i] = (max_value[i] == -10000.0) ? 0.0 : 1.0;\n  }\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n      sum[i] += elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = elements[i][it + element] * scale_value[i] / sum[i];\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count + it * WARP_SIZE, out);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_masked_softmax_warp_backward(output_t *gradInput, const input_t *grad,\n                                                    const input_t *output, acc_t scale,\n                                                    int micro_batch_size, int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_backward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch = (blockDim.y * blockIdx.x + threadIdx.y) * WARP_BATCH;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  // the first element to process by the current thread\n  size_t thread_offset = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  grad += thread_offset;\n  output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory\n  acc_t grad_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  acc_t output_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_grad,\n                                                   grad + i * element_count + it * WARP_SIZE);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_output,\n                                                   output + i * element_count + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          output_reg[i][it + element] = (acc_t)temp_output[element];\n        }\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          grad_reg[i][it + element] = (acc_t)temp_grad[element] * output_reg[i][it + element];\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    sum[i] = grad_reg[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[i] += grad_reg[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n        // compute gradients\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[i][it + element] -\n                                             output_reg[i][it + element] * sum[i]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(gradInput + i * element_count + it * WARP_SIZE,\n                                                    out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_softmax_forward(output_t *dst, const input_t *src, const input_t scale,\n                                     int query_seq_len, int key_seq_len, int batches,\n                                     int attn_heads, cudaStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(query_seq_len % batches_per_block == 0, \"Unsupported shape.\");\n    dim3 blocks(query_seq_len / batches_per_block, attn_heads, batches);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 1:  // 2\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 2:  // 4\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 3:  // 8\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 4:  // 16\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 5:  // 32\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 6:  // 64\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 7:  // 128\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 8:  // 256\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 9:  // 512\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 10:  // 1024\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 11:  // 2048\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 12:  // 4096\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 13:  // 8192\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 14:  // 16384\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_masked_softmax_forward(output_t *dst, const input_t *src, const uint8_t *mask,\n                                            const input_t scale, int query_seq_len, int key_seq_len,\n                                            int batches, int attn_heads, int pad_batches,\n                                            cudaStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(query_seq_len % batches_per_block == 0, \"Unsupported shape.\");\n    dim3 blocks(query_seq_len / batches_per_block, attn_heads, batches);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 1:  // 2\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 2:  // 4\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 3:  // 8\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 4:  // 16\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 5:  // 32\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 6:  // 64\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 7:  // 128\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 8:  // 256\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 9:  // 512\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 10:  // 1024\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 11:  // 2048\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 12:  // 4096\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 13:  // 8192\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 14:  // 16384\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_masked_softmax_backward(output_t *grad_input, const input_t *grad,\n                                             const input_t *output, const acc_t scale,\n                                             int query_seq_len, int key_seq_len, int batches,\n                                             int attn_heads, cudaStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_backward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_backward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    int blocks = batch_count / batches_per_block;\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 1:  // 2\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 2:  // 4\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 3:  // 8\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 4:  // 16\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 5:  // 32\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 6:  // 64\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 7:  // 128\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 8:  // 256\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 9:  // 512\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 10:  // 1024\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 11:  // 2048\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 12:  // 4096\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 13:  // 8192\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 14:  // 16384\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid scaled_softmax_forward(const Tensor &input, Tensor *softmax_results, float scale_factor,\n                            cudaStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                             const Tensor softmax_results, float scale_factor,\n                             cudaStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_masked_softmax_forward(const Tensor input, const Tensor mask, Tensor *softmax_results,\n                                   float scale_factor, cudaStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int pad_batches = mask.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr),\n          reinterpret_cast<const uint8_t *>(mask.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, pad_batches, stream););\n}\n\nvoid scaled_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                    const Tensor softmax_results, float scale_factor,\n                                    cudaStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_softmax_forward(const NVTETensor input, NVTETensor softmax_results,\n                                 float scale_factor, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_softmax_forward);\n  using namespace transformer_engine;\n  scaled_softmax_forward(*convertNVTETensorCheck(input), convertNVTETensorCheck(softmax_results),\n                         scale_factor, stream);\n}\n\nvoid nvte_scaled_softmax_backward(const NVTETensor incoming_grads, const NVTETensor softmax_results,\n                                  NVTETensor output_grads, float scale_factor,\n                                  cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_softmax_backward);\n  using namespace transformer_engine;\n  scaled_softmax_backward(*convertNVTETensorCheck(output_grads),\n                          *convertNVTETensorCheck(incoming_grads),\n                          *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n\nvoid nvte_scaled_masked_softmax_forward(const NVTETensor input, const NVTETensor mask,\n                                        NVTETensor softmax_results, float scale_factor,\n                                        cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_masked_softmax_forward);\n  using namespace transformer_engine;\n  scaled_masked_softmax_forward(*convertNVTETensorCheck(input), *convertNVTETensorCheck(mask),\n                                convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n\nvoid nvte_scaled_masked_softmax_backward(const NVTETensor incoming_grads,\n                                         const NVTETensor softmax_results, NVTETensor output_grads,\n                                         float scale_factor, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_masked_softmax_backward);\n  using namespace transformer_engine;\n  scaled_masked_softmax_backward(*convertNVTETensorCheck(output_grads),\n                                 *convertNVTETensorCheck(incoming_grads),\n                                 *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <hip/hip_runtime_api.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <cfloat>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<half, 1>(half *dst, const half *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<half, 4>(half *dst, const half *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((half2 *)dst) = *((half2 *)src);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_BATCH, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_BATCH; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_softmax_warp_forward(output_t *dst, const input_t *src, const acc_t scale,\n                                            int micro_batch_size, int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch =\n      (blockDim.y * (blockIdx.x + gridDim.x * (blockIdx.y + gridDim.y * blockIdx.z)) +\n       threadIdx.y) *\n      WARP_BATCH;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        int itr_idx = i * element_count + it * WARP_SIZE;\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = (acc_t)temp_data[element] * scale;\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n      sum[i] += elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = elements[i][it + element] / sum[i];\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count + it * WARP_SIZE, out);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n * 2) Explicit masking\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                   const uint8_t *mask, const acc_t scale,\n                                                   int micro_batch_size, int element_count,\n                                                   int pad_batches) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch =\n      (blockDim.y * (blockIdx.x + gridDim.x * (blockIdx.y + gridDim.y * blockIdx.z)) +\n       threadIdx.y) *\n      WARP_BATCH;\n  size_t pad_first_batch = 0;\n  if (pad_batches != 1) {  // bert style\n    pad_first_batch =\n        (blockDim.y * (blockIdx.x + gridDim.x * blockIdx.z) + threadIdx.y) * WARP_BATCH;\n  } else {  // gpt2 style\n    pad_first_batch = (blockDim.y * blockIdx.x + threadIdx.y) * WARP_BATCH;\n  }\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset_src_dst = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  size_t thread_offset_mask = pad_first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset_src_dst;\n  dst += thread_offset_src_dst;\n  mask += thread_offset_mask;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n  uint8_t temp_mask[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        int itr_idx = i * element_count + it * WARP_SIZE;\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n        copy_vector<uint8_t, ELEMENTS_PER_LDG_STG>(temp_mask, mask + itr_idx);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (temp_mask[element] != 1) {\n            elements[i][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[i][it + element] = -10000.0;\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  // compute scale value to account for full mask\n  acc_t scale_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    scale_value[i] = (max_value[i] == -10000.0) ? 0.0 : 1.0;\n  }\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n      sum[i] += elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = elements[i][it + element] * scale_value[i] / sum[i];\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count + it * WARP_SIZE, out);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_masked_softmax_warp_backward(output_t *gradInput, const input_t *grad,\n                                                    const input_t *output, acc_t scale,\n                                                    int micro_batch_size, int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_backward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  // blockDim/threadIdx = (WARP_SIZE, WARPS_PER_BLOCK, )\n  // gridDim/blockIdx = (seq_len, attn_heads, batches)\n  size_t first_batch = (blockDim.y * blockIdx.x + threadIdx.y) * WARP_BATCH;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  // the first element to process by the current thread\n  size_t thread_offset = first_batch * element_count + ELEMENTS_PER_LDG_STG * local_idx;\n  grad += thread_offset;\n  output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory\n  acc_t grad_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  acc_t output_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : element_count;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_grad,\n                                                   grad + i * element_count + it * WARP_SIZE);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_output,\n                                                   output + i * element_count + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          output_reg[i][it + element] = (acc_t)temp_output[element];\n        }\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          grad_reg[i][it + element] = (acc_t)temp_grad[element] * output_reg[i][it + element];\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    sum[i] = grad_reg[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[i] += grad_reg[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n        // compute gradients\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[i][it + element] -\n                                             output_reg[i][it + element] * sum[i]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(gradInput + i * element_count + it * WARP_SIZE,\n                                                    out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_softmax_forward(output_t *dst, const input_t *src, const input_t scale,\n                                     int query_seq_len, int key_seq_len, int batches,\n                                     int attn_heads, hipStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(query_seq_len % batches_per_block == 0, \"Unsupported shape.\");\n    dim3 blocks(query_seq_len / batches_per_block, attn_heads, batches);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 1:  // 2\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 2:  // 4\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 3:  // 8\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 4:  // 16\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 5:  // 32\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 6:  // 64\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 7:  // 128\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 8:  // 256\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 9:  // 512\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 10:  // 1024\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 11:  // 2048\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 12:  // 4096\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 13:  // 8192\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      case 14:  // 16384\n        scaled_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, key_seq_len);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_masked_softmax_forward(output_t *dst, const input_t *src, const uint8_t *mask,\n                                            const input_t scale, int query_seq_len, int key_seq_len,\n                                            int batches, int attn_heads, int pad_batches,\n                                            hipStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(query_seq_len % batches_per_block == 0, \"Unsupported shape.\");\n    dim3 blocks(query_seq_len / batches_per_block, attn_heads, batches);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 1:  // 2\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 2:  // 4\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 3:  // 8\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 4:  // 16\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 5:  // 32\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 6:  // 64\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 7:  // 128\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 8:  // 256\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 9:  // 512\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 10:  // 1024\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 11:  // 2048\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 12:  // 4096\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 13:  // 8192\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      case 14:  // 16384\n        scaled_masked_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, mask, scale, batch_count, key_seq_len,\n                                             pad_batches);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_masked_softmax_backward(output_t *grad_input, const input_t *grad,\n                                             const input_t *output, const acc_t scale,\n                                             int query_seq_len, int key_seq_len, int batches,\n                                             int attn_heads, hipStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n  if (key_seq_len == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(key_seq_len);\n    const int next_power_of_two = 1 << log2_elements;\n    int batch_count = batches * attn_heads * query_seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_backward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_backward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    int blocks = batch_count / batches_per_block;\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 1:  // 2\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 2:  // 4\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 3:  // 8\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 4:  // 16\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 5:  // 32\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 6:  // 64\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 7:  // 128\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 8:  // 256\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 9:  // 512\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 10:  // 1024\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 11:  // 2048\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 12:  // 4096\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 13:  // 8192\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      case 14:  // 16384\n        scaled_masked_softmax_warp_backward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             key_seq_len);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid scaled_softmax_forward(const Tensor &input, Tensor *softmax_results, float scale_factor,\n                            hipStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                             const Tensor softmax_results, float scale_factor,\n                             hipStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_masked_softmax_forward(const Tensor input, const Tensor mask, Tensor *softmax_results,\n                                   float scale_factor, hipStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int pad_batches = mask.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr),\n          reinterpret_cast<const uint8_t *>(mask.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, pad_batches, stream););\n}\n\nvoid scaled_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                    const Tensor softmax_results, float scale_factor,\n                                    hipStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_softmax_forward(const NVTETensor input, NVTETensor softmax_results,\n                                 float scale_factor, hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_softmax_forward);\n  using namespace transformer_engine;\n  scaled_softmax_forward(*convertNVTETensorCheck(input), convertNVTETensorCheck(softmax_results),\n                         scale_factor, stream);\n}\n\nvoid nvte_scaled_softmax_backward(const NVTETensor incoming_grads, const NVTETensor softmax_results,\n                                  NVTETensor output_grads, float scale_factor,\n                                  hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_softmax_backward);\n  using namespace transformer_engine;\n  scaled_softmax_backward(*convertNVTETensorCheck(output_grads),\n                          *convertNVTETensorCheck(incoming_grads),\n                          *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n\nvoid nvte_scaled_masked_softmax_forward(const NVTETensor input, const NVTETensor mask,\n                                        NVTETensor softmax_results, float scale_factor,\n                                        hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_masked_softmax_forward);\n  using namespace transformer_engine;\n  scaled_masked_softmax_forward(*convertNVTETensorCheck(input), *convertNVTETensorCheck(mask),\n                                convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n\nvoid nvte_scaled_masked_softmax_backward(const NVTETensor incoming_grads,\n                                         const NVTETensor softmax_results, NVTETensor output_grads,\n                                         float scale_factor, hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_masked_softmax_backward);\n  using namespace transformer_engine;\n  scaled_masked_softmax_backward(*convertNVTETensorCheck(output_grads),\n                                 *convertNVTETensorCheck(incoming_grads),\n                                 *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_softmax/scaled_masked_softmax.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda_fp16.h>\n#include <cuda_profiler_api.h>\n#include <cuda_runtime.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <array>\n#include <cfloat>\n#include <functional>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((uint64_t *)dst) = *((uint64_t *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 1>(fp16 *dst, const fp16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 4>(fp16 *dst, const fp16 *src) {\n  *((uint64_t *)dst) = *((uint64_t *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((uint32_t *)dst) = *((uint32_t *)src);  // NOLINT(*)\n}\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_zero_vector(Datatype *dst);\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 1>(bf16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 4>(bf16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 1>(fp16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 4>(fp16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_ROWS, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_ROWS; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with the following additional features\n * 1) input scaling\n * 2) implicit causal masking\n *\n * works for all cases:\n *  k > q\n *  k < q\n *  k = q\n *\n * where:\n * microbatches = batches * attn_heads * query_seq_len\n * rows = query_seq_len\n * cols = key_seq_len\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_aligned_causal_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                                  const acc_t scale,\n                                                                  const int microbatches,\n                                                                  const int rows, const int cols) {\n  // 1) WARP_WIDTH must match the value of warp_size\n  // 2) WARP_ROWS must match the value of rows_per_warp\n  // of the dispatch_scaled_aligned_causal_masked_softmax_forward method.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_WIDTH =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_WIDTH;\n  constexpr int WARP_ROWS = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  const int global_row_idx = (blockIdx.x * blockDim.y + threadIdx.y) * WARP_ROWS;\n  const int col = threadIdx.x * ELEMENTS_PER_LDG_STG;\n\n  const size_t thread_offset = global_row_idx * cols + col;\n\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory into registers WITH scaling\n  acc_t elements[WARP_ROWS][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;  // local row index of attention matrix\n    const int masked_elements = i + cols - rows + 1;\n\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            elements[w][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[w][it + element] = (acc_t)(-10'000);\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[w][it + element] = (acc_t)(-10'000);\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_ROWS];\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    max_value[w] = elements[w][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[w] = (max_value[w] > elements[w][it]) ? max_value[w] : elements[w][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Max>(max_value);\n\n  acc_t sum[WARP_ROWS]{0.0f};\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[w][it] = expf((elements[w][it] - max_value[w]));\n      sum[w] += elements[w][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Add>(sum);\n\n  output_t out[ELEMENTS_PER_LDG_STG]{0.0f};\n// store result\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;\n    const int masked_elements = i + cols - rows + 1;\n\n    // out of Attention matrix bounds (rows)\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            out[element] = elements[w][it + element] / sum[w];\n          } else {\n            out[element] = (output_t)(0.0f);\n          }\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + itr_idx, out);\n      } else if (j < cols) {\n        copy_zero_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + itr_idx);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_aligned_causal_masked_softmax_warp_backward(\n    output_t *gradInput, const input_t *grad, const input_t *softmax_output, const acc_t scale,\n    const int microbatches, const int rows, const int cols) {\n  // 1) WARP_WIDTH must match the value of warp_size\n  // 2) WARP_ROWS must match the value of rows_per_warp\n  // of the dispatch_scaled_aligned_causal_masked_softmax_forward method.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_WIDTH =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_WIDTH;\n  constexpr int WARP_ROWS = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  const int global_row_idx = (blockIdx.x * blockDim.y + threadIdx.y) * WARP_ROWS;\n  const int col = threadIdx.x * ELEMENTS_PER_LDG_STG;\n\n  const size_t thread_offset = global_row_idx * cols + col;\n\n  grad += thread_offset;\n  softmax_output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory into registers\n  acc_t grad_reg[WARP_ROWS][WARP_ITERATIONS]{0.0f};\n  acc_t softmax_output_reg[WARP_ROWS][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;  // local row index of attention matrix\n    const int masked_elements = i + cols - rows + 1;\n\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_grad, grad + itr_idx);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_output, softmax_output + itr_idx);\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            softmax_output_reg[w][it + element] = (acc_t)temp_output[element];\n            grad_reg[w][it + element] =\n                (acc_t)temp_grad[element] * softmax_output_reg[w][it + element];\n          }\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_ROWS];\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    sum[w] = grad_reg[w][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[w] += grad_reg[w][it];\n    }\n  }\n\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Add>(sum);\n\n// store result\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < cols) {\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[w][it + element] -\n                                             softmax_output_reg[w][it + element] * sum[w]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(gradInput + itr_idx, out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nvoid call_kernel_scaled_aligned_causal_masked_softmax_forward(\n    dim3 grid_size, dim3 block_size, const int shmem_size, cudaStream_t stream, output_t *dst,\n    const input_t *src, const acc_t scale, const int microbatches, const int query_seq_len,\n    const int key_seq_len) {\n  scaled_aligned_causal_masked_softmax_warp_forward<input_t, output_t, acc_t, log2_elements>\n      <<<grid_size, block_size, shmem_size, stream>>>(dst, src, scale, microbatches, query_seq_len,\n                                                      key_seq_len);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nvoid call_kernel_scaled_aligned_causal_masked_softmax_backward(\n    dim3 grid_size, dim3 block_size, const int shmem_size, cudaStream_t stream, output_t *gradInput,\n    const input_t *grad, const input_t *output, const acc_t scale, const int microbatches,\n    const int query_seq_len, const int key_seq_len) {\n  scaled_aligned_causal_masked_softmax_warp_backward<input_t, output_t, acc_t, log2_elements>\n      <<<grid_size, block_size, 0, stream>>>(gradInput, grad, output, scale, microbatches,\n                                             query_seq_len, key_seq_len);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct FunctionWrapper {\n  using ForwardType =\n      std::function<void(dim3 grid_size, dim3 block_size, const int shmem_size, cudaStream_t stream,\n                         output_t *dst, const input_t *src, const acc_t scale,\n                         const int microbatches, const int query_seq_len, const int key_seq_len)>;\n  using BackwardType = std::function<void(\n      dim3 grid_size, dim3 block_size, const int shmem_size, cudaStream_t stream,\n      output_t *gradInput, const input_t *grad, const input_t *output, const acc_t scale,\n      const int microbatches, const int query_seq_len, const int key_seq_len)>;\n};\n\nconstexpr int MIN_SUPPORTED_POWER = 4;\nconstexpr int MAX_SUPPORTED_POWER = 14;\nconstexpr int MIN_POWER = MIN_SUPPORTED_POWER - 1;\nconstexpr int MAX_POWER = MAX_SUPPORTED_POWER + 1;\n\n// Recursively instantiate the function for the limit of \"log2_elements\",\n// i.e. \"MAX_POWER\" defined above.\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nstruct CompileTimeLoopForward {\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static void populate(std::array<ForwardFuncType, MAX_POWER> *arr) {\n    CompileTimeLoopForward<input_t, output_t, acc_t, log2_elements - 1>::populate(arr);\n    (*arr)[log2_elements] =\n        &call_kernel_scaled_aligned_causal_masked_softmax_forward<output_t, input_t, acc_t,\n                                                                  log2_elements>;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct CompileTimeLoopForward<input_t, output_t, acc_t, MIN_POWER> {\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static void populate(std::array<ForwardFuncType, MAX_POWER> *arr) { (*arr)[MIN_POWER] = nullptr; }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nstruct CompileTimeLoopBackward {\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static void populate(std::array<BackwardFuncType, MAX_POWER> *arr) {\n    CompileTimeLoopBackward<input_t, output_t, acc_t, log2_elements - 1>::populate(arr);\n    (*arr)[log2_elements] =\n        &call_kernel_scaled_aligned_causal_masked_softmax_backward<output_t, input_t, acc_t,\n                                                                   log2_elements>;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct CompileTimeLoopBackward<input_t, output_t, acc_t, MIN_POWER> {\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static void populate(std::array<BackwardFuncType, MAX_POWER> *arr) {\n    (*arr)[MIN_POWER] = nullptr;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_aligned_causal_masked_softmax_forward(output_t *dst, const input_t *src,\n                                                           const input_t scale, int query_seq_len,\n                                                           int key_seq_len, int batches,\n                                                           int attn_heads, cudaStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n\n  if (key_seq_len == 0) {\n    return;\n  }\n  int log2_elements = log2_ceil(key_seq_len);\n  const int next_power_of_two = 1 << log2_elements;\n\n  // This value must match the WARP_WIDTH constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int warp_width = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n  // This value must match the WARP_ROWS constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int microbatches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n  // use 128 threads per block to maximimize gpu utilization\n  constexpr int threads_per_block = 128;\n\n  int warps_per_block = threads_per_block / warp_width;\n  int microbatches_per_block = warps_per_block * microbatches_per_warp;\n  int microbatches = batches * attn_heads * query_seq_len;\n  int blocks = DIVUP(microbatches, microbatches_per_block);\n\n  dim3 block_size(warp_width, warps_per_block);\n  dim3 grid_size(blocks);\n\n  // create an array of pointers to functions\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static std::array<ForwardFuncType, MAX_POWER> forwardFunctionArray;\n  static bool is_initialized = false;\n  if (!is_initialized) {\n    CompileTimeLoopForward<input_t, output_t, acc_t, MAX_SUPPORTED_POWER>::populate(\n        &forwardFunctionArray);\n    is_initialized = true;\n  }\n  // Call the corresponding kernel\n  forwardFunctionArray[log2_elements](grid_size, block_size, 0, stream, dst, src, scale,\n                                      microbatches, query_seq_len, key_seq_len);\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_aligned_causal_masked_softmax_backward(\n    output_t *grad_input, const input_t *grad, const input_t *output, const acc_t scale,\n    int query_seq_len, int key_seq_len, int batches, int attn_heads, cudaStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n\n  if (key_seq_len == 0) {\n    return;\n  }\n  int log2_elements = log2_ceil(key_seq_len);\n  const int next_power_of_two = 1 << log2_elements;\n\n  // This value must match the WARP_WIDTH constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int warp_width = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n  // This value must match the WARP_ROWS constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int microbatches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n  // use 128 threads per block to maximimize gpu utilization\n  constexpr int threads_per_block = 128;\n\n  int warps_per_block = threads_per_block / warp_width;\n  int microbatches_per_block = warps_per_block * microbatches_per_warp;\n  int microbatches = batches * attn_heads * query_seq_len;\n  int blocks = DIVUP(microbatches, microbatches_per_block);\n\n  dim3 block_size(warp_width, warps_per_block);\n  dim3 grid_size(blocks);\n\n  // create an array of pointers to functions\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static std::array<BackwardFuncType, MAX_POWER> backwardFunctionArray;\n  static bool is_initialized = false;\n  if (!is_initialized) {\n    CompileTimeLoopBackward<input_t, output_t, acc_t, MAX_SUPPORTED_POWER>::populate(\n        &backwardFunctionArray);\n    is_initialized = true;\n  }\n  // Call the corresponding kernel\n  backwardFunctionArray[log2_elements](grid_size, block_size, 0, stream, grad_input, grad, output,\n                                       scale, microbatches, query_seq_len, key_seq_len);\n}\n\nvoid scaled_aligned_causal_masked_softmax_forward(const Tensor &input, Tensor *softmax_results,\n                                                  float scale_factor, cudaStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_aligned_causal_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_aligned_causal_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                                   const Tensor softmax_results, float scale_factor,\n                                                   cudaStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_aligned_causal_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_aligned_causal_masked_softmax_forward(const NVTETensor input,\n                                                       NVTETensor softmax_results,\n                                                       float scale_factor, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_aligned_causal_masked_softmax_forward);\n  using namespace transformer_engine;\n  scaled_aligned_causal_masked_softmax_forward(*convertNVTETensorCheck(input),\n                                               convertNVTETensorCheck(softmax_results),\n                                               scale_factor, stream);\n}\n\nvoid nvte_scaled_aligned_causal_masked_softmax_backward(const NVTETensor incoming_grads,\n                                                        const NVTETensor softmax_results,\n                                                        NVTETensor output_grads, float scale_factor,\n                                                        cudaStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_aligned_causal_masked_softmax_backward);\n  using namespace transformer_engine;\n  scaled_aligned_causal_masked_softmax_backward(\n      *convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(incoming_grads),\n      *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <hip/hip_runtime_api.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <array>\n#include <cfloat>\n#include <functional>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((uint64_t *)dst) = *((uint64_t *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 1>(fp16 *dst, const fp16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 4>(fp16 *dst, const fp16 *src) {\n  *((uint64_t *)dst) = *((uint64_t *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((uint32_t *)dst) = *((uint32_t *)src);  // NOLINT(*)\n}\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_zero_vector(Datatype *dst);\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 1>(bf16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 4>(bf16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 1>(fp16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 4>(fp16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_ROWS, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_ROWS; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with the following additional features\n * 1) input scaling\n * 2) implicit causal masking\n *\n * works for all cases:\n *  k > q\n *  k < q\n *  k = q\n *\n * where:\n * microbatches = batches * attn_heads * query_seq_len\n * rows = query_seq_len\n * cols = key_seq_len\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_aligned_causal_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                                  const acc_t scale,\n                                                                  const int microbatches,\n                                                                  const int rows, const int cols) {\n  // 1) WARP_WIDTH must match the value of warp_size\n  // 2) WARP_ROWS must match the value of rows_per_warp\n  // of the dispatch_scaled_aligned_causal_masked_softmax_forward method.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_WIDTH =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_WIDTH;\n  constexpr int WARP_ROWS = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  const int global_row_idx = (blockIdx.x * blockDim.y + threadIdx.y) * WARP_ROWS;\n  const int col = threadIdx.x * ELEMENTS_PER_LDG_STG;\n\n  const size_t thread_offset = global_row_idx * cols + col;\n\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory into registers WITH scaling\n  acc_t elements[WARP_ROWS][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;  // local row index of attention matrix\n    const int masked_elements = i + cols - rows + 1;\n\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_data, src + itr_idx);\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            elements[w][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[w][it + element] = (acc_t)(-10'000);\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[w][it + element] = (acc_t)(-10'000);\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_ROWS];\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    max_value[w] = elements[w][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[w] = (max_value[w] > elements[w][it]) ? max_value[w] : elements[w][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Max>(max_value);\n\n  acc_t sum[WARP_ROWS]{0.0f};\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      elements[w][it] = expf((elements[w][it] - max_value[w]));\n      sum[w] += elements[w][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Add>(sum);\n\n  output_t out[ELEMENTS_PER_LDG_STG]{0.0f};\n// store result\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;\n    const int masked_elements = i + cols - rows + 1;\n\n    // out of Attention matrix bounds (rows)\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            out[element] = elements[w][it + element] / sum[w];\n          } else {\n            out[element] = (output_t)(0.0f);\n          }\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + itr_idx, out);\n      } else if (j < cols) {\n        copy_zero_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + itr_idx);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_aligned_causal_masked_softmax_warp_backward(\n    output_t *gradInput, const input_t *grad, const input_t *softmax_output, const acc_t scale,\n    const int microbatches, const int rows, const int cols) {\n  // 1) WARP_WIDTH must match the value of warp_size\n  // 2) WARP_ROWS must match the value of rows_per_warp\n  // of the dispatch_scaled_aligned_causal_masked_softmax_forward method.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_WIDTH =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_WIDTH;\n  constexpr int WARP_ROWS = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  const int global_row_idx = (blockIdx.x * blockDim.y + threadIdx.y) * WARP_ROWS;\n  const int col = threadIdx.x * ELEMENTS_PER_LDG_STG;\n\n  const size_t thread_offset = global_row_idx * cols + col;\n\n  grad += thread_offset;\n  softmax_output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory into registers\n  acc_t grad_reg[WARP_ROWS][WARP_ITERATIONS]{0.0f};\n  acc_t softmax_output_reg[WARP_ROWS][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    const int i = microbatch % rows;  // local row index of attention matrix\n    const int masked_elements = i + cols - rows + 1;\n\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < masked_elements) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_grad, grad + itr_idx);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(temp_output, softmax_output + itr_idx);\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (j + element < masked_elements) {\n            softmax_output_reg[w][it + element] = (acc_t)temp_output[element];\n            grad_reg[w][it + element] =\n                (acc_t)temp_grad[element] * softmax_output_reg[w][it + element];\n          }\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_ROWS];\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    sum[w] = grad_reg[w][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[w] += grad_reg[w][it];\n    }\n  }\n\n  warp_reduce<acc_t, WARP_ROWS, WARP_WIDTH, Add>(sum);\n\n// store result\n#pragma unroll\n  for (int w = 0; w < WARP_ROWS; ++w) {\n    const int microbatch = global_row_idx + w;\n    if (microbatch >= microbatches) {\n      break;\n    }\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      const int j = col + it * WARP_WIDTH;  // index of the first column\n      const int itr_idx = w * cols + it * WARP_WIDTH;\n\n      if (j < cols) {\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[w][it + element] -\n                                             softmax_output_reg[w][it + element] * sum[w]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(gradInput + itr_idx, out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nvoid call_kernel_scaled_aligned_causal_masked_softmax_forward(\n    dim3 grid_size, dim3 block_size, const int shmem_size, hipStream_t stream, output_t *dst,\n    const input_t *src, const acc_t scale, const int microbatches, const int query_seq_len,\n    const int key_seq_len) {\n  scaled_aligned_causal_masked_softmax_warp_forward<input_t, output_t, acc_t, log2_elements>\n      <<<grid_size, block_size, shmem_size, stream>>>(dst, src, scale, microbatches, query_seq_len,\n                                                      key_seq_len);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nvoid call_kernel_scaled_aligned_causal_masked_softmax_backward(\n    dim3 grid_size, dim3 block_size, const int shmem_size, hipStream_t stream, output_t *gradInput,\n    const input_t *grad, const input_t *output, const acc_t scale, const int microbatches,\n    const int query_seq_len, const int key_seq_len) {\n  scaled_aligned_causal_masked_softmax_warp_backward<input_t, output_t, acc_t, log2_elements>\n      <<<grid_size, block_size, 0, stream>>>(gradInput, grad, output, scale, microbatches,\n                                             query_seq_len, key_seq_len);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct FunctionWrapper {\n  using ForwardType =\n      std::function<void(dim3 grid_size, dim3 block_size, const int shmem_size, hipStream_t stream,\n                         output_t *dst, const input_t *src, const acc_t scale,\n                         const int microbatches, const int query_seq_len, const int key_seq_len)>;\n  using BackwardType = std::function<void(\n      dim3 grid_size, dim3 block_size, const int shmem_size, hipStream_t stream,\n      output_t *gradInput, const input_t *grad, const input_t *output, const acc_t scale,\n      const int microbatches, const int query_seq_len, const int key_seq_len)>;\n};\n\nconstexpr int MIN_SUPPORTED_POWER = 4;\nconstexpr int MAX_SUPPORTED_POWER = 14;\nconstexpr int MIN_POWER = MIN_SUPPORTED_POWER - 1;\nconstexpr int MAX_POWER = MAX_SUPPORTED_POWER + 1;\n\n// Recursively instantiate the function for the limit of \"log2_elements\",\n// i.e. \"MAX_POWER\" defined above.\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nstruct CompileTimeLoopForward {\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static void populate(std::array<ForwardFuncType, MAX_POWER> *arr) {\n    CompileTimeLoopForward<input_t, output_t, acc_t, log2_elements - 1>::populate(arr);\n    (*arr)[log2_elements] =\n        &call_kernel_scaled_aligned_causal_masked_softmax_forward<output_t, input_t, acc_t,\n                                                                  log2_elements>;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct CompileTimeLoopForward<input_t, output_t, acc_t, MIN_POWER> {\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static void populate(std::array<ForwardFuncType, MAX_POWER> *arr) { (*arr)[MIN_POWER] = nullptr; }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\nstruct CompileTimeLoopBackward {\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static void populate(std::array<BackwardFuncType, MAX_POWER> *arr) {\n    CompileTimeLoopBackward<input_t, output_t, acc_t, log2_elements - 1>::populate(arr);\n    (*arr)[log2_elements] =\n        &call_kernel_scaled_aligned_causal_masked_softmax_backward<output_t, input_t, acc_t,\n                                                                   log2_elements>;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nstruct CompileTimeLoopBackward<input_t, output_t, acc_t, MIN_POWER> {\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static void populate(std::array<BackwardFuncType, MAX_POWER> *arr) {\n    (*arr)[MIN_POWER] = nullptr;\n  }\n};\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_aligned_causal_masked_softmax_forward(output_t *dst, const input_t *src,\n                                                           const input_t scale, int query_seq_len,\n                                                           int key_seq_len, int batches,\n                                                           int attn_heads, hipStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n\n  if (key_seq_len == 0) {\n    return;\n  }\n  int log2_elements = log2_ceil(key_seq_len);\n  const int next_power_of_two = 1 << log2_elements;\n\n  // This value must match the WARP_WIDTH constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int warp_width = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n  // This value must match the WARP_ROWS constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int microbatches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n  // use 128 threads per block to maximimize gpu utilization\n  constexpr int threads_per_block = 128;\n\n  int warps_per_block = threads_per_block / warp_width;\n  int microbatches_per_block = warps_per_block * microbatches_per_warp;\n  int microbatches = batches * attn_heads * query_seq_len;\n  int blocks = DIVUP(microbatches, microbatches_per_block);\n\n  dim3 block_size(warp_width, warps_per_block);\n  dim3 grid_size(blocks);\n\n  // create an array of pointers to functions\n  using ForwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::ForwardType;\n  static std::array<ForwardFuncType, MAX_POWER> forwardFunctionArray;\n  static bool is_initialized = false;\n  if (!is_initialized) {\n    CompileTimeLoopForward<input_t, output_t, acc_t, MAX_SUPPORTED_POWER>::populate(\n        &forwardFunctionArray);\n    is_initialized = true;\n  }\n  // Call the corresponding kernel\n  forwardFunctionArray[log2_elements](grid_size, block_size, 0, stream, dst, src, scale,\n                                      microbatches, query_seq_len, key_seq_len);\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_aligned_causal_masked_softmax_backward(\n    output_t *grad_input, const input_t *grad, const input_t *output, const acc_t scale,\n    int query_seq_len, int key_seq_len, int batches, int attn_heads, hipStream_t stream) {\n  NVTE_CHECK(key_seq_len >= 0 && key_seq_len <= 16384, \"Unsupported shape.\");\n\n  if (key_seq_len == 0) {\n    return;\n  }\n  int log2_elements = log2_ceil(key_seq_len);\n  const int next_power_of_two = 1 << log2_elements;\n\n  // This value must match the WARP_WIDTH constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int warp_width = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n  // This value must match the WARP_ROWS constexpr\n  // value computed inside scaled_aligned_causal_masked_softmax_warp_forward.\n  int microbatches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n  // use 128 threads per block to maximimize gpu utilization\n  constexpr int threads_per_block = 128;\n\n  int warps_per_block = threads_per_block / warp_width;\n  int microbatches_per_block = warps_per_block * microbatches_per_warp;\n  int microbatches = batches * attn_heads * query_seq_len;\n  int blocks = DIVUP(microbatches, microbatches_per_block);\n\n  dim3 block_size(warp_width, warps_per_block);\n  dim3 grid_size(blocks);\n\n  // create an array of pointers to functions\n  using BackwardFuncType = typename FunctionWrapper<input_t, output_t, acc_t>::BackwardType;\n  static std::array<BackwardFuncType, MAX_POWER> backwardFunctionArray;\n  static bool is_initialized = false;\n  if (!is_initialized) {\n    CompileTimeLoopBackward<input_t, output_t, acc_t, MAX_SUPPORTED_POWER>::populate(\n        &backwardFunctionArray);\n    is_initialized = true;\n  }\n  // Call the corresponding kernel\n  backwardFunctionArray[log2_elements](grid_size, block_size, 0, stream, grad_input, grad, output,\n                                       scale, microbatches, query_seq_len, key_seq_len);\n}\n\nvoid scaled_aligned_causal_masked_softmax_forward(const Tensor &input, Tensor *softmax_results,\n                                                  float scale_factor, hipStream_t stream) {\n  const int batches = input.data.shape[0];\n  const int attn_heads = input.data.shape[1];\n  const int query_seq_len = input.data.shape[2];\n  const int key_seq_len = input.data.shape[3];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_aligned_causal_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, query_seq_len,\n          key_seq_len, batches, attn_heads, stream););\n}\n\nvoid scaled_aligned_causal_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                                   const Tensor softmax_results, float scale_factor,\n                                                   hipStream_t stream) {\n  // output grads is a 4d tensor with dimensions [batches, attn_heads, seq_len, seq_len]\n  const int batches = output_grads.data.shape[0];\n  const int attn_heads = output_grads.data.shape[1];\n  const int query_seq_len = output_grads.data.shape[2];\n  const int key_seq_len = output_grads.data.shape[3];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_aligned_causal_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor,\n          query_seq_len, key_seq_len, batches, attn_heads, stream););\n}\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_aligned_causal_masked_softmax_forward(const NVTETensor input,\n                                                       NVTETensor softmax_results,\n                                                       float scale_factor, hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_aligned_causal_masked_softmax_forward);\n  using namespace transformer_engine;\n  scaled_aligned_causal_masked_softmax_forward(*convertNVTETensorCheck(input),\n                                               convertNVTETensorCheck(softmax_results),\n                                               scale_factor, stream);\n}\n\nvoid nvte_scaled_aligned_causal_masked_softmax_backward(const NVTETensor incoming_grads,\n                                                        const NVTETensor softmax_results,\n                                                        NVTETensor output_grads, float scale_factor,\n                                                        hipStream_t stream) {\n  NVTE_API_CALL(nvte_scaled_aligned_causal_masked_softmax_backward);\n  using namespace transformer_engine;\n  scaled_aligned_causal_masked_softmax_backward(\n      *convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(incoming_grads),\n      *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_softmax/scaled_aligned_causal_masked_softmax.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda.h>\n#include <cuda_fp16.h>\n#include <cuda_profiler_api.h>\n#include <cuda_runtime.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <cfloat>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 1>(fp16 *dst, const fp16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 4>(fp16 *dst, const fp16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((half2 *)dst) = *((half2 *)src);  // NOLINT(*)\n}\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_zero_vector(Datatype *dst);\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 1>(bf16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 4>(bf16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 1>(fp16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 4>(fp16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_BATCH, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_BATCH; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n * 2) Implicit time (diagonal masking)\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_upper_triang_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                                const acc_t scale,\n                                                                int micro_batch_size, int stride,\n                                                                int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  size_t first_batch =\n      (blockDim.y * blockIdx.y + threadIdx.y) * gridDim.x * WARP_BATCH + blockIdx.x;\n  int local_seq = blockIdx.x + 1;\n  int warp_iteration_limit = (local_seq + ELEMENTS_PER_LDG_STG * WARP_SIZE - 1) / WARP_SIZE;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset = first_batch * stride + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : local_seq;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_data, src + i * element_count * stride + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if ((element_index + element) < batch_element_count) {\n            elements[i][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      if (it < warp_iteration_limit) {\n        elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n        sum[i] += elements[i][it];\n      }\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < local_seq) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < local_seq) {\n            out[element] = elements[i][it + element] / sum[i];\n          } else {\n            out[element] = 0.0f;\n          }\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(\n            dst + i * element_count * stride + it * WARP_SIZE, out);\n      } else if (element_index < element_count) {\n        copy_zero_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count * stride +\n                                                         it * WARP_SIZE);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_upper_triang_masked_softmax_warp_backward(output_t *gradInput,\n                                                                 const input_t *grad,\n                                                                 const input_t *output, acc_t scale,\n                                                                 int micro_batch_size, int stride,\n                                                                 int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_backward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  size_t first_batch =\n      (blockDim.y * blockIdx.y + threadIdx.y) * gridDim.x * WARP_BATCH + blockIdx.x;\n  int local_seq = blockIdx.x + 1;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  // the first element to process by the current thread\n  size_t thread_offset = first_batch * stride + ELEMENTS_PER_LDG_STG * local_idx;\n  grad += thread_offset;\n  output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory\n  acc_t grad_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  acc_t output_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : local_seq;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_grad, grad + i * element_count * stride + it * WARP_SIZE);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_output, output + i * element_count * stride + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < batch_element_count) {\n            output_reg[i][it + element] = (acc_t)temp_output[element];\n          }\n        }\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < batch_element_count) {\n            grad_reg[i][it + element] = (acc_t)temp_grad[element] * output_reg[i][it + element];\n          }\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    sum[i] = grad_reg[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[i] += grad_reg[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n        // compute gradients\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[i][it + element] -\n                                             output_reg[i][it + element] * sum[i]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(\n            gradInput + i * element_count * stride + it * WARP_SIZE, out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_upper_triang_masked_softmax_forward(output_t *dst, const input_t *src,\n                                                         const input_t scale, int softmax_elements,\n                                                         int softmax_elements_stride,\n                                                         int attn_batches, cudaStream_t stream) {\n  NVTE_CHECK(softmax_elements >= 0 && softmax_elements <= 16384, \"Unsupported shape.\");\n  if (softmax_elements == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(softmax_elements);\n    const int next_power_of_two = 1 << log2_elements;\n    int seq_len = softmax_elements;\n    int batch_count = attn_batches * seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(attn_batches % batches_per_block == 0, \"Unsupported shape.\");\n\n    int blocks_per_seq = attn_batches / batches_per_block;\n    dim3 blocks(seq_len, blocks_per_seq, 1);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 1:  // 2\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 2:  // 4\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 3:  // 8\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 4:  // 16\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 5:  // 32\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 6:  // 64\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 7:  // 128\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 8:  // 256\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 9:  // 512\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 10:  // 1024\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 11:  // 2048\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 12:  // 4096\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 13:  // 8192\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 14:  // 16384\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_upper_triang_masked_softmax_backward(output_t *grad_input, const input_t *grad,\n                                                          const input_t *output, const acc_t scale,\n                                                          int softmax_elements,\n                                                          int softmax_elements_stride,\n                                                          int attn_batches, cudaStream_t stream) {\n  NVTE_CHECK(softmax_elements >= 0 && softmax_elements <= 16384, \"Unsupported shape.\");\n  if (softmax_elements == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(softmax_elements);\n    const int next_power_of_two = 1 << log2_elements;\n    int seq_len = softmax_elements;\n    int batch_count = attn_batches * seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_backward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_backward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(attn_batches % batches_per_block == 0, \"Unsupported shape.\");\n\n    int blocks_per_seq = attn_batches / batches_per_block;\n    dim3 blocks(seq_len, blocks_per_seq, 1);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 1:  // 2\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 2:  // 4\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 3:  // 8\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 4:  // 16\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 5:  // 32\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 6:  // 64\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 7:  // 128\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 8:  // 256\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 9:  // 512\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 10:  // 1024\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 11:  // 2048\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 12:  // 4096\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 13:  // 8192\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 14:  // 16384\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid scaled_upper_triang_masked_softmax_forward(const Tensor input, Tensor *softmax_results,\n                                                float scale_factor, cudaStream_t stream) {\n  const int attn_batches = input.data.shape[0];\n  const int seq_len = input.data.shape[1];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_upper_triang_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, seq_len, seq_len,\n          attn_batches, stream););\n}\n\nvoid scaled_upper_triang_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                                 const Tensor softmax_results, float scale_factor,\n                                                 cudaStream_t stream) {\n  const int attn_batches = output_grads.data.shape[0];\n  const int seq_len = output_grads.data.shape[1];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_upper_triang_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor, seq_len,\n          seq_len, attn_batches, stream););\n}\n\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_upper_triang_masked_softmax_forward(const NVTETensor input,\n                                                     NVTETensor softmax_results, float scale_factor,\n                                                     cudaStream_t stream) {\n  using namespace transformer_engine;\n  scaled_upper_triang_masked_softmax_forward(*convertNVTETensorCheck(input),\n                                             convertNVTETensorCheck(softmax_results), scale_factor,\n                                             stream);\n}\n\nvoid nvte_scaled_upper_triang_masked_softmax_backward(const NVTETensor incoming_grads,\n                                                      const NVTETensor softmax_results,\n                                                      NVTETensor output_grads, float scale_factor,\n                                                      cudaStream_t stream) {\n  using namespace transformer_engine;\n  scaled_upper_triang_masked_softmax_backward(\n      *convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(incoming_grads),\n      *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <hip/hip_runtime_api.h>\n#include <stdint.h>\n#include <transformer_engine/softmax.h>\n\n#include <cfloat>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_vector(Datatype *dst, const Datatype *src);\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 1>(bf16 *dst, const bf16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<bf16, 4>(bf16 *dst, const bf16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 1>(fp16 *dst, const fp16 *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<fp16, 4>(fp16 *dst, const fp16 *src) {\n  *((float2 *)dst) = *((float2 *)src);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 1>(uint8_t *dst, const uint8_t *src) {\n  *dst = *src;\n}\n\ntemplate <>\n__device__ __inline__ void copy_vector<uint8_t, 4>(uint8_t *dst, const uint8_t *src) {\n  *((half2 *)dst) = *((half2 *)src);  // NOLINT(*)\n}\n\ntemplate <typename Datatype, int ELEMENTS_PER_LDG>\n__device__ __inline__ void copy_zero_vector(Datatype *dst);\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 1>(bf16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<bf16, 4>(bf16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 1>(fp16 *dst) {\n  *dst = 0.0f;\n}\n\ntemplate <>\n__device__ __inline__ void copy_zero_vector<fp16, 4>(fp16 *dst) {\n  *((float2 *)dst) = make_float2(0.0f, 0.0f);  // NOLINT(*)\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\ntemplate <typename T>\n__device__ __forceinline__ T WARP_SHFL_XOR_NATIVE(T value, int laneMask, int width = warpSize,\n                                                  unsigned int mask = 0xffffffff) {\n#if CUDA_VERSION >= 9000\n  return __shfl_xor_sync(mask, value, laneMask, width);\n#else\n  return __shfl_xor(value, laneMask, width);\n#endif\n}\n\ntemplate <typename acc_t, int WARP_BATCH, int WARP_SIZE, template <typename> class ReduceOp>\n__device__ __forceinline__ void warp_reduce(acc_t *sum) {\n  ReduceOp<acc_t> r;\n#pragma unroll\n  for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n#pragma unroll\n    for (int i = 0; i < WARP_BATCH; ++i) {\n      acc_t b = WARP_SHFL_XOR_NATIVE(sum[i], offset, WARP_SIZE);\n      sum[i] = r(sum[i], b);\n    }\n  }\n}\n\n/*\n * Extended softmax (from native aten pytorch) with following additional features\n * 1) input scaling\n * 2) Implicit time (diagonal masking)\n */\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_upper_triang_masked_softmax_warp_forward(output_t *dst, const input_t *src,\n                                                                const acc_t scale,\n                                                                int micro_batch_size, int stride,\n                                                                int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_forward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  size_t first_batch =\n      (blockDim.y * blockIdx.y + threadIdx.y) * gridDim.x * WARP_BATCH + blockIdx.x;\n  int local_seq = blockIdx.x + 1;\n  int warp_iteration_limit = (local_seq + ELEMENTS_PER_LDG_STG * WARP_SIZE - 1) / WARP_SIZE;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  size_t thread_offset = first_batch * stride + ELEMENTS_PER_LDG_STG * local_idx;\n  src += thread_offset;\n  dst += thread_offset;\n\n  // load data from global memory\n  acc_t elements[WARP_BATCH][WARP_ITERATIONS];\n  input_t temp_data[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : local_seq;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_data, src + i * element_count * stride + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if ((element_index + element) < batch_element_count) {\n            elements[i][it + element] = (acc_t)temp_data[element] * scale;\n          } else {\n            elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n          }\n        }\n      } else {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          elements[i][it + element] = -std::numeric_limits<acc_t>::infinity();\n        }\n      }\n    }\n  }\n\n  // compute max_value\n  acc_t max_value[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    max_value[i] = elements[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      max_value[i] = (max_value[i] > elements[i][it]) ? max_value[i] : elements[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Max>(max_value);\n\n  acc_t sum[WARP_BATCH]{0.0f};\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; ++it) {\n      if (it < warp_iteration_limit) {\n        elements[i][it] = std::exp((elements[i][it] - max_value[i]));\n        sum[i] += elements[i][it];\n      }\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n  output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n\n      if (element_index < local_seq) {\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < local_seq) {\n            out[element] = elements[i][it + element] / sum[i];\n          } else {\n            out[element] = 0.0f;\n          }\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(\n            dst + i * element_count * stride + it * WARP_SIZE, out);\n      } else if (element_index < element_count) {\n        copy_zero_vector<output_t, ELEMENTS_PER_LDG_STG>(dst + i * element_count * stride +\n                                                         it * WARP_SIZE);\n      } else {\n        break;\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t, int log2_elements>\n__global__ void scaled_upper_triang_masked_softmax_warp_backward(output_t *gradInput,\n                                                                 const input_t *grad,\n                                                                 const input_t *output, acc_t scale,\n                                                                 int micro_batch_size, int stride,\n                                                                 int element_count) {\n  // WARP_SIZE and WARP_BATCH must match the return values batches_per_warp and\n  // warp_size of method warp_softmax_backward_kernel.\n  constexpr int next_power_of_two = 1 << log2_elements;\n  constexpr int WARP_SIZE =\n      (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n  constexpr int WARP_ITERATIONS = next_power_of_two / WARP_SIZE;\n  constexpr int WARP_BATCH = (next_power_of_two <= 128) ? 2 : 1;\n  constexpr int ELEMENTS_PER_LDG_STG = (WARP_ITERATIONS < 4) ? 1 : 4;\n\n  size_t first_batch =\n      (blockDim.y * blockIdx.y + threadIdx.y) * gridDim.x * WARP_BATCH + blockIdx.x;\n  int local_seq = blockIdx.x + 1;\n\n  // micro_batch_size might not be a multiple of WARP_BATCH. Check how\n  // many batches have to computed within this WARP.\n  int local_batches = micro_batch_size - first_batch;\n  if (local_batches > WARP_BATCH) local_batches = WARP_BATCH;\n\n  // there might be multiple batches per warp. compute the index within the batch\n  int local_idx = threadIdx.x;\n\n  // the first element to process by the current thread\n  size_t thread_offset = first_batch * stride + ELEMENTS_PER_LDG_STG * local_idx;\n  grad += thread_offset;\n  output += thread_offset;\n  gradInput += thread_offset;\n\n  // load data from global memory\n  acc_t grad_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  acc_t output_reg[WARP_BATCH][WARP_ITERATIONS]{0.0f};\n  input_t temp_grad[ELEMENTS_PER_LDG_STG];\n  input_t temp_output[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    int batch_element_count = (i >= local_batches) ? 0 : local_seq;\n\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < batch_element_count) {\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_grad, grad + i * element_count * stride + it * WARP_SIZE);\n        copy_vector<input_t, ELEMENTS_PER_LDG_STG>(\n            temp_output, output + i * element_count * stride + it * WARP_SIZE);\n\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < batch_element_count) {\n            output_reg[i][it + element] = (acc_t)temp_output[element];\n          }\n        }\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          if (element_index + element < batch_element_count) {\n            grad_reg[i][it + element] = (acc_t)temp_grad[element] * output_reg[i][it + element];\n          }\n        }\n      }\n    }\n  }\n\n  acc_t sum[WARP_BATCH];\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    sum[i] = grad_reg[i][0];\n#pragma unroll\n    for (int it = 1; it < WARP_ITERATIONS; ++it) {\n      sum[i] += grad_reg[i][it];\n    }\n  }\n  warp_reduce<acc_t, WARP_BATCH, WARP_SIZE, Add>(sum);\n\n  // store result\n#pragma unroll\n  for (int i = 0; i < WARP_BATCH; ++i) {\n    if (i >= local_batches) break;\n#pragma unroll\n    for (int it = 0; it < WARP_ITERATIONS; it += ELEMENTS_PER_LDG_STG) {\n      int element_index = ELEMENTS_PER_LDG_STG * local_idx + it * WARP_SIZE;\n      if (element_index < element_count) {\n        // compute gradients\n        output_t out[ELEMENTS_PER_LDG_STG];\n#pragma unroll\n        for (int element = 0; element < ELEMENTS_PER_LDG_STG; ++element) {\n          out[element] = (output_t)(scale * (grad_reg[i][it + element] -\n                                             output_reg[i][it + element] * sum[i]));\n        }\n        copy_vector<output_t, ELEMENTS_PER_LDG_STG>(\n            gradInput + i * element_count * stride + it * WARP_SIZE, out);\n      }\n    }\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_upper_triang_masked_softmax_forward(output_t *dst, const input_t *src,\n                                                         const input_t scale, int softmax_elements,\n                                                         int softmax_elements_stride,\n                                                         int attn_batches, hipStream_t stream) {\n  NVTE_CHECK(softmax_elements >= 0 && softmax_elements <= 16384, \"Unsupported shape.\");\n  if (softmax_elements == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(softmax_elements);\n    const int next_power_of_two = 1 << log2_elements;\n    int seq_len = softmax_elements;\n    int batch_count = attn_batches * seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_forward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_forward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(attn_batches % batches_per_block == 0, \"Unsupported shape.\");\n\n    int blocks_per_seq = attn_batches / batches_per_block;\n    dim3 blocks(seq_len, blocks_per_seq, 1);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 1:  // 2\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 2:  // 4\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 3:  // 8\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 4:  // 16\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 5:  // 32\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 6:  // 64\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 7:  // 128\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 8:  // 256\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 9:  // 512\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 10:  // 1024\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 11:  // 2048\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 12:  // 4096\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 13:  // 8192\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      case 14:  // 16384\n        scaled_upper_triang_masked_softmax_warp_forward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(dst, src, scale, batch_count, softmax_elements_stride,\n                                             softmax_elements);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\ntemplate <typename input_t, typename output_t, typename acc_t>\nvoid dispatch_scaled_upper_triang_masked_softmax_backward(output_t *grad_input, const input_t *grad,\n                                                          const input_t *output, const acc_t scale,\n                                                          int softmax_elements,\n                                                          int softmax_elements_stride,\n                                                          int attn_batches, hipStream_t stream) {\n  NVTE_CHECK(softmax_elements >= 0 && softmax_elements <= 16384, \"Unsupported shape.\");\n  if (softmax_elements == 0) {\n    return;\n  } else {\n    int log2_elements = log2_ceil(softmax_elements);\n    const int next_power_of_two = 1 << log2_elements;\n    int seq_len = softmax_elements;\n    int batch_count = attn_batches * seq_len;\n\n    // This value must match the WARP_SIZE constexpr\n    // value computed inside softmax_warp_backward.\n    int warp_size = (next_power_of_two < THREADS_PER_WARP) ? next_power_of_two : THREADS_PER_WARP;\n\n    // This value must match the WARP_BATCH constexpr\n    // value computed inside softmax_warp_backward.\n    int batches_per_warp = (next_power_of_two <= 128) ? 2 : 1;\n\n    // use 128 threads per block to maximimize gpu utilization\n    constexpr int threads_per_block = 128;\n\n    int warps_per_block = (threads_per_block / warp_size);\n    int batches_per_block = warps_per_block * batches_per_warp;\n    NVTE_CHECK(attn_batches % batches_per_block == 0, \"Unsupported shape.\");\n\n    int blocks_per_seq = attn_batches / batches_per_block;\n    dim3 blocks(seq_len, blocks_per_seq, 1);\n    dim3 threads(warp_size, warps_per_block, 1);\n    // Launch code would be more elegant if C++ supported FOR CONSTEXPR\n    switch (log2_elements) {\n      case 0:  // 1\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 0>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 1:  // 2\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 1>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 2:  // 4\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 2>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 3:  // 8\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 3>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 4:  // 16\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 4>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 5:  // 32\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 5>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 6:  // 64\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 6>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 7:  // 128\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 7>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 8:  // 256\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 8>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 9:  // 512\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 9>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 10:  // 1024\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 10>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 11:  // 2048\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 11>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 12:  // 4096\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 12>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 13:  // 8192\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 13>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      case 14:  // 16384\n        scaled_upper_triang_masked_softmax_warp_backward<input_t, output_t, acc_t, 14>\n            <<<blocks, threads, 0, stream>>>(grad_input, grad, output, scale, batch_count,\n                                             softmax_elements_stride, softmax_elements);\n        break;\n      default:\n        break;\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid scaled_upper_triang_masked_softmax_forward(const Tensor input, Tensor *softmax_results,\n                                                float scale_factor, hipStream_t stream) {\n  const int attn_batches = input.data.shape[0];\n  const int seq_len = input.data.shape[1];\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      input.data.dtype, softmax_type,\n      dispatch_scaled_upper_triang_masked_softmax_forward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(softmax_results->data.dptr),\n          reinterpret_cast<const softmax_type *>(input.data.dptr), scale_factor, seq_len, seq_len,\n          attn_batches, stream););\n}\n\nvoid scaled_upper_triang_masked_softmax_backward(Tensor output_grads, const Tensor incoming_grads,\n                                                 const Tensor softmax_results, float scale_factor,\n                                                 hipStream_t stream) {\n  const int attn_batches = output_grads.data.shape[0];\n  const int seq_len = output_grads.data.shape[1];\n\n  // Softmax Grad\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      output_grads.data.dtype, softmax_type,\n      dispatch_scaled_upper_triang_masked_softmax_backward<softmax_type, softmax_type, float>(\n          reinterpret_cast<softmax_type *>(output_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(incoming_grads.data.dptr),\n          reinterpret_cast<softmax_type const *>(softmax_results.data.dptr), scale_factor, seq_len,\n          seq_len, attn_batches, stream););\n}\n\n}  // end namespace transformer_engine\n\nvoid nvte_scaled_upper_triang_masked_softmax_forward(const NVTETensor input,\n                                                     NVTETensor softmax_results, float scale_factor,\n                                                     hipStream_t stream) {\n  using namespace transformer_engine;\n  scaled_upper_triang_masked_softmax_forward(*convertNVTETensorCheck(input),\n                                             convertNVTETensorCheck(softmax_results), scale_factor,\n                                             stream);\n}\n\nvoid nvte_scaled_upper_triang_masked_softmax_backward(const NVTETensor incoming_grads,\n                                                      const NVTETensor softmax_results,\n                                                      NVTETensor output_grads, float scale_factor,\n                                                      hipStream_t stream) {\n  using namespace transformer_engine;\n  scaled_upper_triang_masked_softmax_backward(\n      *convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(incoming_grads),\n      *convertNVTETensorCheck(softmax_results), scale_factor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_softmax/scaled_upper_triang_masked_softmax.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/swizzle.h>\n\n#include <cstdint>\n#include <type_traits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"transformer_engine/transformer_engine.h\"\n\nnamespace transformer_engine {\nnamespace {\nconstexpr uint32_t WARP_SIZE = 32;\n}  // namespace\nnamespace swizzle_kernel_1d {\nconstexpr uint32_t WARPS_X_PER_TB = 2;  // configurable\nconstexpr uint32_t WARPS_Y_PER_TB = 2;  // configurable\n\n// Transposes a 4x4 matrix of bytes stored across four threads with consecutive thread ids where\n// each thread stores a single row (of four bytes).\n// Example:\n//   lane0.row = 0x00010203\n//   lane1.row = 0x04050607\n//   lane2.row = 0x08090a0b\n//   lane3.row = 0x0c0d0e0f\n// Becomes:\n//   lane0.row = 0x0004080c\n//   lane1.row = 0x0105090d\n//   lane2.row = 0x02060a0e\n//   lane3.row = 0x03070b0f\nuint32_t __device__ __forceinline__ transpose_4x4_byte_matrix(const uint32_t row,\n                                                              const uint32_t lane,\n                                                              const uint32_t active_mask) {\n  using cu = const uint32_t;\n\n  // Threads operate in groups of 4, and each thread stores 4 bytes at a time.\n  // The bytes in this 4x4 matrix are labeled in hex. We shuffle around bytes\n  // until we have transposed the 4x4 matrix.\n  cu m_0123_4567_89ab_cdef = row;\n  cu m_4567_0123_cdef_89ab = __shfl_xor_sync(active_mask, m_0123_4567_89ab_cdef, 1, 4);\n  cu m_0426_4062_8cae_c8ea = __byte_perm(m_0123_4567_89ab_cdef, m_4567_0123_cdef_89ab, 0x6240);\n  cu m_5173_1537_d9fb_9dbf = __byte_perm(m_0123_4567_89ab_cdef, m_4567_0123_cdef_89ab, 0x3715);\n  cu m_0426_1537_8cae_9dbf = (lane & 1) ? m_5173_1537_d9fb_9dbf : m_0426_4062_8cae_c8ea;\n  cu m_8cae_9dbf_0426_1537 = __shfl_xor_sync(active_mask, m_0426_1537_8cae_9dbf, 2, 4);\n  cu m_048c_159d_8c04_9d15 = __byte_perm(m_0426_1537_8cae_9dbf, m_8cae_9dbf_0426_1537, 0x5410);\n  cu m_ae26_bf37_26ae_37bf = __byte_perm(m_0426_1537_8cae_9dbf, m_8cae_9dbf_0426_1537, 0x3276);\n  cu m_048c_159d_26ae_37bf = (lane & 2) ? m_ae26_bf37_26ae_37bf : m_048c_159d_8c04_9d15;\n\n  return m_048c_159d_26ae_37bf;\n}\n\n// Expands a uint32_t to a uint4 by duplicating each byte four times.\n// Example: 0x01020304u becomes uint4{0x01010101, 0x02020202, 0x03030303, 0x04040404}\nuint4 __device__ __forceinline__ broadcast_uint32_t_to_uint4(uint32_t x) {\n  return {__byte_perm(x, 0, 0x0000), __byte_perm(x, 0, 0x1111), __byte_perm(x, 0, 0x2222),\n          __byte_perm(x, 0, 0x3333)};\n}\n\n// Tag struct denoting whether the number of rows of the input fp8 block scaling tensor's data\n// matrix is divisible by 128. If it is not, some threads could read out of bounds scaling factors.\nstruct no_oob_tag_t {};\nconstexpr no_oob_tag_t NO_OOB_TAG;\n\ntemplate <typename OOBT>\nvoid __global__ __launch_bounds__(WARPS_X_PER_TB* WARPS_Y_PER_TB* WARP_SIZE)\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel(\n        const void* __restrict__ const in, void* __restrict__ const out, const uint32_t tiles_x,\n        const uint32_t tiles_y, const uint32_t in_y_stride, const uint32_t out_y_stride,\n        OOBT first_oob) {\n  // resolve kernel variant\n  constexpr bool no_oob = std::is_same_v<OOBT, no_oob_tag_t>;\n  static_assert(no_oob || std::is_same_v<OOBT, uint32_t>);\n\n  // load thread indices\n  const uint32_t lane = threadIdx.x;\n  __builtin_assume(lane < WARP_SIZE);\n  const uint32_t warp_x = threadIdx.z;\n  __builtin_assume(warp_x < WARPS_X_PER_TB);\n  const uint32_t warp_y = threadIdx.y;\n  __builtin_assume(warp_y < WARPS_Y_PER_TB);\n\n  // compute tile indices\n  const uint32_t out_tile_y = blockIdx.y * WARPS_Y_PER_TB + warp_y;\n  const uint32_t out_tile_x = blockIdx.x * WARPS_X_PER_TB + warp_x;\n  const uint32_t in_tile_y = out_tile_x;\n  const uint32_t in_tile_x = out_tile_y;\n\n  // bounds check; uniform branch\n  if (out_tile_y >= tiles_y || out_tile_x >= tiles_x) {\n    return;\n  }\n\n  // calculate this warp's input base pointer\n  constexpr uint32_t in_x_stride = WARP_SIZE * sizeof(uint4);\n  const void* const warp_src =\n      (reinterpret_cast<const uint8_t*>(in) + in_tile_y * in_y_stride + in_tile_x * in_x_stride);\n\n  // load scaling factors for this lane's initial four 1x128 tiles\n  uint4 sf;\n  if constexpr (no_oob) {\n    sf = reinterpret_cast<const uint4*>(warp_src)[lane];\n  } else {\n    if ((out_tile_y < tiles_y - 1) || lane < first_oob) {\n      sf = reinterpret_cast<const uint4*>(warp_src)[lane];\n    } else {\n      sf = uint4{0, 0, 0, 0};\n    }\n  }\n\n  // pack the exponent bits of the scaling factors\n  uint32_t packed_exponents = ((sf.x >> 23) & 0xFF) | (((sf.y >> 23) & 0xFF) << 8) |\n                              (((sf.z >> 23) & 0xFF) << 16) | (((sf.w >> 23) & 0xFF) << 24);\n\n  // partially swizzle the scaling factors\n  constexpr uint32_t ACTIVE_MASK = 0xFFFFFFFF;  // no divergent branches\n  const uint32_t lane_load_idx = (lane % 4) * 8 + (lane / 4);\n  packed_exponents = __shfl_sync(ACTIVE_MASK, packed_exponents, lane_load_idx);\n\n  // transpose 4x4 matrices of scaling factors\n  packed_exponents = transpose_4x4_byte_matrix(packed_exponents, lane % 4, ACTIVE_MASK);\n\n  // broadcast the scaling factors for sixteen 1x32 tiles\n  sf = broadcast_uint32_t_to_uint4(packed_exponents);\n\n  // store them cooperatively for 512 1x32 tiles in a 128x128 tile\n  constexpr uint32_t out_x_stride = 512;\n  void* const warp_dst =\n      (reinterpret_cast<uint8_t*>(out) + out_tile_y * out_y_stride + out_tile_x * out_x_stride);\n  reinterpret_cast<uint4*>(warp_dst)[lane] = sf;\n}\n\nvoid launch_kernel(const void* const in, void* const out, uint32_t data_rows, uint32_t data_cols,\n                   cudaStream_t stream) {\n  NVTE_CHECK(is_aligned_ptr(in, alignof(uint4)), \"Input scaling factor pointer must be aligned to \",\n             alignof(uint4), \" bytes\");\n  NVTE_CHECK(is_aligned_ptr(out, alignof(uint4)),\n             \"Output scaling factor pointer must be aligned to \", alignof(uint4), \" bytes\");\n  NVTE_CHECK(data_rows % 4 == 0, \"Input tensor must not have any padding scaling factors\");\n\n  const uint32_t tiles_x = DIVUP(data_cols, 128u);\n  const uint32_t tiles_y = DIVUP(data_rows, 128u);\n  const dim3 grid_dim{DIVUP(tiles_x, WARPS_X_PER_TB), DIVUP(tiles_y, WARPS_Y_PER_TB), 1};\n  const dim3 block_dim{WARP_SIZE, WARPS_Y_PER_TB, WARPS_X_PER_TB};\n\n  // Each 128x128 tile in the data corresponds to a 128x1 tile in the input scales\n  // and a 128x4 tile in the output scales. The input scales are in transposed order.\n  const uint32_t input_scale_inv_cols = DIVUP(data_rows, 4u) * 4;\n  const uint32_t output_scale_inv_cols = tiles_x * 128 * 4;\n  const uint32_t in_y_stride = input_scale_inv_cols * sizeof(float);\n  const uint32_t out_y_stride = output_scale_inv_cols * sizeof(uint8_t);\n\n  const uint32_t first_oob = (input_scale_inv_cols % 128) / 4;\n\n  if (first_oob == 0) {\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n        in, out, tiles_x, tiles_y, in_y_stride, out_y_stride, NO_OOB_TAG);\n  } else {\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n        in, out, tiles_x, tiles_y, in_y_stride, out_y_stride, first_oob);\n  }\n}\n}  // namespace swizzle_kernel_1d\nnamespace swizzle_kernel_2d {\nconstexpr uint32_t WARPS_X_PER_TB = 2;  // configurable\nconstexpr uint32_t WARPS_Y_PER_TB = 2;  // configurable\n\nvoid __global__ __launch_bounds__(WARPS_X_PER_TB* WARPS_Y_PER_TB* WARP_SIZE)\n    swizzle_block_scaling_2d_to_mxfp8_scaling_factors_kernel(\n        const void* __restrict__ const in, void* __restrict__ const out, const uint32_t tiles_x,\n        const uint32_t tiles_y, const uint32_t in_y_stride, const uint32_t out_y_stride) {\n  // load thread indices\n  const uint32_t lane = threadIdx.x;\n  __builtin_assume(lane < WARP_SIZE);\n  const uint32_t warp_x = threadIdx.z;\n  __builtin_assume(warp_x < WARPS_X_PER_TB);\n  const uint32_t warp_y = threadIdx.y;\n  __builtin_assume(warp_y < WARPS_Y_PER_TB);\n\n  // compute tile indices\n  const uint32_t out_tile_y = blockIdx.y * WARPS_Y_PER_TB + warp_y;\n  const uint32_t out_tile_x = blockIdx.x * WARPS_X_PER_TB + warp_x;\n  const uint32_t in_tile_y = out_tile_y;\n  const uint32_t in_tile_x = out_tile_x;\n\n  // bounds check; uniform branch\n  if (out_tile_y >= tiles_y || out_tile_x >= tiles_x) {\n    return;\n  }\n\n  // calculate this warp's input base pointer\n  constexpr uint32_t in_x_stride = sizeof(float);\n  const void* const warp_src =\n      (reinterpret_cast<const uint8_t*>(in) + in_tile_y * in_y_stride + in_tile_x * in_x_stride);\n\n  // load scaling factor for this warp's 128x128 tile\n  uint32_t sf = *reinterpret_cast<const uint32_t*>(warp_src);\n\n  // broadcast it to four scaling factors for 1x32 tiles\n  // extract and broadcast the exponent byte to four bytes for E8M0 format\n  uint32_t exp_byte = (sf >> 23) & 0xFF;\n  sf = exp_byte | (exp_byte << 8) | (exp_byte << 16) | (exp_byte << 24);\n\n  // broadcast it to sixteen scaling factors for 1x32 tiles\n  const uint4 sf4{sf, sf, sf, sf};\n\n  // store it cooperatively for 512 1x32 tiles in a 128x128 tile\n  constexpr uint32_t out_x_stride = 512;\n  void* const warp_dst =\n      (reinterpret_cast<uint8_t*>(out) + out_tile_y * out_y_stride + out_tile_x * out_x_stride);\n  reinterpret_cast<uint4*>(warp_dst)[lane] = sf4;\n}\n\nvoid launch_kernel(const void* const in, void* const out, uint32_t data_rows, uint32_t data_cols,\n                   cudaStream_t stream) {\n  NVTE_CHECK(is_aligned_ptr(in, alignof(float)), \"Input scaling factor pointer must be aligned to \",\n             alignof(float), \" bytes\");\n  NVTE_CHECK(is_aligned_ptr(out, alignof(uint4)),\n             \"Output scaling factor pointer must be aligned to \", alignof(uint4), \" bytes\");\n\n  const uint32_t tiles_x = DIVUP(data_cols, 128u);\n  const uint32_t tiles_y = DIVUP(data_rows, 128u);\n  const dim3 grid_dim{DIVUP(tiles_x, WARPS_X_PER_TB), DIVUP(tiles_y, WARPS_Y_PER_TB), 1};\n  const dim3 block_dim{WARP_SIZE, WARPS_Y_PER_TB, WARPS_X_PER_TB};\n\n  // Each 128x128 tile in the data corresponds to a 1x1 tile in the input scales\n  // and a 128x4 tile in the output scales.\n  const uint32_t input_scale_inv_cols = DIVUP(data_cols, 512u) * 4;\n  const uint32_t output_scale_inv_cols = tiles_x * 128 * 4;\n  const uint32_t in_y_stride = input_scale_inv_cols * sizeof(float);\n  const uint32_t out_y_stride = output_scale_inv_cols * sizeof(uint8_t);\n\n  swizzle_block_scaling_2d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n      in, out, tiles_x, tiles_y, in_y_stride, out_y_stride);\n}\n}  // namespace swizzle_kernel_2d\n\nvoid swizzle_block_scaling_to_mxfp8_scaling_factors(const Tensor* input, Tensor* output,\n                                                    cudaStream_t stream) {\n  // Do nothing if tensor is empty\n  if (input->data.numel() == 0) {\n    return;\n  }\n\n  CheckInputTensor(*input, \"block_scaling_scaling_factor_input\");\n  CheckInputTensor(*output, \"mxfp8_scaling_factor_output\");\n\n  const NVTEScalingMode scaling_mode = input->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_BLOCK_SCALING_1D || scaling_mode == NVTE_BLOCK_SCALING_2D,\n             \"Input tensor must be a block scaling tensor\");\n  NVTE_CHECK(output->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Output tensor must be an mxfp8 tensor\");\n\n  NVTE_CHECK(input->data.dtype == transformer_engine::DType::kFloat8E4M3 ||\n                 input->data.dtype == transformer_engine::DType::kFloat8E5M2,\n             \"Input data must have FP8E4M3 or FP8E5M2 dtype to be compatible with MXFP8\");\n  NVTE_CHECK(output->data.dtype == input->data.dtype,\n             \"Output data must have the same dtype as input data\");\n  NVTE_CHECK(input->scale_inv.dtype == DType::kFloat32, \"Input must have FP32 scaling factors\");\n  NVTE_CHECK(output->scale_inv.dtype == DType::kFloat8E8M0,\n             \"Output must have E8M0 scaling factors\");\n\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output tensor with scales in GEMM swizzled format.\");\n\n  NVTE_CHECK(input->data.dptr != nullptr, \"Input must have rowwise data\");\n  NVTE_CHECK(output->data.dptr == input->data.dptr, \"Output must share data with input\");\n  NVTE_CHECK(input->scale_inv.dptr != nullptr, \"Input must have rowwise scaling factors\");\n  NVTE_CHECK(output->scale_inv.dptr != nullptr, \"Output must have rowwise scaling factors\");\n\n  NVTE_CHECK(input->data.shape.size() == 2, \"Input data must be a matrix\");\n  NVTE_CHECK(output->data.shape == input->data.shape,\n             \"Output data must have the same shape as input data\");\n  NVTE_CHECK(input->scale_inv.shape.size() == 2, \"Input scaling factors must be a matrix\");\n  NVTE_CHECK(output->scale_inv.shape.size() == 2, \"Output scaling factors must be a matrix\");\n\n  const size_t data_rows = input->data.shape[0];\n  const size_t data_cols = input->data.shape[1];\n  const size_t input_scale_inv_rows = input->scale_inv.shape[0];\n  const size_t input_scale_inv_cols = input->scale_inv.shape[1];\n  const size_t output_scale_inv_rows = output->scale_inv.shape[0];\n  const size_t output_scale_inv_cols = output->scale_inv.shape[1];\n\n  NVTE_CHECK(output_scale_inv_rows == DIVUP<size_t>(data_rows, 128) * 128,\n             \"Expected the output scaling factor matrix to have \",\n             DIVUP<size_t>(data_rows, 128) * 128, \" rows, but it has \", output_scale_inv_rows,\n             \" rows instead.\");\n  NVTE_CHECK(output_scale_inv_cols == DIVUP<size_t>(data_cols, 128) * 4,\n             \"Expected the output scaling factor matrix to have \",\n             DIVUP<size_t>(data_cols, 128) * 4, \" columns, but it has \", output_scale_inv_cols,\n             \" columns instead.\");\n\n  if (scaling_mode == NVTE_BLOCK_SCALING_1D) {\n    NVTE_CHECK(input_scale_inv_rows == DIVUP<size_t>(data_cols, 128),\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_cols, 128),\n               \" rows, but it has \", input_scale_inv_rows, \" rows instead.\");\n    NVTE_CHECK(input_scale_inv_cols == DIVUP<size_t>(data_rows, 4) * 4,\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_rows, 4) * 4,\n               \" columns, but it has \", input_scale_inv_cols, \" columns instead.\");\n\n    swizzle_kernel_1d::launch_kernel(input->scale_inv.dptr, output->scale_inv.dptr, data_rows,\n                                     data_cols, stream);\n  } else {  // scaling_mode == NVTE_BLOCK_SCALING_2D\n    NVTE_CHECK(input_scale_inv_rows == DIVUP<size_t>(data_rows, 128),\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_rows, 128),\n               \" rows, but it has \", input_scale_inv_rows, \" rows instead.\");\n    NVTE_CHECK(input_scale_inv_cols == DIVUP<size_t>(data_cols, 512) * 4,\n               \"Expected the input scaling factor matrix to have \",\n               DIVUP<size_t>(data_cols, 512) * 4, \" columns, but it has \", input_scale_inv_cols,\n               \" columns instead.\");\n\n    swizzle_kernel_2d::launch_kernel(input->scale_inv.dptr, output->scale_inv.dptr, data_rows,\n                                     data_cols, stream);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swizzle_block_scaling_to_mxfp8_scaling_factors(const NVTETensor input, NVTETensor output,\n                                                         cudaStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_block_scaling_to_mxfp8_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_block_scaling_to_mxfp8_scaling_factors(convertNVTETensorCheck(input),\n                                                 convertNVTETensorCheck(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/swizzle.h>\n\n#include <cstdint>\n#include <type_traits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"transformer_engine/transformer_engine.h\"\n\nnamespace transformer_engine {\nnamespace {\nconstexpr uint32_t WARP_SIZE = 32;\n}  // namespace\nnamespace swizzle_kernel_1d {\nconstexpr uint32_t WARPS_X_PER_TB = 2;  // configurable\nconstexpr uint32_t WARPS_Y_PER_TB = 2;  // configurable\n\n// Transposes a 4x4 matrix of bytes stored across four threads with consecutive thread ids where\n// each thread stores a single row (of four bytes).\n// Example:\n//   lane0.row = 0x00010203\n//   lane1.row = 0x04050607\n//   lane2.row = 0x08090a0b\n//   lane3.row = 0x0c0d0e0f\n// Becomes:\n//   lane0.row = 0x0004080c\n//   lane1.row = 0x0105090d\n//   lane2.row = 0x02060a0e\n//   lane3.row = 0x03070b0f\nuint32_t __device__ __forceinline__ transpose_4x4_byte_matrix(const uint32_t row,\n                                                              const uint32_t lane,\n                                                              const uint32_t active_mask) {\n  using cu = const uint32_t;\n\n  // Threads operate in groups of 4, and each thread stores 4 bytes at a time.\n  // The bytes in this 4x4 matrix are labeled in hex. We shuffle around bytes\n  // until we have transposed the 4x4 matrix.\n  cu m_0123_4567_89ab_cdef = row;\n  cu m_4567_0123_cdef_89ab = __shfl_xor_sync(active_mask, m_0123_4567_89ab_cdef, 1, 4);\n  cu m_0426_4062_8cae_c8ea = __byte_perm(m_0123_4567_89ab_cdef, m_4567_0123_cdef_89ab, 0x6240);\n  cu m_5173_1537_d9fb_9dbf = __byte_perm(m_0123_4567_89ab_cdef, m_4567_0123_cdef_89ab, 0x3715);\n  cu m_0426_1537_8cae_9dbf = (lane & 1) ? m_5173_1537_d9fb_9dbf : m_0426_4062_8cae_c8ea;\n  cu m_8cae_9dbf_0426_1537 = __shfl_xor_sync(active_mask, m_0426_1537_8cae_9dbf, 2, 4);\n  cu m_048c_159d_8c04_9d15 = __byte_perm(m_0426_1537_8cae_9dbf, m_8cae_9dbf_0426_1537, 0x5410);\n  cu m_ae26_bf37_26ae_37bf = __byte_perm(m_0426_1537_8cae_9dbf, m_8cae_9dbf_0426_1537, 0x3276);\n  cu m_048c_159d_26ae_37bf = (lane & 2) ? m_ae26_bf37_26ae_37bf : m_048c_159d_8c04_9d15;\n\n  return m_048c_159d_26ae_37bf;\n}\n\n// Expands a uint32_t to a uint4 by duplicating each byte four times.\n// Example: 0x01020304u becomes uint4{0x01010101, 0x02020202, 0x03030303, 0x04040404}\nuint4 __device__ __forceinline__ broadcast_uint32_t_to_uint4(uint32_t x) {\n  return {__byte_perm(x, 0, 0x0000), __byte_perm(x, 0, 0x1111), __byte_perm(x, 0, 0x2222),\n          __byte_perm(x, 0, 0x3333)};\n}\n\n// Tag struct denoting whether the number of rows of the input fp8 block scaling tensor's data\n// matrix is divisible by 128. If it is not, some threads could read out of bounds scaling factors.\nstruct no_oob_tag_t {};\nconstexpr no_oob_tag_t NO_OOB_TAG;\n\ntemplate <typename OOBT>\nvoid __global__ __launch_bounds__(WARPS_X_PER_TB* WARPS_Y_PER_TB* WARP_SIZE)\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel(\n        const void* __restrict__ const in, void* __restrict__ const out, const uint32_t tiles_x,\n        const uint32_t tiles_y, const uint32_t in_y_stride, const uint32_t out_y_stride,\n        OOBT first_oob) {\n  // resolve kernel variant\n  constexpr bool no_oob = std::is_same_v<OOBT, no_oob_tag_t>;\n  static_assert(no_oob || std::is_same_v<OOBT, uint32_t>);\n\n  // load thread indices\n  const uint32_t lane = threadIdx.x;\n  __builtin_assume(lane < WARP_SIZE);\n  const uint32_t warp_x = threadIdx.z;\n  __builtin_assume(warp_x < WARPS_X_PER_TB);\n  const uint32_t warp_y = threadIdx.y;\n  __builtin_assume(warp_y < WARPS_Y_PER_TB);\n\n  // compute tile indices\n  const uint32_t out_tile_y = blockIdx.y * WARPS_Y_PER_TB + warp_y;\n  const uint32_t out_tile_x = blockIdx.x * WARPS_X_PER_TB + warp_x;\n  const uint32_t in_tile_y = out_tile_x;\n  const uint32_t in_tile_x = out_tile_y;\n\n  // bounds check; uniform branch\n  if (out_tile_y >= tiles_y || out_tile_x >= tiles_x) {\n    return;\n  }\n\n  // calculate this warp's input base pointer\n  constexpr uint32_t in_x_stride = WARP_SIZE * sizeof(uint4);\n  const void* const warp_src =\n      (reinterpret_cast<const uint8_t*>(in) + in_tile_y * in_y_stride + in_tile_x * in_x_stride);\n\n  // load scaling factors for this lane's initial four 1x128 tiles\n  uint4 sf;\n  if constexpr (no_oob) {\n    sf = reinterpret_cast<const uint4*>(warp_src)[lane];\n  } else {\n    if ((out_tile_y < tiles_y - 1) || lane < first_oob) {\n      sf = reinterpret_cast<const uint4*>(warp_src)[lane];\n    } else {\n      sf = uint4{0, 0, 0, 0};\n    }\n  }\n\n  // pack the exponent bits of the scaling factors\n  uint32_t packed_exponents = ((sf.x >> 23) & 0xFF) | (((sf.y >> 23) & 0xFF) << 8) |\n                              (((sf.z >> 23) & 0xFF) << 16) | (((sf.w >> 23) & 0xFF) << 24);\n\n  // partially swizzle the scaling factors\n  constexpr uint32_t ACTIVE_MASK = 0xFFFFFFFF;  // no divergent branches\n  const uint32_t lane_load_idx = (lane % 4) * 8 + (lane / 4);\n  packed_exponents = __shfl_sync(ACTIVE_MASK, packed_exponents, lane_load_idx);\n\n  // transpose 4x4 matrices of scaling factors\n  packed_exponents = transpose_4x4_byte_matrix(packed_exponents, lane % 4, ACTIVE_MASK);\n\n  // broadcast the scaling factors for sixteen 1x32 tiles\n  sf = broadcast_uint32_t_to_uint4(packed_exponents);\n\n  // store them cooperatively for 512 1x32 tiles in a 128x128 tile\n  constexpr uint32_t out_x_stride = 512;\n  void* const warp_dst =\n      (reinterpret_cast<uint8_t*>(out) + out_tile_y * out_y_stride + out_tile_x * out_x_stride);\n  reinterpret_cast<uint4*>(warp_dst)[lane] = sf;\n}\n\nvoid launch_kernel(const void* const in, void* const out, uint32_t data_rows, uint32_t data_cols,\n                   hipStream_t stream) {\n  NVTE_CHECK(is_aligned_ptr(in, alignof(uint4)), \"Input scaling factor pointer must be aligned to \",\n             alignof(uint4), \" bytes\");\n  NVTE_CHECK(is_aligned_ptr(out, alignof(uint4)),\n             \"Output scaling factor pointer must be aligned to \", alignof(uint4), \" bytes\");\n  NVTE_CHECK(data_rows % 4 == 0, \"Input tensor must not have any padding scaling factors\");\n\n  const uint32_t tiles_x = DIVUP(data_cols, 128u);\n  const uint32_t tiles_y = DIVUP(data_rows, 128u);\n  const dim3 grid_dim{DIVUP(tiles_x, WARPS_X_PER_TB), DIVUP(tiles_y, WARPS_Y_PER_TB), 1};\n  const dim3 block_dim{WARP_SIZE, WARPS_Y_PER_TB, WARPS_X_PER_TB};\n\n  // Each 128x128 tile in the data corresponds to a 128x1 tile in the input scales\n  // and a 128x4 tile in the output scales. The input scales are in transposed order.\n  const uint32_t input_scale_inv_cols = DIVUP(data_rows, 4u) * 4;\n  const uint32_t output_scale_inv_cols = tiles_x * 128 * 4;\n  const uint32_t in_y_stride = input_scale_inv_cols * sizeof(float);\n  const uint32_t out_y_stride = output_scale_inv_cols * sizeof(uint8_t);\n\n  const uint32_t first_oob = (input_scale_inv_cols % 128) / 4;\n\n  if (first_oob == 0) {\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n        in, out, tiles_x, tiles_y, in_y_stride, out_y_stride, NO_OOB_TAG);\n  } else {\n    swizzle_block_scaling_1d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n        in, out, tiles_x, tiles_y, in_y_stride, out_y_stride, first_oob);\n  }\n}\n}  // namespace swizzle_kernel_1d\nnamespace swizzle_kernel_2d {\nconstexpr uint32_t WARPS_X_PER_TB = 2;  // configurable\nconstexpr uint32_t WARPS_Y_PER_TB = 2;  // configurable\n\nvoid __global__ __launch_bounds__(WARPS_X_PER_TB* WARPS_Y_PER_TB* WARP_SIZE)\n    swizzle_block_scaling_2d_to_mxfp8_scaling_factors_kernel(\n        const void* __restrict__ const in, void* __restrict__ const out, const uint32_t tiles_x,\n        const uint32_t tiles_y, const uint32_t in_y_stride, const uint32_t out_y_stride) {\n  // load thread indices\n  const uint32_t lane = threadIdx.x;\n  __builtin_assume(lane < WARP_SIZE);\n  const uint32_t warp_x = threadIdx.z;\n  __builtin_assume(warp_x < WARPS_X_PER_TB);\n  const uint32_t warp_y = threadIdx.y;\n  __builtin_assume(warp_y < WARPS_Y_PER_TB);\n\n  // compute tile indices\n  const uint32_t out_tile_y = blockIdx.y * WARPS_Y_PER_TB + warp_y;\n  const uint32_t out_tile_x = blockIdx.x * WARPS_X_PER_TB + warp_x;\n  const uint32_t in_tile_y = out_tile_y;\n  const uint32_t in_tile_x = out_tile_x;\n\n  // bounds check; uniform branch\n  if (out_tile_y >= tiles_y || out_tile_x >= tiles_x) {\n    return;\n  }\n\n  // calculate this warp's input base pointer\n  constexpr uint32_t in_x_stride = sizeof(float);\n  const void* const warp_src =\n      (reinterpret_cast<const uint8_t*>(in) + in_tile_y * in_y_stride + in_tile_x * in_x_stride);\n\n  // load scaling factor for this warp's 128x128 tile\n  uint32_t sf = *reinterpret_cast<const uint32_t*>(warp_src);\n\n  // broadcast it to four scaling factors for 1x32 tiles\n  // extract and broadcast the exponent byte to four bytes for E8M0 format\n  uint32_t exp_byte = (sf >> 23) & 0xFF;\n  sf = exp_byte | (exp_byte << 8) | (exp_byte << 16) | (exp_byte << 24);\n\n  // broadcast it to sixteen scaling factors for 1x32 tiles\n  const uint4 sf4{sf, sf, sf, sf};\n\n  // store it cooperatively for 512 1x32 tiles in a 128x128 tile\n  constexpr uint32_t out_x_stride = 512;\n  void* const warp_dst =\n      (reinterpret_cast<uint8_t*>(out) + out_tile_y * out_y_stride + out_tile_x * out_x_stride);\n  reinterpret_cast<uint4*>(warp_dst)[lane] = sf4;\n}\n\nvoid launch_kernel(const void* const in, void* const out, uint32_t data_rows, uint32_t data_cols,\n                   hipStream_t stream) {\n  NVTE_CHECK(is_aligned_ptr(in, alignof(float)), \"Input scaling factor pointer must be aligned to \",\n             alignof(float), \" bytes\");\n  NVTE_CHECK(is_aligned_ptr(out, alignof(uint4)),\n             \"Output scaling factor pointer must be aligned to \", alignof(uint4), \" bytes\");\n\n  const uint32_t tiles_x = DIVUP(data_cols, 128u);\n  const uint32_t tiles_y = DIVUP(data_rows, 128u);\n  const dim3 grid_dim{DIVUP(tiles_x, WARPS_X_PER_TB), DIVUP(tiles_y, WARPS_Y_PER_TB), 1};\n  const dim3 block_dim{WARP_SIZE, WARPS_Y_PER_TB, WARPS_X_PER_TB};\n\n  // Each 128x128 tile in the data corresponds to a 1x1 tile in the input scales\n  // and a 128x4 tile in the output scales.\n  const uint32_t input_scale_inv_cols = DIVUP(data_cols, 512u) * 4;\n  const uint32_t output_scale_inv_cols = tiles_x * 128 * 4;\n  const uint32_t in_y_stride = input_scale_inv_cols * sizeof(float);\n  const uint32_t out_y_stride = output_scale_inv_cols * sizeof(uint8_t);\n\n  swizzle_block_scaling_2d_to_mxfp8_scaling_factors_kernel<<<grid_dim, block_dim, 0, stream>>>(\n      in, out, tiles_x, tiles_y, in_y_stride, out_y_stride);\n}\n}  // namespace swizzle_kernel_2d\n\nvoid swizzle_block_scaling_to_mxfp8_scaling_factors(const Tensor* input, Tensor* output,\n                                                    hipStream_t stream) {\n  // Do nothing if tensor is empty\n  if (input->data.numel() == 0) {\n    return;\n  }\n\n  CheckInputTensor(*input, \"block_scaling_scaling_factor_input\");\n  CheckInputTensor(*output, \"mxfp8_scaling_factor_output\");\n\n  const NVTEScalingMode scaling_mode = input->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_BLOCK_SCALING_1D || scaling_mode == NVTE_BLOCK_SCALING_2D,\n             \"Input tensor must be a block scaling tensor\");\n  NVTE_CHECK(output->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Output tensor must be an mxfp8 tensor\");\n\n  NVTE_CHECK(input->data.dtype == transformer_engine::DType::kFloat8E4M3 ||\n                 input->data.dtype == transformer_engine::DType::kFloat8E5M2,\n             \"Input data must have FP8E4M3 or FP8E5M2 dtype to be compatible with MXFP8\");\n  NVTE_CHECK(output->data.dtype == input->data.dtype,\n             \"Output data must have the same dtype as input data\");\n  NVTE_CHECK(input->scale_inv.dtype == DType::kFloat32, \"Input must have FP32 scaling factors\");\n  NVTE_CHECK(output->scale_inv.dtype == DType::kFloat8E8M0,\n             \"Output must have E8M0 scaling factors\");\n\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output tensor with scales in GEMM swizzled format.\");\n\n  NVTE_CHECK(input->data.dptr != nullptr, \"Input must have rowwise data\");\n  NVTE_CHECK(output->data.dptr == input->data.dptr, \"Output must share data with input\");\n  NVTE_CHECK(input->scale_inv.dptr != nullptr, \"Input must have rowwise scaling factors\");\n  NVTE_CHECK(output->scale_inv.dptr != nullptr, \"Output must have rowwise scaling factors\");\n\n  NVTE_CHECK(input->data.shape.size() == 2, \"Input data must be a matrix\");\n  NVTE_CHECK(output->data.shape == input->data.shape,\n             \"Output data must have the same shape as input data\");\n  NVTE_CHECK(input->scale_inv.shape.size() == 2, \"Input scaling factors must be a matrix\");\n  NVTE_CHECK(output->scale_inv.shape.size() == 2, \"Output scaling factors must be a matrix\");\n\n  const size_t data_rows = input->data.shape[0];\n  const size_t data_cols = input->data.shape[1];\n  const size_t input_scale_inv_rows = input->scale_inv.shape[0];\n  const size_t input_scale_inv_cols = input->scale_inv.shape[1];\n  const size_t output_scale_inv_rows = output->scale_inv.shape[0];\n  const size_t output_scale_inv_cols = output->scale_inv.shape[1];\n\n  NVTE_CHECK(output_scale_inv_rows == DIVUP<size_t>(data_rows, 128) * 128,\n             \"Expected the output scaling factor matrix to have \",\n             DIVUP<size_t>(data_rows, 128) * 128, \" rows, but it has \", output_scale_inv_rows,\n             \" rows instead.\");\n  NVTE_CHECK(output_scale_inv_cols == DIVUP<size_t>(data_cols, 128) * 4,\n             \"Expected the output scaling factor matrix to have \",\n             DIVUP<size_t>(data_cols, 128) * 4, \" columns, but it has \", output_scale_inv_cols,\n             \" columns instead.\");\n\n  if (scaling_mode == NVTE_BLOCK_SCALING_1D) {\n    NVTE_CHECK(input_scale_inv_rows == DIVUP<size_t>(data_cols, 128),\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_cols, 128),\n               \" rows, but it has \", input_scale_inv_rows, \" rows instead.\");\n    NVTE_CHECK(input_scale_inv_cols == DIVUP<size_t>(data_rows, 4) * 4,\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_rows, 4) * 4,\n               \" columns, but it has \", input_scale_inv_cols, \" columns instead.\");\n\n    swizzle_kernel_1d::launch_kernel(input->scale_inv.dptr, output->scale_inv.dptr, data_rows,\n                                     data_cols, stream);\n  } else {  // scaling_mode == NVTE_BLOCK_SCALING_2D\n    NVTE_CHECK(input_scale_inv_rows == DIVUP<size_t>(data_rows, 128),\n               \"Expected the input scaling factor matrix to have \", DIVUP<size_t>(data_rows, 128),\n               \" rows, but it has \", input_scale_inv_rows, \" rows instead.\");\n    NVTE_CHECK(input_scale_inv_cols == DIVUP<size_t>(data_cols, 512) * 4,\n               \"Expected the input scaling factor matrix to have \",\n               DIVUP<size_t>(data_cols, 512) * 4, \" columns, but it has \", input_scale_inv_cols,\n               \" columns instead.\");\n\n    swizzle_kernel_2d::launch_kernel(input->scale_inv.dptr, output->scale_inv.dptr, data_rows,\n                                     data_cols, stream);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swizzle_block_scaling_to_mxfp8_scaling_factors(const NVTETensor input, NVTETensor output,\n                                                         hipStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_block_scaling_to_mxfp8_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_block_scaling_to_mxfp8_scaling_factors(convertNVTETensorCheck(input),\n                                                 convertNVTETensorCheck(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/swizzle/swizzle_block_scaling.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/swizzle.h>\n\n#include <cassert>\n#include <mutex>\n#include <numeric>\n#include <type_traits>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/logging.h\"\n#include \"transformer_engine/transformer_engine.h\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int MXFP8_BLOCK_SIZE = 32;\nconstexpr int NVFP4_BLOCK_SIZE = 16;\n\nint get_max_dynamic_smem() {\n  static int max_smem = -1;\n  if (max_smem < 0) {\n    int device;\n    NVTE_CHECK_CUDA(cudaGetDevice(&device));\n    NVTE_CHECK_CUDA(\n        cudaDeviceGetAttribute(&max_smem, cudaDevAttrMaxSharedMemoryPerBlockOptin, device));\n  }\n  return max_smem;\n}\n\nconstexpr __device__ __host__ int TB_DIM = 32;\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_K = 16;\nconstexpr __device__ __host__ int N_SF_PER_TD_PER_TILE = 4;\n\n// output is in ~K-major interleaved blocks\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_K_I32 = NEW_SF_TILE_DIM_K / 4;\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_M_I32 = 32;\n\ntemplate <typename LType>\n__device__ inline void regs_shuffle_with_bit_shifts(LType* regs_vec) {\n  // inp, 4-byte chunks [0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15]\n  // out, swapping byte to form new 4-byte chunks [0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15]\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t new_regs[kVectorSize];\n  int32_t* regs = reinterpret_cast<int32_t*>(regs_vec);\n\n#pragma unroll\n  for (int i = 0; i < N_TILE_PER_TD; i++) {\n#pragma unroll\n    for (int j = 0; j < N_SF_PER_TD_PER_TILE; j++) {\n      new_regs[i * N_SF_PER_TD_PER_TILE + j] =\n          (((regs[i + 0 * N_TILE_PER_TD] >> 8 * j) & 0xFF)) |\n          (((regs[i + 1 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 8) |\n          (((regs[i + 2 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 16) |\n          (((regs[i + 3 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 24);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) regs[i] = new_regs[i];\n}\n\ntemplate <typename LType>\n__device__ inline void regs_unshuffle_with_bit_shifts(LType* regs_vec) {\n  // Inverse of regs_shuffle_with_bit_shifts\n  // inp, 4-byte chunks [0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15]\n  // out, swapping byte to form new 4-byte chunks [0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15]\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t new_regs[kVectorSize];\n  int32_t* regs = reinterpret_cast<int32_t*>(regs_vec);\n\n#pragma unroll\n  for (int i = 0; i < N_TILE_PER_TD; i++) {\n#pragma unroll\n    for (int j = 0; j < N_SF_PER_TD_PER_TILE; j++) {\n      new_regs[i + j * N_TILE_PER_TD] =\n          ((regs[i * N_SF_PER_TD_PER_TILE + 0] >> 8 * j) & 0xFF) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 1] >> 8 * j) & 0xFF) << 8) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 2] >> 8 * j) & 0xFF) << 16) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 3] >> 8 * j) & 0xFF) << 24);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) regs[i] = new_regs[i];\n}\n\n// IS_PADDED_K / IS_PADDED_M select the boundary-block specialization at compile\n// time so the inner load loop avoids the per-iteration runtime checks. The\n// caller computes the runtime predicates from blockIdx/gridDim once per block\n// (uniform across the block) and dispatches to the right specialization.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K, bool IS_PADDED_K, bool IS_PADDED_M>\n__device__ void swizzle_col_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                const int K, const int original_M,\n                                                const int original_K, const int bid_x,\n                                                const int bid_y, const int grid_dim_x,\n                                                const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_SF_PER_TD = N_TILE_PER_TD * N_SF_PER_TD_PER_TILE;\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n\n  // input is in M-major\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n\n  int m_tiles_in_tb = N_TILE_PER_TD;\n  int k_tiles_in_tb = TB_DIM;\n  if (bid_x == grid_dim_x - 1) {\n    k_tiles_in_tb = (K_i32 / SF_TILE_DIM_K_I32 - 1) % k_tiles_in_tb + 1;\n  }\n  if (bid_y == grid_dim_y - 1) {\n    m_tiles_in_tb = (M_i32 / SF_TILE_DIM_M_I32 - 1) % m_tiles_in_tb + 1;\n  }\n\n  const int input_offset =\n      bid_x * TB_DIM * SF_TILE_DIM_K_I32 * M_i32 + bid_y * N_TILE_PER_TD * SF_TILE_DIM_M_I32;\n  const int32_t* input_i32 = reinterpret_cast<const int32_t*>(input) + input_offset;\n  int32_t* output_i32[N_TILE_PER_TD];\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    output_i32[i] = reinterpret_cast<int32_t*>(output) + bid_x * TB_DIM * SF_TILE_SIZE_I32 +\n                    (bid_y * N_TILE_PER_TD + i) * SF_TILE_DIM_M_I32 * K_i32;\n  }\n  extern __shared__ int slm[];\n\n  // load, global -> regs\n  // Each register read for a given i is along the M direction at K-coord\n  // (bid_x * TB_DIM * SF_TILE_DIM_K + threadIdx.y * SF_TILE_DIM_K + i). When that\n  // K-coord is past original_K, the entire register is out of the per-tensor data\n  // region (which may be the unpadded compact extent), so we must NOT issue the\n  // __ldg there -- it could read past the per-tensor buffer (and, for the last\n  // tensor in a grouped allocation, past the end of the allocation entirely).\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < m_tiles_in_tb * SF_TILE_DIM_M_I32 &&\n      threadIdx.y < k_tiles_in_tb) {\n    const int k_base = bid_x * TB_DIM * SF_TILE_DIM_K + threadIdx.y * SF_TILE_DIM_K;\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset =\n          (threadIdx.y * SF_TILE_DIM_K_I32 + i) * M_i32 + threadIdx.x * N_TILE_PER_TD;\n      const int k_coord = k_base + i;\n      if constexpr (IS_PADDED_K) {\n        if (k_coord >= original_K) {\n          // Entire register is past original_K: zero directly without loading.\n          uint8_t* zero_bytes = reinterpret_cast<uint8_t*>(regs_vec + i);\n#pragma unroll\n          for (int j = 0; j < static_cast<int>(sizeof(LType)); j++) zero_bytes[j] = 0;\n          continue;\n        }\n      }\n      regs_vec[i] = __ldg(reinterpret_cast<const LType*>(input_i32 + thread_offset));\n      // Per-byte M masking is still needed when only part of the register is past\n      // original_M (i.e. K-coord is in range but the M position spans the boundary).\n      if constexpr (IS_PADDED_M) {\n        for (int j = 0; j < N_TILE_PER_TD * sizeof(int); j++) {\n          const int index = (input_offset + thread_offset) * sizeof(int) + j;\n          if (index % M >= original_M) {\n            reinterpret_cast<uint8_t*>(regs_vec + i)[j] = 0;\n          }\n        }\n      }\n    }\n\n    // local shuffle\n    regs_shuffle_with_bit_shifts(regs_vec);\n\n    // store, regs -> shared\n    int tM = threadIdx.x * N_SF_PER_TD;\n    int* slm_tile = slm + (threadIdx.y * SF_TILE_SIZE_I32 +\n                           tM / SF_TILE_DIM_M * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD; i++) {\n      /* TODO rotate_i */\n      slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n               ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32] =\n          reinterpret_cast<int*>(regs_vec)[i];\n    }\n  }\n  __syncthreads();\n\n  // store, shared -> global\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    __align__(16) int4* output_v4i = reinterpret_cast<int4*>(output_i32[i]);\n    __align__(16) int4* slm_v4i =\n        reinterpret_cast<int4*>(slm + i * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int j = linear_id; j < SF_TILE_SIZE_I32 * k_tiles_in_tb / 4;\n         j += blockDim.x * blockDim.y) {\n      output_v4i[j] = slm_v4i[j];\n    }\n  }\n}\n\n// Dispatch helper: pick the right (IS_PADDED_K, IS_PADDED_M) col-scaling impl\n// specialization at runtime based on the per-block padding predicates. The\n// branching here is uniform across all threads in the block, so the indirect\n// path each block takes still inlines cleanly.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ __forceinline__ void dispatch_swizzle_col_scaling_kernel_impl(\n    const void* input, void* output, const int M, const int K, const int original_M,\n    const int original_K, const int bid_x, const int bid_y, const int grid_dim_x,\n    const int grid_dim_y, const bool padding_k, const bool padding_m) {\n  if (padding_k && padding_m) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_k) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_m) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_col_scaling_kernel(const void* input, void* output, const int M, const int K,\n                               const int original_M, const int original_K) {\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y,\n      padding_k, padding_m);\n}\n\ntemplate <typename LType>\n__device__ inline void regs_shuffle(LType* regs_vec) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  if constexpr (N_TILE_PER_TD == 1) return;\n\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t tmp[kVectorSize];\n  int32_t* ptr = reinterpret_cast<int32_t*>(regs_vec);\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++)\n    tmp[i % N_TILE_PER_TD * N_SF_PER_TD_PER_TILE + i / N_TILE_PER_TD] = ptr[i];\n\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) ptr[i] = tmp[i];\n}\n\n// Inverse of regs_shuffle.\ntemplate <typename LType>\n__device__ inline void regs_unshuffle(LType* regs_vec) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  if constexpr (N_TILE_PER_TD == 1) return;\n\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t tmp[kVectorSize];\n  int32_t* ptr = reinterpret_cast<int32_t*>(regs_vec);\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++)\n    tmp[i % N_SF_PER_TD_PER_TILE * N_TILE_PER_TD + i / N_SF_PER_TD_PER_TILE] = ptr[i];\n\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) ptr[i] = tmp[i];\n}\n\n// IS_PADDED_K / IS_PADDED_M select the boundary-block specialization at compile\n// time so the inner load loop avoids the per-iteration runtime checks. The\n// caller computes the runtime predicates from blockIdx/gridDim once per block\n// (uniform across the block) and dispatches to the right specialization.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K, bool IS_PADDED_K, bool IS_PADDED_M>\n__device__ void swizzle_row_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                const int K, const int original_M,\n                                                const int original_K, const int bid_x,\n                                                const int bid_y, const int grid_dim_x,\n                                                const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  // input is in K-major\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M;\n\n  int n_tiles_in_tb = N_TILES_IN_TB;\n  const int K_i32 = K / 4;\n  if (bid_x == grid_dim_x - 1) {\n    n_tiles_in_tb = (K_i32 - 1) % N_TILES_IN_TB + 1;\n  }\n\n  const int input_offset = bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB;\n  const int* input_i32 = reinterpret_cast<const int*>(input) + input_offset;\n  int* output_i32 = reinterpret_cast<int*>(output) + bid_y * SF_TILE_DIM_M_I32 * K_i32 +\n                    bid_x * N_TILES_IN_TB * SF_TILE_SIZE_I32;\n\n  extern __shared__ int4 slm_v4i[];\n\n  // load, global -> regs\n  // Each register read for a given i is along the K direction at row\n  // (bid_y * SF_TILE_DIM_M + i * TB_DIM + threadIdx.y). When that row is past\n  // original_M, the entire register is out of the per-tensor data region (which\n  // may be the unpadded compact extent), so we must NOT issue the __ldg there --\n  // it could read past the per-tensor buffer (and, for the last tensor in a\n  // grouped allocation, past the end of the allocation entirely).\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < n_tiles_in_tb) {\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int row = bid_y * SF_TILE_DIM_M + i * TB_DIM + threadIdx.y;\n      const int thread_offset = (i * TB_DIM + threadIdx.y) * K_i32 + threadIdx.x * N_TILE_PER_TD;\n      if constexpr (IS_PADDED_M) {\n        if (row >= original_M) {\n          // Entire register is past original_M: zero directly without loading.\n          uint8_t* zero_bytes = reinterpret_cast<uint8_t*>(regs_vec + i);\n#pragma unroll\n          for (int j = 0; j < static_cast<int>(sizeof(LType)); j++) zero_bytes[j] = 0;\n          continue;\n        }\n      }\n      regs_vec[i] = __ldg(reinterpret_cast<const LType*>(input_i32 + thread_offset));\n      // Per-byte K masking is still needed when only part of the register is past\n      // original_K (i.e. row is in range but the K position spans the boundary).\n      if constexpr (IS_PADDED_K) {\n#pragma unroll\n        for (int j = 0; j < N_TILE_PER_TD * sizeof(int); j++) {\n          const int index = (input_offset + thread_offset) * sizeof(int) + j;\n          if (index % K >= original_K) {\n            reinterpret_cast<uint8_t*>(regs_vec + i)[j] = 0;\n          }\n        }\n      }\n    }\n\n    // shuffle regs\n    regs_shuffle<LType>(regs_vec);\n\n// store, regs -> shared\n#pragma unroll\n    for (int i = 0; i < N_TILE_PER_TD; i++) {\n      /* TODO rotate i */\n      slm_v4i[(threadIdx.x * N_TILE_PER_TD + i) * SF_TILE_SIZE_I32 / 4 + threadIdx.y] =\n          reinterpret_cast<int4*>(regs_vec)[i];\n    }\n  }\n  __syncthreads();\n\n  // store, shared -> global\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  __align__(16) int4* output_v4i = reinterpret_cast<int4*>(output_i32);\n#pragma unroll\n  for (int i = linear_id; i < SF_TILE_SIZE_I32 * n_tiles_in_tb / 4; i += blockDim.x * blockDim.y) {\n    output_v4i[i] = slm_v4i[i];\n  }\n}\n\n// Dispatch helper: pick the right (IS_PADDED_K, IS_PADDED_M) row-scaling impl\n// specialization at runtime based on the per-block padding predicates. The\n// branching here is uniform across all threads in the block, so the indirect\n// path each block takes still inlines cleanly.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ __forceinline__ void dispatch_swizzle_row_scaling_kernel_impl(\n    const void* input, void* output, const int M, const int K, const int original_M,\n    const int original_K, const int bid_x, const int bid_y, const int grid_dim_x,\n    const int grid_dim_y, const bool padding_k, const bool padding_m) {\n  if (padding_k && padding_m) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_k) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_m) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_row_scaling_kernel(const void* input, void* output, const int M, const int K,\n                               const int original_M, const int original_K) {\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y,\n      padding_k, padding_m);\n}\n\n// Narrow-K specialization for row scaling swizzle.\n// When K is small (num_tiles_k < TB_DIM), the standard kernel wastes threadIdx.x\n// because there aren't enough K-tiles to distribute across threads.\n// This kernel repurposes the thread dimensions: threadIdx.x iterates rows within\n// an M-tile, threadIdx.y indexes M-tiles within the block, processing TB_DIM\n// M-tiles per block with full thread utilization.\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void swizzle_row_scaling_narrow_k_kernel_impl(const void* input, void* output,\n                                                         const int M, const int K,\n                                                         const int original_M, const int original_K,\n                                                         const int bid, const int grid_dim) {\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  const int K_i32 = K / 4;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n\n  const int m_tile = bid * blockDim.y + threadIdx.y;\n  const bool active = (m_tile < num_tiles_m);\n\n  extern __shared__ int4 slm_v4i[];\n  const int slm_tile_v4i = K_i32 * (SF_TILE_SIZE_I32 / 4);\n\n  if (active) {\n    const bool padding_m = (m_tile == num_tiles_m - 1) && (original_M < M);\n    const bool padding_k = (original_K < K);\n\n    int4* my_slm = slm_v4i + threadIdx.y * slm_tile_v4i;\n\n    for (int k = 0; k < K_i32; k++) {\n      const int input_base = m_tile * SF_TILE_DIM_M * K_i32 + k;\n      const int* input_i32 = reinterpret_cast<const int*>(input) + input_base;\n\n      int regs[N_SF_PER_TD_PER_TILE];\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        const int row = i * TB_DIM + threadIdx.x;\n        regs[i] = __ldg(input_i32 + row * K_i32);\n        if (padding_m || padding_k) {\n          for (int j = 0; j < 4; j++) {\n            const int byte_row = m_tile * SF_TILE_DIM_M + row;\n            const int byte_col = k * 4 + j;\n            if (byte_row >= original_M || byte_col >= original_K) {\n              reinterpret_cast<uint8_t*>(&regs[i])[j] = 0;\n            }\n          }\n        }\n      }\n\n      my_slm[k * (SF_TILE_SIZE_I32 / 4) + threadIdx.x] = *reinterpret_cast<int4*>(regs);\n    }\n  }\n\n  __syncthreads();\n\n  if (active) {\n    int4* my_slm = slm_v4i + threadIdx.y * slm_tile_v4i;\n    int4* out_v4i =\n        reinterpret_cast<int4*>(reinterpret_cast<int*>(output) + m_tile * SF_TILE_DIM_M * K_i32);\n\n    for (int i = threadIdx.x; i < slm_tile_v4i; i += blockDim.x) {\n      out_v4i[i] = my_slm[i];\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_row_scaling_narrow_k_kernel(const void* input, void* output, const int M, const int K,\n                                        const int original_M, const int original_K) {\n  swizzle_row_scaling_narrow_k_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, gridDim.x);\n}\n\n// Narrow-M variant of the column scaling swizzle kernel, for when num_tiles_m < TB_DIM.\n// Analogous to the narrow-K row kernel: when the M dimension is small, the normal\n// col kernel underutilizes threads in the load phase because threadIdx.x covers M\n// positions with vectorized loads, leaving many threads idle. This kernel repurposes\n// thread dimensions: threadIdx.y indexes K-tiles within the block, threadIdx.x covers\n// one int32 column of an M-tile, and M-tiles are iterated serially.\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void swizzle_col_scaling_narrow_m_kernel_impl(const void* input, void* output,\n                                                         const int M, const int K,\n                                                         const int original_M, const int original_K,\n                                                         const int bid, const int grid_dim) {\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n\n  const int k_tile = bid * blockDim.y + threadIdx.y;\n  const bool active = (k_tile < num_tiles_k);\n  const int remaining = num_tiles_k - bid * static_cast<int>(blockDim.y);\n  const int k_tiles_in_block = remaining <= 0 ? 0 : (remaining < TB_DIM ? remaining : TB_DIM);\n\n  extern __shared__ int slm_narrow_m[];\n\n  if (active) {\n    const bool padding_k = (k_tile == num_tiles_k - 1) && (original_K < K);\n    const int32_t* input_i32 = reinterpret_cast<const int32_t*>(input);\n\n    for (int m_tile = 0; m_tile < num_tiles_m; m_tile++) {\n      const bool padding_m = (m_tile == num_tiles_m - 1) && (original_M < M);\n\n      int regs[N_SF_PER_TD_PER_TILE];\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        const int k_row = k_tile * SF_TILE_DIM_K_I32 + i;\n        const int m_col = m_tile * SF_TILE_DIM_M_I32 + threadIdx.x;\n        regs[i] = __ldg(input_i32 + k_row * M_i32 + m_col);\n        if (padding_m || padding_k) {\n          for (int j = 0; j < 4; j++) {\n            if (m_col * 4 + j >= original_M || k_row >= original_K) {\n              reinterpret_cast<uint8_t*>(&regs[i])[j] = 0;\n            }\n          }\n        }\n      }\n\n      regs_shuffle_with_bit_shifts<int>(regs);\n\n      int tM = threadIdx.x * N_SF_PER_TD_PER_TILE;\n      int* slm_tile =\n          slm_narrow_m + m_tile * TB_DIM * SF_TILE_SIZE_I32 + threadIdx.y * SF_TILE_SIZE_I32;\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n                 ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32] = regs[i];\n      }\n    }\n  }\n\n  __syncthreads();\n\n  const int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  for (int m_tile = 0; m_tile < num_tiles_m; m_tile++) {\n    int4* out_v4i = reinterpret_cast<int4*>(reinterpret_cast<int*>(output) +\n                                            m_tile * SF_TILE_DIM_M_I32 * K_i32 +\n                                            bid * TB_DIM * SF_TILE_SIZE_I32);\n    int4* slm_v4i = reinterpret_cast<int4*>(slm_narrow_m + m_tile * TB_DIM * SF_TILE_SIZE_I32);\n    const int n_v4i = k_tiles_in_block * SF_TILE_SIZE_I32 / 4;\n    for (int j = linear_id; j < n_v4i; j += blockDim.x * blockDim.y) {\n      out_v4i[j] = slm_v4i[j];\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_col_scaling_narrow_m_kernel(const void* input, void* output, const int M, const int K,\n                                        const int original_M, const int original_K) {\n  swizzle_col_scaling_narrow_m_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, gridDim.x);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void unswizzle_row_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                  const int K, const int bid_x, const int bid_y,\n                                                  const int grid_dim_x, const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M;\n\n  int n_tiles_in_tb = N_TILES_IN_TB;\n  const int K_i32 = K / 4;\n  if (bid_x == grid_dim_x - 1) {\n    n_tiles_in_tb = (K_i32 - 1) % N_TILES_IN_TB + 1;\n  }\n\n  const int input_offset =\n      bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB * SF_TILE_SIZE_I32;\n  const int* input_i32 = reinterpret_cast<const int*>(input) + input_offset;\n  const int output_offset = bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB;\n  int* output_i32 = reinterpret_cast<int*>(output) + output_offset;\n\n  extern __shared__ int4 slm_v4i[];\n\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  const int4* input_v4i = reinterpret_cast<const int4*>(input_i32);\n#pragma unroll\n  for (int i = linear_id; i < SF_TILE_SIZE_I32 * n_tiles_in_tb / 4; i += blockDim.x * blockDim.y) {\n    slm_v4i[i] = input_v4i[i];\n  }\n  __syncthreads();\n\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < n_tiles_in_tb) {\n#pragma unroll\n    for (int i = 0; i < N_TILE_PER_TD; i++) {\n      reinterpret_cast<int4*>(regs_vec)[i] =\n          slm_v4i[(threadIdx.x * N_TILE_PER_TD + i) * SF_TILE_SIZE_I32 / 4 + threadIdx.y];\n    }\n\n    regs_unshuffle<LType>(regs_vec);\n\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset = (i * TB_DIM + threadIdx.y) * K_i32 + threadIdx.x * N_TILE_PER_TD;\n      reinterpret_cast<LType*>(output_i32 + thread_offset)[0] = regs_vec[i];\n    }\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void unswizzle_col_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                  const int K, const int bid_x, const int bid_y,\n                                                  const int grid_dim_x, const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_SF_PER_TD = N_TILE_PER_TD * N_SF_PER_TD_PER_TILE;\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n\n  int m_tiles_in_tb = N_TILE_PER_TD;\n  int k_tiles_in_tb = TB_DIM;\n  if (bid_x == grid_dim_x - 1) {\n    k_tiles_in_tb = (K_i32 / SF_TILE_DIM_K_I32 - 1) % k_tiles_in_tb + 1;\n  }\n  if (bid_y == grid_dim_y - 1) {\n    m_tiles_in_tb = (M_i32 / SF_TILE_DIM_M_I32 - 1) % m_tiles_in_tb + 1;\n  }\n\n  const int32_t* input_i32[N_TILE_PER_TD];\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    input_i32[i] = reinterpret_cast<const int32_t*>(input) + bid_x * TB_DIM * SF_TILE_SIZE_I32 +\n                   (bid_y * N_TILE_PER_TD + i) * SF_TILE_DIM_M_I32 * K_i32;\n  }\n  const int output_offset =\n      bid_x * TB_DIM * SF_TILE_DIM_K_I32 * M_i32 + bid_y * N_TILE_PER_TD * SF_TILE_DIM_M_I32;\n  int* output_i32 = reinterpret_cast<int*>(output) + output_offset;\n\n  extern __shared__ int slm[];\n\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    __align__(16) const int4* input_v4i = reinterpret_cast<const int4*>(input_i32[i]);\n    __align__(16) int4* slm_v4i =\n        reinterpret_cast<int4*>(slm + i * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int j = linear_id; j < SF_TILE_SIZE_I32 * k_tiles_in_tb / 4;\n         j += blockDim.x * blockDim.y) {\n      slm_v4i[j] = input_v4i[j];\n    }\n  }\n  __syncthreads();\n\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < m_tiles_in_tb * SF_TILE_DIM_M_I32 &&\n      threadIdx.y < k_tiles_in_tb) {\n    int tM = threadIdx.x * N_SF_PER_TD;\n    int* slm_tile = slm + (threadIdx.y * SF_TILE_SIZE_I32 +\n                           tM / SF_TILE_DIM_M * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD; i++) {\n      reinterpret_cast<int*>(regs_vec)[i] =\n          slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n                   ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32];\n    }\n\n    regs_unshuffle_with_bit_shifts(regs_vec);\n\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset =\n          (threadIdx.y * SF_TILE_DIM_K_I32 + i) * M_i32 + threadIdx.x * N_TILE_PER_TD;\n      reinterpret_cast<LType*>(output_i32 + thread_offset)[0] = regs_vec[i];\n    }\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    unswizzle_scaling_kernel(const void* input, void* output, const int M, const int K,\n                             const bool row_scaling) {\n  const int bid_x = blockIdx.x;\n  const int bid_y = blockIdx.y;\n  const int grid_dim_x = gridDim.x;\n  const int grid_dim_y = gridDim.y;\n  if (row_scaling) {\n    unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\nstruct MultiSwizzleArgs {\n  // (input) Data buffers for input scaling factors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for swizzled scaling factors\n  void* output_list[kMaxTensorsPerKernel];\n  // Input scaling factor m\n  int m_list[kMaxTensorsPerKernel];\n  // Input scaling factor k\n  int k_list[kMaxTensorsPerKernel];\n  // Input scaling factor m before padding\n  int original_m_list[kMaxTensorsPerKernel];\n  // Input scaling factor k before padding\n  int original_k_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\nconstexpr size_t round_up_to_multiple(size_t value, size_t multiple) {\n  return DIVUP(value, multiple) * multiple;\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_row_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                     const int K, const int original_M,\n                                                     const int original_K,\n                                                     const size_t input_stride_bytes,\n                                                     const size_t output_stride_bytes) {\n  const int tensor_id = blockIdx.z;\n  // Input and output strides may differ: input is in the kernel-produced \"compact\"\n  // layout (per-tensor stride = original_M * padded_k * elem_size) when callers\n  // pass the unswizzled grouped scale buffer as-is, while the output is always in\n  // the per-tensor padded (\"swizzle-ready\") layout (padded_m * padded_k * elem_size).\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * input_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * output_stride_bytes;\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input_base, output_base, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x,\n      gridDim.y, padding_k, padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_col_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                     const int K, const int original_M,\n                                                     const int original_K,\n                                                     const size_t input_stride_bytes,\n                                                     const size_t output_stride_bytes) {\n  const int tensor_id = blockIdx.z;\n  // See the rowwise kernel for stride semantics. For columnwise the per-tensor\n  // compact stride is DIVUP(original_K, 1) * padded_m * elem_size (i.e. the\n  // unpadded scale-row count in the K direction times the padded M extent).\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * input_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * output_stride_bytes;\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input_base, output_base, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x,\n      gridDim.y, padding_k, padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_unswizzle_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                   const int K, const size_t scale_stride_bytes,\n                                                   const bool row_scaling) {\n  const int tensor_id = blockIdx.z;\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * scale_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * scale_stride_bytes;\n  if (row_scaling) {\n    unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input_base, output_base, M, K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y);\n  } else {\n    unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input_base, output_base, M, K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_unswizzle_row_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int flat_offset = bid - kernel_args.block_range[tensor_id];\n  const int grid_dim_x = DIVUP(num_tiles_k, N_TILES_IN_TB);\n  const int grid_dim_y = num_tiles_m;\n  const int bid_x = flat_offset / grid_dim_y;\n  const int bid_y = flat_offset % grid_dim_y;\n\n  unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_unswizzle_col_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int flat_offset = bid - kernel_args.block_range[tensor_id];\n  const int grid_dim_x = DIVUP(num_tiles_k, TB_DIM);\n  const int grid_dim_y = DIVUP(num_tiles_m, N_TILE_PER_TD);\n  const int bid_x = flat_offset / grid_dim_y;\n  const int bid_y = flat_offset % grid_dim_y;\n\n  unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_swizzle_row_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  // Find tensor corresponding to block\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  // Get args corresponding to block\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  // Get block index in grid. Emulate 2D grid.\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim_x = DIVUP(num_tiles_k, N_TILES_IN_TB);\n  const int grid_dim_y = num_tiles_m;\n  const int bid_x = (bid - kernel_args.block_range[tensor_id]) / grid_dim_y;\n  const int bid_y = (bid - kernel_args.block_range[tensor_id]) % grid_dim_y;\n\n  const bool padding_m = (bid_y == grid_dim_y - 1) && (original_M < M);\n  const bool padding_k = (bid_x == grid_dim_x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y, padding_k,\n      padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_swizzle_col_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  // Find tensor corresponding to block\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  // Get args corresponding to block\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n\n  // Get block index in grid. Emulate 2D grid.\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim_x = DIVUP(num_tiles_k, TB_DIM);\n  const int grid_dim_y = DIVUP(num_tiles_m, N_TILE_PER_TD);\n  const int bid_x = (bid - kernel_args.block_range[tensor_id]) / grid_dim_y;\n  const int bid_y = (bid - kernel_args.block_range[tensor_id]) % grid_dim_y;\n\n  const bool padding_m = (bid_y == grid_dim_y - 1) && (original_M < M);\n  const bool padding_k = (bid_x == grid_dim_x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y, padding_k,\n      padding_m);\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    multi_tensor_swizzle_row_scaling_narrow_k_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n  const int flat_bid = bid - kernel_args.block_range[tensor_id];\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim = DIVUP(num_tiles_m, TB_DIM);\n\n  swizzle_row_scaling_narrow_k_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, flat_bid, grid_dim);\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    multi_tensor_swizzle_col_scaling_narrow_m_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n  const int flat_bid = bid - kernel_args.block_range[tensor_id];\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int grid_dim = DIVUP(num_tiles_k, TB_DIM);\n\n  swizzle_col_scaling_narrow_m_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, flat_bid, grid_dim);\n}\n\n}  // namespace\n\nvoid swizzle_scaling_factors(const Tensor* input, Tensor* output, cudaStream_t stream) {\n  // Check scaling mode\n  const auto& scaling_mode = input->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_MXFP8_1D_SCALING || scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Input tensor has invalid scaling mode (\", to_string(input->scaling_mode), \").\");\n\n  // Check tensors\n  CheckInputTensor(*input, \"scaling_factor_input\");\n  CheckInputTensor(*output, \"scaling_factor_output\");\n  NVTE_CHECK(!input->with_gemm_swizzled_scales,\n             \"Expected input tensor with scales in compact format.\");\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output tensor with scales in GEMM swizzled format.\");\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING:\n      NVTE_CHECK(is_fp8_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP8, got \",\n                 to_string(input->dtype()), \").\");\n      break;\n    case NVTE_NVFP4_1D_SCALING:\n      NVTE_CHECK(is_fp4_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP4, got \",\n                 to_string(input->dtype()), \").\");\n      break;\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  // Check if scaling factors are non-trivial\n  const bool has_rowwise_scale_inv = input->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = input->columnwise_scale_inv.has_data();\n  NVTE_CHECK(!has_rowwise_scale_inv || !has_columnwise_scale_inv,\n             \"Input tensor has both row-wise and column-wise scaling factors\");\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  // Deduce tensor dims\n  int m{0}, k{0};\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(input->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->scale_inv.shape, \".\");\n        m = input->scale_inv.shape[0];\n        k = input->scale_inv.shape[1];\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(input->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->columnwise_scale_inv.shape,\n                   \".\");\n        m = input->columnwise_scale_inv.shape[1];\n        k = input->columnwise_scale_inv.shape[0];\n      }\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(input->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->scale_inv.shape, \".\");\n        m = input->scale_inv.shape[0];\n        k = input->scale_inv.shape[1];\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(input->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->columnwise_scale_inv.shape,\n                   \".\");\n        m = input->columnwise_scale_inv.shape[0];\n        k = input->columnwise_scale_inv.shape[1];\n      }\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  // Check dims\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n  NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n\n  // Check that output tensor matches input tensor\n  if (has_rowwise_scale_inv) {\n    NVTE_CHECK(output->scale_inv.has_data(),\n               \"Output tensor does not have row-wise scaling factors.\");\n    NVTE_CHECK(m * k == output->scale_inv.numel(), \"Expected output tensor to have \", m * k,\n               \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n  }\n  if (has_columnwise_scale_inv) {\n    NVTE_CHECK(output->columnwise_scale_inv.has_data(),\n               \"Output tensor does not have column-wise scaling factors.\");\n    NVTE_CHECK(\n        m * k == output->columnwise_scale_inv.numel(), \"Expected output tensor to have \", m * k,\n        \" column-wise scaling factors, but got shape=\", output->columnwise_scale_inv.shape, \".\");\n  }\n\n  // Choose swizzle implementation\n  bool rowwise_swizzle{false}, columnwise_swizzle{false};\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      rowwise_swizzle = has_rowwise_scale_inv;\n      columnwise_swizzle = has_columnwise_scale_inv;\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      // NVFP4 column-wise data is transposed, so row-wise and\n      // column-wise scales have same swizzling format\n      rowwise_swizzle = true;\n      columnwise_swizzle = false;\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  const dim3 block_size(TB_DIM, TB_DIM);\n  const int num_tiles_m = m / SF_TILE_DIM_M;\n  const int num_tiles_k = k / SF_TILE_DIM_K;\n\n  // Perform row-wise swizzle\n  if (rowwise_swizzle) {\n    int original_M{0}, original_K{0};\n    void *input_scale_inv_ptr{nullptr}, *output_scale_inv_ptr{nullptr};\n    switch (scaling_mode) {\n      case NVTE_MXFP8_1D_SCALING: {\n        original_M = input->flat_first_dim();\n        original_K = input->flat_last_dim() / MXFP8_BLOCK_SIZE;\n        input_scale_inv_ptr = input->scale_inv.dptr;\n        output_scale_inv_ptr = output->scale_inv.dptr;\n        break;\n      }\n      case NVTE_NVFP4_1D_SCALING: {\n        if (has_rowwise_scale_inv) {\n          original_M = input->flat_first_dim();\n          original_K = input->flat_last_dim() / NVFP4_BLOCK_SIZE;\n          input_scale_inv_ptr = input->scale_inv.dptr;\n          output_scale_inv_ptr = output->scale_inv.dptr;\n        } else if (has_columnwise_scale_inv) {\n          original_M = input->flat_last_dim();\n          original_K = input->flat_first_dim() / NVFP4_BLOCK_SIZE;\n          input_scale_inv_ptr = input->columnwise_scale_inv.dptr;\n          output_scale_inv_ptr = output->columnwise_scale_inv.dptr;\n        }\n        break;\n      }\n      default:\n        NVTE_ERROR(\"Invalid scaling mode\");\n    }\n\n    const int narrow_k_slm_size =\n        TB_DIM * num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n    if (num_tiles_k < TB_DIM && narrow_k_slm_size <= get_max_dynamic_smem()) {\n      // Narrow-K: batch TB_DIM M-tiles per block, fully utilizing all threads.\n      dim3 num_blocks_narrow(DIVUP(num_tiles_m, TB_DIM));\n      NVTE_CHECK_CUDA(\n          cudaFuncSetAttribute(swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                               cudaFuncAttributeMaxDynamicSharedMemorySize, narrow_k_slm_size));\n      swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks_narrow, block_size, narrow_k_slm_size, stream>>>(\n              input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n    } else {\n      int vec_load_size = (num_tiles_k - 1) % 4 + 1;\n      /* there is no int3 and misaligned if using int4/int2 */\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n      dim3 num_blocks(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m);\n      int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n\n  // Perform column-wise swizzle\n  if (columnwise_swizzle) {\n    const int original_M = input->flat_last_dim();\n    const int original_K = input->flat_first_dim() / MXFP8_BLOCK_SIZE;\n\n    const int narrow_m_slm_size =\n        TB_DIM * num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n    if (num_tiles_m < TB_DIM && narrow_m_slm_size <= get_max_dynamic_smem()) {\n      // Narrow-M: batch TB_DIM K-tiles per block, fully utilizing all threads.\n      dim3 num_blocks_narrow(DIVUP(num_tiles_k, TB_DIM));\n      NVTE_CHECK_CUDA(\n          cudaFuncSetAttribute(swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                               cudaFuncAttributeMaxDynamicSharedMemorySize, narrow_m_slm_size));\n      swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks_narrow, block_size, narrow_m_slm_size, stream>>>(\n              input->columnwise_scale_inv.dptr, output->columnwise_scale_inv.dptr, m, k, original_M,\n              original_K);\n    } else {\n      int vec_load_size = (num_tiles_m - 1) % 4 + 1;\n      if (vec_load_size == 3) vec_load_size = 1; /* no int3 and misaligned if using int4/int2 */\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n      dim3 num_blocks(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size));\n      int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(\n              cudaFuncSetAttribute(swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                   cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nvoid launch_multi_tensor_swizzle_scaling_factors(MultiSwizzleArgs& kernel_args,\n                                                 const int vec_load_size, const bool is_rowwise,\n                                                 const bool use_narrow_k, const bool use_narrow_m,\n                                                 cudaStream_t stream) {\n  // cudaFuncSetAttribute is a host-synchronous driver call; cache the max shared memory\n  // setting per kernel variant so we only pay the cost when slm_size actually increases.\n  auto set_smem_if_needed = [](auto kernel_fn, int slm, int& cached) {\n    if (cached < slm) {\n      NVTE_CHECK_CUDA(\n          cudaFuncSetAttribute(kernel_fn, cudaFuncAttributeMaxDynamicSharedMemorySize, slm));\n      cached = slm;\n    }\n  };\n\n  dim3 block_size(TB_DIM, TB_DIM);\n\n  if (is_rowwise && use_narrow_k) {\n    // Narrow-K path: each block handles TB_DIM M-tiles with full thread utilization.\n    // slm_size depends on num_tiles_k, which can vary per tensor \u2014 use the max.\n    int max_num_tiles_k = 0;\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int num_tiles_m = kernel_args.m_list[j] / SF_TILE_DIM_M;\n      const int num_tiles_k = kernel_args.k_list[j] / SF_TILE_DIM_K;\n      max_num_tiles_k = std::max(max_num_tiles_k, num_tiles_k);\n      kernel_args.block_range[j + 1] = kernel_args.block_range[j] + DIVUP(num_tiles_m, TB_DIM);\n    }\n    int slm_size = TB_DIM * max_num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_narrow_k = -1;\n    set_smem_if_needed(\n        multi_tensor_swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n        cached_narrow_k);\n    multi_tensor_swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n        <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n  } else if (!is_rowwise && use_narrow_m) {\n    // Narrow-M path: each block handles TB_DIM K-tiles with full thread utilization.\n    // slm_size depends on num_tiles_m, which can vary per tensor \u2014 use the max.\n    int max_num_tiles_m = 0;\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int num_tiles_m = kernel_args.m_list[j] / SF_TILE_DIM_M;\n      const int num_tiles_k = kernel_args.k_list[j] / SF_TILE_DIM_K;\n      max_num_tiles_m = std::max(max_num_tiles_m, num_tiles_m);\n      kernel_args.block_range[j + 1] = kernel_args.block_range[j] + DIVUP(num_tiles_k, TB_DIM);\n    }\n    int slm_size = TB_DIM * max_num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_narrow_m = -1;\n    set_smem_if_needed(\n        multi_tensor_swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n        cached_narrow_m);\n    multi_tensor_swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n        <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n  } else {\n    int n_tiles_in_tb = TB_DIM * vec_load_size;\n    int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    /* Calculate number of CUDA blocks needed for each tensor.\n    * We have to do it here because we have to iterate over all tensors in this batch to\n    * get the minimum vec_load_size.\n    */\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int m = kernel_args.m_list[j];\n      const int k = kernel_args.k_list[j];\n      int num_tiles_m = m / SF_TILE_DIM_M;\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      if (is_rowwise) {\n        kernel_args.block_range[j + 1] =\n            kernel_args.block_range[j] + DIVUP(num_tiles_k, n_tiles_in_tb) * num_tiles_m;\n      } else {\n        kernel_args.block_range[j + 1] =\n            kernel_args.block_range[j] +\n            DIVUP(num_tiles_k, TB_DIM) * DIVUP(num_tiles_m, vec_load_size);\n      }\n    }\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_row_int4 = -1, cached_row_int2 = -1, cached_row_int1 = -1;\n    static int cached_col_int4 = -1, cached_col_int2 = -1, cached_col_int1 = -1;\n\n    if (is_rowwise) {\n      switch (vec_load_size) {\n        case 4:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int4);\n          multi_tensor_swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int2);\n          multi_tensor_swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int1);\n          multi_tensor_swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    } else {\n      switch (vec_load_size) {\n        case 4:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int4);\n          multi_tensor_swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int2);\n          multi_tensor_swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int1);\n          multi_tensor_swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n  }\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nvoid launch_multi_tensor_unswizzle_scaling_factors(MultiSwizzleArgs& kernel_args,\n                                                   const int vec_load_size, const bool is_rowwise,\n                                                   cudaStream_t stream) {\n  int n_tiles_in_tb = TB_DIM * vec_load_size;\n  int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n  for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n    const int m = kernel_args.m_list[j];\n    const int k = kernel_args.k_list[j];\n    int num_tiles_m = m / SF_TILE_DIM_M;\n    int num_tiles_k = k / SF_TILE_DIM_K;\n    if (is_rowwise) {\n      kernel_args.block_range[j + 1] =\n          kernel_args.block_range[j] + DIVUP(num_tiles_k, n_tiles_in_tb) * num_tiles_m;\n    } else {\n      kernel_args.block_range[j + 1] =\n          kernel_args.block_range[j] +\n          DIVUP(num_tiles_k, TB_DIM) * DIVUP(num_tiles_m, vec_load_size);\n    }\n  }\n\n  int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n  if (num_blocks > 0) {\n    dim3 block_size(TB_DIM, TB_DIM);\n    if (is_rowwise) {\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    } else {\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              multi_tensor_unswizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid multi_tensor_swizzle_scaling_factors(const std::vector<Tensor*>& input,\n                                          std::vector<Tensor*>& output, cudaStream_t stream,\n                                          bool check_scale_inv_shapes) {\n  auto num_tensors = input.size();\n  bool all_has_data = true;\n  bool all_has_columnwise_data = true;\n  bool all_nvfp4 = true;\n  for (size_t i = 0; i < num_tensors; i++) {\n    auto scaling_mode = input[i]->scaling_mode;\n    auto is_fp8 = is_fp8_dtype(input[i]->dtype());\n    auto is_fp4 = is_fp4_dtype(input[i]->dtype());\n    NVTE_CHECK(\n        (is_fp8 && is_mxfp8_scaling(scaling_mode)) || (is_fp4 && is_nvfp4_scaling(scaling_mode)),\n        \"Not implemented scaling mode \" + to_string(scaling_mode) + \".\");\n    NVTE_CHECK(!input[i]->with_gemm_swizzled_scales,\n               \"Expected input tensors with scales in compact format.\");\n    NVTE_CHECK(output[i]->with_gemm_swizzled_scales,\n               \"Expected output tensors with scales in GEMM swizzled format.\");\n\n    // We don't allow empty tensors. They should be filtered out before calling this function.\n    NVTE_CHECK(input[i]->numel() != 0, \"Tensor input[\", i, \"] is empty.\");\n    CheckInputTensor(*input[i], \"scaling_factor_input[\" + std::to_string(i) + \"]\",\n                     check_scale_inv_shapes);\n    CheckInputTensor(*output[i], \"scaling_factor_output[\" + std::to_string(i) + \"]\",\n                     check_scale_inv_shapes);\n    all_has_data = all_has_data && input[i]->scale_inv.has_data();\n    all_has_columnwise_data =\n        (all_has_columnwise_data && input[i]->columnwise_scale_inv.has_data());\n    all_nvfp4 = all_nvfp4 && is_nvfp4_scaling(scaling_mode);\n  }\n  NVTE_CHECK(all_has_data || all_has_columnwise_data,\n             \"All tensors should have data or columnwise data.\");\n  NVTE_CHECK(!all_has_data || !all_has_columnwise_data,\n             \"All tensors have both data and columnwise data.\");\n\n  const bool rowwise_swizzle = all_has_data || all_nvfp4;\n  const bool columnwise_swizzle = all_has_columnwise_data && !all_nvfp4;\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  if (rowwise_swizzle) {\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    bool all_narrow_k = true;\n    for (size_t i = 0; i < num_tensors; i++) {\n      //Launch kernel if argument struct is full\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        // There is no int3 and misaligned if using int4/int2.\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, true, all_narrow_k, false, stream);\n        // Reset the argument struct and vec_load_size\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n        all_narrow_k = true;\n      }\n\n      int m, k;\n\n      if (all_has_data) {\n        m = input[i]->scale_inv.shape[0];\n        k = input[i]->scale_inv.shape[1];\n      } else {\n        NVTE_CHECK(all_nvfp4, \"When doing rowwise swizzle with rowwise data, it has to be NVFP4\");\n        m = input[i]->columnwise_scale_inv.shape[0];\n        k = input[i]->columnwise_scale_inv.shape[1];\n      }\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Input scale inverse should be 2D!\");\n\n      if (all_has_data) {\n        NVTE_CHECK(output[i]->scale_inv.has_data(), \"Output tensor \", i,\n                   \" does not have row-wise scaling factors.\");\n        NVTE_CHECK(m * k == output[i]->scale_inv.numel(), \"Expected output tensor \", i, \" to have \",\n                   m * k, \" row-wise scaling factors, but got shape=\", output[i]->scale_inv.shape,\n                   \".\");\n      }\n      if (all_has_columnwise_data) {\n        NVTE_CHECK(output[i]->columnwise_scale_inv.has_data(), \"Output tensor \", i,\n                   \" does not have column-wise scaling factors.\");\n        NVTE_CHECK(m * k == output[i]->columnwise_scale_inv.numel(), \"Expected output tensor \", i,\n                   \" to have \", m * k, \" column-wise scaling factors, but got shape=\",\n                   output[i]->columnwise_scale_inv.shape, \".\");\n      }\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      const int narrow_k_slm =\n          TB_DIM * num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n      all_narrow_k =\n          all_narrow_k && (num_tiles_k < TB_DIM) && (narrow_k_slm <= get_max_dynamic_smem());\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      // We use the minimum vec_load_size across all tensors.\n      // TODO(zhongbo): fix vec_load_size for NVFP4\n      // Current unit test won't capture this issue, but in E2E\n      // using vec_load_size = 1 other than 1 will lead to mis-aligned\n      // address error in MOE training\n      vec_load_size = all_nvfp4 ? 1 : std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      if (!all_nvfp4 || all_has_data) {\n        int block_scale_size = all_nvfp4 ? NVFP4_BLOCK_SIZE : MXFP8_BLOCK_SIZE;\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->scale_inv.dptr;\n        kernel_args.original_m_list[pos] = input[i]->flat_first_dim();\n        kernel_args.original_k_list[pos] = input[i]->flat_last_dim() / block_scale_size;\n      } else {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n        kernel_args.original_m_list[pos] = input[i]->flat_last_dim();\n        kernel_args.original_k_list[pos] = input[i]->flat_first_dim() / NVFP4_BLOCK_SIZE;\n      }\n      kernel_args.num_tensors++;\n    }\n    // Launch the remaining tensors\n    // There is no int3 and misaligned if using int4/int2.\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, true, all_narrow_k, false, stream);\n  }\n\n  if (columnwise_swizzle) {\n    // NVFP4 shouldn't end up here because it only needs rowwise swizzle\n    NVTE_CHECK(!all_nvfp4, \"NVFP4 shouldn't end up here because it only needs rowwise swizzle\");\n\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    bool all_narrow_m = true;\n    for (size_t i = 0; i < num_tensors; i++) {\n      //Launch kernel if argument struct is full\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        // There is no int3 and misaligned if using int4/int2.\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, false, false, all_narrow_m, stream);\n        // Reset the argument struct and vec_load_size\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n        all_narrow_m = true;\n      }\n      const int m = input[i]->columnwise_scale_inv.shape[1];\n      const int k = input[i]->columnwise_scale_inv.shape[0];\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Input scale inverse should be 2D!\");\n      NVTE_CHECK(m * k == std::accumulate(output[i]->columnwise_scale_inv.shape.begin(),\n                                          output[i]->columnwise_scale_inv.shape.end(), 1,\n                                          std::multiplies<int>()),\n                 \"Input.columnwise_scale_inv size is not equal to \"\n                 \"Output.columnwise_scale_inv size!\");\n\n      int num_tiles_m = m / SF_TILE_DIM_M;\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      const int narrow_m_slm =\n          TB_DIM * num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n      all_narrow_m =\n          all_narrow_m && (num_tiles_m < TB_DIM) && (narrow_m_slm <= get_max_dynamic_smem());\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      // We use the minimum vec_load_size across all tensors.\n      vec_load_size = std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n      kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      kernel_args.original_m_list[pos] = input[i]->flat_last_dim();\n      kernel_args.original_k_list[pos] = input[i]->flat_first_dim() / MXFP8_BLOCK_SIZE;\n      kernel_args.num_tensors++;\n    }\n    // Launch the remaining tensors\n    // There is no int3 and misaligned if using int4/int2.\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, false, false, all_narrow_m, stream);\n  }\n}\n\nvoid unswizzle_scaling_factors(const Tensor* input, Tensor* output, cudaStream_t stream) {\n  const auto& scaling_mode = output->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_MXFP8_1D_SCALING || scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Output tensor has invalid scaling mode (\", to_string(output->scaling_mode), \").\");\n\n  CheckInputTensor(*input, \"scaling_factor_input\");\n  CheckInputTensor(*output, \"scaling_factor_output\");\n  NVTE_CHECK(input->with_gemm_swizzled_scales, \"Expected input tensor with swizzled scales.\");\n  NVTE_CHECK(!output->with_gemm_swizzled_scales,\n             \"Expected output tensor in row-major compact format.\");\n  NVTE_CHECK(input->scaling_mode == scaling_mode,\n             \"Input and output tensors must have matching scaling modes, but got \",\n             to_string(input->scaling_mode), \" and \", to_string(output->scaling_mode), \".\");\n\n  const bool has_rowwise_scale_inv = output->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = output->columnwise_scale_inv.has_data();\n  NVTE_CHECK(!has_rowwise_scale_inv || !has_columnwise_scale_inv,\n             \"Output tensor has both row-wise and column-wise scaling factors\");\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n  if (has_rowwise_scale_inv) {\n    NVTE_CHECK(input->scale_inv.has_data(),\n               \"Output tensor requests row-wise scaling factors, but input tensor does not \"\n               \"provide them.\");\n  } else if (has_columnwise_scale_inv) {\n    NVTE_CHECK(input->columnwise_scale_inv.has_data(),\n               \"Output tensor requests column-wise scaling factors, but input tensor does not \"\n               \"provide them.\");\n  }\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  const dim3 block_size(TB_DIM, TB_DIM);\n\n  int m{0}, k{0};\n  void* input_ptr{nullptr};\n  void* output_ptr{nullptr};\n  bool rowwise{false};\n\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      NVTE_CHECK(is_fp8_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP8, got \",\n                 to_string(input->dtype()), \").\");\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(output->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->scale_inv.shape, \".\");\n        m = output->scale_inv.shape[0];\n        k = output->scale_inv.shape[1];\n        NVTE_CHECK(static_cast<size_t>(m) * k == input->scale_inv.numel(),\n                   \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", input->scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n        input_ptr = input->scale_inv.dptr;\n        output_ptr = output->scale_inv.dptr;\n        rowwise = true;\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(output->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->columnwise_scale_inv.shape,\n                   \".\");\n        m = output->columnwise_scale_inv.shape[1];\n        k = output->columnwise_scale_inv.shape[0];\n        NVTE_CHECK(\n            static_cast<size_t>(m) * k == input->columnwise_scale_inv.numel(),\n            \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n            \" column-wise scaling factors, but got shape=\", input->columnwise_scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->columnwise_scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" column-wise scaling factors, but got shape=\",\n                   output->columnwise_scale_inv.shape, \".\");\n        input_ptr = input->columnwise_scale_inv.dptr;\n        output_ptr = output->columnwise_scale_inv.dptr;\n        rowwise = false;\n      }\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      NVTE_CHECK(is_fp4_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP4, got \",\n                 to_string(input->dtype()), \").\");\n      // NVFP4: always unswizzle rowwise regardless of which scale buffer holds the data\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(output->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->scale_inv.shape, \".\");\n        m = output->scale_inv.shape[0];\n        k = output->scale_inv.shape[1];\n        // Example for NVFP4 rowwise path:\n        NVTE_CHECK(static_cast<size_t>(m) * k == input->scale_inv.numel(),\n                   \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", input->scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n        input_ptr = input->scale_inv.dptr;\n        output_ptr = output->scale_inv.dptr;\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(output->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->columnwise_scale_inv.shape,\n                   \".\");\n        m = output->columnwise_scale_inv.shape[0];\n        k = output->columnwise_scale_inv.shape[1];\n        NVTE_CHECK(\n            static_cast<size_t>(m) * k == input->columnwise_scale_inv.numel(),\n            \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n            \" column-wise scaling factors, but got shape=\", input->columnwise_scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->columnwise_scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" column-wise scaling factors, but got shape=\",\n                   output->columnwise_scale_inv.shape, \".\");\n        input_ptr = input->columnwise_scale_inv.dptr;\n        output_ptr = output->columnwise_scale_inv.dptr;\n      }\n      rowwise = true;\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n  NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n\n  const int num_tiles_m = m / SF_TILE_DIM_M;\n  const int num_tiles_k = k / SF_TILE_DIM_K;\n\n  auto launch_unswizzle = [&](int vec_load_size, const dim3& num_blocks, int slm_size) {\n    switch (vec_load_size) {\n      case 4:\n        NVTE_CHECK_CUDA(\n            cudaFuncSetAttribute(unswizzle_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                 cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      case 2:\n        NVTE_CHECK_CUDA(\n            cudaFuncSetAttribute(unswizzle_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                 cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      case 1:\n        NVTE_CHECK_CUDA(\n            cudaFuncSetAttribute(unswizzle_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n                                 cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      default:\n        NVTE_ERROR(\"Not valid vec_load_size.\");\n    }\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  };\n\n  int vec_load_size = rowwise ? (num_tiles_k - 1) % 4 + 1 : (num_tiles_m - 1) % 4 + 1;\n  if (vec_load_size == 3) vec_load_size = 1;\n  int n_tiles_in_tb = TB_DIM * vec_load_size;\n  dim3 num_blocks = rowwise ? dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m)\n                            : dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size));\n  int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n  launch_unswizzle(vec_load_size, num_blocks, slm_size);\n}\n\nvoid multi_tensor_unswizzle_scaling_factors(const std::vector<Tensor*>& input,\n                                            std::vector<Tensor*>& output, cudaStream_t stream) {\n  size_t num_tensors = output.size();\n  const auto& first_scaling_mode = output[0]->scaling_mode;\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n\n  bool all_has_data = true;\n  bool all_has_columnwise_data = true;\n  bool all_nvfp4 = true;\n  for (size_t i = 0; i < num_tensors; i++) {\n    const auto scaling_mode = output[i]->scaling_mode;\n    const auto is_fp8 = is_fp8_dtype(input[i]->dtype());\n    const auto is_fp4 = is_fp4_dtype(input[i]->dtype());\n\n    NVTE_CHECK(scaling_mode == first_scaling_mode,\n               \"All tensors should have the same scaling mode in multi-tensor unswizzle.\");\n    NVTE_CHECK(\n        (is_fp8 && is_mxfp8_scaling(scaling_mode)) || (is_fp4 && is_nvfp4_scaling(scaling_mode)),\n        \"Not implemented scaling mode \" + to_string(scaling_mode) + \".\");\n    NVTE_CHECK(input[i]->with_gemm_swizzled_scales,\n               \"Expected input tensors with scales in GEMM swizzled format.\");\n    NVTE_CHECK(!output[i]->with_gemm_swizzled_scales,\n               \"Expected output tensors with scales in compact format.\");\n    NVTE_CHECK(input[i]->numel() != 0, \"Tensor input[\", i, \"] is empty.\");\n    CheckInputTensor(*input[i], \"scaling_factor_input[\" + std::to_string(i) + \"]\");\n    CheckInputTensor(*output[i], \"scaling_factor_output[\" + std::to_string(i) + \"]\");\n\n    all_has_data = all_has_data && output[i]->scale_inv.has_data();\n    all_has_columnwise_data =\n        (all_has_columnwise_data && output[i]->columnwise_scale_inv.has_data());\n    all_nvfp4 = all_nvfp4 && is_nvfp4_scaling(scaling_mode);\n  }\n  NVTE_CHECK(all_has_data || all_has_columnwise_data,\n             \"All tensors should have data or columnwise data.\");\n  NVTE_CHECK(!all_has_data || !all_has_columnwise_data,\n             \"All tensors have both data and columnwise data.\");\n\n  const bool rowwise_unswizzle = all_has_data || all_nvfp4;\n  const bool columnwise_unswizzle = all_has_columnwise_data && !all_nvfp4;\n\n  if (rowwise_unswizzle) {\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    for (size_t i = 0; i < num_tensors; i++) {\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, true, stream);\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n      }\n      int m, k;\n      if (all_has_data) {\n        NVTE_CHECK(input[i]->scale_inv.has_data(), \"Input tensor \", i,\n                   \" does not have row-wise scaling factors.\");\n        NVTE_CHECK(output[i]->scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                   \" to have \", \"2D scaling factors, got shape=\", output[i]->scale_inv.shape, \".\");\n        m = output[i]->scale_inv.shape[0];\n        k = output[i]->scale_inv.shape[1];\n        NVTE_CHECK(m * k == input[i]->scale_inv.numel(), \"Expected input tensor \", i, \" to have \",\n                   m * k, \" row-wise scaling factors, but got shape=\", input[i]->scale_inv.shape,\n                   \".\");\n      }\n\n      if (all_has_columnwise_data) {\n        NVTE_CHECK(all_nvfp4,\n                   \"When doing rowwise unswizzle with columnwise data, it has to be NVFP4\");\n        NVTE_CHECK(input[i]->columnwise_scale_inv.has_data(), \"Input tensor \", i,\n                   \" does not have column-wise scaling factors.\");\n        NVTE_CHECK(output[i]->columnwise_scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                   \" to have \",\n                   \"2D scaling factors, got shape=\", output[i]->columnwise_scale_inv.shape, \".\");\n        m = output[i]->columnwise_scale_inv.shape[0];\n        k = output[i]->columnwise_scale_inv.shape[1];\n        NVTE_CHECK(m * k == input[i]->columnwise_scale_inv.numel(), \"Expected input tensor \", i,\n                   \" to have \", m * k, \" column-wise scaling factors, but got shape=\",\n                   input[i]->columnwise_scale_inv.shape, \".\");\n      }\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Output scale inverse should be 2D!\");\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      vec_load_size = all_nvfp4 ? 1 : std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      if (!all_nvfp4 || all_has_data) {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->scale_inv.dptr;\n      } else {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      }\n      kernel_args.num_tensors++;\n    }\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, true, stream);\n  }\n\n  if (columnwise_unswizzle) {\n    NVTE_CHECK(!all_nvfp4, \"NVFP4 shouldn't end up here because it only needs rowwise unswizzle\");\n\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    for (size_t i = 0; i < num_tensors; i++) {\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, false, stream);\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n      }\n      NVTE_CHECK(output[i]->columnwise_scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                 \" to have \",\n                 \"2D scaling factors, got shape=\", output[i]->columnwise_scale_inv.shape, \".\");\n      const int m = output[i]->columnwise_scale_inv.shape[1];\n      const int k = output[i]->columnwise_scale_inv.shape[0];\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Output scale inverse should be 2D!\");\n      NVTE_CHECK(m * k == std::accumulate(input[i]->columnwise_scale_inv.shape.begin(),\n                                          input[i]->columnwise_scale_inv.shape.end(), 1,\n                                          std::multiplies<int>()),\n                 \"Input.columnwise_scale_inv size is not equal to \"\n                 \"Output.columnwise_scale_inv size!\");\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      vec_load_size = std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n      kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      kernel_args.num_tensors++;\n    }\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, false, stream);\n  }\n}\n}  // namespace transformer_engine\n\n/*\n* WIP (Phuong):\n*   - Opt for bank conflicts\n*   - Adding swizzle for 2d-block scaling.\n*/\nvoid nvte_swizzle_scaling_factors(const NVTETensor input, NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_scaling_factors(convertNVTETensorCheck(input), convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_multi_tensor_swizzle_scaling_factors(const NVTETensor* inputs, NVTETensor* outputs,\n                                               const size_t num_tensors, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_swizzle_scaling_factors);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_swizzle_scaling_factors(input_list, output_list, stream,\n                                       /*check_scale_inv_shapes=*/true);\n}\n\nvoid nvte_multi_tensor_swizzle_scaling_factors_unchecked(const NVTETensor* inputs,\n                                                         NVTETensor* outputs,\n                                                         const size_t num_tensors,\n                                                         cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_swizzle_scaling_factors_unchecked);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_swizzle_scaling_factors(input_list, output_list, stream,\n                                       /*check_scale_inv_shapes=*/false);\n}\n\nvoid nvte_unswizzle_scaling_factors(const NVTETensor input, NVTETensor output,\n                                    cudaStream_t stream) {\n  NVTE_API_CALL(nvte_unswizzle_scaling_factors);\n  using namespace transformer_engine;\n  unswizzle_scaling_factors(convertNVTETensorCheck(input), convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_multi_tensor_unswizzle_scaling_factors(const NVTETensor* inputs, NVTETensor* outputs,\n                                                 const size_t num_tensors, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_unswizzle_scaling_factors);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_unswizzle_scaling_factors(input_list, output_list, stream);\n}\n\nnamespace transformer_engine {\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_scaling_variable_shape_kernel(const void* input, void* output,\n                                                  const int64_t* m_array, const int64_t* k_array,\n                                                  int num_tensors, bool rowwise,\n                                                  size_t scale_elem_size, size_t common_m,\n                                                  size_t common_k) {\n  extern __shared__ int s_metadata[];\n  int* s_total_blocks = &s_metadata[0];\n\n  // Warp reduction to compute total workload\n  if (threadIdx.x < 32 && threadIdx.y == 0) {\n    int local_blocks = 0;\n    for (int i = threadIdx.x; i < num_tensors; i += 32) {\n      size_t m = rowwise ? (m_array ? m_array[i] : common_m) : (k_array ? k_array[i] : common_k);\n      size_t k = rowwise ? (k_array ? k_array[i] : common_k) : (m_array ? m_array[i] : common_m);\n\n      size_t padded_m = round_up_to_multiple(m, 128);\n      size_t padded_k = round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n\n      int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      int num_tiles_k = padded_k / SF_TILE_DIM_K;\n\n      int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      int grid_dim_x = rowwise ? DIVUP(num_tiles_k, n_tiles_in_tb) : DIVUP(num_tiles_k, TB_DIM);\n      int grid_dim_y = rowwise ? num_tiles_m : DIVUP(num_tiles_m, vec_load_size);\n      local_blocks += grid_dim_x * grid_dim_y;\n    }\n\n    for (int offset = 16; offset > 0; offset /= 2) {\n      local_blocks += __shfl_down_sync(0xffffffff, local_blocks, offset);\n    }\n    if (threadIdx.x == 0) *s_total_blocks = local_blocks;\n  }\n  __syncthreads();\n\n  const int total_blocks = *s_total_blocks;\n\n  // Persistent-grid loop\n  for (int linear_block_id = blockIdx.x; linear_block_id < total_blocks;\n       linear_block_id += gridDim.x) {\n    // Discover tensor_id and local_block_id via linear scan\n    int tensor_id = 0;\n    int current_block_base = 0;\n    size_t current_scale_base = 0;\n    int grid_dim_x = 0;\n    int grid_dim_y = 0;\n    size_t M = 0, K = 0;\n    int vec_load_size = 0;\n\n    for (int i = 0; i < num_tensors; ++i) {\n      M = rowwise ? (m_array ? m_array[i] : common_m) : (k_array ? k_array[i] : common_k);\n      K = rowwise ? (k_array ? k_array[i] : common_k) : (m_array ? m_array[i] : common_m);\n\n      size_t padded_m = round_up_to_multiple(M, 128);\n      size_t padded_k = round_up_to_multiple(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n\n      int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      int num_tiles_k = padded_k / SF_TILE_DIM_K;\n\n      vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      grid_dim_x = rowwise ? DIVUP(num_tiles_k, n_tiles_in_tb) : DIVUP(num_tiles_k, TB_DIM);\n      grid_dim_y = rowwise ? num_tiles_m : DIVUP(num_tiles_m, vec_load_size);\n      int blocks_i = grid_dim_x * grid_dim_y;\n\n      if (linear_block_id < current_block_base + blocks_i) {\n        tensor_id = i;\n        break;\n      }\n      current_block_base += blocks_i;\n      current_scale_base += padded_m * padded_k * scale_elem_size;\n    }\n\n    int local_block_id = linear_block_id - current_block_base;\n    int block_x = local_block_id % grid_dim_x;\n    int block_y = local_block_id / grid_dim_x;\n\n    const uint8_t* input_base = reinterpret_cast<const uint8_t*>(input) + current_scale_base;\n    uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + current_scale_base;\n\n    const int padded_m = static_cast<int>(round_up_to_multiple(M, 128));\n    const int padded_k =\n        static_cast<int>(round_up_to_multiple(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4));\n    const int original_M = static_cast<int>(M);\n    const int original_K = static_cast<int>(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)));\n    const bool padding_m = (block_y == grid_dim_y - 1) && (original_M < padded_m);\n    const bool padding_k = (block_x == grid_dim_x - 1) && (original_K < padded_k);\n\n    if (rowwise) {\n      if (vec_load_size == 4) {\n        dispatch_swizzle_row_scaling_kernel_impl<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else if (vec_load_size == 2) {\n        dispatch_swizzle_row_scaling_kernel_impl<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else {\n        dispatch_swizzle_row_scaling_kernel_impl<int, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      }\n    } else {\n      if (vec_load_size == 4) {\n        dispatch_swizzle_col_scaling_kernel_impl<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else if (vec_load_size == 2) {\n        dispatch_swizzle_col_scaling_kernel_impl<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else {\n        dispatch_swizzle_col_scaling_kernel_impl<int, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      }\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nint grouped_swizzle_variable_max_active_blocks_per_sm(int device_id) {\n  static std::vector<int> cache(cuda::num_devices(), -1);\n  static std::vector<std::once_flag> flags(cuda::num_devices());\n  NVTE_CHECK(0 <= device_id && device_id < cuda::num_devices(), \"invalid CUDA device ID\");\n\n  auto init = [&]() {\n    constexpr int metadata_shmem = sizeof(int);  // s_total_blocks\n    constexpr int dynamic_smem_size =\n        TB_DIM * 4 * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t) + metadata_shmem;\n    int max_active_blocks_per_sm;\n    NVTE_CHECK_CUDA(cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n        &max_active_blocks_per_sm,\n        grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>,\n        TB_DIM * TB_DIM, dynamic_smem_size));\n    NVTE_CHECK(max_active_blocks_per_sm > 0, \"Occupancy query returned 0 blocks per SM.\");\n    cache[device_id] = max_active_blocks_per_sm;\n  };\n  std::call_once(flags[device_id], init);\n  return cache[device_id];\n}\n\nvoid swizzle_grouped_scaling_factors(const GroupedTensor* input, GroupedTensor* output,\n                                     cudaStream_t stream) {\n  // Check scaling mode\n  NVTE_CHECK(input->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Grouped swizzle supports only MXFP8 scaling.\");\n\n  // Check tensors\n  CheckInputGroupedTensor(*input, \"input\");\n  CheckOutputGroupedTensor(*output, \"output\", false);\n  NVTE_CHECK(!input->with_gemm_swizzled_scales,\n             \"Expected input grouped tensor with scales in compact format.\");\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output grouped tensor with scales in GEMM swizzled format.\");\n\n  // Check scaling factors availability\n  const bool has_rowwise_scale_inv = input->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = input->columnwise_scale_inv.has_data();\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  const int64_t* m_array = reinterpret_cast<const int64_t*>(input->first_dims.dptr);\n  const int64_t* k_array = reinterpret_cast<const int64_t*>(input->last_dims.dptr);\n  const bool is_variable_shape = !input->all_same_shape();\n\n  if (!is_variable_shape) {\n    // Fallback to uniform shape implementation\n    // Assumption is that all the tensors share the same shapes and are contgiuous.\n    // And so we dont need to pass array of input/output pointers(due to conttiguity)\n    // as well as array of shapes(due to uniform shapes).\n    const size_t first_dim = input->get_common_first_dim();\n    const size_t last_dim = input->get_common_last_dim();\n\n    constexpr int SF_TILE_DIM_M = 128;\n    constexpr int SF_TILE_DIM_K = 4;\n    const dim3 block_size(TB_DIM, TB_DIM);\n\n    auto launch_grouped_swizzle = [&](bool rowwise) {\n      const size_t m = rowwise ? first_dim : last_dim;\n      const size_t k = rowwise ? last_dim : first_dim;\n      const size_t padded_m = round_up_to_multiple(m, 128);\n      const size_t padded_k =\n          round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n      // Per-tensor scale-element counts:\n      //  - \"padded\" layout: each tensor occupies padded_m * padded_k elements\n      //    (total buffer = num_tensors * padded_m * padded_k).\n      //  - \"compact\" layout (what the grouped MXFP8 quantize kernel actually writes):\n      //      per-tensor stride is m * padded_k (rowwise) or DIVUP(k,32) * padded_m\n      //      (columnwise) and the total buffer the C++ allocator hands out has its\n      //      grouped first dim padded up to a multiple of 128 (rowwise) or 4\n      //      (columnwise) -- so the buffer may be slightly larger than\n      //      num_tensors * compact_scale_elems, with trailing alignment slack at\n      //      the very end (never read because of the per-tensor row/k guard in the\n      //      kernel impl).\n      // The output is always written in the padded layout. The input may be in\n      // either layout; the kernel handles the compact case safely by using\n      // different per-tensor strides for input vs output and skipping loads past\n      // the per-tensor extent.\n      const size_t padded_scale_elems = padded_m * padded_k;\n      const size_t compact_scale_elems =\n          rowwise ? m * padded_k : DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)) * padded_m;\n      const size_t compact_total_scale_elems =\n          rowwise ? round_up_to_multiple(input->num_tensors * m, 128) * padded_k\n                  : round_up_to_multiple(\n                        input->num_tensors * DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4) *\n                        padded_m;\n\n      const size_t scale_elem_size = rowwise ? typeToSize(input->scale_inv.dtype)\n                                             : typeToSize(input->columnwise_scale_inv.dtype);\n\n      const size_t input_scale_numel =\n          rowwise ? input->scale_inv.numel() : input->columnwise_scale_inv.numel();\n      const size_t output_scale_numel =\n          rowwise ? output->scale_inv.numel() : output->columnwise_scale_inv.numel();\n\n      bool input_is_compact;\n      if (input_scale_numel == input->num_tensors * padded_scale_elems) {\n        input_is_compact = false;\n      } else if (input_scale_numel == compact_total_scale_elems) {\n        input_is_compact = true;\n      } else {\n        NVTE_ERROR(\"Grouped input \", (rowwise ? \"scale_inv\" : \"columnwise_scale_inv\"),\n                   \" size does not match expected packed size (got \", input_scale_numel,\n                   \", expected either \", input->num_tensors * padded_scale_elems,\n                   \" (per-tensor padded) or \", compact_total_scale_elems, \" (compact)).\");\n      }\n      NVTE_CHECK(output_scale_numel == input->num_tensors * padded_scale_elems, \"Grouped output \",\n                 (rowwise ? \"scale_inv\" : \"columnwise_scale_inv\"),\n                 \" size does not match expected per-tensor padded size.\");\n\n      const size_t input_stride_bytes =\n          (input_is_compact ? compact_scale_elems : padded_scale_elems) * scale_elem_size;\n      const size_t output_stride_bytes = padded_scale_elems * scale_elem_size;\n\n      const int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      const int num_tiles_k = padded_k / SF_TILE_DIM_K;\n      int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      const int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      dim3 num_blocks;\n      if (rowwise) {\n        num_blocks = dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m, input->num_tensors);\n      } else {\n        num_blocks =\n            dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size), input->num_tensors);\n      }\n      const int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      const int original_M = static_cast<int>(rowwise ? first_dim : last_dim);\n      const int original_K = static_cast<int>(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)));\n      const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n      void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n      if (rowwise) {\n        TRANSFORMER_ENGINE_VECTORIZED_LOAD_INTEGER_TYPE_SWITCH(vec_load_size, LType, {\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              grouped_swizzle_row_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          grouped_swizzle_row_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_ptr, output_ptr, padded_m, padded_k, original_M, original_K,\n                  input_stride_bytes, output_stride_bytes);\n        });\n      } else {\n        TRANSFORMER_ENGINE_VECTORIZED_LOAD_INTEGER_TYPE_SWITCH(vec_load_size, LType, {\n          NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n              grouped_swizzle_col_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>,\n              cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          grouped_swizzle_col_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_ptr, output_ptr, padded_m, padded_k, original_M, original_K,\n                  input_stride_bytes, output_stride_bytes);\n        });\n      }\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    };\n\n    if (has_rowwise_scale_inv) {\n      launch_grouped_swizzle(true);\n    }\n    if (has_columnwise_scale_inv) {\n      launch_grouped_swizzle(false);\n    }\n  } else {\n    // Variable shape implementation using Device-Side Block Scheduler\n    size_t num_tensors = input->num_tensors;\n\n    constexpr int SF_TILE_DIM_M = 128;\n    constexpr int SF_TILE_DIM_K = 4;\n    const dim3 block_size(TB_DIM, TB_DIM);\n    const int max_slm_size = TB_DIM * 4 * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int metadata_shmem = sizeof(int);  // s_total_blocks\n    const int dynamic_smem_size = max_slm_size + metadata_shmem;\n\n    size_t common_m = input->all_same_first_dim() ? input->get_common_first_dim() : 0;\n    size_t common_k = input->all_same_last_dim() ? input->get_common_last_dim() : 0;\n\n    NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n        grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, dynamic_smem_size));\n\n    const int device_id = cuda::current_device();\n    const int num_SMs = cuda::sm_count(device_id);\n    const int max_active_blocks_per_sm =\n        grouped_swizzle_variable_max_active_blocks_per_sm<SF_TILE_DIM_M, SF_TILE_DIM_K>(device_id);\n    const int persistent_blocks = num_SMs * max_active_blocks_per_sm;\n    const dim3 num_blocks(persistent_blocks);\n\n    auto launch_grouped_swizzle_variable = [&](bool rowwise) {\n      const size_t scale_elem_size = rowwise ? typeToSize(input->scale_inv.dtype)\n                                             : typeToSize(input->columnwise_scale_inv.dtype);\n\n      const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n      void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n      grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks, block_size, dynamic_smem_size, stream>>>(\n              input_ptr, output_ptr, m_array, k_array, num_tensors, rowwise, scale_elem_size,\n              common_m, common_k);\n\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    };\n\n    if (has_rowwise_scale_inv) {\n      launch_grouped_swizzle_variable(true);\n    }\n    if (has_columnwise_scale_inv) {\n      launch_grouped_swizzle_variable(false);\n    }\n  }\n}\n\nvoid unswizzle_grouped_scaling_factors(const GroupedTensor* input, GroupedTensor* output,\n                                       cudaStream_t stream) {\n  NVTE_CHECK(output->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Grouped unswizzle supports only MXFP8 scaling.\");\n\n  CheckInputGroupedTensor(*input, \"input\");\n  CheckOutputGroupedTensor(*output, \"output\", false);\n  NVTE_CHECK(input->with_gemm_swizzled_scales,\n             \"Expected input grouped tensor with scales in GEMM swizzled format.\");\n  NVTE_CHECK(!output->with_gemm_swizzled_scales,\n             \"Expected output grouped tensor with scales in compact format.\");\n  NVTE_CHECK(input->scaling_mode == output->scaling_mode,\n             \"Input and output grouped tensors must have matching scaling modes.\");\n  NVTE_CHECK(input->num_tensors == output->num_tensors,\n             \"Input and output grouped tensors must have the same number of tensors.\");\n\n  const bool has_rowwise_scale_inv = output->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = output->columnwise_scale_inv.has_data();\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  NVTE_CHECK(input->all_same_shape() && output->all_same_shape(),\n             \"Grouped unswizzle requires uniform tensor shapes.\");\n\n  const size_t first_dim = output->get_common_first_dim();\n  const size_t last_dim = output->get_common_last_dim();\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  const dim3 block_size(TB_DIM, TB_DIM);\n\n  auto launch_grouped_unswizzle = [&](bool rowwise) {\n    const size_t m = rowwise ? first_dim : last_dim;\n    const size_t k = rowwise ? last_dim : first_dim;\n    const size_t padded_m = round_up_to_multiple(m, 128);\n    const size_t padded_k =\n        round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n    const size_t scale_elems = padded_m * padded_k;\n\n    const size_t scale_elem_size = rowwise ? typeToSize(output->scale_inv.dtype)\n                                           : typeToSize(output->columnwise_scale_inv.dtype);\n    const size_t scale_stride_bytes = scale_elems * scale_elem_size;\n\n    if (rowwise) {\n      NVTE_CHECK(input->scale_inv.numel() == input->num_tensors * scale_elems,\n                 \"Grouped input scale_inv size does not match expected packed size.\");\n      NVTE_CHECK(output->scale_inv.numel() == output->num_tensors * scale_elems,\n                 \"Grouped output scale_inv size does not match expected packed size.\");\n    } else {\n      NVTE_CHECK(input->columnwise_scale_inv.numel() == input->num_tensors * scale_elems,\n                 \"Grouped input columnwise_scale_inv size does not match expected packed size.\");\n      NVTE_CHECK(output->columnwise_scale_inv.numel() == output->num_tensors * scale_elems,\n                 \"Grouped output columnwise_scale_inv size does not match expected packed size.\");\n    }\n\n    const int num_tiles_m = padded_m / SF_TILE_DIM_M;\n    const int num_tiles_k = padded_k / SF_TILE_DIM_K;\n    int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n    if (vec_load_size == 3) vec_load_size = 1;\n    const int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n    dim3 num_blocks;\n    if (rowwise) {\n      num_blocks = dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m, output->num_tensors);\n    } else {\n      num_blocks =\n          dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size), output->num_tensors);\n    }\n    const int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n    const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n    void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n    using kernel_t = void (*)(const void*, void*, const int, const int, const size_t, const bool);\n    kernel_t kernel_fn = nullptr;\n    switch (vec_load_size) {\n      case 4:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      case 2:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      case 1:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      default:\n        NVTE_ERROR(\"Not valid vec_load_size.\");\n    }\n    NVTE_CHECK_CUDA(\n        cudaFuncSetAttribute(kernel_fn, cudaFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n    kernel_fn<<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, padded_m,\n                                                            padded_k, scale_stride_bytes, rowwise);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  };\n\n  if (has_rowwise_scale_inv) {\n    launch_grouped_unswizzle(true);\n  }\n  if (has_columnwise_scale_inv) {\n    launch_grouped_unswizzle(false);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swizzle_grouped_scaling_factors(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                          cudaStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_grouped_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_grouped_scaling_factors(convertNVTEGroupedTensorCheck(input),\n                                  convertNVTEGroupedTensorCheck(output), stream);\n}\n\nvoid nvte_unswizzle_grouped_scaling_factors(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                            cudaStream_t stream) {\n  NVTE_API_CALL(nvte_unswizzle_grouped_scaling_factors);\n  using namespace transformer_engine;\n  unswizzle_grouped_scaling_factors(convertNVTEGroupedTensorCheck(input),\n                                    convertNVTEGroupedTensorCheck(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/swizzle.h>\n\n#include <cassert>\n#include <mutex>\n#include <numeric>\n#include <type_traits>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/logging.h\"\n#include \"transformer_engine/transformer_engine.h\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int MXFP8_BLOCK_SIZE = 32;\nconstexpr int NVFP4_BLOCK_SIZE = 16;\n\nint get_max_dynamic_smem() {\n  static int max_smem = -1;\n  if (max_smem < 0) {\n    int device;\n    NVTE_CHECK_CUDA(hipGetDevice(&device));\n    NVTE_CHECK_CUDA(\n        hipDeviceGetAttribute(&max_smem, hipDeviceAttributeSharedMemPerBlockOptin, device));\n  }\n  return max_smem;\n}\n\nconstexpr __device__ __host__ int TB_DIM = 32;\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_K = 16;\nconstexpr __device__ __host__ int N_SF_PER_TD_PER_TILE = 4;\n\n// output is in ~K-major interleaved blocks\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_K_I32 = NEW_SF_TILE_DIM_K / 4;\nconstexpr __device__ __host__ int NEW_SF_TILE_DIM_M_I32 = 32;\n\ntemplate <typename LType>\n__device__ inline void regs_shuffle_with_bit_shifts(LType* regs_vec) {\n  // inp, 4-byte chunks [0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15]\n  // out, swapping byte to form new 4-byte chunks [0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15]\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t new_regs[kVectorSize];\n  int32_t* regs = reinterpret_cast<int32_t*>(regs_vec);\n\n#pragma unroll\n  for (int i = 0; i < N_TILE_PER_TD; i++) {\n#pragma unroll\n    for (int j = 0; j < N_SF_PER_TD_PER_TILE; j++) {\n      new_regs[i * N_SF_PER_TD_PER_TILE + j] =\n          (((regs[i + 0 * N_TILE_PER_TD] >> 8 * j) & 0xFF)) |\n          (((regs[i + 1 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 8) |\n          (((regs[i + 2 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 16) |\n          (((regs[i + 3 * N_TILE_PER_TD] >> 8 * j) & 0xFF) << 24);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) regs[i] = new_regs[i];\n}\n\ntemplate <typename LType>\n__device__ inline void regs_unshuffle_with_bit_shifts(LType* regs_vec) {\n  // Inverse of regs_shuffle_with_bit_shifts\n  // inp, 4-byte chunks [0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15]\n  // out, swapping byte to form new 4-byte chunks [0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15]\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t new_regs[kVectorSize];\n  int32_t* regs = reinterpret_cast<int32_t*>(regs_vec);\n\n#pragma unroll\n  for (int i = 0; i < N_TILE_PER_TD; i++) {\n#pragma unroll\n    for (int j = 0; j < N_SF_PER_TD_PER_TILE; j++) {\n      new_regs[i + j * N_TILE_PER_TD] =\n          ((regs[i * N_SF_PER_TD_PER_TILE + 0] >> 8 * j) & 0xFF) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 1] >> 8 * j) & 0xFF) << 8) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 2] >> 8 * j) & 0xFF) << 16) |\n          (((regs[i * N_SF_PER_TD_PER_TILE + 3] >> 8 * j) & 0xFF) << 24);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) regs[i] = new_regs[i];\n}\n\n// IS_PADDED_K / IS_PADDED_M select the boundary-block specialization at compile\n// time so the inner load loop avoids the per-iteration runtime checks. The\n// caller computes the runtime predicates from blockIdx/gridDim once per block\n// (uniform across the block) and dispatches to the right specialization.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K, bool IS_PADDED_K, bool IS_PADDED_M>\n__device__ void swizzle_col_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                const int K, const int original_M,\n                                                const int original_K, const int bid_x,\n                                                const int bid_y, const int grid_dim_x,\n                                                const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_SF_PER_TD = N_TILE_PER_TD * N_SF_PER_TD_PER_TILE;\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n\n  // input is in M-major\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n\n  int m_tiles_in_tb = N_TILE_PER_TD;\n  int k_tiles_in_tb = TB_DIM;\n  if (bid_x == grid_dim_x - 1) {\n    k_tiles_in_tb = (K_i32 / SF_TILE_DIM_K_I32 - 1) % k_tiles_in_tb + 1;\n  }\n  if (bid_y == grid_dim_y - 1) {\n    m_tiles_in_tb = (M_i32 / SF_TILE_DIM_M_I32 - 1) % m_tiles_in_tb + 1;\n  }\n\n  const int input_offset =\n      bid_x * TB_DIM * SF_TILE_DIM_K_I32 * M_i32 + bid_y * N_TILE_PER_TD * SF_TILE_DIM_M_I32;\n  const int32_t* input_i32 = reinterpret_cast<const int32_t*>(input) + input_offset;\n  int32_t* output_i32[N_TILE_PER_TD];\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    output_i32[i] = reinterpret_cast<int32_t*>(output) + bid_x * TB_DIM * SF_TILE_SIZE_I32 +\n                    (bid_y * N_TILE_PER_TD + i) * SF_TILE_DIM_M_I32 * K_i32;\n  }\n  extern __shared__ int slm[];\n\n  // load, global -> regs\n  // Each register read for a given i is along the M direction at K-coord\n  // (bid_x * TB_DIM * SF_TILE_DIM_K + threadIdx.y * SF_TILE_DIM_K + i). When that\n  // K-coord is past original_K, the entire register is out of the per-tensor data\n  // region (which may be the unpadded compact extent), so we must NOT issue the\n  // __ldg there -- it could read past the per-tensor buffer (and, for the last\n  // tensor in a grouped allocation, past the end of the allocation entirely).\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < m_tiles_in_tb * SF_TILE_DIM_M_I32 &&\n      threadIdx.y < k_tiles_in_tb) {\n    const int k_base = bid_x * TB_DIM * SF_TILE_DIM_K + threadIdx.y * SF_TILE_DIM_K;\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset =\n          (threadIdx.y * SF_TILE_DIM_K_I32 + i) * M_i32 + threadIdx.x * N_TILE_PER_TD;\n      const int k_coord = k_base + i;\n      if constexpr (IS_PADDED_K) {\n        if (k_coord >= original_K) {\n          // Entire register is past original_K: zero directly without loading.\n          uint8_t* zero_bytes = reinterpret_cast<uint8_t*>(regs_vec + i);\n#pragma unroll\n          for (int j = 0; j < static_cast<int>(sizeof(LType)); j++) zero_bytes[j] = 0;\n          continue;\n        }\n      }\n      regs_vec[i] = __ldg(reinterpret_cast<const LType*>(input_i32 + thread_offset));\n      // Per-byte M masking is still needed when only part of the register is past\n      // original_M (i.e. K-coord is in range but the M position spans the boundary).\n      if constexpr (IS_PADDED_M) {\n        for (int j = 0; j < N_TILE_PER_TD * sizeof(int); j++) {\n          const int index = (input_offset + thread_offset) * sizeof(int) + j;\n          if (index % M >= original_M) {\n            reinterpret_cast<uint8_t*>(regs_vec + i)[j] = 0;\n          }\n        }\n      }\n    }\n\n    // local shuffle\n    regs_shuffle_with_bit_shifts(regs_vec);\n\n    // store, regs -> shared\n    int tM = threadIdx.x * N_SF_PER_TD;\n    int* slm_tile = slm + (threadIdx.y * SF_TILE_SIZE_I32 +\n                           tM / SF_TILE_DIM_M * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD; i++) {\n      /* TODO rotate_i */\n      slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n               ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32] =\n          reinterpret_cast<int*>(regs_vec)[i];\n    }\n  }\n  __syncthreads();\n\n  // store, shared -> global\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    __align__(16) int4* output_v4i = reinterpret_cast<int4*>(output_i32[i]);\n    __align__(16) int4* slm_v4i =\n        reinterpret_cast<int4*>(slm + i * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int j = linear_id; j < SF_TILE_SIZE_I32 * k_tiles_in_tb / 4;\n         j += blockDim.x * blockDim.y) {\n      output_v4i[j] = slm_v4i[j];\n    }\n  }\n}\n\n// Dispatch helper: pick the right (IS_PADDED_K, IS_PADDED_M) col-scaling impl\n// specialization at runtime based on the per-block padding predicates. The\n// branching here is uniform across all threads in the block, so the indirect\n// path each block takes still inlines cleanly.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ __forceinline__ void dispatch_swizzle_col_scaling_kernel_impl(\n    const void* input, void* output, const int M, const int K, const int original_M,\n    const int original_K, const int bid_x, const int bid_y, const int grid_dim_x,\n    const int grid_dim_y, const bool padding_k, const bool padding_m) {\n  if (padding_k && padding_m) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_k) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_m) {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_col_scaling_kernel(const void* input, void* output, const int M, const int K,\n                               const int original_M, const int original_K) {\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y,\n      padding_k, padding_m);\n}\n\ntemplate <typename LType>\n__device__ inline void regs_shuffle(LType* regs_vec) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  if constexpr (N_TILE_PER_TD == 1) return;\n\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t tmp[kVectorSize];\n  int32_t* ptr = reinterpret_cast<int32_t*>(regs_vec);\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++)\n    tmp[i % N_TILE_PER_TD * N_SF_PER_TD_PER_TILE + i / N_TILE_PER_TD] = ptr[i];\n\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) ptr[i] = tmp[i];\n}\n\n// Inverse of regs_shuffle.\ntemplate <typename LType>\n__device__ inline void regs_unshuffle(LType* regs_vec) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  if constexpr (N_TILE_PER_TD == 1) return;\n\n  constexpr int kVectorSize = N_SF_PER_TD_PER_TILE * N_TILE_PER_TD;\n  int32_t tmp[kVectorSize];\n  int32_t* ptr = reinterpret_cast<int32_t*>(regs_vec);\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++)\n    tmp[i % N_SF_PER_TD_PER_TILE * N_TILE_PER_TD + i / N_SF_PER_TD_PER_TILE] = ptr[i];\n\n#pragma unroll\n  for (int i = 0; i < kVectorSize; i++) ptr[i] = tmp[i];\n}\n\n// IS_PADDED_K / IS_PADDED_M select the boundary-block specialization at compile\n// time so the inner load loop avoids the per-iteration runtime checks. The\n// caller computes the runtime predicates from blockIdx/gridDim once per block\n// (uniform across the block) and dispatches to the right specialization.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K, bool IS_PADDED_K, bool IS_PADDED_M>\n__device__ void swizzle_row_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                const int K, const int original_M,\n                                                const int original_K, const int bid_x,\n                                                const int bid_y, const int grid_dim_x,\n                                                const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  // input is in K-major\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M;\n\n  int n_tiles_in_tb = N_TILES_IN_TB;\n  const int K_i32 = K / 4;\n  if (bid_x == grid_dim_x - 1) {\n    n_tiles_in_tb = (K_i32 - 1) % N_TILES_IN_TB + 1;\n  }\n\n  const int input_offset = bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB;\n  const int* input_i32 = reinterpret_cast<const int*>(input) + input_offset;\n  int* output_i32 = reinterpret_cast<int*>(output) + bid_y * SF_TILE_DIM_M_I32 * K_i32 +\n                    bid_x * N_TILES_IN_TB * SF_TILE_SIZE_I32;\n\n  extern __shared__ int4 slm_v4i[];\n\n  // load, global -> regs\n  // Each register read for a given i is along the K direction at row\n  // (bid_y * SF_TILE_DIM_M + i * TB_DIM + threadIdx.y). When that row is past\n  // original_M, the entire register is out of the per-tensor data region (which\n  // may be the unpadded compact extent), so we must NOT issue the __ldg there --\n  // it could read past the per-tensor buffer (and, for the last tensor in a\n  // grouped allocation, past the end of the allocation entirely).\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < n_tiles_in_tb) {\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int row = bid_y * SF_TILE_DIM_M + i * TB_DIM + threadIdx.y;\n      const int thread_offset = (i * TB_DIM + threadIdx.y) * K_i32 + threadIdx.x * N_TILE_PER_TD;\n      if constexpr (IS_PADDED_M) {\n        if (row >= original_M) {\n          // Entire register is past original_M: zero directly without loading.\n          uint8_t* zero_bytes = reinterpret_cast<uint8_t*>(regs_vec + i);\n#pragma unroll\n          for (int j = 0; j < static_cast<int>(sizeof(LType)); j++) zero_bytes[j] = 0;\n          continue;\n        }\n      }\n      regs_vec[i] = __ldg(reinterpret_cast<const LType*>(input_i32 + thread_offset));\n      // Per-byte K masking is still needed when only part of the register is past\n      // original_K (i.e. row is in range but the K position spans the boundary).\n      if constexpr (IS_PADDED_K) {\n#pragma unroll\n        for (int j = 0; j < N_TILE_PER_TD * sizeof(int); j++) {\n          const int index = (input_offset + thread_offset) * sizeof(int) + j;\n          if (index % K >= original_K) {\n            reinterpret_cast<uint8_t*>(regs_vec + i)[j] = 0;\n          }\n        }\n      }\n    }\n\n    // shuffle regs\n    regs_shuffle<LType>(regs_vec);\n\n// store, regs -> shared\n#pragma unroll\n    for (int i = 0; i < N_TILE_PER_TD; i++) {\n      /* TODO rotate i */\n      slm_v4i[(threadIdx.x * N_TILE_PER_TD + i) * SF_TILE_SIZE_I32 / 4 + threadIdx.y] =\n          reinterpret_cast<int4*>(regs_vec)[i];\n    }\n  }\n  __syncthreads();\n\n  // store, shared -> global\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  __align__(16) int4* output_v4i = reinterpret_cast<int4*>(output_i32);\n#pragma unroll\n  for (int i = linear_id; i < SF_TILE_SIZE_I32 * n_tiles_in_tb / 4; i += blockDim.x * blockDim.y) {\n    output_v4i[i] = slm_v4i[i];\n  }\n}\n\n// Dispatch helper: pick the right (IS_PADDED_K, IS_PADDED_M) row-scaling impl\n// specialization at runtime based on the per-block padding predicates. The\n// branching here is uniform across all threads in the block, so the indirect\n// path each block takes still inlines cleanly.\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ __forceinline__ void dispatch_swizzle_row_scaling_kernel_impl(\n    const void* input, void* output, const int M, const int K, const int original_M,\n    const int original_K, const int bid_x, const int bid_y, const int grid_dim_x,\n    const int grid_dim_y, const bool padding_k, const bool padding_m) {\n  if (padding_k && padding_m) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_k) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/true,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else if (padding_m) {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/true>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K, /*IS_PADDED_K=*/false,\n                                    /*IS_PADDED_M=*/false>(\n        input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_row_scaling_kernel(const void* input, void* output, const int M, const int K,\n                               const int original_M, const int original_K) {\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y,\n      padding_k, padding_m);\n}\n\n// Narrow-K specialization for row scaling swizzle.\n// When K is small (num_tiles_k < TB_DIM), the standard kernel wastes threadIdx.x\n// because there aren't enough K-tiles to distribute across threads.\n// This kernel repurposes the thread dimensions: threadIdx.x iterates rows within\n// an M-tile, threadIdx.y indexes M-tiles within the block, processing TB_DIM\n// M-tiles per block with full thread utilization.\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void swizzle_row_scaling_narrow_k_kernel_impl(const void* input, void* output,\n                                                         const int M, const int K,\n                                                         const int original_M, const int original_K,\n                                                         const int bid, const int grid_dim) {\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  const int K_i32 = K / 4;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n\n  const int m_tile = bid * blockDim.y + threadIdx.y;\n  const bool active = (m_tile < num_tiles_m);\n\n  extern __shared__ int4 slm_v4i[];\n  const int slm_tile_v4i = K_i32 * (SF_TILE_SIZE_I32 / 4);\n\n  if (active) {\n    const bool padding_m = (m_tile == num_tiles_m - 1) && (original_M < M);\n    const bool padding_k = (original_K < K);\n\n    int4* my_slm = slm_v4i + threadIdx.y * slm_tile_v4i;\n\n    for (int k = 0; k < K_i32; k++) {\n      const int input_base = m_tile * SF_TILE_DIM_M * K_i32 + k;\n      const int* input_i32 = reinterpret_cast<const int*>(input) + input_base;\n\n      int regs[N_SF_PER_TD_PER_TILE];\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        const int row = i * TB_DIM + threadIdx.x;\n        regs[i] = __ldg(input_i32 + row * K_i32);\n        if (padding_m || padding_k) {\n          for (int j = 0; j < 4; j++) {\n            const int byte_row = m_tile * SF_TILE_DIM_M + row;\n            const int byte_col = k * 4 + j;\n            if (byte_row >= original_M || byte_col >= original_K) {\n              reinterpret_cast<uint8_t*>(&regs[i])[j] = 0;\n            }\n          }\n        }\n      }\n\n      my_slm[k * (SF_TILE_SIZE_I32 / 4) + threadIdx.x] = *reinterpret_cast<int4*>(regs);\n    }\n  }\n\n  __syncthreads();\n\n  if (active) {\n    int4* my_slm = slm_v4i + threadIdx.y * slm_tile_v4i;\n    int4* out_v4i =\n        reinterpret_cast<int4*>(reinterpret_cast<int*>(output) + m_tile * SF_TILE_DIM_M * K_i32);\n\n    for (int i = threadIdx.x; i < slm_tile_v4i; i += blockDim.x) {\n      out_v4i[i] = my_slm[i];\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_row_scaling_narrow_k_kernel(const void* input, void* output, const int M, const int K,\n                                        const int original_M, const int original_K) {\n  swizzle_row_scaling_narrow_k_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, gridDim.x);\n}\n\n// Narrow-M variant of the column scaling swizzle kernel, for when num_tiles_m < TB_DIM.\n// Analogous to the narrow-K row kernel: when the M dimension is small, the normal\n// col kernel underutilizes threads in the load phase because threadIdx.x covers M\n// positions with vectorized loads, leaving many threads idle. This kernel repurposes\n// thread dimensions: threadIdx.y indexes K-tiles within the block, threadIdx.x covers\n// one int32 column of an M-tile, and M-tiles are iterated serially.\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void swizzle_col_scaling_narrow_m_kernel_impl(const void* input, void* output,\n                                                         const int M, const int K,\n                                                         const int original_M, const int original_K,\n                                                         const int bid, const int grid_dim) {\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n\n  const int k_tile = bid * blockDim.y + threadIdx.y;\n  const bool active = (k_tile < num_tiles_k);\n  const int remaining = num_tiles_k - bid * static_cast<int>(blockDim.y);\n  const int k_tiles_in_block = remaining <= 0 ? 0 : (remaining < TB_DIM ? remaining : TB_DIM);\n\n  extern __shared__ int slm_narrow_m[];\n\n  if (active) {\n    const bool padding_k = (k_tile == num_tiles_k - 1) && (original_K < K);\n    const int32_t* input_i32 = reinterpret_cast<const int32_t*>(input);\n\n    for (int m_tile = 0; m_tile < num_tiles_m; m_tile++) {\n      const bool padding_m = (m_tile == num_tiles_m - 1) && (original_M < M);\n\n      int regs[N_SF_PER_TD_PER_TILE];\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        const int k_row = k_tile * SF_TILE_DIM_K_I32 + i;\n        const int m_col = m_tile * SF_TILE_DIM_M_I32 + threadIdx.x;\n        regs[i] = __ldg(input_i32 + k_row * M_i32 + m_col);\n        if (padding_m || padding_k) {\n          for (int j = 0; j < 4; j++) {\n            if (m_col * 4 + j >= original_M || k_row >= original_K) {\n              reinterpret_cast<uint8_t*>(&regs[i])[j] = 0;\n            }\n          }\n        }\n      }\n\n      regs_shuffle_with_bit_shifts<int>(regs);\n\n      int tM = threadIdx.x * N_SF_PER_TD_PER_TILE;\n      int* slm_tile =\n          slm_narrow_m + m_tile * TB_DIM * SF_TILE_SIZE_I32 + threadIdx.y * SF_TILE_SIZE_I32;\n#pragma unroll\n      for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n        slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n                 ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32] = regs[i];\n      }\n    }\n  }\n\n  __syncthreads();\n\n  const int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  for (int m_tile = 0; m_tile < num_tiles_m; m_tile++) {\n    int4* out_v4i = reinterpret_cast<int4*>(reinterpret_cast<int*>(output) +\n                                            m_tile * SF_TILE_DIM_M_I32 * K_i32 +\n                                            bid * TB_DIM * SF_TILE_SIZE_I32);\n    int4* slm_v4i = reinterpret_cast<int4*>(slm_narrow_m + m_tile * TB_DIM * SF_TILE_SIZE_I32);\n    const int n_v4i = k_tiles_in_block * SF_TILE_SIZE_I32 / 4;\n    for (int j = linear_id; j < n_v4i; j += blockDim.x * blockDim.y) {\n      out_v4i[j] = slm_v4i[j];\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    swizzle_col_scaling_narrow_m_kernel(const void* input, void* output, const int M, const int K,\n                                        const int original_M, const int original_K) {\n  swizzle_col_scaling_narrow_m_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, blockIdx.x, gridDim.x);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void unswizzle_row_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                  const int K, const int bid_x, const int bid_y,\n                                                  const int grid_dim_x, const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M;\n\n  int n_tiles_in_tb = N_TILES_IN_TB;\n  const int K_i32 = K / 4;\n  if (bid_x == grid_dim_x - 1) {\n    n_tiles_in_tb = (K_i32 - 1) % N_TILES_IN_TB + 1;\n  }\n\n  const int input_offset =\n      bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB * SF_TILE_SIZE_I32;\n  const int* input_i32 = reinterpret_cast<const int*>(input) + input_offset;\n  const int output_offset = bid_y * SF_TILE_DIM_M_I32 * K_i32 + bid_x * N_TILES_IN_TB;\n  int* output_i32 = reinterpret_cast<int*>(output) + output_offset;\n\n  extern __shared__ int4 slm_v4i[];\n\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n  const int4* input_v4i = reinterpret_cast<const int4*>(input_i32);\n#pragma unroll\n  for (int i = linear_id; i < SF_TILE_SIZE_I32 * n_tiles_in_tb / 4; i += blockDim.x * blockDim.y) {\n    slm_v4i[i] = input_v4i[i];\n  }\n  __syncthreads();\n\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < n_tiles_in_tb) {\n#pragma unroll\n    for (int i = 0; i < N_TILE_PER_TD; i++) {\n      reinterpret_cast<int4*>(regs_vec)[i] =\n          slm_v4i[(threadIdx.x * N_TILE_PER_TD + i) * SF_TILE_SIZE_I32 / 4 + threadIdx.y];\n    }\n\n    regs_unshuffle<LType>(regs_vec);\n\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset = (i * TB_DIM + threadIdx.y) * K_i32 + threadIdx.x * N_TILE_PER_TD;\n      reinterpret_cast<LType*>(output_i32 + thread_offset)[0] = regs_vec[i];\n    }\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__device__ void unswizzle_col_scaling_kernel_impl(const void* input, void* output, const int M,\n                                                  const int K, const int bid_x, const int bid_y,\n                                                  const int grid_dim_x, const int grid_dim_y) {\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_SF_PER_TD = N_TILE_PER_TD * N_SF_PER_TD_PER_TILE;\n  constexpr int SF_TILE_SIZE_I32 = SF_TILE_DIM_M * SF_TILE_DIM_K / 4;\n\n  constexpr int SF_TILE_DIM_M_I32 = SF_TILE_DIM_M / 4;\n  constexpr int SF_TILE_DIM_K_I32 = SF_TILE_DIM_K;\n\n  const int M_i32 = M / 4;\n  const int K_i32 = K;\n\n  int m_tiles_in_tb = N_TILE_PER_TD;\n  int k_tiles_in_tb = TB_DIM;\n  if (bid_x == grid_dim_x - 1) {\n    k_tiles_in_tb = (K_i32 / SF_TILE_DIM_K_I32 - 1) % k_tiles_in_tb + 1;\n  }\n  if (bid_y == grid_dim_y - 1) {\n    m_tiles_in_tb = (M_i32 / SF_TILE_DIM_M_I32 - 1) % m_tiles_in_tb + 1;\n  }\n\n  const int32_t* input_i32[N_TILE_PER_TD];\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    input_i32[i] = reinterpret_cast<const int32_t*>(input) + bid_x * TB_DIM * SF_TILE_SIZE_I32 +\n                   (bid_y * N_TILE_PER_TD + i) * SF_TILE_DIM_M_I32 * K_i32;\n  }\n  const int output_offset =\n      bid_x * TB_DIM * SF_TILE_DIM_K_I32 * M_i32 + bid_y * N_TILE_PER_TD * SF_TILE_DIM_M_I32;\n  int* output_i32 = reinterpret_cast<int*>(output) + output_offset;\n\n  extern __shared__ int slm[];\n\n  int linear_id = threadIdx.y * blockDim.x + threadIdx.x;\n#pragma unroll\n  for (int i = 0; i < m_tiles_in_tb; i++) {\n    __align__(16) const int4* input_v4i = reinterpret_cast<const int4*>(input_i32[i]);\n    __align__(16) int4* slm_v4i =\n        reinterpret_cast<int4*>(slm + i * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int j = linear_id; j < SF_TILE_SIZE_I32 * k_tiles_in_tb / 4;\n         j += blockDim.x * blockDim.y) {\n      slm_v4i[j] = input_v4i[j];\n    }\n  }\n  __syncthreads();\n\n  LType regs_vec[N_SF_PER_TD_PER_TILE];\n  if (threadIdx.x * N_TILE_PER_TD < m_tiles_in_tb * SF_TILE_DIM_M_I32 &&\n      threadIdx.y < k_tiles_in_tb) {\n    int tM = threadIdx.x * N_SF_PER_TD;\n    int* slm_tile = slm + (threadIdx.y * SF_TILE_SIZE_I32 +\n                           tM / SF_TILE_DIM_M * k_tiles_in_tb * SF_TILE_SIZE_I32);\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD; i++) {\n      reinterpret_cast<int*>(regs_vec)[i] =\n          slm_tile[(tM % SF_TILE_DIM_M) / NEW_SF_TILE_DIM_M_I32 +\n                   ((tM + i) % NEW_SF_TILE_DIM_M_I32) * NEW_SF_TILE_DIM_K_I32];\n    }\n\n    regs_unshuffle_with_bit_shifts(regs_vec);\n\n#pragma unroll\n    for (int i = 0; i < N_SF_PER_TD_PER_TILE; i++) {\n      const int thread_offset =\n          (threadIdx.y * SF_TILE_DIM_K_I32 + i) * M_i32 + threadIdx.x * N_TILE_PER_TD;\n      reinterpret_cast<LType*>(output_i32 + thread_offset)[0] = regs_vec[i];\n    }\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    unswizzle_scaling_kernel(const void* input, void* output, const int M, const int K,\n                             const bool row_scaling) {\n  const int bid_x = blockIdx.x;\n  const int bid_y = blockIdx.y;\n  const int grid_dim_x = gridDim.x;\n  const int grid_dim_y = gridDim.y;\n  if (row_scaling) {\n    unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  } else {\n    unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n  }\n}\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\nstruct MultiSwizzleArgs {\n  // (input) Data buffers for input scaling factors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for swizzled scaling factors\n  void* output_list[kMaxTensorsPerKernel];\n  // Input scaling factor m\n  int m_list[kMaxTensorsPerKernel];\n  // Input scaling factor k\n  int k_list[kMaxTensorsPerKernel];\n  // Input scaling factor m before padding\n  int original_m_list[kMaxTensorsPerKernel];\n  // Input scaling factor k before padding\n  int original_k_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\nconstexpr size_t round_up_to_multiple(size_t value, size_t multiple) {\n  return DIVUP(value, multiple) * multiple;\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_row_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                     const int K, const int original_M,\n                                                     const int original_K,\n                                                     const size_t input_stride_bytes,\n                                                     const size_t output_stride_bytes) {\n  const int tensor_id = blockIdx.z;\n  // Input and output strides may differ: input is in the kernel-produced \"compact\"\n  // layout (per-tensor stride = original_M * padded_k * elem_size) when callers\n  // pass the unswizzled grouped scale buffer as-is, while the output is always in\n  // the per-tensor padded (\"swizzle-ready\") layout (padded_m * padded_k * elem_size).\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * input_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * output_stride_bytes;\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input_base, output_base, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x,\n      gridDim.y, padding_k, padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_col_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                     const int K, const int original_M,\n                                                     const int original_K,\n                                                     const size_t input_stride_bytes,\n                                                     const size_t output_stride_bytes) {\n  const int tensor_id = blockIdx.z;\n  // See the rowwise kernel for stride semantics. For columnwise the per-tensor\n  // compact stride is DIVUP(original_K, 1) * padded_m * elem_size (i.e. the\n  // unpadded scale-row count in the K direction times the padded M extent).\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * input_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * output_stride_bytes;\n  const bool padding_m = (blockIdx.y == gridDim.y - 1) && (original_M < M);\n  const bool padding_k = (blockIdx.x == gridDim.x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input_base, output_base, M, K, original_M, original_K, blockIdx.x, blockIdx.y, gridDim.x,\n      gridDim.y, padding_k, padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_unswizzle_scaling_uniform_shape_kernel(const void* input, void* output, const int M,\n                                                   const int K, const size_t scale_stride_bytes,\n                                                   const bool row_scaling) {\n  const int tensor_id = blockIdx.z;\n  const uint8_t* input_base =\n      reinterpret_cast<const uint8_t*>(input) + tensor_id * scale_stride_bytes;\n  uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + tensor_id * scale_stride_bytes;\n  if (row_scaling) {\n    unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input_base, output_base, M, K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y);\n  } else {\n    unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        input_base, output_base, M, K, blockIdx.x, blockIdx.y, gridDim.x, gridDim.y);\n  }\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_unswizzle_row_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int flat_offset = bid - kernel_args.block_range[tensor_id];\n  const int grid_dim_x = DIVUP(num_tiles_k, N_TILES_IN_TB);\n  const int grid_dim_y = num_tiles_m;\n  const int bid_x = flat_offset / grid_dim_y;\n  const int bid_y = flat_offset % grid_dim_y;\n\n  unswizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_unswizzle_col_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int flat_offset = bid - kernel_args.block_range[tensor_id];\n  const int grid_dim_x = DIVUP(num_tiles_k, TB_DIM);\n  const int grid_dim_y = DIVUP(num_tiles_m, N_TILE_PER_TD);\n  const int bid_x = flat_offset / grid_dim_y;\n  const int bid_y = flat_offset % grid_dim_y;\n\n  unswizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, bid_x, bid_y, grid_dim_x, grid_dim_y);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_swizzle_row_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  // Find tensor corresponding to block\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  // Get args corresponding to block\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n  constexpr int N_TILES_IN_TB = TB_DIM * N_TILE_PER_TD;\n\n  // Get block index in grid. Emulate 2D grid.\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim_x = DIVUP(num_tiles_k, N_TILES_IN_TB);\n  const int grid_dim_y = num_tiles_m;\n  const int bid_x = (bid - kernel_args.block_range[tensor_id]) / grid_dim_y;\n  const int bid_y = (bid - kernel_args.block_range[tensor_id]) % grid_dim_y;\n\n  const bool padding_m = (bid_y == grid_dim_y - 1) && (original_M < M);\n  const bool padding_k = (bid_x == grid_dim_x - 1) && (original_K < K);\n  dispatch_swizzle_row_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y, padding_k,\n      padding_m);\n}\n\ntemplate <typename LType, int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void multi_tensor_swizzle_col_scaling_kernel(MultiSwizzleArgs kernel_args) {\n  // Find tensor corresponding to block\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  // Get args corresponding to block\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n\n  constexpr int N_TILE_PER_TD = sizeof(LType) / sizeof(int);\n\n  // Get block index in grid. Emulate 2D grid.\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim_x = DIVUP(num_tiles_k, TB_DIM);\n  const int grid_dim_y = DIVUP(num_tiles_m, N_TILE_PER_TD);\n  const int bid_x = (bid - kernel_args.block_range[tensor_id]) / grid_dim_y;\n  const int bid_y = (bid - kernel_args.block_range[tensor_id]) % grid_dim_y;\n\n  const bool padding_m = (bid_y == grid_dim_y - 1) && (original_M < M);\n  const bool padding_k = (bid_x == grid_dim_x - 1) && (original_K < K);\n  dispatch_swizzle_col_scaling_kernel_impl<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, bid_x, bid_y, grid_dim_x, grid_dim_y, padding_k,\n      padding_m);\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    multi_tensor_swizzle_row_scaling_narrow_k_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n  const int flat_bid = bid - kernel_args.block_range[tensor_id];\n  const int num_tiles_m = M / SF_TILE_DIM_M;\n  const int grid_dim = DIVUP(num_tiles_m, TB_DIM);\n\n  swizzle_row_scaling_narrow_k_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, flat_bid, grid_dim);\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    multi_tensor_swizzle_col_scaling_narrow_m_kernel(MultiSwizzleArgs kernel_args) {\n  const int bid = blockIdx.x;\n  int tensor_id = 0;\n  while (kernel_args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const void* input = kernel_args.input_list[tensor_id];\n  void* output = kernel_args.output_list[tensor_id];\n  const int M = kernel_args.m_list[tensor_id];\n  const int K = kernel_args.k_list[tensor_id];\n  const int original_M = kernel_args.original_m_list[tensor_id];\n  const int original_K = kernel_args.original_k_list[tensor_id];\n  const int flat_bid = bid - kernel_args.block_range[tensor_id];\n  const int num_tiles_k = K / SF_TILE_DIM_K;\n  const int grid_dim = DIVUP(num_tiles_k, TB_DIM);\n\n  swizzle_col_scaling_narrow_m_kernel_impl<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n      input, output, M, K, original_M, original_K, flat_bid, grid_dim);\n}\n\n}  // namespace\n\nvoid swizzle_scaling_factors(const Tensor* input, Tensor* output, hipStream_t stream) {\n  // Check scaling mode\n  const auto& scaling_mode = input->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_MXFP8_1D_SCALING || scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Input tensor has invalid scaling mode (\", to_string(input->scaling_mode), \").\");\n\n  // Check tensors\n  CheckInputTensor(*input, \"scaling_factor_input\");\n  CheckInputTensor(*output, \"scaling_factor_output\");\n  NVTE_CHECK(!input->with_gemm_swizzled_scales,\n             \"Expected input tensor with scales in compact format.\");\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output tensor with scales in GEMM swizzled format.\");\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING:\n      NVTE_CHECK(is_fp8_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP8, got \",\n                 to_string(input->dtype()), \").\");\n      break;\n    case NVTE_NVFP4_1D_SCALING:\n      NVTE_CHECK(is_fp4_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP4, got \",\n                 to_string(input->dtype()), \").\");\n      break;\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  // Check if scaling factors are non-trivial\n  const bool has_rowwise_scale_inv = input->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = input->columnwise_scale_inv.has_data();\n  NVTE_CHECK(!has_rowwise_scale_inv || !has_columnwise_scale_inv,\n             \"Input tensor has both row-wise and column-wise scaling factors\");\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  // Deduce tensor dims\n  int m{0}, k{0};\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(input->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->scale_inv.shape, \".\");\n        m = input->scale_inv.shape[0];\n        k = input->scale_inv.shape[1];\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(input->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->columnwise_scale_inv.shape,\n                   \".\");\n        m = input->columnwise_scale_inv.shape[1];\n        k = input->columnwise_scale_inv.shape[0];\n      }\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(input->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->scale_inv.shape, \".\");\n        m = input->scale_inv.shape[0];\n        k = input->scale_inv.shape[1];\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(input->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", input->columnwise_scale_inv.shape,\n                   \".\");\n        m = input->columnwise_scale_inv.shape[0];\n        k = input->columnwise_scale_inv.shape[1];\n      }\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  // Check dims\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n  NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n\n  // Check that output tensor matches input tensor\n  if (has_rowwise_scale_inv) {\n    NVTE_CHECK(output->scale_inv.has_data(),\n               \"Output tensor does not have row-wise scaling factors.\");\n    NVTE_CHECK(m * k == output->scale_inv.numel(), \"Expected output tensor to have \", m * k,\n               \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n  }\n  if (has_columnwise_scale_inv) {\n    NVTE_CHECK(output->columnwise_scale_inv.has_data(),\n               \"Output tensor does not have column-wise scaling factors.\");\n    NVTE_CHECK(\n        m * k == output->columnwise_scale_inv.numel(), \"Expected output tensor to have \", m * k,\n        \" column-wise scaling factors, but got shape=\", output->columnwise_scale_inv.shape, \".\");\n  }\n\n  // Choose swizzle implementation\n  bool rowwise_swizzle{false}, columnwise_swizzle{false};\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      rowwise_swizzle = has_rowwise_scale_inv;\n      columnwise_swizzle = has_columnwise_scale_inv;\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      // NVFP4 column-wise data is transposed, so row-wise and\n      // column-wise scales have same swizzling format\n      rowwise_swizzle = true;\n      columnwise_swizzle = false;\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  const dim3 block_size(TB_DIM, TB_DIM);\n  const int num_tiles_m = m / SF_TILE_DIM_M;\n  const int num_tiles_k = k / SF_TILE_DIM_K;\n\n  // Perform row-wise swizzle\n  if (rowwise_swizzle) {\n    int original_M{0}, original_K{0};\n    void *input_scale_inv_ptr{nullptr}, *output_scale_inv_ptr{nullptr};\n    switch (scaling_mode) {\n      case NVTE_MXFP8_1D_SCALING: {\n        original_M = input->flat_first_dim();\n        original_K = input->flat_last_dim() / MXFP8_BLOCK_SIZE;\n        input_scale_inv_ptr = input->scale_inv.dptr;\n        output_scale_inv_ptr = output->scale_inv.dptr;\n        break;\n      }\n      case NVTE_NVFP4_1D_SCALING: {\n        if (has_rowwise_scale_inv) {\n          original_M = input->flat_first_dim();\n          original_K = input->flat_last_dim() / NVFP4_BLOCK_SIZE;\n          input_scale_inv_ptr = input->scale_inv.dptr;\n          output_scale_inv_ptr = output->scale_inv.dptr;\n        } else if (has_columnwise_scale_inv) {\n          original_M = input->flat_last_dim();\n          original_K = input->flat_first_dim() / NVFP4_BLOCK_SIZE;\n          input_scale_inv_ptr = input->columnwise_scale_inv.dptr;\n          output_scale_inv_ptr = output->columnwise_scale_inv.dptr;\n        }\n        break;\n      }\n      default:\n        NVTE_ERROR(\"Invalid scaling mode\");\n    }\n\n    const int narrow_k_slm_size =\n        TB_DIM * num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n    if (num_tiles_k < TB_DIM && narrow_k_slm_size <= get_max_dynamic_smem()) {\n      // Narrow-K: batch TB_DIM M-tiles per block, fully utilizing all threads.\n      dim3 num_blocks_narrow(DIVUP(num_tiles_m, TB_DIM));\n      NVTE_CHECK_CUDA(\n          hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                               hipFuncAttributeMaxDynamicSharedMemorySize, narrow_k_slm_size));\n      swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks_narrow, block_size, narrow_k_slm_size, stream>>>(\n              input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n    } else {\n      int vec_load_size = (num_tiles_k - 1) % 4 + 1;\n      /* there is no int3 and misaligned if using int4/int2 */\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n      dim3 num_blocks(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m);\n      int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_scale_inv_ptr, output_scale_inv_ptr, m, k, original_M, original_K);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n\n  // Perform column-wise swizzle\n  if (columnwise_swizzle) {\n    const int original_M = input->flat_last_dim();\n    const int original_K = input->flat_first_dim() / MXFP8_BLOCK_SIZE;\n\n    const int narrow_m_slm_size =\n        TB_DIM * num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n    if (num_tiles_m < TB_DIM && narrow_m_slm_size <= get_max_dynamic_smem()) {\n      // Narrow-M: batch TB_DIM K-tiles per block, fully utilizing all threads.\n      dim3 num_blocks_narrow(DIVUP(num_tiles_k, TB_DIM));\n      NVTE_CHECK_CUDA(\n          hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                               hipFuncAttributeMaxDynamicSharedMemorySize, narrow_m_slm_size));\n      swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks_narrow, block_size, narrow_m_slm_size, stream>>>(\n              input->columnwise_scale_inv.dptr, output->columnwise_scale_inv.dptr, m, k, original_M,\n              original_K);\n    } else {\n      int vec_load_size = (num_tiles_m - 1) % 4 + 1;\n      if (vec_load_size == 3) vec_load_size = 1; /* no int3 and misaligned if using int4/int2 */\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n      dim3 num_blocks(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size));\n      int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(\n              hipFuncSetAttribute(reinterpret_cast<const void*>(swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                   hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(input->columnwise_scale_inv.dptr,\n                                                             output->columnwise_scale_inv.dptr, m,\n                                                             k, original_M, original_K);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nvoid launch_multi_tensor_swizzle_scaling_factors(MultiSwizzleArgs& kernel_args,\n                                                 const int vec_load_size, const bool is_rowwise,\n                                                 const bool use_narrow_k, const bool use_narrow_m,\n                                                 hipStream_t stream) {\n  // cudaFuncSetAttribute is a host-synchronous driver call; cache the max shared memory\n  // setting per kernel variant so we only pay the cost when slm_size actually increases.\n  auto set_smem_if_needed = [](auto kernel_fn, int slm, int& cached) {\n    if (cached < slm) {\n      NVTE_CHECK_CUDA(\n          hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_fn), hipFuncAttributeMaxDynamicSharedMemorySize, slm));\n      cached = slm;\n    }\n  };\n\n  dim3 block_size(TB_DIM, TB_DIM);\n\n  if (is_rowwise && use_narrow_k) {\n    // Narrow-K path: each block handles TB_DIM M-tiles with full thread utilization.\n    // slm_size depends on num_tiles_k, which can vary per tensor \u2014 use the max.\n    int max_num_tiles_k = 0;\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int num_tiles_m = kernel_args.m_list[j] / SF_TILE_DIM_M;\n      const int num_tiles_k = kernel_args.k_list[j] / SF_TILE_DIM_K;\n      max_num_tiles_k = std::max(max_num_tiles_k, num_tiles_k);\n      kernel_args.block_range[j + 1] = kernel_args.block_range[j] + DIVUP(num_tiles_m, TB_DIM);\n    }\n    int slm_size = TB_DIM * max_num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_narrow_k = -1;\n    set_smem_if_needed(\n        multi_tensor_swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n        cached_narrow_k);\n    multi_tensor_swizzle_row_scaling_narrow_k_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n        <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n  } else if (!is_rowwise && use_narrow_m) {\n    // Narrow-M path: each block handles TB_DIM K-tiles with full thread utilization.\n    // slm_size depends on num_tiles_m, which can vary per tensor \u2014 use the max.\n    int max_num_tiles_m = 0;\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int num_tiles_m = kernel_args.m_list[j] / SF_TILE_DIM_M;\n      const int num_tiles_k = kernel_args.k_list[j] / SF_TILE_DIM_K;\n      max_num_tiles_m = std::max(max_num_tiles_m, num_tiles_m);\n      kernel_args.block_range[j + 1] = kernel_args.block_range[j] + DIVUP(num_tiles_k, TB_DIM);\n    }\n    int slm_size = TB_DIM * max_num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_narrow_m = -1;\n    set_smem_if_needed(\n        multi_tensor_swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n        cached_narrow_m);\n    multi_tensor_swizzle_col_scaling_narrow_m_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n        <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n  } else {\n    int n_tiles_in_tb = TB_DIM * vec_load_size;\n    int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    /* Calculate number of CUDA blocks needed for each tensor.\n    * We have to do it here because we have to iterate over all tensors in this batch to\n    * get the minimum vec_load_size.\n    */\n    for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n      const int m = kernel_args.m_list[j];\n      const int k = kernel_args.k_list[j];\n      int num_tiles_m = m / SF_TILE_DIM_M;\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      if (is_rowwise) {\n        kernel_args.block_range[j + 1] =\n            kernel_args.block_range[j] + DIVUP(num_tiles_k, n_tiles_in_tb) * num_tiles_m;\n      } else {\n        kernel_args.block_range[j + 1] =\n            kernel_args.block_range[j] +\n            DIVUP(num_tiles_k, TB_DIM) * DIVUP(num_tiles_m, vec_load_size);\n      }\n    }\n    const int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n\n    static int cached_row_int4 = -1, cached_row_int2 = -1, cached_row_int1 = -1;\n    static int cached_col_int4 = -1, cached_col_int2 = -1, cached_col_int1 = -1;\n\n    if (is_rowwise) {\n      switch (vec_load_size) {\n        case 4:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int4);\n          multi_tensor_swizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int2);\n          multi_tensor_swizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          set_smem_if_needed(\n              multi_tensor_swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_row_int1);\n          multi_tensor_swizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    } else {\n      switch (vec_load_size) {\n        case 4:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int4);\n          multi_tensor_swizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int2);\n          multi_tensor_swizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          set_smem_if_needed(\n              multi_tensor_swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>, slm_size,\n              cached_col_int1);\n          multi_tensor_swizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n  }\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nvoid launch_multi_tensor_unswizzle_scaling_factors(MultiSwizzleArgs& kernel_args,\n                                                   const int vec_load_size, const bool is_rowwise,\n                                                   hipStream_t stream) {\n  int n_tiles_in_tb = TB_DIM * vec_load_size;\n  int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n  for (size_t j = 0; j < kernel_args.num_tensors; j++) {\n    const int m = kernel_args.m_list[j];\n    const int k = kernel_args.k_list[j];\n    int num_tiles_m = m / SF_TILE_DIM_M;\n    int num_tiles_k = k / SF_TILE_DIM_K;\n    if (is_rowwise) {\n      kernel_args.block_range[j + 1] =\n          kernel_args.block_range[j] + DIVUP(num_tiles_k, n_tiles_in_tb) * num_tiles_m;\n    } else {\n      kernel_args.block_range[j + 1] =\n          kernel_args.block_range[j] +\n          DIVUP(num_tiles_k, TB_DIM) * DIVUP(num_tiles_m, vec_load_size);\n    }\n  }\n\n  int num_blocks = kernel_args.block_range[kernel_args.num_tensors];\n  if (num_blocks > 0) {\n    dim3 block_size(TB_DIM, TB_DIM);\n    if (is_rowwise) {\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_row_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    } else {\n      switch (vec_load_size) {\n        case 4:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 2:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        case 1:\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(multi_tensor_unswizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          multi_tensor_unswizzle_col_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(kernel_args);\n          break;\n        default:\n          NVTE_ERROR(\"Not valid vec_load_size.\");\n          break;\n      }\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid multi_tensor_swizzle_scaling_factors(const std::vector<Tensor*>& input,\n                                          std::vector<Tensor*>& output, hipStream_t stream,\n                                          bool check_scale_inv_shapes) {\n  auto num_tensors = input.size();\n  bool all_has_data = true;\n  bool all_has_columnwise_data = true;\n  bool all_nvfp4 = true;\n  for (size_t i = 0; i < num_tensors; i++) {\n    auto scaling_mode = input[i]->scaling_mode;\n    auto is_fp8 = is_fp8_dtype(input[i]->dtype());\n    auto is_fp4 = is_fp4_dtype(input[i]->dtype());\n    NVTE_CHECK(\n        (is_fp8 && is_mxfp8_scaling(scaling_mode)) || (is_fp4 && is_nvfp4_scaling(scaling_mode)),\n        \"Not implemented scaling mode \" + to_string(scaling_mode) + \".\");\n    NVTE_CHECK(!input[i]->with_gemm_swizzled_scales,\n               \"Expected input tensors with scales in compact format.\");\n    NVTE_CHECK(output[i]->with_gemm_swizzled_scales,\n               \"Expected output tensors with scales in GEMM swizzled format.\");\n\n    // We don't allow empty tensors. They should be filtered out before calling this function.\n    NVTE_CHECK(input[i]->numel() != 0, \"Tensor input[\", i, \"] is empty.\");\n    CheckInputTensor(*input[i], \"scaling_factor_input[\" + std::to_string(i) + \"]\",\n                     check_scale_inv_shapes);\n    CheckInputTensor(*output[i], \"scaling_factor_output[\" + std::to_string(i) + \"]\",\n                     check_scale_inv_shapes);\n    all_has_data = all_has_data && input[i]->scale_inv.has_data();\n    all_has_columnwise_data =\n        (all_has_columnwise_data && input[i]->columnwise_scale_inv.has_data());\n    all_nvfp4 = all_nvfp4 && is_nvfp4_scaling(scaling_mode);\n  }\n  NVTE_CHECK(all_has_data || all_has_columnwise_data,\n             \"All tensors should have data or columnwise data.\");\n  NVTE_CHECK(!all_has_data || !all_has_columnwise_data,\n             \"All tensors have both data and columnwise data.\");\n\n  const bool rowwise_swizzle = all_has_data || all_nvfp4;\n  const bool columnwise_swizzle = all_has_columnwise_data && !all_nvfp4;\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  if (rowwise_swizzle) {\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    bool all_narrow_k = true;\n    for (size_t i = 0; i < num_tensors; i++) {\n      //Launch kernel if argument struct is full\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        // There is no int3 and misaligned if using int4/int2.\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, true, all_narrow_k, false, stream);\n        // Reset the argument struct and vec_load_size\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n        all_narrow_k = true;\n      }\n\n      int m, k;\n\n      if (all_has_data) {\n        m = input[i]->scale_inv.shape[0];\n        k = input[i]->scale_inv.shape[1];\n      } else {\n        NVTE_CHECK(all_nvfp4, \"When doing rowwise swizzle with rowwise data, it has to be NVFP4\");\n        m = input[i]->columnwise_scale_inv.shape[0];\n        k = input[i]->columnwise_scale_inv.shape[1];\n      }\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Input scale inverse should be 2D!\");\n\n      if (all_has_data) {\n        NVTE_CHECK(output[i]->scale_inv.has_data(), \"Output tensor \", i,\n                   \" does not have row-wise scaling factors.\");\n        NVTE_CHECK(m * k == output[i]->scale_inv.numel(), \"Expected output tensor \", i, \" to have \",\n                   m * k, \" row-wise scaling factors, but got shape=\", output[i]->scale_inv.shape,\n                   \".\");\n      }\n      if (all_has_columnwise_data) {\n        NVTE_CHECK(output[i]->columnwise_scale_inv.has_data(), \"Output tensor \", i,\n                   \" does not have column-wise scaling factors.\");\n        NVTE_CHECK(m * k == output[i]->columnwise_scale_inv.numel(), \"Expected output tensor \", i,\n                   \" to have \", m * k, \" column-wise scaling factors, but got shape=\",\n                   output[i]->columnwise_scale_inv.shape, \".\");\n      }\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      const int narrow_k_slm =\n          TB_DIM * num_tiles_k * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n      all_narrow_k =\n          all_narrow_k && (num_tiles_k < TB_DIM) && (narrow_k_slm <= get_max_dynamic_smem());\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      // We use the minimum vec_load_size across all tensors.\n      // TODO(zhongbo): fix vec_load_size for NVFP4\n      // Current unit test won't capture this issue, but in E2E\n      // using vec_load_size = 1 other than 1 will lead to mis-aligned\n      // address error in MOE training\n      vec_load_size = all_nvfp4 ? 1 : std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      if (!all_nvfp4 || all_has_data) {\n        int block_scale_size = all_nvfp4 ? NVFP4_BLOCK_SIZE : MXFP8_BLOCK_SIZE;\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->scale_inv.dptr;\n        kernel_args.original_m_list[pos] = input[i]->flat_first_dim();\n        kernel_args.original_k_list[pos] = input[i]->flat_last_dim() / block_scale_size;\n      } else {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n        kernel_args.original_m_list[pos] = input[i]->flat_last_dim();\n        kernel_args.original_k_list[pos] = input[i]->flat_first_dim() / NVFP4_BLOCK_SIZE;\n      }\n      kernel_args.num_tensors++;\n    }\n    // Launch the remaining tensors\n    // There is no int3 and misaligned if using int4/int2.\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, true, all_narrow_k, false, stream);\n  }\n\n  if (columnwise_swizzle) {\n    // NVFP4 shouldn't end up here because it only needs rowwise swizzle\n    NVTE_CHECK(!all_nvfp4, \"NVFP4 shouldn't end up here because it only needs rowwise swizzle\");\n\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    bool all_narrow_m = true;\n    for (size_t i = 0; i < num_tensors; i++) {\n      //Launch kernel if argument struct is full\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        // There is no int3 and misaligned if using int4/int2.\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, false, false, all_narrow_m, stream);\n        // Reset the argument struct and vec_load_size\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n        all_narrow_m = true;\n      }\n      const int m = input[i]->columnwise_scale_inv.shape[1];\n      const int k = input[i]->columnwise_scale_inv.shape[0];\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Input should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Input should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Input scale inverse should be 2D!\");\n      NVTE_CHECK(m * k == std::accumulate(output[i]->columnwise_scale_inv.shape.begin(),\n                                          output[i]->columnwise_scale_inv.shape.end(), 1,\n                                          std::multiplies<int>()),\n                 \"Input.columnwise_scale_inv size is not equal to \"\n                 \"Output.columnwise_scale_inv size!\");\n\n      int num_tiles_m = m / SF_TILE_DIM_M;\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      const int narrow_m_slm =\n          TB_DIM * num_tiles_m * SF_TILE_DIM_M * SF_TILE_DIM_K * static_cast<int>(sizeof(int8_t));\n      all_narrow_m =\n          all_narrow_m && (num_tiles_m < TB_DIM) && (narrow_m_slm <= get_max_dynamic_smem());\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      // We use the minimum vec_load_size across all tensors.\n      vec_load_size = std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n      kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      kernel_args.original_m_list[pos] = input[i]->flat_last_dim();\n      kernel_args.original_k_list[pos] = input[i]->flat_first_dim() / MXFP8_BLOCK_SIZE;\n      kernel_args.num_tensors++;\n    }\n    // Launch the remaining tensors\n    // There is no int3 and misaligned if using int4/int2.\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_swizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, false, false, all_narrow_m, stream);\n  }\n}\n\nvoid unswizzle_scaling_factors(const Tensor* input, Tensor* output, hipStream_t stream) {\n  const auto& scaling_mode = output->scaling_mode;\n  NVTE_CHECK(scaling_mode == NVTE_MXFP8_1D_SCALING || scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Output tensor has invalid scaling mode (\", to_string(output->scaling_mode), \").\");\n\n  CheckInputTensor(*input, \"scaling_factor_input\");\n  CheckInputTensor(*output, \"scaling_factor_output\");\n  NVTE_CHECK(input->with_gemm_swizzled_scales, \"Expected input tensor with swizzled scales.\");\n  NVTE_CHECK(!output->with_gemm_swizzled_scales,\n             \"Expected output tensor in row-major compact format.\");\n  NVTE_CHECK(input->scaling_mode == scaling_mode,\n             \"Input and output tensors must have matching scaling modes, but got \",\n             to_string(input->scaling_mode), \" and \", to_string(output->scaling_mode), \".\");\n\n  const bool has_rowwise_scale_inv = output->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = output->columnwise_scale_inv.has_data();\n  NVTE_CHECK(!has_rowwise_scale_inv || !has_columnwise_scale_inv,\n             \"Output tensor has both row-wise and column-wise scaling factors\");\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n  if (has_rowwise_scale_inv) {\n    NVTE_CHECK(input->scale_inv.has_data(),\n               \"Output tensor requests row-wise scaling factors, but input tensor does not \"\n               \"provide them.\");\n  } else if (has_columnwise_scale_inv) {\n    NVTE_CHECK(input->columnwise_scale_inv.has_data(),\n               \"Output tensor requests column-wise scaling factors, but input tensor does not \"\n               \"provide them.\");\n  }\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  const dim3 block_size(TB_DIM, TB_DIM);\n\n  int m{0}, k{0};\n  void* input_ptr{nullptr};\n  void* output_ptr{nullptr};\n  bool rowwise{false};\n\n  switch (scaling_mode) {\n    case NVTE_MXFP8_1D_SCALING: {\n      NVTE_CHECK(is_fp8_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP8, got \",\n                 to_string(input->dtype()), \").\");\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(output->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->scale_inv.shape, \".\");\n        m = output->scale_inv.shape[0];\n        k = output->scale_inv.shape[1];\n        NVTE_CHECK(static_cast<size_t>(m) * k == input->scale_inv.numel(),\n                   \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", input->scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n        input_ptr = input->scale_inv.dptr;\n        output_ptr = output->scale_inv.dptr;\n        rowwise = true;\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(output->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->columnwise_scale_inv.shape,\n                   \".\");\n        m = output->columnwise_scale_inv.shape[1];\n        k = output->columnwise_scale_inv.shape[0];\n        NVTE_CHECK(\n            static_cast<size_t>(m) * k == input->columnwise_scale_inv.numel(),\n            \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n            \" column-wise scaling factors, but got shape=\", input->columnwise_scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->columnwise_scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" column-wise scaling factors, but got shape=\",\n                   output->columnwise_scale_inv.shape, \".\");\n        input_ptr = input->columnwise_scale_inv.dptr;\n        output_ptr = output->columnwise_scale_inv.dptr;\n        rowwise = false;\n      }\n      break;\n    }\n    case NVTE_NVFP4_1D_SCALING: {\n      NVTE_CHECK(is_fp4_dtype(input->dtype()), \"Input tensor has invalid dtype (expected FP4, got \",\n                 to_string(input->dtype()), \").\");\n      // NVFP4: always unswizzle rowwise regardless of which scale buffer holds the data\n      if (has_rowwise_scale_inv) {\n        NVTE_CHECK(output->scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->scale_inv.shape, \".\");\n        m = output->scale_inv.shape[0];\n        k = output->scale_inv.shape[1];\n        // Example for NVFP4 rowwise path:\n        NVTE_CHECK(static_cast<size_t>(m) * k == input->scale_inv.numel(),\n                   \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", input->scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" row-wise scaling factors, but got shape=\", output->scale_inv.shape, \".\");\n        input_ptr = input->scale_inv.dptr;\n        output_ptr = output->scale_inv.dptr;\n      } else if (has_columnwise_scale_inv) {\n        NVTE_CHECK(output->columnwise_scale_inv.shape.size() == 2,\n                   \"Expected 2D scaling factors, got shape=\", output->columnwise_scale_inv.shape,\n                   \".\");\n        m = output->columnwise_scale_inv.shape[0];\n        k = output->columnwise_scale_inv.shape[1];\n        NVTE_CHECK(\n            static_cast<size_t>(m) * k == input->columnwise_scale_inv.numel(),\n            \"Expected input tensor to have \", static_cast<size_t>(m) * k,\n            \" column-wise scaling factors, but got shape=\", input->columnwise_scale_inv.shape, \".\");\n        NVTE_CHECK(static_cast<size_t>(m) * k == output->columnwise_scale_inv.numel(),\n                   \"Expected output tensor to have \", static_cast<size_t>(m) * k,\n                   \" column-wise scaling factors, but got shape=\",\n                   output->columnwise_scale_inv.shape, \".\");\n        input_ptr = input->columnwise_scale_inv.dptr;\n        output_ptr = output->columnwise_scale_inv.dptr;\n      }\n      rowwise = true;\n      break;\n    }\n    default:\n      NVTE_ERROR(\"Invalid scaling mode\");\n  }\n\n  NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n  NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n\n  const int num_tiles_m = m / SF_TILE_DIM_M;\n  const int num_tiles_k = k / SF_TILE_DIM_K;\n\n  auto launch_unswizzle = [&](int vec_load_size, const dim3& num_blocks, int slm_size) {\n    switch (vec_load_size) {\n      case 4:\n        NVTE_CHECK_CUDA(\n            hipFuncSetAttribute(reinterpret_cast<const void*>(unswizzle_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                 hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      case 2:\n        NVTE_CHECK_CUDA(\n            hipFuncSetAttribute(reinterpret_cast<const void*>(unswizzle_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                 hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      case 1:\n        NVTE_CHECK_CUDA(\n            hipFuncSetAttribute(reinterpret_cast<const void*>(unswizzle_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n                                 hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n        unswizzle_scaling_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>\n            <<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, m, k, rowwise);\n        break;\n      default:\n        NVTE_ERROR(\"Not valid vec_load_size.\");\n    }\n    NVTE_CHECK_CUDA(hipGetLastError());\n  };\n\n  int vec_load_size = rowwise ? (num_tiles_k - 1) % 4 + 1 : (num_tiles_m - 1) % 4 + 1;\n  if (vec_load_size == 3) vec_load_size = 1;\n  int n_tiles_in_tb = TB_DIM * vec_load_size;\n  dim3 num_blocks = rowwise ? dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m)\n                            : dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size));\n  int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n  launch_unswizzle(vec_load_size, num_blocks, slm_size);\n}\n\nvoid multi_tensor_unswizzle_scaling_factors(const std::vector<Tensor*>& input,\n                                            std::vector<Tensor*>& output, hipStream_t stream) {\n  size_t num_tensors = output.size();\n  const auto& first_scaling_mode = output[0]->scaling_mode;\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n\n  bool all_has_data = true;\n  bool all_has_columnwise_data = true;\n  bool all_nvfp4 = true;\n  for (size_t i = 0; i < num_tensors; i++) {\n    const auto scaling_mode = output[i]->scaling_mode;\n    const auto is_fp8 = is_fp8_dtype(input[i]->dtype());\n    const auto is_fp4 = is_fp4_dtype(input[i]->dtype());\n\n    NVTE_CHECK(scaling_mode == first_scaling_mode,\n               \"All tensors should have the same scaling mode in multi-tensor unswizzle.\");\n    NVTE_CHECK(\n        (is_fp8 && is_mxfp8_scaling(scaling_mode)) || (is_fp4 && is_nvfp4_scaling(scaling_mode)),\n        \"Not implemented scaling mode \" + to_string(scaling_mode) + \".\");\n    NVTE_CHECK(input[i]->with_gemm_swizzled_scales,\n               \"Expected input tensors with scales in GEMM swizzled format.\");\n    NVTE_CHECK(!output[i]->with_gemm_swizzled_scales,\n               \"Expected output tensors with scales in compact format.\");\n    NVTE_CHECK(input[i]->numel() != 0, \"Tensor input[\", i, \"] is empty.\");\n    CheckInputTensor(*input[i], \"scaling_factor_input[\" + std::to_string(i) + \"]\");\n    CheckInputTensor(*output[i], \"scaling_factor_output[\" + std::to_string(i) + \"]\");\n\n    all_has_data = all_has_data && output[i]->scale_inv.has_data();\n    all_has_columnwise_data =\n        (all_has_columnwise_data && output[i]->columnwise_scale_inv.has_data());\n    all_nvfp4 = all_nvfp4 && is_nvfp4_scaling(scaling_mode);\n  }\n  NVTE_CHECK(all_has_data || all_has_columnwise_data,\n             \"All tensors should have data or columnwise data.\");\n  NVTE_CHECK(!all_has_data || !all_has_columnwise_data,\n             \"All tensors have both data and columnwise data.\");\n\n  const bool rowwise_unswizzle = all_has_data || all_nvfp4;\n  const bool columnwise_unswizzle = all_has_columnwise_data && !all_nvfp4;\n\n  if (rowwise_unswizzle) {\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    for (size_t i = 0; i < num_tensors; i++) {\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, true, stream);\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n      }\n      int m, k;\n      if (all_has_data) {\n        NVTE_CHECK(input[i]->scale_inv.has_data(), \"Input tensor \", i,\n                   \" does not have row-wise scaling factors.\");\n        NVTE_CHECK(output[i]->scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                   \" to have \", \"2D scaling factors, got shape=\", output[i]->scale_inv.shape, \".\");\n        m = output[i]->scale_inv.shape[0];\n        k = output[i]->scale_inv.shape[1];\n        NVTE_CHECK(m * k == input[i]->scale_inv.numel(), \"Expected input tensor \", i, \" to have \",\n                   m * k, \" row-wise scaling factors, but got shape=\", input[i]->scale_inv.shape,\n                   \".\");\n      }\n\n      if (all_has_columnwise_data) {\n        NVTE_CHECK(all_nvfp4,\n                   \"When doing rowwise unswizzle with columnwise data, it has to be NVFP4\");\n        NVTE_CHECK(input[i]->columnwise_scale_inv.has_data(), \"Input tensor \", i,\n                   \" does not have column-wise scaling factors.\");\n        NVTE_CHECK(output[i]->columnwise_scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                   \" to have \",\n                   \"2D scaling factors, got shape=\", output[i]->columnwise_scale_inv.shape, \".\");\n        m = output[i]->columnwise_scale_inv.shape[0];\n        k = output[i]->columnwise_scale_inv.shape[1];\n        NVTE_CHECK(m * k == input[i]->columnwise_scale_inv.numel(), \"Expected input tensor \", i,\n                   \" to have \", m * k, \" column-wise scaling factors, but got shape=\",\n                   input[i]->columnwise_scale_inv.shape, \".\");\n      }\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Output scale inverse should be 2D!\");\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      vec_load_size = all_nvfp4 ? 1 : std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      if (!all_nvfp4 || all_has_data) {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->scale_inv.dptr;\n      } else {\n        kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n        kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      }\n      kernel_args.num_tensors++;\n    }\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, true, stream);\n  }\n\n  if (columnwise_unswizzle) {\n    NVTE_CHECK(!all_nvfp4, \"NVFP4 shouldn't end up here because it only needs rowwise unswizzle\");\n\n    MultiSwizzleArgs kernel_args;\n    kernel_args.num_tensors = 0;\n    kernel_args.block_range[0] = 0;\n    int vec_load_size = 4;\n    for (size_t i = 0; i < num_tensors; i++) {\n      if (kernel_args.num_tensors == kMaxTensorsPerKernel) {\n        if (vec_load_size == 3) vec_load_size = 1;\n        launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            kernel_args, vec_load_size, false, stream);\n        kernel_args.num_tensors = 0;\n        vec_load_size = 4;\n      }\n      NVTE_CHECK(output[i]->columnwise_scale_inv.shape.size() == 2, \"Expected output tensor \", i,\n                 \" to have \",\n                 \"2D scaling factors, got shape=\", output[i]->columnwise_scale_inv.shape, \".\");\n      const int m = output[i]->columnwise_scale_inv.shape[1];\n      const int k = output[i]->columnwise_scale_inv.shape[0];\n\n      NVTE_CHECK(m % SF_TILE_DIM_M == 0, \"Output should be padded in M/N dimension!\");\n      NVTE_CHECK(k % SF_TILE_DIM_K == 0, \"Output should be padded in K dimension!\");\n      NVTE_CHECK(k > 0, \"Output scale inverse should be 2D!\");\n      NVTE_CHECK(m * k == std::accumulate(input[i]->columnwise_scale_inv.shape.begin(),\n                                          input[i]->columnwise_scale_inv.shape.end(), 1,\n                                          std::multiplies<int>()),\n                 \"Input.columnwise_scale_inv size is not equal to \"\n                 \"Output.columnwise_scale_inv size!\");\n\n      int num_tiles_k = k / SF_TILE_DIM_K;\n      int vec_load_size_i = (num_tiles_k - 1) % 4 + 1;\n      vec_load_size = std::min(vec_load_size, vec_load_size_i);\n\n      const int pos = kernel_args.num_tensors;\n      kernel_args.input_list[pos] = const_cast<void*>(input[i]->columnwise_scale_inv.dptr);\n      kernel_args.output_list[pos] = output[i]->columnwise_scale_inv.dptr;\n      kernel_args.m_list[pos] = m;\n      kernel_args.k_list[pos] = k;\n      kernel_args.num_tensors++;\n    }\n    if (vec_load_size == 3) vec_load_size = 1;\n    launch_multi_tensor_unswizzle_scaling_factors<SF_TILE_DIM_M, SF_TILE_DIM_K>(\n        kernel_args, vec_load_size, false, stream);\n  }\n}\n}  // namespace transformer_engine\n\n/*\n* WIP (Phuong):\n*   - Opt for bank conflicts\n*   - Adding swizzle for 2d-block scaling.\n*/\nvoid nvte_swizzle_scaling_factors(const NVTETensor input, NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_scaling_factors(convertNVTETensorCheck(input), convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_multi_tensor_swizzle_scaling_factors(const NVTETensor* inputs, NVTETensor* outputs,\n                                               const size_t num_tensors, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_swizzle_scaling_factors);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_swizzle_scaling_factors(input_list, output_list, stream,\n                                       /*check_scale_inv_shapes=*/true);\n}\n\nvoid nvte_multi_tensor_swizzle_scaling_factors_unchecked(const NVTETensor* inputs,\n                                                         NVTETensor* outputs,\n                                                         const size_t num_tensors,\n                                                         hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_swizzle_scaling_factors_unchecked);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_swizzle_scaling_factors(input_list, output_list, stream,\n                                       /*check_scale_inv_shapes=*/false);\n}\n\nvoid nvte_unswizzle_scaling_factors(const NVTETensor input, NVTETensor output,\n                                    hipStream_t stream) {\n  NVTE_API_CALL(nvte_unswizzle_scaling_factors);\n  using namespace transformer_engine;\n  unswizzle_scaling_factors(convertNVTETensorCheck(input), convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_multi_tensor_unswizzle_scaling_factors(const NVTETensor* inputs, NVTETensor* outputs,\n                                                 const size_t num_tensors, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_unswizzle_scaling_factors);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n  std::vector<Tensor*> input_list, output_list;\n  for (size_t i = 0; i < num_tensors; i++) {\n    input_list.push_back(convertNVTETensorCheck(inputs[i]));\n    output_list.push_back(convertNVTETensorCheck(outputs[i]));\n  }\n  multi_tensor_unswizzle_scaling_factors(input_list, output_list, stream);\n}\n\nnamespace transformer_engine {\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\n__global__ void __launch_bounds__(TB_DIM* TB_DIM)\n    grouped_swizzle_scaling_variable_shape_kernel(const void* input, void* output,\n                                                  const int64_t* m_array, const int64_t* k_array,\n                                                  int num_tensors, bool rowwise,\n                                                  size_t scale_elem_size, size_t common_m,\n                                                  size_t common_k) {\n  extern __shared__ int s_metadata[];\n  int* s_total_blocks = &s_metadata[0];\n\n  // Warp reduction to compute total workload\n  if (threadIdx.x < 32 && threadIdx.y == 0) {\n    int local_blocks = 0;\n    for (int i = threadIdx.x; i < num_tensors; i += 32) {\n      size_t m = rowwise ? (m_array ? m_array[i] : common_m) : (k_array ? k_array[i] : common_k);\n      size_t k = rowwise ? (k_array ? k_array[i] : common_k) : (m_array ? m_array[i] : common_m);\n\n      size_t padded_m = round_up_to_multiple(m, 128);\n      size_t padded_k = round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n\n      int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      int num_tiles_k = padded_k / SF_TILE_DIM_K;\n\n      int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      int grid_dim_x = rowwise ? DIVUP(num_tiles_k, n_tiles_in_tb) : DIVUP(num_tiles_k, TB_DIM);\n      int grid_dim_y = rowwise ? num_tiles_m : DIVUP(num_tiles_m, vec_load_size);\n      local_blocks += grid_dim_x * grid_dim_y;\n    }\n\n    for (int offset = 16; offset > 0; offset /= 2) {\n      local_blocks += __shfl_down_sync(0xffffffff, local_blocks, offset);\n    }\n    if (threadIdx.x == 0) *s_total_blocks = local_blocks;\n  }\n  __syncthreads();\n\n  const int total_blocks = *s_total_blocks;\n\n  // Persistent-grid loop\n  for (int linear_block_id = blockIdx.x; linear_block_id < total_blocks;\n       linear_block_id += gridDim.x) {\n    // Discover tensor_id and local_block_id via linear scan\n    int tensor_id = 0;\n    int current_block_base = 0;\n    size_t current_scale_base = 0;\n    int grid_dim_x = 0;\n    int grid_dim_y = 0;\n    size_t M = 0, K = 0;\n    int vec_load_size = 0;\n\n    for (int i = 0; i < num_tensors; ++i) {\n      M = rowwise ? (m_array ? m_array[i] : common_m) : (k_array ? k_array[i] : common_k);\n      K = rowwise ? (k_array ? k_array[i] : common_k) : (m_array ? m_array[i] : common_m);\n\n      size_t padded_m = round_up_to_multiple(M, 128);\n      size_t padded_k = round_up_to_multiple(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n\n      int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      int num_tiles_k = padded_k / SF_TILE_DIM_K;\n\n      vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      grid_dim_x = rowwise ? DIVUP(num_tiles_k, n_tiles_in_tb) : DIVUP(num_tiles_k, TB_DIM);\n      grid_dim_y = rowwise ? num_tiles_m : DIVUP(num_tiles_m, vec_load_size);\n      int blocks_i = grid_dim_x * grid_dim_y;\n\n      if (linear_block_id < current_block_base + blocks_i) {\n        tensor_id = i;\n        break;\n      }\n      current_block_base += blocks_i;\n      current_scale_base += padded_m * padded_k * scale_elem_size;\n    }\n\n    int local_block_id = linear_block_id - current_block_base;\n    int block_x = local_block_id % grid_dim_x;\n    int block_y = local_block_id / grid_dim_x;\n\n    const uint8_t* input_base = reinterpret_cast<const uint8_t*>(input) + current_scale_base;\n    uint8_t* output_base = reinterpret_cast<uint8_t*>(output) + current_scale_base;\n\n    const int padded_m = static_cast<int>(round_up_to_multiple(M, 128));\n    const int padded_k =\n        static_cast<int>(round_up_to_multiple(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4));\n    const int original_M = static_cast<int>(M);\n    const int original_K = static_cast<int>(DIVUP(K, static_cast<size_t>(MXFP8_BLOCK_SIZE)));\n    const bool padding_m = (block_y == grid_dim_y - 1) && (original_M < padded_m);\n    const bool padding_k = (block_x == grid_dim_x - 1) && (original_K < padded_k);\n\n    if (rowwise) {\n      if (vec_load_size == 4) {\n        dispatch_swizzle_row_scaling_kernel_impl<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else if (vec_load_size == 2) {\n        dispatch_swizzle_row_scaling_kernel_impl<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else {\n        dispatch_swizzle_row_scaling_kernel_impl<int, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      }\n    } else {\n      if (vec_load_size == 4) {\n        dispatch_swizzle_col_scaling_kernel_impl<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else if (vec_load_size == 2) {\n        dispatch_swizzle_col_scaling_kernel_impl<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      } else {\n        dispatch_swizzle_col_scaling_kernel_impl<int, SF_TILE_DIM_M, SF_TILE_DIM_K>(\n            input_base, output_base, padded_m, padded_k, original_M, original_K, block_x, block_y,\n            grid_dim_x, grid_dim_y, padding_k, padding_m);\n      }\n    }\n  }\n}\n\ntemplate <int SF_TILE_DIM_M, int SF_TILE_DIM_K>\nint grouped_swizzle_variable_max_active_blocks_per_sm(int device_id) {\n  static std::vector<int> cache(cuda::num_devices(), -1);\n  static std::vector<std::once_flag> flags(cuda::num_devices());\n  NVTE_CHECK(0 <= device_id && device_id < cuda::num_devices(), \"invalid CUDA device ID\");\n\n  auto init = [&]() {\n    constexpr int metadata_shmem = sizeof(int);  // s_total_blocks\n    constexpr int dynamic_smem_size =\n        TB_DIM * 4 * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t) + metadata_shmem;\n    int max_active_blocks_per_sm;\n    NVTE_CHECK_CUDA(hipOccupancyMaxActiveBlocksPerMultiprocessor(\n        &max_active_blocks_per_sm,\n        grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>,\n        TB_DIM * TB_DIM, dynamic_smem_size));\n    NVTE_CHECK(max_active_blocks_per_sm > 0, \"Occupancy query returned 0 blocks per SM.\");\n    cache[device_id] = max_active_blocks_per_sm;\n  };\n  std::call_once(flags[device_id], init);\n  return cache[device_id];\n}\n\nvoid swizzle_grouped_scaling_factors(const GroupedTensor* input, GroupedTensor* output,\n                                     hipStream_t stream) {\n  // Check scaling mode\n  NVTE_CHECK(input->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Grouped swizzle supports only MXFP8 scaling.\");\n\n  // Check tensors\n  CheckInputGroupedTensor(*input, \"input\");\n  CheckOutputGroupedTensor(*output, \"output\", false);\n  NVTE_CHECK(!input->with_gemm_swizzled_scales,\n             \"Expected input grouped tensor with scales in compact format.\");\n  NVTE_CHECK(output->with_gemm_swizzled_scales,\n             \"Expected output grouped tensor with scales in GEMM swizzled format.\");\n\n  // Check scaling factors availability\n  const bool has_rowwise_scale_inv = input->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = input->columnwise_scale_inv.has_data();\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  const int64_t* m_array = reinterpret_cast<const int64_t*>(input->first_dims.dptr);\n  const int64_t* k_array = reinterpret_cast<const int64_t*>(input->last_dims.dptr);\n  const bool is_variable_shape = !input->all_same_shape();\n\n  if (!is_variable_shape) {\n    // Fallback to uniform shape implementation\n    // Assumption is that all the tensors share the same shapes and are contgiuous.\n    // And so we dont need to pass array of input/output pointers(due to conttiguity)\n    // as well as array of shapes(due to uniform shapes).\n    const size_t first_dim = input->get_common_first_dim();\n    const size_t last_dim = input->get_common_last_dim();\n\n    constexpr int SF_TILE_DIM_M = 128;\n    constexpr int SF_TILE_DIM_K = 4;\n    const dim3 block_size(TB_DIM, TB_DIM);\n\n    auto launch_grouped_swizzle = [&](bool rowwise) {\n      const size_t m = rowwise ? first_dim : last_dim;\n      const size_t k = rowwise ? last_dim : first_dim;\n      const size_t padded_m = round_up_to_multiple(m, 128);\n      const size_t padded_k =\n          round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n      // Per-tensor scale-element counts:\n      //  - \"padded\" layout: each tensor occupies padded_m * padded_k elements\n      //    (total buffer = num_tensors * padded_m * padded_k).\n      //  - \"compact\" layout (what the grouped MXFP8 quantize kernel actually writes):\n      //      per-tensor stride is m * padded_k (rowwise) or DIVUP(k,32) * padded_m\n      //      (columnwise) and the total buffer the C++ allocator hands out has its\n      //      grouped first dim padded up to a multiple of 128 (rowwise) or 4\n      //      (columnwise) -- so the buffer may be slightly larger than\n      //      num_tensors * compact_scale_elems, with trailing alignment slack at\n      //      the very end (never read because of the per-tensor row/k guard in the\n      //      kernel impl).\n      // The output is always written in the padded layout. The input may be in\n      // either layout; the kernel handles the compact case safely by using\n      // different per-tensor strides for input vs output and skipping loads past\n      // the per-tensor extent.\n      const size_t padded_scale_elems = padded_m * padded_k;\n      const size_t compact_scale_elems =\n          rowwise ? m * padded_k : DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)) * padded_m;\n      const size_t compact_total_scale_elems =\n          rowwise ? round_up_to_multiple(input->num_tensors * m, 128) * padded_k\n                  : round_up_to_multiple(\n                        input->num_tensors * DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4) *\n                        padded_m;\n\n      const size_t scale_elem_size = rowwise ? typeToSize(input->scale_inv.dtype)\n                                             : typeToSize(input->columnwise_scale_inv.dtype);\n\n      const size_t input_scale_numel =\n          rowwise ? input->scale_inv.numel() : input->columnwise_scale_inv.numel();\n      const size_t output_scale_numel =\n          rowwise ? output->scale_inv.numel() : output->columnwise_scale_inv.numel();\n\n      bool input_is_compact;\n      if (input_scale_numel == input->num_tensors * padded_scale_elems) {\n        input_is_compact = false;\n      } else if (input_scale_numel == compact_total_scale_elems) {\n        input_is_compact = true;\n      } else {\n        NVTE_ERROR(\"Grouped input \", (rowwise ? \"scale_inv\" : \"columnwise_scale_inv\"),\n                   \" size does not match expected packed size (got \", input_scale_numel,\n                   \", expected either \", input->num_tensors * padded_scale_elems,\n                   \" (per-tensor padded) or \", compact_total_scale_elems, \" (compact)).\");\n      }\n      NVTE_CHECK(output_scale_numel == input->num_tensors * padded_scale_elems, \"Grouped output \",\n                 (rowwise ? \"scale_inv\" : \"columnwise_scale_inv\"),\n                 \" size does not match expected per-tensor padded size.\");\n\n      const size_t input_stride_bytes =\n          (input_is_compact ? compact_scale_elems : padded_scale_elems) * scale_elem_size;\n      const size_t output_stride_bytes = padded_scale_elems * scale_elem_size;\n\n      const int num_tiles_m = padded_m / SF_TILE_DIM_M;\n      const int num_tiles_k = padded_k / SF_TILE_DIM_K;\n      int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n      if (vec_load_size == 3) vec_load_size = 1;\n      const int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n      dim3 num_blocks;\n      if (rowwise) {\n        num_blocks = dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m, input->num_tensors);\n      } else {\n        num_blocks =\n            dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size), input->num_tensors);\n      }\n      const int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n      const int original_M = static_cast<int>(rowwise ? first_dim : last_dim);\n      const int original_K = static_cast<int>(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)));\n      const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n      void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n      if (rowwise) {\n        TRANSFORMER_ENGINE_VECTORIZED_LOAD_INTEGER_TYPE_SWITCH(vec_load_size, LType, {\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(grouped_swizzle_row_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          grouped_swizzle_row_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_ptr, output_ptr, padded_m, padded_k, original_M, original_K,\n                  input_stride_bytes, output_stride_bytes);\n        });\n      } else {\n        TRANSFORMER_ENGINE_VECTORIZED_LOAD_INTEGER_TYPE_SWITCH(vec_load_size, LType, {\n          NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(grouped_swizzle_col_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>),\n              hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n          grouped_swizzle_col_scaling_uniform_shape_kernel<LType, SF_TILE_DIM_M, SF_TILE_DIM_K>\n              <<<num_blocks, block_size, slm_size, stream>>>(\n                  input_ptr, output_ptr, padded_m, padded_k, original_M, original_K,\n                  input_stride_bytes, output_stride_bytes);\n        });\n      }\n      NVTE_CHECK_CUDA(hipGetLastError());\n    };\n\n    if (has_rowwise_scale_inv) {\n      launch_grouped_swizzle(true);\n    }\n    if (has_columnwise_scale_inv) {\n      launch_grouped_swizzle(false);\n    }\n  } else {\n    // Variable shape implementation using Device-Side Block Scheduler\n    size_t num_tensors = input->num_tensors;\n\n    constexpr int SF_TILE_DIM_M = 128;\n    constexpr int SF_TILE_DIM_K = 4;\n    const dim3 block_size(TB_DIM, TB_DIM);\n    const int max_slm_size = TB_DIM * 4 * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n    const int metadata_shmem = sizeof(int);  // s_total_blocks\n    const int dynamic_smem_size = max_slm_size + metadata_shmem;\n\n    size_t common_m = input->all_same_first_dim() ? input->get_common_first_dim() : 0;\n    size_t common_k = input->all_same_last_dim() ? input->get_common_last_dim() : 0;\n\n    NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, dynamic_smem_size));\n\n    const int device_id = cuda::current_device();\n    const int num_SMs = cuda::sm_count(device_id);\n    const int max_active_blocks_per_sm =\n        grouped_swizzle_variable_max_active_blocks_per_sm<SF_TILE_DIM_M, SF_TILE_DIM_K>(device_id);\n    const int persistent_blocks = num_SMs * max_active_blocks_per_sm;\n    const dim3 num_blocks(persistent_blocks);\n\n    auto launch_grouped_swizzle_variable = [&](bool rowwise) {\n      const size_t scale_elem_size = rowwise ? typeToSize(input->scale_inv.dtype)\n                                             : typeToSize(input->columnwise_scale_inv.dtype);\n\n      const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n      void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n      grouped_swizzle_scaling_variable_shape_kernel<SF_TILE_DIM_M, SF_TILE_DIM_K>\n          <<<num_blocks, block_size, dynamic_smem_size, stream>>>(\n              input_ptr, output_ptr, m_array, k_array, num_tensors, rowwise, scale_elem_size,\n              common_m, common_k);\n\n      NVTE_CHECK_CUDA(hipGetLastError());\n    };\n\n    if (has_rowwise_scale_inv) {\n      launch_grouped_swizzle_variable(true);\n    }\n    if (has_columnwise_scale_inv) {\n      launch_grouped_swizzle_variable(false);\n    }\n  }\n}\n\nvoid unswizzle_grouped_scaling_factors(const GroupedTensor* input, GroupedTensor* output,\n                                       hipStream_t stream) {\n  NVTE_CHECK(output->scaling_mode == NVTE_MXFP8_1D_SCALING,\n             \"Grouped unswizzle supports only MXFP8 scaling.\");\n\n  CheckInputGroupedTensor(*input, \"input\");\n  CheckOutputGroupedTensor(*output, \"output\", false);\n  NVTE_CHECK(input->with_gemm_swizzled_scales,\n             \"Expected input grouped tensor with scales in GEMM swizzled format.\");\n  NVTE_CHECK(!output->with_gemm_swizzled_scales,\n             \"Expected output grouped tensor with scales in compact format.\");\n  NVTE_CHECK(input->scaling_mode == output->scaling_mode,\n             \"Input and output grouped tensors must have matching scaling modes.\");\n  NVTE_CHECK(input->num_tensors == output->num_tensors,\n             \"Input and output grouped tensors must have the same number of tensors.\");\n\n  const bool has_rowwise_scale_inv = output->scale_inv.has_data();\n  const bool has_columnwise_scale_inv = output->columnwise_scale_inv.has_data();\n  if (!has_rowwise_scale_inv && !has_columnwise_scale_inv) {\n    return;\n  }\n\n  NVTE_CHECK(input->all_same_shape() && output->all_same_shape(),\n             \"Grouped unswizzle requires uniform tensor shapes.\");\n\n  const size_t first_dim = output->get_common_first_dim();\n  const size_t last_dim = output->get_common_last_dim();\n\n  constexpr int SF_TILE_DIM_M = 128;\n  constexpr int SF_TILE_DIM_K = 4;\n  const dim3 block_size(TB_DIM, TB_DIM);\n\n  auto launch_grouped_unswizzle = [&](bool rowwise) {\n    const size_t m = rowwise ? first_dim : last_dim;\n    const size_t k = rowwise ? last_dim : first_dim;\n    const size_t padded_m = round_up_to_multiple(m, 128);\n    const size_t padded_k =\n        round_up_to_multiple(DIVUP(k, static_cast<size_t>(MXFP8_BLOCK_SIZE)), 4);\n    const size_t scale_elems = padded_m * padded_k;\n\n    const size_t scale_elem_size = rowwise ? typeToSize(output->scale_inv.dtype)\n                                           : typeToSize(output->columnwise_scale_inv.dtype);\n    const size_t scale_stride_bytes = scale_elems * scale_elem_size;\n\n    if (rowwise) {\n      NVTE_CHECK(input->scale_inv.numel() == input->num_tensors * scale_elems,\n                 \"Grouped input scale_inv size does not match expected packed size.\");\n      NVTE_CHECK(output->scale_inv.numel() == output->num_tensors * scale_elems,\n                 \"Grouped output scale_inv size does not match expected packed size.\");\n    } else {\n      NVTE_CHECK(input->columnwise_scale_inv.numel() == input->num_tensors * scale_elems,\n                 \"Grouped input columnwise_scale_inv size does not match expected packed size.\");\n      NVTE_CHECK(output->columnwise_scale_inv.numel() == output->num_tensors * scale_elems,\n                 \"Grouped output columnwise_scale_inv size does not match expected packed size.\");\n    }\n\n    const int num_tiles_m = padded_m / SF_TILE_DIM_M;\n    const int num_tiles_k = padded_k / SF_TILE_DIM_K;\n    int vec_load_size = (rowwise ? ((num_tiles_k - 1) % 4 + 1) : ((num_tiles_m - 1) % 4 + 1));\n    if (vec_load_size == 3) vec_load_size = 1;\n    const int n_tiles_in_tb = TB_DIM * vec_load_size;\n\n    dim3 num_blocks;\n    if (rowwise) {\n      num_blocks = dim3(DIVUP(num_tiles_k, n_tiles_in_tb), num_tiles_m, output->num_tensors);\n    } else {\n      num_blocks =\n          dim3(DIVUP(num_tiles_k, TB_DIM), DIVUP(num_tiles_m, vec_load_size), output->num_tensors);\n    }\n    const int slm_size = n_tiles_in_tb * SF_TILE_DIM_M * SF_TILE_DIM_K * sizeof(int8_t);\n\n    const void* input_ptr = rowwise ? input->scale_inv.dptr : input->columnwise_scale_inv.dptr;\n    void* output_ptr = rowwise ? output->scale_inv.dptr : output->columnwise_scale_inv.dptr;\n\n    using kernel_t = void (*)(const void*, void*, const int, const int, const size_t, const bool);\n    kernel_t kernel_fn = nullptr;\n    switch (vec_load_size) {\n      case 4:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int4, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      case 2:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int2, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      case 1:\n        kernel_fn =\n            grouped_unswizzle_scaling_uniform_shape_kernel<int, SF_TILE_DIM_M, SF_TILE_DIM_K>;\n        break;\n      default:\n        NVTE_ERROR(\"Not valid vec_load_size.\");\n    }\n    NVTE_CHECK_CUDA(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_fn), hipFuncAttributeMaxDynamicSharedMemorySize, slm_size));\n    kernel_fn<<<num_blocks, block_size, slm_size, stream>>>(input_ptr, output_ptr, padded_m,\n                                                            padded_k, scale_stride_bytes, rowwise);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  };\n\n  if (has_rowwise_scale_inv) {\n    launch_grouped_unswizzle(true);\n  }\n  if (has_columnwise_scale_inv) {\n    launch_grouped_unswizzle(false);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swizzle_grouped_scaling_factors(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                          hipStream_t stream) {\n  NVTE_API_CALL(nvte_swizzle_grouped_scaling_factors);\n  using namespace transformer_engine;\n  swizzle_grouped_scaling_factors(convertNVTEGroupedTensorCheck(input),\n                                  convertNVTEGroupedTensorCheck(output), stream);\n}\n\nvoid nvte_unswizzle_grouped_scaling_factors(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                            hipStream_t stream) {\n  NVTE_API_CALL(nvte_unswizzle_grouped_scaling_factors);\n  using namespace transformer_engine;\n  unswizzle_grouped_scaling_factors(convertNVTEGroupedTensorCheck(input),\n                                    convertNVTEGroupedTensorCheck(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/swizzle/swizzle.cu"}
{"input": "/*************************************************************************\n* Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n*\n* See LICENSE for license information.\n************************************************************************/\n\n#include <cublasLt.h>\n#include <cublas_v2.h>\n#include <cuda.h>\n#include <transformer_engine/gemm.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <type_traits>\n#include <vector>\n\n#include \"../cast/mxfp8/swizzle.cuh\"\n#include \"../common.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/handle_manager.h\"\n#include \"../util/logging.h\"\n#include \"../util/vectorized_pointwise.h\"\n#include \"./config.h\"\n\nnamespace {\n\ninline void CreateCublasHandle(cublasLtHandle_t *handle) {\n  NVTE_CHECK_CUBLAS(cublasLtCreate(handle));\n}\n\n}  // namespace\n\n// MXFP8 support for grouped GEMM requires cuBLAS 13.3+\n#define CUBLAS_MXFP8_GROUPED_GEMM_VERSION 130300\n// BF16 support for grouped GEMM requires cuBLAS 13.3+\n#define CUBLAS_GROUPED_GEMM_VERSION 130300\n\n#if CUBLAS_VERSION >= CUBLAS_GROUPED_GEMM_VERSION\n\nnamespace {\n\n// Helper struct to pass per-tensor shape/offset info (pointer or uniform value)\nstruct TensorShapeInfo {\n  const int64_t *first_dims;  // nullptr if uniform\n  const int64_t *last_dims;   // nullptr if uniform\n  const int64_t *offsets;     // nullptr if need to compute\n  int64_t uniform_first;      // used if first_dims == nullptr\n  int64_t uniform_last;       // used if last_dims == nullptr\n\n  // Create from GroupedTensor\n  static TensorShapeInfo from_tensor(const transformer_engine::GroupedTensor *t) {\n    const bool has_first = t->first_dims.has_data();\n    const bool has_last = t->last_dims.has_data();\n    // When per-tensor dims are not provided, we must be in the uniform-shape case.\n    NVTE_CHECK(has_first || t->all_same_first_dim(),\n               \"GroupedTensor is missing first_dims for varying shapes\");\n    NVTE_CHECK(has_last || t->all_same_last_dim(),\n               \"GroupedTensor is missing last_dims for varying shapes\");\n\n    const int64_t *first_ptr =\n        has_first ? static_cast<const int64_t *>(t->first_dims.dptr) : nullptr;\n    const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(t->last_dims.dptr) : nullptr;\n\n    const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(t->get_common_first_dim());\n    const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(t->get_common_last_dim());\n\n    return {first_ptr, last_ptr,\n            t->tensor_offsets.has_data() ? static_cast<const int64_t *>(t->tensor_offsets.dptr)\n                                         : nullptr,\n            uniform_first, uniform_last};\n  }\n\n  // Create for C tensor (uses D's dimensions, only has offsets)\n  static TensorShapeInfo create_shape_info_for_C(const transformer_engine::GroupedTensor *C,\n                                                 const transformer_engine::GroupedTensor *D) {\n    const bool has_first = D->first_dims.has_data();\n    const bool has_last = D->last_dims.has_data();\n    NVTE_CHECK(has_first || D->all_same_first_dim(),\n               \"GroupedTensor D is missing first_dims for varying shapes\");\n    NVTE_CHECK(has_last || D->all_same_last_dim(),\n               \"GroupedTensor D is missing last_dims for varying shapes\");\n\n    const int64_t *first_ptr =\n        has_first ? static_cast<const int64_t *>(D->first_dims.dptr) : nullptr;\n    const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(D->last_dims.dptr) : nullptr;\n    const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(D->get_common_first_dim());\n    const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(D->get_common_last_dim());\n\n    return {first_ptr, last_ptr,\n            C->tensor_offsets.has_data() ? static_cast<const int64_t *>(C->tensor_offsets.dptr)\n                                         : nullptr,\n            uniform_first, uniform_last};\n  }\n};\n\n// Helper functions to compute average dimensions for cuBLASLt algorithm-selection heuristics.\n//\n// logical_shape encoding (from build_grouped_tensor):\n//   all_same:      {num_tensors * M, N}\n//   varying_first: {sum_of_first_dims, common_last}\n//   varying_last:  {common_first, sum_of_last_dims}\n//   varying_both:  {1, total_elements}   <-- lossy, can't recover per-dim averages\n//\n// We use all_same_first/last_dim() + get_common_first/last_dim() to get exact\n// answers whenever possible, falling back to logical_shape division otherwise.\n// For varying_both, per-dim averages are unrecoverable without a D2H copy,\n// so we return 1 \u2014 a valid non-zero hint that won't skip work.\ninline int64_t compute_avg_first_dim(const transformer_engine::GroupedTensor *t) {\n  if (t->all_same_first_dim()) {\n    return static_cast<int64_t>(t->get_common_first_dim());\n  }\n  const int64_t n = static_cast<int64_t>(t->num_tensors);\n  if (t->all_same_last_dim()) {\n    // varying_first only: logical_shape = {sum_of_first_dims, common_last}\n    return static_cast<int64_t>(t->logical_shape.data[0]) / n;\n  }\n  // varying_both: logical_shape = {1, total_elements}, no way to recover avg first dim\n  return 1;\n}\n\ninline int64_t compute_avg_last_dim(const transformer_engine::GroupedTensor *t) {\n  if (t->all_same_last_dim()) {\n    // logical_shape[1] is the common N\n    return static_cast<int64_t>(t->logical_shape.data[1]);\n  }\n  // When varying, logical_shape[1] should be sum of last dims if provided; otherwise fallback to avg via division.\n  return static_cast<int64_t>(t->logical_shape.data[1]) / static_cast<int64_t>(t->num_tensors);\n}\n\n// Constants for grouped GEMM workspace (declared early for use in helpers)\nstatic constexpr size_t kGroupedGemmAlignment = 256;\nstatic constexpr size_t kGroupedGemmCublasWorkspaceSize = 32ull * 1024 * 1024;  // 32 MiB\n\n// Workspace layout for grouped GEMM\nstruct GroupedGemmSetupWorkspace {\n  void **A_ptrs;\n  void **B_ptrs;\n  void **C_ptrs;\n  void **D_ptrs;\n  float **alpha_ptrs;\n  float **beta_ptrs;\n  void **\n      a_scale_inv_ptrs;  // Per-tensor FP8 scale pointers for A (float* for tensor scaling, E8M0* for MXFP8)\n  void **\n      b_scale_inv_ptrs;  // Per-tensor FP8 scale pointers for B (float* for tensor scaling, E8M0* for MXFP8)\n  // Storage dimensions for cuBLAS matrix layouts\n  int *a_rows;\n  int *a_cols;\n  int *b_rows;\n  int *b_cols;\n  int *d_rows;  // M (first dim) - also used for C\n  int *d_cols;  // N (last dim) - also used for C\n\n  // Initialize from workspace buffer\n  // Layout: all pointer arrays first (16-byte aligned for cuBLAS), then int arrays\n  static GroupedGemmSetupWorkspace from_buffers(char *setup_ws_ptr, size_t num_tensors) {\n    GroupedGemmSetupWorkspace ws;\n    size_t offset = 0;\n    const size_t ptr_size = num_tensors * sizeof(void *);\n    const size_t int_size = num_tensors * sizeof(int);\n    constexpr size_t kPtrAlignment = 16;  // cuBLAS requires 16-byte alignment for pointer arrays\n\n    // Helper to align offset to kPtrAlignment\n    auto align_offset = [&]() {\n      offset = (offset + kPtrAlignment - 1) / kPtrAlignment * kPtrAlignment;\n    };\n\n    // Pointer arrays first (all 16-byte aligned for cuBLAS grouped GEMM)\n    align_offset();\n    ws.A_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.B_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.C_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.D_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.alpha_ptrs = reinterpret_cast<float **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.beta_ptrs = reinterpret_cast<float **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.a_scale_inv_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.b_scale_inv_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n\n    // Int arrays for storage dimensions (4-byte aligned is fine)\n    align_offset();\n    ws.a_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.a_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.b_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.b_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.d_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.d_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n\n    return ws;\n  }\n\n  // Calculate required size for setup workspace\n  static size_t required_setup_size(size_t num_tensors, size_t alignment) {\n    const size_t ptr_size = num_tensors * sizeof(void *);\n    const size_t int_size = num_tensors * sizeof(int);\n    constexpr size_t kPtrAlignment = 16;  // Must match from_buffers\n\n    // Layout: 8 ptr arrays (each 16-byte aligned), then 6 int arrays\n    // Each ptr array takes ptr_size bytes but needs to start at 16-byte boundary\n    auto aligned_ptr_size = ((ptr_size + kPtrAlignment - 1) / kPtrAlignment) * kPtrAlignment;\n    size_t size = 8 * aligned_ptr_size + 6 * int_size;\n    size = ((size + alignment - 1) / alignment) * alignment;\n    return size;\n  }\n};\n\ninline size_t validate_grouped_gemm_inputs(\n    size_t num_tensors, std::initializer_list<const transformer_engine::GroupedTensor *> inputs,\n    const transformer_engine::Tensor *alpha_tensor, const transformer_engine::Tensor *beta_tensor) {\n  NVTE_CHECK(num_tensors >= 1, \"Grouped GEMM: number of tensors must be at least 1\");\n  for (const auto *tensor : inputs) {\n    NVTE_CHECK(tensor->num_tensors == num_tensors,\n               \"Grouped GEMM: inputs must have the same number of tensors\");\n  }\n\n  const size_t alpha_numel = alpha_tensor->data.numel();\n  const size_t beta_numel = beta_tensor->data.numel();\n  NVTE_CHECK(alpha_numel == num_tensors, \"Grouped GEMM: alpha must have num_tensors (\", num_tensors,\n             \") elements, got \", alpha_numel);\n  NVTE_CHECK(beta_numel == num_tensors, \"Grouped GEMM: beta must have num_tensors (\", num_tensors,\n             \") elements, got \", beta_numel);\n\n  auto is_supported_input_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kFloat8E4M3 ||\n           dtype == transformer_engine::DType::kFloat8E5M2 ||\n           dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16;\n  };\n  for (const auto *tensor : inputs) {\n    if (tensor->has_data() || tensor->has_columnwise_data()) {\n      NVTE_CHECK(is_supported_input_dtype(tensor->dtype()),\n                 \"Grouped GEMM inputs must be FP8, BF16, or FP16, got \",\n                 transformer_engine::to_string(tensor->dtype()), \".\");\n    }\n  }\n  // Cross-operand consistency across all inputs (skip tensors without data).\n  const transformer_engine::GroupedTensor *ref = nullptr;\n  for (const auto *tensor : inputs) {\n    if (tensor->has_data() || tensor->has_columnwise_data()) {\n      ref = tensor;\n      break;\n    }\n  }\n  if (ref != nullptr) {\n    const bool ref_is_fp8 = is_fp8_dtype(ref->dtype());\n    const bool ref_is_mxfp8 = transformer_engine::is_mxfp_scaling(ref->scaling_mode);\n    for (const auto *tensor : inputs) {\n      if (!(tensor->has_data() || tensor->has_columnwise_data())) continue;\n      NVTE_CHECK(is_fp8_dtype(tensor->dtype()) == ref_is_fp8,\n                 \"Grouped GEMM: A and B must both be FP8 or both be non-FP8.\");\n      NVTE_CHECK(transformer_engine::is_mxfp_scaling(tensor->scaling_mode) == ref_is_mxfp8,\n                 \"Grouped GEMM: A and B must both use MXFP8 scaling or both use tensor scaling.\");\n      if (ref_is_mxfp8) {\n        NVTE_CHECK(tensor->with_gemm_swizzled_scales,\n                   \"MXFP8 grouped GEMM: scales must be swizzled for GEMM.\");\n      }\n    }\n  }\n  return num_tensors;\n}\n\ninline void validate_grouped_gemm_outputs(\n    size_t num_tensors, std::initializer_list<const transformer_engine::GroupedTensor *> outputs) {\n  auto is_output_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16 ||\n           dtype == transformer_engine::DType::kFloat32;\n  };\n  for (const auto *tensor : outputs) {\n    if (tensor == nullptr) {\n      continue;\n    }\n    NVTE_CHECK(tensor->num_tensors == num_tensors,\n               \"Grouped GEMM: outputs must have the same number of tensors as inputs\");\n    NVTE_CHECK(is_output_dtype(tensor->dtype()),\n               \"Grouped GEMM: outputs must be BF16, FP16, or FP32.\");\n  }\n}\n\ninline size_t grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  return GroupedGemmSetupWorkspace::required_setup_size(num_tensors, kGroupedGemmAlignment);\n}\n\ninline void check_grouped_gemm_requirements(const char *api_name) {\n  const int current_device = transformer_engine::cuda::current_device();\n  NVTE_CHECK(transformer_engine::cuda::sm_arch(current_device) >= 100, api_name,\n             \" requires Blackwell (SM100) or newer architecture.\");\n  NVTE_CHECK(transformer_engine::cuda::cublas_version() >= CUBLAS_GROUPED_GEMM_VERSION, api_name,\n             \" requires cuBLAS 13.3+, but run-time cuBLAS version is \",\n             transformer_engine::cuda::cublas_version());\n}\n\ninline transformer_engine::GroupedMatmulConfig parse_grouped_gemm_config(\n    NVTEGroupedMatmulConfig config) {\n  transformer_engine::GroupedMatmulConfig config_;\n  if (config != nullptr) {\n    config_ = *reinterpret_cast<transformer_engine::GroupedMatmulConfig *>(config);\n  }\n  return config_;\n}\n\n// Select row-wise vs column-wise storage and adjust transpose flag for grouped GEMM.\n// Mirrors the non-grouped GEMM logic for FP8 layout handling (TN-only on Hopper) and\n// fallback to column-wise data when row-wise is absent.\n// Contains all information needed for GEMM setup - shape already accounts for storage layout.\nstruct GroupedOperandSelection {\n  TensorShapeInfo shape;  // Shape info with dims already swapped for columnwise if needed\n  char *dptr = nullptr;\n  void *scale_inv = nullptr;  // Contiguous array of scales (input)\n  transformer_engine::DType dtype = transformer_engine::DType::kNumTypes;\n  NVTEScalingMode scaling_mode = NVTE_DELAYED_TENSOR_SCALING;\n  bool with_gemm_swizzled_scales = false;\n  bool trans = false;\n  bool rowwise = true;\n};\n\nconstexpr int kMaxGroups = 64;\n// Arguments for the grouped GEMM kernel that operates on multiple output tensors.\nstruct MultiTensorGroupGemmOutputArgs {\n  void *data_ptrs[kMaxGroups];\n  int rows[kMaxGroups];\n  int cols[kMaxGroups];\n};\n\n// Arguments for the grouped GEMM kernel that operates on multiple inputA tensors.\nstruct MultiTensorGroupGemmInputArgs {\n  void *data_ptrs[kMaxGroups];\n  void *scale_inv_ptrs[kMaxGroups];\n  int rows[kMaxGroups];\n  int cols[kMaxGroups];\n};\nstruct MultiTensorListInfo {\n  bool all_row = true;\n  bool all_col = true;\n  transformer_engine::DType row_dtype = transformer_engine::DType::kNumTypes;\n  transformer_engine::DType col_dtype = transformer_engine::DType::kNumTypes;\n  NVTEScalingMode scaling_mode = NVTE_DELAYED_TENSOR_SCALING;\n  bool with_gemm_swizzled_scales = false;\n};\n\nstruct OperandStorageChoice {\n  bool use_rowwise = true;\n  bool swap_dims = true;\n  bool trans = false;\n};\n\ninline OperandStorageChoice choose_grouped_operand_storage(bool trans, bool is_A, bool is_mxfp8,\n                                                           bool is_fp8, bool non_tn_fp8_ok,\n                                                           bool has_row, bool has_col,\n                                                           const char *name) {\n  NVTE_CHECK(has_row || has_col, \"Grouped GEMM: \", name,\n             \" is missing both row-wise and column-wise data\");\n  if (is_mxfp8) {\n    if (is_A) {\n      if (trans) {\n        NVTE_CHECK(has_row, \"Grouped GEMM: MXFP8 transposed \", name, \" is missing row-wise data\");\n        return {true, true, trans};\n      }\n      NVTE_CHECK(has_col, \"Grouped GEMM: MXFP8 non-transposed \", name,\n                 \" is missing column-wise data\");\n      return {false, false, trans};\n    }\n    if (trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: MXFP8 transposed \", name, \" is missing column-wise data\");\n      return {false, false, trans};\n    }\n    NVTE_CHECK(has_row, \"Grouped GEMM: MXFP8 non-transposed \", name, \" is missing row-wise data\");\n    return {true, true, trans};\n  }\n\n  // Hopper-style TN-only FP8: force TN by switching layout and flipping transpose when needed.\n  if (is_fp8 && !non_tn_fp8_ok) {\n    if (is_A && !trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: \", name,\n                 \" is missing column-wise data needed for FP8 TN layout\");\n      return {false, true, true};\n    }\n    if (!is_A && trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: \", name,\n                 \" is missing column-wise data needed for FP8 TN layout\");\n      return {false, true, false};\n    }\n  }\n\n  // If only column-wise data is available, mirror the transpose flag (pre-transposed storage).\n  if (!has_row && has_col) {\n    NVTE_CHECK(!is_fp8 || non_tn_fp8_ok,\n               \"Grouped GEMM: FP8 on Hopper requires row-wise data for this transpose config.\");\n    return {false, true, !trans};\n  }\n\n  NVTE_CHECK(has_row, \"Grouped GEMM: \", name, \" is missing row-wise data\");\n  return {true, true, trans};\n}\n\n// Build Kernel Arguments detailing out addresses and other metadata for list of C/D tensors\n// passed to the grouped GEMM kernel. Use-case: C/D --> List of wgrads for experts in MOE\ninline MultiTensorGroupGemmOutputArgs build_grouped_gemm_multi_out_args(\n    const NVTETensor *tensor_list, size_t list_size, size_t expected_num_tensors,\n    transformer_engine::DType expected_dtype, const char *name) {\n  MultiTensorGroupGemmOutputArgs args{};\n  if (list_size == 0) {\n    NVTE_CHECK(tensor_list == nullptr, \"Grouped GEMM: \", name, \"_list provided with num_\", name,\n               \"_tensors=0\");\n    return args;\n  }\n  NVTE_CHECK(tensor_list != nullptr, \"Grouped GEMM: \", name, \"_list is null but num_\", name,\n             \"_tensors=\", list_size);\n  NVTE_CHECK(list_size == expected_num_tensors, \"Grouped GEMM: \", name,\n             \"_list must have num_tensors (\", expected_num_tensors, \") entries, got \", list_size);\n  NVTE_CHECK(list_size <= static_cast<size_t>(kMaxGroups), \"Grouped GEMM: \", name,\n             \"_list supports up to \", kMaxGroups, \" tensors per kernel, got \", list_size);\n\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    NVTE_CHECK(t->has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i, \" has no data\");\n    NVTE_CHECK(t->dtype() == expected_dtype, \"Grouped GEMM: \", name, \"_list tensor \", i,\n               \" dtype mismatch. Expected \", transformer_engine::to_string(expected_dtype), \" got \",\n               transformer_engine::to_string(t->dtype()));\n    const auto &shape = t->shape();\n    NVTE_CHECK(shape.size() == 2, \"Grouped GEMM: \", name, \"_list tensor \", i, \" must be 2D.\");\n    args.data_ptrs[i] = t->data.dptr;\n    args.rows[i] = static_cast<int>(shape[1]);\n    args.cols[i] = static_cast<int>(shape[0]);\n  }\n  return args;\n}\n\n// Build Kernel Arguments detailing out addresses and other metadata for list of A tensors\n// passed to the grouped GEMM kernel. Use-case: A --> List of Expert weights\ninline MultiTensorGroupGemmInputArgs build_grouped_gemm_multi_inputA_args(\n    const NVTETensor *tensor_list, size_t list_size, bool use_rowwise, bool is_fp8,\n    int64_t *avg_first_dim, int64_t *avg_last_dim, const char *name) {\n  using namespace transformer_engine;\n  MultiTensorGroupGemmInputArgs args{};\n  *avg_first_dim = 0;\n  *avg_last_dim = 0;\n  if (list_size == 0) {\n    return args;\n  }\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    const transformer_engine::SimpleTensor &data = use_rowwise ? t->data : t->columnwise_data;\n    const transformer_engine::SimpleTensor &scale_inv =\n        use_rowwise ? t->scale_inv : t->columnwise_scale_inv;\n    NVTE_CHECK(data.has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i,\n               \" is missing required data.\");\n    NVTE_CHECK(data.shape.size() == 2, \"Grouped GEMM: \", name, \"_list tensor \", i, \" must be 2D.\");\n    args.data_ptrs[i] = data.dptr;\n    args.rows[i] = static_cast<int>(data.shape[1]);\n    args.cols[i] = static_cast<int>(data.shape[0]);\n    *avg_first_dim += static_cast<int64_t>(data.shape[0]);\n    *avg_last_dim += static_cast<int64_t>(data.shape[1]);\n\n    if (is_fp8) {\n      NVTE_CHECK(scale_inv.has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i,\n                 \" requires scale_inv for FP8.\");\n      args.scale_inv_ptrs[i] = scale_inv.dptr;\n    } else {\n      args.scale_inv_ptrs[i] = nullptr;\n    }\n  }\n  *avg_first_dim /= static_cast<int64_t>(list_size);\n  *avg_last_dim /= static_cast<int64_t>(list_size);\n  return args;\n}\n\ninline MultiTensorListInfo validate_grouped_gemm_multi_inputA_list(const NVTETensor *tensor_list,\n                                                                   size_t list_size,\n                                                                   size_t expected_num_tensors,\n                                                                   const char *name) {\n  using namespace transformer_engine;\n  MultiTensorListInfo info{};\n  if (list_size == 0) {\n    NVTE_CHECK(tensor_list == nullptr, \"Grouped GEMM: \", name, \"_list provided with num_\", name,\n               \"_tensors=0\");\n    return info;\n  }\n  NVTE_CHECK(tensor_list != nullptr, \"Grouped GEMM: \", name, \"_list is null but num_\", name,\n             \"_tensors=\", list_size);\n  NVTE_CHECK(list_size == expected_num_tensors, \"Grouped GEMM: \", name,\n             \"_list must have num_tensors (\", expected_num_tensors, \") entries, got \", list_size);\n  NVTE_CHECK(list_size <= static_cast<size_t>(kMaxGroups), \"Grouped GEMM: \", name,\n             \"_list supports up to \", kMaxGroups, \" tensors per kernel, got \", list_size);\n\n  const transformer_engine::Tensor *t0 = transformer_engine::convertNVTETensorCheck(tensor_list[0]);\n  info.scaling_mode = t0->scaling_mode;\n  info.with_gemm_swizzled_scales = t0->with_gemm_swizzled_scales;\n  const bool mxfp8 = transformer_engine::is_mxfp_scaling(info.scaling_mode);\n  NVTE_CHECK(info.scaling_mode == NVTE_DELAYED_TENSOR_SCALING || mxfp8,\n             \"Grouped GEMM: input list only supports tensor scaling or MXFP8.\");\n\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    NVTE_CHECK(t->scaling_mode == info.scaling_mode, \"Grouped GEMM: \", name,\n               \"_list tensors must share the same scaling mode.\");\n    NVTE_CHECK(t->with_gemm_swizzled_scales == info.with_gemm_swizzled_scales,\n               \"Grouped GEMM: \", name, \"_list tensors must share GEMM swizzled scale state.\");\n\n    if (t->has_data()) {\n      if (info.row_dtype == DType::kNumTypes) {\n        info.row_dtype = t->data.dtype;\n      }\n      // Check all tensors have the same dtype\n      NVTE_CHECK(t->data.dtype == info.row_dtype, \"Grouped GEMM: \", name,\n                 \"_list rowwise dtypes must match.\");\n    } else {\n      // All tensors must have either data or columnwise data\n      info.all_row = false;\n    }\n\n    if (t->has_columnwise_data()) {\n      if (info.col_dtype == DType::kNumTypes) {\n        info.col_dtype = t->columnwise_data.dtype;\n      }\n      NVTE_CHECK(t->columnwise_data.dtype == info.col_dtype, \"Grouped GEMM: \", name,\n                 \"_list columnwise dtypes must match.\");\n    } else {\n      // All tensors must have either data or columnwise data\n      info.all_col = false;\n    }\n  }\n\n  return info;\n}\n\n// Helper to create TensorShapeInfo from a GroupedTensor, optionally swapping first/last dims.\n// When swap_dims=true, first_dims and last_dims are swapped to account for columnwise storage.\n// Note: tensor_offsets are the same for rowwise and columnwise data (same element count per tensor).\ninline TensorShapeInfo create_shape_info(const transformer_engine::GroupedTensor *t,\n                                         bool swap_dims) {\n  const bool has_first = t->first_dims.has_data();\n  const bool has_last = t->last_dims.has_data();\n  NVTE_CHECK(has_first || t->all_same_first_dim(),\n             \"GroupedTensor is missing first_dims for varying shapes\");\n  NVTE_CHECK(has_last || t->all_same_last_dim(),\n             \"GroupedTensor is missing last_dims for varying shapes\");\n\n  const int64_t *first_ptr = has_first ? static_cast<const int64_t *>(t->first_dims.dptr) : nullptr;\n  const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(t->last_dims.dptr) : nullptr;\n  const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(t->get_common_first_dim());\n  const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(t->get_common_last_dim());\n\n  const int64_t *offsets_ptr =\n      t->tensor_offsets.has_data() ? static_cast<const int64_t *>(t->tensor_offsets.dptr) : nullptr;\n\n  if (swap_dims) {\n    // Swap first/last to account for columnwise (transposed) storage\n    return {last_ptr, first_ptr, offsets_ptr, uniform_last, uniform_first};\n  }\n  return {first_ptr, last_ptr, offsets_ptr, uniform_first, uniform_last};\n}\n\ninline GroupedOperandSelection select_grouped_operand(const transformer_engine::GroupedTensor *t,\n                                                      bool trans, bool is_A) {\n  using namespace transformer_engine;\n  const bool has_row = t->has_data();\n  const bool has_col = t->has_columnwise_data();\n\n  if (!has_row && !has_col) {\n    GroupedOperandSelection sel{};\n    sel.trans = trans;\n    sel.scaling_mode = t->scaling_mode;\n    sel.dtype = t->dtype();\n    sel.shape = create_shape_info(t, /*swap_dims=*/false);\n    return sel;\n  }\n\n  const auto sm = t->scaling_mode;\n  const bool mxfp8 = is_mxfp_scaling(sm);\n\n  // Validate scaling mode\n  NVTE_CHECK(sm == NVTE_DELAYED_TENSOR_SCALING || mxfp8,\n             \"Grouped GEMM is only supported with bf16, fp8 tensor scaling and MXFP8\");\n\n  const DType row_dtype = t->data.dtype;\n  const DType col_dtype = t->columnwise_data.dtype;\n  GroupedOperandSelection sel{};\n  sel.trans = trans;\n  sel.scaling_mode = sm;\n  sel.with_gemm_swizzled_scales = t->with_gemm_swizzled_scales;\n\n  const DType rep_dtype = has_row ? row_dtype : col_dtype;\n  const bool is_fp8 = is_fp8_dtype(rep_dtype);\n  const bool non_tn_fp8_ok = nvte_is_non_tn_fp8_gemm_supported();\n\n  // Helper to select columnwise storage.\n  // swap_dims=true (default): swap first/last dims in shape info (used when columnwise == transposed).\n  // swap_dims=false: keep original dims (MXFP8: columnwise data has different scale direction,\n  //                  but the logical matrix shape and transpose flag remain unchanged).\n  auto use_columnwise = [&](bool swap_dims = true) {\n    sel.dptr = static_cast<char *>(t->columnwise_data.dptr);\n    sel.scale_inv = t->columnwise_scale_inv.dptr;\n    sel.dtype = col_dtype;\n    sel.rowwise = false;\n    sel.shape = create_shape_info(t, swap_dims);\n  };\n\n  // Helper to select row-wise storage\n  auto use_rowwise = [&]() {\n    sel.dptr = static_cast<char *>(t->data.dptr);\n    sel.scale_inv = t->scale_inv.dptr;\n    sel.dtype = row_dtype;\n    sel.rowwise = true;\n    sel.shape = create_shape_info(t, /*swap_dims=*/false);\n  };\n\n  const auto choice = choose_grouped_operand_storage(trans, is_A, mxfp8, is_fp8, non_tn_fp8_ok,\n                                                     has_row, has_col, is_A ? \"A\" : \"B\");\n  sel.trans = choice.trans;\n  if (choice.use_rowwise) {\n    use_rowwise();\n  } else {\n    use_columnwise(choice.swap_dims);\n  }\n  return sel;\n}\n\ninline void *validate_and_get_workspace_ptr(transformer_engine::Tensor *ws, size_t required_size,\n                                            const char *workspace_name) {\n  NVTE_CHECK(ws != nullptr, workspace_name, \" tensor is null.\");\n  const size_t provided_size = get_buffer_size_bytes(ws->data.numel(), ws->data.dtype);\n  NVTE_CHECK(provided_size >= required_size, \"Grouped GEMM: Insufficient \", workspace_name,\n             \". Required: \", required_size, \" bytes, Available: \", provided_size, \" bytes.\");\n  return ws->data.dptr;\n}\n\ninline void init_matrix_layouts(\n    cublasLtMatrixLayoutOpaque_t &descA, cublasLtMatrixLayoutOpaque_t &descB,\n    cublasLtMatrixLayoutOpaque_t &descC, cublasLtMatrixLayoutOpaque_t &descD,\n    const GroupedGemmSetupWorkspace &ws, const GroupedOperandSelection &A_sel,\n    const GroupedOperandSelection &B_sel, transformer_engine::DType d_dtype, size_t num_tensors) {\n  const cudaDataType_t A_type = get_cuda_dtype(A_sel.dtype);\n  const cudaDataType_t B_type = get_cuda_dtype(B_sel.dtype);\n  const cudaDataType_t D_type = get_cuda_dtype(d_dtype);\n\n  // Storage dimensions computed by kernel, leading dimension = rows\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descA, A_type, num_tensors, ws.a_rows,\n                                                    ws.a_cols, ws.a_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descB, B_type, num_tensors, ws.b_rows,\n                                                    ws.b_cols, ws.b_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descC, D_type, num_tensors, ws.d_rows,\n                                                    ws.d_cols, ws.d_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descD, D_type, num_tensors, ws.d_rows,\n                                                    ws.d_cols, ws.d_rows));\n}\n\ninline void init_matmul_desc(cublasLtMatmulDescOpaque_t &matmulDesc, cublasOperation_t op_A,\n                             cublasOperation_t op_B, bool use_fp8, bool use_split_accumulator) {\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescInit(&matmulDesc, CUBLAS_COMPUTE_32F, CUDA_R_32F));\n\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_TRANSA, &op_A,\n                                                   sizeof(op_A)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_TRANSB, &op_B,\n                                                   sizeof(op_B)));\n\n  cublasLtPointerMode_t pointer_mode = CUBLASLT_POINTER_MODE_DEVICE;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_POINTER_MODE,\n                                                   &pointer_mode, sizeof(pointer_mode)));\n\n  int64_t alphabeta_batch_stride = 1;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_ALPHA_BATCH_STRIDE,\n                                                   &alphabeta_batch_stride, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_BETA_BATCH_STRIDE,\n                                                   &alphabeta_batch_stride, sizeof(int64_t)));\n\n  // Fast accumulation is only supported for FP8 (mirrors non-grouped GEMM logic).\n  int8_t fastAccuMode = use_split_accumulator ? 0 : static_cast<int8_t>(use_fp8);\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_FAST_ACCUM,\n                                                   &fastAccuMode, sizeof(fastAccuMode)));\n}\n\n// Configures cuBLAS for MXFP8 grouped GEMM: sets VEC32_UE8M0 scale mode and scale pointers\n// for both A and B.\ninline void set_mxfp8_scale_pointers(cublasLtMatmulDescOpaque_t &matmulDesc,\n                                     void **a_scale_inv_ptrs, void **b_scale_inv_ptrs) {\n#if CUBLAS_VERSION >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION\n  NVTE_CHECK(transformer_engine::cuda::cublas_version() >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"MXFP8 grouped GEMM requires cuBLAS \", CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"+, but run-time cuBLAS version is \", transformer_engine::cuda::cublas_version());\n  const cublasLtMatmulMatrixScale_t scale_mode = CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                   &a_scale_inv_ptrs, sizeof(a_scale_inv_ptrs)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                   &b_scale_inv_ptrs, sizeof(b_scale_inv_ptrs)));\n#else\n  NVTE_CHECK(false, \"MXFP8 grouped GEMM requires cuBLAS \", CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"+, but compile-time cuBLAS version is \", CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION\n}\n\n// Configures cuBLAS for tensor-scaling FP8 grouped GEMM: sets PER_BATCH_SCALAR_32F scale mode\n// and scale pointers for A and B. Both operands are guaranteed FP8 by the caller.\ninline void set_fp8_scale_pointers(cublasLtMatmulDescOpaque_t &matmulDesc, void **a_scale_inv_ptrs,\n                                   void **b_scale_inv_ptrs) {\n  const cublasLtMatmulMatrixScale_t scale_mode = CUBLASLT_MATMUL_MATRIX_SCALE_PER_BATCH_SCALAR_32F;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                   &a_scale_inv_ptrs, sizeof(a_scale_inv_ptrs)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                   &b_scale_inv_ptrs, sizeof(b_scale_inv_ptrs)));\n}\ninline cublasLtMatmulAlgo_t select_grouped_gemm_algo(cublasLtHandle_t handle,\n                                                     cublasLtMatmulDescOpaque_t &matmulDesc,\n                                                     cublasLtMatrixLayoutOpaque_t &descA,\n                                                     cublasLtMatrixLayoutOpaque_t &descB,\n                                                     cublasLtMatrixLayoutOpaque_t &descC,\n                                                     cublasLtMatrixLayoutOpaque_t &descD,\n                                                     int64_t avg_m, int64_t avg_n, int64_t avg_k) {\n  cublasLtMatmulPreferenceOpaque_t preference;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceInit(&preference));\n  NVTE_CHECK_CUBLAS(\n      cublasLtMatmulPreferenceSetAttribute(&preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES,\n                                           &kGroupedGemmCublasWorkspaceSize, sizeof(size_t)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_DESC_D_AVERAGE_ROWS, &avg_m, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_DESC_D_AVERAGE_COLS, &avg_n, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_AVERAGE_REDUCTION_DIM, &avg_k, sizeof(int64_t)));\n\n  cublasLtMatmulHeuristicResult_t heuristicResult;\n  int returnedResults = 0;\n  auto status = cublasLtMatmulAlgoGetHeuristic(handle, &matmulDesc, &descA, &descB, &descC, &descD,\n                                               &preference, 1, &heuristicResult, &returnedResults);\n  NVTE_CHECK(status != CUBLAS_STATUS_NOT_SUPPORTED,\n             \"Unable to find suitable cuBLAS grouped GEMM algorithm\");\n  NVTE_CHECK_CUBLAS(status);\n  NVTE_CHECK(returnedResults > 0, \"No suitable algorithm found for grouped GEMM\");\n  return heuristicResult.algo;\n}\n\nstruct GroupedGemmWorkspace {\n  GroupedGemmSetupWorkspace setup_workspace;\n  void *cublas_workspace_ptr = nullptr;\n  size_t num_tensors = 0;\n};\n\ninline GroupedGemmWorkspace setup_grouped_gemm_workspace(transformer_engine::Tensor *wspace_setup,\n                                                         transformer_engine::Tensor *wspace_cublas,\n                                                         size_t num_tensors) {\n  const size_t setup_workspace_size = grouped_gemm_setup_workspace_size(num_tensors);\n  const size_t cublas_workspace_size = kGroupedGemmCublasWorkspaceSize;\n  void *setup_workspace_ptr = validate_and_get_workspace_ptr(wspace_setup, setup_workspace_size,\n                                                             \"Grouped GEMM setup workspace\");\n  void *cublas_workspace_ptr = validate_and_get_workspace_ptr(wspace_cublas, cublas_workspace_size,\n                                                              \"Grouped GEMM cuBLAS workspace\");\n  auto setup_workspace = GroupedGemmSetupWorkspace::from_buffers(\n      static_cast<char *>(setup_workspace_ptr), num_tensors);\n  return {std::move(setup_workspace), cublas_workspace_ptr, num_tensors};\n}\n\ninline void execute_grouped_gemm(const GroupedGemmSetupWorkspace &setup_workspace,\n                                 const GroupedOperandSelection &A_sel,\n                                 const GroupedOperandSelection &B_sel,\n                                 transformer_engine::DType d_dtype, size_t num_tensors,\n                                 bool use_split_accumulator, bool use_fp8, int64_t avg_m_val,\n                                 int64_t avg_n_val, int64_t avg_k_val, void *cublas_workspace_ptr,\n                                 cudaStream_t stream, int math_sm_count = 0) {\n  using cublasHandleManager =\n      transformer_engine::detail::HandleManager<cublasLtHandle_t, CreateCublasHandle>;\n  cublasLtHandle_t handle = cublasHandleManager::Instance().GetHandle();\n\n  cublasOperation_t op_A = A_sel.trans ? CUBLAS_OP_T : CUBLAS_OP_N;\n  cublasOperation_t op_B = B_sel.trans ? CUBLAS_OP_T : CUBLAS_OP_N;\n\n  cublasLtMatrixLayoutOpaque_t descA, descB, descC, descD;\n  init_matrix_layouts(descA, descB, descC, descD, setup_workspace, A_sel, B_sel, d_dtype,\n                      num_tensors);\n\n  cublasLtMatmulDescOpaque_t matmulDesc;\n  init_matmul_desc(matmulDesc, op_A, op_B, use_fp8, use_split_accumulator);\n  if (transformer_engine::is_mxfp_scaling(A_sel.scaling_mode)) {\n    set_mxfp8_scale_pointers(matmulDesc, setup_workspace.a_scale_inv_ptrs,\n                             setup_workspace.b_scale_inv_ptrs);\n  } else if (use_fp8) {\n    set_fp8_scale_pointers(matmulDesc, setup_workspace.a_scale_inv_ptrs,\n                           setup_workspace.b_scale_inv_ptrs);\n  }\n  if (math_sm_count != 0) {\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        &matmulDesc, CUBLASLT_MATMUL_DESC_SM_COUNT_TARGET, &math_sm_count, sizeof(math_sm_count)));\n  }\n  cublasLtMatmulAlgo_t algo = select_grouped_gemm_algo(handle, matmulDesc, descA, descB, descC,\n                                                       descD, avg_m_val, avg_n_val, avg_k_val);\n\n  NVTE_CHECK_CUBLAS(cublasLtMatmul(handle, &matmulDesc, setup_workspace.alpha_ptrs,\n                                   setup_workspace.A_ptrs, &descA, setup_workspace.B_ptrs, &descB,\n                                   setup_workspace.beta_ptrs, setup_workspace.C_ptrs, &descC,\n                                   setup_workspace.D_ptrs, &descD, &algo, cublas_workspace_ptr,\n                                   kGroupedGemmCublasWorkspaceSize, stream));\n}\n\n// Device helper: compute the element offset for tensor `idx` given shape metadata.\n// Three cases:\n//   1. Explicit per-tensor offset array provided  \u2192 use it directly.\n//   2. Per-tensor first/last dims provided but no offsets \u2192 cumulative sum of (first*last) products.\n//   3. Fully uniform shapes                        \u2192 idx * uniform_first * uniform_last.\n__forceinline__ __device__ int64_t compute_grouped_tensor_offset(const TensorShapeInfo &meta,\n                                                                 size_t idx) {\n  if (meta.offsets) {\n    return meta.offsets[idx];\n  } else if (meta.first_dims != nullptr || meta.last_dims != nullptr) {\n    // offset[i] = sum_{j < i} (first_dims[j] * last_dims[j])\n    int64_t cumsum = 0;\n    for (size_t i = 0; i < idx; i++) {\n      int64_t f = meta.first_dims ? meta.first_dims[i] : meta.uniform_first;\n      int64_t l = meta.last_dims ? meta.last_dims[i] : meta.uniform_last;\n      cumsum += f * l;\n    }\n    return cumsum;\n  } else {\n    return static_cast<int64_t>(idx) * meta.uniform_first * meta.uniform_last;\n  }\n}\n\n__forceinline__ __device__ int64_t padded_mxfp8_scale_inv_bytes(int64_t first, int64_t last,\n                                                                bool rowwise) {\n  namespace mxfp8_swizzle = transformer_engine::dispatch::mxfp8::swizzle;\n  constexpr int64_t kMxfp8BlockSize = 32;\n  // x is the dimension along which quantization is applied, y is other dimension\n  const int64_t scale_tile_y = static_cast<int64_t>(mxfp8_swizzle::GEMM_SWIZZLED_SCALE_TILE_DIM_Y);\n  const int64_t scale_tile_x = static_cast<int64_t>(mxfp8_swizzle::GEMM_SWIZZLED_SCALE_TILE_DIM_X);\n  // Padded byte size of the swizzled MXFP8 scale_inv for a single tensor with data\n  // shape (first, last). Rowwise scales use rows=first, cols=last; columnwise\n  // scales swap the orientation since they are stored in column-major order.\n  const int64_t scale_dim_y = rowwise ? first : last;\n  const int64_t padded_scale_dim_y =\n      ((scale_dim_y + scale_tile_y - 1) / scale_tile_y) * scale_tile_y;\n  const int64_t data_dim_x = rowwise ? last : first;\n  const int64_t scale_dim_x = (data_dim_x + kMxfp8BlockSize - 1) / kMxfp8BlockSize;\n  const int64_t padded_scale_dim_x =\n      ((scale_dim_x + scale_tile_x - 1) / scale_tile_x) * scale_tile_x;\n  // MXFP8 scales are E8M0 (1 byte per element), so element count == byte count.\n  return padded_scale_dim_y * padded_scale_dim_x;\n}\n\n// Device helper: byte offset into a contiguous grouped MXFP8 scale_inv buffer for\n// tensor `idx`. Each expert's scale_inv is expected to be padded\n// to the 128x4 swizzled layout.\n__forceinline__ __device__ int64_t compute_grouped_tensor_mxfp8_scale_inv_offset(\n    const TensorShapeInfo &meta, size_t idx, bool rowwise) {\n  if (meta.first_dims != nullptr || meta.last_dims != nullptr) {\n    int64_t cumsum = 0;\n    for (size_t i = 0; i < idx; i++) {\n      const int64_t f = meta.first_dims ? meta.first_dims[i] : meta.uniform_first;\n      const int64_t l = meta.last_dims ? meta.last_dims[i] : meta.uniform_last;\n      cumsum += padded_mxfp8_scale_inv_bytes(f, l, rowwise);\n    }\n    return cumsum;\n  }\n  return static_cast<int64_t>(idx) *\n         padded_mxfp8_scale_inv_bytes(meta.uniform_first, meta.uniform_last, rowwise);\n}\n\n// Linear scan to find which tensor contains the given row.\n// Returns the tensor index and writes the exclusive end-row of that tensor to *out_tensor_row_end.\n__forceinline__ __device__ int find_tensor_for_row(const int64_t *first_dims, int64_t uniform_first,\n                                                   int row, int num_tensors,\n                                                   int *out_tensor_row_end) {\n  int offset = 0;\n  for (int i = 0; i < num_tensors; i++) {\n    int dim = first_dims ? static_cast<int>(first_dims[i]) : static_cast<int>(uniform_first);\n    offset += dim;\n    if (row < offset) {\n      *out_tensor_row_end = offset;\n      return i;\n    }\n  }\n  *out_tensor_row_end = offset;\n  return num_tensors - 1;\n}\n\n// Kernel that performs (optionally scaled) bias addition to Grouped GEMM output tensors.\n// SM-filling grid with grid-stride over row chunks.\n// 2D grid: blockIdx.x = SM-filling row blocks, blockIdx.y = column chunk.\n// Each block grid-strides over kRowsPerBlock-sized row chunks, processing\n// all chunks that map to it. Safe when sum(first_dims) <= total_rows.\ntemplate <typename T, int kVec, bool UseScale, int kBlockDim, int kRowsPerBlock>\n__global__ void grouped_bias_add_kernel(char *__restrict__ d_base,\n                                        const char *__restrict__ bias_base,\n                                        const float *__restrict__ scale_base,\n                                        TensorShapeInfo d_meta, int n, int total_rows,\n                                        int num_tensors) {\n  using VecStorage = transformer_engine::VectorizedStorage<T, kVec>;\n  using VecType = typename VecStorage::LType;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  const int row_bid = static_cast<int>(blockIdx.x);\n  const int col_bid = static_cast<int>(blockIdx.y);\n  const int row_grid_stride = static_cast<int>(gridDim.x);\n\n  // Single-warp reduction to compute valid_rows = sum(first_dims).\n  // kMaxGroups <= 64 so warp 0 (32 lanes) covers it with <=2 loads each.\n  __shared__ int s_valid_rows;\n  if (tid < 32) {\n    int local_sum = 0;\n    for (int i = tid; i < num_tensors; i += 32) {\n      local_sum += d_meta.first_dims ? static_cast<int>(d_meta.first_dims[i])\n                                     : static_cast<int>(d_meta.uniform_first);\n    }\n    for (int offset = 16; offset > 0; offset >>= 1) {\n      local_sum += __shfl_down_sync(0xffffffff, local_sum, offset);\n    }\n    if (tid == 0) s_valid_rows = local_sum;\n  }\n  __syncthreads();\n  const int valid_rows = s_valid_rows;\n\n  const int block_cols = kBlockDim * kVec;\n  const int col = col_bid * block_cols + tid * kVec;\n  if (col >= n) return;\n\n  T *__restrict__ d = reinterpret_cast<T *>(d_base);\n  const T *__restrict__ bias = reinterpret_cast<const T *>(bias_base);\n\n  // Grid-stride loop over row chunks.\n  for (int chunk_start = row_bid * kRowsPerBlock; chunk_start < valid_rows;\n       chunk_start += row_grid_stride * kRowsPerBlock) {\n    const int row_start = chunk_start;\n    const int row_end = min(row_start + kRowsPerBlock, valid_rows);\n\n    // Linear scan to find the starting row's tensor and its boundary.\n    int tensor_row_end;\n    int tensor_idx = find_tensor_for_row(d_meta.first_dims, d_meta.uniform_first, row_start,\n                                         num_tensors, &tensor_row_end);\n    int bias_idx = tensor_idx * n;\n\n    VecStorage b_in;\n\n    // Walk tensor segments within this chunk's row range.\n    int seg_start = row_start;\n    while (seg_start < row_end) {\n      while (tensor_idx < num_tensors - 1 && tensor_row_end <= seg_start) {\n        tensor_idx++;\n        bias_idx += n;\n        int dim = d_meta.first_dims ? static_cast<int>(d_meta.first_dims[tensor_idx])\n                                    : static_cast<int>(d_meta.uniform_first);\n        tensor_row_end += dim;\n      }\n      b_in.scratch_.aligned = *reinterpret_cast<const VecType *>(bias + bias_idx + col);\n      const int seg_end = min(tensor_row_end, row_end);\n\n      for (int row = seg_start; row < seg_end; row++) {\n        T *d_ptr = d + row * n + col;\n        VecStorage d_in;\n        d_in.scratch_.aligned = *reinterpret_cast<const VecType *>(d_ptr);\n\n        [[maybe_unused]] float s_val;\n        if constexpr (UseScale) s_val = scale_base[row];\n\n#pragma unroll\n        for (int i = 0; i < kVec; ++i) {\n          if constexpr (UseScale) {\n            d_in.scratch_.separate[i] =\n                static_cast<T>(fmaf(static_cast<float>(b_in.scratch_.separate[i]), s_val,\n                                    static_cast<float>(d_in.scratch_.separate[i])));\n          } else {\n            d_in.scratch_.separate[i] =\n                static_cast<T>(static_cast<float>(d_in.scratch_.separate[i]) +\n                               static_cast<float>(b_in.scratch_.separate[i]));\n          }\n        }\n        *reinterpret_cast<VecType *>(d_ptr) = d_in.scratch_.aligned;\n      }\n\n      seg_start = seg_end;\n    }\n  }\n}\n\n// Single kernel that sets up all GEMM parameters.\n// Rationale: cuBLASLt grouped matmul API needs flat arrays of pointers and per-matrix dimensions,\n// but NVTEGroupedTensor stores a single contiguous buffer + optional per-tensor offsets/shapes.\n// We bridge the mismatch on GPU by computing per-group pointers and storage dims in one kernel.\n__global__ void setup_grouped_gemm_kernel(\n    // Output arrays\n    void **A_ptrs, void **B_ptrs, void **C_ptrs, void **D_ptrs, int *a_rows, int *a_cols,\n    int *b_rows, int *b_cols, int *d_rows, int *d_cols, float **alpha_ptrs, float **beta_ptrs,\n    void **a_scale_inv_ptrs, void **b_scale_inv_ptrs,\n    // Inputs\n    char *a_base, char *b_base, char *c_base, char *d_base, TensorShapeInfo A_meta,\n    TensorShapeInfo B_meta, TensorShapeInfo C_meta, TensorShapeInfo D_meta, size_t a_elem_size,\n    size_t b_elem_size, size_t c_elem_size, size_t d_elem_size, float *alpha_ptr, float *beta_ptr,\n    // Scale inputs: for tensor scaling, pass float* and set mxfp8_base to nullptr\n    // For MXFP8, pass nullptr for tensor_scale and set mxfp8_base\n    float *a_scale_base, float *b_scale_base, bool a_rowwise, bool b_rowwise,\n    NVTEScalingMode scaling_mode, size_t num_tensors,\n    MultiTensorGroupGemmInputArgs a_multi_tensor_args,\n    MultiTensorGroupGemmOutputArgs c_multi_tensor_args,\n    MultiTensorGroupGemmOutputArgs d_multi_tensor_args) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= num_tensors) return;\n\n  // Get dimensions for this tensor (from array or uniform value)\n  const bool has_a_multi_tensor = (a_base == nullptr);\n  const bool has_c_multi_tensor = (c_base == nullptr);\n  const bool has_d_multi_tensor = (d_base == nullptr);\n  int64_t a_first = 0;\n  int64_t a_last = 0;\n  if (has_a_multi_tensor) {\n    a_first = static_cast<int64_t>(a_multi_tensor_args.cols[idx]);\n    a_last = static_cast<int64_t>(a_multi_tensor_args.rows[idx]);\n  } else {\n    a_first = A_meta.first_dims ? A_meta.first_dims[idx] : A_meta.uniform_first;\n    a_last = A_meta.last_dims ? A_meta.last_dims[idx] : A_meta.uniform_last;\n  }\n  int64_t b_first = B_meta.first_dims ? B_meta.first_dims[idx] : B_meta.uniform_first;\n  int64_t b_last = B_meta.last_dims ? B_meta.last_dims[idx] : B_meta.uniform_last;\n  int64_t d_first = D_meta.first_dims ? D_meta.first_dims[idx] : D_meta.uniform_first;\n  int64_t d_last = D_meta.last_dims ? D_meta.last_dims[idx] : D_meta.uniform_last;\n\n  // Compute offsets (from explicit array, cumulative from per-tensor dims, or uniform)\n  int64_t a_offset = has_a_multi_tensor ? 0 : compute_grouped_tensor_offset(A_meta, idx);\n  int64_t b_offset = compute_grouped_tensor_offset(B_meta, idx);\n  int64_t c_offset = compute_grouped_tensor_offset(C_meta, idx);\n  int64_t d_offset = compute_grouped_tensor_offset(D_meta, idx);\n\n  // Compute data pointers\n  A_ptrs[idx] =\n      has_a_multi_tensor ? a_multi_tensor_args.data_ptrs[idx] : (a_base + a_offset * a_elem_size);\n  B_ptrs[idx] = b_base + b_offset * b_elem_size;\n  C_ptrs[idx] =\n      has_c_multi_tensor ? c_multi_tensor_args.data_ptrs[idx] : (c_base + c_offset * c_elem_size);\n  D_ptrs[idx] =\n      has_d_multi_tensor ? d_multi_tensor_args.data_ptrs[idx] : (d_base + d_offset * d_elem_size);\n\n  // Compute storage dimensions for cuBLAS matrix layouts.\n  // For INPUTS (A, B): Row-wise storage is seen as transposed column-major by cuBLAS,\n  // so rows=last, cols=first. For columnwise, dims are already swapped.\n  a_rows[idx] = static_cast<int>(a_last);\n  a_cols[idx] = static_cast<int>(a_first);\n  b_rows[idx] = static_cast<int>(b_last);\n  b_cols[idx] = static_cast<int>(b_first);\n  if (has_d_multi_tensor) {\n    d_rows[idx] = d_multi_tensor_args.rows[idx];\n    d_cols[idx] = d_multi_tensor_args.cols[idx];\n  } else {\n    d_rows[idx] = static_cast<int>(d_last);\n    d_cols[idx] = static_cast<int>(d_first);\n  }\n\n  // Fill alpha/beta pointers (per-matrix)\n  alpha_ptrs[idx] = alpha_ptr + idx;\n  beta_ptrs[idx] = beta_ptr + idx;\n\n  // Fill scale pointers (per-matrix).\n  // The interpretation of the scale buffers depends on the shared scaling recipe:\n  //   otherwise             : one float per tensor, indexed by tensor index\n  if (a_scale_base) {\n    if (scaling_mode == NVTE_MXFP8_1D_SCALING) {\n      const int64_t a_scale_offset =\n          compute_grouped_tensor_mxfp8_scale_inv_offset(A_meta, idx, a_rowwise);\n      a_scale_inv_ptrs[idx] = reinterpret_cast<void *>(\n          static_cast<char *>(static_cast<void *>(a_scale_base)) + a_scale_offset);\n    } else {\n      a_scale_inv_ptrs[idx] = static_cast<float *>(a_scale_base) + idx;\n    }\n  } else {\n    a_scale_inv_ptrs[idx] = a_multi_tensor_args.scale_inv_ptrs[idx];\n  }\n  if (b_scale_base) {\n    if (scaling_mode == NVTE_MXFP8_1D_SCALING) {\n      const int64_t b_scale_offset =\n          compute_grouped_tensor_mxfp8_scale_inv_offset(B_meta, idx, b_rowwise);\n      b_scale_inv_ptrs[idx] = reinterpret_cast<void *>(\n          static_cast<char *>(static_cast<void *>(b_scale_base)) + b_scale_offset);\n    } else {\n      b_scale_inv_ptrs[idx] = static_cast<float *>(b_scale_base) + idx;\n    }\n  }\n}\n\n// Launch the setup kernel to populate workspace arrays\ninline void launch_grouped_gemm_setup(\n    const GroupedGemmSetupWorkspace &ws, const GroupedOperandSelection &A_sel,\n    const GroupedOperandSelection &B_sel, const transformer_engine::GroupedTensor *C,\n    const transformer_engine::GroupedTensor *D, const transformer_engine::Tensor *alpha_tensor,\n    const transformer_engine::Tensor *beta_tensor, size_t num_tensors, cudaStream_t stream,\n    const MultiTensorGroupGemmInputArgs &a_multi_tensor_args, const NVTETensor *C_list,\n    const NVTETensor *D_list, char *a_base, transformer_engine::DType c_dtype,\n    transformer_engine::DType d_dtype) {\n  // Use shape info from selection (already accounts for columnwise dimension swap)\n  TensorShapeInfo A_meta = A_sel.shape;\n  TensorShapeInfo B_meta = B_sel.shape;\n  TensorShapeInfo C_meta{};\n  TensorShapeInfo D_meta{};\n\n  const bool has_d_multi_tensor = (D_list != nullptr);\n  const bool has_c_multi_tensor = (C_list != nullptr) || has_d_multi_tensor;\n  MultiTensorGroupGemmOutputArgs c_multi_tensor_args{};\n  MultiTensorGroupGemmOutputArgs d_multi_tensor_args{};\n  if (has_d_multi_tensor) {\n    d_multi_tensor_args =\n        build_grouped_gemm_multi_out_args(D_list, num_tensors, num_tensors, d_dtype, \"D\");\n  }\n  if (C_list != nullptr) {\n    c_multi_tensor_args =\n        build_grouped_gemm_multi_out_args(C_list, num_tensors, num_tensors, d_dtype, \"C\");\n  } else if (has_d_multi_tensor) {\n    c_multi_tensor_args = d_multi_tensor_args;\n  }\n\n  char *c_base = nullptr;\n  char *d_base = nullptr;\n\n  if (!has_c_multi_tensor) {\n    NVTE_CHECK(C != nullptr && D != nullptr,\n               \"Grouped GEMM: C/D grouped tensors are required when no C list is provided\");\n    C_meta = TensorShapeInfo::create_shape_info_for_C(C, D);\n    c_base = static_cast<char *>(C->data.dptr);\n  }\n  if (!has_d_multi_tensor) {\n    NVTE_CHECK(D != nullptr,\n               \"Grouped GEMM: D grouped tensor is required when no D list is provided\");\n    D_meta = TensorShapeInfo::from_tensor(D);\n    d_base = static_cast<char *>(D->data.dptr);\n  }\n\n  const size_t a_elem_size = transformer_engine::typeToSize(A_sel.dtype);\n  const size_t b_elem_size = transformer_engine::typeToSize(B_sel.dtype);\n  const size_t c_elem_size = transformer_engine::typeToSize(c_dtype);\n  const size_t d_elem_size = transformer_engine::typeToSize(d_dtype);\n\n  const int threads_per_block = 256;\n  const int num_blocks = (num_tensors + threads_per_block - 1) / threads_per_block;\n\n  // A and B share the same scaling recipe (validated in validate_grouped_gemm_inputs).\n  // Pass scale buffers as void* and let the kernel interpret them via scaling_mode.\n\n  // Scale rowwise flag for MXFP8/NVFP4: to calculate scale_inv padding based offsets\n  // within kernel. Ignored for tensor scaling.\n  const bool a_rowwise = A_sel.rowwise;\n  const bool b_rowwise = B_sel.rowwise;\n  setup_grouped_gemm_kernel<<<num_blocks, threads_per_block, 0, stream>>>(\n      ws.A_ptrs, ws.B_ptrs, ws.C_ptrs, ws.D_ptrs, ws.a_rows, ws.a_cols, ws.b_rows, ws.b_cols,\n      ws.d_rows, ws.d_cols, ws.alpha_ptrs, ws.beta_ptrs, ws.a_scale_inv_ptrs, ws.b_scale_inv_ptrs,\n      A_sel.dptr, B_sel.dptr, c_base, d_base, A_meta, B_meta, C_meta, D_meta, a_elem_size,\n      b_elem_size, c_elem_size, d_elem_size, static_cast<float *>(alpha_tensor->data.dptr),\n      static_cast<float *>(beta_tensor->data.dptr), reinterpret_cast<float *>(A_sel.scale_inv),\n      reinterpret_cast<float *>(B_sel.scale_inv), a_rowwise, b_rowwise, A_sel.scaling_mode,\n      num_tensors, a_multi_tensor_args, c_multi_tensor_args, d_multi_tensor_args);\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace\n\nsize_t nvte_get_grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  NVTE_API_CALL(nvte_get_grouped_gemm_setup_workspace_size);\n  return grouped_gemm_setup_workspace_size(num_tensors);\n}\n\nvoid nvte_grouped_gemm(const NVTEGroupedTensor A, int transa, const NVTEGroupedTensor B, int transb,\n                       const NVTEGroupedTensor C, NVTEGroupedTensor D, const NVTETensor alpha,\n                       const NVTETensor beta, NVTETensor workspace_setup,\n                       NVTETensor workspace_cublas, NVTEGroupedMatmulConfig config,\n                       cudaStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm\");\n\n  // Convert to internal types\n  const GroupedTensor *inputA = convertNVTEGroupedTensorCheck(A);\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const GroupedTensor *inputC_raw = convertNVTEGroupedTensor(C);  // Can be NULL\n  GroupedTensor *outputD = convertNVTEGroupedTensorCheck(D);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Validate inputs and outputs.\n  const size_t num_tensors = validate_grouped_gemm_inputs(inputA->num_tensors, {inputA, inputB},\n                                                          alpha_tensor, beta_tensor);\n  validate_grouped_gemm_outputs(num_tensors, {inputC_raw, outputD});\n\n  // If C is NULL, use D as C (valid when beta=0, cuBLAS won't read C data)\n  const GroupedTensor *inputC = (inputC_raw != nullptr) ? inputC_raw : outputD;\n  // num_tensors validated above.\n  // Select operand storage (row-wise vs column-wise) and adjust transpose flags to\n  // mirror the non-grouped GEMM logic for FP8 layout constraints.\n  auto A_sel = select_grouped_operand(inputA, static_cast<bool>(transa), /*is_A=*/true);\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, inputC, outputD, alpha_tensor,\n                            beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            /*C_list=*/nullptr, /*D_list=*/nullptr, A_sel.dptr, inputC->dtype(),\n                            outputD->dtype());\n\n  // Compute average dimensions for heuristics\n  // K dimension: if transa, K is A's first dim; if not, K is A's last dim\n  // Use original inputA and transa for heuristics (not modified A_sel.trans)\n  int64_t avg_m_val = config_.avg_m.value_or(compute_avg_first_dim(outputD));\n  int64_t avg_n_val = config_.avg_n.value_or(compute_avg_last_dim(outputD));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(transa ? compute_avg_first_dim(inputA) : compute_avg_last_dim(inputA));\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, outputD->dtype(), num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nvoid nvte_grouped_gemm_with_discrete_inputA(const NVTETensor *A_list, size_t num_a_tensors,\n                                            int transa, const NVTEGroupedTensor B, int transb,\n                                            const NVTEGroupedTensor C, NVTEGroupedTensor D,\n                                            const NVTETensor alpha, const NVTETensor beta,\n                                            NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                            NVTEGroupedMatmulConfig config, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm_with_discrete_inputA);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm_with_discrete_inputA\");\n\n  NVTE_CHECK(A_list != nullptr, \"Grouped GEMM: A_list is null.\");\n  NVTE_CHECK(num_a_tensors > 0, \"Grouped GEMM: num_a_tensors must be > 0.\");\n\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const GroupedTensor *inputC_raw = convertNVTEGroupedTensor(C);  // Can be NULL\n  GroupedTensor *outputD = convertNVTEGroupedTensorCheck(D);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Validate inputs and outputs.\n  const size_t num_tensors =\n      validate_grouped_gemm_inputs(num_a_tensors, {inputB}, alpha_tensor, beta_tensor);\n\n  validate_grouped_gemm_outputs(num_tensors, {inputC_raw, outputD});\n\n  // If C is NULL, use D as C (valid when beta=0, cuBLAS won't read C data)\n  const GroupedTensor *inputC = (inputC_raw != nullptr) ? inputC_raw : outputD;\n\n  // Validate A list and selection\n  auto A_list_info =\n      validate_grouped_gemm_multi_inputA_list(A_list, num_a_tensors, num_tensors, \"A\");\n  auto is_fp8_or_16bit = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kFloat8E4M3 ||\n           dtype == transformer_engine::DType::kFloat8E5M2 ||\n           dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16;\n  };\n  NVTE_CHECK(is_fp8_or_16bit(A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype),\n             \"Grouped GEMM: A_list tensors must be FP8, BF16, or FP16.\");\n\n  // Cross-operand consistency (mirrors validate_grouped_gemm_inputs).\n  const DType a_rep_dtype = A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype;\n  NVTE_CHECK(is_fp8_dtype(a_rep_dtype) == is_fp8_dtype(inputB->dtype()),\n             \"Grouped GEMM: A and B must both be FP8 or both be non-FP8.\");\n  NVTE_CHECK(transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode) ==\n                 transformer_engine::is_mxfp_scaling(inputB->scaling_mode),\n             \"Grouped GEMM: A and B must both use MXFP8 scaling or both use tensor scaling.\");\n  if (transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode)) {\n    NVTE_CHECK(A_list_info.with_gemm_swizzled_scales,\n               \"MXFP8 grouped GEMM: A scales must be swizzled for GEMM.\");\n    NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n               \"MXFP8 grouped GEMM: B scales must be swizzled for GEMM.\");\n  }\n\n  // Select operand storage for B (row-wise vs column-wise)\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n\n  GroupedOperandSelection A_sel{};\n  A_sel.scaling_mode = A_list_info.scaling_mode;\n  A_sel.with_gemm_swizzled_scales = A_list_info.with_gemm_swizzled_scales;\n  A_sel.trans = static_cast<bool>(transa);\n\n  const DType rep_dtype = A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype;\n  const bool is_fp8 = is_fp8_dtype(rep_dtype);\n  const bool non_tn_fp8_ok = nvte_is_non_tn_fp8_gemm_supported();\n  const bool mxfp8 = transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode);\n\n  int64_t avg_first_dim = 0;\n  int64_t avg_last_dim = 0;\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n\n  const auto choice =\n      choose_grouped_operand_storage(static_cast<bool>(transa), /*is_A=*/true, mxfp8, is_fp8,\n                                     non_tn_fp8_ok, A_list_info.all_row, A_list_info.all_col, \"A\");\n  A_sel.trans = choice.trans;\n  A_sel.rowwise = choice.use_rowwise;\n  if (choice.use_rowwise) {\n    NVTE_CHECK(A_list_info.all_row, \"Grouped GEMM: A_list is missing row-wise data\");\n    A_sel.dtype = A_list_info.row_dtype;\n    a_multi_tensor_args = build_grouped_gemm_multi_inputA_args(\n        A_list, num_a_tensors, /*use_rowwise=*/true, is_fp8, &avg_first_dim, &avg_last_dim, \"A\");\n  } else {\n    NVTE_CHECK(A_list_info.all_col, \"Grouped GEMM: A_list is missing column-wise data\");\n    A_sel.dtype = A_list_info.col_dtype;\n    a_multi_tensor_args = build_grouped_gemm_multi_inputA_args(\n        A_list, num_a_tensors, /*use_rowwise=*/false, is_fp8, &avg_first_dim, &avg_last_dim, \"A\");\n  }\n\n  // For discrete A_list, scale pointers are per-tensor; use multi-tensor args.\n  // Base pointer is unused when providing per-tensor pointers.\n  A_sel.scale_inv = nullptr;\n  A_sel.dptr = nullptr;\n\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, inputC, outputD, alpha_tensor,\n                            beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            /*C_list=*/nullptr, /*D_list=*/nullptr, nullptr, inputC->dtype(),\n                            outputD->dtype());\n\n  // Compute average dimensions for heuristics\n  int64_t avg_m_val = config_.avg_m.value_or(compute_avg_first_dim(outputD));\n  int64_t avg_n_val =\n      config_.avg_n.value_or(transb ? compute_avg_first_dim(inputB) : compute_avg_last_dim(inputB));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(static_cast<bool>(transa) ? avg_last_dim : avg_first_dim);\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, outputD->dtype(), num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nvoid nvte_grouped_gemm_with_discrete_out(const NVTEGroupedTensor A, int transa,\n                                         const NVTEGroupedTensor B, int transb,\n                                         const NVTETensor *C_list, size_t num_c_tensors,\n                                         NVTETensor *D_list, size_t num_d_tensors,\n                                         const NVTETensor alpha, const NVTETensor beta,\n                                         NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                         NVTEGroupedMatmulConfig config, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm_with_discrete_out);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm_with_discrete_out\");\n\n  NVTE_CHECK(D_list != nullptr, \"Grouped GEMM: D_list is null.\");\n  NVTE_CHECK(num_d_tensors > 0, \"Grouped GEMM: num_d_tensors must be > 0.\");\n  if (num_c_tensors > 0) {\n    NVTE_CHECK(C_list != nullptr, \"Grouped GEMM: C_list is null but num_c_tensors > 0.\");\n  }\n\n  const GroupedTensor *inputA = convertNVTEGroupedTensorCheck(A);\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  const Tensor *d0 = convertNVTETensorCheck(D_list[0]);\n  const DType d_dtype = d0->dtype();\n\n  const size_t num_tensors = validate_grouped_gemm_inputs(inputA->num_tensors, {inputA, inputB},\n                                                          alpha_tensor, beta_tensor);\n  NVTE_CHECK(num_d_tensors == num_tensors, \"Grouped GEMM: D_list must have num_tensors (\",\n             num_tensors, \") entries, got \", num_d_tensors);\n  if (num_c_tensors > 0) {\n    NVTE_CHECK(num_c_tensors == num_tensors, \"Grouped GEMM: C_list must have num_tensors (\",\n               num_tensors, \") entries, got \", num_c_tensors);\n  }\n  auto is_output_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16 ||\n           dtype == transformer_engine::DType::kFloat32;\n  };\n  NVTE_CHECK(is_output_dtype(d_dtype), \"Grouped GEMM: D must be BF16, FP16, or FP32.\");\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Select operand storage (row-wise vs column-wise) and adjust transpose flags to\n  // mirror the non-grouped GEMM logic for FP8 layout constraints.\n  auto A_sel = select_grouped_operand(inputA, static_cast<bool>(transa), /*is_A=*/true);\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, /*C=*/nullptr, /*D=*/nullptr,\n                            alpha_tensor, beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            C_list, D_list, A_sel.dptr, d_dtype, d_dtype);\n\n  // Compute average dimensions for heuristics\n  int64_t avg_m_val =\n      config_.avg_m.value_or(transa ? compute_avg_last_dim(inputA) : compute_avg_first_dim(inputA));\n  int64_t avg_n_val =\n      config_.avg_n.value_or(transb ? compute_avg_first_dim(inputB) : compute_avg_last_dim(inputB));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(transa ? compute_avg_first_dim(inputA) : compute_avg_last_dim(inputA));\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, d_dtype, num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nnamespace {\n\nvoid launch_grouped_bias_add(const transformer_engine::GroupedTensor *outputD,\n                             const transformer_engine::GroupedTensor *bias_tensor,\n                             const float *scale_ptr, bool use_scale, cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  const char *api_name = use_scale ? \"Grouped scaled bias add\" : \"Grouped bias add\";\n\n  NVTE_CHECK(outputD->num_tensors >= 1, api_name, \": number of tensors must be at least 1\");\n  NVTE_CHECK(outputD->num_tensors == bias_tensor->num_tensors, api_name,\n             \": output and bias must have the same number of tensors\");\n  NVTE_CHECK(outputD->has_data(), api_name, \": output is missing row-wise data\");\n  NVTE_CHECK(bias_tensor->has_data(), api_name, \": bias is missing row-wise data\");\n  NVTE_CHECK(outputD->dtype() == bias_tensor->dtype(), api_name,\n             \": output and bias must have matching dtypes\");\n  NVTE_CHECK(bias_tensor->all_same_first_dim(), api_name,\n             \": bias must have uniform first dim (expected 1)\");\n  NVTE_CHECK(bias_tensor->get_common_first_dim() == 1, api_name, \": bias first dim must be 1\");\n  NVTE_CHECK(outputD->all_same_last_dim() && bias_tensor->all_same_last_dim(), api_name,\n             \": requires uniform last dim for output and bias\");\n  NVTE_CHECK(outputD->get_common_last_dim() == bias_tensor->get_common_last_dim(), api_name,\n             \": output and bias last dims must match\");\n\n  const TensorShapeInfo d_meta = TensorShapeInfo::from_tensor(outputD);\n\n  const DType dtype = outputD->dtype();\n  constexpr int kThreads = 128;\n\n  const int num_tensors = static_cast<int>(outputD->num_tensors);\n  NVTE_CHECK(num_tensors <= kMaxGroups, api_name, \" supports at most \", kMaxGroups,\n             \" tensors, got \", num_tensors);\n  const int total_rows = static_cast<int>(outputD->logical_shape.data[0]);\n  const int n = static_cast<int>(outputD->get_common_last_dim());\n\n  const size_t elem_size = typeToSize(dtype);\n  const int kVec = (elem_size <= 2) ? 8 : 4;\n  NVTE_CHECK(n % kVec == 0, api_name, \": requires last dim divisible by \", kVec);\n\n  constexpr int kRowsPerBlock = 16;\n  constexpr int kBlocksPerSM = 32;\n\n  const int num_sms = transformer_engine::cuda::sm_count();\n\n  const int block_cols = kThreads * kVec;\n  const int col_blocks = (n + block_cols - 1) / block_cols;\n  const int max_row_chunks = (total_rows + kRowsPerBlock - 1) / kRowsPerBlock;\n  const int row_blocks = std::min(max_row_chunks, num_sms * kBlocksPerSM / col_blocks);\n  const dim3 grid(std::max(1, row_blocks), col_blocks);\n  const dim3 block(kThreads);\n\n  auto launch = [&](auto use_scale_tag) {\n    constexpr bool kUseScale = decltype(use_scale_tag)::value;\n    if (elem_size <= 2) {\n      constexpr int kV = 8;\n      TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(dtype, T, {\n        grouped_bias_add_kernel<T, kV, kUseScale, kThreads, kRowsPerBlock>\n            <<<grid, block, 0, stream>>>(static_cast<char *>(outputD->data.dptr),\n                                         static_cast<const char *>(bias_tensor->data.dptr),\n                                         scale_ptr, d_meta, n, total_rows, num_tensors);\n      });\n    } else {\n      constexpr int kV = 4;\n      TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(dtype, T, {\n        grouped_bias_add_kernel<T, kV, kUseScale, kThreads, kRowsPerBlock>\n            <<<grid, block, 0, stream>>>(static_cast<char *>(outputD->data.dptr),\n                                         static_cast<const char *>(bias_tensor->data.dptr),\n                                         scale_ptr, d_meta, n, total_rows, num_tensors);\n      });\n    }\n  };\n\n  if (use_scale) {\n    launch(std::true_type{});\n  } else {\n    launch(std::false_type{});\n  }\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace\n\nvoid nvte_grouped_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                           cudaStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_bias_add);\n  using namespace transformer_engine;\n  const GroupedTensor *outputD = convertNVTEGroupedTensorCheck(output);\n  const GroupedTensor *bias_tensor = convertNVTEGroupedTensorCheck(bias);\n  launch_grouped_bias_add(outputD, bias_tensor, nullptr, false, stream);\n}\n\nvoid nvte_grouped_scaled_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                                  const NVTETensor scale, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_scaled_bias_add);\n  using namespace transformer_engine;\n  const GroupedTensor *outputD = convertNVTEGroupedTensorCheck(output);\n  const GroupedTensor *bias_tensor = convertNVTEGroupedTensorCheck(bias);\n  const Tensor *scale_tensor = convertNVTETensorCheck(scale);\n\n  NVTE_CHECK(scale_tensor->data.dptr != nullptr,\n             \"Grouped scaled bias add: scale tensor must not be null\");\n  NVTE_CHECK(scale_tensor->dtype() == DType::kFloat32,\n             \"Grouped scaled bias add: scale must be float32\");\n  NVTE_CHECK(scale_tensor->data.shape.size() == 1,\n             \"Grouped scaled bias add: scale must be 1D, got \", scale_tensor->data.shape.size(),\n             \"D\");\n  const size_t total_rows = static_cast<size_t>(outputD->logical_shape.data[0]);\n  NVTE_CHECK(scale_tensor->data.shape[0] == total_rows, \"Grouped scaled bias add: scale size (\",\n             scale_tensor->data.shape[0], \") must equal total rows (\", total_rows, \")\");\n\n  const float *scale_ptr = static_cast<const float *>(scale_tensor->data.dptr);\n  launch_grouped_bias_add(outputD, bias_tensor, scale_ptr, true, stream);\n}\n\n#else  // CUBLAS_VERSION < CUBLAS_GROUPED_GEMM_VERSION\n\nvoid nvte_grouped_gemm(const NVTEGroupedTensor A, int transa, const NVTEGroupedTensor B, int transb,\n                       const NVTEGroupedTensor C, NVTEGroupedTensor D, const NVTETensor alpha,\n                       const NVTETensor beta, NVTETensor workspace_setup,\n                       NVTETensor workspace_cublas, NVTEGroupedMatmulConfig config,\n                       cudaStream_t stream) {\n  NVTE_ERROR(\"nvte_grouped_gemm requires cuBLAS 13.3+, but compile-time cuBLAS version is \",\n             CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_gemm_with_discrete_inputA(const NVTETensor *A_list, size_t num_a_tensors,\n                                            int transa, const NVTEGroupedTensor B, int transb,\n                                            const NVTEGroupedTensor C, NVTEGroupedTensor D,\n                                            const NVTETensor alpha, const NVTETensor beta,\n                                            NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                            NVTEGroupedMatmulConfig config, cudaStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_gemm_with_discrete_inputA requires cuBLAS 13.3+, but compile-time \"\n      \"cuBLAS version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_gemm_with_discrete_out(const NVTEGroupedTensor A, int transa,\n                                         const NVTEGroupedTensor B, int transb,\n                                         const NVTETensor *C_list, size_t num_c_tensors,\n                                         NVTETensor *D_list, size_t num_d_tensors,\n                                         const NVTETensor alpha, const NVTETensor beta,\n                                         NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                         NVTEGroupedMatmulConfig config, cudaStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_gemm_with_discrete_out requires cuBLAS 13.3+, but compile-time \"\n      \"cuBLAS version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                           cudaStream_t stream) {\n  NVTE_ERROR(\"nvte_grouped_bias_add requires cuBLAS 13.3+, but compile-time cuBLAS version is \",\n             CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_scaled_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                                  const NVTETensor scale, cudaStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_scaled_bias_add requires cuBLAS 13.3+, but compile-time cuBLAS version \"\n      \"is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nsize_t nvte_get_grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  NVTE_ERROR(\n      \"nvte_get_grouped_gemm_setup_workspace_size requires cuBLAS 13.3+, but compile-time cuBLAS \"\n      \"version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n  return 0;\n}\n\n#endif  // CUBLAS_VERSION >= CUBLAS_GROUPED_GEMM_VERSION\n\nnamespace {\n\n__global__ void convert_int32_to_int64_kernel(const int32_t *src, int64_t *dst, size_t n) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n) dst[idx] = static_cast<int64_t>(src[idx]);\n}\n\n// Like convert_int32_to_int64_kernel but scales each element by multiplier.\n// Used to convert per-expert slice counts to per-expert row counts for multi-dim tensors.\n__global__ void convert_int32_to_int64_with_multiplier_kernel(const int32_t *src, int64_t *dst,\n                                                              size_t n, int64_t multiplier) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n) dst[idx] = static_cast<int64_t>(src[idx]) * multiplier;\n}\n\n// Computes exclusive prefix sums: offsets[0]=0, offsets[i]=sum(first_dims[0..i-1]*last_dim).\n// Produces n_groups+1 values. Single-threaded sequential scan; n_groups is typically small.\n__global__ void compute_grouped_tensor_offsets_kernel(const int64_t *first_dims, int64_t *offsets,\n                                                      size_t n_groups, int64_t last_dim) {\n  offsets[0] = 0;\n  for (size_t i = 0; i < n_groups; i++) {\n    offsets[i + 1] = offsets[i] + first_dims[i] * last_dim;\n  }\n}\n\n}  // namespace\n\nvoid nvte_convert_int32_to_int64(const int32_t *src, int64_t *dst, size_t n, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_convert_int32_to_int64);\n  if (n == 0) return;\n  const int threads = 256;\n  const int blocks = static_cast<int>((n + threads - 1) / threads);\n  convert_int32_to_int64_kernel<<<blocks, threads, 0, stream>>>(src, dst, n);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid nvte_convert_int32_to_int64_with_multiplier(const int32_t *src, int64_t *dst, size_t n,\n                                                 int64_t multiplier, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_convert_int32_to_int64_with_multiplier);\n  if (n == 0) return;\n  const int threads = 256;\n  const int blocks = static_cast<int>((n + threads - 1) / threads);\n  convert_int32_to_int64_with_multiplier_kernel<<<blocks, threads, 0, stream>>>(src, dst, n,\n                                                                                multiplier);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid nvte_compute_grouped_tensor_offsets(const int64_t *first_dims, int64_t *offsets,\n                                         size_t n_groups, int64_t last_dim, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_compute_grouped_tensor_offsets);\n  // Always write at least offsets[0]=0 (needed even for n_groups==0).\n  compute_grouped_tensor_offsets_kernel<<<1, 1, 0, stream>>>(first_dims, offsets, n_groups,\n                                                             last_dim);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n", "output": "/*************************************************************************\n* Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n*\n* See LICENSE for license information.\n************************************************************************/\n\n#include <hipblaslt.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/gemm.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <type_traits>\n#include <vector>\n\n#include \"../cast/mxfp8/swizzle.cuh\"\n#include \"../common.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/handle_manager.h\"\n#include \"../util/logging.h\"\n#include \"../util/vectorized_pointwise.h\"\n#include \"./config.h\"\n\nnamespace {\n\ninline void CreateCublasHandle(hipblasLtHandle_t *handle) {\n  NVTE_CHECK_CUBLAS(hipblasLtCreate(handle));\n}\n\n}  // namespace\n\n// MXFP8 support for grouped GEMM requires cuBLAS 13.3+\n#define CUBLAS_MXFP8_GROUPED_GEMM_VERSION 130300\n// BF16 support for grouped GEMM requires cuBLAS 13.3+\n#define CUBLAS_GROUPED_GEMM_VERSION 130300\n\n#if CUBLAS_VERSION >= CUBLAS_GROUPED_GEMM_VERSION\n\nnamespace {\n\n// Helper struct to pass per-tensor shape/offset info (pointer or uniform value)\nstruct TensorShapeInfo {\n  const int64_t *first_dims;  // nullptr if uniform\n  const int64_t *last_dims;   // nullptr if uniform\n  const int64_t *offsets;     // nullptr if need to compute\n  int64_t uniform_first;      // used if first_dims == nullptr\n  int64_t uniform_last;       // used if last_dims == nullptr\n\n  // Create from GroupedTensor\n  static TensorShapeInfo from_tensor(const transformer_engine::GroupedTensor *t) {\n    const bool has_first = t->first_dims.has_data();\n    const bool has_last = t->last_dims.has_data();\n    // When per-tensor dims are not provided, we must be in the uniform-shape case.\n    NVTE_CHECK(has_first || t->all_same_first_dim(),\n               \"GroupedTensor is missing first_dims for varying shapes\");\n    NVTE_CHECK(has_last || t->all_same_last_dim(),\n               \"GroupedTensor is missing last_dims for varying shapes\");\n\n    const int64_t *first_ptr =\n        has_first ? static_cast<const int64_t *>(t->first_dims.dptr) : nullptr;\n    const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(t->last_dims.dptr) : nullptr;\n\n    const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(t->get_common_first_dim());\n    const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(t->get_common_last_dim());\n\n    return {first_ptr, last_ptr,\n            t->tensor_offsets.has_data() ? static_cast<const int64_t *>(t->tensor_offsets.dptr)\n                                         : nullptr,\n            uniform_first, uniform_last};\n  }\n\n  // Create for C tensor (uses D's dimensions, only has offsets)\n  static TensorShapeInfo create_shape_info_for_C(const transformer_engine::GroupedTensor *C,\n                                                 const transformer_engine::GroupedTensor *D) {\n    const bool has_first = D->first_dims.has_data();\n    const bool has_last = D->last_dims.has_data();\n    NVTE_CHECK(has_first || D->all_same_first_dim(),\n               \"GroupedTensor D is missing first_dims for varying shapes\");\n    NVTE_CHECK(has_last || D->all_same_last_dim(),\n               \"GroupedTensor D is missing last_dims for varying shapes\");\n\n    const int64_t *first_ptr =\n        has_first ? static_cast<const int64_t *>(D->first_dims.dptr) : nullptr;\n    const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(D->last_dims.dptr) : nullptr;\n    const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(D->get_common_first_dim());\n    const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(D->get_common_last_dim());\n\n    return {first_ptr, last_ptr,\n            C->tensor_offsets.has_data() ? static_cast<const int64_t *>(C->tensor_offsets.dptr)\n                                         : nullptr,\n            uniform_first, uniform_last};\n  }\n};\n\n// Helper functions to compute average dimensions for cuBLASLt algorithm-selection heuristics.\n//\n// logical_shape encoding (from build_grouped_tensor):\n//   all_same:      {num_tensors * M, N}\n//   varying_first: {sum_of_first_dims, common_last}\n//   varying_last:  {common_first, sum_of_last_dims}\n//   varying_both:  {1, total_elements}   <-- lossy, can't recover per-dim averages\n//\n// We use all_same_first/last_dim() + get_common_first/last_dim() to get exact\n// answers whenever possible, falling back to logical_shape division otherwise.\n// For varying_both, per-dim averages are unrecoverable without a D2H copy,\n// so we return 1 \u2014 a valid non-zero hint that won't skip work.\ninline int64_t compute_avg_first_dim(const transformer_engine::GroupedTensor *t) {\n  if (t->all_same_first_dim()) {\n    return static_cast<int64_t>(t->get_common_first_dim());\n  }\n  const int64_t n = static_cast<int64_t>(t->num_tensors);\n  if (t->all_same_last_dim()) {\n    // varying_first only: logical_shape = {sum_of_first_dims, common_last}\n    return static_cast<int64_t>(t->logical_shape.data[0]) / n;\n  }\n  // varying_both: logical_shape = {1, total_elements}, no way to recover avg first dim\n  return 1;\n}\n\ninline int64_t compute_avg_last_dim(const transformer_engine::GroupedTensor *t) {\n  if (t->all_same_last_dim()) {\n    // logical_shape[1] is the common N\n    return static_cast<int64_t>(t->logical_shape.data[1]);\n  }\n  // When varying, logical_shape[1] should be sum of last dims if provided; otherwise fallback to avg via division.\n  return static_cast<int64_t>(t->logical_shape.data[1]) / static_cast<int64_t>(t->num_tensors);\n}\n\n// Constants for grouped GEMM workspace (declared early for use in helpers)\nstatic constexpr size_t kGroupedGemmAlignment = 256;\nstatic constexpr size_t kGroupedGemmCublasWorkspaceSize = 32ull * 1024 * 1024;  // 32 MiB\n\n// Workspace layout for grouped GEMM\nstruct GroupedGemmSetupWorkspace {\n  void **A_ptrs;\n  void **B_ptrs;\n  void **C_ptrs;\n  void **D_ptrs;\n  float **alpha_ptrs;\n  float **beta_ptrs;\n  void **\n      a_scale_inv_ptrs;  // Per-tensor FP8 scale pointers for A (float* for tensor scaling, E8M0* for MXFP8)\n  void **\n      b_scale_inv_ptrs;  // Per-tensor FP8 scale pointers for B (float* for tensor scaling, E8M0* for MXFP8)\n  // Storage dimensions for cuBLAS matrix layouts\n  int *a_rows;\n  int *a_cols;\n  int *b_rows;\n  int *b_cols;\n  int *d_rows;  // M (first dim) - also used for C\n  int *d_cols;  // N (last dim) - also used for C\n\n  // Initialize from workspace buffer\n  // Layout: all pointer arrays first (16-byte aligned for cuBLAS), then int arrays\n  static GroupedGemmSetupWorkspace from_buffers(char *setup_ws_ptr, size_t num_tensors) {\n    GroupedGemmSetupWorkspace ws;\n    size_t offset = 0;\n    const size_t ptr_size = num_tensors * sizeof(void *);\n    const size_t int_size = num_tensors * sizeof(int);\n    constexpr size_t kPtrAlignment = 16;  // cuBLAS requires 16-byte alignment for pointer arrays\n\n    // Helper to align offset to kPtrAlignment\n    auto align_offset = [&]() {\n      offset = (offset + kPtrAlignment - 1) / kPtrAlignment * kPtrAlignment;\n    };\n\n    // Pointer arrays first (all 16-byte aligned for cuBLAS grouped GEMM)\n    align_offset();\n    ws.A_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.B_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.C_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.D_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.alpha_ptrs = reinterpret_cast<float **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.beta_ptrs = reinterpret_cast<float **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.a_scale_inv_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n    align_offset();\n    ws.b_scale_inv_ptrs = reinterpret_cast<void **>(setup_ws_ptr + offset);\n    offset += ptr_size;\n\n    // Int arrays for storage dimensions (4-byte aligned is fine)\n    align_offset();\n    ws.a_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.a_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.b_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.b_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.d_rows = reinterpret_cast<int *>(setup_ws_ptr + offset);\n    offset += int_size;\n    ws.d_cols = reinterpret_cast<int *>(setup_ws_ptr + offset);\n\n    return ws;\n  }\n\n  // Calculate required size for setup workspace\n  static size_t required_setup_size(size_t num_tensors, size_t alignment) {\n    const size_t ptr_size = num_tensors * sizeof(void *);\n    const size_t int_size = num_tensors * sizeof(int);\n    constexpr size_t kPtrAlignment = 16;  // Must match from_buffers\n\n    // Layout: 8 ptr arrays (each 16-byte aligned), then 6 int arrays\n    // Each ptr array takes ptr_size bytes but needs to start at 16-byte boundary\n    auto aligned_ptr_size = ((ptr_size + kPtrAlignment - 1) / kPtrAlignment) * kPtrAlignment;\n    size_t size = 8 * aligned_ptr_size + 6 * int_size;\n    size = ((size + alignment - 1) / alignment) * alignment;\n    return size;\n  }\n};\n\ninline size_t validate_grouped_gemm_inputs(\n    size_t num_tensors, std::initializer_list<const transformer_engine::GroupedTensor *> inputs,\n    const transformer_engine::Tensor *alpha_tensor, const transformer_engine::Tensor *beta_tensor) {\n  NVTE_CHECK(num_tensors >= 1, \"Grouped GEMM: number of tensors must be at least 1\");\n  for (const auto *tensor : inputs) {\n    NVTE_CHECK(tensor->num_tensors == num_tensors,\n               \"Grouped GEMM: inputs must have the same number of tensors\");\n  }\n\n  const size_t alpha_numel = alpha_tensor->data.numel();\n  const size_t beta_numel = beta_tensor->data.numel();\n  NVTE_CHECK(alpha_numel == num_tensors, \"Grouped GEMM: alpha must have num_tensors (\", num_tensors,\n             \") elements, got \", alpha_numel);\n  NVTE_CHECK(beta_numel == num_tensors, \"Grouped GEMM: beta must have num_tensors (\", num_tensors,\n             \") elements, got \", beta_numel);\n\n  auto is_supported_input_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kFloat8E4M3 ||\n           dtype == transformer_engine::DType::kFloat8E5M2 ||\n           dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16;\n  };\n  for (const auto *tensor : inputs) {\n    if (tensor->has_data() || tensor->has_columnwise_data()) {\n      NVTE_CHECK(is_supported_input_dtype(tensor->dtype()),\n                 \"Grouped GEMM inputs must be FP8, BF16, or FP16, got \",\n                 transformer_engine::to_string(tensor->dtype()), \".\");\n    }\n  }\n  // Cross-operand consistency across all inputs (skip tensors without data).\n  const transformer_engine::GroupedTensor *ref = nullptr;\n  for (const auto *tensor : inputs) {\n    if (tensor->has_data() || tensor->has_columnwise_data()) {\n      ref = tensor;\n      break;\n    }\n  }\n  if (ref != nullptr) {\n    const bool ref_is_fp8 = is_fp8_dtype(ref->dtype());\n    const bool ref_is_mxfp8 = transformer_engine::is_mxfp_scaling(ref->scaling_mode);\n    for (const auto *tensor : inputs) {\n      if (!(tensor->has_data() || tensor->has_columnwise_data())) continue;\n      NVTE_CHECK(is_fp8_dtype(tensor->dtype()) == ref_is_fp8,\n                 \"Grouped GEMM: A and B must both be FP8 or both be non-FP8.\");\n      NVTE_CHECK(transformer_engine::is_mxfp_scaling(tensor->scaling_mode) == ref_is_mxfp8,\n                 \"Grouped GEMM: A and B must both use MXFP8 scaling or both use tensor scaling.\");\n      if (ref_is_mxfp8) {\n        NVTE_CHECK(tensor->with_gemm_swizzled_scales,\n                   \"MXFP8 grouped GEMM: scales must be swizzled for GEMM.\");\n      }\n    }\n  }\n  return num_tensors;\n}\n\ninline void validate_grouped_gemm_outputs(\n    size_t num_tensors, std::initializer_list<const transformer_engine::GroupedTensor *> outputs) {\n  auto is_output_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16 ||\n           dtype == transformer_engine::DType::kFloat32;\n  };\n  for (const auto *tensor : outputs) {\n    if (tensor == nullptr) {\n      continue;\n    }\n    NVTE_CHECK(tensor->num_tensors == num_tensors,\n               \"Grouped GEMM: outputs must have the same number of tensors as inputs\");\n    NVTE_CHECK(is_output_dtype(tensor->dtype()),\n               \"Grouped GEMM: outputs must be BF16, FP16, or FP32.\");\n  }\n}\n\ninline size_t grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  return GroupedGemmSetupWorkspace::required_setup_size(num_tensors, kGroupedGemmAlignment);\n}\n\ninline void check_grouped_gemm_requirements(const char *api_name) {\n  const int current_device = transformer_engine::cuda::current_device();\n  NVTE_CHECK(transformer_engine::cuda::sm_arch(current_device) >= 100, api_name,\n             \" requires Blackwell (SM100) or newer architecture.\");\n  NVTE_CHECK(transformer_engine::cuda::cublas_version() >= CUBLAS_GROUPED_GEMM_VERSION, api_name,\n             \" requires cuBLAS 13.3+, but run-time cuBLAS version is \",\n             transformer_engine::cuda::cublas_version());\n}\n\ninline transformer_engine::GroupedMatmulConfig parse_grouped_gemm_config(\n    NVTEGroupedMatmulConfig config) {\n  transformer_engine::GroupedMatmulConfig config_;\n  if (config != nullptr) {\n    config_ = *reinterpret_cast<transformer_engine::GroupedMatmulConfig *>(config);\n  }\n  return config_;\n}\n\n// Select row-wise vs column-wise storage and adjust transpose flag for grouped GEMM.\n// Mirrors the non-grouped GEMM logic for FP8 layout handling (TN-only on Hopper) and\n// fallback to column-wise data when row-wise is absent.\n// Contains all information needed for GEMM setup - shape already accounts for storage layout.\nstruct GroupedOperandSelection {\n  TensorShapeInfo shape;  // Shape info with dims already swapped for columnwise if needed\n  char *dptr = nullptr;\n  void *scale_inv = nullptr;  // Contiguous array of scales (input)\n  transformer_engine::DType dtype = transformer_engine::DType::kNumTypes;\n  NVTEScalingMode scaling_mode = NVTE_DELAYED_TENSOR_SCALING;\n  bool with_gemm_swizzled_scales = false;\n  bool trans = false;\n  bool rowwise = true;\n};\n\nconstexpr int kMaxGroups = 64;\n// Arguments for the grouped GEMM kernel that operates on multiple output tensors.\nstruct MultiTensorGroupGemmOutputArgs {\n  void *data_ptrs[kMaxGroups];\n  int rows[kMaxGroups];\n  int cols[kMaxGroups];\n};\n\n// Arguments for the grouped GEMM kernel that operates on multiple inputA tensors.\nstruct MultiTensorGroupGemmInputArgs {\n  void *data_ptrs[kMaxGroups];\n  void *scale_inv_ptrs[kMaxGroups];\n  int rows[kMaxGroups];\n  int cols[kMaxGroups];\n};\nstruct MultiTensorListInfo {\n  bool all_row = true;\n  bool all_col = true;\n  transformer_engine::DType row_dtype = transformer_engine::DType::kNumTypes;\n  transformer_engine::DType col_dtype = transformer_engine::DType::kNumTypes;\n  NVTEScalingMode scaling_mode = NVTE_DELAYED_TENSOR_SCALING;\n  bool with_gemm_swizzled_scales = false;\n};\n\nstruct OperandStorageChoice {\n  bool use_rowwise = true;\n  bool swap_dims = true;\n  bool trans = false;\n};\n\ninline OperandStorageChoice choose_grouped_operand_storage(bool trans, bool is_A, bool is_mxfp8,\n                                                           bool is_fp8, bool non_tn_fp8_ok,\n                                                           bool has_row, bool has_col,\n                                                           const char *name) {\n  NVTE_CHECK(has_row || has_col, \"Grouped GEMM: \", name,\n             \" is missing both row-wise and column-wise data\");\n  if (is_mxfp8) {\n    if (is_A) {\n      if (trans) {\n        NVTE_CHECK(has_row, \"Grouped GEMM: MXFP8 transposed \", name, \" is missing row-wise data\");\n        return {true, true, trans};\n      }\n      NVTE_CHECK(has_col, \"Grouped GEMM: MXFP8 non-transposed \", name,\n                 \" is missing column-wise data\");\n      return {false, false, trans};\n    }\n    if (trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: MXFP8 transposed \", name, \" is missing column-wise data\");\n      return {false, false, trans};\n    }\n    NVTE_CHECK(has_row, \"Grouped GEMM: MXFP8 non-transposed \", name, \" is missing row-wise data\");\n    return {true, true, trans};\n  }\n\n  // Hopper-style TN-only FP8: force TN by switching layout and flipping transpose when needed.\n  if (is_fp8 && !non_tn_fp8_ok) {\n    if (is_A && !trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: \", name,\n                 \" is missing column-wise data needed for FP8 TN layout\");\n      return {false, true, true};\n    }\n    if (!is_A && trans) {\n      NVTE_CHECK(has_col, \"Grouped GEMM: \", name,\n                 \" is missing column-wise data needed for FP8 TN layout\");\n      return {false, true, false};\n    }\n  }\n\n  // If only column-wise data is available, mirror the transpose flag (pre-transposed storage).\n  if (!has_row && has_col) {\n    NVTE_CHECK(!is_fp8 || non_tn_fp8_ok,\n               \"Grouped GEMM: FP8 on Hopper requires row-wise data for this transpose config.\");\n    return {false, true, !trans};\n  }\n\n  NVTE_CHECK(has_row, \"Grouped GEMM: \", name, \" is missing row-wise data\");\n  return {true, true, trans};\n}\n\n// Build Kernel Arguments detailing out addresses and other metadata for list of C/D tensors\n// passed to the grouped GEMM kernel. Use-case: C/D --> List of wgrads for experts in MOE\ninline MultiTensorGroupGemmOutputArgs build_grouped_gemm_multi_out_args(\n    const NVTETensor *tensor_list, size_t list_size, size_t expected_num_tensors,\n    transformer_engine::DType expected_dtype, const char *name) {\n  MultiTensorGroupGemmOutputArgs args{};\n  if (list_size == 0) {\n    NVTE_CHECK(tensor_list == nullptr, \"Grouped GEMM: \", name, \"_list provided with num_\", name,\n               \"_tensors=0\");\n    return args;\n  }\n  NVTE_CHECK(tensor_list != nullptr, \"Grouped GEMM: \", name, \"_list is null but num_\", name,\n             \"_tensors=\", list_size);\n  NVTE_CHECK(list_size == expected_num_tensors, \"Grouped GEMM: \", name,\n             \"_list must have num_tensors (\", expected_num_tensors, \") entries, got \", list_size);\n  NVTE_CHECK(list_size <= static_cast<size_t>(kMaxGroups), \"Grouped GEMM: \", name,\n             \"_list supports up to \", kMaxGroups, \" tensors per kernel, got \", list_size);\n\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    NVTE_CHECK(t->has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i, \" has no data\");\n    NVTE_CHECK(t->dtype() == expected_dtype, \"Grouped GEMM: \", name, \"_list tensor \", i,\n               \" dtype mismatch. Expected \", transformer_engine::to_string(expected_dtype), \" got \",\n               transformer_engine::to_string(t->dtype()));\n    const auto &shape = t->shape();\n    NVTE_CHECK(shape.size() == 2, \"Grouped GEMM: \", name, \"_list tensor \", i, \" must be 2D.\");\n    args.data_ptrs[i] = t->data.dptr;\n    args.rows[i] = static_cast<int>(shape[1]);\n    args.cols[i] = static_cast<int>(shape[0]);\n  }\n  return args;\n}\n\n// Build Kernel Arguments detailing out addresses and other metadata for list of A tensors\n// passed to the grouped GEMM kernel. Use-case: A --> List of Expert weights\ninline MultiTensorGroupGemmInputArgs build_grouped_gemm_multi_inputA_args(\n    const NVTETensor *tensor_list, size_t list_size, bool use_rowwise, bool is_fp8,\n    int64_t *avg_first_dim, int64_t *avg_last_dim, const char *name) {\n  using namespace transformer_engine;\n  MultiTensorGroupGemmInputArgs args{};\n  *avg_first_dim = 0;\n  *avg_last_dim = 0;\n  if (list_size == 0) {\n    return args;\n  }\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    const transformer_engine::SimpleTensor &data = use_rowwise ? t->data : t->columnwise_data;\n    const transformer_engine::SimpleTensor &scale_inv =\n        use_rowwise ? t->scale_inv : t->columnwise_scale_inv;\n    NVTE_CHECK(data.has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i,\n               \" is missing required data.\");\n    NVTE_CHECK(data.shape.size() == 2, \"Grouped GEMM: \", name, \"_list tensor \", i, \" must be 2D.\");\n    args.data_ptrs[i] = data.dptr;\n    args.rows[i] = static_cast<int>(data.shape[1]);\n    args.cols[i] = static_cast<int>(data.shape[0]);\n    *avg_first_dim += static_cast<int64_t>(data.shape[0]);\n    *avg_last_dim += static_cast<int64_t>(data.shape[1]);\n\n    if (is_fp8) {\n      NVTE_CHECK(scale_inv.has_data(), \"Grouped GEMM: \", name, \"_list tensor \", i,\n                 \" requires scale_inv for FP8.\");\n      args.scale_inv_ptrs[i] = scale_inv.dptr;\n    } else {\n      args.scale_inv_ptrs[i] = nullptr;\n    }\n  }\n  *avg_first_dim /= static_cast<int64_t>(list_size);\n  *avg_last_dim /= static_cast<int64_t>(list_size);\n  return args;\n}\n\ninline MultiTensorListInfo validate_grouped_gemm_multi_inputA_list(const NVTETensor *tensor_list,\n                                                                   size_t list_size,\n                                                                   size_t expected_num_tensors,\n                                                                   const char *name) {\n  using namespace transformer_engine;\n  MultiTensorListInfo info{};\n  if (list_size == 0) {\n    NVTE_CHECK(tensor_list == nullptr, \"Grouped GEMM: \", name, \"_list provided with num_\", name,\n               \"_tensors=0\");\n    return info;\n  }\n  NVTE_CHECK(tensor_list != nullptr, \"Grouped GEMM: \", name, \"_list is null but num_\", name,\n             \"_tensors=\", list_size);\n  NVTE_CHECK(list_size == expected_num_tensors, \"Grouped GEMM: \", name,\n             \"_list must have num_tensors (\", expected_num_tensors, \") entries, got \", list_size);\n  NVTE_CHECK(list_size <= static_cast<size_t>(kMaxGroups), \"Grouped GEMM: \", name,\n             \"_list supports up to \", kMaxGroups, \" tensors per kernel, got \", list_size);\n\n  const transformer_engine::Tensor *t0 = transformer_engine::convertNVTETensorCheck(tensor_list[0]);\n  info.scaling_mode = t0->scaling_mode;\n  info.with_gemm_swizzled_scales = t0->with_gemm_swizzled_scales;\n  const bool mxfp8 = transformer_engine::is_mxfp_scaling(info.scaling_mode);\n  NVTE_CHECK(info.scaling_mode == NVTE_DELAYED_TENSOR_SCALING || mxfp8,\n             \"Grouped GEMM: input list only supports tensor scaling or MXFP8.\");\n\n  for (size_t i = 0; i < list_size; ++i) {\n    const transformer_engine::Tensor *t =\n        transformer_engine::convertNVTETensorCheck(tensor_list[i]);\n    NVTE_CHECK(t->scaling_mode == info.scaling_mode, \"Grouped GEMM: \", name,\n               \"_list tensors must share the same scaling mode.\");\n    NVTE_CHECK(t->with_gemm_swizzled_scales == info.with_gemm_swizzled_scales,\n               \"Grouped GEMM: \", name, \"_list tensors must share GEMM swizzled scale state.\");\n\n    if (t->has_data()) {\n      if (info.row_dtype == DType::kNumTypes) {\n        info.row_dtype = t->data.dtype;\n      }\n      // Check all tensors have the same dtype\n      NVTE_CHECK(t->data.dtype == info.row_dtype, \"Grouped GEMM: \", name,\n                 \"_list rowwise dtypes must match.\");\n    } else {\n      // All tensors must have either data or columnwise data\n      info.all_row = false;\n    }\n\n    if (t->has_columnwise_data()) {\n      if (info.col_dtype == DType::kNumTypes) {\n        info.col_dtype = t->columnwise_data.dtype;\n      }\n      NVTE_CHECK(t->columnwise_data.dtype == info.col_dtype, \"Grouped GEMM: \", name,\n                 \"_list columnwise dtypes must match.\");\n    } else {\n      // All tensors must have either data or columnwise data\n      info.all_col = false;\n    }\n  }\n\n  return info;\n}\n\n// Helper to create TensorShapeInfo from a GroupedTensor, optionally swapping first/last dims.\n// When swap_dims=true, first_dims and last_dims are swapped to account for columnwise storage.\n// Note: tensor_offsets are the same for rowwise and columnwise data (same element count per tensor).\ninline TensorShapeInfo create_shape_info(const transformer_engine::GroupedTensor *t,\n                                         bool swap_dims) {\n  const bool has_first = t->first_dims.has_data();\n  const bool has_last = t->last_dims.has_data();\n  NVTE_CHECK(has_first || t->all_same_first_dim(),\n             \"GroupedTensor is missing first_dims for varying shapes\");\n  NVTE_CHECK(has_last || t->all_same_last_dim(),\n             \"GroupedTensor is missing last_dims for varying shapes\");\n\n  const int64_t *first_ptr = has_first ? static_cast<const int64_t *>(t->first_dims.dptr) : nullptr;\n  const int64_t *last_ptr = has_last ? static_cast<const int64_t *>(t->last_dims.dptr) : nullptr;\n  const int64_t uniform_first = has_first ? 0 : static_cast<int64_t>(t->get_common_first_dim());\n  const int64_t uniform_last = has_last ? 0 : static_cast<int64_t>(t->get_common_last_dim());\n\n  const int64_t *offsets_ptr =\n      t->tensor_offsets.has_data() ? static_cast<const int64_t *>(t->tensor_offsets.dptr) : nullptr;\n\n  if (swap_dims) {\n    // Swap first/last to account for columnwise (transposed) storage\n    return {last_ptr, first_ptr, offsets_ptr, uniform_last, uniform_first};\n  }\n  return {first_ptr, last_ptr, offsets_ptr, uniform_first, uniform_last};\n}\n\ninline GroupedOperandSelection select_grouped_operand(const transformer_engine::GroupedTensor *t,\n                                                      bool trans, bool is_A) {\n  using namespace transformer_engine;\n  const bool has_row = t->has_data();\n  const bool has_col = t->has_columnwise_data();\n\n  if (!has_row && !has_col) {\n    GroupedOperandSelection sel{};\n    sel.trans = trans;\n    sel.scaling_mode = t->scaling_mode;\n    sel.dtype = t->dtype();\n    sel.shape = create_shape_info(t, /*swap_dims=*/false);\n    return sel;\n  }\n\n  const auto sm = t->scaling_mode;\n  const bool mxfp8 = is_mxfp_scaling(sm);\n\n  // Validate scaling mode\n  NVTE_CHECK(sm == NVTE_DELAYED_TENSOR_SCALING || mxfp8,\n             \"Grouped GEMM is only supported with bf16, fp8 tensor scaling and MXFP8\");\n\n  const DType row_dtype = t->data.dtype;\n  const DType col_dtype = t->columnwise_data.dtype;\n  GroupedOperandSelection sel{};\n  sel.trans = trans;\n  sel.scaling_mode = sm;\n  sel.with_gemm_swizzled_scales = t->with_gemm_swizzled_scales;\n\n  const DType rep_dtype = has_row ? row_dtype : col_dtype;\n  const bool is_fp8 = is_fp8_dtype(rep_dtype);\n  const bool non_tn_fp8_ok = nvte_is_non_tn_fp8_gemm_supported();\n\n  // Helper to select columnwise storage.\n  // swap_dims=true (default): swap first/last dims in shape info (used when columnwise == transposed).\n  // swap_dims=false: keep original dims (MXFP8: columnwise data has different scale direction,\n  //                  but the logical matrix shape and transpose flag remain unchanged).\n  auto use_columnwise = [&](bool swap_dims = true) {\n    sel.dptr = static_cast<char *>(t->columnwise_data.dptr);\n    sel.scale_inv = t->columnwise_scale_inv.dptr;\n    sel.dtype = col_dtype;\n    sel.rowwise = false;\n    sel.shape = create_shape_info(t, swap_dims);\n  };\n\n  // Helper to select row-wise storage\n  auto use_rowwise = [&]() {\n    sel.dptr = static_cast<char *>(t->data.dptr);\n    sel.scale_inv = t->scale_inv.dptr;\n    sel.dtype = row_dtype;\n    sel.rowwise = true;\n    sel.shape = create_shape_info(t, /*swap_dims=*/false);\n  };\n\n  const auto choice = choose_grouped_operand_storage(trans, is_A, mxfp8, is_fp8, non_tn_fp8_ok,\n                                                     has_row, has_col, is_A ? \"A\" : \"B\");\n  sel.trans = choice.trans;\n  if (choice.use_rowwise) {\n    use_rowwise();\n  } else {\n    use_columnwise(choice.swap_dims);\n  }\n  return sel;\n}\n\ninline void *validate_and_get_workspace_ptr(transformer_engine::Tensor *ws, size_t required_size,\n                                            const char *workspace_name) {\n  NVTE_CHECK(ws != nullptr, workspace_name, \" tensor is null.\");\n  const size_t provided_size = get_buffer_size_bytes(ws->data.numel(), ws->data.dtype);\n  NVTE_CHECK(provided_size >= required_size, \"Grouped GEMM: Insufficient \", workspace_name,\n             \". Required: \", required_size, \" bytes, Available: \", provided_size, \" bytes.\");\n  return ws->data.dptr;\n}\n\ninline void init_matrix_layouts(\n    hipblasLtMatrixLayoutOpaque_t &descA, hipblasLtMatrixLayoutOpaque_t &descB,\n    hipblasLtMatrixLayoutOpaque_t &descC, hipblasLtMatrixLayoutOpaque_t &descD,\n    const GroupedGemmSetupWorkspace &ws, const GroupedOperandSelection &A_sel,\n    const GroupedOperandSelection &B_sel, transformer_engine::DType d_dtype, size_t num_tensors) {\n  const hipDataType A_type = get_cuda_dtype(A_sel.dtype);\n  const hipDataType B_type = get_cuda_dtype(B_sel.dtype);\n  const hipDataType D_type = get_cuda_dtype(d_dtype);\n\n  // Storage dimensions computed by kernel, leading dimension = rows\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descA, A_type, num_tensors, ws.a_rows,\n                                                    ws.a_cols, ws.a_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descB, B_type, num_tensors, ws.b_rows,\n                                                    ws.b_cols, ws.b_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descC, D_type, num_tensors, ws.d_rows,\n                                                    ws.d_cols, ws.d_rows));\n  NVTE_CHECK_CUBLAS(cublasLtGroupedMatrixLayoutInit(&descD, D_type, num_tensors, ws.d_rows,\n                                                    ws.d_cols, ws.d_rows));\n}\n\ninline void init_matmul_desc(hipblasLtMatmulDescOpaque_t &matmulDesc, hipblasOperation_t op_A,\n                             hipblasOperation_t op_B, bool use_fp8, bool use_split_accumulator) {\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescInit(&matmulDesc, HIPBLAS_COMPUTE_32F, HIP_R_32F));\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_TRANSA, &op_A,\n                                                   sizeof(op_A)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_TRANSB, &op_B,\n                                                   sizeof(op_B)));\n\n  hipblasLtPointerMode_t pointer_mode = HIPBLASLT_POINTER_MODE_DEVICE;\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_POINTER_MODE,\n                                                   &pointer_mode, sizeof(pointer_mode)));\n\n  int64_t alphabeta_batch_stride = 1;\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_ALPHA_BATCH_STRIDE,\n                                                   &alphabeta_batch_stride, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   CUBLASLT_MATMUL_DESC_BETA_BATCH_STRIDE,\n                                                   &alphabeta_batch_stride, sizeof(int64_t)));\n\n  // Fast accumulation is only supported for FP8 (mirrors non-grouped GEMM logic).\n  int8_t fastAccuMode = use_split_accumulator ? 0 : static_cast<int8_t>(use_fp8);\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, CUBLASLT_MATMUL_DESC_FAST_ACCUM,\n                                                   &fastAccuMode, sizeof(fastAccuMode)));\n}\n\n// Configures cuBLAS for MXFP8 grouped GEMM: sets VEC32_UE8M0 scale mode and scale pointers\n// for both A and B.\ninline void set_mxfp8_scale_pointers(hipblasLtMatmulDescOpaque_t &matmulDesc,\n                                     void **a_scale_inv_ptrs, void **b_scale_inv_ptrs) {\n#if CUBLAS_VERSION >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION\n  NVTE_CHECK(transformer_engine::cuda::cublas_version() >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"MXFP8 grouped GEMM requires cuBLAS \", CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"+, but run-time cuBLAS version is \", transformer_engine::cuda::cublas_version());\n  const hipblasLtMatmulMatrixScale_t scale_mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                   &a_scale_inv_ptrs, sizeof(a_scale_inv_ptrs)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                   &b_scale_inv_ptrs, sizeof(b_scale_inv_ptrs)));\n#else\n  NVTE_CHECK(false, \"MXFP8 grouped GEMM requires cuBLAS \", CUBLAS_MXFP8_GROUPED_GEMM_VERSION,\n             \"+, but compile-time cuBLAS version is \", CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= CUBLAS_MXFP8_GROUPED_GEMM_VERSION\n}\n\n// Configures cuBLAS for tensor-scaling FP8 grouped GEMM: sets PER_BATCH_SCALAR_32F scale mode\n// and scale pointers for A and B. Both operands are guaranteed FP8 by the caller.\ninline void set_fp8_scale_pointers(hipblasLtMatmulDescOpaque_t &matmulDesc, void **a_scale_inv_ptrs,\n                                   void **b_scale_inv_ptrs) {\n  const hipblasLtMatmulMatrixScale_t scale_mode = CUBLASLT_MATMUL_MATRIX_SCALE_PER_BATCH_SCALAR_32F;\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                   &a_scale_inv_ptrs, sizeof(a_scale_inv_ptrs)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc, HIPBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                   &scale_mode, sizeof(scale_mode)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(&matmulDesc,\n                                                   HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                   &b_scale_inv_ptrs, sizeof(b_scale_inv_ptrs)));\n}\ninline hipblasLtMatmulAlgo_t select_grouped_gemm_algo(hipblasLtHandle_t handle,\n                                                     hipblasLtMatmulDescOpaque_t &matmulDesc,\n                                                     hipblasLtMatrixLayoutOpaque_t &descA,\n                                                     hipblasLtMatrixLayoutOpaque_t &descB,\n                                                     hipblasLtMatrixLayoutOpaque_t &descC,\n                                                     hipblasLtMatrixLayoutOpaque_t &descD,\n                                                     int64_t avg_m, int64_t avg_n, int64_t avg_k) {\n  hipblasLtMatmulPreferenceOpaque_t preference;\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceInit(&preference));\n  NVTE_CHECK_CUBLAS(\n      hipblasLtMatmulPreferenceSetAttribute(&preference, HIPBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES,\n                                           &kGroupedGemmCublasWorkspaceSize, sizeof(size_t)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_DESC_D_AVERAGE_ROWS, &avg_m, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_DESC_D_AVERAGE_COLS, &avg_n, sizeof(int64_t)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      &preference, CUBLASLT_MATMUL_PREF_GROUPED_AVERAGE_REDUCTION_DIM, &avg_k, sizeof(int64_t)));\n\n  hipblasLtMatmulHeuristicResult_t heuristicResult;\n  int returnedResults = 0;\n  auto status = hipblasLtMatmulAlgoGetHeuristic(handle, &matmulDesc, &descA, &descB, &descC, &descD,\n                                               &preference, 1, &heuristicResult, &returnedResults);\n  NVTE_CHECK(status != HIPBLAS_STATUS_NOT_SUPPORTED,\n             \"Unable to find suitable cuBLAS grouped GEMM algorithm\");\n  NVTE_CHECK_CUBLAS(status);\n  NVTE_CHECK(returnedResults > 0, \"No suitable algorithm found for grouped GEMM\");\n  return heuristicResult.algo;\n}\n\nstruct GroupedGemmWorkspace {\n  GroupedGemmSetupWorkspace setup_workspace;\n  void *cublas_workspace_ptr = nullptr;\n  size_t num_tensors = 0;\n};\n\ninline GroupedGemmWorkspace setup_grouped_gemm_workspace(transformer_engine::Tensor *wspace_setup,\n                                                         transformer_engine::Tensor *wspace_cublas,\n                                                         size_t num_tensors) {\n  const size_t setup_workspace_size = grouped_gemm_setup_workspace_size(num_tensors);\n  const size_t cublas_workspace_size = kGroupedGemmCublasWorkspaceSize;\n  void *setup_workspace_ptr = validate_and_get_workspace_ptr(wspace_setup, setup_workspace_size,\n                                                             \"Grouped GEMM setup workspace\");\n  void *cublas_workspace_ptr = validate_and_get_workspace_ptr(wspace_cublas, cublas_workspace_size,\n                                                              \"Grouped GEMM cuBLAS workspace\");\n  auto setup_workspace = GroupedGemmSetupWorkspace::from_buffers(\n      static_cast<char *>(setup_workspace_ptr), num_tensors);\n  return {std::move(setup_workspace), cublas_workspace_ptr, num_tensors};\n}\n\ninline void execute_grouped_gemm(const GroupedGemmSetupWorkspace &setup_workspace,\n                                 const GroupedOperandSelection &A_sel,\n                                 const GroupedOperandSelection &B_sel,\n                                 transformer_engine::DType d_dtype, size_t num_tensors,\n                                 bool use_split_accumulator, bool use_fp8, int64_t avg_m_val,\n                                 int64_t avg_n_val, int64_t avg_k_val, void *cublas_workspace_ptr,\n                                 hipStream_t stream, int math_sm_count = 0) {\n  using cublasHandleManager =\n      transformer_engine::detail::HandleManager<hipblasLtHandle_t, CreateCublasHandle>;\n  hipblasLtHandle_t handle = cublasHandleManager::Instance().GetHandle();\n\n  hipblasOperation_t op_A = A_sel.trans ? HIPBLAS_OP_T : HIPBLAS_OP_N;\n  hipblasOperation_t op_B = B_sel.trans ? HIPBLAS_OP_T : HIPBLAS_OP_N;\n\n  hipblasLtMatrixLayoutOpaque_t descA, descB, descC, descD;\n  init_matrix_layouts(descA, descB, descC, descD, setup_workspace, A_sel, B_sel, d_dtype,\n                      num_tensors);\n\n  hipblasLtMatmulDescOpaque_t matmulDesc;\n  init_matmul_desc(matmulDesc, op_A, op_B, use_fp8, use_split_accumulator);\n  if (transformer_engine::is_mxfp_scaling(A_sel.scaling_mode)) {\n    set_mxfp8_scale_pointers(matmulDesc, setup_workspace.a_scale_inv_ptrs,\n                             setup_workspace.b_scale_inv_ptrs);\n  } else if (use_fp8) {\n    set_fp8_scale_pointers(matmulDesc, setup_workspace.a_scale_inv_ptrs,\n                           setup_workspace.b_scale_inv_ptrs);\n  }\n  if (math_sm_count != 0) {\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        &matmulDesc, CUBLASLT_MATMUL_DESC_SM_COUNT_TARGET, &math_sm_count, sizeof(math_sm_count)));\n  }\n  hipblasLtMatmulAlgo_t algo = select_grouped_gemm_algo(handle, matmulDesc, descA, descB, descC,\n                                                       descD, avg_m_val, avg_n_val, avg_k_val);\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatmul(handle, &matmulDesc, setup_workspace.alpha_ptrs,\n                                   setup_workspace.A_ptrs, &descA, setup_workspace.B_ptrs, &descB,\n                                   setup_workspace.beta_ptrs, setup_workspace.C_ptrs, &descC,\n                                   setup_workspace.D_ptrs, &descD, &algo, cublas_workspace_ptr,\n                                   kGroupedGemmCublasWorkspaceSize, stream));\n}\n\n// Device helper: compute the element offset for tensor `idx` given shape metadata.\n// Three cases:\n//   1. Explicit per-tensor offset array provided  \u2192 use it directly.\n//   2. Per-tensor first/last dims provided but no offsets \u2192 cumulative sum of (first*last) products.\n//   3. Fully uniform shapes                        \u2192 idx * uniform_first * uniform_last.\n__forceinline__ __device__ int64_t compute_grouped_tensor_offset(const TensorShapeInfo &meta,\n                                                                 size_t idx) {\n  if (meta.offsets) {\n    return meta.offsets[idx];\n  } else if (meta.first_dims != nullptr || meta.last_dims != nullptr) {\n    // offset[i] = sum_{j < i} (first_dims[j] * last_dims[j])\n    int64_t cumsum = 0;\n    for (size_t i = 0; i < idx; i++) {\n      int64_t f = meta.first_dims ? meta.first_dims[i] : meta.uniform_first;\n      int64_t l = meta.last_dims ? meta.last_dims[i] : meta.uniform_last;\n      cumsum += f * l;\n    }\n    return cumsum;\n  } else {\n    return static_cast<int64_t>(idx) * meta.uniform_first * meta.uniform_last;\n  }\n}\n\n__forceinline__ __device__ int64_t padded_mxfp8_scale_inv_bytes(int64_t first, int64_t last,\n                                                                bool rowwise) {\n  namespace mxfp8_swizzle = transformer_engine::dispatch::mxfp8::swizzle;\n  constexpr int64_t kMxfp8BlockSize = 32;\n  // x is the dimension along which quantization is applied, y is other dimension\n  const int64_t scale_tile_y = static_cast<int64_t>(mxfp8_swizzle::GEMM_SWIZZLED_SCALE_TILE_DIM_Y);\n  const int64_t scale_tile_x = static_cast<int64_t>(mxfp8_swizzle::GEMM_SWIZZLED_SCALE_TILE_DIM_X);\n  // Padded byte size of the swizzled MXFP8 scale_inv for a single tensor with data\n  // shape (first, last). Rowwise scales use rows=first, cols=last; columnwise\n  // scales swap the orientation since they are stored in column-major order.\n  const int64_t scale_dim_y = rowwise ? first : last;\n  const int64_t padded_scale_dim_y =\n      ((scale_dim_y + scale_tile_y - 1) / scale_tile_y) * scale_tile_y;\n  const int64_t data_dim_x = rowwise ? last : first;\n  const int64_t scale_dim_x = (data_dim_x + kMxfp8BlockSize - 1) / kMxfp8BlockSize;\n  const int64_t padded_scale_dim_x =\n      ((scale_dim_x + scale_tile_x - 1) / scale_tile_x) * scale_tile_x;\n  // MXFP8 scales are E8M0 (1 byte per element), so element count == byte count.\n  return padded_scale_dim_y * padded_scale_dim_x;\n}\n\n// Device helper: byte offset into a contiguous grouped MXFP8 scale_inv buffer for\n// tensor `idx`. Each expert's scale_inv is expected to be padded\n// to the 128x4 swizzled layout.\n__forceinline__ __device__ int64_t compute_grouped_tensor_mxfp8_scale_inv_offset(\n    const TensorShapeInfo &meta, size_t idx, bool rowwise) {\n  if (meta.first_dims != nullptr || meta.last_dims != nullptr) {\n    int64_t cumsum = 0;\n    for (size_t i = 0; i < idx; i++) {\n      const int64_t f = meta.first_dims ? meta.first_dims[i] : meta.uniform_first;\n      const int64_t l = meta.last_dims ? meta.last_dims[i] : meta.uniform_last;\n      cumsum += padded_mxfp8_scale_inv_bytes(f, l, rowwise);\n    }\n    return cumsum;\n  }\n  return static_cast<int64_t>(idx) *\n         padded_mxfp8_scale_inv_bytes(meta.uniform_first, meta.uniform_last, rowwise);\n}\n\n// Linear scan to find which tensor contains the given row.\n// Returns the tensor index and writes the exclusive end-row of that tensor to *out_tensor_row_end.\n__forceinline__ __device__ int find_tensor_for_row(const int64_t *first_dims, int64_t uniform_first,\n                                                   int row, int num_tensors,\n                                                   int *out_tensor_row_end) {\n  int offset = 0;\n  for (int i = 0; i < num_tensors; i++) {\n    int dim = first_dims ? static_cast<int>(first_dims[i]) : static_cast<int>(uniform_first);\n    offset += dim;\n    if (row < offset) {\n      *out_tensor_row_end = offset;\n      return i;\n    }\n  }\n  *out_tensor_row_end = offset;\n  return num_tensors - 1;\n}\n\n// Kernel that performs (optionally scaled) bias addition to Grouped GEMM output tensors.\n// SM-filling grid with grid-stride over row chunks.\n// 2D grid: blockIdx.x = SM-filling row blocks, blockIdx.y = column chunk.\n// Each block grid-strides over kRowsPerBlock-sized row chunks, processing\n// all chunks that map to it. Safe when sum(first_dims) <= total_rows.\ntemplate <typename T, int kVec, bool UseScale, int kBlockDim, int kRowsPerBlock>\n__global__ void grouped_bias_add_kernel(char *__restrict__ d_base,\n                                        const char *__restrict__ bias_base,\n                                        const float *__restrict__ scale_base,\n                                        TensorShapeInfo d_meta, int n, int total_rows,\n                                        int num_tensors) {\n  using VecStorage = transformer_engine::VectorizedStorage<T, kVec>;\n  using VecType = typename VecStorage::LType;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  const int row_bid = static_cast<int>(blockIdx.x);\n  const int col_bid = static_cast<int>(blockIdx.y);\n  const int row_grid_stride = static_cast<int>(gridDim.x);\n\n  // Single-warp reduction to compute valid_rows = sum(first_dims).\n  // kMaxGroups <= 64 so warp 0 (32 lanes) covers it with <=2 loads each.\n  __shared__ int s_valid_rows;\n  if (tid < 32) {\n    int local_sum = 0;\n    for (int i = tid; i < num_tensors; i += 32) {\n      local_sum += d_meta.first_dims ? static_cast<int>(d_meta.first_dims[i])\n                                     : static_cast<int>(d_meta.uniform_first);\n    }\n    for (int offset = 16; offset > 0; offset >>= 1) {\n      local_sum += __shfl_down_sync(0xffffffff, local_sum, offset);\n    }\n    if (tid == 0) s_valid_rows = local_sum;\n  }\n  __syncthreads();\n  const int valid_rows = s_valid_rows;\n\n  const int block_cols = kBlockDim * kVec;\n  const int col = col_bid * block_cols + tid * kVec;\n  if (col >= n) return;\n\n  T *__restrict__ d = reinterpret_cast<T *>(d_base);\n  const T *__restrict__ bias = reinterpret_cast<const T *>(bias_base);\n\n  // Grid-stride loop over row chunks.\n  for (int chunk_start = row_bid * kRowsPerBlock; chunk_start < valid_rows;\n       chunk_start += row_grid_stride * kRowsPerBlock) {\n    const int row_start = chunk_start;\n    const int row_end = min(row_start + kRowsPerBlock, valid_rows);\n\n    // Linear scan to find the starting row's tensor and its boundary.\n    int tensor_row_end;\n    int tensor_idx = find_tensor_for_row(d_meta.first_dims, d_meta.uniform_first, row_start,\n                                         num_tensors, &tensor_row_end);\n    int bias_idx = tensor_idx * n;\n\n    VecStorage b_in;\n\n    // Walk tensor segments within this chunk's row range.\n    int seg_start = row_start;\n    while (seg_start < row_end) {\n      while (tensor_idx < num_tensors - 1 && tensor_row_end <= seg_start) {\n        tensor_idx++;\n        bias_idx += n;\n        int dim = d_meta.first_dims ? static_cast<int>(d_meta.first_dims[tensor_idx])\n                                    : static_cast<int>(d_meta.uniform_first);\n        tensor_row_end += dim;\n      }\n      b_in.scratch_.aligned = *reinterpret_cast<const VecType *>(bias + bias_idx + col);\n      const int seg_end = min(tensor_row_end, row_end);\n\n      for (int row = seg_start; row < seg_end; row++) {\n        T *d_ptr = d + row * n + col;\n        VecStorage d_in;\n        d_in.scratch_.aligned = *reinterpret_cast<const VecType *>(d_ptr);\n\n        [[maybe_unused]] float s_val;\n        if constexpr (UseScale) s_val = scale_base[row];\n\n#pragma unroll\n        for (int i = 0; i < kVec; ++i) {\n          if constexpr (UseScale) {\n            d_in.scratch_.separate[i] =\n                static_cast<T>(fmaf(static_cast<float>(b_in.scratch_.separate[i]), s_val,\n                                    static_cast<float>(d_in.scratch_.separate[i])));\n          } else {\n            d_in.scratch_.separate[i] =\n                static_cast<T>(static_cast<float>(d_in.scratch_.separate[i]) +\n                               static_cast<float>(b_in.scratch_.separate[i]));\n          }\n        }\n        *reinterpret_cast<VecType *>(d_ptr) = d_in.scratch_.aligned;\n      }\n\n      seg_start = seg_end;\n    }\n  }\n}\n\n// Single kernel that sets up all GEMM parameters.\n// Rationale: cuBLASLt grouped matmul API needs flat arrays of pointers and per-matrix dimensions,\n// but NVTEGroupedTensor stores a single contiguous buffer + optional per-tensor offsets/shapes.\n// We bridge the mismatch on GPU by computing per-group pointers and storage dims in one kernel.\n__global__ void setup_grouped_gemm_kernel(\n    // Output arrays\n    void **A_ptrs, void **B_ptrs, void **C_ptrs, void **D_ptrs, int *a_rows, int *a_cols,\n    int *b_rows, int *b_cols, int *d_rows, int *d_cols, float **alpha_ptrs, float **beta_ptrs,\n    void **a_scale_inv_ptrs, void **b_scale_inv_ptrs,\n    // Inputs\n    char *a_base, char *b_base, char *c_base, char *d_base, TensorShapeInfo A_meta,\n    TensorShapeInfo B_meta, TensorShapeInfo C_meta, TensorShapeInfo D_meta, size_t a_elem_size,\n    size_t b_elem_size, size_t c_elem_size, size_t d_elem_size, float *alpha_ptr, float *beta_ptr,\n    // Scale inputs: for tensor scaling, pass float* and set mxfp8_base to nullptr\n    // For MXFP8, pass nullptr for tensor_scale and set mxfp8_base\n    float *a_scale_base, float *b_scale_base, bool a_rowwise, bool b_rowwise,\n    NVTEScalingMode scaling_mode, size_t num_tensors,\n    MultiTensorGroupGemmInputArgs a_multi_tensor_args,\n    MultiTensorGroupGemmOutputArgs c_multi_tensor_args,\n    MultiTensorGroupGemmOutputArgs d_multi_tensor_args) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= num_tensors) return;\n\n  // Get dimensions for this tensor (from array or uniform value)\n  const bool has_a_multi_tensor = (a_base == nullptr);\n  const bool has_c_multi_tensor = (c_base == nullptr);\n  const bool has_d_multi_tensor = (d_base == nullptr);\n  int64_t a_first = 0;\n  int64_t a_last = 0;\n  if (has_a_multi_tensor) {\n    a_first = static_cast<int64_t>(a_multi_tensor_args.cols[idx]);\n    a_last = static_cast<int64_t>(a_multi_tensor_args.rows[idx]);\n  } else {\n    a_first = A_meta.first_dims ? A_meta.first_dims[idx] : A_meta.uniform_first;\n    a_last = A_meta.last_dims ? A_meta.last_dims[idx] : A_meta.uniform_last;\n  }\n  int64_t b_first = B_meta.first_dims ? B_meta.first_dims[idx] : B_meta.uniform_first;\n  int64_t b_last = B_meta.last_dims ? B_meta.last_dims[idx] : B_meta.uniform_last;\n  int64_t d_first = D_meta.first_dims ? D_meta.first_dims[idx] : D_meta.uniform_first;\n  int64_t d_last = D_meta.last_dims ? D_meta.last_dims[idx] : D_meta.uniform_last;\n\n  // Compute offsets (from explicit array, cumulative from per-tensor dims, or uniform)\n  int64_t a_offset = has_a_multi_tensor ? 0 : compute_grouped_tensor_offset(A_meta, idx);\n  int64_t b_offset = compute_grouped_tensor_offset(B_meta, idx);\n  int64_t c_offset = compute_grouped_tensor_offset(C_meta, idx);\n  int64_t d_offset = compute_grouped_tensor_offset(D_meta, idx);\n\n  // Compute data pointers\n  A_ptrs[idx] =\n      has_a_multi_tensor ? a_multi_tensor_args.data_ptrs[idx] : (a_base + a_offset * a_elem_size);\n  B_ptrs[idx] = b_base + b_offset * b_elem_size;\n  C_ptrs[idx] =\n      has_c_multi_tensor ? c_multi_tensor_args.data_ptrs[idx] : (c_base + c_offset * c_elem_size);\n  D_ptrs[idx] =\n      has_d_multi_tensor ? d_multi_tensor_args.data_ptrs[idx] : (d_base + d_offset * d_elem_size);\n\n  // Compute storage dimensions for cuBLAS matrix layouts.\n  // For INPUTS (A, B): Row-wise storage is seen as transposed column-major by cuBLAS,\n  // so rows=last, cols=first. For columnwise, dims are already swapped.\n  a_rows[idx] = static_cast<int>(a_last);\n  a_cols[idx] = static_cast<int>(a_first);\n  b_rows[idx] = static_cast<int>(b_last);\n  b_cols[idx] = static_cast<int>(b_first);\n  if (has_d_multi_tensor) {\n    d_rows[idx] = d_multi_tensor_args.rows[idx];\n    d_cols[idx] = d_multi_tensor_args.cols[idx];\n  } else {\n    d_rows[idx] = static_cast<int>(d_last);\n    d_cols[idx] = static_cast<int>(d_first);\n  }\n\n  // Fill alpha/beta pointers (per-matrix)\n  alpha_ptrs[idx] = alpha_ptr + idx;\n  beta_ptrs[idx] = beta_ptr + idx;\n\n  // Fill scale pointers (per-matrix).\n  // The interpretation of the scale buffers depends on the shared scaling recipe:\n  //   otherwise             : one float per tensor, indexed by tensor index\n  if (a_scale_base) {\n    if (scaling_mode == NVTE_MXFP8_1D_SCALING) {\n      const int64_t a_scale_offset =\n          compute_grouped_tensor_mxfp8_scale_inv_offset(A_meta, idx, a_rowwise);\n      a_scale_inv_ptrs[idx] = reinterpret_cast<void *>(\n          static_cast<char *>(static_cast<void *>(a_scale_base)) + a_scale_offset);\n    } else {\n      a_scale_inv_ptrs[idx] = static_cast<float *>(a_scale_base) + idx;\n    }\n  } else {\n    a_scale_inv_ptrs[idx] = a_multi_tensor_args.scale_inv_ptrs[idx];\n  }\n  if (b_scale_base) {\n    if (scaling_mode == NVTE_MXFP8_1D_SCALING) {\n      const int64_t b_scale_offset =\n          compute_grouped_tensor_mxfp8_scale_inv_offset(B_meta, idx, b_rowwise);\n      b_scale_inv_ptrs[idx] = reinterpret_cast<void *>(\n          static_cast<char *>(static_cast<void *>(b_scale_base)) + b_scale_offset);\n    } else {\n      b_scale_inv_ptrs[idx] = static_cast<float *>(b_scale_base) + idx;\n    }\n  }\n}\n\n// Launch the setup kernel to populate workspace arrays\ninline void launch_grouped_gemm_setup(\n    const GroupedGemmSetupWorkspace &ws, const GroupedOperandSelection &A_sel,\n    const GroupedOperandSelection &B_sel, const transformer_engine::GroupedTensor *C,\n    const transformer_engine::GroupedTensor *D, const transformer_engine::Tensor *alpha_tensor,\n    const transformer_engine::Tensor *beta_tensor, size_t num_tensors, hipStream_t stream,\n    const MultiTensorGroupGemmInputArgs &a_multi_tensor_args, const NVTETensor *C_list,\n    const NVTETensor *D_list, char *a_base, transformer_engine::DType c_dtype,\n    transformer_engine::DType d_dtype) {\n  // Use shape info from selection (already accounts for columnwise dimension swap)\n  TensorShapeInfo A_meta = A_sel.shape;\n  TensorShapeInfo B_meta = B_sel.shape;\n  TensorShapeInfo C_meta{};\n  TensorShapeInfo D_meta{};\n\n  const bool has_d_multi_tensor = (D_list != nullptr);\n  const bool has_c_multi_tensor = (C_list != nullptr) || has_d_multi_tensor;\n  MultiTensorGroupGemmOutputArgs c_multi_tensor_args{};\n  MultiTensorGroupGemmOutputArgs d_multi_tensor_args{};\n  if (has_d_multi_tensor) {\n    d_multi_tensor_args =\n        build_grouped_gemm_multi_out_args(D_list, num_tensors, num_tensors, d_dtype, \"D\");\n  }\n  if (C_list != nullptr) {\n    c_multi_tensor_args =\n        build_grouped_gemm_multi_out_args(C_list, num_tensors, num_tensors, d_dtype, \"C\");\n  } else if (has_d_multi_tensor) {\n    c_multi_tensor_args = d_multi_tensor_args;\n  }\n\n  char *c_base = nullptr;\n  char *d_base = nullptr;\n\n  if (!has_c_multi_tensor) {\n    NVTE_CHECK(C != nullptr && D != nullptr,\n               \"Grouped GEMM: C/D grouped tensors are required when no C list is provided\");\n    C_meta = TensorShapeInfo::create_shape_info_for_C(C, D);\n    c_base = static_cast<char *>(C->data.dptr);\n  }\n  if (!has_d_multi_tensor) {\n    NVTE_CHECK(D != nullptr,\n               \"Grouped GEMM: D grouped tensor is required when no D list is provided\");\n    D_meta = TensorShapeInfo::from_tensor(D);\n    d_base = static_cast<char *>(D->data.dptr);\n  }\n\n  const size_t a_elem_size = transformer_engine::typeToSize(A_sel.dtype);\n  const size_t b_elem_size = transformer_engine::typeToSize(B_sel.dtype);\n  const size_t c_elem_size = transformer_engine::typeToSize(c_dtype);\n  const size_t d_elem_size = transformer_engine::typeToSize(d_dtype);\n\n  const int threads_per_block = 256;\n  const int num_blocks = (num_tensors + threads_per_block - 1) / threads_per_block;\n\n  // A and B share the same scaling recipe (validated in validate_grouped_gemm_inputs).\n  // Pass scale buffers as void* and let the kernel interpret them via scaling_mode.\n\n  // Scale rowwise flag for MXFP8/NVFP4: to calculate scale_inv padding based offsets\n  // within kernel. Ignored for tensor scaling.\n  const bool a_rowwise = A_sel.rowwise;\n  const bool b_rowwise = B_sel.rowwise;\n  setup_grouped_gemm_kernel<<<num_blocks, threads_per_block, 0, stream>>>(\n      ws.A_ptrs, ws.B_ptrs, ws.C_ptrs, ws.D_ptrs, ws.a_rows, ws.a_cols, ws.b_rows, ws.b_cols,\n      ws.d_rows, ws.d_cols, ws.alpha_ptrs, ws.beta_ptrs, ws.a_scale_inv_ptrs, ws.b_scale_inv_ptrs,\n      A_sel.dptr, B_sel.dptr, c_base, d_base, A_meta, B_meta, C_meta, D_meta, a_elem_size,\n      b_elem_size, c_elem_size, d_elem_size, static_cast<float *>(alpha_tensor->data.dptr),\n      static_cast<float *>(beta_tensor->data.dptr), reinterpret_cast<float *>(A_sel.scale_inv),\n      reinterpret_cast<float *>(B_sel.scale_inv), a_rowwise, b_rowwise, A_sel.scaling_mode,\n      num_tensors, a_multi_tensor_args, c_multi_tensor_args, d_multi_tensor_args);\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace\n\nsize_t nvte_get_grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  NVTE_API_CALL(nvte_get_grouped_gemm_setup_workspace_size);\n  return grouped_gemm_setup_workspace_size(num_tensors);\n}\n\nvoid nvte_grouped_gemm(const NVTEGroupedTensor A, int transa, const NVTEGroupedTensor B, int transb,\n                       const NVTEGroupedTensor C, NVTEGroupedTensor D, const NVTETensor alpha,\n                       const NVTETensor beta, NVTETensor workspace_setup,\n                       NVTETensor workspace_cublas, NVTEGroupedMatmulConfig config,\n                       hipStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm\");\n\n  // Convert to internal types\n  const GroupedTensor *inputA = convertNVTEGroupedTensorCheck(A);\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const GroupedTensor *inputC_raw = convertNVTEGroupedTensor(C);  // Can be NULL\n  GroupedTensor *outputD = convertNVTEGroupedTensorCheck(D);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Validate inputs and outputs.\n  const size_t num_tensors = validate_grouped_gemm_inputs(inputA->num_tensors, {inputA, inputB},\n                                                          alpha_tensor, beta_tensor);\n  validate_grouped_gemm_outputs(num_tensors, {inputC_raw, outputD});\n\n  // If C is NULL, use D as C (valid when beta=0, cuBLAS won't read C data)\n  const GroupedTensor *inputC = (inputC_raw != nullptr) ? inputC_raw : outputD;\n  // num_tensors validated above.\n  // Select operand storage (row-wise vs column-wise) and adjust transpose flags to\n  // mirror the non-grouped GEMM logic for FP8 layout constraints.\n  auto A_sel = select_grouped_operand(inputA, static_cast<bool>(transa), /*is_A=*/true);\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, inputC, outputD, alpha_tensor,\n                            beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            /*C_list=*/nullptr, /*D_list=*/nullptr, A_sel.dptr, inputC->dtype(),\n                            outputD->dtype());\n\n  // Compute average dimensions for heuristics\n  // K dimension: if transa, K is A's first dim; if not, K is A's last dim\n  // Use original inputA and transa for heuristics (not modified A_sel.trans)\n  int64_t avg_m_val = config_.avg_m.value_or(compute_avg_first_dim(outputD));\n  int64_t avg_n_val = config_.avg_n.value_or(compute_avg_last_dim(outputD));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(transa ? compute_avg_first_dim(inputA) : compute_avg_last_dim(inputA));\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, outputD->dtype(), num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nvoid nvte_grouped_gemm_with_discrete_inputA(const NVTETensor *A_list, size_t num_a_tensors,\n                                            int transa, const NVTEGroupedTensor B, int transb,\n                                            const NVTEGroupedTensor C, NVTEGroupedTensor D,\n                                            const NVTETensor alpha, const NVTETensor beta,\n                                            NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                            NVTEGroupedMatmulConfig config, hipStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm_with_discrete_inputA);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm_with_discrete_inputA\");\n\n  NVTE_CHECK(A_list != nullptr, \"Grouped GEMM: A_list is null.\");\n  NVTE_CHECK(num_a_tensors > 0, \"Grouped GEMM: num_a_tensors must be > 0.\");\n\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const GroupedTensor *inputC_raw = convertNVTEGroupedTensor(C);  // Can be NULL\n  GroupedTensor *outputD = convertNVTEGroupedTensorCheck(D);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Validate inputs and outputs.\n  const size_t num_tensors =\n      validate_grouped_gemm_inputs(num_a_tensors, {inputB}, alpha_tensor, beta_tensor);\n\n  validate_grouped_gemm_outputs(num_tensors, {inputC_raw, outputD});\n\n  // If C is NULL, use D as C (valid when beta=0, cuBLAS won't read C data)\n  const GroupedTensor *inputC = (inputC_raw != nullptr) ? inputC_raw : outputD;\n\n  // Validate A list and selection\n  auto A_list_info =\n      validate_grouped_gemm_multi_inputA_list(A_list, num_a_tensors, num_tensors, \"A\");\n  auto is_fp8_or_16bit = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kFloat8E4M3 ||\n           dtype == transformer_engine::DType::kFloat8E5M2 ||\n           dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16;\n  };\n  NVTE_CHECK(is_fp8_or_16bit(A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype),\n             \"Grouped GEMM: A_list tensors must be FP8, BF16, or FP16.\");\n\n  // Cross-operand consistency (mirrors validate_grouped_gemm_inputs).\n  const DType a_rep_dtype = A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype;\n  NVTE_CHECK(is_fp8_dtype(a_rep_dtype) == is_fp8_dtype(inputB->dtype()),\n             \"Grouped GEMM: A and B must both be FP8 or both be non-FP8.\");\n  NVTE_CHECK(transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode) ==\n                 transformer_engine::is_mxfp_scaling(inputB->scaling_mode),\n             \"Grouped GEMM: A and B must both use MXFP8 scaling or both use tensor scaling.\");\n  if (transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode)) {\n    NVTE_CHECK(A_list_info.with_gemm_swizzled_scales,\n               \"MXFP8 grouped GEMM: A scales must be swizzled for GEMM.\");\n    NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n               \"MXFP8 grouped GEMM: B scales must be swizzled for GEMM.\");\n  }\n\n  // Select operand storage for B (row-wise vs column-wise)\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n\n  GroupedOperandSelection A_sel{};\n  A_sel.scaling_mode = A_list_info.scaling_mode;\n  A_sel.with_gemm_swizzled_scales = A_list_info.with_gemm_swizzled_scales;\n  A_sel.trans = static_cast<bool>(transa);\n\n  const DType rep_dtype = A_list_info.all_row ? A_list_info.row_dtype : A_list_info.col_dtype;\n  const bool is_fp8 = is_fp8_dtype(rep_dtype);\n  const bool non_tn_fp8_ok = nvte_is_non_tn_fp8_gemm_supported();\n  const bool mxfp8 = transformer_engine::is_mxfp_scaling(A_list_info.scaling_mode);\n\n  int64_t avg_first_dim = 0;\n  int64_t avg_last_dim = 0;\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n\n  const auto choice =\n      choose_grouped_operand_storage(static_cast<bool>(transa), /*is_A=*/true, mxfp8, is_fp8,\n                                     non_tn_fp8_ok, A_list_info.all_row, A_list_info.all_col, \"A\");\n  A_sel.trans = choice.trans;\n  A_sel.rowwise = choice.use_rowwise;\n  if (choice.use_rowwise) {\n    NVTE_CHECK(A_list_info.all_row, \"Grouped GEMM: A_list is missing row-wise data\");\n    A_sel.dtype = A_list_info.row_dtype;\n    a_multi_tensor_args = build_grouped_gemm_multi_inputA_args(\n        A_list, num_a_tensors, /*use_rowwise=*/true, is_fp8, &avg_first_dim, &avg_last_dim, \"A\");\n  } else {\n    NVTE_CHECK(A_list_info.all_col, \"Grouped GEMM: A_list is missing column-wise data\");\n    A_sel.dtype = A_list_info.col_dtype;\n    a_multi_tensor_args = build_grouped_gemm_multi_inputA_args(\n        A_list, num_a_tensors, /*use_rowwise=*/false, is_fp8, &avg_first_dim, &avg_last_dim, \"A\");\n  }\n\n  // For discrete A_list, scale pointers are per-tensor; use multi-tensor args.\n  // Base pointer is unused when providing per-tensor pointers.\n  A_sel.scale_inv = nullptr;\n  A_sel.dptr = nullptr;\n\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, inputC, outputD, alpha_tensor,\n                            beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            /*C_list=*/nullptr, /*D_list=*/nullptr, nullptr, inputC->dtype(),\n                            outputD->dtype());\n\n  // Compute average dimensions for heuristics\n  int64_t avg_m_val = config_.avg_m.value_or(compute_avg_first_dim(outputD));\n  int64_t avg_n_val =\n      config_.avg_n.value_or(transb ? compute_avg_first_dim(inputB) : compute_avg_last_dim(inputB));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(static_cast<bool>(transa) ? avg_last_dim : avg_first_dim);\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, outputD->dtype(), num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nvoid nvte_grouped_gemm_with_discrete_out(const NVTEGroupedTensor A, int transa,\n                                         const NVTEGroupedTensor B, int transb,\n                                         const NVTETensor *C_list, size_t num_c_tensors,\n                                         NVTETensor *D_list, size_t num_d_tensors,\n                                         const NVTETensor alpha, const NVTETensor beta,\n                                         NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                         NVTEGroupedMatmulConfig config, hipStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_gemm_with_discrete_out);\n  using namespace transformer_engine;\n\n  // Grouped GEMM requires Blackwell (SM100) or newer and cuBLAS 13.3+\n  check_grouped_gemm_requirements(\"nvte_grouped_gemm_with_discrete_out\");\n\n  NVTE_CHECK(D_list != nullptr, \"Grouped GEMM: D_list is null.\");\n  NVTE_CHECK(num_d_tensors > 0, \"Grouped GEMM: num_d_tensors must be > 0.\");\n  if (num_c_tensors > 0) {\n    NVTE_CHECK(C_list != nullptr, \"Grouped GEMM: C_list is null but num_c_tensors > 0.\");\n  }\n\n  const GroupedTensor *inputA = convertNVTEGroupedTensorCheck(A);\n  const GroupedTensor *inputB = convertNVTEGroupedTensorCheck(B);\n  const Tensor *alpha_tensor = convertNVTETensorCheck(alpha);\n  const Tensor *beta_tensor = convertNVTETensorCheck(beta);\n  Tensor *wspace_setup = convertNVTETensor(workspace_setup);\n  Tensor *wspace_cublas = convertNVTETensor(workspace_cublas);\n\n  const Tensor *d0 = convertNVTETensorCheck(D_list[0]);\n  const DType d_dtype = d0->dtype();\n\n  const size_t num_tensors = validate_grouped_gemm_inputs(inputA->num_tensors, {inputA, inputB},\n                                                          alpha_tensor, beta_tensor);\n  NVTE_CHECK(num_d_tensors == num_tensors, \"Grouped GEMM: D_list must have num_tensors (\",\n             num_tensors, \") entries, got \", num_d_tensors);\n  if (num_c_tensors > 0) {\n    NVTE_CHECK(num_c_tensors == num_tensors, \"Grouped GEMM: C_list must have num_tensors (\",\n               num_tensors, \") entries, got \", num_c_tensors);\n  }\n  auto is_output_dtype = [](transformer_engine::DType dtype) {\n    return dtype == transformer_engine::DType::kBFloat16 ||\n           dtype == transformer_engine::DType::kFloat16 ||\n           dtype == transformer_engine::DType::kFloat32;\n  };\n  NVTE_CHECK(is_output_dtype(d_dtype), \"Grouped GEMM: D must be BF16, FP16, or FP32.\");\n\n  // Parse config (if provided)\n  GroupedMatmulConfig config_ = parse_grouped_gemm_config(config);\n\n  // Select operand storage (row-wise vs column-wise) and adjust transpose flags to\n  // mirror the non-grouped GEMM logic for FP8 layout constraints.\n  auto A_sel = select_grouped_operand(inputA, static_cast<bool>(transa), /*is_A=*/true);\n  auto B_sel = select_grouped_operand(inputB, static_cast<bool>(transb), /*is_A=*/false);\n  // Workspaces: setup (pointer arrays) and cuBLAS\n  auto workspace = setup_grouped_gemm_workspace(wspace_setup, wspace_cublas, num_tensors);\n\n  MultiTensorGroupGemmInputArgs a_multi_tensor_args{};\n  launch_grouped_gemm_setup(workspace.setup_workspace, A_sel, B_sel, /*C=*/nullptr, /*D=*/nullptr,\n                            alpha_tensor, beta_tensor, num_tensors, stream, a_multi_tensor_args,\n                            C_list, D_list, A_sel.dptr, d_dtype, d_dtype);\n\n  // Compute average dimensions for heuristics\n  int64_t avg_m_val =\n      config_.avg_m.value_or(transa ? compute_avg_last_dim(inputA) : compute_avg_first_dim(inputA));\n  int64_t avg_n_val =\n      config_.avg_n.value_or(transb ? compute_avg_first_dim(inputB) : compute_avg_last_dim(inputB));\n  int64_t avg_k_val =\n      config_.avg_k.value_or(transa ? compute_avg_first_dim(inputA) : compute_avg_last_dim(inputA));\n  const bool use_fp8 = is_fp8_dtype(A_sel.dtype) || is_fp8_dtype(B_sel.dtype);\n  execute_grouped_gemm(workspace.setup_workspace, A_sel, B_sel, d_dtype, num_tensors,\n                       config_.use_split_accumulator, use_fp8, avg_m_val, avg_n_val, avg_k_val,\n                       workspace.cublas_workspace_ptr, stream, config_.sm_count);\n}\n\nnamespace {\n\nvoid launch_grouped_bias_add(const transformer_engine::GroupedTensor *outputD,\n                             const transformer_engine::GroupedTensor *bias_tensor,\n                             const float *scale_ptr, bool use_scale, hipStream_t stream) {\n  using namespace transformer_engine;\n\n  const char *api_name = use_scale ? \"Grouped scaled bias add\" : \"Grouped bias add\";\n\n  NVTE_CHECK(outputD->num_tensors >= 1, api_name, \": number of tensors must be at least 1\");\n  NVTE_CHECK(outputD->num_tensors == bias_tensor->num_tensors, api_name,\n             \": output and bias must have the same number of tensors\");\n  NVTE_CHECK(outputD->has_data(), api_name, \": output is missing row-wise data\");\n  NVTE_CHECK(bias_tensor->has_data(), api_name, \": bias is missing row-wise data\");\n  NVTE_CHECK(outputD->dtype() == bias_tensor->dtype(), api_name,\n             \": output and bias must have matching dtypes\");\n  NVTE_CHECK(bias_tensor->all_same_first_dim(), api_name,\n             \": bias must have uniform first dim (expected 1)\");\n  NVTE_CHECK(bias_tensor->get_common_first_dim() == 1, api_name, \": bias first dim must be 1\");\n  NVTE_CHECK(outputD->all_same_last_dim() && bias_tensor->all_same_last_dim(), api_name,\n             \": requires uniform last dim for output and bias\");\n  NVTE_CHECK(outputD->get_common_last_dim() == bias_tensor->get_common_last_dim(), api_name,\n             \": output and bias last dims must match\");\n\n  const TensorShapeInfo d_meta = TensorShapeInfo::from_tensor(outputD);\n\n  const DType dtype = outputD->dtype();\n  constexpr int kThreads = 128;\n\n  const int num_tensors = static_cast<int>(outputD->num_tensors);\n  NVTE_CHECK(num_tensors <= kMaxGroups, api_name, \" supports at most \", kMaxGroups,\n             \" tensors, got \", num_tensors);\n  const int total_rows = static_cast<int>(outputD->logical_shape.data[0]);\n  const int n = static_cast<int>(outputD->get_common_last_dim());\n\n  const size_t elem_size = typeToSize(dtype);\n  const int kVec = (elem_size <= 2) ? 8 : 4;\n  NVTE_CHECK(n % kVec == 0, api_name, \": requires last dim divisible by \", kVec);\n\n  constexpr int kRowsPerBlock = 16;\n  constexpr int kBlocksPerSM = 32;\n\n  const int num_sms = transformer_engine::cuda::sm_count();\n\n  const int block_cols = kThreads * kVec;\n  const int col_blocks = (n + block_cols - 1) / block_cols;\n  const int max_row_chunks = (total_rows + kRowsPerBlock - 1) / kRowsPerBlock;\n  const int row_blocks = std::min(max_row_chunks, num_sms * kBlocksPerSM / col_blocks);\n  const dim3 grid(std::max(1, row_blocks), col_blocks);\n  const dim3 block(kThreads);\n\n  auto launch = [&](auto use_scale_tag) {\n    constexpr bool kUseScale = decltype(use_scale_tag)::value;\n    if (elem_size <= 2) {\n      constexpr int kV = 8;\n      TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(dtype, T, {\n        grouped_bias_add_kernel<T, kV, kUseScale, kThreads, kRowsPerBlock>\n            <<<grid, block, 0, stream>>>(static_cast<char *>(outputD->data.dptr),\n                                         static_cast<const char *>(bias_tensor->data.dptr),\n                                         scale_ptr, d_meta, n, total_rows, num_tensors);\n      });\n    } else {\n      constexpr int kV = 4;\n      TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(dtype, T, {\n        grouped_bias_add_kernel<T, kV, kUseScale, kThreads, kRowsPerBlock>\n            <<<grid, block, 0, stream>>>(static_cast<char *>(outputD->data.dptr),\n                                         static_cast<const char *>(bias_tensor->data.dptr),\n                                         scale_ptr, d_meta, n, total_rows, num_tensors);\n      });\n    }\n  };\n\n  if (use_scale) {\n    launch(std::true_type{});\n  } else {\n    launch(std::false_type{});\n  }\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace\n\nvoid nvte_grouped_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                           hipStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_bias_add);\n  using namespace transformer_engine;\n  const GroupedTensor *outputD = convertNVTEGroupedTensorCheck(output);\n  const GroupedTensor *bias_tensor = convertNVTEGroupedTensorCheck(bias);\n  launch_grouped_bias_add(outputD, bias_tensor, nullptr, false, stream);\n}\n\nvoid nvte_grouped_scaled_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                                  const NVTETensor scale, hipStream_t stream) {\n  NVTE_API_CALL(nvte_grouped_scaled_bias_add);\n  using namespace transformer_engine;\n  const GroupedTensor *outputD = convertNVTEGroupedTensorCheck(output);\n  const GroupedTensor *bias_tensor = convertNVTEGroupedTensorCheck(bias);\n  const Tensor *scale_tensor = convertNVTETensorCheck(scale);\n\n  NVTE_CHECK(scale_tensor->data.dptr != nullptr,\n             \"Grouped scaled bias add: scale tensor must not be null\");\n  NVTE_CHECK(scale_tensor->dtype() == DType::kFloat32,\n             \"Grouped scaled bias add: scale must be float32\");\n  NVTE_CHECK(scale_tensor->data.shape.size() == 1,\n             \"Grouped scaled bias add: scale must be 1D, got \", scale_tensor->data.shape.size(),\n             \"D\");\n  const size_t total_rows = static_cast<size_t>(outputD->logical_shape.data[0]);\n  NVTE_CHECK(scale_tensor->data.shape[0] == total_rows, \"Grouped scaled bias add: scale size (\",\n             scale_tensor->data.shape[0], \") must equal total rows (\", total_rows, \")\");\n\n  const float *scale_ptr = static_cast<const float *>(scale_tensor->data.dptr);\n  launch_grouped_bias_add(outputD, bias_tensor, scale_ptr, true, stream);\n}\n\n#else  // CUBLAS_VERSION < CUBLAS_GROUPED_GEMM_VERSION\n\nvoid nvte_grouped_gemm(const NVTEGroupedTensor A, int transa, const NVTEGroupedTensor B, int transb,\n                       const NVTEGroupedTensor C, NVTEGroupedTensor D, const NVTETensor alpha,\n                       const NVTETensor beta, NVTETensor workspace_setup,\n                       NVTETensor workspace_cublas, NVTEGroupedMatmulConfig config,\n                       hipStream_t stream) {\n  NVTE_ERROR(\"nvte_grouped_gemm requires cuBLAS 13.3+, but compile-time cuBLAS version is \",\n             CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_gemm_with_discrete_inputA(const NVTETensor *A_list, size_t num_a_tensors,\n                                            int transa, const NVTEGroupedTensor B, int transb,\n                                            const NVTEGroupedTensor C, NVTEGroupedTensor D,\n                                            const NVTETensor alpha, const NVTETensor beta,\n                                            NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                            NVTEGroupedMatmulConfig config, hipStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_gemm_with_discrete_inputA requires cuBLAS 13.3+, but compile-time \"\n      \"cuBLAS version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_gemm_with_discrete_out(const NVTEGroupedTensor A, int transa,\n                                         const NVTEGroupedTensor B, int transb,\n                                         const NVTETensor *C_list, size_t num_c_tensors,\n                                         NVTETensor *D_list, size_t num_d_tensors,\n                                         const NVTETensor alpha, const NVTETensor beta,\n                                         NVTETensor workspace_setup, NVTETensor workspace_cublas,\n                                         NVTEGroupedMatmulConfig config, hipStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_gemm_with_discrete_out requires cuBLAS 13.3+, but compile-time \"\n      \"cuBLAS version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                           hipStream_t stream) {\n  NVTE_ERROR(\"nvte_grouped_bias_add requires cuBLAS 13.3+, but compile-time cuBLAS version is \",\n             CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nvoid nvte_grouped_scaled_bias_add(const NVTEGroupedTensor output, const NVTEGroupedTensor bias,\n                                  const NVTETensor scale, hipStream_t stream) {\n  NVTE_ERROR(\n      \"nvte_grouped_scaled_bias_add requires cuBLAS 13.3+, but compile-time cuBLAS version \"\n      \"is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n}\n\nsize_t nvte_get_grouped_gemm_setup_workspace_size(size_t num_tensors) {\n  NVTE_ERROR(\n      \"nvte_get_grouped_gemm_setup_workspace_size requires cuBLAS 13.3+, but compile-time cuBLAS \"\n      \"version is \",\n      CUBLAS_VERSION, \". Please upgrade to cuBLAS 13.3 (shipped with CUDA 13.2) or newer.\");\n  return 0;\n}\n\n#endif  // CUBLAS_VERSION >= CUBLAS_GROUPED_GEMM_VERSION\n\nnamespace {\n\n__global__ void convert_int32_to_int64_kernel(const int32_t *src, int64_t *dst, size_t n) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n) dst[idx] = static_cast<int64_t>(src[idx]);\n}\n\n// Like convert_int32_to_int64_kernel but scales each element by multiplier.\n// Used to convert per-expert slice counts to per-expert row counts for multi-dim tensors.\n__global__ void convert_int32_to_int64_with_multiplier_kernel(const int32_t *src, int64_t *dst,\n                                                              size_t n, int64_t multiplier) {\n  size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n) dst[idx] = static_cast<int64_t>(src[idx]) * multiplier;\n}\n\n// Computes exclusive prefix sums: offsets[0]=0, offsets[i]=sum(first_dims[0..i-1]*last_dim).\n// Produces n_groups+1 values. Single-threaded sequential scan; n_groups is typically small.\n__global__ void compute_grouped_tensor_offsets_kernel(const int64_t *first_dims, int64_t *offsets,\n                                                      size_t n_groups, int64_t last_dim) {\n  offsets[0] = 0;\n  for (size_t i = 0; i < n_groups; i++) {\n    offsets[i + 1] = offsets[i] + first_dims[i] * last_dim;\n  }\n}\n\n}  // namespace\n\nvoid nvte_convert_int32_to_int64(const int32_t *src, int64_t *dst, size_t n, hipStream_t stream) {\n  NVTE_API_CALL(nvte_convert_int32_to_int64);\n  if (n == 0) return;\n  const int threads = 256;\n  const int blocks = static_cast<int>((n + threads - 1) / threads);\n  convert_int32_to_int64_kernel<<<blocks, threads, 0, stream>>>(src, dst, n);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid nvte_convert_int32_to_int64_with_multiplier(const int32_t *src, int64_t *dst, size_t n,\n                                                 int64_t multiplier, hipStream_t stream) {\n  NVTE_API_CALL(nvte_convert_int32_to_int64_with_multiplier);\n  if (n == 0) return;\n  const int threads = 256;\n  const int blocks = static_cast<int>((n + threads - 1) / threads);\n  convert_int32_to_int64_with_multiplier_kernel<<<blocks, threads, 0, stream>>>(src, dst, n,\n                                                                                multiplier);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid nvte_compute_grouped_tensor_offsets(const int64_t *first_dims, int64_t *offsets,\n                                         size_t n_groups, int64_t last_dim, hipStream_t stream) {\n  NVTE_API_CALL(nvte_compute_grouped_tensor_offsets);\n  // Always write at least offsets[0]=0 (needed even for n_groups==0).\n  compute_grouped_tensor_offsets_kernel<<<1, 1, 0, stream>>>(first_dims, offsets, n_groups,\n                                                             last_dim);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/gemm/cublaslt_grouped_gemm.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cublasLt.h>\n#include <cublas_v2.h>\n#include <cuda.h>\n#include <transformer_engine/gemm.h>\n#include <transformer_engine/multi_stream.h>\n#include <transformer_engine/recipe.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <mutex>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/handle_manager.h\"\n#include \"../util/logging.h\"\n#include \"../util/multi_stream.h\"\n#include \"./config.h\"\n#include \"./cutlass_grouped_gemm.cuh\"\n\nnamespace {\n\n/* Use CUDA const memory to store scalar 1 and 0 for cublas usage\n*/\n__device__ __constant__ float one_device;\n__device__ __constant__ float zero_device;\n\ninline float *GetScalarOne() {\n  static std::once_flag init_flag;\n  std::call_once(init_flag, []() {\n    float one = 1.0f;\n    NVTE_CHECK_CUDA(cudaMemcpyToSymbol(one_device, &one, sizeof(float)));\n  });\n  // return address by cudaGetSymbolAddress\n  float *dev_ptr;\n  NVTE_CHECK_CUDA(cudaGetSymbolAddress(reinterpret_cast<void **>(&dev_ptr), one_device));\n  return dev_ptr;\n}\n\ninline float *GetScalarZero() {\n  static std::once_flag init_flag;\n  std::call_once(init_flag, []() {\n    float zero = 0.0f;\n    NVTE_CHECK_CUDA(cudaMemcpyToSymbol(zero_device, &zero, sizeof(float)));\n  });\n  // return address by cudaGetSymbolAddress\n  float *dev_ptr;\n  NVTE_CHECK_CUDA(cudaGetSymbolAddress(reinterpret_cast<void **>(&dev_ptr), zero_device));\n  return dev_ptr;\n}\n\n__global__ __launch_bounds__(1) void set_float_kernel(float *ptr, float val) { *ptr = val; }\n\nuint32_t _getAlignment(uintptr_t address) {\n  // alignment are in bytes\n  uint32_t alignment = 256;\n  for (;; alignment /= 2) {\n    if (address % alignment == 0) {\n      return alignment;\n    }\n  }\n}\n\ninline void CreateCublasHandle(cublasLtHandle_t *handle) {\n  NVTE_CHECK_CUBLAS(cublasLtCreate(handle));\n}\n\n/* Parameters for cuBLAS GEMM\n *\n * cuBLAS follows the BLAS convention of column-major ordering. This\n * is different than the row-major that is typically used in\n * Transformer Engine.\n *\n */\nstruct GemmParam {\n  void *A = nullptr;\n  void *B = nullptr;\n  cublasOperation_t transA = CUBLAS_OP_N;\n  cublasOperation_t transB = CUBLAS_OP_N;\n  transformer_engine::DType Atype = transformer_engine::DType::kNumTypes;\n  transformer_engine::DType Btype = transformer_engine::DType::kNumTypes;\n  void *A_scale_inv = nullptr;\n  void *B_scale_inv = nullptr;\n  int lda = 0;  // A column strides\n  int ldb = 0;  // B column strides\n};\n\n/* Populate parameters for cuBLAS GEMM\n *\n * cuBLAS follows the BLAS convention of column-major ordering. This\n * is different than the row-major that is typically used in\n * Transformer Engine.\n *\n */\nGemmParam CanonicalizeGemmInput(const transformer_engine::Tensor &A, const cublasOperation_t transA,\n                                const transformer_engine::Tensor &B, const cublasOperation_t transB,\n                                int m, int n, int k) {\n  using namespace transformer_engine;\n  NVTE_CHECK(\n      A.scaling_mode == B.scaling_mode ||\n          (A.scaling_mode == NVTE_BLOCK_SCALING_1D && B.scaling_mode == NVTE_BLOCK_SCALING_2D) ||\n          (A.scaling_mode == NVTE_BLOCK_SCALING_2D && B.scaling_mode == NVTE_BLOCK_SCALING_1D),\n      \"Inputs A and B to GEMM need to have compatible scaling modes, but got A.scaling_mode = \" +\n          to_string(A.scaling_mode) + \", B.scaling_mode = \" + to_string(B.scaling_mode));\n  NVTE_CHECK(A.has_data() || A.has_columnwise_data(), \"Input A does not hold any data!\");\n  NVTE_CHECK(B.has_data() || B.has_columnwise_data(), \"Input B does not hold any data!\");\n  GemmParam ret;\n\n  // Transpose mode with column-major ordering\n  bool is_A_transposed = transA == CUBLAS_OP_T;\n  bool is_B_transposed = transB == CUBLAS_OP_T;\n\n  // Set conditions for MXFP8 and NVFP4 gemm execution.\n  const auto nvfp4 = is_nvfp_scaling(A.scaling_mode) && is_nvfp_scaling(B.scaling_mode);\n  const auto mxfp8 = !nvfp4 && is_mxfp_scaling(A.scaling_mode) && is_mxfp_scaling(B.scaling_mode);\n  int is_nvte_non_tn_fp8_gemm_supported = 0;  // needed only for per tensor scaling\n  if (is_tensor_scaling(A.scaling_mode) || is_tensor_scaling(B.scaling_mode)) {\n    is_nvte_non_tn_fp8_gemm_supported = nvte_is_non_tn_fp8_gemm_supported();\n  }\n\n  // Configure A matrix\n  if (is_tensor_scaling(A.scaling_mode)) {\n    // Unscaled or FP8 tensor scaling\n    ret.A = A.data.dptr;\n    ret.transA = transA;\n    ret.Atype = A.data.dtype;\n    ret.A_scale_inv = A.scale_inv.dptr;\n    ret.lda = is_A_transposed ? k : m;\n    if (!is_nvte_non_tn_fp8_gemm_supported && !is_A_transposed) {\n      // Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n      if (A.has_columnwise_data() && is_fp8_dtype(A.columnwise_data.dtype)) {\n        ret.A = A.columnwise_data.dptr;\n        ret.transA = CUBLAS_OP_T;\n        ret.Atype = A.columnwise_data.dtype;\n        ret.A_scale_inv = A.columnwise_scale_inv.dptr;\n        ret.lda = k;\n      } else {\n        NVTE_CHECK(!is_fp8_dtype(ret.Atype), \"Input A is missing column-wise usage\");\n      }\n    } else if (is_nvte_non_tn_fp8_gemm_supported && !A.has_data()) {\n      // Blackwell supports any GEMM layout for FP8, so we can use column-wise/transposed\n      // data  with the mirrored transpose-flag if we don't have row-wise data.\n      NVTE_CHECK(A.has_columnwise_data() && is_fp8_dtype(A.columnwise_data.dtype),\n                 \"Input A is missing column-wise usage\");\n      ret.A = A.columnwise_data.dptr;\n      ret.transA = is_A_transposed ? CUBLAS_OP_N : CUBLAS_OP_T;\n      ret.Atype = A.columnwise_data.dtype;\n      ret.A_scale_inv = A.columnwise_scale_inv.dptr;\n      ret.lda = is_A_transposed ? m : k;\n    }\n\n    if (is_fp8_dtype(ret.Atype)) {\n      // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n      NVTE_CHECK(ret.lda % 16 == 0,\n                 \"Leading dimension requirement on A for FP8 GEMM. Caller must pad.\");\n    }\n  } else if (nvfp4) {\n    // NVFP4 GEMM. Either the pure NVFP4 recipe or the FWD pass of the Hybrid NVFP4/MXFP8 recipe.\n\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(is_nvfp4_scaling(A.scaling_mode),\n                 \"Input A has unsupported combination of recipe and layout\");\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = CUBLAS_OP_T;  // NVFP4 gemm is only supported in TN layout.\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = k;\n  } else if (mxfp8) {\n    // MXFP8 GEMM. Either for pure MXFP8 recipe or backward of Hybrid NVFP4 recipe.\n    // Note: Row-wise and column-wise data are scaled along different\n    // dimensions (with matrix interpreted in row-major order).\n\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = transA;\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = is_A_transposed ? k : m;\n  } else if (A.scaling_mode == NVTE_BLOCK_SCALING_1D || A.scaling_mode == NVTE_BLOCK_SCALING_2D) {\n    // FP8 block scaling\n    // Note: Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = CUBLAS_OP_T;\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = k;\n\n    // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n    NVTE_CHECK((ret.lda % 16) == 0,\n               \"Leading dimension requirement on NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    // Divisibility of 8 derived from FP8 (m * CTypeSize) % 16 == 0 requirement.\n    // Smallest supported CType is 2 bytes in this scaling mode.\n    NVTE_CHECK((m % 8) == 0,\n               \"Outer dimension requirement on A for NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n  } else {\n    NVTE_ERROR(\"A has unsupported scaling mode\");\n  }\n\n  // Configure B matrix\n  if (is_tensor_scaling(B.scaling_mode)) {\n    // Unscaled or FP8 tensor scaling\n    ret.B = B.data.dptr;\n    ret.transB = transB;\n    ret.Btype = B.data.dtype;\n    ret.B_scale_inv = B.scale_inv.dptr;\n    ret.ldb = is_B_transposed ? n : k;\n    if (!is_nvte_non_tn_fp8_gemm_supported && is_B_transposed) {\n      // Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n      if (B.has_columnwise_data() && is_fp8_dtype(B.columnwise_data.dtype)) {\n        ret.B = B.columnwise_data.dptr;\n        ret.transB = CUBLAS_OP_N;\n        ret.Btype = B.columnwise_data.dtype;\n        ret.B_scale_inv = B.columnwise_scale_inv.dptr;\n        ret.ldb = k;\n      } else {\n        NVTE_CHECK(!is_fp8_dtype(ret.Btype), \"Input B is missing column-wise usage\");\n      }\n    } else if (is_nvte_non_tn_fp8_gemm_supported && !B.has_data()) {\n      // Blackwell supports any GEMM layout for FP8, so we can use column-wise/transposed\n      // data with the mirrored transpose-flag if we don't have row-wise data.\n      NVTE_CHECK(B.has_columnwise_data() && is_fp8_dtype(B.columnwise_data.dtype),\n                 \"Input B is missing column-wise usage\");\n      ret.B = B.columnwise_data.dptr;\n      ret.transB = is_B_transposed ? CUBLAS_OP_N : CUBLAS_OP_T;\n      ret.Btype = B.columnwise_data.dtype;\n      ret.B_scale_inv = B.columnwise_scale_inv.dptr;\n      ret.ldb = is_B_transposed ? k : n;\n    }\n\n    if (is_fp8_dtype(ret.Atype)) {\n      // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n      NVTE_CHECK(ret.ldb % 16 == 0,\n                 \"Leading dimension requirement on B for FP8 GEMM. Caller must pad.\");\n    }\n  } else if (nvfp4) {\n    if (is_B_transposed) {\n      NVTE_CHECK(is_nvfp4_scaling(B.scaling_mode),\n                 \"Input B has unsupported combination of recipe and layout\");\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = CUBLAS_OP_N;  // NVFP4 gemm is only supported in TN layout.\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = k;\n  } else if (mxfp8) {\n    if (is_B_transposed) {\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = transB;\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = is_B_transposed ? n : k;\n  } else if (B.scaling_mode == NVTE_BLOCK_SCALING_1D || B.scaling_mode == NVTE_BLOCK_SCALING_2D) {\n    // FP8 block scaling\n    // Note: Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n    if (is_B_transposed) {\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = CUBLAS_OP_N;\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = k;\n\n    // Requirements from\n    // https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n    NVTE_CHECK((ret.ldb % 16) == 0,\n               \"B tensor stride requirement on NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    if (B.scaling_mode == NVTE_BLOCK_SCALING_1D) {\n      // Observed this requirement only present for B tensor is 1D quantized.\n      NVTE_CHECK((n % 8) == 0,\n                 \"Outer dimension requirement on B for NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    }\n  } else {\n    NVTE_ERROR(\"B has unsupported scaling mode\");\n  }\n\n  return ret;\n}\n\n}  // namespace\n\nnamespace transformer_engine {\n\nusing cublasHandleManager = detail::HandleManager<cublasLtHandle_t, CreateCublasHandle>;\n\nvoid cublas_gemm(const Tensor *inputA, const Tensor *inputB, Tensor *outputD,\n                 const Tensor *inputBias, Tensor *outputPreGelu, cublasOperation_t transa,\n                 cublasOperation_t transb, bool grad, void *workspace, size_t workspaceSize,\n                 const void *alpha, const void *beta, bool use_split_accumulator, int math_sm_count,\n                 int m_split, int n_split, bool gemm_producer, const Tensor *inputCounter,\n                 cudaStream_t stream) {\n  NVTE_CHECK(!inputA->row_scaled_nvfp4 && !inputB->row_scaled_nvfp4,\n             \"cuBLAS GEMM does not support row-scaled NVFP4 inputs.\");\n\n  // Tensor dims in row-major order\n  const int A0 = inputA->flat_first_dim();\n  const int A1 = inputA->flat_last_dim();\n  const int B0 = inputB->flat_first_dim();\n  const int B1 = inputB->flat_last_dim();\n\n  // GEMM dims in column-major order\n  const int m = transa == CUBLAS_OP_T ? A0 : A1;\n  const int n = transb == CUBLAS_OP_T ? B1 : B0;\n  const int k = transa == CUBLAS_OP_T ? A1 : A0;\n  NVTE_CHECK((transb == CUBLAS_OP_T ? B0 : B1) == k,\n             \"GEMM inputs have incompatible dimensions (A is \", A0, \"x\", A1, \", B is \", B0, \"x\", B1,\n             \")\");\n  const int ldd = m;\n\n  // Return immediately if GEMM is trivial\n  if (m <= 0 || n <= 0) {\n    return;\n  }\n  NVTE_CHECK(k > 0);\n\n  const GemmParam param = CanonicalizeGemmInput(*inputA, transa, *inputB, transb, m, n, k);\n\n  void *C = outputD->data.dptr;\n  void *D = outputD->data.dptr;\n  void *D_scale = outputD->scale.dptr;\n  void *D_amax = outputD->amax.dptr;\n  void *bias_ptr = inputBias->data.dptr;\n  const bool bias = bias_ptr != nullptr;\n  void *pre_gelu_out = outputPreGelu->data.dptr;\n  void *counter = nullptr;\n  if (inputCounter != nullptr) {\n    counter = inputCounter->data.dptr;\n  }\n  const bool gelu = pre_gelu_out != nullptr;\n  const bool use_fp8 = is_fp8_dtype(param.Atype) || is_fp8_dtype(param.Btype);\n  const bool use_fp4 = is_fp4_dtype(param.Atype) || is_fp4_dtype(param.Btype);\n\n  // Update scaling factors with NVFP4 tensor scales\n  // TODO: Check whether scales are on CPU/GPU or add API to control.\n  // Currently scales are assumed to be on CPU when amax is provided\n  // and on GPU when not provided, but this is brittle.\n  if (use_fp4 &&\n      ((transa == CUBLAS_OP_T ? inputA->amax.dptr : inputA->columnwise_amax.dptr) != nullptr ||\n       (transb == CUBLAS_OP_T ? inputB->columnwise_amax.dptr : inputB->amax.dptr) != nullptr)) {\n    // Reserve some workspace for alpha scale\n    NVTE_CHECK(workspaceSize >= 4,\n               \"NVFP4 GEMM requires at least 4 byte workspace for alpha scale, but only has \",\n               workspaceSize, \" bytes remaining.\");\n    workspaceSize = (workspaceSize / 4) * 4 - 4;  // Remove last 4 aligned bytes\n    uint8_t *workspace_ptr = reinterpret_cast<uint8_t *>(workspace);\n    float *new_alpha_ptr = reinterpret_cast<float *>(&workspace_ptr[workspaceSize]);\n\n    // Update alpha scale on device\n    // Note: Compute NVFP4 tensor scales based on amaxes and then\n    // divide from alpha scale. This way we only need to apply NVFP4\n    // tensor scales in matmul output, instead of in matmul inputs.\n    float old_alpha = *reinterpret_cast<const float *>(alpha);  // Assumed to be on CPU\n    TensorWrapper new_alpha_tensor(new_alpha_ptr, std::vector<size_t>{1}, DType::kFloat32);\n    bool a_rowwise_amax = transa == CUBLAS_OP_T;\n    bool b_rowwise_amax = transb != CUBLAS_OP_T;\n    nvte_nvfp4_compute_per_tensor_scale(inputA->nvte_tensor, a_rowwise_amax, inputB->nvte_tensor,\n                                        b_rowwise_amax, old_alpha, new_alpha_tensor.data(), stream);\n    alpha = new_alpha_ptr;\n\n    // Make sure beta scale is on device\n    float old_beta = *reinterpret_cast<const float *>(beta);  // Assumed to be on CPU\n    if (old_beta == 0) {\n      beta = GetScalarZero();  // Device constant memory\n    } else if (old_beta == 1) {\n      beta = GetScalarOne();  // Device constant memory\n    } else {\n      // Move beta to workspace\n      NVTE_CHECK(workspaceSize >= 4,\n                 \"NVFP4 GEMM requires at least 4 byte workspace for beta scale, but only has \",\n                 workspaceSize, \" bytes remaining.\");\n      workspaceSize = (workspaceSize / 4) * 4 - 4;  // Remove last 4 aligned bytes\n      float *new_beta_ptr = reinterpret_cast<float *>(&workspace_ptr[workspaceSize]);\n      set_float_kernel<<<1, 1, 0, stream>>>(new_beta_ptr, old_beta);\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      beta = new_beta_ptr;\n    }\n  }\n\n  const cudaDataType_t A_type = get_cuda_dtype(param.Atype);\n  const cudaDataType_t B_type = get_cuda_dtype(param.Btype);\n  const cudaDataType_t D_type = get_cuda_dtype(outputD->data.dtype);\n  const cudaDataType_t bias_type = get_cuda_dtype(inputBias->data.dtype);\n\n  NVTE_CHECK(!is_fp8_dtype(param.Atype) || param.A_scale_inv != nullptr,\n             \"FP8 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp8_dtype(param.Btype) || param.B_scale_inv != nullptr,\n             \"FP8 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp4_dtype(param.Atype) || param.A_scale_inv != nullptr,\n             \"FP4 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp4_dtype(param.Btype) || param.B_scale_inv != nullptr,\n             \"FP4 input to GEMM requires inverse of scale!\");\n\n  // check consistency of arguments:\n  // if fp8 is desired, context cannot be null\n  // fp8 + gelu fusion + fp8 aux is unavailable right now.\n  if ((use_fp8 || use_fp4) && gelu) {\n    NVTE_CHECK(!is_fp8_dtype(outputPreGelu->data.dtype),\n               \"fp8 Aux output for gemm + gelu fusion not supported!\");\n  }\n  if (is_fp4_dtype(outputD->data.dtype)) {\n    NVTE_ERROR(\"FP4 GEMM output is not supported!\");\n  }\n  if (use_fp4 && (D_type == CUDA_R_16F)) {\n    NVTE_ERROR(\"FP4 GEMM does not support FP16 output!\");\n  }\n\n  cublasLtHandle_t handle = cublasHandleManager::Instance().GetHandle();\n\n  cublasLtMatmulDesc_t operationDesc = nullptr;\n  cublasLtMatrixLayout_t Adesc = nullptr, Bdesc = nullptr, Cdesc = nullptr, Ddesc = nullptr;\n  cublasLtMatmulPreference_t preference = nullptr;\n  int returnedResults = 0;\n  cublasLtMatmulHeuristicResult_t heuristicResult = {};\n  cublasLtEpilogue_t epilogue = CUBLASLT_EPILOGUE_DEFAULT;\n\n  int64_t ld_gelumat = (int64_t)ldd;\n\n  // Use TF32 only for pure FP32 GEMM.\n  cublasComputeType_t gemm_compute_type = CUBLAS_COMPUTE_32F;\n  if (A_type == CUDA_R_32F && B_type == CUDA_R_32F && D_type == CUDA_R_32F) {\n    gemm_compute_type = CUBLAS_COMPUTE_32F_FAST_TF32;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Adesc, A_type, param.transA == CUBLAS_OP_N ? m : k,\n                                               param.transA == CUBLAS_OP_N ? k : m, param.lda));\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Bdesc, B_type, param.transB == CUBLAS_OP_N ? k : n,\n                                               param.transB == CUBLAS_OP_N ? n : k, param.ldb));\n\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Ddesc, D_type, m, n, ldd));\n\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescCreate(&operationDesc, gemm_compute_type, CUDA_R_32F));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc, CUBLASLT_MATMUL_DESC_TRANSA,\n                                                   &param.transA, sizeof(param.transA)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc, CUBLASLT_MATMUL_DESC_TRANSB,\n                                                   &param.transB, sizeof(param.transB)));\n  // Set math SM count\n  if (math_sm_count != 0) {\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                     CUBLASLT_MATMUL_DESC_SM_COUNT_TARGET,\n                                                     &math_sm_count, sizeof(math_sm_count)));\n  }\n\n  // set fp8/fp4 attributes -- input and output types should already be set to fp8/fp4\n  // as appropriate. Note: gelu fusion isn't available right now, and we don't need\n  // amax(D) either (next op is high precision).\n  const bool mxfp8_gemm = !use_fp4 && is_mxfp8_scaling(inputA->scaling_mode);\n\n  if (use_fp8 || use_fp4) {\n    // Fast accumulation is only supported for FP8.\n    const int8_t fastAccuMode = (use_split_accumulator) ? 0 : use_fp8;\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc, CUBLASLT_MATMUL_DESC_FAST_ACCUM,\n                                                     &fastAccuMode, sizeof(fastAccuMode)));\n\n    // Scaling factors.\n#if CUBLAS_VERSION >= 120800\n    cublasLtMatmulMatrixScale_t scaling_mode_a;\n    cublasLtMatmulMatrixScale_t scaling_mode_b;\n#endif  // CUBLAS_VERSION >= 120800\n    if (is_tensor_scaling(inputA->scaling_mode) && is_tensor_scaling(inputB->scaling_mode)) {\n      void *A_scale_inverse = param.A_scale_inv;\n      void *B_scale_inverse = param.B_scale_inv;\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n#if CUBLAS_VERSION >= 120800\n      scaling_mode_a = CUBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;\n      scaling_mode_b = CUBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;\n#endif  // CUBLAS_VERSION >= 120800\n    } else if (mxfp8_gemm) {\n#if CUBLAS_VERSION >= 120800\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120800,\n                 \"MXFP8 requires cuBLAS 12.8+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that scales are in expected format\n      NVTE_CHECK(inputA->with_gemm_swizzled_scales,\n                 \"MXFP8 scales are not in format expected by GEMM\");\n      NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n                 \"MXFP8 scales are not in format expected by GEMM\");\n\n      // Configure cuBLAS scales\n      fp8e8m0 *A_scale_inverse = reinterpret_cast<fp8e8m0 *>(param.A_scale_inv);\n      fp8e8m0 *B_scale_inverse = reinterpret_cast<fp8e8m0 *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n      scaling_mode_b = CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n\n      // Workaround for heuristic cache bug in cublasLt. This separates the MXFP8 cache key from non-block scaling.\n      // CUBLASLT_MATMUL_DESC_ALPHA_VECTOR_BATCH_STRIDE is unused for block scaling so it's safe to set.\n      if (transformer_engine::cuda::cublas_version() <= 120803) {\n        const int64_t dummy_a_vec_stride = 1;\n        NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n            operationDesc, CUBLASLT_MATMUL_DESC_ALPHA_VECTOR_BATCH_STRIDE, &dummy_a_vec_stride,\n            sizeof(dummy_a_vec_stride)));\n      }\n#else\n      NVTE_ERROR(\"MXFP8 requires cuBLAS 12.8+, but compile-time cuBLAS version is \",\n                 CUBLAS_VERSION);\n#endif                     // CUBLAS_VERSION >= 120800\n    } else if (use_fp4) {  // NVFP4 GEMM\n#if CUBLAS_VERSION >= 120800\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120800,\n                 \"FP4 requires cuBLAS 12.8+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that scales are in expected format\n      NVTE_CHECK(inputA->with_gemm_swizzled_scales,\n                 \"NVFP4 block scales are not in format expected by GEMM\");\n      NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n                 \"NVFP4 block scales are not in format expected by GEMM\");\n\n      // alpha and beta are device pointers to FP32\n      const cublasDataType_t scale_type = CUDA_R_32F;\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_SCALE_TYPE, &scale_type, sizeof(scale_type)));\n      const cublasLtPointerMode_t pointer_mode = CUBLASLT_POINTER_MODE_DEVICE;\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_POINTER_MODE, &pointer_mode, sizeof(pointer_mode)));\n\n      // Configure cuBLAS scales\n      fp8e4m3 *A_scale_inverse = reinterpret_cast<fp8e4m3 *>(param.A_scale_inv);\n      fp8e4m3 *B_scale_inverse = reinterpret_cast<fp8e4m3 *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;\n      scaling_mode_b = CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;\n#else\n      NVTE_ERROR(\"FP4 requires cuBLAS 12.8+, but compile-time cuBLAS version is \", CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= 120800\n    } else if ((inputA->scaling_mode == NVTE_BLOCK_SCALING_1D ||\n                inputA->scaling_mode == NVTE_BLOCK_SCALING_2D) &&\n               (inputB->scaling_mode == NVTE_BLOCK_SCALING_1D ||\n                inputB->scaling_mode == NVTE_BLOCK_SCALING_2D)) {\n#if CUBLAS_VERSION >= 120900\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120900,\n                 \"FP8 block scaling requires cuBLAS 12.9+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that matrix formats are valid\n      NVTE_CHECK((!(inputA->scaling_mode == NVTE_BLOCK_SCALING_2D &&\n                    inputB->scaling_mode == NVTE_BLOCK_SCALING_2D)),\n                 \"Only 1D by 1D, 1D by 2D, and 2D by 1D block scaling GEMM is supported, \"\n                 \"but got 2D by 2D\");\n\n      // Configure cuBLAS scales\n      float *A_scale_inverse = reinterpret_cast<float *>(param.A_scale_inv);\n      float *B_scale_inverse = reinterpret_cast<float *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = inputA->scaling_mode == NVTE_BLOCK_SCALING_1D\n                           ? CUBLASLT_MATMUL_MATRIX_SCALE_VEC128_32F\n                           : CUBLASLT_MATMUL_MATRIX_SCALE_BLK128x128_32F;\n      scaling_mode_b = inputB->scaling_mode == NVTE_BLOCK_SCALING_1D\n                           ? CUBLASLT_MATMUL_MATRIX_SCALE_VEC128_32F\n                           : CUBLASLT_MATMUL_MATRIX_SCALE_BLK128x128_32F;\n#else\n      NVTE_ERROR(\"FP8 block scaling requires cuBLAS 12.9+, but compile-time cuBLAS version is \",\n                 CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= 120900\n    } else {\n      NVTE_ERROR(\"Not implemented scaling modes: \" + to_string(inputA->scaling_mode) + \" and  \" +\n                 to_string(inputB->scaling_mode) + \".\");\n    }\n\n#if CUBLAS_VERSION >= 120800\n    if (transformer_engine::cuda::cublas_version() >= 120800) {\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                       &scaling_mode_a, sizeof(scaling_mode_a)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                       CUBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                       &scaling_mode_b, sizeof(scaling_mode_b)));\n    }\n#endif  // CUBLAS_VERSION >= 120800\n    if (is_fp8_dtype(outputD->data.dtype)) {\n      // Accumulation mode not supported for FP8 output\n      C = nullptr;\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_D_SCALE_POINTER, &D_scale, sizeof(D_scale)));\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_AMAX_D_POINTER, &D_amax, sizeof(D_amax)));\n#if CUBLAS_VERSION >= 120800\n      if (transformer_engine::cuda::cublas_version() >= 120800) {\n        // NOTE: In all current cases where FP8 output is supported, the input is\n        // scaled identically to the output.\n        NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                         CUBLASLT_MATMUL_DESC_D_SCALE_MODE,\n                                                         &scaling_mode_a, sizeof(scaling_mode_a)));\n      }\n#endif  // CUBLAS_VERSION >= 120800\n      // For FP8 output, cuBLAS requires C_type to match bias_type and\n      // be FP16/BF16\n      const cudaDataType_t C_type = bias ? bias_type : CUDA_R_16BF;\n      NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Cdesc, C_type, m, n, ldd));\n    } else {\n      NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Cdesc, D_type, m, n, ldd));\n    }\n    if (bias) {\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_BIAS_DATA_TYPE, &bias_type, sizeof(bias_type)));\n    }\n  } else {\n    NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&Cdesc, D_type, m, n, ldd));\n  }\n\n  if (bias && gelu) {\n    if (grad) {\n      epilogue = CUBLASLT_EPILOGUE_DGELU_BGRAD;\n    } else {\n      epilogue = CUBLASLT_EPILOGUE_GELU_AUX_BIAS;\n    }\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_BIAS_POINTER, &bias_ptr, sizeof(bias_ptr)));\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                     CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,\n                                                     &pre_gelu_out, sizeof(pre_gelu_out)));\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_LD, &ld_gelumat, sizeof(ld_gelumat)));\n    const cudaDataType_t aux_type = get_cuda_dtype(outputPreGelu->data.dtype);\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE, &aux_type, sizeof(aux_type)));\n  } else if (bias) {\n    if (grad) {\n      // grad output is always input B\n      epilogue = CUBLASLT_EPILOGUE_BGRADB;\n    } else {\n      epilogue = CUBLASLT_EPILOGUE_BIAS;\n    }\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_BIAS_POINTER, &bias_ptr, sizeof(bias_ptr)));\n  } else if (gelu) {\n    if (grad) {\n      epilogue = CUBLASLT_EPILOGUE_DGELU;\n    } else {\n      epilogue = CUBLASLT_EPILOGUE_GELU_AUX;\n    }\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc,\n                                                     CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,\n                                                     &pre_gelu_out, sizeof(pre_gelu_out)));\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_LD, &ld_gelumat, sizeof(ld_gelumat)));\n    const cudaDataType_t aux_type = get_cuda_dtype(outputPreGelu->data.dtype);\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE, &aux_type, sizeof(aux_type)));\n  }\n\n  if ((inputA->scaling_mode == NVTE_BLOCK_SCALING_1D) ||\n      (inputA->scaling_mode == NVTE_BLOCK_SCALING_2D)) {\n    NVTE_CHECK((epilogue == CUBLASLT_EPILOGUE_DEFAULT || epilogue == CUBLASLT_EPILOGUE_BIAS ||\n                epilogue == CUBLASLT_EPILOGUE_DGELU),\n               \"Epilogue requested outside of the available and tested cuBLAS functionality for \"\n               \"float8 block scaled GEMM\");\n  }\n\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE,\n                                                   &epilogue, sizeof(epilogue)));\n\n  if (counter != nullptr) {\n#if !(CUDA_VERSION >= 12020 && CUDA_VERSION < 13000)\n    NVTE_ERROR(\"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but compile-time CUDA version is \",\n               CUDA_VERSION);\n#elif !(CUBLAS_VERSION >= 120205 && CUBLAS_VERSION < 130000)\n    NVTE_ERROR(\n        \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but compile-time cuBLAS version is \",\n        CUBLAS_VERSION);\n#else\n    NVTE_CHECK(transformer_engine::cuda::cudart_version() >= 12020 &&\n                   transformer_engine::cuda::cudart_version() < 13000,\n               \"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but run-time CUDA version is \",\n               transformer_engine::cuda::cudart_version());\n    NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120205 &&\n                   transformer_engine::cuda::cublas_version() < 130000,\n               \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but run-time cuBLAS version is \",\n               transformer_engine::cuda::cublas_version());\n    if (m_split == 0) m_split = 1;\n    if (n_split == 0) n_split = 1;\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_ROWS, &m_split,\n        sizeof(m_split)));\n    NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS, &n_split,\n        sizeof(n_split)));\n    if (gemm_producer) {\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER, &counter,\n          sizeof(counter)));\n    } else {\n      NVTE_CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER, &counter,\n          sizeof(counter)));\n    }\n#endif\n  }\n\n  // align the workspace to 256 B\n  const int required_alignment = 256;\n  const auto original_workspace_alignment = _getAlignment(reinterpret_cast<uintptr_t>(workspace));\n  uint8_t *aligned_workspace_ptr =\n      reinterpret_cast<uint8_t *>(workspace) + required_alignment - original_workspace_alignment;\n  workspaceSize = workspaceSize - required_alignment + original_workspace_alignment;\n  const auto new_workspace_alignment =\n      _getAlignment(reinterpret_cast<uintptr_t>(aligned_workspace_ptr));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceCreate(&preference));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize, sizeof(workspaceSize)));\n  const auto A_alignment = _getAlignment(reinterpret_cast<uintptr_t>(param.A));\n  const auto B_alignment = _getAlignment(reinterpret_cast<uintptr_t>(param.B));\n  const auto C_alignment = _getAlignment(reinterpret_cast<uintptr_t>(C));\n  const auto D_alignment = _getAlignment(reinterpret_cast<uintptr_t>(D));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_A_BYTES, &A_alignment, sizeof(A_alignment)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_B_BYTES, &B_alignment, sizeof(B_alignment)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_C_BYTES, &C_alignment, sizeof(C_alignment)));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_D_BYTES, &D_alignment, sizeof(D_alignment)));\n  NVTE_CHECK(new_workspace_alignment % 256 == 0,\n             \"cuBLAS workspace pointer must be aligned to 256 bytes, got \",\n             new_workspace_alignment);\n\n  const auto status =\n      cublasLtMatmulAlgoGetHeuristic(handle, operationDesc, Adesc, Bdesc, Cdesc, Ddesc, preference,\n                                     1, &heuristicResult, &returnedResults);\n  NVTE_CHECK(status != CUBLAS_STATUS_NOT_SUPPORTED,\n             \"Unable to find suitable cuBLAS GEMM algorithm\");\n  NVTE_CHECK_CUBLAS(status);\n  if (returnedResults == 0) NVTE_ERROR(\"Unable to find any suitable algorithms\");\n\n  // D = alpha * (A * B) + beta * C\n  NVTE_CHECK_CUBLAS(cublasLtMatmul(handle, operationDesc, alpha, /* alpha */\n                                   param.A,                      /* A */\n                                   Adesc, param.B,               /* B */\n                                   Bdesc, beta,                  /* beta */\n                                   C,                            /* C */\n                                   Cdesc, D,                     /* D */\n                                   Ddesc, &heuristicResult.algo, /* algo */\n                                   aligned_workspace_ptr,        /* workspace */\n                                   workspaceSize, stream));      /* stream */\n\n  // Update FP8 scale-inv in output tensor\n  // Note: This is a WAR for the case when we have fp8 output but D->scale_inv is not allocated.\n  // TODO: Changing gemm interface so that D->scale_inv is allocated and the scale_inv can be\n  // calculated here.\n  if (is_fp8_dtype(outputD->data.dtype) && outputD->scale_inv.dptr) {\n    update_tensor_scale_inv(outputD, stream);\n  }\n\n  NVTE_CHECK_CUBLAS(cublasLtMatmulPreferenceDestroy(preference));\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(Ddesc));\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(Cdesc));\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(Bdesc));\n  NVTE_CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(Adesc));\n  NVTE_CHECK_CUBLAS(cublasLtMatmulDescDestroy(operationDesc));\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_cublas_gemm(const NVTETensor A, const NVTETensor B, NVTETensor D, const NVTETensor bias,\n                      NVTETensor pre_gelu_out, bool transa, bool transb, bool grad,\n                      NVTETensor workspace, bool accumulate, bool use_split_accumulator,\n                      int math_sm_count, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Tensors\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensorCheck(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  // Scales\n  const float alpha = 1;\n  const float beta = accumulate ? 1 : 0;\n\n  // Check for NVFP4\n  // TODO Remove once alpha scale logic is moved into cublas_gemm function\n  if (is_nvfp_scaling(inputA->scaling_mode) || is_nvfp_scaling(inputB->scaling_mode)) {\n    NVTE_ERROR(\"nvte_cublas_gemm does not support NVFP4 data. Use nvte_cublas_gemm_v2 instead.\");\n  }\n\n  // Launch GEMM\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? CUBLAS_OP_T : CUBLAS_OP_N,\n              (transb) ? CUBLAS_OP_T : CUBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              &alpha, &beta, use_split_accumulator, math_sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_gemm_v2(int transa, int transb, const float *alpha, const NVTETensor A,\n                         const NVTETensor B, const float *beta, const NVTETensor C, NVTETensor D,\n                         NVTETensor workspace, NVTEMatmulConfig config, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm_v2);\n  using namespace transformer_engine;\n\n  // Data tensors\n  const Tensor *A_tensor = convertNVTETensorCheck(A);\n  const Tensor *B_tensor = convertNVTETensorCheck(B);\n  const Tensor *C_tensor = convertNVTETensorCheck(C);\n  Tensor *D_tensor = convertNVTETensorCheck(D);\n  NVTE_CHECK(C_tensor == D_tensor,\n             \"Currently nvte_cublas_gemm_v2 does not support different C and D tensors.\");\n\n  // Workspace\n  void *workspace_ptr = nullptr;\n  size_t workspace_size = 0;\n  Tensor *workspace_tensor = convertNVTETensor(workspace);\n  if (workspace_tensor != nullptr) {\n    workspace_ptr = workspace_tensor->data.dptr;\n    workspace_size =\n        get_buffer_size_bytes(workspace_tensor->data.numel(), workspace_tensor->data.dtype);\n  }\n\n  // Additional config\n  MatmulConfig config_;\n  if (config != nullptr) {\n    config_ = *reinterpret_cast<MatmulConfig *>(config);\n  }\n\n  // Configure GEMM epilogue\n  const bool with_grad_epilogue = (config_.dbias_tensor != nullptr || config_.with_dgelu_epilogue);\n  if (with_grad_epilogue) {\n    NVTE_CHECK(config_.bias_tensor == nullptr && !config_.with_gelu_epilogue,\n               \"Invalid epilogue (bias=\", config_.bias_tensor != nullptr,\n               \", dbias=\", config_.dbias_tensor != nullptr, \", gelu=\", config_.with_gelu_epilogue,\n               \", dgelu=\", config_.with_dgelu_epilogue, \").\");\n  }\n  Tensor dummy_tensor;\n  Tensor *epilogue_bias_tensor = &dummy_tensor;\n  if (!with_grad_epilogue && config_.bias_tensor != nullptr) {\n    epilogue_bias_tensor = convertNVTETensorCheck(config_.bias_tensor);\n  } else if (with_grad_epilogue && config_.dbias_tensor != nullptr) {\n    epilogue_bias_tensor = convertNVTETensorCheck(config_.dbias_tensor);\n  }\n  Tensor *epilogue_aux_tensor = &dummy_tensor;\n  if (config_.with_gelu_epilogue || config_.with_dgelu_epilogue) {\n    NVTE_CHECK(config_.epilogue_aux_tensor != nullptr,\n               \"Requested epilogue (bias=\", config_.bias_tensor != nullptr,\n               \", dbias=\", config_.dbias_tensor != nullptr, \", gelu=\", config_.with_gelu_epilogue,\n               \", dgelu=\", config_.with_dgelu_epilogue, \") without providing aux tensor.\");\n    epilogue_aux_tensor = convertNVTETensor(config_.epilogue_aux_tensor);\n  }\n\n  // Launch GEMM\n  cublas_gemm(A_tensor, B_tensor, D_tensor, epilogue_bias_tensor, epilogue_aux_tensor,\n              transa ? CUBLAS_OP_T : CUBLAS_OP_N, transb ? CUBLAS_OP_T : CUBLAS_OP_N,\n              with_grad_epilogue, workspace_ptr, workspace_size, alpha, beta,\n              config_.use_split_accumulator, config_.sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_gemm_scaled(const NVTETensor A, const NVTETensor B, NVTETensor D,\n                             const NVTETensor bias, NVTETensor pre_gelu_out, bool transa,\n                             bool transb, bool grad, NVTETensor workspace, float alpha, float beta,\n                             bool use_split_accumulator, int math_sm_count, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Tensors\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensorCheck(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  // Check for NVFP4\n  // TODO Remove once alpha scale logic is moved into cublas_gemm function\n  if (is_nvfp_scaling(inputA->scaling_mode) || is_nvfp_scaling(inputB->scaling_mode)) {\n    NVTE_ERROR(\"nvte_cublas_gemm does not support NVFP4 data. Use nvte_cublas_gemm_v2 instead.\");\n  }\n\n  // Launch GEMM\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? CUBLAS_OP_T : CUBLAS_OP_N,\n              (transb) ? CUBLAS_OP_T : CUBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              &alpha, &beta, use_split_accumulator, math_sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_atomic_gemm(const NVTETensor A, const NVTETensor B, NVTETensor D,\n                             const NVTETensor bias, NVTETensor pre_gelu_out, bool transa,\n                             bool transb, bool grad, NVTETensor workspace, bool accumulate,\n                             bool use_split_accumulator, int math_sm_count, int m_split,\n                             int n_split, bool gemm_producer, const NVTETensor counter,\n                             cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_atomic_gemm);\n  using namespace transformer_engine;\n#if !(CUDA_VERSION >= 12020 && CUDA_VERSION < 13000)\n  NVTE_ERROR(\"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#elif !(CUBLAS_VERSION >= 120205 && CUBLAS_VERSION < 130000)\n  NVTE_ERROR(\n      \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but compile-time cuBLAS version is \",\n      CUBLAS_VERSION);\n#else\n  NVTE_CHECK(\n      transformer_engine::cuda::cudart_version() >= 12020 &&\n          transformer_engine::cuda::cudart_version() < 13000,\n      \"Atomic GEMM requires CUDA version >=12.2.0 and <13.0.0, but run-time CUDA version is \",\n      transformer_engine::cuda::cudart_version());\n  NVTE_CHECK(\n      transformer_engine::cuda::cublas_version() >= 120205 &&\n          transformer_engine::cuda::cublas_version() < 130000,\n      \"Atomic GEMM requires cuBLAS version >=12.2.5 and <13.0.0, but run-time cuBLAS version is \",\n      transformer_engine::cuda::cublas_version());\n\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensor(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  const Tensor *inputCounter = convertNVTETensor(counter);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  const void *alpha_ptr = GetScalarOne();\n  const void *beta_ptr = accumulate ? GetScalarOne() : GetScalarZero();\n\n  NVTE_CHECK(is_delayed_tensor_scaling(inputA->scaling_mode) &&\n                 is_delayed_tensor_scaling(inputB->scaling_mode),\n             \"Atomic GEMM only supports delayed scaling.\");\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? CUBLAS_OP_T : CUBLAS_OP_N,\n              (transb) ? CUBLAS_OP_T : CUBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              alpha_ptr, beta_ptr, use_split_accumulator, math_sm_count, m_split, n_split,\n              gemm_producer, inputCounter, stream);\n#endif\n}\n\nvoid multi_stream_cublas_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                              const NVTETensor *bias, NVTETensor *pre_gelu_out, const int num_gemms,\n                              bool transa, bool transb, bool grad, NVTETensor *workspace,\n                              bool accumulate, bool use_split_accumulator, int math_sm_count,\n                              cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  int num_streams = nvte_get_num_compute_streams();\n\n  int num_stream_used = std::min(num_streams, num_gemms);\n  // wait for current stream to finish\n  NVTE_CHECK_CUDA(cudaEventRecord(detail::get_compute_stream_event(0), stream));\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(\n        cudaStreamWaitEvent(detail::get_compute_stream(s), detail::get_compute_stream_event(0)));\n  }\n\n  for (int i = 0; i < num_gemms; i++) {\n    // Check whether GELU or dGELU epilogue is requested\n    Tensor *pre_gelu_tensor = convertNVTETensor(pre_gelu_out[i]);\n    bool with_gelu_dgelu_epilogue =\n        (pre_gelu_tensor != nullptr && pre_gelu_tensor->data.dptr != nullptr);\n\n    // Construct config\n    MatmulConfig config;\n    if (grad) {\n      config.dbias_tensor = bias[i];\n      config.with_dgelu_epilogue = with_gelu_dgelu_epilogue;\n    } else {\n      config.bias_tensor = bias[i];\n      config.with_gelu_epilogue = with_gelu_dgelu_epilogue;\n    }\n    config.epilogue_aux_tensor = pre_gelu_out[i];\n    config.use_split_accumulator = use_split_accumulator;\n    config.sm_count = math_sm_count;\n\n    // Launch GEMM\n    const float alpha = 1.f;\n    const float beta = accumulate ? 1.f : 0.f;\n    nvte_cublas_gemm_v2(transa, transb, &alpha, A[i], B[i], &beta, D[i], D[i],\n                        workspace[i % num_streams], &config,\n                        detail::get_compute_stream(i % num_streams));\n  }\n\n  // record events on compute streams\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(\n        cudaEventRecord(detail::get_compute_stream_event(s), detail::get_compute_stream(s)));\n  }\n  // wait for all compute streams to finish\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(cudaStreamWaitEvent(stream, detail::get_compute_stream_event(s)));\n  }\n}\n\nvoid nvte_multi_stream_cublas_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                                   const NVTETensor *bias, NVTETensor *pre_gelu_out,\n                                   const int num_gemms, bool transa, bool transb, bool grad,\n                                   NVTETensor *workspace, bool accumulate,\n                                   bool use_split_accumulator, int math_sm_count,\n                                   cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_stream_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Deprecation warning\n  NVTE_WARN(\n      \"nvte_multi_stream_cublas_gemm is deprecated and will be removed in a future release. \"\n      \"Please migrate to nvte_multi_tensor_gemm (with CUTLASS Grouped GEMM support when \"\n      \"applicable).\");\n\n  multi_stream_cublas_gemm(A, B, D, bias, pre_gelu_out, num_gemms, transa, transb, grad, workspace,\n                           accumulate, use_split_accumulator, math_sm_count, stream);\n}\n\nnamespace transformer_engine {\n\nusing cublasHandleManager = detail::HandleManager<cublasLtHandle_t, CreateCublasHandle>;\n\nvoid nvte_cublas_handle_init() { auto _ = cublasHandleManager::Instance().GetHandle(); }\n\n}  //  namespace transformer_engine\n\nvoid nvte_multi_tensor_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                            const NVTETensor *bias, NVTETensor *pre_gelu_out, const int num_gemms,\n                            bool transa, bool transb, bool grad, NVTETensor *workspace,\n                            bool accumulate, bool use_split_accumulator, int math_sm_count,\n                            cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_gemm);\n\n  const int current_device = transformer_engine::cuda::current_device();\n  const bool is_hopper = (transformer_engine::cuda::sm_arch(current_device) == 90);\n  const bool use_cutlass = transformer_engine::getenv<bool>(\"NVTE_USE_CUTLASS_GROUPED_GEMM\", false);\n  const bool warn_fallback =\n      transformer_engine::getenv<bool>(\"NVTE_CUTLASS_GROUPED_GEMM_WARN_FALLBACK\", false);\n\n  auto cublas_path = [&]() {\n    multi_stream_cublas_gemm(A, B, D, bias, pre_gelu_out, num_gemms, transa, transb, grad,\n                             workspace, accumulate, use_split_accumulator, math_sm_count, stream);\n  };\n\n  // Currently only support cutlass group gemm on Hopper Arch\n  if (!(is_hopper && use_cutlass)) {\n    cublas_path();\n    return;\n  }\n\n  auto is_empty_arr = [&](const NVTETensor *p) -> bool {\n    if (p == nullptr) return true;\n    for (int i = 0; i < num_gemms; ++i) {\n      if (transformer_engine::convertNVTETensor(p[i])->has_data()) return false;\n    }\n    return true;\n  };\n\n  auto all_groups_uniform_k128 = [&](const NVTETensor *p, bool trans) -> bool {\n    int64_t ref_k = -1;\n    for (size_t i = 0; i < num_gemms; i++) {\n      const auto tensor = transformer_engine::convertNVTETensorCheck(p[i]);\n      const int k = trans ? tensor->data.shape[0] : tensor->data.shape[1];\n\n      if ((k & 127) != 0) return false;\n\n      if (ref_k < 0)\n        ref_k = k;\n      else if (k != ref_k)\n        return false;\n    }\n\n    return true;\n  };\n\n  auto is_supported_dtype = [&]() -> bool {\n    auto *inputA = transformer_engine::convertNVTETensorCheck(A[0]);\n    auto *inputB = transformer_engine::convertNVTETensorCheck(B[0]);\n    auto *OutputD = transformer_engine::convertNVTETensorCheck(D[0]);\n    auto A_type = get_cuda_dtype(inputA->data.dtype);\n    auto B_type = get_cuda_dtype(inputB->data.dtype);\n    auto D_type = get_cuda_dtype(OutputD->data.dtype);\n\n    return (A_type == B_type) && (A_type == D_type) &&\n           ((A_type == CUDA_R_16BF) || (A_type == CUDA_R_16F));\n  };\n\n  // CUTLASS Grouped GEMM fast path (SM90/TMA)\n  // Conditions:\n  //  - No fused epilogue: both bias and pre_gelu_out are empty.\n  //  - Supported dtypes only: FP16/BF16 (FP32 accumulate).\n  //  - Uniform K across groups and K % 128 == 0.\n  //  - use_split_accumulator is ignored for FP16/BF16.\n  //  - grad is irrelevant when bias/pre_gelu_out are empty.\n  //\n  // Otherwise, fall back to cuBLAS.\n  if (is_empty_arr(bias) && is_empty_arr(pre_gelu_out) && is_supported_dtype() &&\n      all_groups_uniform_k128(B, transb)) {\n    cutlass_grouped_gemm(A, B, D, num_gemms, transa, transb, grad, workspace, accumulate,\n                         current_device, math_sm_count, stream);\n  } else {\n    if (warn_fallback) {\n      NVTE_WARN(\"Fallback to cuBLAS grouped GEMM.\");\n    }\n    cublas_path();\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hipblaslt.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/gemm.h>\n#include <transformer_engine/multi_stream.h>\n#include <transformer_engine/recipe.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <mutex>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/handle_manager.h\"\n#include \"../util/logging.h\"\n#include \"../util/multi_stream.h\"\n#include \"./config.h\"\n#include \"./cutlass_grouped_gemm.cuh\"\n\nnamespace {\n\n/* Use CUDA const memory to store scalar 1 and 0 for cublas usage\n*/\n__device__ __constant__ float one_device;\n__device__ __constant__ float zero_device;\n\ninline float *GetScalarOne() {\n  static std::once_flag init_flag;\n  std::call_once(init_flag, []() {\n    float one = 1.0f;\n    NVTE_CHECK_CUDA(hipMemcpyToSymbol(HIP_SYMBOL(one_device), &one, sizeof(float)));\n  });\n  // return address by cudaGetSymbolAddress\n  float *dev_ptr;\n  NVTE_CHECK_CUDA(hipGetSymbolAddress(reinterpret_cast<void **>(&dev_ptr),HIP_SYMBOL(one_device)));\n  return dev_ptr;\n}\n\ninline float *GetScalarZero() {\n  static std::once_flag init_flag;\n  std::call_once(init_flag, []() {\n    float zero = 0.0f;\n    NVTE_CHECK_CUDA(hipMemcpyToSymbol(HIP_SYMBOL(zero_device), &zero, sizeof(float)));\n  });\n  // return address by cudaGetSymbolAddress\n  float *dev_ptr;\n  NVTE_CHECK_CUDA(hipGetSymbolAddress(reinterpret_cast<void **>(&dev_ptr),HIP_SYMBOL(zero_device)));\n  return dev_ptr;\n}\n\n__global__ __launch_bounds__(1) void set_float_kernel(float *ptr, float val) { *ptr = val; }\n\nuint32_t _getAlignment(uintptr_t address) {\n  // alignment are in bytes\n  uint32_t alignment = 256;\n  for (;; alignment /= 2) {\n    if (address % alignment == 0) {\n      return alignment;\n    }\n  }\n}\n\ninline void CreateCublasHandle(hipblasLtHandle_t *handle) {\n  NVTE_CHECK_CUBLAS(hipblasLtCreate(handle));\n}\n\n/* Parameters for cuBLAS GEMM\n *\n * cuBLAS follows the BLAS convention of column-major ordering. This\n * is different than the row-major that is typically used in\n * Transformer Engine.\n *\n */\nstruct GemmParam {\n  void *A = nullptr;\n  void *B = nullptr;\n  hipblasOperation_t transA = HIPBLAS_OP_N;\n  hipblasOperation_t transB = HIPBLAS_OP_N;\n  transformer_engine::DType Atype = transformer_engine::DType::kNumTypes;\n  transformer_engine::DType Btype = transformer_engine::DType::kNumTypes;\n  void *A_scale_inv = nullptr;\n  void *B_scale_inv = nullptr;\n  int lda = 0;  // A column strides\n  int ldb = 0;  // B column strides\n};\n\n/* Populate parameters for cuBLAS GEMM\n *\n * cuBLAS follows the BLAS convention of column-major ordering. This\n * is different than the row-major that is typically used in\n * Transformer Engine.\n *\n */\nGemmParam CanonicalizeGemmInput(const transformer_engine::Tensor &A, const hipblasOperation_t transA,\n                                const transformer_engine::Tensor &B, const hipblasOperation_t transB,\n                                int m, int n, int k) {\n  using namespace transformer_engine;\n  NVTE_CHECK(\n      A.scaling_mode == B.scaling_mode ||\n          (A.scaling_mode == NVTE_BLOCK_SCALING_1D && B.scaling_mode == NVTE_BLOCK_SCALING_2D) ||\n          (A.scaling_mode == NVTE_BLOCK_SCALING_2D && B.scaling_mode == NVTE_BLOCK_SCALING_1D),\n      \"Inputs A and B to GEMM need to have compatible scaling modes, but got A.scaling_mode = \" +\n          to_string(A.scaling_mode) + \", B.scaling_mode = \" + to_string(B.scaling_mode));\n  NVTE_CHECK(A.has_data() || A.has_columnwise_data(), \"Input A does not hold any data!\");\n  NVTE_CHECK(B.has_data() || B.has_columnwise_data(), \"Input B does not hold any data!\");\n  GemmParam ret;\n\n  // Transpose mode with column-major ordering\n  bool is_A_transposed = transA == HIPBLAS_OP_T;\n  bool is_B_transposed = transB == HIPBLAS_OP_T;\n\n  // Set conditions for MXFP8 and NVFP4 gemm execution.\n  const auto nvfp4 = is_nvfp_scaling(A.scaling_mode) && is_nvfp_scaling(B.scaling_mode);\n  const auto mxfp8 = !nvfp4 && is_mxfp_scaling(A.scaling_mode) && is_mxfp_scaling(B.scaling_mode);\n  int is_nvte_non_tn_fp8_gemm_supported = 0;  // needed only for per tensor scaling\n  if (is_tensor_scaling(A.scaling_mode) || is_tensor_scaling(B.scaling_mode)) {\n    is_nvte_non_tn_fp8_gemm_supported = nvte_is_non_tn_fp8_gemm_supported();\n  }\n\n  // Configure A matrix\n  if (is_tensor_scaling(A.scaling_mode)) {\n    // Unscaled or FP8 tensor scaling\n    ret.A = A.data.dptr;\n    ret.transA = transA;\n    ret.Atype = A.data.dtype;\n    ret.A_scale_inv = A.scale_inv.dptr;\n    ret.lda = is_A_transposed ? k : m;\n    if (!is_nvte_non_tn_fp8_gemm_supported && !is_A_transposed) {\n      // Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n      if (A.has_columnwise_data() && is_fp8_dtype(A.columnwise_data.dtype)) {\n        ret.A = A.columnwise_data.dptr;\n        ret.transA = HIPBLAS_OP_T;\n        ret.Atype = A.columnwise_data.dtype;\n        ret.A_scale_inv = A.columnwise_scale_inv.dptr;\n        ret.lda = k;\n      } else {\n        NVTE_CHECK(!is_fp8_dtype(ret.Atype), \"Input A is missing column-wise usage\");\n      }\n    } else if (is_nvte_non_tn_fp8_gemm_supported && !A.has_data()) {\n      // Blackwell supports any GEMM layout for FP8, so we can use column-wise/transposed\n      // data  with the mirrored transpose-flag if we don't have row-wise data.\n      NVTE_CHECK(A.has_columnwise_data() && is_fp8_dtype(A.columnwise_data.dtype),\n                 \"Input A is missing column-wise usage\");\n      ret.A = A.columnwise_data.dptr;\n      ret.transA = is_A_transposed ? HIPBLAS_OP_N : HIPBLAS_OP_T;\n      ret.Atype = A.columnwise_data.dtype;\n      ret.A_scale_inv = A.columnwise_scale_inv.dptr;\n      ret.lda = is_A_transposed ? m : k;\n    }\n\n    if (is_fp8_dtype(ret.Atype)) {\n      // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n      NVTE_CHECK(ret.lda % 16 == 0,\n                 \"Leading dimension requirement on A for FP8 GEMM. Caller must pad.\");\n    }\n  } else if (nvfp4) {\n    // NVFP4 GEMM. Either the pure NVFP4 recipe or the FWD pass of the Hybrid NVFP4/MXFP8 recipe.\n\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(is_nvfp4_scaling(A.scaling_mode),\n                 \"Input A has unsupported combination of recipe and layout\");\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = HIPBLAS_OP_T;  // NVFP4 gemm is only supported in TN layout.\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = k;\n  } else if (mxfp8) {\n    // MXFP8 GEMM. Either for pure MXFP8 recipe or backward of Hybrid NVFP4 recipe.\n    // Note: Row-wise and column-wise data are scaled along different\n    // dimensions (with matrix interpreted in row-major order).\n\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = transA;\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = is_A_transposed ? k : m;\n  } else if (A.scaling_mode == NVTE_BLOCK_SCALING_1D || A.scaling_mode == NVTE_BLOCK_SCALING_2D) {\n    // FP8 block scaling\n    // Note: Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n    if (is_A_transposed) {\n      NVTE_CHECK(A.has_data(), \"Input A is missing row-wise usage\");\n    } else {\n      NVTE_CHECK(A.has_columnwise_data(), \"Input A is missing column-wise usage\");\n    }\n    ret.A = is_A_transposed ? A.data.dptr : A.columnwise_data.dptr;\n    ret.transA = HIPBLAS_OP_T;\n    ret.Atype = is_A_transposed ? A.data.dtype : A.columnwise_data.dtype;\n    ret.A_scale_inv = is_A_transposed ? A.scale_inv.dptr : A.columnwise_scale_inv.dptr;\n    ret.lda = k;\n\n    // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n    NVTE_CHECK((ret.lda % 16) == 0,\n               \"Leading dimension requirement on NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    // Divisibility of 8 derived from FP8 (m * CTypeSize) % 16 == 0 requirement.\n    // Smallest supported CType is 2 bytes in this scaling mode.\n    NVTE_CHECK((m % 8) == 0,\n               \"Outer dimension requirement on A for NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n  } else {\n    NVTE_ERROR(\"A has unsupported scaling mode\");\n  }\n\n  // Configure B matrix\n  if (is_tensor_scaling(B.scaling_mode)) {\n    // Unscaled or FP8 tensor scaling\n    ret.B = B.data.dptr;\n    ret.transB = transB;\n    ret.Btype = B.data.dtype;\n    ret.B_scale_inv = B.scale_inv.dptr;\n    ret.ldb = is_B_transposed ? n : k;\n    if (!is_nvte_non_tn_fp8_gemm_supported && is_B_transposed) {\n      // Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n      if (B.has_columnwise_data() && is_fp8_dtype(B.columnwise_data.dtype)) {\n        ret.B = B.columnwise_data.dptr;\n        ret.transB = HIPBLAS_OP_N;\n        ret.Btype = B.columnwise_data.dtype;\n        ret.B_scale_inv = B.columnwise_scale_inv.dptr;\n        ret.ldb = k;\n      } else {\n        NVTE_CHECK(!is_fp8_dtype(ret.Btype), \"Input B is missing column-wise usage\");\n      }\n    } else if (is_nvte_non_tn_fp8_gemm_supported && !B.has_data()) {\n      // Blackwell supports any GEMM layout for FP8, so we can use column-wise/transposed\n      // data with the mirrored transpose-flag if we don't have row-wise data.\n      NVTE_CHECK(B.has_columnwise_data() && is_fp8_dtype(B.columnwise_data.dtype),\n                 \"Input B is missing column-wise usage\");\n      ret.B = B.columnwise_data.dptr;\n      ret.transB = is_B_transposed ? HIPBLAS_OP_N : HIPBLAS_OP_T;\n      ret.Btype = B.columnwise_data.dtype;\n      ret.B_scale_inv = B.columnwise_scale_inv.dptr;\n      ret.ldb = is_B_transposed ? k : n;\n    }\n\n    if (is_fp8_dtype(ret.Atype)) {\n      // Requirements from https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n      NVTE_CHECK(ret.ldb % 16 == 0,\n                 \"Leading dimension requirement on B for FP8 GEMM. Caller must pad.\");\n    }\n  } else if (nvfp4) {\n    if (is_B_transposed) {\n      NVTE_CHECK(is_nvfp4_scaling(B.scaling_mode),\n                 \"Input B has unsupported combination of recipe and layout\");\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = HIPBLAS_OP_N;  // NVFP4 gemm is only supported in TN layout.\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = k;\n  } else if (mxfp8) {\n    if (is_B_transposed) {\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = transB;\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = is_B_transposed ? n : k;\n  } else if (B.scaling_mode == NVTE_BLOCK_SCALING_1D || B.scaling_mode == NVTE_BLOCK_SCALING_2D) {\n    // FP8 block scaling\n    // Note: Hopper only supports TN GEMMs for FP8. \"Column-wise data\" is transpose of data.\n    if (is_B_transposed) {\n      NVTE_CHECK(B.has_columnwise_data(), \"Input B is missing column-wise usage\");\n    } else {\n      NVTE_CHECK(B.has_data(), \"Input B is missing row-wise usage\");\n    }\n    ret.B = is_B_transposed ? B.columnwise_data.dptr : B.data.dptr;\n    ret.transB = HIPBLAS_OP_N;\n    ret.Btype = is_B_transposed ? B.columnwise_data.dtype : B.data.dtype;\n    ret.B_scale_inv = is_B_transposed ? B.columnwise_scale_inv.dptr : B.scale_inv.dptr;\n    ret.ldb = k;\n\n    // Requirements from\n    // https://docs.nvidia.com/cuda/cublas/#tensor-core-usage\n    NVTE_CHECK((ret.ldb % 16) == 0,\n               \"B tensor stride requirement on NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    if (B.scaling_mode == NVTE_BLOCK_SCALING_1D) {\n      // Observed this requirement only present for B tensor is 1D quantized.\n      NVTE_CHECK((n % 8) == 0,\n                 \"Outer dimension requirement on B for NVTE_BLOCK_SCALING GEMM. Caller must pad.\");\n    }\n  } else {\n    NVTE_ERROR(\"B has unsupported scaling mode\");\n  }\n\n  return ret;\n}\n\n}  // namespace\n\nnamespace transformer_engine {\n\nusing cublasHandleManager = detail::HandleManager<hipblasLtHandle_t, CreateCublasHandle>;\n\nvoid cublas_gemm(const Tensor *inputA, const Tensor *inputB, Tensor *outputD,\n                 const Tensor *inputBias, Tensor *outputPreGelu, hipblasOperation_t transa,\n                 hipblasOperation_t transb, bool grad, void *workspace, size_t workspaceSize,\n                 const void *alpha, const void *beta, bool use_split_accumulator, int math_sm_count,\n                 int m_split, int n_split, bool gemm_producer, const Tensor *inputCounter,\n                 hipStream_t stream) {\n  NVTE_CHECK(!inputA->row_scaled_nvfp4 && !inputB->row_scaled_nvfp4,\n             \"cuBLAS GEMM does not support row-scaled NVFP4 inputs.\");\n\n  // Tensor dims in row-major order\n  const int A0 = inputA->flat_first_dim();\n  const int A1 = inputA->flat_last_dim();\n  const int B0 = inputB->flat_first_dim();\n  const int B1 = inputB->flat_last_dim();\n\n  // GEMM dims in column-major order\n  const int m = transa == HIPBLAS_OP_T ? A0 : A1;\n  const int n = transb == HIPBLAS_OP_T ? B1 : B0;\n  const int k = transa == HIPBLAS_OP_T ? A1 : A0;\n  NVTE_CHECK((transb == HIPBLAS_OP_T ? B0 : B1) == k,\n             \"GEMM inputs have incompatible dimensions (A is \", A0, \"x\", A1, \", B is \", B0, \"x\", B1,\n             \")\");\n  const int ldd = m;\n\n  // Return immediately if GEMM is trivial\n  if (m <= 0 || n <= 0) {\n    return;\n  }\n  NVTE_CHECK(k > 0);\n\n  const GemmParam param = CanonicalizeGemmInput(*inputA, transa, *inputB, transb, m, n, k);\n\n  void *C = outputD->data.dptr;\n  void *D = outputD->data.dptr;\n  void *D_scale = outputD->scale.dptr;\n  void *D_amax = outputD->amax.dptr;\n  void *bias_ptr = inputBias->data.dptr;\n  const bool bias = bias_ptr != nullptr;\n  void *pre_gelu_out = outputPreGelu->data.dptr;\n  void *counter = nullptr;\n  if (inputCounter != nullptr) {\n    counter = inputCounter->data.dptr;\n  }\n  const bool gelu = pre_gelu_out != nullptr;\n  const bool use_fp8 = is_fp8_dtype(param.Atype) || is_fp8_dtype(param.Btype);\n  const bool use_fp4 = is_fp4_dtype(param.Atype) || is_fp4_dtype(param.Btype);\n\n  // Update scaling factors with NVFP4 tensor scales\n  // TODO: Check whether scales are on CPU/GPU or add API to control.\n  // Currently scales are assumed to be on CPU when amax is provided\n  // and on GPU when not provided, but this is brittle.\n  if (use_fp4 &&\n      ((transa == HIPBLAS_OP_T ? inputA->amax.dptr : inputA->columnwise_amax.dptr) != nullptr ||\n       (transb == HIPBLAS_OP_T ? inputB->columnwise_amax.dptr : inputB->amax.dptr) != nullptr)) {\n    // Reserve some workspace for alpha scale\n    NVTE_CHECK(workspaceSize >= 4,\n               \"NVFP4 GEMM requires at least 4 byte workspace for alpha scale, but only has \",\n               workspaceSize, \" bytes remaining.\");\n    workspaceSize = (workspaceSize / 4) * 4 - 4;  // Remove last 4 aligned bytes\n    uint8_t *workspace_ptr = reinterpret_cast<uint8_t *>(workspace);\n    float *new_alpha_ptr = reinterpret_cast<float *>(&workspace_ptr[workspaceSize]);\n\n    // Update alpha scale on device\n    // Note: Compute NVFP4 tensor scales based on amaxes and then\n    // divide from alpha scale. This way we only need to apply NVFP4\n    // tensor scales in matmul output, instead of in matmul inputs.\n    float old_alpha = *reinterpret_cast<const float *>(alpha);  // Assumed to be on CPU\n    TensorWrapper new_alpha_tensor(new_alpha_ptr, std::vector<size_t>{1}, DType::kFloat32);\n    bool a_rowwise_amax = transa == HIPBLAS_OP_T;\n    bool b_rowwise_amax = transb != HIPBLAS_OP_T;\n    nvte_nvfp4_compute_per_tensor_scale(inputA->nvte_tensor, a_rowwise_amax, inputB->nvte_tensor,\n                                        b_rowwise_amax, old_alpha, new_alpha_tensor.data(), stream);\n    alpha = new_alpha_ptr;\n\n    // Make sure beta scale is on device\n    float old_beta = *reinterpret_cast<const float *>(beta);  // Assumed to be on CPU\n    if (old_beta == 0) {\n      beta = GetScalarZero();  // Device constant memory\n    } else if (old_beta == 1) {\n      beta = GetScalarOne();  // Device constant memory\n    } else {\n      // Move beta to workspace\n      NVTE_CHECK(workspaceSize >= 4,\n                 \"NVFP4 GEMM requires at least 4 byte workspace for beta scale, but only has \",\n                 workspaceSize, \" bytes remaining.\");\n      workspaceSize = (workspaceSize / 4) * 4 - 4;  // Remove last 4 aligned bytes\n      float *new_beta_ptr = reinterpret_cast<float *>(&workspace_ptr[workspaceSize]);\n      set_float_kernel<<<1, 1, 0, stream>>>(new_beta_ptr, old_beta);\n      NVTE_CHECK_CUDA(hipGetLastError());\n      beta = new_beta_ptr;\n    }\n  }\n\n  const hipDataType A_type = get_cuda_dtype(param.Atype);\n  const hipDataType B_type = get_cuda_dtype(param.Btype);\n  const hipDataType D_type = get_cuda_dtype(outputD->data.dtype);\n  const hipDataType bias_type = get_cuda_dtype(inputBias->data.dtype);\n\n  NVTE_CHECK(!is_fp8_dtype(param.Atype) || param.A_scale_inv != nullptr,\n             \"FP8 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp8_dtype(param.Btype) || param.B_scale_inv != nullptr,\n             \"FP8 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp4_dtype(param.Atype) || param.A_scale_inv != nullptr,\n             \"FP4 input to GEMM requires inverse of scale!\");\n  NVTE_CHECK(!is_fp4_dtype(param.Btype) || param.B_scale_inv != nullptr,\n             \"FP4 input to GEMM requires inverse of scale!\");\n\n  // check consistency of arguments:\n  // if fp8 is desired, context cannot be null\n  // fp8 + gelu fusion + fp8 aux is unavailable right now.\n  if ((use_fp8 || use_fp4) && gelu) {\n    NVTE_CHECK(!is_fp8_dtype(outputPreGelu->data.dtype),\n               \"fp8 Aux output for gemm + gelu fusion not supported!\");\n  }\n  if (is_fp4_dtype(outputD->data.dtype)) {\n    NVTE_ERROR(\"FP4 GEMM output is not supported!\");\n  }\n  if (use_fp4 && (D_type == HIP_R_16F)) {\n    NVTE_ERROR(\"FP4 GEMM does not support FP16 output!\");\n  }\n\n  hipblasLtHandle_t handle = cublasHandleManager::Instance().GetHandle();\n\n  hipblasLtMatmulDesc_t operationDesc = nullptr;\n  hipblasLtMatrixLayout_t Adesc = nullptr, Bdesc = nullptr, Cdesc = nullptr, Ddesc = nullptr;\n  hipblasLtMatmulPreference_t preference = nullptr;\n  int returnedResults = 0;\n  hipblasLtMatmulHeuristicResult_t heuristicResult = {};\n  hipblasLtEpilogue_t epilogue = HIPBLASLT_EPILOGUE_DEFAULT;\n\n  int64_t ld_gelumat = (int64_t)ldd;\n\n  // Use TF32 only for pure FP32 GEMM.\n  hipblasComputeType_t gemm_compute_type = HIPBLAS_COMPUTE_32F;\n  if (A_type == HIP_R_32F && B_type == HIP_R_32F && D_type == HIP_R_32F) {\n    gemm_compute_type = HIPBLAS_COMPUTE_32F_FAST_TF32;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Adesc, A_type, param.transA == HIPBLAS_OP_N ? m : k,\n                                               param.transA == HIPBLAS_OP_N ? k : m, param.lda));\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Bdesc, B_type, param.transB == HIPBLAS_OP_N ? k : n,\n                                               param.transB == HIPBLAS_OP_N ? n : k, param.ldb));\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Ddesc, D_type, m, n, ldd));\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescCreate(&operationDesc, gemm_compute_type, HIP_R_32F));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc, HIPBLASLT_MATMUL_DESC_TRANSA,\n                                                   &param.transA, sizeof(param.transA)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc, HIPBLASLT_MATMUL_DESC_TRANSB,\n                                                   &param.transB, sizeof(param.transB)));\n  // Set math SM count\n  if (math_sm_count != 0) {\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                     CUBLASLT_MATMUL_DESC_SM_COUNT_TARGET,\n                                                     &math_sm_count, sizeof(math_sm_count)));\n  }\n\n  // set fp8/fp4 attributes -- input and output types should already be set to fp8/fp4\n  // as appropriate. Note: gelu fusion isn't available right now, and we don't need\n  // amax(D) either (next op is high precision).\n  const bool mxfp8_gemm = !use_fp4 && is_mxfp8_scaling(inputA->scaling_mode);\n\n  if (use_fp8 || use_fp4) {\n    // Fast accumulation is only supported for FP8.\n    const int8_t fastAccuMode = (use_split_accumulator) ? 0 : use_fp8;\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc, CUBLASLT_MATMUL_DESC_FAST_ACCUM,\n                                                     &fastAccuMode, sizeof(fastAccuMode)));\n\n    // Scaling factors.\n#if CUBLAS_VERSION >= 120800\n    hipblasLtMatmulMatrixScale_t scaling_mode_a;\n    hipblasLtMatmulMatrixScale_t scaling_mode_b;\n#endif  // CUBLAS_VERSION >= 120800\n    if (is_tensor_scaling(inputA->scaling_mode) && is_tensor_scaling(inputB->scaling_mode)) {\n      void *A_scale_inverse = param.A_scale_inv;\n      void *B_scale_inverse = param.B_scale_inv;\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n#if CUBLAS_VERSION >= 120800\n      scaling_mode_a = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;\n      scaling_mode_b = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;\n#endif  // CUBLAS_VERSION >= 120800\n    } else if (mxfp8_gemm) {\n#if CUBLAS_VERSION >= 120800\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120800,\n                 \"MXFP8 requires cuBLAS 12.8+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that scales are in expected format\n      NVTE_CHECK(inputA->with_gemm_swizzled_scales,\n                 \"MXFP8 scales are not in format expected by GEMM\");\n      NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n                 \"MXFP8 scales are not in format expected by GEMM\");\n\n      // Configure cuBLAS scales\n      fp8e8m0 *A_scale_inverse = reinterpret_cast<fp8e8m0 *>(param.A_scale_inv);\n      fp8e8m0 *B_scale_inverse = reinterpret_cast<fp8e8m0 *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n      scaling_mode_b = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;\n\n      // Workaround for heuristic cache bug in cublasLt. This separates the MXFP8 cache key from non-block scaling.\n      // CUBLASLT_MATMUL_DESC_ALPHA_VECTOR_BATCH_STRIDE is unused for block scaling so it's safe to set.\n      if (transformer_engine::cuda::cublas_version() <= 120803) {\n        const int64_t dummy_a_vec_stride = 1;\n        NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n            operationDesc, CUBLASLT_MATMUL_DESC_ALPHA_VECTOR_BATCH_STRIDE, &dummy_a_vec_stride,\n            sizeof(dummy_a_vec_stride)));\n      }\n#else\n      NVTE_ERROR(\"MXFP8 requires cuBLAS 12.8+, but compile-time cuBLAS version is \",\n                 CUBLAS_VERSION);\n#endif                     // CUBLAS_VERSION >= 120800\n    } else if (use_fp4) {  // NVFP4 GEMM\n#if CUBLAS_VERSION >= 120800\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120800,\n                 \"FP4 requires cuBLAS 12.8+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that scales are in expected format\n      NVTE_CHECK(inputA->with_gemm_swizzled_scales,\n                 \"NVFP4 block scales are not in format expected by GEMM\");\n      NVTE_CHECK(inputB->with_gemm_swizzled_scales,\n                 \"NVFP4 block scales are not in format expected by GEMM\");\n\n      // alpha and beta are device pointers to FP32\n      const hipDataType scale_type = HIP_R_32F;\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_SCALE_TYPE, &scale_type, sizeof(scale_type)));\n      const hipblasLtPointerMode_t pointer_mode = HIPBLASLT_POINTER_MODE_DEVICE;\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, HIPBLASLT_MATMUL_DESC_POINTER_MODE, &pointer_mode, sizeof(pointer_mode)));\n\n      // Configure cuBLAS scales\n      fp8e4m3 *A_scale_inverse = reinterpret_cast<fp8e4m3 *>(param.A_scale_inv);\n      fp8e4m3 *B_scale_inverse = reinterpret_cast<fp8e4m3 *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;\n      scaling_mode_b = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;\n#else\n      NVTE_ERROR(\"FP4 requires cuBLAS 12.8+, but compile-time cuBLAS version is \", CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= 120800\n    } else if ((inputA->scaling_mode == NVTE_BLOCK_SCALING_1D ||\n                inputA->scaling_mode == NVTE_BLOCK_SCALING_2D) &&\n               (inputB->scaling_mode == NVTE_BLOCK_SCALING_1D ||\n                inputB->scaling_mode == NVTE_BLOCK_SCALING_2D)) {\n#if CUBLAS_VERSION >= 120900\n      NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120900,\n                 \"FP8 block scaling requires cuBLAS 12.9+, but run-time cuBLAS version is \",\n                 transformer_engine::cuda::cublas_version());\n\n      // Check that matrix formats are valid\n      NVTE_CHECK((!(inputA->scaling_mode == NVTE_BLOCK_SCALING_2D &&\n                    inputB->scaling_mode == NVTE_BLOCK_SCALING_2D)),\n                 \"Only 1D by 1D, 1D by 2D, and 2D by 1D block scaling GEMM is supported, \"\n                 \"but got 2D by 2D\");\n\n      // Configure cuBLAS scales\n      float *A_scale_inverse = reinterpret_cast<float *>(param.A_scale_inv);\n      float *B_scale_inverse = reinterpret_cast<float *>(param.B_scale_inv);\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER,\n                                                       &A_scale_inverse, sizeof(A_scale_inverse)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER,\n                                                       &B_scale_inverse, sizeof(B_scale_inverse)));\n      scaling_mode_a = inputA->scaling_mode == NVTE_BLOCK_SCALING_1D\n                           ? HIPBLASLT_MATMUL_MATRIX_SCALE_VEC128_32F\n                           : HIPBLASLT_MATMUL_MATRIX_SCALE_BLK128x128_32F;\n      scaling_mode_b = inputB->scaling_mode == NVTE_BLOCK_SCALING_1D\n                           ? HIPBLASLT_MATMUL_MATRIX_SCALE_VEC128_32F\n                           : HIPBLASLT_MATMUL_MATRIX_SCALE_BLK128x128_32F;\n#else\n      NVTE_ERROR(\"FP8 block scaling requires cuBLAS 12.9+, but compile-time cuBLAS version is \",\n                 CUBLAS_VERSION);\n#endif  // CUBLAS_VERSION >= 120900\n    } else {\n      NVTE_ERROR(\"Not implemented scaling modes: \" + to_string(inputA->scaling_mode) + \" and  \" +\n                 to_string(inputB->scaling_mode) + \".\");\n    }\n\n#if CUBLAS_VERSION >= 120800\n    if (transformer_engine::cuda::cublas_version() >= 120800) {\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_A_SCALE_MODE,\n                                                       &scaling_mode_a, sizeof(scaling_mode_a)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                       HIPBLASLT_MATMUL_DESC_B_SCALE_MODE,\n                                                       &scaling_mode_b, sizeof(scaling_mode_b)));\n    }\n#endif  // CUBLAS_VERSION >= 120800\n    if (is_fp8_dtype(outputD->data.dtype)) {\n      // Accumulation mode not supported for FP8 output\n      C = nullptr;\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, HIPBLASLT_MATMUL_DESC_D_SCALE_POINTER, &D_scale, sizeof(D_scale)));\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, HIPBLASLT_MATMUL_DESC_AMAX_D_POINTER, &D_amax, sizeof(D_amax)));\n#if CUBLAS_VERSION >= 120800\n      if (transformer_engine::cuda::cublas_version() >= 120800) {\n        // NOTE: In all current cases where FP8 output is supported, the input is\n        // scaled identically to the output.\n        NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                         CUBLASLT_MATMUL_DESC_D_SCALE_MODE,\n                                                         &scaling_mode_a, sizeof(scaling_mode_a)));\n      }\n#endif  // CUBLAS_VERSION >= 120800\n      // For FP8 output, cuBLAS requires C_type to match bias_type and\n      // be FP16/BF16\n      const hipDataType C_type = bias ? bias_type : HIP_R_16BF;\n      NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Cdesc, C_type, m, n, ldd));\n    } else {\n      NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Cdesc, D_type, m, n, ldd));\n    }\n    if (bias) {\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, HIPBLASLT_MATMUL_DESC_BIAS_DATA_TYPE, &bias_type, sizeof(bias_type)));\n    }\n  } else {\n    NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutCreate(&Cdesc, D_type, m, n, ldd));\n  }\n\n  if (bias && gelu) {\n    if (grad) {\n      epilogue = HIPBLASLT_EPILOGUE_DGELU_BGRAD;\n    } else {\n      epilogue = HIPBLASLT_EPILOGUE_GELU_AUX_BIAS;\n    }\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_BIAS_POINTER, &bias_ptr, sizeof(bias_ptr)));\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                     HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,\n                                                     &pre_gelu_out, sizeof(pre_gelu_out)));\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_LD, &ld_gelumat, sizeof(ld_gelumat)));\n    const hipDataType aux_type = get_cuda_dtype(outputPreGelu->data.dtype);\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE, &aux_type, sizeof(aux_type)));\n  } else if (bias) {\n    if (grad) {\n      // grad output is always input B\n      epilogue = HIPBLASLT_EPILOGUE_BGRADB;\n    } else {\n      epilogue = HIPBLASLT_EPILOGUE_BIAS;\n    }\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_BIAS_POINTER, &bias_ptr, sizeof(bias_ptr)));\n  } else if (gelu) {\n    if (grad) {\n      epilogue = HIPBLASLT_EPILOGUE_DGELU;\n    } else {\n      epilogue = HIPBLASLT_EPILOGUE_GELU_AUX;\n    }\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc,\n                                                     HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,\n                                                     &pre_gelu_out, sizeof(pre_gelu_out)));\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_LD, &ld_gelumat, sizeof(ld_gelumat)));\n    const hipDataType aux_type = get_cuda_dtype(outputPreGelu->data.dtype);\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE, &aux_type, sizeof(aux_type)));\n  }\n\n  if ((inputA->scaling_mode == NVTE_BLOCK_SCALING_1D) ||\n      (inputA->scaling_mode == NVTE_BLOCK_SCALING_2D)) {\n    NVTE_CHECK((epilogue == HIPBLASLT_EPILOGUE_DEFAULT || epilogue == HIPBLASLT_EPILOGUE_BIAS ||\n                epilogue == HIPBLASLT_EPILOGUE_DGELU),\n               \"Epilogue requested outside of the available and tested cuBLAS functionality for \"\n               \"float8 block scaled GEMM\");\n  }\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE,\n                                                   &epilogue, sizeof(epilogue)));\n\n  if (counter != nullptr) {\n#if !(CUDA_VERSION >= 12020 && CUDA_VERSION < 13000)\n    NVTE_ERROR(\"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but compile-time CUDA version is \",\n               CUDA_VERSION);\n#elif !(CUBLAS_VERSION >= 120205 && CUBLAS_VERSION < 130000)\n    NVTE_ERROR(\n        \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but compile-time cuBLAS version is \",\n        CUBLAS_VERSION);\n#else\n    NVTE_CHECK(transformer_engine::cuda::cudart_version() >= 12020 &&\n                   transformer_engine::cuda::cudart_version() < 13000,\n               \"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but run-time CUDA version is \",\n               transformer_engine::cuda::cudart_version());\n    NVTE_CHECK(transformer_engine::cuda::cublas_version() >= 120205 &&\n                   transformer_engine::cuda::cublas_version() < 130000,\n               \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but run-time cuBLAS version is \",\n               transformer_engine::cuda::cublas_version());\n    if (m_split == 0) m_split = 1;\n    if (n_split == 0) n_split = 1;\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_ROWS, &m_split,\n        sizeof(m_split)));\n    NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n        operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS, &n_split,\n        sizeof(n_split)));\n    if (gemm_producer) {\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER, &counter,\n          sizeof(counter)));\n    } else {\n      NVTE_CHECK_CUBLAS(hipblasLtMatmulDescSetAttribute(\n          operationDesc, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER, &counter,\n          sizeof(counter)));\n    }\n#endif\n  }\n\n  // align the workspace to 256 B\n  const int required_alignment = 256;\n  const auto original_workspace_alignment = _getAlignment(reinterpret_cast<uintptr_t>(workspace));\n  uint8_t *aligned_workspace_ptr =\n      reinterpret_cast<uint8_t *>(workspace) + required_alignment - original_workspace_alignment;\n  workspaceSize = workspaceSize - required_alignment + original_workspace_alignment;\n  const auto new_workspace_alignment =\n      _getAlignment(reinterpret_cast<uintptr_t>(aligned_workspace_ptr));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceCreate(&preference));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      preference, HIPBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize, sizeof(workspaceSize)));\n  const auto A_alignment = _getAlignment(reinterpret_cast<uintptr_t>(param.A));\n  const auto B_alignment = _getAlignment(reinterpret_cast<uintptr_t>(param.B));\n  const auto C_alignment = _getAlignment(reinterpret_cast<uintptr_t>(C));\n  const auto D_alignment = _getAlignment(reinterpret_cast<uintptr_t>(D));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_A_BYTES, &A_alignment, sizeof(A_alignment)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_B_BYTES, &B_alignment, sizeof(B_alignment)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_C_BYTES, &C_alignment, sizeof(C_alignment)));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceSetAttribute(\n      preference, CUBLASLT_MATMUL_PREF_MIN_ALIGNMENT_D_BYTES, &D_alignment, sizeof(D_alignment)));\n  NVTE_CHECK(new_workspace_alignment % 256 == 0,\n             \"cuBLAS workspace pointer must be aligned to 256 bytes, got \",\n             new_workspace_alignment);\n\n  const auto status =\n      hipblasLtMatmulAlgoGetHeuristic(handle, operationDesc, Adesc, Bdesc, Cdesc, Ddesc, preference,\n                                     1, &heuristicResult, &returnedResults);\n  NVTE_CHECK(status != HIPBLAS_STATUS_NOT_SUPPORTED,\n             \"Unable to find suitable cuBLAS GEMM algorithm\");\n  NVTE_CHECK_CUBLAS(status);\n  if (returnedResults == 0) NVTE_ERROR(\"Unable to find any suitable algorithms\");\n\n  // D = alpha * (A * B) + beta * C\n  NVTE_CHECK_CUBLAS(hipblasLtMatmul(handle, operationDesc, alpha, /* alpha */\n                                   param.A,                      /* A */\n                                   Adesc, param.B,               /* B */\n                                   Bdesc, beta,                  /* beta */\n                                   C,                            /* C */\n                                   Cdesc, D,                     /* D */\n                                   Ddesc, &heuristicResult.algo, /* algo */\n                                   aligned_workspace_ptr,        /* workspace */\n                                   workspaceSize, stream));      /* stream */\n\n  // Update FP8 scale-inv in output tensor\n  // Note: This is a WAR for the case when we have fp8 output but D->scale_inv is not allocated.\n  // TODO: Changing gemm interface so that D->scale_inv is allocated and the scale_inv can be\n  // calculated here.\n  if (is_fp8_dtype(outputD->data.dtype) && outputD->scale_inv.dptr) {\n    update_tensor_scale_inv(outputD, stream);\n  }\n\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulPreferenceDestroy(preference));\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutDestroy(Ddesc));\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutDestroy(Cdesc));\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutDestroy(Bdesc));\n  NVTE_CHECK_CUBLAS(hipblasLtMatrixLayoutDestroy(Adesc));\n  NVTE_CHECK_CUBLAS(hipblasLtMatmulDescDestroy(operationDesc));\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_cublas_gemm(const NVTETensor A, const NVTETensor B, NVTETensor D, const NVTETensor bias,\n                      NVTETensor pre_gelu_out, bool transa, bool transb, bool grad,\n                      NVTETensor workspace, bool accumulate, bool use_split_accumulator,\n                      int math_sm_count, hipStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Tensors\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensorCheck(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  // Scales\n  const float alpha = 1;\n  const float beta = accumulate ? 1 : 0;\n\n  // Check for NVFP4\n  // TODO Remove once alpha scale logic is moved into cublas_gemm function\n  if (is_nvfp_scaling(inputA->scaling_mode) || is_nvfp_scaling(inputB->scaling_mode)) {\n    NVTE_ERROR(\"nvte_cublas_gemm does not support NVFP4 data. Use nvte_cublas_gemm_v2 instead.\");\n  }\n\n  // Launch GEMM\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? HIPBLAS_OP_T : HIPBLAS_OP_N,\n              (transb) ? HIPBLAS_OP_T : HIPBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              &alpha, &beta, use_split_accumulator, math_sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_gemm_v2(int transa, int transb, const float *alpha, const NVTETensor A,\n                         const NVTETensor B, const float *beta, const NVTETensor C, NVTETensor D,\n                         NVTETensor workspace, NVTEMatmulConfig config, hipStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm_v2);\n  using namespace transformer_engine;\n\n  // Data tensors\n  const Tensor *A_tensor = convertNVTETensorCheck(A);\n  const Tensor *B_tensor = convertNVTETensorCheck(B);\n  const Tensor *C_tensor = convertNVTETensorCheck(C);\n  Tensor *D_tensor = convertNVTETensorCheck(D);\n  NVTE_CHECK(C_tensor == D_tensor,\n             \"Currently nvte_cublas_gemm_v2 does not support different C and D tensors.\");\n\n  // Workspace\n  void *workspace_ptr = nullptr;\n  size_t workspace_size = 0;\n  Tensor *workspace_tensor = convertNVTETensor(workspace);\n  if (workspace_tensor != nullptr) {\n    workspace_ptr = workspace_tensor->data.dptr;\n    workspace_size =\n        get_buffer_size_bytes(workspace_tensor->data.numel(), workspace_tensor->data.dtype);\n  }\n\n  // Additional config\n  MatmulConfig config_;\n  if (config != nullptr) {\n    config_ = *reinterpret_cast<MatmulConfig *>(config);\n  }\n\n  // Configure GEMM epilogue\n  const bool with_grad_epilogue = (config_.dbias_tensor != nullptr || config_.with_dgelu_epilogue);\n  if (with_grad_epilogue) {\n    NVTE_CHECK(config_.bias_tensor == nullptr && !config_.with_gelu_epilogue,\n               \"Invalid epilogue (bias=\", config_.bias_tensor != nullptr,\n               \", dbias=\", config_.dbias_tensor != nullptr, \", gelu=\", config_.with_gelu_epilogue,\n               \", dgelu=\", config_.with_dgelu_epilogue, \").\");\n  }\n  Tensor dummy_tensor;\n  Tensor *epilogue_bias_tensor = &dummy_tensor;\n  if (!with_grad_epilogue && config_.bias_tensor != nullptr) {\n    epilogue_bias_tensor = convertNVTETensorCheck(config_.bias_tensor);\n  } else if (with_grad_epilogue && config_.dbias_tensor != nullptr) {\n    epilogue_bias_tensor = convertNVTETensorCheck(config_.dbias_tensor);\n  }\n  Tensor *epilogue_aux_tensor = &dummy_tensor;\n  if (config_.with_gelu_epilogue || config_.with_dgelu_epilogue) {\n    NVTE_CHECK(config_.epilogue_aux_tensor != nullptr,\n               \"Requested epilogue (bias=\", config_.bias_tensor != nullptr,\n               \", dbias=\", config_.dbias_tensor != nullptr, \", gelu=\", config_.with_gelu_epilogue,\n               \", dgelu=\", config_.with_dgelu_epilogue, \") without providing aux tensor.\");\n    epilogue_aux_tensor = convertNVTETensor(config_.epilogue_aux_tensor);\n  }\n\n  // Launch GEMM\n  cublas_gemm(A_tensor, B_tensor, D_tensor, epilogue_bias_tensor, epilogue_aux_tensor,\n              transa ? HIPBLAS_OP_T : HIPBLAS_OP_N, transb ? HIPBLAS_OP_T : HIPBLAS_OP_N,\n              with_grad_epilogue, workspace_ptr, workspace_size, alpha, beta,\n              config_.use_split_accumulator, config_.sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_gemm_scaled(const NVTETensor A, const NVTETensor B, NVTETensor D,\n                             const NVTETensor bias, NVTETensor pre_gelu_out, bool transa,\n                             bool transb, bool grad, NVTETensor workspace, float alpha, float beta,\n                             bool use_split_accumulator, int math_sm_count, hipStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Tensors\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensorCheck(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  // Check for NVFP4\n  // TODO Remove once alpha scale logic is moved into cublas_gemm function\n  if (is_nvfp_scaling(inputA->scaling_mode) || is_nvfp_scaling(inputB->scaling_mode)) {\n    NVTE_ERROR(\"nvte_cublas_gemm does not support NVFP4 data. Use nvte_cublas_gemm_v2 instead.\");\n  }\n\n  // Launch GEMM\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? HIPBLAS_OP_T : HIPBLAS_OP_N,\n              (transb) ? HIPBLAS_OP_T : HIPBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              &alpha, &beta, use_split_accumulator, math_sm_count, 0, 0, false, nullptr, stream);\n}\n\nvoid nvte_cublas_atomic_gemm(const NVTETensor A, const NVTETensor B, NVTETensor D,\n                             const NVTETensor bias, NVTETensor pre_gelu_out, bool transa,\n                             bool transb, bool grad, NVTETensor workspace, bool accumulate,\n                             bool use_split_accumulator, int math_sm_count, int m_split,\n                             int n_split, bool gemm_producer, const NVTETensor counter,\n                             hipStream_t stream) {\n  NVTE_API_CALL(nvte_cublas_atomic_gemm);\n  using namespace transformer_engine;\n#if !(CUDA_VERSION >= 12020 && CUDA_VERSION < 13000)\n  NVTE_ERROR(\"Atomic GEMM requires CUDA >=12.2.0 and <13.0.0, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#elif !(CUBLAS_VERSION >= 120205 && CUBLAS_VERSION < 130000)\n  NVTE_ERROR(\n      \"Atomic GEMM requires cuBLAS >=12.2.5 and <13.0.0, but compile-time cuBLAS version is \",\n      CUBLAS_VERSION);\n#else\n  NVTE_CHECK(\n      transformer_engine::cuda::cudart_version() >= 12020 &&\n          transformer_engine::cuda::cudart_version() < 13000,\n      \"Atomic GEMM requires CUDA version >=12.2.0 and <13.0.0, but run-time CUDA version is \",\n      transformer_engine::cuda::cudart_version());\n  NVTE_CHECK(\n      transformer_engine::cuda::cublas_version() >= 120205 &&\n          transformer_engine::cuda::cublas_version() < 130000,\n      \"Atomic GEMM requires cuBLAS version >=12.2.5 and <13.0.0, but run-time cuBLAS version is \",\n      transformer_engine::cuda::cublas_version());\n\n  const Tensor *inputA = convertNVTETensorCheck(A);\n  const Tensor *inputB = convertNVTETensorCheck(B);\n  Tensor *outputD = convertNVTETensor(D);\n  const Tensor *biasTensor = convertNVTETensor(bias);\n  Tensor *outputGelu = convertNVTETensor(pre_gelu_out);\n  const Tensor *inputCounter = convertNVTETensor(counter);\n  Tensor *wspace = convertNVTETensor(workspace);\n\n  const void *alpha_ptr = GetScalarOne();\n  const void *beta_ptr = accumulate ? GetScalarOne() : GetScalarZero();\n\n  NVTE_CHECK(is_delayed_tensor_scaling(inputA->scaling_mode) &&\n                 is_delayed_tensor_scaling(inputB->scaling_mode),\n             \"Atomic GEMM only supports delayed scaling.\");\n  cublas_gemm(inputA, inputB, outputD, biasTensor, outputGelu, (transa) ? HIPBLAS_OP_T : HIPBLAS_OP_N,\n              (transb) ? HIPBLAS_OP_T : HIPBLAS_OP_N, grad, wspace->data.dptr, wspace->data.shape[0],\n              alpha_ptr, beta_ptr, use_split_accumulator, math_sm_count, m_split, n_split,\n              gemm_producer, inputCounter, stream);\n#endif\n}\n\nvoid multi_stream_cublas_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                              const NVTETensor *bias, NVTETensor *pre_gelu_out, const int num_gemms,\n                              bool transa, bool transb, bool grad, NVTETensor *workspace,\n                              bool accumulate, bool use_split_accumulator, int math_sm_count,\n                              hipStream_t stream) {\n  using namespace transformer_engine;\n\n  int num_streams = nvte_get_num_compute_streams();\n\n  int num_stream_used = std::min(num_streams, num_gemms);\n  // wait for current stream to finish\n  NVTE_CHECK_CUDA(hipEventRecord(detail::get_compute_stream_event(0), stream));\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(\n        hipStreamWaitEvent(detail::get_compute_stream(s), detail::get_compute_stream_event(0)));\n  }\n\n  for (int i = 0; i < num_gemms; i++) {\n    // Check whether GELU or dGELU epilogue is requested\n    Tensor *pre_gelu_tensor = convertNVTETensor(pre_gelu_out[i]);\n    bool with_gelu_dgelu_epilogue =\n        (pre_gelu_tensor != nullptr && pre_gelu_tensor->data.dptr != nullptr);\n\n    // Construct config\n    MatmulConfig config;\n    if (grad) {\n      config.dbias_tensor = bias[i];\n      config.with_dgelu_epilogue = with_gelu_dgelu_epilogue;\n    } else {\n      config.bias_tensor = bias[i];\n      config.with_gelu_epilogue = with_gelu_dgelu_epilogue;\n    }\n    config.epilogue_aux_tensor = pre_gelu_out[i];\n    config.use_split_accumulator = use_split_accumulator;\n    config.sm_count = math_sm_count;\n\n    // Launch GEMM\n    const float alpha = 1.f;\n    const float beta = accumulate ? 1.f : 0.f;\n    nvte_cublas_gemm_v2(transa, transb, &alpha, A[i], B[i], &beta, D[i], D[i],\n                        workspace[i % num_streams], &config,\n                        detail::get_compute_stream(i % num_streams));\n  }\n\n  // record events on compute streams\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(\n        hipEventRecord(detail::get_compute_stream_event(s), detail::get_compute_stream(s)));\n  }\n  // wait for all compute streams to finish\n  for (int s = 0; s < num_stream_used; s++) {\n    NVTE_CHECK_CUDA(hipStreamWaitEvent(stream, detail::get_compute_stream_event(s)));\n  }\n}\n\nvoid nvte_multi_stream_cublas_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                                   const NVTETensor *bias, NVTETensor *pre_gelu_out,\n                                   const int num_gemms, bool transa, bool transb, bool grad,\n                                   NVTETensor *workspace, bool accumulate,\n                                   bool use_split_accumulator, int math_sm_count,\n                                   hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_stream_cublas_gemm);\n  using namespace transformer_engine;\n\n  // Deprecation warning\n  NVTE_WARN(\n      \"nvte_multi_stream_cublas_gemm is deprecated and will be removed in a future release. \"\n      \"Please migrate to nvte_multi_tensor_gemm (with CUTLASS Grouped GEMM support when \"\n      \"applicable).\");\n\n  multi_stream_cublas_gemm(A, B, D, bias, pre_gelu_out, num_gemms, transa, transb, grad, workspace,\n                           accumulate, use_split_accumulator, math_sm_count, stream);\n}\n\nnamespace transformer_engine {\n\nusing cublasHandleManager = detail::HandleManager<hipblasLtHandle_t, CreateCublasHandle>;\n\nvoid nvte_cublas_handle_init() { auto _ = cublasHandleManager::Instance().GetHandle(); }\n\n}  //  namespace transformer_engine\n\nvoid nvte_multi_tensor_gemm(const NVTETensor *A, const NVTETensor *B, NVTETensor *D,\n                            const NVTETensor *bias, NVTETensor *pre_gelu_out, const int num_gemms,\n                            bool transa, bool transb, bool grad, NVTETensor *workspace,\n                            bool accumulate, bool use_split_accumulator, int math_sm_count,\n                            hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_gemm);\n\n  const int current_device = transformer_engine::cuda::current_device();\n  const bool is_hopper = (transformer_engine::cuda::sm_arch(current_device) == 90);\n  const bool use_cutlass = transformer_engine::getenv<bool>(\"NVTE_USE_CUTLASS_GROUPED_GEMM\", false);\n  const bool warn_fallback =\n      transformer_engine::getenv<bool>(\"NVTE_CUTLASS_GROUPED_GEMM_WARN_FALLBACK\", false);\n\n  auto cublas_path = [&]() {\n    multi_stream_cublas_gemm(A, B, D, bias, pre_gelu_out, num_gemms, transa, transb, grad,\n                             workspace, accumulate, use_split_accumulator, math_sm_count, stream);\n  };\n\n  // Currently only support cutlass group gemm on Hopper Arch\n  if (!(is_hopper && use_cutlass)) {\n    cublas_path();\n    return;\n  }\n\n  auto is_empty_arr = [&](const NVTETensor *p) -> bool {\n    if (p == nullptr) return true;\n    for (int i = 0; i < num_gemms; ++i) {\n      if (transformer_engine::convertNVTETensor(p[i])->has_data()) return false;\n    }\n    return true;\n  };\n\n  auto all_groups_uniform_k128 = [&](const NVTETensor *p, bool trans) -> bool {\n    int64_t ref_k = -1;\n    for (size_t i = 0; i < num_gemms; i++) {\n      const auto tensor = transformer_engine::convertNVTETensorCheck(p[i]);\n      const int k = trans ? tensor->data.shape[0] : tensor->data.shape[1];\n\n      if ((k & 127) != 0) return false;\n\n      if (ref_k < 0)\n        ref_k = k;\n      else if (k != ref_k)\n        return false;\n    }\n\n    return true;\n  };\n\n  auto is_supported_dtype = [&]() -> bool {\n    auto *inputA = transformer_engine::convertNVTETensorCheck(A[0]);\n    auto *inputB = transformer_engine::convertNVTETensorCheck(B[0]);\n    auto *OutputD = transformer_engine::convertNVTETensorCheck(D[0]);\n    auto A_type = get_cuda_dtype(inputA->data.dtype);\n    auto B_type = get_cuda_dtype(inputB->data.dtype);\n    auto D_type = get_cuda_dtype(OutputD->data.dtype);\n\n    return (A_type == B_type) && (A_type == D_type) &&\n           ((A_type == HIP_R_16BF) || (A_type == HIP_R_16F));\n  };\n\n  // CUTLASS Grouped GEMM fast path (SM90/TMA)\n  // Conditions:\n  //  - No fused epilogue: both bias and pre_gelu_out are empty.\n  //  - Supported dtypes only: FP16/BF16 (FP32 accumulate).\n  //  - Uniform K across groups and K % 128 == 0.\n  //  - use_split_accumulator is ignored for FP16/BF16.\n  //  - grad is irrelevant when bias/pre_gelu_out are empty.\n  //\n  // Otherwise, fall back to cuBLAS.\n  if (is_empty_arr(bias) && is_empty_arr(pre_gelu_out) && is_supported_dtype() &&\n      all_groups_uniform_k128(B, transb)) {\n    cutlass_grouped_gemm(A, B, D, num_gemms, transa, transb, grad, workspace, accumulate,\n                         current_device, math_sm_count, stream);\n  } else {\n    if (warn_fallback) {\n      NVTE_WARN(\"Fallback to cuBLAS grouped GEMM.\");\n    }\n    cublas_path();\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/gemm/cublaslt_gemm.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <string>\n\n#include \"../common.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/logging.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_swap_first_dims_cu.h\"\n\n// Hard-coded kernel parameters\nconstexpr size_t block_size = 128;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /* Vector load/store size */\n  size_t vector_size;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n  /* Whether each thread needs to make exactly one load/store */\n  bool single_load_store = true;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Used bytes per L1 cache load */\n  size_t bytes_per_load = 0;\n  /* Used bytes per L1 cache store */\n  size_t bytes_per_store = 0;\n\n  KernelConfig(size_t dim0, size_t dim1, size_t dim2, size_t sm_count, size_t vector_size_)\n      : vector_size{vector_size_} {\n    // Check that tiles are correctly aligned\n    if (dim2 % vector_size_ != 0) {\n      return;\n    }\n    valid = true;\n\n    // Number of CUDA blocks\n    num_blocks = DIVUP(dim0 * dim1 * dim2 / vector_size, block_size);\n    if (num_blocks > 2147483647ull) {\n      // Maximum number of CUDA blocks\n      single_load_store = false;\n      num_blocks = 2147483647ull;\n    } else if (num_blocks * block_size != dim0 * dim1 * dim2 / vector_size) {\n      single_load_store = false;\n    }\n\n    // SM occupancy\n    constexpr size_t warp_size = 32;\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * block_size / warp_size, warps_per_sm), sm_count);\n\n    // L1 cache efficiency\n    constexpr size_t cache_line_size = 128;\n    bytes_per_store = std::min(cache_line_size, warp_size * vector_size);  // Contiguous writes\n    bytes_per_load = bytes_per_store;\n    if (dim2 % (vector_size * warp_size) != 0) {\n      // Some warps are reading from two non-contiguous regions\n      bytes_per_load /= 2;\n    }\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/bytes_per_load + 1/bytes_per_store) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->bytes_per_load;\n      const auto &s1 = this->bytes_per_store;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.bytes_per_load;\n      const auto &s2 = other.bytes_per_store;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * s1 * p1 * l2 * s2 * p2;\n      const auto cost1 = (scale / l1 + scale / s1) / p1;\n      const auto cost2 = (scale / l2 + scale / s2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <typename Type>\n__global__ void __launch_bounds__(block_size)\n    swap_first_dims_untuned_kernel(const Type *__restrict__ input, Type *__restrict__ output,\n                                   const size_t dim0, const size_t dim1, const size_t dim2) {\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t idx = gid; idx < dim0 * dim1 * dim2; idx += nthreads) {\n    const auto idx2 = idx % dim2;\n    const auto idx1 = (idx / dim2) % dim1;\n    const auto idx0 = (idx / dim2) / dim1;\n    const auto in_offset = idx1 * dim0 * dim2 + idx0 * dim2 + idx2;\n    output[idx] = input[in_offset];\n  }\n}\n\n}  // namespace\n\nvoid swap_first_dims(const Tensor &input, Tensor &output, cudaStream_t stream) {\n  // Check tensors\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be simple tensor, but scaling mode is \",\n             to_string(input.scaling_mode), \".\");\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be simple tensor, but scaling mode is \",\n             to_string(output.scaling_mode), \".\");\n  NVTE_CHECK(input.dtype() == output.dtype(), \"Input tensor (dtype=\", to_string(input.dtype()),\n             \") and output tensor (dtype=\", to_string(output.dtype()), \") do not match.\");\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input is not allocated.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output is not allocated.\");\n\n  // Check tensor dimensions\n  const auto input_shape = input.shape();\n  const auto output_shape = output.shape();\n  NVTE_CHECK(input_shape.size() >= 2, \"Invalid input tensor dimensions (shape=\", input_shape, \").\");\n  NVTE_CHECK(output_shape.size() == input_shape.size(), \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  NVTE_CHECK(input_shape[0] == output_shape[1], \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  NVTE_CHECK(input_shape[1] == output_shape[0], \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  for (size_t i = 2; i < input_shape.size(); ++i) {\n    NVTE_CHECK(input_shape[i] == output_shape[i], \"Input tensor (shape=\", input_shape,\n               \") and output tensor (shape=\", output_shape, \") do not match.\");\n  }\n\n  // Reinterpret tensors as 3D tensors of bytes\n  const size_t dim0 = output_shape[0];\n  const size_t dim1 = output_shape[1];\n  size_t dim2 = 1;\n  for (size_t i = 2; i < output_shape.size(); ++i) {\n    dim2 *= output_shape[i];\n  }\n  dim2 = get_buffer_size_bytes(dim2, output.dtype());\n\n  // Choose kernel config with performance heuristics\n  const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n  KernelConfig config(dim0, dim1, dim2, sm_count, 1);\n  if (rtc::is_enabled()) {\n    auto try_config = [&](size_t vector_size) {\n      KernelConfig new_config(dim0, dim1, dim2, sm_count, vector_size);\n      if (new_config < config) {\n        config = new_config;\n      }\n    };\n    try_config(16);\n    try_config(8);\n    try_config(4);\n    try_config(2);\n  }\n  const size_t vector_size = config.vector_size;\n\n  // Launch kernel\n  if (vector_size == 1) {\n    // General kernel\n    swap_first_dims_untuned_kernel<<<config.num_blocks, block_size, 0, stream>>>(\n        static_cast<const uint8_t *>(input.data.dptr), static_cast<uint8_t *>(output.data.dptr),\n        dim0, dim1, dim2);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    // Compile NVRTC kernel if needed\n    auto &rtc_manager = rtc::KernelManager::instance();\n    const std::string kernel_label =\n        concat_strings(\"swap_first_dims,vector_size=\", vector_size,\n                       \",single_load_store=\", config.single_load_store);\n    if (!rtc_manager.is_compiled(kernel_label)) {\n      std::string code = string_code_transpose_rtc_swap_first_dims_cu;\n      code = regex_replace(code, \"__VECTOR_SIZE__\", vector_size);\n      code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n      code =\n          regex_replace(code, \"__SINGLE_LOAD_STORE__\", static_cast<int>(config.single_load_store));\n      rtc_manager.compile(kernel_label, \"swap_first_dims_kernel\", code,\n                          \"transformer_engine/common/transpose/rtc/swap_first_dims.cu\");\n    }\n\n    // Launch NVRTC kernel\n    rtc_manager.launch(kernel_label, config.num_blocks, block_size, 0, stream, input.data.dptr,\n                       output.data.dptr, dim0, dim1, dim2 / vector_size);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swap_first_dims(const NVTETensor input, NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_swap_first_dims);\n  using namespace transformer_engine;\n  swap_first_dims(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <string>\n\n#include \"../common.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/logging.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_swap_first_dims_cu.h\"\n\n// Hard-coded kernel parameters\nconstexpr size_t block_size = 128;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /* Vector load/store size */\n  size_t vector_size;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n  /* Whether each thread needs to make exactly one load/store */\n  bool single_load_store = true;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Used bytes per L1 cache load */\n  size_t bytes_per_load = 0;\n  /* Used bytes per L1 cache store */\n  size_t bytes_per_store = 0;\n\n  KernelConfig(size_t dim0, size_t dim1, size_t dim2, size_t sm_count, size_t vector_size_)\n      : vector_size{vector_size_} {\n    // Check that tiles are correctly aligned\n    if (dim2 % vector_size_ != 0) {\n      return;\n    }\n    valid = true;\n\n    // Number of CUDA blocks\n    num_blocks = DIVUP(dim0 * dim1 * dim2 / vector_size, block_size);\n    if (num_blocks > 2147483647ull) {\n      // Maximum number of CUDA blocks\n      single_load_store = false;\n      num_blocks = 2147483647ull;\n    } else if (num_blocks * block_size != dim0 * dim1 * dim2 / vector_size) {\n      single_load_store = false;\n    }\n\n    // SM occupancy\n    constexpr size_t warp_size = 32;\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * block_size / warp_size, warps_per_sm), sm_count);\n\n    // L1 cache efficiency\n    constexpr size_t cache_line_size = 128;\n    bytes_per_store = std::min(cache_line_size, warp_size * vector_size);  // Contiguous writes\n    bytes_per_load = bytes_per_store;\n    if (dim2 % (vector_size * warp_size) != 0) {\n      // Some warps are reading from two non-contiguous regions\n      bytes_per_load /= 2;\n    }\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/bytes_per_load + 1/bytes_per_store) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->bytes_per_load;\n      const auto &s1 = this->bytes_per_store;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.bytes_per_load;\n      const auto &s2 = other.bytes_per_store;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * s1 * p1 * l2 * s2 * p2;\n      const auto cost1 = (scale / l1 + scale / s1) / p1;\n      const auto cost2 = (scale / l2 + scale / s2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <typename Type>\n__global__ void __launch_bounds__(block_size)\n    swap_first_dims_untuned_kernel(const Type *__restrict__ input, Type *__restrict__ output,\n                                   const size_t dim0, const size_t dim1, const size_t dim2) {\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t idx = gid; idx < dim0 * dim1 * dim2; idx += nthreads) {\n    const auto idx2 = idx % dim2;\n    const auto idx1 = (idx / dim2) % dim1;\n    const auto idx0 = (idx / dim2) / dim1;\n    const auto in_offset = idx1 * dim0 * dim2 + idx0 * dim2 + idx2;\n    output[idx] = input[in_offset];\n  }\n}\n\n}  // namespace\n\nvoid swap_first_dims(const Tensor &input, Tensor &output, hipStream_t stream) {\n  // Check tensors\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be simple tensor, but scaling mode is \",\n             to_string(input.scaling_mode), \".\");\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be simple tensor, but scaling mode is \",\n             to_string(output.scaling_mode), \".\");\n  NVTE_CHECK(input.dtype() == output.dtype(), \"Input tensor (dtype=\", to_string(input.dtype()),\n             \") and output tensor (dtype=\", to_string(output.dtype()), \") do not match.\");\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input is not allocated.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output is not allocated.\");\n\n  // Check tensor dimensions\n  const auto input_shape = input.shape();\n  const auto output_shape = output.shape();\n  NVTE_CHECK(input_shape.size() >= 2, \"Invalid input tensor dimensions (shape=\", input_shape, \").\");\n  NVTE_CHECK(output_shape.size() == input_shape.size(), \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  NVTE_CHECK(input_shape[0] == output_shape[1], \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  NVTE_CHECK(input_shape[1] == output_shape[0], \"Input tensor (shape=\", input_shape,\n             \") and output tensor (shape=\", output_shape, \") do not match.\");\n  for (size_t i = 2; i < input_shape.size(); ++i) {\n    NVTE_CHECK(input_shape[i] == output_shape[i], \"Input tensor (shape=\", input_shape,\n               \") and output tensor (shape=\", output_shape, \") do not match.\");\n  }\n\n  // Reinterpret tensors as 3D tensors of bytes\n  const size_t dim0 = output_shape[0];\n  const size_t dim1 = output_shape[1];\n  size_t dim2 = 1;\n  for (size_t i = 2; i < output_shape.size(); ++i) {\n    dim2 *= output_shape[i];\n  }\n  dim2 = get_buffer_size_bytes(dim2, output.dtype());\n\n  // Choose kernel config with performance heuristics\n  const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n  KernelConfig config(dim0, dim1, dim2, sm_count, 1);\n  if (rtc::is_enabled()) {\n    auto try_config = [&](size_t vector_size) {\n      KernelConfig new_config(dim0, dim1, dim2, sm_count, vector_size);\n      if (new_config < config) {\n        config = new_config;\n      }\n    };\n    try_config(16);\n    try_config(8);\n    try_config(4);\n    try_config(2);\n  }\n  const size_t vector_size = config.vector_size;\n\n  // Launch kernel\n  if (vector_size == 1) {\n    // General kernel\n    swap_first_dims_untuned_kernel<<<config.num_blocks, block_size, 0, stream>>>(\n        static_cast<const uint8_t *>(input.data.dptr), static_cast<uint8_t *>(output.data.dptr),\n        dim0, dim1, dim2);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    // Compile NVRTC kernel if needed\n    auto &rtc_manager = rtc::KernelManager::instance();\n    const std::string kernel_label =\n        concat_strings(\"swap_first_dims,vector_size=\", vector_size,\n                       \",single_load_store=\", config.single_load_store);\n    if (!rtc_manager.is_compiled(kernel_label)) {\n      std::string code = string_code_transpose_rtc_swap_first_dims_cu;\n      code = regex_replace(code, \"__VECTOR_SIZE__\", vector_size);\n      code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n      code =\n          regex_replace(code, \"__SINGLE_LOAD_STORE__\", static_cast<int>(config.single_load_store));\n      rtc_manager.compile(kernel_label, \"swap_first_dims_kernel\", code,\n                          \"transformer_engine/common/transpose/rtc/swap_first_dims.cu\");\n    }\n\n    // Launch NVRTC kernel\n    rtc_manager.launch(kernel_label, config.num_blocks, block_size, 0, stream, input.data.dptr,\n                       output.data.dptr, dim0, dim1, dim2 / vector_size);\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_swap_first_dims(const NVTETensor input, NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_swap_first_dims);\n  using namespace transformer_engine;\n  swap_first_dims(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/swap_first_dims.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/cast_transpose_noop.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n\n#include \"../common.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"./transpose.h\"\n\nnamespace transformer_engine {\nnamespace detail {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_transpose_cu.h\"\n\n// Hard-coded kernel parameters\nconstexpr size_t warps_per_tile = 4;\nconstexpr size_t block_size = THREADS_PER_WARP * warps_per_tile;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /** Vector load size */\n  size_t load_size;\n  /** Vector store size */\n  size_t store_size;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Elements per L1 cache load */\n  size_t elements_per_load = 0;\n  /* Elements per L1 cache store */\n  size_t elements_per_store = 0;\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t type_size, size_t load_size_,\n               size_t store_size_, size_t sm_count)\n      : load_size{load_size_}, store_size{store_size_} {\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % type_size != 0 || store_size % type_size != 0 ||\n        cache_line_size % type_size != 0) {\n      return;\n    }\n    const size_t row_tile_elements = load_size * THREADS_PER_WARP / type_size;\n    const size_t col_tile_elements = store_size * THREADS_PER_WARP / type_size;\n    valid = (row_length % row_tile_elements == 0 && num_rows % col_tile_elements == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = (row_length / row_tile_elements) * (num_rows / col_tile_elements);\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, row_tile_elements * type_size) / type_size);\n    elements_per_store = (std::min(cache_line_size, col_tile_elements * type_size) / type_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load + 1/elements_per_store) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &s1 = this->elements_per_store;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &s2 = other.elements_per_store;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * s1 * p1 * l2 * s2 * p2;\n      const auto cost1 = (scale / l1 + scale / s1) / p1;\n      const auto cost2 = (scale / l2 + scale / s2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <size_t load_size, size_t store_size, typename Type>\n__global__ void __launch_bounds__(block_size)\n    transpose_general_kernel(const Type *__restrict__ const input, const fp32 *const noop,\n                             Type *__restrict__ const output, const size_t row_length,\n                             const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(Type);\n  constexpr size_t nvec_out = store_size / sizeof(Type);\n  using IVec = Vec<Type, nvec_in>;\n  using OVec = Vec<Type, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // Load input and store to registers\n  // Note: Each thread loads num_iterations subtiles and transposes in\n  // registers.\n  OVec local_output[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      local_input.clear();\n      if (row < num_rows) {\n#pragma unroll\n        for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[row * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        local_output[j2][iter].data.elt[i2] = local_input.data.elt[j2];\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVec shared_output[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output[j1][i1] = local_output[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      if (col < row_length) {\n#pragma unroll\n        for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n          if (row + i2 < num_rows) {\n            output[col * num_rows + row + i2] = shared_output[j1][i1].data.elt[i2];\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n}\n\n}  // namespace\n\nvoid transpose(const Tensor &input, const Tensor &noop, Tensor *output_, cudaStream_t stream) {\n  Tensor &output = *output_;\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(output.data.shape.size() == 2, \"Output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(output.data.shape[0] == row_length, \"Wrong dimension of output.\");\n  NVTE_CHECK(output.data.shape[1] == num_rows, \"Wrong dimension of output.\");\n\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input is not allocated.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output is not allocated.\");\n  NVTE_CHECK(input.data.dtype == output.data.dtype, \"Input (dtype=\", to_string(input.data.dtype),\n             \") and output (dtype=\", to_string(output.data.dtype), \") do not match.\");\n\n  if (noop.data.dptr != nullptr) {\n    NVTE_CHECK(noop.numel() == 1, \"Expected 1 element, \", \"but found \", noop.numel(), \".\");\n    NVTE_CHECK(noop.data.dtype == DType::kFloat32);\n    NVTE_CHECK(noop.data.dptr != nullptr);\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n      input.data.dtype, Type, constexpr const char *type_name = TypeInfo<Type>::name;\n      constexpr size_t type_size = sizeof(Type);\n\n      // Choose between runtime-compiled or statically-compiled kernel\n      const bool aligned = (row_length % THREADS_PER_WARP == 0 && num_rows % THREADS_PER_WARP == 0);\n      if (aligned && rtc::is_enabled()) {  // Runtime-compiled tuned kernel\n        // Pick kernel config\n        std::vector<KernelConfig> kernel_configs;\n        kernel_configs.reserve(16);\n        const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n        auto add_config = [&](size_t load_size, size_t store_size) {\n          kernel_configs.emplace_back(row_length, num_rows, type_size, load_size, store_size,\n                                      sm_count);\n        };\n        add_config(8, 8);\n        add_config(4, 8);\n        add_config(8, 4);\n        add_config(4, 4);\n        add_config(2, 8);\n        add_config(8, 2);\n        add_config(2, 4);\n        add_config(4, 2);\n        add_config(2, 2);\n        add_config(1, 8);\n        add_config(8, 1);\n        add_config(1, 4);\n        add_config(4, 1);\n        add_config(1, 2);\n        add_config(2, 1);\n        add_config(1, 1);\n        const auto &kernel_config = *std::min_element(kernel_configs.begin(), kernel_configs.end());\n        NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n        const size_t load_size = kernel_config.load_size;\n        const size_t store_size = kernel_config.store_size;\n        const size_t num_blocks = kernel_config.num_blocks;\n\n        // Compile NVRTC kernel if needed and launch\n        auto &rtc_manager = rtc::KernelManager::instance();\n        const std::string kernel_label = concat_strings(\n            \"transpose\"\n            \",type=\",\n            type_name, \",load_size=\", load_size, \",store_size=\", store_size);\n        if (!rtc_manager.is_compiled(kernel_label)) {\n          std::string code = string_code_transpose_rtc_transpose_cu;\n          code = regex_replace(code, \"__TYPE__\", type_name);\n          code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n          code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n          code = regex_replace(code, \"__WARPS_PER_TILE__\", warps_per_tile);\n          code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n          rtc_manager.compile(kernel_label, \"transpose_optimized_kernel\", code,\n                              \"transformer_engine/common/transpose/rtc/transpose.cu\");\n        }\n        rtc_manager.launch(kernel_label, num_blocks, block_size, 0, stream,\n                           static_cast<const Type *>(input.data.dptr),\n                           static_cast<const fp32 *>(noop.data.dptr),\n                           static_cast<Type *>(output.data.dptr), row_length, num_rows);\n      } else {  // Statically-compiled general kernel\n        constexpr size_t load_size = 4;\n        constexpr size_t store_size = 4;\n        constexpr size_t row_tile_size = load_size / type_size * THREADS_PER_WARP;\n        constexpr size_t col_tile_size = store_size / type_size * THREADS_PER_WARP;\n        const int num_blocks = (DIVUP(row_length, row_tile_size) * DIVUP(num_rows, col_tile_size));\n        transpose_general_kernel<load_size, store_size, Type>\n            <<<num_blocks, block_size, 0, stream>>>(static_cast<const Type *>(input.data.dptr),\n                                                    static_cast<const fp32 *>(noop.data.dptr),\n                                                    static_cast<Type *>(output.data.dptr),\n                                                    row_length, num_rows);\n        NVTE_CHECK_CUDA(cudaGetLastError());\n      });  // NOLINT(*)\n}\n\n}  // namespace detail\n}  // namespace transformer_engine\n\nvoid nvte_transpose(const NVTETensor input, NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_transpose);\n  using namespace transformer_engine;\n  auto noop = Tensor();\n  detail::transpose(*convertNVTETensorCheck(input), noop, convertNVTETensor(output), stream);\n}\n\nvoid nvte_transpose_with_noop(const NVTETensor input, const NVTETensor noop, NVTETensor output,\n                              cudaStream_t stream) {\n  NVTE_API_CALL(nvte_transpose_with_noop);\n  using namespace transformer_engine;\n  detail::transpose(*convertNVTETensorCheck(input), *convertNVTETensorCheck(noop),\n                    convertNVTETensor(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/cast_transpose_noop.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n\n#include \"../common.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"./transpose.h\"\n\nnamespace transformer_engine {\nnamespace detail {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_transpose_cu.h\"\n\n// Hard-coded kernel parameters\nconstexpr size_t warps_per_tile = 4;\nconstexpr size_t block_size = THREADS_PER_WARP * warps_per_tile;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /** Vector load size */\n  size_t load_size;\n  /** Vector store size */\n  size_t store_size;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Elements per L1 cache load */\n  size_t elements_per_load = 0;\n  /* Elements per L1 cache store */\n  size_t elements_per_store = 0;\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t type_size, size_t load_size_,\n               size_t store_size_, size_t sm_count)\n      : load_size{load_size_}, store_size{store_size_} {\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % type_size != 0 || store_size % type_size != 0 ||\n        cache_line_size % type_size != 0) {\n      return;\n    }\n    const size_t row_tile_elements = load_size * THREADS_PER_WARP / type_size;\n    const size_t col_tile_elements = store_size * THREADS_PER_WARP / type_size;\n    valid = (row_length % row_tile_elements == 0 && num_rows % col_tile_elements == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = (row_length / row_tile_elements) * (num_rows / col_tile_elements);\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, row_tile_elements * type_size) / type_size);\n    elements_per_store = (std::min(cache_line_size, col_tile_elements * type_size) / type_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load + 1/elements_per_store) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &s1 = this->elements_per_store;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &s2 = other.elements_per_store;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * s1 * p1 * l2 * s2 * p2;\n      const auto cost1 = (scale / l1 + scale / s1) / p1;\n      const auto cost2 = (scale / l2 + scale / s2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <size_t load_size, size_t store_size, typename Type>\n__global__ void __launch_bounds__(block_size)\n    transpose_general_kernel(const Type *__restrict__ const input, const fp32 *const noop,\n                             Type *__restrict__ const output, const size_t row_length,\n                             const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(Type);\n  constexpr size_t nvec_out = store_size / sizeof(Type);\n  using IVec = Vec<Type, nvec_in>;\n  using OVec = Vec<Type, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // Load input and store to registers\n  // Note: Each thread loads num_iterations subtiles and transposes in\n  // registers.\n  OVec local_output[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      local_input.clear();\n      if (row < num_rows) {\n#pragma unroll\n        for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n          if (col + j2 < row_length) {\n            local_input.data.elt[j2] = input[row * row_length + col + j2];\n          }\n        }\n      }\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        local_output[j2][iter].data.elt[i2] = local_input.data.elt[j2];\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVec shared_output[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output[j1][i1] = local_output[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      if (col < row_length) {\n#pragma unroll\n        for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n          if (row + i2 < num_rows) {\n            output[col * num_rows + row + i2] = shared_output[j1][i1].data.elt[i2];\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n}\n\n}  // namespace\n\nvoid transpose(const Tensor &input, const Tensor &noop, Tensor *output_, hipStream_t stream) {\n  Tensor &output = *output_;\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(output.data.shape.size() == 2, \"Output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(output.data.shape[0] == row_length, \"Wrong dimension of output.\");\n  NVTE_CHECK(output.data.shape[1] == num_rows, \"Wrong dimension of output.\");\n\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input is not allocated.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output is not allocated.\");\n  NVTE_CHECK(input.data.dtype == output.data.dtype, \"Input (dtype=\", to_string(input.data.dtype),\n             \") and output (dtype=\", to_string(output.data.dtype), \") do not match.\");\n\n  if (noop.data.dptr != nullptr) {\n    NVTE_CHECK(noop.numel() == 1, \"Expected 1 element, \", \"but found \", noop.numel(), \".\");\n    NVTE_CHECK(noop.data.dtype == DType::kFloat32);\n    NVTE_CHECK(noop.data.dptr != nullptr);\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n      input.data.dtype, Type, constexpr const char *type_name = TypeInfo<Type>::name;\n      constexpr size_t type_size = sizeof(Type);\n\n      // Choose between runtime-compiled or statically-compiled kernel\n      const bool aligned = (row_length % THREADS_PER_WARP == 0 && num_rows % THREADS_PER_WARP == 0);\n      if (aligned && rtc::is_enabled()) {  // Runtime-compiled tuned kernel\n        // Pick kernel config\n        std::vector<KernelConfig> kernel_configs;\n        kernel_configs.reserve(16);\n        const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n        auto add_config = [&](size_t load_size, size_t store_size) {\n          kernel_configs.emplace_back(row_length, num_rows, type_size, load_size, store_size,\n                                      sm_count);\n        };\n        add_config(8, 8);\n        add_config(4, 8);\n        add_config(8, 4);\n        add_config(4, 4);\n        add_config(2, 8);\n        add_config(8, 2);\n        add_config(2, 4);\n        add_config(4, 2);\n        add_config(2, 2);\n        add_config(1, 8);\n        add_config(8, 1);\n        add_config(1, 4);\n        add_config(4, 1);\n        add_config(1, 2);\n        add_config(2, 1);\n        add_config(1, 1);\n        const auto &kernel_config = *std::min_element(kernel_configs.begin(), kernel_configs.end());\n        NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n        const size_t load_size = kernel_config.load_size;\n        const size_t store_size = kernel_config.store_size;\n        const size_t num_blocks = kernel_config.num_blocks;\n\n        // Compile NVRTC kernel if needed and launch\n        auto &rtc_manager = rtc::KernelManager::instance();\n        const std::string kernel_label = concat_strings(\n            \"transpose\"\n            \",type=\",\n            type_name, \",load_size=\", load_size, \",store_size=\", store_size);\n        if (!rtc_manager.is_compiled(kernel_label)) {\n          std::string code = string_code_transpose_rtc_transpose_cu;\n          code = regex_replace(code, \"__TYPE__\", type_name);\n          code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n          code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n          code = regex_replace(code, \"__WARPS_PER_TILE__\", warps_per_tile);\n          code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n          rtc_manager.compile(kernel_label, \"transpose_optimized_kernel\", code,\n                              \"transformer_engine/common/transpose/rtc/transpose.cu\");\n        }\n        rtc_manager.launch(kernel_label, num_blocks, block_size, 0, stream,\n                           static_cast<const Type *>(input.data.dptr),\n                           static_cast<const fp32 *>(noop.data.dptr),\n                           static_cast<Type *>(output.data.dptr), row_length, num_rows);\n      } else {  // Statically-compiled general kernel\n        constexpr size_t load_size = 4;\n        constexpr size_t store_size = 4;\n        constexpr size_t row_tile_size = load_size / type_size * THREADS_PER_WARP;\n        constexpr size_t col_tile_size = store_size / type_size * THREADS_PER_WARP;\n        const int num_blocks = (DIVUP(row_length, row_tile_size) * DIVUP(num_rows, col_tile_size));\n        transpose_general_kernel<load_size, store_size, Type>\n            <<<num_blocks, block_size, 0, stream>>>(static_cast<const Type *>(input.data.dptr),\n                                                    static_cast<const fp32 *>(noop.data.dptr),\n                                                    static_cast<Type *>(output.data.dptr),\n                                                    row_length, num_rows);\n        NVTE_CHECK_CUDA(hipGetLastError());\n      });  // NOLINT(*)\n}\n\n}  // namespace detail\n}  // namespace transformer_engine\n\nvoid nvte_transpose(const NVTETensor input, NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_transpose);\n  using namespace transformer_engine;\n  auto noop = Tensor();\n  detail::transpose(*convertNVTETensorCheck(input), noop, convertNVTETensor(output), stream);\n}\n\nvoid nvte_transpose_with_noop(const NVTETensor input, const NVTETensor noop, NVTETensor output,\n                              hipStream_t stream) {\n  NVTE_API_CALL(nvte_transpose_with_noop);\n  using namespace transformer_engine;\n  detail::transpose(*convertNVTETensorCheck(input), *convertNVTETensorCheck(noop),\n                    convertNVTETensor(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/transpose.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <functional>\n#include <numeric>\n#include <type_traits>\n\n#include \"../util/math.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"cast_transpose.h\"\n#include \"common/common.h\"\n\nnamespace transformer_engine {\n\nnamespace detail {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_cast_transpose_fusion_cu.h\"\n\n// STUFF TO TUNE\nconstexpr size_t n_warps_per_tile = 8;\nconstexpr size_t desired_load_size = 8;\nconstexpr size_t desired_store_size = 8;\nconstexpr size_t desired_load_size_dact = 4;  // dAct fusion kernels use more registers\nconstexpr size_t desired_store_size_dact = 4;\n\nconstexpr size_t threads_per_warp = static_cast<size_t>(THREADS_PER_WARP);\nconstexpr size_t max_threads_per_block = 256;\nconstexpr size_t reduce_dbias_num_threads = 256;\nconstexpr size_t cast_transpose_num_threads = n_warps_per_tile * threads_per_warp;\nconstexpr size_t n_warps_per_block = cast_transpose_num_threads / threads_per_warp;\nstatic_assert(cast_transpose_num_threads <= max_threads_per_block);\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  size_t load_size = 0;   // Vector load size\n  size_t store_size = 0;  // Vector store size to transposed output\n\n  bool valid = false;     // Whether config is valid\n  bool is_dact = false;   // Whether dact is used\n  size_t num_blocks = 0;  // Number of CUDA blocks\n\n  size_t active_sm_count = 0;         // Number of active SMs\n  size_t elements_per_load = 0;       // Elements per L1 cache load\n  size_t elements_per_load_dact = 0;  // Elements per L1 cache load dact\n  size_t elements_per_store_c = 0;    // Elements per L1 cache store to cast output\n  size_t elements_per_store_t = 0;    // Elements per L1 cache store to transposed output\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t itype_size, size_t itype2_size,\n               size_t otype_size, size_t load_size_, size_t store_size_, size_t sm_count,\n               bool is_dact_)\n      : load_size{load_size_}, store_size{store_size_}, is_dact{is_dact_} {\n    if (is_dact) {\n      if (load_size > desired_load_size_dact || store_size > desired_store_size_dact) {\n        return;\n      }\n    }\n\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % itype_size != 0 || store_size % otype_size != 0 ||\n        cache_line_size % itype_size != 0 || cache_line_size % otype_size != 0) {\n      return;\n    }\n    /* row_tile_elements */\n    const size_t tile_size_x = (load_size * THREADS_PER_WARP) / itype_size;\n    /* col_tile_elements */\n    const size_t tile_size_y = (store_size * THREADS_PER_WARP) / otype_size;\n    const size_t num_tiles_x = row_length / tile_size_x;\n    const size_t num_tiles_y = num_rows / tile_size_y;\n\n    valid = (row_length % tile_size_x == 0 && num_rows % tile_size_y == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = num_tiles_x * num_tiles_y;\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * n_warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, tile_size_x * itype_size) / itype_size);\n    elements_per_load_dact = (std::min(cache_line_size, tile_size_x * itype2_size) / itype2_size);\n    elements_per_store_c = (std::min(cache_line_size, tile_size_x * otype_size) / otype_size);\n    elements_per_store_t = (std::min(cache_line_size, tile_size_y * otype_size) / otype_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load\n      //         + 1/elements_per_load_dact\n      //         + 1/elements_per_store_c\n      //         + 1/elements_per_store_t) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &la1 = this->elements_per_load_dact;\n      const auto &sc1 = this->elements_per_store_c;\n      const auto &st1 = this->elements_per_store_t;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &la2 = other.elements_per_load_dact;\n      const auto &sc2 = other.elements_per_store_c;\n      const auto &st2 = other.elements_per_store_t;\n      const auto &p2 = other.active_sm_count;\n      const auto scale1 = l1 * sc1 * st1 * p1 * (is_dact ? la1 : 1);\n      const auto scale2 = l2 * sc2 * st2 * p2 * (is_dact ? la2 : 1);\n      const auto scale = scale1 * scale2;\n      const auto cost1 =\n          (scale / l1 + scale / sc1 + scale / st1 + (is_dact ? (scale / la1) : 0)) / p1;\n      const auto cost2 =\n          (scale / l2 + scale / sc2 + scale / st2 + (is_dact ? (scale / la2) : 0)) / p2;\n\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <bool IS_DBIAS, bool IS_FULL_TILE, int nvec_in, int nvec_out, typename OVec, typename CVec,\n          typename CType>\ninline __device__ void cast_and_transpose_regs(const CVec (&in)[nvec_out],\n                                               OVec (&out_trans)[nvec_in],\n                                               CVec &out_dbias,  // NOLINT(*)\n                                               typename OVec::type *output_cast_tile,\n                                               const size_t current_place, const size_t stride,\n                                               const CType scale,\n                                               CType &amax,  // NOLINT(*)\n                                               const int dbias_shfl_src_lane,\n                                               const bool valid_store) {\n  using OType = typename OVec::type;\n  using OVecC = Vec<OType, nvec_in>;\n\n  CVec step_dbias;\n  if constexpr (IS_DBIAS) {\n    step_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    OVecC out_cast;\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      const CType tmp = in[i].data.elt[j];\n      if constexpr (IS_DBIAS) {\n        step_dbias.data.elt[j] += tmp;  // dbias: thread tile local accumulation\n      }\n      out_cast.data.elt[j] = static_cast<OType>(tmp * scale);\n      out_trans[j].data.elt[i] = static_cast<OType>(tmp * scale);  // thread tile transpose\n\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(fabsf(tmp), amax);\n    }\n    if (IS_FULL_TILE || valid_store) {\n      out_cast.store_to(output_cast_tile, current_place + stride * i);\n    }\n  }\n\n  if constexpr (IS_DBIAS) {\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      CType elt = step_dbias.data.elt[j];\n      elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in a warp\n      out_dbias.data.elt[j] += elt;\n    }\n  }\n}\n\nvoid populate_cast_transpose_dbias_workspace_config(const Tensor &cast_output, /*cast*/\n                                                    Tensor *workspace, const int nvec_out) {\n  const size_t row_length = cast_output.flat_last_dim();\n  const size_t num_rows = cast_output.flat_first_dim();\n\n  const size_t tile_size_y = (nvec_out * THREADS_PER_WARP);\n  NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n  const size_t num_rows_partial_dbias = DIVUP(num_rows, tile_size_y);\n\n  if (workspace->data.dptr == nullptr) {\n    workspace->data.shape = {num_rows_partial_dbias, row_length};\n    workspace->data.dtype = DType::kFloat32;\n  } else {\n    // Check that workspace matches expected size\n    const size_t workspace_size = get_buffer_size_bytes(\n        std::accumulate(workspace->data.shape.begin(), workspace->data.shape.end(), 1,\n                        std::multiplies<size_t>()),\n        workspace->data.dtype);\n    const size_t required_size =\n        get_buffer_size_bytes(num_rows_partial_dbias, row_length, DType::kFloat32);\n    NVTE_CHECK(workspace_size >= required_size, \"Invalid workspace (expected dims=(\",\n               num_rows_partial_dbias, \",\", row_length, \"), dtype=\", to_string(DType::kFloat32),\n               \"; found dims=\", workspace->data.shape,\n               \", dtype=\", typeToNumBits(workspace->data.dtype), \" bits)\");\n  }\n}\n\ntemplate <int nvec, typename ComputeType, typename OutputType>\n__global__ void __launch_bounds__(reduce_dbias_num_threads)\n    reduce_dbias_kernel(OutputType *const dbias_output, const ComputeType *const dbias_partial,\n                        const int row_length, const int num_rows) {\n  using ComputeVec = Vec<ComputeType, nvec>;\n  using OutputVec = Vec<OutputType, nvec>;\n\n  const int thread_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (thread_id * nvec >= row_length) {\n    return;\n  }\n\n  const ComputeType *const thread_in_base = dbias_partial + thread_id * nvec;\n  OutputType *const thread_out_base = dbias_output + thread_id * nvec;\n\n  const int stride_in_vec = row_length / nvec;\n\n  ComputeVec ldg_vec;\n  ComputeVec acc_vec;\n  acc_vec.clear();\n  for (int i = 0; i < num_rows; ++i) {\n    ldg_vec.load_from(thread_in_base, i * stride_in_vec);\n#pragma unroll\n    for (int e = 0; e < nvec; ++e) {\n      acc_vec.data.elt[e] += ldg_vec.data.elt[e];\n    }\n  }\n\n  OutputVec stg_vec;\n#pragma unroll\n  for (int e = 0; e < nvec; ++e) {\n    stg_vec.data.elt[e] = OutputType(acc_vec.data.elt[e]);\n  }\n  stg_vec.store_to(thread_out_base, 0);\n}\n\ntemplate <typename InputType>\nvoid reduce_dbias(const Tensor &workspace, Tensor *dbias, const size_t row_length,\n                  const size_t num_rows, const int nvec_out, cudaStream_t stream) {\n  constexpr int reduce_dbias_store_bytes = 8;  // stg.64\n  constexpr int reduce_dbias_nvec = reduce_dbias_store_bytes / sizeof(InputType);\n\n  NVTE_CHECK(row_length % reduce_dbias_nvec == 0, \"Unsupported shape.\");\n\n  const size_t reduce_dbias_row_length = row_length;\n  const size_t reduce_dbias_num_rows =\n      DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n  const size_t reduce_dbias_num_blocks =\n      DIVUP(row_length, reduce_dbias_num_threads * reduce_dbias_nvec);\n\n  using DbiasOutputType = fp32;\n  reduce_dbias_kernel<reduce_dbias_nvec, DbiasOutputType, InputType>\n      <<<reduce_dbias_num_blocks, reduce_dbias_num_threads, 0, stream>>>(\n          reinterpret_cast<InputType *>(dbias->data.dptr),\n          reinterpret_cast<const fp32 *>(workspace.data.dptr), reduce_dbias_row_length,\n          reduce_dbias_num_rows);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <bool IS_DBIAS, bool IS_DACT, bool IS_ACT, typename ComputeType, typename Param,\n          int nvec_in, int nvec_out, typename ParamOP,\n          ComputeType (*OP)(ComputeType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    cast_transpose_fused_kernel_notaligned(const Param param, const size_t row_length,\n                                           const size_t num_rows, const size_t num_tiles) {\n  static_assert(!(IS_DACT && IS_ACT), \"forward and backward activation are mutually exclusive\");\n  using IType = typename Param::InputType;\n  using IType2 = typename Param::InputType2;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using IVec2 = Vec<IType2, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const size_t tile_offset =\n      (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const size_t tile_offset_transp =\n      (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n\n  const IType *const my_input_tile = param.input + tile_offset;\n  const IType2 *const my_act_input_tile = param.act_input + tile_offset;\n  OType *const my_output_c_tile = param.output_c + tile_offset;\n  OType *const my_output_t_tile = param.output_t + tile_offset_transp;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  IVec2 act_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  size_t current_row = (tile_id_y * THREADS_PER_WARP + warp_id_in_tile * n_iterations) * nvec_out;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  CType amax = 0;\n  const CType scale = param.scale_ptr != nullptr ? *param.scale_ptr : 1;\n\n  CVec partial_dbias;\n  if constexpr (IS_DBIAS) {\n    partial_dbias.clear();\n  }\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        const size_t ld_offset = current_stride + my_place + stride * i;\n        in[0][i].load_from(my_input_tile, ld_offset);\n        if constexpr (IS_DACT) {\n          act_in[0][i].load_from(my_act_input_tile, ld_offset);\n        }\n      } else {\n        in[0][i].clear();\n        if constexpr (IS_DACT) {\n          act_in[0][i].clear();\n        }\n      }\n    }\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      const bool valid_load =\n          my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        if (valid_load) {\n          const size_t ld_offset = current_stride + my_place_in + stride * (nvec_out + j);\n          in[current_in][j].load_from(my_input_tile, ld_offset);\n          if constexpr (IS_DACT) {\n            act_in[current_in][j].load_from(my_act_input_tile, ld_offset);\n          }\n        } else {\n          in[current_in][j].clear();\n          if constexpr (IS_DACT) {\n            act_in[current_in][j].clear();\n          }\n        }\n      }\n    }\n    CVec after_dact[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        if constexpr (IS_DACT) {\n          after_dact[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                      OP(act_in[current_in ^ 1][j].data.elt[k], {});\n        } else if constexpr (IS_ACT) {\n          after_dact[j].data.elt[k] = OP(in[current_in ^ 1][j].data.elt[k], {});\n        } else {\n          after_dact[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]);\n        }\n      }\n    }\n    const int dbias_shfl_src_lane =\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    constexpr bool IS_FULL_TILE = false;\n    const bool valid_store =\n        (my_place < tile_length) && (warp_id_in_tile * n_iterations + i < tile_height);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_space[i], partial_dbias,\n                                                    my_output_c_tile, current_place, stride, scale,\n                                                    amax, dbias_shfl_src_lane, valid_store);\n\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_row += nvec_out;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  if constexpr (IS_DBIAS) {\n    my_dbias_scratch[threadIdx.x] = partial_dbias;\n    __syncthreads();\n    if (warp_id_in_tile == 0) {\n#pragma unroll\n      for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n        CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n        for (unsigned int j = 0; j < nvec_in; ++j) {\n          partial_dbias.data.elt[j] += tmp.data.elt[j];\n        }\n      }\n      if (my_id_in_warp < tile_length) {\n        partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n      }\n    }\n  }\n\n  // Reduce amax over block\n  if (param.amax != nullptr) {\n    amax = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(amax, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(param.amax, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && param.scale_inv != nullptr) {\n    reciprocal<CType>(param.scale_inv, scale);\n  }\n}\n\nstatic const char *ActTypeToString[] = {\n    \"none\",      // 0\n    \"sigmoid\",   // 1\n    \"dsigmoid\",  // 2\n    \"gelu\",      // 3\n    \"dgelu\",     // 4\n    \"qgelu\",     // 5\n    \"dqgelu\",    // 6\n    \"silu\",      // 7\n    \"dsilu\",     // 8\n    \"relu\",      // 9\n    \"drelu\",     // 10\n    \"srelu\",     // 11\n    \"dsrelu\"     // 12\n};\n\ntemplate <typename ComputeType, typename ParamOP, ComputeType (*OP)(ComputeType, const ParamOP &)>\nconstexpr int get_activation_type() {\n  constexpr decltype(OP) ActivationList[] = {\n      nullptr,                              // 0\n      &sigmoid<ComputeType, ComputeType>,   // 1\n      &dsigmoid<ComputeType, ComputeType>,  // 2\n      &gelu<ComputeType, ComputeType>,      // 3\n      &dgelu<ComputeType, ComputeType>,     // 4\n      &qgelu<ComputeType, ComputeType>,     // 5\n      &dqgelu<ComputeType, ComputeType>,    // 6\n      &silu<ComputeType, ComputeType>,      // 7\n      &dsilu<ComputeType, ComputeType>,     // 8\n      &relu<ComputeType, ComputeType>,      // 9\n      &drelu<ComputeType, ComputeType>,     // 10\n      &srelu<ComputeType, ComputeType>,     // 11\n      &dsrelu<ComputeType, ComputeType>     // 12\n  };\n#pragma unroll\n  for (int i = 0; i < sizeof(ActivationList) / sizeof(ActivationList[0]); ++i) {\n    if (OP == ActivationList[i]) {\n      return i;\n    }\n  }\n  return 0;\n}\n\ntemplate <bool IS_DBIAS, bool IS_DACT, bool IS_ACT, typename ComputeType, typename ParamOP,\n          ComputeType (*OP)(ComputeType, const ParamOP &)>\nvoid cast_transpose_fused(const Tensor &input, const Tensor *act_input, Tensor *output,\n                          Tensor *dbias, Tensor *workspace, cudaStream_t stream) {\n  // Check tensors, unless querying dbias workspace\n  if (!IS_DBIAS || workspace->data.dptr != nullptr) {\n    CheckInputTensor(input, \"cast_transpose_fused_input\");\n    CheckOutputTensor(*output, \"output\");\n    if constexpr (IS_DBIAS) {\n      NVTE_CHECK(dbias != nullptr && dbias->has_data());\n      CheckOutputTensor(*dbias, \"dbias\");\n    }\n    if constexpr (IS_DACT) {\n      NVTE_CHECK(act_input != nullptr && act_input->has_data());\n      CheckInputTensor(*act_input, \"act_input\");\n    }\n  }\n\n  // Check that inputs and outputs are available\n  NVTE_CHECK(input.has_data(), \"Input is not allocated\");\n  NVTE_CHECK(output->has_data(), \"Output rowwise data is not allocated\");\n  NVTE_CHECK(output->has_columnwise_data(), \"Output columnwise data is not allocated\");\n\n  // Flatten tensor to 2D\n  NVTE_CHECK(input.data.shape == output->data.shape,\n             \"Input and output shapes do not match (input=\", input.data.shape,\n             \", output=\", output->data.shape);\n  const size_t row_length = input.flat_last_dim();\n  const size_t num_rows = input.flat_first_dim();\n\n  // Check that cast and transposed output data matches\n  NVTE_CHECK(output->data.dtype == output->columnwise_data.dtype,\n             \"Cast and transposed output types must match.\");\n  NVTE_CHECK(output->scale_inv.dptr == output->columnwise_scale_inv.dptr,\n             \"Cast and transposed outputs need to share scale-inverse tensor.\");\n\n  if constexpr (IS_DBIAS) {\n    NVTE_CHECK(dbias->data.dtype == input.data.dtype, \"DBias must have the same type as input.\");\n    NVTE_CHECK(dbias->data.shape == std::vector<size_t>{row_length}, \"Wrong shape of DBias.\");\n  }\n  if constexpr (IS_DACT) {\n    NVTE_CHECK(input.dtype() == act_input->dtype(), \"Types of both inputs must match.\");\n    NVTE_CHECK(input.data.shape == act_input->data.shape, \"Shapes of both inputs must match.\");\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output->dtype(), OutputType, using InputType2 = InputType;\n          using Param = CTDBiasDActParam<InputType, InputType2, OutputType, ComputeType>;\n\n          constexpr int itype_size = sizeof(InputType);\n          constexpr int itype2_size = sizeof(InputType2);\n          constexpr int otype_size = sizeof(OutputType);\n\n          const bool aligned =\n              (row_length % THREADS_PER_WARP == 0) && (num_rows % THREADS_PER_WARP == 0);\n          const bool jit_compiled = aligned && rtc::is_enabled();\n\n          size_t load_size = (IS_DACT ? desired_load_size_dact : desired_load_size);\n          size_t store_size = (IS_DACT ? desired_store_size_dact : desired_store_size);\n          size_t num_blocks;\n\n          if (jit_compiled) {\n            // Pick kernel config\n            std::vector<KernelConfig> kernel_configs;\n            kernel_configs.reserve(16);\n            const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n            auto add_config = [&](size_t load_size_config, size_t store_size_config) {\n              kernel_configs.emplace_back(row_length, num_rows, itype_size, itype2_size, otype_size,\n                                          load_size_config, store_size_config, sm_count, IS_DACT);\n            };\n            add_config(8, 8);\n            add_config(4, 8);\n            add_config(8, 4);\n            add_config(4, 4);\n            add_config(2, 8);\n            add_config(8, 2);\n            add_config(2, 4);\n            add_config(4, 2);\n            add_config(2, 2);\n            add_config(1, 8);\n            add_config(8, 1);\n            add_config(1, 4);\n            add_config(4, 1);\n            add_config(1, 2);\n            add_config(2, 1);\n            add_config(1, 1);\n\n            // Select the kernel configuration with the lowest cost\n            const auto &kernel_config =\n                *std::min_element(kernel_configs.begin(), kernel_configs.end());\n            NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n            load_size = kernel_config.load_size;\n            store_size = kernel_config.store_size;\n            num_blocks = kernel_config.num_blocks;\n          }\n\n          const size_t nvec_in = load_size / itype_size;\n          const size_t nvec_out = store_size / otype_size;\n          const size_t tile_size_x = nvec_in * threads_per_warp;\n          const size_t tile_size_y = nvec_out * threads_per_warp;\n          const size_t num_tiles_x = DIVUP(row_length, tile_size_x);\n          const size_t num_tiles_y = DIVUP(num_rows, tile_size_y);\n          const size_t num_tiles = num_tiles_x * num_tiles_y;\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n          if (!jit_compiled) {\n            num_blocks = DIVUP(num_tiles * n_warps_per_tile, n_warps_per_block);\n          } if constexpr (IS_DBIAS) {\n            // Check workspace size\n            populate_cast_transpose_dbias_workspace_config(*output, workspace, nvec_out);\n            if (workspace->data.dptr == nullptr) {\n              return;\n            }\n          }\n\n          size_t VecOutputTypeSize;\n          switch (nvec_out) {\n            case 1:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 1>);\n              break;\n            case 2:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 2>);\n              break;\n            case 4:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 4>);\n              break;\n            case 8:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 8>);\n              break;\n          } size_t shared_size_transpose = cast_transpose_num_threads / n_warps_per_tile *\n                                           (threads_per_warp + 1) * VecOutputTypeSize;\n\n          if constexpr (IS_DBIAS) {\n            size_t VecComputeTypeSize;\n            switch (nvec_in) {\n              case 1:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 1>);\n                break;\n              case 2:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 2>);\n                break;\n              case 4:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 4>);\n                break;\n              case 8:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 8>);\n                break;\n            }\n            const size_t shared_size_dbias = cast_transpose_num_threads * VecComputeTypeSize;\n            if (shared_size_transpose < shared_size_dbias) {\n              shared_size_transpose = shared_size_dbias;\n            }\n          }\n\n          Param param;\n          param.input = reinterpret_cast<const InputType *>(input.data.dptr);\n          param.output_c = reinterpret_cast<OutputType *>(output->data.dptr);\n          param.output_t = reinterpret_cast<OutputType *>(output->columnwise_data.dptr);\n          param.scale_ptr = reinterpret_cast<const ComputeType *>(output->scale.dptr);\n          param.amax = reinterpret_cast<ComputeType *>(output->amax.dptr);\n          param.scale_inv = reinterpret_cast<ComputeType *>(output->scale_inv.dptr);\n          if constexpr (IS_DBIAS) {\n            param.workspace = reinterpret_cast<ComputeType *>(workspace->data.dptr);\n          } if constexpr (IS_DACT) {\n            param.act_input = reinterpret_cast<const InputType2 *>(act_input->data.dptr);\n          }\n\n          // Runtime-compiled tuned kernel\n          if (jit_compiled) {\n            constexpr const char *itype_name = TypeInfo<InputType>::name;\n            constexpr const char *itype2_name = TypeInfo<InputType2>::name;\n            constexpr const char *otype_name = TypeInfo<OutputType>::name;\n\n            int actType = 0;\n            if constexpr (IS_DACT || IS_ACT) {\n              actType = get_activation_type<ComputeType, ParamOP, OP>();\n            }\n\n            // Compile NVRTC kernel if needed and launch\n            auto &rtc_manager = rtc::KernelManager::instance();\n            const std::string kernel_label = concat_strings(\n                \"cast_transpose_fusion\"\n                \",itype=\",\n                itype_name, \",itype2=\", itype2_name, \",otype=\", otype_name,\n                \",load_size=\", load_size, \",store_size=\", store_size, \",IS_DBIAS=\", IS_DBIAS,\n                \",IS_DACT=\", IS_DACT, \",IS_ACT=\", IS_ACT,\n                \",activationType=\", ActTypeToString[actType]);\n\n            if (!rtc_manager.is_compiled(kernel_label)) {\n              std::string code = string_code_transpose_rtc_cast_transpose_fusion_cu;\n              code = regex_replace(code, \"__ITYPE__\", itype_name);\n              code = regex_replace(code, \"__ITYPE2__\", itype2_name);\n              code = regex_replace(code, \"__OTYPE__\", otype_name);\n              code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n              code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n              code = regex_replace(code, \"__WARPS_PER_TILE__\", n_warps_per_tile);\n              code = regex_replace(code, \"__BLOCK_SIZE__\", cast_transpose_num_threads);\n              code = regex_replace(code, \"__IS_DBIAS__\", IS_DBIAS);\n              code = regex_replace(code, \"__IS_DACT__\", IS_DACT);\n              code = regex_replace(code, \"__IS_ACT__\", IS_ACT);\n              code = regex_replace(code, \"__ACTIVATION_TYPE__\", actType);\n\n              rtc_manager.compile(\n                  kernel_label, \"cast_transpose_fusion_kernel_optimized\", code,\n                  \"transformer_engine/common/transpose/rtc/cast_transpose_fusion.cu\");\n            }\n\n            rtc_manager.set_cache_config(kernel_label, CU_FUNC_CACHE_PREFER_SHARED);\n\n            rtc_manager.launch(kernel_label, num_blocks, cast_transpose_num_threads,\n                               shared_size_transpose, stream, param, row_length, num_rows,\n                               num_tiles);\n          } else {  // Statically-compiled general kernel\n            constexpr size_t load_size = IS_DACT ? desired_load_size_dact : desired_load_size;\n            constexpr size_t store_size = IS_DACT ? desired_store_size_dact : desired_store_size;\n            constexpr size_t nvec_in = load_size / itype_size;\n            constexpr size_t nvec_out = store_size / otype_size;\n\n            NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n            NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n                cast_transpose_fused_kernel_notaligned<IS_DBIAS, IS_DACT, IS_ACT, ComputeType,\n                                                       Param, nvec_in, nvec_out, Empty, OP>,\n                cudaFuncAttributePreferredSharedMemoryCarveout, 100));\n            cast_transpose_fused_kernel_notaligned<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Param,\n                                                   nvec_in, nvec_out, Empty, OP>\n                <<<num_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, num_tiles);\n            NVTE_CHECK_CUDA(cudaGetLastError());\n          }\n\n          if constexpr (IS_DBIAS) {\n            reduce_dbias<InputType>(*workspace, dbias, row_length, num_rows, nvec_out, stream);\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\ntemplate <int nvec_in, int nvec_out, typename CType, typename IType, typename OType,\n          typename ParamOP, CType (*OP1)(CType, const ParamOP &),\n          CType (*OP2)(CType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    dgated_act_cast_transpose_kernel(const IType *const input, const IType *const act_input,\n                                     OType *const output_c, OType *const output_t,\n                                     const CType *const scale_ptr, CType *const amax,\n                                     CType *const scale_inv, const size_t row_length,\n                                     const size_t num_rows, const size_t num_tiles) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_act_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_gate_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_c_tile_0 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_c_tile_1 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_t_tile_0 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  OType *const my_output_t_tile_1 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP +\n      row_length * num_rows;\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  IVec in[2][nvec_out];\n  IVec act_in[2][nvec_out];\n  IVec gate_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space_0[n_iterations][nvec_in];\n  OVec out_space_1[n_iterations][nvec_in];\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const size_t stride2 = 2 * row_length / nvec_in;\n  size_t current_stride2 = warp_id_in_tile * n_iterations * nvec_out * stride2;\n  CType max = 0;\n  const CType scale = scale_ptr != nullptr ? *scale_ptr : 1;\n\n  CVec partial_dbias;\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n    act_in[0][i].load_from(my_act_input_tile, current_stride2 + my_place + stride2 * i);\n    gate_in[0][i].load_from(my_gate_input_tile, current_stride2 + my_place + stride2 * i);\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride2 + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        in[current_in][j].load_from(my_input_tile,\n                                    current_stride + my_place_in + stride * (nvec_out + j));\n        act_in[current_in][j].load_from(my_act_input_tile,\n                                        current_stride2 + my_place_in + stride2 * (nvec_out + j));\n        gate_in[current_in][j].load_from(my_gate_input_tile,\n                                         current_stride2 + my_place_in + stride2 * (nvec_out + j));\n      }\n    }\n    CVec after_dact[nvec_out];   // NOLINT(*)\n    CVec after_dgate[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        after_dact[j].data.elt[k] = OP1(act_in[current_in ^ 1][j].data.elt[k], {}) *\n                                    CType(in[current_in ^ 1][j].data.elt[k]) *\n                                    CType(gate_in[current_in ^ 1][j].data.elt[k]);\n        after_dgate[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                     OP2(act_in[current_in ^ 1][j].data.elt[k], {});\n      }\n    }\n    OVec out_trans_0[nvec_in];  // NOLINT(*)\n    OVec out_trans_1[nvec_in];  // NOLINT(*)\n\n    constexpr bool IS_DBIAS = false;\n    constexpr bool IS_FULL_TILE = true;\n    constexpr bool valid_store = true;\n    constexpr int dbias_shfl_src_lane = 0;\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_trans_0, partial_dbias,\n                                                    my_output_c_tile_0, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dgate, out_trans_1, partial_dbias,\n                                                    my_output_c_tile_1, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space_0[i][j].data.vec = out_trans_0[j].data.vec;\n      out_space_1[i][j].data.vec = out_trans_1[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_stride2 += nvec_out * stride2;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_0[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_0,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_1[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_1,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax != nullptr) {\n    max = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(max, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax, max);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv != nullptr) {\n    reciprocal<CType>(scale_inv, scale);\n  }\n}\n\ntemplate <int nvec_in, int nvec_out, typename CType, typename IType, typename OType,\n          typename ParamOP, CType (*OP1)(CType, const ParamOP &),\n          CType (*OP2)(CType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    dgated_act_cast_transpose_kernel_notaligned(const IType *const input,\n                                                const IType *const act_input, OType *const output_c,\n                                                OType *const output_t, const CType *const scale_ptr,\n                                                CType *const amax, CType *const scale_inv,\n                                                const size_t row_length, const size_t num_rows,\n                                                const size_t num_tiles) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_act_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_gate_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_c_tile_0 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_c_tile_1 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_t_tile_0 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  OType *const my_output_t_tile_1 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP +\n      row_length * num_rows;\n  const size_t stride = row_length / nvec_in;\n  const size_t stride2 = 2 * row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  IVec in[2][nvec_out];\n  IVec act_in[2][nvec_out];\n  IVec gate_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space_0[n_iterations][nvec_in];\n  OVec out_space_1[n_iterations][nvec_in];\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  size_t current_stride2 = warp_id_in_tile * n_iterations * nvec_out * stride2;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  CType max = 0;\n  const CType scale = scale_ptr != nullptr ? *scale_ptr : 1;\n\n  CVec partial_dbias;\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n        act_in[0][i].load_from(my_act_input_tile, current_stride2 + my_place + stride2 * i);\n        gate_in[0][i].load_from(my_gate_input_tile, current_stride2 + my_place + stride2 * i);\n      } else {\n        in[0][i].clear();\n        act_in[0][i].clear();\n        gate_in[0][i].clear();\n      }\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride2 + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      {\n        const bool valid_load =\n            my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n        for (unsigned int j = 0; j < nvec_out; ++j) {\n          if (valid_load) {\n            in[current_in][j].load_from(my_input_tile,\n                                        current_stride + my_place_in + stride * (nvec_out + j));\n            act_in[current_in][j].load_from(\n                my_act_input_tile, current_stride2 + my_place_in + stride2 * (nvec_out + j));\n            gate_in[current_in][j].load_from(\n                my_gate_input_tile, current_stride2 + my_place_in + stride2 * (nvec_out + j));\n          } else {\n            in[current_in][j].clear();\n            act_in[current_in][j].clear();\n            gate_in[current_in][j].clear();\n          }\n        }\n      }\n    }\n    CVec after_dact[nvec_out];   // NOLINT(*)\n    CVec after_dgate[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        after_dact[j].data.elt[k] = OP1(act_in[current_in ^ 1][j].data.elt[k], {}) *\n                                    CType(in[current_in ^ 1][j].data.elt[k]) *\n                                    CType(gate_in[current_in ^ 1][j].data.elt[k]);\n        after_dgate[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                     OP2(act_in[current_in ^ 1][j].data.elt[k], {});\n      }\n    }\n    OVec out_trans_0[nvec_in];  // NOLINT(*)\n    OVec out_trans_1[nvec_in];  // NOLINT(*)\n\n    constexpr bool IS_DBIAS = false;\n    constexpr bool IS_FULL_TILE = false;\n    constexpr int dbias_shfl_src_lane = 0;\n    const bool valid_store =\n        (my_place < tile_length) && (warp_id_in_tile * n_iterations + i < tile_height);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_trans_0, partial_dbias,\n                                                    my_output_c_tile_0, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dgate, out_trans_1, partial_dbias,\n                                                    my_output_c_tile_1, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space_0[i][j].data.vec = out_trans_0[j].data.vec;\n      out_space_1[i][j].data.vec = out_trans_1[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_stride2 += nvec_out * stride2;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_0[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_0,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_1[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_1,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax != nullptr) {\n    max = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(max, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax, max);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv != nullptr) {\n    reciprocal<CType>(scale_inv, scale);\n  }\n}\n\ntemplate <typename ComputeType, typename ParamOP, ComputeType (*OP1)(ComputeType, const ParamOP &),\n          ComputeType (*OP2)(ComputeType, const ParamOP &)>\nvoid dgated_act_cast_transpose(const Tensor &input, const Tensor &gated_act_input, Tensor *output,\n                               cudaStream_t stream) {\n  CheckInputTensor(input, \"dgated_act_cast_transpose_input\");\n  CheckInputTensor(gated_act_input, \"dgated_act_cast_transpose_gated_act_input\");\n  CheckOutputTensor(*output, \"dgated_act_cast_transpose_output\");\n\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(gated_act_input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(output->has_data() && output->has_columnwise_data(),\n             \"Both rowwise and columnwise data need to be allocated.\");\n  NVTE_CHECK(output->data.shape.size() == 2, \"C output must have 2 dimensions.\");\n  NVTE_CHECK(output->columnwise_data.shape.size() == 2, \"T output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(gated_act_input.data.shape[0] == num_rows, \"Wrong dimension of output.\");\n  NVTE_CHECK(gated_act_input.data.shape[1] == row_length * 2, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->data.shape[0] == num_rows, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->data.shape[1] == row_length * 2, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->columnwise_data.shape[0] == row_length * 2, \"Wrong dimension of T output.\");\n  NVTE_CHECK(output->columnwise_data.shape[1] == num_rows, \"Wrong dimension of T output.\");\n\n  NVTE_CHECK(input.data.dtype == gated_act_input.data.dtype, \"Types of both inputs must match.\");\n\n  NVTE_CHECK(output->data.dtype == output->columnwise_data.dtype,\n             \"C and T outputs need to have the same type.\");\n  NVTE_CHECK(output->scale_inv.dptr == output->columnwise_scale_inv.dptr,\n             \"C and T outputs need to share scale inverse tensor.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output->dtype(), OutputType, using InputType2 = InputType;\n          /* dact fusion kernel uses more registers */\n          constexpr int desired_load_size_dact = 4;\n          constexpr int desired_store_size_dact = 4; constexpr int itype_size = sizeof(InputType);\n          constexpr int otype_size = sizeof(OutputType);\n          constexpr int nvec_in = desired_load_size_dact / itype_size;\n          constexpr int nvec_out = desired_store_size_dact / otype_size;\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n          const size_t n_tiles =\n              DIVUP(row_length, static_cast<size_t>(nvec_in * THREADS_PER_WARP)) *\n              DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n          const size_t n_warps_per_block = cast_transpose_num_threads / THREADS_PER_WARP;\n          const size_t n_blocks = DIVUP(n_tiles * n_warps_per_tile, n_warps_per_block);\n\n          const bool full_tile = row_length % (nvec_in * THREADS_PER_WARP) == 0 &&\n                                 num_rows % (nvec_out * THREADS_PER_WARP) == 0;\n          const size_t shmem_size = cast_transpose_num_threads / n_warps_per_tile *\n                                    (THREADS_PER_WARP + 1) * sizeof(Vec<OutputType, nvec_out>);\n          if (full_tile) {\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n                dgated_act_cast_transpose_kernel<nvec_in, nvec_out, ComputeType, InputType,\n                                                 OutputType, Empty, OP1, OP2>,\n                cudaFuncAttributePreferredSharedMemoryCarveout, 100));\n\n            dgated_act_cast_transpose_kernel<nvec_in, nvec_out, ComputeType, InputType, OutputType,\n                                             Empty, OP1, OP2>\n                <<<n_blocks, cast_transpose_num_threads, shmem_size, stream>>>(\n                    reinterpret_cast<const InputType *>(input.data.dptr),\n                    reinterpret_cast<const InputType *>(gated_act_input.data.dptr),\n                    reinterpret_cast<OutputType *>(output->data.dptr),\n                    reinterpret_cast<OutputType *>(output->columnwise_data.dptr),\n                    reinterpret_cast<const fp32 *>(output->scale.dptr),\n                    reinterpret_cast<fp32 *>(output->amax.dptr),\n                    reinterpret_cast<fp32 *>(output->scale_inv.dptr), row_length, num_rows,\n                    n_tiles);\n            NVTE_CHECK_CUDA(cudaGetLastError());\n          } else {\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n                dgated_act_cast_transpose_kernel_notaligned<nvec_in, nvec_out, ComputeType,\n                                                            InputType, OutputType, Empty, OP1, OP2>,\n                cudaFuncAttributePreferredSharedMemoryCarveout, 100));\n            dgated_act_cast_transpose_kernel_notaligned<nvec_in, nvec_out, ComputeType, InputType,\n                                                        OutputType, Empty, OP1, OP2>\n                <<<n_blocks, cast_transpose_num_threads, shmem_size, stream>>>(\n                    reinterpret_cast<const InputType *>(input.data.dptr),\n                    reinterpret_cast<const InputType *>(gated_act_input.data.dptr),\n                    reinterpret_cast<OutputType *>(output->data.dptr),\n                    reinterpret_cast<OutputType *>(output->columnwise_data.dptr),\n                    reinterpret_cast<const fp32 *>(output->scale.dptr),\n                    reinterpret_cast<fp32 *>(output->amax.dptr),\n                    reinterpret_cast<fp32 *>(output->scale_inv.dptr), row_length, num_rows,\n                    n_tiles);\n            NVTE_CHECK_CUDA(cudaGetLastError());\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\n// Explicit template instantiation\ntemplate void cast_transpose_fused<true, false, false, float, transformer_engine::Empty, nullptr>(\n    const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, cudaStream_t);\n#define NVTE_INSTANTIATE_ACTIVATION(op)                                                    \\\n  template void cast_transpose_fused<false, false, true, float, transformer_engine::Empty, \\\n                                     transformer_engine::op<float, float>>(                \\\n      const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, cudaStream_t);         \\\n  template void cast_transpose_fused<false, true, false, float, transformer_engine::Empty, \\\n                                     transformer_engine::d##op<float, float>>(             \\\n      const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, cudaStream_t);\nNVTE_INSTANTIATE_ACTIVATION(relu);\nNVTE_INSTANTIATE_ACTIVATION(srelu);\nNVTE_INSTANTIATE_ACTIVATION(gelu);\nNVTE_INSTANTIATE_ACTIVATION(qgelu);\nNVTE_INSTANTIATE_ACTIVATION(silu);\n#undef NVTE_INSTANTIATE_ACTIVATION\n\n}  // namespace detail\n\n}  // namespace transformer_engine\n\nusing ComputeType = typename transformer_engine::fp32;\n\nvoid nvte_cast_transpose_dbias(const NVTETensor input, NVTETensor output, NVTETensor dbias,\n                               NVTETensor workspace, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = false;\n  constexpr bool IS_ACT = false;\n\n  constexpr const NVTETensor activation_input = nullptr;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, nullptr>(\n      *convertNVTETensorCheck(input), convertNVTETensor(activation_input),\n      convertNVTETensor(output), convertNVTETensor(dbias), convertNVTETensor(workspace), stream);\n}\n\nvoid nvte_cast_transpose_dbias_dgelu(const NVTETensor input, const NVTETensor act_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dgelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(act_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dsilu(const NVTETensor input, const NVTETensor silu_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dsilu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dsilu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(silu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_drelu(const NVTETensor input, const NVTETensor relu_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_drelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, drelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(relu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dsrelu(const NVTETensor input, const NVTETensor srelu_input,\n                                      NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                      cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dsrelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dsrelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(srelu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dqgelu(const NVTETensor input, const NVTETensor qgelu_input,\n                                      NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                      cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dqgelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dqgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(qgelu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_dgeglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dgeglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dgelu<fp32, fp32>, gelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dswiglu_cast_transpose(const NVTETensor input, const NVTETensor swiglu_input,\n                                 NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dswiglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dsilu<fp32, fp32>, silu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(swiglu_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dreglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dreglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, drelu<fp32, fp32>, relu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dsreglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                 NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dsreglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dsrelu<fp32, fp32>, srelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dqgeglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                 NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dqgeglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dqgelu<fp32, fp32>, qgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <functional>\n#include <numeric>\n#include <type_traits>\n\n#include \"../util/math.h\"\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"cast_transpose.h\"\n#include \"common/common.h\"\n\nnamespace transformer_engine {\n\nnamespace detail {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_cast_transpose_fusion_cu.h\"\n\n// STUFF TO TUNE\nconstexpr size_t n_warps_per_tile = 8;\nconstexpr size_t desired_load_size = 8;\nconstexpr size_t desired_store_size = 8;\nconstexpr size_t desired_load_size_dact = 4;  // dAct fusion kernels use more registers\nconstexpr size_t desired_store_size_dact = 4;\n\nconstexpr size_t threads_per_warp = static_cast<size_t>(THREADS_PER_WARP);\nconstexpr size_t max_threads_per_block = 256;\nconstexpr size_t reduce_dbias_num_threads = 256;\nconstexpr size_t cast_transpose_num_threads = n_warps_per_tile * threads_per_warp;\nconstexpr size_t n_warps_per_block = cast_transpose_num_threads / threads_per_warp;\nstatic_assert(cast_transpose_num_threads <= max_threads_per_block);\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  size_t load_size = 0;   // Vector load size\n  size_t store_size = 0;  // Vector store size to transposed output\n\n  bool valid = false;     // Whether config is valid\n  bool is_dact = false;   // Whether dact is used\n  size_t num_blocks = 0;  // Number of CUDA blocks\n\n  size_t active_sm_count = 0;         // Number of active SMs\n  size_t elements_per_load = 0;       // Elements per L1 cache load\n  size_t elements_per_load_dact = 0;  // Elements per L1 cache load dact\n  size_t elements_per_store_c = 0;    // Elements per L1 cache store to cast output\n  size_t elements_per_store_t = 0;    // Elements per L1 cache store to transposed output\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t itype_size, size_t itype2_size,\n               size_t otype_size, size_t load_size_, size_t store_size_, size_t sm_count,\n               bool is_dact_)\n      : load_size{load_size_}, store_size{store_size_}, is_dact{is_dact_} {\n    if (is_dact) {\n      if (load_size > desired_load_size_dact || store_size > desired_store_size_dact) {\n        return;\n      }\n    }\n\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % itype_size != 0 || store_size % otype_size != 0 ||\n        cache_line_size % itype_size != 0 || cache_line_size % otype_size != 0) {\n      return;\n    }\n    /* row_tile_elements */\n    const size_t tile_size_x = (load_size * THREADS_PER_WARP) / itype_size;\n    /* col_tile_elements */\n    const size_t tile_size_y = (store_size * THREADS_PER_WARP) / otype_size;\n    const size_t num_tiles_x = row_length / tile_size_x;\n    const size_t num_tiles_y = num_rows / tile_size_y;\n\n    valid = (row_length % tile_size_x == 0 && num_rows % tile_size_y == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = num_tiles_x * num_tiles_y;\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * n_warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, tile_size_x * itype_size) / itype_size);\n    elements_per_load_dact = (std::min(cache_line_size, tile_size_x * itype2_size) / itype2_size);\n    elements_per_store_c = (std::min(cache_line_size, tile_size_x * otype_size) / otype_size);\n    elements_per_store_t = (std::min(cache_line_size, tile_size_y * otype_size) / otype_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load\n      //         + 1/elements_per_load_dact\n      //         + 1/elements_per_store_c\n      //         + 1/elements_per_store_t) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &la1 = this->elements_per_load_dact;\n      const auto &sc1 = this->elements_per_store_c;\n      const auto &st1 = this->elements_per_store_t;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &la2 = other.elements_per_load_dact;\n      const auto &sc2 = other.elements_per_store_c;\n      const auto &st2 = other.elements_per_store_t;\n      const auto &p2 = other.active_sm_count;\n      const auto scale1 = l1 * sc1 * st1 * p1 * (is_dact ? la1 : 1);\n      const auto scale2 = l2 * sc2 * st2 * p2 * (is_dact ? la2 : 1);\n      const auto scale = scale1 * scale2;\n      const auto cost1 =\n          (scale / l1 + scale / sc1 + scale / st1 + (is_dact ? (scale / la1) : 0)) / p1;\n      const auto cost2 =\n          (scale / l2 + scale / sc2 + scale / st2 + (is_dact ? (scale / la2) : 0)) / p2;\n\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <bool IS_DBIAS, bool IS_FULL_TILE, int nvec_in, int nvec_out, typename OVec, typename CVec,\n          typename CType>\ninline __device__ void cast_and_transpose_regs(const CVec (&in)[nvec_out],\n                                               OVec (&out_trans)[nvec_in],\n                                               CVec &out_dbias,  // NOLINT(*)\n                                               typename OVec::type *output_cast_tile,\n                                               const size_t current_place, const size_t stride,\n                                               const CType scale,\n                                               CType &amax,  // NOLINT(*)\n                                               const int dbias_shfl_src_lane,\n                                               const bool valid_store) {\n  using OType = typename OVec::type;\n  using OVecC = Vec<OType, nvec_in>;\n\n  CVec step_dbias;\n  if constexpr (IS_DBIAS) {\n    step_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    OVecC out_cast;\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      const CType tmp = in[i].data.elt[j];\n      if constexpr (IS_DBIAS) {\n        step_dbias.data.elt[j] += tmp;  // dbias: thread tile local accumulation\n      }\n      out_cast.data.elt[j] = static_cast<OType>(tmp * scale);\n      out_trans[j].data.elt[i] = static_cast<OType>(tmp * scale);  // thread tile transpose\n\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(fabsf(tmp), amax);\n    }\n    if (IS_FULL_TILE || valid_store) {\n      out_cast.store_to(output_cast_tile, current_place + stride * i);\n    }\n  }\n\n  if constexpr (IS_DBIAS) {\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      CType elt = step_dbias.data.elt[j];\n      elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in a warp\n      out_dbias.data.elt[j] += elt;\n    }\n  }\n}\n\nvoid populate_cast_transpose_dbias_workspace_config(const Tensor &cast_output, /*cast*/\n                                                    Tensor *workspace, const int nvec_out) {\n  const size_t row_length = cast_output.flat_last_dim();\n  const size_t num_rows = cast_output.flat_first_dim();\n\n  const size_t tile_size_y = (nvec_out * THREADS_PER_WARP);\n  NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n  const size_t num_rows_partial_dbias = DIVUP(num_rows, tile_size_y);\n\n  if (workspace->data.dptr == nullptr) {\n    workspace->data.shape = {num_rows_partial_dbias, row_length};\n    workspace->data.dtype = DType::kFloat32;\n  } else {\n    // Check that workspace matches expected size\n    const size_t workspace_size = get_buffer_size_bytes(\n        std::accumulate(workspace->data.shape.begin(), workspace->data.shape.end(), 1,\n                        std::multiplies<size_t>()),\n        workspace->data.dtype);\n    const size_t required_size =\n        get_buffer_size_bytes(num_rows_partial_dbias, row_length, DType::kFloat32);\n    NVTE_CHECK(workspace_size >= required_size, \"Invalid workspace (expected dims=(\",\n               num_rows_partial_dbias, \",\", row_length, \"), dtype=\", to_string(DType::kFloat32),\n               \"; found dims=\", workspace->data.shape,\n               \", dtype=\", typeToNumBits(workspace->data.dtype), \" bits)\");\n  }\n}\n\ntemplate <int nvec, typename ComputeType, typename OutputType>\n__global__ void __launch_bounds__(reduce_dbias_num_threads)\n    reduce_dbias_kernel(OutputType *const dbias_output, const ComputeType *const dbias_partial,\n                        const int row_length, const int num_rows) {\n  using ComputeVec = Vec<ComputeType, nvec>;\n  using OutputVec = Vec<OutputType, nvec>;\n\n  const int thread_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (thread_id * nvec >= row_length) {\n    return;\n  }\n\n  const ComputeType *const thread_in_base = dbias_partial + thread_id * nvec;\n  OutputType *const thread_out_base = dbias_output + thread_id * nvec;\n\n  const int stride_in_vec = row_length / nvec;\n\n  ComputeVec ldg_vec;\n  ComputeVec acc_vec;\n  acc_vec.clear();\n  for (int i = 0; i < num_rows; ++i) {\n    ldg_vec.load_from(thread_in_base, i * stride_in_vec);\n#pragma unroll\n    for (int e = 0; e < nvec; ++e) {\n      acc_vec.data.elt[e] += ldg_vec.data.elt[e];\n    }\n  }\n\n  OutputVec stg_vec;\n#pragma unroll\n  for (int e = 0; e < nvec; ++e) {\n    stg_vec.data.elt[e] = OutputType(acc_vec.data.elt[e]);\n  }\n  stg_vec.store_to(thread_out_base, 0);\n}\n\ntemplate <typename InputType>\nvoid reduce_dbias(const Tensor &workspace, Tensor *dbias, const size_t row_length,\n                  const size_t num_rows, const int nvec_out, hipStream_t stream) {\n  constexpr int reduce_dbias_store_bytes = 8;  // stg.64\n  constexpr int reduce_dbias_nvec = reduce_dbias_store_bytes / sizeof(InputType);\n\n  NVTE_CHECK(row_length % reduce_dbias_nvec == 0, \"Unsupported shape.\");\n\n  const size_t reduce_dbias_row_length = row_length;\n  const size_t reduce_dbias_num_rows =\n      DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n  const size_t reduce_dbias_num_blocks =\n      DIVUP(row_length, reduce_dbias_num_threads * reduce_dbias_nvec);\n\n  using DbiasOutputType = fp32;\n  reduce_dbias_kernel<reduce_dbias_nvec, DbiasOutputType, InputType>\n      <<<reduce_dbias_num_blocks, reduce_dbias_num_threads, 0, stream>>>(\n          reinterpret_cast<InputType *>(dbias->data.dptr),\n          reinterpret_cast<const fp32 *>(workspace.data.dptr), reduce_dbias_row_length,\n          reduce_dbias_num_rows);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <bool IS_DBIAS, bool IS_DACT, bool IS_ACT, typename ComputeType, typename Param,\n          int nvec_in, int nvec_out, typename ParamOP,\n          ComputeType (*OP)(ComputeType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    cast_transpose_fused_kernel_notaligned(const Param param, const size_t row_length,\n                                           const size_t num_rows, const size_t num_tiles) {\n  static_assert(!(IS_DACT && IS_ACT), \"forward and backward activation are mutually exclusive\");\n  using IType = typename Param::InputType;\n  using IType2 = typename Param::InputType2;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using IVec2 = Vec<IType2, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const size_t tile_offset =\n      (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const size_t tile_offset_transp =\n      (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n\n  const IType *const my_input_tile = param.input + tile_offset;\n  const IType2 *const my_act_input_tile = param.act_input + tile_offset;\n  OType *const my_output_c_tile = param.output_c + tile_offset;\n  OType *const my_output_t_tile = param.output_t + tile_offset_transp;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  IVec2 act_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  size_t current_row = (tile_id_y * THREADS_PER_WARP + warp_id_in_tile * n_iterations) * nvec_out;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  CType amax = 0;\n  const CType scale = param.scale_ptr != nullptr ? *param.scale_ptr : 1;\n\n  CVec partial_dbias;\n  if constexpr (IS_DBIAS) {\n    partial_dbias.clear();\n  }\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        const size_t ld_offset = current_stride + my_place + stride * i;\n        in[0][i].load_from(my_input_tile, ld_offset);\n        if constexpr (IS_DACT) {\n          act_in[0][i].load_from(my_act_input_tile, ld_offset);\n        }\n      } else {\n        in[0][i].clear();\n        if constexpr (IS_DACT) {\n          act_in[0][i].clear();\n        }\n      }\n    }\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      const bool valid_load =\n          my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        if (valid_load) {\n          const size_t ld_offset = current_stride + my_place_in + stride * (nvec_out + j);\n          in[current_in][j].load_from(my_input_tile, ld_offset);\n          if constexpr (IS_DACT) {\n            act_in[current_in][j].load_from(my_act_input_tile, ld_offset);\n          }\n        } else {\n          in[current_in][j].clear();\n          if constexpr (IS_DACT) {\n            act_in[current_in][j].clear();\n          }\n        }\n      }\n    }\n    CVec after_dact[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        if constexpr (IS_DACT) {\n          after_dact[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                      OP(act_in[current_in ^ 1][j].data.elt[k], {});\n        } else if constexpr (IS_ACT) {\n          after_dact[j].data.elt[k] = OP(in[current_in ^ 1][j].data.elt[k], {});\n        } else {\n          after_dact[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]);\n        }\n      }\n    }\n    const int dbias_shfl_src_lane =\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    constexpr bool IS_FULL_TILE = false;\n    const bool valid_store =\n        (my_place < tile_length) && (warp_id_in_tile * n_iterations + i < tile_height);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_space[i], partial_dbias,\n                                                    my_output_c_tile, current_place, stride, scale,\n                                                    amax, dbias_shfl_src_lane, valid_store);\n\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_row += nvec_out;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  if constexpr (IS_DBIAS) {\n    my_dbias_scratch[threadIdx.x] = partial_dbias;\n    __syncthreads();\n    if (warp_id_in_tile == 0) {\n#pragma unroll\n      for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n        CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n        for (unsigned int j = 0; j < nvec_in; ++j) {\n          partial_dbias.data.elt[j] += tmp.data.elt[j];\n        }\n      }\n      if (my_id_in_warp < tile_length) {\n        partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n      }\n    }\n  }\n\n  // Reduce amax over block\n  if (param.amax != nullptr) {\n    amax = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(amax, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(param.amax, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && param.scale_inv != nullptr) {\n    reciprocal<CType>(param.scale_inv, scale);\n  }\n}\n\nstatic const char *ActTypeToString[] = {\n    \"none\",      // 0\n    \"sigmoid\",   // 1\n    \"dsigmoid\",  // 2\n    \"gelu\",      // 3\n    \"dgelu\",     // 4\n    \"qgelu\",     // 5\n    \"dqgelu\",    // 6\n    \"silu\",      // 7\n    \"dsilu\",     // 8\n    \"relu\",      // 9\n    \"drelu\",     // 10\n    \"srelu\",     // 11\n    \"dsrelu\"     // 12\n};\n\ntemplate <typename ComputeType, typename ParamOP, ComputeType (*OP)(ComputeType, const ParamOP &)>\nconstexpr int get_activation_type() {\n  constexpr decltype(OP) ActivationList[] = {\n      nullptr,                              // 0\n      &sigmoid<ComputeType, ComputeType>,   // 1\n      &dsigmoid<ComputeType, ComputeType>,  // 2\n      &gelu<ComputeType, ComputeType>,      // 3\n      &dgelu<ComputeType, ComputeType>,     // 4\n      &qgelu<ComputeType, ComputeType>,     // 5\n      &dqgelu<ComputeType, ComputeType>,    // 6\n      &silu<ComputeType, ComputeType>,      // 7\n      &dsilu<ComputeType, ComputeType>,     // 8\n      &relu<ComputeType, ComputeType>,      // 9\n      &drelu<ComputeType, ComputeType>,     // 10\n      &srelu<ComputeType, ComputeType>,     // 11\n      &dsrelu<ComputeType, ComputeType>     // 12\n  };\n#pragma unroll\n  for (int i = 0; i < sizeof(ActivationList) / sizeof(ActivationList[0]); ++i) {\n    if (OP == ActivationList[i]) {\n      return i;\n    }\n  }\n  return 0;\n}\n\ntemplate <bool IS_DBIAS, bool IS_DACT, bool IS_ACT, typename ComputeType, typename ParamOP,\n          ComputeType (*OP)(ComputeType, const ParamOP &)>\nvoid cast_transpose_fused(const Tensor &input, const Tensor *act_input, Tensor *output,\n                          Tensor *dbias, Tensor *workspace, hipStream_t stream) {\n  // Check tensors, unless querying dbias workspace\n  if (!IS_DBIAS || workspace->data.dptr != nullptr) {\n    CheckInputTensor(input, \"cast_transpose_fused_input\");\n    CheckOutputTensor(*output, \"output\");\n    if constexpr (IS_DBIAS) {\n      NVTE_CHECK(dbias != nullptr && dbias->has_data());\n      CheckOutputTensor(*dbias, \"dbias\");\n    }\n    if constexpr (IS_DACT) {\n      NVTE_CHECK(act_input != nullptr && act_input->has_data());\n      CheckInputTensor(*act_input, \"act_input\");\n    }\n  }\n\n  // Check that inputs and outputs are available\n  NVTE_CHECK(input.has_data(), \"Input is not allocated\");\n  NVTE_CHECK(output->has_data(), \"Output rowwise data is not allocated\");\n  NVTE_CHECK(output->has_columnwise_data(), \"Output columnwise data is not allocated\");\n\n  // Flatten tensor to 2D\n  NVTE_CHECK(input.data.shape == output->data.shape,\n             \"Input and output shapes do not match (input=\", input.data.shape,\n             \", output=\", output->data.shape);\n  const size_t row_length = input.flat_last_dim();\n  const size_t num_rows = input.flat_first_dim();\n\n  // Check that cast and transposed output data matches\n  NVTE_CHECK(output->data.dtype == output->columnwise_data.dtype,\n             \"Cast and transposed output types must match.\");\n  NVTE_CHECK(output->scale_inv.dptr == output->columnwise_scale_inv.dptr,\n             \"Cast and transposed outputs need to share scale-inverse tensor.\");\n\n  if constexpr (IS_DBIAS) {\n    NVTE_CHECK(dbias->data.dtype == input.data.dtype, \"DBias must have the same type as input.\");\n    NVTE_CHECK(dbias->data.shape == std::vector<size_t>{row_length}, \"Wrong shape of DBias.\");\n  }\n  if constexpr (IS_DACT) {\n    NVTE_CHECK(input.dtype() == act_input->dtype(), \"Types of both inputs must match.\");\n    NVTE_CHECK(input.data.shape == act_input->data.shape, \"Shapes of both inputs must match.\");\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output->dtype(), OutputType, using InputType2 = InputType;\n          using Param = CTDBiasDActParam<InputType, InputType2, OutputType, ComputeType>;\n\n          constexpr int itype_size = sizeof(InputType);\n          constexpr int itype2_size = sizeof(InputType2);\n          constexpr int otype_size = sizeof(OutputType);\n\n          const bool aligned =\n              (row_length % THREADS_PER_WARP == 0) && (num_rows % THREADS_PER_WARP == 0);\n          const bool jit_compiled = aligned && rtc::is_enabled();\n\n          size_t load_size = (IS_DACT ? desired_load_size_dact : desired_load_size);\n          size_t store_size = (IS_DACT ? desired_store_size_dact : desired_store_size);\n          size_t num_blocks;\n\n          if (jit_compiled) {\n            // Pick kernel config\n            std::vector<KernelConfig> kernel_configs;\n            kernel_configs.reserve(16);\n            const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n            auto add_config = [&](size_t load_size_config, size_t store_size_config) {\n              kernel_configs.emplace_back(row_length, num_rows, itype_size, itype2_size, otype_size,\n                                          load_size_config, store_size_config, sm_count, IS_DACT);\n            };\n            add_config(8, 8);\n            add_config(4, 8);\n            add_config(8, 4);\n            add_config(4, 4);\n            add_config(2, 8);\n            add_config(8, 2);\n            add_config(2, 4);\n            add_config(4, 2);\n            add_config(2, 2);\n            add_config(1, 8);\n            add_config(8, 1);\n            add_config(1, 4);\n            add_config(4, 1);\n            add_config(1, 2);\n            add_config(2, 1);\n            add_config(1, 1);\n\n            // Select the kernel configuration with the lowest cost\n            const auto &kernel_config =\n                *std::min_element(kernel_configs.begin(), kernel_configs.end());\n            NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n            load_size = kernel_config.load_size;\n            store_size = kernel_config.store_size;\n            num_blocks = kernel_config.num_blocks;\n          }\n\n          const size_t nvec_in = load_size / itype_size;\n          const size_t nvec_out = store_size / otype_size;\n          const size_t tile_size_x = nvec_in * threads_per_warp;\n          const size_t tile_size_y = nvec_out * threads_per_warp;\n          const size_t num_tiles_x = DIVUP(row_length, tile_size_x);\n          const size_t num_tiles_y = DIVUP(num_rows, tile_size_y);\n          const size_t num_tiles = num_tiles_x * num_tiles_y;\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n          if (!jit_compiled) {\n            num_blocks = DIVUP(num_tiles * n_warps_per_tile, n_warps_per_block);\n          } if constexpr (IS_DBIAS) {\n            // Check workspace size\n            populate_cast_transpose_dbias_workspace_config(*output, workspace, nvec_out);\n            if (workspace->data.dptr == nullptr) {\n              return;\n            }\n          }\n\n          size_t VecOutputTypeSize;\n          switch (nvec_out) {\n            case 1:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 1>);\n              break;\n            case 2:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 2>);\n              break;\n            case 4:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 4>);\n              break;\n            case 8:\n              VecOutputTypeSize = sizeof(Vec<OutputType, 8>);\n              break;\n          } size_t shared_size_transpose = cast_transpose_num_threads / n_warps_per_tile *\n                                           (threads_per_warp + 1) * VecOutputTypeSize;\n\n          if constexpr (IS_DBIAS) {\n            size_t VecComputeTypeSize;\n            switch (nvec_in) {\n              case 1:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 1>);\n                break;\n              case 2:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 2>);\n                break;\n              case 4:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 4>);\n                break;\n              case 8:\n                VecComputeTypeSize = sizeof(Vec<ComputeType, 8>);\n                break;\n            }\n            const size_t shared_size_dbias = cast_transpose_num_threads * VecComputeTypeSize;\n            if (shared_size_transpose < shared_size_dbias) {\n              shared_size_transpose = shared_size_dbias;\n            }\n          }\n\n          Param param;\n          param.input = reinterpret_cast<const InputType *>(input.data.dptr);\n          param.output_c = reinterpret_cast<OutputType *>(output->data.dptr);\n          param.output_t = reinterpret_cast<OutputType *>(output->columnwise_data.dptr);\n          param.scale_ptr = reinterpret_cast<const ComputeType *>(output->scale.dptr);\n          param.amax = reinterpret_cast<ComputeType *>(output->amax.dptr);\n          param.scale_inv = reinterpret_cast<ComputeType *>(output->scale_inv.dptr);\n          if constexpr (IS_DBIAS) {\n            param.workspace = reinterpret_cast<ComputeType *>(workspace->data.dptr);\n          } if constexpr (IS_DACT) {\n            param.act_input = reinterpret_cast<const InputType2 *>(act_input->data.dptr);\n          }\n\n          // Runtime-compiled tuned kernel\n          if (jit_compiled) {\n            constexpr const char *itype_name = TypeInfo<InputType>::name;\n            constexpr const char *itype2_name = TypeInfo<InputType2>::name;\n            constexpr const char *otype_name = TypeInfo<OutputType>::name;\n\n            int actType = 0;\n            if constexpr (IS_DACT || IS_ACT) {\n              actType = get_activation_type<ComputeType, ParamOP, OP>();\n            }\n\n            // Compile NVRTC kernel if needed and launch\n            auto &rtc_manager = rtc::KernelManager::instance();\n            const std::string kernel_label = concat_strings(\n                \"cast_transpose_fusion\"\n                \",itype=\",\n                itype_name, \",itype2=\", itype2_name, \",otype=\", otype_name,\n                \",load_size=\", load_size, \",store_size=\", store_size, \",IS_DBIAS=\", IS_DBIAS,\n                \",IS_DACT=\", IS_DACT, \",IS_ACT=\", IS_ACT,\n                \",activationType=\", ActTypeToString[actType]);\n\n            if (!rtc_manager.is_compiled(kernel_label)) {\n              std::string code = string_code_transpose_rtc_cast_transpose_fusion_cu;\n              code = regex_replace(code, \"__ITYPE__\", itype_name);\n              code = regex_replace(code, \"__ITYPE2__\", itype2_name);\n              code = regex_replace(code, \"__OTYPE__\", otype_name);\n              code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n              code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n              code = regex_replace(code, \"__WARPS_PER_TILE__\", n_warps_per_tile);\n              code = regex_replace(code, \"__BLOCK_SIZE__\", cast_transpose_num_threads);\n              code = regex_replace(code, \"__IS_DBIAS__\", IS_DBIAS);\n              code = regex_replace(code, \"__IS_DACT__\", IS_DACT);\n              code = regex_replace(code, \"__IS_ACT__\", IS_ACT);\n              code = regex_replace(code, \"__ACTIVATION_TYPE__\", actType);\n\n              rtc_manager.compile(\n                  kernel_label, \"cast_transpose_fusion_kernel_optimized\", code,\n                  \"transformer_engine/common/transpose/rtc/cast_transpose_fusion.cu\");\n            }\n\n            rtc_manager.set_cache_config(kernel_label, hipFuncCachePreferShared);\n\n            rtc_manager.launch(kernel_label, num_blocks, cast_transpose_num_threads,\n                               shared_size_transpose, stream, param, row_length, num_rows,\n                               num_tiles);\n          } else {  // Statically-compiled general kernel\n            constexpr size_t load_size = IS_DACT ? desired_load_size_dact : desired_load_size;\n            constexpr size_t store_size = IS_DACT ? desired_store_size_dact : desired_store_size;\n            constexpr size_t nvec_in = load_size / itype_size;\n            constexpr size_t nvec_out = store_size / otype_size;\n\n            NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n            NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(cast_transpose_fused_kernel_notaligned<IS_DBIAS, IS_DACT, IS_ACT, ComputeType,\n                                                       Param, nvec_in, nvec_out, Empty, OP>),\n                hipFuncAttributePreferredSharedMemoryCarveout, 100));\n            cast_transpose_fused_kernel_notaligned<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Param,\n                                                   nvec_in, nvec_out, Empty, OP>\n                <<<num_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, num_tiles);\n            NVTE_CHECK_CUDA(hipGetLastError());\n          }\n\n          if constexpr (IS_DBIAS) {\n            reduce_dbias<InputType>(*workspace, dbias, row_length, num_rows, nvec_out, stream);\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\ntemplate <int nvec_in, int nvec_out, typename CType, typename IType, typename OType,\n          typename ParamOP, CType (*OP1)(CType, const ParamOP &),\n          CType (*OP2)(CType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    dgated_act_cast_transpose_kernel(const IType *const input, const IType *const act_input,\n                                     OType *const output_c, OType *const output_t,\n                                     const CType *const scale_ptr, CType *const amax,\n                                     CType *const scale_inv, const size_t row_length,\n                                     const size_t num_rows, const size_t num_tiles) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_act_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_gate_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_c_tile_0 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_c_tile_1 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_t_tile_0 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  OType *const my_output_t_tile_1 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP +\n      row_length * num_rows;\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  IVec in[2][nvec_out];\n  IVec act_in[2][nvec_out];\n  IVec gate_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space_0[n_iterations][nvec_in];\n  OVec out_space_1[n_iterations][nvec_in];\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const size_t stride2 = 2 * row_length / nvec_in;\n  size_t current_stride2 = warp_id_in_tile * n_iterations * nvec_out * stride2;\n  CType max = 0;\n  const CType scale = scale_ptr != nullptr ? *scale_ptr : 1;\n\n  CVec partial_dbias;\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n    act_in[0][i].load_from(my_act_input_tile, current_stride2 + my_place + stride2 * i);\n    gate_in[0][i].load_from(my_gate_input_tile, current_stride2 + my_place + stride2 * i);\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride2 + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        in[current_in][j].load_from(my_input_tile,\n                                    current_stride + my_place_in + stride * (nvec_out + j));\n        act_in[current_in][j].load_from(my_act_input_tile,\n                                        current_stride2 + my_place_in + stride2 * (nvec_out + j));\n        gate_in[current_in][j].load_from(my_gate_input_tile,\n                                         current_stride2 + my_place_in + stride2 * (nvec_out + j));\n      }\n    }\n    CVec after_dact[nvec_out];   // NOLINT(*)\n    CVec after_dgate[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        after_dact[j].data.elt[k] = OP1(act_in[current_in ^ 1][j].data.elt[k], {}) *\n                                    CType(in[current_in ^ 1][j].data.elt[k]) *\n                                    CType(gate_in[current_in ^ 1][j].data.elt[k]);\n        after_dgate[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                     OP2(act_in[current_in ^ 1][j].data.elt[k], {});\n      }\n    }\n    OVec out_trans_0[nvec_in];  // NOLINT(*)\n    OVec out_trans_1[nvec_in];  // NOLINT(*)\n\n    constexpr bool IS_DBIAS = false;\n    constexpr bool IS_FULL_TILE = true;\n    constexpr bool valid_store = true;\n    constexpr int dbias_shfl_src_lane = 0;\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_trans_0, partial_dbias,\n                                                    my_output_c_tile_0, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dgate, out_trans_1, partial_dbias,\n                                                    my_output_c_tile_1, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space_0[i][j].data.vec = out_trans_0[j].data.vec;\n      out_space_1[i][j].data.vec = out_trans_1[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_stride2 += nvec_out * stride2;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_0[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_0,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_1[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_1,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax != nullptr) {\n    max = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(max, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax, max);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv != nullptr) {\n    reciprocal<CType>(scale_inv, scale);\n  }\n}\n\ntemplate <int nvec_in, int nvec_out, typename CType, typename IType, typename OType,\n          typename ParamOP, CType (*OP1)(CType, const ParamOP &),\n          CType (*OP2)(CType, const ParamOP &)>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    dgated_act_cast_transpose_kernel_notaligned(const IType *const input,\n                                                const IType *const act_input, OType *const output_c,\n                                                OType *const output_t, const CType *const scale_ptr,\n                                                CType *const amax, CType *const scale_inv,\n                                                const size_t row_length, const size_t num_rows,\n                                                const size_t num_tiles) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_act_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  const IType *const my_gate_input_tile =\n      act_input + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_c_tile_0 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_c_tile_1 =\n      output_c + (tile_id_x * nvec_in + tile_id_y * row_length * 2 * nvec_out) * THREADS_PER_WARP +\n      row_length;\n  OType *const my_output_t_tile_0 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  OType *const my_output_t_tile_1 =\n      output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP +\n      row_length * num_rows;\n  const size_t stride = row_length / nvec_in;\n  const size_t stride2 = 2 * row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  IVec in[2][nvec_out];\n  IVec act_in[2][nvec_out];\n  IVec gate_in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space_0[n_iterations][nvec_in];\n  OVec out_space_1[n_iterations][nvec_in];\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  size_t current_stride2 = warp_id_in_tile * n_iterations * nvec_out * stride2;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  CType max = 0;\n  const CType scale = scale_ptr != nullptr ? *scale_ptr : 1;\n\n  CVec partial_dbias;\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n        act_in[0][i].load_from(my_act_input_tile, current_stride2 + my_place + stride2 * i);\n        gate_in[0][i].load_from(my_gate_input_tile, current_stride2 + my_place + stride2 * i);\n      } else {\n        in[0][i].clear();\n        act_in[0][i].clear();\n        gate_in[0][i].clear();\n      }\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride2 + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      {\n        const bool valid_load =\n            my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n        for (unsigned int j = 0; j < nvec_out; ++j) {\n          if (valid_load) {\n            in[current_in][j].load_from(my_input_tile,\n                                        current_stride + my_place_in + stride * (nvec_out + j));\n            act_in[current_in][j].load_from(\n                my_act_input_tile, current_stride2 + my_place_in + stride2 * (nvec_out + j));\n            gate_in[current_in][j].load_from(\n                my_gate_input_tile, current_stride2 + my_place_in + stride2 * (nvec_out + j));\n          } else {\n            in[current_in][j].clear();\n            act_in[current_in][j].clear();\n            gate_in[current_in][j].clear();\n          }\n        }\n      }\n    }\n    CVec after_dact[nvec_out];   // NOLINT(*)\n    CVec after_dgate[nvec_out];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_out; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < nvec_in; ++k) {\n        after_dact[j].data.elt[k] = OP1(act_in[current_in ^ 1][j].data.elt[k], {}) *\n                                    CType(in[current_in ^ 1][j].data.elt[k]) *\n                                    CType(gate_in[current_in ^ 1][j].data.elt[k]);\n        after_dgate[j].data.elt[k] = CType(in[current_in ^ 1][j].data.elt[k]) *\n                                     OP2(act_in[current_in ^ 1][j].data.elt[k], {});\n      }\n    }\n    OVec out_trans_0[nvec_in];  // NOLINT(*)\n    OVec out_trans_1[nvec_in];  // NOLINT(*)\n\n    constexpr bool IS_DBIAS = false;\n    constexpr bool IS_FULL_TILE = false;\n    constexpr int dbias_shfl_src_lane = 0;\n    const bool valid_store =\n        (my_place < tile_length) && (warp_id_in_tile * n_iterations + i < tile_height);\n\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dact, out_trans_0, partial_dbias,\n                                                    my_output_c_tile_0, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n    cast_and_transpose_regs<IS_DBIAS, IS_FULL_TILE>(after_dgate, out_trans_1, partial_dbias,\n                                                    my_output_c_tile_1, current_place, stride2,\n                                                    scale, max, dbias_shfl_src_lane, valid_store);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space_0[i][j].data.vec = out_trans_0[j].data.vec;\n      out_space_1[i][j].data.vec = out_trans_1[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n    current_stride2 += nvec_out * stride2;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_0[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_0,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space_1[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile_1,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax != nullptr) {\n    max = reduce_max<cast_transpose_num_threads / THREADS_PER_WARP>(max, warp_id);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax, max);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv != nullptr) {\n    reciprocal<CType>(scale_inv, scale);\n  }\n}\n\ntemplate <typename ComputeType, typename ParamOP, ComputeType (*OP1)(ComputeType, const ParamOP &),\n          ComputeType (*OP2)(ComputeType, const ParamOP &)>\nvoid dgated_act_cast_transpose(const Tensor &input, const Tensor &gated_act_input, Tensor *output,\n                               hipStream_t stream) {\n  CheckInputTensor(input, \"dgated_act_cast_transpose_input\");\n  CheckInputTensor(gated_act_input, \"dgated_act_cast_transpose_gated_act_input\");\n  CheckOutputTensor(*output, \"dgated_act_cast_transpose_output\");\n\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(gated_act_input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(output->has_data() && output->has_columnwise_data(),\n             \"Both rowwise and columnwise data need to be allocated.\");\n  NVTE_CHECK(output->data.shape.size() == 2, \"C output must have 2 dimensions.\");\n  NVTE_CHECK(output->columnwise_data.shape.size() == 2, \"T output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(gated_act_input.data.shape[0] == num_rows, \"Wrong dimension of output.\");\n  NVTE_CHECK(gated_act_input.data.shape[1] == row_length * 2, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->data.shape[0] == num_rows, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->data.shape[1] == row_length * 2, \"Wrong dimension of output.\");\n  NVTE_CHECK(output->columnwise_data.shape[0] == row_length * 2, \"Wrong dimension of T output.\");\n  NVTE_CHECK(output->columnwise_data.shape[1] == num_rows, \"Wrong dimension of T output.\");\n\n  NVTE_CHECK(input.data.dtype == gated_act_input.data.dtype, \"Types of both inputs must match.\");\n\n  NVTE_CHECK(output->data.dtype == output->columnwise_data.dtype,\n             \"C and T outputs need to have the same type.\");\n  NVTE_CHECK(output->scale_inv.dptr == output->columnwise_scale_inv.dptr,\n             \"C and T outputs need to share scale inverse tensor.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output->dtype(), OutputType, using InputType2 = InputType;\n          /* dact fusion kernel uses more registers */\n          constexpr int desired_load_size_dact = 4;\n          constexpr int desired_store_size_dact = 4; constexpr int itype_size = sizeof(InputType);\n          constexpr int otype_size = sizeof(OutputType);\n          constexpr int nvec_in = desired_load_size_dact / itype_size;\n          constexpr int nvec_out = desired_store_size_dact / otype_size;\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n          const size_t n_tiles =\n              DIVUP(row_length, static_cast<size_t>(nvec_in * THREADS_PER_WARP)) *\n              DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n          const size_t n_warps_per_block = cast_transpose_num_threads / THREADS_PER_WARP;\n          const size_t n_blocks = DIVUP(n_tiles * n_warps_per_tile, n_warps_per_block);\n\n          const bool full_tile = row_length % (nvec_in * THREADS_PER_WARP) == 0 &&\n                                 num_rows % (nvec_out * THREADS_PER_WARP) == 0;\n          const size_t shmem_size = cast_transpose_num_threads / n_warps_per_tile *\n                                    (THREADS_PER_WARP + 1) * sizeof(Vec<OutputType, nvec_out>);\n          if (full_tile) {\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(dgated_act_cast_transpose_kernel<nvec_in, nvec_out, ComputeType, InputType,\n                                                 OutputType, Empty, OP1, OP2>),\n                hipFuncAttributePreferredSharedMemoryCarveout, 100));\n\n            dgated_act_cast_transpose_kernel<nvec_in, nvec_out, ComputeType, InputType, OutputType,\n                                             Empty, OP1, OP2>\n                <<<n_blocks, cast_transpose_num_threads, shmem_size, stream>>>(\n                    reinterpret_cast<const InputType *>(input.data.dptr),\n                    reinterpret_cast<const InputType *>(gated_act_input.data.dptr),\n                    reinterpret_cast<OutputType *>(output->data.dptr),\n                    reinterpret_cast<OutputType *>(output->columnwise_data.dptr),\n                    reinterpret_cast<const fp32 *>(output->scale.dptr),\n                    reinterpret_cast<fp32 *>(output->amax.dptr),\n                    reinterpret_cast<fp32 *>(output->scale_inv.dptr), row_length, num_rows,\n                    n_tiles);\n            NVTE_CHECK_CUDA(hipGetLastError());\n          } else {\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(dgated_act_cast_transpose_kernel_notaligned<nvec_in, nvec_out, ComputeType,\n                                                            InputType, OutputType, Empty, OP1, OP2>),\n                hipFuncAttributePreferredSharedMemoryCarveout, 100));\n            dgated_act_cast_transpose_kernel_notaligned<nvec_in, nvec_out, ComputeType, InputType,\n                                                        OutputType, Empty, OP1, OP2>\n                <<<n_blocks, cast_transpose_num_threads, shmem_size, stream>>>(\n                    reinterpret_cast<const InputType *>(input.data.dptr),\n                    reinterpret_cast<const InputType *>(gated_act_input.data.dptr),\n                    reinterpret_cast<OutputType *>(output->data.dptr),\n                    reinterpret_cast<OutputType *>(output->columnwise_data.dptr),\n                    reinterpret_cast<const fp32 *>(output->scale.dptr),\n                    reinterpret_cast<fp32 *>(output->amax.dptr),\n                    reinterpret_cast<fp32 *>(output->scale_inv.dptr), row_length, num_rows,\n                    n_tiles);\n            NVTE_CHECK_CUDA(hipGetLastError());\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\n// Explicit template instantiation\ntemplate void cast_transpose_fused<true, false, false, float, transformer_engine::Empty, nullptr>(\n    const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, hipStream_t);\n#define NVTE_INSTANTIATE_ACTIVATION(op)                                                    \\\n  template void cast_transpose_fused<false, false, true, float, transformer_engine::Empty, \\\n                                     transformer_engine::op<float, float>>(                \\\n      const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, hipStream_t);         \\\n  template void cast_transpose_fused<false, true, false, float, transformer_engine::Empty, \\\n                                     transformer_engine::d##op<float, float>>(             \\\n      const Tensor &, const Tensor *, Tensor *, Tensor *, Tensor *, hipStream_t);\nNVTE_INSTANTIATE_ACTIVATION(relu);\nNVTE_INSTANTIATE_ACTIVATION(srelu);\nNVTE_INSTANTIATE_ACTIVATION(gelu);\nNVTE_INSTANTIATE_ACTIVATION(qgelu);\nNVTE_INSTANTIATE_ACTIVATION(silu);\n#undef NVTE_INSTANTIATE_ACTIVATION\n\n}  // namespace detail\n\n}  // namespace transformer_engine\n\nusing ComputeType = typename transformer_engine::fp32;\n\nvoid nvte_cast_transpose_dbias(const NVTETensor input, NVTETensor output, NVTETensor dbias,\n                               NVTETensor workspace, hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = false;\n  constexpr bool IS_ACT = false;\n\n  constexpr const NVTETensor activation_input = nullptr;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, nullptr>(\n      *convertNVTETensorCheck(input), convertNVTETensor(activation_input),\n      convertNVTETensor(output), convertNVTETensor(dbias), convertNVTETensor(workspace), stream);\n}\n\nvoid nvte_cast_transpose_dbias_dgelu(const NVTETensor input, const NVTETensor act_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dgelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(act_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dsilu(const NVTETensor input, const NVTETensor silu_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dsilu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dsilu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(silu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_drelu(const NVTETensor input, const NVTETensor relu_input,\n                                     NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                     hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_drelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, drelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(relu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dsrelu(const NVTETensor input, const NVTETensor srelu_input,\n                                      NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                      hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dsrelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dsrelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(srelu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_cast_transpose_dbias_dqgelu(const NVTETensor input, const NVTETensor qgelu_input,\n                                      NVTETensor output, NVTETensor dbias, NVTETensor workspace,\n                                      hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_dbias_dqgelu);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  constexpr bool IS_DBIAS = true;\n  constexpr bool IS_DACT = true;\n  constexpr bool IS_ACT = false;\n\n  cast_transpose_fused<IS_DBIAS, IS_DACT, IS_ACT, ComputeType, Empty, dqgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), convertNVTETensorCheck(qgelu_input),\n      convertNVTETensorCheck(output), convertNVTETensorCheck(dbias), convertNVTETensor(workspace),\n      stream);\n}\n\nvoid nvte_dgeglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dgeglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dgelu<fp32, fp32>, gelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dswiglu_cast_transpose(const NVTETensor input, const NVTETensor swiglu_input,\n                                 NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dswiglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dsilu<fp32, fp32>, silu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(swiglu_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dreglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dreglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, drelu<fp32, fp32>, relu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dsreglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                 NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dsreglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dsrelu<fp32, fp32>, srelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n\nvoid nvte_dqgeglu_cast_transpose(const NVTETensor input, const NVTETensor gated_act_input,\n                                 NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dqgeglu_cast_transpose);\n  using namespace transformer_engine;\n  using namespace transformer_engine::detail;\n\n  dgated_act_cast_transpose<ComputeType, Empty, dqgelu<fp32, fp32>, qgelu<fp32, fp32>>(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(gated_act_input),\n      convertNVTETensorCheck(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/cast_transpose_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <functional>\n#include <numeric>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <int nvec_in, int nvec_out, typename IVec, typename OVec, typename CVec, typename CType>\ninline __device__ void transpose_regs_partial_dbias(const IVec (&in)[nvec_out],\n                                                    OVec (&out_trans)[nvec_in],\n                                                    CVec &out_dbias,  // NOLINT(*)\n                                                    const CType scale_inv,\n                                                    const int dbias_shfl_src_lane) {\n  using T = typename OVec::type;\n  using OVecC = Vec<T, nvec_in>;\n\n  CVec step_dbias;\n  step_dbias.clear();\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      const CType tmp = static_cast<CType>(in[i].data.elt[j]) * scale_inv;\n      const T elt_o = in[i].data.elt[j];\n\n      /* dbias: thread tile local accumulation */\n      step_dbias.data.elt[j] += tmp;\n\n      out_trans[j].data.elt[i] = elt_o;  // thread tile transpose\n    }\n  }\n\n#pragma unroll\n  for (unsigned int j = 0; j < nvec_in; ++j) {\n    CType elt = step_dbias.data.elt[j];\n    elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in warp\n    out_dbias.data.elt[j] += elt;\n  }\n}\n\n// STUFF TO TUNE\nconstexpr unsigned int n_warps_per_tile = 4;\nconstexpr int desired_load_size = 8;\nconstexpr int desired_store_size = 8;\n\nconstexpr unsigned int max_threads_per_block = 256;\nstatic_assert(n_warps_per_tile * THREADS_PER_WARP <= max_threads_per_block);\nconstexpr unsigned int cast_transpose_num_threads = n_warps_per_tile * THREADS_PER_WARP;\n\nnamespace {\n\ntemplate <typename IType, typename OType, typename CType>\nstruct TDBiasParam {\n  using InputType = IType;\n  using OutputType = OType;\n  using ComputeType = CType;\n  const IType *input;\n  OType *output_t;\n  const CType *scale_inv;\n  CType *workspace;\n};\n\n}  // namespace\n\ntemplate <int nvec_in, int nvec_out, typename Param>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    transpose_dbias_kernel(const Param param, const size_t row_length, const size_t num_rows,\n                           const size_t num_tiles) {\n  using IType = typename Param::InputType;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (nvec_in * THREADS_PER_WARP);\n  // const size_t num_tiles_y = num_rows / (nvec * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      param.input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_t_tile =\n      param.output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n  CVec partial_dbias;\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const CType scale_inv = param.scale_inv != nullptr ? *param.scale_inv : 1;\n\n  partial_dbias.clear();\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        in[current_in][j].load_from(my_input_tile,\n                                    current_stride + my_place_in + stride * (nvec_out + j));\n      }\n    }\n    OVec out_trans[nvec_in];  // NOLINT(*)\n    transpose_regs_partial_dbias(\n        in[current_in ^ 1], out_trans, partial_dbias, scale_inv,\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space[i][j].data.vec = out_trans[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  my_dbias_scratch[threadIdx.x] = partial_dbias;\n  __syncthreads();\n  // TODO(ptredak): check if the regular reduction is better\n  if (warp_id_in_tile == 0) {\n#pragma unroll\n    for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n      CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_in; ++j) {\n        partial_dbias.data.elt[j] += tmp.data.elt[j];\n      }\n    }\n\n    partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n  }\n}\n\ntemplate <int nvec_in, int nvec_out, typename Param>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    transpose_dbias_kernel_notaligned(const Param param, const size_t row_length,\n                                      const size_t num_rows, const size_t num_tiles) {\n  using IType = typename Param::InputType;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      param.input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_t_tile =\n      param.output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n  CVec partial_dbias;\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const CType scale_inv = param.scale_inv != nullptr ? *param.scale_inv : 1;\n\n  partial_dbias.clear();\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n      } else {\n        in[0][i].clear();\n      }\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      const bool valid_load =\n          my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        if (valid_load) {\n          in[current_in][j].load_from(my_input_tile,\n                                      current_stride + my_place_in + stride * (nvec_out + j));\n        } else {\n          in[current_in][j].clear();\n        }\n      }\n    }\n    OVec out_trans[nvec_in];  // NOLINT(*)\n    transpose_regs_partial_dbias(\n        in[current_in ^ 1], out_trans, partial_dbias, scale_inv,\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space[i][j].data.vec = out_trans[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  my_dbias_scratch[threadIdx.x] = partial_dbias;\n  __syncthreads();\n  // TODO(ptredak): check if the regular reduction is better\n  if (warp_id_in_tile == 0) {\n#pragma unroll\n    for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n      CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_in; ++j) {\n        partial_dbias.data.elt[j] += tmp.data.elt[j];\n      }\n    }\n\n    if (my_id_in_warp < tile_length) {\n      partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n    }\n  }\n}\n\nconstexpr size_t reduce_dbias_num_threads = 256;\n\ntemplate <int nvec, typename ComputeType, typename OutputType>\n__global__ void __launch_bounds__(reduce_dbias_num_threads)\n    reduce_dbias_kernel(OutputType *const dbias_output, const ComputeType *const dbias_partial,\n                        const int row_length, const int num_rows) {\n  using ComputeVec = Vec<ComputeType, nvec>;\n  using OutputVec = Vec<OutputType, nvec>;\n\n  const int thread_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (thread_id * nvec >= row_length) return;\n\n  const ComputeType *const thread_in_base = dbias_partial + thread_id * nvec;\n  OutputType *const thread_out_base = dbias_output + thread_id * nvec;\n\n  const int stride_in_vec = row_length / nvec;\n\n  ComputeVec ldg_vec;\n  ComputeVec acc_vec;\n  acc_vec.clear();\n  for (int i = 0; i < num_rows; ++i) {\n    ldg_vec.load_from(thread_in_base, i * stride_in_vec);\n#pragma unroll\n    for (int e = 0; e < nvec; ++e) {\n      acc_vec.data.elt[e] += ldg_vec.data.elt[e];\n    }\n  }\n\n  OutputVec stg_vec;\n#pragma unroll\n  for (int e = 0; e < nvec; ++e) {\n    stg_vec.data.elt[e] = OutputType(acc_vec.data.elt[e]);\n  }\n  stg_vec.store_to(thread_out_base, 0);\n}\n\nvoid populate_transpose_dbias_workspace_config(const Tensor &input, /*cast*/\n                                               Tensor *workspace, const int nvec_out) {\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  const size_t tile_size_y = (nvec_out * THREADS_PER_WARP);\n  NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n  const size_t num_rows_partial_dbias = DIVUP(num_rows, tile_size_y);\n\n  if (workspace->data.dptr == nullptr) {\n    // Set workspace size\n    workspace->data.shape = {num_rows_partial_dbias, row_length};\n    workspace->data.dtype = DType::kFloat32;\n  } else {\n    // Check that workspace matches expected size\n    const size_t workspace_size = get_buffer_size_bytes(\n        std::accumulate(workspace->data.shape.begin(), workspace->data.shape.end(), 1,\n                        std::multiplies<size_t>()),\n        workspace->data.dtype);\n    const size_t required_size =\n        get_buffer_size_bytes(num_rows_partial_dbias, row_length, DType::kFloat32);\n    NVTE_CHECK(workspace_size >= required_size, \"Invalid workspace (expected dims=(\",\n               num_rows_partial_dbias, \",\", row_length, \"), dtype=\", to_string(DType::kFloat32),\n               \"; found dims=\", workspace->data.shape,\n               \", dtype=\", typeToNumBits(workspace->data.dtype), \" bits)\");\n  }\n}\n\ntemplate <typename BiasType>\nvoid reduce_dbias(const Tensor &workspace, Tensor *dbias, const size_t row_length,\n                  const size_t num_rows, const int nvec_out, cudaStream_t stream) {\n  constexpr int reduce_dbias_store_bytes = 8;  // stg.64\n  constexpr int reduce_dbias_nvec = reduce_dbias_store_bytes / sizeof(BiasType);\n\n  NVTE_CHECK(row_length % reduce_dbias_nvec == 0, \"Unsupported shape.\");\n\n  const size_t reduce_dbias_row_length = row_length;\n  const size_t reduce_dbias_num_rows =\n      DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n  const size_t reduce_dbias_num_blocks =\n      DIVUP(row_length, reduce_dbias_num_threads * reduce_dbias_nvec);\n\n  reduce_dbias_kernel<reduce_dbias_nvec, fp32, BiasType>\n      <<<reduce_dbias_num_blocks, reduce_dbias_num_threads, 0, stream>>>(\n          reinterpret_cast<BiasType *>(dbias->data.dptr),\n          reinterpret_cast<const fp32 *>(workspace.data.dptr), reduce_dbias_row_length,\n          reduce_dbias_num_rows);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fp8_transpose_dbias(const Tensor &input, Tensor *transposed_output, Tensor *dbias,\n                         Tensor *workspace, cudaStream_t stream) {\n  CheckInputTensor(input, \"fp8_transpose_dbias_input\");\n  CheckOutputTensor(*transposed_output, \"transposed_output\");\n  CheckOutputTensor(*dbias, \"dbias\");\n\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(transposed_output->data.shape.size() == 2, \"T output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(transposed_output->data.shape[0] == row_length, \"Wrong dimension of T output.\");\n  NVTE_CHECK(transposed_output->data.shape[1] == num_rows, \"Wrong dimension of T output.\");\n\n  NVTE_CHECK(transposed_output->data.dtype == input.data.dtype,\n             \"T output must have the same type as input.\");\n  NVTE_CHECK(dbias->data.shape == std::vector<size_t>{row_length}, \"Wrong shape of DBias.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      dbias->data.dtype, BiasType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          input.data.dtype, Type, constexpr int type_size = sizeof(Type);\n          constexpr int nvec_in = desired_load_size / type_size;\n          constexpr int nvec_out = desired_store_size / type_size;\n\n          // Check workspace size\n          populate_transpose_dbias_workspace_config(input, workspace, nvec_out);\n          if (workspace->data.dptr == nullptr) { return; }\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n          const size_t n_tiles =\n              DIVUP(row_length, static_cast<size_t>(nvec_in * THREADS_PER_WARP)) *\n              DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n          const size_t n_warps_per_block = cast_transpose_num_threads / THREADS_PER_WARP;\n          const size_t n_blocks = DIVUP(n_tiles * n_warps_per_tile, n_warps_per_block);\n\n          const bool full_tile = row_length % (nvec_in * THREADS_PER_WARP) == 0 &&\n                                 num_rows % (nvec_out * THREADS_PER_WARP) == 0;\n\n          using ComputeType = fp32; constexpr size_t shared_size_transpose =\n                                        cast_transpose_num_threads / n_warps_per_tile *\n                                        (THREADS_PER_WARP + 1) * sizeof(Vec<Type, nvec_out>);\n          constexpr size_t shared_size_dbias =\n              cast_transpose_num_threads * sizeof(Vec<ComputeType, nvec_in>);\n          static_assert(shared_size_transpose >= shared_size_dbias);\n          using Param = TDBiasParam<Type, Type, ComputeType>; Param param;\n          param.input = reinterpret_cast<const Type *>(input.data.dptr);\n          param.output_t = reinterpret_cast<Type *>(transposed_output->data.dptr);\n          param.scale_inv =\n              reinterpret_cast<const ComputeType *>(transposed_output->scale_inv.dptr);\n          param.workspace = reinterpret_cast<ComputeType *>(workspace->data.dptr);\n\n          if (full_tile) {\n            NVTE_CHECK_CUDA(cudaFuncSetAttribute(transpose_dbias_kernel<nvec_in, nvec_out, Param>,\n                                                 cudaFuncAttributePreferredSharedMemoryCarveout,\n                                                 100));\n            transpose_dbias_kernel<nvec_in, nvec_out, Param>\n                <<<n_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, n_tiles);\n            NVTE_CHECK_CUDA(cudaGetLastError());\n          } else {\n            NVTE_CHECK_CUDA(\n                cudaFuncSetAttribute(transpose_dbias_kernel_notaligned<nvec_in, nvec_out, Param>,\n                                     cudaFuncAttributePreferredSharedMemoryCarveout, 100));\n            transpose_dbias_kernel_notaligned<nvec_in, nvec_out, Param>\n                <<<n_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, n_tiles);\n            NVTE_CHECK_CUDA(cudaGetLastError());\n          }\n\n          reduce_dbias<BiasType>(*workspace, dbias, row_length, num_rows, nvec_out,\n                                 stream););  // NOLINT(*)\n  );                                         // NOLINT(*)\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_fp8_transpose_dbias(const NVTETensor input, NVTETensor transposed_output,\n                              NVTETensor dbias, NVTETensor workspace, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_transpose_dbias);\n  using namespace transformer_engine;\n  fp8_transpose_dbias(*convertNVTETensorCheck(input), convertNVTETensor(transposed_output),\n                      convertNVTETensor(dbias), convertNVTETensor(workspace), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <functional>\n#include <numeric>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <int nvec_in, int nvec_out, typename IVec, typename OVec, typename CVec, typename CType>\ninline __device__ void transpose_regs_partial_dbias(const IVec (&in)[nvec_out],\n                                                    OVec (&out_trans)[nvec_in],\n                                                    CVec &out_dbias,  // NOLINT(*)\n                                                    const CType scale_inv,\n                                                    const int dbias_shfl_src_lane) {\n  using T = typename OVec::type;\n  using OVecC = Vec<T, nvec_in>;\n\n  CVec step_dbias;\n  step_dbias.clear();\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      const CType tmp = static_cast<CType>(in[i].data.elt[j]) * scale_inv;\n      const T elt_o = in[i].data.elt[j];\n\n      /* dbias: thread tile local accumulation */\n      step_dbias.data.elt[j] += tmp;\n\n      out_trans[j].data.elt[i] = elt_o;  // thread tile transpose\n    }\n  }\n\n#pragma unroll\n  for (unsigned int j = 0; j < nvec_in; ++j) {\n    CType elt = step_dbias.data.elt[j];\n    elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in warp\n    out_dbias.data.elt[j] += elt;\n  }\n}\n\n// STUFF TO TUNE\nconstexpr unsigned int n_warps_per_tile = 4;\nconstexpr int desired_load_size = 8;\nconstexpr int desired_store_size = 8;\n\nconstexpr unsigned int max_threads_per_block = 256;\nstatic_assert(n_warps_per_tile * THREADS_PER_WARP <= max_threads_per_block);\nconstexpr unsigned int cast_transpose_num_threads = n_warps_per_tile * THREADS_PER_WARP;\n\nnamespace {\n\ntemplate <typename IType, typename OType, typename CType>\nstruct TDBiasParam {\n  using InputType = IType;\n  using OutputType = OType;\n  using ComputeType = CType;\n  const IType *input;\n  OType *output_t;\n  const CType *scale_inv;\n  CType *workspace;\n};\n\n}  // namespace\n\ntemplate <int nvec_in, int nvec_out, typename Param>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    transpose_dbias_kernel(const Param param, const size_t row_length, const size_t num_rows,\n                           const size_t num_tiles) {\n  using IType = typename Param::InputType;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (nvec_in * THREADS_PER_WARP);\n  // const size_t num_tiles_y = num_rows / (nvec * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      param.input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_t_tile =\n      param.output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n  CVec partial_dbias;\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const CType scale_inv = param.scale_inv != nullptr ? *param.scale_inv : 1;\n\n  partial_dbias.clear();\n\n#pragma unroll\n  for (unsigned int i = 0; i < nvec_out; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        in[current_in][j].load_from(my_input_tile,\n                                    current_stride + my_place_in + stride * (nvec_out + j));\n      }\n    }\n    OVec out_trans[nvec_in];  // NOLINT(*)\n    transpose_regs_partial_dbias(\n        in[current_in ^ 1], out_trans, partial_dbias, scale_inv,\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space[i][j].data.vec = out_trans[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  my_dbias_scratch[threadIdx.x] = partial_dbias;\n  __syncthreads();\n  // TODO(ptredak): check if the regular reduction is better\n  if (warp_id_in_tile == 0) {\n#pragma unroll\n    for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n      CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_in; ++j) {\n        partial_dbias.data.elt[j] += tmp.data.elt[j];\n      }\n    }\n\n    partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n  }\n}\n\ntemplate <int nvec_in, int nvec_out, typename Param>\n__global__ void __launch_bounds__(cast_transpose_num_threads)\n    transpose_dbias_kernel_notaligned(const Param param, const size_t row_length,\n                                      const size_t num_rows, const size_t num_tiles) {\n  using IType = typename Param::InputType;\n  using OType = typename Param::OutputType;\n  using CType = typename Param::ComputeType;\n  using IVec = Vec<IType, nvec_in>;\n  using OVec = Vec<OType, nvec_out>;\n  using CVec = Vec<CType, nvec_in>;\n\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x =\n      (row_length + nvec_in * THREADS_PER_WARP - 1) / (nvec_in * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * n_warps_per_tile) + warp_id / n_warps_per_tile;\n  if (tile_id >= num_tiles) return;\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const IType *const my_input_tile =\n      param.input + (tile_id_x * nvec_in + tile_id_y * row_length * nvec_out) * THREADS_PER_WARP;\n  OType *const my_output_t_tile =\n      param.output_t + (tile_id_y * nvec_out + tile_id_x * num_rows * nvec_in) * THREADS_PER_WARP;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (nvec_in * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  const size_t stride = row_length / nvec_in;\n  const size_t output_stride = num_rows / nvec_out;\n  const size_t row_length_rest = stride - tile_id_x * THREADS_PER_WARP;\n  const size_t row_height_rest = output_stride - tile_id_y * THREADS_PER_WARP;\n  const unsigned int tile_length =\n      row_length_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_length_rest;\n  const unsigned int tile_height =\n      row_height_rest > THREADS_PER_WARP ? THREADS_PER_WARP : row_height_rest;\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / n_warps_per_tile * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][nvec_out];\n  const unsigned int warp_id_in_tile = warp_id % n_warps_per_tile;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n  OVec out_space[n_iterations][nvec_in];\n  CVec partial_dbias;\n\n  size_t current_stride = warp_id_in_tile * n_iterations * nvec_out * stride;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n  const CType scale_inv = param.scale_inv != nullptr ? *param.scale_inv : 1;\n\n  partial_dbias.clear();\n\n  {\n    const bool valid_load = my_place < tile_length && warp_id_in_tile * n_iterations < tile_height;\n#pragma unroll\n    for (unsigned int i = 0; i < nvec_out; ++i) {\n      if (valid_load) {\n        in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n      } else {\n        in[0][i].clear();\n      }\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n      const bool valid_load =\n          my_place_in < tile_length && warp_id_in_tile * n_iterations + i + 1 < tile_height;\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_out; ++j) {\n        if (valid_load) {\n          in[current_in][j].load_from(my_input_tile,\n                                      current_stride + my_place_in + stride * (nvec_out + j));\n        } else {\n          in[current_in][j].clear();\n        }\n      }\n    }\n    OVec out_trans[nvec_in];  // NOLINT(*)\n    transpose_regs_partial_dbias(\n        in[current_in ^ 1], out_trans, partial_dbias, scale_inv,\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP);\n\n#pragma unroll\n    for (unsigned int j = 0; j < nvec_in; ++j) {\n      out_space[i][j].data.vec = out_trans[j].data.vec;\n    }\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += nvec_out * stride;\n  }\n\n  for (unsigned int i = 0; i < nvec_in; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * nvec_in;\n    for (unsigned int j = 0; warp_id_in_tile * n_iterations + j < tile_length; ++j) {\n      const bool valid_store = my_place < tile_height;\n      if (valid_store) {\n        my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                                current_stride + my_place);\n      }\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * nvec_in;\n    }\n    __syncthreads();\n  }\n\n  my_dbias_scratch[threadIdx.x] = partial_dbias;\n  __syncthreads();\n  // TODO(ptredak): check if the regular reduction is better\n  if (warp_id_in_tile == 0) {\n#pragma unroll\n    for (unsigned int i = 1; i < n_warps_per_tile; ++i) {\n      CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n      for (unsigned int j = 0; j < nvec_in; ++j) {\n        partial_dbias.data.elt[j] += tmp.data.elt[j];\n      }\n    }\n\n    if (my_id_in_warp < tile_length) {\n      partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n    }\n  }\n}\n\nconstexpr size_t reduce_dbias_num_threads = 256;\n\ntemplate <int nvec, typename ComputeType, typename OutputType>\n__global__ void __launch_bounds__(reduce_dbias_num_threads)\n    reduce_dbias_kernel(OutputType *const dbias_output, const ComputeType *const dbias_partial,\n                        const int row_length, const int num_rows) {\n  using ComputeVec = Vec<ComputeType, nvec>;\n  using OutputVec = Vec<OutputType, nvec>;\n\n  const int thread_id = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (thread_id * nvec >= row_length) return;\n\n  const ComputeType *const thread_in_base = dbias_partial + thread_id * nvec;\n  OutputType *const thread_out_base = dbias_output + thread_id * nvec;\n\n  const int stride_in_vec = row_length / nvec;\n\n  ComputeVec ldg_vec;\n  ComputeVec acc_vec;\n  acc_vec.clear();\n  for (int i = 0; i < num_rows; ++i) {\n    ldg_vec.load_from(thread_in_base, i * stride_in_vec);\n#pragma unroll\n    for (int e = 0; e < nvec; ++e) {\n      acc_vec.data.elt[e] += ldg_vec.data.elt[e];\n    }\n  }\n\n  OutputVec stg_vec;\n#pragma unroll\n  for (int e = 0; e < nvec; ++e) {\n    stg_vec.data.elt[e] = OutputType(acc_vec.data.elt[e]);\n  }\n  stg_vec.store_to(thread_out_base, 0);\n}\n\nvoid populate_transpose_dbias_workspace_config(const Tensor &input, /*cast*/\n                                               Tensor *workspace, const int nvec_out) {\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  const size_t tile_size_y = (nvec_out * THREADS_PER_WARP);\n  NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n\n  const size_t num_rows_partial_dbias = DIVUP(num_rows, tile_size_y);\n\n  if (workspace->data.dptr == nullptr) {\n    // Set workspace size\n    workspace->data.shape = {num_rows_partial_dbias, row_length};\n    workspace->data.dtype = DType::kFloat32;\n  } else {\n    // Check that workspace matches expected size\n    const size_t workspace_size = get_buffer_size_bytes(\n        std::accumulate(workspace->data.shape.begin(), workspace->data.shape.end(), 1,\n                        std::multiplies<size_t>()),\n        workspace->data.dtype);\n    const size_t required_size =\n        get_buffer_size_bytes(num_rows_partial_dbias, row_length, DType::kFloat32);\n    NVTE_CHECK(workspace_size >= required_size, \"Invalid workspace (expected dims=(\",\n               num_rows_partial_dbias, \",\", row_length, \"), dtype=\", to_string(DType::kFloat32),\n               \"; found dims=\", workspace->data.shape,\n               \", dtype=\", typeToNumBits(workspace->data.dtype), \" bits)\");\n  }\n}\n\ntemplate <typename BiasType>\nvoid reduce_dbias(const Tensor &workspace, Tensor *dbias, const size_t row_length,\n                  const size_t num_rows, const int nvec_out, hipStream_t stream) {\n  constexpr int reduce_dbias_store_bytes = 8;  // stg.64\n  constexpr int reduce_dbias_nvec = reduce_dbias_store_bytes / sizeof(BiasType);\n\n  NVTE_CHECK(row_length % reduce_dbias_nvec == 0, \"Unsupported shape.\");\n\n  const size_t reduce_dbias_row_length = row_length;\n  const size_t reduce_dbias_num_rows =\n      DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n  const size_t reduce_dbias_num_blocks =\n      DIVUP(row_length, reduce_dbias_num_threads * reduce_dbias_nvec);\n\n  reduce_dbias_kernel<reduce_dbias_nvec, fp32, BiasType>\n      <<<reduce_dbias_num_blocks, reduce_dbias_num_threads, 0, stream>>>(\n          reinterpret_cast<BiasType *>(dbias->data.dptr),\n          reinterpret_cast<const fp32 *>(workspace.data.dptr), reduce_dbias_row_length,\n          reduce_dbias_num_rows);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fp8_transpose_dbias(const Tensor &input, Tensor *transposed_output, Tensor *dbias,\n                         Tensor *workspace, hipStream_t stream) {\n  CheckInputTensor(input, \"fp8_transpose_dbias_input\");\n  CheckOutputTensor(*transposed_output, \"transposed_output\");\n  CheckOutputTensor(*dbias, \"dbias\");\n\n  NVTE_CHECK(input.data.shape.size() == 2, \"Input must have 2 dimensions.\");\n  NVTE_CHECK(transposed_output->data.shape.size() == 2, \"T output must have 2 dimensions.\");\n  const size_t row_length = input.data.shape[1];\n  const size_t num_rows = input.data.shape[0];\n\n  NVTE_CHECK(transposed_output->data.shape[0] == row_length, \"Wrong dimension of T output.\");\n  NVTE_CHECK(transposed_output->data.shape[1] == num_rows, \"Wrong dimension of T output.\");\n\n  NVTE_CHECK(transposed_output->data.dtype == input.data.dtype,\n             \"T output must have the same type as input.\");\n  NVTE_CHECK(dbias->data.shape == std::vector<size_t>{row_length}, \"Wrong shape of DBias.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      dbias->data.dtype, BiasType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          input.data.dtype, Type, constexpr int type_size = sizeof(Type);\n          constexpr int nvec_in = desired_load_size / type_size;\n          constexpr int nvec_out = desired_store_size / type_size;\n\n          // Check workspace size\n          populate_transpose_dbias_workspace_config(input, workspace, nvec_out);\n          if (workspace->data.dptr == nullptr) { return; }\n\n          NVTE_CHECK(row_length % nvec_in == 0, \"Unsupported shape.\");\n          NVTE_CHECK(num_rows % nvec_out == 0, \"Unsupported shape.\");\n          const size_t n_tiles =\n              DIVUP(row_length, static_cast<size_t>(nvec_in * THREADS_PER_WARP)) *\n              DIVUP(num_rows, static_cast<size_t>(nvec_out * THREADS_PER_WARP));\n          const size_t n_warps_per_block = cast_transpose_num_threads / THREADS_PER_WARP;\n          const size_t n_blocks = DIVUP(n_tiles * n_warps_per_tile, n_warps_per_block);\n\n          const bool full_tile = row_length % (nvec_in * THREADS_PER_WARP) == 0 &&\n                                 num_rows % (nvec_out * THREADS_PER_WARP) == 0;\n\n          using ComputeType = fp32; constexpr size_t shared_size_transpose =\n                                        cast_transpose_num_threads / n_warps_per_tile *\n                                        (THREADS_PER_WARP + 1) * sizeof(Vec<Type, nvec_out>);\n          constexpr size_t shared_size_dbias =\n              cast_transpose_num_threads * sizeof(Vec<ComputeType, nvec_in>);\n          static_assert(shared_size_transpose >= shared_size_dbias);\n          using Param = TDBiasParam<Type, Type, ComputeType>; Param param;\n          param.input = reinterpret_cast<const Type *>(input.data.dptr);\n          param.output_t = reinterpret_cast<Type *>(transposed_output->data.dptr);\n          param.scale_inv =\n              reinterpret_cast<const ComputeType *>(transposed_output->scale_inv.dptr);\n          param.workspace = reinterpret_cast<ComputeType *>(workspace->data.dptr);\n\n          if (full_tile) {\n            NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(transpose_dbias_kernel<nvec_in, nvec_out, Param>),\n                                                 hipFuncAttributePreferredSharedMemoryCarveout,\n                                                 100));\n            transpose_dbias_kernel<nvec_in, nvec_out, Param>\n                <<<n_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, n_tiles);\n            NVTE_CHECK_CUDA(hipGetLastError());\n          } else {\n            NVTE_CHECK_CUDA(\n                hipFuncSetAttribute(reinterpret_cast<const void*>(transpose_dbias_kernel_notaligned<nvec_in, nvec_out, Param>),\n                                     hipFuncAttributePreferredSharedMemoryCarveout, 100));\n            transpose_dbias_kernel_notaligned<nvec_in, nvec_out, Param>\n                <<<n_blocks, cast_transpose_num_threads, shared_size_transpose, stream>>>(\n                    param, row_length, num_rows, n_tiles);\n            NVTE_CHECK_CUDA(hipGetLastError());\n          }\n\n          reduce_dbias<BiasType>(*workspace, dbias, row_length, num_rows, nvec_out,\n                                 stream););  // NOLINT(*)\n  );                                         // NOLINT(*)\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_fp8_transpose_dbias(const NVTETensor input, NVTETensor transposed_output,\n                              NVTETensor dbias, NVTETensor workspace, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_transpose_dbias);\n  using namespace transformer_engine;\n  fp8_transpose_dbias(*convertNVTETensorCheck(input), convertNVTETensor(transposed_output),\n                      convertNVTETensor(dbias), convertNVTETensor(workspace), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/transpose_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_runtime.h>\n\n#include <algorithm>\n#include <cfloat>\n#include <cuda/barrier>\n#include <utility>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/transpose/cast_transpose.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nusing transformer_engine::detail::FP8BlockwiseColumnwiseOption;\nusing transformer_engine::detail::FP8BlockwiseRowwiseOption;\n\n// clang-format off\n/*\n\nStep 1: Load input to shared memory\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 8 times\n* What each thread does in each loop:\n    * 8 elements are read from the input at a time\n    * 2 elements are written to the shared memory at a time, for a total of 4 times\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|  T0   |  T1   |  T2   |  T3   |  T4   |  T5   |  T6   |  T7   |  T8   |  T9   |  T10  |  T11  |  T12  |  T13  |  T14  |  T15  |\n|  T16  |  T17  |  T18  |  T19  |  T20  |  T21  |  T22  |  T23  |  T24  |  T25  |  T26  |  T27  |  T28  |  T29  |  T30  |  T31  |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 8 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 2: Cast and store to output_c\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 4 times\n* What each thread does in each loop:\n    * 2 elements are read from the shared memory at a time, for a total of 8 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each row\n    * 16 elements are quantized and write to output_c at a time\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|      T0       |      T1       |      T2       |      T3       |      T4       |      T5       |      T6       |      T7       |\n|      T8       |      T9       |      T10      |      T11      |      T12      |      T13      |      T14      |      T15      |\n|      T16      |      T17      |      T18      |      T19      |      T20      |      T21      |      T22      |      T23      |\n|      T24      |      T25      |      T26      |      T27      |      T28      |      T29      |      T30      |      T31      |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 4 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 3 (if columnwise transpose is True, GEMM_READY): Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 2 times\n* What each thread does in each loop:\n    * 2 elements (in a row) are read from the shared memory at a time, for a total of 16 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each column\n    * 16 elements are quantized and write to output_t at a time, for a total of 2 times\n+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+\n| T0  | T8  | T16 | T24 |                       |                       |                       | T0  | T8  | T16 | T24 |                       |                       |                       |\n| T1  | T9  | T17 | T25 |                       |                       |                       | T1  | T9  | T17 | T25 |                       |                       |                       |\n| T2  | T10 | T18 | T26 |                       |                       |                       | T2  | T10 | T18 | T26 |                       |                       |                       |\n| T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         | T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         |\n| T4  | T12 | T20 | T28 |                       |                       |                       | T4  | T12 | T20 | T28 |                       |                       |                       |\n| T5  | T13 | T21 | T29 |                       |                       |                       | T5  | T13 | T21 | T29 |                       |                       |                       |\n| T6  | T14 | T22 | T30 |                       |                       |                       | T6  | T14 | T22 | T30 |                       |                       |                       |\n| T7  | T15 | T23 | T31 |                       |                       |                       | T7  | T15 | T23 | T31 |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+\n\nStep 3 (if columnwise transpose is False, COMPACT format): Skip Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 1 times\n* What each thread does in each loop:\n    * 16 elements (in a row) are read from the shared memory, for a total of 4 rows,\n    * it needs 8 reads in smem to get 16 elements in a row, thread tile shape is 16x4\n    * Every 32 consecutive threads in a warp do reduction and calculate the amax of each column,\n    * so each thread will do warp shuffle 16 times to get the amax of each column\n    * 16 elements are quantized and write to output_t at a time, for a total of 4 times\n+------16 elements-------+------16 elements-------+-----80 elements-----+------16 elements------+\n|           T0          |                       |                       |                       |\n|           T1          |                       |                       |                       |\n|           T2          |                       |                       |                       |\n|           T3          |                       |                       |                       |\n|           T4          |                       |                       |                       |\n|           T5          |                       |                       |                       |\n|           T6          |                       |                       |                       |\n|           T7          |                       |                       |                       |\n|           ...         |                       |                       |                       |\n|           T31         |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+\n\n*/\n// clang-format on\n\nconstexpr size_t kThreadsPerWarp = 32;\n\n// Hyperparameters for performance tuning\nconstexpr int kTileDim = 128;  // Fixed to 128 beacause we are using 1x128 and 128x1 quantization\nconstexpr int kNVecIn = 8;     // The number of elements each LDG touches\nconstexpr int kNVecOut = 16;   // The number of elements each STG touches\nconstexpr int kNVecSMem = 2;   // The number of elements each LDS/STS touches\nconstexpr int kThreadsPerBlock = 256;  // Thread block size, 8 warps in total\n\n// Auto-calculated constants, do not modify directly)\nstatic_assert(kNVecIn % kNVecSMem == 0, \"kNVecIn must be divisible by kNVecSMem\");\nstatic_assert(kNVecOut % kNVecSMem == 0, \"kNVecOut must be divisible by kNVecSMem\");\nconstexpr int kSMemRow = kTileDim;\nconstexpr int kSMemCol = (kTileDim / kNVecSMem) + 1;\nconstexpr int kSMemSize = kSMemRow * kSMemCol * kNVecSMem;\nconstexpr int kNumThreadsLoad = kTileDim / kNVecIn;\nconstexpr int kNumThreadsStore = kTileDim / kNVecOut;\nstatic_assert(kNumThreadsLoad <= kThreadsPerWarp, \"kNumThreadsLoad must be <= kThreadsPerWarp\");\nstatic_assert(kNumThreadsStore <= kThreadsPerWarp, \"kNumThreadsStore must be <= kThreadsPerWarp\");\nconstexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n\ntemplate <bool kAligned, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(kThreadsPerBlock) block_scaled_1d_cast_transpose_kernel(\n    const IType* const input, OType* const output_c, OType* const output_t,\n    CType* const tile_scales_inv_c, CType* const tile_scales_inv_t, const size_t row_length,\n    const size_t num_rows, const size_t scale_stride_x, const size_t scale_stride_y,\n    const size_t scale_t_stride_x, const size_t scale_t_stride_y, const float epsilon,\n    FP8BlockwiseRowwiseOption rowwise_option, FP8BlockwiseColumnwiseOption columnwise_option,\n    const bool pow_2_scaling, const float* noop_ptr) {\n  // skip execution if noop\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  bool return_rowwise = rowwise_option != FP8BlockwiseRowwiseOption::NONE;\n  bool return_columnwise_gemm_ready =\n      columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_GEMM_READY;\n  bool return_columnwise_compact =\n      columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT;\n\n  using SMemVec = Vec<IType, kNVecSMem>;\n  using OVec = Vec<OType, kNVecOut>;\n  union IVec {\n    Vec<IType, kNVecIn> input_type;\n    Vec<SMemVec, kNVecIn / kNVecSMem> smem_type;\n  };\n\n  extern __shared__ char smem_base[];\n  SMemVec* smem = reinterpret_cast<SMemVec*>(&smem_base[0]);\n\n  // Step 1: Load input to shared memory\n  {\n    constexpr int r_stride = kThreadsPerBlock / kNumThreadsLoad;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsLoad) * (kNVecIn / kNVecSMem);  // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsLoad;                      // Row in shared memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;    // Column in global memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;       // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = c_g < row_length ? min(static_cast<size_t>(kNVecIn), row_length - c_g)\n                                            : 0;            // For not aligned case\n    const IType* input_g = &input[r_g * row_length + c_g];  // Input address in global memory\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      IVec input_vec;\n      // Step 1.1: Load from global memory (input) to registers\n      if constexpr (kAligned) {\n        input_vec.input_type.load_from(input_g);\n      } else {\n        if (r_g < num_rows) {\n          input_vec.input_type.load_from_elts(input_g, 0, num_ele);\n        } else {\n          input_vec.input_type.clear();\n        }\n      }\n      // Step 1.2: Write to shared memory\n#pragma unroll\n      for (int i = 0; i < kNVecIn / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem[r * kSMemCol + c] = input_vec.smem_type.data.elt[i];\n      }\n      // Step 1.3: Update input address, row index of shared memory, (and row index of global memory for not aligned case)\n      input_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  __syncthreads();\n\n  // Step 2: Cast and store to output_c\n  if (return_rowwise) {\n    constexpr int r_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsStore) * (kNVecOut / kNVecSMem);  // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsStore;                       // Row in shared memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;    // Column in global memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;       // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = c_g < row_length ? min(static_cast<size_t>(kNVecOut), row_length - c_g)\n                                            : 0;          // For not aligned case\n    OType* output_g = &output_c[r_g * row_length + c_g];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane = (threadIdx.x % kThreadsPerWarp) / kNumThreadsStore * kNumThreadsStore;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsStore) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsStore) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut / kNVecSMem];\n      // Step 2.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n      // Step 2.2: Compute local amax\n      CType amax = 0;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          __builtin_assume(amax >= 0);\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[j]));\n        }\n      }\n      // Step 2.3: Reduce amax\n#pragma unroll\n      for (int delta = kNumThreadsStore / 2; delta > 0; delta /= 2) {\n        const float other_amax = __shfl_down_sync(mask, amax, delta);\n        __builtin_assume(amax >= 0);\n        __builtin_assume(other_amax >= 0);\n        amax = fmaxf(amax, other_amax);\n      }\n      amax = __shfl_sync(mask, amax, src_lane);\n      CType scale;\n      // Step 2.4: Compute scale\n      scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n      // Step 2.5: Write scale_inv\n      bool write_scale_inv = is_src_lane;\n      if constexpr (!kAligned) {\n        write_scale_inv &= (r_g < num_rows);\n      }\n      if (write_scale_inv) {\n        CType scale_inv = 1.0 / scale;\n        size_t row_idx = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;\n        size_t col_idx = static_cast<size_t>(blockIdx.x);\n        tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n      }\n      // Step 2.6: Quantize\n      OVec output_vec;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          output_vec.data.elt[i * kNVecSMem + j] =\n              static_cast<OType>(static_cast<CType>(smem_vec[i].data.elt[j]) * scale);\n        }\n      }\n      // Step 2.7: Store output_c\n      if constexpr (kAligned) {\n        output_vec.store_to(output_g);\n      } else {\n        if (r_g < num_rows) {\n          output_vec.store_to_elts(output_g, 0, num_ele);\n        }\n      }\n      // Step 2.8: Update output address, row index of shared memory (and row index of global memory for not aligned case)\n      output_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  // Step 3 (return_columnwise_gemm_ready): Transpose, cast and store to output_t\n  if (return_columnwise_gemm_ready) {\n    constexpr int c_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // Stride in columns of shared memory\n    constexpr int num_iterations = kTileDim / (c_stride * kNVecSMem);\n    const int r_s = (threadIdx.x % kNumThreadsStore) * kNVecOut;  // Row in shared memory\n    int c_s = threadIdx.x / kNumThreadsStore;                     // Column in shared memory\n    size_t r_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;     // Row in global memory\n    const size_t c_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;  // Column in global memory\n    const size_t stride_g =\n        static_cast<size_t>(c_stride) * kNVecSMem * num_rows;  // Stride in global memory\n    const size_t num_ele = c_g < num_rows ? min(static_cast<size_t>(kNVecOut), num_rows - c_g)\n                                          : 0;          // For not aligned case\n    OType* output_g = &output_t[r_g * num_rows + c_g];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane = (threadIdx.x % kThreadsPerWarp) / kNumThreadsStore * kNumThreadsStore;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsStore) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsStore) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut];\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut; ++i) {\n        int r = r_s + i;\n        int c = c_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n#pragma unroll\n      for (int smem_idx = 0; smem_idx < kNVecSMem; ++smem_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n#pragma unroll\n        for (int i = 0; i < kNVecOut; ++i) {\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[smem_idx]));\n        }\n        // Step 3.3: Reduce amax\n#pragma unroll\n        for (int delta = kNumThreadsStore / 2; delta > 0; delta /= 2) {\n          const float other_amax = __shfl_down_sync(mask, amax, delta);\n          __builtin_assume(amax >= 0);\n          __builtin_assume(other_amax >= 0);\n          amax = fmaxf(amax, other_amax);\n        }\n        amax = __shfl_sync(mask, amax, src_lane);\n        // Step 3.4: Compute scale\n        CType scale;\n        scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (r_g + smem_idx < row_length);\n        }\n        if (write_scale_inv) {\n          CType scale_inv = 1.0 / scale;\n          size_t row_idx = static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem + smem_idx;\n          size_t col_idx = static_cast<size_t>(blockIdx.y);\n          tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n        }\n        // Step 3.6: Quantize\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kNVecOut; ++i) {\n          output_vec.data.elt[i] =\n              static_cast<OType>(static_cast<CType>(smem_vec[i].data.elt[smem_idx]) * scale);\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g + smem_idx * num_rows);\n        } else {\n          if (r_g + smem_idx < row_length) {\n            output_vec.store_to_elts(output_g + smem_idx * num_rows, 0, num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory (and row index of global memory for not aligned case)\n      output_g += stride_g;\n      c_s += c_stride;\n      if constexpr (!kAligned) {\n        r_g += c_stride * kNVecSMem;\n      }\n    }\n  }\n\n  // Step 4 (return_columnwise_compact): cast in 128x1 style and store to output, skip transpose\n  if (return_columnwise_compact) {\n    // thread tile should be 4x16, 16 means 8 smem reads\n    constexpr int kThreadTileRow = kTileDim / kThreadsPerWarp;\n    constexpr int kThreadTileCol = kNVecOut;\n    using RegVec = Vec<IType, kThreadTileCol>;\n    using RegScaleVec = Vec<CType, kThreadTileCol>;\n    constexpr int num_smem_reads = kNVecOut / kNVecSMem;\n    // c_stride will not be used here because we only have one iteration\n    // constexpr int c_stride = kThreadTileCol * kNumWarps / kNVecSMem;\n    constexpr int num_iterations =\n        kTileDim / (kNumWarps * kThreadTileCol);  // should be only one iteration\n    static_assert(num_iterations == 1,\n                  \"num_iterations should be 1 for columnwise non-transpose case\");\n    const int thr_idx_in_warp = threadIdx.x % kThreadsPerWarp;\n    const int warp_idx = threadIdx.x / kThreadsPerWarp;\n    const int r_s = thr_idx_in_warp * kThreadTileRow;               // Row in shared memory\n    int c_s = warp_idx * num_smem_reads;                            // Column in shared memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;  // Row in global memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;  // Column in global memory\n    const size_t num_ele = c_g < row_length\n                               ? min(static_cast<size_t>(kThreadTileCol), row_length - c_g)\n                               : 0;  // For not aligned case\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      RegVec reg_vec[kThreadTileRow];\n      RegScaleVec thr_scale;\n\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kThreadTileRow; ++i) {\n        int r = r_s + i;\n#pragma unroll\n        for (int j = 0; j < num_smem_reads; ++j) {\n          int c = c_s + j;\n          SMemVec smem_vec = smem[r * kSMemCol + c];\n          // copy smem_vec to reg vec with its elements\n#pragma unroll\n          for (int k = 0; k < kNVecSMem; ++k) {\n            reg_vec[i].data.elt[j * kNVecSMem + k] = smem_vec.data.elt[k];\n          }\n        }\n      }\n#pragma unroll\n      for (int reg_idx = 0; reg_idx < kThreadTileCol; ++reg_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n#pragma unroll\n        for (int i = 0; i < kThreadTileRow; ++i) {\n          amax = fmaxf(amax, fabsf(reg_vec[i].data.elt[reg_idx]));\n        }\n        // Step 3.3: Reduce amax\n        const bool is_src_lane = thr_idx_in_warp == 0;\n        amax = warp_reduce_max<kThreadsPerWarp>(amax);\n        constexpr int lane_zero = 0;\n        amax = __shfl_sync(0xFFFFFFFF, amax, lane_zero);\n        // Step 3.4: Compute scale\n        CType scale;\n        scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n        thr_scale.data.elt[reg_idx] = scale;\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (c_g + reg_idx < row_length);\n        }\n        if (write_scale_inv) {\n          CType scale_inv = 1.0 / scale;\n          size_t row_idx = static_cast<size_t>(blockIdx.y);\n          size_t col_idx = static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem + reg_idx;\n          tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n        }\n      }\n      // Step 3.6: Quantize\n      for (int row_idx = 0; row_idx < kThreadTileRow; ++row_idx) {\n        OType* output_g =\n            &output_t[(r_g + row_idx) * row_length + c_g];  // Output address in global memory\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kThreadTileCol; ++i) {\n          output_vec.data.elt[i] = static_cast<OType>(\n              static_cast<CType>(reg_vec[row_idx].data.elt[i]) * thr_scale.data.elt[i]);\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g);\n        } else {\n          if (r_g + row_idx < num_rows) {\n            output_vec.store_to_elts(output_g, 0, num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory\n      // this section shouldn't matter since we only have one iteration\n    }\n  }\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace transformer_engine::detail {\n\nvoid quantize_transpose_vector_blockwise(const SimpleTensor& input, SimpleTensor& scale_inv,\n                                         SimpleTensor& scale_inv_t, SimpleTensor& output,\n                                         SimpleTensor& output_t, const float epsilon,\n                                         FP8BlockwiseRowwiseOption rowwise_option,\n                                         FP8BlockwiseColumnwiseOption columnwise_option,\n                                         const bool pow2_scale, const SimpleTensor& noop_tensor,\n                                         cudaStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_vector_blockwise);\n\n  if (transformer_engine::cuda::sm_arch() >= 100) {\n    NVTE_CHECK(pow2_scale, \"On Blackwell and newer, the FP8 block scaling recipe is emulated \",\n               \"with MXFP8, which requires using power of two scaling factors.\");\n  }\n\n  const size_t row_length = input.shape.size() > 0 ? input.shape.at(input.shape.size() - 1) : 1u;\n  size_t num_elements = row_length;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n    num_elements *= input.shape.at(i);\n  }\n\n  // Early return if the input tensor is empty\n  if (num_elements == 0) {\n    return;\n  }\n\n  // Options for scale layout of cuBLAS GEMM kernel.\n  size_t scale_stride_x = 0;\n  size_t scale_stride_y = 0;\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  if (rowwise_option != FP8BlockwiseRowwiseOption::NONE) {\n    NVTE_CHECK(rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_GEMM_READY ||\n                   rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_COMPACT,\n               \"Unexpected rowwise enum value\");\n    NVTE_CHECK(input.shape == output.shape, \"Input and output must have the same shape.\");\n    NVTE_CHECK(scale_inv.shape.size() == 2, \"Scale dimension must be 2.\");\n    size_t scale_k = scale_inv.shape[1];\n    bool rowwise_compact = rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_COMPACT;\n    scale_stride_x = rowwise_compact ? 1 : scale_k;\n    scale_stride_y = rowwise_compact ? scale_k : 1;\n  }\n\n  if (columnwise_option != FP8BlockwiseColumnwiseOption::NONE) {\n    NVTE_CHECK(output_t.shape.size() == input.shape.size(),\n               \"output_t must have same number of dimensions as input.\");\n\n    if (output_t.shape.size() > 0) {\n      if (columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_GEMM_READY) {\n        NVTE_CHECK(output_t.shape[0] == row_length, \"Wrong dimension 0 of output_t.\");\n        for (size_t i = 1; i < output_t.shape.size(); ++i) {\n          NVTE_CHECK(output_t.shape.at(i) == input.shape.at(i - 1), \"Wrong dimension in output_t\");\n        }\n      } else {\n        NVTE_CHECK(columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT,\n                   \"Unexpected columnwise option enum value\");\n        NVTE_CHECK(output_t.shape[0] == input.shape[0], \"Wrong dimension 0 of output_t.\");\n        NVTE_CHECK(\n            input.shape == output_t.shape,\n            \"Input and output_t must have the same shape for columnwise non-transpose case.\");\n      }\n    }\n    if (rowwise_option != FP8BlockwiseRowwiseOption::NONE) {\n      // output may not be defined if rowwise quantization is not needed.\n      NVTE_CHECK(output.dtype == output_t.dtype,\n                 \"output and output_t need to have the same dtype.\");\n    }\n    NVTE_CHECK(scale_inv_t.shape.size() == 2, \"Scale_t dimension must be 2.\");\n    bool columnwise_compact = columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT;\n    size_t scale_t_k = scale_inv_t.shape[1];\n    scale_t_stride_x = columnwise_compact ? 1 : scale_t_k;\n    scale_t_stride_y = columnwise_compact ? scale_t_k : 1;\n  }\n  auto output_dtype =\n      rowwise_option != FP8BlockwiseRowwiseOption::NONE ? output.dtype : output_t.dtype;\n\n  const size_t num_blocks_x = DIVUP(row_length, (size_t)kTileDim);\n  const size_t num_blocks_y = DIVUP(num_rows, (size_t)kTileDim);\n\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          output_dtype, OutputType,\n\n          dim3 grid(num_blocks_x, num_blocks_y, 1);\n\n          const bool full_tile = row_length % kTileDim == 0 && num_rows % kTileDim == 0;\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              full_tile, kAligned,\n\n              size_t smem_bytes = kSMemSize * sizeof(InputType);\n              // shared memory must be requested up\n              if (smem_bytes >= 48 * 1024) {\n                cudaError_t err = cudaFuncSetAttribute(\n                    &block_scaled_1d_cast_transpose_kernel<kAligned, float, InputType, OutputType>,\n                    cudaFuncAttributeMaxDynamicSharedMemorySize, smem_bytes);\n                NVTE_CHECK(err == cudaSuccess, \"Failed to set dynamic shared memory size.\");\n              } block_scaled_1d_cast_transpose_kernel<kAligned, float, InputType, OutputType>\n              <<<grid, kThreadsPerBlock, smem_bytes, stream>>>(\n                  reinterpret_cast<const InputType*>(input.dptr),\n                  reinterpret_cast<OutputType*>(output.dptr),\n                  reinterpret_cast<OutputType*>(output_t.dptr),\n                  reinterpret_cast<float*>(scale_inv.dptr),\n                  reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows, scale_stride_x,\n                  scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon, rowwise_option,\n                  columnwise_option, pow2_scale, noop_ptr);)  // kAligned\n          )                                                   // OutputType\n      )                                                       // InputType\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace transformer_engine::detail\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n\n#include <algorithm>\n#include <cfloat>\n#include <cuda/barrier>\n#include <utility>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/transpose/cast_transpose.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nusing transformer_engine::detail::FP8BlockwiseColumnwiseOption;\nusing transformer_engine::detail::FP8BlockwiseRowwiseOption;\n\n// clang-format off\n/*\n\nStep 1: Load input to shared memory\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 8 times\n* What each thread does in each loop:\n    * 8 elements are read from the input at a time\n    * 2 elements are written to the shared memory at a time, for a total of 4 times\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|  T0   |  T1   |  T2   |  T3   |  T4   |  T5   |  T6   |  T7   |  T8   |  T9   |  T10  |  T11  |  T12  |  T13  |  T14  |  T15  |\n|  T16  |  T17  |  T18  |  T19  |  T20  |  T21  |  T22  |  T23  |  T24  |  T25  |  T26  |  T27  |  T28  |  T29  |  T30  |  T31  |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 8 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 2: Cast and store to output_c\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 4 times\n* What each thread does in each loop:\n    * 2 elements are read from the shared memory at a time, for a total of 8 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each row\n    * 16 elements are quantized and write to output_c at a time\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|      T0       |      T1       |      T2       |      T3       |      T4       |      T5       |      T6       |      T7       |\n|      T8       |      T9       |      T10      |      T11      |      T12      |      T13      |      T14      |      T15      |\n|      T16      |      T17      |      T18      |      T19      |      T20      |      T21      |      T22      |      T23      |\n|      T24      |      T25      |      T26      |      T27      |      T28      |      T29      |      T30      |      T31      |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 4 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 3 (if columnwise transpose is True, GEMM_READY): Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 2 times\n* What each thread does in each loop:\n    * 2 elements (in a row) are read from the shared memory at a time, for a total of 16 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each column\n    * 16 elements are quantized and write to output_t at a time, for a total of 2 times\n+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+\n| T0  | T8  | T16 | T24 |                       |                       |                       | T0  | T8  | T16 | T24 |                       |                       |                       |\n| T1  | T9  | T17 | T25 |                       |                       |                       | T1  | T9  | T17 | T25 |                       |                       |                       |\n| T2  | T10 | T18 | T26 |                       |                       |                       | T2  | T10 | T18 | T26 |                       |                       |                       |\n| T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         | T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         |\n| T4  | T12 | T20 | T28 |                       |                       |                       | T4  | T12 | T20 | T28 |                       |                       |                       |\n| T5  | T13 | T21 | T29 |                       |                       |                       | T5  | T13 | T21 | T29 |                       |                       |                       |\n| T6  | T14 | T22 | T30 |                       |                       |                       | T6  | T14 | T22 | T30 |                       |                       |                       |\n| T7  | T15 | T23 | T31 |                       |                       |                       | T7  | T15 | T23 | T31 |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+\n\nStep 3 (if columnwise transpose is False, COMPACT format): Skip Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 1 times\n* What each thread does in each loop:\n    * 16 elements (in a row) are read from the shared memory, for a total of 4 rows,\n    * it needs 8 reads in smem to get 16 elements in a row, thread tile shape is 16x4\n    * Every 32 consecutive threads in a warp do reduction and calculate the amax of each column,\n    * so each thread will do warp shuffle 16 times to get the amax of each column\n    * 16 elements are quantized and write to output_t at a time, for a total of 4 times\n+------16 elements-------+------16 elements-------+-----80 elements-----+------16 elements------+\n|           T0          |                       |                       |                       |\n|           T1          |                       |                       |                       |\n|           T2          |                       |                       |                       |\n|           T3          |                       |                       |                       |\n|           T4          |                       |                       |                       |\n|           T5          |                       |                       |                       |\n|           T6          |                       |                       |                       |\n|           T7          |                       |                       |                       |\n|           ...         |                       |                       |                       |\n|           T31         |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+\n\n*/\n// clang-format on\n\nconstexpr size_t kThreadsPerWarp = 32;\n\n// Hyperparameters for performance tuning\nconstexpr int kTileDim = 128;  // Fixed to 128 beacause we are using 1x128 and 128x1 quantization\nconstexpr int kNVecIn = 8;     // The number of elements each LDG touches\nconstexpr int kNVecOut = 16;   // The number of elements each STG touches\nconstexpr int kNVecSMem = 2;   // The number of elements each LDS/STS touches\nconstexpr int kThreadsPerBlock = 256;  // Thread block size, 8 warps in total\n\n// Auto-calculated constants, do not modify directly)\nstatic_assert(kNVecIn % kNVecSMem == 0, \"kNVecIn must be divisible by kNVecSMem\");\nstatic_assert(kNVecOut % kNVecSMem == 0, \"kNVecOut must be divisible by kNVecSMem\");\nconstexpr int kSMemRow = kTileDim;\nconstexpr int kSMemCol = (kTileDim / kNVecSMem) + 1;\nconstexpr int kSMemSize = kSMemRow * kSMemCol * kNVecSMem;\nconstexpr int kNumThreadsLoad = kTileDim / kNVecIn;\nconstexpr int kNumThreadsStore = kTileDim / kNVecOut;\nstatic_assert(kNumThreadsLoad <= kThreadsPerWarp, \"kNumThreadsLoad must be <= kThreadsPerWarp\");\nstatic_assert(kNumThreadsStore <= kThreadsPerWarp, \"kNumThreadsStore must be <= kThreadsPerWarp\");\nconstexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n\ntemplate <bool kAligned, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(kThreadsPerBlock) block_scaled_1d_cast_transpose_kernel(\n    const IType* const input, OType* const output_c, OType* const output_t,\n    CType* const tile_scales_inv_c, CType* const tile_scales_inv_t, const size_t row_length,\n    const size_t num_rows, const size_t scale_stride_x, const size_t scale_stride_y,\n    const size_t scale_t_stride_x, const size_t scale_t_stride_y, const float epsilon,\n    FP8BlockwiseRowwiseOption rowwise_option, FP8BlockwiseColumnwiseOption columnwise_option,\n    const bool pow_2_scaling, const float* noop_ptr) {\n  // skip execution if noop\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  bool return_rowwise = rowwise_option != FP8BlockwiseRowwiseOption::NONE;\n  bool return_columnwise_gemm_ready =\n      columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_GEMM_READY;\n  bool return_columnwise_compact =\n      columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT;\n\n  using SMemVec = Vec<IType, kNVecSMem>;\n  using OVec = Vec<OType, kNVecOut>;\n  union IVec {\n    Vec<IType, kNVecIn> input_type;\n    Vec<SMemVec, kNVecIn / kNVecSMem> smem_type;\n  };\n\n  extern __shared__ char smem_base[];\n  SMemVec* smem = reinterpret_cast<SMemVec*>(&smem_base[0]);\n\n  // Step 1: Load input to shared memory\n  {\n    constexpr int r_stride = kThreadsPerBlock / kNumThreadsLoad;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsLoad) * (kNVecIn / kNVecSMem);  // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsLoad;                      // Row in shared memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;    // Column in global memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;       // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = c_g < row_length ? min(static_cast<size_t>(kNVecIn), row_length - c_g)\n                                            : 0;            // For not aligned case\n    const IType* input_g = &input[r_g * row_length + c_g];  // Input address in global memory\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      IVec input_vec;\n      // Step 1.1: Load from global memory (input) to registers\n      if constexpr (kAligned) {\n        input_vec.input_type.load_from(input_g);\n      } else {\n        if (r_g < num_rows) {\n          input_vec.input_type.load_from_elts(input_g, 0, num_ele);\n        } else {\n          input_vec.input_type.clear();\n        }\n      }\n      // Step 1.2: Write to shared memory\n#pragma unroll\n      for (int i = 0; i < kNVecIn / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem[r * kSMemCol + c] = input_vec.smem_type.data.elt[i];\n      }\n      // Step 1.3: Update input address, row index of shared memory, (and row index of global memory for not aligned case)\n      input_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  __syncthreads();\n\n  // Step 2: Cast and store to output_c\n  if (return_rowwise) {\n    constexpr int r_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsStore) * (kNVecOut / kNVecSMem);  // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsStore;                       // Row in shared memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;    // Column in global memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;       // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = c_g < row_length ? min(static_cast<size_t>(kNVecOut), row_length - c_g)\n                                            : 0;          // For not aligned case\n    OType* output_g = &output_c[r_g * row_length + c_g];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane = (threadIdx.x % kThreadsPerWarp) / kNumThreadsStore * kNumThreadsStore;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsStore) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsStore) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut / kNVecSMem];\n      // Step 2.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n      // Step 2.2: Compute local amax\n      CType amax = 0;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          __builtin_assume(amax >= 0);\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[j]));\n        }\n      }\n      // Step 2.3: Reduce amax\n#pragma unroll\n      for (int delta = kNumThreadsStore / 2; delta > 0; delta /= 2) {\n        const float other_amax = __shfl_down_sync(mask, amax, delta);\n        __builtin_assume(amax >= 0);\n        __builtin_assume(other_amax >= 0);\n        amax = fmaxf(amax, other_amax);\n      }\n      amax = __shfl_sync(mask, amax, src_lane);\n      CType scale;\n      // Step 2.4: Compute scale\n      scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n      // Step 2.5: Write scale_inv\n      bool write_scale_inv = is_src_lane;\n      if constexpr (!kAligned) {\n        write_scale_inv &= (r_g < num_rows);\n      }\n      if (write_scale_inv) {\n        CType scale_inv = 1.0 / scale;\n        size_t row_idx = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;\n        size_t col_idx = static_cast<size_t>(blockIdx.x);\n        tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n      }\n      // Step 2.6: Quantize\n      OVec output_vec;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          output_vec.data.elt[i * kNVecSMem + j] =\n              static_cast<OType>(static_cast<CType>(smem_vec[i].data.elt[j]) * scale);\n        }\n      }\n      // Step 2.7: Store output_c\n      if constexpr (kAligned) {\n        output_vec.store_to(output_g);\n      } else {\n        if (r_g < num_rows) {\n          output_vec.store_to_elts(output_g, 0, num_ele);\n        }\n      }\n      // Step 2.8: Update output address, row index of shared memory (and row index of global memory for not aligned case)\n      output_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  // Step 3 (return_columnwise_gemm_ready): Transpose, cast and store to output_t\n  if (return_columnwise_gemm_ready) {\n    constexpr int c_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // Stride in columns of shared memory\n    constexpr int num_iterations = kTileDim / (c_stride * kNVecSMem);\n    const int r_s = (threadIdx.x % kNumThreadsStore) * kNVecOut;  // Row in shared memory\n    int c_s = threadIdx.x / kNumThreadsStore;                     // Column in shared memory\n    size_t r_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;     // Row in global memory\n    const size_t c_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;  // Column in global memory\n    const size_t stride_g =\n        static_cast<size_t>(c_stride) * kNVecSMem * num_rows;  // Stride in global memory\n    const size_t num_ele = c_g < num_rows ? min(static_cast<size_t>(kNVecOut), num_rows - c_g)\n                                          : 0;          // For not aligned case\n    OType* output_g = &output_t[r_g * num_rows + c_g];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane = (threadIdx.x % kThreadsPerWarp) / kNumThreadsStore * kNumThreadsStore;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsStore) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsStore) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut];\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut; ++i) {\n        int r = r_s + i;\n        int c = c_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n#pragma unroll\n      for (int smem_idx = 0; smem_idx < kNVecSMem; ++smem_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n#pragma unroll\n        for (int i = 0; i < kNVecOut; ++i) {\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[smem_idx]));\n        }\n        // Step 3.3: Reduce amax\n#pragma unroll\n        for (int delta = kNumThreadsStore / 2; delta > 0; delta /= 2) {\n          const float other_amax = __shfl_down_sync(mask, amax, delta);\n          __builtin_assume(amax >= 0);\n          __builtin_assume(other_amax >= 0);\n          amax = fmaxf(amax, other_amax);\n        }\n        amax = __shfl_sync(mask, amax, src_lane);\n        // Step 3.4: Compute scale\n        CType scale;\n        scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (r_g + smem_idx < row_length);\n        }\n        if (write_scale_inv) {\n          CType scale_inv = 1.0 / scale;\n          size_t row_idx = static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem + smem_idx;\n          size_t col_idx = static_cast<size_t>(blockIdx.y);\n          tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n        }\n        // Step 3.6: Quantize\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kNVecOut; ++i) {\n          output_vec.data.elt[i] =\n              static_cast<OType>(static_cast<CType>(smem_vec[i].data.elt[smem_idx]) * scale);\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g + smem_idx * num_rows);\n        } else {\n          if (r_g + smem_idx < row_length) {\n            output_vec.store_to_elts(output_g + smem_idx * num_rows, 0, num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory (and row index of global memory for not aligned case)\n      output_g += stride_g;\n      c_s += c_stride;\n      if constexpr (!kAligned) {\n        r_g += c_stride * kNVecSMem;\n      }\n    }\n  }\n\n  // Step 4 (return_columnwise_compact): cast in 128x1 style and store to output, skip transpose\n  if (return_columnwise_compact) {\n    // thread tile should be 4x16, 16 means 8 smem reads\n    constexpr int kThreadTileRow = kTileDim / kThreadsPerWarp;\n    constexpr int kThreadTileCol = kNVecOut;\n    using RegVec = Vec<IType, kThreadTileCol>;\n    using RegScaleVec = Vec<CType, kThreadTileCol>;\n    constexpr int num_smem_reads = kNVecOut / kNVecSMem;\n    // c_stride will not be used here because we only have one iteration\n    // constexpr int c_stride = kThreadTileCol * kNumWarps / kNVecSMem;\n    constexpr int num_iterations =\n        kTileDim / (kNumWarps * kThreadTileCol);  // should be only one iteration\n    static_assert(num_iterations == 1,\n                  \"num_iterations should be 1 for columnwise non-transpose case\");\n    const int thr_idx_in_warp = threadIdx.x % kThreadsPerWarp;\n    const int warp_idx = threadIdx.x / kThreadsPerWarp;\n    const int r_s = thr_idx_in_warp * kThreadTileRow;               // Row in shared memory\n    int c_s = warp_idx * num_smem_reads;                            // Column in shared memory\n    size_t r_g = static_cast<size_t>(blockIdx.y) * kTileDim + r_s;  // Row in global memory\n    const size_t c_g =\n        static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem;  // Column in global memory\n    const size_t num_ele = c_g < row_length\n                               ? min(static_cast<size_t>(kThreadTileCol), row_length - c_g)\n                               : 0;  // For not aligned case\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      RegVec reg_vec[kThreadTileRow];\n      RegScaleVec thr_scale;\n\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kThreadTileRow; ++i) {\n        int r = r_s + i;\n#pragma unroll\n        for (int j = 0; j < num_smem_reads; ++j) {\n          int c = c_s + j;\n          SMemVec smem_vec = smem[r * kSMemCol + c];\n          // copy smem_vec to reg vec with its elements\n#pragma unroll\n          for (int k = 0; k < kNVecSMem; ++k) {\n            reg_vec[i].data.elt[j * kNVecSMem + k] = smem_vec.data.elt[k];\n          }\n        }\n      }\n#pragma unroll\n      for (int reg_idx = 0; reg_idx < kThreadTileCol; ++reg_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n#pragma unroll\n        for (int i = 0; i < kThreadTileRow; ++i) {\n          amax = fmaxf(amax, fabsf(reg_vec[i].data.elt[reg_idx]));\n        }\n        // Step 3.3: Reduce amax\n        const bool is_src_lane = thr_idx_in_warp == 0;\n        amax = warp_reduce_max<kThreadsPerWarp>(amax);\n        constexpr int lane_zero = 0;\n        amax = __shfl_sync(0xFFFFFFFF, amax, lane_zero);\n        // Step 3.4: Compute scale\n        CType scale;\n        scale = compute_scale_from_types<IType, OType>(amax, epsilon, pow_2_scaling);\n        thr_scale.data.elt[reg_idx] = scale;\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (c_g + reg_idx < row_length);\n        }\n        if (write_scale_inv) {\n          CType scale_inv = 1.0 / scale;\n          size_t row_idx = static_cast<size_t>(blockIdx.y);\n          size_t col_idx = static_cast<size_t>(blockIdx.x) * kTileDim + c_s * kNVecSMem + reg_idx;\n          tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n        }\n      }\n      // Step 3.6: Quantize\n      for (int row_idx = 0; row_idx < kThreadTileRow; ++row_idx) {\n        OType* output_g =\n            &output_t[(r_g + row_idx) * row_length + c_g];  // Output address in global memory\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kThreadTileCol; ++i) {\n          output_vec.data.elt[i] = static_cast<OType>(\n              static_cast<CType>(reg_vec[row_idx].data.elt[i]) * thr_scale.data.elt[i]);\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g);\n        } else {\n          if (r_g + row_idx < num_rows) {\n            output_vec.store_to_elts(output_g, 0, num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory\n      // this section shouldn't matter since we only have one iteration\n    }\n  }\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace transformer_engine::detail {\n\nvoid quantize_transpose_vector_blockwise(const SimpleTensor& input, SimpleTensor& scale_inv,\n                                         SimpleTensor& scale_inv_t, SimpleTensor& output,\n                                         SimpleTensor& output_t, const float epsilon,\n                                         FP8BlockwiseRowwiseOption rowwise_option,\n                                         FP8BlockwiseColumnwiseOption columnwise_option,\n                                         const bool pow2_scale, const SimpleTensor& noop_tensor,\n                                         hipStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_vector_blockwise);\n\n  if (transformer_engine::cuda::sm_arch() >= 100) {\n    NVTE_CHECK(pow2_scale, \"On Blackwell and newer, the FP8 block scaling recipe is emulated \",\n               \"with MXFP8, which requires using power of two scaling factors.\");\n  }\n\n  const size_t row_length = input.shape.size() > 0 ? input.shape.at(input.shape.size() - 1) : 1u;\n  size_t num_elements = row_length;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n    num_elements *= input.shape.at(i);\n  }\n\n  // Early return if the input tensor is empty\n  if (num_elements == 0) {\n    return;\n  }\n\n  // Options for scale layout of cuBLAS GEMM kernel.\n  size_t scale_stride_x = 0;\n  size_t scale_stride_y = 0;\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  if (rowwise_option != FP8BlockwiseRowwiseOption::NONE) {\n    NVTE_CHECK(rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_GEMM_READY ||\n                   rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_COMPACT,\n               \"Unexpected rowwise enum value\");\n    NVTE_CHECK(input.shape == output.shape, \"Input and output must have the same shape.\");\n    NVTE_CHECK(scale_inv.shape.size() == 2, \"Scale dimension must be 2.\");\n    size_t scale_k = scale_inv.shape[1];\n    bool rowwise_compact = rowwise_option == FP8BlockwiseRowwiseOption::ROWWISE_COMPACT;\n    scale_stride_x = rowwise_compact ? 1 : scale_k;\n    scale_stride_y = rowwise_compact ? scale_k : 1;\n  }\n\n  if (columnwise_option != FP8BlockwiseColumnwiseOption::NONE) {\n    NVTE_CHECK(output_t.shape.size() == input.shape.size(),\n               \"output_t must have same number of dimensions as input.\");\n\n    if (output_t.shape.size() > 0) {\n      if (columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_GEMM_READY) {\n        NVTE_CHECK(output_t.shape[0] == row_length, \"Wrong dimension 0 of output_t.\");\n        for (size_t i = 1; i < output_t.shape.size(); ++i) {\n          NVTE_CHECK(output_t.shape.at(i) == input.shape.at(i - 1), \"Wrong dimension in output_t\");\n        }\n      } else {\n        NVTE_CHECK(columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT,\n                   \"Unexpected columnwise option enum value\");\n        NVTE_CHECK(output_t.shape[0] == input.shape[0], \"Wrong dimension 0 of output_t.\");\n        NVTE_CHECK(\n            input.shape == output_t.shape,\n            \"Input and output_t must have the same shape for columnwise non-transpose case.\");\n      }\n    }\n    if (rowwise_option != FP8BlockwiseRowwiseOption::NONE) {\n      // output may not be defined if rowwise quantization is not needed.\n      NVTE_CHECK(output.dtype == output_t.dtype,\n                 \"output and output_t need to have the same dtype.\");\n    }\n    NVTE_CHECK(scale_inv_t.shape.size() == 2, \"Scale_t dimension must be 2.\");\n    bool columnwise_compact = columnwise_option == FP8BlockwiseColumnwiseOption::COLUMNWISE_COMPACT;\n    size_t scale_t_k = scale_inv_t.shape[1];\n    scale_t_stride_x = columnwise_compact ? 1 : scale_t_k;\n    scale_t_stride_y = columnwise_compact ? scale_t_k : 1;\n  }\n  auto output_dtype =\n      rowwise_option != FP8BlockwiseRowwiseOption::NONE ? output.dtype : output_t.dtype;\n\n  const size_t num_blocks_x = DIVUP(row_length, (size_t)kTileDim);\n  const size_t num_blocks_y = DIVUP(num_rows, (size_t)kTileDim);\n\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          output_dtype, OutputType,\n\n          dim3 grid(num_blocks_x, num_blocks_y, 1);\n\n          const bool full_tile = row_length % kTileDim == 0 && num_rows % kTileDim == 0;\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              full_tile, kAligned,\n\n              size_t smem_bytes = kSMemSize * sizeof(InputType);\n              // shared memory must be requested up\n              if (smem_bytes >= 48 * 1024) {\n                hipError_t err = hipFuncSetAttribute(reinterpret_cast<const void*>(&block_scaled_1d_cast_transpose_kernel<kAligned, float, InputType, OutputType>),\n                    hipFuncAttributeMaxDynamicSharedMemorySize, smem_bytes);\n                NVTE_CHECK(err == hipSuccess, \"Failed to set dynamic shared memory size.\");\n              } block_scaled_1d_cast_transpose_kernel<kAligned, float, InputType, OutputType>\n              <<<grid, kThreadsPerBlock, smem_bytes, stream>>>(\n                  reinterpret_cast<const InputType*>(input.dptr),\n                  reinterpret_cast<OutputType*>(output.dptr),\n                  reinterpret_cast<OutputType*>(output_t.dptr),\n                  reinterpret_cast<float*>(scale_inv.dptr),\n                  reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows, scale_stride_x,\n                  scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon, rowwise_option,\n                  columnwise_option, pow2_scale, noop_ptr);)  // kAligned\n          )                                                   // OutputType\n      )                                                       // InputType\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace transformer_engine::detail\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/quantize_transpose_vector_blockwise.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/cast_transpose_noop.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"cast_transpose.h\"\n\nnamespace transformer_engine::detail {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_cast_transpose_cu.h\"\n\n// Hard-coded kernel parameters\nusing CType = float;\nconstexpr size_t warps_per_tile = 4;\nconstexpr size_t block_size = THREADS_PER_WARP * warps_per_tile;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /** Vector load size */\n  size_t load_size = 0;\n  /** Vector store size to transposed output */\n  size_t store_size = 0;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Elements per L1 cache load */\n  size_t elements_per_load = 0;\n  /* Elements per L1 cache store to cast output*/\n  size_t elements_per_store_c = 0;\n  /* Elements per L1 cache store to transposed output */\n  size_t elements_per_store_t = 0;\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t itype_size, size_t otype_size,\n               size_t load_size_, size_t store_size_, size_t sm_count)\n      : load_size{load_size_}, store_size{store_size_} {\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % itype_size != 0 || store_size % otype_size != 0 ||\n        cache_line_size % itype_size != 0 || cache_line_size % otype_size != 0) {\n      return;\n    }\n    const size_t row_tile_elements = load_size * THREADS_PER_WARP / itype_size;\n    const size_t col_tile_elements = store_size * THREADS_PER_WARP / otype_size;\n    valid = (row_length % row_tile_elements == 0 && num_rows % col_tile_elements == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = (row_length / row_tile_elements) * (num_rows / col_tile_elements);\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, row_tile_elements * itype_size) / itype_size);\n    elements_per_store_c = (std::min(cache_line_size, row_tile_elements * otype_size) / otype_size);\n    elements_per_store_t = (std::min(cache_line_size, col_tile_elements * otype_size) / otype_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load\n      //         + 1/elements_per_store_c\n      //         + 1/elements_per_store_t) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &sc1 = this->elements_per_store_c;\n      const auto &st1 = this->elements_per_store_t;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &sc2 = other.elements_per_store_c;\n      const auto &st2 = other.elements_per_store_t;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * sc1 * st1 * p1 * l2 * sc2 * st2 * p2;\n      const auto cost1 = (scale / l1 + scale / sc1 + scale / st1) / p1;\n      const auto cost2 = (scale / l2 + scale / sc2 + scale / st2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <size_t load_size, size_t store_size, typename IType, typename OType>\n__global__ void __launch_bounds__(block_size) cast_transpose_general_kernel(\n    const IType *__restrict__ const input, const CType *__restrict__ const noop,\n    OType *__restrict__ const output_c, OType *__restrict__ const output_t,\n    const CType *__restrict__ const scale_ptr, CType *__restrict__ const amax_ptr,\n    CType *__restrict__ const scale_inv_ptr, const size_t row_length, const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(IType);\n  constexpr size_t nvec_out = store_size / sizeof(OType);\n  using IVec = Vec<IType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // FP8 factors\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType amax = 0;\n\n  // Load input and store to registers\n  // Note: Each thread loads num_iterations subtiles, computes amax,\n  // casts type, and transposes in registers.\n  OVecT local_output_t[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      if (row < num_rows) {\n#pragma unroll\n        for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n          if (col + j2 < row_length) {\n            const CType in = input[row * row_length + col + j2];\n            const OType out = OType(in * scale);\n            __builtin_assume(amax >= 0);\n            amax = fmaxf(fabsf(in), amax);\n            output_c[row * row_length + col + j2] = out;\n            local_output_t[j2][iter].data.elt[i2] = out;\n          }\n        }\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      if (col < row_length) {\n#pragma unroll\n        for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n          if (row + i2 < num_rows) {\n            output_t[col * num_rows + row + i2] = shared_output_t[j1][i1].data.elt[i2];\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax_ptr != nullptr) {\n    amax = reduce_max<warps_per_tile>(amax, tidy);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax_ptr, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n\n}  // namespace\n\nvoid cast_transpose(const Tensor &input, const Tensor &noop, Tensor *output_, cudaStream_t stream) {\n  Tensor &output = *output_;\n\n  CheckNoopTensor(noop, \"cast_transpose_noop\");\n  CheckInputTensor(input, \"cast_transpose_input\");\n  CheckOutputTensor(output, \"cast_transpose_output\");\n\n  // Check that inputs and outputs are available\n  NVTE_CHECK(input.has_data(), \"Input is not allocated\");\n  NVTE_CHECK(output.has_data(), \"Output rowwise data is not allocated\");\n  NVTE_CHECK(output.has_columnwise_data(), \"Output columnwise is not allocated\");\n\n  // Flatten tensor to 2D\n  NVTE_CHECK(input.data.shape == output.data.shape,\n             \"Input and output shapes do not match (input=\", input.data.shape,\n             \", output=\", output.data.shape);\n  const size_t row_length = input.flat_last_dim();\n  const size_t num_rows = input.flat_first_dim();\n  NVTE_CHECK(output.flat_first_dim() == num_rows && output.flat_last_dim() == row_length,\n             \"Invalid output dimensions (expected \", std::vector<size_t>{num_rows, row_length},\n             \", got \", std::vector<size_t>{output.flat_first_dim(), output.flat_last_dim()}, \")\");\n\n  // Check that cast and transposed output data matches\n  NVTE_CHECK(output.data.dtype == output.columnwise_data.dtype,\n             \"Cast and transposed output types must match.\");\n  NVTE_CHECK(output.scale_inv.dptr == output.columnwise_scale_inv.dptr,\n             \"Cast and transposed outputs need to share scale-inverse tensor.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output.dtype(), OutputType,\n          if (is_tensor_scaling(output.scaling_mode)) {\n            // delayed scaling and current scaling are two variants of per-tensor scaling\n\n            constexpr const char *itype_name = TypeInfo<InputType>::name;\n            constexpr const char *otype_name = TypeInfo<OutputType>::name;\n            constexpr size_t itype_size = sizeof(InputType);\n            constexpr size_t otype_size = sizeof(OutputType);\n\n            // Choose between runtime-compiled or statically-compiled kernel\n            const bool aligned =\n                (row_length % THREADS_PER_WARP == 0 && num_rows % THREADS_PER_WARP == 0);\n            if (aligned && rtc::is_enabled()) {  // Runtime-compiled tuned kernel\n              // Pick kernel config\n              std::vector<KernelConfig> kernel_configs;\n              kernel_configs.reserve(16);\n              const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n              auto add_config = [&](size_t load_size, size_t store_size) {\n                kernel_configs.emplace_back(row_length, num_rows, itype_size, otype_size, load_size,\n                                            store_size, sm_count);\n              };\n              add_config(8, 8);\n              add_config(4, 8);\n              add_config(8, 4);\n              add_config(4, 4);\n              add_config(2, 8);\n              add_config(8, 2);\n              add_config(2, 4);\n              add_config(4, 2);\n              add_config(2, 2);\n              add_config(1, 8);\n              add_config(8, 1);\n              add_config(1, 4);\n              add_config(4, 1);\n              add_config(1, 2);\n              add_config(2, 1);\n              add_config(1, 1);\n              const auto &kernel_config =\n                  *std::min_element(kernel_configs.begin(), kernel_configs.end());\n              NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n              const size_t load_size = kernel_config.load_size;\n              const size_t store_size = kernel_config.store_size;\n              const size_t num_blocks = kernel_config.num_blocks;\n\n              // Compile NVRTC kernel if needed and launch\n              auto &rtc_manager = rtc::KernelManager::instance();\n              const std::string kernel_label = concat_strings(\n                  \"cast_transpose\"\n                  \",itype=\",\n                  itype_name, \",otype=\", otype_name, \",load_size=\", load_size,\n                  \",store_size=\", store_size);\n              if (!rtc_manager.is_compiled(kernel_label)) {\n                std::string code = string_code_transpose_rtc_cast_transpose_cu;\n                code = regex_replace(code, \"__ITYPE__\", itype_name);\n                code = regex_replace(code, \"__OTYPE__\", otype_name);\n                code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n                code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n                code = regex_replace(code, \"__WARPS_PER_TILE__\", warps_per_tile);\n                code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n                rtc_manager.compile(kernel_label, \"cast_transpose_optimized_kernel\", code,\n                                    \"transformer_engine/common/transpose/rtc/cast_transpose.cu\");\n              }\n              rtc_manager.launch(kernel_label, num_blocks, block_size, 0, stream,\n                                 static_cast<const InputType *>(input.data.dptr),\n                                 reinterpret_cast<const CType *>(noop.data.dptr),\n                                 static_cast<OutputType *>(output.data.dptr),\n                                 static_cast<OutputType *>(output.columnwise_data.dptr),\n                                 static_cast<const CType *>(output.scale.dptr),\n                                 static_cast<CType *>(output.amax.dptr),\n                                 static_cast<CType *>(output.scale_inv.dptr), row_length, num_rows);\n            } else {  // Statically-compiled general kernel\n              constexpr size_t load_size = 4;\n              constexpr size_t store_size = 4;\n              constexpr size_t row_tile_size = load_size / itype_size * THREADS_PER_WARP;\n              constexpr size_t col_tile_size = store_size / otype_size * THREADS_PER_WARP;\n              const int num_blocks =\n                  (DIVUP(row_length, row_tile_size) * DIVUP(num_rows, col_tile_size));\n\n              cast_transpose_general_kernel<load_size, store_size, InputType, OutputType>\n                  <<<num_blocks, block_size, 0, stream>>>(\n                      static_cast<const InputType *>(input.data.dptr),\n                      reinterpret_cast<const CType *>(noop.data.dptr),\n                      static_cast<OutputType *>(output.data.dptr),\n                      static_cast<OutputType *>(output.columnwise_data.dptr),\n                      static_cast<const CType *>(output.scale.dptr),\n                      static_cast<CType *>(output.amax.dptr),\n                      static_cast<CType *>(output.scale_inv.dptr), row_length, num_rows);\n              NVTE_CHECK_CUDA(cudaGetLastError());\n            }\n          } else {\n            NVTE_ERROR(\"Not implemented scaling mode: \", to_string(output.scaling_mode));\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\n}  // namespace transformer_engine::detail\n\nvoid nvte_cast_transpose(const NVTETensor input, NVTETensor output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose);\n  using namespace transformer_engine;\n  auto noop = Tensor();\n  transformer_engine::detail::cast_transpose(*convertNVTETensorCheck(input), noop,\n                                             convertNVTETensor(output), stream);\n}\n\nvoid nvte_cast_transpose_with_noop(const NVTETensor input, const NVTETensor noop, NVTETensor output,\n                                   cudaStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_with_noop);\n  using namespace transformer_engine;\n  transformer_engine::detail::cast_transpose(*convertNVTETensorCheck(input),\n                                             *convertNVTETensorCheck(noop),\n                                             convertNVTETensor(output), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/cast_transpose_noop.h>\n#include <transformer_engine/transpose.h>\n\n#include <algorithm>\n\n#include \"../util/rtc.h\"\n#include \"../util/string.h\"\n#include \"../utils.cuh\"\n#include \"cast_transpose.h\"\n\nnamespace transformer_engine::detail {\n\nnamespace {\n\n// String with RTC kernel implementation\n#include \"string_code_transpose_rtc_cast_transpose_cu.h\"\n\n// Hard-coded kernel parameters\nusing CType = float;\nconstexpr size_t warps_per_tile = 4;\nconstexpr size_t block_size = THREADS_PER_WARP * warps_per_tile;\n\n/* Performance heuristics for optimized kernel parameters */\nstruct KernelConfig {\n  /** Vector load size */\n  size_t load_size = 0;\n  /** Vector store size to transposed output */\n  size_t store_size = 0;\n\n  /* Whether config is valid */\n  bool valid = false;\n  /* Number of CUDA blocks */\n  size_t num_blocks = 0;\n\n  /* Number of active SMs */\n  size_t active_sm_count = 0;\n  /* Elements per L1 cache load */\n  size_t elements_per_load = 0;\n  /* Elements per L1 cache store to cast output*/\n  size_t elements_per_store_c = 0;\n  /* Elements per L1 cache store to transposed output */\n  size_t elements_per_store_t = 0;\n\n  KernelConfig(size_t row_length, size_t num_rows, size_t itype_size, size_t otype_size,\n               size_t load_size_, size_t store_size_, size_t sm_count)\n      : load_size{load_size_}, store_size{store_size_} {\n    // Check that tiles are correctly aligned\n    constexpr size_t cache_line_size = 128;\n    if (load_size % itype_size != 0 || store_size % otype_size != 0 ||\n        cache_line_size % itype_size != 0 || cache_line_size % otype_size != 0) {\n      return;\n    }\n    const size_t row_tile_elements = load_size * THREADS_PER_WARP / itype_size;\n    const size_t col_tile_elements = store_size * THREADS_PER_WARP / otype_size;\n    valid = (row_length % row_tile_elements == 0 && num_rows % col_tile_elements == 0);\n    if (!valid) {\n      return;\n    }\n\n    // Number of CUDA blocks\n    num_blocks = (row_length / row_tile_elements) * (num_rows / col_tile_elements);\n\n    // Parameters for performance model\n    constexpr size_t warps_per_sm = 16;  // Rough estimate for saturated SMs\n    active_sm_count = std::min(DIVUP(num_blocks * warps_per_tile, warps_per_sm), sm_count);\n    elements_per_load = (std::min(cache_line_size, row_tile_elements * itype_size) / itype_size);\n    elements_per_store_c = (std::min(cache_line_size, row_tile_elements * otype_size) / otype_size);\n    elements_per_store_t = (std::min(cache_line_size, col_tile_elements * otype_size) / otype_size);\n  }\n\n  /* Compare by estimated cost */\n  bool operator<(const KernelConfig &other) const {\n    if (this->valid && other.valid) {\n      // cost ~ (1/elements_per_load\n      //         + 1/elements_per_store_c\n      //         + 1/elements_per_store_t) / active_sms\n      // Note: Integer arithmetic ensures stable ordering\n      const auto &l1 = this->elements_per_load;\n      const auto &sc1 = this->elements_per_store_c;\n      const auto &st1 = this->elements_per_store_t;\n      const auto &p1 = this->active_sm_count;\n      const auto &l2 = other.elements_per_load;\n      const auto &sc2 = other.elements_per_store_c;\n      const auto &st2 = other.elements_per_store_t;\n      const auto &p2 = other.active_sm_count;\n      const auto scale = l1 * sc1 * st1 * p1 * l2 * sc2 * st2 * p2;\n      const auto cost1 = (scale / l1 + scale / sc1 + scale / st1) / p1;\n      const auto cost2 = (scale / l2 + scale / sc2 + scale / st2) / p2;\n      return cost1 < cost2;\n    } else {\n      return this->valid && !other.valid;\n    }\n  }\n};\n\ntemplate <size_t load_size, size_t store_size, typename IType, typename OType>\n__global__ void __launch_bounds__(block_size) cast_transpose_general_kernel(\n    const IType *__restrict__ const input, const CType *__restrict__ const noop,\n    OType *__restrict__ const output_c, OType *__restrict__ const output_t,\n    const CType *__restrict__ const scale_ptr, CType *__restrict__ const amax_ptr,\n    CType *__restrict__ const scale_inv_ptr, const size_t row_length, const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(IType);\n  constexpr size_t nvec_out = store_size / sizeof(OType);\n  using IVec = Vec<IType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // FP8 factors\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType amax = 0;\n\n  // Load input and store to registers\n  // Note: Each thread loads num_iterations subtiles, computes amax,\n  // casts type, and transposes in registers.\n  OVecT local_output_t[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      if (row < num_rows) {\n#pragma unroll\n        for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n          if (col + j2 < row_length) {\n            const CType in = input[row * row_length + col + j2];\n            const OType out = OType(in * scale);\n            __builtin_assume(amax >= 0);\n            amax = fmaxf(fabsf(in), amax);\n            output_c[row * row_length + col + j2] = out;\n            local_output_t[j2][iter].data.elt[i2] = out;\n          }\n        }\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      if (col < row_length) {\n#pragma unroll\n        for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n          if (row + i2 < num_rows) {\n            output_t[col * num_rows + row + i2] = shared_output_t[j1][i1].data.elt[i2];\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax_ptr != nullptr) {\n    amax = reduce_max<warps_per_tile>(amax, tidy);\n    if (threadIdx.x == 0) {\n      static_assert(std::is_same<CType, float>::value);\n      atomicMaxFloat(amax_ptr, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n\n}  // namespace\n\nvoid cast_transpose(const Tensor &input, const Tensor &noop, Tensor *output_, hipStream_t stream) {\n  Tensor &output = *output_;\n\n  CheckNoopTensor(noop, \"cast_transpose_noop\");\n  CheckInputTensor(input, \"cast_transpose_input\");\n  CheckOutputTensor(output, \"cast_transpose_output\");\n\n  // Check that inputs and outputs are available\n  NVTE_CHECK(input.has_data(), \"Input is not allocated\");\n  NVTE_CHECK(output.has_data(), \"Output rowwise data is not allocated\");\n  NVTE_CHECK(output.has_columnwise_data(), \"Output columnwise is not allocated\");\n\n  // Flatten tensor to 2D\n  NVTE_CHECK(input.data.shape == output.data.shape,\n             \"Input and output shapes do not match (input=\", input.data.shape,\n             \", output=\", output.data.shape);\n  const size_t row_length = input.flat_last_dim();\n  const size_t num_rows = input.flat_first_dim();\n  NVTE_CHECK(output.flat_first_dim() == num_rows && output.flat_last_dim() == row_length,\n             \"Invalid output dimensions (expected \", std::vector<size_t>{num_rows, row_length},\n             \", got \", std::vector<size_t>{output.flat_first_dim(), output.flat_last_dim()}, \")\");\n\n  // Check that cast and transposed output data matches\n  NVTE_CHECK(output.data.dtype == output.columnwise_data.dtype,\n             \"Cast and transposed output types must match.\");\n  NVTE_CHECK(output.scale_inv.dptr == output.columnwise_scale_inv.dptr,\n             \"Cast and transposed outputs need to share scale-inverse tensor.\");\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype(), InputType,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n          output.dtype(), OutputType,\n          if (is_tensor_scaling(output.scaling_mode)) {\n            // delayed scaling and current scaling are two variants of per-tensor scaling\n\n            constexpr const char *itype_name = TypeInfo<InputType>::name;\n            constexpr const char *otype_name = TypeInfo<OutputType>::name;\n            constexpr size_t itype_size = sizeof(InputType);\n            constexpr size_t otype_size = sizeof(OutputType);\n\n            // Choose between runtime-compiled or statically-compiled kernel\n            const bool aligned =\n                (row_length % THREADS_PER_WARP == 0 && num_rows % THREADS_PER_WARP == 0);\n            if (aligned && rtc::is_enabled()) {  // Runtime-compiled tuned kernel\n              // Pick kernel config\n              std::vector<KernelConfig> kernel_configs;\n              kernel_configs.reserve(16);\n              const size_t sm_count = static_cast<size_t>(cuda::sm_count());\n              auto add_config = [&](size_t load_size, size_t store_size) {\n                kernel_configs.emplace_back(row_length, num_rows, itype_size, otype_size, load_size,\n                                            store_size, sm_count);\n              };\n              add_config(8, 8);\n              add_config(4, 8);\n              add_config(8, 4);\n              add_config(4, 4);\n              add_config(2, 8);\n              add_config(8, 2);\n              add_config(2, 4);\n              add_config(4, 2);\n              add_config(2, 2);\n              add_config(1, 8);\n              add_config(8, 1);\n              add_config(1, 4);\n              add_config(4, 1);\n              add_config(1, 2);\n              add_config(2, 1);\n              add_config(1, 1);\n              const auto &kernel_config =\n                  *std::min_element(kernel_configs.begin(), kernel_configs.end());\n              NVTE_CHECK(kernel_config.valid, \"invalid kernel config\");\n              const size_t load_size = kernel_config.load_size;\n              const size_t store_size = kernel_config.store_size;\n              const size_t num_blocks = kernel_config.num_blocks;\n\n              // Compile NVRTC kernel if needed and launch\n              auto &rtc_manager = rtc::KernelManager::instance();\n              const std::string kernel_label = concat_strings(\n                  \"cast_transpose\"\n                  \",itype=\",\n                  itype_name, \",otype=\", otype_name, \",load_size=\", load_size,\n                  \",store_size=\", store_size);\n              if (!rtc_manager.is_compiled(kernel_label)) {\n                std::string code = string_code_transpose_rtc_cast_transpose_cu;\n                code = regex_replace(code, \"__ITYPE__\", itype_name);\n                code = regex_replace(code, \"__OTYPE__\", otype_name);\n                code = regex_replace(code, \"__LOAD_SIZE__\", load_size);\n                code = regex_replace(code, \"__STORE_SIZE__\", store_size);\n                code = regex_replace(code, \"__WARPS_PER_TILE__\", warps_per_tile);\n                code = regex_replace(code, \"__BLOCK_SIZE__\", block_size);\n                rtc_manager.compile(kernel_label, \"cast_transpose_optimized_kernel\", code,\n                                    \"transformer_engine/common/transpose/rtc/cast_transpose.cu\");\n              }\n              rtc_manager.launch(kernel_label, num_blocks, block_size, 0, stream,\n                                 static_cast<const InputType *>(input.data.dptr),\n                                 reinterpret_cast<const CType *>(noop.data.dptr),\n                                 static_cast<OutputType *>(output.data.dptr),\n                                 static_cast<OutputType *>(output.columnwise_data.dptr),\n                                 static_cast<const CType *>(output.scale.dptr),\n                                 static_cast<CType *>(output.amax.dptr),\n                                 static_cast<CType *>(output.scale_inv.dptr), row_length, num_rows);\n            } else {  // Statically-compiled general kernel\n              constexpr size_t load_size = 4;\n              constexpr size_t store_size = 4;\n              constexpr size_t row_tile_size = load_size / itype_size * THREADS_PER_WARP;\n              constexpr size_t col_tile_size = store_size / otype_size * THREADS_PER_WARP;\n              const int num_blocks =\n                  (DIVUP(row_length, row_tile_size) * DIVUP(num_rows, col_tile_size));\n\n              cast_transpose_general_kernel<load_size, store_size, InputType, OutputType>\n                  <<<num_blocks, block_size, 0, stream>>>(\n                      static_cast<const InputType *>(input.data.dptr),\n                      reinterpret_cast<const CType *>(noop.data.dptr),\n                      static_cast<OutputType *>(output.data.dptr),\n                      static_cast<OutputType *>(output.columnwise_data.dptr),\n                      static_cast<const CType *>(output.scale.dptr),\n                      static_cast<CType *>(output.amax.dptr),\n                      static_cast<CType *>(output.scale_inv.dptr), row_length, num_rows);\n              NVTE_CHECK_CUDA(hipGetLastError());\n            }\n          } else {\n            NVTE_ERROR(\"Not implemented scaling mode: \", to_string(output.scaling_mode));\n          });  // NOLINT(*)\n  );           // NOLINT(*)\n}\n\n}  // namespace transformer_engine::detail\n\nvoid nvte_cast_transpose(const NVTETensor input, NVTETensor output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose);\n  using namespace transformer_engine;\n  auto noop = Tensor();\n  transformer_engine::detail::cast_transpose(*convertNVTETensorCheck(input), noop,\n                                             convertNVTETensor(output), stream);\n}\n\nvoid nvte_cast_transpose_with_noop(const NVTETensor input, const NVTETensor noop, NVTETensor output,\n                                   hipStream_t stream) {\n  NVTE_API_CALL(nvte_cast_transpose_with_noop);\n  using namespace transformer_engine;\n  transformer_engine::detail::cast_transpose(*convertNVTETensorCheck(input),\n                                             *convertNVTETensorCheck(noop),\n                                             convertNVTETensor(output), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/cast_transpose.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <iostream>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// Parameters to tune\nconstexpr int n_warps_per_tile = 4;\nconstexpr int threads_per_block = THREADS_PER_WARP * n_warps_per_tile;\nconstexpr int desired_load_size = 8;\nconstexpr int desired_store_size = 8;\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\n\nstruct MultiCastTransposeArgs {\n  // (input) Data buffers for input tensors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for cast output tensors\n  void* output_c_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for transpose output tensors\n  void* output_t_list[kMaxTensorsPerKernel];\n  // (input) Scaling factor for output tensors\n  void* scale_list[kMaxTensorsPerKernel];\n  // (output) AMAX's of input tensors\n  void* amax_list[kMaxTensorsPerKernel];\n  // (output) Inverse of scaling factor for output tensors\n  void* scale_inv_list[kMaxTensorsPerKernel];\n  // Input matrix heights\n  int num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix widths\n  int row_length_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\ntemplate <int nvec_in, int nvec_out, bool aligned, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(threads_per_block)\n    multi_cast_transpose_kernel(MultiCastTransposeArgs args) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVecC = Vec<OType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const IType* input = reinterpret_cast<const IType*>(args.input_list[tensor_id]);\n  OType* output_c = reinterpret_cast<OType*>(args.output_c_list[tensor_id]);\n  OType* output_t = reinterpret_cast<OType*>(args.output_t_list[tensor_id]);\n  const CType* scale_ptr = reinterpret_cast<CType*>(args.scale_list[tensor_id]);\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType* amax_ptr = reinterpret_cast<CType*>(args.amax_list[tensor_id]);\n  CType* scale_inv_ptr = reinterpret_cast<CType*>(args.scale_inv_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  OVecT local_output_t[nvec_in][n_iterations];\n  CType local_amax = 0;\n#pragma unroll\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec_out; ++i2) {\n      const int row = tile_row + i1 * nvec_out + i2;\n      const int col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      OVecC local_output_c;\n      if constexpr (aligned) {\n        local_input.load_from(&input[row * row_length + col]);\n      } else {\n        local_input.clear();\n        if (row < num_rows) {\n#pragma unroll\n          for (int j2 = 0; j2 < nvec_in; ++j2) {\n            if (col + j2 < row_length) {\n              local_input.data.elt[j2] = input[row * row_length + col + j2];\n            }\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec_in; ++j2) {\n        const CType x = CType(local_input.data.elt[j2]);\n        const OType y = OType(scale * x);\n        local_output_c.data.elt[j2] = y;\n        local_output_t[j2][iter].data.elt[i2] = y;\n        __builtin_assume(local_amax >= 0);\n        local_amax = fmaxf(fabsf(x), local_amax);\n      }\n      if constexpr (aligned) {\n        local_output_c.store_to(&output_c[row * row_length + col]);\n      } else {\n        if (row < num_rows) {\n#pragma unroll\n          for (int j2 = 0; j2 < nvec_in; ++j2) {\n            if (col + j2 < row_length) {\n              output_c[row * row_length + col + j2] = local_output_c.data.elt[j2];\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (int j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (int iter = 0; iter < n_iterations; ++iter) {\n      const int i1 = tidy + iter * bdimy;\n      const int j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (int iter = 0; iter < n_iterations; ++iter) {\n      const int i1 = tidx;\n      const int j1 = tidy + iter * bdimy;\n      const int row = tile_row + i1 * nvec_out;\n      const int col = tile_col + j1 * nvec_in + j2;\n      if constexpr (aligned) {\n        shared_output_t[j1][i1].store_to(&output_t[col * num_rows + row]);\n      } else {\n        if (col < row_length) {\n#pragma unroll\n          for (int i2 = 0; i2 < nvec_out; ++i2) {\n            if (row + i2 < num_rows) {\n              output_t[col * num_rows + row + i2] = shared_output_t[j1][i1].data.elt[i2];\n            }\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n\n  // Finalize fp8 factors\n  local_amax = reduce_max<n_warps_per_tile>(local_amax, tidy);\n  if (tid == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    if (amax_ptr != nullptr) atomicMaxFloat(amax_ptr, local_amax);\n  }\n  if (tile_id == 0 && tid == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n\n}  // namespace\n\nvoid multi_cast_transpose(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                          cudaStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType itype = input_list[0]->data.dtype;\n  DType otype = output_list[0]->dtype();\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_cast_transpose_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_cast_transpose_output_\" + std::to_string(tensor_id));\n    //std::cout << *static_cast<char*>(output.data.dptr) << std::endl;\n    NVTE_CHECK(output.has_data() && output.has_columnwise_data(),\n               \"Both rowwise and columnwise output data needs to be allocated.\");\n\n    NVTE_CHECK(input.data.dtype == itype, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == otype, \"C output tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == otype, \"T output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions, but shape is \",\n               input.data.shape);\n    NVTE_CHECK(output.data.shape == input.data.shape, \"C output tensor shape \", output.data.shape,\n               \"does not match input tensor shape \", input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape.size() == 2, \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape[0] == input.data.shape[1], \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape[1] == input.data.shape[0], \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_store_size * 8 / typeToNumBits(otype);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_size * 8 / typeToNumBits(itype);\n\n  // Add tensors to kernel argument struct\n  MultiCastTransposeArgs kernel_args_aligned, kernel_args_unaligned;\n  kernel_args_aligned.num_tensors = 0;\n  kernel_args_aligned.block_range[0] = 0;\n  kernel_args_unaligned.num_tensors = 0;\n  kernel_args_unaligned.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args_aligned.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n          itype, InputType,\n          TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n              otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n              constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n              const int n_blocks = kernel_args_aligned.block_range[kernel_args_aligned.num_tensors];\n              multi_cast_transpose_kernel<nvec_in, nvec_out, true, fp32, InputType, OutputType>\n              <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_aligned););  // NOLINT(*)\n      );                                                                            // NOLINT(*)\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      kernel_args_aligned.num_tensors = 0;\n    }\n    if (kernel_args_unaligned.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n          itype, InputType,\n          TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n              otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n              constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n              const int n_blocks =\n                  kernel_args_unaligned.block_range[kernel_args_unaligned.num_tensors];\n              multi_cast_transpose_kernel<nvec_in, nvec_out, false, fp32, InputType, OutputType>\n              <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_unaligned););  // NOLINT(*)\n      );                                                                              // NOLINT(*)\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      kernel_args_unaligned.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = input_list[tensor_id]->data.shape[0];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Figure out whether to use aligned or unaligned kernel\n    const bool aligned =\n        ((num_tiles_m * tile_dim_m == num_rows) && (num_tiles_n * tile_dim_n == row_length));\n    auto& kernel_args = aligned ? kernel_args_aligned : kernel_args_unaligned;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_c_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.output_t_list[pos] = output_list[tensor_id]->columnwise_data.dptr;\n    kernel_args.scale_list[pos] = output_list[tensor_id]->scale.dptr;\n    kernel_args.amax_list[pos] = output_list[tensor_id]->amax.dptr;\n    kernel_args.scale_inv_list[pos] = output_list[tensor_id]->scale_inv.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args_aligned.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n        itype, InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n            otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n            constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n            const int n_blocks = kernel_args_aligned.block_range[kernel_args_aligned.num_tensors];\n            multi_cast_transpose_kernel<nvec_in, nvec_out, true, fp32, InputType, OutputType>\n            <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_aligned););  // NOLINT(*)\n    );                                                                            // NOLINT(*)\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n  if (kernel_args_unaligned.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n        itype, InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n            otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n            constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n            const int n_blocks =\n                kernel_args_unaligned.block_range[kernel_args_unaligned.num_tensors];\n            multi_cast_transpose_kernel<nvec_in, nvec_out, false, fp32, InputType, OutputType>\n            <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_unaligned););  // NOLINT(*)\n    );                                                                              // NOLINT(*)\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_multi_cast_transpose(size_t num_tensors, const NVTETensor* input_list,\n                               NVTETensor* output_list, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_cast_transpose);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n  }\n  multi_cast_transpose(input_list_, output_list_, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <transformer_engine/transpose.h>\n\n#include <cfloat>\n#include <iostream>\n#include <vector>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\nnamespace {\n\n// Parameters to tune\nconstexpr int n_warps_per_tile = 4;\nconstexpr int threads_per_block = THREADS_PER_WARP * n_warps_per_tile;\nconstexpr int desired_load_size = 8;\nconstexpr int desired_store_size = 8;\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB\n\nstruct MultiCastTransposeArgs {\n  // (input) Data buffers for input tensors\n  void* input_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for cast output tensors\n  void* output_c_list[kMaxTensorsPerKernel];\n  // (output) Data buffers for transpose output tensors\n  void* output_t_list[kMaxTensorsPerKernel];\n  // (input) Scaling factor for output tensors\n  void* scale_list[kMaxTensorsPerKernel];\n  // (output) AMAX's of input tensors\n  void* amax_list[kMaxTensorsPerKernel];\n  // (output) Inverse of scaling factor for output tensors\n  void* scale_inv_list[kMaxTensorsPerKernel];\n  // Input matrix heights\n  int num_rows_list[kMaxTensorsPerKernel];\n  // Input matrix widths\n  int row_length_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of CUDA blocks needed for each\n  // tensor\n  int block_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors being processed by kernel\n  int num_tensors;\n};\n\ntemplate <int nvec_in, int nvec_out, bool aligned, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(threads_per_block)\n    multi_cast_transpose_kernel(MultiCastTransposeArgs args) {\n  using IVec = Vec<IType, nvec_in>;\n  using OVecC = Vec<OType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr int bdimx = THREADS_PER_WARP;\n  constexpr int bdimy = n_warps_per_tile;\n  const int tid = threadIdx.x;\n  const int tidx = tid % bdimx;\n  const int tidy = tid / bdimx;\n  const int bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr int tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr int tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr int n_iterations = THREADS_PER_WARP / n_warps_per_tile;\n\n  // Find tensor corresponding to block\n  int tensor_id = 0;\n  while (args.block_range[tensor_id + 1] <= bid) {\n    ++tensor_id;\n  }\n  const IType* input = reinterpret_cast<const IType*>(args.input_list[tensor_id]);\n  OType* output_c = reinterpret_cast<OType*>(args.output_c_list[tensor_id]);\n  OType* output_t = reinterpret_cast<OType*>(args.output_t_list[tensor_id]);\n  const CType* scale_ptr = reinterpret_cast<CType*>(args.scale_list[tensor_id]);\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType* amax_ptr = reinterpret_cast<CType*>(args.amax_list[tensor_id]);\n  CType* scale_inv_ptr = reinterpret_cast<CType*>(args.scale_inv_list[tensor_id]);\n  const int num_rows = args.num_rows_list[tensor_id];\n  const int row_length = args.row_length_list[tensor_id];\n\n  // Find position of tile within tensor\n  const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n  const int tile_id = bid - args.block_range[tensor_id];\n  const int tile_id_m = tile_id / num_tiles_n;\n  const int tile_id_n = tile_id % num_tiles_n;\n  const int tile_row = tile_id_m * tile_dim_m;\n  const int tile_col = tile_id_n * tile_dim_n;\n\n  // Load input and store to registers\n  // Note: Each thread loads n_iterations subtiles, casts to output\n  // type, and transposes in registers.\n  OVecT local_output_t[nvec_in][n_iterations];\n  CType local_amax = 0;\n#pragma unroll\n  for (int iter = 0; iter < n_iterations; ++iter) {\n    const int i1 = tidy + iter * bdimy;\n    const int j1 = tidx;\n#pragma unroll\n    for (int i2 = 0; i2 < nvec_out; ++i2) {\n      const int row = tile_row + i1 * nvec_out + i2;\n      const int col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      OVecC local_output_c;\n      if constexpr (aligned) {\n        local_input.load_from(&input[row * row_length + col]);\n      } else {\n        local_input.clear();\n        if (row < num_rows) {\n#pragma unroll\n          for (int j2 = 0; j2 < nvec_in; ++j2) {\n            if (col + j2 < row_length) {\n              local_input.data.elt[j2] = input[row * row_length + col + j2];\n            }\n          }\n        }\n      }\n#pragma unroll\n      for (int j2 = 0; j2 < nvec_in; ++j2) {\n        const CType x = CType(local_input.data.elt[j2]);\n        const OType y = OType(scale * x);\n        local_output_c.data.elt[j2] = y;\n        local_output_t[j2][iter].data.elt[i2] = y;\n        __builtin_assume(local_amax >= 0);\n        local_amax = fmaxf(fabsf(x), local_amax);\n      }\n      if constexpr (aligned) {\n        local_output_c.store_to(&output_c[row * row_length + col]);\n      } else {\n        if (row < num_rows) {\n#pragma unroll\n          for (int j2 = 0; j2 < nvec_in; ++j2) {\n            if (col + j2 < row_length) {\n              output_c[row * row_length + col + j2] = local_output_c.data.elt[j2];\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // Copy transposed output from registers to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (int j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (int iter = 0; iter < n_iterations; ++iter) {\n      const int i1 = tidy + iter * bdimy;\n      const int j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (int iter = 0; iter < n_iterations; ++iter) {\n      const int i1 = tidx;\n      const int j1 = tidy + iter * bdimy;\n      const int row = tile_row + i1 * nvec_out;\n      const int col = tile_col + j1 * nvec_in + j2;\n      if constexpr (aligned) {\n        shared_output_t[j1][i1].store_to(&output_t[col * num_rows + row]);\n      } else {\n        if (col < row_length) {\n#pragma unroll\n          for (int i2 = 0; i2 < nvec_out; ++i2) {\n            if (row + i2 < num_rows) {\n              output_t[col * num_rows + row + i2] = shared_output_t[j1][i1].data.elt[i2];\n            }\n          }\n        }\n      }\n    }\n    __syncthreads();\n  }\n\n  // Finalize fp8 factors\n  local_amax = reduce_max<n_warps_per_tile>(local_amax, tidy);\n  if (tid == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    if (amax_ptr != nullptr) atomicMaxFloat(amax_ptr, local_amax);\n  }\n  if (tile_id == 0 && tid == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n\n}  // namespace\n\nvoid multi_cast_transpose(const std::vector<Tensor*> input_list, std::vector<Tensor*> output_list,\n                          hipStream_t stream) {\n  // Check that number of tensors is valid\n  NVTE_CHECK(output_list.size() == input_list.size(),\n             \"Number of input and output tensors must match\");\n  if (input_list.empty()) {\n    return;\n  }\n\n  // Check that tensor properties are valid\n  DType itype = input_list[0]->data.dtype;\n  DType otype = output_list[0]->dtype();\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    const auto& input = *input_list[tensor_id];\n    const auto& output = *output_list[tensor_id];\n    CheckInputTensor(input, \"multi_cast_transpose_input_\" + std::to_string(tensor_id));\n    CheckInputTensor(output, \"multi_cast_transpose_output_\" + std::to_string(tensor_id));\n    //std::cout << *static_cast<char*>(output.data.dptr) << std::endl;\n    NVTE_CHECK(output.has_data() && output.has_columnwise_data(),\n               \"Both rowwise and columnwise output data needs to be allocated.\");\n\n    NVTE_CHECK(input.data.dtype == itype, \"Input tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == otype, \"C output tensor types do not match.\");\n    NVTE_CHECK(output.data.dtype == otype, \"T output tensor types do not match.\");\n\n    NVTE_CHECK(input.data.shape.size() == 2, \"Input tensor must have 2 dimensions, but shape is \",\n               input.data.shape);\n    NVTE_CHECK(output.data.shape == input.data.shape, \"C output tensor shape \", output.data.shape,\n               \"does not match input tensor shape \", input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape.size() == 2, \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape[0] == input.data.shape[1], \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n    NVTE_CHECK(output.columnwise_data.shape[1] == input.data.shape[0], \"T output tensor shape \",\n               output.columnwise_data.shape, \"does not match input tensor shape \",\n               input.data.shape);\n  }\n\n  // Input matrices are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  const int tile_dim_m = THREADS_PER_WARP * desired_store_size * 8 / typeToNumBits(otype);\n  const int tile_dim_n = THREADS_PER_WARP * desired_load_size * 8 / typeToNumBits(itype);\n\n  // Add tensors to kernel argument struct\n  MultiCastTransposeArgs kernel_args_aligned, kernel_args_unaligned;\n  kernel_args_aligned.num_tensors = 0;\n  kernel_args_aligned.block_range[0] = 0;\n  kernel_args_unaligned.num_tensors = 0;\n  kernel_args_unaligned.block_range[0] = 0;\n  for (size_t tensor_id = 0; tensor_id < input_list.size(); ++tensor_id) {\n    // Launch kernel if argument struct is full\n    if (kernel_args_aligned.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n          itype, InputType,\n          TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n              otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n              constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n              const int n_blocks = kernel_args_aligned.block_range[kernel_args_aligned.num_tensors];\n              multi_cast_transpose_kernel<nvec_in, nvec_out, true, fp32, InputType, OutputType>\n              <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_aligned););  // NOLINT(*)\n      );                                                                            // NOLINT(*)\n      NVTE_CHECK_CUDA(hipGetLastError());\n      kernel_args_aligned.num_tensors = 0;\n    }\n    if (kernel_args_unaligned.num_tensors == kMaxTensorsPerKernel) {\n      TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n          itype, InputType,\n          TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n              otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n              constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n              const int n_blocks =\n                  kernel_args_unaligned.block_range[kernel_args_unaligned.num_tensors];\n              multi_cast_transpose_kernel<nvec_in, nvec_out, false, fp32, InputType, OutputType>\n              <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_unaligned););  // NOLINT(*)\n      );                                                                              // NOLINT(*)\n      NVTE_CHECK_CUDA(hipGetLastError());\n      kernel_args_unaligned.num_tensors = 0;\n    }\n\n    // Calculate number of thread blocks needed for tensor\n    const int num_rows = input_list[tensor_id]->data.shape[0];\n    const int row_length = input_list[tensor_id]->data.shape[1];\n    const int num_tiles_m = (num_rows + tile_dim_m - 1) / tile_dim_m;\n    const int num_tiles_n = (row_length + tile_dim_n - 1) / tile_dim_n;\n    const int num_tiles = num_tiles_m * num_tiles_n;\n\n    // Figure out whether to use aligned or unaligned kernel\n    const bool aligned =\n        ((num_tiles_m * tile_dim_m == num_rows) && (num_tiles_n * tile_dim_n == row_length));\n    auto& kernel_args = aligned ? kernel_args_aligned : kernel_args_unaligned;\n\n    // Add tensor to kernel argument struct\n    const int pos = kernel_args.num_tensors;\n    kernel_args.input_list[pos] = const_cast<void*>(input_list[tensor_id]->data.dptr);\n    kernel_args.output_c_list[pos] = output_list[tensor_id]->data.dptr;\n    kernel_args.output_t_list[pos] = output_list[tensor_id]->columnwise_data.dptr;\n    kernel_args.scale_list[pos] = output_list[tensor_id]->scale.dptr;\n    kernel_args.amax_list[pos] = output_list[tensor_id]->amax.dptr;\n    kernel_args.scale_inv_list[pos] = output_list[tensor_id]->scale_inv.dptr;\n    kernel_args.num_rows_list[pos] = num_rows;\n    kernel_args.row_length_list[pos] = row_length;\n    kernel_args.block_range[pos + 1] = kernel_args.block_range[pos] + num_tiles;\n    kernel_args.num_tensors++;\n  }\n\n  // Launch kernel\n  if (kernel_args_aligned.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n        itype, InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n            otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n            constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n            const int n_blocks = kernel_args_aligned.block_range[kernel_args_aligned.num_tensors];\n            multi_cast_transpose_kernel<nvec_in, nvec_out, true, fp32, InputType, OutputType>\n            <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_aligned););  // NOLINT(*)\n    );                                                                            // NOLINT(*)\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n  if (kernel_args_unaligned.num_tensors > 0) {\n    TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n        itype, InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_OUTPUT(\n            otype, OutputType, constexpr int nvec_in = desired_load_size / sizeof(InputType);\n            constexpr int nvec_out = desired_store_size / sizeof(OutputType);\n            const int n_blocks =\n                kernel_args_unaligned.block_range[kernel_args_unaligned.num_tensors];\n            multi_cast_transpose_kernel<nvec_in, nvec_out, false, fp32, InputType, OutputType>\n            <<<n_blocks, threads_per_block, 0, stream>>>(kernel_args_unaligned););  // NOLINT(*)\n    );                                                                              // NOLINT(*)\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_multi_cast_transpose(size_t num_tensors, const NVTETensor* input_list,\n                               NVTETensor* output_list, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_cast_transpose);\n  using namespace transformer_engine;\n  std::vector<Tensor*> input_list_, output_list_;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    input_list_.push_back(convertNVTETensorCheck(input_list[i]));\n    output_list_.push_back(convertNVTETensorCheck(output_list[i]));\n  }\n  multi_cast_transpose(input_list_, output_list_, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/multi_cast_transpose.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_runtime.h>\n\n#include <algorithm>\n#include <cfloat>\n#include <cuda/barrier>\n#include <utility>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/transpose/cast_transpose.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n\nnamespace transformer_engine {\n\n#if CUDA_VERSION >= 12080\nnamespace quantize_transpose_nvfp4 {\nnamespace {\n\nusing std::int32_t;\nusing std::uint32_t;\nusing std::uint8_t;\n\nusing transformer_engine::detail::TypeExtrema;\n\n// clang-format off\n/*\n\nStep 1: Load input to shared memory\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 8 times\n* What each thread does in each loop:\n    * 8 elements are read from the input at a time\n    * 2 elements are written to the shared memory at a time, for a total of 4 times\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|  T0   |  T1   |  T2   |  T3   |  T4   |  T5   |  T6   |  T7   |  T8   |  T9   |  T10  |  T11  |  T12  |  T13  |  T14  |  T15  |\n|  T16  |  T17  |  T18  |  T19  |  T20  |  T21  |  T22  |  T23  |  T24  |  T25  |  T26  |  T27  |  T28  |  T29  |  T30  |  T31  |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 8 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 2: Cast and store to output_c\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 4 times\n* What each thread does in each loop:\n    * 2 elements are read from the shared memory at a time, for a total of 8 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each row\n    * 16 elements are quantized and write to output_c at a time\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|      T0       |      T1       |      T2       |      T3       |      T4       |      T5       |      T6       |      T7       |\n|      T8       |      T9       |      T10      |      T11      |      T12      |      T13      |      T14      |      T15      |\n|      T16      |      T17      |      T18      |      T19      |      T20      |      T21      |      T22      |      T23      |\n|      T24      |      T25      |      T26      |      T27      |      T28      |      T29      |      T30      |      T31      |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 4 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 3: Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 2 times\n* What each thread does in each loop:\n    * 2 elements (in a row) are read from the shared memory at a time, for a total of 16 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each column\n    * 16 elements are quantized and write to output_c at a time, for a total of 2 times\n+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+\n| T0  | T8  | T16 | T24 |                       |                       |                       | T0  | T8  | T16 | T24 |                       |                       |                       |\n| T1  | T9  | T17 | T25 |                       |                       |                       | T1  | T9  | T17 | T25 |                       |                       |                       |\n| T2  | T10 | T18 | T26 |                       |                       |                       | T2  | T10 | T18 | T26 |                       |                       |                       |\n| T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         | T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         |\n| T4  | T12 | T20 | T28 |                       |                       |                       | T4  | T12 | T20 | T28 |                       |                       |                       |\n| T5  | T13 | T21 | T29 |                       |                       |                       | T5  | T13 | T21 | T29 |                       |                       |                       |\n| T6  | T14 | T22 | T30 |                       |                       |                       | T6  | T14 | T22 | T30 |                       |                       |                       |\n| T7  | T15 | T23 | T31 |                       |                       |                       | T7  | T15 | T23 | T31 |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+\n\n*/\n// clang-format on\n\nconstexpr int kThreadsPerWarp = 32;\n\n// for fp4, we use uint8_t to store 2 fp4 numbers\nconstexpr int kNFP4PerContainer = 2;\n\n// Hyperparameters for performance tuning\nconstexpr int kTileDim = 128;\n// constexpr int kScaleDim = 32;\nconstexpr int kNVecIn = 8;             // The number of elements each LDG touches\nconstexpr int kNVecOut = 16;           // The number of elements each STG touches\nconstexpr int kNVecSMem = 2;           // The number of elements each LDS/STS touches\nconstexpr int kThreadsPerBlock = 256;  // Thread block size, 8 warps in total\n\n// Auto-calculated constants, do not modify directly)\nstatic_assert(kNVecIn % kNVecSMem == 0, \"kNVecIn must be divisible by kNVecSMem\");\nstatic_assert(kNVecOut % kNVecSMem == 0, \"kNVecOut must be divisible by kNVecSMem\");\nconstexpr int kSMemRow = kTileDim;\nconstexpr int kSMemCol = (kTileDim / kNVecSMem) + 1;\nconstexpr int kSMemSize = kSMemRow * kSMemCol * kNVecSMem;\nconstexpr int kNumThreadsLoad = kTileDim / kNVecIn;    // 16\nconstexpr int kNumThreadsStore = kTileDim / kNVecOut;  // 8\n// constexpr int kNumThreadsReduce = kScaleDim / kNVecOut;\nstatic_assert(kNumThreadsLoad <= kThreadsPerWarp, \"kNumThreadsLoad must be <= kThreadsPerWarp\");\nstatic_assert(kNumThreadsStore <= kThreadsPerWarp, \"kNumThreadsStore must be <= kThreadsPerWarp\");\n\n// for 2D block scaling, we need to reduce amax in warp\nstatic __device__ constexpr unsigned int WARP_REDUCE_AMAX_GROUP_MASKS[8] = {\n    0x01010101, 0x02020202, 0x04040404, 0x08080808, 0x10101010, 0x20202020, 0x40404040, 0x80808080};\n\n// max for every group_size elements in warp\ntemplate <int group_size, int shfl_down_stride>\n__device__ __forceinline__ float groupMax(float val, unsigned int groupMask) {\n  for (int offset = group_size / 2; offset > 0; offset /= 2) {\n    val = max(val, __shfl_down_sync(groupMask, val, offset * shfl_down_stride));\n  }\n  return val;\n}\n\ntemplate <typename ScaleType>\n__device__ __forceinline__ ScaleType\nComputeDecodeScaleFP4(const float amax, const float global_encode_scale_multiplier) {\n  float decode_scale = amax * global_encode_scale_multiplier;\n  decode_scale = fminf(decode_scale, TypeExtrema<float>::max);\n  return static_cast<ScaleType>(decode_scale);\n}\n\ntemplate <typename ScaleType>\n__device__ __forceinline__ float ComputeEncodeScaleFP4(ScaleType decode_scale,\n                                                       const float global_decode_scale) {\n  return fminf(1.0f / (static_cast<float>(decode_scale) * global_decode_scale),\n               TypeExtrema<float>::max);\n}\n\ntemplate <typename IType, typename ScaleType>\n__device__ __forceinline__ float ComputeOutputFP4(IType input, float encode_scale) {\n  return static_cast<float>(input) * encode_scale;\n}\n\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float fp8_max = TypeExtrema<fp8e4m3>::max;\n  constexpr float fp4_max = TypeExtrema<fp4e2m1>::max;\n  float global_encode_scale = fp8_max * fp4_max / global_amax;\n  // If scale is infinity, return max value of float32\n  global_encode_scale = fminf(global_encode_scale, TypeExtrema<float>::max);\n  // If global amax is 0 or infinity, return 1\n  if (global_amax == 0.f || global_encode_scale == 0.f) {\n    return 1.f;\n  }\n  return global_encode_scale;\n}\n\n__device__ __forceinline__ uint32_t get_rbits(\n    transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS>&\n        rng,  // NVTE_BUILD_NUM_PHILOX_ROUNDS rounds of philox4x32\n    uint4& random_uint4, int& rnd_idx) {\n  if (rnd_idx == 4) {\n    rnd_idx = 0;\n    random_uint4 = rng.generate4();\n  }\n\n  // Treat uint4 as an array of 4x uint32_t elements for indexing\n  const uint32_t* const rbits_arr = reinterpret_cast<uint32_t*>(&random_uint4);\n  const uint32_t rbits = rbits_arr[rnd_idx++];\n  return rbits;\n}\n\ntemplate <class ScaleType>\n__device__ __forceinline__ size_t scale_factor_swizzled_offset(size_t row_idx, size_t col_idx,\n                                                               uint32_t col_length) {\n  // This function takes in indices from the scale factor matrix and returns an offset in the\n  // swizzled format. row_idx, col_idx are original indices from the scale factor matrix (unswizzled\n  // index). col_length is the column length of the scale factor matrix. tile_scales_inv is the\n  // pointer to the scale factor matrix.\n\n  // https://github.com/NVIDIA/cutlass/blob/main/media/docs/cpp/blackwell_functionality.md#scale-factor-layouts\n  // For any scale factor matrix, it's 512B base block. Each base block consists of 128 rows and 4\n  // columns. Base block is divided into 4 column blocks, each column block has 32 rows and 4\n  // columns.\n\n  // NOTE: There are not a lot of good illustrations about the swizzled scale factor matrix.\n  // To think in high level, the swizzled scale factor matrix could be composed as:\n  // unswizzled_scale_factor_matrix = torch.empty((M, N // 16), dtype=torch.uint8)\n  // cbg_cnt = N // 16 // 4  # Assuming N is divisible by 64\n  // rb_cnt = M // 128  # Assuming M is divisible by 128\n  // tmp = unswizzled_scale_factor_matrix.reshape(rb_cnt, 4, 32, cbg_cnt, 4)\n  // tmp = torch.permute(tmp, (0, 3, 2, 1, 4))\n  // swizzled_scale_factor_matrix = tmp.reshape((-1, 128, 4))\n\n  constexpr uint32_t kTotalRowsPerBaseBlock = 128;\n  constexpr uint32_t kRowsPerBaseBlockCol = 32;\n  constexpr uint32_t kColsPerBaseBlockCol = 4;\n\n  const size_t rb = row_idx / kTotalRowsPerBaseBlock;\n  const size_t rem = row_idx % kTotalRowsPerBaseBlock;\n  const size_t d4 = rem / kRowsPerBaseBlockCol;\n  const size_t d3 = rem % kRowsPerBaseBlockCol;\n  const size_t cbg = col_idx / kColsPerBaseBlockCol;\n  const size_t d5 = col_idx % kColsPerBaseBlockCol;\n\n  const size_t cbg_cnt = DIVUP(col_length, kColsPerBaseBlockCol);\n  // row-major offset in the logical shape\n  // (rb_cnt , cbg_cnt , 32 , 4 , 4)\n  // Magic number 16 below comes from the fact we have kColsPerBaseBlockCol = 4, and d4 ([0-128] /\n  // 32 = [0-4])\n  return ((rb * cbg_cnt + cbg) * kRowsPerBaseBlockCol + d3) * 16 + d4 * kColsPerBaseBlockCol + d5;\n}\n\n__device__ __forceinline__ __nv_fp4x4_e2m1 cvt_fp32_to_fp4_4x_with_stochastic_rounding(\n    const float2 in01, const float2 in23, const uint32_t rbits) {\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    uint16_t out_4x;\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32 %0, {%3, %4, %1, %2}, %5; \\n\\t\"\n        \"}\"\n        : \"=h\"(out_4x)\n        : \"f\"(in01.y), \"f\"(in01.x), \"f\"(in23.y), \"f\"(in23.x), \"r\"(rbits));\n    return *reinterpret_cast<__nv_fp4x4_e2m1*>(&out_4x);\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt.rs PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n    uint16_t dummy = 0;\n    return *reinterpret_cast<__nv_fp4x4_e2m1*>(&dummy);\n  }\n}\n\n__device__ __forceinline__ __nv_fp4x4_e2m1 cvt_fp32_to_fp4_4x_with_rn(const float2 in01,\n                                                                      const float2 in23,\n                                                                      const uint32_t rbits) {\n  constexpr bool has_fp4 = ARCH_BLACKWELL_FAMILY;\n  if constexpr (has_fp4) {\n    // NOTE: rbits unused for rn.\n    uint32_t out_4x;  // Only need 16 bit. Using 32 bit container for packing.\n    asm volatile(\n        \"{\\n\"\n        \".reg.b8 f0; \\n\\t\"\n        \".reg.b8 f1; \\n\\t\"\n        \"cvt.rn.satfinite.e2m1x2.f32 f0, %1, %2;\\n\\t\"\n        \"cvt.rn.satfinite.e2m1x2.f32 f1, %3, %4;\\n\\t\"\n        \"mov.b32 %0, {f0, f1, f0, f1};\\n\\t\"\n        \"}\"\n        : \"=r\"(out_4x)\n        : \"f\"(in01.y), \"f\"(in01.x), \"f\"(in23.y), \"f\"(in23.x));\n    return reinterpret_cast<__nv_fp4x4_e2m1*>(&out_4x)[0];\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n    uint16_t dummy = 0;\n    return *reinterpret_cast<__nv_fp4x4_e2m1*>(&dummy);\n  }\n}\n\ntemplate <bool kApplyStochasticRounding>\n__device__ __forceinline__ __nv_fp4x4_e2m1 cvt_fp32_to_fp4_4x(const float2 in01, const float2 in23,\n                                                              const uint32_t rbits) {\n  if constexpr (kApplyStochasticRounding) {\n    return cvt_fp32_to_fp4_4x_with_stochastic_rounding(in01, in23, rbits);\n  } else {\n    return cvt_fp32_to_fp4_4x_with_rn(in01, in23, rbits);\n  }\n}\n\ntemplate <bool kReturnIdentity, bool kReturnTranspose, bool kIsE8Scaling, bool kAligned,\n          typename CType, typename IType, typename OType, typename ScaleType, bool kSwizzledScale,\n          bool kApplyStochasticRounding, bool kIs2DBlockScaling, bool kRowScaledNVFP4>\n__global__ void __launch_bounds__(kThreadsPerBlock) block_scaled_1d_cast_transpose_kernel(\n    const IType* const input, const float* global_amax, OType* const output_c,\n    OType* const output_t, ScaleType* const tile_scales_inv_c, ScaleType* const tile_scales_inv_t,\n    const size_t row_length, const size_t num_rows, const size_t scale_stride_x,\n    const size_t scale_stride_y, const size_t scale_t_stride_x, const size_t scale_t_stride_y,\n    const size_t kScaleBlockDim, const float epsilon, const size_t* rng_state,\n    const float* noop_ptr) {\n  constexpr int kNVecContainer = kNVecOut / kNFP4PerContainer;\n  using SMemVec = Vec<IType, kNVecSMem>;\n  using OVec = Vec<OType, kNVecContainer>;\n  union IVec {\n    Vec<IType, kNVecIn> input_type;\n    Vec<SMemVec, kNVecIn / kNVecSMem> smem_type;\n  };\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  const size_t block_idx_x = blockIdx.x;\n  const size_t block_idx_y = blockIdx.y;\n  const size_t rng_sequence =\n      threadIdx.x + block_idx_x * kThreadsPerBlock + block_idx_y * gridDim.x * kThreadsPerBlock;\n  const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n  const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n\n  transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n  rng.init(rng_seed, rng_sequence, rng_offset);\n  uint4 random_uint4 = kApplyStochasticRounding ? rng.generate4() : uint4{0, 0, 0, 0};\n\n  int rnd_idx =\n      0;  // Index of the random number. It increments each time when used and resets to 0 if reaches 4x\n\n  extern __shared__ char smem_base[];\n  SMemVec* smem = reinterpret_cast<SMemVec*>(&smem_base[0]);\n\n  // 2D block scaling is not supported for E8 scaling MXFP4 or for colwise only mode.\n  // Instead of static_assert, return early if these invalid modes are detected.\n  if constexpr (kIs2DBlockScaling && kIsE8Scaling) {\n    return;\n  }\n  if constexpr (kIs2DBlockScaling && !kReturnIdentity) {\n    return;\n  }\n  // for 128x128 block, 2D block scaling means there will be 8x8 amax values for nvfp4, 4x4 for 2D mxfp4\n  // use constexpr to define the size, when not using 2D, use minimal size 1x1\n  constexpr int kFP4BlockScalingSize = 16;\n  constexpr int k2DBlockAmaxDim = kIs2DBlockScaling ? (kTileDim / kFP4BlockScalingSize) : 1;\n  constexpr int kNumRowsPerWarp = kThreadsPerWarp / kNumThreadsStore;  // 4\n  constexpr int k2DBlockAmaxReduceDim =\n      kIs2DBlockScaling ? (kFP4BlockScalingSize / kNumRowsPerWarp) : 1;\n  __shared__ CType amax_smem_red[k2DBlockAmaxDim][k2DBlockAmaxDim][k2DBlockAmaxReduceDim];\n  __shared__ CType amax_smem[k2DBlockAmaxDim][k2DBlockAmaxDim];\n\n  // Step 1: Load input to shared memory\n  {\n    constexpr int r_stride = kThreadsPerBlock / kNumThreadsLoad;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsLoad) * (kNVecIn / kNVecSMem);         // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsLoad;                             // Row in shared memory\n    const size_t c_g = block_idx_x * kTileDim + c_s * kNVecSMem;         // Column in global memory\n    size_t r_g = block_idx_y * kTileDim + r_s;                           // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = (c_g < row_length ? min(static_cast<size_t>(kNVecIn), row_length - c_g)\n                                             : 0);          // For not aligned case\n    const IType* input_g = &input[r_g * row_length + c_g];  // Input address in global memory\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      IVec input_vec;\n      // Step 1.1: Load from global memory (input) to registers\n      if constexpr (kAligned) {\n        input_vec.input_type.load_from(input_g);\n      } else {\n        if (r_g < num_rows) {\n          input_vec.input_type.load_from_elts(input_g, 0, num_ele);\n        } else {\n          input_vec.input_type.clear();\n        }\n      }\n      // Step 1.2: Write to shared memory\n#pragma unroll\n      for (int i = 0; i < kNVecIn / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem[r * kSMemCol + c] = input_vec.smem_type.data.elt[i];\n      }\n      // Step 1.3: Update input address, row index of shared memory, (and row index of global memory\n      // for not aligned case)\n      input_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  __syncthreads();\n\n  const int kNumThreadsReduce = kScaleBlockDim / kNVecOut;\n  const float global_encode_scale =\n      kIsE8Scaling ? 1.0f : ComputeGlobalEncodeScaleFP4(global_amax[0]);\n  constexpr float fp4_max_inv = 1.0f / TypeExtrema<fp4e2m1>::max;\n  const float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n  const float global_decode_scale = 1.0 / global_encode_scale;\n\n  // Step 2: Cast and store to output_c\n  if constexpr (kReturnIdentity) {\n    constexpr int r_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsStore) * (kNVecOut / kNVecSMem);       // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsStore;                            // Row in shared memory\n    const size_t c_g = block_idx_x * kTileDim + c_s * kNVecSMem;         // Column in global memory\n    size_t r_g = block_idx_y * kTileDim + r_s;                           // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele =\n        (c_g < row_length ? min(static_cast<size_t>(kNVecOut / kNFP4PerContainer),\n                                (row_length - c_g) / kNFP4PerContainer)\n                          : 0);  // For not aligned case\n    OType* output_g =\n        &output_c[(r_g * row_length + c_g) / kNFP4PerContainer];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane =\n        (threadIdx.x % kThreadsPerWarp) / kNumThreadsReduce * kNumThreadsReduce;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsReduce) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsReduce) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut / kNVecSMem];\n      // Step 2.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n      // Step 2.2: Compute local amax\n      CType amax = 0;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          __builtin_assume(amax >= 0);\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[j]));\n        }\n      }\n      // Step 2.3: Reduce amax\n      if constexpr (kIsE8Scaling) {\n#pragma unroll\n        for (int delta = kNumThreadsReduce / 2; delta > 0; delta /= 2) {\n          const float other_amax = __shfl_down_sync(mask, amax, delta);\n          __builtin_assume(amax >= 0);\n          __builtin_assume(other_amax >= 0);\n          amax = fmaxf(amax, other_amax);\n        }\n        amax = __shfl_sync(mask, amax, src_lane);\n      }\n      // doing shuffle sync for 2D block scaling (not applicable for E8 scaling)\n      if constexpr (kIs2DBlockScaling) {\n        // first amax shuffle sync in warp, then reduce in smem\n        // T0 T8 T16 T24 should do amax reduction together\n        constexpr int kNumRowsPerIter = kThreadsPerBlock / kNumThreadsStore;  // 32\n        int warp_idx = threadIdx.x / kThreadsPerWarp;                         // 0 ~ 7\n        int tid_in_warp_x = threadIdx.x % kNumThreadsStore;\n        int tid_in_warp_y = (threadIdx.x / kNumThreadsStore) % kNumRowsPerWarp;\n        CType amax_warp_reduced = groupMax<kNumRowsPerWarp, kNumThreadsStore>(\n            amax, WARP_REDUCE_AMAX_GROUP_MASKS[tid_in_warp_x]);\n        // now T0 ~ T8 in each warp has the reduced amax values\n        int data_row_idx = iter * kNumRowsPerIter + warp_idx * kNumRowsPerWarp + tid_in_warp_y;\n        if (tid_in_warp_y == 0) {\n          amax_smem_red[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x]\n                       [warp_idx % k2DBlockAmaxReduceDim] = amax_warp_reduced;\n        }\n        __syncthreads();\n\n        if (data_row_idx % kFP4BlockScalingSize == 0) {\n          CType amax_2d = 0.0;\n          for (int i = 0; i < k2DBlockAmaxReduceDim; i++) {\n            amax_2d = fmaxf(amax_2d,\n                            amax_smem_red[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x][i]);\n          }\n          amax_smem[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x] = amax_2d;\n        }\n        __syncthreads();\n        // every thread now knows 2D amax\n        amax = amax_smem[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x];\n      }\n      // Step 2.4: Compute scale\n      const size_t row_idx = block_idx_y * kTileDim + r_s;\n      float row_global_encode_scale = global_encode_scale;\n      if constexpr (kRowScaledNVFP4) {\n        row_global_encode_scale =\n            row_idx < num_rows ? ComputeGlobalEncodeScaleFP4(global_amax[row_idx]) : 1.0f;\n      }\n      const float row_global_encode_scale_multiplier =\n          kRowScaledNVFP4 ? row_global_encode_scale * fp4_max_inv : global_encode_scale_multiplier;\n      const float row_global_decode_scale =\n          kRowScaledNVFP4 ? 1.0f / row_global_encode_scale : global_decode_scale;\n      ScaleType scale_inv =\n          ComputeDecodeScaleFP4<ScaleType>(amax, row_global_encode_scale_multiplier);\n      float encode_scale = ComputeEncodeScaleFP4<ScaleType>(scale_inv, row_global_decode_scale);\n      // Step 2.5: Write scale_inv\n      bool write_scale_inv = is_src_lane;\n      if constexpr (!kAligned) {\n        write_scale_inv &= (r_g < num_rows);\n        write_scale_inv &= (c_g < row_length);\n      }\n      if (write_scale_inv) {\n        size_t row_idx = block_idx_y * kTileDim + r_s;\n        size_t col_idx = block_idx_x * (kNumThreadsStore / kNumThreadsReduce) +\n                         (threadIdx.x % kNumThreadsStore) / kNumThreadsReduce;\n        if constexpr (kSwizzledScale) {\n          size_t offset = scale_factor_swizzled_offset<ScaleType>(\n              row_idx, col_idx, DIVUP(row_length, kScaleBlockDim));\n          tile_scales_inv_c[offset] = scale_inv;\n        } else {\n          tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n        }\n      }\n      // Step 2.6: Quantize\n      OVec output_vec;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; i += 2) {\n        // Pack two elements into __nv_bfloat162\n        float2 f2_a;\n        float2 f2_b;\n        f2_a.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[i].data.elt[0], encode_scale);\n        f2_a.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[i].data.elt[1], encode_scale);\n        f2_b.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[i + 1].data.elt[0], encode_scale);\n        f2_b.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[i + 1].data.elt[1], encode_scale);\n        const uint32_t rbits = kApplyStochasticRounding ? get_rbits(rng, random_uint4, rnd_idx) : 0;\n        // Convert to __nv_fp4x4_e2m1\n        __nv_fp4x4_e2m1 out_4x = cvt_fp32_to_fp4_4x<kApplyStochasticRounding>(f2_a, f2_b, rbits);\n\n        output_vec.data.elt[i] = reinterpret_cast<__nv_fp4x2_storage_t*>(&out_4x)[0];\n        output_vec.data.elt[i + 1] = reinterpret_cast<__nv_fp4x2_storage_t*>(&out_4x)[1];\n      }\n      // Step 2.7: Store output_c\n      if constexpr (kAligned) {\n        output_vec.store_to(output_g);\n      } else {\n        if (r_g < num_rows) {\n          output_vec.store_to_elts(output_g, 0, num_ele);\n        }\n      }\n      // Step 2.8: Update output address, row index of shared memory (and row index of global memory\n      // for not aligned case)\n      output_g += stride_g / kNFP4PerContainer;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  // Step 3: Transpose, cast and store to output_t\n  if constexpr (kReturnTranspose) {\n    constexpr int c_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // Stride in columns of shared memory\n    constexpr int num_iterations = kTileDim / (c_stride * kNVecSMem);\n    const int r_s = (threadIdx.x % kNumThreadsStore) * kNVecOut;  // Row in shared memory\n    int c_s = threadIdx.x / kNumThreadsStore;                     // Column in shared memory\n    size_t r_g = block_idx_x * kTileDim + c_s * kNVecSMem;        // Row in global memory\n    const size_t c_g = block_idx_y * kTileDim + r_s;              // Column in global memory\n    const size_t stride_g =\n        static_cast<size_t>(c_stride) * kNVecSMem * num_rows;  // Stride in global memory\n    const size_t num_ele = (c_g < num_rows ? min(static_cast<size_t>(kNVecOut / kNFP4PerContainer),\n                                                 (num_rows - c_g) / kNFP4PerContainer)\n                                           : 0);  // For not aligned case\n    OType* output_g =\n        &output_t[(r_g * num_rows + c_g) / kNFP4PerContainer];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane =\n        (threadIdx.x % kThreadsPerWarp) / kNumThreadsReduce * kNumThreadsReduce;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsReduce) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsReduce) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut];\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut; ++i) {\n        int r = r_s + i;\n        int c = c_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n#pragma unroll\n      for (int smem_idx = 0; smem_idx < kNVecSMem; ++smem_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n        if constexpr (kIs2DBlockScaling) {\n          // TODO(zhongbo): 2D block scaling, directly read from amax_smem\n          int warp_idx = threadIdx.x / kThreadsPerWarp;  // 0 ~ 7\n          constexpr int kNumColsPerWarp =\n              kThreadsPerWarp / kNumThreadsStore * kNVecSMem;  // 8 elements\n          constexpr int kNumWarpsPerBlock =\n              kThreadsPerBlock / kThreadsPerWarp;  // 8 warps per block\n          constexpr int kNumColsPerIter = kNumColsPerWarp * kNumWarpsPerBlock;\n          int tid_in_warp_x = (threadIdx.x / kNumThreadsStore) % kNumColsPerWarp;\n          int tid_in_warp_y = (threadIdx.x % kThreadsPerWarp) % kNumThreadsStore;\n          int data_col_idx = iter * kNumColsPerIter + warp_idx * kNumColsPerWarp + tid_in_warp_x;\n          amax = amax_smem[tid_in_warp_y][data_col_idx / kFP4BlockScalingSize];\n        } else {\n#pragma unroll\n          for (int i = 0; i < kNVecOut; ++i) {\n            amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[smem_idx]));\n          }\n        }\n        // Step 3.3: Reduce amax\n        if constexpr (kIsE8Scaling) {\n#pragma unroll\n          for (int delta = kNumThreadsReduce / 2; delta > 0; delta /= 2) {\n            const float other_amax = __shfl_down_sync(mask, amax, delta);\n            __builtin_assume(amax >= 0);\n            __builtin_assume(other_amax >= 0);\n            amax = fmaxf(amax, other_amax);\n          }\n          amax = __shfl_sync(mask, amax, src_lane);\n        }\n        // Step 3.4: Compute scale\n        ScaleType scale_inv =\n            ComputeDecodeScaleFP4<ScaleType>(amax, global_encode_scale_multiplier);\n        float encode_scale = ComputeEncodeScaleFP4<ScaleType>(scale_inv, global_decode_scale);\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (r_g + smem_idx < row_length);\n          write_scale_inv &= (c_g < num_rows);\n        }\n        if (write_scale_inv) {\n          size_t row_idx = block_idx_x * kTileDim + c_s * kNVecSMem + smem_idx;\n          size_t col_idx = (block_idx_y * (kNumThreadsStore / kNumThreadsReduce) +\n                            (threadIdx.x % kNumThreadsStore) / kNumThreadsReduce);\n          if constexpr (kSwizzledScale) {\n            size_t offset = scale_factor_swizzled_offset<ScaleType>(\n                row_idx, col_idx, DIVUP(num_rows, kScaleBlockDim));\n            tile_scales_inv_t[offset] = scale_inv;\n          } else {\n            tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n          }\n        }\n        // Step 3.6: Quantize\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kNVecOut / kNFP4PerContainer; i += 2) {\n          // Pack two elements into __nv_bfloat162\n          float2 f2_a;\n          float2 f2_b;\n          f2_a.x =\n              ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * i].data.elt[smem_idx], encode_scale);\n          f2_a.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * i + 1].data.elt[smem_idx],\n                                                      encode_scale);\n          f2_b.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * (i + 1)].data.elt[smem_idx],\n                                                      encode_scale);\n          f2_b.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * (i + 1) + 1].data.elt[smem_idx],\n                                                      encode_scale);\n          const uint32_t rbits =\n              kApplyStochasticRounding ? get_rbits(rng, random_uint4, rnd_idx) : 0;\n          // Convert to __nv_fp4x4_e2m1\n          __nv_fp4x4_e2m1 out_4x = cvt_fp32_to_fp4_4x<kApplyStochasticRounding>(f2_a, f2_b, rbits);\n\n          output_vec.data.elt[i] = reinterpret_cast<__nv_fp4x2_storage_t*>(&out_4x)[0];\n          output_vec.data.elt[i + 1] = reinterpret_cast<__nv_fp4x2_storage_t*>(&out_4x)[1];\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g + smem_idx * num_rows / kNFP4PerContainer);\n        } else {\n          if (r_g + smem_idx < row_length) {\n            output_vec.store_to_elts(output_g + smem_idx * num_rows / kNFP4PerContainer, 0,\n                                     num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory (and row index of global\n      // memory for not aligned case)\n      output_g += stride_g / kNFP4PerContainer;\n      c_s += c_stride;\n      if constexpr (!kAligned) {\n        r_g += c_stride * kNVecSMem;\n      }\n    }\n  }\n}\n\n}  // namespace\n}  // namespace quantize_transpose_nvfp4\n#endif  // CUDA_VERSION >= 12080\n\nnamespace detail {\n\nvoid quantize_transpose_vector_blockwise_fp4(\n    const SimpleTensor& input, const SimpleTensor& global_amax, SimpleTensor& scale_inv,\n    SimpleTensor& scale_inv_t, SimpleTensor& output, SimpleTensor& output_t, const float epsilon,\n    const bool return_identity, const bool return_transpose, const bool pow2_scale,\n    const bool swizzled_scale, const bool use_stochastic_rounding,\n    const NVTETensor rng_state_tensor, const bool use_2d_quantization, const bool row_scaled_nvfp4,\n    const SimpleTensor& noop_tensor, cudaStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_vector_blockwise_fp4);\n#if CUDA_VERSION >= 12080\n\n  // pow 2 scale is for MXFP4 since it's using E8M0 scaling\n  // raise error if pow2_scale is true\n  NVTE_CHECK(!pow2_scale, \"No support for pow2_scale for MXFP4 for now\");\n\n  NVTE_CHECK(return_identity || return_transpose,\n             \"At least one of return_identity or return_transpose must be true.\");\n\n  NVTE_CHECK(return_identity || !use_2d_quantization,\n             \"2D block quantization is only supported when return_identity is true.\");\n  NVTE_CHECK(!row_scaled_nvfp4 || (return_identity && !return_transpose),\n             \"Row-scaled NVFP4 quantization only supports rowwise quantization.\");\n  NVTE_CHECK(!row_scaled_nvfp4 || !use_2d_quantization,\n             \"Row-scaled NVFP4 quantization does not support 2D quantization.\");\n\n  const size_t row_length = input.shape.size() > 0 ? input.shape.at(input.shape.size() - 1) : 1u;\n  size_t num_elements = row_length;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n    num_elements *= input.shape.at(i);\n  }\n\n  // Early return if the input tensor is empty\n  if (num_elements == 0) {\n    return;\n  }\n\n  size_t scale_stride_x = 0;\n  size_t scale_stride_y = 0;\n\n  if (return_identity) {\n    scale_stride_x = 1;\n    scale_stride_y = scale_inv.shape[1];\n  }\n\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  if (return_transpose) {\n    scale_t_stride_x = 1;\n    scale_t_stride_y = scale_inv_t.shape[1];\n  }\n\n  using namespace transformer_engine::quantize_transpose_nvfp4;\n\n  const size_t num_blocks_x = DIVUP(row_length, static_cast<size_t>(kTileDim));\n  const size_t num_blocks_y = DIVUP(num_rows, static_cast<size_t>(kTileDim));\n\n  // noop tensor for cuda graph\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  const size_t* rng_state = nullptr;\n  if (rng_state_tensor != nullptr) {\n    Tensor& rng_state_te_tensor = *convertNVTETensor(rng_state_tensor);\n    NVTE_CHECK(rng_state_te_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_te_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_te_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t*>(rng_state_te_tensor.data.dptr);\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP4x2_ONLY(\n          return_identity ? output.dtype : output_t.dtype, 2, OutputType,\n\n          dim3 grid(num_blocks_x, num_blocks_y, 1);\n\n          using ScaleType = fp8e4m3; constexpr int kScaleBlockDim = 16;\n          constexpr bool kPow2Scale = false;\n\n          const bool full_tile = row_length % kTileDim == 0 && num_rows % kTileDim == 0;\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_identity, kReturnIdentity,\n\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  return_transpose, kReturnTranspose,\n\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      full_tile, kAligned,\n\n                      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                          swizzled_scale, kSwizzledScale,\n\n                          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                              use_stochastic_rounding, kApplyStochasticRounding,\n\n                              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                                  use_2d_quantization, kIs2DBlockScaling,\n\n                                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                                      row_scaled_nvfp4, kRowScaledNVFP4,\n\n                                      size_t smem_bytes = kSMemSize * sizeof(InputType);\n                                      auto kernel = block_scaled_1d_cast_transpose_kernel<\n                                          kReturnIdentity, kReturnTranspose, kPow2Scale, kAligned,\n                                          float, InputType, OutputType, ScaleType, kSwizzledScale,\n                                          kApplyStochasticRounding, kIs2DBlockScaling,\n                                          kRowScaledNVFP4>;\n                                      if (smem_bytes >= 48 * 1024) {\n                                        cudaError_t err = cudaFuncSetAttribute(\n                                            kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                            smem_bytes);\n                                        NVTE_CHECK(err == cudaSuccess,\n                                                   \"Failed to set dynamic shared memory size.\");\n                                      } kernel<<<grid, kThreadsPerBlock, smem_bytes,\n                                                 stream>>>(\n                                          reinterpret_cast<const InputType*>(input.dptr),\n                                          reinterpret_cast<const float*>(global_amax.dptr),\n                                          reinterpret_cast<OutputType*>(output.dptr),\n                                          reinterpret_cast<OutputType*>(output_t.dptr),\n                                          reinterpret_cast<ScaleType*>(scale_inv.dptr),\n                                          reinterpret_cast<ScaleType*>(scale_inv_t.dptr),\n                                          row_length, num_rows, scale_stride_x, scale_stride_y,\n                                          scale_t_stride_x, scale_t_stride_y, kScaleBlockDim,\n                                          epsilon, rng_state,\n                                          noop_ptr);)  // kRowScaledNVFP4\n                                  )                    // kIs2DBlockScaling\n                              )                        // kApplyStochasticRounding\n                          )                            // kSwizzledScale\n                      )                                // kAligned\n                  )                                    // kReturnTranspose\n              )                                        // kReturnIdentity\n          )                                            // OutputType\n      )                                                // InputType\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace detail\n}  // namespace transformer_engine\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n\n#include <algorithm>\n#include <cfloat>\n#include <cuda/barrier>\n#include <utility>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/transpose/cast_transpose.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n\nnamespace transformer_engine {\n\n#if CUDA_VERSION >= 12080\nnamespace quantize_transpose_nvfp4 {\nnamespace {\n\nusing std::int32_t;\nusing std::uint32_t;\nusing std::uint8_t;\n\nusing transformer_engine::detail::TypeExtrema;\n\n// clang-format off\n/*\n\nStep 1: Load input to shared memory\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 8 times\n* What each thread does in each loop:\n    * 8 elements are read from the input at a time\n    * 2 elements are written to the shared memory at a time, for a total of 4 times\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|  T0   |  T1   |  T2   |  T3   |  T4   |  T5   |  T6   |  T7   |  T8   |  T9   |  T10  |  T11  |  T12  |  T13  |  T14  |  T15  |\n|  T16  |  T17  |  T18  |  T19  |  T20  |  T21  |  T22  |  T23  |  T24  |  T25  |  T26  |  T27  |  T28  |  T29  |  T30  |  T31  |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 8 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 2: Cast and store to output_c\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 4 times\n* What each thread does in each loop:\n    * 2 elements are read from the shared memory at a time, for a total of 8 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each row\n    * 16 elements are quantized and write to output_c at a time\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|      T0       |      T1       |      T2       |      T3       |      T4       |      T5       |      T6       |      T7       |\n|      T8       |      T9       |      T10      |      T11      |      T12      |      T13      |      T14      |      T15      |\n|      T16      |      T17      |      T18      |      T19      |      T20      |      T21      |      T22      |      T23      |\n|      T24      |      T25      |      T26      |      T27      |      T28      |      T29      |      T30      |      T31      |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 1                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                                                                                               |\n|                                                             Warp 7                                                            |\n|                                                                                                                               |\n|                                                                                                                               |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                          Loop 4 times                                                         |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n|                                                              ...                                                              |\n+-------------------------------+-------------------------------+-------------------------------+-------------------------------+\n\nStep 3: Transpose, cast and store to output_t\n* shard memory: 128x128 elements with type=InputType (below graph doesn't consider padding)\n* 8 warps\n* Loop 2 times\n* What each thread does in each loop:\n    * 2 elements (in a row) are read from the shared memory at a time, for a total of 16 times\n    * Every 8 consecutive threads do reduction and calculate the amax of each column\n    * 16 elements are quantized and write to output_c at a time, for a total of 2 times\n+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+------8 elements-------+------8 elements-------+-----40 elements-------+------8 elements-------+\n| T0  | T8  | T16 | T24 |                       |                       |                       | T0  | T8  | T16 | T24 |                       |                       |                       |\n| T1  | T9  | T17 | T25 |                       |                       |                       | T1  | T9  | T17 | T25 |                       |                       |                       |\n| T2  | T10 | T18 | T26 |                       |                       |                       | T2  | T10 | T18 | T26 |                       |                       |                       |\n| T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         | T3  | T11 | T19 | T27 |        Warp 1         |         ...           |        Warp 7         |\n| T4  | T12 | T20 | T28 |                       |                       |                       | T4  | T12 | T20 | T28 |                       |                       |                       |\n| T5  | T13 | T21 | T29 |                       |                       |                       | T5  | T13 | T21 | T29 |                       |                       |                       |\n| T6  | T14 | T22 | T30 |                       |                       |                       | T6  | T14 | T22 | T30 |                       |                       |                       |\n| T7  | T15 | T23 | T31 |                       |                       |                       | T7  | T15 | T23 | T31 |                       |                       |                       |\n+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+\n\n*/\n// clang-format on\n\nconstexpr int kThreadsPerWarp = 32;\n\n// for fp4, we use uint8_t to store 2 fp4 numbers\nconstexpr int kNFP4PerContainer = 2;\n\n// Hyperparameters for performance tuning\nconstexpr int kTileDim = 128;\n// constexpr int kScaleDim = 32;\nconstexpr int kNVecIn = 8;             // The number of elements each LDG touches\nconstexpr int kNVecOut = 16;           // The number of elements each STG touches\nconstexpr int kNVecSMem = 2;           // The number of elements each LDS/STS touches\nconstexpr int kThreadsPerBlock = 256;  // Thread block size, 8 warps in total\n\n// Auto-calculated constants, do not modify directly)\nstatic_assert(kNVecIn % kNVecSMem == 0, \"kNVecIn must be divisible by kNVecSMem\");\nstatic_assert(kNVecOut % kNVecSMem == 0, \"kNVecOut must be divisible by kNVecSMem\");\nconstexpr int kSMemRow = kTileDim;\nconstexpr int kSMemCol = (kTileDim / kNVecSMem) + 1;\nconstexpr int kSMemSize = kSMemRow * kSMemCol * kNVecSMem;\nconstexpr int kNumThreadsLoad = kTileDim / kNVecIn;    // 16\nconstexpr int kNumThreadsStore = kTileDim / kNVecOut;  // 8\n// constexpr int kNumThreadsReduce = kScaleDim / kNVecOut;\nstatic_assert(kNumThreadsLoad <= kThreadsPerWarp, \"kNumThreadsLoad must be <= kThreadsPerWarp\");\nstatic_assert(kNumThreadsStore <= kThreadsPerWarp, \"kNumThreadsStore must be <= kThreadsPerWarp\");\n\n// for 2D block scaling, we need to reduce amax in warp\nstatic __device__ constexpr unsigned int WARP_REDUCE_AMAX_GROUP_MASKS[8] = {\n    0x01010101, 0x02020202, 0x04040404, 0x08080808, 0x10101010, 0x20202020, 0x40404040, 0x80808080};\n\n// max for every group_size elements in warp\ntemplate <int group_size, int shfl_down_stride>\n__device__ __forceinline__ float groupMax(float val, unsigned int groupMask) {\n  for (int offset = group_size / 2; offset > 0; offset /= 2) {\n    val = max(val, __shfl_down_sync(groupMask, val, offset * shfl_down_stride));\n  }\n  return val;\n}\n\ntemplate <typename ScaleType>\n__device__ __forceinline__ ScaleType\nComputeDecodeScaleFP4(const float amax, const float global_encode_scale_multiplier) {\n  float decode_scale = amax * global_encode_scale_multiplier;\n  decode_scale = fminf(decode_scale, TypeExtrema<float>::max);\n  return static_cast<ScaleType>(decode_scale);\n}\n\ntemplate <typename ScaleType>\n__device__ __forceinline__ float ComputeEncodeScaleFP4(ScaleType decode_scale,\n                                                       const float global_decode_scale) {\n  return fminf(1.0f / (static_cast<float>(decode_scale) * global_decode_scale),\n               TypeExtrema<float>::max);\n}\n\ntemplate <typename IType, typename ScaleType>\n__device__ __forceinline__ float ComputeOutputFP4(IType input, float encode_scale) {\n  return static_cast<float>(input) * encode_scale;\n}\n\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float fp8_max = TypeExtrema<fp8e4m3>::max;\n  constexpr float fp4_max = TypeExtrema<fp4e2m1>::max;\n  float global_encode_scale = fp8_max * fp4_max / global_amax;\n  // If scale is infinity, return max value of float32\n  global_encode_scale = fminf(global_encode_scale, TypeExtrema<float>::max);\n  // If global amax is 0 or infinity, return 1\n  if (global_amax == 0.f || global_encode_scale == 0.f) {\n    return 1.f;\n  }\n  return global_encode_scale;\n}\n\n__device__ __forceinline__ uint32_t get_rbits(\n    transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS>&\n        rng,  // NVTE_BUILD_NUM_PHILOX_ROUNDS rounds of philox4x32\n    uint4& random_uint4, int& rnd_idx) {\n  if (rnd_idx == 4) {\n    rnd_idx = 0;\n    random_uint4 = rng.generate4();\n  }\n\n  // Treat uint4 as an array of 4x uint32_t elements for indexing\n  const uint32_t* const rbits_arr = reinterpret_cast<uint32_t*>(&random_uint4);\n  const uint32_t rbits = rbits_arr[rnd_idx++];\n  return rbits;\n}\n\ntemplate <class ScaleType>\n__device__ __forceinline__ size_t scale_factor_swizzled_offset(size_t row_idx, size_t col_idx,\n                                                               uint32_t col_length) {\n  // This function takes in indices from the scale factor matrix and returns an offset in the\n  // swizzled format. row_idx, col_idx are original indices from the scale factor matrix (unswizzled\n  // index). col_length is the column length of the scale factor matrix. tile_scales_inv is the\n  // pointer to the scale factor matrix.\n\n  // https://github.com/NVIDIA/cutlass/blob/main/media/docs/cpp/blackwell_functionality.md#scale-factor-layouts\n  // For any scale factor matrix, it's 512B base block. Each base block consists of 128 rows and 4\n  // columns. Base block is divided into 4 column blocks, each column block has 32 rows and 4\n  // columns.\n\n  // NOTE: There are not a lot of good illustrations about the swizzled scale factor matrix.\n  // To think in high level, the swizzled scale factor matrix could be composed as:\n  // unswizzled_scale_factor_matrix = torch.empty((M, N // 16), dtype=torch.uint8)\n  // cbg_cnt = N // 16 // 4  # Assuming N is divisible by 64\n  // rb_cnt = M // 128  # Assuming M is divisible by 128\n  // tmp = unswizzled_scale_factor_matrix.reshape(rb_cnt, 4, 32, cbg_cnt, 4)\n  // tmp = torch.permute(tmp, (0, 3, 2, 1, 4))\n  // swizzled_scale_factor_matrix = tmp.reshape((-1, 128, 4))\n\n  constexpr uint32_t kTotalRowsPerBaseBlock = 128;\n  constexpr uint32_t kRowsPerBaseBlockCol = 32;\n  constexpr uint32_t kColsPerBaseBlockCol = 4;\n\n  const size_t rb = row_idx / kTotalRowsPerBaseBlock;\n  const size_t rem = row_idx % kTotalRowsPerBaseBlock;\n  const size_t d4 = rem / kRowsPerBaseBlockCol;\n  const size_t d3 = rem % kRowsPerBaseBlockCol;\n  const size_t cbg = col_idx / kColsPerBaseBlockCol;\n  const size_t d5 = col_idx % kColsPerBaseBlockCol;\n\n  const size_t cbg_cnt = DIVUP(col_length, kColsPerBaseBlockCol);\n  // row-major offset in the logical shape\n  // (rb_cnt , cbg_cnt , 32 , 4 , 4)\n  // Magic number 16 below comes from the fact we have kColsPerBaseBlockCol = 4, and d4 ([0-128] /\n  // 32 = [0-4])\n  return ((rb * cbg_cnt + cbg) * kRowsPerBaseBlockCol + d3) * 16 + d4 * kColsPerBaseBlockCol + d5;\n}\n\n__device__ __forceinline__ __hip_fp4x4_e2m1 cvt_fp32_to_fp4_4x_with_stochastic_rounding(\n    const float2 in01, const float2 in23, const uint32_t rbits) {\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    uint16_t out_4x;\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32 %0, {%3, %4, %1, %2}, %5; \\n\\t\"\n        \"}\"\n        : \"=h\"(out_4x)\n        : \"f\"(in01.y), \"f\"(in01.x), \"f\"(in23.y), \"f\"(in23.x), \"r\"(rbits));\n    return *reinterpret_cast<__hip_fp4x4_e2m1*>(&out_4x);\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt.rs PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n    uint16_t dummy = 0;\n    return *reinterpret_cast<__hip_fp4x4_e2m1*>(&dummy);\n  }\n}\n\n__device__ __forceinline__ __hip_fp4x4_e2m1 cvt_fp32_to_fp4_4x_with_rn(const float2 in01,\n                                                                      const float2 in23,\n                                                                      const uint32_t rbits) {\n  constexpr bool has_fp4 = ARCH_BLACKWELL_FAMILY;\n  if constexpr (has_fp4) {\n    // NOTE: rbits unused for rn.\n    uint32_t out_4x;  // Only need 16 bit. Using 32 bit container for packing.\n    asm volatile(\n        \"{\\n\"\n        \".reg.b8 f0; \\n\\t\"\n        \".reg.b8 f1; \\n\\t\"\n        \"cvt.rn.satfinite.e2m1x2.f32 f0, %1, %2;\\n\\t\"\n        \"cvt.rn.satfinite.e2m1x2.f32 f1, %3, %4;\\n\\t\"\n        \"mov.b32 %0, {f0, f1, f0, f1};\\n\\t\"\n        \"}\"\n        : \"=r\"(out_4x)\n        : \"f\"(in01.y), \"f\"(in01.x), \"f\"(in23.y), \"f\"(in23.x));\n    return reinterpret_cast<__hip_fp4x4_e2m1*>(&out_4x)[0];\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n    uint16_t dummy = 0;\n    return *reinterpret_cast<__hip_fp4x4_e2m1*>(&dummy);\n  }\n}\n\ntemplate <bool kApplyStochasticRounding>\n__device__ __forceinline__ __hip_fp4x4_e2m1 cvt_fp32_to_fp4_4x(const float2 in01, const float2 in23,\n                                                              const uint32_t rbits) {\n  if constexpr (kApplyStochasticRounding) {\n    return cvt_fp32_to_fp4_4x_with_stochastic_rounding(in01, in23, rbits);\n  } else {\n    return cvt_fp32_to_fp4_4x_with_rn(in01, in23, rbits);\n  }\n}\n\ntemplate <bool kReturnIdentity, bool kReturnTranspose, bool kIsE8Scaling, bool kAligned,\n          typename CType, typename IType, typename OType, typename ScaleType, bool kSwizzledScale,\n          bool kApplyStochasticRounding, bool kIs2DBlockScaling, bool kRowScaledNVFP4>\n__global__ void __launch_bounds__(kThreadsPerBlock) block_scaled_1d_cast_transpose_kernel(\n    const IType* const input, const float* global_amax, OType* const output_c,\n    OType* const output_t, ScaleType* const tile_scales_inv_c, ScaleType* const tile_scales_inv_t,\n    const size_t row_length, const size_t num_rows, const size_t scale_stride_x,\n    const size_t scale_stride_y, const size_t scale_t_stride_x, const size_t scale_t_stride_y,\n    const size_t kScaleBlockDim, const float epsilon, const size_t* rng_state,\n    const float* noop_ptr) {\n  constexpr int kNVecContainer = kNVecOut / kNFP4PerContainer;\n  using SMemVec = Vec<IType, kNVecSMem>;\n  using OVec = Vec<OType, kNVecContainer>;\n  union IVec {\n    Vec<IType, kNVecIn> input_type;\n    Vec<SMemVec, kNVecIn / kNVecSMem> smem_type;\n  };\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  const size_t block_idx_x = blockIdx.x;\n  const size_t block_idx_y = blockIdx.y;\n  const size_t rng_sequence =\n      threadIdx.x + block_idx_x * kThreadsPerBlock + block_idx_y * gridDim.x * kThreadsPerBlock;\n  const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n  const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n\n  transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n  rng.init(rng_seed, rng_sequence, rng_offset);\n  uint4 random_uint4 = kApplyStochasticRounding ? rng.generate4() : uint4{0, 0, 0, 0};\n\n  int rnd_idx =\n      0;  // Index of the random number. It increments each time when used and resets to 0 if reaches 4x\n\n  extern __shared__ char smem_base[];\n  SMemVec* smem = reinterpret_cast<SMemVec*>(&smem_base[0]);\n\n  // 2D block scaling is not supported for E8 scaling MXFP4 or for colwise only mode.\n  // Instead of static_assert, return early if these invalid modes are detected.\n  if constexpr (kIs2DBlockScaling && kIsE8Scaling) {\n    return;\n  }\n  if constexpr (kIs2DBlockScaling && !kReturnIdentity) {\n    return;\n  }\n  // for 128x128 block, 2D block scaling means there will be 8x8 amax values for nvfp4, 4x4 for 2D mxfp4\n  // use constexpr to define the size, when not using 2D, use minimal size 1x1\n  constexpr int kFP4BlockScalingSize = 16;\n  constexpr int k2DBlockAmaxDim = kIs2DBlockScaling ? (kTileDim / kFP4BlockScalingSize) : 1;\n  constexpr int kNumRowsPerWarp = kThreadsPerWarp / kNumThreadsStore;  // 4\n  constexpr int k2DBlockAmaxReduceDim =\n      kIs2DBlockScaling ? (kFP4BlockScalingSize / kNumRowsPerWarp) : 1;\n  __shared__ CType amax_smem_red[k2DBlockAmaxDim][k2DBlockAmaxDim][k2DBlockAmaxReduceDim];\n  __shared__ CType amax_smem[k2DBlockAmaxDim][k2DBlockAmaxDim];\n\n  // Step 1: Load input to shared memory\n  {\n    constexpr int r_stride = kThreadsPerBlock / kNumThreadsLoad;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsLoad) * (kNVecIn / kNVecSMem);         // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsLoad;                             // Row in shared memory\n    const size_t c_g = block_idx_x * kTileDim + c_s * kNVecSMem;         // Column in global memory\n    size_t r_g = block_idx_y * kTileDim + r_s;                           // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele = (c_g < row_length ? min(static_cast<size_t>(kNVecIn), row_length - c_g)\n                                             : 0);          // For not aligned case\n    const IType* input_g = &input[r_g * row_length + c_g];  // Input address in global memory\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      IVec input_vec;\n      // Step 1.1: Load from global memory (input) to registers\n      if constexpr (kAligned) {\n        input_vec.input_type.load_from(input_g);\n      } else {\n        if (r_g < num_rows) {\n          input_vec.input_type.load_from_elts(input_g, 0, num_ele);\n        } else {\n          input_vec.input_type.clear();\n        }\n      }\n      // Step 1.2: Write to shared memory\n#pragma unroll\n      for (int i = 0; i < kNVecIn / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem[r * kSMemCol + c] = input_vec.smem_type.data.elt[i];\n      }\n      // Step 1.3: Update input address, row index of shared memory, (and row index of global memory\n      // for not aligned case)\n      input_g += stride_g;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  __syncthreads();\n\n  const int kNumThreadsReduce = kScaleBlockDim / kNVecOut;\n  const float global_encode_scale =\n      kIsE8Scaling ? 1.0f : ComputeGlobalEncodeScaleFP4(global_amax[0]);\n  constexpr float fp4_max_inv = 1.0f / TypeExtrema<fp4e2m1>::max;\n  const float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n  const float global_decode_scale = 1.0 / global_encode_scale;\n\n  // Step 2: Cast and store to output_c\n  if constexpr (kReturnIdentity) {\n    constexpr int r_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // stride in rows of shared memory\n    constexpr int num_iterations = kTileDim / r_stride;\n    const int c_s =\n        (threadIdx.x % kNumThreadsStore) * (kNVecOut / kNVecSMem);       // Column in shared memory\n    int r_s = threadIdx.x / kNumThreadsStore;                            // Row in shared memory\n    const size_t c_g = block_idx_x * kTileDim + c_s * kNVecSMem;         // Column in global memory\n    size_t r_g = block_idx_y * kTileDim + r_s;                           // Row in global memory\n    const size_t stride_g = static_cast<size_t>(r_stride) * row_length;  // Stride in global memory\n    const size_t num_ele =\n        (c_g < row_length ? min(static_cast<size_t>(kNVecOut / kNFP4PerContainer),\n                                (row_length - c_g) / kNFP4PerContainer)\n                          : 0);  // For not aligned case\n    OType* output_g =\n        &output_c[(r_g * row_length + c_g) / kNFP4PerContainer];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane =\n        (threadIdx.x % kThreadsPerWarp) / kNumThreadsReduce * kNumThreadsReduce;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsReduce) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsReduce) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut / kNVecSMem];\n      // Step 2.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n        int c = c_s + i;\n        int r = r_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n      // Step 2.2: Compute local amax\n      CType amax = 0;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; ++i) {\n#pragma unroll\n        for (int j = 0; j < kNVecSMem; ++j) {\n          __builtin_assume(amax >= 0);\n          amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[j]));\n        }\n      }\n      // Step 2.3: Reduce amax\n      if constexpr (kIsE8Scaling) {\n#pragma unroll\n        for (int delta = kNumThreadsReduce / 2; delta > 0; delta /= 2) {\n          const float other_amax = __shfl_down_sync(mask, amax, delta);\n          __builtin_assume(amax >= 0);\n          __builtin_assume(other_amax >= 0);\n          amax = fmaxf(amax, other_amax);\n        }\n        amax = __shfl_sync(mask, amax, src_lane);\n      }\n      // doing shuffle sync for 2D block scaling (not applicable for E8 scaling)\n      if constexpr (kIs2DBlockScaling) {\n        // first amax shuffle sync in warp, then reduce in smem\n        // T0 T8 T16 T24 should do amax reduction together\n        constexpr int kNumRowsPerIter = kThreadsPerBlock / kNumThreadsStore;  // 32\n        int warp_idx = threadIdx.x / kThreadsPerWarp;                         // 0 ~ 7\n        int tid_in_warp_x = threadIdx.x % kNumThreadsStore;\n        int tid_in_warp_y = (threadIdx.x / kNumThreadsStore) % kNumRowsPerWarp;\n        CType amax_warp_reduced = groupMax<kNumRowsPerWarp, kNumThreadsStore>(\n            amax, WARP_REDUCE_AMAX_GROUP_MASKS[tid_in_warp_x]);\n        // now T0 ~ T8 in each warp has the reduced amax values\n        int data_row_idx = iter * kNumRowsPerIter + warp_idx * kNumRowsPerWarp + tid_in_warp_y;\n        if (tid_in_warp_y == 0) {\n          amax_smem_red[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x]\n                       [warp_idx % k2DBlockAmaxReduceDim] = amax_warp_reduced;\n        }\n        __syncthreads();\n\n        if (data_row_idx % kFP4BlockScalingSize == 0) {\n          CType amax_2d = 0.0;\n          for (int i = 0; i < k2DBlockAmaxReduceDim; i++) {\n            amax_2d = fmaxf(amax_2d,\n                            amax_smem_red[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x][i]);\n          }\n          amax_smem[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x] = amax_2d;\n        }\n        __syncthreads();\n        // every thread now knows 2D amax\n        amax = amax_smem[data_row_idx / kFP4BlockScalingSize][tid_in_warp_x];\n      }\n      // Step 2.4: Compute scale\n      const size_t row_idx = block_idx_y * kTileDim + r_s;\n      float row_global_encode_scale = global_encode_scale;\n      if constexpr (kRowScaledNVFP4) {\n        row_global_encode_scale =\n            row_idx < num_rows ? ComputeGlobalEncodeScaleFP4(global_amax[row_idx]) : 1.0f;\n      }\n      const float row_global_encode_scale_multiplier =\n          kRowScaledNVFP4 ? row_global_encode_scale * fp4_max_inv : global_encode_scale_multiplier;\n      const float row_global_decode_scale =\n          kRowScaledNVFP4 ? 1.0f / row_global_encode_scale : global_decode_scale;\n      ScaleType scale_inv =\n          ComputeDecodeScaleFP4<ScaleType>(amax, row_global_encode_scale_multiplier);\n      float encode_scale = ComputeEncodeScaleFP4<ScaleType>(scale_inv, row_global_decode_scale);\n      // Step 2.5: Write scale_inv\n      bool write_scale_inv = is_src_lane;\n      if constexpr (!kAligned) {\n        write_scale_inv &= (r_g < num_rows);\n        write_scale_inv &= (c_g < row_length);\n      }\n      if (write_scale_inv) {\n        size_t row_idx = block_idx_y * kTileDim + r_s;\n        size_t col_idx = block_idx_x * (kNumThreadsStore / kNumThreadsReduce) +\n                         (threadIdx.x % kNumThreadsStore) / kNumThreadsReduce;\n        if constexpr (kSwizzledScale) {\n          size_t offset = scale_factor_swizzled_offset<ScaleType>(\n              row_idx, col_idx, DIVUP(row_length, kScaleBlockDim));\n          tile_scales_inv_c[offset] = scale_inv;\n        } else {\n          tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n        }\n      }\n      // Step 2.6: Quantize\n      OVec output_vec;\n#pragma unroll\n      for (int i = 0; i < kNVecOut / kNVecSMem; i += 2) {\n        // Pack two elements into __nv_bfloat162\n        float2 f2_a;\n        float2 f2_b;\n        f2_a.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[i].data.elt[0], encode_scale);\n        f2_a.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[i].data.elt[1], encode_scale);\n        f2_b.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[i + 1].data.elt[0], encode_scale);\n        f2_b.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[i + 1].data.elt[1], encode_scale);\n        const uint32_t rbits = kApplyStochasticRounding ? get_rbits(rng, random_uint4, rnd_idx) : 0;\n        // Convert to __nv_fp4x4_e2m1\n        __hip_fp4x4_e2m1 out_4x = cvt_fp32_to_fp4_4x<kApplyStochasticRounding>(f2_a, f2_b, rbits);\n\n        output_vec.data.elt[i] = reinterpret_cast<__hip_fp4x2_storage_t*>(&out_4x)[0];\n        output_vec.data.elt[i + 1] = reinterpret_cast<__hip_fp4x2_storage_t*>(&out_4x)[1];\n      }\n      // Step 2.7: Store output_c\n      if constexpr (kAligned) {\n        output_vec.store_to(output_g);\n      } else {\n        if (r_g < num_rows) {\n          output_vec.store_to_elts(output_g, 0, num_ele);\n        }\n      }\n      // Step 2.8: Update output address, row index of shared memory (and row index of global memory\n      // for not aligned case)\n      output_g += stride_g / kNFP4PerContainer;\n      r_s += r_stride;\n      if constexpr (!kAligned) {\n        r_g += r_stride;\n      }\n    }\n  }\n\n  // Step 3: Transpose, cast and store to output_t\n  if constexpr (kReturnTranspose) {\n    constexpr int c_stride =\n        kThreadsPerBlock / kNumThreadsStore;  // Stride in columns of shared memory\n    constexpr int num_iterations = kTileDim / (c_stride * kNVecSMem);\n    const int r_s = (threadIdx.x % kNumThreadsStore) * kNVecOut;  // Row in shared memory\n    int c_s = threadIdx.x / kNumThreadsStore;                     // Column in shared memory\n    size_t r_g = block_idx_x * kTileDim + c_s * kNVecSMem;        // Row in global memory\n    const size_t c_g = block_idx_y * kTileDim + r_s;              // Column in global memory\n    const size_t stride_g =\n        static_cast<size_t>(c_stride) * kNVecSMem * num_rows;  // Stride in global memory\n    const size_t num_ele = (c_g < num_rows ? min(static_cast<size_t>(kNVecOut / kNFP4PerContainer),\n                                                 (num_rows - c_g) / kNFP4PerContainer)\n                                           : 0);  // For not aligned case\n    OType* output_g =\n        &output_t[(r_g * num_rows + c_g) / kNFP4PerContainer];  // Output address in global memory\n    // Each kNumThreadsStore threads form a warp process one row, we need to find the lane id of\n    // the first thread to do the reduction.\n    const unsigned src_lane =\n        (threadIdx.x % kThreadsPerWarp) / kNumThreadsReduce * kNumThreadsReduce;\n    // This mask represents which threads should do the reduction together.\n    const unsigned mask = ((1 << kNumThreadsReduce) - 1) << src_lane;\n    const bool is_src_lane = (threadIdx.x % kNumThreadsReduce) == 0;\n#pragma unroll\n    for (int iter = 0; iter < num_iterations; ++iter) {\n      SMemVec smem_vec[kNVecOut];\n      // Step 3.1: Load from shared memory to registers\n#pragma unroll\n      for (int i = 0; i < kNVecOut; ++i) {\n        int r = r_s + i;\n        int c = c_s;\n        smem_vec[i] = smem[r * kSMemCol + c];\n      }\n#pragma unroll\n      for (int smem_idx = 0; smem_idx < kNVecSMem; ++smem_idx) {\n        // Step 3.2: Compute local amax\n        CType amax = 0;\n        if constexpr (kIs2DBlockScaling) {\n          // TODO(zhongbo): 2D block scaling, directly read from amax_smem\n          int warp_idx = threadIdx.x / kThreadsPerWarp;  // 0 ~ 7\n          constexpr int kNumColsPerWarp =\n              kThreadsPerWarp / kNumThreadsStore * kNVecSMem;  // 8 elements\n          constexpr int kNumWarpsPerBlock =\n              kThreadsPerBlock / kThreadsPerWarp;  // 8 warps per block\n          constexpr int kNumColsPerIter = kNumColsPerWarp * kNumWarpsPerBlock;\n          int tid_in_warp_x = (threadIdx.x / kNumThreadsStore) % kNumColsPerWarp;\n          int tid_in_warp_y = (threadIdx.x % kThreadsPerWarp) % kNumThreadsStore;\n          int data_col_idx = iter * kNumColsPerIter + warp_idx * kNumColsPerWarp + tid_in_warp_x;\n          amax = amax_smem[tid_in_warp_y][data_col_idx / kFP4BlockScalingSize];\n        } else {\n#pragma unroll\n          for (int i = 0; i < kNVecOut; ++i) {\n            amax = fmaxf(amax, fabsf(smem_vec[i].data.elt[smem_idx]));\n          }\n        }\n        // Step 3.3: Reduce amax\n        if constexpr (kIsE8Scaling) {\n#pragma unroll\n          for (int delta = kNumThreadsReduce / 2; delta > 0; delta /= 2) {\n            const float other_amax = __shfl_down_sync(mask, amax, delta);\n            __builtin_assume(amax >= 0);\n            __builtin_assume(other_amax >= 0);\n            amax = fmaxf(amax, other_amax);\n          }\n          amax = __shfl_sync(mask, amax, src_lane);\n        }\n        // Step 3.4: Compute scale\n        ScaleType scale_inv =\n            ComputeDecodeScaleFP4<ScaleType>(amax, global_encode_scale_multiplier);\n        float encode_scale = ComputeEncodeScaleFP4<ScaleType>(scale_inv, global_decode_scale);\n        // Step 3.5: Write scale_inv_t\n        bool write_scale_inv = is_src_lane;\n        if constexpr (!kAligned) {\n          write_scale_inv &= (r_g + smem_idx < row_length);\n          write_scale_inv &= (c_g < num_rows);\n        }\n        if (write_scale_inv) {\n          size_t row_idx = block_idx_x * kTileDim + c_s * kNVecSMem + smem_idx;\n          size_t col_idx = (block_idx_y * (kNumThreadsStore / kNumThreadsReduce) +\n                            (threadIdx.x % kNumThreadsStore) / kNumThreadsReduce);\n          if constexpr (kSwizzledScale) {\n            size_t offset = scale_factor_swizzled_offset<ScaleType>(\n                row_idx, col_idx, DIVUP(num_rows, kScaleBlockDim));\n            tile_scales_inv_t[offset] = scale_inv;\n          } else {\n            tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n          }\n        }\n        // Step 3.6: Quantize\n        OVec output_vec;\n#pragma unroll\n        for (int i = 0; i < kNVecOut / kNFP4PerContainer; i += 2) {\n          // Pack two elements into __nv_bfloat162\n          float2 f2_a;\n          float2 f2_b;\n          f2_a.x =\n              ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * i].data.elt[smem_idx], encode_scale);\n          f2_a.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * i + 1].data.elt[smem_idx],\n                                                      encode_scale);\n          f2_b.x = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * (i + 1)].data.elt[smem_idx],\n                                                      encode_scale);\n          f2_b.y = ComputeOutputFP4<IType, ScaleType>(smem_vec[2 * (i + 1) + 1].data.elt[smem_idx],\n                                                      encode_scale);\n          const uint32_t rbits =\n              kApplyStochasticRounding ? get_rbits(rng, random_uint4, rnd_idx) : 0;\n          // Convert to __nv_fp4x4_e2m1\n          __hip_fp4x4_e2m1 out_4x = cvt_fp32_to_fp4_4x<kApplyStochasticRounding>(f2_a, f2_b, rbits);\n\n          output_vec.data.elt[i] = reinterpret_cast<__hip_fp4x2_storage_t*>(&out_4x)[0];\n          output_vec.data.elt[i + 1] = reinterpret_cast<__hip_fp4x2_storage_t*>(&out_4x)[1];\n        }\n        // Step 3.7: Store output_t\n        if constexpr (kAligned) {\n          output_vec.store_to(output_g + smem_idx * num_rows / kNFP4PerContainer);\n        } else {\n          if (r_g + smem_idx < row_length) {\n            output_vec.store_to_elts(output_g + smem_idx * num_rows / kNFP4PerContainer, 0,\n                                     num_ele);\n          }\n        }\n      }\n      // Step 3.8: Update output address, column index of shared memory (and row index of global\n      // memory for not aligned case)\n      output_g += stride_g / kNFP4PerContainer;\n      c_s += c_stride;\n      if constexpr (!kAligned) {\n        r_g += c_stride * kNVecSMem;\n      }\n    }\n  }\n}\n\n}  // namespace\n}  // namespace quantize_transpose_nvfp4\n#endif  // CUDA_VERSION >= 12080\n\nnamespace detail {\n\nvoid quantize_transpose_vector_blockwise_fp4(\n    const SimpleTensor& input, const SimpleTensor& global_amax, SimpleTensor& scale_inv,\n    SimpleTensor& scale_inv_t, SimpleTensor& output, SimpleTensor& output_t, const float epsilon,\n    const bool return_identity, const bool return_transpose, const bool pow2_scale,\n    const bool swizzled_scale, const bool use_stochastic_rounding,\n    const NVTETensor rng_state_tensor, const bool use_2d_quantization, const bool row_scaled_nvfp4,\n    const SimpleTensor& noop_tensor, hipStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_vector_blockwise_fp4);\n#if CUDA_VERSION >= 12080\n\n  // pow 2 scale is for MXFP4 since it's using E8M0 scaling\n  // raise error if pow2_scale is true\n  NVTE_CHECK(!pow2_scale, \"No support for pow2_scale for MXFP4 for now\");\n\n  NVTE_CHECK(return_identity || return_transpose,\n             \"At least one of return_identity or return_transpose must be true.\");\n\n  NVTE_CHECK(return_identity || !use_2d_quantization,\n             \"2D block quantization is only supported when return_identity is true.\");\n  NVTE_CHECK(!row_scaled_nvfp4 || (return_identity && !return_transpose),\n             \"Row-scaled NVFP4 quantization only supports rowwise quantization.\");\n  NVTE_CHECK(!row_scaled_nvfp4 || !use_2d_quantization,\n             \"Row-scaled NVFP4 quantization does not support 2D quantization.\");\n\n  const size_t row_length = input.shape.size() > 0 ? input.shape.at(input.shape.size() - 1) : 1u;\n  size_t num_elements = row_length;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n    num_elements *= input.shape.at(i);\n  }\n\n  // Early return if the input tensor is empty\n  if (num_elements == 0) {\n    return;\n  }\n\n  size_t scale_stride_x = 0;\n  size_t scale_stride_y = 0;\n\n  if (return_identity) {\n    scale_stride_x = 1;\n    scale_stride_y = scale_inv.shape[1];\n  }\n\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  if (return_transpose) {\n    scale_t_stride_x = 1;\n    scale_t_stride_y = scale_inv_t.shape[1];\n  }\n\n  using namespace transformer_engine::quantize_transpose_nvfp4;\n\n  const size_t num_blocks_x = DIVUP(row_length, static_cast<size_t>(kTileDim));\n  const size_t num_blocks_y = DIVUP(num_rows, static_cast<size_t>(kTileDim));\n\n  // noop tensor for cuda graph\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  const size_t* rng_state = nullptr;\n  if (rng_state_tensor != nullptr) {\n    Tensor& rng_state_te_tensor = *convertNVTETensor(rng_state_tensor);\n    NVTE_CHECK(rng_state_te_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_te_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_te_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t*>(rng_state_te_tensor.data.dptr);\n  }\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP4x2_ONLY(\n          return_identity ? output.dtype : output_t.dtype, 2, OutputType,\n\n          dim3 grid(num_blocks_x, num_blocks_y, 1);\n\n          using ScaleType = fp8e4m3; constexpr int kScaleBlockDim = 16;\n          constexpr bool kPow2Scale = false;\n\n          const bool full_tile = row_length % kTileDim == 0 && num_rows % kTileDim == 0;\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_identity, kReturnIdentity,\n\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  return_transpose, kReturnTranspose,\n\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      full_tile, kAligned,\n\n                      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                          swizzled_scale, kSwizzledScale,\n\n                          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                              use_stochastic_rounding, kApplyStochasticRounding,\n\n                              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                                  use_2d_quantization, kIs2DBlockScaling,\n\n                                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                                      row_scaled_nvfp4, kRowScaledNVFP4,\n\n                                      size_t smem_bytes = kSMemSize * sizeof(InputType);\n                                      auto kernel = block_scaled_1d_cast_transpose_kernel<\n                                          kReturnIdentity, kReturnTranspose, kPow2Scale, kAligned,\n                                          float, InputType, OutputType, ScaleType, kSwizzledScale,\n                                          kApplyStochasticRounding, kIs2DBlockScaling,\n                                          kRowScaledNVFP4>;\n                                      if (smem_bytes >= 48 * 1024) {\n                                        hipError_t err = hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize,\n                                            smem_bytes);\n                                        NVTE_CHECK(err == hipSuccess,\n                                                   \"Failed to set dynamic shared memory size.\");\n                                      } kernel<<<grid, kThreadsPerBlock, smem_bytes,\n                                                 stream>>>(\n                                          reinterpret_cast<const InputType*>(input.dptr),\n                                          reinterpret_cast<const float*>(global_amax.dptr),\n                                          reinterpret_cast<OutputType*>(output.dptr),\n                                          reinterpret_cast<OutputType*>(output_t.dptr),\n                                          reinterpret_cast<ScaleType*>(scale_inv.dptr),\n                                          reinterpret_cast<ScaleType*>(scale_inv_t.dptr),\n                                          row_length, num_rows, scale_stride_x, scale_stride_y,\n                                          scale_t_stride_x, scale_t_stride_y, kScaleBlockDim,\n                                          epsilon, rng_state,\n                                          noop_ptr);)  // kRowScaledNVFP4\n                                  )                    // kIs2DBlockScaling\n                              )                        // kApplyStochasticRounding\n                          )                            // kSwizzledScale\n                      )                                // kAligned\n                  )                                    // kReturnTranspose\n              )                                        // kReturnIdentity\n          )                                            // OutputType\n      )                                                // InputType\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace detail\n}  // namespace transformer_engine\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/quantize_transpose_vector_blockwise_fp4.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_runtime.h>\n\n#include <cfloat>\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n\n#if (!defined(__CUDA_MINIMUM_ARCH__) && __CUDA_ARCH__ >= 900) || \\\n    (defined(__CUDA_MINIMUM_ARCH__) && __CUDA_MINIMUM_ARCH__ >= 900)\n#define TMA_HW_SUPPORTED\n#endif\n\nnamespace transformer_engine {\nnamespace {\n\n// const values configuration\n\nconstexpr size_t kThreadsPerWarp = 32;\n#ifdef TMA_HW_SUPPORTED\nconstexpr size_t BLOCK_TILE_DIM = 128;\nconstexpr size_t WARP_TILE_DIM_X = 32;\nconstexpr size_t WARP_TILE_DIM_Y = 64;\nconstexpr size_t THREAD_TILE_DIM_X = 16;\nconstexpr size_t THREAD_TILE_DIM_Y = 4;\n#else\nconstexpr size_t BLOCK_TILE_DIM = 128;\nconstexpr size_t WARP_TILE_DIM_X = 64;\nconstexpr size_t WARP_TILE_DIM_Y = 32;\nconstexpr size_t THREAD_TILE_DIM_X = 8;\nconstexpr size_t THREAD_TILE_DIM_Y = 8;\n#endif\n\n#ifdef TMA_HW_SUPPORTED\nconstexpr size_t NUM_BYTES_PER_BANK = 4;\nconstexpr size_t NUM_BANKS_PER_SHARED_ELEM = THREAD_TILE_DIM_Y / NUM_BYTES_PER_BANK;\nconstexpr size_t SHARED_BLOCK_TILE_DIM_Y = BLOCK_TILE_DIM;\nconstexpr size_t SHARED_BLOCK_TILE_DIM_X_BANKS =\n    BLOCK_TILE_DIM / (NUM_BYTES_PER_BANK * NUM_BANKS_PER_SHARED_ELEM);\nconstexpr size_t NUM_BANKS_Y_IN_WARP = WARP_TILE_DIM_Y / NUM_BYTES_PER_BANK;\n#endif\nconstexpr size_t ELE_PER_THREAD = THREAD_TILE_DIM_X * THREAD_TILE_DIM_Y;\nconstexpr size_t THREADS_PER_BLOCK = BLOCK_TILE_DIM * BLOCK_TILE_DIM / ELE_PER_THREAD;\nconstexpr size_t NUM_WARPS_X_IN_BLOCK = BLOCK_TILE_DIM / WARP_TILE_DIM_X;\nconstexpr size_t NUM_WARPS_Y_IN_BLOCK = BLOCK_TILE_DIM / WARP_TILE_DIM_Y;\nconstexpr size_t NUM_WARPS_IN_BLOCK = NUM_WARPS_X_IN_BLOCK * NUM_WARPS_Y_IN_BLOCK;\n\nconstexpr size_t NUM_THREADS_X_IN_WARP = WARP_TILE_DIM_X / THREAD_TILE_DIM_X;\nconstexpr size_t NUM_THREADS_Y_IN_WARP = kThreadsPerWarp / NUM_THREADS_X_IN_WARP;\n\n#define MIN(a, b) (a < b ? a : b)\n\ntemplate <bool kReturnTranspose, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(THREADS_PER_BLOCK)\n    block_scaled_cast_transpose_kernel(const IType* const input, OType* const output_c,\n                                       OType* const output_t, CType* const tile_scales_inv_c,\n                                       CType* const tile_scales_inv_t, const size_t row_length,\n                                       const size_t num_rows, const size_t scale_stride_x,\n                                       const size_t scale_stride_y, const size_t scale_t_stride_x,\n                                       const size_t scale_t_stride_y, const float epsilon,\n                                       const __grid_constant__ CUtensorMap tensor_map_output_t,\n                                       bool pow_2_scaling, const float* noop_ptr) {\n  using IVec = Vec<IType, THREAD_TILE_DIM_X>;\n  using OVecCast = Vec<OType, THREAD_TILE_DIM_X>;\n  using OVecTrans = Vec<OType, THREAD_TILE_DIM_Y>;\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  // shared mem for amax reduction in entire block, each warp produces one amax, there are\n  // NUM_WARPS_IN_BLOCK amax to reduce\n  __shared__ CType block_tile_amax_shared[NUM_WARPS_IN_BLOCK];\n\n  IVec thrd_tile_input[THREAD_TILE_DIM_Y];\n  constexpr int THREAD_TILE_DIM_X_ = kReturnTranspose ? THREAD_TILE_DIM_X : 1;\n  OVecTrans thrd_tile_out_trans[THREAD_TILE_DIM_X_];\n\n  const int tid_in_warp = threadIdx.x % kThreadsPerWarp;\n  const int tid_in_warp_x = tid_in_warp % NUM_THREADS_X_IN_WARP;\n  const int tid_in_warp_y = tid_in_warp / NUM_THREADS_X_IN_WARP;\n  const int warp_id_in_block = threadIdx.x / kThreadsPerWarp;\n  const int warp_id_in_block_x = warp_id_in_block % NUM_WARPS_X_IN_BLOCK;\n  const int warp_id_in_block_y = warp_id_in_block / NUM_WARPS_X_IN_BLOCK;\n\n  // This is ONLY true if the input is a full tile\n  const int tile_id_x = blockIdx.x;\n  const int tile_id_y = blockIdx.y;\n\n  const size_t block_tile_start_idx =\n      tile_id_y * BLOCK_TILE_DIM * row_length + tile_id_x * BLOCK_TILE_DIM;\n  const size_t warp_tile_start_idx =\n      block_tile_start_idx +\n      warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP * row_length +\n      warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP;\n  const size_t thread_tile_start_idx = warp_tile_start_idx +\n                                       tid_in_warp_y * THREAD_TILE_DIM_Y * row_length +\n                                       tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  CType warp_tile_amax;\n  CType block_tile_amax;\n  CType block_tile_scale;\n  CType amax = 0;\n\n// Step 1: Load a block tile of input data into thread tiles on registers\n#pragma unroll\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n    thrd_tile_input[i].load_from(input + thread_tile_start_idx + i * row_length);\n  }\n\n  // Step 2: calculate block tile amax and scale\n  // Calculate thread_tile amax\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(amax, fabsf(static_cast<CType>(thrd_tile_input[i].data.elt[j])));\n    }\n  }\n  // Reduce amax in the warp (32x32 tile)\n  warp_tile_amax = warp_reduce_max<kThreadsPerWarp>(amax);\n  // broadcast the amax to all threads in a warp from the lane 0\n  constexpr int lane_zero = 0;\n  warp_tile_amax = __shfl_sync(0xFFFFFFFF, warp_tile_amax, lane_zero);\n\n  // reduce warp_tile_amax across multiple warps in a thread block using shared mem\n  if (tid_in_warp == 0) {\n    block_tile_amax_shared[warp_id_in_block_y * NUM_WARPS_X_IN_BLOCK + warp_id_in_block_x] =\n        warp_tile_amax;\n  }\n  __syncthreads();\n  // only 8 elements needs reduction, if using reduction tree, multiple _syncthreads will be needed,\n  // instead we just let thread 0 do the job\n  if (threadIdx.x == 0) {\n    CType blk_amax = block_tile_amax_shared[0];\n#pragma unroll\n    for (int idx = 1; idx < NUM_WARPS_IN_BLOCK; idx++) {\n      blk_amax = fmaxf(blk_amax, block_tile_amax_shared[idx]);\n    }\n    block_tile_amax_shared[0] = blk_amax;\n  }\n  __syncthreads();\n  block_tile_amax = block_tile_amax_shared[0];\n\n  block_tile_scale =\n      compute_scale_from_types<IType, OType>(block_tile_amax, epsilon, pow_2_scaling);\n\n  if (threadIdx.x == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    const CType scale_inv = 1.0f / block_tile_scale;\n\n    size_t row_idx = tile_id_y;\n    size_t col_idx = tile_id_x;\n    tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n\n    if constexpr (kReturnTranspose) {\n      row_idx = tile_id_x;\n      col_idx = tile_id_y;\n      tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n    }\n  }\n\n  // Step 3: Store cast output, Step 4: do transpose within thread tile\n  OVecCast tmp_output_c;\n\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      // Step 3: Store cast output\n      CType scale_data = block_tile_scale;\n\n      OType scaled_elt =\n          static_cast<OType>(static_cast<CType>(thrd_tile_input[i].data.elt[j]) * scale_data);\n      tmp_output_c.data.elt[j] = scaled_elt;\n      // Step 4: do transpose within thread tile\n      if constexpr (kReturnTranspose) {\n        thrd_tile_out_trans[j].data.elt[i] = scaled_elt;\n      }\n    }\n    tmp_output_c.store_to(output_c + thread_tile_start_idx + i * row_length);\n  }\n\n  // Step 4: store transpose into shared memory\n  if constexpr (kReturnTranspose) {\n#ifdef TMA_HW_SUPPORTED\n    __shared__ alignas(128)\n        OVecTrans block_tile_trans_shared[SHARED_BLOCK_TILE_DIM_Y][SHARED_BLOCK_TILE_DIM_X_BANKS];\n    OType(*block_tile_trans_shared_otype_ptr)[BLOCK_TILE_DIM] =\n        reinterpret_cast<OType(*)[BLOCK_TILE_DIM]>(block_tile_trans_shared);\n\n#pragma unroll\n    for (int i = 0; i < THREAD_TILE_DIM_X; i++) {\n      auto warp_id_in_block_x_ = warp_id_in_block_y;\n      auto warp_id_in_block_y_ = warp_id_in_block_x;\n      int row_idx = warp_id_in_block_y_ * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP +\n                    tid_in_warp_x * THREAD_TILE_DIM_X + i;\n      int col_idx =\n          warp_id_in_block_x_ * (NUM_BANKS_Y_IN_WARP / NUM_BANKS_PER_SHARED_ELEM) + tid_in_warp_y;\n      block_tile_trans_shared[row_idx][col_idx] = thrd_tile_out_trans[i];\n    }\n\n    // Wait for shared memory writes to be visible to TMA engine.\n    ptx::fence_proxy_async_shared_cta();\n    __syncthreads();\n    // After syncthreads, writes by all threads are visible to TMA engine.\n\n    // Step 5: store transpose output\n    // Initiate TMA transfer to copy shared memory to global memory\n    if (threadIdx.x == 0) {\n      ptx::cp_async_bulk_tensor_2d_shared_to_global(\n          reinterpret_cast<const uint64_t*>(&tensor_map_output_t), tile_id_y * BLOCK_TILE_DIM,\n          tile_id_x * BLOCK_TILE_DIM,\n          reinterpret_cast<uint64_t*>(block_tile_trans_shared_otype_ptr));\n      // Wait for TMA transfer to have finished reading shared memory.\n      // Create a \"bulk async-group\" out of the previous bulk copy operation.\n      ptx::cp_async_bulk_commit_group();\n      // Wait for the group to have completed reading from shared memory.\n      ptx::cp_async_bulk_wait_group_read<0>();\n    }\n#else\n    // Step 4 Alternative (when TMA is not available, skip writing to shared memory)\n    const size_t block_tile_t_start_idx =\n        tile_id_x * BLOCK_TILE_DIM * num_rows + tile_id_y * BLOCK_TILE_DIM;\n    const size_t warp_tile_t_start_idx =\n        block_tile_t_start_idx +\n        warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP * num_rows +\n        warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP;\n    const size_t thread_tile_t_start_idx = warp_tile_t_start_idx +\n                                           tid_in_warp_x * THREAD_TILE_DIM_X * num_rows +\n                                           tid_in_warp_y * THREAD_TILE_DIM_Y;\n#pragma unroll\n    for (int i = 0; i < THREAD_TILE_DIM_X; i++) {\n      thrd_tile_out_trans[i].store_to(output_t + thread_tile_t_start_idx + i * num_rows);\n    }\n#endif\n  }\n}\n\ntemplate <bool kReturnTranspose, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(THREADS_PER_BLOCK) block_scaled_cast_transpose_kernel_notaligned(\n    const IType* const input, OType* const output_c, OType* const output_t,\n    CType* const tile_scales_inv_c, CType* const tile_scales_inv_t, const size_t row_length,\n    const size_t num_rows, const size_t scale_stride_x, const size_t scale_stride_y,\n    const size_t scale_t_stride_x, const size_t scale_t_stride_y, const float epsilon,\n    bool pow_2_scaling, const float* noop_ptr) {\n  using IVec = Vec<IType, THREAD_TILE_DIM_X>;\n  using OVecCast = Vec<OType, THREAD_TILE_DIM_X>;\n  using OVecTrans = Vec<OType, THREAD_TILE_DIM_Y>;\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  // shared mem for amax reduction in entire block, each warp produces one amax, there are\n  // NUM_WARPS_IN_BLOCK amax to reduce\n  __shared__ CType block_tile_amax_shared[NUM_WARPS_IN_BLOCK];\n\n  IVec thrd_tile_input[THREAD_TILE_DIM_Y];\n  constexpr int THREAD_TILE_DIM_X_ = kReturnTranspose ? THREAD_TILE_DIM_X : 1;\n  OVecTrans thrd_tile_out_trans[THREAD_TILE_DIM_X_];\n\n  const int tid_in_warp = threadIdx.x % kThreadsPerWarp;\n  const int tid_in_warp_x = tid_in_warp % NUM_THREADS_X_IN_WARP;\n  const int tid_in_warp_y = tid_in_warp / NUM_THREADS_X_IN_WARP;\n  const int warp_id_in_block = threadIdx.x / kThreadsPerWarp;\n  const int warp_id_in_block_x = warp_id_in_block % NUM_WARPS_X_IN_BLOCK;\n  const int warp_id_in_block_y = warp_id_in_block / NUM_WARPS_X_IN_BLOCK;\n\n  const int tile_id_x = blockIdx.x;\n  const int tile_id_y = blockIdx.y;\n\n  const size_t block_tile_start_row_idx = tile_id_y * BLOCK_TILE_DIM;\n  const size_t block_tile_start_col_idx = tile_id_x * BLOCK_TILE_DIM;\n  const size_t block_tile_start_idx =\n      block_tile_start_row_idx * row_length + block_tile_start_col_idx;\n  const size_t warp_tile_start_idx =\n      block_tile_start_idx +\n      warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP * row_length +\n      warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP;\n  const size_t thread_tile_start_idx = warp_tile_start_idx +\n                                       tid_in_warp_y * THREAD_TILE_DIM_Y * row_length +\n                                       tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  // handle non-full tile\n  // check for three cases: full thread tile, nonfull thread tile, empty thread tile\n  // for empty thread tile, directly write zero to the transposed shared mem buffer\n  // for nonfull thread tile, fill zero to thread tile and act as if it's full\n  const size_t thread_tile_start_row_idx =\n      tile_id_y * BLOCK_TILE_DIM + warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP +\n      tid_in_warp_y * THREAD_TILE_DIM_Y;\n  const size_t thread_tile_start_col_idx =\n      tile_id_x * BLOCK_TILE_DIM + warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP +\n      tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  const size_t thread_tile_end_row_idx = thread_tile_start_row_idx + THREAD_TILE_DIM_Y - 1;\n  const size_t thread_tile_end_col_idx = thread_tile_start_col_idx + THREAD_TILE_DIM_X - 1;\n\n  bool full_thrd_tile =\n      (thread_tile_end_row_idx < num_rows) && (thread_tile_end_col_idx < row_length);\n  bool empty_thrd_tile =\n      (thread_tile_start_row_idx >= num_rows) || (thread_tile_start_col_idx >= row_length);\n  bool nonfull_thrd_tile = (!full_thrd_tile) && (!empty_thrd_tile);\n\n  const size_t thread_tile_ncols =\n      MIN(THREAD_TILE_DIM_X,\n          (MIN(thread_tile_end_col_idx, row_length - 1) - thread_tile_start_col_idx + 1));\n  const size_t thread_tile_nrows =\n      MIN(THREAD_TILE_DIM_Y,\n          (MIN(thread_tile_end_row_idx, num_rows - 1) - thread_tile_start_row_idx + 1));\n\n  CType warp_tile_amax;\n  CType block_tile_amax;\n  CType block_tile_scale;\n  CType amax = 0;\n\n  if (!empty_thrd_tile) {\n    // Step 1: Load a block tile of input data into thread tiles on registers\n    // Edge case: nonfull thread tile case, will use the partial load function here\n    if (nonfull_thrd_tile) {\n#pragma unroll\n      for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n        if (i >= thread_tile_nrows) {\n          thrd_tile_input[i].clear();\n        } else {\n          thrd_tile_input[i].load_from_elts(input + thread_tile_start_idx + i * row_length, 0,\n                                            thread_tile_ncols);\n        }\n      }\n    } else {\n#pragma unroll\n      for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n        thrd_tile_input[i].load_from_elts(input + thread_tile_start_idx + i * row_length, 0,\n                                          THREAD_TILE_DIM_X);\n      }\n    }\n\n    // Step 2: calculate block tile amax and scale\n    // Calculate thread_tile amax\n    for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n      for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n        __builtin_assume(amax >= 0);\n        amax = fmaxf(amax, fabsf(static_cast<CType>(thrd_tile_input[i].data.elt[j])));\n      }\n    }\n  }\n  // Reduce amax in the warp (32x32 tile)\n  warp_tile_amax = warp_reduce_max<kThreadsPerWarp>(amax);\n  // broadcast the amax to all threads in a warp from the lane 0\n  constexpr int lane_zero = 0;\n  warp_tile_amax = __shfl_sync(0xFFFFFFFF, warp_tile_amax, lane_zero);\n\n  // reduce warp_tile_amax across multiple warps in a thread block using shared mem\n  if (tid_in_warp == 0) {\n    block_tile_amax_shared[warp_id_in_block_y * NUM_WARPS_X_IN_BLOCK + warp_id_in_block_x] =\n        warp_tile_amax;\n  }\n  __syncthreads();\n  // only 8 elements needs reduction, if using reduction tree, multiple _syncthreads will be needed,\n  // instead we just let thread 0 do the job\n  if (threadIdx.x == 0) {\n    CType blk_amax = block_tile_amax_shared[0];\n#pragma unroll\n    for (int idx = 1; idx < NUM_WARPS_IN_BLOCK; idx++) {\n      blk_amax = fmaxf(blk_amax, block_tile_amax_shared[idx]);\n    }\n    block_tile_amax_shared[0] = blk_amax;\n  }\n  __syncthreads();\n  block_tile_amax = block_tile_amax_shared[0];\n\n  block_tile_scale =\n      compute_scale_from_types<IType, OType>(block_tile_amax, epsilon, pow_2_scaling);\n\n  if (threadIdx.x == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    const CType scale_inv = 1.0f / block_tile_scale;\n\n    size_t row_idx = tile_id_y;\n    size_t col_idx = tile_id_x;\n    tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n\n    if constexpr (kReturnTranspose) {\n      row_idx = tile_id_x;\n      col_idx = tile_id_y;\n      tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n    }\n  }\n\n  // Step 3: Store cast output, Step 4: do transpose within thread tile\n  // Edge case: in the non-full tile case, there are three subcases\n  // for full thread tile, it's the same thing here\n  // for nonfull thread tile, pay attention when saving tmp_output_c to global\n  // memory, cannot vec store_to, but need to elt store to for empty tile,\n  // it should not enter this step, skip to Step 4\n\n  // set thrd_tile_out_trans to all zero\n  if constexpr (kReturnTranspose) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      thrd_tile_out_trans[j].clear();\n    }\n  }\n\n  if (!empty_thrd_tile) {\n    OVecCast tmp_output_c;\n    for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n      if (i >= thread_tile_nrows) {\n        continue;\n      }\n#pragma unroll\n      for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n        // Step 3: Store cast output\n        CType scale_data = block_tile_scale;\n\n        OType scaled_elt =\n            static_cast<OType>(static_cast<CType>(thrd_tile_input[i].data.elt[j]) * scale_data);\n        tmp_output_c.data.elt[j] = scaled_elt;\n        // Step 4: do transpose within thread tile\n        if constexpr (kReturnTranspose) {\n          thrd_tile_out_trans[j].data.elt[i] = scaled_elt;\n        }\n      }\n      tmp_output_c.store_to_elts(output_c + thread_tile_start_idx + i * row_length, 0,\n                                 thread_tile_ncols);\n    }\n\n    if constexpr (kReturnTranspose) {\n      const size_t block_tile_t_start_idx =\n          tile_id_x * BLOCK_TILE_DIM * num_rows + tile_id_y * BLOCK_TILE_DIM;\n      const size_t warp_tile_t_start_idx =\n          block_tile_t_start_idx +\n          warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP * num_rows +\n          warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP;\n      const size_t thread_tile_t_start_idx = warp_tile_t_start_idx +\n                                             tid_in_warp_x * THREAD_TILE_DIM_X * num_rows +\n                                             tid_in_warp_y * THREAD_TILE_DIM_Y;\n#pragma unroll\n      for (int i = 0; i < thread_tile_ncols; i++) {\n        thrd_tile_out_trans[i].store_to_elts(output_t + thread_tile_t_start_idx + i * num_rows, 0,\n                                             thread_tile_nrows);\n      }\n    }\n  }\n}\n\ntemplate <typename OutputType>\nCUtensorMap get_tensor_map(const SimpleTensor& tensor, size_t global_dim_x, size_t global_dim_y) {\n  CUtensorMapDataType dataType;\n  if constexpr (std::is_same_v<OutputType, __nv_fp8_e4m3> ||\n                std::is_same_v<OutputType, __nv_fp8_e5m2>) {\n    dataType = CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8;\n  } else {\n    NVTE_ERROR(\n        \"Invalid output type for blockwise transpose (must be FP8: Float8E4M3 or Float8E5M2).\");\n  }\n\n  CUtensorMap tensor_map_output_trans{};\n  create_2D_tensor_map(tensor_map_output_trans, tensor, global_dim_y, global_dim_x,\n                       /*shmemY=*/BLOCK_TILE_DIM, /*shmemX=*/BLOCK_TILE_DIM,\n                       /*stride_elems=*/global_dim_x, /*offset_elems=*/0, sizeof(OutputType) * 8);\n  return tensor_map_output_trans;\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace transformer_engine::detail {\n\nvoid quantize_transpose_square_blockwise(const SimpleTensor& input, SimpleTensor& scale_inv,\n                                         SimpleTensor& scale_inv_t, SimpleTensor& output,\n                                         SimpleTensor& output_t, const float epsilon,\n                                         const bool return_transpose, const bool pow_2_scale,\n                                         const SimpleTensor& noop_tensor, cudaStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_square_blockwise);\n  checkCuDriverContext(stream);\n\n  if (transformer_engine::cuda::sm_arch() >= 100) {\n    NVTE_CHECK(pow_2_scale, \"On Blackwell and newer, the FP8 block scaling recipe is emulated \",\n               \"with MXFP8, which requires using power of two scaling factors.\");\n  }\n\n  NVTE_CHECK(input.shape == output.shape, \"Input and output must have the same shape.\");\n  const size_t row_length = input.shape.size() > 0 ? input.shape.back() : 1;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n  }\n\n  NVTE_CHECK(scale_inv.shape.size() == 2, \"scale_inv must have 2 dimensions.\");\n\n  size_t scale_k = scale_inv.shape[1];\n\n  const size_t scale_stride_x = 1;\n  const size_t scale_stride_y = scale_k;\n\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  if (return_transpose) {\n    NVTE_CHECK(output_t.shape.size() == input.shape.size(), \"input (shape=\", input.shape,\n               \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n    if (output_t.shape.size() > 0) {\n      NVTE_CHECK(output_t.shape.front() == input.shape.back(), \"input (shape=\", input.shape,\n                 \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n      for (size_t i = 1; i < output_t.shape.size(); ++i) {\n        NVTE_CHECK(output_t.shape[i] == input.shape[i - 1], \"input (shape=\", input.shape,\n                   \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n      }\n    }\n    NVTE_CHECK(output.dtype == output_t.dtype, \"output and output_t need to have the same type.\");\n\n    NVTE_CHECK(scale_inv_t.shape.size() == 2, \"scale_inv_t must have 2 dimensions.\");\n\n    scale_t_stride_x = 1;\n    scale_t_stride_y = scale_inv_t.shape[1];\n  }\n\n  const size_t num_blocks_x = DIVUP(row_length, BLOCK_TILE_DIM);\n  const size_t num_blocks_y = DIVUP(num_rows, BLOCK_TILE_DIM);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          output.dtype, OutputType,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_transpose, kReturnTranspose,\n\n              dim3 grid(num_blocks_x, num_blocks_y, 1);\n              const bool full_tile =\n                  row_length % BLOCK_TILE_DIM == 0 && num_rows % BLOCK_TILE_DIM == 0;\n\n              if (full_tile) {\n                CUtensorMap tensor_map_output_trans;\n                if (return_transpose) {\n                  tensor_map_output_trans =\n                      get_tensor_map<OutputType>(output_t, num_rows, row_length);\n                }\n                block_scaled_cast_transpose_kernel<kReturnTranspose, float, InputType, OutputType>\n                    <<<grid, THREADS_PER_BLOCK, 0, stream>>>(\n                        reinterpret_cast<const InputType*>(input.dptr),\n                        reinterpret_cast<OutputType*>(output.dptr),\n                        reinterpret_cast<OutputType*>(output_t.dptr),\n                        reinterpret_cast<float*>(scale_inv.dptr),\n                        reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows,\n                        scale_stride_x, scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon,\n                        tensor_map_output_trans, pow_2_scale, noop_ptr);\n              } else {\n                block_scaled_cast_transpose_kernel_notaligned<kReturnTranspose, float, InputType,\n                                                              OutputType>\n                    <<<grid, THREADS_PER_BLOCK, 0, stream>>>(\n                        reinterpret_cast<const InputType*>(input.dptr),\n                        reinterpret_cast<OutputType*>(output.dptr),\n                        reinterpret_cast<OutputType*>(output_t.dptr),\n                        reinterpret_cast<float*>(scale_inv.dptr),\n                        reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows,\n                        scale_stride_x, scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon,\n                        pow_2_scale, noop_ptr);\n              }  // full-tile\n              )  // return_transpose\n          )      // OutputType\n      )          // InputType\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace transformer_engine::detail\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n\n#include <cfloat>\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/recipe/recipe_common.cuh\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n\n#if (!defined(__CUDA_MINIMUM_ARCH__) && __CUDA_ARCH__ >= 900) || \\\n    (defined(__CUDA_MINIMUM_ARCH__) && __CUDA_MINIMUM_ARCH__ >= 900)\n#define TMA_HW_SUPPORTED\n#endif\n\nnamespace transformer_engine {\nnamespace {\n\n// const values configuration\n\nconstexpr size_t kThreadsPerWarp = 32;\n#ifdef TMA_HW_SUPPORTED\nconstexpr size_t BLOCK_TILE_DIM = 128;\nconstexpr size_t WARP_TILE_DIM_X = 32;\nconstexpr size_t WARP_TILE_DIM_Y = 64;\nconstexpr size_t THREAD_TILE_DIM_X = 16;\nconstexpr size_t THREAD_TILE_DIM_Y = 4;\n#else\nconstexpr size_t BLOCK_TILE_DIM = 128;\nconstexpr size_t WARP_TILE_DIM_X = 64;\nconstexpr size_t WARP_TILE_DIM_Y = 32;\nconstexpr size_t THREAD_TILE_DIM_X = 8;\nconstexpr size_t THREAD_TILE_DIM_Y = 8;\n#endif\n\n#ifdef TMA_HW_SUPPORTED\nconstexpr size_t NUM_BYTES_PER_BANK = 4;\nconstexpr size_t NUM_BANKS_PER_SHARED_ELEM = THREAD_TILE_DIM_Y / NUM_BYTES_PER_BANK;\nconstexpr size_t SHARED_BLOCK_TILE_DIM_Y = BLOCK_TILE_DIM;\nconstexpr size_t SHARED_BLOCK_TILE_DIM_X_BANKS =\n    BLOCK_TILE_DIM / (NUM_BYTES_PER_BANK * NUM_BANKS_PER_SHARED_ELEM);\nconstexpr size_t NUM_BANKS_Y_IN_WARP = WARP_TILE_DIM_Y / NUM_BYTES_PER_BANK;\n#endif\nconstexpr size_t ELE_PER_THREAD = THREAD_TILE_DIM_X * THREAD_TILE_DIM_Y;\nconstexpr size_t THREADS_PER_BLOCK = BLOCK_TILE_DIM * BLOCK_TILE_DIM / ELE_PER_THREAD;\nconstexpr size_t NUM_WARPS_X_IN_BLOCK = BLOCK_TILE_DIM / WARP_TILE_DIM_X;\nconstexpr size_t NUM_WARPS_Y_IN_BLOCK = BLOCK_TILE_DIM / WARP_TILE_DIM_Y;\nconstexpr size_t NUM_WARPS_IN_BLOCK = NUM_WARPS_X_IN_BLOCK * NUM_WARPS_Y_IN_BLOCK;\n\nconstexpr size_t NUM_THREADS_X_IN_WARP = WARP_TILE_DIM_X / THREAD_TILE_DIM_X;\nconstexpr size_t NUM_THREADS_Y_IN_WARP = kThreadsPerWarp / NUM_THREADS_X_IN_WARP;\n\n#define MIN(a, b) (a < b ? a : b)\n\ntemplate <bool kReturnTranspose, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(THREADS_PER_BLOCK)\n    block_scaled_cast_transpose_kernel(const IType* const input, OType* const output_c,\n                                       OType* const output_t, CType* const tile_scales_inv_c,\n                                       CType* const tile_scales_inv_t, const size_t row_length,\n                                       const size_t num_rows, const size_t scale_stride_x,\n                                       const size_t scale_stride_y, const size_t scale_t_stride_x,\n                                       const size_t scale_t_stride_y, const float epsilon,\n                                       const __grid_constant__ CUtensorMap tensor_map_output_t,\n                                       bool pow_2_scaling, const float* noop_ptr) {\n  using IVec = Vec<IType, THREAD_TILE_DIM_X>;\n  using OVecCast = Vec<OType, THREAD_TILE_DIM_X>;\n  using OVecTrans = Vec<OType, THREAD_TILE_DIM_Y>;\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  // shared mem for amax reduction in entire block, each warp produces one amax, there are\n  // NUM_WARPS_IN_BLOCK amax to reduce\n  __shared__ CType block_tile_amax_shared[NUM_WARPS_IN_BLOCK];\n\n  IVec thrd_tile_input[THREAD_TILE_DIM_Y];\n  constexpr int THREAD_TILE_DIM_X_ = kReturnTranspose ? THREAD_TILE_DIM_X : 1;\n  OVecTrans thrd_tile_out_trans[THREAD_TILE_DIM_X_];\n\n  const int tid_in_warp = threadIdx.x % kThreadsPerWarp;\n  const int tid_in_warp_x = tid_in_warp % NUM_THREADS_X_IN_WARP;\n  const int tid_in_warp_y = tid_in_warp / NUM_THREADS_X_IN_WARP;\n  const int warp_id_in_block = threadIdx.x / kThreadsPerWarp;\n  const int warp_id_in_block_x = warp_id_in_block % NUM_WARPS_X_IN_BLOCK;\n  const int warp_id_in_block_y = warp_id_in_block / NUM_WARPS_X_IN_BLOCK;\n\n  // This is ONLY true if the input is a full tile\n  const int tile_id_x = blockIdx.x;\n  const int tile_id_y = blockIdx.y;\n\n  const size_t block_tile_start_idx =\n      tile_id_y * BLOCK_TILE_DIM * row_length + tile_id_x * BLOCK_TILE_DIM;\n  const size_t warp_tile_start_idx =\n      block_tile_start_idx +\n      warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP * row_length +\n      warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP;\n  const size_t thread_tile_start_idx = warp_tile_start_idx +\n                                       tid_in_warp_y * THREAD_TILE_DIM_Y * row_length +\n                                       tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  CType warp_tile_amax;\n  CType block_tile_amax;\n  CType block_tile_scale;\n  CType amax = 0;\n\n// Step 1: Load a block tile of input data into thread tiles on registers\n#pragma unroll\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n    thrd_tile_input[i].load_from(input + thread_tile_start_idx + i * row_length);\n  }\n\n  // Step 2: calculate block tile amax and scale\n  // Calculate thread_tile amax\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(amax, fabsf(static_cast<CType>(thrd_tile_input[i].data.elt[j])));\n    }\n  }\n  // Reduce amax in the warp (32x32 tile)\n  warp_tile_amax = warp_reduce_max<kThreadsPerWarp>(amax);\n  // broadcast the amax to all threads in a warp from the lane 0\n  constexpr int lane_zero = 0;\n  warp_tile_amax = __shfl_sync(0xFFFFFFFF, warp_tile_amax, lane_zero);\n\n  // reduce warp_tile_amax across multiple warps in a thread block using shared mem\n  if (tid_in_warp == 0) {\n    block_tile_amax_shared[warp_id_in_block_y * NUM_WARPS_X_IN_BLOCK + warp_id_in_block_x] =\n        warp_tile_amax;\n  }\n  __syncthreads();\n  // only 8 elements needs reduction, if using reduction tree, multiple _syncthreads will be needed,\n  // instead we just let thread 0 do the job\n  if (threadIdx.x == 0) {\n    CType blk_amax = block_tile_amax_shared[0];\n#pragma unroll\n    for (int idx = 1; idx < NUM_WARPS_IN_BLOCK; idx++) {\n      blk_amax = fmaxf(blk_amax, block_tile_amax_shared[idx]);\n    }\n    block_tile_amax_shared[0] = blk_amax;\n  }\n  __syncthreads();\n  block_tile_amax = block_tile_amax_shared[0];\n\n  block_tile_scale =\n      compute_scale_from_types<IType, OType>(block_tile_amax, epsilon, pow_2_scaling);\n\n  if (threadIdx.x == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    const CType scale_inv = 1.0f / block_tile_scale;\n\n    size_t row_idx = tile_id_y;\n    size_t col_idx = tile_id_x;\n    tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n\n    if constexpr (kReturnTranspose) {\n      row_idx = tile_id_x;\n      col_idx = tile_id_y;\n      tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n    }\n  }\n\n  // Step 3: Store cast output, Step 4: do transpose within thread tile\n  OVecCast tmp_output_c;\n\n  for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      // Step 3: Store cast output\n      CType scale_data = block_tile_scale;\n\n      OType scaled_elt =\n          static_cast<OType>(static_cast<CType>(thrd_tile_input[i].data.elt[j]) * scale_data);\n      tmp_output_c.data.elt[j] = scaled_elt;\n      // Step 4: do transpose within thread tile\n      if constexpr (kReturnTranspose) {\n        thrd_tile_out_trans[j].data.elt[i] = scaled_elt;\n      }\n    }\n    tmp_output_c.store_to(output_c + thread_tile_start_idx + i * row_length);\n  }\n\n  // Step 4: store transpose into shared memory\n  if constexpr (kReturnTranspose) {\n#ifdef TMA_HW_SUPPORTED\n    __shared__ alignas(128)\n        OVecTrans block_tile_trans_shared[SHARED_BLOCK_TILE_DIM_Y][SHARED_BLOCK_TILE_DIM_X_BANKS];\n    OType(*block_tile_trans_shared_otype_ptr)[BLOCK_TILE_DIM] =\n        reinterpret_cast<OType(*)[BLOCK_TILE_DIM]>(block_tile_trans_shared);\n\n#pragma unroll\n    for (int i = 0; i < THREAD_TILE_DIM_X; i++) {\n      auto warp_id_in_block_x_ = warp_id_in_block_y;\n      auto warp_id_in_block_y_ = warp_id_in_block_x;\n      int row_idx = warp_id_in_block_y_ * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP +\n                    tid_in_warp_x * THREAD_TILE_DIM_X + i;\n      int col_idx =\n          warp_id_in_block_x_ * (NUM_BANKS_Y_IN_WARP / NUM_BANKS_PER_SHARED_ELEM) + tid_in_warp_y;\n      block_tile_trans_shared[row_idx][col_idx] = thrd_tile_out_trans[i];\n    }\n\n    // Wait for shared memory writes to be visible to TMA engine.\n    ptx::fence_proxy_async_shared_cta();\n    __syncthreads();\n    // After syncthreads, writes by all threads are visible to TMA engine.\n\n    // Step 5: store transpose output\n    // Initiate TMA transfer to copy shared memory to global memory\n    if (threadIdx.x == 0) {\n      ptx::cp_async_bulk_tensor_2d_shared_to_global(\n          reinterpret_cast<const uint64_t*>(&tensor_map_output_t), tile_id_y * BLOCK_TILE_DIM,\n          tile_id_x * BLOCK_TILE_DIM,\n          reinterpret_cast<uint64_t*>(block_tile_trans_shared_otype_ptr));\n      // Wait for TMA transfer to have finished reading shared memory.\n      // Create a \"bulk async-group\" out of the previous bulk copy operation.\n      ptx::cp_async_bulk_commit_group();\n      // Wait for the group to have completed reading from shared memory.\n      ptx::cp_async_bulk_wait_group_read<0>();\n    }\n#else\n    // Step 4 Alternative (when TMA is not available, skip writing to shared memory)\n    const size_t block_tile_t_start_idx =\n        tile_id_x * BLOCK_TILE_DIM * num_rows + tile_id_y * BLOCK_TILE_DIM;\n    const size_t warp_tile_t_start_idx =\n        block_tile_t_start_idx +\n        warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP * num_rows +\n        warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP;\n    const size_t thread_tile_t_start_idx = warp_tile_t_start_idx +\n                                           tid_in_warp_x * THREAD_TILE_DIM_X * num_rows +\n                                           tid_in_warp_y * THREAD_TILE_DIM_Y;\n#pragma unroll\n    for (int i = 0; i < THREAD_TILE_DIM_X; i++) {\n      thrd_tile_out_trans[i].store_to(output_t + thread_tile_t_start_idx + i * num_rows);\n    }\n#endif\n  }\n}\n\ntemplate <bool kReturnTranspose, typename CType, typename IType, typename OType>\n__global__ void __launch_bounds__(THREADS_PER_BLOCK) block_scaled_cast_transpose_kernel_notaligned(\n    const IType* const input, OType* const output_c, OType* const output_t,\n    CType* const tile_scales_inv_c, CType* const tile_scales_inv_t, const size_t row_length,\n    const size_t num_rows, const size_t scale_stride_x, const size_t scale_stride_y,\n    const size_t scale_t_stride_x, const size_t scale_t_stride_y, const float epsilon,\n    bool pow_2_scaling, const float* noop_ptr) {\n  using IVec = Vec<IType, THREAD_TILE_DIM_X>;\n  using OVecCast = Vec<OType, THREAD_TILE_DIM_X>;\n  using OVecTrans = Vec<OType, THREAD_TILE_DIM_Y>;\n\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  // shared mem for amax reduction in entire block, each warp produces one amax, there are\n  // NUM_WARPS_IN_BLOCK amax to reduce\n  __shared__ CType block_tile_amax_shared[NUM_WARPS_IN_BLOCK];\n\n  IVec thrd_tile_input[THREAD_TILE_DIM_Y];\n  constexpr int THREAD_TILE_DIM_X_ = kReturnTranspose ? THREAD_TILE_DIM_X : 1;\n  OVecTrans thrd_tile_out_trans[THREAD_TILE_DIM_X_];\n\n  const int tid_in_warp = threadIdx.x % kThreadsPerWarp;\n  const int tid_in_warp_x = tid_in_warp % NUM_THREADS_X_IN_WARP;\n  const int tid_in_warp_y = tid_in_warp / NUM_THREADS_X_IN_WARP;\n  const int warp_id_in_block = threadIdx.x / kThreadsPerWarp;\n  const int warp_id_in_block_x = warp_id_in_block % NUM_WARPS_X_IN_BLOCK;\n  const int warp_id_in_block_y = warp_id_in_block / NUM_WARPS_X_IN_BLOCK;\n\n  const int tile_id_x = blockIdx.x;\n  const int tile_id_y = blockIdx.y;\n\n  const size_t block_tile_start_row_idx = tile_id_y * BLOCK_TILE_DIM;\n  const size_t block_tile_start_col_idx = tile_id_x * BLOCK_TILE_DIM;\n  const size_t block_tile_start_idx =\n      block_tile_start_row_idx * row_length + block_tile_start_col_idx;\n  const size_t warp_tile_start_idx =\n      block_tile_start_idx +\n      warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP * row_length +\n      warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP;\n  const size_t thread_tile_start_idx = warp_tile_start_idx +\n                                       tid_in_warp_y * THREAD_TILE_DIM_Y * row_length +\n                                       tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  // handle non-full tile\n  // check for three cases: full thread tile, nonfull thread tile, empty thread tile\n  // for empty thread tile, directly write zero to the transposed shared mem buffer\n  // for nonfull thread tile, fill zero to thread tile and act as if it's full\n  const size_t thread_tile_start_row_idx =\n      tile_id_y * BLOCK_TILE_DIM + warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP +\n      tid_in_warp_y * THREAD_TILE_DIM_Y;\n  const size_t thread_tile_start_col_idx =\n      tile_id_x * BLOCK_TILE_DIM + warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP +\n      tid_in_warp_x * THREAD_TILE_DIM_X;\n\n  const size_t thread_tile_end_row_idx = thread_tile_start_row_idx + THREAD_TILE_DIM_Y - 1;\n  const size_t thread_tile_end_col_idx = thread_tile_start_col_idx + THREAD_TILE_DIM_X - 1;\n\n  bool full_thrd_tile =\n      (thread_tile_end_row_idx < num_rows) && (thread_tile_end_col_idx < row_length);\n  bool empty_thrd_tile =\n      (thread_tile_start_row_idx >= num_rows) || (thread_tile_start_col_idx >= row_length);\n  bool nonfull_thrd_tile = (!full_thrd_tile) && (!empty_thrd_tile);\n\n  const size_t thread_tile_ncols =\n      MIN(THREAD_TILE_DIM_X,\n          (MIN(thread_tile_end_col_idx, row_length - 1) - thread_tile_start_col_idx + 1));\n  const size_t thread_tile_nrows =\n      MIN(THREAD_TILE_DIM_Y,\n          (MIN(thread_tile_end_row_idx, num_rows - 1) - thread_tile_start_row_idx + 1));\n\n  CType warp_tile_amax;\n  CType block_tile_amax;\n  CType block_tile_scale;\n  CType amax = 0;\n\n  if (!empty_thrd_tile) {\n    // Step 1: Load a block tile of input data into thread tiles on registers\n    // Edge case: nonfull thread tile case, will use the partial load function here\n    if (nonfull_thrd_tile) {\n#pragma unroll\n      for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n        if (i >= thread_tile_nrows) {\n          thrd_tile_input[i].clear();\n        } else {\n          thrd_tile_input[i].load_from_elts(input + thread_tile_start_idx + i * row_length, 0,\n                                            thread_tile_ncols);\n        }\n      }\n    } else {\n#pragma unroll\n      for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n        thrd_tile_input[i].load_from_elts(input + thread_tile_start_idx + i * row_length, 0,\n                                          THREAD_TILE_DIM_X);\n      }\n    }\n\n    // Step 2: calculate block tile amax and scale\n    // Calculate thread_tile amax\n    for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n#pragma unroll\n      for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n        __builtin_assume(amax >= 0);\n        amax = fmaxf(amax, fabsf(static_cast<CType>(thrd_tile_input[i].data.elt[j])));\n      }\n    }\n  }\n  // Reduce amax in the warp (32x32 tile)\n  warp_tile_amax = warp_reduce_max<kThreadsPerWarp>(amax);\n  // broadcast the amax to all threads in a warp from the lane 0\n  constexpr int lane_zero = 0;\n  warp_tile_amax = __shfl_sync(0xFFFFFFFF, warp_tile_amax, lane_zero);\n\n  // reduce warp_tile_amax across multiple warps in a thread block using shared mem\n  if (tid_in_warp == 0) {\n    block_tile_amax_shared[warp_id_in_block_y * NUM_WARPS_X_IN_BLOCK + warp_id_in_block_x] =\n        warp_tile_amax;\n  }\n  __syncthreads();\n  // only 8 elements needs reduction, if using reduction tree, multiple _syncthreads will be needed,\n  // instead we just let thread 0 do the job\n  if (threadIdx.x == 0) {\n    CType blk_amax = block_tile_amax_shared[0];\n#pragma unroll\n    for (int idx = 1; idx < NUM_WARPS_IN_BLOCK; idx++) {\n      blk_amax = fmaxf(blk_amax, block_tile_amax_shared[idx]);\n    }\n    block_tile_amax_shared[0] = blk_amax;\n  }\n  __syncthreads();\n  block_tile_amax = block_tile_amax_shared[0];\n\n  block_tile_scale =\n      compute_scale_from_types<IType, OType>(block_tile_amax, epsilon, pow_2_scaling);\n\n  if (threadIdx.x == 0) {\n    static_assert(std::is_same<CType, float>::value);\n    const CType scale_inv = 1.0f / block_tile_scale;\n\n    size_t row_idx = tile_id_y;\n    size_t col_idx = tile_id_x;\n    tile_scales_inv_c[row_idx * scale_stride_y + col_idx * scale_stride_x] = scale_inv;\n\n    if constexpr (kReturnTranspose) {\n      row_idx = tile_id_x;\n      col_idx = tile_id_y;\n      tile_scales_inv_t[row_idx * scale_t_stride_y + col_idx * scale_t_stride_x] = scale_inv;\n    }\n  }\n\n  // Step 3: Store cast output, Step 4: do transpose within thread tile\n  // Edge case: in the non-full tile case, there are three subcases\n  // for full thread tile, it's the same thing here\n  // for nonfull thread tile, pay attention when saving tmp_output_c to global\n  // memory, cannot vec store_to, but need to elt store to for empty tile,\n  // it should not enter this step, skip to Step 4\n\n  // set thrd_tile_out_trans to all zero\n  if constexpr (kReturnTranspose) {\n#pragma unroll\n    for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n      thrd_tile_out_trans[j].clear();\n    }\n  }\n\n  if (!empty_thrd_tile) {\n    OVecCast tmp_output_c;\n    for (int i = 0; i < THREAD_TILE_DIM_Y; i++) {\n      if (i >= thread_tile_nrows) {\n        continue;\n      }\n#pragma unroll\n      for (int j = 0; j < THREAD_TILE_DIM_X; j++) {\n        // Step 3: Store cast output\n        CType scale_data = block_tile_scale;\n\n        OType scaled_elt =\n            static_cast<OType>(static_cast<CType>(thrd_tile_input[i].data.elt[j]) * scale_data);\n        tmp_output_c.data.elt[j] = scaled_elt;\n        // Step 4: do transpose within thread tile\n        if constexpr (kReturnTranspose) {\n          thrd_tile_out_trans[j].data.elt[i] = scaled_elt;\n        }\n      }\n      tmp_output_c.store_to_elts(output_c + thread_tile_start_idx + i * row_length, 0,\n                                 thread_tile_ncols);\n    }\n\n    if constexpr (kReturnTranspose) {\n      const size_t block_tile_t_start_idx =\n          tile_id_x * BLOCK_TILE_DIM * num_rows + tile_id_y * BLOCK_TILE_DIM;\n      const size_t warp_tile_t_start_idx =\n          block_tile_t_start_idx +\n          warp_id_in_block_x * THREAD_TILE_DIM_X * NUM_THREADS_X_IN_WARP * num_rows +\n          warp_id_in_block_y * THREAD_TILE_DIM_Y * NUM_THREADS_Y_IN_WARP;\n      const size_t thread_tile_t_start_idx = warp_tile_t_start_idx +\n                                             tid_in_warp_x * THREAD_TILE_DIM_X * num_rows +\n                                             tid_in_warp_y * THREAD_TILE_DIM_Y;\n#pragma unroll\n      for (int i = 0; i < thread_tile_ncols; i++) {\n        thrd_tile_out_trans[i].store_to_elts(output_t + thread_tile_t_start_idx + i * num_rows, 0,\n                                             thread_tile_nrows);\n      }\n    }\n  }\n}\n\ntemplate <typename OutputType>\nCUtensorMap get_tensor_map(const SimpleTensor& tensor, size_t global_dim_x, size_t global_dim_y) {\n  CUtensorMapDataType dataType;\n  if constexpr (std::is_same_v<OutputType, __hip_fp8_e4m3_fnuz> ||\n                std::is_same_v<OutputType, __hip_fp8_e5m2_fnuz>) {\n    dataType = CUtensorMapDataType::CU_TENSOR_MAP_DATA_TYPE_UINT8;\n  } else {\n    NVTE_ERROR(\n        \"Invalid output type for blockwise transpose (must be FP8: Float8E4M3 or Float8E5M2).\");\n  }\n\n  CUtensorMap tensor_map_output_trans{};\n  create_2D_tensor_map(tensor_map_output_trans, tensor, global_dim_y, global_dim_x,\n                       /*shmemY=*/BLOCK_TILE_DIM, /*shmemX=*/BLOCK_TILE_DIM,\n                       /*stride_elems=*/global_dim_x, /*offset_elems=*/0, sizeof(OutputType) * 8);\n  return tensor_map_output_trans;\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace transformer_engine::detail {\n\nvoid quantize_transpose_square_blockwise(const SimpleTensor& input, SimpleTensor& scale_inv,\n                                         SimpleTensor& scale_inv_t, SimpleTensor& output,\n                                         SimpleTensor& output_t, const float epsilon,\n                                         const bool return_transpose, const bool pow_2_scale,\n                                         const SimpleTensor& noop_tensor, hipStream_t stream) {\n  NVTE_API_CALL(quantize_transpose_square_blockwise);\n  checkCuDriverContext(stream);\n\n  if (transformer_engine::cuda::sm_arch() >= 100) {\n    NVTE_CHECK(pow_2_scale, \"On Blackwell and newer, the FP8 block scaling recipe is emulated \",\n               \"with MXFP8, which requires using power of two scaling factors.\");\n  }\n\n  NVTE_CHECK(input.shape == output.shape, \"Input and output must have the same shape.\");\n  const size_t row_length = input.shape.size() > 0 ? input.shape.back() : 1;\n  size_t num_rows = 1;\n  for (size_t i = 0; (i < input.shape.size() - 1) && (input.shape.size() > 0); ++i) {\n    num_rows *= input.shape.at(i);\n  }\n\n  NVTE_CHECK(scale_inv.shape.size() == 2, \"scale_inv must have 2 dimensions.\");\n\n  size_t scale_k = scale_inv.shape[1];\n\n  const size_t scale_stride_x = 1;\n  const size_t scale_stride_y = scale_k;\n\n  size_t scale_t_stride_x = 0;\n  size_t scale_t_stride_y = 0;\n\n  const float* noop_ptr = reinterpret_cast<const float*>(noop_tensor.dptr);\n\n  if (return_transpose) {\n    NVTE_CHECK(output_t.shape.size() == input.shape.size(), \"input (shape=\", input.shape,\n               \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n    if (output_t.shape.size() > 0) {\n      NVTE_CHECK(output_t.shape.front() == input.shape.back(), \"input (shape=\", input.shape,\n                 \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n      for (size_t i = 1; i < output_t.shape.size(); ++i) {\n        NVTE_CHECK(output_t.shape[i] == input.shape[i - 1], \"input (shape=\", input.shape,\n                   \") and output_t (shape=\", output_t.shape, \") have incompatible dims.\");\n      }\n    }\n    NVTE_CHECK(output.dtype == output_t.dtype, \"output and output_t need to have the same type.\");\n\n    NVTE_CHECK(scale_inv_t.shape.size() == 2, \"scale_inv_t must have 2 dimensions.\");\n\n    scale_t_stride_x = 1;\n    scale_t_stride_y = scale_inv_t.shape[1];\n  }\n\n  const size_t num_blocks_x = DIVUP(row_length, BLOCK_TILE_DIM);\n  const size_t num_blocks_y = DIVUP(num_rows, BLOCK_TILE_DIM);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.dtype, InputType,\n\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          output.dtype, OutputType,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_transpose, kReturnTranspose,\n\n              dim3 grid(num_blocks_x, num_blocks_y, 1);\n              const bool full_tile =\n                  row_length % BLOCK_TILE_DIM == 0 && num_rows % BLOCK_TILE_DIM == 0;\n\n              if (full_tile) {\n                CUtensorMap tensor_map_output_trans;\n                if (return_transpose) {\n                  tensor_map_output_trans =\n                      get_tensor_map<OutputType>(output_t, num_rows, row_length);\n                }\n                block_scaled_cast_transpose_kernel<kReturnTranspose, float, InputType, OutputType>\n                    <<<grid, THREADS_PER_BLOCK, 0, stream>>>(\n                        reinterpret_cast<const InputType*>(input.dptr),\n                        reinterpret_cast<OutputType*>(output.dptr),\n                        reinterpret_cast<OutputType*>(output_t.dptr),\n                        reinterpret_cast<float*>(scale_inv.dptr),\n                        reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows,\n                        scale_stride_x, scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon,\n                        tensor_map_output_trans, pow_2_scale, noop_ptr);\n              } else {\n                block_scaled_cast_transpose_kernel_notaligned<kReturnTranspose, float, InputType,\n                                                              OutputType>\n                    <<<grid, THREADS_PER_BLOCK, 0, stream>>>(\n                        reinterpret_cast<const InputType*>(input.dptr),\n                        reinterpret_cast<OutputType*>(output.dptr),\n                        reinterpret_cast<OutputType*>(output_t.dptr),\n                        reinterpret_cast<float*>(scale_inv.dptr),\n                        reinterpret_cast<float*>(scale_inv_t.dptr), row_length, num_rows,\n                        scale_stride_x, scale_stride_y, scale_t_stride_x, scale_t_stride_y, epsilon,\n                        pow_2_scale, noop_ptr);\n              }  // full-tile\n              )  // return_transpose\n          )      // OutputType\n      )          // InputType\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace transformer_engine::detail\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/quantize_transpose_square_blockwise.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing VectorType = BytesToType<__VECTOR_SIZE__>::Type;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size)\n    swap_first_dims_kernel(const VectorType* __restrict__ const input,\n                           VectorType* __restrict__ const output, const size_t dim0,\n                           const size_t dim1, const size_t dim2) {\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n#if __SINGLE_LOAD_STORE__\n  const auto idx = gid;\n#else\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t idx = gid; idx < dim0 * dim1 * dim2; idx += nthreads)\n#endif  // __SINGLE_LOAD_STORE__\n  {\n    const auto idx2 = idx % dim2;\n    const auto idx1 = (idx / dim2) % dim1;\n    const auto idx0 = (idx / dim2) / dim1;\n    const auto in_offset = idx1 * dim0 * dim2 + idx0 * dim2 + idx2;\n    output[idx] = input[in_offset];\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing VectorType = BytesToType<__VECTOR_SIZE__>::Type;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size)\n    swap_first_dims_kernel(const VectorType* __restrict__ const input,\n                           VectorType* __restrict__ const output, const size_t dim0,\n                           const size_t dim1, const size_t dim2) {\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n#if __SINGLE_LOAD_STORE__\n  const auto idx = gid;\n#else\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t idx = gid; idx < dim0 * dim1 * dim2; idx += nthreads)\n#endif  // __SINGLE_LOAD_STORE__\n  {\n    const auto idx2 = idx % dim2;\n    const auto idx1 = (idx / dim2) % dim1;\n    const auto idx0 = (idx / dim2) / dim1;\n    const auto in_offset = idx1 * dim0 * dim2 + idx0 * dim2 + idx2;\n    output[idx] = input[in_offset];\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/rtc/swap_first_dims.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing Type = __TYPE__;\nconstexpr size_t load_size = __LOAD_SIZE__;\nconstexpr size_t store_size = __STORE_SIZE__;\nconstexpr size_t warps_per_tile = __WARPS_PER_TILE__;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size)\n    transpose_optimized_kernel(const Type* __restrict__ const input, const float* const noop,\n                               Type* __restrict__ const output, const size_t row_length,\n                               const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(Type);\n  constexpr size_t nvec_out = store_size / sizeof(Type);\n  using IVec = Vec<Type, nvec_in>;\n  using OVec = Vec<Type, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = num_rows / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // Load input to registers and transpose\n  // Note: Each thread loads num_iterations subtiles and transposes in\n  // registers.\n  OVec local_output[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      local_input.load_from(&input[row * row_length + col]);\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        local_output[j2][iter].data.elt[i2] = local_input.data.elt[j2];\n      }\n    }\n  }\n\n  // Copy from registers to shared memory to global memory\n  __shared__ OVec shared_output[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output[j1][i1] = local_output[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      shared_output[j1][i1].store_to(&output[col * num_rows + row]);\n    }\n    __syncthreads();\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing Type = __TYPE__;\nconstexpr size_t load_size = __LOAD_SIZE__;\nconstexpr size_t store_size = __STORE_SIZE__;\nconstexpr size_t warps_per_tile = __WARPS_PER_TILE__;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size)\n    transpose_optimized_kernel(const Type* __restrict__ const input, const float* const noop,\n                               Type* __restrict__ const output, const size_t row_length,\n                               const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(Type);\n  constexpr size_t nvec_out = store_size / sizeof(Type);\n  using IVec = Vec<Type, nvec_in>;\n  using OVec = Vec<Type, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = num_rows / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // Load input to registers and transpose\n  // Note: Each thread loads num_iterations subtiles and transposes in\n  // registers.\n  OVec local_output[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      local_input.load_from(&input[row * row_length + col]);\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        local_output[j2][iter].data.elt[i2] = local_input.data.elt[j2];\n      }\n    }\n  }\n\n  // Copy from registers to shared memory to global memory\n  __shared__ OVec shared_output[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output[j1][i1] = local_output[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      shared_output[j1][i1].store_to(&output[col * num_rows + row]);\n    }\n    __syncthreads();\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/rtc/transpose.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"util/math.h\"\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing CType = float;\nusing IType = __ITYPE__;\nusing IType2 = __ITYPE2__;\nusing OType = __OTYPE__;\nconstexpr size_t LOAD_SIZE = __LOAD_SIZE__;\nconstexpr size_t STORE_SIZE = __STORE_SIZE__;\nconstexpr size_t WARPS_PER_TILE = __WARPS_PER_TILE__;\nconstexpr size_t BLOCK_SIZE = __BLOCK_SIZE__;\nconstexpr bool IS_DBIAS = __IS_DBIAS__;\nconstexpr bool IS_DACT = __IS_DACT__;\nconstexpr bool IS_ACT = __IS_ACT__;\nstatic_assert(!(IS_DACT && IS_ACT), \"forward and backward activation are mutually exclusive\");\nconstexpr size_t ACT_TYPE = __ACTIVATION_TYPE__;\n\nconstexpr size_t NVEC_IN = LOAD_SIZE / sizeof(IType);\nconstexpr size_t NVEC_OUT = STORE_SIZE / sizeof(OType);\nusing CVec = Vec<CType, NVEC_IN>;\nusing IVec = Vec<IType, NVEC_IN>;\nusing IVec2 = Vec<IType2, NVEC_IN>;\nusing OVec = Vec<OType, NVEC_OUT>;\nusing Param = CTDBiasDActParam<IType, IType2, OType, CType>;\n\nusing OP = CType (*)(const CType, const Empty &);\nconstexpr OP ActivationList[] = {\n    nullptr,                  // 0\n    &sigmoid<CType, CType>,   // 1\n    &dsigmoid<CType, CType>,  // 2\n    &gelu<CType, CType>,      // 3\n    &dgelu<CType, CType>,     // 4\n    &qgelu<CType, CType>,     // 5\n    &dqgelu<CType, CType>,    // 6\n    &silu<CType, CType>,      // 7\n    &dsilu<CType, CType>,     // 8\n    &relu<CType, CType>,      // 9\n    &drelu<CType, CType>,     // 10\n    &srelu<CType, CType>,     // 11\n    &dsrelu<CType, CType>     // 12\n};\n\n}  // namespace\n\ninline __device__ void cast_and_transpose_regs_optimized(const CVec (&in)[NVEC_OUT],\n                                                         OVec (&out_trans)[NVEC_IN],\n                                                         CVec &out_dbias,  // NOLINT(*)\n                                                         typename OVec::type *output_cast_tile,\n                                                         const size_t current_place,\n                                                         const size_t stride, const CType scale,\n                                                         CType &amax,  // NOLINT(*)\n                                                         const int dbias_shfl_src_lane) {\n  using OVecC = Vec<OType, NVEC_IN>;\n\n  CVec step_dbias;\n  if constexpr (IS_DBIAS) {\n    step_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_OUT; ++i) {\n    OVecC out_cast;\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_IN; ++j) {\n      const CType tmp = in[i].data.elt[j];\n      if constexpr (IS_DBIAS) {\n        step_dbias.data.elt[j] += tmp;  // dbias: thread tile local accumulation\n      }\n      out_cast.data.elt[j] = static_cast<OType>(tmp * scale);\n      out_trans[j].data.elt[i] = static_cast<OType>(tmp * scale);  // thread tile transpose\n\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(fabsf(tmp), amax);\n    }\n    out_cast.store_to(output_cast_tile, current_place + stride * i);\n  }\n\n  if constexpr (IS_DBIAS) {\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_IN; ++j) {\n      CType elt = step_dbias.data.elt[j];\n      elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in a warp\n      out_dbias.data.elt[j] += elt;\n    }\n  }\n}\n\n__global__ void __launch_bounds__(BLOCK_SIZE)\n    cast_transpose_fusion_kernel_optimized(const Param param, const size_t row_length,\n                                           const size_t num_rows, const size_t num_tiles) {\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (NVEC_IN * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * WARPS_PER_TILE) + warp_id / WARPS_PER_TILE;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const size_t tile_offset =\n      (tile_id_x * NVEC_IN + tile_id_y * row_length * NVEC_OUT) * THREADS_PER_WARP;\n  const size_t tile_offset_transp =\n      (tile_id_y * NVEC_OUT + tile_id_x * num_rows * NVEC_IN) * THREADS_PER_WARP;\n\n  const IType *const my_input_tile = param.input + tile_offset;\n  const IType2 *const my_act_input_tile = param.act_input + tile_offset;\n  OType *const my_output_c_tile = param.output_c + tile_offset;\n  OType *const my_output_t_tile = param.output_t + tile_offset_transp;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (NVEC_IN * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / WARPS_PER_TILE * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][NVEC_OUT];\n  IVec2 act_in[2][NVEC_OUT];\n\n  const unsigned int warp_id_in_tile = warp_id % WARPS_PER_TILE;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / WARPS_PER_TILE;\n  OVec out_space[n_iterations][NVEC_IN];\n\n  const size_t stride = row_length / NVEC_IN;\n  const size_t output_stride = num_rows / NVEC_OUT;\n  size_t current_stride = warp_id_in_tile * n_iterations * NVEC_OUT * stride;\n  size_t current_row = (tile_id_y * THREADS_PER_WARP + warp_id_in_tile * n_iterations) * NVEC_OUT;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n\n  CType amax = 0.0f;\n  const CType scale = param.scale_ptr != nullptr ? *param.scale_ptr : 1;\n\n  CVec partial_dbias;\n  if constexpr (IS_DBIAS) {\n    partial_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_OUT; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n    if constexpr (IS_DACT) {\n      act_in[0][i].load_from(my_act_input_tile, current_stride + my_place + stride * i);\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < NVEC_OUT; ++j) {\n        const size_t ld_offset = current_stride + my_place_in + stride * (NVEC_OUT + j);\n        in[current_in][j].load_from(my_input_tile, ld_offset);\n        if constexpr (IS_DACT) {\n          act_in[current_in][j].load_from(my_act_input_tile, ld_offset);\n        }\n      }\n    }\n    CVec in_cast_fp32[NVEC_OUT];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_OUT; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < NVEC_IN; ++k) {\n        if constexpr (IS_DACT) {\n          in_cast_fp32[j].data.elt[k] =\n              static_cast<CType>(in[current_in ^ 1][j].data.elt[k]) *\n              ActivationList[ACT_TYPE](act_in[current_in ^ 1][j].data.elt[k], {});\n        } else if constexpr (IS_ACT) {\n          in_cast_fp32[j].data.elt[k] =\n              ActivationList[ACT_TYPE](in[current_in ^ 1][j].data.elt[k], {});\n        } else {\n          in_cast_fp32[j].data.elt[k] = static_cast<CType>(in[current_in ^ 1][j].data.elt[k]);\n        }\n      }\n    }\n\n    const int dbias_shfl_src_lane =\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n\n    cast_and_transpose_regs_optimized(in_cast_fp32, out_space[i], partial_dbias, my_output_c_tile,\n                                      current_place, stride, scale, amax, dbias_shfl_src_lane);\n\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += NVEC_OUT * stride;\n    current_row += NVEC_OUT;\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_IN; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * NVEC_IN;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * NVEC_IN;\n    }\n    __syncthreads();\n  }\n\n  if constexpr (IS_DBIAS) {\n    my_dbias_scratch[threadIdx.x] = partial_dbias;\n    __syncthreads();\n    if (warp_id_in_tile == 0) {\n#pragma unroll\n      for (unsigned int i = 1; i < WARPS_PER_TILE; ++i) {\n        CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n        for (unsigned int j = 0; j < NVEC_IN; ++j) {\n          partial_dbias.data.elt[j] += tmp.data.elt[j];\n        }\n      }\n      partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n    }\n  }\n\n  // Reduce amax over block\n  if (param.amax != nullptr) {\n    const CType max_block = reduce_max<BLOCK_SIZE / THREADS_PER_WARP>(amax, warp_id);\n    if (threadIdx.x == 0) {\n      atomicMaxFloat(param.amax, max_block);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && param.scale_inv != nullptr) {\n    reciprocal<CType>(param.scale_inv, scale);\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"util/math.h\"\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing CType = float;\nusing IType = __ITYPE__;\nusing IType2 = __ITYPE2__;\nusing OType = __OTYPE__;\nconstexpr size_t LOAD_SIZE = __LOAD_SIZE__;\nconstexpr size_t STORE_SIZE = __STORE_SIZE__;\nconstexpr size_t WARPS_PER_TILE = __WARPS_PER_TILE__;\nconstexpr size_t BLOCK_SIZE = __BLOCK_SIZE__;\nconstexpr bool IS_DBIAS = __IS_DBIAS__;\nconstexpr bool IS_DACT = __IS_DACT__;\nconstexpr bool IS_ACT = __IS_ACT__;\nstatic_assert(!(IS_DACT && IS_ACT), \"forward and backward activation are mutually exclusive\");\nconstexpr size_t ACT_TYPE = __ACTIVATION_TYPE__;\n\nconstexpr size_t NVEC_IN = LOAD_SIZE / sizeof(IType);\nconstexpr size_t NVEC_OUT = STORE_SIZE / sizeof(OType);\nusing CVec = Vec<CType, NVEC_IN>;\nusing IVec = Vec<IType, NVEC_IN>;\nusing IVec2 = Vec<IType2, NVEC_IN>;\nusing OVec = Vec<OType, NVEC_OUT>;\nusing Param = CTDBiasDActParam<IType, IType2, OType, CType>;\n\nusing OP = CType (*)(const CType, const Empty &);\nconstexpr OP ActivationList[] = {\n    nullptr,                  // 0\n    &sigmoid<CType, CType>,   // 1\n    &dsigmoid<CType, CType>,  // 2\n    &gelu<CType, CType>,      // 3\n    &dgelu<CType, CType>,     // 4\n    &qgelu<CType, CType>,     // 5\n    &dqgelu<CType, CType>,    // 6\n    &silu<CType, CType>,      // 7\n    &dsilu<CType, CType>,     // 8\n    &relu<CType, CType>,      // 9\n    &drelu<CType, CType>,     // 10\n    &srelu<CType, CType>,     // 11\n    &dsrelu<CType, CType>     // 12\n};\n\n}  // namespace\n\ninline __device__ void cast_and_transpose_regs_optimized(const CVec (&in)[NVEC_OUT],\n                                                         OVec (&out_trans)[NVEC_IN],\n                                                         CVec &out_dbias,  // NOLINT(*)\n                                                         typename OVec::type *output_cast_tile,\n                                                         const size_t current_place,\n                                                         const size_t stride, const CType scale,\n                                                         CType &amax,  // NOLINT(*)\n                                                         const int dbias_shfl_src_lane) {\n  using OVecC = Vec<OType, NVEC_IN>;\n\n  CVec step_dbias;\n  if constexpr (IS_DBIAS) {\n    step_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_OUT; ++i) {\n    OVecC out_cast;\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_IN; ++j) {\n      const CType tmp = in[i].data.elt[j];\n      if constexpr (IS_DBIAS) {\n        step_dbias.data.elt[j] += tmp;  // dbias: thread tile local accumulation\n      }\n      out_cast.data.elt[j] = static_cast<OType>(tmp * scale);\n      out_trans[j].data.elt[i] = static_cast<OType>(tmp * scale);  // thread tile transpose\n\n      __builtin_assume(amax >= 0);\n      amax = fmaxf(fabsf(tmp), amax);\n    }\n    out_cast.store_to(output_cast_tile, current_place + stride * i);\n  }\n\n  if constexpr (IS_DBIAS) {\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_IN; ++j) {\n      CType elt = step_dbias.data.elt[j];\n      elt = __shfl_sync(0xffffffff, elt, dbias_shfl_src_lane);  // shuffle data in a warp\n      out_dbias.data.elt[j] += elt;\n    }\n  }\n}\n\n__global__ void __launch_bounds__(BLOCK_SIZE)\n    cast_transpose_fusion_kernel_optimized(const Param param, const size_t row_length,\n                                           const size_t num_rows, const size_t num_tiles) {\n  extern __shared__ char scratch[];\n\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const unsigned int my_id_in_warp = threadIdx.x % THREADS_PER_WARP;\n  const size_t num_tiles_x = row_length / (NVEC_IN * THREADS_PER_WARP);\n  const size_t tile_id =\n      blockIdx.x * blockDim.x / (THREADS_PER_WARP * WARPS_PER_TILE) + warp_id / WARPS_PER_TILE;\n  if (tile_id >= num_tiles) {\n    return;\n  }\n\n  const size_t tile_id_x = tile_id % num_tiles_x;\n  const size_t tile_id_y = tile_id / num_tiles_x;\n\n  const size_t tile_offset =\n      (tile_id_x * NVEC_IN + tile_id_y * row_length * NVEC_OUT) * THREADS_PER_WARP;\n  const size_t tile_offset_transp =\n      (tile_id_y * NVEC_OUT + tile_id_x * num_rows * NVEC_IN) * THREADS_PER_WARP;\n\n  const IType *const my_input_tile = param.input + tile_offset;\n  const IType2 *const my_act_input_tile = param.act_input + tile_offset;\n  OType *const my_output_c_tile = param.output_c + tile_offset;\n  OType *const my_output_t_tile = param.output_t + tile_offset_transp;\n  CType *const my_partial_dbias_tile =\n      param.workspace + (tile_id_x * (NVEC_IN * THREADS_PER_WARP) + tile_id_y * row_length);\n\n  OVec *const my_scratch =\n      reinterpret_cast<OVec *>(scratch) +\n      (my_id_in_warp + warp_id / WARPS_PER_TILE * THREADS_PER_WARP) * (THREADS_PER_WARP + 1);\n\n  CVec *const my_dbias_scratch = reinterpret_cast<CVec *>(scratch);\n\n  IVec in[2][NVEC_OUT];\n  IVec2 act_in[2][NVEC_OUT];\n\n  const unsigned int warp_id_in_tile = warp_id % WARPS_PER_TILE;\n  constexpr unsigned int n_iterations = THREADS_PER_WARP / WARPS_PER_TILE;\n  OVec out_space[n_iterations][NVEC_IN];\n\n  const size_t stride = row_length / NVEC_IN;\n  const size_t output_stride = num_rows / NVEC_OUT;\n  size_t current_stride = warp_id_in_tile * n_iterations * NVEC_OUT * stride;\n  size_t current_row = (tile_id_y * THREADS_PER_WARP + warp_id_in_tile * n_iterations) * NVEC_OUT;\n  unsigned int my_place =\n      (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n\n  CType amax = 0.0f;\n  const CType scale = param.scale_ptr != nullptr ? *param.scale_ptr : 1;\n\n  CVec partial_dbias;\n  if constexpr (IS_DBIAS) {\n    partial_dbias.clear();\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_OUT; ++i) {\n    in[0][i].load_from(my_input_tile, current_stride + my_place + stride * i);\n    if constexpr (IS_DACT) {\n      act_in[0][i].load_from(my_act_input_tile, current_stride + my_place + stride * i);\n    }\n  }\n#pragma unroll\n  for (unsigned int i = 0; i < n_iterations; ++i) {\n    const size_t current_place = current_stride + my_place;\n    const unsigned int my_place_in = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    const unsigned int current_in = (i + 1) % 2;\n    if (i < n_iterations - 1) {\n#pragma unroll\n      for (unsigned int j = 0; j < NVEC_OUT; ++j) {\n        const size_t ld_offset = current_stride + my_place_in + stride * (NVEC_OUT + j);\n        in[current_in][j].load_from(my_input_tile, ld_offset);\n        if constexpr (IS_DACT) {\n          act_in[current_in][j].load_from(my_act_input_tile, ld_offset);\n        }\n      }\n    }\n    CVec in_cast_fp32[NVEC_OUT];  // NOLINT(*)\n#pragma unroll\n    for (unsigned int j = 0; j < NVEC_OUT; ++j) {\n#pragma unroll\n      for (unsigned int k = 0; k < NVEC_IN; ++k) {\n        if constexpr (IS_DACT) {\n          in_cast_fp32[j].data.elt[k] =\n              static_cast<CType>(in[current_in ^ 1][j].data.elt[k]) *\n              ActivationList[ACT_TYPE](act_in[current_in ^ 1][j].data.elt[k], {});\n        } else if constexpr (IS_ACT) {\n          in_cast_fp32[j].data.elt[k] =\n              ActivationList[ACT_TYPE](in[current_in ^ 1][j].data.elt[k], {});\n        } else {\n          in_cast_fp32[j].data.elt[k] = static_cast<CType>(in[current_in ^ 1][j].data.elt[k]);\n        }\n      }\n    }\n\n    const int dbias_shfl_src_lane =\n        (my_id_in_warp + i + warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n\n    cast_and_transpose_regs_optimized(in_cast_fp32, out_space[i], partial_dbias, my_output_c_tile,\n                                      current_place, stride, scale, amax, dbias_shfl_src_lane);\n\n    my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n    current_stride += NVEC_OUT * stride;\n    current_row += NVEC_OUT;\n  }\n\n#pragma unroll\n  for (unsigned int i = 0; i < NVEC_IN; ++i) {\n#pragma unroll\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[(my_id_in_warp + THREADS_PER_WARP - j - warp_id_in_tile * n_iterations) %\n                 THREADS_PER_WARP] = out_space[j][i];\n    }\n    __syncthreads();\n    my_place =\n        (my_id_in_warp + THREADS_PER_WARP - warp_id_in_tile * n_iterations) % THREADS_PER_WARP;\n    current_stride = i * output_stride + warp_id_in_tile * n_iterations * output_stride * NVEC_IN;\n    for (unsigned int j = 0; j < n_iterations; ++j) {\n      my_scratch[j + warp_id_in_tile * n_iterations].store_to(my_output_t_tile,\n                                                              current_stride + my_place);\n      my_place = (my_place + THREADS_PER_WARP - 1) % THREADS_PER_WARP;\n      current_stride += output_stride * NVEC_IN;\n    }\n    __syncthreads();\n  }\n\n  if constexpr (IS_DBIAS) {\n    my_dbias_scratch[threadIdx.x] = partial_dbias;\n    __syncthreads();\n    if (warp_id_in_tile == 0) {\n#pragma unroll\n      for (unsigned int i = 1; i < WARPS_PER_TILE; ++i) {\n        CVec tmp = my_dbias_scratch[threadIdx.x + i * THREADS_PER_WARP];\n#pragma unroll\n        for (unsigned int j = 0; j < NVEC_IN; ++j) {\n          partial_dbias.data.elt[j] += tmp.data.elt[j];\n        }\n      }\n      partial_dbias.store_to(my_partial_dbias_tile, my_id_in_warp);\n    }\n  }\n\n  // Reduce amax over block\n  if (param.amax != nullptr) {\n    const CType max_block = reduce_max<BLOCK_SIZE / THREADS_PER_WARP>(amax, warp_id);\n    if (threadIdx.x == 0) {\n      atomicMaxFloat(param.amax, max_block);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && param.scale_inv != nullptr) {\n    reciprocal<CType>(param.scale_inv, scale);\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/rtc/cast_transpose_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing CType = float;\nusing IType = __ITYPE__;\nusing OType = __OTYPE__;\nconstexpr size_t load_size = __LOAD_SIZE__;\nconstexpr size_t store_size = __STORE_SIZE__;\nconstexpr size_t warps_per_tile = __WARPS_PER_TILE__;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size) cast_transpose_optimized_kernel(\n    const IType* __restrict__ const input, const CType* __restrict__ const noop,\n    OType* __restrict__ const output_c, OType* __restrict__ const output_t,\n    const CType* __restrict__ const scale_ptr, CType* __restrict__ const amax_ptr,\n    CType* __restrict__ const scale_inv_ptr, const size_t row_length, const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(IType);\n  constexpr size_t nvec_out = store_size / sizeof(OType);\n  using IVec = Vec<IType, nvec_in>;\n  using OVecC = Vec<OType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = num_rows / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // FP8 factors\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType amax = 0;\n\n  // Load input to registers and transpose\n  // Note: Each thread loads num_iterations subtiles, computes amax,\n  // casts type, and transposes in registers.\n  OVecT local_output_t[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      OVecC local_output_c;\n      local_input.load_from(&input[row * row_length + col]);\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        const CType in = static_cast<CType>(local_input.data.elt[j2]);\n        const OType out = OType(in * scale);\n        __builtin_assume(amax >= 0);\n        amax = fmaxf(fabsf(in), amax);\n        local_output_c.data.elt[j2] = out;\n        local_output_t[j2][iter].data.elt[i2] = out;\n      }\n      local_output_c.store_to(&output_c[row * row_length + col]);\n    }\n  }\n\n  // Copy from registers to shared memory to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      shared_output_t[j1][i1].store_to(&output_t[col * num_rows + row]);\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax_ptr != nullptr) {\n    amax = reduce_max<warps_per_tile>(amax, tidy);\n    if (threadIdx.x == 0) {\n      atomicMaxFloat(amax_ptr, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.cuh\"\n\nusing namespace transformer_engine;\n\nnamespace {\n\n// Parameters\nusing CType = float;\nusing IType = __ITYPE__;\nusing OType = __OTYPE__;\nconstexpr size_t load_size = __LOAD_SIZE__;\nconstexpr size_t store_size = __STORE_SIZE__;\nconstexpr size_t warps_per_tile = __WARPS_PER_TILE__;\nconstexpr size_t block_size = __BLOCK_SIZE__;\n\n}  // namespace\n\n__global__ void __launch_bounds__(block_size) cast_transpose_optimized_kernel(\n    const IType* __restrict__ const input, const CType* __restrict__ const noop,\n    OType* __restrict__ const output_c, OType* __restrict__ const output_t,\n    const CType* __restrict__ const scale_ptr, CType* __restrict__ const amax_ptr,\n    CType* __restrict__ const scale_inv_ptr, const size_t row_length, const size_t num_rows) {\n  if (noop != nullptr && noop[0] == 1.0f) return;\n\n  // Vectorized load/store sizes\n  constexpr size_t nvec_in = load_size / sizeof(IType);\n  constexpr size_t nvec_out = store_size / sizeof(OType);\n  using IVec = Vec<IType, nvec_in>;\n  using OVecC = Vec<OType, nvec_in>;\n  using OVecT = Vec<OType, nvec_out>;\n\n  // Thread indices\n  // Note: Block is interpreted as a warp_size x num_warps grid\n  constexpr size_t bdimx = THREADS_PER_WARP;\n  constexpr size_t bdimy = warps_per_tile;\n  const size_t tid = threadIdx.x;\n  const size_t tidx = tid % bdimx;\n  const size_t tidy = tid / bdimx;\n  const size_t bid = blockIdx.x;\n\n  // Input tensors are divided into tiles\n  // Note: Each tile is a warp_size x warp_size grid of nvec_out x nvec_in subtiles\n  constexpr size_t tile_dim_m = THREADS_PER_WARP * nvec_out;\n  constexpr size_t tile_dim_n = THREADS_PER_WARP * nvec_in;\n\n  // Position of tile within tensor\n  const size_t num_tiles_m = num_rows / tile_dim_m;\n  const size_t tile_id_m = bid % num_tiles_m;\n  const size_t tile_id_n = bid / num_tiles_m;\n  const size_t tile_row = tile_id_m * tile_dim_m;\n  const size_t tile_col = tile_id_n * tile_dim_n;\n\n  // Number of nvec_out x nvec_in subtiles for each thread to\n  // load/store\n  constexpr size_t num_iterations = THREADS_PER_WARP / warps_per_tile;\n\n  // FP8 factors\n  const CType scale = scale_ptr == nullptr ? 1 : *scale_ptr;\n  CType amax = 0;\n\n  // Load input to registers and transpose\n  // Note: Each thread loads num_iterations subtiles, computes amax,\n  // casts type, and transposes in registers.\n  OVecT local_output_t[nvec_in][num_iterations];\n#pragma unroll\n  for (size_t iter = 0; iter < num_iterations; ++iter) {\n    const size_t i1 = tidy + iter * bdimy;\n    const size_t j1 = tidx;\n#pragma unroll\n    for (size_t i2 = 0; i2 < nvec_out; ++i2) {\n      const size_t row = tile_row + i1 * nvec_out + i2;\n      const size_t col = tile_col + j1 * nvec_in;\n      IVec local_input;\n      OVecC local_output_c;\n      local_input.load_from(&input[row * row_length + col]);\n#pragma unroll\n      for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n        const CType in = static_cast<CType>(local_input.data.elt[j2]);\n        const OType out = OType(in * scale);\n        __builtin_assume(amax >= 0);\n        amax = fmaxf(fabsf(in), amax);\n        local_output_c.data.elt[j2] = out;\n        local_output_t[j2][iter].data.elt[i2] = out;\n      }\n      local_output_c.store_to(&output_c[row * row_length + col]);\n    }\n  }\n\n  // Copy from registers to shared memory to global memory\n  __shared__ OVecT shared_output_t[THREADS_PER_WARP][THREADS_PER_WARP + 1];\n#pragma unroll\n  for (size_t j2 = 0; j2 < nvec_in; ++j2) {\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidy + iter * bdimy;\n      const size_t j1 = tidx;\n      shared_output_t[j1][i1] = local_output_t[j2][iter];\n    }\n    __syncthreads();\n#pragma unroll\n    for (size_t iter = 0; iter < num_iterations; ++iter) {\n      const size_t i1 = tidx;\n      const size_t j1 = tidy + iter * bdimy;\n      const size_t row = tile_row + i1 * nvec_out;\n      const size_t col = tile_col + j1 * nvec_in + j2;\n      shared_output_t[j1][i1].store_to(&output_t[col * num_rows + row]);\n    }\n    __syncthreads();\n  }\n\n  // Reduce amax over block\n  if (amax_ptr != nullptr) {\n    amax = reduce_max<warps_per_tile>(amax, tidy);\n    if (threadIdx.x == 0) {\n      atomicMaxFloat(amax_ptr, amax);\n    }\n  }\n\n  // Update scale-inverse\n  if (blockIdx.x == 0 && threadIdx.x == 0 && scale_inv_ptr != nullptr) {\n    reciprocal<CType>(scale_inv_ptr, scale);\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/transpose/rtc/cast_transpose.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <curand.h>\n#include <curand_kernel.h>\n#include <curand_philox4x32_x.h>\n\n#include <cmath>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n#include \"transformer_engine/dropout.h\"\n\nnamespace transformer_engine {\nnamespace {\n\n// RNG kernels process chunks of 16 entries\nconstexpr size_t rng_chunk_size = 16;\n\n// CUDA block size\nconstexpr size_t block_size = 128;\n\n// Vector class to help with vectorized memory accesses\ntemplate <typename T, size_t kSize>\nunion Vector {\n  using StorageType = typename BytesToType<sizeof(T) * kSize>::Type;\n  StorageType storage;\n  T entries[kSize];\n};\n\n/* Byte-wise less-than comparison\n *\n * Results are stored in each byte's most-significant bit (MSB). All\n * other bits are zero.\n */\n__device__ __forceinline__ uint32_t bytewise_less_than(uint32_t a, uint32_t b) {\n  // Compare low bits by masking MSBs and subtracting. The resulting\n  // MSBs are 0 if the low bits of a are less than the low bits of b.\n  uint32_t result = (a | 0x80808080) - (b & 0x7F7F7F7F);\n\n  // Bitwise logical op to get answer in MSBs\n  // Equivalent logic: result = (a == b) ? !result : b\n  asm(\"lop3.b32 %0, %1, %2, %3, 0x4D;\\n\\t\" : \"=r\"(result) : \"r\"(a), \"r\"(b), \"r\"(result));\n\n  // Mask out everything except MSBs and return\n  result &= 0x80808080;\n  return result;\n}\n\n/* Generate dropout mask with 16 bits.\n *\n * 1 corresponds to keep and 0 to drop.\n *\n * Consumes 4 values from cuRAND Philox generator.\n */\n__device__ __forceinline__ uint16_t make_16bit_mask(uint64_t chunk_idx, uint64_t rng_seed,\n                                                    uint64_t rng_offset,\n                                                    uint32_t bytewise_drop_prob) {\n  // Generate random bits\n  curandStatePhilox4_32_10_t state;\n  curand_init(rng_seed, chunk_idx, rng_offset, &state);\n  const uint4 rand_bits = curand4(&state);\n\n  // Compute mask\n  // Note: bytewise_less_than fills MSBs (bits 7, 15, 23, 31). By\n  // shifting 2 bits after every call, every other bit will be filled.\n  uint32_t result = bytewise_less_than(rand_bits.x, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.y, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.z, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.w, bytewise_drop_prob);\n\n  // Consolidate mask in lowest 16 bits\n  result |= result >> 17;\n\n  // Flip bits so 0 corresponds to drop\n  result = ~result;\n\n  return result;\n}\n\n// Dropout forward with FP16/BF16 input and output.\ntemplate <typename T>\n__global__ void __launch_bounds__(block_size)\n    dropout_kernel_fwd_f16(const T *__restrict__ input_ptr, T *__restrict__ output_ptr,\n                           uint8_t *__restrict__ mask_ptr,\n                           const uint64_t *__restrict__ rng_state_ptr, size_t num_chunks,\n                           uint32_t bytewise_drop_prob, float scale) {\n  static_assert(sizeof(T) == 2);\n\n  // Each thread processes a chunk of 16 entries\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Generate dropout mask\n    auto local_mask =\n        make_16bit_mask(chunk_idx, rng_state_ptr[0], rng_state_ptr[1], bytewise_drop_prob);\n    reinterpret_cast<uint16_t *>(mask_ptr)[chunk_idx] = local_mask;\n\n    // Read input data\n    using VectorType = Vector<T, rng_chunk_size>;\n    VectorType local_data;\n    local_data = reinterpret_cast<const VectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n#pragma unroll\n    for (size_t i = 0; i < rng_chunk_size; i++) {\n      float val = static_cast<float>(local_data.entries[i]);\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_data.entries[i] = static_cast<T>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<VectorType *>(output_ptr)[chunk_idx] = local_data;\n  }\n}\n\n// Dropout forward with FP8 input and FP16/BF16 output.\ntemplate <typename InputType, typename OutputType>\n__global__ void __launch_bounds__(block_size)\n    dropout_kernel_fwd_fp8(const InputType *__restrict__ input_ptr,\n                           const float *__restrict__ input_scale_inv_ptr,\n                           OutputType *__restrict__ output_ptr, uint8_t *__restrict__ mask_ptr,\n                           const uint64_t *__restrict__ rng_state_ptr, size_t num_chunks,\n                           uint32_t bytewise_drop_prob, float scale) {\n  static_assert(sizeof(InputType) == 1);\n  static_assert(sizeof(OutputType) == 2);\n  const float input_scale_inv = *input_scale_inv_ptr;\n\n  // Each thread processes a chunk of 16 entries\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Generate dropout mask\n    auto local_mask =\n        make_16bit_mask(chunk_idx, rng_state_ptr[0], rng_state_ptr[1], bytewise_drop_prob);\n    reinterpret_cast<uint16_t *>(mask_ptr)[chunk_idx] = local_mask;\n\n    // Read input data\n    using InputVectorType = Vector<InputType, rng_chunk_size>;\n    InputVectorType local_input;\n    local_input = reinterpret_cast<const InputVectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n    using OutputVectorType = Vector<OutputType, rng_chunk_size>;\n    OutputVectorType local_output;\n#pragma unroll\n    for (size_t i = 0; i < rng_chunk_size; i++) {\n      float val = static_cast<float>(local_input.entries[i]);\n      val *= input_scale_inv;\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_output.entries[i] = static_cast<OutputType>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<OutputVectorType *>(output_ptr)[chunk_idx] = local_output;\n  }\n}\n\n// Apply dropout mask and scale.\ntemplate <typename T>\n__global__ void __launch_bounds__(block_size)\n    apply_dropout_mask(const T *__restrict__ input_ptr, const uint8_t *__restrict__ mask_ptr,\n                       T *__restrict__ output_ptr, size_t num_chunks, float scale) {\n  // Each thread processes a chunk of 8 entries.\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  constexpr size_t chunk_size = 8;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Read dropout mask\n    uint8_t local_mask = mask_ptr[chunk_idx];\n\n    // Read input data\n    using VectorType = Vector<T, chunk_size>;\n    VectorType local_data;\n    local_data = reinterpret_cast<const VectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n#pragma unroll\n    for (size_t i = 0; i < chunk_size; i++) {\n      float val = static_cast<float>(local_data.entries[i]);\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_data.entries[i] = static_cast<T>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<VectorType *>(output_ptr)[chunk_idx] = local_data;\n  }\n}\n\n}  // namespace\n\nvoid dropout_fwd(const Tensor &input, Tensor &output, Tensor &mask, Tensor &rng_state,\n                 float dropout_probability, cudaStream_t stream) {\n  // Check tensors\n  const size_t numel = input.numel();\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be FP16/BF16 tensor or tensor-scaled FP8 tensor, \",\n             \"but scaling mode is \", to_string(input.scaling_mode), \".\");\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(output.scaling_mode), \".\");\n  NVTE_CHECK(mask.scaling_mode == NVTE_DELAYED_TENSOR_SCALING, \"Mask tensor must be plain tensor, \",\n             \"but scaling mode is \", to_string(mask.scaling_mode), \".\");\n  NVTE_CHECK(rng_state.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"RNG state tensor must be INT64 tensor with two entries, \", \"but scaling mode is \",\n             to_string(rng_state.scaling_mode), \".\");\n  NVTE_CHECK(output.dtype() == DType::kFloat16 || output.dtype() == DType::kBFloat16,\n             \"Output tensor must be FP16/BF16 tensor, but dtype is \", to_string(output.dtype()),\n             \".\");\n  NVTE_CHECK(rng_state.dtype() == DType::kInt64,\n             \"RNG state tensor must be INT64 tensor with two entries, but dtype is \",\n             to_string(rng_state.dtype()), \".\");\n  NVTE_CHECK(numel % 16 == 0,\n             \"Input tensor number of elements must be divisible by 16, but shape is \",\n             input.shape(), \".\");\n  NVTE_CHECK(numel == output.numel(), \"Input tensor (shape=\", input.shape(),\n             \") and output tensor (shape=\", output.shape(), \") do not match.\");\n  NVTE_CHECK(typeToNumBits(mask.dtype()) * mask.numel() == numel, \"Mask tensor must have \", numel,\n             \" bits, but found dtype=\", to_string(mask.dtype()), \" and shape=\", mask.shape(), \".\");\n  NVTE_CHECK(rng_state.numel() == 2, \"RNG state tensor must be INT64 tensor with two entries, \",\n             \"but shape is \", rng_state.shape(), \".\");\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input tensor is missing data.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output tensor is missing data.\");\n  NVTE_CHECK(mask.data.dptr != nullptr, \"Mask tensor is missing data.\");\n  NVTE_CHECK(rng_state.data.dptr != nullptr, \"RNG state tensor is missing data.\");\n\n  // Convert dropout probablity to scale and 8-bit representation\n  NVTE_CHECK(dropout_probability >= 0 && dropout_probability < 1, \"Invalid dropout probability (\",\n             dropout_probability, \").\");\n  const float scale = 1 / (1 - dropout_probability);\n  uint32_t bytewise_drop_prob = static_cast<uint32_t>(std::floor(dropout_probability * 256));\n  bytewise_drop_prob |= bytewise_drop_prob << 8;\n  bytewise_drop_prob |= bytewise_drop_prob << 16;\n\n  // CUDA config\n  const size_t num_chunks = numel / rng_chunk_size;\n  const size_t num_blocks = DIVUP(num_chunks, block_size);\n\n  // Launch kernel depending on input dtype\n  if (input.dtype() == DType::kFloat16 || input.dtype() == DType::kBFloat16) {\n    NVTE_CHECK(input.dtype() == output.dtype(), \"Input tensor (dtype=\", to_string(input.dtype()),\n               \") and output tensor (dtype=\", to_string(output.dtype()), \") do not match.\");\n    TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n        input.dtype(), DType,\n        dropout_kernel_fwd_f16<DType><<<num_blocks, block_size, 0, stream>>>(\n            reinterpret_cast<const DType *>(input.data.dptr),\n            reinterpret_cast<DType *>(output.data.dptr),\n            reinterpret_cast<uint8_t *>(mask.data.dptr),\n            reinterpret_cast<const uint64_t *>(rng_state.data.dptr), num_chunks, bytewise_drop_prob,\n            scale););\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else if (input.dtype() == DType::kFloat8E4M3 || input.dtype() == DType::kFloat8E5M2) {\n    NVTE_CHECK(input.scale_inv.dptr != nullptr, \"Input tensor scale-inverse is not allocated.\");\n    TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n        input.dtype(), InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n            output.dtype(), OutputType,\n            dropout_kernel_fwd_fp8<InputType, OutputType><<<num_blocks, block_size, 0, stream>>>(\n                reinterpret_cast<const InputType *>(input.data.dptr),\n                reinterpret_cast<const float *>(input.scale_inv.dptr),\n                reinterpret_cast<OutputType *>(output.data.dptr),\n                reinterpret_cast<uint8_t *>(mask.data.dptr),\n                reinterpret_cast<const uint64_t *>(rng_state.data.dptr), num_chunks,\n                bytewise_drop_prob, scale);\n\n        ););\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    NVTE_ERROR(\"Input tensor must be FP16/BF16 tensor or tensor-scaled FP8 tensor, \",\n               \"but dtype is \", to_string(input.dtype()), \".\");\n  }\n}\n\nvoid dropout_bwd(const Tensor &grad_output, const Tensor &mask, Tensor &grad_input,\n                 float dropout_probability, cudaStream_t stream) {\n  // Check tensors\n  const size_t numel = grad_output.numel();\n  NVTE_CHECK(grad_output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Grad output tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(grad_output.scaling_mode), \".\");\n  NVTE_CHECK(grad_input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Grad input tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(grad_input.scaling_mode), \".\");\n  NVTE_CHECK(mask.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Mask tensor must be a plain tensor, but scaling mode is \",\n             to_string(mask.scaling_mode), \".\");\n  NVTE_CHECK(grad_output.dtype() == DType::kFloat16 || grad_output.dtype() == DType::kBFloat16,\n             \"Grad output tensor must be FP16/BF16 tensor, but dtype is \",\n             to_string(grad_output.dtype()), \".\");\n  NVTE_CHECK(grad_output.dtype() == grad_input.dtype(),\n             \"Grad output tensor (dtype=\", to_string(grad_output.dtype()),\n             \") and grad input tensor (dtype=\", to_string(grad_input.dtype()), \") do not match.\");\n  NVTE_CHECK(numel % 16 == 0,\n             \"Grad output tensor number of elements must be divisible by 16, but shape is \",\n             grad_output.shape(), \".\");\n  NVTE_CHECK(numel == grad_input.numel(), \"Grad output tensor (shape=\", grad_output.shape(),\n             \") and grad input tensor (shape=\", grad_input.shape(), \") do not match.\");\n  NVTE_CHECK(typeToNumBits(mask.dtype()) * mask.numel() == numel, \"Mask tensor must have \", numel,\n             \" bits, but found dtype=\", to_string(mask.dtype()), \" and shape=\", mask.shape(), \".\");\n  NVTE_CHECK(grad_output.data.dptr != nullptr, \"Grad output tensor is missing data.\");\n  NVTE_CHECK(grad_input.data.dptr != nullptr, \"Grad input tensor is missing data.\");\n  NVTE_CHECK(mask.data.dptr != nullptr, \"Mask tensor is missing data.\");\n\n  // Convert dropout probablity to scale\n  NVTE_CHECK(dropout_probability >= 0 && dropout_probability < 1, \"Invalid dropout probability (\",\n             dropout_probability, \").\");\n  const float scale = 1 / (1 - dropout_probability);\n\n  // CUDA config\n  const size_t num_chunks = numel / 8;\n  const size_t num_blocks = DIVUP(num_chunks, block_size);\n\n  // Launch kernel\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      grad_output.dtype(), DType,\n      apply_dropout_mask<DType><<<num_blocks, block_size, 0, stream>>>(\n          reinterpret_cast<const DType *>(grad_output.data.dptr),\n          reinterpret_cast<const uint8_t *>(mask.data.dptr),\n          reinterpret_cast<DType *>(grad_input.data.dptr), num_chunks, scale););\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_dropout_fwd(const NVTETensor input, NVTETensor output, NVTETensor mask,\n                      NVTETensor rng_state, float dropout_probability, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dropout_fwd);\n  using namespace transformer_engine;\n  dropout_fwd(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n              *convertNVTETensorCheck(mask), *convertNVTETensorCheck(rng_state),\n              dropout_probability, stream);\n}\n\nvoid nvte_dropout_bwd(const NVTETensor grad_output, const NVTETensor mask, NVTETensor grad_input,\n                      float dropout_probability, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_dropout_bwd);\n  using namespace transformer_engine;\n  dropout_bwd(*convertNVTETensorCheck(grad_output), *convertNVTETensorCheck(mask),\n              *convertNVTETensorCheck(grad_input), dropout_probability, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <hiprand/hiprand.h>\n#include <hiprand/hiprand_kernel.h>\n\n#include <cmath>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n#include \"transformer_engine/dropout.h\"\n\nnamespace transformer_engine {\nnamespace {\n\n// RNG kernels process chunks of 16 entries\nconstexpr size_t rng_chunk_size = 16;\n\n// CUDA block size\nconstexpr size_t block_size = 128;\n\n// Vector class to help with vectorized memory accesses\ntemplate <typename T, size_t kSize>\nunion Vector {\n  using StorageType = typename BytesToType<sizeof(T) * kSize>::Type;\n  StorageType storage;\n  T entries[kSize];\n};\n\n/* Byte-wise less-than comparison\n *\n * Results are stored in each byte's most-significant bit (MSB). All\n * other bits are zero.\n */\n__device__ __forceinline__ uint32_t bytewise_less_than(uint32_t a, uint32_t b) {\n  // Compare low bits by masking MSBs and subtracting. The resulting\n  // MSBs are 0 if the low bits of a are less than the low bits of b.\n  uint32_t result = (a | 0x80808080) - (b & 0x7F7F7F7F);\n\n  // Bitwise logical op to get answer in MSBs\n  // Equivalent logic: result = (a == b) ? !result : b\n  asm(\"lop3.b32 %0, %1, %2, %3, 0x4D;\\n\\t\" : \"=r\"(result) : \"r\"(a), \"r\"(b), \"r\"(result));\n\n  // Mask out everything except MSBs and return\n  result &= 0x80808080;\n  return result;\n}\n\n/* Generate dropout mask with 16 bits.\n *\n * 1 corresponds to keep and 0 to drop.\n *\n * Consumes 4 values from cuRAND Philox generator.\n */\n__device__ __forceinline__ uint16_t make_16bit_mask(uint64_t chunk_idx, uint64_t rng_seed,\n                                                    uint64_t rng_offset,\n                                                    uint32_t bytewise_drop_prob) {\n  // Generate random bits\n  hiprandStatePhilox4_32_10_t state;\n  hiprand_init(rng_seed, chunk_idx, rng_offset, &state);\n  const uint4 rand_bits = curand4(&state);\n\n  // Compute mask\n  // Note: bytewise_less_than fills MSBs (bits 7, 15, 23, 31). By\n  // shifting 2 bits after every call, every other bit will be filled.\n  uint32_t result = bytewise_less_than(rand_bits.x, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.y, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.z, bytewise_drop_prob);\n  result = (result >> 2) | bytewise_less_than(rand_bits.w, bytewise_drop_prob);\n\n  // Consolidate mask in lowest 16 bits\n  result |= result >> 17;\n\n  // Flip bits so 0 corresponds to drop\n  result = ~result;\n\n  return result;\n}\n\n// Dropout forward with FP16/BF16 input and output.\ntemplate <typename T>\n__global__ void __launch_bounds__(block_size)\n    dropout_kernel_fwd_f16(const T *__restrict__ input_ptr, T *__restrict__ output_ptr,\n                           uint8_t *__restrict__ mask_ptr,\n                           const uint64_t *__restrict__ rng_state_ptr, size_t num_chunks,\n                           uint32_t bytewise_drop_prob, float scale) {\n  static_assert(sizeof(T) == 2);\n\n  // Each thread processes a chunk of 16 entries\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Generate dropout mask\n    auto local_mask =\n        make_16bit_mask(chunk_idx, rng_state_ptr[0], rng_state_ptr[1], bytewise_drop_prob);\n    reinterpret_cast<uint16_t *>(mask_ptr)[chunk_idx] = local_mask;\n\n    // Read input data\n    using VectorType = Vector<T, rng_chunk_size>;\n    VectorType local_data;\n    local_data = reinterpret_cast<const VectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n#pragma unroll\n    for (size_t i = 0; i < rng_chunk_size; i++) {\n      float val = static_cast<float>(local_data.entries[i]);\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_data.entries[i] = static_cast<T>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<VectorType *>(output_ptr)[chunk_idx] = local_data;\n  }\n}\n\n// Dropout forward with FP8 input and FP16/BF16 output.\ntemplate <typename InputType, typename OutputType>\n__global__ void __launch_bounds__(block_size)\n    dropout_kernel_fwd_fp8(const InputType *__restrict__ input_ptr,\n                           const float *__restrict__ input_scale_inv_ptr,\n                           OutputType *__restrict__ output_ptr, uint8_t *__restrict__ mask_ptr,\n                           const uint64_t *__restrict__ rng_state_ptr, size_t num_chunks,\n                           uint32_t bytewise_drop_prob, float scale) {\n  static_assert(sizeof(InputType) == 1);\n  static_assert(sizeof(OutputType) == 2);\n  const float input_scale_inv = *input_scale_inv_ptr;\n\n  // Each thread processes a chunk of 16 entries\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Generate dropout mask\n    auto local_mask =\n        make_16bit_mask(chunk_idx, rng_state_ptr[0], rng_state_ptr[1], bytewise_drop_prob);\n    reinterpret_cast<uint16_t *>(mask_ptr)[chunk_idx] = local_mask;\n\n    // Read input data\n    using InputVectorType = Vector<InputType, rng_chunk_size>;\n    InputVectorType local_input;\n    local_input = reinterpret_cast<const InputVectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n    using OutputVectorType = Vector<OutputType, rng_chunk_size>;\n    OutputVectorType local_output;\n#pragma unroll\n    for (size_t i = 0; i < rng_chunk_size; i++) {\n      float val = static_cast<float>(local_input.entries[i]);\n      val *= input_scale_inv;\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_output.entries[i] = static_cast<OutputType>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<OutputVectorType *>(output_ptr)[chunk_idx] = local_output;\n  }\n}\n\n// Apply dropout mask and scale.\ntemplate <typename T>\n__global__ void __launch_bounds__(block_size)\n    apply_dropout_mask(const T *__restrict__ input_ptr, const uint8_t *__restrict__ mask_ptr,\n                       T *__restrict__ output_ptr, size_t num_chunks, float scale) {\n  // Each thread processes a chunk of 8 entries.\n  const size_t gid = threadIdx.x + blockIdx.x * block_size;\n  const size_t nthreads = gridDim.x * block_size;\n  constexpr size_t chunk_size = 8;\n  for (size_t chunk_idx = gid; chunk_idx < num_chunks; chunk_idx += nthreads) {\n    // Read dropout mask\n    uint8_t local_mask = mask_ptr[chunk_idx];\n\n    // Read input data\n    using VectorType = Vector<T, chunk_size>;\n    VectorType local_data;\n    local_data = reinterpret_cast<const VectorType *>(input_ptr)[chunk_idx];\n\n    // Apply dropout based on mask\n#pragma unroll\n    for (size_t i = 0; i < chunk_size; i++) {\n      float val = static_cast<float>(local_data.entries[i]);\n      if ((local_mask & 0x1) == 0) {\n        val = 0;\n      }\n      val *= scale;\n      local_data.entries[i] = static_cast<T>(val);\n      local_mask >>= 1;\n    }\n\n    // Write output data\n    reinterpret_cast<VectorType *>(output_ptr)[chunk_idx] = local_data;\n  }\n}\n\n}  // namespace\n\nvoid dropout_fwd(const Tensor &input, Tensor &output, Tensor &mask, Tensor &rng_state,\n                 float dropout_probability, hipStream_t stream) {\n  // Check tensors\n  const size_t numel = input.numel();\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be FP16/BF16 tensor or tensor-scaled FP8 tensor, \",\n             \"but scaling mode is \", to_string(input.scaling_mode), \".\");\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(output.scaling_mode), \".\");\n  NVTE_CHECK(mask.scaling_mode == NVTE_DELAYED_TENSOR_SCALING, \"Mask tensor must be plain tensor, \",\n             \"but scaling mode is \", to_string(mask.scaling_mode), \".\");\n  NVTE_CHECK(rng_state.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"RNG state tensor must be INT64 tensor with two entries, \", \"but scaling mode is \",\n             to_string(rng_state.scaling_mode), \".\");\n  NVTE_CHECK(output.dtype() == DType::kFloat16 || output.dtype() == DType::kBFloat16,\n             \"Output tensor must be FP16/BF16 tensor, but dtype is \", to_string(output.dtype()),\n             \".\");\n  NVTE_CHECK(rng_state.dtype() == DType::kInt64,\n             \"RNG state tensor must be INT64 tensor with two entries, but dtype is \",\n             to_string(rng_state.dtype()), \".\");\n  NVTE_CHECK(numel % 16 == 0,\n             \"Input tensor number of elements must be divisible by 16, but shape is \",\n             input.shape(), \".\");\n  NVTE_CHECK(numel == output.numel(), \"Input tensor (shape=\", input.shape(),\n             \") and output tensor (shape=\", output.shape(), \") do not match.\");\n  NVTE_CHECK(typeToNumBits(mask.dtype()) * mask.numel() == numel, \"Mask tensor must have \", numel,\n             \" bits, but found dtype=\", to_string(mask.dtype()), \" and shape=\", mask.shape(), \".\");\n  NVTE_CHECK(rng_state.numel() == 2, \"RNG state tensor must be INT64 tensor with two entries, \",\n             \"but shape is \", rng_state.shape(), \".\");\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input tensor is missing data.\");\n  NVTE_CHECK(output.data.dptr != nullptr, \"Output tensor is missing data.\");\n  NVTE_CHECK(mask.data.dptr != nullptr, \"Mask tensor is missing data.\");\n  NVTE_CHECK(rng_state.data.dptr != nullptr, \"RNG state tensor is missing data.\");\n\n  // Convert dropout probablity to scale and 8-bit representation\n  NVTE_CHECK(dropout_probability >= 0 && dropout_probability < 1, \"Invalid dropout probability (\",\n             dropout_probability, \").\");\n  const float scale = 1 / (1 - dropout_probability);\n  uint32_t bytewise_drop_prob = static_cast<uint32_t>(std::floor(dropout_probability * 256));\n  bytewise_drop_prob |= bytewise_drop_prob << 8;\n  bytewise_drop_prob |= bytewise_drop_prob << 16;\n\n  // CUDA config\n  const size_t num_chunks = numel / rng_chunk_size;\n  const size_t num_blocks = DIVUP(num_chunks, block_size);\n\n  // Launch kernel depending on input dtype\n  if (input.dtype() == DType::kFloat16 || input.dtype() == DType::kBFloat16) {\n    NVTE_CHECK(input.dtype() == output.dtype(), \"Input tensor (dtype=\", to_string(input.dtype()),\n               \") and output tensor (dtype=\", to_string(output.dtype()), \") do not match.\");\n    TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n        input.dtype(), DType,\n        dropout_kernel_fwd_f16<DType><<<num_blocks, block_size, 0, stream>>>(\n            reinterpret_cast<const DType *>(input.data.dptr),\n            reinterpret_cast<DType *>(output.data.dptr),\n            reinterpret_cast<uint8_t *>(mask.data.dptr),\n            reinterpret_cast<const uint64_t *>(rng_state.data.dptr), num_chunks, bytewise_drop_prob,\n            scale););\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else if (input.dtype() == DType::kFloat8E4M3 || input.dtype() == DType::kFloat8E5M2) {\n    NVTE_CHECK(input.scale_inv.dptr != nullptr, \"Input tensor scale-inverse is not allocated.\");\n    TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n        input.dtype(), InputType,\n        TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n            output.dtype(), OutputType,\n            dropout_kernel_fwd_fp8<InputType, OutputType><<<num_blocks, block_size, 0, stream>>>(\n                reinterpret_cast<const InputType *>(input.data.dptr),\n                reinterpret_cast<const float *>(input.scale_inv.dptr),\n                reinterpret_cast<OutputType *>(output.data.dptr),\n                reinterpret_cast<uint8_t *>(mask.data.dptr),\n                reinterpret_cast<const uint64_t *>(rng_state.data.dptr), num_chunks,\n                bytewise_drop_prob, scale);\n\n        ););\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    NVTE_ERROR(\"Input tensor must be FP16/BF16 tensor or tensor-scaled FP8 tensor, \",\n               \"but dtype is \", to_string(input.dtype()), \".\");\n  }\n}\n\nvoid dropout_bwd(const Tensor &grad_output, const Tensor &mask, Tensor &grad_input,\n                 float dropout_probability, hipStream_t stream) {\n  // Check tensors\n  const size_t numel = grad_output.numel();\n  NVTE_CHECK(grad_output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Grad output tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(grad_output.scaling_mode), \".\");\n  NVTE_CHECK(grad_input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Grad input tensor must be FP16/BF16 tensor, \", \"but scaling mode is \",\n             to_string(grad_input.scaling_mode), \".\");\n  NVTE_CHECK(mask.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Mask tensor must be a plain tensor, but scaling mode is \",\n             to_string(mask.scaling_mode), \".\");\n  NVTE_CHECK(grad_output.dtype() == DType::kFloat16 || grad_output.dtype() == DType::kBFloat16,\n             \"Grad output tensor must be FP16/BF16 tensor, but dtype is \",\n             to_string(grad_output.dtype()), \".\");\n  NVTE_CHECK(grad_output.dtype() == grad_input.dtype(),\n             \"Grad output tensor (dtype=\", to_string(grad_output.dtype()),\n             \") and grad input tensor (dtype=\", to_string(grad_input.dtype()), \") do not match.\");\n  NVTE_CHECK(numel % 16 == 0,\n             \"Grad output tensor number of elements must be divisible by 16, but shape is \",\n             grad_output.shape(), \".\");\n  NVTE_CHECK(numel == grad_input.numel(), \"Grad output tensor (shape=\", grad_output.shape(),\n             \") and grad input tensor (shape=\", grad_input.shape(), \") do not match.\");\n  NVTE_CHECK(typeToNumBits(mask.dtype()) * mask.numel() == numel, \"Mask tensor must have \", numel,\n             \" bits, but found dtype=\", to_string(mask.dtype()), \" and shape=\", mask.shape(), \".\");\n  NVTE_CHECK(grad_output.data.dptr != nullptr, \"Grad output tensor is missing data.\");\n  NVTE_CHECK(grad_input.data.dptr != nullptr, \"Grad input tensor is missing data.\");\n  NVTE_CHECK(mask.data.dptr != nullptr, \"Mask tensor is missing data.\");\n\n  // Convert dropout probablity to scale\n  NVTE_CHECK(dropout_probability >= 0 && dropout_probability < 1, \"Invalid dropout probability (\",\n             dropout_probability, \").\");\n  const float scale = 1 / (1 - dropout_probability);\n\n  // CUDA config\n  const size_t num_chunks = numel / 8;\n  const size_t num_blocks = DIVUP(num_chunks, block_size);\n\n  // Launch kernel\n  TRANSFORMER_ENGINE_TYPE_SWITCH_16BIT(\n      grad_output.dtype(), DType,\n      apply_dropout_mask<DType><<<num_blocks, block_size, 0, stream>>>(\n          reinterpret_cast<const DType *>(grad_output.data.dptr),\n          reinterpret_cast<const uint8_t *>(mask.data.dptr),\n          reinterpret_cast<DType *>(grad_input.data.dptr), num_chunks, scale););\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_dropout_fwd(const NVTETensor input, NVTETensor output, NVTETensor mask,\n                      NVTETensor rng_state, float dropout_probability, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dropout_fwd);\n  using namespace transformer_engine;\n  dropout_fwd(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n              *convertNVTETensorCheck(mask), *convertNVTETensorCheck(rng_state),\n              dropout_probability, stream);\n}\n\nvoid nvte_dropout_bwd(const NVTETensor grad_output, const NVTETensor mask, NVTETensor grad_input,\n                      float dropout_probability, hipStream_t stream) {\n  NVTE_API_CALL(nvte_dropout_bwd);\n  using namespace transformer_engine;\n  dropout_bwd(*convertNVTETensorCheck(grad_output), *convertNVTETensorCheck(mask),\n              *convertNVTETensorCheck(grad_input), dropout_probability, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/dropout/dropout.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n#include \"common/util/cuda_runtime.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, typename IndexType>\n__global__ void fused_moe_aux_loss_forward_kernel(const DataType* probs,\n                                                  const IndexType* tokens_per_expert,\n                                                  int total_num_tokens, int num_rows, int num_cols,\n                                                  int topk, float coeff, float* Coeff_buf) {\n  // -----------------------------------------------------------------------\n  // 1) Write the CPU-computed coefficient into a device buffer to re-use in BWD\n  // -----------------------------------------------------------------------\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    Coeff_buf[0] = coeff;\n  }\n\n  // -----------------------------------------------------------------------\n  // 2) Each CTA computes a partial dot-product:\n  //    Sigma_col ( Sigma_row probs[row, col] ) * tokens_per_expert[col]\n  // -----------------------------------------------------------------------\n  CompType thread_sum = CompType(0);\n\n  // Grid-stride over rows so that every row is processed exactly once.\n  // Each thread processes a subset of columns.\n  for (int col = threadIdx.x; col < num_cols; col += blockDim.x) {\n    CompType col_sum = CompType(0);\n\n    // Accumulate probs over the rows assigned to this CTA (grid-stride).\n    for (int row = blockIdx.x; row < num_rows; row += gridDim.x) {\n      col_sum += CompType(probs[row * num_cols + col]);\n    }\n\n    // Multiply by the token count for this expert.\n    col_sum *= CompType(tokens_per_expert[col]);\n\n    // Accumulate the per-column contribution into the thread-local sum.\n    thread_sum += col_sum;\n  }\n\n  // -----------------------------------------------------------------------\n  // 3) Block-level reduction of thread_sum using warp_reduce_on_shmem\n  // -----------------------------------------------------------------------\n  extern __shared__ float shmem[];\n  CompType* shmem_block = reinterpret_cast<CompType*>(shmem);\n  shmem_block[threadIdx.x] = thread_sum;\n  __syncthreads();\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int lane_id = threadIdx.x % kThreadsPerWarp;\n  if (warp_id == 0) {\n    CompType block_sum = warp_reduce_on_shmem(shmem_block, static_cast<int>(blockDim.x),\n                                              ReduceFuncType::SUM, lane_id);\n    if (lane_id == 0) {\n      atomicAdd(&Coeff_buf[1], static_cast<float>(block_sum * coeff));\n    }\n  }\n}\n\n// Small kernel to convert the float accumulator to the output DataType.\ntemplate <typename DataType>\n__global__ void convert_accum_to_output(const float* Coeff_buf, DataType* aux_loss) {\n  aux_loss[0] = static_cast<DataType>(Coeff_buf[1]);\n}\n\n/* -------------------------------------------------------------------------\n *  Kernel launcher -- simplified (no cluster launch).\n * ------------------------------------------------------------------------- */\ntemplate <typename DataType, typename IndexType>\nvoid fused_moe_aux_loss_forward_kernel_launcher(const DataType* probs,\n                                                const IndexType* tokens_per_expert,\n                                                int total_num_tokens, int num_experts, int num_rows,\n                                                int num_cols, int topk, float coeff,\n                                                DataType* aux_loss, float* Coeff_buf,\n                                                cudaStream_t stream) {\n  NVTE_CHECK(num_experts == num_cols, \"Number of experts (\", num_experts,\n             \") must be equal to number of input columns (\", num_cols, \").\");\n\n  // Round up to a multiple of warp size for correct warp shuffles.\n  const int block_size = ((std::min(1024, num_cols) + static_cast<int>(kThreadsPerWarp) - 1) /\n                          static_cast<int>(kThreadsPerWarp)) *\n                         static_cast<int>(kThreadsPerWarp);\n  const int grid_size = cuda::sm_count() * 2;\n\n  // One CompType per thread in shared memory.\n  const size_t smem_size = block_size * sizeof(CompType);\n  check_shared_memory_capacity_num_experts(smem_size, num_experts);\n\n  // Compute final coefficient and zero the float accumulator (Coeff_buf[1]) before launch.\n  const float C_coeff = (num_experts * coeff) / topk / total_num_tokens / total_num_tokens;\n  NVTE_CHECK_CUDA(cudaMemsetAsync(Coeff_buf + 1, 0, sizeof(float), stream));\n  fused_moe_aux_loss_forward_kernel<DataType, IndexType>\n      <<<grid_size, block_size, smem_size, stream>>>(probs, tokens_per_expert, total_num_tokens,\n                                                     num_rows, num_cols, topk, C_coeff, Coeff_buf);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  // Convert the float accumulator to the output DataType.\n  convert_accum_to_output<DataType><<<1, 1, 0, stream>>>(Coeff_buf, aux_loss);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_moe_aux_loss_forward(const Tensor& probs, const Tensor& tokens_per_expert,\n                                int total_num_tokens, int num_experts, int num_rows, int num_cols,\n                                int topk, float coeff, Tensor& aux_loss, Tensor& Coeff_buf,\n                                cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      probs.data.dtype, DataType,\n      TE_ROUTER_INDEX_TYPE_SWITCH_ALL(\n          tokens_per_expert.data.dtype, IndexType,\n          fused_moe_aux_loss_forward_kernel_launcher<DataType, IndexType>(\n              reinterpret_cast<DataType*>(probs.data.dptr),\n              reinterpret_cast<IndexType*>(tokens_per_expert.data.dptr), total_num_tokens,\n              num_experts, num_rows, num_cols, topk, coeff,\n              reinterpret_cast<DataType*>(aux_loss.data.dptr),\n              reinterpret_cast<float*>(Coeff_buf.data.dptr), stream);););\n}\n\ntemplate <typename DataType, typename IndexType>\n__global__ void fused_moe_aux_loss_backward_kernel(const float* Const_buf,\n                                                   const IndexType* tokens_per_expert, int num_rows,\n                                                   int num_cols, DataType* grad_aux_loss,\n                                                   DataType* grad_probs) {\n  int global_warp_num = gridDim.x * blockDim.x / kThreadsPerWarp;\n  int global_warp_id = (blockIdx.x * blockDim.x + threadIdx.x) / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n\n  // Loop: for all positions in each row\n  for (int i = lane_id; i < num_cols; i += kThreadsPerWarp) {\n    float C_coeff = Const_buf[0];\n    CompType tokens_per_expert_i = static_cast<CompType>(tokens_per_expert[i]);\n    CompType grad_aux_loss_value = static_cast<CompType>(grad_aux_loss[0]);\n    // Loop: for all rows\n    for (int j = global_warp_id; j < num_rows; j += global_warp_num) {\n      grad_probs[j * num_cols + i] = C_coeff * tokens_per_expert_i * grad_aux_loss_value;\n    }\n  }\n}\n\ntemplate <typename DataType, typename IndexType>\nvoid fused_moe_aux_loss_backward_kernel_launcher(const float* Const_buf,\n                                                 const IndexType* tokens_per_expert, int num_rows,\n                                                 int num_cols, DataType* grad_aux_loss,\n                                                 DataType* grad_probs, cudaStream_t stream) {\n  // Meta data for the kernel\n  int block_size = 256;\n  int grid_size = (num_rows + block_size - 1) / block_size;\n  fused_moe_aux_loss_backward_kernel<DataType, IndexType><<<grid_size, block_size, 0, stream>>>(\n      Const_buf, tokens_per_expert, num_rows, num_cols, grad_aux_loss, grad_probs);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_moe_aux_loss_backward(const Tensor& Const_buf, const Tensor& tokens_per_expert,\n                                 int num_rows, int num_cols, Tensor& grad_aux_loss,\n                                 Tensor& grad_probs, cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_aux_loss.data.dtype, DataType,\n      TE_ROUTER_INDEX_TYPE_SWITCH_ALL(\n          tokens_per_expert.data.dtype, IndexType,\n          fused_moe_aux_loss_backward_kernel_launcher<DataType, IndexType>(\n              reinterpret_cast<float*>(Const_buf.data.dptr),\n              reinterpret_cast<IndexType*>(tokens_per_expert.data.dptr), num_rows, num_cols,\n              reinterpret_cast<DataType*>(grad_aux_loss.data.dptr),\n              reinterpret_cast<DataType*>(grad_probs.data.dptr), stream);););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_moe_aux_loss_forward(const NVTETensor probs, const NVTETensor tokens_per_expert,\n                                     int total_num_tokens, int num_experts, int num_rows,\n                                     int num_cols, int topk, float coeff, NVTETensor aux_loss,\n                                     NVTETensor Coeff_buf, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_moe_aux_loss_forward);\n  using namespace transformer_engine;\n  fused_router::fused_moe_aux_loss_forward(\n      *convertNVTETensorCheck(probs), *convertNVTETensorCheck(tokens_per_expert), total_num_tokens,\n      num_experts, num_rows, num_cols, topk, coeff, *convertNVTETensorCheck(aux_loss),\n      *convertNVTETensorCheck(Coeff_buf), stream);\n}\n\nvoid nvte_fused_moe_aux_loss_backward(const NVTETensor Const_buf,\n                                      const NVTETensor tokens_per_expert, int num_rows,\n                                      int num_cols, NVTETensor grad_aux_loss, NVTETensor grad_probs,\n                                      cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_moe_aux_loss_backward);\n  using namespace transformer_engine;\n  fused_router::fused_moe_aux_loss_backward(*convertNVTETensorCheck(Const_buf),\n                                            *convertNVTETensorCheck(tokens_per_expert), num_rows,\n                                            num_cols, *convertNVTETensorCheck(grad_aux_loss),\n                                            *convertNVTETensorCheck(grad_probs), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, typename IndexType>\n__global__ void fused_moe_aux_loss_forward_kernel(const DataType* probs,\n                                                  const IndexType* tokens_per_expert,\n                                                  int total_num_tokens, int num_rows, int num_cols,\n                                                  int topk, float coeff, float* Coeff_buf) {\n  // -----------------------------------------------------------------------\n  // 1) Write the CPU-computed coefficient into a device buffer to re-use in BWD\n  // -----------------------------------------------------------------------\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    Coeff_buf[0] = coeff;\n  }\n\n  // -----------------------------------------------------------------------\n  // 2) Each CTA computes a partial dot-product:\n  //    Sigma_col ( Sigma_row probs[row, col] ) * tokens_per_expert[col]\n  // -----------------------------------------------------------------------\n  CompType thread_sum = CompType(0);\n\n  // Grid-stride over rows so that every row is processed exactly once.\n  // Each thread processes a subset of columns.\n  for (int col = threadIdx.x; col < num_cols; col += blockDim.x) {\n    CompType col_sum = CompType(0);\n\n    // Accumulate probs over the rows assigned to this CTA (grid-stride).\n    for (int row = blockIdx.x; row < num_rows; row += gridDim.x) {\n      col_sum += CompType(probs[row * num_cols + col]);\n    }\n\n    // Multiply by the token count for this expert.\n    col_sum *= CompType(tokens_per_expert[col]);\n\n    // Accumulate the per-column contribution into the thread-local sum.\n    thread_sum += col_sum;\n  }\n\n  // -----------------------------------------------------------------------\n  // 3) Block-level reduction of thread_sum using warp_reduce_on_shmem\n  // -----------------------------------------------------------------------\n  extern __shared__ float shmem[];\n  CompType* shmem_block = reinterpret_cast<CompType*>(shmem);\n  shmem_block[threadIdx.x] = thread_sum;\n  __syncthreads();\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int lane_id = threadIdx.x % kThreadsPerWarp;\n  if (warp_id == 0) {\n    CompType block_sum = warp_reduce_on_shmem(shmem_block, static_cast<int>(blockDim.x),\n                                              ReduceFuncType::SUM, lane_id);\n    if (lane_id == 0) {\n      atomicAdd(&Coeff_buf[1], static_cast<float>(block_sum * coeff));\n    }\n  }\n}\n\n// Small kernel to convert the float accumulator to the output DataType.\ntemplate <typename DataType>\n__global__ void convert_accum_to_output(const float* Coeff_buf, DataType* aux_loss) {\n  aux_loss[0] = static_cast<DataType>(Coeff_buf[1]);\n}\n\n/* -------------------------------------------------------------------------\n *  Kernel launcher -- simplified (no cluster launch).\n * ------------------------------------------------------------------------- */\ntemplate <typename DataType, typename IndexType>\nvoid fused_moe_aux_loss_forward_kernel_launcher(const DataType* probs,\n                                                const IndexType* tokens_per_expert,\n                                                int total_num_tokens, int num_experts, int num_rows,\n                                                int num_cols, int topk, float coeff,\n                                                DataType* aux_loss, float* Coeff_buf,\n                                                hipStream_t stream) {\n  NVTE_CHECK(num_experts == num_cols, \"Number of experts (\", num_experts,\n             \") must be equal to number of input columns (\", num_cols, \").\");\n\n  // Round up to a multiple of warp size for correct warp shuffles.\n  const int block_size = ((std::min(1024, num_cols) + static_cast<int>(kThreadsPerWarp) - 1) /\n                          static_cast<int>(kThreadsPerWarp)) *\n                         static_cast<int>(kThreadsPerWarp);\n  const int grid_size = cuda::sm_count() * 2;\n\n  // One CompType per thread in shared memory.\n  const size_t smem_size = block_size * sizeof(CompType);\n  check_shared_memory_capacity_num_experts(smem_size, num_experts);\n\n  // Compute final coefficient and zero the float accumulator (Coeff_buf[1]) before launch.\n  const float C_coeff = (num_experts * coeff) / topk / total_num_tokens / total_num_tokens;\n  NVTE_CHECK_CUDA(hipMemsetAsync(Coeff_buf + 1, 0, sizeof(float), stream));\n  fused_moe_aux_loss_forward_kernel<DataType, IndexType>\n      <<<grid_size, block_size, smem_size, stream>>>(probs, tokens_per_expert, total_num_tokens,\n                                                     num_rows, num_cols, topk, C_coeff, Coeff_buf);\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  // Convert the float accumulator to the output DataType.\n  convert_accum_to_output<DataType><<<1, 1, 0, stream>>>(Coeff_buf, aux_loss);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_moe_aux_loss_forward(const Tensor& probs, const Tensor& tokens_per_expert,\n                                int total_num_tokens, int num_experts, int num_rows, int num_cols,\n                                int topk, float coeff, Tensor& aux_loss, Tensor& Coeff_buf,\n                                hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      probs.data.dtype, DataType,\n      TE_ROUTER_INDEX_TYPE_SWITCH_ALL(\n          tokens_per_expert.data.dtype, IndexType,\n          fused_moe_aux_loss_forward_kernel_launcher<DataType, IndexType>(\n              reinterpret_cast<DataType*>(probs.data.dptr),\n              reinterpret_cast<IndexType*>(tokens_per_expert.data.dptr), total_num_tokens,\n              num_experts, num_rows, num_cols, topk, coeff,\n              reinterpret_cast<DataType*>(aux_loss.data.dptr),\n              reinterpret_cast<float*>(Coeff_buf.data.dptr), stream);););\n}\n\ntemplate <typename DataType, typename IndexType>\n__global__ void fused_moe_aux_loss_backward_kernel(const float* Const_buf,\n                                                   const IndexType* tokens_per_expert, int num_rows,\n                                                   int num_cols, DataType* grad_aux_loss,\n                                                   DataType* grad_probs) {\n  int global_warp_num = gridDim.x * blockDim.x / kThreadsPerWarp;\n  int global_warp_id = (blockIdx.x * blockDim.x + threadIdx.x) / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n\n  // Loop: for all positions in each row\n  for (int i = lane_id; i < num_cols; i += kThreadsPerWarp) {\n    float C_coeff = Const_buf[0];\n    CompType tokens_per_expert_i = static_cast<CompType>(tokens_per_expert[i]);\n    CompType grad_aux_loss_value = static_cast<CompType>(grad_aux_loss[0]);\n    // Loop: for all rows\n    for (int j = global_warp_id; j < num_rows; j += global_warp_num) {\n      grad_probs[j * num_cols + i] = C_coeff * tokens_per_expert_i * grad_aux_loss_value;\n    }\n  }\n}\n\ntemplate <typename DataType, typename IndexType>\nvoid fused_moe_aux_loss_backward_kernel_launcher(const float* Const_buf,\n                                                 const IndexType* tokens_per_expert, int num_rows,\n                                                 int num_cols, DataType* grad_aux_loss,\n                                                 DataType* grad_probs, hipStream_t stream) {\n  // Meta data for the kernel\n  int block_size = 256;\n  int grid_size = (num_rows + block_size - 1) / block_size;\n  fused_moe_aux_loss_backward_kernel<DataType, IndexType><<<grid_size, block_size, 0, stream>>>(\n      Const_buf, tokens_per_expert, num_rows, num_cols, grad_aux_loss, grad_probs);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_moe_aux_loss_backward(const Tensor& Const_buf, const Tensor& tokens_per_expert,\n                                 int num_rows, int num_cols, Tensor& grad_aux_loss,\n                                 Tensor& grad_probs, hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_aux_loss.data.dtype, DataType,\n      TE_ROUTER_INDEX_TYPE_SWITCH_ALL(\n          tokens_per_expert.data.dtype, IndexType,\n          fused_moe_aux_loss_backward_kernel_launcher<DataType, IndexType>(\n              reinterpret_cast<float*>(Const_buf.data.dptr),\n              reinterpret_cast<IndexType*>(tokens_per_expert.data.dptr), num_rows, num_cols,\n              reinterpret_cast<DataType*>(grad_aux_loss.data.dptr),\n              reinterpret_cast<DataType*>(grad_probs.data.dptr), stream);););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_moe_aux_loss_forward(const NVTETensor probs, const NVTETensor tokens_per_expert,\n                                     int total_num_tokens, int num_experts, int num_rows,\n                                     int num_cols, int topk, float coeff, NVTETensor aux_loss,\n                                     NVTETensor Coeff_buf, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_moe_aux_loss_forward);\n  using namespace transformer_engine;\n  fused_router::fused_moe_aux_loss_forward(\n      *convertNVTETensorCheck(probs), *convertNVTETensorCheck(tokens_per_expert), total_num_tokens,\n      num_experts, num_rows, num_cols, topk, coeff, *convertNVTETensorCheck(aux_loss),\n      *convertNVTETensorCheck(Coeff_buf), stream);\n}\n\nvoid nvte_fused_moe_aux_loss_backward(const NVTETensor Const_buf,\n                                      const NVTETensor tokens_per_expert, int num_rows,\n                                      int num_cols, NVTETensor grad_aux_loss, NVTETensor grad_probs,\n                                      hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_moe_aux_loss_backward);\n  using namespace transformer_engine;\n  fused_router::fused_moe_aux_loss_backward(*convertNVTETensorCheck(Const_buf),\n                                            *convertNVTETensorCheck(tokens_per_expert), num_rows,\n                                            num_cols, *convertNVTETensorCheck(grad_aux_loss),\n                                            *convertNVTETensorCheck(grad_probs), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_router/fused_moe_aux_loss.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, typename BiasType, TopkFuncType TopkFunc = TopkFuncType::Naive>\n__global__ void fused_topk_with_score_function_forward_kernel(\n    const DataType *logits, int num_tokens, int num_experts, int topk, bool use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const BiasType *expert_bias, DataType *probs, bool *routing_map,\n    CompType *intermediate_output) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *scores_buf = reinterpret_cast<CompType *>(shmem);\n  CompType *topk_scores_buf = scores_buf + num_experts * num_token_per_block;\n  CompType *group_scores_buf = nullptr, *masked_scores_buf = nullptr;\n  int *topk_indices_buf = nullptr;\n  if (group_topk > 0) {\n    masked_scores_buf = topk_scores_buf + topk * num_token_per_block;\n    group_scores_buf = masked_scores_buf + num_experts * num_token_per_block;\n    topk_indices_buf = reinterpret_cast<int *>(group_scores_buf + num_groups * num_token_per_block);\n  } else {\n    topk_indices_buf = reinterpret_cast<int *>(topk_scores_buf + topk * num_token_per_block);\n  }\n  // The address of buffers on the current warp\n  CompType *scores = scores_buf + warp_id * num_experts;\n  CompType *topk_scores = topk_scores_buf + warp_id * topk;\n  CompType *masked_scores = masked_scores_buf + warp_id * num_experts;\n  CompType *group_scores = group_scores_buf + warp_id * num_groups;\n  int *topk_indices = topk_indices_buf + warp_id * topk;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the logits to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Clear the probs/routing_map (num_experts)\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      probs[pos_offset + i] = 0.0;\n      routing_map[pos_offset + i] = false;\n      if (score_function == 1) {\n        intermediate_output[pos_offset + i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    // Load the logits to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      scores[i] = logits[pos_offset + i];\n    }\n    // If group_topk > 0, init the masked_scores to -inf\n    if (group_topk > 0) {\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        masked_scores[i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Preprocess\n         * Possible preprocess the scores before the topk operation\n         * - Pre-softmax\n         * - Sigmoid\n         * - Sqrtsoftplus\n         * - Expert bias\n         * This is in-place scores update\n         */\n    if (use_pre_softmax && score_function == 1) {  // score_function == 1 means softmax\n      // Apply softmax to the logits before the topk\n      apply_softmax_on_float(scores, num_experts, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];\n      }\n    } else if (score_function == 0) {  // score_function == 0 means sigmoid\n      // Apply sigmoid to the logits\n      apply_sigmoid_on_float(scores, num_experts, lane_id);\n      __syncwarp();\n      // Save the sigmoid output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];\n      }\n    } else if (score_function == 2) {  // score_function == 2 means sqrtsoftplus\n      // First save the original logits for backward (needed for sqrtsoftplus gradient computation)\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];  // Save original logits\n      }\n      __syncwarp();\n      // Apply sqrtsoftplus to the logits\n      apply_sqrtsoftplus_on_float(scores, num_experts, lane_id);\n    }\n\n    __syncwarp();  //Confirm the scores is written to the output\n\n    // Expert bias is only used at the sigmoid/sqrtsoftplus case\n    if (expert_bias && (score_function == 0 || score_function == 2)) {\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        scores[i] += static_cast<CompType>(expert_bias[i]);\n      }\n      __syncwarp();\n    }\n\n    /***\n         * Section: Topk\n         * Get the topk indices\n         * - group_topk\n         * - naive topk\n         * - topk with expert bias\n         */\n    // Topk on the scores\n    // The bias being not empty happens at the sigmoid/sqrtsoftplus case\n    if (group_topk > 0) {\n      int group_size = num_experts / num_groups;\n      // Top2\n      for (int i = 0; i < num_groups; i++) {\n        topk_and_mask<TopkFunc>(\n            /*scores ptr = */ scores + i * group_size,\n            /*data size = */ group_size,\n            /*topk = */ topk / group_topk,\n            /*topk indices ptr = */ topk_indices,\n            /*topk scores ptr = */ topk_scores,\n            /*lane id = */ lane_id);\n        __syncwarp();\n        // Compute the group score\n        if (lane_id == 0) {\n          CompType tmp = 0.0;\n          for (int j = 0; j < topk / group_topk; j++) {\n            tmp = tmp + topk_scores[j];\n          }\n          group_scores[i] = tmp;\n        }\n        __syncwarp();\n      }\n\n      // select the topk groups\n      topk_and_mask<TopkFunc>(\n          /*scores ptr = */ group_scores,\n          /*data size = */ num_groups,\n          /*topk = */ group_topk,\n          /*topk indices ptr = */ topk_indices,\n          /*topk scores ptr = */ topk_scores,\n          /*lane id = */ lane_id);\n      __syncwarp();\n      // Copy the unmasked scores to the buffer\n      for (int i = 0; i < group_topk; i++) {\n        int st = topk_indices[i] * group_size;\n        int ed = st + group_size;\n        for (int j = st + lane_id; j < ed; j += kThreadsPerWarp) {\n          masked_scores[j] = scores[j];\n        }\n      }\n      __syncwarp();\n      topk_and_mask<TopkFunc>(masked_scores, num_experts, topk, topk_indices, topk_scores, lane_id);\n\n    } else {\n      topk_and_mask<TopkFunc>(scores, num_experts, topk, topk_indices, topk_scores, lane_id);\n    }\n    __syncwarp();\n\n    /***\n         * Section: Postprocess\n         * Possible postprocess the scores after the topk operation\n         * - Revert Expert bias\n         * - Softmax\n         * - Sigmoid/Sqrtsoftplus post-processing when topk > 1\n         * - Write the result with scaling_factor\n         */\n    // Revert Expert bias from the topk scores\n    if (expert_bias && (score_function == 0 || score_function == 2)) {\n      for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n        topk_scores[i] = topk_scores[i] - static_cast<CompType>(expert_bias[topk_indices[i]]);\n      }\n      __syncwarp();\n    }\n\n    // score_function == 1 means softmax\n    if (!use_pre_softmax && score_function == 1) {\n      // Apply softmax to the topk logits\n      apply_softmax_on_float(topk_scores, topk, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + topk_indices[i]] = topk_scores[i];\n      }\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus post-processing when topk > 1\n    if (score_function == 0 || score_function == 2) {\n      if (topk > 1) {\n        CompType sum_scores = warp_reduce_on_shmem(topk_scores, topk, ReduceFuncType::SUM, lane_id);\n        for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n          topk_scores[i] = topk_scores[i] / (sum_scores + epsilon);\n        }\n      }\n      __syncwarp();\n    }\n\n    // Write the probs/routing_map to the output tensor\n    for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n      routing_map[pos_offset + topk_indices[i]] = true;\n      probs[pos_offset + topk_indices[i]] = scaling_factor * topk_scores[i];\n    }\n    __threadfence_block();\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType, typename BiasType>\nvoid fused_topk_with_score_function_forward_kernel_launcher(\n    const DataType *logits, int num_tokens, int num_experts, int topk, bool use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const BiasType *expert_bias, DataType *probs, bool *routing_map, CompType *intermediate_output,\n    cudaStream_t stream) {\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // scores\n                              + topk * num_token_per_block * sizeof(CompType)       // topk_scores\n                              + topk * num_token_per_block * sizeof(int);           // topk_indices\n  if (group_topk > 0) {\n    shared_memory_size += num_groups * num_token_per_block * sizeof(CompType);   // group_scores\n    shared_memory_size += num_experts * num_token_per_block * sizeof(CompType);  // maksed_scores\n  }\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  // Radix selection is O(E), independent of K, but it needs 4 passes for 32-bit float;\n  // switch at K=16 where naive O(K^2*E) starts to dominate\n  if (topk < 16) {\n    NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n        fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Naive>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Naive>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, use_pre_softmax, num_groups, group_topk,\n            scaling_factor, score_function, expert_bias, probs, routing_map, intermediate_output);\n  } else {\n    NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n        fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Radix>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Radix>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, use_pre_softmax, num_groups, group_topk,\n            scaling_factor, score_function, expert_bias, probs, routing_map, intermediate_output);\n  }\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_topk_with_score_function_forward(const Tensor logits, int num_tokens, int num_experts,\n                                            int topk, bool use_pre_softmax, int num_groups,\n                                            int group_topk, float scaling_factor,\n                                            int score_function, const Tensor expert_bias,\n                                            Tensor probs, Tensor routing_map,\n                                            Tensor intermediate_output, cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      logits.data.dtype, DataType,\n      TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n          expert_bias.data.dtype, BiasType,\n          fused_topk_with_score_function_forward_kernel_launcher<DataType, BiasType>(\n              reinterpret_cast<DataType *>(logits.data.dptr), num_tokens, num_experts, topk,\n              use_pre_softmax, num_groups, group_topk, scaling_factor, score_function,\n              reinterpret_cast<BiasType *>(expert_bias.data.dptr),\n              reinterpret_cast<DataType *>(probs.data.dptr),\n              reinterpret_cast<bool *>(routing_map.data.dptr),\n              reinterpret_cast<CompType *>(intermediate_output.data.dptr), stream);););\n}\n\ntemplate <typename DataType>\n__global__ void fused_topk_with_score_function_backward_kernel(\n    // Inputs tensor\n    const bool *routing_map, const CompType *intermediate_output, const DataType *grad_probs,\n    // Other parameters\n    int num_tokens, int num_experts, int topk, bool use_pre_softmax, float scaling_factor,\n    int score_function,\n    // Output tensor\n    DataType *grad_logits) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *grad_probs_buf = reinterpret_cast<CompType *>(shmem);\n  // To store the output of softmax/sigmoid from fwd, or original logits for sqrtsoftplus\n  CompType *act_from_fwd_buf = grad_probs_buf + num_experts * num_token_per_block;\n  CompType *comp_buf = act_from_fwd_buf + num_experts * num_token_per_block;\n  // To store the routing_map from the fwd\n  bool *routing_map_buf = reinterpret_cast<bool *>(comp_buf + num_experts * num_token_per_block);\n  // The address of buffers on the current warp\n  CompType *local_grad = grad_probs_buf + warp_id * num_experts;\n  CompType *local_act_from_fwd = act_from_fwd_buf + warp_id * num_experts;\n  CompType *local_comp_buf = comp_buf + warp_id * num_experts;\n  bool *local_routing_map = routing_map_buf + warp_id * num_experts;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the dgrad/output_from_fwd to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Load the dgrad/output_from_fwd to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_grad[i] = grad_probs[pos_offset + i];\n      local_act_from_fwd[i] = intermediate_output[pos_offset + i];\n      local_routing_map[i] = routing_map[pos_offset + i];\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops after the topk\n         * - Backward of the used scaling_factor\n         * - Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1\n         * - Softmax bwd if use_pre_softmax is false\n         */\n    // Backward of the used scaling_factor\n    // In-place update\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      if (local_routing_map[i]) {\n        local_grad[i] = local_grad[i] * scaling_factor;\n      }\n    }\n    __syncwarp();\n\n    // Sqrtsoftplus: First compute sqrtsoftplus output from original logits\n    // (needed for both post-processing bwd and activation bwd, compute once here)\n    // For sqrtsoftplus, intermediate_output stores original logits\n    if (score_function == 2) {\n      // Copy original logits to local_comp_buf and apply sqrtsoftplus in-place\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_comp_buf[i] = local_act_from_fwd[i];\n      }\n      __syncwarp();\n      apply_sqrtsoftplus_on_float(local_comp_buf, num_experts, lane_id);\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1 (normalization backward)\n    if (topk > 1 && (score_function == 0 || score_function == 2)) {\n      // Select the correct activation output buffer:\n      // - Sigmoid: local_act_from_fwd already contains sigmoid output\n      // - Sqrtsoftplus: local_comp_buf contains sqrtsoftplus output computed above\n      CompType *act_output = (score_function == 0) ? local_act_from_fwd : local_comp_buf;\n\n      CompType sum_fwd_input = masked_warp_reduce_on_shmem(\n          /*data ptr = */ act_output,\n          /*mask ptr = */ local_routing_map,\n          /*data size = */ num_experts,\n          /*reduce func = */ ReduceFuncType::SUM, lane_id);\n      // Compute sum of output * grad using registers\n      CompType local_sum_Output_x_Grad = 0.0;\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        if (local_routing_map[i]) {\n          local_sum_Output_x_Grad += local_grad[i] * act_output[i];\n        }\n      }\n      // Warp reduce the sum\n      for (int s = 16; s > 0; s /= 2) {\n        local_sum_Output_x_Grad += __shfl_xor_sync(0xffffffff, local_sum_Output_x_Grad, s);\n      }\n      CompType sum_Output_x_Grad = local_sum_Output_x_Grad;\n      // In-place update\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        if (local_routing_map[i]) {\n          local_grad[i] =\n              local_grad[i] / (sum_fwd_input + epsilon) -\n              sum_Output_x_Grad / ((sum_fwd_input + epsilon) * (sum_fwd_input + epsilon));\n        } else {\n          local_grad[i] = 0.0;\n        }\n      }\n      __syncwarp();\n    }\n\n    // Softmax bwd if use_pre_softmax is false\n    if (!use_pre_softmax && score_function == 1) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, local_routing_map,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n\n    /***\n         * Section: Backward of topk\n         * mask the unselected position in the grad\n         */\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      if (!local_routing_map[i]) {\n        local_grad[i] = 0.0;\n      }\n    }\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops before the topk\n         * - Pre-softmax bwd\n         * - Sigmoid bwd\n         * - Sqrtsoftplus bwd\n         * - Write the grad_logits to the global mem\n         */\n    // Pre-softmax bwd\n    if (score_function == 1 && use_pre_softmax) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, nullptr,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sigmoid bwd\n    if (score_function == 0) {\n      apply_sigmoid_bwd_on_float(local_grad, local_act_from_fwd, num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sqrtsoftplus bwd\n    // For sqrtsoftplus, local_comp_buf already contains sqrtsoftplus output computed earlier\n    // Now compute gradient: dy/dx = sigmoid(x) / (2 * y)\n    if (score_function == 2) {\n      apply_sqrtsoftplus_bwd_on_float(local_grad, local_comp_buf, local_act_from_fwd, num_experts,\n                                      lane_id);\n      __syncwarp();\n    }\n    // Write the grad_logits to the global mem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      grad_logits[pos_offset + i] = local_grad[i];\n    }\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_topk_with_score_function_backward_kernel_launcher(\n    const bool *routing_map, const CompType *intermediate_output, const DataType *grad_probs,\n    int num_tokens, int num_experts, int topk, bool use_pre_softmax, float scaling_factor,\n    int score_function, DataType *grad_logits, cudaStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // grad_probs\n                              +\n                              num_experts * num_token_per_block * sizeof(CompType)  // act_from_fwd\n                              + num_experts * num_token_per_block * sizeof(CompType)  // comp_buf\n                              + num_experts * num_token_per_block * sizeof(bool);     // routing_map\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  NVTE_CHECK_CUDA(cudaFuncSetAttribute(fused_topk_with_score_function_backward_kernel<DataType>,\n                                       cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                       shared_memory_size));\n  fused_topk_with_score_function_backward_kernel<DataType>\n      <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n          routing_map, intermediate_output, grad_probs, num_tokens, num_experts, topk,\n          use_pre_softmax, scaling_factor, score_function, grad_logits);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_topk_with_score_function_backward(const Tensor &routing_map,\n                                             const Tensor &intermediate_output,\n                                             const Tensor &grad_probs, int num_tokens,\n                                             int num_experts, int topk, bool use_pre_softmax,\n                                             float scaling_factor, int score_function,\n                                             Tensor &grad_logits, cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_logits.data.dtype, DataType,\n      fused_topk_with_score_function_backward_kernel_launcher<DataType>(\n          reinterpret_cast<bool *>(routing_map.data.dptr),\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr),\n          reinterpret_cast<DataType *>(grad_probs.data.dptr), num_tokens, num_experts, topk,\n          use_pre_softmax, scaling_factor, score_function,\n          reinterpret_cast<DataType *>(grad_logits.data.dptr), stream););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_topk_with_score_function_forward(\n    const NVTETensor logits, int num_tokens, int num_experts, int topk, int use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const NVTETensor expert_bias, NVTETensor probs, NVTETensor routing_map,\n    NVTETensor intermediate_output, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_topk_with_score_function_forward);\n  using namespace transformer_engine;\n  fused_router::fused_topk_with_score_function_forward(\n      *convertNVTETensorCheck(logits), num_tokens, num_experts, topk,\n      static_cast<bool>(use_pre_softmax), num_groups, group_topk, scaling_factor, score_function,\n      *convertNVTETensorCheck(expert_bias), *convertNVTETensorCheck(probs),\n      *convertNVTETensorCheck(routing_map), *convertNVTETensorCheck(intermediate_output), stream);\n}\n\nvoid nvte_fused_topk_with_score_function_backward(const NVTETensor routing_map,\n                                                  const NVTETensor intermediate_output,\n                                                  const NVTETensor grad_probs, int num_tokens,\n                                                  int num_experts, int topk, int use_pre_softmax,\n                                                  float scaling_factor, int score_function,\n                                                  NVTETensor grad_logits, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_topk_with_score_function_backward);\n  using namespace transformer_engine;\n  fused_router::fused_topk_with_score_function_backward(\n      *convertNVTETensorCheck(routing_map), *convertNVTETensorCheck(intermediate_output),\n      *convertNVTETensorCheck(grad_probs), num_tokens, num_experts, topk,\n      static_cast<bool>(use_pre_softmax), scaling_factor, score_function,\n      *convertNVTETensorCheck(grad_logits), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, typename BiasType, TopkFuncType TopkFunc = TopkFuncType::Naive>\n__global__ void fused_topk_with_score_function_forward_kernel(\n    const DataType *logits, int num_tokens, int num_experts, int topk, bool use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const BiasType *expert_bias, DataType *probs, bool *routing_map,\n    CompType *intermediate_output) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *scores_buf = reinterpret_cast<CompType *>(shmem);\n  CompType *topk_scores_buf = scores_buf + num_experts * num_token_per_block;\n  CompType *group_scores_buf = nullptr, *masked_scores_buf = nullptr;\n  int *topk_indices_buf = nullptr;\n  if (group_topk > 0) {\n    masked_scores_buf = topk_scores_buf + topk * num_token_per_block;\n    group_scores_buf = masked_scores_buf + num_experts * num_token_per_block;\n    topk_indices_buf = reinterpret_cast<int *>(group_scores_buf + num_groups * num_token_per_block);\n  } else {\n    topk_indices_buf = reinterpret_cast<int *>(topk_scores_buf + topk * num_token_per_block);\n  }\n  // The address of buffers on the current warp\n  CompType *scores = scores_buf + warp_id * num_experts;\n  CompType *topk_scores = topk_scores_buf + warp_id * topk;\n  CompType *masked_scores = masked_scores_buf + warp_id * num_experts;\n  CompType *group_scores = group_scores_buf + warp_id * num_groups;\n  int *topk_indices = topk_indices_buf + warp_id * topk;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the logits to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Clear the probs/routing_map (num_experts)\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      probs[pos_offset + i] = 0.0;\n      routing_map[pos_offset + i] = false;\n      if (score_function == 1) {\n        intermediate_output[pos_offset + i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    // Load the logits to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      scores[i] = logits[pos_offset + i];\n    }\n    // If group_topk > 0, init the masked_scores to -inf\n    if (group_topk > 0) {\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        masked_scores[i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Preprocess\n         * Possible preprocess the scores before the topk operation\n         * - Pre-softmax\n         * - Sigmoid\n         * - Sqrtsoftplus\n         * - Expert bias\n         * This is in-place scores update\n         */\n    if (use_pre_softmax && score_function == 1) {  // score_function == 1 means softmax\n      // Apply softmax to the logits before the topk\n      apply_softmax_on_float(scores, num_experts, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];\n      }\n    } else if (score_function == 0) {  // score_function == 0 means sigmoid\n      // Apply sigmoid to the logits\n      apply_sigmoid_on_float(scores, num_experts, lane_id);\n      __syncwarp();\n      // Save the sigmoid output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];\n      }\n    } else if (score_function == 2) {  // score_function == 2 means sqrtsoftplus\n      // First save the original logits for backward (needed for sqrtsoftplus gradient computation)\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = scores[i];  // Save original logits\n      }\n      __syncwarp();\n      // Apply sqrtsoftplus to the logits\n      apply_sqrtsoftplus_on_float(scores, num_experts, lane_id);\n    }\n\n    __syncwarp();  //Confirm the scores is written to the output\n\n    // Expert bias is only used at the sigmoid/sqrtsoftplus case\n    if (expert_bias && (score_function == 0 || score_function == 2)) {\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        scores[i] += static_cast<CompType>(expert_bias[i]);\n      }\n      __syncwarp();\n    }\n\n    /***\n         * Section: Topk\n         * Get the topk indices\n         * - group_topk\n         * - naive topk\n         * - topk with expert bias\n         */\n    // Topk on the scores\n    // The bias being not empty happens at the sigmoid/sqrtsoftplus case\n    if (group_topk > 0) {\n      int group_size = num_experts / num_groups;\n      // Top2\n      for (int i = 0; i < num_groups; i++) {\n        topk_and_mask<TopkFunc>(\n            /*scores ptr = */ scores + i * group_size,\n            /*data size = */ group_size,\n            /*topk = */ topk / group_topk,\n            /*topk indices ptr = */ topk_indices,\n            /*topk scores ptr = */ topk_scores,\n            /*lane id = */ lane_id);\n        __syncwarp();\n        // Compute the group score\n        if (lane_id == 0) {\n          CompType tmp = 0.0;\n          for (int j = 0; j < topk / group_topk; j++) {\n            tmp = tmp + topk_scores[j];\n          }\n          group_scores[i] = tmp;\n        }\n        __syncwarp();\n      }\n\n      // select the topk groups\n      topk_and_mask<TopkFunc>(\n          /*scores ptr = */ group_scores,\n          /*data size = */ num_groups,\n          /*topk = */ group_topk,\n          /*topk indices ptr = */ topk_indices,\n          /*topk scores ptr = */ topk_scores,\n          /*lane id = */ lane_id);\n      __syncwarp();\n      // Copy the unmasked scores to the buffer\n      for (int i = 0; i < group_topk; i++) {\n        int st = topk_indices[i] * group_size;\n        int ed = st + group_size;\n        for (int j = st + lane_id; j < ed; j += kThreadsPerWarp) {\n          masked_scores[j] = scores[j];\n        }\n      }\n      __syncwarp();\n      topk_and_mask<TopkFunc>(masked_scores, num_experts, topk, topk_indices, topk_scores, lane_id);\n\n    } else {\n      topk_and_mask<TopkFunc>(scores, num_experts, topk, topk_indices, topk_scores, lane_id);\n    }\n    __syncwarp();\n\n    /***\n         * Section: Postprocess\n         * Possible postprocess the scores after the topk operation\n         * - Revert Expert bias\n         * - Softmax\n         * - Sigmoid/Sqrtsoftplus post-processing when topk > 1\n         * - Write the result with scaling_factor\n         */\n    // Revert Expert bias from the topk scores\n    if (expert_bias && (score_function == 0 || score_function == 2)) {\n      for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n        topk_scores[i] = topk_scores[i] - static_cast<CompType>(expert_bias[topk_indices[i]]);\n      }\n      __syncwarp();\n    }\n\n    // score_function == 1 means softmax\n    if (!use_pre_softmax && score_function == 1) {\n      // Apply softmax to the topk logits\n      apply_softmax_on_float(topk_scores, topk, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + topk_indices[i]] = topk_scores[i];\n      }\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus post-processing when topk > 1\n    if (score_function == 0 || score_function == 2) {\n      if (topk > 1) {\n        CompType sum_scores = warp_reduce_on_shmem(topk_scores, topk, ReduceFuncType::SUM, lane_id);\n        for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n          topk_scores[i] = topk_scores[i] / (sum_scores + epsilon);\n        }\n      }\n      __syncwarp();\n    }\n\n    // Write the probs/routing_map to the output tensor\n    for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n      routing_map[pos_offset + topk_indices[i]] = true;\n      probs[pos_offset + topk_indices[i]] = scaling_factor * topk_scores[i];\n    }\n    __threadfence_block();\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType, typename BiasType>\nvoid fused_topk_with_score_function_forward_kernel_launcher(\n    const DataType *logits, int num_tokens, int num_experts, int topk, bool use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const BiasType *expert_bias, DataType *probs, bool *routing_map, CompType *intermediate_output,\n    hipStream_t stream) {\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // scores\n                              + topk * num_token_per_block * sizeof(CompType)       // topk_scores\n                              + topk * num_token_per_block * sizeof(int);           // topk_indices\n  if (group_topk > 0) {\n    shared_memory_size += num_groups * num_token_per_block * sizeof(CompType);   // group_scores\n    shared_memory_size += num_experts * num_token_per_block * sizeof(CompType);  // maksed_scores\n  }\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  // Radix selection is O(E), independent of K, but it needs 4 passes for 32-bit float;\n  // switch at K=16 where naive O(K^2*E) starts to dominate\n  if (topk < 16) {\n    NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Naive>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Naive>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, use_pre_softmax, num_groups, group_topk,\n            scaling_factor, score_function, expert_bias, probs, routing_map, intermediate_output);\n  } else {\n    NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Radix>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_topk_with_score_function_forward_kernel<DataType, BiasType, TopkFuncType::Radix>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, use_pre_softmax, num_groups, group_topk,\n            scaling_factor, score_function, expert_bias, probs, routing_map, intermediate_output);\n  }\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_topk_with_score_function_forward(const Tensor logits, int num_tokens, int num_experts,\n                                            int topk, bool use_pre_softmax, int num_groups,\n                                            int group_topk, float scaling_factor,\n                                            int score_function, const Tensor expert_bias,\n                                            Tensor probs, Tensor routing_map,\n                                            Tensor intermediate_output, hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      logits.data.dtype, DataType,\n      TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n          expert_bias.data.dtype, BiasType,\n          fused_topk_with_score_function_forward_kernel_launcher<DataType, BiasType>(\n              reinterpret_cast<DataType *>(logits.data.dptr), num_tokens, num_experts, topk,\n              use_pre_softmax, num_groups, group_topk, scaling_factor, score_function,\n              reinterpret_cast<BiasType *>(expert_bias.data.dptr),\n              reinterpret_cast<DataType *>(probs.data.dptr),\n              reinterpret_cast<bool *>(routing_map.data.dptr),\n              reinterpret_cast<CompType *>(intermediate_output.data.dptr), stream);););\n}\n\ntemplate <typename DataType>\n__global__ void fused_topk_with_score_function_backward_kernel(\n    // Inputs tensor\n    const bool *routing_map, const CompType *intermediate_output, const DataType *grad_probs,\n    // Other parameters\n    int num_tokens, int num_experts, int topk, bool use_pre_softmax, float scaling_factor,\n    int score_function,\n    // Output tensor\n    DataType *grad_logits) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *grad_probs_buf = reinterpret_cast<CompType *>(shmem);\n  // To store the output of softmax/sigmoid from fwd, or original logits for sqrtsoftplus\n  CompType *act_from_fwd_buf = grad_probs_buf + num_experts * num_token_per_block;\n  CompType *comp_buf = act_from_fwd_buf + num_experts * num_token_per_block;\n  // To store the routing_map from the fwd\n  bool *routing_map_buf = reinterpret_cast<bool *>(comp_buf + num_experts * num_token_per_block);\n  // The address of buffers on the current warp\n  CompType *local_grad = grad_probs_buf + warp_id * num_experts;\n  CompType *local_act_from_fwd = act_from_fwd_buf + warp_id * num_experts;\n  CompType *local_comp_buf = comp_buf + warp_id * num_experts;\n  bool *local_routing_map = routing_map_buf + warp_id * num_experts;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the dgrad/output_from_fwd to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Load the dgrad/output_from_fwd to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_grad[i] = grad_probs[pos_offset + i];\n      local_act_from_fwd[i] = intermediate_output[pos_offset + i];\n      local_routing_map[i] = routing_map[pos_offset + i];\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops after the topk\n         * - Backward of the used scaling_factor\n         * - Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1\n         * - Softmax bwd if use_pre_softmax is false\n         */\n    // Backward of the used scaling_factor\n    // In-place update\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      if (local_routing_map[i]) {\n        local_grad[i] = local_grad[i] * scaling_factor;\n      }\n    }\n    __syncwarp();\n\n    // Sqrtsoftplus: First compute sqrtsoftplus output from original logits\n    // (needed for both post-processing bwd and activation bwd, compute once here)\n    // For sqrtsoftplus, intermediate_output stores original logits\n    if (score_function == 2) {\n      // Copy original logits to local_comp_buf and apply sqrtsoftplus in-place\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_comp_buf[i] = local_act_from_fwd[i];\n      }\n      __syncwarp();\n      apply_sqrtsoftplus_on_float(local_comp_buf, num_experts, lane_id);\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1 (normalization backward)\n    if (topk > 1 && (score_function == 0 || score_function == 2)) {\n      // Select the correct activation output buffer:\n      // - Sigmoid: local_act_from_fwd already contains sigmoid output\n      // - Sqrtsoftplus: local_comp_buf contains sqrtsoftplus output computed above\n      CompType *act_output = (score_function == 0) ? local_act_from_fwd : local_comp_buf;\n\n      CompType sum_fwd_input = masked_warp_reduce_on_shmem(\n          /*data ptr = */ act_output,\n          /*mask ptr = */ local_routing_map,\n          /*data size = */ num_experts,\n          /*reduce func = */ ReduceFuncType::SUM, lane_id);\n      // Compute sum of output * grad using registers\n      CompType local_sum_Output_x_Grad = 0.0;\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        if (local_routing_map[i]) {\n          local_sum_Output_x_Grad += local_grad[i] * act_output[i];\n        }\n      }\n      // Warp reduce the sum\n      for (int s = 16; s > 0; s /= 2) {\n        local_sum_Output_x_Grad += __shfl_xor_sync(0xffffffff, local_sum_Output_x_Grad, s);\n      }\n      CompType sum_Output_x_Grad = local_sum_Output_x_Grad;\n      // In-place update\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        if (local_routing_map[i]) {\n          local_grad[i] =\n              local_grad[i] / (sum_fwd_input + epsilon) -\n              sum_Output_x_Grad / ((sum_fwd_input + epsilon) * (sum_fwd_input + epsilon));\n        } else {\n          local_grad[i] = 0.0;\n        }\n      }\n      __syncwarp();\n    }\n\n    // Softmax bwd if use_pre_softmax is false\n    if (!use_pre_softmax && score_function == 1) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, local_routing_map,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n\n    /***\n         * Section: Backward of topk\n         * mask the unselected position in the grad\n         */\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      if (!local_routing_map[i]) {\n        local_grad[i] = 0.0;\n      }\n    }\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops before the topk\n         * - Pre-softmax bwd\n         * - Sigmoid bwd\n         * - Sqrtsoftplus bwd\n         * - Write the grad_logits to the global mem\n         */\n    // Pre-softmax bwd\n    if (score_function == 1 && use_pre_softmax) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, nullptr,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sigmoid bwd\n    if (score_function == 0) {\n      apply_sigmoid_bwd_on_float(local_grad, local_act_from_fwd, num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sqrtsoftplus bwd\n    // For sqrtsoftplus, local_comp_buf already contains sqrtsoftplus output computed earlier\n    // Now compute gradient: dy/dx = sigmoid(x) / (2 * y)\n    if (score_function == 2) {\n      apply_sqrtsoftplus_bwd_on_float(local_grad, local_comp_buf, local_act_from_fwd, num_experts,\n                                      lane_id);\n      __syncwarp();\n    }\n    // Write the grad_logits to the global mem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      grad_logits[pos_offset + i] = local_grad[i];\n    }\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_topk_with_score_function_backward_kernel_launcher(\n    const bool *routing_map, const CompType *intermediate_output, const DataType *grad_probs,\n    int num_tokens, int num_experts, int topk, bool use_pre_softmax, float scaling_factor,\n    int score_function, DataType *grad_logits, hipStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // grad_probs\n                              +\n                              num_experts * num_token_per_block * sizeof(CompType)  // act_from_fwd\n                              + num_experts * num_token_per_block * sizeof(CompType)  // comp_buf\n                              + num_experts * num_token_per_block * sizeof(bool);     // routing_map\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_topk_with_score_function_backward_kernel<DataType>),\n                                       hipFuncAttributeMaxDynamicSharedMemorySize,\n                                       shared_memory_size));\n  fused_topk_with_score_function_backward_kernel<DataType>\n      <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n          routing_map, intermediate_output, grad_probs, num_tokens, num_experts, topk,\n          use_pre_softmax, scaling_factor, score_function, grad_logits);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_topk_with_score_function_backward(const Tensor &routing_map,\n                                             const Tensor &intermediate_output,\n                                             const Tensor &grad_probs, int num_tokens,\n                                             int num_experts, int topk, bool use_pre_softmax,\n                                             float scaling_factor, int score_function,\n                                             Tensor &grad_logits, hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_logits.data.dtype, DataType,\n      fused_topk_with_score_function_backward_kernel_launcher<DataType>(\n          reinterpret_cast<bool *>(routing_map.data.dptr),\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr),\n          reinterpret_cast<DataType *>(grad_probs.data.dptr), num_tokens, num_experts, topk,\n          use_pre_softmax, scaling_factor, score_function,\n          reinterpret_cast<DataType *>(grad_logits.data.dptr), stream););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_topk_with_score_function_forward(\n    const NVTETensor logits, int num_tokens, int num_experts, int topk, int use_pre_softmax,\n    int num_groups, int group_topk, float scaling_factor, int score_function,\n    const NVTETensor expert_bias, NVTETensor probs, NVTETensor routing_map,\n    NVTETensor intermediate_output, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_topk_with_score_function_forward);\n  using namespace transformer_engine;\n  fused_router::fused_topk_with_score_function_forward(\n      *convertNVTETensorCheck(logits), num_tokens, num_experts, topk,\n      static_cast<bool>(use_pre_softmax), num_groups, group_topk, scaling_factor, score_function,\n      *convertNVTETensorCheck(expert_bias), *convertNVTETensorCheck(probs),\n      *convertNVTETensorCheck(routing_map), *convertNVTETensorCheck(intermediate_output), stream);\n}\n\nvoid nvte_fused_topk_with_score_function_backward(const NVTETensor routing_map,\n                                                  const NVTETensor intermediate_output,\n                                                  const NVTETensor grad_probs, int num_tokens,\n                                                  int num_experts, int topk, int use_pre_softmax,\n                                                  float scaling_factor, int score_function,\n                                                  NVTETensor grad_logits, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_topk_with_score_function_backward);\n  using namespace transformer_engine;\n  fused_router::fused_topk_with_score_function_backward(\n      *convertNVTETensorCheck(routing_map), *convertNVTETensorCheck(intermediate_output),\n      *convertNVTETensorCheck(grad_probs), num_tokens, num_experts, topk,\n      static_cast<bool>(use_pre_softmax), scaling_factor, score_function,\n      *convertNVTETensorCheck(grad_logits), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_router/fused_topk_with_score_function.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, TopkFuncType TopkFunc = TopkFuncType::Naive>\n__global__ void fused_score_for_moe_aux_loss_forward_kernel(const DataType *logits, int num_tokens,\n                                                            int num_experts, int topk,\n                                                            int score_function, float *scores,\n                                                            bool *routing_map,\n                                                            CompType *intermediate_output) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem_scores_for_aux_loss[];\n  CompType *logits_buf = reinterpret_cast<CompType *>(shmem_scores_for_aux_loss);\n  CompType *topk_logits_buf =\n      reinterpret_cast<CompType *>(logits_buf + num_experts * num_token_per_block);\n  int *topk_indices_buf = reinterpret_cast<int *>(topk_logits_buf + topk * num_token_per_block);\n  // The address of buffers on the current warp\n  CompType *local_logits = logits_buf + warp_id * num_experts;\n  CompType *topk_logits = topk_logits_buf + warp_id * topk;\n  int *topk_indices = topk_indices_buf + warp_id * topk;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the logits to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Clear the routing_map (num_experts)\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      routing_map[pos_offset + i] = false;\n      if (score_function == 1) {\n        intermediate_output[pos_offset + i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    // Load the logits to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_logits[i] = static_cast<CompType>(logits[pos_offset + i]);\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Preprocess\n         * Possible preprocess the scores before the topk operation\n         * - Pre-softmax\n         * - Sigmoid\n         * - Sqrtsoftplus\n         * - Sigmoid/Sqrtsoftplus post-processing when topk > 1\n         * This is in-place scores update\n         */\n    if (score_function == 1) {  // score_function == 1 means softmax\n      // Apply softmax to the logits before the topk\n      apply_softmax_on_float(local_logits, num_experts, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];\n      }\n    } else if (score_function == 0) {  // score_function == 0 means sigmoid\n      // Apply sigmoid to the logits\n      apply_sigmoid_on_float(local_logits, num_experts, lane_id);\n      __syncwarp();\n      // Save the sigmoid output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];\n      }\n    } else if (score_function == 2) {  // score_function == 2 means sqrtsoftplus\n      // First save the original logits for backward (needed for gradient computation)\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];  // Save original logits\n      }\n      __syncwarp();\n      // Apply sqrtsoftplus to the logits\n      apply_sqrtsoftplus_on_float(local_logits, num_experts, lane_id);\n    }\n\n    __syncwarp();  //Confirm the scores is written to the output\n\n    // Sigmoid/Sqrtsoftplus post-processing\n    if (score_function == 0 || score_function == 2) {\n      auto sum_logits =\n          warp_reduce_on_shmem(local_logits, num_experts, ReduceFuncType::SUM, lane_id);\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_logits[i] /= (sum_logits + epsilon);\n      }\n      __syncwarp();\n    }\n\n    /***\n         * Section: Topk\n         * Get the topk indices\n         */\n    topk_and_mask<TopkFunc>(local_logits, num_experts, topk, topk_indices, topk_logits, lane_id);\n    __syncwarp();\n\n    // Write the routing_map to the output tensor\n    for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n      routing_map[pos_offset + topk_indices[i]] = true;\n    }\n    // Write the scores to the output tensor\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      scores[pos_offset + i] = local_logits[i];\n    }\n    __threadfence_block();\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_score_for_moe_aux_loss_forward_kernel_launcher(\n    const DataType *logits, int num_tokens, int num_experts, int topk, int score_function,\n    float *scores, bool *routing_map, CompType *intermediate_output, cudaStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // logits\n                              + topk * num_token_per_block * sizeof(CompType)       // topk_logits\n                              + topk * num_token_per_block * sizeof(int);           // topk_indices\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  // Radix selection is O(E), independent of K, but it needs 4 passes for 32-bit float;\n  // switch at K=16 where naive O(K^2*E) starts to dominate\n  if (topk < 16) {\n    NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n        fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Naive>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Naive>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, score_function, scores, routing_map,\n            intermediate_output);\n  } else {\n    NVTE_CHECK_CUDA(cudaFuncSetAttribute(\n        fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Radix>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Radix>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, score_function, scores, routing_map,\n            intermediate_output);\n  }\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_score_for_moe_aux_loss_forward(const Tensor &logits, int num_tokens, int num_experts,\n                                          int topk, int score_function, Tensor &scores,\n                                          Tensor &routing_map, Tensor &intermediate_output,\n                                          cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      logits.data.dtype, DataType,\n      fused_score_for_moe_aux_loss_forward_kernel_launcher<DataType>(\n          reinterpret_cast<DataType *>(logits.data.dptr), num_tokens, num_experts, topk,\n          score_function, reinterpret_cast<float *>(scores.data.dptr),\n          reinterpret_cast<bool *>(routing_map.data.dptr),\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr), stream););\n}\n\ntemplate <typename DataType>\n__global__ void fused_score_for_moe_aux_loss_backward_kernel(const CompType *intermediate_output,\n                                                             const float *grad_scores,\n                                                             int num_tokens, int num_experts,\n                                                             int topk, int score_function,\n                                                             DataType *grad_logits) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *grad_scores_buf = reinterpret_cast<CompType *>(shmem);\n  // To store the output of softmax/sigmoid from fwd, or original logits for sqrtsoftplus\n  CompType *act_from_fwd_buf = grad_scores_buf + num_experts * num_token_per_block;\n  CompType *comp_buf = act_from_fwd_buf + num_experts * num_token_per_block;\n  // The address of buffers on the current warp\n  CompType *local_grad = grad_scores_buf + warp_id * num_experts;\n  CompType *local_act_from_fwd = act_from_fwd_buf + warp_id * num_experts;\n  CompType *local_comp_buf = comp_buf + warp_id * num_experts;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the dgrad/output_from_fwd to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Load the dgrad/output_from_fwd to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_grad[i] = grad_scores[pos_offset + i];\n      local_act_from_fwd[i] = intermediate_output[pos_offset + i];\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops before the topk\n         * - Pre-softmax bwd\n         * - Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1\n         * - Sigmoid bwd\n         * - Sqrtsoftplus bwd\n         * - Write the grad_logits to the global mem\n         */\n    // Sqrtsoftplus: First compute sqrtsoftplus output from original logits\n    // (needed for both post-processing bwd and activation bwd, compute once here)\n    // For sqrtsoftplus, intermediate_output stores original logits\n    if (score_function == 2) {\n      // Copy original logits to local_comp_buf and apply sqrtsoftplus in-place\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_comp_buf[i] = local_act_from_fwd[i];\n      }\n      __syncwarp();\n      apply_sqrtsoftplus_on_float(local_comp_buf, num_experts, lane_id);\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus Post-processing bwd (normalization backward)\n    if (score_function == 0 || score_function == 2) {\n      // Select the correct activation output buffer:\n      // - Sigmoid: local_act_from_fwd already contains sigmoid output\n      // - Sqrtsoftplus: local_comp_buf contains sqrtsoftplus output computed above\n      CompType *act_output = (score_function == 0) ? local_act_from_fwd : local_comp_buf;\n\n      auto sum_fwd_input =\n          warp_reduce_on_shmem(act_output, num_experts, ReduceFuncType::SUM, lane_id);\n      // Compute sum of output * grad using registers\n      CompType local_sum_Output_x_Grad = 0.0;\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_sum_Output_x_Grad += local_grad[i] * act_output[i];\n      }\n      // Warp reduce the sum\n      for (int s = 16; s > 0; s /= 2) {\n        local_sum_Output_x_Grad += __shfl_xor_sync(0xffffffff, local_sum_Output_x_Grad, s);\n      }\n      CompType sum_Output_x_Grad = local_sum_Output_x_Grad;\n      // In-place update\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_grad[i] = local_grad[i] / (sum_fwd_input + epsilon) -\n                        sum_Output_x_Grad / ((sum_fwd_input + epsilon) * (sum_fwd_input + epsilon));\n      }\n      __syncwarp();\n    }\n\n    // Pre-softmax bwd\n    if (score_function == 1) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, nullptr,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sigmoid bwd\n    if (score_function == 0) {\n      apply_sigmoid_bwd_on_float(local_grad, local_act_from_fwd, num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sqrtsoftplus bwd\n    // For sqrtsoftplus, local_comp_buf already contains sqrtsoftplus output computed earlier\n    // Now compute gradient: dy/dx = sigmoid(x) / (2 * y)\n    if (score_function == 2) {\n      apply_sqrtsoftplus_bwd_on_float(local_grad, local_comp_buf, local_act_from_fwd, num_experts,\n                                      lane_id);\n      __syncwarp();\n    }\n    // Write the grad_logits to the global mem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      grad_logits[pos_offset + i] = static_cast<DataType>(local_grad[i]);\n    }\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_score_for_moe_aux_loss_backward_kernel_launcher(\n    const CompType *intermediate_output, const float *grad_scores, int num_tokens, int num_experts,\n    int topk, int score_function, DataType *grad_logits, cudaStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // grad_scores\n                              +\n                              num_experts * num_token_per_block * sizeof(CompType)  // act_from_fwd\n                              + num_experts * num_token_per_block * sizeof(CompType);  // comp_buf\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  NVTE_CHECK_CUDA(cudaFuncSetAttribute(fused_score_for_moe_aux_loss_backward_kernel<DataType>,\n                                       cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                       shared_memory_size));\n  fused_score_for_moe_aux_loss_backward_kernel<DataType>\n      <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n          intermediate_output, grad_scores, num_tokens, num_experts, topk, score_function,\n          grad_logits);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_score_for_moe_aux_loss_backward(const Tensor &intermediate_output,\n                                           const Tensor &grad_scores, int num_tokens,\n                                           int num_experts, int topk, int score_function,\n                                           Tensor &grad_logits, cudaStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_logits.data.dtype, DataType,\n      fused_score_for_moe_aux_loss_backward_kernel_launcher<DataType>(\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr),\n          reinterpret_cast<float *>(grad_scores.data.dptr), num_tokens, num_experts, topk,\n          score_function, reinterpret_cast<DataType *>(grad_logits.data.dptr), stream););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_score_for_moe_aux_loss_forward(const NVTETensor logits, int num_tokens,\n                                               int num_experts, int topk, int score_function,\n                                               NVTETensor scores, const NVTETensor routing_map,\n                                               const NVTETensor intermediate_output,\n                                               cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_score_for_moe_aux_loss_forward);\n  using namespace transformer_engine;\n  fused_router::fused_score_for_moe_aux_loss_forward(\n      *convertNVTETensorCheck(logits), num_tokens, num_experts, topk, score_function,\n      *convertNVTETensorCheck(scores), *convertNVTETensorCheck(routing_map),\n      *convertNVTETensorCheck(intermediate_output), stream);\n}\n\nvoid nvte_fused_score_for_moe_aux_loss_backward(const NVTETensor intermediate_output,\n                                                const NVTETensor grad_scores, int num_tokens,\n                                                int num_experts, int topk, int score_function,\n                                                NVTETensor grad_logits, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_score_for_moe_aux_loss_backward);\n  using namespace transformer_engine;\n  fused_router::fused_score_for_moe_aux_loss_backward(\n      *convertNVTETensorCheck(intermediate_output), *convertNVTETensorCheck(grad_scores),\n      num_tokens, num_experts, topk, score_function, *convertNVTETensorCheck(grad_logits), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/fused_router.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n#include \"utils.h\"\n\nnamespace transformer_engine {\nnamespace fused_router {\n\ntemplate <typename DataType, TopkFuncType TopkFunc = TopkFuncType::Naive>\n__global__ void fused_score_for_moe_aux_loss_forward_kernel(const DataType *logits, int num_tokens,\n                                                            int num_experts, int topk,\n                                                            int score_function, float *scores,\n                                                            bool *routing_map,\n                                                            CompType *intermediate_output) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem_scores_for_aux_loss[];\n  CompType *logits_buf = reinterpret_cast<CompType *>(shmem_scores_for_aux_loss);\n  CompType *topk_logits_buf =\n      reinterpret_cast<CompType *>(logits_buf + num_experts * num_token_per_block);\n  int *topk_indices_buf = reinterpret_cast<int *>(topk_logits_buf + topk * num_token_per_block);\n  // The address of buffers on the current warp\n  CompType *local_logits = logits_buf + warp_id * num_experts;\n  CompType *topk_logits = topk_logits_buf + warp_id * topk;\n  int *topk_indices = topk_indices_buf + warp_id * topk;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the logits to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Clear the routing_map (num_experts)\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      routing_map[pos_offset + i] = false;\n      if (score_function == 1) {\n        intermediate_output[pos_offset + i] = -std::numeric_limits<CompType>::infinity();\n      }\n    }\n    // Load the logits to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_logits[i] = static_cast<CompType>(logits[pos_offset + i]);\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Preprocess\n         * Possible preprocess the scores before the topk operation\n         * - Pre-softmax\n         * - Sigmoid\n         * - Sqrtsoftplus\n         * - Sigmoid/Sqrtsoftplus post-processing when topk > 1\n         * This is in-place scores update\n         */\n    if (score_function == 1) {  // score_function == 1 means softmax\n      // Apply softmax to the logits before the topk\n      apply_softmax_on_float(local_logits, num_experts, lane_id);\n      __syncwarp();\n      // Save the softmax output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];\n      }\n    } else if (score_function == 0) {  // score_function == 0 means sigmoid\n      // Apply sigmoid to the logits\n      apply_sigmoid_on_float(local_logits, num_experts, lane_id);\n      __syncwarp();\n      // Save the sigmoid output for backward\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];\n      }\n    } else if (score_function == 2) {  // score_function == 2 means sqrtsoftplus\n      // First save the original logits for backward (needed for gradient computation)\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        intermediate_output[pos_offset + i] = local_logits[i];  // Save original logits\n      }\n      __syncwarp();\n      // Apply sqrtsoftplus to the logits\n      apply_sqrtsoftplus_on_float(local_logits, num_experts, lane_id);\n    }\n\n    __syncwarp();  //Confirm the scores is written to the output\n\n    // Sigmoid/Sqrtsoftplus post-processing\n    if (score_function == 0 || score_function == 2) {\n      auto sum_logits =\n          warp_reduce_on_shmem(local_logits, num_experts, ReduceFuncType::SUM, lane_id);\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_logits[i] /= (sum_logits + epsilon);\n      }\n      __syncwarp();\n    }\n\n    /***\n         * Section: Topk\n         * Get the topk indices\n         */\n    topk_and_mask<TopkFunc>(local_logits, num_experts, topk, topk_indices, topk_logits, lane_id);\n    __syncwarp();\n\n    // Write the routing_map to the output tensor\n    for (int i = lane_id; i < topk; i += kThreadsPerWarp) {\n      routing_map[pos_offset + topk_indices[i]] = true;\n    }\n    // Write the scores to the output tensor\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      scores[pos_offset + i] = local_logits[i];\n    }\n    __threadfence_block();\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_score_for_moe_aux_loss_forward_kernel_launcher(\n    const DataType *logits, int num_tokens, int num_experts, int topk, int score_function,\n    float *scores, bool *routing_map, CompType *intermediate_output, hipStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // logits\n                              + topk * num_token_per_block * sizeof(CompType)       // topk_logits\n                              + topk * num_token_per_block * sizeof(int);           // topk_indices\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  // Radix selection is O(E), independent of K, but it needs 4 passes for 32-bit float;\n  // switch at K=16 where naive O(K^2*E) starts to dominate\n  if (topk < 16) {\n    NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Naive>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Naive>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, score_function, scores, routing_map,\n            intermediate_output);\n  } else {\n    NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Radix>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    fused_score_for_moe_aux_loss_forward_kernel<DataType, TopkFuncType::Radix>\n        <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n            logits, num_tokens, num_experts, topk, score_function, scores, routing_map,\n            intermediate_output);\n  }\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_score_for_moe_aux_loss_forward(const Tensor &logits, int num_tokens, int num_experts,\n                                          int topk, int score_function, Tensor &scores,\n                                          Tensor &routing_map, Tensor &intermediate_output,\n                                          hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      logits.data.dtype, DataType,\n      fused_score_for_moe_aux_loss_forward_kernel_launcher<DataType>(\n          reinterpret_cast<DataType *>(logits.data.dptr), num_tokens, num_experts, topk,\n          score_function, reinterpret_cast<float *>(scores.data.dptr),\n          reinterpret_cast<bool *>(routing_map.data.dptr),\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr), stream););\n}\n\ntemplate <typename DataType>\n__global__ void fused_score_for_moe_aux_loss_backward_kernel(const CompType *intermediate_output,\n                                                             const float *grad_scores,\n                                                             int num_tokens, int num_experts,\n                                                             int topk, int score_function,\n                                                             DataType *grad_logits) {\n  /***\n     * Section: Global Variables/Addresses init\n     * - Each warp is responsible for one token, and has own shared memory buffer.\n     *   Then __syncwarp() is used instead of __syncthreads()\n     */\n  // Used variables/addresses init\n  int num_token_per_block = blockDim.x / kThreadsPerWarp;\n  int warp_id = threadIdx.x / kThreadsPerWarp;\n  int lane_id = threadIdx.x % kThreadsPerWarp;\n  extern __shared__ float shmem[];\n  CompType *grad_scores_buf = reinterpret_cast<CompType *>(shmem);\n  // To store the output of softmax/sigmoid from fwd, or original logits for sqrtsoftplus\n  CompType *act_from_fwd_buf = grad_scores_buf + num_experts * num_token_per_block;\n  CompType *comp_buf = act_from_fwd_buf + num_experts * num_token_per_block;\n  // The address of buffers on the current warp\n  CompType *local_grad = grad_scores_buf + warp_id * num_experts;\n  CompType *local_act_from_fwd = act_from_fwd_buf + warp_id * num_experts;\n  CompType *local_comp_buf = comp_buf + warp_id * num_experts;\n\n  /***\n     * Section: Main Loop\n     * - Each warp is responsible for one token\n     */\n  int total_round = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  for (int round = blockIdx.x; round < total_round; round += gridDim.x) {\n    int token_offset_cur_warp = round * num_token_per_block + warp_id;\n    // Each warp is responsible for one token\n    if (token_offset_cur_warp >= num_tokens) break;\n\n    /***\n         * Section: Init buffer\n         * - Clear the global buffer which will accept the result of this round\n         * - Clear/Init the shmem buffer used by current warp this round\n         * - Load the dgrad/output_from_fwd to shmem\n         */\n    int pos_offset = token_offset_cur_warp * num_experts;\n    // Load the dgrad/output_from_fwd to shmem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      local_grad[i] = grad_scores[pos_offset + i];\n      local_act_from_fwd[i] = intermediate_output[pos_offset + i];\n    }\n    __threadfence_block();\n    __syncwarp();\n\n    /***\n         * Section: Backward of ops before the topk\n         * - Pre-softmax bwd\n         * - Sigmoid/Sqrtsoftplus Post-processing bwd when topk > 1\n         * - Sigmoid bwd\n         * - Sqrtsoftplus bwd\n         * - Write the grad_logits to the global mem\n         */\n    // Sqrtsoftplus: First compute sqrtsoftplus output from original logits\n    // (needed for both post-processing bwd and activation bwd, compute once here)\n    // For sqrtsoftplus, intermediate_output stores original logits\n    if (score_function == 2) {\n      // Copy original logits to local_comp_buf and apply sqrtsoftplus in-place\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_comp_buf[i] = local_act_from_fwd[i];\n      }\n      __syncwarp();\n      apply_sqrtsoftplus_on_float(local_comp_buf, num_experts, lane_id);\n      __syncwarp();\n    }\n\n    // Sigmoid/Sqrtsoftplus Post-processing bwd (normalization backward)\n    if (score_function == 0 || score_function == 2) {\n      // Select the correct activation output buffer:\n      // - Sigmoid: local_act_from_fwd already contains sigmoid output\n      // - Sqrtsoftplus: local_comp_buf contains sqrtsoftplus output computed above\n      CompType *act_output = (score_function == 0) ? local_act_from_fwd : local_comp_buf;\n\n      auto sum_fwd_input =\n          warp_reduce_on_shmem(act_output, num_experts, ReduceFuncType::SUM, lane_id);\n      // Compute sum of output * grad using registers\n      CompType local_sum_Output_x_Grad = 0.0;\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_sum_Output_x_Grad += local_grad[i] * act_output[i];\n      }\n      // Warp reduce the sum\n      for (int s = 16; s > 0; s /= 2) {\n        local_sum_Output_x_Grad += __shfl_xor_sync(0xffffffff, local_sum_Output_x_Grad, s);\n      }\n      CompType sum_Output_x_Grad = local_sum_Output_x_Grad;\n      // In-place update\n      for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n        local_grad[i] = local_grad[i] / (sum_fwd_input + epsilon) -\n                        sum_Output_x_Grad / ((sum_fwd_input + epsilon) * (sum_fwd_input + epsilon));\n      }\n      __syncwarp();\n    }\n\n    // Pre-softmax bwd\n    if (score_function == 1) {\n      apply_softmax_bwd_on_float(local_grad, local_act_from_fwd, local_comp_buf, nullptr,\n                                 num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sigmoid bwd\n    if (score_function == 0) {\n      apply_sigmoid_bwd_on_float(local_grad, local_act_from_fwd, num_experts, lane_id);\n      __syncwarp();\n    }\n    // Sqrtsoftplus bwd\n    // For sqrtsoftplus, local_comp_buf already contains sqrtsoftplus output computed earlier\n    // Now compute gradient: dy/dx = sigmoid(x) / (2 * y)\n    if (score_function == 2) {\n      apply_sqrtsoftplus_bwd_on_float(local_grad, local_comp_buf, local_act_from_fwd, num_experts,\n                                      lane_id);\n      __syncwarp();\n    }\n    // Write the grad_logits to the global mem\n    for (int i = lane_id; i < num_experts; i += kThreadsPerWarp) {\n      grad_logits[pos_offset + i] = static_cast<DataType>(local_grad[i]);\n    }\n    __syncwarp();\n  }\n}\n\ntemplate <typename DataType>\nvoid fused_score_for_moe_aux_loss_backward_kernel_launcher(\n    const CompType *intermediate_output, const float *grad_scores, int num_tokens, int num_experts,\n    int topk, int score_function, DataType *grad_logits, hipStream_t stream) {\n  // Meta data for the kernel\n  size_t num_token_per_block = kThreadsPerBlock / kThreadsPerWarp;\n  size_t grid_size = (num_tokens + num_token_per_block - 1) / num_token_per_block;\n  size_t shared_memory_size = num_experts * num_token_per_block * sizeof(CompType)  // grad_scores\n                              +\n                              num_experts * num_token_per_block * sizeof(CompType)  // act_from_fwd\n                              + num_experts * num_token_per_block * sizeof(CompType);  // comp_buf\n  check_shared_memory_capacity_num_experts(shared_memory_size, num_experts);\n  NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_score_for_moe_aux_loss_backward_kernel<DataType>),\n                                       hipFuncAttributeMaxDynamicSharedMemorySize,\n                                       shared_memory_size));\n  fused_score_for_moe_aux_loss_backward_kernel<DataType>\n      <<<grid_size, kThreadsPerBlock, shared_memory_size, stream>>>(\n          intermediate_output, grad_scores, num_tokens, num_experts, topk, score_function,\n          grad_logits);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_score_for_moe_aux_loss_backward(const Tensor &intermediate_output,\n                                           const Tensor &grad_scores, int num_tokens,\n                                           int num_experts, int topk, int score_function,\n                                           Tensor &grad_logits, hipStream_t stream) {\n  TE_ROUTER_PROBS_TYPE_SWITCH_ALL(\n      grad_logits.data.dtype, DataType,\n      fused_score_for_moe_aux_loss_backward_kernel_launcher<DataType>(\n          reinterpret_cast<CompType *>(intermediate_output.data.dptr),\n          reinterpret_cast<float *>(grad_scores.data.dptr), num_tokens, num_experts, topk,\n          score_function, reinterpret_cast<DataType *>(grad_logits.data.dptr), stream););\n}\n\n}  // namespace fused_router\n}  // namespace transformer_engine\n\nvoid nvte_fused_score_for_moe_aux_loss_forward(const NVTETensor logits, int num_tokens,\n                                               int num_experts, int topk, int score_function,\n                                               NVTETensor scores, const NVTETensor routing_map,\n                                               const NVTETensor intermediate_output,\n                                               hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_score_for_moe_aux_loss_forward);\n  using namespace transformer_engine;\n  fused_router::fused_score_for_moe_aux_loss_forward(\n      *convertNVTETensorCheck(logits), num_tokens, num_experts, topk, score_function,\n      *convertNVTETensorCheck(scores), *convertNVTETensorCheck(routing_map),\n      *convertNVTETensorCheck(intermediate_output), stream);\n}\n\nvoid nvte_fused_score_for_moe_aux_loss_backward(const NVTETensor intermediate_output,\n                                                const NVTETensor grad_scores, int num_tokens,\n                                                int num_experts, int topk, int score_function,\n                                                NVTETensor grad_logits, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_score_for_moe_aux_loss_backward);\n  using namespace transformer_engine;\n  fused_router::fused_score_for_moe_aux_loss_backward(\n      *convertNVTETensorCheck(intermediate_output), *convertNVTETensorCheck(grad_scores),\n      num_tokens, num_experts, topk, score_function, *convertNVTETensorCheck(grad_logits), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_router/fused_score_for_moe_aux_loss.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/fused_rope.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename scalar_t>\n__device__ void fused_rope_block_forward(const scalar_t *src, const float *freqs, scalar_t *dst,\n                                         const bool interleaved, const int s_id,\n                                         const int offset_block, const int offset_block_dst,\n                                         const int h, const int d, const int d2, const int stride_h,\n                                         const int stride_d, const int o_stride_h,\n                                         const int o_stride_d) {\n  extern __shared__ float shared_mem_cos_sin[];\n  float *shared_mem_cos = shared_mem_cos_sin;\n  float *shared_mem_sin = shared_mem_cos_sin + d2;\n  int tid = threadIdx.x * blockDim.y + threadIdx.y;\n  for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n    sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n      float v_cos = shared_mem_cos[d_id];\n      float v_sin = shared_mem_sin[d_id];\n      int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n      int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n      float v_src = src[offset_src];\n      float v_src_rotate;\n      if (!interleaved) {\n        v_src_rotate = (d_id + d2 / 2 < d2)\n                           ? -static_cast<float>(src[offset_src + (d2 / 2) * stride_d])\n                           : static_cast<float>(src[offset_src + (d2 / 2 - d2) * stride_d]);\n      } else {\n        v_src_rotate = (d_id % 2 == 0)\n                           // d_id + 1\n                           ? -static_cast<float>(src[offset_src + stride_d])\n                           // d_id - 1\n                           : static_cast<float>(src[offset_src - stride_d]);\n      }\n      dst[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n    }\n  }\n\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n        int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n        int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n        dst[offset_dst] = src[offset_src];\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_rope_block_backward(const scalar_t *src, const float *freqs, scalar_t *dst,\n                                          const bool interleaved, const int s_id,\n                                          const int offset_block, const int offset_block_dst,\n                                          const int h, const int d, const int d2,\n                                          const int stride_h, const int stride_d,\n                                          const int o_stride_h, const int o_stride_d) {\n  extern __shared__ float shared_mem_cos_sin[];\n  float *shared_mem_cos = shared_mem_cos_sin;\n  float *shared_mem_sin = shared_mem_cos_sin + d2;\n  int tid = threadIdx.x * blockDim.y + threadIdx.y;\n  for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n    sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n      int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n      int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n      float v_src = src[offset_src];\n      float v_cos = shared_mem_cos[d_id];\n      float v_src_rotate, v_sin;\n      if (!interleaved) {\n        if (d_id + d2 / 2 < d2) {\n          v_src_rotate = static_cast<float>(src[offset_src + (d2 / 2) * stride_d]);\n          v_sin = shared_mem_sin[d_id + d2 / 2];\n        } else {\n          v_src_rotate = static_cast<float>(src[offset_src + (d2 / 2 - d2) * stride_d]);\n          v_sin = -shared_mem_sin[d_id + d2 / 2 - d2];\n        }\n      } else {\n        if (d_id % 2 == 0) {\n          v_src_rotate = static_cast<float>(src[offset_src + stride_d]);\n          v_sin = shared_mem_sin[d_id + 1];\n        } else {\n          v_src_rotate = static_cast<float>(src[offset_src - stride_d]);\n          v_sin = -shared_mem_sin[d_id - 1];\n        }\n      }\n      dst[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n    }\n  }\n\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n        int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n        int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n        dst[offset_dst] = src[offset_src];\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_rope_forward_kernel(const scalar_t *src, const int *cu_seqlens,\n                                          const float *freqs, const int *start_positions,\n                                          scalar_t *dst, const bool interleaved, const int cp_size,\n                                          const int cp_rank, const int s, const int h, const int d,\n                                          const int d2, const int stride_s_or_t, const int stride_b,\n                                          const int stride_h, const int stride_d,\n                                          const int o_stride_s_or_t, const int o_stride_b,\n                                          const int o_stride_h, const int o_stride_d) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int offset_block, offset_block_dst;\n  int cur_seqlens;\n  if (cu_seqlens != nullptr) {  // THD\n    int start = cu_seqlens[b_id] / cp_size;\n    int end = cu_seqlens[b_id + 1] / cp_size;\n    int t_id = s_id + start;\n    if (t_id >= end) return;\n    offset_block = t_id * stride_s_or_t;\n    offset_block_dst = t_id * o_stride_s_or_t;\n    cur_seqlens = end - start;\n  } else {  // SBHD/BSHD\n    offset_block = s_id * stride_s_or_t + b_id * stride_b;\n    offset_block_dst = s_id * o_stride_s_or_t + b_id * o_stride_b;\n    cur_seqlens = s;\n  }\n\n  // Offset the RoPE embedding by start_positions if provided.\n  int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n  int s_id_for_freqs = s_id + begin_offset;\n\n  // If CP_SIZE > 1, offset the RoPE embedding by cp_rank based on the dual-chunk order.\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs += cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs += cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 - cur_seqlens / 2;\n    }\n  }\n\n  fused_rope_block_forward(src, freqs, dst, interleaved, s_id_for_freqs, offset_block,\n                           offset_block_dst, h, d, d2, stride_h, stride_d, o_stride_h, o_stride_d);\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_rope_backward_kernel(\n    const scalar_t *src, const int *cu_seqlens, const float *freqs, const int *start_positions,\n    scalar_t *dst, const bool interleaved, const int cp_size, const int cp_rank, const int s,\n    const int h, const int d, const int d2, const int stride_s_or_t, const int stride_b,\n    const int stride_h, const int stride_d, const int o_stride_s_or_t, const int o_stride_b,\n    const int o_stride_h, const int o_stride_d) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int offset_block, offset_block_dst;\n  int cur_seqlens;\n  if (cu_seqlens != nullptr) {  // THD\n    int start = cu_seqlens[b_id] / cp_size;\n    int end = cu_seqlens[b_id + 1] / cp_size;\n    int t_id = s_id + start;\n    if (t_id >= end) return;\n    offset_block = t_id * stride_s_or_t;\n    offset_block_dst = t_id * o_stride_s_or_t;\n    cur_seqlens = end - start;\n  } else {  // SBHD/BSHD\n    offset_block = s_id * stride_s_or_t + b_id * stride_b;\n    offset_block_dst = s_id * o_stride_s_or_t + b_id * o_stride_b;\n    cur_seqlens = s;\n  }\n\n  // Offset the RoPE embedding by start_positions if provided.\n  int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n  int s_id_for_freqs = s_id + begin_offset;\n\n  // If CP_SIZE > 1, offset the RoPE embedding by cp_rank based on the dual-chunk order.\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs += cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs += cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 - cur_seqlens / 2;\n    }\n  }\n\n  fused_rope_block_backward(src, freqs, dst, interleaved, s_id_for_freqs, offset_block,\n                            offset_block_dst, h, d, d2, stride_h, stride_d, o_stride_h, o_stride_d);\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_qkv_rope_block_forward(const scalar_t *src, const float *freqs, scalar_t *out,\n                                             const bool interleaved, const int s_id,\n                                             const int offset_block, const int offset_block_dst,\n                                             const int h, const int d, const int d2,\n                                             const int row_offset, const int in_row_length,\n                                             const int out_row_length) {\n  extern __shared__ float shared_mem_cos_sin_qk[];\n  // Split the shared memory into cos and sin parts for q or k\n  float *shared_mem_cos = nullptr;\n  float *shared_mem_sin = nullptr;\n  if (row_offset == 0) {  // q\n    shared_mem_cos = shared_mem_cos_sin_qk;\n    shared_mem_sin = shared_mem_cos_sin_qk + d2;\n  } else {  // k\n    shared_mem_cos = shared_mem_cos_sin_qk + 2 * d2;\n    shared_mem_sin = shared_mem_cos_sin_qk + 3 * d2;\n  }\n  if (freqs != nullptr) {\n    int tid = threadIdx.x * blockDim.y + threadIdx.y;\n    for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n      sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n    }\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n      for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n        int offset_src = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n        int offset_dst = offset_block_dst + h_id * out_row_length + i + d_id;\n        if (freqs != nullptr) {\n          float v_cos, v_sin;\n          v_cos = shared_mem_cos[d_id];\n          v_sin = shared_mem_sin[d_id];\n          float v_src = src[offset_src];\n          float v_src_rotate;\n          if (!interleaved) {\n            v_src_rotate = (d_id + d2 / 2 < d2)\n                               ? -static_cast<float>(src[offset_src + (d2 / 2)])\n                               : static_cast<float>(src[offset_src + (d2 / 2 - d2)]);\n          } else {\n            v_src_rotate = (d_id % 2 == 0) ? -static_cast<float>(src[offset_src + 1])\n                                           : static_cast<float>(src[offset_src - 1]);\n          }\n          out[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n        } else {\n          out[offset_dst] = src[offset_src];\n        }\n      }\n    }\n  }\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n        for (int i = 0; i < out_row_length; i += d) {\n          int offset_src = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n          int offset_dst = offset_block_dst + h_id * out_row_length + i + d_id;\n          out[offset_dst] = src[offset_src];\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_qkv_rope_block_backward(const scalar_t *grad_out, const float *freqs,\n                                              scalar_t *out, const bool interleaved, const int s_id,\n                                              const int offset_block, const int offset_block_dst,\n                                              const int h, const int d, const int d2,\n                                              const int row_offset, const int in_row_length,\n                                              const int out_row_length) {\n  extern __shared__ float shared_mem_cos_sin_qk[];\n  float *shared_mem_cos = nullptr;\n  float *shared_mem_sin = nullptr;\n  // Split the shared memory into cos and sin parts for q or k\n  if (row_offset == 0) {  // q\n    shared_mem_cos = shared_mem_cos_sin_qk;\n    shared_mem_sin = shared_mem_cos_sin_qk + d2;\n  } else {  // k\n    shared_mem_cos = shared_mem_cos_sin_qk + 2 * d2;\n    shared_mem_sin = shared_mem_cos_sin_qk + 3 * d2;\n  }\n  if (freqs != nullptr) {\n    int tid = threadIdx.x * blockDim.y + threadIdx.y;\n    for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n      sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n    }\n  }\n  __syncthreads();\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n      for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n        int offset_dst = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n        int offset_src = offset_block_dst + h_id * out_row_length + i + d_id;\n\n        float v_src = grad_out[offset_src];\n        if (freqs != nullptr) {\n          float v_cos, v_sin;\n          v_cos = shared_mem_cos[d_id];\n          float v_src_rotate;\n          if (!interleaved) {\n            if (d_id + d2 / 2 < d2) {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + (d2 / 2)]);\n              v_sin = shared_mem_sin[d_id + d2 / 2];\n            } else {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + (d2 / 2 - d2)]);\n              v_sin = -shared_mem_sin[d_id + d2 / 2 - d2];\n            }\n          } else {\n            if (d_id % 2 == 0) {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + 1]);\n              v_sin = shared_mem_sin[d_id + 1];\n            } else {\n              v_src_rotate = static_cast<float>(grad_out[offset_src - 1]);\n              v_sin = -shared_mem_sin[d_id - 1];\n            }\n          }\n          out[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n        } else {\n          out[offset_dst] = grad_out[offset_src];\n        }\n      }\n    }\n  }\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n      for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n        for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n          int offset_dst = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n          int offset_src = offset_block_dst + h_id * out_row_length + i + d_id;\n          out[offset_dst] = grad_out[offset_src];\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_qkv_rope_forward_kernel(\n    const scalar_t *qkv_input, const float *q_freqs, const float *k_freqs,\n    const int *start_positions, scalar_t *q_out, scalar_t *k_out, scalar_t *v_out,\n    const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size, const int cp_rank,\n    const int s, const int b, const int h, const int d, const int d2, const int q_split_arg,\n    const int k_split_arg, const int v_split_arg) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int cur_seqlens = s;\n  int total_d = q_split_arg + k_split_arg + v_split_arg;\n  int offset_block, offset_block_dst_q, offset_block_dst_k, offset_block_dst_v;\n  if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    offset_block = s_id * b * h * total_d + b_id * h * total_d;\n    offset_block_dst_q = s_id * b * h * q_split_arg + b_id * h * q_split_arg;\n    offset_block_dst_k = s_id * b * h * k_split_arg + b_id * h * k_split_arg;\n    offset_block_dst_v = s_id * b * h * v_split_arg + b_id * h * v_split_arg;\n  } else {\n    offset_block = b_id * s * h * total_d + s_id * h * total_d;\n    offset_block_dst_q = b_id * s * h * q_split_arg + s_id * h * q_split_arg;\n    offset_block_dst_k = b_id * s * h * k_split_arg + s_id * h * k_split_arg;\n    offset_block_dst_v = b_id * s * h * v_split_arg + s_id * h * v_split_arg;\n  }\n\n  int q_limit = q_split_arg;\n  int k_limit = q_limit + k_split_arg;\n  int s_id_for_freqs;\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs = s_id + cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs =\n          cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 + s_id - cur_seqlens / 2;\n    }\n  } else {\n    int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n    s_id_for_freqs = s_id + begin_offset;\n  }\n  fused_qkv_rope_block_forward(qkv_input, q_freqs, q_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_q, h, d, d2, 0, total_d, q_split_arg);\n  fused_qkv_rope_block_forward(qkv_input, k_freqs, k_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_k, h, d, d2, q_limit, total_d, k_split_arg);\n  fused_qkv_rope_block_forward(qkv_input, nullptr, v_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_v, h, d, d2, k_limit, total_d, v_split_arg);\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_qkv_rope_backward_kernel(\n    const scalar_t *grad_out_q, const scalar_t *grad_out_k, const scalar_t *grad_out_v,\n    const float *q_freqs, const float *k_freqs, scalar_t *qkv_grad,\n    const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size, const int cp_rank,\n    const int s, const int b, const int h, const int d, const int d2, const int q_split_arg,\n    const int k_split_arg, const int v_split_arg) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int cur_seqlens = s;\n  int offset_block, offset_block_dst_q, offset_block_dst_k, offset_block_dst_v;\n  int total_d = q_split_arg + k_split_arg + v_split_arg;\n  if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    offset_block = s_id * b * h * total_d + b_id * h * total_d;\n    offset_block_dst_q = s_id * b * h * q_split_arg + b_id * h * q_split_arg;\n    offset_block_dst_k = s_id * b * h * k_split_arg + b_id * h * k_split_arg;\n    offset_block_dst_v = s_id * b * h * v_split_arg + b_id * h * v_split_arg;\n  } else {\n    offset_block = b_id * s * h * total_d + s_id * h * total_d;\n    offset_block_dst_q = b_id * s * h * q_split_arg + s_id * h * q_split_arg;\n    offset_block_dst_k = b_id * s * h * k_split_arg + s_id * h * k_split_arg;\n    offset_block_dst_v = b_id * s * h * v_split_arg + s_id * h * v_split_arg;\n  }\n  int q_limit = q_split_arg;\n  int k_limit = q_limit + k_split_arg;\n  int s_id_for_freqs;\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs = s_id + cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs =\n          cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 + s_id - cur_seqlens / 2;\n    }\n  } else {\n    s_id_for_freqs = s_id;\n  }\n  fused_qkv_rope_block_backward(grad_out_q, q_freqs, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_q, h, d, d2, 0, total_d,\n                                q_split_arg);\n  fused_qkv_rope_block_backward(grad_out_k, k_freqs, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_k, h, d, d2, q_limit, total_d,\n                                k_split_arg);\n  fused_qkv_rope_block_backward(grad_out_v, nullptr, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_v, h, d, d2, k_limit, total_d,\n                                v_split_arg);\n}\n\ntemplate <typename scalar_t>\nvoid fused_rope_forward_launcher(const scalar_t *input, const int *cu_seqlens, const float *freqs,\n                                 const int *start_positions, scalar_t *output,\n                                 const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                 const int cp_size, const int cp_rank, const int s, const int b,\n                                 const int h, const int d, const int d2, const int stride_s_or_t,\n                                 const int stride_b, const int stride_h, const int stride_d,\n                                 cudaStream_t stream) {\n  int warps_per_block = h < 16 ? 4 : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 2 * d2 * sizeof(float);  // cos, sin\n  int o_stride_s_or_t, o_stride_b;\n  if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    NVTE_CHECK(cu_seqlens != nullptr, \"cu_seqlens is required for THD format\");\n    o_stride_s_or_t = h * d;\n    o_stride_b = 0;\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    o_stride_s_or_t = b * h * d;\n    o_stride_b = h * d;\n  } else {\n    o_stride_s_or_t = h * d;\n    o_stride_b = s * h * d;\n  }\n  const int o_stride_h = d;\n  const int o_stride_d = 1;\n\n  fused_rope_forward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      input, cu_seqlens, freqs, start_positions, output, interleaved, cp_size, cp_rank, s, h, d, d2,\n      stride_s_or_t, stride_b, stride_h, stride_d, o_stride_s_or_t, o_stride_b, o_stride_h,\n      o_stride_d);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_rope_backward_launcher(const scalar_t *output_grads, const int *cu_seqlens,\n                                  const float *freqs, const int *start_positions,\n                                  scalar_t *input_grads, const NVTE_QKV_Format qkv_format,\n                                  const bool interleaved, const int cp_size, const int cp_rank,\n                                  const int s, const int b, const int h, const int d, const int d2,\n                                  const int stride_s_or_t, const int stride_b, const int stride_h,\n                                  const int stride_d, cudaStream_t stream) {\n  int warps_per_block = h < 16 ? 4 : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 2 * d2 * sizeof(float);  // cos, sin\n  int o_stride_s_or_t, o_stride_b;\n  if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    NVTE_CHECK(cu_seqlens != nullptr, \"cu_seqlens is required for THD format\");\n    o_stride_s_or_t = h * d;\n    o_stride_b = 0;\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    o_stride_s_or_t = b * h * d;\n    o_stride_b = h * d;\n  } else {\n    o_stride_s_or_t = h * d;\n    o_stride_b = s * h * d;\n  }\n  const int o_stride_h = d;\n  const int o_stride_d = 1;\n\n  fused_rope_backward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      output_grads, cu_seqlens, freqs, start_positions, input_grads, interleaved, cp_size, cp_rank,\n      s, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, o_stride_s_or_t, o_stride_b,\n      o_stride_h, o_stride_d);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_qkv_rope_forward_launcher(const scalar_t *qkv_input, const float *q_freqs,\n                                     const float *k_freqs, const int *start_positions,\n                                     scalar_t *q_out, scalar_t *k_out, scalar_t *v_out,\n                                     const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                     const int cp_size, const int cp_rank, const int s, const int b,\n                                     const int h, const int d, const int d2,\n                                     const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                     const int qkv_split_arg_list_2, cudaStream_t stream) {\n  const int THREADS_PER_WARP = 32;\n  int warps_per_block = (h <= 8) ? h : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 4 * d2 * sizeof(float);  // cos, sin * q ,k\n\n  fused_qkv_rope_forward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      qkv_input, q_freqs, k_freqs, start_positions, q_out, k_out, v_out, qkv_format, interleaved,\n      cp_size, cp_rank, s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n      qkv_split_arg_list_2);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_qkv_rope_backward_launcher(const scalar_t *q_grad_out, const scalar_t *k_grad_out,\n                                      const scalar_t *v_grad_out, const float *q_freqs,\n                                      const float *k_freqs, scalar_t *qkv_grad_input,\n                                      const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                      const int cp_size, const int cp_rank, const int s,\n                                      const int b, const int h, const int d, const int d2,\n                                      const int qkv_split_arg_list_0,\n                                      const int qkv_split_arg_list_1,\n                                      const int qkv_split_arg_list_2, cudaStream_t stream) {\n  const int THREADS_PER_WARP = 32;\n  const int warps_per_block = (h <= 8) ? h : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 4 * d2 * sizeof(float);  // cos, sin * q ,k\n\n  fused_qkv_rope_backward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      q_grad_out, k_grad_out, v_grad_out, q_freqs, k_freqs, qkv_grad_input, qkv_format, interleaved,\n      cp_size, cp_rank, s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n      qkv_split_arg_list_2);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fused_rope_forward(const Tensor &input, const Tensor &cu_seqlens, const Tensor &freqs,\n                        const Tensor &start_positions, Tensor *output,\n                        const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size,\n                        const int cp_rank, const int s, const int b, const int h, const int d,\n                        const int d2, const int stride_s_or_t, const int stride_b,\n                        const int stride_h, const int stride_d, cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.data.dtype, scalar_t,\n      fused_rope_forward_launcher(reinterpret_cast<const scalar_t *>(input.data.dptr),\n                                  reinterpret_cast<const int *>(cu_seqlens.data.dptr),\n                                  reinterpret_cast<const float *>(freqs.data.dptr),\n                                  reinterpret_cast<const int *>(start_positions.data.dptr),\n                                  reinterpret_cast<scalar_t *>(output->data.dptr), qkv_format,\n                                  interleaved, cp_size, cp_rank, s, b, h, d, d2, stride_s_or_t,\n                                  stride_b, stride_h, stride_d, stream););\n}\n\nvoid fused_rope_backward(const Tensor &output_grads, const Tensor &cu_seqlens, const Tensor &freqs,\n                         const Tensor &start_positions, Tensor *input_grads,\n                         const NVTE_QKV_Format qkv_format, const bool interleaved,\n                         const int cp_size, const int cp_rank, const int s, const int b,\n                         const int h, const int d, const int d2, const int stride_s_or_t,\n                         const int stride_b, const int stride_h, const int stride_d,\n                         cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      output_grads.data.dtype, scalar_t,\n      fused_rope_backward_launcher(reinterpret_cast<const scalar_t *>(output_grads.data.dptr),\n                                   reinterpret_cast<const int *>(cu_seqlens.data.dptr),\n                                   reinterpret_cast<const float *>(freqs.data.dptr),\n                                   reinterpret_cast<const int *>(start_positions.data.dptr),\n                                   reinterpret_cast<scalar_t *>(input_grads->data.dptr), qkv_format,\n                                   interleaved, cp_size, cp_rank, s, b, h, d, d2, stride_s_or_t,\n                                   stride_b, stride_h, stride_d, stream););\n}\n\nvoid fused_qkv_rope_forward(const Tensor &qkv_input, const Tensor &q_freqs, const Tensor &k_freqs,\n                            const Tensor &start_positions, Tensor *q_out, Tensor *k_out,\n                            Tensor *v_out, const NVTE_QKV_Format qkv_format, const bool interleaved,\n                            const int cp_size, const int cp_rank, const int s, const int b,\n                            const int h, const int d, const int d2, const int qkv_split_arg_list_0,\n                            const int qkv_split_arg_list_1, const int qkv_split_arg_list_2,\n                            cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      qkv_input.data.dtype, scalar_t,\n      fused_qkv_rope_forward_launcher(reinterpret_cast<const scalar_t *>(qkv_input.data.dptr),\n                                      reinterpret_cast<const float *>(q_freqs.data.dptr),\n                                      reinterpret_cast<const float *>(k_freqs.data.dptr),\n                                      reinterpret_cast<const int *>(start_positions.data.dptr),\n                                      reinterpret_cast<scalar_t *>(q_out->data.dptr),\n                                      reinterpret_cast<scalar_t *>(k_out->data.dptr),\n                                      reinterpret_cast<scalar_t *>(v_out->data.dptr), qkv_format,\n                                      interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                                      qkv_split_arg_list_0, qkv_split_arg_list_1,\n                                      qkv_split_arg_list_2, stream););\n}\n\nvoid fused_qkv_rope_backward(const Tensor &q_grad_out, const Tensor &k_grad_out,\n                             const Tensor &v_grad_out, const Tensor &q_freqs, const Tensor &k_freqs,\n                             Tensor *qkv_grad_input, const NVTE_QKV_Format qkv_format,\n                             const bool interleaved, const int cp_size, const int cp_rank,\n                             const int s, const int b, const int h, const int d, const int d2,\n                             const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                             const int qkv_split_arg_list_2, cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      q_grad_out.data.dtype, scalar_t,\n      fused_qkv_rope_backward_launcher(reinterpret_cast<const scalar_t *>(q_grad_out.data.dptr),\n                                       reinterpret_cast<const scalar_t *>(k_grad_out.data.dptr),\n                                       reinterpret_cast<const scalar_t *>(v_grad_out.data.dptr),\n                                       reinterpret_cast<const float *>(q_freqs.data.dptr),\n                                       reinterpret_cast<const float *>(k_freqs.data.dptr),\n                                       reinterpret_cast<scalar_t *>(qkv_grad_input->data.dptr),\n                                       qkv_format, interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                                       qkv_split_arg_list_0, qkv_split_arg_list_1,\n                                       qkv_split_arg_list_2, stream););\n}\n}  // end namespace transformer_engine\n\nvoid nvte_fused_rope_forward(const NVTETensor input, const NVTETensor cu_seqlens,\n                             const NVTETensor freqs, const NVTETensor start_positions,\n                             NVTETensor output, const NVTE_QKV_Format qkv_format,\n                             const bool interleaved, const int cp_size, const int cp_rank,\n                             const int s, const int b, const int h, const int d, const int d2,\n                             const int stride_s_or_t, const int stride_b, const int stride_h,\n                             const int stride_d, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_rope_forward);\n  using namespace transformer_engine;\n  fused_rope_forward(*convertNVTETensorCheck(input), *convertNVTETensorCheck(cu_seqlens),\n                     *convertNVTETensorCheck(freqs), *convertNVTETensorCheck(start_positions),\n                     convertNVTETensorCheck(output), qkv_format, interleaved, cp_size, cp_rank, s,\n                     b, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, stream);\n}\n\nvoid nvte_fused_rope_backward(const NVTETensor output_grads, const NVTETensor cu_seqlens,\n                              const NVTETensor freqs, const NVTETensor start_positions,\n                              NVTETensor input_grads, const NVTE_QKV_Format qkv_format,\n                              const bool interleaved, const int cp_size, const int cp_rank,\n                              const int s, const int b, const int h, const int d, const int d2,\n                              const int stride_s_or_t, const int stride_b, const int stride_h,\n                              const int stride_d, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_rope_backward);\n  using namespace transformer_engine;\n  fused_rope_backward(*convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(cu_seqlens),\n                      *convertNVTETensorCheck(freqs), *convertNVTETensorCheck(start_positions),\n                      convertNVTETensorCheck(input_grads), qkv_format, interleaved, cp_size,\n                      cp_rank, s, b, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, stream);\n}\n\nvoid nvte_fused_qkv_rope_forward(const NVTETensor qkv_input, const NVTETensor q_freqs,\n                                 const NVTETensor k_freqs, const NVTETensor start_positions,\n                                 NVTETensor q_out, NVTETensor k_out, NVTETensor v_out,\n                                 const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                 const int cp_size, const int cp_rank, const int s, const int b,\n                                 const int h, const int d, const int d2,\n                                 const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                 const int qkv_split_arg_list_2, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_qkv_rope_forward);\n  using namespace transformer_engine;\n  fused_qkv_rope_forward(*convertNVTETensorCheck(qkv_input), *convertNVTETensorCheck(q_freqs),\n                         *convertNVTETensorCheck(k_freqs), *convertNVTETensorCheck(start_positions),\n                         convertNVTETensorCheck(q_out), convertNVTETensorCheck(k_out),\n                         convertNVTETensorCheck(v_out), qkv_format, interleaved, cp_size, cp_rank,\n                         s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n                         qkv_split_arg_list_2, stream);\n}\n\nvoid nvte_fused_qkv_rope_backward(const NVTETensor q_grad_out, const NVTETensor k_grad_out,\n                                  const NVTETensor v_grad_out, const NVTETensor q_freqs,\n                                  const NVTETensor k_freqs, NVTETensor qkv_grad_input,\n                                  const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                  const int cp_size, const int cp_rank, const int s, const int b,\n                                  const int h, const int d, const int d2,\n                                  const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                  const int qkv_split_arg_list_2, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fused_qkv_rope_backward);\n  using namespace transformer_engine;\n  fused_qkv_rope_backward(*convertNVTETensorCheck(q_grad_out), *convertNVTETensorCheck(k_grad_out),\n                          *convertNVTETensorCheck(v_grad_out), *convertNVTETensorCheck(q_freqs),\n                          *convertNVTETensorCheck(k_freqs), convertNVTETensorCheck(qkv_grad_input),\n                          qkv_format, interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                          qkv_split_arg_list_0, qkv_split_arg_list_1, qkv_split_arg_list_2, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <hip/hip_runtime.h>\n#include <transformer_engine/fused_rope.h>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\n\ntemplate <typename scalar_t>\n__device__ void fused_rope_block_forward(const scalar_t *src, const float *freqs, scalar_t *dst,\n                                         const bool interleaved, const int s_id,\n                                         const int offset_block, const int offset_block_dst,\n                                         const int h, const int d, const int d2, const int stride_h,\n                                         const int stride_d, const int o_stride_h,\n                                         const int o_stride_d) {\n  extern __shared__ float shared_mem_cos_sin[];\n  float *shared_mem_cos = shared_mem_cos_sin;\n  float *shared_mem_sin = shared_mem_cos_sin + d2;\n  int tid = threadIdx.x * blockDim.y + threadIdx.y;\n  for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n    sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n      float v_cos = shared_mem_cos[d_id];\n      float v_sin = shared_mem_sin[d_id];\n      int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n      int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n      float v_src = src[offset_src];\n      float v_src_rotate;\n      if (!interleaved) {\n        v_src_rotate = (d_id + d2 / 2 < d2)\n                           ? -static_cast<float>(src[offset_src + (d2 / 2) * stride_d])\n                           : static_cast<float>(src[offset_src + (d2 / 2 - d2) * stride_d]);\n      } else {\n        v_src_rotate = (d_id % 2 == 0)\n                           // d_id + 1\n                           ? -static_cast<float>(src[offset_src + stride_d])\n                           // d_id - 1\n                           : static_cast<float>(src[offset_src - stride_d]);\n      }\n      dst[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n    }\n  }\n\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n        int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n        int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n        dst[offset_dst] = src[offset_src];\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_rope_block_backward(const scalar_t *src, const float *freqs, scalar_t *dst,\n                                          const bool interleaved, const int s_id,\n                                          const int offset_block, const int offset_block_dst,\n                                          const int h, const int d, const int d2,\n                                          const int stride_h, const int stride_d,\n                                          const int o_stride_h, const int o_stride_d) {\n  extern __shared__ float shared_mem_cos_sin[];\n  float *shared_mem_cos = shared_mem_cos_sin;\n  float *shared_mem_sin = shared_mem_cos_sin + d2;\n  int tid = threadIdx.x * blockDim.y + threadIdx.y;\n  for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n    sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n      int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n      int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n      float v_src = src[offset_src];\n      float v_cos = shared_mem_cos[d_id];\n      float v_src_rotate, v_sin;\n      if (!interleaved) {\n        if (d_id + d2 / 2 < d2) {\n          v_src_rotate = static_cast<float>(src[offset_src + (d2 / 2) * stride_d]);\n          v_sin = shared_mem_sin[d_id + d2 / 2];\n        } else {\n          v_src_rotate = static_cast<float>(src[offset_src + (d2 / 2 - d2) * stride_d]);\n          v_sin = -shared_mem_sin[d_id + d2 / 2 - d2];\n        }\n      } else {\n        if (d_id % 2 == 0) {\n          v_src_rotate = static_cast<float>(src[offset_src + stride_d]);\n          v_sin = shared_mem_sin[d_id + 1];\n        } else {\n          v_src_rotate = static_cast<float>(src[offset_src - stride_d]);\n          v_sin = -shared_mem_sin[d_id - 1];\n        }\n      }\n      dst[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n    }\n  }\n\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n        int offset_src = offset_block + h_id * stride_h + d_id * stride_d;\n        int offset_dst = offset_block_dst + h_id * o_stride_h + d_id * o_stride_d;\n        dst[offset_dst] = src[offset_src];\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_rope_forward_kernel(const scalar_t *src, const int *cu_seqlens,\n                                          const float *freqs, const int *start_positions,\n                                          scalar_t *dst, const bool interleaved, const int cp_size,\n                                          const int cp_rank, const int s, const int h, const int d,\n                                          const int d2, const int stride_s_or_t, const int stride_b,\n                                          const int stride_h, const int stride_d,\n                                          const int o_stride_s_or_t, const int o_stride_b,\n                                          const int o_stride_h, const int o_stride_d) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int offset_block, offset_block_dst;\n  int cur_seqlens;\n  if (cu_seqlens != nullptr) {  // THD\n    int start = cu_seqlens[b_id] / cp_size;\n    int end = cu_seqlens[b_id + 1] / cp_size;\n    int t_id = s_id + start;\n    if (t_id >= end) return;\n    offset_block = t_id * stride_s_or_t;\n    offset_block_dst = t_id * o_stride_s_or_t;\n    cur_seqlens = end - start;\n  } else {  // SBHD/BSHD\n    offset_block = s_id * stride_s_or_t + b_id * stride_b;\n    offset_block_dst = s_id * o_stride_s_or_t + b_id * o_stride_b;\n    cur_seqlens = s;\n  }\n\n  // Offset the RoPE embedding by start_positions if provided.\n  int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n  int s_id_for_freqs = s_id + begin_offset;\n\n  // If CP_SIZE > 1, offset the RoPE embedding by cp_rank based on the dual-chunk order.\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs += cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs += cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 - cur_seqlens / 2;\n    }\n  }\n\n  fused_rope_block_forward(src, freqs, dst, interleaved, s_id_for_freqs, offset_block,\n                           offset_block_dst, h, d, d2, stride_h, stride_d, o_stride_h, o_stride_d);\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_rope_backward_kernel(\n    const scalar_t *src, const int *cu_seqlens, const float *freqs, const int *start_positions,\n    scalar_t *dst, const bool interleaved, const int cp_size, const int cp_rank, const int s,\n    const int h, const int d, const int d2, const int stride_s_or_t, const int stride_b,\n    const int stride_h, const int stride_d, const int o_stride_s_or_t, const int o_stride_b,\n    const int o_stride_h, const int o_stride_d) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int offset_block, offset_block_dst;\n  int cur_seqlens;\n  if (cu_seqlens != nullptr) {  // THD\n    int start = cu_seqlens[b_id] / cp_size;\n    int end = cu_seqlens[b_id + 1] / cp_size;\n    int t_id = s_id + start;\n    if (t_id >= end) return;\n    offset_block = t_id * stride_s_or_t;\n    offset_block_dst = t_id * o_stride_s_or_t;\n    cur_seqlens = end - start;\n  } else {  // SBHD/BSHD\n    offset_block = s_id * stride_s_or_t + b_id * stride_b;\n    offset_block_dst = s_id * o_stride_s_or_t + b_id * o_stride_b;\n    cur_seqlens = s;\n  }\n\n  // Offset the RoPE embedding by start_positions if provided.\n  int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n  int s_id_for_freqs = s_id + begin_offset;\n\n  // If CP_SIZE > 1, offset the RoPE embedding by cp_rank based on the dual-chunk order.\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs += cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs += cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 - cur_seqlens / 2;\n    }\n  }\n\n  fused_rope_block_backward(src, freqs, dst, interleaved, s_id_for_freqs, offset_block,\n                            offset_block_dst, h, d, d2, stride_h, stride_d, o_stride_h, o_stride_d);\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_qkv_rope_block_forward(const scalar_t *src, const float *freqs, scalar_t *out,\n                                             const bool interleaved, const int s_id,\n                                             const int offset_block, const int offset_block_dst,\n                                             const int h, const int d, const int d2,\n                                             const int row_offset, const int in_row_length,\n                                             const int out_row_length) {\n  extern __shared__ float shared_mem_cos_sin_qk[];\n  // Split the shared memory into cos and sin parts for q or k\n  float *shared_mem_cos = nullptr;\n  float *shared_mem_sin = nullptr;\n  if (row_offset == 0) {  // q\n    shared_mem_cos = shared_mem_cos_sin_qk;\n    shared_mem_sin = shared_mem_cos_sin_qk + d2;\n  } else {  // k\n    shared_mem_cos = shared_mem_cos_sin_qk + 2 * d2;\n    shared_mem_sin = shared_mem_cos_sin_qk + 3 * d2;\n  }\n  if (freqs != nullptr) {\n    int tid = threadIdx.x * blockDim.y + threadIdx.y;\n    for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n      sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n    }\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n      for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n        int offset_src = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n        int offset_dst = offset_block_dst + h_id * out_row_length + i + d_id;\n        if (freqs != nullptr) {\n          float v_cos, v_sin;\n          v_cos = shared_mem_cos[d_id];\n          v_sin = shared_mem_sin[d_id];\n          float v_src = src[offset_src];\n          float v_src_rotate;\n          if (!interleaved) {\n            v_src_rotate = (d_id + d2 / 2 < d2)\n                               ? -static_cast<float>(src[offset_src + (d2 / 2)])\n                               : static_cast<float>(src[offset_src + (d2 / 2 - d2)]);\n          } else {\n            v_src_rotate = (d_id % 2 == 0) ? -static_cast<float>(src[offset_src + 1])\n                                           : static_cast<float>(src[offset_src - 1]);\n          }\n          out[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n        } else {\n          out[offset_dst] = src[offset_src];\n        }\n      }\n    }\n  }\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n#pragma unroll\n      for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n        for (int i = 0; i < out_row_length; i += d) {\n          int offset_src = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n          int offset_dst = offset_block_dst + h_id * out_row_length + i + d_id;\n          out[offset_dst] = src[offset_src];\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__device__ void fused_qkv_rope_block_backward(const scalar_t *grad_out, const float *freqs,\n                                              scalar_t *out, const bool interleaved, const int s_id,\n                                              const int offset_block, const int offset_block_dst,\n                                              const int h, const int d, const int d2,\n                                              const int row_offset, const int in_row_length,\n                                              const int out_row_length) {\n  extern __shared__ float shared_mem_cos_sin_qk[];\n  float *shared_mem_cos = nullptr;\n  float *shared_mem_sin = nullptr;\n  // Split the shared memory into cos and sin parts for q or k\n  if (row_offset == 0) {  // q\n    shared_mem_cos = shared_mem_cos_sin_qk;\n    shared_mem_sin = shared_mem_cos_sin_qk + d2;\n  } else {  // k\n    shared_mem_cos = shared_mem_cos_sin_qk + 2 * d2;\n    shared_mem_sin = shared_mem_cos_sin_qk + 3 * d2;\n  }\n  if (freqs != nullptr) {\n    int tid = threadIdx.x * blockDim.y + threadIdx.y;\n    for (int i = tid; i < d2; i += blockDim.x * blockDim.y) {\n      sincosf(freqs[s_id * d2 + i], &shared_mem_sin[i], &shared_mem_cos[i]);\n    }\n  }\n  __syncthreads();\n#pragma unroll\n  for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n    for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n      for (int d_id = threadIdx.x; d_id < d2; d_id += blockDim.x) {\n        int offset_dst = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n        int offset_src = offset_block_dst + h_id * out_row_length + i + d_id;\n\n        float v_src = grad_out[offset_src];\n        if (freqs != nullptr) {\n          float v_cos, v_sin;\n          v_cos = shared_mem_cos[d_id];\n          float v_src_rotate;\n          if (!interleaved) {\n            if (d_id + d2 / 2 < d2) {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + (d2 / 2)]);\n              v_sin = shared_mem_sin[d_id + d2 / 2];\n            } else {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + (d2 / 2 - d2)]);\n              v_sin = -shared_mem_sin[d_id + d2 / 2 - d2];\n            }\n          } else {\n            if (d_id % 2 == 0) {\n              v_src_rotate = static_cast<float>(grad_out[offset_src + 1]);\n              v_sin = shared_mem_sin[d_id + 1];\n            } else {\n              v_src_rotate = static_cast<float>(grad_out[offset_src - 1]);\n              v_sin = -shared_mem_sin[d_id - 1];\n            }\n          }\n          out[offset_dst] = v_src * v_cos + v_src_rotate * v_sin;\n        } else {\n          out[offset_dst] = grad_out[offset_src];\n        }\n      }\n    }\n  }\n  // copy the rest\n  if (d > d2) {\n#pragma unroll\n    for (int h_id = threadIdx.y; h_id < h; h_id += blockDim.y) {\n#pragma unroll\n      for (int i = 0; i < out_row_length; i += d) {\n#pragma unroll\n        for (int d_id = d2 + threadIdx.x; d_id < d; d_id += blockDim.x) {\n          int offset_dst = offset_block + h_id * in_row_length + (row_offset + i) + d_id;\n          int offset_src = offset_block_dst + h_id * out_row_length + i + d_id;\n          out[offset_dst] = grad_out[offset_src];\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_qkv_rope_forward_kernel(\n    const scalar_t *qkv_input, const float *q_freqs, const float *k_freqs,\n    const int *start_positions, scalar_t *q_out, scalar_t *k_out, scalar_t *v_out,\n    const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size, const int cp_rank,\n    const int s, const int b, const int h, const int d, const int d2, const int q_split_arg,\n    const int k_split_arg, const int v_split_arg) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int cur_seqlens = s;\n  int total_d = q_split_arg + k_split_arg + v_split_arg;\n  int offset_block, offset_block_dst_q, offset_block_dst_k, offset_block_dst_v;\n  if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    offset_block = s_id * b * h * total_d + b_id * h * total_d;\n    offset_block_dst_q = s_id * b * h * q_split_arg + b_id * h * q_split_arg;\n    offset_block_dst_k = s_id * b * h * k_split_arg + b_id * h * k_split_arg;\n    offset_block_dst_v = s_id * b * h * v_split_arg + b_id * h * v_split_arg;\n  } else {\n    offset_block = b_id * s * h * total_d + s_id * h * total_d;\n    offset_block_dst_q = b_id * s * h * q_split_arg + s_id * h * q_split_arg;\n    offset_block_dst_k = b_id * s * h * k_split_arg + s_id * h * k_split_arg;\n    offset_block_dst_v = b_id * s * h * v_split_arg + s_id * h * v_split_arg;\n  }\n\n  int q_limit = q_split_arg;\n  int k_limit = q_limit + k_split_arg;\n  int s_id_for_freqs;\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs = s_id + cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs =\n          cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 + s_id - cur_seqlens / 2;\n    }\n  } else {\n    int begin_offset = (start_positions == nullptr) ? 0 : start_positions[b_id];\n    s_id_for_freqs = s_id + begin_offset;\n  }\n  fused_qkv_rope_block_forward(qkv_input, q_freqs, q_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_q, h, d, d2, 0, total_d, q_split_arg);\n  fused_qkv_rope_block_forward(qkv_input, k_freqs, k_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_k, h, d, d2, q_limit, total_d, k_split_arg);\n  fused_qkv_rope_block_forward(qkv_input, nullptr, v_out, interleaved, s_id_for_freqs, offset_block,\n                               offset_block_dst_v, h, d, d2, k_limit, total_d, v_split_arg);\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_qkv_rope_backward_kernel(\n    const scalar_t *grad_out_q, const scalar_t *grad_out_k, const scalar_t *grad_out_v,\n    const float *q_freqs, const float *k_freqs, scalar_t *qkv_grad,\n    const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size, const int cp_rank,\n    const int s, const int b, const int h, const int d, const int d2, const int q_split_arg,\n    const int k_split_arg, const int v_split_arg) {\n  int s_id = blockIdx.x, b_id = blockIdx.y;\n  int cur_seqlens = s;\n  int offset_block, offset_block_dst_q, offset_block_dst_k, offset_block_dst_v;\n  int total_d = q_split_arg + k_split_arg + v_split_arg;\n  if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    offset_block = s_id * b * h * total_d + b_id * h * total_d;\n    offset_block_dst_q = s_id * b * h * q_split_arg + b_id * h * q_split_arg;\n    offset_block_dst_k = s_id * b * h * k_split_arg + b_id * h * k_split_arg;\n    offset_block_dst_v = s_id * b * h * v_split_arg + b_id * h * v_split_arg;\n  } else {\n    offset_block = b_id * s * h * total_d + s_id * h * total_d;\n    offset_block_dst_q = b_id * s * h * q_split_arg + s_id * h * q_split_arg;\n    offset_block_dst_k = b_id * s * h * k_split_arg + s_id * h * k_split_arg;\n    offset_block_dst_v = b_id * s * h * v_split_arg + s_id * h * v_split_arg;\n  }\n  int q_limit = q_split_arg;\n  int k_limit = q_limit + k_split_arg;\n  int s_id_for_freqs;\n  if (cp_size > 1) {\n    assert(cur_seqlens % 2 == 0);\n    if (s_id < cur_seqlens / 2) {\n      s_id_for_freqs = s_id + cp_rank * cur_seqlens / 2;\n    } else {\n      s_id_for_freqs =\n          cur_seqlens * cp_size - (cp_rank + 1) * cur_seqlens / 2 + s_id - cur_seqlens / 2;\n    }\n  } else {\n    s_id_for_freqs = s_id;\n  }\n  fused_qkv_rope_block_backward(grad_out_q, q_freqs, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_q, h, d, d2, 0, total_d,\n                                q_split_arg);\n  fused_qkv_rope_block_backward(grad_out_k, k_freqs, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_k, h, d, d2, q_limit, total_d,\n                                k_split_arg);\n  fused_qkv_rope_block_backward(grad_out_v, nullptr, qkv_grad, interleaved, s_id_for_freqs,\n                                offset_block, offset_block_dst_v, h, d, d2, k_limit, total_d,\n                                v_split_arg);\n}\n\ntemplate <typename scalar_t>\nvoid fused_rope_forward_launcher(const scalar_t *input, const int *cu_seqlens, const float *freqs,\n                                 const int *start_positions, scalar_t *output,\n                                 const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                 const int cp_size, const int cp_rank, const int s, const int b,\n                                 const int h, const int d, const int d2, const int stride_s_or_t,\n                                 const int stride_b, const int stride_h, const int stride_d,\n                                 hipStream_t stream) {\n  int warps_per_block = h < 16 ? 4 : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 2 * d2 * sizeof(float);  // cos, sin\n  int o_stride_s_or_t, o_stride_b;\n  if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    NVTE_CHECK(cu_seqlens != nullptr, \"cu_seqlens is required for THD format\");\n    o_stride_s_or_t = h * d;\n    o_stride_b = 0;\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    o_stride_s_or_t = b * h * d;\n    o_stride_b = h * d;\n  } else {\n    o_stride_s_or_t = h * d;\n    o_stride_b = s * h * d;\n  }\n  const int o_stride_h = d;\n  const int o_stride_d = 1;\n\n  fused_rope_forward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      input, cu_seqlens, freqs, start_positions, output, interleaved, cp_size, cp_rank, s, h, d, d2,\n      stride_s_or_t, stride_b, stride_h, stride_d, o_stride_s_or_t, o_stride_b, o_stride_h,\n      o_stride_d);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_rope_backward_launcher(const scalar_t *output_grads, const int *cu_seqlens,\n                                  const float *freqs, const int *start_positions,\n                                  scalar_t *input_grads, const NVTE_QKV_Format qkv_format,\n                                  const bool interleaved, const int cp_size, const int cp_rank,\n                                  const int s, const int b, const int h, const int d, const int d2,\n                                  const int stride_s_or_t, const int stride_b, const int stride_h,\n                                  const int stride_d, hipStream_t stream) {\n  int warps_per_block = h < 16 ? 4 : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 2 * d2 * sizeof(float);  // cos, sin\n  int o_stride_s_or_t, o_stride_b;\n  if (qkv_format == NVTE_QKV_Format::NVTE_THD) {\n    NVTE_CHECK(cu_seqlens != nullptr, \"cu_seqlens is required for THD format\");\n    o_stride_s_or_t = h * d;\n    o_stride_b = 0;\n  } else if (qkv_format == NVTE_QKV_Format::NVTE_SBHD) {\n    o_stride_s_or_t = b * h * d;\n    o_stride_b = h * d;\n  } else {\n    o_stride_s_or_t = h * d;\n    o_stride_b = s * h * d;\n  }\n  const int o_stride_h = d;\n  const int o_stride_d = 1;\n\n  fused_rope_backward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      output_grads, cu_seqlens, freqs, start_positions, input_grads, interleaved, cp_size, cp_rank,\n      s, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, o_stride_s_or_t, o_stride_b,\n      o_stride_h, o_stride_d);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_qkv_rope_forward_launcher(const scalar_t *qkv_input, const float *q_freqs,\n                                     const float *k_freqs, const int *start_positions,\n                                     scalar_t *q_out, scalar_t *k_out, scalar_t *v_out,\n                                     const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                     const int cp_size, const int cp_rank, const int s, const int b,\n                                     const int h, const int d, const int d2,\n                                     const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                     const int qkv_split_arg_list_2, hipStream_t stream) {\n  const int THREADS_PER_WARP = 32;\n  int warps_per_block = (h <= 8) ? h : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 4 * d2 * sizeof(float);  // cos, sin * q ,k\n\n  fused_qkv_rope_forward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      qkv_input, q_freqs, k_freqs, start_positions, q_out, k_out, v_out, qkv_format, interleaved,\n      cp_size, cp_rank, s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n      qkv_split_arg_list_2);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename scalar_t>\nvoid fused_qkv_rope_backward_launcher(const scalar_t *q_grad_out, const scalar_t *k_grad_out,\n                                      const scalar_t *v_grad_out, const float *q_freqs,\n                                      const float *k_freqs, scalar_t *qkv_grad_input,\n                                      const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                      const int cp_size, const int cp_rank, const int s,\n                                      const int b, const int h, const int d, const int d2,\n                                      const int qkv_split_arg_list_0,\n                                      const int qkv_split_arg_list_1,\n                                      const int qkv_split_arg_list_2, hipStream_t stream) {\n  const int THREADS_PER_WARP = 32;\n  const int warps_per_block = (h <= 8) ? h : 8;\n  dim3 blocks(s, b);\n  dim3 threads(THREADS_PER_WARP, warps_per_block);\n  const int shared_mem_size = 4 * d2 * sizeof(float);  // cos, sin * q ,k\n\n  fused_qkv_rope_backward_kernel<<<blocks, threads, shared_mem_size, stream>>>(\n      q_grad_out, k_grad_out, v_grad_out, q_freqs, k_freqs, qkv_grad_input, qkv_format, interleaved,\n      cp_size, cp_rank, s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n      qkv_split_arg_list_2);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fused_rope_forward(const Tensor &input, const Tensor &cu_seqlens, const Tensor &freqs,\n                        const Tensor &start_positions, Tensor *output,\n                        const NVTE_QKV_Format qkv_format, const bool interleaved, const int cp_size,\n                        const int cp_rank, const int s, const int b, const int h, const int d,\n                        const int d2, const int stride_s_or_t, const int stride_b,\n                        const int stride_h, const int stride_d, hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.data.dtype, scalar_t,\n      fused_rope_forward_launcher(reinterpret_cast<const scalar_t *>(input.data.dptr),\n                                  reinterpret_cast<const int *>(cu_seqlens.data.dptr),\n                                  reinterpret_cast<const float *>(freqs.data.dptr),\n                                  reinterpret_cast<const int *>(start_positions.data.dptr),\n                                  reinterpret_cast<scalar_t *>(output->data.dptr), qkv_format,\n                                  interleaved, cp_size, cp_rank, s, b, h, d, d2, stride_s_or_t,\n                                  stride_b, stride_h, stride_d, stream););\n}\n\nvoid fused_rope_backward(const Tensor &output_grads, const Tensor &cu_seqlens, const Tensor &freqs,\n                         const Tensor &start_positions, Tensor *input_grads,\n                         const NVTE_QKV_Format qkv_format, const bool interleaved,\n                         const int cp_size, const int cp_rank, const int s, const int b,\n                         const int h, const int d, const int d2, const int stride_s_or_t,\n                         const int stride_b, const int stride_h, const int stride_d,\n                         hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      output_grads.data.dtype, scalar_t,\n      fused_rope_backward_launcher(reinterpret_cast<const scalar_t *>(output_grads.data.dptr),\n                                   reinterpret_cast<const int *>(cu_seqlens.data.dptr),\n                                   reinterpret_cast<const float *>(freqs.data.dptr),\n                                   reinterpret_cast<const int *>(start_positions.data.dptr),\n                                   reinterpret_cast<scalar_t *>(input_grads->data.dptr), qkv_format,\n                                   interleaved, cp_size, cp_rank, s, b, h, d, d2, stride_s_or_t,\n                                   stride_b, stride_h, stride_d, stream););\n}\n\nvoid fused_qkv_rope_forward(const Tensor &qkv_input, const Tensor &q_freqs, const Tensor &k_freqs,\n                            const Tensor &start_positions, Tensor *q_out, Tensor *k_out,\n                            Tensor *v_out, const NVTE_QKV_Format qkv_format, const bool interleaved,\n                            const int cp_size, const int cp_rank, const int s, const int b,\n                            const int h, const int d, const int d2, const int qkv_split_arg_list_0,\n                            const int qkv_split_arg_list_1, const int qkv_split_arg_list_2,\n                            hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      qkv_input.data.dtype, scalar_t,\n      fused_qkv_rope_forward_launcher(reinterpret_cast<const scalar_t *>(qkv_input.data.dptr),\n                                      reinterpret_cast<const float *>(q_freqs.data.dptr),\n                                      reinterpret_cast<const float *>(k_freqs.data.dptr),\n                                      reinterpret_cast<const int *>(start_positions.data.dptr),\n                                      reinterpret_cast<scalar_t *>(q_out->data.dptr),\n                                      reinterpret_cast<scalar_t *>(k_out->data.dptr),\n                                      reinterpret_cast<scalar_t *>(v_out->data.dptr), qkv_format,\n                                      interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                                      qkv_split_arg_list_0, qkv_split_arg_list_1,\n                                      qkv_split_arg_list_2, stream););\n}\n\nvoid fused_qkv_rope_backward(const Tensor &q_grad_out, const Tensor &k_grad_out,\n                             const Tensor &v_grad_out, const Tensor &q_freqs, const Tensor &k_freqs,\n                             Tensor *qkv_grad_input, const NVTE_QKV_Format qkv_format,\n                             const bool interleaved, const int cp_size, const int cp_rank,\n                             const int s, const int b, const int h, const int d, const int d2,\n                             const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                             const int qkv_split_arg_list_2, hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      q_grad_out.data.dtype, scalar_t,\n      fused_qkv_rope_backward_launcher(reinterpret_cast<const scalar_t *>(q_grad_out.data.dptr),\n                                       reinterpret_cast<const scalar_t *>(k_grad_out.data.dptr),\n                                       reinterpret_cast<const scalar_t *>(v_grad_out.data.dptr),\n                                       reinterpret_cast<const float *>(q_freqs.data.dptr),\n                                       reinterpret_cast<const float *>(k_freqs.data.dptr),\n                                       reinterpret_cast<scalar_t *>(qkv_grad_input->data.dptr),\n                                       qkv_format, interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                                       qkv_split_arg_list_0, qkv_split_arg_list_1,\n                                       qkv_split_arg_list_2, stream););\n}\n}  // end namespace transformer_engine\n\nvoid nvte_fused_rope_forward(const NVTETensor input, const NVTETensor cu_seqlens,\n                             const NVTETensor freqs, const NVTETensor start_positions,\n                             NVTETensor output, const NVTE_QKV_Format qkv_format,\n                             const bool interleaved, const int cp_size, const int cp_rank,\n                             const int s, const int b, const int h, const int d, const int d2,\n                             const int stride_s_or_t, const int stride_b, const int stride_h,\n                             const int stride_d, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_rope_forward);\n  using namespace transformer_engine;\n  fused_rope_forward(*convertNVTETensorCheck(input), *convertNVTETensorCheck(cu_seqlens),\n                     *convertNVTETensorCheck(freqs), *convertNVTETensorCheck(start_positions),\n                     convertNVTETensorCheck(output), qkv_format, interleaved, cp_size, cp_rank, s,\n                     b, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, stream);\n}\n\nvoid nvte_fused_rope_backward(const NVTETensor output_grads, const NVTETensor cu_seqlens,\n                              const NVTETensor freqs, const NVTETensor start_positions,\n                              NVTETensor input_grads, const NVTE_QKV_Format qkv_format,\n                              const bool interleaved, const int cp_size, const int cp_rank,\n                              const int s, const int b, const int h, const int d, const int d2,\n                              const int stride_s_or_t, const int stride_b, const int stride_h,\n                              const int stride_d, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_rope_backward);\n  using namespace transformer_engine;\n  fused_rope_backward(*convertNVTETensorCheck(output_grads), *convertNVTETensorCheck(cu_seqlens),\n                      *convertNVTETensorCheck(freqs), *convertNVTETensorCheck(start_positions),\n                      convertNVTETensorCheck(input_grads), qkv_format, interleaved, cp_size,\n                      cp_rank, s, b, h, d, d2, stride_s_or_t, stride_b, stride_h, stride_d, stream);\n}\n\nvoid nvte_fused_qkv_rope_forward(const NVTETensor qkv_input, const NVTETensor q_freqs,\n                                 const NVTETensor k_freqs, const NVTETensor start_positions,\n                                 NVTETensor q_out, NVTETensor k_out, NVTETensor v_out,\n                                 const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                 const int cp_size, const int cp_rank, const int s, const int b,\n                                 const int h, const int d, const int d2,\n                                 const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                 const int qkv_split_arg_list_2, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_qkv_rope_forward);\n  using namespace transformer_engine;\n  fused_qkv_rope_forward(*convertNVTETensorCheck(qkv_input), *convertNVTETensorCheck(q_freqs),\n                         *convertNVTETensorCheck(k_freqs), *convertNVTETensorCheck(start_positions),\n                         convertNVTETensorCheck(q_out), convertNVTETensorCheck(k_out),\n                         convertNVTETensorCheck(v_out), qkv_format, interleaved, cp_size, cp_rank,\n                         s, b, h, d, d2, qkv_split_arg_list_0, qkv_split_arg_list_1,\n                         qkv_split_arg_list_2, stream);\n}\n\nvoid nvte_fused_qkv_rope_backward(const NVTETensor q_grad_out, const NVTETensor k_grad_out,\n                                  const NVTETensor v_grad_out, const NVTETensor q_freqs,\n                                  const NVTETensor k_freqs, NVTETensor qkv_grad_input,\n                                  const NVTE_QKV_Format qkv_format, const bool interleaved,\n                                  const int cp_size, const int cp_rank, const int s, const int b,\n                                  const int h, const int d, const int d2,\n                                  const int qkv_split_arg_list_0, const int qkv_split_arg_list_1,\n                                  const int qkv_split_arg_list_2, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fused_qkv_rope_backward);\n  using namespace transformer_engine;\n  fused_qkv_rope_backward(*convertNVTETensorCheck(q_grad_out), *convertNVTETensorCheck(k_grad_out),\n                          *convertNVTETensorCheck(v_grad_out), *convertNVTETensorCheck(q_freqs),\n                          *convertNVTETensorCheck(k_freqs), convertNVTETensorCheck(qkv_grad_input),\n                          qkv_format, interleaved, cp_size, cp_rank, s, b, h, d, d2,\n                          qkv_split_arg_list_0, qkv_split_arg_list_1, qkv_split_arg_list_2, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/fused_rope/fused_rope.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/permutation.h>\n\n#include <cub/cub.cuh>\n\n#include \"../common.h\"\n\nstatic __global__ void moe_permute_row_map(const int *sorted_row_id, int *row_id_map,\n                                           const int num_rows, const int topK,\n                                           const int num_out_tokens) {\n  // Each block corresponds to one source token\n  // row_id_map[topK][num_rows]\n  const int bid = blockIdx.x;\n  const int tid = threadIdx.x;\n  const int idx = bid * blockDim.x + tid;\n\n  if (idx >= num_rows * topK) return;\n\n  int source_row = sorted_row_id[idx];\n  int source_token_id = source_row / topK;\n  int source_topK_id = source_row % topK;\n\n  if (idx >= num_out_tokens) {\n    // Set the indices of dropped tokens to -1\n    row_id_map[source_topK_id * num_rows + source_token_id] = -1;\n  } else {\n    // Create a row id map for subsequent unpermute operation\n    row_id_map[source_topK_id * num_rows + source_token_id] = idx;\n  }\n}\n\ntemplate <typename T, typename TCompute, bool hasProb>\n__global__ void moe_unpermute_kernel(const T *input, T *unpermuted_output, const int *row_id_map,\n                                     const float *prob, const int num_rows, const int topK,\n                                     const int num_cols) {\n  extern __shared__ int8_t s_mem[];\n  TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);\n\n  // Each block corresponds to one dest token\n  const int source_token = blockIdx.x;\n  const int tid = threadIdx.x;\n\n  if (hasProb) {\n    for (int i = tid; i < topK; i += blockDim.x * blockDim.y) {\n      // Load all the topK probs related to the source row into smem\n      s_prob[i] = TCompute(prob[source_token * topK + i]);\n    }\n    __syncthreads();\n  }\n\n  // Register buffers for vector type (float4) memory access\n  float4 frag_load_store;\n  T *frag_load_store_ptr = reinterpret_cast<T *>(&frag_load_store);\n\n  // Number of elemments in frag_load_store\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  // Traverse along the hidden dimention\n  for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess) {\n    TCompute frag_elem[kElementsPerAccess];\n    TCompute frag_sum[kElementsPerAccess];\n\n    int source_row = row_id_map[source_token];\n\n    // source_row == -1 represents a dropped token\n    if (source_row != -1) {\n      const T *source_row_ptr = input + source_row * num_cols;\n\n      frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = TCompute(frag_load_store_ptr[e]);\n      }\n\n      if (hasProb) {\n        for (int e = 0; e < kElementsPerAccess; e++) {\n          frag_sum[e] = frag_sum[e] * s_prob[0];\n        }\n      }\n    } else {\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = TCompute(0.0f);\n      }\n    }\n\n    for (int k = 1; k < topK; k++) {\n      source_row = row_id_map[k * num_rows + source_token];\n\n      if (source_row == -1) continue;\n\n      const T *source_row_ptr = input + source_row * num_cols;\n\n      frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_elem[e] = TCompute(frag_load_store_ptr[e]);\n      }\n\n      if (hasProb) {\n        for (int e = 0; e < kElementsPerAccess; e++) {\n          frag_elem[e] = frag_elem[e] * s_prob[k];\n        }\n      }\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = frag_sum[e] + frag_elem[e];\n      }\n    }\n\n    T *dest_row_ptr = unpermuted_output + source_token * num_cols;\n\n    for (int e = 0; e < kElementsPerAccess; e++) {\n      if constexpr ((std::is_same_v<T, __nv_fp8_e4m3> || std::is_same_v<T, __nv_fp8_e5m2>) &&\n                    (!hasProb)) {\n        frag_sum[e] = frag_sum[e] / TCompute(topK);\n      }\n      frag_load_store_ptr[e] = T(frag_sum[e]);\n    }\n\n    *reinterpret_cast<float4 *>(dest_row_ptr + i) = frag_load_store;\n  }\n}\n\ntemplate <typename T, typename TCompute, int topKTile, bool hasProb>\n__global__ void moe_permute_kernel(const T *input_bwd, const T *input_fwd, T *act_grad,\n                                   const float *prob, float *prob_grad, const int *row_id_map,\n                                   const int num_rows, const int topK, const int num_cols) {\n  extern __shared__ int8_t s_mem[];\n  TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);\n\n  // Each block corresponds to one source token\n  const int source_token = blockIdx.x;\n  const int tid = threadIdx.x;\n\n  if (hasProb) {\n    for (int i = tid; i < topK; i += blockDim.x) {\n      // Load all the topK probs related to the source row into smem\n      s_prob[i] = TCompute(prob[source_token * topK + i]);\n    }\n    __syncthreads();\n  }\n\n  // Accumulators for the calculation of prob_grad\n  float accum[topKTile] = {0.0f};\n\n  // Register buffers for vector type (float4) memory access\n  float4 frag_load_store;\n  T *frag_load_store_ptr = reinterpret_cast<T *>(&frag_load_store);\n\n  // Number of elemments in frag_load_store\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  // The starting address of each source row\n  const T *source_row_ptr = input_bwd + source_token * num_cols;\n\n  // Traverse along the hidden dimention\n  for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess) {\n    TCompute frag_src[kElementsPerAccess];\n\n    frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n    for (int e = 0; e < kElementsPerAccess; e++) frag_src[e] = TCompute(frag_load_store_ptr[e]);\n\n    int index = source_token;\n\n    // Process each row in the corresponding topK rows\n    for (int k = 0; k < topKTile; k++) {\n      if (k == topK) break;\n\n      int dest_row = row_id_map[index];\n      index += num_rows;\n\n      if (dest_row != -1) {\n        if (hasProb) {\n          // Calculate act_grad in unpermute bwd\n          for (int e = 0; e < kElementsPerAccess; e++)\n            frag_load_store_ptr[e] = T(frag_src[e] * s_prob[k]);\n        } else {\n          // permute fwd\n          for (int e = 0; e < kElementsPerAccess; e++) frag_load_store_ptr[e] = T(frag_src[e]);\n        }\n\n        T *dest_row_ptr = act_grad + dest_row * num_cols;\n        *reinterpret_cast<float4 *>(dest_row_ptr + i) = frag_load_store;\n\n        if (hasProb) {\n          // Inner product calculation for prob_grad in unpermute bwd\n          const T *input_fwd_ptr = input_fwd + dest_row * num_cols;\n\n          frag_load_store = __ldlu(reinterpret_cast<const float4 *>(input_fwd_ptr + i));\n\n          TCompute frag_input_fwd[kElementsPerAccess];\n          for (int e = 0; e < kElementsPerAccess; e++)\n            frag_input_fwd[e] = TCompute(frag_load_store_ptr[e]);\n\n          for (int e = 0; e < kElementsPerAccess; e++) {\n            accum[k] += static_cast<float>(frag_src[e] * frag_input_fwd[e]);\n          }\n        }\n      }\n    }\n  }\n\n  if (hasProb) {\n    for (int k = 0; k < topKTile; k++) {\n      if (k == topK) break;\n      // Warp-level reduction\n      for (int mask = 16; mask > 0; mask /= 2) {\n        accum[k] = accum[k] + __shfl_xor_sync(0xffffffff, accum[k], mask, 32);\n      }\n    }\n\n    if (tid == 0) {\n      for (int k = 0; k < topKTile; k++) {\n        if (k == topK) break;\n        prob_grad[source_token * topK + k] = accum[k];\n      }\n    }\n  }\n}\n\ntemplate <typename T>\nvoid nvte_permute_launcher(const T *input, T *output, const int *sorted_row_id, int *row_id_map,\n                           const float *prob, float *prob_grad, const T *input_fwd,\n                           const int num_rows, const int topK, const int num_cols,\n                           const int num_out_tokens, cudaStream_t stream) {\n  using TCompute = typename std::conditional<(std::is_same<T, __nv_fp8_e5m2>::value ||\n                                              std::is_same<T, __nv_fp8_e4m3>::value),\n                                             half, T>::type;\n\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  if (input_fwd == nullptr) {\n    // moe_permute_fwd\n\n    int threads = 64;\n    int blocks = (num_rows * topK + threads - 1) / threads;\n\n    moe_permute_row_map<<<blocks, threads, 0, stream>>>(sorted_row_id, row_id_map, num_rows, topK,\n                                                        num_out_tokens);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n\n    blocks = num_rows;\n    threads = std::min(num_cols / kElementsPerAccess, 1024);\n    moe_permute_kernel<T, TCompute, 128, false><<<blocks, threads, 0, stream>>>(\n        input, nullptr, output, nullptr, nullptr, row_id_map, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    // moe_unpermute_bwd\n\n    int threads = 32;\n    int blocks = num_rows;\n\n    if (prob == nullptr) {\n      // moe_unpermute_bwd without probs\n\n      moe_permute_kernel<T, TCompute, 1, false><<<blocks, threads, 0, stream>>>(\n          input, input_fwd, output, nullptr, nullptr, row_id_map, num_rows, topK, num_cols);\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    } else {\n      // moe_unpermute_bwd with probs\n\n      size_t smem_bytes = topK * sizeof(TCompute);\n\n      if (topK <= 8) {\n        moe_permute_kernel<T, TCompute, 8, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 16) {\n        moe_permute_kernel<T, TCompute, 16, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 32) {\n        moe_permute_kernel<T, TCompute, 32, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 64) {\n        moe_permute_kernel<T, TCompute, 64, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 128) {\n        moe_permute_kernel<T, TCompute, 128, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else {\n        NVTE_ERROR(\"topK cannot exceed 128.\");\n      }\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    }\n  }\n}\n\ntemplate <typename T>\nvoid nvte_unpermute_launcher(const T *input, T *output, int *row_id_map, const float *prob,\n                             const int num_rows, const int topK, const int num_cols,\n                             cudaStream_t stream) {\n  using TCompute = typename std::conditional<(std::is_same<T, __nv_fp8_e5m2>::value ||\n                                              std::is_same<T, __nv_fp8_e4m3>::value),\n                                             half, T>::type;\n\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  int blocks = num_rows;\n  int threads = std::min(num_cols / kElementsPerAccess, 1024);\n  size_t smem_bytes = topK * sizeof(TCompute);\n\n  if (prob == nullptr) {\n    // moe_permute_bwd\n    // moe_unpermute_fwd without probs\n\n    moe_unpermute_kernel<T, TCompute, false><<<blocks, threads, smem_bytes, stream>>>(\n        input, output, row_id_map, nullptr, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    // moe_unpermute_fwd with probs\n\n    moe_unpermute_kernel<T, TCompute, true><<<blocks, threads, smem_bytes, stream>>>(\n        input, output, row_id_map, prob, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid nvte_permute(const NVTETensor input, NVTETensor output, const NVTETensor sorted_row_id,\n                  NVTETensor row_id_map, const NVTETensor prob, NVTETensor prob_grad,\n                  const NVTETensor input_fwd, const int num_rows, const int topK,\n                  const int num_cols, const int num_out_tokens, cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_API_CALL(nvte_permute);\n\n  const Tensor *input_cu = convertNVTETensorCheck(input);\n  const Tensor *output_cu = convertNVTETensorCheck(output);\n  const Tensor *sorted_row_id_cu = convertNVTETensorCheck(sorted_row_id);\n  const Tensor *row_id_map_cu = convertNVTETensorCheck(row_id_map);\n  const Tensor *prob_cu = convertNVTETensorCheck(prob);\n  const Tensor *prob_grad_cu = convertNVTETensorCheck(prob_grad);\n  const Tensor *input_fwd_cu = convertNVTETensorCheck(input_fwd);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input_cu->data.dtype, T,\n      nvte_permute_launcher(reinterpret_cast<const T *>(input_cu->data.dptr),\n                            reinterpret_cast<T *>(output_cu->data.dptr),\n                            reinterpret_cast<const int *>(sorted_row_id_cu->data.dptr),\n                            reinterpret_cast<int *>(row_id_map_cu->data.dptr),\n                            reinterpret_cast<const float *>(prob_cu->data.dptr),\n                            reinterpret_cast<float *>(prob_grad_cu->data.dptr),\n                            reinterpret_cast<const T *>(input_fwd_cu->data.dptr), num_rows, topK,\n                            num_cols, num_out_tokens, stream););\n}\n\nvoid nvte_unpermute(const NVTETensor input, NVTETensor output, NVTETensor row_id_map,\n                    const NVTETensor prob, const int num_rows, const int topK, const int num_cols,\n                    cudaStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_API_CALL(nvte_unpermute);\n\n  const Tensor *input_cu = convertNVTETensorCheck(input);\n  const Tensor *output_cu = convertNVTETensorCheck(output);\n  const Tensor *row_id_map_cu = convertNVTETensorCheck(row_id_map);\n  const Tensor *prob_cu = convertNVTETensorCheck(prob);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input_cu->data.dtype, T,\n      nvte_unpermute_launcher(reinterpret_cast<const T *>(input_cu->data.dptr),\n                              reinterpret_cast<T *>(output_cu->data.dptr),\n                              reinterpret_cast<int *>(row_id_map_cu->data.dptr),\n                              reinterpret_cast<const float *>(prob_cu->data.dptr), num_rows, topK,\n                              num_cols, stream););\n}\n\nvoid nvte_device_radix_sort_pairs(void *temp_storage, size_t *temp_storage_bytes, int *keys_in,\n                                  int *keys_out, int *values_in, int *values_out,\n                                  size_t num_items) {\n  NVTE_API_CALL(nvte_device_radix_sort_pairs);\n  cub::DeviceRadixSort::SortPairs(temp_storage, *temp_storage_bytes, keys_in, keys_out, values_in,\n                                  values_out, num_items);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/permutation.h>\n\n#include <hipcub/hipcub.hpp>\n\n#include \"../common.h\"\n\nstatic __global__ void moe_permute_row_map(const int *sorted_row_id, int *row_id_map,\n                                           const int num_rows, const int topK,\n                                           const int num_out_tokens) {\n  // Each block corresponds to one source token\n  // row_id_map[topK][num_rows]\n  const int bid = blockIdx.x;\n  const int tid = threadIdx.x;\n  const int idx = bid * blockDim.x + tid;\n\n  if (idx >= num_rows * topK) return;\n\n  int source_row = sorted_row_id[idx];\n  int source_token_id = source_row / topK;\n  int source_topK_id = source_row % topK;\n\n  if (idx >= num_out_tokens) {\n    // Set the indices of dropped tokens to -1\n    row_id_map[source_topK_id * num_rows + source_token_id] = -1;\n  } else {\n    // Create a row id map for subsequent unpermute operation\n    row_id_map[source_topK_id * num_rows + source_token_id] = idx;\n  }\n}\n\ntemplate <typename T, typename TCompute, bool hasProb>\n__global__ void moe_unpermute_kernel(const T *input, T *unpermuted_output, const int *row_id_map,\n                                     const float *prob, const int num_rows, const int topK,\n                                     const int num_cols) {\n  extern __shared__ int8_t s_mem[];\n  TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);\n\n  // Each block corresponds to one dest token\n  const int source_token = blockIdx.x;\n  const int tid = threadIdx.x;\n\n  if (hasProb) {\n    for (int i = tid; i < topK; i += blockDim.x * blockDim.y) {\n      // Load all the topK probs related to the source row into smem\n      s_prob[i] = TCompute(prob[source_token * topK + i]);\n    }\n    __syncthreads();\n  }\n\n  // Register buffers for vector type (float4) memory access\n  float4 frag_load_store;\n  T *frag_load_store_ptr = reinterpret_cast<T *>(&frag_load_store);\n\n  // Number of elemments in frag_load_store\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  // Traverse along the hidden dimention\n  for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess) {\n    TCompute frag_elem[kElementsPerAccess];\n    TCompute frag_sum[kElementsPerAccess];\n\n    int source_row = row_id_map[source_token];\n\n    // source_row == -1 represents a dropped token\n    if (source_row != -1) {\n      const T *source_row_ptr = input + source_row * num_cols;\n\n      frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = TCompute(frag_load_store_ptr[e]);\n      }\n\n      if (hasProb) {\n        for (int e = 0; e < kElementsPerAccess; e++) {\n          frag_sum[e] = frag_sum[e] * s_prob[0];\n        }\n      }\n    } else {\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = TCompute(0.0f);\n      }\n    }\n\n    for (int k = 1; k < topK; k++) {\n      source_row = row_id_map[k * num_rows + source_token];\n\n      if (source_row == -1) continue;\n\n      const T *source_row_ptr = input + source_row * num_cols;\n\n      frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_elem[e] = TCompute(frag_load_store_ptr[e]);\n      }\n\n      if (hasProb) {\n        for (int e = 0; e < kElementsPerAccess; e++) {\n          frag_elem[e] = frag_elem[e] * s_prob[k];\n        }\n      }\n\n      for (int e = 0; e < kElementsPerAccess; e++) {\n        frag_sum[e] = frag_sum[e] + frag_elem[e];\n      }\n    }\n\n    T *dest_row_ptr = unpermuted_output + source_token * num_cols;\n\n    for (int e = 0; e < kElementsPerAccess; e++) {\n      if constexpr ((std::is_same_v<T, __hip_fp8_e4m3_fnuz> || std::is_same_v<T, __hip_fp8_e5m2_fnuz>) &&\n                    (!hasProb)) {\n        frag_sum[e] = frag_sum[e] / TCompute(topK);\n      }\n      frag_load_store_ptr[e] = T(frag_sum[e]);\n    }\n\n    *reinterpret_cast<float4 *>(dest_row_ptr + i) = frag_load_store;\n  }\n}\n\ntemplate <typename T, typename TCompute, int topKTile, bool hasProb>\n__global__ void moe_permute_kernel(const T *input_bwd, const T *input_fwd, T *act_grad,\n                                   const float *prob, float *prob_grad, const int *row_id_map,\n                                   const int num_rows, const int topK, const int num_cols) {\n  extern __shared__ int8_t s_mem[];\n  TCompute *s_prob = reinterpret_cast<TCompute *>(s_mem);\n\n  // Each block corresponds to one source token\n  const int source_token = blockIdx.x;\n  const int tid = threadIdx.x;\n\n  if (hasProb) {\n    for (int i = tid; i < topK; i += blockDim.x) {\n      // Load all the topK probs related to the source row into smem\n      s_prob[i] = TCompute(prob[source_token * topK + i]);\n    }\n    __syncthreads();\n  }\n\n  // Accumulators for the calculation of prob_grad\n  float accum[topKTile] = {0.0f};\n\n  // Register buffers for vector type (float4) memory access\n  float4 frag_load_store;\n  T *frag_load_store_ptr = reinterpret_cast<T *>(&frag_load_store);\n\n  // Number of elemments in frag_load_store\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  // The starting address of each source row\n  const T *source_row_ptr = input_bwd + source_token * num_cols;\n\n  // Traverse along the hidden dimention\n  for (int i = tid * kElementsPerAccess; i < num_cols; i += blockDim.x * kElementsPerAccess) {\n    TCompute frag_src[kElementsPerAccess];\n\n    frag_load_store = __ldlu(reinterpret_cast<const float4 *>(source_row_ptr + i));\n\n    for (int e = 0; e < kElementsPerAccess; e++) frag_src[e] = TCompute(frag_load_store_ptr[e]);\n\n    int index = source_token;\n\n    // Process each row in the corresponding topK rows\n    for (int k = 0; k < topKTile; k++) {\n      if (k == topK) break;\n\n      int dest_row = row_id_map[index];\n      index += num_rows;\n\n      if (dest_row != -1) {\n        if (hasProb) {\n          // Calculate act_grad in unpermute bwd\n          for (int e = 0; e < kElementsPerAccess; e++)\n            frag_load_store_ptr[e] = T(frag_src[e] * s_prob[k]);\n        } else {\n          // permute fwd\n          for (int e = 0; e < kElementsPerAccess; e++) frag_load_store_ptr[e] = T(frag_src[e]);\n        }\n\n        T *dest_row_ptr = act_grad + dest_row * num_cols;\n        *reinterpret_cast<float4 *>(dest_row_ptr + i) = frag_load_store;\n\n        if (hasProb) {\n          // Inner product calculation for prob_grad in unpermute bwd\n          const T *input_fwd_ptr = input_fwd + dest_row * num_cols;\n\n          frag_load_store = __ldlu(reinterpret_cast<const float4 *>(input_fwd_ptr + i));\n\n          TCompute frag_input_fwd[kElementsPerAccess];\n          for (int e = 0; e < kElementsPerAccess; e++)\n            frag_input_fwd[e] = TCompute(frag_load_store_ptr[e]);\n\n          for (int e = 0; e < kElementsPerAccess; e++) {\n            accum[k] += static_cast<float>(frag_src[e] * frag_input_fwd[e]);\n          }\n        }\n      }\n    }\n  }\n\n  if (hasProb) {\n    for (int k = 0; k < topKTile; k++) {\n      if (k == topK) break;\n      // Warp-level reduction\n      for (int mask = 16; mask > 0; mask /= 2) {\n        accum[k] = accum[k] + __shfl_xor_sync(0xffffffff, accum[k], mask, 32);\n      }\n    }\n\n    if (tid == 0) {\n      for (int k = 0; k < topKTile; k++) {\n        if (k == topK) break;\n        prob_grad[source_token * topK + k] = accum[k];\n      }\n    }\n  }\n}\n\ntemplate <typename T>\nvoid nvte_permute_launcher(const T *input, T *output, const int *sorted_row_id, int *row_id_map,\n                           const float *prob, float *prob_grad, const T *input_fwd,\n                           const int num_rows, const int topK, const int num_cols,\n                           const int num_out_tokens, hipStream_t stream) {\n  using TCompute = typename std::conditional<(std::is_same<T, __hip_fp8_e5m2_fnuz>::value ||\n                                              std::is_same<T, __hip_fp8_e4m3_fnuz>::value),\n                                             half, T>::type;\n\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  if (input_fwd == nullptr) {\n    // moe_permute_fwd\n\n    int threads = 64;\n    int blocks = (num_rows * topK + threads - 1) / threads;\n\n    moe_permute_row_map<<<blocks, threads, 0, stream>>>(sorted_row_id, row_id_map, num_rows, topK,\n                                                        num_out_tokens);\n    NVTE_CHECK_CUDA(hipGetLastError());\n\n    blocks = num_rows;\n    threads = std::min(num_cols / kElementsPerAccess, 1024);\n    moe_permute_kernel<T, TCompute, 128, false><<<blocks, threads, 0, stream>>>(\n        input, nullptr, output, nullptr, nullptr, row_id_map, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    // moe_unpermute_bwd\n\n    int threads = 32;\n    int blocks = num_rows;\n\n    if (prob == nullptr) {\n      // moe_unpermute_bwd without probs\n\n      moe_permute_kernel<T, TCompute, 1, false><<<blocks, threads, 0, stream>>>(\n          input, input_fwd, output, nullptr, nullptr, row_id_map, num_rows, topK, num_cols);\n      NVTE_CHECK_CUDA(hipGetLastError());\n    } else {\n      // moe_unpermute_bwd with probs\n\n      size_t smem_bytes = topK * sizeof(TCompute);\n\n      if (topK <= 8) {\n        moe_permute_kernel<T, TCompute, 8, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 16) {\n        moe_permute_kernel<T, TCompute, 16, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 32) {\n        moe_permute_kernel<T, TCompute, 32, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 64) {\n        moe_permute_kernel<T, TCompute, 64, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else if (topK <= 128) {\n        moe_permute_kernel<T, TCompute, 128, true><<<blocks, threads, smem_bytes, stream>>>(\n            input, input_fwd, output, prob, prob_grad, row_id_map, num_rows, topK, num_cols);\n      } else {\n        NVTE_ERROR(\"topK cannot exceed 128.\");\n      }\n      NVTE_CHECK_CUDA(hipGetLastError());\n    }\n  }\n}\n\ntemplate <typename T>\nvoid nvte_unpermute_launcher(const T *input, T *output, int *row_id_map, const float *prob,\n                             const int num_rows, const int topK, const int num_cols,\n                             hipStream_t stream) {\n  using TCompute = typename std::conditional<(std::is_same<T, __hip_fp8_e5m2_fnuz>::value ||\n                                              std::is_same<T, __hip_fp8_e4m3_fnuz>::value),\n                                             half, T>::type;\n\n  static constexpr int kElementsPerAccess = 16 / sizeof(T);\n\n  int blocks = num_rows;\n  int threads = std::min(num_cols / kElementsPerAccess, 1024);\n  size_t smem_bytes = topK * sizeof(TCompute);\n\n  if (prob == nullptr) {\n    // moe_permute_bwd\n    // moe_unpermute_fwd without probs\n\n    moe_unpermute_kernel<T, TCompute, false><<<blocks, threads, smem_bytes, stream>>>(\n        input, output, row_id_map, nullptr, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    // moe_unpermute_fwd with probs\n\n    moe_unpermute_kernel<T, TCompute, true><<<blocks, threads, smem_bytes, stream>>>(\n        input, output, row_id_map, prob, num_rows, topK, num_cols);\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid nvte_permute(const NVTETensor input, NVTETensor output, const NVTETensor sorted_row_id,\n                  NVTETensor row_id_map, const NVTETensor prob, NVTETensor prob_grad,\n                  const NVTETensor input_fwd, const int num_rows, const int topK,\n                  const int num_cols, const int num_out_tokens, hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_API_CALL(nvte_permute);\n\n  const Tensor *input_cu = convertNVTETensorCheck(input);\n  const Tensor *output_cu = convertNVTETensorCheck(output);\n  const Tensor *sorted_row_id_cu = convertNVTETensorCheck(sorted_row_id);\n  const Tensor *row_id_map_cu = convertNVTETensorCheck(row_id_map);\n  const Tensor *prob_cu = convertNVTETensorCheck(prob);\n  const Tensor *prob_grad_cu = convertNVTETensorCheck(prob_grad);\n  const Tensor *input_fwd_cu = convertNVTETensorCheck(input_fwd);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input_cu->data.dtype, T,\n      nvte_permute_launcher(reinterpret_cast<const T *>(input_cu->data.dptr),\n                            reinterpret_cast<T *>(output_cu->data.dptr),\n                            reinterpret_cast<const int *>(sorted_row_id_cu->data.dptr),\n                            reinterpret_cast<int *>(row_id_map_cu->data.dptr),\n                            reinterpret_cast<const float *>(prob_cu->data.dptr),\n                            reinterpret_cast<float *>(prob_grad_cu->data.dptr),\n                            reinterpret_cast<const T *>(input_fwd_cu->data.dptr), num_rows, topK,\n                            num_cols, num_out_tokens, stream););\n}\n\nvoid nvte_unpermute(const NVTETensor input, NVTETensor output, NVTETensor row_id_map,\n                    const NVTETensor prob, const int num_rows, const int topK, const int num_cols,\n                    hipStream_t stream) {\n  using namespace transformer_engine;\n  NVTE_API_CALL(nvte_unpermute);\n\n  const Tensor *input_cu = convertNVTETensorCheck(input);\n  const Tensor *output_cu = convertNVTETensorCheck(output);\n  const Tensor *row_id_map_cu = convertNVTETensorCheck(row_id_map);\n  const Tensor *prob_cu = convertNVTETensorCheck(prob);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input_cu->data.dtype, T,\n      nvte_unpermute_launcher(reinterpret_cast<const T *>(input_cu->data.dptr),\n                              reinterpret_cast<T *>(output_cu->data.dptr),\n                              reinterpret_cast<int *>(row_id_map_cu->data.dptr),\n                              reinterpret_cast<const float *>(prob_cu->data.dptr), num_rows, topK,\n                              num_cols, stream););\n}\n\nvoid nvte_device_radix_sort_pairs(void *temp_storage, size_t *temp_storage_bytes, int *keys_in,\n                                  int *keys_out, int *values_in, int *values_out,\n                                  size_t num_items) {\n  NVTE_API_CALL(nvte_device_radix_sort_pairs);\n  hipcub::DeviceRadixSort::SortPairs(temp_storage, *temp_storage_bytes, keys_in, keys_out, values_in,\n                                  values_out, num_items);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/permutation/permutation.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cuda_bf16.h>\n#include <nvshmem.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <functional>\n#include <iostream>\n#include <sstream>\n#include <string>\n\n#include \"../util/cuda_driver.h\"\n#include \"../util/logging.h\"\n#include \"nvshmem_waitkernel.h\"\n\n__global__ void __launch_bounds__(1)\n    wait_until_on_stream_and_reset(uint64_t* wait_flag, uint64_t wait_value,\n                                   uint64_t signal_reset) {\n  nvshmem_uint64_wait_until(wait_flag, NVSHMEM_CMP_EQ, wait_value);\n  *wait_flag = signal_reset;\n}\nvoid nvshmem_wait_on_stream(uint64_t* sig_addr, WaitKind wait_kind, cudaStream_t stream) {\n  uint64_t wait_value = 1;\n  uint64_t signal_reset = 0;\n  cudaStream_t cur_stream = stream;\n\n  NVTE_CHECK(wait_kind >= WaitKind::KERNEL_WAIT && wait_kind <= WaitKind::STREAM_WAIT,\n             \"Invalid wait kind: \", static_cast<int>(wait_kind));\n\n  switch (wait_kind) {\n    case WaitKind::KERNEL_WAIT:\n      wait_until_on_stream_and_reset<<<1, 1, 0, cur_stream>>>(sig_addr, wait_value, signal_reset);\n      NVTE_CHECK_CUDA(cudaGetLastError());\n      break;\n    case WaitKind::NVSHMEM_WAIT:\n      nvshmemx_uint64_wait_until_on_stream(sig_addr, NVSHMEM_CMP_EQ, wait_value, cur_stream);\n      NVTE_CHECK_CUDA_DRIVER(cuStreamWriteValue64((CUstream)cur_stream, (CUdeviceptr)sig_addr,\n                                                  (cuuint64_t)signal_reset,\n                                                  CU_STREAM_WRITE_VALUE_DEFAULT));\n      break;\n    case WaitKind::STREAM_WAIT:\n      NVTE_CHECK_CUDA_DRIVER(cuStreamWaitValue64((CUstream)cur_stream, (CUdeviceptr)sig_addr,\n                                                 (cuuint64_t)wait_value, CU_STREAM_WAIT_VALUE_GEQ));\n      NVTE_CHECK_CUDA_DRIVER(cuStreamWriteValue64((CUstream)cur_stream, (CUdeviceptr)sig_addr,\n                                                  (cuuint64_t)signal_reset,\n                                                  CU_STREAM_WRITE_VALUE_DEFAULT));\n      break;\n  }\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_bf16.h>\n#include <nvshmem.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <functional>\n#include <iostream>\n#include <sstream>\n#include <string>\n\n#include \"../util/cuda_driver.h\"\n#include \"../util/logging.h\"\n#include \"nvshmem_waitkernel.h\"\n\n__global__ void __launch_bounds__(1)\n    wait_until_on_stream_and_reset(uint64_t* wait_flag, uint64_t wait_value,\n                                   uint64_t signal_reset) {\n  nvshmem_uint64_wait_until(wait_flag, NVSHMEM_CMP_EQ, wait_value);\n  *wait_flag = signal_reset;\n}\nvoid nvshmem_wait_on_stream(uint64_t* sig_addr, WaitKind wait_kind, hipStream_t stream) {\n  uint64_t wait_value = 1;\n  uint64_t signal_reset = 0;\n  hipStream_t cur_stream = stream;\n\n  NVTE_CHECK(wait_kind >= WaitKind::KERNEL_WAIT && wait_kind <= WaitKind::STREAM_WAIT,\n             \"Invalid wait kind: \", static_cast<int>(wait_kind));\n\n  switch (wait_kind) {\n    case WaitKind::KERNEL_WAIT:\n      wait_until_on_stream_and_reset<<<1, 1, 0, cur_stream>>>(sig_addr, wait_value, signal_reset);\n      NVTE_CHECK_CUDA(hipGetLastError());\n      break;\n    case WaitKind::NVSHMEM_WAIT:\n      nvshmemx_uint64_wait_until_on_stream(sig_addr, NVSHMEM_CMP_EQ, wait_value, cur_stream);\n      NVTE_CHECK_CUDA_DRIVER(hipStreamWriteValue64((hipStream_t)cur_stream, (hipDeviceptr_t)sig_addr,\n                                                  (cuuint64_t)signal_reset,\n                                                  CU_STREAM_WRITE_VALUE_DEFAULT));\n      break;\n    case WaitKind::STREAM_WAIT:\n      NVTE_CHECK_CUDA_DRIVER(hipStreamWaitValue64((hipStream_t)cur_stream, (hipDeviceptr_t)sig_addr,\n                                                 (cuuint64_t)wait_value, hipStreamWaitValueGte));\n      NVTE_CHECK_CUDA_DRIVER(hipStreamWriteValue64((hipStream_t)cur_stream, (hipDeviceptr_t)sig_addr,\n                                                  (cuuint64_t)signal_reset,\n                                                  CU_STREAM_WRITE_VALUE_DEFAULT));\n      break;\n  }\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/nvshmem_api/nvshmem_waitkernel.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cuda_fp8.h>\n#include <cuda_runtime.h>\n\n#if __CUDA_ARCH__ >= 800\n#define half_dtype nv_bfloat16\n#else\n#define half_dtype half\n#endif\n\n#include <assert.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include \"common/util/system.h\"\n#include \"common/util/vectorized_pointwise.h\"\n#include \"userbuffers.h\"\n\n#define MAX_THREADS 1024\n\n#define ATOMIC_CONSUMER(chunk)                                             \\\n  if (counters) {                                                          \\\n    if (threadIdx.x == 0 && blockIdx.x == 0) {                             \\\n      while (0 != (atomicCAS(((unsigned int *)counters) + chunk, 0, 0))) { \\\n      }                                                                    \\\n      ((unsigned int *)counters)[chunk] = 1;                               \\\n      asm volatile(\"fence.sc.gpu;\\n\");                                     \\\n    }                                                                      \\\n    if (blockIdx.x == 0) __syncthreads();                                  \\\n  }\n\n#define ATOMIC_PRODUCER(chunk)             \\\n  if (counters) {                          \\\n    ((unsigned int *)counters)[chunk] = 0; \\\n  }\n\n// Return true if producer > consumer, otherwise false while preventing integer overflow\n// If we expect that producer will be 2B+ messages behind consumer\n#define CHECK_IDS(producer, consumer) (((unsigned)(producer) - (unsigned)(consumer)) & (~INT_MAX))\n\n// Strip the path from a full filename\n#define FILENAME(file)                                      \\\n  ({                                                        \\\n    const char *filename = file;                            \\\n    const char *basename = filename;                        \\\n    for (const char *ptr = filename; *ptr != '\\0'; ptr++) { \\\n      if (*ptr == '/' || *ptr == '\\\\') {                    \\\n        basename = ptr + 1;                                 \\\n      }                                                     \\\n    }                                                       \\\n    basename;                                               \\\n  })\n\n// Printf to provide enough information so it is easier to attribute failures\n#define UB_PRINT(message, ...) \\\n  printf(\"[%s:%s:%d] \" message \"\\n\", FILENAME(__FILE__), __FUNCTION__, __LINE__, __VA_ARGS__)\n\n// Report and error on timeout\n#define CHECK_TIMEOUT(t, timeout) ((clock64() - (t)) > timeout)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rw(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank,\n                 blockIdx.x, threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x); line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      // int dest = (i+myrank+warp)&(RANKS-1);\n      val[i] = userptr[dest[i]][lineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      // int dest = (i+myrank+warp)&(RANKS-1);\n      userptr[dest[i]][lineoffset + line] = sum;\n    }\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce Gather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Volta,Hopper)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d ]Allreduce reduce-scatter:SM %d [%d]: expecting %d got %d\", myrank,\n                 blockIdx.x, threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x); line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][lineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    userptr[myrank][lineoffset + line] = sum;\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce gather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n  __syncthreads();\n\n  for (int line = threadIdx.x + blockDim.x * RANKS * blockIdx.x; line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS - 1];\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      val[i] = userptr[dest[i]][lineoffset + line + blockDim.x * dest[i]];\n    }\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[myrank][lineoffset + line + blockDim.x * dest[i]] = val[i];\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Ampere)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    userptr[myrank][mylineoffset + line] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce-scatter kernel\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    (reinterpret_cast<int4 *>(outbuf))[(line / rowlines) * skiplines + (line % rowlines)] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place)\n\n#if __CUDA_ARCH__ >= 900 && CUDART_VERSION >= 12010\n// All MC kernels here\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        float4 *mc_ptr, const uint64_t ub_timeout) {\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (clock64() - s > ub_timeout) {\n        UB_PRINT(\"Reduce-scatter: SM %d [%d]: expecting %d got %d\", blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n#define UNROLL_MC 8\n  const int loop_step0 = blockDim.x * gridDim.x * RANKS;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x);\n  const int end_elem = max(start_elem, numlines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (lineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (lineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      asm volatile(\"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(\n                       mc_ptr + (lineoffset + line + i * loop_step0)),\n                   \"r\"(val[i].x), \"r\"(val[i].y), \"r\"(val[i].z), \"r\"(val[i].w)\n                   : \"memory\");\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (lineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (lineoffset + line))\n        : \"memory\");\n#endif\n    asm volatile(\n        \"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(mc_ptr + (lineoffset + line)),\n        \"r\"(val.x), \"r\"(val.y), \"r\"(val.z), \"r\"(val.w)\n        : \"memory\");\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (clock64() - s > 2ull * ub_timeout) {\n        UB_PRINT(\"Allgather: SM %d [%d]: expecting %d got %d\", blockIdx.x, threadIdx.x, reduce_id,\n                 *flag);\n        break;\n      }\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Hopper) MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, float4 *mc_ptr,\n                                           const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  uint4 *localptr = reinterpret_cast<uint4 *>(commbuff[myrank * gpustep + firstrank + handleridx]);\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++) localptr[mylineoffset + line + i * loop_step0] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#endif\n    localptr[mylineoffset + line] = val;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce-scatter kernel MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs_oop(const int op, const int flagoffset,\n                                               const int firstrank, const int myrank,\n                                               const int gpustep, const int mylineoffset,\n                                               const int totallines, const int rowlines,\n                                               const int skiplines, void **commbuff,\n                                               const int handleridx, void *outbuf, float4 *mc_ptr,\n                                               const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      (reinterpret_cast<uint4 *>(outbuf))[((line + i * loop_step0) / rowlines) * skiplines +\n                                          ((line + i * loop_step0) % rowlines)] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#endif\n    reinterpret_cast<uint4 *>(outbuf)[(line / rowlines) * skiplines + (line % rowlines)] = val;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16 MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, uint4 *mc_ptr,\n                                           const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  uint4 *localptr = reinterpret_cast<uint4 *>(commbuff[myrank * gpustep + firstrank + handleridx]);\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n  }\n  __syncthreads();\n\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++) val[i] = localptr[mylineoffset + line + i * loop_step0];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      asm volatile(\"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(\n                       mc_ptr + (mylineoffset + line + i * loop_step0)),\n                   \"r\"(val[i].x), \"r\"(val[i].y), \"r\"(val[i].z), \"r\"(val[i].w)\n                   : \"memory\");\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val = localptr[mylineoffset + line];\n    asm volatile(\n        \"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(mc_ptr + (mylineoffset + line)),\n        \"r\"(val.x), \"r\"(val.y), \"r\"(val.z), \"r\"(val.w)\n        : \"memory\");\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace allgather kernel (Hopper) MC\n\n#else\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        float4 *mc_ptr, const uint64_t ub_timeout) {}\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs_oop(const int op, const int flagoffset,\n                                               const int firstrank, const int myrank,\n                                               const int gpustep, const int mylineoffset,\n                                               const int totallines, const int rowlines,\n                                               const int skiplines, void **commbuff,\n                                               const int handleridx, void *outbuf, float4 *mc_ptr,\n                                               const uint64_t ub_timeout) {}\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, uint4 *mc_ptr,\n                                           const uint64_t ub_timeout) {}\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, float4 *mc_ptr,\n                                           const uint64_t ub_timeout) {}\n#endif\n\ntemplate <int RANKS, typename fp8type>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_fp8(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, float *scale, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  half_dtype hscale = (half_dtype)*scale;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      fp8type *x = reinterpret_cast<fp8type *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < sizeof(int4) / sizeof(fp8type); j++) s[j] += hscale * (half_dtype)(x[j]);\n    }\n    int hline = 2 * line;\n    (reinterpret_cast<int4 *>(outbuf))[(hline / rowlines) * skiplines + (hline % rowlines)] =\n        sum[0];\n    hline++;\n    (reinterpret_cast<int4 *>(outbuf))[(hline / rowlines) * skiplines + (hline % rowlines)] =\n        sum[1];\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) (fp8->fp16)\n\ntemplate <int RANKS, typename fp8type>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_atomic_fp8(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines_out, const int skiplines_in, void **commbuff, const int handleridx,\n        void *outbuf, float *scale, void *counters, const int numchunks, const int atomicindex,\n        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  half_dtype hscale = (half_dtype)*scale;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    // const int blockflagoffset = MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr);\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;  // + blockflagoffset;\n  }\n\n  for (int chunk_i = 0; chunk_i < numchunks; chunk_i++) {\n    ATOMIC_CONSUMER(chunk_i);\n\n    lastSM = 0;\n    if (threadIdx.x < RANKS) {\n      reduce_id++;\n      if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n      volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n      userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, reduce_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                   threadIdx.x, reduce_id, *flag);\n          break;\n        }\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x == 0) {\n      const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n      int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), /*numchunks * */ adder);\n      if (old_val + adder == NVTE_MAX_SMS * (reduce_id /* + numchunks*/)) lastSM = 1;\n    }\n\n    int warp = blockIdx.x + (threadIdx.x >> 5);\n    int dest[RANKS];\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n    __syncthreads();\n    for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n         line += blockDim.x * gridDim.x) {\n      int4 val[RANKS];\n      const int rowlines_in = rowlines / 2;\n      const int index_in = skiplines_in == 0\n                               ? mylineoffset + myrank * totallines + line\n                               : (numchunks <= 1 ? 1 : chunk_i) * mylineoffset +\n                                     myrank * (totallines * skiplines_in / rowlines_in) +\n                                     (line / rowlines_in) * skiplines_in + (line % rowlines_in);\n      const int index1_out = chunk_i * mylineoffset * 2 + ((2 * line) / rowlines) * skiplines_out +\n                             ((2 * line) % rowlines);\n      const int index2_out = chunk_i * mylineoffset * 2 +\n                             ((2 * line + 1) / rowlines) * skiplines_out +\n                             ((2 * line + 1) % rowlines);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        val[i] = userptr[dest[i]][index_in];\n      }\n\n      int4 sum[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};\n      half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        fp8type *x = reinterpret_cast<fp8type *>(&val[i]);\n#pragma unroll\n        for (int j = 0; j < sizeof(int4) / sizeof(fp8type); j++)\n          s[j] += hscale * (half_dtype)(x[j]);\n      }\n      (reinterpret_cast<int4 *>(outbuf))[index1_out] = sum[0];\n      (reinterpret_cast<int4 *>(outbuf))[index2_out] = sum[1];\n    }\n  }\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) (fp8->fp16)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n    int index_in = mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                   (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][index_in];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    int index_out = (line / rowlines) * skiplines + (line % rowlines);\n    (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_atomic(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines, const int numchunks, void **commbuff, const int handleridx,\n        void *outbuf, void *counters, const uint64_t ub_timeout) {\n  if (counters) {\n    if (threadIdx.x == 0) {\n      // spin-lock on counter from producer\n      while (0 != (atomicCAS(((unsigned int *)counters), 0, 0))) {\n      }\n\n      // make sure all threadblocks have read/waited on counters.\n      atomicInc(((unsigned int *)counters) + numchunks, gridDim.x - 1);\n      while (0 != (atomicCAS(((unsigned int *)counters) + numchunks, 0, 0))) {\n      }\n\n      // reset counter for next producer.\n      ((unsigned int *)counters)[0] = 1;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n  __syncthreads();\n\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n    int index_in = mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                   (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][index_in];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    int index_out = (line / rowlines) * skiplines + (line % rowlines);\n    (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_multiatomic(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines, const int numchunks, void **commbuff, const int handleridx,\n        void *outbuf, void *counters, const uint64_t ub_timeout) {\n  for (int chunk_i = 0; chunk_i < numchunks; chunk_i++) {\n    if (counters) {\n      if (threadIdx.x == 0) {\n        // spin-lock on counter from producer\n        while (0 != (atomicCAS(((unsigned int *)counters) + chunk_i, 0, 0))) {\n        }\n\n        // make sure all threadblocks have read/waited on counters.\n        atomicInc(((unsigned int *)counters) + numchunks + chunk_i, gridDim.x - 1);\n        while (0 != (atomicCAS(((unsigned int *)counters) + numchunks + chunk_i, 0, 0))) {\n        }\n\n        // reset counter for next producer.\n        ((unsigned int *)counters)[chunk_i] = 1;\n        asm volatile(\"fence.sc.gpu;\\n\");\n      }\n    }\n    __syncthreads();\n\n    __shared__ int4 *userptr[RANKS];\n    volatile int *flagptr;\n    int physgpu, targetgpu, *myptr;\n    int *reduceidptr, reduce_id;\n    int lastSM = 0;\n\n    if (threadIdx.x < RANKS) {\n      physgpu = myrank * gpustep + firstrank;\n      targetgpu = threadIdx.x * gpustep + firstrank;\n      myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n      reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n      reduce_id = (*reduceidptr) + 1;\n      flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n      if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n      volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n      userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, reduce_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                   threadIdx.x, reduce_id, *flag);\n          break;\n        }\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x == 0) {\n      const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n      int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n      if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n    }\n\n    int warp = blockIdx.x + (threadIdx.x >> 5);\n    int dest[RANKS];\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n    for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n         line += blockDim.x * gridDim.x) {\n      int4 val[RANKS];\n      int index_in = chunk_i * mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                     (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        val[i] = userptr[dest[i]][index_in];\n      }\n\n      int4 sum = val[0];\n      half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n      for (int i = 1; i < RANKS; i++) {\n        half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n        for (int j = 0; j < 8; j++) s[j] += x[j];\n      }\n\n      int index_out = chunk_i * mylineoffset + (line / rowlines) * skiplines + (line % rowlines);\n      (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n    }\n    if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n  }\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n  __syncthreads();\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS - 1];\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line + totallines * dest[i]];\n    }\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[myrank][mylineoffset + line + totallines * dest[i]] = val[i];\n    }\n  }\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace reduce kernel (Ampere)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rw_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int4 *localptr;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n  }\n  __syncthreads();\n  localptr = userptr[myrank];\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS - 1];\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n#define UNROLLAG 4\n  __syncthreads();\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLLAG;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    int4 val[UNROLLAG];\n#pragma unroll\n    for (int j = 0; j < UNROLLAG; j++) val[j] = localptr[mylineoffset + line + loop_step0 * j];\n\n#pragma unroll\n    for (int j = 0; j < UNROLLAG; j++)\n#pragma unroll\n      for (int i = 0; i < RANKS - 1; i++) {\n        userptr[dest[i]][mylineoffset + line + j * loop_step0] = val[j];\n      }\n  }\n\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    int4 sum = localptr[mylineoffset + line];\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[dest[i]][mylineoffset + line] = sum;\n    }\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace allgather kernel (Volta,Hopper)\n\n#define SETUP_LAUNCH_CONFIG(sms, threads, stream)                                    \\\n  cudaLaunchConfig_t cfg = {sms, threads, 0, stream, NULL, 0};                       \\\n  cudaLaunchAttribute attribute_ub[2];                                               \\\n  attribute_ub[1].id = cudaLaunchAttributeClusterDimension;                          \\\n  attribute_ub[1].val.clusterDim.x = sms % comm->cga_size == 0 ? comm->cga_size : 1; \\\n  attribute_ub[1].val.clusterDim.y = 1;                                              \\\n  attribute_ub[1].val.clusterDim.z = 1;                                              \\\n  attribute_ub[0].id = cudaLaunchAttributeCooperative;                               \\\n  cfg.attrs = attribute_ub;                                                          \\\n  cfg.numAttrs = comm->sm_arch >= 9 ? 2 : 1;\n\n#if (CUDART_VERSION >= 12030)\n#define ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event) \\\n  attribute_ub[2].id = cudaLaunchAttributeLaunchCompletionEvent;     \\\n  attribute_ub[2].val.launchCompletionEvent.event = comm_launch_event;\n#define NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH 3\n#else\n#define ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event)\n#define NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH 2\n#endif\n\n#define SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, threads, stream, comm_launch_event) \\\n  cudaLaunchConfig_t cfg = {sms, threads, 0, stream, NULL, 0};                             \\\n  cudaLaunchAttribute attribute_ub[NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH] = {};              \\\n  ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event)                             \\\n  attribute_ub[1].id = cudaLaunchAttributeClusterDimension;                                \\\n  attribute_ub[1].val.clusterDim.x = sms % comm->cga_size == 0 ? comm->cga_size : 1;       \\\n  attribute_ub[1].val.clusterDim.y = 1;                                                    \\\n  attribute_ub[1].val.clusterDim.z = 1;                                                    \\\n  attribute_ub[0].id = cudaLaunchAttributeCooperative;                                     \\\n  cfg.attrs = attribute_ub;                                                                \\\n  cfg.numAttrs = NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH;\n\n#define callranks_ag(x)                                                                            \\\n  if (ar_nvsize == x) {                                                                            \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                  \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                            \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +           \\\n               NVTE_MAX_OPS,                                                                       \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,             \\\n        arg6 = offset / 8 + (comm->use_rr_kernel ? 0 : arg4 * arg7);                               \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                       \\\n    int arg9 = handler * comm->nvsize;                                                             \\\n    uint64_t arg10 = comm->ub_timeout;                                                             \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),        \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),        \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),        \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),        \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10)};      \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                           \\\n        &cfg,                                                                                      \\\n        reinterpret_cast<void *>(comm->use_rr_kernel ? userbuffers_fp16_sum_inplace_gpu_rr_ag<x>   \\\n                                                     : userbuffers_fp16_sum_inplace_gpu_rw_ag<x>), \\\n        kernelArgs));                                                                              \\\n  }\n\n#define callranks_agMC(x)                                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    uint4 *arg10 = reinterpret_cast<uint4 *>(comm->mc_ptr[handler]);                             \\\n    uint64_t arg11 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10),     \\\n                          reinterpret_cast<void *>(&arg11)};                                     \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_ag<x>), kernelArgs)); \\\n  }\n\n#define callranks_rs(x)                                                                          \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    uint64_t arg10 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10)};    \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs<x>), kernelArgs)); \\\n  }\n\n#define callranks_rsMC(x)                                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    void *arg10 = comm->mc_ptr[handler];                                                         \\\n    uint64_t arg11 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10),     \\\n                          reinterpret_cast<void *>(&arg11)};                                     \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_rs<x>), kernelArgs)); \\\n  }\n\n#define callranks_rs_oop(x)                                                                   \\\n  if (ar_nvsize == x) {                                                                       \\\n    int arg1 = op - NVTE_MAX_OPS,                                                             \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                       \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +      \\\n               NVTE_MAX_OPS,                                                                  \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,        \\\n        arg6 = offset / 8 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;   \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                 \\\n    int arg11 = handler * comm->nvsize;                                                       \\\n    void *arg12 = output;                                                                     \\\n    uint64_t arg13 = comm->ub_timeout;                                                        \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),  \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),  \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),  \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),  \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10), \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12), \\\n                          reinterpret_cast<void *>(&arg13)};                                  \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                      \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop<x>),        \\\n        kernelArgs));                                                                         \\\n  }\n\n#define callranks_rs_oop_fp8(x)                                                                \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 16 / x,        \\\n        arg6 = offset / 16 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;   \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg11 = handler * comm->nvsize;                                                        \\\n    void *arg12 = output;                                                                      \\\n    float *arg13 = scale;                                                                      \\\n    uint64_t arg14 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14)}; \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                       \\\n        &cfg,                                                                                  \\\n        reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_fp8<x, fp8type>),  \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oopMC(x)                                                                  \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,         \\\n        arg6 = offset / 8 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;    \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg11 = handler * comm->nvsize;                                                        \\\n    void *arg12 = output;                                                                      \\\n    void *arg13 = comm->mc_ptr[handler];                                                       \\\n    uint64_t arg14 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14)}; \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                       \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_rs_oop<x>),         \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oop_atomic_fp8(x)                                                         \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 16 / x,        \\\n        arg6 = offset / 16, arg8 = rowelements / 8, arg9 = strideelements_out / 8,             \\\n        arg10 = strideelements_in / 16;                                                        \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg12 = handler * comm->nvsize;                                                        \\\n    void *arg13 = output;                                                                      \\\n    float *arg14 = scale;                                                                      \\\n    void *arg15 = counters;                                                                    \\\n    int arg16 = numchunks, arg17 = atomicindex;                                                \\\n    uint64_t arg18 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),  \\\n                          reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16),  \\\n                          reinterpret_cast<void *>(&arg17), reinterpret_cast<void *>(&arg18)}; \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                       \\\n        &cfg,                                                                                  \\\n        reinterpret_cast<void *>(                                                              \\\n            userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_atomic_fp8<x, fp8type>),                \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oop_stride(x)                                                            \\\n  if (ar_nvsize == x) {                                                                       \\\n    int arg1 = op - NVTE_MAX_OPS,                                                             \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                       \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +      \\\n               NVTE_MAX_OPS,                                                                  \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,        \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8;                 \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                 \\\n    int arg11 = handler * comm->nvsize;                                                       \\\n    void *arg12 = output;                                                                     \\\n    uint64_t arg13 = comm->ub_timeout;                                                        \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),  \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),  \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),  \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),  \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10), \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12), \\\n                          reinterpret_cast<void *>(&arg13)};                                  \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                      \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride<x>), \\\n        kernelArgs));                                                                         \\\n  }\n\n#define callranks_rs_oop_stride_atomic(x)                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8, arg10 = numchunks; \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                    \\\n    int arg12 = handler * comm->nvsize;                                                          \\\n    void *arg13 = output;                                                                        \\\n    void *arg14 = counters;                                                                      \\\n    uint64_t arg15 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),     \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),     \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),     \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),     \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),    \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),    \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),    \\\n                          reinterpret_cast<void *>(&arg15)};                                     \\\n    NVTE_CHECK_CUDA(cudaLaunchKernelExC(                                                         \\\n        &cfg,                                                                                    \\\n        reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_atomic<x>),   \\\n        kernelArgs));                                                                            \\\n  }\n\n#define callranks_rs_oop_stride_multiatomic(x)                                                     \\\n  if (ar_nvsize == x) {                                                                            \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                  \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                            \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +           \\\n               NVTE_MAX_OPS,                                                                       \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,             \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8, arg10 = numchunks;   \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                      \\\n    int arg12 = handler * comm->nvsize;                                                            \\\n    void *arg13 = output;                                                                          \\\n    void *arg14 = counters;                                                                        \\\n    uint64_t arg15 = comm->ub_timeout;                                                             \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),       \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),       \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),       \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),       \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),      \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),      \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),      \\\n                          reinterpret_cast<void *>(&arg15)};                                       \\\n    NVTE_CHECK_CUDA(                                                                               \\\n        cudaLaunchKernelExC(&cfg,                                                                  \\\n                            reinterpret_cast<void *>(                                              \\\n                                userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_multiatomic<x>), \\\n                            kernelArgs));                                                          \\\n  }\n\nvoid reducescatter2_userbuff_strided(void *output, const int handler, const int offset,\n                                     const int rowelements, const int colelements,\n                                     const int strideelements, communicator *comm,\n                                     cudaStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride(2) callranks_rs_oop_stride(4) callranks_rs_oop_stride(8)\n      callranks_rs_oop_stride(16) callranks_rs_oop_stride(32)\n}\nvoid reducescatter2_userbuff_strided_atomic(void *output, const int handler, const int offset,\n                                            const int rowelements, const int colelements,\n                                            const int strideelements, const int numchunks,\n                                            void *counters, communicator *comm,\n                                            cudaStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride_atomic(2) callranks_rs_oop_stride_atomic(4)\n      callranks_rs_oop_stride_atomic(8) callranks_rs_oop_stride_atomic(16)\n          callranks_rs_oop_stride_atomic(32)\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_universal_fp8(void *output, float *scale, const int handler,\n                                                   const int offset, const int rowelements,\n                                                   const int colelements,\n                                                   const int strideelements_out,\n                                                   const int strideelements_in, const int numchunks,\n                                                   const int atomicindex, void *counters,\n                                                   communicator *comm, cudaStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  assert(comm->sm_arch >= 9);\n  if (elements < 128) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_atomic_fp8(2) callranks_rs_oop_atomic_fp8(4) callranks_rs_oop_atomic_fp8(8)\n      callranks_rs_oop_atomic_fp8(16) callranks_rs_oop_atomic_fp8(32)\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_atomic_fp8(void *output, float *scale, const int handler,\n                                                const int offset, const int rowelements,\n                                                const int colelements, const int strideelements_out,\n                                                const int strideelements_in, const int numchunks,\n                                                void *counters, communicator *comm,\n                                                cudaStream_t stream) {\n  reducescatter2_userbuff_strided_universal_fp8<fp8type>(\n      output, scale, handler, offset, rowelements, colelements, strideelements_out,\n      strideelements_in, 1, numchunks, counters /*nullptr*/, comm, stream);\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_multiatomic_fp8(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, cudaStream_t stream) {\n  reducescatter2_userbuff_strided_universal_fp8<fp8type>(\n      output, scale, handler, offset, rowelements, colelements, strideelements_out,\n      strideelements_in, numchunks, 0, counters /*nullptr*/, comm, stream);\n}\n\nvoid reducescatter2_userbuff_strided_multiatomic(void *output, const int handler, const int offset,\n                                                 const int rowelements, const int colelements,\n                                                 const int strideelements, const int numchunks,\n                                                 void *counters, communicator *comm,\n                                                 cudaStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride_multiatomic(2) callranks_rs_oop_stride_multiatomic(4)\n      callranks_rs_oop_stride_multiatomic(8) callranks_rs_oop_stride_multiatomic(16)\n          callranks_rs_oop_stride_multiatomic(32)\n}\n\nvoid allgather2_userbuff_inplace(const int handler, const int offset, const int elements,\n                                 communicator *comm, cudaStream_t stream,\n                                 cudaEvent_t comm_launch_event) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_agMC(2) callranks_agMC(4) callranks_agMC(8) callranks_agMC(16) callranks_agMC(32)\n    } else {\n      callranks_ag(2) callranks_ag(4) callranks_ag(8) callranks_ag(16) callranks_ag(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_agMC(2) callranks_agMC(4) callranks_agMC(8) callranks_agMC(16) callranks_agMC(32)\n    } else {\n      callranks_ag(2) callranks_ag(4) callranks_ag(8) callranks_ag(16) callranks_ag(32)\n    }\n  }\n}\n\nvoid allgather2_userbuff_inplace_sliced(const int handler, const int offset, const int elements,\n                                        communicator *comm, const int slice_id, const int nslices,\n                                        cudaStream_t stream) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  int peerelements = elements / ar_nvsize;\n  int saverrkernel = comm->use_rr_kernel;\n  comm->use_rr_kernel = 0;\n  allgather2_userbuff_inplace(\n      handler, offset + ar_nvrank * peerelements * (nslices - 1) + slice_id * peerelements,\n      elements, comm, stream);\n  comm->use_rr_kernel = saverrkernel;\n}\n\nvoid reducescatter2_userbuff_inplace(const int handler, const int offset, const int elements,\n                                     communicator *comm, cudaStream_t stream,\n                                     cudaEvent_t comm_launch_event) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rsMC(2) callranks_rsMC(4) callranks_rsMC(8) callranks_rsMC(16) callranks_rsMC(32)\n    } else {\n      callranks_rs(2) callranks_rs(4) callranks_rs(8) callranks_rs(16) callranks_rs(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rsMC(2) callranks_rsMC(4) callranks_rsMC(8) callranks_rsMC(16) callranks_rsMC(32)\n    } else {\n      callranks_rs(2) callranks_rs(4) callranks_rs(8) callranks_rs(16) callranks_rs(32)\n    }\n  }\n}\nvoid reducescatter2_userbuff_stridedoutput(void *output, const int handler, const int offset,\n                                           const int rowelements, const int colelements,\n                                           const int strideelements, communicator *comm,\n                                           cudaStream_t stream, cudaEvent_t comm_launch_event) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rs_oopMC(2) callranks_rs_oopMC(4) callranks_rs_oopMC(8) callranks_rs_oopMC(16)\n          callranks_rs_oopMC(32)\n    } else {\n      callranks_rs_oop(2) callranks_rs_oop(4) callranks_rs_oop(8) callranks_rs_oop(16)\n          callranks_rs_oop(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rs_oopMC(2) callranks_rs_oopMC(4) callranks_rs_oopMC(8) callranks_rs_oopMC(16)\n          callranks_rs_oopMC(32)\n    } else {\n      callranks_rs_oop(2) callranks_rs_oop(4) callranks_rs_oop(8) callranks_rs_oop(16)\n          callranks_rs_oop(32)\n    }\n  }\n}\nvoid reducescatter2_userbuff(void *output, const int handler, const int offset, const int elements,\n                             communicator *comm, cudaStream_t stream,\n                             cudaEvent_t comm_launch_event) {\n  reducescatter2_userbuff_stridedoutput(output, handler, offset, elements, 1, 0, comm, stream,\n                                        comm_launch_event);\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_stridedoutput_fp8(void *output, float *scale, const int handler,\n                                               const int offset, const int rowelements,\n                                               const int colelements, const int strideelements,\n                                               communicator *comm, cudaStream_t stream,\n                                               cudaEvent_t comm_launch_event) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  assert(comm->sm_arch >= 9);\n  if (elements < 128) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    callranks_rs_oop_fp8(2) callranks_rs_oop_fp8(4) callranks_rs_oop_fp8(8) callranks_rs_oop_fp8(16)\n        callranks_rs_oop_fp8(32)\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    callranks_rs_oop_fp8(2) callranks_rs_oop_fp8(4) callranks_rs_oop_fp8(8) callranks_rs_oop_fp8(16)\n        callranks_rs_oop_fp8(32)\n  }\n}\n\ntemplate void reducescatter2_userbuff_stridedoutput_fp8<__nv_fp8_e5m2>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements, communicator *comm, cudaStream_t stream,\n    cudaEvent_t comm_launch_event);\n\ntemplate void reducescatter2_userbuff_stridedoutput_fp8<__nv_fp8_e4m3>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements, communicator *comm, cudaStream_t stream,\n    cudaEvent_t comm_launch_event);\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_fp8(void *output, float *scale, const int handler, const int offset,\n                                 const int elements, communicator *comm, cudaStream_t stream,\n                                 cudaEvent_t comm_launch_event) {\n  reducescatter2_userbuff_stridedoutput_fp8<fp8type>(output, scale, handler, offset, elements, 1, 0,\n                                                     comm, stream, comm_launch_event);\n}\n\ntemplate void reducescatter2_userbuff_fp8<__nv_fp8_e5m2>(void *output, float *scale,\n                                                         const int handler, const int offset,\n                                                         const int elements, communicator *comm,\n                                                         cudaStream_t stream,\n                                                         cudaEvent_t comm_launch_event);\ntemplate void reducescatter2_userbuff_fp8<__nv_fp8_e4m3>(void *output, float *scale,\n                                                         const int handler, const int offset,\n                                                         const int elements, communicator *comm,\n                                                         cudaStream_t stream,\n                                                         cudaEvent_t comm_launch_event);\n\ntemplate void reducescatter2_userbuff_strided_atomic_fp8<__nv_fp8_e4m3>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, cudaStream_t stream);\ntemplate void reducescatter2_userbuff_strided_atomic_fp8<__nv_fp8_e5m2>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, cudaStream_t stream);\ntemplate void reducescatter2_userbuff_strided_multiatomic_fp8<__nv_fp8_e4m3>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, cudaStream_t stream);\ntemplate void reducescatter2_userbuff_strided_multiatomic_fp8<__nv_fp8_e5m2>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, cudaStream_t stream);\n\n__global__ void kuserbuffers_pullsend(int myrank, int peer, int *send_id, int *flagptr) {\n  atomicAdd_system(flagptr, 1);\n}\n\n__global__ void kuserbuffers_inc(int *id) { atomicAdd(id, 1); }\n\n__global__ void kuserbuffers_dummy(void) {}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pullrecv(int myrank, int peer, int nvrank, int nvpeer, int *recv_id, int *flagptr,\n                          int4 *srcptr, int4 *dstptr, const int lines, uint64_t ub_timeout) {\n#define UNROLLCOPY 8\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = lines;\n  const int aligned_elem = (end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1));\n  const int end_aligned = start_elem + aligned_elem;\n\n  if (threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + 1;\n    volatile int *flag = (volatile int *)flagptr;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pullrecv [grank dst:%d global src:%d][nvrank(GPU) dst: %d src: %d]: expecting %d,\"\n            \" observed %d\",\n            myrank, peer, nvrank, nvpeer, signal_id, *flag);\n        break;\n      }\n    }\n    if (lines == 0) {\n      *recv_id = signal_id;\n      return;\n    }  // otherwise need an extra kernel\n  }\n  __syncthreads();\n\n  if (end_elem <= start_elem) return;\n\n  for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n    int4 val[UNROLLCOPY];\n#pragma unroll\n    for (int i = 0; i < UNROLLCOPY; i++) val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n#pragma unroll\n    for (int i = 0; i < UNROLLCOPY; i++) dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x)\n    dstptr[line] = srcptr[line];\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsend(int *send_id, int *flagptr, int4 *srcptr, int4 *dstptr, const int lines) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x)\n        dstptr[line] = srcptr[line];\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(flagptr, 1);\n  }\n}\n\n#define CHECK_CE(ce_start, ce_end) \\\n  ((ce_start) != nullptr && (ce_end) != nullptr && *(ce_start) != *(ce_end))\n\n__global__ void kuserbuffers_pushrecv(int myrank, int peer, int nvrank, int nvpeer, int *recv_id,\n                                      int *flagptr, int adder, uint64_t ub_timeout,\n                                      int *ce_start_ptr, int *ce_end_ptr) {\n  const int signal_id = (*recv_id) + adder;\n  *recv_id = signal_id;\n  volatile int *flag = (volatile int *)flagptr;\n  if (*flag >= signal_id) return;\n  clock_t s = clock64();\n  while (CHECK_IDS(*flag, signal_id)) {\n    if (CHECK_TIMEOUT(s, ub_timeout)) {\n      UB_PRINT(\n          \"pushrecv [grank dst:%d global src:%d][nvrank(GPU) dst: %d src: %d]: \"\n          \"expecting %d, observed %d\",\n          myrank, peer, nvrank, nvpeer, signal_id, *flag);\n      if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n        UB_PRINT(\"pushrecv: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\", *ce_start_ptr,\n                 *ce_end_ptr);\n      return;\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsendrecv(int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr,\n                              const int lines, int send_peer, int recv_peer, int *recv_id,\n                              int *recv_flagptr, int adder, uint64_t ub_timeout, int nv_send,\n                              int nv_recv, int *ce_start_ptr, int *ce_end_ptr) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n        }\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n        }\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n        dstptr[line] = srcptr[line];\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(send_flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(send_flagptr, 1);\n  }\n\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + adder;\n    *recv_id = signal_id;\n    volatile int *flag = (volatile int *)recv_flagptr;\n    if (*flag >= signal_id) return;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pushsendrecv [sending peer:%d receiving peer:%d][nvrank(GPU) sending peer: %d\"\n            \" receiving peer: %d]: expecting %d, observed %d\",\n            send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag);\n        if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n          UB_PRINT(\"pushrecv: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\", *ce_start_ptr,\n                   *ce_end_ptr);\n        return;\n      }\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsendrecv_atomic(int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr,\n                                     const int lines, int send_peer, int recv_peer, int *recv_id,\n                                     int *recv_flagptr, int adder, void *counters,\n                                     uint64_t ub_timeout, int nv_send, int nv_recv,\n                                     int *ce_start_ptr, int *ce_end_ptr) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n        }\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n        }\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n        dstptr[line] = srcptr[line];\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(send_flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(send_flagptr, 1);\n  }\n\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + adder;\n    *recv_id = signal_id;\n    volatile int *flag = (volatile int *)recv_flagptr;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pushsendrecv atomic [sending peer:%d receiving peer:%d][nvrank(GPU) sending peer:\"\n            \" %d receiving peer: %d]: expecting %d, observed %d\",\n            send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag); /*return;*/\n        if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n          UB_PRINT(\"pushsendrecv atomic: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\",\n                   *ce_start_ptr, *ce_end_ptr);\n      }\n    }\n\n    // Decrement atomic val to signal current output tile finish\n    if (counters) {\n      ((unsigned int *)counters)[0] = 0;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS) kuserbuffers_pushsendrecv_multiatomic(\n    int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr, const int lines, int send_peer,\n    int recv_peer, int *recv_id, int *recv_flagptr, int adder, void *counters, int nchunks,\n    int send_stride, int recv_stride, bool shuffle, uint64_t ub_timeout, int nv_send, int nv_recv) {\n  for (int chunk_i = 0; chunk_i < nchunks - 1; chunk_i++) {\n    int send_chunk_id = shuffle ? chunk_i : (nchunks + send_peer - chunk_i) % nchunks;\n    int recv_chunk_id = shuffle ? chunk_i + 1 : (nchunks + send_peer - chunk_i - 1) % nchunks;\n    int send_offset = (send_chunk_id * send_stride) / 16;\n    int recv_offset = ((shuffle ? recv_chunk_id : send_chunk_id) * recv_stride) / 16;\n\n    if (lines) {\n      const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n      const int end_elem = lines;\n      const int aligned_elem =\n          ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n      const int end_aligned = start_elem + aligned_elem;\n      if (end_elem > start_elem) {\n        for (int line = start_elem; line < end_aligned;\n             line += blockDim.x * gridDim.x * UNROLLCOPY) {\n          int4 val[UNROLLCOPY];\n#pragma unroll\n          for (int i = 0; i < UNROLLCOPY; i++) {\n            val[i] = srcptr[send_offset + line + i * blockDim.x * gridDim.x];\n          }\n#pragma unroll\n          for (int i = 0; i < UNROLLCOPY; i++) {\n            dstptr[recv_offset + line + i * blockDim.x * gridDim.x] = val[i];\n          }\n        }\n        for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n          dstptr[recv_offset + line] = srcptr[send_offset + line];\n        }\n      }\n      __syncthreads();\n      if (!threadIdx.x) {\n        __threadfence_system();\n        atomicAdd_system(send_flagptr,\n                         1);  // otherwise need local SM sync before sending flag\n      }\n    } else {  // 0 bytes and 1 SM only\n      atomicAdd_system(send_flagptr, 1);\n    }\n\n    // wait for message to arrive.\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n      const int signal_id = (*recv_id) + adder;\n      *recv_id = signal_id;\n      volatile int *flag = (volatile int *)recv_flagptr;\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, signal_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\n              \"pushsendrecv multiatomic [sending peer:%d receiving peer:%d][nvrank(GPU)\"\n              \" sending peer: %d receiving peer: %d]: expecting %d, observed %d\",\n              send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag); /*return;*/\n          // CE mode is not supported for multi-atomic, so there is no need to check for a deadlock\n          return;\n        }\n      }\n    }\n\n    // Producer must update counters.\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n      // Decrement atomic val to signal current output tile finish\n      if (counters) {\n        ((unsigned int *)counters)[recv_chunk_id /*chunk_i+1*/] = 0;\n        asm volatile(\"fence.sc.gpu;\\n\");\n      }\n    }\n\n    // sync all CTAs before moving to next chunk.\n    if (threadIdx.x == 0) {\n      atomicInc(((unsigned int *)counters) + nchunks + chunk_i, gridDim.x - 1);\n      while (0 != (atomicCAS(((unsigned int *)counters) + nchunks + chunk_i, 0, 0))) {\n      }\n    }\n    __syncthreads();\n  }\n}\n\n// Return TRUE if two ranks share the same NV domain\n#define INTRANODE(peer) ((peer / comm->nvsize) == (comm->myrank / comm->nvsize))\n\n// Index corresponds to the type of flag:\n// 0 - Send index counter\n// 1 - CE start index counter\n// 2 - CE end index counter\n#define GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsth, index)                                 \\\n  ((reinterpret_cast<char *>((comm)->peer_ptr[0][(peerlocal)])) +                           \\\n   ((NVTE_REG0_OFFSET(comm) + NVTE_REG0_RECV + (comm)->myrank * NVTE_MAX_REGIONS + (dsth) + \\\n     (index) * NVTE_MAX_NVLINK * NVTE_MAX_REGIONS) *                                        \\\n    sizeof(int)))\n\n// Index corresponds to the type of flag:\n// 0 - Receive index counter\n// 1 - CE start index counter\n// 2 - CE end index counter\n#define GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsth, index)                              \\\n  ((reinterpret_cast<char *>((comm)->mem_ptr[0])) +                                      \\\n   ((NVTE_REG0_OFFSET(comm) + NVTE_REG0_RECV + (recv_peer) * NVTE_MAX_REGIONS + (dsth) + \\\n     (index) * NVTE_MAX_NVLINK * NVTE_MAX_REGIONS) *                                     \\\n    sizeof(int)))\n\nstatic __global__ void tiny_delay_kern() {\n  // Although a loop could be used to add a larger delay here, for\n  // the purpose of enforcing proper kernel ordering when using CG,\n  // an empty kernel seems to work well enough.\n}\n\nvoid userbuffers_tiny_delay(cudaStream_t stream) { tiny_delay_kern<<<1, 1, 0, stream>>>(); }\n\nvoid userbuffers_send(const int srchandler, const size_t srcoffset, const int dsthandler,\n                      const size_t dstoffset, const size_t bytes, communicator *comm,\n                      const int peer, cudaStream_t stream) {\n  int peerlocal = peer % comm->nvsize;\n  void *flagptr = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 2);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  assert(INTRANODE(peer));\n\n  if (!(comm->launch_mode & NVTE_LAUNCH_GPU)) return;\n  if (comm->push == 0) {\n    kuserbuffers_pullsend<<<1, 1, 0, stream>>>(comm->myrank, peer, &(comm->send_id[peer]),\n                                               reinterpret_cast<int *>(flagptr));\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  } else {\n    void *srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + srcoffset;\n    void *dstptr = reinterpret_cast<char *>(comm->peer_ptr[dsthandler][peerlocal]) + dstoffset;\n\n    if (comm->use_ce) {\n      // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n      NVTE_CHECK_CUDA(cudaMemcpyAsync(dstptr, srcptr, bytes, cudaMemcpyDeviceToDevice, stream));\n      // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n    }\n    SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n    int *arg1 = &comm->send_id[peer], *arg2 = reinterpret_cast<int *>(flagptr);\n    int4 *arg3 = reinterpret_cast<int4 *>(srcptr), *arg4 = reinterpret_cast<int4 *>(dstptr);\n    int arg5 = signalonly ? 0 : bytes / 16;\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),\n                          reinterpret_cast<void *>(&arg5)};\n    NVTE_CHECK_CUDA(\n        cudaLaunchKernelExC(&cfg, reinterpret_cast<void *>(kuserbuffers_pushsend), kernelArgs));\n  }\n}\n\nvoid userbuffers_sendrecv(const int srchandler, const int dsthandler, const size_t send_offset,\n                          const size_t recv_offset, const size_t bytes, communicator *comm,\n                          const int send_peer, const int recv_peer, cudaStream_t stream) {\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 2);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  void *send_srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + send_offset;\n  void *send_dstptr =\n      reinterpret_cast<char *>(comm->peer_ptr[dsthandler][send_peerlocal]) + send_offset;\n\n  if (comm->use_ce) {\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n    NVTE_CHECK_CUDA(\n        cudaMemcpyAsync(send_dstptr, send_srcptr, bytes, cudaMemcpyDeviceToDevice, stream));\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n  }\n  SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>(send_srcptr);\n  int4 *arg4 = reinterpret_cast<int4 *>(send_dstptr);\n  int arg5 = signalonly ? 0 : bytes / 16;\n  int arg6 = send_peer;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = signalonly ? 1 : comm->sms;\n  uint64_t arg11 = comm->ub_timeout;\n  int arg12 = send_peerlocal;\n  int arg13 = recv_peerlocal;\n  int *arg14 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 1)\n                                         : nullptr);\n  int *arg15 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 2)\n                                         : nullptr);\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15)};\n  NVTE_CHECK_CUDA(\n      cudaLaunchKernelExC(&cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv), kernelArgs));\n}\n\nvoid userbuffers_sendrecv_atomic(const int srchandler, const int dsthandler,\n                                 const size_t send_offset, const size_t recv_offset,\n                                 const size_t bytes, communicator *comm, const int send_peer,\n                                 const int recv_peer, void *counters, cudaStream_t stream) {\n  assert(comm->push && comm->use_ce == 0);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 2);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  void *send_srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + send_offset;\n  void *send_dstptr =\n      reinterpret_cast<char *>(comm->peer_ptr[dsthandler][send_peerlocal]) + send_offset;\n  if (comm->use_ce) {\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n    NVTE_CHECK_CUDA(\n        cudaMemcpyAsync(send_dstptr, send_srcptr, bytes, cudaMemcpyDeviceToDevice, stream));\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n  }\n  SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>(send_srcptr);\n  int4 *arg4 = reinterpret_cast<int4 *>(send_dstptr);\n  int arg5 = signalonly ? 0 : bytes / 16;\n  int arg6 = send_peer;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = signalonly ? 1 : comm->sms;\n  void *arg11 = counters;\n  int arg12 = comm->ub_timeout;\n  int arg13 = send_peerlocal;\n  int arg14 = recv_peerlocal;\n  int *arg15 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 1)\n                                         : nullptr);\n  int *arg16 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 2)\n                                         : nullptr);\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16)};\n  NVTE_CHECK_CUDA(cudaLaunchKernelExC(\n      &cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv_atomic), kernelArgs));\n}\n\nvoid userbuffers_sendrecv_multiatomic(const int srchandler, const int dsthandler,\n                                      const size_t send_stride, const size_t recv_stride,\n                                      const size_t bytes, communicator *comm, const int send_peer,\n                                      const int recv_peer, const int nchunks, void *counters,\n                                      bool shuffle, cudaStream_t stream) {\n  assert(comm->push && comm->use_ce == 0);\n  // CE is not supported\n\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  SETUP_LAUNCH_CONFIG(comm->sms, 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>((comm->mem_ptr[srchandler]));\n  int4 *arg4 = reinterpret_cast<int4 *>((comm->peer_ptr[dsthandler][send_peerlocal]));\n  int arg5 = bytes / 16;\n  int arg6 = comm->myrank;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = comm->sms;\n  void *arg11 = counters;\n  int arg12 = nchunks;\n  int arg13 = send_stride;\n  int arg14 = recv_stride;\n  bool arg15 = shuffle;\n  uint64_t arg16 = comm->ub_timeout;\n  int arg17 = send_peerlocal;\n  int arg18 = recv_peerlocal;\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16),\n                        reinterpret_cast<void *>(&arg17), reinterpret_cast<void *>(&arg18)};\n  NVTE_CHECK_CUDA(cudaLaunchKernelExC(\n      &cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv_multiatomic), kernelArgs));\n}\n\nvoid userbuffers_recv(const int srchandler, const size_t srcoffset, const int dsthandler,\n                      const size_t dstoffset, const size_t bytes, communicator *comm,\n                      const int peer, cudaStream_t stream) {\n  int peerlocal = peer % comm->nvsize;\n  void *flagptr = GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 0);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  assert(INTRANODE(peer));\n\n  if (!(comm->launch_mode & NVTE_LAUNCH_GPU)) return;\n  if (comm->push == 0) {\n    void *dstptr = reinterpret_cast<char *>(comm->mem_ptr[dsthandler]) + dstoffset;\n    void *srcptr = reinterpret_cast<char *>(comm->peer_ptr[srchandler][peerlocal]) + srcoffset;\n\n    kuserbuffers_pullrecv<<<signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, 0, stream>>>(\n        comm->myrank, peer, comm->nvrank, peerlocal,\n        &(comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler]), reinterpret_cast<int *>(flagptr),\n        reinterpret_cast<int4 *>(srcptr), reinterpret_cast<int4 *>(dstptr),\n        signalonly ? 0 : bytes / 16, comm->ub_timeout);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n    if (!signalonly) {\n      kuserbuffers_inc<<<1, 1, 0, stream>>>(&(comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler]));\n      NVTE_CHECK_CUDA(cudaGetLastError());\n    }\n    if (comm->use_ce) {\n      NVTE_CHECK_CUDA(cudaMemcpyAsync(dstptr, srcptr, bytes, cudaMemcpyDeviceToDevice, stream));\n    }\n  } else {\n    kuserbuffers_pushrecv<<<1, 1, 0, stream>>>(\n        comm->myrank, peer, comm->nvrank, peerlocal,\n        &comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler], reinterpret_cast<int *>(flagptr),\n        signalonly || comm->sms, comm->ub_timeout,\n        reinterpret_cast<int *>(0 ?  // temporary disable\n                                    GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 1)\n                                  : nullptr),\n        reinterpret_cast<int *>(0 ?  // temporary disable\n                                    GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 2)\n                                  : nullptr));\n    NVTE_CHECK_CUDA(cudaGetLastError());\n  }\n}\n\nvoid userbuffers_send_all(const int srchandler, const size_t srcoffset, const int dsthandler,\n                          const size_t dstoffset, const size_t bytes_per_slice, int tp_rank,\n                          int tp_size, int world_rank, communicator *comm, cudaStream_t stream) {\n  int rank_round_tp = (world_rank / tp_size) * tp_size;\n  for (int j = 1; j < tp_size; j++) {\n    int i = (tp_rank + j) % tp_size;\n    int send_offset = srcoffset + bytes_per_slice * tp_rank;\n    int recv_offset = dstoffset + bytes_per_slice * tp_rank;\n    userbuffers_send(srchandler, send_offset, dsthandler, recv_offset, bytes_per_slice, comm,\n                     rank_round_tp + i, stream);\n  }\n}\n\nvoid userbuffers_recv_all(const int srchandler, const size_t srcoffset, const int dsthandler,\n                          const size_t dstoffset, const size_t bytes_per_slice, int tp_rank,\n                          int tp_size, int world_rank, communicator *comm, cudaStream_t stream) {\n  int rank_round_tp = (world_rank / tp_size) * tp_size;\n  for (int j = tp_size - 1; j > 0; j--) {\n    int i = (tp_rank + j) % tp_size;\n    int send_offset = srcoffset + bytes_per_slice * i;\n    int recv_offset = dstoffset + bytes_per_slice * i;\n    userbuffers_recv(srchandler, send_offset, dsthandler, recv_offset, bytes_per_slice, comm,\n                     rank_round_tp + i, stream);\n  }\n}\n\n// producer\nstatic __global__ void producer_kernel(void *atomic_ptr, int chunk_i) {\n  // Decrement atomic val to signal current output tile finish\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    ((unsigned int *)atomic_ptr)[chunk_i] = 0;\n  }\n\n  // COMM kernel need to explicitely flash gmem.\n  // GEMM kernel already executed, and can not see gmem\n  // change without COMM kernel explicitely make change\n  asm volatile(\"fence.sc.gpu;\\n\");\n}\n\n// consumer\nstatic __global__ void consumer_kernel(void *atomic_ptr, int chunk_i) {\n  // Wait for producer to change the val to 0, which signal producer ready\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    while (0 != (atomicCAS((unsigned int *)atomic_ptr + chunk_i, 0, 0))) {\n    }\n    ((unsigned int *)atomic_ptr)[chunk_i] = 1;\n    asm volatile(\"fence.sc.gpu;\\n\");\n  }\n}\n\n// consumer_batch\nstatic __global__ void consumer_batch_kernel(void *atomic_ptr, int first_chunk_i, int num_chunks) {\n  // Wait for producer to change the val to 0, which signal producer ready\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    for (int i = first_chunk_i; i < num_chunks; i++) {\n      while (0 != (atomicCAS((unsigned int *)atomic_ptr + i, 0, 0))) {\n      }\n      ((unsigned int *)atomic_ptr)[i] = 1;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n}\n\n// reset counters kernel\nstatic __global__ void reset_counters_kernel(void *atomic_ptr, int num_chunks, bool allgather) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n#pragma unroll\n    for (int i = 0; i < num_chunks; i++) {\n      ((unsigned int *)atomic_ptr)[i] = 1;\n      ((unsigned int *)atomic_ptr)[i + num_chunks] = 0;\n    }\n    if (allgather) ((unsigned int *)atomic_ptr)[0] = 0;\n  }\n}\n\nvoid producer(void *atomic_ptr, int chunk_i, cudaStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  producer_kernel<<<grid, block, 0, stream>>>(atomic_ptr, chunk_i);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid consumer(void *atomic_ptr, int chunk_i, cudaStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  consumer_kernel<<<grid, block, 0, stream>>>(atomic_ptr, chunk_i);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid consumer_batch(void *atomic_ptr, int first_chunk_i, int num_chunks, cudaStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  consumer_batch_kernel<<<grid, block, 0, stream>>>(atomic_ptr, first_chunk_i, num_chunks);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid reset_counters(void *atomic_ptr, int num_chunks, bool allgather, cudaStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  reset_counters_kernel<<<grid, block, 0, stream>>>(atomic_ptr, num_chunks, allgather);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate <typename fp8type, int nvec>\n__global__ void __launch_bounds__(MAX_THREADS / 4)\n    reduce_fp8_in_bf16_out_cuda(void *inputs, void *output, const float *scale,\n                                const int num_inputs, const int input_size,\n                                const int num_aligned_elements_per_input,\n                                const int tot_input_size) {\n  fp8type *inputs_fp8 = reinterpret_cast<fp8type *>(inputs);\n  half_dtype *output_half = reinterpret_cast<half_dtype *>(output);\n\n  transformer_engine::VectorizedLoader<fp8type, nvec, true> loader(inputs_fp8, tot_input_size);\n  transformer_engine::VectorizedStorer<half_dtype, nvec, true> storer(output_half, input_size);\n\n  const size_t tid = threadIdx.x + blockDim.x * blockIdx.x;\n  if (tid >= num_aligned_elements_per_input) {\n    return;\n  }\n  float accum_buf[nvec];  // NOLINT(*)\n\n  loader.load(tid, tot_input_size);\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    accum_buf[i] = static_cast<float>(loader.separate()[i]) * (*scale);\n  }\n  for (int input_id = 1; input_id < num_inputs; ++input_id) {\n    loader.load(tid + num_aligned_elements_per_input * input_id, tot_input_size);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      accum_buf[i] += static_cast<float>(loader.separate()[i]) * (*scale);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    storer.separate()[i] = static_cast<half_dtype>(accum_buf[i]);\n  }\n  storer.store(tid, input_size);\n}\n\ntemplate <typename fp8type>\nvoid reduce_fp8_in_bf16_out(void *inputs, void *output, float *scale, int num_inputs,\n                            int input_size, cudaStream_t stream) {\n  constexpr int nvec = 32;\n  assert(input_size % nvec == 0);\n  const int num_aligned_elements_per_input = input_size / nvec;\n  const int tot_input_size = input_size * num_inputs;\n  size_t num_threads = MAX_THREADS / 4;\n  size_t num_blocks = (num_aligned_elements_per_input + num_threads - 1) / num_threads;\n  dim3 block(num_threads);\n  dim3 grid(num_blocks);\n  reduce_fp8_in_bf16_out_cuda<fp8type, nvec>\n      <<<grid, block, 0, stream>>>(inputs, output, scale, num_inputs, input_size,\n                                   num_aligned_elements_per_input, tot_input_size);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\ntemplate void reduce_fp8_in_bf16_out<__nv_fp8_e4m3>(void *inputs, void *output, float *scale,\n                                                    int num_inputs, int input_size,\n                                                    cudaStream_t stream);\ntemplate void reduce_fp8_in_bf16_out<__nv_fp8_e5m2>(void *inputs, void *output, float *scale,\n                                                    int num_inputs, int input_size,\n                                                    cudaStream_t stream);\n\ntemplate <int nvec>\n__global__ void __launch_bounds__(MAX_THREADS / 4)\n    reduce_bf16_cuda(void *inputs, void *output, const int num_inputs, const int input_size,\n                     const int num_aligned_elements_per_input, const int tot_input_size) {\n  half_dtype *inputs_half = reinterpret_cast<half_dtype *>(inputs);\n  half_dtype *output_half = reinterpret_cast<half_dtype *>(output);\n\n  transformer_engine::VectorizedLoader<half_dtype, nvec, true> loader(inputs_half, tot_input_size);\n  transformer_engine::VectorizedStorer<half_dtype, nvec, true> storer(output_half, input_size);\n\n  const size_t tid = threadIdx.x + blockDim.x * blockIdx.x;\n  if (tid >= num_aligned_elements_per_input) {\n    return;\n  }\n  float accum_buf[nvec];  // NOLINT(*)\n\n  loader.load(tid, tot_input_size);\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    accum_buf[i] = static_cast<float>(loader.separate()[i]);\n  }\n  for (int input_id = 1; input_id < num_inputs; ++input_id) {\n    loader.load(tid + num_aligned_elements_per_input * input_id, tot_input_size);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      accum_buf[i] += static_cast<float>(loader.separate()[i]);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    storer.separate()[i] = static_cast<half_dtype>(accum_buf[i]);\n  }\n  storer.store(tid, input_size);\n}\n\nvoid reduce_bf16(void *inputs, void *output, int num_inputs, int input_size, cudaStream_t stream) {\n  constexpr int nvec = 32;\n  assert(input_size % nvec == 0);\n  const int num_aligned_elements_per_input = input_size / nvec;\n  const int tot_input_size = input_size * num_inputs;\n  size_t num_threads = MAX_THREADS / 4;\n  size_t num_blocks = (num_aligned_elements_per_input + num_threads - 1) / num_threads;\n  dim3 block(num_threads);\n  dim3 grid(num_blocks);\n  reduce_bf16_cuda<nvec><<<grid, block, 0, stream>>>(\n      inputs, output, num_inputs, input_size, num_aligned_elements_per_input, tot_input_size);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp8.h>\n\n#if __CUDA_ARCH__ >= 800\n#define half_dtype hip_bfloat16\n#else\n#define half_dtype half\n#endif\n\n#include <assert.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include \"common/util/system.h\"\n#include \"common/util/vectorized_pointwise.h\"\n#include \"userbuffers.h\"\n\n#define MAX_THREADS 1024\n\n#define ATOMIC_CONSUMER(chunk)                                             \\\n  if (counters) {                                                          \\\n    if (threadIdx.x == 0 && blockIdx.x == 0) {                             \\\n      while (0 != (atomicCAS(((unsigned int *)counters) + chunk, 0, 0))) { \\\n      }                                                                    \\\n      ((unsigned int *)counters)[chunk] = 1;                               \\\n      asm volatile(\"fence.sc.gpu;\\n\");                                     \\\n    }                                                                      \\\n    if (blockIdx.x == 0) __syncthreads();                                  \\\n  }\n\n#define ATOMIC_PRODUCER(chunk)             \\\n  if (counters) {                          \\\n    ((unsigned int *)counters)[chunk] = 0; \\\n  }\n\n// Return true if producer > consumer, otherwise false while preventing integer overflow\n// If we expect that producer will be 2B+ messages behind consumer\n#define CHECK_IDS(producer, consumer) (((unsigned)(producer) - (unsigned)(consumer)) & (~INT_MAX))\n\n// Strip the path from a full filename\n#define FILENAME(file)                                      \\\n  ({                                                        \\\n    const char *filename = file;                            \\\n    const char *basename = filename;                        \\\n    for (const char *ptr = filename; *ptr != '\\0'; ptr++) { \\\n      if (*ptr == '/' || *ptr == '\\\\') {                    \\\n        basename = ptr + 1;                                 \\\n      }                                                     \\\n    }                                                       \\\n    basename;                                               \\\n  })\n\n// Printf to provide enough information so it is easier to attribute failures\n#define UB_PRINT(message, ...) \\\n  printf(\"[%s:%s:%d] \" message \"\\n\", FILENAME(__FILE__), __FUNCTION__, __LINE__, __VA_ARGS__)\n\n// Report and error on timeout\n#define CHECK_TIMEOUT(t, timeout) ((clock64() - (t)) > timeout)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rw(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank,\n                 blockIdx.x, threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x); line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      // int dest = (i+myrank+warp)&(RANKS-1);\n      val[i] = userptr[dest[i]][lineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      // int dest = (i+myrank+warp)&(RANKS-1);\n      userptr[dest[i]][lineoffset + line] = sum;\n    }\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce Gather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Volta,Hopper)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d ]Allreduce reduce-scatter:SM %d [%d]: expecting %d got %d\", myrank,\n                 blockIdx.x, threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x); line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][lineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    userptr[myrank][lineoffset + line] = sum;\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allreduce gather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n  __syncthreads();\n\n  for (int line = threadIdx.x + blockDim.x * RANKS * blockIdx.x; line < numlines;\n       line += blockDim.x * gridDim.x * RANKS) {\n    int4 val[RANKS - 1];\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      val[i] = userptr[dest[i]][lineoffset + line + blockDim.x * dest[i]];\n    }\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[myrank][lineoffset + line + blockDim.x * dest[i]] = val[i];\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Ampere)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    userptr[myrank][mylineoffset + line] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce-scatter kernel\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    (reinterpret_cast<int4 *>(outbuf))[(line / rowlines) * skiplines + (line % rowlines)] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place)\n\n#if __CUDA_ARCH__ >= 900 && CUDART_VERSION >= 12010\n// All MC kernels here\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        float4 *mc_ptr, const uint64_t ub_timeout) {\n  int *flagptr, physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    const int blockflagoffset = NVTE_MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset + blockflagoffset;\n    myptr += blockflagoffset;\n\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (clock64() - s > ub_timeout) {\n        UB_PRINT(\"Reduce-scatter: SM %d [%d]: expecting %d got %d\", blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n    reduce_id++;\n  }\n  __syncthreads();\n#define UNROLL_MC 8\n  const int loop_step0 = blockDim.x * gridDim.x * RANKS;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * (myrank + RANKS * blockIdx.x);\n  const int end_elem = max(start_elem, numlines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (lineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (lineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      asm volatile(\"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(\n                       mc_ptr + (lineoffset + line + i * loop_step0)),\n                   \"r\"(val[i].x), \"r\"(val[i].y), \"r\"(val[i].z), \"r\"(val[i].w)\n                   : \"memory\");\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (lineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (lineoffset + line))\n        : \"memory\");\n#endif\n    asm volatile(\n        \"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(mc_ptr + (lineoffset + line)),\n        \"r\"(val.x), \"r\"(val.y), \"r\"(val.z), \"r\"(val.w)\n        : \"memory\");\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  if (threadIdx.x < RANKS) {\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (clock64() - s > 2ull * ub_timeout) {\n        UB_PRINT(\"Allgather: SM %d [%d]: expecting %d got %d\", blockIdx.x, threadIdx.x, reduce_id,\n                 *flag);\n        break;\n      }\n    }\n  }\n  if (threadIdx.x == 0 && blockIdx.x == 0) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce kernel (Hopper) MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, float4 *mc_ptr,\n                                           const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  uint4 *localptr = reinterpret_cast<uint4 *>(commbuff[myrank * gpustep + firstrank + handleridx]);\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++) localptr[mylineoffset + line + i * loop_step0] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#endif\n    localptr[mylineoffset + line] = val;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 inplace reduce-scatter kernel MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs_oop(const int op, const int flagoffset,\n                                               const int firstrank, const int myrank,\n                                               const int gpustep, const int mylineoffset,\n                                               const int totallines, const int rowlines,\n                                               const int skiplines, void **commbuff,\n                                               const int handleridx, void *outbuf, float4 *mc_ptr,\n                                               const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n#if defined(NVTE_UB_FP16)\n      asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#else\n      asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n          : \"=r\"(val[i].x), \"=r\"(val[i].y), \"=r\"(val[i].z), \"=r\"(val[i].w)\n          : \"l\"(mc_ptr + (mylineoffset + line + i * loop_step0))\n          : \"memory\");\n#endif\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      (reinterpret_cast<uint4 *>(outbuf))[((line + i * loop_step0) / rowlines) * skiplines +\n                                          ((line + i * loop_step0) % rowlines)] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val;\n#if defined(NVTE_UB_FP16)\n    asm(\"multimem.ld_reduce.global.add.v4.f16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#else\n    asm(\"multimem.ld_reduce.global.add.v4.bf16x2 {%0,%1,%2,%3}, [%4];\"\n        : \"=r\"(val.x), \"=r\"(val.y), \"=r\"(val.z), \"=r\"(val.w)\n        : \"l\"(mc_ptr + (mylineoffset + line))\n        : \"memory\");\n#endif\n    reinterpret_cast<uint4 *>(outbuf)[(line / rowlines) * skiplines + (line % rowlines)] = val;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16 MC\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, uint4 *mc_ptr,\n                                           const uint64_t ub_timeout) {\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  uint4 *localptr = reinterpret_cast<uint4 *>(commbuff[myrank * gpustep + firstrank + handleridx]);\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n  }\n  __syncthreads();\n\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLL_MC;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    uint4 val[UNROLL_MC];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++) val[i] = localptr[mylineoffset + line + i * loop_step0];\n#pragma unroll\n    for (int i = 0; i < UNROLL_MC; i++)\n      asm volatile(\"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(\n                       mc_ptr + (mylineoffset + line + i * loop_step0)),\n                   \"r\"(val[i].x), \"r\"(val[i].y), \"r\"(val[i].z), \"r\"(val[i].w)\n                   : \"memory\");\n  }\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    uint4 val = localptr[mylineoffset + line];\n    asm volatile(\n        \"multimem.st.global.v4.f32 [%0], {%1,%2,%3,%4};\" ::\"l\"(mc_ptr + (mylineoffset + line)),\n        \"r\"(val.x), \"r\"(val.y), \"r\"(val.z), \"r\"(val.w)\n        : \"memory\");\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace allgather kernel (Hopper) MC\n\n#else\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc(const int op, const int flagoffset, const int firstrank,\n                                        const int myrank, const int gpustep, const int lineoffset,\n                                        const int numlines, void **commbuff, const int handleridx,\n                                        float4 *mc_ptr, const uint64_t ub_timeout) {}\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs_oop(const int op, const int flagoffset,\n                                               const int firstrank, const int myrank,\n                                               const int gpustep, const int mylineoffset,\n                                               const int totallines, const int rowlines,\n                                               const int skiplines, void **commbuff,\n                                               const int handleridx, void *outbuf, float4 *mc_ptr,\n                                               const uint64_t ub_timeout) {}\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, uint4 *mc_ptr,\n                                           const uint64_t ub_timeout) {}\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_mc_rs(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx, float4 *mc_ptr,\n                                           const uint64_t ub_timeout) {}\n#endif\n\ntemplate <int RANKS, typename fp8type>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_fp8(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, float *scale, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  half_dtype hscale = (half_dtype)*scale;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  __syncthreads();\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line];\n    }\n\n    int4 sum[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      fp8type *x = reinterpret_cast<fp8type *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < sizeof(int4) / sizeof(fp8type); j++) s[j] += hscale * (half_dtype)(x[j]);\n    }\n    int hline = 2 * line;\n    (reinterpret_cast<int4 *>(outbuf))[(hline / rowlines) * skiplines + (hline % rowlines)] =\n        sum[0];\n    hline++;\n    (reinterpret_cast<int4 *>(outbuf))[(hline / rowlines) * skiplines + (hline % rowlines)] =\n        sum[1];\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) (fp8->fp16)\n\ntemplate <int RANKS, typename fp8type>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_atomic_fp8(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines_out, const int skiplines_in, void **commbuff, const int handleridx,\n        void *outbuf, float *scale, void *counters, const int numchunks, const int atomicindex,\n        const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n  half_dtype hscale = (half_dtype)*scale;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    // const int blockflagoffset = MAX_NVLINK * 2 * blockIdx.x;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr);\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;  // + blockflagoffset;\n  }\n\n  for (int chunk_i = 0; chunk_i < numchunks; chunk_i++) {\n    ATOMIC_CONSUMER(chunk_i);\n\n    lastSM = 0;\n    if (threadIdx.x < RANKS) {\n      reduce_id++;\n      if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n      volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n      userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, reduce_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                   threadIdx.x, reduce_id, *flag);\n          break;\n        }\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x == 0) {\n      const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n      int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), /*numchunks * */ adder);\n      if (old_val + adder == NVTE_MAX_SMS * (reduce_id /* + numchunks*/)) lastSM = 1;\n    }\n\n    int warp = blockIdx.x + (threadIdx.x >> 5);\n    int dest[RANKS];\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n    __syncthreads();\n    for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n         line += blockDim.x * gridDim.x) {\n      int4 val[RANKS];\n      const int rowlines_in = rowlines / 2;\n      const int index_in = skiplines_in == 0\n                               ? mylineoffset + myrank * totallines + line\n                               : (numchunks <= 1 ? 1 : chunk_i) * mylineoffset +\n                                     myrank * (totallines * skiplines_in / rowlines_in) +\n                                     (line / rowlines_in) * skiplines_in + (line % rowlines_in);\n      const int index1_out = chunk_i * mylineoffset * 2 + ((2 * line) / rowlines) * skiplines_out +\n                             ((2 * line) % rowlines);\n      const int index2_out = chunk_i * mylineoffset * 2 +\n                             ((2 * line + 1) / rowlines) * skiplines_out +\n                             ((2 * line + 1) % rowlines);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        val[i] = userptr[dest[i]][index_in];\n      }\n\n      int4 sum[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};\n      half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        fp8type *x = reinterpret_cast<fp8type *>(&val[i]);\n#pragma unroll\n        for (int j = 0; j < sizeof(int4) / sizeof(fp8type); j++)\n          s[j] += hscale * (half_dtype)(x[j]);\n      }\n      (reinterpret_cast<int4 *>(outbuf))[index1_out] = sum[0];\n      (reinterpret_cast<int4 *>(outbuf))[index2_out] = sum[1];\n    }\n  }\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) (fp8->fp16)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS) userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride(\n    const int op, const int flagoffset, const int firstrank, const int myrank, const int gpustep,\n    const int mylineoffset, const int totallines, const int rowlines, const int skiplines,\n    void **commbuff, const int handleridx, void *outbuf, const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n    int index_in = mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                   (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][index_in];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    int index_out = (line / rowlines) * skiplines + (line % rowlines);\n    (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_atomic(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines, const int numchunks, void **commbuff, const int handleridx,\n        void *outbuf, void *counters, const uint64_t ub_timeout) {\n  if (counters) {\n    if (threadIdx.x == 0) {\n      // spin-lock on counter from producer\n      while (0 != (atomicCAS(((unsigned int *)counters), 0, 0))) {\n      }\n\n      // make sure all threadblocks have read/waited on counters.\n      atomicInc(((unsigned int *)counters) + numchunks, gridDim.x - 1);\n      while (0 != (atomicCAS(((unsigned int *)counters) + numchunks, 0, 0))) {\n      }\n\n      // reset counter for next producer.\n      ((unsigned int *)counters)[0] = 1;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n  __syncthreads();\n\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int lastSM = 0;\n\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                 threadIdx.x, reduce_id, *flag);\n        break;\n      }\n    }\n  }\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS];\n    int index_in = mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                   (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) {\n      val[i] = userptr[dest[i]][index_in];\n    }\n\n    int4 sum = val[0];\n    half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n    for (int i = 1; i < RANKS; i++) {\n      half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n      for (int j = 0; j < 8; j++) s[j] += x[j];\n    }\n\n    int index_out = (line / rowlines) * skiplines + (line % rowlines);\n    (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n  }\n\n  if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_multiatomic(\n        const int op, const int flagoffset, const int firstrank, const int myrank,\n        const int gpustep, const int mylineoffset, const int totallines, const int rowlines,\n        const int skiplines, const int numchunks, void **commbuff, const int handleridx,\n        void *outbuf, void *counters, const uint64_t ub_timeout) {\n  for (int chunk_i = 0; chunk_i < numchunks; chunk_i++) {\n    if (counters) {\n      if (threadIdx.x == 0) {\n        // spin-lock on counter from producer\n        while (0 != (atomicCAS(((unsigned int *)counters) + chunk_i, 0, 0))) {\n        }\n\n        // make sure all threadblocks have read/waited on counters.\n        atomicInc(((unsigned int *)counters) + numchunks + chunk_i, gridDim.x - 1);\n        while (0 != (atomicCAS(((unsigned int *)counters) + numchunks + chunk_i, 0, 0))) {\n        }\n\n        // reset counter for next producer.\n        ((unsigned int *)counters)[chunk_i] = 1;\n        asm volatile(\"fence.sc.gpu;\\n\");\n      }\n    }\n    __syncthreads();\n\n    __shared__ int4 *userptr[RANKS];\n    volatile int *flagptr;\n    int physgpu, targetgpu, *myptr;\n    int *reduceidptr, reduce_id;\n    int lastSM = 0;\n\n    if (threadIdx.x < RANKS) {\n      physgpu = myrank * gpustep + firstrank;\n      targetgpu = threadIdx.x * gpustep + firstrank;\n      myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n      reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n      reduce_id = (*reduceidptr) + 1;\n      flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n      if (blockIdx.x == 0) flagptr[physgpu] = reduce_id;\n      volatile int *flag = (volatile int *)&(myptr[targetgpu]);\n      userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, reduce_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\"[%d] Reduce-scatter: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x,\n                   threadIdx.x, reduce_id, *flag);\n          break;\n        }\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x == 0) {\n      const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n      int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n      if (old_val + adder == NVTE_MAX_SMS * reduce_id) lastSM = 1;\n    }\n\n    int warp = blockIdx.x + (threadIdx.x >> 5);\n    int dest[RANKS];\n#pragma unroll\n    for (int i = 0; i < RANKS; i++) dest[i] = (i + myrank + warp) & (RANKS - 1);\n\n    for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n         line += blockDim.x * gridDim.x) {\n      int4 val[RANKS];\n      int index_in = chunk_i * mylineoffset + myrank * (totallines * skiplines / rowlines) +\n                     (line / rowlines) * skiplines + (line % rowlines);\n\n#pragma unroll\n      for (int i = 0; i < RANKS; i++) {\n        val[i] = userptr[dest[i]][index_in];\n      }\n\n      int4 sum = val[0];\n      half_dtype *s = reinterpret_cast<half_dtype *>(&sum);\n\n#pragma unroll\n      for (int i = 1; i < RANKS; i++) {\n        half_dtype *x = reinterpret_cast<half_dtype *>(&val[i]);\n#pragma unroll\n        for (int j = 0; j < 8; j++) s[j] += x[j];\n      }\n\n      int index_out = chunk_i * mylineoffset + (line / rowlines) * skiplines + (line % rowlines);\n      (reinterpret_cast<int4 *>(outbuf))[index_out] = sum;\n    }\n    if (threadIdx.x == 0 && lastSM) *reduceidptr = reduce_id;\n  }\n}  // fp16 reduce-scatter kernel (out of place) fp16\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rr_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n    clock_t s = clock64();\n  }\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS];\n\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n  __syncthreads();\n\n  for (int line = threadIdx.x + blockDim.x * blockIdx.x; line < totallines;\n       line += blockDim.x * gridDim.x) {\n    int4 val[RANKS - 1];\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      val[i] = userptr[dest[i]][mylineoffset + line + totallines * dest[i]];\n    }\n\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[myrank][mylineoffset + line + totallines * dest[i]] = val[i];\n    }\n  }\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace reduce kernel (Ampere)\n\ntemplate <int RANKS>\n__global__ void __launch_bounds__(MAX_THREADS)\n    userbuffers_fp16_sum_inplace_gpu_rw_ag(const int op, const int flagoffset, const int firstrank,\n                                           const int myrank, const int gpustep,\n                                           const int mylineoffset, const int totallines,\n                                           void **commbuff, const int handleridx,\n                                           const uint64_t ub_timeout) {\n  __shared__ int4 *userptr[RANKS];\n  volatile int *flagptr;\n  int physgpu, targetgpu, *myptr;\n  int *reduceidptr, reduce_id;\n  int4 *localptr;\n  if (threadIdx.x < RANKS) {\n    physgpu = myrank * gpustep + firstrank;\n    targetgpu = threadIdx.x * gpustep + firstrank;\n    myptr = (reinterpret_cast<int *>(commbuff[physgpu])) + flagoffset;\n    reduceidptr = myptr - NVTE_MAX_OPS;  // +op;\n    reduce_id = (*reduceidptr) + 1;\n    flagptr = (reinterpret_cast<int *>(commbuff[targetgpu])) + flagoffset;\n    userptr[threadIdx.x] = reinterpret_cast<int4 *>(commbuff[targetgpu + handleridx]);\n  }\n  __syncthreads();\n  localptr = userptr[myrank];\n\n  int warp = blockIdx.x + (threadIdx.x >> 5);\n  int dest[RANKS - 1];\n  int skipmy = 0;\n#pragma unroll\n  for (int i = 0; i < RANKS; i++) {\n    int dst = (i + warp + myrank) & (RANKS - 1);\n    if (dst == myrank) {\n      skipmy++;\n      continue;\n    }\n    dest[i - skipmy] = dst;\n  }\n#define UNROLLAG 4\n  __syncthreads();\n  const int loop_step0 = blockDim.x * gridDim.x;\n  const int loop_step = loop_step0 * UNROLLAG;\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = max(start_elem, totallines);\n  const int aligned_elem = ((end_elem - start_elem) / loop_step) * loop_step;\n  const int end_aligned = start_elem + aligned_elem;\n\n  for (int line = start_elem; line < end_aligned; line += loop_step) {\n    int4 val[UNROLLAG];\n#pragma unroll\n    for (int j = 0; j < UNROLLAG; j++) val[j] = localptr[mylineoffset + line + loop_step0 * j];\n\n#pragma unroll\n    for (int j = 0; j < UNROLLAG; j++)\n#pragma unroll\n      for (int i = 0; i < RANKS - 1; i++) {\n        userptr[dest[i]][mylineoffset + line + j * loop_step0] = val[j];\n      }\n  }\n\n  for (int line = end_aligned; line < end_elem; line += loop_step0) {\n    int4 sum = localptr[mylineoffset + line];\n#pragma unroll\n    for (int i = 0; i < RANKS - 1; i++) {\n      userptr[dest[i]][mylineoffset + line] = sum;\n    }\n  }\n\n  __syncthreads();\n  if (threadIdx.x == 0) __threadfence_system();\n  __syncthreads();\n\n  __shared__ int lastSM;\n  if (threadIdx.x == 0) {\n    const int adder = blockIdx.x == 0 ? NVTE_MAX_SMS - gridDim.x + 1 : 1;\n    int old_val = atomicAdd(myptr + (NVTE_MAX_NVLINK * 2), adder);\n    if (old_val + adder == NVTE_MAX_SMS * reduce_id)\n      lastSM = 1;\n    else\n      lastSM = 0;\n  }\n  __syncthreads();\n  if (lastSM && threadIdx.x < RANKS) {\n    if (threadIdx.x == 0) *reduceidptr = reduce_id;\n    flagptr[physgpu] = reduce_id;\n    volatile int *flag = (volatile int *)&myptr[targetgpu];\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, reduce_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\"[%d] Allgather: SM %d [%d]: expecting %d got %d\", myrank, blockIdx.x, threadIdx.x,\n                 reduce_id, *flag);\n        break;\n      }\n    }\n  }\n}  // fp16 inplace allgather kernel (Volta,Hopper)\n\n#define SETUP_LAUNCH_CONFIG(sms, threads, stream)                                    \\\n  hipLaunchConfig_t cfg = {sms, threads, 0, stream, NULL, 0};                       \\\n  hipLaunchAttribute attribute_ub[2];                                               \\\n  attribute_ub[1].id = cudaLaunchAttributeClusterDimension;                          \\\n  attribute_ub[1].val.clusterDim.x = sms % comm->cga_size == 0 ? comm->cga_size : 1; \\\n  attribute_ub[1].val.clusterDim.y = 1;                                              \\\n  attribute_ub[1].val.clusterDim.z = 1;                                              \\\n  attribute_ub[0].id = hipLaunchAttributeCooperative;                               \\\n  cfg.attrs = attribute_ub;                                                          \\\n  cfg.numAttrs = comm->sm_arch >= 9 ? 2 : 1;\n\n#if (CUDART_VERSION >= 12030)\n#define ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event) \\\n  attribute_ub[2].id = cudaLaunchAttributeLaunchCompletionEvent;     \\\n  attribute_ub[2].val.launchCompletionEvent.event = comm_launch_event;\n#define NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH 3\n#else\n#define ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event)\n#define NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH 2\n#endif\n\n#define SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, threads, stream, comm_launch_event) \\\n  hipLaunchConfig_t cfg = {sms, threads, 0, stream, NULL, 0};                             \\\n  hipLaunchAttribute attribute_ub[NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH] = {};              \\\n  ADD_LAUNCH_COMPLETION_EVENT(attribute_ub, comm_launch_event)                             \\\n  attribute_ub[1].id = cudaLaunchAttributeClusterDimension;                                \\\n  attribute_ub[1].val.clusterDim.x = sms % comm->cga_size == 0 ? comm->cga_size : 1;       \\\n  attribute_ub[1].val.clusterDim.y = 1;                                                    \\\n  attribute_ub[1].val.clusterDim.z = 1;                                                    \\\n  attribute_ub[0].id = hipLaunchAttributeCooperative;                                     \\\n  cfg.attrs = attribute_ub;                                                                \\\n  cfg.numAttrs = NUM_LAUNCH_ATTRIBUTE_FOR_FDL_LAUNCH;\n\n#define callranks_ag(x)                                                                            \\\n  if (ar_nvsize == x) {                                                                            \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                  \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                            \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +           \\\n               NVTE_MAX_OPS,                                                                       \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,             \\\n        arg6 = offset / 8 + (comm->use_rr_kernel ? 0 : arg4 * arg7);                               \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                       \\\n    int arg9 = handler * comm->nvsize;                                                             \\\n    uint64_t arg10 = comm->ub_timeout;                                                             \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),        \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),        \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),        \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),        \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10)};      \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                           \\\n        &cfg,                                                                                      \\\n        reinterpret_cast<void *>(comm->use_rr_kernel ? userbuffers_fp16_sum_inplace_gpu_rr_ag<x>   \\\n                                                     : userbuffers_fp16_sum_inplace_gpu_rw_ag<x>), \\\n        kernelArgs));                                                                              \\\n  }\n\n#define callranks_agMC(x)                                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    uint4 *arg10 = reinterpret_cast<uint4 *>(comm->mc_ptr[handler]);                             \\\n    uint64_t arg11 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10),     \\\n                          reinterpret_cast<void *>(&arg11)};                                     \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_ag<x>), kernelArgs)); \\\n  }\n\n#define callranks_rs(x)                                                                          \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    uint64_t arg10 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10)};    \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs<x>), kernelArgs)); \\\n  }\n\n#define callranks_rsMC(x)                                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8 + arg4 * arg7;                                                         \\\n    void **arg8 = reinterpret_cast<void **>(comm->gpu_ptrs);                                     \\\n    int arg9 = handler * comm->nvsize;                                                           \\\n    void *arg10 = comm->mc_ptr[handler];                                                         \\\n    uint64_t arg11 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),      \\\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),      \\\n                          reinterpret_cast<void *>(&arg5), reinterpret_cast<void *>(&arg6),      \\\n                          reinterpret_cast<void *>(&arg7), reinterpret_cast<void *>(&arg8),      \\\n                          reinterpret_cast<void *>(&arg9), reinterpret_cast<void *>(&arg10),     \\\n                          reinterpret_cast<void *>(&arg11)};                                     \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                         \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_rs<x>), kernelArgs)); \\\n  }\n\n#define callranks_rs_oop(x)                                                                   \\\n  if (ar_nvsize == x) {                                                                       \\\n    int arg1 = op - NVTE_MAX_OPS,                                                             \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                       \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +      \\\n               NVTE_MAX_OPS,                                                                  \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,        \\\n        arg6 = offset / 8 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;   \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                 \\\n    int arg11 = handler * comm->nvsize;                                                       \\\n    void *arg12 = output;                                                                     \\\n    uint64_t arg13 = comm->ub_timeout;                                                        \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),  \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),  \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),  \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),  \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10), \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12), \\\n                          reinterpret_cast<void *>(&arg13)};                                  \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                      \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop<x>),        \\\n        kernelArgs));                                                                         \\\n  }\n\n#define callranks_rs_oop_fp8(x)                                                                \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 16 / x,        \\\n        arg6 = offset / 16 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;   \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg11 = handler * comm->nvsize;                                                        \\\n    void *arg12 = output;                                                                      \\\n    float *arg13 = scale;                                                                      \\\n    uint64_t arg14 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14)}; \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                       \\\n        &cfg,                                                                                  \\\n        reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_fp8<x, fp8type>),  \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oopMC(x)                                                                  \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,         \\\n        arg6 = offset / 8 + arg4 * arg7, arg8 = rowelements / 8, arg9 = strideelements / 8;    \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg11 = handler * comm->nvsize;                                                        \\\n    void *arg12 = output;                                                                      \\\n    void *arg13 = comm->mc_ptr[handler];                                                       \\\n    uint64_t arg14 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14)}; \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                       \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_mc_rs_oop<x>),         \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oop_atomic_fp8(x)                                                         \\\n  if (ar_nvsize == x) {                                                                        \\\n    int arg1 = op - NVTE_MAX_OPS,                                                              \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                        \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +       \\\n               NVTE_MAX_OPS,                                                                   \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 16 / x,        \\\n        arg6 = offset / 16, arg8 = rowelements / 8, arg9 = strideelements_out / 8,             \\\n        arg10 = strideelements_in / 16;                                                        \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                  \\\n    int arg12 = handler * comm->nvsize;                                                        \\\n    void *arg13 = output;                                                                      \\\n    float *arg14 = scale;                                                                      \\\n    void *arg15 = counters;                                                                    \\\n    int arg16 = numchunks, arg17 = atomicindex;                                                \\\n    uint64_t arg18 = comm->ub_timeout;                                                         \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),   \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),   \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),   \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),   \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),  \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),  \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),  \\\n                          reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16),  \\\n                          reinterpret_cast<void *>(&arg17), reinterpret_cast<void *>(&arg18)}; \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                       \\\n        &cfg,                                                                                  \\\n        reinterpret_cast<void *>(                                                              \\\n            userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_atomic_fp8<x, fp8type>),                \\\n        kernelArgs));                                                                          \\\n  }\n\n#define callranks_rs_oop_stride(x)                                                            \\\n  if (ar_nvsize == x) {                                                                       \\\n    int arg1 = op - NVTE_MAX_OPS,                                                             \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                       \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +      \\\n               NVTE_MAX_OPS,                                                                  \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,        \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8;                 \\\n    void **arg10 = reinterpret_cast<void **>(comm->gpu_ptrs);                                 \\\n    int arg11 = handler * comm->nvsize;                                                       \\\n    void *arg12 = output;                                                                     \\\n    uint64_t arg13 = comm->ub_timeout;                                                        \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),  \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),  \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),  \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),  \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10), \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12), \\\n                          reinterpret_cast<void *>(&arg13)};                                  \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                      \\\n        &cfg, reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride<x>), \\\n        kernelArgs));                                                                         \\\n  }\n\n#define callranks_rs_oop_stride_atomic(x)                                                        \\\n  if (ar_nvsize == x) {                                                                          \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                          \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +         \\\n               NVTE_MAX_OPS,                                                                     \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,           \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8, arg10 = numchunks; \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                    \\\n    int arg12 = handler * comm->nvsize;                                                          \\\n    void *arg13 = output;                                                                        \\\n    void *arg14 = counters;                                                                      \\\n    uint64_t arg15 = comm->ub_timeout;                                                           \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),     \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),     \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),     \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),     \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),    \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),    \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),    \\\n                          reinterpret_cast<void *>(&arg15)};                                     \\\n    NVTE_CHECK_CUDA(hipLaunchKernelExC(                                                         \\\n        &cfg,                                                                                    \\\n        reinterpret_cast<void *>(userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_atomic<x>),   \\\n        kernelArgs));                                                                            \\\n  }\n\n#define callranks_rs_oop_stride_multiatomic(x)                                                     \\\n  if (ar_nvsize == x) {                                                                            \\\n    int arg1 = op - NVTE_MAX_OPS,                                                                  \\\n        arg2 = NVTE_REG0_OFFSET(comm) -                                                            \\\n               (op == userbuffers_allreduceop_nonsharp ? 2 : 1) * NVTE_REG0_SINGLENODE +           \\\n               NVTE_MAX_OPS,                                                                       \\\n        arg3 = ar_firstgpu, arg4 = ar_nvrank, arg5 = ar_step, arg7 = elements / 8 / x,             \\\n        arg6 = offset / 8, arg8 = rowelements / 8, arg9 = strideelements / 8, arg10 = numchunks;   \\\n    void **arg11 = reinterpret_cast<void **>(comm->gpu_ptrs);                                      \\\n    int arg12 = handler * comm->nvsize;                                                            \\\n    void *arg13 = output;                                                                          \\\n    void *arg14 = counters;                                                                        \\\n    uint64_t arg15 = comm->ub_timeout;                                                             \\\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),       \\\n                          reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),       \\\n                          reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),       \\\n                          reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),       \\\n                          reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),      \\\n                          reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),      \\\n                          reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),      \\\n                          reinterpret_cast<void *>(&arg15)};                                       \\\n    NVTE_CHECK_CUDA(                                                                               \\\n        hipLaunchKernelExC(&cfg,                                                                  \\\n                            reinterpret_cast<void *>(                                              \\\n                                userbuffers_fp16_sum_inplace_gpu_rr_rs_oop_stride_multiatomic<x>), \\\n                            kernelArgs));                                                          \\\n  }\n\nvoid reducescatter2_userbuff_strided(void *output, const int handler, const int offset,\n                                     const int rowelements, const int colelements,\n                                     const int strideelements, communicator *comm,\n                                     hipStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride(2) callranks_rs_oop_stride(4) callranks_rs_oop_stride(8)\n      callranks_rs_oop_stride(16) callranks_rs_oop_stride(32)\n}\nvoid reducescatter2_userbuff_strided_atomic(void *output, const int handler, const int offset,\n                                            const int rowelements, const int colelements,\n                                            const int strideelements, const int numchunks,\n                                            void *counters, communicator *comm,\n                                            hipStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride_atomic(2) callranks_rs_oop_stride_atomic(4)\n      callranks_rs_oop_stride_atomic(8) callranks_rs_oop_stride_atomic(16)\n          callranks_rs_oop_stride_atomic(32)\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_universal_fp8(void *output, float *scale, const int handler,\n                                                   const int offset, const int rowelements,\n                                                   const int colelements,\n                                                   const int strideelements_out,\n                                                   const int strideelements_in, const int numchunks,\n                                                   const int atomicindex, void *counters,\n                                                   communicator *comm, hipStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  assert(comm->sm_arch >= 9);\n  if (elements < 128) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_atomic_fp8(2) callranks_rs_oop_atomic_fp8(4) callranks_rs_oop_atomic_fp8(8)\n      callranks_rs_oop_atomic_fp8(16) callranks_rs_oop_atomic_fp8(32)\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_atomic_fp8(void *output, float *scale, const int handler,\n                                                const int offset, const int rowelements,\n                                                const int colelements, const int strideelements_out,\n                                                const int strideelements_in, const int numchunks,\n                                                void *counters, communicator *comm,\n                                                hipStream_t stream) {\n  reducescatter2_userbuff_strided_universal_fp8<fp8type>(\n      output, scale, handler, offset, rowelements, colelements, strideelements_out,\n      strideelements_in, 1, numchunks, counters /*nullptr*/, comm, stream);\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_strided_multiatomic_fp8(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, hipStream_t stream) {\n  reducescatter2_userbuff_strided_universal_fp8<fp8type>(\n      output, scale, handler, offset, rowelements, colelements, strideelements_out,\n      strideelements_in, numchunks, 0, counters /*nullptr*/, comm, stream);\n}\n\nvoid reducescatter2_userbuff_strided_multiatomic(void *output, const int handler, const int offset,\n                                                 const int rowelements, const int colelements,\n                                                 const int strideelements, const int numchunks,\n                                                 void *counters, communicator *comm,\n                                                 hipStream_t stream) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n  callranks_rs_oop_stride_multiatomic(2) callranks_rs_oop_stride_multiatomic(4)\n      callranks_rs_oop_stride_multiatomic(8) callranks_rs_oop_stride_multiatomic(16)\n          callranks_rs_oop_stride_multiatomic(32)\n}\n\nvoid allgather2_userbuff_inplace(const int handler, const int offset, const int elements,\n                                 communicator *comm, hipStream_t stream,\n                                 hipEvent_t comm_launch_event) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_agMC(2) callranks_agMC(4) callranks_agMC(8) callranks_agMC(16) callranks_agMC(32)\n    } else {\n      callranks_ag(2) callranks_ag(4) callranks_ag(8) callranks_ag(16) callranks_ag(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_agMC(2) callranks_agMC(4) callranks_agMC(8) callranks_agMC(16) callranks_agMC(32)\n    } else {\n      callranks_ag(2) callranks_ag(4) callranks_ag(8) callranks_ag(16) callranks_ag(32)\n    }\n  }\n}\n\nvoid allgather2_userbuff_inplace_sliced(const int handler, const int offset, const int elements,\n                                        communicator *comm, const int slice_id, const int nslices,\n                                        hipStream_t stream) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  int peerelements = elements / ar_nvsize;\n  int saverrkernel = comm->use_rr_kernel;\n  comm->use_rr_kernel = 0;\n  allgather2_userbuff_inplace(\n      handler, offset + ar_nvrank * peerelements * (nslices - 1) + slice_id * peerelements,\n      elements, comm, stream);\n  comm->use_rr_kernel = saverrkernel;\n}\n\nvoid reducescatter2_userbuff_inplace(const int handler, const int offset, const int elements,\n                                     communicator *comm, hipStream_t stream,\n                                     hipEvent_t comm_launch_event) {\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rsMC(2) callranks_rsMC(4) callranks_rsMC(8) callranks_rsMC(16) callranks_rsMC(32)\n    } else {\n      callranks_rs(2) callranks_rs(4) callranks_rs(8) callranks_rs(16) callranks_rs(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rsMC(2) callranks_rsMC(4) callranks_rsMC(8) callranks_rsMC(16) callranks_rsMC(32)\n    } else {\n      callranks_rs(2) callranks_rs(4) callranks_rs(8) callranks_rs(16) callranks_rs(32)\n    }\n  }\n}\nvoid reducescatter2_userbuff_stridedoutput(void *output, const int handler, const int offset,\n                                           const int rowelements, const int colelements,\n                                           const int strideelements, communicator *comm,\n                                           hipStream_t stream, hipEvent_t comm_launch_event) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n\n  if (elements < 64) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rs_oopMC(2) callranks_rs_oopMC(4) callranks_rs_oopMC(8) callranks_rs_oopMC(16)\n          callranks_rs_oopMC(32)\n    } else {\n      callranks_rs_oop(2) callranks_rs_oop(4) callranks_rs_oop(8) callranks_rs_oop(16)\n          callranks_rs_oop(32)\n    }\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    if (comm->use_mc && (comm->memflags[handler] & NVTE_UB_MEM_MC_CREATED)) {\n      callranks_rs_oopMC(2) callranks_rs_oopMC(4) callranks_rs_oopMC(8) callranks_rs_oopMC(16)\n          callranks_rs_oopMC(32)\n    } else {\n      callranks_rs_oop(2) callranks_rs_oop(4) callranks_rs_oop(8) callranks_rs_oop(16)\n          callranks_rs_oop(32)\n    }\n  }\n}\nvoid reducescatter2_userbuff(void *output, const int handler, const int offset, const int elements,\n                             communicator *comm, hipStream_t stream,\n                             hipEvent_t comm_launch_event) {\n  reducescatter2_userbuff_stridedoutput(output, handler, offset, elements, 1, 0, comm, stream,\n                                        comm_launch_event);\n}\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_stridedoutput_fp8(void *output, float *scale, const int handler,\n                                               const int offset, const int rowelements,\n                                               const int colelements, const int strideelements,\n                                               communicator *comm, hipStream_t stream,\n                                               hipEvent_t comm_launch_event) {\n  const int elements = rowelements * colelements;\n  const int op = userbuffers_allreduceop_nonsharp2;\n  const int ar_firstgpu =\n      op == userbuffers_allreduceop_nonsharp ? comm->ar_firstgpu : comm->ar2_firstgpu;\n  const int ar_step = op == userbuffers_allreduceop_nonsharp2 ? 1 : comm->ar2_nvsize;\n  const int ar_nvsize = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvsize : comm->ar2_nvsize;\n  const int ar_nvrank = op == userbuffers_allreduceop_nonsharp ? comm->ar_nvrank : comm->ar2_nvrank;\n  assert(comm->sm_arch >= 9);\n  if (elements < 128) return;\n  int sms = ar_nvsize == 1 ? 2 : comm->sms;\n  int warps = comm->threads / 32;\n  if (warps < ar_nvsize) warps = ar_nvsize;\n\n  if (comm_launch_event) {\n    SETUP_LAUNCH_CONFIG_WITH_COMPLETION_EVENT(sms, warps * 32, stream, comm_launch_event);\n    callranks_rs_oop_fp8(2) callranks_rs_oop_fp8(4) callranks_rs_oop_fp8(8) callranks_rs_oop_fp8(16)\n        callranks_rs_oop_fp8(32)\n  } else {\n    SETUP_LAUNCH_CONFIG(sms, warps * 32, stream);\n    callranks_rs_oop_fp8(2) callranks_rs_oop_fp8(4) callranks_rs_oop_fp8(8) callranks_rs_oop_fp8(16)\n        callranks_rs_oop_fp8(32)\n  }\n}\n\ntemplate void reducescatter2_userbuff_stridedoutput_fp8<__hip_fp8_e5m2_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements, communicator *comm, hipStream_t stream,\n    hipEvent_t comm_launch_event);\n\ntemplate void reducescatter2_userbuff_stridedoutput_fp8<__hip_fp8_e4m3_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements, communicator *comm, hipStream_t stream,\n    hipEvent_t comm_launch_event);\n\ntemplate <typename fp8type>\nvoid reducescatter2_userbuff_fp8(void *output, float *scale, const int handler, const int offset,\n                                 const int elements, communicator *comm, hipStream_t stream,\n                                 hipEvent_t comm_launch_event) {\n  reducescatter2_userbuff_stridedoutput_fp8<fp8type>(output, scale, handler, offset, elements, 1, 0,\n                                                     comm, stream, comm_launch_event);\n}\n\ntemplate void reducescatter2_userbuff_fp8<__hip_fp8_e5m2_fnuz>(void *output, float *scale,\n                                                         const int handler, const int offset,\n                                                         const int elements, communicator *comm,\n                                                         hipStream_t stream,\n                                                         hipEvent_t comm_launch_event);\ntemplate void reducescatter2_userbuff_fp8<__hip_fp8_e4m3_fnuz>(void *output, float *scale,\n                                                         const int handler, const int offset,\n                                                         const int elements, communicator *comm,\n                                                         hipStream_t stream,\n                                                         hipEvent_t comm_launch_event);\n\ntemplate void reducescatter2_userbuff_strided_atomic_fp8<__hip_fp8_e4m3_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, hipStream_t stream);\ntemplate void reducescatter2_userbuff_strided_atomic_fp8<__hip_fp8_e5m2_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, hipStream_t stream);\ntemplate void reducescatter2_userbuff_strided_multiatomic_fp8<__hip_fp8_e4m3_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, hipStream_t stream);\ntemplate void reducescatter2_userbuff_strided_multiatomic_fp8<__hip_fp8_e5m2_fnuz>(\n    void *output, float *scale, const int handler, const int offset, const int rowelements,\n    const int colelements, const int strideelements_out, const int strideelements_in,\n    const int numchunks, void *counters, communicator *comm, hipStream_t stream);\n\n__global__ void kuserbuffers_pullsend(int myrank, int peer, int *send_id, int *flagptr) {\n  atomicAdd_system(flagptr, 1);\n}\n\n__global__ void kuserbuffers_inc(int *id) { atomicAdd(id, 1); }\n\n__global__ void kuserbuffers_dummy(void) {}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pullrecv(int myrank, int peer, int nvrank, int nvpeer, int *recv_id, int *flagptr,\n                          int4 *srcptr, int4 *dstptr, const int lines, uint64_t ub_timeout) {\n#define UNROLLCOPY 8\n  const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n  const int end_elem = lines;\n  const int aligned_elem = (end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1));\n  const int end_aligned = start_elem + aligned_elem;\n\n  if (threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + 1;\n    volatile int *flag = (volatile int *)flagptr;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pullrecv [grank dst:%d global src:%d][nvrank(GPU) dst: %d src: %d]: expecting %d,\"\n            \" observed %d\",\n            myrank, peer, nvrank, nvpeer, signal_id, *flag);\n        break;\n      }\n    }\n    if (lines == 0) {\n      *recv_id = signal_id;\n      return;\n    }  // otherwise need an extra kernel\n  }\n  __syncthreads();\n\n  if (end_elem <= start_elem) return;\n\n  for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n    int4 val[UNROLLCOPY];\n#pragma unroll\n    for (int i = 0; i < UNROLLCOPY; i++) val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n#pragma unroll\n    for (int i = 0; i < UNROLLCOPY; i++) dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n  }\n  for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x)\n    dstptr[line] = srcptr[line];\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsend(int *send_id, int *flagptr, int4 *srcptr, int4 *dstptr, const int lines) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x)\n        dstptr[line] = srcptr[line];\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(flagptr, 1);\n  }\n}\n\n#define CHECK_CE(ce_start, ce_end) \\\n  ((ce_start) != nullptr && (ce_end) != nullptr && *(ce_start) != *(ce_end))\n\n__global__ void kuserbuffers_pushrecv(int myrank, int peer, int nvrank, int nvpeer, int *recv_id,\n                                      int *flagptr, int adder, uint64_t ub_timeout,\n                                      int *ce_start_ptr, int *ce_end_ptr) {\n  const int signal_id = (*recv_id) + adder;\n  *recv_id = signal_id;\n  volatile int *flag = (volatile int *)flagptr;\n  if (*flag >= signal_id) return;\n  clock_t s = clock64();\n  while (CHECK_IDS(*flag, signal_id)) {\n    if (CHECK_TIMEOUT(s, ub_timeout)) {\n      UB_PRINT(\n          \"pushrecv [grank dst:%d global src:%d][nvrank(GPU) dst: %d src: %d]: \"\n          \"expecting %d, observed %d\",\n          myrank, peer, nvrank, nvpeer, signal_id, *flag);\n      if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n        UB_PRINT(\"pushrecv: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\", *ce_start_ptr,\n                 *ce_end_ptr);\n      return;\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsendrecv(int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr,\n                              const int lines, int send_peer, int recv_peer, int *recv_id,\n                              int *recv_flagptr, int adder, uint64_t ub_timeout, int nv_send,\n                              int nv_recv, int *ce_start_ptr, int *ce_end_ptr) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n        }\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n        }\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n        dstptr[line] = srcptr[line];\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(send_flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(send_flagptr, 1);\n  }\n\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + adder;\n    *recv_id = signal_id;\n    volatile int *flag = (volatile int *)recv_flagptr;\n    if (*flag >= signal_id) return;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pushsendrecv [sending peer:%d receiving peer:%d][nvrank(GPU) sending peer: %d\"\n            \" receiving peer: %d]: expecting %d, observed %d\",\n            send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag);\n        if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n          UB_PRINT(\"pushrecv: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\", *ce_start_ptr,\n                   *ce_end_ptr);\n        return;\n      }\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS)\n    kuserbuffers_pushsendrecv_atomic(int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr,\n                                     const int lines, int send_peer, int recv_peer, int *recv_id,\n                                     int *recv_flagptr, int adder, void *counters,\n                                     uint64_t ub_timeout, int nv_send, int nv_recv,\n                                     int *ce_start_ptr, int *ce_end_ptr) {\n  if (lines) {\n    const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n    const int end_elem = lines;\n    const int aligned_elem =\n        ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n    const int end_aligned = start_elem + aligned_elem;\n    if (end_elem > start_elem) {\n      for (int line = start_elem; line < end_aligned; line += blockDim.x * gridDim.x * UNROLLCOPY) {\n        int4 val[UNROLLCOPY];\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          val[i] = srcptr[line + i * blockDim.x * gridDim.x];\n        }\n#pragma unroll\n        for (int i = 0; i < UNROLLCOPY; i++) {\n          dstptr[line + i * blockDim.x * gridDim.x] = val[i];\n        }\n      }\n      for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n        dstptr[line] = srcptr[line];\n      }\n    }\n    __syncthreads();\n    if (threadIdx.x) return;\n    __threadfence_system();\n    atomicAdd_system(send_flagptr,\n                     1);  // otherwise need local SM sync before sending flag\n  } else {                // 0 bytes and 1 SM only\n    atomicAdd_system(send_flagptr, 1);\n  }\n\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    const int signal_id = (*recv_id) + adder;\n    *recv_id = signal_id;\n    volatile int *flag = (volatile int *)recv_flagptr;\n    clock_t s = clock64();\n    while (CHECK_IDS(*flag, signal_id)) {\n      if (CHECK_TIMEOUT(s, ub_timeout)) {\n        UB_PRINT(\n            \"pushsendrecv atomic [sending peer:%d receiving peer:%d][nvrank(GPU) sending peer:\"\n            \" %d receiving peer: %d]: expecting %d, observed %d\",\n            send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag); /*return;*/\n        if (CHECK_CE(ce_start_ptr, ce_end_ptr))\n          UB_PRINT(\"pushsendrecv atomic: CE deadlock DETECTED: %d (ce_start) != %d (ce_end)\\n\",\n                   *ce_start_ptr, *ce_end_ptr);\n      }\n    }\n\n    // Decrement atomic val to signal current output tile finish\n    if (counters) {\n      ((unsigned int *)counters)[0] = 0;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n}\n\n__global__ void __launch_bounds__(MAX_THREADS) kuserbuffers_pushsendrecv_multiatomic(\n    int *send_id, int *send_flagptr, int4 *srcptr, int4 *dstptr, const int lines, int send_peer,\n    int recv_peer, int *recv_id, int *recv_flagptr, int adder, void *counters, int nchunks,\n    int send_stride, int recv_stride, bool shuffle, uint64_t ub_timeout, int nv_send, int nv_recv) {\n  for (int chunk_i = 0; chunk_i < nchunks - 1; chunk_i++) {\n    int send_chunk_id = shuffle ? chunk_i : (nchunks + send_peer - chunk_i) % nchunks;\n    int recv_chunk_id = shuffle ? chunk_i + 1 : (nchunks + send_peer - chunk_i - 1) % nchunks;\n    int send_offset = (send_chunk_id * send_stride) / 16;\n    int recv_offset = ((shuffle ? recv_chunk_id : send_chunk_id) * recv_stride) / 16;\n\n    if (lines) {\n      const int start_elem = threadIdx.x + blockDim.x * blockIdx.x;\n      const int end_elem = lines;\n      const int aligned_elem =\n          ((end_elem - start_elem) & (~(blockDim.x * gridDim.x * UNROLLCOPY - 1)));\n      const int end_aligned = start_elem + aligned_elem;\n      if (end_elem > start_elem) {\n        for (int line = start_elem; line < end_aligned;\n             line += blockDim.x * gridDim.x * UNROLLCOPY) {\n          int4 val[UNROLLCOPY];\n#pragma unroll\n          for (int i = 0; i < UNROLLCOPY; i++) {\n            val[i] = srcptr[send_offset + line + i * blockDim.x * gridDim.x];\n          }\n#pragma unroll\n          for (int i = 0; i < UNROLLCOPY; i++) {\n            dstptr[recv_offset + line + i * blockDim.x * gridDim.x] = val[i];\n          }\n        }\n        for (int line = end_aligned; line < end_elem; line += blockDim.x * gridDim.x) {\n          dstptr[recv_offset + line] = srcptr[send_offset + line];\n        }\n      }\n      __syncthreads();\n      if (!threadIdx.x) {\n        __threadfence_system();\n        atomicAdd_system(send_flagptr,\n                         1);  // otherwise need local SM sync before sending flag\n      }\n    } else {  // 0 bytes and 1 SM only\n      atomicAdd_system(send_flagptr, 1);\n    }\n\n    // wait for message to arrive.\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n      const int signal_id = (*recv_id) + adder;\n      *recv_id = signal_id;\n      volatile int *flag = (volatile int *)recv_flagptr;\n      clock_t s = clock64();\n      while (CHECK_IDS(*flag, signal_id)) {\n        if (CHECK_TIMEOUT(s, ub_timeout)) {\n          UB_PRINT(\n              \"pushsendrecv multiatomic [sending peer:%d receiving peer:%d][nvrank(GPU)\"\n              \" sending peer: %d receiving peer: %d]: expecting %d, observed %d\",\n              send_peer, recv_peer, nv_send, nv_recv, signal_id, *flag); /*return;*/\n          // CE mode is not supported for multi-atomic, so there is no need to check for a deadlock\n          return;\n        }\n      }\n    }\n\n    // Producer must update counters.\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n      // Decrement atomic val to signal current output tile finish\n      if (counters) {\n        ((unsigned int *)counters)[recv_chunk_id /*chunk_i+1*/] = 0;\n        asm volatile(\"fence.sc.gpu;\\n\");\n      }\n    }\n\n    // sync all CTAs before moving to next chunk.\n    if (threadIdx.x == 0) {\n      atomicInc(((unsigned int *)counters) + nchunks + chunk_i, gridDim.x - 1);\n      while (0 != (atomicCAS(((unsigned int *)counters) + nchunks + chunk_i, 0, 0))) {\n      }\n    }\n    __syncthreads();\n  }\n}\n\n// Return TRUE if two ranks share the same NV domain\n#define INTRANODE(peer) ((peer / comm->nvsize) == (comm->myrank / comm->nvsize))\n\n// Index corresponds to the type of flag:\n// 0 - Send index counter\n// 1 - CE start index counter\n// 2 - CE end index counter\n#define GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsth, index)                                 \\\n  ((reinterpret_cast<char *>((comm)->peer_ptr[0][(peerlocal)])) +                           \\\n   ((NVTE_REG0_OFFSET(comm) + NVTE_REG0_RECV + (comm)->myrank * NVTE_MAX_REGIONS + (dsth) + \\\n     (index) * NVTE_MAX_NVLINK * NVTE_MAX_REGIONS) *                                        \\\n    sizeof(int)))\n\n// Index corresponds to the type of flag:\n// 0 - Receive index counter\n// 1 - CE start index counter\n// 2 - CE end index counter\n#define GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsth, index)                              \\\n  ((reinterpret_cast<char *>((comm)->mem_ptr[0])) +                                      \\\n   ((NVTE_REG0_OFFSET(comm) + NVTE_REG0_RECV + (recv_peer) * NVTE_MAX_REGIONS + (dsth) + \\\n     (index) * NVTE_MAX_NVLINK * NVTE_MAX_REGIONS) *                                     \\\n    sizeof(int)))\n\nstatic __global__ void tiny_delay_kern() {\n  // Although a loop could be used to add a larger delay here, for\n  // the purpose of enforcing proper kernel ordering when using CG,\n  // an empty kernel seems to work well enough.\n}\n\nvoid userbuffers_tiny_delay(hipStream_t stream) { tiny_delay_kern<<<1, 1, 0, stream>>>(); }\n\nvoid userbuffers_send(const int srchandler, const size_t srcoffset, const int dsthandler,\n                      const size_t dstoffset, const size_t bytes, communicator *comm,\n                      const int peer, hipStream_t stream) {\n  int peerlocal = peer % comm->nvsize;\n  void *flagptr = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(peerlocal, comm, dsthandler, 2);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  assert(INTRANODE(peer));\n\n  if (!(comm->launch_mode & NVTE_LAUNCH_GPU)) return;\n  if (comm->push == 0) {\n    kuserbuffers_pullsend<<<1, 1, 0, stream>>>(comm->myrank, peer, &(comm->send_id[peer]),\n                                               reinterpret_cast<int *>(flagptr));\n    NVTE_CHECK_CUDA(hipGetLastError());\n  } else {\n    void *srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + srcoffset;\n    void *dstptr = reinterpret_cast<char *>(comm->peer_ptr[dsthandler][peerlocal]) + dstoffset;\n\n    if (comm->use_ce) {\n      // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n      NVTE_CHECK_CUDA(hipMemcpyAsync(dstptr, srcptr, bytes, hipMemcpyDeviceToDevice, stream));\n      // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n    }\n    SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n    int *arg1 = &comm->send_id[peer], *arg2 = reinterpret_cast<int *>(flagptr);\n    int4 *arg3 = reinterpret_cast<int4 *>(srcptr), *arg4 = reinterpret_cast<int4 *>(dstptr);\n    int arg5 = signalonly ? 0 : bytes / 16;\n    void *kernelArgs[] = {reinterpret_cast<void *>(&arg1), reinterpret_cast<void *>(&arg2),\n                          reinterpret_cast<void *>(&arg3), reinterpret_cast<void *>(&arg4),\n                          reinterpret_cast<void *>(&arg5)};\n    NVTE_CHECK_CUDA(\n        hipLaunchKernelExC(&cfg, reinterpret_cast<void *>(kuserbuffers_pushsend), kernelArgs));\n  }\n}\n\nvoid userbuffers_sendrecv(const int srchandler, const int dsthandler, const size_t send_offset,\n                          const size_t recv_offset, const size_t bytes, communicator *comm,\n                          const int send_peer, const int recv_peer, hipStream_t stream) {\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 2);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  void *send_srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + send_offset;\n  void *send_dstptr =\n      reinterpret_cast<char *>(comm->peer_ptr[dsthandler][send_peerlocal]) + send_offset;\n\n  if (comm->use_ce) {\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n    NVTE_CHECK_CUDA(\n        hipMemcpyAsync(send_dstptr, send_srcptr, bytes, hipMemcpyDeviceToDevice, stream));\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n  }\n  SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>(send_srcptr);\n  int4 *arg4 = reinterpret_cast<int4 *>(send_dstptr);\n  int arg5 = signalonly ? 0 : bytes / 16;\n  int arg6 = send_peer;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = signalonly ? 1 : comm->sms;\n  uint64_t arg11 = comm->ub_timeout;\n  int arg12 = send_peerlocal;\n  int arg13 = recv_peerlocal;\n  int *arg14 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 1)\n                                         : nullptr);\n  int *arg15 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 2)\n                                         : nullptr);\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15)};\n  NVTE_CHECK_CUDA(\n      hipLaunchKernelExC(&cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv), kernelArgs));\n}\n\nvoid userbuffers_sendrecv_atomic(const int srchandler, const int dsthandler,\n                                 const size_t send_offset, const size_t recv_offset,\n                                 const size_t bytes, communicator *comm, const int send_peer,\n                                 const int recv_peer, void *counters, hipStream_t stream) {\n  assert(comm->push && comm->use_ce == 0);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  // void *ce_send_start_ptr = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 1);\n  // void *ce_send_end_ptr   = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 2);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  void *send_srcptr = reinterpret_cast<char *>(comm->mem_ptr[srchandler]) + send_offset;\n  void *send_dstptr =\n      reinterpret_cast<char *>(comm->peer_ptr[dsthandler][send_peerlocal]) + send_offset;\n  if (comm->use_ce) {\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_start_ptr));\n    NVTE_CHECK_CUDA(\n        hipMemcpyAsync(send_dstptr, send_srcptr, bytes, hipMemcpyDeviceToDevice, stream));\n    // kuserbuffers_inc<<<1, 1, 0, stream>>>(reinterpret_cast<int *>(ce_send_end_ptr));\n  }\n  SETUP_LAUNCH_CONFIG(signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>(send_srcptr);\n  int4 *arg4 = reinterpret_cast<int4 *>(send_dstptr);\n  int arg5 = signalonly ? 0 : bytes / 16;\n  int arg6 = send_peer;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = signalonly ? 1 : comm->sms;\n  void *arg11 = counters;\n  int arg12 = comm->ub_timeout;\n  int arg13 = send_peerlocal;\n  int arg14 = recv_peerlocal;\n  int *arg15 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 1)\n                                         : nullptr);\n  int *arg16 = reinterpret_cast<int *>(0 ?  // temporary disable\n                                           GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 2)\n                                         : nullptr);\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16)};\n  NVTE_CHECK_CUDA(hipLaunchKernelExC(\n      &cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv_atomic), kernelArgs));\n}\n\nvoid userbuffers_sendrecv_multiatomic(const int srchandler, const int dsthandler,\n                                      const size_t send_stride, const size_t recv_stride,\n                                      const size_t bytes, communicator *comm, const int send_peer,\n                                      const int recv_peer, const int nchunks, void *counters,\n                                      bool shuffle, hipStream_t stream) {\n  assert(comm->push && comm->use_ce == 0);\n  // CE is not supported\n\n  int send_peerlocal = send_peer % comm->nvsize;\n  int recv_peerlocal = recv_peer % comm->nvsize;\n  void *flagptr_send = GET_SEND_PTR_BY_INDEX(send_peerlocal, comm, dsthandler, 0);\n  void *flagptr_recv = GET_RECV_PTR_BY_INDEX(recv_peer, comm, dsthandler, 0);\n\n  SETUP_LAUNCH_CONFIG(comm->sms, 1024, stream);\n\n  int *arg1 = &comm->send_id[send_peer];\n  int *arg2 = reinterpret_cast<int *>(flagptr_send);\n  int4 *arg3 = reinterpret_cast<int4 *>((comm->mem_ptr[srchandler]));\n  int4 *arg4 = reinterpret_cast<int4 *>((comm->peer_ptr[dsthandler][send_peerlocal]));\n  int arg5 = bytes / 16;\n  int arg6 = comm->myrank;\n  int arg7 = recv_peer;\n  int *arg8 = &comm->recv_id[recv_peer * NVTE_MAX_REGIONS + dsthandler];\n  int *arg9 = reinterpret_cast<int *>(flagptr_recv);\n  int arg10 = comm->sms;\n  void *arg11 = counters;\n  int arg12 = nchunks;\n  int arg13 = send_stride;\n  int arg14 = recv_stride;\n  bool arg15 = shuffle;\n  uint64_t arg16 = comm->ub_timeout;\n  int arg17 = send_peerlocal;\n  int arg18 = recv_peerlocal;\n  void *kernelArgs[] = {reinterpret_cast<void *>(&arg1),  reinterpret_cast<void *>(&arg2),\n                        reinterpret_cast<void *>(&arg3),  reinterpret_cast<void *>(&arg4),\n                        reinterpret_cast<void *>(&arg5),  reinterpret_cast<void *>(&arg6),\n                        reinterpret_cast<void *>(&arg7),  reinterpret_cast<void *>(&arg8),\n                        reinterpret_cast<void *>(&arg9),  reinterpret_cast<void *>(&arg10),\n                        reinterpret_cast<void *>(&arg11), reinterpret_cast<void *>(&arg12),\n                        reinterpret_cast<void *>(&arg13), reinterpret_cast<void *>(&arg14),\n                        reinterpret_cast<void *>(&arg15), reinterpret_cast<void *>(&arg16),\n                        reinterpret_cast<void *>(&arg17), reinterpret_cast<void *>(&arg18)};\n  NVTE_CHECK_CUDA(hipLaunchKernelExC(\n      &cfg, reinterpret_cast<void *>(kuserbuffers_pushsendrecv_multiatomic), kernelArgs));\n}\n\nvoid userbuffers_recv(const int srchandler, const size_t srcoffset, const int dsthandler,\n                      const size_t dstoffset, const size_t bytes, communicator *comm,\n                      const int peer, hipStream_t stream) {\n  int peerlocal = peer % comm->nvsize;\n  void *flagptr = GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 0);\n  bool signalonly = (bytes / 16 == 0) || (comm->use_ce != 0);\n\n  assert(INTRANODE(peer));\n\n  if (!(comm->launch_mode & NVTE_LAUNCH_GPU)) return;\n  if (comm->push == 0) {\n    void *dstptr = reinterpret_cast<char *>(comm->mem_ptr[dsthandler]) + dstoffset;\n    void *srcptr = reinterpret_cast<char *>(comm->peer_ptr[srchandler][peerlocal]) + srcoffset;\n\n    kuserbuffers_pullrecv<<<signalonly ? 1 : comm->sms, signalonly ? 1 : 1024, 0, stream>>>(\n        comm->myrank, peer, comm->nvrank, peerlocal,\n        &(comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler]), reinterpret_cast<int *>(flagptr),\n        reinterpret_cast<int4 *>(srcptr), reinterpret_cast<int4 *>(dstptr),\n        signalonly ? 0 : bytes / 16, comm->ub_timeout);\n    NVTE_CHECK_CUDA(hipGetLastError());\n    if (!signalonly) {\n      kuserbuffers_inc<<<1, 1, 0, stream>>>(&(comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler]));\n      NVTE_CHECK_CUDA(hipGetLastError());\n    }\n    if (comm->use_ce) {\n      NVTE_CHECK_CUDA(hipMemcpyAsync(dstptr, srcptr, bytes, hipMemcpyDeviceToDevice, stream));\n    }\n  } else {\n    kuserbuffers_pushrecv<<<1, 1, 0, stream>>>(\n        comm->myrank, peer, comm->nvrank, peerlocal,\n        &comm->recv_id[peer * NVTE_MAX_REGIONS + dsthandler], reinterpret_cast<int *>(flagptr),\n        signalonly || comm->sms, comm->ub_timeout,\n        reinterpret_cast<int *>(0 ?  // temporary disable\n                                    GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 1)\n                                  : nullptr),\n        reinterpret_cast<int *>(0 ?  // temporary disable\n                                    GET_RECV_PTR_BY_INDEX(peer, comm, dsthandler, 2)\n                                  : nullptr));\n    NVTE_CHECK_CUDA(hipGetLastError());\n  }\n}\n\nvoid userbuffers_send_all(const int srchandler, const size_t srcoffset, const int dsthandler,\n                          const size_t dstoffset, const size_t bytes_per_slice, int tp_rank,\n                          int tp_size, int world_rank, communicator *comm, hipStream_t stream) {\n  int rank_round_tp = (world_rank / tp_size) * tp_size;\n  for (int j = 1; j < tp_size; j++) {\n    int i = (tp_rank + j) % tp_size;\n    int send_offset = srcoffset + bytes_per_slice * tp_rank;\n    int recv_offset = dstoffset + bytes_per_slice * tp_rank;\n    userbuffers_send(srchandler, send_offset, dsthandler, recv_offset, bytes_per_slice, comm,\n                     rank_round_tp + i, stream);\n  }\n}\n\nvoid userbuffers_recv_all(const int srchandler, const size_t srcoffset, const int dsthandler,\n                          const size_t dstoffset, const size_t bytes_per_slice, int tp_rank,\n                          int tp_size, int world_rank, communicator *comm, hipStream_t stream) {\n  int rank_round_tp = (world_rank / tp_size) * tp_size;\n  for (int j = tp_size - 1; j > 0; j--) {\n    int i = (tp_rank + j) % tp_size;\n    int send_offset = srcoffset + bytes_per_slice * i;\n    int recv_offset = dstoffset + bytes_per_slice * i;\n    userbuffers_recv(srchandler, send_offset, dsthandler, recv_offset, bytes_per_slice, comm,\n                     rank_round_tp + i, stream);\n  }\n}\n\n// producer\nstatic __global__ void producer_kernel(void *atomic_ptr, int chunk_i) {\n  // Decrement atomic val to signal current output tile finish\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    ((unsigned int *)atomic_ptr)[chunk_i] = 0;\n  }\n\n  // COMM kernel need to explicitely flash gmem.\n  // GEMM kernel already executed, and can not see gmem\n  // change without COMM kernel explicitely make change\n  asm volatile(\"fence.sc.gpu;\\n\");\n}\n\n// consumer\nstatic __global__ void consumer_kernel(void *atomic_ptr, int chunk_i) {\n  // Wait for producer to change the val to 0, which signal producer ready\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    while (0 != (atomicCAS((unsigned int *)atomic_ptr + chunk_i, 0, 0))) {\n    }\n    ((unsigned int *)atomic_ptr)[chunk_i] = 1;\n    asm volatile(\"fence.sc.gpu;\\n\");\n  }\n}\n\n// consumer_batch\nstatic __global__ void consumer_batch_kernel(void *atomic_ptr, int first_chunk_i, int num_chunks) {\n  // Wait for producer to change the val to 0, which signal producer ready\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    for (int i = first_chunk_i; i < num_chunks; i++) {\n      while (0 != (atomicCAS((unsigned int *)atomic_ptr + i, 0, 0))) {\n      }\n      ((unsigned int *)atomic_ptr)[i] = 1;\n      asm volatile(\"fence.sc.gpu;\\n\");\n    }\n  }\n}\n\n// reset counters kernel\nstatic __global__ void reset_counters_kernel(void *atomic_ptr, int num_chunks, bool allgather) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n#pragma unroll\n    for (int i = 0; i < num_chunks; i++) {\n      ((unsigned int *)atomic_ptr)[i] = 1;\n      ((unsigned int *)atomic_ptr)[i + num_chunks] = 0;\n    }\n    if (allgather) ((unsigned int *)atomic_ptr)[0] = 0;\n  }\n}\n\nvoid producer(void *atomic_ptr, int chunk_i, hipStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  producer_kernel<<<grid, block, 0, stream>>>(atomic_ptr, chunk_i);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid consumer(void *atomic_ptr, int chunk_i, hipStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  consumer_kernel<<<grid, block, 0, stream>>>(atomic_ptr, chunk_i);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid consumer_batch(void *atomic_ptr, int first_chunk_i, int num_chunks, hipStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  consumer_batch_kernel<<<grid, block, 0, stream>>>(atomic_ptr, first_chunk_i, num_chunks);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid reset_counters(void *atomic_ptr, int num_chunks, bool allgather, hipStream_t stream) {\n  dim3 block(1);\n  dim3 grid(1);\n  reset_counters_kernel<<<grid, block, 0, stream>>>(atomic_ptr, num_chunks, allgather);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate <typename fp8type, int nvec>\n__global__ void __launch_bounds__(MAX_THREADS / 4)\n    reduce_fp8_in_bf16_out_cuda(void *inputs, void *output, const float *scale,\n                                const int num_inputs, const int input_size,\n                                const int num_aligned_elements_per_input,\n                                const int tot_input_size) {\n  fp8type *inputs_fp8 = reinterpret_cast<fp8type *>(inputs);\n  half_dtype *output_half = reinterpret_cast<half_dtype *>(output);\n\n  transformer_engine::VectorizedLoader<fp8type, nvec, true> loader(inputs_fp8, tot_input_size);\n  transformer_engine::VectorizedStorer<half_dtype, nvec, true> storer(output_half, input_size);\n\n  const size_t tid = threadIdx.x + blockDim.x * blockIdx.x;\n  if (tid >= num_aligned_elements_per_input) {\n    return;\n  }\n  float accum_buf[nvec];  // NOLINT(*)\n\n  loader.load(tid, tot_input_size);\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    accum_buf[i] = static_cast<float>(loader.separate()[i]) * (*scale);\n  }\n  for (int input_id = 1; input_id < num_inputs; ++input_id) {\n    loader.load(tid + num_aligned_elements_per_input * input_id, tot_input_size);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      accum_buf[i] += static_cast<float>(loader.separate()[i]) * (*scale);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    storer.separate()[i] = static_cast<half_dtype>(accum_buf[i]);\n  }\n  storer.store(tid, input_size);\n}\n\ntemplate <typename fp8type>\nvoid reduce_fp8_in_bf16_out(void *inputs, void *output, float *scale, int num_inputs,\n                            int input_size, hipStream_t stream) {\n  constexpr int nvec = 32;\n  assert(input_size % nvec == 0);\n  const int num_aligned_elements_per_input = input_size / nvec;\n  const int tot_input_size = input_size * num_inputs;\n  size_t num_threads = MAX_THREADS / 4;\n  size_t num_blocks = (num_aligned_elements_per_input + num_threads - 1) / num_threads;\n  dim3 block(num_threads);\n  dim3 grid(num_blocks);\n  reduce_fp8_in_bf16_out_cuda<fp8type, nvec>\n      <<<grid, block, 0, stream>>>(inputs, output, scale, num_inputs, input_size,\n                                   num_aligned_elements_per_input, tot_input_size);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\ntemplate void reduce_fp8_in_bf16_out<__hip_fp8_e4m3_fnuz>(void *inputs, void *output, float *scale,\n                                                    int num_inputs, int input_size,\n                                                    hipStream_t stream);\ntemplate void reduce_fp8_in_bf16_out<__hip_fp8_e5m2_fnuz>(void *inputs, void *output, float *scale,\n                                                    int num_inputs, int input_size,\n                                                    hipStream_t stream);\n\ntemplate <int nvec>\n__global__ void __launch_bounds__(MAX_THREADS / 4)\n    reduce_bf16_cuda(void *inputs, void *output, const int num_inputs, const int input_size,\n                     const int num_aligned_elements_per_input, const int tot_input_size) {\n  half_dtype *inputs_half = reinterpret_cast<half_dtype *>(inputs);\n  half_dtype *output_half = reinterpret_cast<half_dtype *>(output);\n\n  transformer_engine::VectorizedLoader<half_dtype, nvec, true> loader(inputs_half, tot_input_size);\n  transformer_engine::VectorizedStorer<half_dtype, nvec, true> storer(output_half, input_size);\n\n  const size_t tid = threadIdx.x + blockDim.x * blockIdx.x;\n  if (tid >= num_aligned_elements_per_input) {\n    return;\n  }\n  float accum_buf[nvec];  // NOLINT(*)\n\n  loader.load(tid, tot_input_size);\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    accum_buf[i] = static_cast<float>(loader.separate()[i]);\n  }\n  for (int input_id = 1; input_id < num_inputs; ++input_id) {\n    loader.load(tid + num_aligned_elements_per_input * input_id, tot_input_size);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      accum_buf[i] += static_cast<float>(loader.separate()[i]);\n    }\n  }\n#pragma unroll\n  for (int i = 0; i < nvec; ++i) {\n    storer.separate()[i] = static_cast<half_dtype>(accum_buf[i]);\n  }\n  storer.store(tid, input_size);\n}\n\nvoid reduce_bf16(void *inputs, void *output, int num_inputs, int input_size, hipStream_t stream) {\n  constexpr int nvec = 32;\n  assert(input_size % nvec == 0);\n  const int num_aligned_elements_per_input = input_size / nvec;\n  const int tot_input_size = input_size * num_inputs;\n  size_t num_threads = MAX_THREADS / 4;\n  size_t num_blocks = (num_aligned_elements_per_input + num_threads - 1) / num_threads;\n  dim3 block(num_threads);\n  dim3 grid(num_blocks);\n  reduce_bf16_cuda<nvec><<<grid, block, 0, stream>>>(\n      inputs, output, num_inputs, input_size, num_aligned_elements_per_input, tot_input_size);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/comm_gemm_overlap/userbuffers/userbuffers.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/recipe.h>\n\n#include <cmath>\n#include <limits>\n#include <string>\n\n#include \"../common.h\"\n#include \"../util/cuda_runtime.h\"\n#include \"../util/logging.h\"\n\nnamespace transformer_engine {\nnamespace delayed_scaling_recipe {\n\nnamespace {\n\n// amax value to use for updating scaling factor\nenum class AmaxComputeAlgo { INVALID, MOST_RECENT, MAX };\n\nconst char* dtype_name(DType dtype) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(dtype, Type,\n                                     return TypeInfo<Type>::name;);  // NOLINT(*)\n  return \"\";\n}\n\n// Maximum representable value of an FP8 dtype\ninline float fp8_dtype_max(DType dtype) {\n  switch (dtype) {\n    case DType::kFloat8E4M3:\n      return 448;\n    case DType::kFloat8E5M2:\n      return 57344;\n    default:\n      NVTE_ERROR(\"Expected FP8 dtype, but got \", dtype_name(dtype));\n  }\n  return 0;\n}\n\n// struct for amax parameters\nstruct AmaxParam {\n  int num_scale = 0;\n  float* amax_history = nullptr;\n  float* scale = nullptr;\n};\n\n// dummy struct for kernel_bulk's other params\nstruct OtherParams {\n  float* a;\n  size_t b;\n  AmaxComputeAlgo c;\n  float d;\n};\n\n#if CUDART_VERSION >= 12010\nconstexpr size_t max_constant_memory_per_kernel = 32768;\nconstexpr size_t AMAX_PARAMS_LIMIT =\n    (max_constant_memory_per_kernel - sizeof(OtherParams)) / sizeof(AmaxParam);\n#else\nconstexpr size_t max_constant_memory_per_kernel = 4096;\nconstexpr size_t AMAX_PARAMS_LIMIT =\n    (max_constant_memory_per_kernel - sizeof(OtherParams)) / sizeof(AmaxParam);\n#endif\n\nstruct AmaxParams {\n  AmaxParam param[AMAX_PARAMS_LIMIT];\n};\n\nnamespace amax_and_scale_update_impl {\n\n// CUDA block size\nconstexpr size_t bsize = 256;\n\n/* CUDA kernel to update amax history and FP8 scaling factors\n *\n * Block dims: bsize x 1 x 1\n *\n * Grid dims: num_scales x 1 x 1\n */\n__global__ void __launch_bounds__(bsize)\n    kernel(const float* amax_history_ptr, const float* scale_ptr, float* updated_amax_history_ptr,\n           float* updated_scale_ptr, size_t amax_history_length, size_t amax_history_stride,\n           AmaxComputeAlgo amax_compute_algo, float scaled_max) {\n  const size_t tid = threadIdx.x;\n  const size_t bid = blockIdx.x;\n\n  // Update amax\n  float amax = 0;\n  {\n    // Roll amax history\n    const auto* amax_history = amax_history_ptr + bid;\n    auto* updated_amax_history = updated_amax_history_ptr + bid;\n    const auto last_amax = amax_history[0];\n    const auto& length = amax_history_length;\n    const auto& stride = amax_history_stride;\n    for (size_t off = 0; off < length; off += bsize) {\n      const size_t i = off + tid;\n      float a = 0;\n      if (i < length) {\n        a = (i < length - 1) ? amax_history[(i + 1) * stride] : last_amax;\n        amax = fmaxf(amax, a);\n      }\n      __syncthreads();  // In case roll is in-place\n      if (i < length) {\n        updated_amax_history[i * stride] = (i > 0) ? a : 0;\n      }\n    }\n\n    // Compute amax to use for scaling factor\n    switch (amax_compute_algo) {\n      case AmaxComputeAlgo::MOST_RECENT:\n        amax = last_amax;\n        break;\n      case AmaxComputeAlgo::MAX: {\n        __shared__ float shared_amax[bsize];\n        shared_amax[tid] = amax;\n        __syncthreads();\n#pragma unroll\n        for (size_t off = bsize / 2; off > 0; off /= 2) {\n          if (tid < off) {\n            shared_amax[tid] = fmaxf(shared_amax[tid], shared_amax[tid + off]);\n          }\n          __syncthreads();\n        }\n        amax = shared_amax[tid];\n      } break;\n      default:\n        amax = 0;\n    }\n  }\n\n  // Update scale\n  if (tid == 0) {\n    // Update scale\n    float scale;\n    if (isfinite(amax) && amax > 0) {\n      scale = scaled_max / amax;\n    } else {\n      scale = scale_ptr[bid];\n    }\n    // When the amax is too tiny that the scale becoming infinite in FP32,\n    // we set the scale to the max value of FP32. In this case, the tensor\u2019s\n    // amax won't get mapped to the FP8 max representable, but rather\n    // something below that, but this is the best thing we can do.\n    if (isinf(scale)) {\n      scale = std::numeric_limits<float>::max();\n    }\n    updated_scale_ptr[bid] = scale;\n  }\n}\n\n/* CUDA kernel to bulk-update amax history and FP8 scaling factors\n *\n * Block dims: bsize x 1 x 1\n *\n * Grid dims: num_tensors x 1 x 1\n */\n__global__ void __launch_bounds__(bsize)\n    kernel_bulk(float* amax_reduction_buffer, AmaxParams p, size_t amax_history_length,\n                AmaxComputeAlgo amax_compute_algo, float scaled_max) {\n  const size_t bid = blockIdx.x;\n  const size_t tid = threadIdx.x;\n  const int num_scale = p.param[bid].num_scale;\n\n  int offset_in_buffer = 0;\n  for (int j = 0; j < bid; j++) {\n    offset_in_buffer += p.param[j].num_scale;\n  }\n\n  for (int count = 0; count < num_scale; count++) {\n    // Update amax\n    float amax = 0;\n    {\n      // Roll amax history\n      const auto& length = amax_history_length;\n      const auto& stride = p.param[bid].num_scale;\n      auto* amax_history = p.param[bid].amax_history + count;\n      const auto last_amax = ((amax_reduction_buffer != nullptr) &&\n                              (amax_reduction_buffer[offset_in_buffer + count] != 0.0f))\n                                 ? amax_reduction_buffer[offset_in_buffer + count]\n                                 : amax_history[0];\n      if (last_amax != 0.0f) {\n        for (size_t off = 0; off < length; off += bsize) {\n          const size_t i = off + tid;\n          float a = 0;\n          if (i < length) {\n            a = (i < length - 1) ? amax_history[(i + 1) * stride] : last_amax;\n            amax = fmaxf(amax, a);\n          }\n          __syncthreads();  // Inplace roll\n          if (i < length) {\n            amax_history[i * stride] = (i > 0) ? a : 0;\n          }\n        }\n      }\n\n      // Compute amax to use for scaling factor\n      switch (amax_compute_algo) {\n        case AmaxComputeAlgo::MOST_RECENT:\n          amax = last_amax;\n          break;\n        case AmaxComputeAlgo::MAX: {\n          __shared__ float shared_amax[bsize];\n          shared_amax[tid] = amax;\n          __syncthreads();\n#pragma unroll\n          for (size_t off = bsize / 2; off > 0; off /= 2) {\n            if (tid < off) {\n              shared_amax[tid] = fmaxf(shared_amax[tid], shared_amax[tid + off]);\n            }\n            __syncthreads();\n          }\n          amax = shared_amax[tid];\n        } break;\n        default:\n          amax = 0;\n      }\n    }\n\n    // Update scale\n    if (tid == 0) {\n      // Computing the scaling factor requires consideration of the following scenarios:\n      // 1. amax == 0:\n      //    No action is possible, set scale to the previous scale (or 1).\n      // 2. 0 < amax < tiny_amax\n      //    The amax is too tiny that the scale becomes infinite in FP32.\n      //    Set scale = FP32_max\n      // 3. tiny_amax <= amax < FP32_max:\n      //    Set scale = FP8_max (or scaled_max) / amax\n      // 4. When amax == inf or amax == nan:\n      //    No action is possible, set scale to the previous scale (or 1).\n\n      float scale;\n      if (isfinite(amax) && amax > 0) {\n        scale = scaled_max / amax;\n      } else {\n        scale = p.param[bid].scale[count];\n      }\n      // When the amax is too tiny that the scale becoming infinite in FP32,\n      // we set the scale to the max value of FP32. In this case, the tensor\u2019s\n      // amax won't get mapped to the FP8 max representable, but rather\n      // something below that, but this is the best thing we can do.\n      if (isinf(scale)) {\n        scale = std::numeric_limits<float>::max();\n      }\n      p.param[bid].scale[count] = scale;\n    }\n  }\n}\n\n}  // namespace amax_and_scale_update_impl\n\n}  // namespace\n\nvoid amax_and_scale_update(const Tensor& amax_history, const Tensor& scale,\n                           Tensor* updated_amax_history_, Tensor* updated_scale_,\n                           const std::string& amax_compute_algo, DType fp8_dtype, float margin,\n                           cudaStream_t stream) {\n  auto& updated_amax_history = *updated_amax_history_;\n  auto& updated_scale = *updated_scale_;\n\n  // Check tensors\n  NVTE_CHECK(amax_history.data.shape.size() == 2, \"Found \", amax_history.data.shape.size(),\n             \" dims\");\n  const size_t amax_history_length = amax_history.data.shape[0];\n  const size_t num_scales = amax_history.data.shape[1];\n  NVTE_CHECK(amax_history.data.dtype == DType::kFloat32, \"Found \",\n             dtype_name(amax_history.data.dtype), \".\");\n  NVTE_CHECK(scale.numel() == num_scales, \"Expected \", num_scales, \" elements, \", \"but found \",\n             scale.numel(), \".\");\n  NVTE_CHECK(scale.data.dtype == DType::kFloat32, \"Found \", dtype_name(scale.data.dtype), \".\");\n  NVTE_CHECK(updated_amax_history.data.shape.size() == 2, \"Found \",\n             updated_amax_history.data.shape.size(), \" dims.\");\n  NVTE_CHECK(updated_amax_history.data.shape[0] == amax_history_length, \"Expected \",\n             amax_history_length, \", \", \"but found \", updated_amax_history.data.shape[0]);\n  NVTE_CHECK(updated_amax_history.data.shape[1] == num_scales, \"Expected \", num_scales, \", \",\n             \"but found \", updated_amax_history.data.shape[1]);\n  NVTE_CHECK(updated_amax_history.data.dtype == DType::kFloat32, \"Got \",\n             dtype_name(updated_amax_history.data.dtype), \".\");\n  NVTE_CHECK(updated_scale.numel() == num_scales, \"Expected \", num_scales, \" elements, \",\n             \"but found \", updated_scale.numel(), \".\");\n  NVTE_CHECK(updated_scale.data.dtype == DType::kFloat32, \"Got \",\n             dtype_name(updated_scale.data.dtype), \".\");\n\n  // amax value to use for updating scaling factor\n  AmaxComputeAlgo amax_compute_algo_ = AmaxComputeAlgo::INVALID;\n  if (amax_compute_algo == \"max\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MAX;\n  } else if (amax_compute_algo == \"most_recent\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MOST_RECENT;\n  } else {\n    NVTE_ERROR(\"Unsupported amax compute algorithm (\", amax_compute_algo, \")\");\n  }\n\n  // Expected maximum value after scale is applied\n  const float scaled_max = fp8_dtype_max(fp8_dtype) * std::pow(2.f, -margin);\n\n  // Launch CUDA kernel\n  constexpr size_t block_size = amax_and_scale_update_impl::bsize;\n  const size_t grid_size = num_scales;\n  amax_and_scale_update_impl::kernel<<<grid_size, block_size, 0, stream>>>(\n      static_cast<const float*>(amax_history.data.dptr), static_cast<const float*>(scale.data.dptr),\n      static_cast<float*>(updated_amax_history.data.dptr),\n      static_cast<float*>(updated_scale.data.dptr), amax_history_length, num_scales,\n      amax_compute_algo_, scaled_max);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid amax_and_scale_update_after_reduction(const Tensor& amax_reduction_buffer,\n                                           std::vector<Tensor*> amax_histories,\n                                           std::vector<Tensor*> scales,\n                                           const std::string& amax_compute_algo, DType fp8_dtype,\n                                           float margin, cudaStream_t stream) {\n  using namespace transformer_engine;\n\n  // amax value to use for updating scaling factor\n  AmaxComputeAlgo amax_compute_algo_ = AmaxComputeAlgo::INVALID;\n  if (amax_compute_algo == \"max\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MAX;\n  } else if (amax_compute_algo == \"most_recent\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MOST_RECENT;\n  } else {\n    NVTE_ERROR(\"Unsupported amax compute algorithm (\", amax_compute_algo, \")\");\n  }\n\n  // Expected maximum value after scale is applied\n  const float scaled_max = fp8_dtype_max(fp8_dtype) * std::pow(2.f, -margin);\n\n  // Number of tensors in the bulk\n  const size_t num_tensors = amax_histories.size();\n  size_t num_remaining_tensors = num_tensors;\n  const int num_kernels = (num_tensors + AMAX_PARAMS_LIMIT - 1) / AMAX_PARAMS_LIMIT;\n  size_t amax_history_length = 0;\n  if (num_tensors > 0) {\n    amax_history_length = amax_histories[0]->data.shape[0];\n  }\n\n  // amax parameters\n  float* amax_buffer = static_cast<float*>(amax_reduction_buffer.data.dptr);\n  AmaxParams p;\n  for (int iter = 0; iter < num_kernels; iter++) {\n    size_t kernel_num_scales = 0;\n    size_t kernel_num_tensors =\n        (iter == (num_kernels - 1)) ? num_remaining_tensors : AMAX_PARAMS_LIMIT;\n    for (size_t pi = 0; pi < kernel_num_tensors; pi++) {\n      size_t i = iter * AMAX_PARAMS_LIMIT + pi;\n\n      // Check tensors\n      int num_scale = amax_histories[i]->data.shape[1];\n      NVTE_CHECK(amax_histories[i]->data.dtype == DType::kFloat32, \"Found \",\n                 dtype_name(amax_histories[i]->data.dtype), \".\");\n      NVTE_CHECK(amax_histories[i]->data.shape.size() == 2, \"Found \",\n                 amax_histories[i]->data.shape.size(), \" dims\");\n      NVTE_CHECK(amax_histories[i]->numel() == amax_history_length * num_scale, \"Expected \",\n                 amax_history_length * num_scale, \" elements, \", \"but found \",\n                 amax_histories[i]->numel(), \".\");\n      NVTE_CHECK(scales[i]->data.dtype == DType::kFloat32, \"Found \",\n                 dtype_name(scales[i]->data.dtype), \".\");\n      NVTE_CHECK(scales[i]->data.shape.size() == 1, \"Found \", scales[i]->data.shape.size(),\n                 \" dims\");\n      NVTE_CHECK(scales[i]->numel() == num_scale, \"Expected \", num_scale, \" elements, \", \"Found \",\n                 scales[i]->numel(), \".\");\n\n      // amax parameters\n      kernel_num_scales += num_scale;\n      p.param[pi].num_scale = num_scale;\n      p.param[pi].amax_history = static_cast<float*>(amax_histories[i]->data.dptr);\n      p.param[pi].scale = static_cast<float*>(scales[i]->data.dptr);\n    }\n\n    // Launch CUDA kernel\n    size_t grid_size = kernel_num_tensors;\n    const size_t block_size = amax_and_scale_update_impl::bsize;\n    amax_and_scale_update_impl::kernel_bulk<<<grid_size, block_size, 0, stream>>>(\n        amax_buffer, p, amax_history_length, amax_compute_algo_, scaled_max);\n    NVTE_CHECK_CUDA(cudaGetLastError());\n\n    // shift amax buffer pointer\n    if (amax_buffer != nullptr) {\n      amax_buffer += kernel_num_scales;\n    }\n    num_remaining_tensors -= AMAX_PARAMS_LIMIT;\n  }\n}\n\n}  // namespace delayed_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_delayed_scaling_recipe_amax_and_scale_update(\n    const NVTETensor amax_history, const NVTETensor scale, NVTETensor updated_amax_history,\n    NVTETensor updated_scale, const char* amax_compute_algo, NVTEDType fp8_dtype, float margin,\n    cudaStream_t stream) {\n  NVTE_API_CALL(nvte_delayed_scaling_recipe_amax_and_scale_update);\n  using namespace transformer_engine;\n  delayed_scaling_recipe::amax_and_scale_update(\n      *convertNVTETensorCheck(amax_history), *convertNVTETensorCheck(scale),\n      convertNVTETensor(updated_amax_history), convertNVTETensor(updated_scale), amax_compute_algo,\n      static_cast<DType>(fp8_dtype), margin, stream);\n}\n\nvoid nvte_delayed_scaling_recipe_amax_and_scale_update_after_reduction(\n    const NVTETensor amax_reduction_buffer, std::vector<NVTETensor> amax_histories,\n    std::vector<NVTETensor> scales, const char* amax_compute_algo, NVTEDType fp8_dtype,\n    float margin, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_delayed_scaling_recipe_amax_and_scale_update_after_reduction);\n  using namespace transformer_engine;\n  size_t num_tensors = amax_histories.size();\n  std::vector<Tensor*> t_amax_histories, t_scales;\n  for (size_t i = 0; i < num_tensors; i++) {\n    t_amax_histories.push_back(convertNVTETensor(amax_histories[i]));\n    t_scales.push_back(convertNVTETensor(scales[i]));\n  }\n  delayed_scaling_recipe::amax_and_scale_update_after_reduction(\n      *convertNVTETensorCheck(amax_reduction_buffer), t_amax_histories, t_scales, amax_compute_algo,\n      static_cast<DType>(fp8_dtype), margin, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/recipe.h>\n\n#include <cmath>\n#include <limits>\n#include <string>\n\n#include \"../common.h\"\n#include \"../util/hip/hip_runtime.h\"\n#include \"../util/logging.h\"\n\nnamespace transformer_engine {\nnamespace delayed_scaling_recipe {\n\nnamespace {\n\n// amax value to use for updating scaling factor\nenum class AmaxComputeAlgo { INVALID, MOST_RECENT, MAX };\n\nconst char* dtype_name(DType dtype) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_ALL(dtype, Type,\n                                     return TypeInfo<Type>::name;);  // NOLINT(*)\n  return \"\";\n}\n\n// Maximum representable value of an FP8 dtype\ninline float fp8_dtype_max(DType dtype) {\n  switch (dtype) {\n    case DType::kFloat8E4M3:\n      return 448;\n    case DType::kFloat8E5M2:\n      return 57344;\n    default:\n      NVTE_ERROR(\"Expected FP8 dtype, but got \", dtype_name(dtype));\n  }\n  return 0;\n}\n\n// struct for amax parameters\nstruct AmaxParam {\n  int num_scale = 0;\n  float* amax_history = nullptr;\n  float* scale = nullptr;\n};\n\n// dummy struct for kernel_bulk's other params\nstruct OtherParams {\n  float* a;\n  size_t b;\n  AmaxComputeAlgo c;\n  float d;\n};\n\n#if CUDART_VERSION >= 12010\nconstexpr size_t max_constant_memory_per_kernel = 32768;\nconstexpr size_t AMAX_PARAMS_LIMIT =\n    (max_constant_memory_per_kernel - sizeof(OtherParams)) / sizeof(AmaxParam);\n#else\nconstexpr size_t max_constant_memory_per_kernel = 4096;\nconstexpr size_t AMAX_PARAMS_LIMIT =\n    (max_constant_memory_per_kernel - sizeof(OtherParams)) / sizeof(AmaxParam);\n#endif\n\nstruct AmaxParams {\n  AmaxParam param[AMAX_PARAMS_LIMIT];\n};\n\nnamespace amax_and_scale_update_impl {\n\n// CUDA block size\nconstexpr size_t bsize = 256;\n\n/* CUDA kernel to update amax history and FP8 scaling factors\n *\n * Block dims: bsize x 1 x 1\n *\n * Grid dims: num_scales x 1 x 1\n */\n__global__ void __launch_bounds__(bsize)\n    kernel(const float* amax_history_ptr, const float* scale_ptr, float* updated_amax_history_ptr,\n           float* updated_scale_ptr, size_t amax_history_length, size_t amax_history_stride,\n           AmaxComputeAlgo amax_compute_algo, float scaled_max) {\n  const size_t tid = threadIdx.x;\n  const size_t bid = blockIdx.x;\n\n  // Update amax\n  float amax = 0;\n  {\n    // Roll amax history\n    const auto* amax_history = amax_history_ptr + bid;\n    auto* updated_amax_history = updated_amax_history_ptr + bid;\n    const auto last_amax = amax_history[0];\n    const auto& length = amax_history_length;\n    const auto& stride = amax_history_stride;\n    for (size_t off = 0; off < length; off += bsize) {\n      const size_t i = off + tid;\n      float a = 0;\n      if (i < length) {\n        a = (i < length - 1) ? amax_history[(i + 1) * stride] : last_amax;\n        amax = fmaxf(amax, a);\n      }\n      __syncthreads();  // In case roll is in-place\n      if (i < length) {\n        updated_amax_history[i * stride] = (i > 0) ? a : 0;\n      }\n    }\n\n    // Compute amax to use for scaling factor\n    switch (amax_compute_algo) {\n      case AmaxComputeAlgo::MOST_RECENT:\n        amax = last_amax;\n        break;\n      case AmaxComputeAlgo::MAX: {\n        __shared__ float shared_amax[bsize];\n        shared_amax[tid] = amax;\n        __syncthreads();\n#pragma unroll\n        for (size_t off = bsize / 2; off > 0; off /= 2) {\n          if (tid < off) {\n            shared_amax[tid] = fmaxf(shared_amax[tid], shared_amax[tid + off]);\n          }\n          __syncthreads();\n        }\n        amax = shared_amax[tid];\n      } break;\n      default:\n        amax = 0;\n    }\n  }\n\n  // Update scale\n  if (tid == 0) {\n    // Update scale\n    float scale;\n    if (isfinite(amax) && amax > 0) {\n      scale = scaled_max / amax;\n    } else {\n      scale = scale_ptr[bid];\n    }\n    // When the amax is too tiny that the scale becoming infinite in FP32,\n    // we set the scale to the max value of FP32. In this case, the tensor\u2019s\n    // amax won't get mapped to the FP8 max representable, but rather\n    // something below that, but this is the best thing we can do.\n    if (isinf(scale)) {\n      scale = std::numeric_limits<float>::max();\n    }\n    updated_scale_ptr[bid] = scale;\n  }\n}\n\n/* CUDA kernel to bulk-update amax history and FP8 scaling factors\n *\n * Block dims: bsize x 1 x 1\n *\n * Grid dims: num_tensors x 1 x 1\n */\n__global__ void __launch_bounds__(bsize)\n    kernel_bulk(float* amax_reduction_buffer, AmaxParams p, size_t amax_history_length,\n                AmaxComputeAlgo amax_compute_algo, float scaled_max) {\n  const size_t bid = blockIdx.x;\n  const size_t tid = threadIdx.x;\n  const int num_scale = p.param[bid].num_scale;\n\n  int offset_in_buffer = 0;\n  for (int j = 0; j < bid; j++) {\n    offset_in_buffer += p.param[j].num_scale;\n  }\n\n  for (int count = 0; count < num_scale; count++) {\n    // Update amax\n    float amax = 0;\n    {\n      // Roll amax history\n      const auto& length = amax_history_length;\n      const auto& stride = p.param[bid].num_scale;\n      auto* amax_history = p.param[bid].amax_history + count;\n      const auto last_amax = ((amax_reduction_buffer != nullptr) &&\n                              (amax_reduction_buffer[offset_in_buffer + count] != 0.0f))\n                                 ? amax_reduction_buffer[offset_in_buffer + count]\n                                 : amax_history[0];\n      if (last_amax != 0.0f) {\n        for (size_t off = 0; off < length; off += bsize) {\n          const size_t i = off + tid;\n          float a = 0;\n          if (i < length) {\n            a = (i < length - 1) ? amax_history[(i + 1) * stride] : last_amax;\n            amax = fmaxf(amax, a);\n          }\n          __syncthreads();  // Inplace roll\n          if (i < length) {\n            amax_history[i * stride] = (i > 0) ? a : 0;\n          }\n        }\n      }\n\n      // Compute amax to use for scaling factor\n      switch (amax_compute_algo) {\n        case AmaxComputeAlgo::MOST_RECENT:\n          amax = last_amax;\n          break;\n        case AmaxComputeAlgo::MAX: {\n          __shared__ float shared_amax[bsize];\n          shared_amax[tid] = amax;\n          __syncthreads();\n#pragma unroll\n          for (size_t off = bsize / 2; off > 0; off /= 2) {\n            if (tid < off) {\n              shared_amax[tid] = fmaxf(shared_amax[tid], shared_amax[tid + off]);\n            }\n            __syncthreads();\n          }\n          amax = shared_amax[tid];\n        } break;\n        default:\n          amax = 0;\n      }\n    }\n\n    // Update scale\n    if (tid == 0) {\n      // Computing the scaling factor requires consideration of the following scenarios:\n      // 1. amax == 0:\n      //    No action is possible, set scale to the previous scale (or 1).\n      // 2. 0 < amax < tiny_amax\n      //    The amax is too tiny that the scale becomes infinite in FP32.\n      //    Set scale = FP32_max\n      // 3. tiny_amax <= amax < FP32_max:\n      //    Set scale = FP8_max (or scaled_max) / amax\n      // 4. When amax == inf or amax == nan:\n      //    No action is possible, set scale to the previous scale (or 1).\n\n      float scale;\n      if (isfinite(amax) && amax > 0) {\n        scale = scaled_max / amax;\n      } else {\n        scale = p.param[bid].scale[count];\n      }\n      // When the amax is too tiny that the scale becoming infinite in FP32,\n      // we set the scale to the max value of FP32. In this case, the tensor\u2019s\n      // amax won't get mapped to the FP8 max representable, but rather\n      // something below that, but this is the best thing we can do.\n      if (isinf(scale)) {\n        scale = std::numeric_limits<float>::max();\n      }\n      p.param[bid].scale[count] = scale;\n    }\n  }\n}\n\n}  // namespace amax_and_scale_update_impl\n\n}  // namespace\n\nvoid amax_and_scale_update(const Tensor& amax_history, const Tensor& scale,\n                           Tensor* updated_amax_history_, Tensor* updated_scale_,\n                           const std::string& amax_compute_algo, DType fp8_dtype, float margin,\n                           hipStream_t stream) {\n  auto& updated_amax_history = *updated_amax_history_;\n  auto& updated_scale = *updated_scale_;\n\n  // Check tensors\n  NVTE_CHECK(amax_history.data.shape.size() == 2, \"Found \", amax_history.data.shape.size(),\n             \" dims\");\n  const size_t amax_history_length = amax_history.data.shape[0];\n  const size_t num_scales = amax_history.data.shape[1];\n  NVTE_CHECK(amax_history.data.dtype == DType::kFloat32, \"Found \",\n             dtype_name(amax_history.data.dtype), \".\");\n  NVTE_CHECK(scale.numel() == num_scales, \"Expected \", num_scales, \" elements, \", \"but found \",\n             scale.numel(), \".\");\n  NVTE_CHECK(scale.data.dtype == DType::kFloat32, \"Found \", dtype_name(scale.data.dtype), \".\");\n  NVTE_CHECK(updated_amax_history.data.shape.size() == 2, \"Found \",\n             updated_amax_history.data.shape.size(), \" dims.\");\n  NVTE_CHECK(updated_amax_history.data.shape[0] == amax_history_length, \"Expected \",\n             amax_history_length, \", \", \"but found \", updated_amax_history.data.shape[0]);\n  NVTE_CHECK(updated_amax_history.data.shape[1] == num_scales, \"Expected \", num_scales, \", \",\n             \"but found \", updated_amax_history.data.shape[1]);\n  NVTE_CHECK(updated_amax_history.data.dtype == DType::kFloat32, \"Got \",\n             dtype_name(updated_amax_history.data.dtype), \".\");\n  NVTE_CHECK(updated_scale.numel() == num_scales, \"Expected \", num_scales, \" elements, \",\n             \"but found \", updated_scale.numel(), \".\");\n  NVTE_CHECK(updated_scale.data.dtype == DType::kFloat32, \"Got \",\n             dtype_name(updated_scale.data.dtype), \".\");\n\n  // amax value to use for updating scaling factor\n  AmaxComputeAlgo amax_compute_algo_ = AmaxComputeAlgo::INVALID;\n  if (amax_compute_algo == \"max\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MAX;\n  } else if (amax_compute_algo == \"most_recent\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MOST_RECENT;\n  } else {\n    NVTE_ERROR(\"Unsupported amax compute algorithm (\", amax_compute_algo, \")\");\n  }\n\n  // Expected maximum value after scale is applied\n  const float scaled_max = fp8_dtype_max(fp8_dtype) * std::pow(2.f, -margin);\n\n  // Launch CUDA kernel\n  constexpr size_t block_size = amax_and_scale_update_impl::bsize;\n  const size_t grid_size = num_scales;\n  amax_and_scale_update_impl::kernel<<<grid_size, block_size, 0, stream>>>(\n      static_cast<const float*>(amax_history.data.dptr), static_cast<const float*>(scale.data.dptr),\n      static_cast<float*>(updated_amax_history.data.dptr),\n      static_cast<float*>(updated_scale.data.dptr), amax_history_length, num_scales,\n      amax_compute_algo_, scaled_max);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid amax_and_scale_update_after_reduction(const Tensor& amax_reduction_buffer,\n                                           std::vector<Tensor*> amax_histories,\n                                           std::vector<Tensor*> scales,\n                                           const std::string& amax_compute_algo, DType fp8_dtype,\n                                           float margin, hipStream_t stream) {\n  using namespace transformer_engine;\n\n  // amax value to use for updating scaling factor\n  AmaxComputeAlgo amax_compute_algo_ = AmaxComputeAlgo::INVALID;\n  if (amax_compute_algo == \"max\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MAX;\n  } else if (amax_compute_algo == \"most_recent\") {\n    amax_compute_algo_ = AmaxComputeAlgo::MOST_RECENT;\n  } else {\n    NVTE_ERROR(\"Unsupported amax compute algorithm (\", amax_compute_algo, \")\");\n  }\n\n  // Expected maximum value after scale is applied\n  const float scaled_max = fp8_dtype_max(fp8_dtype) * std::pow(2.f, -margin);\n\n  // Number of tensors in the bulk\n  const size_t num_tensors = amax_histories.size();\n  size_t num_remaining_tensors = num_tensors;\n  const int num_kernels = (num_tensors + AMAX_PARAMS_LIMIT - 1) / AMAX_PARAMS_LIMIT;\n  size_t amax_history_length = 0;\n  if (num_tensors > 0) {\n    amax_history_length = amax_histories[0]->data.shape[0];\n  }\n\n  // amax parameters\n  float* amax_buffer = static_cast<float*>(amax_reduction_buffer.data.dptr);\n  AmaxParams p;\n  for (int iter = 0; iter < num_kernels; iter++) {\n    size_t kernel_num_scales = 0;\n    size_t kernel_num_tensors =\n        (iter == (num_kernels - 1)) ? num_remaining_tensors : AMAX_PARAMS_LIMIT;\n    for (size_t pi = 0; pi < kernel_num_tensors; pi++) {\n      size_t i = iter * AMAX_PARAMS_LIMIT + pi;\n\n      // Check tensors\n      int num_scale = amax_histories[i]->data.shape[1];\n      NVTE_CHECK(amax_histories[i]->data.dtype == DType::kFloat32, \"Found \",\n                 dtype_name(amax_histories[i]->data.dtype), \".\");\n      NVTE_CHECK(amax_histories[i]->data.shape.size() == 2, \"Found \",\n                 amax_histories[i]->data.shape.size(), \" dims\");\n      NVTE_CHECK(amax_histories[i]->numel() == amax_history_length * num_scale, \"Expected \",\n                 amax_history_length * num_scale, \" elements, \", \"but found \",\n                 amax_histories[i]->numel(), \".\");\n      NVTE_CHECK(scales[i]->data.dtype == DType::kFloat32, \"Found \",\n                 dtype_name(scales[i]->data.dtype), \".\");\n      NVTE_CHECK(scales[i]->data.shape.size() == 1, \"Found \", scales[i]->data.shape.size(),\n                 \" dims\");\n      NVTE_CHECK(scales[i]->numel() == num_scale, \"Expected \", num_scale, \" elements, \", \"Found \",\n                 scales[i]->numel(), \".\");\n\n      // amax parameters\n      kernel_num_scales += num_scale;\n      p.param[pi].num_scale = num_scale;\n      p.param[pi].amax_history = static_cast<float*>(amax_histories[i]->data.dptr);\n      p.param[pi].scale = static_cast<float*>(scales[i]->data.dptr);\n    }\n\n    // Launch CUDA kernel\n    size_t grid_size = kernel_num_tensors;\n    const size_t block_size = amax_and_scale_update_impl::bsize;\n    amax_and_scale_update_impl::kernel_bulk<<<grid_size, block_size, 0, stream>>>(\n        amax_buffer, p, amax_history_length, amax_compute_algo_, scaled_max);\n    NVTE_CHECK_CUDA(hipGetLastError());\n\n    // shift amax buffer pointer\n    if (amax_buffer != nullptr) {\n      amax_buffer += kernel_num_scales;\n    }\n    num_remaining_tensors -= AMAX_PARAMS_LIMIT;\n  }\n}\n\n}  // namespace delayed_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_delayed_scaling_recipe_amax_and_scale_update(\n    const NVTETensor amax_history, const NVTETensor scale, NVTETensor updated_amax_history,\n    NVTETensor updated_scale, const char* amax_compute_algo, NVTEDType fp8_dtype, float margin,\n    hipStream_t stream) {\n  NVTE_API_CALL(nvte_delayed_scaling_recipe_amax_and_scale_update);\n  using namespace transformer_engine;\n  delayed_scaling_recipe::amax_and_scale_update(\n      *convertNVTETensorCheck(amax_history), *convertNVTETensorCheck(scale),\n      convertNVTETensor(updated_amax_history), convertNVTETensor(updated_scale), amax_compute_algo,\n      static_cast<DType>(fp8_dtype), margin, stream);\n}\n\nvoid nvte_delayed_scaling_recipe_amax_and_scale_update_after_reduction(\n    const NVTETensor amax_reduction_buffer, std::vector<NVTETensor> amax_histories,\n    std::vector<NVTETensor> scales, const char* amax_compute_algo, NVTEDType fp8_dtype,\n    float margin, hipStream_t stream) {\n  NVTE_API_CALL(nvte_delayed_scaling_recipe_amax_and_scale_update_after_reduction);\n  using namespace transformer_engine;\n  size_t num_tensors = amax_histories.size();\n  std::vector<Tensor*> t_amax_histories, t_scales;\n  for (size_t i = 0; i < num_tensors; i++) {\n    t_amax_histories.push_back(convertNVTETensor(amax_histories[i]));\n    t_scales.push_back(convertNVTETensor(scales[i]));\n  }\n  delayed_scaling_recipe::amax_and_scale_update_after_reduction(\n      *convertNVTETensorCheck(amax_reduction_buffer), t_amax_histories, t_scales, amax_compute_algo,\n      static_cast<DType>(fp8_dtype), margin, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/recipe/delayed_scaling.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/recipe.h>\n\n#include \"../common.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace mxfp8_scaling_recipe {\n\nconstexpr int rowwise_row_padding = 128;  // Row padding of rowwise_scale and rowwise_amax\nconstexpr int rowwise_col_padding = 4;    // Column padding of rowwise_scale and rowwise_amax\nconstexpr int colwise_row_padding = 4;    // Row padding of colwise_scale and colwise_amax\nconstexpr int colwise_col_padding = 128;  // Column padding of colwise_scale and colwise_amax\n\nconstexpr int kRowsPerTile = 32;   // Rows each block processes\nconstexpr int kColsPerTile = 128;  // Columns each block processes\n\nconstexpr int kThreadsPerBlock = 128;\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    mxfp8_scaling_compute_partial_amax_kernel(const IType *input, IType *amax_rowwise,\n                                              IType *amax_colwise, int amax_rowwise_stride,\n                                              int amax_colwise_stride, int rows, int cols,\n                                              size_t start_offset, size_t len) {\n  __shared__ float smem_amax_rowwise[kRowsPerTile][kColsPerTile / 32];\n\n  size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  int warp_idx = threadIdx.x / 32;\n  int lane_idx = threadIdx.x % 32;\n  int c = blockIdx.x * kColsPerTile + threadIdx.x;\n  int r = blockIdx.y * kRowsPerTile;\n\n  float col_amax = 0.0f;\n#pragma unroll\n  for (int i = 0; i < kRowsPerTile; i++) {\n    size_t idx = r * cols + c;\n    float row_amax = 0.0f;\n\n    if (r < rows && c < cols && idx >= start_offset && idx < end_offset) {\n      float abs_input = fabs(static_cast<float>(input_minus_offset[idx]));\n      row_amax = fmaxf(row_amax, abs_input);\n      col_amax = fmaxf(col_amax, abs_input);\n    }\n\n#pragma unroll\n    for (int delta = 16; delta > 0; delta /= 2) {\n      float other_row_amax = __shfl_down_sync(0xFFFFFFFF, row_amax, delta);\n      row_amax = fmaxf(row_amax, other_row_amax);\n    }\n\n    if (lane_idx == 0) {\n      smem_amax_rowwise[i][warp_idx] = row_amax;\n    }\n\n    r++;\n  }\n\n  amax_colwise[blockIdx.y * amax_colwise_stride + c] = static_cast<IType>(col_amax);\n\n  __syncthreads();\n\n  int r_ = threadIdx.x / (kColsPerTile / 32);  // rows in shared memory\n  int c_ = threadIdx.x % (kColsPerTile / 32);  // cols in shared memory\n  r = blockIdx.y * kRowsPerTile + r_;\n  c = blockIdx.x * kColsPerTile / 32 + c_;\n  amax_rowwise[r * amax_rowwise_stride + c] = static_cast<IType>(smem_amax_rowwise[r_][c_]);\n}\n\ntemplate <typename IType, typename OType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    mxfp8_scaling_partial_cast_kernel(const IType *input, OType *output_rowwise,\n                                      OType *output_colwise, const e8m0_t *scale_inv_rowwise,\n                                      const e8m0_t *scale_inv_colwise, int scale_inv_rowwise_stride,\n                                      int scale_inv_colwise_stride, int rows, int cols,\n                                      size_t start_offset, size_t len) {\n  __shared__ float smem_scales_rowwise[kRowsPerTile][kColsPerTile / 32];\n  __shared__ float smem_scales_colwise[kColsPerTile];\n\n  // Load scales_rowwise\n  {\n    int r_ = threadIdx.x / (kColsPerTile / 32);  // rows in shared memory\n    int c_ = threadIdx.x % (kColsPerTile / 32);  // cols in shared memory\n    int r = blockIdx.y * kRowsPerTile + r_;\n    int c = blockIdx.x * kColsPerTile / 32 + c_;\n    size_t idx = r * scale_inv_rowwise_stride + c;\n    smem_scales_rowwise[r_][c_] = ptx::exp2f_rcp<float>(scale_inv_rowwise[idx]);\n  }\n\n  // Load scales_colwise\n  {\n    int c_ = threadIdx.x;\n    int r = blockIdx.y * kRowsPerTile / 32;\n    int c = blockIdx.x * kColsPerTile + c_;\n    size_t idx = r * scale_inv_colwise_stride + c;\n    smem_scales_colwise[c_] = ptx::exp2f_rcp<float>(scale_inv_colwise[idx]);\n  }\n\n  __syncthreads();\n\n  size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  OType *output_rowwise_minus_offset = output_rowwise - start_offset;\n  OType *output_colwise_minus_offset = output_colwise - start_offset;\n  int warp_idx = threadIdx.x / 32;\n  // int lane_idx = threadIdx.x % 32;\n  int c = blockIdx.x * kColsPerTile + threadIdx.x;\n  int r = blockIdx.y * kRowsPerTile;\n\n#pragma unroll\n  for (int i = 0; i < kRowsPerTile; i++) {\n    size_t idx = r * cols + c;\n\n    if (r < rows && c < cols && idx >= start_offset && idx < end_offset) {\n      float inp = static_cast<float>(input_minus_offset[idx]);\n      OType out_rowwise = static_cast<OType>(inp * smem_scales_rowwise[i][warp_idx]);\n      OType out_colwise = static_cast<OType>(inp * smem_scales_colwise[threadIdx.x]);\n      output_rowwise_minus_offset[idx] = out_rowwise;\n      output_colwise_minus_offset[idx] = out_colwise;\n    }\n\n    r++;\n  }\n}\n\nvoid mxfp8_scaling_compute_partial_amax(const Tensor input, Tensor amax_rowwise,\n                                        Tensor amax_colwise, int rows, int cols,\n                                        size_t start_offset, cudaStream_t stream) {\n  NVTE_CHECK(rows % 32 == 0, \"rows must be divisible by 32\");\n  NVTE_CHECK(cols % 32 == 0, \"cols must be divisible by 32\");\n\n  NVTE_CHECK(input.data.shape.size() == 1, \"input must be a 1D tensor\");\n  NVTE_CHECK(start_offset + input.data.shape[0] <= static_cast<size_t>(rows) * cols,\n             \"Invalid start_offset\");\n\n  NVTE_CHECK(amax_rowwise.data.shape.size() == 2, \"amax_rowwise must be a 2D tensor\");\n  NVTE_CHECK(amax_rowwise.data.shape[0] % rowwise_row_padding == 0,\n             \"Wrong padding of amax_rowwise's rows\");\n  NVTE_CHECK(amax_rowwise.data.shape[0] >= rows, \"Invalid rows\");\n  NVTE_CHECK(amax_rowwise.data.shape[1] % rowwise_col_padding == 0,\n             \"Wrong padding of amax_rowwise's cols\");\n  NVTE_CHECK(amax_rowwise.data.shape[1] >= cols / 32, \"Invalid cols\");\n  NVTE_CHECK(amax_rowwise.dtype() == input.dtype(), \"Wrong dtype of amax_rowwise\");\n\n  NVTE_CHECK(amax_colwise.data.shape.size() == 2, \"amax_colwise must be a 2D tensor\");\n  NVTE_CHECK(amax_colwise.data.shape[0] % colwise_row_padding == 0,\n             \"Wrong padding of amax_colwise's rows\");\n  NVTE_CHECK(amax_colwise.data.shape[0] >= rows / 32, \"Invalid rows\");\n  NVTE_CHECK(amax_colwise.data.shape[1] % colwise_col_padding == 0,\n             \"Wrong padding of amax_colwise's cols\");\n  NVTE_CHECK(amax_colwise.data.shape[1] >= cols, \"Invalid cols\");\n  NVTE_CHECK(amax_colwise.dtype() == input.dtype(), \"Wrong dtype of amax_colwise\");\n\n  int blocks_x = (cols + kColsPerTile - 1) / kColsPerTile;\n  int blocks_y = (rows + kRowsPerTile - 1) / kRowsPerTile;\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input.dtype(), IType,\n      mxfp8_scaling_compute_partial_amax_kernel<IType><<<grid, kColsPerTile, 0, stream>>>(\n          reinterpret_cast<const IType *>(input.data.dptr),\n          reinterpret_cast<IType *>(amax_rowwise.data.dptr),\n          reinterpret_cast<IType *>(amax_colwise.data.dptr), amax_rowwise.data.shape[1],\n          amax_colwise.data.shape[1], rows, cols, start_offset, input.data.shape[0]);)\n}\n\nvoid mxfp8_scaling_partial_cast(const Tensor input, Tensor output_rowwise, Tensor output_colwise,\n                                const Tensor scale_inv_rowwise, const Tensor scale_inv_colwise,\n                                int rows, int cols, size_t start_offset, cudaStream_t stream) {\n  NVTE_CHECK(rows % 32 == 0, \"rows must be divisible by 32\");\n  NVTE_CHECK(cols % 32 == 0, \"cols must be divisible by 32\");\n\n  NVTE_CHECK(input.data.shape.size() == 1, \"input must be a 1D tensor\");\n  NVTE_CHECK(start_offset + input.data.shape[0] <= static_cast<size_t>(rows) * cols,\n             \"Invalid start_offset\");\n\n  NVTE_CHECK(output_rowwise.data.shape.size() == 1, \"output_rowwise must be a 1D tensor\");\n  NVTE_CHECK(output_colwise.data.shape.size() == 1, \"output_colwise must be a 1D tensor\");\n  NVTE_CHECK(output_rowwise.data.shape[0] == input.data.shape[0],\n             \"Size of input and output_rowwise mismatch\");\n  NVTE_CHECK(output_colwise.data.shape[0] == input.data.shape[0],\n             \"Size of input and output_colwise mismatch\");\n\n  NVTE_CHECK(output_rowwise.dtype() == DType::kFloat8E4M3 || output_rowwise.dtype() == DType::kByte,\n             \"output_rowwise should be e4m3 or uint8\");\n  NVTE_CHECK(output_colwise.dtype() == DType::kFloat8E4M3 || output_colwise.dtype() == DType::kByte,\n             \"output_colwise should be e4m3 or uint8\");\n\n  NVTE_CHECK(scale_inv_rowwise.data.shape.size() == 2, \"scale_inv_rowwise must be a 2D tensor\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[0] % rowwise_row_padding == 0,\n             \"Wrong padding of scale_inv_rowwise's rows\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[0] >= rows, \"Invalid rows\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[1] % rowwise_col_padding == 0,\n             \"Wrong padding of scale_inv_rowwise's cols\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[1] >= cols / 32, \"Invalid cols\");\n  NVTE_CHECK(scale_inv_rowwise.dtype() == DType::kByte, \"Wrong dtype of scale_inv_rowwise\");\n\n  NVTE_CHECK(scale_inv_colwise.data.shape.size() == 2, \"scale_inv_colwise must be a 2D tensor\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[0] % colwise_row_padding == 0,\n             \"Wrong padding of scale_inv_colwise's rows\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[0] >= rows / 32, \"Invalid rows\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[1] % colwise_col_padding == 0,\n             \"Wrong padding of scale_inv_colwise's cols\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[1] >= cols, \"Invalid cols\");\n  NVTE_CHECK(scale_inv_colwise.dtype() == DType::kByte, \"Wrong dtype of scale_inv_colwise\");\n\n  int blocks_x = (cols + kColsPerTile - 1) / kColsPerTile;\n  int blocks_y = (rows + kRowsPerTile - 1) / kRowsPerTile;\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input.dtype(), IType,\n      mxfp8_scaling_partial_cast_kernel<IType, fp8e4m3><<<grid, kColsPerTile, 0, stream>>>(\n          reinterpret_cast<const IType *>(input.data.dptr),\n          reinterpret_cast<fp8e4m3 *>(output_rowwise.data.dptr),\n          reinterpret_cast<fp8e4m3 *>(output_colwise.data.dptr),\n          reinterpret_cast<const e8m0_t *>(scale_inv_rowwise.data.dptr),\n          reinterpret_cast<const e8m0_t *>(scale_inv_colwise.data.dptr),\n          scale_inv_rowwise.data.shape[1], scale_inv_colwise.data.shape[1], rows, cols,\n          start_offset, input.data.shape[0]);)\n}\n\n}  // namespace mxfp8_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_mxfp8_scaling_compute_partial_amax(const NVTETensor input, NVTETensor amax_rowwise,\n                                             NVTETensor amax_colwise, int rows, int cols,\n                                             size_t start_offset, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_mxfp8_scaling_compute_partial_amax);\n  using namespace transformer_engine;\n  mxfp8_scaling_recipe::mxfp8_scaling_compute_partial_amax(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(amax_rowwise),\n      *convertNVTETensorCheck(amax_colwise), rows, cols, start_offset, stream);\n}\n\nvoid nvte_mxfp8_scaling_partial_cast(const NVTETensor input, NVTETensor output_rowwise,\n                                     NVTETensor output_colwise, const NVTETensor scale_inv_rowwise,\n                                     const NVTETensor scale_inv_colwise, int rows, int cols,\n                                     size_t start_offset, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_mxfp8_scaling_partial_cast);\n  using namespace transformer_engine;\n  mxfp8_scaling_recipe::mxfp8_scaling_partial_cast(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(output_rowwise),\n      *convertNVTETensorCheck(output_colwise), *convertNVTETensorCheck(scale_inv_rowwise),\n      *convertNVTETensorCheck(scale_inv_colwise), rows, cols, start_offset, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/recipe.h>\n\n#include \"../common.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace mxfp8_scaling_recipe {\n\nconstexpr int rowwise_row_padding = 128;  // Row padding of rowwise_scale and rowwise_amax\nconstexpr int rowwise_col_padding = 4;    // Column padding of rowwise_scale and rowwise_amax\nconstexpr int colwise_row_padding = 4;    // Row padding of colwise_scale and colwise_amax\nconstexpr int colwise_col_padding = 128;  // Column padding of colwise_scale and colwise_amax\n\nconstexpr int kRowsPerTile = 32;   // Rows each block processes\nconstexpr int kColsPerTile = 128;  // Columns each block processes\n\nconstexpr int kThreadsPerBlock = 128;\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    mxfp8_scaling_compute_partial_amax_kernel(const IType *input, IType *amax_rowwise,\n                                              IType *amax_colwise, int amax_rowwise_stride,\n                                              int amax_colwise_stride, int rows, int cols,\n                                              size_t start_offset, size_t len) {\n  __shared__ float smem_amax_rowwise[kRowsPerTile][kColsPerTile / 32];\n\n  size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  int warp_idx = threadIdx.x / 32;\n  int lane_idx = threadIdx.x % 32;\n  int c = blockIdx.x * kColsPerTile + threadIdx.x;\n  int r = blockIdx.y * kRowsPerTile;\n\n  float col_amax = 0.0f;\n#pragma unroll\n  for (int i = 0; i < kRowsPerTile; i++) {\n    size_t idx = r * cols + c;\n    float row_amax = 0.0f;\n\n    if (r < rows && c < cols && idx >= start_offset && idx < end_offset) {\n      float abs_input = fabs(static_cast<float>(input_minus_offset[idx]));\n      row_amax = fmaxf(row_amax, abs_input);\n      col_amax = fmaxf(col_amax, abs_input);\n    }\n\n#pragma unroll\n    for (int delta = 16; delta > 0; delta /= 2) {\n      float other_row_amax = __shfl_down_sync(0xFFFFFFFF, row_amax, delta);\n      row_amax = fmaxf(row_amax, other_row_amax);\n    }\n\n    if (lane_idx == 0) {\n      smem_amax_rowwise[i][warp_idx] = row_amax;\n    }\n\n    r++;\n  }\n\n  amax_colwise[blockIdx.y * amax_colwise_stride + c] = static_cast<IType>(col_amax);\n\n  __syncthreads();\n\n  int r_ = threadIdx.x / (kColsPerTile / 32);  // rows in shared memory\n  int c_ = threadIdx.x % (kColsPerTile / 32);  // cols in shared memory\n  r = blockIdx.y * kRowsPerTile + r_;\n  c = blockIdx.x * kColsPerTile / 32 + c_;\n  amax_rowwise[r * amax_rowwise_stride + c] = static_cast<IType>(smem_amax_rowwise[r_][c_]);\n}\n\ntemplate <typename IType, typename OType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    mxfp8_scaling_partial_cast_kernel(const IType *input, OType *output_rowwise,\n                                      OType *output_colwise, const e8m0_t *scale_inv_rowwise,\n                                      const e8m0_t *scale_inv_colwise, int scale_inv_rowwise_stride,\n                                      int scale_inv_colwise_stride, int rows, int cols,\n                                      size_t start_offset, size_t len) {\n  __shared__ float smem_scales_rowwise[kRowsPerTile][kColsPerTile / 32];\n  __shared__ float smem_scales_colwise[kColsPerTile];\n\n  // Load scales_rowwise\n  {\n    int r_ = threadIdx.x / (kColsPerTile / 32);  // rows in shared memory\n    int c_ = threadIdx.x % (kColsPerTile / 32);  // cols in shared memory\n    int r = blockIdx.y * kRowsPerTile + r_;\n    int c = blockIdx.x * kColsPerTile / 32 + c_;\n    size_t idx = r * scale_inv_rowwise_stride + c;\n    smem_scales_rowwise[r_][c_] = ptx::exp2f_rcp<float>(scale_inv_rowwise[idx]);\n  }\n\n  // Load scales_colwise\n  {\n    int c_ = threadIdx.x;\n    int r = blockIdx.y * kRowsPerTile / 32;\n    int c = blockIdx.x * kColsPerTile + c_;\n    size_t idx = r * scale_inv_colwise_stride + c;\n    smem_scales_colwise[c_] = ptx::exp2f_rcp<float>(scale_inv_colwise[idx]);\n  }\n\n  __syncthreads();\n\n  size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  OType *output_rowwise_minus_offset = output_rowwise - start_offset;\n  OType *output_colwise_minus_offset = output_colwise - start_offset;\n  int warp_idx = threadIdx.x / 32;\n  // int lane_idx = threadIdx.x % 32;\n  int c = blockIdx.x * kColsPerTile + threadIdx.x;\n  int r = blockIdx.y * kRowsPerTile;\n\n#pragma unroll\n  for (int i = 0; i < kRowsPerTile; i++) {\n    size_t idx = r * cols + c;\n\n    if (r < rows && c < cols && idx >= start_offset && idx < end_offset) {\n      float inp = static_cast<float>(input_minus_offset[idx]);\n      OType out_rowwise = static_cast<OType>(inp * smem_scales_rowwise[i][warp_idx]);\n      OType out_colwise = static_cast<OType>(inp * smem_scales_colwise[threadIdx.x]);\n      output_rowwise_minus_offset[idx] = out_rowwise;\n      output_colwise_minus_offset[idx] = out_colwise;\n    }\n\n    r++;\n  }\n}\n\nvoid mxfp8_scaling_compute_partial_amax(const Tensor input, Tensor amax_rowwise,\n                                        Tensor amax_colwise, int rows, int cols,\n                                        size_t start_offset, hipStream_t stream) {\n  NVTE_CHECK(rows % 32 == 0, \"rows must be divisible by 32\");\n  NVTE_CHECK(cols % 32 == 0, \"cols must be divisible by 32\");\n\n  NVTE_CHECK(input.data.shape.size() == 1, \"input must be a 1D tensor\");\n  NVTE_CHECK(start_offset + input.data.shape[0] <= static_cast<size_t>(rows) * cols,\n             \"Invalid start_offset\");\n\n  NVTE_CHECK(amax_rowwise.data.shape.size() == 2, \"amax_rowwise must be a 2D tensor\");\n  NVTE_CHECK(amax_rowwise.data.shape[0] % rowwise_row_padding == 0,\n             \"Wrong padding of amax_rowwise's rows\");\n  NVTE_CHECK(amax_rowwise.data.shape[0] >= rows, \"Invalid rows\");\n  NVTE_CHECK(amax_rowwise.data.shape[1] % rowwise_col_padding == 0,\n             \"Wrong padding of amax_rowwise's cols\");\n  NVTE_CHECK(amax_rowwise.data.shape[1] >= cols / 32, \"Invalid cols\");\n  NVTE_CHECK(amax_rowwise.dtype() == input.dtype(), \"Wrong dtype of amax_rowwise\");\n\n  NVTE_CHECK(amax_colwise.data.shape.size() == 2, \"amax_colwise must be a 2D tensor\");\n  NVTE_CHECK(amax_colwise.data.shape[0] % colwise_row_padding == 0,\n             \"Wrong padding of amax_colwise's rows\");\n  NVTE_CHECK(amax_colwise.data.shape[0] >= rows / 32, \"Invalid rows\");\n  NVTE_CHECK(amax_colwise.data.shape[1] % colwise_col_padding == 0,\n             \"Wrong padding of amax_colwise's cols\");\n  NVTE_CHECK(amax_colwise.data.shape[1] >= cols, \"Invalid cols\");\n  NVTE_CHECK(amax_colwise.dtype() == input.dtype(), \"Wrong dtype of amax_colwise\");\n\n  int blocks_x = (cols + kColsPerTile - 1) / kColsPerTile;\n  int blocks_y = (rows + kRowsPerTile - 1) / kRowsPerTile;\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input.dtype(), IType,\n      mxfp8_scaling_compute_partial_amax_kernel<IType><<<grid, kColsPerTile, 0, stream>>>(\n          reinterpret_cast<const IType *>(input.data.dptr),\n          reinterpret_cast<IType *>(amax_rowwise.data.dptr),\n          reinterpret_cast<IType *>(amax_colwise.data.dptr), amax_rowwise.data.shape[1],\n          amax_colwise.data.shape[1], rows, cols, start_offset, input.data.shape[0]);)\n}\n\nvoid mxfp8_scaling_partial_cast(const Tensor input, Tensor output_rowwise, Tensor output_colwise,\n                                const Tensor scale_inv_rowwise, const Tensor scale_inv_colwise,\n                                int rows, int cols, size_t start_offset, hipStream_t stream) {\n  NVTE_CHECK(rows % 32 == 0, \"rows must be divisible by 32\");\n  NVTE_CHECK(cols % 32 == 0, \"cols must be divisible by 32\");\n\n  NVTE_CHECK(input.data.shape.size() == 1, \"input must be a 1D tensor\");\n  NVTE_CHECK(start_offset + input.data.shape[0] <= static_cast<size_t>(rows) * cols,\n             \"Invalid start_offset\");\n\n  NVTE_CHECK(output_rowwise.data.shape.size() == 1, \"output_rowwise must be a 1D tensor\");\n  NVTE_CHECK(output_colwise.data.shape.size() == 1, \"output_colwise must be a 1D tensor\");\n  NVTE_CHECK(output_rowwise.data.shape[0] == input.data.shape[0],\n             \"Size of input and output_rowwise mismatch\");\n  NVTE_CHECK(output_colwise.data.shape[0] == input.data.shape[0],\n             \"Size of input and output_colwise mismatch\");\n\n  NVTE_CHECK(output_rowwise.dtype() == DType::kFloat8E4M3 || output_rowwise.dtype() == DType::kByte,\n             \"output_rowwise should be e4m3 or uint8\");\n  NVTE_CHECK(output_colwise.dtype() == DType::kFloat8E4M3 || output_colwise.dtype() == DType::kByte,\n             \"output_colwise should be e4m3 or uint8\");\n\n  NVTE_CHECK(scale_inv_rowwise.data.shape.size() == 2, \"scale_inv_rowwise must be a 2D tensor\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[0] % rowwise_row_padding == 0,\n             \"Wrong padding of scale_inv_rowwise's rows\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[0] >= rows, \"Invalid rows\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[1] % rowwise_col_padding == 0,\n             \"Wrong padding of scale_inv_rowwise's cols\");\n  NVTE_CHECK(scale_inv_rowwise.data.shape[1] >= cols / 32, \"Invalid cols\");\n  NVTE_CHECK(scale_inv_rowwise.dtype() == DType::kByte, \"Wrong dtype of scale_inv_rowwise\");\n\n  NVTE_CHECK(scale_inv_colwise.data.shape.size() == 2, \"scale_inv_colwise must be a 2D tensor\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[0] % colwise_row_padding == 0,\n             \"Wrong padding of scale_inv_colwise's rows\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[0] >= rows / 32, \"Invalid rows\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[1] % colwise_col_padding == 0,\n             \"Wrong padding of scale_inv_colwise's cols\");\n  NVTE_CHECK(scale_inv_colwise.data.shape[1] >= cols, \"Invalid cols\");\n  NVTE_CHECK(scale_inv_colwise.dtype() == DType::kByte, \"Wrong dtype of scale_inv_colwise\");\n\n  int blocks_x = (cols + kColsPerTile - 1) / kColsPerTile;\n  int blocks_y = (rows + kRowsPerTile - 1) / kRowsPerTile;\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      input.dtype(), IType,\n      mxfp8_scaling_partial_cast_kernel<IType, fp8e4m3><<<grid, kColsPerTile, 0, stream>>>(\n          reinterpret_cast<const IType *>(input.data.dptr),\n          reinterpret_cast<fp8e4m3 *>(output_rowwise.data.dptr),\n          reinterpret_cast<fp8e4m3 *>(output_colwise.data.dptr),\n          reinterpret_cast<const e8m0_t *>(scale_inv_rowwise.data.dptr),\n          reinterpret_cast<const e8m0_t *>(scale_inv_colwise.data.dptr),\n          scale_inv_rowwise.data.shape[1], scale_inv_colwise.data.shape[1], rows, cols,\n          start_offset, input.data.shape[0]);)\n}\n\n}  // namespace mxfp8_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_mxfp8_scaling_compute_partial_amax(const NVTETensor input, NVTETensor amax_rowwise,\n                                             NVTETensor amax_colwise, int rows, int cols,\n                                             size_t start_offset, hipStream_t stream) {\n  NVTE_API_CALL(nvte_mxfp8_scaling_compute_partial_amax);\n  using namespace transformer_engine;\n  mxfp8_scaling_recipe::mxfp8_scaling_compute_partial_amax(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(amax_rowwise),\n      *convertNVTETensorCheck(amax_colwise), rows, cols, start_offset, stream);\n}\n\nvoid nvte_mxfp8_scaling_partial_cast(const NVTETensor input, NVTETensor output_rowwise,\n                                     NVTETensor output_colwise, const NVTETensor scale_inv_rowwise,\n                                     const NVTETensor scale_inv_colwise, int rows, int cols,\n                                     size_t start_offset, hipStream_t stream) {\n  NVTE_API_CALL(nvte_mxfp8_scaling_partial_cast);\n  using namespace transformer_engine;\n  mxfp8_scaling_recipe::mxfp8_scaling_partial_cast(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(output_rowwise),\n      *convertNVTETensorCheck(output_colwise), *convertNVTETensorCheck(scale_inv_rowwise),\n      *convertNVTETensorCheck(scale_inv_colwise), rows, cols, start_offset, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/recipe/mxfp8_scaling.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/recipe.h>\n\n#include <cassert>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace fp8_block_scaling_recipe {\n\nconstexpr int kTileDim = 128;\nconstexpr int kThreadsPerBlock = 256;\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    fp8_block_scaling_compute_partial_amax_kernel(const IType *input, float *amax_ptr,\n                                                  const size_t amax_stride_h,\n                                                  const size_t amax_stride_w, const size_t h,\n                                                  const size_t w, const size_t start_offset,\n                                                  const size_t len) {\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = kTileDim / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kLoopsPerCol = kTileDim / kNumWarps;\n\n  const int tile_col = blockIdx.x;\n  const int tile_row = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  __shared__ float smem[kNumWarps];\n  float amax = 0.0f;\n\n  for (int loop_col = 0; loop_col < kLoopsPerCol; ++loop_col) {\n    size_t r = tile_row * kTileDim + loop_col * kNumWarps + threadIdx.x / kThreadsPerWarp;\n    for (int loop_row = 0; loop_row < kLoopsPerRow; ++loop_row) {\n      size_t c = tile_col * kTileDim + loop_row * kThreadsPerWarp + (threadIdx.x % kThreadsPerWarp);\n      size_t idx = r * w + c;\n      if (r < h && c < w && idx >= start_offset && idx < end_offset) {\n        float other_amax = fabs(static_cast<float>(input_minus_offset[idx]));\n        __builtin_assume(amax >= 0);\n        __builtin_assume(other_amax >= 0);\n        amax = fmaxf(amax, other_amax);\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    float other_amax = __shfl_down_sync(0xFFFFFFFF, amax, delta);\n    __builtin_assume(amax >= 0);\n    __builtin_assume(other_amax >= 0);\n    amax = fmaxf(amax, other_amax);\n  }\n\n  if (threadIdx.x % kThreadsPerWarp == 0) {\n    smem[threadIdx.x / kThreadsPerWarp] = amax;\n  }\n\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < kNumWarps; ++i) {\n      float other_amax = smem[i];\n      __builtin_assume(amax >= 0);\n      __builtin_assume(other_amax >= 0);\n      amax = fmaxf(amax, other_amax);\n    }\n    amax_ptr[tile_row * amax_stride_h + tile_col * amax_stride_w] = amax;\n  }\n}\n\ntemplate <typename IType, typename OType, bool kWidthAligned>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    fp8_block_scaling_partial_cast_kernel(const IType *input, OType *output, const float *scale_ptr,\n                                          const size_t scale_stride_h, const size_t scale_stride_w,\n                                          const size_t h, const size_t w, const size_t start_offset,\n                                          const size_t len) {\n  using transformer_engine::Vec;\n\n  static_assert(sizeof(OType) == 1);\n  constexpr int kNumOutputElemsPerBank = 4 / sizeof(OType);\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = kTileDim / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kRowsPerWarp = kTileDim / kNumWarps;\n\n  __shared__ OType smem[kTileDim][kTileDim + kNumOutputElemsPerBank];\n\n  const int tile_w = blockIdx.x;\n  const int tile_h = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  OType *output_minus_offset = output - start_offset;\n\n  const float scale = scale_ptr[tile_h * scale_stride_h + tile_w * scale_stride_w];\n\n  // Load input data into shared memory\n  bool skip_store = true;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    for (int j = 0; j < kLoopsPerRow; ++j) {\n      const int h_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n      const int w_in_smem = threadIdx.x % kThreadsPerWarp + kThreadsPerWarp * j;\n      const int h_in_input = tile_h * kTileDim + h_in_smem;\n      const int w_in_input = tile_w * kTileDim + w_in_smem;\n      const size_t idx_in_input = static_cast<size_t>(h_in_input) * w + w_in_input;\n      if (h_in_input < h && w_in_input < w && idx_in_input >= start_offset &&\n          idx_in_input < end_offset) {\n        float inp = static_cast<float>(input_minus_offset[idx_in_input]) * scale;\n        smem[h_in_smem][w_in_smem] = static_cast<OType>(inp);\n        skip_store = false;\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    bool other_skip_store = __shfl_down_sync(0xFFFFFFFF, skip_store, delta);\n    skip_store = skip_store && other_skip_store;\n  }\n  skip_store = __shfl_sync(0xFFFFFFFF, skip_store, 0);\n  if (skip_store) {\n    return;\n  }\n\n  // Store the casted data into the output.\n  // Note that this store operation might write \"out-of-bounds\", but it is intentional:\n  //   1. The \"out-of-bounds\" here only crosses the boundary of the \"local shard\" (i.e., the region\n  //      from start_offset to end_offset), not the boundary of the entire output memory. Therefore,\n  //      this out-of-bounds write will not cause illegal memory access.\n  //   2. We assume that the subsequent all-gather operation happens in-place, so any parts that\n  //      should not be updated here will be overwritten by the all-gather.\n  // This tricky approach allows us to avoid checking whether each output index falls within\n  // [start, end), resulting in a significant performance improvement.\n  Vec<OType, kNumOutputElemsPerBank> vec_output;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    const int row_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n    const int col_in_smem = threadIdx.x % kThreadsPerWarp * kNumOutputElemsPerBank;\n    for (int j = 0; j < kNumOutputElemsPerBank; ++j) {\n      vec_output.data.elt[j] = smem[row_in_smem][col_in_smem + j];\n    }\n    const int row_in_output = tile_h * kTileDim + row_in_smem;\n    const int col_in_output = tile_w * kTileDim + col_in_smem;\n    const size_t idx_in_output = static_cast<size_t>(row_in_output) * w + col_in_output;\n    if (row_in_output < h) {\n      if constexpr (kWidthAligned) {\n        vec_output.store_to(output_minus_offset + idx_in_output);\n      } else {\n        int num = min(static_cast<size_t>(kNumOutputElemsPerBank),\n                      static_cast<size_t>(col_in_output < w ? w - col_in_output : 0));\n        vec_output.store_to_elts(output_minus_offset, idx_in_output, num);\n      }\n    }\n  }\n}\n\nvoid fp8_block_scaling_compute_partial_amax(const Tensor inp, Tensor amax, size_t h, size_t w,\n                                            size_t amax_stride_h, size_t amax_stride_w,\n                                            size_t start_offset, size_t block_len,\n                                            cudaStream_t stream) {\n  NVTE_CHECK(block_len == 128, \"Currently only block_len = 128 is supported\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      fp8_block_scaling_compute_partial_amax_kernel<inp_dtype>\n      <<<grid, kThreadsPerBlock, 0, stream>>>(reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n                                              reinterpret_cast<float *>(amax.data.dptr),\n                                              amax_stride_h, amax_stride_w, h, w, start_offset,\n                                              len);)\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid fp8_block_scaling_partial_cast(const Tensor inp, Tensor out, const Tensor scale, size_t h,\n                                    size_t w, size_t scale_stride_h, size_t scale_stride_w,\n                                    size_t start_offset, size_t block_len, const DType out_dtype,\n                                    cudaStream_t stream) {\n  NVTE_CHECK(block_len == 128, \"Currently only block_len = 128 is supported\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          out_dtype, fp8_type,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              w % kTileDim == 0, kWidthAligned,\n              fp8_block_scaling_partial_cast_kernel<inp_dtype, fp8_type, kWidthAligned>\n              <<<grid, kThreadsPerBlock, 0, stream>>>(\n                  reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n                  reinterpret_cast<fp8_type *>(out.data.dptr),\n                  reinterpret_cast<const float *>(scale.data.dptr), scale_stride_h, scale_stride_w,\n                  h, w, start_offset, len);)))\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace fp8_block_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_fp8_block_scaling_compute_partial_amax(const NVTETensor inp, NVTETensor amax, size_t h,\n                                                 size_t w, size_t amax_stride_h,\n                                                 size_t amax_stride_w, size_t start_offset,\n                                                 size_t block_len, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_block_scaling_compute_partial_amax);\n  using namespace transformer_engine;\n  fp8_block_scaling_recipe::fp8_block_scaling_compute_partial_amax(\n      *convertNVTETensorCheck(inp), *convertNVTETensorCheck(amax), h, w, amax_stride_h,\n      amax_stride_w, start_offset, block_len, stream);\n}\n\nvoid nvte_fp8_block_scaling_partial_cast(const NVTETensor inp, NVTETensor out,\n                                         const NVTETensor scale, size_t h, size_t w,\n                                         size_t scale_stride_h, size_t scale_stride_w,\n                                         size_t start_offset, size_t block_len,\n                                         const NVTEDType out_dtype, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_block_scaling_partial_cast);\n  using namespace transformer_engine;\n  fp8_block_scaling_recipe::fp8_block_scaling_partial_cast(\n      *convertNVTETensorCheck(inp), *convertNVTETensorCheck(out), *convertNVTETensorCheck(scale), h,\n      w, scale_stride_h, scale_stride_w, start_offset, block_len, static_cast<DType>(out_dtype),\n      stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/recipe.h>\n\n#include <cassert>\n\n#include \"../common.h\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace fp8_block_scaling_recipe {\n\nconstexpr int kTileDim = 128;\nconstexpr int kThreadsPerBlock = 256;\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    fp8_block_scaling_compute_partial_amax_kernel(const IType *input, float *amax_ptr,\n                                                  const size_t amax_stride_h,\n                                                  const size_t amax_stride_w, const size_t h,\n                                                  const size_t w, const size_t start_offset,\n                                                  const size_t len) {\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = kTileDim / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kLoopsPerCol = kTileDim / kNumWarps;\n\n  const int tile_col = blockIdx.x;\n  const int tile_row = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  __shared__ float smem[kNumWarps];\n  float amax = 0.0f;\n\n  for (int loop_col = 0; loop_col < kLoopsPerCol; ++loop_col) {\n    size_t r = tile_row * kTileDim + loop_col * kNumWarps + threadIdx.x / kThreadsPerWarp;\n    for (int loop_row = 0; loop_row < kLoopsPerRow; ++loop_row) {\n      size_t c = tile_col * kTileDim + loop_row * kThreadsPerWarp + (threadIdx.x % kThreadsPerWarp);\n      size_t idx = r * w + c;\n      if (r < h && c < w && idx >= start_offset && idx < end_offset) {\n        float other_amax = fabs(static_cast<float>(input_minus_offset[idx]));\n        __builtin_assume(amax >= 0);\n        __builtin_assume(other_amax >= 0);\n        amax = fmaxf(amax, other_amax);\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    float other_amax = __shfl_down_sync(0xFFFFFFFF, amax, delta);\n    __builtin_assume(amax >= 0);\n    __builtin_assume(other_amax >= 0);\n    amax = fmaxf(amax, other_amax);\n  }\n\n  if (threadIdx.x % kThreadsPerWarp == 0) {\n    smem[threadIdx.x / kThreadsPerWarp] = amax;\n  }\n\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < kNumWarps; ++i) {\n      float other_amax = smem[i];\n      __builtin_assume(amax >= 0);\n      __builtin_assume(other_amax >= 0);\n      amax = fmaxf(amax, other_amax);\n    }\n    amax_ptr[tile_row * amax_stride_h + tile_col * amax_stride_w] = amax;\n  }\n}\n\ntemplate <typename IType, typename OType, bool kWidthAligned>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    fp8_block_scaling_partial_cast_kernel(const IType *input, OType *output, const float *scale_ptr,\n                                          const size_t scale_stride_h, const size_t scale_stride_w,\n                                          const size_t h, const size_t w, const size_t start_offset,\n                                          const size_t len) {\n  using transformer_engine::Vec;\n\n  static_assert(sizeof(OType) == 1);\n  constexpr int kNumOutputElemsPerBank = 4 / sizeof(OType);\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = kTileDim / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kRowsPerWarp = kTileDim / kNumWarps;\n\n  __shared__ OType smem[kTileDim][kTileDim + kNumOutputElemsPerBank];\n\n  const int tile_w = blockIdx.x;\n  const int tile_h = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n  OType *output_minus_offset = output - start_offset;\n\n  const float scale = scale_ptr[tile_h * scale_stride_h + tile_w * scale_stride_w];\n\n  // Load input data into shared memory\n  bool skip_store = true;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    for (int j = 0; j < kLoopsPerRow; ++j) {\n      const int h_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n      const int w_in_smem = threadIdx.x % kThreadsPerWarp + kThreadsPerWarp * j;\n      const int h_in_input = tile_h * kTileDim + h_in_smem;\n      const int w_in_input = tile_w * kTileDim + w_in_smem;\n      const size_t idx_in_input = static_cast<size_t>(h_in_input) * w + w_in_input;\n      if (h_in_input < h && w_in_input < w && idx_in_input >= start_offset &&\n          idx_in_input < end_offset) {\n        float inp = static_cast<float>(input_minus_offset[idx_in_input]) * scale;\n        smem[h_in_smem][w_in_smem] = static_cast<OType>(inp);\n        skip_store = false;\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    bool other_skip_store = __shfl_down_sync(0xFFFFFFFF, skip_store, delta);\n    skip_store = skip_store && other_skip_store;\n  }\n  skip_store = __shfl_sync(0xFFFFFFFF, skip_store, 0);\n  if (skip_store) {\n    return;\n  }\n\n  // Store the casted data into the output.\n  // Note that this store operation might write \"out-of-bounds\", but it is intentional:\n  //   1. The \"out-of-bounds\" here only crosses the boundary of the \"local shard\" (i.e., the region\n  //      from start_offset to end_offset), not the boundary of the entire output memory. Therefore,\n  //      this out-of-bounds write will not cause illegal memory access.\n  //   2. We assume that the subsequent all-gather operation happens in-place, so any parts that\n  //      should not be updated here will be overwritten by the all-gather.\n  // This tricky approach allows us to avoid checking whether each output index falls within\n  // [start, end), resulting in a significant performance improvement.\n  Vec<OType, kNumOutputElemsPerBank> vec_output;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    const int row_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n    const int col_in_smem = threadIdx.x % kThreadsPerWarp * kNumOutputElemsPerBank;\n    for (int j = 0; j < kNumOutputElemsPerBank; ++j) {\n      vec_output.data.elt[j] = smem[row_in_smem][col_in_smem + j];\n    }\n    const int row_in_output = tile_h * kTileDim + row_in_smem;\n    const int col_in_output = tile_w * kTileDim + col_in_smem;\n    const size_t idx_in_output = static_cast<size_t>(row_in_output) * w + col_in_output;\n    if (row_in_output < h) {\n      if constexpr (kWidthAligned) {\n        vec_output.store_to(output_minus_offset + idx_in_output);\n      } else {\n        int num = min(static_cast<size_t>(kNumOutputElemsPerBank),\n                      static_cast<size_t>(col_in_output < w ? w - col_in_output : 0));\n        vec_output.store_to_elts(output_minus_offset, idx_in_output, num);\n      }\n    }\n  }\n}\n\nvoid fp8_block_scaling_compute_partial_amax(const Tensor inp, Tensor amax, size_t h, size_t w,\n                                            size_t amax_stride_h, size_t amax_stride_w,\n                                            size_t start_offset, size_t block_len,\n                                            hipStream_t stream) {\n  NVTE_CHECK(block_len == 128, \"Currently only block_len = 128 is supported\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      fp8_block_scaling_compute_partial_amax_kernel<inp_dtype>\n      <<<grid, kThreadsPerBlock, 0, stream>>>(reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n                                              reinterpret_cast<float *>(amax.data.dptr),\n                                              amax_stride_h, amax_stride_w, h, w, start_offset,\n                                              len);)\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid fp8_block_scaling_partial_cast(const Tensor inp, Tensor out, const Tensor scale, size_t h,\n                                    size_t w, size_t scale_stride_h, size_t scale_stride_w,\n                                    size_t start_offset, size_t block_len, const DType out_dtype,\n                                    hipStream_t stream) {\n  NVTE_CHECK(block_len == 128, \"Currently only block_len = 128 is supported\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(\n          out_dtype, fp8_type,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              w % kTileDim == 0, kWidthAligned,\n              fp8_block_scaling_partial_cast_kernel<inp_dtype, fp8_type, kWidthAligned>\n              <<<grid, kThreadsPerBlock, 0, stream>>>(\n                  reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n                  reinterpret_cast<fp8_type *>(out.data.dptr),\n                  reinterpret_cast<const float *>(scale.data.dptr), scale_stride_h, scale_stride_w,\n                  h, w, start_offset, len);)))\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace fp8_block_scaling_recipe\n}  // namespace transformer_engine\n\nvoid nvte_fp8_block_scaling_compute_partial_amax(const NVTETensor inp, NVTETensor amax, size_t h,\n                                                 size_t w, size_t amax_stride_h,\n                                                 size_t amax_stride_w, size_t start_offset,\n                                                 size_t block_len, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_block_scaling_compute_partial_amax);\n  using namespace transformer_engine;\n  fp8_block_scaling_recipe::fp8_block_scaling_compute_partial_amax(\n      *convertNVTETensorCheck(inp), *convertNVTETensorCheck(amax), h, w, amax_stride_h,\n      amax_stride_w, start_offset, block_len, stream);\n}\n\nvoid nvte_fp8_block_scaling_partial_cast(const NVTETensor inp, NVTETensor out,\n                                         const NVTETensor scale, size_t h, size_t w,\n                                         size_t scale_stride_h, size_t scale_stride_w,\n                                         size_t start_offset, size_t block_len,\n                                         const NVTEDType out_dtype, hipStream_t stream) {\n  NVTE_API_CALL(nvte_fp8_block_scaling_partial_cast);\n  using namespace transformer_engine;\n  fp8_block_scaling_recipe::fp8_block_scaling_partial_cast(\n      *convertNVTETensorCheck(inp), *convertNVTETensorCheck(out), *convertNVTETensorCheck(scale), h,\n      w, scale_stride_h, scale_stride_w, start_offset, block_len, static_cast<DType>(out_dtype),\n      stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/recipe/fp8_block_scaling.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/recipe.h>\n\n#include <algorithm>\n#include <limits>\n#include <type_traits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../util/vectorized_pointwise.h\"\n#include \"recipe_common.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int amax_kernel_threads = 512;\n\n__launch_bounds__(1) __global__ void zero_amax_kernel(float *amax_ptr, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n  *amax_ptr = 0;\n}\n\ntemplate <int nvec, bool aligned, typename InputType>\n__launch_bounds__(amax_kernel_threads) __global__\n    void amax_kernel(const InputType *input, float *amax, const size_t N,\n                     const size_t num_aligned_elements, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  VectorizedLoader<InputType, nvec, aligned> loader(input, N);\n  InputType max = 0.f;\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const size_t M = num_aligned_elements;\n\n  for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < M; tid += gridDim.x * blockDim.x) {\n    loader.load(tid, N);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      const InputType val = static_cast<InputType>(loader.separate()[i]);\n      __builtin_assume(max >= InputType{0.f});\n      if constexpr (std::is_same_v<InputType, __nv_bfloat16>) {\n#if __CUDA_ARCH__ >= 800\n        max = __hmax(__habs(val), max);\n#else  // Turing\n        max = static_cast<__nv_bfloat16>(\n            fmaxf(fabsf(static_cast<float>(val)), static_cast<float>(max)));\n#endif\n      } else if constexpr (std::is_same_v<InputType, __half>) {\n        max = __hmax(__habs(val), max);\n      } else {\n        max = fmaxf(fabsf(val), max);\n      }\n    }\n  }\n\n  // Reduce amax over block\n  max = reduce_max<amax_kernel_threads / THREADS_PER_WARP>(max, warp_id);\n  if (threadIdx.x == 0) {\n    atomicMaxFloat(amax, max);\n  }\n}\n\ntemplate <int nvec, typename InputType>\nvoid launch_amax_kernel(const InputType *input, float *amax, const size_t N, const float *noop_ptr,\n                        cudaStream_t stream) {\n  // Zero out amax so we can update with atomic max\n  zero_amax_kernel<<<1, 1, 0, stream>>>(amax, noop_ptr);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  // Return immediately if tensor is empty\n  if (N == 0) {\n    return;\n  }\n\n  // Figure out alignment\n  auto align = CheckAlignment(N, nvec, input);\n  size_t num_aligned_elements = get_num_aligned_elements(input, N, nvec, sizeof(InputType));\n\n  // Figure out CUDA blocks\n  constexpr size_t threads = amax_kernel_threads;\n  size_t num_blocks = DIVUP(num_aligned_elements, threads);\n  constexpr size_t max_blocks = 65535;\n  num_blocks = std::min(num_blocks, max_blocks);\n\n  // Launch kernel\n  switch (align) {\n    case Alignment::SAME_ALIGNED:\n      amax_kernel<nvec, true, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, num_aligned_elements, noop_ptr);\n      break;\n    case Alignment::SAME_UNALIGNED:\n      amax_kernel<nvec, false, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, num_aligned_elements, noop_ptr);\n      break;\n    case Alignment::DIFFERENT: {\n      // This case is a logic error, since there is only one pointer (input)\n      // in the alignment check. Still safe to process without vectorization.\n      amax_kernel<1, true, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, N, noop_ptr);\n      break;\n    }\n  }\n\n  // Check results\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace {\n\nvoid compute_amax_impl(const NVTETensor input_, const NVTETensor output_, cudaStream_t stream,\n                       const NVTEQuantizationConfig config_) {\n  using namespace transformer_engine;\n\n  // Check input tensor\n  NVTE_CHECK(input_ != nullptr, \"Invalid input tensor (got NULL)\");\n  const auto &input = *convertNVTETensorCheck(input_);\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor for amax computation must unquantized, \"\n             \"but got scaling_mode=\",\n             to_string(input.scaling_mode));\n  NVTE_CHECK(!is_fp8_dtype(input.data.dtype),\n             \"Input tensor for amax computation must be unquantized, but got dtype=\",\n             to_string(input.data.dtype));\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input tensor for amax computation has no data\");\n  CheckInputTensor(input, \"input_compute_amax\");\n\n  // Check output tensor\n  NVTE_CHECK(output_ != nullptr, \"Invalid output tensor (got NULL)\");\n  auto &output = *convertNVTETensorCheck(output_);\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING ||\n                 output.scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Output tensor for amax computation must be FP8 tensor with per-tensor scaling or \"\n             \"NVFP4 1D scaling, \"\n             \"but got scaling_mode=\",\n             to_string(output.scaling_mode));\n  NVTE_CHECK(output.amax.numel() == 1,\n             \"Output tensor for amax computation has invalid amax tensor \"\n             \"(expected 1 entry, got shape=\",\n             output.amax.shape, \")\");\n  NVTE_CHECK(output.amax.dptr != nullptr || output.columnwise_amax.dptr != nullptr,\n             \"Output tensor for amax computation has amax tensor without data\");\n  NVTE_CHECK(output.amax.dtype == DType::kFloat32,\n             \"Output tensor for amax computation has invalid amax tensor  \"\n             \"(expected FP32, got dtype=\",\n             to_string(output.amax.dtype), \")\");\n  CheckOutputTensor(output, \"output_compute_amax\", true);\n\n  float *noop_ptr = nullptr;\n  if (config_ != nullptr) {\n    const QuantizationConfig *config_cpp = reinterpret_cast<const QuantizationConfig *>(config_);\n\n    // extract noop tensor from quant_config_cpp if it's not null\n    const NVTETensor noop = config_cpp ? config_cpp->noop_tensor : nullptr;\n    noop_ptr = reinterpret_cast<float *>(\n        (noop != nullptr ? convertNVTETensorCheck(noop)->data.dptr : nullptr));\n  }\n\n  // Compute amax\n  float *amax_ptr = reinterpret_cast<float *>(\n      (output.amax.dptr != nullptr) ? output.amax.dptr : output.columnwise_amax.dptr);\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.data.dtype, IType, constexpr int nvec = 32 / sizeof(IType); launch_amax_kernel<nvec>(\n          reinterpret_cast<const IType *>(input.data.dptr), amax_ptr, input.data.numel(), noop_ptr,\n          stream););  // NOLINT(*)\n}\n\n}  // anonymous namespace\n\nvoid nvte_compute_amax(const NVTETensor input_, const NVTETensor output_, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_compute_amax);\n  compute_amax_impl(input_, output_, stream, nullptr);\n}\n\nvoid nvte_compute_amax_with_config(const NVTETensor input_, const NVTETensor output_,\n                                   const NVTEQuantizationConfig config_, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_compute_amax_with_config);\n  compute_amax_impl(input_, output_, stream, config_);\n}\n\nnamespace transformer_engine {\nnamespace {\n\n__global__ void compute_scale_from_amax_kernel(const float *amax_ptr, float *scale_ptr,\n                                               const float max_fp8, const bool force_pow_2_scales,\n                                               const float epsilon, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  *scale_ptr = compute_scale_from_amax(*amax_ptr, max_fp8, force_pow_2_scales, epsilon,\n                                       std::numeric_limits<float>::max());\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nvoid nvte_compute_scale_from_amax(NVTETensor output_, const NVTEQuantizationConfig config_,\n                                  cudaStream_t stream) {\n  NVTE_API_CALL(nvte_compute_scale_from_amax);\n  using namespace transformer_engine;\n\n  // Check output tensor\n  NVTE_CHECK(output_ != nullptr, \"Invalid output tensor (got NULL)\");\n  auto &output = *convertNVTETensorCheck(output_);\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Tensor must be FP8 tensor with per-tensor scaling, \"\n             \"but got scaling_mode=\",\n             to_string(output.scaling_mode));\n  NVTE_CHECK(is_fp8_dtype(output.data.dtype),\n             \"Tensor must be FP8, but got dtype=\", to_string(output.data.dtype));\n  NVTE_CHECK(output.amax.numel() == 1,\n             \"Tensor has invalid amax tensor (expected 1 entry, got shape=\", output.amax.shape,\n             \")\");\n  NVTE_CHECK(output.amax.dptr != nullptr, \"Tensor has amax tensor without data\");\n  NVTE_CHECK(output.amax.dtype == DType::kFloat32,\n             \"Tensor has invalid amax tensor (expected FP32, got dtype=\",\n             to_string(output.amax.dtype), \")\");\n  NVTE_CHECK(output.scale.numel() == 1,\n             \"Tensor has invalid scale tensor (expected 1 entry, got shape=\", output.scale.shape,\n             \")\");\n  NVTE_CHECK(output.scale.dptr != nullptr, \"Tensor has scale tensor without data\");\n  NVTE_CHECK(output.scale.dtype == DType::kFloat32,\n             \"Tensor has invalid scale tensor (expected FP32, got dtype=\",\n             to_string(output.scale.dtype), \")\");\n\n  // Check config\n  NVTE_CHECK(config_ != nullptr, \"Invalid config (got NULL)\");\n  const auto &config = *reinterpret_cast<const QuantizationConfig *>(config_);\n\n  // Maximum FP8 value\n  float max_fp8 = 0.f;\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(output.data.dtype, DType,\n                                         max_fp8 = Quantized_Limits<DType>::max_norm;);\n\n  // noop tensor for cuda graph\n  float *noop_ptr = nullptr;\n  if (config_ != nullptr) {\n    const QuantizationConfig *config_cpp = reinterpret_cast<const QuantizationConfig *>(config_);\n\n    // extract noop tensor from quant_config_cpp if it's not null\n    const NVTETensor noop = config_cpp ? config_cpp->noop_tensor : nullptr;\n    noop_ptr = reinterpret_cast<float *>(\n        (noop != nullptr ? convertNVTETensorCheck(noop)->data.dptr : nullptr));\n  }\n\n  // Update scale\n  compute_scale_from_amax_kernel<<<1, 1, 0, stream>>>(\n      reinterpret_cast<const float *>(output.amax.dptr),\n      reinterpret_cast<float *>(output.scale.dptr), max_fp8, config.force_pow_2_scales,\n      config.amax_epsilon, noop_ptr);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/recipe.h>\n\n#include <algorithm>\n#include <limits>\n#include <type_traits>\n\n#include \"../common.h\"\n#include \"../util/logging.h\"\n#include \"../util/vectorized_pointwise.h\"\n#include \"recipe_common.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int amax_kernel_threads = 512;\n\n__launch_bounds__(1) __global__ void zero_amax_kernel(float *amax_ptr, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n  *amax_ptr = 0;\n}\n\ntemplate <int nvec, bool aligned, typename InputType>\n__launch_bounds__(amax_kernel_threads) __global__\n    void amax_kernel(const InputType *input, float *amax, const size_t N,\n                     const size_t num_aligned_elements, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  VectorizedLoader<InputType, nvec, aligned> loader(input, N);\n  InputType max = 0.f;\n  const int warp_id = threadIdx.x / THREADS_PER_WARP;\n  const size_t M = num_aligned_elements;\n\n  for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < M; tid += gridDim.x * blockDim.x) {\n    loader.load(tid, N);\n#pragma unroll\n    for (int i = 0; i < nvec; ++i) {\n      const InputType val = static_cast<InputType>(loader.separate()[i]);\n      __builtin_assume(max >= InputType{0.f});\n      if constexpr (std::is_same_v<InputType, __hip_bfloat16>) {\n#if __CUDA_ARCH__ >= 800\n        max = __hmax(__habs(val), max);\n#else  // Turing\n        max = static_cast<__hip_bfloat16>(\n            fmaxf(fabsf(static_cast<float>(val)), static_cast<float>(max)));\n#endif\n      } else if constexpr (std::is_same_v<InputType, __half>) {\n        max = __hmax(__habs(val), max);\n      } else {\n        max = fmaxf(fabsf(val), max);\n      }\n    }\n  }\n\n  // Reduce amax over block\n  max = reduce_max<amax_kernel_threads / THREADS_PER_WARP>(max, warp_id);\n  if (threadIdx.x == 0) {\n    atomicMaxFloat(amax, max);\n  }\n}\n\ntemplate <int nvec, typename InputType>\nvoid launch_amax_kernel(const InputType *input, float *amax, const size_t N, const float *noop_ptr,\n                        hipStream_t stream) {\n  // Zero out amax so we can update with atomic max\n  zero_amax_kernel<<<1, 1, 0, stream>>>(amax, noop_ptr);\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  // Return immediately if tensor is empty\n  if (N == 0) {\n    return;\n  }\n\n  // Figure out alignment\n  auto align = CheckAlignment(N, nvec, input);\n  size_t num_aligned_elements = get_num_aligned_elements(input, N, nvec, sizeof(InputType));\n\n  // Figure out CUDA blocks\n  constexpr size_t threads = amax_kernel_threads;\n  size_t num_blocks = DIVUP(num_aligned_elements, threads);\n  constexpr size_t max_blocks = 65535;\n  num_blocks = std::min(num_blocks, max_blocks);\n\n  // Launch kernel\n  switch (align) {\n    case Alignment::SAME_ALIGNED:\n      amax_kernel<nvec, true, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, num_aligned_elements, noop_ptr);\n      break;\n    case Alignment::SAME_UNALIGNED:\n      amax_kernel<nvec, false, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, num_aligned_elements, noop_ptr);\n      break;\n    case Alignment::DIFFERENT: {\n      // This case is a logic error, since there is only one pointer (input)\n      // in the alignment check. Still safe to process without vectorization.\n      amax_kernel<1, true, InputType>\n          <<<num_blocks, threads, 0, stream>>>(input, amax, N, N, noop_ptr);\n      break;\n    }\n  }\n\n  // Check results\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nnamespace {\n\nvoid compute_amax_impl(const NVTETensor input_, const NVTETensor output_, hipStream_t stream,\n                       const NVTEQuantizationConfig config_) {\n  using namespace transformer_engine;\n\n  // Check input tensor\n  NVTE_CHECK(input_ != nullptr, \"Invalid input tensor (got NULL)\");\n  const auto &input = *convertNVTETensorCheck(input_);\n  NVTE_CHECK(input.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor for amax computation must unquantized, \"\n             \"but got scaling_mode=\",\n             to_string(input.scaling_mode));\n  NVTE_CHECK(!is_fp8_dtype(input.data.dtype),\n             \"Input tensor for amax computation must be unquantized, but got dtype=\",\n             to_string(input.data.dtype));\n  NVTE_CHECK(input.data.dptr != nullptr, \"Input tensor for amax computation has no data\");\n  CheckInputTensor(input, \"input_compute_amax\");\n\n  // Check output tensor\n  NVTE_CHECK(output_ != nullptr, \"Invalid output tensor (got NULL)\");\n  auto &output = *convertNVTETensorCheck(output_);\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING ||\n                 output.scaling_mode == NVTE_NVFP4_1D_SCALING,\n             \"Output tensor for amax computation must be FP8 tensor with per-tensor scaling or \"\n             \"NVFP4 1D scaling, \"\n             \"but got scaling_mode=\",\n             to_string(output.scaling_mode));\n  NVTE_CHECK(output.amax.numel() == 1,\n             \"Output tensor for amax computation has invalid amax tensor \"\n             \"(expected 1 entry, got shape=\",\n             output.amax.shape, \")\");\n  NVTE_CHECK(output.amax.dptr != nullptr || output.columnwise_amax.dptr != nullptr,\n             \"Output tensor for amax computation has amax tensor without data\");\n  NVTE_CHECK(output.amax.dtype == DType::kFloat32,\n             \"Output tensor for amax computation has invalid amax tensor  \"\n             \"(expected FP32, got dtype=\",\n             to_string(output.amax.dtype), \")\");\n  CheckOutputTensor(output, \"output_compute_amax\", true);\n\n  float *noop_ptr = nullptr;\n  if (config_ != nullptr) {\n    const QuantizationConfig *config_cpp = reinterpret_cast<const QuantizationConfig *>(config_);\n\n    // extract noop tensor from quant_config_cpp if it's not null\n    const NVTETensor noop = config_cpp ? config_cpp->noop_tensor : nullptr;\n    noop_ptr = reinterpret_cast<float *>(\n        (noop != nullptr ? convertNVTETensorCheck(noop)->data.dptr : nullptr));\n  }\n\n  // Compute amax\n  float *amax_ptr = reinterpret_cast<float *>(\n      (output.amax.dptr != nullptr) ? output.amax.dptr : output.columnwise_amax.dptr);\n  TRANSFORMER_ENGINE_TYPE_SWITCH_INPUT(\n      input.data.dtype, IType, constexpr int nvec = 32 / sizeof(IType); launch_amax_kernel<nvec>(\n          reinterpret_cast<const IType *>(input.data.dptr), amax_ptr, input.data.numel(), noop_ptr,\n          stream););  // NOLINT(*)\n}\n\n}  // anonymous namespace\n\nvoid nvte_compute_amax(const NVTETensor input_, const NVTETensor output_, hipStream_t stream) {\n  NVTE_API_CALL(nvte_compute_amax);\n  compute_amax_impl(input_, output_, stream, nullptr);\n}\n\nvoid nvte_compute_amax_with_config(const NVTETensor input_, const NVTETensor output_,\n                                   const NVTEQuantizationConfig config_, hipStream_t stream) {\n  NVTE_API_CALL(nvte_compute_amax_with_config);\n  compute_amax_impl(input_, output_, stream, config_);\n}\n\nnamespace transformer_engine {\nnamespace {\n\n__global__ void compute_scale_from_amax_kernel(const float *amax_ptr, float *scale_ptr,\n                                               const float max_fp8, const bool force_pow_2_scales,\n                                               const float epsilon, const float *noop_ptr) {\n  if (noop_ptr != nullptr && noop_ptr[0] == 1.0f) {\n    return;\n  }\n\n  *scale_ptr = compute_scale_from_amax(*amax_ptr, max_fp8, force_pow_2_scales, epsilon,\n                                       std::numeric_limits<float>::max());\n}\n\n}  // namespace\n}  // namespace transformer_engine\n\nvoid nvte_compute_scale_from_amax(NVTETensor output_, const NVTEQuantizationConfig config_,\n                                  hipStream_t stream) {\n  NVTE_API_CALL(nvte_compute_scale_from_amax);\n  using namespace transformer_engine;\n\n  // Check output tensor\n  NVTE_CHECK(output_ != nullptr, \"Invalid output tensor (got NULL)\");\n  auto &output = *convertNVTETensorCheck(output_);\n  NVTE_CHECK(output.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Tensor must be FP8 tensor with per-tensor scaling, \"\n             \"but got scaling_mode=\",\n             to_string(output.scaling_mode));\n  NVTE_CHECK(is_fp8_dtype(output.data.dtype),\n             \"Tensor must be FP8, but got dtype=\", to_string(output.data.dtype));\n  NVTE_CHECK(output.amax.numel() == 1,\n             \"Tensor has invalid amax tensor (expected 1 entry, got shape=\", output.amax.shape,\n             \")\");\n  NVTE_CHECK(output.amax.dptr != nullptr, \"Tensor has amax tensor without data\");\n  NVTE_CHECK(output.amax.dtype == DType::kFloat32,\n             \"Tensor has invalid amax tensor (expected FP32, got dtype=\",\n             to_string(output.amax.dtype), \")\");\n  NVTE_CHECK(output.scale.numel() == 1,\n             \"Tensor has invalid scale tensor (expected 1 entry, got shape=\", output.scale.shape,\n             \")\");\n  NVTE_CHECK(output.scale.dptr != nullptr, \"Tensor has scale tensor without data\");\n  NVTE_CHECK(output.scale.dtype == DType::kFloat32,\n             \"Tensor has invalid scale tensor (expected FP32, got dtype=\",\n             to_string(output.scale.dtype), \")\");\n\n  // Check config\n  NVTE_CHECK(config_ != nullptr, \"Invalid config (got NULL)\");\n  const auto &config = *reinterpret_cast<const QuantizationConfig *>(config_);\n\n  // Maximum FP8 value\n  float max_fp8 = 0.f;\n  TRANSFORMER_ENGINE_TYPE_SWITCH_FP8ONLY(output.data.dtype, DType,\n                                         max_fp8 = Quantized_Limits<DType>::max_norm;);\n\n  // noop tensor for cuda graph\n  float *noop_ptr = nullptr;\n  if (config_ != nullptr) {\n    const QuantizationConfig *config_cpp = reinterpret_cast<const QuantizationConfig *>(config_);\n\n    // extract noop tensor from quant_config_cpp if it's not null\n    const NVTETensor noop = config_cpp ? config_cpp->noop_tensor : nullptr;\n    noop_ptr = reinterpret_cast<float *>(\n        (noop != nullptr ? convertNVTETensorCheck(noop)->data.dptr : nullptr));\n  }\n\n  // Update scale\n  compute_scale_from_amax_kernel<<<1, 1, 0, stream>>>(\n      reinterpret_cast<const float *>(output.amax.dptr),\n      reinterpret_cast<float *>(output.scale.dptr), max_fp8, config.force_pow_2_scales,\n      config.amax_epsilon, noop_ptr);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/recipe/current_scaling.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <transformer_engine/recipe.h>\n#include <transformer_engine/transpose.h>\n\n#include <cassert>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace nvfp4_recipe {\n\n#if FP4_TYPE_SUPPORTED\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 2D PARTIAL-SHARD KERNEL DESIGN\n *\n * These kernels mirror the FP8 block-scaling helpers but operate on shard-local\n * slices and nibble-packed FP4 rowwise buffers. One CUDA block covers a logical\n * 16x16 tile (grid = ceil(W/16) x ceil(H/16), blockDim = 256 threads).\n *\n * 1) Partial Amax (`nvfp4_2d_compute_partial_amax_kernel`)\n *    - Warps sweep the tile using nested loops, accumulating local maxima only\n *      for elements in [start_offset, start_offset + len).\n *    - Shared memory reduces the 8 warp maxima; the block writes a float into\n *      `amax_ptr[tile_row * stride_h + tile_col * stride_w]`.\n *\n *      Tile/warp mapping (each '#' = elements visited by that warp):\n *\n *          +------------------+\n *          |########..........|  Warp 0\n *          |########..........|  Warp 1\n *          |   ...            |\n *          |########..........|  Warp 7\n *          +------------------+\n *\n * 2) Partial Cast (`nvfp4_2d_partial_cast_kernel`)\n *    - Stage the tile into shared memory (same pattern as FP8).\n *    - For each 4-value group, build float2 pairs and call\n *      `ptx::mul_cvt_fp32_to_fp4_4x`, producing packed FP4 nibbles.\n *    - Compute a shard-local byte index and update only the owned nibble(s)\n *      using read-modify-write:\n *\n *          packed_bits = [mw3 | mw2 | mw1 | mw0]\n *          byte_idx    = (ref_elem_idx - start_offset) >> 1\n *          if elem_idx % 2 == 0:  // low nibble\n *              byte = (byte & 0xF0) | nibble\n *          else:                  // high nibble\n *              byte = (byte & 0x0F) | (nibble << 4)\n *\n *      Thread coverage inside a tile:\n *\n *          rows:   16           columns: 16\n *          Warp 0 -> rows 0-1   lanes sweep cols 0..3, 4..7, ...\n *          Warp 1 -> rows 2-3   (groups of 4 elements per thread)\n *          ...\n *          Warp 7 -> rows 14-15\n * ---------------------------------------------------------------------------\n */\n\n// constexpr float factor = 6.0 * 6.0 * 448.0 * 448.0;\nconstexpr float factor_inv = 1.0 / (6.0 * 6.0 * 448.0 * 448.0);\nconstexpr int kTileDim = 16;\nconstexpr int kThreadsPerBlock = 256;\n\n// Kernel to compute alpha *= amax_A * amax_B / factor\n__global__ void compute_nvfp4_per_tensor_scale_kernel(float alpha_in, const float *amax_A,\n                                                      const float *amax_B, float *alpha_out) {\n  // factor is defined in the enclosing namespace\n  *alpha_out = alpha_in * (*amax_A) * (*amax_B) * factor_inv;\n}\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    nvfp4_2d_compute_partial_amax_kernel(const IType *input, float *amax_ptr,\n                                         const size_t amax_stride_h, const size_t amax_stride_w,\n                                         const size_t h, const size_t w, const size_t start_offset,\n                                         const size_t len) {\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  static_assert(kTileDim * kTileDim == kThreadsPerBlock);\n\n  const size_t tile_col = blockIdx.x;\n  const size_t tile_row = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  __shared__ float smem[kNumWarps];\n  float amax = 0.0f;\n\n  size_t r = tile_row * kTileDim + threadIdx.x / kTileDim;\n  size_t c = tile_col * kTileDim + threadIdx.x % kTileDim;\n  size_t idx = r * w + c;\n  if (r < h && c < w && idx >= start_offset && idx < end_offset) {\n    amax = fabs(static_cast<float>(input_minus_offset[idx]));\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    float other_amax = __shfl_down_sync(0xFFFFFFFF, amax, delta);\n    __builtin_assume(amax >= 0);\n    __builtin_assume(other_amax >= 0);\n    amax = fmaxf(amax, other_amax);\n  }\n\n  if (threadIdx.x % kThreadsPerWarp == 0) {\n    smem[threadIdx.x / kThreadsPerWarp] = amax;\n  }\n\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < kNumWarps; ++i) {\n      float other_amax = smem[i];\n      __builtin_assume(amax >= 0);\n      __builtin_assume(other_amax >= 0);\n      amax = fmaxf(amax, other_amax);\n    }\n    amax_ptr[tile_row * amax_stride_h + tile_col * amax_stride_w] = amax;\n  }\n}\n\ntemplate <typename IType, bool kWidthAligned>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    nvfp4_2d_partial_cast_kernel(const IType *input, uint8_t *output, const float *decode_scale_ptr,\n                                 const size_t scale_stride_h, const size_t scale_stride_w,\n                                 const float *global_scale_ptr, const size_t h, const size_t w,\n                                 const size_t start_offset, const size_t len) {\n  constexpr int kNumOutputElemsPerBank = 4;\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = (kTileDim + kThreadsPerWarp - 1) / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kRowsPerWarp = (kTileDim + kNumWarps - 1) / kNumWarps;\n\n  __shared__ float smem[kTileDim][kTileDim + kNumOutputElemsPerBank];\n\n  const int tile_w = blockIdx.x;\n  const int tile_h = blockIdx.y;\n  const size_t shard_end = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  float global_encode_scale = global_scale_ptr[0];\n  if (global_encode_scale <= 0.f) {\n    global_encode_scale = 1.f;\n  }\n  const float global_decode_scale = 1.0f / global_encode_scale;\n\n  float tile_decode_scale = decode_scale_ptr[tile_h * scale_stride_h + tile_w * scale_stride_w];\n  tile_decode_scale = static_cast<float>(static_cast<fp8e4m3>(tile_decode_scale));\n  constexpr float kFp32Max = 3.402823466e+38F;\n  float tile_encode_val =\n      (tile_decode_scale > 0.f) ? 1.0f / (tile_decode_scale * global_decode_scale) : kFp32Max;\n  tile_encode_val = fminf(tile_encode_val, kFp32Max);\n  const float2 scale_vec = make_float2(tile_encode_val, tile_encode_val);\n\n  bool skip_store = true;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    for (int j = 0; j < kLoopsPerRow; ++j) {\n      const int h_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n      const int w_in_smem = threadIdx.x % kThreadsPerWarp + kThreadsPerWarp * j;\n      if (h_in_smem >= kTileDim || w_in_smem >= kTileDim) {\n        continue;\n      }\n      const int h_in_input = tile_h * kTileDim + h_in_smem;\n      const int w_in_input = tile_w * kTileDim + w_in_smem;\n      const size_t idx_in_input = static_cast<size_t>(h_in_input) * w + w_in_input;\n      if (h_in_input < h && w_in_input < w && idx_in_input >= start_offset &&\n          idx_in_input < shard_end) {\n        smem[h_in_smem][w_in_smem] = static_cast<float>(input_minus_offset[idx_in_input]);\n        skip_store = false;\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    bool other = __shfl_down_sync(0xFFFFFFFF, skip_store, delta);\n    skip_store = skip_store && other;\n  }\n  skip_store = __shfl_sync(0xFFFFFFFF, skip_store, 0);\n  if (skip_store) {\n    return;\n  }\n\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    const int row_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n    const int row_in_output = tile_h * kTileDim + row_in_smem;\n    if (row_in_output >= h) {\n      continue;\n    }\n    const int col_in_smem = threadIdx.x % kThreadsPerWarp * kNumOutputElemsPerBank;\n    if (col_in_smem >= kTileDim) {\n      continue;\n    }\n    const int col_in_output = tile_w * kTileDim + col_in_smem;\n\n    float vals[kNumOutputElemsPerBank];\n    bool mask[kNumOutputElemsPerBank];\n    size_t elem_idx[kNumOutputElemsPerBank];\n    bool any_valid = false;\n\n    for (int j = 0; j < kNumOutputElemsPerBank; ++j) {\n      const int col = col_in_output + j;\n      const bool in_width = col < w;\n      const size_t idx = static_cast<size_t>(row_in_output) * w + col;\n      elem_idx[j] = idx;\n      const bool in_shard = in_width && idx >= start_offset && idx < shard_end;\n      mask[j] = in_shard;\n      const bool in_tile = (col_in_smem + j) < kTileDim;\n      const float tile_val = in_tile ? smem[row_in_smem][col_in_smem + j] : 0.0f;\n      vals[j] = in_shard ? tile_val : 0.0f;\n      any_valid |= in_shard;\n    }\n\n    if (!any_valid) {\n      continue;\n    }\n\n    const float2 in01 = make_float2(vals[0], vals[1]);\n    const float2 in23 = make_float2(vals[2], vals[3]);\n    const auto packed =\n        transformer_engine::ptx::mul_cvt_fp32_to_fp4_4x<false>(in01, in23, scale_vec, 0);\n    const uint16_t packed_bits = reinterpret_cast<const uint16_t &>(packed);\n\n    for (int pair = 0; pair < 2; ++pair) {\n      const int first = pair * 2;\n      const int second = first + 1;\n      if (!mask[first] && !mask[second]) {\n        continue;\n      }\n      const size_t ref_idx = mask[first] ? elem_idx[first] : elem_idx[second];\n      const size_t byte_idx = (ref_idx - start_offset) >> 1;\n      uint8_t byte = output[byte_idx];\n\n      if (mask[first]) {\n        const uint8_t nibble = static_cast<uint8_t>((packed_bits >> (4 * first)) & 0xF);\n        if ((elem_idx[first] & 1u) == 0) {\n          byte = static_cast<uint8_t>((byte & 0xF0u) | nibble);\n        } else {\n          byte = static_cast<uint8_t>((byte & 0x0Fu) | (nibble << 4));\n        }\n      }\n\n      if (mask[second]) {\n        const uint8_t nibble = static_cast<uint8_t>((packed_bits >> (4 * second)) & 0xF);\n        if ((elem_idx[second] & 1u) == 0) {\n          byte = static_cast<uint8_t>((byte & 0xF0u) | nibble);\n        } else {\n          byte = static_cast<uint8_t>((byte & 0x0Fu) | (nibble << 4));\n        }\n      }\n\n      output[byte_idx] = byte;\n    }\n  }\n}\n\nvoid nvfp4_2d_compute_partial_amax(const Tensor inp, Tensor amax, size_t h, size_t w,\n                                   size_t amax_stride_h, size_t amax_stride_w, size_t start_offset,\n                                   size_t block_len, cudaStream_t stream) {\n  NVTE_CHECK(block_len == 16, \"NVFP4 2D supports 16x16 tiles only (block_len = 16).\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      nvfp4_2d_compute_partial_amax_kernel<inp_dtype><<<grid, kThreadsPerBlock, 0, stream>>>(\n          reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n          reinterpret_cast<float *>(amax.data.dptr), amax_stride_h, amax_stride_w, h, w,\n          start_offset, len);)\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid nvfp4_2d_partial_cast(const Tensor inp, Tensor out, const Tensor scale,\n                           const Tensor global_scale, size_t h, size_t w, size_t scale_stride_h,\n                           size_t scale_stride_w, size_t start_offset, size_t block_len,\n                           cudaStream_t stream) {\n  NVTE_CHECK(block_len == 16, \"NVFP4 2D supports 16x16 tiles only (block_len = 16).\");\n  NVTE_CHECK(out.dtype() == DType::kByte, \"NVFP4 rowwise data must be uint8.\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          w % kTileDim == 0, kWidthAligned,\n          nvfp4_2d_partial_cast_kernel<inp_dtype, kWidthAligned>\n          <<<grid, kThreadsPerBlock, 0, stream>>>(\n              reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n              reinterpret_cast<uint8_t *>(out.data.dptr),\n              reinterpret_cast<const float *>(scale.data.dptr), scale_stride_h, scale_stride_w,\n              reinterpret_cast<const float *>(global_scale.data.dptr), h, w, start_offset, len);))\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 TRANSPOSE KERNEL\n *\n * Unlike FP8, NVFP4 packs two 4-bit values into each byte. A simple byte-wise\n * transpose doesn't work because the packing changes:\n *   - Before transpose: elements [m, 2c] and [m, 2c+1] share a byte\n *   - After transpose:  elements [k, 2*m_packed] and [k, 2*m_packed+1] share a byte\n *                       which were originally [2*m_packed, k] and [2*m_packed+1, k]\n * ---------------------------------------------------------------------------\n */\n\n// Vectorized transpose kernel parameters\nconstexpr int TRANSPOSE_TILE_DIM = 64;  // Logical FP4 elements per tile dimension\n// constexpr int TRANSPOSE_TILE_PACKED = 32;  // TILE_DIM / 2 bytes\nconstexpr int TRANSPOSE_BLOCK_SIZE = 256;  // threads per block\n\n// Shared memory: store unpacked 4-bit values as bytes for easy transpose\n// Size: TILE_DIM x (TILE_DIM + 4) to avoid bank conflicts\nconstexpr int TRANSPOSE_SHMEM_STRIDE = TRANSPOSE_TILE_DIM + 4;\n\n/*\n * Vectorized transpose kernel with uint2 loads/stores (256 threads)\n * Tile: 64x64 logical FP4 = 64x32 packed bytes\n */\n__global__ void __launch_bounds__(TRANSPOSE_BLOCK_SIZE)\n    nvfp4_transpose_kernel(const uint8_t *__restrict__ input, uint8_t *__restrict__ output,\n                           const size_t M, const size_t K) {\n  const size_t K_packed = K / 2;\n  const size_t M_packed = M / 2;\n\n  const size_t tile_m_start = blockIdx.x * TRANSPOSE_TILE_DIM;\n  const size_t tile_k_start = blockIdx.y * TRANSPOSE_TILE_DIM;\n\n  __shared__ uint8_t shmem[TRANSPOSE_TILE_DIM][TRANSPOSE_SHMEM_STRIDE];\n\n  const int tid = threadIdx.x;\n\n  // Phase 1: Load input tile with VECTORIZED uint2 reads\n  // 256 threads, each loads 8 bytes (uint2) = 2048 bytes total\n  // Input tile: [64 rows, 32 cols] = 2048 bytes\n  {\n    const int thread_row = tid / 4;        // 64 rows, 4 threads per row\n    const int thread_col = (tid % 4) * 8;  // 4 x 8 = 32 bytes per row\n\n    const size_t global_m = tile_m_start + thread_row;\n    const size_t global_k_packed_base = tile_k_start / 2 + thread_col;\n\n    // Load 8 bytes as uint2\n    uint2 loaded = make_uint2(0, 0);\n    if (global_m < M && global_k_packed_base + 7 < K_packed) {\n      loaded = *reinterpret_cast<const uint2 *>(&input[global_m * K_packed + global_k_packed_base]);\n    } else if (global_m < M) {\n      // Boundary: scalar loads\n      uint8_t *bytes = reinterpret_cast<uint8_t *>(&loaded);\n#pragma unroll\n      for (int b = 0; b < 8; ++b) {\n        size_t col = global_k_packed_base + b;\n        bytes[b] = (col < K_packed) ? input[global_m * K_packed + col] : 0;\n      }\n    }\n\n    // Unpack 8 bytes -> 16 nibbles and store to shared memory\n    const uint8_t *bytes = reinterpret_cast<const uint8_t *>(&loaded);\n#pragma unroll\n    for (int b = 0; b < 8; ++b) {\n      const int k0 = thread_col * 2 + b * 2;\n      const int k1 = k0 + 1;\n      shmem[thread_row][k0] = bytes[b] & 0x0F;\n      shmem[thread_row][k1] = (bytes[b] >> 4) & 0x0F;\n    }\n  }\n\n  __syncthreads();\n\n  // Phase 2: Write output with VECTORIZED uint2 stores\n  // Output tile: [64 rows, 32 cols] = 2048 bytes\n  {\n    const int thread_row = tid / 4;             // output K dimension [0, 64)\n    const int thread_col_base = (tid % 4) * 8;  // output M_packed [0, 32) in steps of 8\n\n    const size_t global_k = tile_k_start + thread_row;\n    const size_t global_m_packed_base = tile_m_start / 2 + thread_col_base;\n\n    if (global_k >= K) return;\n\n    // Build 8 output bytes in registers\n    uint8_t out_bytes[8];\n\n#pragma unroll\n    for (int b = 0; b < 8; ++b) {\n      const int out_m_packed = thread_col_base + b;\n\n      if (global_m_packed_base + b >= M_packed) {\n        out_bytes[b] = 0;\n        continue;\n      }\n\n      // Two M positions that pack into this output byte\n      const int m0 = out_m_packed * 2;\n      const int m1 = out_m_packed * 2 + 1;\n      const int k = thread_row;\n\n      // Read from shared memory (transposed access)\n      const uint8_t val0 = shmem[m0][k];\n      const uint8_t val1 = shmem[m1][k];\n\n      out_bytes[b] = val0 | (val1 << 4);\n    }\n\n    // Vectorized store as uint2\n    if (global_m_packed_base + 7 < M_packed) {\n      *reinterpret_cast<uint2 *>(&output[global_k * M_packed + global_m_packed_base]) =\n          *reinterpret_cast<uint2 *>(out_bytes);\n    } else {\n      // Boundary: scalar stores\n      for (int b = 0; b < 8 && global_m_packed_base + b < M_packed; ++b) {\n        output[global_k * M_packed + global_m_packed_base + b] = out_bytes[b];\n      }\n    }\n  }\n}\n\nvoid nvfp4_transpose(const Tensor input, Tensor output, cudaStream_t stream) {\n  // Input has logical shape [M, K], stored as [M, K/2] bytes\n  // Output has logical shape [K, M], stored as [K, M/2] bytes\n\n  NVTE_CHECK(input.dtype() == DType::kByte, \"NVFP4 transpose input must be uint8.\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"NVFP4 transpose output must be uint8.\");\n\n  // Get dimensions from packed storage\n  // input.shape() = [M, K/2], so M = shape[0], K = shape[1] * 2\n  const auto in_shape = input.shape();\n  NVTE_CHECK(in_shape.size() == 2, \"NVFP4 transpose expects 2D input (packed), got \",\n             in_shape.size(), \"D.\");\n  const size_t M = in_shape[0];\n  const size_t K_packed = in_shape[1];\n  const size_t K = K_packed * 2;\n\n  // Output should be [K, M/2]\n  const size_t M_packed = M / 2;\n  NVTE_CHECK(M % 2 == 0, \"NVFP4 transpose requires M (\", M, \") to be even.\");\n\n  const auto out_shape = output.shape();\n  NVTE_CHECK(out_shape.size() == 2, \"NVFP4 transpose expects 2D output.\");\n  NVTE_CHECK(out_shape[0] == K && out_shape[1] == M_packed,\n             \"NVFP4 transpose output shape mismatch. Expected [\", K, \", \", M_packed, \"], got [\",\n             out_shape[0], \", \", out_shape[1], \"].\");\n\n  if (M == 0 || K == 0) return;\n\n  // Use vectorized kernel (faster than TMA for pure transpose)\n  // 128x128 tiles with 512 threads and uint4 vectorized access\n  dim3 block(TRANSPOSE_BLOCK_SIZE);\n  dim3 grid((M + TRANSPOSE_TILE_DIM - 1) / TRANSPOSE_TILE_DIM,\n            (K + TRANSPOSE_TILE_DIM - 1) / TRANSPOSE_TILE_DIM);\n\n  nvfp4_transpose_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const uint8_t *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), M, K);\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 SCALE TRANSPOSE KERNEL\n *\n * Transposes tile-level scales from rowwise to columnwise format.\n * Scale values are stored as E4M3 (fp8) in uint8 tensors.\n *\n * Input (rowwise_scale_inv): [M_padded, K_tiles] where scales are stored\n *   at every 16th row (i.e., row 0, 16, 32, ... contain the actual scales,\n *   and each row i within a tile block has the same scale as row (i // 16) * 16).\n *\n * Output (columnwise_scale_inv): [K_padded, M_tiles] where scales are\n *   repeated 16 times per tile row.\n *\n * Mapping:\n *   output[k_tile * 16 + i, m_tile] = input[m_tile * 16, k_tile]\n *   for i in [0, 16) and valid (k_tile, m_tile) indices.\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_scale_transpose_kernel(\n    const uint8_t *__restrict__ input,  // [M_padded, K_tiles], E4M3 stored as uint8\n    uint8_t *__restrict__ output,       // [K_padded, M_tiles], E4M3 stored as uint8\n    const size_t M_tiles,               // Number of M tiles\n    const size_t K_tiles,               // Number of K tiles\n    const size_t input_stride,          // K_tiles (input row stride)\n    const size_t output_stride,         // M_tiles (output row stride)\n    const size_t K_padded               // Output height\n) {\n  // Each thread handles one output element\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (out_row >= K_padded || out_col >= M_tiles) return;\n\n  // Determine which tile row this belongs to\n  const size_t k_tile = out_row / kTileDim;\n\n  // Read from input: row = m_tile * 16 (first row of the tile), col = k_tile\n  // m_tile = out_col\n  if (k_tile < K_tiles) {\n    const size_t in_row = out_col * kTileDim;  // m_tile * 16\n    const uint8_t scale = input[in_row * input_stride + k_tile];\n    output[out_row * output_stride + out_col] = scale;\n  } else {\n    output[out_row * output_stride + out_col] = 0;\n  }\n}\n\nvoid nvfp4_scale_transpose(const Tensor input, Tensor output, size_t M_tiles, size_t K_tiles,\n                           cudaStream_t stream) {\n  NVTE_CHECK(input.dtype() == DType::kByte, \"NVFP4 scale transpose input must be uint8 (E4M3).\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"NVFP4 scale transpose output must be uint8 (E4M3).\");\n\n  const auto in_shape = input.shape();\n  const auto out_shape = output.shape();\n  NVTE_CHECK(in_shape.size() == 2, \"NVFP4 scale transpose expects 2D input.\");\n  NVTE_CHECK(out_shape.size() == 2, \"NVFP4 scale transpose expects 2D output.\");\n\n  const size_t input_stride = in_shape[1];    // K_tiles\n  const size_t output_stride = out_shape[1];  // M_tiles\n  const size_t K_padded = out_shape[0];\n\n  if (M_tiles == 0 || K_tiles == 0 || K_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((M_tiles + kBlockDim - 1) / kBlockDim, (K_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_scale_transpose_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const uint8_t *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), M_tiles, K_tiles, input_stride, output_stride,\n      K_padded);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 SCALE EXPANSION KERNEL\n *\n * Expands tile-level scales to row-level scales and converts to FP8 E4M3, used in partial cast.\n *\n * Input (per_block_decode_scale): [tile_rows, tile_cols] in float32\n * Output (target_scale): [rows_padded, tile_cols] in uint8 (E4M3)\n *\n * Each tile row's scale is repeated block_len times in the output.\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_expand_scale_to_fp8_kernel(\n    const float *__restrict__ input,  // [tile_rows, tile_cols]\n    uint8_t *__restrict__ output,     // [rows_padded, tile_cols]\n    const size_t tile_rows, const size_t tile_cols, const size_t rows_padded,\n    const size_t block_len) {\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (out_row >= rows_padded || out_col >= tile_cols) return;\n\n  // Determine which tile row this output row belongs to\n  const size_t tile_row = out_row / block_len;\n\n  float scale_val = 0.0f;\n  if (tile_row < tile_rows) {\n    scale_val = input[tile_row * tile_cols + out_col];\n  }\n\n  // Convert float32 to FP8 E4M3\n  // Clamp to FP8 E4M3 range and convert\n  fp8e4m3 fp8_val = static_cast<fp8e4m3>(scale_val);\n  output[out_row * tile_cols + out_col] = reinterpret_cast<const uint8_t &>(fp8_val);\n}\n\nvoid nvfp4_expand_scale_to_fp8(const Tensor input, Tensor output, size_t tile_rows,\n                               size_t tile_cols, size_t rows_padded, size_t block_len,\n                               cudaStream_t stream) {\n  NVTE_CHECK(input.dtype() == DType::kFloat32, \"Scale input must be float32.\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"Scale output must be uint8 (E4M3).\");\n\n  if (tile_rows == 0 || tile_cols == 0 || rows_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((tile_cols + kBlockDim - 1) / kBlockDim, (rows_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_expand_scale_to_fp8_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const float *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), tile_rows, tile_cols, rows_padded, block_len);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 COMPUTE PER-BLOCK DECODE SCALE KERNEL\n *\n * Computes per-block decode scale from block amax and global amax:\n *   global_scale = (fp8_max * fp4_max) / global_amax = 2688 / global_amax\n *   per_block_decode_scale = block_amax * (global_scale * (1 / fp4_max))\n *                          = block_amax * 448 / global_amax\n *\n * This matches the CUDA device function compute_decoding_scaling_factor() in core_nvfp4.cuh\n *\n * Input (block_amax): [tile_rows, tile_cols] in float32\n * Input (global_amax): scalar float32 (per-tensor amax after all-reduce)\n * Output (scale): [tile_rows, tile_cols] in float32\n * Output (global_scale_out): scalar float32 (the computed global encode scale)\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_compute_per_block_scale_kernel(\n    const float *__restrict__ block_amax,       // [tile_rows, tile_cols]\n    float *__restrict__ scale,                  // [tile_rows, tile_cols]\n    const float *__restrict__ global_amax_ptr,  // Pointer to single float value (avoids D2H)\n    const size_t numel) {\n  const size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= numel) return;\n\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;  // FLT_MIN\n\n  // Read global_amax from device memory (avoids D2H transfer)\n  float global_amax = *global_amax_ptr;\n\n  // Compute global encode scale: S_enc = (fp8_max * fp4_max) / global_amax\n  float safe_global_amax = fmaxf(global_amax, tiny);\n  float global_scale =\n      (global_amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_global_amax, flt_max) : 1.0f;\n\n  // Compute per-block decode scale: S_dec_b = block_amax * (S_enc * (1 / fp4_max))\n  float amax_val = block_amax[idx];\n  constexpr float fp4_max_inv = 1.0f / fp4_max;\n  const float global_scale_multiplier = global_scale * fp4_max_inv;\n  float result = fminf(amax_val * global_scale_multiplier, flt_max);\n  scale[idx] = result;\n}\n\n// Simple kernel to compute global encode scale from global amax\n__global__ void nvfp4_compute_global_scale_kernel(\n    const float *__restrict__ global_amax,  // [num_params]\n    float *__restrict__ global_scale,       // [num_params]\n    const size_t num_params) {\n  const size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= num_params) return;\n\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;  // FLT_MIN\n\n  float amax = global_amax[idx];\n  float safe_amax = fmaxf(amax, tiny);\n  float scale = (amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_amax, flt_max) : 1.0f;\n  global_scale[idx] = scale;\n}\n\nvoid nvfp4_compute_per_block_scale(const Tensor block_amax, Tensor scale, const Tensor global_amax,\n                                   cudaStream_t stream) {\n  NVTE_CHECK(block_amax.dtype() == DType::kFloat32, \"Block amax must be float32.\");\n  NVTE_CHECK(scale.dtype() == DType::kFloat32, \"Scale must be float32.\");\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(global_amax.numel() == 1, \"Global amax must be a single element tensor.\");\n\n  size_t numel = block_amax.numel();\n  if (numel == 0) return;\n\n  constexpr int kBlockSize = 256;\n  int grid_size = (numel + kBlockSize - 1) / kBlockSize;\n\n  nvfp4_compute_per_block_scale_kernel<<<grid_size, kBlockSize, 0, stream>>>(\n      reinterpret_cast<const float *>(block_amax.data.dptr),\n      reinterpret_cast<float *>(scale.data.dptr),\n      reinterpret_cast<const float *>(global_amax.data.dptr), numel);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid nvfp4_compute_global_scale(const Tensor global_amax, Tensor global_scale,\n                                cudaStream_t stream) {\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(global_scale.dtype() == DType::kFloat32, \"Global scale must be float32.\");\n\n  size_t num_params = global_amax.numel();\n  if (num_params == 0) return;\n\n  constexpr int kBlockSize = 256;\n  int grid_size = (num_params + kBlockSize - 1) / kBlockSize;\n\n  nvfp4_compute_global_scale_kernel<<<grid_size, kBlockSize, 0, stream>>>(\n      reinterpret_cast<const float *>(global_amax.data.dptr),\n      reinterpret_cast<float *>(global_scale.data.dptr), num_params);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * FUSED NVFP4 SCALE COMPUTATION KERNEL\n *\n * Fuses three operations into one kernel:\n * 1. nvfp4_compute_per_block_scale: compute tile-level decode scales from block amax\n * 2. target_amax.copy_: copy global amax to target tensor\n * 3. nvfp4_expand_scale_to_fp8: expand to row-level and convert to FP8 E4M3\n *\n * Input (block_amax): [tile_rows, tile_cols] float32\n * Input (global_amax): [1] float32\n * Output (per_block_scale): [tile_rows, tile_cols] float32 (intermediate, for partial_cast)\n * Output (target_scale): [rows_padded, tile_cols] uint8 (E4M3)\n * Output (target_amax): [1] float32 (copy of global_amax)\n *\n * Saves 2 kernel launches per parameter (eliminates nvfp4_compute_per_block_scale and\n * nvfp4_expand_scale_to_fp8 as separate calls, plus the amax copy).\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_fused_scale_kernel(\n    const float *__restrict__ block_amax,   // [tile_rows, tile_cols]\n    const float *__restrict__ global_amax,  // [1]\n    float *__restrict__ per_block_scale,    // [tile_rows, tile_cols] - for partial_cast\n    uint8_t *__restrict__ target_scale,     // [rows_padded, tile_cols]\n    float *__restrict__ target_amax,        // [1]\n    const size_t tile_rows, const size_t tile_cols, const size_t rows_padded,\n    const size_t block_len) {\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Read global amax once per thread (broadcast)\n  const float g_amax = *global_amax;\n\n  // Thread (0,0) copies global_amax to target_amax\n  if (out_row == 0 && out_col == 0) {\n    *target_amax = g_amax;\n  }\n\n  if (out_row >= rows_padded || out_col >= tile_cols) return;\n\n  // Determine which tile row this output row belongs to\n  const size_t tile_row = out_row / block_len;\n\n  // Compute the scale value\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;\n\n  float scale_val = 0.0f;\n  if (tile_row < tile_rows) {\n    float safe_global_amax = fmaxf(g_amax, tiny);\n    float global_scale =\n        (g_amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_global_amax, flt_max) : 1.0f;\n    constexpr float fp4_max_inv = 1.0f / fp4_max;\n    const float global_scale_multiplier = global_scale * fp4_max_inv;\n\n    // Read block amax and compute per-block decode scale\n    float amax_val = block_amax[tile_row * tile_cols + out_col];\n    scale_val = fminf(amax_val * global_scale_multiplier, flt_max);\n\n    // Write per-block scale (only once per tile, when out_row % block_len == 0)\n    if (out_row % block_len == 0) {\n      per_block_scale[tile_row * tile_cols + out_col] = scale_val;\n    }\n  }\n\n  // Convert float32 to FP8 E4M3 and write expanded scale\n  fp8e4m3 fp8_val = static_cast<fp8e4m3>(scale_val);\n  target_scale[out_row * tile_cols + out_col] = reinterpret_cast<const uint8_t &>(fp8_val);\n}\n\nvoid nvfp4_fused_scale(const Tensor block_amax, const Tensor global_amax, Tensor per_block_scale,\n                       Tensor target_scale, Tensor target_amax, size_t tile_rows, size_t tile_cols,\n                       size_t rows_padded, size_t block_len, cudaStream_t stream) {\n  NVTE_CHECK(block_amax.dtype() == DType::kFloat32, \"Block amax must be float32.\");\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(per_block_scale.dtype() == DType::kFloat32, \"Per-block scale must be float32.\");\n  NVTE_CHECK(target_scale.dtype() == DType::kByte, \"Target scale must be uint8 (E4M3).\");\n  NVTE_CHECK(target_amax.dtype() == DType::kFloat32, \"Target amax must be float32.\");\n  NVTE_CHECK(global_amax.numel() == 1, \"Global amax must be a single element tensor.\");\n  NVTE_CHECK(target_amax.numel() == 1, \"Target amax must be a single element tensor.\");\n\n  if (tile_rows == 0 || tile_cols == 0 || rows_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((tile_cols + kBlockDim - 1) / kBlockDim, (rows_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_fused_scale_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const float *>(block_amax.data.dptr),\n      reinterpret_cast<const float *>(global_amax.data.dptr),\n      reinterpret_cast<float *>(per_block_scale.data.dptr),\n      reinterpret_cast<uint8_t *>(target_scale.data.dptr),\n      reinterpret_cast<float *>(target_amax.data.dptr), tile_rows, tile_cols, rows_padded,\n      block_len);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n#endif  // FP4_TYPE_SUPPORTED\n}  // namespace nvfp4_recipe\n}  // namespace transformer_engine\n\nvoid nvte_nvfp4_expand_scale_to_fp8(const NVTETensor input, NVTETensor output, size_t tile_rows,\n                                    size_t tile_cols, size_t rows_padded, size_t block_len,\n                                    cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_expand_scale_to_fp8);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_expand_scale_to_fp8(*convertNVTETensorCheck(input),\n                                          *convertNVTETensorCheck(output), tile_rows, tile_cols,\n                                          rows_padded, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_per_block_scale(const NVTETensor block_amax, NVTETensor scale,\n                                        const NVTETensor global_amax, cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_per_block_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_compute_per_block_scale(*convertNVTETensorCheck(block_amax),\n                                              *convertNVTETensorCheck(scale),\n                                              *convertNVTETensorCheck(global_amax), stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_global_scale(const NVTETensor global_amax, NVTETensor global_scale,\n                                     cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_global_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_compute_global_scale(*convertNVTETensorCheck(global_amax),\n                                           *convertNVTETensorCheck(global_scale), stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_scale_transpose(const NVTETensor input, NVTETensor output, size_t M_tiles,\n                                size_t K_tiles, cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_scale_transpose);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_scale_transpose(*convertNVTETensorCheck(input),\n                                      *convertNVTETensorCheck(output), M_tiles, K_tiles, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_data_transpose(const NVTETensor input, NVTETensor output, cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_data_transpose);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_transpose(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                                stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_2d_compute_partial_amax(const NVTETensor inp, NVTETensor amax, size_t h, size_t w,\n                                        size_t amax_stride_h, size_t amax_stride_w,\n                                        size_t start_offset, size_t block_len,\n                                        cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_2d_compute_partial_amax);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_2d_compute_partial_amax(*convertNVTETensorCheck(inp),\n                                              *convertNVTETensorCheck(amax), h, w, amax_stride_h,\n                                              amax_stride_w, start_offset, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_2d_partial_cast(const NVTETensor inp, NVTETensor out, const NVTETensor scale,\n                                const NVTETensor global_scale, size_t h, size_t w,\n                                size_t scale_stride_h, size_t scale_stride_w, size_t start_offset,\n                                size_t block_len, cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_2d_partial_cast);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_2d_partial_cast(*convertNVTETensorCheck(inp), *convertNVTETensorCheck(out),\n                                      *convertNVTETensorCheck(scale),\n                                      *convertNVTETensorCheck(global_scale), h, w, scale_stride_h,\n                                      scale_stride_w, start_offset, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_per_tensor_scale(const NVTETensor inpA, const bool use_rowwise_amax_A,\n                                         const NVTETensor inpB, const bool use_rowwise_amax_B,\n                                         float alpha_in, NVTETensor alpha_out,\n                                         cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_per_tensor_scale);\n  using namespace transformer_engine;\n\n  auto *tA = convertNVTETensor(inpA);\n  auto *tB = convertNVTETensor(inpB);\n  auto *tOut = convertNVTETensor(alpha_out);\n\n  void *amax_A_ptr = use_rowwise_amax_A ? tA->amax.dptr : tA->columnwise_amax.dptr;\n  void *amax_B_ptr = use_rowwise_amax_B ? tB->amax.dptr : tB->columnwise_amax.dptr;\n  void *alpha_ptr = tOut->data.dptr;\n\n  // check for not null pointers\n  NVTE_CHECK(amax_A_ptr != nullptr, \"amax_A_ptr is null\");\n  NVTE_CHECK(amax_B_ptr != nullptr, \"amax_B_ptr is null\");\n  NVTE_CHECK(alpha_ptr != nullptr, \"alpha_ptr is null\");\n\n  nvfp4_recipe::compute_nvfp4_per_tensor_scale_kernel<<<1, 1, 0, stream>>>(\n      alpha_in, reinterpret_cast<const float *>(amax_A_ptr),\n      reinterpret_cast<const float *>(amax_B_ptr), reinterpret_cast<float *>(alpha_ptr));\n  NVTE_CHECK_CUDA(cudaGetLastError());\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_fused_scale(const NVTETensor block_amax, const NVTETensor global_amax,\n                            NVTETensor per_block_scale, NVTETensor target_scale,\n                            NVTETensor target_amax, size_t tile_rows, size_t tile_cols,\n                            size_t rows_padded, size_t block_len, cudaStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_fused_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_fused_scale(\n      *convertNVTETensorCheck(block_amax), *convertNVTETensorCheck(global_amax),\n      *convertNVTETensorCheck(per_block_scale), *convertNVTETensorCheck(target_scale),\n      *convertNVTETensorCheck(target_amax), tile_rows, tile_cols, rows_padded, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <transformer_engine/recipe.h>\n#include <transformer_engine/transpose.h>\n\n#include <cassert>\n#include <limits>\n\n#include \"../common.h\"\n#include \"../util/ptx.cuh\"\n#include \"../utils.cuh\"\n\nnamespace transformer_engine {\nnamespace nvfp4_recipe {\n\n#if FP4_TYPE_SUPPORTED\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 2D PARTIAL-SHARD KERNEL DESIGN\n *\n * These kernels mirror the FP8 block-scaling helpers but operate on shard-local\n * slices and nibble-packed FP4 rowwise buffers. One CUDA block covers a logical\n * 16x16 tile (grid = ceil(W/16) x ceil(H/16), blockDim = 256 threads).\n *\n * 1) Partial Amax (`nvfp4_2d_compute_partial_amax_kernel`)\n *    - Warps sweep the tile using nested loops, accumulating local maxima only\n *      for elements in [start_offset, start_offset + len).\n *    - Shared memory reduces the 8 warp maxima; the block writes a float into\n *      `amax_ptr[tile_row * stride_h + tile_col * stride_w]`.\n *\n *      Tile/warp mapping (each '#' = elements visited by that warp):\n *\n *          +------------------+\n *          |########..........|  Warp 0\n *          |########..........|  Warp 1\n *          |   ...            |\n *          |########..........|  Warp 7\n *          +------------------+\n *\n * 2) Partial Cast (`nvfp4_2d_partial_cast_kernel`)\n *    - Stage the tile into shared memory (same pattern as FP8).\n *    - For each 4-value group, build float2 pairs and call\n *      `ptx::mul_cvt_fp32_to_fp4_4x`, producing packed FP4 nibbles.\n *    - Compute a shard-local byte index and update only the owned nibble(s)\n *      using read-modify-write:\n *\n *          packed_bits = [mw3 | mw2 | mw1 | mw0]\n *          byte_idx    = (ref_elem_idx - start_offset) >> 1\n *          if elem_idx % 2 == 0:  // low nibble\n *              byte = (byte & 0xF0) | nibble\n *          else:                  // high nibble\n *              byte = (byte & 0x0F) | (nibble << 4)\n *\n *      Thread coverage inside a tile:\n *\n *          rows:   16           columns: 16\n *          Warp 0 -> rows 0-1   lanes sweep cols 0..3, 4..7, ...\n *          Warp 1 -> rows 2-3   (groups of 4 elements per thread)\n *          ...\n *          Warp 7 -> rows 14-15\n * ---------------------------------------------------------------------------\n */\n\n// constexpr float factor = 6.0 * 6.0 * 448.0 * 448.0;\nconstexpr float factor_inv = 1.0 / (6.0 * 6.0 * 448.0 * 448.0);\nconstexpr int kTileDim = 16;\nconstexpr int kThreadsPerBlock = 256;\n\n// Kernel to compute alpha *= amax_A * amax_B / factor\n__global__ void compute_nvfp4_per_tensor_scale_kernel(float alpha_in, const float *amax_A,\n                                                      const float *amax_B, float *alpha_out) {\n  // factor is defined in the enclosing namespace\n  *alpha_out = alpha_in * (*amax_A) * (*amax_B) * factor_inv;\n}\n\ntemplate <typename IType>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    nvfp4_2d_compute_partial_amax_kernel(const IType *input, float *amax_ptr,\n                                         const size_t amax_stride_h, const size_t amax_stride_w,\n                                         const size_t h, const size_t w, const size_t start_offset,\n                                         const size_t len) {\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  static_assert(kTileDim * kTileDim == kThreadsPerBlock);\n\n  const size_t tile_col = blockIdx.x;\n  const size_t tile_row = blockIdx.y;\n  const size_t end_offset = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  __shared__ float smem[kNumWarps];\n  float amax = 0.0f;\n\n  size_t r = tile_row * kTileDim + threadIdx.x / kTileDim;\n  size_t c = tile_col * kTileDim + threadIdx.x % kTileDim;\n  size_t idx = r * w + c;\n  if (r < h && c < w && idx >= start_offset && idx < end_offset) {\n    amax = fabs(static_cast<float>(input_minus_offset[idx]));\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    float other_amax = __shfl_down_sync(0xFFFFFFFF, amax, delta);\n    __builtin_assume(amax >= 0);\n    __builtin_assume(other_amax >= 0);\n    amax = fmaxf(amax, other_amax);\n  }\n\n  if (threadIdx.x % kThreadsPerWarp == 0) {\n    smem[threadIdx.x / kThreadsPerWarp] = amax;\n  }\n\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < kNumWarps; ++i) {\n      float other_amax = smem[i];\n      __builtin_assume(amax >= 0);\n      __builtin_assume(other_amax >= 0);\n      amax = fmaxf(amax, other_amax);\n    }\n    amax_ptr[tile_row * amax_stride_h + tile_col * amax_stride_w] = amax;\n  }\n}\n\ntemplate <typename IType, bool kWidthAligned>\n__global__ void __launch_bounds__(kThreadsPerBlock)\n    nvfp4_2d_partial_cast_kernel(const IType *input, uint8_t *output, const float *decode_scale_ptr,\n                                 const size_t scale_stride_h, const size_t scale_stride_w,\n                                 const float *global_scale_ptr, const size_t h, const size_t w,\n                                 const size_t start_offset, const size_t len) {\n  constexpr int kNumOutputElemsPerBank = 4;\n  constexpr int kThreadsPerWarp = 32;\n  constexpr int kLoopsPerRow = (kTileDim + kThreadsPerWarp - 1) / kThreadsPerWarp;\n  constexpr int kNumWarps = kThreadsPerBlock / kThreadsPerWarp;\n  constexpr int kRowsPerWarp = (kTileDim + kNumWarps - 1) / kNumWarps;\n\n  __shared__ float smem[kTileDim][kTileDim + kNumOutputElemsPerBank];\n\n  const int tile_w = blockIdx.x;\n  const int tile_h = blockIdx.y;\n  const size_t shard_end = start_offset + len;\n  const IType *input_minus_offset = input - start_offset;\n\n  float global_encode_scale = global_scale_ptr[0];\n  if (global_encode_scale <= 0.f) {\n    global_encode_scale = 1.f;\n  }\n  const float global_decode_scale = 1.0f / global_encode_scale;\n\n  float tile_decode_scale = decode_scale_ptr[tile_h * scale_stride_h + tile_w * scale_stride_w];\n  tile_decode_scale = static_cast<float>(static_cast<fp8e4m3>(tile_decode_scale));\n  constexpr float kFp32Max = 3.402823466e+38F;\n  float tile_encode_val =\n      (tile_decode_scale > 0.f) ? 1.0f / (tile_decode_scale * global_decode_scale) : kFp32Max;\n  tile_encode_val = fminf(tile_encode_val, kFp32Max);\n  const float2 scale_vec = make_float2(tile_encode_val, tile_encode_val);\n\n  bool skip_store = true;\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    for (int j = 0; j < kLoopsPerRow; ++j) {\n      const int h_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n      const int w_in_smem = threadIdx.x % kThreadsPerWarp + kThreadsPerWarp * j;\n      if (h_in_smem >= kTileDim || w_in_smem >= kTileDim) {\n        continue;\n      }\n      const int h_in_input = tile_h * kTileDim + h_in_smem;\n      const int w_in_input = tile_w * kTileDim + w_in_smem;\n      const size_t idx_in_input = static_cast<size_t>(h_in_input) * w + w_in_input;\n      if (h_in_input < h && w_in_input < w && idx_in_input >= start_offset &&\n          idx_in_input < shard_end) {\n        smem[h_in_smem][w_in_smem] = static_cast<float>(input_minus_offset[idx_in_input]);\n        skip_store = false;\n      }\n    }\n  }\n\n  for (int delta = kThreadsPerWarp / 2; delta > 0; delta /= 2) {\n    bool other = __shfl_down_sync(0xFFFFFFFF, skip_store, delta);\n    skip_store = skip_store && other;\n  }\n  skip_store = __shfl_sync(0xFFFFFFFF, skip_store, 0);\n  if (skip_store) {\n    return;\n  }\n\n  for (int i = 0; i < kRowsPerWarp; ++i) {\n    const int row_in_smem = threadIdx.x / kThreadsPerWarp * kRowsPerWarp + i;\n    const int row_in_output = tile_h * kTileDim + row_in_smem;\n    if (row_in_output >= h) {\n      continue;\n    }\n    const int col_in_smem = threadIdx.x % kThreadsPerWarp * kNumOutputElemsPerBank;\n    if (col_in_smem >= kTileDim) {\n      continue;\n    }\n    const int col_in_output = tile_w * kTileDim + col_in_smem;\n\n    float vals[kNumOutputElemsPerBank];\n    bool mask[kNumOutputElemsPerBank];\n    size_t elem_idx[kNumOutputElemsPerBank];\n    bool any_valid = false;\n\n    for (int j = 0; j < kNumOutputElemsPerBank; ++j) {\n      const int col = col_in_output + j;\n      const bool in_width = col < w;\n      const size_t idx = static_cast<size_t>(row_in_output) * w + col;\n      elem_idx[j] = idx;\n      const bool in_shard = in_width && idx >= start_offset && idx < shard_end;\n      mask[j] = in_shard;\n      const bool in_tile = (col_in_smem + j) < kTileDim;\n      const float tile_val = in_tile ? smem[row_in_smem][col_in_smem + j] : 0.0f;\n      vals[j] = in_shard ? tile_val : 0.0f;\n      any_valid |= in_shard;\n    }\n\n    if (!any_valid) {\n      continue;\n    }\n\n    const float2 in01 = make_float2(vals[0], vals[1]);\n    const float2 in23 = make_float2(vals[2], vals[3]);\n    const auto packed =\n        transformer_engine::ptx::mul_cvt_fp32_to_fp4_4x<false>(in01, in23, scale_vec, 0);\n    const uint16_t packed_bits = reinterpret_cast<const uint16_t &>(packed);\n\n    for (int pair = 0; pair < 2; ++pair) {\n      const int first = pair * 2;\n      const int second = first + 1;\n      if (!mask[first] && !mask[second]) {\n        continue;\n      }\n      const size_t ref_idx = mask[first] ? elem_idx[first] : elem_idx[second];\n      const size_t byte_idx = (ref_idx - start_offset) >> 1;\n      uint8_t byte = output[byte_idx];\n\n      if (mask[first]) {\n        const uint8_t nibble = static_cast<uint8_t>((packed_bits >> (4 * first)) & 0xF);\n        if ((elem_idx[first] & 1u) == 0) {\n          byte = static_cast<uint8_t>((byte & 0xF0u) | nibble);\n        } else {\n          byte = static_cast<uint8_t>((byte & 0x0Fu) | (nibble << 4));\n        }\n      }\n\n      if (mask[second]) {\n        const uint8_t nibble = static_cast<uint8_t>((packed_bits >> (4 * second)) & 0xF);\n        if ((elem_idx[second] & 1u) == 0) {\n          byte = static_cast<uint8_t>((byte & 0xF0u) | nibble);\n        } else {\n          byte = static_cast<uint8_t>((byte & 0x0Fu) | (nibble << 4));\n        }\n      }\n\n      output[byte_idx] = byte;\n    }\n  }\n}\n\nvoid nvfp4_2d_compute_partial_amax(const Tensor inp, Tensor amax, size_t h, size_t w,\n                                   size_t amax_stride_h, size_t amax_stride_w, size_t start_offset,\n                                   size_t block_len, hipStream_t stream) {\n  NVTE_CHECK(block_len == 16, \"NVFP4 2D supports 16x16 tiles only (block_len = 16).\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      nvfp4_2d_compute_partial_amax_kernel<inp_dtype><<<grid, kThreadsPerBlock, 0, stream>>>(\n          reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n          reinterpret_cast<float *>(amax.data.dptr), amax_stride_h, amax_stride_w, h, w,\n          start_offset, len);)\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid nvfp4_2d_partial_cast(const Tensor inp, Tensor out, const Tensor scale,\n                           const Tensor global_scale, size_t h, size_t w, size_t scale_stride_h,\n                           size_t scale_stride_w, size_t start_offset, size_t block_len,\n                           hipStream_t stream) {\n  NVTE_CHECK(block_len == 16, \"NVFP4 2D supports 16x16 tiles only (block_len = 16).\");\n  NVTE_CHECK(out.dtype() == DType::kByte, \"NVFP4 rowwise data must be uint8.\");\n\n  size_t len = inp.numel();\n\n  assert(h > 0 && w > 0);\n  assert(start_offset < h * w);\n  assert(start_offset + len <= h * w);\n\n  size_t blocks_x = (w + kTileDim - 1) / kTileDim;\n  size_t blocks_y = (h + kTileDim - 1) / kTileDim;\n  assert(blocks_x <= std::numeric_limits<unsigned int>::max());\n  assert(blocks_y <= std::numeric_limits<unsigned int>::max());\n  dim3 grid(blocks_x, blocks_y);\n\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      inp.dtype(), inp_dtype,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          w % kTileDim == 0, kWidthAligned,\n          nvfp4_2d_partial_cast_kernel<inp_dtype, kWidthAligned>\n          <<<grid, kThreadsPerBlock, 0, stream>>>(\n              reinterpret_cast<const inp_dtype *>(inp.data.dptr),\n              reinterpret_cast<uint8_t *>(out.data.dptr),\n              reinterpret_cast<const float *>(scale.data.dptr), scale_stride_h, scale_stride_w,\n              reinterpret_cast<const float *>(global_scale.data.dptr), h, w, start_offset, len);))\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 TRANSPOSE KERNEL\n *\n * Unlike FP8, NVFP4 packs two 4-bit values into each byte. A simple byte-wise\n * transpose doesn't work because the packing changes:\n *   - Before transpose: elements [m, 2c] and [m, 2c+1] share a byte\n *   - After transpose:  elements [k, 2*m_packed] and [k, 2*m_packed+1] share a byte\n *                       which were originally [2*m_packed, k] and [2*m_packed+1, k]\n * ---------------------------------------------------------------------------\n */\n\n// Vectorized transpose kernel parameters\nconstexpr int TRANSPOSE_TILE_DIM = 64;  // Logical FP4 elements per tile dimension\n// constexpr int TRANSPOSE_TILE_PACKED = 32;  // TILE_DIM / 2 bytes\nconstexpr int TRANSPOSE_BLOCK_SIZE = 256;  // threads per block\n\n// Shared memory: store unpacked 4-bit values as bytes for easy transpose\n// Size: TILE_DIM x (TILE_DIM + 4) to avoid bank conflicts\nconstexpr int TRANSPOSE_SHMEM_STRIDE = TRANSPOSE_TILE_DIM + 4;\n\n/*\n * Vectorized transpose kernel with uint2 loads/stores (256 threads)\n * Tile: 64x64 logical FP4 = 64x32 packed bytes\n */\n__global__ void __launch_bounds__(TRANSPOSE_BLOCK_SIZE)\n    nvfp4_transpose_kernel(const uint8_t *__restrict__ input, uint8_t *__restrict__ output,\n                           const size_t M, const size_t K) {\n  const size_t K_packed = K / 2;\n  const size_t M_packed = M / 2;\n\n  const size_t tile_m_start = blockIdx.x * TRANSPOSE_TILE_DIM;\n  const size_t tile_k_start = blockIdx.y * TRANSPOSE_TILE_DIM;\n\n  __shared__ uint8_t shmem[TRANSPOSE_TILE_DIM][TRANSPOSE_SHMEM_STRIDE];\n\n  const int tid = threadIdx.x;\n\n  // Phase 1: Load input tile with VECTORIZED uint2 reads\n  // 256 threads, each loads 8 bytes (uint2) = 2048 bytes total\n  // Input tile: [64 rows, 32 cols] = 2048 bytes\n  {\n    const int thread_row = tid / 4;        // 64 rows, 4 threads per row\n    const int thread_col = (tid % 4) * 8;  // 4 x 8 = 32 bytes per row\n\n    const size_t global_m = tile_m_start + thread_row;\n    const size_t global_k_packed_base = tile_k_start / 2 + thread_col;\n\n    // Load 8 bytes as uint2\n    uint2 loaded = make_uint2(0, 0);\n    if (global_m < M && global_k_packed_base + 7 < K_packed) {\n      loaded = *reinterpret_cast<const uint2 *>(&input[global_m * K_packed + global_k_packed_base]);\n    } else if (global_m < M) {\n      // Boundary: scalar loads\n      uint8_t *bytes = reinterpret_cast<uint8_t *>(&loaded);\n#pragma unroll\n      for (int b = 0; b < 8; ++b) {\n        size_t col = global_k_packed_base + b;\n        bytes[b] = (col < K_packed) ? input[global_m * K_packed + col] : 0;\n      }\n    }\n\n    // Unpack 8 bytes -> 16 nibbles and store to shared memory\n    const uint8_t *bytes = reinterpret_cast<const uint8_t *>(&loaded);\n#pragma unroll\n    for (int b = 0; b < 8; ++b) {\n      const int k0 = thread_col * 2 + b * 2;\n      const int k1 = k0 + 1;\n      shmem[thread_row][k0] = bytes[b] & 0x0F;\n      shmem[thread_row][k1] = (bytes[b] >> 4) & 0x0F;\n    }\n  }\n\n  __syncthreads();\n\n  // Phase 2: Write output with VECTORIZED uint2 stores\n  // Output tile: [64 rows, 32 cols] = 2048 bytes\n  {\n    const int thread_row = tid / 4;             // output K dimension [0, 64)\n    const int thread_col_base = (tid % 4) * 8;  // output M_packed [0, 32) in steps of 8\n\n    const size_t global_k = tile_k_start + thread_row;\n    const size_t global_m_packed_base = tile_m_start / 2 + thread_col_base;\n\n    if (global_k >= K) return;\n\n    // Build 8 output bytes in registers\n    uint8_t out_bytes[8];\n\n#pragma unroll\n    for (int b = 0; b < 8; ++b) {\n      const int out_m_packed = thread_col_base + b;\n\n      if (global_m_packed_base + b >= M_packed) {\n        out_bytes[b] = 0;\n        continue;\n      }\n\n      // Two M positions that pack into this output byte\n      const int m0 = out_m_packed * 2;\n      const int m1 = out_m_packed * 2 + 1;\n      const int k = thread_row;\n\n      // Read from shared memory (transposed access)\n      const uint8_t val0 = shmem[m0][k];\n      const uint8_t val1 = shmem[m1][k];\n\n      out_bytes[b] = val0 | (val1 << 4);\n    }\n\n    // Vectorized store as uint2\n    if (global_m_packed_base + 7 < M_packed) {\n      *reinterpret_cast<uint2 *>(&output[global_k * M_packed + global_m_packed_base]) =\n          *reinterpret_cast<uint2 *>(out_bytes);\n    } else {\n      // Boundary: scalar stores\n      for (int b = 0; b < 8 && global_m_packed_base + b < M_packed; ++b) {\n        output[global_k * M_packed + global_m_packed_base + b] = out_bytes[b];\n      }\n    }\n  }\n}\n\nvoid nvfp4_transpose(const Tensor input, Tensor output, hipStream_t stream) {\n  // Input has logical shape [M, K], stored as [M, K/2] bytes\n  // Output has logical shape [K, M], stored as [K, M/2] bytes\n\n  NVTE_CHECK(input.dtype() == DType::kByte, \"NVFP4 transpose input must be uint8.\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"NVFP4 transpose output must be uint8.\");\n\n  // Get dimensions from packed storage\n  // input.shape() = [M, K/2], so M = shape[0], K = shape[1] * 2\n  const auto in_shape = input.shape();\n  NVTE_CHECK(in_shape.size() == 2, \"NVFP4 transpose expects 2D input (packed), got \",\n             in_shape.size(), \"D.\");\n  const size_t M = in_shape[0];\n  const size_t K_packed = in_shape[1];\n  const size_t K = K_packed * 2;\n\n  // Output should be [K, M/2]\n  const size_t M_packed = M / 2;\n  NVTE_CHECK(M % 2 == 0, \"NVFP4 transpose requires M (\", M, \") to be even.\");\n\n  const auto out_shape = output.shape();\n  NVTE_CHECK(out_shape.size() == 2, \"NVFP4 transpose expects 2D output.\");\n  NVTE_CHECK(out_shape[0] == K && out_shape[1] == M_packed,\n             \"NVFP4 transpose output shape mismatch. Expected [\", K, \", \", M_packed, \"], got [\",\n             out_shape[0], \", \", out_shape[1], \"].\");\n\n  if (M == 0 || K == 0) return;\n\n  // Use vectorized kernel (faster than TMA for pure transpose)\n  // 128x128 tiles with 512 threads and uint4 vectorized access\n  dim3 block(TRANSPOSE_BLOCK_SIZE);\n  dim3 grid((M + TRANSPOSE_TILE_DIM - 1) / TRANSPOSE_TILE_DIM,\n            (K + TRANSPOSE_TILE_DIM - 1) / TRANSPOSE_TILE_DIM);\n\n  nvfp4_transpose_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const uint8_t *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), M, K);\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 SCALE TRANSPOSE KERNEL\n *\n * Transposes tile-level scales from rowwise to columnwise format.\n * Scale values are stored as E4M3 (fp8) in uint8 tensors.\n *\n * Input (rowwise_scale_inv): [M_padded, K_tiles] where scales are stored\n *   at every 16th row (i.e., row 0, 16, 32, ... contain the actual scales,\n *   and each row i within a tile block has the same scale as row (i // 16) * 16).\n *\n * Output (columnwise_scale_inv): [K_padded, M_tiles] where scales are\n *   repeated 16 times per tile row.\n *\n * Mapping:\n *   output[k_tile * 16 + i, m_tile] = input[m_tile * 16, k_tile]\n *   for i in [0, 16) and valid (k_tile, m_tile) indices.\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_scale_transpose_kernel(\n    const uint8_t *__restrict__ input,  // [M_padded, K_tiles], E4M3 stored as uint8\n    uint8_t *__restrict__ output,       // [K_padded, M_tiles], E4M3 stored as uint8\n    const size_t M_tiles,               // Number of M tiles\n    const size_t K_tiles,               // Number of K tiles\n    const size_t input_stride,          // K_tiles (input row stride)\n    const size_t output_stride,         // M_tiles (output row stride)\n    const size_t K_padded               // Output height\n) {\n  // Each thread handles one output element\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (out_row >= K_padded || out_col >= M_tiles) return;\n\n  // Determine which tile row this belongs to\n  const size_t k_tile = out_row / kTileDim;\n\n  // Read from input: row = m_tile * 16 (first row of the tile), col = k_tile\n  // m_tile = out_col\n  if (k_tile < K_tiles) {\n    const size_t in_row = out_col * kTileDim;  // m_tile * 16\n    const uint8_t scale = input[in_row * input_stride + k_tile];\n    output[out_row * output_stride + out_col] = scale;\n  } else {\n    output[out_row * output_stride + out_col] = 0;\n  }\n}\n\nvoid nvfp4_scale_transpose(const Tensor input, Tensor output, size_t M_tiles, size_t K_tiles,\n                           hipStream_t stream) {\n  NVTE_CHECK(input.dtype() == DType::kByte, \"NVFP4 scale transpose input must be uint8 (E4M3).\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"NVFP4 scale transpose output must be uint8 (E4M3).\");\n\n  const auto in_shape = input.shape();\n  const auto out_shape = output.shape();\n  NVTE_CHECK(in_shape.size() == 2, \"NVFP4 scale transpose expects 2D input.\");\n  NVTE_CHECK(out_shape.size() == 2, \"NVFP4 scale transpose expects 2D output.\");\n\n  const size_t input_stride = in_shape[1];    // K_tiles\n  const size_t output_stride = out_shape[1];  // M_tiles\n  const size_t K_padded = out_shape[0];\n\n  if (M_tiles == 0 || K_tiles == 0 || K_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((M_tiles + kBlockDim - 1) / kBlockDim, (K_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_scale_transpose_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const uint8_t *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), M_tiles, K_tiles, input_stride, output_stride,\n      K_padded);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 SCALE EXPANSION KERNEL\n *\n * Expands tile-level scales to row-level scales and converts to FP8 E4M3, used in partial cast.\n *\n * Input (per_block_decode_scale): [tile_rows, tile_cols] in float32\n * Output (target_scale): [rows_padded, tile_cols] in uint8 (E4M3)\n *\n * Each tile row's scale is repeated block_len times in the output.\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_expand_scale_to_fp8_kernel(\n    const float *__restrict__ input,  // [tile_rows, tile_cols]\n    uint8_t *__restrict__ output,     // [rows_padded, tile_cols]\n    const size_t tile_rows, const size_t tile_cols, const size_t rows_padded,\n    const size_t block_len) {\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (out_row >= rows_padded || out_col >= tile_cols) return;\n\n  // Determine which tile row this output row belongs to\n  const size_t tile_row = out_row / block_len;\n\n  float scale_val = 0.0f;\n  if (tile_row < tile_rows) {\n    scale_val = input[tile_row * tile_cols + out_col];\n  }\n\n  // Convert float32 to FP8 E4M3\n  // Clamp to FP8 E4M3 range and convert\n  fp8e4m3 fp8_val = static_cast<fp8e4m3>(scale_val);\n  output[out_row * tile_cols + out_col] = reinterpret_cast<const uint8_t &>(fp8_val);\n}\n\nvoid nvfp4_expand_scale_to_fp8(const Tensor input, Tensor output, size_t tile_rows,\n                               size_t tile_cols, size_t rows_padded, size_t block_len,\n                               hipStream_t stream) {\n  NVTE_CHECK(input.dtype() == DType::kFloat32, \"Scale input must be float32.\");\n  NVTE_CHECK(output.dtype() == DType::kByte, \"Scale output must be uint8 (E4M3).\");\n\n  if (tile_rows == 0 || tile_cols == 0 || rows_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((tile_cols + kBlockDim - 1) / kBlockDim, (rows_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_expand_scale_to_fp8_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const float *>(input.data.dptr),\n      reinterpret_cast<uint8_t *>(output.data.dptr), tile_rows, tile_cols, rows_padded, block_len);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * NVFP4 COMPUTE PER-BLOCK DECODE SCALE KERNEL\n *\n * Computes per-block decode scale from block amax and global amax:\n *   global_scale = (fp8_max * fp4_max) / global_amax = 2688 / global_amax\n *   per_block_decode_scale = block_amax * (global_scale * (1 / fp4_max))\n *                          = block_amax * 448 / global_amax\n *\n * This matches the CUDA device function compute_decoding_scaling_factor() in core_nvfp4.cuh\n *\n * Input (block_amax): [tile_rows, tile_cols] in float32\n * Input (global_amax): scalar float32 (per-tensor amax after all-reduce)\n * Output (scale): [tile_rows, tile_cols] in float32\n * Output (global_scale_out): scalar float32 (the computed global encode scale)\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_compute_per_block_scale_kernel(\n    const float *__restrict__ block_amax,       // [tile_rows, tile_cols]\n    float *__restrict__ scale,                  // [tile_rows, tile_cols]\n    const float *__restrict__ global_amax_ptr,  // Pointer to single float value (avoids D2H)\n    const size_t numel) {\n  const size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= numel) return;\n\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;  // FLT_MIN\n\n  // Read global_amax from device memory (avoids D2H transfer)\n  float global_amax = *global_amax_ptr;\n\n  // Compute global encode scale: S_enc = (fp8_max * fp4_max) / global_amax\n  float safe_global_amax = fmaxf(global_amax, tiny);\n  float global_scale =\n      (global_amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_global_amax, flt_max) : 1.0f;\n\n  // Compute per-block decode scale: S_dec_b = block_amax * (S_enc * (1 / fp4_max))\n  float amax_val = block_amax[idx];\n  constexpr float fp4_max_inv = 1.0f / fp4_max;\n  const float global_scale_multiplier = global_scale * fp4_max_inv;\n  float result = fminf(amax_val * global_scale_multiplier, flt_max);\n  scale[idx] = result;\n}\n\n// Simple kernel to compute global encode scale from global amax\n__global__ void nvfp4_compute_global_scale_kernel(\n    const float *__restrict__ global_amax,  // [num_params]\n    float *__restrict__ global_scale,       // [num_params]\n    const size_t num_params) {\n  const size_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx >= num_params) return;\n\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;  // FLT_MIN\n\n  float amax = global_amax[idx];\n  float safe_amax = fmaxf(amax, tiny);\n  float scale = (amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_amax, flt_max) : 1.0f;\n  global_scale[idx] = scale;\n}\n\nvoid nvfp4_compute_per_block_scale(const Tensor block_amax, Tensor scale, const Tensor global_amax,\n                                   hipStream_t stream) {\n  NVTE_CHECK(block_amax.dtype() == DType::kFloat32, \"Block amax must be float32.\");\n  NVTE_CHECK(scale.dtype() == DType::kFloat32, \"Scale must be float32.\");\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(global_amax.numel() == 1, \"Global amax must be a single element tensor.\");\n\n  size_t numel = block_amax.numel();\n  if (numel == 0) return;\n\n  constexpr int kBlockSize = 256;\n  int grid_size = (numel + kBlockSize - 1) / kBlockSize;\n\n  nvfp4_compute_per_block_scale_kernel<<<grid_size, kBlockSize, 0, stream>>>(\n      reinterpret_cast<const float *>(block_amax.data.dptr),\n      reinterpret_cast<float *>(scale.data.dptr),\n      reinterpret_cast<const float *>(global_amax.data.dptr), numel);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid nvfp4_compute_global_scale(const Tensor global_amax, Tensor global_scale,\n                                hipStream_t stream) {\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(global_scale.dtype() == DType::kFloat32, \"Global scale must be float32.\");\n\n  size_t num_params = global_amax.numel();\n  if (num_params == 0) return;\n\n  constexpr int kBlockSize = 256;\n  int grid_size = (num_params + kBlockSize - 1) / kBlockSize;\n\n  nvfp4_compute_global_scale_kernel<<<grid_size, kBlockSize, 0, stream>>>(\n      reinterpret_cast<const float *>(global_amax.data.dptr),\n      reinterpret_cast<float *>(global_scale.data.dptr), num_params);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n/*\n * ---------------------------------------------------------------------------\n * FUSED NVFP4 SCALE COMPUTATION KERNEL\n *\n * Fuses three operations into one kernel:\n * 1. nvfp4_compute_per_block_scale: compute tile-level decode scales from block amax\n * 2. target_amax.copy_: copy global amax to target tensor\n * 3. nvfp4_expand_scale_to_fp8: expand to row-level and convert to FP8 E4M3\n *\n * Input (block_amax): [tile_rows, tile_cols] float32\n * Input (global_amax): [1] float32\n * Output (per_block_scale): [tile_rows, tile_cols] float32 (intermediate, for partial_cast)\n * Output (target_scale): [rows_padded, tile_cols] uint8 (E4M3)\n * Output (target_amax): [1] float32 (copy of global_amax)\n *\n * Saves 2 kernel launches per parameter (eliminates nvfp4_compute_per_block_scale and\n * nvfp4_expand_scale_to_fp8 as separate calls, plus the amax copy).\n * ---------------------------------------------------------------------------\n */\n__global__ void nvfp4_fused_scale_kernel(\n    const float *__restrict__ block_amax,   // [tile_rows, tile_cols]\n    const float *__restrict__ global_amax,  // [1]\n    float *__restrict__ per_block_scale,    // [tile_rows, tile_cols] - for partial_cast\n    uint8_t *__restrict__ target_scale,     // [rows_padded, tile_cols]\n    float *__restrict__ target_amax,        // [1]\n    const size_t tile_rows, const size_t tile_cols, const size_t rows_padded,\n    const size_t block_len) {\n  const size_t out_row = blockIdx.y * blockDim.y + threadIdx.y;\n  const size_t out_col = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Read global amax once per thread (broadcast)\n  const float g_amax = *global_amax;\n\n  // Thread (0,0) copies global_amax to target_amax\n  if (out_row == 0 && out_col == 0) {\n    *target_amax = g_amax;\n  }\n\n  if (out_row >= rows_padded || out_col >= tile_cols) return;\n\n  // Determine which tile row this output row belongs to\n  const size_t tile_row = out_row / block_len;\n\n  // Compute the scale value\n  constexpr float fp4_max = 6.0f;\n  constexpr float fp8_max = 448.0f;\n  constexpr float flt_max = 3.402823466e+38f;\n  constexpr float tiny = 1.17549435e-38f;\n\n  float scale_val = 0.0f;\n  if (tile_row < tile_rows) {\n    float safe_global_amax = fmaxf(g_amax, tiny);\n    float global_scale =\n        (g_amax > 0.0f) ? fminf((fp8_max * fp4_max) / safe_global_amax, flt_max) : 1.0f;\n    constexpr float fp4_max_inv = 1.0f / fp4_max;\n    const float global_scale_multiplier = global_scale * fp4_max_inv;\n\n    // Read block amax and compute per-block decode scale\n    float amax_val = block_amax[tile_row * tile_cols + out_col];\n    scale_val = fminf(amax_val * global_scale_multiplier, flt_max);\n\n    // Write per-block scale (only once per tile, when out_row % block_len == 0)\n    if (out_row % block_len == 0) {\n      per_block_scale[tile_row * tile_cols + out_col] = scale_val;\n    }\n  }\n\n  // Convert float32 to FP8 E4M3 and write expanded scale\n  fp8e4m3 fp8_val = static_cast<fp8e4m3>(scale_val);\n  target_scale[out_row * tile_cols + out_col] = reinterpret_cast<const uint8_t &>(fp8_val);\n}\n\nvoid nvfp4_fused_scale(const Tensor block_amax, const Tensor global_amax, Tensor per_block_scale,\n                       Tensor target_scale, Tensor target_amax, size_t tile_rows, size_t tile_cols,\n                       size_t rows_padded, size_t block_len, hipStream_t stream) {\n  NVTE_CHECK(block_amax.dtype() == DType::kFloat32, \"Block amax must be float32.\");\n  NVTE_CHECK(global_amax.dtype() == DType::kFloat32, \"Global amax must be float32.\");\n  NVTE_CHECK(per_block_scale.dtype() == DType::kFloat32, \"Per-block scale must be float32.\");\n  NVTE_CHECK(target_scale.dtype() == DType::kByte, \"Target scale must be uint8 (E4M3).\");\n  NVTE_CHECK(target_amax.dtype() == DType::kFloat32, \"Target amax must be float32.\");\n  NVTE_CHECK(global_amax.numel() == 1, \"Global amax must be a single element tensor.\");\n  NVTE_CHECK(target_amax.numel() == 1, \"Target amax must be a single element tensor.\");\n\n  if (tile_rows == 0 || tile_cols == 0 || rows_padded == 0) return;\n\n  constexpr int kBlockDim = 16;\n  dim3 block(kBlockDim, kBlockDim);\n  dim3 grid((tile_cols + kBlockDim - 1) / kBlockDim, (rows_padded + kBlockDim - 1) / kBlockDim);\n\n  nvfp4_fused_scale_kernel<<<grid, block, 0, stream>>>(\n      reinterpret_cast<const float *>(block_amax.data.dptr),\n      reinterpret_cast<const float *>(global_amax.data.dptr),\n      reinterpret_cast<float *>(per_block_scale.data.dptr),\n      reinterpret_cast<uint8_t *>(target_scale.data.dptr),\n      reinterpret_cast<float *>(target_amax.data.dptr), tile_rows, tile_cols, rows_padded,\n      block_len);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n#endif  // FP4_TYPE_SUPPORTED\n}  // namespace nvfp4_recipe\n}  // namespace transformer_engine\n\nvoid nvte_nvfp4_expand_scale_to_fp8(const NVTETensor input, NVTETensor output, size_t tile_rows,\n                                    size_t tile_cols, size_t rows_padded, size_t block_len,\n                                    hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_expand_scale_to_fp8);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_expand_scale_to_fp8(*convertNVTETensorCheck(input),\n                                          *convertNVTETensorCheck(output), tile_rows, tile_cols,\n                                          rows_padded, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_per_block_scale(const NVTETensor block_amax, NVTETensor scale,\n                                        const NVTETensor global_amax, hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_per_block_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_compute_per_block_scale(*convertNVTETensorCheck(block_amax),\n                                              *convertNVTETensorCheck(scale),\n                                              *convertNVTETensorCheck(global_amax), stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_global_scale(const NVTETensor global_amax, NVTETensor global_scale,\n                                     hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_global_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_compute_global_scale(*convertNVTETensorCheck(global_amax),\n                                           *convertNVTETensorCheck(global_scale), stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_scale_transpose(const NVTETensor input, NVTETensor output, size_t M_tiles,\n                                size_t K_tiles, hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_scale_transpose);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_scale_transpose(*convertNVTETensorCheck(input),\n                                      *convertNVTETensorCheck(output), M_tiles, K_tiles, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_data_transpose(const NVTETensor input, NVTETensor output, hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_data_transpose);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_transpose(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                                stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_2d_compute_partial_amax(const NVTETensor inp, NVTETensor amax, size_t h, size_t w,\n                                        size_t amax_stride_h, size_t amax_stride_w,\n                                        size_t start_offset, size_t block_len,\n                                        hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_2d_compute_partial_amax);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_2d_compute_partial_amax(*convertNVTETensorCheck(inp),\n                                              *convertNVTETensorCheck(amax), h, w, amax_stride_h,\n                                              amax_stride_w, start_offset, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_2d_partial_cast(const NVTETensor inp, NVTETensor out, const NVTETensor scale,\n                                const NVTETensor global_scale, size_t h, size_t w,\n                                size_t scale_stride_h, size_t scale_stride_w, size_t start_offset,\n                                size_t block_len, hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_2d_partial_cast);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_2d_partial_cast(*convertNVTETensorCheck(inp), *convertNVTETensorCheck(out),\n                                      *convertNVTETensorCheck(scale),\n                                      *convertNVTETensorCheck(global_scale), h, w, scale_stride_h,\n                                      scale_stride_w, start_offset, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_compute_per_tensor_scale(const NVTETensor inpA, const bool use_rowwise_amax_A,\n                                         const NVTETensor inpB, const bool use_rowwise_amax_B,\n                                         float alpha_in, NVTETensor alpha_out,\n                                         hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_compute_per_tensor_scale);\n  using namespace transformer_engine;\n\n  auto *tA = convertNVTETensor(inpA);\n  auto *tB = convertNVTETensor(inpB);\n  auto *tOut = convertNVTETensor(alpha_out);\n\n  void *amax_A_ptr = use_rowwise_amax_A ? tA->amax.dptr : tA->columnwise_amax.dptr;\n  void *amax_B_ptr = use_rowwise_amax_B ? tB->amax.dptr : tB->columnwise_amax.dptr;\n  void *alpha_ptr = tOut->data.dptr;\n\n  // check for not null pointers\n  NVTE_CHECK(amax_A_ptr != nullptr, \"amax_A_ptr is null\");\n  NVTE_CHECK(amax_B_ptr != nullptr, \"amax_B_ptr is null\");\n  NVTE_CHECK(alpha_ptr != nullptr, \"alpha_ptr is null\");\n\n  nvfp4_recipe::compute_nvfp4_per_tensor_scale_kernel<<<1, 1, 0, stream>>>(\n      alpha_in, reinterpret_cast<const float *>(amax_A_ptr),\n      reinterpret_cast<const float *>(amax_B_ptr), reinterpret_cast<float *>(alpha_ptr));\n  NVTE_CHECK_CUDA(hipGetLastError());\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n\nvoid nvte_nvfp4_fused_scale(const NVTETensor block_amax, const NVTETensor global_amax,\n                            NVTETensor per_block_scale, NVTETensor target_scale,\n                            NVTETensor target_amax, size_t tile_rows, size_t tile_cols,\n                            size_t rows_padded, size_t block_len, hipStream_t stream) {\n#if FP4_TYPE_SUPPORTED\n  NVTE_API_CALL(nvte_nvfp4_fused_scale);\n  using namespace transformer_engine;\n  nvfp4_recipe::nvfp4_fused_scale(\n      *convertNVTETensorCheck(block_amax), *convertNVTETensorCheck(global_amax),\n      *convertNVTETensorCheck(per_block_scale), *convertNVTETensorCheck(target_scale),\n      *convertNVTETensorCheck(target_amax), tile_rows, tile_cols, rows_padded, block_len, stream);\n#else\n  NVTE_ERROR(\"FP4 support requires CUDA 12.8+, but compile-time CUDA version is \", CUDA_VERSION);\n#endif  // FP4_TYPE_SUPPORTED\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/recipe/nvfp4.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <assert.h>\n#include <cuda_fp8.h>\n#include <transformer_engine/multi_tensor.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include \"../utils.cuh\"\n#include \"multi_tensor_apply.cuh\"\n\nnamespace transformer_engine {\nnamespace multi_tensor_l2norm {\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T *p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T *dst, T *src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT *)dst)[dst_offset] = ((LT *)src)[src_offset];  // NOLINT(*)\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T\nreduce_block_into_lanes(T *x, T val, int lanes = 1,\n                        bool share_result = false) {  // lanes is intended to be <= 32.\n  int tid = threadIdx.x + threadIdx.y * blockDim.x;\n  int blockSize = blockDim.x * blockDim.y;  // blockSize is intended to be a multiple of 32.\n\n  if (blockSize >= 64) {\n    x[tid] = val;\n    __syncthreads();\n  }\n\n#pragma unroll\n  for (int i = (blockSize >> 1); i >= 64; i >>= 1) {\n    if (tid < i) x[tid] = x[tid] + x[tid + i];\n    __syncthreads();\n  }\n\n  T final;\n\n  if (tid < 32) {\n    if (blockSize >= 64)\n      final = x[tid] + x[tid + 32];\n    else\n      final = val;\n      // __SYNCWARP();\n\n#pragma unroll\n    for (int i = 16; i >= lanes; i >>= 1) final = final + __shfl_down_sync(0xffffffff, final, i);\n  }\n\n  if (share_result) {\n    if (tid < lanes) x[tid] = final;  // EpilogueOp\n    // Make sure the smem result is visible to all warps.\n  }\n  __syncthreads();\n  // Avoid potential write before read race when reduce_block_into_lanes is called back to back\n\n  return final;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T\nreduce_block_into_lanes_max_op(T *x, T val, int lanes = 1,\n                               bool share_result = false) {  // lanes is intended to be <= 32.\n  int tid = threadIdx.x + threadIdx.y * blockDim.x;\n  int blockSize = blockDim.x * blockDim.y;  // blockSize is intended to be a multiple of 32.\n\n  if (blockSize >= 64) {\n    x[tid] = val;\n    __syncthreads();\n  }\n\n#pragma unroll\n  for (int i = (blockSize >> 1); i >= 64; i >>= 1) {\n    if (tid < i) x[tid] = fmaxf(fabsf(x[tid]), fabsf(x[tid + i]));\n    __syncthreads();\n  }\n\n  T final;\n\n  if (tid < 32) {\n    if (blockSize >= 64)\n      final = fmaxf(fabsf(x[tid]), fabsf(x[tid + 32]));\n    else\n      final = val;\n      // __SYNCWARP();\n\n#pragma unroll\n    for (int i = 16; i >= lanes; i >>= 1)\n      final = fmaxf(fabsf(final), fabsf(__shfl_down_sync(0xffffffff, final, i)));\n  }\n\n  if (share_result) {\n    if (tid < lanes) x[tid] = final;  // EpilogueOp\n    // Make sure the smem result is visible to all warps.\n    __syncthreads();\n  }\n\n  return final;\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             float *output, float *output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0.f;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\ntemplate <typename x_t>\nstruct UnscaleL2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             const float *inv_scale, float *output,\n                                             float *output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0.f;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]) * (*inv_scale);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]) * (*inv_scale);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             float *output, float *output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float *output, float *output_per_tensor, float *ret, float *ret_per_tensor,\n                        bool per_tensor, int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float *output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n      val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\n__global__ void cleanup_v2(float *output, float *output_per_tensor, float *ret,\n                           float *ret_per_tensor, bool per_tensor, int max_chunks_per_tensor,\n                           int norm_type, float alpha, float beta) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    if (norm_type == 0) {\n      float final = reduce_block_into_lanes_max_op(vals, val);\n      if (threadIdx.x == 0) *ret = alpha * (*ret) + beta * final;\n    } else {\n      float final = reduce_block_into_lanes(vals, val);\n      if (threadIdx.x == 0) *ret = sqrt(alpha * (*ret) * (*ret) + beta * final);\n    }\n  }\n\n  if (per_tensor) {\n    float *output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    if (norm_type == 0) {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val = fmaxf(fabsf(val), fabsf(output_this_tensor[i]));\n\n      float final = reduce_block_into_lanes_max_op(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] = alpha * ret_per_tensor[blockIdx.x] + beta * final;\n    } else {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val += output_this_tensor[i];\n\n      float final = reduce_block_into_lanes(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] =\n            sqrt(alpha * ret_per_tensor[blockIdx.x] * ret_per_tensor[blockIdx.x] + beta * final);\n    }\n  }\n}\n\nvoid multi_tensor_l2norm_cuda(int chunk_size, Tensor noop_flag,\n                              std::vector<std::vector<Tensor *>> tensor_lists, Tensor output,\n                              Tensor output_per_tensor, Tensor ret, Tensor ret_per_tensor,\n                              bool per_tensor, int max_chunks_per_tensor, cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      tensor_lists[0][0]->dtype(), dtype,\n      multi_tensor_apply<1>(\n          BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<dtype>(), stream,\n          reinterpret_cast<float *>(output.data.dptr),\n          per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr, per_tensor,\n          max_chunks_per_tensor);)\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  cleanup<<<per_tensor ? tensor_lists[0].size() : 1, 512, 0, stream>>>(\n      reinterpret_cast<float *>(output.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr,\n      reinterpret_cast<float *>(ret.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(ret_per_tensor.data.dptr) : nullptr, per_tensor,\n      max_chunks_per_tensor);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\nvoid multi_tensor_unscale_l2norm_cuda(int chunk_size, Tensor noop_flag,\n                                      std::vector<std::vector<Tensor *>> tensor_lists,\n                                      Tensor output, Tensor output_per_tensor, Tensor ret,\n                                      Tensor ret_per_tensor, Tensor inv_scale, bool per_tensor,\n                                      int max_chunks_per_tensor, cudaStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      tensor_lists[0][0]->dtype(), dtype,\n      multi_tensor_apply<1>(\n          BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, UnscaleL2NormFunctor<dtype>(), stream,\n          reinterpret_cast<float *>(inv_scale.data.dptr),\n          reinterpret_cast<float *>(output.data.dptr),\n          per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr, per_tensor,\n          max_chunks_per_tensor);)\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  cleanup<<<per_tensor ? tensor_lists[0].size() : 1, 512, 0, stream>>>(\n      reinterpret_cast<float *>(output.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr,\n      reinterpret_cast<float *>(ret.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(ret_per_tensor.data.dptr) : nullptr, per_tensor,\n      max_chunks_per_tensor);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n}  // namespace multi_tensor_l2norm\n}  // namespace transformer_engine\n\nvoid nvte_multi_tensor_l2norm_cuda(int chunk_size, NVTETensor noop_flag, NVTETensor **tensor_lists,\n                                   const size_t num_tensor_lists, const size_t num_tensors_per_list,\n                                   NVTETensor output, NVTETensor output_per_tensor, NVTETensor ret,\n                                   NVTETensor ret_per_tensor, int per_tensor,\n                                   int max_chunks_per_tensor, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_l2norm_cuda);\n  using namespace transformer_engine;\n\n  multi_tensor_l2norm::multi_tensor_l2norm_cuda(\n      chunk_size, *convertNVTETensorCheck(noop_flag),\n      convert_tensor_array(tensor_lists, num_tensor_lists, num_tensors_per_list),\n      *convertNVTETensorCheck(output), *convertNVTETensorCheck(output_per_tensor),\n      *convertNVTETensorCheck(ret), *convertNVTETensorCheck(ret_per_tensor), per_tensor,\n      max_chunks_per_tensor, stream);\n}\n\nvoid nvte_multi_tensor_unscale_l2norm_cuda(int chunk_size, NVTETensor noop_flag,\n                                           NVTETensor **tensor_lists, const size_t num_tensor_lists,\n                                           const size_t num_tensors_per_list, NVTETensor output,\n                                           NVTETensor output_per_tensor, NVTETensor ret,\n                                           NVTETensor ret_per_tensor, NVTETensor inv_scale,\n                                           int per_tensor, int max_chunks_per_tensor,\n                                           cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_unscale_l2norm_cuda);\n  using namespace transformer_engine;\n\n  multi_tensor_l2norm::multi_tensor_unscale_l2norm_cuda(\n      chunk_size, *convertNVTETensorCheck(noop_flag),\n      convert_tensor_array(tensor_lists, num_tensor_lists, num_tensors_per_list),\n      *convertNVTETensorCheck(output), *convertNVTETensorCheck(output_per_tensor),\n      *convertNVTETensorCheck(ret), *convertNVTETensorCheck(ret_per_tensor),\n      *convertNVTETensorCheck(inv_scale), per_tensor, max_chunks_per_tensor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <hip/hip_fp8.h>\n#include <transformer_engine/multi_tensor.h>\n#include <transformer_engine/transformer_engine.h>\n\n#include \"../utils.cuh\"\n#include \"multi_tensor_apply.cuh\"\n\nnamespace transformer_engine {\nnamespace multi_tensor_l2norm {\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T *p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T *dst, T *src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT *)dst)[dst_offset] = ((LT *)src)[src_offset];  // NOLINT(*)\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T\nreduce_block_into_lanes(T *x, T val, int lanes = 1,\n                        bool share_result = false) {  // lanes is intended to be <= 32.\n  int tid = threadIdx.x + threadIdx.y * blockDim.x;\n  int blockSize = blockDim.x * blockDim.y;  // blockSize is intended to be a multiple of 32.\n\n  if (blockSize >= 64) {\n    x[tid] = val;\n    __syncthreads();\n  }\n\n#pragma unroll\n  for (int i = (blockSize >> 1); i >= 64; i >>= 1) {\n    if (tid < i) x[tid] = x[tid] + x[tid + i];\n    __syncthreads();\n  }\n\n  T final;\n\n  if (tid < 32) {\n    if (blockSize >= 64)\n      final = x[tid] + x[tid + 32];\n    else\n      final = val;\n      // __SYNCWARP();\n\n#pragma unroll\n    for (int i = 16; i >= lanes; i >>= 1) final = final + __shfl_down_sync(0xffffffff, final, i);\n  }\n\n  if (share_result) {\n    if (tid < lanes) x[tid] = final;  // EpilogueOp\n    // Make sure the smem result is visible to all warps.\n  }\n  __syncthreads();\n  // Avoid potential write before read race when reduce_block_into_lanes is called back to back\n\n  return final;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T\nreduce_block_into_lanes_max_op(T *x, T val, int lanes = 1,\n                               bool share_result = false) {  // lanes is intended to be <= 32.\n  int tid = threadIdx.x + threadIdx.y * blockDim.x;\n  int blockSize = blockDim.x * blockDim.y;  // blockSize is intended to be a multiple of 32.\n\n  if (blockSize >= 64) {\n    x[tid] = val;\n    __syncthreads();\n  }\n\n#pragma unroll\n  for (int i = (blockSize >> 1); i >= 64; i >>= 1) {\n    if (tid < i) x[tid] = fmaxf(fabsf(x[tid]), fabsf(x[tid + i]));\n    __syncthreads();\n  }\n\n  T final;\n\n  if (tid < 32) {\n    if (blockSize >= 64)\n      final = fmaxf(fabsf(x[tid]), fabsf(x[tid + 32]));\n    else\n      final = val;\n      // __SYNCWARP();\n\n#pragma unroll\n    for (int i = 16; i >= lanes; i >>= 1)\n      final = fmaxf(fabsf(final), fabsf(__shfl_down_sync(0xffffffff, final, i)));\n  }\n\n  if (share_result) {\n    if (tid < lanes) x[tid] = final;  // EpilogueOp\n    // Make sure the smem result is visible to all warps.\n    __syncthreads();\n  }\n\n  return final;\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             float *output, float *output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0.f;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\ntemplate <typename x_t>\nstruct UnscaleL2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             const float *inv_scale, float *output,\n                                             float *output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0.f;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]) * (*inv_scale);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]) * (*inv_scale);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int *noop_gmem,\n                                             TensorListMetadata<1> &tl,  // NOLINT(*),\n                                             float *output, float *output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t *x = reinterpret_cast<x_t *>(tl.addresses[0][tensor_loc]);\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size;\n           i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final))\n        *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor +\n                          chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float *output, float *output_per_tensor, float *ret, float *ret_per_tensor,\n                        bool per_tensor, int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float *output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n      val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\n__global__ void cleanup_v2(float *output, float *output_per_tensor, float *ret,\n                           float *ret_per_tensor, bool per_tensor, int max_chunks_per_tensor,\n                           int norm_type, float alpha, float beta) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    if (norm_type == 0) {\n      float final = reduce_block_into_lanes_max_op(vals, val);\n      if (threadIdx.x == 0) *ret = alpha * (*ret) + beta * final;\n    } else {\n      float final = reduce_block_into_lanes(vals, val);\n      if (threadIdx.x == 0) *ret = sqrt(alpha * (*ret) * (*ret) + beta * final);\n    }\n  }\n\n  if (per_tensor) {\n    float *output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    if (norm_type == 0) {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val = fmaxf(fabsf(val), fabsf(output_this_tensor[i]));\n\n      float final = reduce_block_into_lanes_max_op(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] = alpha * ret_per_tensor[blockIdx.x] + beta * final;\n    } else {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val += output_this_tensor[i];\n\n      float final = reduce_block_into_lanes(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] =\n            sqrt(alpha * ret_per_tensor[blockIdx.x] * ret_per_tensor[blockIdx.x] + beta * final);\n    }\n  }\n}\n\nvoid multi_tensor_l2norm_cuda(int chunk_size, Tensor noop_flag,\n                              std::vector<std::vector<Tensor *>> tensor_lists, Tensor output,\n                              Tensor output_per_tensor, Tensor ret, Tensor ret_per_tensor,\n                              bool per_tensor, int max_chunks_per_tensor, hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      tensor_lists[0][0]->dtype(), dtype,\n      multi_tensor_apply<1>(\n          BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<dtype>(), stream,\n          reinterpret_cast<float *>(output.data.dptr),\n          per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr, per_tensor,\n          max_chunks_per_tensor);)\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  cleanup<<<per_tensor ? tensor_lists[0].size() : 1, 512, 0, stream>>>(\n      reinterpret_cast<float *>(output.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr,\n      reinterpret_cast<float *>(ret.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(ret_per_tensor.data.dptr) : nullptr, per_tensor,\n      max_chunks_per_tensor);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\nvoid multi_tensor_unscale_l2norm_cuda(int chunk_size, Tensor noop_flag,\n                                      std::vector<std::vector<Tensor *>> tensor_lists,\n                                      Tensor output, Tensor output_per_tensor, Tensor ret,\n                                      Tensor ret_per_tensor, Tensor inv_scale, bool per_tensor,\n                                      int max_chunks_per_tensor, hipStream_t stream) {\n  TRANSFORMER_ENGINE_TYPE_SWITCH_NON_FP8ONLY(\n      tensor_lists[0][0]->dtype(), dtype,\n      multi_tensor_apply<1>(\n          BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, UnscaleL2NormFunctor<dtype>(), stream,\n          reinterpret_cast<float *>(inv_scale.data.dptr),\n          reinterpret_cast<float *>(output.data.dptr),\n          per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr, per_tensor,\n          max_chunks_per_tensor);)\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  cleanup<<<per_tensor ? tensor_lists[0].size() : 1, 512, 0, stream>>>(\n      reinterpret_cast<float *>(output.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(output_per_tensor.data.dptr) : nullptr,\n      reinterpret_cast<float *>(ret.data.dptr),\n      per_tensor ? reinterpret_cast<float *>(ret_per_tensor.data.dptr) : nullptr, per_tensor,\n      max_chunks_per_tensor);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n}  // namespace multi_tensor_l2norm\n}  // namespace transformer_engine\n\nvoid nvte_multi_tensor_l2norm_cuda(int chunk_size, NVTETensor noop_flag, NVTETensor **tensor_lists,\n                                   const size_t num_tensor_lists, const size_t num_tensors_per_list,\n                                   NVTETensor output, NVTETensor output_per_tensor, NVTETensor ret,\n                                   NVTETensor ret_per_tensor, int per_tensor,\n                                   int max_chunks_per_tensor, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_l2norm_cuda);\n  using namespace transformer_engine;\n\n  multi_tensor_l2norm::multi_tensor_l2norm_cuda(\n      chunk_size, *convertNVTETensorCheck(noop_flag),\n      convert_tensor_array(tensor_lists, num_tensor_lists, num_tensors_per_list),\n      *convertNVTETensorCheck(output), *convertNVTETensorCheck(output_per_tensor),\n      *convertNVTETensorCheck(ret), *convertNVTETensorCheck(ret_per_tensor), per_tensor,\n      max_chunks_per_tensor, stream);\n}\n\nvoid nvte_multi_tensor_unscale_l2norm_cuda(int chunk_size, NVTETensor noop_flag,\n                                           NVTETensor **tensor_lists, const size_t num_tensor_lists,\n                                           const size_t num_tensors_per_list, NVTETensor output,\n                                           NVTETensor output_per_tensor, NVTETensor ret,\n                                           NVTETensor ret_per_tensor, NVTETensor inv_scale,\n                                           int per_tensor, int max_chunks_per_tensor,\n                                           hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_tensor_unscale_l2norm_cuda);\n  using namespace transformer_engine;\n\n  multi_tensor_l2norm::multi_tensor_unscale_l2norm_cuda(\n      chunk_size, *convertNVTETensorCheck(noop_flag),\n      convert_tensor_array(tensor_lists, num_tensor_lists, num_tensors_per_list),\n      *convertNVTETensorCheck(output), *convertNVTETensorCheck(output_per_tensor),\n      *convertNVTETensorCheck(ret), *convertNVTETensorCheck(ret_per_tensor),\n      *convertNVTETensorCheck(inv_scale), per_tensor, max_chunks_per_tensor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/multi_tensor/l2norm.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/hadamard_transform.h>\n#include <transformer_engine/multi_tensor.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB, expand 64 if needed\nstruct MultiAmaxArgs {\n  // (output) Amax buffer for pre-RHT amax buffer\n  void* output_pre_rht_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for RHT identity amax buffer\n  void* output_identity_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for RHT transpose amax buffer\n  void* output_transpose_amax_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\nconstexpr int kThreadsPerWarp = 32;\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n// args: the mult-tensor amax arguments\n__global__ void MultiZeroAmaxKernel(MultiAmaxArgs args) {\n  int num_tensors = args.num_tensors;\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for (; tid < num_tensors; tid += stride) {\n    float* output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tid]);\n    float* output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tid]);\n    float* output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tid]);\n    if (output_pre_rht_amax_ptr != nullptr) {\n      *output_pre_rht_amax_ptr = 0;\n    }\n    if (output_identity_amax_ptr != nullptr) {\n      *output_identity_amax_ptr = 0;\n    }\n    if (output_transpose_amax_ptr != nullptr) {\n      *output_transpose_amax_ptr = 0;\n    }\n  }\n}\n\n// args: the mult-tensor amax arguments\n__global__ void MultiAmaxMemcpyD2DKernelPreRHT(MultiAmaxArgs args) {\n  int num_tensors = args.num_tensors;\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for (; tid < num_tensors; tid += stride) {\n    float* output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tid]);\n    float* output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tid]);\n    float* output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tid]);\n    if (output_pre_rht_amax_ptr != nullptr) {\n      float pre_rht_amax = *output_pre_rht_amax_ptr;\n      if (output_identity_amax_ptr != nullptr) {\n        *output_identity_amax_ptr = pre_rht_amax;\n      }\n      if (output_transpose_amax_ptr != nullptr) {\n        *output_transpose_amax_ptr = pre_rht_amax;\n      }\n    }\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void GroupHadamardAmaxTmaKernel(const __grid_constant__ CUtensorMap tensor_map_input,\n                                           const MultiAmaxArgs args, uint16_t random_sign_mask,\n                                           uint16_t random_sign_mask_t, uint64_t num_rows,\n                                           uint64_t row_length) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  float* output_pre_rht_amax_ptr;\n  float* output_identity_amax_ptr;\n  float* output_transpose_amax_ptr;\n\n  // calculate the global offset in Y direction to access the correct amax buffer\n  int global_offset_y = blockIdx.y * CHUNK_DIM_Y;\n  int tensor_id = 0;\n  while (args.split_sections_range[tensor_id + 1] <= global_offset_y) {\n    ++tensor_id;\n  }\n  output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tensor_id]);\n  output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tensor_id]);\n  output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tensor_id]);\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\n}  // namespace\n\n// broadcast_pre_rht_amax: when it's true, hadamard transform will be disabled\n// if at this time, the amax buffers for output expects both amax_rowwise and amax_colwise\n// then call MultiAmaxMemcpyD2DKernelPreRHT to D2D copy the amax values\nvoid group_hadamard_transform_amax(const Tensor& input_, std::vector<Tensor*>& output_list,\n                                   const size_t* split_sections, size_t num_tensors,\n                                   uint16_t random_sign_mask, uint16_t random_sign_mask_t,\n                                   bool broadcast_pre_rht_amax, cudaStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_amax);\n#if CUDA_VERSION >= 12080\n\n  // Check input tensor\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor& input = input_.data;\n\n  // TODO: validate num_tensors and split_sections\n  // assert if num_tensors is greater than kMaxTensorsPerKernel\n  // will expand 64 to higher value if needed\n  // if input size is going to exceed 4KB kernel launch limit, will then support multi-launch\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // check split_sections\n  // TODO: support m_splits_tensor for device initiated API\n  NVTE_CHECK(split_sections != nullptr, \"split_sections should not be nullptr\");\n\n  MultiAmaxArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  bool all_return_pre_rht_amax = true;\n  bool all_return_identity_amax = true;\n  bool all_return_transposed_amax = true;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    void* output_pre_rht_amax_ptr = output_list[i]->amax.dptr;\n    // disable RHT(x) for now, only RHT_T(x) should be used\n    void* output_identity_amax_ptr = nullptr;\n    void* output_transpose_amax_ptr = output_list[i]->columnwise_amax.dptr;\n    all_return_pre_rht_amax &= (output_pre_rht_amax_ptr != nullptr);\n    all_return_identity_amax &= (output_identity_amax_ptr != nullptr);\n    all_return_transposed_amax &= (output_transpose_amax_ptr != nullptr);\n    // sanity check split_sections component to see if it's 64 multiple for each element\n    NVTE_CHECK(split_sections[i] % 64 == 0, \"component \", i,\n               \" of split_sections should be 64 multiple\");\n    // also skip adding this tensor to the kernel args there are zero elements in this split\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    // fill in kernel arguments\n    kernel_args.output_pre_rht_amax_list[kernel_args.num_tensors] = output_pre_rht_amax_ptr;\n    kernel_args.output_identity_amax_list[kernel_args.num_tensors] = output_identity_amax_ptr;\n    kernel_args.output_transpose_amax_list[kernel_args.num_tensors] = output_transpose_amax_ptr;\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  NVTE_CHECK(all_return_pre_rht_amax || all_return_identity_amax || all_return_transposed_amax,\n             \"At least one of return_pre_rht_amax, return_identity_amax, or return_transposed_amax \"\n             \"must be true\");\n  // currently we haven't supported all_return_identity_amax, assert error if it's mistakenly enabled\n  NVTE_CHECK(!all_return_identity_amax,\n             \"Currently RHT transform should only be applied to transposed input\");\n\n  if (broadcast_pre_rht_amax) {\n    NVTE_CHECK(all_return_pre_rht_amax,\n               \"broadcast_pre_rht_amax is only supported when we compute pre-RHT amax\");\n    // if all_return_identity_amax and all_return_transposed_amax both are false, there is no need to broadcast anything\n    broadcast_pre_rht_amax &= (all_return_identity_amax || all_return_transposed_amax);\n  }\n\n  // Multi zero out multiple amaxes if needed\n  // Currently don't support multi-launch when num_tensors is larger than kMaxTensorsPerKernel\n  // let the number of threads equal to number of tensors, use 1 block, kMaxTensorsPerKernel threads per block\n  dim3 block_setup_amax(kMaxTensorsPerKernel);\n  dim3 grid_setup_amax(1);\n  MultiZeroAmaxKernel<<<grid_setup_amax, block_setup_amax, 0, stream>>>(kernel_args);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  checkCuDriverContext(stream);\n\n  using IType = bf16;\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  // four (1x4) 64x64 sub-tiles for ping-pong overlap\n  constexpr uint64_t kChunkBlockXSmall = 256;\n  constexpr uint64_t kChunkBlockYSmall = 64;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input,\n      /*globalY=*/num_rows,\n      /*globalX=*/row_length,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/row_length,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length, kChunkBlockXSmall), DIVUP(num_rows, kChunkBlockYSmall));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      (all_return_transposed_amax && !broadcast_pre_rht_amax), kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          (all_return_identity_amax && !broadcast_pre_rht_amax), kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = GroupHadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(tensor_map_input, kernel_args,\n                                                           random_sign_mask, random_sign_mask_t,\n                                                           num_rows, row_length);\n              if (broadcast_pre_rht_amax) {\n                MultiAmaxMemcpyD2DKernelPreRHT<<<grid_setup_amax, block_setup_amax, 0, stream>>>(\n                    kernel_args);\n              })));\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\n// Naming convention: \"Group\" kernels here means contiguous input concatenated\n// While \"Multi\" kernels are processing a list of pointers, like the zero amax kernel\n\n// Group hadamard transform API is unlike other multi-input & multi-output APIs\n// Group hadamard transform will take in a single input tensor, and directly calculate amax\n// with optional RHT transform. That's because we can assume the input tensor list to be\n// contiguous in memory, so the tensors are only splitted in dimension 0.\n// RHT transform is 16x16, so as long as each split of the input has 16 multiple shape\n// in dimension 0, we can treat the entire input as a single tensor.\n// Although mathmatically 16 multple is enough for this function to be correct,\n// for this kernel, we required 64 multiple of 16 in dimension 0 for better performance.\nvoid nvte_group_hadamard_transform_amax(const NVTETensor input, NVTETensor* outputs,\n                                        const size_t* split_sections, size_t num_tensors,\n                                        int random_sign_mask, int random_sign_mask_t,\n                                        cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_amax);\n  using namespace transformer_engine;\n  if (num_tensors == 0) {\n    return;\n  }\n\n  Tensor* input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor*> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n  // Call the group tensor Hadamard transform amax implementation.\n  group_hadamard_transform_amax(*input_tensor, output_list, split_sections, num_tensors,\n                                static_cast<uint16_t>(random_sign_mask),\n                                static_cast<uint16_t>(random_sign_mask_t), false, stream);\n}\n\n// Grouped-tensor amax without doing hadamard transform\nvoid nvte_group_amax(const NVTETensor input, NVTETensor* outputs, const size_t* split_sections,\n                     size_t num_tensors, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_amax);\n  using namespace transformer_engine;\n  if (num_tensors == 0) {\n    return;\n  }\n\n  Tensor* input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor*> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  group_hadamard_transform_amax(*input_tensor, output_list, split_sections, num_tensors, 0, 0, true,\n                                stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <transformer_engine/hadamard_transform.h>\n#include <transformer_engine/multi_tensor.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB, expand 64 if needed\nstruct MultiAmaxArgs {\n  // (output) Amax buffer for pre-RHT amax buffer\n  void* output_pre_rht_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for RHT identity amax buffer\n  void* output_identity_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for RHT transpose amax buffer\n  void* output_transpose_amax_list[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\nconstexpr int kThreadsPerWarp = 32;\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n// args: the mult-tensor amax arguments\n__global__ void MultiZeroAmaxKernel(MultiAmaxArgs args) {\n  int num_tensors = args.num_tensors;\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for (; tid < num_tensors; tid += stride) {\n    float* output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tid]);\n    float* output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tid]);\n    float* output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tid]);\n    if (output_pre_rht_amax_ptr != nullptr) {\n      *output_pre_rht_amax_ptr = 0;\n    }\n    if (output_identity_amax_ptr != nullptr) {\n      *output_identity_amax_ptr = 0;\n    }\n    if (output_transpose_amax_ptr != nullptr) {\n      *output_transpose_amax_ptr = 0;\n    }\n  }\n}\n\n// args: the mult-tensor amax arguments\n__global__ void MultiAmaxMemcpyD2DKernelPreRHT(MultiAmaxArgs args) {\n  int num_tensors = args.num_tensors;\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  for (; tid < num_tensors; tid += stride) {\n    float* output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tid]);\n    float* output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tid]);\n    float* output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tid]);\n    if (output_pre_rht_amax_ptr != nullptr) {\n      float pre_rht_amax = *output_pre_rht_amax_ptr;\n      if (output_identity_amax_ptr != nullptr) {\n        *output_identity_amax_ptr = pre_rht_amax;\n      }\n      if (output_transpose_amax_ptr != nullptr) {\n        *output_transpose_amax_ptr = pre_rht_amax;\n      }\n    }\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void GroupHadamardAmaxTmaKernel(const __grid_constant__ CUtensorMap tensor_map_input,\n                                           const MultiAmaxArgs args, uint16_t random_sign_mask,\n                                           uint16_t random_sign_mask_t, uint64_t num_rows,\n                                           uint64_t row_length) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  float* output_pre_rht_amax_ptr;\n  float* output_identity_amax_ptr;\n  float* output_transpose_amax_ptr;\n\n  // calculate the global offset in Y direction to access the correct amax buffer\n  int global_offset_y = blockIdx.y * CHUNK_DIM_Y;\n  int tensor_id = 0;\n  while (args.split_sections_range[tensor_id + 1] <= global_offset_y) {\n    ++tensor_id;\n  }\n  output_pre_rht_amax_ptr = static_cast<float*>(args.output_pre_rht_amax_list[tensor_id]);\n  output_identity_amax_ptr = static_cast<float*>(args.output_identity_amax_list[tensor_id]);\n  output_transpose_amax_ptr = static_cast<float*>(args.output_transpose_amax_list[tensor_id]);\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\n}  // namespace\n\n// broadcast_pre_rht_amax: when it's true, hadamard transform will be disabled\n// if at this time, the amax buffers for output expects both amax_rowwise and amax_colwise\n// then call MultiAmaxMemcpyD2DKernelPreRHT to D2D copy the amax values\nvoid group_hadamard_transform_amax(const Tensor& input_, std::vector<Tensor*>& output_list,\n                                   const size_t* split_sections, size_t num_tensors,\n                                   uint16_t random_sign_mask, uint16_t random_sign_mask_t,\n                                   bool broadcast_pre_rht_amax, hipStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_amax);\n#if CUDA_VERSION >= 12080\n\n  // Check input tensor\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor& input = input_.data;\n\n  // TODO: validate num_tensors and split_sections\n  // assert if num_tensors is greater than kMaxTensorsPerKernel\n  // will expand 64 to higher value if needed\n  // if input size is going to exceed 4KB kernel launch limit, will then support multi-launch\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // check split_sections\n  // TODO: support m_splits_tensor for device initiated API\n  NVTE_CHECK(split_sections != nullptr, \"split_sections should not be nullptr\");\n\n  MultiAmaxArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  bool all_return_pre_rht_amax = true;\n  bool all_return_identity_amax = true;\n  bool all_return_transposed_amax = true;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    void* output_pre_rht_amax_ptr = output_list[i]->amax.dptr;\n    // disable RHT(x) for now, only RHT_T(x) should be used\n    void* output_identity_amax_ptr = nullptr;\n    void* output_transpose_amax_ptr = output_list[i]->columnwise_amax.dptr;\n    all_return_pre_rht_amax &= (output_pre_rht_amax_ptr != nullptr);\n    all_return_identity_amax &= (output_identity_amax_ptr != nullptr);\n    all_return_transposed_amax &= (output_transpose_amax_ptr != nullptr);\n    // sanity check split_sections component to see if it's 64 multiple for each element\n    NVTE_CHECK(split_sections[i] % 64 == 0, \"component \", i,\n               \" of split_sections should be 64 multiple\");\n    // also skip adding this tensor to the kernel args there are zero elements in this split\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    // fill in kernel arguments\n    kernel_args.output_pre_rht_amax_list[kernel_args.num_tensors] = output_pre_rht_amax_ptr;\n    kernel_args.output_identity_amax_list[kernel_args.num_tensors] = output_identity_amax_ptr;\n    kernel_args.output_transpose_amax_list[kernel_args.num_tensors] = output_transpose_amax_ptr;\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  NVTE_CHECK(all_return_pre_rht_amax || all_return_identity_amax || all_return_transposed_amax,\n             \"At least one of return_pre_rht_amax, return_identity_amax, or return_transposed_amax \"\n             \"must be true\");\n  // currently we haven't supported all_return_identity_amax, assert error if it's mistakenly enabled\n  NVTE_CHECK(!all_return_identity_amax,\n             \"Currently RHT transform should only be applied to transposed input\");\n\n  if (broadcast_pre_rht_amax) {\n    NVTE_CHECK(all_return_pre_rht_amax,\n               \"broadcast_pre_rht_amax is only supported when we compute pre-RHT amax\");\n    // if all_return_identity_amax and all_return_transposed_amax both are false, there is no need to broadcast anything\n    broadcast_pre_rht_amax &= (all_return_identity_amax || all_return_transposed_amax);\n  }\n\n  // Multi zero out multiple amaxes if needed\n  // Currently don't support multi-launch when num_tensors is larger than kMaxTensorsPerKernel\n  // let the number of threads equal to number of tensors, use 1 block, kMaxTensorsPerKernel threads per block\n  dim3 block_setup_amax(kMaxTensorsPerKernel);\n  dim3 grid_setup_amax(1);\n  MultiZeroAmaxKernel<<<grid_setup_amax, block_setup_amax, 0, stream>>>(kernel_args);\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  checkCuDriverContext(stream);\n\n  using IType = bf16;\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  // four (1x4) 64x64 sub-tiles for ping-pong overlap\n  constexpr uint64_t kChunkBlockXSmall = 256;\n  constexpr uint64_t kChunkBlockYSmall = 64;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input,\n      /*globalY=*/num_rows,\n      /*globalX=*/row_length,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/row_length,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length, kChunkBlockXSmall), DIVUP(num_rows, kChunkBlockYSmall));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      (all_return_transposed_amax && !broadcast_pre_rht_amax), kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          (all_return_identity_amax && !broadcast_pre_rht_amax), kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = GroupHadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(tensor_map_input, kernel_args,\n                                                           random_sign_mask, random_sign_mask_t,\n                                                           num_rows, row_length);\n              if (broadcast_pre_rht_amax) {\n                MultiAmaxMemcpyD2DKernelPreRHT<<<grid_setup_amax, block_setup_amax, 0, stream>>>(\n                    kernel_args);\n              })));\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\n// Naming convention: \"Group\" kernels here means contiguous input concatenated\n// While \"Multi\" kernels are processing a list of pointers, like the zero amax kernel\n\n// Group hadamard transform API is unlike other multi-input & multi-output APIs\n// Group hadamard transform will take in a single input tensor, and directly calculate amax\n// with optional RHT transform. That's because we can assume the input tensor list to be\n// contiguous in memory, so the tensors are only splitted in dimension 0.\n// RHT transform is 16x16, so as long as each split of the input has 16 multiple shape\n// in dimension 0, we can treat the entire input as a single tensor.\n// Although mathmatically 16 multple is enough for this function to be correct,\n// for this kernel, we required 64 multiple of 16 in dimension 0 for better performance.\nvoid nvte_group_hadamard_transform_amax(const NVTETensor input, NVTETensor* outputs,\n                                        const size_t* split_sections, size_t num_tensors,\n                                        int random_sign_mask, int random_sign_mask_t,\n                                        hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_amax);\n  using namespace transformer_engine;\n  if (num_tensors == 0) {\n    return;\n  }\n\n  Tensor* input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor*> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n  // Call the group tensor Hadamard transform amax implementation.\n  group_hadamard_transform_amax(*input_tensor, output_list, split_sections, num_tensors,\n                                static_cast<uint16_t>(random_sign_mask),\n                                static_cast<uint16_t>(random_sign_mask_t), false, stream);\n}\n\n// Grouped-tensor amax without doing hadamard transform\nvoid nvte_group_amax(const NVTETensor input, NVTETensor* outputs, const size_t* split_sections,\n                     size_t num_tensors, hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_amax);\n  using namespace transformer_engine;\n  if (num_tensors == 0) {\n    return;\n  }\n\n  Tensor* input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor*> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  group_hadamard_transform_amax(*input_tensor, output_list, split_sections, num_tensors, 0, 0, true,\n                                stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/group_hadamard_transform.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\n// clang-format off\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\nstruct CLCResponse { uint32_t data[4] = {0}; };\n\nconstexpr int kFp4ConvertChunkElements = 8;\nconstexpr int kFp4ConvertFullElements = 16;\nconstexpr int kFp4RbitsPerChunk = 2;\nconstexpr int kFp4ChunkCount = kFp4ConvertFullElements / kFp4ConvertChunkElements;\n\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> StochasticNumericConverterBase(\n    cutlass::Array<float, kFp4ConvertChunkElements> const &input,\n    cutlass::Array<uint32_t, kFp4RbitsPerChunk> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, kFp4ConvertFullElements>\nStochasticNumericConverter(cutlass::Array<float, kFp4ConvertFullElements> const &input,\n                           cutlass::Array<uint32_t, kFp4RbitsPerChunk * kFp4ChunkCount> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertFullElements>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> *>(&output);\n  cutlass::Array<float, kFp4ConvertChunkElements> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, kFp4ConvertChunkElements> const *>(&input);\n  cutlass::Array<uint32_t, kFp4RbitsPerChunk> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, kFp4RbitsPerChunk> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < kFp4ChunkCount; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <\n  class ElementA,\n  class ElementB,\n  class ASmemLayout,\n  class BSmemLayout,\n  class ClusterShape,\n  int AccumulatorPipelineStageCount_,\n  int EpilogueUnrollFactor_,\n  int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n    MainloopPipelineStageCount,\n    Shape<_1,_1,_1>,\n    AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using CLCPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using CLCPipelineStorage = typename CLCPipeline::SharedStorage;\n  using CLCThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using CLCThrottlePipelineStorage = typename CLCThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) CLCPipelineStorage clc;\n  alignas(16) CLCThrottlePipelineStorage clc_throttle;\n  alignas(16) CLCResponse clc_response[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA,\n          class TB, class BStride, class BSmemLayout, class TmaLoadB,\n          class TD, class DStride, class DSmemLayout,\n          class TSFD, class TSFDLayout,\n          class TQA, class QAStride,\n          class TSFA, class TSFALayout,\n          class TiledMMA,\n          int AccumulatorPipelineStageCount_,\n          int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false,\n          bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true,\n          bool kUseFastMath_ = true>\n__launch_bounds__(512, 1)\n__global__ static void row_col_rht_gemm_device(\n    MShape M,\n    NShape N,\n    KShape K,\n    ClusterShape cluster_shape,\n    ClusterTileShape cluster_tile,\n    TA const* A,\n    AStride dA,\n    ASmemLayout sAlayout,\n    CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const* B,\n    BStride dB,\n    BSmemLayout sBlayout,\n    CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TD* D,\n    DStride dD,\n    DSmemLayout,\n    TSFD* SFD,\n    TSFDLayout sfd_layout,\n    TQA* QA,\n    QAStride dQA,\n    TSFA* SFA,\n    TSFALayout sfa_layout,\n    TiledMMA mma,\n    float const* a_global_amax,\n    float const* c_global_amax,\n    const size_t* rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                \"row_col_rht_gemm_device must generate row-wise \"\n                \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n  CUTLASS_NOT_IMPLEMENTED();\n  return;\n#endif\n\n  using X = Underscore;\n  // static constexpr bool kApplyStochasticRounding = true;\n  using ElementAccumulator = float;\n  static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n  using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n  static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n      size(AtomThrShapeMNK{}) * cosize(take<0,3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n  static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n  static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n  static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n  static constexpr bool kUseFastMath = kUseFastMath_;\n  static int constexpr RhtTensorSize = 16;\n  static int constexpr kTmaRhtTensorTransactionBytes = cutlass::bits_to_bytes(\n      RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n    MainloopPipelineStageCount,\n    ClusterShape,\n    AtomThrShapeMNK>;\n  using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n  using CLCPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n  using CLCPipelineState = typename CLCPipeline::PipelineState;\n  using CLCThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n  using CLCThrottlePipelineState = typename CLCThrottlePipeline::PipelineState;\n\n  static_assert(ClusterShape{} == Shape<_1,_1,_1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  static int constexpr VectorSize = RhtTensorSize;\n  // Preconditions\n  CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n  auto cluster_size = size<0>(cluster_shape);\n  auto mainloop_tiler = Shape<_128,_16,_128>{};\n  auto epilogue_tiler = Shape<_128,_128,_128>{};\n\n  static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n  // Get the appropriate blocks for this Cluster\n  dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n  // Total number of k-tiles\n  int const K_TILE_MAX = ceil_div(min(N, K), size<2>(epilogue_tiler));\n\n  struct TileScheduler {\n    struct WorkTileInfo {\n      uint32_t m_idx = 0;\n      uint32_t n_idx = 0;\n      uint32_t l_idx = 0;\n      bool is_valid_tile = false;\n    };\n    uint32_t tiles_in_m = 0;\n    uint32_t tiles_in_n = 0;\n\n    int k_tile_max = 0;\n\n    int wave_cnt = 0;\n    WorkTileInfo work_tile_info;\n    WorkTileInfo next_work_tile_info;\n    CLCResponse* clc_response_ptr_;\n    CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax, CLCResponse* clc_response_ptr)\n      : tiles_in_m(tiles_m),\n        tiles_in_n(tiles_n),\n\n        k_tile_max(kmax),\n        work_tile_info({blockIdx.x, blockIdx.y, blockIdx.z, blockIdx.x<tiles_m && blockIdx.y<tiles_n}),\n        next_work_tile_info({blockIdx.x, blockIdx.y, blockIdx.z, blockIdx.x<tiles_m && blockIdx.y<tiles_n}),\n        clc_response_ptr_(clc_response_ptr) {}\n\n    CUTLASS_DEVICE uint32_t tile_m() const {\n      return work_tile_info.m_idx;\n    }\n    CUTLASS_DEVICE uint32_t tile_n_base() const {\n      return work_tile_info.n_idx * uint32_t(k_tile_max);\n    }\n\n    CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n    CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n    CUTLASS_DEVICE bool is_valid() const {\n      return cute::elem_less(cute::make_coord(work_tile_info.m_idx, work_tile_info.n_idx), cute::make_coord(tiles_in_m, tiles_in_n)) && work_tile_info.is_valid_tile;\n    }\n    CUTLASS_DEVICE bool is_first_wave() const { return wave_cnt == 0; }\n    CUTLASS_DEVICE auto advance_to_next_work(CLCPipeline& clc_pipeline, CLCPipelineState clc_pipe_producer_state) {\n      uint32_t mbarrier_addr = clc_pipeline.producer_get_barrier(clc_pipe_producer_state);\n      // Wait for clcID buffer to become empty with a flipped phase\n      clc_pipeline.producer_acquire(clc_pipe_producer_state);\n\n      if (cute::elect_one_sync()) {\n        issue_clc_query(clc_pipe_producer_state, mbarrier_addr, clc_response_ptr_);\n      }\n\n      ++clc_pipe_producer_state;\n      return clc_pipe_producer_state;\n    }\n\n    CUTLASS_DEVICE auto fetch_next_work(CLCPipeline& clc_pipeline, CLCPipelineState clc_pipe_producer_state) {\n      clc_pipeline.consumer_wait(clc_pipe_producer_state);\n      uint32_t smem_addr = cute::cast_smem_ptr_to_uint(&clc_response_ptr_[clc_pipe_producer_state.index()]);\n      next_work_tile_info = work_tile_info_from_clc_response(smem_addr);\n      clc_pipeline.consumer_release(clc_pipe_producer_state);\n      wave_cnt++;\n      return;\n    }\n\n    CUTLASS_DEVICE auto update_work_tile_info() {\n      work_tile_info = next_work_tile_info;\n      return;\n    }\n\n    CUTLASS_DEVICE uint32_t get_linear_tile_idx() const {\n      return work_tile_info.m_idx + work_tile_info.n_idx * tiles_in_m;\n    }\n\n    CUTLASS_HOST_DEVICE\n    static void\n    issue_clc_query(CLCPipelineState state, uint32_t mbarrier_addr, CLCResponse* clc_response_ptr) {\n    #if defined(CUTLASS_ARCH_CLC_ENABLED)\n        uint32_t result_addr = cute::cast_smem_ptr_to_uint(reinterpret_cast<const void*>(\n              &clc_response_ptr[state.index()]));\n        asm volatile(\n          \"{\\n\\t\"\n          \"clusterlaunchcontrol.try_cancel.async.shared::cta.mbarrier::complete_tx::bytes.multicast::cluster::all.b128 [%0], [%1];\\n\\t\"\n          \"}\\n\"\n          :\n          : \"r\"(result_addr), \"r\"(mbarrier_addr));\n    #else\n        CUTLASS_NOT_IMPLEMENTED();\n    #endif\n    }\n  CUTLASS_DEVICE\n  static WorkTileInfo\n  work_tile_info_from_clc_response(uint32_t result_addr) {\n    WorkTileInfo work_tile_info;\n    uint32_t valid = 0;\n    #if defined(CUTLASS_ARCH_CLC_ENABLED)\n      asm volatile(\n        \"{\\n\"\n        \".reg .pred p1;\\n\\t\"\n        \".reg .b128 clc_result;\\n\\t\"\n        \"ld.shared.b128 clc_result, [%4];\\n\\t\"\n        \"clusterlaunchcontrol.query_cancel.is_canceled.pred.b128 p1, clc_result;\\n\\t\"\n        \"selp.u32 %3, 1, 0, p1;\\n\\t\"\n        \"@p1 clusterlaunchcontrol.query_cancel.get_first_ctaid.v4.b32.b128 {%0, %1, %2, _}, clc_result;\\n\\t\"\n        \"}\\n\"\n        : \"=r\"(work_tile_info.m_idx), \"=r\"(work_tile_info.n_idx), \"=r\"(work_tile_info.l_idx), \"=r\"(valid)\n        : \"r\"(result_addr)\n        : \"memory\"\n      );\n\n      cutlass::arch::fence_view_async_shared();\n    #else\n      CUTLASS_NOT_IMPLEMENTED();\n    #endif\n    work_tile_info.is_valid_tile = (valid == 1);\n    return work_tile_info;\n  }\n  };\n\n\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount, EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n  uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n  uint32_t tiles_in_n = uint32_t(size(ceil_div(N, size<2>(epilogue_tiler))));\n  TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, shared_storage.clc_response);\n\n  int block_rank_in_cluster = cute::block_rank_in_cluster();\n  auto acc_shape_mma = make_shape(take<0,2>(mainloop_tiler), _1{}, _1{});\n  auto acc_shape_epilogue = make_shape(take<0,2>(epilogue_tiler), _1{}, _1{});\n\n  auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n  auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n  static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n  static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n  static int constexpr NumMmaThreadCount = kEnableRHTColQuant? 32: 0;\n  static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant? 1: 0;\n  static int constexpr NumSchedThreads = 32;\n  static int constexpr NumMainloopLoadThreads = 32;\n  static int constexpr NumEpilogueThreads = NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n  TmemAllocator tmem_allocator{};\n  cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n    NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n    cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n\n  // warp assignment\n  bool is_mma_warp = (warp_idx == 0);\n  bool is_dma_warp = (warp_idx == 1);\n  bool is_sched_warp = (warp_idx == 2);\n  bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n  bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n  if (is_epilogue_col_quant_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(c_global_amax));\n  }\n  if (is_epilogue_row_quant_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(a_global_amax));\n  }\n\n  typename MainloopPipeline::Params mainloop_pipeline_params;\n  if (is_dma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  if (is_mma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n  mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n  mainloop_pipeline_params.initializing_warp = 0;\n  mainloop_pipeline_params.num_consumers = NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n  MainloopPipeline mainloop_pipeline(\n    shared_storage.mainloop,\n    mainloop_pipeline_params,\n    cluster_shape,\n    cute::true_type{},  // Perform barrier init\n    cute::true_type{}); // Delay mask calculation\n\n  MainloopPipelineState mainloop_pipe_consumer_state;\n  MainloopPipelineState mainloop_pipe_producer_state = cutlass::make_producer_start_state<MainloopPipeline>();\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, AtomThrShapeMNK>;\n  using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n  AccumulatorPipelineState accumulator_pipe_consumer_state;\n  AccumulatorPipelineState accumulator_pipe_producer_state = cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n  typename AccumulatorPipeline::Params accumulator_pipeline_params;\n  if (is_mma_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n  }\n  if (is_epilogue_col_quant_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n  }\n  // Only one producer thread arrives on this barrier.\n  accumulator_pipeline_params.producer_arv_count = 1;\n  accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n  accumulator_pipeline_params.initializing_warp = 1;\n  using IsInitAccumulatorPipeline = cute::conditional_t<kEnableRHTColQuant, cute::true_type, cute::false_type>;\n  AccumulatorPipeline accumulator_pipeline(\n    shared_storage.accumulator,\n    accumulator_pipeline_params,\n    cluster_shape,\n    IsInitAccumulatorPipeline{},  // Perform barrier init\n    cute::true_type{}); // Delay mask calculation\n  // CLC pipeline\n  typename CLCPipeline::Params clc_pipeline_params;\n  if (is_sched_warp) {\n    clc_pipeline_params.role = CLCPipeline::ThreadCategory::ProducerConsumer;\n  } else {\n    clc_pipeline_params.role = CLCPipeline::ThreadCategory::Consumer;\n  }\n  clc_pipeline_params.producer_blockid = 0;\n  clc_pipeline_params.producer_arv_count = 1;\n  clc_pipeline_params.consumer_arv_count = NumSchedThreads + cluster_size *\n                                                (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n  clc_pipeline_params.transaction_bytes = sizeof(CLCResponse);\n  clc_pipeline_params.initializing_warp = 3;\n  CLCPipeline clc_pipeline(shared_storage.clc, clc_pipeline_params, cluster_shape);\n  CLCPipelineState clc_pipeline_consumer_state;\n  CLCPipelineState clc_pipeline_producer_state = cutlass::make_producer_start_state<CLCPipeline>();\n\n  // CLC throttle pipeline\n  typename CLCThrottlePipeline::Params clc_throttle_pipeline_params;\n  if (is_dma_warp) {\n    clc_throttle_pipeline_params.role = CLCThrottlePipeline::ThreadCategory::Producer;\n  }\n  if (is_sched_warp) {\n    clc_throttle_pipeline_params.role = CLCThrottlePipeline::ThreadCategory::Consumer;\n  }\n  clc_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n  clc_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n  clc_throttle_pipeline_params.dst_blockid = 0;\n  clc_throttle_pipeline_params.initializing_warp = 4;\n\n  CLCThrottlePipeline clc_throttle_pipeline(shared_storage.clc_throttle, clc_throttle_pipeline_params);\n  CLCThrottlePipelineState clc_pipe_throttle_consumer_state;\n  CLCThrottlePipelineState clc_pipe_throttle_producer_state = cutlass::make_producer_start_state<CLCThrottlePipeline>();\n\n  if (warp_idx == 2 && elect_one_sync()) {\n    cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n  }\n  __syncthreads();\n\n  if (is_dma_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    cute::Tensor mA = tma_load_a.get_tma_tensor(make_shape(M,N));\n    cute::Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n    cute::Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_,_, _), Step<_1, X,_1>{});\n    cute::Tensor gB_nk = local_tile(mB, cluster_tile, make_coord(_,_, _), Step< X,_1,_1>{}); // (BLK_N,BLK_K,k)\n\n    cute::Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout); // (MMA,MMA_M,MMA_N,PIPE)\n    cute::Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout); // (MMA,MMA_N,MMA_K,PIPE)\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n    ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n    cute::Tensor tCgA = thr_mma.partition_A(gA_mk);                                                    // (MMA,MMA_M,MMA_K,k)\n    cute::Tensor tCgB = thr_mma.partition_B(gB_nk);                                                    // (MMA,MMA_N,MMA_K,k)\n\n    Layout cta_layout_mnk  = make_layout(cluster_shape);\n    Layout cta_layout_vmnk = tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n    auto cta_coord_vmnk  = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n    auto [tAgA, tAsA] = tma_partition(\n      tma_load_a,\n      get<2>(cta_coord_vmnk),\n      make_layout(size<2>(cta_layout_vmnk)),\n      group_modes<0,3>(tCsA),\n      group_modes<0,3>(tCgA));\n\n    auto [tBgB, tBsB] = tma_partition(\n      tma_load_b,\n      get<1>(cta_coord_vmnk),\n      make_layout(size<1>(cta_layout_vmnk)),\n      group_modes<0,3>(tCsB),\n      group_modes<0,3>(tCgB));\n\n    uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n    uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n    if constexpr (kEnableRHTColQuant) {\n      if (elect_one_sync()) {\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0], kTmaRhtTensorTransactionBytes);\n        copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_,0,0), tBsB(_,0));\n      }\n    }\n\n    do {\n      bool is_first_wave = scheduler.is_first_wave();\n      uint32_t skip_wait = is_first_wave;\n      auto tAgA_mk = tAgA(_,scheduler.tile_m(),_);\n      int k_tile = 0;\n      // Throttle CLC producer\n      clc_throttle_pipeline.producer_acquire(clc_pipe_throttle_producer_state);\n      clc_throttle_pipeline.producer_commit(clc_pipe_throttle_producer_state);\n      ++clc_pipe_throttle_producer_state;\n\n      CUTLASS_PRAGMA_NO_UNROLL\n      while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n\n        int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n        ++k_tile;\n        skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n        mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n        using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n        BarrierType* tma_barrier = mainloop_pipeline.producer_get_barrier(\n          mainloop_pipe_producer_state);\n        int write_stage = mainloop_pipe_producer_state.index();\n        ++mainloop_pipe_producer_state;\n        if (cute::elect_one_sync()) {\n          copy(\n            tma_load_a.with(*tma_barrier, tma_mcast_mask_a),\n            tAgA_mk(_,k_tile_idx_n),\n            tAsA(_,write_stage));\n        }\n      }\n      scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n      ++clc_pipeline_consumer_state;\n      scheduler.update_work_tile_info();\n    } while (scheduler.is_valid());\n    mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n  } else if (is_mma_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    if constexpr (kEnableRHTColQuant) {\n      cute::Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout); // (MMA,MMA_M,MMA_N,PIPE)\n      cute::Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout); // (MMA,MMA_N,MMA_K,PIPE)\n\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n      // Allocate \"fragments\" -- these are actually umma smem descriptors\n      cute::Tensor tCrA = thr_mma.make_fragment_A(tCsA);                                              // (MMA,MMA_M,MMA_K,PIPE)\n      cute::Tensor tCrB = thr_mma.make_fragment_B(tCsB);                                              // (MMA,MMA_M,MMA_K,PIPE)\n\n      mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n      tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n      __syncwarp();\n      tmem_allocation_result_barrier.arrive();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_mma.data() = tmem_base_ptr;\n      cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n\n        auto barrier_token = mainloop_pipeline.consumer_try_wait(\n          mainloop_pipe_consumer_state,\n          skip_wait);\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ) {\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          int read_stage = mainloop_pipe_consumer_state.index();\n          auto tCrA_mk = tCrA(_,_,_,read_stage);\n          auto tCrB_nk = tCrB(_,_,0,0);\n          CUTLASS_PRAGMA_UNROLL\n          for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block)\n          {\n            int accumulator_k_block = accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n            int tCrA_k_block = k_block * EpilogueUnrollFactor;\n            accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n            CUTLASS_PRAGMA_UNROLL\n            for (int i = 0; i < EpilogueUnrollFactor; i++) {\n              auto accumulators = bulk_tmem_mma(_,_,_,accumulator_k_block + i);\n              gemm(mma, tCrA_mk(_,_,tCrA_k_block + i), tCrB_nk, accumulators);\n            }\n\n            accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n            ++accumulator_pipe_producer_state;\n          }\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n          barrier_token = mainloop_pipeline.consumer_try_wait(\n            mainloop_pipe_consumer_state,\n            skip_wait);\n        }\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n      tmem_allocator.release_allocation_lock();\n      accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n      tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n    }\n  } else if(is_sched_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    do {\n      clc_throttle_pipeline.consumer_wait(clc_pipe_throttle_consumer_state);\n      clc_throttle_pipeline.consumer_release(clc_pipe_throttle_consumer_state);\n      ++clc_pipe_throttle_consumer_state;\n      clc_pipeline_producer_state = scheduler.advance_to_next_work(clc_pipeline, clc_pipeline_producer_state);\n      scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n      ++clc_pipeline_consumer_state;\n      scheduler.update_work_tile_info();\n    } while (scheduler.is_valid());\n  } else if (is_epilogue_col_quant_warp) {\n    cutlass::arch::warpgroup_reg_alloc<192>();\n    if constexpr (kEnableRHTColQuant) {\n      using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n      float const c_global_amax_val = *c_global_amax;\n      auto acc_epilogue_pipelined_shape = append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n      auto bulk_tmem_epilogue_layout = make_layout(\n        acc_epilogue_pipelined_shape,\n        make_stride(\n          stride<0>(bulk_tmem_mma),\n          Int<0>{},\n          Int<0>{},\n          size<1>(epilogue_tiler)));\n      auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n      // leveraging 256-bit writes to global memory\n      static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n      tmem_allocation_result_barrier.arrive_and_wait();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_epilogue.data() = tmem_base_ptr;\n      int global_thread_idx = threadIdx.x;\n      int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n\n      size_t rng_seed = 0;\n      size_t rng_offset = 0;\n      if constexpr (kEnableStochasticRounding) {\n        rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n        rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n      }\n\n      cute::Tensor mD = make_tensor(\n        cute::subbyte_iterator<TD>(D),\n        make_shape(M,N),\n        dD); // (M,N)\n      cute::Tensor gD_mn = local_tile(\n        mD,\n        epilogue_tiler,\n        make_coord(_,_, _),\n        Step<_1,_1, X>{}); // (BLK_M,BLK_N)\n      cute::Tensor pD = make_identity_tensor(mD.shape());\n      cute::Tensor pD_mn = local_tile(\n        pD,\n        epilogue_tiler,\n        make_coord(_,_, _),\n        Step<_1,_1, X>{}); // (BLK_M,BLK_N)\n      cute::Tensor mSFD = make_tensor(make_gmem_ptr(SFD), sfd_layout);\n      cute::Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});           // (BLK_M,BLK_N)\n      cute::Tensor pSFD = make_identity_tensor(mSFD.shape());\n      cute::Tensor pSFD_mn = local_tile(pSFD, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});           // (BLK_M,BLK_N)\n\n      cute::Tensor gD_mn_view = tiled_divide(gD_mn, take<0,2>(epilogue_tiler));\n      cute::Tensor pD_mn_view = tiled_divide(pD_mn, take<0,2>(epilogue_tiler));\n      auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_,_,_,_0{}));\n      auto tiled_r2g = make_tiled_copy_D(\n        Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{},\n        tiled_t2r);\n      auto thr_t2r   = tiled_t2r.get_slice(local_thread_idx);\n      auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n      // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp8_max = 448.0f;\n      float const fp4_max_inv = 1.0f / fp4_max;\n      float const global_encode_scale = c_global_amax_val > 0.0f\n        ? cutlass::minimum_with_nan_propagation<float>{}(\n          (fp8_max * fp4_max) / c_global_amax_val,\n          cutlass::platform::numeric_limits<float>::max())\n        : 1.0f;\n\n      float const global_decode_scale = 1.0f / global_encode_scale;\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n      auto sfc_converter = cutlass::NumericConverter<TSFD, float>{};\n\n      do {\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ++k_tile) {\n          cute::Tensor tDgD_mn = gD_mn_view(_,_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDgSFD_mn = gSFD_mn(_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDpD_mn = pD_mn_view(_,_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDpSFD_mn = pSFD_mn(_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n\n          accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n          auto Acc = bulk_tmem_epilogue(_,_,_,accumulator_pipe_consumer_state.index());\n          cute::Tensor tDtAcc = thr_t2r.partition_S(Acc); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDgD = thr_t2r.partition_D(tDgD_mn); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDpD = thr_t2r.partition_D(tDpD_mn); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tTR_rAcc = make_tensor<ElementAccumulator>(shape(tDgD)); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDrD = make_tensor<TD>(shape(tDgD));\n          cute::Tensor tTR_rAcc_frag = recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n          cute::Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n          cute::Tensor src = thr_r2g.retile_S(tDrD);\n          cute::Tensor dst = thr_r2g.retile_D(tDgD);\n          cute::Tensor pSrc = thr_r2g.retile_D(tDpD);\n\n          cute::Tensor tDgSFD_view = make_tensor(\n            tDgSFD_mn.data(),\n              make_layout(\n                make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n          cute::Tensor tDpSFD_view = make_tensor(\n            tDpSFD_mn.data(),\n              make_layout(\n                make_shape(shape(tDpSFD_mn), Int<1>{}, Int<1>{}),\n                make_stride(stride(tDpSFD_mn), Int<0>{}, Int<0>{})));\n          cute::Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n          cute::Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n          cute::Tensor tDpSFD = filter(thr_t2r.partition_D(tDpSFD_view));\n          static int constexpr NumVecs = size(tDgD) / VectorSize;\n          cute::Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n          cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n          cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n          // TMEM_LOAD\n          copy(tiled_t2r, tDtAcc, tTR_rAcc);\n          cutlass::arch::fence_view_async_tmem_load();\n          accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n          ++accumulator_pipe_consumer_state;\n\n          if constexpr (!kUseFastMath) {\n            // Downcast to BF16 for bit-wise compatibility with\n            // unfused kernels\n            auto convert_accum_to_bf16 =\n                cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                               FragmentSize>{};\n            auto convert_bf16_to_accum =\n                cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                               FragmentSize>{};\n            tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n            tTR_rAcc_frag(_1{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n          }\n\n          auto compute_frgs = reinterpret_cast<cutlass::Array< ElementAccumulator, VectorSize> *>(tTR_rAcc_frag.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array< TD, VectorSize> *>(tDrD_frag.data());\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n          }\n\n          pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n            vec_maxs, global_encode_scale_multiplier);\n          auto pvscales_cvted = cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n          tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n          auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(tD_rRowSFD_frg(_0{}));\n          auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n            qpvscale_ups,\n            global_decode_scale);\n\n          cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n          if constexpr (kUseFastMath) {\n            // fast math: use reciprocal approximate to replace div\n            acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n          } else {\n            // regular path for slower math, use divide to replace div\n            acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(1.0, qpvscale_scaled);\n          }\n\n          uint4 random_uint4 = uint4{0, 0, 0, 0};\n          transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n          // \"Prefetch\" a stochastic rounding state for the first tile\n          if constexpr (kEnableStochasticRounding) {\n            const size_t rng_sequence = global_thread_idx + k_tile * 512 + scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n            rng.init(rng_seed, rng_sequence, rng_offset);\n          }\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n              acc_scales[v],\n              cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs[v], acc_scale), *reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n              cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                compute_frgs[v],\n                acc_scale));\n            }\n\n          }\n\n          cute::Tensor pred_pSrc = cute::lazy::transform(make_tensor(counting_iterator<int>{}, replace<0>(shape(dst), _1{})), [&](auto coord){\n            cute::Tensor pSrc_view = group_modes<1,rank(pSrc)>(pSrc);\n            return elem_less(pSrc_view(_0{},coord), shape(mD));\n          });\n          copy_if(tiled_r2g, pred_pSrc, src, dst);\n          // 32bit vectorization copy 4 e4m3 SFD for per 64 or(16,4):(0, 1) element\n\n          constexpr int vec_len = 32 / sizeof_bits_v<TSFD>;\n          cute::Tensor  tDrSFD_v = recast<uint_bit_t<32>>(tDrSFD);\n          cute::Tensor  tDgSFD_v = recast<uint_bit_t<32>>(tDgSFD);\n          copy_if(\n                  [&](auto coord){\n                    cute::Tensor tDpSFD_view = group_modes<1,rank(tDpSFD)>(tDpSFD);\n                    return elem_less(tDpSFD_view(_0{}, coord * vec_len), shape(mSFD));\n                  },\n                  tDrSFD_v, tDgSFD_v);\n        }\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    }\n  } else if (is_epilogue_row_quant_warp) {\n    cutlass::arch::warpgroup_reg_alloc<136>();\n    if constexpr (kEnableRowQuant) {\n      using S2RVectorType = uint128_t;\n      float const a_global_amax_val = *a_global_amax;\n      int global_thread_idx = threadIdx.x;\n      int local_thread_idx = global_thread_idx % 256;\n      size_t rng_seed = 0;\n      size_t rng_offset = 0;\n      if constexpr (kEnableStochasticRounding) {\n        rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n        rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n      }\n      cute::Tensor mQA = make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, N), dQA));\n      cute::Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n      cute::Tensor pQA = make_identity_tensor(mQA.shape());\n      cute::Tensor pQA_mn = local_tile(pQA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n\n      cute::Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n      cute::Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});           // (BLK_M,BLK_N)\n      cute::Tensor pSFA = make_identity_tensor(mSFA.shape());\n      cute::Tensor pSFA_mn = local_tile(pSFA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n      cute::Tensor sA = as_position_independent_swizzle_tensor(\n                    group_modes<0,2>(coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout)))); // (BLOCK_M, BLOCK_M,PIPE)\n      using S2RWarpLayout = Layout<Shape<_2,_16>>;\n      using WarpGroupLayout = Layout<Shape<_1,_8>>;\n      using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n      using S2RValLayout = Layout<Shape<Int<64>, _1>>;\n      using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n      using R2GAtomQA = Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TQA>;\n\n      auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n      auto tiled_r2g_QA = make_tiled_copy_D(R2GAtomQA{}, tiled_s2r);\n\n      auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n      auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n\n      cute::Tensor tQAsA = thr_s2r.partition_S(sA); // (Copy, Copy_M, Copy_N, PIPE)\n\n      cute::Tensor tQArA = make_tensor_like<TA>(make_layout(tQAsA(_, _, _, _0{}).shape())); // (Copy, Copy_M, Copy_N)\n      // Tensor tQArA_PI = thr_s2r.partition_S(sA_PI);\n      cute::Tensor tQAgQA = thr_r2g_QA.partition_D(gQA_mn);\n      cute::Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n      cute::Tensor tQApQA = thr_r2g_QA.partition_D(pQA_mn);\n\n      cute::Tensor tQAgSFA = thr_s2r.partition_D(gSFA_mn);\n      cute::Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n      cute::Tensor tQApSFA = thr_s2r.partition_D(pSFA_mn);\n\n      // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp8_max = 448.0f;\n      float const fp4_max_inv = 1.0f / fp4_max;\n      float const global_encode_scale = a_global_amax_val > 0.0f\n        ? cutlass::minimum_with_nan_propagation<float>{}(\n          (fp8_max * fp4_max) / a_global_amax_val,\n          cutlass::platform::numeric_limits<float>::max())\n        : 1.0f;\n\n      float const global_decode_scale = 1.0f / global_encode_scale;\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n      auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ) {\n          auto tQAgSFA_mn = tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQApSFA_mn = tQApSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQApQA_mn = tQApQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto barrier_token = mainloop_pipeline.consumer_try_wait(\n            mainloop_pipe_consumer_state);\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n          cutlass::arch::fence_view_async_shared();\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n          // static int constexpr NumVecs = size(tQArA) / VectorSize;\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n          auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(raw_pointer_cast(tQArQA.data()));\n          transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n          if constexpr (kEnableStochasticRounding) {\n            const size_t rng_sequence = global_thread_idx + k_tile * 512 + scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n            rng.init(rng_seed, rng_sequence, rng_offset);\n          }\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < size(tQArA)/VectorSize; v++) {\n            auto compute_frgs_up = cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(compute_frgs[v]);\n            auto amax = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n            // declare pvscales\n            ElementAccumulator pvscales;\n            pvscales = cutlass::multiplies<ElementAccumulator>{}(amax, global_encode_scale_multiplier);\n            filter(tQArSFA)(v) = sfa_converter(pvscales);\n            auto qpvscale_ups = cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n            auto qpvscale_scaled = cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n            ElementAccumulator acc_scales;\n            if constexpr (kUseFastMath) {\n              // fast math: use reciprocal approximate to replace div\n              acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // regular path for slower math, use divide to replace div\n              acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n            }\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n              acc_scales,\n              cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs_up, acc_scale), *reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                  compute_frgs_up,\n                  acc_scale));\n            }\n          }\n\n          cute::Tensor pred_tQApQA = cute::lazy::transform(make_tensor(counting_iterator<int>{}, replace<0>(shape(tQAgQA_mn), _1{})), [&](auto coord){\n            cute::Tensor tQApQA_view = group_modes<1,rank(tQApQA_mn)>(tQApQA_mn);\n            return elem_less(tQApQA_view(_0{}, coord), shape(mQA));\n          });\n          copy_if(tiled_r2g_QA, pred_tQApQA, tQArQA, tQAgQA_mn);\n          // 32bit vectorization copy 4 e4m3 SFA for per 64 or (16,4):(0, 1)  element\n          constexpr int vec_len = 32 / sizeof_bits_v<TSFD>;\n          cute::Tensor  tQArSFA_v = recast<uint_bit_t<32>>(filter(tQArSFA));\n          cute::Tensor  tQAgSFA_v = recast<uint_bit_t<32>>(filter(tQAgSFA_mn));\n          copy_if(\n                  [&](auto coord){\n                    cute::Tensor tQApSFA_view = filter(tQApSFA_mn);\n                    return elem_less(tQApSFA_view(_0{}, coord * vec_len), shape(mSFA));\n                  },\n                  tQArSFA_v, tQAgSFA_v);\n        }\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      }while (scheduler.is_valid());\n    }\n  } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n  }\n  } // sm100 compile guard end\n} // NOLINT(readability/fn_size)\n\n\n// this function computes RHT-GEMM for\n// m = hidden_size, n = sequence_length\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// D: m x n: row-major\n// SFD: m x (n/16): row-major\n// QA: m x n: col-major\n// SFA: m/16 x n: col-major\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant, bool kEnableSwizzleSFOutput,\nclass TA, class TB, class TD, class TSFD, class TQA, class TSFA, bool kUseFastMath=true>\nvoid row_col_rht_gemm_ntt_w_sfc(\n    int sequence_length,\n    int hidden_size,\n    TA const* A,\n    TB const* B,\n    TD* D,\n    TSFD* SFD,\n    TQA* QA,\n    TSFA* SFA,\n    float const* a_global_amax,\n    float const* d_global_amax,\n    const size_t* rng_state,\n    uint32_t sm_count,\n    cudaStream_t stream,\n    int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0), make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFCLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)), make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom = cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom = cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(SwizzledSFALayoutAtom{}, make_shape(hidden_size,sequence_length), Step<_1,_2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(hidden_size,sequence_length), Step<_2,_1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFCLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFCLayout>;\n  SFALayout sfa_layout;\n  SFCLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{}, make_shape(hidden_size, sequence_length), Step<_1,_2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(hidden_size, sequence_length), Step<_2,_1>{});\n  } else {\n    sfa_layout = make_layout(make_shape(make_shape(Int<SFVecSize>{}, hidden_size/SFVecSize), sequence_length), make_stride(make_stride(_0{}, _1{}), hidden_size/SFVecSize));\n    sfd_layout = make_layout(make_shape(hidden_size, make_shape(Int<SFVecSize>{}, sequence_length/SFVecSize)), make_stride(sequence_length/SFVecSize, make_stride(_0{}, _1{})));\n  }\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = sequence_length;\n  cute::Tensor tensorA = make_tensor(A, make_shape(hidden_size, sequence_length), LayoutLeft{});\n  cute::Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  cute::Tensor tensorD = make_tensor(D, make_shape(hidden_size, sequence_length), LayoutRight{});\n  cute::Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, sequence_length), LayoutLeft{});\n  cute::Tensor tensorSFD = make_tensor(SFD, sfd_layout);\n  cute::Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);   // (dM,dK)\n  auto dB = stride(tensorB);   // (dN,dK)\n  auto dD = stride(tensorD);   // (dM,dN)\n  auto dQA = stride(tensorQA); // (dM,dK)\n  using ClusterShape = Shape<  _1,  _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128,Int<RhtTensorSize>,Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128,Int<RhtTensorSize>,_128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n    size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, float,\n                                               size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B = partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(shape<0>(cluster_tile_shape), shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(shape<1>(cluster_tile_shape), shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TB, SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A = decltype(shape_div(shape<0>(cluster_tile_mainloop), shape_div(shape<0>(cluster_tile_mainloop), size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem /\n      (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 6;\n  static int constexpr MainloopPipelineBytes = sizeof(typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n                                                1,\n                                                Shape<_1,_1,_1>,\n                                                Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr ClcResponseBytes = sizeof(CLCResponse) * SchedulerPipelineStageCount;\n  static int constexpr CLCThrottlePipelineBytes = sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr CLCPipelineBytes = sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>::SharedStorage);\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448; // 232KB in bytes\n  static int constexpr kBytesPerStage =\n    cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = ClcResponseBytes + CLCThrottlePipelineBytes + TmemBasePtrsBytes +\n                                    CLCPipelineBytes + TmemDeallocBytes+BTensorBytes + AccPipelineBytes; // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};      // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(\n    SmemLayoutAtomA{},\n    append(mma_shape_A, sP), Step<_2,_1,_3>{}); // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(\n    SmemLayoutAtomB{},\n    append(mma_shape_B, _1{})); // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};  // XXX Dummy\n\n  auto tma_load_a = make_tma_copy_A_sm100(\n    SM90_TMA_LOAD{},\n    tensorA,\n    sA(_,_,_,0),\n    cluster_tile_mainloop,\n    mma);\n  auto tma_load_b = make_tma_copy_B_sm100(\n    SM90_TMA_LOAD{},\n    tensorB,\n    sB(_,_,_,0),\n    cluster_tile_shape,\n    mma);\n\n  // Assert checks problem size should be multiple of 64\n  NVTE_CHECK(M % 64 == 0, \"M must be a multiple of 64, but got \", M);\n  NVTE_CHECK(N % 64 == 0, \"N must be a multiple of 64, but got \", N);\n\n  uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile_shape))));\n  uint32_t tiles_in_n = uint32_t(size(ceil_div(N, k_tile_size)));\n  uint32_t tiles = tiles_in_m * tiles_in_n;\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(tiles_in_m, tiles_in_n, 1);\n\n  int smem_size = sizeof(\n    SharedStorage<\n    TA,\n    TB,\n    decltype(sA),\n    decltype(sB),\n    ClusterShape,\n    AccumulatorPipelineStageCount,\n    EpilogueUnrollFactor,\n    SchedulerPipelineStageCount>);\n\n  auto* kernel_ptr = &row_col_rht_gemm_device<\n    decltype(M), decltype(N), decltype(k_tile_size),\n    decltype(cluster_shape), decltype(cluster_tile_shape),\n    TA, decltype(dA), decltype(sA), decltype(tma_load_a),\n    TB, decltype(dB), decltype(sB), decltype(tma_load_b),\n    TD, decltype(dD), decltype(sD),\n    TSFD, decltype(sfd_layout),\n    TQA, decltype(dQA),\n    TSFA, decltype(sfa_layout),\n    decltype(mma),\n    AccumulatorPipelineStageCount,\n    SchedulerPipelineStageCount,\n    kEnableStochasticRounding,\n    kEnableRHTColQuant,\n    kEnableRowQuant,\n    kUseFastMath>;\n\n  NVTE_CHECK_CUDA(cudaFuncSetAttribute(*kernel_ptr, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n     params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape,\n     tensorA.data(), dA, sA, tma_load_a,\n     tensorB.data(), dB, sB, tma_load_b,\n     tensorD.data(), dD, sD,\n     tensorSFD.data(), sfd_layout,\n     tensorQA.data(), dQA,\n     tensorSFA.data(), sfa_layout,\n     mma, a_global_amax, d_global_amax, rng_state);\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n\n}\n\n}  // namespace\n}  // namespace detail\n\n// clang-format on\n\nvoid hadamard_transform_cast_fusion(const Tensor &input_, Tensor &output_,\n                                    const Tensor &hadamard_matrix_, QuantizationConfig quant_config,\n                                    cudaStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_cast_fusion);\n\n  // Check input and output tensors\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  // rowwise cast and columnwise cast has different output data pointers\n  bool has_rowwise_quant = false;\n  bool has_columnwise_quant = false;\n  void *rowwise_data_ptr = nullptr;\n  void *rowwise_scale_inv_ptr = nullptr;\n  void *rowwise_amax_ptr = nullptr;\n  void *columnwise_data_ptr = nullptr;\n  void *columnwise_scale_inv_ptr = nullptr;\n  void *columnwise_amax_ptr = nullptr;\n\n  // examine the output tensor (single tensor for dense)\n  if (output_.data.dptr != nullptr) {\n    has_rowwise_quant = true;\n    rowwise_data_ptr = output_.data.dptr;\n    rowwise_scale_inv_ptr = output_.scale_inv.dptr;\n    rowwise_amax_ptr = output_.amax.dptr;\n  }\n\n  if (output_.columnwise_data.dptr != nullptr) {\n    has_columnwise_quant = true;\n    columnwise_data_ptr = output_.columnwise_data.dptr;\n    columnwise_scale_inv_ptr = output_.columnwise_scale_inv.dptr;\n    columnwise_amax_ptr = output_.columnwise_amax.dptr;\n  }\n\n  NVTE_CHECK(has_rowwise_quant || has_columnwise_quant,\n             \"Output tensor must have rowwise or columnwise quant.\");\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(hadamard_matrix_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Hadamard matrix must be BF16 tensor, but scaling mode is \",\n             to_string(hadamard_matrix_.scaling_mode), \".\");\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  // TODO: add support for swizzle sf output\n  const bool use_swizzle_sf_output = false;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          has_columnwise_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              has_rowwise_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::row_col_rht_gemm_ntt_w_sfc<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TD, TSFD, TQA, TSFA, kUseFastMath>(\n                            /*sequence_length=*/m, /*hidden_size=*/n,\n                            /*A=*/reinterpret_cast<TA const *>(input.dptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*D=*/reinterpret_cast<TD *>(columnwise_data_ptr),\n                            /*SFD=*/reinterpret_cast<TSFD *>(columnwise_scale_inv_ptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_ptr),\n                            /*a_global_amax=*/reinterpret_cast<float const *>(rowwise_amax_ptr),\n                            /*d_global_amax=*/reinterpret_cast<float const *>(columnwise_amax_ptr),\n                            /*rng_state=*/rng_state, /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_quantize_with_hadamard_transform(const NVTETensor input, NVTETensor output,\n                                           const NVTETensor hadamard_matrix,\n                                           const NVTEQuantizationConfig quant_config,\n                                           cudaStream_t stream) {\n  NVTE_API_CALL(nvte_quantize_with_hadamard_transform);\n  using namespace transformer_engine;\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n  hadamard_transform_cast_fusion(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                                 *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n                                 stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\n// clang-format off\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\nstruct CLCResponse { uint32_t data[4] = {0}; };\n\nconstexpr int kFp4ConvertChunkElements = 8;\nconstexpr int kFp4ConvertFullElements = 16;\nconstexpr int kFp4RbitsPerChunk = 2;\nconstexpr int kFp4ChunkCount = kFp4ConvertFullElements / kFp4ConvertChunkElements;\n\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> StochasticNumericConverterBase(\n    cutlass::Array<float, kFp4ConvertChunkElements> const &input,\n    cutlass::Array<uint32_t, kFp4RbitsPerChunk> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, kFp4ConvertFullElements>\nStochasticNumericConverter(cutlass::Array<float, kFp4ConvertFullElements> const &input,\n                           cutlass::Array<uint32_t, kFp4RbitsPerChunk * kFp4ChunkCount> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertFullElements>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, kFp4ConvertChunkElements> *>(&output);\n  cutlass::Array<float, kFp4ConvertChunkElements> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, kFp4ConvertChunkElements> const *>(&input);\n  cutlass::Array<uint32_t, kFp4RbitsPerChunk> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, kFp4RbitsPerChunk> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < kFp4ChunkCount; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <\n  class ElementA,\n  class ElementB,\n  class ASmemLayout,\n  class BSmemLayout,\n  class ClusterShape,\n  int AccumulatorPipelineStageCount_,\n  int EpilogueUnrollFactor_,\n  int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n    MainloopPipelineStageCount,\n    Shape<_1,_1,_1>,\n    AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using CLCPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using CLCPipelineStorage = typename CLCPipeline::SharedStorage;\n  using CLCThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using CLCThrottlePipelineStorage = typename CLCThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) CLCPipelineStorage clc;\n  alignas(16) CLCThrottlePipelineStorage clc_throttle;\n  alignas(16) CLCResponse clc_response[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA,\n          class TB, class BStride, class BSmemLayout, class TmaLoadB,\n          class TD, class DStride, class DSmemLayout,\n          class TSFD, class TSFDLayout,\n          class TQA, class QAStride,\n          class TSFA, class TSFALayout,\n          class TiledMMA,\n          int AccumulatorPipelineStageCount_,\n          int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false,\n          bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true,\n          bool kUseFastMath_ = true>\n__launch_bounds__(512, 1)\n__global__ static void row_col_rht_gemm_device(\n    MShape M,\n    NShape N,\n    KShape K,\n    ClusterShape cluster_shape,\n    ClusterTileShape cluster_tile,\n    TA const* A,\n    AStride dA,\n    ASmemLayout sAlayout,\n    CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const* B,\n    BStride dB,\n    BSmemLayout sBlayout,\n    CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TD* D,\n    DStride dD,\n    DSmemLayout,\n    TSFD* SFD,\n    TSFDLayout sfd_layout,\n    TQA* QA,\n    QAStride dQA,\n    TSFA* SFA,\n    TSFALayout sfa_layout,\n    TiledMMA mma,\n    float const* a_global_amax,\n    float const* c_global_amax,\n    const size_t* rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                \"row_col_rht_gemm_device must generate row-wise \"\n                \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n  CUTLASS_NOT_IMPLEMENTED();\n  return;\n#endif\n\n  using X = Underscore;\n  // static constexpr bool kApplyStochasticRounding = true;\n  using ElementAccumulator = float;\n  static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n  using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n  static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n      size(AtomThrShapeMNK{}) * cosize(take<0,3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n  static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n  static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n  static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n  static constexpr bool kUseFastMath = kUseFastMath_;\n  static int constexpr RhtTensorSize = 16;\n  static int constexpr kTmaRhtTensorTransactionBytes = cutlass::bits_to_bytes(\n      RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n    MainloopPipelineStageCount,\n    ClusterShape,\n    AtomThrShapeMNK>;\n  using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n  using CLCPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n  using CLCPipelineState = typename CLCPipeline::PipelineState;\n  using CLCThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n  using CLCThrottlePipelineState = typename CLCThrottlePipeline::PipelineState;\n\n  static_assert(ClusterShape{} == Shape<_1,_1,_1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  static int constexpr VectorSize = RhtTensorSize;\n  // Preconditions\n  CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n  auto cluster_size = size<0>(cluster_shape);\n  auto mainloop_tiler = Shape<_128,_16,_128>{};\n  auto epilogue_tiler = Shape<_128,_128,_128>{};\n\n  static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n  // Get the appropriate blocks for this Cluster\n  dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n  // Total number of k-tiles\n  int const K_TILE_MAX = ceil_div(min(N, K), size<2>(epilogue_tiler));\n\n  struct TileScheduler {\n    struct WorkTileInfo {\n      uint32_t m_idx = 0;\n      uint32_t n_idx = 0;\n      uint32_t l_idx = 0;\n      bool is_valid_tile = false;\n    };\n    uint32_t tiles_in_m = 0;\n    uint32_t tiles_in_n = 0;\n\n    int k_tile_max = 0;\n\n    int wave_cnt = 0;\n    WorkTileInfo work_tile_info;\n    WorkTileInfo next_work_tile_info;\n    CLCResponse* clc_response_ptr_;\n    CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax, CLCResponse* clc_response_ptr)\n      : tiles_in_m(tiles_m),\n        tiles_in_n(tiles_n),\n\n        k_tile_max(kmax),\n        work_tile_info({blockIdx.x, blockIdx.y, blockIdx.z, blockIdx.x<tiles_m && blockIdx.y<tiles_n}),\n        next_work_tile_info({blockIdx.x, blockIdx.y, blockIdx.z, blockIdx.x<tiles_m && blockIdx.y<tiles_n}),\n        clc_response_ptr_(clc_response_ptr) {}\n\n    CUTLASS_DEVICE uint32_t tile_m() const {\n      return work_tile_info.m_idx;\n    }\n    CUTLASS_DEVICE uint32_t tile_n_base() const {\n      return work_tile_info.n_idx * uint32_t(k_tile_max);\n    }\n\n    CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n    CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n    CUTLASS_DEVICE bool is_valid() const {\n      return cute::elem_less(cute::make_coord(work_tile_info.m_idx, work_tile_info.n_idx), cute::make_coord(tiles_in_m, tiles_in_n)) && work_tile_info.is_valid_tile;\n    }\n    CUTLASS_DEVICE bool is_first_wave() const { return wave_cnt == 0; }\n    CUTLASS_DEVICE auto advance_to_next_work(CLCPipeline& clc_pipeline, CLCPipelineState clc_pipe_producer_state) {\n      uint32_t mbarrier_addr = clc_pipeline.producer_get_barrier(clc_pipe_producer_state);\n      // Wait for clcID buffer to become empty with a flipped phase\n      clc_pipeline.producer_acquire(clc_pipe_producer_state);\n\n      if (cute::elect_one_sync()) {\n        issue_clc_query(clc_pipe_producer_state, mbarrier_addr, clc_response_ptr_);\n      }\n\n      ++clc_pipe_producer_state;\n      return clc_pipe_producer_state;\n    }\n\n    CUTLASS_DEVICE auto fetch_next_work(CLCPipeline& clc_pipeline, CLCPipelineState clc_pipe_producer_state) {\n      clc_pipeline.consumer_wait(clc_pipe_producer_state);\n      uint32_t smem_addr = cute::cast_smem_ptr_to_uint(&clc_response_ptr_[clc_pipe_producer_state.index()]);\n      next_work_tile_info = work_tile_info_from_clc_response(smem_addr);\n      clc_pipeline.consumer_release(clc_pipe_producer_state);\n      wave_cnt++;\n      return;\n    }\n\n    CUTLASS_DEVICE auto update_work_tile_info() {\n      work_tile_info = next_work_tile_info;\n      return;\n    }\n\n    CUTLASS_DEVICE uint32_t get_linear_tile_idx() const {\n      return work_tile_info.m_idx + work_tile_info.n_idx * tiles_in_m;\n    }\n\n    CUTLASS_HOST_DEVICE\n    static void\n    issue_clc_query(CLCPipelineState state, uint32_t mbarrier_addr, CLCResponse* clc_response_ptr) {\n    #if defined(CUTLASS_ARCH_CLC_ENABLED)\n        uint32_t result_addr = cute::cast_smem_ptr_to_uint(reinterpret_cast<const void*>(\n              &clc_response_ptr[state.index()]));\n        asm volatile(\n          \"{\\n\\t\"\n          \"clusterlaunchcontrol.try_cancel.async.shared::cta.mbarrier::complete_tx::bytes.multicast::cluster::all.b128 [%0], [%1];\\n\\t\"\n          \"}\\n\"\n          :\n          : \"r\"(result_addr), \"r\"(mbarrier_addr));\n    #else\n        CUTLASS_NOT_IMPLEMENTED();\n    #endif\n    }\n  CUTLASS_DEVICE\n  static WorkTileInfo\n  work_tile_info_from_clc_response(uint32_t result_addr) {\n    WorkTileInfo work_tile_info;\n    uint32_t valid = 0;\n    #if defined(CUTLASS_ARCH_CLC_ENABLED)\n      asm volatile(\n        \"{\\n\"\n        \".reg .pred p1;\\n\\t\"\n        \".reg .b128 clc_result;\\n\\t\"\n        \"ld.shared.b128 clc_result, [%4];\\n\\t\"\n        \"clusterlaunchcontrol.query_cancel.is_canceled.pred.b128 p1, clc_result;\\n\\t\"\n        \"selp.u32 %3, 1, 0, p1;\\n\\t\"\n        \"@p1 clusterlaunchcontrol.query_cancel.get_first_ctaid.v4.b32.b128 {%0, %1, %2, _}, clc_result;\\n\\t\"\n        \"}\\n\"\n        : \"=r\"(work_tile_info.m_idx), \"=r\"(work_tile_info.n_idx), \"=r\"(work_tile_info.l_idx), \"=r\"(valid)\n        : \"r\"(result_addr)\n        : \"memory\"\n      );\n\n      cutlass::arch::fence_view_async_shared();\n    #else\n      CUTLASS_NOT_IMPLEMENTED();\n    #endif\n    work_tile_info.is_valid_tile = (valid == 1);\n    return work_tile_info;\n  }\n  };\n\n\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount, EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n  uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n  uint32_t tiles_in_n = uint32_t(size(ceil_div(N, size<2>(epilogue_tiler))));\n  TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, shared_storage.clc_response);\n\n  int block_rank_in_cluster = cute::block_rank_in_cluster();\n  auto acc_shape_mma = make_shape(take<0,2>(mainloop_tiler), _1{}, _1{});\n  auto acc_shape_epilogue = make_shape(take<0,2>(epilogue_tiler), _1{}, _1{});\n\n  auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n  auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n  static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n  static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n  static int constexpr NumMmaThreadCount = kEnableRHTColQuant? 32: 0;\n  static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant? 1: 0;\n  static int constexpr NumSchedThreads = 32;\n  static int constexpr NumMainloopLoadThreads = 32;\n  static int constexpr NumEpilogueThreads = NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n  TmemAllocator tmem_allocator{};\n  cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n    NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n    cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n\n  // warp assignment\n  bool is_mma_warp = (warp_idx == 0);\n  bool is_dma_warp = (warp_idx == 1);\n  bool is_sched_warp = (warp_idx == 2);\n  bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n  bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n  if (is_epilogue_col_quant_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(c_global_amax));\n  }\n  if (is_epilogue_row_quant_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(a_global_amax));\n  }\n\n  typename MainloopPipeline::Params mainloop_pipeline_params;\n  if (is_dma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  if (is_mma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n  mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n  mainloop_pipeline_params.initializing_warp = 0;\n  mainloop_pipeline_params.num_consumers = NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n  MainloopPipeline mainloop_pipeline(\n    shared_storage.mainloop,\n    mainloop_pipeline_params,\n    cluster_shape,\n    cute::true_type{},  // Perform barrier init\n    cute::true_type{}); // Delay mask calculation\n\n  MainloopPipelineState mainloop_pipe_consumer_state;\n  MainloopPipelineState mainloop_pipe_producer_state = cutlass::make_producer_start_state<MainloopPipeline>();\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, AtomThrShapeMNK>;\n  using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n  AccumulatorPipelineState accumulator_pipe_consumer_state;\n  AccumulatorPipelineState accumulator_pipe_producer_state = cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n  typename AccumulatorPipeline::Params accumulator_pipeline_params;\n  if (is_mma_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n  }\n  if (is_epilogue_col_quant_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n  }\n  // Only one producer thread arrives on this barrier.\n  accumulator_pipeline_params.producer_arv_count = 1;\n  accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n  accumulator_pipeline_params.initializing_warp = 1;\n  using IsInitAccumulatorPipeline = cute::conditional_t<kEnableRHTColQuant, cute::true_type, cute::false_type>;\n  AccumulatorPipeline accumulator_pipeline(\n    shared_storage.accumulator,\n    accumulator_pipeline_params,\n    cluster_shape,\n    IsInitAccumulatorPipeline{},  // Perform barrier init\n    cute::true_type{}); // Delay mask calculation\n  // CLC pipeline\n  typename CLCPipeline::Params clc_pipeline_params;\n  if (is_sched_warp) {\n    clc_pipeline_params.role = CLCPipeline::ThreadCategory::ProducerConsumer;\n  } else {\n    clc_pipeline_params.role = CLCPipeline::ThreadCategory::Consumer;\n  }\n  clc_pipeline_params.producer_blockid = 0;\n  clc_pipeline_params.producer_arv_count = 1;\n  clc_pipeline_params.consumer_arv_count = NumSchedThreads + cluster_size *\n                                                (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n  clc_pipeline_params.transaction_bytes = sizeof(CLCResponse);\n  clc_pipeline_params.initializing_warp = 3;\n  CLCPipeline clc_pipeline(shared_storage.clc, clc_pipeline_params, cluster_shape);\n  CLCPipelineState clc_pipeline_consumer_state;\n  CLCPipelineState clc_pipeline_producer_state = cutlass::make_producer_start_state<CLCPipeline>();\n\n  // CLC throttle pipeline\n  typename CLCThrottlePipeline::Params clc_throttle_pipeline_params;\n  if (is_dma_warp) {\n    clc_throttle_pipeline_params.role = CLCThrottlePipeline::ThreadCategory::Producer;\n  }\n  if (is_sched_warp) {\n    clc_throttle_pipeline_params.role = CLCThrottlePipeline::ThreadCategory::Consumer;\n  }\n  clc_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n  clc_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n  clc_throttle_pipeline_params.dst_blockid = 0;\n  clc_throttle_pipeline_params.initializing_warp = 4;\n\n  CLCThrottlePipeline clc_throttle_pipeline(shared_storage.clc_throttle, clc_throttle_pipeline_params);\n  CLCThrottlePipelineState clc_pipe_throttle_consumer_state;\n  CLCThrottlePipelineState clc_pipe_throttle_producer_state = cutlass::make_producer_start_state<CLCThrottlePipeline>();\n\n  if (warp_idx == 2 && elect_one_sync()) {\n    cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n  }\n  __syncthreads();\n\n  if (is_dma_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    cute::Tensor mA = tma_load_a.get_tma_tensor(make_shape(M,N));\n    cute::Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n    cute::Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_,_, _), Step<_1, X,_1>{});\n    cute::Tensor gB_nk = local_tile(mB, cluster_tile, make_coord(_,_, _), Step< X,_1,_1>{}); // (BLK_N,BLK_K,k)\n\n    cute::Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout); // (MMA,MMA_M,MMA_N,PIPE)\n    cute::Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout); // (MMA,MMA_N,MMA_K,PIPE)\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n    ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n    cute::Tensor tCgA = thr_mma.partition_A(gA_mk);                                                    // (MMA,MMA_M,MMA_K,k)\n    cute::Tensor tCgB = thr_mma.partition_B(gB_nk);                                                    // (MMA,MMA_N,MMA_K,k)\n\n    Layout cta_layout_mnk  = make_layout(cluster_shape);\n    Layout cta_layout_vmnk = tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n    auto cta_coord_vmnk  = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n    auto [tAgA, tAsA] = tma_partition(\n      tma_load_a,\n      get<2>(cta_coord_vmnk),\n      make_layout(size<2>(cta_layout_vmnk)),\n      group_modes<0,3>(tCsA),\n      group_modes<0,3>(tCgA));\n\n    auto [tBgB, tBsB] = tma_partition(\n      tma_load_b,\n      get<1>(cta_coord_vmnk),\n      make_layout(size<1>(cta_layout_vmnk)),\n      group_modes<0,3>(tCsB),\n      group_modes<0,3>(tCgB));\n\n    uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n    uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n    if constexpr (kEnableRHTColQuant) {\n      if (elect_one_sync()) {\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0], kTmaRhtTensorTransactionBytes);\n        copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_,0,0), tBsB(_,0));\n      }\n    }\n\n    do {\n      bool is_first_wave = scheduler.is_first_wave();\n      uint32_t skip_wait = is_first_wave;\n      auto tAgA_mk = tAgA(_,scheduler.tile_m(),_);\n      int k_tile = 0;\n      // Throttle CLC producer\n      clc_throttle_pipeline.producer_acquire(clc_pipe_throttle_producer_state);\n      clc_throttle_pipeline.producer_commit(clc_pipe_throttle_producer_state);\n      ++clc_pipe_throttle_producer_state;\n\n      CUTLASS_PRAGMA_NO_UNROLL\n      while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n\n        int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n        ++k_tile;\n        skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n        mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n        using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n        BarrierType* tma_barrier = mainloop_pipeline.producer_get_barrier(\n          mainloop_pipe_producer_state);\n        int write_stage = mainloop_pipe_producer_state.index();\n        ++mainloop_pipe_producer_state;\n        if (cute::elect_one_sync()) {\n          copy(\n            tma_load_a.with(*tma_barrier, tma_mcast_mask_a),\n            tAgA_mk(_,k_tile_idx_n),\n            tAsA(_,write_stage));\n        }\n      }\n      scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n      ++clc_pipeline_consumer_state;\n      scheduler.update_work_tile_info();\n    } while (scheduler.is_valid());\n    mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n  } else if (is_mma_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    if constexpr (kEnableRHTColQuant) {\n      cute::Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout); // (MMA,MMA_M,MMA_N,PIPE)\n      cute::Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout); // (MMA,MMA_N,MMA_K,PIPE)\n\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n      // Allocate \"fragments\" -- these are actually umma smem descriptors\n      cute::Tensor tCrA = thr_mma.make_fragment_A(tCsA);                                              // (MMA,MMA_M,MMA_K,PIPE)\n      cute::Tensor tCrB = thr_mma.make_fragment_B(tCsB);                                              // (MMA,MMA_M,MMA_K,PIPE)\n\n      mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n      tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n      __syncwarp();\n      tmem_allocation_result_barrier.arrive();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_mma.data() = tmem_base_ptr;\n      cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n\n        auto barrier_token = mainloop_pipeline.consumer_try_wait(\n          mainloop_pipe_consumer_state,\n          skip_wait);\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ) {\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          int read_stage = mainloop_pipe_consumer_state.index();\n          auto tCrA_mk = tCrA(_,_,_,read_stage);\n          auto tCrB_nk = tCrB(_,_,0,0);\n          CUTLASS_PRAGMA_UNROLL\n          for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block)\n          {\n            int accumulator_k_block = accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n            int tCrA_k_block = k_block * EpilogueUnrollFactor;\n            accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n            CUTLASS_PRAGMA_UNROLL\n            for (int i = 0; i < EpilogueUnrollFactor; i++) {\n              auto accumulators = bulk_tmem_mma(_,_,_,accumulator_k_block + i);\n              gemm(mma, tCrA_mk(_,_,tCrA_k_block + i), tCrB_nk, accumulators);\n            }\n\n            accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n            ++accumulator_pipe_producer_state;\n          }\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n          barrier_token = mainloop_pipeline.consumer_try_wait(\n            mainloop_pipe_consumer_state,\n            skip_wait);\n        }\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n      tmem_allocator.release_allocation_lock();\n      accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n      tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n    }\n  } else if(is_sched_warp) {\n    cutlass::arch::warpgroup_reg_dealloc<32>();\n    do {\n      clc_throttle_pipeline.consumer_wait(clc_pipe_throttle_consumer_state);\n      clc_throttle_pipeline.consumer_release(clc_pipe_throttle_consumer_state);\n      ++clc_pipe_throttle_consumer_state;\n      clc_pipeline_producer_state = scheduler.advance_to_next_work(clc_pipeline, clc_pipeline_producer_state);\n      scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n      ++clc_pipeline_consumer_state;\n      scheduler.update_work_tile_info();\n    } while (scheduler.is_valid());\n  } else if (is_epilogue_col_quant_warp) {\n    cutlass::arch::warpgroup_reg_alloc<192>();\n    if constexpr (kEnableRHTColQuant) {\n      using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n      float const c_global_amax_val = *c_global_amax;\n      auto acc_epilogue_pipelined_shape = append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n      auto bulk_tmem_epilogue_layout = make_layout(\n        acc_epilogue_pipelined_shape,\n        make_stride(\n          stride<0>(bulk_tmem_mma),\n          Int<0>{},\n          Int<0>{},\n          size<1>(epilogue_tiler)));\n      auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n      // leveraging 256-bit writes to global memory\n      static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n      tmem_allocation_result_barrier.arrive_and_wait();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_epilogue.data() = tmem_base_ptr;\n      int global_thread_idx = threadIdx.x;\n      int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n\n      size_t rng_seed = 0;\n      size_t rng_offset = 0;\n      if constexpr (kEnableStochasticRounding) {\n        rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n        rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n      }\n\n      cute::Tensor mD = make_tensor(\n        cute::subbyte_iterator<TD>(D),\n        make_shape(M,N),\n        dD); // (M,N)\n      cute::Tensor gD_mn = local_tile(\n        mD,\n        epilogue_tiler,\n        make_coord(_,_, _),\n        Step<_1,_1, X>{}); // (BLK_M,BLK_N)\n      cute::Tensor pD = make_identity_tensor(mD.shape());\n      cute::Tensor pD_mn = local_tile(\n        pD,\n        epilogue_tiler,\n        make_coord(_,_, _),\n        Step<_1,_1, X>{}); // (BLK_M,BLK_N)\n      cute::Tensor mSFD = make_tensor(make_gmem_ptr(SFD), sfd_layout);\n      cute::Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});           // (BLK_M,BLK_N)\n      cute::Tensor pSFD = make_identity_tensor(mSFD.shape());\n      cute::Tensor pSFD_mn = local_tile(pSFD, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});           // (BLK_M,BLK_N)\n\n      cute::Tensor gD_mn_view = tiled_divide(gD_mn, take<0,2>(epilogue_tiler));\n      cute::Tensor pD_mn_view = tiled_divide(pD_mn, take<0,2>(epilogue_tiler));\n      auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_,_,_,_0{}));\n      auto tiled_r2g = make_tiled_copy_D(\n        Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{},\n        tiled_t2r);\n      auto thr_t2r   = tiled_t2r.get_slice(local_thread_idx);\n      auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n      // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp8_max = 448.0f;\n      float const fp4_max_inv = 1.0f / fp4_max;\n      float const global_encode_scale = c_global_amax_val > 0.0f\n        ? cutlass::minimum_with_nan_propagation<float>{}(\n          (fp8_max * fp4_max) / c_global_amax_val,\n          cutlass::platform::numeric_limits<float>::max())\n        : 1.0f;\n\n      float const global_decode_scale = 1.0f / global_encode_scale;\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n      auto sfc_converter = cutlass::NumericConverter<TSFD, float>{};\n\n      do {\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ++k_tile) {\n          cute::Tensor tDgD_mn = gD_mn_view(_,_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDgSFD_mn = gSFD_mn(_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDpD_mn = pD_mn_view(_,_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          cute::Tensor tDpSFD_mn = pSFD_mn(_,_,scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n\n          accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n          auto Acc = bulk_tmem_epilogue(_,_,_,accumulator_pipe_consumer_state.index());\n          cute::Tensor tDtAcc = thr_t2r.partition_S(Acc); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDgD = thr_t2r.partition_D(tDgD_mn); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDpD = thr_t2r.partition_D(tDpD_mn); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tTR_rAcc = make_tensor<ElementAccumulator>(shape(tDgD)); // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          cute::Tensor tDrD = make_tensor<TD>(shape(tDgD));\n          cute::Tensor tTR_rAcc_frag = recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n          cute::Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n          cute::Tensor src = thr_r2g.retile_S(tDrD);\n          cute::Tensor dst = thr_r2g.retile_D(tDgD);\n          cute::Tensor pSrc = thr_r2g.retile_D(tDpD);\n\n          cute::Tensor tDgSFD_view = make_tensor(\n            tDgSFD_mn.data(),\n              make_layout(\n                make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n          cute::Tensor tDpSFD_view = make_tensor(\n            tDpSFD_mn.data(),\n              make_layout(\n                make_shape(shape(tDpSFD_mn), Int<1>{}, Int<1>{}),\n                make_stride(stride(tDpSFD_mn), Int<0>{}, Int<0>{})));\n          cute::Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n          cute::Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n          cute::Tensor tDpSFD = filter(thr_t2r.partition_D(tDpSFD_view));\n          static int constexpr NumVecs = size(tDgD) / VectorSize;\n          cute::Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n          cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n          cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n          // TMEM_LOAD\n          copy(tiled_t2r, tDtAcc, tTR_rAcc);\n          cutlass::arch::fence_view_async_tmem_load();\n          accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n          ++accumulator_pipe_consumer_state;\n\n          if constexpr (!kUseFastMath) {\n            // Downcast to BF16 for bit-wise compatibility with\n            // unfused kernels\n            auto convert_accum_to_bf16 =\n                cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                               FragmentSize>{};\n            auto convert_bf16_to_accum =\n                cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                               FragmentSize>{};\n            tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n            tTR_rAcc_frag(_1{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n          }\n\n          auto compute_frgs = reinterpret_cast<cutlass::Array< ElementAccumulator, VectorSize> *>(tTR_rAcc_frag.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array< TD, VectorSize> *>(tDrD_frag.data());\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n          }\n\n          pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n            vec_maxs, global_encode_scale_multiplier);\n          auto pvscales_cvted = cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n          tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n          auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(tD_rRowSFD_frg(_0{}));\n          auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n            qpvscale_ups,\n            global_decode_scale);\n\n          cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n          if constexpr (kUseFastMath) {\n            // fast math: use reciprocal approximate to replace div\n            acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n          } else {\n            // regular path for slower math, use divide to replace div\n            acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(1.0, qpvscale_scaled);\n          }\n\n          uint4 random_uint4 = uint4{0, 0, 0, 0};\n          transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n          // \"Prefetch\" a stochastic rounding state for the first tile\n          if constexpr (kEnableStochasticRounding) {\n            const size_t rng_sequence = global_thread_idx + k_tile * 512 + scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n            rng.init(rng_seed, rng_sequence, rng_offset);\n          }\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n              acc_scales[v],\n              cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs[v], acc_scale), *reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n              cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                compute_frgs[v],\n                acc_scale));\n            }\n\n          }\n\n          cute::Tensor pred_pSrc = cute::lazy::transform(make_tensor(counting_iterator<int>{}, replace<0>(shape(dst), _1{})), [&](auto coord){\n            cute::Tensor pSrc_view = group_modes<1,rank(pSrc)>(pSrc);\n            return elem_less(pSrc_view(_0{},coord), shape(mD));\n          });\n          copy_if(tiled_r2g, pred_pSrc, src, dst);\n          // 32bit vectorization copy 4 e4m3 SFD for per 64 or(16,4):(0, 1) element\n\n          constexpr int vec_len = 32 / sizeof_bits_v<TSFD>;\n          cute::Tensor  tDrSFD_v = recast<uint_bit_t<32>>(tDrSFD);\n          cute::Tensor  tDgSFD_v = recast<uint_bit_t<32>>(tDgSFD);\n          copy_if(\n                  [&](auto coord){\n                    cute::Tensor tDpSFD_view = group_modes<1,rank(tDpSFD)>(tDpSFD);\n                    return elem_less(tDpSFD_view(_0{}, coord * vec_len), shape(mSFD));\n                  },\n                  tDrSFD_v, tDgSFD_v);\n        }\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    }\n  } else if (is_epilogue_row_quant_warp) {\n    cutlass::arch::warpgroup_reg_alloc<136>();\n    if constexpr (kEnableRowQuant) {\n      using S2RVectorType = uint128_t;\n      float const a_global_amax_val = *a_global_amax;\n      int global_thread_idx = threadIdx.x;\n      int local_thread_idx = global_thread_idx % 256;\n      size_t rng_seed = 0;\n      size_t rng_offset = 0;\n      if constexpr (kEnableStochasticRounding) {\n        rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n        rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n      }\n      cute::Tensor mQA = make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, N), dQA));\n      cute::Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n      cute::Tensor pQA = make_identity_tensor(mQA.shape());\n      cute::Tensor pQA_mn = local_tile(pQA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n\n      cute::Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n      cute::Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});           // (BLK_M,BLK_N)\n      cute::Tensor pSFA = make_identity_tensor(mSFA.shape());\n      cute::Tensor pSFA_mn = local_tile(pSFA, epilogue_tiler, make_coord(_,_, _), Step<_1,X,_1>{});\n      cute::Tensor sA = as_position_independent_swizzle_tensor(\n                    group_modes<0,2>(coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout)))); // (BLOCK_M, BLOCK_M,PIPE)\n      using S2RWarpLayout = Layout<Shape<_2,_16>>;\n      using WarpGroupLayout = Layout<Shape<_1,_8>>;\n      using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n      using S2RValLayout = Layout<Shape<Int<64>, _1>>;\n      using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n      using R2GAtomQA = Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TQA>;\n\n      auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n      auto tiled_r2g_QA = make_tiled_copy_D(R2GAtomQA{}, tiled_s2r);\n\n      auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n      auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n\n      cute::Tensor tQAsA = thr_s2r.partition_S(sA); // (Copy, Copy_M, Copy_N, PIPE)\n\n      cute::Tensor tQArA = make_tensor_like<TA>(make_layout(tQAsA(_, _, _, _0{}).shape())); // (Copy, Copy_M, Copy_N)\n      // Tensor tQArA_PI = thr_s2r.partition_S(sA_PI);\n      cute::Tensor tQAgQA = thr_r2g_QA.partition_D(gQA_mn);\n      cute::Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n      cute::Tensor tQApQA = thr_r2g_QA.partition_D(pQA_mn);\n\n      cute::Tensor tQAgSFA = thr_s2r.partition_D(gSFA_mn);\n      cute::Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n      cute::Tensor tQApSFA = thr_s2r.partition_D(pSFA_mn);\n\n      // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp8_max = 448.0f;\n      float const fp4_max_inv = 1.0f / fp4_max;\n      float const global_encode_scale = a_global_amax_val > 0.0f\n        ? cutlass::minimum_with_nan_propagation<float>{}(\n          (fp8_max * fp4_max) / a_global_amax_val,\n          cutlass::platform::numeric_limits<float>::max())\n        : 1.0f;\n\n      float const global_decode_scale = 1.0f / global_encode_scale;\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n      auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n(); ) {\n          auto tQAgSFA_mn = tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQApSFA_mn = tQApSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto tQApQA_mn = tQApQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n          auto barrier_token = mainloop_pipeline.consumer_try_wait(\n            mainloop_pipe_consumer_state);\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n          cutlass::arch::fence_view_async_shared();\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n          // static int constexpr NumVecs = size(tQArA) / VectorSize;\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n          auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(raw_pointer_cast(tQArQA.data()));\n          transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS> rng;\n          if constexpr (kEnableStochasticRounding) {\n            const size_t rng_sequence = global_thread_idx + k_tile * 512 + scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n            rng.init(rng_seed, rng_sequence, rng_offset);\n          }\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < size(tQArA)/VectorSize; v++) {\n            auto compute_frgs_up = cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(compute_frgs[v]);\n            auto amax = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n            // declare pvscales\n            ElementAccumulator pvscales;\n            pvscales = cutlass::multiplies<ElementAccumulator>{}(amax, global_encode_scale_multiplier);\n            filter(tQArSFA)(v) = sfa_converter(pvscales);\n            auto qpvscale_ups = cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n            auto qpvscale_scaled = cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n            ElementAccumulator acc_scales;\n            if constexpr (kUseFastMath) {\n              // fast math: use reciprocal approximate to replace div\n              acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // regular path for slower math, use divide to replace div\n              acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n            }\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n              acc_scales,\n              cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs_up, acc_scale), *reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                  compute_frgs_up,\n                  acc_scale));\n            }\n          }\n\n          cute::Tensor pred_tQApQA = cute::lazy::transform(make_tensor(counting_iterator<int>{}, replace<0>(shape(tQAgQA_mn), _1{})), [&](auto coord){\n            cute::Tensor tQApQA_view = group_modes<1,rank(tQApQA_mn)>(tQApQA_mn);\n            return elem_less(tQApQA_view(_0{}, coord), shape(mQA));\n          });\n          copy_if(tiled_r2g_QA, pred_tQApQA, tQArQA, tQAgQA_mn);\n          // 32bit vectorization copy 4 e4m3 SFA for per 64 or (16,4):(0, 1)  element\n          constexpr int vec_len = 32 / sizeof_bits_v<TSFD>;\n          cute::Tensor  tQArSFA_v = recast<uint_bit_t<32>>(filter(tQArSFA));\n          cute::Tensor  tQAgSFA_v = recast<uint_bit_t<32>>(filter(tQAgSFA_mn));\n          copy_if(\n                  [&](auto coord){\n                    cute::Tensor tQApSFA_view = filter(tQApSFA_mn);\n                    return elem_less(tQApSFA_view(_0{}, coord * vec_len), shape(mSFA));\n                  },\n                  tQArSFA_v, tQAgSFA_v);\n        }\n        scheduler.fetch_next_work(clc_pipeline, clc_pipeline_consumer_state);\n        ++clc_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      }while (scheduler.is_valid());\n    }\n  } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n  }\n  } // sm100 compile guard end\n} // NOLINT(readability/fn_size)\n\n\n// this function computes RHT-GEMM for\n// m = hidden_size, n = sequence_length\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// D: m x n: row-major\n// SFD: m x (n/16): row-major\n// QA: m x n: col-major\n// SFA: m/16 x n: col-major\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant, bool kEnableSwizzleSFOutput,\nclass TA, class TB, class TD, class TSFD, class TQA, class TSFA, bool kUseFastMath=true>\nvoid row_col_rht_gemm_ntt_w_sfc(\n    int sequence_length,\n    int hidden_size,\n    TA const* A,\n    TB const* B,\n    TD* D,\n    TSFD* SFD,\n    TQA* QA,\n    TSFA* SFA,\n    float const* a_global_amax,\n    float const* d_global_amax,\n    const size_t* rng_state,\n    uint32_t sm_count,\n    hipStream_t stream,\n    int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0), make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFCLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)), make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom = cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom = cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(SwizzledSFALayoutAtom{}, make_shape(hidden_size,sequence_length), Step<_1,_2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(hidden_size,sequence_length), Step<_2,_1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFCLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFCLayout>;\n  SFALayout sfa_layout;\n  SFCLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{}, make_shape(hidden_size, sequence_length), Step<_1,_2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(hidden_size, sequence_length), Step<_2,_1>{});\n  } else {\n    sfa_layout = make_layout(make_shape(make_shape(Int<SFVecSize>{}, hidden_size/SFVecSize), sequence_length), make_stride(make_stride(_0{}, _1{}), hidden_size/SFVecSize));\n    sfd_layout = make_layout(make_shape(hidden_size, make_shape(Int<SFVecSize>{}, sequence_length/SFVecSize)), make_stride(sequence_length/SFVecSize, make_stride(_0{}, _1{})));\n  }\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = sequence_length;\n  cute::Tensor tensorA = make_tensor(A, make_shape(hidden_size, sequence_length), LayoutLeft{});\n  cute::Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  cute::Tensor tensorD = make_tensor(D, make_shape(hidden_size, sequence_length), LayoutRight{});\n  cute::Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, sequence_length), LayoutLeft{});\n  cute::Tensor tensorSFD = make_tensor(SFD, sfd_layout);\n  cute::Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);   // (dM,dK)\n  auto dB = stride(tensorB);   // (dN,dK)\n  auto dD = stride(tensorD);   // (dM,dN)\n  auto dQA = stride(tensorQA); // (dM,dK)\n  using ClusterShape = Shape<  _1,  _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128,Int<RhtTensorSize>,Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128,Int<RhtTensorSize>,_128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n    size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, float,\n                                               size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B = partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(shape<0>(cluster_tile_shape), shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(shape<1>(cluster_tile_shape), shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TB, SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A = decltype(shape_div(shape<0>(cluster_tile_mainloop), shape_div(shape<0>(cluster_tile_mainloop), size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem /\n      (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 6;\n  static int constexpr MainloopPipelineBytes = sizeof(typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<\n                                                1,\n                                                Shape<_1,_1,_1>,\n                                                Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr ClcResponseBytes = sizeof(CLCResponse) * SchedulerPipelineStageCount;\n  static int constexpr CLCThrottlePipelineBytes = sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr CLCPipelineBytes = sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>::SharedStorage);\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor, Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448; // 232KB in bytes\n  static int constexpr kBytesPerStage =\n    cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = ClcResponseBytes + CLCThrottlePipelineBytes + TmemBasePtrsBytes +\n                                    CLCPipelineBytes + TmemDeallocBytes+BTensorBytes + AccPipelineBytes; // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};      // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(\n    SmemLayoutAtomA{},\n    append(mma_shape_A, sP), Step<_2,_1,_3>{}); // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(\n    SmemLayoutAtomB{},\n    append(mma_shape_B, _1{})); // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};  // XXX Dummy\n\n  auto tma_load_a = make_tma_copy_A_sm100(\n    SM90_TMA_LOAD{},\n    tensorA,\n    sA(_,_,_,0),\n    cluster_tile_mainloop,\n    mma);\n  auto tma_load_b = make_tma_copy_B_sm100(\n    SM90_TMA_LOAD{},\n    tensorB,\n    sB(_,_,_,0),\n    cluster_tile_shape,\n    mma);\n\n  // Assert checks problem size should be multiple of 64\n  NVTE_CHECK(M % 64 == 0, \"M must be a multiple of 64, but got \", M);\n  NVTE_CHECK(N % 64 == 0, \"N must be a multiple of 64, but got \", N);\n\n  uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile_shape))));\n  uint32_t tiles_in_n = uint32_t(size(ceil_div(N, k_tile_size)));\n  uint32_t tiles = tiles_in_m * tiles_in_n;\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(tiles_in_m, tiles_in_n, 1);\n\n  int smem_size = sizeof(\n    SharedStorage<\n    TA,\n    TB,\n    decltype(sA),\n    decltype(sB),\n    ClusterShape,\n    AccumulatorPipelineStageCount,\n    EpilogueUnrollFactor,\n    SchedulerPipelineStageCount>);\n\n  auto* kernel_ptr = &row_col_rht_gemm_device<\n    decltype(M), decltype(N), decltype(k_tile_size),\n    decltype(cluster_shape), decltype(cluster_tile_shape),\n    TA, decltype(dA), decltype(sA), decltype(tma_load_a),\n    TB, decltype(dB), decltype(sB), decltype(tma_load_b),\n    TD, decltype(dD), decltype(sD),\n    TSFD, decltype(sfd_layout),\n    TQA, decltype(dQA),\n    TSFA, decltype(sfa_layout),\n    decltype(mma),\n    AccumulatorPipelineStageCount,\n    SchedulerPipelineStageCount,\n    kEnableStochasticRounding,\n    kEnableRHTColQuant,\n    kEnableRowQuant,\n    kUseFastMath>;\n\n  NVTE_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(*kernel_ptr), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n     params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape,\n     tensorA.data(), dA, sA, tma_load_a,\n     tensorB.data(), dB, sB, tma_load_b,\n     tensorD.data(), dD, sD,\n     tensorSFD.data(), sfd_layout,\n     tensorQA.data(), dQA,\n     tensorSFA.data(), sfa_layout,\n     mma, a_global_amax, d_global_amax, rng_state);\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n\n}\n\n}  // namespace\n}  // namespace detail\n\n// clang-format on\n\nvoid hadamard_transform_cast_fusion(const Tensor &input_, Tensor &output_,\n                                    const Tensor &hadamard_matrix_, QuantizationConfig quant_config,\n                                    hipStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_cast_fusion);\n\n  // Check input and output tensors\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  // rowwise cast and columnwise cast has different output data pointers\n  bool has_rowwise_quant = false;\n  bool has_columnwise_quant = false;\n  void *rowwise_data_ptr = nullptr;\n  void *rowwise_scale_inv_ptr = nullptr;\n  void *rowwise_amax_ptr = nullptr;\n  void *columnwise_data_ptr = nullptr;\n  void *columnwise_scale_inv_ptr = nullptr;\n  void *columnwise_amax_ptr = nullptr;\n\n  // examine the output tensor (single tensor for dense)\n  if (output_.data.dptr != nullptr) {\n    has_rowwise_quant = true;\n    rowwise_data_ptr = output_.data.dptr;\n    rowwise_scale_inv_ptr = output_.scale_inv.dptr;\n    rowwise_amax_ptr = output_.amax.dptr;\n  }\n\n  if (output_.columnwise_data.dptr != nullptr) {\n    has_columnwise_quant = true;\n    columnwise_data_ptr = output_.columnwise_data.dptr;\n    columnwise_scale_inv_ptr = output_.columnwise_scale_inv.dptr;\n    columnwise_amax_ptr = output_.columnwise_amax.dptr;\n  }\n\n  NVTE_CHECK(has_rowwise_quant || has_columnwise_quant,\n             \"Output tensor must have rowwise or columnwise quant.\");\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(hadamard_matrix_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Hadamard matrix must be BF16 tensor, but scaling mode is \",\n             to_string(hadamard_matrix_.scaling_mode), \".\");\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  // TODO: add support for swizzle sf output\n  const bool use_swizzle_sf_output = false;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          has_columnwise_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              has_rowwise_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::row_col_rht_gemm_ntt_w_sfc<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TD, TSFD, TQA, TSFA, kUseFastMath>(\n                            /*sequence_length=*/m, /*hidden_size=*/n,\n                            /*A=*/reinterpret_cast<TA const *>(input.dptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*D=*/reinterpret_cast<TD *>(columnwise_data_ptr),\n                            /*SFD=*/reinterpret_cast<TSFD *>(columnwise_scale_inv_ptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_ptr),\n                            /*a_global_amax=*/reinterpret_cast<float const *>(rowwise_amax_ptr),\n                            /*d_global_amax=*/reinterpret_cast<float const *>(columnwise_amax_ptr),\n                            /*rng_state=*/rng_state, /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_quantize_with_hadamard_transform(const NVTETensor input, NVTETensor output,\n                                           const NVTETensor hadamard_matrix,\n                                           const NVTEQuantizationConfig quant_config,\n                                           hipStream_t stream) {\n  NVTE_API_CALL(nvte_quantize_with_hadamard_transform);\n  using namespace transformer_engine;\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n  hadamard_transform_cast_fusion(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                                 *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n                                 stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/row_cast_col_hadamard_transform_cast_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\n// clang-format off\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\nusing cute::Tensor;  // Ensure unqualified Tensor refers to cute::Tensor, not transformer_engine::Tensor\n\n// calculate the global encode scale factor for a given global amax.\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float kFP8E4M3Max = 448.0f;\n  constexpr float kFP4E2M1Max = 6.0f;\n  // If scale is infinity, return max value of float32\n  float global_encode_scale = cutlass::minimum_with_nan_propagation<float>{}(\n    kFP8E4M3Max * kFP4E2M1Max / global_amax, cutlass::platform::numeric_limits<float>::max());\n  // If global amax is 0 or infinity, return 1\n  return (global_amax == 0.f || global_encode_scale == 0.f) ? 1.f : global_encode_scale;\n}\n\ntemplate <class ElementA,\n          class ElementB,\n          class ASmemLayout,\n          class BSmemLayout>\nstruct SharedStorage {\n  static constexpr int AccumulatorPipelineStageCount = 16;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<\n                             MainloopPipelineStageCount,\n                             Shape<_1,_1,_1>,\n                             AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  uint32_t tmem_base_ptr;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n};\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8>\nStochasticNumericConverterBase(cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n    asm volatile( \\\n        \"{\\n\" \\\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\" \\\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\" \\\n        \"}\" \\\n        : \"=h\"(output_ptr[0]),\n        \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]),\n        \"f\"(input[4]), \"f\"(input[5]), \"f\"(input[6]), \"f\"(input[7]),\n        \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16>\nStochasticNumericConverter(cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const *rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr = reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr = reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr = reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class MShape, class NShape, class KShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA,\n          class TB, class BStride, class BSmemLayout, class TmaLoadB,\n          class TC, class CStride, class CSmemLayout,\n          class TSFC,\n          class TiledMMA,\n          bool kEnableStochasticRounding = false,\n          bool kUseFastMath = false>\n__global__ static\nvoid\nrht_gemm_device(MShape M, NShape N, KShape K, ClusterTileShape cluster_tile,\n            TA const* A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n            TB const* B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n            TC      * C, CStride dC, CSmemLayout         ,\n            TSFC    * SFC,\n            TiledMMA mma,\n            float const* global_amax,\n            const size_t* rng_state)\n{\n  using namespace cute;\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n  using X = Underscore;\n  // static constexpr bool kApplyStochasticRounding = true;\n  using ElementAccumulator = float;\n  static constexpr int K_PIPE_MAX = size<3>(ASmemLayout{});\n  using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n  static constexpr uint32_t kTmaTransactionBytes =\n    cutlass::bits_to_bytes(size(AtomThrShapeMNK{}) * cosize(take<0,3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n\n  static constexpr int kTmaRhtTensorTransactionBytes =\n    cutlass::bits_to_bytes(16 * 16 * cute::sizeof_bits_v<TB>);\n  static constexpr int AccumulatorPipelineStageCount = 16;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<\n                             MainloopPipelineStageCount,\n                             Shape<_1,_1,_1>,\n                             AtomThrShapeMNK>;\n  using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  static constexpr int VectorSize = 16;\n  const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n  const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n  // Preconditions\n  CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<CSmemLayout>::value);\n\n  // Represent the full tensors\n  Tensor mA = tma_load_a.get_tma_tensor(make_shape(M,N));\n  Tensor mB = tma_load_b.get_tma_tensor(make_shape(16,16));\n  Tensor mC = make_tensor(cute::subbyte_iterator<TC>(C), make_shape(M,N), dC);      // (M,N)\n\n  auto sfc_shape  = make_shape(\n    M,\n    make_shape( make_shape(Int<16>{}, _4{}), N / 64 )\n  );\n\n  auto sfc_stride = make_stride(\n    N / 16,\n    make_stride( make_stride(_0{}, _1{}), _4{} )\n  );\n\n  auto sfc_layout = make_layout(sfc_shape, sfc_stride);\n  Tensor mSFC = make_tensor(make_gmem_ptr(SFC), sfc_layout);\n\n  auto cluster_shape = Shape<  _1,  _1, _1>{};\n\n  // Get the appropriate blocks for this Cluster\n  dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n  // Total number of k-tiles\n  const int K_TILE_MAX  = min(N, K) / 64;\n  uint32_t tiles_in_m = (M + size<0>(cluster_tile) - 1) / size<0>(cluster_tile);\n  uint32_t tiles_in_n = (N + 64 - 1) / 64;\n  uint32_t linear_tile_idx = blockIdx.x;\n  uint32_t tile_idx_m = linear_tile_idx % tiles_in_m;\n  uint32_t tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n\n\n  auto mainloop_tiler = Shape<_128,_16,_64>{};\n  auto epilogue_tiler = Shape<_128,_64,_64>{};\n  Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_,_, _), Step<_1, X,_1>{});\n  Tensor gB_nk = local_tile(mB, cluster_tile, make_coord(_,_, _), Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC_mn = local_tile(mC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  Tensor gSFC_mn = local_tile(mSFC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n  Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n\n  //\n  // MMA: Define C accumulators and A/B partitioning\n  //\n\n  int block_rank_in_cluster = cute::block_rank_in_cluster();\n  ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n  Tensor tCgB = thr_mma.partition_B(gB_nk);                               // (MMA,MMA_N,MMA_K,k)\n\n  auto mma_epilogue = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, ElementAccumulator,\n                                               128, 64,\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n  ThrMMA thr_mma_epilogue = mma_epilogue.get_slice(block_rank_in_cluster);\n\n\n  using TiledMmaEpilogue = decltype(mma_epilogue);\n  Tensor tCgA = thr_mma.partition_A(gA_mk);\n  // Allocate \"fragments\" -- these are actually umma smem descriptors\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_M,MMA_K,PIPE)\n\n  auto acc_shape_mma = partition_shape_C(TiledMMA{}, take<0,2>(ClusterTileShape{}));\n  auto acc_shape_epilogue = partition_shape_C(TiledMmaEpilogue{}, take<0,2>(epilogue_tiler));\n\n  auto bulk_tmem_mma = TiledMMA::make_fragment_C(append(acc_shape_mma,\n                                                      Int<AccumulatorPipelineStageCount>{}));\n\n  auto bulk_tmem_epilogue = TiledMmaEpilogue::make_fragment_C(append(acc_shape_epilogue,\n                                                      Int<AccumulatorPipelineStageCount / 4>{}));\n\n  TmemAllocator tmem_allocator{};\n  cutlass::arch::NamedBarrier tmem_allocation_result_barrier(32 + 128, cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n  Layout cta_layout_mnk  = make_layout(cluster_shape);\n  Layout cta_layout_vmnk = tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n  auto cta_coord_vmnk  = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n  auto [tAgA, tAsA] = tma_partition(tma_load_a,\n    get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  auto [tBgB, tBsB] = tma_partition(tma_load_b,\n    get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n\n  bool is_mma_warp = (warp_idx == 0);\n  bool is_dma_warp = (warp_idx == 1);\n  bool is_epilogue_warp = (warp_idx >= 4 && warp_idx <= 7);\n\n  if (is_epilogue_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(global_amax));\n  }\n\n  typename MainloopPipeline::Params mainloop_pipeline_params;\n  if (is_dma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  if (is_mma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n  mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n  mainloop_pipeline_params.initializing_warp = 0;\n  MainloopPipeline mainloop_pipeline(shared_storage.mainloop,\n                                       mainloop_pipeline_params,\n                                       cluster_shape,\n                                       cute::true_type{},   // Perform barrier init\n                                       cute::true_type{}); // Delay mask calculation\n\n  MainloopPipelineState mainloop_pipe_consumer_state;\n  MainloopPipelineState mainloop_pipe_producer_state = cutlass::make_producer_start_state<MainloopPipeline>();\n\n\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n  AccumulatorPipelineState accumulator_pipe_consumer_state;\n  AccumulatorPipelineState accumulator_pipe_producer_state = cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n  typename AccumulatorPipeline::Params accumulator_pipeline_params;\n  if (is_mma_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n  }\n  if (is_epilogue_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n  }\n  // Only one producer thread arrives on this barrier.\n  accumulator_pipeline_params.producer_arv_count = 1;\n  accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * 128;\n  accumulator_pipeline_params.initializing_warp = 1;\n  AccumulatorPipeline accumulator_pipeline(shared_storage.accumulator,\n                                           accumulator_pipeline_params,\n                                           cluster_shape,\n                                           cute::true_type{},   // Perform barrier init\n                                           cute::true_type{}); // Delay mask calculation\n\n  if (warp_idx == 2 && elect_one_sync()) {\n    cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n  }\n  __syncthreads();\n  using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n  if (is_dma_warp) {\n    if (elect_one_sync()) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0], kTmaRhtTensorTransactionBytes);\n      copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_,0,0), tBsB(_,0));\n    }\n    cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n    do {\n      bool is_first_wave = linear_tile_idx == blockIdx.x;\n      uint32_t skip_wait = is_first_wave;\n      auto tAgA_mk = tAgA(_,tile_idx_m,_);\n      int k_tile = 0;\n      auto barrier_token = mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n\n\n      CUTE_NO_UNROLL\n      while (k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n) {\n        int k_tile_idx_n = tile_idx_n + k_tile;\n        ++k_tile;\n        skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n        mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state, barrier_token);\n        using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n        BarrierType* tma_barrier = mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n        int write_stage = mainloop_pipe_producer_state.index();\n        ++mainloop_pipe_producer_state;\n        barrier_token = mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n        if (cute::elect_one_sync()) {\n          copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_,k_tile_idx_n), tAsA(_,write_stage));\n        }\n      }\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n  } else if (is_mma_warp) {\n    mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n    __syncwarp();\n    tmem_allocation_result_barrier.arrive();\n    uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n    bulk_tmem_mma.data() = tmem_base_ptr;\n\n    do {\n      uint32_t skip_wait = K_TILE_MAX <= 0;\n      auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n      CUTE_NO_UNROLL\n      for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; )\n      {\n        mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n        int read_stage = mainloop_pipe_consumer_state.index();\n        auto tCrA_mk = tCrA(_,_,_,read_stage);\n        auto tCrB_nk = tCrB(_,_,0,0);\n        CUTE_UNROLL\n        for (int k_block = 0; k_block < size<2>(tCrA) / 4; ++k_block)\n        {\n          accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n          CUTE_UNROLL\n          for (int i = 0; i < 4; i++) {\n            auto accumulators = bulk_tmem_mma(_,_,_,accumulator_pipe_producer_state.index() * 4 + i);\n            gemm(mma, tCrA_mk(_,_,k_block * 4 + i), tCrB_nk, accumulators);\n          }\n\n          accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n          ++accumulator_pipe_producer_state;\n        }\n        auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n        ++mainloop_pipe_consumer_state;\n        ++k_tile;\n        skip_wait = k_tile >= K_TILE_MAX;\n        barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n        mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n      }\n\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    tmem_allocator.release_allocation_lock();\n    accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n    tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  } else if (is_epilogue_warp) {\n    const float global_amax_val = *global_amax;\n    static constexpr int FragmentSize = 256 / sizeof_bits_v<TC>;\n\n    tmem_allocation_result_barrier.arrive_and_wait();\n    uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n    bulk_tmem_epilogue.data() = tmem_base_ptr;\n    int thread_idx = threadIdx.x % 128;\n\n    Tensor tCgC = thr_mma_epilogue.partition_C(gC_mn);                             // (MMA,MMA_M,MMA_N)                             // (MMA,MMA_M,MMA_N)\n    auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_,_,_,_0{}));\n    auto tiled_r2g = make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TC>{}, tiled_t2r);\n    auto thr_t2r   = tiled_t2r.get_slice(thread_idx);\n    auto thr_r2g = tiled_r2g.get_slice(thread_idx);\n\n    // NVFP4 non-E8 recipe constants and global scales\n    static constexpr float fp4_max = 6.0f;\n\n    const float global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n    const float global_decode_scale = 1.0f / global_encode_scale;\n\n    // Scaling factor for fast math path\n    static constexpr float fp4_max_inv = 1.0f / fp4_max;\n    float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n    do {\n      for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; ++k_tile) {\n        Tensor tCgC_mn = tCgC(_,_,_,tile_idx_m,tile_idx_n+k_tile);\n\n        Tensor tCgSFC_mn = gSFC_mn(_,_,tile_idx_m,tile_idx_n+k_tile);\n        accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n        auto tCtC = bulk_tmem_epilogue(_,_,_,accumulator_pipe_consumer_state.index());\n        Tensor tDtC = thr_t2r.partition_S(tCtC);                   // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n        Tensor tDgC = thr_t2r.partition_D(tCgC_mn);                   // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n        Tensor tTR_rAcc = make_tensor<ElementAccumulator>(shape(tDgC));                 // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n        Tensor tDrC = make_tensor<TC>(shape(tDgC));\n        Tensor tTR_rAcc_frag = recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n        Tensor tDrC_frag = recast<cutlass::Array<TC, FragmentSize>>(coalesce(tDrC));\n\n        Tensor src = thr_r2g.retile_S(tDrC);\n        Tensor dst = thr_r2g.retile_D(tDgC);\n\n        Tensor tCgSFC = make_tensor(tCgSFC_mn.data(), make_layout(\n                                    make_shape(shape(tCgSFC_mn), Int<1>{}, Int<1>{}),\n                                    make_stride(stride(tCgSFC_mn), Int<0>{}, Int<0>{})\n                                   ));\n\n        Tensor tDgSFC = filter(thr_t2r.partition_D(tCgSFC));\n        Tensor tDrSFC = make_tensor<TSFC>(shape(tDgSFC));\n\n        static constexpr int NumVecs = size(tDgC) / VectorSize;\n        Tensor tC_rRowSFD_frg = recast<cutlass::Array<TSFC, NumVecs>>(tDrSFC);\n\n        cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n        cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n        cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n        // TMEM_LOAD\n        copy(tiled_t2r, tDtC, tTR_rAcc);\n        cutlass::arch::fence_view_async_tmem_load();\n\n        accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n\n        ++accumulator_pipe_consumer_state;\n\n        if constexpr (!kUseFastMath) {\n          // Downcast to BF16 for bit-wise compatibility with unfused\n          // kernels\n          auto convert_accum_to_bf16 = cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator, FragmentSize>{};\n          auto convert_bf16_to_accum = cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t, FragmentSize>{};\n          tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n        }\n\n        auto compute_frgs = reinterpret_cast<cutlass::Array< ElementAccumulator, VectorSize> *>(tTR_rAcc_frag.data());\n        auto output_frgs = reinterpret_cast<cutlass::Array< TC, VectorSize> *>(tDrC_frag.data());\n        CUTLASS_PRAGMA_UNROLL\n        for (int v = 0; v < NumVecs; v++) {\n          vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n        }\n\n        pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(vec_maxs, global_encode_scale_multiplier);\n        auto pvscales_cvted = cutlass::NumericArrayConverter<TSFC, ElementAccumulator, NumVecs>{}(pvscales);\n\n        tC_rRowSFD_frg(_0{}) = pvscales_cvted;\n        auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFC, NumVecs>{}(tC_rRowSFD_frg(_0{}));\n        auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(qpvscale_ups, global_decode_scale);\n        cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n        if constexpr (kUseFastMath) {\n          // Fast math: compute approximate reciprocal\n          acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n        } else {\n          // Accurate math: compute reciprocal with division\n          acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(1.0, qpvscale_scaled);\n        }\n\n        // Initialize RNG for tile\n        const size_t rng_sequence\n          = thread_idx + k_tile * 256 + linear_tile_idx * K_TILE_MAX * 256;\n\n        transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS>\n            rng;\n        rng.init(rng_seed, rng_sequence, rng_offset);\n        uint4 random_uint4 = uint4{0, 0, 0, 0};\n\n        CUTLASS_PRAGMA_UNROLL\n        for (int v = 0; v < NumVecs; v++) {\n          auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n          // auto acc_scale = acc_scales[v];\n          if constexpr (kEnableStochasticRounding) {\n            random_uint4 = rng.generate4();\n            output_frgs[v] = StochasticNumericConverter(\n              cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                compute_frgs[v],\n                acc_scale\n              ),\n              reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n          } else {\n            output_frgs[v] = cutlass::NumericArrayConverter<TC, ElementAccumulator, VectorSize>{}(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs[v], acc_scale));\n          }\n        }\n\n        copy(tiled_r2g, src, dst);\n\n        copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFC, tDgSFC);\n\n      }\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n  }\n  }\n}\n\n// this function computes RHT-GEMM for\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// C: m x n: row-major\n// SFC: m x (n/16): row-major\ntemplate <typename TA, typename TB, typename TC, typename TSFC, bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid\nrht_gemm_ntt_w_sfc(int m, int n,\n        TA const* A,\n        TB const* B,\n        TC      * C,\n        TSFC    * SFC,\n        float const* global_amax,\n        const size_t* rng_state,\n        uint32_t sm_count,\n        cudaStream_t stream,\n        int k_tile_size = 2048)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = static_cast<int>(m);\n  auto N = static_cast<int>(n);\n\n  // Define strides (mixed)\n  auto dA = make_stride(Int<1>{}, m);  // (dM,dK)\n  auto dB = make_stride(Int<1>{}, 16);  // (dN,dK)\n  auto dC = make_stride(n, Int<1>{});  // (dM,dN)\n\n  auto cga_shape      = Shape<  _1,  _1, _1>{};\n  auto cga_tile_shape = Shape<_128,_16,_16>{};\n  auto cluster_tile_mainloop = Shape<_128,_16,_64>{};\n\n  // Construct the MMA\n  auto mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, float,\n                                               128, 16,\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n\n  // MMA in CGA Layout XXX: Need to generalize synchro? {$nv-release-never}\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cga_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cga_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B = partition_shape_B(mma, make_shape(size<1>(cga_tile_shape), size<2>(cga_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(shape<0>(cga_tile_shape), shape_div(shape<0>(cga_tile_shape), size<0>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(shape<1>(cga_tile_shape), shape_div(shape<1>(cga_tile_shape), size<1>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cga_tile_shape));\n\n  using SmemLayoutAtomB = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TB, SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A = decltype(shape_div(shape<0>(cluster_tile_mainloop), shape_div(shape<0>(cluster_tile_mainloop), size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int kBlackwellSmemSize = 232448; // 232KB in bytes\n  constexpr int kBytesPerStage = cute::size(mma_shape_A) * sizeof(TA) + cute::size(mma_shape_B) * sizeof(TB);\n  constexpr int kReservedBytes = 256; // Reserve for barriers and other uses\n  constexpr int kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};      // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP)); // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{}, append(mma_shape_B, sP)); // (MMA,MMA_N,MMA_K,PIPE)\n  auto sC = Layout<_1>{};  // XXX Dummy\n\n  // Create GMEM tensors\n  Tensor tensorA = make_tensor(A, make_layout(make_shape(M,N), dA));      // (M,N)\n  Tensor tensorB = make_tensor(B, make_layout(make_shape(16,16), dB));      // (16,16)\n\n  // Create the TiledCopy\n\n  auto tma_load_a = make_tma_copy_A_sm100(\n        SM90_TMA_LOAD{},\n        tensorA,\n        sA(_,_,_,0),\n        cluster_tile_mainloop,\n        mma);\n  auto tma_load_b =  make_tma_copy_B_sm100(\n        SM90_TMA_LOAD{},\n        tensorB,\n        sB(_,_,_,0),\n        cga_tile_shape,\n        mma);\n\n  // Assert checks on tile sizes -- no predication\n  NVTE_CHECK(M % size<0>(cga_tile_shape) == 0,\n             \"Inner dimension must be divisible by \", static_cast<size_t>(size<0>(cga_tile_shape)), \" but got \", M, \".\");\n  NVTE_CHECK(N % (4 * size<1>(cga_tile_shape)) == 0,\n             \"Outer dimension must be divisible by \", 4 * static_cast<size_t>(size<1>(cga_tile_shape)),\n             \" but got \", N, \".\");\n\n  uint32_t tiles = size(ceil_div(M, get<0>(cga_tile_shape))) * size(ceil_div(N, k_tile_size));\n\n  tiles = (tiles < sm_count) ? tiles : sm_count;\n\n  dim3 dimBlock(256);\n  dim3 dimCluster(size<0>(cga_shape), size<1>(cga_shape), size<2>(cga_shape));\n  dim3 dimGrid(tiles, 1, 1);\n\n  int smem_size = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n  auto* kernel_ptr = &rht_gemm_device<\n                                  decltype(M), decltype(N), decltype(k_tile_size), decltype(cga_tile_shape),\n                                  TA, decltype(dA), decltype(sA), decltype(tma_load_a),\n                                  TB, decltype(dB), decltype(sB), decltype(tma_load_b),\n                                  TC, decltype(dC), decltype(sC),\n                                  TSFC,\n                                  decltype(mma),\n                                  kEnableStochasticRounding,\n                                  kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      cudaFuncSetAttribute(*kernel_ptr,\n                           cudaFuncAttributeMaxDynamicSharedMemorySize,\n                           smem_size)\n  );\n\n  (*kernel_ptr)\n      <<< dimGrid, dimBlock, smem_size, stream >>>\n      (M,  N,  k_tile_size, cga_tile_shape,\n       A, dA, sA, tma_load_a,\n       B, dB, sB, tma_load_b,\n       C, dC, sC,\n       SFC,\n       mma, global_amax,\n       rng_state);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n// this function is used to wrap the rht_gemm_ntt_w_sfc function\n//to transpose the input tensor A\ntemplate <typename TA, typename TB, typename TC, typename TSFC, bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid\nrht_gemm_ttt_wrapper(int m, int n,\n        TA const* A,\n        TB const* B,\n        TC      * C,\n        TSFC    * SFC,\n        float const* global_amax,\n        const size_t* rng_state,\n        uint32_t sm_count,\n        cudaStream_t stream,\n        int k_tile_size = 1024)\n{\n  // in addition to transpose the input tensor A\n  // we also need to reshape m, n to at best\n  // ultilize as many SMs as possible while keeping\n  // a relatively large contiguous dimension.\n  // for example, after swapping m, n for transpose purposes,\n  // the input / output tensor shapes for RHT-GEMM are:\n  // A: n x m: col-major\n  // B: 16 x 16: row-major\n  // C: n x m: row-major\n  // SFC: n x (m/16): row-major\n  rht_gemm_ntt_w_sfc<TA, TB, TC, TSFC, kEnableStochasticRounding, kUseFastMath>(\n    n, m,\n    A, B, C,\n    SFC, global_amax,\n    rng_state,\n    sm_count, stream,\n    k_tile_size);\n}\n\n}  // namespace\n}  // namespace detail\n\n// clang-format on\n\nvoid hadamard_transform_cast_fusion_columnwise(const Tensor &input_, Tensor &output_,\n                                               const Tensor &hadamard_matrix_,\n                                               QuantizationConfig quant_config,\n                                               cudaStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_cast_fusion_columnwise);\n\n  // Check input and output tensors\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  NVTE_CHECK(!output_.with_gemm_swizzled_scales, \"Output must have scales in compact format.\");\n  const SimpleTensor &input = input_.data;\n  SimpleTensor &global_amax = output_.amax;\n  SimpleTensor &output_t = output_.data;\n  SimpleTensor &scale_inv_t = output_.scale_inv;\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TC = cutlass::float_e2m1_t;\n  using TSFC = cutlass::float_ue4m3_t;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(hadamard_matrix_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Hadamard matrix must be BF16 tensor, but scaling mode is \",\n             to_string(hadamard_matrix_.scaling_mode), \".\");\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  if (m == 8192 && n == 5120) {\n    k_tile_size = 512;\n  } else if (m == 8192 && n == 10240) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 2560) {\n    k_tile_size = 1280;\n  } else if (m == 8192 && n == 11328) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 512) {\n    k_tile_size = 256;\n  } else if (m == 8192 && n == 3584) {\n    k_tile_size = 512;\n  } else if (m == 11328 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 5120 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m == 10240 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 2560 && n == 8192) {\n    k_tile_size = 1280;\n  } else if (m == 512 && n == 8192) {\n    k_tile_size = 256;\n  } else if (m == 3584 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m < 1024 || n < 1024) {\n    k_tile_size = 512;\n  }\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kUseStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          quant_config.use_fast_math, kUseFastMath,\n          detail::rht_gemm_ttt_wrapper<TA, TB, TC, TSFC, kUseStochasticRounding, kUseFastMath>(\n              /*m=*/m,\n              /*n=*/n,\n              /*A=*/reinterpret_cast<TA const *>(input.dptr),\n              /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n              /*C=*/reinterpret_cast<TC *>(output_t.dptr),\n              /*SFC=*/reinterpret_cast<TSFC *>(scale_inv_t.dptr),\n              /*global_amax=*/reinterpret_cast<float const *>(global_amax.dptr),\n              /*rng_state=*/rng_state,\n              /*sm_count=*/sm_count,\n              /*stream=*/stream,\n              /*k_tile_size=*/k_tile_size);););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_hadamard_transform_cast_fusion_columnwise(const NVTETensor input, NVTETensor output,\n                                                    const NVTETensor hadamard_matrix,\n                                                    const NVTEQuantizationConfig quant_config,\n                                                    cudaStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform_cast_fusion_columnwise);\n  using namespace transformer_engine;\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n  hadamard_transform_cast_fusion_columnwise(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n      *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\n// clang-format off\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\nusing cute::Tensor;  // Ensure unqualified Tensor refers to cute::Tensor, not transformer_engine::Tensor\n\n// calculate the global encode scale factor for a given global amax.\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float kFP8E4M3Max = 448.0f;\n  constexpr float kFP4E2M1Max = 6.0f;\n  // If scale is infinity, return max value of float32\n  float global_encode_scale = cutlass::minimum_with_nan_propagation<float>{}(\n    kFP8E4M3Max * kFP4E2M1Max / global_amax, cutlass::platform::numeric_limits<float>::max());\n  // If global amax is 0 or infinity, return 1\n  return (global_amax == 0.f || global_encode_scale == 0.f) ? 1.f : global_encode_scale;\n}\n\ntemplate <class ElementA,\n          class ElementB,\n          class ASmemLayout,\n          class BSmemLayout>\nstruct SharedStorage {\n  static constexpr int AccumulatorPipelineStageCount = 16;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<\n                             MainloopPipelineStageCount,\n                             Shape<_1,_1,_1>,\n                             AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  uint32_t tmem_base_ptr;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n};\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8>\nStochasticNumericConverterBase(cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n    asm volatile( \\\n        \"{\\n\" \\\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\" \\\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\" \\\n        \"}\" \\\n        : \"=h\"(output_ptr[0]),\n        \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]),\n        \"f\"(input[4]), \"f\"(input[5]), \"f\"(input[6]), \"f\"(input[7]),\n        \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16>\nStochasticNumericConverter(cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const *rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr = reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr = reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr = reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class MShape, class NShape, class KShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA,\n          class TB, class BStride, class BSmemLayout, class TmaLoadB,\n          class TC, class CStride, class CSmemLayout,\n          class TSFC,\n          class TiledMMA,\n          bool kEnableStochasticRounding = false,\n          bool kUseFastMath = false>\n__global__ static\nvoid\nrht_gemm_device(MShape M, NShape N, KShape K, ClusterTileShape cluster_tile,\n            TA const* A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n            TB const* B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n            TC      * C, CStride dC, CSmemLayout         ,\n            TSFC    * SFC,\n            TiledMMA mma,\n            float const* global_amax,\n            const size_t* rng_state)\n{\n  using namespace cute;\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n  using X = Underscore;\n  // static constexpr bool kApplyStochasticRounding = true;\n  using ElementAccumulator = float;\n  static constexpr int K_PIPE_MAX = size<3>(ASmemLayout{});\n  using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n  static constexpr uint32_t kTmaTransactionBytes =\n    cutlass::bits_to_bytes(size(AtomThrShapeMNK{}) * cosize(take<0,3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n\n  static constexpr int kTmaRhtTensorTransactionBytes =\n    cutlass::bits_to_bytes(16 * 16 * cute::sizeof_bits_v<TB>);\n  static constexpr int AccumulatorPipelineStageCount = 16;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<\n                             MainloopPipelineStageCount,\n                             Shape<_1,_1,_1>,\n                             AtomThrShapeMNK>;\n  using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  static constexpr int VectorSize = 16;\n  const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n  const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n  // Preconditions\n  CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n  CUTE_STATIC_ASSERT(is_static<CSmemLayout>::value);\n\n  // Represent the full tensors\n  Tensor mA = tma_load_a.get_tma_tensor(make_shape(M,N));\n  Tensor mB = tma_load_b.get_tma_tensor(make_shape(16,16));\n  Tensor mC = make_tensor(cute::subbyte_iterator<TC>(C), make_shape(M,N), dC);      // (M,N)\n\n  auto sfc_shape  = make_shape(\n    M,\n    make_shape( make_shape(Int<16>{}, _4{}), N / 64 )\n  );\n\n  auto sfc_stride = make_stride(\n    N / 16,\n    make_stride( make_stride(_0{}, _1{}), _4{} )\n  );\n\n  auto sfc_layout = make_layout(sfc_shape, sfc_stride);\n  Tensor mSFC = make_tensor(make_gmem_ptr(SFC), sfc_layout);\n\n  auto cluster_shape = Shape<  _1,  _1, _1>{};\n\n  // Get the appropriate blocks for this Cluster\n  dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n  // Total number of k-tiles\n  const int K_TILE_MAX  = min(N, K) / 64;\n  uint32_t tiles_in_m = (M + size<0>(cluster_tile) - 1) / size<0>(cluster_tile);\n  uint32_t tiles_in_n = (N + 64 - 1) / 64;\n  uint32_t linear_tile_idx = blockIdx.x;\n  uint32_t tile_idx_m = linear_tile_idx % tiles_in_m;\n  uint32_t tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n\n\n  auto mainloop_tiler = Shape<_128,_16,_64>{};\n  auto epilogue_tiler = Shape<_128,_64,_64>{};\n  Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_,_, _), Step<_1, X,_1>{});\n  Tensor gB_nk = local_tile(mB, cluster_tile, make_coord(_,_, _), Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC_mn = local_tile(mC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  Tensor gSFC_mn = local_tile(mSFC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()), sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n  Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()), sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n\n  //\n  // MMA: Define C accumulators and A/B partitioning\n  //\n\n  int block_rank_in_cluster = cute::block_rank_in_cluster();\n  ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);               // blk idx\n  Tensor tCgB = thr_mma.partition_B(gB_nk);                               // (MMA,MMA_N,MMA_K,k)\n\n  auto mma_epilogue = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, ElementAccumulator,\n                                               128, 64,\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n  ThrMMA thr_mma_epilogue = mma_epilogue.get_slice(block_rank_in_cluster);\n\n\n  using TiledMmaEpilogue = decltype(mma_epilogue);\n  Tensor tCgA = thr_mma.partition_A(gA_mk);\n  // Allocate \"fragments\" -- these are actually umma smem descriptors\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_M,MMA_K,PIPE)\n\n  auto acc_shape_mma = partition_shape_C(TiledMMA{}, take<0,2>(ClusterTileShape{}));\n  auto acc_shape_epilogue = partition_shape_C(TiledMmaEpilogue{}, take<0,2>(epilogue_tiler));\n\n  auto bulk_tmem_mma = TiledMMA::make_fragment_C(append(acc_shape_mma,\n                                                      Int<AccumulatorPipelineStageCount>{}));\n\n  auto bulk_tmem_epilogue = TiledMmaEpilogue::make_fragment_C(append(acc_shape_epilogue,\n                                                      Int<AccumulatorPipelineStageCount / 4>{}));\n\n  TmemAllocator tmem_allocator{};\n  cutlass::arch::NamedBarrier tmem_allocation_result_barrier(32 + 128, cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n  Layout cta_layout_mnk  = make_layout(cluster_shape);\n  Layout cta_layout_vmnk = tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n  auto cta_coord_vmnk  = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n  auto [tAgA, tAsA] = tma_partition(tma_load_a,\n    get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  auto [tBgB, tBsB] = tma_partition(tma_load_b,\n    get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n\n  bool is_mma_warp = (warp_idx == 0);\n  bool is_dma_warp = (warp_idx == 1);\n  bool is_epilogue_warp = (warp_idx >= 4 && warp_idx <= 7);\n\n  if (is_epilogue_warp && elect_one_sync()) {\n    cute::prefetch(raw_pointer_cast(global_amax));\n  }\n\n  typename MainloopPipeline::Params mainloop_pipeline_params;\n  if (is_dma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  if (is_mma_warp) {\n    mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n  mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n  mainloop_pipeline_params.initializing_warp = 0;\n  MainloopPipeline mainloop_pipeline(shared_storage.mainloop,\n                                       mainloop_pipeline_params,\n                                       cluster_shape,\n                                       cute::true_type{},   // Perform barrier init\n                                       cute::true_type{}); // Delay mask calculation\n\n  MainloopPipelineState mainloop_pipe_consumer_state;\n  MainloopPipelineState mainloop_pipe_producer_state = cutlass::make_producer_start_state<MainloopPipeline>();\n\n\n\n  using AccumulatorPipeline = cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n  AccumulatorPipelineState accumulator_pipe_consumer_state;\n  AccumulatorPipelineState accumulator_pipe_producer_state = cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n  typename AccumulatorPipeline::Params accumulator_pipeline_params;\n  if (is_mma_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n  }\n  if (is_epilogue_warp) {\n    accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n  }\n  // Only one producer thread arrives on this barrier.\n  accumulator_pipeline_params.producer_arv_count = 1;\n  accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * 128;\n  accumulator_pipeline_params.initializing_warp = 1;\n  AccumulatorPipeline accumulator_pipeline(shared_storage.accumulator,\n                                           accumulator_pipeline_params,\n                                           cluster_shape,\n                                           cute::true_type{},   // Perform barrier init\n                                           cute::true_type{}); // Delay mask calculation\n\n  if (warp_idx == 2 && elect_one_sync()) {\n    cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n  }\n  __syncthreads();\n  using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n  if (is_dma_warp) {\n    if (elect_one_sync()) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0], kTmaRhtTensorTransactionBytes);\n      copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_,0,0), tBsB(_,0));\n    }\n    cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n    do {\n      bool is_first_wave = linear_tile_idx == blockIdx.x;\n      uint32_t skip_wait = is_first_wave;\n      auto tAgA_mk = tAgA(_,tile_idx_m,_);\n      int k_tile = 0;\n      auto barrier_token = mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n\n\n      CUTE_NO_UNROLL\n      while (k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n) {\n        int k_tile_idx_n = tile_idx_n + k_tile;\n        ++k_tile;\n        skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n        mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state, barrier_token);\n        using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n        BarrierType* tma_barrier = mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n        int write_stage = mainloop_pipe_producer_state.index();\n        ++mainloop_pipe_producer_state;\n        barrier_token = mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n        if (cute::elect_one_sync()) {\n          copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_,k_tile_idx_n), tAsA(_,write_stage));\n        }\n      }\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n  } else if (is_mma_warp) {\n    mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n    __syncwarp();\n    tmem_allocation_result_barrier.arrive();\n    uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n    bulk_tmem_mma.data() = tmem_base_ptr;\n\n    do {\n      uint32_t skip_wait = K_TILE_MAX <= 0;\n      auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n      CUTE_NO_UNROLL\n      for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; )\n      {\n        mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n        int read_stage = mainloop_pipe_consumer_state.index();\n        auto tCrA_mk = tCrA(_,_,_,read_stage);\n        auto tCrB_nk = tCrB(_,_,0,0);\n        CUTE_UNROLL\n        for (int k_block = 0; k_block < size<2>(tCrA) / 4; ++k_block)\n        {\n          accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n          CUTE_UNROLL\n          for (int i = 0; i < 4; i++) {\n            auto accumulators = bulk_tmem_mma(_,_,_,accumulator_pipe_producer_state.index() * 4 + i);\n            gemm(mma, tCrA_mk(_,_,k_block * 4 + i), tCrB_nk, accumulators);\n          }\n\n          accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n          ++accumulator_pipe_producer_state;\n        }\n        auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n        ++mainloop_pipe_consumer_state;\n        ++k_tile;\n        skip_wait = k_tile >= K_TILE_MAX;\n        barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n        mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n      }\n\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    tmem_allocator.release_allocation_lock();\n    accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n    tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  } else if (is_epilogue_warp) {\n    const float global_amax_val = *global_amax;\n    static constexpr int FragmentSize = 256 / sizeof_bits_v<TC>;\n\n    tmem_allocation_result_barrier.arrive_and_wait();\n    uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n    bulk_tmem_epilogue.data() = tmem_base_ptr;\n    int thread_idx = threadIdx.x % 128;\n\n    Tensor tCgC = thr_mma_epilogue.partition_C(gC_mn);                             // (MMA,MMA_M,MMA_N)                             // (MMA,MMA_M,MMA_N)\n    auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_,_,_,_0{}));\n    auto tiled_r2g = make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TC>{}, tiled_t2r);\n    auto thr_t2r   = tiled_t2r.get_slice(thread_idx);\n    auto thr_r2g = tiled_r2g.get_slice(thread_idx);\n\n    // NVFP4 non-E8 recipe constants and global scales\n    static constexpr float fp4_max = 6.0f;\n\n    const float global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n    const float global_decode_scale = 1.0f / global_encode_scale;\n\n    // Scaling factor for fast math path\n    static constexpr float fp4_max_inv = 1.0f / fp4_max;\n    float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n    do {\n      for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; ++k_tile) {\n        Tensor tCgC_mn = tCgC(_,_,_,tile_idx_m,tile_idx_n+k_tile);\n\n        Tensor tCgSFC_mn = gSFC_mn(_,_,tile_idx_m,tile_idx_n+k_tile);\n        accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n        auto tCtC = bulk_tmem_epilogue(_,_,_,accumulator_pipe_consumer_state.index());\n        Tensor tDtC = thr_t2r.partition_S(tCtC);                   // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n        Tensor tDgC = thr_t2r.partition_D(tCgC_mn);                   // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n        Tensor tTR_rAcc = make_tensor<ElementAccumulator>(shape(tDgC));                 // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n        Tensor tDrC = make_tensor<TC>(shape(tDgC));\n        Tensor tTR_rAcc_frag = recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n        Tensor tDrC_frag = recast<cutlass::Array<TC, FragmentSize>>(coalesce(tDrC));\n\n        Tensor src = thr_r2g.retile_S(tDrC);\n        Tensor dst = thr_r2g.retile_D(tDgC);\n\n        Tensor tCgSFC = make_tensor(tCgSFC_mn.data(), make_layout(\n                                    make_shape(shape(tCgSFC_mn), Int<1>{}, Int<1>{}),\n                                    make_stride(stride(tCgSFC_mn), Int<0>{}, Int<0>{})\n                                   ));\n\n        Tensor tDgSFC = filter(thr_t2r.partition_D(tCgSFC));\n        Tensor tDrSFC = make_tensor<TSFC>(shape(tDgSFC));\n\n        static constexpr int NumVecs = size(tDgC) / VectorSize;\n        Tensor tC_rRowSFD_frg = recast<cutlass::Array<TSFC, NumVecs>>(tDrSFC);\n\n        cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>, true> amax_reduction;\n        cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n        cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n        // TMEM_LOAD\n        copy(tiled_t2r, tDtC, tTR_rAcc);\n        cutlass::arch::fence_view_async_tmem_load();\n\n        accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n\n        ++accumulator_pipe_consumer_state;\n\n        if constexpr (!kUseFastMath) {\n          // Downcast to BF16 for bit-wise compatibility with unfused\n          // kernels\n          auto convert_accum_to_bf16 = cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator, FragmentSize>{};\n          auto convert_bf16_to_accum = cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t, FragmentSize>{};\n          tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n        }\n\n        auto compute_frgs = reinterpret_cast<cutlass::Array< ElementAccumulator, VectorSize> *>(tTR_rAcc_frag.data());\n        auto output_frgs = reinterpret_cast<cutlass::Array< TC, VectorSize> *>(tDrC_frag.data());\n        CUTLASS_PRAGMA_UNROLL\n        for (int v = 0; v < NumVecs; v++) {\n          vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n        }\n\n        pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(vec_maxs, global_encode_scale_multiplier);\n        auto pvscales_cvted = cutlass::NumericArrayConverter<TSFC, ElementAccumulator, NumVecs>{}(pvscales);\n\n        tC_rRowSFD_frg(_0{}) = pvscales_cvted;\n        auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFC, NumVecs>{}(tC_rRowSFD_frg(_0{}));\n        auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(qpvscale_ups, global_decode_scale);\n        cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n        if constexpr (kUseFastMath) {\n          // Fast math: compute approximate reciprocal\n          acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n        } else {\n          // Accurate math: compute reciprocal with division\n          acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(1.0, qpvscale_scaled);\n        }\n\n        // Initialize RNG for tile\n        const size_t rng_sequence\n          = thread_idx + k_tile * 256 + linear_tile_idx * K_TILE_MAX * 256;\n\n        transformer_engine::curanddx::detail::philox4x32_native_state<NVTE_BUILD_NUM_PHILOX_ROUNDS>\n            rng;\n        rng.init(rng_seed, rng_sequence, rng_offset);\n        uint4 random_uint4 = uint4{0, 0, 0, 0};\n\n        CUTLASS_PRAGMA_UNROLL\n        for (int v = 0; v < NumVecs; v++) {\n          auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n          // auto acc_scale = acc_scales[v];\n          if constexpr (kEnableStochasticRounding) {\n            random_uint4 = rng.generate4();\n            output_frgs[v] = StochasticNumericConverter(\n              cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                compute_frgs[v],\n                acc_scale\n              ),\n              reinterpret_cast<cutlass::Array<uint32_t, 4>*>(&random_uint4));\n          } else {\n            output_frgs[v] = cutlass::NumericArrayConverter<TC, ElementAccumulator, VectorSize>{}(cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(compute_frgs[v], acc_scale));\n          }\n        }\n\n        copy(tiled_r2g, src, dst);\n\n        copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFC, tDgSFC);\n\n      }\n      linear_tile_idx += gridDim.x;\n      tile_idx_m = linear_tile_idx % tiles_in_m;\n      tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n    } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n  }\n  }\n}\n\n// this function computes RHT-GEMM for\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// C: m x n: row-major\n// SFC: m x (n/16): row-major\ntemplate <typename TA, typename TB, typename TC, typename TSFC, bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid\nrht_gemm_ntt_w_sfc(int m, int n,\n        TA const* A,\n        TB const* B,\n        TC      * C,\n        TSFC    * SFC,\n        float const* global_amax,\n        const size_t* rng_state,\n        uint32_t sm_count,\n        hipStream_t stream,\n        int k_tile_size = 2048)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = static_cast<int>(m);\n  auto N = static_cast<int>(n);\n\n  // Define strides (mixed)\n  auto dA = make_stride(Int<1>{}, m);  // (dM,dK)\n  auto dB = make_stride(Int<1>{}, 16);  // (dN,dK)\n  auto dC = make_stride(n, Int<1>{});  // (dM,dN)\n\n  auto cga_shape      = Shape<  _1,  _1, _1>{};\n  auto cga_tile_shape = Shape<_128,_16,_16>{};\n  auto cluster_tile_mainloop = Shape<_128,_16,_64>{};\n\n  // Construct the MMA\n  auto mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, float,\n                                               128, 16,\n                                               UMMA::Major::MN, UMMA::Major::MN>{},\n                            Layout<Shape<_1,_1>>{});\n\n  // MMA in CGA Layout XXX: Need to generalize synchro? {$nv-release-never}\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cga_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cga_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B = partition_shape_B(mma, make_shape(size<1>(cga_tile_shape), size<2>(cga_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(shape<0>(cga_tile_shape), shape_div(shape<0>(cga_tile_shape), size<0>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(shape<1>(cga_tile_shape), shape_div(shape<1>(cga_tile_shape), size<1>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cga_tile_shape));\n\n  using SmemLayoutAtomB = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TB, SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A = decltype(shape_div(shape<0>(cluster_tile_mainloop), shape_div(shape<0>(cluster_tile_mainloop), size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n      cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int kBlackwellSmemSize = 232448; // 232KB in bytes\n  constexpr int kBytesPerStage = cute::size(mma_shape_A) * sizeof(TA) + cute::size(mma_shape_B) * sizeof(TB);\n  constexpr int kReservedBytes = 256; // Reserve for barriers and other uses\n  constexpr int kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};      // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP)); // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{}, append(mma_shape_B, sP)); // (MMA,MMA_N,MMA_K,PIPE)\n  auto sC = Layout<_1>{};  // XXX Dummy\n\n  // Create GMEM tensors\n  Tensor tensorA = make_tensor(A, make_layout(make_shape(M,N), dA));      // (M,N)\n  Tensor tensorB = make_tensor(B, make_layout(make_shape(16,16), dB));      // (16,16)\n\n  // Create the TiledCopy\n\n  auto tma_load_a = make_tma_copy_A_sm100(\n        SM90_TMA_LOAD{},\n        tensorA,\n        sA(_,_,_,0),\n        cluster_tile_mainloop,\n        mma);\n  auto tma_load_b =  make_tma_copy_B_sm100(\n        SM90_TMA_LOAD{},\n        tensorB,\n        sB(_,_,_,0),\n        cga_tile_shape,\n        mma);\n\n  // Assert checks on tile sizes -- no predication\n  NVTE_CHECK(M % size<0>(cga_tile_shape) == 0,\n             \"Inner dimension must be divisible by \", static_cast<size_t>(size<0>(cga_tile_shape)), \" but got \", M, \".\");\n  NVTE_CHECK(N % (4 * size<1>(cga_tile_shape)) == 0,\n             \"Outer dimension must be divisible by \", 4 * static_cast<size_t>(size<1>(cga_tile_shape)),\n             \" but got \", N, \".\");\n\n  uint32_t tiles = size(ceil_div(M, get<0>(cga_tile_shape))) * size(ceil_div(N, k_tile_size));\n\n  tiles = (tiles < sm_count) ? tiles : sm_count;\n\n  dim3 dimBlock(256);\n  dim3 dimCluster(size<0>(cga_shape), size<1>(cga_shape), size<2>(cga_shape));\n  dim3 dimGrid(tiles, 1, 1);\n\n  int smem_size = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n  auto* kernel_ptr = &rht_gemm_device<\n                                  decltype(M), decltype(N), decltype(k_tile_size), decltype(cga_tile_shape),\n                                  TA, decltype(dA), decltype(sA), decltype(tma_load_a),\n                                  TB, decltype(dB), decltype(sB), decltype(tma_load_b),\n                                  TC, decltype(dC), decltype(sC),\n                                  TSFC,\n                                  decltype(mma),\n                                  kEnableStochasticRounding,\n                                  kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      hipFuncSetAttribute(reinterpret_cast<const void*>(*kernel_ptr),\n                           hipFuncAttributeMaxDynamicSharedMemorySize,\n                           smem_size)\n  );\n\n  (*kernel_ptr)\n      <<< dimGrid, dimBlock, smem_size, stream >>>\n      (M,  N,  k_tile_size, cga_tile_shape,\n       A, dA, sA, tma_load_a,\n       B, dB, sB, tma_load_b,\n       C, dC, sC,\n       SFC,\n       mma, global_amax,\n       rng_state);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n// this function is used to wrap the rht_gemm_ntt_w_sfc function\n//to transpose the input tensor A\ntemplate <typename TA, typename TB, typename TC, typename TSFC, bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid\nrht_gemm_ttt_wrapper(int m, int n,\n        TA const* A,\n        TB const* B,\n        TC      * C,\n        TSFC    * SFC,\n        float const* global_amax,\n        const size_t* rng_state,\n        uint32_t sm_count,\n        hipStream_t stream,\n        int k_tile_size = 1024)\n{\n  // in addition to transpose the input tensor A\n  // we also need to reshape m, n to at best\n  // ultilize as many SMs as possible while keeping\n  // a relatively large contiguous dimension.\n  // for example, after swapping m, n for transpose purposes,\n  // the input / output tensor shapes for RHT-GEMM are:\n  // A: n x m: col-major\n  // B: 16 x 16: row-major\n  // C: n x m: row-major\n  // SFC: n x (m/16): row-major\n  rht_gemm_ntt_w_sfc<TA, TB, TC, TSFC, kEnableStochasticRounding, kUseFastMath>(\n    n, m,\n    A, B, C,\n    SFC, global_amax,\n    rng_state,\n    sm_count, stream,\n    k_tile_size);\n}\n\n}  // namespace\n}  // namespace detail\n\n// clang-format on\n\nvoid hadamard_transform_cast_fusion_columnwise(const Tensor &input_, Tensor &output_,\n                                               const Tensor &hadamard_matrix_,\n                                               QuantizationConfig quant_config,\n                                               hipStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_cast_fusion_columnwise);\n\n  // Check input and output tensors\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  NVTE_CHECK(!output_.with_gemm_swizzled_scales, \"Output must have scales in compact format.\");\n  const SimpleTensor &input = input_.data;\n  SimpleTensor &global_amax = output_.amax;\n  SimpleTensor &output_t = output_.data;\n  SimpleTensor &scale_inv_t = output_.scale_inv;\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TC = cutlass::float_e2m1_t;\n  using TSFC = cutlass::float_ue4m3_t;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(hadamard_matrix_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Hadamard matrix must be BF16 tensor, but scaling mode is \",\n             to_string(hadamard_matrix_.scaling_mode), \".\");\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  if (m == 8192 && n == 5120) {\n    k_tile_size = 512;\n  } else if (m == 8192 && n == 10240) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 2560) {\n    k_tile_size = 1280;\n  } else if (m == 8192 && n == 11328) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 512) {\n    k_tile_size = 256;\n  } else if (m == 8192 && n == 3584) {\n    k_tile_size = 512;\n  } else if (m == 11328 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 5120 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m == 10240 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 2560 && n == 8192) {\n    k_tile_size = 1280;\n  } else if (m == 512 && n == 8192) {\n    k_tile_size = 256;\n  } else if (m == 3584 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m < 1024 || n < 1024) {\n    k_tile_size = 512;\n  }\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kUseStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          quant_config.use_fast_math, kUseFastMath,\n          detail::rht_gemm_ttt_wrapper<TA, TB, TC, TSFC, kUseStochasticRounding, kUseFastMath>(\n              /*m=*/m,\n              /*n=*/n,\n              /*A=*/reinterpret_cast<TA const *>(input.dptr),\n              /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n              /*C=*/reinterpret_cast<TC *>(output_t.dptr),\n              /*SFC=*/reinterpret_cast<TSFC *>(scale_inv_t.dptr),\n              /*global_amax=*/reinterpret_cast<float const *>(global_amax.dptr),\n              /*rng_state=*/rng_state,\n              /*sm_count=*/sm_count,\n              /*stream=*/stream,\n              /*k_tile_size=*/k_tile_size);););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_hadamard_transform_cast_fusion_columnwise(const NVTETensor input, NVTETensor output,\n                                                    const NVTETensor hadamard_matrix,\n                                                    const NVTEQuantizationConfig quant_config,\n                                                    hipStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform_cast_fusion_columnwise);\n  using namespace transformer_engine;\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n  hadamard_transform_cast_fusion_columnwise(\n      *convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n      *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/hadamard_transform_cast_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kThreadsPerWarp = 32;\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n__launch_bounds__(1) __global__ void ZeroAmaxKernel(float* __restrict__ output_pre_rht_amax_ptr,\n                                                    float* __restrict__ output_identity_amax_ptr,\n                                                    float* __restrict__ output_transpose_amax_ptr) {\n  if (output_pre_rht_amax_ptr != nullptr) {\n    *output_pre_rht_amax_ptr = 0;\n  }\n  if (output_identity_amax_ptr != nullptr) {\n    *output_identity_amax_ptr = 0;\n  }\n  if (output_transpose_amax_ptr != nullptr) {\n    *output_transpose_amax_ptr = 0;\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void HadamardAmaxTmaKernel(const __grid_constant__ CUtensorMap tensor_map_input,\n                                      float* __restrict__ output_pre_rht_amax_ptr,\n                                      float* __restrict__ output_identity_amax_ptr,\n                                      float* __restrict__ output_transpose_amax_ptr,\n                                      uint16_t random_sign_mask, uint16_t random_sign_mask_t,\n                                      uint64_t num_rows, uint64_t row_length) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\ntemplate <typename T, int kHadamardDimension, bool kComputeIdentity, bool kComputeTransposed,\n          bool kReturnIdentity, bool kReturnTransposed, bool kUpdateIdentityAmax,\n          bool kUpdateTransposeAmax, bool kOutputTrueTransposed>\n__global__ void HadamardTransformKernel(const T* __restrict__ input, T* __restrict__ output,\n                                        T* __restrict__ output_t, uint16_t random_sign_mask,\n                                        uint16_t random_sign_mask_t, uint64_t num_input_rows,\n                                        uint64_t num_input_cols, float* __restrict__ amax,\n                                        float* __restrict__ amax_t, bool inverse_hadamard) {\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 900\n  static_assert(kHadamardDimension == 16, \"Currently only hadamard dimension 16 is supported.\");\n\n  // The whole threadblock will share the same smem.\n  extern __shared__ __align__(16) T smem[];\n\n  // Each 32 threads process a 16x16 matrix. There is a (y, z) grid of 16x16.\n  // If y = 4, z = 4, then each threadblock is processing a 4x4 grid of 16x16 matrices.\n  int32_t tid = threadIdx.x;\n  int32_t warp_id = threadIdx.y * blockDim.z + threadIdx.z;\n  int32_t local_bx = threadIdx.y;\n  int32_t local_by = threadIdx.z;\n\n  // Define the register fragments\n  uint32_t a_frag[4];    // A matrix fragment\n  uint32_t b_frag_i[4];  // Transposed Hadamard matrix fragment, used for A @ B(col major)\n  uint32_t b_frag_t[4];  // Hadamard matrix fragment, used for A.T @ B.T(col major)\n  uint32_t c_frag[4];    // Result fragment\n\n  // row and col for each thread. 32 threads will work together in 128 chunk to\n  // load the data from global memory to shared memory.\n  uint32_t row = tid / (kHadamardDimension * sizeof(T) / sizeof(uint4));\n  uint32_t col = tid % (kHadamardDimension * sizeof(T) / sizeof(uint4));\n\n  uint32_t smem_index = tid;\n\n  uint32_t input_start_col = (blockIdx.x * blockDim.y + local_bx) * kHadamardDimension;\n  uint32_t input_start_row = (blockIdx.y * blockDim.z + local_by) * kHadamardDimension;\n\n  bool load = (input_start_col < num_input_cols) && (input_start_row < num_input_rows);\n  if (!load) {\n    // Out of bound, we are returning early. No thread divergence since the whole warp\n    // will return early.\n    return;\n  }\n\n  uint64_t global_offset = input_start_col + input_start_row * num_input_cols;\n  uint64_t global_offset_t =\n      kOutputTrueTransposed ? (input_start_row + input_start_col * num_input_rows) : global_offset;\n\n  T* base_smem = smem + kHadamardDimension * kHadamardDimension * warp_id;\n\n  uint32_t* smem_b32 = reinterpret_cast<uint32_t*>(base_smem);\n  uint4* smem_b128 = reinterpret_cast<uint4*>(base_smem);\n\n  // Asynchronously load the data from global memory to shared memory.\n  const uint4* input_b128 = reinterpret_cast<const uint4*>(input + global_offset);\n  // Each 16x16 chunk is divided into 4 8x8 matrices, we are trying to load each\n  // 8x8 chunks consecutively into the smem, so we could leverage ldmatrix m8n8x4\n  // to load the data in the tensor core swizzled format.\n  __pipeline_memcpy_async(&smem_b128[smem_index],\n                          &input_b128[row * num_input_cols / (sizeof(uint4) / sizeof(T)) + col],\n                          sizeof(uint4));\n  __pipeline_commit();  // Commit the memcpy. Wait when we are in the computation.\n\n  if (inverse_hadamard) {\n    get_hadamard_matrix_fragment<kComputeIdentity, kComputeTransposed,\n                                 /*kInverseHadamard=*/true,\n                                 /*kInverseHadamardTransposed=*/true>(b_frag_i, random_sign_mask,\n                                                                      b_frag_t, random_sign_mask_t);\n  } else {\n    get_hadamard_matrix_fragment<kComputeIdentity, kComputeTransposed,\n                                 /*kInverseHadamard=*/false,\n                                 /*kInverseHadamardTransposed=*/false>(\n        b_frag_i, random_sign_mask, b_frag_t, random_sign_mask_t);\n  }\n\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n  __pipeline_wait_prior(0);\n\n  __syncwarp();  // ensure all lanes finished their cp.async before reading smem\n\n  // Load the A to a_frag.\n  if constexpr (kComputeIdentity) {\n    load_matrix_16x16_from_shared<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3], smem_b32,\n                                         kHadamardDimension);\n\n    // 16x16 @ 16x16 leveraging all threads in the warp.\n    mma_m16_n16_k16_b16_b16_b16_noacc<kUpdateIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], local_amax_reg);\n\n    // Store the result to the shared memory in non-transposed order.\n    if constexpr (kReturnIdentity) {\n      uint4* output_b128 = reinterpret_cast<uint4*>(output + global_offset);\n      store_matrix_16x16_to_global<false>(c_frag[0], c_frag[1], c_frag[2], c_frag[3], output_b128,\n                                          num_input_cols);\n    }\n  }\n\n  if constexpr (kComputeTransposed) {\n    if (kComputeIdentity) {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    } else {\n      load_matrix_16x16_from_shared<true>(a_frag[0],\n                                          a_frag[2],  // NOTE: intentional index swapping\n                                          a_frag[1],  // NOTE: intentional index swapping\n                                          a_frag[3], smem_b32, kHadamardDimension);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kUpdateTransposeAmax>(\n        a_frag[0],\n        // 2,1 is used if we are using movmatrix instruction.\n        // Thus loading the matrix in 2,1 order will just be normal.\n        // This is to be compatible with the movmatrix instruction.\n        a_frag[2],  // NOTE: intentional index swapping for transpose purpose.\n        a_frag[1],  // NOTE: intentional index swapping for transpose purpose.\n        a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2], b_frag_t[3], c_frag[0], c_frag[1],\n        c_frag[2], c_frag[3], local_amax_t_reg);\n\n    // Store the result to the shared memory in non-transposed order.\n    if constexpr (kReturnTransposed) {\n      uint4* output_t_b128 = reinterpret_cast<uint4*>(output_t + global_offset_t);\n      store_matrix_16x16_to_global<!kOutputTrueTransposed>(\n          c_frag[0], c_frag[1], c_frag[2], c_frag[3], output_t_b128,\n          kOutputTrueTransposed ? num_input_rows : num_input_cols);\n    }\n  }\n\n  if constexpr (kUpdateIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n    local_amax = warp_reduce_max<kThreadsPerWarp>(local_amax);\n    // broadcast the amax to all threads in a warp from the lane 0\n    constexpr int lane_zero = 0;\n    local_amax = __shfl_sync(0xFFFFFFFF, local_amax, lane_zero);\n    // atomic CAS to output memory.\n    if (tid % kThreadsPerWarp == 0) {\n      atomicMaxFloat(amax, local_amax);\n    }\n  }\n  if constexpr (kUpdateTransposeAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n    local_amax_t = warp_reduce_max<kThreadsPerWarp>(local_amax_t);\n    // broadcast the amax to all threads in a warp from the lane 0\n    constexpr int lane_zero = 0;\n    local_amax_t = __shfl_sync(0xFFFFFFFF, local_amax_t, lane_zero);\n    // atomic CAS to output memory.\n    if (tid % kThreadsPerWarp == 0) {\n      atomicMaxFloat(amax_t, local_amax_t);\n    }\n  }\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 9.0+.\");\n#endif  // defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 900\n}\n\n}  // namespace\n\nvoid hadamard_transform(const Tensor& input_, Tensor& output_, uint16_t random_sign_mask,\n                        uint16_t random_sign_mask_t, cudaStream_t stream) {\n  NVTE_API_CALL(hadamard_transform);\n\n  // Check tensors\n  // NOTE (frsun): This is non-intuitive, we are writing the result of\n  // transposed RHT to the output of rowwise.\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  NVTE_CHECK(output_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be simple tensor, but scaling mode is \",\n             to_string(output_.scaling_mode), \".\");\n  const SimpleTensor& input = input_.data;\n  SimpleTensor output;\n  SimpleTensor& output_t = output_.data;\n\n  // Check requested outputs\n  const bool return_identity = output.dptr != nullptr;\n  const bool return_transposed = output_t.dptr != nullptr;\n  if (!return_identity && !return_transposed) {  // Nothing to do/ill-defined behavior.\n    return;\n  }\n\n  checkCuDriverContext(stream);\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  using IType = bf16;\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  constexpr uint64_t kThreadBlockX = 4;\n  // Configure 4 is used for Hopper, 8 is used for Blackwell for extra memory bandwidth.\n  constexpr uint64_t kThreadBlockY = 4;\n\n  uint64_t kNumWarpsPerSM = kThreadBlockX * kThreadBlockY;\n\n  // The shared memory number of bytes required for **the whole threadblock**.\n  size_t shmem_bytes = kHadamardDimension * kHadamardDimension * sizeof(IType) * kNumWarpsPerSM;\n\n  dim3 block(kThreadsPerWarp, kThreadBlockX, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length / kHadamardDimension, kThreadBlockX),\n            DIVUP(num_rows / kHadamardDimension, kThreadBlockY));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      return_transposed, kReturnTransposed,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          return_identity, kReturnIdentity,\n\n          auto kernel =\n              HadamardTransformKernel<IType, kHadamardDimension, kReturnIdentity, kReturnTransposed,\n                                      kReturnIdentity, kReturnTransposed, false, false, true>;\n\n          cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shmem_bytes);\n\n          kernel<<<grid, block, shmem_bytes, stream>>>(\n              reinterpret_cast<const IType*>(input.dptr), reinterpret_cast<IType*>(output.dptr),\n              reinterpret_cast<IType*>(output_t.dptr), random_sign_mask, random_sign_mask_t,\n              num_rows, row_length, nullptr, nullptr, false);););\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n// Kernel that will apply the 16x16 hadamard transform the input and input.T, and then\n// get the absolute max value of the result.\nvoid hadamard_transform_amax(const Tensor& input_, Tensor& output_, uint16_t random_sign_mask,\n                             uint16_t random_sign_mask_t, cudaStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_amax);\n#if CUDA_VERSION >= 12080\n\n  // Check input tensor\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor& input = input_.data;\n\n  // Check amax tensors\n  SimpleTensor& output_pre_rht_amax = output_.amax;\n  SimpleTensor output_identity_amax;\n  SimpleTensor& output_transpose_amax = output_.columnwise_amax;\n\n  // Check requested outputs\n  const bool return_pre_rht_amax = output_pre_rht_amax.dptr != nullptr;\n  const bool return_identity_amax = output_identity_amax.dptr != nullptr;\n  const bool return_transposed_amax = output_transpose_amax.dptr != nullptr;\n  if (!return_identity_amax && !return_transposed_amax &&\n      !return_pre_rht_amax) {  // Nothing to do/ill-defined behavior.\n    return;\n  }\n\n  // Zero out amaxes if needed\n  ZeroAmaxKernel<<<1, 1, 0, stream>>>(reinterpret_cast<float*>(output_pre_rht_amax.dptr),\n                                      reinterpret_cast<float*>(output_identity_amax.dptr),\n                                      reinterpret_cast<float*>(output_transpose_amax.dptr));\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  checkCuDriverContext(stream);\n\n  using IType = bf16;\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  constexpr uint64_t kChunkBlockXSmall = 128;\n  constexpr uint64_t kChunkBlockYSmall = 128;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input,\n      /*globalY=*/num_rows,\n      /*globalX=*/row_length,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/row_length,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length, kChunkBlockXSmall), DIVUP(num_rows, kChunkBlockYSmall));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      return_transposed_amax, kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          return_identity_amax, kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = HadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(\n                  tensor_map_input, reinterpret_cast<float*>(output_pre_rht_amax.dptr),\n                  reinterpret_cast<float*>(output_identity_amax.dptr),\n                  reinterpret_cast<float*>(output_transpose_amax.dptr), random_sign_mask,\n                  random_sign_mask_t, num_rows, row_length);)));\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_hadamard_transform(const NVTETensor input, NVTETensor output, int random_sign_mask,\n                             int random_sign_mask_t, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform);\n  using namespace transformer_engine;\n  hadamard_transform(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                     static_cast<uint16_t>(random_sign_mask),\n                     static_cast<uint16_t>(random_sign_mask_t), stream);\n}\n\nvoid nvte_hadamard_transform_amax(const NVTETensor input, NVTETensor output, int random_sign_mask,\n                                  int random_sign_mask_t, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform_amax);\n  using namespace transformer_engine;\n  hadamard_transform_amax(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                          static_cast<uint16_t>(random_sign_mask),\n                          static_cast<uint16_t>(random_sign_mask_t), stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kThreadsPerWarp = 32;\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n__launch_bounds__(1) __global__ void ZeroAmaxKernel(float* __restrict__ output_pre_rht_amax_ptr,\n                                                    float* __restrict__ output_identity_amax_ptr,\n                                                    float* __restrict__ output_transpose_amax_ptr) {\n  if (output_pre_rht_amax_ptr != nullptr) {\n    *output_pre_rht_amax_ptr = 0;\n  }\n  if (output_identity_amax_ptr != nullptr) {\n    *output_identity_amax_ptr = 0;\n  }\n  if (output_transpose_amax_ptr != nullptr) {\n    *output_transpose_amax_ptr = 0;\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void HadamardAmaxTmaKernel(const __grid_constant__ CUtensorMap tensor_map_input,\n                                      float* __restrict__ output_pre_rht_amax_ptr,\n                                      float* __restrict__ output_identity_amax_ptr,\n                                      float* __restrict__ output_transpose_amax_ptr,\n                                      uint16_t random_sign_mask, uint16_t random_sign_mask_t,\n                                      uint64_t num_rows, uint64_t row_length) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\ntemplate <typename T, int kHadamardDimension, bool kComputeIdentity, bool kComputeTransposed,\n          bool kReturnIdentity, bool kReturnTransposed, bool kUpdateIdentityAmax,\n          bool kUpdateTransposeAmax, bool kOutputTrueTransposed>\n__global__ void HadamardTransformKernel(const T* __restrict__ input, T* __restrict__ output,\n                                        T* __restrict__ output_t, uint16_t random_sign_mask,\n                                        uint16_t random_sign_mask_t, uint64_t num_input_rows,\n                                        uint64_t num_input_cols, float* __restrict__ amax,\n                                        float* __restrict__ amax_t, bool inverse_hadamard) {\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 900\n  static_assert(kHadamardDimension == 16, \"Currently only hadamard dimension 16 is supported.\");\n\n  // The whole threadblock will share the same smem.\n  extern __shared__ __align__(16) T smem[];\n\n  // Each 32 threads process a 16x16 matrix. There is a (y, z) grid of 16x16.\n  // If y = 4, z = 4, then each threadblock is processing a 4x4 grid of 16x16 matrices.\n  int32_t tid = threadIdx.x;\n  int32_t warp_id = threadIdx.y * blockDim.z + threadIdx.z;\n  int32_t local_bx = threadIdx.y;\n  int32_t local_by = threadIdx.z;\n\n  // Define the register fragments\n  uint32_t a_frag[4];    // A matrix fragment\n  uint32_t b_frag_i[4];  // Transposed Hadamard matrix fragment, used for A @ B(col major)\n  uint32_t b_frag_t[4];  // Hadamard matrix fragment, used for A.T @ B.T(col major)\n  uint32_t c_frag[4];    // Result fragment\n\n  // row and col for each thread. 32 threads will work together in 128 chunk to\n  // load the data from global memory to shared memory.\n  uint32_t row = tid / (kHadamardDimension * sizeof(T) / sizeof(uint4));\n  uint32_t col = tid % (kHadamardDimension * sizeof(T) / sizeof(uint4));\n\n  uint32_t smem_index = tid;\n\n  uint32_t input_start_col = (blockIdx.x * blockDim.y + local_bx) * kHadamardDimension;\n  uint32_t input_start_row = (blockIdx.y * blockDim.z + local_by) * kHadamardDimension;\n\n  bool load = (input_start_col < num_input_cols) && (input_start_row < num_input_rows);\n  if (!load) {\n    // Out of bound, we are returning early. No thread divergence since the whole warp\n    // will return early.\n    return;\n  }\n\n  uint64_t global_offset = input_start_col + input_start_row * num_input_cols;\n  uint64_t global_offset_t =\n      kOutputTrueTransposed ? (input_start_row + input_start_col * num_input_rows) : global_offset;\n\n  T* base_smem = smem + kHadamardDimension * kHadamardDimension * warp_id;\n\n  uint32_t* smem_b32 = reinterpret_cast<uint32_t*>(base_smem);\n  uint4* smem_b128 = reinterpret_cast<uint4*>(base_smem);\n\n  // Asynchronously load the data from global memory to shared memory.\n  const uint4* input_b128 = reinterpret_cast<const uint4*>(input + global_offset);\n  // Each 16x16 chunk is divided into 4 8x8 matrices, we are trying to load each\n  // 8x8 chunks consecutively into the smem, so we could leverage ldmatrix m8n8x4\n  // to load the data in the tensor core swizzled format.\n  __pipeline_memcpy_async(&smem_b128[smem_index],\n                          &input_b128[row * num_input_cols / (sizeof(uint4) / sizeof(T)) + col],\n                          sizeof(uint4));\n  __pipeline_commit();  // Commit the memcpy. Wait when we are in the computation.\n\n  if (inverse_hadamard) {\n    get_hadamard_matrix_fragment<kComputeIdentity, kComputeTransposed,\n                                 /*kInverseHadamard=*/true,\n                                 /*kInverseHadamardTransposed=*/true>(b_frag_i, random_sign_mask,\n                                                                      b_frag_t, random_sign_mask_t);\n  } else {\n    get_hadamard_matrix_fragment<kComputeIdentity, kComputeTransposed,\n                                 /*kInverseHadamard=*/false,\n                                 /*kInverseHadamardTransposed=*/false>(\n        b_frag_i, random_sign_mask, b_frag_t, random_sign_mask_t);\n  }\n\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n  __pipeline_wait_prior(0);\n\n  __syncwarp();  // ensure all lanes finished their cp.async before reading smem\n\n  // Load the A to a_frag.\n  if constexpr (kComputeIdentity) {\n    load_matrix_16x16_from_shared<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3], smem_b32,\n                                         kHadamardDimension);\n\n    // 16x16 @ 16x16 leveraging all threads in the warp.\n    mma_m16_n16_k16_b16_b16_b16_noacc<kUpdateIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], local_amax_reg);\n\n    // Store the result to the shared memory in non-transposed order.\n    if constexpr (kReturnIdentity) {\n      uint4* output_b128 = reinterpret_cast<uint4*>(output + global_offset);\n      store_matrix_16x16_to_global<false>(c_frag[0], c_frag[1], c_frag[2], c_frag[3], output_b128,\n                                          num_input_cols);\n    }\n  }\n\n  if constexpr (kComputeTransposed) {\n    if (kComputeIdentity) {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    } else {\n      load_matrix_16x16_from_shared<true>(a_frag[0],\n                                          a_frag[2],  // NOTE: intentional index swapping\n                                          a_frag[1],  // NOTE: intentional index swapping\n                                          a_frag[3], smem_b32, kHadamardDimension);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kUpdateTransposeAmax>(\n        a_frag[0],\n        // 2,1 is used if we are using movmatrix instruction.\n        // Thus loading the matrix in 2,1 order will just be normal.\n        // This is to be compatible with the movmatrix instruction.\n        a_frag[2],  // NOTE: intentional index swapping for transpose purpose.\n        a_frag[1],  // NOTE: intentional index swapping for transpose purpose.\n        a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2], b_frag_t[3], c_frag[0], c_frag[1],\n        c_frag[2], c_frag[3], local_amax_t_reg);\n\n    // Store the result to the shared memory in non-transposed order.\n    if constexpr (kReturnTransposed) {\n      uint4* output_t_b128 = reinterpret_cast<uint4*>(output_t + global_offset_t);\n      store_matrix_16x16_to_global<!kOutputTrueTransposed>(\n          c_frag[0], c_frag[1], c_frag[2], c_frag[3], output_t_b128,\n          kOutputTrueTransposed ? num_input_rows : num_input_cols);\n    }\n  }\n\n  if constexpr (kUpdateIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n    local_amax = warp_reduce_max<kThreadsPerWarp>(local_amax);\n    // broadcast the amax to all threads in a warp from the lane 0\n    constexpr int lane_zero = 0;\n    local_amax = __shfl_sync(0xFFFFFFFF, local_amax, lane_zero);\n    // atomic CAS to output memory.\n    if (tid % kThreadsPerWarp == 0) {\n      atomicMaxFloat(amax, local_amax);\n    }\n  }\n  if constexpr (kUpdateTransposeAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n    local_amax_t = warp_reduce_max<kThreadsPerWarp>(local_amax_t);\n    // broadcast the amax to all threads in a warp from the lane 0\n    constexpr int lane_zero = 0;\n    local_amax_t = __shfl_sync(0xFFFFFFFF, local_amax_t, lane_zero);\n    // atomic CAS to output memory.\n    if (tid % kThreadsPerWarp == 0) {\n      atomicMaxFloat(amax_t, local_amax_t);\n    }\n  }\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 9.0+.\");\n#endif  // defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 900\n}\n\n}  // namespace\n\nvoid hadamard_transform(const Tensor& input_, Tensor& output_, uint16_t random_sign_mask,\n                        uint16_t random_sign_mask_t, hipStream_t stream) {\n  NVTE_API_CALL(hadamard_transform);\n\n  // Check tensors\n  // NOTE (frsun): This is non-intuitive, we are writing the result of\n  // transposed RHT to the output of rowwise.\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  NVTE_CHECK(output_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Output tensor must be simple tensor, but scaling mode is \",\n             to_string(output_.scaling_mode), \".\");\n  const SimpleTensor& input = input_.data;\n  SimpleTensor output;\n  SimpleTensor& output_t = output_.data;\n\n  // Check requested outputs\n  const bool return_identity = output.dptr != nullptr;\n  const bool return_transposed = output_t.dptr != nullptr;\n  if (!return_identity && !return_transposed) {  // Nothing to do/ill-defined behavior.\n    return;\n  }\n\n  checkCuDriverContext(stream);\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  using IType = bf16;\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  constexpr uint64_t kThreadBlockX = 4;\n  // Configure 4 is used for Hopper, 8 is used for Blackwell for extra memory bandwidth.\n  constexpr uint64_t kThreadBlockY = 4;\n\n  uint64_t kNumWarpsPerSM = kThreadBlockX * kThreadBlockY;\n\n  // The shared memory number of bytes required for **the whole threadblock**.\n  size_t shmem_bytes = kHadamardDimension * kHadamardDimension * sizeof(IType) * kNumWarpsPerSM;\n\n  dim3 block(kThreadsPerWarp, kThreadBlockX, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length / kHadamardDimension, kThreadBlockX),\n            DIVUP(num_rows / kHadamardDimension, kThreadBlockY));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      return_transposed, kReturnTransposed,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          return_identity, kReturnIdentity,\n\n          auto kernel =\n              HadamardTransformKernel<IType, kHadamardDimension, kReturnIdentity, kReturnTransposed,\n                                      kReturnIdentity, kReturnTransposed, false, false, true>;\n\n          hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shmem_bytes);\n\n          kernel<<<grid, block, shmem_bytes, stream>>>(\n              reinterpret_cast<const IType*>(input.dptr), reinterpret_cast<IType*>(output.dptr),\n              reinterpret_cast<IType*>(output_t.dptr), random_sign_mask, random_sign_mask_t,\n              num_rows, row_length, nullptr, nullptr, false);););\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n// Kernel that will apply the 16x16 hadamard transform the input and input.T, and then\n// get the absolute max value of the result.\nvoid hadamard_transform_amax(const Tensor& input_, Tensor& output_, uint16_t random_sign_mask,\n                             uint16_t random_sign_mask_t, hipStream_t stream) {\n  NVTE_API_CALL(hadamard_transform_amax);\n#if CUDA_VERSION >= 12080\n\n  // Check input tensor\n  NVTE_CHECK(input_.scaling_mode == NVTE_DELAYED_TENSOR_SCALING,\n             \"Input tensor must be BF16 tensor, but scaling mode is \",\n             to_string(input_.scaling_mode), \".\");\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor& input = input_.data;\n\n  // Check amax tensors\n  SimpleTensor& output_pre_rht_amax = output_.amax;\n  SimpleTensor output_identity_amax;\n  SimpleTensor& output_transpose_amax = output_.columnwise_amax;\n\n  // Check requested outputs\n  const bool return_pre_rht_amax = output_pre_rht_amax.dptr != nullptr;\n  const bool return_identity_amax = output_identity_amax.dptr != nullptr;\n  const bool return_transposed_amax = output_transpose_amax.dptr != nullptr;\n  if (!return_identity_amax && !return_transposed_amax &&\n      !return_pre_rht_amax) {  // Nothing to do/ill-defined behavior.\n    return;\n  }\n\n  // Zero out amaxes if needed\n  ZeroAmaxKernel<<<1, 1, 0, stream>>>(reinterpret_cast<float*>(output_pre_rht_amax.dptr),\n                                      reinterpret_cast<float*>(output_identity_amax.dptr),\n                                      reinterpret_cast<float*>(output_transpose_amax.dptr));\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  checkCuDriverContext(stream);\n\n  using IType = bf16;\n\n  const size_t ndim = input.shape.size();\n  const size_t row_length = input.shape[ndim - 1];\n  size_t num_rows = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    num_rows *= input.shape[i];\n  }\n\n  constexpr int kHadamardDimension = 16;\n  NVTE_CHECK(row_length % kHadamardDimension == 0,\n             \"row_length must be divisible by hadamard_dimension.\");\n  NVTE_CHECK(num_rows % kHadamardDimension == 0,\n             \"num_rows must be divisible by hadamard_dimension\");\n\n  constexpr uint64_t kChunkBlockXSmall = 128;\n  constexpr uint64_t kChunkBlockYSmall = 128;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input,\n      /*globalY=*/num_rows,\n      /*globalX=*/row_length,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/row_length,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n\n  dim3 grid(DIVUP(row_length, kChunkBlockXSmall), DIVUP(num_rows, kChunkBlockYSmall));\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      return_transposed_amax, kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          return_identity_amax, kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = HadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(\n                  tensor_map_input, reinterpret_cast<float*>(output_pre_rht_amax.dptr),\n                  reinterpret_cast<float*>(output_identity_amax.dptr),\n                  reinterpret_cast<float*>(output_transpose_amax.dptr), random_sign_mask,\n                  random_sign_mask_t, num_rows, row_length);)));\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_hadamard_transform(const NVTETensor input, NVTETensor output, int random_sign_mask,\n                             int random_sign_mask_t, hipStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform);\n  using namespace transformer_engine;\n  hadamard_transform(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                     static_cast<uint16_t>(random_sign_mask),\n                     static_cast<uint16_t>(random_sign_mask_t), stream);\n}\n\nvoid nvte_hadamard_transform_amax(const NVTETensor input, NVTETensor output, int random_sign_mask,\n                                  int random_sign_mask_t, hipStream_t stream) {\n  NVTE_API_CALL(nvte_hadamard_transform_amax);\n  using namespace transformer_engine;\n  hadamard_transform_amax(*convertNVTETensorCheck(input), *convertNVTETensorCheck(output),\n                          static_cast<uint16_t>(random_sign_mask),\n                          static_cast<uint16_t>(random_sign_mask_t), stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/hadamard_transform.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <transformer_engine/hadamard_transform.h>\n#include <transformer_engine/multi_tensor.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kMaxTensorsPerKernel = 64;\nconstexpr int kThreadsPerWarp = 32;\n\n__device__ __forceinline__ size_t get_current_tensor_id(\n    const ShapeRepresentation shape_rep, const size_t num_tensors, const size_t current_offset,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t* const __restrict__ offsets_ptr) {\n  if (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS) {\n    const size_t current_row = current_offset / last_logical_dim;\n    const size_t rows_per_tensor = first_logical_dim / num_tensors;\n    return current_row / rows_per_tensor;\n  } else {\n    // upper_bound(offsets, current_offset) - 1 in range i in [0..num_tensors)\n    size_t low = 0;\n    size_t hi = num_tensors;  // half-open [low, hi)\n\n    while (low < hi) {\n      const size_t mid = low + (hi - low) / 2;\n      const size_t mid_offset = static_cast<size_t>(offsets_ptr[mid]);\n\n      if (mid_offset <= current_offset) {\n        low = mid + 1;\n      } else {\n        hi = mid;\n      }\n    }\n\n    // low = first index where offsets[low] > current_offset (or low == num_tensors)\n    // id = low - 1, but need to evaluate if current_offset < offsets[0]\n    return (low == 0) ? 0 : (low - 1);\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n__global__ void GraphSafeMultiZeroAmaxKernel(const size_t num_tensors, float* amax_rowwise_ptr,\n                                             float* amax_colwise_ptr) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  // Assign each thread a range for rowwise and colwise independently\n  if (amax_rowwise_ptr != nullptr) {\n    for (int i = tid; i < num_tensors; i += stride) {\n      amax_rowwise_ptr[i] = 0.f;\n    }\n  }\n  if (amax_colwise_ptr != nullptr) {\n    for (int i = tid; i < num_tensors; i += stride) {\n      amax_colwise_ptr[i] = 0.f;\n    }\n  }\n}\n\n__global__ void GraphSafeMultiAmaxMemcpyD2DKernelPreRHT(const size_t num_tensors,\n                                                        float* amax_rowwise_ptr,\n                                                        float* amax_colwise_ptr) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  if (amax_rowwise_ptr != nullptr && amax_colwise_ptr != nullptr) {\n    for (; tid < num_tensors; tid += stride) {\n      float* output_pre_rht_amax_ptr = amax_rowwise_ptr + tid;\n      float* output_transpose_amax_ptr = amax_colwise_ptr + tid;\n      *output_transpose_amax_ptr = *output_pre_rht_amax_ptr;\n    }\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void GraphSafeGroupHadamardAmaxTmaKernel(\n    const __grid_constant__ CUtensorMap tensor_map_input, uint16_t random_sign_mask,\n    uint16_t random_sign_mask_t, const ShapeRepresentation shape_rep, const size_t num_tensors,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t* const __restrict__ offsets_ptr, const int64_t* const __restrict__ first_dims_ptr,\n    float* const __restrict__ amax_rowwise_ptr, float* const __restrict__ amax_colwise_ptr) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  float* output_pre_rht_amax_ptr;\n  float* output_identity_amax_ptr = nullptr;\n  float* output_transpose_amax_ptr;\n\n  // calculate the global offset to get tensor id\n  size_t global_offset = blockIdx.y * CHUNK_DIM_Y * last_logical_dim;\n  // paged stashing: will have input buffer [M, N], where M is larger than sum(first_dims)\n  // also need to early return if this CTA is processing a region larger than the last offsets[num_tensors]\n  if (global_offset >= offsets_ptr[num_tensors]) {\n    return;\n  }\n  int tensor_id = get_current_tensor_id(shape_rep, num_tensors, global_offset, first_logical_dim,\n                                        last_logical_dim, offsets_ptr);\n  output_pre_rht_amax_ptr = static_cast<float*>(amax_rowwise_ptr) + tensor_id;\n  output_transpose_amax_ptr = static_cast<float*>(amax_colwise_ptr) + tensor_id;\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\n}  // namespace\n\n// broadcast_pre_rht_amax: when it's true, hadamard transform will be disabled\n// if at this time, the amax buffers for output expects both amax_rowwise and amax_colwise\n// then call MultiAmaxMemcpyD2DKernelPreRHT to D2D copy the amax values\nvoid group_hadamard_transform_amax_graph_safe(const GroupedTensor* input, GroupedTensor* output,\n                                              uint16_t random_sign_mask,\n                                              uint16_t random_sign_mask_t,\n                                              bool broadcast_pre_rht_amax, cudaStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_amax_graph_safe);\n#if CUDA_VERSION >= 12080\n\n  NVTE_CHECK(input->num_tensors == output->num_tensors,\n             \"Number of input and output tensors must be same.\");\n  NVTE_CHECK(input->has_data(), \"Cannot quantize tensor without rowwise data.\");\n\n  checkCuDriverContext(stream);\n\n  bool all_return_pre_rht_amax = output->has_data();\n  // there is no rowwise RHT transform in current recipe\n  bool all_return_identity_amax = false;\n  bool all_return_transposed_amax = output->has_columnwise_data();\n\n  NVTE_CHECK(all_return_pre_rht_amax || all_return_identity_amax || all_return_transposed_amax,\n             \"At least one of return_pre_rht_amax, return_identity_amax, or return_transposed_amax \"\n             \"must be true\");\n\n  if (broadcast_pre_rht_amax) {\n    NVTE_CHECK(all_return_pre_rht_amax,\n               \"broadcast_pre_rht_amax is only supported when we compute pre-RHT amax\");\n    // if all_return_identity_amax and all_return_transposed_amax both are false, there is no need to broadcast anything\n    broadcast_pre_rht_amax &= (all_return_identity_amax || all_return_transposed_amax);\n  }\n\n  const size_t num_tensors = input->num_tensors;\n  const size_t first_logical_dim = input->logical_shape.data[0];\n  const size_t last_logical_dim = input->logical_shape.data[1];\n  // const size_t elts_total = first_logical_dim * last_logical_dim;\n  NVTE_CHECK(first_logical_dim % 128 == 0,\n             \"First dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(last_logical_dim % 128 == 0,\n             \"Last dimension of a grouped tensor should be divisible by 128.\");\n\n  float* const amax_rowwise_ptr = reinterpret_cast<float*>(output->amax.dptr);\n  float* const amax_colwise_ptr = reinterpret_cast<float*>(output->columnwise_amax.dptr);\n\n  const int64_t* const offsets_ptr = reinterpret_cast<const int64_t*>(output->tensor_offsets.dptr);\n  const int64_t* const first_dims_ptr = reinterpret_cast<const int64_t*>(output->first_dims.dptr);\n\n  // some sanity checks\n  if (all_return_pre_rht_amax) {\n    NVTE_CHECK(amax_rowwise_ptr != nullptr, \"Amax rowwise pointer should not be nullptr.\");\n  }\n  if (all_return_transposed_amax) {\n    NVTE_CHECK(amax_colwise_ptr != nullptr, \"Amax columnwise pointer should not be nullptr.\");\n  }\n\n  // Multi zero out multiple amaxes if needed\n  dim3 block_setup_amax(kMaxTensorsPerKernel);\n  dim3 grid_setup_amax(1);\n  GraphSafeMultiZeroAmaxKernel<<<grid_setup_amax, block_setup_amax, 0, stream>>>(\n      num_tensors, amax_rowwise_ptr, amax_colwise_ptr);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n\n  using IType = bf16;\n  constexpr int kHadamardDimension = 16;\n\n  // four (1x4) 64x64 sub-tiles for ping-pong overlap\n  constexpr uint64_t kChunkBlockXSmall = 256;\n  constexpr uint64_t kChunkBlockYSmall = 64;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input->data,\n      /*globalY=*/first_logical_dim,\n      /*globalX=*/last_logical_dim,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/last_logical_dim,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n  dim3 grid(DIVUP(last_logical_dim, kChunkBlockXSmall),\n            DIVUP(first_logical_dim, kChunkBlockYSmall));\n\n  ShapeRepresentation shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  if (output->all_same_shape()) {\n    shape_rep = ShapeRepresentation::SAME_BOTH_DIMS;\n  } else if (output->all_same_first_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_LAST_DIM;\n  } else if (output->all_same_last_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  } else if (output->varying_both_dims()) {\n    shape_rep = ShapeRepresentation::VARYING_BOTH_DIMS;\n  }\n\n  const bool is_const_last_dim = (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS ||\n                                  shape_rep == ShapeRepresentation::VARYING_FIRST_DIM);\n\n  NVTE_CHECK(is_const_last_dim,\n             \"Currently we only support const last dimension for graph safe hadamard transform.\");\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      (all_return_transposed_amax && !broadcast_pre_rht_amax), kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          (all_return_identity_amax && !broadcast_pre_rht_amax), kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = GraphSafeGroupHadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(\n                  tensor_map_input, random_sign_mask, random_sign_mask_t, shape_rep, num_tensors,\n                  first_logical_dim, last_logical_dim, offsets_ptr, first_dims_ptr,\n                  amax_rowwise_ptr, amax_colwise_ptr);\n              if (broadcast_pre_rht_amax) {\n                GraphSafeMultiAmaxMemcpyD2DKernelPreRHT<<<grid_setup_amax, block_setup_amax, 0,\n                                                          stream>>>(num_tensors, amax_rowwise_ptr,\n                                                                    amax_colwise_ptr);\n              })));\n\n  NVTE_CHECK_CUDA(cudaGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_amax_graph_safe(const NVTEGroupedTensor input,\n                                                   NVTEGroupedTensor output, int random_sign_mask,\n                                                   int random_sign_mask_t, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_amax_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor* input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor* output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  // Call the group tensor Hadamard transform amax implementation.\n  group_hadamard_transform_amax_graph_safe(\n      input_tensor, output_tensor, static_cast<uint16_t>(random_sign_mask),\n      static_cast<uint16_t>(random_sign_mask_t), false, stream);\n}\n\n// Grouped-tensor amax without doing hadamard transform\nvoid nvte_group_amax_graph_safe(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_amax_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor* input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor* output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  group_hadamard_transform_amax_graph_safe(input_tensor, output_tensor, 0, 0, true, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <transformer_engine/hadamard_transform.h>\n#include <transformer_engine/multi_tensor.h>\n\n#include <cuda/barrier>\n\n#include \"common/common.h\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"hadamard_transform_utils.cuh\"\n\nnamespace transformer_engine {\nnamespace {\n\nconstexpr int kMaxTensorsPerKernel = 64;\nconstexpr int kThreadsPerWarp = 32;\n\n__device__ __forceinline__ size_t get_current_tensor_id(\n    const ShapeRepresentation shape_rep, const size_t num_tensors, const size_t current_offset,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t* const __restrict__ offsets_ptr) {\n  if (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS) {\n    const size_t current_row = current_offset / last_logical_dim;\n    const size_t rows_per_tensor = first_logical_dim / num_tensors;\n    return current_row / rows_per_tensor;\n  } else {\n    // upper_bound(offsets, current_offset) - 1 in range i in [0..num_tensors)\n    size_t low = 0;\n    size_t hi = num_tensors;  // half-open [low, hi)\n\n    while (low < hi) {\n      const size_t mid = low + (hi - low) / 2;\n      const size_t mid_offset = static_cast<size_t>(offsets_ptr[mid]);\n\n      if (mid_offset <= current_offset) {\n        low = mid + 1;\n      } else {\n        hi = mid;\n      }\n    }\n\n    // low = first index where offsets[low] > current_offset (or low == num_tensors)\n    // id = low - 1, but need to evaluate if current_offset < offsets[0]\n    return (low == 0) ? 0 : (low - 1);\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int BUFF_DIM_Y, int BUFF_DIM_X,\n          bool kReturnPreRhtAmax, bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__device__ __forceinline__ void ComputeKernel(uint32_t b_frag_i[4], uint32_t b_frag_t[4],\n                                              IType* in_sh_ptr, int swizzle_idx,\n                                              uint32_t& local_pre_rht_amax_reg,\n                                              uint32_t& local_amax_reg,\n                                              uint32_t& local_amax_t_reg) {\n  uint32_t a_frag[4];  // A matrix fragment\n  uint32_t c_frag[4];  // Result fragment\n\n  uint32_t temp_amax_reg;\n  uint32_t temp_amax_t_reg;\n\n  if (kReturnIdentityAmax) {\n    ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                       reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnIdentityAmax>(\n        a_frag[0], a_frag[1], a_frag[2], a_frag[3], b_frag_i[0], b_frag_i[1], b_frag_i[2],\n        b_frag_i[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_reg)\n                 : \"r\"(local_amax_reg), \"r\"(temp_amax_reg));\n  }\n\n  if (kReturnTransposedAmax) {\n    if (!kReturnIdentityAmax) {\n      ldmatrix_x4_m8n8_shared_b16<true>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                        reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    } else {\n      matrix_transpose_m8_n8_b16_inplace(a_frag[0]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[1]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[2]);\n      matrix_transpose_m8_n8_b16_inplace(a_frag[3]);\n    }\n\n    mma_m16_n16_k16_b16_b16_b16_noacc<kReturnTransposedAmax>(\n        a_frag[0], a_frag[2], a_frag[1], a_frag[3], b_frag_t[0], b_frag_t[1], b_frag_t[2],\n        b_frag_t[3], c_frag[0], c_frag[1], c_frag[2], c_frag[3], temp_amax_t_reg);\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_amax_t_reg)\n                 : \"r\"(local_amax_t_reg), \"r\"(temp_amax_t_reg));\n  }\n\n  if (kReturnPreRhtAmax) {\n    if (!kReturnIdentityAmax && !kReturnTransposedAmax) {\n      ldmatrix_x4_m8n8_shared_b16<false>(a_frag[0], a_frag[1], a_frag[2], a_frag[3],\n                                         reinterpret_cast<uint4*>(in_sh_ptr) + swizzle_idx);\n    }\n\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[1]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[2])\n                 : \"r\"(a_frag[2]), \"r\"(a_frag[3]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(a_frag[0])\n                 : \"r\"(a_frag[0]), \"r\"(a_frag[2]));\n    asm volatile(\"max.xorsign.abs.bf16x2 %0, %1, %2;\\n\\t\"\n                 : \"=r\"(local_pre_rht_amax_reg)\n                 : \"r\"(a_frag[0]), \"r\"(local_pre_rht_amax_reg));\n  }\n}\n\ntemplate <int kN>\n__device__ __host__ constexpr int NextPowerOf2() {\n  static_assert(kN > 0, \"kN must be > 0\");\n  // Round up to the next power of 2 by counting leading zeros.\n  return 1 << (32 - __builtin_clz(kN - 1));\n}\n\ntemplate <int kNumWarps, bool kReturnPreRhtAmax, bool kReturnIdentityAmax,\n          bool kReturnTransposedAmax>\n__device__ __forceinline__ void ReduceMax(const float pre_rht_amax, const float identity_amax,\n                                          const float transpose_amax, float* staging_for_pre_rht,\n                                          float* staging_for_identity, float* staging_for_transpose,\n                                          float* output_pre_rht_amax_ptr,\n                                          float* output_identity_amax_ptr,\n                                          float* output_transpose_amax_ptr, const int warpid) {\n  // intra-warp reduction\n  constexpr int kWarpSize = 32;\n  int local_rank = threadIdx.x % 32;\n  float warp_pre_rht_amax = kReturnPreRhtAmax ? warp_reduce_max<kWarpSize>(pre_rht_amax) : 0.0f;\n  float warp_identity_amax = kReturnIdentityAmax ? warp_reduce_max<kWarpSize>(identity_amax) : 0.0f;\n  float warp_transpose_amax =\n      kReturnTransposedAmax ? warp_reduce_max<kWarpSize>(transpose_amax) : 0.0f;\n\n  // inter-warp reduction\n  if (threadIdx.x % 32 == 0) {\n    if (kReturnPreRhtAmax) {\n      staging_for_pre_rht[warpid] = warp_pre_rht_amax;\n    }\n    if (kReturnIdentityAmax) {\n      staging_for_identity[warpid] = warp_identity_amax;\n    }\n    if (kReturnTransposedAmax) {\n      staging_for_transpose[warpid] = warp_transpose_amax;\n    }\n  }\n  __syncthreads();\n  constexpr int kNumWarpsPow2 = NextPowerOf2<kNumWarps>();\n  if (warpid == 0) {\n    if (kReturnIdentityAmax) {\n      float identity_accum = local_rank < kNumWarps ? staging_for_identity[local_rank] : 0.0f;\n      identity_accum = warp_reduce_max<kNumWarpsPow2>(identity_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_identity_amax_ptr, identity_accum);\n      }\n    }\n  }\n  if (warpid == 1) {\n    if (kReturnTransposedAmax) {\n      float transpose_accum = local_rank < kNumWarps ? staging_for_transpose[local_rank] : 0.0f;\n      transpose_accum = warp_reduce_max<kNumWarpsPow2>(transpose_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_transpose_amax_ptr, transpose_accum);\n      }\n    }\n  }\n  if (warpid == 2) {\n    if (kReturnPreRhtAmax) {\n      float pre_rht_accum = local_rank < kNumWarps ? staging_for_pre_rht[local_rank] : 0.0f;\n      pre_rht_accum = warp_reduce_max<kNumWarpsPow2>(pre_rht_accum);\n      if (local_rank == 0) {\n        atomicMaxFloat(output_pre_rht_amax_ptr, pre_rht_accum);\n      }\n    }\n  }\n}\n\n__global__ void GraphSafeMultiZeroAmaxKernel(const size_t num_tensors, float* amax_rowwise_ptr,\n                                             float* amax_colwise_ptr) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  // Assign each thread a range for rowwise and colwise independently\n  if (amax_rowwise_ptr != nullptr) {\n    for (int i = tid; i < num_tensors; i += stride) {\n      amax_rowwise_ptr[i] = 0.f;\n    }\n  }\n  if (amax_colwise_ptr != nullptr) {\n    for (int i = tid; i < num_tensors; i += stride) {\n      amax_colwise_ptr[i] = 0.f;\n    }\n  }\n}\n\n__global__ void GraphSafeMultiAmaxMemcpyD2DKernelPreRHT(const size_t num_tensors,\n                                                        float* amax_rowwise_ptr,\n                                                        float* amax_colwise_ptr) {\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  if (amax_rowwise_ptr != nullptr && amax_colwise_ptr != nullptr) {\n    for (; tid < num_tensors; tid += stride) {\n      float* output_pre_rht_amax_ptr = amax_rowwise_ptr + tid;\n      float* output_transpose_amax_ptr = amax_colwise_ptr + tid;\n      *output_transpose_amax_ptr = *output_pre_rht_amax_ptr;\n    }\n  }\n}\n\ntemplate <typename IType, int kHadamardDimension, int CHUNK_DIM_Y, int CHUNK_DIM_X, int BUFF_DIM_Y,\n          int BUFF_DIM_X, int THREADS_PER_CHUNK, int THREADS_PER_Y, bool kReturnPreRhtAmax,\n          bool kReturnIdentityAmax, bool kReturnTransposedAmax>\n__global__ void GraphSafeGroupHadamardAmaxTmaKernel(\n    const __grid_constant__ CUtensorMap tensor_map_input, uint16_t random_sign_mask,\n    uint16_t random_sign_mask_t, const ShapeRepresentation shape_rep, const size_t num_tensors,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t* const __restrict__ offsets_ptr, const int64_t* const __restrict__ first_dims_ptr,\n    float* const __restrict__ amax_rowwise_ptr, float* const __restrict__ amax_colwise_ptr) {\n#if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n\n  float* output_pre_rht_amax_ptr;\n  float* output_identity_amax_ptr = nullptr;\n  float* output_transpose_amax_ptr;\n\n  // calculate the global offset to get tensor id\n  size_t global_offset = blockIdx.y * CHUNK_DIM_Y * last_logical_dim;\n  // paged stashing: will have input buffer [M, N], where M is larger than sum(first_dims)\n  // also need to early return if this CTA is processing a region larger than the last offsets[num_tensors]\n  if (global_offset >= offsets_ptr[num_tensors]) {\n    return;\n  }\n  int tensor_id = get_current_tensor_id(shape_rep, num_tensors, global_offset, first_logical_dim,\n                                        last_logical_dim, offsets_ptr);\n  output_pre_rht_amax_ptr = static_cast<float*>(amax_rowwise_ptr) + tensor_id;\n  output_transpose_amax_ptr = static_cast<float*>(amax_colwise_ptr) + tensor_id;\n\n  static_assert(CHUNK_DIM_Y >= BUFF_DIM_Y && CHUNK_DIM_Y % BUFF_DIM_Y == 0);\n  static_assert(CHUNK_DIM_X >= BUFF_DIM_X && CHUNK_DIM_X % BUFF_DIM_X == 0);\n\n  constexpr size_t STAGES_Y = CHUNK_DIM_Y / BUFF_DIM_Y;\n  constexpr size_t STAGES_X = CHUNK_DIM_X / BUFF_DIM_X;\n\n  constexpr int kNumWarps = (THREADS_PER_CHUNK * THREADS_PER_Y) / kThreadsPerWarp;\n\n  const int input_block_offset_Y = blockIdx.y * CHUNK_DIM_Y;\n  const int input_block_offset_X = blockIdx.x * CHUNK_DIM_X;\n\n  extern __shared__ __align__(128) char dynamic_shmem[];\n  uintptr_t base_shmem_ptr = reinterpret_cast<uintptr_t>(dynamic_shmem);\n  // Manually align dynamic SHMEM per TMA requirements using padding\n  // __align__(128) Does not guarantee the pointer to be aligned!\n  uint8_t* dshmem = reinterpret_cast<uint8_t*>((base_shmem_ptr + 127) & ~127ULL);\n\n  // The destination shared memory buffer of a bulk tensor operation should be 16-byte aligned\n  constexpr size_t in_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n  IType* in_sh_0 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n  IType* in_sh_1 = reinterpret_cast<IType*>(dshmem);\n  dshmem += in_buff_size;\n\n  IType* in_shs[2] = {in_sh_0, in_sh_1};\n\n  constexpr int shmem_buff_size = BUFF_DIM_X * BUFF_DIM_Y * sizeof(IType);\n\n  const bool is_master_thread = (threadIdx.x == 0 && threadIdx.y == 0);\n\n  // Initialize shared memory barrier with the number of threads participating in the barrier.\n#pragma nv_diag_suppress static_var_with_dynamic_init\n  uint64_t* mbar = reinterpret_cast<uint64_t*>(dshmem);\n  dshmem += sizeof(uint64_t) * (STAGES_X * STAGES_Y);\n\n  float* max_staging_identity = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_transpose = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n  float* max_staging_pre_rht = reinterpret_cast<float*>(dshmem);\n  dshmem += sizeof(float) * kNumWarps;\n\n  initialize_barriers<STAGES_X * STAGES_Y, THREADS_PER_CHUNK * THREADS_PER_Y>(mbar,\n                                                                              is_master_thread);\n\n  copy_2d_to_shared(in_shs[0], reinterpret_cast<const void*>(&tensor_map_input),\n                    input_block_offset_X, input_block_offset_Y, shmem_buff_size, &mbar[0],\n                    is_master_thread);\n\n  uint32_t had_frag_i[4];\n  uint32_t had_frag_t[4];\n  get_hadamard_matrix_fragment<kReturnIdentityAmax, kReturnTransposedAmax, false, false>(\n      had_frag_i, random_sign_mask, had_frag_t, random_sign_mask_t);\n\n  float local_pre_rht_amax = 0.0;\n  float local_amax = 0.0;\n  float local_amax_t = 0.0;\n  uint32_t local_pre_rht_amax_reg = *reinterpret_cast<uint32_t*>(&local_pre_rht_amax);\n  uint32_t local_amax_reg = *reinterpret_cast<uint32_t*>(&local_amax);\n  uint32_t local_amax_t_reg = *reinterpret_cast<uint32_t*>(&local_amax_t);\n\n  const int warp_id = threadIdx.x / kThreadsPerWarp;\n  const int local_rank = threadIdx.x % kThreadsPerWarp;\n  const int ld_row_idx = local_rank % kHadamardDimension;\n  const int ld_col_idx = local_rank / kHadamardDimension + warp_id * 2;\n  const int swizzle_idx = swizzle_128B_atom_32B(ld_row_idx, ld_col_idx);\n\n  for (int stage_y = 0; stage_y < STAGES_Y; ++stage_y) {\n    for (int stage_x = 0; stage_x < STAGES_X; ++stage_x) {\n      int stage = STAGES_X * stage_y + stage_x;\n\n      const int next_stage = stage + 1;\n      const int next_stage_x = stage_x + 1 == STAGES_X ? 0 : stage_x + 1;\n      const int next_stage_y = stage_x + 1 == STAGES_X ? stage_y + 1 : stage_y;\n\n      if (next_stage < STAGES_X * STAGES_Y) {\n        const int input_global_offset_Y = input_block_offset_Y + next_stage_y * BUFF_DIM_Y;\n        const int input_global_offset_X = input_block_offset_X + next_stage_x * BUFF_DIM_X;\n\n        copy_2d_to_shared(in_shs[next_stage % 2],  // ping-pong\n                          reinterpret_cast<const void*>(&tensor_map_input), input_global_offset_X,\n                          input_global_offset_Y, shmem_buff_size, &mbar[next_stage],\n                          is_master_thread);\n      }\n\n      // Wait for the data to have arrived\n      ptx::mbarrier_wait_parity(&mbar[stage], 0);\n\n      const size_t compute_stage_x_num =\n          BUFF_DIM_X / (kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp));\n      const size_t compute_stage_y_num = BUFF_DIM_Y / (kHadamardDimension * THREADS_PER_Y);\n\n      const size_t in_row_stride = BUFF_DIM_X;\n\n      IType* in_sh_ptr = in_shs[stage % 2];\n\n#pragma unroll\n      for (size_t compute_stage_y = 0; compute_stage_y < compute_stage_y_num; compute_stage_y++) {\n        const int row_idx_offset = (compute_stage_y * kHadamardDimension * THREADS_PER_Y +\n                                    threadIdx.y * kHadamardDimension);\n        const int in_row_offset = row_idx_offset * in_row_stride;\n\n#pragma unroll\n        for (size_t compute_stage_x = 0; compute_stage_x < compute_stage_x_num; compute_stage_x++) {\n          ComputeKernel<IType, kHadamardDimension, BUFF_DIM_Y, BUFF_DIM_X, kReturnPreRhtAmax,\n                        kReturnIdentityAmax, kReturnTransposedAmax>(\n              had_frag_i, had_frag_t,\n              in_sh_ptr + in_row_offset +\n                  (compute_stage_x * kHadamardDimension * (THREADS_PER_CHUNK / kThreadsPerWarp)),\n              swizzle_idx, local_pre_rht_amax_reg, local_amax_reg, local_amax_t_reg);\n        }\n      }\n      // Ensure all threads have finished their computation before new data over-writes the shared\n      // memory.\n      __syncthreads();\n      // Ensure generic shared-memory accesses are visible before the next TMA write.\n      ptx::fence_proxy_async_shared_cta();\n    }\n  }\n\n  const int warpid = (threadIdx.x + threadIdx.y * blockDim.x) / kThreadsPerWarp;\n\n  if constexpr (kReturnPreRhtAmax) {\n    unpack_max_of_packed_bf16(local_pre_rht_amax_reg, local_pre_rht_amax);\n  }\n  if constexpr (kReturnIdentityAmax) {\n    unpack_max_of_packed_bf16(local_amax_reg, local_amax);\n  }\n  if constexpr (kReturnTransposedAmax) {\n    unpack_max_of_packed_bf16(local_amax_t_reg, local_amax_t);\n  }\n\n  ReduceMax<kNumWarps, kReturnPreRhtAmax, kReturnIdentityAmax, kReturnTransposedAmax>(\n      local_pre_rht_amax, local_amax, local_amax_t, max_staging_pre_rht, max_staging_identity,\n      max_staging_transpose, output_pre_rht_amax_ptr, output_identity_amax_ptr,\n      output_transpose_amax_ptr, warpid);\n\n  destroy_barriers<STAGES_X * STAGES_Y>(mbar, is_master_thread);\n#else\n  NVTE_DEVICE_ERROR(\"Kernel is only supported on SM 10.0+.\");\n#endif  // #if (defined __CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000)\n}\n\n}  // namespace\n\n// broadcast_pre_rht_amax: when it's true, hadamard transform will be disabled\n// if at this time, the amax buffers for output expects both amax_rowwise and amax_colwise\n// then call MultiAmaxMemcpyD2DKernelPreRHT to D2D copy the amax values\nvoid group_hadamard_transform_amax_graph_safe(const GroupedTensor* input, GroupedTensor* output,\n                                              uint16_t random_sign_mask,\n                                              uint16_t random_sign_mask_t,\n                                              bool broadcast_pre_rht_amax, hipStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_amax_graph_safe);\n#if CUDA_VERSION >= 12080\n\n  NVTE_CHECK(input->num_tensors == output->num_tensors,\n             \"Number of input and output tensors must be same.\");\n  NVTE_CHECK(input->has_data(), \"Cannot quantize tensor without rowwise data.\");\n\n  checkCuDriverContext(stream);\n\n  bool all_return_pre_rht_amax = output->has_data();\n  // there is no rowwise RHT transform in current recipe\n  bool all_return_identity_amax = false;\n  bool all_return_transposed_amax = output->has_columnwise_data();\n\n  NVTE_CHECK(all_return_pre_rht_amax || all_return_identity_amax || all_return_transposed_amax,\n             \"At least one of return_pre_rht_amax, return_identity_amax, or return_transposed_amax \"\n             \"must be true\");\n\n  if (broadcast_pre_rht_amax) {\n    NVTE_CHECK(all_return_pre_rht_amax,\n               \"broadcast_pre_rht_amax is only supported when we compute pre-RHT amax\");\n    // if all_return_identity_amax and all_return_transposed_amax both are false, there is no need to broadcast anything\n    broadcast_pre_rht_amax &= (all_return_identity_amax || all_return_transposed_amax);\n  }\n\n  const size_t num_tensors = input->num_tensors;\n  const size_t first_logical_dim = input->logical_shape.data[0];\n  const size_t last_logical_dim = input->logical_shape.data[1];\n  // const size_t elts_total = first_logical_dim * last_logical_dim;\n  NVTE_CHECK(first_logical_dim % 128 == 0,\n             \"First dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(last_logical_dim % 128 == 0,\n             \"Last dimension of a grouped tensor should be divisible by 128.\");\n\n  float* const amax_rowwise_ptr = reinterpret_cast<float*>(output->amax.dptr);\n  float* const amax_colwise_ptr = reinterpret_cast<float*>(output->columnwise_amax.dptr);\n\n  const int64_t* const offsets_ptr = reinterpret_cast<const int64_t*>(output->tensor_offsets.dptr);\n  const int64_t* const first_dims_ptr = reinterpret_cast<const int64_t*>(output->first_dims.dptr);\n\n  // some sanity checks\n  if (all_return_pre_rht_amax) {\n    NVTE_CHECK(amax_rowwise_ptr != nullptr, \"Amax rowwise pointer should not be nullptr.\");\n  }\n  if (all_return_transposed_amax) {\n    NVTE_CHECK(amax_colwise_ptr != nullptr, \"Amax columnwise pointer should not be nullptr.\");\n  }\n\n  // Multi zero out multiple amaxes if needed\n  dim3 block_setup_amax(kMaxTensorsPerKernel);\n  dim3 grid_setup_amax(1);\n  GraphSafeMultiZeroAmaxKernel<<<grid_setup_amax, block_setup_amax, 0, stream>>>(\n      num_tensors, amax_rowwise_ptr, amax_colwise_ptr);\n  NVTE_CHECK_CUDA(hipGetLastError());\n\n  using IType = bf16;\n  constexpr int kHadamardDimension = 16;\n\n  // four (1x4) 64x64 sub-tiles for ping-pong overlap\n  constexpr uint64_t kChunkBlockXSmall = 256;\n  constexpr uint64_t kChunkBlockYSmall = 64;\n  constexpr uint64_t kBuffDimX = 64;\n  constexpr uint64_t kBuffDimY = 64;\n\n  alignas(64) CUtensorMap tensor_map_input{};\n\n  create_2D_tensor_map(\n      /*tensorMap=*/tensor_map_input,\n      /*tensor=*/input->data,\n      /*globalY=*/first_logical_dim,\n      /*globalX=*/last_logical_dim,\n      /*shmemY=*/kBuffDimY,\n      /*shmemX=*/kBuffDimX,\n      /*stride_elems=*/last_logical_dim,\n      /*offset_elems=*/0,\n      /*type_num_bits=*/sizeof(IType) * 8,\n      /*swizzle=*/CUtensorMapSwizzle::CU_TENSOR_MAP_SWIZZLE_128B_ATOM_32B);\n\n  constexpr uint64_t kThreadBlockX = 4;\n  constexpr uint64_t kThreadBlockY = 1;\n  constexpr uint64_t kNumWarps = kThreadBlockX * kThreadBlockY;\n\n  dim3 block(kThreadBlockX * kThreadsPerWarp, kThreadBlockY);\n  dim3 grid(DIVUP(last_logical_dim, kChunkBlockXSmall),\n            DIVUP(first_logical_dim, kChunkBlockYSmall));\n\n  ShapeRepresentation shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  if (output->all_same_shape()) {\n    shape_rep = ShapeRepresentation::SAME_BOTH_DIMS;\n  } else if (output->all_same_first_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_LAST_DIM;\n  } else if (output->all_same_last_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  } else if (output->varying_both_dims()) {\n    shape_rep = ShapeRepresentation::VARYING_BOTH_DIMS;\n  }\n\n  const bool is_const_last_dim = (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS ||\n                                  shape_rep == ShapeRepresentation::VARYING_FIRST_DIM);\n\n  NVTE_CHECK(is_const_last_dim,\n             \"Currently we only support const last dimension for graph safe hadamard transform.\");\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      (all_return_transposed_amax && !broadcast_pre_rht_amax), kReturnTransposedAmax,\n\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          (all_return_identity_amax && !broadcast_pre_rht_amax), kReturnIdentityAmax,\n\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_return_pre_rht_amax, kReturnPreRhtAmax,\n\n              // *2 for ping-pong\n              size_t in_sh_size = kBuffDimX * kBuffDimY * 2 * sizeof(IType);\n              size_t mbar_size = sizeof(uint64_t) * (kChunkBlockXSmall / kBuffDimX) *\n                                 (kChunkBlockYSmall / kBuffDimY);\n              size_t shmem_bytes = in_sh_size + mbar_size + kNumWarps * sizeof(float) * 3;\n              // Add padding in case shmem ptr is not aligned to 128 bytes.\n              shmem_bytes = (shmem_bytes + 128);\n\n              auto kernel = GraphSafeGroupHadamardAmaxTmaKernel<\n                  IType, kHadamardDimension, kChunkBlockYSmall, kChunkBlockXSmall, kBuffDimY,\n                  kBuffDimX, kThreadBlockX * kThreadsPerWarp, kThreadBlockY, kReturnPreRhtAmax,\n                  kReturnIdentityAmax, kReturnTransposedAmax>;\n              hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize,\n                                   shmem_bytes);\n\n              kernel<<<grid, block, shmem_bytes, stream>>>(\n                  tensor_map_input, random_sign_mask, random_sign_mask_t, shape_rep, num_tensors,\n                  first_logical_dim, last_logical_dim, offsets_ptr, first_dims_ptr,\n                  amax_rowwise_ptr, amax_colwise_ptr);\n              if (broadcast_pre_rht_amax) {\n                GraphSafeMultiAmaxMemcpyD2DKernelPreRHT<<<grid_setup_amax, block_setup_amax, 0,\n                                                          stream>>>(num_tensors, amax_rowwise_ptr,\n                                                                    amax_colwise_ptr);\n              })));\n\n  NVTE_CHECK_CUDA(hipGetLastError());\n#else\n  NVTE_ERROR(\"Hadamard transform requires CUDA 12.8+, but compile-time CUDA version is \",\n             CUDA_VERSION);\n#endif  // CUDA_VERSION >= 12080\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_amax_graph_safe(const NVTEGroupedTensor input,\n                                                   NVTEGroupedTensor output, int random_sign_mask,\n                                                   int random_sign_mask_t, hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_amax_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor* input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor* output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  // Call the group tensor Hadamard transform amax implementation.\n  group_hadamard_transform_amax_graph_safe(\n      input_tensor, output_tensor, static_cast<uint16_t>(random_sign_mask),\n      static_cast<uint16_t>(random_sign_mask_t), false, stream);\n}\n\n// Grouped-tensor amax without doing hadamard transform\nvoid nvte_group_amax_graph_safe(const NVTEGroupedTensor input, NVTEGroupedTensor output,\n                                hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_amax_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor* input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor* output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  group_hadamard_transform_amax_graph_safe(input_tensor, output_tensor, 0, 0, true, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/graph_safe_group_hadamard_transform.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\n// Ensure Tensor refers to cute::Tensor, not transformer_engine::Tensor\nusing cute::Tensor;\n\nconstexpr int kMaxTensorsPerKernel = 64;\nconstexpr int kNVFP4BlockSize = 16;\n\nenum ShapeRepresentation {\n  SAME_BOTH_DIMS = 0,\n  VARYING_FIRST_DIM = 1,\n  VARYING_LAST_DIM = 2,\n  VARYING_BOTH_DIMS = 3\n};\n\n__device__ __forceinline__ size_t get_current_tensor_id(\n    const ShapeRepresentation shape_rep, const size_t num_tensors, const size_t current_offset,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t *const __restrict__ offsets_ptr) {\n  if (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS) {\n    const size_t current_row = current_offset / last_logical_dim;\n    const size_t rows_per_tensor = first_logical_dim / num_tensors;\n    return current_row / rows_per_tensor;\n  } else {\n    // upper_bound(offsets, current_offset) - 1 in range i in [0..num_tensors)\n    size_t low = 0;\n    size_t hi = num_tensors;  // half-open [low, hi)\n\n    while (low < hi) {\n      const size_t mid = low + (hi - low) / 2;\n      const size_t mid_offset = static_cast<size_t>(offsets_ptr[mid]);\n\n      if (mid_offset <= current_offset) {\n        low = mid + 1;\n      } else {\n        hi = mid;\n      }\n    }\n\n    // low = first index where offsets[low] > current_offset (or low == num_tensors)\n    // id = low - 1, but need to evaluate if current_offset < offsets[0]\n    return (low == 0) ? 0 : (low - 1);\n  }\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout, class ClusterShape,\n          int AccumulatorPipelineStageCount_, int EpilogueUnrollFactor_,\n          int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                 AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>,\n                                                      AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using SchedPipelineStorage = typename SchedPipeline::SharedStorage;\n  using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using SchedThrottlePipelineStorage = typename SchedThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) SchedPipelineStorage sched;\n  alignas(16) SchedThrottlePipelineStorage sched_throttle;\n  alignas(16) int32_t atomic_tile_id[SchedulerPipelineStageCount_];\n  alignas(16) float global_a_amax[kMaxTensorsPerKernel];\n  alignas(16) float global_d_amax[kMaxTensorsPerKernel];\n  uint32_t atomic_tile_counter[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\n// Main RHT GEMM kernel entry -- highly templated for flexible architecture/config support\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA, class TB, class BStride,\n          class BSmemLayout, class TmaLoadB, class TD, class DStride, class DSmemLayout, class TSFD,\n          class TSFDLayout, class TQA, class QAStride, class TSFA, class TSFALayout, class TiledMMA,\n          int AccumulatorPipelineStageCount_, int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false, bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true, bool kEnableSwizzleSFOutput_ = false,\n          bool kUseFastMath_ = false>\n__launch_bounds__(512, 1) __global__ static void group_row_col_rht_gemm_device_graph_safe(\n    MShape M, NShape packed_N, KShape K, ClusterShape cluster_shape, ClusterTileShape cluster_tile,\n    TA const *A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const *B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TQA *QA, QAStride dQA, TSFA *SFA, TSFALayout sfa_layout, TQA *QA_COLWISE, TSFA *SFA_COLWISE,\n    float *amax_rowwise, float *amax_colwise, const int64_t *offsets, const int64_t *first_dims,\n    size_t num_tensors, ShapeRepresentation shape_rep, uint32_t *tile_scheduler_workspace,\n    TiledMMA mma, const size_t *rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                  \"group_row_col_rht_gemm_device_graph_safe must generate row-wise \"\n                  \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n    CUTLASS_NOT_IMPLEMENTED();\n    return;\n#endif\n\n    using X = Underscore;\n    // Accumulator data type for main computation\n    using ElementAccumulator = float;\n    static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n    static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n    static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n    static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n    static constexpr bool kEnableSwizzleSFOutput = kEnableSwizzleSFOutput_;\n    static constexpr bool kUseFastMath = kUseFastMath_;\n\n    // Constant for RHT tensor processing (tile size etc)\n    static int constexpr RhtTensorSize = 16;\n\n    // Get the total number of tokens to process\n    // Note that here M is the hidden size, which is the last logical dimension of the input tensor x\n    // The kernel is designed in column major, so M is the hidden size\n    size_t sum_token_dims = offsets[num_tensors] / M;\n\n    // Transaction bytes for TMA transfer on RHT tensor blocks\n    static int constexpr kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n    static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n    static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n    // Mainloop pipeline stage calculation, vectorization parameters for scaling factors\n    static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    static int constexpr SFVecSize = 16;\n    // Swizzle output layout for scaling factor arrays\n    using SwizzledSFALayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n    using SwizzledSFDLayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n\n    // Mainloop pipeline types for TMA async execution and epilogue cluster scheduling\n    using MainloopPipeline =\n        cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, ClusterShape,\n                                                        AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n    using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n    using SchedPipelineState = typename SchedPipeline::PipelineState;\n    using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n    using SchedThrottlePipelineState = typename SchedThrottlePipeline::PipelineState;\n\n    static_assert(ClusterShape{} == Shape<_1, _1, _1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static int constexpr VectorSize = RhtTensorSize;\n\n    // Compile-time safety: static shapes required for shared memory layouts\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    //   CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n\n    auto cluster_size = size<0>(cluster_shape);\n    auto mainloop_tiler = Shape<_128, _16, _128>{};\n    auto epilogue_tiler = Shape<_128, _128, _128>{};\n\n    static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    int const K_TILE_MAX = min(packed_N, K) / size<2>(epilogue_tiler);\n\n    struct TileScheduler {\n      uint32_t tiles_in_m = 0;\n      uint32_t tiles_in_n = 0;\n      uint32_t linear_idx = 0;\n      uint32_t next_linear_idx = 0;\n      uint32_t start_idx = 0;\n      uint32_t tile_m_idx = 0;\n      uint32_t tile_n_idx = 0;\n      int k_tile_max = 0;\n      uint32_t *atomic_tile_index_;\n      uint32_t *smem_tile_counter;\n      uint32_t atomic_offset;\n      cutlass::FastDivmodU64 divmod_tiles_in_m;\n\n      CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax,\n                                   uint32_t *atomic_tile_index, uint32_t *smem_tile_counter)\n          : tiles_in_m(tiles_m),\n            tiles_in_n(tiles_n),\n            linear_idx(blockIdx.x),\n            next_linear_idx(blockIdx.x),\n            start_idx(blockIdx.x),\n            k_tile_max(kmax),\n            atomic_tile_index_(atomic_tile_index),\n            smem_tile_counter(smem_tile_counter),\n            atomic_offset(gridDim.x),\n            divmod_tiles_in_m(uint64_t(tiles_m)) {\n        update_tile_idx();\n      }\n      CUTLASS_DEVICE void update_tile_idx() {\n        uint64_t q, r;\n        divmod_tiles_in_m(q, r, uint64_t(linear_idx));\n        tile_m_idx = static_cast<uint32_t>(r);\n        tile_n_idx = static_cast<uint32_t>(q) * uint32_t(k_tile_max);\n      }\n      CUTLASS_DEVICE uint32_t tile_m() const { return tile_m_idx; }\n      CUTLASS_DEVICE uint32_t tile_n_base() const { return tile_n_idx; }\n      CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n\n      CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n\n      CUTLASS_DEVICE bool is_valid() const {\n        return cute::elem_less(cute::make_coord(tile_m(), tile_n_base()),\n                               cute::make_coord(tiles_in_m, tiles_in_n));\n      }\n\n      CUTLASS_DEVICE bool is_first_wave() const { return linear_idx == start_idx; }\n\n      CUTLASS_DEVICE uint32_t get_linear_tile_idx() const { return linear_idx; }\n\n      // Fetch a new tile_id using atomics.\n      CUTLASS_DEVICE uint32_t fetch_tile_id_counter(int pred) {\n        uint32_t tile_id_counter = 0;\n        asm volatile(\n            \"{\\n\\t\"\n            \".reg .pred p;\\n\\t\"\n            \"setp.eq.u32 p, %2, 1;\\n\\t\"\n            \"@p atom.global.add.u32 %0, [%1], 1; \\n\\t\"\n            \"}\"\n            : \"=r\"(tile_id_counter)\n            : \"l\"(atomic_tile_index_), \"r\"(pred));\n\n        return tile_id_counter;\n      }\n\n      CUTLASS_DEVICE auto fetch_next_work(SchedPipeline &sched_pipeline,\n                                          SchedPipelineState sched_pipeline_consumer_state) {\n        sched_pipeline.consumer_wait(sched_pipeline_consumer_state);\n        next_linear_idx = smem_tile_counter[sched_pipeline_consumer_state.index()];\n        cutlass::arch::fence_view_async_shared();\n        sched_pipeline.consumer_release(sched_pipeline_consumer_state);\n        return;\n      }\n\n      CUTLASS_DEVICE auto advance_to_next_work(SchedPipeline &sched_pipeline,\n                                               SchedPipelineState sched_pipeline_producer_state) {\n        uint32_t mbarrier_addr = sched_pipeline.producer_get_barrier(sched_pipeline_producer_state);\n        // Wait for clcID buffer to become empty with a flipped phase\n        sched_pipeline.producer_acquire(sched_pipeline_producer_state);\n        auto is_leading_thread = cute::elect_one_sync();\n        uint32_t tile_id_counter = fetch_tile_id_counter(is_leading_thread) + atomic_offset;\n        uint32_t smem_addr =\n            cute::cast_smem_ptr_to_uint(&smem_tile_counter[sched_pipeline_producer_state.index()]);\n        if (is_leading_thread) {\n          cute::store_shared_remote(tile_id_counter, smem_addr, mbarrier_addr, 0);\n        }\n\n        ++sched_pipeline_producer_state;\n        return sched_pipeline_producer_state;\n      }\n\n      CUTLASS_DEVICE auto update_work_tile_info() {\n        linear_idx = next_linear_idx;\n        update_tile_idx();\n        return;\n      }\n    };\n\n    // Allocate and alias shared memory to the kernel's shared storage type\n    extern __shared__ char shared_memory[];\n    using SharedStorage =\n        SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount,\n                      EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n\n    // Compute the number of tiles in M and N after tiling and assign scheduler\n    uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n    uint32_t tiles_in_n = uint32_t(size(ceil_div(sum_token_dims, size<2>(epilogue_tiler))));\n\n    TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, tile_scheduler_workspace,\n                            shared_storage.atomic_tile_counter);\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n\n    // Shapes for accumulated tiles in mainloop and epilogue\n    auto acc_shape_mma = make_shape(take<0, 2>(mainloop_tiler), _1{}, _1{});\n    auto acc_shape_epilogue = make_shape(take<0, 2>(epilogue_tiler), _1{}, _1{});\n\n    // Shape of the accumulator fragment for the main loop pipeline, with pipeline stages appended\n    auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n    auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n    // Number of threads assigned for various epilogue roles depending on quantization settings\n    static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n    static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n    static int constexpr NumMmaThreadCount = kEnableRHTColQuant ? 32 : 0;\n    static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant ? 1 : 0;\n    static int constexpr NumSchedThreads = 32;\n    static int constexpr NumMainloopLoadThreads = 32;\n    static int constexpr NumEpilogueThreads =\n        NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n        cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    // warp assignment\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_sched_warp = (warp_idx == 2);\n    bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n    bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    mainloop_pipeline_params.num_consumers =\n        NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                   AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n    using AccumulatorPipelineInitBarriers = cute::bool_constant<kEnableRHTColQuant>;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_col_quant_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count =\n        size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(\n        shared_storage.accumulator, accumulator_pipeline_params, cluster_shape,\n        AccumulatorPipelineInitBarriers{}, cute::true_type{});  // Delay mask calculation\n    typename SchedPipeline::Params sched_pipeline_params;\n    if (is_sched_warp) {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::ProducerConsumer;\n    } else {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::Consumer;\n    }\n    sched_pipeline_params.producer_blockid = 0;\n    sched_pipeline_params.producer_arv_count = 1;\n    sched_pipeline_params.consumer_arv_count =\n        NumSchedThreads +\n        cluster_size * (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n    sched_pipeline_params.transaction_bytes = sizeof(uint32_t);\n    sched_pipeline_params.initializing_warp = 3;\n    SchedPipeline sched_pipeline(shared_storage.sched, sched_pipeline_params, cluster_shape);\n    SchedPipelineState sched_pipeline_consumer_state;\n    SchedPipelineState sched_pipeline_producer_state =\n        cutlass::make_producer_start_state<SchedPipeline>();\n\n    typename SchedThrottlePipeline::Params sched_throttle_pipeline_params;\n    if (is_dma_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Producer;\n    }\n    if (is_sched_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Consumer;\n    }\n    sched_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n    sched_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n    sched_throttle_pipeline_params.dst_blockid = 0;\n    sched_throttle_pipeline_params.initializing_warp = 4;\n\n    SchedThrottlePipeline sched_throttle_pipeline(shared_storage.sched_throttle,\n                                                  sched_throttle_pipeline_params);\n    SchedThrottlePipelineState sched_pipeline_throttle_consumer_state;\n    SchedThrottlePipelineState sched_pipeline_throttle_producer_state =\n        cutlass::make_producer_start_state<SchedThrottlePipeline>();\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n\n    // Warp group roles: DMA (global->shared copy), MMA (tensor core gemm), scheduler, column quantizer, row quantizer\n    if (is_dma_warp) {\n      // Warp responsible for loading input from global to shared memory using TMA (Tensor Memory Access).\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      // Get TMA tensors for input matrix A and B (Hadamard/transform matrix) from global memory.\n      Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, packed_N));\n      Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n      // Partition tensors for tiling according to the mainloop and cluster tilers.\n      Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n      Tensor gB_nk =\n          local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n\n      // Shared memory tensors for pipeline\n      Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n      Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n      // Determine warp/tile positioning\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n      // Partition global to local fragments for A and B\n      Tensor tCgA = thr_mma.partition_A(gA_mk);  // (MMA,MMA_M,MMA_K,k)\n      Tensor tCgB = thr_mma.partition_B(gB_nk);  // (MMA,MMA_N,MMA_K,k)\n\n      Layout cta_layout_mnk = make_layout(cluster_shape);\n      Layout cta_layout_vmnk =\n          tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n      auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n      auto [tAgA, tAsA] =\n          tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n      auto [tBgB, tBsB] =\n          tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n      uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n      uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n      if constexpr (kEnableRHTColQuant) {\n        if (elect_one_sync()) {\n          cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                              kTmaRhtTensorTransactionBytes);\n          copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n               tBsB(_, 0));\n        }\n      }\n\n      do {\n        // is_first_wave indicates whether this scheduler wave is the first among a group.\n        bool is_first_wave = scheduler.is_first_wave();\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, scheduler.tile_m(), _);\n        int k_tile = 0;\n\n        sched_throttle_pipeline.producer_acquire(sched_pipeline_throttle_producer_state);\n        sched_throttle_pipeline.producer_commit(sched_pipeline_throttle_producer_state);\n        ++sched_pipeline_throttle_producer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n          int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n        // scheduler.advance();\n      } while (scheduler.is_valid());\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      // This warp executes the main tensor core matrix-multiply-accumulate for the Hadamard transform.\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      if constexpr (kEnableRHTColQuant) {\n        // Setup shared memory fragments for A and B tiles.\n        Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                  sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n        Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                  sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n        int block_rank_in_cluster = cute::block_rank_in_cluster();\n        ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n        // Allocate \"fragments\" -- these are actually umma smem descriptors\n        Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n        Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n        mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n        tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                                &shared_storage.tmem_base_ptr);\n        __syncwarp();\n        tmem_allocation_result_barrier.arrive();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_mma.data() = tmem_base_ptr;\n        // Wait until the B (Hadamard) tensor copy is complete\n        cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n        do {\n          uint32_t skip_wait = K_TILE_MAX <= 0;\n\n          auto barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            int read_stage = mainloop_pipe_consumer_state.index();\n            auto tCrA_mk = tCrA(_, _, _, read_stage);\n            auto tCrB_nk = tCrB(_, _, 0, 0);\n            CUTLASS_PRAGMA_UNROLL\n            for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block) {\n              int accumulator_k_block =\n                  accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n              int tCrA_k_block = k_block * EpilogueUnrollFactor;\n              accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n              CUTLASS_PRAGMA_UNROLL\n              for (int i = 0; i < EpilogueUnrollFactor; i++) {\n                auto accumulators = bulk_tmem_mma(_, _, _, accumulator_k_block + i);\n                gemm(mma, tCrA_mk(_, _, tCrA_k_block + i), tCrB_nk, accumulators);\n              }\n\n              accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n              ++accumulator_pipe_producer_state;\n            }\n            auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n            skip_wait = k_tile >= K_TILE_MAX;\n            mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n            barrier_token =\n                mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n        tmem_allocator.release_allocation_lock();\n        accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n        tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n      }\n    } else if (is_sched_warp) {\n      // Scheduler warp manages tile assignment and pipeline progress for warps\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      do {\n        sched_throttle_pipeline.consumer_wait(sched_pipeline_throttle_consumer_state);\n        sched_throttle_pipeline.consumer_release(sched_pipeline_throttle_consumer_state);\n        ++sched_pipeline_throttle_consumer_state;\n        sched_pipeline_producer_state =\n            scheduler.advance_to_next_work(sched_pipeline, sched_pipeline_producer_state);\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    } else if (is_epilogue_col_quant_warp) {\n      // Warp responsible for quantizing output of Hadamard transform to FP4 for columnwise usage,\n      // and writing result tensors/scales to global memory.\n      cutlass::arch::warpgroup_reg_alloc<192>();\n      if constexpr (kEnableRHTColQuant) {\n        using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n        auto acc_epilogue_pipelined_shape =\n            append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n        auto bulk_tmem_epilogue_layout = make_layout(\n            acc_epilogue_pipelined_shape,\n            make_stride(stride<0>(bulk_tmem_mma), Int<0>{}, Int<0>{}, size<1>(epilogue_tiler)));\n        auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n        // Use 256-bit fragments for aligned bulk stores\n        static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n        // Wait for TMEM allocation for this pipeline to finish\n        tmem_allocation_result_barrier.arrive_and_wait();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_epilogue.data() = tmem_base_ptr;\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n        // g2s load all global_d_amax\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < num_tensors; g += NumEpilogueColQuantThreadCount) {\n          shared_storage.global_d_amax[g] = __ldg(reinterpret_cast<float *>(amax_colwise + g));\n        }\n\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // Setup RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // TODO(zhongbo): double check the logic here\n        int group_idx = get_current_tensor_id(\n            shape_rep, num_tensors, (scheduler.tile_n_base() * size<1>(epilogue_tiler)) * M,\n            packed_N, M, offsets);\n\n        // Determine quantization scale factor layouts/output splits for this group\n        TSFDLayout sfd_layout;\n        int cur_N = static_cast<int>(first_dims[group_idx]);\n        if constexpr (kEnableSwizzleSFOutput) {\n          sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n        } else {\n          sfd_layout = make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                   make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n        }\n        // Build output tensors for columns and their quant scales\n        // TODO(zhongbo): double check the logic here\n        Tensor mD = make_tensor(cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(\n                                    reinterpret_cast<char *>(QA_COLWISE) + offsets[group_idx] / 2)),\n                                make_shape(M, cur_N), DStride{});  // (M,packed_N)\n        Tensor gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                  Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        // for every tensor [x, y] row major, x y both a multiple of 128\n        // both of its rowwise and colwise scaling factors will have exactly x * y / 16 elements in FP8 E4M3\n        Tensor mSFD = make_tensor(\n            make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(reinterpret_cast<char *>(SFA_COLWISE) +\n                                                         offsets[group_idx] / kNVFP4BlockSize)),\n            sfd_layout);\n        Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n\n        // Setup tile-level TMEM (t2r) and global memory (r2g) copy descriptors\n        auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n        auto tiled_r2g =\n            make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{}, tiled_t2r);\n        auto thr_t2r = tiled_t2r.get_slice(local_thread_idx);\n        auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n        cutlass::arch::NamedBarrier::sync(NumEpilogueColQuantThreadCount,\n                                          cutlass::arch::ReservedNamedBarriers::EpilogueBarrier);\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float c_global_amax_val = shared_storage.global_d_amax[group_idx];\n        float global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n        float global_decode_scale = 1.0f / global_encode_scale;\n\n        // Scaling factor for fast math path\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n        do {\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();\n               ++k_tile) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            // TODO(zhongbo): double check the logic here\n            int cur_group_idx = get_current_tensor_id(\n                shape_rep, num_tensors, global_tile_n_offset * M, packed_N, M, offsets);\n\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              c_global_amax_val = shared_storage.global_d_amax[group_idx];\n              // update amax\n              global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n              // TODO(zhongbo): double check the logic here\n              cur_N = first_dims[group_idx];\n              if constexpr (kEnableSwizzleSFOutput) {\n                sfd_layout =\n                    tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n              } else {\n                sfd_layout =\n                    make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n              }\n              // update tensor\n              mD = make_tensor(cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(\n                                   reinterpret_cast<char *>(QA_COLWISE) + offsets[group_idx] / 2)),\n                               make_shape(M, cur_N), DStride{});\n              gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                 Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n              mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                     reinterpret_cast<char *>(SFA_COLWISE) +\n                                     offsets[group_idx] / kNVFP4BlockSize)),\n                                 sfd_layout);\n              gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                   Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n              gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n            }\n            int group_start_offset = offsets[group_idx] / M;\n            int local_tile_n_idx =\n                (global_tile_n_offset - group_start_offset) / size<1>(epilogue_tiler);\n            Tensor tDgD_mn = gD_mn_view(_, _, _, scheduler.tile_m(), local_tile_n_idx);\n\n            Tensor tDgSFD_mn = gSFD_mn(_, _, scheduler.tile_m(), local_tile_n_idx);\n            accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n            auto Acc = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n            Tensor tDtAcc = thr_t2r.partition_S(Acc);    // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDgD = thr_t2r.partition_D(tDgD_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n            Tensor tTR_rAcc = make_tensor<ElementAccumulator>(\n                shape(tDgD));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDrD = make_tensor<TD>(shape(tDgD));\n            Tensor tTR_rAcc_frag =\n                recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n            Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n            Tensor src = thr_r2g.retile_S(tDrD);\n            Tensor dst = thr_r2g.retile_D(tDgD);\n\n            Tensor tDgSFD_view = make_tensor(\n                tDgSFD_mn.data(), make_layout(make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                                              make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n            Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n            Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n\n            static int constexpr NumVecs = size(tDgD) / VectorSize;\n            Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n            // Compute amax and quantization scales for this tile\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n            cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n            // Copy from TMEM to registers\n            copy(tiled_t2r, tDtAcc, tTR_rAcc);\n            cutlass::arch::fence_view_async_tmem_load();\n            accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n            ++accumulator_pipe_consumer_state;\n\n            if constexpr (!kUseFastMath) {\n              // Downcast to BF16 for bit-wise compatibility with\n              // unfused kernels\n              auto convert_accum_to_bf16 =\n                  cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                                 FragmentSize>{};\n              auto convert_bf16_to_accum =\n                  cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                                 FragmentSize>{};\n              tTR_rAcc_frag(_0{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n              tTR_rAcc_frag(_1{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n            }\n\n            auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n                tTR_rAcc_frag.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TD, VectorSize> *>(tDrD_frag.data());\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < NumVecs; v++) {\n              vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n            }\n\n            pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                vec_maxs, global_encode_scale_multiplier);\n            auto pvscales_cvted =\n                cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n            tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n            auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(\n                tD_rRowSFD_frg(_0{}));\n            auto qpvscale_scaled =\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                    qpvscale_ups, global_decode_scale);\n            cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n            if constexpr (kUseFastMath) {\n              // Fast math: compute approximate reciprocal\n              acc_scales =\n                  cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // Accurate math: compute reciprocal with division\n              acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                  1.0, qpvscale_scaled);\n            }\n\n            // Prepare stochastic rounding random state if enabled\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            // \"Prefetch\" a stochastic rounding state for the first tile\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            // Apply round/quantize to each fragment, with or without stochastic rounding\n            for (int v = 0; v < NumVecs; v++) {\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs[v], acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs[v], acc_scale));\n              }\n            }\n\n            // Write quantized FP4 tile and dequant scale to gmem\n            copy(tiled_r2g, src, dst);\n            copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFD, tDgSFD);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n    } else if (is_epilogue_row_quant_warp) {\n      // Warp responsible for quantizing the input (before Hadamard transform) to FP4 for row-wise usage.\n      cutlass::arch::warpgroup_reg_alloc<136>();\n      if constexpr (kEnableRowQuant) {\n        using S2RVectorType = uint128_t;\n\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % 256;\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // g2s load all global_a_amax for all groups/tensors\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < num_tensors; g += NumEpilogueRowQuantThreadCount) {\n          shared_storage.global_a_amax[g] = __ldg(reinterpret_cast<float *>(amax_rowwise + g));\n        }\n        // RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // Input/output tensors/partitions for row quant warp\n        Tensor mQA =\n            make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, packed_N), dQA));\n        Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n        Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n\n        Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, X, _1>{});  // (BLK_M,BLK_N)\n        // Swizzled shared memory A tile, with layout\n        Tensor sA = as_position_independent_swizzle_tensor(group_modes<0, 2>(\n            coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                 sAlayout))));  // (BLOCK_M, BLOCK_M,PIPE)\n\n        // Set up layouts for partitioning \u2013 tile-by-warp, with vector granularity\n        using S2RWarpLayout = Layout<Shape<_8, _4>>;\n        using WarpGroupLayout = Layout<Shape<_1, _8>>;\n        using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n        using S2RValLayout = Layout<Shape<Int<VectorSize>, _1>>;\n        using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n        using R2GAtomQA = Copy_Atom<AutoVectorizingCopy, TQA>;\n        using R2GAtomSFA = Copy_Atom<AutoVectorizingCopy, TSFA>;\n        auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_QA = make_tiled_copy(R2GAtomQA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_SFA = make_tiled_copy(R2GAtomSFA{}, S2RThreadLayout{}, S2RValLayout{});\n\n        auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n        auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n        auto thr_r2g_SFA = tiled_r2g_SFA.get_slice(local_thread_idx);\n        Tensor tQAsA = thr_s2r.partition_S(sA);  // (Copy, Copy_M, Copy_N, PIPE)\n\n        // Allocate temporary register tensors for copying quantization => output\n        Tensor tQArA = make_tensor_like<TA>(\n            make_layout(tQAsA(_, _, _, _0{}).shape()));  // (Copy, Copy_M, Copy_N)\n        Tensor tQAgQA = thr_r2g_QA.partition_S(gQA_mn);\n        Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n\n        Tensor tQAgSFA = thr_r2g_SFA.partition_S(gSFA_mn);\n        Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n\n        // Will result in barrier_id=10 passed to bar.sync instr as cutlass adds 8\n        // in order to go over the reserved named barrier count.\n        constexpr int row_quant_barrier_id = 2;\n        cutlass::arch::NamedBarrier::sync(NumEpilogueRowQuantThreadCount, row_quant_barrier_id);\n\n        int group_idx = get_current_tensor_id(\n            shape_rep, num_tensors, (scheduler.tile_n_base() * size<1>(epilogue_tiler)) * M,\n            packed_N, M, offsets);\n        float a_global_amax_val = shared_storage.global_a_amax[group_idx];\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n\n        float global_decode_scale = 1.0f / global_encode_scale;\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n        auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n        do {\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = get_current_tensor_id(\n                shape_rep, num_tensors, global_tile_n_offset * M, packed_N, M, offsets);\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              a_global_amax_val = shared_storage.global_a_amax[group_idx];\n              // Update group quantization parameters/scaling\n              global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            }\n\n            auto tQAgSFA_mn =\n                tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state);\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n            cutlass::arch::fence_view_async_shared();\n            mainloop_pipeline.consumer_release(mainloop_pipe_consumer_state);\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n\n            // static int constexpr NumVecs = size(tQArA) / VectorSize;\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(\n                raw_pointer_cast(tQArQA.data()));\n            Tensor amax =\n                make_tensor<ElementAccumulator>(prepend(take<1, rank(tQArA)>(tQArA.shape()), _1{}));\n            Tensor pvscales = make_tensor_like<ElementAccumulator>(amax);\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512 +\n                                          tiles_in_m * tiles_in_n * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < size<1>(group_modes<1, rank(tQArA)>(tQArA)); v++) {\n              auto amax_view = group_modes<1, rank(amax)>(amax);\n              auto pvscales_view = group_modes<1, rank(pvscales)>(pvscales);\n              auto compute_frgs_up =\n                  cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(\n                      compute_frgs[v]);\n              amax_view(_0{}, v) = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n              pvscales_view(_0{}, v) = cutlass::multiplies<ElementAccumulator>{}(\n                  amax_view(_0{}, v), global_encode_scale_multiplier);\n              filter(tQArSFA)(v) = sfa_converter(pvscales_view(_0{}, v));\n              auto qpvscale_ups =\n                  cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n              auto qpvscale_scaled =\n                  cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n              ElementAccumulator acc_scales;\n              if constexpr (kUseFastMath) {\n                // Fast math: compute approximate reciprocal\n                acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(\n                    qpvscale_scaled);\n              } else {\n                // Accurate math: compute reciprocal with division\n                acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n              }\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales, cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              uint4 random_uint4 = uint4{0, 0, 0, 0};\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs_up, acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs_up, acc_scale));\n              }\n            }\n            copy(tiled_r2g_QA, tQArQA, tQAgQA_mn);\n            copy(tiled_r2g_SFA, filter(tQArSFA), filter(tQAgSFA_mn));\n          }\n          // scheduler.advance();\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n\n    } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n    }\n  }\n}  // NOLINT(readability/fn_size)\n\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant,\n          bool kEnableSwizzleSFOutput, class TA, class TB, class TQA, class TSFA, class TD = TQA,\n          class TSFD = TSFA, bool kUseFastMath = false>\nvoid group_row_col_rht_gemm_ntt_w_sfc_graph_safe(\n    int packed_sequence_length, int hidden_size, size_t num_tensors, ShapeRepresentation shape_rep,\n    TA const *A, TB const *B, TQA *QA, TSFA *SFA, TQA *QA_COLWISE, TSFA *SFA_COLWISE,\n    float *amax_rowwise, float *amax_colwise, const int64_t *offsets, const int64_t *first_dims,\n    const size_t *rng_state, uint32_t *tile_scheduler_workspace, uint32_t sm_count,\n    cudaStream_t stream, int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0),\n                                               make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFDLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)),\n                                               make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(\n      SwizzledSFALayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(\n      SwizzledSFDLayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFDLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFDLayout>;\n  SFALayout sfa_layout;\n  SFDLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{});\n  } else {\n    sfa_layout = make_layout(\n        make_shape(make_shape(Int<SFVecSize>{}, hidden_size / SFVecSize), packed_sequence_length),\n        make_stride(make_stride(_0{}, _1{}), hidden_size / SFVecSize));\n    sfd_layout = make_layout(\n        make_shape(hidden_size, make_shape(Int<SFVecSize>{}, packed_sequence_length / SFVecSize)),\n        make_stride(packed_sequence_length / SFVecSize, make_stride(_0{}, _1{})));\n  }\n\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = packed_sequence_length;\n  Tensor tensorA = make_tensor(A, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);    // (dM,dK)\n  auto dB = stride(tensorB);    // (dN,dK)\n  auto dD = LayoutRight{};      // (dM,dN)\n  auto dQA = stride(tensorQA);  // (dM,dK)\n  using ClusterShape = Shape<_1, _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128, Int<RhtTensorSize>, Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128, Int<RhtTensorSize>, _128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n      size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                           UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cluster_tile_shape),\n      shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cluster_tile_shape),\n      shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem / (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 4;\n  static int constexpr MainloopPipelineBytes = sizeof(\n      typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<1, Shape<_1, _1, _1>,\n                                                               Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr SchedulerWorkspaceBytes = sizeof(int) * SchedulerPipelineStageCount;\n  static int constexpr SchedulerThrottlePipelineBytes =\n      sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr SchedulerPipelineBytes =\n      sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount,\n                                                     ClusterShape>::SharedStorage);\n\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(\n      typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                          Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448;  // 232KB in bytes\n  static int constexpr kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = SchedulerWorkspaceBytes + SchedulerThrottlePipelineBytes +\n                                        SchedulerPipelineBytes + TmemBasePtrsBytes +\n                                        TmemDeallocBytes + BTensorBytes +\n                                        AccPipelineBytes;  // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP),\n                                    Step<_2, _1, _3>{});  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, _1{}));  // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};                                        // XXX Dummy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cluster_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  assert(M % size<0>(cluster_tile_shape) == 0);\n  assert(N % size<1>(cluster_tile_shape) == 0);\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(sm_count, 1, 1);\n\n  int smem_size = sizeof(\n      SharedStorage<TA, TB, decltype(sA), decltype(sB), ClusterShape, AccumulatorPipelineStageCount,\n                    EpilogueUnrollFactor, SchedulerPipelineStageCount>);\n\n  auto *kernel_ptr = &group_row_col_rht_gemm_device_graph_safe<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cluster_shape),\n      decltype(cluster_tile_shape), TA, decltype(dA), decltype(sA), decltype(tma_load_a), TB,\n      decltype(dB), decltype(sB), decltype(tma_load_b), TD, decltype(dD), decltype(sD), TSFD,\n      decltype(sfd_layout), TQA, decltype(dQA), TSFA, decltype(sfa_layout), decltype(mma),\n      AccumulatorPipelineStageCount, SchedulerPipelineStageCount, kEnableStochasticRounding,\n      kEnableRHTColQuant, kEnableRowQuant, kEnableSwizzleSFOutput, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      cudaFuncSetAttribute(*kernel_ptr, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  // Set workspace and set to zero\n  NVTE_CHECK_CUDA(cudaMemsetAsync(reinterpret_cast<void *>(tile_scheduler_workspace), 0,\n                                  sizeof(uint32_t), stream));\n\n  // Launch kernel\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n      params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape, A, dA,\n      sA, tma_load_a, B, dB, sB, tma_load_b, QA, dQA, SFA, sfa_layout, QA_COLWISE, SFA_COLWISE,\n      amax_rowwise, amax_colwise, offsets, first_dims, num_tensors, shape_rep,\n      tile_scheduler_workspace, mma, rng_state);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion_graph_safe(const GroupedTensor *input,\n                                                     GroupedTensor *output,\n                                                     const Tensor &hadamard_matrix_,\n                                                     QuantizationConfig &quant_config,\n                                                     Tensor &quant_workspace, cudaStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion_graph_safe);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::ShapeRepresentation;\n\n  void *input_base_ptr = reinterpret_cast<void *>(input->data.dptr);\n  // TODO(zhongbo): add input sanity checks here\n\n  bool all_has_row_quant = output->has_data();\n  bool all_has_col_quant = output->has_columnwise_data();\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (use_stochastic_rounding) {\n    NVTE_CHECK(quant_config.rng_state != nullptr,\n               \"Enabled stochastic rounding without providing RNG state\");\n    const Tensor &rng_state_tensor = *convertNVTETensorCheck(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  uint32_t *tile_scheduler_workspace = nullptr;\n  NVTE_CHECK(quant_workspace.data.dptr != nullptr, \"Quantization workspace must be provided.\");\n  NVTE_CHECK(quant_workspace.data.buffer_size_bytes() >= sizeof(uint32_t),\n             \"Quantization workspace must be at least 4 bytes.\");\n  tile_scheduler_workspace = reinterpret_cast<uint32_t *>(quant_workspace.data.dptr);\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t num_tensors = input->num_tensors;\n  const size_t first_logical_dim = input->logical_shape.data[0];\n  const size_t last_logical_dim = input->logical_shape.data[1];\n  // const size_t elts_total = first_logical_dim * last_logical_dim;\n  NVTE_CHECK(first_logical_dim % 128 == 0,\n             \"First dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(last_logical_dim % 128 == 0,\n             \"Last dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  ShapeRepresentation shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  if (output->all_same_shape()) {\n    shape_rep = ShapeRepresentation::SAME_BOTH_DIMS;\n  } else if (output->all_same_first_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_LAST_DIM;\n  } else if (output->all_same_last_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  } else if (output->varying_both_dims()) {\n    shape_rep = ShapeRepresentation::VARYING_BOTH_DIMS;\n  }\n\n  TQA *const rowwise_data_base_ptr = reinterpret_cast<TQA *>(output->data.dptr);\n  TSFA *const rowwise_scale_inv_base_ptr = reinterpret_cast<TSFA *>(output->scale_inv.dptr);\n  TQA *const colwise_data_base_ptr = reinterpret_cast<TQA *>(output->columnwise_data.dptr);\n  TSFA *const colwise_scale_inv_base_ptr =\n      reinterpret_cast<TSFA *>(output->columnwise_scale_inv.dptr);\n  float *const amax_rowwise_base_ptr = reinterpret_cast<float *>(output->amax.dptr);\n  float *const amax_colwise_base_ptr = reinterpret_cast<float *>(output->columnwise_amax.dptr);\n\n  const int64_t *const offsets_ptr = reinterpret_cast<const int64_t *>(output->tensor_offsets.dptr);\n  const int64_t *const first_dims_ptr = reinterpret_cast<const int64_t *>(output->first_dims.dptr);\n\n  const bool is_const_last_dim = (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS ||\n                                  shape_rep == ShapeRepresentation::VARYING_FIRST_DIM);\n  NVTE_CHECK(is_const_last_dim,\n             \"Currently we only support const last dimension for graph safe hadamard transform.\");\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  int k_tile_size = 1024;\n\n  const bool use_swizzle_sf_output = output->with_gemm_swizzled_scales;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          all_has_col_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_has_row_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::group_row_col_rht_gemm_ntt_w_sfc_graph_safe<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TQA, TSFA, TD, TSFD, kUseFastMath>(\n                            /*packed_sequence_length=*/first_logical_dim,\n                            /*hidden_size=*/last_logical_dim,\n                            /*num_tensors=*/num_tensors,\n                            /*shape_rep=*/shape_rep,\n                            /*A=*/reinterpret_cast<TA const *>(input_base_ptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_base_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_base_ptr),\n                            /*QA_COLWISE=*/reinterpret_cast<TQA *>(colwise_data_base_ptr),\n                            /*SFA_COLWISE=*/reinterpret_cast<TSFA *>(colwise_scale_inv_base_ptr),\n                            /*amax_rowwise=*/reinterpret_cast<float *>(amax_rowwise_base_ptr),\n                            /*amax_colwise=*/reinterpret_cast<float *>(amax_colwise_base_ptr),\n                            /*offsets=*/offsets_ptr,\n                            /*first_dims=*/first_dims_ptr,\n                            /*rng_state=*/rng_state,\n                            /*tile_scheduler_workspace=*/tile_scheduler_workspace,\n                            /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion_graph_safe(\n    const NVTEGroupedTensor input, NVTEGroupedTensor output, const NVTETensor hadamard_matrix,\n    const NVTEQuantizationConfig quant_config, NVTETensor quant_workspace, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_cast_fusion_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor *input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor *output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  Tensor *quant_workspace_tensor = convertNVTETensorCheck(quant_workspace);\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion_graph_safe(\n      input_tensor, output_tensor, *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n      *quant_workspace_tensor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\n// Ensure Tensor refers to cute::Tensor, not transformer_engine::Tensor\nusing cute::Tensor;\n\nconstexpr int kMaxTensorsPerKernel = 64;\nconstexpr int kNVFP4BlockSize = 16;\n\nenum ShapeRepresentation {\n  SAME_BOTH_DIMS = 0,\n  VARYING_FIRST_DIM = 1,\n  VARYING_LAST_DIM = 2,\n  VARYING_BOTH_DIMS = 3\n};\n\n__device__ __forceinline__ size_t get_current_tensor_id(\n    const ShapeRepresentation shape_rep, const size_t num_tensors, const size_t current_offset,\n    const size_t first_logical_dim, const size_t last_logical_dim,\n    const int64_t *const __restrict__ offsets_ptr) {\n  if (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS) {\n    const size_t current_row = current_offset / last_logical_dim;\n    const size_t rows_per_tensor = first_logical_dim / num_tensors;\n    return current_row / rows_per_tensor;\n  } else {\n    // upper_bound(offsets, current_offset) - 1 in range i in [0..num_tensors)\n    size_t low = 0;\n    size_t hi = num_tensors;  // half-open [low, hi)\n\n    while (low < hi) {\n      const size_t mid = low + (hi - low) / 2;\n      const size_t mid_offset = static_cast<size_t>(offsets_ptr[mid]);\n\n      if (mid_offset <= current_offset) {\n        low = mid + 1;\n      } else {\n        hi = mid;\n      }\n    }\n\n    // low = first index where offsets[low] > current_offset (or low == num_tensors)\n    // id = low - 1, but need to evaluate if current_offset < offsets[0]\n    return (low == 0) ? 0 : (low - 1);\n  }\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout, class ClusterShape,\n          int AccumulatorPipelineStageCount_, int EpilogueUnrollFactor_,\n          int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                 AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>,\n                                                      AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using SchedPipelineStorage = typename SchedPipeline::SharedStorage;\n  using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using SchedThrottlePipelineStorage = typename SchedThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) SchedPipelineStorage sched;\n  alignas(16) SchedThrottlePipelineStorage sched_throttle;\n  alignas(16) int32_t atomic_tile_id[SchedulerPipelineStageCount_];\n  alignas(16) float global_a_amax[kMaxTensorsPerKernel];\n  alignas(16) float global_d_amax[kMaxTensorsPerKernel];\n  uint32_t atomic_tile_counter[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\n// Main RHT GEMM kernel entry -- highly templated for flexible architecture/config support\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA, class TB, class BStride,\n          class BSmemLayout, class TmaLoadB, class TD, class DStride, class DSmemLayout, class TSFD,\n          class TSFDLayout, class TQA, class QAStride, class TSFA, class TSFALayout, class TiledMMA,\n          int AccumulatorPipelineStageCount_, int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false, bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true, bool kEnableSwizzleSFOutput_ = false,\n          bool kUseFastMath_ = false>\n__launch_bounds__(512, 1) __global__ static void group_row_col_rht_gemm_device_graph_safe(\n    MShape M, NShape packed_N, KShape K, ClusterShape cluster_shape, ClusterTileShape cluster_tile,\n    TA const *A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const *B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TQA *QA, QAStride dQA, TSFA *SFA, TSFALayout sfa_layout, TQA *QA_COLWISE, TSFA *SFA_COLWISE,\n    float *amax_rowwise, float *amax_colwise, const int64_t *offsets, const int64_t *first_dims,\n    size_t num_tensors, ShapeRepresentation shape_rep, uint32_t *tile_scheduler_workspace,\n    TiledMMA mma, const size_t *rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                  \"group_row_col_rht_gemm_device_graph_safe must generate row-wise \"\n                  \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n    CUTLASS_NOT_IMPLEMENTED();\n    return;\n#endif\n\n    using X = Underscore;\n    // Accumulator data type for main computation\n    using ElementAccumulator = float;\n    static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n    static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n    static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n    static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n    static constexpr bool kEnableSwizzleSFOutput = kEnableSwizzleSFOutput_;\n    static constexpr bool kUseFastMath = kUseFastMath_;\n\n    // Constant for RHT tensor processing (tile size etc)\n    static int constexpr RhtTensorSize = 16;\n\n    // Get the total number of tokens to process\n    // Note that here M is the hidden size, which is the last logical dimension of the input tensor x\n    // The kernel is designed in column major, so M is the hidden size\n    size_t sum_token_dims = offsets[num_tensors] / M;\n\n    // Transaction bytes for TMA transfer on RHT tensor blocks\n    static int constexpr kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n    static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n    static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n    // Mainloop pipeline stage calculation, vectorization parameters for scaling factors\n    static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    static int constexpr SFVecSize = 16;\n    // Swizzle output layout for scaling factor arrays\n    using SwizzledSFALayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n    using SwizzledSFDLayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n\n    // Mainloop pipeline types for TMA async execution and epilogue cluster scheduling\n    using MainloopPipeline =\n        cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, ClusterShape,\n                                                        AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n    using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n    using SchedPipelineState = typename SchedPipeline::PipelineState;\n    using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n    using SchedThrottlePipelineState = typename SchedThrottlePipeline::PipelineState;\n\n    static_assert(ClusterShape{} == Shape<_1, _1, _1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static int constexpr VectorSize = RhtTensorSize;\n\n    // Compile-time safety: static shapes required for shared memory layouts\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    //   CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n\n    auto cluster_size = size<0>(cluster_shape);\n    auto mainloop_tiler = Shape<_128, _16, _128>{};\n    auto epilogue_tiler = Shape<_128, _128, _128>{};\n\n    static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    int const K_TILE_MAX = min(packed_N, K) / size<2>(epilogue_tiler);\n\n    struct TileScheduler {\n      uint32_t tiles_in_m = 0;\n      uint32_t tiles_in_n = 0;\n      uint32_t linear_idx = 0;\n      uint32_t next_linear_idx = 0;\n      uint32_t start_idx = 0;\n      uint32_t tile_m_idx = 0;\n      uint32_t tile_n_idx = 0;\n      int k_tile_max = 0;\n      uint32_t *atomic_tile_index_;\n      uint32_t *smem_tile_counter;\n      uint32_t atomic_offset;\n      cutlass::FastDivmodU64 divmod_tiles_in_m;\n\n      CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax,\n                                   uint32_t *atomic_tile_index, uint32_t *smem_tile_counter)\n          : tiles_in_m(tiles_m),\n            tiles_in_n(tiles_n),\n            linear_idx(blockIdx.x),\n            next_linear_idx(blockIdx.x),\n            start_idx(blockIdx.x),\n            k_tile_max(kmax),\n            atomic_tile_index_(atomic_tile_index),\n            smem_tile_counter(smem_tile_counter),\n            atomic_offset(gridDim.x),\n            divmod_tiles_in_m(uint64_t(tiles_m)) {\n        update_tile_idx();\n      }\n      CUTLASS_DEVICE void update_tile_idx() {\n        uint64_t q, r;\n        divmod_tiles_in_m(q, r, uint64_t(linear_idx));\n        tile_m_idx = static_cast<uint32_t>(r);\n        tile_n_idx = static_cast<uint32_t>(q) * uint32_t(k_tile_max);\n      }\n      CUTLASS_DEVICE uint32_t tile_m() const { return tile_m_idx; }\n      CUTLASS_DEVICE uint32_t tile_n_base() const { return tile_n_idx; }\n      CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n\n      CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n\n      CUTLASS_DEVICE bool is_valid() const {\n        return cute::elem_less(cute::make_coord(tile_m(), tile_n_base()),\n                               cute::make_coord(tiles_in_m, tiles_in_n));\n      }\n\n      CUTLASS_DEVICE bool is_first_wave() const { return linear_idx == start_idx; }\n\n      CUTLASS_DEVICE uint32_t get_linear_tile_idx() const { return linear_idx; }\n\n      // Fetch a new tile_id using atomics.\n      CUTLASS_DEVICE uint32_t fetch_tile_id_counter(int pred) {\n        uint32_t tile_id_counter = 0;\n        asm volatile(\n            \"{\\n\\t\"\n            \".reg .pred p;\\n\\t\"\n            \"setp.eq.u32 p, %2, 1;\\n\\t\"\n            \"@p atom.global.add.u32 %0, [%1], 1; \\n\\t\"\n            \"}\"\n            : \"=r\"(tile_id_counter)\n            : \"l\"(atomic_tile_index_), \"r\"(pred));\n\n        return tile_id_counter;\n      }\n\n      CUTLASS_DEVICE auto fetch_next_work(SchedPipeline &sched_pipeline,\n                                          SchedPipelineState sched_pipeline_consumer_state) {\n        sched_pipeline.consumer_wait(sched_pipeline_consumer_state);\n        next_linear_idx = smem_tile_counter[sched_pipeline_consumer_state.index()];\n        cutlass::arch::fence_view_async_shared();\n        sched_pipeline.consumer_release(sched_pipeline_consumer_state);\n        return;\n      }\n\n      CUTLASS_DEVICE auto advance_to_next_work(SchedPipeline &sched_pipeline,\n                                               SchedPipelineState sched_pipeline_producer_state) {\n        uint32_t mbarrier_addr = sched_pipeline.producer_get_barrier(sched_pipeline_producer_state);\n        // Wait for clcID buffer to become empty with a flipped phase\n        sched_pipeline.producer_acquire(sched_pipeline_producer_state);\n        auto is_leading_thread = cute::elect_one_sync();\n        uint32_t tile_id_counter = fetch_tile_id_counter(is_leading_thread) + atomic_offset;\n        uint32_t smem_addr =\n            cute::cast_smem_ptr_to_uint(&smem_tile_counter[sched_pipeline_producer_state.index()]);\n        if (is_leading_thread) {\n          cute::store_shared_remote(tile_id_counter, smem_addr, mbarrier_addr, 0);\n        }\n\n        ++sched_pipeline_producer_state;\n        return sched_pipeline_producer_state;\n      }\n\n      CUTLASS_DEVICE auto update_work_tile_info() {\n        linear_idx = next_linear_idx;\n        update_tile_idx();\n        return;\n      }\n    };\n\n    // Allocate and alias shared memory to the kernel's shared storage type\n    extern __shared__ char shared_memory[];\n    using SharedStorage =\n        SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount,\n                      EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n\n    // Compute the number of tiles in M and N after tiling and assign scheduler\n    uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n    uint32_t tiles_in_n = uint32_t(size(ceil_div(sum_token_dims, size<2>(epilogue_tiler))));\n\n    TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, tile_scheduler_workspace,\n                            shared_storage.atomic_tile_counter);\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n\n    // Shapes for accumulated tiles in mainloop and epilogue\n    auto acc_shape_mma = make_shape(take<0, 2>(mainloop_tiler), _1{}, _1{});\n    auto acc_shape_epilogue = make_shape(take<0, 2>(epilogue_tiler), _1{}, _1{});\n\n    // Shape of the accumulator fragment for the main loop pipeline, with pipeline stages appended\n    auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n    auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n    // Number of threads assigned for various epilogue roles depending on quantization settings\n    static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n    static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n    static int constexpr NumMmaThreadCount = kEnableRHTColQuant ? 32 : 0;\n    static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant ? 1 : 0;\n    static int constexpr NumSchedThreads = 32;\n    static int constexpr NumMainloopLoadThreads = 32;\n    static int constexpr NumEpilogueThreads =\n        NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n        cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    // warp assignment\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_sched_warp = (warp_idx == 2);\n    bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n    bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    mainloop_pipeline_params.num_consumers =\n        NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                   AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n    using AccumulatorPipelineInitBarriers = cute::bool_constant<kEnableRHTColQuant>;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_col_quant_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count =\n        size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(\n        shared_storage.accumulator, accumulator_pipeline_params, cluster_shape,\n        AccumulatorPipelineInitBarriers{}, cute::true_type{});  // Delay mask calculation\n    typename SchedPipeline::Params sched_pipeline_params;\n    if (is_sched_warp) {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::ProducerConsumer;\n    } else {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::Consumer;\n    }\n    sched_pipeline_params.producer_blockid = 0;\n    sched_pipeline_params.producer_arv_count = 1;\n    sched_pipeline_params.consumer_arv_count =\n        NumSchedThreads +\n        cluster_size * (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n    sched_pipeline_params.transaction_bytes = sizeof(uint32_t);\n    sched_pipeline_params.initializing_warp = 3;\n    SchedPipeline sched_pipeline(shared_storage.sched, sched_pipeline_params, cluster_shape);\n    SchedPipelineState sched_pipeline_consumer_state;\n    SchedPipelineState sched_pipeline_producer_state =\n        cutlass::make_producer_start_state<SchedPipeline>();\n\n    typename SchedThrottlePipeline::Params sched_throttle_pipeline_params;\n    if (is_dma_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Producer;\n    }\n    if (is_sched_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Consumer;\n    }\n    sched_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n    sched_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n    sched_throttle_pipeline_params.dst_blockid = 0;\n    sched_throttle_pipeline_params.initializing_warp = 4;\n\n    SchedThrottlePipeline sched_throttle_pipeline(shared_storage.sched_throttle,\n                                                  sched_throttle_pipeline_params);\n    SchedThrottlePipelineState sched_pipeline_throttle_consumer_state;\n    SchedThrottlePipelineState sched_pipeline_throttle_producer_state =\n        cutlass::make_producer_start_state<SchedThrottlePipeline>();\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n\n    // Warp group roles: DMA (global->shared copy), MMA (tensor core gemm), scheduler, column quantizer, row quantizer\n    if (is_dma_warp) {\n      // Warp responsible for loading input from global to shared memory using TMA (Tensor Memory Access).\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      // Get TMA tensors for input matrix A and B (Hadamard/transform matrix) from global memory.\n      Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, packed_N));\n      Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n      // Partition tensors for tiling according to the mainloop and cluster tilers.\n      Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n      Tensor gB_nk =\n          local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n\n      // Shared memory tensors for pipeline\n      Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n      Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n      // Determine warp/tile positioning\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n      // Partition global to local fragments for A and B\n      Tensor tCgA = thr_mma.partition_A(gA_mk);  // (MMA,MMA_M,MMA_K,k)\n      Tensor tCgB = thr_mma.partition_B(gB_nk);  // (MMA,MMA_N,MMA_K,k)\n\n      Layout cta_layout_mnk = make_layout(cluster_shape);\n      Layout cta_layout_vmnk =\n          tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n      auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n      auto [tAgA, tAsA] =\n          tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n      auto [tBgB, tBsB] =\n          tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n      uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n      uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n      if constexpr (kEnableRHTColQuant) {\n        if (elect_one_sync()) {\n          cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                              kTmaRhtTensorTransactionBytes);\n          copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n               tBsB(_, 0));\n        }\n      }\n\n      do {\n        // is_first_wave indicates whether this scheduler wave is the first among a group.\n        bool is_first_wave = scheduler.is_first_wave();\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, scheduler.tile_m(), _);\n        int k_tile = 0;\n\n        sched_throttle_pipeline.producer_acquire(sched_pipeline_throttle_producer_state);\n        sched_throttle_pipeline.producer_commit(sched_pipeline_throttle_producer_state);\n        ++sched_pipeline_throttle_producer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n          int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n        // scheduler.advance();\n      } while (scheduler.is_valid());\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      // This warp executes the main tensor core matrix-multiply-accumulate for the Hadamard transform.\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      if constexpr (kEnableRHTColQuant) {\n        // Setup shared memory fragments for A and B tiles.\n        Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                  sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n        Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                  sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n        int block_rank_in_cluster = cute::block_rank_in_cluster();\n        ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n        // Allocate \"fragments\" -- these are actually umma smem descriptors\n        Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n        Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n        mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n        tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                                &shared_storage.tmem_base_ptr);\n        __syncwarp();\n        tmem_allocation_result_barrier.arrive();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_mma.data() = tmem_base_ptr;\n        // Wait until the B (Hadamard) tensor copy is complete\n        cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n        do {\n          uint32_t skip_wait = K_TILE_MAX <= 0;\n\n          auto barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            int read_stage = mainloop_pipe_consumer_state.index();\n            auto tCrA_mk = tCrA(_, _, _, read_stage);\n            auto tCrB_nk = tCrB(_, _, 0, 0);\n            CUTLASS_PRAGMA_UNROLL\n            for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block) {\n              int accumulator_k_block =\n                  accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n              int tCrA_k_block = k_block * EpilogueUnrollFactor;\n              accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n              CUTLASS_PRAGMA_UNROLL\n              for (int i = 0; i < EpilogueUnrollFactor; i++) {\n                auto accumulators = bulk_tmem_mma(_, _, _, accumulator_k_block + i);\n                gemm(mma, tCrA_mk(_, _, tCrA_k_block + i), tCrB_nk, accumulators);\n              }\n\n              accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n              ++accumulator_pipe_producer_state;\n            }\n            auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n            skip_wait = k_tile >= K_TILE_MAX;\n            mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n            barrier_token =\n                mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n        tmem_allocator.release_allocation_lock();\n        accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n        tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n      }\n    } else if (is_sched_warp) {\n      // Scheduler warp manages tile assignment and pipeline progress for warps\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      do {\n        sched_throttle_pipeline.consumer_wait(sched_pipeline_throttle_consumer_state);\n        sched_throttle_pipeline.consumer_release(sched_pipeline_throttle_consumer_state);\n        ++sched_pipeline_throttle_consumer_state;\n        sched_pipeline_producer_state =\n            scheduler.advance_to_next_work(sched_pipeline, sched_pipeline_producer_state);\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    } else if (is_epilogue_col_quant_warp) {\n      // Warp responsible for quantizing output of Hadamard transform to FP4 for columnwise usage,\n      // and writing result tensors/scales to global memory.\n      cutlass::arch::warpgroup_reg_alloc<192>();\n      if constexpr (kEnableRHTColQuant) {\n        using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n        auto acc_epilogue_pipelined_shape =\n            append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n        auto bulk_tmem_epilogue_layout = make_layout(\n            acc_epilogue_pipelined_shape,\n            make_stride(stride<0>(bulk_tmem_mma), Int<0>{}, Int<0>{}, size<1>(epilogue_tiler)));\n        auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n        // Use 256-bit fragments for aligned bulk stores\n        static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n        // Wait for TMEM allocation for this pipeline to finish\n        tmem_allocation_result_barrier.arrive_and_wait();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_epilogue.data() = tmem_base_ptr;\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n        // g2s load all global_d_amax\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < num_tensors; g += NumEpilogueColQuantThreadCount) {\n          shared_storage.global_d_amax[g] = __ldg(reinterpret_cast<float *>(amax_colwise + g));\n        }\n\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // Setup RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // TODO(zhongbo): double check the logic here\n        int group_idx = get_current_tensor_id(\n            shape_rep, num_tensors, (scheduler.tile_n_base() * size<1>(epilogue_tiler)) * M,\n            packed_N, M, offsets);\n\n        // Determine quantization scale factor layouts/output splits for this group\n        TSFDLayout sfd_layout;\n        int cur_N = static_cast<int>(first_dims[group_idx]);\n        if constexpr (kEnableSwizzleSFOutput) {\n          sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n        } else {\n          sfd_layout = make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                   make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n        }\n        // Build output tensors for columns and their quant scales\n        // TODO(zhongbo): double check the logic here\n        Tensor mD = make_tensor(cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(\n                                    reinterpret_cast<char *>(QA_COLWISE) + offsets[group_idx] / 2)),\n                                make_shape(M, cur_N), DStride{});  // (M,packed_N)\n        Tensor gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                  Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        // for every tensor [x, y] row major, x y both a multiple of 128\n        // both of its rowwise and colwise scaling factors will have exactly x * y / 16 elements in FP8 E4M3\n        Tensor mSFD = make_tensor(\n            make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(reinterpret_cast<char *>(SFA_COLWISE) +\n                                                         offsets[group_idx] / kNVFP4BlockSize)),\n            sfd_layout);\n        Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n\n        // Setup tile-level TMEM (t2r) and global memory (r2g) copy descriptors\n        auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n        auto tiled_r2g =\n            make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{}, tiled_t2r);\n        auto thr_t2r = tiled_t2r.get_slice(local_thread_idx);\n        auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n        cutlass::arch::NamedBarrier::sync(NumEpilogueColQuantThreadCount,\n                                          cutlass::arch::ReservedNamedBarriers::EpilogueBarrier);\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float c_global_amax_val = shared_storage.global_d_amax[group_idx];\n        float global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n        float global_decode_scale = 1.0f / global_encode_scale;\n\n        // Scaling factor for fast math path\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n        do {\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();\n               ++k_tile) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            // TODO(zhongbo): double check the logic here\n            int cur_group_idx = get_current_tensor_id(\n                shape_rep, num_tensors, global_tile_n_offset * M, packed_N, M, offsets);\n\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              c_global_amax_val = shared_storage.global_d_amax[group_idx];\n              // update amax\n              global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n              // TODO(zhongbo): double check the logic here\n              cur_N = first_dims[group_idx];\n              if constexpr (kEnableSwizzleSFOutput) {\n                sfd_layout =\n                    tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n              } else {\n                sfd_layout =\n                    make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n              }\n              // update tensor\n              mD = make_tensor(cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(\n                                   reinterpret_cast<char *>(QA_COLWISE) + offsets[group_idx] / 2)),\n                               make_shape(M, cur_N), DStride{});\n              gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                 Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n              mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                     reinterpret_cast<char *>(SFA_COLWISE) +\n                                     offsets[group_idx] / kNVFP4BlockSize)),\n                                 sfd_layout);\n              gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                   Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n              gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n            }\n            int group_start_offset = offsets[group_idx] / M;\n            int local_tile_n_idx =\n                (global_tile_n_offset - group_start_offset) / size<1>(epilogue_tiler);\n            Tensor tDgD_mn = gD_mn_view(_, _, _, scheduler.tile_m(), local_tile_n_idx);\n\n            Tensor tDgSFD_mn = gSFD_mn(_, _, scheduler.tile_m(), local_tile_n_idx);\n            accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n            auto Acc = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n            Tensor tDtAcc = thr_t2r.partition_S(Acc);    // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDgD = thr_t2r.partition_D(tDgD_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n            Tensor tTR_rAcc = make_tensor<ElementAccumulator>(\n                shape(tDgD));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDrD = make_tensor<TD>(shape(tDgD));\n            Tensor tTR_rAcc_frag =\n                recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n            Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n            Tensor src = thr_r2g.retile_S(tDrD);\n            Tensor dst = thr_r2g.retile_D(tDgD);\n\n            Tensor tDgSFD_view = make_tensor(\n                tDgSFD_mn.data(), make_layout(make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                                              make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n            Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n            Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n\n            static int constexpr NumVecs = size(tDgD) / VectorSize;\n            Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n            // Compute amax and quantization scales for this tile\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n            cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n            // Copy from TMEM to registers\n            copy(tiled_t2r, tDtAcc, tTR_rAcc);\n            cutlass::arch::fence_view_async_tmem_load();\n            accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n            ++accumulator_pipe_consumer_state;\n\n            if constexpr (!kUseFastMath) {\n              // Downcast to BF16 for bit-wise compatibility with\n              // unfused kernels\n              auto convert_accum_to_bf16 =\n                  cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                                 FragmentSize>{};\n              auto convert_bf16_to_accum =\n                  cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                                 FragmentSize>{};\n              tTR_rAcc_frag(_0{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n              tTR_rAcc_frag(_1{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n            }\n\n            auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n                tTR_rAcc_frag.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TD, VectorSize> *>(tDrD_frag.data());\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < NumVecs; v++) {\n              vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n            }\n\n            pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                vec_maxs, global_encode_scale_multiplier);\n            auto pvscales_cvted =\n                cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n            tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n            auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(\n                tD_rRowSFD_frg(_0{}));\n            auto qpvscale_scaled =\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                    qpvscale_ups, global_decode_scale);\n            cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n            if constexpr (kUseFastMath) {\n              // Fast math: compute approximate reciprocal\n              acc_scales =\n                  cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // Accurate math: compute reciprocal with division\n              acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                  1.0, qpvscale_scaled);\n            }\n\n            // Prepare stochastic rounding random state if enabled\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            // \"Prefetch\" a stochastic rounding state for the first tile\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            // Apply round/quantize to each fragment, with or without stochastic rounding\n            for (int v = 0; v < NumVecs; v++) {\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs[v], acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs[v], acc_scale));\n              }\n            }\n\n            // Write quantized FP4 tile and dequant scale to gmem\n            copy(tiled_r2g, src, dst);\n            copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFD, tDgSFD);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n    } else if (is_epilogue_row_quant_warp) {\n      // Warp responsible for quantizing the input (before Hadamard transform) to FP4 for row-wise usage.\n      cutlass::arch::warpgroup_reg_alloc<136>();\n      if constexpr (kEnableRowQuant) {\n        using S2RVectorType = uint128_t;\n\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % 256;\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // g2s load all global_a_amax for all groups/tensors\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < num_tensors; g += NumEpilogueRowQuantThreadCount) {\n          shared_storage.global_a_amax[g] = __ldg(reinterpret_cast<float *>(amax_rowwise + g));\n        }\n        // RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // Input/output tensors/partitions for row quant warp\n        Tensor mQA =\n            make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, packed_N), dQA));\n        Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n        Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n\n        Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, X, _1>{});  // (BLK_M,BLK_N)\n        // Swizzled shared memory A tile, with layout\n        Tensor sA = as_position_independent_swizzle_tensor(group_modes<0, 2>(\n            coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                 sAlayout))));  // (BLOCK_M, BLOCK_M,PIPE)\n\n        // Set up layouts for partitioning \u2013 tile-by-warp, with vector granularity\n        using S2RWarpLayout = Layout<Shape<_8, _4>>;\n        using WarpGroupLayout = Layout<Shape<_1, _8>>;\n        using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n        using S2RValLayout = Layout<Shape<Int<VectorSize>, _1>>;\n        using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n        using R2GAtomQA = Copy_Atom<AutoVectorizingCopy, TQA>;\n        using R2GAtomSFA = Copy_Atom<AutoVectorizingCopy, TSFA>;\n        auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_QA = make_tiled_copy(R2GAtomQA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_SFA = make_tiled_copy(R2GAtomSFA{}, S2RThreadLayout{}, S2RValLayout{});\n\n        auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n        auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n        auto thr_r2g_SFA = tiled_r2g_SFA.get_slice(local_thread_idx);\n        Tensor tQAsA = thr_s2r.partition_S(sA);  // (Copy, Copy_M, Copy_N, PIPE)\n\n        // Allocate temporary register tensors for copying quantization => output\n        Tensor tQArA = make_tensor_like<TA>(\n            make_layout(tQAsA(_, _, _, _0{}).shape()));  // (Copy, Copy_M, Copy_N)\n        Tensor tQAgQA = thr_r2g_QA.partition_S(gQA_mn);\n        Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n\n        Tensor tQAgSFA = thr_r2g_SFA.partition_S(gSFA_mn);\n        Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n\n        // Will result in barrier_id=10 passed to bar.sync instr as cutlass adds 8\n        // in order to go over the reserved named barrier count.\n        constexpr int row_quant_barrier_id = 2;\n        cutlass::arch::NamedBarrier::sync(NumEpilogueRowQuantThreadCount, row_quant_barrier_id);\n\n        int group_idx = get_current_tensor_id(\n            shape_rep, num_tensors, (scheduler.tile_n_base() * size<1>(epilogue_tiler)) * M,\n            packed_N, M, offsets);\n        float a_global_amax_val = shared_storage.global_a_amax[group_idx];\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n\n        float global_decode_scale = 1.0f / global_encode_scale;\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n        auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n        do {\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = get_current_tensor_id(\n                shape_rep, num_tensors, global_tile_n_offset * M, packed_N, M, offsets);\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              a_global_amax_val = shared_storage.global_a_amax[group_idx];\n              // Update group quantization parameters/scaling\n              global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            }\n\n            auto tQAgSFA_mn =\n                tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state);\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n            cutlass::arch::fence_view_async_shared();\n            mainloop_pipeline.consumer_release(mainloop_pipe_consumer_state);\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n\n            // static int constexpr NumVecs = size(tQArA) / VectorSize;\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(\n                raw_pointer_cast(tQArQA.data()));\n            Tensor amax =\n                make_tensor<ElementAccumulator>(prepend(take<1, rank(tQArA)>(tQArA.shape()), _1{}));\n            Tensor pvscales = make_tensor_like<ElementAccumulator>(amax);\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512 +\n                                          tiles_in_m * tiles_in_n * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < size<1>(group_modes<1, rank(tQArA)>(tQArA)); v++) {\n              auto amax_view = group_modes<1, rank(amax)>(amax);\n              auto pvscales_view = group_modes<1, rank(pvscales)>(pvscales);\n              auto compute_frgs_up =\n                  cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(\n                      compute_frgs[v]);\n              amax_view(_0{}, v) = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n              pvscales_view(_0{}, v) = cutlass::multiplies<ElementAccumulator>{}(\n                  amax_view(_0{}, v), global_encode_scale_multiplier);\n              filter(tQArSFA)(v) = sfa_converter(pvscales_view(_0{}, v));\n              auto qpvscale_ups =\n                  cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n              auto qpvscale_scaled =\n                  cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n              ElementAccumulator acc_scales;\n              if constexpr (kUseFastMath) {\n                // Fast math: compute approximate reciprocal\n                acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(\n                    qpvscale_scaled);\n              } else {\n                // Accurate math: compute reciprocal with division\n                acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n              }\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales, cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              uint4 random_uint4 = uint4{0, 0, 0, 0};\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs_up, acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs_up, acc_scale));\n              }\n            }\n            copy(tiled_r2g_QA, tQArQA, tQAgQA_mn);\n            copy(tiled_r2g_SFA, filter(tQArSFA), filter(tQAgSFA_mn));\n          }\n          // scheduler.advance();\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n\n    } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n    }\n  }\n}  // NOLINT(readability/fn_size)\n\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant,\n          bool kEnableSwizzleSFOutput, class TA, class TB, class TQA, class TSFA, class TD = TQA,\n          class TSFD = TSFA, bool kUseFastMath = false>\nvoid group_row_col_rht_gemm_ntt_w_sfc_graph_safe(\n    int packed_sequence_length, int hidden_size, size_t num_tensors, ShapeRepresentation shape_rep,\n    TA const *A, TB const *B, TQA *QA, TSFA *SFA, TQA *QA_COLWISE, TSFA *SFA_COLWISE,\n    float *amax_rowwise, float *amax_colwise, const int64_t *offsets, const int64_t *first_dims,\n    const size_t *rng_state, uint32_t *tile_scheduler_workspace, uint32_t sm_count,\n    hipStream_t stream, int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0),\n                                               make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFDLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)),\n                                               make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(\n      SwizzledSFALayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(\n      SwizzledSFDLayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFDLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFDLayout>;\n  SFALayout sfa_layout;\n  SFDLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{});\n  } else {\n    sfa_layout = make_layout(\n        make_shape(make_shape(Int<SFVecSize>{}, hidden_size / SFVecSize), packed_sequence_length),\n        make_stride(make_stride(_0{}, _1{}), hidden_size / SFVecSize));\n    sfd_layout = make_layout(\n        make_shape(hidden_size, make_shape(Int<SFVecSize>{}, packed_sequence_length / SFVecSize)),\n        make_stride(packed_sequence_length / SFVecSize, make_stride(_0{}, _1{})));\n  }\n\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = packed_sequence_length;\n  Tensor tensorA = make_tensor(A, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);    // (dM,dK)\n  auto dB = stride(tensorB);    // (dN,dK)\n  auto dD = LayoutRight{};      // (dM,dN)\n  auto dQA = stride(tensorQA);  // (dM,dK)\n  using ClusterShape = Shape<_1, _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128, Int<RhtTensorSize>, Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128, Int<RhtTensorSize>, _128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n      size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                           UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cluster_tile_shape),\n      shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cluster_tile_shape),\n      shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem / (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 4;\n  static int constexpr MainloopPipelineBytes = sizeof(\n      typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<1, Shape<_1, _1, _1>,\n                                                               Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr SchedulerWorkspaceBytes = sizeof(int) * SchedulerPipelineStageCount;\n  static int constexpr SchedulerThrottlePipelineBytes =\n      sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr SchedulerPipelineBytes =\n      sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount,\n                                                     ClusterShape>::SharedStorage);\n\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(\n      typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                          Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448;  // 232KB in bytes\n  static int constexpr kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = SchedulerWorkspaceBytes + SchedulerThrottlePipelineBytes +\n                                        SchedulerPipelineBytes + TmemBasePtrsBytes +\n                                        TmemDeallocBytes + BTensorBytes +\n                                        AccPipelineBytes;  // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP),\n                                    Step<_2, _1, _3>{});  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, _1{}));  // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};                                        // XXX Dummy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cluster_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  assert(M % size<0>(cluster_tile_shape) == 0);\n  assert(N % size<1>(cluster_tile_shape) == 0);\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(sm_count, 1, 1);\n\n  int smem_size = sizeof(\n      SharedStorage<TA, TB, decltype(sA), decltype(sB), ClusterShape, AccumulatorPipelineStageCount,\n                    EpilogueUnrollFactor, SchedulerPipelineStageCount>);\n\n  auto *kernel_ptr = &group_row_col_rht_gemm_device_graph_safe<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cluster_shape),\n      decltype(cluster_tile_shape), TA, decltype(dA), decltype(sA), decltype(tma_load_a), TB,\n      decltype(dB), decltype(sB), decltype(tma_load_b), TD, decltype(dD), decltype(sD), TSFD,\n      decltype(sfd_layout), TQA, decltype(dQA), TSFA, decltype(sfa_layout), decltype(mma),\n      AccumulatorPipelineStageCount, SchedulerPipelineStageCount, kEnableStochasticRounding,\n      kEnableRHTColQuant, kEnableRowQuant, kEnableSwizzleSFOutput, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      hipFuncSetAttribute(reinterpret_cast<const void*>(*kernel_ptr), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  // Set workspace and set to zero\n  NVTE_CHECK_CUDA(hipMemsetAsync(reinterpret_cast<void *>(tile_scheduler_workspace), 0,\n                                  sizeof(uint32_t), stream));\n\n  // Launch kernel\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n      params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape, A, dA,\n      sA, tma_load_a, B, dB, sB, tma_load_b, QA, dQA, SFA, sfa_layout, QA_COLWISE, SFA_COLWISE,\n      amax_rowwise, amax_colwise, offsets, first_dims, num_tensors, shape_rep,\n      tile_scheduler_workspace, mma, rng_state);\n  NVTE_CHECK_CUDA(hipGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion_graph_safe(const GroupedTensor *input,\n                                                     GroupedTensor *output,\n                                                     const Tensor &hadamard_matrix_,\n                                                     QuantizationConfig &quant_config,\n                                                     Tensor &quant_workspace, hipStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion_graph_safe);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::ShapeRepresentation;\n\n  void *input_base_ptr = reinterpret_cast<void *>(input->data.dptr);\n  // TODO(zhongbo): add input sanity checks here\n\n  bool all_has_row_quant = output->has_data();\n  bool all_has_col_quant = output->has_columnwise_data();\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (use_stochastic_rounding) {\n    NVTE_CHECK(quant_config.rng_state != nullptr,\n               \"Enabled stochastic rounding without providing RNG state\");\n    const Tensor &rng_state_tensor = *convertNVTETensorCheck(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  uint32_t *tile_scheduler_workspace = nullptr;\n  NVTE_CHECK(quant_workspace.data.dptr != nullptr, \"Quantization workspace must be provided.\");\n  NVTE_CHECK(quant_workspace.data.buffer_size_bytes() >= sizeof(uint32_t),\n             \"Quantization workspace must be at least 4 bytes.\");\n  tile_scheduler_workspace = reinterpret_cast<uint32_t *>(quant_workspace.data.dptr);\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t num_tensors = input->num_tensors;\n  const size_t first_logical_dim = input->logical_shape.data[0];\n  const size_t last_logical_dim = input->logical_shape.data[1];\n  // const size_t elts_total = first_logical_dim * last_logical_dim;\n  NVTE_CHECK(first_logical_dim % 128 == 0,\n             \"First dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(last_logical_dim % 128 == 0,\n             \"Last dimension of a grouped tensor should be divisible by 128.\");\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  ShapeRepresentation shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  if (output->all_same_shape()) {\n    shape_rep = ShapeRepresentation::SAME_BOTH_DIMS;\n  } else if (output->all_same_first_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_LAST_DIM;\n  } else if (output->all_same_last_dim()) {\n    shape_rep = ShapeRepresentation::VARYING_FIRST_DIM;\n  } else if (output->varying_both_dims()) {\n    shape_rep = ShapeRepresentation::VARYING_BOTH_DIMS;\n  }\n\n  TQA *const rowwise_data_base_ptr = reinterpret_cast<TQA *>(output->data.dptr);\n  TSFA *const rowwise_scale_inv_base_ptr = reinterpret_cast<TSFA *>(output->scale_inv.dptr);\n  TQA *const colwise_data_base_ptr = reinterpret_cast<TQA *>(output->columnwise_data.dptr);\n  TSFA *const colwise_scale_inv_base_ptr =\n      reinterpret_cast<TSFA *>(output->columnwise_scale_inv.dptr);\n  float *const amax_rowwise_base_ptr = reinterpret_cast<float *>(output->amax.dptr);\n  float *const amax_colwise_base_ptr = reinterpret_cast<float *>(output->columnwise_amax.dptr);\n\n  const int64_t *const offsets_ptr = reinterpret_cast<const int64_t *>(output->tensor_offsets.dptr);\n  const int64_t *const first_dims_ptr = reinterpret_cast<const int64_t *>(output->first_dims.dptr);\n\n  const bool is_const_last_dim = (shape_rep == ShapeRepresentation::SAME_BOTH_DIMS ||\n                                  shape_rep == ShapeRepresentation::VARYING_FIRST_DIM);\n  NVTE_CHECK(is_const_last_dim,\n             \"Currently we only support const last dimension for graph safe hadamard transform.\");\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  int k_tile_size = 1024;\n\n  const bool use_swizzle_sf_output = output->with_gemm_swizzled_scales;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          all_has_col_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_has_row_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::group_row_col_rht_gemm_ntt_w_sfc_graph_safe<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TQA, TSFA, TD, TSFD, kUseFastMath>(\n                            /*packed_sequence_length=*/first_logical_dim,\n                            /*hidden_size=*/last_logical_dim,\n                            /*num_tensors=*/num_tensors,\n                            /*shape_rep=*/shape_rep,\n                            /*A=*/reinterpret_cast<TA const *>(input_base_ptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_base_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_base_ptr),\n                            /*QA_COLWISE=*/reinterpret_cast<TQA *>(colwise_data_base_ptr),\n                            /*SFA_COLWISE=*/reinterpret_cast<TSFA *>(colwise_scale_inv_base_ptr),\n                            /*amax_rowwise=*/reinterpret_cast<float *>(amax_rowwise_base_ptr),\n                            /*amax_colwise=*/reinterpret_cast<float *>(amax_colwise_base_ptr),\n                            /*offsets=*/offsets_ptr,\n                            /*first_dims=*/first_dims_ptr,\n                            /*rng_state=*/rng_state,\n                            /*tile_scheduler_workspace=*/tile_scheduler_workspace,\n                            /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion_graph_safe(\n    const NVTEGroupedTensor input, NVTEGroupedTensor output, const NVTETensor hadamard_matrix,\n    const NVTEQuantizationConfig quant_config, NVTETensor quant_workspace, hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_cast_fusion_graph_safe);\n  using namespace transformer_engine;\n\n  GroupedTensor *input_tensor = convertNVTEGroupedTensorCheck(input);\n  GroupedTensor *output_tensor = convertNVTEGroupedTensorCheck(output);\n\n  Tensor *quant_workspace_tensor = convertNVTETensorCheck(quant_workspace);\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  if (input_tensor->num_tensors == 0) {\n    return;\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion_graph_safe(\n      input_tensor, output_tensor, *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n      *quant_workspace_tensor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/graph_safe_group_row_cast_col_hadamard_transform_cast_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\n// Ensure Tensor refers to cute::Tensor, not transformer_engine::Tensor\nusing cute::Tensor;\n\nconstexpr int kMaxTensorsPerKernel = 64;\n\nstruct MultiAmaxHadamardCastFusionArgs {\n  // (output) Amax buffer for input A amax buffer\n  void *global_a_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for pre-RHT amax buffer\n  void *global_d_amax_list[kMaxTensorsPerKernel];\n  // output D pointers for each tensor\n  void *output_colwise_list[kMaxTensorsPerKernel];\n  // output SFD inverse pointers for each tensor\n  void *output_colwise_scale_inv_list[kMaxTensorsPerKernel];\n  // split sections of each tensor of input\n  int split_sections[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\n__device__ __forceinline__ int GetGroupIdx(MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                           int offset) {\n  // Check the kernel args and get the corresponding id\n  const int num_tensors = kernel_args_ptr->num_tensors;\n  if (offset >= kernel_args_ptr->split_sections_range[num_tensors]) {\n    return num_tensors - 1;\n  }\n  int group_idx = 0;\n  while (kernel_args_ptr->split_sections_range[group_idx + 1] <= offset) {\n    ++group_idx;\n  }\n  return group_idx;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout, class ClusterShape,\n          int AccumulatorPipelineStageCount_, int EpilogueUnrollFactor_,\n          int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                 AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>,\n                                                      AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using SchedPipelineStorage = typename SchedPipeline::SharedStorage;\n  using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using SchedThrottlePipelineStorage = typename SchedThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) SchedPipelineStorage sched;\n  alignas(16) SchedThrottlePipelineStorage sched_throttle;\n  alignas(16) int32_t atomic_tile_id[SchedulerPipelineStageCount_];\n  alignas(16) float global_a_amax[kMaxTensorsPerKernel];\n  alignas(16) float global_d_amax[kMaxTensorsPerKernel];\n  uint32_t atomic_tile_counter[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\n// Main RHT GEMM kernel entry -- highly templated for flexible architecture/config support\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA, class TB, class BStride,\n          class BSmemLayout, class TmaLoadB, class TD, class DStride, class DSmemLayout, class TSFD,\n          class TSFDLayout, class TQA, class QAStride, class TSFA, class TSFALayout, class TiledMMA,\n          int AccumulatorPipelineStageCount_, int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false, bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true, bool kEnableSwizzleSFOutput_ = false,\n          bool kUseFastMath_ = false>\n__launch_bounds__(512, 1) __global__ static void group_row_col_rht_gemm_device(\n    MShape M, NShape packed_N, KShape K, ClusterShape cluster_shape, ClusterTileShape cluster_tile,\n    TA const *A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const *B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TQA *QA, QAStride dQA, TSFA *SFA, TSFALayout sfa_layout, MultiAmaxHadamardCastFusionArgs args,\n    uint32_t *tile_scheduler_workspace, TiledMMA mma, const size_t *rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                  \"group_row_col_rht_gemm_device must generate row-wise \"\n                  \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n    CUTLASS_NOT_IMPLEMENTED();\n    return;\n#endif\n\n    using X = Underscore;\n    // Accumulator data type for main computation\n    using ElementAccumulator = float;\n    static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n    static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n    static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n    static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n    static constexpr bool kEnableSwizzleSFOutput = kEnableSwizzleSFOutput_;\n    static constexpr bool kUseFastMath = kUseFastMath_;\n\n    // Constant for RHT tensor processing (tile size etc)\n    static int constexpr RhtTensorSize = 16;\n\n    // Transaction bytes for TMA transfer on RHT tensor blocks\n    static int constexpr kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n    static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n    static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n    // Mainloop pipeline stage calculation, vectorization parameters for scaling factors\n    static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    static int constexpr SFVecSize = 16;\n    // Swizzle output layout for scaling factor arrays\n    using SwizzledSFALayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n    using SwizzledSFDLayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n\n    // Mainloop pipeline types for TMA async execution and epilogue cluster scheduling\n    using MainloopPipeline =\n        cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, ClusterShape,\n                                                        AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n    using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n    using SchedPipelineState = typename SchedPipeline::PipelineState;\n    using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n    using SchedThrottlePipelineState = typename SchedThrottlePipeline::PipelineState;\n\n    static_assert(ClusterShape{} == Shape<_1, _1, _1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static int constexpr VectorSize = RhtTensorSize;\n\n    // Compile-time safety: static shapes required for shared memory layouts\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    //   CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n\n    auto cluster_size = size<0>(cluster_shape);\n    auto mainloop_tiler = Shape<_128, _16, _128>{};\n    auto epilogue_tiler = Shape<_128, _128, _128>{};\n\n    static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    int const K_TILE_MAX = min(packed_N, K) / size<2>(epilogue_tiler);\n\n    struct TileScheduler {\n      uint32_t tiles_in_m = 0;\n      uint32_t tiles_in_n = 0;\n      uint32_t linear_idx = 0;\n      uint32_t next_linear_idx = 0;\n      uint32_t start_idx = 0;\n      uint32_t tile_m_idx = 0;\n      uint32_t tile_n_idx = 0;\n      int k_tile_max = 0;\n      uint32_t *atomic_tile_index_;\n      uint32_t *smem_tile_counter;\n      uint32_t atomic_offset;\n      cutlass::FastDivmodU64 divmod_tiles_in_m;\n\n      CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax,\n                                   uint32_t *atomic_tile_index, uint32_t *smem_tile_counter)\n          : tiles_in_m(tiles_m),\n            tiles_in_n(tiles_n),\n            linear_idx(blockIdx.x),\n            next_linear_idx(blockIdx.x),\n            start_idx(blockIdx.x),\n            k_tile_max(kmax),\n            atomic_tile_index_(atomic_tile_index),\n            smem_tile_counter(smem_tile_counter),\n            atomic_offset(gridDim.x),\n            divmod_tiles_in_m(uint64_t(tiles_m)) {\n        update_tile_idx();\n      }\n      CUTLASS_DEVICE void update_tile_idx() {\n        uint64_t q, r;\n        divmod_tiles_in_m(q, r, uint64_t(linear_idx));\n        tile_m_idx = static_cast<uint32_t>(r);\n        tile_n_idx = static_cast<uint32_t>(q) * uint32_t(k_tile_max);\n      }\n      CUTLASS_DEVICE uint32_t tile_m() const { return tile_m_idx; }\n      CUTLASS_DEVICE uint32_t tile_n_base() const { return tile_n_idx; }\n      CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n\n      CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n\n      CUTLASS_DEVICE bool is_valid() const {\n        return cute::elem_less(cute::make_coord(tile_m(), tile_n_base()),\n                               cute::make_coord(tiles_in_m, tiles_in_n));\n      }\n\n      CUTLASS_DEVICE bool is_first_wave() const { return linear_idx == start_idx; }\n\n      CUTLASS_DEVICE uint32_t get_linear_tile_idx() const { return linear_idx; }\n\n      // Fetch a new tile_id using atomics.\n      CUTLASS_DEVICE uint32_t fetch_tile_id_counter(int pred) {\n        uint32_t tile_id_counter = 0;\n        asm volatile(\n            \"{\\n\\t\"\n            \".reg .pred p;\\n\\t\"\n            \"setp.eq.u32 p, %2, 1;\\n\\t\"\n            \"@p atom.global.add.u32 %0, [%1], 1; \\n\\t\"\n            \"}\"\n            : \"=r\"(tile_id_counter)\n            : \"l\"(atomic_tile_index_), \"r\"(pred));\n\n        return tile_id_counter;\n      }\n\n      CUTLASS_DEVICE auto fetch_next_work(SchedPipeline &sched_pipeline,\n                                          SchedPipelineState sched_pipeline_consumer_state) {\n        sched_pipeline.consumer_wait(sched_pipeline_consumer_state);\n        next_linear_idx = smem_tile_counter[sched_pipeline_consumer_state.index()];\n        cutlass::arch::fence_view_async_shared();\n        sched_pipeline.consumer_release(sched_pipeline_consumer_state);\n        return;\n      }\n\n      CUTLASS_DEVICE auto advance_to_next_work(SchedPipeline &sched_pipeline,\n                                               SchedPipelineState sched_pipeline_producer_state) {\n        uint32_t mbarrier_addr = sched_pipeline.producer_get_barrier(sched_pipeline_producer_state);\n        // Wait for clcID buffer to become empty with a flipped phase\n        sched_pipeline.producer_acquire(sched_pipeline_producer_state);\n        auto is_leading_thread = cute::elect_one_sync();\n        uint32_t tile_id_counter = fetch_tile_id_counter(is_leading_thread) + atomic_offset;\n        uint32_t smem_addr =\n            cute::cast_smem_ptr_to_uint(&smem_tile_counter[sched_pipeline_producer_state.index()]);\n        if (is_leading_thread) {\n          cute::store_shared_remote(tile_id_counter, smem_addr, mbarrier_addr, 0);\n        }\n\n        ++sched_pipeline_producer_state;\n        return sched_pipeline_producer_state;\n      }\n\n      CUTLASS_DEVICE auto update_work_tile_info() {\n        linear_idx = next_linear_idx;\n        update_tile_idx();\n        return;\n      }\n    };\n\n    // Allocate and alias shared memory to the kernel's shared storage type\n    extern __shared__ char shared_memory[];\n    using SharedStorage =\n        SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount,\n                      EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n\n    // Compute the number of tiles in M and N after tiling and assign scheduler\n    uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n    uint32_t tiles_in_n = uint32_t(\n        size(ceil_div(args.split_sections_range[args.num_tensors], size<2>(epilogue_tiler))));\n\n    TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, tile_scheduler_workspace,\n                            shared_storage.atomic_tile_counter);\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n\n    // Shapes for accumulated tiles in mainloop and epilogue\n    auto acc_shape_mma = make_shape(take<0, 2>(mainloop_tiler), _1{}, _1{});\n    auto acc_shape_epilogue = make_shape(take<0, 2>(epilogue_tiler), _1{}, _1{});\n\n    // Shape of the accumulator fragment for the main loop pipeline, with pipeline stages appended\n    auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n    auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n    // Number of threads assigned for various epilogue roles depending on quantization settings\n    static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n    static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n    static int constexpr NumMmaThreadCount = kEnableRHTColQuant ? 32 : 0;\n    static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant ? 1 : 0;\n    static int constexpr NumSchedThreads = 32;\n    static int constexpr NumMainloopLoadThreads = 32;\n    static int constexpr NumEpilogueThreads =\n        NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n        cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    // warp assignment\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_sched_warp = (warp_idx == 2);\n    bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n    bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    mainloop_pipeline_params.num_consumers =\n        NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                   AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n    using AccumulatorPipelineInitBarriers = cute::bool_constant<kEnableRHTColQuant>;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_col_quant_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count =\n        size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(\n        shared_storage.accumulator, accumulator_pipeline_params, cluster_shape,\n        AccumulatorPipelineInitBarriers{}, cute::true_type{});  // Delay mask calculation\n    typename SchedPipeline::Params sched_pipeline_params;\n    if (is_sched_warp) {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::ProducerConsumer;\n    } else {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::Consumer;\n    }\n    sched_pipeline_params.producer_blockid = 0;\n    sched_pipeline_params.producer_arv_count = 1;\n    sched_pipeline_params.consumer_arv_count =\n        NumSchedThreads +\n        cluster_size * (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n    sched_pipeline_params.transaction_bytes = sizeof(uint32_t);\n    sched_pipeline_params.initializing_warp = 3;\n    SchedPipeline sched_pipeline(shared_storage.sched, sched_pipeline_params, cluster_shape);\n    SchedPipelineState sched_pipeline_consumer_state;\n    SchedPipelineState sched_pipeline_producer_state =\n        cutlass::make_producer_start_state<SchedPipeline>();\n\n    typename SchedThrottlePipeline::Params sched_throttle_pipeline_params;\n    if (is_dma_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Producer;\n    }\n    if (is_sched_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Consumer;\n    }\n    sched_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n    sched_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n    sched_throttle_pipeline_params.dst_blockid = 0;\n    sched_throttle_pipeline_params.initializing_warp = 4;\n\n    SchedThrottlePipeline sched_throttle_pipeline(shared_storage.sched_throttle,\n                                                  sched_throttle_pipeline_params);\n    SchedThrottlePipelineState sched_pipeline_throttle_consumer_state;\n    SchedThrottlePipelineState sched_pipeline_throttle_producer_state =\n        cutlass::make_producer_start_state<SchedThrottlePipeline>();\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n\n    // Warp group roles: DMA (global->shared copy), MMA (tensor core gemm), scheduler, column quantizer, row quantizer\n    if (is_dma_warp) {\n      // Warp responsible for loading input from global to shared memory using TMA (Tensor Memory Access).\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      // Get TMA tensors for input matrix A and B (Hadamard/transform matrix) from global memory.\n      Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, packed_N));\n      Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n      // Partition tensors for tiling according to the mainloop and cluster tilers.\n      Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n      Tensor gB_nk =\n          local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n\n      // Shared memory tensors for pipeline\n      Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n      Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n      // Determine warp/tile positioning\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n      // Partition global to local fragments for A and B\n      Tensor tCgA = thr_mma.partition_A(gA_mk);  // (MMA,MMA_M,MMA_K,k)\n      Tensor tCgB = thr_mma.partition_B(gB_nk);  // (MMA,MMA_N,MMA_K,k)\n\n      Layout cta_layout_mnk = make_layout(cluster_shape);\n      Layout cta_layout_vmnk =\n          tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n      auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n      auto [tAgA, tAsA] =\n          tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n      auto [tBgB, tBsB] =\n          tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n      uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n      uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n      if constexpr (kEnableRHTColQuant) {\n        if (elect_one_sync()) {\n          cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                              kTmaRhtTensorTransactionBytes);\n          copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n               tBsB(_, 0));\n        }\n      }\n\n      do {\n        // is_first_wave indicates whether this scheduler wave is the first among a group.\n        bool is_first_wave = scheduler.is_first_wave();\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, scheduler.tile_m(), _);\n        int k_tile = 0;\n\n        sched_throttle_pipeline.producer_acquire(sched_pipeline_throttle_producer_state);\n        sched_throttle_pipeline.producer_commit(sched_pipeline_throttle_producer_state);\n        ++sched_pipeline_throttle_producer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n          int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n        // scheduler.advance();\n      } while (scheduler.is_valid());\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      // This warp executes the main tensor core matrix-multiply-accumulate for the Hadamard transform.\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      if constexpr (kEnableRHTColQuant) {\n        // Setup shared memory fragments for A and B tiles.\n        Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                  sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n        Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                  sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n        int block_rank_in_cluster = cute::block_rank_in_cluster();\n        ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n        // Allocate \"fragments\" -- these are actually umma smem descriptors\n        Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n        Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n        mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n        tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                                &shared_storage.tmem_base_ptr);\n        __syncwarp();\n        tmem_allocation_result_barrier.arrive();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_mma.data() = tmem_base_ptr;\n        // Wait until the B (Hadamard) tensor copy is complete\n        cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n        do {\n          uint32_t skip_wait = K_TILE_MAX <= 0;\n\n          auto barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            int read_stage = mainloop_pipe_consumer_state.index();\n            auto tCrA_mk = tCrA(_, _, _, read_stage);\n            auto tCrB_nk = tCrB(_, _, 0, 0);\n            CUTLASS_PRAGMA_UNROLL\n            for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block) {\n              int accumulator_k_block =\n                  accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n              int tCrA_k_block = k_block * EpilogueUnrollFactor;\n              accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n              CUTLASS_PRAGMA_UNROLL\n              for (int i = 0; i < EpilogueUnrollFactor; i++) {\n                auto accumulators = bulk_tmem_mma(_, _, _, accumulator_k_block + i);\n                gemm(mma, tCrA_mk(_, _, tCrA_k_block + i), tCrB_nk, accumulators);\n              }\n\n              accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n              ++accumulator_pipe_producer_state;\n            }\n            auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n            skip_wait = k_tile >= K_TILE_MAX;\n            mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n            barrier_token =\n                mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n        tmem_allocator.release_allocation_lock();\n        accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n        tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n      }\n    } else if (is_sched_warp) {\n      // Scheduler warp manages tile assignment and pipeline progress for warps\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      do {\n        sched_throttle_pipeline.consumer_wait(sched_pipeline_throttle_consumer_state);\n        sched_throttle_pipeline.consumer_release(sched_pipeline_throttle_consumer_state);\n        ++sched_pipeline_throttle_consumer_state;\n        sched_pipeline_producer_state =\n            scheduler.advance_to_next_work(sched_pipeline, sched_pipeline_producer_state);\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    } else if (is_epilogue_col_quant_warp) {\n      // Warp responsible for quantizing output of Hadamard transform to FP4 for columnwise usage,\n      // and writing result tensors/scales to global memory.\n      cutlass::arch::warpgroup_reg_alloc<192>();\n      if constexpr (kEnableRHTColQuant) {\n        using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n        auto acc_epilogue_pipelined_shape =\n            append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n        auto bulk_tmem_epilogue_layout = make_layout(\n            acc_epilogue_pipelined_shape,\n            make_stride(stride<0>(bulk_tmem_mma), Int<0>{}, Int<0>{}, size<1>(epilogue_tiler)));\n        auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n        // Use 256-bit fragments for aligned bulk stores\n        static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n        // Wait for TMEM allocation for this pipeline to finish\n        tmem_allocation_result_barrier.arrive_and_wait();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_epilogue.data() = tmem_base_ptr;\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n        // g2s load all global_d_amax\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < args.num_tensors; g += NumEpilogueColQuantThreadCount) {\n          shared_storage.global_d_amax[g] =\n              __ldg(reinterpret_cast<float *>(args.global_d_amax_list[g]));\n        }\n\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // Setup RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        int group_idx = GetGroupIdx(&args, scheduler.tile_n_base() * size<1>(epilogue_tiler));\n\n        // Determine quantization scale factor layouts/output splits for this group\n        TSFDLayout sfd_layout;\n        int cur_N = args.split_sections[group_idx];\n        if constexpr (kEnableSwizzleSFOutput) {\n          sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n        } else {\n          sfd_layout = make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                   make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n        }\n        // Build output tensors for columns and their quant scales\n        Tensor mD = make_tensor(\n            cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(args.output_colwise_list[group_idx])),\n            make_shape(M, cur_N), DStride{});  // (M,packed_N)\n        Tensor gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                  Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                      args.output_colwise_scale_inv_list[group_idx])),\n                                  sfd_layout);\n        Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n\n        // Setup tile-level TMEM (t2r) and global memory (r2g) copy descriptors\n        auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n        auto tiled_r2g =\n            make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{}, tiled_t2r);\n        auto thr_t2r = tiled_t2r.get_slice(local_thread_idx);\n        auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n        cutlass::arch::NamedBarrier::sync(NumEpilogueColQuantThreadCount,\n                                          cutlass::arch::ReservedNamedBarriers::EpilogueBarrier);\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float c_global_amax_val = shared_storage.global_d_amax[group_idx];\n        float global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n        float global_decode_scale = 1.0f / global_encode_scale;\n\n        // Scaling factor for fast math path\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n        do {\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();\n               ++k_tile) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = GetGroupIdx(&args, global_tile_n_offset);\n\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              c_global_amax_val = shared_storage.global_d_amax[group_idx];\n              // update amax\n              global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n              cur_N = args.split_sections[group_idx];\n              if constexpr (kEnableSwizzleSFOutput) {\n                sfd_layout =\n                    tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n              } else {\n                sfd_layout =\n                    make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n              }\n              // update tensor\n              mD = make_tensor(cute::subbyte_iterator<TD>(\n                                   reinterpret_cast<TD *>(args.output_colwise_list[group_idx])),\n                               make_shape(M, cur_N), DStride{});\n              gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                 Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n              mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                     args.output_colwise_scale_inv_list[group_idx])),\n                                 sfd_layout);\n              gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                   Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n              gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n            }\n            int group_start_offset = args.split_sections_range[group_idx];\n            int local_tile_n_idx =\n                (global_tile_n_offset - group_start_offset) / size<1>(epilogue_tiler);\n            Tensor tDgD_mn = gD_mn_view(_, _, _, scheduler.tile_m(), local_tile_n_idx);\n\n            Tensor tDgSFD_mn = gSFD_mn(_, _, scheduler.tile_m(), local_tile_n_idx);\n            accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n            auto Acc = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n            Tensor tDtAcc = thr_t2r.partition_S(Acc);    // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDgD = thr_t2r.partition_D(tDgD_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n            Tensor tTR_rAcc = make_tensor<ElementAccumulator>(\n                shape(tDgD));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDrD = make_tensor<TD>(shape(tDgD));\n            Tensor tTR_rAcc_frag =\n                recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n            Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n            Tensor src = thr_r2g.retile_S(tDrD);\n            Tensor dst = thr_r2g.retile_D(tDgD);\n\n            Tensor tDgSFD_view = make_tensor(\n                tDgSFD_mn.data(), make_layout(make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                                              make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n            Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n            Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n\n            static int constexpr NumVecs = size(tDgD) / VectorSize;\n            Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n            // Compute amax and quantization scales for this tile\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n            cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n            // Copy from TMEM to registers\n            copy(tiled_t2r, tDtAcc, tTR_rAcc);\n            cutlass::arch::fence_view_async_tmem_load();\n            accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n            ++accumulator_pipe_consumer_state;\n\n            if constexpr (!kUseFastMath) {\n              // Downcast to BF16 for bit-wise compatibility with\n              // unfused kernels\n              auto convert_accum_to_bf16 =\n                  cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                                 FragmentSize>{};\n              auto convert_bf16_to_accum =\n                  cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                                 FragmentSize>{};\n              tTR_rAcc_frag(_0{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n              tTR_rAcc_frag(_1{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n            }\n\n            auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n                tTR_rAcc_frag.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TD, VectorSize> *>(tDrD_frag.data());\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < NumVecs; v++) {\n              vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n            }\n\n            pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                vec_maxs, global_encode_scale_multiplier);\n            auto pvscales_cvted =\n                cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n            tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n            auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(\n                tD_rRowSFD_frg(_0{}));\n            auto qpvscale_scaled =\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                    qpvscale_ups, global_decode_scale);\n            cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n            if constexpr (kUseFastMath) {\n              // Fast math: compute approximate reciprocal\n              acc_scales =\n                  cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // Accurate math: compute reciprocal with division\n              acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                  1.0, qpvscale_scaled);\n            }\n\n            // Prepare stochastic rounding random state if enabled\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            // \"Prefetch\" a stochastic rounding state for the first tile\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            // Apply round/quantize to each fragment, with or without stochastic rounding\n            for (int v = 0; v < NumVecs; v++) {\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs[v], acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs[v], acc_scale));\n              }\n            }\n\n            // Write quantized FP4 tile and dequant scale to gmem\n            copy(tiled_r2g, src, dst);\n            copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFD, tDgSFD);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n    } else if (is_epilogue_row_quant_warp) {\n      // Warp responsible for quantizing the input (before Hadamard transform) to FP4 for row-wise usage.\n      cutlass::arch::warpgroup_reg_alloc<136>();\n      if constexpr (kEnableRowQuant) {\n        using S2RVectorType = uint128_t;\n\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % 256;\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // g2s load all global_a_amax for all groups/tensors\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < args.num_tensors; g += NumEpilogueRowQuantThreadCount) {\n          shared_storage.global_a_amax[g] =\n              __ldg(reinterpret_cast<float *>(args.global_a_amax_list[g]));\n        }\n        // RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // Input/output tensors/partitions for row quant warp\n        Tensor mQA =\n            make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, packed_N), dQA));\n        Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n        Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n\n        Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, X, _1>{});  // (BLK_M,BLK_N)\n        // Swizzled shared memory A tile, with layout\n        Tensor sA = as_position_independent_swizzle_tensor(group_modes<0, 2>(\n            coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                 sAlayout))));  // (BLOCK_M, BLOCK_M,PIPE)\n\n        // Set up layouts for partitioning \u2013 tile-by-warp, with vector granularity\n        using S2RWarpLayout = Layout<Shape<_8, _4>>;\n        using WarpGroupLayout = Layout<Shape<_1, _8>>;\n        using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n        using S2RValLayout = Layout<Shape<Int<VectorSize>, _1>>;\n        using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n        using R2GAtomQA = Copy_Atom<AutoVectorizingCopy, TQA>;\n        using R2GAtomSFA = Copy_Atom<AutoVectorizingCopy, TSFA>;\n        auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_QA = make_tiled_copy(R2GAtomQA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_SFA = make_tiled_copy(R2GAtomSFA{}, S2RThreadLayout{}, S2RValLayout{});\n\n        auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n        auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n        auto thr_r2g_SFA = tiled_r2g_SFA.get_slice(local_thread_idx);\n        Tensor tQAsA = thr_s2r.partition_S(sA);  // (Copy, Copy_M, Copy_N, PIPE)\n\n        // Allocate temporary register tensors for copying quantization => output\n        Tensor tQArA = make_tensor_like<TA>(\n            make_layout(tQAsA(_, _, _, _0{}).shape()));  // (Copy, Copy_M, Copy_N)\n        Tensor tQAgQA = thr_r2g_QA.partition_S(gQA_mn);\n        Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n\n        Tensor tQAgSFA = thr_r2g_SFA.partition_S(gSFA_mn);\n        Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n\n        // Will result in barrier_id=10 passed to bar.sync instr as cutlass adds 8\n        // in order to go over the reserved named barrier count.\n        constexpr int row_quant_barrier_id = 2;\n        cutlass::arch::NamedBarrier::sync(NumEpilogueRowQuantThreadCount, row_quant_barrier_id);\n\n        int group_idx = GetGroupIdx(&args, scheduler.tile_n_base() * size<1>(epilogue_tiler));\n        float a_global_amax_val = shared_storage.global_a_amax[group_idx];\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n\n        float global_decode_scale = 1.0f / global_encode_scale;\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n        auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n        do {\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = GetGroupIdx(&args, global_tile_n_offset);\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              a_global_amax_val = shared_storage.global_a_amax[group_idx];\n              // Update group quantization parameters/scaling\n              global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            }\n\n            auto tQAgSFA_mn =\n                tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state);\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n            cutlass::arch::fence_view_async_shared();\n            mainloop_pipeline.consumer_release(mainloop_pipe_consumer_state);\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n\n            // static int constexpr NumVecs = size(tQArA) / VectorSize;\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(\n                raw_pointer_cast(tQArQA.data()));\n            Tensor amax =\n                make_tensor<ElementAccumulator>(prepend(take<1, rank(tQArA)>(tQArA.shape()), _1{}));\n            Tensor pvscales = make_tensor_like<ElementAccumulator>(amax);\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512 +\n                                          tiles_in_m * tiles_in_n * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < size<1>(group_modes<1, rank(tQArA)>(tQArA)); v++) {\n              auto amax_view = group_modes<1, rank(amax)>(amax);\n              auto pvscales_view = group_modes<1, rank(pvscales)>(pvscales);\n              auto compute_frgs_up =\n                  cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(\n                      compute_frgs[v]);\n              amax_view(_0{}, v) = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n              pvscales_view(_0{}, v) = cutlass::multiplies<ElementAccumulator>{}(\n                  amax_view(_0{}, v), global_encode_scale_multiplier);\n              filter(tQArSFA)(v) = sfa_converter(pvscales_view(_0{}, v));\n              auto qpvscale_ups =\n                  cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n              auto qpvscale_scaled =\n                  cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n              ElementAccumulator acc_scales;\n              if constexpr (kUseFastMath) {\n                // Fast math: compute approximate reciprocal\n                acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(\n                    qpvscale_scaled);\n              } else {\n                // Accurate math: compute reciprocal with division\n                acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n              }\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales, cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              uint4 random_uint4 = uint4{0, 0, 0, 0};\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs_up, acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs_up, acc_scale));\n              }\n            }\n            copy(tiled_r2g_QA, tQArQA, tQAgQA_mn);\n            copy(tiled_r2g_SFA, filter(tQArSFA), filter(tQAgSFA_mn));\n          }\n          // scheduler.advance();\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n\n    } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n    }\n  }  // sm100 compile guard end\n}  // NOLINT(readability/fn_size)\n\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant,\n          bool kEnableSwizzleSFOutput, class TA, class TB, class TQA, class TSFA, class TD = TQA,\n          class TSFD = TSFA, bool kUseFastMath = false>\nvoid group_row_col_rht_gemm_ntt_w_sfc(int packed_sequence_length, int hidden_size, TA const *A,\n                                      TB const *B, TQA *QA, TSFA *SFA,\n                                      MultiAmaxHadamardCastFusionArgs &args,\n                                      const size_t *rng_state, uint32_t *tile_scheduler_workspace,\n                                      uint32_t sm_count, cudaStream_t stream,\n                                      int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0),\n                                               make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFDLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)),\n                                               make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(\n      SwizzledSFALayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(\n      SwizzledSFDLayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFDLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFDLayout>;\n  SFALayout sfa_layout;\n  SFDLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{});\n  } else {\n    sfa_layout = make_layout(\n        make_shape(make_shape(Int<SFVecSize>{}, hidden_size / SFVecSize), packed_sequence_length),\n        make_stride(make_stride(_0{}, _1{}), hidden_size / SFVecSize));\n    sfd_layout = make_layout(\n        make_shape(hidden_size, make_shape(Int<SFVecSize>{}, packed_sequence_length / SFVecSize)),\n        make_stride(packed_sequence_length / SFVecSize, make_stride(_0{}, _1{})));\n  }\n\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = packed_sequence_length;\n  Tensor tensorA = make_tensor(A, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);    // (dM,dK)\n  auto dB = stride(tensorB);    // (dN,dK)\n  auto dD = LayoutRight{};      // (dM,dN)\n  auto dQA = stride(tensorQA);  // (dM,dK)\n  using ClusterShape = Shape<_1, _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128, Int<RhtTensorSize>, Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128, Int<RhtTensorSize>, _128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n      size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                           UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cluster_tile_shape),\n      shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cluster_tile_shape),\n      shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem / (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 4;\n  static int constexpr MainloopPipelineBytes = sizeof(\n      typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<1, Shape<_1, _1, _1>,\n                                                               Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr SchedulerWorkspaceBytes = sizeof(int) * SchedulerPipelineStageCount;\n  static int constexpr SchedulerThrottlePipelineBytes =\n      sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr SchedulerPipelineBytes =\n      sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount,\n                                                     ClusterShape>::SharedStorage);\n\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(\n      typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                          Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448;  // 232KB in bytes\n  static int constexpr kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = SchedulerWorkspaceBytes + SchedulerThrottlePipelineBytes +\n                                        SchedulerPipelineBytes + TmemBasePtrsBytes +\n                                        TmemDeallocBytes + BTensorBytes +\n                                        AccPipelineBytes;  // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP),\n                                    Step<_2, _1, _3>{});  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, _1{}));  // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};                                        // XXX Dummy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cluster_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  assert(M % size<0>(cluster_tile_shape) == 0);\n  assert(N % size<1>(cluster_tile_shape) == 0);\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(sm_count, 1, 1);\n\n  int smem_size = sizeof(\n      SharedStorage<TA, TB, decltype(sA), decltype(sB), ClusterShape, AccumulatorPipelineStageCount,\n                    EpilogueUnrollFactor, SchedulerPipelineStageCount>);\n\n  auto *kernel_ptr = &group_row_col_rht_gemm_device<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cluster_shape),\n      decltype(cluster_tile_shape), TA, decltype(dA), decltype(sA), decltype(tma_load_a), TB,\n      decltype(dB), decltype(sB), decltype(tma_load_b), TD, decltype(dD), decltype(sD), TSFD,\n      decltype(sfd_layout), TQA, decltype(dQA), TSFA, decltype(sfa_layout), decltype(mma),\n      AccumulatorPipelineStageCount, SchedulerPipelineStageCount, kEnableStochasticRounding,\n      kEnableRHTColQuant, kEnableRowQuant, kEnableSwizzleSFOutput, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      cudaFuncSetAttribute(*kernel_ptr, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  // Set workspace and set to zero\n  NVTE_CHECK_CUDA(cudaMemsetAsync(reinterpret_cast<void *>(tile_scheduler_workspace), 0,\n                                  sizeof(uint32_t), stream));\n\n  // Launch kernel\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n      params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape, A, dA,\n      sA, tma_load_a, B, dB, sB, tma_load_b, QA, dQA, SFA, sfa_layout, args,\n      tile_scheduler_workspace, mma, rng_state);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion(const Tensor &input_, std::vector<Tensor *> &output_list,\n                                          const size_t *split_sections, size_t num_tensors,\n                                          const Tensor &hadamard_matrix_,\n                                          QuantizationConfig &quant_config, Tensor &quant_workspace,\n                                          cudaStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::MultiAmaxHadamardCastFusionArgs;\n\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  NVTE_CHECK(output_list.size() == num_tensors,\n             \"Number of output tensors should match number of tensors.\");\n\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // construct the multi-tensor args\n  MultiAmaxHadamardCastFusionArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  bool all_has_row_quant = true;\n  bool all_has_col_quant = true;\n  void *rowwise_data_base_ptr = nullptr;\n  void *rowwise_scale_inv_base_ptr = nullptr;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    NVTE_CHECK(split_sections[i] % 128 == 0, \"component \", i,\n               \" of split_sections should be 128 multiple\");\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    bool has_row_quant = output_list[i]->data.dptr != nullptr;\n    bool has_col_quant = output_list[i]->columnwise_data.dptr != nullptr;\n    all_has_row_quant = all_has_row_quant && has_row_quant;\n    all_has_col_quant = all_has_col_quant && has_col_quant;\n    // sanity check, the two bool flags cannot be both false\n    NVTE_CHECK(has_row_quant || has_col_quant,\n               \"At least one of the output tensors must have row or column quant.\");\n    void *amax_rowwise_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->amax.dptr) : nullptr;\n    void *amax_colwise_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_amax.dptr) : nullptr;\n    void *rowwise_data_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->data.dptr) : nullptr;\n    void *rowwise_scale_inv_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->scale_inv.dptr) : nullptr;\n    if (all_has_row_quant &&\n        (rowwise_data_base_ptr == nullptr || rowwise_scale_inv_base_ptr == nullptr)) {\n      rowwise_data_base_ptr = rowwise_data_ptr;\n      rowwise_scale_inv_base_ptr = rowwise_scale_inv_ptr;\n    }\n    void *output_colwise_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_data.dptr) : nullptr;\n    void *output_colwise_scale_inv_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_scale_inv.dptr)\n                      : nullptr;\n    kernel_args.global_a_amax_list[kernel_args.num_tensors] = amax_rowwise_ptr;\n    kernel_args.global_d_amax_list[kernel_args.num_tensors] = amax_colwise_ptr;\n    kernel_args.output_colwise_list[kernel_args.num_tensors] = output_colwise_ptr;\n    kernel_args.output_colwise_scale_inv_list[kernel_args.num_tensors] =\n        output_colwise_scale_inv_ptr;\n    kernel_args.split_sections[kernel_args.num_tensors] = split_sections[i];\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (use_stochastic_rounding) {\n    NVTE_CHECK(quant_config.rng_state != nullptr,\n               \"Enabled stochastic rounding without providing RNG state\");\n    const Tensor &rng_state_tensor = *convertNVTETensorCheck(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  uint32_t *tile_scheduler_workspace = nullptr;\n  NVTE_CHECK(quant_workspace.data.dptr != nullptr, \"Quantization workspace must be provided.\");\n  NVTE_CHECK(quant_workspace.data.buffer_size_bytes() >= sizeof(uint32_t),\n             \"Quantization workspace must be at least 4 bytes.\");\n  tile_scheduler_workspace = reinterpret_cast<uint32_t *>(quant_workspace.data.dptr);\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  const bool use_swizzle_sf_output = false;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          all_has_col_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_has_row_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::group_row_col_rht_gemm_ntt_w_sfc<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TQA, TSFA, TD, TSFD, kUseFastMath>(\n                            /*packed_sequence_length=*/m, /*hidden_size=*/n,\n                            /*A=*/reinterpret_cast<TA const *>(input.dptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_base_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_base_ptr),\n                            /*args=*/kernel_args,\n                            /*rng_state=*/rng_state,\n                            /*tile_scheduler_workspace=*/tile_scheduler_workspace,\n                            /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion(const NVTETensor input, NVTETensor *outputs,\n                                               const NVTETensor hadamard_matrix,\n                                               const size_t *split_sections,\n                                               const size_t num_tensors,\n                                               const NVTEQuantizationConfig quant_config,\n                                               NVTETensor quant_workspace, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_cast_fusion);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n\n  Tensor *input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor *> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  Tensor *quant_workspace_tensor = convertNVTETensorCheck(quant_workspace);\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion(*input_tensor, output_list, split_sections, num_tensors,\n                                       *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n                                       *quant_workspace_tensor, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"customized_pipeline.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/detail/sm100_blockscaled_layout.hpp\"\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/float8.h\"\n#include \"cutlass/float_subbyte.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/platform/platform.h\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\n\n// Ensure Tensor refers to cute::Tensor, not transformer_engine::Tensor\nusing cute::Tensor;\n\nconstexpr int kMaxTensorsPerKernel = 64;\n\nstruct MultiAmaxHadamardCastFusionArgs {\n  // (output) Amax buffer for input A amax buffer\n  void *global_a_amax_list[kMaxTensorsPerKernel];\n  // (output) Amax buffer for pre-RHT amax buffer\n  void *global_d_amax_list[kMaxTensorsPerKernel];\n  // output D pointers for each tensor\n  void *output_colwise_list[kMaxTensorsPerKernel];\n  // output SFD inverse pointers for each tensor\n  void *output_colwise_scale_inv_list[kMaxTensorsPerKernel];\n  // split sections of each tensor of input\n  int split_sections[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\n__device__ __forceinline__ int GetGroupIdx(MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                           int offset) {\n  // Check the kernel args and get the corresponding id\n  const int num_tensors = kernel_args_ptr->num_tensors;\n  if (offset >= kernel_args_ptr->split_sections_range[num_tensors]) {\n    return num_tensors - 1;\n  }\n  int group_idx = 0;\n  while (kernel_args_ptr->split_sections_range[group_idx + 1] <= offset) {\n    ++group_idx;\n  }\n  return group_idx;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(&rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout, class ClusterShape,\n          int AccumulatorPipelineStageCount_, int EpilogueUnrollFactor_,\n          int SchedulerPipelineStageCount_>\nstruct SharedStorage {\n  static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n  static int constexpr EpilogueUnrollFactor = EpilogueUnrollFactor_;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                 AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>,\n                                                      AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n  using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount_, ClusterShape>;\n  using SchedPipelineStorage = typename SchedPipeline::SharedStorage;\n  using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount_>;\n  using SchedThrottlePipelineStorage = typename SchedThrottlePipeline::SharedStorage;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  alignas(16) SchedPipelineStorage sched;\n  alignas(16) SchedThrottlePipelineStorage sched_throttle;\n  alignas(16) int32_t atomic_tile_id[SchedulerPipelineStageCount_];\n  alignas(16) float global_a_amax[kMaxTensorsPerKernel];\n  alignas(16) float global_d_amax[kMaxTensorsPerKernel];\n  uint32_t atomic_tile_counter[SchedulerPipelineStageCount_];\n  uint32_t tmem_base_ptr;\n};\n\n// Main RHT GEMM kernel entry -- highly templated for flexible architecture/config support\ntemplate <class MShape, class NShape, class KShape, class ClusterShape, class ClusterTileShape,\n          class TA, class AStride, class ASmemLayout, class TmaLoadA, class TB, class BStride,\n          class BSmemLayout, class TmaLoadB, class TD, class DStride, class DSmemLayout, class TSFD,\n          class TSFDLayout, class TQA, class QAStride, class TSFA, class TSFALayout, class TiledMMA,\n          int AccumulatorPipelineStageCount_, int SchedulerPipelineStageCount_,\n          bool kEnableStochasticRounding_ = false, bool kEnableRHTColQuant_ = true,\n          bool kEnableRowQuant_ = true, bool kEnableSwizzleSFOutput_ = false,\n          bool kUseFastMath_ = false>\n__launch_bounds__(512, 1) __global__ static void group_row_col_rht_gemm_device(\n    MShape M, NShape packed_N, KShape K, ClusterShape cluster_shape, ClusterTileShape cluster_tile,\n    TA const *A, AStride dA, ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a,\n    TB const *B, BStride dB, BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b,\n    TQA *QA, QAStride dQA, TSFA *SFA, TSFALayout sfa_layout, MultiAmaxHadamardCastFusionArgs args,\n    uint32_t *tile_scheduler_workspace, TiledMMA mma, const size_t *rng_state) {\n  using namespace cute;\n\n  // Abort immediately if compilation is not supported\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    static_assert(kEnableRHTColQuant_ || kEnableRowQuant_,\n                  \"group_row_col_rht_gemm_device must generate row-wise \"\n                  \"and/or column-wise output.\");\n#if !defined(CUTLASS_ARCH_CLC_ENABLED)\n    CUTLASS_NOT_IMPLEMENTED();\n    return;\n#endif\n\n    using X = Underscore;\n    // Accumulator data type for main computation\n    using ElementAccumulator = float;\n    static int constexpr K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static uint32_t constexpr kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n    static constexpr bool kEnableStochasticRounding = kEnableStochasticRounding_;\n    static constexpr bool kEnableRHTColQuant = kEnableRHTColQuant_;\n    static constexpr bool kEnableRowQuant = kEnableRowQuant_;\n    static constexpr bool kEnableSwizzleSFOutput = kEnableSwizzleSFOutput_;\n    static constexpr bool kUseFastMath = kUseFastMath_;\n\n    // Constant for RHT tensor processing (tile size etc)\n    static int constexpr RhtTensorSize = 16;\n\n    // Transaction bytes for TMA transfer on RHT tensor blocks\n    static int constexpr kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(RhtTensorSize * RhtTensorSize * cute::sizeof_bits_v<TB>);\n    static int constexpr AccumulatorPipelineStageCount = AccumulatorPipelineStageCount_;\n    static int constexpr SchedulerPipelineStageCount = SchedulerPipelineStageCount_;\n\n    // Mainloop pipeline stage calculation, vectorization parameters for scaling factors\n    static int constexpr MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    static int constexpr SFVecSize = 16;\n    // Swizzle output layout for scaling factor arrays\n    using SwizzledSFALayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n    using SwizzledSFDLayoutAtom =\n        cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n\n    // Mainloop pipeline types for TMA async execution and epilogue cluster scheduling\n    using MainloopPipeline =\n        cutlass::detail::CustomizedPipelineTmaUmmaAsync<MainloopPipelineStageCount, ClusterShape,\n                                                        AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n    using SchedPipeline = cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount, ClusterShape>;\n    using SchedPipelineState = typename SchedPipeline::PipelineState;\n    using SchedThrottlePipeline = cutlass::PipelineAsync<SchedulerPipelineStageCount>;\n    using SchedThrottlePipelineState = typename SchedThrottlePipeline::PipelineState;\n\n    static_assert(ClusterShape{} == Shape<_1, _1, _1>{}, \"ClusterShape must be Shape<_1,_1,_1>\");\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static int constexpr VectorSize = RhtTensorSize;\n\n    // Compile-time safety: static shapes required for shared memory layouts\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    //   CUTE_STATIC_ASSERT(is_static<DSmemLayout>::value);\n\n    auto cluster_size = size<0>(cluster_shape);\n    auto mainloop_tiler = Shape<_128, _16, _128>{};\n    auto epilogue_tiler = Shape<_128, _128, _128>{};\n\n    static int constexpr EpilogueUnrollFactor = size<2>(epilogue_tiler) / size<2>(cluster_tile);\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    int const K_TILE_MAX = min(packed_N, K) / size<2>(epilogue_tiler);\n\n    struct TileScheduler {\n      uint32_t tiles_in_m = 0;\n      uint32_t tiles_in_n = 0;\n      uint32_t linear_idx = 0;\n      uint32_t next_linear_idx = 0;\n      uint32_t start_idx = 0;\n      uint32_t tile_m_idx = 0;\n      uint32_t tile_n_idx = 0;\n      int k_tile_max = 0;\n      uint32_t *atomic_tile_index_;\n      uint32_t *smem_tile_counter;\n      uint32_t atomic_offset;\n      cutlass::FastDivmodU64 divmod_tiles_in_m;\n\n      CUTLASS_DEVICE TileScheduler(uint32_t tiles_m, uint32_t tiles_n, int kmax,\n                                   uint32_t *atomic_tile_index, uint32_t *smem_tile_counter)\n          : tiles_in_m(tiles_m),\n            tiles_in_n(tiles_n),\n            linear_idx(blockIdx.x),\n            next_linear_idx(blockIdx.x),\n            start_idx(blockIdx.x),\n            k_tile_max(kmax),\n            atomic_tile_index_(atomic_tile_index),\n            smem_tile_counter(smem_tile_counter),\n            atomic_offset(gridDim.x),\n            divmod_tiles_in_m(uint64_t(tiles_m)) {\n        update_tile_idx();\n      }\n      CUTLASS_DEVICE void update_tile_idx() {\n        uint64_t q, r;\n        divmod_tiles_in_m(q, r, uint64_t(linear_idx));\n        tile_m_idx = static_cast<uint32_t>(r);\n        tile_n_idx = static_cast<uint32_t>(q) * uint32_t(k_tile_max);\n      }\n      CUTLASS_DEVICE uint32_t tile_m() const { return tile_m_idx; }\n      CUTLASS_DEVICE uint32_t tile_n_base() const { return tile_n_idx; }\n      CUTLASS_DEVICE uint32_t tiles_m() const { return tiles_in_m; }\n\n      CUTLASS_DEVICE uint32_t tiles_n() const { return tiles_in_n; }\n\n      CUTLASS_DEVICE bool is_valid() const {\n        return cute::elem_less(cute::make_coord(tile_m(), tile_n_base()),\n                               cute::make_coord(tiles_in_m, tiles_in_n));\n      }\n\n      CUTLASS_DEVICE bool is_first_wave() const { return linear_idx == start_idx; }\n\n      CUTLASS_DEVICE uint32_t get_linear_tile_idx() const { return linear_idx; }\n\n      // Fetch a new tile_id using atomics.\n      CUTLASS_DEVICE uint32_t fetch_tile_id_counter(int pred) {\n        uint32_t tile_id_counter = 0;\n        asm volatile(\n            \"{\\n\\t\"\n            \".reg .pred p;\\n\\t\"\n            \"setp.eq.u32 p, %2, 1;\\n\\t\"\n            \"@p atom.global.add.u32 %0, [%1], 1; \\n\\t\"\n            \"}\"\n            : \"=r\"(tile_id_counter)\n            : \"l\"(atomic_tile_index_), \"r\"(pred));\n\n        return tile_id_counter;\n      }\n\n      CUTLASS_DEVICE auto fetch_next_work(SchedPipeline &sched_pipeline,\n                                          SchedPipelineState sched_pipeline_consumer_state) {\n        sched_pipeline.consumer_wait(sched_pipeline_consumer_state);\n        next_linear_idx = smem_tile_counter[sched_pipeline_consumer_state.index()];\n        cutlass::arch::fence_view_async_shared();\n        sched_pipeline.consumer_release(sched_pipeline_consumer_state);\n        return;\n      }\n\n      CUTLASS_DEVICE auto advance_to_next_work(SchedPipeline &sched_pipeline,\n                                               SchedPipelineState sched_pipeline_producer_state) {\n        uint32_t mbarrier_addr = sched_pipeline.producer_get_barrier(sched_pipeline_producer_state);\n        // Wait for clcID buffer to become empty with a flipped phase\n        sched_pipeline.producer_acquire(sched_pipeline_producer_state);\n        auto is_leading_thread = cute::elect_one_sync();\n        uint32_t tile_id_counter = fetch_tile_id_counter(is_leading_thread) + atomic_offset;\n        uint32_t smem_addr =\n            cute::cast_smem_ptr_to_uint(&smem_tile_counter[sched_pipeline_producer_state.index()]);\n        if (is_leading_thread) {\n          cute::store_shared_remote(tile_id_counter, smem_addr, mbarrier_addr, 0);\n        }\n\n        ++sched_pipeline_producer_state;\n        return sched_pipeline_producer_state;\n      }\n\n      CUTLASS_DEVICE auto update_work_tile_info() {\n        linear_idx = next_linear_idx;\n        update_tile_idx();\n        return;\n      }\n    };\n\n    // Allocate and alias shared memory to the kernel's shared storage type\n    extern __shared__ char shared_memory[];\n    using SharedStorage =\n        SharedStorage<TA, TB, ASmemLayout, BSmemLayout, ClusterShape, AccumulatorPipelineStageCount,\n                      EpilogueUnrollFactor, SchedulerPipelineStageCount>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n\n    // Compute the number of tiles in M and N after tiling and assign scheduler\n    uint32_t tiles_in_m = uint32_t(size(ceil_div(M, size<0>(cluster_tile))));\n    uint32_t tiles_in_n = uint32_t(\n        size(ceil_div(args.split_sections_range[args.num_tensors], size<2>(epilogue_tiler))));\n\n    TileScheduler scheduler(tiles_in_m, tiles_in_n, K_TILE_MAX, tile_scheduler_workspace,\n                            shared_storage.atomic_tile_counter);\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n\n    // Shapes for accumulated tiles in mainloop and epilogue\n    auto acc_shape_mma = make_shape(take<0, 2>(mainloop_tiler), _1{}, _1{});\n    auto acc_shape_epilogue = make_shape(take<0, 2>(epilogue_tiler), _1{}, _1{});\n\n    // Shape of the accumulator fragment for the main loop pipeline, with pipeline stages appended\n    auto acc_mainloop_pipelined_shape = append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{});\n    auto bulk_tmem_mma = TiledMMA::make_fragment_C(acc_mainloop_pipelined_shape);\n\n    // Number of threads assigned for various epilogue roles depending on quantization settings\n    static int constexpr NumEpilogueColQuantThreadCount = kEnableRHTColQuant ? 128 : 0;\n    static int constexpr NumEpilogueRowQuantThreadCount = kEnableRowQuant ? 256 : 0;\n    static int constexpr NumMmaThreadCount = kEnableRHTColQuant ? 32 : 0;\n    static int constexpr NumMmaIssueThreadCount = kEnableRHTColQuant ? 1 : 0;\n    static int constexpr NumSchedThreads = 32;\n    static int constexpr NumMainloopLoadThreads = 32;\n    static int constexpr NumEpilogueThreads =\n        NumEpilogueColQuantThreadCount + NumEpilogueRowQuantThreadCount;\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        NumMmaThreadCount + NumEpilogueColQuantThreadCount,\n        cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    // warp assignment\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_sched_warp = (warp_idx == 2);\n    bool is_epilogue_col_quant_warp = (warp_idx >= 4 && warp_idx <= 7);\n    bool is_epilogue_row_quant_warp = (warp_idx >= 8 && warp_idx <= 15);\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    mainloop_pipeline_params.num_consumers =\n        NumEpilogueRowQuantThreadCount + NumMmaIssueThreadCount;\n\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                   AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n    using AccumulatorPipelineInitBarriers = cute::bool_constant<kEnableRHTColQuant>;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_col_quant_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count =\n        size(AtomThrShapeMNK{}) * NumEpilogueColQuantThreadCount;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(\n        shared_storage.accumulator, accumulator_pipeline_params, cluster_shape,\n        AccumulatorPipelineInitBarriers{}, cute::true_type{});  // Delay mask calculation\n    typename SchedPipeline::Params sched_pipeline_params;\n    if (is_sched_warp) {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::ProducerConsumer;\n    } else {\n      sched_pipeline_params.role = SchedPipeline::ThreadCategory::Consumer;\n    }\n    sched_pipeline_params.producer_blockid = 0;\n    sched_pipeline_params.producer_arv_count = 1;\n    sched_pipeline_params.consumer_arv_count =\n        NumSchedThreads +\n        cluster_size * (NumMainloopLoadThreads + NumEpilogueThreads + NumMmaThreadCount);\n    sched_pipeline_params.transaction_bytes = sizeof(uint32_t);\n    sched_pipeline_params.initializing_warp = 3;\n    SchedPipeline sched_pipeline(shared_storage.sched, sched_pipeline_params, cluster_shape);\n    SchedPipelineState sched_pipeline_consumer_state;\n    SchedPipelineState sched_pipeline_producer_state =\n        cutlass::make_producer_start_state<SchedPipeline>();\n\n    typename SchedThrottlePipeline::Params sched_throttle_pipeline_params;\n    if (is_dma_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Producer;\n    }\n    if (is_sched_warp) {\n      sched_throttle_pipeline_params.role = SchedThrottlePipeline::ThreadCategory::Consumer;\n    }\n    sched_throttle_pipeline_params.producer_arv_count = NumMainloopLoadThreads;\n    sched_throttle_pipeline_params.consumer_arv_count = NumSchedThreads;\n    sched_throttle_pipeline_params.dst_blockid = 0;\n    sched_throttle_pipeline_params.initializing_warp = 4;\n\n    SchedThrottlePipeline sched_throttle_pipeline(shared_storage.sched_throttle,\n                                                  sched_throttle_pipeline_params);\n    SchedThrottlePipelineState sched_pipeline_throttle_consumer_state;\n    SchedThrottlePipelineState sched_pipeline_throttle_producer_state =\n        cutlass::make_producer_start_state<SchedThrottlePipeline>();\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n\n    // Warp group roles: DMA (global->shared copy), MMA (tensor core gemm), scheduler, column quantizer, row quantizer\n    if (is_dma_warp) {\n      // Warp responsible for loading input from global to shared memory using TMA (Tensor Memory Access).\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      // Get TMA tensors for input matrix A and B (Hadamard/transform matrix) from global memory.\n      Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, packed_N));\n      Tensor mB = tma_load_b.get_tma_tensor(make_shape(RhtTensorSize, RhtTensorSize));\n\n      // Partition tensors for tiling according to the mainloop and cluster tilers.\n      Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n      Tensor gB_nk =\n          local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n\n      // Shared memory tensors for pipeline\n      Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n      Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n      // Determine warp/tile positioning\n      int block_rank_in_cluster = cute::block_rank_in_cluster();\n      ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n      // Partition global to local fragments for A and B\n      Tensor tCgA = thr_mma.partition_A(gA_mk);  // (MMA,MMA_M,MMA_K,k)\n      Tensor tCgB = thr_mma.partition_B(gB_nk);  // (MMA,MMA_N,MMA_K,k)\n\n      Layout cta_layout_mnk = make_layout(cluster_shape);\n      Layout cta_layout_vmnk =\n          tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n      auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n      auto [tAgA, tAsA] =\n          tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n      auto [tBgB, tBsB] =\n          tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                        group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n      uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n      uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n      if constexpr (kEnableRHTColQuant) {\n        if (elect_one_sync()) {\n          cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                              kTmaRhtTensorTransactionBytes);\n          copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n               tBsB(_, 0));\n        }\n      }\n\n      do {\n        // is_first_wave indicates whether this scheduler wave is the first among a group.\n        bool is_first_wave = scheduler.is_first_wave();\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, scheduler.tile_m(), _);\n        int k_tile = 0;\n\n        sched_throttle_pipeline.producer_acquire(sched_pipeline_throttle_producer_state);\n        sched_throttle_pipeline.producer_commit(sched_pipeline_throttle_producer_state);\n        ++sched_pipeline_throttle_producer_state;\n        CUTLASS_PRAGMA_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n()) {\n          int k_tile_idx_n = scheduler.tile_n_base() + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n        // scheduler.advance();\n      } while (scheduler.is_valid());\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      // This warp executes the main tensor core matrix-multiply-accumulate for the Hadamard transform.\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      if constexpr (kEnableRHTColQuant) {\n        // Setup shared memory fragments for A and B tiles.\n        Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                  sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n        Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                                  sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n        int block_rank_in_cluster = cute::block_rank_in_cluster();\n        ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n        // Allocate \"fragments\" -- these are actually umma smem descriptors\n        Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n        Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n        mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n        tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                                &shared_storage.tmem_base_ptr);\n        __syncwarp();\n        tmem_allocation_result_barrier.arrive();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_mma.data() = tmem_base_ptr;\n        // Wait until the B (Hadamard) tensor copy is complete\n        cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n        do {\n          uint32_t skip_wait = K_TILE_MAX <= 0;\n\n          auto barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            int read_stage = mainloop_pipe_consumer_state.index();\n            auto tCrA_mk = tCrA(_, _, _, read_stage);\n            auto tCrB_nk = tCrB(_, _, 0, 0);\n            CUTLASS_PRAGMA_UNROLL\n            for (int k_block = 0; k_block < size<2>(tCrA) / EpilogueUnrollFactor; ++k_block) {\n              int accumulator_k_block =\n                  accumulator_pipe_producer_state.index() * EpilogueUnrollFactor;\n              int tCrA_k_block = k_block * EpilogueUnrollFactor;\n              accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n              CUTLASS_PRAGMA_UNROLL\n              for (int i = 0; i < EpilogueUnrollFactor; i++) {\n                auto accumulators = bulk_tmem_mma(_, _, _, accumulator_k_block + i);\n                gemm(mma, tCrA_mk(_, _, tCrA_k_block + i), tCrB_nk, accumulators);\n              }\n\n              accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n              ++accumulator_pipe_producer_state;\n            }\n            auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n            skip_wait = k_tile >= K_TILE_MAX;\n            mainloop_pipeline.umma_consumer_release(curr_mainloop_pipe_consumer_state);\n            barrier_token =\n                mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n        tmem_allocator.release_allocation_lock();\n        accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n        tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n      }\n    } else if (is_sched_warp) {\n      // Scheduler warp manages tile assignment and pipeline progress for warps\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n      do {\n        sched_throttle_pipeline.consumer_wait(sched_pipeline_throttle_consumer_state);\n        sched_throttle_pipeline.consumer_release(sched_pipeline_throttle_consumer_state);\n        ++sched_pipeline_throttle_consumer_state;\n        sched_pipeline_producer_state =\n            scheduler.advance_to_next_work(sched_pipeline, sched_pipeline_producer_state);\n        scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n        ++sched_pipeline_consumer_state;\n        scheduler.update_work_tile_info();\n      } while (scheduler.is_valid());\n    } else if (is_epilogue_col_quant_warp) {\n      // Warp responsible for quantizing output of Hadamard transform to FP4 for columnwise usage,\n      // and writing result tensors/scales to global memory.\n      cutlass::arch::warpgroup_reg_alloc<192>();\n      if constexpr (kEnableRHTColQuant) {\n        using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n        auto acc_epilogue_pipelined_shape =\n            append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / EpilogueUnrollFactor>{});\n        auto bulk_tmem_epilogue_layout = make_layout(\n            acc_epilogue_pipelined_shape,\n            make_stride(stride<0>(bulk_tmem_mma), Int<0>{}, Int<0>{}, size<1>(epilogue_tiler)));\n        auto bulk_tmem_epilogue = make_tensor(make_tmem_ptr<uint32_t>(), bulk_tmem_epilogue_layout);\n\n        // Use 256-bit fragments for aligned bulk stores\n        static int constexpr FragmentSize = 256 / sizeof_bits_v<TD>;\n\n        // Wait for TMEM allocation for this pipeline to finish\n        tmem_allocation_result_barrier.arrive_and_wait();\n        uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n        bulk_tmem_epilogue.data() = tmem_base_ptr;\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % cutlass::NumThreadsPerWarpGroup;\n        // g2s load all global_d_amax\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < args.num_tensors; g += NumEpilogueColQuantThreadCount) {\n          shared_storage.global_d_amax[g] =\n              __ldg(reinterpret_cast<float *>(args.global_d_amax_list[g]));\n        }\n\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // Setup RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        int group_idx = GetGroupIdx(&args, scheduler.tile_n_base() * size<1>(epilogue_tiler));\n\n        // Determine quantization scale factor layouts/output splits for this group\n        TSFDLayout sfd_layout;\n        int cur_N = args.split_sections[group_idx];\n        if constexpr (kEnableSwizzleSFOutput) {\n          sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n        } else {\n          sfd_layout = make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                   make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n        }\n        // Build output tensors for columns and their quant scales\n        Tensor mD = make_tensor(\n            cute::subbyte_iterator<TD>(reinterpret_cast<TD *>(args.output_colwise_list[group_idx])),\n            make_shape(M, cur_N), DStride{});  // (M,packed_N)\n        Tensor gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                  Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                      args.output_colwise_scale_inv_list[group_idx])),\n                                  sfd_layout);\n        Tensor gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n        Tensor gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n\n        // Setup tile-level TMEM (t2r) and global memory (r2g) copy descriptors\n        auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n        auto tiled_r2g =\n            make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TD>{}, tiled_t2r);\n        auto thr_t2r = tiled_t2r.get_slice(local_thread_idx);\n        auto thr_r2g = tiled_r2g.get_slice(local_thread_idx);\n\n        cutlass::arch::NamedBarrier::sync(NumEpilogueColQuantThreadCount,\n                                          cutlass::arch::ReservedNamedBarriers::EpilogueBarrier);\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float c_global_amax_val = shared_storage.global_d_amax[group_idx];\n        float global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n        float global_decode_scale = 1.0f / global_encode_scale;\n\n        // Scaling factor for fast math path\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n        do {\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();\n               ++k_tile) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = GetGroupIdx(&args, global_tile_n_offset);\n\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              c_global_amax_val = shared_storage.global_d_amax[group_idx];\n              // update amax\n              global_encode_scale = c_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / c_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n              cur_N = args.split_sections[group_idx];\n              if constexpr (kEnableSwizzleSFOutput) {\n                sfd_layout =\n                    tile_to_shape(SwizzledSFDLayoutAtom{}, make_shape(M, cur_N), Step<_2, _1>{});\n              } else {\n                sfd_layout =\n                    make_layout(make_shape(M, make_shape(Int<SFVecSize>{}, cur_N / SFVecSize)),\n                                make_stride(cur_N / SFVecSize, make_stride(_0{}, _1{})));\n              }\n              // update tensor\n              mD = make_tensor(cute::subbyte_iterator<TD>(\n                                   reinterpret_cast<TD *>(args.output_colwise_list[group_idx])),\n                               make_shape(M, cur_N), DStride{});\n              gD_mn = local_tile(mD, epilogue_tiler, make_coord(_, _, _),\n                                 Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n              mSFD = make_tensor(make_gmem_ptr<TSFD>(reinterpret_cast<TSFD *>(\n                                     args.output_colwise_scale_inv_list[group_idx])),\n                                 sfd_layout);\n              gSFD_mn = local_tile(mSFD, epilogue_tiler, make_coord(_, _, _),\n                                   Step<_1, _1, X>{});  // (BLK_M,BLK_N)\n\n              gD_mn_view = tiled_divide(gD_mn, take<0, 2>(epilogue_tiler));\n            }\n            int group_start_offset = args.split_sections_range[group_idx];\n            int local_tile_n_idx =\n                (global_tile_n_offset - group_start_offset) / size<1>(epilogue_tiler);\n            Tensor tDgD_mn = gD_mn_view(_, _, _, scheduler.tile_m(), local_tile_n_idx);\n\n            Tensor tDgSFD_mn = gSFD_mn(_, _, scheduler.tile_m(), local_tile_n_idx);\n            accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n            auto Acc = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n            Tensor tDtAcc = thr_t2r.partition_S(Acc);    // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDgD = thr_t2r.partition_D(tDgD_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n            Tensor tTR_rAcc = make_tensor<ElementAccumulator>(\n                shape(tDgD));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n            Tensor tDrD = make_tensor<TD>(shape(tDgD));\n            Tensor tTR_rAcc_frag =\n                recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n            Tensor tDrD_frag = recast<cutlass::Array<TD, FragmentSize>>(coalesce(tDrD));\n\n            Tensor src = thr_r2g.retile_S(tDrD);\n            Tensor dst = thr_r2g.retile_D(tDgD);\n\n            Tensor tDgSFD_view = make_tensor(\n                tDgSFD_mn.data(), make_layout(make_shape(shape(tDgSFD_mn), Int<1>{}, Int<1>{}),\n                                              make_stride(stride(tDgSFD_mn), Int<0>{}, Int<0>{})));\n            Tensor tDgSFD = filter(thr_t2r.partition_D(tDgSFD_view));\n            Tensor tDrSFD = make_tensor<TSFD>(shape(tDgSFD));\n\n            static int constexpr NumVecs = size(tDgD) / VectorSize;\n            Tensor tD_rRowSFD_frg = recast<cutlass::Array<TSFD, NumVecs>>(tDrSFD);\n\n            // Compute amax and quantization scales for this tile\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n            cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n            // Copy from TMEM to registers\n            copy(tiled_t2r, tDtAcc, tTR_rAcc);\n            cutlass::arch::fence_view_async_tmem_load();\n            accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n            ++accumulator_pipe_consumer_state;\n\n            if constexpr (!kUseFastMath) {\n              // Downcast to BF16 for bit-wise compatibility with\n              // unfused kernels\n              auto convert_accum_to_bf16 =\n                  cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                                 FragmentSize>{};\n              auto convert_bf16_to_accum =\n                  cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                                 FragmentSize>{};\n              tTR_rAcc_frag(_0{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n              tTR_rAcc_frag(_1{}) =\n                  convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_1{})));\n            }\n\n            auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n                tTR_rAcc_frag.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TD, VectorSize> *>(tDrD_frag.data());\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < NumVecs; v++) {\n              vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n            }\n\n            pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                vec_maxs, global_encode_scale_multiplier);\n            auto pvscales_cvted =\n                cutlass::NumericArrayConverter<TSFD, ElementAccumulator, NumVecs>{}(pvscales);\n\n            tD_rRowSFD_frg(_0{}) = pvscales_cvted;\n            auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFD, NumVecs>{}(\n                tD_rRowSFD_frg(_0{}));\n            auto qpvscale_scaled =\n                cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                    qpvscale_ups, global_decode_scale);\n            cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n            if constexpr (kUseFastMath) {\n              // Fast math: compute approximate reciprocal\n              acc_scales =\n                  cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n            } else {\n              // Accurate math: compute reciprocal with division\n              acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                  1.0, qpvscale_scaled);\n            }\n\n            // Prepare stochastic rounding random state if enabled\n            uint4 random_uint4 = uint4{0, 0, 0, 0};\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            // \"Prefetch\" a stochastic rounding state for the first tile\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            // Apply round/quantize to each fragment, with or without stochastic rounding\n            for (int v = 0; v < NumVecs; v++) {\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs[v], acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TD, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs[v], acc_scale));\n              }\n            }\n\n            // Write quantized FP4 tile and dequant scale to gmem\n            copy(tiled_r2g, src, dst);\n            copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFD, tDgSFD);\n          }\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n    } else if (is_epilogue_row_quant_warp) {\n      // Warp responsible for quantizing the input (before Hadamard transform) to FP4 for row-wise usage.\n      cutlass::arch::warpgroup_reg_alloc<136>();\n      if constexpr (kEnableRowQuant) {\n        using S2RVectorType = uint128_t;\n\n        int global_thread_idx = threadIdx.x;\n        int local_thread_idx = global_thread_idx % 256;\n        size_t rng_seed = 0;\n        size_t rng_offset = 0;\n        // g2s load all global_a_amax for all groups/tensors\n        CUTLASS_PRAGMA_NO_UNROLL\n        for (int g = local_thread_idx; g < args.num_tensors; g += NumEpilogueRowQuantThreadCount) {\n          shared_storage.global_a_amax[g] =\n              __ldg(reinterpret_cast<float *>(args.global_a_amax_list[g]));\n        }\n        // RNG for stochastic rounding\n        if constexpr (kEnableStochasticRounding) {\n          rng_seed = rng_state != nullptr ? __ldg(rng_state) : 0;\n          rng_offset = rng_state != nullptr ? __ldg(rng_state + 1) : 0;\n        }\n        // Input/output tensors/partitions for row quant warp\n        Tensor mQA =\n            make_tensor(cute::subbyte_iterator<TQA>(QA), make_layout(make_shape(M, packed_N), dQA));\n        Tensor gQA_mn = local_tile(mQA, epilogue_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n        Tensor mSFA = make_tensor(make_gmem_ptr(SFA), sfa_layout);\n\n        Tensor gSFA_mn = local_tile(mSFA, epilogue_tiler, make_coord(_, _, _),\n                                    Step<_1, X, _1>{});  // (BLK_M,BLK_N)\n        // Swizzled shared memory A tile, with layout\n        Tensor sA = as_position_independent_swizzle_tensor(group_modes<0, 2>(\n            coalesce(make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                                 sAlayout))));  // (BLOCK_M, BLOCK_M,PIPE)\n\n        // Set up layouts for partitioning \u2013 tile-by-warp, with vector granularity\n        using S2RWarpLayout = Layout<Shape<_8, _4>>;\n        using WarpGroupLayout = Layout<Shape<_1, _8>>;\n        using S2RThreadLayout = decltype(blocked_product(S2RWarpLayout{}, WarpGroupLayout{}));\n        using S2RValLayout = Layout<Shape<Int<VectorSize>, _1>>;\n        using S2RAtomA = Copy_Atom<AutoVectorizingCopy, TA>;\n        using R2GAtomQA = Copy_Atom<AutoVectorizingCopy, TQA>;\n        using R2GAtomSFA = Copy_Atom<AutoVectorizingCopy, TSFA>;\n        auto tiled_s2r = make_tiled_copy(S2RAtomA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_QA = make_tiled_copy(R2GAtomQA{}, S2RThreadLayout{}, S2RValLayout{});\n        auto tiled_r2g_SFA = make_tiled_copy(R2GAtomSFA{}, S2RThreadLayout{}, S2RValLayout{});\n\n        auto thr_s2r = tiled_s2r.get_slice(local_thread_idx);\n        auto thr_r2g_QA = tiled_r2g_QA.get_slice(local_thread_idx);\n        auto thr_r2g_SFA = tiled_r2g_SFA.get_slice(local_thread_idx);\n        Tensor tQAsA = thr_s2r.partition_S(sA);  // (Copy, Copy_M, Copy_N, PIPE)\n\n        // Allocate temporary register tensors for copying quantization => output\n        Tensor tQArA = make_tensor_like<TA>(\n            make_layout(tQAsA(_, _, _, _0{}).shape()));  // (Copy, Copy_M, Copy_N)\n        Tensor tQAgQA = thr_r2g_QA.partition_S(gQA_mn);\n        Tensor tQArQA = make_tensor_like(tQAgQA(_, _, _, _0{}, _0{}));\n\n        Tensor tQAgSFA = thr_r2g_SFA.partition_S(gSFA_mn);\n        Tensor tQArSFA = make_tensor_like(tQAgSFA(_, _, _, _0{}, _0{}));\n\n        // Will result in barrier_id=10 passed to bar.sync instr as cutlass adds 8\n        // in order to go over the reserved named barrier count.\n        constexpr int row_quant_barrier_id = 2;\n        cutlass::arch::NamedBarrier::sync(NumEpilogueRowQuantThreadCount, row_quant_barrier_id);\n\n        int group_idx = GetGroupIdx(&args, scheduler.tile_n_base() * size<1>(epilogue_tiler));\n        float a_global_amax_val = shared_storage.global_a_amax[group_idx];\n        // Aligning with TensorEngine's recipe to generate scale factors // {$nv-internal-release}\n        static constexpr float fp4_max = 6.0f;\n        static constexpr float fp8_max = 448.0f;\n        static constexpr float fp4_max_inv = 1.0f / fp4_max;\n        float global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n\n        float global_decode_scale = 1.0f / global_encode_scale;\n        float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n        auto sfa_converter = cutlass::NumericConverter<TSFA, ElementAccumulator>{};\n        do {\n          CUTLASS_PRAGMA_NO_UNROLL\n          for (int k_tile = 0;\n               k_tile < K_TILE_MAX && k_tile + scheduler.tile_n_base() < scheduler.tiles_n();) {\n            int global_tile_n_offset = (scheduler.tile_n_base() + k_tile) * size<1>(epilogue_tiler);\n\n            int cur_group_idx = GetGroupIdx(&args, global_tile_n_offset);\n            if (cur_group_idx != group_idx) {\n              group_idx = cur_group_idx;\n              a_global_amax_val = shared_storage.global_a_amax[group_idx];\n              // Update group quantization parameters/scaling\n              global_encode_scale = a_global_amax_val > 0.0f\n                                        ? cutlass::minimum_with_nan_propagation<float>{}(\n                                              (fp8_max * fp4_max) / a_global_amax_val,\n                                              cutlass::platform::numeric_limits<float>::max())\n                                        : 1.0f;\n              global_decode_scale = 1.0f / global_encode_scale;\n              global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            }\n\n            auto tQAgSFA_mn =\n                tQAgSFA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto tQAgQA_mn = tQAgQA(_, _, _, scheduler.tile_m(), scheduler.tile_n_base() + k_tile);\n            auto barrier_token = mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state);\n            mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n            copy(tiled_s2r, tQAsA(_, _, _, mainloop_pipe_consumer_state.index()), tQArA);\n            cutlass::arch::fence_view_async_shared();\n            mainloop_pipeline.consumer_release(mainloop_pipe_consumer_state);\n            ++mainloop_pipe_consumer_state;\n            ++k_tile;\n\n            // static int constexpr NumVecs = size(tQArA) / VectorSize;\n            cutlass::maximum_absolute_value_reduction<\n                cutlass::Array<ElementAccumulator, VectorSize>, true>\n                amax_reduction;\n            auto compute_frgs = reinterpret_cast<cutlass::Array<TA, VectorSize> *>(tQArA.data());\n            auto output_frgs = reinterpret_cast<cutlass::Array<TQA, VectorSize> *>(\n                raw_pointer_cast(tQArQA.data()));\n            Tensor amax =\n                make_tensor<ElementAccumulator>(prepend(take<1, rank(tQArA)>(tQArA.shape()), _1{}));\n            Tensor pvscales = make_tensor_like<ElementAccumulator>(amax);\n            transformer_engine::curanddx::detail::philox4x32_native_state<\n                NVTE_BUILD_NUM_PHILOX_ROUNDS>\n                rng;\n            if constexpr (kEnableStochasticRounding) {\n              const size_t rng_sequence = global_thread_idx + k_tile * 512 +\n                                          scheduler.get_linear_tile_idx() * K_TILE_MAX * 512 +\n                                          tiles_in_m * tiles_in_n * K_TILE_MAX * 512;\n              rng.init(rng_seed, rng_sequence, rng_offset);\n            }\n            CUTLASS_PRAGMA_UNROLL\n            for (int v = 0; v < size<1>(group_modes<1, rank(tQArA)>(tQArA)); v++) {\n              auto amax_view = group_modes<1, rank(amax)>(amax);\n              auto pvscales_view = group_modes<1, rank(pvscales)>(pvscales);\n              auto compute_frgs_up =\n                  cutlass::NumericArrayConverter<ElementAccumulator, TA, VectorSize>{}(\n                      compute_frgs[v]);\n              amax_view(_0{}, v) = amax_reduction(ElementAccumulator(0), compute_frgs_up);\n              pvscales_view(_0{}, v) = cutlass::multiplies<ElementAccumulator>{}(\n                  amax_view(_0{}, v), global_encode_scale_multiplier);\n              filter(tQArSFA)(v) = sfa_converter(pvscales_view(_0{}, v));\n              auto qpvscale_ups =\n                  cutlass::NumericConverter<ElementAccumulator, TSFA>{}(filter(tQArSFA)(v));\n              auto qpvscale_scaled =\n                  cutlass::multiplies<ElementAccumulator>{}(qpvscale_ups, global_decode_scale);\n              ElementAccumulator acc_scales;\n              if constexpr (kUseFastMath) {\n                // Fast math: compute approximate reciprocal\n                acc_scales = cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(\n                    qpvscale_scaled);\n              } else {\n                // Accurate math: compute reciprocal with division\n                acc_scales = cutlass::divides<ElementAccumulator>{}(1.0, qpvscale_scaled);\n              }\n              auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                  acc_scales, cutlass::platform::numeric_limits<ElementAccumulator>::max());\n              uint4 random_uint4 = uint4{0, 0, 0, 0};\n              if constexpr (kEnableStochasticRounding) {\n                random_uint4 = rng.generate4();\n                output_frgs[v] = StochasticNumericConverter(\n                    cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                        compute_frgs_up, acc_scale),\n                    *reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n              } else {\n                output_frgs[v] =\n                    cutlass::NumericArrayConverter<TQA, ElementAccumulator, VectorSize>{}(\n                        cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                            compute_frgs_up, acc_scale));\n              }\n            }\n            copy(tiled_r2g_QA, tQArQA, tQAgQA_mn);\n            copy(tiled_r2g_SFA, filter(tQArSFA), filter(tQAgSFA_mn));\n          }\n          // scheduler.advance();\n          scheduler.fetch_next_work(sched_pipeline, sched_pipeline_consumer_state);\n          ++sched_pipeline_consumer_state;\n          scheduler.update_work_tile_info();\n        } while (scheduler.is_valid());\n      }\n\n    } else {\n      cutlass::arch::warpgroup_reg_dealloc<32>();\n    }\n  }  // sm100 compile guard end\n}  // NOLINT(readability/fn_size)\n\ntemplate <bool kEnableStochasticRounding, bool kEnableRHTColQuant, bool kEnableRowQuant,\n          bool kEnableSwizzleSFOutput, class TA, class TB, class TQA, class TSFA, class TD = TQA,\n          class TSFD = TSFA, bool kUseFastMath = false>\nvoid group_row_col_rht_gemm_ntt_w_sfc(int packed_sequence_length, int hidden_size, TA const *A,\n                                      TB const *B, TQA *QA, TSFA *SFA,\n                                      MultiAmaxHadamardCastFusionArgs &args,\n                                      const size_t *rng_state, uint32_t *tile_scheduler_workspace,\n                                      uint32_t sm_count, hipStream_t stream,\n                                      int k_tile_size = 1024) {\n  using namespace cute;\n  static int constexpr SFVecSize = 16;\n  static int constexpr RhtTensorSize = 16;\n\n  static_assert(RhtTensorSize == 16, \"RhtTensorSize must be 16\");\n  using LinearSFALayout = decltype(make_layout(make_shape(make_shape(Int<SFVecSize>{}, 0), 0),\n                                               make_stride(make_stride(_0{}, _1{}), 0)));\n  using LinearSFDLayout = decltype(make_layout(make_shape(0, make_shape(Int<SFVecSize>{}, 0)),\n                                               make_stride(0, make_stride(_0{}, _1{}))));\n\n  using SwizzledSFALayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::MN>::SfAtom;\n  using SwizzledSFDLayoutAtom =\n      cutlass::detail::Sm1xxBlockScaledOutputConfig<SFVecSize, UMMA::Major::K>::SfAtom;\n  using SwizzledSFALayout = decltype(tile_to_shape(\n      SwizzledSFALayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{}));\n  using SwizzledSFDLayout = decltype(tile_to_shape(\n      SwizzledSFDLayoutAtom{}, make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{}));\n\n  using SFALayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFALayout, LinearSFALayout>;\n  using SFDLayout = cute::conditional_t<kEnableSwizzleSFOutput, SwizzledSFDLayout, LinearSFDLayout>;\n  SFALayout sfa_layout;\n  SFDLayout sfd_layout;\n\n  if constexpr (kEnableSwizzleSFOutput) {\n    sfa_layout = tile_to_shape(SwizzledSFALayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_1, _2>{});\n    sfd_layout = tile_to_shape(SwizzledSFDLayoutAtom{},\n                               make_shape(hidden_size, packed_sequence_length), Step<_2, _1>{});\n  } else {\n    sfa_layout = make_layout(\n        make_shape(make_shape(Int<SFVecSize>{}, hidden_size / SFVecSize), packed_sequence_length),\n        make_stride(make_stride(_0{}, _1{}), hidden_size / SFVecSize));\n    sfd_layout = make_layout(\n        make_shape(hidden_size, make_shape(Int<SFVecSize>{}, packed_sequence_length / SFVecSize)),\n        make_stride(packed_sequence_length / SFVecSize, make_stride(_0{}, _1{})));\n  }\n\n  // Define shapes (dynamic)\n  auto M = hidden_size;\n  auto N = packed_sequence_length;\n  Tensor tensorA = make_tensor(A, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorB = make_tensor(B, make_shape(RhtTensorSize, RhtTensorSize), LayoutLeft{});\n  Tensor tensorQA = make_tensor(QA, make_shape(hidden_size, packed_sequence_length), LayoutLeft{});\n  Tensor tensorSFA = make_tensor(SFA, sfa_layout);\n\n  // Define strides (from tensors)\n  auto dA = stride(tensorA);    // (dM,dK)\n  auto dB = stride(tensorB);    // (dN,dK)\n  auto dD = LayoutRight{};      // (dM,dN)\n  auto dQA = stride(tensorQA);  // (dM,dK)\n  using ClusterShape = Shape<_1, _1, _1>;\n  auto cluster_shape = ClusterShape{};\n  auto cluster_tile_shape = Shape<_128, Int<RhtTensorSize>, Int<RhtTensorSize>>{};\n  auto cluster_tile_mainloop = Shape<_128, Int<RhtTensorSize>, _128>{};\n\n  // Each mainloop / epilogue loads 128 x 64 tiles while each MMA proceeds with 128 x 16 tiles\n  static int constexpr EpilogueUnrollFactor =\n      size<2>(cluster_tile_mainloop) / size<2>(cluster_tile_shape);\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, size<0>(cluster_tile_shape), size<1>(cluster_tile_shape),\n                           UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cluster_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cluster_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cluster_tile_shape), size<2>(cluster_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cluster_tile_shape),\n      shape_div(shape<0>(cluster_tile_shape), size<0>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cluster_tile_shape),\n      shape_div(shape<1>(cluster_tile_shape), size<1>(cluster_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cluster_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  static uint32_t constexpr TotalTmemRows = 128;\n  static uint32_t constexpr Sm100TmemCapacityColumns = 512;\n  static uint32_t constexpr TotalTmem = TotalTmemRows * Sm100TmemCapacityColumns;\n  static uint32_t constexpr AccumulatorPipelineStageCount =\n      TotalTmem / (cute::size<0>(cluster_tile_shape) * cute::size<1>(cluster_tile_shape));\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int SchedulerPipelineStageCount = 4;\n  static int constexpr MainloopPipelineBytes = sizeof(\n      typename cutlass::detail::CustomizedPipelineTmaUmmaAsync<1, Shape<_1, _1, _1>,\n                                                               Shape<_1, _1, _1>>::SharedStorage);\n\n  static int constexpr SchedulerWorkspaceBytes = sizeof(int) * SchedulerPipelineStageCount;\n  static int constexpr SchedulerThrottlePipelineBytes =\n      sizeof(typename cutlass::PipelineAsync<SchedulerPipelineStageCount>::SharedStorage);\n  static int constexpr SchedulerPipelineBytes =\n      sizeof(typename cutlass::PipelineCLCFetchAsync<SchedulerPipelineStageCount,\n                                                     ClusterShape>::SharedStorage);\n\n  static int constexpr TmemDeallocBytes = sizeof(cutlass::arch::ClusterBarrier);\n  static int constexpr BTensorBytes = cute::size(mma_shape_B) * sizeof(TB);\n  static int constexpr AccPipelineBytes = sizeof(\n      typename cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / EpilogueUnrollFactor,\n                                          Shape<_1, _1, _1>>::SharedStorage);\n  static int constexpr TmemBasePtrsBytes = sizeof(uint32_t);\n  static int constexpr kBlackwellSmemSize = 232448;  // 232KB in bytes\n  static int constexpr kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + MainloopPipelineBytes;\n  static int constexpr kReservedBytes = SchedulerWorkspaceBytes + SchedulerThrottlePipelineBytes +\n                                        SchedulerPipelineBytes + TmemBasePtrsBytes +\n                                        TmemDeallocBytes + BTensorBytes +\n                                        AccPipelineBytes;  // Reserve for barriers and other uses\n  static int constexpr kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{}, append(mma_shape_A, sP),\n                                    Step<_2, _1, _3>{});  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, _1{}));  // (MMA,MMA_N,MMA_K, _1)\n  auto sD = Layout<_1>{};                                        // XXX Dummy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cluster_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  assert(M % size<0>(cluster_tile_shape) == 0);\n  assert(N % size<1>(cluster_tile_shape) == 0);\n\n  dim3 dimBlock(512);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(sm_count, 1, 1);\n\n  int smem_size = sizeof(\n      SharedStorage<TA, TB, decltype(sA), decltype(sB), ClusterShape, AccumulatorPipelineStageCount,\n                    EpilogueUnrollFactor, SchedulerPipelineStageCount>);\n\n  auto *kernel_ptr = &group_row_col_rht_gemm_device<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cluster_shape),\n      decltype(cluster_tile_shape), TA, decltype(dA), decltype(sA), decltype(tma_load_a), TB,\n      decltype(dB), decltype(sB), decltype(tma_load_b), TD, decltype(dD), decltype(sD), TSFD,\n      decltype(sfd_layout), TQA, decltype(dQA), TSFA, decltype(sfa_layout), decltype(mma),\n      AccumulatorPipelineStageCount, SchedulerPipelineStageCount, kEnableStochasticRounding,\n      kEnableRHTColQuant, kEnableRowQuant, kEnableSwizzleSFOutput, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      hipFuncSetAttribute(reinterpret_cast<const void*>(*kernel_ptr), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  // Set workspace and set to zero\n  NVTE_CHECK_CUDA(hipMemsetAsync(reinterpret_cast<void *>(tile_scheduler_workspace), 0,\n                                  sizeof(uint32_t), stream));\n\n  // Launch kernel\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size, stream};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(\n      params, (void const *)kernel_ptr, M, N, k_tile_size, cluster_shape, cluster_tile_shape, A, dA,\n      sA, tma_load_a, B, dB, sB, tma_load_b, QA, dQA, SFA, sfa_layout, args,\n      tile_scheduler_workspace, mma, rng_state);\n  NVTE_CHECK_CUDA(hipGetLastError());\n  NVTE_CHECK(status == cutlass::Status::kSuccess, \"Kernel launch failed.\");\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion(const Tensor &input_, std::vector<Tensor *> &output_list,\n                                          const size_t *split_sections, size_t num_tensors,\n                                          const Tensor &hadamard_matrix_,\n                                          QuantizationConfig &quant_config, Tensor &quant_workspace,\n                                          hipStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::MultiAmaxHadamardCastFusionArgs;\n\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  NVTE_CHECK(output_list.size() == num_tensors,\n             \"Number of output tensors should match number of tensors.\");\n\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // construct the multi-tensor args\n  MultiAmaxHadamardCastFusionArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  bool all_has_row_quant = true;\n  bool all_has_col_quant = true;\n  void *rowwise_data_base_ptr = nullptr;\n  void *rowwise_scale_inv_base_ptr = nullptr;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    NVTE_CHECK(split_sections[i] % 128 == 0, \"component \", i,\n               \" of split_sections should be 128 multiple\");\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    bool has_row_quant = output_list[i]->data.dptr != nullptr;\n    bool has_col_quant = output_list[i]->columnwise_data.dptr != nullptr;\n    all_has_row_quant = all_has_row_quant && has_row_quant;\n    all_has_col_quant = all_has_col_quant && has_col_quant;\n    // sanity check, the two bool flags cannot be both false\n    NVTE_CHECK(has_row_quant || has_col_quant,\n               \"At least one of the output tensors must have row or column quant.\");\n    void *amax_rowwise_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->amax.dptr) : nullptr;\n    void *amax_colwise_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_amax.dptr) : nullptr;\n    void *rowwise_data_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->data.dptr) : nullptr;\n    void *rowwise_scale_inv_ptr =\n        has_row_quant ? reinterpret_cast<void *>(output_list[i]->scale_inv.dptr) : nullptr;\n    if (all_has_row_quant &&\n        (rowwise_data_base_ptr == nullptr || rowwise_scale_inv_base_ptr == nullptr)) {\n      rowwise_data_base_ptr = rowwise_data_ptr;\n      rowwise_scale_inv_base_ptr = rowwise_scale_inv_ptr;\n    }\n    void *output_colwise_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_data.dptr) : nullptr;\n    void *output_colwise_scale_inv_ptr =\n        has_col_quant ? reinterpret_cast<void *>(output_list[i]->columnwise_scale_inv.dptr)\n                      : nullptr;\n    kernel_args.global_a_amax_list[kernel_args.num_tensors] = amax_rowwise_ptr;\n    kernel_args.global_d_amax_list[kernel_args.num_tensors] = amax_colwise_ptr;\n    kernel_args.output_colwise_list[kernel_args.num_tensors] = output_colwise_ptr;\n    kernel_args.output_colwise_scale_inv_list[kernel_args.num_tensors] =\n        output_colwise_scale_inv_ptr;\n    kernel_args.split_sections[kernel_args.num_tensors] = split_sections[i];\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (use_stochastic_rounding) {\n    NVTE_CHECK(quant_config.rng_state != nullptr,\n               \"Enabled stochastic rounding without providing RNG state\");\n    const Tensor &rng_state_tensor = *convertNVTETensorCheck(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  uint32_t *tile_scheduler_workspace = nullptr;\n  NVTE_CHECK(quant_workspace.data.dptr != nullptr, \"Quantization workspace must be provided.\");\n  NVTE_CHECK(quant_workspace.data.buffer_size_bytes() >= sizeof(uint32_t),\n             \"Quantization workspace must be at least 4 bytes.\");\n  tile_scheduler_workspace = reinterpret_cast<uint32_t *>(quant_workspace.data.dptr);\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TD = cutlass::float_e2m1_t;\n  using TSFD = cutlass::float_ue4m3_t;\n  using TQA = TD;\n  using TSFA = TSFD;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  const bool use_swizzle_sf_output = false;\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kEnableStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          all_has_col_quant, kEnableRhtColQuant,\n          TRANSFORMER_ENGINE_SWITCH_CONDITION(\n              all_has_row_quant, kEnableRowQuant,\n              TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                  use_swizzle_sf_output, kEnableSwizzleSFOutput,\n                  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n                      quant_config.use_fast_math, kUseFastMath,\n\n                      if constexpr (kEnableRhtColQuant || kEnableRowQuant) {\n                        detail::group_row_col_rht_gemm_ntt_w_sfc<\n                            kEnableStochasticRounding, kEnableRhtColQuant, kEnableRowQuant,\n                            kEnableSwizzleSFOutput, TA, TB, TQA, TSFA, TD, TSFD, kUseFastMath>(\n                            /*packed_sequence_length=*/m, /*hidden_size=*/n,\n                            /*A=*/reinterpret_cast<TA const *>(input.dptr),\n                            /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n                            /*QA=*/reinterpret_cast<TQA *>(rowwise_data_base_ptr),\n                            /*SFA=*/reinterpret_cast<TSFA *>(rowwise_scale_inv_base_ptr),\n                            /*args=*/kernel_args,\n                            /*rng_state=*/rng_state,\n                            /*tile_scheduler_workspace=*/tile_scheduler_workspace,\n                            /*sm_count=*/sm_count,\n                            /*stream=*/stream, /*k_tile_size=*/k_tile_size);\n                      } else {\n                        NVTE_ERROR(\"Invalid kernel configuration (kEnableRHTColQuant=\",\n                                   kEnableRhtColQuant, \", kEnableRowQuant=\", kEnableRowQuant, \").\");\n                      }\n\n                  );););););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion(const NVTETensor input, NVTETensor *outputs,\n                                               const NVTETensor hadamard_matrix,\n                                               const size_t *split_sections,\n                                               const size_t num_tensors,\n                                               const NVTEQuantizationConfig quant_config,\n                                               NVTETensor quant_workspace, hipStream_t stream) {\n  NVTE_API_CALL(nvte_group_hadamard_transform_cast_fusion);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n\n  Tensor *input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor *> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  Tensor *quant_workspace_tensor = convertNVTETensorCheck(quant_workspace);\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion(*input_tensor, output_list, split_sections, num_tensors,\n                                       *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp,\n                                       *quant_workspace_tensor, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/group_row_cast_col_hadamard_transform_cast_fusion.cu"}
{"input": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <cuda.h>\n#include <cudaTypedefs.h>\n#include <cuda_bf16.h>\n#include <cuda_pipeline.h>\n#include <cuda_runtime.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/cuda_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\nusing cute::\n    Tensor;  // Ensure unqualified Tensor refers to cute::Tensor, not transformer_engine::Tensor\n\nusing Stride2D = cute::Stride<int, cute::Int<1>>;\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB, expand 64 if needed\nstruct MultiAmaxHadamardCastFusionArgs {\n  // (output) Amax buffer for pre-RHT amax buffer\n  void *global_amax_list[kMaxTensorsPerKernel];\n  // output C pointers for each tensor\n  void *output_colwise_list[kMaxTensorsPerKernel];\n  // output scale inverse pointers for each tensor\n  void *output_colwise_scale_inv_list[kMaxTensorsPerKernel];\n  // split sections of each tensor of input\n  int split_sections[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n  // stride 2D struct for CUTE\n  Stride2D output_stride2d_list[kMaxTensorsPerKernel];\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\n__device__ __forceinline__ float *GetGlobalAmaxPtrByTensorId(\n    MultiAmaxHadamardCastFusionArgs *kernel_args_ptr, int tensor_id) {\n  // directly returns the global amax pointer by tensor id\n  if (tensor_id < 0 || tensor_id >= kernel_args_ptr->num_tensors) {\n    return nullptr;\n  }\n  return reinterpret_cast<float *>(kernel_args_ptr->global_amax_list[tensor_id]);\n}\n\n__device__ __forceinline__ int GetTensorId(MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                           int offset) {\n  // Check the kernel args and get the corresponding id\n  const int num_tensors = kernel_args_ptr->num_tensors;\n  if (offset >= kernel_args_ptr->split_sections_range[num_tensors]) {\n    return num_tensors - 1;\n  }\n  int tensor_id = 0;\n  while (kernel_args_ptr->split_sections_range[tensor_id + 1] <= offset) {\n    ++tensor_id;\n  }\n  return tensor_id;\n}\n\n// calculate the global encode scale factor for a given global amax.\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float kFP8E4M3Max = 448.0f;\n  constexpr float kFP4E2M1Max = 6.0f;\n  // If scale is infinity, return max value of float32\n  float global_encode_scale = cutlass::minimum_with_nan_propagation<float>{}(\n      kFP8E4M3Max * kFP4E2M1Max / global_amax, cutlass::platform::numeric_limits<float>::max());\n  // If global amax is 0 or infinity, return 1\n  return (global_amax == 0.f || global_encode_scale == 0.f) ? 1.f : global_encode_scale;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout>\nstruct SharedStorage {\n  static constexpr int AccumulatorPipelineStageCount = 16;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::PipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>, AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  uint32_t tmem_base_ptr;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n};\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const *rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class MShape, class NShape, class KShape, class ClusterTileShape, class TA, class AStride,\n          class ASmemLayout, class TmaLoadA, class TB, class BStride, class BSmemLayout,\n          class TmaLoadB, class TC, class CStride, class CSmemLayout, class TSFC, class TiledMMA,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\n__global__ static void group_rht_gemm_device(\n    MShape M, NShape N, KShape K, ClusterTileShape cluster_tile, TA const *A, AStride dA,\n    ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a, TB const *B, BStride dB,\n    BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b, CSmemLayout, TiledMMA mma,\n    MultiAmaxHadamardCastFusionArgs kernel_args, const size_t *rng_state) {\n  using namespace cute;\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    using X = Underscore;\n    // static constexpr bool kApplyStochasticRounding = true;\n    using ElementAccumulator = float;\n    static constexpr int K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static constexpr uint32_t kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n\n    static constexpr int kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(16 * 16 * cute::sizeof_bits_v<TB>);\n    static constexpr int AccumulatorPipelineStageCount = 16;\n\n    static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<MainloopPipelineStageCount,\n                                                           Shape<_1, _1, _1>, AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static constexpr int VectorSize = 16;\n    const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n    const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n    // Preconditions\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<CSmemLayout>::value);\n\n    // Represent the full tensors\n    Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, N));\n    Tensor mB = tma_load_b.get_tma_tensor(make_shape(16, 16));\n\n    using TensorC = decltype(make_tensor(subbyte_iterator<TC>(recast_ptr<TC>(nullptr)),  // engine\n                                         make_shape(int{}, int{}),                       // (M, N_i)\n                                         Stride2D{}  // stride (dM, dN)\n                                         ));\n\n    using TensorSFC = decltype(make_tensor(\n        make_gmem_ptr(recast_ptr<TSFC>(nullptr)),\n        make_layout(make_shape(int{},                                   // M\n                               make_shape(make_shape(Int<16>{}, _4{}),  // (16, 4)\n                                          int{})                        // n_tiles = split / 64\n                               ),\n                    make_stride(int{},                                // dM = (split / 16)\n                                make_stride(make_stride(_0{}, _1{}),  // inner (16,4) layout\n                                            _4{})                     // tiles stride\n                                ))));\n\n    auto cluster_shape = Shape<_1, _1, _1>{};\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    const int K_TILE_MAX = min(N, K) / 64;\n    uint32_t tiles_in_m = (M + size<0>(cluster_tile) - 1) / size<0>(cluster_tile);\n    uint32_t tiles_in_n = (N + 64 - 1) / 64;\n    uint32_t linear_tile_idx = blockIdx.x;\n    uint32_t tile_idx_m = linear_tile_idx % tiles_in_m;\n    uint32_t tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n\n    auto mainloop_tiler = Shape<_128, _16, _64>{};\n    auto epilogue_tiler = Shape<_128, _64, _64>{};\n    Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n    Tensor gB_nk =\n        local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n    // Tensor gC_mn = local_tile(mC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n    using TensorGC = decltype(local_tile(std::declval<TensorC>(), decltype(epilogue_tiler){},\n                                         make_coord(_, _, _), Step<_1, _1, X>{}));\n\n    using TensorGSFC = decltype(local_tile(std::declval<TensorSFC>(), decltype(epilogue_tiler){},\n                                           make_coord(_, _, _), Step<_1, _1, X>{}));\n\n    // Allocate SMEM\n    extern __shared__ char shared_memory[];\n    using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n    Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                              sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n    Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                              sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n    //\n    // MMA: Define C accumulators and A/B partitioning\n    //\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n    ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n    Tensor tCgB = thr_mma.partition_B(gB_nk);               // (MMA,MMA_N,MMA_K,k)\n\n    auto mma_epilogue = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, ElementAccumulator, 128, 64,\n                                                            UMMA::Major::MN, UMMA::Major::MN>{},\n                                       Layout<Shape<_1, _1>>{});\n    ThrMMA thr_mma_epilogue = mma_epilogue.get_slice(block_rank_in_cluster);\n\n    using TiledMmaEpilogue = decltype(mma_epilogue);\n    Tensor tCgA = thr_mma.partition_A(gA_mk);\n    // Allocate \"fragments\" -- these are actually umma smem descriptors\n    Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n    Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n    auto acc_shape_mma = partition_shape_C(TiledMMA{}, take<0, 2>(ClusterTileShape{}));\n    auto acc_shape_epilogue = partition_shape_C(TiledMmaEpilogue{}, take<0, 2>(epilogue_tiler));\n\n    auto bulk_tmem_mma =\n        TiledMMA::make_fragment_C(append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{}));\n\n    auto bulk_tmem_epilogue = TiledMmaEpilogue::make_fragment_C(\n        append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / 4>{}));\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        32 + 128, cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    Layout cta_layout_mnk = make_layout(cluster_shape);\n    Layout cta_layout_vmnk =\n        tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n    auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n    auto [tAgA, tAsA] =\n        tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                      group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n    auto [tBgB, tBsB] =\n        tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                      group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n    uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n    uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_epilogue_warp = (warp_idx >= 4 && warp_idx <= 7);\n\n    // if (is_epilogue_warp && elect_one_sync()) {\n    //   // prefetch to make the global amax in cache\n    //   for (size_t i = 0; i < kernel_args.num_tensors; ++i) {\n    //     cute::prefetch(raw_pointer_cast(kernel_args.global_amax_list[i]));\n    //   }\n    // }\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * 128;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(shared_storage.accumulator,\n                                             accumulator_pipeline_params, cluster_shape,\n                                             cute::true_type{},   // Perform barrier init\n                                             cute::true_type{});  // Delay mask calculation\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n    using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n    if (is_dma_warp) {\n      if (elect_one_sync()) {\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                            kTmaRhtTensorTransactionBytes);\n        copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n             tBsB(_, 0));\n      }\n\n      do {\n        bool is_first_wave = linear_tile_idx == blockIdx.x;\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, tile_idx_m, _);\n        int k_tile = 0;\n        auto barrier_token =\n            mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n\n        CUTE_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n) {\n          int k_tile_idx_n = tile_idx_n + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state, barrier_token);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          barrier_token =\n              mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n      tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                              &shared_storage.tmem_base_ptr);\n      __syncwarp();\n      tmem_allocation_result_barrier.arrive();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_mma.data() = tmem_base_ptr;\n\n      cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n        auto barrier_token =\n            mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n        CUTE_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n;) {\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          int read_stage = mainloop_pipe_consumer_state.index();\n          auto tCrA_mk = tCrA(_, _, _, read_stage);\n          auto tCrB_nk = tCrB(_, _, 0, 0);\n          CUTE_UNROLL\n          for (int k_block = 0; k_block < size<2>(tCrA) / 4; ++k_block) {\n            accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n            CUTE_UNROLL\n            for (int i = 0; i < 4; i++) {\n              auto accumulators =\n                  bulk_tmem_mma(_, _, _, accumulator_pipe_producer_state.index() * 4 + i);\n              gemm(mma, tCrA_mk(_, _, k_block * 4 + i), tCrB_nk, accumulators);\n            }\n\n            accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n            ++accumulator_pipe_producer_state;\n          }\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n        }\n\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n      tmem_allocator.release_allocation_lock();\n      accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n      tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n    } else if (is_epilogue_warp) {\n      static constexpr int FragmentSize = 256 / sizeof_bits_v<TC>;\n\n      tmem_allocation_result_barrier.arrive_and_wait();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_epilogue.data() = tmem_base_ptr;\n      int thread_idx = threadIdx.x % 128;\n\n      auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n      auto tiled_r2g =\n          make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TC>{}, tiled_t2r);\n      auto thr_t2r = tiled_t2r.get_slice(thread_idx);\n      auto thr_r2g = tiled_r2g.get_slice(thread_idx);\n\n      // NVFP4 non-E8 recipe constants and global scales\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp4_max_inv = 1.0f / fp4_max;\n\n      // get global amax pointer\n      int tensor_id = GetTensorId(&kernel_args, tile_idx_n * 64);\n      float *global_amax_ptr = GetGlobalAmaxPtrByTensorId(&kernel_args, tensor_id);\n\n      TC *cur_output_colwise_ptr =\n          reinterpret_cast<TC *>(kernel_args.output_colwise_list[tensor_id]);\n      TSFC *cur_output_colwise_scale_inv_ptr =\n          reinterpret_cast<TSFC *>(kernel_args.output_colwise_scale_inv_list[tensor_id]);\n      int cur_output_colwise_n = kernel_args.split_sections[tensor_id];\n\n      TensorC cur_mC = cute::make_tensor(\n          cute::subbyte_iterator<TC>(cur_output_colwise_ptr),\n          cute::make_shape(static_cast<int>(M), cur_output_colwise_n),  // (M, N_i)\n          kernel_args.output_stride2d_list[tensor_id]);\n\n      auto cur_sfc_shape =\n          make_shape(M, make_shape(make_shape(Int<16>{}, _4{}), cur_output_colwise_n / 64));\n\n      auto cur_sfc_stride =\n          make_stride(cur_output_colwise_n / 16, make_stride(make_stride(_0{}, _1{}), _4{}));\n\n      TensorSFC cur_mSFC = cute::make_tensor(make_gmem_ptr(cur_output_colwise_scale_inv_ptr),\n                                             make_layout(cur_sfc_shape, cur_sfc_stride));\n\n      TensorGC cur_gC_mn = local_tile(\n          cur_mC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N)\n      );\n\n      TensorGSFC cur_gSFC_mn = local_tile(\n          cur_mSFC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N-like)\n      );\n\n      Tensor tCgC = thr_mma_epilogue.partition_C(cur_gC_mn);\n\n      float global_amax_val = *global_amax_ptr;\n      float global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n      float global_decode_scale = 1.0f / global_encode_scale;\n\n      auto sfd_converter = cutlass::NumericConverter<TSFC, float>{};\n\n      do {\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; ++k_tile) {\n          // get the starting index of current k-tile in global tensor, to query the correct global amax\n          int cur_k_tile_global_elem_idx = (tile_idx_n + k_tile) * 64;\n          int new_tensor_id = GetTensorId(&kernel_args, cur_k_tile_global_elem_idx);\n          // float* new_global_amax_ptr = GetGlobalAmaxPtr(&kernel_args, cur_k_tile_global_elem_idx);\n          global_amax_ptr = GetGlobalAmaxPtrByTensorId(&kernel_args, new_tensor_id);\n          // update the scaling factors when it's no longer the same amax pointer\n          // TODO(zhongbo): the math operations are very expensive\n          // since the kernel is persistent, we can have a cache for all the possible scaling factors\n          if (tensor_id != new_tensor_id) {\n            global_amax_val = *global_amax_ptr;\n            global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n            global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            global_decode_scale = 1.0f / global_encode_scale;\n            tensor_id = new_tensor_id;\n            // went through the cute operations to update the local tensors\n            cur_output_colwise_ptr =\n                reinterpret_cast<TC *>(kernel_args.output_colwise_list[tensor_id]);\n            cur_output_colwise_scale_inv_ptr =\n                reinterpret_cast<TSFC *>(kernel_args.output_colwise_scale_inv_list[tensor_id]);\n            cur_output_colwise_n = kernel_args.split_sections[tensor_id];\n\n            cur_mC = cute::make_tensor(\n                cute::subbyte_iterator<TC>(cur_output_colwise_ptr),\n                cute::make_shape(static_cast<int>(M), cur_output_colwise_n),  // (M, N_i)\n                kernel_args.output_stride2d_list[tensor_id]);\n\n            cur_sfc_shape =\n                make_shape(M, make_shape(make_shape(Int<16>{}, _4{}), cur_output_colwise_n / 64));\n\n            cur_sfc_stride =\n                make_stride(cur_output_colwise_n / 16, make_stride(make_stride(_0{}, _1{}), _4{}));\n\n            cur_mSFC = cute::make_tensor(make_gmem_ptr(cur_output_colwise_scale_inv_ptr),\n                                         make_layout(cur_sfc_shape, cur_sfc_stride));\n\n            cur_gC_mn = local_tile(\n                cur_mC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N)\n            );\n\n            cur_gSFC_mn = local_tile(cur_mSFC, epilogue_tiler, make_coord(_, _, _),\n                                     Step<_1, _1, X>{}  // (BLK_M, BLK_N-like)\n            );\n\n            tCgC = thr_mma_epilogue.partition_C(cur_gC_mn);\n          }\n          // maybe udpated to the new tensor id\n          int tensor_start_elem = kernel_args.split_sections_range[tensor_id];\n          int local_tile_idx_n = (cur_k_tile_global_elem_idx - tensor_start_elem) / 64;\n\n          Tensor tCgC_mn = tCgC(_, _, _, tile_idx_m, local_tile_idx_n);\n          Tensor tCgSFC_mn = cur_gSFC_mn(_, _, tile_idx_m, local_tile_idx_n);\n\n          accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n          auto tCtC = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n          Tensor tDtC = thr_t2r.partition_S(tCtC);     // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          Tensor tDgC = thr_t2r.partition_D(tCgC_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n          Tensor tTR_rAcc =\n              make_tensor<ElementAccumulator>(shape(tDgC));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          Tensor tDrC = make_tensor<TC>(shape(tDgC));\n          Tensor tTR_rAcc_frag =\n              recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n          Tensor tDrC_frag = recast<cutlass::Array<TC, FragmentSize>>(coalesce(tDrC));\n\n          Tensor src = thr_r2g.retile_S(tDrC);\n          Tensor dst = thr_r2g.retile_D(tDgC);\n\n          Tensor tCgSFC = make_tensor(\n              tCgSFC_mn.data(), make_layout(make_shape(shape(tCgSFC_mn), Int<1>{}, Int<1>{}),\n                                            make_stride(stride(tCgSFC_mn), Int<0>{}, Int<0>{})));\n\n          Tensor tDgSFC = filter(thr_t2r.partition_D(tCgSFC));\n          Tensor tDrSFC = make_tensor<TSFC>(shape(tDgSFC));\n\n          static constexpr int NumVecs = size(tDgC) / VectorSize;\n          Tensor tC_rRowSFD_frg = recast<cutlass::Array<TSFC, NumVecs>>(tDrSFC);\n\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>,\n                                                    true>\n              amax_reduction;\n          cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n          cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n          // TMEM_LOAD\n          copy(tiled_t2r, tDtC, tTR_rAcc);\n          cutlass::arch::fence_view_async_tmem_load();\n\n          accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n\n          ++accumulator_pipe_consumer_state;\n\n          if constexpr (!kUseFastMath) {\n            // Downcast to BF16 for bit-wise compatibility with unfused\n            // kernels\n            auto convert_accum_to_bf16 =\n                cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                               FragmentSize>{};\n            auto convert_bf16_to_accum =\n                cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                               FragmentSize>{};\n            tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n          }\n\n          auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n              tTR_rAcc_frag.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array<TC, VectorSize> *>(tDrC_frag.data());\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n          }\n\n          pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n              vec_maxs, global_encode_scale_multiplier);\n          auto pvscales_cvted =\n              cutlass::NumericArrayConverter<TSFC, ElementAccumulator, NumVecs>{}(pvscales);\n\n          tC_rRowSFD_frg(_0{}) = pvscales_cvted;\n          auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFC, NumVecs>{}(\n              tC_rRowSFD_frg(_0{}));\n          auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n              qpvscale_ups, global_decode_scale);\n          cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n          if constexpr (kUseFastMath) {\n            // Fast math: compute approximate reciprocal\n            acc_scales =\n                cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n          } else {\n            // Accurate math: compute reciprocal with division\n            acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                1.0, qpvscale_scaled);\n          }\n\n          // Initialize RNG for tile\n          const size_t rng_sequence =\n              thread_idx + k_tile * 256 + linear_tile_idx * K_TILE_MAX * 256;\n\n          transformer_engine::curanddx::detail::philox4x32_native_state<\n              NVTE_BUILD_NUM_PHILOX_ROUNDS>\n              rng;\n          rng.init(rng_seed, rng_sequence, rng_offset);\n          uint4 random_uint4 = uint4{0, 0, 0, 0};\n\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            // auto acc_scale = acc_scales[v];\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(\n                  cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                      compute_frgs[v], acc_scale),\n                  reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TC, ElementAccumulator, VectorSize>{}(\n                  cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                      compute_frgs[v], acc_scale));\n            }\n          }\n\n          copy(tiled_r2g, src, dst);\n\n          // copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrC, tDgC);\n\n          copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFC, tDgSFC);\n        }\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    }\n  }\n}\n\n// this function computes RHT-GEMM for\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// C: m x n: row-major\n// SFC: m x (n/16): row-major\ntemplate <typename TA, typename TB, typename TC, typename TSFC,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid group_rht_gemm_ntt_w_sfc(int m, int n, TA const *A, TB const *B,\n                              MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                              const size_t *rng_state, uint32_t sm_count, cudaStream_t stream,\n                              int k_tile_size = 2048) {\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = static_cast<int>(m);\n  auto N = static_cast<int>(n);\n\n  // Define strides (mixed)\n  auto dA = make_stride(Int<1>{}, m);   // (dM,dK)\n  auto dB = make_stride(Int<1>{}, 16);  // (dN,dK)\n  for (size_t i = 0; i < kernel_args_ptr->num_tensors; ++i) {\n    kernel_args_ptr->output_stride2d_list[i] =\n        make_stride(kernel_args_ptr->split_sections[i], Int<1>{});\n  }\n\n  auto cga_shape = Shape<_1, _1, _1>{};\n  auto cga_tile_shape = Shape<_128, _16, _16>{};\n  auto cluster_tile_mainloop = Shape<_128, _16, _64>{};\n\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, 128, 16, UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // MMA in CGA Layout XXX: Need to generalize synchro? {$nv-release-never}\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cga_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cga_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cga_tile_shape), size<2>(cga_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cga_tile_shape),\n      shape_div(shape<0>(cga_tile_shape), size<0>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cga_tile_shape),\n      shape_div(shape<1>(cga_tile_shape), size<1>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cga_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int kBlackwellSmemSize = 232448;  // 232KB in bytes\n  constexpr int kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + cute::size(mma_shape_B) * sizeof(TB);\n  constexpr int kReservedBytes = 256;  // Reserve for barriers and other uses\n  constexpr int kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{},\n                                    append(mma_shape_A, sP));  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, sP));  // (MMA,MMA_N,MMA_K,PIPE)\n  auto sC = Layout<_1>{};                                      // XXX Dummy\n\n  // Create GMEM tensors\n  Tensor tensorA = make_tensor(A, make_layout(make_shape(M, N), dA));    // (M,N)\n  Tensor tensorB = make_tensor(B, make_layout(make_shape(16, 16), dB));  // (16,16)\n\n  // Create the TiledCopy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cga_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  NVTE_CHECK(M % size<0>(cga_tile_shape) == 0, \"Inner dimension must be divisible by \",\n             static_cast<size_t>(size<0>(cga_tile_shape)), \" but got \", M, \".\");\n  NVTE_CHECK(N % (4 * size<1>(cga_tile_shape)) == 0, \"Outer dimension must be divisible by \",\n             4 * static_cast<size_t>(size<1>(cga_tile_shape)), \" but got \", N, \".\");\n\n  uint32_t tiles = size(ceil_div(M, get<0>(cga_tile_shape))) * size(ceil_div(N, k_tile_size));\n\n  tiles = (tiles < sm_count) ? tiles : sm_count;\n\n  dim3 dimBlock(256);\n  dim3 dimCluster(size<0>(cga_shape), size<1>(cga_shape), size<2>(cga_shape));\n  dim3 dimGrid(tiles, 1, 1);\n\n  int smem_size = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n  auto *kernel_ptr = &group_rht_gemm_device<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cga_tile_shape), TA, decltype(dA),\n      decltype(sA), decltype(tma_load_a), TB, decltype(dB), decltype(sB), decltype(tma_load_b), TC,\n      Stride2D, decltype(sC), TSFC, decltype(mma), kEnableStochasticRounding, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      cudaFuncSetAttribute(*kernel_ptr, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  (*kernel_ptr)<<<dimGrid, dimBlock, smem_size, stream>>>(M, N, k_tile_size, cga_tile_shape, A, dA,\n                                                          sA, tma_load_a, B, dB, sB, tma_load_b, sC,\n                                                          mma, *kernel_args_ptr, rng_state);\n  NVTE_CHECK_CUDA(cudaGetLastError());\n}\n\n// this function is used to wrap the group_rht_gemm_ntt_w_sfc function\n// to transpose the input tensor A\ntemplate <typename TA, typename TB, typename TC, typename TSFC,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid group_rht_gemm_ttt_wrapper(int m, int n, TA const *A, TB const *B,\n                                MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                const size_t *rng_state, uint32_t sm_count, cudaStream_t stream,\n                                int k_tile_size = 1024) {\n  // in addition to transpose the input tensor A\n  // we also need to reshape m, n to at best\n  // ultilize as many SMs as possible while keeping\n  // a relatively large contiguous dimension.\n  // for example, after swapping m, n for transpose purposes,\n  // the input / output tensor shapes for RHT-GEMM are:\n  // A: n x m: col-major\n  // B: 16 x 16: row-major\n  // C: n x m: row-major\n  // SFC: n x (m/16): row-major\n  group_rht_gemm_ntt_w_sfc<TA, TB, TC, TSFC, kEnableStochasticRounding, kUseFastMath>(\n      n, m, A, B, kernel_args_ptr, rng_state, sm_count, stream, k_tile_size);\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion_columnwise(\n    const Tensor &input_, std::vector<Tensor *> &output_list, const size_t *split_sections,\n    size_t num_tensors, const Tensor &hadamard_matrix_, QuantizationConfig &quant_config,\n    cudaStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion_columnwise);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::MultiAmaxHadamardCastFusionArgs;\n\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  NVTE_CHECK(output_list.size() == num_tensors,\n             \"Number of output tensors should match number of tensors.\");\n\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // construct the multi-tensor args\n  MultiAmaxHadamardCastFusionArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    NVTE_CHECK(split_sections[i] % 64 == 0, \"component \", i,\n               \" of split_sections should be 64 multiple\");\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    kernel_args.global_amax_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->amax.dptr);\n    // TODO(zhongbo): should we change API assumption to use columnwise_data instead of data?\n    kernel_args.output_colwise_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->data.dptr);\n    kernel_args.output_colwise_scale_inv_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->scale_inv.dptr);\n    kernel_args.split_sections[kernel_args.num_tensors] = split_sections[i];\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TC = cutlass::float_e2m1_t;\n  using TSFC = cutlass::float_ue4m3_t;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  if (m == 8192 && n == 5120) {\n    k_tile_size = 512;\n  } else if (m == 8192 && n == 10240) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 2560) {\n    k_tile_size = 1280;\n  } else if (m == 8192 && n == 11328) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 512) {\n    k_tile_size = 256;\n  } else if (m == 8192 && n == 3584) {\n    k_tile_size = 512;\n  } else if (m == 11328 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 5120 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m == 10240 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 2560 && n == 8192) {\n    k_tile_size = 1280;\n  } else if (m == 512 && n == 8192) {\n    k_tile_size = 256;\n  } else if (m == 3584 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m < 1024 || n < 1024) {\n    k_tile_size = 512;\n  }\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kUseStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          quant_config.use_fast_math, kUseFastMath,\n          detail::group_rht_gemm_ttt_wrapper<TA, TB, TC, TSFC, kUseStochasticRounding,\n                                             kUseFastMath>(\n              /*m=*/m, /*n=*/n, /*A=*/reinterpret_cast<TA const *>(input.dptr),\n              /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n              /*kernel_args_ptr=*/&kernel_args, /*rng_state=*/rng_state, /*sm_count=*/sm_count,\n              /*stream=*/stream, /*k_tile_size=*/k_tile_size);););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion_columnwise(\n    const NVTETensor input, NVTETensor *outputs, const NVTETensor hadamard_matrix,\n    const size_t *split_sections, const size_t num_tensors,\n    const NVTEQuantizationConfig quant_config, cudaStream_t stream) {\n  NVTE_API_CALL(nvte_multi_hadamard_transform_cast_fusion_columnwise);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n\n  Tensor *input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor *> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion_columnwise(\n      *input_tensor, output_list, split_sections, num_tensors,\n      *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp, stream);\n}\n", "output": "/*************************************************************************\n * Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * See LICENSE for license information.\n ************************************************************************/\n\n#include <hip/hip_runtime.h>\n#include <cudaTypedefs.h>\n#include <hip/hip_bf16.h>\n#include <cuda_pipeline.h>\n#include <cutlass/arch/barrier.h>\n#include <transformer_engine/hadamard_transform.h>\n\n#include <cuda/barrier>\n#include <cute/algorithm/gemm.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n#include <cute/tensor.hpp>\n\n#include \"common/common.h\"\n#include \"common/util/hip/hip_runtime.h\"\n#include \"common/util/curanddx.hpp\"\n#include \"common/util/ptx.cuh\"\n#include \"common/utils.cuh\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/gemm/collective/builders/sm100_common.inl\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/command_line.h\"\n#include \"cutlass/util/print_error.hpp\"\n\nnamespace transformer_engine {\nnamespace detail {\nnamespace {\n\nusing namespace cute;\nusing cute::\n    Tensor;  // Ensure unqualified Tensor refers to cute::Tensor, not transformer_engine::Tensor\n\nusing Stride2D = cute::Stride<int, cute::Int<1>>;\n\nconstexpr int kMaxTensorsPerKernel = 64;  // Args must be <4 KB, expand 64 if needed\nstruct MultiAmaxHadamardCastFusionArgs {\n  // (output) Amax buffer for pre-RHT amax buffer\n  void *global_amax_list[kMaxTensorsPerKernel];\n  // output C pointers for each tensor\n  void *output_colwise_list[kMaxTensorsPerKernel];\n  // output scale inverse pointers for each tensor\n  void *output_colwise_scale_inv_list[kMaxTensorsPerKernel];\n  // split sections of each tensor of input\n  int split_sections[kMaxTensorsPerKernel];\n  // Prefix sum (with leading zero) of split_sections of each tensor of input\n  int split_sections_range[kMaxTensorsPerKernel + 1];\n  // stride 2D struct for CUTE\n  Stride2D output_stride2d_list[kMaxTensorsPerKernel];\n  // Number of tensors (splits) being processed by kernel\n  int num_tensors;\n};\n\n__device__ __forceinline__ float *GetGlobalAmaxPtrByTensorId(\n    MultiAmaxHadamardCastFusionArgs *kernel_args_ptr, int tensor_id) {\n  // directly returns the global amax pointer by tensor id\n  if (tensor_id < 0 || tensor_id >= kernel_args_ptr->num_tensors) {\n    return nullptr;\n  }\n  return reinterpret_cast<float *>(kernel_args_ptr->global_amax_list[tensor_id]);\n}\n\n__device__ __forceinline__ int GetTensorId(MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                           int offset) {\n  // Check the kernel args and get the corresponding id\n  const int num_tensors = kernel_args_ptr->num_tensors;\n  if (offset >= kernel_args_ptr->split_sections_range[num_tensors]) {\n    return num_tensors - 1;\n  }\n  int tensor_id = 0;\n  while (kernel_args_ptr->split_sections_range[tensor_id + 1] <= offset) {\n    ++tensor_id;\n  }\n  return tensor_id;\n}\n\n// calculate the global encode scale factor for a given global amax.\n__device__ __forceinline__ float ComputeGlobalEncodeScaleFP4(const float global_amax) {\n  constexpr float kFP8E4M3Max = 448.0f;\n  constexpr float kFP4E2M1Max = 6.0f;\n  // If scale is infinity, return max value of float32\n  float global_encode_scale = cutlass::minimum_with_nan_propagation<float>{}(\n      kFP8E4M3Max * kFP4E2M1Max / global_amax, cutlass::platform::numeric_limits<float>::max());\n  // If global amax is 0 or infinity, return 1\n  return (global_amax == 0.f || global_encode_scale == 0.f) ? 1.f : global_encode_scale;\n}\n\ntemplate <class ElementA, class ElementB, class ASmemLayout, class BSmemLayout>\nstruct SharedStorage {\n  static constexpr int AccumulatorPipelineStageCount = 16;\n  using AtomThrShapeMNK = cute::Shape<_1, _1, _1>;\n\n  using AccumulatorPipeline =\n      cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n  using AccumulatorPipelineStorage = typename AccumulatorPipeline::SharedStorage;\n\n  static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n  using MainloopPipeline =\n      cutlass::PipelineTmaUmmaAsync<MainloopPipelineStageCount, Shape<_1, _1, _1>, AtomThrShapeMNK>;\n  using MainloopPipelineStorage = typename MainloopPipeline::SharedStorage;\n\n  alignas(16) AccumulatorPipelineStorage accumulator;\n  alignas(16) MainloopPipelineStorage mainloop;\n  alignas(16) cute::uint64_t tma_barrier[1];\n  uint32_t tmem_base_ptr;\n\n  struct TensorStorage : cute::aligned_struct<128, _1> {\n    // cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementA, cute::cosize_v<ASmemLayout>> smem_A;\n    cute::array_aligned<ElementB, cute::cosize_v<BSmemLayout>> smem_B;\n  } tensors;\n};\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 8> StochasticNumericConverterBase(\n    cutlass::Array<float, 8> const &input, cutlass::Array<uint32_t, 2> const &rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 8>;\n  result_type output;\n  constexpr bool has_rs = ARCH_HAS_STOCHASTIC_ROUNDING;\n  if constexpr (has_rs) {\n    auto output_ptr = reinterpret_cast<uint16_t *>(&output);\n    asm volatile(\n        \"{\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %0, {%5, %4, %3, %2}, %10;\\n\"\n        \"cvt.rs.satfinite.e2m1x4.f32   %1, {%9, %8, %7, %6}, %11;\\n\"\n        \"}\"\n        : \"=h\"(output_ptr[0]), \"=h\"(output_ptr[1])\n        : \"f\"(input[0]), \"f\"(input[1]), \"f\"(input[2]), \"f\"(input[3]), \"f\"(input[4]), \"f\"(input[5]),\n          \"f\"(input[6]), \"f\"(input[7]), \"r\"(rbits[0]), \"r\"(rbits[1]));\n  } else {\n    NVTE_DEVICE_ERROR(\n        \"FP4 cvt PTX instructions are architecture-specific. \"\n        \"Try recompiling with sm_XXXa instead of sm_XXX.\");\n  }\n  return output;\n}\n\nCUTLASS_DEVICE\ncutlass::Array<cutlass::float_e2m1_t, 16> StochasticNumericConverter(\n    cutlass::Array<float, 16> const &input, cutlass::Array<uint32_t, 4> const *rbits) {\n  using result_type = cutlass::Array<cutlass::float_e2m1_t, 16>;\n  result_type output;\n  cutlass::Array<cutlass::float_e2m1_t, 8> *result_ptr =\n      reinterpret_cast<cutlass::Array<cutlass::float_e2m1_t, 8> *>(&output);\n  cutlass::Array<float, 8> const *source_ptr =\n      reinterpret_cast<cutlass::Array<float, 8> const *>(&input);\n  cutlass::Array<uint32_t, 2> const *rbits_ptr =\n      reinterpret_cast<cutlass::Array<uint32_t, 2> const *>(rbits);\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 2; i++) {\n    result_ptr[i] = StochasticNumericConverterBase(source_ptr[i], rbits_ptr[i]);\n  }\n  return output;\n}\n\ntemplate <class MShape, class NShape, class KShape, class ClusterTileShape, class TA, class AStride,\n          class ASmemLayout, class TmaLoadA, class TB, class BStride, class BSmemLayout,\n          class TmaLoadB, class TC, class CStride, class CSmemLayout, class TSFC, class TiledMMA,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\n__global__ static void group_rht_gemm_device(\n    MShape M, NShape N, KShape K, ClusterTileShape cluster_tile, TA const *A, AStride dA,\n    ASmemLayout sAlayout, CUTE_GRID_CONSTANT TmaLoadA const tma_load_a, TB const *B, BStride dB,\n    BSmemLayout sBlayout, CUTE_GRID_CONSTANT TmaLoadB const tma_load_b, CSmemLayout, TiledMMA mma,\n    MultiAmaxHadamardCastFusionArgs kernel_args, const size_t *rng_state) {\n  using namespace cute;\n  constexpr bool is_blackwell_arch = ARCH_BLACKWELL_FAMILY;\n  if constexpr (!is_blackwell_arch) {\n    NVTE_DEVICE_ERROR(\"RHT fusion is only supported on Blackwell.\");\n    return;\n  } else {\n    using X = Underscore;\n    // static constexpr bool kApplyStochasticRounding = true;\n    using ElementAccumulator = float;\n    static constexpr int K_PIPE_MAX = size<3>(ASmemLayout{});\n    using AtomThrShapeMNK = Shape<decltype(shape<0>(typename TiledMMA::ThrLayoutVMNK{})), _1, _1>;\n    static constexpr uint32_t kTmaTransactionBytes = cutlass::bits_to_bytes(\n        size(AtomThrShapeMNK{}) * cosize(take<0, 3>(ASmemLayout{})) * cute::sizeof_bits_v<TA>);\n\n    static constexpr int kTmaRhtTensorTransactionBytes =\n        cutlass::bits_to_bytes(16 * 16 * cute::sizeof_bits_v<TB>);\n    static constexpr int AccumulatorPipelineStageCount = 16;\n\n    static constexpr int MainloopPipelineStageCount = size<3>(ASmemLayout{});\n    using MainloopPipeline = cutlass::PipelineTmaUmmaAsync<MainloopPipelineStageCount,\n                                                           Shape<_1, _1, _1>, AtomThrShapeMNK>;\n    using MainloopPipelineState = typename MainloopPipeline::PipelineState;\n\n    using TmemAllocator = cute::TMEM::Allocator1Sm;\n    static constexpr int VectorSize = 16;\n    const size_t rng_seed = rng_state != nullptr ? rng_state[0] : 0;\n    const size_t rng_offset = rng_state != nullptr ? rng_state[1] : 0;\n    // Preconditions\n    CUTE_STATIC_ASSERT(is_static<ASmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<BSmemLayout>::value);\n    CUTE_STATIC_ASSERT(is_static<CSmemLayout>::value);\n\n    // Represent the full tensors\n    Tensor mA = tma_load_a.get_tma_tensor(make_shape(M, N));\n    Tensor mB = tma_load_b.get_tma_tensor(make_shape(16, 16));\n\n    using TensorC = decltype(make_tensor(subbyte_iterator<TC>(recast_ptr<TC>(nullptr)),  // engine\n                                         make_shape(int{}, int{}),                       // (M, N_i)\n                                         Stride2D{}  // stride (dM, dN)\n                                         ));\n\n    using TensorSFC = decltype(make_tensor(\n        make_gmem_ptr(recast_ptr<TSFC>(nullptr)),\n        make_layout(make_shape(int{},                                   // M\n                               make_shape(make_shape(Int<16>{}, _4{}),  // (16, 4)\n                                          int{})                        // n_tiles = split / 64\n                               ),\n                    make_stride(int{},                                // dM = (split / 16)\n                                make_stride(make_stride(_0{}, _1{}),  // inner (16,4) layout\n                                            _4{})                     // tiles stride\n                                ))));\n\n    auto cluster_shape = Shape<_1, _1, _1>{};\n\n    // Get the appropriate blocks for this Cluster\n    dim3 cluster_coord_in_grid = cluster_id_in_grid();\n\n    // Total number of k-tiles\n    const int K_TILE_MAX = min(N, K) / 64;\n    uint32_t tiles_in_m = (M + size<0>(cluster_tile) - 1) / size<0>(cluster_tile);\n    uint32_t tiles_in_n = (N + 64 - 1) / 64;\n    uint32_t linear_tile_idx = blockIdx.x;\n    uint32_t tile_idx_m = linear_tile_idx % tiles_in_m;\n    uint32_t tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n\n    auto mainloop_tiler = Shape<_128, _16, _64>{};\n    auto epilogue_tiler = Shape<_128, _64, _64>{};\n    Tensor gA_mk = local_tile(mA, mainloop_tiler, make_coord(_, _, _), Step<_1, X, _1>{});\n    Tensor gB_nk =\n        local_tile(mB, cluster_tile, make_coord(_, _, _), Step<X, _1, _1>{});  // (BLK_N,BLK_K,k)\n    // Tensor gC_mn = local_tile(mC, epilogue_tiler, make_coord(_,_, _), Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n    using TensorGC = decltype(local_tile(std::declval<TensorC>(), decltype(epilogue_tiler){},\n                                         make_coord(_, _, _), Step<_1, _1, X>{}));\n\n    using TensorGSFC = decltype(local_tile(std::declval<TensorSFC>(), decltype(epilogue_tiler){},\n                                           make_coord(_, _, _), Step<_1, _1, X>{}));\n\n    // Allocate SMEM\n    extern __shared__ char shared_memory[];\n    using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n    SharedStorage &shared_storage = *reinterpret_cast<SharedStorage *>(shared_memory);\n    Tensor tCsA = make_tensor(make_smem_ptr(shared_storage.tensors.smem_A.data()),\n                              sAlayout);  // (MMA,MMA_M,MMA_N,PIPE)\n    Tensor tCsB = make_tensor(make_smem_ptr(shared_storage.tensors.smem_B.data()),\n                              sBlayout);  // (MMA,MMA_N,MMA_K,PIPE)\n\n    //\n    // MMA: Define C accumulators and A/B partitioning\n    //\n\n    int block_rank_in_cluster = cute::block_rank_in_cluster();\n    ThrMMA thr_mma = mma.get_slice(block_rank_in_cluster);  // blk idx\n    Tensor tCgB = thr_mma.partition_B(gB_nk);               // (MMA,MMA_N,MMA_K,k)\n\n    auto mma_epilogue = make_tiled_mma(SM100_MMA_F16BF16_SS<TA, TB, ElementAccumulator, 128, 64,\n                                                            UMMA::Major::MN, UMMA::Major::MN>{},\n                                       Layout<Shape<_1, _1>>{});\n    ThrMMA thr_mma_epilogue = mma_epilogue.get_slice(block_rank_in_cluster);\n\n    using TiledMmaEpilogue = decltype(mma_epilogue);\n    Tensor tCgA = thr_mma.partition_A(gA_mk);\n    // Allocate \"fragments\" -- these are actually umma smem descriptors\n    Tensor tCrA = thr_mma.make_fragment_A(tCsA);  // (MMA,MMA_M,MMA_K,PIPE)\n    Tensor tCrB = thr_mma.make_fragment_B(tCsB);  // (MMA,MMA_M,MMA_K,PIPE)\n\n    auto acc_shape_mma = partition_shape_C(TiledMMA{}, take<0, 2>(ClusterTileShape{}));\n    auto acc_shape_epilogue = partition_shape_C(TiledMmaEpilogue{}, take<0, 2>(epilogue_tiler));\n\n    auto bulk_tmem_mma =\n        TiledMMA::make_fragment_C(append(acc_shape_mma, Int<AccumulatorPipelineStageCount>{}));\n\n    auto bulk_tmem_epilogue = TiledMmaEpilogue::make_fragment_C(\n        append(acc_shape_epilogue, Int<AccumulatorPipelineStageCount / 4>{}));\n\n    TmemAllocator tmem_allocator{};\n    cutlass::arch::NamedBarrier tmem_allocation_result_barrier(\n        32 + 128, cutlass::arch::ReservedNamedBarriers::TmemAllocBarrier);\n\n    Layout cta_layout_mnk = make_layout(cluster_shape);\n    Layout cta_layout_vmnk =\n        tiled_divide(cta_layout_mnk, make_tile(typename TiledMMA::AtomThrID{}));\n    auto cta_coord_vmnk = cta_layout_vmnk.get_flat_coord(block_rank_in_cluster);\n\n    auto [tAgA, tAsA] =\n        tma_partition(tma_load_a, get<2>(cta_coord_vmnk), make_layout(size<2>(cta_layout_vmnk)),\n                      group_modes<0, 3>(tCsA), group_modes<0, 3>(tCgA));\n\n    auto [tBgB, tBsB] =\n        tma_partition(tma_load_b, get<1>(cta_coord_vmnk), make_layout(size<1>(cta_layout_vmnk)),\n                      group_modes<0, 3>(tCsB), group_modes<0, 3>(tCgB));\n\n    uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cta_layout_vmnk, cta_coord_vmnk);\n    uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cta_layout_vmnk, cta_coord_vmnk);\n\n    int warp_idx = cutlass::canonical_warp_idx_sync();\n\n    bool is_mma_warp = (warp_idx == 0);\n    bool is_dma_warp = (warp_idx == 1);\n    bool is_epilogue_warp = (warp_idx >= 4 && warp_idx <= 7);\n\n    // if (is_epilogue_warp && elect_one_sync()) {\n    //   // prefetch to make the global amax in cache\n    //   for (size_t i = 0; i < kernel_args.num_tensors; ++i) {\n    //     cute::prefetch(raw_pointer_cast(kernel_args.global_amax_list[i]));\n    //   }\n    // }\n\n    typename MainloopPipeline::Params mainloop_pipeline_params;\n    if (is_dma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n    }\n    if (is_mma_warp) {\n      mainloop_pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n    }\n    mainloop_pipeline_params.is_leader = cute::elect_one_sync() && is_dma_warp;\n    mainloop_pipeline_params.transaction_bytes = kTmaTransactionBytes;\n    mainloop_pipeline_params.initializing_warp = 0;\n    MainloopPipeline mainloop_pipeline(shared_storage.mainloop, mainloop_pipeline_params,\n                                       cluster_shape, cute::true_type{},  // Perform barrier init\n                                       cute::true_type{});                // Delay mask calculation\n\n    MainloopPipelineState mainloop_pipe_consumer_state;\n    MainloopPipelineState mainloop_pipe_producer_state =\n        cutlass::make_producer_start_state<MainloopPipeline>();\n\n    using AccumulatorPipeline =\n        cutlass::PipelineUmmaAsync<AccumulatorPipelineStageCount / 4, AtomThrShapeMNK>;\n    using AccumulatorPipelineState = typename AccumulatorPipeline::PipelineState;\n\n    AccumulatorPipelineState accumulator_pipe_consumer_state;\n    AccumulatorPipelineState accumulator_pipe_producer_state =\n        cutlass::make_producer_start_state<AccumulatorPipeline>();\n\n    typename AccumulatorPipeline::Params accumulator_pipeline_params;\n    if (is_mma_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Producer;\n    }\n    if (is_epilogue_warp) {\n      accumulator_pipeline_params.role = AccumulatorPipeline::ThreadCategory::Consumer;\n    }\n    // Only one producer thread arrives on this barrier.\n    accumulator_pipeline_params.producer_arv_count = 1;\n    accumulator_pipeline_params.consumer_arv_count = size(AtomThrShapeMNK{}) * 128;\n    accumulator_pipeline_params.initializing_warp = 1;\n    AccumulatorPipeline accumulator_pipeline(shared_storage.accumulator,\n                                             accumulator_pipeline_params, cluster_shape,\n                                             cute::true_type{},   // Perform barrier init\n                                             cute::true_type{});  // Delay mask calculation\n\n    if (warp_idx == 2 && elect_one_sync()) {\n      cute::initialize_barrier(shared_storage.tma_barrier[0], /* num_threads */ 1);\n    }\n    __syncthreads();\n    using TMEM_LOAD_NEW = cute::SM100::TMEM::LOAD::SM100_TMEM_LOAD_32dp32b64x;\n\n    if (is_dma_warp) {\n      if (elect_one_sync()) {\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier[0],\n                                            kTmaRhtTensorTransactionBytes);\n        copy(tma_load_b.with(shared_storage.tma_barrier[0], tma_mcast_mask_b), tBgB(_, 0, 0),\n             tBsB(_, 0));\n      }\n\n      do {\n        bool is_first_wave = linear_tile_idx == blockIdx.x;\n        uint32_t skip_wait = is_first_wave;\n        auto tAgA_mk = tAgA(_, tile_idx_m, _);\n        int k_tile = 0;\n        auto barrier_token =\n            mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n\n        CUTE_NO_UNROLL\n        while (k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n) {\n          int k_tile_idx_n = tile_idx_n + k_tile;\n          ++k_tile;\n          skip_wait = (is_first_wave && k_tile < MainloopPipelineStageCount);\n          mainloop_pipeline.producer_acquire(mainloop_pipe_producer_state, barrier_token);\n          using BarrierType = typename MainloopPipeline::ProducerBarrierType;\n          BarrierType *tma_barrier =\n              mainloop_pipeline.producer_get_barrier(mainloop_pipe_producer_state);\n          int write_stage = mainloop_pipe_producer_state.index();\n          ++mainloop_pipe_producer_state;\n          barrier_token =\n              mainloop_pipeline.producer_try_acquire(mainloop_pipe_producer_state, skip_wait);\n          if (cute::elect_one_sync()) {\n            copy(tma_load_a.with(*tma_barrier, tma_mcast_mask_a), tAgA_mk(_, k_tile_idx_n),\n                 tAsA(_, write_stage));\n          }\n        }\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n      mainloop_pipeline.producer_tail(mainloop_pipe_producer_state);\n    } else if (is_mma_warp) {\n      mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n      tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns,\n                              &shared_storage.tmem_base_ptr);\n      __syncwarp();\n      tmem_allocation_result_barrier.arrive();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_mma.data() = tmem_base_ptr;\n\n      cute::wait_barrier(shared_storage.tma_barrier[0], 0 /*tma_phase_bit*/);\n      do {\n        uint32_t skip_wait = K_TILE_MAX <= 0;\n        auto barrier_token =\n            mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n        CUTE_NO_UNROLL\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n;) {\n          mainloop_pipeline.consumer_wait(mainloop_pipe_consumer_state, barrier_token);\n          int read_stage = mainloop_pipe_consumer_state.index();\n          auto tCrA_mk = tCrA(_, _, _, read_stage);\n          auto tCrB_nk = tCrB(_, _, 0, 0);\n          CUTE_UNROLL\n          for (int k_block = 0; k_block < size<2>(tCrA) / 4; ++k_block) {\n            accumulator_pipeline.producer_acquire(accumulator_pipe_producer_state);\n            CUTE_UNROLL\n            for (int i = 0; i < 4; i++) {\n              auto accumulators =\n                  bulk_tmem_mma(_, _, _, accumulator_pipe_producer_state.index() * 4 + i);\n              gemm(mma, tCrA_mk(_, _, k_block * 4 + i), tCrB_nk, accumulators);\n            }\n\n            accumulator_pipeline.producer_commit(accumulator_pipe_producer_state);\n            ++accumulator_pipe_producer_state;\n          }\n          auto curr_mainloop_pipe_consumer_state = mainloop_pipe_consumer_state;\n          ++mainloop_pipe_consumer_state;\n          ++k_tile;\n          skip_wait = k_tile >= K_TILE_MAX;\n          barrier_token =\n              mainloop_pipeline.consumer_try_wait(mainloop_pipe_consumer_state, skip_wait);\n          mainloop_pipeline.consumer_release(curr_mainloop_pipe_consumer_state);\n        }\n\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n      tmem_allocator.release_allocation_lock();\n      accumulator_pipeline.producer_tail(accumulator_pipe_producer_state);\n      tmem_allocator.free(tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n    } else if (is_epilogue_warp) {\n      static constexpr int FragmentSize = 256 / sizeof_bits_v<TC>;\n\n      tmem_allocation_result_barrier.arrive_and_wait();\n      uint32_t tmem_base_ptr = shared_storage.tmem_base_ptr;\n      bulk_tmem_epilogue.data() = tmem_base_ptr;\n      int thread_idx = threadIdx.x % 128;\n\n      auto tiled_t2r = make_tmem_copy(TMEM_LOAD_NEW{}, bulk_tmem_epilogue(_, _, _, _0{}));\n      auto tiled_r2g =\n          make_tiled_copy_D(Copy_Atom<SM100_STORE_256bit_CACHE_NOALLOCATION, TC>{}, tiled_t2r);\n      auto thr_t2r = tiled_t2r.get_slice(thread_idx);\n      auto thr_r2g = tiled_r2g.get_slice(thread_idx);\n\n      // NVFP4 non-E8 recipe constants and global scales\n      static constexpr float fp4_max = 6.0f;\n      static constexpr float fp4_max_inv = 1.0f / fp4_max;\n\n      // get global amax pointer\n      int tensor_id = GetTensorId(&kernel_args, tile_idx_n * 64);\n      float *global_amax_ptr = GetGlobalAmaxPtrByTensorId(&kernel_args, tensor_id);\n\n      TC *cur_output_colwise_ptr =\n          reinterpret_cast<TC *>(kernel_args.output_colwise_list[tensor_id]);\n      TSFC *cur_output_colwise_scale_inv_ptr =\n          reinterpret_cast<TSFC *>(kernel_args.output_colwise_scale_inv_list[tensor_id]);\n      int cur_output_colwise_n = kernel_args.split_sections[tensor_id];\n\n      TensorC cur_mC = cute::make_tensor(\n          cute::subbyte_iterator<TC>(cur_output_colwise_ptr),\n          cute::make_shape(static_cast<int>(M), cur_output_colwise_n),  // (M, N_i)\n          kernel_args.output_stride2d_list[tensor_id]);\n\n      auto cur_sfc_shape =\n          make_shape(M, make_shape(make_shape(Int<16>{}, _4{}), cur_output_colwise_n / 64));\n\n      auto cur_sfc_stride =\n          make_stride(cur_output_colwise_n / 16, make_stride(make_stride(_0{}, _1{}), _4{}));\n\n      TensorSFC cur_mSFC = cute::make_tensor(make_gmem_ptr(cur_output_colwise_scale_inv_ptr),\n                                             make_layout(cur_sfc_shape, cur_sfc_stride));\n\n      TensorGC cur_gC_mn = local_tile(\n          cur_mC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N)\n      );\n\n      TensorGSFC cur_gSFC_mn = local_tile(\n          cur_mSFC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N-like)\n      );\n\n      Tensor tCgC = thr_mma_epilogue.partition_C(cur_gC_mn);\n\n      float global_amax_val = *global_amax_ptr;\n      float global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n\n      // Scaling factor for fast math path\n      float global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n\n      float global_decode_scale = 1.0f / global_encode_scale;\n\n      auto sfd_converter = cutlass::NumericConverter<TSFC, float>{};\n\n      do {\n        for (int k_tile = 0; k_tile < K_TILE_MAX && k_tile + tile_idx_n < tiles_in_n; ++k_tile) {\n          // get the starting index of current k-tile in global tensor, to query the correct global amax\n          int cur_k_tile_global_elem_idx = (tile_idx_n + k_tile) * 64;\n          int new_tensor_id = GetTensorId(&kernel_args, cur_k_tile_global_elem_idx);\n          // float* new_global_amax_ptr = GetGlobalAmaxPtr(&kernel_args, cur_k_tile_global_elem_idx);\n          global_amax_ptr = GetGlobalAmaxPtrByTensorId(&kernel_args, new_tensor_id);\n          // update the scaling factors when it's no longer the same amax pointer\n          // TODO(zhongbo): the math operations are very expensive\n          // since the kernel is persistent, we can have a cache for all the possible scaling factors\n          if (tensor_id != new_tensor_id) {\n            global_amax_val = *global_amax_ptr;\n            global_encode_scale = ComputeGlobalEncodeScaleFP4(global_amax_val);\n            global_encode_scale_multiplier = global_encode_scale * fp4_max_inv;\n            global_decode_scale = 1.0f / global_encode_scale;\n            tensor_id = new_tensor_id;\n            // went through the cute operations to update the local tensors\n            cur_output_colwise_ptr =\n                reinterpret_cast<TC *>(kernel_args.output_colwise_list[tensor_id]);\n            cur_output_colwise_scale_inv_ptr =\n                reinterpret_cast<TSFC *>(kernel_args.output_colwise_scale_inv_list[tensor_id]);\n            cur_output_colwise_n = kernel_args.split_sections[tensor_id];\n\n            cur_mC = cute::make_tensor(\n                cute::subbyte_iterator<TC>(cur_output_colwise_ptr),\n                cute::make_shape(static_cast<int>(M), cur_output_colwise_n),  // (M, N_i)\n                kernel_args.output_stride2d_list[tensor_id]);\n\n            cur_sfc_shape =\n                make_shape(M, make_shape(make_shape(Int<16>{}, _4{}), cur_output_colwise_n / 64));\n\n            cur_sfc_stride =\n                make_stride(cur_output_colwise_n / 16, make_stride(make_stride(_0{}, _1{}), _4{}));\n\n            cur_mSFC = cute::make_tensor(make_gmem_ptr(cur_output_colwise_scale_inv_ptr),\n                                         make_layout(cur_sfc_shape, cur_sfc_stride));\n\n            cur_gC_mn = local_tile(\n                cur_mC, epilogue_tiler, make_coord(_, _, _), Step<_1, _1, X>{}  // (BLK_M, BLK_N)\n            );\n\n            cur_gSFC_mn = local_tile(cur_mSFC, epilogue_tiler, make_coord(_, _, _),\n                                     Step<_1, _1, X>{}  // (BLK_M, BLK_N-like)\n            );\n\n            tCgC = thr_mma_epilogue.partition_C(cur_gC_mn);\n          }\n          // maybe udpated to the new tensor id\n          int tensor_start_elem = kernel_args.split_sections_range[tensor_id];\n          int local_tile_idx_n = (cur_k_tile_global_elem_idx - tensor_start_elem) / 64;\n\n          Tensor tCgC_mn = tCgC(_, _, _, tile_idx_m, local_tile_idx_n);\n          Tensor tCgSFC_mn = cur_gSFC_mn(_, _, tile_idx_m, local_tile_idx_n);\n\n          accumulator_pipeline.consumer_wait(accumulator_pipe_consumer_state);\n\n          auto tCtC = bulk_tmem_epilogue(_, _, _, accumulator_pipe_consumer_state.index());\n          Tensor tDtC = thr_t2r.partition_S(tCtC);     // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          Tensor tDgC = thr_t2r.partition_D(tCgC_mn);  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n\n          Tensor tTR_rAcc =\n              make_tensor<ElementAccumulator>(shape(tDgC));  // ((TMEM_LOAD,#TMEM_LOAD),MMA_M,MMA_N)\n          Tensor tDrC = make_tensor<TC>(shape(tDgC));\n          Tensor tTR_rAcc_frag =\n              recast<cutlass::Array<ElementAccumulator, FragmentSize>>(coalesce(tTR_rAcc));\n          Tensor tDrC_frag = recast<cutlass::Array<TC, FragmentSize>>(coalesce(tDrC));\n\n          Tensor src = thr_r2g.retile_S(tDrC);\n          Tensor dst = thr_r2g.retile_D(tDgC);\n\n          Tensor tCgSFC = make_tensor(\n              tCgSFC_mn.data(), make_layout(make_shape(shape(tCgSFC_mn), Int<1>{}, Int<1>{}),\n                                            make_stride(stride(tCgSFC_mn), Int<0>{}, Int<0>{})));\n\n          Tensor tDgSFC = filter(thr_t2r.partition_D(tCgSFC));\n          Tensor tDrSFC = make_tensor<TSFC>(shape(tDgSFC));\n\n          static constexpr int NumVecs = size(tDgC) / VectorSize;\n          Tensor tC_rRowSFD_frg = recast<cutlass::Array<TSFC, NumVecs>>(tDrSFC);\n\n          cutlass::maximum_absolute_value_reduction<cutlass::Array<ElementAccumulator, VectorSize>,\n                                                    true>\n              amax_reduction;\n          cutlass::Array<ElementAccumulator, NumVecs> vec_maxs;\n          cutlass::Array<ElementAccumulator, NumVecs> pvscales;\n          // TMEM_LOAD\n          copy(tiled_t2r, tDtC, tTR_rAcc);\n          cutlass::arch::fence_view_async_tmem_load();\n\n          accumulator_pipeline.consumer_release(accumulator_pipe_consumer_state);\n\n          ++accumulator_pipe_consumer_state;\n\n          if constexpr (!kUseFastMath) {\n            // Downcast to BF16 for bit-wise compatibility with unfused\n            // kernels\n            auto convert_accum_to_bf16 =\n                cutlass::NumericArrayConverter<cutlass::bfloat16_t, ElementAccumulator,\n                                               FragmentSize>{};\n            auto convert_bf16_to_accum =\n                cutlass::NumericArrayConverter<ElementAccumulator, cutlass::bfloat16_t,\n                                               FragmentSize>{};\n            tTR_rAcc_frag(_0{}) = convert_bf16_to_accum(convert_accum_to_bf16(tTR_rAcc_frag(_0{})));\n          }\n\n          auto compute_frgs = reinterpret_cast<cutlass::Array<ElementAccumulator, VectorSize> *>(\n              tTR_rAcc_frag.data());\n          auto output_frgs = reinterpret_cast<cutlass::Array<TC, VectorSize> *>(tDrC_frag.data());\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            vec_maxs[v] = amax_reduction(ElementAccumulator(0), compute_frgs[v]);\n          }\n\n          pvscales = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n              vec_maxs, global_encode_scale_multiplier);\n          auto pvscales_cvted =\n              cutlass::NumericArrayConverter<TSFC, ElementAccumulator, NumVecs>{}(pvscales);\n\n          tC_rRowSFD_frg(_0{}) = pvscales_cvted;\n          auto qpvscale_ups = cutlass::NumericArrayConverter<ElementAccumulator, TSFC, NumVecs>{}(\n              tC_rRowSFD_frg(_0{}));\n          auto qpvscale_scaled = cutlass::multiplies<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n              qpvscale_ups, global_decode_scale);\n          cutlass::Array<ElementAccumulator, NumVecs> acc_scales;\n          if constexpr (kUseFastMath) {\n            // Fast math: compute approximate reciprocal\n            acc_scales =\n                cutlass::reciprocal_approximate_ftz<decltype(qpvscale_scaled)>{}(qpvscale_scaled);\n          } else {\n            // Accurate math: compute reciprocal with division\n            acc_scales = cutlass::divides<cutlass::Array<ElementAccumulator, NumVecs>>{}(\n                1.0, qpvscale_scaled);\n          }\n\n          // Initialize RNG for tile\n          const size_t rng_sequence =\n              thread_idx + k_tile * 256 + linear_tile_idx * K_TILE_MAX * 256;\n\n          transformer_engine::curanddx::detail::philox4x32_native_state<\n              NVTE_BUILD_NUM_PHILOX_ROUNDS>\n              rng;\n          rng.init(rng_seed, rng_sequence, rng_offset);\n          uint4 random_uint4 = uint4{0, 0, 0, 0};\n\n          CUTLASS_PRAGMA_UNROLL\n          for (int v = 0; v < NumVecs; v++) {\n            auto acc_scale = cutlass::minimum_with_nan_propagation<ElementAccumulator>{}(\n                acc_scales[v], cutlass::platform::numeric_limits<ElementAccumulator>::max());\n            // auto acc_scale = acc_scales[v];\n            if constexpr (kEnableStochasticRounding) {\n              random_uint4 = rng.generate4();\n              output_frgs[v] = StochasticNumericConverter(\n                  cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                      compute_frgs[v], acc_scale),\n                  reinterpret_cast<cutlass::Array<uint32_t, 4> *>(&random_uint4));\n            } else {\n              output_frgs[v] = cutlass::NumericArrayConverter<TC, ElementAccumulator, VectorSize>{}(\n                  cutlass::multiplies<cutlass::Array<ElementAccumulator, VectorSize>>{}(\n                      compute_frgs[v], acc_scale));\n            }\n          }\n\n          copy(tiled_r2g, src, dst);\n\n          // copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrC, tDgC);\n\n          copy(AutoVectorizingCopyWithAssumedAlignment<128>{}, tDrSFC, tDgSFC);\n        }\n        linear_tile_idx += gridDim.x;\n        tile_idx_m = linear_tile_idx % tiles_in_m;\n        tile_idx_n = (linear_tile_idx / tiles_in_m) * K_TILE_MAX;\n      } while (tile_idx_m < tiles_in_m && tile_idx_n < tiles_in_n);\n    }\n  }\n}\n\n// this function computes RHT-GEMM for\n// A: m x n: col-major\n// B: 16 x 16: row-major\n// C: m x n: row-major\n// SFC: m x (n/16): row-major\ntemplate <typename TA, typename TB, typename TC, typename TSFC,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid group_rht_gemm_ntt_w_sfc(int m, int n, TA const *A, TB const *B,\n                              MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                              const size_t *rng_state, uint32_t sm_count, hipStream_t stream,\n                              int k_tile_size = 2048) {\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = static_cast<int>(m);\n  auto N = static_cast<int>(n);\n\n  // Define strides (mixed)\n  auto dA = make_stride(Int<1>{}, m);   // (dM,dK)\n  auto dB = make_stride(Int<1>{}, 16);  // (dN,dK)\n  for (size_t i = 0; i < kernel_args_ptr->num_tensors; ++i) {\n    kernel_args_ptr->output_stride2d_list[i] =\n        make_stride(kernel_args_ptr->split_sections[i], Int<1>{});\n  }\n\n  auto cga_shape = Shape<_1, _1, _1>{};\n  auto cga_tile_shape = Shape<_128, _16, _16>{};\n  auto cluster_tile_mainloop = Shape<_128, _16, _64>{};\n\n  // Construct the MMA\n  auto mma = make_tiled_mma(\n      SM100_MMA_F16BF16_SS<TA, TB, float, 128, 16, UMMA::Major::MN, UMMA::Major::MN>{},\n      Layout<Shape<_1, _1>>{});\n\n  // MMA in CGA Layout XXX: Need to generalize synchro? {$nv-release-never}\n\n  // Assert that the TiledMMA uses all CTAs in the CGA.\n  CUTE_STATIC_ASSERT_V(size(cga_shape) == size(mma));\n  CUTE_STATIC_ASSERT_V(evenly_divides(cga_tile_shape, tile_shape(mma)));\n\n  // Determine the A and B shapes\n  auto mma_shape_B =\n      partition_shape_B(mma, make_shape(size<1>(cga_tile_shape), size<2>(cga_tile_shape)));\n\n  using TiledMma = decltype(mma);\n  using AtomThrID = typename TiledMma::AtomThrID;\n\n  using SmemShape_M = decltype(shape_div(\n      shape<0>(cga_tile_shape),\n      shape_div(shape<0>(cga_tile_shape), size<0>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_N = decltype(shape_div(\n      shape<1>(cga_tile_shape),\n      shape_div(shape<1>(cga_tile_shape), size<1>(cga_tile_shape) / size(AtomThrID{}))));\n  using SmemShape_K = decltype(cute::get<2>(cga_tile_shape));\n\n  using SmemLayoutAtomB =\n      decltype(cutlass::gemm::collective::detail::sm100_smem_selector<cute::UMMA::Major::MN, TB,\n                                                                      SmemShape_N, SmemShape_K>());\n\n  auto mma_shape_A = partition_shape_A(\n      mma, make_shape(size<0>(cluster_tile_mainloop), size<2>(cluster_tile_mainloop)));\n  using SmemShape_M_A =\n      decltype(shape_div(shape<0>(cluster_tile_mainloop),\n                         shape_div(shape<0>(cluster_tile_mainloop),\n                                   size<0>(cluster_tile_mainloop) / size(AtomThrID{}))));\n  using SmemShape_K_A = decltype(cute::get<2>(cluster_tile_mainloop));\n  using SmemLayoutAtomA = decltype(cutlass::gemm::collective::detail::sm100_smem_selector<\n                                   cute::UMMA::Major::MN, TA, SmemShape_M_A, SmemShape_K_A>());\n\n  // Define the smem layouts (static)\n  // Calculate max pipeline stages based on Blackwell SM100's 232KB shared memory\n  constexpr int kBlackwellSmemSize = 232448;  // 232KB in bytes\n  constexpr int kBytesPerStage =\n      cute::size(mma_shape_A) * sizeof(TA) + cute::size(mma_shape_B) * sizeof(TB);\n  constexpr int kReservedBytes = 256;  // Reserve for barriers and other uses\n  constexpr int kMaxStages = (kBlackwellSmemSize - kReservedBytes) / kBytesPerStage;\n  auto sP = Int<kMaxStages>{};  // SMEM pipelines\n  auto sA = UMMA::tile_to_mma_shape(SmemLayoutAtomA{},\n                                    append(mma_shape_A, sP));  // (MMA,MMA_M,MMA_K,PIPE)\n  auto sB = UMMA::tile_to_mma_shape(SmemLayoutAtomB{},\n                                    append(mma_shape_B, sP));  // (MMA,MMA_N,MMA_K,PIPE)\n  auto sC = Layout<_1>{};                                      // XXX Dummy\n\n  // Create GMEM tensors\n  Tensor tensorA = make_tensor(A, make_layout(make_shape(M, N), dA));    // (M,N)\n  Tensor tensorB = make_tensor(B, make_layout(make_shape(16, 16), dB));  // (16,16)\n\n  // Create the TiledCopy\n\n  auto tma_load_a =\n      make_tma_copy_A_sm100(SM90_TMA_LOAD{}, tensorA, sA(_, _, _, 0), cluster_tile_mainloop, mma);\n  auto tma_load_b =\n      make_tma_copy_B_sm100(SM90_TMA_LOAD{}, tensorB, sB(_, _, _, 0), cga_tile_shape, mma);\n\n  // Assert checks on tile sizes -- no predication\n  NVTE_CHECK(M % size<0>(cga_tile_shape) == 0, \"Inner dimension must be divisible by \",\n             static_cast<size_t>(size<0>(cga_tile_shape)), \" but got \", M, \".\");\n  NVTE_CHECK(N % (4 * size<1>(cga_tile_shape)) == 0, \"Outer dimension must be divisible by \",\n             4 * static_cast<size_t>(size<1>(cga_tile_shape)), \" but got \", N, \".\");\n\n  uint32_t tiles = size(ceil_div(M, get<0>(cga_tile_shape))) * size(ceil_div(N, k_tile_size));\n\n  tiles = (tiles < sm_count) ? tiles : sm_count;\n\n  dim3 dimBlock(256);\n  dim3 dimCluster(size<0>(cga_shape), size<1>(cga_shape), size<2>(cga_shape));\n  dim3 dimGrid(tiles, 1, 1);\n\n  int smem_size = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n  auto *kernel_ptr = &group_rht_gemm_device<\n      decltype(M), decltype(N), decltype(k_tile_size), decltype(cga_tile_shape), TA, decltype(dA),\n      decltype(sA), decltype(tma_load_a), TB, decltype(dB), decltype(sB), decltype(tma_load_b), TC,\n      Stride2D, decltype(sC), TSFC, decltype(mma), kEnableStochasticRounding, kUseFastMath>;\n\n  NVTE_CHECK_CUDA(\n      hipFuncSetAttribute(reinterpret_cast<const void*>(*kernel_ptr), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n\n  (*kernel_ptr)<<<dimGrid, dimBlock, smem_size, stream>>>(M, N, k_tile_size, cga_tile_shape, A, dA,\n                                                          sA, tma_load_a, B, dB, sB, tma_load_b, sC,\n                                                          mma, *kernel_args_ptr, rng_state);\n  NVTE_CHECK_CUDA(hipGetLastError());\n}\n\n// this function is used to wrap the group_rht_gemm_ntt_w_sfc function\n// to transpose the input tensor A\ntemplate <typename TA, typename TB, typename TC, typename TSFC,\n          bool kEnableStochasticRounding = false, bool kUseFastMath = false>\nvoid group_rht_gemm_ttt_wrapper(int m, int n, TA const *A, TB const *B,\n                                MultiAmaxHadamardCastFusionArgs *kernel_args_ptr,\n                                const size_t *rng_state, uint32_t sm_count, hipStream_t stream,\n                                int k_tile_size = 1024) {\n  // in addition to transpose the input tensor A\n  // we also need to reshape m, n to at best\n  // ultilize as many SMs as possible while keeping\n  // a relatively large contiguous dimension.\n  // for example, after swapping m, n for transpose purposes,\n  // the input / output tensor shapes for RHT-GEMM are:\n  // A: n x m: col-major\n  // B: 16 x 16: row-major\n  // C: n x m: row-major\n  // SFC: n x (m/16): row-major\n  group_rht_gemm_ntt_w_sfc<TA, TB, TC, TSFC, kEnableStochasticRounding, kUseFastMath>(\n      n, m, A, B, kernel_args_ptr, rng_state, sm_count, stream, k_tile_size);\n}\n\n}  // namespace\n}  // namespace detail\n\nvoid group_hadamard_transform_cast_fusion_columnwise(\n    const Tensor &input_, std::vector<Tensor *> &output_list, const size_t *split_sections,\n    size_t num_tensors, const Tensor &hadamard_matrix_, QuantizationConfig &quant_config,\n    hipStream_t stream) {\n  NVTE_API_CALL(group_hadamard_transform_cast_fusion_columnwise);\n\n  using transformer_engine::detail::kMaxTensorsPerKernel;\n  using transformer_engine::detail::MultiAmaxHadamardCastFusionArgs;\n\n  NVTE_CHECK(input_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Input tensor must be BF16 tensor, but dtype is \", to_string(input_.dtype()), \".\");\n  NVTE_CHECK(input_.dim() >= 2, \"Input must be a 2D tensor.\");\n  const SimpleTensor &input = input_.data;\n\n  NVTE_CHECK(output_list.size() == num_tensors,\n             \"Number of output tensors should match number of tensors.\");\n\n  NVTE_CHECK(num_tensors <= kMaxTensorsPerKernel,\n             \"Number of tensors should be less than or equal to \", kMaxTensorsPerKernel);\n\n  // construct the multi-tensor args\n  MultiAmaxHadamardCastFusionArgs kernel_args;\n  kernel_args.num_tensors = 0;\n  kernel_args.split_sections_range[0] = 0;\n  for (size_t i = 0; i < num_tensors; ++i) {\n    NVTE_CHECK(split_sections[i] % 64 == 0, \"component \", i,\n               \" of split_sections should be 64 multiple\");\n    if (split_sections[i] == 0) {\n      continue;\n    }\n    kernel_args.global_amax_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->amax.dptr);\n    // TODO(zhongbo): should we change API assumption to use columnwise_data instead of data?\n    kernel_args.output_colwise_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->data.dptr);\n    kernel_args.output_colwise_scale_inv_list[kernel_args.num_tensors] =\n        reinterpret_cast<void *>(output_list[i]->scale_inv.dptr);\n    kernel_args.split_sections[kernel_args.num_tensors] = split_sections[i];\n    kernel_args.split_sections_range[kernel_args.num_tensors + 1] =\n        kernel_args.split_sections_range[kernel_args.num_tensors] + split_sections[i];\n    kernel_args.num_tensors++;\n  }\n\n  // Stochastic rounding config\n  const bool use_stochastic_rounding = quant_config.stochastic_rounding;\n  const size_t *rng_state = nullptr;\n  if (quant_config.rng_state != nullptr) {\n    Tensor &rng_state_tensor = *convertNVTETensor(quant_config.rng_state);\n    NVTE_CHECK(rng_state_tensor.dtype() == DType::kInt64,\n               \"RNG state should contain 2 64-bit values.\");\n    NVTE_CHECK(rng_state_tensor.data.shape == std::vector<size_t>{2},\n               \"Shape of the RNG state should be [2], but got \", rng_state_tensor.data.shape);\n    rng_state = reinterpret_cast<const size_t *>(rng_state_tensor.data.dptr);\n  }\n\n  // Template arguments\n  using TA = cute::bfloat16_t;\n  using TB = cute::bfloat16_t;\n  using TC = cutlass::float_e2m1_t;\n  using TSFC = cutlass::float_ue4m3_t;\n\n  checkCuDriverContext(stream);\n\n  // Check Hadamard matrix\n  constexpr int kHadamardDimension = 16;\n\n  NVTE_CHECK(hadamard_matrix_.dtype() == transformer_engine::DType::kBFloat16,\n             \"Hadamard matrix must be BF16 tensor, but dtype is \",\n             to_string(hadamard_matrix_.dtype()), \".\");\n  const SimpleTensor &hadamard_matrix = hadamard_matrix_.data;\n  NVTE_CHECK(\n      (hadamard_matrix_.shape() == std::vector<size_t>{kHadamardDimension, kHadamardDimension}),\n      \"Hadamard matrix must have shape=\",\n      std::vector<size_t>{kHadamardDimension, kHadamardDimension},\n      \", but got shape=\", hadamard_matrix_.shape(), \".\");\n  const size_t hadamard_dimension = hadamard_matrix.shape[0];\n\n  const size_t ndim = input.shape.size();\n  const size_t n = input.shape[ndim - 1];\n  size_t m = 1;\n  for (size_t i = 0; i < ndim - 1; ++i) {\n    m *= input.shape[i];\n  }\n\n  auto sm_count = transformer_engine::cuda::sm_count();\n\n  NVTE_CHECK(n % hadamard_dimension == 0, \"row_length must be divisible by hadamard_dimension.\");\n\n  NVTE_CHECK(m % hadamard_dimension == 0, \"num_rows must be divisible by hadamard_dimension\");\n\n  int k_tile_size = 1024;\n\n  if (m == 8192 && n == 5120) {\n    k_tile_size = 512;\n  } else if (m == 8192 && n == 10240) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 2560) {\n    k_tile_size = 1280;\n  } else if (m == 8192 && n == 11328) {\n    k_tile_size = 1024;\n  } else if (m == 8192 && n == 512) {\n    k_tile_size = 256;\n  } else if (m == 8192 && n == 3584) {\n    k_tile_size = 512;\n  } else if (m == 11328 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 5120 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m == 10240 && n == 8192) {\n    k_tile_size = 1024;\n  } else if (m == 2560 && n == 8192) {\n    k_tile_size = 1280;\n  } else if (m == 512 && n == 8192) {\n    k_tile_size = 256;\n  } else if (m == 3584 && n == 8192) {\n    k_tile_size = 512;\n  } else if (m < 1024 || n < 1024) {\n    k_tile_size = 512;\n  }\n\n  TRANSFORMER_ENGINE_SWITCH_CONDITION(\n      use_stochastic_rounding, kUseStochasticRounding,\n      TRANSFORMER_ENGINE_SWITCH_CONDITION(\n          quant_config.use_fast_math, kUseFastMath,\n          detail::group_rht_gemm_ttt_wrapper<TA, TB, TC, TSFC, kUseStochasticRounding,\n                                             kUseFastMath>(\n              /*m=*/m, /*n=*/n, /*A=*/reinterpret_cast<TA const *>(input.dptr),\n              /*B=*/reinterpret_cast<TB const *>(hadamard_matrix.dptr),\n              /*kernel_args_ptr=*/&kernel_args, /*rng_state=*/rng_state, /*sm_count=*/sm_count,\n              /*stream=*/stream, /*k_tile_size=*/k_tile_size);););\n}\n\n}  // namespace transformer_engine\n\nvoid nvte_group_hadamard_transform_cast_fusion_columnwise(\n    const NVTETensor input, NVTETensor *outputs, const NVTETensor hadamard_matrix,\n    const size_t *split_sections, const size_t num_tensors,\n    const NVTEQuantizationConfig quant_config, hipStream_t stream) {\n  NVTE_API_CALL(nvte_multi_hadamard_transform_cast_fusion_columnwise);\n  using namespace transformer_engine;\n  NVTE_CHECK(num_tensors > 0, \"Number of tensors should be greater than 0.\");\n\n  Tensor *input_tensor = convertNVTETensorCheck(input);\n  std::vector<Tensor *> output_list(num_tensors);\n  for (size_t i = 0; i < num_tensors; ++i) {\n    output_list[i] = convertNVTETensorCheck(outputs[i]);\n  }\n\n  QuantizationConfig quant_config_cpp;\n  if (quant_config != nullptr) {\n    quant_config_cpp = *reinterpret_cast<QuantizationConfig *>(quant_config);\n  }\n\n  // Call the multi-tensor Hadamard transform amax implementation.\n  group_hadamard_transform_cast_fusion_columnwise(\n      *input_tensor, output_list, split_sections, num_tensors,\n      *convertNVTETensorCheck(hadamard_matrix), quant_config_cpp, stream);\n}\n", "source": "/kaggle/working/TransformerEngine/transformer_engine/common/hadamard_transform/group_hadamard_transform_cast_fusion.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"pt_stable_utils.h\"\n#include \"warp_tensor.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <int N, int M, typename Element, typename PreprocFn>\nvoid __global__ sparseNM_dense_kernel(\n    Element const* in_ptr,\n    int64_t in_s0,\n    Element* out_ptr,\n    int64_t out_s0,\n    PreprocFn sort_preproc) {\n  constexpr int kBlockSize0 = 64;\n  constexpr int kBlockSize1 = 64;\n\n  int64_t block0 = blockIdx.x * kBlockSize0;\n  int64_t block1 = blockIdx.y * kBlockSize1;\n\n  WarpTensor<Element, 8, M * 4> tile;\n  static_assert(tile.kElementsPerThread == M);\n  CUTLASS_PRAGMA_UNROLL\n  for (int tile0 = 0; tile0 < kBlockSize0; tile0 += tile.kRows) {\n    CUTLASS_PRAGMA_UNROLL\n    for (int tile1 = 0; tile1 < kBlockSize1; tile1 += tile.kCols) {\n      // load\n      tile.load(in_ptr + (block0 + tile0) * in_s0 + block1 + tile1, in_s0);\n\n      // sparsify dense\n      tile = tile.template sparsify_dense<N, M>(sort_preproc);\n\n      // store\n      tile.store(out_ptr + (block0 + tile0) * out_s0 + block1 + tile1, out_s0);\n    }\n  }\n}\n\ntemplate <bool kIsMeta>\ntorch::stable::Tensor sparseNM_dense(\n    torch::stable::Tensor input,\n    std::string sort_preproc,\n    int64_t kN,\n    int64_t kM) {\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    STD_TORCH_CHECK(input.is_cuda(), \"All tensors must be on GPU\");\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2, \"Can only sparsify 2d tensors\");\n  STD_TORCH_CHECK(\n      input.stride(1) == 1,\n      \"Can only sparsify contiguous tensors. Sparsify the transpose otherwise.\");\n  STD_TORCH_CHECK(input.size(0) % 64 == 0);\n  STD_TORCH_CHECK(input.size(1) % 64 == 0);\n\n  torch::stable::Tensor out = torch::stable::empty_like(input);\n  bool foundKernel = false;\n  auto launchKernel = [&](auto dtype, auto N, auto M, auto sort_preproc) {\n    if (foundKernel) {\n      return;\n    }\n    foundKernel = true;\n    if (kIsMeta) {\n      return;\n    }\n    using Element = decltype(dtype);\n    dim3 num_blocks(input.size(0) / 64, input.size(1) / 64, 1);\n    sparseNM_dense_kernel<N.value, M.value>\n        <<<num_blocks, 32, 0, xf_getCurrentCUDAStream()>>>(\n            (Element const*)input.data_ptr(),\n            input.stride(0),\n            (Element*)out.data_ptr(),\n            out.stride(0),\n            sort_preproc);\n  };\n  STD_TORCH_CHECK(\n      input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n  auto dtype = cutlass::bfloat16_t();\n  auto Identity = [] __device__(auto x) { return x; };\n  auto Abs = [] __device__(auto x) { return cutlass::abs(x); };\n  auto testNM = [&](auto N, auto M) {\n    if (N.value != kN || M.value != kM) {\n      return;\n    }\n    if (sort_preproc == \"largest\") {\n      launchKernel(dtype, N, M, Identity);\n    } else if (sort_preproc == \"largest_abs\") {\n      launchKernel(dtype, N, M, Abs);\n    }\n  };\n  // 2:8 sparsification\n  testNM(std::integral_constant<int, 2>(), std::integral_constant<int, 8>());\n  // 2:4 sparsification\n  testNM(std::integral_constant<int, 2>(), std::integral_constant<int, 4>());\n  STD_TORCH_CHECK(foundKernel, \"Kernel not found\");\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n\n  return out;\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparseNM_dense\", TORCH_BOX(sparseNM_dense<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\"sparseNM_dense\", TORCH_BOX(sparseNM_dense<true>));\n}\n\nSTABLE_TORCH_LIBRARY_FRAGMENT(xformers, m) {\n  m.def(\n      \"sparseNM_dense(Tensor input, str sort_preproc, int N, int M) -> Tensor\");\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"pt_stable_utils.h\"\n#include \"warp_tensor.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <int N, int M, typename Element, typename PreprocFn>\nvoid __global__ sparseNM_dense_kernel(\n    Element const* in_ptr,\n    int64_t in_s0,\n    Element* out_ptr,\n    int64_t out_s0,\n    PreprocFn sort_preproc) {\n  constexpr int kBlockSize0 = 64;\n  constexpr int kBlockSize1 = 64;\n\n  int64_t block0 = blockIdx.x * kBlockSize0;\n  int64_t block1 = blockIdx.y * kBlockSize1;\n\n  WarpTensor<Element, 8, M * 4> tile;\n  static_assert(tile.kElementsPerThread == M);\n  CUTLASS_PRAGMA_UNROLL\n  for (int tile0 = 0; tile0 < kBlockSize0; tile0 += tile.kRows) {\n    CUTLASS_PRAGMA_UNROLL\n    for (int tile1 = 0; tile1 < kBlockSize1; tile1 += tile.kCols) {\n      // load\n      tile.load(in_ptr + (block0 + tile0) * in_s0 + block1 + tile1, in_s0);\n\n      // sparsify dense\n      tile = tile.template sparsify_dense<N, M>(sort_preproc);\n\n      // store\n      tile.store(out_ptr + (block0 + tile0) * out_s0 + block1 + tile1, out_s0);\n    }\n  }\n}\n\ntemplate <bool kIsMeta>\ntorch::stable::Tensor sparseNM_dense(\n    torch::stable::Tensor input,\n    std::string sort_preproc,\n    int64_t kN,\n    int64_t kM) {\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    STD_TORCH_CHECK(input.is_cuda(), \"All tensors must be on GPU\");\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2, \"Can only sparsify 2d tensors\");\n  STD_TORCH_CHECK(\n      input.stride(1) == 1,\n      \"Can only sparsify contiguous tensors. Sparsify the transpose otherwise.\");\n  STD_TORCH_CHECK(input.size(0) % 64 == 0);\n  STD_TORCH_CHECK(input.size(1) % 64 == 0);\n\n  torch::stable::Tensor out = torch::stable::empty_like(input);\n  bool foundKernel = false;\n  auto launchKernel = [&](auto dtype, auto N, auto M, auto sort_preproc) {\n    if (foundKernel) {\n      return;\n    }\n    foundKernel = true;\n    if (kIsMeta) {\n      return;\n    }\n    using Element = decltype(dtype);\n    dim3 num_blocks(input.size(0) / 64, input.size(1) / 64, 1);\n    sparseNM_dense_kernel<N.value, M.value>\n        <<<num_blocks, 32, 0, xf_getCurrentCUDAStream()>>>(\n            (Element const*)input.data_ptr(),\n            input.stride(0),\n            (Element*)out.data_ptr(),\n            out.stride(0),\n            sort_preproc);\n  };\n  STD_TORCH_CHECK(\n      input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n  auto dtype = cutlass::bfloat16_t();\n  auto Identity = [] __device__(auto x) { return x; };\n  auto Abs = [] __device__(auto x) { return cutlass::abs(x); };\n  auto testNM = [&](auto N, auto M) {\n    if (N.value != kN || M.value != kM) {\n      return;\n    }\n    if (sort_preproc == \"largest\") {\n      launchKernel(dtype, N, M, Identity);\n    } else if (sort_preproc == \"largest_abs\") {\n      launchKernel(dtype, N, M, Abs);\n    }\n  };\n  // 2:8 sparsification\n  testNM(std::integral_constant<int, 2>(), std::integral_constant<int, 8>());\n  // 2:4 sparsification\n  testNM(std::integral_constant<int, 2>(), std::integral_constant<int, 4>());\n  STD_TORCH_CHECK(foundKernel, \"Kernel not found\");\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n\n  return out;\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparseNM_dense\", TORCH_BOX(sparseNM_dense<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\"sparseNM_dense\", TORCH_BOX(sparseNM_dense<true>));\n}\n\nSTABLE_TORCH_LIBRARY_FRAGMENT(xformers, m) {\n  m.def(\n      \"sparseNM_dense(Tensor input, str sort_preproc, int N, int M) -> Tensor\");\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparseNM_dense.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"compute_sparse_tile.h\"\n#include \"pt_stable_utils.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <typename T>\nstruct Params {\n  uint64_t const* threads_masks;\n\n  T const* input;\n  int64_t input_stride;\n  int64_t input_dim0;\n  int64_t input_dim1;\n\n  T* output;\n  int64_t output_stride;\n\n  T mul0;\n  T mul1;\n\n  __host__ dim3 getBlocksGrid() const {\n    return dim3(\n        cutlass::ceil_div(input_dim0, kWarpX),\n        cutlass::ceil_div(input_dim1, kWarpY),\n        1);\n  }\n\n  static CUTLASS_HOST_DEVICE dim3 getThreadsGrid() {\n    return dim3(kWarpX / kThreadX, kWarpY / kThreadY, 1);\n  }\n\n  CUTLASS_DEVICE Tile8x8Masks* getCurrentThreadIndices() const {\n    Tile8x8Masks* gmem_threads_masks = (Tile8x8Masks*)threads_masks;\n    gmem_threads_masks += blockIdx.y * getThreadsGrid().y + threadIdx.y;\n    int64_t strideX = gridDim.y * getThreadsGrid().y;\n    gmem_threads_masks +=\n        (blockIdx.x * getThreadsGrid().x + threadIdx.x) * strideX;\n    return gmem_threads_masks;\n  }\n};\n\ntemplate <typename T, bool kInputRowMajor = true, bool kOutputRowMajor = true>\n__global__ void __launch_bounds__(32 /* num_threads */)\n    sparse24_apply_dense_output_k(Params<T> p) {\n  using Fragment = cutlass::Array<T, 8>;\n\n  // Top-left of the 8x8 tile we own\n  int warp_x = blockIdx.x * kWarpX;\n  int warp_y = blockIdx.y * kWarpY;\n  int x = warp_x + threadIdx.x * kThreadX;\n  int y = warp_y + threadIdx.y * kThreadY;\n\n  T* output = p.output + x * p.output_stride + y;\n  Tile8x8Masks indices = *p.getCurrentThreadIndices();\n\n  // Load dense\n  Fragment lines[8];\n  if (kInputRowMajor) {\n    T const* input = p.input + x * p.input_stride + y;\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      cutlass::arch::global_load<Fragment, sizeof(Fragment)>(\n          lines[i], input + i * p.input_stride, true);\n    }\n  } else {\n    T const* input = p.input + x + y * p.input_stride;\n    Fragment columns[8];\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      cutlass::arch::global_load<Fragment, sizeof(Fragment)>(\n          columns[i], input + i * p.input_stride, true);\n    }\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < 8; ++j) {\n        lines[i][j] = columns[j][i].get();\n      }\n    }\n  }\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int row = 0; row < 2; ++row) {\n    Indices4x4 masks[2];\n    if (row == 0) {\n      masks[0] = indices.a;\n      masks[1] = indices.b;\n    } else {\n      masks[0] = indices.c;\n      masks[1] = indices.d;\n    }\n\n    // Apply mask\n    CUTLASS_PRAGMA_UNROLL\n    for (int m = 0; m < 2; ++m) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int r = 0; r < 4; ++r) {\n        CUTLASS_PRAGMA_UNROLL\n        for (int c = 0; c < 4; ++c) {\n          if ((masks[m] >> (4 * r + c)) & 1) {\n            lines[4 * row + r][4 * m + c] =\n                p.mul1 * lines[4 * row + r][4 * m + c];\n          } else {\n            lines[4 * row + r][4 * m + c] =\n                p.mul0 * lines[4 * row + r][4 * m + c];\n          }\n        }\n      }\n    }\n  }\n  static_assert(kOutputRowMajor, \"Transpose here for ColMajor output\");\n  // Save dense with zeros\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 8; ++i) {\n    cutlass::arch::global_store<Fragment, sizeof(Fragment)>(\n        lines[i], output + i * p.output_stride, true);\n  }\n}\n\ntemplate <typename T, bool kIsMeta = false>\ntorch::stable::Tensor sparse24_apply_dense_output_typed(\n    torch::stable::Tensor input,\n    torch::stable::Tensor threads_masks,\n    float mul0,\n    float mul1) {\n  STD_TORCH_CHECK(\n      input.stride(0) == 1 || input.stride(1) == 1,\n      \"`input` should be either RowMajor or ColMajor. Invalid memory layout - try .contiguous()?\");\n\n  auto roundedx = cutlass::round_up(input.size(0), kWarpX);\n  auto roundedy = cutlass::round_up(input.size(1), kWarpY);\n\n  Params<T> p;\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n  if (!kIsMeta) {\n    p.input = (T const*)input.data_ptr();\n    p.threads_masks = (uint64_t const*)threads_masks.data_ptr();\n  }\n\n  STD_TORCH_CHECK(threads_masks.dim() == 3);\n  STD_TORCH_CHECK(\n      threads_masks.size(0) == p.getBlocksGrid().x * p.getThreadsGrid().x);\n  STD_TORCH_CHECK(\n      threads_masks.size(1) == p.getBlocksGrid().y * p.getThreadsGrid().y);\n  STD_TORCH_CHECK(threads_masks.stride(1) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.size(2) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.stride(2) == 1);\n  STD_TORCH_CHECK(\n      threads_masks.scalar_type() == torch::headeronly::ScalarType::Byte);\n\n  torch::stable::Tensor output =\n      torch::stable::new_empty(input, {input.size(0), input.size(1)});\n  STD_TORCH_CHECK(output.stride(-1) == 1, \"expected RowMajor?\");\n  if (kIsMeta) {\n    return output;\n  }\n  p.output = (T*)output.data_ptr();\n  p.mul0 = T(mul0);\n  p.mul1 = T(mul1);\n\n  bool inputRowMajor = input.stride(-1) == 1;\n  bool outputRowMajor = output.stride(-1) == 1;\n  p.input_stride = input.stride(inputRowMajor ? 0 : 1);\n  p.output_stride = output.stride(outputRowMajor ? 0 : 1);\n  torch::stable::accelerator::DeviceGuard device_guard(input.device().index());\n\n  cudaStream_t stream = xf_getCurrentCUDAStream();\n  size_t smem_bytes = 0;\n  if (inputRowMajor && outputRowMajor) {\n    sparse24_apply_dense_output_k<T, true, true>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), smem_bytes, stream>>>(p);\n  } else if (!inputRowMajor && outputRowMajor) {\n    sparse24_apply_dense_output_k<T, false, true>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), smem_bytes, stream>>>(p);\n  } else {\n    STD_TORCH_CHECK(\n        false,\n        \"Unsupported configuration: `input` is \",\n        inputRowMajor ? \"RowMajor\" : \"ColMajor\",\n        \", and `output` is \",\n        outputRowMajor ? \"RowMajor\" : \"ColMajor\");\n  }\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n  return output;\n}\n\ntemplate <bool kIsMeta = false>\ntorch::stable::Tensor sparse24_apply_dense_output(\n    torch::stable::Tensor input,\n    torch::stable::Tensor threads_masks,\n    double mul0,\n    double mul1) {\n  STD_TORCH_CHECK(\n      input.scalar_type() == torch::headeronly::ScalarType::Half ||\n          input.scalar_type() == torch::headeronly::ScalarType::BFloat16,\n      \"Unsupported `input` dtype\");\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return sparse24_apply_dense_output_typed<cutlass::half_t, kIsMeta>(\n        input, threads_masks, mul0, mul1);\n  } else {\n    return sparse24_apply_dense_output_typed<cutlass::bfloat16_t, kIsMeta>(\n        input, threads_masks, mul0, mul1);\n  }\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\n      \"sparse24_apply_dense_output\",\n      TORCH_BOX(sparse24_apply_dense_output<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\n      \"sparse24_apply_dense_output\",\n      TORCH_BOX(sparse24_apply_dense_output<true>));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"compute_sparse_tile.h\"\n#include \"pt_stable_utils.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <typename T>\nstruct Params {\n  uint64_t const* threads_masks;\n\n  T const* input;\n  int64_t input_stride;\n  int64_t input_dim0;\n  int64_t input_dim1;\n\n  T* output;\n  int64_t output_stride;\n\n  T mul0;\n  T mul1;\n\n  __host__ dim3 getBlocksGrid() const {\n    return dim3(\n        cutlass::ceil_div(input_dim0, kWarpX),\n        cutlass::ceil_div(input_dim1, kWarpY),\n        1);\n  }\n\n  static CUTLASS_HOST_DEVICE dim3 getThreadsGrid() {\n    return dim3(kWarpX / kThreadX, kWarpY / kThreadY, 1);\n  }\n\n  CUTLASS_DEVICE Tile8x8Masks* getCurrentThreadIndices() const {\n    Tile8x8Masks* gmem_threads_masks = (Tile8x8Masks*)threads_masks;\n    gmem_threads_masks += blockIdx.y * getThreadsGrid().y + threadIdx.y;\n    int64_t strideX = gridDim.y * getThreadsGrid().y;\n    gmem_threads_masks +=\n        (blockIdx.x * getThreadsGrid().x + threadIdx.x) * strideX;\n    return gmem_threads_masks;\n  }\n};\n\ntemplate <typename T, bool kInputRowMajor = true, bool kOutputRowMajor = true>\n__global__ void __launch_bounds__(32 /* num_threads */)\n    sparse24_apply_dense_output_k(Params<T> p) {\n  using Fragment = cutlass::Array<T, 8>;\n\n  // Top-left of the 8x8 tile we own\n  int warp_x = blockIdx.x * kWarpX;\n  int warp_y = blockIdx.y * kWarpY;\n  int x = warp_x + threadIdx.x * kThreadX;\n  int y = warp_y + threadIdx.y * kThreadY;\n\n  T* output = p.output + x * p.output_stride + y;\n  Tile8x8Masks indices = *p.getCurrentThreadIndices();\n\n  // Load dense\n  Fragment lines[8];\n  if (kInputRowMajor) {\n    T const* input = p.input + x * p.input_stride + y;\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      cutlass::arch::global_load<Fragment, sizeof(Fragment)>(\n          lines[i], input + i * p.input_stride, true);\n    }\n  } else {\n    T const* input = p.input + x + y * p.input_stride;\n    Fragment columns[8];\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      cutlass::arch::global_load<Fragment, sizeof(Fragment)>(\n          columns[i], input + i * p.input_stride, true);\n    }\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < 8; ++i) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < 8; ++j) {\n        lines[i][j] = columns[j][i].get();\n      }\n    }\n  }\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int row = 0; row < 2; ++row) {\n    Indices4x4 masks[2];\n    if (row == 0) {\n      masks[0] = indices.a;\n      masks[1] = indices.b;\n    } else {\n      masks[0] = indices.c;\n      masks[1] = indices.d;\n    }\n\n    // Apply mask\n    CUTLASS_PRAGMA_UNROLL\n    for (int m = 0; m < 2; ++m) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int r = 0; r < 4; ++r) {\n        CUTLASS_PRAGMA_UNROLL\n        for (int c = 0; c < 4; ++c) {\n          if ((masks[m] >> (4 * r + c)) & 1) {\n            lines[4 * row + r][4 * m + c] =\n                p.mul1 * lines[4 * row + r][4 * m + c];\n          } else {\n            lines[4 * row + r][4 * m + c] =\n                p.mul0 * lines[4 * row + r][4 * m + c];\n          }\n        }\n      }\n    }\n  }\n  static_assert(kOutputRowMajor, \"Transpose here for ColMajor output\");\n  // Save dense with zeros\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < 8; ++i) {\n    cutlass::arch::global_store<Fragment, sizeof(Fragment)>(\n        lines[i], output + i * p.output_stride, true);\n  }\n}\n\ntemplate <typename T, bool kIsMeta = false>\ntorch::stable::Tensor sparse24_apply_dense_output_typed(\n    torch::stable::Tensor input,\n    torch::stable::Tensor threads_masks,\n    float mul0,\n    float mul1) {\n  STD_TORCH_CHECK(\n      input.stride(0) == 1 || input.stride(1) == 1,\n      \"`input` should be either RowMajor or ColMajor. Invalid memory layout - try .contiguous()?\");\n\n  auto roundedx = cutlass::round_up(input.size(0), kWarpX);\n  auto roundedy = cutlass::round_up(input.size(1), kWarpY);\n\n  Params<T> p;\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n  if (!kIsMeta) {\n    p.input = (T const*)input.data_ptr();\n    p.threads_masks = (uint64_t const*)threads_masks.data_ptr();\n  }\n\n  STD_TORCH_CHECK(threads_masks.dim() == 3);\n  STD_TORCH_CHECK(\n      threads_masks.size(0) == p.getBlocksGrid().x * p.getThreadsGrid().x);\n  STD_TORCH_CHECK(\n      threads_masks.size(1) == p.getBlocksGrid().y * p.getThreadsGrid().y);\n  STD_TORCH_CHECK(threads_masks.stride(1) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.size(2) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.stride(2) == 1);\n  STD_TORCH_CHECK(\n      threads_masks.scalar_type() == torch::headeronly::ScalarType::Byte);\n\n  torch::stable::Tensor output =\n      torch::stable::new_empty(input, {input.size(0), input.size(1)});\n  STD_TORCH_CHECK(output.stride(-1) == 1, \"expected RowMajor?\");\n  if (kIsMeta) {\n    return output;\n  }\n  p.output = (T*)output.data_ptr();\n  p.mul0 = T(mul0);\n  p.mul1 = T(mul1);\n\n  bool inputRowMajor = input.stride(-1) == 1;\n  bool outputRowMajor = output.stride(-1) == 1;\n  p.input_stride = input.stride(inputRowMajor ? 0 : 1);\n  p.output_stride = output.stride(outputRowMajor ? 0 : 1);\n  torch::stable::accelerator::DeviceGuard device_guard(input.device().index());\n\n  hipStream_t stream = xf_getCurrentCUDAStream();\n  size_t smem_bytes = 0;\n  if (inputRowMajor && outputRowMajor) {\n    sparse24_apply_dense_output_k<T, true, true>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), smem_bytes, stream>>>(p);\n  } else if (!inputRowMajor && outputRowMajor) {\n    sparse24_apply_dense_output_k<T, false, true>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), smem_bytes, stream>>>(p);\n  } else {\n    STD_TORCH_CHECK(\n        false,\n        \"Unsupported configuration: `input` is \",\n        inputRowMajor ? \"RowMajor\" : \"ColMajor\",\n        \", and `output` is \",\n        outputRowMajor ? \"RowMajor\" : \"ColMajor\");\n  }\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n  return output;\n}\n\ntemplate <bool kIsMeta = false>\ntorch::stable::Tensor sparse24_apply_dense_output(\n    torch::stable::Tensor input,\n    torch::stable::Tensor threads_masks,\n    double mul0,\n    double mul1) {\n  STD_TORCH_CHECK(\n      input.scalar_type() == torch::headeronly::ScalarType::Half ||\n          input.scalar_type() == torch::headeronly::ScalarType::BFloat16,\n      \"Unsupported `input` dtype\");\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return sparse24_apply_dense_output_typed<cutlass::half_t, kIsMeta>(\n        input, threads_masks, mul0, mul1);\n  } else {\n    return sparse24_apply_dense_output_typed<cutlass::bfloat16_t, kIsMeta>(\n        input, threads_masks, mul0, mul1);\n  }\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\n      \"sparse24_apply_dense_output\",\n      TORCH_BOX(sparse24_apply_dense_output<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\n      \"sparse24_apply_dense_output\",\n      TORCH_BOX(sparse24_apply_dense_output<true>));\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparse24_apply_dense_output.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n\n#include \"compute_sparse_tile.h\"\n#include \"pt_stable_utils.h\"\n#include \"sparse24_metadata.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <typename KT, typename Metadata, typename Algorithm>\n__global__ void __launch_bounds__(32 /* num_threads */, 20)\n    sparse24_sparsify_both_ways_kernel(\n        typename KT::Params p,\n        Metadata metadata,\n        Algorithm algo) {\n  KT::sparse24_sparsify_both_ways_kernel(p, metadata, algo);\n}\n\ntemplate <typename Element, typename MetadataFormat, bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor, // packed_trans_meta_reordered\n        torch::stable::Tensor // threads_masks\n        >\n    sparse24_sparsify_both_ways_typed(\n        const torch::stable::Tensor input,\n        std::string algorithm) {\n  using KT = KernelTypes<Element>;\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2, \"Can only sparsify 2d tensors\");\n  STD_TORCH_CHECK(\n      input.stride(1) == 1,\n      \"Can only sparsify contiguous tensors. Sparsify the transpose otherwise.\");\n\n  auto rows = input.size(0);\n  auto cols = input.size(1);\n\n  auto [compressed, packed, packed_meta_reordered] =\n      MetadataFormat::create_compressed_representation(rows, cols, input, true);\n  auto [compressed_trans, packed_trans, packed_trans_meta_reordered] =\n      MetadataFormat::create_compressed_representation(cols, rows, input, true);\n  STD_TORCH_CHECK(\n      input.size(1) % 32 == 0, \"Number of cols should be multiple of 32\");\n\n  typename KT::Params p;\n  p.input_s0 = input.stride(0);\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n  p.packed_stride = packed.stride(0);\n  p.packed_trans_stride = packed_trans.stride(0);\n\n  MetadataFormat metadata = MetadataFormat(\n      packed_meta_reordered, packed_trans_meta_reordered, rows, cols);\n  torch::stable::Tensor threads_masks = torch::stable::new_empty(\n      input,\n      {p.getBlocksGrid().x * p.getThreadsGrid().x,\n       p.getBlocksGrid().y * p.getThreadsGrid().y,\n       sizeof(p.threads_masks[0])},\n      torch::headeronly::ScalarType::Byte);\n  if (!kIsMeta) {\n    p.input = (Element const*)input.data_ptr();\n    p.packed = (Element*)packed.data_ptr();\n    p.packed_trans = (Element*)packed_trans.data_ptr();\n    p.threads_masks = (uint64_t*)threads_masks.data_ptr();\n  }\n\n  bool kernel_launched = false;\n  auto launchKernel = [&](auto algo, std::string const& algo_name) {\n    if (algo_name == algorithm) {\n      kernel_launched = true;\n      if (kIsMeta) {\n        return;\n      }\n      size_t smem_bytes = 0;\n      sparse24_sparsify_both_ways_kernel<KT>\n          <<<p.getBlocksGrid(),\n             p.getThreadsGrid(),\n             smem_bytes,\n             xf_getCurrentCUDAStream()>>>(p, metadata, algo);\n    }\n  };\n  named_algorithms(launchKernel);\n  STD_TORCH_CHECK(kernel_launched, \"Unknown algorithm \\\"\", algorithm, \"\\\"\");\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n  return std::make_tuple(\n      compressed,\n      packed_meta_reordered,\n      compressed_trans,\n      packed_trans_meta_reordered,\n      threads_masks);\n}\n\ntemplate <bool kIsMeta = false>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor, // packed_trans_meta_reordered\n        torch::stable::Tensor // threads_masks\n        >\n    sparse24_sparsify_both_ways(\n        const torch::stable::Tensor input,\n        std::string algorithm,\n        std::string backend) {\n  auto runTyped = [&](auto type) {\n    using ElementT = decltype(type);\n    if (backend == \"cusparselt\") {\n      return sparse24_sparsify_both_ways_typed<\n          ElementT,\n          MetadataCuSparseLtSm80,\n          kIsMeta>(input, algorithm);\n    } else {\n      STD_TORCH_CHECK(\n          backend == \"cutlass\",\n          \"backend argument only supports `cutlass` or `cusparselt`\");\n      return sparse24_sparsify_both_ways_typed<\n          ElementT,\n          MetadataCutlassSm80,\n          kIsMeta>(input, algorithm);\n    }\n  };\n\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return runTyped(cutlass::half_t());\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::Half ||\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n    return runTyped(cutlass::bfloat16_t());\n  }\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\n      \"sparse24_sparsify_both_ways\",\n      TORCH_BOX(sparse24_sparsify_both_ways<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\n      \"sparse24_sparsify_both_ways\",\n      TORCH_BOX(sparse24_sparsify_both_ways<true>));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n\n#include \"compute_sparse_tile.h\"\n#include \"pt_stable_utils.h\"\n#include \"sparse24_metadata.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\ntemplate <typename KT, typename Metadata, typename Algorithm>\n__global__ void __launch_bounds__(32 /* num_threads */, 20)\n    sparse24_sparsify_both_ways_kernel(\n        typename KT::Params p,\n        Metadata metadata,\n        Algorithm algo) {\n  KT::sparse24_sparsify_both_ways_kernel(p, metadata, algo);\n}\n\ntemplate <typename Element, typename MetadataFormat, bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor, // packed_trans_meta_reordered\n        torch::stable::Tensor // threads_masks\n        >\n    sparse24_sparsify_both_ways_typed(\n        const torch::stable::Tensor input,\n        std::string algorithm) {\n  using KT = KernelTypes<Element>;\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2, \"Can only sparsify 2d tensors\");\n  STD_TORCH_CHECK(\n      input.stride(1) == 1,\n      \"Can only sparsify contiguous tensors. Sparsify the transpose otherwise.\");\n\n  auto rows = input.size(0);\n  auto cols = input.size(1);\n\n  auto [compressed, packed, packed_meta_reordered] =\n      MetadataFormat::create_compressed_representation(rows, cols, input, true);\n  auto [compressed_trans, packed_trans, packed_trans_meta_reordered] =\n      MetadataFormat::create_compressed_representation(cols, rows, input, true);\n  STD_TORCH_CHECK(\n      input.size(1) % 32 == 0, \"Number of cols should be multiple of 32\");\n\n  typename KT::Params p;\n  p.input_s0 = input.stride(0);\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n  p.packed_stride = packed.stride(0);\n  p.packed_trans_stride = packed_trans.stride(0);\n\n  MetadataFormat metadata = MetadataFormat(\n      packed_meta_reordered, packed_trans_meta_reordered, rows, cols);\n  torch::stable::Tensor threads_masks = torch::stable::new_empty(\n      input,\n      {p.getBlocksGrid().x * p.getThreadsGrid().x,\n       p.getBlocksGrid().y * p.getThreadsGrid().y,\n       sizeof(p.threads_masks[0])},\n      torch::headeronly::ScalarType::Byte);\n  if (!kIsMeta) {\n    p.input = (Element const*)input.data_ptr();\n    p.packed = (Element*)packed.data_ptr();\n    p.packed_trans = (Element*)packed_trans.data_ptr();\n    p.threads_masks = (uint64_t*)threads_masks.data_ptr();\n  }\n\n  bool kernel_launched = false;\n  auto launchKernel = [&](auto algo, std::string const& algo_name) {\n    if (algo_name == algorithm) {\n      kernel_launched = true;\n      if (kIsMeta) {\n        return;\n      }\n      size_t smem_bytes = 0;\n      sparse24_sparsify_both_ways_kernel<KT>\n          <<<p.getBlocksGrid(),\n             p.getThreadsGrid(),\n             smem_bytes,\n             xf_getCurrentCUDAStream()>>>(p, metadata, algo);\n    }\n  };\n  named_algorithms(launchKernel);\n  STD_TORCH_CHECK(kernel_launched, \"Unknown algorithm \\\"\", algorithm, \"\\\"\");\n  STD_CUDA_KERNEL_LAUNCH_CHECK();\n  return std::make_tuple(\n      compressed,\n      packed_meta_reordered,\n      compressed_trans,\n      packed_trans_meta_reordered,\n      threads_masks);\n}\n\ntemplate <bool kIsMeta = false>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor, // packed_trans_meta_reordered\n        torch::stable::Tensor // threads_masks\n        >\n    sparse24_sparsify_both_ways(\n        const torch::stable::Tensor input,\n        std::string algorithm,\n        std::string backend) {\n  auto runTyped = [&](auto type) {\n    using ElementT = decltype(type);\n    if (backend == \"cusparselt\") {\n      return sparse24_sparsify_both_ways_typed<\n          ElementT,\n          MetadataCuSparseLtSm80,\n          kIsMeta>(input, algorithm);\n    } else {\n      STD_TORCH_CHECK(\n          backend == \"cutlass\",\n          \"backend argument only supports `cutlass` or `cusparselt`\");\n      return sparse24_sparsify_both_ways_typed<\n          ElementT,\n          MetadataCutlassSm80,\n          kIsMeta>(input, algorithm);\n    }\n  };\n\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return runTyped(cutlass::half_t());\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::Half ||\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n    return runTyped(cutlass::bfloat16_t());\n  }\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\n      \"sparse24_sparsify_both_ways\",\n      TORCH_BOX(sparse24_sparsify_both_ways<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\n      \"sparse24_sparsify_both_ways\",\n      TORCH_BOX(sparse24_sparsify_both_ways<true>));\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparse24_pack.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"pt_stable_utils.h\"\n#include \"sparse24_metadata.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\n\ntemplate <typename KT>\n__global__ void __launch_bounds__(32 /* num_threads */)\n    sparse24_apply_kernel(typename KT::Params p) {\n  KT::sparse24_apply_kernel(p);\n}\n\n// Apply a 2:4 sparsify pattern computed with\n// `sparse24_sparsify_both_ways_kernel` to another Tensor\ntemplate <typename Element, typename MetadataFormat, bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor // packed_trans_meta_reordered\n        >\n    sparse24_apply_typed(\n        torch::stable::Tensor input, // Tensor to sparsify\n        torch::stable::Tensor\n            threads_masks // Returned by `sparse24_sparsify_both_ways`\n    ) {\n  using KT = KernelTypes<Element>;\n  // TODO: Technically we should be able to deal with that\n  // by running on the transpose of `input` and swapping\n  // `packed` & `packed_t`.\n  // This would require to adapt the `threads_masks` a bit tho.\n  if (input.stride(1) != 1) {\n    input = xf_contiguous(input);\n  }\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2);\n  STD_TORCH_CHECK(input.stride(1) == 1);\n  STD_TORCH_CHECK(input.stride(0) % 8 == 0);\n  STD_TORCH_CHECK(input.size(1) % 32 == 0, \"Wrong alignment shape[1]\");\n\n  auto rows = input.size(0);\n  auto cols = input.size(1);\n\n  auto [compressed, packed, packed_meta_reordered] =\n      MetadataFormat::create_compressed_representation(\n          rows, cols, input, false);\n  auto [compressed_trans, packed_trans, packed_trans_meta_reordered] =\n      MetadataFormat::create_compressed_representation(\n          cols, rows, input, false);\n\n  typename KT::Params p;\n  p.input_s0 = input.stride(0);\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n\n  p.packed_stride = packed.stride(0);\n  p.packed_trans_stride = packed_trans.stride(0);\n\n  if (!kIsMeta) {\n    p.input = (Element const*)input.data_ptr();\n    p.packed = (Element*)packed.data_ptr();\n    p.packed_trans = (Element*)packed_trans.data_ptr();\n    p.threads_masks = (uint64_t*)threads_masks.data_ptr();\n  }\n\n  STD_TORCH_CHECK(threads_masks.dim() == 3);\n  STD_TORCH_CHECK(\n      threads_masks.size(0) == p.getBlocksGrid().x * p.getThreadsGrid().x);\n  STD_TORCH_CHECK(\n      threads_masks.size(1) == p.getBlocksGrid().y * p.getThreadsGrid().y);\n  STD_TORCH_CHECK(threads_masks.stride(1) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.size(2) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.stride(2) == 1);\n  STD_TORCH_CHECK(\n      threads_masks.scalar_type() == torch::headeronly::ScalarType::Byte);\n\n  if (!kIsMeta) {\n    size_t smem_bytes = 0;\n    sparse24_apply_kernel<KT>\n        <<<p.getBlocksGrid(),\n           p.getThreadsGrid(),\n           smem_bytes,\n           xf_getCurrentCUDAStream()>>>(p);\n    STD_CUDA_KERNEL_LAUNCH_CHECK();\n  }\n  return std::make_tuple(\n      compressed,\n      packed_meta_reordered,\n      compressed_trans,\n      packed_trans_meta_reordered);\n}\n\ntemplate <bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor // packed_trans_meta_reordered\n        >\n    sparse24_apply(\n        torch::stable::Tensor input, // Tensor to sparsify\n        torch::stable::Tensor\n            threads_masks, // Returned by `sparse24_sparsify_both_ways`\n        std::string backend) {\n  auto runTyped = [&](auto type) {\n    using ElementT = decltype(type);\n    if (backend == \"cusparselt\") {\n      return sparse24_apply_typed<ElementT, MetadataCuSparseLtSm80, kIsMeta>(\n          input, threads_masks);\n    } else {\n      STD_TORCH_CHECK(\n          backend == \"cutlass\",\n          \"backend argument only supports `cutlass` or `cusparselt`\");\n      return sparse24_apply_typed<ElementT, MetadataCutlassSm80, kIsMeta>(\n          input, threads_masks);\n    }\n  };\n\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return runTyped(cutlass::half_t());\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::Half ||\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n    return runTyped(cutlass::bfloat16_t());\n  }\n}\n\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparse24_apply\", TORCH_BOX(sparse24_apply<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\"sparse24_apply\", TORCH_BOX(sparse24_apply<true>));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/macros.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include \"pt_stable_utils.h\"\n#include \"sparse24_metadata.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\n\ntemplate <typename KT>\n__global__ void __launch_bounds__(32 /* num_threads */)\n    sparse24_apply_kernel(typename KT::Params p) {\n  KT::sparse24_apply_kernel(p);\n}\n\n// Apply a 2:4 sparsify pattern computed with\n// `sparse24_sparsify_both_ways_kernel` to another Tensor\ntemplate <typename Element, typename MetadataFormat, bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor // packed_trans_meta_reordered\n        >\n    sparse24_apply_typed(\n        torch::stable::Tensor input, // Tensor to sparsify\n        torch::stable::Tensor\n            threads_masks // Returned by `sparse24_sparsify_both_ways`\n    ) {\n  using KT = KernelTypes<Element>;\n  // TODO: Technically we should be able to deal with that\n  // by running on the transpose of `input` and swapping\n  // `packed` & `packed_t`.\n  // This would require to adapt the `threads_masks` a bit tho.\n  if (input.stride(1) != 1) {\n    input = xf_contiguous(input);\n  }\n  std::optional<torch::stable::accelerator::DeviceGuard> device_guard;\n  if (!kIsMeta) {\n    device_guard.emplace(input.device().index());\n  }\n\n  STD_TORCH_CHECK(input.dim() == 2);\n  STD_TORCH_CHECK(input.stride(1) == 1);\n  STD_TORCH_CHECK(input.stride(0) % 8 == 0);\n  STD_TORCH_CHECK(input.size(1) % 32 == 0, \"Wrong alignment shape[1]\");\n\n  auto rows = input.size(0);\n  auto cols = input.size(1);\n\n  auto [compressed, packed, packed_meta_reordered] =\n      MetadataFormat::create_compressed_representation(\n          rows, cols, input, false);\n  auto [compressed_trans, packed_trans, packed_trans_meta_reordered] =\n      MetadataFormat::create_compressed_representation(\n          cols, rows, input, false);\n\n  typename KT::Params p;\n  p.input_s0 = input.stride(0);\n  p.input_dim0 = input.size(0);\n  p.input_dim1 = input.size(1);\n\n  p.packed_stride = packed.stride(0);\n  p.packed_trans_stride = packed_trans.stride(0);\n\n  if (!kIsMeta) {\n    p.input = (Element const*)input.data_ptr();\n    p.packed = (Element*)packed.data_ptr();\n    p.packed_trans = (Element*)packed_trans.data_ptr();\n    p.threads_masks = (uint64_t*)threads_masks.data_ptr();\n  }\n\n  STD_TORCH_CHECK(threads_masks.dim() == 3);\n  STD_TORCH_CHECK(\n      threads_masks.size(0) == p.getBlocksGrid().x * p.getThreadsGrid().x);\n  STD_TORCH_CHECK(\n      threads_masks.size(1) == p.getBlocksGrid().y * p.getThreadsGrid().y);\n  STD_TORCH_CHECK(threads_masks.stride(1) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.size(2) == sizeof(p.threads_masks[0]));\n  STD_TORCH_CHECK(threads_masks.stride(2) == 1);\n  STD_TORCH_CHECK(\n      threads_masks.scalar_type() == torch::headeronly::ScalarType::Byte);\n\n  if (!kIsMeta) {\n    size_t smem_bytes = 0;\n    sparse24_apply_kernel<KT>\n        <<<p.getBlocksGrid(),\n           p.getThreadsGrid(),\n           smem_bytes,\n           xf_getCurrentCUDAStream()>>>(p);\n    STD_CUDA_KERNEL_LAUNCH_CHECK();\n  }\n  return std::make_tuple(\n      compressed,\n      packed_meta_reordered,\n      compressed_trans,\n      packed_trans_meta_reordered);\n}\n\ntemplate <bool kIsMeta>\nstd::\n    tuple<\n        torch::stable::Tensor, // packed\n        torch::stable::Tensor, // packed_meta_reordered\n        torch::stable::Tensor, // packed_trans\n        torch::stable::Tensor // packed_trans_meta_reordered\n        >\n    sparse24_apply(\n        torch::stable::Tensor input, // Tensor to sparsify\n        torch::stable::Tensor\n            threads_masks, // Returned by `sparse24_sparsify_both_ways`\n        std::string backend) {\n  auto runTyped = [&](auto type) {\n    using ElementT = decltype(type);\n    if (backend == \"cusparselt\") {\n      return sparse24_apply_typed<ElementT, MetadataCuSparseLtSm80, kIsMeta>(\n          input, threads_masks);\n    } else {\n      STD_TORCH_CHECK(\n          backend == \"cutlass\",\n          \"backend argument only supports `cutlass` or `cusparselt`\");\n      return sparse24_apply_typed<ElementT, MetadataCutlassSm80, kIsMeta>(\n          input, threads_masks);\n    }\n  };\n\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    return runTyped(cutlass::half_t());\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::Half ||\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16);\n    return runTyped(cutlass::bfloat16_t());\n  }\n}\n\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparse24_apply\", TORCH_BOX(sparse24_apply<false>));\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, Meta, m) {\n  m.impl(\"sparse24_apply\", TORCH_BOX(sparse24_apply<true>));\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparse24_apply.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include <cutlass/array.h>\n#include <cutlass/bfloat16.h>\n#include <cutlass/half.h>\n\n#include \"pt_stable_utils.h\"\n#include \"static_sort.h\"\n\ntemplate <typename Element, bool kHasRandom>\nstruct Sp24MaskKernel {\n  // thread-level tiling\n  static constexpr int kNumRows = 4;\n  static constexpr int kNumCols = 4;\n  static constexpr int kMaxPerRow = 2;\n  static constexpr int kMaxPerCol = 2;\n  static constexpr int kTakeMaximumCount = kNumRows * kMaxPerRow;\n  // block-level tiling\n  static constexpr int kRowsPerBlock = 32;\n  static constexpr int kColsPerBlock = 16;\n  static constexpr int kNumThreadsPerBlock =\n      (kRowsPerBlock / kNumRows) * (kRowsPerBlock / kNumRows);\n  static_assert(kNumThreadsPerBlock % 32 == 0, \"invalid warp size\");\n\n  struct PseudoRandomOrder {\n    uint8_t row;\n    uint8_t col;\n    uint16_t random_score;\n\n    CUTLASS_DEVICE bool operator<(PseudoRandomOrder const& other) const {\n      return random_score < other.random_score;\n    }\n  };\n\n  struct ElementWithPos {\n    Element e;\n    uint8_t row;\n    uint8_t col;\n\n    CUTLASS_DEVICE bool operator<(ElementWithPos const& other) const {\n      return e < other.e;\n    }\n  };\n\n  struct Params {\n    // Inputs assumed in RowMajor\n    Element const* input;\n    Element* output;\n    int64_t stride0;\n    int64_t size0;\n    int64_t size1;\n    int8_t numRandom;\n\n    CUTLASS_HOST_DEVICE int _getNumBlocksRows() const {\n      return (size0 + kRowsPerBlock - 1) / kRowsPerBlock;\n    }\n    CUTLASS_HOST_DEVICE int _getNumBlocksCols() const {\n      return (size1 + kColsPerBlock - 1) / kColsPerBlock;\n    }\n    CUTLASS_HOST_DEVICE int getBlocksGrid() const {\n      return _getNumBlocksRows() * _getNumBlocksCols();\n    }\n    CUTLASS_HOST_DEVICE int getThreadsGrid() const {\n      return kNumThreadsPerBlock;\n    }\n  };\n\n  CUTLASS_DEVICE static void run(Params p) {\n    // Block-level position\n    int block_id = blockIdx.x;\n    int thread_row = (block_id % p._getNumBlocksRows()) * kRowsPerBlock;\n    int thread_col = (block_id / p._getNumBlocksRows()) * kColsPerBlock;\n\n    // Thread-level position\n    int thread_id = threadIdx.x;\n    constexpr int kTilingRows = kRowsPerBlock / kNumRows;\n    thread_row += (thread_id % kTilingRows) * kNumRows;\n    thread_col += (thread_id / kTilingRows) * kNumCols;\n\n    bool enabled = thread_row < p.size0 && thread_col < p.size1;\n    if (!enabled) {\n      return;\n    }\n\n    // We operate on a small 4x4 patch per thread\n    using FragmentRow = cutlass::Array<Element, kNumCols>;\n    cutlass::Array<ElementWithPos, kNumCols * kNumRows> allValues;\n    cutlass::Array<cutlass::uint1b_t, kNumCols * kNumRows> allOutputs;\n    allOutputs.clear();\n    uint8_t numOutputsPerRow[kNumRows] = {0};\n    uint8_t numOutputsPerCol[kNumCols] = {0};\n    uint8_t totalAdded = 0;\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int row = 0; row < kNumRows; ++row) {\n      FragmentRow l = *reinterpret_cast<FragmentRow const*>(\n          p.input + thread_col + (thread_row + row) * p.stride0);\n      CUTLASS_PRAGMA_UNROLL\n      for (int col = 0; col < kNumCols; ++col) {\n        allValues[col + row * kNumCols].e = l[col];\n        allValues[col + row * kNumCols].row = row;\n        allValues[col + row * kNumCols].col = col;\n      }\n    }\n\n    // Sort - ascending order\n    StaticSort<kNumCols * kNumRows> sorter;\n    sorter(allValues);\n\n    // Take all the values we can starting with the largest\n    int i = allValues.size() - 1;\n    CUTLASS_PRAGMA_UNROLL\n    for (; i >= 0; --i) {\n      if (kHasRandom && totalAdded + p.numRandom == kTakeMaximumCount) {\n        break;\n      }\n\n      auto const& v = allValues[i];\n      if (numOutputsPerRow[v.row] == kMaxPerRow ||\n          numOutputsPerCol[v.col] == kMaxPerCol) {\n        continue;\n      }\n      numOutputsPerRow[v.row] += 1;\n      numOutputsPerCol[v.col] += 1;\n      totalAdded += 1;\n      allOutputs[v.col + v.row * kNumCols] = cutlass::uint1b_t(1);\n    }\n\n    // Add random elements now\n    if (kHasRandom) {\n      cutlass::Array<PseudoRandomOrder, kNumCols * kNumRows> randomSorting;\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < randomSorting.size(); ++j) {\n        randomSorting[j].row = allValues[j].row;\n        randomSorting[j].col = allValues[j].col;\n        randomSorting[j].random_score = uint16_t(-1);\n        if (j <= i) { // not already considered\n          uint16_t seed = allValues[j].e.storage;\n          // Assume the lower bits of the significant are more random\n          seed = seed & 0x2f;\n          randomSorting[j].random_score = (seed ^ (seed << 1)) & 0x7fff;\n        }\n      }\n      sorter(randomSorting);\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < randomSorting.size(); ++j) {\n        auto const& v = randomSorting[j];\n        if (numOutputsPerRow[v.row] == kMaxPerRow ||\n            numOutputsPerCol[v.col] == kMaxPerCol ||\n            allOutputs[v.col + v.row * kNumCols].get()) {\n          continue;\n        }\n        numOutputsPerRow[v.row] += 1;\n        numOutputsPerCol[v.col] += 1;\n        allOutputs[v.col + v.row * kNumCols] = cutlass::uint1b_t(1);\n      }\n    }\n\n    // Write output\n    CUTLASS_PRAGMA_UNROLL\n    for (int row = 0; row < kNumRows; ++row) {\n      FragmentRow fragmentOut;\n      fragmentOut.clear();\n      CUTLASS_PRAGMA_UNROLL\n      for (int col = 0; col < kNumCols; ++col) {\n        fragmentOut[col] =\n            allOutputs[col + row * kNumCols].get() ? Element(1) : Element(0);\n      }\n      *reinterpret_cast<FragmentRow*>(\n          p.output + thread_col + (thread_row + row) * p.stride0) = fragmentOut;\n    }\n  }\n};\n\ntemplate <typename Kernel>\n__global__ void sparse24_largest_mask_2d_cu(typename Kernel::Params p) {\n  Kernel::run(p);\n}\n\ntemplate <bool kHasRandom>\ntorch::stable::Tensor sparse24_largest_with_random_mask_2d_impl(\n    const torch::stable::Tensor input,\n    int64_t numRandom) {\n  STD_TORCH_CHECK(input.is_cuda(), \"must be a CUDA tensor\");\n  STD_TORCH_CHECK(!xf_is_sparse(input), \"must be a dense tensor\");\n  STD_TORCH_CHECK(input.is_contiguous(), \"must be contiguous\");\n  STD_TORCH_CHECK(input.dim() == 2, \"only works on 2d tensors\");\n  STD_TORCH_CHECK(numRandom <= 8, \"There are at most 4x2 elements\")\n\n  torch::stable::accelerator::DeviceGuard device_guard(input.device().index());\n  cudaStream_t stream = xf_getCurrentCUDAStream();\n  torch::stable::Tensor output = torch::stable::empty_like(input);\n\n  auto runKernel = [&](auto _) {\n    using Element = decltype(_);\n    using Kernel = Sp24MaskKernel<Element, kHasRandom>;\n    typename Kernel::Params p;\n    p.input = (Element*)input.data_ptr();\n    p.output = (Element*)output.data_ptr();\n    p.stride0 = input.stride(0);\n    p.size0 = input.size(0);\n    p.size1 = input.size(1);\n    p.numRandom = numRandom;\n    STD_TORCH_CHECK((input.size(-1) % Kernel::kNumRows) == 0, \"Wrong shape\");\n    STD_TORCH_CHECK((input.size(-2) % Kernel::kNumCols) == 0, \"Wrong shape\");\n\n    sparse24_largest_mask_2d_cu<Kernel>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), 0, stream>>>(p);\n  };\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    runKernel(cutlass::half_t(0));\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16,\n        \"only f16/bf16 supported\");\n    runKernel(cutlass::bfloat16_t(0));\n  }\n  return output;\n}\n\ntorch::stable::Tensor sparse24_largest_mask_2d(\n    const torch::stable::Tensor input) {\n  return sparse24_largest_with_random_mask_2d_impl<false>(input, 0);\n}\n\ntorch::stable::Tensor sparse24_largest_with_Krandom_mask_2d(\n    const torch::stable::Tensor input,\n    int64_t numRandom) {\n  return sparse24_largest_with_random_mask_2d_impl<true>(input, numRandom);\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparse24_largest_mask_2d\", TORCH_BOX(sparse24_largest_mask_2d));\n  m.impl(\n      \"sparse24_largest_with_Krandom_mask_2d\",\n      TORCH_BOX(sparse24_largest_with_Krandom_mask_2d));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/ScalarType.h>\n\n#include <cutlass/array.h>\n#include <cutlass/bfloat16.h>\n#include <cutlass/half.h>\n\n#include \"pt_stable_utils.h\"\n#include \"static_sort.h\"\n\ntemplate <typename Element, bool kHasRandom>\nstruct Sp24MaskKernel {\n  // thread-level tiling\n  static constexpr int kNumRows = 4;\n  static constexpr int kNumCols = 4;\n  static constexpr int kMaxPerRow = 2;\n  static constexpr int kMaxPerCol = 2;\n  static constexpr int kTakeMaximumCount = kNumRows * kMaxPerRow;\n  // block-level tiling\n  static constexpr int kRowsPerBlock = 32;\n  static constexpr int kColsPerBlock = 16;\n  static constexpr int kNumThreadsPerBlock =\n      (kRowsPerBlock / kNumRows) * (kRowsPerBlock / kNumRows);\n  static_assert(kNumThreadsPerBlock % 32 == 0, \"invalid warp size\");\n\n  struct PseudoRandomOrder {\n    uint8_t row;\n    uint8_t col;\n    uint16_t random_score;\n\n    CUTLASS_DEVICE bool operator<(PseudoRandomOrder const& other) const {\n      return random_score < other.random_score;\n    }\n  };\n\n  struct ElementWithPos {\n    Element e;\n    uint8_t row;\n    uint8_t col;\n\n    CUTLASS_DEVICE bool operator<(ElementWithPos const& other) const {\n      return e < other.e;\n    }\n  };\n\n  struct Params {\n    // Inputs assumed in RowMajor\n    Element const* input;\n    Element* output;\n    int64_t stride0;\n    int64_t size0;\n    int64_t size1;\n    int8_t numRandom;\n\n    CUTLASS_HOST_DEVICE int _getNumBlocksRows() const {\n      return (size0 + kRowsPerBlock - 1) / kRowsPerBlock;\n    }\n    CUTLASS_HOST_DEVICE int _getNumBlocksCols() const {\n      return (size1 + kColsPerBlock - 1) / kColsPerBlock;\n    }\n    CUTLASS_HOST_DEVICE int getBlocksGrid() const {\n      return _getNumBlocksRows() * _getNumBlocksCols();\n    }\n    CUTLASS_HOST_DEVICE int getThreadsGrid() const {\n      return kNumThreadsPerBlock;\n    }\n  };\n\n  CUTLASS_DEVICE static void run(Params p) {\n    // Block-level position\n    int block_id = blockIdx.x;\n    int thread_row = (block_id % p._getNumBlocksRows()) * kRowsPerBlock;\n    int thread_col = (block_id / p._getNumBlocksRows()) * kColsPerBlock;\n\n    // Thread-level position\n    int thread_id = threadIdx.x;\n    constexpr int kTilingRows = kRowsPerBlock / kNumRows;\n    thread_row += (thread_id % kTilingRows) * kNumRows;\n    thread_col += (thread_id / kTilingRows) * kNumCols;\n\n    bool enabled = thread_row < p.size0 && thread_col < p.size1;\n    if (!enabled) {\n      return;\n    }\n\n    // We operate on a small 4x4 patch per thread\n    using FragmentRow = cutlass::Array<Element, kNumCols>;\n    cutlass::Array<ElementWithPos, kNumCols * kNumRows> allValues;\n    cutlass::Array<cutlass::uint1b_t, kNumCols * kNumRows> allOutputs;\n    allOutputs.clear();\n    uint8_t numOutputsPerRow[kNumRows] = {0};\n    uint8_t numOutputsPerCol[kNumCols] = {0};\n    uint8_t totalAdded = 0;\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int row = 0; row < kNumRows; ++row) {\n      FragmentRow l = *reinterpret_cast<FragmentRow const*>(\n          p.input + thread_col + (thread_row + row) * p.stride0);\n      CUTLASS_PRAGMA_UNROLL\n      for (int col = 0; col < kNumCols; ++col) {\n        allValues[col + row * kNumCols].e = l[col];\n        allValues[col + row * kNumCols].row = row;\n        allValues[col + row * kNumCols].col = col;\n      }\n    }\n\n    // Sort - ascending order\n    StaticSort<kNumCols * kNumRows> sorter;\n    sorter(allValues);\n\n    // Take all the values we can starting with the largest\n    int i = allValues.size() - 1;\n    CUTLASS_PRAGMA_UNROLL\n    for (; i >= 0; --i) {\n      if (kHasRandom && totalAdded + p.numRandom == kTakeMaximumCount) {\n        break;\n      }\n\n      auto const& v = allValues[i];\n      if (numOutputsPerRow[v.row] == kMaxPerRow ||\n          numOutputsPerCol[v.col] == kMaxPerCol) {\n        continue;\n      }\n      numOutputsPerRow[v.row] += 1;\n      numOutputsPerCol[v.col] += 1;\n      totalAdded += 1;\n      allOutputs[v.col + v.row * kNumCols] = cutlass::uint1b_t(1);\n    }\n\n    // Add random elements now\n    if (kHasRandom) {\n      cutlass::Array<PseudoRandomOrder, kNumCols * kNumRows> randomSorting;\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < randomSorting.size(); ++j) {\n        randomSorting[j].row = allValues[j].row;\n        randomSorting[j].col = allValues[j].col;\n        randomSorting[j].random_score = uint16_t(-1);\n        if (j <= i) { // not already considered\n          uint16_t seed = allValues[j].e.storage;\n          // Assume the lower bits of the significant are more random\n          seed = seed & 0x2f;\n          randomSorting[j].random_score = (seed ^ (seed << 1)) & 0x7fff;\n        }\n      }\n      sorter(randomSorting);\n      CUTLASS_PRAGMA_UNROLL\n      for (int j = 0; j < randomSorting.size(); ++j) {\n        auto const& v = randomSorting[j];\n        if (numOutputsPerRow[v.row] == kMaxPerRow ||\n            numOutputsPerCol[v.col] == kMaxPerCol ||\n            allOutputs[v.col + v.row * kNumCols].get()) {\n          continue;\n        }\n        numOutputsPerRow[v.row] += 1;\n        numOutputsPerCol[v.col] += 1;\n        allOutputs[v.col + v.row * kNumCols] = cutlass::uint1b_t(1);\n      }\n    }\n\n    // Write output\n    CUTLASS_PRAGMA_UNROLL\n    for (int row = 0; row < kNumRows; ++row) {\n      FragmentRow fragmentOut;\n      fragmentOut.clear();\n      CUTLASS_PRAGMA_UNROLL\n      for (int col = 0; col < kNumCols; ++col) {\n        fragmentOut[col] =\n            allOutputs[col + row * kNumCols].get() ? Element(1) : Element(0);\n      }\n      *reinterpret_cast<FragmentRow*>(\n          p.output + thread_col + (thread_row + row) * p.stride0) = fragmentOut;\n    }\n  }\n};\n\ntemplate <typename Kernel>\n__global__ void sparse24_largest_mask_2d_cu(typename Kernel::Params p) {\n  Kernel::run(p);\n}\n\ntemplate <bool kHasRandom>\ntorch::stable::Tensor sparse24_largest_with_random_mask_2d_impl(\n    const torch::stable::Tensor input,\n    int64_t numRandom) {\n  STD_TORCH_CHECK(input.is_cuda(), \"must be a CUDA tensor\");\n  STD_TORCH_CHECK(!xf_is_sparse(input), \"must be a dense tensor\");\n  STD_TORCH_CHECK(input.is_contiguous(), \"must be contiguous\");\n  STD_TORCH_CHECK(input.dim() == 2, \"only works on 2d tensors\");\n  STD_TORCH_CHECK(numRandom <= 8, \"There are at most 4x2 elements\")\n\n  torch::stable::accelerator::DeviceGuard device_guard(input.device().index());\n  hipStream_t stream = xf_getCurrentCUDAStream();\n  torch::stable::Tensor output = torch::stable::empty_like(input);\n\n  auto runKernel = [&](auto _) {\n    using Element = decltype(_);\n    using Kernel = Sp24MaskKernel<Element, kHasRandom>;\n    typename Kernel::Params p;\n    p.input = (Element*)input.data_ptr();\n    p.output = (Element*)output.data_ptr();\n    p.stride0 = input.stride(0);\n    p.size0 = input.size(0);\n    p.size1 = input.size(1);\n    p.numRandom = numRandom;\n    STD_TORCH_CHECK((input.size(-1) % Kernel::kNumRows) == 0, \"Wrong shape\");\n    STD_TORCH_CHECK((input.size(-2) % Kernel::kNumCols) == 0, \"Wrong shape\");\n\n    sparse24_largest_mask_2d_cu<Kernel>\n        <<<p.getBlocksGrid(), p.getThreadsGrid(), 0, stream>>>(p);\n  };\n  if (input.scalar_type() == torch::headeronly::ScalarType::Half) {\n    runKernel(cutlass::half_t(0));\n  } else {\n    STD_TORCH_CHECK(\n        input.scalar_type() == torch::headeronly::ScalarType::BFloat16,\n        \"only f16/bf16 supported\");\n    runKernel(cutlass::bfloat16_t(0));\n  }\n  return output;\n}\n\ntorch::stable::Tensor sparse24_largest_mask_2d(\n    const torch::stable::Tensor input) {\n  return sparse24_largest_with_random_mask_2d_impl<false>(input, 0);\n}\n\ntorch::stable::Tensor sparse24_largest_with_Krandom_mask_2d(\n    const torch::stable::Tensor input,\n    int64_t numRandom) {\n  return sparse24_largest_with_random_mask_2d_impl<true>(input, numRandom);\n}\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"sparse24_largest_mask_2d\", TORCH_BOX(sparse24_largest_mask_2d));\n  m.impl(\n      \"sparse24_largest_with_Krandom_mask_2d\",\n      TORCH_BOX(sparse24_largest_with_Krandom_mask_2d));\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparse24_largest_mask_2d.cu"}
{"input": "#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/TensorAccessor.h>\n\n#include \"pt_stable_utils.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\n__global__ void meta_shuffle_test_kernel(\n    torch::headeronly::HeaderOnlyGenericPackedTensorAccessor<int64_t, 3>\n        local_meta,\n    torch::headeronly::HeaderOnlyGenericPackedTensorAccessor<int64_t, 3>\n        final_meta,\n    bool transpose) {\n  uint32_t meta_ab = 0;\n  uint32_t meta_cd = 0;\n  for (int i = 0; i < 4; ++i) {\n    meta_ab |= uint8b_t(uint32_t(local_meta[threadIdx.x][threadIdx.y][i]))\n        << (8 * i);\n    meta_cd |= uint8b_t(uint32_t(local_meta[threadIdx.x][threadIdx.y][4 + i]))\n        << (8 * i);\n  }\n  final_meta[threadIdx.x][threadIdx.y][0] =\n      warp_shuffle_meta(meta_ab, transpose);\n  final_meta[threadIdx.x][threadIdx.y][1] =\n      warp_shuffle_meta(meta_cd, transpose);\n}\n\ntorch::stable::Tensor _sparse24_meta_shuffle_test(\n    torch::stable::Tensor local_meta,\n    bool transpose) {\n  auto threads_grid = KernelTypes<cutlass::half_t>::Params::getThreadsGrid();\n\n  STD_TORCH_CHECK(\n      local_meta.scalar_type() == torch::headeronly::ScalarType::Long);\n  STD_TORCH_CHECK(local_meta.dim() == 3);\n  STD_TORCH_CHECK(local_meta.size(0) == threads_grid.x);\n  STD_TORCH_CHECK(local_meta.size(1) == threads_grid.y);\n  STD_TORCH_CHECK(local_meta.size(2) == kThreadY);\n  torch::stable::accelerator::DeviceGuard device_guard(\n      local_meta.device().index());\n  cudaStream_t stream = xf_getCurrentCUDAStream();\n\n  torch::stable::Tensor final_meta = xf_zeros(\n      {threads_grid.x, threads_grid.y, 2},\n      local_meta.scalar_type(),\n      local_meta.device());\n  size_t smem_bytes = 0;\n  meta_shuffle_test_kernel<<<1, threads_grid, smem_bytes, stream>>>(\n      xf_packed_accessor<int64_t, 3>(local_meta),\n      xf_packed_accessor<int64_t, 3>(final_meta),\n      transpose);\n  return final_meta;\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"_sparse24_meta_shuffle_test\", TORCH_BOX(_sparse24_meta_shuffle_test));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <torch/csrc/stable/accelerator.h>\n#include <torch/csrc/stable/device.h>\n#include <torch/csrc/stable/library.h>\n#include <torch/csrc/stable/ops.h>\n#include <torch/csrc/stable/tensor.h>\n#include <torch/headeronly/core/TensorAccessor.h>\n\n#include \"pt_stable_utils.h\"\n#include \"sparse24_pack.h\"\n\nusing namespace xformers::sp24;\n\nnamespace {\n__global__ void meta_shuffle_test_kernel(\n    torch::headeronly::HeaderOnlyGenericPackedTensorAccessor<int64_t, 3>\n        local_meta,\n    torch::headeronly::HeaderOnlyGenericPackedTensorAccessor<int64_t, 3>\n        final_meta,\n    bool transpose) {\n  uint32_t meta_ab = 0;\n  uint32_t meta_cd = 0;\n  for (int i = 0; i < 4; ++i) {\n    meta_ab |= uint8b_t(uint32_t(local_meta[threadIdx.x][threadIdx.y][i]))\n        << (8 * i);\n    meta_cd |= uint8b_t(uint32_t(local_meta[threadIdx.x][threadIdx.y][4 + i]))\n        << (8 * i);\n  }\n  final_meta[threadIdx.x][threadIdx.y][0] =\n      warp_shuffle_meta(meta_ab, transpose);\n  final_meta[threadIdx.x][threadIdx.y][1] =\n      warp_shuffle_meta(meta_cd, transpose);\n}\n\ntorch::stable::Tensor _sparse24_meta_shuffle_test(\n    torch::stable::Tensor local_meta,\n    bool transpose) {\n  auto threads_grid = KernelTypes<cutlass::half_t>::Params::getThreadsGrid();\n\n  STD_TORCH_CHECK(\n      local_meta.scalar_type() == torch::headeronly::ScalarType::Long);\n  STD_TORCH_CHECK(local_meta.dim() == 3);\n  STD_TORCH_CHECK(local_meta.size(0) == threads_grid.x);\n  STD_TORCH_CHECK(local_meta.size(1) == threads_grid.y);\n  STD_TORCH_CHECK(local_meta.size(2) == kThreadY);\n  torch::stable::accelerator::DeviceGuard device_guard(\n      local_meta.device().index());\n  hipStream_t stream = xf_getCurrentCUDAStream();\n\n  torch::stable::Tensor final_meta = xf_zeros(\n      {threads_grid.x, threads_grid.y, 2},\n      local_meta.scalar_type(),\n      local_meta.device());\n  size_t smem_bytes = 0;\n  meta_shuffle_test_kernel<<<1, threads_grid, smem_bytes, stream>>>(\n      xf_packed_accessor<int64_t, 3>(local_meta),\n      xf_packed_accessor<int64_t, 3>(final_meta),\n      transpose);\n  return final_meta;\n}\n} // namespace\n\nSTABLE_TORCH_LIBRARY_IMPL(xformers, CUDA, m) {\n  m.impl(\"_sparse24_meta_shuffle_test\", TORCH_BOX(_sparse24_meta_shuffle_test));\n}\n", "source": "/kaggle/working/xformers/xformers/csrc/sparse24/sparse24_pack_test.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <torch/torch.h>\n\n/* Includes, cuda */\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\n// includes cublaslt\n#include <cublasLt.h>\n#endif\n// constants for fused bias+relu kernel\n#define BIAS_RELU_FW_NTHREADS 128    // forward number of thread per block\n#define BIAS_RELU_BW_NTHREADS_X 32   // backward number of thread in feature dim\n#define BIAS_RELU_BW_NTHREADS_Y 16   // backward number of thread in batch dim\n#define BIAS_RELU_RED_PER_THREAD 16  // backward minimal reduction length per thread\n\n// move to a header later on\n#define ILP 4\ntemplate <typename T>\n__host__ __device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, volatile T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\ntemplate <typename T>\n__device__ __forceinline__ void load_store(volatile T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\n// Keep ReLU in float only. When using half, cast to float before calling.\n__device__ __inline__ float relu(float a) {\n  float retf = max(a, 0.f);\n  return (retf);\n}\n\n// Keep Sigmoid in float only. When using half, cast to float before calling.\n__device__ __inline__ float sigmoid(float a) {\n  float retf = 1.f / (1.f + expf(-a));\n  return (retf);\n}\n\n// FP64 Wrapper around cublas GEMMEx\ncublasStatus_t mlp_gemm(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                        float* alpha, const double* A, int lda, const double* B, int ldb, const float* beta, double* C,\n                        int ldc) {\n  return cublasGemmEx(handle, transa, transb, m, n, k, alpha, A, CUDA_R_64F, lda, B, CUDA_R_64F, ldb, beta, C,\n                      CUDA_R_64F, ldc, CUDA_R_64F, CUBLAS_GEMM_DEFAULT);\n}\n\n// FP32 Wrapper around cublas GEMMEx\ncublasStatus_t mlp_gemm(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                        float* alpha, const float* A, int lda, const float* B, int ldb, const float* beta, float* C,\n                        int ldc) {\n  return cublasGemmEx(handle, transa, transb, m, n, k, alpha, A, CUDA_R_32F, lda, B, CUDA_R_32F, ldb, beta, C,\n                      CUDA_R_32F, ldc, CUDA_R_32F, CUBLAS_GEMM_DEFAULT);\n}\n\n// FP16 Tensor core wrapper around cublas GEMMEx\ncublasStatus_t mlp_gemm(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                        float* alpha, const at::Half* A, int lda, const at::Half* B, int ldb, float* beta, at::Half* C,\n                        int ldc) {\n  return cublasGemmEx(handle, transa, transb, m, n, k, alpha, A, CUDA_R_16F, lda, B, CUDA_R_16F, ldb, beta, C,\n                      CUDA_R_16F, ldc, CUDA_R_32F, CUBLAS_GEMM_DEFAULT_TENSOR_OP);\n}\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\nint mlp_gemm_lt(cublasLtHandle_t ltHandle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                        /* host pointer */\n                const at::Half* A, int lda, const at::Half* B, int ldb, float* beta, /* host pointer */\n                at::Half* C, int ldc, void* workspace, size_t workspaceSize, cudaStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  cublasStatus_t status = CUBLAS_STATUS_SUCCESS;\n\n  cublasLtMatmulDescOpaque_t operationDesc = {};\n  cublasLtMatrixLayoutOpaque_t Adesc = {}, Bdesc = {}, Cdesc = {};\n  cublasLtMatmulPreferenceOpaque_t preference = {};\n\n  int returnedResults = 0;\n  cublasLtMatmulHeuristicResult_t heuristicResult = {};\n  cublasLtEpilogue_t epilogue = CUBLASLT_EPILOGUE_DEFAULT;\n\n  // Create operation descriptor; see cublasLtMatmulDescAttributes_t\n  // for details about defaults; here we just set the transforms for\n  // A and B.\n  status = cublasLtMatmulDescInit(&operationDesc, CUBLAS_COMPUTE_32F, CUDA_R_32F);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_TRANSA, &transa, sizeof(transa));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_TRANSB, &transb, sizeof(transa));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (use_bias) {\n    status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_BIAS_POINTER, &bias, sizeof(bias));\n    if (status != CUBLAS_STATUS_SUCCESS) {\n      goto CLEANUP;\n    }\n    if (use_relu) {\n      epilogue = CUBLASLT_EPILOGUE_RELU_BIAS;\n    } else {\n      epilogue = CUBLASLT_EPILOGUE_BIAS;\n    }\n  } else {\n    if (use_relu) {\n      epilogue = CUBLASLT_EPILOGUE_RELU;\n    }\n  }\n\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE, &epilogue, sizeof(epilogue));\n  if (status != CUBLAS_STATUS_SUCCESS) {\n    goto CLEANUP;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  status =\n      cublasLtMatrixLayoutInit(&Adesc, CUDA_R_16F, transa == CUBLAS_OP_N ? m : k, transa == CUBLAS_OP_N ? k : m, lda);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status =\n      cublasLtMatrixLayoutInit(&Bdesc, CUDA_R_16F, transb == CUBLAS_OP_N ? k : n, transb == CUBLAS_OP_N ? n : k, ldb);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatrixLayoutInit(&Cdesc, CUDA_R_16F, m, n, ldc);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // Create preference handle; In general, extra attributes can be\n  // used here to disable tensor ops or to make sure algo selected\n  // will work with badly aligned A, B, C. However, for simplicity\n  // here we assume A,B,C are always well aligned (e.g., directly\n  // come from cudaMalloc)\n  status = cublasLtMatmulPreferenceInit(&preference);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulPreferenceSetAttribute(&preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize,\n                                                sizeof(workspaceSize));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // We just need the best available heuristic to try and run matmul.\n  // There is no guarantee that this will work. For example, if A is\n  // badly aligned, you can request more (e.g. 32) algos and try to\n  // run them one by one until something works.\n  status = cublasLtMatmulAlgoGetHeuristic(ltHandle, &operationDesc, &Adesc, &Bdesc, &Cdesc, &Cdesc, &preference, 1,\n                                          &heuristicResult, &returnedResults);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (returnedResults == 0) {\n    status = CUBLAS_STATUS_NOT_SUPPORTED;\n    goto CLEANUP;\n  }\n  status = cublasLtMatmul(ltHandle, &operationDesc, alpha, A, &Adesc, B, &Bdesc, beta, C, &Cdesc, C, &Cdesc,\n                          &heuristicResult.algo, workspace, workspaceSize, stream);\n\nCLEANUP:\n  // Descriptors are no longer needed as all GPU work was already\n  // enqueued.\n  return status == CUBLAS_STATUS_SUCCESS ? 0 : 1;\n}\n\nint mlp_gemm_lt(cublasLtHandle_t ltHandle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                    /* host pointer */\n                const double* A, int lda, const double* B, int ldb, float* beta, /* host pointer */\n                double* C, int ldc, void* workspace, size_t workspaceSize, cudaStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  return 1;\n}\n\nint mlp_gemm_lt(cublasLtHandle_t ltHandle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                  /* host pointer */\n                const float* A, int lda, const float* B, int ldb, float* beta, /* host pointer */\n                float* C, int ldc, void* workspace, size_t workspaceSize, cudaStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  cublasStatus_t status = CUBLAS_STATUS_SUCCESS;\n\n  cublasLtMatmulDescOpaque_t operationDesc = {};\n  cublasLtMatrixLayoutOpaque_t Adesc = {}, Bdesc = {}, Cdesc = {};\n  cublasLtMatmulPreferenceOpaque_t preference = {};\n\n  int returnedResults = 0;\n  cublasLtMatmulHeuristicResult_t heuristicResult = {};\n  cublasLtEpilogue_t epilogue = CUBLASLT_EPILOGUE_DEFAULT;\n\n  // Create operation descriptor; see cublasLtMatmulDescAttributes_t\n  // for details about defaults; here we just set the transforms for\n  // A and B.\n  status = cublasLtMatmulDescInit(&operationDesc, CUBLAS_COMPUTE_32F, CUDA_R_32F);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_TRANSA, &transa, sizeof(transa));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_TRANSB, &transb, sizeof(transa));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (use_bias) {\n    status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_BIAS_POINTER, &bias, sizeof(bias));\n    if (status != CUBLAS_STATUS_SUCCESS) {\n      goto CLEANUP;\n    }\n    if (use_relu) {\n      epilogue = CUBLASLT_EPILOGUE_RELU_BIAS;\n    } else {\n      epilogue = CUBLASLT_EPILOGUE_BIAS;\n    }\n  } else {\n    if (use_relu) {\n      epilogue = CUBLASLT_EPILOGUE_RELU;\n    }\n  }\n\n  status = cublasLtMatmulDescSetAttribute(&operationDesc, CUBLASLT_MATMUL_DESC_EPILOGUE, &epilogue, sizeof(epilogue));\n  if (status != CUBLAS_STATUS_SUCCESS) {\n    goto CLEANUP;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  status =\n      cublasLtMatrixLayoutInit(&Adesc, CUDA_R_32F, transa == CUBLAS_OP_N ? m : k, transa == CUBLAS_OP_N ? k : m, lda);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status =\n      cublasLtMatrixLayoutInit(&Bdesc, CUDA_R_32F, transb == CUBLAS_OP_N ? k : n, transb == CUBLAS_OP_N ? n : k, ldb);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatrixLayoutInit(&Cdesc, CUDA_R_32F, m, n, ldc);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // Create preference handle; In general, extra attributes can be\n  // used here to disable tensor ops or to make sure algo selected\n  // will work with badly aligned A, B, C. However, for simplicity\n  // here we assume A,B,C are always well aligned (e.g., directly\n  // come from cudaMalloc)\n  status = cublasLtMatmulPreferenceInit(&preference);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatmulPreferenceSetAttribute(&preference, CUBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize,\n                                                sizeof(workspaceSize));\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // We just need the best available heuristic to try and run matmul.\n  // There is no guarantee that this will work. For example, if A is\n  // badly aligned, you can request more (e.g. 32) algos and try to\n  // run them one by one until something works.\n  status = cublasLtMatmulAlgoGetHeuristic(ltHandle, &operationDesc, &Adesc, &Bdesc, &Cdesc, &Cdesc, &preference, 1,\n                                          &heuristicResult, &returnedResults);\n  if (status != CUBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (returnedResults == 0) {\n    status = CUBLAS_STATUS_NOT_SUPPORTED;\n    goto CLEANUP;\n  }\n\n  status = cublasLtMatmul(ltHandle, &operationDesc, alpha, A, &Adesc, B, &Bdesc, beta, C, &Cdesc, C, &Cdesc,\n                          &heuristicResult.algo, workspace, workspaceSize, stream);\n\nCLEANUP:\n  // Descriptors are no longer needed as all GPU work was already\n  // enqueued.\n  return status == CUBLAS_STATUS_SUCCESS ? 0 : 1;\n}\n#endif\n\n// Bias ADD. Assume input X is [features x batch size], column major.\n// Bias is one 'features' long vector, with implicit broadcast.\ntemplate <typename T>\n__global__ void biasAdd_fprop(T* X, T* b, uint batch_size, uint features) {\n  T r_x[ILP];\n  T r_b[ILP];\n  if (is_aligned(X) && is_aligned(b) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      int row = tid % (features / ILP);\n      load_store(r_x, X, 0, tid);\n      load_store(r_b, b, 0, row);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = bias_sum;\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          int row = tid % features;\n          r_x[ii] = X[idx];\n          r_b[ii] = b[row];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = bias_sum;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// Bias ADD + ReLU. Assume input X is [features x batch size], column major.\n// Activation support fuesed ReLU. Safe to call in-place.\ntemplate <typename T>\n__global__ void biasAddRelu_fprop(T* X, T* b, uint batch_size, uint features) {\n  T r_x[ILP];\n  T r_b[ILP];\n  if (is_aligned(X) && is_aligned(b) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      int row = tid % (features / ILP);\n      load_store(r_x, X, 0, tid);\n      load_store(r_b, b, 0, row);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = relu(bias_sum);\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          int row = tid % features;\n          r_x[ii] = X[idx];\n          r_b[ii] = b[row];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = relu(bias_sum);\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// ReLU. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Relu_fprop(T* X, uint batch_size, uint features) {\n  T r_x[ILP];\n  if (is_aligned(X) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_x, X, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = relu(static_cast<float>(r_x[ii]));\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_x[ii] = X[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = relu(static_cast<float>(r_x[ii]));\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// Sigmoid. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Sigmoid_fprop(T* X, uint batch_size, uint features) {\n  T r_x[ILP];\n  if (is_aligned(X) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_x, X, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = sigmoid(static_cast<float>(r_x[ii]));\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_x[ii] = X[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = sigmoid(static_cast<float>(r_x[ii]));\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// ReLU. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Relu_bprop(T* dY, T* Y, uint batch_size, uint features, T* dX) {\n  T r_dy[ILP];\n  T r_y[ILP];\n  if (is_aligned(dY) && is_aligned(Y) && is_aligned(dX) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_dy, dY, 0, tid);\n      load_store(r_y, Y, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      }\n      load_store(dX, r_dy, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_dy[ii] = dY[idx];\n          r_y[ii] = Y[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          dX[idx] = r_dy[ii];\n        }\n      }\n    }\n  }\n}\n\n// Sigmoid. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Sigmoid_bprop(T* dY, T* Y, uint batch_size, uint features, T* dX) {\n  T r_dy[ILP];\n  T r_y[ILP];\n  if (is_aligned(dY) && is_aligned(Y) && is_aligned(dX) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_dy, dY, 0, tid);\n      load_store(r_y, Y, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float grad_out = r_dy[ii];\n        float out = r_y[ii];\n        float grad_i = out * (1.f - out) * grad_out;\n        r_dy[ii] = grad_i;\n      }\n      load_store(dX, r_dy, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_dy[ii] = dY[idx];\n          r_y[ii] = Y[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float grad_out = r_dy[ii];\n        float out = r_y[ii];\n        float grad_i = out * (1.f - out) * grad_out;\n        r_dy[ii] = grad_i;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          dX[idx] = r_dy[ii];\n        }\n      }\n    }\n  }\n}\n\n// Compute grid size for pointwise backward kernel.\n// block_x/y is total elment being handled per block, not number of threads\nvoid get_biasAddRelu_bprop_grid_size(int yfeat, int batch_size, int block_x, int block_y, int* grid_x, int* grid_y) {\n  *grid_x = (yfeat + block_x - 1) / block_x;\n  // Get number of SMs for efficient reduction.\n  int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n  // can switch to occupancy calculation. use 4 below now for sm_70\n  int max_blocks_y = (num_SMs * 4 + (*grid_x) - 1) / (*grid_x);\n  // block_y should be from minimal work per thread\n  int nRedSplits = (batch_size + block_y - 1) / block_y;\n  // increase number of elem per thread redcution to not launch more than enough\n  // kernel adjust work, so here we just launch max block\n  *grid_y = std::min(nRedSplits, max_blocks_y);\n  return;\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAdd_bprop(T* dY, int features, int batch_size, volatile float* intermediate, int* semaphores,\n                              T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y];\n\n  // Accumulate db in FP32 always\n  float db_local = 0;\n  if (f < features) {\n    int nidx = 0;\n    // Handle non-multiple of UNROLL_FACTOR residue\n    for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n      int64_t row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n      db_local += (float)dY[flat_idx];\n    }\n\n    // Handle meat of work\n    for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n      int64_t row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n#pragma unroll 4\n      for (int u = 0; u < UNROLL_FACTOR; u++) {\n        db_local += (float)dY[flat_idx];\n        flat_idx += features;\n      }\n    }\n\n    // naive block reduction on y-dim\n    int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n    smem[linear_idx] = db_local;\n  }\n  __syncthreads();\n  if (f < features) {\n    if (threadIdx.y == 0) {\n      for (int yidx = 1; yidx < blockDim.y; yidx++) {\n        db_local += smem[yidx * blockDim.x + threadIdx.x];\n      }\n\n      // block result is in db_local now for all threadIdx.y == 0\n      // Write out partial result\n      out[f] = db_local;\n    }\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0 && f < features) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n  db_local = 0;\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock && f < features) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        db_local += (float)(intermediate[col * features + row]);\n      }\n      db[f] = (T)db_local;\n    }\n  }\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAddRelu_bprop(T* Y, T* dY, int features, int batch_size, T* dX, volatile float* intermediate,\n                                  int* semaphores, T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y];\n\n  // Accumulate db in FP32 always\n  float db_local = 0;\n  if (f < features) {\n    int nidx = 0;\n    // Handle non-multiple of UNROLL_FACTOR residue\n    for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n      int row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n      T y_val = Y[flat_idx];\n      T dy_val = dY[flat_idx];\n      T dx_val;\n      if ((float)y_val > 0.f)\n        dx_val = dy_val;\n      else\n        dx_val = 0;\n      dX[flat_idx] = dx_val;\n      db_local += (float)dx_val;\n    }\n\n    // Handle meat of work\n    for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n      int row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n#pragma unroll 4\n      for (int u = 0; u < UNROLL_FACTOR; u++) {\n        T y_val = Y[flat_idx];\n        T dy_val = dY[flat_idx];\n        T dx_val;\n        if ((float)y_val > 0.f)\n          dx_val = dy_val;\n        else\n          dx_val = 0;\n        dX[flat_idx] = dx_val;\n        db_local += (float)dx_val;\n        flat_idx += features;\n      }\n    }\n\n    // naive block reduction on y-dim\n    int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n    smem[linear_idx] = db_local;\n  }\n  __syncthreads();\n  if (f < features) {\n    if (threadIdx.y == 0) {\n      for (int yidx = 1; yidx < blockDim.y; yidx++) {\n        db_local += smem[yidx * blockDim.x + threadIdx.x];\n      }\n\n      // block result is in db_local now for all threadIdx.y == 0\n      // Write out partial result\n      out[f] = db_local;\n    }\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0 && f < features) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n  db_local = 0;\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock && f < features) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        db_local += (float)(intermediate[col * features + row]);\n      }\n      db[f] = (T)db_local;\n    }\n  }\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAddRelu_bprop_aligned(T* Y, T* dY, int features, int batch_size, T* dX,\n                                          volatile float* intermediate, int* semaphores, T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n\n  // Accumulate db in FP32 always\n  float db_local[ILP];\n  T r_y[ILP];\n  T r_dy[ILP];\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    db_local[ii] = 0.f;\n  }\n\n  // f always <= features in this case\n  // if (f < features) {\n  int nidx = 0;\n\n  // Handle non-multiple of UNROLL_FACTOR residue\n  for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n    int row, col, flat_idx;\n    row = f;\n    col = nStart + nidx;\n    flat_idx = col * features / ILP + row;\n\n    load_store(r_y, Y, 0, flat_idx);\n    load_store(r_dy, dY, 0, flat_idx);\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      db_local[ii] += (float)r_dy[ii];\n    }\n    load_store(dX, r_dy, flat_idx, 0);\n  }\n\n  // Handle meat of work\n  for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n    int row, col, flat_idx;\n    row = f;\n    col = nStart + nidx;\n    flat_idx = col * features / ILP + row;  // total threads in x == features/ILP\n#pragma unroll\n    for (int u = 0; u < UNROLL_FACTOR; u++) {\n      load_store(r_y, Y, 0, flat_idx);\n      load_store(r_dy, dY, 0, flat_idx);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n        db_local[ii] += (float)r_dy[ii];\n      }\n      load_store(dX, r_dy, flat_idx, 0);\n      flat_idx += features / ILP;\n    }\n  }\n\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y * ILP];\n  // naive block reduction on y-dim\n  int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n  float* smem_out = smem + ILP * linear_idx;\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    smem_out[ii] = db_local[ii];  // reuse local dy buffer\n  }\n  __syncthreads();\n  if (threadIdx.y == 0) {\n    for (int yidx = 1; yidx < blockDim.y; yidx++) {\n      float* smem_in = smem + ILP * (yidx * blockDim.x + threadIdx.x);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        db_local[ii] += smem_in[ii];  // reuse local dy buffer\n      }\n    }\n\n    // block result is in db_local now for all threadIdx.y == 0\n    if (gridDim.y == 1) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_dy[ii] = db_local[ii];  // reuse local dy buffer\n      }\n      load_store(db, r_dy, f, 0);\n      return;\n    }\n\n    // Write out partial result\n    load_store(out, db_local, f, 0);\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    db_local[ii] = 0.f;\n  }\n  float r_db[ILP];\n\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        load_store(r_db, intermediate, 0, col * features / ILP + row);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          db_local[ii] += r_db[ii];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_dy[ii] = db_local[ii];  // reuse local dy buffer\n      }\n      load_store(db, r_dy, f, 0);\n    }\n  }\n}\n\n// Lists where the num_layers-1 intermediate Y buffers start in reserved space on fprop, starting\n// offset 0. The last Y value is, of course, stored in the user provided output buffer.\nvoid get_y_offsets(int batch_size, int num_layers, const int* output_features, int* y_start_offsets) {\n  y_start_offsets[0] = 0;\n  for (int i = 1; i < num_layers; i++) {\n    y_start_offsets[i] = y_start_offsets[i - 1] + batch_size * output_features[i - 1];\n  }\n}\n\n// Returns the reserved space (in elements) needed for the MLP\nsize_t get_mlp_reserved_space(int64_t batch_size, int num_layers, const int* output_features) {\n  size_t res_space = 0;\n  // Need to store output of every intermediate MLP - size equal to output_features[i] * batch_size\n  // for all 'i' in [0, num_layers-1)\n  for (int l = 0; l < num_layers; l++) {\n    res_space += output_features[l] * batch_size;\n  }\n  return res_space;\n}\n\n// Returns the size of all fprop activations combined\nsize_t get_all_activations_size(int64_t batch_size, int num_layers, const int* output_features) {\n  size_t acts_size = 0;\n  for (int l = 0; l < num_layers; l++) {\n    acts_size += output_features[l] * batch_size;\n  }\n  return acts_size;\n}\n\n#if 0\n// Returns the work space (in elements) needed for the MLP bprop.\nsize_t get_mlp_bp_workspace (int batch_size, int num_layers, const int* output_features) {\n    /*\n       Workspace is partitioned as\n       DY_GEMMs : DX_GEMMs\n    */\n    size_t work_space = 0;\n\n    // Store each intermediate dY explicitly. Need 2 dYs per MLP layer (one for o/p\n    // of biasReLU_bp and one for o/p of dgrad GEMM).\n    work_space += 2*get_all_activations_size(batch_size, num_layers, output_features);\n\n    return work_space;\n}\n#endif\n\n// Scratch space needed for reductions in number of elements\nsize_t get_reduction_scratch_space(int batch_size, int num_layers, const int* output_features) {\n  size_t max_scratch_space = 0;\n  // Loop over all layers to see which one needs the max scratch space\n  for (int l = 0; l < num_layers; l++) {\n    // need to find max(aligned, not_aligned)\n    int tmp, res0, res1;\n\n    int block_x = BIAS_RELU_BW_NTHREADS_X;\n    int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n    get_biasAddRelu_bprop_grid_size(output_features[l], batch_size, block_x, block_y, &tmp, &res0);\n\n    block_x = ILP * BIAS_RELU_BW_NTHREADS_X;\n    get_biasAddRelu_bprop_grid_size(output_features[l], batch_size, block_x, block_y, &tmp, &res1);\n\n    max_scratch_space = std::max(max_scratch_space, (size_t)(output_features[l] * res0));\n    max_scratch_space = std::max(max_scratch_space, (size_t)(output_features[l] * res1));\n  }\n\n  return max_scratch_space;\n}\n\n// Buffer for semaphores\nsize_t get_semaphores_size(int num_layers, const int* output_features) {\n  // Upper bound on semaphores is one per feature for the layer\n  // with the most features.\n  int max_features = 0;\n  for (int l = 0; l < num_layers; l++) {\n    max_features = std::max(max_features, output_features[l]);\n  }\n  return (size_t)max_features;\n}\n\n// Returns the work space (in elements) needed for the MLP bprop.\ntemplate <typename T>\nsize_t get_mlp_bp_workspace_in_bytes(int batch_size, int num_layers, const int* output_features) {\n  size_t work_space = 0;\n\n  // Store each intermediate dY explicitly. Need 2 dYs per MLP layer (one for o/p\n  // of biasReLU_bp and one for o/p of dgrad GEMM).\n  work_space += 2 * get_all_activations_size(batch_size, num_layers, output_features) * sizeof(T);\n  work_space += get_reduction_scratch_space(batch_size, num_layers, output_features) * sizeof(float);\n  work_space += get_semaphores_size(num_layers, output_features) * sizeof(int);\n\n  return work_space;\n}\n\n// Returns pointers to each segment of the workspace\ntemplate <typename T>\nvoid partition_mlp_bp_workspace(int batch_size, int num_layers, const int* output_features, void* work_space,\n                                T** dy_gemms, T** dx_gemms, float** db_scratch, int** semaphores) {\n  /*\n     Workspace is partitioned as\n     DY_GEMMs : DX_GEMMs : DB_SCRATCH : SEMAPHORES\n  */\n  // Start address where dy_gemm tensors are stored\n  *dy_gemms = reinterpret_cast<T*>(work_space);\n  // Start address where dx_gemm tensors are stored\n  *dx_gemms = *dy_gemms + get_all_activations_size(batch_size, num_layers, output_features);\n  // Start address where db intermediate tensors are stored\n  *db_scratch = reinterpret_cast<float*>(*dx_gemms + get_all_activations_size(batch_size, num_layers, output_features));\n  // Start address of semaphores\n  *semaphores =\n      reinterpret_cast<int*>(*db_scratch + get_reduction_scratch_space(batch_size, num_layers, output_features));\n\n  return;\n}\n\n// Does a simple MLP fprop (GEMM+bias+ReLU).\n// Can handle num_layers number of layers, each with its own shape. Output of layer i is assumed\n// to be input of layer i+1. output_features, WPtr and BPtr are arrays of length num_layers, and\n// must be in the same order i.e. WPtr[i] and BPtr[i] are respectively the weight and bias of layer\n// 'i'.\ntemplate <typename T>\nint mlp_fp(T* X, int input_features, int batch_size, T** WPtr, int num_layers, int* output_features, T** BPtr, T* Y,\n           T* reserved_space, int use_bias, int activation, void* lt_workspace) {\n  T *weight, *input, *output, *bias;\n  T *reserved_space_x, *reserved_space_y;\n  reserved_space_x = NULL;\n  reserved_space_y = reserved_space;\n\n  // Get cublas handle from Pytorch\n  cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n  // Get the stream from cublas handle to reuse for biasReLU kernel.\n  cudaStream_t stream;\n  cublasGetStream(handle, &stream);\n\n  for (int layer = 0; layer < num_layers; layer++) {\n    weight = WPtr[layer];\n    input = (layer == 0) ? X : reserved_space_x;\n    output = (layer == num_layers - 1) ? Y : reserved_space_y;\n    if (use_bias) {\n      bias = BPtr[layer];\n    }\n    int ifeat = (layer == 0) ? input_features : output_features[layer - 1];\n    int ofeat = output_features[layer];\n\n    float one = 1.f;\n    float zero = 0.f;\n\n    // try with cublaslt first for supported case with valid handle\n    int cublaslt_status = 1;\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\n    if (activation < 1) {\n      cublaslt_status = mlp_gemm_lt(\n          // ltHandle,\n          (cublasLtHandle_t)handle, CUBLAS_OP_T, CUBLAS_OP_N, ofeat, batch_size, ifeat, &one, weight, ifeat, input,\n          ifeat, &zero, output, ofeat, lt_workspace, 1 << 22, stream, use_bias == 1, activation == 1, bias);\n    }\n#endif\n\n    // if cublaslt failed or not executed, fallback to cublas\n    if (cublaslt_status != 0) {\n      cublasStatus_t cublas_status;\n      // Call GEMM: fprop is Y = W'X\n      cublas_status = mlp_gemm(handle, CUBLAS_OP_T, CUBLAS_OP_N, ofeat, batch_size, ifeat, &one, weight, ifeat, input,\n                               ifeat, &zero, output, ofeat);\n\n      if (cublas_status != CUBLAS_STATUS_SUCCESS) {\n        printf(\"GEMM fprop failed with %d\\n\", cublas_status);\n        return 1;\n      }\n\n      const uint& input_size = ofeat;\n      int num_blocks = 0;\n      int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n      // Call biasReLU\n      if (use_bias == 1) {\n        if (activation == 0) {  // no activation\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAdd_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAdd_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                    input_size);\n        } else if (activation == 1) {  // relu\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAddRelu_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAddRelu_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                        input_size);\n        } else if (activation == 2) {  // sigmoid\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAdd_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAdd_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                    input_size);\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Sigmoid_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        }\n      } else {\n        // don't need to do anything in case of no activation and no bias\n        if (activation == 1) {  // relu\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Relu_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Relu_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        } else if (activation == 2) {  // sigmoid\n          cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Sigmoid_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        }\n      }\n    }\n    // Set current output as next layer input\n    reserved_space_x = reserved_space_y;\n    // Set next layer output\n    reserved_space_y += ofeat * batch_size;\n  }\n\n  return 0;\n}\n\n// Does a simple MLP bprop (GEMM+bias+ReLU).\n// Needs reserved space to come back exactly as it was populated in fprop.\n// Does dgrad and wgrad sequentially.\ntemplate <typename T>\nint mlp_bp(T* X, T* Y, int input_features, int batch_size, T** WPtr, int num_layers, int* output_features, T* dY,\n           T* reserved_space, T* work_space, T* dX, T** dwPtr, T** dbPtr, bool requires_grad, int use_bias,\n           int activation) {\n  T* weight;\n  T *dweight, *dx, *dy, *dbias;\n  T *x, *y;\n\n  // Where the dx of the biasReLU (== dy of gemm) is stored. Can be thrown away\n  // after bp call.\n  T* dy_gemm_base;\n  // Where the dx after GEMM is stored.\n  T* dx_gemm_base;\n  // Where partial reduction results are stored.\n  float* db_scratch;\n  // Semaphores for reduction.\n  int* semaphores;\n\n  partition_mlp_bp_workspace<T>(batch_size, num_layers, output_features, work_space, &dy_gemm_base, &dx_gemm_base,\n                                &db_scratch, &semaphores);\n\n  size_t semaphore_size = get_semaphores_size(num_layers, output_features) * sizeof(int);\n\n  // Get cublas handle from Pytorch\n  cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n  // Get the stream from cublas handle to reuse for biasReLU kernel.\n  cudaStream_t stream;\n  cublasGetStream(handle, &stream);\n\n  int* y_offsets = (int*)malloc(num_layers * sizeof(int));\n  get_y_offsets(batch_size, num_layers, output_features, y_offsets);\n\n  for (int layer = num_layers - 1; layer >= 0; layer--) {\n    weight = WPtr[layer];\n    dweight = dwPtr[layer];\n\n    // x is read from reserved space\n    x = (layer == 0) ? X : reserved_space + y_offsets[layer - 1];\n    // dx is written in workspace for all but layer==0\n    dx = (layer == 0) ? dX : dx_gemm_base + y_offsets[layer - 1];\n\n    // y is read from reserved space\n    y = (layer == num_layers - 1) ? Y : reserved_space + y_offsets[layer];\n    // dx from layer+1\n    dy = (layer == num_layers - 1) ? dY : dx_gemm_base + y_offsets[layer];\n    // dy_gemm is written to and read immediately\n    T* dy_gemm = dy_gemm_base + y_offsets[layer];\n\n    dbias = dbPtr[layer];\n    int xfeat = (layer == 0) ? input_features : output_features[layer - 1];\n    int yfeat = output_features[layer];\n\n    float one = 1.f;\n    float zero = 0.f;\n\n    if (use_bias == 1) {\n      if (activation == 0) {  // no acitvation\n        // bgrad\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        cudaMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        int block_x = BIAS_RELU_BW_NTHREADS_X;\n        int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n        get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n        dim3 grid(grid_x, grid_y);\n        biasAdd_bprop<T, 4><<<grid, block, 0, stream>>>(dy, yfeat, batch_size, db_scratch, semaphores, dbias);\n        // bypass dgrad through reset pointer\n        dy_gemm = dy;\n      } else if (activation == 1) {  // relu\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        cudaMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        if (yfeat % (ILP * BIAS_RELU_BW_NTHREADS_X) == 0 && is_aligned(y) && is_aligned(dy) && is_aligned(dy_gemm) &&\n            is_aligned(dbias)) {\n          int block_x = ILP * BIAS_RELU_BW_NTHREADS_X;\n          int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n          get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n          dim3 grid(grid_x, grid_y);\n          biasAddRelu_bprop_aligned<T, 4>\n              <<<grid, block, 0, stream>>>(y, dy, yfeat, batch_size, dy_gemm, db_scratch, semaphores, dbias);\n        } else {\n          int block_x = BIAS_RELU_BW_NTHREADS_X;\n          int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n          get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n          dim3 grid(grid_x, grid_y);\n          biasAddRelu_bprop<T, 4>\n              <<<grid, block, 0, stream>>>(y, dy, yfeat, batch_size, dy_gemm, db_scratch, semaphores, dbias);\n        }\n      } else if (activation == 2) {  // sigmoid\n        // activation backward\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Sigmoid_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n\n        // bgrad, from dy_gemm\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        cudaMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        int block_x = BIAS_RELU_BW_NTHREADS_X;\n        int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n        get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n        dim3 grid(grid_x, grid_y);\n        biasAdd_bprop<T, 4><<<grid, block, 0, stream>>>(dy_gemm, yfeat, batch_size, db_scratch, semaphores, dbias);\n      }\n    } else {  // no bias below\n      if (activation == 0) {\n        // bypass dgrad through reset pointer\n        dy_gemm = dy;\n      } else if (activation == 1) {  // relu\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Relu_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Relu_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n      } else if (activation == 2) {  // sigmoid\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Sigmoid_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n      }\n    }\n\n    cublasStatus_t cublas_status;\n    // Call GEMM dgrad\n    if (layer > 0 || requires_grad == 1) {\n      cublas_status = mlp_gemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, xfeat, batch_size, yfeat, &one, weight, xfeat, dy_gemm,\n                               yfeat, &zero, dx, xfeat);\n\n      if (cublas_status != CUBLAS_STATUS_SUCCESS) {\n        printf(\"GEMM dgrad failed with %d\\n\", cublas_status);\n        return 1;\n      }\n    }\n\n    // Call GEMM wgrad\n    cublas_status = mlp_gemm(handle, CUBLAS_OP_N, CUBLAS_OP_T, xfeat, yfeat, batch_size, &one, x, xfeat, dy_gemm, yfeat,\n                             &zero, dweight, xfeat);\n\n    if (cublas_status != CUBLAS_STATUS_SUCCESS) {\n      printf(\"GEMM wgrad failed with %d\\n\", cublas_status);\n      return 1;\n    }\n  }\n\n  return 0;\n}\n\n// Instantiate for floating point types\ntemplate int mlp_fp<float>(float* X, int input_features, int batch_size, float** WPtr, int num_layers,\n                           int* output_features, float** BPtr, float* Y, float* reserved_space, int use_bias,\n                           int activation, void* lt_workspace);\n\ntemplate int mlp_bp<float>(float* X, float* Y, int input_features, int batch_size, float** WPtr, int num_layers,\n                           int* output_features, float* dY, float* reserved_space, float* work_space, float* dX,\n                           float** dwPtr, float** dbPtr, bool requires_grad, int use_bias, int activation);\n\ntemplate int mlp_fp<at::Half>(at::Half* X, int input_features, int batch_size, at::Half** WPtr, int num_layers,\n                              int* output_features, at::Half** BPtr, at::Half* Y, at::Half* reserved_space,\n                              int use_bias, int activation, void* lt_workspace);\n\ntemplate int mlp_bp<at::Half>(at::Half* X, at::Half* Y, int input_features, int batch_size, at::Half** WPtr,\n                              int num_layers, int* output_features, at::Half* dY, at::Half* reserved_space,\n                              at::Half* work_space, at::Half* dX, at::Half** dwPtr, at::Half** dbPtr,\n                              bool requires_grad, int use_bias, int activation);\n\ntemplate int mlp_fp<double>(double* X, int input_features, int batch_size, double** WPtr, int num_layers,\n                            int* output_features, double** BPtr, double* Y, double* reserved_space, int use_bias,\n                            int activation, void* lt_workspace);\n\ntemplate int mlp_bp<double>(double* X, double* Y, int input_features, int batch_size, double** WPtr, int num_layers,\n                            int* output_features, double* dY, double* reserved_space, double* work_space, double* dX,\n                            double** dwPtr, double** dbPtr, bool requires_grad, int use_bias, int activation);\n\ntemplate size_t get_mlp_bp_workspace_in_bytes<float>(int batch_size, int num_layers, const int* output_features);\ntemplate size_t get_mlp_bp_workspace_in_bytes<at::Half>(int batch_size, int num_layers, const int* output_features);\ntemplate size_t get_mlp_bp_workspace_in_bytes<double>(int batch_size, int num_layers, const int* output_features);\n", "output": "#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <torch/torch.h>\n\n/* Includes, cuda */\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\n// includes cublaslt\n#include <hipblaslt.h>\n#endif\n// constants for fused bias+relu kernel\n#define BIAS_RELU_FW_NTHREADS 128    // forward number of thread per block\n#define BIAS_RELU_BW_NTHREADS_X 32   // backward number of thread in feature dim\n#define BIAS_RELU_BW_NTHREADS_Y 16   // backward number of thread in batch dim\n#define BIAS_RELU_RED_PER_THREAD 16  // backward minimal reduction length per thread\n\n// move to a header later on\n#define ILP 4\ntemplate <typename T>\n__host__ __device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, volatile T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\ntemplate <typename T>\n__device__ __forceinline__ void load_store(volatile T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\n// Keep ReLU in float only. When using half, cast to float before calling.\n__device__ __inline__ float relu(float a) {\n  float retf = max(a, 0.f);\n  return (retf);\n}\n\n// Keep Sigmoid in float only. When using half, cast to float before calling.\n__device__ __inline__ float sigmoid(float a) {\n  float retf = 1.f / (1.f + expf(-a));\n  return (retf);\n}\n\n// FP64 Wrapper around cublas GEMMEx\nhipblasStatus_t mlp_gemm(hipblasHandle_t handle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                        float* alpha, const double* A, int lda, const double* B, int ldb, const float* beta, double* C,\n                        int ldc) {\n  return hipblasGemmEx(handle, transa, transb, m, n, k, alpha, A, HIP_R_64F, lda, B, HIP_R_64F, ldb, beta, C,\n                      HIP_R_64F, ldc, HIP_R_64F, HIPBLAS_GEMM_DEFAULT);\n}\n\n// FP32 Wrapper around cublas GEMMEx\nhipblasStatus_t mlp_gemm(hipblasHandle_t handle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                        float* alpha, const float* A, int lda, const float* B, int ldb, const float* beta, float* C,\n                        int ldc) {\n  return hipblasGemmEx(handle, transa, transb, m, n, k, alpha, A, HIP_R_32F, lda, B, HIP_R_32F, ldb, beta, C,\n                      HIP_R_32F, ldc, HIP_R_32F, HIPBLAS_GEMM_DEFAULT);\n}\n\n// FP16 Tensor core wrapper around cublas GEMMEx\nhipblasStatus_t mlp_gemm(hipblasHandle_t handle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                        float* alpha, const at::Half* A, int lda, const at::Half* B, int ldb, float* beta, at::Half* C,\n                        int ldc) {\n  return hipblasGemmEx(handle, transa, transb, m, n, k, alpha, A, HIP_R_16F, lda, B, HIP_R_16F, ldb, beta, C,\n                      HIP_R_16F, ldc, HIP_R_32F, CUBLAS_GEMM_DEFAULT_TENSOR_OP);\n}\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\nint mlp_gemm_lt(hipblasLtHandle_t ltHandle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                        /* host pointer */\n                const at::Half* A, int lda, const at::Half* B, int ldb, float* beta, /* host pointer */\n                at::Half* C, int ldc, void* workspace, size_t workspaceSize, hipStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  hipblasStatus_t status = HIPBLAS_STATUS_SUCCESS;\n\n  hipblasLtMatmulDescOpaque_t operationDesc = {};\n  hipblasLtMatrixLayoutOpaque_t Adesc = {}, Bdesc = {}, Cdesc = {};\n  hipblasLtMatmulPreferenceOpaque_t preference = {};\n\n  int returnedResults = 0;\n  hipblasLtMatmulHeuristicResult_t heuristicResult = {};\n  hipblasLtEpilogue_t epilogue = HIPBLASLT_EPILOGUE_DEFAULT;\n\n  // Create operation descriptor; see cublasLtMatmulDescAttributes_t\n  // for details about defaults; here we just set the transforms for\n  // A and B.\n  status = cublasLtMatmulDescInit(&operationDesc, HIPBLAS_COMPUTE_32F, HIP_R_32F);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_TRANSA, &transa, sizeof(transa));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_TRANSB, &transb, sizeof(transa));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (use_bias) {\n    status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_BIAS_POINTER, &bias, sizeof(bias));\n    if (status != HIPBLAS_STATUS_SUCCESS) {\n      goto CLEANUP;\n    }\n    if (use_relu) {\n      epilogue = HIPBLASLT_EPILOGUE_RELU_BIAS;\n    } else {\n      epilogue = HIPBLASLT_EPILOGUE_BIAS;\n    }\n  } else {\n    if (use_relu) {\n      epilogue = HIPBLASLT_EPILOGUE_RELU;\n    }\n  }\n\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE, &epilogue, sizeof(epilogue));\n  if (status != HIPBLAS_STATUS_SUCCESS) {\n    goto CLEANUP;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  status =\n      cublasLtMatrixLayoutInit(&Adesc, HIP_R_16F, transa == HIPBLAS_OP_N ? m : k, transa == HIPBLAS_OP_N ? k : m, lda);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status =\n      cublasLtMatrixLayoutInit(&Bdesc, HIP_R_16F, transb == HIPBLAS_OP_N ? k : n, transb == HIPBLAS_OP_N ? n : k, ldb);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatrixLayoutInit(&Cdesc, HIP_R_16F, m, n, ldc);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // Create preference handle; In general, extra attributes can be\n  // used here to disable tensor ops or to make sure algo selected\n  // will work with badly aligned A, B, C. However, for simplicity\n  // here we assume A,B,C are always well aligned (e.g., directly\n  // come from cudaMalloc)\n  status = cublasLtMatmulPreferenceInit(&preference);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulPreferenceSetAttribute(&preference, HIPBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize,\n                                                sizeof(workspaceSize));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // We just need the best available heuristic to try and run matmul.\n  // There is no guarantee that this will work. For example, if A is\n  // badly aligned, you can request more (e.g. 32) algos and try to\n  // run them one by one until something works.\n  status = hipblasLtMatmulAlgoGetHeuristic(ltHandle, &operationDesc, &Adesc, &Bdesc, &Cdesc, &Cdesc, &preference, 1,\n                                          &heuristicResult, &returnedResults);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (returnedResults == 0) {\n    status = HIPBLAS_STATUS_NOT_SUPPORTED;\n    goto CLEANUP;\n  }\n  status = hipblasLtMatmul(ltHandle, &operationDesc, alpha, A, &Adesc, B, &Bdesc, beta, C, &Cdesc, C, &Cdesc,\n                          &heuristicResult.algo, workspace, workspaceSize, stream);\n\nCLEANUP:\n  // Descriptors are no longer needed as all GPU work was already\n  // enqueued.\n  return status == HIPBLAS_STATUS_SUCCESS ? 0 : 1;\n}\n\nint mlp_gemm_lt(hipblasLtHandle_t ltHandle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                    /* host pointer */\n                const double* A, int lda, const double* B, int ldb, float* beta, /* host pointer */\n                double* C, int ldc, void* workspace, size_t workspaceSize, hipStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  return 1;\n}\n\nint mlp_gemm_lt(hipblasLtHandle_t ltHandle, hipblasOperation_t transa, hipblasOperation_t transb, int m, int n, int k,\n                float* alpha,                                                  /* host pointer */\n                const float* A, int lda, const float* B, int ldb, float* beta, /* host pointer */\n                float* C, int ldc, void* workspace, size_t workspaceSize, hipStream_t stream, bool use_bias,\n                bool use_relu, const void* bias) {\n  hipblasStatus_t status = HIPBLAS_STATUS_SUCCESS;\n\n  hipblasLtMatmulDescOpaque_t operationDesc = {};\n  hipblasLtMatrixLayoutOpaque_t Adesc = {}, Bdesc = {}, Cdesc = {};\n  hipblasLtMatmulPreferenceOpaque_t preference = {};\n\n  int returnedResults = 0;\n  hipblasLtMatmulHeuristicResult_t heuristicResult = {};\n  hipblasLtEpilogue_t epilogue = HIPBLASLT_EPILOGUE_DEFAULT;\n\n  // Create operation descriptor; see cublasLtMatmulDescAttributes_t\n  // for details about defaults; here we just set the transforms for\n  // A and B.\n  status = cublasLtMatmulDescInit(&operationDesc, HIPBLAS_COMPUTE_32F, HIP_R_32F);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_TRANSA, &transa, sizeof(transa));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_TRANSB, &transb, sizeof(transa));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (use_bias) {\n    status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_BIAS_POINTER, &bias, sizeof(bias));\n    if (status != HIPBLAS_STATUS_SUCCESS) {\n      goto CLEANUP;\n    }\n    if (use_relu) {\n      epilogue = HIPBLASLT_EPILOGUE_RELU_BIAS;\n    } else {\n      epilogue = HIPBLASLT_EPILOGUE_BIAS;\n    }\n  } else {\n    if (use_relu) {\n      epilogue = HIPBLASLT_EPILOGUE_RELU;\n    }\n  }\n\n  status = hipblasLtMatmulDescSetAttribute(&operationDesc, HIPBLASLT_MATMUL_DESC_EPILOGUE, &epilogue, sizeof(epilogue));\n  if (status != HIPBLAS_STATUS_SUCCESS) {\n    goto CLEANUP;\n  }\n\n  // Create matrix descriptors. Not setting any extra attributes.\n  status =\n      cublasLtMatrixLayoutInit(&Adesc, HIP_R_32F, transa == HIPBLAS_OP_N ? m : k, transa == HIPBLAS_OP_N ? k : m, lda);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status =\n      cublasLtMatrixLayoutInit(&Bdesc, HIP_R_32F, transb == HIPBLAS_OP_N ? k : n, transb == HIPBLAS_OP_N ? n : k, ldb);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = cublasLtMatrixLayoutInit(&Cdesc, HIP_R_32F, m, n, ldc);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // Create preference handle; In general, extra attributes can be\n  // used here to disable tensor ops or to make sure algo selected\n  // will work with badly aligned A, B, C. However, for simplicity\n  // here we assume A,B,C are always well aligned (e.g., directly\n  // come from cudaMalloc)\n  status = cublasLtMatmulPreferenceInit(&preference);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n  status = hipblasLtMatmulPreferenceSetAttribute(&preference, HIPBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES, &workspaceSize,\n                                                sizeof(workspaceSize));\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  // We just need the best available heuristic to try and run matmul.\n  // There is no guarantee that this will work. For example, if A is\n  // badly aligned, you can request more (e.g. 32) algos and try to\n  // run them one by one until something works.\n  status = hipblasLtMatmulAlgoGetHeuristic(ltHandle, &operationDesc, &Adesc, &Bdesc, &Cdesc, &Cdesc, &preference, 1,\n                                          &heuristicResult, &returnedResults);\n  if (status != HIPBLAS_STATUS_SUCCESS) goto CLEANUP;\n\n  if (returnedResults == 0) {\n    status = HIPBLAS_STATUS_NOT_SUPPORTED;\n    goto CLEANUP;\n  }\n\n  status = hipblasLtMatmul(ltHandle, &operationDesc, alpha, A, &Adesc, B, &Bdesc, beta, C, &Cdesc, C, &Cdesc,\n                          &heuristicResult.algo, workspace, workspaceSize, stream);\n\nCLEANUP:\n  // Descriptors are no longer needed as all GPU work was already\n  // enqueued.\n  return status == HIPBLAS_STATUS_SUCCESS ? 0 : 1;\n}\n#endif\n\n// Bias ADD. Assume input X is [features x batch size], column major.\n// Bias is one 'features' long vector, with implicit broadcast.\ntemplate <typename T>\n__global__ void biasAdd_fprop(T* X, T* b, uint batch_size, uint features) {\n  T r_x[ILP];\n  T r_b[ILP];\n  if (is_aligned(X) && is_aligned(b) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      int row = tid % (features / ILP);\n      load_store(r_x, X, 0, tid);\n      load_store(r_b, b, 0, row);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = bias_sum;\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          int row = tid % features;\n          r_x[ii] = X[idx];\n          r_b[ii] = b[row];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = bias_sum;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// Bias ADD + ReLU. Assume input X is [features x batch size], column major.\n// Activation support fuesed ReLU. Safe to call in-place.\ntemplate <typename T>\n__global__ void biasAddRelu_fprop(T* X, T* b, uint batch_size, uint features) {\n  T r_x[ILP];\n  T r_b[ILP];\n  if (is_aligned(X) && is_aligned(b) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      int row = tid % (features / ILP);\n      load_store(r_x, X, 0, tid);\n      load_store(r_b, b, 0, row);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = relu(bias_sum);\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          int row = tid % features;\n          r_x[ii] = X[idx];\n          r_b[ii] = b[row];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float bias_sum = static_cast<float>(r_x[ii]) + static_cast<float>(r_b[ii]);\n        r_x[ii] = relu(bias_sum);\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// ReLU. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Relu_fprop(T* X, uint batch_size, uint features) {\n  T r_x[ILP];\n  if (is_aligned(X) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_x, X, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = relu(static_cast<float>(r_x[ii]));\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_x[ii] = X[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = relu(static_cast<float>(r_x[ii]));\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// Sigmoid. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Sigmoid_fprop(T* X, uint batch_size, uint features) {\n  T r_x[ILP];\n  if (is_aligned(X) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_x, X, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = sigmoid(static_cast<float>(r_x[ii]));\n      }\n      load_store(X, r_x, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_x[ii] = X[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_x[ii] = sigmoid(static_cast<float>(r_x[ii]));\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          X[idx] = r_x[ii];\n        }\n      }\n    }\n  }\n}\n\n// ReLU. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Relu_bprop(T* dY, T* Y, uint batch_size, uint features, T* dX) {\n  T r_dy[ILP];\n  T r_y[ILP];\n  if (is_aligned(dY) && is_aligned(Y) && is_aligned(dX) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_dy, dY, 0, tid);\n      load_store(r_y, Y, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      }\n      load_store(dX, r_dy, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_dy[ii] = dY[idx];\n          r_y[ii] = Y[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          dX[idx] = r_dy[ii];\n        }\n      }\n    }\n  }\n}\n\n// Sigmoid. Assume input X is [features x batch size], column major.\n// Safe to call in-place.\ntemplate <typename T>\n__global__ void Sigmoid_bprop(T* dY, T* Y, uint batch_size, uint features, T* dX) {\n  T r_dy[ILP];\n  T r_y[ILP];\n  if (is_aligned(dY) && is_aligned(Y) && is_aligned(dX) && features % ILP == 0) {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid * ILP < features * batch_size; tid += blockDim.x * gridDim.x) {\n      load_store(r_dy, dY, 0, tid);\n      load_store(r_y, Y, 0, tid);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float grad_out = r_dy[ii];\n        float out = r_y[ii];\n        float grad_i = out * (1.f - out) * grad_out;\n        r_dy[ii] = grad_i;\n      }\n      load_store(dX, r_dy, tid, 0);\n    }\n  } else {\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    for (; tid < features * batch_size; tid += ILP * blockDim.x * gridDim.x) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          r_dy[ii] = dY[idx];\n          r_y[ii] = Y[idx];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        float grad_out = r_dy[ii];\n        float out = r_y[ii];\n        float grad_i = out * (1.f - out) * grad_out;\n        r_dy[ii] = grad_i;\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int idx = tid + ii * blockDim.x * gridDim.x;\n        if (idx < features * batch_size) {\n          dX[idx] = r_dy[ii];\n        }\n      }\n    }\n  }\n}\n\n// Compute grid size for pointwise backward kernel.\n// block_x/y is total elment being handled per block, not number of threads\nvoid get_biasAddRelu_bprop_grid_size(int yfeat, int batch_size, int block_x, int block_y, int* grid_x, int* grid_y) {\n  *grid_x = (yfeat + block_x - 1) / block_x;\n  // Get number of SMs for efficient reduction.\n  int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n  // can switch to occupancy calculation. use 4 below now for sm_70\n  int max_blocks_y = (num_SMs * 4 + (*grid_x) - 1) / (*grid_x);\n  // block_y should be from minimal work per thread\n  int nRedSplits = (batch_size + block_y - 1) / block_y;\n  // increase number of elem per thread redcution to not launch more than enough\n  // kernel adjust work, so here we just launch max block\n  *grid_y = std::min(nRedSplits, max_blocks_y);\n  return;\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAdd_bprop(T* dY, int features, int batch_size, volatile float* intermediate, int* semaphores,\n                              T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y];\n\n  // Accumulate db in FP32 always\n  float db_local = 0;\n  if (f < features) {\n    int nidx = 0;\n    // Handle non-multiple of UNROLL_FACTOR residue\n    for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n      int64_t row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n      db_local += (float)dY[flat_idx];\n    }\n\n    // Handle meat of work\n    for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n      int64_t row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n#pragma unroll 4\n      for (int u = 0; u < UNROLL_FACTOR; u++) {\n        db_local += (float)dY[flat_idx];\n        flat_idx += features;\n      }\n    }\n\n    // naive block reduction on y-dim\n    int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n    smem[linear_idx] = db_local;\n  }\n  __syncthreads();\n  if (f < features) {\n    if (threadIdx.y == 0) {\n      for (int yidx = 1; yidx < blockDim.y; yidx++) {\n        db_local += smem[yidx * blockDim.x + threadIdx.x];\n      }\n\n      // block result is in db_local now for all threadIdx.y == 0\n      // Write out partial result\n      out[f] = db_local;\n    }\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0 && f < features) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n  db_local = 0;\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock && f < features) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        db_local += (float)(intermediate[col * features + row]);\n      }\n      db[f] = (T)db_local;\n    }\n  }\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAddRelu_bprop(T* Y, T* dY, int features, int batch_size, T* dX, volatile float* intermediate,\n                                  int* semaphores, T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y];\n\n  // Accumulate db in FP32 always\n  float db_local = 0;\n  if (f < features) {\n    int nidx = 0;\n    // Handle non-multiple of UNROLL_FACTOR residue\n    for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n      int row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n      T y_val = Y[flat_idx];\n      T dy_val = dY[flat_idx];\n      T dx_val;\n      if ((float)y_val > 0.f)\n        dx_val = dy_val;\n      else\n        dx_val = 0;\n      dX[flat_idx] = dx_val;\n      db_local += (float)dx_val;\n    }\n\n    // Handle meat of work\n    for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n      int row, col, flat_idx;\n      row = f;\n      col = nStart + nidx;\n      flat_idx = col * features + row;\n#pragma unroll 4\n      for (int u = 0; u < UNROLL_FACTOR; u++) {\n        T y_val = Y[flat_idx];\n        T dy_val = dY[flat_idx];\n        T dx_val;\n        if ((float)y_val > 0.f)\n          dx_val = dy_val;\n        else\n          dx_val = 0;\n        dX[flat_idx] = dx_val;\n        db_local += (float)dx_val;\n        flat_idx += features;\n      }\n    }\n\n    // naive block reduction on y-dim\n    int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n    smem[linear_idx] = db_local;\n  }\n  __syncthreads();\n  if (f < features) {\n    if (threadIdx.y == 0) {\n      for (int yidx = 1; yidx < blockDim.y; yidx++) {\n        db_local += smem[yidx * blockDim.x + threadIdx.x];\n      }\n\n      // block result is in db_local now for all threadIdx.y == 0\n      // Write out partial result\n      out[f] = db_local;\n    }\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0 && f < features) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n  db_local = 0;\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock && f < features) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        db_local += (float)(intermediate[col * features + row]);\n      }\n      db[f] = (T)db_local;\n    }\n  }\n}\n\n// Addition done deterministically via a 2-pass approach. Each CTA writes out partial\n// sum, and the last CTA in grid Y dimension accumulates partials serially and writes to result.\ntemplate <typename T, int UNROLL_FACTOR>\n__global__ void biasAddRelu_bprop_aligned(T* Y, T* dY, int features, int batch_size, T* dX,\n                                          volatile float* intermediate, int* semaphores, T* db) {\n  // The feature that this thread is responsible for\n  int f = blockIdx.x * blockDim.x + threadIdx.x;\n\n  // Compute the span this thread is responsible for\n  // For this block\n  int b_chunkSize = (batch_size + gridDim.y - 1) / gridDim.y;\n  int b_nStart = blockIdx.y * b_chunkSize;\n  int b_nSpan = min(batch_size, b_nStart + b_chunkSize) - b_nStart;\n  // For this thread\n  int chunkSize = (b_chunkSize + blockDim.y - 1) / blockDim.y;\n  int nStart = threadIdx.y * chunkSize + b_nStart;\n  int nSpan = min(b_nStart + b_nSpan, nStart + chunkSize) - nStart;\n\n  volatile float* out = intermediate + blockIdx.y * features;\n\n  // Flag to trigger last reduction.\n  __shared__ bool isLastBlock;\n\n  // Accumulate db in FP32 always\n  float db_local[ILP];\n  T r_y[ILP];\n  T r_dy[ILP];\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    db_local[ii] = 0.f;\n  }\n\n  // f always <= features in this case\n  // if (f < features) {\n  int nidx = 0;\n\n  // Handle non-multiple of UNROLL_FACTOR residue\n  for (; nidx < nSpan % UNROLL_FACTOR; nidx++) {\n    int row, col, flat_idx;\n    row = f;\n    col = nStart + nidx;\n    flat_idx = col * features / ILP + row;\n\n    load_store(r_y, Y, 0, flat_idx);\n    load_store(r_dy, dY, 0, flat_idx);\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n      db_local[ii] += (float)r_dy[ii];\n    }\n    load_store(dX, r_dy, flat_idx, 0);\n  }\n\n  // Handle meat of work\n  for (; (nidx + UNROLL_FACTOR - 1) < nSpan; nidx += UNROLL_FACTOR) {\n    int row, col, flat_idx;\n    row = f;\n    col = nStart + nidx;\n    flat_idx = col * features / ILP + row;  // total threads in x == features/ILP\n#pragma unroll\n    for (int u = 0; u < UNROLL_FACTOR; u++) {\n      load_store(r_y, Y, 0, flat_idx);\n      load_store(r_dy, dY, 0, flat_idx);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        if ((float)r_y[ii] <= 0.f) r_dy[ii] = 0;\n        db_local[ii] += (float)r_dy[ii];\n      }\n      load_store(dX, r_dy, flat_idx, 0);\n      flat_idx += features / ILP;\n    }\n  }\n\n  // we know block size for now\n  __shared__ float smem[BIAS_RELU_BW_NTHREADS_X * BIAS_RELU_BW_NTHREADS_Y * ILP];\n  // naive block reduction on y-dim\n  int linear_idx = threadIdx.y * blockDim.x + threadIdx.x;\n  float* smem_out = smem + ILP * linear_idx;\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    smem_out[ii] = db_local[ii];  // reuse local dy buffer\n  }\n  __syncthreads();\n  if (threadIdx.y == 0) {\n    for (int yidx = 1; yidx < blockDim.y; yidx++) {\n      float* smem_in = smem + ILP * (yidx * blockDim.x + threadIdx.x);\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        db_local[ii] += smem_in[ii];  // reuse local dy buffer\n      }\n    }\n\n    // block result is in db_local now for all threadIdx.y == 0\n    if (gridDim.y == 1) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_dy[ii] = db_local[ii];  // reuse local dy buffer\n      }\n      load_store(db, r_dy, f, 0);\n      return;\n    }\n\n    // Write out partial result\n    load_store(out, db_local, f, 0);\n  }\n  __threadfence();\n  __syncthreads();\n\n  // Increment semaphore and check if this is the last CTA in the grid_y dimension.\n  // Only thread (0,0) calls this\n  if (threadIdx.x == 0 && threadIdx.y == 0) {\n    unsigned int sum_idx;\n    sum_idx = atomicAdd(&(semaphores[blockIdx.x]), 1);\n    isLastBlock = (sum_idx == (gridDim.y - 1));\n  }\n  __syncthreads();\n\n#pragma unroll\n  for (int ii = 0; ii < ILP; ii++) {\n    db_local[ii] = 0.f;\n  }\n  float r_db[ILP];\n\n  // No block reduction for now, only thread (*,0) do grid reduction\n  if (isLastBlock) {\n    if (threadIdx.y == 0) {\n      for (int n = 0; n < gridDim.y; n++) {\n        int row, col;\n        row = f;\n        col = n;\n        load_store(r_db, intermediate, 0, col * features / ILP + row);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          db_local[ii] += r_db[ii];\n        }\n      }\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        r_dy[ii] = db_local[ii];  // reuse local dy buffer\n      }\n      load_store(db, r_dy, f, 0);\n    }\n  }\n}\n\n// Lists where the num_layers-1 intermediate Y buffers start in reserved space on fprop, starting\n// offset 0. The last Y value is, of course, stored in the user provided output buffer.\nvoid get_y_offsets(int batch_size, int num_layers, const int* output_features, int* y_start_offsets) {\n  y_start_offsets[0] = 0;\n  for (int i = 1; i < num_layers; i++) {\n    y_start_offsets[i] = y_start_offsets[i - 1] + batch_size * output_features[i - 1];\n  }\n}\n\n// Returns the reserved space (in elements) needed for the MLP\nsize_t get_mlp_reserved_space(int64_t batch_size, int num_layers, const int* output_features) {\n  size_t res_space = 0;\n  // Need to store output of every intermediate MLP - size equal to output_features[i] * batch_size\n  // for all 'i' in [0, num_layers-1)\n  for (int l = 0; l < num_layers; l++) {\n    res_space += output_features[l] * batch_size;\n  }\n  return res_space;\n}\n\n// Returns the size of all fprop activations combined\nsize_t get_all_activations_size(int64_t batch_size, int num_layers, const int* output_features) {\n  size_t acts_size = 0;\n  for (int l = 0; l < num_layers; l++) {\n    acts_size += output_features[l] * batch_size;\n  }\n  return acts_size;\n}\n\n#if 0\n// Returns the work space (in elements) needed for the MLP bprop.\nsize_t get_mlp_bp_workspace (int batch_size, int num_layers, const int* output_features) {\n    /*\n       Workspace is partitioned as\n       DY_GEMMs : DX_GEMMs\n    */\n    size_t work_space = 0;\n\n    // Store each intermediate dY explicitly. Need 2 dYs per MLP layer (one for o/p\n    // of biasReLU_bp and one for o/p of dgrad GEMM).\n    work_space += 2*get_all_activations_size(batch_size, num_layers, output_features);\n\n    return work_space;\n}\n#endif\n\n// Scratch space needed for reductions in number of elements\nsize_t get_reduction_scratch_space(int batch_size, int num_layers, const int* output_features) {\n  size_t max_scratch_space = 0;\n  // Loop over all layers to see which one needs the max scratch space\n  for (int l = 0; l < num_layers; l++) {\n    // need to find max(aligned, not_aligned)\n    int tmp, res0, res1;\n\n    int block_x = BIAS_RELU_BW_NTHREADS_X;\n    int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n    get_biasAddRelu_bprop_grid_size(output_features[l], batch_size, block_x, block_y, &tmp, &res0);\n\n    block_x = ILP * BIAS_RELU_BW_NTHREADS_X;\n    get_biasAddRelu_bprop_grid_size(output_features[l], batch_size, block_x, block_y, &tmp, &res1);\n\n    max_scratch_space = std::max(max_scratch_space, (size_t)(output_features[l] * res0));\n    max_scratch_space = std::max(max_scratch_space, (size_t)(output_features[l] * res1));\n  }\n\n  return max_scratch_space;\n}\n\n// Buffer for semaphores\nsize_t get_semaphores_size(int num_layers, const int* output_features) {\n  // Upper bound on semaphores is one per feature for the layer\n  // with the most features.\n  int max_features = 0;\n  for (int l = 0; l < num_layers; l++) {\n    max_features = std::max(max_features, output_features[l]);\n  }\n  return (size_t)max_features;\n}\n\n// Returns the work space (in elements) needed for the MLP bprop.\ntemplate <typename T>\nsize_t get_mlp_bp_workspace_in_bytes(int batch_size, int num_layers, const int* output_features) {\n  size_t work_space = 0;\n\n  // Store each intermediate dY explicitly. Need 2 dYs per MLP layer (one for o/p\n  // of biasReLU_bp and one for o/p of dgrad GEMM).\n  work_space += 2 * get_all_activations_size(batch_size, num_layers, output_features) * sizeof(T);\n  work_space += get_reduction_scratch_space(batch_size, num_layers, output_features) * sizeof(float);\n  work_space += get_semaphores_size(num_layers, output_features) * sizeof(int);\n\n  return work_space;\n}\n\n// Returns pointers to each segment of the workspace\ntemplate <typename T>\nvoid partition_mlp_bp_workspace(int batch_size, int num_layers, const int* output_features, void* work_space,\n                                T** dy_gemms, T** dx_gemms, float** db_scratch, int** semaphores) {\n  /*\n     Workspace is partitioned as\n     DY_GEMMs : DX_GEMMs : DB_SCRATCH : SEMAPHORES\n  */\n  // Start address where dy_gemm tensors are stored\n  *dy_gemms = reinterpret_cast<T*>(work_space);\n  // Start address where dx_gemm tensors are stored\n  *dx_gemms = *dy_gemms + get_all_activations_size(batch_size, num_layers, output_features);\n  // Start address where db intermediate tensors are stored\n  *db_scratch = reinterpret_cast<float*>(*dx_gemms + get_all_activations_size(batch_size, num_layers, output_features));\n  // Start address of semaphores\n  *semaphores =\n      reinterpret_cast<int*>(*db_scratch + get_reduction_scratch_space(batch_size, num_layers, output_features));\n\n  return;\n}\n\n// Does a simple MLP fprop (GEMM+bias+ReLU).\n// Can handle num_layers number of layers, each with its own shape. Output of layer i is assumed\n// to be input of layer i+1. output_features, WPtr and BPtr are arrays of length num_layers, and\n// must be in the same order i.e. WPtr[i] and BPtr[i] are respectively the weight and bias of layer\n// 'i'.\ntemplate <typename T>\nint mlp_fp(T* X, int input_features, int batch_size, T** WPtr, int num_layers, int* output_features, T** BPtr, T* Y,\n           T* reserved_space, int use_bias, int activation, void* lt_workspace) {\n  T *weight, *input, *output, *bias;\n  T *reserved_space_x, *reserved_space_y;\n  reserved_space_x = NULL;\n  reserved_space_y = reserved_space;\n\n  // Get cublas handle from Pytorch\n  hipblasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n  // Get the stream from cublas handle to reuse for biasReLU kernel.\n  hipStream_t stream;\n  hipblasGetStream(handle, &stream);\n\n  for (int layer = 0; layer < num_layers; layer++) {\n    weight = WPtr[layer];\n    input = (layer == 0) ? X : reserved_space_x;\n    output = (layer == num_layers - 1) ? Y : reserved_space_y;\n    if (use_bias) {\n      bias = BPtr[layer];\n    }\n    int ifeat = (layer == 0) ? input_features : output_features[layer - 1];\n    int ofeat = output_features[layer];\n\n    float one = 1.f;\n    float zero = 0.f;\n\n    // try with cublaslt first for supported case with valid handle\n    int cublaslt_status = 1;\n#if defined(CUBLAS_VERSION) && CUBLAS_VERSION >= 11000\n    if (activation < 1) {\n      cublaslt_status = mlp_gemm_lt(\n          // ltHandle,\n          (hipblasLtHandle_t)handle, HIPBLAS_OP_T, HIPBLAS_OP_N, ofeat, batch_size, ifeat, &one, weight, ifeat, input,\n          ifeat, &zero, output, ofeat, lt_workspace, 1 << 22, stream, use_bias == 1, activation == 1, bias);\n    }\n#endif\n\n    // if cublaslt failed or not executed, fallback to cublas\n    if (cublaslt_status != 0) {\n      hipblasStatus_t cublas_status;\n      // Call GEMM: fprop is Y = W'X\n      cublas_status = mlp_gemm(handle, HIPBLAS_OP_T, HIPBLAS_OP_N, ofeat, batch_size, ifeat, &one, weight, ifeat, input,\n                               ifeat, &zero, output, ofeat);\n\n      if (cublas_status != HIPBLAS_STATUS_SUCCESS) {\n        printf(\"GEMM fprop failed with %d\\n\", cublas_status);\n        return 1;\n      }\n\n      const uint& input_size = ofeat;\n      int num_blocks = 0;\n      int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n      // Call biasReLU\n      if (use_bias == 1) {\n        if (activation == 0) {  // no activation\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAdd_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAdd_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                    input_size);\n        } else if (activation == 1) {  // relu\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAddRelu_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAddRelu_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                        input_size);\n        } else if (activation == 2) {  // sigmoid\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, biasAdd_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          biasAdd_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, bias, batch_size,\n                                                                                    input_size);\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Sigmoid_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        }\n      } else {\n        // don't need to do anything in case of no activation and no bias\n        if (activation == 1) {  // relu\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Relu_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Relu_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        } else if (activation == 2) {  // sigmoid\n          hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_fprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n          Sigmoid_fprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(output, batch_size, input_size);\n        }\n      }\n    }\n    // Set current output as next layer input\n    reserved_space_x = reserved_space_y;\n    // Set next layer output\n    reserved_space_y += ofeat * batch_size;\n  }\n\n  return 0;\n}\n\n// Does a simple MLP bprop (GEMM+bias+ReLU).\n// Needs reserved space to come back exactly as it was populated in fprop.\n// Does dgrad and wgrad sequentially.\ntemplate <typename T>\nint mlp_bp(T* X, T* Y, int input_features, int batch_size, T** WPtr, int num_layers, int* output_features, T* dY,\n           T* reserved_space, T* work_space, T* dX, T** dwPtr, T** dbPtr, bool requires_grad, int use_bias,\n           int activation) {\n  T* weight;\n  T *dweight, *dx, *dy, *dbias;\n  T *x, *y;\n\n  // Where the dx of the biasReLU (== dy of gemm) is stored. Can be thrown away\n  // after bp call.\n  T* dy_gemm_base;\n  // Where the dx after GEMM is stored.\n  T* dx_gemm_base;\n  // Where partial reduction results are stored.\n  float* db_scratch;\n  // Semaphores for reduction.\n  int* semaphores;\n\n  partition_mlp_bp_workspace<T>(batch_size, num_layers, output_features, work_space, &dy_gemm_base, &dx_gemm_base,\n                                &db_scratch, &semaphores);\n\n  size_t semaphore_size = get_semaphores_size(num_layers, output_features) * sizeof(int);\n\n  // Get cublas handle from Pytorch\n  hipblasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n  // Get the stream from cublas handle to reuse for biasReLU kernel.\n  hipStream_t stream;\n  hipblasGetStream(handle, &stream);\n\n  int* y_offsets = (int*)malloc(num_layers * sizeof(int));\n  get_y_offsets(batch_size, num_layers, output_features, y_offsets);\n\n  for (int layer = num_layers - 1; layer >= 0; layer--) {\n    weight = WPtr[layer];\n    dweight = dwPtr[layer];\n\n    // x is read from reserved space\n    x = (layer == 0) ? X : reserved_space + y_offsets[layer - 1];\n    // dx is written in workspace for all but layer==0\n    dx = (layer == 0) ? dX : dx_gemm_base + y_offsets[layer - 1];\n\n    // y is read from reserved space\n    y = (layer == num_layers - 1) ? Y : reserved_space + y_offsets[layer];\n    // dx from layer+1\n    dy = (layer == num_layers - 1) ? dY : dx_gemm_base + y_offsets[layer];\n    // dy_gemm is written to and read immediately\n    T* dy_gemm = dy_gemm_base + y_offsets[layer];\n\n    dbias = dbPtr[layer];\n    int xfeat = (layer == 0) ? input_features : output_features[layer - 1];\n    int yfeat = output_features[layer];\n\n    float one = 1.f;\n    float zero = 0.f;\n\n    if (use_bias == 1) {\n      if (activation == 0) {  // no acitvation\n        // bgrad\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        hipMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        int block_x = BIAS_RELU_BW_NTHREADS_X;\n        int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n        get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n        dim3 grid(grid_x, grid_y);\n        biasAdd_bprop<T, 4><<<grid, block, 0, stream>>>(dy, yfeat, batch_size, db_scratch, semaphores, dbias);\n        // bypass dgrad through reset pointer\n        dy_gemm = dy;\n      } else if (activation == 1) {  // relu\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        hipMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        if (yfeat % (ILP * BIAS_RELU_BW_NTHREADS_X) == 0 && is_aligned(y) && is_aligned(dy) && is_aligned(dy_gemm) &&\n            is_aligned(dbias)) {\n          int block_x = ILP * BIAS_RELU_BW_NTHREADS_X;\n          int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n          get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n          dim3 grid(grid_x, grid_y);\n          biasAddRelu_bprop_aligned<T, 4>\n              <<<grid, block, 0, stream>>>(y, dy, yfeat, batch_size, dy_gemm, db_scratch, semaphores, dbias);\n        } else {\n          int block_x = BIAS_RELU_BW_NTHREADS_X;\n          int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n          get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n          dim3 grid(grid_x, grid_y);\n          biasAddRelu_bprop<T, 4>\n              <<<grid, block, 0, stream>>>(y, dy, yfeat, batch_size, dy_gemm, db_scratch, semaphores, dbias);\n        }\n      } else if (activation == 2) {  // sigmoid\n        // activation backward\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Sigmoid_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n\n        // bgrad, from dy_gemm\n        dim3 block(BIAS_RELU_BW_NTHREADS_X, BIAS_RELU_BW_NTHREADS_Y);\n        int grid_x, grid_y;\n        hipMemsetAsync(semaphores, 0, semaphore_size, stream);\n\n        int block_x = BIAS_RELU_BW_NTHREADS_X;\n        int block_y = BIAS_RELU_RED_PER_THREAD * BIAS_RELU_BW_NTHREADS_Y;\n        get_biasAddRelu_bprop_grid_size(yfeat, batch_size, block_x, block_y, &grid_x, &grid_y);\n        dim3 grid(grid_x, grid_y);\n        biasAdd_bprop<T, 4><<<grid, block, 0, stream>>>(dy_gemm, yfeat, batch_size, db_scratch, semaphores, dbias);\n      }\n    } else {  // no bias below\n      if (activation == 0) {\n        // bypass dgrad through reset pointer\n        dy_gemm = dy;\n      } else if (activation == 1) {  // relu\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Relu_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Relu_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n      } else if (activation == 2) {  // sigmoid\n        int num_blocks = 0;\n        int num_SMs = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&num_blocks, Sigmoid_bprop<T>, BIAS_RELU_FW_NTHREADS, 0);\n        Sigmoid_bprop<<<num_SMs * num_blocks, BIAS_RELU_FW_NTHREADS, 0, stream>>>(dy, y, batch_size, yfeat, dy_gemm);\n      }\n    }\n\n    hipblasStatus_t cublas_status;\n    // Call GEMM dgrad\n    if (layer > 0 || requires_grad == 1) {\n      cublas_status = mlp_gemm(handle, HIPBLAS_OP_N, HIPBLAS_OP_N, xfeat, batch_size, yfeat, &one, weight, xfeat, dy_gemm,\n                               yfeat, &zero, dx, xfeat);\n\n      if (cublas_status != HIPBLAS_STATUS_SUCCESS) {\n        printf(\"GEMM dgrad failed with %d\\n\", cublas_status);\n        return 1;\n      }\n    }\n\n    // Call GEMM wgrad\n    cublas_status = mlp_gemm(handle, HIPBLAS_OP_N, HIPBLAS_OP_T, xfeat, yfeat, batch_size, &one, x, xfeat, dy_gemm, yfeat,\n                             &zero, dweight, xfeat);\n\n    if (cublas_status != HIPBLAS_STATUS_SUCCESS) {\n      printf(\"GEMM wgrad failed with %d\\n\", cublas_status);\n      return 1;\n    }\n  }\n\n  return 0;\n}\n\n// Instantiate for floating point types\ntemplate int mlp_fp<float>(float* X, int input_features, int batch_size, float** WPtr, int num_layers,\n                           int* output_features, float** BPtr, float* Y, float* reserved_space, int use_bias,\n                           int activation, void* lt_workspace);\n\ntemplate int mlp_bp<float>(float* X, float* Y, int input_features, int batch_size, float** WPtr, int num_layers,\n                           int* output_features, float* dY, float* reserved_space, float* work_space, float* dX,\n                           float** dwPtr, float** dbPtr, bool requires_grad, int use_bias, int activation);\n\ntemplate int mlp_fp<at::Half>(at::Half* X, int input_features, int batch_size, at::Half** WPtr, int num_layers,\n                              int* output_features, at::Half** BPtr, at::Half* Y, at::Half* reserved_space,\n                              int use_bias, int activation, void* lt_workspace);\n\ntemplate int mlp_bp<at::Half>(at::Half* X, at::Half* Y, int input_features, int batch_size, at::Half** WPtr,\n                              int num_layers, int* output_features, at::Half* dY, at::Half* reserved_space,\n                              at::Half* work_space, at::Half* dX, at::Half** dwPtr, at::Half** dbPtr,\n                              bool requires_grad, int use_bias, int activation);\n\ntemplate int mlp_fp<double>(double* X, int input_features, int batch_size, double** WPtr, int num_layers,\n                            int* output_features, double** BPtr, double* Y, double* reserved_space, int use_bias,\n                            int activation, void* lt_workspace);\n\ntemplate int mlp_bp<double>(double* X, double* Y, int input_features, int batch_size, double** WPtr, int num_layers,\n                            int* output_features, double* dY, double* reserved_space, double* work_space, double* dX,\n                            double** dwPtr, double** dbPtr, bool requires_grad, int use_bias, int activation);\n\ntemplate size_t get_mlp_bp_workspace_in_bytes<float>(int batch_size, int num_layers, const int* output_features);\ntemplate size_t get_mlp_bp_workspace_in_bytes<at::Half>(int batch_size, int num_layers, const int* output_features);\ntemplate size_t get_mlp_bp_workspace_in_bytes<double>(int batch_size, int num_layers, const int* output_features);\n", "source": "/kaggle/working/apex/csrc/mlp_cuda.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n\n__global__ void update_scale_hysteresis_cuda_kernel(float* current_scale, int* growth_tracker, int* hysteresis_tracker,\n                                                    const float* found_inf, double growth_factor, double backoff_factor,\n                                                    int growth_interval, int hysteresis) {\n  if (*found_inf > 0) {\n    *hysteresis_tracker -= 1;\n\n    // Only reset the growth tracker when hysteresis is larger than zero\n    if (*hysteresis_tracker > 0) {\n      *growth_tracker = 0;\n      return;\n    }\n  }\n\n  if (*found_inf) {\n    *current_scale = (*current_scale) * backoff_factor;\n    *growth_tracker = 0;\n  } else {\n    // Entering this branch means we just carried out a successful step,\n    // so growth_tracker is incremented before comparing to growth_interval.\n    auto successful = (*growth_tracker) + 1;\n    if (successful == growth_interval) {\n      auto new_scale = static_cast<float>((*current_scale) * growth_factor);\n      // Do not grow the scale past fp32 bounds to inf.\n      if (isfinite(new_scale)) {\n        *current_scale = new_scale;\n      }\n      *growth_tracker = 0;\n    } else {\n      *growth_tracker = successful;\n    }\n  }\n\n  // Reset the hysteresis tracker if no infs are found\n  if (*found_inf <= 0) {\n    *hysteresis_tracker = hysteresis;\n  }\n}\n\nat::Tensor update_scale_hysteresis_cuda(at::Tensor current_scale, at::Tensor growth_tracker,\n                                        at::Tensor hysteresis_tracker, at::Tensor found_inf, const double growth_factor,\n                                        const double backoff_factor, const int64_t growth_interval,\n                                        const int hysteresis) {\n  update_scale_hysteresis_cuda_kernel<<<1, 1, 0, at::cuda::getCurrentCUDAStream()>>>(\n      current_scale.mutable_data_ptr<float>(), growth_tracker.mutable_data_ptr<int>(),\n      hysteresis_tracker.mutable_data_ptr<int>(), found_inf.const_data_ptr<float>(), growth_factor, backoff_factor,\n      growth_interval, hysteresis);\n\n  AT_CUDA_CHECK(cudaGetLastError());\n\n  return current_scale;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n\n__global__ void update_scale_hysteresis_cuda_kernel(float* current_scale, int* growth_tracker, int* hysteresis_tracker,\n                                                    const float* found_inf, double growth_factor, double backoff_factor,\n                                                    int growth_interval, int hysteresis) {\n  if (*found_inf > 0) {\n    *hysteresis_tracker -= 1;\n\n    // Only reset the growth tracker when hysteresis is larger than zero\n    if (*hysteresis_tracker > 0) {\n      *growth_tracker = 0;\n      return;\n    }\n  }\n\n  if (*found_inf) {\n    *current_scale = (*current_scale) * backoff_factor;\n    *growth_tracker = 0;\n  } else {\n    // Entering this branch means we just carried out a successful step,\n    // so growth_tracker is incremented before comparing to growth_interval.\n    auto successful = (*growth_tracker) + 1;\n    if (successful == growth_interval) {\n      auto new_scale = static_cast<float>((*current_scale) * growth_factor);\n      // Do not grow the scale past fp32 bounds to inf.\n      if (isfinite(new_scale)) {\n        *current_scale = new_scale;\n      }\n      *growth_tracker = 0;\n    } else {\n      *growth_tracker = successful;\n    }\n  }\n\n  // Reset the hysteresis tracker if no infs are found\n  if (*found_inf <= 0) {\n    *hysteresis_tracker = hysteresis;\n  }\n}\n\nat::Tensor update_scale_hysteresis_cuda(at::Tensor current_scale, at::Tensor growth_tracker,\n                                        at::Tensor hysteresis_tracker, at::Tensor found_inf, const double growth_factor,\n                                        const double backoff_factor, const int64_t growth_interval,\n                                        const int hysteresis) {\n  update_scale_hysteresis_cuda_kernel<<<1, 1, 0, at::cuda::getCurrentCUDAStream()>>>(\n      current_scale.mutable_data_ptr<float>(), growth_tracker.mutable_data_ptr<int>(),\n      hysteresis_tracker.mutable_data_ptr<int>(), found_inf.const_data_ptr<float>(), growth_factor, backoff_factor,\n      growth_interval, hysteresis);\n\n  AT_CUDA_CHECK(hipGetLastError());\n\n  return current_scale;\n}\n", "source": "/kaggle/working/apex/csrc/update_scale_hysteresis.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    if (*noop_gmem) {\n      return;\n    }\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                        int max_chunks_per_tensor, volatile int* noop_gmem) {\n  if (*noop_gmem) {\n    return;\n  }\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_mp_cuda(int chunk_size, at::Tensor noop_flag,\n                                                               std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                               at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_mp_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                            output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr,\n                            per_tensor, max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor,\n      noop_flag.data_ptr<int>());\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    if (*noop_gmem) {\n      return;\n    }\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                        int max_chunks_per_tensor, volatile int* noop_gmem) {\n  if (*noop_gmem) {\n    return;\n  }\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_mp_cuda(int chunk_size, at::Tensor noop_flag,\n                                                               std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                               at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_mp_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                            output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr,\n                            per_tensor, max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(hipGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor,\n      noop_flag.data_ptr<int>());\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n", "source": "/kaggle/working/apex/csrc/multi_tensor_l2norm_kernel_mp.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\ntemplate <typename x_t>\nstruct UnscaleL2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             const float* inv_scale, float* output, float* output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]) * (*inv_scale);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]) * (*inv_scale);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                        int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\n__global__ void cleanup_v2(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                           int max_chunks_per_tensor, int norm_type, float alpha, float beta) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    if (norm_type == 0) {\n      float final = reduce_block_into_lanes_max_op(vals, val);\n      if (threadIdx.x == 0) *ret = alpha * (*ret) + beta * final;\n    } else {\n      float final = reduce_block_into_lanes(vals, val);\n      if (threadIdx.x == 0) *ret = sqrt(alpha * (*ret) * (*ret) + beta * final);\n    }\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    if (norm_type == 0) {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val = fmaxf(fabsf(val), fabsf(output_this_tensor[i]));\n\n      float final = reduce_block_into_lanes_max_op(vals, val);\n\n      if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = alpha * ret_per_tensor[blockIdx.x] + beta * final;\n    } else {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n      float final = reduce_block_into_lanes(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] =\n            sqrt(alpha * ret_per_tensor[blockIdx.x] * ret_per_tensor[blockIdx.x] + beta * final);\n    }\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_cuda(int chunk_size, at::Tensor noop_flag,\n                                                            std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                            at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                            output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr,\n                            per_tensor, max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_unscale_l2norm_cuda(int chunk_size, at::Tensor noop_flag,\n                                                                    std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                                    at::Tensor inv_scale,\n                                                                    at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_unscale_l2norm_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, UnscaleL2NormFunctor<scalar_t_0>(),\n                            inv_scale.data_ptr<float>(), output.data_ptr<float>(),\n                            per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, per_tensor,\n                            max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n\n// Compute and update grad norm\n// Here use a per tensor norm, and blend new norm(n) and old norm(gn) by\n// L-2: gn = sqrt(a * gn^2 + b * n^2)\n// L-inf: gn = a * gn + b * n\nvoid multi_tensor_norm_out_cuda(int chunk_size, at::Tensor noop_flag, std::vector<std::vector<at::Tensor>> tensor_lists,\n                                at::Tensor out, const float alpha, const float beta, const int norm_type) {\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  TORCH_CHECK(tensor_lists[0][0].device() == noop_flag.device(), \"noop flag should be on the same device as tensors\");\n  // we don't need global thus uses empty here\n  auto output = at::empty({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  for (int t = 0; t < ntensors; t++) {\n    int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n    if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n  }\n\n  // Although it is single write then read, still need to be zero\n  // Since tailing element also participate cleanup\n  output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n\n  if (norm_type == 0) {\n    DISPATCH_FLOAT_AND_HALF(tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_maxnorm_cuda\",\n                            multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                  MaxNormFunctor<scalar_t_0>(), output.data_ptr<float>(),\n                                                  output_per_tensor.data_ptr<float>(), true, max_chunks_per_tensor);)\n  } else {\n    DISPATCH_FLOAT_HALF_AND_BFLOAT(\n        tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_cuda\",\n        multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                              output.data_ptr<float>(), output_per_tensor.data_ptr<float>(), true,\n                              max_chunks_per_tensor);)\n  }\n  AT_CUDA_CHECK(cudaGetLastError());\n\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n\n  // Adding the following device guard since it happens sometimes that the\n  // tensors are on one device and the cuda stream is on another device which\n  // results in ILLEGAL MEM ACCESS error.\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup_v2<<<ntensors, 512, 0, stream>>>(output.data_ptr<float>(), output_per_tensor.data_ptr<float>(),\n                                           ret.data_ptr<float>(), out.data_ptr<float>(), true, max_chunks_per_tensor,\n                                           norm_type, alpha, beta);\n\n  return;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename x_t>\nstruct L2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\ntemplate <typename x_t>\nstruct UnscaleL2NormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             const float* inv_scale, float* output, float* output_per_tensor,\n                                             bool per_tensor, int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]) * (*inv_scale);\n          vals[ii] += next * next;\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]) * (*inv_scale);\n            vals[ii] += next * next;\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                        int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\n__global__ void cleanup_v2(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                           int max_chunks_per_tensor, int norm_type, float alpha, float beta) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    if (norm_type == 0) {\n      float final = reduce_block_into_lanes_max_op(vals, val);\n      if (threadIdx.x == 0) *ret = alpha * (*ret) + beta * final;\n    } else {\n      float final = reduce_block_into_lanes(vals, val);\n      if (threadIdx.x == 0) *ret = sqrt(alpha * (*ret) * (*ret) + beta * final);\n    }\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    if (norm_type == 0) {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x)\n        val = fmaxf(fabsf(val), fabsf(output_this_tensor[i]));\n\n      float final = reduce_block_into_lanes_max_op(vals, val);\n\n      if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = alpha * ret_per_tensor[blockIdx.x] + beta * final;\n    } else {\n      float val = 0;\n      for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n      float final = reduce_block_into_lanes(vals, val);\n\n      if (threadIdx.x == 0)\n        ret_per_tensor[blockIdx.x] =\n            sqrt(alpha * ret_per_tensor[blockIdx.x] * ret_per_tensor[blockIdx.x] + beta * final);\n    }\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_cuda(int chunk_size, at::Tensor noop_flag,\n                                                            std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                            at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                            output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr,\n                            per_tensor, max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(hipGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_unscale_l2norm_cuda(int chunk_size, at::Tensor noop_flag,\n                                                                    std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                                    at::Tensor inv_scale,\n                                                                    at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_unscale_l2norm_cuda\",\n      multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, UnscaleL2NormFunctor<scalar_t_0>(),\n                            inv_scale.data_ptr<float>(), output.data_ptr<float>(),\n                            per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, per_tensor,\n                            max_chunks_per_tensor);)\n\n  AT_CUDA_CHECK(hipGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n\n// Compute and update grad norm\n// Here use a per tensor norm, and blend new norm(n) and old norm(gn) by\n// L-2: gn = sqrt(a * gn^2 + b * n^2)\n// L-inf: gn = a * gn + b * n\nvoid multi_tensor_norm_out_cuda(int chunk_size, at::Tensor noop_flag, std::vector<std::vector<at::Tensor>> tensor_lists,\n                                at::Tensor out, const float alpha, const float beta, const int norm_type) {\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  TORCH_CHECK(tensor_lists[0][0].device() == noop_flag.device(), \"noop flag should be on the same device as tensors\");\n  // we don't need global thus uses empty here\n  auto output = at::empty({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  for (int t = 0; t < ntensors; t++) {\n    int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n    if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n  }\n\n  // Although it is single write then read, still need to be zero\n  // Since tailing element also participate cleanup\n  output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n\n  if (norm_type == 0) {\n    DISPATCH_FLOAT_AND_HALF(tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_maxnorm_cuda\",\n                            multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                  MaxNormFunctor<scalar_t_0>(), output.data_ptr<float>(),\n                                                  output_per_tensor.data_ptr<float>(), true, max_chunks_per_tensor);)\n  } else {\n    DISPATCH_FLOAT_HALF_AND_BFLOAT(\n        tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_cuda\",\n        multi_tensor_apply<1>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists, L2NormFunctor<scalar_t_0>(),\n                              output.data_ptr<float>(), output_per_tensor.data_ptr<float>(), true,\n                              max_chunks_per_tensor);)\n  }\n  AT_CUDA_CHECK(hipGetLastError());\n\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n\n  // Adding the following device guard since it happens sometimes that the\n  // tensors are on one device and the cuda stream is on another device which\n  // results in ILLEGAL MEM ACCESS error.\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup_v2<<<ntensors, 512, 0, stream>>>(output.data_ptr<float>(), output_per_tensor.data_ptr<float>(),\n                                           ret.data_ptr<float>(), out.data_ptr<float>(), true, max_chunks_per_tensor,\n                                           norm_type, alpha, beta);\n\n  return;\n}\n", "source": "/kaggle/working/apex/csrc/multi_tensor_l2norm_kernel.cu"}
{"input": "#include <cuda.h>\n#include <cuda_runtime.h>\n\n#include <optional>\n\n#include \"ATen/ATen.h\"\n#include \"ATen/AccumulateType.h\"\n#include \"ATen/cuda/CUDAContext.h\"\n#include \"ATen/cuda/DeviceUtils.cuh\"\n#include \"static_switch.h\"\n#include \"type_shim.h\"\n\ntemplate <typename U>\n__device__ void cuWelfordOnlineSum(const U curr, U& mu, U& sigma2, U& count) {\n  count = count + U(1);\n  U delta = curr - mu;\n  U lmean = mu + delta / count;\n  mu = lmean;\n  U delta2 = curr - lmean;\n  sigma2 = sigma2 + delta * delta2;\n}\n\ntemplate <typename U>\n__device__ void cuChanOnlineSum(const U muB, const U sigma2B, const U countB, U& mu, U& sigma2, U& count) {\n  U delta = muB - mu;\n  U nA = count;\n  U nB = countB;\n  count = count + countB;\n  U nX = count;\n  if (nX > U(0)) {\n    nA = nA / nX;\n    nB = nB / nX;\n    mu = nA * mu + nB * muB;\n    sigma2 = sigma2 + sigma2B + delta * delta * nA * nB * nX;\n  } else {\n    mu = U(0);\n    sigma2 = U(0);\n  }\n}\n\ntemplate <typename U>\n__device__ void cuRMSOnlineSum(const U curr, U& sigma2) {\n  sigma2 = sigma2 + curr * curr;\n}\n\ntemplate <typename U>\n__device__ void cuChanRMSOnlineSum(const U sigma2B, U& sigma2) {\n  sigma2 = sigma2 + sigma2B;\n}\n\ntemplate <typename T, typename U>\n__device__ void cuWelfordMuSigma2(const T* __restrict__ vals, const int n1, const int n2, const int i1, U& mu,\n                                  U& sigma2, U* buf, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensor is contiguous\n  // 3) 2*blockDim.y*sizeof(U)+blockDim.y*sizeof(int) shared memory available.\n  //\n  // compute variance and mean over n2\n  U count = U(0);\n  mu = U(0);\n  sigma2 = U(0);\n  if (i1 < n1) {\n    // one warp normalizes one n1 index,\n    // synchronization is implicit\n    // initialize with standard Welford algorithm\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    const T* lvals = vals + i1 * n2;\n    int l = 4 * thrx;\n    for (; l + 3 < n2; l += 4 * numx) {\n      for (int k = 0; k < 4; ++k) {\n        U curr = static_cast<U>(lvals[l + k]);\n        if (!rms_only) {\n          cuWelfordOnlineSum<U>(curr, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum<U>(curr, sigma2);\n        }\n      }\n    }\n    for (; l < n2; ++l) {\n      U curr = static_cast<U>(lvals[l]);\n      if (!rms_only) {\n        cuWelfordOnlineSum<U>(curr, mu, sigma2, count);\n      } else {\n        cuRMSOnlineSum<U>(curr, sigma2);\n      }\n    }\n    // intra-warp reductions\n    for (int l = 0; l <= 4; ++l) {\n      int srcLaneB = (threadIdx.x + (1 << l)) & 31;\n      U sigma2B = WARP_SHFL(sigma2, srcLaneB);\n      if (!rms_only) {\n        U muB = WARP_SHFL(mu, srcLaneB);\n        U countB = WARP_SHFL(count, srcLaneB);\n        cuChanOnlineSum<U>(muB, sigma2B, countB, mu, sigma2, count);\n      } else {\n        cuChanRMSOnlineSum<U>(sigma2B, sigma2);\n      }\n    }\n    // threadIdx.x == 0 has correct values for each warp\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      U* ubuf = (U*)buf;\n      U* ibuf = (U*)(ubuf + blockDim.y);\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.x == 0 && threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_y = threadIdx.y - offset;\n          if (!rms_only) {\n            ubuf[2 * wrt_y] = mu;\n            ibuf[wrt_y] = count;\n          }\n          ubuf[2 * wrt_y + 1] = sigma2;\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.x == 0 && threadIdx.y < offset) {\n          U sigma2B = ubuf[2 * threadIdx.y + 1];\n          if (!rms_only) {\n            U muB = ubuf[2 * threadIdx.y];\n            U countB = ibuf[threadIdx.y];\n            cuChanOnlineSum<U>(muB, sigma2B, countB, mu, sigma2, count);\n          } else {\n            cuChanRMSOnlineSum<U>(sigma2B, sigma2);\n          }\n        }\n        __syncthreads();\n      }\n      // threadIdx.x = 0 && threadIdx.y == 0 only thread that has correct values\n      if (threadIdx.x == 0 && threadIdx.y == 0) {\n        if (!rms_only) {\n          ubuf[0] = mu;\n        }\n        ubuf[1] = sigma2;\n      }\n      __syncthreads();\n      if (!rms_only) {\n        mu = ubuf[0];\n      }\n      sigma2 = ubuf[1] / U(n2);\n      // don't care about final value of count, we know count == n2\n    } else {\n      if (!rms_only) {\n        mu = WARP_SHFL(mu, 0);\n      }\n      sigma2 = WARP_SHFL(sigma2 / U(n2), 0);\n    }\n  }\n}\n\ntemplate <>\n__device__ void cuWelfordMuSigma2(const at::Half* __restrict__ vals, const int n1, const int n2, const int i1,\n                                  float& mu, float& sigma2, float* buf, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensor is contiguous\n  // 3) 2*blockDim.y*sizeof(U)+blockDim.y*sizeof(int) shared memory available.\n  //\n  // compute variance and mean over n2\n  float count = 0.0f;\n  mu = float(0);\n  sigma2 = float(0);\n  if (i1 < n1) {\n    // one warp normalizes one n1 index,\n    // synchronization is implicit\n    // initialize with standard Welford algorithm\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    const at::Half* lvals = vals + i1 * n2;\n    int l = 8 * thrx;\n    if ((((size_t)lvals) & 3) != 0) {\n      // 16 bit alignment\n      // first thread consumes first point\n      if (thrx == 0) {\n        float curr = static_cast<float>(lvals[0]);\n        if (!rms_only) {\n          cuWelfordOnlineSum(curr, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum(curr, sigma2);\n        }\n      }\n      ++l;\n    }\n    // at this point, lvals[l] are 32 bit aligned for all threads.\n    for (; l + 7 < n2; l += 8 * numx) {\n      for (int k = 0; k < 8; k += 2) {\n        float2 curr = __half22float2(*((__half2*)(lvals + l + k)));\n        if (!rms_only) {\n          cuWelfordOnlineSum(curr.x, mu, sigma2, count);\n          cuWelfordOnlineSum(curr.y, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum(curr.x, sigma2);\n          cuRMSOnlineSum(curr.y, sigma2);\n        }\n      }\n    }\n    for (; l < n2; ++l) {\n      float curr = static_cast<float>(lvals[l]);\n      if (!rms_only) {\n        cuWelfordOnlineSum(curr, mu, sigma2, count);\n      } else {\n        cuRMSOnlineSum(curr, sigma2);\n      }\n    }\n    // intra-warp reductions\n    for (int l = 0; l <= 4; ++l) {\n      int srcLaneB = (threadIdx.x + (1 << l)) & 31;\n      float sigma2B = WARP_SHFL(sigma2, srcLaneB);\n      if (!rms_only) {\n        float muB = WARP_SHFL(mu, srcLaneB);\n        float countB = WARP_SHFL(count, srcLaneB);\n        cuChanOnlineSum(muB, sigma2B, countB, mu, sigma2, count);\n      } else {\n        cuChanRMSOnlineSum(sigma2B, sigma2);\n      }\n    }\n    // threadIdx.x == 0 has correct values for each warp\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      float* ubuf = (float*)buf;\n      float* ibuf = (float*)(ubuf + blockDim.y);\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.x == 0 && threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_y = threadIdx.y - offset;\n          ubuf[2 * wrt_y + 1] = sigma2;\n          if (!rms_only) {\n            ubuf[2 * wrt_y] = mu;\n            ibuf[wrt_y] = count;\n          }\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.x == 0 && threadIdx.y < offset) {\n          float sigma2B = ubuf[2 * threadIdx.y + 1];\n          if (!rms_only) {\n            float muB = ubuf[2 * threadIdx.y];\n            float countB = ibuf[threadIdx.y];\n            cuChanOnlineSum(muB, sigma2B, countB, mu, sigma2, count);\n          } else {\n            cuChanRMSOnlineSum(sigma2B, sigma2);\n          }\n        }\n        __syncthreads();\n      }\n      // threadIdx.x = 0 && threadIdx.y == 0 only thread that has correct values\n      if (threadIdx.x == 0 && threadIdx.y == 0) {\n        if (!rms_only) {\n          ubuf[0] = mu;\n        }\n        ubuf[1] = sigma2;\n      }\n      __syncthreads();\n      if (!rms_only) {\n        mu = ubuf[0];\n      }\n      sigma2 = ubuf[1] / float(n2);\n      // don't care about final value of count, we know count == n2\n    } else {\n      if (!rms_only) {\n        mu = WARP_SHFL(mu, 0);\n      }\n      sigma2 = WARP_SHFL(sigma2 / float(n2), 0);\n    }\n  }\n}\n\ntemplate <typename U>\nU rsqrt(U v) {\n  return U(1) / sqrt(v);\n}\ntemplate <>\nfloat rsqrt(float v) {\n  return rsqrtf(v);\n}\ntemplate <>\ndouble rsqrt(double v) {\n  return rsqrt(v);\n}\n\nnamespace {\n// This is the un-specialized struct.  Note that we prevent instantiation of this\n// struct by putting an undefined symbol in the function body so it won't compile.\n//  template <typename T>\n//  struct SharedMemory\n//  {\n//      // Ensure that we won't compile any un-specialized types\n//      __device__ T *getPointer()\n//      {\n//          extern __device__ void error(void);\n//          error();\n//          return nullptr;\n//      }\n//  };\n// https://github.com/NVIDIA/apex/issues/246\ntemplate <typename T>\nstruct SharedMemory;\n\ntemplate <>\nstruct SharedMemory<float> {\n  __device__ float* getPointer() {\n    extern __shared__ float s_float[];\n    return s_float;\n  }\n};\n\ntemplate <>\nstruct SharedMemory<double> {\n  __device__ double* getPointer() {\n    extern __shared__ double s_double[];\n    return s_double;\n  }\n};\n}  // namespace\n\ntemplate <typename T, typename U, typename V>\n__device__ void cuApplyLayerNorm_(V* __restrict__ output_vals, U* __restrict__ mean, U* __restrict__ invvar,\n                                  const T* __restrict__ vals, const int n1, const int n2, const U epsilon,\n                                  const V* __restrict__ gamma, const V* __restrict__ beta, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensors are contiguous\n  //\n  for (auto i1 = blockIdx.y; i1 < n1; i1 += gridDim.y) {\n    SharedMemory<U> shared;\n    U* buf = shared.getPointer();\n    U mu, sigma2;\n    cuWelfordMuSigma2(vals, n1, n2, i1, mu, sigma2, buf, rms_only);\n\n    const T* lvals = vals + i1 * n2;\n    V* ovals = output_vals + i1 * n2;\n    U c_invvar = rsqrt(sigma2 + epsilon);\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    if (gamma != nullptr && (beta != nullptr || rms_only)) {\n      for (int i = thrx; i < n2; i += numx) {\n        U curr = static_cast<U>(lvals[i]);\n        if (!rms_only) {\n          ovals[i] = gamma[i] * static_cast<V>(c_invvar * (curr - mu)) + beta[i];\n        } else {\n          ovals[i] = gamma[i] * static_cast<V>(c_invvar * curr);\n        }\n      }\n    } else {\n      for (int i = thrx; i < n2; i += numx) {\n        U curr = static_cast<U>(lvals[i]);\n        if (!rms_only) {\n          ovals[i] = static_cast<V>(c_invvar * (curr - mu));\n        } else {\n          ovals[i] = static_cast<V>(c_invvar * curr);\n        }\n      }\n    }\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      if (!rms_only) {\n        mean[i1] = mu;\n      }\n      invvar[i1] = c_invvar;\n    }\n    __syncthreads();\n  }\n}\n\ntemplate <typename T, typename U, typename V = T>\n__global__ void cuApplyLayerNorm(V* __restrict__ output_vals, U* __restrict__ mean, U* __restrict__ invvar,\n                                 const T* __restrict__ vals, const int n1, const int n2, const U epsilon,\n                                 const V* __restrict__ gamma, const V* __restrict__ beta) {\n  cuApplyLayerNorm_<T, U, V>(output_vals, mean, invvar, vals, n1, n2, epsilon, gamma, beta, false);\n}\n\ntemplate <typename T, typename U, typename V = T>\n__global__ void cuApplyRMSNorm(V* __restrict__ output_vals, U* __restrict__ invvar, const T* __restrict__ vals,\n                               const int n1, const int n2, const U epsilon, const V* __restrict__ gamma) {\n  cuApplyLayerNorm_<T, U, V>(output_vals, nullptr, invvar, vals, n1, n2, epsilon, gamma, nullptr, true);\n}\n\ntemplate <typename V>\n__device__ V clamp_by_magnitude(V curr_gamma, double eps) {\n  const V kMinGamma = V(eps);\n  if (curr_gamma >= 0) {\n    if (curr_gamma < kMinGamma) {\n      return kMinGamma;\n    } else {\n      return curr_gamma;\n    }\n  } else {\n    if (curr_gamma > -kMinGamma) {\n      return -kMinGamma;\n    } else {\n      return curr_gamma;\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__device__ void cuLoadWriteStridedInputs(const int i1_block, const int thr_load_row_off, const int thr_load_col_off,\n                                         const int i2_off, const int row_stride, U* warp_buf1, U* warp_buf2,\n                                         const T* input_or_output, const V* dout, const int i1_end, const int n2,\n                                         const U* __restrict__ mean, const U* __restrict__ invvar,\n                                         const V* __restrict__ gamma, const V* __restrict__ beta, const double eps,\n                                         bool rms_only) {\n  int i1 = i1_block + thr_load_row_off;\n  if (i1 < i1_end) {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int i2 = i2_off + k;\n      int load_idx = i1 * n2 + i2;\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (i2 < n2) {\n        U c_h = static_cast<U>(input_or_output[load_idx]);\n        U curr_dout = static_cast<U>(dout[load_idx]);\n        if (!rms_only) {\n          warp_buf1[write_idx] = curr_dout;\n          if (MemoryEfficient) {\n            U curr_beta = static_cast<U>(beta[i2]);\n            warp_buf2[write_idx] = curr_dout * (c_h - curr_beta) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] = curr_dout * (c_h - mean[i1]) * invvar[i1];\n          }\n        } else {\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] = curr_dout * (c_h) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] = curr_dout * (c_h)*invvar[i1];\n          }\n        }\n      } else {\n        if (!rms_only) {\n          warp_buf1[write_idx] = U(0);\n        }\n        warp_buf2[write_idx] = U(0);\n      }\n    }\n  } else {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (!rms_only) {\n        warp_buf1[write_idx] = U(0);\n      }\n      warp_buf2[write_idx] = U(0);\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__device__ void cuLoadAddStridedInputs(const int i1_block, const int thr_load_row_off, const int thr_load_col_off,\n                                       const int i2_off, const int row_stride, U* warp_buf1, U* warp_buf2,\n                                       const T* input_or_output, const V* dout, const int i1_end, const int n2,\n                                       const U* __restrict__ mean, const U* __restrict__ invvar,\n                                       const V* __restrict__ gamma, const V* __restrict__ beta, const double eps,\n                                       bool rms_only) {\n  int i1 = i1_block + thr_load_row_off;\n  if (i1 < i1_end) {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int i2 = i2_off + k;\n      int load_idx = i1 * n2 + i2;\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (i2 < n2) {\n        U c_h = static_cast<U>(input_or_output[load_idx]);\n        U curr_dout = static_cast<U>(dout[load_idx]);\n        if (!rms_only) {\n          U curr_beta = static_cast<U>(beta[i2]);\n          warp_buf1[write_idx] += curr_dout;\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] += curr_dout * (c_h - curr_beta) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] += curr_dout * (c_h - mean[i1]) * invvar[i1];\n          }\n        } else {\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] += curr_dout * (c_h) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] += curr_dout * (c_h)*invvar[i1];\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__global__ void cuComputePartGradGammaBeta(const V* __restrict__ dout, const T* __restrict__ input_or_output,\n                                           const int n1, const int n2, const U* __restrict__ mean,\n                                           const U* __restrict__ invvar, U epsilon, const V* __restrict__ gamma,\n                                           const V* __restrict__ beta, U* part_grad_gamma, U* part_grad_beta,\n                                           const double eps, bool rms_only) {\n  const int numsegs_n1 = (n1 + blockDim.y * blockDim.y - 1) / (blockDim.y * blockDim.y);\n  const int segs_per_block = (numsegs_n1 + gridDim.y - 1) / gridDim.y;\n  const int i1_beg = blockIdx.y * segs_per_block * blockDim.y * blockDim.y;\n  const int i1_beg_plus_one = (blockIdx.y + 1) * segs_per_block * blockDim.y * blockDim.y;\n  const int i1_end = i1_beg_plus_one < n1 ? i1_beg_plus_one : n1;\n  const int row_stride = blockDim.x + 1;\n  const int thr_load_col_off = (threadIdx.x * blockDim.y) & (blockDim.x - 1);\n  const int thr_load_row_off = (threadIdx.x * blockDim.y) / blockDim.x + threadIdx.y * blockDim.y;\n  const int i2_off = blockIdx.x * blockDim.x + thr_load_col_off;\n  SharedMemory<U> shared;\n  U* buf = shared.getPointer();  // buf has at least blockDim.x * blockDim.y * blockDim.y + (blockDim.y -\n                                 // 1)*(blockDim.x/blockDim.y) elements\n  U* warp_buf1 = (U*)buf;\n  U* warp_buf2 = warp_buf1 + blockDim.y * blockDim.y * row_stride;\n  // compute partial sums from strided inputs\n  // do this to increase number of loads in flight\n  cuLoadWriteStridedInputs<T, U, V, MemoryEfficient>(i1_beg, thr_load_row_off, thr_load_col_off, i2_off, row_stride,\n                                                     warp_buf1, warp_buf2, input_or_output, dout, i1_end, n2, mean,\n                                                     invvar, gamma, beta, eps, rms_only);\n  for (int i1_block = i1_beg + blockDim.y * blockDim.y; i1_block < i1_end; i1_block += blockDim.y * blockDim.y) {\n    cuLoadAddStridedInputs<T, U, V, MemoryEfficient>(i1_block, thr_load_row_off, thr_load_col_off, i2_off, row_stride,\n                                                     warp_buf1, warp_buf2, input_or_output, dout, i1_end, n2, mean,\n                                                     invvar, gamma, beta, eps, rms_only);\n  }\n  __syncthreads();\n  // inter-warp reductions\n  // sum within each warp\n  U acc1 = U(0);\n  U acc2 = U(0);\n  for (int k = 0; k < blockDim.y; ++k) {\n    int row1 = threadIdx.y + k * blockDim.y;\n    int idx1 = row1 * row_stride + threadIdx.x;\n    if (!rms_only) {\n      acc1 += warp_buf1[idx1];\n    }\n    acc2 += warp_buf2[idx1];\n  }\n  if (!rms_only) {\n    warp_buf1[threadIdx.y * row_stride + threadIdx.x] = acc1;\n  }\n  warp_buf2[threadIdx.y * row_stride + threadIdx.x] = acc2;\n  __syncthreads();\n  // sum all warps\n  for (int offset = blockDim.y / 2; offset > 1; offset /= 2) {\n    if (threadIdx.y < offset) {\n      int row1 = threadIdx.y;\n      int row2 = threadIdx.y + offset;\n      int idx1 = row1 * row_stride + threadIdx.x;\n      int idx2 = row2 * row_stride + threadIdx.x;\n      if (!rms_only) {\n        warp_buf1[idx1] += warp_buf1[idx2];\n      }\n      warp_buf2[idx1] += warp_buf2[idx2];\n    }\n    __syncthreads();\n  }\n  int i2 = blockIdx.x * blockDim.x + threadIdx.x;\n  if (threadIdx.y == 0 && i2 < n2) {\n    int row1 = threadIdx.y;\n    int row2 = threadIdx.y + 1;\n    int idx1 = row1 * row_stride + threadIdx.x;\n    int idx2 = row2 * row_stride + threadIdx.x;\n    if (!rms_only) {\n      part_grad_beta[blockIdx.y * n2 + i2] = warp_buf1[idx1] + warp_buf1[idx2];\n    }\n    part_grad_gamma[blockIdx.y * n2 + i2] = warp_buf2[idx1] + warp_buf2[idx2];\n  }\n}\n\ntemplate <typename U, typename V>\n__global__ void cuComputeGradGammaBeta(const U* part_grad_gamma, const U* part_grad_beta, const int part_size,\n                                       const int n1, const int n2, V* grad_gamma, V* grad_beta, bool rms_only) {\n  // sum partial gradients for gamma and beta\n  SharedMemory<U> shared;\n  U* buf = shared.getPointer();\n  int i2 = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i2 < n2) {\n    // each warp does sequential reductions until reduced part_size is num_warps\n    int num_warp_reductions = part_size / blockDim.y;\n    U sum_gamma = U(0);\n    U sum_beta = U(0);\n    const U* part_grad_gamma_ptr = part_grad_gamma + threadIdx.y * num_warp_reductions * n2 + i2;\n    const U* part_grad_beta_ptr = part_grad_beta + threadIdx.y * num_warp_reductions * n2 + i2;\n    for (int warp_offset = 0; warp_offset < num_warp_reductions; ++warp_offset) {\n      sum_gamma += part_grad_gamma_ptr[warp_offset * n2];\n      if (!rms_only) {\n        sum_beta += part_grad_beta_ptr[warp_offset * n2];\n      }\n    }\n    // inter-warp reductions\n    const int nbsize3 = blockDim.x * blockDim.y / 2;\n    for (int offset = blockDim.y / 2; offset >= 1; offset /= 2) {\n      // top half write to shared memory\n      if (threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n        const int write_idx = (threadIdx.y - offset) * blockDim.x + threadIdx.x;\n        buf[write_idx] = sum_gamma;\n        if (!rms_only) {\n          buf[write_idx + nbsize3] = sum_beta;\n        }\n      }\n      __syncthreads();\n      // bottom half sums\n      if (threadIdx.y < offset) {\n        const int read_idx = threadIdx.y * blockDim.x + threadIdx.x;\n        sum_gamma += buf[read_idx];\n        if (!rms_only) {\n          sum_beta += buf[read_idx + nbsize3];\n        }\n      }\n      __syncthreads();\n    }\n    // write out fully summed gradients\n    if (threadIdx.y == 0) {\n      grad_gamma[i2] = sum_gamma;\n      if (!rms_only) {\n        grad_beta[i2] = sum_beta;\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__global__ void cuComputeGradInput(const V* __restrict__ dout, const T* __restrict__ input_or_output, const int n1,\n                                   const int n2, const U* __restrict__ mean, const U* __restrict__ invvar, U epsilon,\n                                   const V* gamma, const V* beta, T* grad_input, const double eps, bool rms_only) {\n  for (auto i1 = blockIdx.y; i1 < n1; i1 += gridDim.y) {\n    U sum_loss1 = U(0);\n    U sum_loss2 = U(0);\n    const T* k_h = input_or_output + i1 * n2;\n    const V* k_dout = dout + i1 * n2;\n    const U c_invvar = invvar[i1];\n    const U c_mean = !MemoryEfficient ? mean[i1] : 0.;\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    if (gamma != nullptr) {\n      int l = 4 * thrx;\n      for (; l + 3 < n2; l += 4 * numx) {\n        for (int k = 0; k < 4; ++k) {\n          const U c_h = static_cast<U>(k_h[l + k]);\n          const U c_loss = static_cast<U>(k_dout[l + k]);\n          if (!rms_only) {\n            sum_loss1 += c_loss * gamma[l + k];\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * (c_h - beta[l + k]);\n            } else {\n              sum_loss2 += c_loss * gamma[l + k] * (c_h - c_mean) * c_invvar;\n            }\n          } else {\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * gamma[l + k] * (c_h)*c_invvar;\n            }\n          }\n        }\n      }\n      for (; l < n2; ++l) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        if (!rms_only) {\n          sum_loss1 += c_loss * gamma[l];\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * (c_h - beta[l]);\n          } else {\n            sum_loss2 += c_loss * gamma[l] * (c_h - c_mean) * c_invvar;\n          }\n        } else {\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * gamma[l] * (c_h)*c_invvar;\n          }\n        }\n      }\n    } else {\n      int l = 4 * thrx;\n      for (; l + 3 < n2; l += 4 * numx) {\n        for (int k = 0; k < 4; ++k) {\n          const U c_h = static_cast<U>(k_h[l + k]);\n          const U c_loss = static_cast<U>(k_dout[l + k]);\n          if (!rms_only) {\n            sum_loss1 += c_loss;\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * (c_h - c_mean) * c_invvar;\n            }\n          } else {\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * (c_h)*c_invvar;\n            }\n          }\n        }\n      }\n      for (; l < n2; ++l) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        if (!rms_only) {\n          sum_loss1 += c_loss;\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * (c_h - c_mean) * c_invvar;\n          }\n        } else {\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * (c_h)*c_invvar;\n          }\n        }\n      }\n    }\n    // intra-warp reductions\n    for (int mask = blockDim.x / 2; mask > 0; mask /= 2) {\n      if (!rms_only) {\n        sum_loss1 += WARP_SHFL_XOR(sum_loss1, mask);\n      }\n      sum_loss2 += WARP_SHFL_XOR(sum_loss2, mask);\n    }\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      SharedMemory<U> shared;\n      U* buf = shared.getPointer();\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_i = (threadIdx.y - offset) * blockDim.x + threadIdx.x;\n          if (!rms_only) {\n            buf[2 * wrt_i] = sum_loss1;\n          }\n          buf[2 * wrt_i + 1] = sum_loss2;\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.y < offset) {\n          const int read_i = threadIdx.y * blockDim.x + threadIdx.x;\n          if (!rms_only) {\n            sum_loss1 += buf[2 * read_i];\n          }\n          sum_loss2 += buf[2 * read_i + 1];\n        }\n        __syncthreads();\n      }\n      if (threadIdx.y == 0) {\n        if (!rms_only) {\n          buf[2 * threadIdx.x] = sum_loss1;\n        }\n        buf[2 * threadIdx.x + 1] = sum_loss2;\n      }\n      __syncthreads();\n      if (threadIdx.y != 0) {\n        if (!rms_only) {\n          sum_loss1 = buf[2 * threadIdx.x];\n        }\n        sum_loss2 = buf[2 * threadIdx.x + 1];\n      }\n    }\n    // all threads now have the two sums over l\n    U fH = (U)n2;\n    U term1 = (U(1) / fH) * c_invvar;\n    T* k_grad_input = grad_input + i1 * n2;\n    if (gamma != nullptr) {\n      for (int l = thrx; l < n2; l += numx) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        const U k_gamma = static_cast<U>(clamp_by_magnitude(gamma[l], eps));\n        U f_grad_input = fH * c_loss * k_gamma;\n        if (!rms_only) {\n          const U k_beta = beta[l];\n          f_grad_input -= sum_loss1;\n          if (MemoryEfficient) {\n            f_grad_input -= (c_h - k_beta) / k_gamma * sum_loss2;\n          } else {\n            f_grad_input -= (c_h - c_mean) * c_invvar * sum_loss2;\n          }\n        } else {\n          if (MemoryEfficient) {\n            f_grad_input -= c_h / k_gamma * sum_loss2;\n          } else {\n            f_grad_input -= c_h * c_invvar * sum_loss2;\n          }\n        }\n        f_grad_input *= term1;\n        k_grad_input[l] = static_cast<T>(f_grad_input);\n      }\n    } else {\n      for (int l = thrx; l < n2; l += numx) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        U f_grad_input = fH * c_loss;\n        if (!rms_only) {\n          f_grad_input -= sum_loss1;\n          if (MemoryEfficient) {\n            f_grad_input -= c_h * sum_loss2;\n          } else {\n            f_grad_input -= (c_h - c_mean) * c_invvar * sum_loss2;\n          }\n        } else {\n          if (MemoryEfficient) {\n            f_grad_input -= c_h * sum_loss2;\n          } else {\n            f_grad_input -= c_h * c_invvar * sum_loss2;\n          }\n        }\n        f_grad_input *= term1;\n        k_grad_input[l] = static_cast<T>(f_grad_input);\n      }\n    }\n    // prevent race where buf is written again before reads are done\n    __syncthreads();\n  }\n}\n\ntemplate <typename T, typename U, typename V = T>\nvoid HostApplyLayerNorm(V* output, U* mean, U* invvar, const T* input, int n1, int n2, double epsilon, const V* gamma,\n                        const V* beta) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n  const dim3 threads(32, 4, 1);\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks(1, std::min((uint64_t)n1, maxGridY), 1);\n  int nshared = threads.y > 1 ? threads.y * sizeof(U) + (threads.y / 2) * sizeof(U) : 0;\n  cuApplyLayerNorm<<<blocks, threads, nshared, stream>>>(output, mean, invvar, input, n1, n2, U(epsilon), gamma, beta);\n}\n\ntemplate <typename T, typename U, typename V = T>\nvoid HostApplyRMSNorm(V* output, U* invvar, const T* input, int n1, int n2, double epsilon, const V* gamma) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n  const dim3 threads(32, 4, 1);\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks(1, std::min((uint64_t)n1, maxGridY), 1);\n  int nshared = threads.y > 1 ? threads.y * sizeof(U) + (threads.y / 2) * sizeof(U) : 0;\n  cuApplyRMSNorm<<<blocks, threads, nshared, stream>>>(output, invvar, input, n1, n2, U(epsilon), gamma);\n}\n\nvoid cuda_layer_norm(at::Tensor& output, at::Tensor& mean, at::Tensor& invvar, const at::Tensor& input, int n1, int n2,\n                     at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma,\n                     const std::optional<at::Tensor>& beta, double epsilon) {\n  using namespace at;\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input.scalar_type(), output.scalar_type(), \"layer_norm_cuda_kernel\",\n      using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostApplyLayerNorm<scalar_t_in, accscalar_t, scalar_t_out>(\n          output.data_ptr<scalar_t_out>(), mean.data_ptr<accscalar_t>(), invvar.data_ptr<accscalar_t>(),\n          input.data_ptr<scalar_t_in>(), n1, n2, epsilon, gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr,\n          beta.has_value() ? beta->data_ptr<scalar_t_out>() : nullptr);)\n}\n\nvoid cuda_rms_norm(at::Tensor& output, at::Tensor& invvar, const at::Tensor& input, int n1, int n2,\n                   at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma, double epsilon) {\n  using namespace at;\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input.scalar_type(), output.scalar_type(), \"rms_norm_cuda_kernel\",\n      using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostApplyRMSNorm<scalar_t_in, accscalar_t, scalar_t_out>(\n          output.data_ptr<scalar_t_out>(), invvar.data_ptr<accscalar_t>(), input.data_ptr<scalar_t_in>(), n1, n2,\n          epsilon, gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr);)\n}\n\ntemplate <typename T, typename U = float, typename V = T>\nvoid HostLayerNormGradient(const V* dout, const U* mean, const U* invvar, at::Tensor& input_or_output, int n1, int n2,\n                           const V* gamma, const V* beta, double epsilon, T* grad_input, V* grad_gamma, V* grad_beta,\n                           bool memory_efficient) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n\n  if (gamma != nullptr && beta != nullptr) {\n    // compute grad_gamma(j) and grad_beta(j)\n    const int part_size = 16;\n    const dim3 threads2(32, 4, 1);\n    const dim3 blocks2((n2 + threads2.x - 1) / threads2.x, part_size, 1);\n    const int nshared2_a = 2 * sizeof(U) * threads2.y * threads2.y * (threads2.x + 1);\n    const int nshared2_b = threads2.x * threads2.y * sizeof(U);\n    const int nshared2 = nshared2_a > nshared2_b ? nshared2_a : nshared2_b;\n    // note (mkozuki): I can hard code part_grad_gamma's dtype as float given that\n    // the `cuda_layer_norm_gradient` doesn't support double.\n    const auto part_grad_dtype = (input_or_output.scalar_type() == at::ScalarType::Half ||\n                                  input_or_output.scalar_type() == at::ScalarType::BFloat16)\n                                     ? at::ScalarType::Float\n                                     : input_or_output.scalar_type();\n    at::Tensor part_grad_gamma = at::empty({part_size, n2}, input_or_output.options().dtype(part_grad_dtype));\n    at::Tensor part_grad_beta = at::empty_like(part_grad_gamma);\n    BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n      auto kernel = &cuComputePartGradGammaBeta<T, U, V, MemoryEfficient>;\n      kernel<<<blocks2, threads2, nshared2, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, mean, invvar,\n                                                      U(epsilon), gamma, beta, part_grad_gamma.data_ptr<U>(),\n                                                      part_grad_beta.data_ptr<U>(), epsilon, false);\n    });\n\n    const dim3 threads3(32, 8, 1);\n    const dim3 blocks3((n2 + threads2.x - 1) / threads2.x, 1, 1);\n    const int nshared3 = threads3.x * threads3.y * sizeof(U);\n    cuComputeGradGammaBeta<<<blocks3, threads3, nshared3, stream>>>(\n        part_grad_gamma.data_ptr<U>(), part_grad_beta.data_ptr<U>(), part_size, n1, n2, grad_gamma, grad_beta, false);\n  }\n\n  // compute grad_input\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks1(1, std::min((uint64_t)n1, maxGridY), 1);\n  const dim3 threads1(32, 4, 1);\n  int nshared = threads1.y > 1 ? threads1.y * threads1.x * sizeof(U) : 0;\n  BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n    auto kernel = cuComputeGradInput<T, U, V, MemoryEfficient>;\n    kernel<<<blocks1, threads1, nshared, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, mean, invvar,\n                                                   U(epsilon), gamma, beta, grad_input, epsilon, false);\n  });\n}\n\ntemplate <typename T, typename U = float, typename V = T>\nvoid HostRMSNormGradient(const V* dout, const U* invvar, at::Tensor& input_or_output, int n1, int n2, const V* gamma,\n                         double epsilon, T* grad_input, V* grad_gamma, bool memory_efficient) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n\n  if (gamma != nullptr) {\n    const int part_size = 16;\n    const dim3 threads2(32, 4, 1);\n    const dim3 blocks2((n2 + threads2.x - 1) / threads2.x, part_size, 1);\n    const int nshared2_a = 2 * sizeof(U) * threads2.y * threads2.y * (threads2.x + 1);\n    const int nshared2_b = threads2.x * threads2.y * sizeof(U);\n    const int nshared2 = nshared2_a > nshared2_b ? nshared2_a : nshared2_b;\n    // note (mkozuki): I can hard code part_grad_gamma's dtype as float given that\n    // the `cuda_layer_norm_gradient` doesn't support double.\n    const auto part_grad_dtype = (input_or_output.scalar_type() == at::ScalarType::Half ||\n                                  input_or_output.scalar_type() == at::ScalarType::BFloat16)\n                                     ? at::ScalarType::Float\n                                     : input_or_output.scalar_type();\n    at::Tensor part_grad_gamma = at::empty({part_size, n2}, input_or_output.options().dtype(part_grad_dtype));\n    BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n      auto kernel = &cuComputePartGradGammaBeta<T, U, V, MemoryEfficient>;\n      kernel<<<blocks2, threads2, nshared2, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, invvar, /* unused */\n                                                      invvar, U(epsilon), gamma, gamma,                    /* unused */\n                                                      part_grad_gamma.data_ptr<U>(),\n                                                      part_grad_gamma.data_ptr<U>(), /* unused */\n                                                      epsilon, true);\n    });\n\n    const dim3 threads3(32, 8, 1);\n    const dim3 blocks3((n2 + threads2.x - 1) / threads2.x, 1, 1);\n    const int nshared3 = threads3.x * threads3.y * sizeof(U);\n    cuComputeGradGammaBeta<<<blocks3, threads3, nshared3, stream>>>(\n        part_grad_gamma.data_ptr<U>(), part_grad_gamma.data_ptr<U>(), /* unused */\n        part_size, n1, n2, grad_gamma, grad_gamma,                    /* unused */\n        true);\n  }\n\n  // compute grad_input\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks1(1, std::min((uint64_t)n1, maxGridY), 1);\n  const dim3 threads1(32, 4, 1);\n  int nshared = threads1.y > 1 ? threads1.y * threads1.x * sizeof(U) : 0;\n  BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n    auto kernel = cuComputeGradInput<T, U, V, MemoryEfficient>;\n    kernel<<<blocks1, threads1, nshared, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, invvar, /* unused */\n                                                   invvar, U(epsilon), gamma, gamma,                    /* unused */\n                                                   grad_input, epsilon, true);\n  });\n}\n\nvoid cuda_layer_norm_gradient(at::Tensor& dout, const std::optional<at::Tensor>& mean, at::Tensor& invvar,\n                              at::Tensor& input_or_output, int n1, int n2, at::IntArrayRef normalized_shape,\n                              const std::optional<at::Tensor>& gamma, const std::optional<at::Tensor>& beta,\n                              double epsilon, at::Tensor& grad_input, const std::optional<at::Tensor>& grad_gamma,\n                              const std::optional<at::Tensor>& grad_beta, bool memory_efficient) {\n  using namespace at;\n  // we can do away with `accscalar_t` as there're only three dtypes: fp32, fp16, bf16\n  DISPATCH_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input_or_output.scalar_type(), gamma.has_value() ? gamma->scalar_type() : input_or_output.scalar_type(),\n      \"cuComputeGradInput\", using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostLayerNormGradient(dout.data_ptr<scalar_t_out>(), mean.has_value() ? mean->data_ptr<accscalar_t>() : nullptr,\n                            invvar.data_ptr<accscalar_t>(), input_or_output, n1, n2,\n                            // TMJ pass nullptr argument for gamma, beta, grad_gamma and grad_beta\n                            // if gamma Tensor is nullptr on input.\n                            gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr,\n                            gamma.has_value() ? beta->data_ptr<scalar_t_out>() : nullptr, epsilon,\n                            grad_input.data_ptr<scalar_t_in>(),\n                            gamma.has_value() ? grad_gamma->data_ptr<scalar_t_out>() : nullptr,\n                            gamma.has_value() ? grad_beta->data_ptr<scalar_t_out>() : nullptr, memory_efficient);)\n}\n\nvoid cuda_rms_norm_gradient(at::Tensor& dout, at::Tensor& invvar, at::Tensor& input_or_output, int n1, int n2,\n                            at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma, double epsilon,\n                            at::Tensor& grad_input, const std::optional<at::Tensor>& grad_gamma,\n                            bool memory_efficient) {\n  using namespace at;\n  // we can do away with `accscalar_t` as there're only three dtypes: fp32, fp16, bf16\n  // DISPATCH_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input_or_output.scalar_type(), gamma.has_value() ? gamma->scalar_type() : input_or_output.scalar_type(),\n      \"cuComputeGradInputRMS\", using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostRMSNormGradient(dout.data_ptr<scalar_t_out>(), invvar.data_ptr<accscalar_t>(), input_or_output, n1, n2,\n                          // TMJ pass nullptr argument for gamma, beta, grad_gamma and grad_beta\n                          // if gamma Tensor is nullptr on input.\n                          gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr, epsilon,\n                          grad_input.data_ptr<scalar_t_in>(),\n                          gamma.has_value() ? grad_gamma->data_ptr<scalar_t_out>() : nullptr, memory_efficient);)\n}\n", "output": "#include <hip/hip_runtime.h>\n\n#include <optional>\n\n#include \"ATen/ATen.h\"\n#include \"ATen/AccumulateType.h\"\n#include \"ATen/cuda/CUDAContext.h\"\n#include \"ATen/cuda/DeviceUtils.cuh\"\n#include \"static_switch.h\"\n#include \"type_shim.h\"\n\ntemplate <typename U>\n__device__ void cuWelfordOnlineSum(const U curr, U& mu, U& sigma2, U& count) {\n  count = count + U(1);\n  U delta = curr - mu;\n  U lmean = mu + delta / count;\n  mu = lmean;\n  U delta2 = curr - lmean;\n  sigma2 = sigma2 + delta * delta2;\n}\n\ntemplate <typename U>\n__device__ void cuChanOnlineSum(const U muB, const U sigma2B, const U countB, U& mu, U& sigma2, U& count) {\n  U delta = muB - mu;\n  U nA = count;\n  U nB = countB;\n  count = count + countB;\n  U nX = count;\n  if (nX > U(0)) {\n    nA = nA / nX;\n    nB = nB / nX;\n    mu = nA * mu + nB * muB;\n    sigma2 = sigma2 + sigma2B + delta * delta * nA * nB * nX;\n  } else {\n    mu = U(0);\n    sigma2 = U(0);\n  }\n}\n\ntemplate <typename U>\n__device__ void cuRMSOnlineSum(const U curr, U& sigma2) {\n  sigma2 = sigma2 + curr * curr;\n}\n\ntemplate <typename U>\n__device__ void cuChanRMSOnlineSum(const U sigma2B, U& sigma2) {\n  sigma2 = sigma2 + sigma2B;\n}\n\ntemplate <typename T, typename U>\n__device__ void cuWelfordMuSigma2(const T* __restrict__ vals, const int n1, const int n2, const int i1, U& mu,\n                                  U& sigma2, U* buf, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensor is contiguous\n  // 3) 2*blockDim.y*sizeof(U)+blockDim.y*sizeof(int) shared memory available.\n  //\n  // compute variance and mean over n2\n  U count = U(0);\n  mu = U(0);\n  sigma2 = U(0);\n  if (i1 < n1) {\n    // one warp normalizes one n1 index,\n    // synchronization is implicit\n    // initialize with standard Welford algorithm\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    const T* lvals = vals + i1 * n2;\n    int l = 4 * thrx;\n    for (; l + 3 < n2; l += 4 * numx) {\n      for (int k = 0; k < 4; ++k) {\n        U curr = static_cast<U>(lvals[l + k]);\n        if (!rms_only) {\n          cuWelfordOnlineSum<U>(curr, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum<U>(curr, sigma2);\n        }\n      }\n    }\n    for (; l < n2; ++l) {\n      U curr = static_cast<U>(lvals[l]);\n      if (!rms_only) {\n        cuWelfordOnlineSum<U>(curr, mu, sigma2, count);\n      } else {\n        cuRMSOnlineSum<U>(curr, sigma2);\n      }\n    }\n    // intra-warp reductions\n    for (int l = 0; l <= 4; ++l) {\n      int srcLaneB = (threadIdx.x + (1 << l)) & 31;\n      U sigma2B = WARP_SHFL(sigma2, srcLaneB);\n      if (!rms_only) {\n        U muB = WARP_SHFL(mu, srcLaneB);\n        U countB = WARP_SHFL(count, srcLaneB);\n        cuChanOnlineSum<U>(muB, sigma2B, countB, mu, sigma2, count);\n      } else {\n        cuChanRMSOnlineSum<U>(sigma2B, sigma2);\n      }\n    }\n    // threadIdx.x == 0 has correct values for each warp\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      U* ubuf = (U*)buf;\n      U* ibuf = (U*)(ubuf + blockDim.y);\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.x == 0 && threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_y = threadIdx.y - offset;\n          if (!rms_only) {\n            ubuf[2 * wrt_y] = mu;\n            ibuf[wrt_y] = count;\n          }\n          ubuf[2 * wrt_y + 1] = sigma2;\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.x == 0 && threadIdx.y < offset) {\n          U sigma2B = ubuf[2 * threadIdx.y + 1];\n          if (!rms_only) {\n            U muB = ubuf[2 * threadIdx.y];\n            U countB = ibuf[threadIdx.y];\n            cuChanOnlineSum<U>(muB, sigma2B, countB, mu, sigma2, count);\n          } else {\n            cuChanRMSOnlineSum<U>(sigma2B, sigma2);\n          }\n        }\n        __syncthreads();\n      }\n      // threadIdx.x = 0 && threadIdx.y == 0 only thread that has correct values\n      if (threadIdx.x == 0 && threadIdx.y == 0) {\n        if (!rms_only) {\n          ubuf[0] = mu;\n        }\n        ubuf[1] = sigma2;\n      }\n      __syncthreads();\n      if (!rms_only) {\n        mu = ubuf[0];\n      }\n      sigma2 = ubuf[1] / U(n2);\n      // don't care about final value of count, we know count == n2\n    } else {\n      if (!rms_only) {\n        mu = WARP_SHFL(mu, 0);\n      }\n      sigma2 = WARP_SHFL(sigma2 / U(n2), 0);\n    }\n  }\n}\n\ntemplate <>\n__device__ void cuWelfordMuSigma2(const at::Half* __restrict__ vals, const int n1, const int n2, const int i1,\n                                  float& mu, float& sigma2, float* buf, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensor is contiguous\n  // 3) 2*blockDim.y*sizeof(U)+blockDim.y*sizeof(int) shared memory available.\n  //\n  // compute variance and mean over n2\n  float count = 0.0f;\n  mu = float(0);\n  sigma2 = float(0);\n  if (i1 < n1) {\n    // one warp normalizes one n1 index,\n    // synchronization is implicit\n    // initialize with standard Welford algorithm\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    const at::Half* lvals = vals + i1 * n2;\n    int l = 8 * thrx;\n    if ((((size_t)lvals) & 3) != 0) {\n      // 16 bit alignment\n      // first thread consumes first point\n      if (thrx == 0) {\n        float curr = static_cast<float>(lvals[0]);\n        if (!rms_only) {\n          cuWelfordOnlineSum(curr, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum(curr, sigma2);\n        }\n      }\n      ++l;\n    }\n    // at this point, lvals[l] are 32 bit aligned for all threads.\n    for (; l + 7 < n2; l += 8 * numx) {\n      for (int k = 0; k < 8; k += 2) {\n        float2 curr = __half22float2(*((__half2*)(lvals + l + k)));\n        if (!rms_only) {\n          cuWelfordOnlineSum(curr.x, mu, sigma2, count);\n          cuWelfordOnlineSum(curr.y, mu, sigma2, count);\n        } else {\n          cuRMSOnlineSum(curr.x, sigma2);\n          cuRMSOnlineSum(curr.y, sigma2);\n        }\n      }\n    }\n    for (; l < n2; ++l) {\n      float curr = static_cast<float>(lvals[l]);\n      if (!rms_only) {\n        cuWelfordOnlineSum(curr, mu, sigma2, count);\n      } else {\n        cuRMSOnlineSum(curr, sigma2);\n      }\n    }\n    // intra-warp reductions\n    for (int l = 0; l <= 4; ++l) {\n      int srcLaneB = (threadIdx.x + (1 << l)) & 31;\n      float sigma2B = WARP_SHFL(sigma2, srcLaneB);\n      if (!rms_only) {\n        float muB = WARP_SHFL(mu, srcLaneB);\n        float countB = WARP_SHFL(count, srcLaneB);\n        cuChanOnlineSum(muB, sigma2B, countB, mu, sigma2, count);\n      } else {\n        cuChanRMSOnlineSum(sigma2B, sigma2);\n      }\n    }\n    // threadIdx.x == 0 has correct values for each warp\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      float* ubuf = (float*)buf;\n      float* ibuf = (float*)(ubuf + blockDim.y);\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.x == 0 && threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_y = threadIdx.y - offset;\n          ubuf[2 * wrt_y + 1] = sigma2;\n          if (!rms_only) {\n            ubuf[2 * wrt_y] = mu;\n            ibuf[wrt_y] = count;\n          }\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.x == 0 && threadIdx.y < offset) {\n          float sigma2B = ubuf[2 * threadIdx.y + 1];\n          if (!rms_only) {\n            float muB = ubuf[2 * threadIdx.y];\n            float countB = ibuf[threadIdx.y];\n            cuChanOnlineSum(muB, sigma2B, countB, mu, sigma2, count);\n          } else {\n            cuChanRMSOnlineSum(sigma2B, sigma2);\n          }\n        }\n        __syncthreads();\n      }\n      // threadIdx.x = 0 && threadIdx.y == 0 only thread that has correct values\n      if (threadIdx.x == 0 && threadIdx.y == 0) {\n        if (!rms_only) {\n          ubuf[0] = mu;\n        }\n        ubuf[1] = sigma2;\n      }\n      __syncthreads();\n      if (!rms_only) {\n        mu = ubuf[0];\n      }\n      sigma2 = ubuf[1] / float(n2);\n      // don't care about final value of count, we know count == n2\n    } else {\n      if (!rms_only) {\n        mu = WARP_SHFL(mu, 0);\n      }\n      sigma2 = WARP_SHFL(sigma2 / float(n2), 0);\n    }\n  }\n}\n\ntemplate <typename U>\nU rsqrt(U v) {\n  return U(1) / sqrt(v);\n}\ntemplate <>\nfloat rsqrt(float v) {\n  return rsqrtf(v);\n}\ntemplate <>\ndouble rsqrt(double v) {\n  return rsqrt(v);\n}\n\nnamespace {\n// This is the un-specialized struct.  Note that we prevent instantiation of this\n// struct by putting an undefined symbol in the function body so it won't compile.\n//  template <typename T>\n//  struct SharedMemory\n//  {\n//      // Ensure that we won't compile any un-specialized types\n//      __device__ T *getPointer()\n//      {\n//          extern __device__ void error(void);\n//          error();\n//          return nullptr;\n//      }\n//  };\n// https://github.com/NVIDIA/apex/issues/246\ntemplate <typename T>\nstruct SharedMemory;\n\ntemplate <>\nstruct SharedMemory<float> {\n  __device__ float* getPointer() {\n    extern __shared__ float s_float[];\n    return s_float;\n  }\n};\n\ntemplate <>\nstruct SharedMemory<double> {\n  __device__ double* getPointer() {\n    extern __shared__ double s_double[];\n    return s_double;\n  }\n};\n}  // namespace\n\ntemplate <typename T, typename U, typename V>\n__device__ void cuApplyLayerNorm_(V* __restrict__ output_vals, U* __restrict__ mean, U* __restrict__ invvar,\n                                  const T* __restrict__ vals, const int n1, const int n2, const U epsilon,\n                                  const V* __restrict__ gamma, const V* __restrict__ beta, bool rms_only) {\n  // Assumptions:\n  // 1) blockDim.x == warpSize\n  // 2) Tensors are contiguous\n  //\n  for (auto i1 = blockIdx.y; i1 < n1; i1 += gridDim.y) {\n    SharedMemory<U> shared;\n    U* buf = shared.getPointer();\n    U mu, sigma2;\n    cuWelfordMuSigma2(vals, n1, n2, i1, mu, sigma2, buf, rms_only);\n\n    const T* lvals = vals + i1 * n2;\n    V* ovals = output_vals + i1 * n2;\n    U c_invvar = rsqrt(sigma2 + epsilon);\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    if (gamma != nullptr && (beta != nullptr || rms_only)) {\n      for (int i = thrx; i < n2; i += numx) {\n        U curr = static_cast<U>(lvals[i]);\n        if (!rms_only) {\n          ovals[i] = gamma[i] * static_cast<V>(c_invvar * (curr - mu)) + beta[i];\n        } else {\n          ovals[i] = gamma[i] * static_cast<V>(c_invvar * curr);\n        }\n      }\n    } else {\n      for (int i = thrx; i < n2; i += numx) {\n        U curr = static_cast<U>(lvals[i]);\n        if (!rms_only) {\n          ovals[i] = static_cast<V>(c_invvar * (curr - mu));\n        } else {\n          ovals[i] = static_cast<V>(c_invvar * curr);\n        }\n      }\n    }\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      if (!rms_only) {\n        mean[i1] = mu;\n      }\n      invvar[i1] = c_invvar;\n    }\n    __syncthreads();\n  }\n}\n\ntemplate <typename T, typename U, typename V = T>\n__global__ void cuApplyLayerNorm(V* __restrict__ output_vals, U* __restrict__ mean, U* __restrict__ invvar,\n                                 const T* __restrict__ vals, const int n1, const int n2, const U epsilon,\n                                 const V* __restrict__ gamma, const V* __restrict__ beta) {\n  cuApplyLayerNorm_<T, U, V>(output_vals, mean, invvar, vals, n1, n2, epsilon, gamma, beta, false);\n}\n\ntemplate <typename T, typename U, typename V = T>\n__global__ void cuApplyRMSNorm(V* __restrict__ output_vals, U* __restrict__ invvar, const T* __restrict__ vals,\n                               const int n1, const int n2, const U epsilon, const V* __restrict__ gamma) {\n  cuApplyLayerNorm_<T, U, V>(output_vals, nullptr, invvar, vals, n1, n2, epsilon, gamma, nullptr, true);\n}\n\ntemplate <typename V>\n__device__ V clamp_by_magnitude(V curr_gamma, double eps) {\n  const V kMinGamma = V(eps);\n  if (curr_gamma >= 0) {\n    if (curr_gamma < kMinGamma) {\n      return kMinGamma;\n    } else {\n      return curr_gamma;\n    }\n  } else {\n    if (curr_gamma > -kMinGamma) {\n      return -kMinGamma;\n    } else {\n      return curr_gamma;\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__device__ void cuLoadWriteStridedInputs(const int i1_block, const int thr_load_row_off, const int thr_load_col_off,\n                                         const int i2_off, const int row_stride, U* warp_buf1, U* warp_buf2,\n                                         const T* input_or_output, const V* dout, const int i1_end, const int n2,\n                                         const U* __restrict__ mean, const U* __restrict__ invvar,\n                                         const V* __restrict__ gamma, const V* __restrict__ beta, const double eps,\n                                         bool rms_only) {\n  int i1 = i1_block + thr_load_row_off;\n  if (i1 < i1_end) {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int i2 = i2_off + k;\n      int load_idx = i1 * n2 + i2;\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (i2 < n2) {\n        U c_h = static_cast<U>(input_or_output[load_idx]);\n        U curr_dout = static_cast<U>(dout[load_idx]);\n        if (!rms_only) {\n          warp_buf1[write_idx] = curr_dout;\n          if (MemoryEfficient) {\n            U curr_beta = static_cast<U>(beta[i2]);\n            warp_buf2[write_idx] = curr_dout * (c_h - curr_beta) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] = curr_dout * (c_h - mean[i1]) * invvar[i1];\n          }\n        } else {\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] = curr_dout * (c_h) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] = curr_dout * (c_h)*invvar[i1];\n          }\n        }\n      } else {\n        if (!rms_only) {\n          warp_buf1[write_idx] = U(0);\n        }\n        warp_buf2[write_idx] = U(0);\n      }\n    }\n  } else {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (!rms_only) {\n        warp_buf1[write_idx] = U(0);\n      }\n      warp_buf2[write_idx] = U(0);\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__device__ void cuLoadAddStridedInputs(const int i1_block, const int thr_load_row_off, const int thr_load_col_off,\n                                       const int i2_off, const int row_stride, U* warp_buf1, U* warp_buf2,\n                                       const T* input_or_output, const V* dout, const int i1_end, const int n2,\n                                       const U* __restrict__ mean, const U* __restrict__ invvar,\n                                       const V* __restrict__ gamma, const V* __restrict__ beta, const double eps,\n                                       bool rms_only) {\n  int i1 = i1_block + thr_load_row_off;\n  if (i1 < i1_end) {\n    for (int k = 0; k < blockDim.y; ++k) {\n      int i2 = i2_off + k;\n      int load_idx = i1 * n2 + i2;\n      int write_idx = thr_load_row_off * row_stride + thr_load_col_off + k;\n      if (i2 < n2) {\n        U c_h = static_cast<U>(input_or_output[load_idx]);\n        U curr_dout = static_cast<U>(dout[load_idx]);\n        if (!rms_only) {\n          U curr_beta = static_cast<U>(beta[i2]);\n          warp_buf1[write_idx] += curr_dout;\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] += curr_dout * (c_h - curr_beta) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] += curr_dout * (c_h - mean[i1]) * invvar[i1];\n          }\n        } else {\n          if (MemoryEfficient) {\n            warp_buf2[write_idx] += curr_dout * (c_h) / static_cast<U>(clamp_by_magnitude(gamma[i2], eps));\n          } else {\n            warp_buf2[write_idx] += curr_dout * (c_h)*invvar[i1];\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__global__ void cuComputePartGradGammaBeta(const V* __restrict__ dout, const T* __restrict__ input_or_output,\n                                           const int n1, const int n2, const U* __restrict__ mean,\n                                           const U* __restrict__ invvar, U epsilon, const V* __restrict__ gamma,\n                                           const V* __restrict__ beta, U* part_grad_gamma, U* part_grad_beta,\n                                           const double eps, bool rms_only) {\n  const int numsegs_n1 = (n1 + blockDim.y * blockDim.y - 1) / (blockDim.y * blockDim.y);\n  const int segs_per_block = (numsegs_n1 + gridDim.y - 1) / gridDim.y;\n  const int i1_beg = blockIdx.y * segs_per_block * blockDim.y * blockDim.y;\n  const int i1_beg_plus_one = (blockIdx.y + 1) * segs_per_block * blockDim.y * blockDim.y;\n  const int i1_end = i1_beg_plus_one < n1 ? i1_beg_plus_one : n1;\n  const int row_stride = blockDim.x + 1;\n  const int thr_load_col_off = (threadIdx.x * blockDim.y) & (blockDim.x - 1);\n  const int thr_load_row_off = (threadIdx.x * blockDim.y) / blockDim.x + threadIdx.y * blockDim.y;\n  const int i2_off = blockIdx.x * blockDim.x + thr_load_col_off;\n  SharedMemory<U> shared;\n  U* buf = shared.getPointer();  // buf has at least blockDim.x * blockDim.y * blockDim.y + (blockDim.y -\n                                 // 1)*(blockDim.x/blockDim.y) elements\n  U* warp_buf1 = (U*)buf;\n  U* warp_buf2 = warp_buf1 + blockDim.y * blockDim.y * row_stride;\n  // compute partial sums from strided inputs\n  // do this to increase number of loads in flight\n  cuLoadWriteStridedInputs<T, U, V, MemoryEfficient>(i1_beg, thr_load_row_off, thr_load_col_off, i2_off, row_stride,\n                                                     warp_buf1, warp_buf2, input_or_output, dout, i1_end, n2, mean,\n                                                     invvar, gamma, beta, eps, rms_only);\n  for (int i1_block = i1_beg + blockDim.y * blockDim.y; i1_block < i1_end; i1_block += blockDim.y * blockDim.y) {\n    cuLoadAddStridedInputs<T, U, V, MemoryEfficient>(i1_block, thr_load_row_off, thr_load_col_off, i2_off, row_stride,\n                                                     warp_buf1, warp_buf2, input_or_output, dout, i1_end, n2, mean,\n                                                     invvar, gamma, beta, eps, rms_only);\n  }\n  __syncthreads();\n  // inter-warp reductions\n  // sum within each warp\n  U acc1 = U(0);\n  U acc2 = U(0);\n  for (int k = 0; k < blockDim.y; ++k) {\n    int row1 = threadIdx.y + k * blockDim.y;\n    int idx1 = row1 * row_stride + threadIdx.x;\n    if (!rms_only) {\n      acc1 += warp_buf1[idx1];\n    }\n    acc2 += warp_buf2[idx1];\n  }\n  if (!rms_only) {\n    warp_buf1[threadIdx.y * row_stride + threadIdx.x] = acc1;\n  }\n  warp_buf2[threadIdx.y * row_stride + threadIdx.x] = acc2;\n  __syncthreads();\n  // sum all warps\n  for (int offset = blockDim.y / 2; offset > 1; offset /= 2) {\n    if (threadIdx.y < offset) {\n      int row1 = threadIdx.y;\n      int row2 = threadIdx.y + offset;\n      int idx1 = row1 * row_stride + threadIdx.x;\n      int idx2 = row2 * row_stride + threadIdx.x;\n      if (!rms_only) {\n        warp_buf1[idx1] += warp_buf1[idx2];\n      }\n      warp_buf2[idx1] += warp_buf2[idx2];\n    }\n    __syncthreads();\n  }\n  int i2 = blockIdx.x * blockDim.x + threadIdx.x;\n  if (threadIdx.y == 0 && i2 < n2) {\n    int row1 = threadIdx.y;\n    int row2 = threadIdx.y + 1;\n    int idx1 = row1 * row_stride + threadIdx.x;\n    int idx2 = row2 * row_stride + threadIdx.x;\n    if (!rms_only) {\n      part_grad_beta[blockIdx.y * n2 + i2] = warp_buf1[idx1] + warp_buf1[idx2];\n    }\n    part_grad_gamma[blockIdx.y * n2 + i2] = warp_buf2[idx1] + warp_buf2[idx2];\n  }\n}\n\ntemplate <typename U, typename V>\n__global__ void cuComputeGradGammaBeta(const U* part_grad_gamma, const U* part_grad_beta, const int part_size,\n                                       const int n1, const int n2, V* grad_gamma, V* grad_beta, bool rms_only) {\n  // sum partial gradients for gamma and beta\n  SharedMemory<U> shared;\n  U* buf = shared.getPointer();\n  int i2 = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i2 < n2) {\n    // each warp does sequential reductions until reduced part_size is num_warps\n    int num_warp_reductions = part_size / blockDim.y;\n    U sum_gamma = U(0);\n    U sum_beta = U(0);\n    const U* part_grad_gamma_ptr = part_grad_gamma + threadIdx.y * num_warp_reductions * n2 + i2;\n    const U* part_grad_beta_ptr = part_grad_beta + threadIdx.y * num_warp_reductions * n2 + i2;\n    for (int warp_offset = 0; warp_offset < num_warp_reductions; ++warp_offset) {\n      sum_gamma += part_grad_gamma_ptr[warp_offset * n2];\n      if (!rms_only) {\n        sum_beta += part_grad_beta_ptr[warp_offset * n2];\n      }\n    }\n    // inter-warp reductions\n    const int nbsize3 = blockDim.x * blockDim.y / 2;\n    for (int offset = blockDim.y / 2; offset >= 1; offset /= 2) {\n      // top half write to shared memory\n      if (threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n        const int write_idx = (threadIdx.y - offset) * blockDim.x + threadIdx.x;\n        buf[write_idx] = sum_gamma;\n        if (!rms_only) {\n          buf[write_idx + nbsize3] = sum_beta;\n        }\n      }\n      __syncthreads();\n      // bottom half sums\n      if (threadIdx.y < offset) {\n        const int read_idx = threadIdx.y * blockDim.x + threadIdx.x;\n        sum_gamma += buf[read_idx];\n        if (!rms_only) {\n          sum_beta += buf[read_idx + nbsize3];\n        }\n      }\n      __syncthreads();\n    }\n    // write out fully summed gradients\n    if (threadIdx.y == 0) {\n      grad_gamma[i2] = sum_gamma;\n      if (!rms_only) {\n        grad_beta[i2] = sum_beta;\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename U, typename V, bool MemoryEfficient>\n__global__ void cuComputeGradInput(const V* __restrict__ dout, const T* __restrict__ input_or_output, const int n1,\n                                   const int n2, const U* __restrict__ mean, const U* __restrict__ invvar, U epsilon,\n                                   const V* gamma, const V* beta, T* grad_input, const double eps, bool rms_only) {\n  for (auto i1 = blockIdx.y; i1 < n1; i1 += gridDim.y) {\n    U sum_loss1 = U(0);\n    U sum_loss2 = U(0);\n    const T* k_h = input_or_output + i1 * n2;\n    const V* k_dout = dout + i1 * n2;\n    const U c_invvar = invvar[i1];\n    const U c_mean = !MemoryEfficient ? mean[i1] : 0.;\n    const int numx = blockDim.x * blockDim.y;\n    const int thrx = threadIdx.x + threadIdx.y * blockDim.x;\n    if (gamma != nullptr) {\n      int l = 4 * thrx;\n      for (; l + 3 < n2; l += 4 * numx) {\n        for (int k = 0; k < 4; ++k) {\n          const U c_h = static_cast<U>(k_h[l + k]);\n          const U c_loss = static_cast<U>(k_dout[l + k]);\n          if (!rms_only) {\n            sum_loss1 += c_loss * gamma[l + k];\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * (c_h - beta[l + k]);\n            } else {\n              sum_loss2 += c_loss * gamma[l + k] * (c_h - c_mean) * c_invvar;\n            }\n          } else {\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * gamma[l + k] * (c_h)*c_invvar;\n            }\n          }\n        }\n      }\n      for (; l < n2; ++l) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        if (!rms_only) {\n          sum_loss1 += c_loss * gamma[l];\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * (c_h - beta[l]);\n          } else {\n            sum_loss2 += c_loss * gamma[l] * (c_h - c_mean) * c_invvar;\n          }\n        } else {\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * gamma[l] * (c_h)*c_invvar;\n          }\n        }\n      }\n    } else {\n      int l = 4 * thrx;\n      for (; l + 3 < n2; l += 4 * numx) {\n        for (int k = 0; k < 4; ++k) {\n          const U c_h = static_cast<U>(k_h[l + k]);\n          const U c_loss = static_cast<U>(k_dout[l + k]);\n          if (!rms_only) {\n            sum_loss1 += c_loss;\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * (c_h - c_mean) * c_invvar;\n            }\n          } else {\n            if (MemoryEfficient) {\n              sum_loss2 += c_loss * c_h;\n            } else {\n              sum_loss2 += c_loss * (c_h)*c_invvar;\n            }\n          }\n        }\n      }\n      for (; l < n2; ++l) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        if (!rms_only) {\n          sum_loss1 += c_loss;\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * (c_h - c_mean) * c_invvar;\n          }\n        } else {\n          if (MemoryEfficient) {\n            sum_loss2 += c_loss * c_h;\n          } else {\n            sum_loss2 += c_loss * (c_h)*c_invvar;\n          }\n        }\n      }\n    }\n    // intra-warp reductions\n    for (int mask = blockDim.x / 2; mask > 0; mask /= 2) {\n      if (!rms_only) {\n        sum_loss1 += WARP_SHFL_XOR(sum_loss1, mask);\n      }\n      sum_loss2 += WARP_SHFL_XOR(sum_loss2, mask);\n    }\n    // inter-warp reductions\n    if (blockDim.y > 1) {\n      SharedMemory<U> shared;\n      U* buf = shared.getPointer();\n      for (int offset = blockDim.y / 2; offset > 0; offset /= 2) {\n        // upper half of warps write to shared\n        if (threadIdx.y >= offset && threadIdx.y < 2 * offset) {\n          const int wrt_i = (threadIdx.y - offset) * blockDim.x + threadIdx.x;\n          if (!rms_only) {\n            buf[2 * wrt_i] = sum_loss1;\n          }\n          buf[2 * wrt_i + 1] = sum_loss2;\n        }\n        __syncthreads();\n        // lower half merges\n        if (threadIdx.y < offset) {\n          const int read_i = threadIdx.y * blockDim.x + threadIdx.x;\n          if (!rms_only) {\n            sum_loss1 += buf[2 * read_i];\n          }\n          sum_loss2 += buf[2 * read_i + 1];\n        }\n        __syncthreads();\n      }\n      if (threadIdx.y == 0) {\n        if (!rms_only) {\n          buf[2 * threadIdx.x] = sum_loss1;\n        }\n        buf[2 * threadIdx.x + 1] = sum_loss2;\n      }\n      __syncthreads();\n      if (threadIdx.y != 0) {\n        if (!rms_only) {\n          sum_loss1 = buf[2 * threadIdx.x];\n        }\n        sum_loss2 = buf[2 * threadIdx.x + 1];\n      }\n    }\n    // all threads now have the two sums over l\n    U fH = (U)n2;\n    U term1 = (U(1) / fH) * c_invvar;\n    T* k_grad_input = grad_input + i1 * n2;\n    if (gamma != nullptr) {\n      for (int l = thrx; l < n2; l += numx) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        const U k_gamma = static_cast<U>(clamp_by_magnitude(gamma[l], eps));\n        U f_grad_input = fH * c_loss * k_gamma;\n        if (!rms_only) {\n          const U k_beta = beta[l];\n          f_grad_input -= sum_loss1;\n          if (MemoryEfficient) {\n            f_grad_input -= (c_h - k_beta) / k_gamma * sum_loss2;\n          } else {\n            f_grad_input -= (c_h - c_mean) * c_invvar * sum_loss2;\n          }\n        } else {\n          if (MemoryEfficient) {\n            f_grad_input -= c_h / k_gamma * sum_loss2;\n          } else {\n            f_grad_input -= c_h * c_invvar * sum_loss2;\n          }\n        }\n        f_grad_input *= term1;\n        k_grad_input[l] = static_cast<T>(f_grad_input);\n      }\n    } else {\n      for (int l = thrx; l < n2; l += numx) {\n        const U c_h = static_cast<U>(k_h[l]);\n        const U c_loss = static_cast<U>(k_dout[l]);\n        U f_grad_input = fH * c_loss;\n        if (!rms_only) {\n          f_grad_input -= sum_loss1;\n          if (MemoryEfficient) {\n            f_grad_input -= c_h * sum_loss2;\n          } else {\n            f_grad_input -= (c_h - c_mean) * c_invvar * sum_loss2;\n          }\n        } else {\n          if (MemoryEfficient) {\n            f_grad_input -= c_h * sum_loss2;\n          } else {\n            f_grad_input -= c_h * c_invvar * sum_loss2;\n          }\n        }\n        f_grad_input *= term1;\n        k_grad_input[l] = static_cast<T>(f_grad_input);\n      }\n    }\n    // prevent race where buf is written again before reads are done\n    __syncthreads();\n  }\n}\n\ntemplate <typename T, typename U, typename V = T>\nvoid HostApplyLayerNorm(V* output, U* mean, U* invvar, const T* input, int n1, int n2, double epsilon, const V* gamma,\n                        const V* beta) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n  const dim3 threads(32, 4, 1);\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks(1, std::min((uint64_t)n1, maxGridY), 1);\n  int nshared = threads.y > 1 ? threads.y * sizeof(U) + (threads.y / 2) * sizeof(U) : 0;\n  cuApplyLayerNorm<<<blocks, threads, nshared, stream>>>(output, mean, invvar, input, n1, n2, U(epsilon), gamma, beta);\n}\n\ntemplate <typename T, typename U, typename V = T>\nvoid HostApplyRMSNorm(V* output, U* invvar, const T* input, int n1, int n2, double epsilon, const V* gamma) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n  const dim3 threads(32, 4, 1);\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks(1, std::min((uint64_t)n1, maxGridY), 1);\n  int nshared = threads.y > 1 ? threads.y * sizeof(U) + (threads.y / 2) * sizeof(U) : 0;\n  cuApplyRMSNorm<<<blocks, threads, nshared, stream>>>(output, invvar, input, n1, n2, U(epsilon), gamma);\n}\n\nvoid cuda_layer_norm(at::Tensor& output, at::Tensor& mean, at::Tensor& invvar, const at::Tensor& input, int n1, int n2,\n                     at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma,\n                     const std::optional<at::Tensor>& beta, double epsilon) {\n  using namespace at;\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input.scalar_type(), output.scalar_type(), \"layer_norm_cuda_kernel\",\n      using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostApplyLayerNorm<scalar_t_in, accscalar_t, scalar_t_out>(\n          output.data_ptr<scalar_t_out>(), mean.data_ptr<accscalar_t>(), invvar.data_ptr<accscalar_t>(),\n          input.data_ptr<scalar_t_in>(), n1, n2, epsilon, gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr,\n          beta.has_value() ? beta->data_ptr<scalar_t_out>() : nullptr);)\n}\n\nvoid cuda_rms_norm(at::Tensor& output, at::Tensor& invvar, const at::Tensor& input, int n1, int n2,\n                   at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma, double epsilon) {\n  using namespace at;\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input.scalar_type(), output.scalar_type(), \"rms_norm_cuda_kernel\",\n      using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostApplyRMSNorm<scalar_t_in, accscalar_t, scalar_t_out>(\n          output.data_ptr<scalar_t_out>(), invvar.data_ptr<accscalar_t>(), input.data_ptr<scalar_t_in>(), n1, n2,\n          epsilon, gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr);)\n}\n\ntemplate <typename T, typename U = float, typename V = T>\nvoid HostLayerNormGradient(const V* dout, const U* mean, const U* invvar, at::Tensor& input_or_output, int n1, int n2,\n                           const V* gamma, const V* beta, double epsilon, T* grad_input, V* grad_gamma, V* grad_beta,\n                           bool memory_efficient) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n\n  if (gamma != nullptr && beta != nullptr) {\n    // compute grad_gamma(j) and grad_beta(j)\n    const int part_size = 16;\n    const dim3 threads2(32, 4, 1);\n    const dim3 blocks2((n2 + threads2.x - 1) / threads2.x, part_size, 1);\n    const int nshared2_a = 2 * sizeof(U) * threads2.y * threads2.y * (threads2.x + 1);\n    const int nshared2_b = threads2.x * threads2.y * sizeof(U);\n    const int nshared2 = nshared2_a > nshared2_b ? nshared2_a : nshared2_b;\n    // note (mkozuki): I can hard code part_grad_gamma's dtype as float given that\n    // the `cuda_layer_norm_gradient` doesn't support double.\n    const auto part_grad_dtype = (input_or_output.scalar_type() == at::ScalarType::Half ||\n                                  input_or_output.scalar_type() == at::ScalarType::BFloat16)\n                                     ? at::ScalarType::Float\n                                     : input_or_output.scalar_type();\n    at::Tensor part_grad_gamma = at::empty({part_size, n2}, input_or_output.options().dtype(part_grad_dtype));\n    at::Tensor part_grad_beta = at::empty_like(part_grad_gamma);\n    BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n      auto kernel = &cuComputePartGradGammaBeta<T, U, V, MemoryEfficient>;\n      kernel<<<blocks2, threads2, nshared2, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, mean, invvar,\n                                                      U(epsilon), gamma, beta, part_grad_gamma.data_ptr<U>(),\n                                                      part_grad_beta.data_ptr<U>(), epsilon, false);\n    });\n\n    const dim3 threads3(32, 8, 1);\n    const dim3 blocks3((n2 + threads2.x - 1) / threads2.x, 1, 1);\n    const int nshared3 = threads3.x * threads3.y * sizeof(U);\n    cuComputeGradGammaBeta<<<blocks3, threads3, nshared3, stream>>>(\n        part_grad_gamma.data_ptr<U>(), part_grad_beta.data_ptr<U>(), part_size, n1, n2, grad_gamma, grad_beta, false);\n  }\n\n  // compute grad_input\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks1(1, std::min((uint64_t)n1, maxGridY), 1);\n  const dim3 threads1(32, 4, 1);\n  int nshared = threads1.y > 1 ? threads1.y * threads1.x * sizeof(U) : 0;\n  BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n    auto kernel = cuComputeGradInput<T, U, V, MemoryEfficient>;\n    kernel<<<blocks1, threads1, nshared, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, mean, invvar,\n                                                   U(epsilon), gamma, beta, grad_input, epsilon, false);\n  });\n}\n\ntemplate <typename T, typename U = float, typename V = T>\nvoid HostRMSNormGradient(const V* dout, const U* invvar, at::Tensor& input_or_output, int n1, int n2, const V* gamma,\n                         double epsilon, T* grad_input, V* grad_gamma, bool memory_efficient) {\n  auto stream = at::cuda::getCurrentCUDAStream().stream();\n\n  if (gamma != nullptr) {\n    const int part_size = 16;\n    const dim3 threads2(32, 4, 1);\n    const dim3 blocks2((n2 + threads2.x - 1) / threads2.x, part_size, 1);\n    const int nshared2_a = 2 * sizeof(U) * threads2.y * threads2.y * (threads2.x + 1);\n    const int nshared2_b = threads2.x * threads2.y * sizeof(U);\n    const int nshared2 = nshared2_a > nshared2_b ? nshared2_a : nshared2_b;\n    // note (mkozuki): I can hard code part_grad_gamma's dtype as float given that\n    // the `cuda_layer_norm_gradient` doesn't support double.\n    const auto part_grad_dtype = (input_or_output.scalar_type() == at::ScalarType::Half ||\n                                  input_or_output.scalar_type() == at::ScalarType::BFloat16)\n                                     ? at::ScalarType::Float\n                                     : input_or_output.scalar_type();\n    at::Tensor part_grad_gamma = at::empty({part_size, n2}, input_or_output.options().dtype(part_grad_dtype));\n    BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n      auto kernel = &cuComputePartGradGammaBeta<T, U, V, MemoryEfficient>;\n      kernel<<<blocks2, threads2, nshared2, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, invvar, /* unused */\n                                                      invvar, U(epsilon), gamma, gamma,                    /* unused */\n                                                      part_grad_gamma.data_ptr<U>(),\n                                                      part_grad_gamma.data_ptr<U>(), /* unused */\n                                                      epsilon, true);\n    });\n\n    const dim3 threads3(32, 8, 1);\n    const dim3 blocks3((n2 + threads2.x - 1) / threads2.x, 1, 1);\n    const int nshared3 = threads3.x * threads3.y * sizeof(U);\n    cuComputeGradGammaBeta<<<blocks3, threads3, nshared3, stream>>>(\n        part_grad_gamma.data_ptr<U>(), part_grad_gamma.data_ptr<U>(), /* unused */\n        part_size, n1, n2, grad_gamma, grad_gamma,                    /* unused */\n        true);\n  }\n\n  // compute grad_input\n  const uint64_t maxGridY = at::cuda::getCurrentDeviceProperties()->maxGridSize[1];\n  const dim3 blocks1(1, std::min((uint64_t)n1, maxGridY), 1);\n  const dim3 threads1(32, 4, 1);\n  int nshared = threads1.y > 1 ? threads1.y * threads1.x * sizeof(U) : 0;\n  BOOL_SWITCH(memory_efficient, MemoryEfficient, [&] {\n    auto kernel = cuComputeGradInput<T, U, V, MemoryEfficient>;\n    kernel<<<blocks1, threads1, nshared, stream>>>(dout, input_or_output.data_ptr<T>(), n1, n2, invvar, /* unused */\n                                                   invvar, U(epsilon), gamma, gamma,                    /* unused */\n                                                   grad_input, epsilon, true);\n  });\n}\n\nvoid cuda_layer_norm_gradient(at::Tensor& dout, const std::optional<at::Tensor>& mean, at::Tensor& invvar,\n                              at::Tensor& input_or_output, int n1, int n2, at::IntArrayRef normalized_shape,\n                              const std::optional<at::Tensor>& gamma, const std::optional<at::Tensor>& beta,\n                              double epsilon, at::Tensor& grad_input, const std::optional<at::Tensor>& grad_gamma,\n                              const std::optional<at::Tensor>& grad_beta, bool memory_efficient) {\n  using namespace at;\n  // we can do away with `accscalar_t` as there're only three dtypes: fp32, fp16, bf16\n  DISPATCH_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input_or_output.scalar_type(), gamma.has_value() ? gamma->scalar_type() : input_or_output.scalar_type(),\n      \"cuComputeGradInput\", using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostLayerNormGradient(dout.data_ptr<scalar_t_out>(), mean.has_value() ? mean->data_ptr<accscalar_t>() : nullptr,\n                            invvar.data_ptr<accscalar_t>(), input_or_output, n1, n2,\n                            // TMJ pass nullptr argument for gamma, beta, grad_gamma and grad_beta\n                            // if gamma Tensor is nullptr on input.\n                            gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr,\n                            gamma.has_value() ? beta->data_ptr<scalar_t_out>() : nullptr, epsilon,\n                            grad_input.data_ptr<scalar_t_in>(),\n                            gamma.has_value() ? grad_gamma->data_ptr<scalar_t_out>() : nullptr,\n                            gamma.has_value() ? grad_beta->data_ptr<scalar_t_out>() : nullptr, memory_efficient);)\n}\n\nvoid cuda_rms_norm_gradient(at::Tensor& dout, at::Tensor& invvar, at::Tensor& input_or_output, int n1, int n2,\n                            at::IntArrayRef normalized_shape, const std::optional<at::Tensor>& gamma, double epsilon,\n                            at::Tensor& grad_input, const std::optional<at::Tensor>& grad_gamma,\n                            bool memory_efficient) {\n  using namespace at;\n  // we can do away with `accscalar_t` as there're only three dtypes: fp32, fp16, bf16\n  // DISPATCH_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n  DISPATCH_DOUBLE_FLOAT_HALF_AND_BFLOAT_INOUT_TYPES(\n      input_or_output.scalar_type(), gamma.has_value() ? gamma->scalar_type() : input_or_output.scalar_type(),\n      \"cuComputeGradInputRMS\", using accscalar_t = at::acc_type<scalar_t_in, true>;\n      HostRMSNormGradient(dout.data_ptr<scalar_t_out>(), invvar.data_ptr<accscalar_t>(), input_or_output, n1, n2,\n                          // TMJ pass nullptr argument for gamma, beta, grad_gamma and grad_beta\n                          // if gamma Tensor is nullptr on input.\n                          gamma.has_value() ? gamma->data_ptr<scalar_t_out>() : nullptr, epsilon,\n                          grad_input.data_ptr<scalar_t_in>(),\n                          gamma.has_value() ? grad_gamma->data_ptr<scalar_t_out>() : nullptr, memory_efficient);)\n}\n", "source": "/kaggle/working/apex/csrc/layer_norm_cuda_kernel.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename in_t, typename out_t>\nstruct L2NormScaleFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<2>& tl,\n                                             float* output, float* output_per_tensor, float scale, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    in_t* in = (in_t*)tl.addresses[0][tensor_loc];\n    in += chunk_idx * chunk_size;\n\n    out_t* out = (out_t*)tl.addresses[1][tensor_loc];\n    out += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    in_t r_in[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_in[i] = 0;\n    }\n    // bool finite = true;\n    out_t r_out[ILP];\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(in) && is_aligned(out)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_in, in, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_in[ii]);\n          r_out[ii] = next * scale;\n          vals[ii] += next * next;\n          // finite = finite && isfinite(r_in[ii]);\n        }\n        load_store(out, r_out, i_start, 0);\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          r_in[ii] = 0;\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            r_in[ii] = in[i];\n            float next = static_cast<float>(in[i]);\n            vals[ii] += next * next;\n          }\n        }\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          r_out[ii] = static_cast<float>(r_in[ii]) * scale;\n          // finite = finite && isfinite(r_in[ii]);\n        }\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) out[i] = r_out[ii];\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup_v3(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                           int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_scale_cuda(int chunk_size, at::Tensor noop_flag,\n                                                                  std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                                  float scale, at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_AND_HALF(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_scale_cuda\",\n      DISPATCH_FLOAT_AND_HALF(\n          tensor_lists[1][0].scalar_type(), 1, \"multi_tensor_l2norm_scale_cuda\",\n          multi_tensor_apply<2>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                L2NormScaleFunctor<scalar_t_0, scalar_t_1>(), output.data_ptr<float>(),\n                                per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, scale, per_tensor,\n                                max_chunks_per_tensor);))\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup_v3<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/cuda/Exceptions.h>\n#include <c10/cuda/CUDAGuard.h>\n// Another possibility:\n// #include <torch/all.h>\n\n#include <assert.h>\n\n#include \"multi_tensor_apply.cuh\"\n#include \"type_shim.h\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\ntemplate <typename in_t, typename out_t>\nstruct L2NormScaleFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<2>& tl,\n                                             float* output, float* output_per_tensor, float scale, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    in_t* in = (in_t*)tl.addresses[0][tensor_loc];\n    in += chunk_idx * chunk_size;\n\n    out_t* out = (out_t*)tl.addresses[1][tensor_loc];\n    out += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    in_t r_in[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_in[i] = 0;\n    }\n    // bool finite = true;\n    out_t r_out[ILP];\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(in) && is_aligned(out)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_in, in, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_in[ii]);\n          r_out[ii] = next * scale;\n          vals[ii] += next * next;\n          // finite = finite && isfinite(r_in[ii]);\n        }\n        load_store(out, r_out, i_start, 0);\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          r_in[ii] = 0;\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            r_in[ii] = in[i];\n            float next = static_cast<float>(in[i]);\n            vals[ii] += next * next;\n          }\n        }\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          r_out[ii] = static_cast<float>(r_in[ii]) * scale;\n          // finite = finite && isfinite(r_in[ii]);\n        }\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) out[i] = r_out[ii];\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val += vals[i];\n\n    float final = reduce_block_into_lanes(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] += final;\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n// Probably better to template, but since we are not likely to support other norm\ntemplate <typename x_t>\nstruct MaxNormFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<1>& tl,\n                                             float* output, float* output_per_tensor, bool per_tensor,\n                                             int max_chunks_per_tensor) {\n    // I'd like this kernel to propagate infs/nans.\n    // if(*noop_gmem == 1)\n    //   return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    x_t* x = (x_t*)tl.addresses[0][tensor_loc];\n    x += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n\n    __shared__ float s_vals[512];\n\n    float vals[ILP];  // = {0}; // this probably works too but I want to be sure...\n    x_t r_x[ILP];\n    for (int i = 0; i < ILP; i++) {\n      vals[i] = 0.f;\n      r_x[i] = 0;\n    }\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(x)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        load_store(r_x, x, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          float next = static_cast<float>(r_x[ii]);\n          vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n        }\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            float next = static_cast<float>(x[i]);\n            vals[ii] = fmaxf(fabsf(vals[ii]), fabsf(next));\n          }\n        }\n      }\n    }\n\n    float val = 0.f;\n    for (int i = 0; i < ILP; i++) val = fmaxf(fabsf(val), fabsf(vals[i]));\n\n    float final = reduce_block_into_lanes_max_op(s_vals, val);\n\n    if (threadIdx.x == 0) {\n      if (!isfinite(final)) *noop_gmem = 1;  // Blindly fire off a write.  These will race but that's ok.\n      output[blockIdx.x] = fmaxf(fabsf(output[blockIdx.x]), fabsf(final));\n      if (per_tensor)\n        output_per_tensor[(tl.start_tensor_this_launch + tensor_loc) * max_chunks_per_tensor + chunk_idx] = final;\n    }\n  }\n};\n\n__global__ void cleanup_v3(float* output, float* output_per_tensor, float* ret, float* ret_per_tensor, bool per_tensor,\n                           int max_chunks_per_tensor) {\n  __shared__ float vals[512];\n\n  if (blockIdx.x == 0) {\n    float val = 0;\n    if (threadIdx.x < 320) val = output[threadIdx.x];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) *ret = sqrt(final);\n  }\n\n  if (per_tensor) {\n    float* output_this_tensor = output_per_tensor + blockIdx.x * max_chunks_per_tensor;\n\n    float val = 0;\n    for (int i = threadIdx.x; i < max_chunks_per_tensor; i += blockDim.x) val += output_this_tensor[i];\n\n    float final = reduce_block_into_lanes(vals, val);\n\n    if (threadIdx.x == 0) ret_per_tensor[blockIdx.x] = sqrt(final);\n  }\n}\n\nstd::tuple<at::Tensor, at::Tensor> multi_tensor_l2norm_scale_cuda(int chunk_size, at::Tensor noop_flag,\n                                                                  std::vector<std::vector<at::Tensor>> tensor_lists,\n                                                                  float scale, at::optional<bool> per_tensor_python) {\n  bool per_tensor = per_tensor_python.has_value() ? per_tensor_python.value() : false;\n\n  auto float_options = tensor_lists[0][0].options().dtype(at::kFloat);\n  auto output = at::zeros({320}, float_options);\n\n  at::Tensor output_per_tensor;\n  at::Tensor ret_per_tensor;\n\n  int ntensors = tensor_lists[0].size();\n  int max_chunks_per_tensor = -1;\n\n  if (per_tensor) {\n    for (int t = 0; t < ntensors; t++) {\n      int max_chunks_this_tensor = (tensor_lists[0][t].numel() + chunk_size - 1) / chunk_size;\n      if (max_chunks_this_tensor > max_chunks_per_tensor) max_chunks_per_tensor = max_chunks_this_tensor;\n    }\n    output_per_tensor = at::zeros({ntensors * max_chunks_per_tensor}, float_options);\n    ret_per_tensor = at::empty({ntensors}, float_options);\n  } else {\n    ret_per_tensor = at::empty({0}, float_options);\n  }\n\n  DISPATCH_FLOAT_AND_HALF(\n      tensor_lists[0][0].scalar_type(), 0, \"multi_tensor_l2norm_scale_cuda\",\n      DISPATCH_FLOAT_AND_HALF(\n          tensor_lists[1][0].scalar_type(), 1, \"multi_tensor_l2norm_scale_cuda\",\n          multi_tensor_apply<2>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                L2NormScaleFunctor<scalar_t_0, scalar_t_1>(), output.data_ptr<float>(),\n                                per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, scale, per_tensor,\n                                max_chunks_per_tensor);))\n\n  AT_CUDA_CHECK(hipGetLastError());\n  // AT_CUDA_CHECK(cudaDeviceSynchronize());\n\n  // This involves one more small kernel launches, but will be negligible end to end.\n  // I could get rid of these by hacking the functor + multi tensor harness with persistence\n  // logic, but keeping it simple for now\n  auto ret = at::empty({1}, output.options());\n  const at::cuda::OptionalCUDAGuard device_guard(device_of(output));\n  auto stream = at::cuda::getCurrentCUDAStream();\n  cleanup_v3<<<per_tensor ? ntensors : 1, 512, 0, stream>>>(\n      output.data_ptr<float>(), per_tensor ? output_per_tensor.data_ptr<float>() : nullptr, ret.data_ptr<float>(),\n      per_tensor ? ret_per_tensor.data_ptr<float>() : nullptr, per_tensor, max_chunks_per_tensor);\n\n  return std::tuple<at::Tensor, at::Tensor>(ret, ret_per_tensor);\n}\n", "source": "/kaggle/working/apex/csrc/multi_tensor_l2norm_scale_kernel.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#include <iostream>\n#include <vector>\n\n#include \"type_shim.h\"\n\n__device__ __forceinline__ int lastpow2(int n) {\n  int out = 1 << (31 - __clz(n));\n  if (n == out) out >>= 1;\n  return out;\n}\n\n__host__ __forceinline__ int h_next_pow2(unsigned int n) {\n  n--;\n  n |= (n >> 1);\n  n |= (n >> 2);\n  n |= (n >> 4);\n  n |= (n >> 8);\n  n |= (n >> 16);\n  return ++n;\n}\n\n__host__ __forceinline__ int h_last_pow2(unsigned int n) {\n  n |= (n >> 1);\n  n |= (n >> 2);\n  n |= (n >> 4);\n  n |= (n >> 8);\n  n |= (n >> 16);\n  return n - (n >> 1);\n}\n\n#define WARP_SIZE 32\n\ntemplate <typename T>\n__device__ __forceinline__ T warp_reduce_sum(T val) {\n#pragma unroll\n  for (int i = WARP_SIZE / 2; i > 0; i >>= 1) val = val + __shfl_down_sync(0xffffffff, val, i);\n  return val;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T reduce_block(T* x, T val) {\n  int tid = threadIdx.y * blockDim.x + threadIdx.x;\n  int blockSize = blockDim.x * blockDim.y;\n\n  if (blockSize > 32) {\n    val = warp_reduce_sum(val);\n    if (tid % WARP_SIZE == 0) x[tid / WARP_SIZE] = val;\n\n    __syncthreads();\n\n    val = (tid < blockSize / WARP_SIZE ? x[tid % WARP_SIZE] : T(0));\n  }\n\n  if (tid / WARP_SIZE == 0) val = warp_reduce_sum(val);\n\n  return val;\n}\n\n#define ELEMENTS_PER_ITER 4  // enables concurrency within each thread to hide latency\n#define ELEMENTS_PER_THREAD 16\n#define OPTIMAL_TILE_W 32\n#define MAX_H_BLOCK 128\n#define MAX_BLOCK_SIZE 512\n\n__host__ int div_ru(int x, int y) { return h_last_pow2(1 + (x - 1) / y); }\n\n__host__ void flexible_launch_configs(const int reduction, const int stride, dim3& block, dim3& grid,\n                                      const bool coop_flag = false) {\n  int block_x = std::min(h_last_pow2(stride), OPTIMAL_TILE_W);\n  int block_y = std::min(h_last_pow2(div_ru(reduction, ELEMENTS_PER_THREAD)), MAX_BLOCK_SIZE / block_x);\n  if (block_x * block_y != MAX_BLOCK_SIZE) {\n    block_x = std::min(h_last_pow2(stride), MAX_BLOCK_SIZE / block_y);\n  }\n\n  int grid_x = div_ru(stride, block_x);\n  int grid_y = std::min(div_ru(reduction, block_y * ELEMENTS_PER_THREAD), MAX_H_BLOCK);\n  if (coop_flag) {\n    // it's not worth having a grid reduction if the reduction dimension is not big enough\n    grid_y = grid_y < 8 ? 1 : grid_y;\n  }\n\n  block.x = block_x;\n  block.y = block_y;\n  block.z = 1;\n  grid.x = grid_x;\n  grid.y = grid_y;\n  grid.z = 1;\n}\n\ntemplate <typename T, typename C>\n__device__ __forceinline__ void welford_merge_element(C& count, T& mean, T& m2n, const C& num_new, const T& mean_new,\n                                                      const T& m2n_new) {\n  T factor = T(1.0) / max(1, (count + num_new));\n  T delta0 = mean - mean_new;\n  mean = (mean_new * num_new + mean * count) * factor;\n  m2n += m2n_new + delta0 * delta0 * num_new * count * factor;\n  count += num_new;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void warp_reduce_mean_m2n(T& mean, T& m2n, int& num) {\n#pragma unroll\n  for (int i = WARP_SIZE / 2; i > 0; i >>= 1) {\n    auto num_new = __shfl_down_sync(0xffffffff, num, i);\n    auto mean_new = __shfl_down_sync(0xffffffff, mean, i);\n    auto m2n_new = __shfl_down_sync(0xffffffff, m2n, i);\n    welford_merge_element(num, mean, m2n, num_new, mean_new, m2n_new);\n  }\n}\n\ntemplate <typename T>\n__device__ void welford_reduce_mean_m2n(T* __restrict__ x, int* __restrict__ count, T& mean, T& m2n, int& num,\n                                        int block_size, int thread_id) {\n  int lane = thread_id % WARP_SIZE;\n  int wid = thread_id / WARP_SIZE;\n\n  if (block_size > 32) {\n    warp_reduce_mean_m2n(mean, m2n, num);\n    if (lane == 0) {\n      x[wid * 2] = mean;\n      x[wid * 2 + 1] = m2n;\n      count[wid] = num;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n      mean = (thread_id < block_size / WARP_SIZE) ? x[lane * 2] : T(0);\n      m2n = (thread_id < block_size / WARP_SIZE) ? x[lane * 2 + 1] : T(0);\n      num = (thread_id < block_size / WARP_SIZE) ? count[lane] : int(0);\n    }\n  }\n\n  if (wid == 0) warp_reduce_mean_m2n(mean, m2n, num);\n\n  return;\n}\n\n// return spatial size for NC+ Tensors\n__host__ int get_tensor_spatial_size(const at::Tensor& input) {\n  auto space_size = input.size(2);\n  for (int i = 3; i < input.ndimension(); i++) {\n    space_size *= input.size(i);\n  }\n  return space_size;\n}\n\n// promote accumulation scalar type. promote half to float.\n__host__ at::ScalarType promote_scalartype(const at::Tensor& input) {\n  return input.scalar_type() == at::ScalarType::Half ? at::ScalarType::Float : input.scalar_type();\n}\n\n// return single element size, optional accumulation type promotion.\n__host__ size_t get_element_data_size(const at::Tensor& input, bool accumulation = false) {\n  auto scalar_type = accumulation ? promote_scalartype(input) : input.scalar_type();\n  return at::elementSize(scalar_type);\n}\n\ntemplate <typename T, typename C>\n__device__ __forceinline__ void welford_merge_block_vertical(C& count, T& mean, T& m2n, C* shmem_count, T* shmem_mean,\n                                                             T* shmem_m2n) {\n  // write to shared memory\n  auto address_base = threadIdx.x + threadIdx.y * blockDim.x;\n  shmem_mean[address_base] = mean;\n  shmem_m2n[address_base] = m2n;\n  shmem_count[address_base] = count;\n\n#pragma unroll\n  for (int offset = blockDim.y / 2; offset > 0; offset >>= 1) {\n    __syncthreads();\n    if (threadIdx.y < offset && threadIdx.y + offset < blockDim.y) {\n      auto address = address_base + offset * blockDim.x;\n      // read shared memory back to register for reduction\n      auto num_new = shmem_count[address];\n      auto mean_new = shmem_mean[address];\n      auto m2n_new = shmem_m2n[address];\n\n      welford_merge_element(count, mean, m2n, num_new, mean_new, m2n_new);\n\n      // last write is not necessary\n      shmem_mean[address_base] = mean;\n      shmem_m2n[address_base] = m2n;\n      shmem_count[address_base] = count;\n    }\n  }\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void merge_block_vertical(T& sum_dy, T& sum_dy_xmu, T* shmem_sum_dy, T* shmem_sum_dy_xmu) {\n  // write to shared memory\n  auto address_base = threadIdx.x + threadIdx.y * blockDim.x;\n  shmem_sum_dy[address_base] = sum_dy;\n  shmem_sum_dy_xmu[address_base] = sum_dy_xmu;\n\n#pragma unroll\n  for (int offset = blockDim.y / 2; offset > 0; offset >>= 1) {\n    __syncthreads();\n    if (threadIdx.y < offset && threadIdx.y + offset < blockDim.y) {\n      auto address = address_base + offset * blockDim.x;\n\n      sum_dy += shmem_sum_dy[address];\n      sum_dy_xmu += shmem_sum_dy_xmu[address];\n\n      // last write is not necessary\n      shmem_sum_dy[address_base] = sum_dy;\n      shmem_sum_dy_xmu[address_base] = sum_dy_xmu;\n    }\n  }\n}\n\n// welford kernel calculating mean/biased_variance/unbiased_variance\ntemplate <typename scalar_t, typename accscalar_t, typename outscalar_t>\n__global__ void welford_kernel(const scalar_t* __restrict__ input, outscalar_t* __restrict__ out_mean,\n                               outscalar_t* __restrict__ out_var_biased, const int bs, const int fs, const int ss) {\n  int block_size = blockDim.x * blockDim.y;\n  int count = 0;\n  accscalar_t x_mean = accscalar_t(0);\n  accscalar_t m_2_n = accscalar_t(0);\n\n  int thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  for (int batch_id = threadIdx.y; batch_id < bs; batch_id += blockDim.y) {\n    int input_base = blockIdx.x * ss + batch_id * ss * fs;\n    // sequential welford\n    for (int offset = threadIdx.x; offset < ss; offset += blockDim.x) {\n      count++;\n      auto x_n = static_cast<accscalar_t>(input[offset + input_base]);\n      auto d = x_n - x_mean;\n      x_mean += d / count;\n      m_2_n += d * (x_n - x_mean);\n    }\n  }\n\n  static __shared__ int s_mem[160];\n  accscalar_t* s_mem_ac = (accscalar_t*)&s_mem[32];\n\n  welford_reduce_mean_m2n<accscalar_t>(s_mem_ac, s_mem, x_mean, m_2_n, count, block_size, thread_id);\n\n  if (thread_id == 0) {\n    out_mean[blockIdx.x] = static_cast<outscalar_t>(x_mean);\n    out_var_biased[blockIdx.x] = static_cast<outscalar_t>(m_2_n / count);\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void batchnorm_forward_kernel(const scalar_t* __restrict__ input, const accscalar_t* __restrict__ mean,\n                                         const accscalar_t* __restrict__ inv_std,\n                                         const layerscalar_t* __restrict__ weight,\n                                         const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                         const int ss, const int bs) {\n  auto m_c = mean[blockIdx.x];\n  auto inv_std_c = inv_std[blockIdx.x];\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[blockIdx.x]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[blockIdx.x]);\n\n  for (int batch_offset = blockIdx.y * blockDim.y + threadIdx.y; batch_offset < bs;\n       batch_offset += gridDim.y * blockDim.y) {\n    int address_base = blockIdx.x * ss + batch_offset * gridDim.x * ss;\n    for (int offset = threadIdx.x + blockIdx.z * blockDim.x; offset < ss; offset += gridDim.z * blockDim.x) {\n      out[address_base + offset] =\n          static_cast<scalar_t>(w_c * (static_cast<accscalar_t>(input[address_base + offset]) - m_c) * inv_std_c + s_c);\n    }\n  }\n}\n\n// Backward BN kernel, calculates grad_bias, grad_weight as well as intermediate\n// results to calculating grad_input.\n// Breaking the grad_input to two step to support sync BN, which requires all\n// reduce of the intermediate results across processes.\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void reduce_bn_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ grad_output,\n                                 const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                 accscalar_t* __restrict__ sum_dy_o, accscalar_t* __restrict__ sum_dy_xmu_o,\n                                 layerscalar_t* __restrict__ grad_weight, layerscalar_t* __restrict__ grad_bias,\n                                 const int bs, const int fs, const int ss) {\n  static __shared__ int s_mem[64];\n  // int total_item_num = bs * ss;\n\n  int thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  auto r_mean = mean[blockIdx.x];\n  auto factor = inv_std[blockIdx.x];\n\n  // Kahan sum\n  accscalar_t sum_dy = 0.0;\n  accscalar_t sum_dy_xmu = 0.0;\n  accscalar_t sum_dy_c = 0.0;\n  accscalar_t sum_dy_xmu_c = 0.0;\n  for (int batch_id = threadIdx.y; batch_id < bs; batch_id += blockDim.y) {\n    int input_base = blockIdx.x * ss + batch_id * ss * fs;\n    for (int offset = threadIdx.x; offset < ss; offset += blockDim.x) {\n      auto e_grad = static_cast<accscalar_t>(grad_output[offset + input_base]);\n      auto e_input = static_cast<accscalar_t>(input[offset + input_base]);\n      // calculating sum_dy\n      auto sum_dy_y = e_grad - sum_dy_c;\n      auto sum_dy_t = sum_dy + sum_dy_y;\n      sum_dy_c = (sum_dy_t - sum_dy) - sum_dy_y;\n      sum_dy = sum_dy_t;\n\n      // calculating sum_dy_xmu\n      auto sum_dy_xmu_y = e_grad * (e_input - r_mean) - sum_dy_xmu_c;\n      auto sum_dy_xmu_t = sum_dy_xmu + sum_dy_xmu_y;\n      sum_dy_xmu_c = (sum_dy_xmu_t - sum_dy_xmu) - sum_dy_xmu_y;\n      sum_dy_xmu = sum_dy_xmu_t;\n    }\n  }\n\n  sum_dy = reduce_block((accscalar_t*)s_mem, sum_dy);\n  __syncthreads();\n  sum_dy_xmu = reduce_block((accscalar_t*)s_mem, sum_dy_xmu);\n\n  if (thread_id == 0) {\n    if (grad_bias != NULL) {\n      grad_bias[blockIdx.x] = static_cast<layerscalar_t>(sum_dy);\n    }\n    if (grad_weight != NULL) {\n      grad_weight[blockIdx.x] = static_cast<layerscalar_t>(sum_dy_xmu * factor);\n    }\n    // mean_dy[blockIdx.x] = sum_dy / total_item_num;\n    // mean_dy_xmu[blockIdx.x] = sum_dy_xmu / total_item_num;\n    sum_dy_o[blockIdx.x] = sum_dy;\n    sum_dy_xmu_o[blockIdx.x] = sum_dy_xmu;\n  }\n}\n\n// elementwise backward BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void batchnorm_backward_kernel(const scalar_t* __restrict__ grad_output, const scalar_t* __restrict__ input,\n                                          const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                          const layerscalar_t* __restrict__ weight,\n                                          const accscalar_t* __restrict__ sum_dy,\n                                          const accscalar_t* __restrict__ sum_dy_xmu, const int* __restrict__ numel,\n                                          scalar_t* __restrict__ grad_input, const int64_t world_size, const int ss,\n                                          const int bs) {\n  int64_t div = 0;\n  for (int i = 0; i < world_size; i++) {\n    div += numel[i];\n  }\n  auto m_c = static_cast<accscalar_t>(mean[blockIdx.x]);\n  // auto m_dy_c = static_cast<accscalar_t>(mean_dy[blockIdx.x]);\n  auto m_dy_c = static_cast<accscalar_t>(sum_dy[blockIdx.x]) / div;\n  auto factor_1_c = inv_std[blockIdx.x];\n  auto factor_2_c = (weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[blockIdx.x])) * factor_1_c;\n  // factor_1_c = factor_1_c * factor_1_c * mean_dy_xmu[blockIdx.x];\n  factor_1_c = factor_1_c * factor_1_c * sum_dy_xmu[blockIdx.x] / div;\n\n  for (int batch_offset = blockIdx.y * blockDim.y + threadIdx.y; batch_offset < bs;\n       batch_offset += gridDim.y * blockDim.y) {\n    int address_base = blockIdx.x * ss + batch_offset * gridDim.x * ss;\n    for (int offset = threadIdx.x + blockIdx.z * blockDim.x; offset < ss; offset += gridDim.z * blockDim.x) {\n      grad_input[address_base + offset] =\n          (static_cast<accscalar_t>(grad_output[address_base + offset]) - m_dy_c -\n           (static_cast<accscalar_t>(input[address_base + offset]) - m_c) * factor_1_c) *\n          factor_2_c;\n    }\n  }\n}\n\n// welford kernel for c last tensor calculating mean/biased_variance/unbiased_variance\ntemplate <typename scalar_t, typename accscalar_t, typename outscalar_t, int PARALLEL_LOADS>\n__global__ void welford_kernel_c_last(const scalar_t* __restrict__ input, outscalar_t* __restrict__ out_mean,\n                                      outscalar_t* __restrict__ out_var_biased, volatile accscalar_t* staging_data,\n                                      int* semaphores, const int reduction_size, const int stride) {\n  // hide latency with concurrency\n  accscalar_t x_mean[PARALLEL_LOADS];\n  accscalar_t m_2_n[PARALLEL_LOADS];\n  int count[PARALLEL_LOADS];\n\n#pragma unroll\n  for (int i = 0; i < PARALLEL_LOADS; i++) {\n    x_mean[i] = accscalar_t(0);\n    m_2_n[i] = accscalar_t(0);\n    count[i] = accscalar_t(0);\n  }\n  // tensor dimension (m,c)\n\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n    accscalar_t x_math[PARALLEL_LOADS];\n    accscalar_t x_count_inv[PARALLEL_LOADS];\n    accscalar_t is_valid[PARALLEL_LOADS];\n\n    // load multiple data in\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        x_math[j] = input[address_base];\n        count[j]++;\n        x_count_inv[j] = accscalar_t(1) / count[j];\n        is_valid[j] = accscalar_t(1);\n      } else {\n        x_math[j] = accscalar_t(0);\n        x_count_inv[j] = accscalar_t(0);\n        is_valid[j] = accscalar_t(0);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n\n    // calculate mean/m2n with welford\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      accscalar_t delta0 = x_math[j] - x_mean[j];\n      x_mean[j] += delta0 * x_count_inv[j];\n      accscalar_t delta1 = x_math[j] - x_mean[j];\n      m_2_n[j] += delta0 * delta1 * is_valid[j];\n    }\n  }\n\n  // thread reduction to accumulate mean/m_2_n/count between PARALLEL_LOADS\n#pragma unroll\n  for (int j = 1; j < PARALLEL_LOADS; j++) {\n    welford_merge_element(count[0], x_mean[0], m_2_n[0], count[j], x_mean[j], m_2_n[j]);\n  }\n\n  // release x_mean / m_2_n\n  auto mean_th = x_mean[0];\n  auto m2_th = m_2_n[0];\n  auto count_th = count[0];\n\n  // block-wise reduction with shared memory (since reduction cannot be done within a warp)\n  static __shared__ accscalar_t shmem_mean[MAX_BLOCK_SIZE];\n  static __shared__ accscalar_t shmem_m2n[MAX_BLOCK_SIZE];\n  static __shared__ int shmem_count[MAX_BLOCK_SIZE];\n\n  welford_merge_block_vertical(count_th, mean_th, m2_th, shmem_count, shmem_mean, shmem_m2n);\n\n  // grid reduction if needed (coop launch used at the first place)\n  if (gridDim.y > 1) {\n    volatile accscalar_t* staging_mean = staging_data;\n    volatile accscalar_t* staging_m2n = &staging_data[stride * gridDim.y];\n    volatile int* staging_count = reinterpret_cast<volatile int*>(&staging_m2n[stride * gridDim.y]);\n\n    address_base = c_offset + blockIdx.y * stride;\n    // write data to staging_data;\n    if (threadIdx.y == 0 && c_offset < stride) {\n      staging_mean[address_base] = mean_th;\n      staging_m2n[address_base] = m2_th;\n      staging_count[address_base] = count_th;\n    }\n\n    __threadfence();\n    __syncthreads();  // ensuring writes to staging_ is visible to all blocks\n\n    __shared__ bool is_last_block_done;\n    // mark block done\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      int old = atomicAdd(&semaphores[blockIdx.x], 1);\n      is_last_block_done = (old == (gridDim.y - 1));\n    }\n\n    __syncthreads();\n\n    // check that all data is now available in global memory\n    if (is_last_block_done) {\n      count_th = 0;\n      mean_th = accscalar_t(0.0);\n      m2_th = accscalar_t(0.0);\n\n      for (int y = threadIdx.y; y < gridDim.y; y += blockDim.y) {\n        address_base = c_offset + y * stride;\n        int num_new = c_offset < stride ? staging_count[address_base] : 0;\n        accscalar_t mean_new = c_offset < stride ? staging_mean[address_base] : accscalar_t(0.0);\n        accscalar_t m2n_new = c_offset < stride ? staging_m2n[address_base] : accscalar_t(0.0);\n\n        welford_merge_element(count_th, mean_th, m2_th, num_new, mean_new, m2n_new);\n      }\n\n      welford_merge_block_vertical(count_th, mean_th, m2_th, shmem_count, shmem_mean, shmem_m2n);\n      if (threadIdx.y == 0 && c_offset < stride) {\n        out_mean[c_offset] = static_cast<outscalar_t>(mean_th);\n        out_var_biased[c_offset] = static_cast<outscalar_t>(m2_th / count_th);\n      }\n    }\n  } else {\n    if (blockIdx.y == 0 && threadIdx.y == 0 && c_offset < stride) {\n      out_mean[c_offset] = static_cast<outscalar_t>(mean_th);\n      out_var_biased[c_offset] = static_cast<outscalar_t>(m2_th / count_th);\n    }\n  }\n}\n\n// parallel welford kernel to further reduce mean / biased_var\n// into mean / unbiased_var / inv_std across multiple processes.\ntemplate <typename scalar_t>\n__global__ void welford_kernel_parallel(const scalar_t* __restrict__ mean, const scalar_t* __restrict__ var_biased,\n                                        const int* __restrict__ numel, scalar_t* __restrict__ out_mean,\n                                        scalar_t* __restrict__ out_var, scalar_t* __restrict__ inv_std,\n                                        const int world_size, const int feature_size, const float eps) {\n  for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < feature_size; i += gridDim.x * blockDim.x) {\n    // load data;\n    int address = i;\n    scalar_t x_mean = 0;\n    scalar_t m_2_n = 0;\n    int count = 0;\n    for (int j = 0; j < world_size; j++) {\n      welford_merge_element(count, x_mean, m_2_n, numel[j], mean[address], var_biased[address] * numel[j]);\n      address += feature_size;\n    }\n    out_mean[i] = x_mean;\n    out_var[i] = m_2_n / (count - 1);\n    inv_std[i] = scalar_t(1) / sqrt(m_2_n / count + eps);\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void batchnorm_forward_c_last_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ z,\n                                                const accscalar_t* __restrict__ mean,\n                                                const accscalar_t* __restrict__ inv_std,\n                                                const layerscalar_t* __restrict__ weight,\n                                                const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                                const int reduction_size, const int stride, const bool fuse_relu) {\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto inv_std_c = static_cast<accscalar_t>(inv_std[c_offset]);\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[c_offset]);\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        auto tmp = w_c * (static_cast<accscalar_t>(input[address_base]) - m_c) * inv_std_c + s_c;\n        if (z != NULL) {\n          tmp += z[address_base];\n        }\n        out[address_base] = (fuse_relu && tmp <= accscalar_t(0.0) ? scalar_t(0.0) : static_cast<scalar_t>(tmp));\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void relu_backward_c_last_kernel(const scalar_t* __restrict__ grad_output,\n                                            const scalar_t* __restrict__ input, const scalar_t* __restrict__ z,\n                                            const accscalar_t* __restrict__ mean,\n                                            const accscalar_t* __restrict__ inv_std,\n                                            const layerscalar_t* __restrict__ weight,\n                                            const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                            const int reduction_size, const int stride) {\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto inv_std_c = static_cast<accscalar_t>(inv_std[c_offset]);\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[c_offset]);\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        auto tmp = w_c * (static_cast<accscalar_t>(input[address_base]) - m_c) * inv_std_c + s_c;\n        if (z != NULL) {\n          tmp += z[address_base];\n        }\n        out[address_base] = (tmp <= accscalar_t(0.0) ? scalar_t(0.0) : grad_output[address_base]);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\n// batchnorm backward kernel for c last tensor\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void reduce_bn_c_last_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ grad_output,\n                                        const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                        accscalar_t* __restrict__ sum_dy_o, accscalar_t* __restrict__ sum_dy_xmu_o,\n                                        layerscalar_t* __restrict__ grad_weight, layerscalar_t* __restrict__ grad_bias,\n                                        volatile accscalar_t* staging_data, int* semaphores, const int reduction_size,\n                                        const int stride) {\n  // hide latency with concurrency\n  accscalar_t sum_dy[PARALLEL_LOADS];\n  accscalar_t sum_dy_xmu[PARALLEL_LOADS];\n\n#pragma unroll\n  for (int i = 0; i < PARALLEL_LOADS; i++) {\n    sum_dy[i] = accscalar_t(0);\n    sum_dy_xmu[i] = accscalar_t(0);\n  }\n  // tensor dimension (m,c)\n\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  auto r_mean = mean[c_offset];\n  auto factor = inv_std[c_offset];\n\n  for (int i = 0; i < loop_count; i++) {\n    accscalar_t x_input[PARALLEL_LOADS];\n    accscalar_t x_grad_output[PARALLEL_LOADS];\n\n    // load multiple data in\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        x_input[j] = input[address_base];\n        x_grad_output[j] = grad_output[address_base];\n      } else {\n        x_input[j] = accscalar_t(0);\n        x_grad_output[j] = accscalar_t(0);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n\n    // calculate sum_dy / sum_dy_xmu\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      sum_dy[j] += x_grad_output[j];\n      sum_dy_xmu[j] += x_grad_output[j] * (x_input[j] - r_mean);\n    }\n  }\n\n  // thread reduction to accumulate sum_dy / sum_dy_xmu between PARALLEL_LOADS\n#pragma unroll\n  for (int j = 1; j < PARALLEL_LOADS; j++) {\n    sum_dy[0] += sum_dy[j];\n    sum_dy_xmu[0] += sum_dy_xmu[j];\n  }\n\n  // release array of registers\n  auto sum_dy_th = sum_dy[0];\n  auto sum_dy_xmu_th = sum_dy_xmu[0];\n\n  // block-wise reduction with shared memory (since reduction cannot be done within a warp)\n  static __shared__ accscalar_t shmem_sum_dy[MAX_BLOCK_SIZE];\n  static __shared__ accscalar_t shmem_sum_dy_xmu[MAX_BLOCK_SIZE];\n\n  merge_block_vertical(sum_dy_th, sum_dy_xmu_th, shmem_sum_dy, shmem_sum_dy_xmu);\n\n  // grid reduction if needed (coop launch used at the first place)\n  if (gridDim.y > 1) {\n    volatile accscalar_t* staging_sum_dy = staging_data;\n    volatile accscalar_t* staging_sum_dy_xmu = &staging_data[stride * gridDim.y];\n\n    address_base = c_offset + blockIdx.y * stride;\n    // write data to staging_data;\n    if (threadIdx.y == 0 && c_offset < stride) {\n      staging_sum_dy[address_base] = sum_dy_th;\n      staging_sum_dy_xmu[address_base] = sum_dy_xmu_th;\n    }\n\n    __threadfence();\n    __syncthreads();  // ensuring writes to staging_ is visible to all blocks\n\n    __shared__ bool is_last_block_done;\n    // mark block done\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      int old = atomicAdd(&semaphores[blockIdx.x], 1);\n      is_last_block_done = (old == (gridDim.y - 1));\n    }\n\n    __syncthreads();\n\n    // check that all data is now available in global memory\n    if (is_last_block_done) {\n      sum_dy_th = accscalar_t(0.0);\n      sum_dy_xmu_th = accscalar_t(0.0);\n\n      for (int y = threadIdx.y; y < gridDim.y; y += blockDim.y) {\n        address_base = c_offset + y * stride;\n        sum_dy_th += (c_offset < stride ? staging_sum_dy[address_base] : accscalar_t(0.0));\n        sum_dy_xmu_th += (c_offset < stride ? staging_sum_dy_xmu[address_base] : accscalar_t(0.0));\n      }\n\n      merge_block_vertical(sum_dy_th, sum_dy_xmu_th, shmem_sum_dy, shmem_sum_dy_xmu);\n      if (threadIdx.y == 0 && c_offset < stride) {\n        if (grad_bias != NULL) {\n          grad_bias[c_offset] = static_cast<layerscalar_t>(sum_dy_th);\n        }\n        if (grad_weight != NULL) {\n          grad_weight[c_offset] = static_cast<layerscalar_t>(sum_dy_xmu_th * factor);\n        }\n        // mean_dy[c_offset] = sum_dy_th / reduction_size;\n        // mean_dy_xmu[c_offset] = sum_dy_xmu_th / reduction_size;\n        sum_dy_o[c_offset] = sum_dy_th;\n        sum_dy_xmu_o[c_offset] = sum_dy_xmu_th;\n      }\n    }\n  } else {\n    if (blockIdx.y == 0 && threadIdx.y == 0 && c_offset < stride) {\n      if (grad_bias != NULL) {\n        grad_bias[c_offset] = static_cast<layerscalar_t>(sum_dy_th);\n      }\n      if (grad_weight != NULL) {\n        grad_weight[c_offset] = static_cast<layerscalar_t>(sum_dy_xmu_th * factor);\n      }\n      // mean_dy[c_offset] = sum_dy_th / reduction_size;\n      // mean_dy_xmu[c_offset] = sum_dy_xmu_th / reduction_size;\n      sum_dy_o[c_offset] = sum_dy_th;\n      sum_dy_xmu_o[c_offset] = sum_dy_xmu_th;\n    }\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void batchnorm_backward_c_last_kernel(\n    const scalar_t* __restrict__ grad_output, const scalar_t* __restrict__ input, const accscalar_t* __restrict__ mean,\n    const accscalar_t* __restrict__ inv_std, const layerscalar_t* __restrict__ weight,\n    const accscalar_t* __restrict__ sum_dy, const accscalar_t* __restrict__ sum_dy_xmu, const int* __restrict__ numel,\n    scalar_t* __restrict__ grad_input, const int64_t world_size, const int reduction_size, const int stride) {\n  int64_t div = 0;\n  for (int i = 0; i < world_size; i++) {\n    div += numel[i];\n  }\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto m_dy_c = sum_dy[c_offset] / div;\n  auto factor_1_c = inv_std[c_offset];\n  auto factor_2_c = (weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset])) * factor_1_c;\n  factor_1_c = factor_1_c * factor_1_c * sum_dy_xmu[c_offset] / div;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        grad_input[address_base] =\n            static_cast<scalar_t>((static_cast<accscalar_t>(grad_output[address_base]) - m_dy_c -\n                                   (static_cast<accscalar_t>(input[address_base]) - m_c) * factor_1_c) *\n                                  factor_2_c);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\nstd::vector<at::Tensor> welford_mean_var_CUDA(const at::Tensor input) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  auto space_size = get_tensor_spatial_size(input);\n  auto scalar_type = promote_scalartype(input);\n\n  at::Tensor out_var_biased = at::empty({feature_size}, input.options().dtype(scalar_type));\n  at::Tensor out_mean = at::empty({feature_size}, input.options().dtype(scalar_type));\n\n  int block_y = min(h_last_pow2(batch_size), int(MAX_BLOCK_SIZE / 32));\n  int block_x = max(1, min(MAX_BLOCK_SIZE / block_y, h_last_pow2(space_size)));\n  const dim3 block(block_x, block_y);\n  const dim3 grid(feature_size);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"welford_mean_var_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        welford_kernel<scalar_t_0, accscalar_t, accscalar_t>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(), out_mean.data_ptr<accscalar_t>(),\n                                     out_var_biased.data_ptr<accscalar_t>(), batch_size, feature_size, space_size););\n  }\n\n  return {out_mean, out_var_biased};\n}\n\nat::Tensor batchnorm_forward_CUDA(const at::Tensor input, const at::Tensor mean, const at::Tensor inv_std,\n                                  const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n  at::Tensor out = at::empty_like(input);\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_x = max(32, min(MAX_BLOCK_SIZE, h_last_pow2(space_size) / 4));\n  int block_y = max(1, min(MAX_BLOCK_SIZE / block_x, h_last_pow2(batch_size) / 4));\n  const dim3 block(block_x, block_y);\n  int grid_z = max(1, min(65535, h_last_pow2(space_size) / 4 / block_x));\n  int batch_group_size = max(1, min(65535, h_last_pow2(batch_size) / block_y));\n  const dim3 grid(feature_size, batch_group_size, grid_z);\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n            weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL, out.data_ptr<scalar_t_0>(), space_size,\n            batch_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n            weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL, out.data_ptr<scalar_t_0>(), space_size,\n            batch_size););\n  }\n  return out;\n}\n\nstd::vector<at::Tensor> reduce_bn_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                       const at::Tensor inv_std, const at::optional<at::Tensor> weight) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  auto scalar_type = promote_scalartype(input);\n\n  at::Tensor sum_dy = at::empty({feature_size}, mean.options());\n  at::Tensor sum_dy_xmu = at::empty({feature_size}, mean.options());\n\n  at::Tensor grad_weight;\n  at::Tensor grad_bias;\n  if (weight.has_value()) {\n    grad_weight = at::empty({feature_size}, weight.value().options());\n    grad_bias = at::empty({feature_size}, weight.value().options());\n  } else {\n    grad_weight = at::empty({0}, mean.options());\n    grad_bias = at::empty({0}, mean.options());\n  }\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_y = min(h_last_pow2(batch_size), int(MAX_BLOCK_SIZE / 32));\n  int block_x = max(1, min(MAX_BLOCK_SIZE / block_y, h_last_pow2(space_size)));\n  const dim3 block(block_x, block_y);\n  const dim3 grid(feature_size);\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        reduce_bn_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<accscalar_t>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<accscalar_t>() : NULL, batch_size, feature_size, space_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        reduce_bn_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<scalar_t_0>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<scalar_t_0>() : NULL, batch_size, feature_size, space_size););\n  }\n\n  return {sum_dy, sum_dy_xmu, grad_weight, grad_bias};\n}\n\nat::Tensor batchnorm_backward_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                   const at::Tensor inv_std, const at::optional<at::Tensor> weight,\n                                   const at::Tensor sum_dy, const at::Tensor sum_dy_xmu, const at::Tensor count) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  at::Tensor grad_input = at::empty_like(input);\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_x = max(32, min(MAX_BLOCK_SIZE, h_last_pow2(space_size) / 4));\n  int block_y = max(1, min(MAX_BLOCK_SIZE / block_x, h_last_pow2(batch_size) / 4));\n  const dim3 block(block_x, block_y);\n  int grid_z = max(1, min(65535, h_last_pow2(space_size) / 4 / block_x));\n  int batch_group_size = max(1, min(65535, h_last_pow2(batch_size) / block_y));\n  const dim3 grid(feature_size, batch_group_size, grid_z);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), space_size, batch_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), space_size, batch_size););\n  }\n\n  return grad_input;\n}\n\nstd::vector<at::Tensor> welford_parallel_CUDA(const at::Tensor mean_feature_nodes, const at::Tensor var_biased,\n                                              const at::Tensor numel, const float eps) {\n  const auto world_size = mean_feature_nodes.size(0);\n  const auto feature_size = mean_feature_nodes.size(1);\n\n  at::Tensor out_var = at::empty({feature_size}, var_biased.options());\n  at::Tensor inv_std = at::empty_like(out_var);\n  at::Tensor out_mean = at::empty_like(out_var);\n\n  at::Tensor mean_feature_nodes_ = mean_feature_nodes.contiguous();\n  at::Tensor var_biased_ = var_biased.contiguous();\n  at::Tensor numel_ = numel.contiguous();\n\n  // TODO(jie): tile this for memory coalescing!\n  const int block = std::min(h_last_pow2(feature_size), MAX_BLOCK_SIZE);\n  const int grid = std::max<int>(1, feature_size / block);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(mean_feature_nodes.scalar_type(), 0, \"welford_parallel_kernel\",\n                            welford_kernel_parallel<scalar_t_0><<<grid, block, 0, stream>>>(\n                                mean_feature_nodes_.data_ptr<scalar_t_0>(), var_biased_.data_ptr<scalar_t_0>(),\n                                numel_.data_ptr<int>(), out_mean.data_ptr<scalar_t_0>(), out_var.data_ptr<scalar_t_0>(),\n                                inv_std.data_ptr<scalar_t_0>(), world_size, feature_size, eps););\n  }\n\n  return {out_mean, out_var, inv_std};\n}\n\nstd::vector<at::Tensor> welford_mean_var_c_last_CUDA(const at::Tensor input) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  auto scalar_type = promote_scalartype(input);\n  auto option = input.options().dtype(scalar_type);\n\n  at::Tensor out_var_biased = at::empty({stride}, option);\n  at::Tensor out_mean = at::empty({stride}, option);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid, true);\n\n  at::Tensor staging_data;\n  at::Tensor semaphores;\n  if (grid.y > 1) {\n    staging_data = at::empty({4 * stride * grid.y}, option);\n    semaphores = at::zeros({grid.x}, input.options().dtype(at::kInt));\n  }\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"welford_mean_var_c_last\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        welford_kernel_c_last<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), out_mean.data_ptr<accscalar_t>(), out_var_biased.data_ptr<accscalar_t>(),\n            staging_data_ptr, semaphores_ptr, reduction_size, stride););\n  }\n\n  return {out_mean, out_var_biased};\n}\n\nat::Tensor batchnorm_forward_c_last_CUDA(const at::Tensor input, const at::optional<at::Tensor> z,\n                                         const at::Tensor mean, const at::Tensor inv_std,\n                                         const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift,\n                                         const bool fuse_relu) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor out = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride, fuse_relu););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride, fuse_relu););\n  }\n  return out;\n}\n\nstd::vector<at::Tensor> reduce_bn_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input,\n                                              const at::Tensor mean, const at::Tensor inv_std,\n                                              const at::optional<at::Tensor> weight) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor sumn_dy = at::empty({stride}, mean.options());\n  at::Tensor sum_dy_xmu = at::empty({stride}, mean.options());\n\n  at::Tensor grad_weight;\n  at::Tensor grad_bias;\n  if (weight.has_value()) {\n    grad_weight = at::empty({stride}, weight.value().options());\n    grad_bias = at::empty({stride}, weight.value().options());\n  } else {\n    // because I cannot return an uninitialized at::Tensor\n    grad_weight = at::empty({0}, mean.options());\n    grad_bias = at::empty({0}, mean.options());\n  }\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid, true);\n\n  at::Tensor staging_data;\n  at::Tensor semaphores;\n  if (grid.y > 1) {\n    staging_data = at::empty({2 * stride * grid.y}, mean.options());\n    semaphores = at::zeros({grid.x}, input.options().dtype(at::kInt));\n  }\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        reduce_bn_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sumn_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<accscalar_t>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<accscalar_t>() : NULL, staging_data_ptr, semaphores_ptr,\n            reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        reduce_bn_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sumn_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<scalar_t_0>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<scalar_t_0>() : NULL, staging_data_ptr, semaphores_ptr,\n            reduction_size, stride););\n  }\n\n  return {sumn_dy, sum_dy_xmu, grad_weight, grad_bias};\n}\n\nat::Tensor batchnorm_backward_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                          const at::Tensor inv_std, const at::optional<at::Tensor> weight,\n                                          const at::Tensor sum_dy, const at::Tensor sum_dy_xmu,\n                                          const at::Tensor count) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor grad_input = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), reduction_size, stride););\n  }\n\n  return grad_input;\n}\n\nat::Tensor relu_backward_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input,\n                                     const at::optional<at::Tensor> z, const at::Tensor mean, const at::Tensor inv_std,\n                                     const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor out = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        relu_backward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        relu_backward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n            z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL, mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL, out.data_ptr<scalar_t_0>(), reduction_size,\n            stride););\n  }\n  return out;\n}\n", "output": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <hip/hip_runtime.h>\n\n#include <iostream>\n#include <vector>\n\n#include \"type_shim.h\"\n\n__device__ __forceinline__ int lastpow2(int n) {\n  int out = 1 << (31 - __clz(n));\n  if (n == out) out >>= 1;\n  return out;\n}\n\n__host__ __forceinline__ int h_next_pow2(unsigned int n) {\n  n--;\n  n |= (n >> 1);\n  n |= (n >> 2);\n  n |= (n >> 4);\n  n |= (n >> 8);\n  n |= (n >> 16);\n  return ++n;\n}\n\n__host__ __forceinline__ int h_last_pow2(unsigned int n) {\n  n |= (n >> 1);\n  n |= (n >> 2);\n  n |= (n >> 4);\n  n |= (n >> 8);\n  n |= (n >> 16);\n  return n - (n >> 1);\n}\n\n#define WARP_SIZE 32\n\ntemplate <typename T>\n__device__ __forceinline__ T warp_reduce_sum(T val) {\n#pragma unroll\n  for (int i = WARP_SIZE / 2; i > 0; i >>= 1) val = val + __shfl_down_sync(0xffffffff, val, i);\n  return val;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ T reduce_block(T* x, T val) {\n  int tid = threadIdx.y * blockDim.x + threadIdx.x;\n  int blockSize = blockDim.x * blockDim.y;\n\n  if (blockSize > 32) {\n    val = warp_reduce_sum(val);\n    if (tid % WARP_SIZE == 0) x[tid / WARP_SIZE] = val;\n\n    __syncthreads();\n\n    val = (tid < blockSize / WARP_SIZE ? x[tid % WARP_SIZE] : T(0));\n  }\n\n  if (tid / WARP_SIZE == 0) val = warp_reduce_sum(val);\n\n  return val;\n}\n\n#define ELEMENTS_PER_ITER 4  // enables concurrency within each thread to hide latency\n#define ELEMENTS_PER_THREAD 16\n#define OPTIMAL_TILE_W 32\n#define MAX_H_BLOCK 128\n#define MAX_BLOCK_SIZE 512\n\n__host__ int div_ru(int x, int y) { return h_last_pow2(1 + (x - 1) / y); }\n\n__host__ void flexible_launch_configs(const int reduction, const int stride, dim3& block, dim3& grid,\n                                      const bool coop_flag = false) {\n  int block_x = std::min(h_last_pow2(stride), OPTIMAL_TILE_W);\n  int block_y = std::min(h_last_pow2(div_ru(reduction, ELEMENTS_PER_THREAD)), MAX_BLOCK_SIZE / block_x);\n  if (block_x * block_y != MAX_BLOCK_SIZE) {\n    block_x = std::min(h_last_pow2(stride), MAX_BLOCK_SIZE / block_y);\n  }\n\n  int grid_x = div_ru(stride, block_x);\n  int grid_y = std::min(div_ru(reduction, block_y * ELEMENTS_PER_THREAD), MAX_H_BLOCK);\n  if (coop_flag) {\n    // it's not worth having a grid reduction if the reduction dimension is not big enough\n    grid_y = grid_y < 8 ? 1 : grid_y;\n  }\n\n  block.x = block_x;\n  block.y = block_y;\n  block.z = 1;\n  grid.x = grid_x;\n  grid.y = grid_y;\n  grid.z = 1;\n}\n\ntemplate <typename T, typename C>\n__device__ __forceinline__ void welford_merge_element(C& count, T& mean, T& m2n, const C& num_new, const T& mean_new,\n                                                      const T& m2n_new) {\n  T factor = T(1.0) / max(1, (count + num_new));\n  T delta0 = mean - mean_new;\n  mean = (mean_new * num_new + mean * count) * factor;\n  m2n += m2n_new + delta0 * delta0 * num_new * count * factor;\n  count += num_new;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void warp_reduce_mean_m2n(T& mean, T& m2n, int& num) {\n#pragma unroll\n  for (int i = WARP_SIZE / 2; i > 0; i >>= 1) {\n    auto num_new = __shfl_down_sync(0xffffffff, num, i);\n    auto mean_new = __shfl_down_sync(0xffffffff, mean, i);\n    auto m2n_new = __shfl_down_sync(0xffffffff, m2n, i);\n    welford_merge_element(num, mean, m2n, num_new, mean_new, m2n_new);\n  }\n}\n\ntemplate <typename T>\n__device__ void welford_reduce_mean_m2n(T* __restrict__ x, int* __restrict__ count, T& mean, T& m2n, int& num,\n                                        int block_size, int thread_id) {\n  int lane = thread_id % WARP_SIZE;\n  int wid = thread_id / WARP_SIZE;\n\n  if (block_size > 32) {\n    warp_reduce_mean_m2n(mean, m2n, num);\n    if (lane == 0) {\n      x[wid * 2] = mean;\n      x[wid * 2 + 1] = m2n;\n      count[wid] = num;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n      mean = (thread_id < block_size / WARP_SIZE) ? x[lane * 2] : T(0);\n      m2n = (thread_id < block_size / WARP_SIZE) ? x[lane * 2 + 1] : T(0);\n      num = (thread_id < block_size / WARP_SIZE) ? count[lane] : int(0);\n    }\n  }\n\n  if (wid == 0) warp_reduce_mean_m2n(mean, m2n, num);\n\n  return;\n}\n\n// return spatial size for NC+ Tensors\n__host__ int get_tensor_spatial_size(const at::Tensor& input) {\n  auto space_size = input.size(2);\n  for (int i = 3; i < input.ndimension(); i++) {\n    space_size *= input.size(i);\n  }\n  return space_size;\n}\n\n// promote accumulation scalar type. promote half to float.\n__host__ at::ScalarType promote_scalartype(const at::Tensor& input) {\n  return input.scalar_type() == at::ScalarType::Half ? at::ScalarType::Float : input.scalar_type();\n}\n\n// return single element size, optional accumulation type promotion.\n__host__ size_t get_element_data_size(const at::Tensor& input, bool accumulation = false) {\n  auto scalar_type = accumulation ? promote_scalartype(input) : input.scalar_type();\n  return at::elementSize(scalar_type);\n}\n\ntemplate <typename T, typename C>\n__device__ __forceinline__ void welford_merge_block_vertical(C& count, T& mean, T& m2n, C* shmem_count, T* shmem_mean,\n                                                             T* shmem_m2n) {\n  // write to shared memory\n  auto address_base = threadIdx.x + threadIdx.y * blockDim.x;\n  shmem_mean[address_base] = mean;\n  shmem_m2n[address_base] = m2n;\n  shmem_count[address_base] = count;\n\n#pragma unroll\n  for (int offset = blockDim.y / 2; offset > 0; offset >>= 1) {\n    __syncthreads();\n    if (threadIdx.y < offset && threadIdx.y + offset < blockDim.y) {\n      auto address = address_base + offset * blockDim.x;\n      // read shared memory back to register for reduction\n      auto num_new = shmem_count[address];\n      auto mean_new = shmem_mean[address];\n      auto m2n_new = shmem_m2n[address];\n\n      welford_merge_element(count, mean, m2n, num_new, mean_new, m2n_new);\n\n      // last write is not necessary\n      shmem_mean[address_base] = mean;\n      shmem_m2n[address_base] = m2n;\n      shmem_count[address_base] = count;\n    }\n  }\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void merge_block_vertical(T& sum_dy, T& sum_dy_xmu, T* shmem_sum_dy, T* shmem_sum_dy_xmu) {\n  // write to shared memory\n  auto address_base = threadIdx.x + threadIdx.y * blockDim.x;\n  shmem_sum_dy[address_base] = sum_dy;\n  shmem_sum_dy_xmu[address_base] = sum_dy_xmu;\n\n#pragma unroll\n  for (int offset = blockDim.y / 2; offset > 0; offset >>= 1) {\n    __syncthreads();\n    if (threadIdx.y < offset && threadIdx.y + offset < blockDim.y) {\n      auto address = address_base + offset * blockDim.x;\n\n      sum_dy += shmem_sum_dy[address];\n      sum_dy_xmu += shmem_sum_dy_xmu[address];\n\n      // last write is not necessary\n      shmem_sum_dy[address_base] = sum_dy;\n      shmem_sum_dy_xmu[address_base] = sum_dy_xmu;\n    }\n  }\n}\n\n// welford kernel calculating mean/biased_variance/unbiased_variance\ntemplate <typename scalar_t, typename accscalar_t, typename outscalar_t>\n__global__ void welford_kernel(const scalar_t* __restrict__ input, outscalar_t* __restrict__ out_mean,\n                               outscalar_t* __restrict__ out_var_biased, const int bs, const int fs, const int ss) {\n  int block_size = blockDim.x * blockDim.y;\n  int count = 0;\n  accscalar_t x_mean = accscalar_t(0);\n  accscalar_t m_2_n = accscalar_t(0);\n\n  int thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  for (int batch_id = threadIdx.y; batch_id < bs; batch_id += blockDim.y) {\n    int input_base = blockIdx.x * ss + batch_id * ss * fs;\n    // sequential welford\n    for (int offset = threadIdx.x; offset < ss; offset += blockDim.x) {\n      count++;\n      auto x_n = static_cast<accscalar_t>(input[offset + input_base]);\n      auto d = x_n - x_mean;\n      x_mean += d / count;\n      m_2_n += d * (x_n - x_mean);\n    }\n  }\n\n  static __shared__ int s_mem[160];\n  accscalar_t* s_mem_ac = (accscalar_t*)&s_mem[32];\n\n  welford_reduce_mean_m2n<accscalar_t>(s_mem_ac, s_mem, x_mean, m_2_n, count, block_size, thread_id);\n\n  if (thread_id == 0) {\n    out_mean[blockIdx.x] = static_cast<outscalar_t>(x_mean);\n    out_var_biased[blockIdx.x] = static_cast<outscalar_t>(m_2_n / count);\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void batchnorm_forward_kernel(const scalar_t* __restrict__ input, const accscalar_t* __restrict__ mean,\n                                         const accscalar_t* __restrict__ inv_std,\n                                         const layerscalar_t* __restrict__ weight,\n                                         const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                         const int ss, const int bs) {\n  auto m_c = mean[blockIdx.x];\n  auto inv_std_c = inv_std[blockIdx.x];\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[blockIdx.x]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[blockIdx.x]);\n\n  for (int batch_offset = blockIdx.y * blockDim.y + threadIdx.y; batch_offset < bs;\n       batch_offset += gridDim.y * blockDim.y) {\n    int address_base = blockIdx.x * ss + batch_offset * gridDim.x * ss;\n    for (int offset = threadIdx.x + blockIdx.z * blockDim.x; offset < ss; offset += gridDim.z * blockDim.x) {\n      out[address_base + offset] =\n          static_cast<scalar_t>(w_c * (static_cast<accscalar_t>(input[address_base + offset]) - m_c) * inv_std_c + s_c);\n    }\n  }\n}\n\n// Backward BN kernel, calculates grad_bias, grad_weight as well as intermediate\n// results to calculating grad_input.\n// Breaking the grad_input to two step to support sync BN, which requires all\n// reduce of the intermediate results across processes.\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void reduce_bn_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ grad_output,\n                                 const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                 accscalar_t* __restrict__ sum_dy_o, accscalar_t* __restrict__ sum_dy_xmu_o,\n                                 layerscalar_t* __restrict__ grad_weight, layerscalar_t* __restrict__ grad_bias,\n                                 const int bs, const int fs, const int ss) {\n  static __shared__ int s_mem[64];\n  // int total_item_num = bs * ss;\n\n  int thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  auto r_mean = mean[blockIdx.x];\n  auto factor = inv_std[blockIdx.x];\n\n  // Kahan sum\n  accscalar_t sum_dy = 0.0;\n  accscalar_t sum_dy_xmu = 0.0;\n  accscalar_t sum_dy_c = 0.0;\n  accscalar_t sum_dy_xmu_c = 0.0;\n  for (int batch_id = threadIdx.y; batch_id < bs; batch_id += blockDim.y) {\n    int input_base = blockIdx.x * ss + batch_id * ss * fs;\n    for (int offset = threadIdx.x; offset < ss; offset += blockDim.x) {\n      auto e_grad = static_cast<accscalar_t>(grad_output[offset + input_base]);\n      auto e_input = static_cast<accscalar_t>(input[offset + input_base]);\n      // calculating sum_dy\n      auto sum_dy_y = e_grad - sum_dy_c;\n      auto sum_dy_t = sum_dy + sum_dy_y;\n      sum_dy_c = (sum_dy_t - sum_dy) - sum_dy_y;\n      sum_dy = sum_dy_t;\n\n      // calculating sum_dy_xmu\n      auto sum_dy_xmu_y = e_grad * (e_input - r_mean) - sum_dy_xmu_c;\n      auto sum_dy_xmu_t = sum_dy_xmu + sum_dy_xmu_y;\n      sum_dy_xmu_c = (sum_dy_xmu_t - sum_dy_xmu) - sum_dy_xmu_y;\n      sum_dy_xmu = sum_dy_xmu_t;\n    }\n  }\n\n  sum_dy = reduce_block((accscalar_t*)s_mem, sum_dy);\n  __syncthreads();\n  sum_dy_xmu = reduce_block((accscalar_t*)s_mem, sum_dy_xmu);\n\n  if (thread_id == 0) {\n    if (grad_bias != NULL) {\n      grad_bias[blockIdx.x] = static_cast<layerscalar_t>(sum_dy);\n    }\n    if (grad_weight != NULL) {\n      grad_weight[blockIdx.x] = static_cast<layerscalar_t>(sum_dy_xmu * factor);\n    }\n    // mean_dy[blockIdx.x] = sum_dy / total_item_num;\n    // mean_dy_xmu[blockIdx.x] = sum_dy_xmu / total_item_num;\n    sum_dy_o[blockIdx.x] = sum_dy;\n    sum_dy_xmu_o[blockIdx.x] = sum_dy_xmu;\n  }\n}\n\n// elementwise backward BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t>\n__global__ void batchnorm_backward_kernel(const scalar_t* __restrict__ grad_output, const scalar_t* __restrict__ input,\n                                          const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                          const layerscalar_t* __restrict__ weight,\n                                          const accscalar_t* __restrict__ sum_dy,\n                                          const accscalar_t* __restrict__ sum_dy_xmu, const int* __restrict__ numel,\n                                          scalar_t* __restrict__ grad_input, const int64_t world_size, const int ss,\n                                          const int bs) {\n  int64_t div = 0;\n  for (int i = 0; i < world_size; i++) {\n    div += numel[i];\n  }\n  auto m_c = static_cast<accscalar_t>(mean[blockIdx.x]);\n  // auto m_dy_c = static_cast<accscalar_t>(mean_dy[blockIdx.x]);\n  auto m_dy_c = static_cast<accscalar_t>(sum_dy[blockIdx.x]) / div;\n  auto factor_1_c = inv_std[blockIdx.x];\n  auto factor_2_c = (weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[blockIdx.x])) * factor_1_c;\n  // factor_1_c = factor_1_c * factor_1_c * mean_dy_xmu[blockIdx.x];\n  factor_1_c = factor_1_c * factor_1_c * sum_dy_xmu[blockIdx.x] / div;\n\n  for (int batch_offset = blockIdx.y * blockDim.y + threadIdx.y; batch_offset < bs;\n       batch_offset += gridDim.y * blockDim.y) {\n    int address_base = blockIdx.x * ss + batch_offset * gridDim.x * ss;\n    for (int offset = threadIdx.x + blockIdx.z * blockDim.x; offset < ss; offset += gridDim.z * blockDim.x) {\n      grad_input[address_base + offset] =\n          (static_cast<accscalar_t>(grad_output[address_base + offset]) - m_dy_c -\n           (static_cast<accscalar_t>(input[address_base + offset]) - m_c) * factor_1_c) *\n          factor_2_c;\n    }\n  }\n}\n\n// welford kernel for c last tensor calculating mean/biased_variance/unbiased_variance\ntemplate <typename scalar_t, typename accscalar_t, typename outscalar_t, int PARALLEL_LOADS>\n__global__ void welford_kernel_c_last(const scalar_t* __restrict__ input, outscalar_t* __restrict__ out_mean,\n                                      outscalar_t* __restrict__ out_var_biased, volatile accscalar_t* staging_data,\n                                      int* semaphores, const int reduction_size, const int stride) {\n  // hide latency with concurrency\n  accscalar_t x_mean[PARALLEL_LOADS];\n  accscalar_t m_2_n[PARALLEL_LOADS];\n  int count[PARALLEL_LOADS];\n\n#pragma unroll\n  for (int i = 0; i < PARALLEL_LOADS; i++) {\n    x_mean[i] = accscalar_t(0);\n    m_2_n[i] = accscalar_t(0);\n    count[i] = accscalar_t(0);\n  }\n  // tensor dimension (m,c)\n\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n    accscalar_t x_math[PARALLEL_LOADS];\n    accscalar_t x_count_inv[PARALLEL_LOADS];\n    accscalar_t is_valid[PARALLEL_LOADS];\n\n    // load multiple data in\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        x_math[j] = input[address_base];\n        count[j]++;\n        x_count_inv[j] = accscalar_t(1) / count[j];\n        is_valid[j] = accscalar_t(1);\n      } else {\n        x_math[j] = accscalar_t(0);\n        x_count_inv[j] = accscalar_t(0);\n        is_valid[j] = accscalar_t(0);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n\n    // calculate mean/m2n with welford\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      accscalar_t delta0 = x_math[j] - x_mean[j];\n      x_mean[j] += delta0 * x_count_inv[j];\n      accscalar_t delta1 = x_math[j] - x_mean[j];\n      m_2_n[j] += delta0 * delta1 * is_valid[j];\n    }\n  }\n\n  // thread reduction to accumulate mean/m_2_n/count between PARALLEL_LOADS\n#pragma unroll\n  for (int j = 1; j < PARALLEL_LOADS; j++) {\n    welford_merge_element(count[0], x_mean[0], m_2_n[0], count[j], x_mean[j], m_2_n[j]);\n  }\n\n  // release x_mean / m_2_n\n  auto mean_th = x_mean[0];\n  auto m2_th = m_2_n[0];\n  auto count_th = count[0];\n\n  // block-wise reduction with shared memory (since reduction cannot be done within a warp)\n  static __shared__ accscalar_t shmem_mean[MAX_BLOCK_SIZE];\n  static __shared__ accscalar_t shmem_m2n[MAX_BLOCK_SIZE];\n  static __shared__ int shmem_count[MAX_BLOCK_SIZE];\n\n  welford_merge_block_vertical(count_th, mean_th, m2_th, shmem_count, shmem_mean, shmem_m2n);\n\n  // grid reduction if needed (coop launch used at the first place)\n  if (gridDim.y > 1) {\n    volatile accscalar_t* staging_mean = staging_data;\n    volatile accscalar_t* staging_m2n = &staging_data[stride * gridDim.y];\n    volatile int* staging_count = reinterpret_cast<volatile int*>(&staging_m2n[stride * gridDim.y]);\n\n    address_base = c_offset + blockIdx.y * stride;\n    // write data to staging_data;\n    if (threadIdx.y == 0 && c_offset < stride) {\n      staging_mean[address_base] = mean_th;\n      staging_m2n[address_base] = m2_th;\n      staging_count[address_base] = count_th;\n    }\n\n    __threadfence();\n    __syncthreads();  // ensuring writes to staging_ is visible to all blocks\n\n    __shared__ bool is_last_block_done;\n    // mark block done\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      int old = atomicAdd(&semaphores[blockIdx.x], 1);\n      is_last_block_done = (old == (gridDim.y - 1));\n    }\n\n    __syncthreads();\n\n    // check that all data is now available in global memory\n    if (is_last_block_done) {\n      count_th = 0;\n      mean_th = accscalar_t(0.0);\n      m2_th = accscalar_t(0.0);\n\n      for (int y = threadIdx.y; y < gridDim.y; y += blockDim.y) {\n        address_base = c_offset + y * stride;\n        int num_new = c_offset < stride ? staging_count[address_base] : 0;\n        accscalar_t mean_new = c_offset < stride ? staging_mean[address_base] : accscalar_t(0.0);\n        accscalar_t m2n_new = c_offset < stride ? staging_m2n[address_base] : accscalar_t(0.0);\n\n        welford_merge_element(count_th, mean_th, m2_th, num_new, mean_new, m2n_new);\n      }\n\n      welford_merge_block_vertical(count_th, mean_th, m2_th, shmem_count, shmem_mean, shmem_m2n);\n      if (threadIdx.y == 0 && c_offset < stride) {\n        out_mean[c_offset] = static_cast<outscalar_t>(mean_th);\n        out_var_biased[c_offset] = static_cast<outscalar_t>(m2_th / count_th);\n      }\n    }\n  } else {\n    if (blockIdx.y == 0 && threadIdx.y == 0 && c_offset < stride) {\n      out_mean[c_offset] = static_cast<outscalar_t>(mean_th);\n      out_var_biased[c_offset] = static_cast<outscalar_t>(m2_th / count_th);\n    }\n  }\n}\n\n// parallel welford kernel to further reduce mean / biased_var\n// into mean / unbiased_var / inv_std across multiple processes.\ntemplate <typename scalar_t>\n__global__ void welford_kernel_parallel(const scalar_t* __restrict__ mean, const scalar_t* __restrict__ var_biased,\n                                        const int* __restrict__ numel, scalar_t* __restrict__ out_mean,\n                                        scalar_t* __restrict__ out_var, scalar_t* __restrict__ inv_std,\n                                        const int world_size, const int feature_size, const float eps) {\n  for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < feature_size; i += gridDim.x * blockDim.x) {\n    // load data;\n    int address = i;\n    scalar_t x_mean = 0;\n    scalar_t m_2_n = 0;\n    int count = 0;\n    for (int j = 0; j < world_size; j++) {\n      welford_merge_element(count, x_mean, m_2_n, numel[j], mean[address], var_biased[address] * numel[j]);\n      address += feature_size;\n    }\n    out_mean[i] = x_mean;\n    out_var[i] = m_2_n / (count - 1);\n    inv_std[i] = scalar_t(1) / sqrt(m_2_n / count + eps);\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void batchnorm_forward_c_last_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ z,\n                                                const accscalar_t* __restrict__ mean,\n                                                const accscalar_t* __restrict__ inv_std,\n                                                const layerscalar_t* __restrict__ weight,\n                                                const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                                const int reduction_size, const int stride, const bool fuse_relu) {\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto inv_std_c = static_cast<accscalar_t>(inv_std[c_offset]);\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[c_offset]);\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        auto tmp = w_c * (static_cast<accscalar_t>(input[address_base]) - m_c) * inv_std_c + s_c;\n        if (z != NULL) {\n          tmp += z[address_base];\n        }\n        out[address_base] = (fuse_relu && tmp <= accscalar_t(0.0) ? scalar_t(0.0) : static_cast<scalar_t>(tmp));\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void relu_backward_c_last_kernel(const scalar_t* __restrict__ grad_output,\n                                            const scalar_t* __restrict__ input, const scalar_t* __restrict__ z,\n                                            const accscalar_t* __restrict__ mean,\n                                            const accscalar_t* __restrict__ inv_std,\n                                            const layerscalar_t* __restrict__ weight,\n                                            const layerscalar_t* __restrict__ shift, scalar_t* __restrict__ out,\n                                            const int reduction_size, const int stride) {\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto inv_std_c = static_cast<accscalar_t>(inv_std[c_offset]);\n  auto w_c = weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset]);\n  auto s_c = shift == NULL ? accscalar_t(0.0) : static_cast<accscalar_t>(shift[c_offset]);\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        auto tmp = w_c * (static_cast<accscalar_t>(input[address_base]) - m_c) * inv_std_c + s_c;\n        if (z != NULL) {\n          tmp += z[address_base];\n        }\n        out[address_base] = (tmp <= accscalar_t(0.0) ? scalar_t(0.0) : grad_output[address_base]);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\n// batchnorm backward kernel for c last tensor\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void reduce_bn_c_last_kernel(const scalar_t* __restrict__ input, const scalar_t* __restrict__ grad_output,\n                                        const accscalar_t* __restrict__ mean, const accscalar_t* __restrict__ inv_std,\n                                        accscalar_t* __restrict__ sum_dy_o, accscalar_t* __restrict__ sum_dy_xmu_o,\n                                        layerscalar_t* __restrict__ grad_weight, layerscalar_t* __restrict__ grad_bias,\n                                        volatile accscalar_t* staging_data, int* semaphores, const int reduction_size,\n                                        const int stride) {\n  // hide latency with concurrency\n  accscalar_t sum_dy[PARALLEL_LOADS];\n  accscalar_t sum_dy_xmu[PARALLEL_LOADS];\n\n#pragma unroll\n  for (int i = 0; i < PARALLEL_LOADS; i++) {\n    sum_dy[i] = accscalar_t(0);\n    sum_dy_xmu[i] = accscalar_t(0);\n  }\n  // tensor dimension (m,c)\n\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  auto r_mean = mean[c_offset];\n  auto factor = inv_std[c_offset];\n\n  for (int i = 0; i < loop_count; i++) {\n    accscalar_t x_input[PARALLEL_LOADS];\n    accscalar_t x_grad_output[PARALLEL_LOADS];\n\n    // load multiple data in\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        x_input[j] = input[address_base];\n        x_grad_output[j] = grad_output[address_base];\n      } else {\n        x_input[j] = accscalar_t(0);\n        x_grad_output[j] = accscalar_t(0);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n\n    // calculate sum_dy / sum_dy_xmu\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      sum_dy[j] += x_grad_output[j];\n      sum_dy_xmu[j] += x_grad_output[j] * (x_input[j] - r_mean);\n    }\n  }\n\n  // thread reduction to accumulate sum_dy / sum_dy_xmu between PARALLEL_LOADS\n#pragma unroll\n  for (int j = 1; j < PARALLEL_LOADS; j++) {\n    sum_dy[0] += sum_dy[j];\n    sum_dy_xmu[0] += sum_dy_xmu[j];\n  }\n\n  // release array of registers\n  auto sum_dy_th = sum_dy[0];\n  auto sum_dy_xmu_th = sum_dy_xmu[0];\n\n  // block-wise reduction with shared memory (since reduction cannot be done within a warp)\n  static __shared__ accscalar_t shmem_sum_dy[MAX_BLOCK_SIZE];\n  static __shared__ accscalar_t shmem_sum_dy_xmu[MAX_BLOCK_SIZE];\n\n  merge_block_vertical(sum_dy_th, sum_dy_xmu_th, shmem_sum_dy, shmem_sum_dy_xmu);\n\n  // grid reduction if needed (coop launch used at the first place)\n  if (gridDim.y > 1) {\n    volatile accscalar_t* staging_sum_dy = staging_data;\n    volatile accscalar_t* staging_sum_dy_xmu = &staging_data[stride * gridDim.y];\n\n    address_base = c_offset + blockIdx.y * stride;\n    // write data to staging_data;\n    if (threadIdx.y == 0 && c_offset < stride) {\n      staging_sum_dy[address_base] = sum_dy_th;\n      staging_sum_dy_xmu[address_base] = sum_dy_xmu_th;\n    }\n\n    __threadfence();\n    __syncthreads();  // ensuring writes to staging_ is visible to all blocks\n\n    __shared__ bool is_last_block_done;\n    // mark block done\n    if (threadIdx.x == 0 && threadIdx.y == 0) {\n      int old = atomicAdd(&semaphores[blockIdx.x], 1);\n      is_last_block_done = (old == (gridDim.y - 1));\n    }\n\n    __syncthreads();\n\n    // check that all data is now available in global memory\n    if (is_last_block_done) {\n      sum_dy_th = accscalar_t(0.0);\n      sum_dy_xmu_th = accscalar_t(0.0);\n\n      for (int y = threadIdx.y; y < gridDim.y; y += blockDim.y) {\n        address_base = c_offset + y * stride;\n        sum_dy_th += (c_offset < stride ? staging_sum_dy[address_base] : accscalar_t(0.0));\n        sum_dy_xmu_th += (c_offset < stride ? staging_sum_dy_xmu[address_base] : accscalar_t(0.0));\n      }\n\n      merge_block_vertical(sum_dy_th, sum_dy_xmu_th, shmem_sum_dy, shmem_sum_dy_xmu);\n      if (threadIdx.y == 0 && c_offset < stride) {\n        if (grad_bias != NULL) {\n          grad_bias[c_offset] = static_cast<layerscalar_t>(sum_dy_th);\n        }\n        if (grad_weight != NULL) {\n          grad_weight[c_offset] = static_cast<layerscalar_t>(sum_dy_xmu_th * factor);\n        }\n        // mean_dy[c_offset] = sum_dy_th / reduction_size;\n        // mean_dy_xmu[c_offset] = sum_dy_xmu_th / reduction_size;\n        sum_dy_o[c_offset] = sum_dy_th;\n        sum_dy_xmu_o[c_offset] = sum_dy_xmu_th;\n      }\n    }\n  } else {\n    if (blockIdx.y == 0 && threadIdx.y == 0 && c_offset < stride) {\n      if (grad_bias != NULL) {\n        grad_bias[c_offset] = static_cast<layerscalar_t>(sum_dy_th);\n      }\n      if (grad_weight != NULL) {\n        grad_weight[c_offset] = static_cast<layerscalar_t>(sum_dy_xmu_th * factor);\n      }\n      // mean_dy[c_offset] = sum_dy_th / reduction_size;\n      // mean_dy_xmu[c_offset] = sum_dy_xmu_th / reduction_size;\n      sum_dy_o[c_offset] = sum_dy_th;\n      sum_dy_xmu_o[c_offset] = sum_dy_xmu_th;\n    }\n  }\n}\n\n// elementwise BN kernel\ntemplate <typename scalar_t, typename accscalar_t, typename layerscalar_t, int PARALLEL_LOADS>\n__global__ void batchnorm_backward_c_last_kernel(\n    const scalar_t* __restrict__ grad_output, const scalar_t* __restrict__ input, const accscalar_t* __restrict__ mean,\n    const accscalar_t* __restrict__ inv_std, const layerscalar_t* __restrict__ weight,\n    const accscalar_t* __restrict__ sum_dy, const accscalar_t* __restrict__ sum_dy_xmu, const int* __restrict__ numel,\n    scalar_t* __restrict__ grad_input, const int64_t world_size, const int reduction_size, const int stride) {\n  int64_t div = 0;\n  for (int i = 0; i < world_size; i++) {\n    div += numel[i];\n  }\n  // tensor dimension (m,c)\n  // loop along m dimension\n  int inner_loop_stride = blockDim.y * gridDim.y;\n\n  // offset along m dimension\n  int m_offset = blockIdx.y * blockDim.y + threadIdx.y;\n  int c_offset = blockIdx.x * blockDim.x + threadIdx.x;\n\n  auto m_c = mean[c_offset];\n  auto m_dy_c = sum_dy[c_offset] / div;\n  auto factor_1_c = inv_std[c_offset];\n  auto factor_2_c = (weight == NULL ? accscalar_t(1.0) : static_cast<accscalar_t>(weight[c_offset])) * factor_1_c;\n  factor_1_c = factor_1_c * factor_1_c * sum_dy_xmu[c_offset] / div;\n\n  int loop_count = 1 + (reduction_size - 1) / (inner_loop_stride * PARALLEL_LOADS);\n  int address_base = m_offset * stride + c_offset;\n  int address_increment = inner_loop_stride * stride;\n\n  for (int i = 0; i < loop_count; i++) {\n#pragma unroll\n    for (int j = 0; j < PARALLEL_LOADS; j++) {\n      if (c_offset < stride && m_offset < reduction_size) {\n        grad_input[address_base] =\n            static_cast<scalar_t>((static_cast<accscalar_t>(grad_output[address_base]) - m_dy_c -\n                                   (static_cast<accscalar_t>(input[address_base]) - m_c) * factor_1_c) *\n                                  factor_2_c);\n      }\n      m_offset += inner_loop_stride;\n      address_base += address_increment;\n    }\n  }\n}\n\nstd::vector<at::Tensor> welford_mean_var_CUDA(const at::Tensor input) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  auto space_size = get_tensor_spatial_size(input);\n  auto scalar_type = promote_scalartype(input);\n\n  at::Tensor out_var_biased = at::empty({feature_size}, input.options().dtype(scalar_type));\n  at::Tensor out_mean = at::empty({feature_size}, input.options().dtype(scalar_type));\n\n  int block_y = min(h_last_pow2(batch_size), int(MAX_BLOCK_SIZE / 32));\n  int block_x = max(1, min(MAX_BLOCK_SIZE / block_y, h_last_pow2(space_size)));\n  const dim3 block(block_x, block_y);\n  const dim3 grid(feature_size);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"welford_mean_var_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        welford_kernel<scalar_t_0, accscalar_t, accscalar_t>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(), out_mean.data_ptr<accscalar_t>(),\n                                     out_var_biased.data_ptr<accscalar_t>(), batch_size, feature_size, space_size););\n  }\n\n  return {out_mean, out_var_biased};\n}\n\nat::Tensor batchnorm_forward_CUDA(const at::Tensor input, const at::Tensor mean, const at::Tensor inv_std,\n                                  const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n  at::Tensor out = at::empty_like(input);\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_x = max(32, min(MAX_BLOCK_SIZE, h_last_pow2(space_size) / 4));\n  int block_y = max(1, min(MAX_BLOCK_SIZE / block_x, h_last_pow2(batch_size) / 4));\n  const dim3 block(block_x, block_y);\n  int grid_z = max(1, min(65535, h_last_pow2(space_size) / 4 / block_x));\n  int batch_group_size = max(1, min(65535, h_last_pow2(batch_size) / block_y));\n  const dim3 grid(feature_size, batch_group_size, grid_z);\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n            weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL, out.data_ptr<scalar_t_0>(), space_size,\n            batch_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n            weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL, out.data_ptr<scalar_t_0>(), space_size,\n            batch_size););\n  }\n  return out;\n}\n\nstd::vector<at::Tensor> reduce_bn_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                       const at::Tensor inv_std, const at::optional<at::Tensor> weight) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  auto scalar_type = promote_scalartype(input);\n\n  at::Tensor sum_dy = at::empty({feature_size}, mean.options());\n  at::Tensor sum_dy_xmu = at::empty({feature_size}, mean.options());\n\n  at::Tensor grad_weight;\n  at::Tensor grad_bias;\n  if (weight.has_value()) {\n    grad_weight = at::empty({feature_size}, weight.value().options());\n    grad_bias = at::empty({feature_size}, weight.value().options());\n  } else {\n    grad_weight = at::empty({0}, mean.options());\n    grad_bias = at::empty({0}, mean.options());\n  }\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_y = min(h_last_pow2(batch_size), int(MAX_BLOCK_SIZE / 32));\n  int block_x = max(1, min(MAX_BLOCK_SIZE / block_y, h_last_pow2(space_size)));\n  const dim3 block(block_x, block_y);\n  const dim3 grid(feature_size);\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        reduce_bn_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<accscalar_t>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<accscalar_t>() : NULL, batch_size, feature_size, space_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        reduce_bn_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<scalar_t_0>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<scalar_t_0>() : NULL, batch_size, feature_size, space_size););\n  }\n\n  return {sum_dy, sum_dy_xmu, grad_weight, grad_bias};\n}\n\nat::Tensor batchnorm_backward_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                   const at::Tensor inv_std, const at::optional<at::Tensor> weight,\n                                   const at::Tensor sum_dy, const at::Tensor sum_dy_xmu, const at::Tensor count) {\n  const auto batch_size = input.size(0);\n  const auto feature_size = input.size(1);\n\n  at::Tensor grad_input = at::empty_like(input);\n\n  auto space_size = get_tensor_spatial_size(input);\n\n  int block_x = max(32, min(MAX_BLOCK_SIZE, h_last_pow2(space_size) / 4));\n  int block_y = max(1, min(MAX_BLOCK_SIZE / block_x, h_last_pow2(batch_size) / 4));\n  const dim3 block(block_x, block_y);\n  int grid_z = max(1, min(65535, h_last_pow2(space_size) / 4 / block_x));\n  int batch_group_size = max(1, min(65535, h_last_pow2(batch_size) / block_y));\n  const dim3 grid(feature_size, batch_group_size, grid_z);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_kernel<scalar_t_0, accscalar_t, accscalar_t><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), space_size, batch_size););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_kernel<scalar_t_0, accscalar_t, scalar_t_0><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), space_size, batch_size););\n  }\n\n  return grad_input;\n}\n\nstd::vector<at::Tensor> welford_parallel_CUDA(const at::Tensor mean_feature_nodes, const at::Tensor var_biased,\n                                              const at::Tensor numel, const float eps) {\n  const auto world_size = mean_feature_nodes.size(0);\n  const auto feature_size = mean_feature_nodes.size(1);\n\n  at::Tensor out_var = at::empty({feature_size}, var_biased.options());\n  at::Tensor inv_std = at::empty_like(out_var);\n  at::Tensor out_mean = at::empty_like(out_var);\n\n  at::Tensor mean_feature_nodes_ = mean_feature_nodes.contiguous();\n  at::Tensor var_biased_ = var_biased.contiguous();\n  at::Tensor numel_ = numel.contiguous();\n\n  // TODO(jie): tile this for memory coalescing!\n  const int block = std::min(h_last_pow2(feature_size), MAX_BLOCK_SIZE);\n  const int grid = std::max<int>(1, feature_size / block);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(mean_feature_nodes.scalar_type(), 0, \"welford_parallel_kernel\",\n                            welford_kernel_parallel<scalar_t_0><<<grid, block, 0, stream>>>(\n                                mean_feature_nodes_.data_ptr<scalar_t_0>(), var_biased_.data_ptr<scalar_t_0>(),\n                                numel_.data_ptr<int>(), out_mean.data_ptr<scalar_t_0>(), out_var.data_ptr<scalar_t_0>(),\n                                inv_std.data_ptr<scalar_t_0>(), world_size, feature_size, eps););\n  }\n\n  return {out_mean, out_var, inv_std};\n}\n\nstd::vector<at::Tensor> welford_mean_var_c_last_CUDA(const at::Tensor input) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  auto scalar_type = promote_scalartype(input);\n  auto option = input.options().dtype(scalar_type);\n\n  at::Tensor out_var_biased = at::empty({stride}, option);\n  at::Tensor out_mean = at::empty({stride}, option);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid, true);\n\n  at::Tensor staging_data;\n  at::Tensor semaphores;\n  if (grid.y > 1) {\n    staging_data = at::empty({4 * stride * grid.y}, option);\n    semaphores = at::zeros({grid.x}, input.options().dtype(at::kInt));\n  }\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"welford_mean_var_c_last\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        welford_kernel_c_last<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), out_mean.data_ptr<accscalar_t>(), out_var_biased.data_ptr<accscalar_t>(),\n            staging_data_ptr, semaphores_ptr, reduction_size, stride););\n  }\n\n  return {out_mean, out_var_biased};\n}\n\nat::Tensor batchnorm_forward_c_last_CUDA(const at::Tensor input, const at::optional<at::Tensor> z,\n                                         const at::Tensor mean, const at::Tensor inv_std,\n                                         const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift,\n                                         const bool fuse_relu) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor out = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride, fuse_relu););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_forward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride, fuse_relu););\n  }\n  return out;\n}\n\nstd::vector<at::Tensor> reduce_bn_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input,\n                                              const at::Tensor mean, const at::Tensor inv_std,\n                                              const at::optional<at::Tensor> weight) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor sumn_dy = at::empty({stride}, mean.options());\n  at::Tensor sum_dy_xmu = at::empty({stride}, mean.options());\n\n  at::Tensor grad_weight;\n  at::Tensor grad_bias;\n  if (weight.has_value()) {\n    grad_weight = at::empty({stride}, weight.value().options());\n    grad_bias = at::empty({stride}, weight.value().options());\n  } else {\n    // because I cannot return an uninitialized at::Tensor\n    grad_weight = at::empty({0}, mean.options());\n    grad_bias = at::empty({0}, mean.options());\n  }\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid, true);\n\n  at::Tensor staging_data;\n  at::Tensor semaphores;\n  if (grid.y > 1) {\n    staging_data = at::empty({2 * stride * grid.y}, mean.options());\n    semaphores = at::zeros({grid.x}, input.options().dtype(at::kInt));\n  }\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        reduce_bn_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sumn_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<accscalar_t>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<accscalar_t>() : NULL, staging_data_ptr, semaphores_ptr,\n            reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_backward_reduce\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        accscalar_t* staging_data_ptr = grid.y > 1 ? staging_data.data_ptr<accscalar_t>() : nullptr;\n        int* semaphores_ptr = grid.y > 1 ? semaphores.data_ptr<int>() : nullptr;\n        reduce_bn_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            input.data_ptr<scalar_t_0>(), grad_output.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), sumn_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(),\n            weight.has_value() ? grad_weight.data_ptr<scalar_t_0>() : NULL,\n            weight.has_value() ? grad_bias.data_ptr<scalar_t_0>() : NULL, staging_data_ptr, semaphores_ptr,\n            reduction_size, stride););\n  }\n\n  return {sumn_dy, sum_dy_xmu, grad_weight, grad_bias};\n}\n\nat::Tensor batchnorm_backward_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input, const at::Tensor mean,\n                                          const at::Tensor inv_std, const at::optional<at::Tensor> weight,\n                                          const at::Tensor sum_dy, const at::Tensor sum_dy_xmu,\n                                          const at::Tensor count) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor grad_input = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        batchnorm_backward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(), mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            sum_dy.data_ptr<accscalar_t>(), sum_dy_xmu.data_ptr<accscalar_t>(), count.data_ptr<int>(),\n            grad_input.data_ptr<scalar_t_0>(), count.numel(), reduction_size, stride););\n  }\n\n  return grad_input;\n}\n\nat::Tensor relu_backward_c_last_CUDA(const at::Tensor grad_output, const at::Tensor input,\n                                     const at::optional<at::Tensor> z, const at::Tensor mean, const at::Tensor inv_std,\n                                     const at::optional<at::Tensor> weight, const at::optional<at::Tensor> shift) {\n  const auto stride = input.size(input.ndimension() - 1);\n  const auto reduction_size = input.numel() / stride;\n\n  at::Tensor out = at::empty_like(input);\n\n  dim3 block;\n  dim3 grid;\n  flexible_launch_configs(reduction_size, stride, block, grid);\n\n  auto stream = at::cuda::getCurrentCUDAStream();\n\n  if (input.scalar_type() == at::ScalarType::Half && weight.has_value() &&\n      weight.value().scalar_type() == at::ScalarType::Float) {\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        relu_backward_c_last_kernel<scalar_t_0, accscalar_t, accscalar_t, ELEMENTS_PER_ITER>\n        <<<grid, block, 0, stream>>>(grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n                                     z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL,\n                                     mean.data_ptr<accscalar_t>(), inv_std.data_ptr<accscalar_t>(),\n                                     weight.has_value() ? weight.value().data_ptr<accscalar_t>() : NULL,\n                                     shift.has_value() ? shift.value().data_ptr<accscalar_t>() : NULL,\n                                     out.data_ptr<scalar_t_0>(), reduction_size, stride););\n  } else {\n    if (weight.has_value()) {\n      TORCH_CHECK(input.scalar_type() == weight.value().scalar_type(),\n                  \"input.scalar_type() is not supported with weight.scalar_type()\");\n    }\n    using namespace at;\n    DISPATCH_FLOAT_AND_HALF(\n        input.scalar_type(), 0, \"batchnorm_forward\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n        relu_backward_c_last_kernel<scalar_t_0, accscalar_t, scalar_t_0, ELEMENTS_PER_ITER><<<grid, block, 0, stream>>>(\n            grad_output.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n            z.has_value() ? z.value().data_ptr<scalar_t_0>() : NULL, mean.data_ptr<accscalar_t>(),\n            inv_std.data_ptr<accscalar_t>(), weight.has_value() ? weight.value().data_ptr<scalar_t_0>() : NULL,\n            shift.has_value() ? shift.value().data_ptr<scalar_t_0>() : NULL, out.data_ptr<scalar_t_0>(), reduction_size,\n            stride););\n  }\n  return out;\n}\n", "source": "/kaggle/working/apex/csrc/welford.cu"}
{"input": "#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDACachingAllocator.h>\n#include <torch/extension.h>\n\n#include <cassert>\n#include <cstdio>\n#include <ctime>\n#include <list>\n\n#include \"nccl.h\"\n\n/*\n * This file implements a crude but effective mechanism for copying data between tenors owned by different ranks\n * on the same machine using cudaMemcpyAsync peer-to-peer transfers.\n */\n\nnamespace {\n\n__global__ void AddDelay_kernel(const int delay, int* counter) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    // waste time while doing something compiler can't predict, thus preventing it from optimizing away this code.\n    int new_counter = 0;\n    double elapsed = 0;\n    clock_t start = clock();\n    do {\n      clock_t now = clock();\n      elapsed = (double)(now - start) * 1e9 / CLOCKS_PER_SEC;\n      ++new_counter;\n    } while (elapsed < (double)delay);\n    *counter = new_counter;\n  }\n}\n\nclass NcclCommWrapper {\n private:\n  ncclComm_t comm;\n  int rank, world_size;\n\n  ncclDataType_t get_nccl_type(at::Tensor input) {\n    switch (input.scalar_type()) {\n      case at::ScalarType::Half:\n        return ncclFloat16;\n      case at::ScalarType::Float:\n        return ncclFloat32;\n      case at::ScalarType::Double:\n        return ncclFloat64;\n      case at::ScalarType::Byte:\n        return ncclUint8;\n      case at::ScalarType::Char:\n        return ncclInt8;\n      case at::ScalarType::Int:\n        return ncclInt32;\n      case at::ScalarType::Long:\n        return ncclInt64;\n      case at::ScalarType::BFloat16:\n        return ncclBfloat16;\n      default:\n        assert(false);\n    }\n  }\n\n public:\n  NcclCommWrapper() {\n    memset(&comm, 0, sizeof(ncclComm_t));\n    rank = 0;\n    world_size = 0;\n  }\n  NcclCommWrapper(ncclUniqueId id, int my_rank, int num_ranks) {\n    ncclCommInitRank(&comm, num_ranks, id, my_rank);\n    rank = my_rank;\n    world_size = num_ranks;\n  }\n\n  ~NcclCommWrapper() {\n    printf(\"ncclCommDestroy()\\n\");\n    ncclCommDestroy(comm);\n  }\n\n  void left_right_halo_exchange_inplace(int left_rank, int right_rank, at::Tensor left_output_halo,\n                                        at::Tensor right_output_halo, at::Tensor left_input_halo,\n                                        at::Tensor right_input_halo) {\n    auto stream = at::cuda::getCurrentCUDAStream();\n    ncclGroupStart();\n    ncclDataType_t ncclType = get_nccl_type(left_output_halo);\n    bool left_zero = (left_rank < 0);\n    bool right_zero = (right_rank < 0);\n    size_t left_n = torch::numel(left_output_halo);\n    size_t right_n = torch::numel(right_output_halo);\n    assert(left_n > 0 && left_n == right_n);\n    if (left_zero) {\n      left_input_halo.zero_();\n    } else {\n      AT_DISPATCH_ALL_TYPES_AND3(\n          at::ScalarType::Bool, at::ScalarType::BFloat16, at::ScalarType::Half, left_output_halo.scalar_type(),\n          \"left_halo_exch\", [&]() {\n            // send left (to my_rank - 1)\n            ncclSend(left_output_halo.data_ptr<scalar_t>(), left_n, ncclType, left_rank, comm, stream);\n            // receive left (from my_rank - 1)\n            ncclRecv(left_input_halo.data_ptr<scalar_t>(), right_n, ncclType, left_rank, comm, stream);\n          });\n    }\n    if (right_zero) {\n      right_input_halo.zero_();\n    } else {\n      AT_DISPATCH_ALL_TYPES_AND3(\n          at::ScalarType::Bool, at::ScalarType::BFloat16, at::ScalarType::Half, right_output_halo.scalar_type(),\n          \"right_halo_exch\", [&]() {\n            // send right (to my_rank + 1 )\n            ncclSend(right_output_halo.data_ptr<scalar_t>(), right_n, ncclType, right_rank, comm, stream);\n            // receive right (from my_rank + 1)\n            ncclRecv(right_input_halo.data_ptr<scalar_t>(), left_n, ncclType, right_rank, comm, stream);\n          });\n    }\n    ncclGroupEnd();\n  }\n\n  std::vector<at::Tensor> left_right_halo_exchange(int left_rank, int right_rank, at::Tensor left_output_halo,\n                                                   at::Tensor right_output_halo) {\n    // after halo exchange:\n    // left_output_halo of rank+1 ends up in right_input_halo of rank\n    // right_output_halo of rank-1 ends up in left_input_halo of rank\n    auto right_input_halo = torch::empty_like(left_output_halo);\n    auto left_input_halo = torch::empty_like(right_output_halo);\n    left_right_halo_exchange_inplace(left_rank, right_rank, left_output_halo, right_output_halo, left_input_halo,\n                                     right_input_halo);\n    return {left_input_halo, right_input_halo};\n  }\n};\n\nclass ManagedObjects {\n public:\n  ManagedObjects() {}\n  ~ManagedObjects() {\n    for (auto it = _nccl_comms.begin(); it != _nccl_comms.end(); ++it) {\n      delete *it;\n    }\n  }\n\n  int add_comm(NcclCommWrapper* comm) {\n    int handle = _nccl_comms.size();\n    _nccl_comms.push_back(comm);\n    return handle;\n  }\n\n  NcclCommWrapper& get_comm(int handle) {\n    assert(handle >= 0 && handle < _nccl_comms.size());\n    return *_nccl_comms[handle];\n  }\n\n private:\n  std::vector<NcclCommWrapper*> _nccl_comms;\n};\nclass ManagedObjects mo;\n\n}  // end anonymous namespace\n\nnamespace apex {\nnamespace contrib {\nnamespace nccl_p2p {\n\nat::Tensor get_unique_nccl_id(int n) {\n  ncclUniqueId id;\n  ncclGetUniqueId(&id);\n  auto id_tensor = torch::empty({n, (int)sizeof(ncclUniqueId)},\n                                torch::dtype(torch::kUInt8).device(torch::kCPU).requires_grad(false));\n  auto id_ptr = id_tensor.data_ptr<uint8_t>();\n  size_t offset = 0;\n  for (int i = 0; i < n; ++i) {\n    ncclUniqueId id;\n    ncclGetUniqueId(&id);\n    memcpy(id_ptr + offset, &id, sizeof(ncclUniqueId));\n    offset += sizeof(ncclUniqueId);\n  }\n  return id_tensor;\n}\n\nint init_nccl_comm(at::Tensor unique_nccl_id, int my_rank, int num_ranks) {\n  ncclUniqueId id;\n  auto unique_nccl_id_ptr = unique_nccl_id.data_ptr<uint8_t>();\n  memcpy(&id, unique_nccl_id_ptr, sizeof(ncclUniqueId));\n  NcclCommWrapper* comm = new NcclCommWrapper(id, my_rank, num_ranks);\n  int handle = mo.add_comm(comm);\n  comm = 0L;\n  return handle;\n}\n\nvoid left_right_halo_exchange_inplace(int handle, int left_rank, int right_rank, at::Tensor left_output_halo,\n                                      at::Tensor right_output_halo, at::Tensor left_input_halo,\n                                      at::Tensor right_input_halo) {\n  class NcclCommWrapper& communicator = mo.get_comm(handle);\n  return communicator.left_right_halo_exchange_inplace(left_rank, right_rank, left_output_halo, right_output_halo,\n                                                       left_input_halo, right_input_halo);\n}\n\nstd::vector<at::Tensor> left_right_halo_exchange(int handle, int left_rank, int right_rank, at::Tensor left_output_halo,\n                                                 at::Tensor right_output_halo) {\n  class NcclCommWrapper& communicator = mo.get_comm(handle);\n  return communicator.left_right_halo_exchange(left_rank, right_rank, left_output_halo, right_output_halo);\n}\n\nvoid add_delay(int delay) {\n  auto stream = at::cuda::getCurrentCUDAStream();\n  auto t = torch::empty({1}, torch::dtype(torch::kInt32).device(torch::kCUDA));\n  AddDelay_kernel<<<1, 1, 0, stream>>>(delay, t.data_ptr<int>());\n}\n\n}  // namespace nccl_p2p\n}  // namespace contrib\n}  // namespace apex\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDACachingAllocator.h>\n#include <torch/extension.h>\n\n#include <cassert>\n#include <cstdio>\n#include <ctime>\n#include <list>\n\n#include \"nccl.h\"\n\n/*\n * This file implements a crude but effective mechanism for copying data between tenors owned by different ranks\n * on the same machine using cudaMemcpyAsync peer-to-peer transfers.\n */\n\nnamespace {\n\n__global__ void AddDelay_kernel(const int delay, int* counter) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    // waste time while doing something compiler can't predict, thus preventing it from optimizing away this code.\n    int new_counter = 0;\n    double elapsed = 0;\n    clock_t start = clock();\n    do {\n      clock_t now = clock();\n      elapsed = (double)(now - start) * 1e9 / CLOCKS_PER_SEC;\n      ++new_counter;\n    } while (elapsed < (double)delay);\n    *counter = new_counter;\n  }\n}\n\nclass NcclCommWrapper {\n private:\n  ncclComm_t comm;\n  int rank, world_size;\n\n  ncclDataType_t get_nccl_type(at::Tensor input) {\n    switch (input.scalar_type()) {\n      case at::ScalarType::Half:\n        return ncclFloat16;\n      case at::ScalarType::Float:\n        return ncclFloat32;\n      case at::ScalarType::Double:\n        return ncclFloat64;\n      case at::ScalarType::Byte:\n        return ncclUint8;\n      case at::ScalarType::Char:\n        return ncclInt8;\n      case at::ScalarType::Int:\n        return ncclInt32;\n      case at::ScalarType::Long:\n        return ncclInt64;\n      case at::ScalarType::BFloat16:\n        return ncclBfloat16;\n      default:\n        assert(false);\n    }\n  }\n\n public:\n  NcclCommWrapper() {\n    memset(&comm, 0, sizeof(ncclComm_t));\n    rank = 0;\n    world_size = 0;\n  }\n  NcclCommWrapper(ncclUniqueId id, int my_rank, int num_ranks) {\n    ncclCommInitRank(&comm, num_ranks, id, my_rank);\n    rank = my_rank;\n    world_size = num_ranks;\n  }\n\n  ~NcclCommWrapper() {\n    printf(\"ncclCommDestroy()\\n\");\n    ncclCommDestroy(comm);\n  }\n\n  void left_right_halo_exchange_inplace(int left_rank, int right_rank, at::Tensor left_output_halo,\n                                        at::Tensor right_output_halo, at::Tensor left_input_halo,\n                                        at::Tensor right_input_halo) {\n    auto stream = at::cuda::getCurrentCUDAStream();\n    ncclGroupStart();\n    ncclDataType_t ncclType = get_nccl_type(left_output_halo);\n    bool left_zero = (left_rank < 0);\n    bool right_zero = (right_rank < 0);\n    size_t left_n = torch::numel(left_output_halo);\n    size_t right_n = torch::numel(right_output_halo);\n    assert(left_n > 0 && left_n == right_n);\n    if (left_zero) {\n      left_input_halo.zero_();\n    } else {\n      AT_DISPATCH_ALL_TYPES_AND3(\n          at::ScalarType::Bool, at::ScalarType::BFloat16, at::ScalarType::Half, left_output_halo.scalar_type(),\n          \"left_halo_exch\", [&]() {\n            // send left (to my_rank - 1)\n            ncclSend(left_output_halo.data_ptr<scalar_t>(), left_n, ncclType, left_rank, comm, stream);\n            // receive left (from my_rank - 1)\n            ncclRecv(left_input_halo.data_ptr<scalar_t>(), right_n, ncclType, left_rank, comm, stream);\n          });\n    }\n    if (right_zero) {\n      right_input_halo.zero_();\n    } else {\n      AT_DISPATCH_ALL_TYPES_AND3(\n          at::ScalarType::Bool, at::ScalarType::BFloat16, at::ScalarType::Half, right_output_halo.scalar_type(),\n          \"right_halo_exch\", [&]() {\n            // send right (to my_rank + 1 )\n            ncclSend(right_output_halo.data_ptr<scalar_t>(), right_n, ncclType, right_rank, comm, stream);\n            // receive right (from my_rank + 1)\n            ncclRecv(right_input_halo.data_ptr<scalar_t>(), left_n, ncclType, right_rank, comm, stream);\n          });\n    }\n    ncclGroupEnd();\n  }\n\n  std::vector<at::Tensor> left_right_halo_exchange(int left_rank, int right_rank, at::Tensor left_output_halo,\n                                                   at::Tensor right_output_halo) {\n    // after halo exchange:\n    // left_output_halo of rank+1 ends up in right_input_halo of rank\n    // right_output_halo of rank-1 ends up in left_input_halo of rank\n    auto right_input_halo = torch::empty_like(left_output_halo);\n    auto left_input_halo = torch::empty_like(right_output_halo);\n    left_right_halo_exchange_inplace(left_rank, right_rank, left_output_halo, right_output_halo, left_input_halo,\n                                     right_input_halo);\n    return {left_input_halo, right_input_halo};\n  }\n};\n\nclass ManagedObjects {\n public:\n  ManagedObjects() {}\n  ~ManagedObjects() {\n    for (auto it = _nccl_comms.begin(); it != _nccl_comms.end(); ++it) {\n      delete *it;\n    }\n  }\n\n  int add_comm(NcclCommWrapper* comm) {\n    int handle = _nccl_comms.size();\n    _nccl_comms.push_back(comm);\n    return handle;\n  }\n\n  NcclCommWrapper& get_comm(int handle) {\n    assert(handle >= 0 && handle < _nccl_comms.size());\n    return *_nccl_comms[handle];\n  }\n\n private:\n  std::vector<NcclCommWrapper*> _nccl_comms;\n};\nclass ManagedObjects mo;\n\n}  // end anonymous namespace\n\nnamespace apex {\nnamespace contrib {\nnamespace nccl_p2p {\n\nat::Tensor get_unique_nccl_id(int n) {\n  ncclUniqueId id;\n  ncclGetUniqueId(&id);\n  auto id_tensor = torch::empty({n, (int)sizeof(ncclUniqueId)},\n                                torch::dtype(torch::kUInt8).device(torch::kCPU).requires_grad(false));\n  auto id_ptr = id_tensor.data_ptr<uint8_t>();\n  size_t offset = 0;\n  for (int i = 0; i < n; ++i) {\n    ncclUniqueId id;\n    ncclGetUniqueId(&id);\n    memcpy(id_ptr + offset, &id, sizeof(ncclUniqueId));\n    offset += sizeof(ncclUniqueId);\n  }\n  return id_tensor;\n}\n\nint init_nccl_comm(at::Tensor unique_nccl_id, int my_rank, int num_ranks) {\n  ncclUniqueId id;\n  auto unique_nccl_id_ptr = unique_nccl_id.data_ptr<uint8_t>();\n  memcpy(&id, unique_nccl_id_ptr, sizeof(ncclUniqueId));\n  NcclCommWrapper* comm = new NcclCommWrapper(id, my_rank, num_ranks);\n  int handle = mo.add_comm(comm);\n  comm = 0L;\n  return handle;\n}\n\nvoid left_right_halo_exchange_inplace(int handle, int left_rank, int right_rank, at::Tensor left_output_halo,\n                                      at::Tensor right_output_halo, at::Tensor left_input_halo,\n                                      at::Tensor right_input_halo) {\n  class NcclCommWrapper& communicator = mo.get_comm(handle);\n  return communicator.left_right_halo_exchange_inplace(left_rank, right_rank, left_output_halo, right_output_halo,\n                                                       left_input_halo, right_input_halo);\n}\n\nstd::vector<at::Tensor> left_right_halo_exchange(int handle, int left_rank, int right_rank, at::Tensor left_output_halo,\n                                                 at::Tensor right_output_halo) {\n  class NcclCommWrapper& communicator = mo.get_comm(handle);\n  return communicator.left_right_halo_exchange(left_rank, right_rank, left_output_halo, right_output_halo);\n}\n\nvoid add_delay(int delay) {\n  auto stream = at::cuda::getCurrentCUDAStream();\n  auto t = torch::empty({1}, torch::dtype(torch::kInt32).device(torch::kCUDA));\n  AddDelay_kernel<<<1, 1, 0, stream>>>(delay, t.data_ptr<int>());\n}\n\n}  // namespace nccl_p2p\n}  // namespace contrib\n}  // namespace apex\n", "source": "/kaggle/working/apex/apex/contrib/csrc/nccl_p2p/nccl_p2p_cuda.cu"}
{"input": "#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDACachingAllocator.h>\n#include <cuda_runtime_api.h>\n#include <torch/extension.h>\n\n#include <cassert>\n#include <cstdio>\n#include <list>\n\n#include \"nccl.h\"\n\n#define CUDACHECK(cmd)                                                                                \\\n  do {                                                                                                \\\n    cudaError_t err = cmd;                                                                            \\\n    if (err != cudaSuccess) {                                                                         \\\n      char hostname[1024];                                                                            \\\n      gethostname(hostname, 1024);                                                                    \\\n      printf(\"%s: CUDA failure %s:%d '%s'\\n\", hostname, __FILE__, __LINE__, cudaGetErrorString(err)); \\\n    }                                                                                                 \\\n  } while (0)\n\nnamespace {\n\nconstexpr int THREADS_PER_CTA = 128;\n\n/* Basic deleter function for from_blob function.\nvoid deleter(void* ptr)\n{\n    printf(\"deleter(ptr=%p)\\n\",ptr);\n    cudaFree(ptr);\n}\n*/\n\ntemplate <class T>\nat::Tensor blob_view(T* raw_ptr, std::vector<int64_t> shape, const at::TensorOptions& options, bool channels_last) {\n  size_t size = 1;\n  std::vector<int64_t> strides(shape.size());\n  if (channels_last) {\n    assert(shape.size() == 4);\n    strides[0] = shape[1] * shape[2] * shape[3];\n    strides[1] = 1;\n    strides[2] = shape[1] * shape[3];\n    strides[3] = shape[1];\n  } else {\n    int idx = strides.size();\n    for (auto it = shape.rbegin(); it != shape.rend(); ++it) {\n      strides[--idx] = size;\n      size *= *it;\n    }\n  }\n  size *= sizeof(T);\n  // TODO: Implement dynamic reuse of pooled peer memory.\n  // We provide no deleter function because all peer memory allocations are static in this implementation.\n  return torch::from_blob((void*)raw_ptr, shape, strides, 0L, options);\n}\n\nvoid tensor_shape(at::Tensor t, bool explicit_nhwc, int& N, int& C, int& H, int& W) {\n  if (t.dim() == 3) {\n    N = 1;\n    if (explicit_nhwc) {\n      C = t.size(2);\n      H = t.size(0);\n      W = t.size(1);\n    } else {\n      C = t.size(0);\n      H = t.size(1);\n      W = t.size(2);\n    }\n  } else if (t.dim() == 4) {\n    if (explicit_nhwc) {\n      N = t.size(0);\n      C = t.size(3);\n      H = t.size(1);\n      W = t.size(2);\n    } else {\n      N = t.size(0);\n      C = t.size(1);\n      H = t.size(2);\n      W = t.size(3);\n    }\n  } else {\n    printf(\"%s;%d - t.dim() must be either 3 or 4 (was %d)\\n\", __FILE__, __LINE__, int(t.dim()));\n    assert(t.dim() == 3 || t.dim() == 4);\n  }\n}\n\nvoid tensor_strides(at::Tensor t, bool explicit_nhwc, int& stride_N, int& stride_C, int& stride_H, int& stride_W) {\n  if (t.dim() == 3) {\n    if (explicit_nhwc) {\n      stride_C = t.stride(2);\n      stride_H = t.stride(0);\n      stride_W = t.stride(1);\n    } else {\n      stride_C = t.stride(0);\n      stride_H = t.stride(1);\n      stride_W = t.stride(2);\n    }\n    stride_N = t.size(0) * t.size(1) * t.size(2);\n  } else if (t.dim() == 4) {\n    if (explicit_nhwc) {\n      stride_N = t.stride(0);\n      stride_C = t.stride(3);\n      stride_H = t.stride(1);\n      stride_W = t.stride(2);\n    } else {\n      stride_N = t.stride(0);\n      stride_C = t.stride(1);\n      stride_H = t.stride(2);\n      stride_W = t.stride(3);\n    }\n  } else {\n    printf(\"%s;%d - t.dim() must be either 3 or 4 (was %d)\\n\", __FILE__, __LINE__, t.dim());\n    assert(t.dim() == 3 || t.dim() == 4);\n  }\n}\n\ntemplate <class T>\ninline __device__ void __zero(T* dst) {\n  *dst = T(0);\n}\n\ninline __device__ void __zero(int2* dst) { *dst = {0, 0}; }\n\ntemplate <class T, bool contiguous>\ninline __device__ void zero_tensor(const int dim0, const int dim1, const int dim2, T* __restrict__ data,\n                                   const int data_stride0, const int data_stride1, const int data_stride2,\n                                   const int thread_id, const int block_id, const int num_blocks) {\n  const int global_id = thread_id + block_id * THREADS_PER_CTA;\n  const int num_threads = num_blocks * THREADS_PER_CTA;\n  const int count = dim0 * dim1 * dim2;\n  for (int i = global_id; i < count; i += num_threads) {\n    int offset;\n    if (contiguous) {\n      offset = i;\n    } else {\n      const int j2 = i % dim2;\n      const int k = i / dim2;\n      const int j1 = k % dim1;\n      const int j0 = k / dim1;\n      offset = j0 * data_stride0 + j1 * data_stride1 + j2 * data_stride2;\n    }\n    __zero(data + offset);\n  }\n}\n\ntemplate <class T, bool contiguous>\ninline __device__ void push_pull_tensor(const int dim0, const int dim1, const int dim2, const T* __restrict__ data_in,\n                                        const int data_in_stride0, const int data_in_stride1, const int data_in_stride2,\n                                        T* __restrict__ data_out, const int data_out_stride0,\n                                        const int data_out_stride1, const int data_out_stride2, int4* local_peer,\n                                        int4* remote_peer, const int thread_id, const int block_id,\n                                        const int num_blocks) {\n  // 128b=16B NVLink flit\n  // Note: Use last 4B as a semaphore\n  static_assert(sizeof(T) <= 12);\n  union Flit {\n    T payload;\n    uint uints[4];\n  };\n  // Communication bit indicates whether flit has been received from\n  // a remote GPU\n  constexpr uint communication_mask = 1 << 0;\n  // Status bit is used to choose the active peer buffer in an\n  // alternating double buffer scheme. We use buffer 1 if the bits\n  // match, use buffer 2 if the bits differ, and invert the bit\n  // after finishing with a buffer.\n  constexpr uint status_mask = 1 << 1;\n\n  // Split peer memory into two sets of buffers\n  // Note: Each block owns a THREADS_PER_CTA*2*16B chunk of peer\n  // memory\n  const int peer_offset1 = block_id * THREADS_PER_CTA * 2 + thread_id;\n  const int peer_offset2 = peer_offset1 + THREADS_PER_CTA;\n  volatile int* local_peer1 = reinterpret_cast<volatile int*>(local_peer + peer_offset1);\n  volatile int* local_peer2 = reinterpret_cast<volatile int*>(local_peer + peer_offset2);\n  volatile int* remote_peer1 = reinterpret_cast<volatile int*>(remote_peer + peer_offset1);\n  volatile int* remote_peer2 = reinterpret_cast<volatile int*>(remote_peer + peer_offset2);\n\n  // Iterate through tensor entries\n  const int num_threads = num_blocks * THREADS_PER_CTA;\n  const int count = dim0 * dim1 * dim2;\n  for (int i0 = block_id * THREADS_PER_CTA; i0 < count; i0 += num_threads) {\n    const int i = i0 + thread_id;\n    const bool has_data = i < count;\n\n    // Calculate buffer positions\n    int data_in_offset, data_out_offset;\n    if (contiguous) {\n      data_in_offset = i;\n      data_out_offset = i;\n    } else {\n      const int j2 = i % dim2;\n      const int k = i / dim2;\n      const int j1 = k % dim1;\n      const int j0 = k / dim1;\n      data_in_offset = j0 * data_in_stride0 + j1 * data_in_stride1 + j2 * data_in_stride2;\n      data_out_offset = j0 * data_out_stride0 + j1 * data_out_stride1 + j2 * data_out_stride2;\n    }\n\n    // Determine which peer memory buffer to use\n    // Note: The status bit is not affected by asynchronous\n    // communication from the remote GPU.\n    Flit local_message1, local_message2;\n    asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                 : \"=r\"(local_message1.uints[0]), \"=r\"(local_message1.uints[1]), \"=r\"(local_message1.uints[2]),\n                   \"=r\"(local_message1.uints[3])\n                 : \"l\"(local_peer1)\n                 : \"memory\");\n    asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                 : \"=r\"(local_message2.uints[0]), \"=r\"(local_message2.uints[1]), \"=r\"(local_message2.uints[2]),\n                   \"=r\"(local_message2.uints[3])\n                 : \"l\"(local_peer2)\n                 : \"memory\");\n    const uint status1 = local_message1.uints[3] & status_mask;\n    const uint status2 = local_message2.uints[3] & status_mask;\n    const bool peer1_is_active = (status1 ^ status2) == 0;\n    volatile int* ox = peer1_is_active ? remote_peer1 : remote_peer2;\n    volatile int* ix = peer1_is_active ? local_peer1 : local_peer2;\n    const uint status = peer1_is_active ? status1 : status2;\n    Flit recv_message = peer1_is_active ? local_message1 : local_message2;\n\n    // Send flit to remote GPU\n    // Note: Set communication bit and keep status bit\n    Flit send_message;\n    if (has_data) {\n      send_message.payload = data_in[data_in_offset];\n    }\n    send_message.uints[3] = communication_mask | status;\n    asm volatile(\"st.volatile.global.v4.u32 [%0], {%1,%2,%3,%4};\" ::\"l\"(ox), \"r\"(send_message.uints[0]),\n                 \"r\"(send_message.uints[1]), \"r\"(send_message.uints[2]), \"r\"(send_message.uints[3])\n                 : \"memory\");\n\n    // Recieve flit from peer\n    while ((recv_message.uints[3] & communication_mask) == 0) {\n      asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                   : \"=r\"(recv_message.uints[0]), \"=r\"(recv_message.uints[1]), \"=r\"(recv_message.uints[2]),\n                     \"=r\"(recv_message.uints[3])\n                   : \"l\"(ix)\n                   : \"memory\");\n    }\n    if (has_data) {\n      data_out[data_out_offset] = recv_message.payload;\n    }\n\n    // Reset semaphore\n    // Note: Clear communication bit and invert status bit\n    uint flag = ~status & status_mask;\n    asm volatile(\"st.volatile.global.v4.u32 [%0], {%1,%2,%3,%4};\" ::\"l\"(ix), \"n\"(0), \"n\"(0), \"n\"(0), \"r\"(flag)\n                 : \"memory\");\n    if (i0 + num_threads < count) {\n      __threadfence_system();\n    }\n  }\n}\n\ntemplate <class T, bool contiguous, bool top_zero, bool btm_zero>\n#if __CUDA_ARCH__ >= 700\n__launch_bounds__(THREADS_PER_CTA)\n#endif\n    __global__ void push_pull_halos_1d_kernel(\n        // top halo,\n        T* toh, int toh_stride0, int toh_stride1, int toh_stride2,        // top output halo (local)\n        const T* tih, int tih_stride0, int tih_stride1, int tih_stride2,  // top input halo (local)\n        int4* tox,                                                        // top output transfer buffer (remote peer)\n        int4* tix,                                                        // top input transfer buffer (local peer)\n        // btm halo\n        T* boh, int boh_stride0, int boh_stride1, int boh_stride2,        // btm output halo (local)\n        const T* bih, int bih_stride0, int bih_stride1, int bih_stride2,  // btm input halo (local)\n        int4* box,                                                        // btm output transfer buffer (remote peer)\n        int4* bix,                                                        // btm input transfer buffer (local peer)\n        // dimensions\n        int dim0, int dim1, int dim2,\n        bool top_first  // whether to launch communicate top halo first\n    ) {\n  const int num_blocks_side = gridDim.x / 2;\n  const int block_id_side = (blockIdx.x < num_blocks_side ? blockIdx.x : blockIdx.x - num_blocks_side);\n  const bool in_top_block = top_first == (blockIdx.x < num_blocks_side);\n  if (in_top_block) {\n    if (top_zero) {\n      zero_tensor<T, contiguous>(dim0, dim1, dim2, toh, toh_stride0, toh_stride1, toh_stride2, threadIdx.x,\n                                 block_id_side, num_blocks_side);\n    } else {\n      push_pull_tensor<T, contiguous>(dim0, dim1, dim2, tih, tih_stride0, tih_stride1, tih_stride2, toh, toh_stride0,\n                                      toh_stride1, toh_stride2, tix, tox, threadIdx.x, block_id_side, num_blocks_side);\n    }\n  } else {\n    if (btm_zero) {\n      zero_tensor<T, contiguous>(dim0, dim1, dim2, boh, boh_stride0, boh_stride1, boh_stride2, threadIdx.x,\n                                 block_id_side, num_blocks_side);\n    } else {\n      push_pull_tensor<T, contiguous>(dim0, dim1, dim2, bih, bih_stride0, bih_stride1, bih_stride2, boh, boh_stride0,\n                                      boh_stride1, boh_stride2, bix, box, threadIdx.x, block_id_side, num_blocks_side);\n    }\n  }\n}\n\n__global__ void delay_kernel(int delay_nanoseconds, int* counter) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    // waste time while doing something compiler can't predict, thus preventing it from optimizing away this code.\n    int new_counter = 0;\n    double elapsed = 0;\n    clock_t start = clock();\n    do {\n      clock_t now = clock();\n      elapsed = (double)(now - start) * 1e9 / CLOCKS_PER_SEC;\n      ++new_counter;\n    } while (elapsed < (double)delay_nanoseconds);\n    *counter = new_counter;\n  }\n}\n\n}  // namespace\n\nnamespace apex {\nnamespace contrib {\nnamespace peer_memory {\n\nint64_t allocate_raw(int64_t size) {\n  float* ptr = 0L;\n  cudaMalloc(&ptr, size);\n  cudaMemset(ptr, 0, size);\n  return (int64_t)ptr;\n}\n\nvoid free_raw(int64_t raw) { cudaFree((void*)raw); }\n\nvoid zero(int64_t raw, int64_t size) { cudaMemset((void*)raw, 0, size); }\n\nat::Tensor get_raw_ipc_address(int64_t raw) {\n  cudaIpcMemHandle_t mem_handle;\n  CUDACHECK(cudaIpcGetMemHandle(&mem_handle, (void*)raw));\n  const int n = sizeof(cudaIpcMemHandle_t);\n  auto address_tensor = torch::empty({n}, torch::dtype(torch::kUInt8));\n  auto address_tensor_p = address_tensor.data_ptr<uint8_t>();\n  memcpy(address_tensor_p, (uint8_t*)&mem_handle, n);\n  return address_tensor;\n}\n\nstd::vector<int64_t> get_raw_peers(at::Tensor ipc_addresses, int peer_rank, int64_t raw) {\n  int peer_group_size = ipc_addresses.size(0);\n  std::vector<int64_t> results(peer_group_size);\n  for (int i = 0; i < peer_group_size; ++i) {\n    if (i != peer_rank) {\n      cudaIpcMemHandle_t mem_handle;\n      memcpy(&mem_handle, ipc_addresses.index({i}).data_ptr<uint8_t>(), sizeof(cudaIpcMemHandle_t));\n      void* p = 0L;\n      CUDACHECK(cudaIpcOpenMemHandle((void**)&p, mem_handle, cudaIpcMemLazyEnablePeerAccess));\n      results[i] = (int64_t)p;\n    } else {\n      results[i] = (int64_t)raw;\n    }\n  }\n  return results;\n}\n\nat::Tensor blob_view_half(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<at::Half>((at::Half*)raw, shape, torch::dtype(torch::kFloat16).device(torch::kCUDA), channels_last);\n}\n\nat::Tensor blob_view_float(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<float>((float*)raw, shape, torch::dtype(torch::kFloat32).device(torch::kCUDA), channels_last);\n}\n\nat::Tensor blob_view_int(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<int>((int*)raw, shape, torch::dtype(torch::kInt32).device(torch::kCUDA), channels_last);\n}\n\nvoid push_pull_halos_1d(\n    bool diagnostics, bool explicit_nhwc,\n    int numSM,                    // number of SMs to use (zero corresponds to all SMs)\n    int rank,                     // rank in spatial parallel group\n    bool top_zero,                // if top halo should be zeroed\n    at::Tensor top_in_halo,       // top input halo buffer (in local device memory, sent to top neighbor)\n    at::Tensor top_in_transfer,   // top input transfer buffer (in local peer memory)\n    at::Tensor top_out_transfer,  // top output transfer buffer (in top neighbor peer memory)\n    at::Tensor top_out_halo,      // top output halo buffer (in local device memory, received from top neighbor)\n    bool btm_zero,                // if btm halo should be zeroed\n    at::Tensor btm_in_halo,       // btm input halo buffer (in local device memory, sent to btm neighbor)\n    at::Tensor btm_in_transfer,   // btm input transfer buffer (in local peer memory)\n    at::Tensor btm_out_transfer,  // btm output transfer buffer (in btm neighbor peer memory)\n    at::Tensor btm_out_halo       // btm output halo buffer (in local device memory, received from btm neighbor)\n) {\n  // basic checks of inputs\n  TORCH_CHECK(!(top_zero && btm_zero));\n  TORCH_CHECK(top_in_halo.is_cuda());\n  TORCH_CHECK(top_out_transfer.is_cuda());\n  TORCH_CHECK(top_in_transfer.is_cuda());\n  TORCH_CHECK(top_out_halo.is_cuda());\n  TORCH_CHECK(btm_in_halo.is_cuda());\n  TORCH_CHECK(btm_out_transfer.is_cuda());\n  TORCH_CHECK(btm_in_transfer.is_cuda());\n  TORCH_CHECK(btm_out_halo.is_cuda());\n\n  // tensor shapes\n  int tih_N, tih_C, tih_H, tih_W;\n  tensor_shape(top_in_halo, explicit_nhwc, tih_N, tih_C, tih_H, tih_W);\n  int toh_N, toh_C, toh_H, toh_W;\n  tensor_shape(top_out_halo, explicit_nhwc, toh_N, toh_C, toh_H, toh_W);\n  int bih_N, bih_C, bih_H, bih_W;\n  tensor_shape(btm_in_halo, explicit_nhwc, bih_N, bih_C, bih_H, bih_W);\n  int boh_N, boh_C, boh_H, boh_W;\n  tensor_shape(btm_out_halo, explicit_nhwc, boh_N, boh_C, boh_H, boh_W);\n  TORCH_CHECK(toh_N == tih_N && tih_N == boh_N && boh_N == bih_N && toh_C == tih_C && tih_C == boh_C &&\n              boh_C == bih_C && toh_H == tih_H && tih_H == boh_H && boh_H == bih_H && toh_W == tih_W &&\n              tih_W == boh_W && boh_W == bih_W);\n  int NN = toh_N, NC = toh_C, NH = toh_H, NW = toh_W;\n  if (diagnostics) {\n    printf(\"rank %d: NN=%d, NC=%d, NH=%d, NW=%d\\n\", rank, NN, NC, NH, NW);\n  }\n  TORCH_CHECK(NN == 1);\n\n  // tensor strides\n  int tih_stride_N, tih_stride_C, tih_stride_H, tih_stride_W;\n  tensor_strides(top_in_halo, explicit_nhwc, tih_stride_N, tih_stride_C, tih_stride_H, tih_stride_W);\n  int toh_stride_N, toh_stride_C, toh_stride_H, toh_stride_W;\n  tensor_strides(top_out_halo, explicit_nhwc, toh_stride_N, toh_stride_C, toh_stride_H, toh_stride_W);\n  int bih_stride_N, bih_stride_C, bih_stride_H, bih_stride_W;\n  tensor_strides(btm_in_halo, explicit_nhwc, bih_stride_N, bih_stride_C, bih_stride_H, bih_stride_W);\n  int boh_stride_N, boh_stride_C, boh_stride_H, boh_stride_W;\n  tensor_strides(btm_out_halo, explicit_nhwc, boh_stride_N, boh_stride_C, boh_stride_H, boh_stride_W);\n  if (diagnostics) {\n    printf(\"rank %d: tih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, tih_stride_N, tih_stride_C, tih_stride_H,\n           tih_stride_W);\n    printf(\"rank %d: toh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, toh_stride_N, toh_stride_C, toh_stride_H,\n           toh_stride_W);\n    printf(\"rank %d: bih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, bih_stride_N, bih_stride_C, bih_stride_H,\n           bih_stride_W);\n    printf(\"rank %d: boh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, boh_stride_N, boh_stride_C, boh_stride_H,\n           boh_stride_W);\n  }\n\n  // determine if nhwc\n  bool is_nhwc = (toh_stride_C == 1);\n  if (diagnostics) {\n    printf(\"rank %d: is_nhwc = %s\\n\", rank, is_nhwc ? \"true\" : \"false\");\n  }\n\n  // determine if contiguous\n  bool contiguous = true;\n  if ((NN - 1) * toh_stride_N + (NC - 1) * toh_stride_C + (NH - 1) * toh_stride_H + (NW - 1) * toh_stride_W !=\n      NN * NC * NH * NW - 1) {\n    contiguous = false;\n  }\n  if ((NN - 1) * boh_stride_N + (NC - 1) * boh_stride_C + (NH - 1) * boh_stride_H + (NW - 1) * boh_stride_W !=\n      NN * NC * NH * NW - 1) {\n    contiguous = false;\n  }\n  if (!top_zero) {\n    if (toh_stride_N != tih_stride_N || toh_stride_C != tih_stride_C || toh_stride_H != tih_stride_H ||\n        toh_stride_W != tih_stride_W) {\n      contiguous = false;\n    }\n  }\n  if (!btm_zero) {\n    if (boh_stride_N != bih_stride_N || boh_stride_C != bih_stride_C || boh_stride_H != bih_stride_H ||\n        boh_stride_W != bih_stride_W) {\n      contiguous = false;\n    }\n  }\n  if (diagnostics) {\n    printf(\"rank %d: contiguous = %s\\n\", rank, contiguous ? \"true\" : \"false\");\n  }\n\n  // determine whether to communicate top halo first\n  bool top_first = rank % 2 != 0;\n  if (diagnostics) {\n    printf(\"rank %d: top_first = %s\\n\", rank, top_first ? \"true\" : \"false\");\n  }\n\n  // peer memory buffers\n  int tox_size = top_out_transfer.numel() * top_out_transfer.element_size();\n  int tix_size = top_in_transfer.numel() * top_in_transfer.element_size();\n  int box_size = btm_out_transfer.numel() * btm_out_transfer.element_size();\n  int bix_size = btm_in_transfer.numel() * btm_in_transfer.element_size();\n  if (!top_zero) {\n    TORCH_CHECK(top_out_transfer.is_contiguous());\n    TORCH_CHECK(top_in_transfer.is_contiguous());\n    TORCH_CHECK(tox_size == tix_size);\n  }\n  if (!btm_zero) {\n    TORCH_CHECK(btm_out_transfer.is_contiguous());\n    TORCH_CHECK(btm_in_transfer.is_contiguous());\n    TORCH_CHECK(box_size == bix_size);\n  }\n\n  // figure out launch parameters\n  int device;\n  cudaGetDevice(&device);\n  cudaDeviceProp prop;\n  cudaGetDeviceProperties(&prop, device);\n  if (numSM <= 0 || numSM > prop.multiProcessorCount) {\n    numSM = prop.multiProcessorCount;\n  }\n  auto current_stream = at::cuda::getCurrentCUDAStream();\n  dim3 block(THREADS_PER_CTA, 1, 1);\n\n  // helper macros to launch templated kernel\n#define LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, TOP_ZERO, BTM_ZERO, KERNEL_ARGS, NUM_ELEMENTS)           \\\n  do {                                                                                                            \\\n    /* kernel configuration */                                                                                    \\\n    int numBlocksPerSm;                                                                                           \\\n    cudaOccupancyMaxActiveBlocksPerMultiprocessor(                                                                \\\n        &numBlocksPerSm, push_pull_halos_1d_kernel<T, CONTIGUOUS, TOP_ZERO, BTM_ZERO>, THREADS_PER_CTA, 0);       \\\n    dim3 grid(numSM * numBlocksPerSm, 1, 1);                                                                      \\\n    if (grid.x % 2 != 0) {                                                                                        \\\n      /* require even number of blocks (half for top, half for bottom) */                                         \\\n      grid.x -= 1;                                                                                                \\\n    }                                                                                                             \\\n    if ((grid.x / 2) * THREADS_PER_CTA > NUM_ELEMENTS) {                                                          \\\n      /* only need enough blocks to cover top and bottom halo elements */                                         \\\n      grid.x = 2 * ((NUM_ELEMENTS + THREADS_PER_CTA - 1) / THREADS_PER_CTA);                                      \\\n    }                                                                                                             \\\n    if (!TOP_ZERO) {                                                                                              \\\n      /* require 2*128b=32B peer memory per thread */                                                             \\\n      if ((grid.x / 2) * THREADS_PER_CTA * 32 > tox_size) {                                                       \\\n        grid.x = 2 * (tox_size / (THREADS_PER_CTA * 32));                                                         \\\n      }                                                                                                           \\\n    }                                                                                                             \\\n    if (!BTM_ZERO) {                                                                                              \\\n      /* require 2*128b=32B peer memory per thread */                                                             \\\n      if ((grid.x / 2) * THREADS_PER_CTA * 32 > box_size) {                                                       \\\n        grid.x = 2 * (box_size / (THREADS_PER_CTA * 32));                                                         \\\n      }                                                                                                           \\\n    }                                                                                                             \\\n    TORCH_CHECK(grid.x >= 2);                                                                                     \\\n                                                                                                                  \\\n    /* launch kernel */                                                                                           \\\n    cudaLaunchCooperativeKernel((void*)push_pull_halos_1d_kernel<T, CONTIGUOUS, TOP_ZERO, BTM_ZERO>, grid, block, \\\n                                KERNEL_ARGS, 0, current_stream);                                                  \\\n  } while (false)\n#define LAUNCH_PUSH_PULL_HALO_KERNEL(T, CONTIGUOUS, KERNEL_ARGS, NUM_ELEMENTS)                   \\\n  do {                                                                                           \\\n    if (top_zero) {                                                                              \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, true, false, KERNEL_ARGS, NUM_ELEMENTS);  \\\n    } else if (btm_zero) {                                                                       \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, false, true, KERNEL_ARGS, NUM_ELEMENTS);  \\\n    } else {                                                                                     \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, false, false, KERNEL_ARGS, NUM_ELEMENTS); \\\n    }                                                                                            \\\n  } while (false)\n\n  AT_DISPATCH_ALL_TYPES_AND(at::ScalarType::Half, top_out_halo.scalar_type(), \"push_pull_halos_1d_kernel\", [&] {\n    if (diagnostics) {\n      printf(\"rank %d: size(scalar_t) = %ld\\n\", rank, sizeof(scalar_t));\n    }\n    scalar_t* toh_p = top_out_halo.data_ptr<scalar_t>();\n    scalar_t* tih_p = top_in_halo.data_ptr<scalar_t>();\n    int4* tox_p = reinterpret_cast<int4*>(top_out_transfer.data_ptr<scalar_t>());\n    int4* tix_p = reinterpret_cast<int4*>(top_in_transfer.data_ptr<scalar_t>());\n    scalar_t* boh_p = btm_out_halo.data_ptr<scalar_t>();\n    scalar_t* bih_p = btm_in_halo.data_ptr<scalar_t>();\n    int4* box_p = reinterpret_cast<int4*>(btm_out_transfer.data_ptr<scalar_t>());\n    int4* bix_p = reinterpret_cast<int4*>(btm_in_transfer.data_ptr<scalar_t>());\n    if (diagnostics) printf(\"rank %d: choosing halo exchange kernel\\n\", rank);\n\n    // do int2 vector loads if channel count permits\n    if (contiguous && (NN * NH * NW * NC * sizeof(scalar_t)) % sizeof(int2) == 0) {\n      // can do contiguous int2 transfers\n      if (diagnostics) {\n      }\n      toh_stride_N = toh_stride_H = toh_stride_W = toh_stride_C = 1;\n      tih_stride_N = tih_stride_H = tih_stride_W = tih_stride_C = 1;\n      boh_stride_N = boh_stride_H = boh_stride_W = boh_stride_C = 1;\n      bih_stride_N = bih_stride_H = bih_stride_W = bih_stride_C = 1;\n      NC = (NN * NH * NW * NC * sizeof(scalar_t)) / sizeof(int2);\n      NN = NH = NW = 1;\n      if (diagnostics) {\n        printf(\"rank %d: launching contiguous int2 halo exchange kernel\\n\", rank);\n        printf(\"rank %d: NC=%d, NH=%d, NW=%d\\n\", rank, NC, NH, NW);\n      }\n      void* kernel_args[] = {(int2**)&toh_p,\n                             &toh_stride_H,\n                             &toh_stride_W,\n                             &toh_stride_C,\n                             (int2**)&tih_p,\n                             &tih_stride_H,\n                             &tih_stride_W,\n                             &tih_stride_C,\n                             &tox_p,\n                             &tix_p,\n                             (int2**)&boh_p,\n                             &boh_stride_H,\n                             &boh_stride_W,\n                             &boh_stride_C,\n                             (int2**)&bih_p,\n                             &bih_stride_H,\n                             &bih_stride_W,\n                             &bih_stride_C,\n                             &box_p,\n                             &bix_p,\n                             &NH,\n                             &NW,\n                             &NC,\n                             &top_first};\n      int num_elem = NN * NH * NW * NC;\n      LAUNCH_PUSH_PULL_HALO_KERNEL(int2, true, kernel_args, num_elem);\n    } else if (is_nhwc && (NC * sizeof(scalar_t)) % sizeof(int2) == 0) {\n      // can do strided int2 transfers\n      int divisor = sizeof(int2) / sizeof(scalar_t);\n      if (diagnostics) {\n        printf(\"rank %d: launching strided int2 halo exchange kernel\\n\", rank);\n      }\n      toh_stride_N /= divisor;\n      toh_stride_H /= divisor;\n      toh_stride_W /= divisor;\n      tih_stride_N /= divisor;\n      tih_stride_H /= divisor;\n      tih_stride_W /= divisor;\n      boh_stride_N /= divisor;\n      boh_stride_H /= divisor;\n      boh_stride_W /= divisor;\n      bih_stride_N /= divisor;\n      bih_stride_H /= divisor;\n      bih_stride_W /= divisor;\n      NC /= divisor;\n      if (diagnostics) {\n        printf(\"rank %d: divisor=%d\\n\", rank, divisor);\n        printf(\"rank %d: tih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, tih_stride_N, tih_stride_C, tih_stride_H,\n               tih_stride_W);\n        printf(\"rank %d: toh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, toh_stride_N, toh_stride_C, toh_stride_H,\n               toh_stride_W);\n        printf(\"rank %d: bih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, bih_stride_N, bih_stride_C, bih_stride_H,\n               bih_stride_W);\n        printf(\"rank %d: boh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, boh_stride_N, boh_stride_C, boh_stride_H,\n               boh_stride_W);\n        printf(\"rank %d: NC=%d, NH=%d, NW=%d\\n\", rank, NC, NH, NW);\n      }\n      void* kernel_args[] = {(int2**)&toh_p,\n                             &toh_stride_H,\n                             &toh_stride_W,\n                             &toh_stride_C,\n                             (int2**)&tih_p,\n                             &tih_stride_H,\n                             &tih_stride_W,\n                             &tih_stride_C,\n                             &tox_p,\n                             &tix_p,\n                             (int2**)&boh_p,\n                             &boh_stride_H,\n                             &boh_stride_W,\n                             &boh_stride_C,\n                             (int2**)&bih_p,\n                             &bih_stride_H,\n                             &bih_stride_W,\n                             &bih_stride_C,\n                             &box_p,\n                             &bix_p,\n                             &NH,\n                             &NW,\n                             &NC,\n                             &top_first};\n      int num_elem = NH * NW * NC;\n      LAUNCH_PUSH_PULL_HALO_KERNEL(int2, false, kernel_args, num_elem);\n    } else {\n      // cannot do int2 transfers\n      if (diagnostics) {\n        printf(\"rank %d: launching non-int2 halo exchange kernel\\n\", rank);\n      }\n      int num_elem = NC * NH * NW;\n      if (is_nhwc) {\n        void* kernel_args[] = {&toh_p,        &toh_stride_H, &toh_stride_W, &toh_stride_C, &tih_p,        &tih_stride_H,\n                               &tih_stride_W, &tih_stride_C, &tox_p,        &tix_p,        &boh_p,        &boh_stride_H,\n                               &boh_stride_W, &boh_stride_C, &bih_p,        &bih_stride_H, &bih_stride_W, &bih_stride_C,\n                               &box_p,        &bix_p,        &NH,           &NW,           &NC,           &top_first};\n        LAUNCH_PUSH_PULL_HALO_KERNEL(scalar_t, false, kernel_args, num_elem);\n      } else {\n        void* kernel_args[] = {&toh_p,        &toh_stride_C, &toh_stride_H, &toh_stride_W, &tih_p,        &tih_stride_C,\n                               &tih_stride_H, &tih_stride_W, &tox_p,        &tix_p,        &boh_p,        &boh_stride_C,\n                               &boh_stride_H, &boh_stride_W, &bih_p,        &bih_stride_C, &bih_stride_H, &bih_stride_W,\n                               &box_p,        &bix_p,        &NC,           &NH,           &NW,           &top_first};\n        LAUNCH_PUSH_PULL_HALO_KERNEL(scalar_t, false, kernel_args, num_elem);\n      }\n    }\n  });\n\n#undef LAUNCH_PUSH_PULL_HALO_KERNEL_BASE\n#undef LAUNCH_PUSH_PULL_HALO_KERNEL\n}\n\n}  // namespace peer_memory\n}  // namespace contrib\n}  // namespace apex\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDACachingAllocator.h>\n#include <hip/hip_runtime_api.h>\n#include <torch/extension.h>\n\n#include <cassert>\n#include <cstdio>\n#include <list>\n\n#include \"nccl.h\"\n\n#define CUDACHECK(cmd)                                                                                \\\n  do {                                                                                                \\\n    hipError_t err = cmd;                                                                            \\\n    if (err != hipSuccess) {                                                                         \\\n      char hostname[1024];                                                                            \\\n      gethostname(hostname, 1024);                                                                    \\\n      printf(\"%s: CUDA failure %s:%d '%s'\\n\", hostname, __FILE__, __LINE__, hipGetErrorString(err)); \\\n    }                                                                                                 \\\n  } while (0)\n\nnamespace {\n\nconstexpr int THREADS_PER_CTA = 128;\n\n/* Basic deleter function for from_blob function.\nvoid deleter(void* ptr)\n{\n    printf(\"deleter(ptr=%p)\\n\",ptr);\n    cudaFree(ptr);\n}\n*/\n\ntemplate <class T>\nat::Tensor blob_view(T* raw_ptr, std::vector<int64_t> shape, const at::TensorOptions& options, bool channels_last) {\n  size_t size = 1;\n  std::vector<int64_t> strides(shape.size());\n  if (channels_last) {\n    assert(shape.size() == 4);\n    strides[0] = shape[1] * shape[2] * shape[3];\n    strides[1] = 1;\n    strides[2] = shape[1] * shape[3];\n    strides[3] = shape[1];\n  } else {\n    int idx = strides.size();\n    for (auto it = shape.rbegin(); it != shape.rend(); ++it) {\n      strides[--idx] = size;\n      size *= *it;\n    }\n  }\n  size *= sizeof(T);\n  // TODO: Implement dynamic reuse of pooled peer memory.\n  // We provide no deleter function because all peer memory allocations are static in this implementation.\n  return torch::from_blob((void*)raw_ptr, shape, strides, 0L, options);\n}\n\nvoid tensor_shape(at::Tensor t, bool explicit_nhwc, int& N, int& C, int& H, int& W) {\n  if (t.dim() == 3) {\n    N = 1;\n    if (explicit_nhwc) {\n      C = t.size(2);\n      H = t.size(0);\n      W = t.size(1);\n    } else {\n      C = t.size(0);\n      H = t.size(1);\n      W = t.size(2);\n    }\n  } else if (t.dim() == 4) {\n    if (explicit_nhwc) {\n      N = t.size(0);\n      C = t.size(3);\n      H = t.size(1);\n      W = t.size(2);\n    } else {\n      N = t.size(0);\n      C = t.size(1);\n      H = t.size(2);\n      W = t.size(3);\n    }\n  } else {\n    printf(\"%s;%d - t.dim() must be either 3 or 4 (was %d)\\n\", __FILE__, __LINE__, int(t.dim()));\n    assert(t.dim() == 3 || t.dim() == 4);\n  }\n}\n\nvoid tensor_strides(at::Tensor t, bool explicit_nhwc, int& stride_N, int& stride_C, int& stride_H, int& stride_W) {\n  if (t.dim() == 3) {\n    if (explicit_nhwc) {\n      stride_C = t.stride(2);\n      stride_H = t.stride(0);\n      stride_W = t.stride(1);\n    } else {\n      stride_C = t.stride(0);\n      stride_H = t.stride(1);\n      stride_W = t.stride(2);\n    }\n    stride_N = t.size(0) * t.size(1) * t.size(2);\n  } else if (t.dim() == 4) {\n    if (explicit_nhwc) {\n      stride_N = t.stride(0);\n      stride_C = t.stride(3);\n      stride_H = t.stride(1);\n      stride_W = t.stride(2);\n    } else {\n      stride_N = t.stride(0);\n      stride_C = t.stride(1);\n      stride_H = t.stride(2);\n      stride_W = t.stride(3);\n    }\n  } else {\n    printf(\"%s;%d - t.dim() must be either 3 or 4 (was %d)\\n\", __FILE__, __LINE__, t.dim());\n    assert(t.dim() == 3 || t.dim() == 4);\n  }\n}\n\ntemplate <class T>\ninline __device__ void __zero(T* dst) {\n  *dst = T(0);\n}\n\ninline __device__ void __zero(int2* dst) { *dst = {0, 0}; }\n\ntemplate <class T, bool contiguous>\ninline __device__ void zero_tensor(const int dim0, const int dim1, const int dim2, T* __restrict__ data,\n                                   const int data_stride0, const int data_stride1, const int data_stride2,\n                                   const int thread_id, const int block_id, const int num_blocks) {\n  const int global_id = thread_id + block_id * THREADS_PER_CTA;\n  const int num_threads = num_blocks * THREADS_PER_CTA;\n  const int count = dim0 * dim1 * dim2;\n  for (int i = global_id; i < count; i += num_threads) {\n    int offset;\n    if (contiguous) {\n      offset = i;\n    } else {\n      const int j2 = i % dim2;\n      const int k = i / dim2;\n      const int j1 = k % dim1;\n      const int j0 = k / dim1;\n      offset = j0 * data_stride0 + j1 * data_stride1 + j2 * data_stride2;\n    }\n    __zero(data + offset);\n  }\n}\n\ntemplate <class T, bool contiguous>\ninline __device__ void push_pull_tensor(const int dim0, const int dim1, const int dim2, const T* __restrict__ data_in,\n                                        const int data_in_stride0, const int data_in_stride1, const int data_in_stride2,\n                                        T* __restrict__ data_out, const int data_out_stride0,\n                                        const int data_out_stride1, const int data_out_stride2, int4* local_peer,\n                                        int4* remote_peer, const int thread_id, const int block_id,\n                                        const int num_blocks) {\n  // 128b=16B NVLink flit\n  // Note: Use last 4B as a semaphore\n  static_assert(sizeof(T) <= 12);\n  union Flit {\n    T payload;\n    uint uints[4];\n  };\n  // Communication bit indicates whether flit has been received from\n  // a remote GPU\n  constexpr uint communication_mask = 1 << 0;\n  // Status bit is used to choose the active peer buffer in an\n  // alternating double buffer scheme. We use buffer 1 if the bits\n  // match, use buffer 2 if the bits differ, and invert the bit\n  // after finishing with a buffer.\n  constexpr uint status_mask = 1 << 1;\n\n  // Split peer memory into two sets of buffers\n  // Note: Each block owns a THREADS_PER_CTA*2*16B chunk of peer\n  // memory\n  const int peer_offset1 = block_id * THREADS_PER_CTA * 2 + thread_id;\n  const int peer_offset2 = peer_offset1 + THREADS_PER_CTA;\n  volatile int* local_peer1 = reinterpret_cast<volatile int*>(local_peer + peer_offset1);\n  volatile int* local_peer2 = reinterpret_cast<volatile int*>(local_peer + peer_offset2);\n  volatile int* remote_peer1 = reinterpret_cast<volatile int*>(remote_peer + peer_offset1);\n  volatile int* remote_peer2 = reinterpret_cast<volatile int*>(remote_peer + peer_offset2);\n\n  // Iterate through tensor entries\n  const int num_threads = num_blocks * THREADS_PER_CTA;\n  const int count = dim0 * dim1 * dim2;\n  for (int i0 = block_id * THREADS_PER_CTA; i0 < count; i0 += num_threads) {\n    const int i = i0 + thread_id;\n    const bool has_data = i < count;\n\n    // Calculate buffer positions\n    int data_in_offset, data_out_offset;\n    if (contiguous) {\n      data_in_offset = i;\n      data_out_offset = i;\n    } else {\n      const int j2 = i % dim2;\n      const int k = i / dim2;\n      const int j1 = k % dim1;\n      const int j0 = k / dim1;\n      data_in_offset = j0 * data_in_stride0 + j1 * data_in_stride1 + j2 * data_in_stride2;\n      data_out_offset = j0 * data_out_stride0 + j1 * data_out_stride1 + j2 * data_out_stride2;\n    }\n\n    // Determine which peer memory buffer to use\n    // Note: The status bit is not affected by asynchronous\n    // communication from the remote GPU.\n    Flit local_message1, local_message2;\n    asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                 : \"=r\"(local_message1.uints[0]), \"=r\"(local_message1.uints[1]), \"=r\"(local_message1.uints[2]),\n                   \"=r\"(local_message1.uints[3])\n                 : \"l\"(local_peer1)\n                 : \"memory\");\n    asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                 : \"=r\"(local_message2.uints[0]), \"=r\"(local_message2.uints[1]), \"=r\"(local_message2.uints[2]),\n                   \"=r\"(local_message2.uints[3])\n                 : \"l\"(local_peer2)\n                 : \"memory\");\n    const uint status1 = local_message1.uints[3] & status_mask;\n    const uint status2 = local_message2.uints[3] & status_mask;\n    const bool peer1_is_active = (status1 ^ status2) == 0;\n    volatile int* ox = peer1_is_active ? remote_peer1 : remote_peer2;\n    volatile int* ix = peer1_is_active ? local_peer1 : local_peer2;\n    const uint status = peer1_is_active ? status1 : status2;\n    Flit recv_message = peer1_is_active ? local_message1 : local_message2;\n\n    // Send flit to remote GPU\n    // Note: Set communication bit and keep status bit\n    Flit send_message;\n    if (has_data) {\n      send_message.payload = data_in[data_in_offset];\n    }\n    send_message.uints[3] = communication_mask | status;\n    asm volatile(\"st.volatile.global.v4.u32 [%0], {%1,%2,%3,%4};\" ::\"l\"(ox), \"r\"(send_message.uints[0]),\n                 \"r\"(send_message.uints[1]), \"r\"(send_message.uints[2]), \"r\"(send_message.uints[3])\n                 : \"memory\");\n\n    // Recieve flit from peer\n    while ((recv_message.uints[3] & communication_mask) == 0) {\n      asm volatile(\"ld.volatile.global.v4.u32 {%0,%1,%2,%3}, [%4];\"\n                   : \"=r\"(recv_message.uints[0]), \"=r\"(recv_message.uints[1]), \"=r\"(recv_message.uints[2]),\n                     \"=r\"(recv_message.uints[3])\n                   : \"l\"(ix)\n                   : \"memory\");\n    }\n    if (has_data) {\n      data_out[data_out_offset] = recv_message.payload;\n    }\n\n    // Reset semaphore\n    // Note: Clear communication bit and invert status bit\n    uint flag = ~status & status_mask;\n    asm volatile(\"st.volatile.global.v4.u32 [%0], {%1,%2,%3,%4};\" ::\"l\"(ix), \"n\"(0), \"n\"(0), \"n\"(0), \"r\"(flag)\n                 : \"memory\");\n    if (i0 + num_threads < count) {\n      __threadfence_system();\n    }\n  }\n}\n\ntemplate <class T, bool contiguous, bool top_zero, bool btm_zero>\n#if __CUDA_ARCH__ >= 700\n__launch_bounds__(THREADS_PER_CTA)\n#endif\n    __global__ void push_pull_halos_1d_kernel(\n        // top halo,\n        T* toh, int toh_stride0, int toh_stride1, int toh_stride2,        // top output halo (local)\n        const T* tih, int tih_stride0, int tih_stride1, int tih_stride2,  // top input halo (local)\n        int4* tox,                                                        // top output transfer buffer (remote peer)\n        int4* tix,                                                        // top input transfer buffer (local peer)\n        // btm halo\n        T* boh, int boh_stride0, int boh_stride1, int boh_stride2,        // btm output halo (local)\n        const T* bih, int bih_stride0, int bih_stride1, int bih_stride2,  // btm input halo (local)\n        int4* box,                                                        // btm output transfer buffer (remote peer)\n        int4* bix,                                                        // btm input transfer buffer (local peer)\n        // dimensions\n        int dim0, int dim1, int dim2,\n        bool top_first  // whether to launch communicate top halo first\n    ) {\n  const int num_blocks_side = gridDim.x / 2;\n  const int block_id_side = (blockIdx.x < num_blocks_side ? blockIdx.x : blockIdx.x - num_blocks_side);\n  const bool in_top_block = top_first == (blockIdx.x < num_blocks_side);\n  if (in_top_block) {\n    if (top_zero) {\n      zero_tensor<T, contiguous>(dim0, dim1, dim2, toh, toh_stride0, toh_stride1, toh_stride2, threadIdx.x,\n                                 block_id_side, num_blocks_side);\n    } else {\n      push_pull_tensor<T, contiguous>(dim0, dim1, dim2, tih, tih_stride0, tih_stride1, tih_stride2, toh, toh_stride0,\n                                      toh_stride1, toh_stride2, tix, tox, threadIdx.x, block_id_side, num_blocks_side);\n    }\n  } else {\n    if (btm_zero) {\n      zero_tensor<T, contiguous>(dim0, dim1, dim2, boh, boh_stride0, boh_stride1, boh_stride2, threadIdx.x,\n                                 block_id_side, num_blocks_side);\n    } else {\n      push_pull_tensor<T, contiguous>(dim0, dim1, dim2, bih, bih_stride0, bih_stride1, bih_stride2, boh, boh_stride0,\n                                      boh_stride1, boh_stride2, bix, box, threadIdx.x, block_id_side, num_blocks_side);\n    }\n  }\n}\n\n__global__ void delay_kernel(int delay_nanoseconds, int* counter) {\n  if (blockIdx.x == 0 && threadIdx.x == 0) {\n    // waste time while doing something compiler can't predict, thus preventing it from optimizing away this code.\n    int new_counter = 0;\n    double elapsed = 0;\n    clock_t start = clock();\n    do {\n      clock_t now = clock();\n      elapsed = (double)(now - start) * 1e9 / CLOCKS_PER_SEC;\n      ++new_counter;\n    } while (elapsed < (double)delay_nanoseconds);\n    *counter = new_counter;\n  }\n}\n\n}  // namespace\n\nnamespace apex {\nnamespace contrib {\nnamespace peer_memory {\n\nint64_t allocate_raw(int64_t size) {\n  float* ptr = 0L;\n  hipMalloc(&ptr, size);\n  hipMemset(ptr, 0, size);\n  return (int64_t)ptr;\n}\n\nvoid free_raw(int64_t raw) { hipFree((void*)raw); }\n\nvoid zero(int64_t raw, int64_t size) { hipMemset((void*)raw, 0, size); }\n\nat::Tensor get_raw_ipc_address(int64_t raw) {\n  hipIpcMemHandle_t mem_handle;\n  CUDACHECK(hipIpcGetMemHandle(&mem_handle, (void*)raw));\n  const int n = sizeof(hipIpcMemHandle_t);\n  auto address_tensor = torch::empty({n}, torch::dtype(torch::kUInt8));\n  auto address_tensor_p = address_tensor.data_ptr<uint8_t>();\n  memcpy(address_tensor_p, (uint8_t*)&mem_handle, n);\n  return address_tensor;\n}\n\nstd::vector<int64_t> get_raw_peers(at::Tensor ipc_addresses, int peer_rank, int64_t raw) {\n  int peer_group_size = ipc_addresses.size(0);\n  std::vector<int64_t> results(peer_group_size);\n  for (int i = 0; i < peer_group_size; ++i) {\n    if (i != peer_rank) {\n      hipIpcMemHandle_t mem_handle;\n      memcpy(&mem_handle, ipc_addresses.index({i}).data_ptr<uint8_t>(), sizeof(hipIpcMemHandle_t));\n      void* p = 0L;\n      CUDACHECK(hipIpcOpenMemHandle((void**)&p, mem_handle, hipIpcMemLazyEnablePeerAccess));\n      results[i] = (int64_t)p;\n    } else {\n      results[i] = (int64_t)raw;\n    }\n  }\n  return results;\n}\n\nat::Tensor blob_view_half(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<at::Half>((at::Half*)raw, shape, torch::dtype(torch::kFloat16).device(torch::kCUDA), channels_last);\n}\n\nat::Tensor blob_view_float(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<float>((float*)raw, shape, torch::dtype(torch::kFloat32).device(torch::kCUDA), channels_last);\n}\n\nat::Tensor blob_view_int(int64_t raw, std::vector<int64_t> shape, bool channels_last) {\n  return blob_view<int>((int*)raw, shape, torch::dtype(torch::kInt32).device(torch::kCUDA), channels_last);\n}\n\nvoid push_pull_halos_1d(\n    bool diagnostics, bool explicit_nhwc,\n    int numSM,                    // number of SMs to use (zero corresponds to all SMs)\n    int rank,                     // rank in spatial parallel group\n    bool top_zero,                // if top halo should be zeroed\n    at::Tensor top_in_halo,       // top input halo buffer (in local device memory, sent to top neighbor)\n    at::Tensor top_in_transfer,   // top input transfer buffer (in local peer memory)\n    at::Tensor top_out_transfer,  // top output transfer buffer (in top neighbor peer memory)\n    at::Tensor top_out_halo,      // top output halo buffer (in local device memory, received from top neighbor)\n    bool btm_zero,                // if btm halo should be zeroed\n    at::Tensor btm_in_halo,       // btm input halo buffer (in local device memory, sent to btm neighbor)\n    at::Tensor btm_in_transfer,   // btm input transfer buffer (in local peer memory)\n    at::Tensor btm_out_transfer,  // btm output transfer buffer (in btm neighbor peer memory)\n    at::Tensor btm_out_halo       // btm output halo buffer (in local device memory, received from btm neighbor)\n) {\n  // basic checks of inputs\n  TORCH_CHECK(!(top_zero && btm_zero));\n  TORCH_CHECK(top_in_halo.is_cuda());\n  TORCH_CHECK(top_out_transfer.is_cuda());\n  TORCH_CHECK(top_in_transfer.is_cuda());\n  TORCH_CHECK(top_out_halo.is_cuda());\n  TORCH_CHECK(btm_in_halo.is_cuda());\n  TORCH_CHECK(btm_out_transfer.is_cuda());\n  TORCH_CHECK(btm_in_transfer.is_cuda());\n  TORCH_CHECK(btm_out_halo.is_cuda());\n\n  // tensor shapes\n  int tih_N, tih_C, tih_H, tih_W;\n  tensor_shape(top_in_halo, explicit_nhwc, tih_N, tih_C, tih_H, tih_W);\n  int toh_N, toh_C, toh_H, toh_W;\n  tensor_shape(top_out_halo, explicit_nhwc, toh_N, toh_C, toh_H, toh_W);\n  int bih_N, bih_C, bih_H, bih_W;\n  tensor_shape(btm_in_halo, explicit_nhwc, bih_N, bih_C, bih_H, bih_W);\n  int boh_N, boh_C, boh_H, boh_W;\n  tensor_shape(btm_out_halo, explicit_nhwc, boh_N, boh_C, boh_H, boh_W);\n  TORCH_CHECK(toh_N == tih_N && tih_N == boh_N && boh_N == bih_N && toh_C == tih_C && tih_C == boh_C &&\n              boh_C == bih_C && toh_H == tih_H && tih_H == boh_H && boh_H == bih_H && toh_W == tih_W &&\n              tih_W == boh_W && boh_W == bih_W);\n  int NN = toh_N, NC = toh_C, NH = toh_H, NW = toh_W;\n  if (diagnostics) {\n    printf(\"rank %d: NN=%d, NC=%d, NH=%d, NW=%d\\n\", rank, NN, NC, NH, NW);\n  }\n  TORCH_CHECK(NN == 1);\n\n  // tensor strides\n  int tih_stride_N, tih_stride_C, tih_stride_H, tih_stride_W;\n  tensor_strides(top_in_halo, explicit_nhwc, tih_stride_N, tih_stride_C, tih_stride_H, tih_stride_W);\n  int toh_stride_N, toh_stride_C, toh_stride_H, toh_stride_W;\n  tensor_strides(top_out_halo, explicit_nhwc, toh_stride_N, toh_stride_C, toh_stride_H, toh_stride_W);\n  int bih_stride_N, bih_stride_C, bih_stride_H, bih_stride_W;\n  tensor_strides(btm_in_halo, explicit_nhwc, bih_stride_N, bih_stride_C, bih_stride_H, bih_stride_W);\n  int boh_stride_N, boh_stride_C, boh_stride_H, boh_stride_W;\n  tensor_strides(btm_out_halo, explicit_nhwc, boh_stride_N, boh_stride_C, boh_stride_H, boh_stride_W);\n  if (diagnostics) {\n    printf(\"rank %d: tih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, tih_stride_N, tih_stride_C, tih_stride_H,\n           tih_stride_W);\n    printf(\"rank %d: toh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, toh_stride_N, toh_stride_C, toh_stride_H,\n           toh_stride_W);\n    printf(\"rank %d: bih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, bih_stride_N, bih_stride_C, bih_stride_H,\n           bih_stride_W);\n    printf(\"rank %d: boh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, boh_stride_N, boh_stride_C, boh_stride_H,\n           boh_stride_W);\n  }\n\n  // determine if nhwc\n  bool is_nhwc = (toh_stride_C == 1);\n  if (diagnostics) {\n    printf(\"rank %d: is_nhwc = %s\\n\", rank, is_nhwc ? \"true\" : \"false\");\n  }\n\n  // determine if contiguous\n  bool contiguous = true;\n  if ((NN - 1) * toh_stride_N + (NC - 1) * toh_stride_C + (NH - 1) * toh_stride_H + (NW - 1) * toh_stride_W !=\n      NN * NC * NH * NW - 1) {\n    contiguous = false;\n  }\n  if ((NN - 1) * boh_stride_N + (NC - 1) * boh_stride_C + (NH - 1) * boh_stride_H + (NW - 1) * boh_stride_W !=\n      NN * NC * NH * NW - 1) {\n    contiguous = false;\n  }\n  if (!top_zero) {\n    if (toh_stride_N != tih_stride_N || toh_stride_C != tih_stride_C || toh_stride_H != tih_stride_H ||\n        toh_stride_W != tih_stride_W) {\n      contiguous = false;\n    }\n  }\n  if (!btm_zero) {\n    if (boh_stride_N != bih_stride_N || boh_stride_C != bih_stride_C || boh_stride_H != bih_stride_H ||\n        boh_stride_W != bih_stride_W) {\n      contiguous = false;\n    }\n  }\n  if (diagnostics) {\n    printf(\"rank %d: contiguous = %s\\n\", rank, contiguous ? \"true\" : \"false\");\n  }\n\n  // determine whether to communicate top halo first\n  bool top_first = rank % 2 != 0;\n  if (diagnostics) {\n    printf(\"rank %d: top_first = %s\\n\", rank, top_first ? \"true\" : \"false\");\n  }\n\n  // peer memory buffers\n  int tox_size = top_out_transfer.numel() * top_out_transfer.element_size();\n  int tix_size = top_in_transfer.numel() * top_in_transfer.element_size();\n  int box_size = btm_out_transfer.numel() * btm_out_transfer.element_size();\n  int bix_size = btm_in_transfer.numel() * btm_in_transfer.element_size();\n  if (!top_zero) {\n    TORCH_CHECK(top_out_transfer.is_contiguous());\n    TORCH_CHECK(top_in_transfer.is_contiguous());\n    TORCH_CHECK(tox_size == tix_size);\n  }\n  if (!btm_zero) {\n    TORCH_CHECK(btm_out_transfer.is_contiguous());\n    TORCH_CHECK(btm_in_transfer.is_contiguous());\n    TORCH_CHECK(box_size == bix_size);\n  }\n\n  // figure out launch parameters\n  int device;\n  hipGetDevice(&device);\n  hipDeviceProp_t prop;\n  hipGetDeviceProperties(&prop, device);\n  if (numSM <= 0 || numSM > prop.multiProcessorCount) {\n    numSM = prop.multiProcessorCount;\n  }\n  auto current_stream = at::cuda::getCurrentCUDAStream();\n  dim3 block(THREADS_PER_CTA, 1, 1);\n\n  // helper macros to launch templated kernel\n#define LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, TOP_ZERO, BTM_ZERO, KERNEL_ARGS, NUM_ELEMENTS)           \\\n  do {                                                                                                            \\\n    /* kernel configuration */                                                                                    \\\n    int numBlocksPerSm;                                                                                           \\\n    hipOccupancyMaxActiveBlocksPerMultiprocessor(                                                                \\\n        &numBlocksPerSm, push_pull_halos_1d_kernel<T, CONTIGUOUS, TOP_ZERO, BTM_ZERO>, THREADS_PER_CTA, 0);       \\\n    dim3 grid(numSM * numBlocksPerSm, 1, 1);                                                                      \\\n    if (grid.x % 2 != 0) {                                                                                        \\\n      /* require even number of blocks (half for top, half for bottom) */                                         \\\n      grid.x -= 1;                                                                                                \\\n    }                                                                                                             \\\n    if ((grid.x / 2) * THREADS_PER_CTA > NUM_ELEMENTS) {                                                          \\\n      /* only need enough blocks to cover top and bottom halo elements */                                         \\\n      grid.x = 2 * ((NUM_ELEMENTS + THREADS_PER_CTA - 1) / THREADS_PER_CTA);                                      \\\n    }                                                                                                             \\\n    if (!TOP_ZERO) {                                                                                              \\\n      /* require 2*128b=32B peer memory per thread */                                                             \\\n      if ((grid.x / 2) * THREADS_PER_CTA * 32 > tox_size) {                                                       \\\n        grid.x = 2 * (tox_size / (THREADS_PER_CTA * 32));                                                         \\\n      }                                                                                                           \\\n    }                                                                                                             \\\n    if (!BTM_ZERO) {                                                                                              \\\n      /* require 2*128b=32B peer memory per thread */                                                             \\\n      if ((grid.x / 2) * THREADS_PER_CTA * 32 > box_size) {                                                       \\\n        grid.x = 2 * (box_size / (THREADS_PER_CTA * 32));                                                         \\\n      }                                                                                                           \\\n    }                                                                                                             \\\n    TORCH_CHECK(grid.x >= 2);                                                                                     \\\n                                                                                                                  \\\n    /* launch kernel */                                                                                           \\\n    hipLaunchCooperativeKernel(reinterpret_cast<const void*>((void*)push_pull_halos_1d_kernel<T, CONTIGUOUS, TOP_ZERO, BTM_ZERO>), grid, block, \\\n                                KERNEL_ARGS, 0, current_stream);                                                  \\\n  } while (false)\n#define LAUNCH_PUSH_PULL_HALO_KERNEL(T, CONTIGUOUS, KERNEL_ARGS, NUM_ELEMENTS)                   \\\n  do {                                                                                           \\\n    if (top_zero) {                                                                              \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, true, false, KERNEL_ARGS, NUM_ELEMENTS);  \\\n    } else if (btm_zero) {                                                                       \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, false, true, KERNEL_ARGS, NUM_ELEMENTS);  \\\n    } else {                                                                                     \\\n      LAUNCH_PUSH_PULL_HALO_KERNEL_BASE(T, CONTIGUOUS, false, false, KERNEL_ARGS, NUM_ELEMENTS); \\\n    }                                                                                            \\\n  } while (false)\n\n  AT_DISPATCH_ALL_TYPES_AND(at::ScalarType::Half, top_out_halo.scalar_type(), \"push_pull_halos_1d_kernel\", [&] {\n    if (diagnostics) {\n      printf(\"rank %d: size(scalar_t) = %ld\\n\", rank, sizeof(scalar_t));\n    }\n    scalar_t* toh_p = top_out_halo.data_ptr<scalar_t>();\n    scalar_t* tih_p = top_in_halo.data_ptr<scalar_t>();\n    int4* tox_p = reinterpret_cast<int4*>(top_out_transfer.data_ptr<scalar_t>());\n    int4* tix_p = reinterpret_cast<int4*>(top_in_transfer.data_ptr<scalar_t>());\n    scalar_t* boh_p = btm_out_halo.data_ptr<scalar_t>();\n    scalar_t* bih_p = btm_in_halo.data_ptr<scalar_t>();\n    int4* box_p = reinterpret_cast<int4*>(btm_out_transfer.data_ptr<scalar_t>());\n    int4* bix_p = reinterpret_cast<int4*>(btm_in_transfer.data_ptr<scalar_t>());\n    if (diagnostics) printf(\"rank %d: choosing halo exchange kernel\\n\", rank);\n\n    // do int2 vector loads if channel count permits\n    if (contiguous && (NN * NH * NW * NC * sizeof(scalar_t)) % sizeof(int2) == 0) {\n      // can do contiguous int2 transfers\n      if (diagnostics) {\n      }\n      toh_stride_N = toh_stride_H = toh_stride_W = toh_stride_C = 1;\n      tih_stride_N = tih_stride_H = tih_stride_W = tih_stride_C = 1;\n      boh_stride_N = boh_stride_H = boh_stride_W = boh_stride_C = 1;\n      bih_stride_N = bih_stride_H = bih_stride_W = bih_stride_C = 1;\n      NC = (NN * NH * NW * NC * sizeof(scalar_t)) / sizeof(int2);\n      NN = NH = NW = 1;\n      if (diagnostics) {\n        printf(\"rank %d: launching contiguous int2 halo exchange kernel\\n\", rank);\n        printf(\"rank %d: NC=%d, NH=%d, NW=%d\\n\", rank, NC, NH, NW);\n      }\n      void* kernel_args[] = {(int2**)&toh_p,\n                             &toh_stride_H,\n                             &toh_stride_W,\n                             &toh_stride_C,\n                             (int2**)&tih_p,\n                             &tih_stride_H,\n                             &tih_stride_W,\n                             &tih_stride_C,\n                             &tox_p,\n                             &tix_p,\n                             (int2**)&boh_p,\n                             &boh_stride_H,\n                             &boh_stride_W,\n                             &boh_stride_C,\n                             (int2**)&bih_p,\n                             &bih_stride_H,\n                             &bih_stride_W,\n                             &bih_stride_C,\n                             &box_p,\n                             &bix_p,\n                             &NH,\n                             &NW,\n                             &NC,\n                             &top_first};\n      int num_elem = NN * NH * NW * NC;\n      LAUNCH_PUSH_PULL_HALO_KERNEL(int2, true, kernel_args, num_elem);\n    } else if (is_nhwc && (NC * sizeof(scalar_t)) % sizeof(int2) == 0) {\n      // can do strided int2 transfers\n      int divisor = sizeof(int2) / sizeof(scalar_t);\n      if (diagnostics) {\n        printf(\"rank %d: launching strided int2 halo exchange kernel\\n\", rank);\n      }\n      toh_stride_N /= divisor;\n      toh_stride_H /= divisor;\n      toh_stride_W /= divisor;\n      tih_stride_N /= divisor;\n      tih_stride_H /= divisor;\n      tih_stride_W /= divisor;\n      boh_stride_N /= divisor;\n      boh_stride_H /= divisor;\n      boh_stride_W /= divisor;\n      bih_stride_N /= divisor;\n      bih_stride_H /= divisor;\n      bih_stride_W /= divisor;\n      NC /= divisor;\n      if (diagnostics) {\n        printf(\"rank %d: divisor=%d\\n\", rank, divisor);\n        printf(\"rank %d: tih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, tih_stride_N, tih_stride_C, tih_stride_H,\n               tih_stride_W);\n        printf(\"rank %d: toh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, toh_stride_N, toh_stride_C, toh_stride_H,\n               toh_stride_W);\n        printf(\"rank %d: bih_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, bih_stride_N, bih_stride_C, bih_stride_H,\n               bih_stride_W);\n        printf(\"rank %d: boh_stride :: N=%d, C=%d, H=%d, W=%d\\n\", rank, boh_stride_N, boh_stride_C, boh_stride_H,\n               boh_stride_W);\n        printf(\"rank %d: NC=%d, NH=%d, NW=%d\\n\", rank, NC, NH, NW);\n      }\n      void* kernel_args[] = {(int2**)&toh_p,\n                             &toh_stride_H,\n                             &toh_stride_W,\n                             &toh_stride_C,\n                             (int2**)&tih_p,\n                             &tih_stride_H,\n                             &tih_stride_W,\n                             &tih_stride_C,\n                             &tox_p,\n                             &tix_p,\n                             (int2**)&boh_p,\n                             &boh_stride_H,\n                             &boh_stride_W,\n                             &boh_stride_C,\n                             (int2**)&bih_p,\n                             &bih_stride_H,\n                             &bih_stride_W,\n                             &bih_stride_C,\n                             &box_p,\n                             &bix_p,\n                             &NH,\n                             &NW,\n                             &NC,\n                             &top_first};\n      int num_elem = NH * NW * NC;\n      LAUNCH_PUSH_PULL_HALO_KERNEL(int2, false, kernel_args, num_elem);\n    } else {\n      // cannot do int2 transfers\n      if (diagnostics) {\n        printf(\"rank %d: launching non-int2 halo exchange kernel\\n\", rank);\n      }\n      int num_elem = NC * NH * NW;\n      if (is_nhwc) {\n        void* kernel_args[] = {&toh_p,        &toh_stride_H, &toh_stride_W, &toh_stride_C, &tih_p,        &tih_stride_H,\n                               &tih_stride_W, &tih_stride_C, &tox_p,        &tix_p,        &boh_p,        &boh_stride_H,\n                               &boh_stride_W, &boh_stride_C, &bih_p,        &bih_stride_H, &bih_stride_W, &bih_stride_C,\n                               &box_p,        &bix_p,        &NH,           &NW,           &NC,           &top_first};\n        LAUNCH_PUSH_PULL_HALO_KERNEL(scalar_t, false, kernel_args, num_elem);\n      } else {\n        void* kernel_args[] = {&toh_p,        &toh_stride_C, &toh_stride_H, &toh_stride_W, &tih_p,        &tih_stride_C,\n                               &tih_stride_H, &tih_stride_W, &tox_p,        &tix_p,        &boh_p,        &boh_stride_C,\n                               &boh_stride_H, &boh_stride_W, &bih_p,        &bih_stride_C, &bih_stride_H, &bih_stride_W,\n                               &box_p,        &bix_p,        &NC,           &NH,           &NW,           &top_first};\n        LAUNCH_PUSH_PULL_HALO_KERNEL(scalar_t, false, kernel_args, num_elem);\n      }\n    }\n  });\n\n#undef LAUNCH_PUSH_PULL_HALO_KERNEL_BASE\n#undef LAUNCH_PUSH_PULL_HALO_KERNEL\n}\n\n}  // namespace peer_memory\n}  // namespace contrib\n}  // namespace apex\n", "source": "/kaggle/working/apex/apex/contrib/csrc/peer_memory/peer_memory_cuda.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <torch/extension.h>\n\n#include <vector>\n\ntemplate <typename scalar_t>\n__device__ __forceinline__ scalar_t logSumExp(scalar_t a, scalar_t b) {\n  // standard log-sum-exp trick is used here to provide better numerical stability\n  return (a >= b) ? a + std::log1p(exp(b - a)) : b + std::log1p(exp(a - b));\n}\n\n// Vanilla transducer loss function (i.e. forward-backward algorithm)\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n\n// Forward (alpha) and backward (beta) path are launched together. Input is assumed to be converted\n// into log scale by the preceding log_softmax layer\n// Diagonal wavefront advancing usually used in dynamic programming is leveraged here.\n// alpha and beta are of acc_t type, as they are essentially accumulators.\n\n// This loss function supports packed input where a tensor of shape [B, T, U, H] is packed into\n// [B_packed, H].\n// Don't-care region (t > audLen) or (u > txtLen) is removed.\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_forward(const scalar_t* x, const int* label, const int* audLen, const int* txtLen,\n                                        const int64_t* batchOffset,\n                                        int64_t dictSize,  // 64-bit indexing for data tensor\n                                        int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                        acc_t* alpha, acc_t* beta, scalar_t* loss) {\n  const int batch = blockIdx.y;\n  const int tid = threadIdx.x;\n  const auto myFLen = audLen[batch];\n  // Note that start of the sentence is added as 1 here\n  const auto myGLen = txtLen[batch] + 1;\n  const auto myLabel = label + batch * (maxGLen - 1);\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  const scalar_t* myX = x + myBatchOffset * dictSize;\n  int u = tid;\n\n  if (blockIdx.x == 0) {\n    // alpha path\n    acc_t* myAlpha = alpha + batch * maxFLen * maxGLen;\n    if (u == 0) myAlpha[0] = 0;\n    __syncthreads();\n\n    for (int64_t step = 1; step < myFLen + myGLen - 1; ++step) {\n      // Move along the diagonal wavefront to leverage available parallelism\n      for (u = tid; u < myGLen; u += blockDim.x) {\n        int64_t t = step - u;\n        if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n          // Eq(16) in [1]\n          if (u == 0) {\n            // alpha(t, u) = alpha(t-1, u) * null(t-1, u)\n            myAlpha[t * maxGLen + u] = myAlpha[(t - 1) * maxGLen] + myX[((t - 1) * myStrideT) * dictSize + blankIdx];\n          } else if (t == 0) {\n            // alpha(t, u-1) = alpha(t, u-1) * y(t, u-1)\n            myAlpha[u] = myAlpha[u - 1] + myX[(u - 1) * dictSize + myLabel[u - 1]];\n          } else {\n            // alpha(t, u) = alpha(t-1, u) * null(t-1, u) + alpha(t, u-1) * y(t, u-1)\n            acc_t current = myAlpha[(t - 1) * maxGLen + u] + myX[((t - 1) * myStrideT + u) * dictSize + blankIdx];\n            acc_t next = myAlpha[t * maxGLen + u - 1] + myX[(t * myStrideT + u - 1) * dictSize + myLabel[u - 1]];\n            myAlpha[t * maxGLen + u] = logSumExp(next, current);\n          }\n        }\n      }\n      __syncthreads();\n    }\n  } else if (blockIdx.x == 1) {\n    // beta path\n    acc_t* myBeta = beta + batch * maxFLen * maxGLen;\n    if (u == 0) {\n      myBeta[(myFLen - 1) * maxGLen + myGLen - 1] = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    }\n    __syncthreads();\n\n    for (int64_t step = myFLen + myGLen - 3; step >= 0; --step) {\n      for (u = tid; u < myGLen; u += blockDim.x) {\n        int64_t t = step - u;\n        if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n          // Eq(18) in [1]\n          if (u == myGLen - 1) {\n            // beta(t, u) = beta(t+1, u) * null(t, u)\n            myBeta[t * maxGLen + u] = myBeta[(t + 1) * maxGLen + u] + myX[(t * myStrideT + u) * dictSize + blankIdx];\n          } else if (t == myFLen - 1) {\n            // beta(t, u) = beta(t, u+1) * y(t, u)\n            myBeta[t * maxGLen + u] = myBeta[t * maxGLen + u + 1] + myX[(t * myStrideT + u) * dictSize + myLabel[u]];\n          } else {\n            // beta(t, u) = beta(t+1, u)*null(t, u) + beta(t, u+1)*y(t, u)\n            acc_t current = myBeta[(t + 1) * maxGLen + u] + myX[(t * myStrideT + u) * dictSize + blankIdx];\n            acc_t next = myBeta[t * maxGLen + u + 1] + myX[(t * myStrideT + u) * dictSize + myLabel[u]];\n            myBeta[t * maxGLen + u] = logSumExp(next, current);\n          }\n        }\n      }\n      __syncthreads();\n    }\n    if (tid == 0) loss[batch] = -myBeta[0];\n  }\n}\n\n// transudcer loss function (i.e. forward-backward algorithm) with batch loading optimization.\n// Compared to the vanilla version, there are two optimizations:\n// 1. load x in batch through loop unrolling to reduce the latency.\n// 2. Use registers and shared memory to hold alpha and beta values passed from one step the next.\n// For simplicity, this kernel currently only supports U <= maxThread, which should be the common\n// case. For cases where U > maxThread, the vanilla kernel is used as a fallback option.\n\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// Forward (alpha) and backward (beta) path are launched together. Input is assumed to be converted\n// into log scale by the preceding log_softmax layer\n// Diagonal wavefront advancing usually used in dynamic programming is leveraged here.\n// alpha and beta are of acc_t type, as they are essentially accumulators.\n\n// This loss function supports packed input where a tensor of shape [B, T, U, H] is packed into\n// [B_packed, H].\n// Don't-care region (t > audLen) or (u > txtLen) is removed.\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t, int batchLdSize>\n__global__ void transducer_loss_batch_load_forward(const scalar_t* x, const int* label, const int* audLen,\n                                                   const int* txtLen, const int64_t* batchOffset, int64_t dictSize,\n                                                   int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                                   acc_t* alpha, acc_t* beta, scalar_t* loss) {\n  const int batch = blockIdx.y;\n  int u = threadIdx.x;\n  const auto myFLen = audLen[batch];\n  const auto myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  const scalar_t* myX = x + myBatchOffset * dictSize;\n  scalar_t next[batchLdSize], current[batchLdSize];\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  if (blockIdx.x == 0) {\n    // alpha path\n    acc_t* myAlpha = alpha + batch * maxFLen * maxGLen;\n    // two SMEM regions for double buffering read and write data to avoid data race\n    acc_t* const sharedAlpha[2] = {smem, smem + maxGLen};\n\n    sharedAlpha[0][u] = 0;\n    __syncthreads();\n\n    if (u == 0) myAlpha[0] = 0;\n\n    auto myAlphaLabel = (u == 0) ? 0 : label[batch * (maxGLen - 1) + u - 1];\n    // register used to pass value to the next step for the same thread\n    acc_t prvStepAlpha = 0;\n    for (int64_t step = 1; step < myFLen + myGLen - 1 + batchLdSize; step += batchLdSize) {\n// Move along the diagonal wavefront to leverage available parallelism\n// Batch loading X through loop unrolling\n#pragma unroll\n      for (int i = 0; i < batchLdSize; ++i) {\n        if (step + i < myFLen + myGLen - 1) {\n          // index computing\n          int64_t t = step + i - u;\n          int64_t currentId = ((t - 1) * myStrideT + u) * dictSize + blankIdx;\n          int64_t nextId = (t * myStrideT + u - 1) * dictSize + myAlphaLabel;\n          // main loading loop\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            if (u == 0) {\n              current[i] = myX[currentId];\n            } else if (t == 0) {\n              next[i] = myX[nextId];\n            } else {\n              current[i] = myX[currentId];\n              next[i] = myX[nextId];\n            }\n          }\n        }\n      }\n      // main computing loop\n      for (int i = 0; i < batchLdSize; ++i) {\n        // swap the pointer for double buffering\n        auto sharedAlphaRd = sharedAlpha[(step + i - 1) % 2];\n        auto sharedAlphaWr = sharedAlpha[(step + i) % 2];\n        if (step + i < myFLen + myGLen - 1) {\n          int64_t t = step + i - u;\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            // Eq(16) in [1]\n            if (u == 0)\n              prvStepAlpha = prvStepAlpha + current[i];\n            else if (t == 0)\n              prvStepAlpha = sharedAlphaRd[u - 1] + next[i];\n            else\n              prvStepAlpha = logSumExp(prvStepAlpha + current[i], sharedAlphaRd[u - 1] + next[i]);\n            sharedAlphaWr[u] = prvStepAlpha;\n            myAlpha[t * maxGLen + u] = prvStepAlpha;\n          }\n        }\n        __syncthreads();\n      }\n    }\n  } else if (blockIdx.x == 1) {\n    // beta path\n    acc_t* myBeta = beta + batch * maxFLen * maxGLen;\n    // two SMEM regions for double buffering read and write data to avoid data race\n    acc_t* const sharedBeta[2] = {smem, smem + maxGLen};\n    sharedBeta[0][u] = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    __syncthreads();\n\n    auto myBetaLabel = (u == maxGLen - 1) ? 0 : label[batch * (maxGLen - 1) + u];\n    // register used to pass value to the next step for the same thread\n    acc_t prvStepBeta = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    if (u == 0) myBeta[(myFLen - 1) * maxGLen + myGLen - 1] = prvStepBeta;\n\n    for (int64_t step = 1; step < myFLen + myGLen - 1; step += batchLdSize) {\n// Move along the diagonal wavefront to leverage available parallelism\n// Batch loading X\n#pragma unroll\n      for (int i = 0; i < batchLdSize; ++i) {\n        if (step + i < myFLen + myGLen - 1) {\n          // index computing\n          int64_t t = myFLen + myGLen - (step + i) - 2 - u;\n          int64_t currentId = (t * myStrideT + u) * dictSize + blankIdx;\n          int64_t nextId = (t * myStrideT + u) * dictSize + myBetaLabel;\n          // main loading loop\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            if (u == myGLen - 1) {\n              current[i] = myX[currentId];\n            } else if (t == myFLen - 1) {\n              next[i] = myX[nextId];\n            } else {\n              current[i] = myX[currentId];\n              next[i] = myX[nextId];\n            }\n          }\n        }\n      }\n      // main computing loop\n      for (int i = 0; i < batchLdSize; ++i) {\n        // swap the pointer for double buffering\n        auto sharedBetaRd = sharedBeta[(step + i - 1) % 2];\n        auto sharedBetaWr = sharedBeta[(step + i) % 2];\n        if (step + i < myFLen + myGLen - 1) {\n          int64_t t = myFLen + myGLen - (step + i) - 2 - u;\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            // Eq(18) in [1]\n            if (u == myGLen - 1)\n              prvStepBeta = prvStepBeta + current[i];\n            else if (t == myFLen - 1)\n              prvStepBeta = sharedBetaRd[u + 1] + next[i];\n            else\n              prvStepBeta = logSumExp(prvStepBeta + current[i], sharedBetaRd[u + 1] + next[i]);\n            sharedBetaWr[u] = prvStepBeta;\n            myBeta[t * maxGLen + u] = prvStepBeta;\n          }\n        }\n        __syncthreads();\n      }\n    }\n    if (u == 0) loss[batch] = -prvStepBeta;\n  }\n}\n\n// Vanilla transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// For this backward kernel, bwd op for the preceding softmax is assumed to be handled elsewhere,\n// hence only Eq(20) in [1] is implemented in this kernel.\n\n// Each thread block works on [batch, t, :, :] of data. Each thread works on a specific u at a time\n// Since only gradients for the correct token and null token need to be updated, gradients at other\n// locations are initialized to 0.\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                         const int* txtLen, const int* label, const acc_t* alpha, const acc_t* beta,\n                                         const int64_t* batchOffset, int64_t dictSize, int64_t blankIdx,\n                                         int64_t maxFLen, int64_t maxGLen, bool packedInput, scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int t = blockIdx.x;\n  const int batch = blockIdx.y;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  auto myX = x + (myBatchOffset + t * myStrideT) * dictSize;\n  auto myAlpha = alpha + batch * maxFLen * maxGLen;\n  auto myBeta = beta + batch * maxFLen * maxGLen;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT) * dictSize;\n  auto myLabel = label + batch * (maxGLen - 1);\n\n  int64_t u = tid;\n  while (t < myFLen and u < myGLen) {\n    // Do the update\n    // loss = -ln(Pr(y*|x))\n    acc_t grad = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n    if (u != myGLen - 1)\n      myXGrad[u * dictSize + myLabel[u]] =\n          -std::exp(grad + myBeta[t * maxGLen + u + 1] + myX[u * dictSize + myLabel[u]]);\n    if (t == myFLen - 1 and u == myGLen - 1)\n      myXGrad[u * dictSize + blankIdx] = -std::exp(grad + myX[u * dictSize + blankIdx]);\n    else if (t != myFLen - 1)\n      myXGrad[u * dictSize + blankIdx] = -std::exp(grad + myBeta[(t + 1) * maxGLen + u] + myX[u * dictSize + blankIdx]);\n\n    u += blockDim.x;\n  }\n}\n\n// Fused transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// The bwd op of the preceding softmax layer is fused in this kernel.\n// Each thread block works on [batch, t, u, :] of data. Each thread works on a specific h at a time\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_fused_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                               const int* txtLen, const int* label, const acc_t* alpha,\n                                               const acc_t* beta, const int64_t* batchOffset, int64_t dictSize,\n                                               int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                               scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int u = blockIdx.x;\n  const int t = blockIdx.y;\n  const int batch = blockIdx.z;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n\n  __shared__ acc_t commonFactor, myBetaTU, myBetaTUp1, myBetaTp1U, myLabelShared;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT + u) * dictSize;\n\n  if (t < myFLen and u < myGLen) {\n    auto myX = x + (myBatchOffset + t * myStrideT + u) * dictSize;\n    auto myAlpha = alpha + batch * maxFLen * maxGLen;\n    auto myBeta = beta + batch * maxFLen * maxGLen;\n    auto myLabel = label + batch * (maxGLen - 1);\n\n    // load and store shared variables in SMEM\n    if (tid == 0) {\n      commonFactor = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n      myBetaTU = myBeta[t * maxGLen + u];\n      myBetaTUp1 = myBeta[t * maxGLen + u + 1];\n      myBetaTp1U = myBeta[(t + 1) * maxGLen + u];\n      myLabelShared = myLabel[u];\n    }\n\n    __syncthreads();\n\n    for (int64_t h = tid; h < dictSize; h += blockDim.x) {\n      // Do the update\n      acc_t grad = commonFactor + myX[h];  // loss = -ln(Pr(y*|x))\n      acc_t myGrad = std::exp(grad + myBetaTU);\n      if (u != myGLen - 1 and h == myLabelShared) {\n        myGrad -= std::exp(grad + myBetaTUp1);\n      } else if (h == blankIdx) {\n        if (t == myFLen - 1 and u == myGLen - 1)\n          myGrad -= std::exp(grad);\n        else if (t != myFLen - 1)\n          myGrad -= std::exp(grad + myBetaTp1U);\n      }\n      myXGrad[h] = myGrad;\n    }\n  } else if (!packedInput) {\n    // In non-pack mode, need to make sure the gradients for don't-care regions are zero.\n    for (int64_t h = tid; h < dictSize; h += blockDim.x) {\n      myXGrad[h] = 0;\n    }\n  }\n}\n\n// Vectorized version of fused transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// The bwd op of the preceding softmax layer is fused in this kernel.\n// Each thread block works on [batch, t, u, :] of data. Each thread works on a specific h at a time\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V>\n__global__ void transducer_loss_fused_vec_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                                   const int* txtLen, const int* label, const acc_t* alpha,\n                                                   const acc_t* beta, const int64_t* batchOffset, int64_t dictSize,\n                                                   int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                                   scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int u = blockIdx.x;\n  const int t = blockIdx.y;\n  const int batch = blockIdx.z;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n\n  __shared__ acc_t commonFactor, myBetaTU, myBetaTUp1, myBetaTp1U, myLabelShared;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT + u) * dictSize;\n  auto myX = x + (myBatchOffset + t * myStrideT + u) * dictSize;\n  auto myAlpha = alpha + batch * maxFLen * maxGLen;\n  auto myBeta = beta + batch * maxFLen * maxGLen;\n  auto myLabel = label + batch * (maxGLen - 1);\n\n  // Variabels for vectorization\n  scalar_t myXBuffer[V], myXGradBuffer[V];\n  auto myXVec = reinterpret_cast<vec_t const*>(myX);\n  auto myXGradVec = reinterpret_cast<vec_t*>(myXGrad);\n  auto myXBufferVec = reinterpret_cast<vec_t*>(myXBuffer);\n  auto myXGradBufferVec = reinterpret_cast<vec_t*>(myXGradBuffer);\n  if (t < myFLen and u < myGLen) {\n    // load and store shared variables in SMEM\n    if (tid == 0) {\n      commonFactor = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n      myBetaTU = myBeta[t * maxGLen + u];\n      if (t != myFLen - 1) myBetaTp1U = myBeta[(t + 1) * maxGLen + u];\n      if (u != myGLen - 1) {\n        myBetaTUp1 = myBeta[t * maxGLen + u + 1];\n        myLabelShared = myLabel[u];\n      }\n    }\n\n    __syncthreads();\n\n#pragma unroll\n    for (int64_t h0 = tid * V; h0 < dictSize; h0 += blockDim.x * V) {\n      // Load myX in a vector form\n      *myXBufferVec = myXVec[h0 / V];\n// Do the update for a vector of input\n#pragma unroll\n      for (int i = 0; i < V; ++i) {\n        auto h = h0 + i;\n        acc_t grad = commonFactor + myXBuffer[i];  // loss = -ln(Pr(y*|x))\n        acc_t myGrad = std::exp(grad + myBetaTU);\n        if (u != myGLen - 1 and h == myLabelShared) {\n          myGrad -= std::exp(grad + myBetaTUp1);\n        } else if (h == blankIdx) {\n          if (t == myFLen - 1 and u == myGLen - 1)\n            myGrad -= std::exp(grad);\n          else if (t != myFLen - 1)\n            myGrad -= std::exp(grad + myBetaTp1U);\n        }\n        myXGradBuffer[i] = myGrad;\n      }\n\n      // Store myXGrad in a vector form\n      myXGradVec[h0 / V] = *myXGradBufferVec;\n    }\n  } else if (!packedInput) {\n    // In non-pack mode, need to make sure the gradients for don't-care regions are zero.\n    for (int64_t h0 = tid * V; h0 < dictSize; h0 += blockDim.x * V) {\n      myXGradVec[h0 / V] = 0;\n    }\n  }\n}\n\nstd::vector<torch::Tensor> transducer_loss_cuda_forward(torch::Tensor x, torch::Tensor label, torch::Tensor audLen,\n                                                        torch::Tensor txtLen, torch::Tensor batchOffset, int maxFLen,\n                                                        int blankIdx, int opt, bool packedInput) {\n  auto scalarType = x.scalar_type();\n  auto tensorOpt = x.options();\n  const int batchSize = label.size(0);\n  const int maxGLen = label.size(1) + 1;\n  const int dictSize = x.size(-1);\n\n  TORCH_CHECK(blankIdx >= 0 and blankIdx < dictSize, \"Expected blank index to be in the range of 0 to \", dictSize - 1,\n              \", but got \", blankIdx);\n  TORCH_CHECK(opt == -1 or opt == 0 or opt == 1, \"Got an invalid optimization level \", opt);\n\n  // The data type of alpha and beta will be resolved at dispatch time,\n  // hence defined here and assigned later\n  torch::Tensor alpha;\n  torch::Tensor beta;\n  torch::Tensor loss = torch::empty({batchSize}, tensorOpt);\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const auto maxThreadPerBlock = deviceProperties->maxThreadsPerBlock;\n  const auto maxSmemPerBlock = deviceProperties->sharedMemPerBlock;\n  const auto batchOffsetPtr = packedInput ? batchOffset.data_ptr<int64_t>() : nullptr;\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n      scalarType, \"transducer_loss_cuda_forward\", ([&] {\n        // resolve accumulation type\n        using acc_t = at::acc_type<scalar_t, true>;\n        auto accType = c10::CppTypeToScalarType<acc_t>::value;\n        auto accTensorOpt = tensorOpt.dtype(accType);\n        alpha = torch::empty({batchSize, maxFLen, maxGLen}, accTensorOpt);\n        beta = torch::empty({batchSize, maxFLen, maxGLen}, accTensorOpt);\n\n        // decide what kernel to launch based on the problem size\n        // if the required SMEM size or number threads exceeds the limit, fall back to the vanilla\n        // kernel.\n        const auto smemSize = 2 * maxGLen * sizeof(acc_t);\n        const auto optFallBack = (maxGLen > maxThreadPerBlock or smemSize > maxSmemPerBlock) ? 0\n                                 : (opt == -1)                                               ? 1\n                                                                                             : opt;\n        const int threads = std::min(maxThreadPerBlock, maxGLen);\n        const dim3 blocks(2, batchSize, 1);\n\n        if (optFallBack == 0)\n          transducer_loss_forward<<<blocks, threads, 0, stream>>>(\n              x.data_ptr<scalar_t>(), label.data_ptr<int>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n              batchOffsetPtr, dictSize, blankIdx, maxFLen, maxGLen, packedInput, alpha.data_ptr<acc_t>(),\n              beta.data_ptr<acc_t>(), loss.data_ptr<scalar_t>());\n        else if (optFallBack == 1)\n          transducer_loss_batch_load_forward<scalar_t, acc_t, 4><<<blocks, threads, smemSize, stream>>>(\n              x.data_ptr<scalar_t>(), label.data_ptr<int>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n              batchOffsetPtr, dictSize, blankIdx, maxFLen, maxGLen, packedInput, alpha.data_ptr<acc_t>(),\n              beta.data_ptr<acc_t>(), loss.data_ptr<scalar_t>());\n      }));\n  C10_CUDA_CHECK(cudaGetLastError());\n\n  return {alpha, beta, loss};\n}\n\ntorch::Tensor transducer_loss_cuda_backward(torch::Tensor x, torch::Tensor lossGrad, torch::Tensor alpha,\n                                            torch::Tensor beta, torch::Tensor audLen, torch::Tensor txtLen,\n                                            torch::Tensor label, torch::Tensor batchOffset, int maxFLen, int blankIdx,\n                                            int opt, bool fuseSoftmaxBackward, bool packedInput) {\n  auto dtype = x.scalar_type();\n  torch::Tensor xGrad;\n  const int batchSize = label.size(0);\n  const int maxGLen = label.size(1) + 1;\n  const int dictSize = x.size(-1);\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const int maxThreadPerBlock = deviceProperties->maxThreadsPerBlock;\n  const int warpSize = deviceProperties->warpSize;\n  const auto batchOffsetPtr = packedInput ? batchOffset.data_ptr<int64_t>() : nullptr;\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fuseSoftmaxBackward) {\n    // alloc empty tensors for performance, hence need to ensure zeros are writtern to\n    // don't-care region in the kernel.\n    xGrad = torch::empty_like(x);\n\n    // Would like each thread to work on 4 hidden units\n    const int workPerThread = 4;\n    // Don't want to have more than 128 threads per thread block\n    const int maxThreadPerElmt = std::min(128, maxThreadPerBlock);\n    const int threads = std::min(maxThreadPerElmt, std::max(warpSize, (dictSize + workPerThread - 1) / workPerThread));\n    const dim3 blocks(maxGLen, maxFLen, batchSize);\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_loss_cuda_backward\", ([&] {\n          using vec_t = uint64_t;\n          using acc_t = at::acc_type<scalar_t, true>;\n          constexpr int vectFactor = sizeof(vec_t) / sizeof(scalar_t);\n          constexpr int vecAlignment = std::alignment_of<vec_t>::value;\n          // if all input and output tensors meet the alignment requirement\n          bool memAlign = reinterpret_cast<uint64_t>(x.data_ptr<scalar_t>()) % vecAlignment == 0 and\n                          reinterpret_cast<uint64_t>(xGrad.data_ptr<scalar_t>()) % vecAlignment == 0;\n\n          if (vectFactor > 1 and dictSize % vectFactor == 0 and memAlign) {\n            transducer_loss_fused_vec_backward<scalar_t, acc_t, vec_t, vectFactor><<<blocks, threads, 0, stream>>>(\n                x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n                label.data_ptr<int>(), alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n          } else {\n            transducer_loss_fused_backward<<<blocks, threads, 0, stream>>>(\n                x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n                label.data_ptr<int>(), alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n          }\n        }));\n  } else {\n    // for non-fused kernel, the gradients need to be writtern are very sparse, hence initialize\n    // the tensor with all zeros.\n    xGrad = torch::zeros_like(x);\n    // don't launch more threads than needed.\n    const int threads = std::min(maxThreadPerBlock, maxGLen);\n    const dim3 blocks(maxFLen, batchSize);\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(dtype, \"transducer_loss_cuda_backward\", ([&] {\n                                          using acc_t = at::acc_type<scalar_t, true>;\n                                          transducer_loss_backward<<<blocks, threads, 0, stream>>>(\n                                              x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(),\n                                              audLen.data_ptr<int>(), txtLen.data_ptr<int>(), label.data_ptr<int>(),\n                                              alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                                              blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n                                        }));\n  }\n  C10_CUDA_CHECK(cudaGetLastError());\n\n  return xGrad;\n}\n", "output": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <hip/hip_runtime.h>\n#include <torch/extension.h>\n\n#include <vector>\n\ntemplate <typename scalar_t>\n__device__ __forceinline__ scalar_t logSumExp(scalar_t a, scalar_t b) {\n  // standard log-sum-exp trick is used here to provide better numerical stability\n  return (a >= b) ? a + std::log1p(exp(b - a)) : b + std::log1p(exp(a - b));\n}\n\n// Vanilla transducer loss function (i.e. forward-backward algorithm)\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n\n// Forward (alpha) and backward (beta) path are launched together. Input is assumed to be converted\n// into log scale by the preceding log_softmax layer\n// Diagonal wavefront advancing usually used in dynamic programming is leveraged here.\n// alpha and beta are of acc_t type, as they are essentially accumulators.\n\n// This loss function supports packed input where a tensor of shape [B, T, U, H] is packed into\n// [B_packed, H].\n// Don't-care region (t > audLen) or (u > txtLen) is removed.\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_forward(const scalar_t* x, const int* label, const int* audLen, const int* txtLen,\n                                        const int64_t* batchOffset,\n                                        int64_t dictSize,  // 64-bit indexing for data tensor\n                                        int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                        acc_t* alpha, acc_t* beta, scalar_t* loss) {\n  const int batch = blockIdx.y;\n  const int tid = threadIdx.x;\n  const auto myFLen = audLen[batch];\n  // Note that start of the sentence is added as 1 here\n  const auto myGLen = txtLen[batch] + 1;\n  const auto myLabel = label + batch * (maxGLen - 1);\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  const scalar_t* myX = x + myBatchOffset * dictSize;\n  int u = tid;\n\n  if (blockIdx.x == 0) {\n    // alpha path\n    acc_t* myAlpha = alpha + batch * maxFLen * maxGLen;\n    if (u == 0) myAlpha[0] = 0;\n    __syncthreads();\n\n    for (int64_t step = 1; step < myFLen + myGLen - 1; ++step) {\n      // Move along the diagonal wavefront to leverage available parallelism\n      for (u = tid; u < myGLen; u += blockDim.x) {\n        int64_t t = step - u;\n        if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n          // Eq(16) in [1]\n          if (u == 0) {\n            // alpha(t, u) = alpha(t-1, u) * null(t-1, u)\n            myAlpha[t * maxGLen + u] = myAlpha[(t - 1) * maxGLen] + myX[((t - 1) * myStrideT) * dictSize + blankIdx];\n          } else if (t == 0) {\n            // alpha(t, u-1) = alpha(t, u-1) * y(t, u-1)\n            myAlpha[u] = myAlpha[u - 1] + myX[(u - 1) * dictSize + myLabel[u - 1]];\n          } else {\n            // alpha(t, u) = alpha(t-1, u) * null(t-1, u) + alpha(t, u-1) * y(t, u-1)\n            acc_t current = myAlpha[(t - 1) * maxGLen + u] + myX[((t - 1) * myStrideT + u) * dictSize + blankIdx];\n            acc_t next = myAlpha[t * maxGLen + u - 1] + myX[(t * myStrideT + u - 1) * dictSize + myLabel[u - 1]];\n            myAlpha[t * maxGLen + u] = logSumExp(next, current);\n          }\n        }\n      }\n      __syncthreads();\n    }\n  } else if (blockIdx.x == 1) {\n    // beta path\n    acc_t* myBeta = beta + batch * maxFLen * maxGLen;\n    if (u == 0) {\n      myBeta[(myFLen - 1) * maxGLen + myGLen - 1] = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    }\n    __syncthreads();\n\n    for (int64_t step = myFLen + myGLen - 3; step >= 0; --step) {\n      for (u = tid; u < myGLen; u += blockDim.x) {\n        int64_t t = step - u;\n        if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n          // Eq(18) in [1]\n          if (u == myGLen - 1) {\n            // beta(t, u) = beta(t+1, u) * null(t, u)\n            myBeta[t * maxGLen + u] = myBeta[(t + 1) * maxGLen + u] + myX[(t * myStrideT + u) * dictSize + blankIdx];\n          } else if (t == myFLen - 1) {\n            // beta(t, u) = beta(t, u+1) * y(t, u)\n            myBeta[t * maxGLen + u] = myBeta[t * maxGLen + u + 1] + myX[(t * myStrideT + u) * dictSize + myLabel[u]];\n          } else {\n            // beta(t, u) = beta(t+1, u)*null(t, u) + beta(t, u+1)*y(t, u)\n            acc_t current = myBeta[(t + 1) * maxGLen + u] + myX[(t * myStrideT + u) * dictSize + blankIdx];\n            acc_t next = myBeta[t * maxGLen + u + 1] + myX[(t * myStrideT + u) * dictSize + myLabel[u]];\n            myBeta[t * maxGLen + u] = logSumExp(next, current);\n          }\n        }\n      }\n      __syncthreads();\n    }\n    if (tid == 0) loss[batch] = -myBeta[0];\n  }\n}\n\n// transudcer loss function (i.e. forward-backward algorithm) with batch loading optimization.\n// Compared to the vanilla version, there are two optimizations:\n// 1. load x in batch through loop unrolling to reduce the latency.\n// 2. Use registers and shared memory to hold alpha and beta values passed from one step the next.\n// For simplicity, this kernel currently only supports U <= maxThread, which should be the common\n// case. For cases where U > maxThread, the vanilla kernel is used as a fallback option.\n\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// Forward (alpha) and backward (beta) path are launched together. Input is assumed to be converted\n// into log scale by the preceding log_softmax layer\n// Diagonal wavefront advancing usually used in dynamic programming is leveraged here.\n// alpha and beta are of acc_t type, as they are essentially accumulators.\n\n// This loss function supports packed input where a tensor of shape [B, T, U, H] is packed into\n// [B_packed, H].\n// Don't-care region (t > audLen) or (u > txtLen) is removed.\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t, int batchLdSize>\n__global__ void transducer_loss_batch_load_forward(const scalar_t* x, const int* label, const int* audLen,\n                                                   const int* txtLen, const int64_t* batchOffset, int64_t dictSize,\n                                                   int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                                   acc_t* alpha, acc_t* beta, scalar_t* loss) {\n  const int batch = blockIdx.y;\n  int u = threadIdx.x;\n  const auto myFLen = audLen[batch];\n  const auto myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  const scalar_t* myX = x + myBatchOffset * dictSize;\n  scalar_t next[batchLdSize], current[batchLdSize];\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  if (blockIdx.x == 0) {\n    // alpha path\n    acc_t* myAlpha = alpha + batch * maxFLen * maxGLen;\n    // two SMEM regions for double buffering read and write data to avoid data race\n    acc_t* const sharedAlpha[2] = {smem, smem + maxGLen};\n\n    sharedAlpha[0][u] = 0;\n    __syncthreads();\n\n    if (u == 0) myAlpha[0] = 0;\n\n    auto myAlphaLabel = (u == 0) ? 0 : label[batch * (maxGLen - 1) + u - 1];\n    // register used to pass value to the next step for the same thread\n    acc_t prvStepAlpha = 0;\n    for (int64_t step = 1; step < myFLen + myGLen - 1 + batchLdSize; step += batchLdSize) {\n// Move along the diagonal wavefront to leverage available parallelism\n// Batch loading X through loop unrolling\n#pragma unroll\n      for (int i = 0; i < batchLdSize; ++i) {\n        if (step + i < myFLen + myGLen - 1) {\n          // index computing\n          int64_t t = step + i - u;\n          int64_t currentId = ((t - 1) * myStrideT + u) * dictSize + blankIdx;\n          int64_t nextId = (t * myStrideT + u - 1) * dictSize + myAlphaLabel;\n          // main loading loop\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            if (u == 0) {\n              current[i] = myX[currentId];\n            } else if (t == 0) {\n              next[i] = myX[nextId];\n            } else {\n              current[i] = myX[currentId];\n              next[i] = myX[nextId];\n            }\n          }\n        }\n      }\n      // main computing loop\n      for (int i = 0; i < batchLdSize; ++i) {\n        // swap the pointer for double buffering\n        auto sharedAlphaRd = sharedAlpha[(step + i - 1) % 2];\n        auto sharedAlphaWr = sharedAlpha[(step + i) % 2];\n        if (step + i < myFLen + myGLen - 1) {\n          int64_t t = step + i - u;\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            // Eq(16) in [1]\n            if (u == 0)\n              prvStepAlpha = prvStepAlpha + current[i];\n            else if (t == 0)\n              prvStepAlpha = sharedAlphaRd[u - 1] + next[i];\n            else\n              prvStepAlpha = logSumExp(prvStepAlpha + current[i], sharedAlphaRd[u - 1] + next[i]);\n            sharedAlphaWr[u] = prvStepAlpha;\n            myAlpha[t * maxGLen + u] = prvStepAlpha;\n          }\n        }\n        __syncthreads();\n      }\n    }\n  } else if (blockIdx.x == 1) {\n    // beta path\n    acc_t* myBeta = beta + batch * maxFLen * maxGLen;\n    // two SMEM regions for double buffering read and write data to avoid data race\n    acc_t* const sharedBeta[2] = {smem, smem + maxGLen};\n    sharedBeta[0][u] = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    __syncthreads();\n\n    auto myBetaLabel = (u == maxGLen - 1) ? 0 : label[batch * (maxGLen - 1) + u];\n    // register used to pass value to the next step for the same thread\n    acc_t prvStepBeta = myX[((myFLen - 1) * myStrideT + myGLen - 1) * dictSize + blankIdx];\n    if (u == 0) myBeta[(myFLen - 1) * maxGLen + myGLen - 1] = prvStepBeta;\n\n    for (int64_t step = 1; step < myFLen + myGLen - 1; step += batchLdSize) {\n// Move along the diagonal wavefront to leverage available parallelism\n// Batch loading X\n#pragma unroll\n      for (int i = 0; i < batchLdSize; ++i) {\n        if (step + i < myFLen + myGLen - 1) {\n          // index computing\n          int64_t t = myFLen + myGLen - (step + i) - 2 - u;\n          int64_t currentId = (t * myStrideT + u) * dictSize + blankIdx;\n          int64_t nextId = (t * myStrideT + u) * dictSize + myBetaLabel;\n          // main loading loop\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            if (u == myGLen - 1) {\n              current[i] = myX[currentId];\n            } else if (t == myFLen - 1) {\n              next[i] = myX[nextId];\n            } else {\n              current[i] = myX[currentId];\n              next[i] = myX[nextId];\n            }\n          }\n        }\n      }\n      // main computing loop\n      for (int i = 0; i < batchLdSize; ++i) {\n        // swap the pointer for double buffering\n        auto sharedBetaRd = sharedBeta[(step + i - 1) % 2];\n        auto sharedBetaWr = sharedBeta[(step + i) % 2];\n        if (step + i < myFLen + myGLen - 1) {\n          int64_t t = myFLen + myGLen - (step + i) - 2 - u;\n          if (t >= 0 and t < myFLen and u >= 0 and u < myGLen) {\n            // Eq(18) in [1]\n            if (u == myGLen - 1)\n              prvStepBeta = prvStepBeta + current[i];\n            else if (t == myFLen - 1)\n              prvStepBeta = sharedBetaRd[u + 1] + next[i];\n            else\n              prvStepBeta = logSumExp(prvStepBeta + current[i], sharedBetaRd[u + 1] + next[i]);\n            sharedBetaWr[u] = prvStepBeta;\n            myBeta[t * maxGLen + u] = prvStepBeta;\n          }\n        }\n        __syncthreads();\n      }\n    }\n    if (u == 0) loss[batch] = -prvStepBeta;\n  }\n}\n\n// Vanilla transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// For this backward kernel, bwd op for the preceding softmax is assumed to be handled elsewhere,\n// hence only Eq(20) in [1] is implemented in this kernel.\n\n// Each thread block works on [batch, t, :, :] of data. Each thread works on a specific u at a time\n// Since only gradients for the correct token and null token need to be updated, gradients at other\n// locations are initialized to 0.\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                         const int* txtLen, const int* label, const acc_t* alpha, const acc_t* beta,\n                                         const int64_t* batchOffset, int64_t dictSize, int64_t blankIdx,\n                                         int64_t maxFLen, int64_t maxGLen, bool packedInput, scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int t = blockIdx.x;\n  const int batch = blockIdx.y;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n  auto myX = x + (myBatchOffset + t * myStrideT) * dictSize;\n  auto myAlpha = alpha + batch * maxFLen * maxGLen;\n  auto myBeta = beta + batch * maxFLen * maxGLen;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT) * dictSize;\n  auto myLabel = label + batch * (maxGLen - 1);\n\n  int64_t u = tid;\n  while (t < myFLen and u < myGLen) {\n    // Do the update\n    // loss = -ln(Pr(y*|x))\n    acc_t grad = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n    if (u != myGLen - 1)\n      myXGrad[u * dictSize + myLabel[u]] =\n          -std::exp(grad + myBeta[t * maxGLen + u + 1] + myX[u * dictSize + myLabel[u]]);\n    if (t == myFLen - 1 and u == myGLen - 1)\n      myXGrad[u * dictSize + blankIdx] = -std::exp(grad + myX[u * dictSize + blankIdx]);\n    else if (t != myFLen - 1)\n      myXGrad[u * dictSize + blankIdx] = -std::exp(grad + myBeta[(t + 1) * maxGLen + u] + myX[u * dictSize + blankIdx]);\n\n    u += blockDim.x;\n  }\n}\n\n// Fused transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// The bwd op of the preceding softmax layer is fused in this kernel.\n// Each thread block works on [batch, t, u, :] of data. Each thread works on a specific h at a time\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t>\n__global__ void transducer_loss_fused_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                               const int* txtLen, const int* label, const acc_t* alpha,\n                                               const acc_t* beta, const int64_t* batchOffset, int64_t dictSize,\n                                               int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                               scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int u = blockIdx.x;\n  const int t = blockIdx.y;\n  const int batch = blockIdx.z;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n\n  __shared__ acc_t commonFactor, myBetaTU, myBetaTUp1, myBetaTp1U, myLabelShared;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT + u) * dictSize;\n\n  if (t < myFLen and u < myGLen) {\n    auto myX = x + (myBatchOffset + t * myStrideT + u) * dictSize;\n    auto myAlpha = alpha + batch * maxFLen * maxGLen;\n    auto myBeta = beta + batch * maxFLen * maxGLen;\n    auto myLabel = label + batch * (maxGLen - 1);\n\n    // load and store shared variables in SMEM\n    if (tid == 0) {\n      commonFactor = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n      myBetaTU = myBeta[t * maxGLen + u];\n      myBetaTUp1 = myBeta[t * maxGLen + u + 1];\n      myBetaTp1U = myBeta[(t + 1) * maxGLen + u];\n      myLabelShared = myLabel[u];\n    }\n\n    __syncthreads();\n\n    for (int64_t h = tid; h < dictSize; h += blockDim.x) {\n      // Do the update\n      acc_t grad = commonFactor + myX[h];  // loss = -ln(Pr(y*|x))\n      acc_t myGrad = std::exp(grad + myBetaTU);\n      if (u != myGLen - 1 and h == myLabelShared) {\n        myGrad -= std::exp(grad + myBetaTUp1);\n      } else if (h == blankIdx) {\n        if (t == myFLen - 1 and u == myGLen - 1)\n          myGrad -= std::exp(grad);\n        else if (t != myFLen - 1)\n          myGrad -= std::exp(grad + myBetaTp1U);\n      }\n      myXGrad[h] = myGrad;\n    }\n  } else if (!packedInput) {\n    // In non-pack mode, need to make sure the gradients for don't-care regions are zero.\n    for (int64_t h = tid; h < dictSize; h += blockDim.x) {\n      myXGrad[h] = 0;\n    }\n  }\n}\n\n// Vectorized version of fused transudcer loss backward operation.\n// Detail of this loss function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n// The bwd op of the preceding softmax layer is fused in this kernel.\n// Each thread block works on [batch, t, u, :] of data. Each thread works on a specific h at a time\n\n// To support the packed input, the starting offsets for each batch need to be specified with\n// batchOffset.\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V>\n__global__ void transducer_loss_fused_vec_backward(const scalar_t* x, const scalar_t* lossGrad, const int* audLen,\n                                                   const int* txtLen, const int* label, const acc_t* alpha,\n                                                   const acc_t* beta, const int64_t* batchOffset, int64_t dictSize,\n                                                   int64_t blankIdx, int64_t maxFLen, int64_t maxGLen, bool packedInput,\n                                                   scalar_t* xGrad) {\n  const int tid = threadIdx.x;\n  const int u = blockIdx.x;\n  const int t = blockIdx.y;\n  const int batch = blockIdx.z;\n  const int64_t myFLen = audLen[batch];\n  const int64_t myGLen = txtLen[batch] + 1;\n  const int64_t myBatchOffset = packedInput ? (batch == 0 ? 0 : batchOffset[batch - 1]) : batch * maxFLen * maxGLen;\n  const int64_t myStrideT = packedInput ? myGLen : maxGLen;\n\n  __shared__ acc_t commonFactor, myBetaTU, myBetaTUp1, myBetaTp1U, myLabelShared;\n  auto myXGrad = xGrad + (myBatchOffset + t * myStrideT + u) * dictSize;\n  auto myX = x + (myBatchOffset + t * myStrideT + u) * dictSize;\n  auto myAlpha = alpha + batch * maxFLen * maxGLen;\n  auto myBeta = beta + batch * maxFLen * maxGLen;\n  auto myLabel = label + batch * (maxGLen - 1);\n\n  // Variabels for vectorization\n  scalar_t myXBuffer[V], myXGradBuffer[V];\n  auto myXVec = reinterpret_cast<vec_t const*>(myX);\n  auto myXGradVec = reinterpret_cast<vec_t*>(myXGrad);\n  auto myXBufferVec = reinterpret_cast<vec_t*>(myXBuffer);\n  auto myXGradBufferVec = reinterpret_cast<vec_t*>(myXGradBuffer);\n  if (t < myFLen and u < myGLen) {\n    // load and store shared variables in SMEM\n    if (tid == 0) {\n      commonFactor = std::log(lossGrad[batch]) + myAlpha[t * maxGLen + u] - myBeta[0];\n      myBetaTU = myBeta[t * maxGLen + u];\n      if (t != myFLen - 1) myBetaTp1U = myBeta[(t + 1) * maxGLen + u];\n      if (u != myGLen - 1) {\n        myBetaTUp1 = myBeta[t * maxGLen + u + 1];\n        myLabelShared = myLabel[u];\n      }\n    }\n\n    __syncthreads();\n\n#pragma unroll\n    for (int64_t h0 = tid * V; h0 < dictSize; h0 += blockDim.x * V) {\n      // Load myX in a vector form\n      *myXBufferVec = myXVec[h0 / V];\n// Do the update for a vector of input\n#pragma unroll\n      for (int i = 0; i < V; ++i) {\n        auto h = h0 + i;\n        acc_t grad = commonFactor + myXBuffer[i];  // loss = -ln(Pr(y*|x))\n        acc_t myGrad = std::exp(grad + myBetaTU);\n        if (u != myGLen - 1 and h == myLabelShared) {\n          myGrad -= std::exp(grad + myBetaTUp1);\n        } else if (h == blankIdx) {\n          if (t == myFLen - 1 and u == myGLen - 1)\n            myGrad -= std::exp(grad);\n          else if (t != myFLen - 1)\n            myGrad -= std::exp(grad + myBetaTp1U);\n        }\n        myXGradBuffer[i] = myGrad;\n      }\n\n      // Store myXGrad in a vector form\n      myXGradVec[h0 / V] = *myXGradBufferVec;\n    }\n  } else if (!packedInput) {\n    // In non-pack mode, need to make sure the gradients for don't-care regions are zero.\n    for (int64_t h0 = tid * V; h0 < dictSize; h0 += blockDim.x * V) {\n      myXGradVec[h0 / V] = 0;\n    }\n  }\n}\n\nstd::vector<torch::Tensor> transducer_loss_cuda_forward(torch::Tensor x, torch::Tensor label, torch::Tensor audLen,\n                                                        torch::Tensor txtLen, torch::Tensor batchOffset, int maxFLen,\n                                                        int blankIdx, int opt, bool packedInput) {\n  auto scalarType = x.scalar_type();\n  auto tensorOpt = x.options();\n  const int batchSize = label.size(0);\n  const int maxGLen = label.size(1) + 1;\n  const int dictSize = x.size(-1);\n\n  TORCH_CHECK(blankIdx >= 0 and blankIdx < dictSize, \"Expected blank index to be in the range of 0 to \", dictSize - 1,\n              \", but got \", blankIdx);\n  TORCH_CHECK(opt == -1 or opt == 0 or opt == 1, \"Got an invalid optimization level \", opt);\n\n  // The data type of alpha and beta will be resolved at dispatch time,\n  // hence defined here and assigned later\n  torch::Tensor alpha;\n  torch::Tensor beta;\n  torch::Tensor loss = torch::empty({batchSize}, tensorOpt);\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const auto maxThreadPerBlock = deviceProperties->maxThreadsPerBlock;\n  const auto maxSmemPerBlock = deviceProperties->sharedMemPerBlock;\n  const auto batchOffsetPtr = packedInput ? batchOffset.data_ptr<int64_t>() : nullptr;\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n      scalarType, \"transducer_loss_cuda_forward\", ([&] {\n        // resolve accumulation type\n        using acc_t = at::acc_type<scalar_t, true>;\n        auto accType = c10::CppTypeToScalarType<acc_t>::value;\n        auto accTensorOpt = tensorOpt.dtype(accType);\n        alpha = torch::empty({batchSize, maxFLen, maxGLen}, accTensorOpt);\n        beta = torch::empty({batchSize, maxFLen, maxGLen}, accTensorOpt);\n\n        // decide what kernel to launch based on the problem size\n        // if the required SMEM size or number threads exceeds the limit, fall back to the vanilla\n        // kernel.\n        const auto smemSize = 2 * maxGLen * sizeof(acc_t);\n        const auto optFallBack = (maxGLen > maxThreadPerBlock or smemSize > maxSmemPerBlock) ? 0\n                                 : (opt == -1)                                               ? 1\n                                                                                             : opt;\n        const int threads = std::min(maxThreadPerBlock, maxGLen);\n        const dim3 blocks(2, batchSize, 1);\n\n        if (optFallBack == 0)\n          transducer_loss_forward<<<blocks, threads, 0, stream>>>(\n              x.data_ptr<scalar_t>(), label.data_ptr<int>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n              batchOffsetPtr, dictSize, blankIdx, maxFLen, maxGLen, packedInput, alpha.data_ptr<acc_t>(),\n              beta.data_ptr<acc_t>(), loss.data_ptr<scalar_t>());\n        else if (optFallBack == 1)\n          transducer_loss_batch_load_forward<scalar_t, acc_t, 4><<<blocks, threads, smemSize, stream>>>(\n              x.data_ptr<scalar_t>(), label.data_ptr<int>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n              batchOffsetPtr, dictSize, blankIdx, maxFLen, maxGLen, packedInput, alpha.data_ptr<acc_t>(),\n              beta.data_ptr<acc_t>(), loss.data_ptr<scalar_t>());\n      }));\n  C10_CUDA_CHECK(hipGetLastError());\n\n  return {alpha, beta, loss};\n}\n\ntorch::Tensor transducer_loss_cuda_backward(torch::Tensor x, torch::Tensor lossGrad, torch::Tensor alpha,\n                                            torch::Tensor beta, torch::Tensor audLen, torch::Tensor txtLen,\n                                            torch::Tensor label, torch::Tensor batchOffset, int maxFLen, int blankIdx,\n                                            int opt, bool fuseSoftmaxBackward, bool packedInput) {\n  auto dtype = x.scalar_type();\n  torch::Tensor xGrad;\n  const int batchSize = label.size(0);\n  const int maxGLen = label.size(1) + 1;\n  const int dictSize = x.size(-1);\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const int maxThreadPerBlock = deviceProperties->maxThreadsPerBlock;\n  const int warpSize = deviceProperties->warpSize;\n  const auto batchOffsetPtr = packedInput ? batchOffset.data_ptr<int64_t>() : nullptr;\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fuseSoftmaxBackward) {\n    // alloc empty tensors for performance, hence need to ensure zeros are writtern to\n    // don't-care region in the kernel.\n    xGrad = torch::empty_like(x);\n\n    // Would like each thread to work on 4 hidden units\n    const int workPerThread = 4;\n    // Don't want to have more than 128 threads per thread block\n    const int maxThreadPerElmt = std::min(128, maxThreadPerBlock);\n    const int threads = std::min(maxThreadPerElmt, std::max(warpSize, (dictSize + workPerThread - 1) / workPerThread));\n    const dim3 blocks(maxGLen, maxFLen, batchSize);\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_loss_cuda_backward\", ([&] {\n          using vec_t = uint64_t;\n          using acc_t = at::acc_type<scalar_t, true>;\n          constexpr int vectFactor = sizeof(vec_t) / sizeof(scalar_t);\n          constexpr int vecAlignment = std::alignment_of<vec_t>::value;\n          // if all input and output tensors meet the alignment requirement\n          bool memAlign = reinterpret_cast<uint64_t>(x.data_ptr<scalar_t>()) % vecAlignment == 0 and\n                          reinterpret_cast<uint64_t>(xGrad.data_ptr<scalar_t>()) % vecAlignment == 0;\n\n          if (vectFactor > 1 and dictSize % vectFactor == 0 and memAlign) {\n            transducer_loss_fused_vec_backward<scalar_t, acc_t, vec_t, vectFactor><<<blocks, threads, 0, stream>>>(\n                x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n                label.data_ptr<int>(), alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n          } else {\n            transducer_loss_fused_backward<<<blocks, threads, 0, stream>>>(\n                x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(), audLen.data_ptr<int>(), txtLen.data_ptr<int>(),\n                label.data_ptr<int>(), alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n          }\n        }));\n  } else {\n    // for non-fused kernel, the gradients need to be writtern are very sparse, hence initialize\n    // the tensor with all zeros.\n    xGrad = torch::zeros_like(x);\n    // don't launch more threads than needed.\n    const int threads = std::min(maxThreadPerBlock, maxGLen);\n    const dim3 blocks(maxFLen, batchSize);\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(dtype, \"transducer_loss_cuda_backward\", ([&] {\n                                          using acc_t = at::acc_type<scalar_t, true>;\n                                          transducer_loss_backward<<<blocks, threads, 0, stream>>>(\n                                              x.data_ptr<scalar_t>(), lossGrad.data_ptr<scalar_t>(),\n                                              audLen.data_ptr<int>(), txtLen.data_ptr<int>(), label.data_ptr<int>(),\n                                              alpha.data_ptr<acc_t>(), beta.data_ptr<acc_t>(), batchOffsetPtr, dictSize,\n                                              blankIdx, maxFLen, maxGLen, packedInput, xGrad.data_ptr<scalar_t>());\n                                        }));\n  }\n  C10_CUDA_CHECK(hipGetLastError());\n\n  return xGrad;\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/transducer/transducer_loss_kernel.cu"}
{"input": "#include <ATen/AccumulateType.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <curand_kernel.h>\n#include <torch/extension.h>\n\n#ifdef OLD_GENERATOR_PATH\n#include <ATen/CUDAGeneratorImpl.h>\n#else\n#include <ATen/cuda/CUDAGeneratorImpl.h>\n#endif\n\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/macros/Macros.h>\n\n#include <ATen/cuda/CUDAGraphsUtils.cuh>\n\n#include \"philox.cuh\"\n\n// Warp reduce kernels to reduce N groups of data into N numbers, where N = warpSize / width.\n// width should be a power of 2 and should be less than warpSize.\ntemplate <typename scalar_t>\n__device__ __forceinline__ scalar_t warpReduce(scalar_t x, int width = C10_WARP_SIZE) {\n  for (unsigned offset = width / 2; offset > 0; offset /= 2) {\n    x += __shfl_down_sync(0xffffffff, x, offset, width);\n  }\n  return x;\n}\n\ninline int largestPowerOfTwo(int x) {\n  int y = 1;\n  while (y <= x) y <<= 1;\n  return y >> 1;\n}\n\n/*\nFigure out vectorization type for masks.\nSimilar to how PyTorch figures out acc_t here:\naten/src/ATen/AccumulateType.h\n*/\ntemplate <int V>\nstruct MaskVecType {};\n\ntemplate <>\nstruct MaskVecType<1> {\n  using type = uint8_t;\n};\ntemplate <>\nstruct MaskVecType<2> {\n  using type = uint16_t;\n};\ntemplate <>\nstruct MaskVecType<4> {\n  using type = uint32_t;\n};\n\ntemplate <int V>\nusing mvec_type = typename MaskVecType<V>::type;\n\n// Helper class to calculate pointer offset that can be shared by different flavors of kernels.\n// For fwd, batch offset and stride are different for packing and non-packing mode.\nstruct OffsetCalFwd {\n  __device__ __forceinline__ OffsetCalFwd(int64_t batch, const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                          int64_t gLen, int64_t hiddenSize, bool packOutput)\n      : batch(batch),\n        batchOffset(batchOffset),\n        maxFLen(maxFLen),\n        maxGLen(maxGLen),\n        gLen(gLen),\n        hiddenSize(hiddenSize),\n        packOutput(packOutput) {}\n\n  int64_t batch;\n  const int64_t* batchOffset;\n  int64_t maxFLen;\n  int64_t maxGLen;\n  int64_t gLen;\n  int64_t hiddenSize;\n  bool packOutput;\n\n  __device__ __forceinline__ int64_t getBatchOffset() {\n    return packOutput ? ((batch == 0) ? 0 : batchOffset[batch - 1]) * hiddenSize\n                      : batch * maxFLen * maxGLen * hiddenSize;\n  }\n\n  __device__ __forceinline__ int64_t getStrideF() { return packOutput ? gLen * hiddenSize : maxGLen * hiddenSize; }\n};\n\n// Helper class to calculate pointer offset that can be shared by different flavors of kernels\n// For bwd, batch offset and stride are different for packing and non-packing mode.\n// The reducion is done for two input tensors. Therefore, generating two sets of offsets\n// according to bwdFasterDim can lead to a unified implementation in the actual kernel.\nstruct OffsetCalBwd {\n  __device__ __forceinline__ OffsetCalBwd(int64_t batch, const int64_t* batchOffset, const int* fLen, const int* gLen,\n                                          int64_t maxFLen, int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                          bool bwdFasterDim)\n      : batch(batch),\n        batchOffset(batchOffset),\n        maxFLen(maxFLen),\n        maxGLen(maxGLen),\n        fLen(fLen),\n        gLen(gLen),\n        hiddenSize(hiddenSize),\n        packOutput(packOutput),\n        bwdFasterDim(bwdFasterDim) {}\n\n  int64_t batch;\n  const int64_t* batchOffset;\n  const int* fLen;\n  const int* gLen;\n  int64_t maxFLen;\n  int64_t maxGLen;\n  int64_t hiddenSize;\n  bool packOutput;\n  bool bwdFasterDim;  // whether doing bwd on the faster moving dimension\n\n  __device__ __forceinline__ int64_t getBatchOffset() {\n    return packOutput ? ((batch == 0) ? 0 : batchOffset[batch - 1]) * hiddenSize\n                      : batch * maxFLen * maxGLen * hiddenSize;\n  }\n\n  __device__ __forceinline__ int64_t getMaxXLen() { return bwdFasterDim ? maxGLen : maxFLen; }\n\n  __device__ __forceinline__ auto getMyXLen() -> decltype(gLen[batch]) {\n    return bwdFasterDim ? gLen[batch] : fLen[batch];\n  }\n\n  __device__ __forceinline__ auto getMyYLen() -> decltype(gLen[batch]) {\n    return bwdFasterDim ? fLen[batch] : gLen[batch];\n  }\n\n  __device__ __forceinline__ int64_t getStrideX() {\n    return bwdFasterDim ? hiddenSize : ((packOutput ? gLen[batch] : maxGLen) * hiddenSize);\n  }\n\n  __device__ __forceinline__ int64_t getStrideY() {\n    return bwdFasterDim ? ((packOutput ? gLen[batch] : maxGLen) * hiddenSize) : hiddenSize;\n  }\n};\n\n// Vanila transducer joint forward kernel\n// Detail of this joint function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n\n// f is a tensor of shape [batch, T, H]\n// g is a tensor of shape [batch, U, H]\n// the transducer joint does\n// sum = f.unsqueeze(dim=2) + g.unsqueeze(dim=1)\n// The resultant tensor is of shape [batch, T, U, H]\n// Each thread block is working on one \"batch\" of data in the output tensor, [batch, t, u, :]\n\n// This joint function can optionally pack the output where the output tensor with a shape of\n// [B, T, U, H] is packed into [B_packed, H].\n// Don't-care region (t > fLen) or (u > gLen) is removed.\n// To enable packing, the starting offset for each batch need to be specified with batchOffset.\ntemplate <typename scalar_t, class OffsetCal>\n__global__ void transducer_joint_forward(const scalar_t* f, const scalar_t* g, const int* fLen, const int* gLen,\n                                         const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                         int64_t hiddenSize, bool packOutput, scalar_t* sum) {\n  const int batch = blockIdx.z;\n  const int t = blockIdx.y;\n  const int u = blockIdx.x;\n  const auto myFLen = fLen[batch];\n  const auto myGLen = gLen[batch];\n\n  OffsetCal offsetCal(batch, batchOffset, maxFLen, maxGLen, myGLen, hiddenSize, packOutput);\n  const auto myBatchOffset = offsetCal.getBatchOffset();\n  const auto strideF = offsetCal.getStrideF();\n  scalar_t const* myF = f + batch * maxFLen * hiddenSize + t * hiddenSize;\n  scalar_t const* myG = g + batch * maxGLen * hiddenSize + u * hiddenSize;\n  scalar_t* mySum = sum + myBatchOffset + t * strideF + u * hiddenSize;\n\n  if (t < myFLen and u < myGLen) {\n#pragma unroll\n    for (int h = threadIdx.x; h < hiddenSize; h += blockDim.x) {\n      if (h < hiddenSize) {\n        mySum[h] = myF[h] + myG[h];\n      }\n    }\n  } else if (packOutput == false and t < maxFLen and u < maxGLen) {\n// Need to write finite data to don't-care region because we instantiate the result tensor\n// with torch::empty for performance reasons. Even though it is don't-care region, the\n// contents need to be finite, otherwise could lead to NaN in WGRAD.\n// In packing mode, this write is no longer necessary as we remove the don't-care region\n// from the output.\n// Picking -1 (over 0) here for ease of testing.\n#pragma unroll\n    for (int h = threadIdx.x; h < hiddenSize; h += blockDim.x) {\n      if (h < hiddenSize) {\n        mySum[h] = -1;\n      }\n    }\n  }\n}\n\n/*\nTiled version of the joint forward kernel\nDetail of this joint function can be found in:\n[1] Sequence Transduction with Recurrent Neural Networks.\n\nf is a tensor of shape [batch, T, H]\ng is a tensor of shape [batch, U, H]\nthe transducer joint does\nsum = f.unsqueeze(dim=2) + g.unsqueeze(dim=1)\nThe resultant tensor is of shape [batch, T, U, H]\nEach thread is working on a tile of the shape of tileF x tileG in the result tensor.\nThe input for the tile is first loaded in the register and is reused tileG and tileF times.\n\nThis joint function can optionally pack the output where the output tensor with a shape of\n[B, T, U, H] is packed into [B_packed, H].\nDon't-care region (t > fLen) or (u > gLen) is removed.\nTo enable packing, the starting offset for each batch need to be specified with batchOffset.\n\nOptionally this joint function performs ReLU and/or dropout on the joint output, which is\ncontrolled by arguments relu and dropout, respectively. philoxArgs is argument used for generating\npseudorandom number. When at least one of operations in ReLU and dropout is activated, the joint\nfunction is a masked operation, which is controlled by the template argument masked. In this case,\nmasks are saved to backward.\n*/\ntemplate <typename scalar_t, int tileF, int tileG, int U, class OffsetCal, bool masked>\n__global__ void transducer_joint_tiled_forward(const scalar_t* f, const scalar_t* g, const int* fLen, const int* gLen,\n                                               const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                               int64_t hiddenSize, int64_t hiddenPerBlock, bool packOutput, bool relu,\n                                               bool dropout, float p, at::PhiloxCudaState philoxArgs, scalar_t* sum,\n                                               uint8_t* mask) {\n  static_assert(U == 4, \"U has to be 4, as random numbers are generated in batch of 4\");\n\n  const int batch = blockIdx.z;\n  const int t = blockIdx.y * tileF;\n  const int hiddenBlock = (hiddenSize + hiddenPerBlock - 1) / hiddenPerBlock;\n  const int u = blockIdx.x / hiddenBlock * tileG;\n  const int hOffset = (blockIdx.x % hiddenBlock) * hiddenPerBlock;\n  const int h = threadIdx.x;\n  const auto myFLen = fLen[batch];\n  const auto myGLen = gLen[batch];\n\n  OffsetCal offsetCal(batch, batchOffset, maxFLen, maxGLen, myGLen, hiddenSize, packOutput);\n  const auto myBatchOffset = offsetCal.getBatchOffset();\n  const auto strideF = offsetCal.getStrideF();\n\n  scalar_t const* myF = f + batch * maxFLen * hiddenSize + t * hiddenSize + hOffset;\n  scalar_t const* myG = g + batch * maxGLen * hiddenSize + u * hiddenSize + hOffset;\n  scalar_t* mySum = sum + myBatchOffset + t * strideF + u * hiddenSize + hOffset;\n  uint8_t* myMask = mask + myBatchOffset + t * strideF + u * hiddenSize + hOffset;\n\n  // The following code is only needed for dropout. We try to bypass them as much as possible.\n  auto seeds = masked ? at::cuda::philox::unpack(philoxArgs)\n                      : std::make_tuple(static_cast<uint64_t>(0), static_cast<uint64_t>(0));\n  uint64_t tid =\n      masked ? (static_cast<uint64_t>(blockIdx.z) * gridDim.y * gridDim.x + blockIdx.y * gridDim.x + blockIdx.x) *\n                       blockDim.x +\n                   threadIdx.x\n             : 0;\n  Philox ph(std::get<0>(seeds), tid, std::get<1>(seeds));\n  scalar_t scale = masked ? ((p == 0) ? 0 : 1 / p) : 0;\n  bool dropoutMask[U];\n\n  if (t < myFLen and u < myGLen and hOffset + h < hiddenSize) {\n    // register buffers for tiled input reuse\n    scalar_t fBuffer[tileF], gBuffer[tileG];\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < myFLen) fBuffer[i] = myF[i * hiddenSize + h];\n    }\n    for (int j = 0; j < tileG; ++j) {\n      if (u + j < myGLen) gBuffer[j] = myG[j * hiddenSize + h];\n    }\n#pragma unroll\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < myFLen) {\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          int idx = i * tileG + j;\n          if (masked and dropout and idx % U == 0) {\n            // For performance, generate 4 random numbers in one shot\n            // auto rand4 = curand_uniform4(&state);\n            auto rand4 = uniform4(ph());\n            dropoutMask[0] = rand4.x < p;\n            dropoutMask[1] = rand4.y < p;\n            dropoutMask[2] = rand4.z < p;\n            dropoutMask[3] = rand4.w < p;\n          }\n\n          if (u + j < myGLen) {\n            scalar_t out = fBuffer[i] + gBuffer[j];\n            if (masked) {\n              // Apply ReLU here when relu is True\n              bool localMask = relu ? (out > 0) : 1;\n              localMask = dropout ? localMask & dropoutMask[idx % U] : localMask;\n              out = dropout ? out * localMask * scale : out * localMask;\n              myMask[i * strideF + j * hiddenSize + h] = static_cast<uint8_t>(localMask);\n            }\n            mySum[i * strideF + j * hiddenSize + h] = out;\n          } else if (packOutput == false and u + j < maxGLen)\n            mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      } else if (packOutput == false and t + i < maxFLen) {\n// Again need to write finite data to don't-care region\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          if (u + j < maxGLen) mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      }\n    }\n  } else if (packOutput == false and t < maxFLen and u < maxGLen and hOffset + h < hiddenSize) {\n// Only need to ensure the finity in normal mode\n#pragma unroll\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < maxFLen) {\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          if (u + j < maxGLen) mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      }\n    }\n  }\n}\n\n/*\nBwd operation (reduction) on one input tensor. Since the operation performed for the two input\ntensors are exactly the same, only one kernel is needed, and the different indexing offsets\nand strides are handled by OffsetCalBwd.\n\nWhen packing is enabled in the fwd op, unpacking is needed to restore the gradients in a\nnon-packed form.\n\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, class OffsetCal, bool masked>\n__device__ void transducer_joint_single_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                 const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                 int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                 bool bwdFasterDim,  // whether bwd on the faster moving dimension (u)\n                                                 float scale, scalar_t* inGrad, int yBlockOffset = 0) {\n  const int batch = blockIdx.z;\n  // For the second input tensor, this offset need to be subtracted because the first yBlockOffset\n  // sets of thread blocks are for the first input tensor.\n  const int x = blockIdx.y - yBlockOffset;\n  const int hOffset = blockIdx.x * C10_WARP_SIZE;\n  const int wid = threadIdx.y;\n  const int lid = threadIdx.x;\n  const int numWarp = blockDim.y;\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  OffsetCal offsetCal(batch, batchOffset, fLen, gLen, maxFLen, maxGLen, hiddenSize, packOutput, bwdFasterDim);\n  const auto maxXLen = offsetCal.getMaxXLen();\n  const auto myXLen = offsetCal.getMyXLen();\n  const auto myYLen = offsetCal.getMyYLen();\n  scalar_t* myInGrad = inGrad + batch * maxXLen * hiddenSize + x * hiddenSize + hOffset;\n\n  if (x < myXLen) {\n    const auto myBatchOffset = offsetCal.getBatchOffset();\n    const auto strideX = offsetCal.getStrideX();\n    const auto strideY = offsetCal.getStrideY();\n    const scalar_t* myGrad = grad + myBatchOffset + x * strideX + hOffset;\n    const uint8_t* myMask = masked ? mask + myBatchOffset + x * strideX + hOffset : nullptr;\n\n    // Each warp reduces numYPerWarp \"y\" first\n    acc_t warpSum = 0;\n    auto numYPerWarp = (myYLen + numWarp - 1) / numWarp;\n#pragma unroll\n    for (int warpY = 0; warpY < numYPerWarp; ++warpY) {\n      auto y = wid * numYPerWarp + warpY;\n      if (y < myYLen and (hOffset + lid) < hiddenSize)\n        if (masked)\n          warpSum += static_cast<acc_t>(myGrad[y * strideY + lid]) * myMask[y * strideY + lid] * scale;\n        else\n          warpSum += myGrad[y * strideY + lid];\n    }\n\n    // transpose partial sum in SMEM and reduce further using warpReduce\n    smem[lid * numWarp + wid] = warpSum;\n    __syncthreads();\n    auto sum = smem[wid * C10_WARP_SIZE + lid];\n    sum = warpReduce(sum, numWarp);\n\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // example of 4 warps (a, b, c, d) with 8 threads per warp\n    // Each warp need 8 / 4 = 2 threads to write the results.\n    if (hOffset + wid * C10_WARP_SIZE / numWarp + lid / numWarp < hiddenSize) {\n      if (lid % numWarp == 0) {\n        myInGrad[wid * C10_WARP_SIZE / numWarp + lid / numWarp] = sum;\n      }\n    }\n  } else if (wid == 0 and hOffset + lid < hiddenSize) {\n    // Need to ensure the grad is zero for don't care region\n    myInGrad[lid] = 0;\n  }\n}\n\n/*\nActual bwd (reduction) kernel get launched.\nCall transducer_joint_single_backward twice on two input tensors.\nThe two bwd ops are launched together, the first op uses blockIdx.y < maxFLen, and the second op\nuses the rest.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, class OffsetCal, bool masked>\n__global__ void transducer_joint_combined_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                   const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                   int64_t maxGLen, int64_t hiddenSize, bool packOutput, float scale,\n                                                   scalar_t* fGrad, scalar_t* gGrad) {\n  if (blockIdx.y < maxFLen) {\n    transducer_joint_single_backward<scalar_t, acc_t, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, false, scale, fGrad);\n  } else {\n    transducer_joint_single_backward<scalar_t, acc_t, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, true, scale, gGrad, maxFLen);\n  }\n}\n\n/*\nVectorized version of transducer_joint_single_backward\nDoing exact same operation as transducer_joint_single_backward except the load and store are\nvectorized.\nWhen packing is enabled in the fwd op, unpacking is needed to restore the gradients in a\nnon-packed form.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V, class OffsetCal, bool masked>\n__device__ void transducer_joint_single_vec_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                     const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                     int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                     bool bwdFasterDim, float scale, scalar_t* inGrad,\n                                                     int yBlockOffset = 0) {\n  const int batch = blockIdx.z;\n  const int x = blockIdx.y - yBlockOffset;\n  const int hOffset = blockIdx.x * C10_WARP_SIZE * V;\n  const int wid = threadIdx.y;\n  const int lid = threadIdx.x;\n  const int numWarp = blockDim.y;\n\n  // Figure out the vectorization type for mask\n  using mvec_t = mvec_type<V>;\n\n  OffsetCal offsetCal(batch, batchOffset, fLen, gLen, maxFLen, maxGLen, hiddenSize, packOutput, bwdFasterDim);\n  const auto maxXLen = offsetCal.getMaxXLen();\n  const auto myXLen = offsetCal.getMyXLen();\n  const auto myYLen = offsetCal.getMyYLen();\n  scalar_t* myInGrad = inGrad + batch * maxXLen * hiddenSize + x * hiddenSize + hOffset;\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  acc_t warpSum[V];\n  scalar_t inBuffer[V];\n  uint8_t maskBuffer[V];\n  scalar_t outBuffer[V];\n  auto myInGradVec = reinterpret_cast<vec_t*>(myInGrad);\n  auto outBufferVec = reinterpret_cast<vec_t*>(outBuffer);\n\n  if (x < myXLen) {\n    const auto myBatchOffset = offsetCal.getBatchOffset();\n    const auto strideX = offsetCal.getStrideX();\n    const auto strideY = offsetCal.getStrideY();\n    const scalar_t* myGrad = grad + myBatchOffset + x * strideX + hOffset;\n    const uint8_t* myMask = masked ? mask + myBatchOffset + x * strideX + hOffset : nullptr;\n\n    for (int i = 0; i < V; ++i) warpSum[i] = 0;\n\n    // Each warp reduces numYPerWarp \"y\" first\n    auto numYPerWarp = (myYLen + numWarp - 1) / numWarp;\n    for (int warpY = 0; warpY < numYPerWarp; ++warpY) {\n      auto y = wid * numYPerWarp + warpY;\n      auto myGradVec = reinterpret_cast<vec_t const*>(myGrad + y * strideY);\n      auto myMaskVec = masked ? reinterpret_cast<mvec_t const*>(myMask + y * strideY) : nullptr;\n      auto inBufferVec = reinterpret_cast<vec_t*>(inBuffer);\n      auto maskBufferVec = reinterpret_cast<mvec_t*>(maskBuffer);\n      if (hOffset + lid * V < hiddenSize and y < myYLen) {\n        *inBufferVec = myGradVec[lid];  // vectorized load\n        if (masked) {\n          *maskBufferVec = myMaskVec[lid];\n#pragma unroll\n          for (int i = 0; i < V; ++i) warpSum[i] += static_cast<acc_t>(inBuffer[i]) * maskBuffer[i] * scale;\n        } else {\n#pragma unroll\n          for (int i = 0; i < V; ++i) warpSum[i] += inBuffer[i];\n        }\n      }\n    }\n\n    // transpose partial sum in SMEM and reduce further using warpReduce\n    for (int i = 0; i < V; ++i) {\n      smem[lid * numWarp + wid] = warpSum[i];\n      __syncthreads();\n      auto sum = smem[wid * C10_WARP_SIZE + lid];\n\n      if (hOffset + (wid * C10_WARP_SIZE / numWarp) * V < hiddenSize) {\n        sum = warpReduce(sum, numWarp);\n        if (lid % numWarp == 0) {\n          outBuffer[i] = sum;\n        }\n      }\n      __syncthreads();\n    }\n\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // example of 4 warps (a, b, c, d) with 8 threads per warp\n    // Each warp need 8 / 4 = 2 threads to write the results.\n    if (lid % numWarp == 0 and hOffset + (wid * C10_WARP_SIZE / numWarp + lid / numWarp) * V < hiddenSize)\n      myInGradVec[wid * C10_WARP_SIZE / numWarp + lid / numWarp] = *outBufferVec;\n  } else if (wid == 0 and hOffset + lid * V < hiddenSize) {\n    // Need to ensure the grad is zero for don't care region\n    myInGradVec[lid] = 0;\n  }\n}\n\n/*\nVecotrized version of transducer_joint_combined_backward\nCall transducer_joint_single_vec_backward twice on two input tensors.\nThe two bwd ops are launched together, the first op uses blockIdx.y < maxFLen, and the second op\nuses the rest.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V, class OffsetCal, bool masked>\n__global__ void transducer_joint_combined_vec_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                       const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                       int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                       float scale, scalar_t* fGrad, scalar_t* gGrad) {\n  if (blockIdx.y < maxFLen) {\n    transducer_joint_single_vec_backward<scalar_t, acc_t, vec_t, V, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, false, scale, fGrad);\n  } else {\n    transducer_joint_single_vec_backward<scalar_t, acc_t, vec_t, V, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, true, scale, gGrad, maxFLen);\n  }\n}\n\nstd::vector<torch::Tensor> transducer_joint_cuda_forward(torch::Tensor f, torch::Tensor g, torch::Tensor fLen,\n                                                         torch::Tensor gLen, torch::Tensor batchOffset,\n                                                         int64_t packedBatch, int opt, bool packOutput, bool relu,\n                                                         bool dropout, float dropoutProb, int tileSize) {\n  auto tensorOpt = f.options();\n  auto dtype = f.scalar_type();\n  const auto batchSize = f.size(0);\n  const auto maxFLen = f.size(1);\n  const auto maxGLen = g.size(1);\n  const auto hiddenSize = f.size(2);\n  bool masked = dropout or relu;\n\n  int64_t* batchOffsetPtr = nullptr;\n  torch::Tensor sum, mask;\n  auto maskOpt = tensorOpt.dtype(torch::kUInt8);\n  if (!packOutput) {\n    sum = torch::empty({batchSize, maxFLen, maxGLen, hiddenSize}, tensorOpt);\n    batchOffsetPtr = nullptr;\n    if (masked) mask = torch::empty({batchSize, maxFLen, maxGLen, hiddenSize}, maskOpt);\n  } else {\n    sum = torch::empty({packedBatch, hiddenSize}, tensorOpt);\n    batchOffsetPtr = batchOffset.data_ptr<int64_t>();\n    if (masked) mask = torch::empty({packedBatch, hiddenSize}, maskOpt);\n  }\n  uint8_t* maskPtr = masked ? mask.data_ptr<uint8_t>() : nullptr;\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  TORCH_CHECK(opt == 0 or opt == 1, \"Got an invalid optimization level \", opt);\n  // Simple heuristics\n  const int numThread =\n      std::min(128, (static_cast<int>(hiddenSize) + C10_WARP_SIZE - 1) / C10_WARP_SIZE * C10_WARP_SIZE);\n\n  if (opt == 0) {\n    // vanilla kernel\n    const int threads = numThread;\n    const dim3 blocks(maxGLen, maxFLen, batchSize);\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_joint_forward\", ([&] {\n          transducer_joint_forward<scalar_t, OffsetCalFwd><<<blocks, threads, 0, stream>>>(\n              f.data_ptr<scalar_t>(), g.data_ptr<scalar_t>(), fLen.data_ptr<int>(), gLen.data_ptr<int>(),\n              batchOffsetPtr, maxFLen, maxGLen, hiddenSize, packOutput, sum.data_ptr<scalar_t>());\n        }));\n  }\n  if (opt == 1) {\n    // tiled version. For simplicity, assume tileF == tileG, even though the kernel can\n    // support more general cases.\n    const int threads = numThread;\n    const int hiddenPerBlock = numThread;\n    const int hiddenBlock = (hiddenSize + hiddenPerBlock - 1) / hiddenPerBlock;\n    const dim3 blocks((maxGLen + tileSize - 1) / tileSize * hiddenBlock, (maxFLen + tileSize - 1) / tileSize,\n                      batchSize);\n\n    TORCH_CHECK(tileSize == 1 or tileSize == 2 or tileSize == 4, \"Expected tileSize to be in [1, 2, 4], but got \",\n                tileSize);\n\n    at::PhiloxCudaState rng_engine_inputs;\n    if (masked) {\n      // set up PRG when the input is masked. rng_engine_inputs will be used as a space filler\n      // for non-masked calls.\n      // Therefore no need to initialize.\n      c10::optional<at::Generator> gen_;\n      auto gen = at::get_generator_or_default<at::CUDAGeneratorImpl>(gen_, at::cuda::detail::getDefaultCUDAGenerator());\n      // counterOffset records how many cuRAND calls each thread makes. For a tiled kernel,\n      // each thread processes tileF * tileG output elements.\n      int64_t counterOffset = tileSize * tileSize;\n      {\n        std::lock_guard<std::mutex> lock(gen->mutex_);\n        rng_engine_inputs = gen->philox_cuda_state(counterOffset);\n      }\n    }\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_joint_forward\", ([&] {\n          void (*kernel)(const scalar_t*, const scalar_t*, const int*, const int*, const int64_t*, int64_t, int64_t,\n                         int64_t, int64_t, bool, bool, bool, float, at::PhiloxCudaState, scalar_t*, uint8_t*);\n          if (masked) {\n            switch (tileSize) {\n              case 2:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 2, 2, 4, OffsetCalFwd, true>;\n                break;\n              case 4:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 4, 4, 4, OffsetCalFwd, true>;\n                break;\n            }\n          } else {\n            switch (tileSize) {\n              case 1:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 1, 1, 4, OffsetCalFwd, false>;\n                break;\n              case 2:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 2, 2, 4, OffsetCalFwd, false>;\n                break;\n              case 4:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 4, 4, 4, OffsetCalFwd, false>;\n                break;\n            }\n          }\n\n          kernel<<<blocks, threads, 0, stream>>>(f.data_ptr<scalar_t>(), g.data_ptr<scalar_t>(), fLen.data_ptr<int>(),\n                                                 gLen.data_ptr<int>(), batchOffsetPtr, maxFLen, maxGLen, hiddenSize,\n                                                 hiddenPerBlock, packOutput, relu, dropout, 1.0f - dropoutProb,\n                                                 rng_engine_inputs, sum.data_ptr<scalar_t>(), maskPtr);\n        }));\n  }\n\n  C10_CUDA_CHECK(cudaGetLastError());\n  if (masked)\n    return {sum, mask};\n  else\n    return {sum};\n}\n\nstd::vector<torch::Tensor> transducer_joint_cuda_backward(std::vector<torch::Tensor> in, torch::Tensor fLen,\n                                                          torch::Tensor gLen, torch::Tensor batchOffset, int maxFLen,\n                                                          int maxGLen, bool packOutput, float scale) {\n  auto grad = in[0];\n  bool masked = (in.size() == 2);\n  uint8_t* maskPtr = masked ? in[1].data_ptr<uint8_t>() : nullptr;\n\n  auto tensorOpt = grad.options();\n  auto dtype = grad.scalar_type();\n  const int batchSize = fLen.size(0);\n  const int hiddenSize = grad.size(-1);\n\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const int maxNumWarp = deviceProperties->maxThreadsPerBlock / C10_WARP_SIZE;\n\n  torch::Tensor fGrad = torch::empty({batchSize, maxFLen, hiddenSize}, tensorOpt);\n  torch::Tensor gGrad = torch::empty({batchSize, maxGLen, hiddenSize}, tensorOpt);\n\n  int64_t* batchOffsetPtr = (!packOutput) ? nullptr : batchOffset.data_ptr<int64_t>();\n\n  // The number \"y\" I would like each thread to work on\n  const int workPerThread = 32;\n  // Since the bwd for f and g have the same thread block size, we need to use the max of the two.\n  int numWarp = largestPowerOfTwo((std::max(maxFLen, maxGLen) + workPerThread - 1) / workPerThread);\n  // Would like to have at least 2 warps\n  numWarp = std::max(2, numWarp);\n  // cap on the maximum number of warps allowed\n  numWarp = std::min(maxNumWarp, numWarp);\n\n  // Need smem for transposing the partial sum. The partial sum is in a matrix of the shape\n  // numWarp x warpSize\n  const int smemSize = numWarp * C10_WARP_SIZE;\n  const dim3 threads(C10_WARP_SIZE, numWarp, 1);\n\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n      dtype, \"transducer_joint_cuda_backward_kernel\", ([&] {\n        auto gradPtr = grad.data_ptr<scalar_t>();\n        auto fLenPtr = fLen.data_ptr<int>();\n        auto gLenPtr = gLen.data_ptr<int>();\n        auto fGradPtr = fGrad.data_ptr<scalar_t>();\n        auto gGradPtr = gGrad.data_ptr<scalar_t>();\n\n        // resolve the acc_t type\n        using acc_t = at::acc_type<scalar_t, true>;\n        using vec_t = uint64_t;\n\n        constexpr int vectFactor = sizeof(vec_t) / sizeof(scalar_t);\n        constexpr int vecAlignment = std::alignment_of<vec_t>::value;\n\n        // if all input and output tensors meet the alignment requirement\n        bool memAlign = (reinterpret_cast<uint64_t>(gradPtr) % vecAlignment == 0) and\n                        (reinterpret_cast<uint64_t>(fGradPtr) % vecAlignment == 0) and\n                        (reinterpret_cast<uint64_t>(gGradPtr) % vecAlignment == 0);\n\n        if (vectFactor > 1 and hiddenSize % vectFactor == 0 and memAlign) {\n          // If vectorization helps and the alignment requirement is met, use the vectorized\n          // kernel. For simplicity, hiddenSize needs to be a multiple vecFactor.\n          const dim3 blocks((hiddenSize + C10_WARP_SIZE * vectFactor - 1) / (C10_WARP_SIZE * vectFactor),\n                            maxFLen + maxGLen, batchSize);\n          if (masked) {\n            transducer_joint_combined_vec_backward<scalar_t, acc_t, vec_t, vectFactor, OffsetCalBwd, true>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          } else {\n            transducer_joint_combined_vec_backward<scalar_t, acc_t, vec_t, vectFactor, OffsetCalBwd, false>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          }\n        } else {\n          const dim3 blocks((hiddenSize + C10_WARP_SIZE - 1) / C10_WARP_SIZE, maxFLen + maxGLen, batchSize);\n          if (masked) {\n            transducer_joint_combined_backward<scalar_t, acc_t, OffsetCalBwd, true>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          } else {\n            transducer_joint_combined_backward<scalar_t, acc_t, OffsetCalBwd, false>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          }\n        }\n      }));\n\n  return {fGrad, gGrad};\n}\n", "output": "#include <ATen/AccumulateType.h>\n#include <hip/hip_runtime.h>\n#include <hiprand/hiprand_kernel.h>\n#include <torch/extension.h>\n\n#ifdef OLD_GENERATOR_PATH\n#include <ATen/CUDAGeneratorImpl.h>\n#else\n#include <ATen/cuda/CUDAGeneratorImpl.h>\n#endif\n\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/macros/Macros.h>\n\n#include <ATen/cuda/CUDAGraphsUtils.cuh>\n\n#include \"philox.cuh\"\n\n// Warp reduce kernels to reduce N groups of data into N numbers, where N = warpSize / width.\n// width should be a power of 2 and should be less than warpSize.\ntemplate <typename scalar_t>\n__device__ __forceinline__ scalar_t warpReduce(scalar_t x, int width = C10_WARP_SIZE) {\n  for (unsigned offset = width / 2; offset > 0; offset /= 2) {\n    x += __shfl_down_sync(0xffffffff, x, offset, width);\n  }\n  return x;\n}\n\ninline int largestPowerOfTwo(int x) {\n  int y = 1;\n  while (y <= x) y <<= 1;\n  return y >> 1;\n}\n\n/*\nFigure out vectorization type for masks.\nSimilar to how PyTorch figures out acc_t here:\naten/src/ATen/AccumulateType.h\n*/\ntemplate <int V>\nstruct MaskVecType {};\n\ntemplate <>\nstruct MaskVecType<1> {\n  using type = uint8_t;\n};\ntemplate <>\nstruct MaskVecType<2> {\n  using type = uint16_t;\n};\ntemplate <>\nstruct MaskVecType<4> {\n  using type = uint32_t;\n};\n\ntemplate <int V>\nusing mvec_type = typename MaskVecType<V>::type;\n\n// Helper class to calculate pointer offset that can be shared by different flavors of kernels.\n// For fwd, batch offset and stride are different for packing and non-packing mode.\nstruct OffsetCalFwd {\n  __device__ __forceinline__ OffsetCalFwd(int64_t batch, const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                          int64_t gLen, int64_t hiddenSize, bool packOutput)\n      : batch(batch),\n        batchOffset(batchOffset),\n        maxFLen(maxFLen),\n        maxGLen(maxGLen),\n        gLen(gLen),\n        hiddenSize(hiddenSize),\n        packOutput(packOutput) {}\n\n  int64_t batch;\n  const int64_t* batchOffset;\n  int64_t maxFLen;\n  int64_t maxGLen;\n  int64_t gLen;\n  int64_t hiddenSize;\n  bool packOutput;\n\n  __device__ __forceinline__ int64_t getBatchOffset() {\n    return packOutput ? ((batch == 0) ? 0 : batchOffset[batch - 1]) * hiddenSize\n                      : batch * maxFLen * maxGLen * hiddenSize;\n  }\n\n  __device__ __forceinline__ int64_t getStrideF() { return packOutput ? gLen * hiddenSize : maxGLen * hiddenSize; }\n};\n\n// Helper class to calculate pointer offset that can be shared by different flavors of kernels\n// For bwd, batch offset and stride are different for packing and non-packing mode.\n// The reducion is done for two input tensors. Therefore, generating two sets of offsets\n// according to bwdFasterDim can lead to a unified implementation in the actual kernel.\nstruct OffsetCalBwd {\n  __device__ __forceinline__ OffsetCalBwd(int64_t batch, const int64_t* batchOffset, const int* fLen, const int* gLen,\n                                          int64_t maxFLen, int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                          bool bwdFasterDim)\n      : batch(batch),\n        batchOffset(batchOffset),\n        maxFLen(maxFLen),\n        maxGLen(maxGLen),\n        fLen(fLen),\n        gLen(gLen),\n        hiddenSize(hiddenSize),\n        packOutput(packOutput),\n        bwdFasterDim(bwdFasterDim) {}\n\n  int64_t batch;\n  const int64_t* batchOffset;\n  const int* fLen;\n  const int* gLen;\n  int64_t maxFLen;\n  int64_t maxGLen;\n  int64_t hiddenSize;\n  bool packOutput;\n  bool bwdFasterDim;  // whether doing bwd on the faster moving dimension\n\n  __device__ __forceinline__ int64_t getBatchOffset() {\n    return packOutput ? ((batch == 0) ? 0 : batchOffset[batch - 1]) * hiddenSize\n                      : batch * maxFLen * maxGLen * hiddenSize;\n  }\n\n  __device__ __forceinline__ int64_t getMaxXLen() { return bwdFasterDim ? maxGLen : maxFLen; }\n\n  __device__ __forceinline__ auto getMyXLen() -> decltype(gLen[batch]) {\n    return bwdFasterDim ? gLen[batch] : fLen[batch];\n  }\n\n  __device__ __forceinline__ auto getMyYLen() -> decltype(gLen[batch]) {\n    return bwdFasterDim ? fLen[batch] : gLen[batch];\n  }\n\n  __device__ __forceinline__ int64_t getStrideX() {\n    return bwdFasterDim ? hiddenSize : ((packOutput ? gLen[batch] : maxGLen) * hiddenSize);\n  }\n\n  __device__ __forceinline__ int64_t getStrideY() {\n    return bwdFasterDim ? ((packOutput ? gLen[batch] : maxGLen) * hiddenSize) : hiddenSize;\n  }\n};\n\n// Vanila transducer joint forward kernel\n// Detail of this joint function can be found in:\n// [1] Sequence Transduction with Recurrent Neural Networks.\n\n// f is a tensor of shape [batch, T, H]\n// g is a tensor of shape [batch, U, H]\n// the transducer joint does\n// sum = f.unsqueeze(dim=2) + g.unsqueeze(dim=1)\n// The resultant tensor is of shape [batch, T, U, H]\n// Each thread block is working on one \"batch\" of data in the output tensor, [batch, t, u, :]\n\n// This joint function can optionally pack the output where the output tensor with a shape of\n// [B, T, U, H] is packed into [B_packed, H].\n// Don't-care region (t > fLen) or (u > gLen) is removed.\n// To enable packing, the starting offset for each batch need to be specified with batchOffset.\ntemplate <typename scalar_t, class OffsetCal>\n__global__ void transducer_joint_forward(const scalar_t* f, const scalar_t* g, const int* fLen, const int* gLen,\n                                         const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                         int64_t hiddenSize, bool packOutput, scalar_t* sum) {\n  const int batch = blockIdx.z;\n  const int t = blockIdx.y;\n  const int u = blockIdx.x;\n  const auto myFLen = fLen[batch];\n  const auto myGLen = gLen[batch];\n\n  OffsetCal offsetCal(batch, batchOffset, maxFLen, maxGLen, myGLen, hiddenSize, packOutput);\n  const auto myBatchOffset = offsetCal.getBatchOffset();\n  const auto strideF = offsetCal.getStrideF();\n  scalar_t const* myF = f + batch * maxFLen * hiddenSize + t * hiddenSize;\n  scalar_t const* myG = g + batch * maxGLen * hiddenSize + u * hiddenSize;\n  scalar_t* mySum = sum + myBatchOffset + t * strideF + u * hiddenSize;\n\n  if (t < myFLen and u < myGLen) {\n#pragma unroll\n    for (int h = threadIdx.x; h < hiddenSize; h += blockDim.x) {\n      if (h < hiddenSize) {\n        mySum[h] = myF[h] + myG[h];\n      }\n    }\n  } else if (packOutput == false and t < maxFLen and u < maxGLen) {\n// Need to write finite data to don't-care region because we instantiate the result tensor\n// with torch::empty for performance reasons. Even though it is don't-care region, the\n// contents need to be finite, otherwise could lead to NaN in WGRAD.\n// In packing mode, this write is no longer necessary as we remove the don't-care region\n// from the output.\n// Picking -1 (over 0) here for ease of testing.\n#pragma unroll\n    for (int h = threadIdx.x; h < hiddenSize; h += blockDim.x) {\n      if (h < hiddenSize) {\n        mySum[h] = -1;\n      }\n    }\n  }\n}\n\n/*\nTiled version of the joint forward kernel\nDetail of this joint function can be found in:\n[1] Sequence Transduction with Recurrent Neural Networks.\n\nf is a tensor of shape [batch, T, H]\ng is a tensor of shape [batch, U, H]\nthe transducer joint does\nsum = f.unsqueeze(dim=2) + g.unsqueeze(dim=1)\nThe resultant tensor is of shape [batch, T, U, H]\nEach thread is working on a tile of the shape of tileF x tileG in the result tensor.\nThe input for the tile is first loaded in the register and is reused tileG and tileF times.\n\nThis joint function can optionally pack the output where the output tensor with a shape of\n[B, T, U, H] is packed into [B_packed, H].\nDon't-care region (t > fLen) or (u > gLen) is removed.\nTo enable packing, the starting offset for each batch need to be specified with batchOffset.\n\nOptionally this joint function performs ReLU and/or dropout on the joint output, which is\ncontrolled by arguments relu and dropout, respectively. philoxArgs is argument used for generating\npseudorandom number. When at least one of operations in ReLU and dropout is activated, the joint\nfunction is a masked operation, which is controlled by the template argument masked. In this case,\nmasks are saved to backward.\n*/\ntemplate <typename scalar_t, int tileF, int tileG, int U, class OffsetCal, bool masked>\n__global__ void transducer_joint_tiled_forward(const scalar_t* f, const scalar_t* g, const int* fLen, const int* gLen,\n                                               const int64_t* batchOffset, int64_t maxFLen, int64_t maxGLen,\n                                               int64_t hiddenSize, int64_t hiddenPerBlock, bool packOutput, bool relu,\n                                               bool dropout, float p, at::PhiloxCudaState philoxArgs, scalar_t* sum,\n                                               uint8_t* mask) {\n  static_assert(U == 4, \"U has to be 4, as random numbers are generated in batch of 4\");\n\n  const int batch = blockIdx.z;\n  const int t = blockIdx.y * tileF;\n  const int hiddenBlock = (hiddenSize + hiddenPerBlock - 1) / hiddenPerBlock;\n  const int u = blockIdx.x / hiddenBlock * tileG;\n  const int hOffset = (blockIdx.x % hiddenBlock) * hiddenPerBlock;\n  const int h = threadIdx.x;\n  const auto myFLen = fLen[batch];\n  const auto myGLen = gLen[batch];\n\n  OffsetCal offsetCal(batch, batchOffset, maxFLen, maxGLen, myGLen, hiddenSize, packOutput);\n  const auto myBatchOffset = offsetCal.getBatchOffset();\n  const auto strideF = offsetCal.getStrideF();\n\n  scalar_t const* myF = f + batch * maxFLen * hiddenSize + t * hiddenSize + hOffset;\n  scalar_t const* myG = g + batch * maxGLen * hiddenSize + u * hiddenSize + hOffset;\n  scalar_t* mySum = sum + myBatchOffset + t * strideF + u * hiddenSize + hOffset;\n  uint8_t* myMask = mask + myBatchOffset + t * strideF + u * hiddenSize + hOffset;\n\n  // The following code is only needed for dropout. We try to bypass them as much as possible.\n  auto seeds = masked ? at::cuda::philox::unpack(philoxArgs)\n                      : std::make_tuple(static_cast<uint64_t>(0), static_cast<uint64_t>(0));\n  uint64_t tid =\n      masked ? (static_cast<uint64_t>(blockIdx.z) * gridDim.y * gridDim.x + blockIdx.y * gridDim.x + blockIdx.x) *\n                       blockDim.x +\n                   threadIdx.x\n             : 0;\n  Philox ph(std::get<0>(seeds), tid, std::get<1>(seeds));\n  scalar_t scale = masked ? ((p == 0) ? 0 : 1 / p) : 0;\n  bool dropoutMask[U];\n\n  if (t < myFLen and u < myGLen and hOffset + h < hiddenSize) {\n    // register buffers for tiled input reuse\n    scalar_t fBuffer[tileF], gBuffer[tileG];\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < myFLen) fBuffer[i] = myF[i * hiddenSize + h];\n    }\n    for (int j = 0; j < tileG; ++j) {\n      if (u + j < myGLen) gBuffer[j] = myG[j * hiddenSize + h];\n    }\n#pragma unroll\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < myFLen) {\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          int idx = i * tileG + j;\n          if (masked and dropout and idx % U == 0) {\n            // For performance, generate 4 random numbers in one shot\n            // auto rand4 = curand_uniform4(&state);\n            auto rand4 = uniform4(ph());\n            dropoutMask[0] = rand4.x < p;\n            dropoutMask[1] = rand4.y < p;\n            dropoutMask[2] = rand4.z < p;\n            dropoutMask[3] = rand4.w < p;\n          }\n\n          if (u + j < myGLen) {\n            scalar_t out = fBuffer[i] + gBuffer[j];\n            if (masked) {\n              // Apply ReLU here when relu is True\n              bool localMask = relu ? (out > 0) : 1;\n              localMask = dropout ? localMask & dropoutMask[idx % U] : localMask;\n              out = dropout ? out * localMask * scale : out * localMask;\n              myMask[i * strideF + j * hiddenSize + h] = static_cast<uint8_t>(localMask);\n            }\n            mySum[i * strideF + j * hiddenSize + h] = out;\n          } else if (packOutput == false and u + j < maxGLen)\n            mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      } else if (packOutput == false and t + i < maxFLen) {\n// Again need to write finite data to don't-care region\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          if (u + j < maxGLen) mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      }\n    }\n  } else if (packOutput == false and t < maxFLen and u < maxGLen and hOffset + h < hiddenSize) {\n// Only need to ensure the finity in normal mode\n#pragma unroll\n    for (int i = 0; i < tileF; ++i) {\n      if (t + i < maxFLen) {\n#pragma unroll\n        for (int j = 0; j < tileG; ++j) {\n          if (u + j < maxGLen) mySum[i * strideF + j * hiddenSize + h] = -1;\n        }\n      }\n    }\n  }\n}\n\n/*\nBwd operation (reduction) on one input tensor. Since the operation performed for the two input\ntensors are exactly the same, only one kernel is needed, and the different indexing offsets\nand strides are handled by OffsetCalBwd.\n\nWhen packing is enabled in the fwd op, unpacking is needed to restore the gradients in a\nnon-packed form.\n\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, class OffsetCal, bool masked>\n__device__ void transducer_joint_single_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                 const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                 int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                 bool bwdFasterDim,  // whether bwd on the faster moving dimension (u)\n                                                 float scale, scalar_t* inGrad, int yBlockOffset = 0) {\n  const int batch = blockIdx.z;\n  // For the second input tensor, this offset need to be subtracted because the first yBlockOffset\n  // sets of thread blocks are for the first input tensor.\n  const int x = blockIdx.y - yBlockOffset;\n  const int hOffset = blockIdx.x * C10_WARP_SIZE;\n  const int wid = threadIdx.y;\n  const int lid = threadIdx.x;\n  const int numWarp = blockDim.y;\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  OffsetCal offsetCal(batch, batchOffset, fLen, gLen, maxFLen, maxGLen, hiddenSize, packOutput, bwdFasterDim);\n  const auto maxXLen = offsetCal.getMaxXLen();\n  const auto myXLen = offsetCal.getMyXLen();\n  const auto myYLen = offsetCal.getMyYLen();\n  scalar_t* myInGrad = inGrad + batch * maxXLen * hiddenSize + x * hiddenSize + hOffset;\n\n  if (x < myXLen) {\n    const auto myBatchOffset = offsetCal.getBatchOffset();\n    const auto strideX = offsetCal.getStrideX();\n    const auto strideY = offsetCal.getStrideY();\n    const scalar_t* myGrad = grad + myBatchOffset + x * strideX + hOffset;\n    const uint8_t* myMask = masked ? mask + myBatchOffset + x * strideX + hOffset : nullptr;\n\n    // Each warp reduces numYPerWarp \"y\" first\n    acc_t warpSum = 0;\n    auto numYPerWarp = (myYLen + numWarp - 1) / numWarp;\n#pragma unroll\n    for (int warpY = 0; warpY < numYPerWarp; ++warpY) {\n      auto y = wid * numYPerWarp + warpY;\n      if (y < myYLen and (hOffset + lid) < hiddenSize)\n        if (masked)\n          warpSum += static_cast<acc_t>(myGrad[y * strideY + lid]) * myMask[y * strideY + lid] * scale;\n        else\n          warpSum += myGrad[y * strideY + lid];\n    }\n\n    // transpose partial sum in SMEM and reduce further using warpReduce\n    smem[lid * numWarp + wid] = warpSum;\n    __syncthreads();\n    auto sum = smem[wid * C10_WARP_SIZE + lid];\n    sum = warpReduce(sum, numWarp);\n\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // example of 4 warps (a, b, c, d) with 8 threads per warp\n    // Each warp need 8 / 4 = 2 threads to write the results.\n    if (hOffset + wid * C10_WARP_SIZE / numWarp + lid / numWarp < hiddenSize) {\n      if (lid % numWarp == 0) {\n        myInGrad[wid * C10_WARP_SIZE / numWarp + lid / numWarp] = sum;\n      }\n    }\n  } else if (wid == 0 and hOffset + lid < hiddenSize) {\n    // Need to ensure the grad is zero for don't care region\n    myInGrad[lid] = 0;\n  }\n}\n\n/*\nActual bwd (reduction) kernel get launched.\nCall transducer_joint_single_backward twice on two input tensors.\nThe two bwd ops are launched together, the first op uses blockIdx.y < maxFLen, and the second op\nuses the rest.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, class OffsetCal, bool masked>\n__global__ void transducer_joint_combined_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                   const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                   int64_t maxGLen, int64_t hiddenSize, bool packOutput, float scale,\n                                                   scalar_t* fGrad, scalar_t* gGrad) {\n  if (blockIdx.y < maxFLen) {\n    transducer_joint_single_backward<scalar_t, acc_t, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, false, scale, fGrad);\n  } else {\n    transducer_joint_single_backward<scalar_t, acc_t, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, true, scale, gGrad, maxFLen);\n  }\n}\n\n/*\nVectorized version of transducer_joint_single_backward\nDoing exact same operation as transducer_joint_single_backward except the load and store are\nvectorized.\nWhen packing is enabled in the fwd op, unpacking is needed to restore the gradients in a\nnon-packed form.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V, class OffsetCal, bool masked>\n__device__ void transducer_joint_single_vec_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                     const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                     int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                     bool bwdFasterDim, float scale, scalar_t* inGrad,\n                                                     int yBlockOffset = 0) {\n  const int batch = blockIdx.z;\n  const int x = blockIdx.y - yBlockOffset;\n  const int hOffset = blockIdx.x * C10_WARP_SIZE * V;\n  const int wid = threadIdx.y;\n  const int lid = threadIdx.x;\n  const int numWarp = blockDim.y;\n\n  // Figure out the vectorization type for mask\n  using mvec_t = mvec_type<V>;\n\n  OffsetCal offsetCal(batch, batchOffset, fLen, gLen, maxFLen, maxGLen, hiddenSize, packOutput, bwdFasterDim);\n  const auto maxXLen = offsetCal.getMaxXLen();\n  const auto myXLen = offsetCal.getMyXLen();\n  const auto myYLen = offsetCal.getMyYLen();\n  scalar_t* myInGrad = inGrad + batch * maxXLen * hiddenSize + x * hiddenSize + hOffset;\n  extern __shared__ char smem8[];\n  auto smem = reinterpret_cast<acc_t*>(smem8);\n\n  acc_t warpSum[V];\n  scalar_t inBuffer[V];\n  uint8_t maskBuffer[V];\n  scalar_t outBuffer[V];\n  auto myInGradVec = reinterpret_cast<vec_t*>(myInGrad);\n  auto outBufferVec = reinterpret_cast<vec_t*>(outBuffer);\n\n  if (x < myXLen) {\n    const auto myBatchOffset = offsetCal.getBatchOffset();\n    const auto strideX = offsetCal.getStrideX();\n    const auto strideY = offsetCal.getStrideY();\n    const scalar_t* myGrad = grad + myBatchOffset + x * strideX + hOffset;\n    const uint8_t* myMask = masked ? mask + myBatchOffset + x * strideX + hOffset : nullptr;\n\n    for (int i = 0; i < V; ++i) warpSum[i] = 0;\n\n    // Each warp reduces numYPerWarp \"y\" first\n    auto numYPerWarp = (myYLen + numWarp - 1) / numWarp;\n    for (int warpY = 0; warpY < numYPerWarp; ++warpY) {\n      auto y = wid * numYPerWarp + warpY;\n      auto myGradVec = reinterpret_cast<vec_t const*>(myGrad + y * strideY);\n      auto myMaskVec = masked ? reinterpret_cast<mvec_t const*>(myMask + y * strideY) : nullptr;\n      auto inBufferVec = reinterpret_cast<vec_t*>(inBuffer);\n      auto maskBufferVec = reinterpret_cast<mvec_t*>(maskBuffer);\n      if (hOffset + lid * V < hiddenSize and y < myYLen) {\n        *inBufferVec = myGradVec[lid];  // vectorized load\n        if (masked) {\n          *maskBufferVec = myMaskVec[lid];\n#pragma unroll\n          for (int i = 0; i < V; ++i) warpSum[i] += static_cast<acc_t>(inBuffer[i]) * maskBuffer[i] * scale;\n        } else {\n#pragma unroll\n          for (int i = 0; i < V; ++i) warpSum[i] += inBuffer[i];\n        }\n      }\n    }\n\n    // transpose partial sum in SMEM and reduce further using warpReduce\n    for (int i = 0; i < V; ++i) {\n      smem[lid * numWarp + wid] = warpSum[i];\n      __syncthreads();\n      auto sum = smem[wid * C10_WARP_SIZE + lid];\n\n      if (hOffset + (wid * C10_WARP_SIZE / numWarp) * V < hiddenSize) {\n        sum = warpReduce(sum, numWarp);\n        if (lid % numWarp == 0) {\n          outBuffer[i] = sum;\n        }\n      }\n      __syncthreads();\n    }\n\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // a a b b c c d d\n    // example of 4 warps (a, b, c, d) with 8 threads per warp\n    // Each warp need 8 / 4 = 2 threads to write the results.\n    if (lid % numWarp == 0 and hOffset + (wid * C10_WARP_SIZE / numWarp + lid / numWarp) * V < hiddenSize)\n      myInGradVec[wid * C10_WARP_SIZE / numWarp + lid / numWarp] = *outBufferVec;\n  } else if (wid == 0 and hOffset + lid * V < hiddenSize) {\n    // Need to ensure the grad is zero for don't care region\n    myInGradVec[lid] = 0;\n  }\n}\n\n/*\nVecotrized version of transducer_joint_combined_backward\nCall transducer_joint_single_vec_backward twice on two input tensors.\nThe two bwd ops are launched together, the first op uses blockIdx.y < maxFLen, and the second op\nuses the rest.\nWhen ReLU and/or dropout are performed in the fwd pass, this operation becomes a masked operation,\nand mask contains the mask information.\n*/\ntemplate <typename scalar_t, typename acc_t, typename vec_t, int V, class OffsetCal, bool masked>\n__global__ void transducer_joint_combined_vec_backward(const scalar_t* grad, const uint8_t* mask, const int* fLen,\n                                                       const int* gLen, const int64_t* batchOffset, int64_t maxFLen,\n                                                       int64_t maxGLen, int64_t hiddenSize, bool packOutput,\n                                                       float scale, scalar_t* fGrad, scalar_t* gGrad) {\n  if (blockIdx.y < maxFLen) {\n    transducer_joint_single_vec_backward<scalar_t, acc_t, vec_t, V, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, false, scale, fGrad);\n  } else {\n    transducer_joint_single_vec_backward<scalar_t, acc_t, vec_t, V, OffsetCal, masked>(\n        grad, mask, fLen, gLen, batchOffset, maxFLen, maxGLen, hiddenSize, packOutput, true, scale, gGrad, maxFLen);\n  }\n}\n\nstd::vector<torch::Tensor> transducer_joint_cuda_forward(torch::Tensor f, torch::Tensor g, torch::Tensor fLen,\n                                                         torch::Tensor gLen, torch::Tensor batchOffset,\n                                                         int64_t packedBatch, int opt, bool packOutput, bool relu,\n                                                         bool dropout, float dropoutProb, int tileSize) {\n  auto tensorOpt = f.options();\n  auto dtype = f.scalar_type();\n  const auto batchSize = f.size(0);\n  const auto maxFLen = f.size(1);\n  const auto maxGLen = g.size(1);\n  const auto hiddenSize = f.size(2);\n  bool masked = dropout or relu;\n\n  int64_t* batchOffsetPtr = nullptr;\n  torch::Tensor sum, mask;\n  auto maskOpt = tensorOpt.dtype(torch::kUInt8);\n  if (!packOutput) {\n    sum = torch::empty({batchSize, maxFLen, maxGLen, hiddenSize}, tensorOpt);\n    batchOffsetPtr = nullptr;\n    if (masked) mask = torch::empty({batchSize, maxFLen, maxGLen, hiddenSize}, maskOpt);\n  } else {\n    sum = torch::empty({packedBatch, hiddenSize}, tensorOpt);\n    batchOffsetPtr = batchOffset.data_ptr<int64_t>();\n    if (masked) mask = torch::empty({packedBatch, hiddenSize}, maskOpt);\n  }\n  uint8_t* maskPtr = masked ? mask.data_ptr<uint8_t>() : nullptr;\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  TORCH_CHECK(opt == 0 or opt == 1, \"Got an invalid optimization level \", opt);\n  // Simple heuristics\n  const int numThread =\n      std::min(128, (static_cast<int>(hiddenSize) + C10_WARP_SIZE - 1) / C10_WARP_SIZE * C10_WARP_SIZE);\n\n  if (opt == 0) {\n    // vanilla kernel\n    const int threads = numThread;\n    const dim3 blocks(maxGLen, maxFLen, batchSize);\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_joint_forward\", ([&] {\n          transducer_joint_forward<scalar_t, OffsetCalFwd><<<blocks, threads, 0, stream>>>(\n              f.data_ptr<scalar_t>(), g.data_ptr<scalar_t>(), fLen.data_ptr<int>(), gLen.data_ptr<int>(),\n              batchOffsetPtr, maxFLen, maxGLen, hiddenSize, packOutput, sum.data_ptr<scalar_t>());\n        }));\n  }\n  if (opt == 1) {\n    // tiled version. For simplicity, assume tileF == tileG, even though the kernel can\n    // support more general cases.\n    const int threads = numThread;\n    const int hiddenPerBlock = numThread;\n    const int hiddenBlock = (hiddenSize + hiddenPerBlock - 1) / hiddenPerBlock;\n    const dim3 blocks((maxGLen + tileSize - 1) / tileSize * hiddenBlock, (maxFLen + tileSize - 1) / tileSize,\n                      batchSize);\n\n    TORCH_CHECK(tileSize == 1 or tileSize == 2 or tileSize == 4, \"Expected tileSize to be in [1, 2, 4], but got \",\n                tileSize);\n\n    at::PhiloxCudaState rng_engine_inputs;\n    if (masked) {\n      // set up PRG when the input is masked. rng_engine_inputs will be used as a space filler\n      // for non-masked calls.\n      // Therefore no need to initialize.\n      c10::optional<at::Generator> gen_;\n      auto gen = at::get_generator_or_default<at::CUDAGeneratorImpl>(gen_, at::cuda::detail::getDefaultCUDAGenerator());\n      // counterOffset records how many cuRAND calls each thread makes. For a tiled kernel,\n      // each thread processes tileF * tileG output elements.\n      int64_t counterOffset = tileSize * tileSize;\n      {\n        std::lock_guard<std::mutex> lock(gen->mutex_);\n        rng_engine_inputs = gen->philox_cuda_state(counterOffset);\n      }\n    }\n\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n        dtype, \"transducer_joint_forward\", ([&] {\n          void (*kernel)(const scalar_t*, const scalar_t*, const int*, const int*, const int64_t*, int64_t, int64_t,\n                         int64_t, int64_t, bool, bool, bool, float, at::PhiloxCudaState, scalar_t*, uint8_t*);\n          if (masked) {\n            switch (tileSize) {\n              case 2:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 2, 2, 4, OffsetCalFwd, true>;\n                break;\n              case 4:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 4, 4, 4, OffsetCalFwd, true>;\n                break;\n            }\n          } else {\n            switch (tileSize) {\n              case 1:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 1, 1, 4, OffsetCalFwd, false>;\n                break;\n              case 2:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 2, 2, 4, OffsetCalFwd, false>;\n                break;\n              case 4:\n                kernel = &transducer_joint_tiled_forward<scalar_t, 4, 4, 4, OffsetCalFwd, false>;\n                break;\n            }\n          }\n\n          kernel<<<blocks, threads, 0, stream>>>(f.data_ptr<scalar_t>(), g.data_ptr<scalar_t>(), fLen.data_ptr<int>(),\n                                                 gLen.data_ptr<int>(), batchOffsetPtr, maxFLen, maxGLen, hiddenSize,\n                                                 hiddenPerBlock, packOutput, relu, dropout, 1.0f - dropoutProb,\n                                                 rng_engine_inputs, sum.data_ptr<scalar_t>(), maskPtr);\n        }));\n  }\n\n  C10_CUDA_CHECK(hipGetLastError());\n  if (masked)\n    return {sum, mask};\n  else\n    return {sum};\n}\n\nstd::vector<torch::Tensor> transducer_joint_cuda_backward(std::vector<torch::Tensor> in, torch::Tensor fLen,\n                                                          torch::Tensor gLen, torch::Tensor batchOffset, int maxFLen,\n                                                          int maxGLen, bool packOutput, float scale) {\n  auto grad = in[0];\n  bool masked = (in.size() == 2);\n  uint8_t* maskPtr = masked ? in[1].data_ptr<uint8_t>() : nullptr;\n\n  auto tensorOpt = grad.options();\n  auto dtype = grad.scalar_type();\n  const int batchSize = fLen.size(0);\n  const int hiddenSize = grad.size(-1);\n\n  const auto deviceProperties = at::cuda::getCurrentDeviceProperties();\n  const int maxNumWarp = deviceProperties->maxThreadsPerBlock / C10_WARP_SIZE;\n\n  torch::Tensor fGrad = torch::empty({batchSize, maxFLen, hiddenSize}, tensorOpt);\n  torch::Tensor gGrad = torch::empty({batchSize, maxGLen, hiddenSize}, tensorOpt);\n\n  int64_t* batchOffsetPtr = (!packOutput) ? nullptr : batchOffset.data_ptr<int64_t>();\n\n  // The number \"y\" I would like each thread to work on\n  const int workPerThread = 32;\n  // Since the bwd for f and g have the same thread block size, we need to use the max of the two.\n  int numWarp = largestPowerOfTwo((std::max(maxFLen, maxGLen) + workPerThread - 1) / workPerThread);\n  // Would like to have at least 2 warps\n  numWarp = std::max(2, numWarp);\n  // cap on the maximum number of warps allowed\n  numWarp = std::min(maxNumWarp, numWarp);\n\n  // Need smem for transposing the partial sum. The partial sum is in a matrix of the shape\n  // numWarp x warpSize\n  const int smemSize = numWarp * C10_WARP_SIZE;\n  const dim3 threads(C10_WARP_SIZE, numWarp, 1);\n\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(\n      dtype, \"transducer_joint_cuda_backward_kernel\", ([&] {\n        auto gradPtr = grad.data_ptr<scalar_t>();\n        auto fLenPtr = fLen.data_ptr<int>();\n        auto gLenPtr = gLen.data_ptr<int>();\n        auto fGradPtr = fGrad.data_ptr<scalar_t>();\n        auto gGradPtr = gGrad.data_ptr<scalar_t>();\n\n        // resolve the acc_t type\n        using acc_t = at::acc_type<scalar_t, true>;\n        using vec_t = uint64_t;\n\n        constexpr int vectFactor = sizeof(vec_t) / sizeof(scalar_t);\n        constexpr int vecAlignment = std::alignment_of<vec_t>::value;\n\n        // if all input and output tensors meet the alignment requirement\n        bool memAlign = (reinterpret_cast<uint64_t>(gradPtr) % vecAlignment == 0) and\n                        (reinterpret_cast<uint64_t>(fGradPtr) % vecAlignment == 0) and\n                        (reinterpret_cast<uint64_t>(gGradPtr) % vecAlignment == 0);\n\n        if (vectFactor > 1 and hiddenSize % vectFactor == 0 and memAlign) {\n          // If vectorization helps and the alignment requirement is met, use the vectorized\n          // kernel. For simplicity, hiddenSize needs to be a multiple vecFactor.\n          const dim3 blocks((hiddenSize + C10_WARP_SIZE * vectFactor - 1) / (C10_WARP_SIZE * vectFactor),\n                            maxFLen + maxGLen, batchSize);\n          if (masked) {\n            transducer_joint_combined_vec_backward<scalar_t, acc_t, vec_t, vectFactor, OffsetCalBwd, true>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          } else {\n            transducer_joint_combined_vec_backward<scalar_t, acc_t, vec_t, vectFactor, OffsetCalBwd, false>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          }\n        } else {\n          const dim3 blocks((hiddenSize + C10_WARP_SIZE - 1) / C10_WARP_SIZE, maxFLen + maxGLen, batchSize);\n          if (masked) {\n            transducer_joint_combined_backward<scalar_t, acc_t, OffsetCalBwd, true>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          } else {\n            transducer_joint_combined_backward<scalar_t, acc_t, OffsetCalBwd, false>\n                <<<blocks, threads, smemSize * sizeof(acc_t)>>>(gradPtr, maskPtr, fLenPtr, gLenPtr, batchOffsetPtr,\n                                                                maxFLen, maxGLen, hiddenSize, packOutput, scale,\n                                                                fGradPtr, gGradPtr);\n          }\n        }\n      }));\n\n  return {fGrad, gGrad};\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/transducer/transducer_joint_kernel.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n */\n#include <assert.h>\n\n#include <cub/cub.cuh>\n\n#include \"group_norm_nhwc.h\"\n#include \"macros.h\"\n#include \"traits.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// F O R W A R D\n//\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_fwd_sum_kernel(Group_norm_nhwc_fwd_params params) {\n  // The traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // The object in charge of doing the sums for the different blocks.\n  typedef cub::BlockScan<Group_sums, THREADS_PER_BLOCK> Block_scan;\n\n  // Allocate shared memory for Block_scan.\n  __shared__ typename Block_scan::TempStorage temp_storage;\n  // Allocate shared memory for the groups. We could reduce the amount of shared memory reserved.\n  __shared__ float2 smem[THREADS_PER_BLOCK];\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // The sums.\n  float sum = 0.f, sum_sq = 0.f;\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 v2 = IOTraits::zero();\n    if (ci < params.c) {\n      v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n    }\n\n    // Extract the two values.\n    float2 f2 = IOTraits::unpack(v2);\n\n    // Update the sum.\n    sum += f2.x + f2.y;\n    // Update the sum of squares.\n    sum_sq += f2.x * f2.x + f2.y * f2.y;\n  }\n\n  // The group that thread works on and the channel in the group (modulus).\n  int gj = threadIdx.x * 2 / params.channels_per_group;\n  int cj = threadIdx.x * 2 - params.channels_per_group * gj;\n\n  // The data for the summations.\n  Group_sums inp{cj == 0 ? 1 : 0, sum, sum_sq};\n\n  // Do the segmented scan.\n  Group_sums out;\n  Block_scan(temp_storage).InclusiveScan(inp, out, Group_sums_op());\n\n  // Store the results for the groups in shared memory (to produce coalesced stores later).\n  if (cj == params.channels_per_group - 2 /* 2 channels per thread */) {\n    smem[gj] = make_float2(out.sum, out.sum_sq);\n  }\n\n  // Make sure the data is in shared memory.\n  __syncthreads();\n\n  // The global group index.\n  int gk = blockIdx.x * params.groups_per_block + threadIdx.x;\n\n  // Threads that have nothing left to do, exit.\n  if (threadIdx.x >= params.groups_per_block || gk >= params.groups) {\n    return;\n  }\n\n  // The first threads (those storing to global memory, load the values).\n  float2 sums = smem[threadIdx.x];\n\n  // Store to global memory.\n  atomicAdd(&params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gk], sums.x);\n  atomicAdd(&params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gk], sums.y);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_setup(Group_norm_nhwc_fwd_params& params, size_t& zeroed_red_buffer_elts) {\n  // The pre-computed dimensions.\n  params.hw = params.h * params.w;\n  params.hwc = params.c * params.hw;\n\n  // The number of channels per group.\n  params.channels_per_group = params.c / params.groups;\n  // The inverse to compute the mean/variance.\n  params.inv_hwc_per_group = 1.f / (float)(params.hw * params.channels_per_group);\n\n  // Define the number of blocks per activation map. That's a simple heuristic.\n  int blocks_per_act_slice = 0;\n  if (params.c >= 1280) {\n    blocks_per_act_slice = 128 / params.n;\n  } else if (params.c >= 640) {\n    blocks_per_act_slice = 256 / params.n;\n  } else {\n    blocks_per_act_slice = 512 / params.n;\n  }\n\n  // Clamp to at least 1 to avoid divide-by-zero when batch size is large.\n  blocks_per_act_slice = max(blocks_per_act_slice, 1);\n\n  // Make sure we launch blocks per activation is no less than activations\n  blocks_per_act_slice = min(blocks_per_act_slice, div_up(params.hw, params.n));\n\n  // Define how many activations are computed per block.\n  params.acts_per_block = div_up(params.hw, blocks_per_act_slice);\n  // The number of channels per block.\n  params.channels_per_block = 320;\n  // Special case to deal with 30 channels per group.\n  if (params.channels_per_block % params.channels_per_group != 0) {\n    params.channels_per_block = 240;\n  }\n\n  // Special case to deal with 70 channels per group.\n  if (params.c == 2240) {\n    params.channels_per_block = 280;\n  } else if (params.c == 832) {\n    params.channels_per_block = 208;\n  }\n\n  if (params.c % params.channels_per_block != 0) {\n    if (params.c % 512 == 0 && params.c != 1536 && params.c != 3072 && params.c % 448 != 0) {\n      params.channels_per_block = 512;\n    } else if (params.c % 42 == 0) {\n      params.channels_per_block = 336;\n    } else if (params.c % 384 == 0) {\n      params.channels_per_block = 384;\n    } else if (params.c % 256 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 256;\n    } else if (params.c % 128 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 128;\n    } else if (params.c % 448 == 0 && params.c % 392 != 0) {\n      params.channels_per_block = 448;\n    } else if (params.c % 392 == 0) {\n      params.channels_per_block = 392;\n    }\n  }\n\n  // The number of groups per block.\n  params.groups_per_block = params.channels_per_block / params.channels_per_group;\n\n  // Make sure the number of channels is a multiple of the number of channels per block.\n  assert(params.c % params.channels_per_block == 0);\n  // Make sure a group does not span multiple blocks.\n  assert(params.channels_per_block % params.channels_per_group == 0);\n\n  // The number of elements in the reduction buffer (for the sums and sums of squared).\n  zeroed_red_buffer_elts = params.n * params.groups * 2;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_sum(const Group_norm_nhwc_fwd_params& params, cudaStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  // Launch the kernel.\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(cudaGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_fwd_scale_kernel(Group_norm_nhwc_fwd_params params) {\n  // The traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // Load the sum and sum of squares for the group.\n  float sum = 0.f, sum_sq = 0.f;\n  if (gi < params.groups) {\n    sum = params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gi];\n    sum_sq = params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gi];\n  }\n\n  // Load gamma/beta.\n  float2 gamma_f2, beta_f2;\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n  }\n\n  // Compute the mean.\n  float mean = sum * params.inv_hwc_per_group;\n  // Compute the variance.\n  float var = sum_sq * params.inv_hwc_per_group - (mean * mean);\n  // Compute the inverse of the stddev.\n  float inv_stddev = var <= 0.f ? 1.f : rsqrtf(var + params.epsilon);\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The src/dst offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 v2 = IOTraits::zero();\n    if (ci < params.c) {\n      v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n    }\n\n    // Extract the two values.\n    float2 f2 = IOTraits::unpack(v2);\n\n    // Normalize the channels.\n    f2.x = (f2.x - mean) * inv_stddev;\n    f2.y = (f2.y - mean) * inv_stddev;\n\n    // Scale by gamma and add beta.\n    f2.x = gamma_f2.x * f2.x + beta_f2.x;\n    f2.y = gamma_f2.y * f2.y + beta_f2.y;\n\n    // Apply Swish if needed.\n    if (params.with_swish) {\n      f2.x = f2.x * sigmoid(f2.x);\n      f2.y = f2.y * sigmoid(f2.y);\n    }\n\n    // Store the scaled values.\n    if (ci < params.c) {\n      *reinterpret_cast<IOType2*>(&reinterpret_cast<IOType*>(params.y)[offset]) = IOTraits::pack(f2);\n    }\n  }\n\n  // Write the sums if needed.\n  if (params.sums != nullptr && gi < params.groups) {\n    float2 sums;\n    sums.x = sum * params.inv_hwc_per_group;\n    sums.y = sum_sq * params.inv_hwc_per_group;\n    params.sums[ni * params.groups + gi] = sums;\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_scale(const Group_norm_nhwc_fwd_params& params, cudaStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(cudaGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n */\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n\n#include <hipcub/hipcub.hpp>\n\n#include \"group_norm_nhwc.h\"\n#include \"macros.h\"\n#include \"traits.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// F O R W A R D\n//\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_fwd_sum_kernel(Group_norm_nhwc_fwd_params params) {\n  // The traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // The object in charge of doing the sums for the different blocks.\n  typedef hipcub::BlockScan<Group_sums, THREADS_PER_BLOCK> Block_scan;\n\n  // Allocate shared memory for Block_scan.\n  __shared__ typename Block_scan::TempStorage temp_storage;\n  // Allocate shared memory for the groups. We could reduce the amount of shared memory reserved.\n  __shared__ float2 smem[THREADS_PER_BLOCK];\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // The sums.\n  float sum = 0.f, sum_sq = 0.f;\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 v2 = IOTraits::zero();\n    if (ci < params.c) {\n      v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n    }\n\n    // Extract the two values.\n    float2 f2 = IOTraits::unpack(v2);\n\n    // Update the sum.\n    sum += f2.x + f2.y;\n    // Update the sum of squares.\n    sum_sq += f2.x * f2.x + f2.y * f2.y;\n  }\n\n  // The group that thread works on and the channel in the group (modulus).\n  int gj = threadIdx.x * 2 / params.channels_per_group;\n  int cj = threadIdx.x * 2 - params.channels_per_group * gj;\n\n  // The data for the summations.\n  Group_sums inp{cj == 0 ? 1 : 0, sum, sum_sq};\n\n  // Do the segmented scan.\n  Group_sums out;\n  Block_scan(temp_storage).InclusiveScan(inp, out, Group_sums_op());\n\n  // Store the results for the groups in shared memory (to produce coalesced stores later).\n  if (cj == params.channels_per_group - 2 /* 2 channels per thread */) {\n    smem[gj] = make_float2(out.sum, out.sum_sq);\n  }\n\n  // Make sure the data is in shared memory.\n  __syncthreads();\n\n  // The global group index.\n  int gk = blockIdx.x * params.groups_per_block + threadIdx.x;\n\n  // Threads that have nothing left to do, exit.\n  if (threadIdx.x >= params.groups_per_block || gk >= params.groups) {\n    return;\n  }\n\n  // The first threads (those storing to global memory, load the values).\n  float2 sums = smem[threadIdx.x];\n\n  // Store to global memory.\n  atomicAdd(&params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gk], sums.x);\n  atomicAdd(&params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gk], sums.y);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_setup(Group_norm_nhwc_fwd_params& params, size_t& zeroed_red_buffer_elts) {\n  // The pre-computed dimensions.\n  params.hw = params.h * params.w;\n  params.hwc = params.c * params.hw;\n\n  // The number of channels per group.\n  params.channels_per_group = params.c / params.groups;\n  // The inverse to compute the mean/variance.\n  params.inv_hwc_per_group = 1.f / (float)(params.hw * params.channels_per_group);\n\n  // Define the number of blocks per activation map. That's a simple heuristic.\n  int blocks_per_act_slice = 0;\n  if (params.c >= 1280) {\n    blocks_per_act_slice = 128 / params.n;\n  } else if (params.c >= 640) {\n    blocks_per_act_slice = 256 / params.n;\n  } else {\n    blocks_per_act_slice = 512 / params.n;\n  }\n\n  // Clamp to at least 1 to avoid divide-by-zero when batch size is large.\n  blocks_per_act_slice = max(blocks_per_act_slice, 1);\n\n  // Make sure we launch blocks per activation is no less than activations\n  blocks_per_act_slice = min(blocks_per_act_slice, div_up(params.hw, params.n));\n\n  // Define how many activations are computed per block.\n  params.acts_per_block = div_up(params.hw, blocks_per_act_slice);\n  // The number of channels per block.\n  params.channels_per_block = 320;\n  // Special case to deal with 30 channels per group.\n  if (params.channels_per_block % params.channels_per_group != 0) {\n    params.channels_per_block = 240;\n  }\n\n  // Special case to deal with 70 channels per group.\n  if (params.c == 2240) {\n    params.channels_per_block = 280;\n  } else if (params.c == 832) {\n    params.channels_per_block = 208;\n  }\n\n  if (params.c % params.channels_per_block != 0) {\n    if (params.c % 512 == 0 && params.c != 1536 && params.c != 3072 && params.c % 448 != 0) {\n      params.channels_per_block = 512;\n    } else if (params.c % 42 == 0) {\n      params.channels_per_block = 336;\n    } else if (params.c % 384 == 0) {\n      params.channels_per_block = 384;\n    } else if (params.c % 256 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 256;\n    } else if (params.c % 128 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 128;\n    } else if (params.c % 448 == 0 && params.c % 392 != 0) {\n      params.channels_per_block = 448;\n    } else if (params.c % 392 == 0) {\n      params.channels_per_block = 392;\n    }\n  }\n\n  // The number of groups per block.\n  params.groups_per_block = params.channels_per_block / params.channels_per_group;\n\n  // Make sure the number of channels is a multiple of the number of channels per block.\n  assert(params.c % params.channels_per_block == 0);\n  // Make sure a group does not span multiple blocks.\n  assert(params.channels_per_block % params.channels_per_group == 0);\n\n  // The number of elements in the reduction buffer (for the sums and sums of squared).\n  zeroed_red_buffer_elts = params.n * params.groups * 2;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_sum(const Group_norm_nhwc_fwd_params& params, hipStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  // Launch the kernel.\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_sum_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(hipGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_fwd_scale_kernel(Group_norm_nhwc_fwd_params params) {\n  // The traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // Load the sum and sum of squares for the group.\n  float sum = 0.f, sum_sq = 0.f;\n  if (gi < params.groups) {\n    sum = params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gi];\n    sum_sq = params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gi];\n  }\n\n  // Load gamma/beta.\n  float2 gamma_f2, beta_f2;\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n  }\n\n  // Compute the mean.\n  float mean = sum * params.inv_hwc_per_group;\n  // Compute the variance.\n  float var = sum_sq * params.inv_hwc_per_group - (mean * mean);\n  // Compute the inverse of the stddev.\n  float inv_stddev = var <= 0.f ? 1.f : rsqrtf(var + params.epsilon);\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The src/dst offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 v2 = IOTraits::zero();\n    if (ci < params.c) {\n      v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n    }\n\n    // Extract the two values.\n    float2 f2 = IOTraits::unpack(v2);\n\n    // Normalize the channels.\n    f2.x = (f2.x - mean) * inv_stddev;\n    f2.y = (f2.y - mean) * inv_stddev;\n\n    // Scale by gamma and add beta.\n    f2.x = gamma_f2.x * f2.x + beta_f2.x;\n    f2.y = gamma_f2.y * f2.y + beta_f2.y;\n\n    // Apply Swish if needed.\n    if (params.with_swish) {\n      f2.x = f2.x * sigmoid(f2.x);\n      f2.y = f2.y * sigmoid(f2.y);\n    }\n\n    // Store the scaled values.\n    if (ci < params.c) {\n      *reinterpret_cast<IOType2*>(&reinterpret_cast<IOType*>(params.y)[offset]) = IOTraits::pack(f2);\n    }\n  }\n\n  // Write the sums if needed.\n  if (params.sums != nullptr && gi < params.groups) {\n    float2 sums;\n    sums.x = sum * params.inv_hwc_per_group;\n    sums.y = sum_sq * params.inv_hwc_per_group;\n    params.sums[ni * params.groups + gi] = sums;\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_fwd_two_passes_scale(const Group_norm_nhwc_fwd_params& params, hipStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_fwd_scale_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(hipGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/apex/apex/contrib/csrc/group_norm/group_norm_nhwc_fwd_two_pass.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n */\n#include <assert.h>\n\n#include <cub/cub.cuh>\n\n#include \"group_norm_nhwc.h\"\n#include \"macros.h\"\n#include \"traits.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// B A C K W A R D\n//\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_bwd_sum_kernel(Group_norm_nhwc_bwd_params params) {\n  // The IO traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The object in charge of doing the sums for the different blocks.\n  typedef cub::BlockScan<Group_sums, THREADS_PER_BLOCK> Block_scan;\n\n  // Allocate shared memory for Block_scan.\n  __shared__ typename Block_scan::TempStorage temp_storage;\n  // Allocate shared memory for the groups. We could reduce the amount of shared memory reserved.\n  __shared__ float2 smem[THREADS_PER_BLOCK];\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // The sums from the fwd pass.\n  float2 fwd = params.sums[ni * params.groups + gi];\n  // The mean of X (computed during the fwd pass -- one value per batch*group).\n  float x_mean = fwd.x;\n  // The mean of squares of X (computed during the fwd pass -- one value per batch*group).\n  float x_sq_mean = fwd.y;\n  // The variance.\n  float x_var = x_sq_mean - x_mean * x_mean;\n  // The reciprocal of the standard deviation (i.e. 1.f / sqrt(var + epsilon)).\n  float rcp_x_stddev = x_var <= 0.f ? 1.f : 1.f / sqrtf(x_var + params.epsilon);\n\n  // Load gamma.\n  float2 gamma_f2 = make_float2(0.f, 0.f);\n  float2 beta_f2 = make_float2(0.f, 0.f);\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    if (params.with_swish) {\n      beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n    }\n  }\n\n  // The group that thread works on and the channel in the group (modulus).\n  int gj = threadIdx.x * 2 / params.channels_per_group;\n  int cj = threadIdx.x * 2 - params.channels_per_group * gj;\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // The gradients for gamma/beta.\n  float2 dgamma = make_float2(0.f, 0.f), dbeta = make_float2(0.f, 0.f);\n  // Accumulated gradients for dgrad calculation\n  float mean_1 = 0.f, mean_2 = 0.f;\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 x_v2 = IOTraits::zero();\n    IOType2 dy_v2 = IOTraits::zero();\n    if (ci < params.c) {\n      x_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n      dy_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.dy)[offset]);\n    }\n\n    // Extract the two half values.\n    float2 x_f2 = IOTraits::unpack(x_v2);\n    float2 dy_f2 = IOTraits::unpack(dy_v2);\n\n    // X - X_mean.\n    float x_minus_x_mean_x = x_f2.x - x_mean;\n    float x_minus_x_mean_y = x_f2.y - x_mean;\n\n    // Normalize X.\n    float x_norm_x = x_minus_x_mean_x * rcp_x_stddev;\n    float x_norm_y = x_minus_x_mean_y * rcp_x_stddev;\n\n    if (params.with_swish) {\n      float x_gn_x = x_norm_x * gamma_f2.x + beta_f2.x;\n      float x_gn_y = x_norm_y * gamma_f2.y + beta_f2.y;\n      float s_x = sigmoid(x_gn_x);\n      float s_y = sigmoid(x_gn_y);\n      dy_f2.x = dy_f2.x * s_x * (1.f + x_gn_x * (1.f - s_x));\n      dy_f2.y = dy_f2.y * s_y * (1.f + x_gn_y * (1.f - s_y));\n    }\n\n    // Update beta.\n    dbeta.x += dy_f2.x;\n    dbeta.y += dy_f2.y;\n\n    // Update dgamma.\n    dgamma.x += dy_f2.x * x_norm_x;\n    dgamma.y += dy_f2.y * x_norm_y;\n\n    // The gradient that enters the x_norm node.\n    float dx_norm_x = dy_f2.x * gamma_f2.x;\n    float dx_norm_y = dy_f2.y * gamma_f2.y;\n\n    // Add to the 1st mean.\n    mean_1 += dx_norm_x * x_norm_x;\n    mean_1 += dx_norm_y * x_norm_y;\n\n    // Add to the 2nd mean.\n    mean_2 += dx_norm_x;\n    mean_2 += dx_norm_y;\n  }\n\n  // The data for the summations.\n  Group_sums inp{cj == 0 ? 1 : 0, mean_1, mean_2};\n\n  // Do the segmented scan.\n  Group_sums out;\n  Block_scan(temp_storage).InclusiveScan(inp, out, Group_sums_op());\n\n  // Store the results for the groups in shared memory (to produce coalesced stores later).\n  if (cj == params.channels_per_group - 2 /* 2 channels per thread */) {\n    smem[gj] = make_float2(out.sum, out.sum_sq);\n  }\n\n  // Make sure the data is in shared memory.\n  __syncthreads();\n\n  // The global group index.\n  int gk = blockIdx.x * params.groups_per_block + threadIdx.x;\n\n  // The first threads (those storing to global memory, load the values).\n  float2 sums = smem[threadIdx.x];\n\n  // Store to global memory.\n  if (threadIdx.x < params.groups_per_block && gk < params.groups) {\n    atomicAdd(&params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gk], sums.x);\n    atomicAdd(&params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gk], sums.y);\n  }\n\n  // The base pointer for the gradients for gamma and beta.\n  float* dgamma_beta_ptr = &params.zeroed_red_buffer[params.n * params.groups * 2];\n\n  // The 1st channel in the output tensor. NOTE: Two channels per thread store interleaved.\n  int ck = blockIdx.x * params.channels_per_block + threadIdx.x;\n\n  // Store dgamma and dbeta as well.\n  if (ck < params.c) {\n    atomicAdd(&dgamma_beta_ptr[0 * params.c + 0 * blockDim.x + ck], dgamma.x);\n    atomicAdd(&dgamma_beta_ptr[0 * params.c + 1 * blockDim.x + ck], dgamma.y);\n    atomicAdd(&dgamma_beta_ptr[1 * params.c + 0 * blockDim.x + ck], dbeta.x);\n    atomicAdd(&dgamma_beta_ptr[1 * params.c + 1 * blockDim.x + ck], dbeta.y);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_setup(Group_norm_nhwc_bwd_params& params, size_t& zeroed_red_buffer_elts) {\n  // The pre-computed dimensions.\n  params.hw = params.h * params.w;\n  params.hwc = params.c * params.hw;\n\n  // The number of channels per group.\n  params.channels_per_group = params.c / params.groups;\n  // The inverse to compute the mean/variance.\n  params.inv_hwc_per_group = 1.f / (float)(params.hw * params.channels_per_group);\n\n  // Define the number of blocks per activation map. That's a simple heuristic.\n  int blocks_per_act_slice = 0;\n  if (params.c >= 1280) {\n    blocks_per_act_slice = 128 / params.n;\n  } else if (params.c >= 640) {\n    blocks_per_act_slice = 256 / params.n;\n  } else {\n    blocks_per_act_slice = 512 / params.n;\n  }\n\n  // Clamp to at least 1 to avoid divide-by-zero when batch size is large.\n  blocks_per_act_slice = max(blocks_per_act_slice, 1);\n\n  // Make sure we launch blocks per activation is no less than activations\n  blocks_per_act_slice = min(blocks_per_act_slice, div_up(params.hw, params.n));\n\n  // Define how many activations are computed per block.\n  params.acts_per_block = div_up(params.hw, blocks_per_act_slice);\n\n  // The number of channels per block.\n  params.channels_per_block = 320;\n  // Special case to deal with 30 channels per group.\n  if (params.channels_per_block % params.channels_per_group != 0) {\n    params.channels_per_block = 240;\n  }\n\n  // Special case to deal with 70 channels per group.\n  if (params.c == 2240) {\n    params.channels_per_block = 280;\n  } else if (params.c == 832) {\n    params.channels_per_block = 208;\n  }\n\n  if (params.c % params.channels_per_block != 0) {\n    if (params.c % 512 == 0 && params.c != 1536 && params.c != 3072 && params.c % 448 != 0) {\n      params.channels_per_block = 512;\n    } else if (params.c % 42 == 0) {\n      params.channels_per_block = 336;\n    } else if (params.c % 384 == 0) {\n      params.channels_per_block = 384;\n    } else if (params.c % 256 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 256;\n    } else if (params.c % 128 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 128;\n    } else if (params.c % 448 == 0 && params.c % 392 != 0) {\n      params.channels_per_block = 448;\n    } else if (params.c % 392 == 0) {\n      params.channels_per_block = 392;\n    }\n  }\n\n  // The number of groups per block.\n  params.groups_per_block = params.channels_per_block / params.channels_per_group;\n\n  // Make sure the number of channels is a multiple of the number of channels per block.\n  assert(params.c % params.channels_per_block == 0);\n  // Make sure a group does not span multiple blocks.\n  assert(params.channels_per_block % params.channels_per_group == 0);\n\n  // The number of elements in the reduction buffer (for the sums and sums of squared).\n  zeroed_red_buffer_elts = params.n * params.groups * 2 + params.c * 2;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_sum(const Group_norm_nhwc_bwd_params& params, cudaStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(cudaGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_bwd_scale_kernel(Group_norm_nhwc_bwd_params params) {\n  // The IO traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // Load the gradients for the group.\n  float mean_1 = 0.f, mean_2 = 0.f;\n  if (gi < params.groups) {\n    mean_1 = params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gi];\n    mean_2 = params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gi];\n  }\n\n  // The sums from the fwd pass.\n  float2 fwd = params.sums[ni * params.groups + gi];\n  // The mean of X (computed during the fwd pass -- one value per batch*group).\n  float x_mean = fwd.x;\n  // The mean of squares of X (computed during the fwd pass -- one value per batch*group).\n  float x_sq_mean = fwd.y;\n  // The variance.\n  float x_var = x_sq_mean - x_mean * x_mean;\n  // The reciprocal of the standard deviation (i.e. 1.f / sqrt(var + epsilon)).\n  float rcp_x_stddev = x_var <= 0.f ? 1.f : 1.f / sqrtf(x_var + params.epsilon);\n\n  // Mutiply by 1/(HWC) to get real mean\n  mean_1 *= params.inv_hwc_per_group;\n  mean_2 *= params.inv_hwc_per_group;\n\n  // Load gamma.\n  float2 gamma_f2 = make_float2(0.f, 0.f);\n  float2 beta_f2 = make_float2(0.f, 0.f);\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    if (params.with_swish) {\n      beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n    }\n  }\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The src/dst offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 x_v2 = IOTraits::zero();\n    IOType2 dy_v2 = IOTraits::zero();\n    if (ci < params.c) {\n      x_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n      dy_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.dy)[offset]);\n    }\n\n    // Extract the two half values.\n    float2 x_f2 = IOTraits::unpack(x_v2);\n    float2 dy_f2 = IOTraits::unpack(dy_v2);\n\n    // X - X_mean.\n    float2 x_minus_x_mean_f2;\n    x_minus_x_mean_f2.x = x_f2.x - x_mean;\n    x_minus_x_mean_f2.y = x_f2.y - x_mean;\n\n    // Normalize X.\n    float2 x_norm_f2;\n    x_norm_f2.x = x_minus_x_mean_f2.x * rcp_x_stddev;\n    x_norm_f2.y = x_minus_x_mean_f2.y * rcp_x_stddev;\n\n    if (params.with_swish) {\n      float x_gn_x = x_norm_f2.x * gamma_f2.x + beta_f2.x;\n      float x_gn_y = x_norm_f2.y * gamma_f2.y + beta_f2.y;\n      float s_x = sigmoid(x_gn_x);\n      float s_y = sigmoid(x_gn_y);\n      dy_f2.x = dy_f2.x * s_x * (1.f + x_gn_x * (1.f - s_x));\n      dy_f2.y = dy_f2.y * s_y * (1.f + x_gn_y * (1.f - s_y));\n    }\n\n    // The gradient that enters the x_norm node.\n    float2 dx_norm;\n    dx_norm.x = dy_f2.x * gamma_f2.x;\n    dx_norm.y = dy_f2.y * gamma_f2.y;\n\n    // The gradient along the input path.\n    float2 dx;\n    dx.x = (dx_norm.x - (x_norm_f2.x * mean_1 + mean_2)) * rcp_x_stddev;\n    dx.y = (dx_norm.y - (x_norm_f2.y * mean_1 + mean_2)) * rcp_x_stddev;\n\n    // Store the scaled values.\n    if (ci < params.c) {\n      *reinterpret_cast<IOType2*>(&reinterpret_cast<IOType*>(params.dx)[offset]) = IOTraits::pack(dx);\n    }\n  }\n\n  // Load gamma/beta and convert to half.\n  if (blockIdx.y > 0 || blockIdx.z > 0 || ci >= params.c) {\n    return;\n  }\n\n  // The base pointer for the gradients for gamma and beta.\n  float* dgamma_beta_ptr = &params.zeroed_red_buffer[params.n * params.groups * 2];\n\n  // The 1st channel in the output tensor. NOTE: Two channels per thread store interleaved.\n  int ck = blockIdx.x * params.channels_per_block + threadIdx.x;\n\n  // Load the FP32 version of dgamma and dbeta.\n  float2 dgamma, dbeta;\n  if (ck < params.c) {\n    dgamma.x = dgamma_beta_ptr[0 * params.c + 0 * blockDim.x + ck];\n    dgamma.y = dgamma_beta_ptr[0 * params.c + 1 * blockDim.x + ck];\n    dbeta.x = dgamma_beta_ptr[1 * params.c + 0 * blockDim.x + ck];\n    dbeta.y = dgamma_beta_ptr[1 * params.c + 1 * blockDim.x + ck];\n\n    // Convert to half2 and store to memory.\n    *reinterpret_cast<WType2*>(&reinterpret_cast<WType*>(params.dgamma)[ci]) = WTraits::pack(dgamma);\n    *reinterpret_cast<WType2*>(&reinterpret_cast<WType*>(params.dbeta)[ci]) = WTraits::pack(dbeta);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_scale(const Group_norm_nhwc_bwd_params& params, cudaStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(cudaGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n */\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n\n#include <hipcub/hipcub.hpp>\n\n#include \"group_norm_nhwc.h\"\n#include \"macros.h\"\n#include \"traits.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// B A C K W A R D\n//\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_bwd_sum_kernel(Group_norm_nhwc_bwd_params params) {\n  // The IO traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The object in charge of doing the sums for the different blocks.\n  typedef hipcub::BlockScan<Group_sums, THREADS_PER_BLOCK> Block_scan;\n\n  // Allocate shared memory for Block_scan.\n  __shared__ typename Block_scan::TempStorage temp_storage;\n  // Allocate shared memory for the groups. We could reduce the amount of shared memory reserved.\n  __shared__ float2 smem[THREADS_PER_BLOCK];\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // The sums from the fwd pass.\n  float2 fwd = params.sums[ni * params.groups + gi];\n  // The mean of X (computed during the fwd pass -- one value per batch*group).\n  float x_mean = fwd.x;\n  // The mean of squares of X (computed during the fwd pass -- one value per batch*group).\n  float x_sq_mean = fwd.y;\n  // The variance.\n  float x_var = x_sq_mean - x_mean * x_mean;\n  // The reciprocal of the standard deviation (i.e. 1.f / sqrt(var + epsilon)).\n  float rcp_x_stddev = x_var <= 0.f ? 1.f : 1.f / sqrtf(x_var + params.epsilon);\n\n  // Load gamma.\n  float2 gamma_f2 = make_float2(0.f, 0.f);\n  float2 beta_f2 = make_float2(0.f, 0.f);\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    if (params.with_swish) {\n      beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n    }\n  }\n\n  // The group that thread works on and the channel in the group (modulus).\n  int gj = threadIdx.x * 2 / params.channels_per_group;\n  int cj = threadIdx.x * 2 - params.channels_per_group * gj;\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // The gradients for gamma/beta.\n  float2 dgamma = make_float2(0.f, 0.f), dbeta = make_float2(0.f, 0.f);\n  // Accumulated gradients for dgrad calculation\n  float mean_1 = 0.f, mean_2 = 0.f;\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 x_v2 = IOTraits::zero();\n    IOType2 dy_v2 = IOTraits::zero();\n    if (ci < params.c) {\n      x_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n      dy_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.dy)[offset]);\n    }\n\n    // Extract the two half values.\n    float2 x_f2 = IOTraits::unpack(x_v2);\n    float2 dy_f2 = IOTraits::unpack(dy_v2);\n\n    // X - X_mean.\n    float x_minus_x_mean_x = x_f2.x - x_mean;\n    float x_minus_x_mean_y = x_f2.y - x_mean;\n\n    // Normalize X.\n    float x_norm_x = x_minus_x_mean_x * rcp_x_stddev;\n    float x_norm_y = x_minus_x_mean_y * rcp_x_stddev;\n\n    if (params.with_swish) {\n      float x_gn_x = x_norm_x * gamma_f2.x + beta_f2.x;\n      float x_gn_y = x_norm_y * gamma_f2.y + beta_f2.y;\n      float s_x = sigmoid(x_gn_x);\n      float s_y = sigmoid(x_gn_y);\n      dy_f2.x = dy_f2.x * s_x * (1.f + x_gn_x * (1.f - s_x));\n      dy_f2.y = dy_f2.y * s_y * (1.f + x_gn_y * (1.f - s_y));\n    }\n\n    // Update beta.\n    dbeta.x += dy_f2.x;\n    dbeta.y += dy_f2.y;\n\n    // Update dgamma.\n    dgamma.x += dy_f2.x * x_norm_x;\n    dgamma.y += dy_f2.y * x_norm_y;\n\n    // The gradient that enters the x_norm node.\n    float dx_norm_x = dy_f2.x * gamma_f2.x;\n    float dx_norm_y = dy_f2.y * gamma_f2.y;\n\n    // Add to the 1st mean.\n    mean_1 += dx_norm_x * x_norm_x;\n    mean_1 += dx_norm_y * x_norm_y;\n\n    // Add to the 2nd mean.\n    mean_2 += dx_norm_x;\n    mean_2 += dx_norm_y;\n  }\n\n  // The data for the summations.\n  Group_sums inp{cj == 0 ? 1 : 0, mean_1, mean_2};\n\n  // Do the segmented scan.\n  Group_sums out;\n  Block_scan(temp_storage).InclusiveScan(inp, out, Group_sums_op());\n\n  // Store the results for the groups in shared memory (to produce coalesced stores later).\n  if (cj == params.channels_per_group - 2 /* 2 channels per thread */) {\n    smem[gj] = make_float2(out.sum, out.sum_sq);\n  }\n\n  // Make sure the data is in shared memory.\n  __syncthreads();\n\n  // The global group index.\n  int gk = blockIdx.x * params.groups_per_block + threadIdx.x;\n\n  // The first threads (those storing to global memory, load the values).\n  float2 sums = smem[threadIdx.x];\n\n  // Store to global memory.\n  if (threadIdx.x < params.groups_per_block && gk < params.groups) {\n    atomicAdd(&params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gk], sums.x);\n    atomicAdd(&params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gk], sums.y);\n  }\n\n  // The base pointer for the gradients for gamma and beta.\n  float* dgamma_beta_ptr = &params.zeroed_red_buffer[params.n * params.groups * 2];\n\n  // The 1st channel in the output tensor. NOTE: Two channels per thread store interleaved.\n  int ck = blockIdx.x * params.channels_per_block + threadIdx.x;\n\n  // Store dgamma and dbeta as well.\n  if (ck < params.c) {\n    atomicAdd(&dgamma_beta_ptr[0 * params.c + 0 * blockDim.x + ck], dgamma.x);\n    atomicAdd(&dgamma_beta_ptr[0 * params.c + 1 * blockDim.x + ck], dgamma.y);\n    atomicAdd(&dgamma_beta_ptr[1 * params.c + 0 * blockDim.x + ck], dbeta.x);\n    atomicAdd(&dgamma_beta_ptr[1 * params.c + 1 * blockDim.x + ck], dbeta.y);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_setup(Group_norm_nhwc_bwd_params& params, size_t& zeroed_red_buffer_elts) {\n  // The pre-computed dimensions.\n  params.hw = params.h * params.w;\n  params.hwc = params.c * params.hw;\n\n  // The number of channels per group.\n  params.channels_per_group = params.c / params.groups;\n  // The inverse to compute the mean/variance.\n  params.inv_hwc_per_group = 1.f / (float)(params.hw * params.channels_per_group);\n\n  // Define the number of blocks per activation map. That's a simple heuristic.\n  int blocks_per_act_slice = 0;\n  if (params.c >= 1280) {\n    blocks_per_act_slice = 128 / params.n;\n  } else if (params.c >= 640) {\n    blocks_per_act_slice = 256 / params.n;\n  } else {\n    blocks_per_act_slice = 512 / params.n;\n  }\n\n  // Clamp to at least 1 to avoid divide-by-zero when batch size is large.\n  blocks_per_act_slice = max(blocks_per_act_slice, 1);\n\n  // Make sure we launch blocks per activation is no less than activations\n  blocks_per_act_slice = min(blocks_per_act_slice, div_up(params.hw, params.n));\n\n  // Define how many activations are computed per block.\n  params.acts_per_block = div_up(params.hw, blocks_per_act_slice);\n\n  // The number of channels per block.\n  params.channels_per_block = 320;\n  // Special case to deal with 30 channels per group.\n  if (params.channels_per_block % params.channels_per_group != 0) {\n    params.channels_per_block = 240;\n  }\n\n  // Special case to deal with 70 channels per group.\n  if (params.c == 2240) {\n    params.channels_per_block = 280;\n  } else if (params.c == 832) {\n    params.channels_per_block = 208;\n  }\n\n  if (params.c % params.channels_per_block != 0) {\n    if (params.c % 512 == 0 && params.c != 1536 && params.c != 3072 && params.c % 448 != 0) {\n      params.channels_per_block = 512;\n    } else if (params.c % 42 == 0) {\n      params.channels_per_block = 336;\n    } else if (params.c % 384 == 0) {\n      params.channels_per_block = 384;\n    } else if (params.c % 256 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 256;\n    } else if (params.c % 128 == 0 && params.c % 448 != 0 && params.c % 392 != 0) {\n      params.channels_per_block = 128;\n    } else if (params.c % 448 == 0 && params.c % 392 != 0) {\n      params.channels_per_block = 448;\n    } else if (params.c % 392 == 0) {\n      params.channels_per_block = 392;\n    }\n  }\n\n  // The number of groups per block.\n  params.groups_per_block = params.channels_per_block / params.channels_per_group;\n\n  // Make sure the number of channels is a multiple of the number of channels per block.\n  assert(params.c % params.channels_per_block == 0);\n  // Make sure a group does not span multiple blocks.\n  assert(params.channels_per_block % params.channels_per_group == 0);\n\n  // The number of elements in the reduction buffer (for the sums and sums of squared).\n  zeroed_red_buffer_elts = params.n * params.groups * 2 + params.c * 2;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_sum(const Group_norm_nhwc_bwd_params& params, hipStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_sum_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(hipGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Traits_, int THREADS_PER_BLOCK>\n__global__ void group_norm_nhwc_bwd_scale_kernel(Group_norm_nhwc_bwd_params params) {\n  // The IO traits.\n  using Traits = Traits_;\n  // The IO traits.\n  using IOTraits = typename Traits::IOTraits;\n  // The Weights traits.\n  using WTraits = typename Traits::WTraits;\n\n  // The IO type\n  using IOType = typename IOTraits::Type;\n  // The IO doubled type\n  using IOType2 = typename IOTraits::Type2;\n\n  // Weights type\n  using WType = typename WTraits::Type;\n  // Weights doubled type\n  using WType2 = typename WTraits::Type2;\n\n  // The instance in the batch.\n  int ni = blockIdx.z;\n  // The channel loaded by that thread (2 channels per thread for F16x2).\n  int ci = blockIdx.x * params.channels_per_block + threadIdx.x * 2;\n  // The group that thread works on and the channel in the group (modulus).\n  int gi = ci / params.channels_per_group;\n\n  // Load the gradients for the group.\n  float mean_1 = 0.f, mean_2 = 0.f;\n  if (gi < params.groups) {\n    mean_1 = params.zeroed_red_buffer[(2 * ni + 0) * params.groups + gi];\n    mean_2 = params.zeroed_red_buffer[(2 * ni + 1) * params.groups + gi];\n  }\n\n  // The sums from the fwd pass.\n  float2 fwd = params.sums[ni * params.groups + gi];\n  // The mean of X (computed during the fwd pass -- one value per batch*group).\n  float x_mean = fwd.x;\n  // The mean of squares of X (computed during the fwd pass -- one value per batch*group).\n  float x_sq_mean = fwd.y;\n  // The variance.\n  float x_var = x_sq_mean - x_mean * x_mean;\n  // The reciprocal of the standard deviation (i.e. 1.f / sqrt(var + epsilon)).\n  float rcp_x_stddev = x_var <= 0.f ? 1.f : 1.f / sqrtf(x_var + params.epsilon);\n\n  // Mutiply by 1/(HWC) to get real mean\n  mean_1 *= params.inv_hwc_per_group;\n  mean_2 *= params.inv_hwc_per_group;\n\n  // Load gamma.\n  float2 gamma_f2 = make_float2(0.f, 0.f);\n  float2 beta_f2 = make_float2(0.f, 0.f);\n  if (ci < params.c) {\n    gamma_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.gamma)[ci]));\n    if (params.with_swish) {\n      beta_f2 = WTraits::unpack(*reinterpret_cast<const WType2*>(&reinterpret_cast<const WType*>(params.beta)[ci]));\n    }\n  }\n\n  // The first activation loaded by that block.\n  int hw_begin = blockIdx.y * params.acts_per_block;\n  // The last activation loaded by that block.\n  int hw_end = min((int64_t)hw_begin + params.acts_per_block, params.hw);\n\n  // Iterate over the activations to compute the sums.\n  for (int hwi = hw_begin; hwi < hw_end; ++hwi) {\n    // The src/dst offset.\n    int64_t offset = (int64_t)ni * params.hwc + hwi * params.c + ci;\n\n    // Fetch two channels per thread.\n    IOType2 x_v2 = IOTraits::zero();\n    IOType2 dy_v2 = IOTraits::zero();\n    if (ci < params.c) {\n      x_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.x)[offset]);\n      dy_v2 = *reinterpret_cast<const IOType2*>(&reinterpret_cast<const IOType*>(params.dy)[offset]);\n    }\n\n    // Extract the two half values.\n    float2 x_f2 = IOTraits::unpack(x_v2);\n    float2 dy_f2 = IOTraits::unpack(dy_v2);\n\n    // X - X_mean.\n    float2 x_minus_x_mean_f2;\n    x_minus_x_mean_f2.x = x_f2.x - x_mean;\n    x_minus_x_mean_f2.y = x_f2.y - x_mean;\n\n    // Normalize X.\n    float2 x_norm_f2;\n    x_norm_f2.x = x_minus_x_mean_f2.x * rcp_x_stddev;\n    x_norm_f2.y = x_minus_x_mean_f2.y * rcp_x_stddev;\n\n    if (params.with_swish) {\n      float x_gn_x = x_norm_f2.x * gamma_f2.x + beta_f2.x;\n      float x_gn_y = x_norm_f2.y * gamma_f2.y + beta_f2.y;\n      float s_x = sigmoid(x_gn_x);\n      float s_y = sigmoid(x_gn_y);\n      dy_f2.x = dy_f2.x * s_x * (1.f + x_gn_x * (1.f - s_x));\n      dy_f2.y = dy_f2.y * s_y * (1.f + x_gn_y * (1.f - s_y));\n    }\n\n    // The gradient that enters the x_norm node.\n    float2 dx_norm;\n    dx_norm.x = dy_f2.x * gamma_f2.x;\n    dx_norm.y = dy_f2.y * gamma_f2.y;\n\n    // The gradient along the input path.\n    float2 dx;\n    dx.x = (dx_norm.x - (x_norm_f2.x * mean_1 + mean_2)) * rcp_x_stddev;\n    dx.y = (dx_norm.y - (x_norm_f2.y * mean_1 + mean_2)) * rcp_x_stddev;\n\n    // Store the scaled values.\n    if (ci < params.c) {\n      *reinterpret_cast<IOType2*>(&reinterpret_cast<IOType*>(params.dx)[offset]) = IOTraits::pack(dx);\n    }\n  }\n\n  // Load gamma/beta and convert to half.\n  if (blockIdx.y > 0 || blockIdx.z > 0 || ci >= params.c) {\n    return;\n  }\n\n  // The base pointer for the gradients for gamma and beta.\n  float* dgamma_beta_ptr = &params.zeroed_red_buffer[params.n * params.groups * 2];\n\n  // The 1st channel in the output tensor. NOTE: Two channels per thread store interleaved.\n  int ck = blockIdx.x * params.channels_per_block + threadIdx.x;\n\n  // Load the FP32 version of dgamma and dbeta.\n  float2 dgamma, dbeta;\n  if (ck < params.c) {\n    dgamma.x = dgamma_beta_ptr[0 * params.c + 0 * blockDim.x + ck];\n    dgamma.y = dgamma_beta_ptr[0 * params.c + 1 * blockDim.x + ck];\n    dbeta.x = dgamma_beta_ptr[1 * params.c + 0 * blockDim.x + ck];\n    dbeta.y = dgamma_beta_ptr[1 * params.c + 1 * blockDim.x + ck];\n\n    // Convert to half2 and store to memory.\n    *reinterpret_cast<WType2*>(&reinterpret_cast<WType*>(params.dgamma)[ci]) = WTraits::pack(dgamma);\n    *reinterpret_cast<WType2*>(&reinterpret_cast<WType*>(params.dbeta)[ci]) = WTraits::pack(dbeta);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid group_norm_nhwc_bwd_two_passes_scale(const Group_norm_nhwc_bwd_params& params, hipStream_t stream) {\n  // The dimension of the grid.\n  dim3 grid;\n\n  // The number of blocks to compute all the channels.\n  grid.x = params.c / params.channels_per_block;\n  // The number of blocks to compute all the activations in a given instance.\n  grid.y = div_up(params.hw, params.acts_per_block);\n  // The number of instances.\n  grid.z = params.n;\n\n  if (params.precision == PrecisionMode::FP16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOFp16W)\n  } else if (params.precision == PrecisionMode::FP16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOBf16W)\n  } else if (params.precision == PrecisionMode::FP16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp16IOFp32W)\n  } else if (params.precision == PrecisionMode::BF16IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOFp16W)\n  } else if (params.precision == PrecisionMode::BF16IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOBf16W)\n  } else if (params.precision == PrecisionMode::BF16IOFP32W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Bf16IOFp32W)\n  } else if (params.precision == PrecisionMode::FP32IOFP16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOFp16W)\n  } else if (params.precision == PrecisionMode::FP32IOBF16W) {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOBf16W)\n  } else {\n    CALL_TWO_PASS_KERNEL(group_norm_nhwc_bwd_scale_kernel, Fp32IOFp32W)\n  }\n\n  // Make sure it launched ok.\n  CHECK_CUDA(hipGetLastError());\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/apex/apex/contrib/csrc/group_norm/group_norm_nhwc_bwd_two_pass.cu"}
{"input": "#include <cuda.h>\n#include <cuda_runtime.h>\n#include <stdio.h>\n\n#include <cmath>\n\n#include \"ATen/ATen.h\"\n#include \"ATen/TensorUtils.h\"\n#include \"ATen/cuda/CUDAContext.h\"\n#include \"ATen/cuda/detail/IndexUtils.cuh\"\n// #include \"ATen/Type.h\"\n#include \"ATen/AccumulateType.h\"\n#include \"multi_tensor_apply.cuh\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\n#include \"type_shim.h\"\n\ntypedef enum {\n  ADAM_MODE_0 = 0,  // eps under square root\n  ADAM_MODE_1 = 1   // eps outside square root\n} adamMode_t;\n\ntemplate <typename T, typename GRAD_T>\n__global__ void adam_cuda_kernel(T* __restrict__ p,\n                                 GRAD_T* __restrict__ p_copy,  // For mixed precision training, pass NULL if not needed\n                                 T* __restrict__ m, T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1,\n                                 const float b2, const float eps, const float grad_scale, const float step_size,\n                                 const size_t tsize, adamMode_t mode, const float decay) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  for (int j = i; j < tsize; j += totThreads) {\n    T scaled_grad = g[j] / grad_scale;\n    m[j] = b1 * m[j] + (1 - b1) * scaled_grad;\n    v[j] = b2 * v[j] + (1 - b2) * scaled_grad * scaled_grad;\n    float denom;\n    if (mode == ADAM_MODE_0)\n      denom = sqrtf(v[j] + eps);\n    else  // Mode 1\n      denom = sqrtf(v[j]) + eps;\n    float update = (m[j] / denom) + (decay * p[j]);\n    p[j] = p[j] - (step_size * update);\n    if (p_copy != NULL) p_copy[j] = (GRAD_T)p[j];\n  }\n}\n\ntemplate <int DEPTH, typename T, typename GRAD_T>\nstruct AdamFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<DEPTH>& tl,\n                                             const float b1, const float b2, const float eps, const float grad_scale,\n                                             const float step_size, adamMode_t mode, const float decay) {\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    T* p = (T*)tl.addresses[0][tensor_loc];\n    p += chunk_idx * chunk_size;\n    T* m = (T*)tl.addresses[1][tensor_loc];\n    m += chunk_idx * chunk_size;\n    T* v = (T*)tl.addresses[2][tensor_loc];\n    v += chunk_idx * chunk_size;\n    GRAD_T* g = (GRAD_T*)tl.addresses[3][tensor_loc];\n    g += chunk_idx * chunk_size;\n    GRAD_T* p_copy = NULL;\n    if (DEPTH == 5) {\n      p_copy = (GRAD_T*)tl.addresses[4][tensor_loc];\n      p_copy += chunk_idx * chunk_size;\n    }\n\n    n -= chunk_idx * chunk_size;\n\n    T incoming_p[ILP];\n    T incoming_m[ILP];\n    T incoming_v[ILP];\n    T incoming_g[ILP];\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(p) && is_aligned(m) && is_aligned(v) && is_aligned(g) &&\n        is_aligned(p_copy)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        GRAD_T tmp_g[ILP];\n        load_store(incoming_p, p, 0, i_start);\n        load_store(incoming_m, m, 0, i_start);\n        load_store(incoming_v, v, 0, i_start);\n        load_store(tmp_g, g, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          incoming_g[ii] = static_cast<T>(tmp_g[ii]);\n          T scaled_grad = incoming_g[ii] / grad_scale;\n          incoming_m[ii] = b1 * incoming_m[ii] + (1 - b1) * scaled_grad;\n          incoming_v[ii] = b2 * incoming_v[ii] + (1 - b2) * scaled_grad * scaled_grad;\n          float denom;\n          if (mode == ADAM_MODE_0)\n            denom = sqrtf(incoming_v[ii] + eps);\n          else  // Mode 1\n            denom = sqrtf(incoming_v[ii]) + eps;\n          float update = (incoming_m[ii] / denom) + (decay * incoming_p[ii]);\n          incoming_p[ii] = incoming_p[ii] - (step_size * update);\n          if (DEPTH == 5) tmp_g[ii] = static_cast<GRAD_T>(incoming_p[ii]);\n        }\n        load_store(p, incoming_p, i_start, 0);\n        load_store(m, incoming_m, i_start, 0);\n        load_store(v, incoming_v, i_start, 0);\n        if (DEPTH == 5) load_store(p_copy, tmp_g, i_start, 0);\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          incoming_p[ii] = 0;\n          incoming_m[ii] = 0;\n          incoming_v[ii] = 0;\n          incoming_g[ii] = 0;\n\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            incoming_p[ii] = p[i];\n            incoming_m[ii] = m[i];\n            incoming_v[ii] = v[i];\n            incoming_g[ii] = static_cast<T>(g[i]);\n          }\n        }\n\n        // note for clarification to future michael:\n        // From a pure memory dependency perspective, there's likely no point unrolling\n        // the write loop, since writes just fire off once their LDGs arrive.\n        // Put another way, the STGs are dependent on the LDGs, but not on each other.\n        // There is still compute ILP benefit from unrolling the loop though.\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int j = i_start + threadIdx.x + ii * blockDim.x;\n\n          if (j < n && j < chunk_size) {\n            T scaled_grad = incoming_g[ii] / grad_scale;\n            m[j] = b1 * incoming_m[ii] + (1 - b1) * scaled_grad;\n            v[j] = b2 * incoming_v[ii] + (1 - b2) * scaled_grad * scaled_grad;\n            float denom;\n            if (mode == ADAM_MODE_0)\n              denom = sqrtf(v[j] + eps);\n            else  // Mode 1\n              denom = sqrtf(v[j]) + eps;\n            float update = (m[j] / denom) + (decay * incoming_p[ii]);\n            p[j] = incoming_p[ii] - (step_size * update);\n            if (DEPTH == 5) p_copy[j] = (GRAD_T)p[j];\n          }\n        }\n      }\n    }\n  }\n};\n\nvoid fused_adam_cuda(at::Tensor& p, at::Tensor& p_copy, at::Tensor& m, at::Tensor& v, at::Tensor& g, float lr,\n                     float beta1, float beta2, float eps, float grad_scale, int step, int mode, int bias_correction,\n                     float decay) {\n  //        using namespace at;\n\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    DISPATCH_FLOAT_AND_HALF(g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n                            adam_cuda_kernel<accscalar_t, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n                                p.data_ptr<accscalar_t>(), p_copy.numel() ? p_copy.data_ptr<scalar_t_0>() : NULL,\n                                m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1,\n                                beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(g.scalar_type(), 0, \"adam_cuda_kernel\",\n                              adam_cuda_kernel<scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n                                  p.data_ptr<scalar_t_0>(),\n                                  NULL,  // don't output p_copy for fp32, it's wasted write\n                                  m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1,\n                                  beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid fused_adam_cuda_mt(int chunk_size, at::Tensor noop_flag,\n                        std::vector<std::vector<at::Tensor>> tensor_lists,  // p, m, v, g, p_copy\n                        float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                        int bias_correction, float decay) {\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  size_t tl_sz = tensor_lists.size();\n  TORCH_CHECK(tl_sz == 4 || tl_sz == 5, \"expected tensor lists of size 4 or 5\");\n\n  if (tensor_lists[3][0].scalar_type() == at::ScalarType::Half) {\n    // alher values should be fp32 for half gradients\n    TORCH_CHECK(tensor_lists[0][0].scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dich is done on the gradient type\n    if (tl_sz == 5) {\n      DISPATCH_FLOAT_AND_HALF(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                              using accscalar_t = at::acc_type<scalar_t_0, true>;\n                              multi_tensor_apply<5>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                    AdamFunctor<5, accscalar_t, scalar_t_0>(), beta1, beta2, eps,\n                                                    grad_scale, step_size, (adamMode_t)mode, decay););\n    } else {\n      DISPATCH_FLOAT_AND_HALF(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                              using accscalar_t = at::acc_type<scalar_t_0, true>;\n                              multi_tensor_apply<4>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                    AdamFunctor<4, accscalar_t, scalar_t_0>(), beta1, beta2, eps,\n                                                    grad_scale, step_size, (adamMode_t)mode, decay););\n    }\n  } else {\n    if (tl_sz == 5) {\n      DISPATCH_DOUBLE_AND_FLOAT(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                                multi_tensor_apply<5>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                      AdamFunctor<5, scalar_t_0, scalar_t_0>(), beta1, beta2, eps,\n                                                      grad_scale, step_size, (adamMode_t)mode, decay););\n    } else {\n      DISPATCH_DOUBLE_AND_FLOAT(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                                multi_tensor_apply<4>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                      AdamFunctor<4, scalar_t_0, scalar_t_0>(), beta1, beta2, eps,\n                                                      grad_scale, step_size, (adamMode_t)mode, decay););\n    }\n  }\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\ntemplate <typename FROM_T, typename TO_T>\n__device__ void convert(const FROM_T vi, TO_T& vo) {\n  vo = static_cast<TO_T>(vi);\n}\n\ntemplate <>\n__device__ void convert(const float vi, uint8_t& vo) {\n  union S {\n    float as_float;\n    int as_int;\n  };\n  S s;\n  s.as_float = vi;\n  s.as_int = s.as_int & 0xFF800000;\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_half = static_cast<at::Half>(vi + s.as_float / 8.0f);\n  vo = t.as_byte[1];\n}\n\ntemplate <>\n__device__ void convert(const uint8_t vi, float& vo) {\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_byte[0] = 0;\n  t.as_byte[1] = vi;\n  vo = static_cast<float>(t.as_half);\n}\n\ntemplate <>\n__device__ void convert(const at::Half vi, uint8_t& vo) {\n  union S {\n    float as_float;\n    int as_int;\n  };\n  S s;\n  s.as_float = static_cast<float>(vi);\n  s.as_int = s.as_int & 0xFF800000;\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_half = static_cast<at::Half>(vi + s.as_float / 8.0f);\n  vo = t.as_byte[1];\n}\n\ntemplate <>\n__device__ void convert(const uint8_t vi, at::Half& vo) {\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_byte[0] = 0;\n  t.as_byte[1] = vi;\n  vo = t.as_half;\n}\n\ntemplate <typename GRAD_T>\n__global__ void strided_check_finite_cuda_kernel(volatile int* noop_gmem, GRAD_T* __restrict__ p_copy,\n                                                 const size_t tsize, int stride, int clear_overflow_first) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock) * stride;\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock * stride;\n\n  if (clear_overflow_first) {\n    if (i == 0) {\n      *noop_gmem = 0;\n    }\n    __syncthreads();\n  }\n\n  for (int j = i; j < tsize; j += totThreads) {\n    GRAD_T pi = p_copy[j];\n    if (!isfinite(pi)) {\n      *noop_gmem = 1;\n    }\n  }\n}\ntemplate <>\n__global__ void strided_check_finite_cuda_kernel(volatile int* noop_gmem, uint8_t* __restrict__ p_copy,\n                                                 const size_t tsize, int stride, int clear_overflow_first) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock) * stride;\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock * stride;\n\n  if (clear_overflow_first) {\n    if (i == 0) {\n      *noop_gmem = 0;\n    }\n    __syncthreads();\n  }\n\n  for (int j = i; j < tsize; j += totThreads) {\n    at::Half pi;\n    convert(p_copy[j], pi);\n    if (!isfinite(pi)) {\n      *noop_gmem = 1;\n    }\n  }\n}\n\ntemplate <typename FROM_T, typename TO_T>\n__global__ void maybe_cast_kernel(volatile int* overflow_flag, const FROM_T* p_in, TO_T* p_out, const size_t tsize) {\n  if (overflow_flag && *overflow_flag != 0) return;\n\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  FROM_T pi[ILP];\n  TO_T po[ILP];\n\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      pi[ii] = 0;\n\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        pi[ii] = p_in[j];\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      convert(pi[ii], po[ii]);\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        p_out[j] = po[ii];\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename GRAD_T, typename REDU_T>\n__global__ void reversible_adam_cuda_kernel(\n    T* __restrict__ p,\n    REDU_T* __restrict__ p_copy,  // For mixed precision training, pass NULL if not needed\n    T* __restrict__ m, T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1, const float b2, const float eps,\n    const float grad_scale, const float step_size, const size_t tsize, adamMode_t mode, const float decay) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  T mi[ILP];\n  T vi[ILP];\n  T pi[ILP];\n  T gi[ILP];\n\n  bool overflow = false;\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      mi[ii] = T(0);\n      vi[ii] = T(0);\n      pi[ii] = T(0);\n      gi[ii] = GRAD_T(0);\n\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        pi[ii] = p[j];\n        mi[ii] = m[j];\n        vi[ii] = v[j];\n        gi[ii] = static_cast<T>(g[j]);\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      T scaled_grad = gi[ii] / grad_scale;\n      if (isfinite(scaled_grad)) {\n        mi[ii] = b1 * mi[ii] + (1 - b1) * scaled_grad;\n        vi[ii] = b2 * vi[ii] + (1 - b2) * scaled_grad * scaled_grad;\n        float denom;\n        if (mode == ADAM_MODE_0)\n          denom = sqrtf(vi[ii] + eps);\n        else  // Mode 1\n          denom = sqrtf(vi[ii]) + eps;\n        float update = (mi[ii] / denom) + (decay * pi[ii]);\n        pi[ii] = pi[ii] - (step_size * update);\n      } else {\n        overflow = true;\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        m[j] = mi[ii];\n        v[j] = vi[ii];\n        p[j] = pi[ii];\n        if (p_copy != NULL) {\n          convert(pi[ii], p_copy[j]);\n        }\n      }\n    }\n  }\n\n  if (p_copy != NULL) {\n    __syncthreads();\n    if (overflow) {\n      convert(float(INFINITY), p_copy[0]);\n    }\n  }\n}\n\ntemplate <typename T, typename GRAD_T>\n__global__ void maybe_adam_undo_cuda_kernel(volatile int* overflow_flag, T* __restrict__ p, T* __restrict__ m,\n                                            T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1,\n                                            const float b2, const float eps, const float grad_scale,\n                                            const float step_size, const size_t tsize, adamMode_t mode,\n                                            const float decay) {\n  // NB! Skip undo kernel when overflow flag is NOT set\n  if (overflow_flag && *overflow_flag == 0) return;\n\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  T mi[ILP];\n  T vi[ILP];\n  T pi[ILP];\n  T gi[ILP];\n\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      mi[ii] = T(0);\n      vi[ii] = T(0);\n      pi[ii] = T(0);\n      gi[ii] = GRAD_T(0);\n\n      int j = j_start + i * ILP;\n      if (j < tsize) {\n        pi[ii] = p[j];\n        mi[ii] = m[j];\n        vi[ii] = v[j];\n        gi[ii] = static_cast<T>(g[j]);\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      T scaled_grad = gi[ii] / grad_scale;\n      if (isfinite(scaled_grad)) {\n        float denom;\n        if (mode == ADAM_MODE_0)\n          denom = sqrtf(vi[ii] + eps);\n        else  // Mode 1\n          denom = sqrtf(vi[ii]) + eps;\n        pi[ii] = (pi[ii] + step_size * (mi[ii] / denom)) / (1.0f - step_size * decay);\n        mi[ii] = (mi[ii] - (1 - b1) * scaled_grad) / b1;\n        vi[ii] = (vi[ii] - (1 - b2) * scaled_grad * scaled_grad) / b2;\n        // Make sure round off errors don't create (small) negative value.\n        // This can happen if we have to revert the very first step.\n        vi[ii] = vi[ii] >= 0.0f ? vi[ii] : 0.0f;\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i * ILP;\n      if (j < tsize) {\n        m[j] = mi[ii];\n        v[j] = vi[ii];\n        p[j] = pi[ii];\n      }\n    }\n  }\n}\n\ntemplate <int DEPTH, typename FROM_T, typename TO_T>\nstruct MaybeCastFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* overflow_flag,\n                                             TensorListMetadata<DEPTH>& tl) {\n    if (overflow_flag && *overflow_flag != 0) return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    FROM_T* p_in = (FROM_T*)tl.addresses[0][tensor_loc];\n    p_in += chunk_idx * chunk_size;\n    TO_T* p_out = (TO_T*)tl.addresses[1][tensor_loc];\n    p_out += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n    int dim = chunk_size < n ? chunk_size : n;\n\n    FROM_T pi[ILP];\n    TO_T po[ILP];\n\n    for (int j_start = 0; j_start < dim; j_start += blockDim.x * ILP) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        pi[ii] = FROM_T(0);\n        int j = j_start + threadIdx.x + ii * blockDim.x;\n        if (j < dim) {\n          pi[ii] = p_in[j];\n        }\n      }\n\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        convert(pi[ii], po[ii]);\n      }\n\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int j = j_start + threadIdx.x + ii * blockDim.x;\n        if (j < dim) {\n          p_out[j] = po[ii];\n        }\n      }\n    }\n  }\n};\n\nvoid fused_strided_check_finite(at::Tensor& overflow_flag, at::Tensor& p_copy, int stride, int clear_overflow_first) {\n  // Get tensor size\n  int tsize = p_copy.numel();\n  int niter = (tsize + stride - 1) / stride;\n\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  // In order to avoid race condition, blocks must be 1 when clear_overflow_first flag is set.\n  const dim3 blocks(clear_overflow_first ? 1 : (niter + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p_copy), \"parameter tensor is too large to be indexed with int32\");\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  using namespace at;  // prevents \"toString is undefined\" errors\n  DISPATCH_FLOAT_HALF_AND_BYTE(\n      p_copy.scalar_type(), 0, \"check_finite_cuda_kernel\",\n      strided_check_finite_cuda_kernel<scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n          overflow_flag.data_ptr<int>(), p_copy.data_ptr<scalar_t_0>(), tsize, stride, clear_overflow_first););\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid fused_reversible_adam_cuda(at::Tensor& p, at::Tensor& p_copy, at::Tensor& m, at::Tensor& v, at::Tensor& g,\n                                float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                                int bias_correction, float decay) {\n  //      using namespace at;\n\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    if (p_copy.numel() == 0 || p_copy.scalar_type() == g.scalar_type()) {\n      DISPATCH_FLOAT_AND_HALF(\n          g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n          reversible_adam_cuda_kernel<accscalar_t, scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n              p.data_ptr<accscalar_t>(), p_copy.numel() ? p_copy.data_ptr<scalar_t_0>() : NULL,\n              m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps,\n              grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n    } else {\n      TORCH_CHECK(p_copy.scalar_type() == at::ScalarType::Byte, \"expected parameter to be of byte type\");\n      DISPATCH_FLOAT_AND_HALF(\n          g.scalar_type(), 0, \"adam_cuda_e5m2_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n          reversible_adam_cuda_kernel<accscalar_t, scalar_t_0, uint8_t><<<blocks, threadsPerBlock, 0, stream>>>(\n              p.data_ptr<accscalar_t>(), p_copy.data_ptr<uint8_t>(), m.data_ptr<accscalar_t>(),\n              v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps, grad_scale, step_size, tsize,\n              (adamMode_t)mode, decay););\n    }\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(g.scalar_type(), 0, \"adam_cuda_kernel\",\n                              reversible_adam_cuda_kernel<scalar_t_0, scalar_t_0, scalar_t_0>\n                              <<<blocks, threadsPerBlock, 0, stream>>>(\n                                  p.data_ptr<scalar_t_0>(),\n                                  NULL,  // don't output p_copy for fp32, it's wasted write\n                                  m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1,\n                                  beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid maybe_cast_cuda(at::Tensor& overflow_flag, at::Tensor& p_in, at::Tensor& p_out) {\n  // Get tensor size\n  int tsize = p_in.numel();\n  TORCH_CHECK(tsize == p_out.numel(), \"p_in.numel() must equal p_out.numel()\");\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p_in), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  DISPATCH_FLOAT_HALF_AND_BYTE(p_in.scalar_type(), 0,\n                               \"maybe_cast_cuda\" DISPATCH_FLOAT_HALF_AND_BYTE(\n                                   p_out.scalar_type(), 1, \"maybe_cast_cuda\",\n                                   maybe_cast_kernel<scalar_t_0, scalar_t_1><<<blocks, threadsPerBlock, 0, stream>>>(\n                                       overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL,\n                                       p_in.data_ptr<scalar_t_0>(), p_out.data_ptr<scalar_t_1>(), tsize);))\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid maybe_cast_cuda_mt(int chunk_size, at::Tensor overflow_flag,\n                        std::vector<std::vector<at::Tensor>> tensor_lists)  // p_in, p_out\n{\n  // Constants\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  size_t tl_sz = tensor_lists.size();\n  TORCH_CHECK(tl_sz == 2, \"expected tensor lists of size 2\");\n\n  DISPATCH_FLOAT_HALF_AND_BYTE(\n      tensor_lists[0][0].scalar_type(), 0, \"maybe_cast_cuda_mt_kernel\",\n      DISPATCH_FLOAT_HALF_AND_BYTE(tensor_lists[1][0].scalar_type(), 1, \"maybe_cast_cuda_mt_kernel\",\n                                   multi_tensor_apply<2>(BLOCK_SIZE, chunk_size, overflow_flag, tensor_lists,\n                                                         MaybeCastFunctor<2, scalar_t_0, scalar_t_1>());))\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid fused_maybe_adam_undo_cuda(at::Tensor& overflow_flag, at::Tensor& p, at::Tensor& m, at::Tensor& v, at::Tensor& g,\n                                float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                                int bias_correction, float decay) {\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    DISPATCH_FLOAT_AND_HALF(g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n                            maybe_adam_undo_cuda_kernel<accscalar_t, scalar_t_0>\n                            <<<blocks, threadsPerBlock, 0, stream>>>(\n                                overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL, p.data_ptr<accscalar_t>(),\n                                m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1,\n                                beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(\n        g.scalar_type(), 0, \"adam_cuda_kernel\",\n        maybe_adam_undo_cuda_kernel<scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n            overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL, p.data_ptr<scalar_t_0>(),\n            m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps, grad_scale,\n            step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(cudaGetLastError());\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <stdio.h>\n\n#include <cmath>\n\n#include \"ATen/ATen.h\"\n#include \"ATen/TensorUtils.h\"\n#include \"ATen/cuda/CUDAContext.h\"\n#include \"ATen/cuda/detail/IndexUtils.cuh\"\n// #include \"ATen/Type.h\"\n#include \"ATen/AccumulateType.h\"\n#include \"multi_tensor_apply.cuh\"\n\n#define BLOCK_SIZE 512\n#define ILP 4\n\ntemplate <typename T>\n__device__ __forceinline__ bool is_aligned(T* p) {\n  return ((uint64_t)p) % (ILP * sizeof(T)) == 0;\n}\n\ntemplate <typename T>\n__device__ __forceinline__ void load_store(T* dst, T* src, int dst_offset, int src_offset) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LT;\n  ((LT*)dst)[dst_offset] = ((LT*)src)[src_offset];\n}\n\n#include \"type_shim.h\"\n\ntypedef enum {\n  ADAM_MODE_0 = 0,  // eps under square root\n  ADAM_MODE_1 = 1   // eps outside square root\n} adamMode_t;\n\ntemplate <typename T, typename GRAD_T>\n__global__ void adam_cuda_kernel(T* __restrict__ p,\n                                 GRAD_T* __restrict__ p_copy,  // For mixed precision training, pass NULL if not needed\n                                 T* __restrict__ m, T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1,\n                                 const float b2, const float eps, const float grad_scale, const float step_size,\n                                 const size_t tsize, adamMode_t mode, const float decay) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  for (int j = i; j < tsize; j += totThreads) {\n    T scaled_grad = g[j] / grad_scale;\n    m[j] = b1 * m[j] + (1 - b1) * scaled_grad;\n    v[j] = b2 * v[j] + (1 - b2) * scaled_grad * scaled_grad;\n    float denom;\n    if (mode == ADAM_MODE_0)\n      denom = sqrtf(v[j] + eps);\n    else  // Mode 1\n      denom = sqrtf(v[j]) + eps;\n    float update = (m[j] / denom) + (decay * p[j]);\n    p[j] = p[j] - (step_size * update);\n    if (p_copy != NULL) p_copy[j] = (GRAD_T)p[j];\n  }\n}\n\ntemplate <int DEPTH, typename T, typename GRAD_T>\nstruct AdamFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* noop_gmem, TensorListMetadata<DEPTH>& tl,\n                                             const float b1, const float b2, const float eps, const float grad_scale,\n                                             const float step_size, adamMode_t mode, const float decay) {\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    T* p = (T*)tl.addresses[0][tensor_loc];\n    p += chunk_idx * chunk_size;\n    T* m = (T*)tl.addresses[1][tensor_loc];\n    m += chunk_idx * chunk_size;\n    T* v = (T*)tl.addresses[2][tensor_loc];\n    v += chunk_idx * chunk_size;\n    GRAD_T* g = (GRAD_T*)tl.addresses[3][tensor_loc];\n    g += chunk_idx * chunk_size;\n    GRAD_T* p_copy = NULL;\n    if (DEPTH == 5) {\n      p_copy = (GRAD_T*)tl.addresses[4][tensor_loc];\n      p_copy += chunk_idx * chunk_size;\n    }\n\n    n -= chunk_idx * chunk_size;\n\n    T incoming_p[ILP];\n    T incoming_m[ILP];\n    T incoming_v[ILP];\n    T incoming_g[ILP];\n\n    // to make things simple, we put aligned case in a different code path\n    if (n % ILP == 0 && chunk_size % ILP == 0 && is_aligned(p) && is_aligned(m) && is_aligned(v) && is_aligned(g) &&\n        is_aligned(p_copy)) {\n      for (int i_start = threadIdx.x; i_start * ILP < n && i_start * ILP < chunk_size; i_start += blockDim.x) {\n        // load\n        GRAD_T tmp_g[ILP];\n        load_store(incoming_p, p, 0, i_start);\n        load_store(incoming_m, m, 0, i_start);\n        load_store(incoming_v, v, 0, i_start);\n        load_store(tmp_g, g, 0, i_start);\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          incoming_g[ii] = static_cast<T>(tmp_g[ii]);\n          T scaled_grad = incoming_g[ii] / grad_scale;\n          incoming_m[ii] = b1 * incoming_m[ii] + (1 - b1) * scaled_grad;\n          incoming_v[ii] = b2 * incoming_v[ii] + (1 - b2) * scaled_grad * scaled_grad;\n          float denom;\n          if (mode == ADAM_MODE_0)\n            denom = sqrtf(incoming_v[ii] + eps);\n          else  // Mode 1\n            denom = sqrtf(incoming_v[ii]) + eps;\n          float update = (incoming_m[ii] / denom) + (decay * incoming_p[ii]);\n          incoming_p[ii] = incoming_p[ii] - (step_size * update);\n          if (DEPTH == 5) tmp_g[ii] = static_cast<GRAD_T>(incoming_p[ii]);\n        }\n        load_store(p, incoming_p, i_start, 0);\n        load_store(m, incoming_m, i_start, 0);\n        load_store(v, incoming_v, i_start, 0);\n        if (DEPTH == 5) load_store(p_copy, tmp_g, i_start, 0);\n      }\n    } else {\n      for (int i_start = 0; i_start < n && i_start < chunk_size; i_start += blockDim.x * ILP) {\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          incoming_p[ii] = 0;\n          incoming_m[ii] = 0;\n          incoming_v[ii] = 0;\n          incoming_g[ii] = 0;\n\n          int i = i_start + threadIdx.x + ii * blockDim.x;\n          if (i < n && i < chunk_size) {\n            incoming_p[ii] = p[i];\n            incoming_m[ii] = m[i];\n            incoming_v[ii] = v[i];\n            incoming_g[ii] = static_cast<T>(g[i]);\n          }\n        }\n\n        // note for clarification to future michael:\n        // From a pure memory dependency perspective, there's likely no point unrolling\n        // the write loop, since writes just fire off once their LDGs arrive.\n        // Put another way, the STGs are dependent on the LDGs, but not on each other.\n        // There is still compute ILP benefit from unrolling the loop though.\n#pragma unroll\n        for (int ii = 0; ii < ILP; ii++) {\n          int j = i_start + threadIdx.x + ii * blockDim.x;\n\n          if (j < n && j < chunk_size) {\n            T scaled_grad = incoming_g[ii] / grad_scale;\n            m[j] = b1 * incoming_m[ii] + (1 - b1) * scaled_grad;\n            v[j] = b2 * incoming_v[ii] + (1 - b2) * scaled_grad * scaled_grad;\n            float denom;\n            if (mode == ADAM_MODE_0)\n              denom = sqrtf(v[j] + eps);\n            else  // Mode 1\n              denom = sqrtf(v[j]) + eps;\n            float update = (m[j] / denom) + (decay * incoming_p[ii]);\n            p[j] = incoming_p[ii] - (step_size * update);\n            if (DEPTH == 5) p_copy[j] = (GRAD_T)p[j];\n          }\n        }\n      }\n    }\n  }\n};\n\nvoid fused_adam_cuda(at::Tensor& p, at::Tensor& p_copy, at::Tensor& m, at::Tensor& v, at::Tensor& g, float lr,\n                     float beta1, float beta2, float eps, float grad_scale, int step, int mode, int bias_correction,\n                     float decay) {\n  //        using namespace at;\n\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    DISPATCH_FLOAT_AND_HALF(g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n                            adam_cuda_kernel<accscalar_t, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n                                p.data_ptr<accscalar_t>(), p_copy.numel() ? p_copy.data_ptr<scalar_t_0>() : NULL,\n                                m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1,\n                                beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(g.scalar_type(), 0, \"adam_cuda_kernel\",\n                              adam_cuda_kernel<scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n                                  p.data_ptr<scalar_t_0>(),\n                                  NULL,  // don't output p_copy for fp32, it's wasted write\n                                  m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1,\n                                  beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\nvoid fused_adam_cuda_mt(int chunk_size, at::Tensor noop_flag,\n                        std::vector<std::vector<at::Tensor>> tensor_lists,  // p, m, v, g, p_copy\n                        float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                        int bias_correction, float decay) {\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  size_t tl_sz = tensor_lists.size();\n  TORCH_CHECK(tl_sz == 4 || tl_sz == 5, \"expected tensor lists of size 4 or 5\");\n\n  if (tensor_lists[3][0].scalar_type() == at::ScalarType::Half) {\n    // alher values should be fp32 for half gradients\n    TORCH_CHECK(tensor_lists[0][0].scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dich is done on the gradient type\n    if (tl_sz == 5) {\n      DISPATCH_FLOAT_AND_HALF(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                              using accscalar_t = at::acc_type<scalar_t_0, true>;\n                              multi_tensor_apply<5>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                    AdamFunctor<5, accscalar_t, scalar_t_0>(), beta1, beta2, eps,\n                                                    grad_scale, step_size, (adamMode_t)mode, decay););\n    } else {\n      DISPATCH_FLOAT_AND_HALF(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                              using accscalar_t = at::acc_type<scalar_t_0, true>;\n                              multi_tensor_apply<4>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                    AdamFunctor<4, accscalar_t, scalar_t_0>(), beta1, beta2, eps,\n                                                    grad_scale, step_size, (adamMode_t)mode, decay););\n    }\n  } else {\n    if (tl_sz == 5) {\n      DISPATCH_DOUBLE_AND_FLOAT(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                                multi_tensor_apply<5>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                      AdamFunctor<5, scalar_t_0, scalar_t_0>(), beta1, beta2, eps,\n                                                      grad_scale, step_size, (adamMode_t)mode, decay););\n    } else {\n      DISPATCH_DOUBLE_AND_FLOAT(tensor_lists[3][0].scalar_type(), 0, \"adam_cuda_mt_kernel\",\n                                multi_tensor_apply<4>(BLOCK_SIZE, chunk_size, noop_flag, tensor_lists,\n                                                      AdamFunctor<4, scalar_t_0, scalar_t_0>(), beta1, beta2, eps,\n                                                      grad_scale, step_size, (adamMode_t)mode, decay););\n    }\n  }\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\ntemplate <typename FROM_T, typename TO_T>\n__device__ void convert(const FROM_T vi, TO_T& vo) {\n  vo = static_cast<TO_T>(vi);\n}\n\ntemplate <>\n__device__ void convert(const float vi, uint8_t& vo) {\n  union S {\n    float as_float;\n    int as_int;\n  };\n  S s;\n  s.as_float = vi;\n  s.as_int = s.as_int & 0xFF800000;\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_half = static_cast<at::Half>(vi + s.as_float / 8.0f);\n  vo = t.as_byte[1];\n}\n\ntemplate <>\n__device__ void convert(const uint8_t vi, float& vo) {\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_byte[0] = 0;\n  t.as_byte[1] = vi;\n  vo = static_cast<float>(t.as_half);\n}\n\ntemplate <>\n__device__ void convert(const at::Half vi, uint8_t& vo) {\n  union S {\n    float as_float;\n    int as_int;\n  };\n  S s;\n  s.as_float = static_cast<float>(vi);\n  s.as_int = s.as_int & 0xFF800000;\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_half = static_cast<at::Half>(vi + s.as_float / 8.0f);\n  vo = t.as_byte[1];\n}\n\ntemplate <>\n__device__ void convert(const uint8_t vi, at::Half& vo) {\n  union T {\n    at::Half as_half;\n    uint8_t as_byte[2];\n  };\n  T t;\n  t.as_byte[0] = 0;\n  t.as_byte[1] = vi;\n  vo = t.as_half;\n}\n\ntemplate <typename GRAD_T>\n__global__ void strided_check_finite_cuda_kernel(volatile int* noop_gmem, GRAD_T* __restrict__ p_copy,\n                                                 const size_t tsize, int stride, int clear_overflow_first) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock) * stride;\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock * stride;\n\n  if (clear_overflow_first) {\n    if (i == 0) {\n      *noop_gmem = 0;\n    }\n    __syncthreads();\n  }\n\n  for (int j = i; j < tsize; j += totThreads) {\n    GRAD_T pi = p_copy[j];\n    if (!isfinite(pi)) {\n      *noop_gmem = 1;\n    }\n  }\n}\ntemplate <>\n__global__ void strided_check_finite_cuda_kernel(volatile int* noop_gmem, uint8_t* __restrict__ p_copy,\n                                                 const size_t tsize, int stride, int clear_overflow_first) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock) * stride;\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock * stride;\n\n  if (clear_overflow_first) {\n    if (i == 0) {\n      *noop_gmem = 0;\n    }\n    __syncthreads();\n  }\n\n  for (int j = i; j < tsize; j += totThreads) {\n    at::Half pi;\n    convert(p_copy[j], pi);\n    if (!isfinite(pi)) {\n      *noop_gmem = 1;\n    }\n  }\n}\n\ntemplate <typename FROM_T, typename TO_T>\n__global__ void maybe_cast_kernel(volatile int* overflow_flag, const FROM_T* p_in, TO_T* p_out, const size_t tsize) {\n  if (overflow_flag && *overflow_flag != 0) return;\n\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  FROM_T pi[ILP];\n  TO_T po[ILP];\n\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      pi[ii] = 0;\n\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        pi[ii] = p_in[j];\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      convert(pi[ii], po[ii]);\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        p_out[j] = po[ii];\n      }\n    }\n  }\n}\n\ntemplate <typename T, typename GRAD_T, typename REDU_T>\n__global__ void reversible_adam_cuda_kernel(\n    T* __restrict__ p,\n    REDU_T* __restrict__ p_copy,  // For mixed precision training, pass NULL if not needed\n    T* __restrict__ m, T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1, const float b2, const float eps,\n    const float grad_scale, const float step_size, const size_t tsize, adamMode_t mode, const float decay) {\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  T mi[ILP];\n  T vi[ILP];\n  T pi[ILP];\n  T gi[ILP];\n\n  bool overflow = false;\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      mi[ii] = T(0);\n      vi[ii] = T(0);\n      pi[ii] = T(0);\n      gi[ii] = GRAD_T(0);\n\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        pi[ii] = p[j];\n        mi[ii] = m[j];\n        vi[ii] = v[j];\n        gi[ii] = static_cast<T>(g[j]);\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      T scaled_grad = gi[ii] / grad_scale;\n      if (isfinite(scaled_grad)) {\n        mi[ii] = b1 * mi[ii] + (1 - b1) * scaled_grad;\n        vi[ii] = b2 * vi[ii] + (1 - b2) * scaled_grad * scaled_grad;\n        float denom;\n        if (mode == ADAM_MODE_0)\n          denom = sqrtf(vi[ii] + eps);\n        else  // Mode 1\n          denom = sqrtf(vi[ii]) + eps;\n        float update = (mi[ii] / denom) + (decay * pi[ii]);\n        pi[ii] = pi[ii] - (step_size * update);\n      } else {\n        overflow = true;\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i + totThreads * ii;\n      if (j < tsize) {\n        m[j] = mi[ii];\n        v[j] = vi[ii];\n        p[j] = pi[ii];\n        if (p_copy != NULL) {\n          convert(pi[ii], p_copy[j]);\n        }\n      }\n    }\n  }\n\n  if (p_copy != NULL) {\n    __syncthreads();\n    if (overflow) {\n      convert(float(INFINITY), p_copy[0]);\n    }\n  }\n}\n\ntemplate <typename T, typename GRAD_T>\n__global__ void maybe_adam_undo_cuda_kernel(volatile int* overflow_flag, T* __restrict__ p, T* __restrict__ m,\n                                            T* __restrict__ v, const GRAD_T* __restrict__ g, const float b1,\n                                            const float b2, const float eps, const float grad_scale,\n                                            const float step_size, const size_t tsize, adamMode_t mode,\n                                            const float decay) {\n  // NB! Skip undo kernel when overflow flag is NOT set\n  if (overflow_flag && *overflow_flag == 0) return;\n\n  // Assuming 2D grids and 2D blocks\n  const int blockId = gridDim.x * blockIdx.y + blockIdx.x;\n  const int threadsPerBlock = blockDim.x * blockDim.y;\n  const int threadIdInBlock = threadIdx.y * blockDim.x + threadIdx.x;\n  const int i = (blockId * threadsPerBlock + threadIdInBlock);\n  const int totThreads = gridDim.x * gridDim.y * threadsPerBlock;\n\n  T mi[ILP];\n  T vi[ILP];\n  T pi[ILP];\n  T gi[ILP];\n\n  for (int j_start = 0; j_start < tsize; j_start += totThreads * ILP) {\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      mi[ii] = T(0);\n      vi[ii] = T(0);\n      pi[ii] = T(0);\n      gi[ii] = GRAD_T(0);\n\n      int j = j_start + i * ILP;\n      if (j < tsize) {\n        pi[ii] = p[j];\n        mi[ii] = m[j];\n        vi[ii] = v[j];\n        gi[ii] = static_cast<T>(g[j]);\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      T scaled_grad = gi[ii] / grad_scale;\n      if (isfinite(scaled_grad)) {\n        float denom;\n        if (mode == ADAM_MODE_0)\n          denom = sqrtf(vi[ii] + eps);\n        else  // Mode 1\n          denom = sqrtf(vi[ii]) + eps;\n        pi[ii] = (pi[ii] + step_size * (mi[ii] / denom)) / (1.0f - step_size * decay);\n        mi[ii] = (mi[ii] - (1 - b1) * scaled_grad) / b1;\n        vi[ii] = (vi[ii] - (1 - b2) * scaled_grad * scaled_grad) / b2;\n        // Make sure round off errors don't create (small) negative value.\n        // This can happen if we have to revert the very first step.\n        vi[ii] = vi[ii] >= 0.0f ? vi[ii] : 0.0f;\n      }\n    }\n\n#pragma unroll\n    for (int ii = 0; ii < ILP; ii++) {\n      int j = j_start + i * ILP;\n      if (j < tsize) {\n        m[j] = mi[ii];\n        v[j] = vi[ii];\n        p[j] = pi[ii];\n      }\n    }\n  }\n}\n\ntemplate <int DEPTH, typename FROM_T, typename TO_T>\nstruct MaybeCastFunctor {\n  __device__ __forceinline__ void operator()(int chunk_size, volatile int* overflow_flag,\n                                             TensorListMetadata<DEPTH>& tl) {\n    if (overflow_flag && *overflow_flag != 0) return;\n\n    int tensor_loc = tl.block_to_tensor[blockIdx.x];\n    int chunk_idx = tl.block_to_chunk[blockIdx.x];\n    int n = tl.sizes[tensor_loc];\n\n    FROM_T* p_in = (FROM_T*)tl.addresses[0][tensor_loc];\n    p_in += chunk_idx * chunk_size;\n    TO_T* p_out = (TO_T*)tl.addresses[1][tensor_loc];\n    p_out += chunk_idx * chunk_size;\n\n    n -= chunk_idx * chunk_size;\n    int dim = chunk_size < n ? chunk_size : n;\n\n    FROM_T pi[ILP];\n    TO_T po[ILP];\n\n    for (int j_start = 0; j_start < dim; j_start += blockDim.x * ILP) {\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        pi[ii] = FROM_T(0);\n        int j = j_start + threadIdx.x + ii * blockDim.x;\n        if (j < dim) {\n          pi[ii] = p_in[j];\n        }\n      }\n\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        convert(pi[ii], po[ii]);\n      }\n\n#pragma unroll\n      for (int ii = 0; ii < ILP; ii++) {\n        int j = j_start + threadIdx.x + ii * blockDim.x;\n        if (j < dim) {\n          p_out[j] = po[ii];\n        }\n      }\n    }\n  }\n};\n\nvoid fused_strided_check_finite(at::Tensor& overflow_flag, at::Tensor& p_copy, int stride, int clear_overflow_first) {\n  // Get tensor size\n  int tsize = p_copy.numel();\n  int niter = (tsize + stride - 1) / stride;\n\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  // In order to avoid race condition, blocks must be 1 when clear_overflow_first flag is set.\n  const dim3 blocks(clear_overflow_first ? 1 : (niter + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p_copy), \"parameter tensor is too large to be indexed with int32\");\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  using namespace at;  // prevents \"toString is undefined\" errors\n  DISPATCH_FLOAT_HALF_AND_BYTE(\n      p_copy.scalar_type(), 0, \"check_finite_cuda_kernel\",\n      strided_check_finite_cuda_kernel<scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n          overflow_flag.data_ptr<int>(), p_copy.data_ptr<scalar_t_0>(), tsize, stride, clear_overflow_first););\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\nvoid fused_reversible_adam_cuda(at::Tensor& p, at::Tensor& p_copy, at::Tensor& m, at::Tensor& v, at::Tensor& g,\n                                float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                                int bias_correction, float decay) {\n  //      using namespace at;\n\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    if (p_copy.numel() == 0 || p_copy.scalar_type() == g.scalar_type()) {\n      DISPATCH_FLOAT_AND_HALF(\n          g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n          reversible_adam_cuda_kernel<accscalar_t, scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n              p.data_ptr<accscalar_t>(), p_copy.numel() ? p_copy.data_ptr<scalar_t_0>() : NULL,\n              m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps,\n              grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n    } else {\n      TORCH_CHECK(p_copy.scalar_type() == at::ScalarType::Byte, \"expected parameter to be of byte type\");\n      DISPATCH_FLOAT_AND_HALF(\n          g.scalar_type(), 0, \"adam_cuda_e5m2_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n          reversible_adam_cuda_kernel<accscalar_t, scalar_t_0, uint8_t><<<blocks, threadsPerBlock, 0, stream>>>(\n              p.data_ptr<accscalar_t>(), p_copy.data_ptr<uint8_t>(), m.data_ptr<accscalar_t>(),\n              v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps, grad_scale, step_size, tsize,\n              (adamMode_t)mode, decay););\n    }\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(g.scalar_type(), 0, \"adam_cuda_kernel\",\n                              reversible_adam_cuda_kernel<scalar_t_0, scalar_t_0, scalar_t_0>\n                              <<<blocks, threadsPerBlock, 0, stream>>>(\n                                  p.data_ptr<scalar_t_0>(),\n                                  NULL,  // don't output p_copy for fp32, it's wasted write\n                                  m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1,\n                                  beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\nvoid maybe_cast_cuda(at::Tensor& overflow_flag, at::Tensor& p_in, at::Tensor& p_out) {\n  // Get tensor size\n  int tsize = p_in.numel();\n  TORCH_CHECK(tsize == p_out.numel(), \"p_in.numel() must equal p_out.numel()\");\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p_in), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  DISPATCH_FLOAT_HALF_AND_BYTE(p_in.scalar_type(), 0,\n                               \"maybe_cast_cuda\" DISPATCH_FLOAT_HALF_AND_BYTE(\n                                   p_out.scalar_type(), 1, \"maybe_cast_cuda\",\n                                   maybe_cast_kernel<scalar_t_0, scalar_t_1><<<blocks, threadsPerBlock, 0, stream>>>(\n                                       overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL,\n                                       p_in.data_ptr<scalar_t_0>(), p_out.data_ptr<scalar_t_1>(), tsize);))\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\nvoid maybe_cast_cuda_mt(int chunk_size, at::Tensor overflow_flag,\n                        std::vector<std::vector<at::Tensor>> tensor_lists)  // p_in, p_out\n{\n  // Constants\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  size_t tl_sz = tensor_lists.size();\n  TORCH_CHECK(tl_sz == 2, \"expected tensor lists of size 2\");\n\n  DISPATCH_FLOAT_HALF_AND_BYTE(\n      tensor_lists[0][0].scalar_type(), 0, \"maybe_cast_cuda_mt_kernel\",\n      DISPATCH_FLOAT_HALF_AND_BYTE(tensor_lists[1][0].scalar_type(), 1, \"maybe_cast_cuda_mt_kernel\",\n                                   multi_tensor_apply<2>(BLOCK_SIZE, chunk_size, overflow_flag, tensor_lists,\n                                                         MaybeCastFunctor<2, scalar_t_0, scalar_t_1>());))\n  C10_CUDA_CHECK(hipGetLastError());\n}\n\nvoid fused_maybe_adam_undo_cuda(at::Tensor& overflow_flag, at::Tensor& p, at::Tensor& m, at::Tensor& v, at::Tensor& g,\n                                float lr, float beta1, float beta2, float eps, float grad_scale, int step, int mode,\n                                int bias_correction, float decay) {\n  // Get tensor size\n  int tsize = p.numel();\n  // Determine #threads and #blocks\n  const int threadsPerBlock = 512;\n  const dim3 blocks((tsize + threadsPerBlock - 1) / threadsPerBlock);\n  TORCH_CHECK(at::cuda::detail::canUse32BitIndexMath(p), \"parameter tensor is too large to be indexed with int32\");\n  // Constants\n  float step_size = 0;\n  if (bias_correction == 1) {\n    const float bias_correction1 = 1 - std::pow(beta1, step);\n    const float bias_correction2 = 1 - std::pow(beta2, step);\n    step_size = lr * std::sqrt(bias_correction2) / bias_correction1;\n  } else {\n    step_size = lr;\n  }\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (g.scalar_type() == at::ScalarType::Half) {\n    // all other values should be fp32 for half gradients\n    TORCH_CHECK(p.scalar_type() == at::ScalarType::Float, \"expected parameter to be of float type\");\n    // dispatch is done on the gradient type\n    using namespace at;  // prevents \"toString is undefined\" errors\n    DISPATCH_FLOAT_AND_HALF(g.scalar_type(), 0, \"adam_cuda_kernel\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n                            maybe_adam_undo_cuda_kernel<accscalar_t, scalar_t_0>\n                            <<<blocks, threadsPerBlock, 0, stream>>>(\n                                overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL, p.data_ptr<accscalar_t>(),\n                                m.data_ptr<accscalar_t>(), v.data_ptr<accscalar_t>(), g.data_ptr<scalar_t_0>(), beta1,\n                                beta2, eps, grad_scale, step_size, tsize, (adamMode_t)mode, decay););\n  } else {\n    using namespace at;\n    DISPATCH_DOUBLE_AND_FLOAT(\n        g.scalar_type(), 0, \"adam_cuda_kernel\",\n        maybe_adam_undo_cuda_kernel<scalar_t_0, scalar_t_0><<<blocks, threadsPerBlock, 0, stream>>>(\n            overflow_flag.numel() ? overflow_flag.data_ptr<int>() : NULL, p.data_ptr<scalar_t_0>(),\n            m.data_ptr<scalar_t_0>(), v.data_ptr<scalar_t_0>(), g.data_ptr<scalar_t_0>(), beta1, beta2, eps, grad_scale,\n            step_size, tsize, (adamMode_t)mode, decay););\n  }\n  C10_CUDA_CHECK(hipGetLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/optimizers/fused_adam_cuda_kernel.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include <ATen/cuda/Atomic.cuh>\n\n__global__ void index_mul_2d_float_dim64(float* out, const float* in1, const float* in2, const int64_t* idx1,\n                                         const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 res, src1, src2;\n    src1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    res.x = src1.x * src2.x;\n    res.y = src1.y * src2.y;\n    res.z = src1.z * src2.z;\n    res.w = src1.w * src2.w;\n    reinterpret_cast<float4*>(out)[vec_idx2] = res;\n  }\n}\n\n__global__ void index_mul_2d_float(float* out, const float* in1, const float* in2, const int64_t* idx1,\n                                   const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim);\n    int64_t vec_idx2 = (start_idx * fea_dim);\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      out[vec_idx2 + i] = in1[vec_idx1 + i] * in2[vec_idx2 + i];\n    }\n  }\n}\n\n__global__ void index_mul_2d_half(at::Half* out, const at::Half* in1, const at::Half* in2, const int64_t* idx1,\n                                  const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim);\n    int64_t vec_idx2 = (start_idx * fea_dim);\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      out[vec_idx2 + i] = at::Half(static_cast<float>(in1[vec_idx1 + i]) * static_cast<float>(in2[vec_idx2 + i]));\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_float_dim64(float* grad_in1, float* grad_in2, const float* grad_out, const float* in1,\n                                              const float* in2, const int64_t* idx1, const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 src_in1, src_in2, src_grad_out, dst_grad_in2;\n    src_grad_out = reinterpret_cast<const float4*>(grad_out)[vec_idx2];\n    src_in1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src_in2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    int64_t grad_in1_base_idx = idx1[start_idx] * fea_dim + tidx * 4;\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 0, src_grad_out.x * src_in2.x);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 1, src_grad_out.y * src_in2.y);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 2, src_grad_out.z * src_in2.z);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 3, src_grad_out.w * src_in2.w);\n    dst_grad_in2.x = src_grad_out.x * src_in1.x;\n    dst_grad_in2.y = src_grad_out.y * src_in1.y;\n    dst_grad_in2.z = src_grad_out.z * src_in1.z;\n    dst_grad_in2.w = src_grad_out.w * src_in1.w;\n    reinterpret_cast<float4*>(grad_in2)[vec_idx2] = dst_grad_in2;\n  }\n}\n\n__global__ void index_mul_2d_grad_float(float* grad_in1, float* grad_in2, const float* grad_out, const float* in1,\n                                        const float* in2, const int64_t* idx1, const int64_t size,\n                                        const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_in1 = in1[vec_idx1 + i];\n      float src_in2 = in2[vec_idx2 + i];\n      float src_grad_out = grad_out[vec_idx2 + i];\n      grad_in2[vec_idx2 + i] = src_grad_out * src_in1;\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, src_grad_out * src_in2);\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_half(at::Half* grad_in1, at::Half* grad_in2, const at::Half* grad_out,\n                                       const at::Half* in1, const at::Half* in2, const int64_t* idx1,\n                                       const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_in1 = static_cast<float>(in1[vec_idx1 + i]);\n      float src_in2 = static_cast<float>(in2[vec_idx2 + i]);\n      float src_grad_out = static_cast<float>(grad_out[vec_idx2 + i]);\n      grad_in2[vec_idx2 + i] = at::Half(src_grad_out * src_in1);\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, at::Half(src_grad_out * src_in2));\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_float_dim64(float* grad_grad_out, float* grad_in1, float* grad_in2,\n                                                   const float* grad_out, const float* grad_grad_in1,\n                                                   const float* grad_grad_in2, const float* in1, const float* in2,\n                                                   const int64_t* idx1, const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 src_grad_grad_in1, src_in1, src_grad_grad_in2, src_in2, src_grad_out;\n    float4 dst_grad_grad_out, dst_grad_in2;\n    src_grad_grad_in1 = reinterpret_cast<const float4*>(grad_grad_in1)[vec_idx1];\n    src_in1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src_grad_grad_in2 = reinterpret_cast<const float4*>(grad_grad_in2)[vec_idx2];\n    src_in2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    dst_grad_grad_out.x = src_grad_grad_in1.x * src_in2.x + src_grad_grad_in2.x * src_in1.x;\n    dst_grad_grad_out.y = src_grad_grad_in1.y * src_in2.y + src_grad_grad_in2.y * src_in1.y;\n    dst_grad_grad_out.z = src_grad_grad_in1.z * src_in2.z + src_grad_grad_in2.z * src_in1.z;\n    dst_grad_grad_out.w = src_grad_grad_in1.w * src_in2.w + src_grad_grad_in2.w * src_in1.w;\n    reinterpret_cast<float4*>(grad_grad_out)[vec_idx2] = dst_grad_grad_out;\n    src_grad_out = reinterpret_cast<const float4*>(grad_out)[vec_idx2];\n    int64_t grad_in1_base_idx = idx1[start_idx] * fea_dim + tidx * 4;\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 0, src_grad_grad_in2.x * src_grad_out.x);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 1, src_grad_grad_in2.y * src_grad_out.y);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 2, src_grad_grad_in2.z * src_grad_out.z);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 3, src_grad_grad_in2.w * src_grad_out.w);\n    dst_grad_in2.x = src_grad_grad_in1.x * src_grad_out.x;\n    dst_grad_in2.y = src_grad_grad_in1.y * src_grad_out.y;\n    dst_grad_in2.z = src_grad_grad_in1.z * src_grad_out.z;\n    dst_grad_in2.w = src_grad_grad_in1.w * src_grad_out.w;\n    reinterpret_cast<float4*>(grad_in2)[vec_idx2] = dst_grad_in2;\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_float(float* grad_grad_out, float* grad_in1, float* grad_in2,\n                                             const float* grad_out, const float* grad_grad_in1,\n                                             const float* grad_grad_in2, const float* in1, const float* in2,\n                                             const int64_t* idx1, const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_grad_grad_in1 = grad_grad_in1[vec_idx1 + i];\n      float src_grad_grad_in2 = grad_grad_in2[vec_idx2 + i];\n      float src_in1 = in1[vec_idx1 + i];\n      float src_in2 = in2[vec_idx2 + i];\n      float src_grad_out = grad_out[vec_idx2 + i];\n      grad_grad_out[vec_idx2 + i] = src_grad_grad_in1 * src_in2 + src_grad_grad_in2 * src_in1;\n      grad_in2[vec_idx2 + i] = src_grad_grad_in1 * src_grad_out;\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, src_grad_grad_in2 * src_grad_out);\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_half(at::Half* grad_grad_out, at::Half* grad_in1, at::Half* grad_in2,\n                                            const at::Half* grad_out, const at::Half* grad_grad_in1,\n                                            const at::Half* grad_grad_in2, const at::Half* in1, const at::Half* in2,\n                                            const int64_t* idx1, const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_grad_grad_in1 = static_cast<float>(grad_grad_in1[vec_idx1 + i]);\n      float src_grad_grad_in2 = static_cast<float>(grad_grad_in2[vec_idx2 + i]);\n      float src_in1 = static_cast<float>(in1[vec_idx1 + i]);\n      float src_in2 = static_cast<float>(in2[vec_idx2 + i]);\n      float src_grad_out = static_cast<float>(grad_out[vec_idx2 + i]);\n      grad_grad_out[vec_idx2 + i] = at::Half(src_grad_grad_in1 * src_in2 + src_grad_grad_in2 * src_in1);\n      grad_in2[vec_idx2 + i] = at::Half(src_grad_grad_in1 * src_grad_out);\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, at::Half(src_grad_grad_in2 * src_grad_out));\n    }\n  }\n}\n\nvoid index_mul_2d_float_foward_cuda(at::Tensor& out, const at::Tensor& in1, const at::Tensor& in2,\n                                    const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        out.data_ptr<float>(), in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        out.data_ptr<float>(), in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n\n  AT_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid index_mul_2d_float_backward_cuda(at::Tensor& grad_in1, at::Tensor& grad_in2, const at::Tensor& grad_out,\n                                      const at::Tensor& in1, const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(), grad_out.data_ptr<float>(), in1.data_ptr<float>(),\n        in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n\n    AT_CUDA_CHECK(cudaGetLastError());\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(), grad_out.data_ptr<float>(), in1.data_ptr<float>(),\n        in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n}\n\nvoid index_mul_2d_float_backward_backward_cuda(at::Tensor& grad_grad_out, at::Tensor& grad_in1, at::Tensor& grad_in2,\n                                               const at::Tensor& grad_out, const at::Tensor& grad_grad_in1,\n                                               const at::Tensor& grad_grad_in2, const at::Tensor& in1,\n                                               const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_grad_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_grad_out.data_ptr<float>(), grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(),\n        grad_out.data_ptr<float>(), grad_grad_in1.data_ptr<float>(), grad_grad_in2.data_ptr<float>(),\n        in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_grad_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_grad_out.data_ptr<float>(), grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(),\n        grad_out.data_ptr<float>(), grad_grad_in1.data_ptr<float>(), grad_grad_in2.data_ptr<float>(),\n        in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n\n  AT_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid index_mul_2d_half_foward_cuda(at::Tensor& out, const at::Tensor& in1, const at::Tensor& in2,\n                                   const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      out.data_ptr<at::Half>(), in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size,\n      fea_dim);\n\n  AT_CUDA_CHECK(cudaGetLastError());\n}\n\nvoid index_mul_2d_half_backward_cuda(at::Tensor& grad_in1, at::Tensor& grad_in2, const at::Tensor& grad_out,\n                                     const at::Tensor& in1, const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_grad_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      grad_in1.data_ptr<at::Half>(), grad_in2.data_ptr<at::Half>(), grad_out.data_ptr<at::Half>(),\n      in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n}\n\nvoid index_mul_2d_half_backward_backward_cuda(at::Tensor& grad_grad_out, at::Tensor& grad_in1, at::Tensor& grad_in2,\n                                              const at::Tensor& grad_out, const at::Tensor& grad_grad_in1,\n                                              const at::Tensor& grad_grad_in2, const at::Tensor& in1,\n                                              const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_grad_grad_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      grad_grad_out.data_ptr<at::Half>(), grad_in1.data_ptr<at::Half>(), grad_in2.data_ptr<at::Half>(),\n      grad_out.data_ptr<at::Half>(), grad_grad_in1.data_ptr<at::Half>(), grad_grad_in2.data_ptr<at::Half>(),\n      in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n\n  AT_CUDA_CHECK(cudaGetLastError());\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include <ATen/cuda/Atomic.cuh>\n\n__global__ void index_mul_2d_float_dim64(float* out, const float* in1, const float* in2, const int64_t* idx1,\n                                         const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 res, src1, src2;\n    src1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    res.x = src1.x * src2.x;\n    res.y = src1.y * src2.y;\n    res.z = src1.z * src2.z;\n    res.w = src1.w * src2.w;\n    reinterpret_cast<float4*>(out)[vec_idx2] = res;\n  }\n}\n\n__global__ void index_mul_2d_float(float* out, const float* in1, const float* in2, const int64_t* idx1,\n                                   const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim);\n    int64_t vec_idx2 = (start_idx * fea_dim);\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      out[vec_idx2 + i] = in1[vec_idx1 + i] * in2[vec_idx2 + i];\n    }\n  }\n}\n\n__global__ void index_mul_2d_half(at::Half* out, const at::Half* in1, const at::Half* in2, const int64_t* idx1,\n                                  const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim);\n    int64_t vec_idx2 = (start_idx * fea_dim);\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      out[vec_idx2 + i] = at::Half(static_cast<float>(in1[vec_idx1 + i]) * static_cast<float>(in2[vec_idx2 + i]));\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_float_dim64(float* grad_in1, float* grad_in2, const float* grad_out, const float* in1,\n                                              const float* in2, const int64_t* idx1, const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 src_in1, src_in2, src_grad_out, dst_grad_in2;\n    src_grad_out = reinterpret_cast<const float4*>(grad_out)[vec_idx2];\n    src_in1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src_in2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    int64_t grad_in1_base_idx = idx1[start_idx] * fea_dim + tidx * 4;\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 0, src_grad_out.x * src_in2.x);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 1, src_grad_out.y * src_in2.y);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 2, src_grad_out.z * src_in2.z);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 3, src_grad_out.w * src_in2.w);\n    dst_grad_in2.x = src_grad_out.x * src_in1.x;\n    dst_grad_in2.y = src_grad_out.y * src_in1.y;\n    dst_grad_in2.z = src_grad_out.z * src_in1.z;\n    dst_grad_in2.w = src_grad_out.w * src_in1.w;\n    reinterpret_cast<float4*>(grad_in2)[vec_idx2] = dst_grad_in2;\n  }\n}\n\n__global__ void index_mul_2d_grad_float(float* grad_in1, float* grad_in2, const float* grad_out, const float* in1,\n                                        const float* in2, const int64_t* idx1, const int64_t size,\n                                        const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_in1 = in1[vec_idx1 + i];\n      float src_in2 = in2[vec_idx2 + i];\n      float src_grad_out = grad_out[vec_idx2 + i];\n      grad_in2[vec_idx2 + i] = src_grad_out * src_in1;\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, src_grad_out * src_in2);\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_half(at::Half* grad_in1, at::Half* grad_in2, const at::Half* grad_out,\n                                       const at::Half* in1, const at::Half* in2, const int64_t* idx1,\n                                       const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_in1 = static_cast<float>(in1[vec_idx1 + i]);\n      float src_in2 = static_cast<float>(in2[vec_idx2 + i]);\n      float src_grad_out = static_cast<float>(grad_out[vec_idx2 + i]);\n      grad_in2[vec_idx2 + i] = at::Half(src_grad_out * src_in1);\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, at::Half(src_grad_out * src_in2));\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_float_dim64(float* grad_grad_out, float* grad_in1, float* grad_in2,\n                                                   const float* grad_out, const float* grad_grad_in1,\n                                                   const float* grad_grad_in2, const float* in1, const float* in2,\n                                                   const int64_t* idx1, const int64_t size) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  constexpr int fea_dim = 64;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = (idx1[start_idx] * fea_dim) / 4 + tidx;\n    int64_t vec_idx2 = (start_idx * fea_dim) / 4 + tidx;\n\n    float4 src_grad_grad_in1, src_in1, src_grad_grad_in2, src_in2, src_grad_out;\n    float4 dst_grad_grad_out, dst_grad_in2;\n    src_grad_grad_in1 = reinterpret_cast<const float4*>(grad_grad_in1)[vec_idx1];\n    src_in1 = reinterpret_cast<const float4*>(in1)[vec_idx1];\n    src_grad_grad_in2 = reinterpret_cast<const float4*>(grad_grad_in2)[vec_idx2];\n    src_in2 = reinterpret_cast<const float4*>(in2)[vec_idx2];\n    dst_grad_grad_out.x = src_grad_grad_in1.x * src_in2.x + src_grad_grad_in2.x * src_in1.x;\n    dst_grad_grad_out.y = src_grad_grad_in1.y * src_in2.y + src_grad_grad_in2.y * src_in1.y;\n    dst_grad_grad_out.z = src_grad_grad_in1.z * src_in2.z + src_grad_grad_in2.z * src_in1.z;\n    dst_grad_grad_out.w = src_grad_grad_in1.w * src_in2.w + src_grad_grad_in2.w * src_in1.w;\n    reinterpret_cast<float4*>(grad_grad_out)[vec_idx2] = dst_grad_grad_out;\n    src_grad_out = reinterpret_cast<const float4*>(grad_out)[vec_idx2];\n    int64_t grad_in1_base_idx = idx1[start_idx] * fea_dim + tidx * 4;\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 0, src_grad_grad_in2.x * src_grad_out.x);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 1, src_grad_grad_in2.y * src_grad_out.y);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 2, src_grad_grad_in2.z * src_grad_out.z);\n    gpuAtomicAdd(grad_in1 + grad_in1_base_idx + 3, src_grad_grad_in2.w * src_grad_out.w);\n    dst_grad_in2.x = src_grad_grad_in1.x * src_grad_out.x;\n    dst_grad_in2.y = src_grad_grad_in1.y * src_grad_out.y;\n    dst_grad_in2.z = src_grad_grad_in1.z * src_grad_out.z;\n    dst_grad_in2.w = src_grad_grad_in1.w * src_grad_out.w;\n    reinterpret_cast<float4*>(grad_in2)[vec_idx2] = dst_grad_in2;\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_float(float* grad_grad_out, float* grad_in1, float* grad_in2,\n                                             const float* grad_out, const float* grad_grad_in1,\n                                             const float* grad_grad_in2, const float* in1, const float* in2,\n                                             const int64_t* idx1, const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_grad_grad_in1 = grad_grad_in1[vec_idx1 + i];\n      float src_grad_grad_in2 = grad_grad_in2[vec_idx2 + i];\n      float src_in1 = in1[vec_idx1 + i];\n      float src_in2 = in2[vec_idx2 + i];\n      float src_grad_out = grad_out[vec_idx2 + i];\n      grad_grad_out[vec_idx2 + i] = src_grad_grad_in1 * src_in2 + src_grad_grad_in2 * src_in1;\n      grad_in2[vec_idx2 + i] = src_grad_grad_in1 * src_grad_out;\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, src_grad_grad_in2 * src_grad_out);\n    }\n  }\n}\n\n__global__ void index_mul_2d_grad_grad_half(at::Half* grad_grad_out, at::Half* grad_in1, at::Half* grad_in2,\n                                            const at::Half* grad_out, const at::Half* grad_grad_in1,\n                                            const at::Half* grad_grad_in2, const at::Half* in1, const at::Half* in2,\n                                            const int64_t* idx1, const int64_t size, const int64_t fea_dim) {\n  const int tidx = threadIdx.x;\n  const int tidy = threadIdx.y;\n  const int bidx = blockIdx.x;\n  const int start_idx = bidx * blockDim.y + tidy;\n  const int stride = blockDim.x;\n\n  if (start_idx < size) {\n    int64_t vec_idx1 = idx1[start_idx] * fea_dim;\n    int64_t vec_idx2 = start_idx * fea_dim;\n\n    for (int i = tidx; i < fea_dim; i += stride) {\n      float src_grad_grad_in1 = static_cast<float>(grad_grad_in1[vec_idx1 + i]);\n      float src_grad_grad_in2 = static_cast<float>(grad_grad_in2[vec_idx2 + i]);\n      float src_in1 = static_cast<float>(in1[vec_idx1 + i]);\n      float src_in2 = static_cast<float>(in2[vec_idx2 + i]);\n      float src_grad_out = static_cast<float>(grad_out[vec_idx2 + i]);\n      grad_grad_out[vec_idx2 + i] = at::Half(src_grad_grad_in1 * src_in2 + src_grad_grad_in2 * src_in1);\n      grad_in2[vec_idx2 + i] = at::Half(src_grad_grad_in1 * src_grad_out);\n      gpuAtomicAdd(grad_in1 + vec_idx1 + i, at::Half(src_grad_grad_in2 * src_grad_out));\n    }\n  }\n}\n\nvoid index_mul_2d_float_foward_cuda(at::Tensor& out, const at::Tensor& in1, const at::Tensor& in2,\n                                    const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        out.data_ptr<float>(), in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        out.data_ptr<float>(), in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n\n  AT_CUDA_CHECK(hipGetLastError());\n}\n\nvoid index_mul_2d_float_backward_cuda(at::Tensor& grad_in1, at::Tensor& grad_in2, const at::Tensor& grad_out,\n                                      const at::Tensor& in1, const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(), grad_out.data_ptr<float>(), in1.data_ptr<float>(),\n        in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n\n    AT_CUDA_CHECK(hipGetLastError());\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(), grad_out.data_ptr<float>(), in1.data_ptr<float>(),\n        in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n}\n\nvoid index_mul_2d_float_backward_backward_cuda(at::Tensor& grad_grad_out, at::Tensor& grad_in1, at::Tensor& grad_in2,\n                                               const at::Tensor& grad_out, const at::Tensor& grad_grad_in1,\n                                               const at::Tensor& grad_grad_in2, const at::Tensor& in1,\n                                               const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  if (fea_dim == 64) {\n    const int BLOCK_THREADS_DIMX = 16;\n    const int BLOCK_THREADS_DIMY = 16;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_grad_float_dim64<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_grad_out.data_ptr<float>(), grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(),\n        grad_out.data_ptr<float>(), grad_grad_in1.data_ptr<float>(), grad_grad_in2.data_ptr<float>(),\n        in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size);\n  } else {\n    const int BLOCK_THREADS_DIMX = 32;\n    const int BLOCK_THREADS_DIMY = 8;\n    const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n    index_mul_2d_grad_grad_float<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n        grad_grad_out.data_ptr<float>(), grad_in1.data_ptr<float>(), grad_in2.data_ptr<float>(),\n        grad_out.data_ptr<float>(), grad_grad_in1.data_ptr<float>(), grad_grad_in2.data_ptr<float>(),\n        in1.data_ptr<float>(), in2.data_ptr<float>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n  }\n\n  AT_CUDA_CHECK(hipGetLastError());\n}\n\nvoid index_mul_2d_half_foward_cuda(at::Tensor& out, const at::Tensor& in1, const at::Tensor& in2,\n                                   const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      out.data_ptr<at::Half>(), in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size,\n      fea_dim);\n\n  AT_CUDA_CHECK(hipGetLastError());\n}\n\nvoid index_mul_2d_half_backward_cuda(at::Tensor& grad_in1, at::Tensor& grad_in2, const at::Tensor& grad_out,\n                                     const at::Tensor& in1, const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_grad_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      grad_in1.data_ptr<at::Half>(), grad_in2.data_ptr<at::Half>(), grad_out.data_ptr<at::Half>(),\n      in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n}\n\nvoid index_mul_2d_half_backward_backward_cuda(at::Tensor& grad_grad_out, at::Tensor& grad_in1, at::Tensor& grad_in2,\n                                              const at::Tensor& grad_out, const at::Tensor& grad_grad_in1,\n                                              const at::Tensor& grad_grad_in2, const at::Tensor& in1,\n                                              const at::Tensor& in2, const at::Tensor& idx1) {\n  const int64_t size = in2.size(0);\n  const int64_t fea_dim = in2.size(1);\n  if (size < 0) {\n    return;\n  }\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n\n  const int BLOCK_THREADS_DIMX = 32;\n  const int BLOCK_THREADS_DIMY = 8;\n  const int BLOCK_NUMS = (size + BLOCK_THREADS_DIMY - 1) / BLOCK_THREADS_DIMY;\n\n  index_mul_2d_grad_grad_half<<<BLOCK_NUMS, {BLOCK_THREADS_DIMX, BLOCK_THREADS_DIMY, 1}, 0, stream>>>(\n      grad_grad_out.data_ptr<at::Half>(), grad_in1.data_ptr<at::Half>(), grad_in2.data_ptr<at::Half>(),\n      grad_out.data_ptr<at::Half>(), grad_grad_in1.data_ptr<at::Half>(), grad_grad_in2.data_ptr<at::Half>(),\n      in1.data_ptr<at::Half>(), in2.data_ptr<at::Half>(), idx1.data_ptr<int64_t>(), size, fea_dim);\n\n  AT_CUDA_CHECK(hipGetLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/index_mul_2d/index_mul_2d_cuda_kernel.cu"}
{"input": "/**\n * From PyTorch:\n *\n * Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)\n * Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)\n * Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)\n * Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)\n * Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)\n * Copyright (c) 2011-2013 NYU                      (Clement Farabet)\n * Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)\n * Copyright (c) 2006      Idiap Research Institute (Samy Bengio)\n * Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)\n *\n * From Caffe2:\n *\n * Copyright (c) 2016-present, Facebook Inc. All rights reserved.\n *\n * All contributions by Facebook:\n * Copyright (c) 2016 Facebook Inc.\n *\n * All contributions by Google:\n * Copyright (c) 2015 Google Inc.\n * All rights reserved.\n *\n * All contributions by Yangqing Jia:\n * Copyright (c) 2015 Yangqing Jia\n * All rights reserved.\n *\n * All contributions from Caffe:\n * Copyright(c) 2013, 2014, 2015, the respective contributors\n * All rights reserved.\n *\n * All other contributions:\n * Copyright(c) 2015, 2016 the respective contributors\n * All rights reserved.\n *\n * Caffe2 uses a copyright model similar to Caffe: each contributor holds\n * copyright over their contributions to Caffe2. The project versioning records\n * all such contribution and copyright details. If a contributor wants to further\n * mark their specific copyright on a particular contribution, they should\n * indicate their copyright solely in the commit message of the change when it is\n * committed.\n *\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * 3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America\n *    and IDIAP Research Institute nor the names of its contributors may be\n *    used to endorse or promote products derived from this software without\n *    specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include <ATen/cuda/NumericLimits.cuh>\n\n#include \"type_shim.h\"\n\n#define ALIGN_BYTES 16\n\nusing Tensor = at::Tensor;\nusing TensorList = at::TensorList;\nusing ScalarType = at::ScalarType;\nusing at::acc_type;\n\ntemplate <typename T, typename AccumT, typename OutT>\nstruct LogSoftMaxForwardEpilogue {\n  __device__ __forceinline__ LogSoftMaxForwardEpilogue(AccumT max_input, AccumT sum)\n      : logsum(max_input + std::log(sum)) {}\n\n  __device__ __forceinline__ LogSoftMaxForwardEpilogue(AccumT max_log_sum_exp) : logsum(max_log_sum_exp) {}\n\n  __device__ __forceinline__ OutT operator()(T input) const { return static_cast<OutT>(input - logsum); }\n\n  const AccumT logsum;\n};\n\ntemplate <typename T, typename AccumT, typename OutT>\nstruct LogSoftMaxBackwardEpilogue {\n  __device__ __forceinline__ LogSoftMaxBackwardEpilogue(AccumT sum) : sum(sum) {}\n\n  __device__ __forceinline__ T operator()(OutT gradOutput, OutT output) const {\n    return static_cast<T>(gradOutput - std::exp(static_cast<AccumT>(output)) * sum);\n  }\n\n  const AccumT sum;\n};\n\nconst int max_threads = 1024;\n\ninline dim3 SoftMax_getBlockSize(int ILP, uint64_t dim_size) {\n  uint64_t block_size = 1;\n  uint64_t max_block_size = std::min(dim_size / ILP, static_cast<uint64_t>(max_threads));\n  while (block_size < (max_block_size / 2)) block_size *= 2;\n  // Launch at least a single warp - the kernel assumes that.\n  block_size = std::max(block_size, static_cast<uint64_t>(32));\n  return dim3(block_size);\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Regular kernel (fast when dim_size is large; requires inner_size == 1)\n////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, typename AccumT>\nstruct MaxFloat {\n  __device__ __forceinline__ AccumT operator()(AccumT max, T v) const { return ::max(max, (AccumT)v); }\n};\n\ntemplate <typename T, typename AccumT>\nstruct AddFloat {\n  __device__ __forceinline__ AccumT operator()(AccumT sum, T v) const { return sum + v; }\n};\n\ntemplate <typename T, typename AccumT>\nstruct SumExpFloat {\n  __device__ __forceinline__ SumExpFloat(AccumT v) : max_k(v) {}\n\n  __device__ __forceinline__ AccumT operator()(AccumT sum, T v) const { return sum + std::exp(v - max_k); }\n\n  const AccumT max_k;\n};\n\ntemplate <template <typename> class Reduction, typename AccumT>\n__device__ __forceinline__ AccumT blockReduce(AccumT* smem, AccumT val, const Reduction<AccumT>& r, AccumT defaultVal) {\n  // To avoid RaW races from chaining blockReduce calls together, we need a sync here\n  __syncthreads();\n\n  smem[threadIdx.x] = val;\n\n  __syncthreads();\n\n  AccumT warpVal = defaultVal;\n\n  // First warp will perform per-warp reductions for the remaining warps\n  uint32_t mask = (((uint64_t)1) << (blockDim.x / 32)) - 1;\n  if (threadIdx.x < 32) {\n    int lane = threadIdx.x % 32;\n    if (lane < blockDim.x / 32) {\n#pragma unroll\n      for (int i = 0; i < 32; ++i) {\n        warpVal = r(warpVal, smem[lane * 32 + i]);\n      }\n      __syncwarp(mask);\n      smem[lane] = warpVal;\n    }\n  }\n\n  __syncthreads();\n\n  // First thread will perform a reduction of the above per-warp reductions\n  AccumT blockVal = defaultVal;\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < blockDim.x / 32; ++i) {\n      blockVal = r(blockVal, smem[i]);\n    }\n    smem[0] = blockVal;\n  }\n\n  // Sync and broadcast\n  __syncthreads();\n  return smem[0];\n}\n\ntemplate <template <typename> class Reduction1, template <typename> class Reduction2, typename AccumT>\n__device__ __forceinline__ void blockReduce(AccumT* smem, AccumT* reducVal1, AccumT val1, const Reduction1<AccumT>& r1,\n                                            AccumT defaultVal1, AccumT* reducVal2, AccumT val2,\n                                            const Reduction2<AccumT>& r2, AccumT defaultVal2) {\n  // To avoid RaW races from chaining blockReduce calls together, we need a sync here\n  __syncthreads();\n\n  smem[threadIdx.x] = val1;\n  smem[blockDim.x + threadIdx.x] = val2;\n\n  __syncthreads();\n\n  AccumT warpVal1 = defaultVal1;\n  AccumT warpVal2 = defaultVal2;\n\n  // First warp will perform per-warp reductions for the remaining warps\n  uint32_t mask = (((uint64_t)1) << (blockDim.x / 32)) - 1;\n  if (threadIdx.x < 32) {\n    int lane = threadIdx.x % 32;\n    if (lane < blockDim.x / 32) {\n#pragma unroll\n      for (int i = 0; i < 32; ++i) {\n        warpVal1 = r1(warpVal1, smem[lane * 32 + i]);\n        warpVal2 = r2(warpVal2, smem[lane * 32 + i + blockDim.x]);\n      }\n      __syncwarp(mask);\n      smem[lane] = warpVal1;\n      smem[lane + blockDim.x] = warpVal2;\n    }\n  }\n\n  __syncthreads();\n\n  // First thread will perform a reduction of the above per-warp reductions\n  AccumT blockVal1 = defaultVal1;\n  AccumT blockVal2 = defaultVal2;\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < blockDim.x / 32; ++i) {\n      blockVal1 = r1(blockVal1, smem[i]);\n      blockVal2 = r2(blockVal2, smem[i + blockDim.x]);\n    }\n    smem[0] = blockVal1;\n    smem[blockDim.x] = blockVal2;\n  }\n\n  // Sync and broadcast\n  __syncthreads();\n  *reducVal1 = smem[0];\n  *reducVal2 = smem[blockDim.x];\n  __syncthreads();\n}\n\ntemplate <template <typename, typename> class Reduction, int ILP, typename T, typename AccumT>\n__device__ __forceinline__ AccumT ilpReduce(int shift, T* data, int size, const Reduction<T, AccumT>& r,\n                                            AccumT defaultVal) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LoadT;\n  AccumT threadVal = defaultVal;\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    data -= shift;\n    size += shift;\n    if (threadIdx.x >= shift) {\n      threadVal = r(threadVal, data[offset]);\n    }\n    size -= blockDim.x;\n    data += blockDim.x;\n  }\n  int last = size % (ILP * blockDim.x);\n\n  T v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n\n  for (; offset * ILP < (size - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(data)[offset];\n\n    for (int j = 0; j < ILP; ++j) {\n      threadVal = r(threadVal, v[j]);\n    }\n  }\n\n  offset = size - last + threadIdx.x;\n  // Epilogue\n  for (; offset < size; offset += blockDim.x) threadVal = r(threadVal, data[offset]);\n\n  return threadVal;\n}\n\ntemplate <template <typename, typename> class Reduction1, template <typename, typename> class Reduction2, int ILP,\n          typename T, typename AccumT>\n__device__ __forceinline__ void ilpReduce(int shift, T* data, int size, AccumT* reducVal1,\n                                          const Reduction1<T, AccumT>& r1, AccumT defaultVal1, AccumT* reducVal2,\n                                          const Reduction2<T, AccumT>& r2, AccumT defaultVal2) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LoadT;\n\n  AccumT threadVal1 = defaultVal1;\n  AccumT threadVal2 = defaultVal2;\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    data -= shift;\n    size += shift;\n    if (threadIdx.x >= shift) {\n      threadVal1 = r1(threadVal1, data[offset]);\n      threadVal2 = r2(threadVal2, data[offset]);\n    }\n    size -= blockDim.x;\n    data += blockDim.x;\n  }\n  int last = size % (ILP * blockDim.x);\n\n  T v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n\n  for (; offset * ILP < (size - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(data)[offset];\n\n    for (int j = 0; j < ILP; ++j) {\n      threadVal1 = r1(threadVal1, v[j]);\n      threadVal2 = r2(threadVal2, v[j]);\n    }\n  }\n\n  offset = size - last + threadIdx.x;\n  // Epilogue\n  for (; offset < size; offset += blockDim.x) {\n    threadVal1 = r1(threadVal1, data[offset]);\n    threadVal2 = r2(threadVal2, data[offset]);\n  }\n\n  *reducVal1 = threadVal1;\n  *reducVal2 = threadVal2;\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t,\n          template <typename, typename, typename> class Epilogue>\n__global__ void cunn_SoftMaxXEntropyForward(accscalar_t* losses, outscalar_t* max_log_sum_exp, scalar_t* input,\n                                            int64_t* labels, int64_t classes, const float smoothing) {\n  extern __shared__ unsigned char smem[];\n  auto sdata = reinterpret_cast<accscalar_t*>(smem);\n  // forward pointers to batch[blockIdx.x]\n  // each block handles a sample in the mini-batch\n  input += blockIdx.x * classes;\n  // output += blockIdx.x * classes;\n  const int shift = ((uint64_t)input) % ALIGN_BYTES / sizeof(scalar_t);\n\n  int64_t label = labels[blockIdx.x];\n\n  // find the max and sum\n  accscalar_t threadMax, threadSum, max_k, sum_k;\n  ilpReduce<MaxFloat, AddFloat, ILP, scalar_t, accscalar_t>(\n      shift, input, classes, &threadMax, MaxFloat<scalar_t, accscalar_t>(), -at::numeric_limits<accscalar_t>::max(),\n      &threadSum, AddFloat<scalar_t, accscalar_t>(), static_cast<accscalar_t>(0));\n\n  blockReduce<Max, Add, accscalar_t>(sdata, &max_k, threadMax, Max<accscalar_t>(),\n                                     -at::numeric_limits<accscalar_t>::max(), &sum_k, threadSum, Add<accscalar_t>(),\n                                     static_cast<accscalar_t>(0));\n\n  accscalar_t threadExp = ilpReduce<SumExpFloat, ILP, scalar_t, accscalar_t>(\n      shift, input, classes, SumExpFloat<scalar_t, accscalar_t>(max_k), static_cast<accscalar_t>(0));\n  accscalar_t sumAll = blockReduce<Add, accscalar_t>(sdata, threadExp, Add<accscalar_t>(), static_cast<accscalar_t>(0));\n\n  Epilogue<scalar_t, accscalar_t, outscalar_t> epilogue(max_k, sumAll);\n\n  // calculate per element loss with label smoothing\n  // reserve max + log_sum_exp for bprop\n  if (threadIdx.x == 0) {\n    accscalar_t log_prob = epilogue(static_cast<accscalar_t>(input[label]));\n    losses[blockIdx.x] = (max_k + std::log(sumAll) - sum_k / classes) * smoothing - log_prob * (1 - smoothing);\n    max_log_sum_exp[blockIdx.x] = max_k + std::log(sumAll);\n  }\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__device__ __forceinline__ void apply(scalar_t* gradInput, scalar_t* logits, outscalar_t* max_log_sum_exp,\n                                      outscalar_t* gradOutput, int64_t* labels, const float smoothing, int classes) {\n  accscalar_t smooth_positives = 1.0 - smoothing;\n  accscalar_t smooth_negatives = smoothing / classes;\n  accscalar_t tmpGradOutput = gradOutput[blockIdx.x];\n  int64_t label = labels[blockIdx.x];\n  accscalar_t coeff = max_log_sum_exp[blockIdx.x];\n\n  int offset = threadIdx.x;\n  int last = classes % (ILP * blockDim.x);\n\n  for (; offset < classes - last; offset += blockDim.x * ILP) {\n    accscalar_t tmpLogits[ILP];\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j) {\n      tmpLogits[j] = static_cast<accscalar_t>(logits[offset + j * blockDim.x]);\n    }\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j)\n      gradInput[offset + j * blockDim.x] =\n          tmpGradOutput *\n          (std::exp(tmpLogits[j] - coeff) -\n           static_cast<accscalar_t>((offset + j * blockDim.x == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n  }\n\n  for (; offset < classes; offset += blockDim.x)\n    gradInput[offset] =\n        tmpGradOutput * (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n                         static_cast<accscalar_t>((offset == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__device__ __forceinline__ void aligned_apply(int shift, scalar_t* gradInput, scalar_t* logits,\n                                              outscalar_t* max_log_sum_exp, outscalar_t* gradOutput, int64_t* labels,\n                                              const float smoothing, int classes) {\n  accscalar_t smooth_positives = 1.0 - smoothing;\n  accscalar_t smooth_negatives = smoothing / classes;\n  accscalar_t tmpGradOutput = gradOutput[blockIdx.x];\n  int64_t label = labels[blockIdx.x];\n  accscalar_t coeff = max_log_sum_exp[blockIdx.x];\n\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    logits -= shift;\n    gradInput -= shift;\n    classes += shift;\n    if (threadIdx.x >= shift) {\n      gradInput[offset] =\n          tmpGradOutput *\n          (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n           static_cast<accscalar_t>(((offset - shift) == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n    }\n    classes -= blockDim.x;\n    gradInput += blockDim.x;\n    logits += blockDim.x;\n    shift -= blockDim.x;\n  }\n\n  int last = classes % (ILP * blockDim.x);\n\n  typedef typename std::aligned_storage<ILP * sizeof(scalar_t), ILP * alignof(scalar_t)>::type LoadT;\n  // input\n  scalar_t v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n  // output\n  scalar_t r[ILP];\n  LoadT* result = reinterpret_cast<LoadT*>(&r);\n\n  for (; offset * ILP < (classes - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(logits)[offset];\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j) {\n      r[j] =\n          tmpGradOutput * (std::exp(static_cast<accscalar_t>(v[j]) - coeff) -\n                           static_cast<accscalar_t>(((ILP * offset + j - shift) == label) ? 1 : 0) * smooth_positives -\n                           smooth_negatives);\n    }\n    reinterpret_cast<LoadT*>(gradInput)[offset] = *result;\n  }\n\n  offset = classes - last + threadIdx.x;\n  for (; offset < classes; offset += blockDim.x)\n    gradInput[offset] =\n        tmpGradOutput *\n        (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n         static_cast<accscalar_t>(((offset - shift) == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t,\n          template <typename, typename, typename> class Epilogue>\n__global__ void cunn_SoftMaxXEntropyBackward(scalar_t* gradInput, scalar_t* logits, outscalar_t* max_log_sum_exp,\n                                             outscalar_t* gradOutput, int64_t* labels, const float smoothing,\n                                             int classes) {\n  gradInput += blockIdx.x * classes;\n  logits += blockIdx.x * classes;\n\n  // Do vectorized load/store when input/output have same alignment\n  const int shift = ((uint64_t)logits) % ALIGN_BYTES / sizeof(scalar_t);\n  const int shift_ = ((uint64_t)gradInput) % ALIGN_BYTES / sizeof(scalar_t);\n  if (shift == shift_) {\n    aligned_apply<ILP, scalar_t, accscalar_t, outscalar_t>(shift, gradInput, logits, max_log_sum_exp, gradOutput,\n                                                           labels, smoothing, classes);\n  } else {\n    apply<ILP, scalar_t, accscalar_t, outscalar_t>(gradInput, logits, max_log_sum_exp, gradOutput, labels, smoothing,\n                                                   classes);\n  }\n}\n\ntemplate <template <typename, typename, typename> class Epilogue>\nstd::vector<Tensor> host_softmax_xentropy(const Tensor& input_, const Tensor& labels_, const float smoothing,\n                                          const bool half_to_float) {\n  if (half_to_float)\n    TORCH_CHECK(input_.scalar_type() == ScalarType::Half, \"conversion is supported for Half type only\");\n  TORCH_CHECK(labels_.scalar_type() == ScalarType::Long, \"Label type should be CUDA Long\");\n\n  auto input = input_.contiguous();\n  Tensor max_log_sum_exp =\n      at::empty_like(labels_, half_to_float ? input.options().dtype(ScalarType::Float) : input.options());\n  Tensor losses = at::empty_like(labels_, input_.options().dtype(ScalarType::Float));\n\n  static_assert(\n      std::is_same<acc_type<at::Half, true>, float>::value || std::is_same<acc_type<at::Half, true>, double>::value,\n      \"accscalar_t for half should be float or double\");\n  TORCH_CHECK(input.dim() == 2, \"Currently only 2 dim input supported\");\n  TORCH_CHECK(labels_.dim() == 1, \"Labels should be 1 dimensional\");\n  TORCH_CHECK(input.size(0) == labels_.size(0), \"Input and label should have same number of examples\");\n  TORCH_CHECK(input.numel() > 0, \"Number of classes in input should not be 0\");\n\n  const int64_t dim = 1;\n  int64_t outer_size = 1;\n  int64_t dim_size = input.size(dim);\n  int64_t inner_size = 1;\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  for (int64_t i = 0; i < dim; ++i) outer_size *= input.size(i);\n  for (int64_t i = dim + 1; i < input.dim(); ++i) inner_size *= input.size(i);\n  // This kernel spawns a block per each element in the batch.\n  // XXX: it assumes that inner_size == 1\n  TORCH_CHECK(inner_size == 1, \"Currently only inner size 1 supported\");\n\n  dim3 grid(outer_size);\n\n  using namespace at;\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      input.scalar_type(), 0, \"host_softmax_xentropy\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n      const int ILP = sizeof(float4) / sizeof(scalar_t_0); dim3 block = SoftMax_getBlockSize(ILP, dim_size);\n      if (!half_to_float) {\n        cunn_SoftMaxXEntropyForward<ILP, scalar_t_0, accscalar_t, scalar_t_0, Epilogue>\n            <<<grid, block, 2 * block.x * sizeof(accscalar_t), stream>>>(\n                losses.data_ptr<accscalar_t>(), max_log_sum_exp.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n                labels_.data_ptr<int64_t>(), dim_size, smoothing);\n      } else {\n        cunn_SoftMaxXEntropyForward<ILP, scalar_t_0, accscalar_t, accscalar_t, Epilogue>\n            <<<grid, block, 2 * block.x * sizeof(accscalar_t), stream>>>(\n                losses.data_ptr<accscalar_t>(), max_log_sum_exp.data_ptr<accscalar_t>(), input.data_ptr<scalar_t_0>(),\n                labels_.data_ptr<int64_t>(), dim_size, smoothing);\n      });\n\n  C10_CUDA_CHECK(cudaGetLastError());\n\n  std::vector<at::Tensor> ret = {losses, max_log_sum_exp};\n  return ret;\n}\n\ntemplate <template <typename, typename, typename> class Epilogue>\nTensor host_softmax_xentropy_backward(const at::Tensor& grad_loss, const at::Tensor& logits_,\n                                      const at::Tensor& max_log_sum_exp, const at::Tensor& labels,\n                                      const float smoothing, bool half_to_float) {\n  const int64_t dim = 1;\n  Tensor gI = at::empty_like(logits_);\n  if (grad_loss.numel() == 0) {\n    return gI;\n  }\n\n  auto grad = grad_loss.contiguous();\n  auto logits = logits_.contiguous();\n\n  static_assert(\n      std::is_same<acc_type<at::Half, true>, float>::value || std::is_same<acc_type<at::Half, true>, double>::value,\n      \"accscalar_t for half should be float or double\");\n  if (grad.dim() == 0) grad = grad.view(1);\n\n  TORCH_CHECK(logits_.dim() == 2, \"Currently only 2 dim input supported\");\n  TORCH_CHECK(labels.dim() == 1, \"Labels should be 1 dimensional\");\n  TORCH_CHECK(logits_.numel() > 0, \"Number of classes in input should not be 0\");\n  TORCH_CHECK(logits_.size(0) == labels.size(0), \"Input and label should have same number of examples\");\n  TORCH_CHECK(labels.size(0) == grad.size(0), \"Label and loss should have same number of examples\");\n\n  int64_t outer_size = 1;\n  int64_t dim_size = logits.size(dim);\n  int64_t inner_size = 1;\n  for (int64_t i = 0; i < dim; ++i) outer_size *= logits.size(i);\n  for (int64_t i = dim + 1; i < logits.dim(); ++i) inner_size *= logits.size(i);\n  // See descriptions of kernels above.\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  TORCH_CHECK(inner_size == 1, \"Currently only inner size 1 supported\");\n\n  dim3 grid(outer_size);\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      gI.scalar_type(), 0, \"host_softmax_xentropy_backward\", using accscalar_t = acc_type<scalar_t_0, true>;\n      const int ILP = sizeof(float4) / sizeof(scalar_t_0); dim3 block = SoftMax_getBlockSize(ILP, dim_size);\n      if (!half_to_float) {\n        cunn_SoftMaxXEntropyBackward<ILP, scalar_t_0, accscalar_t, scalar_t_0, Epilogue>\n            <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n                gI.data_ptr<scalar_t_0>(), logits.data_ptr<scalar_t_0>(), max_log_sum_exp.data_ptr<scalar_t_0>(),\n                grad.data_ptr<scalar_t_0>(), labels.data_ptr<int64_t>(), smoothing, dim_size);\n      } else {\n        cunn_SoftMaxXEntropyBackward<ILP, scalar_t_0, accscalar_t, accscalar_t, Epilogue>\n            <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n                gI.data_ptr<scalar_t_0>(), logits.data_ptr<scalar_t_0>(), max_log_sum_exp.data_ptr<accscalar_t>(),\n                grad.data_ptr<accscalar_t>(), labels.data_ptr<int64_t>(), smoothing, dim_size);\n      });\n\n  C10_CUDA_CHECK(cudaGetLastError());\n  return gI;\n}\n\nstd::vector<Tensor> softmax_xentropy_cuda(const Tensor& input, const Tensor& labels, const float smoothing,\n                                          const bool half_to_float) {\n  return host_softmax_xentropy<LogSoftMaxForwardEpilogue>(input, labels, smoothing, half_to_float);\n}\n\nat::Tensor softmax_xentropy_backward_cuda(const at::Tensor& grad_loss, const at::Tensor& logits,\n                                          const at::Tensor& max_log_sum_exp, const at::Tensor& labels,\n                                          const float smoothing) {\n  bool half_to_float = grad_loss.scalar_type() != logits.scalar_type();\n  if (half_to_float) {\n    TORCH_CHECK((grad_loss.scalar_type() == ScalarType::Float && logits.scalar_type() == ScalarType::Half),\n                \"expected input and grad types to match, or input to be at::Half and grad to be at::Float\");\n  }\n  return host_softmax_xentropy_backward<LogSoftMaxBackwardEpilogue>(grad_loss, logits, max_log_sum_exp, labels,\n                                                                    smoothing, half_to_float);\n}\n", "output": "/**\n * From PyTorch:\n *\n * Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)\n * Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)\n * Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)\n * Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)\n * Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)\n * Copyright (c) 2011-2013 NYU                      (Clement Farabet)\n * Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)\n * Copyright (c) 2006      Idiap Research Institute (Samy Bengio)\n * Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)\n *\n * From Caffe2:\n *\n * Copyright (c) 2016-present, Facebook Inc. All rights reserved.\n *\n * All contributions by Facebook:\n * Copyright (c) 2016 Facebook Inc.\n *\n * All contributions by Google:\n * Copyright (c) 2015 Google Inc.\n * All rights reserved.\n *\n * All contributions by Yangqing Jia:\n * Copyright (c) 2015 Yangqing Jia\n * All rights reserved.\n *\n * All contributions from Caffe:\n * Copyright(c) 2013, 2014, 2015, the respective contributors\n * All rights reserved.\n *\n * All other contributions:\n * Copyright(c) 2015, 2016 the respective contributors\n * All rights reserved.\n *\n * Caffe2 uses a copyright model similar to Caffe: each contributor holds\n * copyright over their contributions to Caffe2. The project versioning records\n * all such contribution and copyright details. If a contributor wants to further\n * mark their specific copyright on a particular contribution, they should\n * indicate their copyright solely in the commit message of the change when it is\n * committed.\n *\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * 3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America\n *    and IDIAP Research Institute nor the names of its contributors may be\n *    used to endorse or promote products derived from this software without\n *    specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include <ATen/cuda/NumericLimits.cuh>\n\n#include \"type_shim.h\"\n\n#define ALIGN_BYTES 16\n\nusing Tensor = at::Tensor;\nusing TensorList = at::TensorList;\nusing ScalarType = at::ScalarType;\nusing at::acc_type;\n\ntemplate <typename T, typename AccumT, typename OutT>\nstruct LogSoftMaxForwardEpilogue {\n  __device__ __forceinline__ LogSoftMaxForwardEpilogue(AccumT max_input, AccumT sum)\n      : logsum(max_input + std::log(sum)) {}\n\n  __device__ __forceinline__ LogSoftMaxForwardEpilogue(AccumT max_log_sum_exp) : logsum(max_log_sum_exp) {}\n\n  __device__ __forceinline__ OutT operator()(T input) const { return static_cast<OutT>(input - logsum); }\n\n  const AccumT logsum;\n};\n\ntemplate <typename T, typename AccumT, typename OutT>\nstruct LogSoftMaxBackwardEpilogue {\n  __device__ __forceinline__ LogSoftMaxBackwardEpilogue(AccumT sum) : sum(sum) {}\n\n  __device__ __forceinline__ T operator()(OutT gradOutput, OutT output) const {\n    return static_cast<T>(gradOutput - std::exp(static_cast<AccumT>(output)) * sum);\n  }\n\n  const AccumT sum;\n};\n\nconst int max_threads = 1024;\n\ninline dim3 SoftMax_getBlockSize(int ILP, uint64_t dim_size) {\n  uint64_t block_size = 1;\n  uint64_t max_block_size = std::min(dim_size / ILP, static_cast<uint64_t>(max_threads));\n  while (block_size < (max_block_size / 2)) block_size *= 2;\n  // Launch at least a single warp - the kernel assumes that.\n  block_size = std::max(block_size, static_cast<uint64_t>(32));\n  return dim3(block_size);\n}\n\ntemplate <typename T>\nstruct Add {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a + b; }\n};\n\ntemplate <typename T>\nstruct Max {\n  __device__ __forceinline__ T operator()(T a, T b) const { return a < b ? b : a; }\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Regular kernel (fast when dim_size is large; requires inner_size == 1)\n////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, typename AccumT>\nstruct MaxFloat {\n  __device__ __forceinline__ AccumT operator()(AccumT max, T v) const { return ::max(max, (AccumT)v); }\n};\n\ntemplate <typename T, typename AccumT>\nstruct AddFloat {\n  __device__ __forceinline__ AccumT operator()(AccumT sum, T v) const { return sum + v; }\n};\n\ntemplate <typename T, typename AccumT>\nstruct SumExpFloat {\n  __device__ __forceinline__ SumExpFloat(AccumT v) : max_k(v) {}\n\n  __device__ __forceinline__ AccumT operator()(AccumT sum, T v) const { return sum + std::exp(v - max_k); }\n\n  const AccumT max_k;\n};\n\ntemplate <template <typename> class Reduction, typename AccumT>\n__device__ __forceinline__ AccumT blockReduce(AccumT* smem, AccumT val, const Reduction<AccumT>& r, AccumT defaultVal) {\n  // To avoid RaW races from chaining blockReduce calls together, we need a sync here\n  __syncthreads();\n\n  smem[threadIdx.x] = val;\n\n  __syncthreads();\n\n  AccumT warpVal = defaultVal;\n\n  // First warp will perform per-warp reductions for the remaining warps\n  uint32_t mask = (((uint64_t)1) << (blockDim.x / 32)) - 1;\n  if (threadIdx.x < 32) {\n    int lane = threadIdx.x % 32;\n    if (lane < blockDim.x / 32) {\n#pragma unroll\n      for (int i = 0; i < 32; ++i) {\n        warpVal = r(warpVal, smem[lane * 32 + i]);\n      }\n      __syncwarp(mask);\n      smem[lane] = warpVal;\n    }\n  }\n\n  __syncthreads();\n\n  // First thread will perform a reduction of the above per-warp reductions\n  AccumT blockVal = defaultVal;\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < blockDim.x / 32; ++i) {\n      blockVal = r(blockVal, smem[i]);\n    }\n    smem[0] = blockVal;\n  }\n\n  // Sync and broadcast\n  __syncthreads();\n  return smem[0];\n}\n\ntemplate <template <typename> class Reduction1, template <typename> class Reduction2, typename AccumT>\n__device__ __forceinline__ void blockReduce(AccumT* smem, AccumT* reducVal1, AccumT val1, const Reduction1<AccumT>& r1,\n                                            AccumT defaultVal1, AccumT* reducVal2, AccumT val2,\n                                            const Reduction2<AccumT>& r2, AccumT defaultVal2) {\n  // To avoid RaW races from chaining blockReduce calls together, we need a sync here\n  __syncthreads();\n\n  smem[threadIdx.x] = val1;\n  smem[blockDim.x + threadIdx.x] = val2;\n\n  __syncthreads();\n\n  AccumT warpVal1 = defaultVal1;\n  AccumT warpVal2 = defaultVal2;\n\n  // First warp will perform per-warp reductions for the remaining warps\n  uint32_t mask = (((uint64_t)1) << (blockDim.x / 32)) - 1;\n  if (threadIdx.x < 32) {\n    int lane = threadIdx.x % 32;\n    if (lane < blockDim.x / 32) {\n#pragma unroll\n      for (int i = 0; i < 32; ++i) {\n        warpVal1 = r1(warpVal1, smem[lane * 32 + i]);\n        warpVal2 = r2(warpVal2, smem[lane * 32 + i + blockDim.x]);\n      }\n      __syncwarp(mask);\n      smem[lane] = warpVal1;\n      smem[lane + blockDim.x] = warpVal2;\n    }\n  }\n\n  __syncthreads();\n\n  // First thread will perform a reduction of the above per-warp reductions\n  AccumT blockVal1 = defaultVal1;\n  AccumT blockVal2 = defaultVal2;\n\n  if (threadIdx.x == 0) {\n    for (int i = 0; i < blockDim.x / 32; ++i) {\n      blockVal1 = r1(blockVal1, smem[i]);\n      blockVal2 = r2(blockVal2, smem[i + blockDim.x]);\n    }\n    smem[0] = blockVal1;\n    smem[blockDim.x] = blockVal2;\n  }\n\n  // Sync and broadcast\n  __syncthreads();\n  *reducVal1 = smem[0];\n  *reducVal2 = smem[blockDim.x];\n  __syncthreads();\n}\n\ntemplate <template <typename, typename> class Reduction, int ILP, typename T, typename AccumT>\n__device__ __forceinline__ AccumT ilpReduce(int shift, T* data, int size, const Reduction<T, AccumT>& r,\n                                            AccumT defaultVal) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LoadT;\n  AccumT threadVal = defaultVal;\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    data -= shift;\n    size += shift;\n    if (threadIdx.x >= shift) {\n      threadVal = r(threadVal, data[offset]);\n    }\n    size -= blockDim.x;\n    data += blockDim.x;\n  }\n  int last = size % (ILP * blockDim.x);\n\n  T v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n\n  for (; offset * ILP < (size - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(data)[offset];\n\n    for (int j = 0; j < ILP; ++j) {\n      threadVal = r(threadVal, v[j]);\n    }\n  }\n\n  offset = size - last + threadIdx.x;\n  // Epilogue\n  for (; offset < size; offset += blockDim.x) threadVal = r(threadVal, data[offset]);\n\n  return threadVal;\n}\n\ntemplate <template <typename, typename> class Reduction1, template <typename, typename> class Reduction2, int ILP,\n          typename T, typename AccumT>\n__device__ __forceinline__ void ilpReduce(int shift, T* data, int size, AccumT* reducVal1,\n                                          const Reduction1<T, AccumT>& r1, AccumT defaultVal1, AccumT* reducVal2,\n                                          const Reduction2<T, AccumT>& r2, AccumT defaultVal2) {\n  typedef typename std::aligned_storage<ILP * sizeof(T), ILP * alignof(T)>::type LoadT;\n\n  AccumT threadVal1 = defaultVal1;\n  AccumT threadVal2 = defaultVal2;\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    data -= shift;\n    size += shift;\n    if (threadIdx.x >= shift) {\n      threadVal1 = r1(threadVal1, data[offset]);\n      threadVal2 = r2(threadVal2, data[offset]);\n    }\n    size -= blockDim.x;\n    data += blockDim.x;\n  }\n  int last = size % (ILP * blockDim.x);\n\n  T v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n\n  for (; offset * ILP < (size - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(data)[offset];\n\n    for (int j = 0; j < ILP; ++j) {\n      threadVal1 = r1(threadVal1, v[j]);\n      threadVal2 = r2(threadVal2, v[j]);\n    }\n  }\n\n  offset = size - last + threadIdx.x;\n  // Epilogue\n  for (; offset < size; offset += blockDim.x) {\n    threadVal1 = r1(threadVal1, data[offset]);\n    threadVal2 = r2(threadVal2, data[offset]);\n  }\n\n  *reducVal1 = threadVal1;\n  *reducVal2 = threadVal2;\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t,\n          template <typename, typename, typename> class Epilogue>\n__global__ void cunn_SoftMaxXEntropyForward(accscalar_t* losses, outscalar_t* max_log_sum_exp, scalar_t* input,\n                                            int64_t* labels, int64_t classes, const float smoothing) {\n  extern __shared__ unsigned char smem[];\n  auto sdata = reinterpret_cast<accscalar_t*>(smem);\n  // forward pointers to batch[blockIdx.x]\n  // each block handles a sample in the mini-batch\n  input += blockIdx.x * classes;\n  // output += blockIdx.x * classes;\n  const int shift = ((uint64_t)input) % ALIGN_BYTES / sizeof(scalar_t);\n\n  int64_t label = labels[blockIdx.x];\n\n  // find the max and sum\n  accscalar_t threadMax, threadSum, max_k, sum_k;\n  ilpReduce<MaxFloat, AddFloat, ILP, scalar_t, accscalar_t>(\n      shift, input, classes, &threadMax, MaxFloat<scalar_t, accscalar_t>(), -at::numeric_limits<accscalar_t>::max(),\n      &threadSum, AddFloat<scalar_t, accscalar_t>(), static_cast<accscalar_t>(0));\n\n  blockReduce<Max, Add, accscalar_t>(sdata, &max_k, threadMax, Max<accscalar_t>(),\n                                     -at::numeric_limits<accscalar_t>::max(), &sum_k, threadSum, Add<accscalar_t>(),\n                                     static_cast<accscalar_t>(0));\n\n  accscalar_t threadExp = ilpReduce<SumExpFloat, ILP, scalar_t, accscalar_t>(\n      shift, input, classes, SumExpFloat<scalar_t, accscalar_t>(max_k), static_cast<accscalar_t>(0));\n  accscalar_t sumAll = blockReduce<Add, accscalar_t>(sdata, threadExp, Add<accscalar_t>(), static_cast<accscalar_t>(0));\n\n  Epilogue<scalar_t, accscalar_t, outscalar_t> epilogue(max_k, sumAll);\n\n  // calculate per element loss with label smoothing\n  // reserve max + log_sum_exp for bprop\n  if (threadIdx.x == 0) {\n    accscalar_t log_prob = epilogue(static_cast<accscalar_t>(input[label]));\n    losses[blockIdx.x] = (max_k + std::log(sumAll) - sum_k / classes) * smoothing - log_prob * (1 - smoothing);\n    max_log_sum_exp[blockIdx.x] = max_k + std::log(sumAll);\n  }\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__device__ __forceinline__ void apply(scalar_t* gradInput, scalar_t* logits, outscalar_t* max_log_sum_exp,\n                                      outscalar_t* gradOutput, int64_t* labels, const float smoothing, int classes) {\n  accscalar_t smooth_positives = 1.0 - smoothing;\n  accscalar_t smooth_negatives = smoothing / classes;\n  accscalar_t tmpGradOutput = gradOutput[blockIdx.x];\n  int64_t label = labels[blockIdx.x];\n  accscalar_t coeff = max_log_sum_exp[blockIdx.x];\n\n  int offset = threadIdx.x;\n  int last = classes % (ILP * blockDim.x);\n\n  for (; offset < classes - last; offset += blockDim.x * ILP) {\n    accscalar_t tmpLogits[ILP];\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j) {\n      tmpLogits[j] = static_cast<accscalar_t>(logits[offset + j * blockDim.x]);\n    }\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j)\n      gradInput[offset + j * blockDim.x] =\n          tmpGradOutput *\n          (std::exp(tmpLogits[j] - coeff) -\n           static_cast<accscalar_t>((offset + j * blockDim.x == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n  }\n\n  for (; offset < classes; offset += blockDim.x)\n    gradInput[offset] =\n        tmpGradOutput * (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n                         static_cast<accscalar_t>((offset == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__device__ __forceinline__ void aligned_apply(int shift, scalar_t* gradInput, scalar_t* logits,\n                                              outscalar_t* max_log_sum_exp, outscalar_t* gradOutput, int64_t* labels,\n                                              const float smoothing, int classes) {\n  accscalar_t smooth_positives = 1.0 - smoothing;\n  accscalar_t smooth_negatives = smoothing / classes;\n  accscalar_t tmpGradOutput = gradOutput[blockIdx.x];\n  int64_t label = labels[blockIdx.x];\n  accscalar_t coeff = max_log_sum_exp[blockIdx.x];\n\n  int offset = threadIdx.x;\n\n  // shift and do 1\n  if (shift > 0) {\n    logits -= shift;\n    gradInput -= shift;\n    classes += shift;\n    if (threadIdx.x >= shift) {\n      gradInput[offset] =\n          tmpGradOutput *\n          (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n           static_cast<accscalar_t>(((offset - shift) == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n    }\n    classes -= blockDim.x;\n    gradInput += blockDim.x;\n    logits += blockDim.x;\n    shift -= blockDim.x;\n  }\n\n  int last = classes % (ILP * blockDim.x);\n\n  typedef typename std::aligned_storage<ILP * sizeof(scalar_t), ILP * alignof(scalar_t)>::type LoadT;\n  // input\n  scalar_t v[ILP];\n  LoadT* value = reinterpret_cast<LoadT*>(&v);\n  // output\n  scalar_t r[ILP];\n  LoadT* result = reinterpret_cast<LoadT*>(&r);\n\n  for (; offset * ILP < (classes - last); offset += blockDim.x) {\n    *value = reinterpret_cast<LoadT*>(logits)[offset];\n\n#pragma unroll\n    for (int j = 0; j < ILP; ++j) {\n      r[j] =\n          tmpGradOutput * (std::exp(static_cast<accscalar_t>(v[j]) - coeff) -\n                           static_cast<accscalar_t>(((ILP * offset + j - shift) == label) ? 1 : 0) * smooth_positives -\n                           smooth_negatives);\n    }\n    reinterpret_cast<LoadT*>(gradInput)[offset] = *result;\n  }\n\n  offset = classes - last + threadIdx.x;\n  for (; offset < classes; offset += blockDim.x)\n    gradInput[offset] =\n        tmpGradOutput *\n        (std::exp(static_cast<accscalar_t>(logits[offset]) - coeff) -\n         static_cast<accscalar_t>(((offset - shift) == label) ? 1 : 0) * smooth_positives - smooth_negatives);\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t,\n          template <typename, typename, typename> class Epilogue>\n__global__ void cunn_SoftMaxXEntropyBackward(scalar_t* gradInput, scalar_t* logits, outscalar_t* max_log_sum_exp,\n                                             outscalar_t* gradOutput, int64_t* labels, const float smoothing,\n                                             int classes) {\n  gradInput += blockIdx.x * classes;\n  logits += blockIdx.x * classes;\n\n  // Do vectorized load/store when input/output have same alignment\n  const int shift = ((uint64_t)logits) % ALIGN_BYTES / sizeof(scalar_t);\n  const int shift_ = ((uint64_t)gradInput) % ALIGN_BYTES / sizeof(scalar_t);\n  if (shift == shift_) {\n    aligned_apply<ILP, scalar_t, accscalar_t, outscalar_t>(shift, gradInput, logits, max_log_sum_exp, gradOutput,\n                                                           labels, smoothing, classes);\n  } else {\n    apply<ILP, scalar_t, accscalar_t, outscalar_t>(gradInput, logits, max_log_sum_exp, gradOutput, labels, smoothing,\n                                                   classes);\n  }\n}\n\ntemplate <template <typename, typename, typename> class Epilogue>\nstd::vector<Tensor> host_softmax_xentropy(const Tensor& input_, const Tensor& labels_, const float smoothing,\n                                          const bool half_to_float) {\n  if (half_to_float)\n    TORCH_CHECK(input_.scalar_type() == ScalarType::Half, \"conversion is supported for Half type only\");\n  TORCH_CHECK(labels_.scalar_type() == ScalarType::Long, \"Label type should be CUDA Long\");\n\n  auto input = input_.contiguous();\n  Tensor max_log_sum_exp =\n      at::empty_like(labels_, half_to_float ? input.options().dtype(ScalarType::Float) : input.options());\n  Tensor losses = at::empty_like(labels_, input_.options().dtype(ScalarType::Float));\n\n  static_assert(\n      std::is_same<acc_type<at::Half, true>, float>::value || std::is_same<acc_type<at::Half, true>, double>::value,\n      \"accscalar_t for half should be float or double\");\n  TORCH_CHECK(input.dim() == 2, \"Currently only 2 dim input supported\");\n  TORCH_CHECK(labels_.dim() == 1, \"Labels should be 1 dimensional\");\n  TORCH_CHECK(input.size(0) == labels_.size(0), \"Input and label should have same number of examples\");\n  TORCH_CHECK(input.numel() > 0, \"Number of classes in input should not be 0\");\n\n  const int64_t dim = 1;\n  int64_t outer_size = 1;\n  int64_t dim_size = input.size(dim);\n  int64_t inner_size = 1;\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  for (int64_t i = 0; i < dim; ++i) outer_size *= input.size(i);\n  for (int64_t i = dim + 1; i < input.dim(); ++i) inner_size *= input.size(i);\n  // This kernel spawns a block per each element in the batch.\n  // XXX: it assumes that inner_size == 1\n  TORCH_CHECK(inner_size == 1, \"Currently only inner size 1 supported\");\n\n  dim3 grid(outer_size);\n\n  using namespace at;\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      input.scalar_type(), 0, \"host_softmax_xentropy\", using accscalar_t = at::acc_type<scalar_t_0, true>;\n      const int ILP = sizeof(float4) / sizeof(scalar_t_0); dim3 block = SoftMax_getBlockSize(ILP, dim_size);\n      if (!half_to_float) {\n        cunn_SoftMaxXEntropyForward<ILP, scalar_t_0, accscalar_t, scalar_t_0, Epilogue>\n            <<<grid, block, 2 * block.x * sizeof(accscalar_t), stream>>>(\n                losses.data_ptr<accscalar_t>(), max_log_sum_exp.data_ptr<scalar_t_0>(), input.data_ptr<scalar_t_0>(),\n                labels_.data_ptr<int64_t>(), dim_size, smoothing);\n      } else {\n        cunn_SoftMaxXEntropyForward<ILP, scalar_t_0, accscalar_t, accscalar_t, Epilogue>\n            <<<grid, block, 2 * block.x * sizeof(accscalar_t), stream>>>(\n                losses.data_ptr<accscalar_t>(), max_log_sum_exp.data_ptr<accscalar_t>(), input.data_ptr<scalar_t_0>(),\n                labels_.data_ptr<int64_t>(), dim_size, smoothing);\n      });\n\n  C10_CUDA_CHECK(hipGetLastError());\n\n  std::vector<at::Tensor> ret = {losses, max_log_sum_exp};\n  return ret;\n}\n\ntemplate <template <typename, typename, typename> class Epilogue>\nTensor host_softmax_xentropy_backward(const at::Tensor& grad_loss, const at::Tensor& logits_,\n                                      const at::Tensor& max_log_sum_exp, const at::Tensor& labels,\n                                      const float smoothing, bool half_to_float) {\n  const int64_t dim = 1;\n  Tensor gI = at::empty_like(logits_);\n  if (grad_loss.numel() == 0) {\n    return gI;\n  }\n\n  auto grad = grad_loss.contiguous();\n  auto logits = logits_.contiguous();\n\n  static_assert(\n      std::is_same<acc_type<at::Half, true>, float>::value || std::is_same<acc_type<at::Half, true>, double>::value,\n      \"accscalar_t for half should be float or double\");\n  if (grad.dim() == 0) grad = grad.view(1);\n\n  TORCH_CHECK(logits_.dim() == 2, \"Currently only 2 dim input supported\");\n  TORCH_CHECK(labels.dim() == 1, \"Labels should be 1 dimensional\");\n  TORCH_CHECK(logits_.numel() > 0, \"Number of classes in input should not be 0\");\n  TORCH_CHECK(logits_.size(0) == labels.size(0), \"Input and label should have same number of examples\");\n  TORCH_CHECK(labels.size(0) == grad.size(0), \"Label and loss should have same number of examples\");\n\n  int64_t outer_size = 1;\n  int64_t dim_size = logits.size(dim);\n  int64_t inner_size = 1;\n  for (int64_t i = 0; i < dim; ++i) outer_size *= logits.size(i);\n  for (int64_t i = dim + 1; i < logits.dim(); ++i) inner_size *= logits.size(i);\n  // See descriptions of kernels above.\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  TORCH_CHECK(inner_size == 1, \"Currently only inner size 1 supported\");\n\n  dim3 grid(outer_size);\n\n  DISPATCH_FLOAT_HALF_AND_BFLOAT(\n      gI.scalar_type(), 0, \"host_softmax_xentropy_backward\", using accscalar_t = acc_type<scalar_t_0, true>;\n      const int ILP = sizeof(float4) / sizeof(scalar_t_0); dim3 block = SoftMax_getBlockSize(ILP, dim_size);\n      if (!half_to_float) {\n        cunn_SoftMaxXEntropyBackward<ILP, scalar_t_0, accscalar_t, scalar_t_0, Epilogue>\n            <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n                gI.data_ptr<scalar_t_0>(), logits.data_ptr<scalar_t_0>(), max_log_sum_exp.data_ptr<scalar_t_0>(),\n                grad.data_ptr<scalar_t_0>(), labels.data_ptr<int64_t>(), smoothing, dim_size);\n      } else {\n        cunn_SoftMaxXEntropyBackward<ILP, scalar_t_0, accscalar_t, accscalar_t, Epilogue>\n            <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n                gI.data_ptr<scalar_t_0>(), logits.data_ptr<scalar_t_0>(), max_log_sum_exp.data_ptr<accscalar_t>(),\n                grad.data_ptr<accscalar_t>(), labels.data_ptr<int64_t>(), smoothing, dim_size);\n      });\n\n  C10_CUDA_CHECK(hipGetLastError());\n  return gI;\n}\n\nstd::vector<Tensor> softmax_xentropy_cuda(const Tensor& input, const Tensor& labels, const float smoothing,\n                                          const bool half_to_float) {\n  return host_softmax_xentropy<LogSoftMaxForwardEpilogue>(input, labels, smoothing, half_to_float);\n}\n\nat::Tensor softmax_xentropy_backward_cuda(const at::Tensor& grad_loss, const at::Tensor& logits,\n                                          const at::Tensor& max_log_sum_exp, const at::Tensor& labels,\n                                          const float smoothing) {\n  bool half_to_float = grad_loss.scalar_type() != logits.scalar_type();\n  if (half_to_float) {\n    TORCH_CHECK((grad_loss.scalar_type() == ScalarType::Float && logits.scalar_type() == ScalarType::Half),\n                \"expected input and grad types to match, or input to be at::Half and grad to be at::Float\");\n  }\n  return host_softmax_xentropy_backward<LogSoftMaxBackwardEpilogue>(grad_loss, logits, max_log_sum_exp, labels,\n                                                                    smoothing, half_to_float);\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/xentropy/xentropy_kernel.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n#include \"fmha_dgrad_kernel_1xN_reload_nl.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<512, 64, 16, 1, 8, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_512_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\ntemplate <int CHUNKS>\n__global__ void fmha_dgrad_fp16_512_64_sm80_nl_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN_nl<CHUNKS, Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN_nl<CHUNKS, Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_512_64_sm80(const Fused_multihead_attention_fprop_params& params, cudaStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 512 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(fmha_dgrad_fp16_512_64_sm80_kernel,\n                                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_512_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n\nvoid run_fmha_dgrad_fp16_512_64_sm80_nl(const Fused_multihead_attention_fprop_params& params, const int num_chunks,\n                                        cudaStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 512 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  auto kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<2>;\n\n  if (num_chunks == 2) {\n    kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<2>;\n  } else if (num_chunks == 3) {\n    kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<3>;\n  } else {\n    assert(false && \"Unsupperted number of chunks\");\n  }\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  dim3 grid(params.h, params.b, num_chunks);\n\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n#include \"fmha_dgrad_kernel_1xN_reload_nl.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<512, 64, 16, 1, 8, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_512_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\ntemplate <int CHUNKS>\n__global__ void fmha_dgrad_fp16_512_64_sm80_nl_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN_nl<CHUNKS, Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN_nl<CHUNKS, Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_512_64_sm80(const Fused_multihead_attention_fprop_params& params, hipStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 512 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fmha_dgrad_fp16_512_64_sm80_kernel),\n                                         hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_512_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n\nvoid run_fmha_dgrad_fp16_512_64_sm80_nl(const Fused_multihead_attention_fprop_params& params, const int num_chunks,\n                                        hipStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 512 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  auto kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<2>;\n\n  if (num_chunks == 2) {\n    kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<2>;\n  } else if (num_chunks == 3) {\n    kernel = fmha_dgrad_fp16_512_64_sm80_nl_kernel<3>;\n  } else {\n    assert(false && \"Unsupperted number of chunks\");\n  }\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  dim3 grid(params.h, params.b, num_chunks);\n\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_dgrad_fp16_512_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<128, 64, 16, 1, 4, 0x08u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_128_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_128_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_128_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_128_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      cudaOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<128, 64, 16, 1, 4, 0x08u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_128_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_128_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_128_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_128_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      hipOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_fprop_fp16_128_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<384, 64, 16, 1, 8, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_384_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_384_64_sm80(const Fused_multihead_attention_fprop_params& params, cudaStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 384 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(fmha_dgrad_fp16_384_64_sm80_kernel,\n                                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_384_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<384, 64, 16, 1, 8, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_384_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_384_64_sm80(const Fused_multihead_attention_fprop_params& params, hipStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 384 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fmha_dgrad_fp16_384_64_sm80_kernel),\n                                         hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_384_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_dgrad_fp16_384_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<384, 64, 16, 1, 4, 0x18u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_384_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_384_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_384_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_384_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      cudaOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<384, 64, 16, 1, 4, 0x18u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_384_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_384_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_384_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_384_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      hipOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_fprop_fp16_384_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<256, 64, 16, 1, 4, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_256_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_256_64_sm80(const Fused_multihead_attention_fprop_params& params, cudaStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 256 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(fmha_dgrad_fp16_256_64_sm80_kernel,\n                                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_256_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<256, 64, 16, 1, 4, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_256_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_256_64_sm80(const Fused_multihead_attention_fprop_params& params, hipStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 256 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fmha_dgrad_fp16_256_64_sm80_kernel),\n                                         hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_256_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_dgrad_fp16_256_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<128, 64, 16, 1, 4, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_128_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_128_64_sm80(const Fused_multihead_attention_fprop_params& params, cudaStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 128 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(fmha_dgrad_fp16_128_64_sm80_kernel,\n                                         cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_128_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_dgrad_kernel_1xN_reload.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<128, 64, 16, 1, 4, 0x08u>;\n\nextern \"C\" __global__ void fmha_dgrad_fp16_128_64_sm80_kernel(Fused_multihead_attention_fprop_params params) {\n  fmha::compute_dv_1xN<Kernel_traits>(params);\n  fmha::compute_dq_dk_1xN<Kernel_traits>(params);\n}\n\nvoid run_fmha_dgrad_fp16_128_64_sm80(const Fused_multihead_attention_fprop_params& params, hipStream_t stream) {\n  constexpr int smem_size_softmax = Kernel_traits::Cta_tile_p::M * Kernel_traits::Cta_tile_p::WARPS_N * sizeof(float);\n  constexpr int smem_size_q = Kernel_traits::Smem_tile_q::BYTES_PER_TILE;\n  constexpr int smem_size_v = Kernel_traits::Smem_tile_v::BYTES_PER_TILE;\n  constexpr int smem_size_o = Kernel_traits::Smem_tile_o::BYTES_PER_TILE;\n\n  using Smem_tile_s = fmha::Smem_tile_mma_transposed<Kernel_traits::Cta_tile_p>;\n  constexpr int smem_size_s = Smem_tile_s::BYTES_PER_TILE;\n  static_assert(smem_size_s == 16 * 128 * 2);\n  static_assert(smem_size_o == 16 * 64 * 4 * Kernel_traits::Cta_tile_p::WARPS_N);\n\n  constexpr int smem_size_dv = smem_size_s + 2 * smem_size_q + smem_size_v + smem_size_softmax;\n  constexpr int smem_size_dq_dk = smem_size_s + smem_size_o + smem_size_q + smem_size_v;\n  constexpr int smem_size = std::max(smem_size_dv, smem_size_dq_dk);\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(fmha_dgrad_fp16_128_64_sm80_kernel),\n                                         hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n  dim3 grid(params.h, params.b);\n  fmha_dgrad_fp16_128_64_sm80_kernel<<<grid, Kernel_traits::THREADS, smem_size, stream>>>(params);\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_dgrad_fp16_128_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n\ninline __device__ float4 ldg128(const void* ptr) { return *static_cast<const float4*>(ptr); }\n\ninline __device__ void stg128(void* ptr, const float4& data) { *static_cast<float4*>(ptr) = data; }\n\ntemplate <typename T, int THREADS, int HIDDEN_SIZE, int CHUNKS>\n__global__ __launch_bounds__(THREADS) void fmha_noloop_reduce_kernel(void* __restrict__ out,\n                                                                     const void* __restrict__ in,\n                                                                     const int* __restrict__ cu_seqlens,\n                                                                     const int batch_size) {\n  enum { BYTES_PER_LDG = 16 };\n  enum { NUM_ELTS = BYTES_PER_LDG / sizeof(T) };\n\n  // One CTA hidden vector for K and V\n  enum { BYTES_PER_ROW = HIDDEN_SIZE * sizeof(T) * 2 };\n  // The stride in bytes in dQKV\n  enum { OUT_STRIDE_BYTES = 3 * HIDDEN_SIZE * sizeof(T) };\n  // The offset in bytes in dQKV to the dKV part for non-interleaved heads\n  enum { OUT_OFFSET_KV_BYTES = HIDDEN_SIZE * sizeof(T) };\n\n  static_assert(BYTES_PER_ROW == HIDDEN_SIZE * 2 * sizeof(T));\n\n  // Size in bytes of the input tile\n  enum { BYTES_PER_TILE = CHUNKS * BYTES_PER_ROW };\n\n  enum { BYTES_PER_CTA = THREADS * BYTES_PER_LDG };\n\n  enum { LDGS = BYTES_PER_ROW / BYTES_PER_CTA };\n  static_assert(BYTES_PER_CTA * LDGS == BYTES_PER_ROW);\n\n  union Vec_t {\n    float4 raw;\n    T elt[NUM_ELTS];\n  };\n\n  // ZERO-OUT invalid positions in dQKV\n  const int total = cu_seqlens[batch_size];\n  if (blockIdx.x >= total) {\n    enum { BYTES_PER_QKV_ROW = 3 * HIDDEN_SIZE * sizeof(T) };\n    enum { STGS = BYTES_PER_QKV_ROW / BYTES_PER_LDG };\n\n    const float4 zeros = make_float4(0.f, 0.f, 0.f, 0.f);\n\n    char* base_ptr = static_cast<char*>(out) + blockIdx.x * OUT_STRIDE_BYTES;\n\n    for (int tidx = threadIdx.x; tidx < STGS; tidx += THREADS) {\n      stg128(base_ptr + tidx * BYTES_PER_LDG, zeros);\n    }\n\n    return;\n  }\n\n  // SETUP\n  const int offset_in = blockIdx.x * BYTES_PER_TILE + threadIdx.x * BYTES_PER_LDG;\n  const char* ptr_in = static_cast<const char*>(in) + offset_in;\n\n  const int offset_out = blockIdx.x * OUT_STRIDE_BYTES + threadIdx.x * BYTES_PER_LDG;\n  char* ptr_out = static_cast<char*>(out) + OUT_OFFSET_KV_BYTES + offset_out;\n\n  // LOAD\n\n  Vec_t local_in[CHUNKS][LDGS];\n\n#pragma unroll\n  for (int c = 0; c < CHUNKS; c++) {\n#pragma unroll\n    for (int l = 0; l < LDGS; l++) {\n      int offset = c * BYTES_PER_ROW + l * BYTES_PER_CTA;\n      local_in[c][l].raw = ldg128(ptr_in + offset);\n    }\n  }\n\n  // UNPACK\n  float acc[LDGS][NUM_ELTS];\n\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n    for (int e = 0; e < NUM_ELTS; e++) {\n      acc[l][e] = float(local_in[0][l].elt[e]);\n    }\n  }\n\n// COMPUTE\n#pragma unroll\n  for (int c = 1; c < CHUNKS; c++) {\n#pragma unroll\n    for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n      for (int e = 0; e < NUM_ELTS; e++) {\n        acc[l][e] += float(local_in[c][l].elt[e]);\n      }\n    }\n  }\n\n  // PACK\n  Vec_t local_out[LDGS];\n\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n    for (int e = 0; e < NUM_ELTS; e++) {\n      local_out[l].elt[e] = T(acc[l][e]);\n    }\n  }\n\n// STORE\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n    const int offset = l * BYTES_PER_CTA;\n    stg128(ptr_out + offset, local_out[l].raw);\n  }\n}\n\nvoid fmha_run_noloop_reduce(void* out, const void* in, const int* cu_seqlens, const int hidden_size,\n                            const int batch_size, const int total, const int num_chunks, cudaStream_t stream) {\n  const int blocks = total;\n\n  if (hidden_size == 1024) {\n    constexpr int HIDDEN_SIZE = 1024;\n    constexpr int THREADS = 256;\n\n    if (num_chunks == 2) {\n      fmha_noloop_reduce_kernel<half, THREADS, HIDDEN_SIZE, 2>\n          <<<blocks, THREADS, 0, stream>>>(out, in, cu_seqlens, batch_size);\n    } else if (num_chunks == 3) {\n      fmha_noloop_reduce_kernel<half, THREADS, HIDDEN_SIZE, 3>\n          <<<blocks, THREADS, 0, stream>>>(out, in, cu_seqlens, batch_size);\n    } else {\n      assert(false && \"Unsupported num_chunks\");\n    }\n\n  } else {\n    assert(false && \"Unsupported hidden_size\");\n  }\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n\ninline __device__ float4 ldg128(const void* ptr) { return *static_cast<const float4*>(ptr); }\n\ninline __device__ void stg128(void* ptr, const float4& data) { *static_cast<float4*>(ptr) = data; }\n\ntemplate <typename T, int THREADS, int HIDDEN_SIZE, int CHUNKS>\n__global__ __launch_bounds__(THREADS) void fmha_noloop_reduce_kernel(void* __restrict__ out,\n                                                                     const void* __restrict__ in,\n                                                                     const int* __restrict__ cu_seqlens,\n                                                                     const int batch_size) {\n  enum { BYTES_PER_LDG = 16 };\n  enum { NUM_ELTS = BYTES_PER_LDG / sizeof(T) };\n\n  // One CTA hidden vector for K and V\n  enum { BYTES_PER_ROW = HIDDEN_SIZE * sizeof(T) * 2 };\n  // The stride in bytes in dQKV\n  enum { OUT_STRIDE_BYTES = 3 * HIDDEN_SIZE * sizeof(T) };\n  // The offset in bytes in dQKV to the dKV part for non-interleaved heads\n  enum { OUT_OFFSET_KV_BYTES = HIDDEN_SIZE * sizeof(T) };\n\n  static_assert(BYTES_PER_ROW == HIDDEN_SIZE * 2 * sizeof(T));\n\n  // Size in bytes of the input tile\n  enum { BYTES_PER_TILE = CHUNKS * BYTES_PER_ROW };\n\n  enum { BYTES_PER_CTA = THREADS * BYTES_PER_LDG };\n\n  enum { LDGS = BYTES_PER_ROW / BYTES_PER_CTA };\n  static_assert(BYTES_PER_CTA * LDGS == BYTES_PER_ROW);\n\n  union Vec_t {\n    float4 raw;\n    T elt[NUM_ELTS];\n  };\n\n  // ZERO-OUT invalid positions in dQKV\n  const int total = cu_seqlens[batch_size];\n  if (blockIdx.x >= total) {\n    enum { BYTES_PER_QKV_ROW = 3 * HIDDEN_SIZE * sizeof(T) };\n    enum { STGS = BYTES_PER_QKV_ROW / BYTES_PER_LDG };\n\n    const float4 zeros = make_float4(0.f, 0.f, 0.f, 0.f);\n\n    char* base_ptr = static_cast<char*>(out) + blockIdx.x * OUT_STRIDE_BYTES;\n\n    for (int tidx = threadIdx.x; tidx < STGS; tidx += THREADS) {\n      stg128(base_ptr + tidx * BYTES_PER_LDG, zeros);\n    }\n\n    return;\n  }\n\n  // SETUP\n  const int offset_in = blockIdx.x * BYTES_PER_TILE + threadIdx.x * BYTES_PER_LDG;\n  const char* ptr_in = static_cast<const char*>(in) + offset_in;\n\n  const int offset_out = blockIdx.x * OUT_STRIDE_BYTES + threadIdx.x * BYTES_PER_LDG;\n  char* ptr_out = static_cast<char*>(out) + OUT_OFFSET_KV_BYTES + offset_out;\n\n  // LOAD\n\n  Vec_t local_in[CHUNKS][LDGS];\n\n#pragma unroll\n  for (int c = 0; c < CHUNKS; c++) {\n#pragma unroll\n    for (int l = 0; l < LDGS; l++) {\n      int offset = c * BYTES_PER_ROW + l * BYTES_PER_CTA;\n      local_in[c][l].raw = ldg128(ptr_in + offset);\n    }\n  }\n\n  // UNPACK\n  float acc[LDGS][NUM_ELTS];\n\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n    for (int e = 0; e < NUM_ELTS; e++) {\n      acc[l][e] = float(local_in[0][l].elt[e]);\n    }\n  }\n\n// COMPUTE\n#pragma unroll\n  for (int c = 1; c < CHUNKS; c++) {\n#pragma unroll\n    for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n      for (int e = 0; e < NUM_ELTS; e++) {\n        acc[l][e] += float(local_in[c][l].elt[e]);\n      }\n    }\n  }\n\n  // PACK\n  Vec_t local_out[LDGS];\n\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n#pragma unroll\n    for (int e = 0; e < NUM_ELTS; e++) {\n      local_out[l].elt[e] = T(acc[l][e]);\n    }\n  }\n\n// STORE\n#pragma unroll\n  for (int l = 0; l < LDGS; l++) {\n    const int offset = l * BYTES_PER_CTA;\n    stg128(ptr_out + offset, local_out[l].raw);\n  }\n}\n\nvoid fmha_run_noloop_reduce(void* out, const void* in, const int* cu_seqlens, const int hidden_size,\n                            const int batch_size, const int total, const int num_chunks, hipStream_t stream) {\n  const int blocks = total;\n\n  if (hidden_size == 1024) {\n    constexpr int HIDDEN_SIZE = 1024;\n    constexpr int THREADS = 256;\n\n    if (num_chunks == 2) {\n      fmha_noloop_reduce_kernel<half, THREADS, HIDDEN_SIZE, 2>\n          <<<blocks, THREADS, 0, stream>>>(out, in, cu_seqlens, batch_size);\n    } else if (num_chunks == 3) {\n      fmha_noloop_reduce_kernel<half, THREADS, HIDDEN_SIZE, 3>\n          <<<blocks, THREADS, 0, stream>>>(out, in, cu_seqlens, batch_size);\n    } else {\n      assert(false && \"Unsupported num_chunks\");\n    }\n\n  } else {\n    assert(false && \"Unsupported hidden_size\");\n  }\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_noloop_reduce.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<512, 64, 16, 1, 8, 0x00u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_512_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_512_64_sm80_kernel_nl(Fused_multihead_attention_fprop_params params,\n                                                      const int num_full_heads, const int num_main_groups,\n                                                      const int main_group_size, const int main_steps,\n                                                      const int rest_steps) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, num_full_heads, num_main_groups, main_group_size, main_steps,\n                                               rest_steps);\n}\n\nvoid run_fmha_fp16_512_64_sm80_(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                                const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_512_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_512_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      cudaOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n\nvoid run_fmha_fp16_512_64_sm80_nl_(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                                   const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_512_64_sm80_kernel_nl<true>\n                                          : &fmha_fprop_fp16_512_64_sm80_kernel_nl<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      cudaOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  if (configure) {\n    const int heads_total = launch_params.params.b * launch_params.params.h;\n    std::tie(launch_params.num_full_heads, launch_params.num_main_groups, launch_params.heads_last_wave,\n             launch_params.main_steps, launch_params.rest_steps, launch_params.elts_per_thread) =\n        fmha::work_dist<Kernel_traits>(total_ctas, heads_total);\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(\n      launch_params.params, launch_params.num_full_heads, launch_params.num_main_groups, launch_params.heads_last_wave,\n      launch_params.main_steps, launch_params.rest_steps);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n\nvoid run_fmha_fp16_512_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  if (launch_params.is_nl) {\n    run_fmha_fp16_512_64_sm80_nl_(launch_params, configure);\n  } else {\n    run_fmha_fp16_512_64_sm80_(launch_params, configure);\n  }\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<512, 64, 16, 1, 8, 0x00u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_512_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_512_64_sm80_kernel_nl(Fused_multihead_attention_fprop_params params,\n                                                      const int num_full_heads, const int num_main_groups,\n                                                      const int main_group_size, const int main_steps,\n                                                      const int rest_steps) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, num_full_heads, num_main_groups, main_group_size, main_steps,\n                                               rest_steps);\n}\n\nvoid run_fmha_fp16_512_64_sm80_(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                                const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_512_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_512_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      hipOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n\nvoid run_fmha_fp16_512_64_sm80_nl_(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                                   const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_512_64_sm80_kernel_nl<true>\n                                          : &fmha_fprop_fp16_512_64_sm80_kernel_nl<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      hipOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  if (configure) {\n    const int heads_total = launch_params.params.b * launch_params.params.h;\n    std::tie(launch_params.num_full_heads, launch_params.num_main_groups, launch_params.heads_last_wave,\n             launch_params.main_steps, launch_params.rest_steps, launch_params.elts_per_thread) =\n        fmha::work_dist<Kernel_traits>(total_ctas, heads_total);\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(\n      launch_params.params, launch_params.num_full_heads, launch_params.num_main_groups, launch_params.heads_last_wave,\n      launch_params.main_steps, launch_params.rest_steps);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n\nvoid run_fmha_fp16_512_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  if (launch_params.is_nl) {\n    run_fmha_fp16_512_64_sm80_nl_(launch_params, configure);\n  } else {\n    run_fmha_fp16_512_64_sm80_(launch_params, configure);\n  }\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_fprop_fp16_512_64_kernel.sm80.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include <ATen/Dispatch.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <torch/extension.h>\n\nconstexpr int block_size = 512;\nconstexpr int ctas_per_sm = 4;\n\ntemplate <typename scalar_t>\n__global__ void __launch_bounds__(block_size)\n    mha_fill_kernel(scalar_t* out_tensor, const int32_t* const start_row, const size_t num_rows) {\n  size_t row_stride = gridDim.y * blockDim.x;\n  size_t row_index = blockIdx.x + (size_t)start_row[0];\n  size_t col_index = blockIdx.y * blockDim.x + threadIdx.x;\n  while (row_index < num_rows) {\n    out_tensor[row_index * row_stride + col_index] = 0;\n    row_index += gridDim.x;\n  }\n}\n\nat::Tensor& mha_fill(at::Tensor& self, const at::Tensor& start_index) {\n  auto max_tokens = self.size(0);\n  auto self_2d = self.view({max_tokens, -1});\n  auto fcd_size = self_2d.size(1);\n  TORCH_CHECK(self.is_contiguous(), \"input not contiguous\");\n  TORCH_CHECK(fcd_size % block_size == 0, \"input size not aligned to block size\");\n  const int num_mp = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n  uint64_t num_blk_y = (uint64_t)(fcd_size / block_size);\n  uint64_t num_blk_x = (uint64_t)std::ceil(num_mp * ctas_per_sm / num_blk_y);\n  dim3 dim_grid(num_blk_x, num_blk_y);\n  dim3 dim_block(block_size);\n\n  AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2(\n      at::ScalarType::Half, at::ScalarType::BFloat16, self_2d.scalar_type(), \"mha_padding_fill_\", [&]() {\n        mha_fill_kernel<<<dim_grid, dim_block, 0, at::cuda::getCurrentCUDAStream()>>>(\n            self_2d.data_ptr<scalar_t>(), start_index.data_ptr<int32_t>(), max_tokens);\n        C10_CUDA_KERNEL_LAUNCH_CHECK();\n      });\n  return self;\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <ATen/Dispatch.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <torch/extension.h>\n\nconstexpr int block_size = 512;\nconstexpr int ctas_per_sm = 4;\n\ntemplate <typename scalar_t>\n__global__ void __launch_bounds__(block_size)\n    mha_fill_kernel(scalar_t* out_tensor, const int32_t* const start_row, const size_t num_rows) {\n  size_t row_stride = gridDim.y * blockDim.x;\n  size_t row_index = blockIdx.x + (size_t)start_row[0];\n  size_t col_index = blockIdx.y * blockDim.x + threadIdx.x;\n  while (row_index < num_rows) {\n    out_tensor[row_index * row_stride + col_index] = 0;\n    row_index += gridDim.x;\n  }\n}\n\nat::Tensor& mha_fill(at::Tensor& self, const at::Tensor& start_index) {\n  auto max_tokens = self.size(0);\n  auto self_2d = self.view({max_tokens, -1});\n  auto fcd_size = self_2d.size(1);\n  TORCH_CHECK(self.is_contiguous(), \"input not contiguous\");\n  TORCH_CHECK(fcd_size % block_size == 0, \"input size not aligned to block size\");\n  const int num_mp = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;\n  uint64_t num_blk_y = (uint64_t)(fcd_size / block_size);\n  uint64_t num_blk_x = (uint64_t)std::ceil(num_mp * ctas_per_sm / num_blk_y);\n  dim3 dim_grid(num_blk_x, num_blk_y);\n  dim3 dim_block(block_size);\n\n  AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2(\n      at::ScalarType::Half, at::ScalarType::BFloat16, self_2d.scalar_type(), \"mha_padding_fill_\", [&]() {\n        mha_fill_kernel<<<dim_grid, dim_block, 0, at::cuda::getCurrentCUDAStream()>>>(\n            self_2d.data_ptr<scalar_t>(), start_index.data_ptr<int32_t>(), max_tokens);\n        C10_CUDA_KERNEL_LAUNCH_CHECK();\n      });\n  return self;\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_fill.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<256, 64, 16, 1, 4, 0x08u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_256_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_256_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_256_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_256_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      cudaOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(cudaPeekAtLastError());\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2011-2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"fmha.h\"\n#include \"fmha_fprop_kernel_1xN.h\"\n\nusing Kernel_traits = FMHA_kernel_traits<256, 64, 16, 1, 4, 0x08u>;\n\ntemplate <bool Is_training>\n__global__ void fmha_fprop_fp16_256_64_sm80_kernel(Fused_multihead_attention_fprop_params params,\n                                                   const int total_heads) {\n  fmha::device_1xN<Kernel_traits, Is_training>(params, total_heads);\n}\n\nvoid run_fmha_fp16_256_64_sm80(Launch_params<Fused_multihead_attention_fprop_params>& launch_params,\n                               const bool configure) {\n  auto kernel = launch_params.is_training ? &fmha_fprop_fp16_256_64_sm80_kernel<true>\n                                          : &fmha_fprop_fp16_256_64_sm80_kernel<false>;\n\n  constexpr int smem_size = fmha::get_dynamic_smem_size<Kernel_traits>();\n\n  if (smem_size >= 48 * 1024) {\n    FMHA_CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n  }\n\n  const int sm_count = launch_params.props->multiProcessorCount;\n  int ctas_per_sm;\n  FMHA_CHECK_CUDA(\n      hipOccupancyMaxActiveBlocksPerMultiprocessor(&ctas_per_sm, kernel, Kernel_traits::THREADS, smem_size));\n  int total_ctas = sm_count * ctas_per_sm;\n\n  const int heads_total = launch_params.params.b * launch_params.params.h;\n  if (configure) {\n    using Mma_tile_p = fmha::Hmma_tile<typename Kernel_traits::Cta_tile_p>;\n    constexpr size_t STEPS = Kernel_traits::Cta_tile_p::N / Kernel_traits::Cta_tile_p::M;\n    constexpr size_t MMAS_M = Mma_tile_p::MMAS_M;\n    constexpr size_t MMAS_N = Mma_tile_p::MMAS_N;\n\n    size_t heads_per_cta = ((heads_total + total_ctas - 1) / total_ctas);\n    size_t elts_per_head = STEPS * MMAS_M * MMAS_N * 8;\n    launch_params.elts_per_thread = heads_per_cta * elts_per_head;\n    return;\n  }\n\n  dim3 grid(total_ctas);\n  kernel<<<grid, Kernel_traits::THREADS, smem_size, launch_params.stream>>>(launch_params.params, heads_total);\n\n  FMHA_CHECK_CUDA(hipPeekAtLastError());\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/fmha/src/fmha_fprop_fp16_256_64_kernel.sm80.cu"}
{"input": "#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n// Use 128-bit vectorization\ntypedef uint4 vector_t;\n\n#define ASSERT_ALIGNED(DTYPE, PTR) \\\n  TORCH_INTERNAL_ASSERT(is_aligned<DTYPE>(PTR), \"Tensor \" #PTR \" is not \" #DTYPE \" aligned\")\n\ntemplate <class T>\nbool is_aligned(const void* ptr) noexcept {\n  auto iptr = reinterpret_cast<std::uintptr_t>(ptr);\n  return !(iptr % alignof(T));\n}\n\ntemplate <bool SMOOTHING, int ILP, typename scalar_t, typename labelscalar_t, typename accscalar_t,\n          typename outscalar_t>\n__global__ void focal_loss_forward_cuda_kernel(outscalar_t* loss, scalar_t* partial_grad,\n                                               const scalar_t* __restrict__ cls_output,\n                                               const labelscalar_t* __restrict__ cls_targets_at_level,\n                                               const float* __restrict__ num_positives_sum, const int64_t num_examples,\n                                               const int64_t num_classes, const int64_t num_real_classes,\n                                               const float alpha, const float gamma, const float smoothing_factor) {\n  extern __shared__ unsigned char shm[];\n  accscalar_t* loss_shm = reinterpret_cast<accscalar_t*>(shm);\n  loss_shm[threadIdx.x] = 0;\n  accscalar_t loss_acc = 0;\n\n  accscalar_t one = accscalar_t(1.0);\n  accscalar_t K = accscalar_t(2.0);\n  accscalar_t normalizer = one / static_cast<accscalar_t>(num_positives_sum[0]);\n  accscalar_t nn_norm, np_norm, pn_norm, pp_norm;\n\n  // *_norm is used for label smoothing only\n  if (SMOOTHING) {\n    nn_norm = one - smoothing_factor / K;\n    np_norm = smoothing_factor / K;\n    pn_norm = smoothing_factor - smoothing_factor / K;\n    pp_norm = one - smoothing_factor + smoothing_factor / K;\n  }\n\n  vector_t p_vec, grad_vec;\n\n  // Accumulate loss on each thread\n  for (int64_t i = (blockIdx.x * blockDim.x + threadIdx.x) * ILP; i < num_examples * num_classes;\n       i += gridDim.x * blockDim.x * ILP) {\n    int64_t idy = i / num_classes;\n    labelscalar_t y = cls_targets_at_level[idy];\n    int64_t base_yid = i % num_classes;\n\n    int64_t pos_idx = idy * num_classes + y;\n    p_vec = *(vector_t*)&cls_output[i];  // Vectorized load\n\n    // Skip ignored matches\n    if (y == -2) {\n#pragma unroll\n      for (int j = 0; j < ILP; j++) {\n        *((scalar_t*)(&grad_vec) + j) = 0;\n      }\n      *(vector_t*)&partial_grad[i] = grad_vec;\n      continue;\n    }\n\n#pragma unroll\n    for (int j = 0; j < ILP; j++) {\n      // Skip the pad classes\n      if (base_yid + j >= num_real_classes) {\n        *((scalar_t*)(&grad_vec) + j) = 0;\n        continue;\n      }\n\n      accscalar_t p = static_cast<accscalar_t>(*((scalar_t*)(&p_vec) + j));\n      accscalar_t exp_np = ::exp(-p);\n      accscalar_t exp_pp = ::exp(p);\n      accscalar_t sigma = one / (one + exp_np);\n      accscalar_t logee = (p >= 0) ? exp_np : exp_pp;\n      accscalar_t addee = (p >= 0) ? 0 : -p;\n      accscalar_t off_a = addee + ::log(one + logee);\n\n      // Negative matches\n      accscalar_t base = SMOOTHING ? nn_norm * p : p;\n      accscalar_t off_b = (SMOOTHING ? np_norm : 0) - sigma;\n      accscalar_t coeff_f1 = one - alpha;\n      accscalar_t coeff_f2 = sigma;\n      accscalar_t coeff_b1 = gamma;\n      accscalar_t coeff_b2 = one - sigma;\n\n      // Positive matches\n      if (y >= 0 && (i + j == pos_idx)) {\n        base = SMOOTHING ? pn_norm * p : 0;\n        off_b = (SMOOTHING ? pp_norm : one) - sigma;\n        coeff_f1 = alpha;\n        coeff_f2 = one - sigma;\n        coeff_b1 = -gamma;\n        coeff_b2 = sigma;\n      }\n\n      accscalar_t coeff_f = coeff_f1 * ::pow(coeff_f2, gamma);\n      accscalar_t coeff_b = coeff_b1 * coeff_b2;\n\n      accscalar_t loss_t = coeff_f * (base + off_a);\n      accscalar_t grad = coeff_f * (coeff_b * (base + off_a) - off_b);\n\n      // Delay the normalize of partial gradient by num_positives_sum to back\n      // propagation because scalar_t reduces precision. Focal loss is very\n      // sensitive to the small gradient. No worry on overflow here since\n      // gradient has relative smaller range than input.\n      loss_acc += loss_t;\n      *((scalar_t*)(&grad_vec) + j) = static_cast<scalar_t>(grad);\n    }\n\n    // This may generate two vectorized stores instead of one\n    *(vector_t*)&partial_grad[i] = grad_vec;\n  }\n  loss_shm[threadIdx.x] = loss_acc;\n\n  // Intra-CTA reduction\n  __syncthreads();\n  for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n    if (threadIdx.x < s) {\n      loss_shm[threadIdx.x] += loss_shm[threadIdx.x + s];\n    }\n    __syncthreads();\n  }\n\n  // Inter-CTA reduction\n  if (threadIdx.x == 0) {\n    loss_acc = loss_shm[0] * normalizer;\n    atomicAdd(loss, loss_acc);\n  }\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__global__ void focal_loss_backward_cuda_kernel(scalar_t* partial_grad, const outscalar_t* __restrict__ grad_output,\n                                                const float* __restrict__ num_positives_sum, const uint64_t numel) {\n  int64_t idx = (blockIdx.x * blockDim.x + threadIdx.x) * ILP;\n\n  accscalar_t normalizer = static_cast<accscalar_t>(grad_output[0]) / static_cast<accscalar_t>(num_positives_sum[0]);\n\n  // The input is enforced to pad to use vector load, thus there's no need to\n  // check whether the last element of ILP can out of bound.\n  if (idx >= numel) return;\n\n  vector_t grad_vec;\n  grad_vec = *(vector_t*)&partial_grad[idx];\n#pragma unroll(ILP)\n  for (int i = 0; i < ILP; i++) {\n    auto grad = static_cast<accscalar_t>(*((scalar_t*)(&grad_vec) + i));\n    grad *= normalizer;\n    *((scalar_t*)(&grad_vec) + i) = static_cast<scalar_t>(grad);\n  }\n  *(vector_t*)&partial_grad[idx] = grad_vec;\n}\n\nstd::vector<at::Tensor> focal_loss_forward_cuda(const at::Tensor& cls_output, const at::Tensor& cls_targets_at_level,\n                                                const at::Tensor& num_positives_sum, const int64_t num_real_classes,\n                                                const float alpha, const float gamma, const float smoothing_factor) {\n  // Checks required for correctness\n  TORCH_INTERNAL_ASSERT(cls_output.size(-1) >= num_real_classes, \"Incorrect number of real classes.\");\n  TORCH_INTERNAL_ASSERT(cls_targets_at_level.scalar_type() == at::kLong, \"Invalid label type.\");\n  TORCH_INTERNAL_ASSERT((num_positives_sum.numel() == 1) && (num_positives_sum.scalar_type() == at::kFloat),\n                        \"Expect num_positives_sum to be a float32 tensor with only one element.\");\n  TORCH_INTERNAL_ASSERT(cls_output.dim() == cls_targets_at_level.dim() + 1,\n                        \"Mis-matched dimensions between class output and label.\");\n  for (int64_t i = 0; i < cls_targets_at_level.dim(); i++)\n    TORCH_INTERNAL_ASSERT(cls_output.size(i) == cls_targets_at_level.size(i),\n                          \"Mis-matched shape between class output and label.\");\n\n  // Checks required for better performance\n  const int ILP = sizeof(vector_t) / cls_output.element_size();\n  ASSERT_ALIGNED(vector_t, cls_output.data_ptr());\n  TORCH_INTERNAL_ASSERT(cls_output.size(-1) % ILP == 0,\n                        \"Pad number of classes first to take advantage of vectorized load.\");\n  TORCH_INTERNAL_ASSERT(num_real_classes >= ILP, \"Too few classes.\");\n\n  int64_t num_classes = cls_output.size(-1);\n  int64_t num_examples = cls_output.numel() / num_classes;\n  at::Tensor loss = at::zeros({}, cls_output.options().dtype(at::kFloat));\n\n  // Compute the incompelete gradient during fprop since most of the heavy\n  // functions of bprop are the same as fprop, thus trade memory for compute\n  // helps with focal loss.\n  at::Tensor partial_grad = at::empty_like(cls_output);\n\n  // Set the number of CTAs per SM according to the compute capability.\n  // Each CTA loops on input with stride till the last item.\n  cudaDeviceProp props;\n  cudaGetDeviceProperties(&props, at::cuda::current_device());\n  int cta_per_sm = 2;\n  if (props.major >= 10) {\n    cta_per_sm = 8;\n  }\n  dim3 block(512);\n  dim3 grid(cta_per_sm * props.multiProcessorCount);\n\n  // Specialize on label smoothing or not to reduce redundant operations\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  if (smoothing_factor == 0.0f) {\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(cls_output.scalar_type(), \"focal_loss_fprop\", [&] {\n      using accscalar_t = at::acc_type<scalar_t, true>;\n      using labelscalar_t = int64_t;\n      using outscalar_t = float;\n      const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n      focal_loss_forward_cuda_kernel<false, ILP, scalar_t, labelscalar_t, accscalar_t, outscalar_t>\n          <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n              loss.data_ptr<outscalar_t>(), partial_grad.data_ptr<scalar_t>(), cls_output.data_ptr<scalar_t>(),\n              cls_targets_at_level.data_ptr<labelscalar_t>(), num_positives_sum.data_ptr<float>(), num_examples,\n              num_classes, num_real_classes, alpha, gamma, smoothing_factor);\n    });\n  } else {\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(cls_output.scalar_type(), \"focal_loss_fprop\", [&] {\n      using accscalar_t = at::acc_type<scalar_t, true>;\n      using labelscalar_t = int64_t;\n      using outscalar_t = float;\n      const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n      focal_loss_forward_cuda_kernel<true, ILP, scalar_t, labelscalar_t, accscalar_t, outscalar_t>\n          <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n              loss.data_ptr<outscalar_t>(), partial_grad.data_ptr<scalar_t>(), cls_output.data_ptr<scalar_t>(),\n              cls_targets_at_level.data_ptr<labelscalar_t>(), num_positives_sum.data_ptr<float>(), num_examples,\n              num_classes, num_real_classes, alpha, gamma, smoothing_factor);\n    });\n  }\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  return {loss, partial_grad};\n}\n\nat::Tensor focal_loss_backward_cuda(const at::Tensor& grad_output, const at::Tensor& partial_grad,\n                                    const at::Tensor& num_positives_sum) {\n  // Each thread process ILP elements\n  const int ILP = sizeof(vector_t) / partial_grad.element_size();\n  dim3 block(512);\n  dim3 grid((partial_grad.numel() + block.x * ILP - 1) / (block.x * ILP));\n\n  cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(partial_grad.scalar_type(), \"focal_loss_bprop\", [&] {\n    using accscalar_t = at::acc_type<scalar_t, true>;\n    using outscalar_t = float;\n    const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n    focal_loss_backward_cuda_kernel<ILP, scalar_t, accscalar_t, outscalar_t>\n        <<<grid, block, 0, stream>>>(partial_grad.data_ptr<scalar_t>(), grad_output.data_ptr<outscalar_t>(),\n                                     num_positives_sum.data_ptr<float>(), partial_grad.numel());\n  });\n\n  AT_CUDA_CHECK(cudaGetLastError());\n  return partial_grad;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <ATen/ATen.h>\n#include <ATen/AccumulateType.h>\n#include <ATen/cuda/CUDAContext.h>\n\n// Use 128-bit vectorization\ntypedef uint4 vector_t;\n\n#define ASSERT_ALIGNED(DTYPE, PTR) \\\n  TORCH_INTERNAL_ASSERT(is_aligned<DTYPE>(PTR), \"Tensor \" #PTR \" is not \" #DTYPE \" aligned\")\n\ntemplate <class T>\nbool is_aligned(const void* ptr) noexcept {\n  auto iptr = reinterpret_cast<std::uintptr_t>(ptr);\n  return !(iptr % alignof(T));\n}\n\ntemplate <bool SMOOTHING, int ILP, typename scalar_t, typename labelscalar_t, typename accscalar_t,\n          typename outscalar_t>\n__global__ void focal_loss_forward_cuda_kernel(outscalar_t* loss, scalar_t* partial_grad,\n                                               const scalar_t* __restrict__ cls_output,\n                                               const labelscalar_t* __restrict__ cls_targets_at_level,\n                                               const float* __restrict__ num_positives_sum, const int64_t num_examples,\n                                               const int64_t num_classes, const int64_t num_real_classes,\n                                               const float alpha, const float gamma, const float smoothing_factor) {\n  extern __shared__ unsigned char shm[];\n  accscalar_t* loss_shm = reinterpret_cast<accscalar_t*>(shm);\n  loss_shm[threadIdx.x] = 0;\n  accscalar_t loss_acc = 0;\n\n  accscalar_t one = accscalar_t(1.0);\n  accscalar_t K = accscalar_t(2.0);\n  accscalar_t normalizer = one / static_cast<accscalar_t>(num_positives_sum[0]);\n  accscalar_t nn_norm, np_norm, pn_norm, pp_norm;\n\n  // *_norm is used for label smoothing only\n  if (SMOOTHING) {\n    nn_norm = one - smoothing_factor / K;\n    np_norm = smoothing_factor / K;\n    pn_norm = smoothing_factor - smoothing_factor / K;\n    pp_norm = one - smoothing_factor + smoothing_factor / K;\n  }\n\n  vector_t p_vec, grad_vec;\n\n  // Accumulate loss on each thread\n  for (int64_t i = (blockIdx.x * blockDim.x + threadIdx.x) * ILP; i < num_examples * num_classes;\n       i += gridDim.x * blockDim.x * ILP) {\n    int64_t idy = i / num_classes;\n    labelscalar_t y = cls_targets_at_level[idy];\n    int64_t base_yid = i % num_classes;\n\n    int64_t pos_idx = idy * num_classes + y;\n    p_vec = *(vector_t*)&cls_output[i];  // Vectorized load\n\n    // Skip ignored matches\n    if (y == -2) {\n#pragma unroll\n      for (int j = 0; j < ILP; j++) {\n        *((scalar_t*)(&grad_vec) + j) = 0;\n      }\n      *(vector_t*)&partial_grad[i] = grad_vec;\n      continue;\n    }\n\n#pragma unroll\n    for (int j = 0; j < ILP; j++) {\n      // Skip the pad classes\n      if (base_yid + j >= num_real_classes) {\n        *((scalar_t*)(&grad_vec) + j) = 0;\n        continue;\n      }\n\n      accscalar_t p = static_cast<accscalar_t>(*((scalar_t*)(&p_vec) + j));\n      accscalar_t exp_np = ::exp(-p);\n      accscalar_t exp_pp = ::exp(p);\n      accscalar_t sigma = one / (one + exp_np);\n      accscalar_t logee = (p >= 0) ? exp_np : exp_pp;\n      accscalar_t addee = (p >= 0) ? 0 : -p;\n      accscalar_t off_a = addee + ::log(one + logee);\n\n      // Negative matches\n      accscalar_t base = SMOOTHING ? nn_norm * p : p;\n      accscalar_t off_b = (SMOOTHING ? np_norm : 0) - sigma;\n      accscalar_t coeff_f1 = one - alpha;\n      accscalar_t coeff_f2 = sigma;\n      accscalar_t coeff_b1 = gamma;\n      accscalar_t coeff_b2 = one - sigma;\n\n      // Positive matches\n      if (y >= 0 && (i + j == pos_idx)) {\n        base = SMOOTHING ? pn_norm * p : 0;\n        off_b = (SMOOTHING ? pp_norm : one) - sigma;\n        coeff_f1 = alpha;\n        coeff_f2 = one - sigma;\n        coeff_b1 = -gamma;\n        coeff_b2 = sigma;\n      }\n\n      accscalar_t coeff_f = coeff_f1 * ::pow(coeff_f2, gamma);\n      accscalar_t coeff_b = coeff_b1 * coeff_b2;\n\n      accscalar_t loss_t = coeff_f * (base + off_a);\n      accscalar_t grad = coeff_f * (coeff_b * (base + off_a) - off_b);\n\n      // Delay the normalize of partial gradient by num_positives_sum to back\n      // propagation because scalar_t reduces precision. Focal loss is very\n      // sensitive to the small gradient. No worry on overflow here since\n      // gradient has relative smaller range than input.\n      loss_acc += loss_t;\n      *((scalar_t*)(&grad_vec) + j) = static_cast<scalar_t>(grad);\n    }\n\n    // This may generate two vectorized stores instead of one\n    *(vector_t*)&partial_grad[i] = grad_vec;\n  }\n  loss_shm[threadIdx.x] = loss_acc;\n\n  // Intra-CTA reduction\n  __syncthreads();\n  for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n    if (threadIdx.x < s) {\n      loss_shm[threadIdx.x] += loss_shm[threadIdx.x + s];\n    }\n    __syncthreads();\n  }\n\n  // Inter-CTA reduction\n  if (threadIdx.x == 0) {\n    loss_acc = loss_shm[0] * normalizer;\n    atomicAdd(loss, loss_acc);\n  }\n}\n\ntemplate <int ILP, typename scalar_t, typename accscalar_t, typename outscalar_t>\n__global__ void focal_loss_backward_cuda_kernel(scalar_t* partial_grad, const outscalar_t* __restrict__ grad_output,\n                                                const float* __restrict__ num_positives_sum, const uint64_t numel) {\n  int64_t idx = (blockIdx.x * blockDim.x + threadIdx.x) * ILP;\n\n  accscalar_t normalizer = static_cast<accscalar_t>(grad_output[0]) / static_cast<accscalar_t>(num_positives_sum[0]);\n\n  // The input is enforced to pad to use vector load, thus there's no need to\n  // check whether the last element of ILP can out of bound.\n  if (idx >= numel) return;\n\n  vector_t grad_vec;\n  grad_vec = *(vector_t*)&partial_grad[idx];\n#pragma unroll(ILP)\n  for (int i = 0; i < ILP; i++) {\n    auto grad = static_cast<accscalar_t>(*((scalar_t*)(&grad_vec) + i));\n    grad *= normalizer;\n    *((scalar_t*)(&grad_vec) + i) = static_cast<scalar_t>(grad);\n  }\n  *(vector_t*)&partial_grad[idx] = grad_vec;\n}\n\nstd::vector<at::Tensor> focal_loss_forward_cuda(const at::Tensor& cls_output, const at::Tensor& cls_targets_at_level,\n                                                const at::Tensor& num_positives_sum, const int64_t num_real_classes,\n                                                const float alpha, const float gamma, const float smoothing_factor) {\n  // Checks required for correctness\n  TORCH_INTERNAL_ASSERT(cls_output.size(-1) >= num_real_classes, \"Incorrect number of real classes.\");\n  TORCH_INTERNAL_ASSERT(cls_targets_at_level.scalar_type() == at::kLong, \"Invalid label type.\");\n  TORCH_INTERNAL_ASSERT((num_positives_sum.numel() == 1) && (num_positives_sum.scalar_type() == at::kFloat),\n                        \"Expect num_positives_sum to be a float32 tensor with only one element.\");\n  TORCH_INTERNAL_ASSERT(cls_output.dim() == cls_targets_at_level.dim() + 1,\n                        \"Mis-matched dimensions between class output and label.\");\n  for (int64_t i = 0; i < cls_targets_at_level.dim(); i++)\n    TORCH_INTERNAL_ASSERT(cls_output.size(i) == cls_targets_at_level.size(i),\n                          \"Mis-matched shape between class output and label.\");\n\n  // Checks required for better performance\n  const int ILP = sizeof(vector_t) / cls_output.element_size();\n  ASSERT_ALIGNED(vector_t, cls_output.data_ptr());\n  TORCH_INTERNAL_ASSERT(cls_output.size(-1) % ILP == 0,\n                        \"Pad number of classes first to take advantage of vectorized load.\");\n  TORCH_INTERNAL_ASSERT(num_real_classes >= ILP, \"Too few classes.\");\n\n  int64_t num_classes = cls_output.size(-1);\n  int64_t num_examples = cls_output.numel() / num_classes;\n  at::Tensor loss = at::zeros({}, cls_output.options().dtype(at::kFloat));\n\n  // Compute the incompelete gradient during fprop since most of the heavy\n  // functions of bprop are the same as fprop, thus trade memory for compute\n  // helps with focal loss.\n  at::Tensor partial_grad = at::empty_like(cls_output);\n\n  // Set the number of CTAs per SM according to the compute capability.\n  // Each CTA loops on input with stride till the last item.\n  hipDeviceProp_t props;\n  hipGetDeviceProperties(&props, at::cuda::current_device());\n  int cta_per_sm = 2;\n  if (props.major >= 10) {\n    cta_per_sm = 8;\n  }\n  dim3 block(512);\n  dim3 grid(cta_per_sm * props.multiProcessorCount);\n\n  // Specialize on label smoothing or not to reduce redundant operations\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  if (smoothing_factor == 0.0f) {\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(cls_output.scalar_type(), \"focal_loss_fprop\", [&] {\n      using accscalar_t = at::acc_type<scalar_t, true>;\n      using labelscalar_t = int64_t;\n      using outscalar_t = float;\n      const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n      focal_loss_forward_cuda_kernel<false, ILP, scalar_t, labelscalar_t, accscalar_t, outscalar_t>\n          <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n              loss.data_ptr<outscalar_t>(), partial_grad.data_ptr<scalar_t>(), cls_output.data_ptr<scalar_t>(),\n              cls_targets_at_level.data_ptr<labelscalar_t>(), num_positives_sum.data_ptr<float>(), num_examples,\n              num_classes, num_real_classes, alpha, gamma, smoothing_factor);\n    });\n  } else {\n    AT_DISPATCH_FLOATING_TYPES_AND_HALF(cls_output.scalar_type(), \"focal_loss_fprop\", [&] {\n      using accscalar_t = at::acc_type<scalar_t, true>;\n      using labelscalar_t = int64_t;\n      using outscalar_t = float;\n      const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n      focal_loss_forward_cuda_kernel<true, ILP, scalar_t, labelscalar_t, accscalar_t, outscalar_t>\n          <<<grid, block, block.x * sizeof(accscalar_t), stream>>>(\n              loss.data_ptr<outscalar_t>(), partial_grad.data_ptr<scalar_t>(), cls_output.data_ptr<scalar_t>(),\n              cls_targets_at_level.data_ptr<labelscalar_t>(), num_positives_sum.data_ptr<float>(), num_examples,\n              num_classes, num_real_classes, alpha, gamma, smoothing_factor);\n    });\n  }\n\n  AT_CUDA_CHECK(hipGetLastError());\n  return {loss, partial_grad};\n}\n\nat::Tensor focal_loss_backward_cuda(const at::Tensor& grad_output, const at::Tensor& partial_grad,\n                                    const at::Tensor& num_positives_sum) {\n  // Each thread process ILP elements\n  const int ILP = sizeof(vector_t) / partial_grad.element_size();\n  dim3 block(512);\n  dim3 grid((partial_grad.numel() + block.x * ILP - 1) / (block.x * ILP));\n\n  hipStream_t stream = at::cuda::getCurrentCUDAStream();\n  AT_DISPATCH_FLOATING_TYPES_AND_HALF(partial_grad.scalar_type(), \"focal_loss_bprop\", [&] {\n    using accscalar_t = at::acc_type<scalar_t, true>;\n    using outscalar_t = float;\n    const int ILP = sizeof(vector_t) / sizeof(scalar_t);\n    focal_loss_backward_cuda_kernel<ILP, scalar_t, accscalar_t, outscalar_t>\n        <<<grid, block, 0, stream>>>(partial_grad.data_ptr<scalar_t>(), grad_output.data_ptr<outscalar_t>(),\n                                     num_positives_sum.data_ptr<float>(), partial_grad.numel());\n  });\n\n  AT_CUDA_CHECK(hipGetLastError());\n  return partial_grad;\n}\n", "source": "/kaggle/working/apex/apex/contrib/csrc/focal_loss/focal_loss_cuda_kernel.cu"}
{"input": "#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <stdio.h>\nnamespace py = pybind11;\n\n#define gpuErrchk(ans)                    \\\n  {                                       \\\n    gpuAssert((ans), __FILE__, __LINE__); \\\n  }\ninline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) {\n  if (code != cudaSuccess) {\n    fprintf(stderr, \"GPUassert %d: %s %s %d\\n\", (int)code, cudaGetErrorString(code), file, line);\n    if (abort) exit(code);\n  }\n}\n\n// find the magnitude after enforcing the 2:4 sparsity constraint on a group of 4 values\n__device__ float group_2_to_4(float4 vals) {\n  vals.x = fabs(vals.x);\n  vals.y = fabs(vals.y);\n  vals.z = fabs(vals.z);\n  vals.w = fabs(vals.w);\n\n  float sum0 = vals.x + vals.y;\n  float sum1 = vals.x + vals.z;\n  float sum2 = vals.x + vals.w;\n  float sum3 = vals.y + vals.z;\n  float sum4 = vals.y + vals.w;\n  float sum5 = vals.z + vals.w;\n\n  float best_sum0 = fmax(sum0, sum1);\n  float best_sum1 = fmax(sum2, sum3);\n  float best_sum2 = fmax(sum4, sum5);\n  float best_sum = fmax(fmax(best_sum0, best_sum1), best_sum2);\n\n  return best_sum;\n}\n\ninline float* float_ptr_from_numpy(py::array_t<float>& py_float) { return (float*)py_float.data(); }\n\ninline unsigned int* uint_ptr_from_numpy(py::array_t<unsigned int>& py_uint) { return (unsigned int*)py_uint.data(); }\n\n/**********************************************************\n *  Check for the best permutation for an entire matrix\n **********************************************************/\n__global__ void permute_and_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols, unsigned int* stripes,\n                                             unsigned int total_stripes, unsigned int* permutations, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each thread in a block takes some number of rows\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n  size_t num_stripes = total_stripes;  // total_stripes / gridDim.x;\n  size_t stripe_offset = 0;            // num_stripes * blockIdx.x;\n  unsigned int localStart = stripe_offset;\n  unsigned int localEnd = localStart + num_stripes;\n\n  // each block takes care of one permutation\n  unsigned int p = blockIdx.x;\n  unsigned int* permutation = &permutations[p * total_stripes * 4];\n\n  float sum = 0.0f;\n  extern __shared__ float s[32][32];\n  float4* local_stripes = (float4*)&s[threadIdx.x];\n  float* local_columns = (float*)&s[threadIdx.x];\n  float4* permuted_local_stripes = (float4*)&local_stripes[num_stripes];\n  float* permuted_local_columns = (float*)&local_columns[num_stripes * 4];\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n\n    // load into smem\n    for (unsigned int s = localStart; s < localEnd; ++s) {\n      unsigned int stripe = stripes[s];\n      local_stripes[s] = mat4[r * cols + stripe];\n    }\n\n// now permute\n#pragma unroll 4\n    for (unsigned int c = 0; c < num_stripes * 4; ++c) {\n      permuted_local_columns[c] = local_columns[permutation[c]];\n    }\n\n    // now sum 2:4\n    for (unsigned int s = 0; s < num_stripes; ++s) {\n      sum += group_2_to_4(permuted_local_stripes[s]);\n    }\n  }\n\n  atomicAdd(&output[p], sum);\n}\n\nvoid free_permutation_memory(float** dmatrix, unsigned int** dstripe_groups, unsigned int** dpermutations,\n                             float** dresults, float** hresults) {\n  cudaFree(*dmatrix);\n  cudaFree(*dresults);\n  cudaFree(*dpermutations);\n  cudaFree(*dstripe_groups);\n  free(*hresults);\n}\n\nint set_up_check_permutation_memory(float** dmatrix, unsigned int rows, unsigned int cols,\n                                    unsigned int** dstripe_groups, unsigned int group_width, unsigned int num_groups,\n                                    unsigned int** dpermutations, unsigned int num_permutations, float** dresults,\n                                    float** hresults) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static unsigned int setupGroupWidth = 0;\n  static unsigned int setupNumGroups = 0;\n  static unsigned int setupNumPermutations = 0;\n  static bool allocated = false;\n  int fresh_alloc = 0;\n  if (!allocated || setupRows != rows || setupCols != cols || setupGroupWidth != group_width ||\n      setupNumGroups != num_groups || setupNumPermutations != num_permutations) {\n    if (allocated) {\n      free_permutation_memory(dmatrix, dstripe_groups, dpermutations, dresults, hresults);\n    }\n\n    gpuErrchk(cudaMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    gpuErrchk(cudaMalloc((void**)dstripe_groups, group_width * num_groups * sizeof(unsigned int)));\n    gpuErrchk(cudaMalloc((void**)dpermutations, num_permutations * group_width * 4 * sizeof(unsigned int)));\n    gpuErrchk(cudaMalloc((void**)dresults, num_permutations * sizeof(float)));\n    *hresults = (float*)malloc(num_permutations * sizeof(float));\n\n    allocated = true;\n    setupRows = rows;\n    setupCols = cols;\n    setupGroupWidth = group_width;\n    setupNumGroups = num_groups;\n    setupNumPermutations = num_permutations;\n    fresh_alloc = 1;\n  }\n\n  return fresh_alloc;\n}\n\nint run_check_permutations(\n    py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n    py::array_t<unsigned int>&\n        py_stripe_groups,  // groups of stripes, group_width = stripes per group, num_groups = groups in the array\n    unsigned int group_width, unsigned int num_groups,\n    py::array_t<unsigned int>& py_permutations,  // array of permutations to try, group_width*4 values per each of\n                                                 // num_permutations permutations\n    unsigned int num_permutations,\n    py::array_t<float>& py_improvement,        // improvment offered by the best permutation\n    py::array_t<unsigned int>& py_permutation  // the best permutation\n) {\n  const unsigned int threads = 32;\n  static float* d_matrix;\n  static unsigned int* d_permutations;\n  static unsigned int* d_stripes;\n  static float* d_results;\n  static float* results;\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  unsigned int* stripe_groups = uint_ptr_from_numpy(py_stripe_groups);\n  unsigned int* permutations = uint_ptr_from_numpy(py_permutations);\n  float* improvement = float_ptr_from_numpy(py_improvement);\n  unsigned int* permutation = uint_ptr_from_numpy(py_permutation);\n\n  int fresh_alloc = set_up_check_permutation_memory(&d_matrix, rows, cols, &d_stripes, group_width, num_groups,\n                                                    &d_permutations, num_permutations, &d_results, &results);\n  if (fresh_alloc == 1) {\n    gpuErrchk(cudaMemcpy(d_permutations, permutations, num_permutations * group_width * 4 * sizeof(unsigned int),\n                         cudaMemcpyHostToDevice));\n    gpuErrchk(\n        cudaMemcpy(d_stripes, stripe_groups, group_width * num_groups * sizeof(unsigned int), cudaMemcpyHostToDevice));\n  }\n\n  // initialize results, new matrix\n  gpuErrchk(cudaMemset(d_results, 0, num_permutations * sizeof(float)));\n  gpuErrchk(cudaMemcpy(d_matrix, matrix, rows * cols * sizeof(float), cudaMemcpyHostToDevice));\n\n  // get results for all permutations\n  permute_and_sum_after_2_to_4<<<num_permutations, threads, threads * group_width * 4 * 2 * sizeof(float)>>>(\n      d_matrix, rows, cols, d_stripes, group_width, d_permutations, d_results);\n  gpuErrchk(cudaDeviceSynchronize());\n\n  gpuErrchk(cudaMemcpy(results, d_results, num_permutations * sizeof(float), cudaMemcpyDeviceToHost));\n\n  // find the best permutation - could reduce on GPU\n  unsigned int best_permutation = 0;\n  float best_improvement = 0.0f;\n  for (unsigned int p = 1; p < num_permutations; ++p) {\n    float cur_improvement = results[p] - results[0];\n    if (best_improvement < cur_improvement) {\n      best_permutation = p;\n      best_improvement = cur_improvement;\n    }\n  }\n\n  *improvement = best_improvement;\n  *permutation = best_permutation;\n\n  return 0;\n}\n\n///////////////////////////////////////////////////////////\n\n/**********************************************************\n * Get the magnitude of a matrix after applying 2:4\n **********************************************************/\n// find the magnitude after enforcing the 2:4 sparsity constraint on a subset of the columns of an input matrix\n__global__ void subset_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols, unsigned int start_col,\n                                        unsigned int end_col, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n  start_col /= 4;\n  end_col /= 4;\n\n  // each thread in a block takes some number of rows\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n  // each block takes some number of columns\n  size_t num_cols = (end_col - start_col) / gridDim.x;\n  size_t col_offset = num_cols * blockIdx.x;\n  start_col += col_offset;\n  end_col = start_col + num_cols;\n\n  float sum = 0.0f;\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r < rows) {\n      for (unsigned int c = start_col; c < end_col; c++) {\n        sum += group_2_to_4(mat4[r * cols + c]);\n      }\n    }\n  }\n\n  atomicAdd(output, sum);\n}\n\n// build the entire permute map at once\n// each block handles one group of stripes\n// each threads in the block handle all handle the same permutation at the same time on different rows before moving to\n// the next permutation\n__global__ void build_permute_map(float* matrix, unsigned int rows, unsigned int cols, unsigned int* stripes,\n                                  unsigned int group_width, unsigned int* permutations, unsigned int num_permutations,\n                                  unsigned int perm_length, float* output, unsigned int* best_indices) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each block handles a group of stripes\n  unsigned int* stripe_group = (unsigned int*)&stripes[blockIdx.x * group_width];\n\n  // shared memory: 32 threads each need 16*2\n  extern __shared__ float pm_shared[32][32];\n  float4* local_stripes = (float4*)&pm_shared[threadIdx.x];\n  float* local_columns = (float*)&pm_shared[threadIdx.x];\n  float4* permuted_stripes = (float4*)&local_stripes[4];\n  float* permuted_columns = (float*)&local_columns[16];\n\n  // each thread handles all permutations in the row before moving on to the next row\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n\n    // load a row into smem\n    for (unsigned int s = 0; s < group_width; ++s) {\n      unsigned int const stripe = stripe_group[s];\n      local_stripes[s] = mat4[r * cols + stripe];\n    }\n\n    for (unsigned int p = 0; p < num_permutations; ++p) {\n      unsigned int* permutation = &permutations[p * perm_length];\n      float sum = 0.0f;\n\n// permute\n#pragma unroll 4\n      for (unsigned int c = 0; c < group_width * 4; ++c) {\n        permuted_columns[c] = local_columns[permutation[c]];\n      }\n\n      // sum 2:4\n      for (unsigned int s = 0; s < group_width; ++s) {\n        sum += group_2_to_4(permuted_stripes[s]);\n      }\n\n      // update the running sum for this stripe group's permutation\n      atomicAdd(&output[blockIdx.x * num_permutations + p], sum);\n    }\n  }\n\n  // at this point, each permutation's sum in this stripe group has been calculated\n  // now, find the best option\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    unsigned int best_permutation = 0;\n    float best_magnitude = output[blockIdx.x * num_permutations];\n    float base_magnitude = best_magnitude;\n\n    // #pragma unroll 32\n    for (unsigned int p = 1; p < num_permutations; ++p) {\n      float magnitude = output[blockIdx.x * num_permutations + p];\n      if (magnitude > best_magnitude) {\n        best_permutation = p;\n        best_magnitude = magnitude;\n      }\n    }\n\n    output[blockIdx.x * num_permutations] = best_magnitude - base_magnitude;\n    best_indices[blockIdx.x] = best_permutation;\n  }\n}\n\nvoid free_sum_after_2_to_4_memory(float** dmatrix, float** dresult) {\n  cudaFree(*dmatrix);\n  cudaFree(*dresult);\n}\n\nint set_up_sum_after_2_to_4_memory(float** dmatrix, unsigned int rows, unsigned int cols, float** dresult) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static bool allocated = false;\n\n  int fresh_allocation = 0;\n  if (!allocated || setupRows != rows || setupCols != cols) {\n    if (allocated) free_sum_after_2_to_4_memory(dmatrix, dresult);\n\n    gpuErrchk(cudaMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    gpuErrchk(cudaMalloc((void**)dresult, sizeof(float)));\n\n    setupRows = rows;\n    setupCols = cols;\n\n    fresh_allocation = 1;\n  }\n\n  allocated = true;\n\n  return fresh_allocation;\n}\n\nint run_subset_sum_after_2_to_4(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                                unsigned int start_col, unsigned int end_col, unsigned int blocks, unsigned int threads,\n                                py::array_t<float>& py_output) {\n  static float* d_matrix;\n  static float* d_result;\n\n  int fresh_allocation = set_up_sum_after_2_to_4_memory(&d_matrix, rows, cols, &d_result);\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  float* output = float_ptr_from_numpy(py_output);\n\n  gpuErrchk(cudaMemcpy(d_matrix, matrix, rows * cols * sizeof(float), cudaMemcpyHostToDevice));\n  gpuErrchk(cudaMemset(d_result, 0, sizeof(float)));\n\n  subset_sum_after_2_to_4<<<blocks, threads>>>(d_matrix, rows, cols, start_col, end_col, d_result);\n  gpuErrchk(cudaDeviceSynchronize());\n\n  gpuErrchk(cudaMemcpy(output, d_result, sizeof(float), cudaMemcpyDeviceToHost));\n\n  return 0;\n}\n\nvoid set_up_permute_map_memory(float** dmatrix, unsigned int rows, unsigned int cols, unsigned int** dstripes,\n                               unsigned int num_groups, unsigned int group_width, unsigned int** dpermutations,\n                               unsigned int num_permutations, unsigned int perm_length, float** doutput,\n                               unsigned int** dindices, float** hresult, unsigned int** hindices) {\n  static unsigned int setUpRows = 0;\n  static unsigned int setUpCols = 0;\n  static unsigned int setUpGroupWidth = 0;\n  static unsigned int setUpNumGroups = 0;\n  static unsigned int setUpNumPerms = 0;\n  static unsigned int setUpPermLength = 0;\n\n  if (setUpRows != rows || setUpCols != cols) {\n    if (*dmatrix != NULL) {\n      gpuErrchk(cudaFree(*dmatrix));\n      *dmatrix = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n  }\n\n  if (setUpGroupWidth < group_width || setUpNumGroups < num_groups) {\n    if (*dstripes != NULL) {\n      gpuErrchk(cudaFree(*dstripes));\n      *dstripes = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)dstripes, num_groups * group_width * sizeof(unsigned int)));\n\n    if (setUpNumGroups < num_groups) {\n      if (*dindices != NULL) {\n        gpuErrchk(cudaFree(*dindices));\n        *dindices = NULL;\n      }\n      gpuErrchk(cudaMalloc((void**)dindices, num_groups * sizeof(unsigned int)));\n      if (*hindices != NULL) {\n        free(*hindices);\n        *hindices = NULL;\n      }\n      *hindices = (unsigned int*)malloc(num_groups * sizeof(unsigned int));\n    }\n  }\n\n  if (setUpNumPerms < num_permutations || setUpPermLength < perm_length) {\n    if (*dpermutations != NULL) {\n      gpuErrchk(cudaFree(*dpermutations));\n      *dpermutations = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)dpermutations, perm_length * num_permutations * sizeof(unsigned int)));\n  }\n\n  if (setUpNumPerms < num_permutations || setUpNumGroups < num_groups) {\n    if (*doutput != NULL) {\n      gpuErrchk(cudaFree(*doutput));\n      *doutput = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)doutput, num_permutations * num_groups * sizeof(float)));\n    if (*hresult != NULL) {\n      free(*hresult);\n      *hresult = NULL;\n    }\n    *hresult = (float*)malloc(num_permutations * num_groups * sizeof(float));\n  }\n\n  setUpRows = rows;\n  setUpCols = cols;\n  setUpGroupWidth = group_width;\n  setUpNumGroups = num_groups;\n  setUpNumPerms = num_permutations;\n  setUpPermLength = perm_length;\n}\n\nint run_build_permute_map(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                          py::array_t<unsigned int>& py_stripes, unsigned int num_groups, unsigned int group_width,\n                          py::array_t<unsigned int>& py_permutations, unsigned int perm_length,\n                          py::array_t<float>& py_improvements, py::array_t<unsigned int>& py_best_indices) {\n  static float* d_matrix = NULL;\n  static unsigned int* d_stripes = NULL;\n  static unsigned int* d_permutations = NULL;\n  static float* d_output = NULL;\n  static unsigned int* d_indices = NULL;\n  static float* hresult = NULL;\n  static unsigned int* hindices = NULL;\n\n  const unsigned int num_permutations = py_permutations.size() / perm_length;\n\n  const unsigned int MAX_GROUPS_PER_LAUNCH = num_permutations <= 5775 ? 1820 : 40;\n  const unsigned int full_launches = num_groups / MAX_GROUPS_PER_LAUNCH;\n  const unsigned int final_launch = num_groups % MAX_GROUPS_PER_LAUNCH;\n  const unsigned int launches = full_launches + (final_launch != 0 ? 1 : 0);\n\n  set_up_permute_map_memory(&d_matrix, rows, cols, &d_stripes, min(num_groups, MAX_GROUPS_PER_LAUNCH), group_width,\n                            &d_permutations, num_permutations, perm_length, &d_output, &d_indices, &hresult, &hindices);\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  unsigned int* stripes = uint_ptr_from_numpy(py_stripes);\n  unsigned int* permutations = uint_ptr_from_numpy(py_permutations);\n  float* improvements = float_ptr_from_numpy(py_improvements);\n  unsigned int* best_indices = uint_ptr_from_numpy(py_best_indices);\n\n  gpuErrchk(cudaMemcpy(d_matrix, matrix, rows * cols * sizeof(float), cudaMemcpyHostToDevice));\n  gpuErrchk(cudaMemcpy(d_permutations, permutations, num_permutations * perm_length * sizeof(unsigned int),\n                       cudaMemcpyHostToDevice));\n\n  unsigned int group_offset = 0;\n  for (unsigned int l = 0; l < launches; ++l) {\n    unsigned int groups_this_launch = (l < full_launches) ? MAX_GROUPS_PER_LAUNCH : final_launch;\n\n    gpuErrchk(cudaMemcpy(d_stripes, &stripes[group_offset * group_width],\n                         groups_this_launch * group_width * sizeof(unsigned int), cudaMemcpyHostToDevice));\n    gpuErrchk(cudaMemset(d_output, 0, groups_this_launch * num_permutations * sizeof(float)));\n    gpuErrchk(cudaMemset(d_indices, 0, groups_this_launch * sizeof(unsigned int)));\n\n    unsigned int shmem = 32 * (32) * sizeof(float);\n    build_permute_map<<<groups_this_launch, 32, shmem>>>(d_matrix, rows, cols, d_stripes, group_width, d_permutations,\n                                                         num_permutations, perm_length, d_output, d_indices);\n    gpuErrchk(cudaDeviceSynchronize());\n\n    gpuErrchk(\n        cudaMemcpy(hresult, d_output, num_permutations * groups_this_launch * sizeof(float), cudaMemcpyDeviceToHost));\n    gpuErrchk(cudaMemcpy(hindices, d_indices, groups_this_launch * sizeof(unsigned int), cudaMemcpyDeviceToHost));\n\n    // thread0 stuck the minimum in the first slot of each group\n    for (unsigned int g = 0; g < groups_this_launch; ++g) {\n      improvements[group_offset + g] = hresult[g * num_permutations];\n      best_indices[group_offset + g] = hindices[g];\n    }\n\n    group_offset += groups_this_launch;\n  }\n\n  return 0;\n}\n\n/**********************************************************\n * Build the swap map for channel_swaps\n **********************************************************/\n// find the magnitude improvement if some columns were swapped (check all pairs of columns in all the stripe_pairs)\n__global__ void swap_columns_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols,\n                                              unsigned int* stripe_pairs, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each thread takes some number of rows\n  size_t const num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t const row_offset = num_rows * threadIdx.x;\n\n  // each block is repsonsible for a pair of stripes\n  unsigned int const stripe0 = stripe_pairs[2 * blockIdx.x];\n  unsigned int const stripe1 = stripe_pairs[2 * blockIdx.x + 1];\n  // space for 32 threads, 8 values (2 stripes) in use at a time, plus 16 partial sums and one base sum\n  extern __shared__ float cs[32][32];\n  float4* local_stripe0 = (float4*)&cs[threadIdx.x][0];\n  float* local_cols0 = (float*)&cs[threadIdx.x][0];\n  float4* local_stripe1 = (float4*)&cs[threadIdx.x][4];\n  float* local_cols1 = (float*)&cs[threadIdx.x][4];\n  float* local_psum = (float*)&cs[threadIdx.x][8];\n  float* base_psum = (float*)&cs[threadIdx.x][24];\n\n  *base_psum = 0.0f;\n  for (unsigned int s = 0; s < 16; ++s) {\n    local_psum[s] = 0.0f;\n  }\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n    *local_stripe0 = mat4[r * cols + stripe0];\n    *local_stripe1 = mat4[r * cols + stripe1];\n    *base_psum += group_2_to_4(*local_stripe0) + group_2_to_4(*local_stripe1);\n    unsigned int swap_idx = 0;\n    for (unsigned int c0 = 0; c0 < 4; ++c0) {\n      for (unsigned int c1 = 0; c1 < 4; ++c1) {\n        // swap c0 and c1\n        float tmp = local_cols0[c0];\n        local_cols0[c0] = local_cols1[c1];\n        local_cols1[c1] = tmp;\n\n        // grab the sum\n        local_psum[swap_idx] += group_2_to_4(*local_stripe0) + group_2_to_4(*local_stripe1);\n\n        // swap back\n        local_cols1[c1] = local_cols0[c0];\n        local_cols0[c0] = tmp;\n\n        swap_idx++;\n      }\n    }\n  }\n\n  // reduce partial sums, store local diffs in the output\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    for (unsigned int t = 1; t < blockDim.x; ++t) {\n      for (unsigned int swap = 0; swap < 16; ++swap) {\n        local_psum[swap] += cs[t][8 + swap];\n      }\n      *base_psum += cs[t][24];\n    }\n\n    for (unsigned int swap = 0; swap < 16; ++swap) {\n      atomicAdd(&output[blockIdx.x * 16 + swap], local_psum[swap] - (*base_psum));\n    }\n  }\n}\n\nvoid set_up_swap_map_memory(float** dmatrix, unsigned int rows, unsigned int cols, unsigned int** dstripe_pairs,\n                            unsigned int num_pairs, float** dresult) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static unsigned int setupPairs = 0;\n\n  if (*dmatrix == NULL || setupRows != rows || setupCols != cols) {\n    if (*dmatrix != NULL) {\n      gpuErrchk(cudaFree(*dmatrix));\n      *dmatrix = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    setupRows = rows;\n    setupCols = cols;\n  }\n\n  if (*dstripe_pairs == NULL || *dresult == NULL || setupPairs < num_pairs) {\n    if (*dstripe_pairs != NULL) {\n      gpuErrchk(cudaFree(*dstripe_pairs));\n      *dstripe_pairs = NULL;\n    }\n    if (*dresult != NULL) {\n      gpuErrchk(cudaFree(*dresult));\n      *dresult = NULL;\n    }\n    gpuErrchk(cudaMalloc((void**)dstripe_pairs, num_pairs * 2 * sizeof(unsigned int)));\n    gpuErrchk(cudaMalloc((void**)dresult, num_pairs * 16 * sizeof(float)));\n\n    setupPairs = num_pairs;\n  }\n}\n\nint run_build_swap_map(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                       py::array_t<uint32_t>& py_stripe_pairs, py::array_t<float>& py_output) {\n  static float* d_matrix = NULL;\n  static float* d_result = NULL;\n  static unsigned int* d_stripe_pairs = NULL;\n\n  float* matrix = float_ptr_from_numpy(py_matrix);                    //(float*)py_matrix.data();\n  unsigned int* stripe_pairs = uint_ptr_from_numpy(py_stripe_pairs);  //(unsigned int*)py_stripe_pairs.data();\n  float* output = float_ptr_from_numpy(py_output);                    //(float*)py_output.data();\n\n  unsigned int num_pairs = py_stripe_pairs.size() / 2;\n\n  set_up_swap_map_memory(&d_matrix, rows, cols, &d_stripe_pairs, num_pairs, &d_result);\n  gpuErrchk(cudaMemcpy(d_matrix, matrix, rows * cols * sizeof(float), cudaMemcpyHostToDevice));\n  gpuErrchk(cudaMemcpy(d_stripe_pairs, stripe_pairs, 2 * num_pairs * sizeof(unsigned int), cudaMemcpyHostToDevice));\n  gpuErrchk(cudaMemset(d_result, 0, num_pairs * 16 * sizeof(float)));\n\n  unsigned int shmem = 32 * (32) * sizeof(float);\n  swap_columns_sum_after_2_to_4<<<num_pairs, 32, shmem>>>(d_matrix, rows, cols, d_stripe_pairs, d_result);\n  gpuErrchk(cudaDeviceSynchronize());\n\n  gpuErrchk(cudaMemcpy(output, d_result, num_pairs * 16 * sizeof(float), cudaMemcpyDeviceToHost));\n\n  return 0;\n}\n///////////////////////////////////////////////////////////\n\nPYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {\n  m.def(\"sum_after_2_to_4\", &run_subset_sum_after_2_to_4, \"matrix sum after applying 2:4 (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"build_permute_map\", &run_build_permute_map, \"optimize stripe groups (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"check_permutations\", &run_check_permutations, \"exhaustively check all permutations (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"build_swap_map\", &run_build_swap_map, \"channel swaps (CUDA)\", py::call_guard<py::gil_scoped_release>());\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <stdio.h>\nnamespace py = pybind11;\n\n#define gpuErrchk(ans)                    \\\n  {                                       \\\n    gpuAssert((ans), __FILE__, __LINE__); \\\n  }\ninline void gpuAssert(hipError_t code, const char* file, int line, bool abort = true) {\n  if (code != hipSuccess) {\n    fprintf(stderr, \"GPUassert %d: %s %s %d\\n\", (int)code, hipGetErrorString(code), file, line);\n    if (abort) exit(code);\n  }\n}\n\n// find the magnitude after enforcing the 2:4 sparsity constraint on a group of 4 values\n__device__ float group_2_to_4(float4 vals) {\n  vals.x = fabs(vals.x);\n  vals.y = fabs(vals.y);\n  vals.z = fabs(vals.z);\n  vals.w = fabs(vals.w);\n\n  float sum0 = vals.x + vals.y;\n  float sum1 = vals.x + vals.z;\n  float sum2 = vals.x + vals.w;\n  float sum3 = vals.y + vals.z;\n  float sum4 = vals.y + vals.w;\n  float sum5 = vals.z + vals.w;\n\n  float best_sum0 = fmax(sum0, sum1);\n  float best_sum1 = fmax(sum2, sum3);\n  float best_sum2 = fmax(sum4, sum5);\n  float best_sum = fmax(fmax(best_sum0, best_sum1), best_sum2);\n\n  return best_sum;\n}\n\ninline float* float_ptr_from_numpy(py::array_t<float>& py_float) { return (float*)py_float.data(); }\n\ninline unsigned int* uint_ptr_from_numpy(py::array_t<unsigned int>& py_uint) { return (unsigned int*)py_uint.data(); }\n\n/**********************************************************\n *  Check for the best permutation for an entire matrix\n **********************************************************/\n__global__ void permute_and_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols, unsigned int* stripes,\n                                             unsigned int total_stripes, unsigned int* permutations, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each thread in a block takes some number of rows\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n  size_t num_stripes = total_stripes;  // total_stripes / gridDim.x;\n  size_t stripe_offset = 0;            // num_stripes * blockIdx.x;\n  unsigned int localStart = stripe_offset;\n  unsigned int localEnd = localStart + num_stripes;\n\n  // each block takes care of one permutation\n  unsigned int p = blockIdx.x;\n  unsigned int* permutation = &permutations[p * total_stripes * 4];\n\n  float sum = 0.0f;\n  extern __shared__ float s[32][32];\n  float4* local_stripes = (float4*)&s[threadIdx.x];\n  float* local_columns = (float*)&s[threadIdx.x];\n  float4* permuted_local_stripes = (float4*)&local_stripes[num_stripes];\n  float* permuted_local_columns = (float*)&local_columns[num_stripes * 4];\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n\n    // load into smem\n    for (unsigned int s = localStart; s < localEnd; ++s) {\n      unsigned int stripe = stripes[s];\n      local_stripes[s] = mat4[r * cols + stripe];\n    }\n\n// now permute\n#pragma unroll 4\n    for (unsigned int c = 0; c < num_stripes * 4; ++c) {\n      permuted_local_columns[c] = local_columns[permutation[c]];\n    }\n\n    // now sum 2:4\n    for (unsigned int s = 0; s < num_stripes; ++s) {\n      sum += group_2_to_4(permuted_local_stripes[s]);\n    }\n  }\n\n  atomicAdd(&output[p], sum);\n}\n\nvoid free_permutation_memory(float** dmatrix, unsigned int** dstripe_groups, unsigned int** dpermutations,\n                             float** dresults, float** hresults) {\n  hipFree(*dmatrix);\n  hipFree(*dresults);\n  hipFree(*dpermutations);\n  hipFree(*dstripe_groups);\n  free(*hresults);\n}\n\nint set_up_check_permutation_memory(float** dmatrix, unsigned int rows, unsigned int cols,\n                                    unsigned int** dstripe_groups, unsigned int group_width, unsigned int num_groups,\n                                    unsigned int** dpermutations, unsigned int num_permutations, float** dresults,\n                                    float** hresults) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static unsigned int setupGroupWidth = 0;\n  static unsigned int setupNumGroups = 0;\n  static unsigned int setupNumPermutations = 0;\n  static bool allocated = false;\n  int fresh_alloc = 0;\n  if (!allocated || setupRows != rows || setupCols != cols || setupGroupWidth != group_width ||\n      setupNumGroups != num_groups || setupNumPermutations != num_permutations) {\n    if (allocated) {\n      free_permutation_memory(dmatrix, dstripe_groups, dpermutations, dresults, hresults);\n    }\n\n    gpuErrchk(hipMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    gpuErrchk(hipMalloc((void**)dstripe_groups, group_width * num_groups * sizeof(unsigned int)));\n    gpuErrchk(hipMalloc((void**)dpermutations, num_permutations * group_width * 4 * sizeof(unsigned int)));\n    gpuErrchk(hipMalloc((void**)dresults, num_permutations * sizeof(float)));\n    *hresults = (float*)malloc(num_permutations * sizeof(float));\n\n    allocated = true;\n    setupRows = rows;\n    setupCols = cols;\n    setupGroupWidth = group_width;\n    setupNumGroups = num_groups;\n    setupNumPermutations = num_permutations;\n    fresh_alloc = 1;\n  }\n\n  return fresh_alloc;\n}\n\nint run_check_permutations(\n    py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n    py::array_t<unsigned int>&\n        py_stripe_groups,  // groups of stripes, group_width = stripes per group, num_groups = groups in the array\n    unsigned int group_width, unsigned int num_groups,\n    py::array_t<unsigned int>& py_permutations,  // array of permutations to try, group_width*4 values per each of\n                                                 // num_permutations permutations\n    unsigned int num_permutations,\n    py::array_t<float>& py_improvement,        // improvment offered by the best permutation\n    py::array_t<unsigned int>& py_permutation  // the best permutation\n) {\n  const unsigned int threads = 32;\n  static float* d_matrix;\n  static unsigned int* d_permutations;\n  static unsigned int* d_stripes;\n  static float* d_results;\n  static float* results;\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  unsigned int* stripe_groups = uint_ptr_from_numpy(py_stripe_groups);\n  unsigned int* permutations = uint_ptr_from_numpy(py_permutations);\n  float* improvement = float_ptr_from_numpy(py_improvement);\n  unsigned int* permutation = uint_ptr_from_numpy(py_permutation);\n\n  int fresh_alloc = set_up_check_permutation_memory(&d_matrix, rows, cols, &d_stripes, group_width, num_groups,\n                                                    &d_permutations, num_permutations, &d_results, &results);\n  if (fresh_alloc == 1) {\n    gpuErrchk(hipMemcpy(d_permutations, permutations, num_permutations * group_width * 4 * sizeof(unsigned int),\n                         hipMemcpyHostToDevice));\n    gpuErrchk(\n        hipMemcpy(d_stripes, stripe_groups, group_width * num_groups * sizeof(unsigned int), hipMemcpyHostToDevice));\n  }\n\n  // initialize results, new matrix\n  gpuErrchk(hipMemset(d_results, 0, num_permutations * sizeof(float)));\n  gpuErrchk(hipMemcpy(d_matrix, matrix, rows * cols * sizeof(float), hipMemcpyHostToDevice));\n\n  // get results for all permutations\n  permute_and_sum_after_2_to_4<<<num_permutations, threads, threads * group_width * 4 * 2 * sizeof(float)>>>(\n      d_matrix, rows, cols, d_stripes, group_width, d_permutations, d_results);\n  gpuErrchk(hipDeviceSynchronize());\n\n  gpuErrchk(hipMemcpy(results, d_results, num_permutations * sizeof(float), hipMemcpyDeviceToHost));\n\n  // find the best permutation - could reduce on GPU\n  unsigned int best_permutation = 0;\n  float best_improvement = 0.0f;\n  for (unsigned int p = 1; p < num_permutations; ++p) {\n    float cur_improvement = results[p] - results[0];\n    if (best_improvement < cur_improvement) {\n      best_permutation = p;\n      best_improvement = cur_improvement;\n    }\n  }\n\n  *improvement = best_improvement;\n  *permutation = best_permutation;\n\n  return 0;\n}\n\n///////////////////////////////////////////////////////////\n\n/**********************************************************\n * Get the magnitude of a matrix after applying 2:4\n **********************************************************/\n// find the magnitude after enforcing the 2:4 sparsity constraint on a subset of the columns of an input matrix\n__global__ void subset_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols, unsigned int start_col,\n                                        unsigned int end_col, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n  start_col /= 4;\n  end_col /= 4;\n\n  // each thread in a block takes some number of rows\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n  // each block takes some number of columns\n  size_t num_cols = (end_col - start_col) / gridDim.x;\n  size_t col_offset = num_cols * blockIdx.x;\n  start_col += col_offset;\n  end_col = start_col + num_cols;\n\n  float sum = 0.0f;\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r < rows) {\n      for (unsigned int c = start_col; c < end_col; c++) {\n        sum += group_2_to_4(mat4[r * cols + c]);\n      }\n    }\n  }\n\n  atomicAdd(output, sum);\n}\n\n// build the entire permute map at once\n// each block handles one group of stripes\n// each threads in the block handle all handle the same permutation at the same time on different rows before moving to\n// the next permutation\n__global__ void build_permute_map(float* matrix, unsigned int rows, unsigned int cols, unsigned int* stripes,\n                                  unsigned int group_width, unsigned int* permutations, unsigned int num_permutations,\n                                  unsigned int perm_length, float* output, unsigned int* best_indices) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each block handles a group of stripes\n  unsigned int* stripe_group = (unsigned int*)&stripes[blockIdx.x * group_width];\n\n  // shared memory: 32 threads each need 16*2\n  extern __shared__ float pm_shared[32][32];\n  float4* local_stripes = (float4*)&pm_shared[threadIdx.x];\n  float* local_columns = (float*)&pm_shared[threadIdx.x];\n  float4* permuted_stripes = (float4*)&local_stripes[4];\n  float* permuted_columns = (float*)&local_columns[16];\n\n  // each thread handles all permutations in the row before moving on to the next row\n  size_t num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t row_offset = num_rows * threadIdx.x;\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n\n    // load a row into smem\n    for (unsigned int s = 0; s < group_width; ++s) {\n      unsigned int const stripe = stripe_group[s];\n      local_stripes[s] = mat4[r * cols + stripe];\n    }\n\n    for (unsigned int p = 0; p < num_permutations; ++p) {\n      unsigned int* permutation = &permutations[p * perm_length];\n      float sum = 0.0f;\n\n// permute\n#pragma unroll 4\n      for (unsigned int c = 0; c < group_width * 4; ++c) {\n        permuted_columns[c] = local_columns[permutation[c]];\n      }\n\n      // sum 2:4\n      for (unsigned int s = 0; s < group_width; ++s) {\n        sum += group_2_to_4(permuted_stripes[s]);\n      }\n\n      // update the running sum for this stripe group's permutation\n      atomicAdd(&output[blockIdx.x * num_permutations + p], sum);\n    }\n  }\n\n  // at this point, each permutation's sum in this stripe group has been calculated\n  // now, find the best option\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    unsigned int best_permutation = 0;\n    float best_magnitude = output[blockIdx.x * num_permutations];\n    float base_magnitude = best_magnitude;\n\n    // #pragma unroll 32\n    for (unsigned int p = 1; p < num_permutations; ++p) {\n      float magnitude = output[blockIdx.x * num_permutations + p];\n      if (magnitude > best_magnitude) {\n        best_permutation = p;\n        best_magnitude = magnitude;\n      }\n    }\n\n    output[blockIdx.x * num_permutations] = best_magnitude - base_magnitude;\n    best_indices[blockIdx.x] = best_permutation;\n  }\n}\n\nvoid free_sum_after_2_to_4_memory(float** dmatrix, float** dresult) {\n  hipFree(*dmatrix);\n  hipFree(*dresult);\n}\n\nint set_up_sum_after_2_to_4_memory(float** dmatrix, unsigned int rows, unsigned int cols, float** dresult) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static bool allocated = false;\n\n  int fresh_allocation = 0;\n  if (!allocated || setupRows != rows || setupCols != cols) {\n    if (allocated) free_sum_after_2_to_4_memory(dmatrix, dresult);\n\n    gpuErrchk(hipMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    gpuErrchk(hipMalloc((void**)dresult, sizeof(float)));\n\n    setupRows = rows;\n    setupCols = cols;\n\n    fresh_allocation = 1;\n  }\n\n  allocated = true;\n\n  return fresh_allocation;\n}\n\nint run_subset_sum_after_2_to_4(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                                unsigned int start_col, unsigned int end_col, unsigned int blocks, unsigned int threads,\n                                py::array_t<float>& py_output) {\n  static float* d_matrix;\n  static float* d_result;\n\n  int fresh_allocation = set_up_sum_after_2_to_4_memory(&d_matrix, rows, cols, &d_result);\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  float* output = float_ptr_from_numpy(py_output);\n\n  gpuErrchk(hipMemcpy(d_matrix, matrix, rows * cols * sizeof(float), hipMemcpyHostToDevice));\n  gpuErrchk(hipMemset(d_result, 0, sizeof(float)));\n\n  subset_sum_after_2_to_4<<<blocks, threads>>>(d_matrix, rows, cols, start_col, end_col, d_result);\n  gpuErrchk(hipDeviceSynchronize());\n\n  gpuErrchk(hipMemcpy(output, d_result, sizeof(float), hipMemcpyDeviceToHost));\n\n  return 0;\n}\n\nvoid set_up_permute_map_memory(float** dmatrix, unsigned int rows, unsigned int cols, unsigned int** dstripes,\n                               unsigned int num_groups, unsigned int group_width, unsigned int** dpermutations,\n                               unsigned int num_permutations, unsigned int perm_length, float** doutput,\n                               unsigned int** dindices, float** hresult, unsigned int** hindices) {\n  static unsigned int setUpRows = 0;\n  static unsigned int setUpCols = 0;\n  static unsigned int setUpGroupWidth = 0;\n  static unsigned int setUpNumGroups = 0;\n  static unsigned int setUpNumPerms = 0;\n  static unsigned int setUpPermLength = 0;\n\n  if (setUpRows != rows || setUpCols != cols) {\n    if (*dmatrix != NULL) {\n      gpuErrchk(hipFree(*dmatrix));\n      *dmatrix = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n  }\n\n  if (setUpGroupWidth < group_width || setUpNumGroups < num_groups) {\n    if (*dstripes != NULL) {\n      gpuErrchk(hipFree(*dstripes));\n      *dstripes = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)dstripes, num_groups * group_width * sizeof(unsigned int)));\n\n    if (setUpNumGroups < num_groups) {\n      if (*dindices != NULL) {\n        gpuErrchk(hipFree(*dindices));\n        *dindices = NULL;\n      }\n      gpuErrchk(hipMalloc((void**)dindices, num_groups * sizeof(unsigned int)));\n      if (*hindices != NULL) {\n        free(*hindices);\n        *hindices = NULL;\n      }\n      *hindices = (unsigned int*)malloc(num_groups * sizeof(unsigned int));\n    }\n  }\n\n  if (setUpNumPerms < num_permutations || setUpPermLength < perm_length) {\n    if (*dpermutations != NULL) {\n      gpuErrchk(hipFree(*dpermutations));\n      *dpermutations = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)dpermutations, perm_length * num_permutations * sizeof(unsigned int)));\n  }\n\n  if (setUpNumPerms < num_permutations || setUpNumGroups < num_groups) {\n    if (*doutput != NULL) {\n      gpuErrchk(hipFree(*doutput));\n      *doutput = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)doutput, num_permutations * num_groups * sizeof(float)));\n    if (*hresult != NULL) {\n      free(*hresult);\n      *hresult = NULL;\n    }\n    *hresult = (float*)malloc(num_permutations * num_groups * sizeof(float));\n  }\n\n  setUpRows = rows;\n  setUpCols = cols;\n  setUpGroupWidth = group_width;\n  setUpNumGroups = num_groups;\n  setUpNumPerms = num_permutations;\n  setUpPermLength = perm_length;\n}\n\nint run_build_permute_map(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                          py::array_t<unsigned int>& py_stripes, unsigned int num_groups, unsigned int group_width,\n                          py::array_t<unsigned int>& py_permutations, unsigned int perm_length,\n                          py::array_t<float>& py_improvements, py::array_t<unsigned int>& py_best_indices) {\n  static float* d_matrix = NULL;\n  static unsigned int* d_stripes = NULL;\n  static unsigned int* d_permutations = NULL;\n  static float* d_output = NULL;\n  static unsigned int* d_indices = NULL;\n  static float* hresult = NULL;\n  static unsigned int* hindices = NULL;\n\n  const unsigned int num_permutations = py_permutations.size() / perm_length;\n\n  const unsigned int MAX_GROUPS_PER_LAUNCH = num_permutations <= 5775 ? 1820 : 40;\n  const unsigned int full_launches = num_groups / MAX_GROUPS_PER_LAUNCH;\n  const unsigned int final_launch = num_groups % MAX_GROUPS_PER_LAUNCH;\n  const unsigned int launches = full_launches + (final_launch != 0 ? 1 : 0);\n\n  set_up_permute_map_memory(&d_matrix, rows, cols, &d_stripes, min(num_groups, MAX_GROUPS_PER_LAUNCH), group_width,\n                            &d_permutations, num_permutations, perm_length, &d_output, &d_indices, &hresult, &hindices);\n\n  float* matrix = float_ptr_from_numpy(py_matrix);\n  unsigned int* stripes = uint_ptr_from_numpy(py_stripes);\n  unsigned int* permutations = uint_ptr_from_numpy(py_permutations);\n  float* improvements = float_ptr_from_numpy(py_improvements);\n  unsigned int* best_indices = uint_ptr_from_numpy(py_best_indices);\n\n  gpuErrchk(hipMemcpy(d_matrix, matrix, rows * cols * sizeof(float), hipMemcpyHostToDevice));\n  gpuErrchk(hipMemcpy(d_permutations, permutations, num_permutations * perm_length * sizeof(unsigned int),\n                       hipMemcpyHostToDevice));\n\n  unsigned int group_offset = 0;\n  for (unsigned int l = 0; l < launches; ++l) {\n    unsigned int groups_this_launch = (l < full_launches) ? MAX_GROUPS_PER_LAUNCH : final_launch;\n\n    gpuErrchk(hipMemcpy(d_stripes, &stripes[group_offset * group_width],\n                         groups_this_launch * group_width * sizeof(unsigned int), hipMemcpyHostToDevice));\n    gpuErrchk(hipMemset(d_output, 0, groups_this_launch * num_permutations * sizeof(float)));\n    gpuErrchk(hipMemset(d_indices, 0, groups_this_launch * sizeof(unsigned int)));\n\n    unsigned int shmem = 32 * (32) * sizeof(float);\n    build_permute_map<<<groups_this_launch, 32, shmem>>>(d_matrix, rows, cols, d_stripes, group_width, d_permutations,\n                                                         num_permutations, perm_length, d_output, d_indices);\n    gpuErrchk(hipDeviceSynchronize());\n\n    gpuErrchk(\n        hipMemcpy(hresult, d_output, num_permutations * groups_this_launch * sizeof(float), hipMemcpyDeviceToHost));\n    gpuErrchk(hipMemcpy(hindices, d_indices, groups_this_launch * sizeof(unsigned int), hipMemcpyDeviceToHost));\n\n    // thread0 stuck the minimum in the first slot of each group\n    for (unsigned int g = 0; g < groups_this_launch; ++g) {\n      improvements[group_offset + g] = hresult[g * num_permutations];\n      best_indices[group_offset + g] = hindices[g];\n    }\n\n    group_offset += groups_this_launch;\n  }\n\n  return 0;\n}\n\n/**********************************************************\n * Build the swap map for channel_swaps\n **********************************************************/\n// find the magnitude improvement if some columns were swapped (check all pairs of columns in all the stripe_pairs)\n__global__ void swap_columns_sum_after_2_to_4(float* matrix, unsigned int rows, unsigned int cols,\n                                              unsigned int* stripe_pairs, float* output) {\n  // vectorize\n  float4* mat4 = (float4*)matrix;\n  cols /= 4;\n\n  // each thread takes some number of rows\n  size_t const num_rows = max((int)ceilf((float)rows / (float)blockDim.x), 1);\n  size_t const row_offset = num_rows * threadIdx.x;\n\n  // each block is repsonsible for a pair of stripes\n  unsigned int const stripe0 = stripe_pairs[2 * blockIdx.x];\n  unsigned int const stripe1 = stripe_pairs[2 * blockIdx.x + 1];\n  // space for 32 threads, 8 values (2 stripes) in use at a time, plus 16 partial sums and one base sum\n  extern __shared__ float cs[32][32];\n  float4* local_stripe0 = (float4*)&cs[threadIdx.x][0];\n  float* local_cols0 = (float*)&cs[threadIdx.x][0];\n  float4* local_stripe1 = (float4*)&cs[threadIdx.x][4];\n  float* local_cols1 = (float*)&cs[threadIdx.x][4];\n  float* local_psum = (float*)&cs[threadIdx.x][8];\n  float* base_psum = (float*)&cs[threadIdx.x][24];\n\n  *base_psum = 0.0f;\n  for (unsigned int s = 0; s < 16; ++s) {\n    local_psum[s] = 0.0f;\n  }\n\n  for (unsigned int r = row_offset; r < row_offset + num_rows; ++r) {\n    if (r >= rows) break;\n    *local_stripe0 = mat4[r * cols + stripe0];\n    *local_stripe1 = mat4[r * cols + stripe1];\n    *base_psum += group_2_to_4(*local_stripe0) + group_2_to_4(*local_stripe1);\n    unsigned int swap_idx = 0;\n    for (unsigned int c0 = 0; c0 < 4; ++c0) {\n      for (unsigned int c1 = 0; c1 < 4; ++c1) {\n        // swap c0 and c1\n        float tmp = local_cols0[c0];\n        local_cols0[c0] = local_cols1[c1];\n        local_cols1[c1] = tmp;\n\n        // grab the sum\n        local_psum[swap_idx] += group_2_to_4(*local_stripe0) + group_2_to_4(*local_stripe1);\n\n        // swap back\n        local_cols1[c1] = local_cols0[c0];\n        local_cols0[c0] = tmp;\n\n        swap_idx++;\n      }\n    }\n  }\n\n  // reduce partial sums, store local diffs in the output\n  __syncthreads();\n  if (threadIdx.x == 0) {\n    for (unsigned int t = 1; t < blockDim.x; ++t) {\n      for (unsigned int swap = 0; swap < 16; ++swap) {\n        local_psum[swap] += cs[t][8 + swap];\n      }\n      *base_psum += cs[t][24];\n    }\n\n    for (unsigned int swap = 0; swap < 16; ++swap) {\n      atomicAdd(&output[blockIdx.x * 16 + swap], local_psum[swap] - (*base_psum));\n    }\n  }\n}\n\nvoid set_up_swap_map_memory(float** dmatrix, unsigned int rows, unsigned int cols, unsigned int** dstripe_pairs,\n                            unsigned int num_pairs, float** dresult) {\n  static unsigned int setupRows = 0;\n  static unsigned int setupCols = 0;\n  static unsigned int setupPairs = 0;\n\n  if (*dmatrix == NULL || setupRows != rows || setupCols != cols) {\n    if (*dmatrix != NULL) {\n      gpuErrchk(hipFree(*dmatrix));\n      *dmatrix = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)dmatrix, rows * cols * sizeof(float)));\n    setupRows = rows;\n    setupCols = cols;\n  }\n\n  if (*dstripe_pairs == NULL || *dresult == NULL || setupPairs < num_pairs) {\n    if (*dstripe_pairs != NULL) {\n      gpuErrchk(hipFree(*dstripe_pairs));\n      *dstripe_pairs = NULL;\n    }\n    if (*dresult != NULL) {\n      gpuErrchk(hipFree(*dresult));\n      *dresult = NULL;\n    }\n    gpuErrchk(hipMalloc((void**)dstripe_pairs, num_pairs * 2 * sizeof(unsigned int)));\n    gpuErrchk(hipMalloc((void**)dresult, num_pairs * 16 * sizeof(float)));\n\n    setupPairs = num_pairs;\n  }\n}\n\nint run_build_swap_map(py::array_t<float>& py_matrix, unsigned int rows, unsigned int cols,\n                       py::array_t<uint32_t>& py_stripe_pairs, py::array_t<float>& py_output) {\n  static float* d_matrix = NULL;\n  static float* d_result = NULL;\n  static unsigned int* d_stripe_pairs = NULL;\n\n  float* matrix = float_ptr_from_numpy(py_matrix);                    //(float*)py_matrix.data();\n  unsigned int* stripe_pairs = uint_ptr_from_numpy(py_stripe_pairs);  //(unsigned int*)py_stripe_pairs.data();\n  float* output = float_ptr_from_numpy(py_output);                    //(float*)py_output.data();\n\n  unsigned int num_pairs = py_stripe_pairs.size() / 2;\n\n  set_up_swap_map_memory(&d_matrix, rows, cols, &d_stripe_pairs, num_pairs, &d_result);\n  gpuErrchk(hipMemcpy(d_matrix, matrix, rows * cols * sizeof(float), hipMemcpyHostToDevice));\n  gpuErrchk(hipMemcpy(d_stripe_pairs, stripe_pairs, 2 * num_pairs * sizeof(unsigned int), hipMemcpyHostToDevice));\n  gpuErrchk(hipMemset(d_result, 0, num_pairs * 16 * sizeof(float)));\n\n  unsigned int shmem = 32 * (32) * sizeof(float);\n  swap_columns_sum_after_2_to_4<<<num_pairs, 32, shmem>>>(d_matrix, rows, cols, d_stripe_pairs, d_result);\n  gpuErrchk(hipDeviceSynchronize());\n\n  gpuErrchk(hipMemcpy(output, d_result, num_pairs * 16 * sizeof(float), hipMemcpyDeviceToHost));\n\n  return 0;\n}\n///////////////////////////////////////////////////////////\n\nPYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {\n  m.def(\"sum_after_2_to_4\", &run_subset_sum_after_2_to_4, \"matrix sum after applying 2:4 (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"build_permute_map\", &run_build_permute_map, \"optimize stripe groups (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"check_permutations\", &run_check_permutations, \"exhaustively check all permutations (CUDA)\",\n        py::call_guard<py::gil_scoped_release>());\n  m.def(\"build_swap_map\", &run_build_swap_map, \"channel swaps (CUDA)\", py::call_guard<py::gil_scoped_release>());\n}\n", "source": "/kaggle/working/apex/apex/contrib/sparsity/permutation_search_kernels/CUDA_kernels/permutation_search_kernels.cu"}
{"input": "#include <stdio.h>\n#include \"gputimer.h\"\n\n#define NUM_THREADS 1000000\n#define ARRAY_SIZE  100\n\n#define BLOCK_WIDTH 1000\n\nvoid print_array(int *array, int size)\n{\n    printf(\"{ \");\n    for (int i = 0; i < size; i++)  { printf(\"%d \", array[i]); }\n    printf(\"}\\n\");\n}\n\n__global__ void increment_naive(int *g)\n{\n\t// which thread is this?\n\tint i = blockIdx.x * blockDim.x + threadIdx.x; \n\n\t// each thread to increment consecutive elements, wrapping at ARRAY_SIZE\n\ti = i % ARRAY_SIZE;  \n\tg[i] = g[i] + 1;\n}\n\n__global__ void increment_atomic(int *g)\n{\n\t// which thread is this?\n\tint i = blockIdx.x * blockDim.x + threadIdx.x; \n\n\t// each thread to increment consecutive elements, wrapping at ARRAY_SIZE\n\ti = i % ARRAY_SIZE;  \n\tatomicAdd(& g[i], 1);\n}\n\nint main(int argc,char **argv)\n{   \n    GpuTimer timer;\n    printf(\"%d total threads in %d blocks writing into %d array elements\\n\",\n           NUM_THREADS, NUM_THREADS / BLOCK_WIDTH, ARRAY_SIZE);\n\n    // declare and allocate host memory\n    int h_array[ARRAY_SIZE];\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);\n \n    // declare, allocate, and zero out GPU memory\n    int * d_array;\n    cudaMalloc((void **) &d_array, ARRAY_BYTES);\n    cudaMemset((void *) d_array, 0, ARRAY_BYTES); \n\n    // launch the kernel - comment out one of these\n    timer.Start();\n    // increment_naive<<<NUM_THREADS/BLOCK_WIDTH, BLOCK_WIDTH>>>(d_array);\n    increment_atomic<<<NUM_THREADS/BLOCK_WIDTH, BLOCK_WIDTH>>>(d_array);\n    timer.Stop();\n    \n    // copy back the array of sums from GPU and print\n    cudaMemcpy(h_array, d_array, ARRAY_BYTES, cudaMemcpyDeviceToHost);\n    print_array(h_array, ARRAY_SIZE);\n    printf(\"Time elapsed = %g ms\\n\", timer.Elapsed());\n \n    // free GPU memory allocation and exit\n    cudaFree(d_array);\n    return 0;\n}", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include \"gputimer.h\"\n\n#define NUM_THREADS 1000000\n#define ARRAY_SIZE  100\n\n#define BLOCK_WIDTH 1000\n\nvoid print_array(int *array, int size)\n{\n    printf(\"{ \");\n    for (int i = 0; i < size; i++)  { printf(\"%d \", array[i]); }\n    printf(\"}\\n\");\n}\n\n__global__ void increment_naive(int *g)\n{\n\t// which thread is this?\n\tint i = blockIdx.x * blockDim.x + threadIdx.x; \n\n\t// each thread to increment consecutive elements, wrapping at ARRAY_SIZE\n\ti = i % ARRAY_SIZE;  \n\tg[i] = g[i] + 1;\n}\n\n__global__ void increment_atomic(int *g)\n{\n\t// which thread is this?\n\tint i = blockIdx.x * blockDim.x + threadIdx.x; \n\n\t// each thread to increment consecutive elements, wrapping at ARRAY_SIZE\n\ti = i % ARRAY_SIZE;  \n\tatomicAdd(& g[i], 1);\n}\n\nint main(int argc,char **argv)\n{   \n    GpuTimer timer;\n    printf(\"%d total threads in %d blocks writing into %d array elements\\n\",\n           NUM_THREADS, NUM_THREADS / BLOCK_WIDTH, ARRAY_SIZE);\n\n    // declare and allocate host memory\n    int h_array[ARRAY_SIZE];\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);\n \n    // declare, allocate, and zero out GPU memory\n    int * d_array;\n    hipMalloc((void **) &d_array, ARRAY_BYTES);\n    hipMemset((void *) d_array, 0, ARRAY_BYTES); \n\n    // launch the kernel - comment out one of these\n    timer.Start();\n    // increment_naive<<<NUM_THREADS/BLOCK_WIDTH, BLOCK_WIDTH>>>(d_array);\n    increment_atomic<<<NUM_THREADS/BLOCK_WIDTH, BLOCK_WIDTH>>>(d_array);\n    timer.Stop();\n    \n    // copy back the array of sums from GPU and print\n    hipMemcpy(h_array, d_array, ARRAY_BYTES, hipMemcpyDeviceToHost);\n    print_array(h_array, ARRAY_SIZE);\n    printf(\"Time elapsed = %g ms\\n\", timer.Elapsed());\n \n    // free GPU memory allocation and exit\n    hipFree(d_array);\n    return 0;\n}", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 2 Code Snippets/atomics.cu"}
{"input": "#include <stdio.h>\n\n#define NUM_BLOCKS 1\n#define BLOCK_WIDTH 256\n\n__global__ void hello()\n{\n    printf(\"Hello world! I'm thread %d\\n\", threadIdx.x);\n}\n\n\nint main(int argc,char **argv)\n{\n    // launch the kernel\n    hello<<<NUM_BLOCKS, BLOCK_WIDTH>>>();\n\n    // force the printf()s to flush\n    cudaDeviceSynchronize();\n\n    printf(\"That's all!\\n\");\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#define NUM_BLOCKS 1\n#define BLOCK_WIDTH 256\n\n__global__ void hello()\n{\n    printf(\"Hello world! I'm thread %d\\n\", threadIdx.x);\n}\n\n\nint main(int argc,char **argv)\n{\n    // launch the kernel\n    hello<<<NUM_BLOCKS, BLOCK_WIDTH>>>();\n\n    // force the printf()s to flush\n    hipDeviceSynchronize();\n\n    printf(\"That's all!\\n\");\n\n    return 0;\n}\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 2 Code Snippets/hello_threadIdx.cu"}
{"input": "#include <stdio.h>\n\n#define NUM_BLOCKS 16\n#define BLOCK_WIDTH 1\n\n__global__ void hello()\n{\n    printf(\"Hello world! I'm a thread in block %d\\n\", blockIdx.x);\n}\n\n\nint main(int argc,char **argv)\n{\n    // launch the kernel\n    hello<<<NUM_BLOCKS, BLOCK_WIDTH>>>();\n\n    // force the printf()s to flush\n    cudaDeviceSynchronize();\n\n    printf(\"That's all!\\n\");\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n#define NUM_BLOCKS 16\n#define BLOCK_WIDTH 1\n\n__global__ void hello()\n{\n    printf(\"Hello world! I'm a thread in block %d\\n\", blockIdx.x);\n}\n\n\nint main(int argc,char **argv)\n{\n    // launch the kernel\n    hello<<<NUM_BLOCKS, BLOCK_WIDTH>>>();\n\n    // force the printf()s to flush\n    hipDeviceSynchronize();\n\n    printf(\"That's all!\\n\");\n\n    return 0;\n}\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 2 Code Snippets/hello_blockIdx.cu"}
{"input": "// Using different memory spaces in CUDA\n#include <stdio.h>\n\n/**********************\n * using local memory *\n **********************/\n\n// a __device__ or __global__ function runs on the GPU\n__global__ void use_local_memory_GPU(float in)\n{\n    float f;    // variable \"f\" is in local memory and private to each thread\n    f = in;     // parameter \"in\" is in local memory and private to each thread\n    // ... real code would presumably do other stuff here ... \n}\n\n/**********************\n * using global memory *\n **********************/\n\n// a __global__ function runs on the GPU & can be called from host\n__global__ void use_global_memory_GPU(float *array)\n{\n    // \"array\" is a pointer into global memory on the device\n    array[threadIdx.x] = 2.0f * (float) threadIdx.x;\n}\n\n/**********************\n * using shared memory *\n **********************/\n\n// (for clarity, hardcoding 128 threads/elements and omitting out-of-bounds checks)\n__global__ void use_shared_memory_GPU(float *array)\n{\n    // local variables, private to each thread\n    int i, index = threadIdx.x;\n    float average, sum = 0.0f;\n\n    // __shared__ variables are visible to all threads in the thread block\n    // and have the same lifetime as the thread block\n    __shared__ float sh_arr[128];\n\n    // copy data from \"array\" in global memory to sh_arr in shared memory.\n    // here, each thread is responsible for copying a single element.\n    sh_arr[index] = array[index];\n\n    __syncthreads();    // ensure all the writes to shared memory have completed\n\n    // now, sh_arr is fully populated. Let's find the average of all previous elements\n    for (i=0; i<index; i++) { sum += sh_arr[i]; }\n    average = sum / (index + 1.0f);\n\n    // if array[index] is greater than the average of array[0..index-1], replace with average.\n    // since array[] is in global memory, this change will be seen by the host (and potentially \n    // other thread blocks, if any)\n    if (array[index] > average) { array[index] = average; }\n\n    // the following code has NO EFFECT: it modifies shared memory, but \n    // the resulting modified data is never copied back to global memory\n    // and vanishes when the thread block completes\n    sh_arr[index] = 3.14;\n}\n\nint main(int argc, char **argv)\n{\n    /*\n     * First, call a kernel that shows using local memory \n     */\n    use_local_memory_GPU<<<1, 128>>>(2.0f);\n\n    /*\n     * Next, call a kernel that shows using global memory\n     */\n    float h_arr[128];   // convention: h_ variables live on host\n    float *d_arr;       // convention: d_ variables live on device (GPU global mem)\n\n    // allocate global memory on the device, place result in \"d_arr\"\n    cudaMalloc((void **) &d_arr, sizeof(float) * 128);\n    // now copy data from host memory \"h_arr\" to device memory \"d_arr\"\n    cudaMemcpy((void *)d_arr, (void *)h_arr, sizeof(float) * 128, cudaMemcpyHostToDevice);\n    // launch the kernel (1 block of 128 threads)\n    use_global_memory_GPU<<<1, 128>>>(d_arr);  // modifies the contents of array at d_arr\n    // copy the modified array back to the host, overwriting contents of h_arr\n    cudaMemcpy((void *)h_arr, (void *)d_arr, sizeof(float) * 128, cudaMemcpyDeviceToHost);\n    // ... do other stuff ...\n\n    /*\n     * Next, call a kernel that shows using shared memory\n     */\n\n    // as before, pass in a pointer to data in global memory\n    use_shared_memory_GPU<<<1, 128>>>(d_arr); \n    // copy the modified array back to the host\n    cudaMemcpy((void *)h_arr, (void *)d_arr, sizeof(float) * 128, cudaMemcpyHostToDevice);\n    // ... do other stuff ...\n    return 0;\n}", "output": "// Using different memory spaces in CUDA\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n/**********************\n * using local memory *\n **********************/\n\n// a __device__ or __global__ function runs on the GPU\n__global__ void use_local_memory_GPU(float in)\n{\n    float f;    // variable \"f\" is in local memory and private to each thread\n    f = in;     // parameter \"in\" is in local memory and private to each thread\n    // ... real code would presumably do other stuff here ... \n}\n\n/**********************\n * using global memory *\n **********************/\n\n// a __global__ function runs on the GPU & can be called from host\n__global__ void use_global_memory_GPU(float *array)\n{\n    // \"array\" is a pointer into global memory on the device\n    array[threadIdx.x] = 2.0f * (float) threadIdx.x;\n}\n\n/**********************\n * using shared memory *\n **********************/\n\n// (for clarity, hardcoding 128 threads/elements and omitting out-of-bounds checks)\n__global__ void use_shared_memory_GPU(float *array)\n{\n    // local variables, private to each thread\n    int i, index = threadIdx.x;\n    float average, sum = 0.0f;\n\n    // __shared__ variables are visible to all threads in the thread block\n    // and have the same lifetime as the thread block\n    __shared__ float sh_arr[128];\n\n    // copy data from \"array\" in global memory to sh_arr in shared memory.\n    // here, each thread is responsible for copying a single element.\n    sh_arr[index] = array[index];\n\n    __syncthreads();    // ensure all the writes to shared memory have completed\n\n    // now, sh_arr is fully populated. Let's find the average of all previous elements\n    for (i=0; i<index; i++) { sum += sh_arr[i]; }\n    average = sum / (index + 1.0f);\n\n    // if array[index] is greater than the average of array[0..index-1], replace with average.\n    // since array[] is in global memory, this change will be seen by the host (and potentially \n    // other thread blocks, if any)\n    if (array[index] > average) { array[index] = average; }\n\n    // the following code has NO EFFECT: it modifies shared memory, but \n    // the resulting modified data is never copied back to global memory\n    // and vanishes when the thread block completes\n    sh_arr[index] = 3.14;\n}\n\nint main(int argc, char **argv)\n{\n    /*\n     * First, call a kernel that shows using local memory \n     */\n    use_local_memory_GPU<<<1, 128>>>(2.0f);\n\n    /*\n     * Next, call a kernel that shows using global memory\n     */\n    float h_arr[128];   // convention: h_ variables live on host\n    float *d_arr;       // convention: d_ variables live on device (GPU global mem)\n\n    // allocate global memory on the device, place result in \"d_arr\"\n    hipMalloc((void **) &d_arr, sizeof(float) * 128);\n    // now copy data from host memory \"h_arr\" to device memory \"d_arr\"\n    hipMemcpy((void *)d_arr, (void *)h_arr, sizeof(float) * 128, hipMemcpyHostToDevice);\n    // launch the kernel (1 block of 128 threads)\n    use_global_memory_GPU<<<1, 128>>>(d_arr);  // modifies the contents of array at d_arr\n    // copy the modified array back to the host, overwriting contents of h_arr\n    hipMemcpy((void *)h_arr, (void *)d_arr, sizeof(float) * 128, hipMemcpyDeviceToHost);\n    // ... do other stuff ...\n\n    /*\n     * Next, call a kernel that shows using shared memory\n     */\n\n    // as before, pass in a pointer to data in global memory\n    use_shared_memory_GPU<<<1, 128>>>(d_arr); \n    // copy the modified array back to the host\n    hipMemcpy((void *)h_arr, (void *)d_arr, sizeof(float) * 128, hipMemcpyHostToDevice);\n    // ... do other stuff ...\n    return 0;\n}", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 2 Code Snippets/memory.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\nint log2(int i)\n{\n    int r = 0;\n    while (i >>= 1) r++;\n    return r;\n}\n\nint bit_reverse(int w, int bits)\n{\n    int r = 0;\n    for (int i = 0; i < bits; i++)\n    {\n        int bit = (w & (1 << i)) >> i;\n        r |= bit << (bits - i - 1);\n    }\n    return r;\n}\n\n__global__ void naive_histo(int *d_bins, const int *d_in, const int BIN_COUNT)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int myItem = d_in[myId];\n    int myBin = myItem % BIN_COUNT;\n    d_bins[myBin]++;\n}\n\n__global__ void simple_histo(int *d_bins, const int *d_in, const int BIN_COUNT)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int myItem = d_in[myId];\n    int myBin = myItem % BIN_COUNT;\n    atomicAdd(&(d_bins[myBin]), 1);\n}\n\n\nint main(int argc, char **argv)\n{\n    int deviceCount;\n    cudaGetDeviceCount(&deviceCount);\n    if (deviceCount == 0) {\n        fprintf(stderr, \"error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n    int dev = 0;\n    cudaSetDevice(dev);\n\n    cudaDeviceProp devProps;\n    if (cudaGetDeviceProperties(&devProps, dev) == 0)\n    {\n        printf(\"Using device %d:\\n\", dev);\n        printf(\"%s; global mem: %dB; compute v%d.%d; clock: %d kHz\\n\",\n               devProps.name, (int)devProps.totalGlobalMem, \n               (int)devProps.major, (int)devProps.minor, \n               (int)devProps.clockRate);\n    }\n\n    const int ARRAY_SIZE = 65536;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);\n    const int BIN_COUNT = 16;\n    const int BIN_BYTES = BIN_COUNT * sizeof(int);\n\n    // generate the input array on the host\n    int h_in[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        h_in[i] = bit_reverse(i, log2(ARRAY_SIZE));\n    }\n    int h_bins[BIN_COUNT];\n    for(int i = 0; i < BIN_COUNT; i++) {\n        h_bins[i] = 0;\n    }\n\n    // declare GPU memory pointers\n    int * d_in;\n    int * d_bins;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_bins, BIN_BYTES);\n\n    // transfer the arrays to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n    cudaMemcpy(d_bins, h_bins, BIN_BYTES, cudaMemcpyHostToDevice); \n\n    int whichKernel = 0;\n    if (argc == 2) {\n        whichKernel = atoi(argv[1]);\n    }\n        \n    // launch the kernel\n    switch(whichKernel) {\n    case 0:\n        printf(\"Running naive histo\\n\");\n        naive_histo<<<ARRAY_SIZE / 64, 64>>>(d_bins, d_in, BIN_COUNT);\n        break;\n    case 1:\n        printf(\"Running simple histo\\n\");\n        simple_histo<<<ARRAY_SIZE / 64, 64>>>(d_bins, d_in, BIN_COUNT);\n        break;\n    default:\n        fprintf(stderr, \"error: ran no kernel\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // copy back the sum from GPU\n    cudaMemcpy(h_bins, d_bins, BIN_BYTES, cudaMemcpyDeviceToHost);\n\n    for(int i = 0; i < BIN_COUNT; i++) {\n        printf(\"bin %d: count %d\\n\", i, h_bins[i]);\n    }\n\n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_bins);\n        \n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\nint log2(int i)\n{\n    int r = 0;\n    while (i >>= 1) r++;\n    return r;\n}\n\nint bit_reverse(int w, int bits)\n{\n    int r = 0;\n    for (int i = 0; i < bits; i++)\n    {\n        int bit = (w & (1 << i)) >> i;\n        r |= bit << (bits - i - 1);\n    }\n    return r;\n}\n\n__global__ void naive_histo(int *d_bins, const int *d_in, const int BIN_COUNT)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int myItem = d_in[myId];\n    int myBin = myItem % BIN_COUNT;\n    d_bins[myBin]++;\n}\n\n__global__ void simple_histo(int *d_bins, const int *d_in, const int BIN_COUNT)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int myItem = d_in[myId];\n    int myBin = myItem % BIN_COUNT;\n    atomicAdd(&(d_bins[myBin]), 1);\n}\n\n\nint main(int argc, char **argv)\n{\n    int deviceCount;\n    hipGetDeviceCount(&deviceCount);\n    if (deviceCount == 0) {\n        fprintf(stderr, \"error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n    int dev = 0;\n    hipSetDevice(dev);\n\n    hipDeviceProp_t devProps;\n    if (hipGetDeviceProperties(&devProps, dev) == 0)\n    {\n        printf(\"Using device %d:\\n\", dev);\n        printf(\"%s; global mem: %dB; compute v%d.%d; clock: %d kHz\\n\",\n               devProps.name, (int)devProps.totalGlobalMem, \n               (int)devProps.major, (int)devProps.minor, \n               (int)devProps.clockRate);\n    }\n\n    const int ARRAY_SIZE = 65536;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);\n    const int BIN_COUNT = 16;\n    const int BIN_BYTES = BIN_COUNT * sizeof(int);\n\n    // generate the input array on the host\n    int h_in[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        h_in[i] = bit_reverse(i, log2(ARRAY_SIZE));\n    }\n    int h_bins[BIN_COUNT];\n    for(int i = 0; i < BIN_COUNT; i++) {\n        h_bins[i] = 0;\n    }\n\n    // declare GPU memory pointers\n    int * d_in;\n    int * d_bins;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_bins, BIN_BYTES);\n\n    // transfer the arrays to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n    hipMemcpy(d_bins, h_bins, BIN_BYTES, hipMemcpyHostToDevice); \n\n    int whichKernel = 0;\n    if (argc == 2) {\n        whichKernel = atoi(argv[1]);\n    }\n        \n    // launch the kernel\n    switch(whichKernel) {\n    case 0:\n        printf(\"Running naive histo\\n\");\n        naive_histo<<<ARRAY_SIZE / 64, 64>>>(d_bins, d_in, BIN_COUNT);\n        break;\n    case 1:\n        printf(\"Running simple histo\\n\");\n        simple_histo<<<ARRAY_SIZE / 64, 64>>>(d_bins, d_in, BIN_COUNT);\n        break;\n    default:\n        fprintf(stderr, \"error: ran no kernel\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // copy back the sum from GPU\n    hipMemcpy(h_bins, d_bins, BIN_BYTES, hipMemcpyDeviceToHost);\n\n    for(int i = 0; i < BIN_COUNT; i++) {\n        printf(\"bin %d: count %d\\n\", i, h_bins[i]);\n    }\n\n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_bins);\n        \n    return 0;\n}\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 3 Code Snippets/histo.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n\n__global__ void global_reduce_kernel(float * d_out, float * d_in)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int tid  = threadIdx.x;\n\n    // do reduction in global mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1)\n    {\n        if (tid < s)\n        {\n            d_in[myId] += d_in[myId + s];\n        }\n        __syncthreads();        // make sure all adds at one stage are done!\n    }\n\n    // only thread 0 writes result for this block back to global mem\n    if (tid == 0)\n    {\n        d_out[blockIdx.x] = d_in[myId];\n    }\n}\n\n__global__ void shmem_reduce_kernel(float * d_out, const float * d_in)\n{\n    // sdata is allocated in the kernel call: 3rd arg to <<<b, t, shmem>>>\n    extern __shared__ float sdata[];\n\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int tid  = threadIdx.x;\n\n    // load shared mem from global mem\n    sdata[tid] = d_in[myId];\n    __syncthreads();            // make sure entire block is loaded!\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1)\n    {\n        if (tid < s)\n        {\n            sdata[tid] += sdata[tid + s];\n        }\n        __syncthreads();        // make sure all adds at one stage are done!\n    }\n\n    // only thread 0 writes result for this block back to global mem\n    if (tid == 0)\n    {\n        d_out[blockIdx.x] = sdata[0];\n    }\n}\n\nvoid reduce(float * d_out, float * d_intermediate, float * d_in, \n            int size, bool usesSharedMemory)\n{\n    // assumes that size is not greater than maxThreadsPerBlock^2\n    // and that size is a multiple of maxThreadsPerBlock\n    const int maxThreadsPerBlock = 1024;\n    int threads = maxThreadsPerBlock;\n    int blocks = size / maxThreadsPerBlock;\n    if (usesSharedMemory)\n    {\n        shmem_reduce_kernel<<<blocks, threads, threads * sizeof(float)>>>\n            (d_intermediate, d_in);\n    }\n    else\n    {\n        global_reduce_kernel<<<blocks, threads>>>\n            (d_intermediate, d_in);\n    }\n    // now we're down to one block left, so reduce it\n    threads = blocks; // launch one thread for each block in prev step\n    blocks = 1;\n    if (usesSharedMemory)\n    {\n        shmem_reduce_kernel<<<blocks, threads, threads * sizeof(float)>>>\n            (d_out, d_intermediate);\n    }\n    else\n    {\n        global_reduce_kernel<<<blocks, threads>>>\n            (d_out, d_intermediate);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int deviceCount;\n    cudaGetDeviceCount(&deviceCount);\n    if (deviceCount == 0) {\n        fprintf(stderr, \"error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n    int dev = 0;\n    cudaSetDevice(dev);\n\n    cudaDeviceProp devProps;\n    if (cudaGetDeviceProperties(&devProps, dev) == 0)\n    {\n        printf(\"Using device %d:\\n\", dev);\n        printf(\"%s; global mem: %dB; compute v%d.%d; clock: %d kHz\\n\",\n               devProps.name, (int)devProps.totalGlobalMem, \n               (int)devProps.major, (int)devProps.minor, \n               (int)devProps.clockRate);\n    }\n\n    const int ARRAY_SIZE = 1 << 20;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float sum = 0.0f;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [-1.0f, 1.0f]\n        h_in[i] = -1.0f + (float)random()/((float)RAND_MAX/2.0f);\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    float * d_in, * d_intermediate, * d_out;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_intermediate, ARRAY_BYTES); // overallocated\n    cudaMalloc((void **) &d_out, sizeof(float));\n\n    // transfer the input array to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n\n    int whichKernel = 0;\n    if (argc == 2) {\n        whichKernel = atoi(argv[1]);\n    }\n        \n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n    // launch the kernel\n    switch(whichKernel) {\n    case 0:\n        printf(\"Running global reduce\\n\");\n        cudaEventRecord(start, 0);\n        for (int i = 0; i < 100; i++)\n        {\n            reduce(d_out, d_intermediate, d_in, ARRAY_SIZE, false);\n        }\n        cudaEventRecord(stop, 0);\n        break;\n    case 1:\n        printf(\"Running reduce with shared mem\\n\");\n        cudaEventRecord(start, 0);\n        for (int i = 0; i < 100; i++)\n        {\n            reduce(d_out, d_intermediate, d_in, ARRAY_SIZE, true);\n        }\n        cudaEventRecord(stop, 0);\n        break;\n    default:\n        fprintf(stderr, \"error: ran no kernel\\n\");\n        exit(EXIT_FAILURE);\n    }\n    cudaEventSynchronize(stop);\n    float elapsedTime;\n    cudaEventElapsedTime(&elapsedTime, start, stop);    \n    elapsedTime /= 100.0f;      // 100 trials\n\n    // copy back the sum from GPU\n    float h_out;\n    cudaMemcpy(&h_out, d_out, sizeof(float), cudaMemcpyDeviceToHost);\n\n    printf(\"average time elapsed: %f\\n\", elapsedTime);\n\n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_intermediate);\n    cudaFree(d_out);\n        \n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n\n__global__ void global_reduce_kernel(float * d_out, float * d_in)\n{\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int tid  = threadIdx.x;\n\n    // do reduction in global mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1)\n    {\n        if (tid < s)\n        {\n            d_in[myId] += d_in[myId + s];\n        }\n        __syncthreads();        // make sure all adds at one stage are done!\n    }\n\n    // only thread 0 writes result for this block back to global mem\n    if (tid == 0)\n    {\n        d_out[blockIdx.x] = d_in[myId];\n    }\n}\n\n__global__ void shmem_reduce_kernel(float * d_out, const float * d_in)\n{\n    // sdata is allocated in the kernel call: 3rd arg to <<<b, t, shmem>>>\n    extern __shared__ float sdata[];\n\n    int myId = threadIdx.x + blockDim.x * blockIdx.x;\n    int tid  = threadIdx.x;\n\n    // load shared mem from global mem\n    sdata[tid] = d_in[myId];\n    __syncthreads();            // make sure entire block is loaded!\n\n    // do reduction in shared mem\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1)\n    {\n        if (tid < s)\n        {\n            sdata[tid] += sdata[tid + s];\n        }\n        __syncthreads();        // make sure all adds at one stage are done!\n    }\n\n    // only thread 0 writes result for this block back to global mem\n    if (tid == 0)\n    {\n        d_out[blockIdx.x] = sdata[0];\n    }\n}\n\nvoid reduce(float * d_out, float * d_intermediate, float * d_in, \n            int size, bool usesSharedMemory)\n{\n    // assumes that size is not greater than maxThreadsPerBlock^2\n    // and that size is a multiple of maxThreadsPerBlock\n    const int maxThreadsPerBlock = 1024;\n    int threads = maxThreadsPerBlock;\n    int blocks = size / maxThreadsPerBlock;\n    if (usesSharedMemory)\n    {\n        shmem_reduce_kernel<<<blocks, threads, threads * sizeof(float)>>>\n            (d_intermediate, d_in);\n    }\n    else\n    {\n        global_reduce_kernel<<<blocks, threads>>>\n            (d_intermediate, d_in);\n    }\n    // now we're down to one block left, so reduce it\n    threads = blocks; // launch one thread for each block in prev step\n    blocks = 1;\n    if (usesSharedMemory)\n    {\n        shmem_reduce_kernel<<<blocks, threads, threads * sizeof(float)>>>\n            (d_out, d_intermediate);\n    }\n    else\n    {\n        global_reduce_kernel<<<blocks, threads>>>\n            (d_out, d_intermediate);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    int deviceCount;\n    hipGetDeviceCount(&deviceCount);\n    if (deviceCount == 0) {\n        fprintf(stderr, \"error: no devices supporting CUDA.\\n\");\n        exit(EXIT_FAILURE);\n    }\n    int dev = 0;\n    hipSetDevice(dev);\n\n    hipDeviceProp_t devProps;\n    if (hipGetDeviceProperties(&devProps, dev) == 0)\n    {\n        printf(\"Using device %d:\\n\", dev);\n        printf(\"%s; global mem: %dB; compute v%d.%d; clock: %d kHz\\n\",\n               devProps.name, (int)devProps.totalGlobalMem, \n               (int)devProps.major, (int)devProps.minor, \n               (int)devProps.clockRate);\n    }\n\n    const int ARRAY_SIZE = 1 << 20;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float sum = 0.0f;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [-1.0f, 1.0f]\n        h_in[i] = -1.0f + (float)random()/((float)RAND_MAX/2.0f);\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    float * d_in, * d_intermediate, * d_out;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_intermediate, ARRAY_BYTES); // overallocated\n    hipMalloc((void **) &d_out, sizeof(float));\n\n    // transfer the input array to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n\n    int whichKernel = 0;\n    if (argc == 2) {\n        whichKernel = atoi(argv[1]);\n    }\n        \n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n    // launch the kernel\n    switch(whichKernel) {\n    case 0:\n        printf(\"Running global reduce\\n\");\n        hipEventRecord(start, 0);\n        for (int i = 0; i < 100; i++)\n        {\n            reduce(d_out, d_intermediate, d_in, ARRAY_SIZE, false);\n        }\n        hipEventRecord(stop, 0);\n        break;\n    case 1:\n        printf(\"Running reduce with shared mem\\n\");\n        hipEventRecord(start, 0);\n        for (int i = 0; i < 100; i++)\n        {\n            reduce(d_out, d_intermediate, d_in, ARRAY_SIZE, true);\n        }\n        hipEventRecord(stop, 0);\n        break;\n    default:\n        fprintf(stderr, \"error: ran no kernel\\n\");\n        exit(EXIT_FAILURE);\n    }\n    hipEventSynchronize(stop);\n    float elapsedTime;\n    hipEventElapsedTime(&elapsedTime, start, stop);    \n    elapsedTime /= 100.0f;      // 100 trials\n\n    // copy back the sum from GPU\n    float h_out;\n    hipMemcpy(&h_out, d_out, sizeof(float), hipMemcpyDeviceToHost);\n\n    printf(\"average time elapsed: %f\\n\", elapsedTime);\n\n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_intermediate);\n    hipFree(d_out);\n        \n    return 0;\n}\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 3 Code Snippets/reduce.cu"}
{"input": "#include <stdio.h>\n#include \"gputimer.h\"\n\nconst int N= 1024;\t\t// matrix size is NxN\nconst int K= 32;\t\t\t\t// tile size is KxK\n\n// Utility functions: compare, print, and fill matrices\n#define checkCudaErrors(val) check( (val), #val, __FILE__, __LINE__)\n\ntemplate<typename T>\nvoid check(T err, const char* const func, const char* const file, const int line)\n{\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"CUDA error at: %s : %d\\n\", file,line);\n    fprintf(stderr, \"%s %s\\n\", cudaGetErrorString(err), func);;\n    exit(1);\n  }\n}\n\nint compare_matrices(float *gpu, float *ref)\n{\n\tint result = 0;\n\n\tfor(int j=0; j < N; j++)\n    \tfor(int i=0; i < N; i++)\n    \t\tif (ref[i + j*N] != gpu[i + j*N])\n    \t\t{\n    \t\t\t// printf(\"reference(%d,%d) = %f but test(%d,%d) = %f\\n\",\n    \t\t\t// i,j,ref[i+j*N],i,j,test[i+j*N]);\n    \t\t\tresult = 1;\n    \t\t}\n    return result;\n}\n\nvoid print_matrix(float *mat)\n{\n\tfor(int j=0; j < N; j++) \n\t{\n\t\tfor(int i=0; i < N; i++) { printf(\"%4.4g \", mat[i + j*N]); }\n\t\tprintf(\"\\n\");\n\t}\t\n}\n\n// fill a matrix with sequential numbers in the range 0..N-1\nvoid fill_matrix(float *mat)\n{\n\tfor(int j=0; j < N * N; j++)\n\t\tmat[j] = (float) j;\n}\n\n\n\nvoid \ntranspose_CPU(float in[], float out[])\n{\n\tfor(int j=0; j < N; j++)\n    \tfor(int i=0; i < N; i++)\n      \t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched on a single thread\n__global__ void \ntranspose_serial(float in[], float out[])\n{\n\tfor(int j=0; j < N; j++)\n\t\tfor(int i=0; i < N; i++)\n\t\t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per row of output matrix\n__global__ void \ntranspose_parallel_per_row(float in[], float out[])\n{\n\tint i = threadIdx.x;\n\n\tfor(int j=0; j < N; j++)\n\t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread (x,y) in grid writes element (i,j) of output matrix \n__global__ void \ntranspose_parallel_per_element(float in[], float out[])\n{\n\tint i = blockIdx.x * K + threadIdx.x;\n\tint j = blockIdx.y * K + threadIdx.y;\n\n\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per element, in (tilesize)x(tilesize) threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// adjacent threads read adjacent input elements, write adjacent output elmts\n__global__ void \ntranspose_parallel_per_element_tiled(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * K, in_corner_j  = blockIdx.y * K;\n\tint out_corner_i = blockIdx.y * K, out_corner_j = blockIdx.x * K;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[K][K];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in (tilesize)x(tilesize) threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// adjacent threads read adjacent input elements, write adjacent output elmts\n__global__ void \ntranspose_parallel_per_element_tiled16(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * 16, in_corner_j  = blockIdx.y * 16;\n\tint out_corner_i = blockIdx.y * 16, out_corner_j = blockIdx.x * 16;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[16][16];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// shared memory array padded to avoid bank conflicts\n__global__ void \ntranspose_parallel_per_element_tiled_padded(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * K, in_corner_j  = blockIdx.y * K;\n\tint out_corner_i = blockIdx.y * K, out_corner_j = blockIdx.x * K;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[K][K+1];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// shared memory array padded to avoid bank conflicts\n__global__ void \ntranspose_parallel_per_element_tiled_padded16(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * 16, in_corner_j  = blockIdx.y * 16;\n\tint out_corner_i = blockIdx.y * 16, out_corner_j = blockIdx.x * 16;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[16][16+1];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\nint main(int argc, char **argv)\n{\n\tint numbytes = N * N * sizeof(float);\n\n\tfloat *in = (float *) malloc(numbytes);\n\tfloat *out = (float *) malloc(numbytes);\n\tfloat *gold = (float *) malloc(numbytes);\n\n\tfill_matrix(in);\n\ttranspose_CPU(in, gold);\n\n\tfloat *d_in, *d_out;\n\n\tcudaMalloc(&d_in, numbytes);\n\tcudaMalloc(&d_out, numbytes);\n\tcudaMemcpy(d_in, in, numbytes, cudaMemcpyHostToDevice);\n\n\tGpuTimer timer;\n\n/*  \n * Now time each kernel and verify that it produces the correct result.\n *\n * To be really careful about benchmarking purposes, we should run every kernel once\n * to \"warm\" the system and avoid any compilation or code-caching effects, then run \n * every kernel 10 or 100 times and average the timings to smooth out any variance. \n * But this makes for messy code and our goal is teaching, not detailed benchmarking.\n */\n\n\ttimer.Start();\n\ttranspose_serial<<<1,1>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_serial: %g ms.\\nVerifying transpose...%s\\n\", \n\t       timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\ttimer.Start();\n\ttranspose_parallel_per_row<<<1,N>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_row: %g ms.\\nVerifying transpose...%s\\n\", \n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\tdim3 blocks(N/K,N/K); // blocks per grid\n\tdim3 threads(K,K);\t// threads per block\n\n\ttimer.Start();\n\ttranspose_parallel_per_element<<<blocks,threads>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element: %g ms.\\nVerifying transpose...%s\\n\",\n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\ttimer.Start();\n\ttranspose_parallel_per_element_tiled<<<blocks,threads>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled %dx%d: %g ms.\\nVerifying ...%s\\n\", \n\t\t   K, K, timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\t\n\tdim3 blocks16x16(N/16,N/16); // blocks per grid\n\tdim3 threads16x16(16,16);\t // threads per block\n\n\ttimer.Start();\n\ttranspose_parallel_per_element_tiled16<<<blocks16x16,threads16x16>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled 16x16: %g ms.\\nVerifying ...%s\\n\", \n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\t\n\ttimer.Start();\n \ttranspose_parallel_per_element_tiled_padded16<<<blocks16x16,threads16x16>>>(d_in, d_out);\n\ttimer.Stop();\n\tcudaMemcpy(out, d_out, numbytes, cudaMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled_padded 16x16: %g ms.\\nVerifying...%s\\n\", \n\t       timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\tcudaFree(d_in);\n\tcudaFree(d_out);\n}", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include \"gputimer.h\"\n\nconst int N= 1024;\t\t// matrix size is NxN\nconst int K= 32;\t\t\t\t// tile size is KxK\n\n// Utility functions: compare, print, and fill matrices\n#define checkCudaErrors(val) check( (val), #val, __FILE__, __LINE__)\n\ntemplate<typename T>\nvoid check(T err, const char* const func, const char* const file, const int line)\n{\n  if (err != hipSuccess) {\n    fprintf(stderr, \"CUDA error at: %s : %d\\n\", file,line);\n    fprintf(stderr, \"%s %s\\n\", hipGetErrorString(err), func);;\n    exit(1);\n  }\n}\n\nint compare_matrices(float *gpu, float *ref)\n{\n\tint result = 0;\n\n\tfor(int j=0; j < N; j++)\n    \tfor(int i=0; i < N; i++)\n    \t\tif (ref[i + j*N] != gpu[i + j*N])\n    \t\t{\n    \t\t\t// printf(\"reference(%d,%d) = %f but test(%d,%d) = %f\\n\",\n    \t\t\t// i,j,ref[i+j*N],i,j,test[i+j*N]);\n    \t\t\tresult = 1;\n    \t\t}\n    return result;\n}\n\nvoid print_matrix(float *mat)\n{\n\tfor(int j=0; j < N; j++) \n\t{\n\t\tfor(int i=0; i < N; i++) { printf(\"%4.4g \", mat[i + j*N]); }\n\t\tprintf(\"\\n\");\n\t}\t\n}\n\n// fill a matrix with sequential numbers in the range 0..N-1\nvoid fill_matrix(float *mat)\n{\n\tfor(int j=0; j < N * N; j++)\n\t\tmat[j] = (float) j;\n}\n\n\n\nvoid \ntranspose_CPU(float in[], float out[])\n{\n\tfor(int j=0; j < N; j++)\n    \tfor(int i=0; i < N; i++)\n      \t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched on a single thread\n__global__ void \ntranspose_serial(float in[], float out[])\n{\n\tfor(int j=0; j < N; j++)\n\t\tfor(int i=0; i < N; i++)\n\t\t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per row of output matrix\n__global__ void \ntranspose_parallel_per_row(float in[], float out[])\n{\n\tint i = threadIdx.x;\n\n\tfor(int j=0; j < N; j++)\n\t\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread (x,y) in grid writes element (i,j) of output matrix \n__global__ void \ntranspose_parallel_per_element(float in[], float out[])\n{\n\tint i = blockIdx.x * K + threadIdx.x;\n\tint j = blockIdx.y * K + threadIdx.y;\n\n\tout[j + i*N] = in[i + j*N]; // out(j,i) = in(i,j)\n}\n\n// to be launched with one thread per element, in (tilesize)x(tilesize) threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// adjacent threads read adjacent input elements, write adjacent output elmts\n__global__ void \ntranspose_parallel_per_element_tiled(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * K, in_corner_j  = blockIdx.y * K;\n\tint out_corner_i = blockIdx.y * K, out_corner_j = blockIdx.x * K;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[K][K];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in (tilesize)x(tilesize) threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// adjacent threads read adjacent input elements, write adjacent output elmts\n__global__ void \ntranspose_parallel_per_element_tiled16(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * 16, in_corner_j  = blockIdx.y * 16;\n\tint out_corner_i = blockIdx.y * 16, out_corner_j = blockIdx.x * 16;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[16][16];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// shared memory array padded to avoid bank conflicts\n__global__ void \ntranspose_parallel_per_element_tiled_padded(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * K, in_corner_j  = blockIdx.y * K;\n\tint out_corner_i = blockIdx.y * K, out_corner_j = blockIdx.x * K;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[K][K+1];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\n// to be launched with one thread per element, in KxK threadblocks\n// thread blocks read & write tiles, in coalesced fashion\n// shared memory array padded to avoid bank conflicts\n__global__ void \ntranspose_parallel_per_element_tiled_padded16(float in[], float out[])\n{\n\t// (i,j) locations of the tile corners for input & output matrices:\n\tint in_corner_i  = blockIdx.x * 16, in_corner_j  = blockIdx.y * 16;\n\tint out_corner_i = blockIdx.y * 16, out_corner_j = blockIdx.x * 16;\n\n\tint x = threadIdx.x, y = threadIdx.y;\n\n\t__shared__ float tile[16][16+1];\n\n\t// coalesced read from global mem, TRANSPOSED write into shared mem:\n\ttile[y][x] = in[(in_corner_i + x) + (in_corner_j + y)*N];\n\t__syncthreads();\n\t// read from shared mem, coalesced write to global mem:\n\tout[(out_corner_i + x) + (out_corner_j + y)*N] = tile[x][y];\n}\n\nint main(int argc, char **argv)\n{\n\tint numbytes = N * N * sizeof(float);\n\n\tfloat *in = (float *) malloc(numbytes);\n\tfloat *out = (float *) malloc(numbytes);\n\tfloat *gold = (float *) malloc(numbytes);\n\n\tfill_matrix(in);\n\ttranspose_CPU(in, gold);\n\n\tfloat *d_in, *d_out;\n\n\thipMalloc(&d_in, numbytes);\n\thipMalloc(&d_out, numbytes);\n\thipMemcpy(d_in, in, numbytes, hipMemcpyHostToDevice);\n\n\tGpuTimer timer;\n\n/*  \n * Now time each kernel and verify that it produces the correct result.\n *\n * To be really careful about benchmarking purposes, we should run every kernel once\n * to \"warm\" the system and avoid any compilation or code-caching effects, then run \n * every kernel 10 or 100 times and average the timings to smooth out any variance. \n * But this makes for messy code and our goal is teaching, not detailed benchmarking.\n */\n\n\ttimer.Start();\n\ttranspose_serial<<<1,1>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_serial: %g ms.\\nVerifying transpose...%s\\n\", \n\t       timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\ttimer.Start();\n\ttranspose_parallel_per_row<<<1,N>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_row: %g ms.\\nVerifying transpose...%s\\n\", \n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\tdim3 blocks(N/K,N/K); // blocks per grid\n\tdim3 threads(K,K);\t// threads per block\n\n\ttimer.Start();\n\ttranspose_parallel_per_element<<<blocks,threads>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element: %g ms.\\nVerifying transpose...%s\\n\",\n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\ttimer.Start();\n\ttranspose_parallel_per_element_tiled<<<blocks,threads>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled %dx%d: %g ms.\\nVerifying ...%s\\n\", \n\t\t   K, K, timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\t\n\tdim3 blocks16x16(N/16,N/16); // blocks per grid\n\tdim3 threads16x16(16,16);\t // threads per block\n\n\ttimer.Start();\n\ttranspose_parallel_per_element_tiled16<<<blocks16x16,threads16x16>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled 16x16: %g ms.\\nVerifying ...%s\\n\", \n\t\t   timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\t\n\ttimer.Start();\n \ttranspose_parallel_per_element_tiled_padded16<<<blocks16x16,threads16x16>>>(d_in, d_out);\n\ttimer.Stop();\n\thipMemcpy(out, d_out, numbytes, hipMemcpyDeviceToHost);\n\tprintf(\"transpose_parallel_per_element_tiled_padded 16x16: %g ms.\\nVerifying...%s\\n\", \n\t       timer.Elapsed(), compare_matrices(out, gold) ? \"Failed\" : \"Success\");\n\n\thipFree(d_in);\n\thipFree(d_out);\n}", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 5 Code Snippets/transpose.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2013, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * Example compilation string:\n *\n * nvcc example_block_scan_sum.cu -gencode=arch=compute_20,code=\\\"sm_20,compute_20\\\" -o example_block_scan_sum\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <cub/cub.cuh>\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool g_verbose      = false;\nint g_iterations    = 100;\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\n__global__ void BlockPrefixSumKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    // Parameterize BlockScan type for our thread block\n    typedef BlockScan<int, BLOCK_THREADS> BlockScanT;\n\n    // Shared memory\n    __shared__ typename BlockScanT::SmemStorage smem_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n    BlockLoadVectorized(d_in, data);\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute exclusive prefix sum\n    int aggregate;\n    BlockScanT::ExclusiveSum(smem_storage, data, data, aggregate);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store output\n    BlockStoreVectorized(d_out, data);\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(\n    int *h_in,\n    int *h_reference,\n    int num_elements)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_elements; ++i)\n    {\n        h_in[i] = i % 17;\n\n        h_reference[i] = inclusive;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block scan\n */\ntemplate <\n    int BLOCK_THREADS,\n    int ITEMS_PER_THREAD>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_reference    = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    cudaMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    cudaMalloc((void**)&d_out,         sizeof(int) * (TILE_SIZE + 1));\n    cudaMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n    printf(\"BlockScan %d items (%d threads, %d items per thread): \",\n        TILE_SIZE, BLOCK_THREADS, ITEMS_PER_THREAD);\n\n    // Run this several times and average the performance results\n    clock_t elapsed_scan_clocks     = 0;\n    for (int i = 0; i < g_iterations; ++i)\n    {\n        // Run aggregate/prefix kernel\n        BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD><<<1, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        // Copy results from device\n        clock_t scan_clocks;\n        cudaMemcpy(h_gpu, d_out, sizeof(int) * (TILE_SIZE + 1), cudaMemcpyDeviceToHost);\n        cudaMemcpy(&scan_clocks, d_elapsed, sizeof(clock_t), cudaMemcpyDeviceToHost);\n        elapsed_scan_clocks += scan_clocks;\n    }\n\n    // Check scanned items\n    bool correct = true;\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n        if (h_gpu[i] != h_reference[i])\n        {\n            printf(\"Incorrect result @ offset %d (%d != %d)\\n\",\n                i, h_gpu[i], h_reference[i]);\n            correct = false;\n            break;\n        }\n    }\n\n    // Check total aggregate\n    if (h_gpu[TILE_SIZE] != h_aggregate)\n    {\n        printf(\"Incorrect aggregate (%d != %d)\\n\", h_gpu[TILE_SIZE], h_aggregate);\n        correct = false;\n    }\n    if (correct) printf(\"Correct!\\n\");\n\n    // Display results problem data\n    if (g_verbose)\n    {\n        printf(\"GPU output (reference output): \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d (%d), \", h_gpu[i], h_reference[i]);\n        printf(\"\\n\");\n        printf(\"GPU aggregate (reference aggregate)\", h_gpu[TILE_SIZE], h_aggregate);\n        printf(\"\\n\\n\");\n    }\n\n    // Display timing results\n    printf(\"Average clocks per 32-bit int scanned: %.3f\\n\\n\", float(elapsed_scan_clocks) / TILE_SIZE / g_iterations);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) cudaFree(d_in);\n    if (d_out) cudaFree(d_out);\n    if (d_elapsed) cudaFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Display GPU name\n    cudaDeviceProp props;\n    cudaGetDeviceProperties(&props, 0);\n    printf(\"Using device %s\\n\", props.name);\n\n/** Add tests here **/\n\n    // Run tests\n    Test<1024, 1>();\n    Test<512, 2>();\n    Test<256, 4>();\n    Test<128, 8>();\n    Test<64, 16>();\n    Test<32, 32>();\n    Test<16, 64>();\n\n/****/\n\n    return 0;\n}\n\n", "output": "/******************************************************************************\n * Copyright (c) 2011, Duane Merrill.  All rights reserved.\n * Copyright (c) 2011-2013, NVIDIA CORPORATION.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *     * Redistributions of source code must retain the above copyright\n *       notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *     * Neither the name of the NVIDIA CORPORATION nor the\n *       names of its contributors may be used to endorse or promote products\n *       derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n ******************************************************************************/\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * Example compilation string:\n *\n * nvcc example_block_scan_sum.cu -gencode=arch=compute_20,code=\\\"sm_20,compute_20\\\" -o example_block_scan_sum\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <stdio.h>\n#include <iostream>\n\n#include <hipcub/hipcub.hpp>\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and typedefs\n//---------------------------------------------------------------------\n\nbool g_verbose      = false;\nint g_iterations    = 100;\n\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <\n    int         BLOCK_THREADS,\n    int         ITEMS_PER_THREAD>\n__global__ void BlockPrefixSumKernel(\n    int         *d_in,          // Tile of input\n    int         *d_out,         // Tile of output\n    clock_t     *d_elapsed)     // Elapsed cycle count of block scan\n{\n    // Parameterize BlockScan type for our thread block\n    typedef BlockScan<int, BLOCK_THREADS> BlockScanT;\n\n    // Shared memory\n    __shared__ typename BlockScanT::SmemStorage smem_storage;\n\n    // Per-thread tile data\n    int data[ITEMS_PER_THREAD];\n    BlockLoadVectorized(d_in, data);\n\n    // Start cycle timer\n    clock_t start = clock();\n\n    // Compute exclusive prefix sum\n    int aggregate;\n    BlockScanT::ExclusiveSum(smem_storage, data, data, aggregate);\n\n    // Stop cycle timer\n    clock_t stop = clock();\n\n    // Store output\n    BlockStoreVectorized(d_out, data);\n\n    // Store aggregate and elapsed clocks\n    if (threadIdx.x == 0)\n    {\n        *d_elapsed = (start > stop) ? start - stop : stop - start;\n        d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n    }\n}\n\n\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(\n    int *h_in,\n    int *h_reference,\n    int num_elements)\n{\n    int inclusive = 0;\n\n    for (int i = 0; i < num_elements; ++i)\n    {\n        h_in[i] = i % 17;\n\n        h_reference[i] = inclusive;\n        inclusive += h_in[i];\n    }\n\n    return inclusive;\n}\n\n\n/**\n * Test thread block scan\n */\ntemplate <\n    int BLOCK_THREADS,\n    int ITEMS_PER_THREAD>\nvoid Test()\n{\n    const int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n    // Allocate host arrays\n    int *h_in           = new int[TILE_SIZE];\n    int *h_reference    = new int[TILE_SIZE];\n    int *h_gpu          = new int[TILE_SIZE + 1];\n\n    // Initialize problem and reference output on host\n    int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n    // Initialize device arrays\n    int *d_in           = NULL;\n    int *d_out          = NULL;\n    clock_t *d_elapsed  = NULL;\n    hipMalloc((void**)&d_in,          sizeof(int) * TILE_SIZE);\n    hipMalloc((void**)&d_out,         sizeof(int) * (TILE_SIZE + 1));\n    hipMalloc((void**)&d_elapsed,     sizeof(clock_t));\n\n    // Display input problem data\n    if (g_verbose)\n    {\n        printf(\"Input data: \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d, \", h_in[i]);\n        printf(\"\\n\\n\");\n    }\n\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n    printf(\"BlockScan %d items (%d threads, %d items per thread): \",\n        TILE_SIZE, BLOCK_THREADS, ITEMS_PER_THREAD);\n\n    // Run this several times and average the performance results\n    clock_t elapsed_scan_clocks     = 0;\n    for (int i = 0; i < g_iterations; ++i)\n    {\n        // Run aggregate/prefix kernel\n        BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD><<<1, BLOCK_THREADS>>>(\n            d_in,\n            d_out,\n            d_elapsed);\n\n        // Copy results from device\n        clock_t scan_clocks;\n        hipMemcpy(h_gpu, d_out, sizeof(int) * (TILE_SIZE + 1), hipMemcpyDeviceToHost);\n        hipMemcpy(&scan_clocks, d_elapsed, sizeof(clock_t), hipMemcpyDeviceToHost);\n        elapsed_scan_clocks += scan_clocks;\n    }\n\n    // Check scanned items\n    bool correct = true;\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n        if (h_gpu[i] != h_reference[i])\n        {\n            printf(\"Incorrect result @ offset %d (%d != %d)\\n\",\n                i, h_gpu[i], h_reference[i]);\n            correct = false;\n            break;\n        }\n    }\n\n    // Check total aggregate\n    if (h_gpu[TILE_SIZE] != h_aggregate)\n    {\n        printf(\"Incorrect aggregate (%d != %d)\\n\", h_gpu[TILE_SIZE], h_aggregate);\n        correct = false;\n    }\n    if (correct) printf(\"Correct!\\n\");\n\n    // Display results problem data\n    if (g_verbose)\n    {\n        printf(\"GPU output (reference output): \");\n        for (int i = 0; i < TILE_SIZE; i++)\n            printf(\"%d (%d), \", h_gpu[i], h_reference[i]);\n        printf(\"\\n\");\n        printf(\"GPU aggregate (reference aggregate)\", h_gpu[TILE_SIZE], h_aggregate);\n        printf(\"\\n\\n\");\n    }\n\n    // Display timing results\n    printf(\"Average clocks per 32-bit int scanned: %.3f\\n\\n\", float(elapsed_scan_clocks) / TILE_SIZE / g_iterations);\n\n    // Cleanup\n    if (h_in) delete[] h_in;\n    if (h_reference) delete[] h_reference;\n    if (h_gpu) delete[] h_gpu;\n    if (d_in) hipFree(d_in);\n    if (d_out) hipFree(d_out);\n    if (d_elapsed) hipFree(d_elapsed);\n}\n\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n    // Display GPU name\n    hipDeviceProp_t props;\n    hipGetDeviceProperties(&props, 0);\n    printf(\"Using device %s\\n\", props.name);\n\n/** Add tests here **/\n\n    // Run tests\n    Test<1024, 1>();\n    Test<512, 2>();\n    Test<256, 4>();\n    Test<128, 8>();\n    Test<64, 16>();\n    Test<32, 32>();\n    Test<16, 64>();\n\n/****/\n\n    return 0;\n}\n\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 7 Code Snippets/cub/example_block_scan_cum.cu"}
{"input": "#include <stdio.h>\n#include \"gputimer.h\"\n#include \"utils.h\"\n\nconst int BLOCKSIZE\t= 128;\nconst int NUMBLOCKS = 1000;\t\t\t\t\t// set this to 1 or 2 for debugging\nconst int N \t\t= BLOCKSIZE*NUMBLOCKS;\n\n/* \n * TODO: modify the foo and bar kernels to use tiling: \n * \t\t - copy the input data to shared memory\n *\t\t - perform the computation there\n *\t     - copy the result back to global memory\n *\t\t - assume thread blocks of 128 threads\n *\t\t - handle intra-block boundaries correctly\n * You can ignore boundary conditions (we ignore the first 2 and last 2 elements)\n */\n__global__ void foo(float out[], float A[], float B[], float C[], float D[], float E[]){\n\n\tint i = threadIdx.x + blockIdx.x*blockDim.x; \n\t\n\tout[i] = (A[i] + B[i] + C[i] + D[i] + E[i]) / 5.0f;\n}\n\n__global__ void bar(float out[], float in[]) \n{\n\tint i = threadIdx.x + blockIdx.x*blockDim.x; \n\n\tout[i] = (in[i-2] + in[i-1] + in[i] + in[i+1] + in[i+2]) / 5.0f;\n}\n\nvoid cpuFoo(float out[], float A[], float B[], float C[], float D[], float E[])\n{\n\tfor (int i=0; i<N; i++)\n\t{\n\t\tout[i] = (A[i] + B[i] + C[i] + D[i] + E[i]) / 5.0f;\n\t}\n}\n\nvoid cpuBar(float out[], float in[])\n{\n\t// ignore the boundaries\n\tfor (int i=2; i<N-2; i++)\n\t{\n\t\tout[i] = (in[i-2] + in[i-1] + in[i] + in[i+1] + in[i+2]) / 5.0f;\n\t}\n}\n\nint main(int argc, char **argv)\n{\n\t// declare and fill input arrays for foo() and bar()\n\tfloat fooA[N], fooB[N], fooC[N], fooD[N], fooE[N], barIn[N];\n\tfor (int i=0; i<N; i++) \n\t{\n\t\tfooA[i] = i; \n\t\tfooB[i] = i+1;\n\t\tfooC[i] = i+2;\n\t\tfooD[i] = i+3;\n\t\tfooE[i] = i+4;\n\t\tbarIn[i] = 2*i; \n\t}\n\t// device arrays\n\tint numBytes = N * sizeof(float);\n\tfloat *d_fooA;\t \tcudaMalloc(&d_fooA, numBytes);\n\tfloat *d_fooB; \t\tcudaMalloc(&d_fooB, numBytes);\n\tfloat *d_fooC;\t \tcudaMalloc(&d_fooC, numBytes);\n\tfloat *d_fooD; \t\tcudaMalloc(&d_fooD, numBytes);\n\tfloat *d_fooE; \t\tcudaMalloc(&d_fooE, numBytes);\n\tfloat *d_barIn; \tcudaMalloc(&d_barIn, numBytes);\n\tcudaMemcpy(d_fooA, fooA, numBytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_fooB, fooB, numBytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_fooC, fooC, numBytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_fooD, fooD, numBytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_fooE, fooE, numBytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_barIn, barIn, numBytes, cudaMemcpyHostToDevice);\t\n\n\t// output arrays for host and device\n\tfloat fooOut[N], barOut[N], *d_fooOut, *d_barOut;\n\tcudaMalloc(&d_fooOut, numBytes);\n\tcudaMalloc(&d_barOut, numBytes);\n\n\t// declare and compute reference solutions\n\tfloat ref_fooOut[N], ref_barOut[N]; \n\tcpuFoo(ref_fooOut, fooA, fooB, fooC, fooD, fooE);\n\tcpuBar(ref_barOut, barIn);\n\n\t// launch and time foo and bar\n\tGpuTimer fooTimer, barTimer;\n\tfooTimer.Start();\n\tfoo<<<N/BLOCKSIZE, BLOCKSIZE>>>(d_fooOut, d_fooA, d_fooB, d_fooC, d_fooD, d_fooE);\n\tfooTimer.Stop();\n\t\n\tbarTimer.Start();\n\tbar<<<N/BLOCKSIZE, BLOCKSIZE>>>(d_barOut, d_barIn);\n\tbarTimer.Stop();\n\n\tcudaMemcpy(fooOut, d_fooOut, numBytes, cudaMemcpyDeviceToHost);\n\tcudaMemcpy(barOut, d_barOut, numBytes, cudaMemcpyDeviceToHost);\n\tprintf(\"foo<<<>>>(): %g ms elapsed. Verifying solution...\", fooTimer.Elapsed());\n\tcompareArrays(ref_fooOut, fooOut, N);\n\tprintf(\"bar<<<>>>(): %g ms elapsed. Verifying solution...\", barTimer.Elapsed());\n\tcompareArrays(ref_barOut, barOut, N);\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include \"gputimer.h\"\n#include \"utils.h\"\n\nconst int BLOCKSIZE\t= 128;\nconst int NUMBLOCKS = 1000;\t\t\t\t\t// set this to 1 or 2 for debugging\nconst int N \t\t= BLOCKSIZE*NUMBLOCKS;\n\n/* \n * TODO: modify the foo and bar kernels to use tiling: \n * \t\t - copy the input data to shared memory\n *\t\t - perform the computation there\n *\t     - copy the result back to global memory\n *\t\t - assume thread blocks of 128 threads\n *\t\t - handle intra-block boundaries correctly\n * You can ignore boundary conditions (we ignore the first 2 and last 2 elements)\n */\n__global__ void foo(float out[], float A[], float B[], float C[], float D[], float E[]){\n\n\tint i = threadIdx.x + blockIdx.x*blockDim.x; \n\t\n\tout[i] = (A[i] + B[i] + C[i] + D[i] + E[i]) / 5.0f;\n}\n\n__global__ void bar(float out[], float in[]) \n{\n\tint i = threadIdx.x + blockIdx.x*blockDim.x; \n\n\tout[i] = (in[i-2] + in[i-1] + in[i] + in[i+1] + in[i+2]) / 5.0f;\n}\n\nvoid cpuFoo(float out[], float A[], float B[], float C[], float D[], float E[])\n{\n\tfor (int i=0; i<N; i++)\n\t{\n\t\tout[i] = (A[i] + B[i] + C[i] + D[i] + E[i]) / 5.0f;\n\t}\n}\n\nvoid cpuBar(float out[], float in[])\n{\n\t// ignore the boundaries\n\tfor (int i=2; i<N-2; i++)\n\t{\n\t\tout[i] = (in[i-2] + in[i-1] + in[i] + in[i+1] + in[i+2]) / 5.0f;\n\t}\n}\n\nint main(int argc, char **argv)\n{\n\t// declare and fill input arrays for foo() and bar()\n\tfloat fooA[N], fooB[N], fooC[N], fooD[N], fooE[N], barIn[N];\n\tfor (int i=0; i<N; i++) \n\t{\n\t\tfooA[i] = i; \n\t\tfooB[i] = i+1;\n\t\tfooC[i] = i+2;\n\t\tfooD[i] = i+3;\n\t\tfooE[i] = i+4;\n\t\tbarIn[i] = 2*i; \n\t}\n\t// device arrays\n\tint numBytes = N * sizeof(float);\n\tfloat *d_fooA;\t \thipMalloc(&d_fooA, numBytes);\n\tfloat *d_fooB; \t\thipMalloc(&d_fooB, numBytes);\n\tfloat *d_fooC;\t \thipMalloc(&d_fooC, numBytes);\n\tfloat *d_fooD; \t\thipMalloc(&d_fooD, numBytes);\n\tfloat *d_fooE; \t\thipMalloc(&d_fooE, numBytes);\n\tfloat *d_barIn; \thipMalloc(&d_barIn, numBytes);\n\thipMemcpy(d_fooA, fooA, numBytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_fooB, fooB, numBytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_fooC, fooC, numBytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_fooD, fooD, numBytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_fooE, fooE, numBytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_barIn, barIn, numBytes, hipMemcpyHostToDevice);\t\n\n\t// output arrays for host and device\n\tfloat fooOut[N], barOut[N], *d_fooOut, *d_barOut;\n\thipMalloc(&d_fooOut, numBytes);\n\thipMalloc(&d_barOut, numBytes);\n\n\t// declare and compute reference solutions\n\tfloat ref_fooOut[N], ref_barOut[N]; \n\tcpuFoo(ref_fooOut, fooA, fooB, fooC, fooD, fooE);\n\tcpuBar(ref_barOut, barIn);\n\n\t// launch and time foo and bar\n\tGpuTimer fooTimer, barTimer;\n\tfooTimer.Start();\n\tfoo<<<N/BLOCKSIZE, BLOCKSIZE>>>(d_fooOut, d_fooA, d_fooB, d_fooC, d_fooD, d_fooE);\n\tfooTimer.Stop();\n\t\n\tbarTimer.Start();\n\tbar<<<N/BLOCKSIZE, BLOCKSIZE>>>(d_barOut, d_barIn);\n\tbarTimer.Stop();\n\n\thipMemcpy(fooOut, d_fooOut, numBytes, hipMemcpyDeviceToHost);\n\thipMemcpy(barOut, d_barOut, numBytes, hipMemcpyDeviceToHost);\n\tprintf(\"foo<<<>>>(): %g ms elapsed. Verifying solution...\", fooTimer.Elapsed());\n\tcompareArrays(ref_fooOut, fooOut, N);\n\tprintf(\"bar<<<>>>(): %g ms elapsed. Verifying solution...\", barTimer.Elapsed());\n\tcompareArrays(ref_barOut, barOut, N);\n}\n", "source": "/kaggle/working/cs344/Lesson Code Snippets/Lesson 7 Code Snippets/tiling/tiling.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Subpart b:\n// Compute capability 2.0+ GPUs have support for 3 per-warp instructions.\n// Namely, these instructions are:\n//\n// int __popc(int x) Population Count: Returns the number of bits that are set\n// to 1 in the 32-bit integer x.\n//\n// int __clz(int x) Count Leading Zeros: Returns the number of consecutive zero\n// bits beginning at the most significant bit of the 32-bit integer x.\n//\n// int __ballot(int p) Returns a 32-bit integer in which bit k is set if and only\n// if the predicate p provided by the thread in lane k of the warp is non-zero.\n\n__device__ unsigned int warp_reduce(unsigned int p, volatile unsigned int * s) {\n    // Assumes values in 'p' are either 1 or 0\n    // Should not use 's'\n    // Sums p across warp, returning the result.\n    // You can do this without using the character '+' in your code at all\n    //\n    // TODO: Fill in the rest of this function\n    //\n}\n\n__global__ void reduce(unsigned int * d_out_warp, \n                       const unsigned int * d_in)\n{\n    extern __shared__ unsigned int s[];\n    int t = threadIdx.x;\n    int p = d_in[t];\n\n    unsigned int wr = warp_reduce(p, s);\n    if (t == 0)\n    {\n        *d_out_warp = wr;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 32;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(unsigned int);\n\n    // generate the input array on the host\n    unsigned int h_in[ARRAY_SIZE];\n    unsigned int sum = 0;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX > 0.5f ? 1 : 0;\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    unsigned int * d_in, * d_out_warp;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_out_warp, sizeof(unsigned int));\n\n    // transfer the input array to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n\n    GpuTimer timer;\n    timer.Start();\n    // launch the kernel\n    reduce<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(unsigned int)>>>\n        (d_out_warp, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());  \n\n    unsigned int h_out_warp;\n    // copy back the sum from GPU\n    cudaMemcpy(&h_out_warp, d_out_warp, sizeof(unsigned int), \n               cudaMemcpyDeviceToHost);\n\n    // compare your result against the expected reduce sum\n    compare(h_out_warp, sum);\n\n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_out_warp);\n        \n}\n\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Subpart b:\n// Compute capability 2.0+ GPUs have support for 3 per-warp instructions.\n// Namely, these instructions are:\n//\n// int __popc(int x) Population Count: Returns the number of bits that are set\n// to 1 in the 32-bit integer x.\n//\n// int __clz(int x) Count Leading Zeros: Returns the number of consecutive zero\n// bits beginning at the most significant bit of the 32-bit integer x.\n//\n// int __ballot(int p) Returns a 32-bit integer in which bit k is set if and only\n// if the predicate p provided by the thread in lane k of the warp is non-zero.\n\n__device__ unsigned int warp_reduce(unsigned int p, volatile unsigned int * s) {\n    // Assumes values in 'p' are either 1 or 0\n    // Should not use 's'\n    // Sums p across warp, returning the result.\n    // You can do this without using the character '+' in your code at all\n    //\n    // TODO: Fill in the rest of this function\n    //\n}\n\n__global__ void reduce(unsigned int * d_out_warp, \n                       const unsigned int * d_in)\n{\n    extern __shared__ unsigned int s[];\n    int t = threadIdx.x;\n    int p = d_in[t];\n\n    unsigned int wr = warp_reduce(p, s);\n    if (t == 0)\n    {\n        *d_out_warp = wr;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 32;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(unsigned int);\n\n    // generate the input array on the host\n    unsigned int h_in[ARRAY_SIZE];\n    unsigned int sum = 0;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX > 0.5f ? 1 : 0;\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    unsigned int * d_in, * d_out_warp;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_out_warp, sizeof(unsigned int));\n\n    // transfer the input array to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n\n    GpuTimer timer;\n    timer.Start();\n    // launch the kernel\n    reduce<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(unsigned int)>>>\n        (d_out_warp, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());  \n\n    unsigned int h_out_warp;\n    // copy back the sum from GPU\n    hipMemcpy(&h_out_warp, d_out_warp, sizeof(unsigned int), \n               hipMemcpyDeviceToHost);\n\n    // compare your result against the expected reduce sum\n    compare(h_out_warp, sum);\n\n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_out_warp);\n        \n}\n\n", "source": "/kaggle/working/cs344/Final/warpreduce/part_b/warpreduce.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Subpart A:\n// Write step 1 as a kernel that operates on threads 0--31.\n// Assume that the input flags are 0 for false and 1 for true and are stored\n// in a local per-thread register called p (for predicate).\n//\n// You have access to 31 words of shared memory s[0:31], with s[0]\n// corresponding to thread 0 and s[31] corresponding to thread 31.\n// You may change the values of s[0:31]. Put the return sum in s[0].\n// Your code should execute no more than 5 warp-wide addition operations.\n\n__device__ unsigned int shared_reduce(unsigned int p, volatile unsigned int * s) {\n    // Assumes values in 'p' are either 1 or 0\n    // Assumes s[0:31] are allocated\n    // Sums p across warp, returning the result. Suggest you put\n    // result in s[0] and return it\n    // You may change any value in s\n    // You should execute no more than 5 + operations (if you're doing\n    // 31, you're doing it wrong)\n    //\n    // TODO: Fill in the rest of this function\n\n    return s[0];\n}\n\n__global__ void reduce(unsigned int * d_out_shared,\n                       const unsigned int * d_in)\n{\n    extern __shared__ unsigned int s[];\n    int t = threadIdx.x;\n    int p = d_in[t];\n    unsigned int sr = shared_reduce(p, s);\n    if (t == 0)\n    {\n        *d_out_shared = sr;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 32;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(unsigned int);\n\n    // generate the input array on the host\n    unsigned int h_in[ARRAY_SIZE];\n    unsigned int sum = 0;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX > 0.5f ? 1 : 0;\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    unsigned int * d_in, * d_out_shared;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_out_shared, sizeof(unsigned int));\n\n    // transfer the input array to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n\n    GpuTimer timer;\n    timer.Start();\n    // launch the kernel\n    reduce<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(unsigned int)>>>\n        (d_out_shared, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n\n    unsigned int h_out_shared;\n    // copy back the sum from GPU\n    cudaMemcpy(&h_out_shared, d_out_shared, sizeof(unsigned int), \n               cudaMemcpyDeviceToHost);\n    \n    compare(h_out_shared, sum);\n\n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_out_shared);\n}\n\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Subpart A:\n// Write step 1 as a kernel that operates on threads 0--31.\n// Assume that the input flags are 0 for false and 1 for true and are stored\n// in a local per-thread register called p (for predicate).\n//\n// You have access to 31 words of shared memory s[0:31], with s[0]\n// corresponding to thread 0 and s[31] corresponding to thread 31.\n// You may change the values of s[0:31]. Put the return sum in s[0].\n// Your code should execute no more than 5 warp-wide addition operations.\n\n__device__ unsigned int shared_reduce(unsigned int p, volatile unsigned int * s) {\n    // Assumes values in 'p' are either 1 or 0\n    // Assumes s[0:31] are allocated\n    // Sums p across warp, returning the result. Suggest you put\n    // result in s[0] and return it\n    // You may change any value in s\n    // You should execute no more than 5 + operations (if you're doing\n    // 31, you're doing it wrong)\n    //\n    // TODO: Fill in the rest of this function\n\n    return s[0];\n}\n\n__global__ void reduce(unsigned int * d_out_shared,\n                       const unsigned int * d_in)\n{\n    extern __shared__ unsigned int s[];\n    int t = threadIdx.x;\n    int p = d_in[t];\n    unsigned int sr = shared_reduce(p, s);\n    if (t == 0)\n    {\n        *d_out_shared = sr;\n    }\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 32;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(unsigned int);\n\n    // generate the input array on the host\n    unsigned int h_in[ARRAY_SIZE];\n    unsigned int sum = 0;\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX > 0.5f ? 1 : 0;\n        sum += h_in[i];\n    }\n\n    // declare GPU memory pointers\n    unsigned int * d_in, * d_out_shared;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_out_shared, sizeof(unsigned int));\n\n    // transfer the input array to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n\n    GpuTimer timer;\n    timer.Start();\n    // launch the kernel\n    reduce<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(unsigned int)>>>\n        (d_out_shared, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n\n    unsigned int h_out_shared;\n    // copy back the sum from GPU\n    hipMemcpy(&h_out_shared, d_out_shared, sizeof(unsigned int), \n               hipMemcpyDeviceToHost);\n    \n    compare(h_out_shared, sum);\n\n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_out_shared);\n}\n\n", "source": "/kaggle/working/cs344/Final/warpreduce/part_a/warpreduce.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Reference\n__global__ void smooth(float * v_new, const float * v) {\n    int myIdx = threadIdx.x * gridDim.x + blockIdx.x;\n    int numThreads = blockDim.x * gridDim.x;\n    int myLeftIdx = (myIdx == 0) ? 0 : myIdx - 1;\n    int myRightIdx = (myIdx == (numThreads - 1)) ? numThreads - 1 : myIdx + 1;\n    float myElt = v[myIdx];\n    float myLeftElt = v[myLeftIdx];\n    float myRightElt = v[myRightIdx];\n    v_new[myIdx] = 0.25f * myLeftElt + 0.5f * myElt + 0.25f * myRightElt;\n}\n\n// Your code\n__global__ void smooth_shared(float * v_new, const float * v) {\n    extern __shared__ float s[];\n    // TODO: Fill in the rest of this function\n    return v[0];\n}\n\nint main(int argc, char **argv)\n{\n\n    const int ARRAY_SIZE = 4096;\n    const int BLOCK_SIZE = 256;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float h_cmp[ARRAY_SIZE];\n    float h_out[ARRAY_SIZE];\n    float h_out_shared[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX;\n    }\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        h_cmp[i] = (0.25f * h_in[(i == 0) ? 0 : i-1] +\n                    0.50f * h_in[i] +\n                    0.25f * h_in[(i == (ARRAY_SIZE - 1)) ? ARRAY_SIZE - 1 : i+1]);\n    }\n\n    // declare GPU memory pointers\n    float * d_in, * d_out, * d_out_shared;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_out, ARRAY_BYTES);\n    cudaMalloc((void **) &d_out_shared, ARRAY_BYTES);\n\n    // transfer the input array to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n\n    // cudaEvent_t start, stop;\n    // cudaEventCreate(&start);\n    // cudaEventCreate(&stop);\n    // launch the kernel\n    smooth<<<ARRAY_SIZE / BLOCK_SIZE, BLOCK_SIZE>>>(d_out, d_in);\n    GpuTimer timer;\n    timer.Start();\n    smooth_shared<<<ARRAY_SIZE / BLOCK_SIZE, BLOCK_SIZE, (BLOCK_SIZE + 2) * sizeof(float)>>>(d_out_shared, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n    // cudaEventSynchronize(stop);\n    // float elapsedTime;\n    // cudaEventElapsedTime(&elapsedTime, start, stop);    \n\n    // copy back the result from GPU\n    cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);\n    cudaMemcpy(h_out_shared, d_out_shared, ARRAY_BYTES, cudaMemcpyDeviceToHost);\n\n    // testing for correctness\n    compare(h_in, h_out, h_out_shared, h_cmp, ARRAY_SIZE);\n\n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_out);\n    cudaFree(d_out_shared);\n        \n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// Reference\n__global__ void smooth(float * v_new, const float * v) {\n    int myIdx = threadIdx.x * gridDim.x + blockIdx.x;\n    int numThreads = blockDim.x * gridDim.x;\n    int myLeftIdx = (myIdx == 0) ? 0 : myIdx - 1;\n    int myRightIdx = (myIdx == (numThreads - 1)) ? numThreads - 1 : myIdx + 1;\n    float myElt = v[myIdx];\n    float myLeftElt = v[myLeftIdx];\n    float myRightElt = v[myRightIdx];\n    v_new[myIdx] = 0.25f * myLeftElt + 0.5f * myElt + 0.25f * myRightElt;\n}\n\n// Your code\n__global__ void smooth_shared(float * v_new, const float * v) {\n    extern __shared__ float s[];\n    // TODO: Fill in the rest of this function\n    return v[0];\n}\n\nint main(int argc, char **argv)\n{\n\n    const int ARRAY_SIZE = 4096;\n    const int BLOCK_SIZE = 256;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float h_cmp[ARRAY_SIZE];\n    float h_out[ARRAY_SIZE];\n    float h_out_shared[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX;\n    }\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        h_cmp[i] = (0.25f * h_in[(i == 0) ? 0 : i-1] +\n                    0.50f * h_in[i] +\n                    0.25f * h_in[(i == (ARRAY_SIZE - 1)) ? ARRAY_SIZE - 1 : i+1]);\n    }\n\n    // declare GPU memory pointers\n    float * d_in, * d_out, * d_out_shared;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_out, ARRAY_BYTES);\n    hipMalloc((void **) &d_out_shared, ARRAY_BYTES);\n\n    // transfer the input array to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n\n    // cudaEvent_t start, stop;\n    // cudaEventCreate(&start);\n    // cudaEventCreate(&stop);\n    // launch the kernel\n    smooth<<<ARRAY_SIZE / BLOCK_SIZE, BLOCK_SIZE>>>(d_out, d_in);\n    GpuTimer timer;\n    timer.Start();\n    smooth_shared<<<ARRAY_SIZE / BLOCK_SIZE, BLOCK_SIZE, (BLOCK_SIZE + 2) * sizeof(float)>>>(d_out_shared, d_in);\n    timer.Stop();\n\n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n    // cudaEventSynchronize(stop);\n    // float elapsedTime;\n    // cudaEventElapsedTime(&elapsedTime, start, stop);    \n\n    // copy back the result from GPU\n    hipMemcpy(h_out, d_out, ARRAY_BYTES, hipMemcpyDeviceToHost);\n    hipMemcpy(h_out_shared, d_out_shared, ARRAY_BYTES, hipMemcpyDeviceToHost);\n\n    // testing for correctness\n    compare(h_in, h_out, h_out_shared, h_cmp, ARRAY_SIZE);\n\n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_out);\n    hipFree(d_out_shared);\n        \n    return 0;\n}\n", "source": "/kaggle/working/cs344/Final/smooth/smooth.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// http://en.wikipedia.org/wiki/Bitonic_sort\n__global__ void batcherBitonicMergesort64(float * d_out, const float * d_in)\n{\n    // you are guaranteed this is called with <<<1, 64, 64*4>>>\n    extern __shared__ float sdata[];\n    int tid  = threadIdx.x;\n    sdata[tid] = d_in[tid];\n    __syncthreads();\n    \n    for (int stage = 0; stage <= 5; stage++)\n    {\n        for (int substage = stage; substage >= 0; substage--)\n        {\n            // TODO\n        }\n    }\n\n    d_out[tid] = sdata[tid];\n}\n\nint compareFloat (const void * a, const void * b)\n{\n  if ( *(float*)a <  *(float*)b ) return -1;\n  if ( *(float*)a == *(float*)b ) return 0;\n  if ( *(float*)a >  *(float*)b ) return 1;\n  return 0;                     // should never reach this\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 64;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float h_sorted[ARRAY_SIZE];\n    float h_out[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX;\n        h_sorted[i] = h_in[i];\n    }\n    qsort(h_sorted, ARRAY_SIZE, sizeof(float), compareFloat);\n\n    // declare GPU memory pointers\n    float * d_in, * d_out;\n\n    // allocate GPU memory\n    cudaMalloc((void **) &d_in, ARRAY_BYTES);\n    cudaMalloc((void **) &d_out, ARRAY_BYTES);\n\n    // transfer the input array to the GPU\n    cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice); \n\n    // launch the kernel\n    GpuTimer timer;\n    timer.Start();\n    batcherBitonicMergesort64<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(float)>>>(d_out, d_in);\n    timer.Stop();\n    \n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n    \n    // copy back the sum from GPU\n    cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);\n\n    compare(h_out, h_sorted, ARRAY_SIZE);\n  \n    // free GPU memory allocation\n    cudaFree(d_in);\n    cudaFree(d_out);\n        \n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include \"compare.h\"\n#include \"gputimer.h\"\n\n// http://en.wikipedia.org/wiki/Bitonic_sort\n__global__ void batcherBitonicMergesort64(float * d_out, const float * d_in)\n{\n    // you are guaranteed this is called with <<<1, 64, 64*4>>>\n    extern __shared__ float sdata[];\n    int tid  = threadIdx.x;\n    sdata[tid] = d_in[tid];\n    __syncthreads();\n    \n    for (int stage = 0; stage <= 5; stage++)\n    {\n        for (int substage = stage; substage >= 0; substage--)\n        {\n            // TODO\n        }\n    }\n\n    d_out[tid] = sdata[tid];\n}\n\nint compareFloat (const void * a, const void * b)\n{\n  if ( *(float*)a <  *(float*)b ) return -1;\n  if ( *(float*)a == *(float*)b ) return 0;\n  if ( *(float*)a >  *(float*)b ) return 1;\n  return 0;                     // should never reach this\n}\n\nint main(int argc, char **argv)\n{\n    const int ARRAY_SIZE = 64;\n    const int ARRAY_BYTES = ARRAY_SIZE * sizeof(float);\n\n    // generate the input array on the host\n    float h_in[ARRAY_SIZE];\n    float h_sorted[ARRAY_SIZE];\n    float h_out[ARRAY_SIZE];\n    for(int i = 0; i < ARRAY_SIZE; i++) {\n        // generate random float in [0, 1]\n        h_in[i] = (float)random()/(float)RAND_MAX;\n        h_sorted[i] = h_in[i];\n    }\n    qsort(h_sorted, ARRAY_SIZE, sizeof(float), compareFloat);\n\n    // declare GPU memory pointers\n    float * d_in, * d_out;\n\n    // allocate GPU memory\n    hipMalloc((void **) &d_in, ARRAY_BYTES);\n    hipMalloc((void **) &d_out, ARRAY_BYTES);\n\n    // transfer the input array to the GPU\n    hipMemcpy(d_in, h_in, ARRAY_BYTES, hipMemcpyHostToDevice); \n\n    // launch the kernel\n    GpuTimer timer;\n    timer.Start();\n    batcherBitonicMergesort64<<<1, ARRAY_SIZE, ARRAY_SIZE * sizeof(float)>>>(d_out, d_in);\n    timer.Stop();\n    \n    printf(\"Your code executed in %g ms\\n\", timer.Elapsed());\n    \n    // copy back the sum from GPU\n    hipMemcpy(h_out, d_out, ARRAY_BYTES, hipMemcpyDeviceToHost);\n\n    compare(h_out, h_sorted, ARRAY_SIZE);\n  \n    // free GPU memory allocation\n    hipFree(d_in);\n    hipFree(d_out);\n        \n    return 0;\n}\n", "source": "/kaggle/working/cs344/Final/batcher/batcher.cu"}
{"input": "#include \"utils.h\"\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <string>\n#include <iostream>\n\n#include <thrust/device_vector.h>\n#include <thrust/copy.h>\n#include <thrust/transform.h>\n#include <thrust/functional.h>\n#include <thrust/reduce.h>\n#include <thrust/extrema.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/sequence.h>\n\n#include \"loadSaveImage.h\"\n#include <stdio.h>\n\n//simple cross correlation kernel copied from Mike's IPython Notebook\n__global__ void naive_normalized_cross_correlation(\n    float*         d_response,\n    unsigned char* d_original,\n    unsigned char* d_template,\n    int            num_pixels_y,\n    int            num_pixels_x,\n    int            template_half_height,\n    int            template_height,\n    int            template_half_width,\n    int            template_width,\n    int            template_size,\n    float          template_mean\n    )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int  knx            = template_width;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    //\n    // compute image mean\n    //\n    float image_sum = 0.0f;\n\n    for ( int y = -template_half_height; y <= template_half_height; y++ )\n    {\n      for ( int x = -template_half_width; x <= template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( image_index_2d.x + x, image_index_2d.y + y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char image_offset_value = d_original[ image_offset_index_1d_clamped ];\n\n        image_sum += (float)image_offset_value;\n      }\n    }\n\n    float image_mean = image_sum / (float)template_size;\n\n    //\n    // compute sums\n    //\n    float sum_of_image_template_diff_products = 0.0f;\n    float sum_of_squared_image_diffs          = 0.0f;\n    float sum_of_squared_template_diffs       = 0.0f;\n\n    for ( int y = -template_half_height; y <= template_half_height; y++ )\n    {\n      for ( int x = -template_half_width; x <= template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( image_index_2d.x + x, image_index_2d.y + y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char image_offset_value = d_original[ image_offset_index_1d_clamped ];\n        float         image_diff         = (float)image_offset_value - image_mean;\n\n        int2 template_index_2d = make_int2( x + template_half_width, y + template_half_height );\n        int  template_index_1d = ( knx * template_index_2d.y ) + template_index_2d.x;\n\n        unsigned char template_value = d_template[ template_index_1d ];\n        float         template_diff  = template_value - template_mean;\n\n        float image_template_diff_product = image_offset_value   * template_diff;\n        float squared_image_diff          = image_diff           * image_diff;\n        float squared_template_diff       = template_diff        * template_diff;\n\n        sum_of_image_template_diff_products += image_template_diff_product;\n        sum_of_squared_image_diffs          += squared_image_diff;\n        sum_of_squared_template_diffs       += squared_template_diff;\n      }\n    }\n\n\n    //\n    // compute final result\n    //\n    float result_value = 0.0f;\n\n    if ( sum_of_squared_image_diffs != 0 && sum_of_squared_template_diffs != 0 )\n    {\n      result_value = sum_of_image_template_diff_products / sqrt( sum_of_squared_image_diffs * sum_of_squared_template_diffs );\n    }\n\n    d_response[ image_index_1d ] = result_value;\n  }\n}\n\n\n__global__ void remove_redness_from_coordinates(\n    const unsigned int*  d_coordinates,\n    unsigned char* d_r,\n    unsigned char* d_b,\n    unsigned char* d_g,\n    unsigned char* d_r_output,\n    int    num_coordinates,\n    int    num_pixels_y,\n    int    num_pixels_x,\n    int    template_half_height,\n    int    template_half_width\n    )\n{\n  int ny              = num_pixels_y;\n  int nx              = num_pixels_x;\n  int global_index_1d = ( blockIdx.x * blockDim.x ) + threadIdx.x;\n\n  int imgSize = num_pixels_x * num_pixels_y;\n\n  if ( global_index_1d < num_coordinates )\n  {\n    unsigned int image_index_1d = d_coordinates[ imgSize - global_index_1d - 1 ];\n    ushort2 image_index_2d = make_ushort2(image_index_1d % num_pixels_x, image_index_1d / num_pixels_x);\n\n    for ( int y = image_index_2d.y - template_half_height; y <= image_index_2d.y + template_half_height; y++ )\n    {\n      for ( int x = image_index_2d.x - template_half_width; x <= image_index_2d.x + template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( x, y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char g_value = d_g[ image_offset_index_1d_clamped ];\n        unsigned char b_value = d_b[ image_offset_index_1d_clamped ];\n\n        unsigned int gb_average = ( g_value + b_value ) / 2;\n\n        d_r_output[ image_offset_index_1d_clamped ] = (unsigned char)gb_average;\n      }\n    }\n\n  }\n}\n\n\n\n\nstruct splitChannels : thrust::unary_function<uchar4, thrust::tuple<unsigned char, unsigned char, unsigned char> >{\n  __host__ __device__\n  thrust::tuple<unsigned char, unsigned char, unsigned char> operator()(uchar4 pixel) {\n    return thrust::make_tuple(pixel.x, pixel.y, pixel.z);\n  }\n};\n\nstruct combineChannels : thrust::unary_function<thrust::tuple<unsigned char, unsigned char, unsigned char>, uchar4> {\n  __host__ __device__\n  uchar4 operator()(thrust::tuple<unsigned char, unsigned char, unsigned char> t) {\n    return make_uchar4(thrust::get<0>(t), thrust::get<1>(t), thrust::get<2>(t), 255);\n  }\n};\n\nstruct combineResponses : thrust::unary_function<float, thrust::tuple<float, float, float> > {\n  __host__ __device__\n  float operator()(thrust::tuple<float, float, float> t) {\n    return thrust::get<0>(t) * thrust::get<1>(t) * thrust::get<2>(t);\n  }\n};\n\n//we need to save the input so we can remove the redeye for the output\nstatic thrust::device_vector<unsigned char> d_red;\nstatic thrust::device_vector<unsigned char> d_blue;\nstatic thrust::device_vector<unsigned char> d_green;\n\nstatic size_t numRowsImg;\nstatic size_t numColsImg;\nstatic size_t templateHalfWidth;\nstatic size_t templateHalfHeight;\n\n//return types are void since any internal error will be handled by quitting\n//no point in returning error codes...\nvoid preProcess(unsigned int **inputVals,\n                unsigned int **inputPos,\n                unsigned int **outputVals,\n                unsigned int **outputPos,\n                size_t &numElem,\n                const std::string& filename,\n\t\t\t\tconst std::string& templateFilename) {\n  //make sure the context initializes ok\n  checkCudaErrors(cudaFree(0));\n\n  uchar4 *inImg;\n  uchar4 *eyeTemplate;\n\n  size_t numRowsTemplate, numColsTemplate;\n\n  loadImageRGBA(filename, &inImg, &numRowsImg, &numColsImg);\n  loadImageRGBA(templateFilename, &eyeTemplate, &numRowsTemplate, &numColsTemplate);\n\n  templateHalfWidth = (numColsTemplate - 1) / 2;\n  templateHalfHeight = (numRowsTemplate - 1) / 2;\n\n  //we need to split each image into its separate channels\n  //use thrust to demonstrate basic uses\n\n  numElem = numRowsImg * numColsImg;\n  size_t templateSize = numRowsTemplate * numColsTemplate;\n\n  thrust::device_vector<uchar4> d_Img(inImg, inImg + numRowsImg * numColsImg);\n  thrust::device_vector<uchar4> d_Template(eyeTemplate, eyeTemplate + numRowsTemplate * numColsTemplate);\n\n  d_red.  resize(numElem);\n  d_blue. resize(numElem);\n  d_green.resize(numElem);\n\n  thrust::device_vector<unsigned char> d_red_template(templateSize);\n  thrust::device_vector<unsigned char> d_blue_template(templateSize);\n  thrust::device_vector<unsigned char> d_green_template(templateSize);\n\n  //split the image\n  thrust::transform(d_Img.begin(), d_Img.end(), thrust::make_zip_iterator(\n                                                  thrust::make_tuple(d_red.begin(),\n                                                                     d_blue.begin(),\n                                                                     d_green.begin())),\n                                                splitChannels());\n\n  //split the template\n  thrust::transform(d_Template.begin(), d_Template.end(), \n                    thrust::make_zip_iterator(thrust::make_tuple(d_red_template.begin(),\n                                                                 d_blue_template.begin(),\n                                                                 d_green_template.begin())),\n                                                splitChannels());\n\n  \n  thrust::device_vector<float> d_red_response(numElem);\n  thrust::device_vector<float> d_blue_response(numElem);\n  thrust::device_vector<float> d_green_response(numElem);\n\n  //need to compute the mean for each template channel\n  unsigned int r_sum = thrust::reduce(d_red_template.begin(), d_red_template.end(), 0);\n  unsigned int b_sum = thrust::reduce(d_blue_template.begin(), d_blue_template.end(), 0);\n  unsigned int g_sum = thrust::reduce(d_green_template.begin(), d_green_template.end(), 0);\n\n  float r_mean = (double)r_sum / templateSize;\n  float b_mean = (double)b_sum / templateSize;\n  float g_mean = (double)g_sum / templateSize;\n\n  const dim3 blockSize(32, 8, 1);\n  const dim3 gridSize( (numColsImg + blockSize.x - 1) / blockSize.x, (numRowsImg + blockSize.y - 1) / blockSize.y, 1);\n\n  //now compute the cross-correlations for each channel\n\n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_red_response.data()),\n                                                              thrust::raw_pointer_cast(d_red.data()),\n                                                              thrust::raw_pointer_cast(d_red_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, r_mean);\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n                                                             \n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_blue_response.data()),\n                                                              thrust::raw_pointer_cast(d_blue.data()),\n                                                              thrust::raw_pointer_cast(d_blue_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, b_mean);\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_green_response.data()),\n                                                              thrust::raw_pointer_cast(d_green.data()),\n                                                              thrust::raw_pointer_cast(d_green_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, g_mean);\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  //generate combined response - multiply all channels together\n\n\n  thrust::device_vector<float> d_combined_response(numElem);\n\n  thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(\n                        d_red_response.begin(),\n                        d_blue_response.begin(),\n                        d_green_response.begin())),\n                    thrust::make_zip_iterator(thrust::make_tuple(\n                        d_red_response.end(),\n                        d_blue_response.end(),\n                        d_green_response.end())),\n                    d_combined_response.begin(),\n                    combineResponses());\n\n  //find max/min of response\n\n  typedef thrust::device_vector<float>::iterator floatIt;\n  thrust::pair<floatIt, floatIt> minmax = thrust::minmax_element(d_combined_response.begin(), d_combined_response.end());\n\n  float bias = *minmax.first;\n\n  //we need to make all the numbers positive so that the students can sort them without any bit twiddling\n  thrust::transform(d_combined_response.begin(), d_combined_response.end(), thrust::make_constant_iterator(-bias), \n                    d_combined_response.begin(), thrust::plus<float>());\n\n  //now we need to create the 1-D coordinates that will be attached to the keys\n  thrust::device_vector<unsigned int> coords(numElem);\n  thrust::sequence(coords.begin(), coords.end()); //[0, ..., numElem - 1]\n\n  //allocate memory for output and copy since our device vectors will go out of scope\n  //and be deleted\n  checkCudaErrors(cudaMalloc(inputVals,  sizeof(unsigned int) * numElem));\n  checkCudaErrors(cudaMalloc(inputPos,   sizeof(unsigned int) * numElem));\n  checkCudaErrors(cudaMalloc(outputVals, sizeof(unsigned int) * numElem));\n  checkCudaErrors(cudaMalloc(outputPos,  sizeof(unsigned int) * numElem));\n\n  cudaMemcpy(*inputVals, thrust::raw_pointer_cast(d_combined_response.data()), sizeof(unsigned int) * numElem, cudaMemcpyDeviceToDevice);\n  cudaMemcpy(*inputPos,  thrust::raw_pointer_cast(coords.data()), sizeof(unsigned int) * numElem, cudaMemcpyDeviceToDevice);\n  checkCudaErrors(cudaMemset(*outputVals, 0, sizeof(unsigned int) * numElem));\n  checkCudaErrors(cudaMemset(*outputPos, 0,  sizeof(unsigned int) * numElem));\n}\n\nvoid postProcess(const unsigned int* const outputVals,\n                 const unsigned int* const outputPos,\n                 const size_t numElems,\n                 const std::string& output_file){\n\n  thrust::device_vector<unsigned char> d_output_red = d_red;\n\n  const dim3 blockSize(256, 1, 1);\n  const dim3 gridSize( (40 + blockSize.x - 1) / blockSize.x, 1, 1);\n\n  remove_redness_from_coordinates<<<gridSize, blockSize>>>(outputPos,\n                                                           thrust::raw_pointer_cast(d_red.data()),\n                                                           thrust::raw_pointer_cast(d_blue.data()),\n                                                           thrust::raw_pointer_cast(d_green.data()),\n                                                           thrust::raw_pointer_cast(d_output_red.data()),\n                                                           40,\n                                                           numRowsImg, numColsImg,\n                                                           9, 9);\n\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  //combine the new red channel with original blue and green for output\n  thrust::device_vector<uchar4> d_outputImg(numElems);\n\n  thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(\n                          d_output_red.begin(),\n                          d_blue.begin(),\n                          d_green.begin())),\n                    thrust::make_zip_iterator(thrust::make_tuple(\n                          d_output_red.end(),\n                          d_blue.end(),\n                          d_green.end())),\n                    d_outputImg.begin(),\n                    combineChannels());\n\n  thrust::host_vector<uchar4> h_Img = d_outputImg;\n\n  saveImageRGBA(&h_Img[0], numRowsImg, numColsImg, output_file);\n\n  //Clear the global vectors otherwise something goes wrong trying to free them\n  d_red.clear(); d_red.shrink_to_fit();\n  d_blue.clear(); d_blue.shrink_to_fit();\n  d_green.clear(); d_green.shrink_to_fit();\n}\n\n", "output": "#include \"utils.h\"\n#include <hip/hip_runtime.h>\n#include <string>\n#include <iostream>\n\n#include <thrust/device_vector.h>\n#include <thrust/copy.h>\n#include <thrust/transform.h>\n#include <thrust/functional.h>\n#include <thrust/reduce.h>\n#include <thrust/extrema.h>\n#include <thrust/iterator/constant_iterator.h>\n#include <thrust/sequence.h>\n\n#include \"loadSaveImage.h\"\n#include <stdio.h>\n\n//simple cross correlation kernel copied from Mike's IPython Notebook\n__global__ void naive_normalized_cross_correlation(\n    float*         d_response,\n    unsigned char* d_original,\n    unsigned char* d_template,\n    int            num_pixels_y,\n    int            num_pixels_x,\n    int            template_half_height,\n    int            template_height,\n    int            template_half_width,\n    int            template_width,\n    int            template_size,\n    float          template_mean\n    )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int  knx            = template_width;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    //\n    // compute image mean\n    //\n    float image_sum = 0.0f;\n\n    for ( int y = -template_half_height; y <= template_half_height; y++ )\n    {\n      for ( int x = -template_half_width; x <= template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( image_index_2d.x + x, image_index_2d.y + y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char image_offset_value = d_original[ image_offset_index_1d_clamped ];\n\n        image_sum += (float)image_offset_value;\n      }\n    }\n\n    float image_mean = image_sum / (float)template_size;\n\n    //\n    // compute sums\n    //\n    float sum_of_image_template_diff_products = 0.0f;\n    float sum_of_squared_image_diffs          = 0.0f;\n    float sum_of_squared_template_diffs       = 0.0f;\n\n    for ( int y = -template_half_height; y <= template_half_height; y++ )\n    {\n      for ( int x = -template_half_width; x <= template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( image_index_2d.x + x, image_index_2d.y + y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char image_offset_value = d_original[ image_offset_index_1d_clamped ];\n        float         image_diff         = (float)image_offset_value - image_mean;\n\n        int2 template_index_2d = make_int2( x + template_half_width, y + template_half_height );\n        int  template_index_1d = ( knx * template_index_2d.y ) + template_index_2d.x;\n\n        unsigned char template_value = d_template[ template_index_1d ];\n        float         template_diff  = template_value - template_mean;\n\n        float image_template_diff_product = image_offset_value   * template_diff;\n        float squared_image_diff          = image_diff           * image_diff;\n        float squared_template_diff       = template_diff        * template_diff;\n\n        sum_of_image_template_diff_products += image_template_diff_product;\n        sum_of_squared_image_diffs          += squared_image_diff;\n        sum_of_squared_template_diffs       += squared_template_diff;\n      }\n    }\n\n\n    //\n    // compute final result\n    //\n    float result_value = 0.0f;\n\n    if ( sum_of_squared_image_diffs != 0 && sum_of_squared_template_diffs != 0 )\n    {\n      result_value = sum_of_image_template_diff_products / sqrt( sum_of_squared_image_diffs * sum_of_squared_template_diffs );\n    }\n\n    d_response[ image_index_1d ] = result_value;\n  }\n}\n\n\n__global__ void remove_redness_from_coordinates(\n    const unsigned int*  d_coordinates,\n    unsigned char* d_r,\n    unsigned char* d_b,\n    unsigned char* d_g,\n    unsigned char* d_r_output,\n    int    num_coordinates,\n    int    num_pixels_y,\n    int    num_pixels_x,\n    int    template_half_height,\n    int    template_half_width\n    )\n{\n  int ny              = num_pixels_y;\n  int nx              = num_pixels_x;\n  int global_index_1d = ( blockIdx.x * blockDim.x ) + threadIdx.x;\n\n  int imgSize = num_pixels_x * num_pixels_y;\n\n  if ( global_index_1d < num_coordinates )\n  {\n    unsigned int image_index_1d = d_coordinates[ imgSize - global_index_1d - 1 ];\n    ushort2 image_index_2d = make_ushort2(image_index_1d % num_pixels_x, image_index_1d / num_pixels_x);\n\n    for ( int y = image_index_2d.y - template_half_height; y <= image_index_2d.y + template_half_height; y++ )\n    {\n      for ( int x = image_index_2d.x - template_half_width; x <= image_index_2d.x + template_half_width; x++ )\n      {\n        int2 image_offset_index_2d         = make_int2( x, y );\n        int2 image_offset_index_2d_clamped = make_int2( min( nx - 1, max( 0, image_offset_index_2d.x ) ), min( ny - 1, max( 0, image_offset_index_2d.y ) ) );\n        int  image_offset_index_1d_clamped = ( nx * image_offset_index_2d_clamped.y ) + image_offset_index_2d_clamped.x;\n\n        unsigned char g_value = d_g[ image_offset_index_1d_clamped ];\n        unsigned char b_value = d_b[ image_offset_index_1d_clamped ];\n\n        unsigned int gb_average = ( g_value + b_value ) / 2;\n\n        d_r_output[ image_offset_index_1d_clamped ] = (unsigned char)gb_average;\n      }\n    }\n\n  }\n}\n\n\n\n\nstruct splitChannels : thrust::unary_function<uchar4, thrust::tuple<unsigned char, unsigned char, unsigned char> >{\n  __host__ __device__\n  thrust::tuple<unsigned char, unsigned char, unsigned char> operator()(uchar4 pixel) {\n    return thrust::make_tuple(pixel.x, pixel.y, pixel.z);\n  }\n};\n\nstruct combineChannels : thrust::unary_function<thrust::tuple<unsigned char, unsigned char, unsigned char>, uchar4> {\n  __host__ __device__\n  uchar4 operator()(thrust::tuple<unsigned char, unsigned char, unsigned char> t) {\n    return make_uchar4(thrust::get<0>(t), thrust::get<1>(t), thrust::get<2>(t), 255);\n  }\n};\n\nstruct combineResponses : thrust::unary_function<float, thrust::tuple<float, float, float> > {\n  __host__ __device__\n  float operator()(thrust::tuple<float, float, float> t) {\n    return thrust::get<0>(t) * thrust::get<1>(t) * thrust::get<2>(t);\n  }\n};\n\n//we need to save the input so we can remove the redeye for the output\nstatic thrust::device_vector<unsigned char> d_red;\nstatic thrust::device_vector<unsigned char> d_blue;\nstatic thrust::device_vector<unsigned char> d_green;\n\nstatic size_t numRowsImg;\nstatic size_t numColsImg;\nstatic size_t templateHalfWidth;\nstatic size_t templateHalfHeight;\n\n//return types are void since any internal error will be handled by quitting\n//no point in returning error codes...\nvoid preProcess(unsigned int **inputVals,\n                unsigned int **inputPos,\n                unsigned int **outputVals,\n                unsigned int **outputPos,\n                size_t &numElem,\n                const std::string& filename,\n\t\t\t\tconst std::string& templateFilename) {\n  //make sure the context initializes ok\n  checkCudaErrors(hipFree(0));\n\n  uchar4 *inImg;\n  uchar4 *eyeTemplate;\n\n  size_t numRowsTemplate, numColsTemplate;\n\n  loadImageRGBA(filename, &inImg, &numRowsImg, &numColsImg);\n  loadImageRGBA(templateFilename, &eyeTemplate, &numRowsTemplate, &numColsTemplate);\n\n  templateHalfWidth = (numColsTemplate - 1) / 2;\n  templateHalfHeight = (numRowsTemplate - 1) / 2;\n\n  //we need to split each image into its separate channels\n  //use thrust to demonstrate basic uses\n\n  numElem = numRowsImg * numColsImg;\n  size_t templateSize = numRowsTemplate * numColsTemplate;\n\n  thrust::device_vector<uchar4> d_Img(inImg, inImg + numRowsImg * numColsImg);\n  thrust::device_vector<uchar4> d_Template(eyeTemplate, eyeTemplate + numRowsTemplate * numColsTemplate);\n\n  d_red.  resize(numElem);\n  d_blue. resize(numElem);\n  d_green.resize(numElem);\n\n  thrust::device_vector<unsigned char> d_red_template(templateSize);\n  thrust::device_vector<unsigned char> d_blue_template(templateSize);\n  thrust::device_vector<unsigned char> d_green_template(templateSize);\n\n  //split the image\n  thrust::transform(d_Img.begin(), d_Img.end(), thrust::make_zip_iterator(\n                                                  thrust::make_tuple(d_red.begin(),\n                                                                     d_blue.begin(),\n                                                                     d_green.begin())),\n                                                splitChannels());\n\n  //split the template\n  thrust::transform(d_Template.begin(), d_Template.end(), \n                    thrust::make_zip_iterator(thrust::make_tuple(d_red_template.begin(),\n                                                                 d_blue_template.begin(),\n                                                                 d_green_template.begin())),\n                                                splitChannels());\n\n  \n  thrust::device_vector<float> d_red_response(numElem);\n  thrust::device_vector<float> d_blue_response(numElem);\n  thrust::device_vector<float> d_green_response(numElem);\n\n  //need to compute the mean for each template channel\n  unsigned int r_sum = thrust::reduce(d_red_template.begin(), d_red_template.end(), 0);\n  unsigned int b_sum = thrust::reduce(d_blue_template.begin(), d_blue_template.end(), 0);\n  unsigned int g_sum = thrust::reduce(d_green_template.begin(), d_green_template.end(), 0);\n\n  float r_mean = (double)r_sum / templateSize;\n  float b_mean = (double)b_sum / templateSize;\n  float g_mean = (double)g_sum / templateSize;\n\n  const dim3 blockSize(32, 8, 1);\n  const dim3 gridSize( (numColsImg + blockSize.x - 1) / blockSize.x, (numRowsImg + blockSize.y - 1) / blockSize.y, 1);\n\n  //now compute the cross-correlations for each channel\n\n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_red_response.data()),\n                                                              thrust::raw_pointer_cast(d_red.data()),\n                                                              thrust::raw_pointer_cast(d_red_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, r_mean);\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n                                                             \n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_blue_response.data()),\n                                                              thrust::raw_pointer_cast(d_blue.data()),\n                                                              thrust::raw_pointer_cast(d_blue_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, b_mean);\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  naive_normalized_cross_correlation<<<gridSize, blockSize>>>(thrust::raw_pointer_cast(d_green_response.data()),\n                                                              thrust::raw_pointer_cast(d_green.data()),\n                                                              thrust::raw_pointer_cast(d_green_template.data()),\n                                                              numRowsImg, numColsImg,\n                                                              templateHalfHeight, numRowsTemplate,\n                                                              templateHalfWidth, numColsTemplate,\n                                                              numRowsTemplate * numColsTemplate, g_mean);\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  //generate combined response - multiply all channels together\n\n\n  thrust::device_vector<float> d_combined_response(numElem);\n\n  thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(\n                        d_red_response.begin(),\n                        d_blue_response.begin(),\n                        d_green_response.begin())),\n                    thrust::make_zip_iterator(thrust::make_tuple(\n                        d_red_response.end(),\n                        d_blue_response.end(),\n                        d_green_response.end())),\n                    d_combined_response.begin(),\n                    combineResponses());\n\n  //find max/min of response\n\n  typedef thrust::device_vector<float>::iterator floatIt;\n  thrust::pair<floatIt, floatIt> minmax = thrust::minmax_element(d_combined_response.begin(), d_combined_response.end());\n\n  float bias = *minmax.first;\n\n  //we need to make all the numbers positive so that the students can sort them without any bit twiddling\n  thrust::transform(d_combined_response.begin(), d_combined_response.end(), thrust::make_constant_iterator(-bias), \n                    d_combined_response.begin(), thrust::plus<float>());\n\n  //now we need to create the 1-D coordinates that will be attached to the keys\n  thrust::device_vector<unsigned int> coords(numElem);\n  thrust::sequence(coords.begin(), coords.end()); //[0, ..., numElem - 1]\n\n  //allocate memory for output and copy since our device vectors will go out of scope\n  //and be deleted\n  checkCudaErrors(hipMalloc(inputVals,  sizeof(unsigned int) * numElem));\n  checkCudaErrors(hipMalloc(inputPos,   sizeof(unsigned int) * numElem));\n  checkCudaErrors(hipMalloc(outputVals, sizeof(unsigned int) * numElem));\n  checkCudaErrors(hipMalloc(outputPos,  sizeof(unsigned int) * numElem));\n\n  hipMemcpy(*inputVals, thrust::raw_pointer_cast(d_combined_response.data()), sizeof(unsigned int) * numElem, hipMemcpyDeviceToDevice);\n  hipMemcpy(*inputPos,  thrust::raw_pointer_cast(coords.data()), sizeof(unsigned int) * numElem, hipMemcpyDeviceToDevice);\n  checkCudaErrors(hipMemset(*outputVals, 0, sizeof(unsigned int) * numElem));\n  checkCudaErrors(hipMemset(*outputPos, 0,  sizeof(unsigned int) * numElem));\n}\n\nvoid postProcess(const unsigned int* const outputVals,\n                 const unsigned int* const outputPos,\n                 const size_t numElems,\n                 const std::string& output_file){\n\n  thrust::device_vector<unsigned char> d_output_red = d_red;\n\n  const dim3 blockSize(256, 1, 1);\n  const dim3 gridSize( (40 + blockSize.x - 1) / blockSize.x, 1, 1);\n\n  remove_redness_from_coordinates<<<gridSize, blockSize>>>(outputPos,\n                                                           thrust::raw_pointer_cast(d_red.data()),\n                                                           thrust::raw_pointer_cast(d_blue.data()),\n                                                           thrust::raw_pointer_cast(d_green.data()),\n                                                           thrust::raw_pointer_cast(d_output_red.data()),\n                                                           40,\n                                                           numRowsImg, numColsImg,\n                                                           9, 9);\n\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  //combine the new red channel with original blue and green for output\n  thrust::device_vector<uchar4> d_outputImg(numElems);\n\n  thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(\n                          d_output_red.begin(),\n                          d_blue.begin(),\n                          d_green.begin())),\n                    thrust::make_zip_iterator(thrust::make_tuple(\n                          d_output_red.end(),\n                          d_blue.end(),\n                          d_green.end())),\n                    d_outputImg.begin(),\n                    combineChannels());\n\n  thrust::host_vector<uchar4> h_Img = d_outputImg;\n\n  saveImageRGBA(&h_Img[0], numRowsImg, numColsImg, output_file);\n\n  //Clear the global vectors otherwise something goes wrong trying to free them\n  d_red.clear(); d_red.shrink_to_fit();\n  d_blue.clear(); d_blue.shrink_to_fit();\n  d_green.clear(); d_green.shrink_to_fit();\n}\n\n", "source": "/kaggle/working/cs344/Problem Sets/Problem Set 4/HW4.cu"}
{"input": "/* Udacity HW5\n   Histogramming for Speed\n\n   The goal of this assignment is compute a histogram\n   as fast as possible.  We have simplified the problem as much as\n   possible to allow you to focus solely on the histogramming algorithm.\n\n   The input values that you need to histogram are already the exact\n   bins that need to be updated.  This is unlike in HW3 where you needed\n   to compute the range of the data and then do:\n   bin = (val - valMin) / valRange to determine the bin.\n\n   Here the bin is just:\n   bin = val\n\n   so the serial histogram calculation looks like:\n   for (i = 0; i < numElems; ++i)\n     histo[val[i]]++;\n\n   That's it!  Your job is to make it run as fast as possible!\n\n   The values are normally distributed - you may take\n   advantage of this fact in your implementation.\n\n*/\n\n\n#include \"utils.h\"\n\n__global__\nvoid yourHisto(const unsigned int* const vals, //INPUT\n               unsigned int* const histo,      //OUPUT\n               int numVals)\n{\n  //TODO fill in this kernel to calculate the histogram\n  //as quickly as possible\n\n  //Although we provide only one kernel skeleton,\n  //feel free to use more if it will help you\n  //write faster code\n}\n\nvoid computeHistogram(const unsigned int* const d_vals, //INPUT\n                      unsigned int* const d_histo,      //OUTPUT\n                      const unsigned int numBins,\n                      const unsigned int numElems)\n{\n  //TODO Launch the yourHisto kernel\n\n  //if you want to use/launch more than one kernel,\n  //feel free\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n}\n", "output": "/* Udacity HW5\n   Histogramming for Speed\n\n   The goal of this assignment is compute a histogram\n   as fast as possible.  We have simplified the problem as much as\n   possible to allow you to focus solely on the histogramming algorithm.\n\n   The input values that you need to histogram are already the exact\n   bins that need to be updated.  This is unlike in HW3 where you needed\n   to compute the range of the data and then do:\n   bin = (val - valMin) / valRange to determine the bin.\n\n   Here the bin is just:\n   bin = val\n\n   so the serial histogram calculation looks like:\n   for (i = 0; i < numElems; ++i)\n     histo[val[i]]++;\n\n   That's it!  Your job is to make it run as fast as possible!\n\n   The values are normally distributed - you may take\n   advantage of this fact in your implementation.\n\n*/\n\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.h\"\n\n__global__\nvoid yourHisto(const unsigned int* const vals, //INPUT\n               unsigned int* const histo,      //OUPUT\n               int numVals)\n{\n  //TODO fill in this kernel to calculate the histogram\n  //as quickly as possible\n\n  //Although we provide only one kernel skeleton,\n  //feel free to use more if it will help you\n  //write faster code\n}\n\nvoid computeHistogram(const unsigned int* const d_vals, //INPUT\n                      unsigned int* const d_histo,      //OUTPUT\n                      const unsigned int numBins,\n                      const unsigned int numElems)\n{\n  //TODO Launch the yourHisto kernel\n\n  //if you want to use/launch more than one kernel,\n  //feel free\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n}\n", "source": "/kaggle/working/cs344/Problem Sets/Problem Set 5/student.cu"}
{"input": "#include \"utils.h\"\n#include <string>\n#include \"loadSaveImage.h\"\n#include <thrust/extrema.h>\n\n//chroma-LogLuminance Space\nstatic float *d_x__;\nstatic float *d_y__;\nstatic float *d_logY__;\n\n//memory for the cdf\nstatic unsigned int *d_cdf__;\n\nstatic const int numBins = 1024;\n\nsize_t numRows__;\nsize_t numCols__;\n\n/* Copied from Mike's IPython notebook with some minor modifications\n * Mainly double precision constants to floats and log10 -> log10f\n * Also removed Luminance (Y) channel since it is never used       eke*/\n\n__global__ void rgb_to_xyY(\n    float* d_r,\n    float* d_g,\n    float* d_b,\n    float* d_x,\n    float* d_y,\n    float* d_log_Y,\n    float  delta,\n    int    num_pixels_y,\n    int    num_pixels_x )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    float r = d_r[ image_index_1d ];\n    float g = d_g[ image_index_1d ];\n    float b = d_b[ image_index_1d ];\n\n    float X = ( r * 0.4124f ) + ( g * 0.3576f ) + ( b * 0.1805f );\n    float Y = ( r * 0.2126f ) + ( g * 0.7152f ) + ( b * 0.0722f );\n    float Z = ( r * 0.0193f ) + ( g * 0.1192f ) + ( b * 0.9505f );\n\n    float L = X + Y + Z;\n    float x = X / L;\n    float y = Y / L;\n\n    float log_Y = log10f( delta + Y );\n\n    d_x[ image_index_1d ]     = x;\n    d_y[ image_index_1d ]     = y;\n    d_log_Y[ image_index_1d ] = log_Y;\n  }\n}\n\n/* Copied from Mike's IPython notebook *\n   Modified just by having threads read the \n   normalization constant directly from device memory\n   instead of copying it back                          */\n\n\n__global__ void normalize_cdf(\n    unsigned int* d_input_cdf,\n    float*        d_output_cdf,\n    int           n\n    )\n{\n  const float normalization_constant = 1.f / d_input_cdf[n - 1];\n\n  int global_index_1d = ( blockIdx.x * blockDim.x ) + threadIdx.x;\n\n  if ( global_index_1d < n )\n  {\n    unsigned int input_value  = d_input_cdf[ global_index_1d ];\n    float        output_value = input_value * normalization_constant;\n\n    d_output_cdf[ global_index_1d ] = output_value;\n  }\n}\n\n\n/* Copied from Mike's IPython notebook *\n   Modified double constants -> float  *\n   Perform tone mapping based upon new *\n   luminance scaling                   */\n\n__global__ void tonemap(\n    float* d_x,\n    float* d_y,\n    float* d_log_Y,\n    float* d_cdf_norm,\n    float* d_r_new,\n    float* d_g_new,\n    float* d_b_new,\n    float  min_log_Y,\n    float  max_log_Y,\n    float  log_Y_range,\n    int    num_bins,\n    int    num_pixels_y,\n    int    num_pixels_x )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    float x         = d_x[ image_index_1d ];\n    float y         = d_y[ image_index_1d ];\n    float log_Y     = d_log_Y[ image_index_1d ];\n    int   bin_index = min( num_bins - 1, int( (num_bins * ( log_Y - min_log_Y ) ) / log_Y_range ) );\n    float Y_new     = d_cdf_norm[ bin_index ];\n\n    float X_new = x * ( Y_new / y );\n    float Z_new = ( 1 - x - y ) * ( Y_new / y );\n\n    float r_new = ( X_new *  3.2406f ) + ( Y_new * -1.5372f ) + ( Z_new * -0.4986f );\n    float g_new = ( X_new * -0.9689f ) + ( Y_new *  1.8758f ) + ( Z_new *  0.0415f );\n    float b_new = ( X_new *  0.0557f ) + ( Y_new * -0.2040f ) + ( Z_new *  1.0570f );\n\n    d_r_new[ image_index_1d ] = r_new;\n    d_g_new[ image_index_1d ] = g_new;\n    d_b_new[ image_index_1d ] = b_new;\n  }\n}\n\n\n//return types are void since any internal error will be handled by quitting\n//no point in returning error codes...\nvoid preProcess(float** d_luminance, unsigned int** d_cdf,\n                size_t *numRows, size_t *numCols,\n                unsigned int *numberOfBins,\n                const std::string &filename) {\n  //make sure the context initializes ok\n  checkCudaErrors(cudaFree(0));\n\n  float *imgPtr; //we will become responsible for this pointer\n  loadImageHDR(filename, &imgPtr, &numRows__, &numCols__);\n  *numRows = numRows__;\n  *numCols = numCols__;\n\n  //first thing to do is split incoming BGR float data into separate channels\n  size_t numPixels = numRows__ * numCols__;\n  float *red   = new float[numPixels];\n  float *green = new float[numPixels];\n  float *blue  = new float[numPixels];\n\n  //Remeber image is loaded BGR\n  for (size_t i = 0; i < numPixels; ++i) {\n    blue[i]  = imgPtr[3 * i + 0];\n    green[i] = imgPtr[3 * i + 1];\n    red[i]   = imgPtr[3 * i + 2];\n  }\n\n  delete[] imgPtr; //being good citizens are releasing resources\n                   //allocated in loadImageHDR\n\n  float *d_red, *d_green, *d_blue;  //RGB space\n\n  size_t channelSize = sizeof(float) * numPixels;\n\n  checkCudaErrors(cudaMalloc(&d_red,    channelSize));\n  checkCudaErrors(cudaMalloc(&d_green,  channelSize));\n  checkCudaErrors(cudaMalloc(&d_blue,   channelSize));\n  checkCudaErrors(cudaMalloc(&d_x__,    channelSize));\n  checkCudaErrors(cudaMalloc(&d_y__,    channelSize));\n  checkCudaErrors(cudaMalloc(&d_logY__, channelSize));\n\n  checkCudaErrors(cudaMemcpy(d_red,   red,   channelSize, cudaMemcpyHostToDevice));\n  checkCudaErrors(cudaMemcpy(d_green, green, channelSize, cudaMemcpyHostToDevice));\n  checkCudaErrors(cudaMemcpy(d_blue,  blue,  channelSize, cudaMemcpyHostToDevice));\n\n  //convert from RGB space to chrominance/luminance space xyY\n  const dim3 blockSize(32, 16, 1);\n  const dim3 gridSize( (numCols__ + blockSize.x - 1) / blockSize.x, \n                       (numRows__ + blockSize.y - 1) / blockSize.y, 1);\n  rgb_to_xyY<<<gridSize, blockSize>>>(d_red, d_green, d_blue,\n                                      d_x__, d_y__,   d_logY__,\n                                      .0001f, numRows__, numCols__);\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  *d_luminance = d_logY__;\n\n  //allocate memory for the cdf of the histogram\n  *numberOfBins = numBins;\n  checkCudaErrors(cudaMalloc(&d_cdf__, sizeof(unsigned int) * numBins));\n  checkCudaErrors(cudaMemset(d_cdf__, 0, sizeof(unsigned int) * numBins));\n\n  *d_cdf = d_cdf__;\n\n  checkCudaErrors(cudaFree(d_red));\n  checkCudaErrors(cudaFree(d_green));\n  checkCudaErrors(cudaFree(d_blue));\n\n  delete[] red;\n  delete[] green;\n  delete[] blue;\n}\n\nvoid postProcess(const std::string& output_file, \n                 size_t numRows, size_t numCols,\n                 float min_log_Y, float max_log_Y) {\n  const int numPixels = numRows__ * numCols__;\n\n  const int numThreads = 192;\n\n  float *d_cdf_normalized;\n\n  checkCudaErrors(cudaMalloc(&d_cdf_normalized, sizeof(float) * numBins));\n\n  //first normalize the cdf to a maximum value of 1\n  //this is how we compress the range of the luminance channel\n  normalize_cdf<<< (numBins + numThreads - 1) / numThreads,\n                    numThreads>>>(d_cdf__,\n                                  d_cdf_normalized,\n                                  numBins);\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  //allocate memory for the output RGB channels\n  float *h_red, *h_green, *h_blue;\n  float *d_red, *d_green, *d_blue;\n\n  h_red   = new float[numPixels];\n  h_green = new float[numPixels];\n  h_blue  = new float[numPixels];\n\n  checkCudaErrors(cudaMalloc(&d_red,   sizeof(float) * numPixels));\n  checkCudaErrors(cudaMalloc(&d_green, sizeof(float) * numPixels));\n  checkCudaErrors(cudaMalloc(&d_blue,  sizeof(float) * numPixels));\n\n  float log_Y_range = max_log_Y - min_log_Y;\n\n  const dim3 blockSize(32, 16, 1);\n  const dim3 gridSize( (numCols + blockSize.x - 1) / blockSize.x,\n                       (numRows + blockSize.y - 1) / blockSize.y );\n  //next perform the actual tone-mapping\n  //we map each luminance value to its new value\n  //and then transform back to RGB space\n  tonemap<<<gridSize, blockSize>>>(d_x__, d_y__, d_logY__,\n                                   d_cdf_normalized,\n                                   d_red, d_green, d_blue,\n                                   min_log_Y, max_log_Y,\n                                   log_Y_range, numBins,\n                                   numRows, numCols);\n\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  checkCudaErrors(cudaMemcpy(h_red,   d_red,   sizeof(float) * numPixels, cudaMemcpyDeviceToHost));\n  checkCudaErrors(cudaMemcpy(h_green, d_green, sizeof(float) * numPixels, cudaMemcpyDeviceToHost));\n  checkCudaErrors(cudaMemcpy(h_blue,  d_blue,  sizeof(float) * numPixels, cudaMemcpyDeviceToHost));\n\n  //recombine the image channels\n  float *imageHDR = new float[numPixels * 3];\n\n  for (int i = 0; i < numPixels; ++i) {\n    imageHDR[3 * i + 0] = h_blue[i];\n    imageHDR[3 * i + 1] = h_green[i];\n    imageHDR[3 * i + 2] = h_red[i];\n  }\n\n  saveImageHDR(imageHDR, numRows, numCols, output_file);\n\n  delete[] imageHDR;\n  delete[] h_red;\n  delete[] h_green;\n  delete[] h_blue;\n\n  //cleanup\n  checkCudaErrors(cudaFree(d_cdf_normalized));\n}\n\nvoid cleanupGlobalMemory(void)\n{\n  checkCudaErrors(cudaFree(d_x__));\n  checkCudaErrors(cudaFree(d_y__));\n  checkCudaErrors(cudaFree(d_logY__));\n  checkCudaErrors(cudaFree(d_cdf__));\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include \"utils.h\"\n#include <string>\n#include \"loadSaveImage.h\"\n#include <thrust/extrema.h>\n\n//chroma-LogLuminance Space\nstatic float *d_x__;\nstatic float *d_y__;\nstatic float *d_logY__;\n\n//memory for the cdf\nstatic unsigned int *d_cdf__;\n\nstatic const int numBins = 1024;\n\nsize_t numRows__;\nsize_t numCols__;\n\n/* Copied from Mike's IPython notebook with some minor modifications\n * Mainly double precision constants to floats and log10 -> log10f\n * Also removed Luminance (Y) channel since it is never used       eke*/\n\n__global__ void rgb_to_xyY(\n    float* d_r,\n    float* d_g,\n    float* d_b,\n    float* d_x,\n    float* d_y,\n    float* d_log_Y,\n    float  delta,\n    int    num_pixels_y,\n    int    num_pixels_x )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    float r = d_r[ image_index_1d ];\n    float g = d_g[ image_index_1d ];\n    float b = d_b[ image_index_1d ];\n\n    float X = ( r * 0.4124f ) + ( g * 0.3576f ) + ( b * 0.1805f );\n    float Y = ( r * 0.2126f ) + ( g * 0.7152f ) + ( b * 0.0722f );\n    float Z = ( r * 0.0193f ) + ( g * 0.1192f ) + ( b * 0.9505f );\n\n    float L = X + Y + Z;\n    float x = X / L;\n    float y = Y / L;\n\n    float log_Y = log10f( delta + Y );\n\n    d_x[ image_index_1d ]     = x;\n    d_y[ image_index_1d ]     = y;\n    d_log_Y[ image_index_1d ] = log_Y;\n  }\n}\n\n/* Copied from Mike's IPython notebook *\n   Modified just by having threads read the \n   normalization constant directly from device memory\n   instead of copying it back                          */\n\n\n__global__ void normalize_cdf(\n    unsigned int* d_input_cdf,\n    float*        d_output_cdf,\n    int           n\n    )\n{\n  const float normalization_constant = 1.f / d_input_cdf[n - 1];\n\n  int global_index_1d = ( blockIdx.x * blockDim.x ) + threadIdx.x;\n\n  if ( global_index_1d < n )\n  {\n    unsigned int input_value  = d_input_cdf[ global_index_1d ];\n    float        output_value = input_value * normalization_constant;\n\n    d_output_cdf[ global_index_1d ] = output_value;\n  }\n}\n\n\n/* Copied from Mike's IPython notebook *\n   Modified double constants -> float  *\n   Perform tone mapping based upon new *\n   luminance scaling                   */\n\n__global__ void tonemap(\n    float* d_x,\n    float* d_y,\n    float* d_log_Y,\n    float* d_cdf_norm,\n    float* d_r_new,\n    float* d_g_new,\n    float* d_b_new,\n    float  min_log_Y,\n    float  max_log_Y,\n    float  log_Y_range,\n    int    num_bins,\n    int    num_pixels_y,\n    int    num_pixels_x )\n{\n  int  ny             = num_pixels_y;\n  int  nx             = num_pixels_x;\n  int2 image_index_2d = make_int2( ( blockIdx.x * blockDim.x ) + threadIdx.x, ( blockIdx.y * blockDim.y ) + threadIdx.y );\n  int  image_index_1d = ( nx * image_index_2d.y ) + image_index_2d.x;\n\n  if ( image_index_2d.x < nx && image_index_2d.y < ny )\n  {\n    float x         = d_x[ image_index_1d ];\n    float y         = d_y[ image_index_1d ];\n    float log_Y     = d_log_Y[ image_index_1d ];\n    int   bin_index = min( num_bins - 1, int( (num_bins * ( log_Y - min_log_Y ) ) / log_Y_range ) );\n    float Y_new     = d_cdf_norm[ bin_index ];\n\n    float X_new = x * ( Y_new / y );\n    float Z_new = ( 1 - x - y ) * ( Y_new / y );\n\n    float r_new = ( X_new *  3.2406f ) + ( Y_new * -1.5372f ) + ( Z_new * -0.4986f );\n    float g_new = ( X_new * -0.9689f ) + ( Y_new *  1.8758f ) + ( Z_new *  0.0415f );\n    float b_new = ( X_new *  0.0557f ) + ( Y_new * -0.2040f ) + ( Z_new *  1.0570f );\n\n    d_r_new[ image_index_1d ] = r_new;\n    d_g_new[ image_index_1d ] = g_new;\n    d_b_new[ image_index_1d ] = b_new;\n  }\n}\n\n\n//return types are void since any internal error will be handled by quitting\n//no point in returning error codes...\nvoid preProcess(float** d_luminance, unsigned int** d_cdf,\n                size_t *numRows, size_t *numCols,\n                unsigned int *numberOfBins,\n                const std::string &filename) {\n  //make sure the context initializes ok\n  checkCudaErrors(hipFree(0));\n\n  float *imgPtr; //we will become responsible for this pointer\n  loadImageHDR(filename, &imgPtr, &numRows__, &numCols__);\n  *numRows = numRows__;\n  *numCols = numCols__;\n\n  //first thing to do is split incoming BGR float data into separate channels\n  size_t numPixels = numRows__ * numCols__;\n  float *red   = new float[numPixels];\n  float *green = new float[numPixels];\n  float *blue  = new float[numPixels];\n\n  //Remeber image is loaded BGR\n  for (size_t i = 0; i < numPixels; ++i) {\n    blue[i]  = imgPtr[3 * i + 0];\n    green[i] = imgPtr[3 * i + 1];\n    red[i]   = imgPtr[3 * i + 2];\n  }\n\n  delete[] imgPtr; //being good citizens are releasing resources\n                   //allocated in loadImageHDR\n\n  float *d_red, *d_green, *d_blue;  //RGB space\n\n  size_t channelSize = sizeof(float) * numPixels;\n\n  checkCudaErrors(hipMalloc(&d_red,    channelSize));\n  checkCudaErrors(hipMalloc(&d_green,  channelSize));\n  checkCudaErrors(hipMalloc(&d_blue,   channelSize));\n  checkCudaErrors(hipMalloc(&d_x__,    channelSize));\n  checkCudaErrors(hipMalloc(&d_y__,    channelSize));\n  checkCudaErrors(hipMalloc(&d_logY__, channelSize));\n\n  checkCudaErrors(hipMemcpy(d_red,   red,   channelSize, hipMemcpyHostToDevice));\n  checkCudaErrors(hipMemcpy(d_green, green, channelSize, hipMemcpyHostToDevice));\n  checkCudaErrors(hipMemcpy(d_blue,  blue,  channelSize, hipMemcpyHostToDevice));\n\n  //convert from RGB space to chrominance/luminance space xyY\n  const dim3 blockSize(32, 16, 1);\n  const dim3 gridSize( (numCols__ + blockSize.x - 1) / blockSize.x, \n                       (numRows__ + blockSize.y - 1) / blockSize.y, 1);\n  rgb_to_xyY<<<gridSize, blockSize>>>(d_red, d_green, d_blue,\n                                      d_x__, d_y__,   d_logY__,\n                                      .0001f, numRows__, numCols__);\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  *d_luminance = d_logY__;\n\n  //allocate memory for the cdf of the histogram\n  *numberOfBins = numBins;\n  checkCudaErrors(hipMalloc(&d_cdf__, sizeof(unsigned int) * numBins));\n  checkCudaErrors(hipMemset(d_cdf__, 0, sizeof(unsigned int) * numBins));\n\n  *d_cdf = d_cdf__;\n\n  checkCudaErrors(hipFree(d_red));\n  checkCudaErrors(hipFree(d_green));\n  checkCudaErrors(hipFree(d_blue));\n\n  delete[] red;\n  delete[] green;\n  delete[] blue;\n}\n\nvoid postProcess(const std::string& output_file, \n                 size_t numRows, size_t numCols,\n                 float min_log_Y, float max_log_Y) {\n  const int numPixels = numRows__ * numCols__;\n\n  const int numThreads = 192;\n\n  float *d_cdf_normalized;\n\n  checkCudaErrors(hipMalloc(&d_cdf_normalized, sizeof(float) * numBins));\n\n  //first normalize the cdf to a maximum value of 1\n  //this is how we compress the range of the luminance channel\n  normalize_cdf<<< (numBins + numThreads - 1) / numThreads,\n                    numThreads>>>(d_cdf__,\n                                  d_cdf_normalized,\n                                  numBins);\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  //allocate memory for the output RGB channels\n  float *h_red, *h_green, *h_blue;\n  float *d_red, *d_green, *d_blue;\n\n  h_red   = new float[numPixels];\n  h_green = new float[numPixels];\n  h_blue  = new float[numPixels];\n\n  checkCudaErrors(hipMalloc(&d_red,   sizeof(float) * numPixels));\n  checkCudaErrors(hipMalloc(&d_green, sizeof(float) * numPixels));\n  checkCudaErrors(hipMalloc(&d_blue,  sizeof(float) * numPixels));\n\n  float log_Y_range = max_log_Y - min_log_Y;\n\n  const dim3 blockSize(32, 16, 1);\n  const dim3 gridSize( (numCols + blockSize.x - 1) / blockSize.x,\n                       (numRows + blockSize.y - 1) / blockSize.y );\n  //next perform the actual tone-mapping\n  //we map each luminance value to its new value\n  //and then transform back to RGB space\n  tonemap<<<gridSize, blockSize>>>(d_x__, d_y__, d_logY__,\n                                   d_cdf_normalized,\n                                   d_red, d_green, d_blue,\n                                   min_log_Y, max_log_Y,\n                                   log_Y_range, numBins,\n                                   numRows, numCols);\n\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  checkCudaErrors(hipMemcpy(h_red,   d_red,   sizeof(float) * numPixels, hipMemcpyDeviceToHost));\n  checkCudaErrors(hipMemcpy(h_green, d_green, sizeof(float) * numPixels, hipMemcpyDeviceToHost));\n  checkCudaErrors(hipMemcpy(h_blue,  d_blue,  sizeof(float) * numPixels, hipMemcpyDeviceToHost));\n\n  //recombine the image channels\n  float *imageHDR = new float[numPixels * 3];\n\n  for (int i = 0; i < numPixels; ++i) {\n    imageHDR[3 * i + 0] = h_blue[i];\n    imageHDR[3 * i + 1] = h_green[i];\n    imageHDR[3 * i + 2] = h_red[i];\n  }\n\n  saveImageHDR(imageHDR, numRows, numCols, output_file);\n\n  delete[] imageHDR;\n  delete[] h_red;\n  delete[] h_green;\n  delete[] h_blue;\n\n  //cleanup\n  checkCudaErrors(hipFree(d_cdf_normalized));\n}\n\nvoid cleanupGlobalMemory(void)\n{\n  checkCudaErrors(hipFree(d_x__));\n  checkCudaErrors(hipFree(d_y__));\n  checkCudaErrors(hipFree(d_logY__));\n  checkCudaErrors(hipFree(d_cdf__));\n}\n", "source": "/kaggle/working/cs344/Problem Sets/Problem Set 3/HW3.cu"}
{"input": "// Homework 2\n// Image Blurring\n//\n// In this homework we are blurring an image. To do this, imagine that we have\n// a square array of weight values. For each pixel in the image, imagine that we\n// overlay this square array of weights on top of the image such that the center\n// of the weight array is aligned with the current pixel. To compute a blurred\n// pixel value, we multiply each pair of numbers that line up. In other words, we\n// multiply each weight with the pixel underneath it. Finally, we add up all of the\n// multiplied numbers and assign that value to our output for the current pixel.\n// We repeat this process for all the pixels in the image.\n\n// To help get you started, we have included some useful notes here.\n\n//****************************************************************************\n\n// For a color image that has multiple channels, we suggest separating\n// the different color channels so that each color is stored contiguously\n// instead of being interleaved. This will simplify your code.\n\n// That is instead of RGBARGBARGBARGBA... we suggest transforming to three\n// arrays (as in the previous homework we ignore the alpha channel again):\n//  1) RRRRRRRR...\n//  2) GGGGGGGG...\n//  3) BBBBBBBB...\n//\n// The original layout is known an Array of Structures (AoS) whereas the\n// format we are converting to is known as a Structure of Arrays (SoA).\n\n// As a warm-up, we will ask you to write the kernel that performs this\n// separation. You should then write the \"meat\" of the assignment,\n// which is the kernel that performs the actual blur. We provide code that\n// re-combines your blurred results for each color channel.\n\n//****************************************************************************\n\n// You must fill in the gaussian_blur kernel to perform the blurring of the\n// inputChannel, using the array of weights, and put the result in the outputChannel.\n\n// Here is an example of computing a blur, using a weighted average, for a single\n// pixel in a small image.\n//\n// Array of weights:\n//\n//  0.0  0.2  0.0\n//  0.2  0.2  0.2\n//  0.0  0.2  0.0\n//\n// Image (note that we align the array of weights to the center of the box):\n//\n//    1  2  5  2  0  3\n//       -------\n//    3 |2  5  1| 6  0       0.0*2 + 0.2*5 + 0.0*1 +\n//      |       |\n//    4 |3  6  2| 1  4   ->  0.2*3 + 0.2*6 + 0.2*2 +   ->  3.2\n//      |       |\n//    0 |4  0  3| 4  2       0.0*4 + 0.2*0 + 0.0*3\n//       -------\n//    9  6  5  0  3  9\n//\n//         (1)                         (2)                 (3)\n//\n// A good starting place is to map each thread to a pixel as you have before.\n// Then every thread can perform steps 2 and 3 in the diagram above\n// completely independently of one another.\n\n// Note that the array of weights is square, so its height is the same as its width.\n// We refer to the array of weights as a filter, and we refer to its width with the\n// variable filterWidth.\n\n//****************************************************************************\n\n// Your homework submission will be evaluated based on correctness and speed.\n// We test each pixel against a reference solution. If any pixel differs by\n// more than some small threshold value, the system will tell you that your\n// solution is incorrect, and it will let you try again.\n\n// Once you have gotten that working correctly, then you can think about using\n// shared memory and having the threads cooperate to achieve better performance.\n\n//****************************************************************************\n\n// Also note that we've supplied a helpful debugging function called checkCudaErrors.\n// You should wrap your allocation and copying statements like we've done in the\n// code we're supplying you. Here is an example of the unsafe way to allocate\n// memory on the GPU:\n//\n// cudaMalloc(&d_red, sizeof(unsigned char) * numRows * numCols);\n//\n// Here is an example of the safe way to do the same thing:\n//\n// checkCudaErrors(cudaMalloc(&d_red, sizeof(unsigned char) * numRows * numCols));\n//\n// Writing code the safe way requires slightly more typing, but is very helpful for\n// catching mistakes. If you write code the unsafe way and you make a mistake, then\n// any subsequent kernels won't compute anything, and it will be hard to figure out\n// why. Writing code the safe way will inform you as soon as you make a mistake.\n\n// Finally, remember to free the memory you allocate at the end of the function.\n\n//****************************************************************************\n\n#include \"utils.h\"\n\n__global__\nvoid gaussian_blur(const unsigned char* const inputChannel,\n                   unsigned char* const outputChannel,\n                   int numRows, int numCols,\n                   const float* const filter, const int filterWidth)\n{\n  // TODO\n  \n  // NOTE: Be sure to compute any intermediate results in floating point\n  // before storing the final result as unsigned char.\n\n  // NOTE: Be careful not to try to access memory that is outside the bounds of\n  // the image. You'll want code that performs the following check before accessing\n  // GPU memory:\n  //\n  // if ( absolute_image_position_x >= numCols ||\n  //      absolute_image_position_y >= numRows )\n  // {\n  //     return;\n  // }\n  \n  // NOTE: If a thread's absolute position 2D position is within the image, but some of\n  // its neighbors are outside the image, then you will need to be extra careful. Instead\n  // of trying to read such a neighbor value from GPU memory (which won't work because\n  // the value is out of bounds), you should explicitly clamp the neighbor values you read\n  // to be within the bounds of the image. If this is not clear to you, then please refer\n  // to sequential reference solution for the exact clamping semantics you should follow.\n}\n\n//This kernel takes in an image represented as a uchar4 and splits\n//it into three images consisting of only one color channel each\n__global__\nvoid separateChannels(const uchar4* const inputImageRGBA,\n                      int numRows,\n                      int numCols,\n                      unsigned char* const redChannel,\n                      unsigned char* const greenChannel,\n                      unsigned char* const blueChannel)\n{\n  // TODO\n  //\n  // NOTE: Be careful not to try to access memory that is outside the bounds of\n  // the image. You'll want code that performs the following check before accessing\n  // GPU memory:\n  //\n  // if ( absolute_image_position_x >= numCols ||\n  //      absolute_image_position_y >= numRows )\n  // {\n  //     return;\n  // }\n}\n\n//This kernel takes in three color channels and recombines them\n//into one image.  The alpha channel is set to 255 to represent\n//that this image has no transparency.\n__global__\nvoid recombineChannels(const unsigned char* const redChannel,\n                       const unsigned char* const greenChannel,\n                       const unsigned char* const blueChannel,\n                       uchar4* const outputImageRGBA,\n                       int numRows,\n                       int numCols)\n{\n  const int2 thread_2D_pos = make_int2( blockIdx.x * blockDim.x + threadIdx.x,\n                                        blockIdx.y * blockDim.y + threadIdx.y);\n\n  const int thread_1D_pos = thread_2D_pos.y * numCols + thread_2D_pos.x;\n\n  //make sure we don't try and access memory outside the image\n  //by having any threads mapped there return early\n  if (thread_2D_pos.x >= numCols || thread_2D_pos.y >= numRows)\n    return;\n\n  unsigned char red   = redChannel[thread_1D_pos];\n  unsigned char green = greenChannel[thread_1D_pos];\n  unsigned char blue  = blueChannel[thread_1D_pos];\n\n  //Alpha should be 255 for no transparency\n  uchar4 outputPixel = make_uchar4(red, green, blue, 255);\n\n  outputImageRGBA[thread_1D_pos] = outputPixel;\n}\n\nunsigned char *d_red, *d_green, *d_blue;\nfloat         *d_filter;\n\nvoid allocateMemoryAndCopyToGPU(const size_t numRowsImage, const size_t numColsImage,\n                                const float* const h_filter, const size_t filterWidth)\n{\n\n  //allocate memory for the three different channels\n  //original\n  checkCudaErrors(cudaMalloc(&d_red,   sizeof(unsigned char) * numRowsImage * numColsImage));\n  checkCudaErrors(cudaMalloc(&d_green, sizeof(unsigned char) * numRowsImage * numColsImage));\n  checkCudaErrors(cudaMalloc(&d_blue,  sizeof(unsigned char) * numRowsImage * numColsImage));\n\n  //TODO:\n  //Allocate memory for the filter on the GPU\n  //Use the pointer d_filter that we have already declared for you\n  //You need to allocate memory for the filter with cudaMalloc\n  //be sure to use checkCudaErrors like the above examples to\n  //be able to tell if anything goes wrong\n  //IMPORTANT: Notice that we pass a pointer to a pointer to cudaMalloc\n\n  //TODO:\n  //Copy the filter on the host (h_filter) to the memory you just allocated\n  //on the GPU.  cudaMemcpy(dst, src, numBytes, cudaMemcpyHostToDevice);\n  //Remember to use checkCudaErrors!\n\n}\n\nvoid your_gaussian_blur(const uchar4 * const h_inputImageRGBA, uchar4 * const d_inputImageRGBA,\n                        uchar4* const d_outputImageRGBA, const size_t numRows, const size_t numCols,\n                        unsigned char *d_redBlurred, \n                        unsigned char *d_greenBlurred, \n                        unsigned char *d_blueBlurred,\n                        const int filterWidth)\n{\n  //TODO: Set reasonable block size (i.e., number of threads per block)\n  const dim3 blockSize;\n\n  //TODO:\n  //Compute correct grid size (i.e., number of blocks per kernel launch)\n  //from the image size and and block size.\n  const dim3 gridSize;\n\n  //TODO: Launch a kernel for separating the RGBA image into different color channels\n\n  // Call cudaDeviceSynchronize(), then call checkCudaErrors() immediately after\n  // launching your kernel to make sure that you didn't make any mistakes.\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  //TODO: Call your convolution kernel here 3 times, once for each color channel.\n\n  // Again, call cudaDeviceSynchronize(), then call checkCudaErrors() immediately after\n  // launching your kernel to make sure that you didn't make any mistakes.\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n  // Now we recombine your results. We take care of launching this kernel for you.\n  //\n  // NOTE: This kernel launch depends on the gridSize and blockSize variables,\n  // which you must set yourself.\n  recombineChannels<<<gridSize, blockSize>>>(d_redBlurred,\n                                             d_greenBlurred,\n                                             d_blueBlurred,\n                                             d_outputImageRGBA,\n                                             numRows,\n                                             numCols);\n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n}\n\n\n//Free all the memory that we allocated\n//TODO: make sure you free any arrays that you allocated\nvoid cleanup() {\n  checkCudaErrors(cudaFree(d_red));\n  checkCudaErrors(cudaFree(d_green));\n  checkCudaErrors(cudaFree(d_blue));\n}\n", "output": "// Homework 2\n// Image Blurring\n//\n// In this homework we are blurring an image. To do this, imagine that we have\n// a square array of weight values. For each pixel in the image, imagine that we\n// overlay this square array of weights on top of the image such that the center\n// of the weight array is aligned with the current pixel. To compute a blurred\n// pixel value, we multiply each pair of numbers that line up. In other words, we\n// multiply each weight with the pixel underneath it. Finally, we add up all of the\n// multiplied numbers and assign that value to our output for the current pixel.\n// We repeat this process for all the pixels in the image.\n\n// To help get you started, we have included some useful notes here.\n\n//****************************************************************************\n\n// For a color image that has multiple channels, we suggest separating\n// the different color channels so that each color is stored contiguously\n// instead of being interleaved. This will simplify your code.\n\n// That is instead of RGBARGBARGBARGBA... we suggest transforming to three\n// arrays (as in the previous homework we ignore the alpha channel again):\n//  1) RRRRRRRR...\n//  2) GGGGGGGG...\n//  3) BBBBBBBB...\n//\n// The original layout is known an Array of Structures (AoS) whereas the\n// format we are converting to is known as a Structure of Arrays (SoA).\n\n// As a warm-up, we will ask you to write the kernel that performs this\n// separation. You should then write the \"meat\" of the assignment,\n// which is the kernel that performs the actual blur. We provide code that\n// re-combines your blurred results for each color channel.\n\n//****************************************************************************\n\n// You must fill in the gaussian_blur kernel to perform the blurring of the\n// inputChannel, using the array of weights, and put the result in the outputChannel.\n\n// Here is an example of computing a blur, using a weighted average, for a single\n// pixel in a small image.\n//\n// Array of weights:\n//\n//  0.0  0.2  0.0\n//  0.2  0.2  0.2\n//  0.0  0.2  0.0\n//\n// Image (note that we align the array of weights to the center of the box):\n//\n//    1  2  5  2  0  3\n//       -------\n//    3 |2  5  1| 6  0       0.0*2 + 0.2*5 + 0.0*1 +\n//      |       |\n//    4 |3  6  2| 1  4   ->  0.2*3 + 0.2*6 + 0.2*2 +   ->  3.2\n//      |       |\n//    0 |4  0  3| 4  2       0.0*4 + 0.2*0 + 0.0*3\n//       -------\n//    9  6  5  0  3  9\n//\n//         (1)                         (2)                 (3)\n//\n// A good starting place is to map each thread to a pixel as you have before.\n// Then every thread can perform steps 2 and 3 in the diagram above\n// completely independently of one another.\n\n// Note that the array of weights is square, so its height is the same as its width.\n// We refer to the array of weights as a filter, and we refer to its width with the\n// variable filterWidth.\n\n//****************************************************************************\n\n// Your homework submission will be evaluated based on correctness and speed.\n// We test each pixel against a reference solution. If any pixel differs by\n// more than some small threshold value, the system will tell you that your\n// solution is incorrect, and it will let you try again.\n\n// Once you have gotten that working correctly, then you can think about using\n// shared memory and having the threads cooperate to achieve better performance.\n\n//****************************************************************************\n\n// Also note that we've supplied a helpful debugging function called checkCudaErrors.\n// You should wrap your allocation and copying statements like we've done in the\n// code we're supplying you. Here is an example of the unsafe way to allocate\n// memory on the GPU:\n//\n// cudaMalloc(&d_red, sizeof(unsigned char) * numRows * numCols);\n//\n// Here is an example of the safe way to do the same thing:\n//\n// checkCudaErrors(cudaMalloc(&d_red, sizeof(unsigned char) * numRows * numCols));\n//\n// Writing code the safe way requires slightly more typing, but is very helpful for\n// catching mistakes. If you write code the unsafe way and you make a mistake, then\n// any subsequent kernels won't compute anything, and it will be hard to figure out\n// why. Writing code the safe way will inform you as soon as you make a mistake.\n\n// Finally, remember to free the memory you allocate at the end of the function.\n\n//****************************************************************************\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.h\"\n\n__global__\nvoid gaussian_blur(const unsigned char* const inputChannel,\n                   unsigned char* const outputChannel,\n                   int numRows, int numCols,\n                   const float* const filter, const int filterWidth)\n{\n  // TODO\n  \n  // NOTE: Be sure to compute any intermediate results in floating point\n  // before storing the final result as unsigned char.\n\n  // NOTE: Be careful not to try to access memory that is outside the bounds of\n  // the image. You'll want code that performs the following check before accessing\n  // GPU memory:\n  //\n  // if ( absolute_image_position_x >= numCols ||\n  //      absolute_image_position_y >= numRows )\n  // {\n  //     return;\n  // }\n  \n  // NOTE: If a thread's absolute position 2D position is within the image, but some of\n  // its neighbors are outside the image, then you will need to be extra careful. Instead\n  // of trying to read such a neighbor value from GPU memory (which won't work because\n  // the value is out of bounds), you should explicitly clamp the neighbor values you read\n  // to be within the bounds of the image. If this is not clear to you, then please refer\n  // to sequential reference solution for the exact clamping semantics you should follow.\n}\n\n//This kernel takes in an image represented as a uchar4 and splits\n//it into three images consisting of only one color channel each\n__global__\nvoid separateChannels(const uchar4* const inputImageRGBA,\n                      int numRows,\n                      int numCols,\n                      unsigned char* const redChannel,\n                      unsigned char* const greenChannel,\n                      unsigned char* const blueChannel)\n{\n  // TODO\n  //\n  // NOTE: Be careful not to try to access memory that is outside the bounds of\n  // the image. You'll want code that performs the following check before accessing\n  // GPU memory:\n  //\n  // if ( absolute_image_position_x >= numCols ||\n  //      absolute_image_position_y >= numRows )\n  // {\n  //     return;\n  // }\n}\n\n//This kernel takes in three color channels and recombines them\n//into one image.  The alpha channel is set to 255 to represent\n//that this image has no transparency.\n__global__\nvoid recombineChannels(const unsigned char* const redChannel,\n                       const unsigned char* const greenChannel,\n                       const unsigned char* const blueChannel,\n                       uchar4* const outputImageRGBA,\n                       int numRows,\n                       int numCols)\n{\n  const int2 thread_2D_pos = make_int2( blockIdx.x * blockDim.x + threadIdx.x,\n                                        blockIdx.y * blockDim.y + threadIdx.y);\n\n  const int thread_1D_pos = thread_2D_pos.y * numCols + thread_2D_pos.x;\n\n  //make sure we don't try and access memory outside the image\n  //by having any threads mapped there return early\n  if (thread_2D_pos.x >= numCols || thread_2D_pos.y >= numRows)\n    return;\n\n  unsigned char red   = redChannel[thread_1D_pos];\n  unsigned char green = greenChannel[thread_1D_pos];\n  unsigned char blue  = blueChannel[thread_1D_pos];\n\n  //Alpha should be 255 for no transparency\n  uchar4 outputPixel = make_uchar4(red, green, blue, 255);\n\n  outputImageRGBA[thread_1D_pos] = outputPixel;\n}\n\nunsigned char *d_red, *d_green, *d_blue;\nfloat         *d_filter;\n\nvoid allocateMemoryAndCopyToGPU(const size_t numRowsImage, const size_t numColsImage,\n                                const float* const h_filter, const size_t filterWidth)\n{\n\n  //allocate memory for the three different channels\n  //original\n  checkCudaErrors(hipMalloc(&d_red,   sizeof(unsigned char) * numRowsImage * numColsImage));\n  checkCudaErrors(hipMalloc(&d_green, sizeof(unsigned char) * numRowsImage * numColsImage));\n  checkCudaErrors(hipMalloc(&d_blue,  sizeof(unsigned char) * numRowsImage * numColsImage));\n\n  //TODO:\n  //Allocate memory for the filter on the GPU\n  //Use the pointer d_filter that we have already declared for you\n  //You need to allocate memory for the filter with cudaMalloc\n  //be sure to use checkCudaErrors like the above examples to\n  //be able to tell if anything goes wrong\n  //IMPORTANT: Notice that we pass a pointer to a pointer to cudaMalloc\n\n  //TODO:\n  //Copy the filter on the host (h_filter) to the memory you just allocated\n  //on the GPU.  cudaMemcpy(dst, src, numBytes, cudaMemcpyHostToDevice);\n  //Remember to use checkCudaErrors!\n\n}\n\nvoid your_gaussian_blur(const uchar4 * const h_inputImageRGBA, uchar4 * const d_inputImageRGBA,\n                        uchar4* const d_outputImageRGBA, const size_t numRows, const size_t numCols,\n                        unsigned char *d_redBlurred, \n                        unsigned char *d_greenBlurred, \n                        unsigned char *d_blueBlurred,\n                        const int filterWidth)\n{\n  //TODO: Set reasonable block size (i.e., number of threads per block)\n  const dim3 blockSize;\n\n  //TODO:\n  //Compute correct grid size (i.e., number of blocks per kernel launch)\n  //from the image size and and block size.\n  const dim3 gridSize;\n\n  //TODO: Launch a kernel for separating the RGBA image into different color channels\n\n  // Call cudaDeviceSynchronize(), then call checkCudaErrors() immediately after\n  // launching your kernel to make sure that you didn't make any mistakes.\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  //TODO: Call your convolution kernel here 3 times, once for each color channel.\n\n  // Again, call cudaDeviceSynchronize(), then call checkCudaErrors() immediately after\n  // launching your kernel to make sure that you didn't make any mistakes.\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n  // Now we recombine your results. We take care of launching this kernel for you.\n  //\n  // NOTE: This kernel launch depends on the gridSize and blockSize variables,\n  // which you must set yourself.\n  recombineChannels<<<gridSize, blockSize>>>(d_redBlurred,\n                                             d_greenBlurred,\n                                             d_blueBlurred,\n                                             d_outputImageRGBA,\n                                             numRows,\n                                             numCols);\n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n}\n\n\n//Free all the memory that we allocated\n//TODO: make sure you free any arrays that you allocated\nvoid cleanup() {\n  checkCudaErrors(hipFree(d_red));\n  checkCudaErrors(hipFree(d_green));\n  checkCudaErrors(hipFree(d_blue));\n}\n", "source": "/kaggle/working/cs344/Problem Sets/Problem Set 2/student_func.cu"}
{"input": "// Homework 1\n// Color to Greyscale Conversion\n\n//A common way to represent color images is known as RGBA - the color\n//is specified by how much Red, Grean and Blue is in it.\n//The 'A' stands for Alpha and is used for transparency, it will be\n//ignored in this homework.\n\n//Each channel Red, Blue, Green and Alpha is represented by one byte.\n//Since we are using one byte for each color there are 256 different\n//possible values for each color.  This means we use 4 bytes per pixel.\n\n//Greyscale images are represented by a single intensity value per pixel\n//which is one byte in size.\n\n//To convert an image from color to grayscale one simple method is to\n//set the intensity to the average of the RGB channels.  But we will\n//use a more sophisticated method that takes into account how the eye \n//perceives color and weights the channels unequally.\n\n//The eye responds most strongly to green followed by red and then blue.\n//The NTSC (National Television System Committee) recommends the following\n//formula for color to greyscale conversion:\n\n//I = .299f * R + .587f * G + .114f * B\n\n//Notice the trailing f's on the numbers which indicate that they are \n//single precision floating point constants and not double precision\n//constants.\n\n//You should fill in the kernel as well as set the block and grid sizes\n//so that the entire image is processed.\n\n#include \"utils.h\"\n\n__global__\nvoid rgba_to_greyscale(const uchar4* const rgbaImage,\n                       unsigned char* const greyImage,\n                       int numRows, int numCols)\n{\n  //TODO\n  //Fill in the kernel to convert from color to greyscale\n  //the mapping from components of a uchar4 to RGBA is:\n  // .x -> R ; .y -> G ; .z -> B ; .w -> A\n  //\n  //The output (greyImage) at each pixel should be the result of\n  //applying the formula: output = .299f * R + .587f * G + .114f * B;\n  //Note: We will be ignoring the alpha channel for this conversion\n\n  //First create a mapping from the 2D block and grid locations\n  //to an absolute 2D location in the image, then use that to\n  //calculate a 1D offset\n}\n\nvoid your_rgba_to_greyscale(const uchar4 * const h_rgbaImage, uchar4 * const d_rgbaImage,\n                            unsigned char* const d_greyImage, size_t numRows, size_t numCols)\n{\n  //You must fill in the correct sizes for the blockSize and gridSize\n  //currently only one block with one thread is being launched\n  const dim3 blockSize(1, 1, 1);  //TODO\n  const dim3 gridSize( 1, 1, 1);  //TODO\n  rgba_to_greyscale<<<gridSize, blockSize>>>(d_rgbaImage, d_greyImage, numRows, numCols);\n  \n  cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());\n\n}\n", "output": "// Homework 1\n// Color to Greyscale Conversion\n\n//A common way to represent color images is known as RGBA - the color\n//is specified by how much Red, Grean and Blue is in it.\n//The 'A' stands for Alpha and is used for transparency, it will be\n//ignored in this homework.\n\n//Each channel Red, Blue, Green and Alpha is represented by one byte.\n//Since we are using one byte for each color there are 256 different\n//possible values for each color.  This means we use 4 bytes per pixel.\n\n//Greyscale images are represented by a single intensity value per pixel\n//which is one byte in size.\n\n//To convert an image from color to grayscale one simple method is to\n//set the intensity to the average of the RGB channels.  But we will\n//use a more sophisticated method that takes into account how the eye \n//perceives color and weights the channels unequally.\n\n//The eye responds most strongly to green followed by red and then blue.\n//The NTSC (National Television System Committee) recommends the following\n//formula for color to greyscale conversion:\n\n//I = .299f * R + .587f * G + .114f * B\n\n//Notice the trailing f's on the numbers which indicate that they are \n//single precision floating point constants and not double precision\n//constants.\n\n//You should fill in the kernel as well as set the block and grid sizes\n//so that the entire image is processed.\n\n#include \"hip/hip_runtime.h\"\n#include \"utils.h\"\n\n__global__\nvoid rgba_to_greyscale(const uchar4* const rgbaImage,\n                       unsigned char* const greyImage,\n                       int numRows, int numCols)\n{\n  //TODO\n  //Fill in the kernel to convert from color to greyscale\n  //the mapping from components of a uchar4 to RGBA is:\n  // .x -> R ; .y -> G ; .z -> B ; .w -> A\n  //\n  //The output (greyImage) at each pixel should be the result of\n  //applying the formula: output = .299f * R + .587f * G + .114f * B;\n  //Note: We will be ignoring the alpha channel for this conversion\n\n  //First create a mapping from the 2D block and grid locations\n  //to an absolute 2D location in the image, then use that to\n  //calculate a 1D offset\n}\n\nvoid your_rgba_to_greyscale(const uchar4 * const h_rgbaImage, uchar4 * const d_rgbaImage,\n                            unsigned char* const d_greyImage, size_t numRows, size_t numCols)\n{\n  //You must fill in the correct sizes for the blockSize and gridSize\n  //currently only one block with one thread is being launched\n  const dim3 blockSize(1, 1, 1);  //TODO\n  const dim3 gridSize( 1, 1, 1);  //TODO\n  rgba_to_greyscale<<<gridSize, blockSize>>>(d_rgbaImage, d_greyImage, numRows, numCols);\n  \n  hipDeviceSynchronize(); checkCudaErrors(hipGetLastError());\n\n}\n", "source": "/kaggle/working/cs344/Problem Sets/Problem Set 1/student_func.cu"}
{"input": "\ufeff// thread_tiled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n#define THREAD_TILE 2  // each thread computes 2 output elements (along columns)\n\n__global__ void matMulTiledThreadTile(const float* A, const float* B, float* C, int n) {\n    // Block covers a TILE_WIDTH x TILE_WIDTH tile of C.\n    // We set blockDim.x = TILE_WIDTH/THREAD_TILE and blockDim.y = TILE_WIDTH.\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n\n    float sum[THREAD_TILE] = { 0.0f, 0.0f };\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n\n    for (int t = 0; t < numTiles; t++) {\n        // Each thread loads one element of A into shared memory.\n        int aCol = t * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n        for (int i = 0; i < THREAD_TILE; i++) {\n            int colA = aCol + i;\n            tileA[threadIdx.y][threadIdx.x * THREAD_TILE + i] =\n                (row < n && colA < n) ? A[row * n + colA] : 0.0f;\n        }\n        // Each thread loads one element of B into shared memory.\n        int bRow = t * TILE_WIDTH + threadIdx.y;\n        int bCol = blockIdx.x * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n        for (int i = 0; i < THREAD_TILE; i++) {\n            int colB = bCol + i;\n            tileB[threadIdx.y][threadIdx.x * THREAD_TILE + i] =\n                (bRow < n && colB < n) ? B[bRow * n + colB] : 0.0f;\n        }\n        __syncthreads();\n\n        // Compute partial products from the current tile.\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            float aVal = tileA[threadIdx.y][k];\n            for (int i = 0; i < THREAD_TILE; i++) {\n                sum[i] += aVal * tileB[k][threadIdx.x * THREAD_TILE + i];\n            }\n        }\n        __syncthreads();\n    }\n\n    // Write computed tile to global memory.\n    for (int i = 0; i < THREAD_TILE; i++) {\n        int cCol = col + i;\n        if (row < n && cCol < n)\n            C[row * n + cCol] = sum[i];\n    }\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C, bytes);\n\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    // Set block dimensions: \n    // blockDim.x = TILE_WIDTH/THREAD_TILE, blockDim.y = TILE_WIDTH.\n    dim3 block(TILE_WIDTH / THREAD_TILE, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n\n    matMulTiledThreadTile << <grid, block >> > (d_A, d_B, d_C, N);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled thread-tiled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "output": "\ufeff// thread_tiled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n#define THREAD_TILE 2  // each thread computes 2 output elements (along columns)\n\n__global__ void matMulTiledThreadTile(const float* A, const float* B, float* C, int n) {\n    // Block covers a TILE_WIDTH x TILE_WIDTH tile of C.\n    // We set blockDim.x = TILE_WIDTH/THREAD_TILE and blockDim.y = TILE_WIDTH.\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n\n    float sum[THREAD_TILE] = { 0.0f, 0.0f };\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n\n    for (int t = 0; t < numTiles; t++) {\n        // Each thread loads one element of A into shared memory.\n        int aCol = t * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n        for (int i = 0; i < THREAD_TILE; i++) {\n            int colA = aCol + i;\n            tileA[threadIdx.y][threadIdx.x * THREAD_TILE + i] =\n                (row < n && colA < n) ? A[row * n + colA] : 0.0f;\n        }\n        // Each thread loads one element of B into shared memory.\n        int bRow = t * TILE_WIDTH + threadIdx.y;\n        int bCol = blockIdx.x * TILE_WIDTH + threadIdx.x * THREAD_TILE;\n        for (int i = 0; i < THREAD_TILE; i++) {\n            int colB = bCol + i;\n            tileB[threadIdx.y][threadIdx.x * THREAD_TILE + i] =\n                (bRow < n && colB < n) ? B[bRow * n + colB] : 0.0f;\n        }\n        __syncthreads();\n\n        // Compute partial products from the current tile.\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            float aVal = tileA[threadIdx.y][k];\n            for (int i = 0; i < THREAD_TILE; i++) {\n                sum[i] += aVal * tileB[k][threadIdx.x * THREAD_TILE + i];\n            }\n        }\n        __syncthreads();\n    }\n\n    // Write computed tile to global memory.\n    for (int i = 0; i < THREAD_TILE; i++) {\n        int cCol = col + i;\n        if (row < n && cCol < n)\n            C[row * n + cCol] = sum[i];\n    }\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C, bytes);\n\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    // Set block dimensions: \n    // blockDim.x = TILE_WIDTH/THREAD_TILE, blockDim.y = TILE_WIDTH.\n    dim3 block(TILE_WIDTH / THREAD_TILE, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n\n    matMulTiledThreadTile << <grid, block >> > (d_A, d_B, d_C, N);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_C, d_C, bytes, hipMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled thread-tiled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 09/Thread_tiling.cu"}
{"input": "\ufeff// tiled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n\n__global__ void matMulTiled(const float* A, const float* B, float* C, int n) {\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x;\n\n    float sum = 0.0f;\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n    for (int t = 0; t < numTiles; t++) {\n        int tiledCol = t * TILE_WIDTH + threadIdx.x;\n        int tiledRow = t * TILE_WIDTH + threadIdx.y;\n        tileA[threadIdx.y][threadIdx.x] = (row < n && tiledCol < n) ? A[row * n + tiledCol] : 0.0f;\n        tileB[threadIdx.y][threadIdx.x] = (tiledRow < n && col < n) ? B[tiledRow * n + col] : 0.0f;\n        __syncthreads();\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            sum += tileA[threadIdx.y][k] * tileB[k][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    if (row < n && col < n)\n        C[row * n + col] = sum;\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C, bytes);\n\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    dim3 block(TILE_WIDTH, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n    matMulTiled << <grid, block >> > (d_A, d_B, d_C, N);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "output": "\ufeff// tiled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n\n__global__ void matMulTiled(const float* A, const float* B, float* C, int n) {\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x;\n\n    float sum = 0.0f;\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n    for (int t = 0; t < numTiles; t++) {\n        int tiledCol = t * TILE_WIDTH + threadIdx.x;\n        int tiledRow = t * TILE_WIDTH + threadIdx.y;\n        tileA[threadIdx.y][threadIdx.x] = (row < n && tiledCol < n) ? A[row * n + tiledCol] : 0.0f;\n        tileB[threadIdx.y][threadIdx.x] = (tiledRow < n && col < n) ? B[tiledRow * n + col] : 0.0f;\n        __syncthreads();\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            sum += tileA[threadIdx.y][k] * tileB[k][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    if (row < n && col < n)\n        C[row * n + col] = sum;\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C, bytes);\n\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    dim3 block(TILE_WIDTH, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n    matMulTiled << <grid, block >> > (d_A, d_B, d_C, N);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_C, d_C, bytes, hipMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 09/Shared_memory.cu"}
{"input": "\ufeff// tiled_unrolled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n\n__global__ void matMulTiledUnrolled(const float* A, const float* B, float* C, int n) {\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x;\n\n    float sum = 0.0f;\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n    for (int t = 0; t < numTiles; t++) {\n        int tiledCol = t * TILE_WIDTH + threadIdx.x;\n        int tiledRow = t * TILE_WIDTH + threadIdx.y;\n        tileA[threadIdx.y][threadIdx.x] = (row < n && tiledCol < n) ? A[row * n + tiledCol] : 0.0f;\n        tileB[threadIdx.y][threadIdx.x] = (tiledRow < n && col < n) ? B[tiledRow * n + col] : 0.0f;\n        __syncthreads();\n#pragma unroll\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            sum += tileA[threadIdx.y][k] * tileB[k][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    if (row < n && col < n)\n        C[row * n + col] = sum;\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C, bytes);\n\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    dim3 block(TILE_WIDTH, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n    matMulTiledUnrolled << <grid, block >> > (d_A, d_B, d_C, N);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled and unrolled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "output": "\ufeff// tiled_unrolled_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n\n#define N 1024\n#define TILE_WIDTH 16\n\n__global__ void matMulTiledUnrolled(const float* A, const float* B, float* C, int n) {\n    __shared__ float tileA[TILE_WIDTH][TILE_WIDTH];\n    __shared__ float tileB[TILE_WIDTH][TILE_WIDTH];\n\n    int row = blockIdx.y * TILE_WIDTH + threadIdx.y;\n    int col = blockIdx.x * TILE_WIDTH + threadIdx.x;\n\n    float sum = 0.0f;\n    int numTiles = (n + TILE_WIDTH - 1) / TILE_WIDTH;\n    for (int t = 0; t < numTiles; t++) {\n        int tiledCol = t * TILE_WIDTH + threadIdx.x;\n        int tiledRow = t * TILE_WIDTH + threadIdx.y;\n        tileA[threadIdx.y][threadIdx.x] = (row < n && tiledCol < n) ? A[row * n + tiledCol] : 0.0f;\n        tileB[threadIdx.y][threadIdx.x] = (tiledRow < n && col < n) ? B[tiledRow * n + col] : 0.0f;\n        __syncthreads();\n#pragma unroll\n        for (int k = 0; k < TILE_WIDTH; k++) {\n            sum += tileA[threadIdx.y][k] * tileB[k][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    if (row < n && col < n)\n        C[row * n + col] = sum;\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C, bytes);\n\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    dim3 block(TILE_WIDTH, TILE_WIDTH);\n    dim3 grid((N + TILE_WIDTH - 1) / TILE_WIDTH, (N + TILE_WIDTH - 1) / TILE_WIDTH);\n    matMulTiledUnrolled << <grid, block >> > (d_A, d_B, d_C, N);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_C, d_C, bytes, hipMemcpyDeviceToHost);\n\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Tiled and unrolled multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 09/Loop_unrolling.cu"}
{"input": "\ufeff// naive_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n#define N 1024  // matrix dimension: N x N\n\n__global__ void matMulNaive(const float* A, const float* B, float* C, int n) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < n && col < n) {\n        float sum = 0.0f;\n        for (int k = 0; k < n; k++) {\n            sum += A[row * n + k] * B[k * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    // Initialize matrices (for simplicity, A filled with 1's, B with 2's)\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C, bytes);\n\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    dim3 block(16, 16);\n    dim3 grid((N + block.x - 1) / block.x, (N + block.y - 1) / block.y);\n    matMulNaive << <grid, block >> > (d_A, d_B, d_C, N);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);\n\n    // Verify: each element should equal 1*2*N = 2*N.\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Naive multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "output": "\ufeff// naive_matmul.cu\n#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n\n#define N 1024  // matrix dimension: N x N\n\n__global__ void matMulNaive(const float* A, const float* B, float* C, int n) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < n && col < n) {\n        float sum = 0.0f;\n        for (int k = 0; k < n; k++) {\n            sum += A[row * n + k] * B[k * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nint main() {\n    int size = N * N;\n    size_t bytes = size * sizeof(float);\n\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    // Initialize matrices (for simplicity, A filled with 1's, B with 2's)\n    for (int i = 0; i < size; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float* d_A, * d_B, * d_C;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C, bytes);\n\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    dim3 block(16, 16);\n    dim3 grid((N + block.x - 1) / block.x, (N + block.y - 1) / block.y);\n    matMulNaive << <grid, block >> > (d_A, d_B, d_C, N);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_C, d_C, bytes, hipMemcpyDeviceToHost);\n\n    // Verify: each element should equal 1*2*N = 2*N.\n    bool correct = true;\n    for (int i = 0; i < size; i++) {\n        float expected = 2.0f * N;\n        if (fabs(h_C[i] - expected) > 1e-5) {\n            correct = false;\n            printf(\"Mismatch at index %d: got %f, expected %f\\n\", i, h_C[i], expected);\n            break;\n        }\n    }\n    printf(\"Naive multiplication %s\\n\", correct ? \"successful!\" : \"FAILED\");\n\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n    free(h_A); free(h_B); free(h_C);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 09/Naive.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cusparse.h>\n#include <curand_kernel.h>\n\n#define N_STATES 100     // Number of states\n#define N_ACTIONS 4      // Number of actions\n#define EPSILON 0.1f     // Exploration rate\n#define ALPHA 0.1f       // Learning rate\n#define GAMMA 0.9f       // Discount factor\n\n// CSR Data Structures\nstruct CSRMatrix {\n    int* rowPtr;         // Row pointer\n    int* colInd;         // Column indices\n    float* values;       // Non-zero values\n};\n\n__global__ void initQTable(float* Q, int nStates, int nActions) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates * nActions) {\n        curandState randState;\n        curand_init(clock64(), idx, 0, &randState);\n        Q[idx] = curand_uniform(&randState) * 0.1f; // Small random values\n    }\n}\n\n// Random action selection for exploration\n__global__ void epsilonGreedy(float* Q, int* actions, int nStates, int nActions, float epsilon) {\n    int state = blockIdx.x * blockDim.x + threadIdx.x;\n    if (state < nStates) {\n        curandState randState;\n        curand_init(clock64(), state, 0, &randState);\n\n        if (curand_uniform(&randState) < epsilon) {\n            actions[state] = curand(&randState) % nActions; // Random action\n        } else {\n            float maxQ = -1e9;\n            int bestAction = 0;\n\n            for (int a = 0; a < nActions; a++) {\n                if (Q[state * nActions + a] > maxQ) {\n                    maxQ = Q[state * nActions + a];\n                    bestAction = a;\n                }\n            }\n\n            actions[state] = bestAction;\n        }\n    }\n}\n\n// Q-Learning Update\n__global__ void qLearningUpdate(float* Q, int* actions, float* rewards, int* nextStates, \n                                int nStates, int nActions, float alpha, float gamma) {\n    int state = blockIdx.x * blockDim.x + threadIdx.x;\n    if (state < nStates) {\n        int action = actions[state];\n        float reward = rewards[state];\n        int nextState = nextStates[state];\n\n        // Bellman Equation: Q(s, a) = Q(s, a) + \u03b1[R + \u03b3 max Q(s', a') - Q(s, a)]\n        float maxQNext = -1e9;\n        for (int a = 0; a < nActions; a++) {\n            maxQNext = fmaxf(maxQNext, Q[nextState * nActions + a]);\n        }\n\n        Q[state * nActions + action] += alpha * (reward + gamma * maxQNext - Q[state * nActions + action]);\n    }\n}\n\nint main() {\n    // Host data\n    float *h_Q, *d_Q;\n    int *d_actions, *d_nextStates;\n    float *d_rewards;\n\n    // Allocate and initialize Q-table\n    cudaMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_actions, N_STATES * sizeof(int));\n    cudaMalloc(&d_rewards, N_STATES * sizeof(float));\n    cudaMalloc(&d_nextStates, N_STATES * sizeof(int));\n\n    // Initialize Q-table\n    initQTable<<<(N_STATES * N_ACTIONS + 255) / 256, 256>>>(d_Q, N_STATES, N_ACTIONS);\n\n    // Simulate one episode (example only)\n    epsilonGreedy<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, EPSILON);\n\n    qLearningUpdate<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, d_rewards, d_nextStates,\n                                                     N_STATES, N_ACTIONS, ALPHA, GAMMA);\n\n    // Copy back and print Q-table\n    h_Q = new float[N_STATES * N_ACTIONS];\n    cudaMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Final Q-table (Sample):\\n\";\n    for (int i = 0; i < 5; ++i) {\n        for (int j = 0; j < N_ACTIONS; ++j) {\n            std::cout << h_Q[i * N_ACTIONS + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    // Cleanup\n    cudaFree(d_Q);\n    cudaFree(d_actions);\n    cudaFree(d_rewards);\n    cudaFree(d_nextStates);\n    delete[] h_Q;\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <hipsparse.h>\n#include <hiprand/hiprand_kernel.h>\n\n#define N_STATES 100     // Number of states\n#define N_ACTIONS 4      // Number of actions\n#define EPSILON 0.1f     // Exploration rate\n#define ALPHA 0.1f       // Learning rate\n#define GAMMA 0.9f       // Discount factor\n\n// CSR Data Structures\nstruct CSRMatrix {\n    int* rowPtr;         // Row pointer\n    int* colInd;         // Column indices\n    float* values;       // Non-zero values\n};\n\n__global__ void initQTable(float* Q, int nStates, int nActions) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates * nActions) {\n        hiprandState randState;\n        hiprand_init(clock64(), idx, 0, &randState);\n        Q[idx] = hiprand_uniform(&randState) * 0.1f; // Small random values\n    }\n}\n\n// Random action selection for exploration\n__global__ void epsilonGreedy(float* Q, int* actions, int nStates, int nActions, float epsilon) {\n    int state = blockIdx.x * blockDim.x + threadIdx.x;\n    if (state < nStates) {\n        hiprandState randState;\n        hiprand_init(clock64(), state, 0, &randState);\n\n        if (hiprand_uniform(&randState) < epsilon) {\n            actions[state] = hiprand(&randState) % nActions; // Random action\n        } else {\n            float maxQ = -1e9;\n            int bestAction = 0;\n\n            for (int a = 0; a < nActions; a++) {\n                if (Q[state * nActions + a] > maxQ) {\n                    maxQ = Q[state * nActions + a];\n                    bestAction = a;\n                }\n            }\n\n            actions[state] = bestAction;\n        }\n    }\n}\n\n// Q-Learning Update\n__global__ void qLearningUpdate(float* Q, int* actions, float* rewards, int* nextStates, \n                                int nStates, int nActions, float alpha, float gamma) {\n    int state = blockIdx.x * blockDim.x + threadIdx.x;\n    if (state < nStates) {\n        int action = actions[state];\n        float reward = rewards[state];\n        int nextState = nextStates[state];\n\n        // Bellman Equation: Q(s, a) = Q(s, a) + \u03b1[R + \u03b3 max Q(s', a') - Q(s, a)]\n        float maxQNext = -1e9;\n        for (int a = 0; a < nActions; a++) {\n            maxQNext = fmaxf(maxQNext, Q[nextState * nActions + a]);\n        }\n\n        Q[state * nActions + action] += alpha * (reward + gamma * maxQNext - Q[state * nActions + action]);\n    }\n}\n\nint main() {\n    // Host data\n    float *h_Q, *d_Q;\n    int *d_actions, *d_nextStates;\n    float *d_rewards;\n\n    // Allocate and initialize Q-table\n    hipMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_actions, N_STATES * sizeof(int));\n    hipMalloc(&d_rewards, N_STATES * sizeof(float));\n    hipMalloc(&d_nextStates, N_STATES * sizeof(int));\n\n    // Initialize Q-table\n    initQTable<<<(N_STATES * N_ACTIONS + 255) / 256, 256>>>(d_Q, N_STATES, N_ACTIONS);\n\n    // Simulate one episode (example only)\n    epsilonGreedy<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, EPSILON);\n\n    qLearningUpdate<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, d_rewards, d_nextStates,\n                                                     N_STATES, N_ACTIONS, ALPHA, GAMMA);\n\n    // Copy back and print Q-table\n    h_Q = new float[N_STATES * N_ACTIONS];\n    hipMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyDeviceToHost);\n\n    std::cout << \"Final Q-table (Sample):\\n\";\n    for (int i = 0; i < 5; ++i) {\n        for (int j = 0; j < N_ACTIONS; ++j) {\n            std::cout << h_Q[i * N_ACTIONS + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    // Cleanup\n    hipFree(d_Q);\n    hipFree(d_actions);\n    hipFree(d_rewards);\n    hipFree(d_nextStates);\n    delete[] h_Q;\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 50/RL_CSR_SPARPSE.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#define TILE_WIDTH 16\n\n__global__ void conv1D(float *input, float *output, float *mask, int inputSize, int maskSize) {\n    __shared__ float sharedInput[TILE_WIDTH + 2];\n\n    int tx = threadIdx.x;\n    int bx = blockIdx.x;\n    int index = bx * TILE_WIDTH + tx;\n\n    int maskRadius = maskSize / 2;\n\n    if (index < inputSize) {\n        sharedInput[tx + maskRadius] = input[index];\n        if (tx < maskRadius) {\n            if (index >= maskRadius) {\n                sharedInput[tx] = input[index - maskRadius];\n            } else {\n                sharedInput[tx] = 0.0f;\n            }\n            if (index + TILE_WIDTH < inputSize) {\n                sharedInput[tx + TILE_WIDTH + maskRadius] = input[index + TILE_WIDTH];\n            } else {\n                sharedInput[tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if (index < inputSize) {\n        float result = 0.0f;\n        for (int i = 0; i < maskSize; i++) {\n            result += sharedInput[tx + i] * mask[i];\n        }\n        output[index] = result;\n    }\n}\n\nvoid measureGflops(float *input, float *output, float *mask, int inputSize, int maskSize) {\n    float *d_input, *d_output, *d_mask;\n    cudaMalloc(&d_input, inputSize * sizeof(float));\n    cudaMalloc(&d_output, inputSize * sizeof(float));\n    cudaMalloc(&d_mask, maskSize * sizeof(float));\n\n    cudaMemcpy(d_input, input, inputSize * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_mask, mask, maskSize * sizeof(float), cudaMemcpyHostToDevice);\n\n    dim3 dimBlock(TILE_WIDTH, 1, 1);\n    dim3 dimGrid((inputSize + TILE_WIDTH - 1) / TILE_WIDTH, 1, 1);\n\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    cudaEventRecord(start);\n    conv1D<<<dimGrid, dimBlock>>>(d_input, d_output, d_mask, inputSize, maskSize);\n    cudaEventRecord(stop);\n\n    cudaMemcpy(output, d_output, inputSize * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaEventSynchronize(stop);\n\n    float milliseconds = 0;\n    cudaEventElapsedTime(&milliseconds, start, stop);\n\n    \n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"GFLOPS: %f\\n\", 2.0 * inputSize * maskSize / (milliseconds * 1e6));\n    printf(\"Output: %f\\n\", output[0]);\n    printf(\"inputSize: %d\\n\", inputSize);\n    printf(\"maskSize: %d\\n\", maskSize);\n   // std::cout << \"GFLOPS: \" << gflops << std::endl;\n\n    cudaFree(d_input);\n    cudaFree(d_output);\n    cudaFree(d_mask);\n}\n\nint main() {\n    int inputSize = 1024;\n    int maskSize = 5;\n\n    float *input = new float[inputSize];\n    float *output = new float[inputSize];\n    float *mask = new float[maskSize];\n\n    for (int i = 0; i < inputSize; i++) {\n        input[i] = static_cast<float>(i);  // 1.0f;\n    }\n\n    for (int i = 0; i < maskSize; i++) {\n        mask[i] = static_cast<float>(i); // 1.0f;\n    }\n\n    measureGflops(input, output, mask, inputSize, maskSize);\n\n    delete[] input;\n    delete[] output;\n    delete[] mask;\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#define TILE_WIDTH 16\n\n__global__ void conv1D(float *input, float *output, float *mask, int inputSize, int maskSize) {\n    __shared__ float sharedInput[TILE_WIDTH + 2];\n\n    int tx = threadIdx.x;\n    int bx = blockIdx.x;\n    int index = bx * TILE_WIDTH + tx;\n\n    int maskRadius = maskSize / 2;\n\n    if (index < inputSize) {\n        sharedInput[tx + maskRadius] = input[index];\n        if (tx < maskRadius) {\n            if (index >= maskRadius) {\n                sharedInput[tx] = input[index - maskRadius];\n            } else {\n                sharedInput[tx] = 0.0f;\n            }\n            if (index + TILE_WIDTH < inputSize) {\n                sharedInput[tx + TILE_WIDTH + maskRadius] = input[index + TILE_WIDTH];\n            } else {\n                sharedInput[tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if (index < inputSize) {\n        float result = 0.0f;\n        for (int i = 0; i < maskSize; i++) {\n            result += sharedInput[tx + i] * mask[i];\n        }\n        output[index] = result;\n    }\n}\n\nvoid measureGflops(float *input, float *output, float *mask, int inputSize, int maskSize) {\n    float *d_input, *d_output, *d_mask;\n    hipMalloc(&d_input, inputSize * sizeof(float));\n    hipMalloc(&d_output, inputSize * sizeof(float));\n    hipMalloc(&d_mask, maskSize * sizeof(float));\n\n    hipMemcpy(d_input, input, inputSize * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_mask, mask, maskSize * sizeof(float), hipMemcpyHostToDevice);\n\n    dim3 dimBlock(TILE_WIDTH, 1, 1);\n    dim3 dimGrid((inputSize + TILE_WIDTH - 1) / TILE_WIDTH, 1, 1);\n\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    hipEventRecord(start);\n    conv1D<<<dimGrid, dimBlock>>>(d_input, d_output, d_mask, inputSize, maskSize);\n    hipEventRecord(stop);\n\n    hipMemcpy(output, d_output, inputSize * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipEventSynchronize(stop);\n\n    float milliseconds = 0;\n    hipEventElapsedTime(&milliseconds, start, stop);\n\n    \n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"GFLOPS: %f\\n\", 2.0 * inputSize * maskSize / (milliseconds * 1e6));\n    printf(\"Output: %f\\n\", output[0]);\n    printf(\"inputSize: %d\\n\", inputSize);\n    printf(\"maskSize: %d\\n\", maskSize);\n   // std::cout << \"GFLOPS: \" << gflops << std::endl;\n\n    hipFree(d_input);\n    hipFree(d_output);\n    hipFree(d_mask);\n}\n\nint main() {\n    int inputSize = 1024;\n    int maskSize = 5;\n\n    float *input = new float[inputSize];\n    float *output = new float[inputSize];\n    float *mask = new float[maskSize];\n\n    for (int i = 0; i < inputSize; i++) {\n        input[i] = static_cast<float>(i);  // 1.0f;\n    }\n\n    for (int i = 0; i < maskSize; i++) {\n        mask[i] = static_cast<float>(i); // 1.0f;\n    }\n\n    measureGflops(input, output, mask, inputSize, maskSize);\n\n    delete[] input;\n    delete[] output;\n    delete[] mask;\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 20/1d_convulution.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#define TILE_WIDTH 16\n\n__global__ void conv2D(float *input, float *output, float *mask, int maskSize, int inputSize) {\n    __shared__ float sharedInput[TILE_WIDTH + 2][TILE_WIDTH + 2];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    int row = by * TILE_WIDTH + ty;\n    int col = bx * TILE_WIDTH + tx;\n\n    int maskRadius = maskSize / 2;\n\n    if (row < inputSize && col < inputSize) {\n        sharedInput[ty + maskRadius][tx + maskRadius] = input[row * inputSize + col];\n        if (ty < maskRadius) {\n            if (row >= maskRadius) {\n                sharedInput[ty][tx + maskRadius] = input[(row - maskRadius) * inputSize + col];\n            } else {\n                sharedInput[ty][tx + maskRadius] = 0.0f;\n            }\n            if (row + TILE_WIDTH < inputSize) {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + maskRadius] = input[(row + TILE_WIDTH) * inputSize + col];\n            } else {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + maskRadius] = 0.0f;\n            }\n        }\n        if (tx < maskRadius) {\n            if (col >= maskRadius) {\n                sharedInput[ty + maskRadius][tx] = input[row * inputSize + col - maskRadius];\n            } else {\n                sharedInput[ty + maskRadius][tx] = 0.0f;\n            }\n            if (col + TILE_WIDTH < inputSize) {\n                sharedInput[ty + maskRadius][tx + TILE_WIDTH + maskRadius] = input[row * inputSize + col + TILE_WIDTH];\n            } else {\n                sharedInput[ty + maskRadius][tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n        if (ty < maskRadius && tx < maskRadius) {\n            if (row >= maskRadius && col >= maskRadius) {\n                sharedInput[ty][tx] = input[(row - maskRadius) * inputSize + col - maskRadius];\n            } else {\n                sharedInput[ty][tx] = 0.0f;\n            }\n            if (row + TILE_WIDTH < inputSize && col + TILE_WIDTH < inputSize) {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + TILE_WIDTH + maskRadius] = input[(row + TILE_WIDTH) * inputSize + col + TILE_WIDTH];\n            } else {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if (row < inputSize && col < inputSize) {\n        float result = 0.0f;\n        for (int i = 0; i < maskSize; i++) {\n            for (int j = 0; j < maskSize; j++) {\n                result += sharedInput[ty + i][tx + j] * mask[i * maskSize + j];\n            }\n        }\n        output[row * inputSize + col] = result;\n    }\n}\n\nvoid measureGflops(float *input, float *output, float *mask, int maskSize, int inputSize) {\n    float *d_input, *d_output, *d_mask;\n    cudaMalloc(&d_input, inputSize * inputSize * sizeof(float));\n    cudaMalloc(&d_output, inputSize * inputSize * sizeof(float));\n    cudaMalloc(&d_mask, maskSize * maskSize * sizeof(float));\n\n    cudaMemcpy(d_input, input, inputSize * inputSize * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_mask, mask, maskSize * maskSize * sizeof(float), cudaMemcpyHostToDevice);\n\n    dim3 dimBlock(TILE_WIDTH, TILE_WIDTH, 1);\n    dim3 dimGrid((inputSize + TILE_WIDTH - 1) / TILE_WIDTH, (inputSize + TILE_WIDTH - 1) / TILE_WIDTH, 1);\n\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    cudaEventRecord(start);\n    conv2D<<<dimGrid, dimBlock>>>(d_input, d_output, d_mask, maskSize, inputSize);\n    cudaEventRecord(stop);\n\n    cudaMemcpy(output, d_output, inputSize * inputSize * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaEventSynchronize(stop);\n\n    float milliseconds = 0;\n    cudaEventElapsedTime(&milliseconds, start, stop);\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"GFLOPS: %f\\n\", 2.0 * inputSize * inputSize * maskSize * maskSize / milliseconds / 1e6);\n    printf(\"Output: %f\\n\", output[0]);\n    std::cout << \"inputSize: \" << inputSize << std::endl;\n    std::cout << \"maskSize: \" << maskSize << std::endl;\n\n    cudaFree(d_input);\n    cudaFree(d_output);\n    cudaFree(d_mask);\n}\n\nint main() {\n    int inputSize = 1024;\n    int maskSize = 5;\n\n    float *input = new float[inputSize * inputSize];\n    float *output = new float[inputSize * inputSize];\n    float *mask = new float[maskSize * maskSize];\n\n    for (int i = 0; i < inputSize * inputSize; i++) {\n        input[i] = static_cast<float>(i);\n    }\n\n    for (int i = 0; i < maskSize * maskSize; i++) {\n        mask[i] = static_cast<float>(i);\n    }\n\n    measureGflops(input, output, mask, maskSize, inputSize);\n\n    delete[] input;\n    delete[] output;\n    delete[] mask;\n\n    return 0;\n}", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#define TILE_WIDTH 16\n\n__global__ void conv2D(float *input, float *output, float *mask, int maskSize, int inputSize) {\n    __shared__ float sharedInput[TILE_WIDTH + 2][TILE_WIDTH + 2];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int bx = blockIdx.x;\n    int by = blockIdx.y;\n\n    int row = by * TILE_WIDTH + ty;\n    int col = bx * TILE_WIDTH + tx;\n\n    int maskRadius = maskSize / 2;\n\n    if (row < inputSize && col < inputSize) {\n        sharedInput[ty + maskRadius][tx + maskRadius] = input[row * inputSize + col];\n        if (ty < maskRadius) {\n            if (row >= maskRadius) {\n                sharedInput[ty][tx + maskRadius] = input[(row - maskRadius) * inputSize + col];\n            } else {\n                sharedInput[ty][tx + maskRadius] = 0.0f;\n            }\n            if (row + TILE_WIDTH < inputSize) {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + maskRadius] = input[(row + TILE_WIDTH) * inputSize + col];\n            } else {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + maskRadius] = 0.0f;\n            }\n        }\n        if (tx < maskRadius) {\n            if (col >= maskRadius) {\n                sharedInput[ty + maskRadius][tx] = input[row * inputSize + col - maskRadius];\n            } else {\n                sharedInput[ty + maskRadius][tx] = 0.0f;\n            }\n            if (col + TILE_WIDTH < inputSize) {\n                sharedInput[ty + maskRadius][tx + TILE_WIDTH + maskRadius] = input[row * inputSize + col + TILE_WIDTH];\n            } else {\n                sharedInput[ty + maskRadius][tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n        if (ty < maskRadius && tx < maskRadius) {\n            if (row >= maskRadius && col >= maskRadius) {\n                sharedInput[ty][tx] = input[(row - maskRadius) * inputSize + col - maskRadius];\n            } else {\n                sharedInput[ty][tx] = 0.0f;\n            }\n            if (row + TILE_WIDTH < inputSize && col + TILE_WIDTH < inputSize) {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + TILE_WIDTH + maskRadius] = input[(row + TILE_WIDTH) * inputSize + col + TILE_WIDTH];\n            } else {\n                sharedInput[ty + TILE_WIDTH + maskRadius][tx + TILE_WIDTH + maskRadius] = 0.0f;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    if (row < inputSize && col < inputSize) {\n        float result = 0.0f;\n        for (int i = 0; i < maskSize; i++) {\n            for (int j = 0; j < maskSize; j++) {\n                result += sharedInput[ty + i][tx + j] * mask[i * maskSize + j];\n            }\n        }\n        output[row * inputSize + col] = result;\n    }\n}\n\nvoid measureGflops(float *input, float *output, float *mask, int maskSize, int inputSize) {\n    float *d_input, *d_output, *d_mask;\n    hipMalloc(&d_input, inputSize * inputSize * sizeof(float));\n    hipMalloc(&d_output, inputSize * inputSize * sizeof(float));\n    hipMalloc(&d_mask, maskSize * maskSize * sizeof(float));\n\n    hipMemcpy(d_input, input, inputSize * inputSize * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_mask, mask, maskSize * maskSize * sizeof(float), hipMemcpyHostToDevice);\n\n    dim3 dimBlock(TILE_WIDTH, TILE_WIDTH, 1);\n    dim3 dimGrid((inputSize + TILE_WIDTH - 1) / TILE_WIDTH, (inputSize + TILE_WIDTH - 1) / TILE_WIDTH, 1);\n\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    hipEventRecord(start);\n    conv2D<<<dimGrid, dimBlock>>>(d_input, d_output, d_mask, maskSize, inputSize);\n    hipEventRecord(stop);\n\n    hipMemcpy(output, d_output, inputSize * inputSize * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipEventSynchronize(stop);\n\n    float milliseconds = 0;\n    hipEventElapsedTime(&milliseconds, start, stop);\n\n    printf(\"Time: %f ms\\n\", milliseconds);\n    printf(\"GFLOPS: %f\\n\", 2.0 * inputSize * inputSize * maskSize * maskSize / milliseconds / 1e6);\n    printf(\"Output: %f\\n\", output[0]);\n    std::cout << \"inputSize: \" << inputSize << std::endl;\n    std::cout << \"maskSize: \" << maskSize << std::endl;\n\n    hipFree(d_input);\n    hipFree(d_output);\n    hipFree(d_mask);\n}\n\nint main() {\n    int inputSize = 1024;\n    int maskSize = 5;\n\n    float *input = new float[inputSize * inputSize];\n    float *output = new float[inputSize * inputSize];\n    float *mask = new float[maskSize * maskSize];\n\n    for (int i = 0; i < inputSize * inputSize; i++) {\n        input[i] = static_cast<float>(i);\n    }\n\n    for (int i = 0; i < maskSize * maskSize; i++) {\n        mask[i] = static_cast<float>(i);\n    }\n\n    measureGflops(input, output, mask, maskSize, inputSize);\n\n    delete[] input;\n    delete[] output;\n    delete[] mask;\n\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 20/2d_convultion.cu"}
{"input": "#include <stdio.h>\n#include <curand_kernel.h>\n\n#define N_STATES 9\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n#define EPSILON 0.1f\n#define EPISODES 1000\n\n__device__ float getExpectedQ(float* Q, int state) {\n    float expectedQ = 0.0f;\n    for (int a = 0; a < N_ACTIONS; a++) {\n        expectedQ += (1.0f / N_ACTIONS) * Q[state * N_ACTIONS + a]; // Uniform policy assumption\n    }\n    return expectedQ;\n}\n\n__global__ void expectedSARSA(float* Q, int* transitions, float* rewards, curandState* states) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    if (tid >= N_STATES) return;\n\n    curandState localState = states[tid];\n\n    for (int episode = 0; episode < EPISODES; episode++) {\n        int state = tid;\n        int action = curand(&localState) % N_ACTIONS;\n\n        int next_state = transitions[state * N_ACTIONS + action];\n        float reward = rewards[state * N_ACTIONS + action];\n\n        float expectedQ = getExpectedQ(Q, next_state);\n\n        // Update Q-value\n        Q[state * N_ACTIONS + action] += ALPHA * (reward + GAMMA * expectedQ - Q[state * N_ACTIONS + action]);\n    }\n    states[tid] = localState;\n}\n\nint main() {\n    float h_Q[N_STATES * N_ACTIONS] = {0};\n    int h_transitions[N_STATES * N_ACTIONS] = { // Fake transition table\n        1, 3, 0, 0, 2, 4, 1, 1, 3, 5, 2, 2,\n        4, 6, 3, 3, 5, 7, 4, 4, 6, 8, 5, 5,\n        7, 7, 6, 6, 8, 8, 7, 7, 8, 8, 8, 8\n    };\n    float h_rewards[N_STATES * N_ACTIONS] = { // Fake reward table\n        0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n        0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0\n    };\n\n    float* d_Q;\n    int* d_transitions;\n    float* d_rewards;\n    curandState* d_states;\n\n    cudaMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_transitions, N_STATES * N_ACTIONS * sizeof(int));\n    cudaMalloc(&d_rewards, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_states, N_STATES * sizeof(curandState));\n\n    cudaMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, h_transitions, N_STATES * N_ACTIONS * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, h_rewards, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n\n    expectedSARSA<<<1, N_STATES>>>(d_Q, d_transitions, d_rewards, d_states);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyDeviceToHost);\n\n    printf(\"Expected SARSA Q-values:\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", h_Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    cudaFree(d_Q);\n    cudaFree(d_transitions);\n    cudaFree(d_rewards);\n    cudaFree(d_states);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <hiprand/hiprand_kernel.h>\n\n#define N_STATES 9\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n#define EPSILON 0.1f\n#define EPISODES 1000\n\n__device__ float getExpectedQ(float* Q, int state) {\n    float expectedQ = 0.0f;\n    for (int a = 0; a < N_ACTIONS; a++) {\n        expectedQ += (1.0f / N_ACTIONS) * Q[state * N_ACTIONS + a]; // Uniform policy assumption\n    }\n    return expectedQ;\n}\n\n__global__ void expectedSARSA(float* Q, int* transitions, float* rewards, hiprandState* states) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    if (tid >= N_STATES) return;\n\n    hiprandState localState = states[tid];\n\n    for (int episode = 0; episode < EPISODES; episode++) {\n        int state = tid;\n        int action = hiprand(&localState) % N_ACTIONS;\n\n        int next_state = transitions[state * N_ACTIONS + action];\n        float reward = rewards[state * N_ACTIONS + action];\n\n        float expectedQ = getExpectedQ(Q, next_state);\n\n        // Update Q-value\n        Q[state * N_ACTIONS + action] += ALPHA * (reward + GAMMA * expectedQ - Q[state * N_ACTIONS + action]);\n    }\n    states[tid] = localState;\n}\n\nint main() {\n    float h_Q[N_STATES * N_ACTIONS] = {0};\n    int h_transitions[N_STATES * N_ACTIONS] = { // Fake transition table\n        1, 3, 0, 0, 2, 4, 1, 1, 3, 5, 2, 2,\n        4, 6, 3, 3, 5, 7, 4, 4, 6, 8, 5, 5,\n        7, 7, 6, 6, 8, 8, 7, 7, 8, 8, 8, 8\n    };\n    float h_rewards[N_STATES * N_ACTIONS] = { // Fake reward table\n        0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n        0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0\n    };\n\n    float* d_Q;\n    int* d_transitions;\n    float* d_rewards;\n    hiprandState* d_states;\n\n    hipMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_transitions, N_STATES * N_ACTIONS * sizeof(int));\n    hipMalloc(&d_rewards, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_states, N_STATES * sizeof(hiprandState));\n\n    hipMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, h_transitions, N_STATES * N_ACTIONS * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, h_rewards, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n\n    expectedSARSA<<<1, N_STATES>>>(d_Q, d_transitions, d_rewards, d_states);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyDeviceToHost);\n\n    printf(\"Expected SARSA Q-values:\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", h_Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    hipFree(d_Q);\n    hipFree(d_transitions);\n    hipFree(d_rewards);\n    hipFree(d_states);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 57/sarsaQ.cu"}
{"input": "#include <stdio.h>\n#include <curand_kernel.h>\n\n#define N_STATES 9\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n#define EPSILON 0.1f\n#define EPISODES 1000\n\n__device__ float getExpectedQ(float* Q, int state) {\n    float expectedQ = 0.0f;\n    for (int a = 0; a < N_ACTIONS; a++) {\n        expectedQ += (1.0f / N_ACTIONS) * Q[state * N_ACTIONS + a]; // Uniform policy assumption\n    }\n    return expectedQ;\n}\n\n__global__ void expectedSARSA(float* Q, int* transitions, float* rewards, curandState* states) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    if (tid >= N_STATES) return;\n\n    curandState localState = states[tid];\n\n    for (int episode = 0; episode < EPISODES; episode++) {\n        int state = tid;\n        int action = curand(&localState) % N_ACTIONS;\n\n        int next_state = transitions[state * N_ACTIONS + action];\n        float reward = rewards[state * N_ACTIONS + action];\n\n        float expectedQ = getExpectedQ(Q, next_state);\n\n        // Update Q-value\n        Q[state * N_ACTIONS + action] += ALPHA * (reward + GAMMA * expectedQ - Q[state * N_ACTIONS + action]);\n    }\n    states[tid] = localState;\n}\n\nint main() {\n    float h_Q[N_STATES * N_ACTIONS] = {0};\n    int h_transitions[N_STATES * N_ACTIONS] = { // Fake transition table\n        1, 3, 0, 0, 2, 4, 1, 1, 3, 5, 2, 2,\n        4, 6, 3, 3, 5, 7, 4, 4, 6, 8, 5, 5,\n        7, 7, 6, 6, 8, 8, 7, 7, 8, 8, 8, 8\n    };\n    float h_rewards[N_STATES * N_ACTIONS] = { // Fake reward table\n        0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n        0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0\n    };\n\n    float* d_Q;\n    int* d_transitions;\n    float* d_rewards;\n    curandState* d_states;\n\n    cudaMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_transitions, N_STATES * N_ACTIONS * sizeof(int));\n    cudaMalloc(&d_rewards, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_states, N_STATES * sizeof(curandState));\n\n    cudaMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, h_transitions, N_STATES * N_ACTIONS * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, h_rewards, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n\n    expectedSARSA<<<1, N_STATES>>>(d_Q, d_transitions, d_rewards, d_states);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyDeviceToHost);\n\n    printf(\"Expected SARSA Q-values:\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", h_Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    cudaFree(d_Q);\n    cudaFree(d_transitions);\n    cudaFree(d_rewards);\n    cudaFree(d_states);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <hiprand/hiprand_kernel.h>\n\n#define N_STATES 9\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n#define EPSILON 0.1f\n#define EPISODES 1000\n\n__device__ float getExpectedQ(float* Q, int state) {\n    float expectedQ = 0.0f;\n    for (int a = 0; a < N_ACTIONS; a++) {\n        expectedQ += (1.0f / N_ACTIONS) * Q[state * N_ACTIONS + a]; // Uniform policy assumption\n    }\n    return expectedQ;\n}\n\n__global__ void expectedSARSA(float* Q, int* transitions, float* rewards, hiprandState* states) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    if (tid >= N_STATES) return;\n\n    hiprandState localState = states[tid];\n\n    for (int episode = 0; episode < EPISODES; episode++) {\n        int state = tid;\n        int action = hiprand(&localState) % N_ACTIONS;\n\n        int next_state = transitions[state * N_ACTIONS + action];\n        float reward = rewards[state * N_ACTIONS + action];\n\n        float expectedQ = getExpectedQ(Q, next_state);\n\n        // Update Q-value\n        Q[state * N_ACTIONS + action] += ALPHA * (reward + GAMMA * expectedQ - Q[state * N_ACTIONS + action]);\n    }\n    states[tid] = localState;\n}\n\nint main() {\n    float h_Q[N_STATES * N_ACTIONS] = {0};\n    int h_transitions[N_STATES * N_ACTIONS] = { // Fake transition table\n        1, 3, 0, 0, 2, 4, 1, 1, 3, 5, 2, 2,\n        4, 6, 3, 3, 5, 7, 4, 4, 6, 8, 5, 5,\n        7, 7, 6, 6, 8, 8, 7, 7, 8, 8, 8, 8\n    };\n    float h_rewards[N_STATES * N_ACTIONS] = { // Fake reward table\n        0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,\n        0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0\n    };\n\n    float* d_Q;\n    int* d_transitions;\n    float* d_rewards;\n    hiprandState* d_states;\n\n    hipMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_transitions, N_STATES * N_ACTIONS * sizeof(int));\n    hipMalloc(&d_rewards, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_states, N_STATES * sizeof(hiprandState));\n\n    hipMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, h_transitions, N_STATES * N_ACTIONS * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, h_rewards, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n\n    expectedSARSA<<<1, N_STATES>>>(d_Q, d_transitions, d_rewards, d_states);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyDeviceToHost);\n\n    printf(\"Expected SARSA Q-values:\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", h_Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    hipFree(d_Q);\n    hipFree(d_transitions);\n    hipFree(d_rewards);\n    hipFree(d_states);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 57/sarsa.cu"}
{"input": "\ufeff#include <stdio.h>\n#include <cuda_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <device_launch_parameters.h>\n\n__global__ void vectorAddUM(int* a, int* b, int* c, int n) {\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\n\tif (tid < n) {\n\t\tc[tid] = a[tid] + b[tid];\n\t}\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid init_vector(int* a, int* b, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t\ta[i] = rand() % 100;\n\n\t}\n}\nvoid check_answer(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n\n\t//get ID from cuda Calls \n\tint id = cudaGetDevice(&id);\n\n\t//elements per array\n\tint n = 1 << 16;\n\n\t//declare unified memory pointers\n\n\tint* a, * b, * c;\n\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\n\tcudaMallocManaged(&a, bytes);\n\tcudaMallocManaged(&b, bytes);\n\tcudaMallocManaged(&c, bytes);\n\n\t//initialize vectors \n\tinit_vector(a, b, n);\n\n\t//threadblock size\n\n\tint BLOCK_SIZE = 256;\n\n\t//Grid SIze\n\n\tint GRID_SIZE = (int)ceil(n / BLOCK_SIZE);\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAddUM << <GRID_SIZE, BLOCK_SIZE >> > (a, b, c, n);\n\n\t//waitforall operations\n\n\tcudaDeviceSynchronize();\n\n\tcheck_answer(a, b, c, n);\n\n\tprintf(\"completed successfully , result of sum is %d\\n\", c);\n\n\treturn 0;\n}\n", "output": "\ufeff#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <>\n\n__global__ void vectorAddUM(int* a, int* b, int* c, int n) {\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\n\tif (tid < n) {\n\t\tc[tid] = a[tid] + b[tid];\n\t}\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid init_vector(int* a, int* b, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t\ta[i] = rand() % 100;\n\n\t}\n}\nvoid check_answer(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n\n\t//get ID from cuda Calls \n\tint id = hipGetDevice(&id);\n\n\t//elements per array\n\tint n = 1 << 16;\n\n\t//declare unified memory pointers\n\n\tint* a, * b, * c;\n\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\n\thipMallocManaged(&a, bytes);\n\thipMallocManaged(&b, bytes);\n\thipMallocManaged(&c, bytes);\n\n\t//initialize vectors \n\tinit_vector(a, b, n);\n\n\t//threadblock size\n\n\tint BLOCK_SIZE = 256;\n\n\t//Grid SIze\n\n\tint GRID_SIZE = (int)ceil(n / BLOCK_SIZE);\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAddUM << <GRID_SIZE, BLOCK_SIZE >> > (a, b, c, n);\n\n\t//waitforall operations\n\n\thipDeviceSynchronize();\n\n\tcheck_answer(a, b, c, n);\n\n\tprintf(\"completed successfully , result of sum is %d\\n\", c);\n\n\treturn 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 04/UnifiedMemory_VectorAddition.cu"}
{"input": "\ufeff#include <stdio.h>\n#include <cuda_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <device_launch_parameters.h>\n\n__global__ void vectorAdd(int* a, int* b, int* c, int n){\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\nif (tid < n) {\n\tc[tid] = a[tid] + b[tid];\n\t}\t\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid matrix_init(int* a, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t}\n}\n\nvoid error_check(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n//vector of size of 2^16 (65536 elements)\n\tint n = 1 << 16;\n\t//Host vector pointers\n\t\tint* h_a, * h_b, * h_c;\n\t\t//device vector pointer\n\t\tint* d_a, * d_b, * d_c;\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\th_a = (int*)malloc(bytes);\n\th_b = (int*)malloc(bytes);\n\th_c = (int*)malloc(bytes);\n\n\t//Allocate device memory\n\tcudaMalloc(&d_a, bytes);\n\tcudaMalloc(&d_b, bytes);\n\tcudaMalloc(&d_c, bytes);\n\n\t//initialize vectors a and b with random values b/w 0 -99\n\tmatrix_init(h_a, n);\n\tmatrix_init(h_b, n);\n\n\t//copy data from \n\n\tcudaMemcpy(d_a, h_a, bytes, cudaMemcpyHostToDevice);\n\tcudaMemcpy(d_b, h_b, bytes, cudaMemcpyHostToDevice);\n\n\t//threadblock size\n\n\tint NUM_THREADS = 256;\n\n\t\t//Grid SIze\n\n\tint NUM_BLOCKS = (int)ceil(n / NUM_THREADS);\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAdd <<<NUM_BLOCKS, NUM_THREADS>>> (d_a, d_b, d_c, n);\n\n\t//copy sum vector from device to host \n\t\n\tcudaMemcpy(h_c, d_c, bytes, cudaMemcpyDeviceToHost);\n\n\t//check result for error \n\terror_check(h_a, h_b, h_c, n);\n\n\tprintf(\"Result is %d\\n\", d_c);\n\n\tcudaFree(h_a);\n\tcudaFree(h_b);\n\tcudaFree(h_c);\n\t\t\n\treturn 0;\n}\n", "output": "\ufeff#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <>\n\n__global__ void vectorAdd(int* a, int* b, int* c, int n){\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\nif (tid < n) {\n\tc[tid] = a[tid] + b[tid];\n\t}\t\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid matrix_init(int* a, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t}\n}\n\nvoid error_check(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n//vector of size of 2^16 (65536 elements)\n\tint n = 1 << 16;\n\t//Host vector pointers\n\t\tint* h_a, * h_b, * h_c;\n\t\t//device vector pointer\n\t\tint* d_a, * d_b, * d_c;\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\th_a = (int*)malloc(bytes);\n\th_b = (int*)malloc(bytes);\n\th_c = (int*)malloc(bytes);\n\n\t//Allocate device memory\n\thipMalloc(&d_a, bytes);\n\thipMalloc(&d_b, bytes);\n\thipMalloc(&d_c, bytes);\n\n\t//initialize vectors a and b with random values b/w 0 -99\n\tmatrix_init(h_a, n);\n\tmatrix_init(h_b, n);\n\n\t//copy data from \n\n\thipMemcpy(d_a, h_a, bytes, hipMemcpyHostToDevice);\n\thipMemcpy(d_b, h_b, bytes, hipMemcpyHostToDevice);\n\n\t//threadblock size\n\n\tint NUM_THREADS = 256;\n\n\t\t//Grid SIze\n\n\tint NUM_BLOCKS = (int)ceil(n / NUM_THREADS);\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAdd <<<NUM_BLOCKS, NUM_THREADS>>> (d_a, d_b, d_c, n);\n\n\t//copy sum vector from device to host \n\t\n\thipMemcpy(h_c, d_c, bytes, hipMemcpyDeviceToHost);\n\n\t//check result for error \n\terror_check(h_a, h_b, h_c, n);\n\n\tprintf(\"Result is %d\\n\", d_c);\n\n\thipFree(h_a);\n\thipFree(h_b);\n\thipFree(h_c);\n\t\t\n\treturn 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 04/VectorAdd_withErrorCheck.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <cuda_runtime.h>\n\n#define N 10000000  // Vector size = 10 million\n#define BLOCK_SIZE 256\n\n// Example:\n// A = [1, 2, 3, 4, 5]\n// B = [6, 7, 8, 9, 10]\n// C = A + B = [7, 9, 11, 13, 15]\n\n// CPU vector addition\nvoid vector_add_cpu(float *a, float *b, float *c, int n) {\n    for (int i = 0; i < n; i++) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// CUDA kernel for vector addition\n__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// Initialize vector with random values\nvoid init_vector(float *vec, int n) {\n    for (int i = 0; i < n; i++) {\n        vec[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// Function to measure execution time\ndouble get_time() {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return ts.tv_sec + ts.tv_nsec * 1e-9;\n}\n\nint main() {\n    float *h_a, *h_b, *h_c_cpu, *h_c_gpu;\n    float *d_a, *d_b, *d_c;\n    size_t size = N * sizeof(float);\n\n    // Allocate host memory\n    h_a = (float*)malloc(size);\n    h_b = (float*)malloc(size);\n    h_c_cpu = (float*)malloc(size);\n    h_c_gpu = (float*)malloc(size);\n\n    // Initialize vectors\n    srand(time(NULL));\n    init_vector(h_a, N);\n    init_vector(h_b, N);\n\n    // Allocate device memory\n    cudaMalloc(&d_a, size);\n    cudaMalloc(&d_b, size);\n    cudaMalloc(&d_c, size);\n\n    // Copy data to device\n    cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    int num_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    // N = 1024, BLOCK_SIZE = 256, num_blocks = 4\n    // (N + BLOCK_SIZE - 1) / BLOCK_SIZE = ( (1025 + 256 - 1) / 256 ) = 1280 / 256 = 4 rounded \n\n    // Warm-up runs\n    printf(\"Performing warm-up runs...\\n\");\n    for (int i = 0; i < 3; i++) {\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        cudaDeviceSynchronize();\n    }\n\n    // Benchmark CPU implementation\n    printf(\"Benchmarking CPU implementation...\\n\");\n    double cpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        double end_time = get_time();\n        cpu_total_time += end_time - start_time;\n    }\n    double cpu_avg_time = cpu_total_time / 20.0;\n\n    // Benchmark GPU implementation\n    printf(\"Benchmarking GPU implementation...\\n\");\n    double gpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        cudaDeviceSynchronize();\n        double end_time = get_time();\n        gpu_total_time += end_time - start_time;\n    }\n    double gpu_avg_time = gpu_total_time / 20.0;\n\n    // Print results\n    printf(\"CPU average time: %f milliseconds\\n\", cpu_avg_time*1000);\n    printf(\"GPU average time: %f milliseconds\\n\", gpu_avg_time*1000);\n    printf(\"Speedup: %fx\\n\", cpu_avg_time / gpu_avg_time);\n\n    // Verify results (optional)\n    cudaMemcpy(h_c_gpu, d_c, size, cudaMemcpyDeviceToHost);\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (fabs(h_c_cpu[i] - h_c_gpu[i]) > 1e-5) {\n            correct = false;\n            break;\n        }\n    }\n    printf(\"Results are %s\\n\", correct ? \"correct\" : \"incorrect\");\n\n    // Free memory\n    free(h_a);\n    free(h_b);\n    free(h_c_cpu);\n    free(h_c_gpu);\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n\n    return 0;\n}", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <hip/hip_runtime.h>\n\n#define N 10000000  // Vector size = 10 million\n#define BLOCK_SIZE 256\n\n// Example:\n// A = [1, 2, 3, 4, 5]\n// B = [6, 7, 8, 9, 10]\n// C = A + B = [7, 9, 11, 13, 15]\n\n// CPU vector addition\nvoid vector_add_cpu(float *a, float *b, float *c, int n) {\n    for (int i = 0; i < n; i++) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// CUDA kernel for vector addition\n__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// Initialize vector with random values\nvoid init_vector(float *vec, int n) {\n    for (int i = 0; i < n; i++) {\n        vec[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// Function to measure execution time\ndouble get_time() {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return ts.tv_sec + ts.tv_nsec * 1e-9;\n}\n\nint main() {\n    float *h_a, *h_b, *h_c_cpu, *h_c_gpu;\n    float *d_a, *d_b, *d_c;\n    size_t size = N * sizeof(float);\n\n    // Allocate host memory\n    h_a = (float*)malloc(size);\n    h_b = (float*)malloc(size);\n    h_c_cpu = (float*)malloc(size);\n    h_c_gpu = (float*)malloc(size);\n\n    // Initialize vectors\n    srand(time(NULL));\n    init_vector(h_a, N);\n    init_vector(h_b, N);\n\n    // Allocate device memory\n    hipMalloc(&d_a, size);\n    hipMalloc(&d_b, size);\n    hipMalloc(&d_c, size);\n\n    // Copy data to device\n    hipMemcpy(d_a, h_a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, h_b, size, hipMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    int num_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    // N = 1024, BLOCK_SIZE = 256, num_blocks = 4\n    // (N + BLOCK_SIZE - 1) / BLOCK_SIZE = ( (1025 + 256 - 1) / 256 ) = 1280 / 256 = 4 rounded \n\n    // Warm-up runs\n    printf(\"Performing warm-up runs...\\n\");\n    for (int i = 0; i < 3; i++) {\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        hipDeviceSynchronize();\n    }\n\n    // Benchmark CPU implementation\n    printf(\"Benchmarking CPU implementation...\\n\");\n    double cpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        double end_time = get_time();\n        cpu_total_time += end_time - start_time;\n    }\n    double cpu_avg_time = cpu_total_time / 20.0;\n\n    // Benchmark GPU implementation\n    printf(\"Benchmarking GPU implementation...\\n\");\n    double gpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        hipDeviceSynchronize();\n        double end_time = get_time();\n        gpu_total_time += end_time - start_time;\n    }\n    double gpu_avg_time = gpu_total_time / 20.0;\n\n    // Print results\n    printf(\"CPU average time: %f milliseconds\\n\", cpu_avg_time*1000);\n    printf(\"GPU average time: %f milliseconds\\n\", gpu_avg_time*1000);\n    printf(\"Speedup: %fx\\n\", cpu_avg_time / gpu_avg_time);\n\n    // Verify results (optional)\n    hipMemcpy(h_c_gpu, d_c, size, hipMemcpyDeviceToHost);\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (fabs(h_c_cpu[i] - h_c_gpu[i]) > 1e-5) {\n            correct = false;\n            break;\n        }\n    }\n    printf(\"Results are %s\\n\", correct ? \"correct\" : \"incorrect\");\n\n    // Free memory\n    free(h_a);\n    free(h_b);\n    free(h_c_cpu);\n    free(h_c_gpu);\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 01/Vector_addition.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__constant__ char d_message[20];\n\n__global__ void welcome(char* msg) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    msg[idx] = d_message[idx];\n}\n\nint main() {\n    char* d_msg;\n    char* h_msg;\n    const char message[] = \"Hello Cuda!\";\n    const int length = strlen(message) + 1;\n\n    // Allocate host and device memory\n    h_msg = (char*)malloc(length * sizeof(char));\n    cudaMalloc(&d_msg, length * sizeof(char));\n    \n    // Copy message to constant memory\n    cudaMemcpyToSymbol(d_message, message, length);\n    \n    // Launch welcome kernel\n    welcome<<<1, length>>>(d_msg);\n    \n    // Copy result back to host\n    cudaMemcpy(h_msg, d_msg, length * sizeof(char), cudaMemcpyDeviceToHost);\n    h_msg[length-1] = '\\0';\n\n    std::cout << h_msg << \"\\n\";\n    \n    // Cleanup\n    free(h_msg);\n    cudaFree(d_msg);\n    \n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__constant__ char d_message[20];\n\n__global__ void welcome(char* msg) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    msg[idx] = d_message[idx];\n}\n\nint main() {\n    char* d_msg;\n    char* h_msg;\n    const char message[] = \"Hello Cuda!\";\n    const int length = strlen(message) + 1;\n\n    // Allocate host and device memory\n    h_msg = (char*)malloc(length * sizeof(char));\n    hipMalloc(&d_msg, length * sizeof(char));\n    \n    // Copy message to constant memory\n    hipMemcpyToSymbol(HIP_SYMBOL(d_message), message, length);\n    \n    // Launch welcome kernel\n    welcome<<<1, length>>>(d_msg);\n    \n    // Copy result back to host\n    hipMemcpy(h_msg, d_msg, length * sizeof(char), hipMemcpyDeviceToHost);\n    h_msg[length-1] = '\\0';\n\n    std::cout << h_msg << \"\\n\";\n    \n    // Cleanup\n    free(h_msg);\n    hipFree(d_msg);\n    \n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 01/HelloCuda.cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cstdlib>\n\n// #include <cuda_runtime.h>\n\n// Note: input_a, input_b, and output_c are all device pointers to float arrays\n//extern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t m, size_t n, size_t k) {\n    \n//} \n\ninline void checkCudaErrors(cudaError_t err, const char* msg = \"\")\n{\n    if (err != cudaSuccess)\n    {\n        std::cerr << \"CUDA Error: \" << msg << \" -> \"\n                  << cudaGetErrorString(err) << std::endl;\n        exit(EXIT_FAILURE);\n    }\n}\n\n__global__ void solution(const float* input_a, const float* input_b, float* output_c,\n    size_t m, size_t k, size_t n)\n{\nconst int TILE_WIDTH = 32;\n\n__shared__ float sharedA[TILE_WIDTH][TILE_WIDTH];\n__shared__ float sharedB[TILE_WIDTH][TILE_WIDTH];\n\nint row = blockDim.y * blockIdx.y + threadIdx.y;\nint col = blockDim.x * blockIdx.x + threadIdx.x;\n\nfloat sum = 0.0f;\n\nint tx = threadIdx.x;\nint ty = threadIdx.y;\n\nfor (int t = 0; t < (k + TILE_WIDTH - 1) / TILE_WIDTH; t++)\n{\nint tiledCol = t * TILE_WIDTH + tx;\nif (row < m && tiledCol < k)\nsharedA[ty][tx] = input_a[row * k + tiledCol];\nelse\nsharedA[ty][tx] = 0.0f;\nint tiledRow = t * TILE_WIDTH + ty;\nif (tiledRow < k && col < n)\nsharedB[ty][tx] = input_b[tiledRow * n + col];\nelse\nsharedB[ty][tx] = 0.0f;\n\n__syncthreads();\n\n#pragma unroll\nfor (int i = 0; i < TILE_WIDTH; i++)\n{\nsum += sharedA[ty][i] * sharedB[i][tx];\n}\n__syncthreads();\n}\nif (row < m && col < n)\n{\noutput_c[row * n + col] = sum;\n}\n}\n                             ", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cstdlib>\n\n// #include <cuda_runtime.h>\n\n// Note: input_a, input_b, and output_c are all device pointers to float arrays\n//extern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t m, size_t n, size_t k) {\n    \n//} \n\ninline void checkCudaErrors(hipError_t err, const char* msg = \"\")\n{\n    if (err != hipSuccess)\n    {\n        std::cerr << \"CUDA Error: \" << msg << \" -> \"\n                  << hipGetErrorString(err) << std::endl;\n        exit(EXIT_FAILURE);\n    }\n}\n\n__global__ void solution(const float* input_a, const float* input_b, float* output_c,\n    size_t m, size_t k, size_t n)\n{\nconst int TILE_WIDTH = 32;\n\n__shared__ float sharedA[TILE_WIDTH][TILE_WIDTH];\n__shared__ float sharedB[TILE_WIDTH][TILE_WIDTH];\n\nint row = blockDim.y * blockIdx.y + threadIdx.y;\nint col = blockDim.x * blockIdx.x + threadIdx.x;\n\nfloat sum = 0.0f;\n\nint tx = threadIdx.x;\nint ty = threadIdx.y;\n\nfor (int t = 0; t < (k + TILE_WIDTH - 1) / TILE_WIDTH; t++)\n{\nint tiledCol = t * TILE_WIDTH + tx;\nif (row < m && tiledCol < k)\nsharedA[ty][tx] = input_a[row * k + tiledCol];\nelse\nsharedA[ty][tx] = 0.0f;\nint tiledRow = t * TILE_WIDTH + ty;\nif (tiledRow < k && col < n)\nsharedB[ty][tx] = input_b[tiledRow * n + col];\nelse\nsharedB[ty][tx] = 0.0f;\n\n__syncthreads();\n\n#pragma unroll\nfor (int i = 0; i < TILE_WIDTH; i++)\n{\nsum += sharedA[ty][i] * sharedB[i][tx];\n}\n__syncthreads();\n}\nif (row < m && col < n)\n{\noutput_c[row * n + col] = sum;\n}\n}\n                             ", "source": "/kaggle/working/100-days-cuda/Day 29/checker.cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cstdlib>\n\n// #include <cuda_runtime.h>\n\n// Note: input_a, input_b, and output_c are all device pointers to float arrays\n//extern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t m, size_t n, size_t k) {\n    \n//} \n\ninline void checkCudaErrors(cudaError_t err, const char* msg = \"\")\n{\n    if (err != cudaSuccess)\n    {\n        std::cerr << \"CUDA Error: \" << msg << \" -> \"\n                  << cudaGetErrorString(err) << std::endl;\n        exit(EXIT_FAILURE);\n    }\n}\n\n__global__ void solution(const float* input_a, const float* input_b, float* output_c,\n    int M, int K, int N)\n{\nconst int TILE_WIDTH = 32;\n\n__shared__ float sharedA[TILE_WIDTH][TILE_WIDTH];\n__shared__ float sharedB[TILE_WIDTH][TILE_WIDTH];\n\nint row = blockDim.y * blockIdx.y + threadIdx.y;\nint col = blockDim.x * blockIdx.x + threadIdx.x;\n\nfloat sum = 0.0f;\n\nint tx = threadIdx.x;\nint ty = threadIdx.y;\n\nfor (int t = 0; t < (K + TILE_WIDTH - 1) / TILE_WIDTH; t++)\n{\nint tiledCol = t * TILE_WIDTH + tx;\nif (row < M && tiledCol < K)\nsharedA[ty][tx] = input_a[row * K + tiledCol];\nelse\nsharedA[ty][tx] = 0.0f;\nint tiledRow = t * TILE_WIDTH + ty;\nif (tiledRow < K && col < N)\nsharedB[ty][tx] = input_b[tiledRow * N + col];\nelse\nsharedB[ty][tx] = 0.0f;\n\n__syncthreads();\n\n#pragma unroll\nfor (int i = 0; i < TILE_WIDTH; i++)\n{\nsum += sharedA[ty][i] * sharedB[i][tx];\n}\n__syncthreads();\n}\nif (row < M && col < N)\n{\noutput_c[row * N + col] = sum;\n}\n}\n\n\nint main()\n{\n    // Dimensions of matrices\n    size_t M = 1024/256;  // rows in A and C\n    size_t K = 1024/256;  // cols in A, rows in B\n    size_t N = 1024/256;  // cols in B and C\n\n    std::cout << \"Matrix multiplication: A(\" << M << \"x\" << K \n              << \") * B(\" << K << \"x\" << N << \") = C(\" \n              << M << \"x\" << N << \")\\n\";\n\n    float *h_A, *h_B, *h_C;\n    checkCudaErrors(cudaMallocHost(&h_A,       M * K * sizeof(float)), \"malloc pinned h_A\");\n    checkCudaErrors(cudaMallocHost(&h_B,       K * N * sizeof(float)), \"malloc pinned h_B\");\n    checkCudaErrors(cudaMallocHost(&h_C,       M * N * sizeof(float)), \"malloc pinned h_C\");\n\n    for (int i = 0; i < M; i++)\n    {\n        for (int j = 0; j < K; j++)\n        {\n            h_A[i * K + j] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n    for (int i = 0; i < K; i++)\n    {\n        for (int j = 0; j < N; j++)\n        {\n            h_B[i * N + j] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n\n    float *d_A = nullptr, *d_B = nullptr, *d_C = nullptr;\n    checkCudaErrors(cudaMalloc((void**)&d_A, M * K * sizeof(float)), \"malloc d_A\");\n    checkCudaErrors(cudaMalloc((void**)&d_B, K * N * sizeof(float)), \"malloc d_B\");\n    checkCudaErrors(cudaMalloc((void**)&d_C, M * N * sizeof(float)), \"malloc d_C\");\n    cudaStream_t stream;\n    checkCudaErrors(cudaStreamCreate(&stream), \"create stream\");\n\n    checkCudaErrors(cudaMemcpyAsync(d_A, h_A, M * K * sizeof(float), \n                                    cudaMemcpyHostToDevice, stream), \"memcpy h_A->d_A\");\n    checkCudaErrors(cudaMemcpyAsync(d_B, h_B, K * N * sizeof(float), \n                                    cudaMemcpyHostToDevice, stream), \"memcpy h_B->d_B\");\n    checkCudaErrors(cudaStreamSynchronize(stream), \"stream sync\");\n\n    auto start = std::chrono::high_resolution_clock::now();\n    auto end   = std::chrono::high_resolution_clock::now();\n    \n    dim3 blockSizeShared(32, 32);\n    dim3 gridSizeShared((N + blockSizeShared.x - 1) / blockSizeShared.x,\n                        (M + blockSizeShared.y - 1) / blockSizeShared.y);\n\n    start = std::chrono::high_resolution_clock::now();\n    matMulSharedKernel<<<gridSizeShared, blockSizeShared, 0, stream>>>(d_A, d_B, d_C, M, K, N);\n    checkCudaErrors(cudaStreamSynchronize(stream), \"shared kernel sync\");\n    end = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<double, std::milli> shared_ms = end - start;\n\n    checkCudaErrors(cudaMemcpyAsync(h_C, d_C, M * N * sizeof(float), \n    cudaMemcpyDeviceToHost, stream), \"memcpy shared result\");\n\n    std::cout << \"Shared memory:     \" << shared_ms.count() << \" ms\\n\";\n\n    cudaStreamDestroy(stream);\n    cudaFreeHost(h_A);\n    cudaFreeHost(h_B);\n    cudaFreeHost(h_C);\n\n    \n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C);\n\n    return 0;\n}\n                                    ", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cstdlib>\n\n// #include <cuda_runtime.h>\n\n// Note: input_a, input_b, and output_c are all device pointers to float arrays\n//extern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t m, size_t n, size_t k) {\n    \n//} \n\ninline void checkCudaErrors(hipError_t err, const char* msg = \"\")\n{\n    if (err != hipSuccess)\n    {\n        std::cerr << \"CUDA Error: \" << msg << \" -> \"\n                  << hipGetErrorString(err) << std::endl;\n        exit(EXIT_FAILURE);\n    }\n}\n\n__global__ void solution(const float* input_a, const float* input_b, float* output_c,\n    int M, int K, int N)\n{\nconst int TILE_WIDTH = 32;\n\n__shared__ float sharedA[TILE_WIDTH][TILE_WIDTH];\n__shared__ float sharedB[TILE_WIDTH][TILE_WIDTH];\n\nint row = blockDim.y * blockIdx.y + threadIdx.y;\nint col = blockDim.x * blockIdx.x + threadIdx.x;\n\nfloat sum = 0.0f;\n\nint tx = threadIdx.x;\nint ty = threadIdx.y;\n\nfor (int t = 0; t < (K + TILE_WIDTH - 1) / TILE_WIDTH; t++)\n{\nint tiledCol = t * TILE_WIDTH + tx;\nif (row < M && tiledCol < K)\nsharedA[ty][tx] = input_a[row * K + tiledCol];\nelse\nsharedA[ty][tx] = 0.0f;\nint tiledRow = t * TILE_WIDTH + ty;\nif (tiledRow < K && col < N)\nsharedB[ty][tx] = input_b[tiledRow * N + col];\nelse\nsharedB[ty][tx] = 0.0f;\n\n__syncthreads();\n\n#pragma unroll\nfor (int i = 0; i < TILE_WIDTH; i++)\n{\nsum += sharedA[ty][i] * sharedB[i][tx];\n}\n__syncthreads();\n}\nif (row < M && col < N)\n{\noutput_c[row * N + col] = sum;\n}\n}\n\n\nint main()\n{\n    // Dimensions of matrices\n    size_t M = 1024/256;  // rows in A and C\n    size_t K = 1024/256;  // cols in A, rows in B\n    size_t N = 1024/256;  // cols in B and C\n\n    std::cout << \"Matrix multiplication: A(\" << M << \"x\" << K \n              << \") * B(\" << K << \"x\" << N << \") = C(\" \n              << M << \"x\" << N << \")\\n\";\n\n    float *h_A, *h_B, *h_C;\n    checkCudaErrors(hipHostMalloc(&h_A,       M * K * sizeof(float)), \"malloc pinned h_A\");\n    checkCudaErrors(hipHostMalloc(&h_B,       K * N * sizeof(float)), \"malloc pinned h_B\");\n    checkCudaErrors(hipHostMalloc(&h_C,       M * N * sizeof(float)), \"malloc pinned h_C\");\n\n    for (int i = 0; i < M; i++)\n    {\n        for (int j = 0; j < K; j++)\n        {\n            h_A[i * K + j] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n    for (int i = 0; i < K; i++)\n    {\n        for (int j = 0; j < N; j++)\n        {\n            h_B[i * N + j] = static_cast<float>(rand()) / RAND_MAX;\n        }\n    }\n\n    float *d_A = nullptr, *d_B = nullptr, *d_C = nullptr;\n    checkCudaErrors(hipMalloc((void**)&d_A, M * K * sizeof(float)), \"malloc d_A\");\n    checkCudaErrors(hipMalloc((void**)&d_B, K * N * sizeof(float)), \"malloc d_B\");\n    checkCudaErrors(hipMalloc((void**)&d_C, M * N * sizeof(float)), \"malloc d_C\");\n    hipStream_t stream;\n    checkCudaErrors(hipStreamCreate(&stream), \"create stream\");\n\n    checkCudaErrors(hipMemcpyAsync(d_A, h_A, M * K * sizeof(float), \n                                    hipMemcpyHostToDevice, stream), \"memcpy h_A->d_A\");\n    checkCudaErrors(hipMemcpyAsync(d_B, h_B, K * N * sizeof(float), \n                                    hipMemcpyHostToDevice, stream), \"memcpy h_B->d_B\");\n    checkCudaErrors(hipStreamSynchronize(stream), \"stream sync\");\n\n    auto start = std::chrono::high_resolution_clock::now();\n    auto end   = std::chrono::high_resolution_clock::now();\n    \n    dim3 blockSizeShared(32, 32);\n    dim3 gridSizeShared((N + blockSizeShared.x - 1) / blockSizeShared.x,\n                        (M + blockSizeShared.y - 1) / blockSizeShared.y);\n\n    start = std::chrono::high_resolution_clock::now();\n    matMulSharedKernel<<<gridSizeShared, blockSizeShared, 0, stream>>>(d_A, d_B, d_C, M, K, N);\n    checkCudaErrors(hipStreamSynchronize(stream), \"shared kernel sync\");\n    end = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<double, std::milli> shared_ms = end - start;\n\n    checkCudaErrors(hipMemcpyAsync(h_C, d_C, M * N * sizeof(float), \n    hipMemcpyDeviceToHost, stream), \"memcpy shared result\");\n\n    std::cout << \"Shared memory:     \" << shared_ms.count() << \" ms\\n\";\n\n    hipStreamDestroy(stream);\n    hipHostFree(h_A);\n    hipHostFree(h_B);\n    hipHostFree(h_C);\n\n    \n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C);\n\n    return 0;\n}\n                                    ", "source": "/kaggle/working/100-days-cuda/Day 29/Shared_matmul.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <stdio.h>\n\n#define BLOCK_SIZE 16  // Tile size for shared memory optimization\n\n// CUDA Kernel for Matrix Multiplication using Tiling\n__global__ void matrixMulShared(float* C, float* A, float* B, int N) {\n    __shared__ float Asub[BLOCK_SIZE][BLOCK_SIZE];\n    __shared__ float Bsub[BLOCK_SIZE][BLOCK_SIZE];\n\n    int tx = threadIdx.x, ty = threadIdx.y;\n    int row = blockIdx.y * BLOCK_SIZE + ty;\n    int col = blockIdx.x * BLOCK_SIZE + tx;\n\n    float sum = 0.0f;\n\n    // Loop over tiles\n    for (int t = 0; t < (N / BLOCK_SIZE); ++t) {\n        Asub[ty][tx] = A[row * N + (t * BLOCK_SIZE + tx)];\n        Bsub[ty][tx] = B[(t * BLOCK_SIZE + ty) * N + col];\n\n        __syncthreads();  // Sync threads in block\n\n        for (int k = 0; k < BLOCK_SIZE; ++k)\n            sum += Asub[ty][k] * Bsub[k][tx];\n\n        __syncthreads();\n    }\n\n    if (row < N && col < N) {\n        C[row * N + col] = sum;\n    }\n\n    // Print first few thread results for debugging\n    if (row == 0 && col < 5) {\n        printf(\"Thread (0, %d): C[%d] = %f\\n\", col, row * N + col, sum);\n    }\n}\n\n// Initialize matrices with random values\nvoid initializeMatrix(float* mat, int N) {\n    for (int i = 0; i < N * N; i++) {\n        mat[i] = static_cast<float>(rand() % 100) / 10.0f;\n    }\n}\n\n// Print matrix for debugging (small matrices only)\nvoid printMatrix(float* mat, int N) {\n    for (int i = 0; i < 5; i++) {  // Print first 5 rows\n        for (int j = 0; j < 5; j++) {  // Print first 5 columns\n            printf(\"%6.2f \", mat[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n    printf(\"...\\n\");  // Indicate truncation for large matrices\n}\n\nint main() {\n    const int N = 1024;  // Matrix size\n    size_t bytes = N * N * sizeof(float);\n\n    printf(\"Allocating memory...\\n\");\n\n    // Allocate memory\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    initializeMatrix(h_A, N);\n    initializeMatrix(h_B, N);\n\n    printf(\"Matrices initialized.\\n\");\n\n    // Allocate device memory\n    float* d_A, * d_B, * d_C;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C, bytes);\n\n    printf(\"Copying data to GPU...\\n\");\n\n    // Copy data to GPU\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    // Launch kernel\n    dim3 threads(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 blocks(N / BLOCK_SIZE, N / BLOCK_SIZE);\n\n    printf(\"Launching CUDA Kernel...\\n\");\n    matrixMulShared << <blocks, threads >> > (d_C, d_A, d_B, N);\n\n    cudaDeviceSynchronize();  // Ensure kernel execution is finished\n\n    printf(\"Kernel execution completed. Copying results back to CPU...\\n\");\n\n    // Copy result back\n    cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);\n\n    printf(\"Computation done! Printing a small portion of result matrix:\\n\");\n    printMatrix(h_C, N);\n\n    // Cleanup\n    free(h_A); free(h_B); free(h_C);\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n\n    printf(\"Memory freed. Exiting program.\\n\");\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <stdio.h>\n\n#define BLOCK_SIZE 16  // Tile size for shared memory optimization\n\n// CUDA Kernel for Matrix Multiplication using Tiling\n__global__ void matrixMulShared(float* C, float* A, float* B, int N) {\n    __shared__ float Asub[BLOCK_SIZE][BLOCK_SIZE];\n    __shared__ float Bsub[BLOCK_SIZE][BLOCK_SIZE];\n\n    int tx = threadIdx.x, ty = threadIdx.y;\n    int row = blockIdx.y * BLOCK_SIZE + ty;\n    int col = blockIdx.x * BLOCK_SIZE + tx;\n\n    float sum = 0.0f;\n\n    // Loop over tiles\n    for (int t = 0; t < (N / BLOCK_SIZE); ++t) {\n        Asub[ty][tx] = A[row * N + (t * BLOCK_SIZE + tx)];\n        Bsub[ty][tx] = B[(t * BLOCK_SIZE + ty) * N + col];\n\n        __syncthreads();  // Sync threads in block\n\n        for (int k = 0; k < BLOCK_SIZE; ++k)\n            sum += Asub[ty][k] * Bsub[k][tx];\n\n        __syncthreads();\n    }\n\n    if (row < N && col < N) {\n        C[row * N + col] = sum;\n    }\n\n    // Print first few thread results for debugging\n    if (row == 0 && col < 5) {\n        printf(\"Thread (0, %d): C[%d] = %f\\n\", col, row * N + col, sum);\n    }\n}\n\n// Initialize matrices with random values\nvoid initializeMatrix(float* mat, int N) {\n    for (int i = 0; i < N * N; i++) {\n        mat[i] = static_cast<float>(rand() % 100) / 10.0f;\n    }\n}\n\n// Print matrix for debugging (small matrices only)\nvoid printMatrix(float* mat, int N) {\n    for (int i = 0; i < 5; i++) {  // Print first 5 rows\n        for (int j = 0; j < 5; j++) {  // Print first 5 columns\n            printf(\"%6.2f \", mat[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n    printf(\"...\\n\");  // Indicate truncation for large matrices\n}\n\nint main() {\n    const int N = 1024;  // Matrix size\n    size_t bytes = N * N * sizeof(float);\n\n    printf(\"Allocating memory...\\n\");\n\n    // Allocate memory\n    float* h_A = (float*)malloc(bytes);\n    float* h_B = (float*)malloc(bytes);\n    float* h_C = (float*)malloc(bytes);\n\n    initializeMatrix(h_A, N);\n    initializeMatrix(h_B, N);\n\n    printf(\"Matrices initialized.\\n\");\n\n    // Allocate device memory\n    float* d_A, * d_B, * d_C;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C, bytes);\n\n    printf(\"Copying data to GPU...\\n\");\n\n    // Copy data to GPU\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    // Launch kernel\n    dim3 threads(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 blocks(N / BLOCK_SIZE, N / BLOCK_SIZE);\n\n    printf(\"Launching CUDA Kernel...\\n\");\n    matrixMulShared << <blocks, threads >> > (d_C, d_A, d_B, N);\n\n    hipDeviceSynchronize();  // Ensure kernel execution is finished\n\n    printf(\"Kernel execution completed. Copying results back to CPU...\\n\");\n\n    // Copy result back\n    hipMemcpy(h_C, d_C, bytes, hipMemcpyDeviceToHost);\n\n    printf(\"Computation done! Printing a small portion of result matrix:\\n\");\n    printMatrix(h_C, N);\n\n    // Cleanup\n    free(h_A); free(h_B); free(h_C);\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n\n    printf(\"Memory freed. Exiting program.\\n\");\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 10/MatMulProfiling.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <cuda_runtime.h>\n\n#define N 10000000  // Vector size = 10 million\n#define BLOCK_SIZE 256\n\n// Example:\n// A = [1, 2, 3, 4, 5]\n// B = [6, 7, 8, 9, 10]\n// C = A + B = [7, 9, 11, 13, 15]\n\n// CPU vector addition\nvoid vector_add_cpu(float *a, float *b, float *c, int n) {\n    for (int i = 0; i < n; i++) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// CUDA kernel for vector addition\n__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// Initialize vector with random values\nvoid init_vector(float *vec, int n) {\n    for (int i = 0; i < n; i++) {\n        vec[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// Function to measure execution time\ndouble get_time() {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return ts.tv_sec + ts.tv_nsec * 1e-9;\n}\n\nint main() {\n    float *h_a, *h_b, *h_c_cpu, *h_c_gpu;\n    float *d_a, *d_b, *d_c;\n    size_t size = N * sizeof(float);\n\n    // Allocate host memory\n    h_a = (float*)malloc(size);\n    h_b = (float*)malloc(size);\n    h_c_cpu = (float*)malloc(size);\n    h_c_gpu = (float*)malloc(size);\n\n    // Initialize vectors\n    srand(time(NULL));\n    init_vector(h_a, N);\n    init_vector(h_b, N);\n\n    // Allocate device memory\n    cudaMalloc(&d_a, size);\n    cudaMalloc(&d_b, size);\n    cudaMalloc(&d_c, size);\n\n    // Copy data to device\n    cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    int num_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    // N = 1024, BLOCK_SIZE = 256, num_blocks = 4\n    // (N + BLOCK_SIZE - 1) / BLOCK_SIZE = ( (1025 + 256 - 1) / 256 ) = 1280 / 256 = 4 rounded \n\n    // Warm-up runs\n    printf(\"Performing warm-up runs...\\n\");\n    for (int i = 0; i < 3; i++) {\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        cudaDeviceSynchronize();\n    }\n\n    // Benchmark CPU implementation\n    printf(\"Benchmarking CPU implementation...\\n\");\n    double cpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        double end_time = get_time();\n        cpu_total_time += end_time - start_time;\n    }\n    double cpu_avg_time = cpu_total_time / 20.0;\n\n    // Benchmark GPU implementation\n    printf(\"Benchmarking GPU implementation...\\n\");\n    double gpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        cudaDeviceSynchronize();\n        double end_time = get_time();\n        gpu_total_time += end_time - start_time;\n    }\n    double gpu_avg_time = gpu_total_time / 20.0;\n\n    // Print results\n    printf(\"CPU average time: %f milliseconds\\n\", cpu_avg_time*1000);\n    printf(\"GPU average time: %f milliseconds\\n\", gpu_avg_time*1000);\n    printf(\"Speedup: %fx\\n\", cpu_avg_time / gpu_avg_time);\n\n    // Verify results (optional)\n    cudaMemcpy(h_c_gpu, d_c, size, cudaMemcpyDeviceToHost);\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (fabs(h_c_cpu[i] - h_c_gpu[i]) > 1e-5) {\n            correct = false;\n            break;\n        }\n    }\n    printf(\"Results are %s\\n\", correct ? \"correct\" : \"incorrect\");\n\n    // Free memory\n    free(h_a);\n    free(h_b);\n    free(h_c_cpu);\n    free(h_c_gpu);\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n\n    return 0;\n}", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <hip/hip_runtime.h>\n\n#define N 10000000  // Vector size = 10 million\n#define BLOCK_SIZE 256\n\n// Example:\n// A = [1, 2, 3, 4, 5]\n// B = [6, 7, 8, 9, 10]\n// C = A + B = [7, 9, 11, 13, 15]\n\n// CPU vector addition\nvoid vector_add_cpu(float *a, float *b, float *c, int n) {\n    for (int i = 0; i < n; i++) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// CUDA kernel for vector addition\n__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        c[i] = a[i] + b[i];\n    }\n}\n\n// Initialize vector with random values\nvoid init_vector(float *vec, int n) {\n    for (int i = 0; i < n; i++) {\n        vec[i] = (float)rand() / RAND_MAX;\n    }\n}\n\n// Function to measure execution time\ndouble get_time() {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return ts.tv_sec + ts.tv_nsec * 1e-9;\n}\n\nint main() {\n    float *h_a, *h_b, *h_c_cpu, *h_c_gpu;\n    float *d_a, *d_b, *d_c;\n    size_t size = N * sizeof(float);\n\n    // Allocate host memory\n    h_a = (float*)malloc(size);\n    h_b = (float*)malloc(size);\n    h_c_cpu = (float*)malloc(size);\n    h_c_gpu = (float*)malloc(size);\n\n    // Initialize vectors\n    srand(time(NULL));\n    init_vector(h_a, N);\n    init_vector(h_b, N);\n\n    // Allocate device memory\n    hipMalloc(&d_a, size);\n    hipMalloc(&d_b, size);\n    hipMalloc(&d_c, size);\n\n    // Copy data to device\n    hipMemcpy(d_a, h_a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, h_b, size, hipMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    int num_blocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    // N = 1024, BLOCK_SIZE = 256, num_blocks = 4\n    // (N + BLOCK_SIZE - 1) / BLOCK_SIZE = ( (1025 + 256 - 1) / 256 ) = 1280 / 256 = 4 rounded \n\n    // Warm-up runs\n    printf(\"Performing warm-up runs...\\n\");\n    for (int i = 0; i < 3; i++) {\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        hipDeviceSynchronize();\n    }\n\n    // Benchmark CPU implementation\n    printf(\"Benchmarking CPU implementation...\\n\");\n    double cpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_cpu(h_a, h_b, h_c_cpu, N);\n        double end_time = get_time();\n        cpu_total_time += end_time - start_time;\n    }\n    double cpu_avg_time = cpu_total_time / 20.0;\n\n    // Benchmark GPU implementation\n    printf(\"Benchmarking GPU implementation...\\n\");\n    double gpu_total_time = 0.0;\n    for (int i = 0; i < 20; i++) {\n        double start_time = get_time();\n        vector_add_gpu<<<num_blocks, BLOCK_SIZE>>>(d_a, d_b, d_c, N);\n        hipDeviceSynchronize();\n        double end_time = get_time();\n        gpu_total_time += end_time - start_time;\n    }\n    double gpu_avg_time = gpu_total_time / 20.0;\n\n    // Print results\n    printf(\"CPU average time: %f milliseconds\\n\", cpu_avg_time*1000);\n    printf(\"GPU average time: %f milliseconds\\n\", gpu_avg_time*1000);\n    printf(\"Speedup: %fx\\n\", cpu_avg_time / gpu_avg_time);\n\n    // Verify results (optional)\n    hipMemcpy(h_c_gpu, d_c, size, hipMemcpyDeviceToHost);\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (fabs(h_c_cpu[i] - h_c_gpu[i]) > 1e-5) {\n            correct = false;\n            break;\n        }\n    }\n    printf(\"Results are %s\\n\", correct ? \"correct\" : \"incorrect\");\n\n    // Free memory\n    free(h_a);\n    free(h_b);\n    free(h_c_cpu);\n    free(h_c_gpu);\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 10/Day1_vectorAdd_for_profiling.cu"}
{"input": "#include <cuda_runtime.h>\n#include <curand_kernel.h>\n#include <iostream>\n\n#define S 10      // States\n#define A 4       // Actions\n#define __A__ 0.7 // Learning Rate\n#define __G__ 0.99 // Discount Factor\n#define EP 10000   // Training Episodes\n\n__global__ void initR(float *R, curandState *states) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S * A) {\n        curand_init(1234, i, 0, &states[i]);\n        R[i] = (curand(&states[i]) % 10) / 10.0f; // Larger rewards\n    }\n}\n\n__global__ void initQ(float *Q1, float *Q2) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S * A) {\n        Q1[i] = 0.0f;\n        Q2[i] = 0.0f;\n    }\n}\n\n__global__ void doubleQlearning(float *Q1, float *Q2, float *R, curandState *states, float eps) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) {\n        int a = curand(&states[i]) % A;\n        int next_s = curand(&states[i]) % S;\n        float maxQ;\n        \n        if (curand_uniform(&states[i]) < 0.5f) {\n            int maxA = 0;\n            for (int j = 1; j < A; j++)\n                if (Q1[next_s * A + j] > Q1[next_s * A + maxA])\n                    maxA = j;\n            maxQ = Q2[next_s * A + maxA];  \n            Q1[i * A + a] += __A__ * (R[i * A + a] + __G__ * maxQ - Q1[i * A + a]);\n        } else {\n            int maxA = 0;\n            for (int j = 1; j < A; j++)\n                if (Q2[next_s * A + j] > Q2[next_s * A + maxA])\n                    maxA = j;\n            maxQ = Q1[next_s * A + maxA];\n            Q2[i * A + a] += __A__ * (R[i * A + a] + __G__ * maxQ - Q2[i * A + a]);\n        }\n    }\n}\n\nint main() {\n    float *d_Q1, *d_Q2, *d_R;\n    curandState *d_states;\n    cudaMalloc(&d_Q1, S * A * sizeof(float));\n    cudaMalloc(&d_Q2, S * A * sizeof(float));\n    cudaMalloc(&d_R, S * A * sizeof(float));\n    cudaMalloc(&d_states, S * A * sizeof(curandState));\n\n    initQ<<<1, S * A>>>(d_Q1, d_Q2);\n    initR<<<1, S * A>>>(d_R, d_states);\n    cudaDeviceSynchronize();\n\n    for (int ep = 0; ep < EP; ep++) {\n        float eps = max(0.1f, 1.0f - 0.001f * ep); // Slower Decay\n        doubleQlearning<<<1, S>>>(d_Q1, d_Q2, d_R, d_states, eps);\n        cudaDeviceSynchronize();\n    }\n\n    float h_Q1[S * A], h_Q2[S * A];\n    cudaMemcpy(h_Q1, d_Q1, S * A * sizeof(float), cudaMemcpyDeviceToHost);\n    cudaMemcpy(h_Q2, d_Q2, S * A * sizeof(float), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Double Q-Learning Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        for (int j = 0; j < A; j++)\n            std::cout << (h_Q1[i * A + j] + h_Q2[i * A + j]) / 2.0 << \" \";\n        std::cout << \"\\n\";\n    }\n\n    cudaFree(d_Q1);\n    cudaFree(d_Q2);\n    cudaFree(d_R);\n    cudaFree(d_states);\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <hiprand/hiprand_kernel.h>\n#include <iostream>\n\n#define S 10      // States\n#define A 4       // Actions\n#define __A__ 0.7 // Learning Rate\n#define __G__ 0.99 // Discount Factor\n#define EP 10000   // Training Episodes\n\n__global__ void initR(float *R, hiprandState *states) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S * A) {\n        hiprand_init(1234, i, 0, &states[i]);\n        R[i] = (hiprand(&states[i]) % 10) / 10.0f; // Larger rewards\n    }\n}\n\n__global__ void initQ(float *Q1, float *Q2) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S * A) {\n        Q1[i] = 0.0f;\n        Q2[i] = 0.0f;\n    }\n}\n\n__global__ void doubleQlearning(float *Q1, float *Q2, float *R, hiprandState *states, float eps) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) {\n        int a = hiprand(&states[i]) % A;\n        int next_s = hiprand(&states[i]) % S;\n        float maxQ;\n        \n        if (hiprand_uniform(&states[i]) < 0.5f) {\n            int maxA = 0;\n            for (int j = 1; j < A; j++)\n                if (Q1[next_s * A + j] > Q1[next_s * A + maxA])\n                    maxA = j;\n            maxQ = Q2[next_s * A + maxA];  \n            Q1[i * A + a] += __A__ * (R[i * A + a] + __G__ * maxQ - Q1[i * A + a]);\n        } else {\n            int maxA = 0;\n            for (int j = 1; j < A; j++)\n                if (Q2[next_s * A + j] > Q2[next_s * A + maxA])\n                    maxA = j;\n            maxQ = Q1[next_s * A + maxA];\n            Q2[i * A + a] += __A__ * (R[i * A + a] + __G__ * maxQ - Q2[i * A + a]);\n        }\n    }\n}\n\nint main() {\n    float *d_Q1, *d_Q2, *d_R;\n    hiprandState *d_states;\n    hipMalloc(&d_Q1, S * A * sizeof(float));\n    hipMalloc(&d_Q2, S * A * sizeof(float));\n    hipMalloc(&d_R, S * A * sizeof(float));\n    hipMalloc(&d_states, S * A * sizeof(hiprandState));\n\n    initQ<<<1, S * A>>>(d_Q1, d_Q2);\n    initR<<<1, S * A>>>(d_R, d_states);\n    hipDeviceSynchronize();\n\n    for (int ep = 0; ep < EP; ep++) {\n        float eps = max(0.1f, 1.0f - 0.001f * ep); // Slower Decay\n        doubleQlearning<<<1, S>>>(d_Q1, d_Q2, d_R, d_states, eps);\n        hipDeviceSynchronize();\n    }\n\n    float h_Q1[S * A], h_Q2[S * A];\n    hipMemcpy(h_Q1, d_Q1, S * A * sizeof(float), hipMemcpyDeviceToHost);\n    hipMemcpy(h_Q2, d_Q2, S * A * sizeof(float), hipMemcpyDeviceToHost);\n\n    std::cout << \"Double Q-Learning Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        for (int j = 0; j < A; j++)\n            std::cout << (h_Q1[i * A + j] + h_Q2[i * A + j]) / 2.0 << \" \";\n        std::cout << \"\\n\";\n    }\n\n    hipFree(d_Q1);\n    hipFree(d_Q2);\n    hipFree(d_R);\n    hipFree(d_states);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 58/Double_Q.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#define TILE_SIZE 16\n#define GROUP_SIZE 3\n\n__global__ void matmul_naive(float* A, float* B, float* C, int M, int N, int K) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < M && col < N) {\n        float sum = 0.0f;\n        for (int i = 0; i < K; ++i)\n            sum += A[row * K + i] * B[i * N + col];\n        C[row * N + col] = sum;\n    }\n}\n\n__global__ void matmul_row_major(float* A, float* B, float* C, int M, int N, int K) {\n    __shared__ float Asub[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bsub[TILE_SIZE][TILE_SIZE];\n\n    int row = blockIdx.y * TILE_SIZE + threadIdx.y;\n    int col = blockIdx.x * TILE_SIZE + threadIdx.x;\n\n    float sum = 0.0f;\n\n    for (int tile = 0; tile < (K + TILE_SIZE - 1) / TILE_SIZE; ++tile) {\n        if (row < M && tile * TILE_SIZE + threadIdx.x < K)\n            Asub[threadIdx.y][threadIdx.x] = A[row * K + tile * TILE_SIZE + threadIdx.x];\n        else\n            Asub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        if (tile * TILE_SIZE + threadIdx.y < K && col < N)\n            Bsub[threadIdx.y][threadIdx.x] = B[(tile * TILE_SIZE + threadIdx.y) * N + col];\n        else\n            Bsub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i)\n            sum += Asub[threadIdx.y][i] * Bsub[i][threadIdx.x];\n\n        __syncthreads();\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = sum;\n}\n\n__global__ void matmul_grouped(float* A, float* B, float* C, int M, int N, int K) {\n    __shared__ float Asub[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bsub[TILE_SIZE][TILE_SIZE];\n\n    int group_id = blockIdx.x;\n    int tile_id_in_group = threadIdx.z;\n\n    int tiles_per_row = N / TILE_SIZE;\n    int tile_row = (group_id / tiles_per_row) * GROUP_SIZE + tile_id_in_group;\n    int tile_col = group_id % tiles_per_row;\n\n    int row = tile_row * TILE_SIZE + threadIdx.y;\n    int col = tile_col * TILE_SIZE + threadIdx.x;\n\n    float sum = 0.0f;\n\n    for (int tile = 0; tile < (K + TILE_SIZE - 1) / TILE_SIZE; ++tile) {\n        int tiled_k = tile * TILE_SIZE;\n\n        if (row < M && tiled_k + threadIdx.x < K)\n            Asub[threadIdx.y][threadIdx.x] = A[row * K + tiled_k + threadIdx.x];\n        else\n            Asub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        if (tiled_k + threadIdx.y < K && col < N)\n            Bsub[threadIdx.y][threadIdx.x] = B[(tiled_k + threadIdx.y) * N + col];\n        else\n            Bsub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i)\n            sum += Asub[threadIdx.y][i] * Bsub[i][threadIdx.x];\n\n        __syncthreads();\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = sum;\n}\n\nvoid launch_and_time(void (*kernel)(float*, float*, float*, int, int, int), float* A, float* B, float* C, int M, int N, int K, dim3 blocks, dim3 threads, const char* name) {\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    cudaEventRecord(start);\n    kernel<<<blocks, threads>>>(A, B, C, M, N, K);\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n\n    float ms;\n    cudaEventElapsedTime(&ms, start, stop);\n\n    std::cout << name << \" execution time: \" << ms << \" ms\" << std::endl;\n\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n}\n\nint main() {\n    int M = 512, N = 512, K = 512;\n    size_t size_A = M * K * sizeof(float);\n    size_t size_B = K * N * sizeof(float);\n    size_t size_C = M * N * sizeof(float);\n\n    float *A, *B, *C;\n    cudaMallocManaged(&A, size_A);\n    cudaMallocManaged(&B, size_B);\n    cudaMallocManaged(&C, size_C);\n\n    for (int i = 0; i < M * K; ++i) A[i] = 1.0f;\n    for (int i = 0; i < K * N; ++i) B[i] = 1.0f;\n\n    dim3 threads(TILE_SIZE, TILE_SIZE);\n    dim3 blocks((N + TILE_SIZE - 1) / TILE_SIZE, (M + TILE_SIZE - 1) / TILE_SIZE);\n\n    launch_and_time(matmul_naive, A, B, C, M, N, K, blocks, threads, \"Naive Matmul\");\n    launch_and_time(matmul_row_major, A, B, C, M, N, K, blocks, threads, \"Row-Major Matmul\");\n\n    dim3 group_threads(TILE_SIZE, TILE_SIZE, GROUP_SIZE);\n    int num_groups = (M / TILE_SIZE / GROUP_SIZE) * (N / TILE_SIZE);\n    launch_and_time(matmul_grouped, A, B, C, M, N, K, dim3(num_groups), group_threads, \"Grouped Matmul\");\n\n    cudaFree(A);\n    cudaFree(B);\n    cudaFree(C);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#define TILE_SIZE 16\n#define GROUP_SIZE 3\n\n__global__ void matmul_naive(float* A, float* B, float* C, int M, int N, int K) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < M && col < N) {\n        float sum = 0.0f;\n        for (int i = 0; i < K; ++i)\n            sum += A[row * K + i] * B[i * N + col];\n        C[row * N + col] = sum;\n    }\n}\n\n__global__ void matmul_row_major(float* A, float* B, float* C, int M, int N, int K) {\n    __shared__ float Asub[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bsub[TILE_SIZE][TILE_SIZE];\n\n    int row = blockIdx.y * TILE_SIZE + threadIdx.y;\n    int col = blockIdx.x * TILE_SIZE + threadIdx.x;\n\n    float sum = 0.0f;\n\n    for (int tile = 0; tile < (K + TILE_SIZE - 1) / TILE_SIZE; ++tile) {\n        if (row < M && tile * TILE_SIZE + threadIdx.x < K)\n            Asub[threadIdx.y][threadIdx.x] = A[row * K + tile * TILE_SIZE + threadIdx.x];\n        else\n            Asub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        if (tile * TILE_SIZE + threadIdx.y < K && col < N)\n            Bsub[threadIdx.y][threadIdx.x] = B[(tile * TILE_SIZE + threadIdx.y) * N + col];\n        else\n            Bsub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i)\n            sum += Asub[threadIdx.y][i] * Bsub[i][threadIdx.x];\n\n        __syncthreads();\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = sum;\n}\n\n__global__ void matmul_grouped(float* A, float* B, float* C, int M, int N, int K) {\n    __shared__ float Asub[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bsub[TILE_SIZE][TILE_SIZE];\n\n    int group_id = blockIdx.x;\n    int tile_id_in_group = threadIdx.z;\n\n    int tiles_per_row = N / TILE_SIZE;\n    int tile_row = (group_id / tiles_per_row) * GROUP_SIZE + tile_id_in_group;\n    int tile_col = group_id % tiles_per_row;\n\n    int row = tile_row * TILE_SIZE + threadIdx.y;\n    int col = tile_col * TILE_SIZE + threadIdx.x;\n\n    float sum = 0.0f;\n\n    for (int tile = 0; tile < (K + TILE_SIZE - 1) / TILE_SIZE; ++tile) {\n        int tiled_k = tile * TILE_SIZE;\n\n        if (row < M && tiled_k + threadIdx.x < K)\n            Asub[threadIdx.y][threadIdx.x] = A[row * K + tiled_k + threadIdx.x];\n        else\n            Asub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        if (tiled_k + threadIdx.y < K && col < N)\n            Bsub[threadIdx.y][threadIdx.x] = B[(tiled_k + threadIdx.y) * N + col];\n        else\n            Bsub[threadIdx.y][threadIdx.x] = 0.0f;\n\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i)\n            sum += Asub[threadIdx.y][i] * Bsub[i][threadIdx.x];\n\n        __syncthreads();\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = sum;\n}\n\nvoid launch_and_time(void (*kernel)(float*, float*, float*, int, int, int), float* A, float* B, float* C, int M, int N, int K, dim3 blocks, dim3 threads, const char* name) {\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    hipEventRecord(start);\n    kernel<<<blocks, threads>>>(A, B, C, M, N, K);\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n\n    float ms;\n    hipEventElapsedTime(&ms, start, stop);\n\n    std::cout << name << \" execution time: \" << ms << \" ms\" << std::endl;\n\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n}\n\nint main() {\n    int M = 512, N = 512, K = 512;\n    size_t size_A = M * K * sizeof(float);\n    size_t size_B = K * N * sizeof(float);\n    size_t size_C = M * N * sizeof(float);\n\n    float *A, *B, *C;\n    hipMallocManaged(&A, size_A);\n    hipMallocManaged(&B, size_B);\n    hipMallocManaged(&C, size_C);\n\n    for (int i = 0; i < M * K; ++i) A[i] = 1.0f;\n    for (int i = 0; i < K * N; ++i) B[i] = 1.0f;\n\n    dim3 threads(TILE_SIZE, TILE_SIZE);\n    dim3 blocks((N + TILE_SIZE - 1) / TILE_SIZE, (M + TILE_SIZE - 1) / TILE_SIZE);\n\n    launch_and_time(matmul_naive, A, B, C, M, N, K, blocks, threads, \"Naive Matmul\");\n    launch_and_time(matmul_row_major, A, B, C, M, N, K, blocks, threads, \"Row-Major Matmul\");\n\n    dim3 group_threads(TILE_SIZE, TILE_SIZE, GROUP_SIZE);\n    int num_groups = (M / TILE_SIZE / GROUP_SIZE) * (N / TILE_SIZE);\n    launch_and_time(matmul_grouped, A, B, C, M, N, K, dim3(num_groups), group_threads, \"Grouped Matmul\");\n\n    hipFree(A);\n    hipFree(B);\n    hipFree(C);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Triton/grouped_matmul.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void leaky_relu_kernel(const float4* __restrict__ input, float4* __restrict__ output, size_t n, size_t m, float alpha) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = (blockIdx.x * blockDim.x + threadIdx.x) * 4;\n\n    if (row < n && col < m) {\n        int idx = row * (m / 4) + (col / 4);\n        float4 val = __ldg(&input[idx]);\n        val.x = val.x * (val.x >= 0.0f) + alpha * val.x * (val.x < 0.0f);\n        val.y = val.y * (val.y >= 0.0f) + alpha * val.y * (val.y < 0.0f);\n        val.z = val.z * (val.z >= 0.0f) + alpha * val.z * (val.z < 0.0f);\n        val.w = val.w * (val.w >= 0.0f) + alpha * val.w * (val.w < 0.0f);\n        output[idx] = val;\n    }\n}\n\nextern \"C\" void solution(float* input, float* output, size_t n, size_t m, float alpha) {\n    float4* input4 = reinterpret_cast<float4*>(input);\n    float4* output4 = reinterpret_cast<float4*>(output);\n    size_t m4 = m / 4;\n    dim3 blockSize(512, 1);  \n    dim3 gridSize((m4 + blockSize.x - 1) / blockSize.x, (n + blockSize.y - 1) / blockSize.y);\n\n    leaky_relu_kernel<<<gridSize, blockSize>>>(input4, output4, n, m, alpha);\n    cudaDeviceSynchronize();\n}\n", "output": "#include <hip/hip_runtime.h>\n\n__global__ void leaky_relu_kernel(const float4* __restrict__ input, float4* __restrict__ output, size_t n, size_t m, float alpha) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = (blockIdx.x * blockDim.x + threadIdx.x) * 4;\n\n    if (row < n && col < m) {\n        int idx = row * (m / 4) + (col / 4);\n        float4 val = __ldg(&input[idx]);\n        val.x = val.x * (val.x >= 0.0f) + alpha * val.x * (val.x < 0.0f);\n        val.y = val.y * (val.y >= 0.0f) + alpha * val.y * (val.y < 0.0f);\n        val.z = val.z * (val.z >= 0.0f) + alpha * val.z * (val.z < 0.0f);\n        val.w = val.w * (val.w >= 0.0f) + alpha * val.w * (val.w < 0.0f);\n        output[idx] = val;\n    }\n}\n\nextern \"C\" void solution(float* input, float* output, size_t n, size_t m, float alpha) {\n    float4* input4 = reinterpret_cast<float4*>(input);\n    float4* output4 = reinterpret_cast<float4*>(output);\n    size_t m4 = m / 4;\n    dim3 blockSize(512, 1);  \n    dim3 gridSize((m4 + blockSize.x - 1) / blockSize.x, (n + blockSize.y - 1) / blockSize.y);\n\n    leaky_relu_kernel<<<gridSize, blockSize>>>(input4, output4, n, m, alpha);\n    hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/100-days-cuda/Day 36/leaky_relu.cu"}
{"input": "\ufeff#include <iostream>\n#include <cuda_runtime.h>\n#include <chrono>\n\n#define N 10000000  // Large size for benchmarking\n\n// CUDA Kernel for Vector Addition\n__global__ void vectorAdd(const float* A, const float* B, float* C, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        C[i] = A[i] + B[i];\n    }\n}\n\n// CPU Function for Vector Addition\nvoid vectorAddCPU(const float* A, const float* B, float* C, int n) {\n    for (int i = 0; i < n; i++) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    size_t size = N * sizeof(float);\n\n    // Allocate memory on host\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C_cpu = (float*)malloc(size);\n    float* h_C_gpu = (float*)malloc(size);\n\n    // Initialize input vectors\n    for (int i = 0; i < N; i++) {\n        h_A[i] = i * 1.0f;\n        h_B[i] = i * 2.0f;\n    }\n\n    // ---------------- CPU Benchmark ----------------\n    auto start_cpu = std::chrono::high_resolution_clock::now();\n    vectorAddCPU(h_A, h_B, h_C_cpu, N);\n    auto stop_cpu = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<float, std::milli> duration_cpu = stop_cpu - start_cpu;\n    std::cout << \"CPU Time: \" << duration_cpu.count() << \" ms\" << std::endl;\n\n    // ---------------- GPU Benchmark ----------------\n\n    // Allocate memory on device\n    float* d_A, * d_B, * d_C;\n    cudaMalloc((void**)&d_A, size);\n    cudaMalloc((void**)&d_B, size);\n    cudaMalloc((void**)&d_C, size);\n\n    // Copy data from host to device\n    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n    // Configure kernel execution\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Use CUDA events for precise timing\n    cudaEvent_t start_gpu, stop_gpu;\n    cudaEventCreate(&start_gpu);\n    cudaEventCreate(&stop_gpu);\n\n    // Start recording time\n    cudaEventRecord(start_gpu);\n    vectorAdd << <blocksPerGrid, threadsPerBlock >> > (d_A, d_B, d_C, N);\n    cudaEventRecord(stop_gpu);\n\n    // Synchronize and measure GPU execution time\n    cudaEventSynchronize(stop_gpu);\n    float gpu_time = 0;\n    cudaEventElapsedTime(&gpu_time, start_gpu, stop_gpu);\n    std::cout << \"GPU Time: \" << gpu_time << \" ms\" << std::endl;\n\n    // Copy result back to host\n    cudaMemcpy(h_C_gpu, d_C, size, cudaMemcpyDeviceToHost);\n\n    // Verify results\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (h_C_cpu[i] != h_C_gpu[i]) {\n            correct = false;\n            std::cout << \"Mismatch at index \" << i << \": CPU(\" << h_C_cpu[i] << \") GPU(\" << h_C_gpu[i] << \")\\n\";\n            break;\n        }\n    }\n    if (correct) {\n        std::cout << \"Results match!\\n\";\n    }\n    else {\n        std::cout << \"Results do not match!\\n\";\n    }\n\n    // Free GPU memory\n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C);\n\n    // Free CPU memory\n    free(h_A);\n    free(h_B);\n    free(h_C_cpu);\n    free(h_C_gpu);\n\n    return 0;\n}\n", "output": "\ufeff#include <iostream>\n#include <hip/hip_runtime.h>\n#include <chrono>\n\n#define N 10000000  // Large size for benchmarking\n\n// CUDA Kernel for Vector Addition\n__global__ void vectorAdd(const float* A, const float* B, float* C, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        C[i] = A[i] + B[i];\n    }\n}\n\n// CPU Function for Vector Addition\nvoid vectorAddCPU(const float* A, const float* B, float* C, int n) {\n    for (int i = 0; i < n; i++) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    size_t size = N * sizeof(float);\n\n    // Allocate memory on host\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C_cpu = (float*)malloc(size);\n    float* h_C_gpu = (float*)malloc(size);\n\n    // Initialize input vectors\n    for (int i = 0; i < N; i++) {\n        h_A[i] = i * 1.0f;\n        h_B[i] = i * 2.0f;\n    }\n\n    // ---------------- CPU Benchmark ----------------\n    auto start_cpu = std::chrono::high_resolution_clock::now();\n    vectorAddCPU(h_A, h_B, h_C_cpu, N);\n    auto stop_cpu = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<float, std::milli> duration_cpu = stop_cpu - start_cpu;\n    std::cout << \"CPU Time: \" << duration_cpu.count() << \" ms\" << std::endl;\n\n    // ---------------- GPU Benchmark ----------------\n\n    // Allocate memory on device\n    float* d_A, * d_B, * d_C;\n    hipMalloc((void**)&d_A, size);\n    hipMalloc((void**)&d_B, size);\n    hipMalloc((void**)&d_C, size);\n\n    // Copy data from host to device\n    hipMemcpy(d_A, h_A, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, size, hipMemcpyHostToDevice);\n\n    // Configure kernel execution\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Use CUDA events for precise timing\n    hipEvent_t start_gpu, stop_gpu;\n    hipEventCreate(&start_gpu);\n    hipEventCreate(&stop_gpu);\n\n    // Start recording time\n    hipEventRecord(start_gpu);\n    vectorAdd << <blocksPerGrid, threadsPerBlock >> > (d_A, d_B, d_C, N);\n    hipEventRecord(stop_gpu);\n\n    // Synchronize and measure GPU execution time\n    hipEventSynchronize(stop_gpu);\n    float gpu_time = 0;\n    hipEventElapsedTime(&gpu_time, start_gpu, stop_gpu);\n    std::cout << \"GPU Time: \" << gpu_time << \" ms\" << std::endl;\n\n    // Copy result back to host\n    hipMemcpy(h_C_gpu, d_C, size, hipMemcpyDeviceToHost);\n\n    // Verify results\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (h_C_cpu[i] != h_C_gpu[i]) {\n            correct = false;\n            std::cout << \"Mismatch at index \" << i << \": CPU(\" << h_C_cpu[i] << \") GPU(\" << h_C_gpu[i] << \")\\n\";\n            break;\n        }\n    }\n    if (correct) {\n        std::cout << \"Results match!\\n\";\n    }\n    else {\n        std::cout << \"Results do not match!\\n\";\n    }\n\n    // Free GPU memory\n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C);\n\n    // Free CPU memory\n    free(h_A);\n    free(h_B);\n    free(h_C_cpu);\n    free(h_C_gpu);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 02/VectorAddtionMoreSimpler.cu"}
{"input": "\ufeff#include <iostream>\n#include <cuda_runtime.h>\n#include <chrono>\n\n#define N 10000000  // Large size for benchmarking\n\n// CUDA Kernel for Vector Addition\n__global__ void vectorAdd(const float* A, const float* B, float* C, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        C[i] = A[i] + B[i];\n    }\n}\n\n// CPU Function for Vector Addition\nvoid vectorAddCPU(const float* A, const float* B, float* C, int n) {\n    for (int i = 0; i < n; i++) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    size_t size = N * sizeof(float);\n\n    // Allocate memory on host\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C_cpu = (float*)malloc(size);\n    float* h_C_gpu = (float*)malloc(size);\n\n    // Initialize input vectors\n    for (int i = 0; i < N; i++) {\n        h_A[i] = i * 1.0f;\n        h_B[i] = i * 2.0f;\n    }\n\n    // ---------------- CPU Benchmark ----------------\n    auto start_cpu = std::chrono::high_resolution_clock::now();\n    vectorAddCPU(h_A, h_B, h_C_cpu, N);\n    auto stop_cpu = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<float, std::milli> duration_cpu = stop_cpu - start_cpu;\n    std::cout << \"CPU Time: \" << duration_cpu.count() << \" ms\" << std::endl;\n\n    // ---------------- GPU Benchmark ----------------\n\n    // Allocate memory on device\n    float* d_A, * d_B, * d_C;\n    cudaMalloc((void**)&d_A, size);\n    cudaMalloc((void**)&d_B, size);\n    cudaMalloc((void**)&d_C, size);\n\n    // Copy data from host to device\n    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n    // Configure kernel execution\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Use CUDA events for precise timing\n    cudaEvent_t start_gpu, stop_gpu;\n    cudaEventCreate(&start_gpu);\n    cudaEventCreate(&stop_gpu);\n\n    // Start recording time\n    cudaEventRecord(start_gpu);\n    vectorAdd << <blocksPerGrid, threadsPerBlock >> > (d_A, d_B, d_C, N);\n    cudaEventRecord(stop_gpu);\n\n    // Synchronize and measure GPU execution time\n    cudaEventSynchronize(stop_gpu);\n    float gpu_time = 0;\n    cudaEventElapsedTime(&gpu_time, start_gpu, stop_gpu);\n    std::cout << \"GPU Time: \" << gpu_time << \" ms\" << std::endl;\n\n    // Copy result back to host\n    cudaMemcpy(h_C_gpu, d_C, size, cudaMemcpyDeviceToHost);\n\n    // ---------------- Print Results ----------------\n    std::cout << \"\\nFirst 10 Vector Addition Results (CPU vs. GPU):\\n\";\n    std::cout << \"Index\\tCPU\\tGPU\\n\";\n    for (int i = 0; i < 10; i++) {\n        std::cout << i << \"\\t\" << h_C_cpu[i] << \"\\t\" << h_C_gpu[i] << \"\\n\";\n    }\n\n    // Verify results\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (h_C_cpu[i] != h_C_gpu[i]) {\n            correct = false;\n            std::cout << \"Mismatch at index \" << i << \": CPU(\" << h_C_cpu[i] << \") GPU(\" << h_C_gpu[i] << \")\\n\";\n            break;\n        }\n    }\n    if (correct) {\n        std::cout << \"\\nResults match!\\n\";\n    }\n    else {\n        std::cout << \"\\nResults do not match!\\n\";\n    }\n\n    // Free GPU memory\n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C);\n\n    // Free CPU memory\n    free(h_A);\n    free(h_B);\n    free(h_C_cpu);\n    free(h_C_gpu);\n\n    return 0;\n}\n", "output": "\ufeff#include <iostream>\n#include <hip/hip_runtime.h>\n#include <chrono>\n\n#define N 10000000  // Large size for benchmarking\n\n// CUDA Kernel for Vector Addition\n__global__ void vectorAdd(const float* A, const float* B, float* C, int n) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < n) {\n        C[i] = A[i] + B[i];\n    }\n}\n\n// CPU Function for Vector Addition\nvoid vectorAddCPU(const float* A, const float* B, float* C, int n) {\n    for (int i = 0; i < n; i++) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    size_t size = N * sizeof(float);\n\n    // Allocate memory on host\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C_cpu = (float*)malloc(size);\n    float* h_C_gpu = (float*)malloc(size);\n\n    // Initialize input vectors\n    for (int i = 0; i < N; i++) {\n        h_A[i] = i * 1.0f;\n        h_B[i] = i * 2.0f;\n    }\n\n    // ---------------- CPU Benchmark ----------------\n    auto start_cpu = std::chrono::high_resolution_clock::now();\n    vectorAddCPU(h_A, h_B, h_C_cpu, N);\n    auto stop_cpu = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<float, std::milli> duration_cpu = stop_cpu - start_cpu;\n    std::cout << \"CPU Time: \" << duration_cpu.count() << \" ms\" << std::endl;\n\n    // ---------------- GPU Benchmark ----------------\n\n    // Allocate memory on device\n    float* d_A, * d_B, * d_C;\n    hipMalloc((void**)&d_A, size);\n    hipMalloc((void**)&d_B, size);\n    hipMalloc((void**)&d_C, size);\n\n    // Copy data from host to device\n    hipMemcpy(d_A, h_A, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, size, hipMemcpyHostToDevice);\n\n    // Configure kernel execution\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Use CUDA events for precise timing\n    hipEvent_t start_gpu, stop_gpu;\n    hipEventCreate(&start_gpu);\n    hipEventCreate(&stop_gpu);\n\n    // Start recording time\n    hipEventRecord(start_gpu);\n    vectorAdd << <blocksPerGrid, threadsPerBlock >> > (d_A, d_B, d_C, N);\n    hipEventRecord(stop_gpu);\n\n    // Synchronize and measure GPU execution time\n    hipEventSynchronize(stop_gpu);\n    float gpu_time = 0;\n    hipEventElapsedTime(&gpu_time, start_gpu, stop_gpu);\n    std::cout << \"GPU Time: \" << gpu_time << \" ms\" << std::endl;\n\n    // Copy result back to host\n    hipMemcpy(h_C_gpu, d_C, size, hipMemcpyDeviceToHost);\n\n    // ---------------- Print Results ----------------\n    std::cout << \"\\nFirst 10 Vector Addition Results (CPU vs. GPU):\\n\";\n    std::cout << \"Index\\tCPU\\tGPU\\n\";\n    for (int i = 0; i < 10; i++) {\n        std::cout << i << \"\\t\" << h_C_cpu[i] << \"\\t\" << h_C_gpu[i] << \"\\n\";\n    }\n\n    // Verify results\n    bool correct = true;\n    for (int i = 0; i < N; i++) {\n        if (h_C_cpu[i] != h_C_gpu[i]) {\n            correct = false;\n            std::cout << \"Mismatch at index \" << i << \": CPU(\" << h_C_cpu[i] << \") GPU(\" << h_C_gpu[i] << \")\\n\";\n            break;\n        }\n    }\n    if (correct) {\n        std::cout << \"\\nResults match!\\n\";\n    }\n    else {\n        std::cout << \"\\nResults do not match!\\n\";\n    }\n\n    // Free GPU memory\n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C);\n\n    // Free CPU memory\n    free(h_A);\n    free(h_B);\n    free(h_C_cpu);\n    free(h_C_gpu);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 02/VectorAddtionMoreSimplerBenchmarked.cu"}
{"input": "#include <cuda_runtime.h>\n#include <cusparse.h>\n#include <iostream>\n#include <chrono>\n#include <cmath>\n#include <cstdlib>\n#include <cstring>\n\n#define PRUNE_THRESHOLD 0.0001f\n#define N 4096\n\nusing namespace std;\n\n__global__ void denseCompressionKernel(const float* input, float* output, float scale, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        output[idx] = roundf(input[idx] * scale) / scale;\n    }\n}\n\n__global__ void pruneKernelOptimized(float* data, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = gridDim.x * blockDim.x;\n    for (int i = idx; i < size; i += stride) {\n        if (fabsf(data[i]) < PRUNE_THRESHOLD)\n            data[i] = 0.0f;\n    }\n}\n\nvoid generateRandomData(float* data, int size, float sparsity = 0.1f) {\n    for (int i = 0; i < size; i++) {\n        data[i] = (rand() % 10 < (sparsity * 10)) ? static_cast<float>(rand() % 100) : 0.0f;\n    }\n}\n\nvoid compressSparseCSR(float* d_denseMatrix, int rows, int cols, cusparseHandle_t handle, cudaStream_t stream) {\n    cusparseSetStream(handle, stream);\n    cusparseDnMatDescr_t matA = nullptr;\n    cusparseSpMatDescr_t matB = nullptr;\n    cusparseCreateDnMat(&matA, rows, cols, rows, d_denseMatrix, CUDA_R_32F, CUSPARSE_ORDER_ROW);\n    int *d_csrRowPtr = nullptr, *d_csrColInd = nullptr;\n    float *d_csrVal = nullptr;\n    cudaMalloc(&d_csrRowPtr, (rows + 1) * sizeof(int));\n    cudaMalloc(&d_csrColInd, rows * cols * sizeof(int));\n    cudaMalloc(&d_csrVal, rows * cols * sizeof(float));\n    cudaMemsetAsync(d_csrRowPtr, 0, (rows + 1) * sizeof(int), stream);\n    cusparseCreateCsr(&matB, rows, cols, 0, d_csrRowPtr, d_csrColInd, d_csrVal,\n                      CUSPARSE_INDEX_32I, CUSPARSE_INDEX_32I,\n                      CUSPARSE_INDEX_BASE_ZERO, CUDA_R_32F);\n    pruneKernelOptimized<<<(rows * cols + 255) / 256, 256, 0, stream>>>(d_denseMatrix, rows * cols);\n    cudaStreamSynchronize(stream);\n    size_t bufferSize = 0;\n    void* dBuffer = nullptr;\n    cusparseDenseToSparse_bufferSize(handle, matA, matB, CUSPARSE_DENSETOSPARSE_ALG_DEFAULT, &bufferSize);\n    cudaMalloc(&dBuffer, bufferSize);\n    cusparseSetPointerMode(handle, CUSPARSE_POINTER_MODE_HOST);\n    cusparseDenseToSparse_analysis(handle, matA, matB, CUSPARSE_DENSETOSPARSE_ALG_DEFAULT, dBuffer);\n    cusparseDenseToSparse_convert(handle, matA, matB, CUSPARSE_DENSETOSPARSE_ALG_DEFAULT, dBuffer);\n    int nnz;\n    cudaMemcpyAsync(&nnz, d_csrRowPtr + rows, sizeof(int), cudaMemcpyDeviceToHost, stream);\n    cudaStreamSynchronize(stream);\n    cout << \"Sparse Compression (CSR) - Non-Zero Count: \" << nnz << \"\\n\";\n    cudaFree(dBuffer);\n    cusparseDestroyDnMat(matA);\n    cusparseDestroySpMat(matB);\n    cudaFree(d_csrRowPtr);\n    cudaFree(d_csrColInd);\n    cudaFree(d_csrVal);\n}\n\nint main() {\n    srand(0);\n    float* h_denseMatrix = nullptr;\n    cudaMallocHost(&h_denseMatrix, N * N * sizeof(float));\n    generateRandomData(h_denseMatrix, N * N);\n    float* d_denseMatrix = nullptr;\n    cudaMalloc(&d_denseMatrix, N * N * sizeof(float));\n    cudaMemcpy(d_denseMatrix, h_denseMatrix, N * N * sizeof(float), cudaMemcpyHostToDevice);\n    float* d_compressedDense = nullptr;\n    cudaMalloc(&d_compressedDense, N * N * sizeof(float));\n    int initialNNZ = 0;\n    for (int i = 0; i < N * N; i++) {\n        if (h_denseMatrix[i] != 0.0f)\n            initialNNZ++;\n    }\n    cout << \"Initial Non-Zero Count: \" << initialNNZ << \"\\n\";\n    cudaEvent_t startEvent, stopEvent;\n    cudaEventCreate(&startEvent);\n    cudaEventCreate(&stopEvent);\n    cudaStream_t sparseStream;\n    cudaStreamCreate(&sparseStream);\n    cudaEventRecord(startEvent, 0);\n    denseCompressionKernel<<<(N * N + 255) / 256, 256>>>(d_denseMatrix, d_compressedDense, 100.0f, N * N);\n    cudaEventRecord(stopEvent, 0);\n    cudaEventSynchronize(stopEvent);\n    float denseTime = 0.0f;\n    cudaEventElapsedTime(&denseTime, startEvent, stopEvent);\n    cout << \"Dense Compression Time: \" << denseTime << \" ms\\n\";\n    cusparseHandle_t handle;\n    cusparseCreate(&handle);\n    cusparseSetStream(handle, sparseStream);\n    cudaEventRecord(startEvent, sparseStream);\n    compressSparseCSR(d_denseMatrix, N, N, handle, sparseStream);\n    cudaEventRecord(stopEvent, sparseStream);\n    cudaStreamSynchronize(sparseStream);\n    float sparseTime = 0.0f;\n    cudaEventElapsedTime(&sparseTime, startEvent, stopEvent);\n    cout << \"Sparse Compression Time: \" << sparseTime << \" ms\\n\";\n    cudaEventDestroy(startEvent);\n    cudaEventDestroy(stopEvent);\n    cudaStreamDestroy(sparseStream);\n    cudaFree(d_denseMatrix);\n    cudaFree(d_compressedDense);\n    cudaFreeHost(h_denseMatrix);\n    cusparseDestroy(handle);\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <hipsparse.h>\n#include <iostream>\n#include <chrono>\n#include <cmath>\n#include <cstdlib>\n#include <cstring>\n\n#define PRUNE_THRESHOLD 0.0001f\n#define N 4096\n\nusing namespace std;\n\n__global__ void denseCompressionKernel(const float* input, float* output, float scale, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        output[idx] = roundf(input[idx] * scale) / scale;\n    }\n}\n\n__global__ void pruneKernelOptimized(float* data, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int stride = gridDim.x * blockDim.x;\n    for (int i = idx; i < size; i += stride) {\n        if (fabsf(data[i]) < PRUNE_THRESHOLD)\n            data[i] = 0.0f;\n    }\n}\n\nvoid generateRandomData(float* data, int size, float sparsity = 0.1f) {\n    for (int i = 0; i < size; i++) {\n        data[i] = (rand() % 10 < (sparsity * 10)) ? static_cast<float>(rand() % 100) : 0.0f;\n    }\n}\n\nvoid compressSparseCSR(float* d_denseMatrix, int rows, int cols, hipsparseHandle_t handle, hipStream_t stream) {\n    hipsparseSetStream(handle, stream);\n    hipsparseDnMatDescr_t matA = nullptr;\n    hipsparseSpMatDescr_t matB = nullptr;\n    hipsparseCreateDnMat(&matA, rows, cols, rows, d_denseMatrix, HIP_R_32F, HIPSPARSE_ORDER_ROW);\n    int *d_csrRowPtr = nullptr, *d_csrColInd = nullptr;\n    float *d_csrVal = nullptr;\n    hipMalloc(&d_csrRowPtr, (rows + 1) * sizeof(int));\n    hipMalloc(&d_csrColInd, rows * cols * sizeof(int));\n    hipMalloc(&d_csrVal, rows * cols * sizeof(float));\n    hipMemsetAsync(d_csrRowPtr, 0, (rows + 1) * sizeof(int), stream);\n    hipsparseCreateCsr(&matB, rows, cols, 0, d_csrRowPtr, d_csrColInd, d_csrVal,\n                      HIPSPARSE_INDEX_32I, HIPSPARSE_INDEX_32I,\n                      HIPSPARSE_INDEX_BASE_ZERO, HIP_R_32F);\n    pruneKernelOptimized<<<(rows * cols + 255) / 256, 256, 0, stream>>>(d_denseMatrix, rows * cols);\n    hipStreamSynchronize(stream);\n    size_t bufferSize = 0;\n    void* dBuffer = nullptr;\n    hipsparseDenseToSparse_bufferSize(handle, matA, matB, HIPSPARSE_DENSETOSPARSE_ALG_DEFAULT, &bufferSize);\n    hipMalloc(&dBuffer, bufferSize);\n    hipsparseSetPointerMode(handle, HIPSPARSE_POINTER_MODE_HOST);\n    hipsparseDenseToSparse_analysis(handle, matA, matB, HIPSPARSE_DENSETOSPARSE_ALG_DEFAULT, dBuffer);\n    hipsparseDenseToSparse_convert(handle, matA, matB, HIPSPARSE_DENSETOSPARSE_ALG_DEFAULT, dBuffer);\n    int nnz;\n    hipMemcpyAsync(&nnz, d_csrRowPtr + rows, sizeof(int), hipMemcpyDeviceToHost, stream);\n    hipStreamSynchronize(stream);\n    cout << \"Sparse Compression (CSR) - Non-Zero Count: \" << nnz << \"\\n\";\n    hipFree(dBuffer);\n    hipsparseDestroyDnMat(matA);\n    hipsparseDestroySpMat(matB);\n    hipFree(d_csrRowPtr);\n    hipFree(d_csrColInd);\n    hipFree(d_csrVal);\n}\n\nint main() {\n    srand(0);\n    float* h_denseMatrix = nullptr;\n    hipHostMalloc(&h_denseMatrix, N * N * sizeof(float));\n    generateRandomData(h_denseMatrix, N * N);\n    float* d_denseMatrix = nullptr;\n    hipMalloc(&d_denseMatrix, N * N * sizeof(float));\n    hipMemcpy(d_denseMatrix, h_denseMatrix, N * N * sizeof(float), hipMemcpyHostToDevice);\n    float* d_compressedDense = nullptr;\n    hipMalloc(&d_compressedDense, N * N * sizeof(float));\n    int initialNNZ = 0;\n    for (int i = 0; i < N * N; i++) {\n        if (h_denseMatrix[i] != 0.0f)\n            initialNNZ++;\n    }\n    cout << \"Initial Non-Zero Count: \" << initialNNZ << \"\\n\";\n    hipEvent_t startEvent, stopEvent;\n    hipEventCreate(&startEvent);\n    hipEventCreate(&stopEvent);\n    hipStream_t sparseStream;\n    hipStreamCreate(&sparseStream);\n    hipEventRecord(startEvent, 0);\n    denseCompressionKernel<<<(N * N + 255) / 256, 256>>>(d_denseMatrix, d_compressedDense, 100.0f, N * N);\n    hipEventRecord(stopEvent, 0);\n    hipEventSynchronize(stopEvent);\n    float denseTime = 0.0f;\n    hipEventElapsedTime(&denseTime, startEvent, stopEvent);\n    cout << \"Dense Compression Time: \" << denseTime << \" ms\\n\";\n    hipsparseHandle_t handle;\n    hipsparseCreate(&handle);\n    hipsparseSetStream(handle, sparseStream);\n    hipEventRecord(startEvent, sparseStream);\n    compressSparseCSR(d_denseMatrix, N, N, handle, sparseStream);\n    hipEventRecord(stopEvent, sparseStream);\n    hipStreamSynchronize(sparseStream);\n    float sparseTime = 0.0f;\n    hipEventElapsedTime(&sparseTime, startEvent, stopEvent);\n    cout << \"Sparse Compression Time: \" << sparseTime << \" ms\\n\";\n    hipEventDestroy(startEvent);\n    hipEventDestroy(stopEvent);\n    hipStreamDestroy(sparseStream);\n    hipFree(d_denseMatrix);\n    hipFree(d_compressedDense);\n    hipHostFree(h_denseMatrix);\n    hipsparseDestroy(handle);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 49/compression.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#define STATES 6\n#define EPISODES 1000\n#define ALPHA 0.1    // Learning rate\n#define GAMMA 0.9    // Discount factor\n#define LAMBDA 0.8   // Trace decay rate\n\n__global__ void td_lambda_kernel(float *V, float *E, int *state_transitions, float *rewards) {\n    int tid = threadIdx.x;\n\n    if (tid < STATES) {\n        float delta;\n        for (int episode = 0; episode < EPISODES; episode++) {\n            for (int t = 0; t < STATES - 1; t++) {\n                int s = t;\n                int s_next = state_transitions[t];\n\n                // TD Error\n                delta = rewards[s] + GAMMA * V[s_next] - V[s];\n\n                // Update eligibility traces\n                E[s] = E[s] * LAMBDA * GAMMA + 1.0f;\n\n                // Update value function\n                V[s] += ALPHA * delta * E[s];\n            }\n        }\n    }\n}\n\nvoid td_lambda() {\n    float V[STATES] = {0};  // State-value function\n    float E[STATES] = {0};  // Eligibility traces\n    int state_transitions[STATES] = {1, 2, 3, 4, 5, 5};  // State transitions\n    float rewards[STATES] = {0, 0, 0, 0, 1, 0}; // Reward structure\n\n    // Allocate device memory\n    float *d_V, *d_E, *d_rewards;\n    int *d_state_transitions;\n    cudaMalloc((void**)&d_V, STATES * sizeof(float));\n    cudaMalloc((void**)&d_E, STATES * sizeof(float));\n    cudaMalloc((void**)&d_state_transitions, STATES * sizeof(int));\n    cudaMalloc((void**)&d_rewards, STATES * sizeof(float));\n\n    // Copy data to device\n    cudaMemcpy(d_V, V, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_E, E, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_state_transitions, state_transitions, STATES * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, rewards, STATES * sizeof(float), cudaMemcpyHostToDevice);\n\n    // Launch kernel\n    td_lambda_kernel<<<1, STATES>>>(d_V, d_E, d_state_transitions, d_rewards);\n    cudaDeviceSynchronize();\n\n    // Copy result back to host\n    cudaMemcpy(V, d_V, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n\n    // Print results\n    std::cout << \"TD(\u03bb) Value Function:\\n\";\n    for (int i = 0; i < STATES; i++) {\n        std::cout << V[i] << \" \";\n    }\n    std::cout << std::endl;\n\n    // Free memory\n    cudaFree(d_V);\n    cudaFree(d_E);\n    cudaFree(d_state_transitions);\n    cudaFree(d_rewards);\n}\n\nint main() {\n    td_lambda();\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#define STATES 6\n#define EPISODES 1000\n#define ALPHA 0.1    // Learning rate\n#define GAMMA 0.9    // Discount factor\n#define LAMBDA 0.8   // Trace decay rate\n\n__global__ void td_lambda_kernel(float *V, float *E, int *state_transitions, float *rewards) {\n    int tid = threadIdx.x;\n\n    if (tid < STATES) {\n        float delta;\n        for (int episode = 0; episode < EPISODES; episode++) {\n            for (int t = 0; t < STATES - 1; t++) {\n                int s = t;\n                int s_next = state_transitions[t];\n\n                // TD Error\n                delta = rewards[s] + GAMMA * V[s_next] - V[s];\n\n                // Update eligibility traces\n                E[s] = E[s] * LAMBDA * GAMMA + 1.0f;\n\n                // Update value function\n                V[s] += ALPHA * delta * E[s];\n            }\n        }\n    }\n}\n\nvoid td_lambda() {\n    float V[STATES] = {0};  // State-value function\n    float E[STATES] = {0};  // Eligibility traces\n    int state_transitions[STATES] = {1, 2, 3, 4, 5, 5};  // State transitions\n    float rewards[STATES] = {0, 0, 0, 0, 1, 0}; // Reward structure\n\n    // Allocate device memory\n    float *d_V, *d_E, *d_rewards;\n    int *d_state_transitions;\n    hipMalloc((void**)&d_V, STATES * sizeof(float));\n    hipMalloc((void**)&d_E, STATES * sizeof(float));\n    hipMalloc((void**)&d_state_transitions, STATES * sizeof(int));\n    hipMalloc((void**)&d_rewards, STATES * sizeof(float));\n\n    // Copy data to device\n    hipMemcpy(d_V, V, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_E, E, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_state_transitions, state_transitions, STATES * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, rewards, STATES * sizeof(float), hipMemcpyHostToDevice);\n\n    // Launch kernel\n    td_lambda_kernel<<<1, STATES>>>(d_V, d_E, d_state_transitions, d_rewards);\n    hipDeviceSynchronize();\n\n    // Copy result back to host\n    hipMemcpy(V, d_V, STATES * sizeof(float), hipMemcpyDeviceToHost);\n\n    // Print results\n    std::cout << \"TD(\u03bb) Value Function:\\n\";\n    for (int i = 0; i < STATES; i++) {\n        std::cout << V[i] << \" \";\n    }\n    std::cout << std::endl;\n\n    // Free memory\n    hipFree(d_V);\n    hipFree(d_E);\n    hipFree(d_state_transitions);\n    hipFree(d_rewards);\n}\n\nint main() {\n    td_lambda();\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 60/td_lambda.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define STATES 6\n#define EPISODES 1000\n#define ALPHA 0.1f   // Learning rate\n#define GAMMA 0.9f   // Discount factor\n\n__global__ void td0_update(float *V, int *transitions, float *rewards) {\n    int state = threadIdx.x;\n    int next_state = transitions[state];\n    float reward = rewards[state];\n    \n    // TD(0) Update Rule: V(s) <- V(s) + alpha * [R + gamma * V(s') - V(s)]\n    V[state] += ALPHA * (reward + GAMMA * V[next_state] - V[state]);\n}\n\nvoid print_values(float *V) {\n    for (int i = 0; i < STATES; i++) {\n        printf(\"%.4f \", V[i]);\n    }\n    printf(\"\\n\");\n}\n\nint main() {\n    float *d_V, *d_rewards;\n    int *d_transitions;\n    \n    float V[STATES] = {0, 0, 0, 0, 0, 0};  // Initialize value function\n    float rewards[STATES] = {0, 1, -1, 2, -2, 3}; // Example rewards\n    int transitions[STATES] = {1, 2, 3, 4, 5, 5}; // Example state transitions\n\n    cudaMalloc(&d_V, STATES * sizeof(float));\n    cudaMalloc(&d_rewards, STATES * sizeof(float));\n    cudaMalloc(&d_transitions, STATES * sizeof(int));\n    \n    cudaMemcpy(d_V, V, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, rewards, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, transitions, STATES * sizeof(int), cudaMemcpyHostToDevice);\n    \n    for (int episode = 0; episode < EPISODES; episode++) {\n        td0_update<<<1, STATES>>>(d_V, d_transitions, d_rewards);\n        cudaDeviceSynchronize();\n    }\n    \n    cudaMemcpy(V, d_V, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n    \n    printf(\"TD(0) Value Function:\\n\");\n    print_values(V);\n    \n    cudaFree(d_V);\n    cudaFree(d_rewards);\n    cudaFree(d_transitions);\n    return 0;\n}", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define STATES 6\n#define EPISODES 1000\n#define ALPHA 0.1f   // Learning rate\n#define GAMMA 0.9f   // Discount factor\n\n__global__ void td0_update(float *V, int *transitions, float *rewards) {\n    int state = threadIdx.x;\n    int next_state = transitions[state];\n    float reward = rewards[state];\n    \n    // TD(0) Update Rule: V(s) <- V(s) + alpha * [R + gamma * V(s') - V(s)]\n    V[state] += ALPHA * (reward + GAMMA * V[next_state] - V[state]);\n}\n\nvoid print_values(float *V) {\n    for (int i = 0; i < STATES; i++) {\n        printf(\"%.4f \", V[i]);\n    }\n    printf(\"\\n\");\n}\n\nint main() {\n    float *d_V, *d_rewards;\n    int *d_transitions;\n    \n    float V[STATES] = {0, 0, 0, 0, 0, 0};  // Initialize value function\n    float rewards[STATES] = {0, 1, -1, 2, -2, 3}; // Example rewards\n    int transitions[STATES] = {1, 2, 3, 4, 5, 5}; // Example state transitions\n\n    hipMalloc(&d_V, STATES * sizeof(float));\n    hipMalloc(&d_rewards, STATES * sizeof(float));\n    hipMalloc(&d_transitions, STATES * sizeof(int));\n    \n    hipMemcpy(d_V, V, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, rewards, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, transitions, STATES * sizeof(int), hipMemcpyHostToDevice);\n    \n    for (int episode = 0; episode < EPISODES; episode++) {\n        td0_update<<<1, STATES>>>(d_V, d_transitions, d_rewards);\n        hipDeviceSynchronize();\n    }\n    \n    hipMemcpy(V, d_V, STATES * sizeof(float), hipMemcpyDeviceToHost);\n    \n    printf(\"TD(0) Value Function:\\n\");\n    print_values(V);\n    \n    hipFree(d_V);\n    hipFree(d_rewards);\n    hipFree(d_transitions);\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 60/td0.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n#include <curand_kernel.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define EPISODES 1000\n#define GAMMA 0.9\n\n__device__ int generateEpisode(int *episode, float *rewards, float *transitions, curandState *state) {\n    int s = curand(state) % STATES; // Start from a random state\n    int length = 0;\n    \n    while (length < STATES) { // Limit episode length to STATES\n        int a = curand(state) % ACTIONS; // Random action selection\n        float rand_val = curand_uniform(state);\n        float prob_sum = 0.0;\n        int next_s = 0;\n        \n        for (int i = 0; i < STATES; i++) {\n            prob_sum += transitions[s * STATES * ACTIONS + a * STATES + i];\n            if (rand_val <= prob_sum) {\n                next_s = i;\n                break;\n            }\n        }\n        \n        episode[length * 3] = s;\n        episode[length * 3 + 1] = a;\n        episode[length * 3 + 2] = next_s;\n        s = next_s;\n        length++;\n    }\n    return length;\n}\n\n__global__ void monteCarloPrediction(float *V, float *rewards, float *transitions) {\n    int s = threadIdx.x;\n    curandState state;\n    curand_init(clock64(), s, 0, &state);\n    \n    float returns[STATES] = {0};\n    int visit_count[STATES] = {0};\n    int episode[STATES * 3];\n    \n    for (int ep = 0; ep < EPISODES; ep++) {\n        int length = generateEpisode(episode, rewards, transitions, &state);\n        float G = 0.0;\n        \n        for (int t = length - 1; t >= 0; t--) {\n            int state_t = episode[t * 3];\n            int action_t = episode[t * 3 + 1];\n            G = rewards[state_t * ACTIONS + action_t] + GAMMA * G;\n            \n            bool first_visit = true;\n            for (int k = 0; k < t; k++) {\n                if (episode[k * 3] == state_t) {\n                    first_visit = false;\n                    break;\n                }\n            }\n            \n            if (first_visit) {\n                returns[state_t] += G;\n                visit_count[state_t] += 1;\n            }\n        }\n    }\n    \n    for (int i = 0; i < STATES; i++) {\n        if (visit_count[i] > 0) {\n            V[i] = returns[i] / visit_count[i];\n        }\n    }\n}\n\nvoid runMonteCarlo() {\n    float V[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    cudaMalloc(&d_V, STATES * sizeof(float));\n    cudaMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    \n    cudaMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    \n    monteCarloPrediction<<<1, STATES>>>(d_V, d_rewards, d_transitions);\n    cudaMemcpy(V, d_V, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n    \n    printf(\"Monte Carlo Value Function:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n    printf(\"\\n\");\n    \n    cudaFree(d_V);\n    cudaFree(d_rewards);\n    cudaFree(d_transitions);\n}\n\nint main() {\n    runMonteCarlo();\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <hiprand/hiprand_kernel.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define EPISODES 1000\n#define GAMMA 0.9\n\n__device__ int generateEpisode(int *episode, float *rewards, float *transitions, hiprandState *state) {\n    int s = hiprand(state) % STATES; // Start from a random state\n    int length = 0;\n    \n    while (length < STATES) { // Limit episode length to STATES\n        int a = hiprand(state) % ACTIONS; // Random action selection\n        float rand_val = hiprand_uniform(state);\n        float prob_sum = 0.0;\n        int next_s = 0;\n        \n        for (int i = 0; i < STATES; i++) {\n            prob_sum += transitions[s * STATES * ACTIONS + a * STATES + i];\n            if (rand_val <= prob_sum) {\n                next_s = i;\n                break;\n            }\n        }\n        \n        episode[length * 3] = s;\n        episode[length * 3 + 1] = a;\n        episode[length * 3 + 2] = next_s;\n        s = next_s;\n        length++;\n    }\n    return length;\n}\n\n__global__ void monteCarloPrediction(float *V, float *rewards, float *transitions) {\n    int s = threadIdx.x;\n    hiprandState state;\n    hiprand_init(clock64(), s, 0, &state);\n    \n    float returns[STATES] = {0};\n    int visit_count[STATES] = {0};\n    int episode[STATES * 3];\n    \n    for (int ep = 0; ep < EPISODES; ep++) {\n        int length = generateEpisode(episode, rewards, transitions, &state);\n        float G = 0.0;\n        \n        for (int t = length - 1; t >= 0; t--) {\n            int state_t = episode[t * 3];\n            int action_t = episode[t * 3 + 1];\n            G = rewards[state_t * ACTIONS + action_t] + GAMMA * G;\n            \n            bool first_visit = true;\n            for (int k = 0; k < t; k++) {\n                if (episode[k * 3] == state_t) {\n                    first_visit = false;\n                    break;\n                }\n            }\n            \n            if (first_visit) {\n                returns[state_t] += G;\n                visit_count[state_t] += 1;\n            }\n        }\n    }\n    \n    for (int i = 0; i < STATES; i++) {\n        if (visit_count[i] > 0) {\n            V[i] = returns[i] / visit_count[i];\n        }\n    }\n}\n\nvoid runMonteCarlo() {\n    float V[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    hipMalloc(&d_V, STATES * sizeof(float));\n    hipMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    \n    hipMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    \n    monteCarloPrediction<<<1, STATES>>>(d_V, d_rewards, d_transitions);\n    hipMemcpy(V, d_V, STATES * sizeof(float), hipMemcpyDeviceToHost);\n    \n    printf(\"Monte Carlo Value Function:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n    printf(\"\\n\");\n    \n    hipFree(d_V);\n    hipFree(d_rewards);\n    hipFree(d_transitions);\n}\n\nint main() {\n    runMonteCarlo();\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 60/monte_carlo.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <curand_kernel.h>\n#include <math.h>\n#include <time.h>\n\n#define N_STATES 5\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n\n__global__ void initCurand(curandState* state, unsigned long seed) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    curand_init(seed, idx, 0, &state[idx]);\n}\n\n__global__ void boltzmannExploration(float* Q, int* actions, int nStates, int nActions, float temperature, curandState* randState) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        float sumExpQ = 0.0f;\n        __shared__ float expQ_shared[N_ACTIONS];\n        for (int a = 0; a < nActions; a++) {\n            expQ_shared[a] = expf(Q[idx * nActions + a] / temperature);\n            sumExpQ += expQ_shared[a];\n        }\n        float randVal = curand_uniform(&randState[idx]) * sumExpQ;\n        float cumulativeSum = 0.0f;\n        for (int a = 0; a < nActions; a++) {\n            cumulativeSum += expQ_shared[a];\n            if (randVal <= cumulativeSum) {\n                actions[idx] = a;\n                break;\n            }\n        }\n    }\n}\n\n__global__ void epsilonGreedy(float* Q, int* actions, int nStates, int nActions, float epsilon, curandState* randState) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        float randVal = curand_uniform(&randState[idx]);\n        if (randVal < epsilon) {\n            actions[idx] = curand(&randState[idx]) % nActions;\n        } else {\n            float maxQ = -1e9;\n            int bestAction = 0;\n            for (int a = 0; a < nActions; a++) {\n                float qValue = Q[idx * nActions + a];\n                if (qValue > maxQ) {\n                    maxQ = qValue;\n                    bestAction = a;\n                }\n            }\n            actions[idx] = bestAction;\n        }\n    }\n}\n\n__global__ void qLearningUpdate(float* Q, int* actions, float* rewards, int* nextStates, int nStates, int nActions, float alpha, float gamma) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        int action = actions[idx];\n        int nextState = nextStates[idx];\n        float maxQNext = -1e9;\n        for (int a = 0; a < nActions; a++) {\n            maxQNext = fmaxf(maxQNext, Q[nextState * nActions + a]);\n        }\n        Q[idx * nActions + action] += alpha * (rewards[idx] + gamma * maxQNext - Q[idx * nActions + action]);\n    }\n}\n\nint main() {\n    float *d_Q, *d_rewards;\n    int *d_actions, *d_nextStates;\n    curandState* d_randState;\n    float epsilon = 1.0f;\n    float temperature = 1.0f;\n    float epsilonDecay = 0.995f;\n    float minEpsilon = 0.01f;\n    float minTemperature = 0.1f;\n    cudaMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    cudaMalloc(&d_actions, N_STATES * sizeof(int));\n    cudaMalloc(&d_rewards, N_STATES * sizeof(float));\n    cudaMalloc(&d_nextStates, N_STATES * sizeof(int));\n    cudaMalloc(&d_randState, N_STATES * sizeof(curandState));\n    float h_Q[N_STATES * N_ACTIONS];\n    for (int i = 0; i < N_STATES * N_ACTIONS; i++) {\n        h_Q[i] = ((float)rand() / RAND_MAX) * 0.1f;\n    }\n    cudaMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    float h_rewards[N_STATES];\n    int h_nextStates[N_STATES];\n    for (int i = 0; i < N_STATES; i++) {\n        h_rewards[i] = ((float)rand() / RAND_MAX);\n        h_nextStates[i] = rand() % N_STATES;\n    }\n    cudaMemcpy(d_rewards, h_rewards, N_STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_nextStates, h_nextStates, N_STATES * sizeof(int), cudaMemcpyHostToDevice);\n    initCurand<<<(N_STATES + 255) / 256, 256>>>(d_randState, time(NULL));\n    for (int episode = 0; episode < 1000; ++episode) {\n        if (episode % 2 == 0) {\n            boltzmannExploration<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, temperature, d_randState);\n        } else {\n            epsilonGreedy<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, epsilon, d_randState);\n        }\n        qLearningUpdate<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, d_rewards, d_nextStates, N_STATES, N_ACTIONS, ALPHA, GAMMA);\n        epsilon = fmaxf(epsilon * epsilonDecay, minEpsilon);\n        temperature = fmaxf(temperature * epsilonDecay, minTemperature);\n    }\n    float Q[N_STATES * N_ACTIONS];\n    cudaMemcpy(Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), cudaMemcpyDeviceToHost);\n    printf(\"Final Q-table (Sample):\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n    cudaFree(d_Q);\n    cudaFree(d_actions);\n    cudaFree(d_rewards);\n    cudaFree(d_nextStates);\n    cudaFree(d_randState);\n    return 0;\n}", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <hiprand/hiprand_kernel.h>\n#include <math.h>\n#include <time.h>\n\n#define N_STATES 5\n#define N_ACTIONS 4\n#define ALPHA 0.1f\n#define GAMMA 0.9f\n\n__global__ void initCurand(hiprandState* state, unsigned long seed) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    hiprand_init(seed, idx, 0, &state[idx]);\n}\n\n__global__ void boltzmannExploration(float* Q, int* actions, int nStates, int nActions, float temperature, hiprandState* randState) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        float sumExpQ = 0.0f;\n        __shared__ float expQ_shared[N_ACTIONS];\n        for (int a = 0; a < nActions; a++) {\n            expQ_shared[a] = expf(Q[idx * nActions + a] / temperature);\n            sumExpQ += expQ_shared[a];\n        }\n        float randVal = hiprand_uniform(&randState[idx]) * sumExpQ;\n        float cumulativeSum = 0.0f;\n        for (int a = 0; a < nActions; a++) {\n            cumulativeSum += expQ_shared[a];\n            if (randVal <= cumulativeSum) {\n                actions[idx] = a;\n                break;\n            }\n        }\n    }\n}\n\n__global__ void epsilonGreedy(float* Q, int* actions, int nStates, int nActions, float epsilon, hiprandState* randState) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        float randVal = hiprand_uniform(&randState[idx]);\n        if (randVal < epsilon) {\n            actions[idx] = hiprand(&randState[idx]) % nActions;\n        } else {\n            float maxQ = -1e9;\n            int bestAction = 0;\n            for (int a = 0; a < nActions; a++) {\n                float qValue = Q[idx * nActions + a];\n                if (qValue > maxQ) {\n                    maxQ = qValue;\n                    bestAction = a;\n                }\n            }\n            actions[idx] = bestAction;\n        }\n    }\n}\n\n__global__ void qLearningUpdate(float* Q, int* actions, float* rewards, int* nextStates, int nStates, int nActions, float alpha, float gamma) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < nStates) {\n        int action = actions[idx];\n        int nextState = nextStates[idx];\n        float maxQNext = -1e9;\n        for (int a = 0; a < nActions; a++) {\n            maxQNext = fmaxf(maxQNext, Q[nextState * nActions + a]);\n        }\n        Q[idx * nActions + action] += alpha * (rewards[idx] + gamma * maxQNext - Q[idx * nActions + action]);\n    }\n}\n\nint main() {\n    float *d_Q, *d_rewards;\n    int *d_actions, *d_nextStates;\n    hiprandState* d_randState;\n    float epsilon = 1.0f;\n    float temperature = 1.0f;\n    float epsilonDecay = 0.995f;\n    float minEpsilon = 0.01f;\n    float minTemperature = 0.1f;\n    hipMalloc(&d_Q, N_STATES * N_ACTIONS * sizeof(float));\n    hipMalloc(&d_actions, N_STATES * sizeof(int));\n    hipMalloc(&d_rewards, N_STATES * sizeof(float));\n    hipMalloc(&d_nextStates, N_STATES * sizeof(int));\n    hipMalloc(&d_randState, N_STATES * sizeof(hiprandState));\n    float h_Q[N_STATES * N_ACTIONS];\n    for (int i = 0; i < N_STATES * N_ACTIONS; i++) {\n        h_Q[i] = ((float)rand() / RAND_MAX) * 0.1f;\n    }\n    hipMemcpy(d_Q, h_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    float h_rewards[N_STATES];\n    int h_nextStates[N_STATES];\n    for (int i = 0; i < N_STATES; i++) {\n        h_rewards[i] = ((float)rand() / RAND_MAX);\n        h_nextStates[i] = rand() % N_STATES;\n    }\n    hipMemcpy(d_rewards, h_rewards, N_STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_nextStates, h_nextStates, N_STATES * sizeof(int), hipMemcpyHostToDevice);\n    initCurand<<<(N_STATES + 255) / 256, 256>>>(d_randState, time(NULL));\n    for (int episode = 0; episode < 1000; ++episode) {\n        if (episode % 2 == 0) {\n            boltzmannExploration<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, temperature, d_randState);\n        } else {\n            epsilonGreedy<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, N_STATES, N_ACTIONS, epsilon, d_randState);\n        }\n        qLearningUpdate<<<(N_STATES + 255) / 256, 256>>>(d_Q, d_actions, d_rewards, d_nextStates, N_STATES, N_ACTIONS, ALPHA, GAMMA);\n        epsilon = fmaxf(epsilon * epsilonDecay, minEpsilon);\n        temperature = fmaxf(temperature * epsilonDecay, minTemperature);\n    }\n    float Q[N_STATES * N_ACTIONS];\n    hipMemcpy(Q, d_Q, N_STATES * N_ACTIONS * sizeof(float), hipMemcpyDeviceToHost);\n    printf(\"Final Q-table (Sample):\\n\");\n    for (int i = 0; i < N_STATES; i++) {\n        for (int j = 0; j < N_ACTIONS; j++) {\n            printf(\"%.4f \", Q[i * N_ACTIONS + j]);\n        }\n        printf(\"\\n\");\n    }\n    hipFree(d_Q);\n    hipFree(d_actions);\n    hipFree(d_rewards);\n    hipFree(d_nextStates);\n    hipFree(d_randState);\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 51/Boltzman.cu"}
{"input": "#include <cuda.h>\n#include <cuda_runtime.h>\n#include <iostream>\n#include <cstdlib>\n\n#define N 1024  // Matrix size (1024x1024)\n#define TILE 16 // Tiling size\n\n// -------------------- Na\u00efve Kernel -------------------- //\n__global__ void naive_matmul(float *A, float *B, float *C, int n) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < n && col < n) {\n        float sum = 0.0f;\n        for (int k = 0; k < n; ++k) {\n            sum += A[row * n + k] * B[k * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\n// -------------------- Warp-Tiling with Tensor Cores -------------------- //\n#include <mma.h>\nusing namespace nvcuda;\n\n#define WARP_SIZE 32\n#define M 16\n#define K 16\n#define N_T 16\n\n__global__ void tensorcore_matmul(half *A, half *B, float *C, int n) {\n    int warpM = blockIdx.y * (WARP_SIZE / M);\n    int warpN = blockIdx.x * (WARP_SIZE / N_T);\n\n    wmma::fragment<wmma::matrix_a, M, N_T, K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N_T, K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N_T, K, float> c_frag;\n\n    wmma::fill_fragment(c_frag, 0.0f);\n\n    for (int k = 0; k < n; k += K) {\n        wmma::load_matrix_sync(a_frag, A + warpM * n + k, n);\n        wmma::load_matrix_sync(b_frag, B + k * n + warpN, n);\n        wmma::mma_sync(c_frag, a_frag, b_frag, c_frag);\n    }\n\n    wmma::store_matrix_sync(C + warpM * n + warpN, c_frag, n, wmma::mem_row_major);\n}\n\n// -------------------- Main Function -------------------- //\nint main() {\n    size_t bytes = N * N * sizeof(float);\n    size_t bytes_half = N * N * sizeof(half);\n\n    float *h_A, *h_B, *h_C_naive, *h_C_tc;\n    cudaMallocHost(&h_A, bytes);\n    cudaMallocHost(&h_B, bytes);\n    cudaMallocHost(&h_C_naive, bytes);\n    cudaMallocHost(&h_C_tc, bytes);\n\n    for (int i = 0; i < N * N; i++) {\n        h_A[i] = static_cast<float>(rand()) / RAND_MAX;\n        h_B[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n\n    float *d_A, *d_B, *d_C_naive;\n    half *d_A_half, *d_B_half;\n    float *d_C_tc;\n    cudaMalloc(&d_A, bytes);\n    cudaMalloc(&d_B, bytes);\n    cudaMalloc(&d_C_naive, bytes);\n    cudaMalloc(&d_A_half, bytes_half);\n    cudaMalloc(&d_B_half, bytes_half);\n    cudaMalloc(&d_C_tc, bytes);\n\n    cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);\n\n    dim3 blockDim(TILE, TILE);\n    dim3 gridDim(N / TILE, N / TILE);\n\n    cudaEvent_t start, stop;\n    float naiveTime, tcTime;\n    \n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    // ------------------ Measure Na\u00efve MatMul ------------------ //\n    cudaEventRecord(start);\n    naive_matmul<<<gridDim, blockDim>>>(d_A, d_B, d_C_naive, N);\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n    cudaEventElapsedTime(&naiveTime, start, stop);\n\n    cudaMemcpy(h_C_naive, d_C_naive, bytes, cudaMemcpyDeviceToHost);\n\n    // ------------------ Measure Tensor Core MatMul ------------------ //\n    cudaMemcpy(d_A_half, h_A, bytes_half, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B_half, h_B, bytes_half, cudaMemcpyHostToDevice);\n\n    dim3 gridDimTC(N / WARP_SIZE, N / WARP_SIZE);\n\n    cudaEventRecord(start);\n    tensorcore_matmul<<<gridDimTC, WARP_SIZE>>>(d_A_half, d_B_half, d_C_tc, N);\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n    cudaEventElapsedTime(&tcTime, start, stop);\n\n    cudaMemcpy(h_C_tc, d_C_tc, bytes, cudaMemcpyDeviceToHost);\n\n    // ------------------ Compute GFLOPS ------------------ //\n    float naiveGFLOPS = (2.0 * N * N * N) / (naiveTime * 1e6);\n    float tcGFLOPS = (2.0 * N * N * N) / (tcTime * 1e6);\n    float speedup = ((tcGFLOPS - naiveGFLOPS) / naiveGFLOPS) * 100;\n\n    std::cout << \"Naive MatMul GFLOPS: \" << naiveGFLOPS << std::endl;\n    std::cout << \"Tensor Core MatMul GFLOPS: \" << tcGFLOPS << std::endl;\n    std::cout << \"Speedup: \" << speedup << \"% faster\" << std::endl;\n\n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C_naive);\n    cudaFree(d_A_half);\n    cudaFree(d_B_half);\n    cudaFree(d_C_tc);\n    cudaFreeHost(h_A);\n    cudaFreeHost(h_B);\n    cudaFreeHost(h_C_naive);\n    cudaFreeHost(h_C_tc);\n\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n#include <cstdlib>\n\n#define N 1024  // Matrix size (1024x1024)\n#define TILE 16 // Tiling size\n\n// -------------------- Na\u00efve Kernel -------------------- //\n__global__ void naive_matmul(float *A, float *B, float *C, int n) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < n && col < n) {\n        float sum = 0.0f;\n        for (int k = 0; k < n; ++k) {\n            sum += A[row * n + k] * B[k * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\n// -------------------- Warp-Tiling with Tensor Cores -------------------- //\n#include <mma.h>\nusing namespace nvcuda;\n\n#define WARP_SIZE 32\n#define M 16\n#define K 16\n#define N_T 16\n\n__global__ void tensorcore_matmul(half *A, half *B, float *C, int n) {\n    int warpM = blockIdx.y * (WARP_SIZE / M);\n    int warpN = blockIdx.x * (WARP_SIZE / N_T);\n\n    wmma::fragment<wmma::matrix_a, M, N_T, K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, M, N_T, K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, M, N_T, K, float> c_frag;\n\n    wmma::fill_fragment(c_frag, 0.0f);\n\n    for (int k = 0; k < n; k += K) {\n        wmma::load_matrix_sync(a_frag, A + warpM * n + k, n);\n        wmma::load_matrix_sync(b_frag, B + k * n + warpN, n);\n        wmma::mma_sync(c_frag, a_frag, b_frag, c_frag);\n    }\n\n    wmma::store_matrix_sync(C + warpM * n + warpN, c_frag, n, wmma::mem_row_major);\n}\n\n// -------------------- Main Function -------------------- //\nint main() {\n    size_t bytes = N * N * sizeof(float);\n    size_t bytes_half = N * N * sizeof(half);\n\n    float *h_A, *h_B, *h_C_naive, *h_C_tc;\n    hipHostMalloc(&h_A, bytes);\n    hipHostMalloc(&h_B, bytes);\n    hipHostMalloc(&h_C_naive, bytes);\n    hipHostMalloc(&h_C_tc, bytes);\n\n    for (int i = 0; i < N * N; i++) {\n        h_A[i] = static_cast<float>(rand()) / RAND_MAX;\n        h_B[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n\n    float *d_A, *d_B, *d_C_naive;\n    half *d_A_half, *d_B_half;\n    float *d_C_tc;\n    hipMalloc(&d_A, bytes);\n    hipMalloc(&d_B, bytes);\n    hipMalloc(&d_C_naive, bytes);\n    hipMalloc(&d_A_half, bytes_half);\n    hipMalloc(&d_B_half, bytes_half);\n    hipMalloc(&d_C_tc, bytes);\n\n    hipMemcpy(d_A, h_A, bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, bytes, hipMemcpyHostToDevice);\n\n    dim3 blockDim(TILE, TILE);\n    dim3 gridDim(N / TILE, N / TILE);\n\n    hipEvent_t start, stop;\n    float naiveTime, tcTime;\n    \n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    // ------------------ Measure Na\u00efve MatMul ------------------ //\n    hipEventRecord(start);\n    naive_matmul<<<gridDim, blockDim>>>(d_A, d_B, d_C_naive, N);\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n    hipEventElapsedTime(&naiveTime, start, stop);\n\n    hipMemcpy(h_C_naive, d_C_naive, bytes, hipMemcpyDeviceToHost);\n\n    // ------------------ Measure Tensor Core MatMul ------------------ //\n    hipMemcpy(d_A_half, h_A, bytes_half, hipMemcpyHostToDevice);\n    hipMemcpy(d_B_half, h_B, bytes_half, hipMemcpyHostToDevice);\n\n    dim3 gridDimTC(N / WARP_SIZE, N / WARP_SIZE);\n\n    hipEventRecord(start);\n    tensorcore_matmul<<<gridDimTC, WARP_SIZE>>>(d_A_half, d_B_half, d_C_tc, N);\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n    hipEventElapsedTime(&tcTime, start, stop);\n\n    hipMemcpy(h_C_tc, d_C_tc, bytes, hipMemcpyDeviceToHost);\n\n    // ------------------ Compute GFLOPS ------------------ //\n    float naiveGFLOPS = (2.0 * N * N * N) / (naiveTime * 1e6);\n    float tcGFLOPS = (2.0 * N * N * N) / (tcTime * 1e6);\n    float speedup = ((tcGFLOPS - naiveGFLOPS) / naiveGFLOPS) * 100;\n\n    std::cout << \"Naive MatMul GFLOPS: \" << naiveGFLOPS << std::endl;\n    std::cout << \"Tensor Core MatMul GFLOPS: \" << tcGFLOPS << std::endl;\n    std::cout << \"Speedup: \" << speedup << \"% faster\" << std::endl;\n\n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C_naive);\n    hipFree(d_A_half);\n    hipFree(d_B_half);\n    hipFree(d_C_tc);\n    hipHostFree(h_A);\n    hipHostFree(h_B);\n    hipHostFree(h_C_naive);\n    hipHostFree(h_C_tc);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 14/matmul.cu"}
{"input": "#include <iostream>\n#include <curand_kernel.h>\n#include <math.h>\n#include <cuda_runtime.h>\n\n#define S 9       // States\n#define A 4       // Actions\n#define EPS 1000  // Episodes\n#define _A_ 0.1f  // Base learning rate (will be adapted)\n#define _G_ 0.9f  // Discount factor\n\n// We'll use adaptive exploration with exponential decay, so no constant _E_ or _D_ macros here.\n\n__device__ int rnd_action(curandState *s) {\n    return curand(s) % A;\n}\n\n__global__ void init_rand(curandState *state, int seed) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) curand_init(seed, i, 0, &state[i]);\n}\n\n__global__ void q_train(float *Q, float *R, curandState *randS, int *visit_count) {\n    int agent_id = threadIdx.x + blockIdx.x * blockDim.x;\n    if (agent_id >= S) return;\n\n    curandState s = randS[agent_id];\n    int st = agent_id;\n    float e = 1.0f;  // Start with full exploration\n\n    for (int ep = 0; ep < EPS; ep++) {\n        bool done = false;\n        while (!done) {\n            int act;\n            if (curand_uniform(&s) < e) {\n                act = rnd_action(&s); // Exploration: random action\n            } else {\n                // Exploitation: choose best action for current state\n                float bestQ = -1e9;\n                for (int j = 0; j < A; j++) {\n                    float q_val = Q[st * A + j];\n                    if (q_val > bestQ) {\n                        bestQ = q_val;\n                        act = j;\n                    }\n                }\n            }\n\n            // Transition to next state based on action (example transition)\n            int nxt = (st + act) % S;\n            float r = R[nxt];\n\n            // Find max Q for the next state\n            float maxQ = -1e9;\n            for (int j = 0; j < A; j++)\n                maxQ = fmaxf(maxQ, Q[nxt * A + j]);\n\n            // Update visit count and compute adaptive learning rate\n            int idx = st * A + act;\n            visit_count[idx] += 1;\n            float alpha = 1.0f / (1.0f + visit_count[idx]);\n\n            // Compute TD error and update Q-value if significant\n            float td_error = fabsf(r + _G_ * maxQ - Q[idx]);\n            if (td_error > 0.01f) {\n                Q[idx] += alpha * (r + _G_ * maxQ - Q[idx]);\n            }\n\n            st = nxt;\n            if (st == S - 1) done = true;\n        }\n        e = expf(-0.001f * ep);  // Adaptive exploration decay\n    }\n    randS[agent_id] = s;\n}\n\nint main() {\n    float h_Q[S * A] = {0};\n    // Change rewards to float for fractional values\n    float h_R[S] = {-0.1f, -0.1f, 10.0f, -0.5f, -10.0f, -0.5f, -0.5f, -10.0f, 10.0f};\n\n    float *d_Q, *d_R;\n    int *d_visit_count;\n    curandState *d_randS;\n\n    cudaMalloc((void **)&d_Q, S * A * sizeof(float));\n    cudaMalloc((void **)&d_R, S * sizeof(float));\n    cudaMalloc((void **)&d_randS, S * sizeof(curandState));\n    cudaMalloc((void **)&d_visit_count, S * A * sizeof(int));\n\n    cudaMemcpy(d_Q, h_Q, S * A * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_R, h_R, S * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemset(d_visit_count, 0, S * A * sizeof(int));\n\n    // Initialize random states\n    init_rand<<<1, S>>>(d_randS, time(NULL));\n    cudaDeviceSynchronize();\n\n    // Train the Q-table using multi-agent training (one agent per state)\n    q_train<<<1, S>>>(d_Q, d_R, d_randS, d_visit_count);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_Q, d_Q, S * A * sizeof(float), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        std::cout << \"S\" << i << \": \";\n        for (int j = 0; j < A; j++)\n            std::cout << h_Q[i * A + j] << \" \";\n        std::cout << std::endl;\n    }\n\n    cudaFree(d_Q);\n    cudaFree(d_R);\n    cudaFree(d_randS);\n    cudaFree(d_visit_count);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hiprand/hiprand_kernel.h>\n#include <math.h>\n#include <hip/hip_runtime.h>\n\n#define S 9       // States\n#define A 4       // Actions\n#define EPS 1000  // Episodes\n#define _A_ 0.1f  // Base learning rate (will be adapted)\n#define _G_ 0.9f  // Discount factor\n\n// We'll use adaptive exploration with exponential decay, so no constant _E_ or _D_ macros here.\n\n__device__ int rnd_action(hiprandState *s) {\n    return hiprand(s) % A;\n}\n\n__global__ void init_rand(hiprandState *state, int seed) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) hiprand_init(seed, i, 0, &state[i]);\n}\n\n__global__ void q_train(float *Q, float *R, hiprandState *randS, int *visit_count) {\n    int agent_id = threadIdx.x + blockIdx.x * blockDim.x;\n    if (agent_id >= S) return;\n\n    hiprandState s = randS[agent_id];\n    int st = agent_id;\n    float e = 1.0f;  // Start with full exploration\n\n    for (int ep = 0; ep < EPS; ep++) {\n        bool done = false;\n        while (!done) {\n            int act;\n            if (hiprand_uniform(&s) < e) {\n                act = rnd_action(&s); // Exploration: random action\n            } else {\n                // Exploitation: choose best action for current state\n                float bestQ = -1e9;\n                for (int j = 0; j < A; j++) {\n                    float q_val = Q[st * A + j];\n                    if (q_val > bestQ) {\n                        bestQ = q_val;\n                        act = j;\n                    }\n                }\n            }\n\n            // Transition to next state based on action (example transition)\n            int nxt = (st + act) % S;\n            float r = R[nxt];\n\n            // Find max Q for the next state\n            float maxQ = -1e9;\n            for (int j = 0; j < A; j++)\n                maxQ = fmaxf(maxQ, Q[nxt * A + j]);\n\n            // Update visit count and compute adaptive learning rate\n            int idx = st * A + act;\n            visit_count[idx] += 1;\n            float alpha = 1.0f / (1.0f + visit_count[idx]);\n\n            // Compute TD error and update Q-value if significant\n            float td_error = fabsf(r + _G_ * maxQ - Q[idx]);\n            if (td_error > 0.01f) {\n                Q[idx] += alpha * (r + _G_ * maxQ - Q[idx]);\n            }\n\n            st = nxt;\n            if (st == S - 1) done = true;\n        }\n        e = expf(-0.001f * ep);  // Adaptive exploration decay\n    }\n    randS[agent_id] = s;\n}\n\nint main() {\n    float h_Q[S * A] = {0};\n    // Change rewards to float for fractional values\n    float h_R[S] = {-0.1f, -0.1f, 10.0f, -0.5f, -10.0f, -0.5f, -0.5f, -10.0f, 10.0f};\n\n    float *d_Q, *d_R;\n    int *d_visit_count;\n    hiprandState *d_randS;\n\n    hipMalloc((void **)&d_Q, S * A * sizeof(float));\n    hipMalloc((void **)&d_R, S * sizeof(float));\n    hipMalloc((void **)&d_randS, S * sizeof(hiprandState));\n    hipMalloc((void **)&d_visit_count, S * A * sizeof(int));\n\n    hipMemcpy(d_Q, h_Q, S * A * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_R, h_R, S * sizeof(float), hipMemcpyHostToDevice);\n    hipMemset(d_visit_count, 0, S * A * sizeof(int));\n\n    // Initialize random states\n    init_rand<<<1, S>>>(d_randS, time(NULL));\n    hipDeviceSynchronize();\n\n    // Train the Q-table using multi-agent training (one agent per state)\n    q_train<<<1, S>>>(d_Q, d_R, d_randS, d_visit_count);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_Q, d_Q, S * A * sizeof(float), hipMemcpyDeviceToHost);\n\n    std::cout << \"Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        std::cout << \"S\" << i << \": \";\n        for (int j = 0; j < A; j++)\n            std::cout << h_Q[i * A + j] << \" \";\n        std::cout << std::endl;\n    }\n\n    hipFree(d_Q);\n    hipFree(d_R);\n    hipFree(d_randS);\n    hipFree(d_visit_count);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 55/q_cuda.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Optimize based on GPU\n#define WARP_SIZE 32    // Standard warp size\n\n// Warp reduction for max operation\n__device__ float warpReduceMax(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation\n__device__ float warpReduceSum(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xffffffff, val, offset);\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel\n__global__ void softmaxOptimized(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n\n    int row = blockIdx.x;  // Each block processes one row\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, input[row * cols + i]);\n    }\n\n    // Block-wide reduction for max\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n    \n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n    \n    max_val = block_max;\n\n    // Compute exponentials and sum\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_data[i] = expf(input[row * cols + i] - max_val);\n        sum += shared_data[i];\n    }\n\n    // Block-wide reduction for sum\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n    \n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n    \n    sum = block_sum;\n\n    // Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_data[i] / sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    cudaMemcpy(d_input, h_input, size, cudaMemcpyHostToDevice);\n\n    // Launch optimized kernel\n    int shared_mem_size = cols * sizeof(float);\n    softmaxOptimized<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n    \n    // Copy result back\n    cudaMemcpy(h_output, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Free device memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Optimize based on GPU\n#define WARP_SIZE 32    // Standard warp size\n\n// Warp reduction for max operation\n__device__ float warpReduceMax(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation\n__device__ float warpReduceSum(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xffffffff, val, offset);\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel\n__global__ void softmaxOptimized(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n\n    int row = blockIdx.x;  // Each block processes one row\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, input[row * cols + i]);\n    }\n\n    // Block-wide reduction for max\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n    \n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n    \n    max_val = block_max;\n\n    // Compute exponentials and sum\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_data[i] = expf(input[row * cols + i] - max_val);\n        sum += shared_data[i];\n    }\n\n    // Block-wide reduction for sum\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n    \n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n    \n    sum = block_sum;\n\n    // Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_data[i] / sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    hipMemcpy(d_input, h_input, size, hipMemcpyHostToDevice);\n\n    // Launch optimized kernel\n    int shared_mem_size = cols * sizeof(float);\n    softmaxOptimized<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n    \n    // Copy result back\n    hipMemcpy(h_output, d_output, size, hipMemcpyDeviceToHost);\n\n    // Free device memory\n    hipFree(d_input);\n    hipFree(d_output);\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 12/Optimized_Softmax.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Adjust based on your GPU capabilities\n\n// Softmax kernel (row-wise computation)\n__global__ void softmaxKernel(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n    \n    int row = blockIdx.x;  // Each block handles one row\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    float* row_data = input + row * cols;  // Pointer to current row\n    float* shared_row = shared_data;  // Shared memory pointer\n\n    // Step 1: Find max element in the row (for numerical stability)\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, row_data[i]);\n    }\n\n    // Reduce max across threads in the block\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n\n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum them\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = expf(row_data[i] - block_max);\n        sum += shared_row[i];\n    }\n\n    // Reduce sum across threads\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n\n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n\n    // Step 3: Normalize the values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_row[i] / block_sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    cudaMemcpy(d_input, h_input, size, cudaMemcpyHostToDevice);\n\n        // Create events for timing\n        cudaEvent_t start, stop;\n        cudaEventCreate(&start);\n        cudaEventCreate(&stop);\n    \n        // Record the start time\n        cudaEventRecord(start);\n\n    // Launch kernel (1 block per row)\n    softmaxKernel<<<rows, BLOCK_SIZE, cols * sizeof(float)>>>(d_input, d_output, rows, cols);\n\n     // Record the stop time\n     cudaEventRecord(stop);\n     cudaEventSynchronize(stop);\n \n     // Calculate execution time in milliseconds\n     float elapsedTime;\n     cudaEventElapsedTime(&elapsedTime, start, stop);\n     float timeInSeconds = elapsedTime / 1000.0f;\n \n    \n    // Copy result back\n    cudaMemcpy(h_output, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Free device memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n       // Calculate GFLOPS\n       long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n       float gflops = (numFLOPs / timeInSeconds) / 1e9;\n   \n       // Output the results\n       std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n       std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Adjust based on your GPU capabilities\n\n// Softmax kernel (row-wise computation)\n__global__ void softmaxKernel(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n    \n    int row = blockIdx.x;  // Each block handles one row\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    float* row_data = input + row * cols;  // Pointer to current row\n    float* shared_row = shared_data;  // Shared memory pointer\n\n    // Step 1: Find max element in the row (for numerical stability)\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, row_data[i]);\n    }\n\n    // Reduce max across threads in the block\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n\n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum them\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = expf(row_data[i] - block_max);\n        sum += shared_row[i];\n    }\n\n    // Reduce sum across threads\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n\n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n\n    // Step 3: Normalize the values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_row[i] / block_sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    hipMemcpy(d_input, h_input, size, hipMemcpyHostToDevice);\n\n        // Create events for timing\n        hipEvent_t start, stop;\n        hipEventCreate(&start);\n        hipEventCreate(&stop);\n    \n        // Record the start time\n        hipEventRecord(start);\n\n    // Launch kernel (1 block per row)\n    softmaxKernel<<<rows, BLOCK_SIZE, cols * sizeof(float)>>>(d_input, d_output, rows, cols);\n\n     // Record the stop time\n     hipEventRecord(stop);\n     hipEventSynchronize(stop);\n \n     // Calculate execution time in milliseconds\n     float elapsedTime;\n     hipEventElapsedTime(&elapsedTime, start, stop);\n     float timeInSeconds = elapsedTime / 1000.0f;\n \n    \n    // Copy result back\n    hipMemcpy(h_output, d_output, size, hipMemcpyDeviceToHost);\n\n    // Free device memory\n    hipFree(d_input);\n    hipFree(d_output);\n\n       // Calculate GFLOPS\n       long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n       float gflops = (numFLOPs / timeInSeconds) / 1e9;\n   \n       // Output the results\n       std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n       std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 12/Basic_Softmax_GFLOPS.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Optimize based on GPU\n#define WARP_SIZE 32    // Standard warp size\n\n// Warp reduction for max operation\n__device__ float warpReduceMax(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation\n__device__ float warpReduceSum(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xffffffff, val, offset);\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel\n__global__ void softmaxOptimized(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n\n    int row = blockIdx.x;  // Each block processes one row\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, input[row * cols + i]);\n    }\n\n    // Block-wide reduction for max\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n    \n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n    \n    max_val = block_max;\n\n    // Compute exponentials and sum\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_data[i] = expf(input[row * cols + i] - max_val);\n        sum += shared_data[i];\n    }\n\n    // Block-wide reduction for sum\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n    \n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n    \n    sum = block_sum;\n\n    // Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_data[i] / sum;\n    }\n}\n\n// Host function to launch kernel and measure GFLOPS\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    cudaMemcpy(d_input, h_input, size, cudaMemcpyHostToDevice);\n\n    // Create events for timing\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    // Record the start time\n    cudaEventRecord(start);\n\n    // Launch optimized kernel\n    int shared_mem_size = cols * sizeof(float);\n    softmaxOptimized<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n    \n    // Record the stop time\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n\n    // Calculate execution time in milliseconds\n    float elapsedTime;\n    cudaEventElapsedTime(&elapsedTime, start, stop);\n    float timeInSeconds = elapsedTime / 1000.0f;\n\n    // Copy result back\n    cudaMemcpy(h_output, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Free device memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax and measure GFLOPS\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Optimize based on GPU\n#define WARP_SIZE 32    // Standard warp size\n\n// Warp reduction for max operation\n__device__ float warpReduceMax(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation\n__device__ float warpReduceSum(float val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val += __shfl_down_sync(0xffffffff, val, offset);\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel\n__global__ void softmaxOptimized(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n\n    int row = blockIdx.x;  // Each block processes one row\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, input[row * cols + i]);\n    }\n\n    // Block-wide reduction for max\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n    \n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n    \n    max_val = block_max;\n\n    // Compute exponentials and sum\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_data[i] = expf(input[row * cols + i] - max_val);\n        sum += shared_data[i];\n    }\n\n    // Block-wide reduction for sum\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n    \n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n    \n    sum = block_sum;\n\n    // Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_data[i] / sum;\n    }\n}\n\n// Host function to launch kernel and measure GFLOPS\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    hipMemcpy(d_input, h_input, size, hipMemcpyHostToDevice);\n\n    // Create events for timing\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    // Record the start time\n    hipEventRecord(start);\n\n    // Launch optimized kernel\n    int shared_mem_size = cols * sizeof(float);\n    softmaxOptimized<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n    \n    // Record the stop time\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n\n    // Calculate execution time in milliseconds\n    float elapsedTime;\n    hipEventElapsedTime(&elapsedTime, start, stop);\n    float timeInSeconds = elapsedTime / 1000.0f;\n\n    // Copy result back\n    hipMemcpy(h_output, d_output, size, hipMemcpyDeviceToHost);\n\n    // Free device memory\n    hipFree(d_input);\n    hipFree(d_output);\n\n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax and measure GFLOPS\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 12/Optimized_Softmax_GFLOPS.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void tensor4d_matmul_kernel(\n    const float* __restrict__ A, \n    const float* __restrict__ B,\n    float* C,         \n    size_t b, size_t i, size_t j, size_t l, size_t k)\n{\n    int batch = blockIdx.z;\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (batch < b && row < i && col < j) {\n        for (int out_k = 0; out_k < k; ++out_k) {\n            float sum = 0.0f;\n            for (int inner = 0; inner < l; ++inner) {\n                size_t a_idx = ((batch * i + row) * j + col) * l + inner;\n                size_t b_idx = inner * k + out_k;                   \n                sum += A[a_idx] * B[b_idx];\n            }\n            size_t c_idx = ((batch * i + row) * j + col) * k + out_k;\n            C[c_idx] = sum;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t b, size_t i, size_t j, size_t l, size_t k)\n{\n    dim3 blockSize(16, 16); i)\n    dim3 gridSize((j + 15) / 16, (i + 15) / 16, b);\n\n    tensor4d_matmul_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, b, i, j, l, k);\n\n    cudaDeviceSynchronize();\n}\n", "output": "#include <hip/hip_runtime.h>\n\n__global__ void tensor4d_matmul_kernel(\n    const float* __restrict__ A, \n    const float* __restrict__ B,\n    float* C,         \n    size_t b, size_t i, size_t j, size_t l, size_t k)\n{\n    int batch = blockIdx.z;\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (batch < b && row < i && col < j) {\n        for (int out_k = 0; out_k < k; ++out_k) {\n            float sum = 0.0f;\n            for (int inner = 0; inner < l; ++inner) {\n                size_t a_idx = ((batch * i + row) * j + col) * l + inner;\n                size_t b_idx = inner * k + out_k;                   \n                sum += A[a_idx] * B[b_idx];\n            }\n            size_t c_idx = ((batch * i + row) * j + col) * k + out_k;\n            C[c_idx] = sum;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t b, size_t i, size_t j, size_t l, size_t k)\n{\n    dim3 blockSize(16, 16); i)\n    dim3 gridSize((j + 15) / 16, (i + 15) / 16, b);\n\n    tensor4d_matmul_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, b, i, j, l, k);\n\n    hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/100-days-cuda/Day 75/4d_tensor.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void batched_tensor_matmul_kernel(\n    const float* __restrict__ A, \n    const float* __restrict__ B, \n    float* C,                    \n    size_t N, size_t M, size_t K, size_t L)\n{\n    int n = blockIdx.z;\n    int m = blockIdx.y * blockDim.y + threadIdx.y;\n    int l = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (n < N && m < M && l < L) {\n        float sum = 0.0f;\n        for (int k = 0; k < K; ++k) {\n            sum += A[n * M * K + m * K + k] * B[k * L + l];\n        }\n        C[n * M * L + m * L + l] = sum;\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t N, size_t M, size_t K, size_t L)\n{\n    dim3 blockSize(16, 16);\n    dim3 gridSize((L + 15) / 16, (M + 15) / 16, N);\n\n    batched_tensor_matmul_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, N, M, K, L);\n\n    cudaDeviceSynchronize();\n}\n", "output": "#include <hip/hip_runtime.h>\n\n__global__ void batched_tensor_matmul_kernel(\n    const float* __restrict__ A, \n    const float* __restrict__ B, \n    float* C,                    \n    size_t N, size_t M, size_t K, size_t L)\n{\n    int n = blockIdx.z;\n    int m = blockIdx.y * blockDim.y + threadIdx.y;\n    int l = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (n < N && m < M && l < L) {\n        float sum = 0.0f;\n        for (int k = 0; k < K; ++k) {\n            sum += A[n * M * K + m * K + k] * B[k * L + l];\n        }\n        C[n * M * L + m * L + l] = sum;\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t N, size_t M, size_t K, size_t L)\n{\n    dim3 blockSize(16, 16);\n    dim3 gridSize((L + 15) / 16, (M + 15) / 16, N);\n\n    batched_tensor_matmul_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, N, M, K, L);\n\n    hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/100-days-cuda/Day 75/3d_tensor.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void matmul_symmetric_kernel(const float* A, const float* B, float* C, size_t N) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < N && col < N) {\n        float value = 0.0f;\n        for (int k = 0; k < N; ++k) {\n            value += A[row * N + k] * B[k * N + col];\n        }\n        C[row * N + col] = value;\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t N) {\n    dim3 blockSize(16, 16); \n    dim3 gridSize((N + blockSize.x - 1) / blockSize.x,\n                  (N + blockSize.y - 1) / blockSize.y);\n\n    matmul_symmetric_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, N);\n\n    cudaDeviceSynchronize();\n}\n", "output": "#include <hip/hip_runtime.h>\n\n__global__ void matmul_symmetric_kernel(const float* A, const float* B, float* C, size_t N) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < N && col < N) {\n        float value = 0.0f;\n        for (int k = 0; k < N; ++k) {\n            value += A[row * N + k] * B[k * N + col];\n        }\n        C[row * N + col] = value;\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c, size_t N) {\n    dim3 blockSize(16, 16); \n    dim3 gridSize((N + blockSize.x - 1) / blockSize.x,\n                  (N + blockSize.y - 1) / blockSize.y);\n\n    matmul_symmetric_kernel<<<gridSize, blockSize>>>(input_a, input_b, output_c, N);\n\n    hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/100-days-cuda/Day 75/sym_mm.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void relu_kernel(float* input, float* output, size_t n, size_t m) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int total = n * m;\n\n    if (idx < total) {\n        output[idx] = fmaxf(0.0f, input[idx]);\n    }\n}\n\nextern \"C\" void solution(float* input, float* output, size_t n, size_t m) {\n    int total = n * m;\n    int threadsPerBlock = 688;\n    int blocksPerGrid = (total + threadsPerBlock - 1) / threadsPerBlock;\n\n    relu_kernel<<<blocksPerGrid, threadsPerBlock>>>(input, output, n, m);\n    cudaDeviceSynchronize();\n}", "output": "#include <hip/hip_runtime.h>\n\n__global__ void relu_kernel(float* input, float* output, size_t n, size_t m) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int total = n * m;\n\n    if (idx < total) {\n        output[idx] = fmaxf(0.0f, input[idx]);\n    }\n}\n\nextern \"C\" void solution(float* input, float* output, size_t n, size_t m) {\n    int total = n * m;\n    int threadsPerBlock = 688;\n    int blocksPerGrid = (total + threadsPerBlock - 1) / threadsPerBlock;\n\n    relu_kernel<<<blocksPerGrid, threadsPerBlock>>>(input, output, n, m);\n    hipDeviceSynchronize();\n}", "source": "/kaggle/working/100-days-cuda/Day 35/Simple_relu.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Reduced block sizes to avoid register pressure\nconst int BM = 64;   // BLOCK_M\nconst int BN = 64;   // BLOCK_N\nconst int BK = 8;    // BLOCK_K\n\ntemplate<int BLOCK_M, int BLOCK_N, int BLOCK_K>\n__global__ void optimized_gemm_kernel(\n    const float* __restrict__ A,\n    const float* __restrict__ B,\n    float* __restrict__ C,\n    const int M, const int N, const int K\n) {\n    // Shared memory for double buffering\n    __shared__ float smem_A[2][64][8];  // Using actual numbers for shared memory\n    __shared__ float smem_B[2][8][64];\n\n    // Thread block coordinates\n    const int block_x = blockIdx.x * BLOCK_N;\n    const int block_y = blockIdx.y * BLOCK_M;\n\n    // Thread coordinates within block\n    const int thread_x = threadIdx.x;\n    const int thread_y = threadIdx.y;\n\n    // Global coordinates\n    const int row = block_y + thread_y;\n    const int col = block_x + thread_x;\n\n    // Initialize accumulator\n    float acc = 0.0f;\n\n    // Loop over K dimension\n    for (int k = 0; k < K; k += BLOCK_K) {\n        // Load data into shared memory\n        if (row < M && (k + thread_x) < K && thread_x < BLOCK_K) {\n            smem_A[0][thread_y][thread_x] = A[row * K + k + thread_x];\n        }\n        else {\n            smem_A[0][thread_y][thread_x] = 0.0f;\n        }\n\n        if (col < N && (k + thread_y) < K && thread_y < BLOCK_K) {\n            smem_B[0][thread_y][thread_x] = B[(k + thread_y) * N + col];\n        }\n        else {\n            smem_B[0][thread_y][thread_x] = 0.0f;\n        }\n\n        __syncthreads();\n\n        // Compute partial sum\n#pragma unroll\n        for (int i = 0; i < BLOCK_K; ++i) {\n            acc += smem_A[0][thread_y][i] * smem_B[0][i][thread_x];\n        }\n\n        __syncthreads();\n    }\n\n    // Write result to global memory\n    if (row < M && col < N) {\n        C[row * N + col] = acc;\n    }\n}\n\n// CPU version for result verification\nvoid cpu_gemm(const float* A, const float* B, float* C, int M, int N, int K) {\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; k++) {\n                sum += A[i * K + k] * B[k * N + j];\n            }\n            C[i * N + j] = sum;\n        }\n    }\n}\n\n// Utility function to check for CUDA errors\n#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)\ntemplate<typename T>\nvoid check(T err, const char* const func, const char* const file, const int line) {\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"CUDA error at %s:%d code=%d(%s) \\\"%s\\\" \\n\",\n            file, line, static_cast<unsigned int>(err),\n            cudaGetErrorString(err), func);\n        exit(EXIT_FAILURE);\n    }\n}\n\nint main() {\n    // Matrix dimensions - reduced for testing\n    int M = 512;  // A rows\n    int N = 512;  // B columns\n    int K = 512;  // A columns and B rows\n\n    // Allocate host memory\n    float* h_A = (float*)malloc(M * K * sizeof(float));\n    float* h_B = (float*)malloc(K * N * sizeof(float));\n    float* h_C = (float*)malloc(M * N * sizeof(float));\n    float* h_C_ref = (float*)malloc(M * N * sizeof(float));\n\n    // Initialize matrices with small values\n    for (int i = 0; i < M * K; i++) {\n        h_A[i] = (float)(rand() % 10) / 10.0f;\n    }\n    for (int i = 0; i < K * N; i++) {\n        h_B[i] = (float)(rand() % 10) / 10.0f;\n    }\n\n    // Allocate device memory\n    float* d_A, * d_B, * d_C;\n    CHECK_CUDA_ERROR(cudaMalloc(&d_A, M * K * sizeof(float)));\n    CHECK_CUDA_ERROR(cudaMalloc(&d_B, K * N * sizeof(float)));\n    CHECK_CUDA_ERROR(cudaMalloc(&d_C, M * N * sizeof(float)));\n\n    // Copy inputs to device\n    CHECK_CUDA_ERROR(cudaMemcpy(d_A, h_A, M * K * sizeof(float), cudaMemcpyHostToDevice));\n    CHECK_CUDA_ERROR(cudaMemcpy(d_B, h_B, K * N * sizeof(float), cudaMemcpyHostToDevice));\n\n    // Create CUDA events for timing\n    cudaEvent_t start, stop;\n    CHECK_CUDA_ERROR(cudaEventCreate(&start));\n    CHECK_CUDA_ERROR(cudaEventCreate(&stop));\n\n    // Launch configuration\n    dim3 threadsPerBlock(16, 16);  // 256 threads per block\n    dim3 numBlocks(\n        (N + BN - 1) / BN,\n        (M + BM - 1) / BM\n    );\n\n    // Clear any previous errors\n    cudaGetLastError();\n\n    // Warm up run\n    optimized_gemm_kernel<BM, BN, BK>\n        << <numBlocks, threadsPerBlock >> > (d_A, d_B, d_C, M, N, K);\n    CHECK_CUDA_ERROR(cudaGetLastError());\n    CHECK_CUDA_ERROR(cudaDeviceSynchronize());\n\n    // Timing run\n    CHECK_CUDA_ERROR(cudaEventRecord(start));\n\n    optimized_gemm_kernel<BM, BN, BK>\n        << <numBlocks, threadsPerBlock >> > (d_A, d_B, d_C, M, N, K);\n\n    CHECK_CUDA_ERROR(cudaEventRecord(stop));\n    CHECK_CUDA_ERROR(cudaEventSynchronize(stop));\n\n    // Calculate elapsed time\n    float milliseconds = 0;\n    CHECK_CUDA_ERROR(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    // Copy result back to host\n    CHECK_CUDA_ERROR(cudaMemcpy(h_C, d_C, M * N * sizeof(float), cudaMemcpyDeviceToHost));\n\n    // Compute reference result on CPU\n    cpu_gemm(h_A, h_B, h_C_ref, M, N, K);\n\n    // Verify result\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; i++) {\n        float error = fabs(h_C[i] - h_C_ref[i]);\n        max_error = max(max_error, error);\n    }\n\n    // Calculate performance\n    float gflops = (2.0f * M * N * K) / (milliseconds * 1e6);\n\n    printf(\"Matrix multiplication results:\\n\");\n    printf(\"Matrix dimensions: M=%d, N=%d, K=%d\\n\", M, N, K);\n    printf(\"Performance: %.2f GFLOPS\\n\", gflops);\n    printf(\"Max error: %e\\n\", max_error);\n    printf(\"Time: %.2f ms\\n\", milliseconds);\n\n    // Cleanup\n    CHECK_CUDA_ERROR(cudaEventDestroy(start));\n    CHECK_CUDA_ERROR(cudaEventDestroy(stop));\n    CHECK_CUDA_ERROR(cudaFree(d_A));\n    CHECK_CUDA_ERROR(cudaFree(d_B));\n    CHECK_CUDA_ERROR(cudaFree(d_C));\n    free(h_A);\n    free(h_B);\n    free(h_C);\n    free(h_C_ref);\n\n    return 0;\n}", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n// Reduced block sizes to avoid register pressure\nconst int BM = 64;   // BLOCK_M\nconst int BN = 64;   // BLOCK_N\nconst int BK = 8;    // BLOCK_K\n\ntemplate<int BLOCK_M, int BLOCK_N, int BLOCK_K>\n__global__ void optimized_gemm_kernel(\n    const float* __restrict__ A,\n    const float* __restrict__ B,\n    float* __restrict__ C,\n    const int M, const int N, const int K\n) {\n    // Shared memory for double buffering\n    __shared__ float smem_A[2][64][8];  // Using actual numbers for shared memory\n    __shared__ float smem_B[2][8][64];\n\n    // Thread block coordinates\n    const int block_x = blockIdx.x * BLOCK_N;\n    const int block_y = blockIdx.y * BLOCK_M;\n\n    // Thread coordinates within block\n    const int thread_x = threadIdx.x;\n    const int thread_y = threadIdx.y;\n\n    // Global coordinates\n    const int row = block_y + thread_y;\n    const int col = block_x + thread_x;\n\n    // Initialize accumulator\n    float acc = 0.0f;\n\n    // Loop over K dimension\n    for (int k = 0; k < K; k += BLOCK_K) {\n        // Load data into shared memory\n        if (row < M && (k + thread_x) < K && thread_x < BLOCK_K) {\n            smem_A[0][thread_y][thread_x] = A[row * K + k + thread_x];\n        }\n        else {\n            smem_A[0][thread_y][thread_x] = 0.0f;\n        }\n\n        if (col < N && (k + thread_y) < K && thread_y < BLOCK_K) {\n            smem_B[0][thread_y][thread_x] = B[(k + thread_y) * N + col];\n        }\n        else {\n            smem_B[0][thread_y][thread_x] = 0.0f;\n        }\n\n        __syncthreads();\n\n        // Compute partial sum\n#pragma unroll\n        for (int i = 0; i < BLOCK_K; ++i) {\n            acc += smem_A[0][thread_y][i] * smem_B[0][i][thread_x];\n        }\n\n        __syncthreads();\n    }\n\n    // Write result to global memory\n    if (row < M && col < N) {\n        C[row * N + col] = acc;\n    }\n}\n\n// CPU version for result verification\nvoid cpu_gemm(const float* A, const float* B, float* C, int M, int N, int K) {\n    for (int i = 0; i < M; i++) {\n        for (int j = 0; j < N; j++) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; k++) {\n                sum += A[i * K + k] * B[k * N + j];\n            }\n            C[i * N + j] = sum;\n        }\n    }\n}\n\n// Utility function to check for CUDA errors\n#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)\ntemplate<typename T>\nvoid check(T err, const char* const func, const char* const file, const int line) {\n    if (err != hipSuccess) {\n        fprintf(stderr, \"CUDA error at %s:%d code=%d(%s) \\\"%s\\\" \\n\",\n            file, line, static_cast<unsigned int>(err),\n            hipGetErrorString(err), func);\n        exit(EXIT_FAILURE);\n    }\n}\n\nint main() {\n    // Matrix dimensions - reduced for testing\n    int M = 512;  // A rows\n    int N = 512;  // B columns\n    int K = 512;  // A columns and B rows\n\n    // Allocate host memory\n    float* h_A = (float*)malloc(M * K * sizeof(float));\n    float* h_B = (float*)malloc(K * N * sizeof(float));\n    float* h_C = (float*)malloc(M * N * sizeof(float));\n    float* h_C_ref = (float*)malloc(M * N * sizeof(float));\n\n    // Initialize matrices with small values\n    for (int i = 0; i < M * K; i++) {\n        h_A[i] = (float)(rand() % 10) / 10.0f;\n    }\n    for (int i = 0; i < K * N; i++) {\n        h_B[i] = (float)(rand() % 10) / 10.0f;\n    }\n\n    // Allocate device memory\n    float* d_A, * d_B, * d_C;\n    CHECK_CUDA_ERROR(hipMalloc(&d_A, M * K * sizeof(float)));\n    CHECK_CUDA_ERROR(hipMalloc(&d_B, K * N * sizeof(float)));\n    CHECK_CUDA_ERROR(hipMalloc(&d_C, M * N * sizeof(float)));\n\n    // Copy inputs to device\n    CHECK_CUDA_ERROR(hipMemcpy(d_A, h_A, M * K * sizeof(float), hipMemcpyHostToDevice));\n    CHECK_CUDA_ERROR(hipMemcpy(d_B, h_B, K * N * sizeof(float), hipMemcpyHostToDevice));\n\n    // Create CUDA events for timing\n    hipEvent_t start, stop;\n    CHECK_CUDA_ERROR(hipEventCreate(&start));\n    CHECK_CUDA_ERROR(hipEventCreate(&stop));\n\n    // Launch configuration\n    dim3 threadsPerBlock(16, 16);  // 256 threads per block\n    dim3 numBlocks(\n        (N + BN - 1) / BN,\n        (M + BM - 1) / BM\n    );\n\n    // Clear any previous errors\n    hipGetLastError();\n\n    // Warm up run\n    optimized_gemm_kernel<BM, BN, BK>\n        << <numBlocks, threadsPerBlock >> > (d_A, d_B, d_C, M, N, K);\n    CHECK_CUDA_ERROR(hipGetLastError());\n    CHECK_CUDA_ERROR(hipDeviceSynchronize());\n\n    // Timing run\n    CHECK_CUDA_ERROR(hipEventRecord(start));\n\n    optimized_gemm_kernel<BM, BN, BK>\n        << <numBlocks, threadsPerBlock >> > (d_A, d_B, d_C, M, N, K);\n\n    CHECK_CUDA_ERROR(hipEventRecord(stop));\n    CHECK_CUDA_ERROR(hipEventSynchronize(stop));\n\n    // Calculate elapsed time\n    float milliseconds = 0;\n    CHECK_CUDA_ERROR(hipEventElapsedTime(&milliseconds, start, stop));\n\n    // Copy result back to host\n    CHECK_CUDA_ERROR(hipMemcpy(h_C, d_C, M * N * sizeof(float), hipMemcpyDeviceToHost));\n\n    // Compute reference result on CPU\n    cpu_gemm(h_A, h_B, h_C_ref, M, N, K);\n\n    // Verify result\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; i++) {\n        float error = fabs(h_C[i] - h_C_ref[i]);\n        max_error = max(max_error, error);\n    }\n\n    // Calculate performance\n    float gflops = (2.0f * M * N * K) / (milliseconds * 1e6);\n\n    printf(\"Matrix multiplication results:\\n\");\n    printf(\"Matrix dimensions: M=%d, N=%d, K=%d\\n\", M, N, K);\n    printf(\"Performance: %.2f GFLOPS\\n\", gflops);\n    printf(\"Max error: %e\\n\", max_error);\n    printf(\"Time: %.2f ms\\n\", milliseconds);\n\n    // Cleanup\n    CHECK_CUDA_ERROR(hipEventDestroy(start));\n    CHECK_CUDA_ERROR(hipEventDestroy(stop));\n    CHECK_CUDA_ERROR(hipFree(d_A));\n    CHECK_CUDA_ERROR(hipFree(d_B));\n    CHECK_CUDA_ERROR(hipFree(d_C));\n    free(h_A);\n    free(h_B);\n    free(h_C);\n    free(h_C_ref);\n\n    return 0;\n}", "source": "/kaggle/working/100-days-cuda/Day 07/BasicGEMM.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <stdio.h>\n#include <time.h>\n#include <math.h>\n\n#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)\n\ntemplate <typename T>\nvoid check(T err, const char* const func, const char* const file, const int line) {\n    if (err != cudaSuccess) {\n        fprintf(stderr, \"CUDA error at %s:%d code=%d(%s) \\\"%s\\\" \\n\",\n            file, line, static_cast<unsigned int>(err), cudaGetErrorString(err), func);\n        exit(EXIT_FAILURE);\n    }\n}\n\n// CUDA Kernel for complex math operations\n__global__ void vectorComplexCompute(const float* A, const float* B, float* C, int numElements) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < numElements) {\n        float temp = A[i] * B[i];\n        for (int j = 0; j < 100; ++j) { // Adding workload\n            temp = sqrt(temp) + exp(A[i]) - log(B[i] + 1);\n        }\n        C[i] = temp;\n    }\n}\n\n// CPU version for complex math operations\nvoid vectorComplexComputeCPU(const float* A, const float* B, float* C, int numElements) {\n    for (int i = 0; i < numElements; ++i) {\n        float temp = A[i] * B[i];\n        for (int j = 0; j < 100; ++j) { // Simulating heavy computation\n            temp = sqrt(temp) + exp(A[i]) - log(B[i] + 1);\n        }\n        C[i] = temp;\n    }\n}\n\nint main(void) {\n    int numElements = 100000000;  // 100 million elements\n    size_t size = numElements * sizeof(float);\n    int halfElements = numElements / 2;\n\n    // Host memory allocation\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C = (float*)malloc(size);\n    float* h_C_CPU = (float*)malloc(size);\n\n    // Initialize host arrays\n    for (int i = 0; i < numElements; ++i) {\n        h_A[i] = rand() / (float)RAND_MAX;\n        h_B[i] = rand() / (float)RAND_MAX;\n    }\n\n    // Benchmark CPU Execution\n    clock_t start_cpu = clock();\n    vectorComplexComputeCPU(h_A, h_B, h_C_CPU, numElements);\n    clock_t end_cpu = clock();\n    double cpu_time = ((double)(end_cpu - start_cpu)) / CLOCKS_PER_SEC * 1000.0; // Convert to ms\n\n    // Device memory allocation\n    float* d_A, * d_B, * d_C;\n    CHECK_CUDA_ERROR(cudaMalloc((void**)&d_A, size));\n    CHECK_CUDA_ERROR(cudaMalloc((void**)&d_B, size));\n    CHECK_CUDA_ERROR(cudaMalloc((void**)&d_C, size));\n\n    // Create CUDA streams\n    cudaStream_t stream1, stream2;\n    CHECK_CUDA_ERROR(cudaStreamCreate(&stream1));\n    CHECK_CUDA_ERROR(cudaStreamCreate(&stream2));\n\n    // Start GPU timing\n    cudaEvent_t start, stop;\n    CHECK_CUDA_ERROR(cudaEventCreate(&start));\n    CHECK_CUDA_ERROR(cudaEventCreate(&stop));\n    CHECK_CUDA_ERROR(cudaEventRecord(start, 0));\n\n    // Asynchronously copy first half of data using stream1\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(d_A, h_A, halfElements * sizeof(float), cudaMemcpyHostToDevice, stream1));\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(d_B, h_B, halfElements * sizeof(float), cudaMemcpyHostToDevice, stream1));\n\n    // Asynchronously copy second half of data using stream2\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(d_A + halfElements, h_A + halfElements, halfElements * sizeof(float), cudaMemcpyHostToDevice, stream2));\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(d_B + halfElements, h_B + halfElements, halfElements * sizeof(float), cudaMemcpyHostToDevice, stream2));\n\n    // Kernel configuration\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (halfElements + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Launch kernels on separate streams\n    vectorComplexCompute << <blocksPerGrid, threadsPerBlock, 0, stream1 >> > (d_A, d_B, d_C, halfElements);\n    vectorComplexCompute << <blocksPerGrid, threadsPerBlock, 0, stream2 >> > (d_A + halfElements, d_B + halfElements, d_C + halfElements, halfElements);\n\n    // Copy results back asynchronously\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(h_C, d_C, halfElements * sizeof(float), cudaMemcpyDeviceToHost, stream1));\n    CHECK_CUDA_ERROR(cudaMemcpyAsync(h_C + halfElements, d_C + halfElements, halfElements * sizeof(float), cudaMemcpyDeviceToHost, stream2));\n\n    // Synchronize both streams\n    CHECK_CUDA_ERROR(cudaStreamSynchronize(stream1));\n    CHECK_CUDA_ERROR(cudaStreamSynchronize(stream2));\n\n    // Stop GPU timing\n    CHECK_CUDA_ERROR(cudaEventRecord(stop, 0));\n    CHECK_CUDA_ERROR(cudaEventSynchronize(stop));\n\n    float gpu_time;\n    CHECK_CUDA_ERROR(cudaEventElapsedTime(&gpu_time, start, stop)); // Time in milliseconds\n\n    // Print first 10 results\n    printf(\"\\nVector Computation Results (First 10 Elements):\\n\");\n    for (int i = 0; i < 10; ++i) {\n        printf(\"C[%d] = %f\\n\", i, h_C[i]);\n    }\n\n    // Verify results\n    for (int i = 0; i < numElements; ++i) {\n        if (fabs(h_C_CPU[i] - h_C[i]) > 1e-5) {\n            fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    printf(\"\\nTest PASSED\\n\");\n\n    // Print Benchmark Results\n    printf(\"\\n=== Performance Comparison ===\\n\");\n    printf(\"CPU Time: %.3f ms\\n\", cpu_time);\n    printf(\"GPU Time (CUDA Streams): %.3f ms\\n\", gpu_time);\n    printf(\"Speedup (CPU / GPU): %.2fx\\n\", cpu_time / gpu_time);\n\n    // Cleanup\n    CHECK_CUDA_ERROR(cudaFree(d_A));\n    CHECK_CUDA_ERROR(cudaFree(d_B));\n    CHECK_CUDA_ERROR(cudaFree(d_C));\n    CHECK_CUDA_ERROR(cudaStreamDestroy(stream1));\n    CHECK_CUDA_ERROR(cudaStreamDestroy(stream2));\n    CHECK_CUDA_ERROR(cudaEventDestroy(start));\n    CHECK_CUDA_ERROR(cudaEventDestroy(stop));\n    free(h_A);\n    free(h_B);\n    free(h_C);\n    free(h_C_CPU);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <time.h>\n#include <math.h>\n\n#define CHECK_CUDA_ERROR(val) check((val), #val, __FILE__, __LINE__)\n\ntemplate <typename T>\nvoid check(T err, const char* const func, const char* const file, const int line) {\n    if (err != hipSuccess) {\n        fprintf(stderr, \"CUDA error at %s:%d code=%d(%s) \\\"%s\\\" \\n\",\n            file, line, static_cast<unsigned int>(err), hipGetErrorString(err), func);\n        exit(EXIT_FAILURE);\n    }\n}\n\n// CUDA Kernel for complex math operations\n__global__ void vectorComplexCompute(const float* A, const float* B, float* C, int numElements) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < numElements) {\n        float temp = A[i] * B[i];\n        for (int j = 0; j < 100; ++j) { // Adding workload\n            temp = sqrt(temp) + exp(A[i]) - log(B[i] + 1);\n        }\n        C[i] = temp;\n    }\n}\n\n// CPU version for complex math operations\nvoid vectorComplexComputeCPU(const float* A, const float* B, float* C, int numElements) {\n    for (int i = 0; i < numElements; ++i) {\n        float temp = A[i] * B[i];\n        for (int j = 0; j < 100; ++j) { // Simulating heavy computation\n            temp = sqrt(temp) + exp(A[i]) - log(B[i] + 1);\n        }\n        C[i] = temp;\n    }\n}\n\nint main(void) {\n    int numElements = 100000000;  // 100 million elements\n    size_t size = numElements * sizeof(float);\n    int halfElements = numElements / 2;\n\n    // Host memory allocation\n    float* h_A = (float*)malloc(size);\n    float* h_B = (float*)malloc(size);\n    float* h_C = (float*)malloc(size);\n    float* h_C_CPU = (float*)malloc(size);\n\n    // Initialize host arrays\n    for (int i = 0; i < numElements; ++i) {\n        h_A[i] = rand() / (float)RAND_MAX;\n        h_B[i] = rand() / (float)RAND_MAX;\n    }\n\n    // Benchmark CPU Execution\n    clock_t start_cpu = clock();\n    vectorComplexComputeCPU(h_A, h_B, h_C_CPU, numElements);\n    clock_t end_cpu = clock();\n    double cpu_time = ((double)(end_cpu - start_cpu)) / CLOCKS_PER_SEC * 1000.0; // Convert to ms\n\n    // Device memory allocation\n    float* d_A, * d_B, * d_C;\n    CHECK_CUDA_ERROR(hipMalloc((void**)&d_A, size));\n    CHECK_CUDA_ERROR(hipMalloc((void**)&d_B, size));\n    CHECK_CUDA_ERROR(hipMalloc((void**)&d_C, size));\n\n    // Create CUDA streams\n    hipStream_t stream1, stream2;\n    CHECK_CUDA_ERROR(hipStreamCreate(&stream1));\n    CHECK_CUDA_ERROR(hipStreamCreate(&stream2));\n\n    // Start GPU timing\n    hipEvent_t start, stop;\n    CHECK_CUDA_ERROR(hipEventCreate(&start));\n    CHECK_CUDA_ERROR(hipEventCreate(&stop));\n    CHECK_CUDA_ERROR(hipEventRecord(start, 0));\n\n    // Asynchronously copy first half of data using stream1\n    CHECK_CUDA_ERROR(hipMemcpyAsync(d_A, h_A, halfElements * sizeof(float), hipMemcpyHostToDevice, stream1));\n    CHECK_CUDA_ERROR(hipMemcpyAsync(d_B, h_B, halfElements * sizeof(float), hipMemcpyHostToDevice, stream1));\n\n    // Asynchronously copy second half of data using stream2\n    CHECK_CUDA_ERROR(hipMemcpyAsync(d_A + halfElements, h_A + halfElements, halfElements * sizeof(float), hipMemcpyHostToDevice, stream2));\n    CHECK_CUDA_ERROR(hipMemcpyAsync(d_B + halfElements, h_B + halfElements, halfElements * sizeof(float), hipMemcpyHostToDevice, stream2));\n\n    // Kernel configuration\n    int threadsPerBlock = 256;\n    int blocksPerGrid = (halfElements + threadsPerBlock - 1) / threadsPerBlock;\n\n    // Launch kernels on separate streams\n    vectorComplexCompute << <blocksPerGrid, threadsPerBlock, 0, stream1 >> > (d_A, d_B, d_C, halfElements);\n    vectorComplexCompute << <blocksPerGrid, threadsPerBlock, 0, stream2 >> > (d_A + halfElements, d_B + halfElements, d_C + halfElements, halfElements);\n\n    // Copy results back asynchronously\n    CHECK_CUDA_ERROR(hipMemcpyAsync(h_C, d_C, halfElements * sizeof(float), hipMemcpyDeviceToHost, stream1));\n    CHECK_CUDA_ERROR(hipMemcpyAsync(h_C + halfElements, d_C + halfElements, halfElements * sizeof(float), hipMemcpyDeviceToHost, stream2));\n\n    // Synchronize both streams\n    CHECK_CUDA_ERROR(hipStreamSynchronize(stream1));\n    CHECK_CUDA_ERROR(hipStreamSynchronize(stream2));\n\n    // Stop GPU timing\n    CHECK_CUDA_ERROR(hipEventRecord(stop, 0));\n    CHECK_CUDA_ERROR(hipEventSynchronize(stop));\n\n    float gpu_time;\n    CHECK_CUDA_ERROR(hipEventElapsedTime(&gpu_time, start, stop)); // Time in milliseconds\n\n    // Print first 10 results\n    printf(\"\\nVector Computation Results (First 10 Elements):\\n\");\n    for (int i = 0; i < 10; ++i) {\n        printf(\"C[%d] = %f\\n\", i, h_C[i]);\n    }\n\n    // Verify results\n    for (int i = 0; i < numElements; ++i) {\n        if (fabs(h_C_CPU[i] - h_C[i]) > 1e-5) {\n            fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    printf(\"\\nTest PASSED\\n\");\n\n    // Print Benchmark Results\n    printf(\"\\n=== Performance Comparison ===\\n\");\n    printf(\"CPU Time: %.3f ms\\n\", cpu_time);\n    printf(\"GPU Time (CUDA Streams): %.3f ms\\n\", gpu_time);\n    printf(\"Speedup (CPU / GPU): %.2fx\\n\", cpu_time / gpu_time);\n\n    // Cleanup\n    CHECK_CUDA_ERROR(hipFree(d_A));\n    CHECK_CUDA_ERROR(hipFree(d_B));\n    CHECK_CUDA_ERROR(hipFree(d_C));\n    CHECK_CUDA_ERROR(hipStreamDestroy(stream1));\n    CHECK_CUDA_ERROR(hipStreamDestroy(stream2));\n    CHECK_CUDA_ERROR(hipEventDestroy(start));\n    CHECK_CUDA_ERROR(hipEventDestroy(stop));\n    free(h_A);\n    free(h_B);\n    free(h_C);\n    free(h_C_CPU);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 03/gpu_vs_cpu_complex_math_benchmark.cu"}
{"input": "\ufeff#include <stdio.h>\n#include <cuda_runtime.h>\n\n__global__ void atomicAddKernel(int* sum) {\n\tint tid = threadIdx.x + blockIdx.x * blockDim.x;\n\tatomicAdd(sum, tid); //safe addition to memory\n }\n\nint main() {\n\tint h_sum = 0;  //host value\n\tint *d_sum;    // device\n\n\t//Alocate mem on gpu\n\n\tcudaMalloc((void**)&d_sum, sizeof(int));\n\tcudaMemcpy(d_sum, &h_sum, sizeof(int), cudaMemcpyHostToDevice);\n\n\tatomicAddKernel<<<1, 256>>>(d_sum); //launch kernel with 256 thread and 1 block\n\n\tcudaMemcpy(&h_sum, d_sum, sizeof(int), cudaMemcpyDeviceToHost);\n\n\tprintf(\"The value after atomicAdd is %d\\n\", h_sum);\n\n\tcudaFree(d_sum); //free memory\n\n\treturn 0;\n}\n", "output": "\ufeff#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n__global__ void atomicAddKernel(int* sum) {\n\tint tid = threadIdx.x + blockIdx.x * blockDim.x;\n\tatomicAdd(sum, tid); //safe addition to memory\n }\n\nint main() {\n\tint h_sum = 0;  //host value\n\tint *d_sum;    // device\n\n\t//Alocate mem on gpu\n\n\thipMalloc((void**)&d_sum, sizeof(int));\n\thipMemcpy(d_sum, &h_sum, sizeof(int), hipMemcpyHostToDevice);\n\n\tatomicAddKernel<<<1, 256>>>(d_sum); //launch kernel with 256 thread and 1 block\n\n\thipMemcpy(&h_sum, d_sum, sizeof(int), hipMemcpyDeviceToHost);\n\n\tprintf(\"The value after atomicAdd is %d\\n\", h_sum);\n\n\thipFree(d_sum); //free memory\n\n\treturn 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 03/AtomicAddition.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    cudaMalloc(&device_a, M * K * sizeof(half));\n    cudaMalloc(&device_b, K * N * sizeof(half));\n    cudaMalloc(&device_d, M * N * sizeof(float));\n\n    cudaMemcpy(device_a, host_a.data(), M * K * sizeof(half), cudaMemcpyHostToDevice);\n    cudaMemcpy(device_b, host_b.data(), K * N * sizeof(half), cudaMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32);  // One warp per tile\n    wmma_kernel << <grid, block >> > (device_a, device_b, device_d, M, N, K);\n    cudaDeviceSynchronize();\n    cudaError_t err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        std::cerr << \"CUDA Error: \" << cudaGetErrorString(err) << std::endl;\n    }\n\n    cudaMemcpy(host_d.data(), device_d, M * N * sizeof(float), cudaMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    cudaFree(device_a);\n    cudaFree(device_b);\n    cudaFree(device_d);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    hipMalloc(&device_a, M * K * sizeof(half));\n    hipMalloc(&device_b, K * N * sizeof(half));\n    hipMalloc(&device_d, M * N * sizeof(float));\n\n    hipMemcpy(device_a, host_a.data(), M * K * sizeof(half), hipMemcpyHostToDevice);\n    hipMemcpy(device_b, host_b.data(), K * N * sizeof(half), hipMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32);  // One warp per tile\n    wmma_kernel << <grid, block >> > (device_a, device_b, device_d, M, N, K);\n    hipDeviceSynchronize();\n    hipError_t err = hipGetLastError();\n    if (err != hipSuccess) {\n        std::cerr << \"CUDA Error: \" << hipGetErrorString(err) << std::endl;\n    }\n\n    hipMemcpy(host_d.data(), device_d, M * N * sizeof(float), hipMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    hipFree(device_a);\n    hipFree(device_b);\n    hipFree(device_d);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 03/matmul/kernel.cu"}
{"input": "#include <iostream>\n#include <vector>\n#include <random>\n#include <cuda_runtime.h>\n\n#define STATE_SIZE 4 \n#define ACTION_SIZE 2  \n#define MEMORY_SIZE 10000\n#define BATCH_SIZE 32\n#define GAMMA 0.99\n#define LEARNING_RATE 0.001\n\n__global__ void forwardPassKernel(float* input, float* weights, float* output, int inputSize, int outputSize) {\n    int idx = threadIdx.x;\n    if (idx < outputSize) {\n        float sum = 0.0f;\n        for (int i = 0; i < inputSize; i++) {\n            sum += input[i] * weights[i * outputSize + idx];\n        }\n        output[idx] = sum;\n    }\n}\n\nclass DQN {\npublic:\n    float *d_weights;\n    float *d_input, *d_output;\n\n    DQN() {\n        cudaMalloc(&d_weights, STATE_SIZE * ACTION_SIZE * sizeof(float));\n        cudaMalloc(&d_input, STATE_SIZE * sizeof(float));\n        cudaMalloc(&d_output, ACTION_SIZE * sizeof(float));\n\n        // Initialize weights on host and copy to device\n        std::vector<float> h_weights(STATE_SIZE * ACTION_SIZE);\n        for (auto &w : h_weights) {\n            w = static_cast<float>(rand()) / RAND_MAX; // Random values between 0 and 1\n        }\n        cudaMemcpy(d_weights, h_weights.data(), STATE_SIZE * ACTION_SIZE * sizeof(float), cudaMemcpyHostToDevice);\n    }\n\n    ~DQN() {\n        cudaFree(d_weights);\n        cudaFree(d_input);\n        cudaFree(d_output);\n    }\n\n    void forward(float* state, float* q_values) {\n        cudaMemcpy(d_input, state, STATE_SIZE * sizeof(float), cudaMemcpyHostToDevice);\n        forwardPassKernel<<<1, ACTION_SIZE>>>(d_input, d_weights, d_output, STATE_SIZE, ACTION_SIZE);\n        cudaMemcpy(q_values, d_output, ACTION_SIZE * sizeof(float), cudaMemcpyDeviceToHost);\n    }\n};\n\nint main() {\n    DQN agent;\n    float state[STATE_SIZE] = {1.0, 0.5, -0.5, 0.2};\n    float q_values[ACTION_SIZE];\n    \n    agent.forward(state, q_values);\n    std::cout << \"Q-values: \";\n    for (int i = 0; i < ACTION_SIZE; i++) {\n        std::cout << q_values[i] << \" \";\n    }\n    std::cout << std::endl;\n    \n    return 0;\n}\n", "output": "#include <iostream>\n#include <vector>\n#include <random>\n#include <hip/hip_runtime.h>\n\n#define STATE_SIZE 4 \n#define ACTION_SIZE 2  \n#define MEMORY_SIZE 10000\n#define BATCH_SIZE 32\n#define GAMMA 0.99\n#define LEARNING_RATE 0.001\n\n__global__ void forwardPassKernel(float* input, float* weights, float* output, int inputSize, int outputSize) {\n    int idx = threadIdx.x;\n    if (idx < outputSize) {\n        float sum = 0.0f;\n        for (int i = 0; i < inputSize; i++) {\n            sum += input[i] * weights[i * outputSize + idx];\n        }\n        output[idx] = sum;\n    }\n}\n\nclass DQN {\npublic:\n    float *d_weights;\n    float *d_input, *d_output;\n\n    DQN() {\n        hipMalloc(&d_weights, STATE_SIZE * ACTION_SIZE * sizeof(float));\n        hipMalloc(&d_input, STATE_SIZE * sizeof(float));\n        hipMalloc(&d_output, ACTION_SIZE * sizeof(float));\n\n        // Initialize weights on host and copy to device\n        std::vector<float> h_weights(STATE_SIZE * ACTION_SIZE);\n        for (auto &w : h_weights) {\n            w = static_cast<float>(rand()) / RAND_MAX; // Random values between 0 and 1\n        }\n        hipMemcpy(d_weights, h_weights.data(), STATE_SIZE * ACTION_SIZE * sizeof(float), hipMemcpyHostToDevice);\n    }\n\n    ~DQN() {\n        hipFree(d_weights);\n        hipFree(d_input);\n        hipFree(d_output);\n    }\n\n    void forward(float* state, float* q_values) {\n        hipMemcpy(d_input, state, STATE_SIZE * sizeof(float), hipMemcpyHostToDevice);\n        forwardPassKernel<<<1, ACTION_SIZE>>>(d_input, d_weights, d_output, STATE_SIZE, ACTION_SIZE);\n        hipMemcpy(q_values, d_output, ACTION_SIZE * sizeof(float), hipMemcpyDeviceToHost);\n    }\n};\n\nint main() {\n    DQN agent;\n    float state[STATE_SIZE] = {1.0, 0.5, -0.5, 0.2};\n    float q_values[ACTION_SIZE];\n    \n    agent.forward(state, q_values);\n    std::cout << \"Q-values: \";\n    for (int i = 0; i < ACTION_SIZE; i++) {\n        std::cout << q_values[i] << \" \";\n    }\n    std::cout << std::endl;\n    \n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 64/DQN_CUDA.cu"}
{"input": "#include <iostream>\n#include <vector>\n#include <random>\n#include <algorithm>\n#include <memory>\n#include <deque>\n#include <cuda_runtime.h>\n#include <curand.h>\n#include <cublas_v2.h>\n#include <cuda_fp16.h>\n#include <cmath>  // for sqrt\n#include <cstdlib>\n#include <ctime>\n\n// DQN Configuration\n#define FRAME_HEIGHT 84\n#define FRAME_WIDTH 84\n#define FRAME_HISTORY 4\n#define STATE_SIZE (FRAME_HEIGHT * FRAME_WIDTH * FRAME_HISTORY)\n#define MAX_ACTIONS 18  // Maximum number of actions in Atari games\n#define REPLAY_MEMORY_SIZE 1000000\n#define BATCH_SIZE 32\n#define GAMMA 0.99f\n#define INITIAL_EXPLORATION 1.0f\n#define FINAL_EXPLORATION 0.1f\n#define FINAL_EXPLORATION_FRAME 1000000\n#define TARGET_NETWORK_UPDATE_FREQUENCY 10000\n#define LEARNING_RATE 0.00025f\n#define GRADIENT_MOMENTUM 0.95f\n#define SQUARED_GRADIENT_MOMENTUM 0.95f\n#define MIN_SQUARED_GRADIENT 0.01f\n\n// CUDA Error Checking\n#define CHECK_CUDA_ERROR(val) check_cuda((val), #val, __FILE__, __LINE__)\ninline void check_cuda(cudaError_t err, const char* const func, const char* const file, const int line) {\n    if (err != cudaSuccess) {\n        std::cerr << \"CUDA Runtime Error at: \" << file << \":\" << line << std::endl;\n        std::cerr << cudaGetErrorString(err) << \" \" << func << std::endl;\n        std::exit(EXIT_FAILURE);\n    }\n}\n\n// Experience Replay Memory Structure\nstruct Experience {\n    float state[STATE_SIZE];\n    int action;\n    float reward;\n    float next_state[STATE_SIZE];\n    bool terminal;\n};\n\nclass ReplayMemory {\nprivate:\n    std::deque<Experience> memory;\n    size_t max_size;\n    std::mt19937 rng;\n\npublic:\n    ReplayMemory(size_t size) : max_size(size), rng(std::random_device{}()) {}\n\n    void add(const Experience& exp) {\n        if (memory.size() >= max_size) {\n            memory.pop_front();\n        }\n        memory.push_back(exp);\n    }\n\n    void sample(std::vector<Experience>& batch, size_t batch_size) {\n        batch.clear();\n        if (memory.size() < batch_size) return;\n\n        std::uniform_int_distribution<size_t> dist(0, memory.size() - 1);\n        for (size_t i = 0; i < batch_size; ++i) {\n            batch.push_back(memory[dist(rng)]);\n        }\n    }\n\n    size_t size() const {\n        return memory.size();\n    }\n};\n\n// CUDA kernel implementations (free functions)\n\n__global__ void conv2d(float* input, float* weights, float* bias, float* output,\n                       int input_h, int input_w, int channels,\n                       int kernel_size, int stride, int num_filters) {\n    int filter_idx = threadIdx.x;\n    int out_x = threadIdx.y;\n    int out_y = threadIdx.z;\n\n    if (filter_idx < num_filters) {\n        float sum = bias[filter_idx];\n        int in_x_start = out_x * stride;\n        int in_y_start = out_y * stride;\n\n        for (int c = 0; c < channels; c++) {\n            for (int ky = 0; ky < kernel_size; ky++) {\n                for (int kx = 0; kx < kernel_size; kx++) {\n                    int in_x = in_x_start + kx;\n                    int in_y = in_y_start + ky;\n                    if (in_x < input_w && in_y < input_h) {\n                        int input_idx = c * input_h * input_w + in_y * input_w + in_x;\n                        int weight_idx = filter_idx * channels * kernel_size * kernel_size +\n                                         c * kernel_size * kernel_size +\n                                         ky * kernel_size + kx;\n                        sum += input[input_idx] * weights[weight_idx];\n                    }\n                }\n            }\n        }\n        int out_idx = filter_idx * gridDim.y * gridDim.z + out_y * gridDim.y + out_x;\n        output[out_idx] = sum;\n    }\n}\n\n__global__ void relu_activation(float* input, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        input[idx] = max(0.0f, input[idx]);\n    }\n}\n\n__global__ void fully_connected(float* input, float* weights, float* bias, float* output,\n                                int input_size, int output_size) {\n    int out_idx = threadIdx.x;\n    if (out_idx < output_size) {\n        float sum = bias[out_idx];\n        for (int in_idx = 0; in_idx < input_size; in_idx++) {\n            sum += input[in_idx] * weights[in_idx * output_size + out_idx];\n        }\n        output[out_idx] = sum;\n    }\n}\n\n// Convolutional Neural Network for DQN\nclass ConvolutionalNetwork {\nprivate:\n    // Device memory pointers for weights and biases\n    float *d_conv1_weights, *d_conv1_bias;\n    float *d_conv2_weights, *d_conv2_bias;\n    float *d_fc1_weights, *d_fc1_bias;\n    float *d_fc2_weights, *d_fc2_bias;\n    \n    // Device memory for intermediate results\n    float *d_input;\n    float *d_conv1_output, *d_conv2_output;\n    float *d_fc1_output, *d_output;\n    \n    // Network dimensions\n    int num_actions;\n    \n    // CUDA handles\n    cublasHandle_t cublas_handle;\n    curandGenerator_t curand_gen;\n    \n    // Initialize weights with Xavier/Glorot initialization\n    void init_weights(float* d_weights, int fan_in, int fan_out) {\n        float scale = sqrt(6.0f / (fan_in + fan_out));\n        curandGenerateUniform(curand_gen, d_weights, fan_in * fan_out);\n        float h_scale = 2.0f * scale;\n        float h_offset = -scale;\n        std::vector<float> h_weights(fan_in * fan_out);\n        CHECK_CUDA_ERROR(cudaMemcpy(h_weights.data(), d_weights, sizeof(float) * fan_in * fan_out, cudaMemcpyDeviceToHost));\n        for (int i = 0; i < fan_in * fan_out; i++) {\n            h_weights[i] = h_weights[i] * h_scale + h_offset;\n        }\n        CHECK_CUDA_ERROR(cudaMemcpy(d_weights, h_weights.data(), sizeof(float) * fan_in * fan_out, cudaMemcpyHostToDevice));\n    }\n    \n    // Initialize biases to small constant value\n    void init_biases(float* d_biases, int size, float value = 0.01f) {\n        std::vector<float> h_biases(size, value);\n        CHECK_CUDA_ERROR(cudaMemcpy(d_biases, h_biases.data(), sizeof(float) * size, cudaMemcpyHostToDevice));\n    }\n\npublic:\n    ConvolutionalNetwork(int actions) : num_actions(actions) {\n        // Create CUDA handles\n        cublasCreate(&cublas_handle);\n        curandCreateGenerator(&curand_gen, CURAND_RNG_PSEUDO_DEFAULT);\n        curandSetPseudoRandomGeneratorSeed(curand_gen, static_cast<unsigned long long>(time(NULL)));\n        \n        // Allocate memory for weights and biases\n        // Layer 1: Convolutional 8x8 with stride 4, 16 filters\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv1_weights, sizeof(float) * 8 * 8 * FRAME_HISTORY * 16));\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv1_bias, sizeof(float) * 16));\n        \n        // Layer 2: Convolutional 4x4 with stride 2, 32 filters\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv2_weights, sizeof(float) * 4 * 4 * 16 * 32));\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv2_bias, sizeof(float) * 32));\n        \n        // Layer 3: Fully connected with 256 rectifier units\n        int conv_output_size = 9 * 9 * 32; // After two convolutions\n        CHECK_CUDA_ERROR(cudaMalloc(&d_fc1_weights, sizeof(float) * conv_output_size * 256));\n        CHECK_CUDA_ERROR(cudaMalloc(&d_fc1_bias, sizeof(float) * 256));\n        \n        // Layer 4: Fully connected output layer\n        CHECK_CUDA_ERROR(cudaMalloc(&d_fc2_weights, sizeof(float) * 256 * num_actions));\n        CHECK_CUDA_ERROR(cudaMalloc(&d_fc2_bias, sizeof(float) * num_actions));\n        \n        // Allocate memory for intermediate results\n        CHECK_CUDA_ERROR(cudaMalloc(&d_input, sizeof(float) * STATE_SIZE));\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv1_output, sizeof(float) * 20 * 20 * 16)); // After 1st conv\n        CHECK_CUDA_ERROR(cudaMalloc(&d_conv2_output, sizeof(float) * 9 * 9 * 32));   // After 2nd conv\n        CHECK_CUDA_ERROR(cudaMalloc(&d_fc1_output, sizeof(float) * 256));            // After FC\n        CHECK_CUDA_ERROR(cudaMalloc(&d_output, sizeof(float) * num_actions));        // Final output\n        \n        // Initialize weights and biases\n        init_weights(d_conv1_weights, 8 * 8 * FRAME_HISTORY, 16);\n        init_biases(d_conv1_bias, 16);\n        \n        init_weights(d_conv2_weights, 4 * 4 * 16, 32);\n        init_biases(d_conv2_bias, 32);\n        \n        init_weights(d_fc1_weights, conv_output_size, 256);\n        init_biases(d_fc1_bias, 256);\n        \n        init_weights(d_fc2_weights, 256, num_actions);\n        init_biases(d_fc2_bias, num_actions);\n    }\n    \n    ~ConvolutionalNetwork() {\n        // Free allocated memory\n        cudaFree(d_conv1_weights);\n        cudaFree(d_conv1_bias);\n        cudaFree(d_conv2_weights);\n        cudaFree(d_conv2_bias);\n        cudaFree(d_fc1_weights);\n        cudaFree(d_fc1_bias);\n        cudaFree(d_fc2_weights);\n        cudaFree(d_fc2_bias);\n        \n        cudaFree(d_input);\n        cudaFree(d_conv1_output);\n        cudaFree(d_conv2_output);\n        cudaFree(d_fc1_output);\n        cudaFree(d_output);\n        \n        cublasDestroy(cublas_handle);\n        curandDestroyGenerator(curand_gen);\n    }\n    \n    // Clone network weights to target network\n    void clone_to(ConvolutionalNetwork& target) {\n        cudaMemcpy(target.d_conv1_weights, d_conv1_weights, sizeof(float) * 8 * 8 * FRAME_HISTORY * 16, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_conv1_bias, d_conv1_bias, sizeof(float) * 16, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_conv2_weights, d_conv2_weights, sizeof(float) * 4 * 4 * 16 * 32, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_conv2_bias, d_conv2_bias, sizeof(float) * 32, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_fc1_weights, d_fc1_weights, sizeof(float) * 9 * 9 * 32 * 256, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_fc1_bias, d_fc1_bias, sizeof(float) * 256, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_fc2_weights, d_fc2_weights, sizeof(float) * 256 * num_actions, cudaMemcpyDeviceToDevice);\n        cudaMemcpy(target.d_fc2_bias, d_fc2_bias, sizeof(float) * num_actions, cudaMemcpyDeviceToDevice);\n    }\n    \n    // Forward pass implementation\n    void forward(float* state, float* q_values) {\n        // Copy input state to device\n        cudaMemcpy(d_input, state, sizeof(float) * STATE_SIZE, cudaMemcpyHostToDevice);\n        \n        // First convolutional layer + ReLU\n        dim3 conv1_grid(1, 1, 1);\n        dim3 conv1_block(16, 20, 20); // One thread per output element\n        conv2d<<<conv1_grid, conv1_block>>>(d_input, d_conv1_weights, d_conv1_bias, d_conv1_output, \n                                             FRAME_WIDTH, FRAME_HEIGHT, FRAME_HISTORY, \n                                             8, 4, 16);\n        relu_activation<<<16, 20*20>>>(d_conv1_output, 20 * 20);\n        \n        // Second convolutional layer + ReLU\n        dim3 conv2_grid(1, 1, 1);\n        dim3 conv2_block(32, 9, 9);\n        conv2d<<<conv2_grid, conv2_block>>>(d_conv1_output, d_conv2_weights, d_conv2_bias, d_conv2_output,\n                                            20, 20, 16,\n                                            4, 2, 32);\n        relu_activation<<<32, 9*9>>>(d_conv2_output, 9 * 9);\n        \n        // First fully connected layer + ReLU\n        dim3 fc1_grid(1, 1, 1);\n        dim3 fc1_block(256, 1, 1);\n        fully_connected<<<fc1_grid, fc1_block>>>(d_conv2_output, d_fc1_weights, d_fc1_bias, d_fc1_output,\n                                                 9 * 9 * 32, 256);\n        relu_activation<<<1, 256>>>(d_fc1_output, 256);\n        \n        // Output layer\n        dim3 fc2_grid(1, 1, 1);\n        dim3 fc2_block(num_actions, 1, 1);\n        fully_connected<<<fc2_grid, fc2_block>>>(d_fc1_output, d_fc2_weights, d_fc2_bias, d_output,\n                                                 256, num_actions);\n        \n        // Copy results back to host\n        cudaMemcpy(q_values, d_output, sizeof(float) * num_actions, cudaMemcpyDeviceToHost);\n    }\n};\n\n// Deep Q-Network Agent implementation\nclass DQNAgent {\nprivate:\n    std::unique_ptr<ConvolutionalNetwork> q_network;\n    std::unique_ptr<ConvolutionalNetwork> target_network;\n    std::unique_ptr<ReplayMemory> replay_memory;\n    \n    int num_actions;\n    int frame_count;\n    float epsilon;\n    std::mt19937 rng;\n    \n    // Preprocess a frame (for example, grayscale and normalization)\n    void preprocess_frame(uint8_t* raw_frame, float* processed_frame) {\n        for (int i = 0; i < FRAME_HEIGHT * FRAME_WIDTH; i++) {\n            processed_frame[i] = static_cast<float>(raw_frame[i]) / 255.0f;\n        }\n    }\n    \n    // Update epsilon value based on frame count\n    void update_epsilon() {\n        if (frame_count < FINAL_EXPLORATION_FRAME) {\n            epsilon = INITIAL_EXPLORATION - \n                      frame_count * (INITIAL_EXPLORATION - FINAL_EXPLORATION) / FINAL_EXPLORATION_FRAME;\n        } else {\n            epsilon = FINAL_EXPLORATION;\n        }\n    }\n\npublic:\n    DQNAgent(int actions) : num_actions(actions), frame_count(0), epsilon(INITIAL_EXPLORATION),\n                           rng(std::random_device{}()) {\n        q_network = std::make_unique<ConvolutionalNetwork>(actions);\n        target_network = std::make_unique<ConvolutionalNetwork>(actions);\n        replay_memory = std::make_unique<ReplayMemory>(REPLAY_MEMORY_SIZE);\n        q_network->clone_to(*target_network);\n    }\n    \n    // Epsilon-greedy action selection\n    int select_action(float* state) {\n        std::uniform_real_distribution<float> dist(0, 1);\n        if (dist(rng) < epsilon) {\n            std::uniform_int_distribution<int> action_dist(0, num_actions - 1);\n            return action_dist(rng);\n        } else {\n            float q_values[MAX_ACTIONS];\n            q_network->forward(state, q_values);\n            return std::distance(q_values, std::max_element(q_values, q_values + num_actions));\n        }\n    }\n    \n    // Store experience in replay memory\n    void store_experience(float* state, int action, float reward, float* next_state, bool terminal) {\n        Experience exp;\n        std::copy(state, state + STATE_SIZE, exp.state);\n        exp.action = action;\n        exp.reward = reward;\n        std::copy(next_state, next_state + STATE_SIZE, exp.next_state);\n        exp.terminal = terminal;\n        replay_memory->add(exp);\n    }\n    \n    // Perform one training step\n    void train() {\n        if (replay_memory->size() < BATCH_SIZE) return;\n        \n        std::vector<Experience> batch;\n        replay_memory->sample(batch, BATCH_SIZE);\n        \n        float states[BATCH_SIZE][STATE_SIZE];\n        int actions[BATCH_SIZE];\n        float rewards[BATCH_SIZE];\n        float next_states[BATCH_SIZE][STATE_SIZE];\n        bool terminals[BATCH_SIZE];\n        \n        for (int i = 0; i < BATCH_SIZE; i++) {\n            const Experience& exp = batch[i];\n            std::copy(exp.state, exp.state + STATE_SIZE, states[i]);\n            actions[i] = exp.action;\n            rewards[i] = exp.reward;\n            std::copy(exp.next_state, exp.next_state + STATE_SIZE, next_states[i]);\n            terminals[i] = exp.terminal;\n        }\n        \n        float target_q_values[BATCH_SIZE][MAX_ACTIONS];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            target_network->forward(next_states[i], target_q_values[i]);\n        }\n        \n        float current_q_values[BATCH_SIZE][MAX_ACTIONS];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            q_network->forward(states[i], current_q_values[i]);\n        }\n        \n        float targets[BATCH_SIZE];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            if (terminals[i]) {\n                targets[i] = rewards[i];\n            } else {\n                float max_next_q = *std::max_element(target_q_values[i], target_q_values[i] + num_actions);\n                targets[i] = rewards[i] + GAMMA * max_next_q;\n            }\n        }\n        \n        // RMSProp optimization and weight update would go here\n        \n        frame_count++;\n        update_epsilon();\n        if (frame_count % TARGET_NETWORK_UPDATE_FREQUENCY == 0) {\n            q_network->clone_to(*target_network);\n        }\n    }\n    \n    // Process frame and train the network\n    void process_frame_and_train(uint8_t* raw_frame, int action, float reward, bool terminal) {\n        static float current_state[STATE_SIZE] = {0};\n        static float next_state[STATE_SIZE] = {0};\n        \n        std::copy(current_state + FRAME_WIDTH * FRAME_HEIGHT, \n                  current_state + STATE_SIZE, \n                  current_state);\n        \n        float processed_frame[FRAME_WIDTH * FRAME_HEIGHT];\n        preprocess_frame(raw_frame, processed_frame);\n        std::copy(processed_frame, \n                  processed_frame + FRAME_WIDTH * FRAME_HEIGHT, \n                  current_state + STATE_SIZE - FRAME_WIDTH * FRAME_HEIGHT);\n        \n        if (frame_count >= FRAME_HISTORY - 1) {\n            if (action < 0) {\n                action = select_action(current_state);\n            }\n            std::copy(current_state, current_state + STATE_SIZE, next_state);\n            store_experience(current_state, action, reward, next_state, terminal);\n            train();\n        } else {\n            frame_count++;\n        }\n    }\n};\n\n// Simple example of usage\nint main() {\n    const int NUM_ACTIONS = 4;\n    DQNAgent agent(NUM_ACTIONS);\n    \n    uint8_t dummy_frame[FRAME_WIDTH * FRAME_HEIGHT] = {0};\n    \n    // Simulate 100 frames\n    for (int i = 0; i < 100; i++) {\n        for (int j = 0; j < FRAME_WIDTH * FRAME_HEIGHT; j++) {\n            dummy_frame[j] = rand() % 256;\n        }\n        \n        float reward = (i % 10 == 0) ? 1.0f : 0.0f;\n        bool terminal = (i == 99);\n        \n        agent.process_frame_and_train(dummy_frame, -1, reward, terminal);\n    }\n    \n    std::cout << \"Training completed for 100 frames!\" << std::endl;\n    return 0;\n}\n", "output": "#include <iostream>\n#include <vector>\n#include <random>\n#include <algorithm>\n#include <memory>\n#include <deque>\n#include <hip/hip_runtime.h>\n#include <hiprand/hiprand.h>\n#include <hipblas.h>\n#include <hip/hip_fp16.h>\n#include <cmath>  // for sqrt\n#include <cstdlib>\n#include <ctime>\n\n// DQN Configuration\n#define FRAME_HEIGHT 84\n#define FRAME_WIDTH 84\n#define FRAME_HISTORY 4\n#define STATE_SIZE (FRAME_HEIGHT * FRAME_WIDTH * FRAME_HISTORY)\n#define MAX_ACTIONS 18  // Maximum number of actions in Atari games\n#define REPLAY_MEMORY_SIZE 1000000\n#define BATCH_SIZE 32\n#define GAMMA 0.99f\n#define INITIAL_EXPLORATION 1.0f\n#define FINAL_EXPLORATION 0.1f\n#define FINAL_EXPLORATION_FRAME 1000000\n#define TARGET_NETWORK_UPDATE_FREQUENCY 10000\n#define LEARNING_RATE 0.00025f\n#define GRADIENT_MOMENTUM 0.95f\n#define SQUARED_GRADIENT_MOMENTUM 0.95f\n#define MIN_SQUARED_GRADIENT 0.01f\n\n// CUDA Error Checking\n#define CHECK_CUDA_ERROR(val) check_cuda((val), #val, __FILE__, __LINE__)\ninline void check_cuda(hipError_t err, const char* const func, const char* const file, const int line) {\n    if (err != hipSuccess) {\n        std::cerr << \"CUDA Runtime Error at: \" << file << \":\" << line << std::endl;\n        std::cerr << hipGetErrorString(err) << \" \" << func << std::endl;\n        std::exit(EXIT_FAILURE);\n    }\n}\n\n// Experience Replay Memory Structure\nstruct Experience {\n    float state[STATE_SIZE];\n    int action;\n    float reward;\n    float next_state[STATE_SIZE];\n    bool terminal;\n};\n\nclass ReplayMemory {\nprivate:\n    std::deque<Experience> memory;\n    size_t max_size;\n    std::mt19937 rng;\n\npublic:\n    ReplayMemory(size_t size) : max_size(size), rng(std::random_device{}()) {}\n\n    void add(const Experience& exp) {\n        if (memory.size() >= max_size) {\n            memory.pop_front();\n        }\n        memory.push_back(exp);\n    }\n\n    void sample(std::vector<Experience>& batch, size_t batch_size) {\n        batch.clear();\n        if (memory.size() < batch_size) return;\n\n        std::uniform_int_distribution<size_t> dist(0, memory.size() - 1);\n        for (size_t i = 0; i < batch_size; ++i) {\n            batch.push_back(memory[dist(rng)]);\n        }\n    }\n\n    size_t size() const {\n        return memory.size();\n    }\n};\n\n// CUDA kernel implementations (free functions)\n\n__global__ void conv2d(float* input, float* weights, float* bias, float* output,\n                       int input_h, int input_w, int channels,\n                       int kernel_size, int stride, int num_filters) {\n    int filter_idx = threadIdx.x;\n    int out_x = threadIdx.y;\n    int out_y = threadIdx.z;\n\n    if (filter_idx < num_filters) {\n        float sum = bias[filter_idx];\n        int in_x_start = out_x * stride;\n        int in_y_start = out_y * stride;\n\n        for (int c = 0; c < channels; c++) {\n            for (int ky = 0; ky < kernel_size; ky++) {\n                for (int kx = 0; kx < kernel_size; kx++) {\n                    int in_x = in_x_start + kx;\n                    int in_y = in_y_start + ky;\n                    if (in_x < input_w && in_y < input_h) {\n                        int input_idx = c * input_h * input_w + in_y * input_w + in_x;\n                        int weight_idx = filter_idx * channels * kernel_size * kernel_size +\n                                         c * kernel_size * kernel_size +\n                                         ky * kernel_size + kx;\n                        sum += input[input_idx] * weights[weight_idx];\n                    }\n                }\n            }\n        }\n        int out_idx = filter_idx * gridDim.y * gridDim.z + out_y * gridDim.y + out_x;\n        output[out_idx] = sum;\n    }\n}\n\n__global__ void relu_activation(float* input, int size) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < size) {\n        input[idx] = max(0.0f, input[idx]);\n    }\n}\n\n__global__ void fully_connected(float* input, float* weights, float* bias, float* output,\n                                int input_size, int output_size) {\n    int out_idx = threadIdx.x;\n    if (out_idx < output_size) {\n        float sum = bias[out_idx];\n        for (int in_idx = 0; in_idx < input_size; in_idx++) {\n            sum += input[in_idx] * weights[in_idx * output_size + out_idx];\n        }\n        output[out_idx] = sum;\n    }\n}\n\n// Convolutional Neural Network for DQN\nclass ConvolutionalNetwork {\nprivate:\n    // Device memory pointers for weights and biases\n    float *d_conv1_weights, *d_conv1_bias;\n    float *d_conv2_weights, *d_conv2_bias;\n    float *d_fc1_weights, *d_fc1_bias;\n    float *d_fc2_weights, *d_fc2_bias;\n    \n    // Device memory for intermediate results\n    float *d_input;\n    float *d_conv1_output, *d_conv2_output;\n    float *d_fc1_output, *d_output;\n    \n    // Network dimensions\n    int num_actions;\n    \n    // CUDA handles\n    hipblasHandle_t cublas_handle;\n    hiprandGenerator_t curand_gen;\n    \n    // Initialize weights with Xavier/Glorot initialization\n    void init_weights(float* d_weights, int fan_in, int fan_out) {\n        float scale = sqrt(6.0f / (fan_in + fan_out));\n        hiprandGenerateUniform(curand_gen, d_weights, fan_in * fan_out);\n        float h_scale = 2.0f * scale;\n        float h_offset = -scale;\n        std::vector<float> h_weights(fan_in * fan_out);\n        CHECK_CUDA_ERROR(hipMemcpy(h_weights.data(), d_weights, sizeof(float) * fan_in * fan_out, hipMemcpyDeviceToHost));\n        for (int i = 0; i < fan_in * fan_out; i++) {\n            h_weights[i] = h_weights[i] * h_scale + h_offset;\n        }\n        CHECK_CUDA_ERROR(hipMemcpy(d_weights, h_weights.data(), sizeof(float) * fan_in * fan_out, hipMemcpyHostToDevice));\n    }\n    \n    // Initialize biases to small constant value\n    void init_biases(float* d_biases, int size, float value = 0.01f) {\n        std::vector<float> h_biases(size, value);\n        CHECK_CUDA_ERROR(hipMemcpy(d_biases, h_biases.data(), sizeof(float) * size, hipMemcpyHostToDevice));\n    }\n\npublic:\n    ConvolutionalNetwork(int actions) : num_actions(actions) {\n        // Create CUDA handles\n        hipblasCreate(&cublas_handle);\n        hiprandCreateGenerator(&curand_gen, HIPRAND_RNG_PSEUDO_DEFAULT);\n        hiprandSetPseudoRandomGeneratorSeed(curand_gen, static_cast<unsigned long long>(time(NULL)));\n        \n        // Allocate memory for weights and biases\n        // Layer 1: Convolutional 8x8 with stride 4, 16 filters\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv1_weights, sizeof(float) * 8 * 8 * FRAME_HISTORY * 16));\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv1_bias, sizeof(float) * 16));\n        \n        // Layer 2: Convolutional 4x4 with stride 2, 32 filters\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv2_weights, sizeof(float) * 4 * 4 * 16 * 32));\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv2_bias, sizeof(float) * 32));\n        \n        // Layer 3: Fully connected with 256 rectifier units\n        int conv_output_size = 9 * 9 * 32; // After two convolutions\n        CHECK_CUDA_ERROR(hipMalloc(&d_fc1_weights, sizeof(float) * conv_output_size * 256));\n        CHECK_CUDA_ERROR(hipMalloc(&d_fc1_bias, sizeof(float) * 256));\n        \n        // Layer 4: Fully connected output layer\n        CHECK_CUDA_ERROR(hipMalloc(&d_fc2_weights, sizeof(float) * 256 * num_actions));\n        CHECK_CUDA_ERROR(hipMalloc(&d_fc2_bias, sizeof(float) * num_actions));\n        \n        // Allocate memory for intermediate results\n        CHECK_CUDA_ERROR(hipMalloc(&d_input, sizeof(float) * STATE_SIZE));\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv1_output, sizeof(float) * 20 * 20 * 16)); // After 1st conv\n        CHECK_CUDA_ERROR(hipMalloc(&d_conv2_output, sizeof(float) * 9 * 9 * 32));   // After 2nd conv\n        CHECK_CUDA_ERROR(hipMalloc(&d_fc1_output, sizeof(float) * 256));            // After FC\n        CHECK_CUDA_ERROR(hipMalloc(&d_output, sizeof(float) * num_actions));        // Final output\n        \n        // Initialize weights and biases\n        init_weights(d_conv1_weights, 8 * 8 * FRAME_HISTORY, 16);\n        init_biases(d_conv1_bias, 16);\n        \n        init_weights(d_conv2_weights, 4 * 4 * 16, 32);\n        init_biases(d_conv2_bias, 32);\n        \n        init_weights(d_fc1_weights, conv_output_size, 256);\n        init_biases(d_fc1_bias, 256);\n        \n        init_weights(d_fc2_weights, 256, num_actions);\n        init_biases(d_fc2_bias, num_actions);\n    }\n    \n    ~ConvolutionalNetwork() {\n        // Free allocated memory\n        hipFree(d_conv1_weights);\n        hipFree(d_conv1_bias);\n        hipFree(d_conv2_weights);\n        hipFree(d_conv2_bias);\n        hipFree(d_fc1_weights);\n        hipFree(d_fc1_bias);\n        hipFree(d_fc2_weights);\n        hipFree(d_fc2_bias);\n        \n        hipFree(d_input);\n        hipFree(d_conv1_output);\n        hipFree(d_conv2_output);\n        hipFree(d_fc1_output);\n        hipFree(d_output);\n        \n        hipblasDestroy(cublas_handle);\n        hiprandDestroyGenerator(curand_gen);\n    }\n    \n    // Clone network weights to target network\n    void clone_to(ConvolutionalNetwork& target) {\n        hipMemcpy(target.d_conv1_weights, d_conv1_weights, sizeof(float) * 8 * 8 * FRAME_HISTORY * 16, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_conv1_bias, d_conv1_bias, sizeof(float) * 16, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_conv2_weights, d_conv2_weights, sizeof(float) * 4 * 4 * 16 * 32, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_conv2_bias, d_conv2_bias, sizeof(float) * 32, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_fc1_weights, d_fc1_weights, sizeof(float) * 9 * 9 * 32 * 256, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_fc1_bias, d_fc1_bias, sizeof(float) * 256, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_fc2_weights, d_fc2_weights, sizeof(float) * 256 * num_actions, hipMemcpyDeviceToDevice);\n        hipMemcpy(target.d_fc2_bias, d_fc2_bias, sizeof(float) * num_actions, hipMemcpyDeviceToDevice);\n    }\n    \n    // Forward pass implementation\n    void forward(float* state, float* q_values) {\n        // Copy input state to device\n        hipMemcpy(d_input, state, sizeof(float) * STATE_SIZE, hipMemcpyHostToDevice);\n        \n        // First convolutional layer + ReLU\n        dim3 conv1_grid(1, 1, 1);\n        dim3 conv1_block(16, 20, 20); // One thread per output element\n        conv2d<<<conv1_grid, conv1_block>>>(d_input, d_conv1_weights, d_conv1_bias, d_conv1_output, \n                                             FRAME_WIDTH, FRAME_HEIGHT, FRAME_HISTORY, \n                                             8, 4, 16);\n        relu_activation<<<16, 20*20>>>(d_conv1_output, 20 * 20);\n        \n        // Second convolutional layer + ReLU\n        dim3 conv2_grid(1, 1, 1);\n        dim3 conv2_block(32, 9, 9);\n        conv2d<<<conv2_grid, conv2_block>>>(d_conv1_output, d_conv2_weights, d_conv2_bias, d_conv2_output,\n                                            20, 20, 16,\n                                            4, 2, 32);\n        relu_activation<<<32, 9*9>>>(d_conv2_output, 9 * 9);\n        \n        // First fully connected layer + ReLU\n        dim3 fc1_grid(1, 1, 1);\n        dim3 fc1_block(256, 1, 1);\n        fully_connected<<<fc1_grid, fc1_block>>>(d_conv2_output, d_fc1_weights, d_fc1_bias, d_fc1_output,\n                                                 9 * 9 * 32, 256);\n        relu_activation<<<1, 256>>>(d_fc1_output, 256);\n        \n        // Output layer\n        dim3 fc2_grid(1, 1, 1);\n        dim3 fc2_block(num_actions, 1, 1);\n        fully_connected<<<fc2_grid, fc2_block>>>(d_fc1_output, d_fc2_weights, d_fc2_bias, d_output,\n                                                 256, num_actions);\n        \n        // Copy results back to host\n        hipMemcpy(q_values, d_output, sizeof(float) * num_actions, hipMemcpyDeviceToHost);\n    }\n};\n\n// Deep Q-Network Agent implementation\nclass DQNAgent {\nprivate:\n    std::unique_ptr<ConvolutionalNetwork> q_network;\n    std::unique_ptr<ConvolutionalNetwork> target_network;\n    std::unique_ptr<ReplayMemory> replay_memory;\n    \n    int num_actions;\n    int frame_count;\n    float epsilon;\n    std::mt19937 rng;\n    \n    // Preprocess a frame (for example, grayscale and normalization)\n    void preprocess_frame(uint8_t* raw_frame, float* processed_frame) {\n        for (int i = 0; i < FRAME_HEIGHT * FRAME_WIDTH; i++) {\n            processed_frame[i] = static_cast<float>(raw_frame[i]) / 255.0f;\n        }\n    }\n    \n    // Update epsilon value based on frame count\n    void update_epsilon() {\n        if (frame_count < FINAL_EXPLORATION_FRAME) {\n            epsilon = INITIAL_EXPLORATION - \n                      frame_count * (INITIAL_EXPLORATION - FINAL_EXPLORATION) / FINAL_EXPLORATION_FRAME;\n        } else {\n            epsilon = FINAL_EXPLORATION;\n        }\n    }\n\npublic:\n    DQNAgent(int actions) : num_actions(actions), frame_count(0), epsilon(INITIAL_EXPLORATION),\n                           rng(std::random_device{}()) {\n        q_network = std::make_unique<ConvolutionalNetwork>(actions);\n        target_network = std::make_unique<ConvolutionalNetwork>(actions);\n        replay_memory = std::make_unique<ReplayMemory>(REPLAY_MEMORY_SIZE);\n        q_network->clone_to(*target_network);\n    }\n    \n    // Epsilon-greedy action selection\n    int select_action(float* state) {\n        std::uniform_real_distribution<float> dist(0, 1);\n        if (dist(rng) < epsilon) {\n            std::uniform_int_distribution<int> action_dist(0, num_actions - 1);\n            return action_dist(rng);\n        } else {\n            float q_values[MAX_ACTIONS];\n            q_network->forward(state, q_values);\n            return std::distance(q_values, std::max_element(q_values, q_values + num_actions));\n        }\n    }\n    \n    // Store experience in replay memory\n    void store_experience(float* state, int action, float reward, float* next_state, bool terminal) {\n        Experience exp;\n        std::copy(state, state + STATE_SIZE, exp.state);\n        exp.action = action;\n        exp.reward = reward;\n        std::copy(next_state, next_state + STATE_SIZE, exp.next_state);\n        exp.terminal = terminal;\n        replay_memory->add(exp);\n    }\n    \n    // Perform one training step\n    void train() {\n        if (replay_memory->size() < BATCH_SIZE) return;\n        \n        std::vector<Experience> batch;\n        replay_memory->sample(batch, BATCH_SIZE);\n        \n        float states[BATCH_SIZE][STATE_SIZE];\n        int actions[BATCH_SIZE];\n        float rewards[BATCH_SIZE];\n        float next_states[BATCH_SIZE][STATE_SIZE];\n        bool terminals[BATCH_SIZE];\n        \n        for (int i = 0; i < BATCH_SIZE; i++) {\n            const Experience& exp = batch[i];\n            std::copy(exp.state, exp.state + STATE_SIZE, states[i]);\n            actions[i] = exp.action;\n            rewards[i] = exp.reward;\n            std::copy(exp.next_state, exp.next_state + STATE_SIZE, next_states[i]);\n            terminals[i] = exp.terminal;\n        }\n        \n        float target_q_values[BATCH_SIZE][MAX_ACTIONS];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            target_network->forward(next_states[i], target_q_values[i]);\n        }\n        \n        float current_q_values[BATCH_SIZE][MAX_ACTIONS];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            q_network->forward(states[i], current_q_values[i]);\n        }\n        \n        float targets[BATCH_SIZE];\n        for (int i = 0; i < BATCH_SIZE; i++) {\n            if (terminals[i]) {\n                targets[i] = rewards[i];\n            } else {\n                float max_next_q = *std::max_element(target_q_values[i], target_q_values[i] + num_actions);\n                targets[i] = rewards[i] + GAMMA * max_next_q;\n            }\n        }\n        \n        // RMSProp optimization and weight update would go here\n        \n        frame_count++;\n        update_epsilon();\n        if (frame_count % TARGET_NETWORK_UPDATE_FREQUENCY == 0) {\n            q_network->clone_to(*target_network);\n        }\n    }\n    \n    // Process frame and train the network\n    void process_frame_and_train(uint8_t* raw_frame, int action, float reward, bool terminal) {\n        static float current_state[STATE_SIZE] = {0};\n        static float next_state[STATE_SIZE] = {0};\n        \n        std::copy(current_state + FRAME_WIDTH * FRAME_HEIGHT, \n                  current_state + STATE_SIZE, \n                  current_state);\n        \n        float processed_frame[FRAME_WIDTH * FRAME_HEIGHT];\n        preprocess_frame(raw_frame, processed_frame);\n        std::copy(processed_frame, \n                  processed_frame + FRAME_WIDTH * FRAME_HEIGHT, \n                  current_state + STATE_SIZE - FRAME_WIDTH * FRAME_HEIGHT);\n        \n        if (frame_count >= FRAME_HISTORY - 1) {\n            if (action < 0) {\n                action = select_action(current_state);\n            }\n            std::copy(current_state, current_state + STATE_SIZE, next_state);\n            store_experience(current_state, action, reward, next_state, terminal);\n            train();\n        } else {\n            frame_count++;\n        }\n    }\n};\n\n// Simple example of usage\nint main() {\n    const int NUM_ACTIONS = 4;\n    DQNAgent agent(NUM_ACTIONS);\n    \n    uint8_t dummy_frame[FRAME_WIDTH * FRAME_HEIGHT] = {0};\n    \n    // Simulate 100 frames\n    for (int i = 0; i < 100; i++) {\n        for (int j = 0; j < FRAME_WIDTH * FRAME_HEIGHT; j++) {\n            dummy_frame[j] = rand() % 256;\n        }\n        \n        float reward = (i % 10 == 0) ? 1.0f : 0.0f;\n        bool terminal = (i == 99);\n        \n        agent.process_frame_and_train(dummy_frame, -1, reward, terminal);\n    }\n    \n    std::cout << \"Training completed for 100 frames!\" << std::endl;\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 64/Atari_dqn.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void inlinePTXKernel(int *d_out, int *d_in, int addVal) {\n    int tid = threadIdx.x;\n    int val;\n\n    // Load a value from global memory using PTX\n    asm volatile(\"ld.global.s32 %0, [%1];\" : \"=r\"(val) : \"l\"(d_in + tid));\n\n    // Add a constant using PTX\n    asm volatile(\"add.s32 %0, %0, %1;\" : \"+r\"(val) : \"r\"(addVal));\n\n    // Store the result back to global memory using PTX\n    asm volatile(\"st.global.s32 [%0], %1;\" :: \"l\"(d_out + tid), \"r\"(val));\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    int addVal = 10;\n\n    for (int i = 0; i < N; i++) h_in[i] = i;\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, N * sizeof(int));\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    inlinePTXKernel<<<1, N>>>(d_out, d_in, addVal);\n\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Results: \";\n    for (int i = 0; i < N; i++)\n        std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void inlinePTXKernel(int *d_out, int *d_in, int addVal) {\n    int tid = threadIdx.x;\n    int val;\n\n    // Load a value from global memory using PTX\n    asm volatile(\"ld.global.s32 %0, [%1];\" : \"=r\"(val) : \"l\"(d_in + tid));\n\n    // Add a constant using PTX\n    asm volatile(\"add.s32 %0, %0, %1;\" : \"+r\"(val) : \"r\"(addVal));\n\n    // Store the result back to global memory using PTX\n    asm volatile(\"st.global.s32 [%0], %1;\" :: \"l\"(d_out + tid), \"r\"(val));\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    int addVal = 10;\n\n    for (int i = 0; i < N; i++) h_in[i] = i;\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, N * sizeof(int));\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    inlinePTXKernel<<<1, N>>>(d_out, d_in, addVal);\n\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Results: \";\n    for (int i = 0; i < N; i++)\n        std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 39/inline.cu"}
{"input": "#include <iostream>\n\n__global__ void vectorAdd(float *A, float *B, float *C, int N) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < N) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    const int N = 1024;\n    size_t size = N * sizeof(float);\n\n    float *h_A = new float[N];\n    float *h_B = new float[N];\n    float *h_C = new float[N];\n\n    for (int i = 0; i < N; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float *d_A, *d_B, *d_C;\n    cudaMalloc(&d_A, size);\n    cudaMalloc(&d_B, size);\n    cudaMalloc(&d_C, size);\n\n    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n    dim3 blockSize(256);\n    dim3 gridSize((N + blockSize.x - 1) / blockSize.x);\n    vectorAdd<<<gridSize, blockSize>>>(d_A, d_B, d_C, N);\n\n    cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);\n\n    std::cout << \"C[0] = \" << h_C[0] << std::endl;\n\n    delete[] h_A; delete[] h_B; delete[] h_C;\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <iostream>\n\n__global__ void vectorAdd(float *A, float *B, float *C, int N) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < N) {\n        C[i] = A[i] + B[i];\n    }\n}\n\nint main() {\n    const int N = 1024;\n    size_t size = N * sizeof(float);\n\n    float *h_A = new float[N];\n    float *h_B = new float[N];\n    float *h_C = new float[N];\n\n    for (int i = 0; i < N; i++) {\n        h_A[i] = 1.0f;\n        h_B[i] = 2.0f;\n    }\n\n    float *d_A, *d_B, *d_C;\n    hipMalloc(&d_A, size);\n    hipMalloc(&d_B, size);\n    hipMalloc(&d_C, size);\n\n    hipMemcpy(d_A, h_A, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, size, hipMemcpyHostToDevice);\n\n    dim3 blockSize(256);\n    dim3 gridSize((N + blockSize.x - 1) / blockSize.x);\n    vectorAdd<<<gridSize, blockSize>>>(d_A, d_B, d_C, N);\n\n    hipMemcpy(h_C, d_C, size, hipMemcpyDeviceToHost);\n\n    std::cout << \"C[0] = \" << h_C[0] << std::endl;\n\n    delete[] h_A; delete[] h_B; delete[] h_C;\n    hipFree(d_A); hipFree(d_B); hipFree(d_C);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 39/vector_add.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n#define STATES 4\n#define ACTIONS 2\n#define GAMMA 0.9f\n#define THRESHOLD 0.01f\n\n__global__ void value_iteration_kernel(float *V, float *R, float *P, float *newV) {\n    int state = threadIdx.x;\n    if (state >= STATES) return;\n\n    float max_value = -1e9;\n    for (int action = 0; action < ACTIONS; action++) {\n        float sum_value = 0.0f;\n        for (int next_state = 0; next_state < STATES; next_state++) {\n            float transition_prob = P[state * ACTIONS * STATES + action * STATES + next_state];\n            float reward = R[state * ACTIONS + action];\n            sum_value += transition_prob * (reward + GAMMA * V[next_state]);\n        }\n        if (sum_value > max_value)\n            max_value = sum_value;\n    }\n    newV[state] = max_value;\n}\n\nvoid value_iteration(float *h_V, float *h_R, float *h_P) {\n    float *d_V, *d_R, *d_P, *d_newV;\n    cudaMalloc((void **)&d_V, STATES * sizeof(float));\n    cudaMalloc((void **)&d_R, STATES * ACTIONS * sizeof(float));\n    cudaMalloc((void **)&d_P, STATES * ACTIONS * STATES * sizeof(float));\n    cudaMalloc((void **)&d_newV, STATES * sizeof(float));\n\n    cudaMemcpy(d_V, h_V, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_R, h_R, STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_P, h_P, STATES * ACTIONS * STATES * sizeof(float), cudaMemcpyHostToDevice);\n\n    bool converged = false;\n    while (!converged) {\n        value_iteration_kernel<<<1, STATES>>>(d_V, d_R, d_P, d_newV);\n        cudaMemcpy(h_V, d_newV, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n\n        converged = true;\n        for (int i = 0; i < STATES; i++) {\n            if (fabs(h_V[i] - h_V[i]) > THRESHOLD) {\n                converged = false;\n                break;\n            }\n        }\n\n        cudaMemcpy(d_V, d_newV, STATES * sizeof(float), cudaMemcpyDeviceToDevice);\n    }\n\n    cudaFree(d_V);\n    cudaFree(d_R);\n    cudaFree(d_P);\n    cudaFree(d_newV);\n}\n\nint main() {\n    float h_V[STATES] = {0, 0, 0, 0};\n    float h_R[STATES * ACTIONS] = {\n        5, 10, 2, 3, 0, -1, 7, 8\n    };\n    float h_P[STATES * ACTIONS * STATES] = { \n        0.8, 0.2, 0.0, 0.0, 0.7, 0.3, 0.0, 0.0,  \n        0.0, 0.6, 0.4, 0.0, 0.0, 0.5, 0.5, 0.0,  \n        0.0, 0.0, 0.9, 0.1, 0.0, 0.0, 0.8, 0.2,  \n        0.3, 0.0, 0.0, 0.7, 0.4, 0.0, 0.0, 0.6\n    };\n\n    value_iteration(h_V, h_R, h_P);\n\n    std::cout << \"Optimal Value Function:\\n\";\n    for (int i = 0; i < STATES; i++) {\n        std::cout << \"V[\" << i << \"] = \" << h_V[i] << std::endl;\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n#define STATES 4\n#define ACTIONS 2\n#define GAMMA 0.9f\n#define THRESHOLD 0.01f\n\n__global__ void value_iteration_kernel(float *V, float *R, float *P, float *newV) {\n    int state = threadIdx.x;\n    if (state >= STATES) return;\n\n    float max_value = -1e9;\n    for (int action = 0; action < ACTIONS; action++) {\n        float sum_value = 0.0f;\n        for (int next_state = 0; next_state < STATES; next_state++) {\n            float transition_prob = P[state * ACTIONS * STATES + action * STATES + next_state];\n            float reward = R[state * ACTIONS + action];\n            sum_value += transition_prob * (reward + GAMMA * V[next_state]);\n        }\n        if (sum_value > max_value)\n            max_value = sum_value;\n    }\n    newV[state] = max_value;\n}\n\nvoid value_iteration(float *h_V, float *h_R, float *h_P) {\n    float *d_V, *d_R, *d_P, *d_newV;\n    hipMalloc((void **)&d_V, STATES * sizeof(float));\n    hipMalloc((void **)&d_R, STATES * ACTIONS * sizeof(float));\n    hipMalloc((void **)&d_P, STATES * ACTIONS * STATES * sizeof(float));\n    hipMalloc((void **)&d_newV, STATES * sizeof(float));\n\n    hipMemcpy(d_V, h_V, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_R, h_R, STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_P, h_P, STATES * ACTIONS * STATES * sizeof(float), hipMemcpyHostToDevice);\n\n    bool converged = false;\n    while (!converged) {\n        value_iteration_kernel<<<1, STATES>>>(d_V, d_R, d_P, d_newV);\n        hipMemcpy(h_V, d_newV, STATES * sizeof(float), hipMemcpyDeviceToHost);\n\n        converged = true;\n        for (int i = 0; i < STATES; i++) {\n            if (fabs(h_V[i] - h_V[i]) > THRESHOLD) {\n                converged = false;\n                break;\n            }\n        }\n\n        hipMemcpy(d_V, d_newV, STATES * sizeof(float), hipMemcpyDeviceToDevice);\n    }\n\n    hipFree(d_V);\n    hipFree(d_R);\n    hipFree(d_P);\n    hipFree(d_newV);\n}\n\nint main() {\n    float h_V[STATES] = {0, 0, 0, 0};\n    float h_R[STATES * ACTIONS] = {\n        5, 10, 2, 3, 0, -1, 7, 8\n    };\n    float h_P[STATES * ACTIONS * STATES] = { \n        0.8, 0.2, 0.0, 0.0, 0.7, 0.3, 0.0, 0.0,  \n        0.0, 0.6, 0.4, 0.0, 0.0, 0.5, 0.5, 0.0,  \n        0.0, 0.0, 0.9, 0.1, 0.0, 0.0, 0.8, 0.2,  \n        0.3, 0.0, 0.0, 0.7, 0.4, 0.0, 0.0, 0.6\n    };\n\n    value_iteration(h_V, h_R, h_P);\n\n    std::cout << \"Optimal Value Function:\\n\";\n    for (int i = 0; i < STATES; i++) {\n        std::cout << \"V[\" << i << \"] = \" << h_V[i] << std::endl;\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 53/mdp_cuda.cu"}
{"input": "#include \"matmul.h\"\n\n#define TILE_SIZE 32\n#define THREADS_PER_BLOCK 16\n\n__global__ void matmul_kernel_optimized(const float* __restrict__ A,\n                                        const float* __restrict__ B,\n                                        float* __restrict__ C,\n                                        size_t M, size_t N, size_t K)\n{\n    __shared__ float As[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bs[TILE_SIZE][TILE_SIZE];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    int blockRow = blockIdx.y * TILE_SIZE;\n    int blockCol = blockIdx.x * TILE_SIZE;\n\n    // Each thread computes a 2x2 sub-block of C\n    float sum00 = 0.0f, sum01 = 0.0f, sum10 = 0.0f, sum11 = 0.0f;\n\n    for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; ++t) {\n        int tiledK = t * TILE_SIZE;\n\n        {\n            int aRow0 = blockRow + ty * 2;\n            int aCol0 = tiledK + tx * 2;\n\n            // First row of A\n            if (aRow0 < M) {\n                if (aCol0 + 1 < K) {\n                    float2 aVal = *reinterpret_cast<const float2*>(\n                        &A[aRow0 * K + aCol0]\n                    );\n                    As[ty * 2][tx * 2]     = aVal.x;\n                    As[ty * 2][tx * 2 + 1] = aVal.y;\n                } else if (aCol0 < K) {\n                    As[ty * 2][tx * 2]     = A[aRow0 * K + aCol0];\n                    As[ty * 2][tx * 2 + 1] = 0.0f;\n                } else {\n                    As[ty * 2][tx * 2]     = 0.0f;\n                    As[ty * 2][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                As[ty * 2][tx * 2]     = 0.0f;\n                As[ty * 2][tx * 2 + 1] = 0.0f;\n            }\n\n            int aRow1 = aRow0 + 1;\n            if (aRow1 < M) {\n                if (aCol0 + 1 < K) {\n                    float2 aVal = *reinterpret_cast<const float2*>(\n                        &A[aRow1 * K + aCol0]\n                    );\n                    As[ty * 2 + 1][tx * 2]     = aVal.x;\n                    As[ty * 2 + 1][tx * 2 + 1] = aVal.y;\n                } else if (aCol0 < K) {\n                    As[ty * 2 + 1][tx * 2]     = A[aRow1 * K + aCol0];\n                    As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                } else {\n                    As[ty * 2 + 1][tx * 2]     = 0.0f;\n                    As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                As[ty * 2 + 1][tx * 2]     = 0.0f;\n                As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n            }\n        }\n\n        // --- Load B into shared memory ---\n        {\n            int bRow0 = tiledK + ty * 2;\n            int bCol0 = blockCol + tx * 2;\n\n            if (bRow0 < K) {\n                if (bCol0 + 1 < N) {\n                    float2 bVal = *reinterpret_cast<const float2*>(\n                        &B[bRow0 * N + bCol0]\n                    );\n                    Bs[ty * 2][tx * 2]     = bVal.x;\n                    Bs[ty * 2][tx * 2 + 1] = bVal.y;\n                } else if (bCol0 < N) {\n                    Bs[ty * 2][tx * 2]     = B[bRow0 * N + bCol0];\n                    Bs[ty * 2][tx * 2 + 1] = 0.0f;\n                } else {\n                    Bs[ty * 2][tx * 2]     = 0.0f;\n                    Bs[ty * 2][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                Bs[ty * 2][tx * 2]     = 0.0f;\n                Bs[ty * 2][tx * 2 + 1] = 0.0f;\n            }\n\n            int bRow1 = bRow0 + 1;\n            if (bRow1 < K) {\n                if (bCol0 + 1 < N) {\n                    float2 bVal = *reinterpret_cast<const float2*>(\n                        &B[bRow1 * N + bCol0]\n                    );\n                    Bs[ty * 2 + 1][tx * 2]     = bVal.x;\n                    Bs[ty * 2 + 1][tx * 2 + 1] = bVal.y;\n                } else if (bCol0 < N) {\n                    Bs[ty * 2 + 1][tx * 2]     = B[bRow1 * N + bCol0];\n                    Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                } else {\n                    Bs[ty * 2 + 1][tx * 2]     = 0.0f;\n                    Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                Bs[ty * 2 + 1][tx * 2]     = 0.0f;\n                Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n            }\n        }\n\n        __syncthreads();\n\n        // --- Compute partial products ---\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            float a0 = As[ty * 2][i];\n            float a1 = As[ty * 2 + 1][i];\n            float b0 = Bs[i][tx * 2];\n            float b1 = Bs[i][tx * 2 + 1];\n\n            sum00 += a0 * b0;\n            sum01 += a0 * b1;\n            sum10 += a1 * b0;\n            sum11 += a1 * b1;\n        }\n\n        __syncthreads();\n    }\n\n    // --- Write results to C ---\n    int cRow = blockRow + ty * 2;\n    int cCol = blockCol + tx * 2;\n    if (cRow < M && cCol < N) {\n        C[cRow * N + cCol] = sum00;\n        if (cCol + 1 < N)\n            C[cRow * N + (cCol + 1)] = sum01;\n        if (cRow + 1 < M) {\n            C[(cRow + 1) * N + cCol] = sum10;\n            if (cCol + 1 < N)\n                C[(cRow + 1) * N + (cCol + 1)] = sum11;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t m, size_t n, size_t k)\n{\n    dim3 block(THREADS_PER_BLOCK, THREADS_PER_BLOCK);\n    dim3 grid((n + TILE_SIZE - 1) / TILE_SIZE,\n              (m + TILE_SIZE - 1) / TILE_SIZE);\n\n    matmul_kernel_optimized<<<grid, block>>>(input_a, input_b, output_c, m, n, k);\n    cudaDeviceSynchronize();\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include \"matmul.h\"\n\n#define TILE_SIZE 32\n#define THREADS_PER_BLOCK 16\n\n__global__ void matmul_kernel_optimized(const float* __restrict__ A,\n                                        const float* __restrict__ B,\n                                        float* __restrict__ C,\n                                        size_t M, size_t N, size_t K)\n{\n    __shared__ float As[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bs[TILE_SIZE][TILE_SIZE];\n\n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n\n    int blockRow = blockIdx.y * TILE_SIZE;\n    int blockCol = blockIdx.x * TILE_SIZE;\n\n    // Each thread computes a 2x2 sub-block of C\n    float sum00 = 0.0f, sum01 = 0.0f, sum10 = 0.0f, sum11 = 0.0f;\n\n    for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; ++t) {\n        int tiledK = t * TILE_SIZE;\n\n        {\n            int aRow0 = blockRow + ty * 2;\n            int aCol0 = tiledK + tx * 2;\n\n            // First row of A\n            if (aRow0 < M) {\n                if (aCol0 + 1 < K) {\n                    float2 aVal = *reinterpret_cast<const float2*>(\n                        &A[aRow0 * K + aCol0]\n                    );\n                    As[ty * 2][tx * 2]     = aVal.x;\n                    As[ty * 2][tx * 2 + 1] = aVal.y;\n                } else if (aCol0 < K) {\n                    As[ty * 2][tx * 2]     = A[aRow0 * K + aCol0];\n                    As[ty * 2][tx * 2 + 1] = 0.0f;\n                } else {\n                    As[ty * 2][tx * 2]     = 0.0f;\n                    As[ty * 2][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                As[ty * 2][tx * 2]     = 0.0f;\n                As[ty * 2][tx * 2 + 1] = 0.0f;\n            }\n\n            int aRow1 = aRow0 + 1;\n            if (aRow1 < M) {\n                if (aCol0 + 1 < K) {\n                    float2 aVal = *reinterpret_cast<const float2*>(\n                        &A[aRow1 * K + aCol0]\n                    );\n                    As[ty * 2 + 1][tx * 2]     = aVal.x;\n                    As[ty * 2 + 1][tx * 2 + 1] = aVal.y;\n                } else if (aCol0 < K) {\n                    As[ty * 2 + 1][tx * 2]     = A[aRow1 * K + aCol0];\n                    As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                } else {\n                    As[ty * 2 + 1][tx * 2]     = 0.0f;\n                    As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                As[ty * 2 + 1][tx * 2]     = 0.0f;\n                As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n            }\n        }\n\n        // --- Load B into shared memory ---\n        {\n            int bRow0 = tiledK + ty * 2;\n            int bCol0 = blockCol + tx * 2;\n\n            if (bRow0 < K) {\n                if (bCol0 + 1 < N) {\n                    float2 bVal = *reinterpret_cast<const float2*>(\n                        &B[bRow0 * N + bCol0]\n                    );\n                    Bs[ty * 2][tx * 2]     = bVal.x;\n                    Bs[ty * 2][tx * 2 + 1] = bVal.y;\n                } else if (bCol0 < N) {\n                    Bs[ty * 2][tx * 2]     = B[bRow0 * N + bCol0];\n                    Bs[ty * 2][tx * 2 + 1] = 0.0f;\n                } else {\n                    Bs[ty * 2][tx * 2]     = 0.0f;\n                    Bs[ty * 2][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                Bs[ty * 2][tx * 2]     = 0.0f;\n                Bs[ty * 2][tx * 2 + 1] = 0.0f;\n            }\n\n            int bRow1 = bRow0 + 1;\n            if (bRow1 < K) {\n                if (bCol0 + 1 < N) {\n                    float2 bVal = *reinterpret_cast<const float2*>(\n                        &B[bRow1 * N + bCol0]\n                    );\n                    Bs[ty * 2 + 1][tx * 2]     = bVal.x;\n                    Bs[ty * 2 + 1][tx * 2 + 1] = bVal.y;\n                } else if (bCol0 < N) {\n                    Bs[ty * 2 + 1][tx * 2]     = B[bRow1 * N + bCol0];\n                    Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                } else {\n                    Bs[ty * 2 + 1][tx * 2]     = 0.0f;\n                    Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n                }\n            } else {\n                Bs[ty * 2 + 1][tx * 2]     = 0.0f;\n                Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n            }\n        }\n\n        __syncthreads();\n\n        // --- Compute partial products ---\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            float a0 = As[ty * 2][i];\n            float a1 = As[ty * 2 + 1][i];\n            float b0 = Bs[i][tx * 2];\n            float b1 = Bs[i][tx * 2 + 1];\n\n            sum00 += a0 * b0;\n            sum01 += a0 * b1;\n            sum10 += a1 * b0;\n            sum11 += a1 * b1;\n        }\n\n        __syncthreads();\n    }\n\n    // --- Write results to C ---\n    int cRow = blockRow + ty * 2;\n    int cCol = blockCol + tx * 2;\n    if (cRow < M && cCol < N) {\n        C[cRow * N + cCol] = sum00;\n        if (cCol + 1 < N)\n            C[cRow * N + (cCol + 1)] = sum01;\n        if (cRow + 1 < M) {\n            C[(cRow + 1) * N + cCol] = sum10;\n            if (cCol + 1 < N)\n                C[(cRow + 1) * N + (cCol + 1)] = sum11;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                         size_t m, size_t n, size_t k)\n{\n    dim3 block(THREADS_PER_BLOCK, THREADS_PER_BLOCK);\n    dim3 grid((n + TILE_SIZE - 1) / TILE_SIZE,\n              (m + TILE_SIZE - 1) / TILE_SIZE);\n\n    matmul_kernel_optimized<<<grid, block>>>(input_a, input_b, output_c, m, n, k);\n    hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/100-days-cuda/Day 34/src/matmul.cu"}
{"input": "#include <cuda_runtime.h>\n\n#define TILE_SIZE 32\n#define THREADS_PER_BLOCK 16\n\n__global__ void matmul_kernel_optimized(const float* __restrict__ A, const float* __restrict__ B, float* __restrict__ C,\n                                       size_t M, size_t N, size_t K) {\n    __shared__ float As[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bs[TILE_SIZE][TILE_SIZE];\n    \n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int blockRow = blockIdx.y * TILE_SIZE;\n    int blockCol = blockIdx.x * TILE_SIZE;\n    \n    // Each thread computes 2x2 elements of C\n    float sum00 = 0.0f, sum01 = 0.0f, sum10 = 0.0f, sum11 = 0.0f;\n    \n    for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; ++t) {\n        int tiledK = t * TILE_SIZE;\n        \n        // Load 2x2 tile from A into shared memory using float2\n        int aRow = blockRow + ty * 2;\n        int aCol = tiledK + tx * 2;\n        if (aRow < M && aCol < K) {\n            float2 aVal = *reinterpret_cast<const float2*>(&A[aRow * K + aCol]);\n            As[ty * 2][tx * 2] = aVal.x;\n            As[ty * 2][tx * 2 + 1] = aVal.y;\n        } else {\n            As[ty * 2][tx * 2] = 0.0f;\n            As[ty * 2][tx * 2 + 1] = 0.0f;\n        }\n        if (aRow + 1 < M && aCol < K) {\n            float2 aVal = *reinterpret_cast<const float2*>(&A[(aRow + 1) * K + aCol]);\n            As[ty * 2 + 1][tx * 2] = aVal.x;\n            As[ty * 2 + 1][tx * 2 + 1] = aVal.y;\n        } else {\n            As[ty * 2 + 1][tx * 2] = 0.0f;\n            As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n        }\n        \n        // Load 2x2 tile from B into shared memory using float2\n        int bRow = tiledK + ty * 2;\n        int bCol = blockCol + tx * 2;\n        if (bRow < K && bCol < N) {\n            float2 bVal = *reinterpret_cast<const float2*>(&B[bRow * N + bCol]);\n            Bs[ty * 2][tx * 2] = bVal.x;\n            Bs[ty * 2][tx * 2 + 1] = bVal.y;\n        } else {\n            Bs[ty * 2][tx * 2] = 0.0f;\n            Bs[ty * 2][tx * 2 + 1] = 0.0f;\n        }\n        if (bRow + 1 < K && bCol < N) {\n            float2 bVal = *reinterpret_cast<const float2*>(&B[(bRow + 1) * N + bCol]);\n            Bs[ty * 2 + 1][tx * 2] = bVal.x;\n            Bs[ty * 2 + 1][tx * 2 + 1] = bVal.y;\n        } else {\n            Bs[ty * 2 + 1][tx * 2] = 0.0f;\n            Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n        }\n        \n        __syncthreads();\n        \n        // Compute 2x2 tile\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            float a0 = As[ty * 2][i];\n            float a1 = As[ty * 2 + 1][i];\n            float b0 = Bs[i][tx * 2];\n            float b1 = Bs[i][tx * 2 + 1];\n            \n            sum00 += a0 * b0;\n            sum01 += a0 * b1;\n            sum10 += a1 * b0;\n            sum11 += a1 * b1;\n        }\n        __syncthreads();\n    }\n    \n    // 2x2 result to global memory\n    int cRow = blockRow + ty * 2;\n    int cCol = blockCol + tx * 2;\n    if (cRow < M && cCol < N) {\n        C[cRow * N + cCol] = sum00;\n        if (cCol + 1 < N) C[cRow * N + (cCol + 1)] = sum01;\n        if (cRow + 1 < M) {\n            C[(cRow + 1) * N + cCol] = sum10;\n            if (cCol + 1 < N) C[(cRow + 1) * N + (cCol + 1)] = sum11;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                       size_t m, size_t n, size_t k) {\n    dim3 block(THREADS_PER_BLOCK, THREADS_PER_BLOCK);\n    dim3 grid((n + TILE_SIZE - 1) / TILE_SIZE,\n              (m + TILE_SIZE - 1) / TILE_SIZE);\n    \n    matmul_kernel_optimized<<<grid, block>>>(input_a, input_b, output_c, m, n, k);\n    cudaDeviceSynchronize();\n}", "output": "#include <hip/hip_runtime.h>\n\n#define TILE_SIZE 32\n#define THREADS_PER_BLOCK 16\n\n__global__ void matmul_kernel_optimized(const float* __restrict__ A, const float* __restrict__ B, float* __restrict__ C,\n                                       size_t M, size_t N, size_t K) {\n    __shared__ float As[TILE_SIZE][TILE_SIZE];\n    __shared__ float Bs[TILE_SIZE][TILE_SIZE];\n    \n    int tx = threadIdx.x;\n    int ty = threadIdx.y;\n    int blockRow = blockIdx.y * TILE_SIZE;\n    int blockCol = blockIdx.x * TILE_SIZE;\n    \n    // Each thread computes 2x2 elements of C\n    float sum00 = 0.0f, sum01 = 0.0f, sum10 = 0.0f, sum11 = 0.0f;\n    \n    for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; ++t) {\n        int tiledK = t * TILE_SIZE;\n        \n        // Load 2x2 tile from A into shared memory using float2\n        int aRow = blockRow + ty * 2;\n        int aCol = tiledK + tx * 2;\n        if (aRow < M && aCol < K) {\n            float2 aVal = *reinterpret_cast<const float2*>(&A[aRow * K + aCol]);\n            As[ty * 2][tx * 2] = aVal.x;\n            As[ty * 2][tx * 2 + 1] = aVal.y;\n        } else {\n            As[ty * 2][tx * 2] = 0.0f;\n            As[ty * 2][tx * 2 + 1] = 0.0f;\n        }\n        if (aRow + 1 < M && aCol < K) {\n            float2 aVal = *reinterpret_cast<const float2*>(&A[(aRow + 1) * K + aCol]);\n            As[ty * 2 + 1][tx * 2] = aVal.x;\n            As[ty * 2 + 1][tx * 2 + 1] = aVal.y;\n        } else {\n            As[ty * 2 + 1][tx * 2] = 0.0f;\n            As[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n        }\n        \n        // Load 2x2 tile from B into shared memory using float2\n        int bRow = tiledK + ty * 2;\n        int bCol = blockCol + tx * 2;\n        if (bRow < K && bCol < N) {\n            float2 bVal = *reinterpret_cast<const float2*>(&B[bRow * N + bCol]);\n            Bs[ty * 2][tx * 2] = bVal.x;\n            Bs[ty * 2][tx * 2 + 1] = bVal.y;\n        } else {\n            Bs[ty * 2][tx * 2] = 0.0f;\n            Bs[ty * 2][tx * 2 + 1] = 0.0f;\n        }\n        if (bRow + 1 < K && bCol < N) {\n            float2 bVal = *reinterpret_cast<const float2*>(&B[(bRow + 1) * N + bCol]);\n            Bs[ty * 2 + 1][tx * 2] = bVal.x;\n            Bs[ty * 2 + 1][tx * 2 + 1] = bVal.y;\n        } else {\n            Bs[ty * 2 + 1][tx * 2] = 0.0f;\n            Bs[ty * 2 + 1][tx * 2 + 1] = 0.0f;\n        }\n        \n        __syncthreads();\n        \n        // Compute 2x2 tile\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            float a0 = As[ty * 2][i];\n            float a1 = As[ty * 2 + 1][i];\n            float b0 = Bs[i][tx * 2];\n            float b1 = Bs[i][tx * 2 + 1];\n            \n            sum00 += a0 * b0;\n            sum01 += a0 * b1;\n            sum10 += a1 * b0;\n            sum11 += a1 * b1;\n        }\n        __syncthreads();\n    }\n    \n    // 2x2 result to global memory\n    int cRow = blockRow + ty * 2;\n    int cCol = blockCol + tx * 2;\n    if (cRow < M && cCol < N) {\n        C[cRow * N + cCol] = sum00;\n        if (cCol + 1 < N) C[cRow * N + (cCol + 1)] = sum01;\n        if (cRow + 1 < M) {\n            C[(cRow + 1) * N + cCol] = sum10;\n            if (cCol + 1 < N) C[(cRow + 1) * N + (cCol + 1)] = sum11;\n        }\n    }\n}\n\nextern \"C\" void solution(float* input_a, float* input_b, float* output_c,\n                       size_t m, size_t n, size_t k) {\n    dim3 block(THREADS_PER_BLOCK, THREADS_PER_BLOCK);\n    dim3 grid((n + TILE_SIZE - 1) / TILE_SIZE,\n              (m + TILE_SIZE - 1) / TILE_SIZE);\n    \n    matmul_kernel_optimized<<<grid, block>>>(input_a, input_b, output_c, m, n, k);\n    hipDeviceSynchronize();\n}", "source": "/kaggle/working/100-days-cuda/Day 30/shared_tiled_vectorized_matmul.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define BLOCK_SIZE 256 // Define block size as a constant\n#define CUDA_CHECK(call)                                                          \\\n    do {                                                                        \\\n        cudaError_t err = call;                                                 \\\n        if (err != cudaSuccess) {                                               \\\n            fprintf(stderr, \"CUDA error at %s:%d: %s\\n\", __FILE__, __LINE__,    \\\n                    cudaGetErrorString(err));                                    \\\n            exit(EXIT_FAILURE);                                                  \\\n        }                                                                         \\\n    } while (0)\n\n__global__ void sumReduction(int *input, int *output, int size) {\n    extern __shared__ int sharedData[];\n\n    unsigned int tid = threadIdx.x;\n    unsigned int index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sharedData[tid] = (index < size) ? input[index] : 0;\n    __syncthreads();\n\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sharedData[tid] += sharedData[tid + s];\n        }\n        __syncthreads();\n    }\n\n    if (tid == 0) {\n        output[blockIdx.x] = sharedData[0];\n    }\n}\n\nint main() {\n    const int size = 1024;\n    const int bytes = size * sizeof(int);\n\n    int h_input[size];\n    for (int i = 0; i < size; i++) {\n        h_input[i] = 1; // Initialize array with 1s\n    }\n\n    int *d_input, *d_output;\n    CUDA_CHECK(cudaMalloc(&d_input, bytes));\n\n    int blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    CUDA_CHECK(cudaMalloc(&d_output, blocks * sizeof(int))); // Correct allocation size\n\n    CUDA_CHECK(cudaMemcpy(d_input, h_input, bytes, cudaMemcpyHostToDevice));\n\n    sumReduction<<<blocks, BLOCK_SIZE, BLOCK_SIZE * sizeof(int)>>>(d_input, d_output, size);\n\n    int h_output[blocks];\n    CUDA_CHECK(cudaMemcpy(h_output, d_output, blocks * sizeof(int), cudaMemcpyDeviceToHost));\n\n    int totalSum = 0;\n    for (int i = 0; i < blocks; i++) {\n        totalSum += h_output[i];\n    }\n\n    printf(\"Sum: %d\\n\", totalSum);\n\n    CUDA_CHECK(cudaFree(d_input));\n    CUDA_CHECK(cudaFree(d_output));\n\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define BLOCK_SIZE 256 // Define block size as a constant\n#define CUDA_CHECK(call)                                                          \\\n    do {                                                                        \\\n        hipError_t err = call;                                                 \\\n        if (err != hipSuccess) {                                               \\\n            fprintf(stderr, \"CUDA error at %s:%d: %s\\n\", __FILE__, __LINE__,    \\\n                    hipGetErrorString(err));                                    \\\n            exit(EXIT_FAILURE);                                                  \\\n        }                                                                         \\\n    } while (0)\n\n__global__ void sumReduction(int *input, int *output, int size) {\n    extern __shared__ int sharedData[];\n\n    unsigned int tid = threadIdx.x;\n    unsigned int index = blockIdx.x * blockDim.x + threadIdx.x;\n\n    sharedData[tid] = (index < size) ? input[index] : 0;\n    __syncthreads();\n\n    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {\n        if (tid < s) {\n            sharedData[tid] += sharedData[tid + s];\n        }\n        __syncthreads();\n    }\n\n    if (tid == 0) {\n        output[blockIdx.x] = sharedData[0];\n    }\n}\n\nint main() {\n    const int size = 1024;\n    const int bytes = size * sizeof(int);\n\n    int h_input[size];\n    for (int i = 0; i < size; i++) {\n        h_input[i] = 1; // Initialize array with 1s\n    }\n\n    int *d_input, *d_output;\n    CUDA_CHECK(hipMalloc(&d_input, bytes));\n\n    int blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;\n    CUDA_CHECK(hipMalloc(&d_output, blocks * sizeof(int))); // Correct allocation size\n\n    CUDA_CHECK(hipMemcpy(d_input, h_input, bytes, hipMemcpyHostToDevice));\n\n    sumReduction<<<blocks, BLOCK_SIZE, BLOCK_SIZE * sizeof(int)>>>(d_input, d_output, size);\n\n    int h_output[blocks];\n    CUDA_CHECK(hipMemcpy(h_output, d_output, blocks * sizeof(int), hipMemcpyDeviceToHost));\n\n    int totalSum = 0;\n    for (int i = 0; i < blocks; i++) {\n        totalSum += h_output[i];\n    }\n\n    printf(\"Sum: %d\\n\", totalSum);\n\n    CUDA_CHECK(hipFree(d_input));\n    CUDA_CHECK(hipFree(d_output));\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 19/sum_reduction.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n#include <cmath>\n\n#define N (1 << 26)        // 67 million elements\n#define THREADS_PER_BLOCK 256\n#define ITERATIONS 100     // Run multiple iterations for averaging\n\n__global__ void sumReductionOptimized(float *input, float *output, int n) {\n    __shared__ float shared[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int idx = blockIdx.x * blockDim.x * 2 + threadIdx.x;\n    float sum = 0.0f;\n    \n    if (idx < n)\n        sum += input[idx];\n    if (idx + blockDim.x < n)\n        sum += input[idx + blockDim.x];\n\n    shared[tid] = sum;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride)\n            shared[tid] += shared[tid + stride];\n        __syncthreads();\n    }\n    \n    if (tid == 0)\n        output[blockIdx.x] = shared[0];\n}\n\nfloat finalReductionOnCPU(float *d_output, int size) {\n    float *h_partial_sums = new float[size];\n    cudaMemcpy(h_partial_sums, d_output, size * sizeof(float), cudaMemcpyDeviceToHost);\n    \n    float total_sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        total_sum += h_partial_sums[i];\n    }\n    delete[] h_partial_sums;\n    return total_sum;\n}\n\nint main() {\n    // Allocate and initialize host memory\n    float *h_input = new float[N];\n    for (int i = 0; i < N; ++i) {\n        h_input[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n    \n    // Allocate device memory\n    float *d_input, *d_output;\n    int blocks = (N + THREADS_PER_BLOCK * 2 - 1) / (THREADS_PER_BLOCK * 2);\n    cudaMalloc(&d_input, N * sizeof(float));\n    cudaMalloc(&d_output, blocks * sizeof(float));\n    cudaMemcpy(d_input, h_input, N * sizeof(float), cudaMemcpyHostToDevice);\n    \n    // Warm-up for the custom kernel\n    sumReductionOptimized<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output, N);\n    cudaDeviceSynchronize();\n\n    // Create CUDA events for timing\n    cudaEvent_t startEvent, stopEvent;\n    cudaEventCreate(&startEvent);\n    cudaEventCreate(&stopEvent);\n    float total_time_custom = 0.0f;\n    \n    // Benchmark custom kernel over many iterations\n    for (int i = 0; i < ITERATIONS; i++) {\n        cudaEventRecord(startEvent, 0);\n        sumReductionOptimized<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output, N);\n        cudaEventRecord(stopEvent, 0);\n        cudaEventSynchronize(stopEvent);\n        float ms;\n        cudaEventElapsedTime(&ms, startEvent, stopEvent);\n        total_time_custom += ms;\n    }\n    float avg_time_custom = total_time_custom / ITERATIONS;  // in ms\n    float custom_result = finalReductionOnCPU(d_output, blocks);\n\n    // Setup cuBLAS and warm it up\n    cublasHandle_t handle;\n    cublasCreate(&handle);\n    float dummy;\n    cublasSasum(handle, N, d_input, 1, &dummy);\n    cudaDeviceSynchronize();\n\n    float total_time_cublas = 0.0f;\n    float cublas_result = 0.0f;\n    // Benchmark cuBLAS over many iterations\n    for (int i = 0; i < ITERATIONS; i++) {\n        cudaEventRecord(startEvent, 0);\n        cublasSasum(handle, N, d_input, 1, &cublas_result);\n        cudaDeviceSynchronize();\n        cudaEventRecord(stopEvent, 0);\n        cudaEventSynchronize(stopEvent);\n        float ms;\n        cudaEventElapsedTime(&ms, startEvent, stopEvent);\n        total_time_cublas += ms;\n    }\n    float avg_time_cublas = total_time_cublas / ITERATIONS;  // in ms\n    cublasDestroy(handle);\n    \n    // Clean up CUDA events\n    cudaEventDestroy(startEvent);\n    cudaEventDestroy(stopEvent);\n    \n    // Convert times to seconds\n    double seconds_custom = avg_time_custom / 1000.0;\n    double seconds_cublas = avg_time_cublas / 1000.0;\n    \n    // Calculate GFLOPS (for reduction, we consider N adds as N floating-point operations)\n    double gflops_custom = (N / seconds_custom) / 1e9;\n    double gflops_cublas = (N / seconds_cublas) / 1e9;\n    \n    std::cout << \"Custom Sum Reduction Result: \" << custom_result << std::endl;\n    std::cout << \"Custom Sum Reduction GFLOPS: \" << gflops_custom << std::endl;\n    std::cout << \"cuBLAS Sum Reduction Result: \" << cublas_result << std::endl;\n    std::cout << \"cuBLAS Sum Reduction GFLOPS: \" << gflops_cublas << std::endl;\n    \n    // Compare results (using relative tolerance)\n    if (fabs(custom_result - cublas_result) / fabs(cublas_result) < 1e-3) {\n        double speedup_percentage = (gflops_custom / gflops_cublas) * 100;\n        std::cout << \"Performance Comparison: \" << std::endl;\n        std::cout << \"cuBLAS Speed: 100%\" << std::endl;\n        std::cout << \"Custom Reduction Speed: \" << speedup_percentage << \"%\" << std::endl;\n    } else {\n        std::cout << \"Error: Results do not match!\" << std::endl;\n    }\n    \n    // Free memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n    delete[] h_input;\n    \n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <hipblas.h>\n#include <cmath>\n\n#define N (1 << 26)        // 67 million elements\n#define THREADS_PER_BLOCK 256\n#define ITERATIONS 100     // Run multiple iterations for averaging\n\n__global__ void sumReductionOptimized(float *input, float *output, int n) {\n    __shared__ float shared[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int idx = blockIdx.x * blockDim.x * 2 + threadIdx.x;\n    float sum = 0.0f;\n    \n    if (idx < n)\n        sum += input[idx];\n    if (idx + blockDim.x < n)\n        sum += input[idx + blockDim.x];\n\n    shared[tid] = sum;\n    __syncthreads();\n\n    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {\n        if (tid < stride)\n            shared[tid] += shared[tid + stride];\n        __syncthreads();\n    }\n    \n    if (tid == 0)\n        output[blockIdx.x] = shared[0];\n}\n\nfloat finalReductionOnCPU(float *d_output, int size) {\n    float *h_partial_sums = new float[size];\n    hipMemcpy(h_partial_sums, d_output, size * sizeof(float), hipMemcpyDeviceToHost);\n    \n    float total_sum = 0.0f;\n    for (int i = 0; i < size; i++) {\n        total_sum += h_partial_sums[i];\n    }\n    delete[] h_partial_sums;\n    return total_sum;\n}\n\nint main() {\n    // Allocate and initialize host memory\n    float *h_input = new float[N];\n    for (int i = 0; i < N; ++i) {\n        h_input[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n    \n    // Allocate device memory\n    float *d_input, *d_output;\n    int blocks = (N + THREADS_PER_BLOCK * 2 - 1) / (THREADS_PER_BLOCK * 2);\n    hipMalloc(&d_input, N * sizeof(float));\n    hipMalloc(&d_output, blocks * sizeof(float));\n    hipMemcpy(d_input, h_input, N * sizeof(float), hipMemcpyHostToDevice);\n    \n    // Warm-up for the custom kernel\n    sumReductionOptimized<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output, N);\n    hipDeviceSynchronize();\n\n    // Create CUDA events for timing\n    hipEvent_t startEvent, stopEvent;\n    hipEventCreate(&startEvent);\n    hipEventCreate(&stopEvent);\n    float total_time_custom = 0.0f;\n    \n    // Benchmark custom kernel over many iterations\n    for (int i = 0; i < ITERATIONS; i++) {\n        hipEventRecord(startEvent, 0);\n        sumReductionOptimized<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output, N);\n        hipEventRecord(stopEvent, 0);\n        hipEventSynchronize(stopEvent);\n        float ms;\n        hipEventElapsedTime(&ms, startEvent, stopEvent);\n        total_time_custom += ms;\n    }\n    float avg_time_custom = total_time_custom / ITERATIONS;  // in ms\n    float custom_result = finalReductionOnCPU(d_output, blocks);\n\n    // Setup cuBLAS and warm it up\n    hipblasHandle_t handle;\n    hipblasCreate(&handle);\n    float dummy;\n    hipblasSasum(handle, N, d_input, 1, &dummy);\n    hipDeviceSynchronize();\n\n    float total_time_cublas = 0.0f;\n    float cublas_result = 0.0f;\n    // Benchmark cuBLAS over many iterations\n    for (int i = 0; i < ITERATIONS; i++) {\n        hipEventRecord(startEvent, 0);\n        hipblasSasum(handle, N, d_input, 1, &cublas_result);\n        hipDeviceSynchronize();\n        hipEventRecord(stopEvent, 0);\n        hipEventSynchronize(stopEvent);\n        float ms;\n        hipEventElapsedTime(&ms, startEvent, stopEvent);\n        total_time_cublas += ms;\n    }\n    float avg_time_cublas = total_time_cublas / ITERATIONS;  // in ms\n    hipblasDestroy(handle);\n    \n    // Clean up CUDA events\n    hipEventDestroy(startEvent);\n    hipEventDestroy(stopEvent);\n    \n    // Convert times to seconds\n    double seconds_custom = avg_time_custom / 1000.0;\n    double seconds_cublas = avg_time_cublas / 1000.0;\n    \n    // Calculate GFLOPS (for reduction, we consider N adds as N floating-point operations)\n    double gflops_custom = (N / seconds_custom) / 1e9;\n    double gflops_cublas = (N / seconds_cublas) / 1e9;\n    \n    std::cout << \"Custom Sum Reduction Result: \" << custom_result << std::endl;\n    std::cout << \"Custom Sum Reduction GFLOPS: \" << gflops_custom << std::endl;\n    std::cout << \"cuBLAS Sum Reduction Result: \" << cublas_result << std::endl;\n    std::cout << \"cuBLAS Sum Reduction GFLOPS: \" << gflops_cublas << std::endl;\n    \n    // Compare results (using relative tolerance)\n    if (fabs(custom_result - cublas_result) / fabs(cublas_result) < 1e-3) {\n        double speedup_percentage = (gflops_custom / gflops_cublas) * 100;\n        std::cout << \"Performance Comparison: \" << std::endl;\n        std::cout << \"cuBLAS Speed: 100%\" << std::endl;\n        std::cout << \"Custom Reduction Speed: \" << speedup_percentage << \"%\" << std::endl;\n    } else {\n        std::cout << \"Error: Results do not match!\" << std::endl;\n    }\n    \n    // Free memory\n    hipFree(d_input);\n    hipFree(d_output);\n    delete[] h_input;\n    \n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 19/SRO.cu"}
{"input": "#include <stdio.h>\n\n__global__ void add(int *a, int *b, int *c, int n) {\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < n) {\n        c[index] = a[index] + b[index];\n    }\n}\n\nint main() {\n    int n = 10;\n    int a[10], b[10], c[10];\n    int *d_a, *d_b, *d_c;\n\n    for (int i = 0; i < n; i++) {\n        a[i] = i;\n        b[i] = i * 2;\n    }\n\n    cudaMalloc((void **)&d_a, n * sizeof(int));\n    cudaMalloc((void **)&d_b, n * sizeof(int));\n    cudaMalloc((void **)&d_c, n * sizeof(int));\n\n    cudaMemcpy(d_a, a, n * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, n * sizeof(int), cudaMemcpyHostToDevice);\n\n    add<<<1, n>>>(d_a, d_b, d_c, n);\n\n    cudaMemcpy(c, d_c, n * sizeof(int), cudaMemcpyDeviceToHost);\n\n    for (int i = 0; i < n; i++) {\n        printf(\"%d + %d = %d\\n\", a[i], b[i], c[i]);\n    }\n\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n__global__ void add(int *a, int *b, int *c, int n) {\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < n) {\n        c[index] = a[index] + b[index];\n    }\n}\n\nint main() {\n    int n = 10;\n    int a[10], b[10], c[10];\n    int *d_a, *d_b, *d_c;\n\n    for (int i = 0; i < n; i++) {\n        a[i] = i;\n        b[i] = i * 2;\n    }\n\n    hipMalloc((void **)&d_a, n * sizeof(int));\n    hipMalloc((void **)&d_b, n * sizeof(int));\n    hipMalloc((void **)&d_c, n * sizeof(int));\n\n    hipMemcpy(d_a, a, n * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, n * sizeof(int), hipMemcpyHostToDevice);\n\n    add<<<1, n>>>(d_a, d_b, d_c, n);\n\n    hipMemcpy(c, d_c, n * sizeof(int), hipMemcpyDeviceToHost);\n\n    for (int i = 0; i < n; i++) {\n        printf(\"%d + %d = %d\\n\", a[i], b[i], c[i]);\n    }\n\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 38/simple_kernel.cu"}
{"input": "// fp8_double_buffer_cuda.cu\n#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <iostream>\n#include <chrono>\n#include <random>\n\n#define TILE_M 128\n#define TILE_N 128\n#define TILE_K 32\n#define VECTOR_SIZE 4\n\n// Device-side version (GPU)\n__device__ float fp8_dequantize(uint8_t val, float scale) {\n    return (static_cast<float>(val) / 127.0f - 1.0f) * scale;\n}\n\n__device__ uint8_t fp8_quantize(float val, float scale) {\n    float clamped = fminf(fmaxf(val / scale, -1.0f), 1.0f);\n    return static_cast<uint8_t>((clamped + 1.0f) * 127.0f);\n}\n\n// Host-side version (CPU)\nfloat fp8_quantize_host(float val, float scale) {\n    float clamped = std::min(std::max(val / scale, -1.0f), 1.0f);\n    return static_cast<uint8_t>((clamped + 1.0f) * 127.0f);\n}\n\n__global__ void fp8_gemm_kernel(const uint8_t *A, const uint8_t *B, const float *A_scale, const float *B_scale, float *C, int M, int N, int K) {\n    __shared__ uint8_t As[2][TILE_K][TILE_M];\n    __shared__ uint8_t Bs[2][TILE_K][TILE_N];\n    __shared__ float AScales[2][TILE_M];\n    __shared__ float BScales[2][TILE_N];\n\n    int row = blockIdx.y * TILE_M + threadIdx.y;\n    int col = blockIdx.x * TILE_N + threadIdx.x;\n\n    float acc = 0.0f;\n    int cur = 0, nxt = 1;\n\n    for (int t = 0; t < K; t += TILE_K) {\n        if (row < M && (t + threadIdx.x) < K)\n            As[cur][threadIdx.x][threadIdx.y] = A[row * K + (t + threadIdx.x)];\n        if (col < N && (t + threadIdx.y) < K)\n            Bs[cur][threadIdx.y][threadIdx.x] = B[(t + threadIdx.y) * N + col];\n\n        if (threadIdx.y == 0 && (blockIdx.y * TILE_M + threadIdx.x) < M)\n            AScales[cur][threadIdx.x] = A_scale[blockIdx.y * TILE_M + threadIdx.x];\n        if (threadIdx.x == 0 && (blockIdx.x * TILE_N + threadIdx.y) < N)\n            BScales[cur][threadIdx.y] = B_scale[blockIdx.x * TILE_N + threadIdx.y];\n\n        __syncthreads();\n\n        for (int k = 0; k < TILE_K; ++k) {\n            float a_val = fp8_dequantize(As[cur][k][threadIdx.y], AScales[cur][threadIdx.y]);\n            float b_val = fp8_dequantize(Bs[cur][k][threadIdx.x], BScales[cur][threadIdx.x]);\n            acc += a_val * b_val;\n        }\n\n        __syncthreads();\n        cur ^= 1; nxt ^= 1;\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = acc;\n}\n\nint main(int argc, char **argv) {\n    if (argc != 5) {\n        std::cerr << \"Usage: \" << argv[0] << \" M N K seed\\n\";\n        return 1;\n    }\n    int M = std::stoi(argv[1]);\n    int N = std::stoi(argv[2]);\n    int K = std::stoi(argv[3]);\n    int seed = std::stoi(argv[4]);\n\n    size_t sizeA = size_t(M) * K;\n    size_t sizeB = size_t(K) * N;\n    size_t sizeC = size_t(M) * N;\n\n    uint8_t *h_A = new uint8_t[sizeA];\n    uint8_t *h_B = new uint8_t[sizeB];\n    float *h_A_scale = new float[M];\n    float *h_B_scale = new float[N];\n    float *h_C = new float[sizeC];\n\n    std::mt19937 rng(seed);\n    std::uniform_real_distribution<float> dist(-1.0f, 1.0f);\n\n    for (size_t i = 0; i < sizeA; ++i) h_A[i] = fp8_quantize_host(dist(rng), 1.0f);\n    for (size_t i = 0; i < sizeB; ++i) h_B[i] = fp8_quantize_host(dist(rng), 1.0f);\n    \n    for (int i = 0; i < M; ++i) h_A_scale[i] = 1.0f;\n    for (int i = 0; i < N; ++i) h_B_scale[i] = 1.0f;\n\n    uint8_t *d_A, *d_B;\n    float *d_A_scale, *d_B_scale, *d_C;\n    cudaMalloc(&d_A, sizeA);\n    cudaMalloc(&d_B, sizeB);\n    cudaMalloc(&d_A_scale, M * sizeof(float));\n    cudaMalloc(&d_B_scale, N * sizeof(float));\n    cudaMalloc(&d_C, sizeC * sizeof(float));\n\n    cudaMemcpy(d_A, h_A, sizeA, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, sizeB, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_A_scale, h_A_scale, M * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B_scale, h_B_scale, N * sizeof(float), cudaMemcpyHostToDevice);\n\n    dim3 threads(16, 16);\n    dim3 blocks((N + TILE_N - 1) / TILE_N, (M + TILE_M - 1) / TILE_M);\n\n    auto t0 = std::chrono::high_resolution_clock::now();\n    fp8_gemm_kernel<<<blocks, threads>>>(d_A, d_B, d_A_scale, d_B_scale, d_C, M, N, K);\n    cudaDeviceSynchronize();\n    auto t1 = std::chrono::high_resolution_clock::now();\n\n    double micros = std::chrono::duration<double, std::micro>(t1 - t0).count();\n    std::cout << \"M=\" << M << \" N=\" << N << \" K=\" << K << \" seed=\" << seed << \" -> \" << micros << \" us\\n\";\n\n    cudaMemcpy(h_C, d_C, sizeC * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_A); cudaFree(d_B); cudaFree(d_A_scale); cudaFree(d_B_scale); cudaFree(d_C);\n    delete[] h_A; delete[] h_B; delete[] h_A_scale; delete[] h_B_scale; delete[] h_C;\n    return 0;\n}\n", "output": "// fp8_double_buffer_cuda.cu\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <iostream>\n#include <chrono>\n#include <random>\n\n#define TILE_M 128\n#define TILE_N 128\n#define TILE_K 32\n#define VECTOR_SIZE 4\n\n// Device-side version (GPU)\n__device__ float fp8_dequantize(uint8_t val, float scale) {\n    return (static_cast<float>(val) / 127.0f - 1.0f) * scale;\n}\n\n__device__ uint8_t fp8_quantize(float val, float scale) {\n    float clamped = fminf(fmaxf(val / scale, -1.0f), 1.0f);\n    return static_cast<uint8_t>((clamped + 1.0f) * 127.0f);\n}\n\n// Host-side version (CPU)\nfloat fp8_quantize_host(float val, float scale) {\n    float clamped = std::min(std::max(val / scale, -1.0f), 1.0f);\n    return static_cast<uint8_t>((clamped + 1.0f) * 127.0f);\n}\n\n__global__ void fp8_gemm_kernel(const uint8_t *A, const uint8_t *B, const float *A_scale, const float *B_scale, float *C, int M, int N, int K) {\n    __shared__ uint8_t As[2][TILE_K][TILE_M];\n    __shared__ uint8_t Bs[2][TILE_K][TILE_N];\n    __shared__ float AScales[2][TILE_M];\n    __shared__ float BScales[2][TILE_N];\n\n    int row = blockIdx.y * TILE_M + threadIdx.y;\n    int col = blockIdx.x * TILE_N + threadIdx.x;\n\n    float acc = 0.0f;\n    int cur = 0, nxt = 1;\n\n    for (int t = 0; t < K; t += TILE_K) {\n        if (row < M && (t + threadIdx.x) < K)\n            As[cur][threadIdx.x][threadIdx.y] = A[row * K + (t + threadIdx.x)];\n        if (col < N && (t + threadIdx.y) < K)\n            Bs[cur][threadIdx.y][threadIdx.x] = B[(t + threadIdx.y) * N + col];\n\n        if (threadIdx.y == 0 && (blockIdx.y * TILE_M + threadIdx.x) < M)\n            AScales[cur][threadIdx.x] = A_scale[blockIdx.y * TILE_M + threadIdx.x];\n        if (threadIdx.x == 0 && (blockIdx.x * TILE_N + threadIdx.y) < N)\n            BScales[cur][threadIdx.y] = B_scale[blockIdx.x * TILE_N + threadIdx.y];\n\n        __syncthreads();\n\n        for (int k = 0; k < TILE_K; ++k) {\n            float a_val = fp8_dequantize(As[cur][k][threadIdx.y], AScales[cur][threadIdx.y]);\n            float b_val = fp8_dequantize(Bs[cur][k][threadIdx.x], BScales[cur][threadIdx.x]);\n            acc += a_val * b_val;\n        }\n\n        __syncthreads();\n        cur ^= 1; nxt ^= 1;\n    }\n\n    if (row < M && col < N)\n        C[row * N + col] = acc;\n}\n\nint main(int argc, char **argv) {\n    if (argc != 5) {\n        std::cerr << \"Usage: \" << argv[0] << \" M N K seed\\n\";\n        return 1;\n    }\n    int M = std::stoi(argv[1]);\n    int N = std::stoi(argv[2]);\n    int K = std::stoi(argv[3]);\n    int seed = std::stoi(argv[4]);\n\n    size_t sizeA = size_t(M) * K;\n    size_t sizeB = size_t(K) * N;\n    size_t sizeC = size_t(M) * N;\n\n    uint8_t *h_A = new uint8_t[sizeA];\n    uint8_t *h_B = new uint8_t[sizeB];\n    float *h_A_scale = new float[M];\n    float *h_B_scale = new float[N];\n    float *h_C = new float[sizeC];\n\n    std::mt19937 rng(seed);\n    std::uniform_real_distribution<float> dist(-1.0f, 1.0f);\n\n    for (size_t i = 0; i < sizeA; ++i) h_A[i] = fp8_quantize_host(dist(rng), 1.0f);\n    for (size_t i = 0; i < sizeB; ++i) h_B[i] = fp8_quantize_host(dist(rng), 1.0f);\n    \n    for (int i = 0; i < M; ++i) h_A_scale[i] = 1.0f;\n    for (int i = 0; i < N; ++i) h_B_scale[i] = 1.0f;\n\n    uint8_t *d_A, *d_B;\n    float *d_A_scale, *d_B_scale, *d_C;\n    hipMalloc(&d_A, sizeA);\n    hipMalloc(&d_B, sizeB);\n    hipMalloc(&d_A_scale, M * sizeof(float));\n    hipMalloc(&d_B_scale, N * sizeof(float));\n    hipMalloc(&d_C, sizeC * sizeof(float));\n\n    hipMemcpy(d_A, h_A, sizeA, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, sizeB, hipMemcpyHostToDevice);\n    hipMemcpy(d_A_scale, h_A_scale, M * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_B_scale, h_B_scale, N * sizeof(float), hipMemcpyHostToDevice);\n\n    dim3 threads(16, 16);\n    dim3 blocks((N + TILE_N - 1) / TILE_N, (M + TILE_M - 1) / TILE_M);\n\n    auto t0 = std::chrono::high_resolution_clock::now();\n    fp8_gemm_kernel<<<blocks, threads>>>(d_A, d_B, d_A_scale, d_B_scale, d_C, M, N, K);\n    hipDeviceSynchronize();\n    auto t1 = std::chrono::high_resolution_clock::now();\n\n    double micros = std::chrono::duration<double, std::micro>(t1 - t0).count();\n    std::cout << \"M=\" << M << \" N=\" << N << \" K=\" << K << \" seed=\" << seed << \" -> \" << micros << \" us\\n\";\n\n    hipMemcpy(h_C, d_C, sizeC * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipFree(d_A); hipFree(d_B); hipFree(d_A_scale); hipFree(d_B_scale); hipFree(d_C);\n    delete[] h_A; delete[] h_B; delete[] h_A_scale; delete[] h_B_scale; delete[] h_C;\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 83/fp8_double_buffer_cuda.cu"}
{"input": "// bench_fp16_cuda.cu\n#include <cuda_fp16.h>\n#include <cuda_runtime.h>\n#include <iostream>\n#include <chrono>\n#include <random>\n\n#define TILE_SIZE 16\n\n__global__ void gemm_fp16_kernel(__half *A, __half *B, __half *C, int M, int N, int K) {\n    __shared__ __half As[2][TILE_SIZE][TILE_SIZE];\n    __shared__ __half Bs[2][TILE_SIZE][TILE_SIZE];\n\n    int row = blockIdx.y * TILE_SIZE + threadIdx.y;\n    int col = blockIdx.x * TILE_SIZE + threadIdx.x;\n\n    __half accum = __float2half(0.0f);\n    int numTiles = (K + TILE_SIZE - 1) / TILE_SIZE;\n    int cur = 0, nxt = 1;\n\n    // preload first tile\n    int t = 0;\n    int aCol = t * TILE_SIZE + threadIdx.x;\n    int bRow = t * TILE_SIZE + threadIdx.y;\n    As[cur][threadIdx.y][threadIdx.x] = (row < M && aCol < K) ? A[row*K + aCol] : __float2half(0.0f);\n    Bs[cur][threadIdx.y][threadIdx.x] = (bRow < K && col < N) ? B[bRow*N + col] : __float2half(0.0f);\n    __syncthreads();\n\n    for (t = 0; t < numTiles; ++t) {\n        if (t + 1 < numTiles) {\n            int naCol = (t+1)*TILE_SIZE + threadIdx.x;\n            int nbRow = (t+1)*TILE_SIZE + threadIdx.y;\n            As[nxt][threadIdx.y][threadIdx.x] = (row < M && naCol < K) ? A[row*K + naCol] : __float2half(0.0f);\n            Bs[nxt][threadIdx.y][threadIdx.x] = (nbRow < K && col < N) ? B[nbRow*N + col] : __float2half(0.0f);\n        }\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            accum = __hadd(accum, __hmul(As[cur][threadIdx.y][i], Bs[cur][i][threadIdx.x]));\n        }\n        __syncthreads();\n        cur ^= 1; nxt ^= 1;\n    }\n\n    if (row < M && col < N) {\n        C[row*N + col] = accum;\n    }\n}\n\ninline void check_cuda(cudaError_t err, const char* action) {\n    if (err != cudaSuccess) {\n        std::cerr << \"CUDA error during \" << action << \": \" << cudaGetErrorString(err) << std::endl;\n        exit(1);\n    }\n}\n\nint main(int argc, char** argv) {\n    if (argc != 5) {\n        std::cerr << \"Usage: \" << argv[0] << \" M N K seed\\n\";\n        return 1;\n    }\n    int M = std::stoi(argv[1]);\n    int N = std::stoi(argv[2]);\n    int K = std::stoi(argv[3]);\n    int seed = std::stoi(argv[4]);\n\n    size_t sizeA = size_t(M)*K;\n    size_t sizeB = size_t(K)*N;\n    size_t sizeC = size_t(M)*N;\n\n    __half *h_A = new __half[sizeA];\n    __half *h_B = new __half[sizeB];\n    __half *h_C = new __half[sizeC];\n\n    std::mt19937 rng(seed);\n    std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n    for (size_t i = 0; i < sizeA; ++i) h_A[i] = __float2half(dist(rng));\n    for (size_t i = 0; i < sizeB; ++i) h_B[i] = __float2half(dist(rng));\n\n    __half *d_A, *d_B, *d_C;\n    check_cuda(cudaMalloc(&d_A, sizeA * sizeof(__half)), \"alloc d_A\");\n    check_cuda(cudaMalloc(&d_B, sizeB * sizeof(__half)), \"alloc d_B\");\n    check_cuda(cudaMalloc(&d_C, sizeC * sizeof(__half)), \"alloc d_C\");\n\n    check_cuda(cudaMemcpy(d_A, h_A, sizeA*sizeof(__half), cudaMemcpyHostToDevice), \"copy A\");\n    check_cuda(cudaMemcpy(d_B, h_B, sizeB*sizeof(__half), cudaMemcpyHostToDevice), \"copy B\");\n\n    dim3 threads(TILE_SIZE, TILE_SIZE);\n    dim3 blocks((N+TILE_SIZE-1)/TILE_SIZE, (M+TILE_SIZE-1)/TILE_SIZE);\n\n    // Warm-up\n    gemm_fp16_kernel<<<blocks, threads>>>(d_A, d_B, d_C, M, N, K);\n    check_cuda(cudaDeviceSynchronize(), \"warmup sync\");\n\n    // Timed run\n    auto t0 = std::chrono::high_resolution_clock::now();\n    gemm_fp16_kernel<<<blocks, threads>>>(d_A, d_B, d_C, M, N, K);\n    check_cuda(cudaDeviceSynchronize(), \"timed sync\");\n    auto t1 = std::chrono::high_resolution_clock::now();\n\n    double micros = std::chrono::duration<double, std::micro>(t1 - t0).count();\n    std::cout << \"M=\" << M << \" N=\" << N << \" K=\" << K\n              << \" seed=\" << seed << \" -> \" << micros << \" us\\n\";\n\n    check_cuda(cudaMemcpy(h_C, d_C, sizeC*sizeof(__half), cudaMemcpyDeviceToHost), \"copy C back\");\n    check_cuda(cudaFree(d_A), \"free d_A\");\n    check_cuda(cudaFree(d_B), \"free d_B\");\n    check_cuda(cudaFree(d_C), \"free d_C\");\n    delete[] h_A; delete[] h_B; delete[] h_C;\n    return 0;\n}\n\n", "output": "// bench_fp16_cuda.cu\n#include <hip/hip_fp16.h>\n#include <hip/hip_runtime.h>\n#include <iostream>\n#include <chrono>\n#include <random>\n\n#define TILE_SIZE 16\n\n__global__ void gemm_fp16_kernel(__half *A, __half *B, __half *C, int M, int N, int K) {\n    __shared__ __half As[2][TILE_SIZE][TILE_SIZE];\n    __shared__ __half Bs[2][TILE_SIZE][TILE_SIZE];\n\n    int row = blockIdx.y * TILE_SIZE + threadIdx.y;\n    int col = blockIdx.x * TILE_SIZE + threadIdx.x;\n\n    __half accum = __float2half(0.0f);\n    int numTiles = (K + TILE_SIZE - 1) / TILE_SIZE;\n    int cur = 0, nxt = 1;\n\n    // preload first tile\n    int t = 0;\n    int aCol = t * TILE_SIZE + threadIdx.x;\n    int bRow = t * TILE_SIZE + threadIdx.y;\n    As[cur][threadIdx.y][threadIdx.x] = (row < M && aCol < K) ? A[row*K + aCol] : __float2half(0.0f);\n    Bs[cur][threadIdx.y][threadIdx.x] = (bRow < K && col < N) ? B[bRow*N + col] : __float2half(0.0f);\n    __syncthreads();\n\n    for (t = 0; t < numTiles; ++t) {\n        if (t + 1 < numTiles) {\n            int naCol = (t+1)*TILE_SIZE + threadIdx.x;\n            int nbRow = (t+1)*TILE_SIZE + threadIdx.y;\n            As[nxt][threadIdx.y][threadIdx.x] = (row < M && naCol < K) ? A[row*K + naCol] : __float2half(0.0f);\n            Bs[nxt][threadIdx.y][threadIdx.x] = (nbRow < K && col < N) ? B[nbRow*N + col] : __float2half(0.0f);\n        }\n        __syncthreads();\n\n        for (int i = 0; i < TILE_SIZE; ++i) {\n            accum = __hadd(accum, __hmul(As[cur][threadIdx.y][i], Bs[cur][i][threadIdx.x]));\n        }\n        __syncthreads();\n        cur ^= 1; nxt ^= 1;\n    }\n\n    if (row < M && col < N) {\n        C[row*N + col] = accum;\n    }\n}\n\ninline void check_cuda(hipError_t err, const char* action) {\n    if (err != hipSuccess) {\n        std::cerr << \"CUDA error during \" << action << \": \" << hipGetErrorString(err) << std::endl;\n        exit(1);\n    }\n}\n\nint main(int argc, char** argv) {\n    if (argc != 5) {\n        std::cerr << \"Usage: \" << argv[0] << \" M N K seed\\n\";\n        return 1;\n    }\n    int M = std::stoi(argv[1]);\n    int N = std::stoi(argv[2]);\n    int K = std::stoi(argv[3]);\n    int seed = std::stoi(argv[4]);\n\n    size_t sizeA = size_t(M)*K;\n    size_t sizeB = size_t(K)*N;\n    size_t sizeC = size_t(M)*N;\n\n    __half *h_A = new __half[sizeA];\n    __half *h_B = new __half[sizeB];\n    __half *h_C = new __half[sizeC];\n\n    std::mt19937 rng(seed);\n    std::uniform_real_distribution<float> dist(0.0f, 1.0f);\n    for (size_t i = 0; i < sizeA; ++i) h_A[i] = __float2half(dist(rng));\n    for (size_t i = 0; i < sizeB; ++i) h_B[i] = __float2half(dist(rng));\n\n    __half *d_A, *d_B, *d_C;\n    check_cuda(hipMalloc(&d_A, sizeA * sizeof(__half)), \"alloc d_A\");\n    check_cuda(hipMalloc(&d_B, sizeB * sizeof(__half)), \"alloc d_B\");\n    check_cuda(hipMalloc(&d_C, sizeC * sizeof(__half)), \"alloc d_C\");\n\n    check_cuda(hipMemcpy(d_A, h_A, sizeA*sizeof(__half), hipMemcpyHostToDevice), \"copy A\");\n    check_cuda(hipMemcpy(d_B, h_B, sizeB*sizeof(__half), hipMemcpyHostToDevice), \"copy B\");\n\n    dim3 threads(TILE_SIZE, TILE_SIZE);\n    dim3 blocks((N+TILE_SIZE-1)/TILE_SIZE, (M+TILE_SIZE-1)/TILE_SIZE);\n\n    // Warm-up\n    gemm_fp16_kernel<<<blocks, threads>>>(d_A, d_B, d_C, M, N, K);\n    check_cuda(hipDeviceSynchronize(), \"warmup sync\");\n\n    // Timed run\n    auto t0 = std::chrono::high_resolution_clock::now();\n    gemm_fp16_kernel<<<blocks, threads>>>(d_A, d_B, d_C, M, N, K);\n    check_cuda(hipDeviceSynchronize(), \"timed sync\");\n    auto t1 = std::chrono::high_resolution_clock::now();\n\n    double micros = std::chrono::duration<double, std::micro>(t1 - t0).count();\n    std::cout << \"M=\" << M << \" N=\" << N << \" K=\" << K\n              << \" seed=\" << seed << \" -> \" << micros << \" us\\n\";\n\n    check_cuda(hipMemcpy(h_C, d_C, sizeC*sizeof(__half), hipMemcpyDeviceToHost), \"copy C back\");\n    check_cuda(hipFree(d_A), \"free d_A\");\n    check_cuda(hipFree(d_B), \"free d_B\");\n    check_cuda(hipFree(d_C), \"free d_C\");\n    delete[] h_A; delete[] h_B; delete[] h_C;\n    return 0;\n}\n\n", "source": "/kaggle/working/100-days-cuda/Day 83/bench_fp16_cuda.cu"}
{"input": "#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <cstdint>  // \u2705 Fix for uintptr_t\n#include <cstdio>   // \u2705 Fix for printf\n\n// \u2705 Use CUDA built-in function instead of inline PTX\n__device__ inline half2 my_hadd2(half2 a, half2 b) {\n    return __hadd2(a, b);  // \u2705 Built-in FP16 vectorized addition\n}\n\n// \u2705 Optimized vector addition kernel using FP16 (half2)\n__global__ void vector_add_half2(const half2* __restrict__ d_input1,\n                                 const half2* __restrict__ d_input2,\n                                 half2* __restrict__ d_output,\n                                 size_t n_half2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_half2) {\n        half2 a = __ldg(&d_input1[idx]);  // \u2705 Cached read\n        half2 b = __ldg(&d_input2[idx]);\n\n        half2 c = my_hadd2(a, b);\n        d_output[idx] = c;\n    }\n}\n\n// \u2705 Optimized `vector_add_vec2` kernel using `float2`\n__global__ void vector_add_vec2(const float2* __restrict__ d_input1,\n                                const float2* __restrict__ d_input2,\n                                float2* __restrict__ d_output,\n                                size_t n_vec2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_vec2) {\n        float2 a = __ldg(&d_input1[idx]);  // \u2705 Cached read\n        float2 b = __ldg(&d_input2[idx]);\n\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        d_output[idx] = c;  // \u2705 Store addition result safely\n\n        if (idx < n_vec2 - 1) {  // \u2705 Prevent out-of-bounds write\n            float2 d;\n            d.x = a.x * b.x;\n            d.y = a.y * b.y;\n            d_output[idx + 1] = d;  // \u2705 Only store multiplication if there's space\n        }\n    }\n}\n\n// \u2705 Optimized Solution Function\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    size_t n_vec2 = n / 2;\n    size_t remainder = n % 2;\n\n    int threadsPerBlock = (n < 10'000'00) ? 256 : 512;\n    int blocksPerGrid = (n_vec2 + threadsPerBlock - 1) / threadsPerBlock;\n\n    if (n_vec2 > 0) {\n        vector_add_vec2<<<blocksPerGrid, threadsPerBlock>>>(\n            reinterpret_cast<const float2*>(d_input1),  \n            reinterpret_cast<const float2*>(d_input2),\n            reinterpret_cast<float2*>(d_output),\n            n_vec2);\n    }\n\n    if (remainder > 0) {\n        size_t offset = n - remainder;\n        blocksPerGrid = (remainder + threadsPerBlock - 1) / threadsPerBlock;\n\n        // \ud83d\udea8 Ensure `half2` alignment (4 bytes)\n        if ((reinterpret_cast<std::uintptr_t>(d_input1 + offset) % 4) == 0) {\n            vector_add_half2<<<blocksPerGrid, threadsPerBlock>>>(\n                reinterpret_cast<const half2*>(d_input1 + offset),\n                reinterpret_cast<const half2*>(d_input2 + offset),\n                reinterpret_cast<half2*>(d_output + offset),\n                remainder / 2);\n        } else {\n            printf(\"\ud83d\udea8 Error: Memory misaligned for half2 access!\\n\");\n        }\n    }\n\n    // \u2705 Debugging: Check for kernel errors\n    cudaError_t err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        printf(\"CUDA Kernel Error: %s\\n\", cudaGetErrorString(err));\n    }\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <cstdint>  // \u2705 Fix for uintptr_t\n#include <cstdio>   // \u2705 Fix for printf\n\n// \u2705 Use CUDA built-in function instead of inline PTX\n__device__ inline half2 my_hadd2(half2 a, half2 b) {\n    return __hadd2(a, b);  // \u2705 Built-in FP16 vectorized addition\n}\n\n// \u2705 Optimized vector addition kernel using FP16 (half2)\n__global__ void vector_add_half2(const half2* __restrict__ d_input1,\n                                 const half2* __restrict__ d_input2,\n                                 half2* __restrict__ d_output,\n                                 size_t n_half2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_half2) {\n        half2 a = __ldg(&d_input1[idx]);  // \u2705 Cached read\n        half2 b = __ldg(&d_input2[idx]);\n\n        half2 c = my_hadd2(a, b);\n        d_output[idx] = c;\n    }\n}\n\n// \u2705 Optimized `vector_add_vec2` kernel using `float2`\n__global__ void vector_add_vec2(const float2* __restrict__ d_input1,\n                                const float2* __restrict__ d_input2,\n                                float2* __restrict__ d_output,\n                                size_t n_vec2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_vec2) {\n        float2 a = __ldg(&d_input1[idx]);  // \u2705 Cached read\n        float2 b = __ldg(&d_input2[idx]);\n\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        d_output[idx] = c;  // \u2705 Store addition result safely\n\n        if (idx < n_vec2 - 1) {  // \u2705 Prevent out-of-bounds write\n            float2 d;\n            d.x = a.x * b.x;\n            d.y = a.y * b.y;\n            d_output[idx + 1] = d;  // \u2705 Only store multiplication if there's space\n        }\n    }\n}\n\n// \u2705 Optimized Solution Function\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    size_t n_vec2 = n / 2;\n    size_t remainder = n % 2;\n\n    int threadsPerBlock = (n < 10'000'00) ? 256 : 512;\n    int blocksPerGrid = (n_vec2 + threadsPerBlock - 1) / threadsPerBlock;\n\n    if (n_vec2 > 0) {\n        vector_add_vec2<<<blocksPerGrid, threadsPerBlock>>>(\n            reinterpret_cast<const float2*>(d_input1),  \n            reinterpret_cast<const float2*>(d_input2),\n            reinterpret_cast<float2*>(d_output),\n            n_vec2);\n    }\n\n    if (remainder > 0) {\n        size_t offset = n - remainder;\n        blocksPerGrid = (remainder + threadsPerBlock - 1) / threadsPerBlock;\n\n        // \ud83d\udea8 Ensure `half2` alignment (4 bytes)\n        if ((reinterpret_cast<std::uintptr_t>(d_input1 + offset) % 4) == 0) {\n            vector_add_half2<<<blocksPerGrid, threadsPerBlock>>>(\n                reinterpret_cast<const half2*>(d_input1 + offset),\n                reinterpret_cast<const half2*>(d_input2 + offset),\n                reinterpret_cast<half2*>(d_output + offset),\n                remainder / 2);\n        } else {\n            printf(\"\ud83d\udea8 Error: Memory misaligned for half2 access!\\n\");\n        }\n    }\n\n    // \u2705 Debugging: Check for kernel errors\n    hipError_t err = hipGetLastError();\n    if (err != hipSuccess) {\n        printf(\"CUDA Kernel Error: %s\\n\", hipGetErrorString(err));\n    }\n}\n", "source": "/kaggle/working/100-days-cuda/Day 32/CUDA_CMake/src/vector_add.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n#include <cufft.h>\n#include <complex>\n\n// Define complex number type\ntypedef std::complex<float> Complex;\n#define IDX2C(i,j,ld) (((j)*(ld))+(i)) // Macro for column-major indexing\n\n// CUDA Kernel to print device array (Debugging)\n__global__ void printDeviceArray(cufftComplex *data, int N) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < N) {\n        printf(\"Device Data[%d]: (%f, %f)\\n\", i, data[i].x, data[i].y);\n    }\n}\n\n// Host function to print host array\nvoid printHostArray(const char* name, cufftComplex *data, int N) {\n    printf(\"\\n%s:\\n\", name);\n    for (int i = 0; i < N; i++) {\n        printf(\"[%d]: (%f, %f)\\n\", i, data[i].x, data[i].y);\n    }\n}\n\n// Main function\nint main() {\n    int N = 8; // Must be power of 2 for Cooley-Tukey FFT\n    cufftComplex *h_input, *h_output;\n    cufftComplex *d_data;\n\n    // Allocate host memory\n    h_input = (cufftComplex*)malloc(sizeof(cufftComplex) * N);\n    h_output = (cufftComplex*)malloc(sizeof(cufftComplex) * N);\n\n    // Initialize input data (Real and Imaginary parts)\n    for (int i = 0; i < N; i++) {\n        h_input[i].x = i + 1;  // Real part\n        h_input[i].y = 0.0f;   // Imaginary part (set to 0)\n    }\n    \n    // Print input data\n    printHostArray(\"Input Data\", h_input, N);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_data, sizeof(cufftComplex) * N);\n    cudaMemcpy(d_data, h_input, sizeof(cufftComplex) * N, cudaMemcpyHostToDevice);\n\n    // Create FFT plan\n    cufftHandle plan;\n    cufftPlan1d(&plan, N, CUFFT_C2C, 1);\n\n    // Execute FFT\n    cufftExecC2C(plan, d_data, d_data, CUFFT_FORWARD);\n    cudaDeviceSynchronize();\n\n    // Copy results back to host\n    cudaMemcpy(h_output, d_data, sizeof(cufftComplex) * N, cudaMemcpyDeviceToHost);\n\n    // Print output data\n    printHostArray(\"Output Data (FFT Result)\", h_output, N);\n\n    // Cleanup\n    cufftDestroy(plan);\n    cudaFree(d_data);\n    free(h_input);\n    free(h_output);\n\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <hipfft/hipfft.h>\n#include <complex>\n\n// Define complex number type\ntypedef std::complex<float> Complex;\n#define IDX2C(i,j,ld) (((j)*(ld))+(i)) // Macro for column-major indexing\n\n// CUDA Kernel to print device array (Debugging)\n__global__ void printDeviceArray(hipfftComplex *data, int N) {\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i < N) {\n        printf(\"Device Data[%d]: (%f, %f)\\n\", i, data[i].x, data[i].y);\n    }\n}\n\n// Host function to print host array\nvoid printHostArray(const char* name, hipfftComplex *data, int N) {\n    printf(\"\\n%s:\\n\", name);\n    for (int i = 0; i < N; i++) {\n        printf(\"[%d]: (%f, %f)\\n\", i, data[i].x, data[i].y);\n    }\n}\n\n// Main function\nint main() {\n    int N = 8; // Must be power of 2 for Cooley-Tukey FFT\n    hipfftComplex *h_input, *h_output;\n    hipfftComplex *d_data;\n\n    // Allocate host memory\n    h_input = (hipfftComplex*)malloc(sizeof(hipfftComplex) * N);\n    h_output = (hipfftComplex*)malloc(sizeof(hipfftComplex) * N);\n\n    // Initialize input data (Real and Imaginary parts)\n    for (int i = 0; i < N; i++) {\n        h_input[i].x = i + 1;  // Real part\n        h_input[i].y = 0.0f;   // Imaginary part (set to 0)\n    }\n    \n    // Print input data\n    printHostArray(\"Input Data\", h_input, N);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_data, sizeof(hipfftComplex) * N);\n    hipMemcpy(d_data, h_input, sizeof(hipfftComplex) * N, hipMemcpyHostToDevice);\n\n    // Create FFT plan\n    hipfftHandle plan;\n    hipfftPlan1d(&plan, N, HIPFFT_C2C, 1);\n\n    // Execute FFT\n    hipfftExecC2C(plan, d_data, d_data, HIPFFT_FORWARD);\n    hipDeviceSynchronize();\n\n    // Copy results back to host\n    hipMemcpy(h_output, d_data, sizeof(hipfftComplex) * N, hipMemcpyDeviceToHost);\n\n    // Print output data\n    printHostArray(\"Output Data (FFT Result)\", h_output, N);\n\n    // Cleanup\n    hipfftDestroy(plan);\n    hipFree(d_data);\n    free(h_input);\n    free(h_output);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 26/FFT_cuda.cu"}
{"input": "#include <stdio.h>\n#include <math.h>\n\n__global__ void attention(float *Q, float *K, float *V, float *output, int n) {\n    __shared__ float scores[4];\n    __shared__ float softmax_scores[4];\n\n    int tid = threadIdx.x;\n\n    // Compute dot product (Q \u00b7 K^T)\n    scores[tid] = Q[tid] * K[tid];\n    __syncthreads();\n\n    // Print raw attention scores\n    printf(\"Thread %d: Raw Score = %.2f\\n\", tid, scores[tid]);\n\n    // Compute softmax denominator (sum of exponentials)\n    float sum_exp = 0;\n    for (int i = 0; i < n; i++) {\n        sum_exp += expf(scores[i]);\n    }\n\n    // Compute softmax score\n    softmax_scores[tid] = expf(scores[tid]) / sum_exp;\n    __syncthreads();\n\n    // Print softmax scores\n    printf(\"Thread %d: Softmax Score = %.2f\\n\", tid, softmax_scores[tid]);\n\n    // Compute weighted sum (Softmax \u00d7 V)\n    output[tid] = softmax_scores[tid] * V[tid];\n\n    // Print final output\n    printf(\"Thread %d: Output = %.2f\\n\", tid, output[tid]);\n}\n\nint main() {\n    float h_Q[4] = {1, 0, 1, 0};\n    float h_K[4] = {1, 1, 0, 0};\n    float h_V[4] = {5, 10, 15, 20};\n    float h_output[4] = {0};\n\n    float *d_Q, *d_K, *d_V, *d_output;\n    cudaMalloc(&d_Q, 4 * sizeof(float));\n    cudaMalloc(&d_K, 4 * sizeof(float));\n    cudaMalloc(&d_V, 4 * sizeof(float));\n    cudaMalloc(&d_output, 4 * sizeof(float));\n\n    cudaMemcpy(d_Q, h_Q, 4 * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_K, h_K, 4 * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_V, h_V, 4 * sizeof(float), cudaMemcpyHostToDevice);\n\n    printf(\"Running Attention Kernel:\\n\");\n    attention<<<1, 4>>>(d_Q, d_K, d_V, d_output, 4);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_output, d_output, 4 * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_Q);\n    cudaFree(d_K);\n    cudaFree(d_V);\n    cudaFree(d_output);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <math.h>\n\n__global__ void attention(float *Q, float *K, float *V, float *output, int n) {\n    __shared__ float scores[4];\n    __shared__ float softmax_scores[4];\n\n    int tid = threadIdx.x;\n\n    // Compute dot product (Q \u00b7 K^T)\n    scores[tid] = Q[tid] * K[tid];\n    __syncthreads();\n\n    // Print raw attention scores\n    printf(\"Thread %d: Raw Score = %.2f\\n\", tid, scores[tid]);\n\n    // Compute softmax denominator (sum of exponentials)\n    float sum_exp = 0;\n    for (int i = 0; i < n; i++) {\n        sum_exp += expf(scores[i]);\n    }\n\n    // Compute softmax score\n    softmax_scores[tid] = expf(scores[tid]) / sum_exp;\n    __syncthreads();\n\n    // Print softmax scores\n    printf(\"Thread %d: Softmax Score = %.2f\\n\", tid, softmax_scores[tid]);\n\n    // Compute weighted sum (Softmax \u00d7 V)\n    output[tid] = softmax_scores[tid] * V[tid];\n\n    // Print final output\n    printf(\"Thread %d: Output = %.2f\\n\", tid, output[tid]);\n}\n\nint main() {\n    float h_Q[4] = {1, 0, 1, 0};\n    float h_K[4] = {1, 1, 0, 0};\n    float h_V[4] = {5, 10, 15, 20};\n    float h_output[4] = {0};\n\n    float *d_Q, *d_K, *d_V, *d_output;\n    hipMalloc(&d_Q, 4 * sizeof(float));\n    hipMalloc(&d_K, 4 * sizeof(float));\n    hipMalloc(&d_V, 4 * sizeof(float));\n    hipMalloc(&d_output, 4 * sizeof(float));\n\n    hipMemcpy(d_Q, h_Q, 4 * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_K, h_K, 4 * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_V, h_V, 4 * sizeof(float), hipMemcpyHostToDevice);\n\n    printf(\"Running Attention Kernel:\\n\");\n    attention<<<1, 4>>>(d_Q, d_K, d_V, d_output, 4);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_output, d_output, 4 * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipFree(d_Q);\n    hipFree(d_K);\n    hipFree(d_V);\n    hipFree(d_output);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 23/Flash_attention.cu"}
{"input": "#include <stdio.h>\n#include <math.h>\n\n__global__ void layernorm(float *input, float *output, int n) {\n    __shared__ float mean;\n    __shared__ float var;\n    \n    int tid = threadIdx.x;\n\n    if (tid == 0) {\n        mean = 0;\n        var = 0;\n    }\n    __syncthreads();\n\n    // Compute mean\n    atomicAdd(&mean, input[tid] / n);\n    __syncthreads();\n\n    // Compute variance\n    float diff = input[tid] - mean;\n    atomicAdd(&var, (diff * diff) / n);\n    __syncthreads();\n\n    // Normalize\n    output[tid] = (input[tid] - mean) / sqrtf(var + 1e-5);\n\n    // Print Debugging Info\n    printf(\"Thread %d: Input = %.2f, Mean = %.2f, Variance = %.5f, Normalized = %.2f\\n\",\n           tid, input[tid], mean, var, output[tid]);\n}\n\nint main() {\n    float h_input[4] = {1.0, 2.0, 3.0, 4.0};\n    float h_output[4] = {0};\n\n    float *d_input, *d_output;\n    cudaMalloc(&d_input, 4 * sizeof(float));\n    cudaMalloc(&d_output, 4 * sizeof(float));\n\n    cudaMemcpy(d_input, h_input, 4 * sizeof(float), cudaMemcpyHostToDevice);\n\n    printf(\"Running LayerNorm Kernel:\\n\");\n    layernorm<<<1, 4>>>(d_input, d_output, 4);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_output, d_output, 4 * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <math.h>\n\n__global__ void layernorm(float *input, float *output, int n) {\n    __shared__ float mean;\n    __shared__ float var;\n    \n    int tid = threadIdx.x;\n\n    if (tid == 0) {\n        mean = 0;\n        var = 0;\n    }\n    __syncthreads();\n\n    // Compute mean\n    atomicAdd(&mean, input[tid] / n);\n    __syncthreads();\n\n    // Compute variance\n    float diff = input[tid] - mean;\n    atomicAdd(&var, (diff * diff) / n);\n    __syncthreads();\n\n    // Normalize\n    output[tid] = (input[tid] - mean) / sqrtf(var + 1e-5);\n\n    // Print Debugging Info\n    printf(\"Thread %d: Input = %.2f, Mean = %.2f, Variance = %.5f, Normalized = %.2f\\n\",\n           tid, input[tid], mean, var, output[tid]);\n}\n\nint main() {\n    float h_input[4] = {1.0, 2.0, 3.0, 4.0};\n    float h_output[4] = {0};\n\n    float *d_input, *d_output;\n    hipMalloc(&d_input, 4 * sizeof(float));\n    hipMalloc(&d_output, 4 * sizeof(float));\n\n    hipMemcpy(d_input, h_input, 4 * sizeof(float), hipMemcpyHostToDevice);\n\n    printf(\"Running LayerNorm Kernel:\\n\");\n    layernorm<<<1, 4>>>(d_input, d_output, 4);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_output, d_output, 4 * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipFree(d_input);\n    hipFree(d_output);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 23/LayerNorm.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n\n#define N 3 // 3x3 matrix\n\n// CUDA Kernel for Matrix Multiplication\n__global__ void Matmul(int* A, int* B, int* C, int n) {\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (row < n && col < n) {\n        int sum = 0;\n        for (int k = 0; k < n; k++) {\n            sum += A[row * n + k] * B[k * n + col]; // Correct dot product\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nint main() {\n    // Memory size calculation\n    int size = N * N * sizeof(int);\n\n    // Host matrices\n    int h_A[N * N], h_B[N * N], h_C[N * N];\n\n    // Initialize host matrices\n    for (int i = 0; i < N * N; i++) {\n        h_A[i] = i + 1;           // Example: 1, 2, 3, ..., 9\n        h_B[i] = (i + 1) * 2;     // Example: 2, 4, 6, ..., 18\n    }\n\n    // Print Input Matrices\n    printf(\"Matrix A:\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_A[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"\\nMatrix B:\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_B[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    // Device matrices\n    int *d_A, *d_B, *d_C;\n\n    // Allocate memory on GPU\n    cudaMalloc((void**)&d_A, size);\n    cudaMalloc((void**)&d_B, size);\n    cudaMalloc((void**)&d_C, size);\n\n    // Copy data from Host to Device\n    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    dim3 BlockDim(16, 16); // 16x16 threads per block\n    dim3 GridDim((N + 15) / 16, (N + 15) / 16); // Grid size\n\n    // Launch kernel\n    Matmul<<<GridDim, BlockDim>>>(d_A, d_B, d_C, N);\n\n    // Copy result back to host\n    cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);\n\n    // Print result\n    printf(\"\\nMatrix C (Result):\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_C[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    // Free device memory\n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C);\n\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n\n#define N 3 // 3x3 matrix\n\n// CUDA Kernel for Matrix Multiplication\n__global__ void Matmul(int* A, int* B, int* C, int n) {\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n\n    if (row < n && col < n) {\n        int sum = 0;\n        for (int k = 0; k < n; k++) {\n            sum += A[row * n + k] * B[k * n + col]; // Correct dot product\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nint main() {\n    // Memory size calculation\n    int size = N * N * sizeof(int);\n\n    // Host matrices\n    int h_A[N * N], h_B[N * N], h_C[N * N];\n\n    // Initialize host matrices\n    for (int i = 0; i < N * N; i++) {\n        h_A[i] = i + 1;           // Example: 1, 2, 3, ..., 9\n        h_B[i] = (i + 1) * 2;     // Example: 2, 4, 6, ..., 18\n    }\n\n    // Print Input Matrices\n    printf(\"Matrix A:\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_A[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    printf(\"\\nMatrix B:\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_B[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    // Device matrices\n    int *d_A, *d_B, *d_C;\n\n    // Allocate memory on GPU\n    hipMalloc((void**)&d_A, size);\n    hipMalloc((void**)&d_B, size);\n    hipMalloc((void**)&d_C, size);\n\n    // Copy data from Host to Device\n    hipMemcpy(d_A, h_A, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_B, h_B, size, hipMemcpyHostToDevice);\n\n    // Define grid and block dimensions\n    dim3 BlockDim(16, 16); // 16x16 threads per block\n    dim3 GridDim((N + 15) / 16, (N + 15) / 16); // Grid size\n\n    // Launch kernel\n    Matmul<<<GridDim, BlockDim>>>(d_A, d_B, d_C, N);\n\n    // Copy result back to host\n    hipMemcpy(h_C, d_C, size, hipMemcpyDeviceToHost);\n\n    // Print result\n    printf(\"\\nMatrix C (Result):\\n\");\n    for (int i = 0; i < N; i++) {\n        for (int j = 0; j < N; j++) {\n            printf(\"%d \", h_C[i * N + j]);\n        }\n        printf(\"\\n\");\n    }\n\n    // Free device memory\n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 27/Naive_matmul.cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n#include <cstdlib>\n#include <cmath>\n#include <cstdint>\n#include <chrono>\n\n#define M 1024\n#define N 1024\n#define K 1024\n\n#define BLOCK_SIZE 16\n\n__global__ void int8_gemm(int8_t* A, int8_t* B, int32_t* C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < m && col < n) {\n        int32_t sum = 0;\n        for (int i = 0; i < k; ++i) {\n            sum += static_cast<int32_t>(A[row * k + i]) * static_cast<int32_t>(B[i * n + col]);\n        }\n        C[row * n + col] = sum;\n    }\n}\n\n__global__ void fp32_gemm(float* A, float* B, float* C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < m && col < n) {\n        float sum = 0.0f;\n        for (int i = 0; i < k; ++i) {\n            sum += A[row * k + i] * B[i * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nvoid quantize(float* input, int8_t* output, int size, float scale) {\n    for (int i = 0; i < size; ++i) {\n        output[i] = static_cast<int8_t>(roundf(input[i] * scale));\n    }\n}\n\nvoid dequantize(int32_t* input, float* output, int size, float scale) {\n    for (int i = 0; i < size; ++i) {\n        output[i] = static_cast<float>(input[i]) / (scale * scale);\n    }\n}\n\nfloat compute_mae(float* A, float* B, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; ++i) {\n        sum += std::abs(A[i] - B[i]);\n    }\n    return sum / size;\n}\n\nint main() {\n    float *A_fp32, *B_fp32, *C_fp32, *C_fp32_ref;\n    int8_t *A_int8, *B_int8;\n    int32_t *C_int32;\n\n    A_fp32 = (float*)malloc(M * K * sizeof(float));\n    B_fp32 = (float*)malloc(K * N * sizeof(float));\n    C_fp32 = (float*)malloc(M * N * sizeof(float));\n    C_fp32_ref = (float*)malloc(M * N * sizeof(float));\n\n    A_int8 = (int8_t*)malloc(M * K * sizeof(int8_t));\n    B_int8 = (int8_t*)malloc(K * N * sizeof(int8_t));\n    C_int32 = (int32_t*)malloc(M * N * sizeof(int32_t));\n\n    for (int i = 0; i < M * K; ++i) A_fp32[i] = static_cast<float>(rand()) / RAND_MAX;\n    for (int i = 0; i < K * N; ++i) B_fp32[i] = static_cast<float>(rand()) / RAND_MAX;\n\n    float scale = 127.0f;\n\n    quantize(A_fp32, A_int8, M * K, scale);\n    quantize(B_fp32, B_int8, K * N, scale);\n\n    cudaStream_t stream1, stream2;\n    cudaStreamCreate(&stream1);\n    cudaStreamCreate(&stream2);\n\n    int8_t *d_A, *d_B;\n    int32_t *d_C;\n\n    cudaMalloc(&d_A, M * K * sizeof(int8_t));\n    cudaMalloc(&d_B, K * N * sizeof(int8_t));\n    cudaMalloc(&d_C, M * N * sizeof(int32_t));\n\n    cudaMemcpyAsync(d_A, A_int8, M * K * sizeof(int8_t), cudaMemcpyHostToDevice, stream1);\n    cudaMemcpyAsync(d_B, B_int8, K * N * sizeof(int8_t), cudaMemcpyHostToDevice, stream2);\n\n    dim3 block(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE, (M + BLOCK_SIZE - 1) / BLOCK_SIZE);\n\n    cudaGraph_t graph;\n    cudaGraphExec_t graphExec;\n\n    cudaStreamBeginCapture(stream1, cudaStreamCaptureModeGlobal);\n    int8_gemm<<<grid, block, 0, stream1>>>(d_A, d_B, d_C, M, N, K);\n    cudaStreamEndCapture(stream1, &graph);\n\n    cudaGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0);\n\n    auto start = std::chrono::high_resolution_clock::now();\n    cudaGraphLaunch(graphExec, stream1);\n    cudaStreamSynchronize(stream1);\n    auto end = std::chrono::high_resolution_clock::now();\n\n    std::chrono::duration<float, std::milli> duration_ms = end - start;\n    float gflops = (2.0f * M * N * K) / (duration_ms.count() * 1e6);\n\n    cudaMemcpy(C_int32, d_C, M * N * sizeof(int32_t), cudaMemcpyDeviceToHost);\n\n    std::cout << \"INT8 GEMM - Time: \" << duration_ms.count() << \" ms, GFLOPS: \" << gflops << std::endl;\n\n    dequantize(C_int32, C_fp32, M * N, scale);\n\n    start = std::chrono::high_resolution_clock::now();\n    fp32_gemm<<<grid, block>>>(A_fp32, B_fp32, C_fp32_ref, M, N, K);\n    cudaDeviceSynchronize();\n    end = std::chrono::high_resolution_clock::now();\n\n    duration_ms = end - start;\n    gflops = (2.0f * M * N * K) / (duration_ms.count() * 1e6);\n\n    std::cout << \"FP32 GEMM - Time: \" << duration_ms.count() << \" ms, GFLOPS: \" << gflops << std::endl;\n\n    float mae = compute_mae(C_fp32, C_fp32_ref, M * N);\n    std::cout << \"Mean Absolute Error (MAE) between FP32 and INT8 GEMM: \" << mae << std::endl;\n\n    cudaGraphDestroy(graph);\n    cudaGraphExecDestroy(graphExec);\n    cudaFree(d_A);\n    cudaFree(d_B);\n    cudaFree(d_C);\n\n    free(A_fp32);\n    free(B_fp32);\n    free(C_fp32);\n    free(C_fp32_ref);\n    free(A_int8);\n    free(B_int8);\n    free(C_int32);\n\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n#include <cstdlib>\n#include <cmath>\n#include <cstdint>\n#include <chrono>\n\n#define M 1024\n#define N 1024\n#define K 1024\n\n#define BLOCK_SIZE 16\n\n__global__ void int8_gemm(int8_t* A, int8_t* B, int32_t* C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < m && col < n) {\n        int32_t sum = 0;\n        for (int i = 0; i < k; ++i) {\n            sum += static_cast<int32_t>(A[row * k + i]) * static_cast<int32_t>(B[i * n + col]);\n        }\n        C[row * n + col] = sum;\n    }\n}\n\n__global__ void fp32_gemm(float* A, float* B, float* C, int m, int n, int k) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row < m && col < n) {\n        float sum = 0.0f;\n        for (int i = 0; i < k; ++i) {\n            sum += A[row * k + i] * B[i * n + col];\n        }\n        C[row * n + col] = sum;\n    }\n}\n\nvoid quantize(float* input, int8_t* output, int size, float scale) {\n    for (int i = 0; i < size; ++i) {\n        output[i] = static_cast<int8_t>(roundf(input[i] * scale));\n    }\n}\n\nvoid dequantize(int32_t* input, float* output, int size, float scale) {\n    for (int i = 0; i < size; ++i) {\n        output[i] = static_cast<float>(input[i]) / (scale * scale);\n    }\n}\n\nfloat compute_mae(float* A, float* B, int size) {\n    float sum = 0.0f;\n    for (int i = 0; i < size; ++i) {\n        sum += std::abs(A[i] - B[i]);\n    }\n    return sum / size;\n}\n\nint main() {\n    float *A_fp32, *B_fp32, *C_fp32, *C_fp32_ref;\n    int8_t *A_int8, *B_int8;\n    int32_t *C_int32;\n\n    A_fp32 = (float*)malloc(M * K * sizeof(float));\n    B_fp32 = (float*)malloc(K * N * sizeof(float));\n    C_fp32 = (float*)malloc(M * N * sizeof(float));\n    C_fp32_ref = (float*)malloc(M * N * sizeof(float));\n\n    A_int8 = (int8_t*)malloc(M * K * sizeof(int8_t));\n    B_int8 = (int8_t*)malloc(K * N * sizeof(int8_t));\n    C_int32 = (int32_t*)malloc(M * N * sizeof(int32_t));\n\n    for (int i = 0; i < M * K; ++i) A_fp32[i] = static_cast<float>(rand()) / RAND_MAX;\n    for (int i = 0; i < K * N; ++i) B_fp32[i] = static_cast<float>(rand()) / RAND_MAX;\n\n    float scale = 127.0f;\n\n    quantize(A_fp32, A_int8, M * K, scale);\n    quantize(B_fp32, B_int8, K * N, scale);\n\n    hipStream_t stream1, stream2;\n    hipStreamCreate(&stream1);\n    hipStreamCreate(&stream2);\n\n    int8_t *d_A, *d_B;\n    int32_t *d_C;\n\n    hipMalloc(&d_A, M * K * sizeof(int8_t));\n    hipMalloc(&d_B, K * N * sizeof(int8_t));\n    hipMalloc(&d_C, M * N * sizeof(int32_t));\n\n    hipMemcpyAsync(d_A, A_int8, M * K * sizeof(int8_t), hipMemcpyHostToDevice, stream1);\n    hipMemcpyAsync(d_B, B_int8, K * N * sizeof(int8_t), hipMemcpyHostToDevice, stream2);\n\n    dim3 block(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 grid((N + BLOCK_SIZE - 1) / BLOCK_SIZE, (M + BLOCK_SIZE - 1) / BLOCK_SIZE);\n\n    hipGraph_t graph;\n    hipGraphExec_t graphExec;\n\n    hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal);\n    int8_gemm<<<grid, block, 0, stream1>>>(d_A, d_B, d_C, M, N, K);\n    hipStreamEndCapture(stream1, &graph);\n\n    hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0);\n\n    auto start = std::chrono::high_resolution_clock::now();\n    hipGraphLaunch(graphExec, stream1);\n    hipStreamSynchronize(stream1);\n    auto end = std::chrono::high_resolution_clock::now();\n\n    std::chrono::duration<float, std::milli> duration_ms = end - start;\n    float gflops = (2.0f * M * N * K) / (duration_ms.count() * 1e6);\n\n    hipMemcpy(C_int32, d_C, M * N * sizeof(int32_t), hipMemcpyDeviceToHost);\n\n    std::cout << \"INT8 GEMM - Time: \" << duration_ms.count() << \" ms, GFLOPS: \" << gflops << std::endl;\n\n    dequantize(C_int32, C_fp32, M * N, scale);\n\n    start = std::chrono::high_resolution_clock::now();\n    fp32_gemm<<<grid, block>>>(A_fp32, B_fp32, C_fp32_ref, M, N, K);\n    hipDeviceSynchronize();\n    end = std::chrono::high_resolution_clock::now();\n\n    duration_ms = end - start;\n    gflops = (2.0f * M * N * K) / (duration_ms.count() * 1e6);\n\n    std::cout << \"FP32 GEMM - Time: \" << duration_ms.count() << \" ms, GFLOPS: \" << gflops << std::endl;\n\n    float mae = compute_mae(C_fp32, C_fp32_ref, M * N);\n    std::cout << \"Mean Absolute Error (MAE) between FP32 and INT8 GEMM: \" << mae << std::endl;\n\n    hipGraphDestroy(graph);\n    hipGraphExecDestroy(graphExec);\n    hipFree(d_A);\n    hipFree(d_B);\n    hipFree(d_C);\n\n    free(A_fp32);\n    free(B_fp32);\n    free(C_fp32);\n    free(C_fp32_ref);\n    free(A_int8);\n    free(B_int8);\n    free(C_int32);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 43/Int8_GEMM.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void vector_add(const float* __restrict__ d_input1,\n                           const float* __restrict__ d_input2,\n                           float* __restrict__ d_output,\n                           size_t n)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n) {\n        d_output[idx] = d_input1[idx] + d_input2[idx];\n    }\n}\n\n__global__ void vector_add_vec2(const float* __restrict__ d_input1,\n                                const float* __restrict__ d_input2,\n                                float* __restrict__ d_output,\n                                size_t n_vec2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_vec2) {\n        float2 a = reinterpret_cast<const float2*>(d_input1)[idx];\n        float2 b = reinterpret_cast<const float2*>(d_input2)[idx];\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        reinterpret_cast<float2*>(d_output)[idx] = c;\n    }\n}\n\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    size_t n_vec2 = n / 2;\n    size_t remainder = n % 2;\n\n    int threadsPerBlock = 512;\n    int blocksPerGrid = (n_vec2 + threadsPerBlock - 1) / threadsPerBlock; // Fixed grid size calculation\n\n    if (n_vec2 > 0) {\n        vector_add_vec2<<<blocksPerGrid, threadsPerBlock>>>(d_input1, d_input2, d_output, n_vec2);\n    }\n    if (remainder > 0) {\n        size_t offset = n - remainder;\n        blocksPerGrid = (remainder + threadsPerBlock - 1) / threadsPerBlock;\n        vector_add<<<blocksPerGrid, threadsPerBlock>>>(d_input1 + offset, d_input2 + offset, d_output + offset, remainder);\n    }\n}\n", "output": "#include <hip/hip_runtime.h>\n\n__global__ void vector_add(const float* __restrict__ d_input1,\n                           const float* __restrict__ d_input2,\n                           float* __restrict__ d_output,\n                           size_t n)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n) {\n        d_output[idx] = d_input1[idx] + d_input2[idx];\n    }\n}\n\n__global__ void vector_add_vec2(const float* __restrict__ d_input1,\n                                const float* __restrict__ d_input2,\n                                float* __restrict__ d_output,\n                                size_t n_vec2)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < n_vec2) {\n        float2 a = reinterpret_cast<const float2*>(d_input1)[idx];\n        float2 b = reinterpret_cast<const float2*>(d_input2)[idx];\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        reinterpret_cast<float2*>(d_output)[idx] = c;\n    }\n}\n\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    size_t n_vec2 = n / 2;\n    size_t remainder = n % 2;\n\n    int threadsPerBlock = 512;\n    int blocksPerGrid = (n_vec2 + threadsPerBlock - 1) / threadsPerBlock; // Fixed grid size calculation\n\n    if (n_vec2 > 0) {\n        vector_add_vec2<<<blocksPerGrid, threadsPerBlock>>>(d_input1, d_input2, d_output, n_vec2);\n    }\n    if (remainder > 0) {\n        size_t offset = n - remainder;\n        blocksPerGrid = (remainder + threadsPerBlock - 1) / threadsPerBlock;\n        vector_add<<<blocksPerGrid, threadsPerBlock>>>(d_input1 + offset, d_input2 + offset, d_output + offset, remainder);\n    }\n}\n", "source": "/kaggle/working/100-days-cuda/Day 31/Vector_add.cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n\n#define N 1048576  // Large enough to maximize occupancy\n#define THREADS_PER_BLOCK 256  // Block size\n\n__global__ void naive_reduction(float* input, float* output) {\n    __shared__ float shared_data[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int global_id = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory\n    shared_data[tid] = (global_id < N) ? input[global_id] : 0.0f;\n    __syncthreads();\n\n    // Na\u00efve reduction with warp divergence\n    for (int stride = 1; stride < blockDim.x; stride *= 2) {\n        if (tid % (2 * stride) == 0) {  // \ud83d\udea8 Causes warp divergence\n            shared_data[tid] += shared_data[tid + stride];\n        }\n        __syncthreads();  // \ud83d\udea8 Unnecessary synchronization for inactive threads\n    }\n\n    // Store block result\n    if (tid == 0) output[blockIdx.x] = shared_data[0];\n}\n\nvoid reduce(float* d_input, float* d_output, int num_elements) {\n    int blocks = (num_elements + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n    naive_reduction<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output);\n}\n\nint main() {\n    float *d_input, *d_output;\n    size_t size = N * sizeof(float);\n    size_t output_size = ((N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK) * sizeof(float);\n    \n    // Allocate memory\n    cudaMalloc(&d_input, size);\n    cudaMalloc(&d_output, output_size);\n    \n    // Launch naive reduction kernel\n    reduce(d_input, d_output, N);\n    \n    // Free memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n\n#define N 1048576  // Large enough to maximize occupancy\n#define THREADS_PER_BLOCK 256  // Block size\n\n__global__ void naive_reduction(float* input, float* output) {\n    __shared__ float shared_data[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int global_id = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory\n    shared_data[tid] = (global_id < N) ? input[global_id] : 0.0f;\n    __syncthreads();\n\n    // Na\u00efve reduction with warp divergence\n    for (int stride = 1; stride < blockDim.x; stride *= 2) {\n        if (tid % (2 * stride) == 0) {  // \ud83d\udea8 Causes warp divergence\n            shared_data[tid] += shared_data[tid + stride];\n        }\n        __syncthreads();  // \ud83d\udea8 Unnecessary synchronization for inactive threads\n    }\n\n    // Store block result\n    if (tid == 0) output[blockIdx.x] = shared_data[0];\n}\n\nvoid reduce(float* d_input, float* d_output, int num_elements) {\n    int blocks = (num_elements + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n    naive_reduction<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output);\n}\n\nint main() {\n    float *d_input, *d_output;\n    size_t size = N * sizeof(float);\n    size_t output_size = ((N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK) * sizeof(float);\n    \n    // Allocate memory\n    hipMalloc(&d_input, size);\n    hipMalloc(&d_output, output_size);\n    \n    // Launch naive reduction kernel\n    reduce(d_input, d_output, N);\n    \n    // Free memory\n    hipFree(d_input);\n    hipFree(d_output);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 24/WARP-DIVERGENCE/Naive_reduction.cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n\n#define N 1048576  // Large enough to maximize occupancy\n#define THREADS_PER_BLOCK 256  // Optimal block size\n\n__global__ void optimized_reduction(float* input, float* output) {\n    __shared__ float shared_data[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int global_id = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (avoid extra reads)\n    float local_sum = (global_id < N) ? input[global_id] : 0.0f;\n    shared_data[tid] = local_sum;\n    __syncthreads();\n\n    // Reduction within a block using shared memory\n    for (int stride = blockDim.x / 2; stride > 32; stride >>= 1) {\n        if (tid < stride) {\n            shared_data[tid] += shared_data[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Final warp-level reduction (no need for __syncthreads() here)\n    if (tid < 32) {\n        float val = shared_data[tid];\n        val += __shfl_down_sync(0xFFFFFFFF, val, 16);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 8);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 4);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 2);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 1);\n\n        if (tid == 0) output[blockIdx.x] = val;\n    }\n}\n\nvoid reduce(float* d_input, float* d_output, int num_elements) {\n    int blocks = (num_elements + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n    optimized_reduction<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output);\n}\n\nint main() {\n    float *d_input, *d_output;\n    size_t size = N * sizeof(float);\n    size_t output_size = ((N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK) * sizeof(float);\n    \n    // Allocate memory\n    cudaMalloc(&d_input, size);\n    cudaMalloc(&d_output, output_size);\n    \n    // Launch optimized reduction kernel\n    reduce(d_input, d_output, N);\n    \n    // Free memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n\n#define N 1048576  // Large enough to maximize occupancy\n#define THREADS_PER_BLOCK 256  // Optimal block size\n\n__global__ void optimized_reduction(float* input, float* output) {\n    __shared__ float shared_data[THREADS_PER_BLOCK];\n    int tid = threadIdx.x;\n    int global_id = blockIdx.x * blockDim.x + tid;\n\n    // Load data into shared memory (avoid extra reads)\n    float local_sum = (global_id < N) ? input[global_id] : 0.0f;\n    shared_data[tid] = local_sum;\n    __syncthreads();\n\n    // Reduction within a block using shared memory\n    for (int stride = blockDim.x / 2; stride > 32; stride >>= 1) {\n        if (tid < stride) {\n            shared_data[tid] += shared_data[tid + stride];\n        }\n        __syncthreads();\n    }\n\n    // Final warp-level reduction (no need for __syncthreads() here)\n    if (tid < 32) {\n        float val = shared_data[tid];\n        val += __shfl_down_sync(0xFFFFFFFF, val, 16);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 8);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 4);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 2);\n        val += __shfl_down_sync(0xFFFFFFFF, val, 1);\n\n        if (tid == 0) output[blockIdx.x] = val;\n    }\n}\n\nvoid reduce(float* d_input, float* d_output, int num_elements) {\n    int blocks = (num_elements + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n    optimized_reduction<<<blocks, THREADS_PER_BLOCK>>>(d_input, d_output);\n}\n\nint main() {\n    float *d_input, *d_output;\n    size_t size = N * sizeof(float);\n    size_t output_size = ((N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK) * sizeof(float);\n    \n    // Allocate memory\n    hipMalloc(&d_input, size);\n    hipMalloc(&d_output, output_size);\n    \n    // Launch optimized reduction kernel\n    reduce(d_input, d_output, N);\n    \n    // Free memory\n    hipFree(d_input);\n    hipFree(d_output);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 24/WARP-DIVERGENCE/Reduction_with_Warp_Intrinsics.cu"}
{"input": "#include <stdio.h>\n#include <stdlib.h>\n#include <cuda_runtime.h>\n\n#define N (1024*1024)\n#define ITERATIONS 100\n\n// Na\u00efve kernel: performs 100 iterations (with branch divergence) and then replicates the sum eight times.\n__global__ void naiveKernel(float *a, float *b, float *c, int n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < n) {\n        float sum = 0.0f;\n        // Branch divergence: even indices use multiplication; odd indices use addition.\n        if (idx % 2 == 0) {\n            for (int i = 0; i < ITERATIONS; i++) {\n                sum += a[idx] * b[idx];\n            }\n        } else {\n            for (int i = 0; i < ITERATIONS; i++) {\n                sum += a[idx] + b[idx];\n            }\n        }\n        // Replicate the result eight times (i.e. multiply by 8)\n        float temp1 = sum;\n        float temp2 = sum;\n        float temp3 = sum;\n        float temp4 = sum;\n        float temp5 = sum;\n        float temp6 = sum;\n        float temp7 = sum;\n        float temp8 = sum;\n        c[idx] = temp1 + temp2 + temp3 + temp4 + temp5 + temp6 + temp7 + temp8;\n    }\n}\n\n// Warptiled kernel: unrolls the loop by a factor of 8 into independent accumulators.\n// This increases ILP and helps hide long scoreboard stalls.\n// It computes the per-thread \u201cunit\u201d value once (using a flag to choose between multiply or add)\n// then performs ITERATIONS iterations (with remainder handled if ITERATIONS isn't divisible by 8)\n// and finally replicates the result by multiplying by 8.\n__global__ void warptiledKernel(float *a, float *b, float *c, int n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < n) {\n        float valA = a[idx];\n        float valB = b[idx];\n        // Compute the per-iteration unit based on thread index parity.\n        // For even: use a[idx]*b[idx]; for odd: use a[idx]+b[idx].\n        float unit = (idx % 2 == 0) ? (valA * valB) : (valA + valB);\n\n        // Unroll the 100 iterations by a factor of 8 using 8 independent accumulators.\n        float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;\n        float sum4 = 0.0f, sum5 = 0.0f, sum6 = 0.0f, sum7 = 0.0f;\n        int unrollFactor = 8;\n        int iter = ITERATIONS / unrollFactor;  // integer division\n        for (int i = 0; i < iter; i++) {\n            sum0 += unit;\n            sum1 += unit;\n            sum2 += unit;\n            sum3 += unit;\n            sum4 += unit;\n            sum5 += unit;\n            sum6 += unit;\n            sum7 += unit;\n        }\n        // Handle remaining iterations if ITERATIONS is not a multiple of 8.\n        int remainder = ITERATIONS - iter * unrollFactor;\n        float sumR = 0.0f;\n        for (int i = 0; i < remainder; i++) {\n            sumR += unit;\n        }\n        float sum = sum0 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7 + sumR;\n        // Replicate the result eight times.\n        c[idx] = sum * 8.0f;\n    }\n}\n\nint main() {\n    int n = N;\n    size_t size = n * sizeof(float);\n\n    // Allocate host arrays.\n    float *h_a = (float*) malloc(size);\n    float *h_b = (float*) malloc(size);\n    float *h_c = (float*) malloc(size);\n\n    // Initialize host arrays.\n    for (int i = 0; i < n; i++) {\n        h_a[i] = 1.0f;\n        h_b[i] = 2.0f;\n    }\n\n    // Allocate device memory.\n    float *d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    // Copy data from host to device.\n    cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);\n\n    int blockSize = 256;\n    int numBlocks = (n + blockSize - 1) / blockSize;\n\n    // --------------------------\n    // Run the na\u00efve kernel.\n    // --------------------------\n    naiveKernel<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);\n    cudaDeviceSynchronize();\n    cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);\n    printf(\"Na\u00efve Kernel Results (first 10 elements):\\n\");\n    for (int i = 0; i < 10; i++) {\n        printf(\"c[%d] = %f\\n\", i, h_c[i]);\n    }\n\n    // --------------------------\n    // Run the warptiled kernel.\n    // --------------------------\n    warptiledKernel<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);\n    cudaDeviceSynchronize();\n    cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);\n    printf(\"\\nWarptiled Kernel Results (first 10 elements):\\n\");\n    for (int i = 0; i < 10; i++) {\n        printf(\"c[%d] = %f\\n\", i, h_c[i]);\n    }\n\n    // Cleanup.\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    free(h_a);\n    free(h_b);\n    free(h_c);\n    \n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <stdlib.h>\n#include <hip/hip_runtime.h>\n\n#define N (1024*1024)\n#define ITERATIONS 100\n\n// Na\u00efve kernel: performs 100 iterations (with branch divergence) and then replicates the sum eight times.\n__global__ void naiveKernel(float *a, float *b, float *c, int n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < n) {\n        float sum = 0.0f;\n        // Branch divergence: even indices use multiplication; odd indices use addition.\n        if (idx % 2 == 0) {\n            for (int i = 0; i < ITERATIONS; i++) {\n                sum += a[idx] * b[idx];\n            }\n        } else {\n            for (int i = 0; i < ITERATIONS; i++) {\n                sum += a[idx] + b[idx];\n            }\n        }\n        // Replicate the result eight times (i.e. multiply by 8)\n        float temp1 = sum;\n        float temp2 = sum;\n        float temp3 = sum;\n        float temp4 = sum;\n        float temp5 = sum;\n        float temp6 = sum;\n        float temp7 = sum;\n        float temp8 = sum;\n        c[idx] = temp1 + temp2 + temp3 + temp4 + temp5 + temp6 + temp7 + temp8;\n    }\n}\n\n// Warptiled kernel: unrolls the loop by a factor of 8 into independent accumulators.\n// This increases ILP and helps hide long scoreboard stalls.\n// It computes the per-thread \u201cunit\u201d value once (using a flag to choose between multiply or add)\n// then performs ITERATIONS iterations (with remainder handled if ITERATIONS isn't divisible by 8)\n// and finally replicates the result by multiplying by 8.\n__global__ void warptiledKernel(float *a, float *b, float *c, int n) {\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < n) {\n        float valA = a[idx];\n        float valB = b[idx];\n        // Compute the per-iteration unit based on thread index parity.\n        // For even: use a[idx]*b[idx]; for odd: use a[idx]+b[idx].\n        float unit = (idx % 2 == 0) ? (valA * valB) : (valA + valB);\n\n        // Unroll the 100 iterations by a factor of 8 using 8 independent accumulators.\n        float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;\n        float sum4 = 0.0f, sum5 = 0.0f, sum6 = 0.0f, sum7 = 0.0f;\n        int unrollFactor = 8;\n        int iter = ITERATIONS / unrollFactor;  // integer division\n        for (int i = 0; i < iter; i++) {\n            sum0 += unit;\n            sum1 += unit;\n            sum2 += unit;\n            sum3 += unit;\n            sum4 += unit;\n            sum5 += unit;\n            sum6 += unit;\n            sum7 += unit;\n        }\n        // Handle remaining iterations if ITERATIONS is not a multiple of 8.\n        int remainder = ITERATIONS - iter * unrollFactor;\n        float sumR = 0.0f;\n        for (int i = 0; i < remainder; i++) {\n            sumR += unit;\n        }\n        float sum = sum0 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7 + sumR;\n        // Replicate the result eight times.\n        c[idx] = sum * 8.0f;\n    }\n}\n\nint main() {\n    int n = N;\n    size_t size = n * sizeof(float);\n\n    // Allocate host arrays.\n    float *h_a = (float*) malloc(size);\n    float *h_b = (float*) malloc(size);\n    float *h_c = (float*) malloc(size);\n\n    // Initialize host arrays.\n    for (int i = 0; i < n; i++) {\n        h_a[i] = 1.0f;\n        h_b[i] = 2.0f;\n    }\n\n    // Allocate device memory.\n    float *d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    // Copy data from host to device.\n    hipMemcpy(d_a, h_a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, h_b, size, hipMemcpyHostToDevice);\n\n    int blockSize = 256;\n    int numBlocks = (n + blockSize - 1) / blockSize;\n\n    // --------------------------\n    // Run the na\u00efve kernel.\n    // --------------------------\n    naiveKernel<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);\n    hipDeviceSynchronize();\n    hipMemcpy(h_c, d_c, size, hipMemcpyDeviceToHost);\n    printf(\"Na\u00efve Kernel Results (first 10 elements):\\n\");\n    for (int i = 0; i < 10; i++) {\n        printf(\"c[%d] = %f\\n\", i, h_c[i]);\n    }\n\n    // --------------------------\n    // Run the warptiled kernel.\n    // --------------------------\n    warptiledKernel<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);\n    hipDeviceSynchronize();\n    hipMemcpy(h_c, d_c, size, hipMemcpyDeviceToHost);\n    printf(\"\\nWarptiled Kernel Results (first 10 elements):\\n\");\n    for (int i = 0; i < 10; i++) {\n        printf(\"c[%d] = %f\\n\", i, h_c[i]);\n    }\n\n    // Cleanup.\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    free(h_a);\n    free(h_b);\n    free(h_c);\n    \n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 24/LOW-OCCUPANCY/Optimized.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n__global__ void faultyKernel(int *d_arr, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    \n    // Bug: No boundary check, leads to out-of-bounds access\n    d_arr[idx] = idx * 2;  \n}\n\n \n\nint main() {\n    int N = 100;  // Array size\n    int *d_arr;\n\n    // Allocate memory on the GPU\n    cudaMalloc((void **)&d_arr, N * sizeof(int));\n\n    // Launching more threads than allocated memory\n    int blockSize = 32;\n    int numBlocks = 5;  // 5 * 32 = 160 threads, but only 100 elements in d_arr\n    faultyKernel<<<numBlocks, blockSize>>>(d_arr, N);\n\n    // Error checking\n    cudaError_t err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        printf(\"CUDA Error: %s\\n\", cudaGetErrorString(err));\n    }\n\n    // Synchronize to catch errors\n    cudaDeviceSynchronize();\n    err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        printf(\"CUDA Error after sync: %s\\n\", cudaGetErrorString(err));\n    }\n\n    // Free memory\n\n    // Free memory\n    cudaFree(d_arr);\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n__global__ void faultyKernel(int *d_arr, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    \n    // Bug: No boundary check, leads to out-of-bounds access\n    d_arr[idx] = idx * 2;  \n}\n\n \n\nint main() {\n    int N = 100;  // Array size\n    int *d_arr;\n\n    // Allocate memory on the GPU\n    hipMalloc((void **)&d_arr, N * sizeof(int));\n\n    // Launching more threads than allocated memory\n    int blockSize = 32;\n    int numBlocks = 5;  // 5 * 32 = 160 threads, but only 100 elements in d_arr\n    faultyKernel<<<numBlocks, blockSize>>>(d_arr, N);\n\n    // Error checking\n    hipError_t err = hipGetLastError();\n    if (err != hipSuccess) {\n        printf(\"CUDA Error: %s\\n\", hipGetErrorString(err));\n    }\n\n    // Synchronize to catch errors\n    hipDeviceSynchronize();\n    err = hipGetLastError();\n    if (err != hipSuccess) {\n        printf(\"CUDA Error after sync: %s\\n\", hipGetErrorString(err));\n    }\n\n    // Free memory\n\n    // Free memory\n    hipFree(d_arr);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 24/OUT-OF-BOUNDS/faultyKernel.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n__global__ void faultyKernel(int *d_arr, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    \n    // FIX: Add boundary check to prevent out-of-bounds access\n    if (idx < N) {  \n        d_arr[idx] = idx * 2;\n    }\n}\n\nint main() {\n    int N = 100;  // Array size\n    int *d_arr;\n\n    // Allocate memory on the GPU\n    cudaMalloc((void **)&d_arr, N * sizeof(int));\n\n    // Adjust number of threads to match array size\n    int blockSize = 32;\n    int numBlocks = (N + blockSize - 1) / blockSize; // Ensures we only launch required threads\n\n    faultyKernel<<<numBlocks, blockSize>>>(d_arr, N);\n\n    // Error checking\n    cudaError_t err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        printf(\"CUDA Error: %s\\n\", cudaGetErrorString(err));\n    }\n\n    // Synchronize to catch errors\n    cudaDeviceSynchronize();\n    err = cudaGetLastError();\n    if (err != cudaSuccess) {\n        printf(\"CUDA Error after sync: %s\\n\", cudaGetErrorString(err));\n    }\n\n    // Free memory\n    cudaFree(d_arr);\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n__global__ void faultyKernel(int *d_arr, int N) {\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    \n    // FIX: Add boundary check to prevent out-of-bounds access\n    if (idx < N) {  \n        d_arr[idx] = idx * 2;\n    }\n}\n\nint main() {\n    int N = 100;  // Array size\n    int *d_arr;\n\n    // Allocate memory on the GPU\n    hipMalloc((void **)&d_arr, N * sizeof(int));\n\n    // Adjust number of threads to match array size\n    int blockSize = 32;\n    int numBlocks = (N + blockSize - 1) / blockSize; // Ensures we only launch required threads\n\n    faultyKernel<<<numBlocks, blockSize>>>(d_arr, N);\n\n    // Error checking\n    hipError_t err = hipGetLastError();\n    if (err != hipSuccess) {\n        printf(\"CUDA Error: %s\\n\", hipGetErrorString(err));\n    }\n\n    // Synchronize to catch errors\n    hipDeviceSynchronize();\n    err = hipGetLastError();\n    if (err != hipSuccess) {\n        printf(\"CUDA Error after sync: %s\\n\", hipGetErrorString(err));\n    }\n\n    // Free memory\n    hipFree(d_arr);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 24/OUT-OF-BOUNDS/fixedKernel.cu"}
{"input": "#include <cuda_runtime.h>\n\n__global__ void vector_add_fused(const float* __restrict__ d_input1,\n                                 const float* __restrict__ d_input2,\n                                 float* __restrict__ d_output,\n                                 size_t n)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n_vec2 = n / 2;\n\n    if (idx < n_vec2) {\n        float2 a = reinterpret_cast<const float2*>(d_input1)[idx];\n        float2 b = reinterpret_cast<const float2*>(d_input2)[idx];\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        reinterpret_cast<float2*>(d_output)[idx] = c;\n    }\n    \n    // Handle the remaining elements (if n is odd)\n    if (idx == n_vec2 && (n % 2) != 0) {\n        d_output[n - 1] = d_input1[n - 1] + d_input2[n - 1];\n    }\n}\n\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    \n    int threadsPerBlock = 512;\n    int blocksPerGrid = ((n / 2) + threadsPerBlock - 1) / threadsPerBlock;\n    vector_add_fused<<<blocksPerGrid, threadsPerBlock>>>(d_input1, d_input2, d_output, n);\n}\n\n\n//this is part of cmake file.", "output": "#include <hip/hip_runtime.h>\n\n__global__ void vector_add_fused(const float* __restrict__ d_input1,\n                                 const float* __restrict__ d_input2,\n                                 float* __restrict__ d_output,\n                                 size_t n)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    int n_vec2 = n / 2;\n\n    if (idx < n_vec2) {\n        float2 a = reinterpret_cast<const float2*>(d_input1)[idx];\n        float2 b = reinterpret_cast<const float2*>(d_input2)[idx];\n        float2 c;\n        c.x = a.x + b.x;\n        c.y = a.y + b.y;\n        reinterpret_cast<float2*>(d_output)[idx] = c;\n    }\n    \n    // Handle the remaining elements (if n is odd)\n    if (idx == n_vec2 && (n % 2) != 0) {\n        d_output[n - 1] = d_input1[n - 1] + d_input2[n - 1];\n    }\n}\n\nextern \"C\" void solution(float* d_input1, float* d_input2, float* d_output, size_t n) {\n    \n    int threadsPerBlock = 512;\n    int blocksPerGrid = ((n / 2) + threadsPerBlock - 1) / threadsPerBlock;\n    vector_add_fused<<<blocksPerGrid, threadsPerBlock>>>(d_input1, d_input2, d_output, n);\n}\n\n\n//this is part of cmake file.", "source": "/kaggle/working/100-days-cuda/Day 33/H100_vec_add.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define BLOCK_SIZE 1024\n\n__global__ void prefixSumExclusive(int *d_in, int *d_out, int N) {\n    __shared__ int temp[BLOCK_SIZE * 2];\n\n    int tid = threadIdx.x;\n    int offset = 1;\n\n    int ai = tid;\n    int bi = tid + (N / 2);\n\n    // Load data into shared memory\n    temp[ai] = (ai < N) ? d_in[ai] : 0;\n    temp[bi] = (bi < N) ? d_in[bi] : 0;\n\n    // Reduction phase (upsweep)\n    for (int d = N >> 1; d > 0; d >>= 1) {\n        __syncthreads();\n        if (tid < d) {\n            int indexA = offset * (2 * tid + 1) - 1;\n            int indexB = offset * (2 * tid + 2) - 1;\n            temp[indexB] += temp[indexA];\n        }\n        offset *= 2;\n    }\n\n    // Set last element to 0 for exclusive scan\n    if (tid == 0) temp[N - 1] = 0;\n\n    // Downsweep phase\n    for (int d = 1; d < N; d *= 2) {\n        offset /= 2;\n        __syncthreads();\n        if (tid < d) {\n            int indexA = offset * (2 * tid + 1) - 1;\n            int indexB = offset * (2 * tid + 2) - 1;\n            int t = temp[indexA];\n            temp[indexA] = temp[indexB];\n            temp[indexB] += t;\n        }\n    }\n    __syncthreads();\n\n    // Write results back to global memory\n    if (ai < N) d_out[ai] = temp[ai];\n    if (bi < N) d_out[bi] = temp[bi];\n}\n\nint main() {\n    int N = 1024;\n    int h_in[N], h_out[N];\n\n    // Initialize input array with values 1 to 1024\n    for (int i = 0; i < N; i++) {\n        h_in[i] = i + 1;\n    }\n\n    int *d_in, *d_out;\n    cudaMalloc((void**)&d_in, N * sizeof(int));\n    cudaMalloc((void**)&d_out, N * sizeof(int));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    // Setup CUDA events for timing\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n    \n    // Record the start event\n    cudaEventRecord(start, 0);\n    \n    // Launch kernel (1 block, BLOCK_SIZE threads)\n    prefixSumExclusive<<<1, BLOCK_SIZE>>>(d_in, d_out, N);\n    \n    // Record the stop event and wait for the kernel to finish\n    cudaEventRecord(stop, 0);\n    cudaEventSynchronize(stop);\n    \n    // Calculate elapsed time in milliseconds\n    float milliseconds = 0;\n    cudaEventElapsedTime(&milliseconds, start, stop);\n    \n    // Copy result back to host\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n    \n    // Calculate total additions: approximately 2*(N - 1)\n    long totalOps = 2 * (N - 1);\n    // Convert elapsed time to seconds\n    float seconds = milliseconds / 1000.0f;\n    // Calculate GFLOPS (Giga operations per second)\n    float gflops = totalOps / (seconds * 1e9);\n    \n    // Print scan result\n    printf(\"Scan found (exclusive prefix sum):\\n\");\n    for (int i = 0; i < N; i++) {\n        printf(\"%d \", h_out[i]);\n    }\n    printf(\"\\n\");\n\n    // Print benchmark results\n    printf(\"Kernel execution time: %f ms\\n\", milliseconds);\n    printf(\"Performance: %f GFLOPS\\n\", gflops);\n\n    // Cleanup\n    cudaFree(d_in);\n    cudaFree(d_out);\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define BLOCK_SIZE 1024\n\n__global__ void prefixSumExclusive(int *d_in, int *d_out, int N) {\n    __shared__ int temp[BLOCK_SIZE * 2];\n\n    int tid = threadIdx.x;\n    int offset = 1;\n\n    int ai = tid;\n    int bi = tid + (N / 2);\n\n    // Load data into shared memory\n    temp[ai] = (ai < N) ? d_in[ai] : 0;\n    temp[bi] = (bi < N) ? d_in[bi] : 0;\n\n    // Reduction phase (upsweep)\n    for (int d = N >> 1; d > 0; d >>= 1) {\n        __syncthreads();\n        if (tid < d) {\n            int indexA = offset * (2 * tid + 1) - 1;\n            int indexB = offset * (2 * tid + 2) - 1;\n            temp[indexB] += temp[indexA];\n        }\n        offset *= 2;\n    }\n\n    // Set last element to 0 for exclusive scan\n    if (tid == 0) temp[N - 1] = 0;\n\n    // Downsweep phase\n    for (int d = 1; d < N; d *= 2) {\n        offset /= 2;\n        __syncthreads();\n        if (tid < d) {\n            int indexA = offset * (2 * tid + 1) - 1;\n            int indexB = offset * (2 * tid + 2) - 1;\n            int t = temp[indexA];\n            temp[indexA] = temp[indexB];\n            temp[indexB] += t;\n        }\n    }\n    __syncthreads();\n\n    // Write results back to global memory\n    if (ai < N) d_out[ai] = temp[ai];\n    if (bi < N) d_out[bi] = temp[bi];\n}\n\nint main() {\n    int N = 1024;\n    int h_in[N], h_out[N];\n\n    // Initialize input array with values 1 to 1024\n    for (int i = 0; i < N; i++) {\n        h_in[i] = i + 1;\n    }\n\n    int *d_in, *d_out;\n    hipMalloc((void**)&d_in, N * sizeof(int));\n    hipMalloc((void**)&d_out, N * sizeof(int));\n\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    // Setup CUDA events for timing\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n    \n    // Record the start event\n    hipEventRecord(start, 0);\n    \n    // Launch kernel (1 block, BLOCK_SIZE threads)\n    prefixSumExclusive<<<1, BLOCK_SIZE>>>(d_in, d_out, N);\n    \n    // Record the stop event and wait for the kernel to finish\n    hipEventRecord(stop, 0);\n    hipEventSynchronize(stop);\n    \n    // Calculate elapsed time in milliseconds\n    float milliseconds = 0;\n    hipEventElapsedTime(&milliseconds, start, stop);\n    \n    // Copy result back to host\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n    \n    // Calculate total additions: approximately 2*(N - 1)\n    long totalOps = 2 * (N - 1);\n    // Convert elapsed time to seconds\n    float seconds = milliseconds / 1000.0f;\n    // Calculate GFLOPS (Giga operations per second)\n    float gflops = totalOps / (seconds * 1e9);\n    \n    // Print scan result\n    printf(\"Scan found (exclusive prefix sum):\\n\");\n    for (int i = 0; i < N; i++) {\n        printf(\"%d \", h_out[i]);\n    }\n    printf(\"\\n\");\n\n    // Print benchmark results\n    printf(\"Kernel execution time: %f ms\\n\", milliseconds);\n    printf(\"Performance: %f GFLOPS\\n\", gflops);\n\n    // Cleanup\n    hipFree(d_in);\n    hipFree(d_out);\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 25/para_prefix.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Adjust based on your GPU capabilities\n\n// Softmax kernel (row-wise computation)\n__global__ void softmaxKernel(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n    \n    int row = blockIdx.x;  // Each block handles one row\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    float* row_data = input + row * cols;  // Pointer to current row\n    float* shared_row = shared_data;  // Shared memory pointer\n\n    // Step 1: Find max element in the row (for numerical stability)\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, row_data[i]);\n    }\n\n    // Reduce max across threads in the block\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n\n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum them\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = expf(row_data[i] - block_max);\n        sum += shared_row[i];\n    }\n\n    // Reduce sum across threads\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n\n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n\n    // Step 3: Normalize the values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_row[i] / block_sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    cudaMemcpy(d_input, h_input, size, cudaMemcpyHostToDevice);\n\n    // Launch kernel (1 block per row)\n    softmaxKernel<<<rows, BLOCK_SIZE, cols * sizeof(float)>>>(d_input, d_output, rows, cols);\n    \n    // Copy result back\n    cudaMemcpy(h_output, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Free device memory\n    cudaFree(d_input);\n    cudaFree(d_output);\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <cmath>\n\n#define BLOCK_SIZE 256  // Adjust based on your GPU capabilities\n\n// Softmax kernel (row-wise computation)\n__global__ void softmaxKernel(float* input, float* output, int rows, int cols) {\n    extern __shared__ float shared_data[];\n    \n    int row = blockIdx.x;  // Each block handles one row\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    float* row_data = input + row * cols;  // Pointer to current row\n    float* shared_row = shared_data;  // Shared memory pointer\n\n    // Step 1: Find max element in the row (for numerical stability)\n    float max_val = -INFINITY;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = fmaxf(max_val, row_data[i]);\n    }\n\n    // Reduce max across threads in the block\n    __shared__ float block_max;\n    if (tid == 0) block_max = -INFINITY;\n    __syncthreads();\n\n    atomicMax((int*)&block_max, __float_as_int(max_val));\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum them\n    float sum = 0.0f;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = expf(row_data[i] - block_max);\n        sum += shared_row[i];\n    }\n\n    // Reduce sum across threads\n    __shared__ float block_sum;\n    if (tid == 0) block_sum = 0.0f;\n    __syncthreads();\n\n    atomicAdd(&block_sum, sum);\n    __syncthreads();\n\n    // Step 3: Normalize the values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = shared_row[i] / block_sum;\n    }\n}\n\n// Host function to launch kernel\nvoid softmax(float* h_input, float* h_output, int rows, int cols) {\n    float *d_input, *d_output;\n    size_t size = rows * cols * sizeof(float);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n\n    // Copy data to device\n    hipMemcpy(d_input, h_input, size, hipMemcpyHostToDevice);\n\n    // Launch kernel (1 block per row)\n    softmaxKernel<<<rows, BLOCK_SIZE, cols * sizeof(float)>>>(d_input, d_output, rows, cols);\n    \n    // Copy result back\n    hipMemcpy(h_output, d_output, size, hipMemcpyDeviceToHost);\n\n    // Free device memory\n    hipFree(d_input);\n    hipFree(d_output);\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax\n    softmax(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output:\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 11/Basic_Softmax.cu"}
{"input": "#include <stdio.h>\n#include <curand_kernel.h>\n#include <math.h>\n\n#define STATES 9\n#define ACTIONS 4\n#define EPISODES 5000\n#define ALPHA 0.5\n#define GAMMA 0.9\n#define EPSILON_MAX 0.1\n#define EPSILON_MIN 0.01\n#define LAMBDA 0.001\n\n__device__ int getNextState(int state, int action) {\n    int row = state / 3, col = state % 3;\n    if (action == 0 && row > 0) return state - 3;\n    if (action == 1 && col < 2) return state + 1;\n    if (action == 2 && row < 2) return state + 3;\n    if (action == 3 && col > 0) return state - 1;\n    return state;\n}\n\n__device__ int chooseAction(float *Q, int state, curandState *randState, float epsilon) {\n    if (curand_uniform(randState) < epsilon) {\n        return curand(randState) % ACTIONS;\n    } else {\n        int best_action = 0;\n        float best_value = Q[state * ACTIONS];\n        for (int i = 1; i < ACTIONS; i++) {\n            if (Q[state * ACTIONS + i] > best_value) {\n                best_value = Q[state * ACTIONS + i];\n                best_action = i;\n            }\n        }\n        return best_action;\n    }\n}\n\n__global__ void sarsa_kernel(float *Q, curandState *randState) {\n    int tid = threadIdx.x;\n    curand_init(1234, tid, 0, &randState[tid]);\n    \n    for (int episode = 0; episode < EPISODES; episode++) {\n        float epsilon = EPSILON_MIN + (EPSILON_MAX - EPSILON_MIN) * expf(-LAMBDA * episode);\n        int state = curand(&randState[tid]) % STATES;\n        int action = chooseAction(Q, state, &randState[tid], epsilon);\n        \n        for (int step = 0; step < 100; step++) {\n            int next_state = getNextState(state, action);\n            int next_action = chooseAction(Q, next_state, &randState[tid], epsilon);\n            \n            float reward = (next_state == STATES - 1) ? 10.0f : -0.1f;\n            Q[state * ACTIONS + action] += ALPHA * (reward + GAMMA * Q[next_state * ACTIONS + next_action] - Q[state * ACTIONS + action]);\n            \n            state = next_state;\n            action = next_action;\n        }\n    }\n}\n\nint main() {\n    float *d_Q;\n    curandState *d_randState;\n    cudaMalloc(&d_Q, STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_randState, STATES * sizeof(curandState));\n    cudaMemset(d_Q, 0, STATES * ACTIONS * sizeof(float));\n    \n    sarsa_kernel<<<1, STATES>>>(d_Q, d_randState);\n    cudaDeviceSynchronize();\n    \n    float Q[STATES * ACTIONS];\n    cudaMemcpy(Q, d_Q, STATES * ACTIONS * sizeof(float), cudaMemcpyDeviceToHost);\n    \n    printf(\"SARSA Q-values:\\n\");\n    for (int s = 0; s < STATES; s++) {\n        for (int a = 0; a < ACTIONS; a++) {\n            printf(\"%.4f \", Q[s * ACTIONS + a]);\n        }\n        printf(\"\\n\");\n    }\n    \n    cudaFree(d_Q);\n    cudaFree(d_randState);\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <hiprand/hiprand_kernel.h>\n#include <math.h>\n\n#define STATES 9\n#define ACTIONS 4\n#define EPISODES 5000\n#define ALPHA 0.5\n#define GAMMA 0.9\n#define EPSILON_MAX 0.1\n#define EPSILON_MIN 0.01\n#define LAMBDA 0.001\n\n__device__ int getNextState(int state, int action) {\n    int row = state / 3, col = state % 3;\n    if (action == 0 && row > 0) return state - 3;\n    if (action == 1 && col < 2) return state + 1;\n    if (action == 2 && row < 2) return state + 3;\n    if (action == 3 && col > 0) return state - 1;\n    return state;\n}\n\n__device__ int chooseAction(float *Q, int state, hiprandState *randState, float epsilon) {\n    if (hiprand_uniform(randState) < epsilon) {\n        return hiprand(randState) % ACTIONS;\n    } else {\n        int best_action = 0;\n        float best_value = Q[state * ACTIONS];\n        for (int i = 1; i < ACTIONS; i++) {\n            if (Q[state * ACTIONS + i] > best_value) {\n                best_value = Q[state * ACTIONS + i];\n                best_action = i;\n            }\n        }\n        return best_action;\n    }\n}\n\n__global__ void sarsa_kernel(float *Q, hiprandState *randState) {\n    int tid = threadIdx.x;\n    hiprand_init(1234, tid, 0, &randState[tid]);\n    \n    for (int episode = 0; episode < EPISODES; episode++) {\n        float epsilon = EPSILON_MIN + (EPSILON_MAX - EPSILON_MIN) * expf(-LAMBDA * episode);\n        int state = hiprand(&randState[tid]) % STATES;\n        int action = chooseAction(Q, state, &randState[tid], epsilon);\n        \n        for (int step = 0; step < 100; step++) {\n            int next_state = getNextState(state, action);\n            int next_action = chooseAction(Q, next_state, &randState[tid], epsilon);\n            \n            float reward = (next_state == STATES - 1) ? 10.0f : -0.1f;\n            Q[state * ACTIONS + action] += ALPHA * (reward + GAMMA * Q[next_state * ACTIONS + next_action] - Q[state * ACTIONS + action]);\n            \n            state = next_state;\n            action = next_action;\n        }\n    }\n}\n\nint main() {\n    float *d_Q;\n    hiprandState *d_randState;\n    hipMalloc(&d_Q, STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_randState, STATES * sizeof(hiprandState));\n    hipMemset(d_Q, 0, STATES * ACTIONS * sizeof(float));\n    \n    sarsa_kernel<<<1, STATES>>>(d_Q, d_randState);\n    hipDeviceSynchronize();\n    \n    float Q[STATES * ACTIONS];\n    hipMemcpy(Q, d_Q, STATES * ACTIONS * sizeof(float), hipMemcpyDeviceToHost);\n    \n    printf(\"SARSA Q-values:\\n\");\n    for (int s = 0; s < STATES; s++) {\n        for (int a = 0; a < ACTIONS; a++) {\n            printf(\"%.4f \", Q[s * ACTIONS + a]);\n        }\n        printf(\"\\n\");\n    }\n    \n    hipFree(d_Q);\n    hipFree(d_randState);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 56/sarsa.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <cublas_v2.h>\n#include <cudnn.h>\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// CUDA error check macro\n#define CHECK_CUDA(call) { \\\n    cudaError_t err = call; \\\n    if (err != cudaSuccess) { \\\n        std::cerr << \"CUDA Error: \" << cudaGetErrorString(err) << \" at \" << __FILE__ << \":\" << __LINE__ << std::endl; \\\n        exit(EXIT_FAILURE); \\\n    } \\\n}\n\n// cuDNN error check macro\n#define CHECK_CUDNN(call) { \\\n    cudnnStatus_t err = call; \\\n    if (err != CUDNN_STATUS_SUCCESS) { \\\n        std::cerr << \"cuDNN Error: \" << cudnnGetErrorString(err) << \" at \" << __FILE__ << \":\" << __LINE__ << std::endl; \\\n        exit(EXIT_FAILURE); \\\n    } \\\n}\n\n// Softmax Kernel using FP16 Tensor Cores\n__global__ void softmaxKernel(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];  \n    __shared__ __half block_max;\n    __shared__ __half block_sum;\n    \n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n    \n    // Step 1: Compute max for numerical stability (Block-wide reduction)\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n\n    // Warp reduction for max\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        max_val = __hmax(max_val, __shfl_down_sync(0xffffffff, max_val, offset));\n    }\n\n    if (tid == 0) block_max = max_val;\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum\n    max_val = block_max;\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));\n        sum = __hadd(sum, shared_row[i]);\n    }\n\n    // Warp reduction for sum\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        sum = __hadd(sum, __shfl_down_sync(0xffffffff, sum, offset));\n    }\n\n    if (tid == 0) block_sum = sum;\n    __syncthreads();\n\n    // Step 3: Normalize values\n    sum = block_sum;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function with CUDA Graphs, Streams, and cuBLAS/cuDNN\nvoid softmaxOptimized(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Convert FP32 to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Allocate device memory\n    CHECK_CUDA(cudaMalloc((void**)&d_input, size));\n    CHECK_CUDA(cudaMalloc((void**)&d_output, size));\n    CHECK_CUDA(cudaMemcpy(d_input, h_input_half, size, cudaMemcpyHostToDevice));\n\n    // CUDA Stream & Graph setup\n    cudaStream_t stream;\n    CHECK_CUDA(cudaStreamCreate(&stream));\n    cudaGraph_t graph;\n    cudaGraphExec_t instance;\n\n    // CUDA Event setup for performance measurement\n    cudaEvent_t start, stop;\n    CHECK_CUDA(cudaEventCreate(&start));\n    CHECK_CUDA(cudaEventCreate(&stop));\n\n    // Capture Graph\n    CHECK_CUDA(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal));\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxKernel<<<rows, BLOCK_SIZE, shared_mem_size, stream>>>(d_input, d_output, rows, cols);\n    CHECK_CUDA(cudaStreamEndCapture(stream, &graph));\n\n    // Instantiate and launch graph\n    CHECK_CUDA(cudaGraphInstantiate(&instance, graph, NULL, NULL, 0));\n\n    // Start event\n    CHECK_CUDA(cudaEventRecord(start, stream));\n    CHECK_CUDA(cudaGraphLaunch(instance, stream));\n    CHECK_CUDA(cudaEventRecord(stop, stream));\n\n    // Wait for execution\n    CHECK_CUDA(cudaStreamSynchronize(stream));\n\n    // Compute execution time\n    float milliseconds = 0;\n    CHECK_CUDA(cudaEventElapsedTime(&milliseconds, start, stop));\n\n    // GFLOPS Calculation\n    int num_operations = 2 * rows * cols;  // Each element involves 2 FLOPs (exp + div)\n    double gflops = (num_operations * 1e-9) / (milliseconds * 1e-3);  // GFLOPS = (FLOPs / time)\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    CHECK_CUDA(cudaMemcpy(h_output_half, d_output, size, cudaMemcpyDeviceToHost));\n\n    // Convert FP16 to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    cudaGraphDestroy(graph);\n    cudaGraphExecDestroy(instance);\n    cudaStreamDestroy(stream);\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n    delete[] h_input_half;\n    delete[] h_output_half;\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n    // Print performance metrics\n    std::cout << \"Execution Time: \" << milliseconds << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using CUDA Streams + Graphs + cuBLAS/cuDNN\n    softmaxOptimized(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (CUDA Streams + cuBLAS/cuDNN + FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <hipblas.h>\n#include <hipDNN.h>\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// CUDA error check macro\n#define CHECK_CUDA(call) { \\\n    hipError_t err = call; \\\n    if (err != hipSuccess) { \\\n        std::cerr << \"CUDA Error: \" << hipGetErrorString(err) << \" at \" << __FILE__ << \":\" << __LINE__ << std::endl; \\\n        exit(EXIT_FAILURE); \\\n    } \\\n}\n\n// cuDNN error check macro\n#define CHECK_CUDNN(call) { \\\n    hipdnnStatus_t err = call; \\\n    if (err != HIPDNN_STATUS_SUCCESS) { \\\n        std::cerr << \"cuDNN Error: \" << hipdnnGetErrorString(err) << \" at \" << __FILE__ << \":\" << __LINE__ << std::endl; \\\n        exit(EXIT_FAILURE); \\\n    } \\\n}\n\n// Softmax Kernel using FP16 Tensor Cores\n__global__ void softmaxKernel(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];  \n    __shared__ __half block_max;\n    __shared__ __half block_sum;\n    \n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n    \n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n    \n    // Step 1: Compute max for numerical stability (Block-wide reduction)\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n\n    // Warp reduction for max\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        max_val = __hmax(max_val, __shfl_down_sync(0xffffffff, max_val, offset));\n    }\n\n    if (tid == 0) block_max = max_val;\n    __syncthreads();\n\n    // Step 2: Compute exponentials and sum\n    max_val = block_max;\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));\n        sum = __hadd(sum, shared_row[i]);\n    }\n\n    // Warp reduction for sum\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        sum = __hadd(sum, __shfl_down_sync(0xffffffff, sum, offset));\n    }\n\n    if (tid == 0) block_sum = sum;\n    __syncthreads();\n\n    // Step 3: Normalize values\n    sum = block_sum;\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function with CUDA Graphs, Streams, and cuBLAS/cuDNN\nvoid softmaxOptimized(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Convert FP32 to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Allocate device memory\n    CHECK_CUDA(hipMalloc((void**)&d_input, size));\n    CHECK_CUDA(hipMalloc((void**)&d_output, size));\n    CHECK_CUDA(hipMemcpy(d_input, h_input_half, size, hipMemcpyHostToDevice));\n\n    // CUDA Stream & Graph setup\n    hipStream_t stream;\n    CHECK_CUDA(hipStreamCreate(&stream));\n    hipGraph_t graph;\n    hipGraphExec_t instance;\n\n    // CUDA Event setup for performance measurement\n    hipEvent_t start, stop;\n    CHECK_CUDA(hipEventCreate(&start));\n    CHECK_CUDA(hipEventCreate(&stop));\n\n    // Capture Graph\n    CHECK_CUDA(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal));\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxKernel<<<rows, BLOCK_SIZE, shared_mem_size, stream>>>(d_input, d_output, rows, cols);\n    CHECK_CUDA(hipStreamEndCapture(stream, &graph));\n\n    // Instantiate and launch graph\n    CHECK_CUDA(hipGraphInstantiate(&instance, graph, NULL, NULL, 0));\n\n    // Start event\n    CHECK_CUDA(hipEventRecord(start, stream));\n    CHECK_CUDA(hipGraphLaunch(instance, stream));\n    CHECK_CUDA(hipEventRecord(stop, stream));\n\n    // Wait for execution\n    CHECK_CUDA(hipStreamSynchronize(stream));\n\n    // Compute execution time\n    float milliseconds = 0;\n    CHECK_CUDA(hipEventElapsedTime(&milliseconds, start, stop));\n\n    // GFLOPS Calculation\n    int num_operations = 2 * rows * cols;  // Each element involves 2 FLOPs (exp + div)\n    double gflops = (num_operations * 1e-9) / (milliseconds * 1e-3);  // GFLOPS = (FLOPs / time)\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    CHECK_CUDA(hipMemcpy(h_output_half, d_output, size, hipMemcpyDeviceToHost));\n\n    // Convert FP16 to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    hipGraphDestroy(graph);\n    hipGraphExecDestroy(instance);\n    hipStreamDestroy(stream);\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n    delete[] h_input_half;\n    delete[] h_output_half;\n    hipFree(d_input);\n    hipFree(d_output);\n\n    // Print performance metrics\n    std::cout << \"Execution Time: \" << milliseconds << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using CUDA Streams + Graphs + cuBLAS/cuDNN\n    softmaxOptimized(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (CUDA Streams + cuBLAS/cuDNN + FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 16/Better_Softmax.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    cudaMalloc(&device_a, M * K * sizeof(half));\n    cudaMalloc(&device_b, K * N * sizeof(half));\n    cudaMalloc(&device_d, M * N * sizeof(float));\n\n    cudaMemcpy(device_a, host_a.data(), M * K * sizeof(half), cudaMemcpyHostToDevice);\n    cudaMemcpy(device_b, host_b.data(), K * N * sizeof(half), cudaMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32);  // One warp per tile\n    wmma_kernel<<<grid, block>>>(device_a, device_b, device_d, M, N, K);\n    cudaDeviceSynchronize();\n    cudaError_t err = cudaGetLastError();\n    if (err != cudaSuccess) {\n    std::cerr << \"CUDA Error: \" << cudaGetErrorString(err) << std::endl;\n}\n\n    cudaMemcpy(host_d.data(), device_d, M * N * sizeof(float), cudaMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    cudaFree(device_a);\n    cudaFree(device_b);\n    cudaFree(device_d);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    hipMalloc(&device_a, M * K * sizeof(half));\n    hipMalloc(&device_b, K * N * sizeof(half));\n    hipMalloc(&device_d, M * N * sizeof(float));\n\n    hipMemcpy(device_a, host_a.data(), M * K * sizeof(half), hipMemcpyHostToDevice);\n    hipMemcpy(device_b, host_b.data(), K * N * sizeof(half), hipMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32);  // One warp per tile\n    wmma_kernel<<<grid, block>>>(device_a, device_b, device_d, M, N, K);\n    hipDeviceSynchronize();\n    hipError_t err = hipGetLastError();\n    if (err != hipSuccess) {\n    std::cerr << \"CUDA Error: \" << hipGetErrorString(err) << std::endl;\n}\n\n    hipMemcpy(host_d.data(), device_d, M * N * sizeof(float), hipMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    hipFree(device_a);\n    hipFree(device_b);\n    hipFree(device_d);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 08/WMMA_matmul.cu"}
{"input": "\ufeff#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    cudaMalloc(&device_a, M * K * sizeof(half));\n    cudaMalloc(&device_b, K * N * sizeof(half));\n    cudaMalloc(&device_d, M * N * sizeof(float));\n\n    cudaMemcpy(device_a, host_a.data(), M * K * sizeof(half), cudaMemcpyHostToDevice);\n    cudaMemcpy(device_b, host_b.data(), K * N * sizeof(half), cudaMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32, 1);\n    wmma_kernel << <grid, block >> > (device_a, device_b, device_d, M, N, K);\n\n    cudaMemcpy(host_d.data(), device_d, M * N * sizeof(float), cudaMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    cudaFree(device_a);\n    cudaFree(device_b);\n    cudaFree(device_d);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <iostream>\n#include <vector>\n#include <cmath>\n#include <mma.h>\n\nusing namespace nvcuda;\n\nconst int M = 32;\nconst int N = 32;\nconst int K = 32;\n\nconst int WMMA_M = 16;\nconst int WMMA_N = 16;\nconst int WMMA_K = 16;\n\nconst int M_TILES = M / WMMA_M;\nconst int N_TILES = N / WMMA_N;\nconst int K_TILES = K / WMMA_K;\n\n__global__ void wmma_kernel(half* a, half* b, float* d, int M, int N, int K) {\n    wmma::fragment<wmma::matrix_a, WMMA_M, WMMA_N, WMMA_K, half, wmma::row_major> a_frag;\n    wmma::fragment<wmma::matrix_b, WMMA_M, WMMA_N, WMMA_K, half, wmma::col_major> b_frag;\n    wmma::fragment<wmma::accumulator, WMMA_M, WMMA_N, WMMA_K, float> acc_frag;\n\n    wmma::fill_fragment(acc_frag, 0.0f);\n\n    const int tile_row = blockIdx.x;\n    const int tile_col = blockIdx.y;\n\n    for (int k = 0; k < K_TILES; ++k) {\n        int a_row = tile_row * WMMA_M;\n        int a_col = k * WMMA_K;\n        const half* a_tile_ptr = a + a_row * K + a_col;\n        wmma::load_matrix_sync(a_frag, a_tile_ptr, K);\n\n        int b_row = k * WMMA_K;\n        int b_col = tile_col * WMMA_N;\n        const half* b_tile_ptr = b + b_row + b_col * K;\n        wmma::load_matrix_sync(b_frag, b_tile_ptr, K);\n\n        wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);\n    }\n\n    int d_row = tile_row * WMMA_M;\n    int d_col = tile_col * WMMA_N;\n    float* d_tile_ptr = d + d_row * N + d_col;\n    wmma::store_matrix_sync(d_tile_ptr, acc_frag, N, wmma::mem_row_major);\n}\n\n// Function to print matrices\ntemplate <typename T>\nvoid printMatrix(const char* name, const T* matrix, int rows, int cols) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < rows; ++i) {\n        for (int j = 0; j < cols; ++j) {\n            std::cout << matrix[i * cols + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\nint main() {\n    std::vector<half> host_a(M * K);\n    std::vector<half> host_b(K * N);\n    std::vector<float> host_d(M * N);\n    std::vector<float> host_ref(M * N);\n\n    for (int i = 0; i < M * K; ++i)\n        host_a[i] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int j = 0; j < N; ++j)\n        for (int i = 0; i < K; ++i)\n            host_b[i + j * K] = __float2half(static_cast<float>(rand() % 10) / 10.0f);\n\n    for (int i = 0; i < M; ++i) {\n        for (int j = 0; j < N; ++j) {\n            float sum = 0.0f;\n            for (int k = 0; k < K; ++k) {\n                sum += __half2float(host_a[i * K + k]) *\n                    __half2float(host_b[k + j * K]);\n            }\n            host_ref[i * N + j] = sum;\n        }\n    }\n\n    printMatrix(\"Matrix A (Row-Major)\", host_a.data(), M, K);\n    printMatrix(\"Matrix B (Column-Major)\", host_b.data(), K, N);\n\n    half* device_a, * device_b;\n    float* device_d;\n    hipMalloc(&device_a, M * K * sizeof(half));\n    hipMalloc(&device_b, K * N * sizeof(half));\n    hipMalloc(&device_d, M * N * sizeof(float));\n\n    hipMemcpy(device_a, host_a.data(), M * K * sizeof(half), hipMemcpyHostToDevice);\n    hipMemcpy(device_b, host_b.data(), K * N * sizeof(half), hipMemcpyHostToDevice);\n\n    dim3 grid(M_TILES, N_TILES);\n    dim3 block(32, 1);\n    wmma_kernel << <grid, block >> > (device_a, device_b, device_d, M, N, K);\n\n    hipMemcpy(host_d.data(), device_d, M * N * sizeof(float), hipMemcpyDeviceToHost);\n\n    printMatrix(\"Matrix D (Result from GPU)\", host_d.data(), M, N);\n    printMatrix(\"Reference Matrix (Computed on CPU)\", host_ref.data(), M, N);\n\n    float max_error = 0.0f;\n    for (int i = 0; i < M * N; ++i) {\n        max_error = fmax(max_error, fabs(host_d[i] - host_ref[i]));\n    }\n    std::cout << \"Max error: \" << max_error << std::endl;\n\n    hipFree(device_a);\n    hipFree(device_b);\n    hipFree(device_d);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 08/WMMA (Tensor Core).cu"}
{"input": "#include <cuda_runtime.h>\n#include <iostream>\n#include <cstdlib>\n#include <algorithm>\n\n#ifndef DIM\n#define DIM 8192/16\n#endif\n\n#define BLOCK_SIZE 32\n#define R 23\n#define M (DIM / BLOCK_SIZE)\n\n__global__ void naiveMatMulKernel(const float* A, const float* B, float* C, int dim) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < dim && col < dim) {\n        float sum = 0.0f;\n        for (int k = 0; k < dim; k++) {\n            sum += A[row * dim + k] * B[k * dim + col];\n        }\n        C[row * dim + col] = sum;\n    }\n}\n\n__global__ void alphaTensorLargeMatMulKernel(const float* A, const float* B, float* C, \n                                             const float* U, const float* V, const float* W) {\n    int row = blockIdx.y * BLOCK_SIZE + threadIdx.y;\n    int col = blockIdx.x * BLOCK_SIZE + threadIdx.x;\n    if (row >= DIM || col >= DIM) return;\n    \n    __shared__ float s_A[BLOCK_SIZE][BLOCK_SIZE];\n    __shared__ float s_B[BLOCK_SIZE][BLOCK_SIZE];\n    float sum = 0.0f;\n    for (int k = 0; k < M; k++) {\n        s_A[threadIdx.y][threadIdx.x] = A[row * DIM + k * BLOCK_SIZE + threadIdx.x];\n        s_B[threadIdx.y][threadIdx.x] = B[(k * BLOCK_SIZE + threadIdx.y) * DIM + col];\n        __syncthreads();\n        for (int r = 0; r < R; r++) {\n            sum += U[threadIdx.y * R + r] * s_A[threadIdx.y][threadIdx.x] * V[threadIdx.x * R + r] * s_B[threadIdx.y][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    C[row * DIM + col] = sum;\n}\n\nint main() {\n    size_t bytes = DIM * DIM * sizeof(float);\n    float *A, *B, *C_baseline, *C_alphatensor;\n    cudaMallocManaged(&A, bytes);\n    cudaMallocManaged(&B, bytes);\n    cudaMallocManaged(&C_baseline, bytes);\n    cudaMallocManaged(&C_alphatensor, bytes);\n    for (int i = 0; i < DIM * DIM; i++) {\n        A[i] = static_cast<float>(rand()) / RAND_MAX;\n        B[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n\n    dim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 gridDim(DIM / BLOCK_SIZE, DIM / BLOCK_SIZE);\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n    cudaEventRecord(start);\n    naiveMatMulKernel<<<gridDim, blockDim>>>(A, B, C_baseline, DIM);\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n    float millisecondsBaseline;\n    cudaEventElapsedTime(&millisecondsBaseline, start, stop);\n\n    size_t sizeU = BLOCK_SIZE * R * sizeof(float);\n    size_t sizeW = BLOCK_SIZE * BLOCK_SIZE * R * sizeof(float);\n    float *U, *V, *W;\n    cudaMallocManaged(&U, sizeU);\n    cudaMallocManaged(&V, sizeU);\n    cudaMallocManaged(&W, sizeW);\n    for (int i = 0; i < BLOCK_SIZE * R; i++) { U[i] = 1.0f; V[i] = 1.0f; }\n    for (int i = 0; i < BLOCK_SIZE * BLOCK_SIZE * R; i++) { W[i] = 1.0f; }\n    cudaEventRecord(start);\n    alphaTensorLargeMatMulKernel<<<gridDim, blockDim>>>(A, B, C_alphatensor, U, V, W);\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n    float millisecondsAlphaTensor;\n    cudaEventElapsedTime(&millisecondsAlphaTensor, start, stop);\n    float speedup = ((millisecondsBaseline - millisecondsAlphaTensor) / millisecondsBaseline) * 100.0f;\n    std::cout << \"Naive GEMM baseline time: \" << millisecondsBaseline << \" ms\" << std::endl;\n    std::cout << \"AlphaTensor GPU-optimized time: \" << millisecondsAlphaTensor << \" ms\" << std::endl;\n    std::cout << \"AlphaTensor GPU-optimized vs naive GEMM: \" << speedup << \"% speedup\" << std::endl;\n    cudaFree(A);\n    cudaFree(B);\n    cudaFree(C_baseline);\n    cudaFree(C_alphatensor);\n    cudaFree(U);\n    cudaFree(V);\n    cudaFree(W);\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <iostream>\n#include <cstdlib>\n#include <algorithm>\n\n#ifndef DIM\n#define DIM 8192/16\n#endif\n\n#define BLOCK_SIZE 32\n#define R 23\n#define M (DIM / BLOCK_SIZE)\n\n__global__ void naiveMatMulKernel(const float* A, const float* B, float* C, int dim) {\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n    if (row < dim && col < dim) {\n        float sum = 0.0f;\n        for (int k = 0; k < dim; k++) {\n            sum += A[row * dim + k] * B[k * dim + col];\n        }\n        C[row * dim + col] = sum;\n    }\n}\n\n__global__ void alphaTensorLargeMatMulKernel(const float* A, const float* B, float* C, \n                                             const float* U, const float* V, const float* W) {\n    int row = blockIdx.y * BLOCK_SIZE + threadIdx.y;\n    int col = blockIdx.x * BLOCK_SIZE + threadIdx.x;\n    if (row >= DIM || col >= DIM) return;\n    \n    __shared__ float s_A[BLOCK_SIZE][BLOCK_SIZE];\n    __shared__ float s_B[BLOCK_SIZE][BLOCK_SIZE];\n    float sum = 0.0f;\n    for (int k = 0; k < M; k++) {\n        s_A[threadIdx.y][threadIdx.x] = A[row * DIM + k * BLOCK_SIZE + threadIdx.x];\n        s_B[threadIdx.y][threadIdx.x] = B[(k * BLOCK_SIZE + threadIdx.y) * DIM + col];\n        __syncthreads();\n        for (int r = 0; r < R; r++) {\n            sum += U[threadIdx.y * R + r] * s_A[threadIdx.y][threadIdx.x] * V[threadIdx.x * R + r] * s_B[threadIdx.y][threadIdx.x];\n        }\n        __syncthreads();\n    }\n    C[row * DIM + col] = sum;\n}\n\nint main() {\n    size_t bytes = DIM * DIM * sizeof(float);\n    float *A, *B, *C_baseline, *C_alphatensor;\n    hipMallocManaged(&A, bytes);\n    hipMallocManaged(&B, bytes);\n    hipMallocManaged(&C_baseline, bytes);\n    hipMallocManaged(&C_alphatensor, bytes);\n    for (int i = 0; i < DIM * DIM; i++) {\n        A[i] = static_cast<float>(rand()) / RAND_MAX;\n        B[i] = static_cast<float>(rand()) / RAND_MAX;\n    }\n\n    dim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 gridDim(DIM / BLOCK_SIZE, DIM / BLOCK_SIZE);\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n    hipEventRecord(start);\n    naiveMatMulKernel<<<gridDim, blockDim>>>(A, B, C_baseline, DIM);\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n    float millisecondsBaseline;\n    hipEventElapsedTime(&millisecondsBaseline, start, stop);\n\n    size_t sizeU = BLOCK_SIZE * R * sizeof(float);\n    size_t sizeW = BLOCK_SIZE * BLOCK_SIZE * R * sizeof(float);\n    float *U, *V, *W;\n    hipMallocManaged(&U, sizeU);\n    hipMallocManaged(&V, sizeU);\n    hipMallocManaged(&W, sizeW);\n    for (int i = 0; i < BLOCK_SIZE * R; i++) { U[i] = 1.0f; V[i] = 1.0f; }\n    for (int i = 0; i < BLOCK_SIZE * BLOCK_SIZE * R; i++) { W[i] = 1.0f; }\n    hipEventRecord(start);\n    alphaTensorLargeMatMulKernel<<<gridDim, blockDim>>>(A, B, C_alphatensor, U, V, W);\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n    float millisecondsAlphaTensor;\n    hipEventElapsedTime(&millisecondsAlphaTensor, start, stop);\n    float speedup = ((millisecondsBaseline - millisecondsAlphaTensor) / millisecondsBaseline) * 100.0f;\n    std::cout << \"Naive GEMM baseline time: \" << millisecondsBaseline << \" ms\" << std::endl;\n    std::cout << \"AlphaTensor GPU-optimized time: \" << millisecondsAlphaTensor << \" ms\" << std::endl;\n    std::cout << \"AlphaTensor GPU-optimized vs naive GEMM: \" << speedup << \"% speedup\" << std::endl;\n    hipFree(A);\n    hipFree(B);\n    hipFree(C_baseline);\n    hipFree(C_alphatensor);\n    hipFree(U);\n    hipFree(V);\n    hipFree(W);\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 37/optimized_alphatensor.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define GAMMA 0.9\n#define THETA 1e-6\n\n__global__ void valueIterationStep(float *V, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float v_old = V[s];\n    float best_value = -1e9;\n    \n    for (int a = 0; a < ACTIONS; a++) {\n        float q_sa = 0.0;\n        for (int next_s = 0; next_s < STATES; next_s++) {\n            q_sa += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n        }\n        if (q_sa > best_value) {\n            best_value = q_sa;\n        }\n    }\n    V[s] = best_value;\n    \n    if (fabs(v_old - V[s]) > THETA) *stable = false;\n}\n\nvoid valueIteration() {\n    float V[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    bool *d_stable, stable;\n    cudaMalloc(&d_V, STATES * sizeof(float));\n    cudaMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_stable, sizeof(bool));\n    \n    cudaMemcpy(d_V, V, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    \n    int iteration = 0;\n    do {\n        stable = true;\n        cudaMemcpy(d_stable, &stable, sizeof(bool), cudaMemcpyHostToDevice);\n        valueIterationStep<<<1, STATES>>>(d_V, d_rewards, d_transitions, d_stable);\n        cudaMemcpy(&stable, d_stable, sizeof(bool), cudaMemcpyDeviceToHost);\n        \n        cudaMemcpy(V, d_V, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n        printf(\"Iteration %d - V-values: \", iteration++);\n        for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n        printf(\"\\n\");\n    } while (!stable);\n    \n    printf(\"Optimal Value Function:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n    printf(\"\\n\");\n    \n    cudaFree(d_V);\n    cudaFree(d_rewards);\n    cudaFree(d_transitions);\n    cudaFree(d_stable);\n}\n\nint main() {\n    valueIteration();\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define GAMMA 0.9\n#define THETA 1e-6\n\n__global__ void valueIterationStep(float *V, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float v_old = V[s];\n    float best_value = -1e9;\n    \n    for (int a = 0; a < ACTIONS; a++) {\n        float q_sa = 0.0;\n        for (int next_s = 0; next_s < STATES; next_s++) {\n            q_sa += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n        }\n        if (q_sa > best_value) {\n            best_value = q_sa;\n        }\n    }\n    V[s] = best_value;\n    \n    if (fabs(v_old - V[s]) > THETA) *stable = false;\n}\n\nvoid valueIteration() {\n    float V[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    bool *d_stable, stable;\n    hipMalloc(&d_V, STATES * sizeof(float));\n    hipMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_stable, sizeof(bool));\n    \n    hipMemcpy(d_V, V, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    \n    int iteration = 0;\n    do {\n        stable = true;\n        hipMemcpy(d_stable, &stable, sizeof(bool), hipMemcpyHostToDevice);\n        valueIterationStep<<<1, STATES>>>(d_V, d_rewards, d_transitions, d_stable);\n        hipMemcpy(&stable, d_stable, sizeof(bool), hipMemcpyDeviceToHost);\n        \n        hipMemcpy(V, d_V, STATES * sizeof(float), hipMemcpyDeviceToHost);\n        printf(\"Iteration %d - V-values: \", iteration++);\n        for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n        printf(\"\\n\");\n    } while (!stable);\n    \n    printf(\"Optimal Value Function:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n    printf(\"\\n\");\n    \n    hipFree(d_V);\n    hipFree(d_rewards);\n    hipFree(d_transitions);\n    hipFree(d_stable);\n}\n\nint main() {\n    valueIteration();\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 59/value_iteration.cu"}
{"input": "#include <stdio.h>\n#include <cuda_runtime.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define GAMMA 0.9\n#define THETA 1e-6\n\n__global__ void evaluatePolicy(float *V, int *policy, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float v_old = V[s];\n    \n    int a = policy[s];\n    V[s] = 0.0;\n    for (int next_s = 0; next_s < STATES; next_s++) {\n        V[s] += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n    }\n    \n    if (fabs(v_old - V[s]) > THETA) *stable = false;\n}\n\n__global__ void improvePolicy(float *V, int *policy, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float best_value = -1e9;\n    int best_action = 0;\n    \n    for (int a = 0; a < ACTIONS; a++) {\n        float q_sa = 0.0;\n        for (int next_s = 0; next_s < STATES; next_s++) {\n            q_sa += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n        }\n        if (q_sa > best_value) {\n            best_value = q_sa;\n            best_action = a;\n        }\n    }\n    \n    if (policy[s] != best_action) {\n        policy[s] = best_action;\n        *stable = false;\n    }\n}\n\nvoid policyIteration() {\n    float V[STATES] = {0};\n    int policy[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    int *d_policy;\n    bool *d_stable, stable;\n    cudaMalloc(&d_V, STATES * sizeof(float));\n    cudaMalloc(&d_policy, STATES * sizeof(int));\n    cudaMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    cudaMalloc(&d_stable, sizeof(bool));\n    \n    cudaMemcpy(d_V, V, STATES * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_policy, policy, STATES * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), cudaMemcpyHostToDevice);\n    \n    int iteration = 0;\n    do {\n        stable = true;\n        cudaMemcpy(d_stable, &stable, sizeof(bool), cudaMemcpyHostToDevice);\n        evaluatePolicy<<<1, STATES>>>(d_V, d_policy, d_rewards, d_transitions, d_stable);\n        improvePolicy<<<1, STATES>>>(d_V, d_policy, d_rewards, d_transitions, d_stable);\n        cudaMemcpy(&stable, d_stable, sizeof(bool), cudaMemcpyDeviceToHost);\n        \n        cudaMemcpy(V, d_V, STATES * sizeof(float), cudaMemcpyDeviceToHost);\n        printf(\"Iteration %d - V-values: \", iteration++);\n        for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n        printf(\"\\n\");\n    } while (!stable);\n    \n    cudaMemcpy(policy, d_policy, STATES * sizeof(int), cudaMemcpyDeviceToHost);\n    \n    printf(\"Optimal Policy:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%d \", policy[s]);\n    printf(\"\\n\");\n    \n    cudaFree(d_V);\n    cudaFree(d_policy);\n    cudaFree(d_rewards);\n    cudaFree(d_transitions);\n    cudaFree(d_stable);\n}\n\nint main() {\n    policyIteration();\n    return 0;\n}\n", "output": "#include <stdio.h>\n#include <hip/hip_runtime.h>\n\n#define STATES 6\n#define ACTIONS 2\n#define GAMMA 0.9\n#define THETA 1e-6\n\n__global__ void evaluatePolicy(float *V, int *policy, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float v_old = V[s];\n    \n    int a = policy[s];\n    V[s] = 0.0;\n    for (int next_s = 0; next_s < STATES; next_s++) {\n        V[s] += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n    }\n    \n    if (fabs(v_old - V[s]) > THETA) *stable = false;\n}\n\n__global__ void improvePolicy(float *V, int *policy, float *rewards, float *transitions, bool *stable) {\n    int s = threadIdx.x;\n    float best_value = -1e9;\n    int best_action = 0;\n    \n    for (int a = 0; a < ACTIONS; a++) {\n        float q_sa = 0.0;\n        for (int next_s = 0; next_s < STATES; next_s++) {\n            q_sa += transitions[s * STATES * ACTIONS + a * STATES + next_s] * (rewards[s * ACTIONS + a] + GAMMA * V[next_s]);\n        }\n        if (q_sa > best_value) {\n            best_value = q_sa;\n            best_action = a;\n        }\n    }\n    \n    if (policy[s] != best_action) {\n        policy[s] = best_action;\n        *stable = false;\n    }\n}\n\nvoid policyIteration() {\n    float V[STATES] = {0};\n    int policy[STATES] = {0};\n    float rewards[STATES * ACTIONS] = {0, 1, 0, 2, 0, 3, 0, 4, 1, 5, 0, 6};\n    float transitions[STATES * STATES * ACTIONS] = {0.6, 0.4, 0, 0, 0, 0,  0.5, 0.5, 0, 0, 0, 0,\n                                                     0, 0, 0.7, 0.3, 0, 0,  0, 0, 0.6, 0.4, 0, 0,\n                                                     0, 0, 0, 0, 0.8, 0.2,  0, 0, 0, 0, 0.5, 0.5};\n    \n    float *d_V, *d_rewards, *d_transitions;\n    int *d_policy;\n    bool *d_stable, stable;\n    hipMalloc(&d_V, STATES * sizeof(float));\n    hipMalloc(&d_policy, STATES * sizeof(int));\n    hipMalloc(&d_rewards, STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_transitions, STATES * STATES * ACTIONS * sizeof(float));\n    hipMalloc(&d_stable, sizeof(bool));\n    \n    hipMemcpy(d_V, V, STATES * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_policy, policy, STATES * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_rewards, rewards, STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_transitions, transitions, STATES * STATES * ACTIONS * sizeof(float), hipMemcpyHostToDevice);\n    \n    int iteration = 0;\n    do {\n        stable = true;\n        hipMemcpy(d_stable, &stable, sizeof(bool), hipMemcpyHostToDevice);\n        evaluatePolicy<<<1, STATES>>>(d_V, d_policy, d_rewards, d_transitions, d_stable);\n        improvePolicy<<<1, STATES>>>(d_V, d_policy, d_rewards, d_transitions, d_stable);\n        hipMemcpy(&stable, d_stable, sizeof(bool), hipMemcpyDeviceToHost);\n        \n        hipMemcpy(V, d_V, STATES * sizeof(float), hipMemcpyDeviceToHost);\n        printf(\"Iteration %d - V-values: \", iteration++);\n        for (int s = 0; s < STATES; s++) printf(\"%.4f \", V[s]);\n        printf(\"\\n\");\n    } while (!stable);\n    \n    hipMemcpy(policy, d_policy, STATES * sizeof(int), hipMemcpyDeviceToHost);\n    \n    printf(\"Optimal Policy:\\n\");\n    for (int s = 0; s < STATES; s++) printf(\"%d \", policy[s]);\n    printf(\"\\n\");\n    \n    hipFree(d_V);\n    hipFree(d_policy);\n    hipFree(d_rewards);\n    hipFree(d_transitions);\n    hipFree(d_stable);\n}\n\nint main() {\n    policyIteration();\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 59/policy_iteration.cu"}
{"input": "#include <cuda_runtime.h>\n#include <curand_kernel.h>\n#include <iostream>\n\n#define N_ARMS 10\n#define N_TRIALS 1000\n#define EPSILON 0.1\n\n__global__ void bandit_kernel(float *estimates, int *counts, curandState *states, int n_trials) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    curandState localState;\n    \n    if (tid == 0) {\n        curand_init(1234, tid, 0, &localState);\n\n        for (int t = 0; t < n_trials; t++) {\n            float rand_val = curand_uniform(&localState);\n            int action;\n            if (rand_val < EPSILON) {\n                action = curand(&localState) % N_ARMS;\n            } else {\n                action = 0;\n                float max_estimate = estimates[0];\n                for (int i = 1; i < N_ARMS; i++) {\n                    if (estimates[i] > max_estimate) {\n                        max_estimate = estimates[i];\n                        action = i;\n                    }\n                }\n            }\n\n            float reward = curand_uniform(&localState);\n            counts[action]++;\n            estimates[action] += (reward - estimates[action]) / counts[action];\n        }\n    }\n}\n\nint main() {\n    float *d_estimates, h_estimates[N_ARMS] = {0};\n    int *d_counts, h_counts[N_ARMS] = {0};\n    curandState *d_states;\n\n    cudaMalloc(&d_estimates, N_ARMS * sizeof(float));\n    cudaMalloc(&d_counts, N_ARMS * sizeof(int));\n    cudaMalloc(&d_states, sizeof(curandState));\n\n    cudaMemcpy(d_estimates, h_estimates, N_ARMS * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_counts, h_counts, N_ARMS * sizeof(int), cudaMemcpyHostToDevice);\n\n    bandit_kernel<<<1, 1>>>(d_estimates, d_counts, d_states, N_TRIALS);\n    cudaDeviceSynchronize();\n\n    cudaMemcpy(h_estimates, d_estimates, N_ARMS * sizeof(float), cudaMemcpyDeviceToHost);\n    cudaMemcpy(h_counts, d_counts, N_ARMS * sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Final Action Value Estimates:\\n\";\n    for (int i = 0; i < N_ARMS; i++) {\n        std::cout << \"Arm \" << i << \": \" << h_estimates[i] << \" (Chosen \" << h_counts[i] << \" times)\\n\";\n    }\n\n    cudaFree(d_estimates);\n    cudaFree(d_counts);\n    cudaFree(d_states);\n\n    return 0;\n}\n", "output": "#include <hip/hip_runtime.h>\n#include <hiprand/hiprand_kernel.h>\n#include <iostream>\n\n#define N_ARMS 10\n#define N_TRIALS 1000\n#define EPSILON 0.1\n\n__global__ void bandit_kernel(float *estimates, int *counts, hiprandState *states, int n_trials) {\n    int tid = threadIdx.x + blockIdx.x * blockDim.x;\n    hiprandState localState;\n    \n    if (tid == 0) {\n        hiprand_init(1234, tid, 0, &localState);\n\n        for (int t = 0; t < n_trials; t++) {\n            float rand_val = hiprand_uniform(&localState);\n            int action;\n            if (rand_val < EPSILON) {\n                action = hiprand(&localState) % N_ARMS;\n            } else {\n                action = 0;\n                float max_estimate = estimates[0];\n                for (int i = 1; i < N_ARMS; i++) {\n                    if (estimates[i] > max_estimate) {\n                        max_estimate = estimates[i];\n                        action = i;\n                    }\n                }\n            }\n\n            float reward = hiprand_uniform(&localState);\n            counts[action]++;\n            estimates[action] += (reward - estimates[action]) / counts[action];\n        }\n    }\n}\n\nint main() {\n    float *d_estimates, h_estimates[N_ARMS] = {0};\n    int *d_counts, h_counts[N_ARMS] = {0};\n    hiprandState *d_states;\n\n    hipMalloc(&d_estimates, N_ARMS * sizeof(float));\n    hipMalloc(&d_counts, N_ARMS * sizeof(int));\n    hipMalloc(&d_states, sizeof(hiprandState));\n\n    hipMemcpy(d_estimates, h_estimates, N_ARMS * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_counts, h_counts, N_ARMS * sizeof(int), hipMemcpyHostToDevice);\n\n    bandit_kernel<<<1, 1>>>(d_estimates, d_counts, d_states, N_TRIALS);\n    hipDeviceSynchronize();\n\n    hipMemcpy(h_estimates, d_estimates, N_ARMS * sizeof(float), hipMemcpyDeviceToHost);\n    hipMemcpy(h_counts, d_counts, N_ARMS * sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Final Action Value Estimates:\\n\";\n    for (int i = 0; i < N_ARMS; i++) {\n        std::cout << \"Arm \" << i << \": \" << h_estimates[i] << \" (Chosen \" << h_counts[i] << \" times)\\n\";\n    }\n\n    hipFree(d_estimates);\n    hipFree(d_counts);\n    hipFree(d_states);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 52/multi_armed_bandit.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cuda_fp16.h>  // FP16 support for Tensor Cores\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// Warp reduction for max operation (FP16)\n__device__ __half warpReduceMax(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hmax(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation (FP16)\n__device__ __half warpReduceSum(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hadd(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel using Tensor Cores (FP16)\n__global__ void softmaxTensorCore(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];\n\n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n\n    // Step 1: Compute max for numerical stability\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n    max_val = warpReduceMax(max_val);\n\n    // Step 2: Compute exponentials and sum\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));  // \u2705 Corrected\n        sum = __hadd(sum, shared_row[i]);\n    }\n    sum = warpReduceSum(sum);\n\n    // Step 3: Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function to launch kernel\nvoid softmaxFP16(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n\n    // Convert FP32 input to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Copy data to device\n    cudaMemcpy(d_input, h_input_half, size, cudaMemcpyHostToDevice);\n\n      // Create events for timing\n      cudaEvent_t start, stop;\n      cudaEventCreate(&start);\n      cudaEventCreate(&stop);\n  \n      // Record the start time\n      cudaEventRecord(start);\n\n    // Launch optimized softmax kernel\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxTensorCore<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n\n     // Record the stop time\n     cudaEventRecord(stop);\n     cudaEventSynchronize(stop);\n\n      // Calculate execution time in milliseconds\n    float elapsedTime;\n    cudaEventElapsedTime(&elapsedTime, start, stop);\n    float timeInSeconds = elapsedTime / 1000.0f;\n\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    cudaMemcpy(h_output_half, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Convert FP16 output to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    delete[] h_input_half;\n    delete[] h_output_half;\n    cudaFree(d_input);\n    cudaFree(d_output);\n    \n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using Tensor Core acceleration\n    softmaxFP16(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>  // FP16 support for Tensor Cores\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// Warp reduction for max operation (FP16)\n__device__ __half warpReduceMax(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hmax(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation (FP16)\n__device__ __half warpReduceSum(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hadd(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Optimized Softmax Kernel using Tensor Cores (FP16)\n__global__ void softmaxTensorCore(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];\n\n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n\n    // Step 1: Compute max for numerical stability\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n    max_val = warpReduceMax(max_val);\n\n    // Step 2: Compute exponentials and sum\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));  // \u2705 Corrected\n        sum = __hadd(sum, shared_row[i]);\n    }\n    sum = warpReduceSum(sum);\n\n    // Step 3: Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function to launch kernel\nvoid softmaxFP16(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n\n    // Convert FP32 input to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Copy data to device\n    hipMemcpy(d_input, h_input_half, size, hipMemcpyHostToDevice);\n\n      // Create events for timing\n      hipEvent_t start, stop;\n      hipEventCreate(&start);\n      hipEventCreate(&stop);\n  \n      // Record the start time\n      hipEventRecord(start);\n\n    // Launch optimized softmax kernel\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxTensorCore<<<rows, BLOCK_SIZE, shared_mem_size>>>(d_input, d_output, rows, cols);\n\n     // Record the stop time\n     hipEventRecord(stop);\n     hipEventSynchronize(stop);\n\n      // Calculate execution time in milliseconds\n    float elapsedTime;\n    hipEventElapsedTime(&elapsedTime, start, stop);\n    float timeInSeconds = elapsedTime / 1000.0f;\n\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    hipMemcpy(h_output_half, d_output, size, hipMemcpyDeviceToHost);\n\n    // Convert FP16 output to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    delete[] h_input_half;\n    delete[] h_output_half;\n    hipFree(d_input);\n    hipFree(d_output);\n    \n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using Tensor Core acceleration\n    softmaxFP16(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n\n}\n", "source": "/kaggle/working/100-days-cuda/Day 13/TensorCore_SoftMax.cu"}
{"input": "\ufeff#include <stdio.h>\n#include <cuda_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <device_launch_parameters.h>\n\n__global__ void vectorAddUM(int* a, int* b, int* c, int n) {\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\n\tif (tid < n) {\n\t\tc[tid] = a[tid] + b[tid];\n\t}\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid init_vector(int* a, int* b, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t\tb[i] = rand() % 100;\n\n\t}\n}\nvoid check_answer(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n\n\t//get ID from cuda Calls \n\tint id;\n\tcudaGetDevice(&id);\n\n\t//elements per array\n\tint n = 1 << 16;\n\n\t//declare unified memory pointers\n\n\tint* a, * b, * c;\n\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\n\tcudaMallocManaged(&a, bytes);\n\tcudaMallocManaged(&b, bytes);\n\tcudaMallocManaged(&c, bytes);\n\n\t//initialize vectors \n\tinit_vector(a, b, n);\n\n\t//threadblock size\n\n\tint BLOCK_SIZE = 256;\n\n\t//Grid SIze\n\n\tint GRID_SIZE = (n + BLOCK_SIZE - 1) / BLOCK_SIZE;\n\n\n\tcudaMemPrefetchAsync(a, bytes, id);\n\tcudaMemPrefetchAsync(b, bytes, id);\n\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAddUM << <GRID_SIZE, BLOCK_SIZE >> > (a, b, c, n);\n\n\t//waitforall operations\n\n\tcudaDeviceSynchronize();\n\n\tcudaMemPrefetchAsync(c, bytes, cudaCpuDeviceId);\n\n\tcheck_answer(a, b, c, n);\n\n\tprintf(\"Completed successfully , result of sum is %d\\n\", c[0]);\n\n\treturn 0;\n}\n", "output": "\ufeff#include <stdio.h>\n#include <hip/hip_runtime.h>\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n#include <>\n\n__global__ void vectorAddUM(int* a, int* b, int* c, int n) {\n\t//global thread index (tid)\n\tint tid = (blockIdx.x * blockDim.x) + threadIdx.x;\n\tif (tid < n) {\n\t\tc[tid] = a[tid] + b[tid];\n\t}\n}\n//initialize vector of size n to int b/w 0-99\n\nvoid init_vector(int* a, int* b, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\ta[i] = rand() % 100;\n\t\tb[i] = rand() % 100;\n\n\t}\n}\nvoid check_answer(int* a, int* b, int* c, int n) {\n\tfor (int i = 0; i < n; i++) {\n\t\tassert(c[i] == a[i] + b[i]);\n\t}\n}\n\nint main() {\n\n\t//get ID from cuda Calls \n\tint id;\n\thipGetDevice(&id);\n\n\t//elements per array\n\tint n = 1 << 16;\n\n\t//declare unified memory pointers\n\n\tint* a, * b, * c;\n\n\t//Alocation size for all vectors\n\tsize_t bytes = sizeof(int) * n;\n\n\t//Allocate host memory\n\n\thipMallocManaged(&a, bytes);\n\thipMallocManaged(&b, bytes);\n\thipMallocManaged(&c, bytes);\n\n\t//initialize vectors \n\tinit_vector(a, b, n);\n\n\t//threadblock size\n\n\tint BLOCK_SIZE = 256;\n\n\t//Grid SIze\n\n\tint GRID_SIZE = (n + BLOCK_SIZE - 1) / BLOCK_SIZE;\n\n\n\thipMemPrefetchAsync(a, bytes, id);\n\thipMemPrefetchAsync(b, bytes, id);\n\n\n\t//Launch Kernel on default stream w/o shmem\n\tvectorAddUM << <GRID_SIZE, BLOCK_SIZE >> > (a, b, c, n);\n\n\t//waitforall operations\n\n\thipDeviceSynchronize();\n\n\thipMemPrefetchAsync(c, bytes, hipCpuDeviceId);\n\n\tcheck_answer(a, b, c, n);\n\n\tprintf(\"Completed successfully , result of sum is %d\\n\", c[0]);\n\n\treturn 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 05/BetterPerformance_UnifiedMemory_VectorAddition.cu"}
{"input": "\ufeff#include <algorithm>\n#include <cassert>\n#include <cstdlib>\n#include <functional>\n#include <iostream>\n#include <vector>\n\nusing std::cout;\nusing std::generate;\nusing std::vector;\n\nconst int N = 1 << 10;\nconst int SHMEM_SIZE = 1 << 10;\n\n__global__ void matrixMul(const int* a, const int* b, int* c) {\n\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    __shared__ int s_a[SHMEM_SIZE];\n    __shared__ int s_b[SHMEM_SIZE];\n\n    int tmp = 0;\n\n    for (int i = 0; i < N; i += blockDim.x) {\n  \n        s_a[threadIdx.y * blockDim.x + threadIdx.x] = a[row * N + i + threadIdx.x];\n        s_b[threadIdx.y * blockDim.x + threadIdx.x] =\n            b[i * N + threadIdx.y * N + col];\n\n        __syncthreads();\n\n        for (int j = 0; j < blockDim.x; j++) {\n            tmp +=\n                s_a[threadIdx.y * blockDim.x + j] * s_b[j * blockDim.x + threadIdx.x];\n        }\n\n        __syncthreads();\n    }\n\n    c[row * N + col] = tmp;\n}\n\nvoid verify_result(vector<int>& a, vector<int>& b, vector<int>& c) {\n\n    for (int i = 0; i < N; i++) {\n\n        for (int j = 0; j < N; j++) {\n            int tmp = 0;\n            for (int k = 0; k < N; k++) {\n                tmp += a[i * N + k] * b[k * N + j];\n            }\n            assert(tmp == c[i * N + j]);\n        }\n    }\n}\n\nint main() {\n\n    size_t bytes = N * N * sizeof(int);\n\n    vector<int> h_a(N * N);\n    vector<int> h_b(N * N);\n    vector<int> h_c(N * N);\n\n    generate(h_a.begin(), h_a.end(), []() { return rand() % 100; });\n    generate(h_b.begin(), h_b.end(), []() { return rand() % 100; });\n\n    int* d_a, * d_b, * d_c;\n    cudaMalloc(&d_a, bytes);\n    cudaMalloc(&d_b, bytes);\n    cudaMalloc(&d_c, bytes);\n\n    cudaMemcpy(d_a, h_a.data(), bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, h_b.data(), bytes, cudaMemcpyHostToDevice);\n\n    int THREADS = 32;\n    int BLOCKS = N / THREADS;\n\n    dim3 threads(THREADS, THREADS);\n    dim3 blocks(BLOCKS, BLOCKS);\n\n    matrixMul << <blocks, threads >> > (d_a, d_b, d_c);\n\n    cudaMemcpy(h_c.data(), d_c, bytes, cudaMemcpyDeviceToHost);\n\n    verify_result(h_a, h_b, h_c);\n\n     printf(\"The Result is %d\\n\", d_c);\n\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n\ufeff#include <algorithm>\n#include <cassert>\n#include <cstdlib>\n#include <functional>\n#include <iostream>\n#include <vector>\n\nusing std::cout;\nusing std::generate;\nusing std::vector;\n\nconst int N = 1 << 10;\nconst int SHMEM_SIZE = 1 << 10;\n\n__global__ void matrixMul(const int* a, const int* b, int* c) {\n\n    int row = blockIdx.y * blockDim.y + threadIdx.y;\n    int col = blockIdx.x * blockDim.x + threadIdx.x;\n\n    __shared__ int s_a[SHMEM_SIZE];\n    __shared__ int s_b[SHMEM_SIZE];\n\n    int tmp = 0;\n\n    for (int i = 0; i < N; i += blockDim.x) {\n  \n        s_a[threadIdx.y * blockDim.x + threadIdx.x] = a[row * N + i + threadIdx.x];\n        s_b[threadIdx.y * blockDim.x + threadIdx.x] =\n            b[i * N + threadIdx.y * N + col];\n\n        __syncthreads();\n\n        for (int j = 0; j < blockDim.x; j++) {\n            tmp +=\n                s_a[threadIdx.y * blockDim.x + j] * s_b[j * blockDim.x + threadIdx.x];\n        }\n\n        __syncthreads();\n    }\n\n    c[row * N + col] = tmp;\n}\n\nvoid verify_result(vector<int>& a, vector<int>& b, vector<int>& c) {\n\n    for (int i = 0; i < N; i++) {\n\n        for (int j = 0; j < N; j++) {\n            int tmp = 0;\n            for (int k = 0; k < N; k++) {\n                tmp += a[i * N + k] * b[k * N + j];\n            }\n            assert(tmp == c[i * N + j]);\n        }\n    }\n}\n\nint main() {\n\n    size_t bytes = N * N * sizeof(int);\n\n    vector<int> h_a(N * N);\n    vector<int> h_b(N * N);\n    vector<int> h_c(N * N);\n\n    generate(h_a.begin(), h_a.end(), []() { return rand() % 100; });\n    generate(h_b.begin(), h_b.end(), []() { return rand() % 100; });\n\n    int* d_a, * d_b, * d_c;\n    hipMalloc(&d_a, bytes);\n    hipMalloc(&d_b, bytes);\n    hipMalloc(&d_c, bytes);\n\n    hipMemcpy(d_a, h_a.data(), bytes, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, h_b.data(), bytes, hipMemcpyHostToDevice);\n\n    int THREADS = 32;\n    int BLOCKS = N / THREADS;\n\n    dim3 threads(THREADS, THREADS);\n    dim3 blocks(BLOCKS, BLOCKS);\n\n    matrixMul << <blocks, threads >> > (d_a, d_b, d_c);\n\n    hipMemcpy(h_c.data(), d_c, bytes, hipMemcpyDeviceToHost);\n\n    verify_result(h_a, h_b, h_c);\n\n     printf(\"The Result is %d\\n\", d_c);\n\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 05/Tiled_mat_mul.cu"}
{"input": "\ufeff#include <iostream>\n#include <cuda_runtime.h>\n#include <stdio.h>\n#include <cuda.h>\n#include <device_launch_parameters.h>\n#include <stdlib.h>\n#include <math.h>\n#include <assert.h>\n\n#define BLOCK_SIZE 8  // This remains unchanged to support larger matrices\n\n// CUDA Kernel for Matrix Transposition using Shared Memory\n__global__ void transposeKernel(float* input, float* output, int width, int height) {\n    __shared__ float tile[BLOCK_SIZE][BLOCK_SIZE + 1]; // Padding to avoid bank conflicts\n\n    int x = blockIdx.x * BLOCK_SIZE + threadIdx.x;\n    int y = blockIdx.y * BLOCK_SIZE + threadIdx.y;\n\n    if (x < width && y < height) {\n        tile[threadIdx.y][threadIdx.x] = input[y * width + x];\n    }\n\n    __syncthreads();\n\n    int transposedX = blockIdx.y * BLOCK_SIZE + threadIdx.x;\n    int transposedY = blockIdx.x * BLOCK_SIZE + threadIdx.y;\n\n    if (transposedX < height && transposedY < width) {\n        output[transposedY * height + transposedX] = tile[threadIdx.x][threadIdx.y];\n    }\n}\n\n// Function to print matrices\nvoid printMatrix(const char* name, float* matrix, int width, int height) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < height; ++i) {\n        for (int j = 0; j < width; ++j) {\n            std::cout << matrix[i * width + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\n// Host function to perform matrix transposition\nvoid transpose(float* h_input, float* h_output, int width, int height, float& time) {\n    float* d_input, * d_output;\n\n    cudaMalloc((void**)&d_input, width * height * sizeof(float));\n    cudaMalloc((void**)&d_output, width * height * sizeof(float));\n\n    cudaMemcpy(d_input, h_input, width * height * sizeof(float), cudaMemcpyHostToDevice);\n\n    dim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 gridDim((width + BLOCK_SIZE - 1) / BLOCK_SIZE, (height + BLOCK_SIZE - 1) / BLOCK_SIZE);\n\n    cudaEvent_t start, stop;\n    cudaEventCreate(&start);\n    cudaEventCreate(&stop);\n\n    cudaEventRecord(start);\n\n    transposeKernel <<< gridDim, blockDim >>> (d_input, d_output, width, height);\n    cudaDeviceSynchronize(); // Ensure kernel execution completes before timing\n\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n    cudaEventElapsedTime(&time, start, stop);\n\n    cudaMemcpy(h_output, d_output, width * height * sizeof(float), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n    cudaEventDestroy(start);\n    cudaEventDestroy(stop);\n}\n\nint main() {\n    int width = 3;\n    int height = 3;\n\n    float h_input[9] = { 1, 2, 3, \n                         4, 5, 6, \n                         7, 8, 9 };\n\n    float h_output[9] = { 0 };\n\n    float time = 0.0f;\n    transpose(h_input, h_output, width, height, time);\n\n    // Print matrices\n    printMatrix(\"Input Matrix\", h_input, width, height);\n    printMatrix(\"Transposed Matrix\", h_output, height, width);\n\n    std::cout << \"Optimized Kernel Execution time: \" << time << \" ms\\n\";\n\n    return 0;\n}\n", "output": "\ufeff#include <iostream>\n#include <hip/hip_runtime.h>\n#include <stdio.h>\n#include <>\n#include <stdlib.h>\n#include <math.h>\n#include <assert.h>\n\n#define BLOCK_SIZE 8  // This remains unchanged to support larger matrices\n\n// CUDA Kernel for Matrix Transposition using Shared Memory\n__global__ void transposeKernel(float* input, float* output, int width, int height) {\n    __shared__ float tile[BLOCK_SIZE][BLOCK_SIZE + 1]; // Padding to avoid bank conflicts\n\n    int x = blockIdx.x * BLOCK_SIZE + threadIdx.x;\n    int y = blockIdx.y * BLOCK_SIZE + threadIdx.y;\n\n    if (x < width && y < height) {\n        tile[threadIdx.y][threadIdx.x] = input[y * width + x];\n    }\n\n    __syncthreads();\n\n    int transposedX = blockIdx.y * BLOCK_SIZE + threadIdx.x;\n    int transposedY = blockIdx.x * BLOCK_SIZE + threadIdx.y;\n\n    if (transposedX < height && transposedY < width) {\n        output[transposedY * height + transposedX] = tile[threadIdx.x][threadIdx.y];\n    }\n}\n\n// Function to print matrices\nvoid printMatrix(const char* name, float* matrix, int width, int height) {\n    std::cout << name << \":\\n\";\n    for (int i = 0; i < height; ++i) {\n        for (int j = 0; j < width; ++j) {\n            std::cout << matrix[i * width + j] << \"\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << std::endl;\n}\n\n// Host function to perform matrix transposition\nvoid transpose(float* h_input, float* h_output, int width, int height, float& time) {\n    float* d_input, * d_output;\n\n    hipMalloc((void**)&d_input, width * height * sizeof(float));\n    hipMalloc((void**)&d_output, width * height * sizeof(float));\n\n    hipMemcpy(d_input, h_input, width * height * sizeof(float), hipMemcpyHostToDevice);\n\n    dim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);\n    dim3 gridDim((width + BLOCK_SIZE - 1) / BLOCK_SIZE, (height + BLOCK_SIZE - 1) / BLOCK_SIZE);\n\n    hipEvent_t start, stop;\n    hipEventCreate(&start);\n    hipEventCreate(&stop);\n\n    hipEventRecord(start);\n\n    transposeKernel <<< gridDim, blockDim >>> (d_input, d_output, width, height);\n    hipDeviceSynchronize(); // Ensure kernel execution completes before timing\n\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n    hipEventElapsedTime(&time, start, stop);\n\n    hipMemcpy(h_output, d_output, width * height * sizeof(float), hipMemcpyDeviceToHost);\n\n    hipFree(d_input);\n    hipFree(d_output);\n\n    hipEventDestroy(start);\n    hipEventDestroy(stop);\n}\n\nint main() {\n    int width = 3;\n    int height = 3;\n\n    float h_input[9] = { 1, 2, 3, \n                         4, 5, 6, \n                         7, 8, 9 };\n\n    float h_output[9] = { 0 };\n\n    float time = 0.0f;\n    transpose(h_input, h_output, width, height, time);\n\n    // Print matrices\n    printMatrix(\"Input Matrix\", h_input, width, height);\n    printMatrix(\"Transposed Matrix\", h_output, height, width);\n\n    std::cout << \"Optimized Kernel Execution time: \" << time << \" ms\\n\";\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 06/optimized_Tranposed_matmul.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n#include <cuda_fp16.h>\n#include <cuda.h>\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// Warp reduction for max operation (FP16)\n__device__ __half warpReduceMax(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hmax(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation (FP16)\n__device__ __half warpReduceSum(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hadd(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Softmax Kernel using Tensor Cores and CUDA Graphs\n__global__ void softmaxKernel(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];\n\n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n\n    // Step 1: Compute max for numerical stability\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n    max_val = warpReduceMax(max_val);\n\n    // Step 2: Compute exponentials and sum\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));\n        sum = __hadd(sum, shared_row[i]);\n    }\n    sum = warpReduceSum(sum);\n\n    // Step 3: Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function with CUDA Graphs\nvoid softmaxWithCUDAgraphs(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Convert FP32 to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Allocate device memory\n    cudaMalloc((void**)&d_input, size);\n    cudaMalloc((void**)&d_output, size);\n    cudaMemcpy(d_input, h_input_half, size, cudaMemcpyHostToDevice);\n\n      // Create events for timing\n      cudaEvent_t start, stop;\n      cudaEventCreate(&start);\n      cudaEventCreate(&stop);\n  \n      // Record the start time\n      cudaEventRecord(start);\n\n    // Define CUDA Graph\n    cudaGraph_t graph;\n    cudaGraphExec_t instance;\n    cudaStream_t stream;\n    cudaStreamCreate(&stream);\n\n    // Capture Graph\n    cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal);\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxKernel<<<rows, BLOCK_SIZE, shared_mem_size, stream>>>(d_input, d_output, rows, cols);\n    cudaStreamEndCapture(stream, &graph);\n\n    // Instantiate and launch graph\n    cudaGraphInstantiate(&instance, graph, NULL, NULL, 0);\n    cudaGraphLaunch(instance, stream);\n    cudaStreamSynchronize(stream);\n\n    // Record the stop time\n    cudaEventRecord(stop);\n    cudaEventSynchronize(stop);\n\n     // Calculate execution time in milliseconds\n   float elapsedTime;\n   cudaEventElapsedTime(&elapsedTime, start, stop);\n   float timeInSeconds = elapsedTime / 1000.0f;\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    cudaMemcpy(h_output_half, d_output, size, cudaMemcpyDeviceToHost);\n\n    // Convert FP16 to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    cudaGraphDestroy(graph);\n    cudaGraphExecDestroy(instance);\n    cudaStreamDestroy(stream);\n    delete[] h_input_half;\n    delete[] h_output_half;\n    cudaFree(d_input);\n    cudaFree(d_output);\n\n    \n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using CUDA Graphs + Tensor Cores\n    softmaxWithCUDAgraphs(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (CUDA Graph + FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n\n#define BLOCK_SIZE 256\n#define WARP_SIZE 32\n\n// Warp reduction for max operation (FP16)\n__device__ __half warpReduceMax(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hmax(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Warp reduction for sum operation (FP16)\n__device__ __half warpReduceSum(__half val) {\n    for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2) {\n        val = __hadd(val, __shfl_down_sync(0xffffffff, val, offset));\n    }\n    return val;\n}\n\n// Softmax Kernel using Tensor Cores and CUDA Graphs\n__global__ void softmaxKernel(__half* input, __half* output, int rows, int cols) {\n    extern __shared__ __half shared_data[];\n\n    int row = blockIdx.x;\n    int tid = threadIdx.x;\n\n    if (row >= rows) return;\n\n    __half* row_data = input + row * cols;\n    __half* shared_row = shared_data;\n\n    // Step 1: Compute max for numerical stability\n    __half max_val = __float2half(-INFINITY);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        max_val = __hmax(max_val, row_data[i]);\n    }\n    max_val = warpReduceMax(max_val);\n\n    // Step 2: Compute exponentials and sum\n    __half sum = __float2half(0.0f);\n    for (int i = tid; i < cols; i += blockDim.x) {\n        shared_row[i] = __float2half(expf(__half2float(__hsub(row_data[i], max_val))));\n        sum = __hadd(sum, shared_row[i]);\n    }\n    sum = warpReduceSum(sum);\n\n    // Step 3: Normalize values\n    for (int i = tid; i < cols; i += blockDim.x) {\n        output[row * cols + i] = __hdiv(shared_row[i], sum);\n    }\n}\n\n// Host function with CUDA Graphs\nvoid softmaxWithCUDAgraphs(float* h_input, float* h_output, int rows, int cols) {\n    __half *d_input, *d_output;\n    size_t size = rows * cols * sizeof(__half);\n\n    // Convert FP32 to FP16\n    __half* h_input_half = new __half[rows * cols];\n    for (int i = 0; i < rows * cols; i++)\n        h_input_half[i] = __float2half(h_input[i]);\n\n    // Allocate device memory\n    hipMalloc((void**)&d_input, size);\n    hipMalloc((void**)&d_output, size);\n    hipMemcpy(d_input, h_input_half, size, hipMemcpyHostToDevice);\n\n      // Create events for timing\n      hipEvent_t start, stop;\n      hipEventCreate(&start);\n      hipEventCreate(&stop);\n  \n      // Record the start time\n      hipEventRecord(start);\n\n    // Define CUDA Graph\n    hipGraph_t graph;\n    hipGraphExec_t instance;\n    hipStream_t stream;\n    hipStreamCreate(&stream);\n\n    // Capture Graph\n    hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal);\n    int shared_mem_size = cols * sizeof(__half);\n    softmaxKernel<<<rows, BLOCK_SIZE, shared_mem_size, stream>>>(d_input, d_output, rows, cols);\n    hipStreamEndCapture(stream, &graph);\n\n    // Instantiate and launch graph\n    hipGraphInstantiate(&instance, graph, NULL, NULL, 0);\n    hipGraphLaunch(instance, stream);\n    hipStreamSynchronize(stream);\n\n    // Record the stop time\n    hipEventRecord(stop);\n    hipEventSynchronize(stop);\n\n     // Calculate execution time in milliseconds\n   float elapsedTime;\n   hipEventElapsedTime(&elapsedTime, start, stop);\n   float timeInSeconds = elapsedTime / 1000.0f;\n\n    // Copy result back to host\n    __half* h_output_half = new __half[rows * cols];\n    hipMemcpy(h_output_half, d_output, size, hipMemcpyDeviceToHost);\n\n    // Convert FP16 to FP32\n    for (int i = 0; i < rows * cols; i++)\n        h_output[i] = __half2float(h_output_half[i]);\n\n    // Cleanup\n    hipGraphDestroy(graph);\n    hipGraphExecDestroy(instance);\n    hipStreamDestroy(stream);\n    delete[] h_input_half;\n    delete[] h_output_half;\n    hipFree(d_input);\n    hipFree(d_output);\n\n    \n    // Calculate GFLOPS\n    long long numFLOPs = (long long)rows * cols * 2;  // 2 FLOPs per element: exp and division\n    float gflops = (numFLOPs / timeInSeconds) / 1e9;\n\n    // Output the results\n    std::cout << \"Execution time: \" << elapsedTime << \" ms\\n\";\n    std::cout << \"GFLOPS: \" << gflops << \" GFLOPS\\n\";\n}\n\n// Test function\nint main() {\n    const int rows = 2, cols = 4;\n    float h_input[rows * cols] = {1.0, 2.0, 3.0, 4.0,  5.0, 6.0, 7.0, 8.0};\n    float h_output[rows * cols];\n\n    // Compute softmax using CUDA Graphs + Tensor Cores\n    softmaxWithCUDAgraphs(h_input, h_output, rows, cols);\n\n    // Print result\n    std::cout << \"Softmax Output (CUDA Graph + FP16 Accelerated):\\n\";\n    for (int i = 0; i < rows; i++) {\n        for (int j = 0; j < cols; j++) {\n            std::cout << h_output[i * cols + j] << \" \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 15/CudaGraph_Softmax.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void reductionExample(int *d_out, int *d_in) {\n    int val = d_in[threadIdx.x];\n\n    // Use the built-in shuffle function\n    for (int offset = 16; offset > 0; offset /= 2) {\n        int temp = __shfl_down_sync(0xffffffff, val, offset);\n        val += temp;\n    }\n\n    if (threadIdx.x == 0)\n        d_out[0] = val;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out;\n    int *d_in, *d_out;\n\n    // Initialize with 1s\n    for (int i = 0; i < N; i++) h_in[i] = 1 * 2;\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, sizeof(int));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    reductionExample<<<1, N>>>(d_out, d_in);\n\n    cudaMemcpy(&h_out, d_out, sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Warp Reduction Result: \" << h_out << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void reductionExample(int *d_out, int *d_in) {\n    int val = d_in[threadIdx.x];\n\n    // Use the built-in shuffle function\n    for (int offset = 16; offset > 0; offset /= 2) {\n        int temp = __shfl_down_sync(0xffffffff, val, offset);\n        val += temp;\n    }\n\n    if (threadIdx.x == 0)\n        d_out[0] = val;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out;\n    int *d_in, *d_out;\n\n    // Initialize with 1s\n    for (int i = 0; i < N; i++) h_in[i] = 1 * 2;\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, sizeof(int));\n\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    reductionExample<<<1, N>>>(d_out, d_in);\n\n    hipMemcpy(&h_out, d_out, sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Warp Reduction Result: \" << h_out << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/warp_reduction.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void inlinePTXExample(int *d_out, int *d_in, int addVal) {\n    int tid = threadIdx.x;\n    int val;\n    int result;\n\n   asm volatile(\"ld.global.s32 %0,[%1];\" : \"=r\"(val) : \"l\"(d_in + tid)); //Load value from global\n\n   asm volatile(\"add.s32 %0 , %0, %1;\" : \"+r\"(val) : \"r\"(addVal)); // Add constant\n\n   asm volatile(\"st.global.s32 [%0], %1;\" :: \"l\"(d_out + tid), \"r\"(val)); // Store value to global\n\n   asm volatile(\"shfl.sync.bfly %0, %1 , 0, 0x1f;\" : \"=r\"(result) : \"r\"(val)); // Shuffle\n\n   d_out[tid] = result;\n\n\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    int addVal =10;\n\n    for (int i = 0; i < N; i++) h_in[i] = i * 2;\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, N * sizeof(int));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    inlinePTXExample<<<1, N>>>(d_out, d_in, addVal);\n\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n\n    for (int i = 0; i < N; i++)\n        std::cout << h_out[i] << \" \";\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void inlinePTXExample(int *d_out, int *d_in, int addVal) {\n    int tid = threadIdx.x;\n    int val;\n    int result;\n\n   asm volatile(\"ld.global.s32 %0,[%1];\" : \"=r\"(val) : \"l\"(d_in + tid)); //Load value from global\n\n   asm volatile(\"add.s32 %0 , %0, %1;\" : \"+r\"(val) : \"r\"(addVal)); // Add constant\n\n   asm volatile(\"st.global.s32 [%0], %1;\" :: \"l\"(d_out + tid), \"r\"(val)); // Store value to global\n\n   asm volatile(\"shfl.sync.bfly %0, %1 , 0, 0x1f;\" : \"=r\"(result) : \"r\"(val)); // Shuffle\n\n   d_out[tid] = result;\n\n\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    int addVal =10;\n\n    for (int i = 0; i < N; i++) h_in[i] = i * 2;\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, N * sizeof(int));\n\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    inlinePTXExample<<<1, N>>>(d_out, d_in, addVal);\n\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n\n    for (int i = 0; i < N; i++)\n        std::cout << h_out[i] << \" \";\n\n    hipFree(d_in);\n    hipFree(d_out);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/inline.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void membarExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n\n    // Memory fence for global memory\n    asm volatile(\"membar.gl;\");\n    \n    d_out[tid] = val;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n\n    for (int i = 0; i < N; i++) h_in[i] = i;\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, N * sizeof(int));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    membarExample<<<1, N>>>(d_out, d_in);\n\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Memory fence applied, values: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void membarExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n\n    // Memory fence for global memory\n    asm volatile(\"membar.gl;\");\n    \n    d_out[tid] = val;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n\n    for (int i = 0; i < N; i++) h_in[i] = i;\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, N * sizeof(int));\n\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    membarExample<<<1, N>>>(d_out, d_in);\n\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Memory fence applied, values: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/Membar.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void bitwiseExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n    int result;\n\n    // Count the number of set bits\n    asm volatile(\"popc.b32 %0, %1;\" : \"=r\"(result) : \"r\"(val));\n    \n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 8;\n    int h_in[N] = {0b0001, 0b0011, 0b0111, 0b1111, 0b1000, 0b1100, 0b1010, 0b1110};\n    int h_out[N];\n    int *d_in, *d_out;\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, N * sizeof(int));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    bitwiseExample<<<1, N>>>(d_out, d_in);\n\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Number of set bits: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void bitwiseExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n    int result;\n\n    // Count the number of set bits\n    asm volatile(\"popc.b32 %0, %1;\" : \"=r\"(result) : \"r\"(val));\n    \n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 8;\n    int h_in[N] = {0b0001, 0b0011, 0b0111, 0b1111, 0b1000, 0b1100, 0b1010, 0b1110};\n    int h_out[N];\n    int *d_in, *d_out;\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, N * sizeof(int));\n\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    bitwiseExample<<<1, N>>>(d_out, d_in);\n\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Number of set bits: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/popc.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void fastMathExample(float *d_out, float *d_in) {\n    int tid = threadIdx.x;\n    float val = d_in[tid];\n    float result;\n\n    // Compute reciprocal using PTX\n    asm volatile(\"rcp.approx.f32 %0, %1;\" : \"=f\"(result) : \"f\"(val));\n    \n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 8;\n    float h_in[N] = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f};\n    float h_out[N];\n    float *d_in, *d_out;\n\n    cudaMalloc(&d_in, N * sizeof(float));\n    cudaMalloc(&d_out, N * sizeof(float));\n\n    cudaMemcpy(d_in, h_in, N * sizeof(float), cudaMemcpyHostToDevice);\n\n    fastMathExample<<<1, N>>>(d_out, d_in);\n\n    cudaMemcpy(h_out, d_out, N * sizeof(float), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Reciprocal values: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void fastMathExample(float *d_out, float *d_in) {\n    int tid = threadIdx.x;\n    float val = d_in[tid];\n    float result;\n\n    // Compute reciprocal using PTX\n    asm volatile(\"rcp.approx.f32 %0, %1;\" : \"=f\"(result) : \"f\"(val));\n    \n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 8;\n    float h_in[N] = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f};\n    float h_out[N];\n    float *d_in, *d_out;\n\n    hipMalloc(&d_in, N * sizeof(float));\n    hipMalloc(&d_out, N * sizeof(float));\n\n    hipMemcpy(d_in, h_in, N * sizeof(float), hipMemcpyHostToDevice);\n\n    fastMathExample<<<1, N>>>(d_out, d_in);\n\n    hipMemcpy(h_out, d_out, N * sizeof(float), hipMemcpyDeviceToHost);\n\n    std::cout << \"Reciprocal values: \";\n    for (int i = 0; i < N; i++) std::cout << h_out[i] << \" \";\n    std::cout << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/reciprocal.cu"}
{"input": "#include <iostream>\n#include <cuda_runtime.h>\n\n__global__ void shuffleExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n    // Broadcast the value from lane 0 to all threads in the warp.\n    int result = __shfl_sync(0xFFFFFFFF, val, 1);\n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    \n    // Initialize input data.\n    for (int i = 0; i < N; i++) {\n        h_in[i] = i + 1;\n    }\n\n    cudaMalloc(&d_in, N * sizeof(int));\n    cudaMalloc(&d_out, N * sizeof(int));\n    cudaMemcpy(d_in, h_in, N * sizeof(int), cudaMemcpyHostToDevice);\n\n    // Launch the kernel with one warp (32 threads).\n    shuffleExample<<<1, N>>>(d_out, d_in);\n    cudaMemcpy(h_out, d_out, N * sizeof(int), cudaMemcpyDeviceToHost);\n\n    std::cout << \"Broadcasted value from thread 1: \";\n    for (int i = 0; i < N; i++) {\n        std::cout << h_out[i] << \" \";\n    }\n    std::cout << std::endl;\n\n    cudaFree(d_in);\n    cudaFree(d_out);\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hip/hip_runtime.h>\n\n__global__ void shuffleExample(int *d_out, int *d_in) {\n    int tid = threadIdx.x;\n    int val = d_in[tid];\n    // Broadcast the value from lane 0 to all threads in the warp.\n    int result = __shfl_sync(0xFFFFFFFF, val, 1);\n    d_out[tid] = result;\n}\n\nint main() {\n    const int N = 32;\n    int h_in[N], h_out[N];\n    int *d_in, *d_out;\n    \n    // Initialize input data.\n    for (int i = 0; i < N; i++) {\n        h_in[i] = i + 1;\n    }\n\n    hipMalloc(&d_in, N * sizeof(int));\n    hipMalloc(&d_out, N * sizeof(int));\n    hipMemcpy(d_in, h_in, N * sizeof(int), hipMemcpyHostToDevice);\n\n    // Launch the kernel with one warp (32 threads).\n    shuffleExample<<<1, N>>>(d_out, d_in);\n    hipMemcpy(h_out, d_out, N * sizeof(int), hipMemcpyDeviceToHost);\n\n    std::cout << \"Broadcasted value from thread 1: \";\n    for (int i = 0; i < N; i++) {\n        std::cout << h_out[i] << \" \";\n    }\n    std::cout << std::endl;\n\n    hipFree(d_in);\n    hipFree(d_out);\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 40/shuffleSync.cu"}
{"input": "#include <iostream>\n#include <curand_kernel.h>\n#include <math.h>  // For fmaxf()\n#include <cuda_runtime.h>\n\n#define S 9      // States\n#define A 4      // Actions\n#define EPS 1000 // Episodes\n#define _A_ 0.1f // Learning rate\n#define _G_ 0.9f // Discount factor\n#define _E_ 1.0f // Exploration rate\n#define _D_ 0.99f // Decay\n\n__global__ void init_rand(curandState *state, int seed) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) curand_init(seed, i, 0, &state[i]);\n}\n\n__device__ int rnd_action(curandState *s) {\n    return curand(s) % A;\n}\n\n__global__ void q_train(float *Q, int *R, curandState *randS) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i >= S - 1) return;\n\n    curandState s = randS[i];\n    int st = i;\n    float e = _E_;\n\n    for (int ep = 0; ep < EPS; ep++) {\n        bool done = false;\n\n        while (!done) {\n            int act;\n            if (curand_uniform(&s) < e) {\n                act = rnd_action(&s); // Exploration\n            } else {\n                // Exploitation: Choose best action\n                float maxQ = -1e9;\n                for (int j = 0; j < A; j++) {\n                    if (Q[st * A + j] > maxQ) {\n                        maxQ = Q[st * A + j];\n                        act = j;\n                    }\n                }\n            }\n\n            // Ensure next state follows a valid transition\n            int nxt = (st + act) % S;\n            float r = R[nxt];\n\n            // Find max Q-value for next state\n            float maxQ = -1e9;\n            for (int j = 0; j < A; j++)\n                maxQ = fmaxf(maxQ, Q[nxt * A + j]);\n\n            // Q-value update\n            Q[st * A + act] += _A_ * (r + _G_ * maxQ - Q[st * A + act]);\n\n            st = nxt;\n            if (st == S - 1) done = true;\n        }\n        e *= _D_; // Decay exploration rate\n    }\n    randS[i] = s;\n}\n\nint main() {\n    float h_Q[S * A];\n    int h_R[S] = {-1, -1, 10, -1, -10, -1, -1, -10, 10};\n\n    // Initialize Q-table with small random values\n    for (int i = 0; i < S * A; i++) {\n        h_Q[i] = ((float)(rand() % 100) / 100.0f) * 0.01f; // Small random values\n    }\n\n    float *d_Q;\n    int *d_R;\n    curandState *d_randS;\n\n    cudaMalloc((void **)&d_Q, S * A * sizeof(float));\n    cudaMalloc((void **)&d_R, S * sizeof(int));\n    cudaMalloc((void **)&d_randS, S * sizeof(curandState));\n\n    cudaMemcpy(d_Q, h_Q, S * A * sizeof(float), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_R, h_R, S * sizeof(int), cudaMemcpyHostToDevice);\n\n    // Initialize random states\n    init_rand<<<1, S>>>(d_randS, time(NULL));\n\n    // Train the Q-table\n    q_train<<<1, S - 1>>>(d_Q, d_R, d_randS);\n\n    // Copy results back\n    cudaMemcpy(h_Q, d_Q, S * A * sizeof(float), cudaMemcpyDeviceToHost);\n\n    // Print final Q-values\n    std::cout << \"Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        std::cout << \"S\" << i << \": \";\n        for (int j = 0; j < A; j++)\n            std::cout << h_Q[i * A + j] << \" \";\n        std::cout << std::endl;\n    }\n\n    cudaFree(d_Q);\n    cudaFree(d_R);\n    cudaFree(d_randS);\n\n    return 0;\n}\n", "output": "#include <iostream>\n#include <hiprand/hiprand_kernel.h>\n#include <math.h>  // For fmaxf()\n#include <hip/hip_runtime.h>\n\n#define S 9      // States\n#define A 4      // Actions\n#define EPS 1000 // Episodes\n#define _A_ 0.1f // Learning rate\n#define _G_ 0.9f // Discount factor\n#define _E_ 1.0f // Exploration rate\n#define _D_ 0.99f // Decay\n\n__global__ void init_rand(hiprandState *state, int seed) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i < S) hiprand_init(seed, i, 0, &state[i]);\n}\n\n__device__ int rnd_action(hiprandState *s) {\n    return hiprand(s) % A;\n}\n\n__global__ void q_train(float *Q, int *R, hiprandState *randS) {\n    int i = threadIdx.x + blockIdx.x * blockDim.x;\n    if (i >= S - 1) return;\n\n    hiprandState s = randS[i];\n    int st = i;\n    float e = _E_;\n\n    for (int ep = 0; ep < EPS; ep++) {\n        bool done = false;\n\n        while (!done) {\n            int act;\n            if (hiprand_uniform(&s) < e) {\n                act = rnd_action(&s); // Exploration\n            } else {\n                // Exploitation: Choose best action\n                float maxQ = -1e9;\n                for (int j = 0; j < A; j++) {\n                    if (Q[st * A + j] > maxQ) {\n                        maxQ = Q[st * A + j];\n                        act = j;\n                    }\n                }\n            }\n\n            // Ensure next state follows a valid transition\n            int nxt = (st + act) % S;\n            float r = R[nxt];\n\n            // Find max Q-value for next state\n            float maxQ = -1e9;\n            for (int j = 0; j < A; j++)\n                maxQ = fmaxf(maxQ, Q[nxt * A + j]);\n\n            // Q-value update\n            Q[st * A + act] += _A_ * (r + _G_ * maxQ - Q[st * A + act]);\n\n            st = nxt;\n            if (st == S - 1) done = true;\n        }\n        e *= _D_; // Decay exploration rate\n    }\n    randS[i] = s;\n}\n\nint main() {\n    float h_Q[S * A];\n    int h_R[S] = {-1, -1, 10, -1, -10, -1, -1, -10, 10};\n\n    // Initialize Q-table with small random values\n    for (int i = 0; i < S * A; i++) {\n        h_Q[i] = ((float)(rand() % 100) / 100.0f) * 0.01f; // Small random values\n    }\n\n    float *d_Q;\n    int *d_R;\n    hiprandState *d_randS;\n\n    hipMalloc((void **)&d_Q, S * A * sizeof(float));\n    hipMalloc((void **)&d_R, S * sizeof(int));\n    hipMalloc((void **)&d_randS, S * sizeof(hiprandState));\n\n    hipMemcpy(d_Q, h_Q, S * A * sizeof(float), hipMemcpyHostToDevice);\n    hipMemcpy(d_R, h_R, S * sizeof(int), hipMemcpyHostToDevice);\n\n    // Initialize random states\n    init_rand<<<1, S>>>(d_randS, time(NULL));\n\n    // Train the Q-table\n    q_train<<<1, S - 1>>>(d_Q, d_R, d_randS);\n\n    // Copy results back\n    hipMemcpy(h_Q, d_Q, S * A * sizeof(float), hipMemcpyDeviceToHost);\n\n    // Print final Q-values\n    std::cout << \"Q-values:\\n\";\n    for (int i = 0; i < S; i++) {\n        std::cout << \"S\" << i << \": \";\n        for (int j = 0; j < A; j++)\n            std::cout << h_Q[i * A + j] << \" \";\n        std::cout << std::endl;\n    }\n\n    hipFree(d_Q);\n    hipFree(d_R);\n    hipFree(d_randS);\n\n    return 0;\n}\n", "source": "/kaggle/working/100-days-cuda/Day 54/q_cuda.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2022.  Authored by Yuqing Ding.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/models/wenet/WenetKernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\nnamespace {\nconstexpr auto EPS = 1e-6f;  // 1e-5;\n}\n\ntemplate<typename T>\n__inline__ __device__ T sigmoid(T x)\n{\n    return T(1.0f) / (T(1.0f) + exp(-x));\n    // return T(1.0f) / (T(1.0f) + __expf(-x));\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void add_bias_mul(T* out, const T* __restrict bias, T scale, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        out[id] = (out[id] + (T)ldg(&bias[id % n])) * scale;\n    }\n}\n\ntemplate<>\n__global__ void add_bias_mul(half* out, const half* __restrict bias, half scale, int m, int n)\n{\n    int real_n = 2 * n;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * real_n; id += blockDim.x * gridDim.x) {\n        out[id] = __hmul(__hadd(out[id], bias[id % real_n]), scale);\n    }\n}\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void\nadd_bias_mul(__nv_bfloat16* out, const __nv_bfloat16* __restrict bias, __nv_bfloat16 scale, int m, int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddBiasMul(T* out, const T* bias, const T scale, const int m, const int n, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    add_bias_mul<T><<<grid, block, 0, stream>>>(out, bias, scale, m, n / data_type_factor);\n}\n\ntemplate void\ninvokeAddBiasMul(float* out, const float* bias, const float scale, const int m, const int n, cudaStream_t stream);\ntemplate void\ninvokeAddBiasMul(half* out, const half* bias, const half scale, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasMul(__nv_bfloat16*       out,\n                               const __nv_bfloat16* bias,\n                               const __nv_bfloat16  scale,\n                               const int            m,\n                               const int            n,\n                               cudaStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void cmvn(T* out, const T* __restrict in, const T* __restrict mean, const T* __restrict istd, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        out[id] = (in[id] - mean[id % n]) * istd[id % n];\n    }\n}\n\ntemplate<>\n__global__ void\ncmvn(half* out, const half* __restrict in, const half* __restrict mean, const half* __restrict istd, int m, int n)\n{\n    int real_n = 2 * n;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * real_n; id += blockDim.x * gridDim.x) {\n        out[id] = __hmul(__hsub(in[id], mean[id % real_n]), istd[id % real_n]);\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void cmvn(__nv_bfloat16* out,\n                     const __nv_bfloat16* __restrict in,\n                     const __nv_bfloat16* __restrict mean,\n                     const __nv_bfloat16* __restrict istd,\n                     int m,\n                     int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeCMVN(T* out, const T* in, const T* mean, const T* istd, const int m, const int n, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    cmvn<T><<<grid, block, 0, stream>>>(out, in, mean, istd, m, n / data_type_factor);\n}\n\ntemplate void invokeCMVN(\n    float* out, const float* in, const float* mean, const float* istd, const int m, const int n, cudaStream_t stream);\ntemplate void invokeCMVN(\n    half* out, const half* in, const half* mean, const half* istd, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCMVN(__nv_bfloat16*       out,\n                         const __nv_bfloat16* in,\n                         const __nv_bfloat16* mean,\n                         const __nv_bfloat16* istd,\n                         const int            m,\n                         const int            n,\n                         cudaStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void slice(T* out, const T* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n    // in: (1, 5000, hidden_unit)\n    // out: (batch_size, seq_len, hidden_unit)\n    int seq_id  = blockIdx.x % seq_len;\n    int in_id   = seq_id * blockDim.x + threadIdx.x;\n    int out_id  = blockIdx.x * blockDim.x + threadIdx.x;\n    out[out_id] = (T)ldg(&in[in_id]);\n}\n\ntemplate<>\n__global__ void slice(half* out, const half* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n    half2*       out_ptr = (half2*)out;\n    const half2* in_ptr  = (half2*)in;\n    int          seq_id  = blockIdx.x % seq_len;\n    int          in_id   = seq_id * blockDim.x + threadIdx.x;\n    int          out_id  = blockIdx.x * blockDim.x + threadIdx.x;\n    out_ptr[out_id]      = __ldg(&in_ptr[in_id]);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void\nslice(__nv_bfloat16* out, const __nv_bfloat16* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeSlice(\n    T* out, const T* in, const int batch_size, const int seq_len, const int hidden_unit, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    const int m                = batch_size * seq_len;\n    dim3      block, grid;\n    if (hidden_unit / data_type_factor <= 1024) {\n        block.x = hidden_unit / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * hidden_unit / 1024.);\n    }\n    slice<T><<<grid, block, 0, stream>>>(out, in, batch_size, seq_len, hidden_unit / data_type_factor);\n}\n\ntemplate void invokeSlice(\n    float* out, const float* in, const int batch_size, const int seq_len, const int hidden_unit, cudaStream_t stream);\ntemplate void invokeSlice(\n    half* out, const half* in, const int batch_size, const int seq_len, const int hidden_unit, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeSlice(__nv_bfloat16*       out,\n                          const __nv_bfloat16* in,\n                          const int            batch_size,\n                          const int            seq_len,\n                          const int            hidden_unit,\n                          cudaStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void\ntranspose0213(T* src, T* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int batch_id       = blockIdx.x / (head_num * seq_len);\n    int seq_id         = blockIdx.x % seq_len;\n    int head_id        = (blockIdx.x % (head_num * seq_len)) / seq_len;\n    dst[batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head + head_id * size_per_head\n        + threadIdx.x] = src[blockIdx.x * size_per_head + threadIdx.x];\n}\n\ntemplate<>\n__global__ void transpose0213(\n    half* src, half* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\ntemplate<>\n__global__ void transpose0213(\n    half2* src, half2* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void transpose0213(__nv_bfloat16* src,\n                              __nv_bfloat16* dst,\n                              const int      batch_size,\n                              const int      seq_len,\n                              const int      head_num,\n                              const int      size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\ntemplate<>\n__global__ void transpose0213(__nv_bfloat162* src,\n                              __nv_bfloat162* dst,\n                              const int       batch_size,\n                              const int       seq_len,\n                              const int       head_num,\n                              const int       size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n#endif\n\ntemplate<typename T>\nvoid invokeTranspose0213(T*           dst,\n                         T*           src,\n                         const int    batch_size,\n                         const int    seq_len,\n                         const int    head_num,\n                         const int    size_per_head,\n                         cudaStream_t stream)\n{\n    // src: [batch_size, head_num, seq_len, size_per_head]\n    // dst: [batch_size, seq_len, head_num, size_per_head]\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        int seq_per_block = 1;\n        grid.x            = batch_size * head_num * seq_len / seq_per_block;\n        if (seq_per_block * size_per_head % 2 == 0) {\n            block.x = seq_per_block * size_per_head / 2;\n            if (std::is_same<T, half>::value) {\n                transpose0213<half2><<<grid, block, 0, stream>>>(\n                    (half2*)src, (half2*)dst, batch_size, seq_len, head_num, size_per_head / 2);\n            }\n#ifdef ENABLE_BF16\n            else {\n                transpose0213<__nv_bfloat162><<<grid, block, 0, stream>>>(\n                    (__nv_bfloat162*)src, (__nv_bfloat162*)dst, batch_size, seq_len, head_num, size_per_head / 2);\n            }\n#endif\n        }\n        else {\n            block.x = seq_per_block * size_per_head;\n            transpose0213<T><<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n        }\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transpose0213<T><<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n    }\n}\n\ntemplate void invokeTranspose0213(float*       src,\n                                  float*       dst,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  cudaStream_t stream);\n\ntemplate void invokeTranspose0213(half*        src,\n                                  half*        dst,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose0213(__nv_bfloat16* src,\n                                  __nv_bfloat16* dst,\n                                  const int      batch_size,\n                                  const int      seq_len,\n                                  const int      head_num,\n                                  const int      size_per_head,\n                                  cudaStream_t   stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void embedDecoderInput(T* out,\n                                  const int* __restrict in,\n                                  const T* __restrict embed_weights,\n                                  const T* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n    // blockDim: (bs * beam_size * 63), threadDim: (256)\n    // in: (bs * beam_size, 64)\n    // embed_weights: (4233, 256)\n    // encoding_weights: (1, 5000, 256)\n    // out: (bs * beam_size, 63, n = 256)\n    int in_row       = blockIdx.x / seq_len;\n    int in_col       = blockIdx.x % seq_len;\n    int id           = blockIdx.x * blockDim.x + threadIdx.x;\n    int embed_row    = (int)ldg(&in[in_row * (seq_len + 1) + in_col]);\n    int embed_id     = embed_row * blockDim.x + threadIdx.x;\n    int encoding_row = blockIdx.x % seq_len;\n    int encoding_id  = encoding_row * blockDim.x + threadIdx.x;\n    T   scale        = (T)sqrtf((float)n);\n    out[id]          = (T)ldg(&embed_weights[embed_id]) * scale;\n    T encoding_data  = (T)ldg(&encoding_weights[encoding_id]);\n    out[id] += encoding_data;\n}\n\ntemplate<>\n__global__ void embedDecoderInput(half* out,\n                                  const int* __restrict in,\n                                  const half* __restrict embed_weights,\n                                  const half* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n    int  in_row       = blockIdx.x / seq_len;\n    int  in_col       = blockIdx.x % seq_len;\n    int  id           = blockIdx.x * blockDim.x + threadIdx.x;\n    int  embed_row    = (int)ldg(&in[in_row * (seq_len + 1) + in_col]);\n    int  embed_id     = embed_row * blockDim.x + threadIdx.x;\n    int  encoding_row = blockIdx.x % seq_len;\n    int  encoding_id  = encoding_row * blockDim.x + threadIdx.x;\n    half scale        = (half)sqrtf((float)n);\n    out[id]           = __hadd(__hmul(embed_weights[embed_id], scale), encoding_weights[encoding_id]);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void embedDecoderInput(__nv_bfloat16* out,\n                                  const int* __restrict in,\n                                  const __nv_bfloat16* __restrict embed_weights,\n                                  const __nv_bfloat16* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeEmbedDecoderInput(T*           out,\n                             const int*   in,\n                             const T*     embed_weights,\n                             const T*     encoding_weights,\n                             const int    vocab_size,\n                             const int    max_len,\n                             const int    batch_size,\n                             const int    seq_len,\n                             const int    hidden_units,\n                             cudaStream_t stream)\n{\n    const int data_type_factor = 1;\n    const int m                = batch_size * seq_len;\n    const int n                = hidden_units;\n    dim3      block, grid;\n    if (n / data_type_factor <= 1024) {\n        block.x = n / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    embedDecoderInput<T><<<grid, block, 0, stream>>>(\n        out, in, embed_weights, encoding_weights, vocab_size, seq_len, max_len, m, n / data_type_factor);\n}\n\ntemplate void invokeEmbedDecoderInput(float*       out,\n                                      const int*   in,\n                                      const float* embed_weights,\n                                      const float* encoding_weights,\n                                      const int    vocab_size,\n                                      const int    max_len,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_units,\n                                      cudaStream_t stream);\ntemplate void invokeEmbedDecoderInput(half*        out,\n                                      const int*   in,\n                                      const half*  embed_weights,\n                                      const half*  encoding_weights,\n                                      const int    vocab_size,\n                                      const int    max_len,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_units,\n                                      cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeEmbedDecoderInput(__nv_bfloat16*       out,\n                                      const int*           in,\n                                      const __nv_bfloat16* embed_weights,\n                                      const __nv_bfloat16* encoding_weights,\n                                      const int            vocab_size,\n                                      const int            max_len,\n                                      const int            batch_size,\n                                      const int            seq_len,\n                                      const int            hidden_units,\n                                      cudaStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void maskDecoderOutput(float* score,\n                                  const T* __restrict decoder_output,\n                                  const int* __restrict decoder_sequence_length,\n                                  const int* __restrict decoder_input,\n                                  int max_seq_len,\n                                  int vocab_size)\n{\n    // gridDim: (bs * beam_size), blockDim: (max_seq_len=63)\n    // decoder_output: [batch_size, beam_size, max_seq_len, vocab_size]\n    // decoder_sequence_length: [bs * beam_size]\n    // decoder_input: [bs * beam_size, max_seq_len+1=64]\n    // score: [bs * beam_size]\n    const int len  = decoder_sequence_length[blockIdx.x];\n    T         mask = (T)(0.0f);\n    if (threadIdx.x < len)\n        mask = (T)(1.0f);\n\n    int in_id     = blockIdx.x * (max_seq_len + 1) + threadIdx.x + 1;\n    int in_value  = decoder_input[in_id];\n    int vocab_id  = in_value * (int)mask;\n    int out_id    = (blockIdx.x * blockDim.x + threadIdx.x) * vocab_size + vocab_id;\n    T   out_value = decoder_output[out_id] * mask;\n\n    float sum = blockReduceSum((float)(out_value));\n    if (threadIdx.x == 0)\n        score[blockIdx.x] = sum;\n}\n\ntemplate<typename T>\nvoid invokeMaskDecoderOutput(float*       score,\n                             const T*     decoder_output,\n                             const int*   decoder_sequence_length,\n                             const int*   decoder_input,\n                             const int    batch_size,\n                             const int    max_seq_len,\n                             const int    vocab_size,\n                             cudaStream_t stream)\n{\n    maskDecoderOutput<T><<<batch_size, std::min(1024, max_seq_len), 0, stream>>>(\n        score, decoder_output, decoder_sequence_length, decoder_input, max_seq_len, vocab_size);\n}\n\ntemplate void invokeMaskDecoderOutput(float*       score,\n                                      const float* decoder_output,\n                                      const int*   decoder_sequence_length,\n                                      const int*   decoder_input,\n                                      const int    batch_size,\n                                      const int    max_seq_len,\n                                      const int    vocab_size,\n                                      cudaStream_t stream);\ntemplate void invokeMaskDecoderOutput(float*       score,\n                                      const half*  decoder_output,\n                                      const int*   decoder_sequence_length,\n                                      const int*   decoder_input,\n                                      const int    batch_size,\n                                      const int    max_seq_len,\n                                      const int    vocab_size,\n                                      cudaStream_t stream);\n// #ifdef ENABLE_BF16\n// template void\n// invokeMaskDecoderOutput(__nv_bfloat16* score, const __nv_bfloat16* decoder_output, const int*\n// decoder_sequence_length, const int* decoder_input, const int batch_size, const int max_seq_len, cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void buildBestIndex(\n    int* best_index, const float* __restrict decoder_score, const T* __restrict ctc_score, T ctc_weight, int beam_size)\n{\n    // gridDim: (bs), blockDim: (beam_size=10)\n    // decoder_score: [bs, 10]\n    // ctc_score: [bs, 10]\n    // best_index: [bs,]\n    int id  = blockIdx.x * blockDim.x + threadIdx.x;\n    T   val = (T)ldg(&decoder_score[id]) + (T)ldg(&ctc_score[id]) * ctc_weight;\n\n    extern __shared__ float score[];\n    score[threadIdx.x] = (float)val;\n    __syncthreads();\n\n    int   index = 0;\n    float max   = score[0];\n    for (int i = 1; i < blockDim.x; i++) {\n        if (score[i] > max) {\n            max   = score[i];\n            index = i;\n        }\n    }\n    if (threadIdx.x == 0) {\n        best_index[blockIdx.x] = index;\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildBestIndex(int*         best_index,\n                          const float* decoder_score,\n                          const T*     ctc_score,\n                          const T      ctc_weight,\n                          const int    batch_size,\n                          const int    beam_size,\n                          cudaStream_t stream)\n{\n    buildBestIndex<T><<<batch_size, std::min(1024, beam_size), beam_size * sizeof(float), stream>>>(\n        best_index, decoder_score, ctc_score, ctc_weight, beam_size);\n}\n\ntemplate void invokeBuildBestIndex(int*         best_index,\n                                   const float* decoder_score,\n                                   const float* ctc_score,\n                                   const float  ctc_weight,\n                                   const int    batch_size,\n                                   const int    beam_size,\n                                   cudaStream_t stream);\ntemplate void invokeBuildBestIndex(int*         best_index,\n                                   const float* decoder_score,\n                                   const half*  ctc_score,\n                                   const half   ctc_weight,\n                                   const int    batch_size,\n                                   const int    beam_size,\n                                   cudaStream_t stream);\n// #ifdef ENABLE_BF16\n// template void\n// invokeBuildBestIndex(__nv_bfloat16* score, const __nv_bfloat16* decoder_output, const int* decoder_sequence_length,\n// const int* decoder_input, const int batch_size, const int max_seq_len, cudaStream_t stream); #endif\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void mask_bias_glu(\n    T* out, const T* __restrict in, const T* __restrict bias, int m, int n, const T* __restrict attr_mask, int seq_len)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id     = id / n;\n        int n_id     = id % n;\n        int s_id     = m_id % seq_len;\n        int b_id     = m_id / seq_len;\n        T   cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        // in = in + m_id * 2 * n;\n        const T* in_ptr = in + m_id * 2 * n;\n\n        float val1 = 0.f;\n        float val2 = 0.f;\n\n        if (cur_mask != T(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            val1 = val1 + ldg(&bias[n_id]);\n            val2 = val2 + ldg(&bias[n_id + n]);\n        }\n        out[id] = val1 * sigmoid<float>(val2);\n    }\n}\n\ntemplate<>\n__global__ void mask_bias_glu(half* out,\n                              const half* __restrict in,\n                              const half* __restrict bias,\n                              int m,\n                              int n,\n                              const half* __restrict attr_mask,\n                              int seq_len)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id = id / n;\n        int n_id = id % n;\n        int s_id = m_id % seq_len;\n        int b_id = m_id / seq_len;\n        // const half* attr_mask = attr_mask + ;\n        half cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n\n        const half2* in_ptr = (half2*)in + m_id * 2 * n;\n        // in = in + m_id * 2 * n;\n\n        half2 val1 = cuda_cast<half2>(0.f);\n        half2 val2 = cuda_cast<half2>(0.f);\n\n        if (cur_mask != half(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            half2 bias1 = __ldg(&bias_ptr[n_id]);\n            half2 bias2 = __ldg(&bias_ptr[n_id + n]);\n            val1        = hadd2(val1, bias1);\n            val2        = hadd2(val2, bias2);\n        }\n        half2 local_out;\n        local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n        local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n        out_ptr[id] = local_out;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void mask_bias_glu(__nv_bfloat16* out,\n                              const __nv_bfloat16* __restrict in,\n                              const __nv_bfloat16* __restrict bias,\n                              int m,\n                              int n,\n                              const __nv_bfloat16* __restrict attr_mask,\n                              int seq_len)\n{\n    __nv_bfloat162*       out_ptr  = (__nv_bfloat162*)out;\n    const __nv_bfloat162* bias_ptr = (__nv_bfloat162*)bias;\n    // const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int           m_id     = id / n;\n        int           n_id     = id % n;\n        int           s_id     = m_id % seq_len;\n        int           b_id     = m_id / seq_len;\n        __nv_bfloat16 cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n\n        // in = in + m_id * 2 * n;\n        const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in + m_id * 2 * n;\n\n        __nv_bfloat162 val1 = cuda_cast<__nv_bfloat162>(0.f);\n        __nv_bfloat162 val2 = cuda_cast<__nv_bfloat162>(0.f);\n\n        if (cur_mask != __nv_bfloat16(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            __nv_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n            __nv_bfloat162 bias2 = ldg(&bias_ptr[n_id + n]);\n            val1                 = bf16hadd2(val1, bias1);\n            val2                 = bf16hadd2(val2, bias2);\n        }\n        __nv_bfloat162 local_out;\n        local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n        local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n        out_ptr[id] = local_out;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeMaskBiasGlu(T*           out,\n                       const T*     in,\n                       const T*     bias,\n                       const int    m,\n                       const int    n,\n                       const T*     attr_mask,\n                       const int    seq_len,\n                       cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    mask_bias_glu<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor, attr_mask, seq_len);\n}\n\ntemplate void invokeMaskBiasGlu(float*       out,\n                                const float* in,\n                                const float* bias,\n                                const int    m,\n                                const int    n,\n                                const float* attr_mask,\n                                const int    seq_len,\n                                cudaStream_t stream);\ntemplate void invokeMaskBiasGlu(half*        out,\n                                const half*  in,\n                                const half*  bias,\n                                const int    m,\n                                const int    n,\n                                const half*  attr_mask,\n                                const int    seq_len,\n                                cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMaskBiasGlu(__nv_bfloat16*       out,\n                                const __nv_bfloat16* in,\n                                const __nv_bfloat16* bias,\n                                const int            m,\n                                const int            n,\n                                const __nv_bfloat16* attr_mask,\n                                const int            seq_len,\n                                cudaStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void bias_glu(T* out, const T* __restrict in, const T* __restrict bias, int m, int n)\n// {\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n//         // in = in + m_id * 2 * n;\n//         const T* in_ptr = in + m_id * 2 * n;\n\n//         float val1 = in_ptr[n_id];\n//         float val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             val1 = val1 + ldg(&bias[n_id]);\n//             val2 = val2 + ldg(&bias[n_id + n]);\n//         }\n//         out[id] = val1 * sigmoid<float>(val2);\n//     }\n// }\n\n// template<>\n// __global__ void bias_glu(half* out, const half* __restrict in, const half* __restrict bias, int m, int n)\n// {\n//     half2* out_ptr = (half2*)out;\n//     const half2* bias_ptr = (half2*)bias;\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n//         // const half* attr_mask = attr_mask + ;\n\n//         const half2* in_ptr = (half2*)in + m_id * 2 * n;\n//         // in = in + m_id * 2 * n;\n\n//         half2 val1 = in_ptr[n_id];\n//         half2 val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             half2 bias1 = __ldg(&bias_ptr[n_id]);\n//             half2 bias2 = __ldg(&bias_ptr[n_id + n]);\n//             val1 = hadd2(val1, bias1);\n//             val2 = hadd2(val2, bias2);\n//         }\n//         half2 local_out;\n//         local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n//         local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n//         out_ptr[id] = local_out;\n//     }\n// }\n\n// #ifdef ENABLE_BF16\n// template<>\n// __global__ void\n// bias_glu(__nv_bfloat16* out, const __nv_bfloat16* __restrict in, const __nv_bfloat16* __restrict bias, int m, int n)\n// {\n//     __nv_bfloat162* out_ptr = (__nv_bfloat162*)out;\n//     const __nv_bfloat162* bias_ptr = (__nv_bfloat162*)bias;\n//     // const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in;\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n\n//         // in = in + m_id * 2 * n;\n//         const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in + m_id * 2 * n;\n\n//         __nv_bfloat162 val1 = in_ptr[n_id];\n//         __nv_bfloat162 val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             __nv_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n//             __nv_bfloat162 bias2 = ldg(&bias_ptr[n_id + n]);\n//             val1 = bf16hadd2(val1, bias1);\n//             val2 = bf16hadd2(val2, bias2);\n//         }\n//         __nv_bfloat162 local_out;\n//         local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n//         local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n//         out_ptr[id] = local_out;\n//     }\n// }\n// #endif\n\n// template<typename T>\n// void invokeBiasGlu(T* out, const T* in, const T* bias, const int m, const int n, cudaStream_t stream)\n// {\n//     const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n//     dim3 block, grid;\n//     if (n / 4 / data_type_factor <= 1024) {\n//         block.x = n / 4 / data_type_factor;\n//         grid.x = m;\n//     }\n//     else {\n//         block.x = 1024;\n//         grid.x = ceil(m * n / 1024.);\n//     }\n//     bias_glu<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor);\n// }\n\n// template void\n// invokeBiasGlu(float* out, const float* in, const float* bias, const int m, const int n, cudaStream_t stream);\n// template void invokeBiasGlu(half* out, const half* in, const half* bias, const int m, const int n, cudaStream_t\n// stream); #ifdef ENABLE_BF16 template void invokeBiasGlu(__nv_bfloat16* out,\n//                             const __nv_bfloat16* in,\n//                             const __nv_bfloat16* bias,\n//                             const int m,\n//                             const int n,\n//                             cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void mask_bias(\n    T* out, const T* __restrict in, const T* __restrict bias, int m, int n, const T* __restrict attr_mask, int seq_len)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id = id / n;\n        int n_id = id % n;\n        int s_id = m_id % seq_len;\n        int b_id = m_id / seq_len;\n\n        T cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        // const T* in_ptr = in + m_id * n;\n\n        float val1 = 0.f;\n\n        if (cur_mask != T(0.f)) {\n            val1 = in[id];\n\n            if (bias != nullptr) {\n                val1 = val1 + ldg(&bias[n_id]);\n            }\n        }\n        out[id] = val1;\n    }\n}\n\ntemplate<>\n__global__ void mask_bias(half* out,\n                          const half* __restrict in,\n                          const half* __restrict bias,\n                          int m,\n                          int n,\n                          const half* __restrict attr_mask,\n                          int seq_len)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int          m_id     = id / n;  // TODO optimize / and % with +-\n        int          n_id     = id % n;\n        int          s_id     = m_id % seq_len;\n        int          b_id     = m_id / seq_len;\n        half         cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        const half2* in_ptr   = (const half2*)in + m_id * n;  // TODO optimize\n\n        half2 val1 = cuda_cast<half2>(0.f);\n\n        if (cur_mask != half(0.f)) {\n            val1 = in_ptr[n_id];\n\n            if (bias != nullptr) {\n                half2 bias1 = __ldg(&bias_ptr[n_id]);\n                val1        = hadd2(val1, bias1);\n            }\n        }\n        out_ptr[id] = val1;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void mask_bias(__nv_bfloat16* out,\n                          const __nv_bfloat16* __restrict in,\n                          const __nv_bfloat16* __restrict bias,\n                          int m,\n                          int n,\n                          const __nv_bfloat16* __restrict attr_mask,\n                          int seq_len)\n{\n    __nv_bfloat162*       out_ptr  = (__nv_bfloat162*)out;\n    const __nv_bfloat162* bias_ptr = (__nv_bfloat162*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int                   m_id     = id / n;\n        int                   n_id     = id % n;\n        int                   s_id     = m_id % seq_len;\n        int                   b_id     = m_id / seq_len;\n        __nv_bfloat16         cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        const __nv_bfloat162* in_ptr   = (const __nv_bfloat162*)in + m_id * n;\n\n        __nv_bfloat162 val1 = cuda_cast<__nv_bfloat162>(0.f);\n\n        if (cur_mask != __nv_bfloat16(0.f)) {\n            val1 = in_ptr[n_id];\n\n            if (bias != nullptr) {\n                __nv_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n                val1                 = bf16hadd2(val1, bias1);\n            }\n        }\n        out_ptr[id] = val1;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeMaskBias(T*           out,\n                    const T*     in,\n                    const T*     bias,\n                    const int    m,\n                    const int    n,\n                    const T*     attr_mask,\n                    const int    seq_len,\n                    cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    mask_bias<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor, attr_mask, seq_len);\n}\n\ntemplate void invokeMaskBias(float*       out,\n                             const float* in,\n                             const float* bias,\n                             const int    m,\n                             const int    n,\n                             const float* attr_mask,\n                             const int    seq_len,\n                             cudaStream_t stream);\ntemplate void invokeMaskBias(half*        out,\n                             const half*  in,\n                             const half*  bias,\n                             const int    m,\n                             const int    n,\n                             const half*  attr_mask,\n                             const int    seq_len,\n                             cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMaskBias(__nv_bfloat16*       out,\n                             const __nv_bfloat16* in,\n                             const __nv_bfloat16* bias,\n                             const int            m,\n                             const int            n,\n                             const __nv_bfloat16* attr_mask,\n                             const int            seq_len,\n                             cudaStream_t         stream);\n#endif\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, bool IS_RESIDUAL, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalScaleAddBiasResidualLayerNormOpt(T* normed_output,\n                                                        T* output,\n                                                        const T* __restrict bias,\n                                                        const T* __restrict residual,\n                                                        const T* __restrict gamma,\n                                                        const T* __restrict beta,\n                                                        int   m,\n                                                        int   n,\n                                                        float scale_input,\n                                                        float scale_residual)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T local_sum            = cuda_cast<T>(0.0f);\n    T local_scale_input    = cuda_cast<T>(scale_input);\n    T local_scale_residual = cuda_cast<T>(scale_residual);\n#pragma unroll\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = cuda_cast<T>(0.0f);\n\n        if (IS_OUTPUT) {\n            val = hadd2(val, output[index]);\n        }\n\n        if (IS_BIAS) {\n            val = hadd2(val, ldg(&bias[i]));\n        }\n        val = hmul2(val, local_scale_input);\n        if (IS_RESIDUAL) {\n            val = hadd2(val, hmul2(ldg(&residual[index]), local_scale_residual));\n        }\n\n        output[index] = val;\n\n        local_sum = hadd2(local_sum, val);\n    }\n\n    mean = blockReduceSum((float)(local_sum.x + local_sum.y));\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n / 2;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        T     val    = output[blockIdx.x * n + i];\n        float diff_1 = (float)(val.x) - s_mean;\n        float diff_2 = (float)(val.y) - s_mean;\n        local_var_sum += (diff_1 * diff_1 + diff_2 * diff_2);\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n / 2 + EPS);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hmul2(hsub2(output[index], mean_2), var_2), ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n        normed_output[index] = val;\n    }\n}\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, bool IS_RESIDUAL, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalScaleAddBiasResidualLayerNormOpt2(T* normed_output,\n                                                         T* output,\n                                                         const T* __restrict bias,\n                                                         const T* __restrict residual,\n                                                         const T* __restrict gamma,\n                                                         const T* __restrict beta,\n                                                         int   m,\n                                                         int   n,\n                                                         float scale_input,\n                                                         float scale_residual)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            x_sum    = 0.0f;\n    float            x2_sum   = 0.0f;\n    const int        b_offset = blockIdx.x * n;\n    using T1                  = typename TypeConverter<T>::Type;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        float     val_1 = 0.0f;\n        float     val_2 = 0.0f;\n        T         tmp;\n\n        if (IS_OUTPUT) {\n            tmp = ldg(&output[index]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n\n        if (IS_BIAS) {\n            tmp = ldg(&bias[i]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        val_1 *= scale_input;\n        val_2 *= scale_input;\n\n        if (IS_RESIDUAL) {\n            tmp = ldg(&residual[index]);\n            val_1 += static_cast<float>(tmp.x) * scale_residual;\n            val_2 += static_cast<float>(tmp.y) * scale_residual;\n        }\n\n        tmp.x         = cuda_cast<T1>(val_1);\n        tmp.y         = cuda_cast<T1>(val_2);\n        output[index] = tmp;\n        x_sum += val_1 + val_2;\n        x2_sum += val_1 * val_1 + val_2 * val_2;\n    }\n    float sums[2];\n    sums[0] = x_sum;\n    sums[1] = x2_sum;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / n / 2;\n        s_variance = rsqrtf(sums[1] / n / 2 - s_mean * s_mean + EPS);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        T         val   = hmul2(hmul2(hsub2(output[index], mean_2), var_2), ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n        normed_output[index] = val;\n    }\n}\n\n#define HALF_LAYERNORM_OPT(UNROLL_FACTOR)                                                                              \\\n    if (bias != nullptr)                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt<T2, true, true, true, true, UNROLL_FACTOR>                             \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);                                                              \\\n    else                                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt<T2, true, false, true, true, UNROLL_FACTOR>                            \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);\n\n#define HALF_LAYERNORM_OPT2(UNROLL_FACTOR)                                                                             \\\n    if (bias != nullptr)                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt2<T2, true, true, true, true, UNROLL_FACTOR>                            \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);                                                              \\\n    else                                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt2<T2, true, false, true, true, UNROLL_FACTOR>                           \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);\n\ntemplate<typename T>\n__global__ void generalScaleAddBiasResidualLayerNorm(const T* __restrict input,\n                                                     const T* __restrict gamma,\n                                                     const T* __restrict beta,\n                                                     const T* __restrict bias,\n                                                     T*    output,\n                                                     T*    norm_output,\n                                                     int   m,\n                                                     int   n,\n                                                     float scale_input,\n                                                     float scale_residual)\n\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float local_out = (float)(output[blockIdx.x * n + i]);\n        if (bias != nullptr) {\n            local_out += (float)(ldg(&bias[i]));\n        }\n        local_out *= scale_input;\n        local_out += (float)(ldg(&input[blockIdx.x * n + i])) * scale_residual;\n\n        output[blockIdx.x * n + i] = (T)local_out;\n        local_sum += local_out;\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(output[blockIdx.x * n + i]) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + EPS);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(ldg(&beta[i]));\n        if (norm_output != nullptr)\n            norm_output[blockIdx.x * n + i] =\n                (T)((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralScaleAddBiasResidualPreLayerNorm(T*           output,\n                                                   T*           norm_output,\n                                                   const T*     input,\n                                                   const T*     gamma,\n                                                   const T*     beta,\n                                                   const T*     bias,\n                                                   int          m,\n                                                   int          n,\n                                                   cudaStream_t stream,\n                                                   int          opt_version,\n                                                   float        scale_input,\n                                                   float        scale_residual)\n\n{\n    if (opt_version > 0 && sizeof(T) == 2 && n % 2 == 0) {\n        dim3 grid(m);\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n        if (opt_version == 1) {\n            if (unroll_factor == 1) {\n                HALF_LAYERNORM_OPT(1);\n            }\n            else if (unroll_factor == 2) {\n                HALF_LAYERNORM_OPT(2);\n            }\n            else if (unroll_factor == 3) {\n                HALF_LAYERNORM_OPT(3);\n            }\n            else if (unroll_factor == 4) {\n                HALF_LAYERNORM_OPT(4);\n            }\n            else if (unroll_factor == 8) {\n                HALF_LAYERNORM_OPT(8);\n            }\n        }\n        else {\n            if (unroll_factor == 1) {\n                HALF_LAYERNORM_OPT2(1);\n            }\n            else if (unroll_factor == 2) {\n                HALF_LAYERNORM_OPT2(2);\n            }\n            else if (unroll_factor == 3) {\n                HALF_LAYERNORM_OPT2(3);\n            }\n            else if (unroll_factor == 4) {\n                HALF_LAYERNORM_OPT2(4);\n            }\n            else if (unroll_factor == 8) {\n                HALF_LAYERNORM_OPT2(8);\n            }\n        }\n    }\n    else {\n\n        dim3 grid(m);\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n\n        if (n % 32 != 0) {\n            block.x = 1024;\n        }\n\n        block.x = block.x / (4 / sizeof(T));  // if using half, only need half of block.x\n\n        /* should pay attention to the rsqrt precision*/\n        generalScaleAddBiasResidualLayerNorm<T><<<grid, block, 0, stream>>>(\n            input, gamma, beta, bias, output, norm_output, m, n, scale_input, scale_residual);  // For gpt-3\n    }\n}\n\n#undef HALF_LAYERNORM_OPT\n#undef HALF_LAYERNORM_OPT2\n\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(float*       output,\n                                                            float*       norm_output,\n                                                            const float* input,\n                                                            const float* gamma,\n                                                            const float* beta,\n                                                            const float* bias,\n                                                            int          m,\n                                                            int          n,\n                                                            cudaStream_t stream,\n                                                            int          opt_version,\n                                                            float        scale_input,\n                                                            float        scale_residual);\n\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(half*        output,\n                                                            half*        norm_output,\n                                                            const half*  input,\n                                                            const half*  gamma,\n                                                            const half*  beta,\n                                                            const half*  bias,\n                                                            int          m,\n                                                            int          n,\n                                                            cudaStream_t stream,\n                                                            int          opt_version,\n                                                            float        scale_input,\n                                                            float        scale_residual);\n\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(__nv_bfloat16*       output,\n                                                            __nv_bfloat16*       norm_output,\n                                                            const __nv_bfloat16* input,\n                                                            const __nv_bfloat16* gamma,\n                                                            const __nv_bfloat16* beta,\n                                                            const __nv_bfloat16* bias,\n                                                            int                  m,\n                                                            int                  n,\n                                                            cudaStream_t         stream,\n                                                            int                  opt_version,\n                                                            float                scale_input,\n                                                            float                scale_residual);\n\n#endif\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void scaleAddBiasResidual(\n    T* output, const T* input, const T* bias, const int m, const int n, float scale_input, float scale_residual)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        float local_val = output[blockIdx.x * n + col_index];\n        float bias_val  = (bias == nullptr) ? (0.0f) : (float)bias[col_index];\n        local_val += bias_val;\n        local_val *= scale_input;\n        local_val += (float)input[blockIdx.x * n + col_index] * scale_residual;\n        output[blockIdx.x * n + col_index] = local_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeScaleAddBiasResidual(T*           output,\n                                const T*     input,\n                                const T*     bias,\n                                const int    m,\n                                const int    n,\n                                cudaStream_t stream,\n                                float        scale_input,\n                                float        scale_residual)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    scaleAddBiasResidual<<<grid, block, 0, stream>>>(output, input, bias, m, n, scale_input, scale_residual);\n}\n\ntemplate void invokeScaleAddBiasResidual(float*       output,\n                                         const float* input,\n                                         const float* bias,\n                                         const int    m,\n                                         const int    n,\n                                         cudaStream_t stream,\n                                         float        scale_input,\n                                         float        scale_residual);\n\ntemplate void invokeScaleAddBiasResidual(half*        output,\n                                         const half*  input,\n                                         const half*  bias,\n                                         const int    m,\n                                         const int    n,\n                                         cudaStream_t stream,\n                                         float        scale_input,\n                                         float        scale_residual);\n\n#ifdef ENABLE_BF16\ntemplate void invokeScaleAddBiasResidual(__nv_bfloat16*       output,\n                                         const __nv_bfloat16* input,\n                                         const __nv_bfloat16* bias,\n                                         const int            m,\n                                         const int            n,\n                                         cudaStream_t         stream,\n                                         float                scale_input,\n                                         float                scale_residual);\n#endif\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void ConformerDepthwiseConvBias(T*        out,\n                                           const T*  in,\n                                           const T*  weight,\n                                           const T*  bias,\n                                           const int batch_size,\n                                           const int seq_len,\n                                           const int hidden_unit,\n                                           const int kernel_size,\n                                           const int pad_size)\n{\n    int c_id = threadIdx.x;\n    int s_id = blockIdx.x % seq_len;\n    int b_id = blockIdx.x / seq_len;\n\n    int s_start = s_id - pad_size;\n    int s_end   = min(s_start + kernel_size, seq_len);\n    s_start     = max(s_start, 0);\n\n    int k_start = max(pad_size - s_id, 0);\n\n    in     = in + b_id * seq_len * hidden_unit + c_id;\n    weight = weight + c_id;\n\n    float val = 0.0f;\n    for (int i = s_start; i < s_end; ++i) {\n        val += (float)in[i * hidden_unit] * (float)weight[(k_start + i - s_start) * hidden_unit];\n    }\n    val = val + (float)bias[c_id];\n    // val = val * sigmoid<float>(val);\n\n    out[blockIdx.x * hidden_unit + c_id] = (T)val;\n}\n\ntemplate<typename T>\nvoid invokeConformerDepthwiseConvBias(T*           out,\n                                      const T*     in,\n                                      const T*     weight,\n                                      const T*     bias,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_unit,\n                                      const int    kernel_size,\n                                      const int    pad_size,\n                                      cudaStream_t stream)\n{\n    FT_CHECK(hidden_unit <= 1024);\n    ConformerDepthwiseConvBias<T><<<batch_size * seq_len, hidden_unit, 0, stream>>>(\n        out, in, weight, bias, batch_size, seq_len, hidden_unit, kernel_size, pad_size);\n}\ntemplate void invokeConformerDepthwiseConvBias(float*       out,\n                                               const float* in,\n                                               const float* weight,\n                                               const float* bias,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    hidden_unit,\n                                               const int    kernel_size,\n                                               const int    pad_size,\n                                               cudaStream_t stream);\n\ntemplate void invokeConformerDepthwiseConvBias(half*        out,\n                                               const half*  in,\n                                               const half*  weight,\n                                               const half*  bias,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    hidden_unit,\n                                               const int    kernel_size,\n                                               const int    pad_size,\n                                               cudaStream_t stream);\n\n#ifdef ENABLE_BF16\n\ntemplate void invokeConformerDepthwiseConvBias(__nv_bfloat16*       out,\n                                               const __nv_bfloat16* in,\n                                               const __nv_bfloat16* weight,\n                                               const __nv_bfloat16* bias,\n                                               const int            batch_size,\n                                               const int            seq_len,\n                                               const int            hidden_unit,\n                                               const int            kernel_size,\n                                               const int            pad_size,\n                                               cudaStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void ConformerDepthwiseConvBiasSilu(T*        out,\n                                               const T*  in,\n                                               const T*  weight,\n                                               const T*  bias,\n                                               const int batch_size,\n                                               const int seq_len,\n                                               const int hidden_unit,\n                                               const int kernel_size,\n                                               const int pad_size)\n{\n    int c_id = threadIdx.x;\n    int s_id = blockIdx.x % seq_len;\n    int b_id = blockIdx.x / seq_len;\n\n    int s_start = s_id - pad_size;\n    int s_end   = min(s_start + kernel_size, seq_len);\n    s_start     = max(s_start, 0);\n\n    int k_start = max(pad_size - s_id, 0);\n\n    in     = in + b_id * seq_len * hidden_unit;\n    weight = weight + c_id;\n\n    float val = 0.0f;\n    for (int i = s_start; i < s_end; ++i) {\n        val += (float)in[i * hidden_unit + c_id] * (float)weight[(k_start + i - s_start) * hidden_unit];\n    }\n    val = val + (float)bias[c_id];\n    val = val * sigmoid<float>(val);\n\n    out[blockIdx.x * hidden_unit + c_id] = val;\n}\n\ntemplate<typename T>\nvoid invokeConformerDepthwiseConvBiasSilu(T*           out,\n                                          const T*     in,\n                                          const T*     weight,\n                                          const T*     bias,\n                                          const int    batch_size,\n                                          const int    seq_len,\n                                          const int    hidden_unit,\n                                          const int    kernel_size,\n                                          const int    pad_size,\n                                          cudaStream_t stream)\n{\n    FT_CHECK(hidden_unit <= 1024);\n    ConformerDepthwiseConvBiasSilu<T><<<batch_size * seq_len, hidden_unit, 0, stream>>>(\n        out, in, weight, bias, batch_size, seq_len, hidden_unit, kernel_size, pad_size);\n}\ntemplate void invokeConformerDepthwiseConvBiasSilu(float*       out,\n                                                   const float* in,\n                                                   const float* weight,\n                                                   const float* bias,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    hidden_unit,\n                                                   const int    kernel_size,\n                                                   const int    pad_size,\n                                                   cudaStream_t stream);\n\ntemplate void invokeConformerDepthwiseConvBiasSilu(half*        out,\n                                                   const half*  in,\n                                                   const half*  weight,\n                                                   const half*  bias,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    hidden_unit,\n                                                   const int    kernel_size,\n                                                   const int    pad_size,\n                                                   cudaStream_t stream);\n\n#ifdef ENABLE_BF16\n\ntemplate void invokeConformerDepthwiseConvBiasSilu(__nv_bfloat16*       out,\n                                                   const __nv_bfloat16* in,\n                                                   const __nv_bfloat16* weight,\n                                                   const __nv_bfloat16* bias,\n                                                   const int            batch_size,\n                                                   const int            seq_len,\n                                                   const int            hidden_unit,\n                                                   const int            kernel_size,\n                                                   const int            pad_size,\n                                                   cudaStream_t         stream);\n#endif\n\n/////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void VarLenConformerDepthwiseConvBiasSilu(T* out,\n//                                                      const T* in,\n//                                                      const T* weight,\n//                                                      const T* bias,\n//                                                      const int* bid_start_end,\n//                                                      const T* bias_before_glu,\n//                                                      const int m,\n//                                                      const int batch_size,\n//                                                      const int seq_len,\n//                                                      const int hidden_unit,\n//                                                      const int kernel_size,\n//                                                      const int pad_size)\n// {\n//     __shared__ int b_start, b_end;\n//     if (threadIdx.x == 0) {\n//         // b_id = bid_start_end[blockIdx.x * 3];\n//         b_start = bid_start_end[blockIdx.x * 3 + 1];\n//         b_end = bid_start_end[blockIdx.x * 3 + 2];\n//     }\n//     __syncthreads();\n\n//     int cur_len = b_end - b_start;\n\n//     int c_id = threadIdx.x;\n//     int s_id = blockIdx.x - b_start;\n\n//     int s_start = s_id - pad_size;\n//     int s_end = min(s_start + kernel_size, seq_len);\n//     s_start = max(s_start, 0);\n\n//     int k_start = max(pad_size - s_id, 0);\n\n//     in = in + b_start * hidden_unit;\n//     weight = weight + c_id;\n\n//     float val = 0.0f;\n//     for (int i = s_start; i < s_end; ++i) {\n//         float cur_in = 0.f;\n//         if (i < cur_len)\n//             cur_in = (float)in[i * hidden_unit + c_id];\n//         else {\n//             // glu(0+bias)\n//             cur_in =\n//                 (float)ldg(&bias_before_glu[c_id]) * sigmoid<float>((float)ldg(&bias_before_glu[c_id +\n//                 hidden_unit]));\n//         }\n//         val += cur_in * (float)weight[(k_start + i - s_start) * hidden_unit];\n//     }\n//     val = val + (float)bias[c_id];\n//     val = val * sigmoid<float>(val);\n\n//     out[blockIdx.x * hidden_unit + c_id] = val;\n// }\n\n// template<typename T>\n// void invokeVarLenConformerDepthwiseConvBiasSilu(T* out,\n//                                                 const T* in,\n//                                                 const T* weight,\n//                                                 const T* bias,\n//                                                 const int* bid_start_end,\n//                                                 const T* bias_before_glu,\n//                                                 const int m,\n//                                                 const int batch_size,\n//                                                 const int seq_len,\n//                                                 const int hidden_unit,\n//                                                 const int kernel_size,\n//                                                 const int pad_size,\n//                                                 cudaStream_t stream)\n// {\n//     FT_CHECK(hidden_unit <= 1024);\n//     VarLenConformerDepthwiseConvBiasSilu<T><<<m, hidden_unit, 0, stream>>>(out,\n//                                                                            in,\n//                                                                            weight,\n//                                                                            bias,\n//                                                                            bid_start_end,\n//                                                                            bias_before_glu,\n//                                                                            m,\n//                                                                            batch_size,\n//                                                                            seq_len,\n//                                                                            hidden_unit,\n//                                                                            kernel_size,\n//                                                                            pad_size);\n// }\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(float* out,\n//                                                          const float* in,\n//                                                          const float* weight,\n//                                                          const float* bias,\n//                                                          const int* bid_start_end,\n//                                                          const float* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(half* out,\n//                                                          const half* in,\n//                                                          const half* weight,\n//                                                          const half* bias,\n//                                                          const int* bid_start_end,\n//                                                          const half* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n\n// #ifdef ENABLE_BF16\n\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(__nv_bfloat16* out,\n//                                                          const __nv_bfloat16* in,\n//                                                          const __nv_bfloat16* weight,\n//                                                          const __nv_bfloat16* bias,\n//                                                          const int* bid_start_end,\n//                                                          const __nv_bfloat16* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////\n\n// __global__ void\n// getBatchIDStartEndKernel(int* bid_start_end, const int* sequence_length, const int batch_size, const int seq_len)\n// {\n//     int index = 0;\n//     int total_seq_len = 0;\n//     for (int i = 0; i < batch_size; i++) {\n//         const int seq_len = sequence_length[i];\n//         for (int j = 0; j < seq_len; j++) {\n//             bid_start_end[index * 3] = i;\n//             bid_start_end[index * 3 + 1] = total_seq_len;\n//             bid_start_end[index * 3 + 2] = total_seq_len + seq_len;\n//             index++;\n//         }\n//         total_seq_len += seq_len;\n//     }\n// }\n\n// void invokeGetBatchIDStartEnd(\n//     int* bid_start_end, const int* sequence_length, const int batch_size, const int seq_len, cudaStream_t stream)\n// {\n//     getBatchIDStartEndKernel<<<1, 1, 0, stream>>>(bid_start_end, sequence_length, batch_size, seq_len);\n// }\n\n////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void bias_rebuild_padding(const T* src, T* dst, const T* bias, const int* padding_offset, const int n)\n// {\n//     const int tid = threadIdx.x;\n//     const int bid = blockIdx.x;\n//     const int dst_seq_id = bid + padding_offset[bid];\n//     const int src_seq_id = bid;\n\n//     for (int i = tid; i < n; i += blockDim.x) {\n//         if (bias != nullptr)\n//             dst[dst_seq_id * n + i] = src[src_seq_id * n + i] + bias[i];\n//         else\n//             dst[dst_seq_id * n + i] = src[src_seq_id * n + i];\n//     }\n// }\n\n// template<typename T>\n// void invokeBiasRebuildPadding(\n//     T* dst, const T* src, const T* bias, const int* padding_offset, const int m, const int n, cudaStream_t stream)\n// {\n//     // src: [token_num, hidden_dim]\n//     // dst: [batch_size*max_seq_len, hidden_dim]\n//     bias_rebuild_padding<<<m, 256, 0, stream>>>(src, dst, bias, padding_offset, n);\n// }\n\n// template void invokeBiasRebuildPadding(float* dst,\n//                                        const float* src,\n//                                        const float* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n\n// template void invokeBiasRebuildPadding(half* dst,\n//                                        const half* src,\n//                                        const half* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n\n// #ifdef ENABLE_BF16\n// template void invokeBiasRebuildPadding(__nv_bfloat16* dst,\n//                                        const __nv_bfloat16* src,\n//                                        const __nv_bfloat16* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n// #endif  // ENABLE_BF16\n\n// Encoder\ntemplate<typename T>\n__global__ void addQKVPBiasTranspose(T* q_out,\n                                     T* k_out,\n                                     T* v_out,\n                                     const T* __restrict q_in,\n                                     const T* __restrict bias_q,\n                                     const T* __restrict k_in,\n                                     const T* __restrict bias_k,\n                                     const T* __restrict v_in,\n                                     const T* __restrict bias_v,\n                                     T*        p_buf,\n                                     const T*  P,\n                                     T*        q_buf_bias_v,\n                                     const T*  pos_bias_u,\n                                     const T*  pos_bias_v,\n                                     const int batch_size,\n                                     const int seq_len,\n                                     const int head_num,\n                                     const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n        /*\n                q_out[target_id] = __ldg(&q_in[src_id]);\n                q_out[target_id] = q_out[target_id] + __ldg(&bias_q[col_id]);\n\n                k_out[target_id] = __ldg(&k_in[src_id]);\n                k_out[target_id] = k_out[target_id] + __ldg(&bias_k[col_id]);\n\n                v_out[target_id] = __ldg(&v_in[src_id]);\n                v_out[target_id] = v_out[target_id] + __ldg(&bias_v[col_id]);\n        */\n        T q_val          = __ldg(&q_in[src_id]) + __ldg(&bias_q[col_id]);\n        q_out[target_id] = q_val + __ldg(&pos_bias_u[col_id]);\n\n        k_out[target_id] = __ldg(&k_in[src_id]) + __ldg(&bias_k[col_id]);\n\n        v_out[target_id] = __ldg(&v_in[src_id]) + __ldg(&bias_v[col_id]);\n\n        p_buf[target_id] = __ldg(&P[src_id]);\n\n        q_buf_bias_v[target_id] = q_val + __ldg(&pos_bias_v[col_id]);\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVPTranspose(T* q_out,\n                              T* k_out,\n                              T* v_out,\n                              const T* __restrict q_in,\n                              const T* __restrict k_in,\n                              const T* __restrict v_in,\n                              T*        p_buf,\n                              const T*  P,\n                              T*        q_buf_bias_v,\n                              const T*  pos_bias_u,\n                              const T*  pos_bias_v,\n                              const int batch_size,\n                              const int seq_len,\n                              const int head_num,\n                              const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        T q_val          = __ldg(&q_in[src_id]);\n        q_out[target_id] = q_val + __ldg(&pos_bias_u[col_id]);\n\n        k_out[target_id] = __ldg(&k_in[src_id]);\n        v_out[target_id] = __ldg(&v_in[src_id]);\n\n        p_buf[target_id]        = __ldg(&P[src_id]);\n        q_buf_bias_v[target_id] = q_val + __ldg(&pos_bias_v[col_id]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVPBiasTranspose(T*           q_buf,\n                                T*           k_buf,\n                                T*           v_buf,\n                                T*           Q,\n                                const T*     bias_Q,\n                                T*           K,\n                                const T*     bias_K,\n                                T*           V,\n                                const T*     bias_V,\n                                T*           p_buf,\n                                T*           P,\n                                T*           q_buf_bias_v,\n                                const T*     pos_bias_u,\n                                const T*     pos_bias_v,\n                                const int    batch_size,\n                                const int    seq_len,\n                                const int    head_num,\n                                const int    size_per_head,\n                                cudaStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, seq_len);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVPBiasTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                                k_buf,\n                                                                v_buf,\n                                                                Q,\n                                                                bias_Q,\n                                                                K,\n                                                                bias_K,\n                                                                V,\n                                                                bias_V,\n                                                                p_buf,\n                                                                P,\n                                                                q_buf_bias_v,\n                                                                pos_bias_u,\n                                                                pos_bias_v,\n                                                                batch_size,\n                                                                seq_len,\n                                                                head_num,\n                                                                size_per_head);\n        }\n        else {\n            QKVPTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                         k_buf,\n                                                         v_buf,\n                                                         Q,\n                                                         K,\n                                                         V,\n                                                         p_buf,\n                                                         P,\n                                                         q_buf_bias_v,\n                                                         pos_bias_u,\n                                                         pos_bias_v,\n                                                         batch_size,\n                                                         seq_len,\n                                                         head_num,\n                                                         size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVPBiasTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                                    (half2*)k_buf,\n                                                                    (half2*)v_buf,\n                                                                    (const half2*)Q,\n                                                                    (const half2*)bias_Q,\n                                                                    (const half2*)K,\n                                                                    (const half2*)bias_K,\n                                                                    (const half2*)V,\n                                                                    (const half2*)bias_V,\n                                                                    (half2*)p_buf,\n                                                                    (const half2*)P,\n                                                                    (half2*)q_buf_bias_v,\n                                                                    (const half2*)pos_bias_u,\n                                                                    (const half2*)pos_bias_v,\n                                                                    batch_size,\n                                                                    seq_len,\n                                                                    head_num,\n                                                                    size_per_head / 2);\n        }\n        else {\n            QKVPTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                             (half2*)k_buf,\n                                                             (half2*)v_buf,\n                                                             (const half2*)Q,\n                                                             (const half2*)K,\n                                                             (const half2*)V,\n                                                             (half2*)p_buf,\n                                                             (const half2*)P,\n                                                             (half2*)q_buf_bias_v,\n                                                             (const half2*)pos_bias_u,\n                                                             (const half2*)pos_bias_v,\n                                                             batch_size,\n                                                             seq_len,\n                                                             head_num,\n                                                             size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\ntemplate void invokeAddQKVPBiasTranspose(float*       q_buf,\n                                         float*       k_buf,\n                                         float*       v_buf,\n                                         float*       Q,\n                                         const float* bias_Q,\n                                         float*       K,\n                                         const float* bias_K,\n                                         float*       V,\n                                         const float* bias_V,\n                                         float*       p_buf,\n                                         float*       P,\n                                         float*       q_buf_bias_v,\n                                         const float* pos_bias_u,\n                                         const float* pos_bias_v,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         cudaStream_t stream);\n\ntemplate void invokeAddQKVPBiasTranspose(half*        q_buf,\n                                         half*        k_buf,\n                                         half*        v_buf,\n                                         half*        Q,\n                                         const half*  bias_Q,\n                                         half*        K,\n                                         const half*  bias_K,\n                                         half*        V,\n                                         const half*  bias_V,\n                                         half*        p_buf,\n                                         half*        P,\n                                         half*        q_buf_bias_v,\n                                         const half*  pos_bias_u,\n                                         const half*  pos_bias_v,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         cudaStream_t stream);\n\n// TODO(bhsueh) Rename the softmax_kernel_v4 to softmax_kernel\ntemplate<int ITEMS_PER_THREAD, typename T, typename T_IN>\n__global__ void add_softmax_kernel_v4(T*          qk_buf_,\n                                      const T_IN* qk_buf_src,\n                                      const T_IN* qp_buf_src,\n                                      const T*    attr_mask,\n                                      const int   batch_size,\n                                      const int   head_num,\n                                      const int   seq_len,\n                                      const T     scalar)\n{\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        float            data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n\n            float qk = static_cast<float>(qk_buf_src[qk_offset]);\n            qk += static_cast<float>(qp_buf_src[qk_offset]);\n\n            float mask_val = static_cast<float>(ldg(&attr_mask[mask_offset]));\n\n            mask_val = (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk * static_cast<float>(scalar) + mask_val;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            qk_buf_[qk_offset] = (T)(data[i] * s_mean);\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD>\n__global__ void add_softmax_kernel_v4_half2(T*        qk_buf_,\n                                            const T*  qp_buf_,\n                                            const T*  attr_mask,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int seq_len,\n                                            const T   scalar)\n{\n    using T2                  = typename TypeConverter<T>::Type;\n    T2*       qk_buf_half2    = (T2*)qk_buf_;\n    T2*       qp_buf_half2    = (T2*)qp_buf_;\n    const T2* attr_mask_half2 = (const T2*)attr_mask;\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        T2               data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i\n                        + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n\n            T2 qk       = qk_buf_half2[qk_offset];\n            qk          = hadd2<T2>(qk, qp_buf_half2[qk_offset]);\n            T2 mask_val = ldg(&attr_mask_half2[mask_offset]);\n            mask_val    = hmul2<T2>(hsub2<T2>(cuda_cast<T2>(1.0f), mask_val), cuda_cast<T2>(-10000.0f));\n\n            data[i] = hadd2<T2>(hmul2<T2>(qk, cuda_cast<T2>(scalar)), mask_val);\n\n            local_max = fmax(local_max, fmax((float)data[i].x, (float)data[i].y));\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            data[i] = hexp2<T2>(hsub2<T2>(data[i], cuda_cast<T2>(s_max)));\n            local_sum += (float)(data[i].x + data[i].y);\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i\n                        + threadIdx.x;\n            qk_buf_half2[qk_offset] = hmul2<T2>(data[i], cuda_cast<T2>(s_mean));\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int NUM>\n__global__ void add_softmax_kernel_v5_half2(T*        qk_buf_,\n                                            const T*  qp_buf_,\n                                            const T*  attr_mask,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int seq_len,\n                                            const T   scalar)\n{\n    using T2         = typename TypeConverter<T>::Type;\n    T2* qk_buf_half2 = (T2*)qk_buf_;\n    T2* qp_buf_half2 = (T2*)qp_buf_;\n\n    const T2* attr_mask_half2 = (const T2*)attr_mask;\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x * NUM) {\n        T2 data[NUM][ITEMS_PER_THREAD];\n\n        int qk_offset[NUM];\n\n        __shared__ float s_sum[NUM], s_max[NUM];\n        float            local_max[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            int mask_offset[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n                mask_offset[j] =\n                    (blockIdx.y * seq_len + seq_id + j * gridDim.x) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n            }\n\n            T2 mask_val[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = ldg(&attr_mask_half2[mask_offset[j]]);\n            }\n\n            T2 qk[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk[j] = qk_buf_half2[qk_offset[j]];\n                qk[j] = hadd2<T2>(qk[j], qp_buf_half2[qk_offset[j]]);\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = hmul2<T2>(hsub2<T2>(cuda_cast<T2>(1.0f), mask_val[j]), cuda_cast<T2>(-10000.0f));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i]   = hadd2<T2>(hmul2<T2>(qk[j], cuda_cast<T2>(scalar)), mask_val[j]);\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, NUM>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, NUM>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i] = hexp2<T2>(hsub2<T2>(data[j][i], cuda_cast<T2>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, NUM>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, NUM>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_buf_half2[qk_offset[j]] = hmul2<T2>(data[j][i], cuda_cast<T2>(s_sum[j]));\n            }\n        }\n    }\n}\n\n#define SOFTMAX_KERNEL(ITEMS_PER_THREAD)                                                                               \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            add_softmax_kernel_v5_half2<half, ITEMS_PER_THREAD, 4><<<grid, block, 0, stream>>>((half*)buffer,          \\\n                                                                                               (const half*)qp_buf,    \\\n                                                                                               (const half*)attr_mask, \\\n                                                                                               batch_size,             \\\n                                                                                               head_num,               \\\n                                                                                               seq_len,                \\\n                                                                                               (const half)scalar);    \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            add_softmax_kernel_v4_half2<half, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>((half*)buffer,             \\\n                                                                                            (const half*)qp_buf,       \\\n                                                                                            (const half*)attr_mask,    \\\n                                                                                            batch_size,                \\\n                                                                                            head_num,                  \\\n                                                                                            seq_len,                   \\\n                                                                                            (const half)scalar);       \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        add_softmax_kernel_v4<ITEMS_PER_THREAD, T, T_IN><<<grid, block, 0, stream>>>(                                  \\\n            buffer, buffer_src, qp_buf, attr_mask, batch_size, head_num, seq_len, scalar);                             \\\n    }\n\n#ifdef ENABLE_BF16\n#define SOFTMAX_KERNEL_BF16(ITEMS_PER_THREAD)                                                                          \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            add_softmax_kernel_v5_half2<__nv_bfloat16, ITEMS_PER_THREAD, 4>                                            \\\n                <<<grid, block, 0, stream>>>((__nv_bfloat16*)buffer,                                                   \\\n                                             (const __nv_bfloat16*)qp_buf,                                             \\\n                                             (const __nv_bfloat16*)attr_mask,                                          \\\n                                             batch_size,                                                               \\\n                                             head_num,                                                                 \\\n                                             seq_len,                                                                  \\\n                                             (const __nv_bfloat16)scalar);                                             \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            add_softmax_kernel_v4_half2<__nv_bfloat16, ITEMS_PER_THREAD>                                               \\\n                <<<grid, block, 0, stream>>>((__nv_bfloat16*)buffer,                                                   \\\n                                             (const __nv_bfloat16*)qp_buf,                                             \\\n                                             (const __nv_bfloat16*)attr_mask,                                          \\\n                                             batch_size,                                                               \\\n                                             head_num,                                                                 \\\n                                             seq_len,                                                                  \\\n                                             (const __nv_bfloat16)scalar);                                             \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        add_softmax_kernel_v4<ITEMS_PER_THREAD, __nv_bfloat16, T_IN><<<grid, block, 0, stream>>>(                      \\\n            buffer, buffer_src, qp_buf, attr_mask, batch_size, head_num, seq_len, scalar);                             \\\n    }\n#endif  // ENABLE_BF16\n\ntemplate<typename T, typename T_IN>\nvoid invokeAddMaskedSoftMax(T*           buffer,\n                            const T_IN*  buffer_src,\n                            const T_IN*  qp_buf,\n                            const T*     attr_mask,\n                            const int    batch_size,\n                            const int    seq_len,\n                            const int    head_num,\n                            const T      scalar,\n                            cudaStream_t stream)\n{\n\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && seq_len % 2 == 0;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\nvoid invokeAddMaskedSoftMax(__nv_bfloat16*       buffer,\n                            const __nv_bfloat16* buffer_src,\n                            const __nv_bfloat16* qp_buf,\n                            const __nv_bfloat16* attr_mask,\n                            const int            batch_size,\n                            const int            seq_len,\n                            const int            head_num,\n                            const __nv_bfloat16  scalar,\n                            cudaStream_t         stream)\n{\n\n    using T_IN = __nv_bfloat16;\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = seq_len % 2 == 0;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL_BF16(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL_BF16(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL_BF16(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL_BF16(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n\ntemplate<>\nvoid invokeAddMaskedSoftMax(__nv_bfloat16*       buffer,\n                            const float*         buffer_src,\n                            const float*         qp_buf,\n                            const __nv_bfloat16* attr_mask,\n                            const int            batch_size,\n                            const int            seq_len,\n                            const int            head_num,\n                            const __nv_bfloat16  scalar,\n                            cudaStream_t         stream)\n{\n    using T_IN = float;\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = false;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL_BF16(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL_BF16(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL_BF16(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL_BF16(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n#endif  // ENABLE_BF16\n\ntemplate void invokeAddMaskedSoftMax(float*       buffer,\n                                     const float* buffer_src,\n                                     const float* qp_buf,\n                                     const float* attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const float  scalar,\n                                     cudaStream_t stream);\n\ntemplate void invokeAddMaskedSoftMax(half*        buffer,\n                                     const float* buffer_src,\n                                     const float* qp_buf,\n                                     const half*  attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const half   scalar,\n                                     cudaStream_t stream);\n\ntemplate void invokeAddMaskedSoftMax(half*        buffer,\n                                     const half*  buffer_src,\n                                     const half*  qp_buf,\n                                     const half*  attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const half   scalar,\n                                     cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddMaskedSoftMax(__nv_bfloat16*       buffer,\n                                     const __nv_bfloat16* buffer_src,\n                                     const __nv_bfloat16* qp_buf,\n                                     const __nv_bfloat16* attr_mask,\n                                     const int            batch_size,\n                                     const int            seq_len,\n                                     const int            head_num,\n                                     const __nv_bfloat16  scalar,\n                                     cudaStream_t         stream);\n\ntemplate void invokeAddMaskedSoftMax(__nv_bfloat16*       buffer,\n                                     const float*         buffer_src,\n                                     const float*         qp_buf,\n                                     const __nv_bfloat16* attr_mask,\n                                     const int            batch_size,\n                                     const int            seq_len,\n                                     const int            head_num,\n                                     const __nv_bfloat16  scalar,\n                                     cudaStream_t         stream);\n#endif  // ENABLE_BF16\n\n// Decoder invokeAddQKVBiasTranspose\ntemplate<typename T>\n__global__ void addQKVBiasTranspose(T* q_out,\n                                    T* k_out,\n                                    T* v_out,\n                                    const T* __restrict q_in,\n                                    const T* __restrict bias_q,\n                                    const T* __restrict k_in,\n                                    const T* __restrict bias_k,\n                                    const T* __restrict v_in,\n                                    const T* __restrict bias_v,\n                                    const int batch_size,\n                                    const int seq_len1,\n                                    const int seq_len2,\n                                    const int head_num,\n                                    const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id1  = batch_id * seq_len1 + word_id;\n    const int row_id2  = batch_id * seq_len2 + word_id;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id    = col_id / size_per_head;\n        const int size_id    = col_id % size_per_head;\n        const int target_id1 = batch_id * (head_num * seq_len1 * size_per_head) + head_id * seq_len1 * size_per_head\n                               + word_id * size_per_head + size_id;\n        const int target_id2 = batch_id * (head_num * seq_len2 * size_per_head) + head_id * seq_len2 * size_per_head\n                               + word_id * size_per_head + size_id;\n\n        const int src_id1 = row_id1 * n + col_id;\n        const int src_id2 = row_id2 * n + col_id;\n\n        if (word_id < seq_len1)\n            q_out[target_id1] = __ldg(&q_in[src_id1]) + __ldg(&bias_q[col_id]);\n\n        if (word_id < seq_len2) {\n            k_out[target_id2] = __ldg(&k_in[src_id2]) + __ldg(&bias_k[col_id]);\n\n            v_out[target_id2] = __ldg(&v_in[src_id2]) + __ldg(&bias_v[col_id]);\n        }\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVTranspose(T* q_out,\n                             T* k_out,\n                             T* v_out,\n                             const T* __restrict q_in,\n                             const T* __restrict k_in,\n                             const T* __restrict v_in,\n                             const int batch_size,\n                             const int seq_len1,\n                             const int seq_len2,\n                             const int head_num,\n                             const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id1  = batch_id * seq_len1 + word_id;\n    const int row_id2  = batch_id * seq_len2 + word_id;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id    = col_id / size_per_head;\n        const int size_id    = col_id % size_per_head;\n        const int target_id1 = batch_id * (head_num * seq_len1 * size_per_head) + head_id * seq_len1 * size_per_head\n                               + word_id * size_per_head + size_id;\n        const int target_id2 = batch_id * (head_num * seq_len2 * size_per_head) + head_id * seq_len2 * size_per_head\n                               + word_id * size_per_head + size_id;\n\n        const int src_id1 = row_id1 * n + col_id;\n        const int src_id2 = row_id2 * n + col_id;\n\n        if (word_id < seq_len1)\n            q_out[target_id1] = __ldg(&q_in[src_id1]);\n\n        if (word_id < seq_len2) {\n            k_out[target_id2] = __ldg(&k_in[src_id2]);\n            v_out[target_id2] = __ldg(&v_in[src_id2]);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasTranspose(T*           q_buf,\n                               T*           k_buf,\n                               T*           v_buf,\n                               T*           Q,\n                               const T*     bias_Q,\n                               T*           K,\n                               const T*     bias_K,\n                               T*           V,\n                               const T*     bias_V,\n                               const int    batch_size,\n                               const int    seq_len1,\n                               const int    seq_len2,\n                               const int    head_num,\n                               const int    size_per_head,\n                               cudaStream_t stream)\n{\n    const int k         = head_num * size_per_head;\n    int       seq_len12 = max(seq_len1, seq_len2);\n    dim3      grid(batch_size, seq_len12);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVBiasTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                               k_buf,\n                                                               v_buf,\n                                                               Q,\n                                                               bias_Q,\n                                                               K,\n                                                               bias_K,\n                                                               V,\n                                                               bias_V,\n                                                               batch_size,\n                                                               seq_len1,\n                                                               seq_len2,\n                                                               head_num,\n                                                               size_per_head);\n        }\n        else {\n            QKVTranspose<T><<<grid, block, 0, stream>>>(\n                q_buf, k_buf, v_buf, Q, K, V, batch_size, seq_len1, seq_len2, head_num, size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVBiasTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                                   (half2*)k_buf,\n                                                                   (half2*)v_buf,\n                                                                   (const half2*)Q,\n                                                                   (const half2*)bias_Q,\n                                                                   (const half2*)K,\n                                                                   (const half2*)bias_K,\n                                                                   (const half2*)V,\n                                                                   (const half2*)bias_V,\n                                                                   batch_size,\n                                                                   seq_len1,\n                                                                   seq_len2,\n                                                                   head_num,\n                                                                   size_per_head / 2);\n        }\n        else {\n            QKVTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                            (half2*)k_buf,\n                                                            (half2*)v_buf,\n                                                            (const half2*)Q,\n                                                            (const half2*)K,\n                                                            (const half2*)V,\n                                                            batch_size,\n                                                            seq_len1,\n                                                            seq_len2,\n                                                            head_num,\n                                                            size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\ntemplate void invokeAddQKVBiasTranspose(float*       q_buf,\n                                        float*       k_buf,\n                                        float*       v_buf,\n                                        float*       Q,\n                                        const float* bias_Q,\n                                        float*       K,\n                                        const float* bias_K,\n                                        float*       V,\n                                        const float* bias_V,\n                                        const int    batch_size,\n                                        const int    seq_len1,\n                                        const int    seq_len2,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        cudaStream_t stream);\n\ntemplate void invokeAddQKVBiasTranspose(half*        q_buf,\n                                        half*        k_buf,\n                                        half*        v_buf,\n                                        half*        Q,\n                                        const half*  bias_Q,\n                                        half*        K,\n                                        const half*  bias_K,\n                                        half*        V,\n                                        const half*  bias_V,\n                                        const int    batch_size,\n                                        const int    seq_len1,\n                                        const int    seq_len2,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        cudaStream_t stream);\n\n// Tile\ntemplate<typename T>\n__global__ void repeat_beam_size(T* out, const T* __restrict in, int beam_size, int n)\n{\n    int bid = blockIdx.x / beam_size;\n    out     = out + blockIdx.x * n;\n    in      = in + bid * n;\n    for (int id = threadIdx.x; id < n; id += blockDim.x) {\n        out[id] = in[id];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRepeatBeamSize(T* out, const T* in, const int m, const int n, const int beam_size, cudaStream_t stream)\n{\n    dim3 block, grid;\n    block.x = std::min(1024, n);\n    grid.x  = m * beam_size;\n    repeat_beam_size<T><<<grid, block, 0, stream>>>(out, in, beam_size, n);\n}\n\ntemplate void\ninvokeRepeatBeamSize(float* out, const float* in, const int m, const int n, const int beam_size, cudaStream_t stream);\ntemplate void\ninvokeRepeatBeamSize(half* out, const half* in, const int m, const int n, const int beam_size, cudaStream_t stream);\ntemplate void\ninvokeRepeatBeamSize(int* out, const int* in, const int m, const int n, const int beam_size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeRepeatBeamSize(\n    __nv_bfloat16* out, const __nv_bfloat16* in, const int m, const int n, const int beam_size, cudaStream_t stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////\n__global__ void getWenetOutLensKernel(int* out, const int* in, const int batch_size, const int max_seq_len)\n{\n    if (threadIdx.x < batch_size) {\n        int cur      = in[threadIdx.x];\n        int next_max = max_seq_len;\n\n        next_max = (next_max - 1) / 2;\n        cur      = (cur + 1) / 2;\n        cur      = min(cur, next_max);\n\n        next_max = (next_max - 1) / 2;\n        cur      = (cur + 1) / 2;\n        cur      = min(cur, next_max);\n\n        out[threadIdx.x] = cur;\n    }\n}\n\nvoid invokeGetWenetOutLens(int* out, const int* in, const int batch_size, const int max_seq_len, cudaStream_t stream)\n{\n    FT_CHECK(batch_size <= 1024);\n    getWenetOutLensKernel<<<1, batch_size, 0, stream>>>(out, in, batch_size, max_seq_len);\n    sync_check_cuda_error();\n}\n////////////////////////////////////////////////////////////////////////////\n// __global__ void getPaddingOffsetKernelV2(size_t* valid_word_num,\n//                                          int* tmp_mask_offset,\n//                                          const int* sequence_length,\n//                                          const int batch_size,\n//                                          const int max_seq_len)\n// {\n//     // do cumulated sum\n//     int total_seq_len = 0;\n//     int cum_offset = 0;\n//     int index = 0;\n//     for (int i = 0; i < batch_size; i++) {\n//         const int seq_len = sequence_length[i];\n//         for (int j = 0; j < seq_len; j++) {\n//             tmp_mask_offset[index] = cum_offset;\n//             index++;\n//         }\n//         cum_offset += max_seq_len - seq_len;\n//         total_seq_len += seq_len;\n//     }\n//     valid_word_num[0] = (size_t)total_seq_len;\n// }\n\n// void invokeGetPaddingOffset(size_t* d_token_num,\n//                             int* tmp_mask_offset,\n//                             const int* sequence_lengths,\n//                             const int batch_size,\n//                             const int max_seq_len,\n//                             cudaStream_t stream)\n// {\n//     getPaddingOffsetKernelV2<<<1, 1, 0, stream>>>(\n//         d_token_num, tmp_mask_offset, sequence_lengths, batch_size, max_seq_len);\n//     sync_check_cuda_error();\n// }\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T, bool IS_CROSS>\n__global__ void buildDecoderAttentionMaskKernel(T*         attention_mask,\n                                                const int* sequence_lengths1,\n                                                const int  max_seq_len1,\n                                                const int* sequence_lengths2,\n                                                const int  max_seq_len2)\n{\n    // sequence_lengths1: [batch_size]\n    // sequence_lengths2: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len1, max_seq_len2]\n    const int s1_id = blockIdx.x % max_seq_len1;\n    const int b_id  = blockIdx.x / max_seq_len1;\n    attention_mask += (b_id * max_seq_len1 + s1_id) * max_seq_len2;\n\n    const int len1 = sequence_lengths1[b_id];\n    int       len2 = 0;\n    if (IS_CROSS)\n        len2 = sequence_lengths2[b_id];\n\n    for (int i = threadIdx.x; i < max_seq_len2; i += blockDim.x) {\n        T val = (T)(0.0f);\n        if (IS_CROSS) {\n            if (i < len2)\n                val = (T)(1.0f);\n        }\n        else {\n            if (i < len1 && i <= s1_id)\n                val = (T)(1.0f);\n        }\n        attention_mask[i] = val;\n    }\n}\n\ntemplate<typename T, bool IS_CROSS>\nvoid invokeBuildDecoderAttentionMask(T*           attention_mask,\n                                     const int*   sequence_lengths1,\n                                     const int*   sequence_lengths2,\n                                     const int    batch_size,\n                                     const int    max_seq_len1,\n                                     const int    max_seq_len2,\n                                     cudaStream_t stream)\n{\n    buildDecoderAttentionMaskKernel<T, IS_CROSS>\n        <<<batch_size * max_seq_len1, std::min(1024, max_seq_len2), 0, stream>>>(\n            attention_mask, sequence_lengths1, max_seq_len1, sequence_lengths2, max_seq_len2);\n}\n\ntemplate void invokeBuildDecoderAttentionMask<float, false>(float*       attention_mask,\n                                                            const int*   sequence_lengths1,\n                                                            const int*   sequence_lengths2,\n                                                            const int    batch_size,\n                                                            const int    max_seq_len1,\n                                                            const int    max_seq_len2,\n                                                            cudaStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<float, true>(float*       attention_mask,\n                                                           const int*   sequence_lengths1,\n                                                           const int*   sequence_lengths2,\n                                                           const int    batch_size,\n                                                           const int    max_seq_len1,\n                                                           const int    max_seq_len2,\n                                                           cudaStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<half, false>(half*        attention_mask,\n                                                           const int*   sequence_lengths1,\n                                                           const int*   sequence_lengths2,\n                                                           const int    batch_size,\n                                                           const int    max_seq_len1,\n                                                           const int    max_seq_len2,\n                                                           cudaStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<half, true>(half*        attention_mask,\n                                                          const int*   sequence_lengths1,\n                                                          const int*   sequence_lengths2,\n                                                          const int    batch_size,\n                                                          const int    max_seq_len1,\n                                                          const int    max_seq_len2,\n                                                          cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildDecoderAttentionMask<__nv_bfloat16, false>(__nv_bfloat16* attention_mask,\n                                                                    const int*     sequence_lengths1,\n                                                                    const int*     sequence_lengths2,\n                                                                    const int      batch_size,\n                                                                    const int      max_seq_len1,\n                                                                    const int      max_seq_len2,\n                                                                    cudaStream_t   stream);\ntemplate void invokeBuildDecoderAttentionMask<__nv_bfloat16, true>(__nv_bfloat16* attention_mask,\n                                                                   const int*     sequence_lengths1,\n                                                                   const int*     sequence_lengths2,\n                                                                   const int      batch_size,\n                                                                   const int      max_seq_len1,\n                                                                   const int      max_seq_len2,\n                                                                   cudaStream_t   stream);\n\n#endif\n//////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T, int EPT>\n__global__ void biasLogSoftmaxKernel(float*       log_probs,\n                                     const T*     logits,\n                                     const T*     bias,\n                                     const int*   lengths,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const size_t vocab_size,\n                                     const size_t vocab_size_padded)\n{\n    constexpr bool IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int tidx = threadIdx.x;  // vocab dim\n    int bidx = blockIdx.x;   // batch dim\n    int step = blockIdx.y;   // step dim\n\n    __shared__ float s_max_logit, s_sum_logit;\n\n    bool is_valid = (bidx < batch_size) && (step < max_input_length);\n    if (lengths != nullptr)\n        is_valid = is_valid && (step < lengths[bidx]);\n    if (is_valid) {\n        // reposition logits to data for the current batch.\n        logits += bidx * max_input_length * vocab_size_padded + step * vocab_size_padded;\n        log_probs += bidx * max_input_length * vocab_size + step * vocab_size;\n\n        // load and add bias\n        T local_logit[EPT];\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                local_logit[i] = logits[cur_idx];\n                if (bias != nullptr)\n                    local_logit[i] = (float)local_logit[i] + (float)bias[cur_idx];\n            }\n            // else\n            //     local_logit[i] = -MAX_T_VAL;\n        }\n        // Find max(logits).\n        float local_max = -MAX_T_VAL;\n        float val       = -MAX_T_VAL;\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                val       = static_cast<float>(local_logit[i]);\n                local_max = fmax(local_max, val);\n            }\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (tidx == 0) {\n            s_max_logit = max_val;\n        }\n        __syncthreads();\n\n        // Calculate the denominator: sum_i exp(logits[i])\n        float local_sum_exp = 0.0f;\n// val = 0.0f;\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                val = __expf(static_cast<float>(local_logit[i]) - s_max_logit);\n                local_sum_exp += val;\n            }\n        }\n\n        float sum_exp = blockDim.x <= 32 ? warpReduceSum(local_sum_exp) : blockReduceSum<float>(local_sum_exp);\n        if (tidx == 0) {\n            s_sum_logit = sum_exp;\n        }\n        __syncthreads();\n\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size)\n                log_probs[cur_idx] = static_cast<float>(local_logit[i]) - s_max_logit - __logf(s_sum_logit + 1e-9f);\n        }\n    }\n}\n\ntemplate<typename T>\n__global__ void biasLogSoftmaxKernel(float*       log_probs,\n                                     const T*     logits,\n                                     const T*     bias,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const size_t vocab_size,\n                                     const size_t vocab_size_padded)\n{\n    constexpr bool IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int bid    = blockIdx.x;\n    int offset = bid * vocab_size_padded;\n\n    float local_max = -MAX_T_VAL;\n    float val       = -MAX_T_VAL;\n\n    __shared__ float s_max_logit;\n    __shared__ float s_sum_logit;\n\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        if (tid < vocab_size) {\n            float bias_val          = (bias != nullptr) ? bias[tid] : (T)0.0f;\n            log_probs[offset + tid] = (float)logits[offset + tid] + bias_val;\n        }\n        // else {\n        //     log_probs[offset + tid] = -MAX_T_VAL;\n        // }\n        local_max = fmax(local_max, (float)log_probs[offset + tid]);\n    }\n\n    float max_val = blockReduceMax<float>(local_max);\n    if (threadIdx.x == 0) {\n        s_max_logit = max_val;\n    }\n    __syncthreads();\n\n    float local_sum_exp = 0.0f;\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        val = __expf((float)log_probs[offset + tid] - s_max_logit);\n        local_sum_exp += val;\n    }\n\n    float sum_exp = blockReduceSum<float>(local_sum_exp);\n    if (threadIdx.x == 0) {\n        s_sum_logit = sum_exp;\n    }\n    __syncthreads();\n\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        log_probs[offset + tid] = (float)log_probs[offset + tid] - s_max_logit - __logf(s_sum_logit + 1e-9f);\n    }\n}\n\ntemplate<typename T>\nvoid invokeBiasLogSoftmax(float*       log_probs,\n                          const T*     logits,\n                          const T*     bias,\n                          const int*   lengths,\n                          const size_t max_input_length,\n                          const size_t batch_size,\n                          const size_t vocab_size,\n                          const size_t vocab_size_padded,\n                          bool         batch_first,\n                          cudaStream_t stream)\n{\n    // FT_CHECK(vocab_size <= 768 * 6);\n    dim3 block, grid;\n\n    // Better perf at the cost of register pressure\n    if (vocab_size > 768 * 5 && vocab_size <= 768 * 6) {\n        block.x = 768;\n        grid.x  = batch_size;\n        grid.y  = max_input_length;\n        biasLogSoftmaxKernel<T, 6><<<grid, block, 0, stream>>>(\n            log_probs, logits, bias, lengths, max_input_length, batch_size, vocab_size, vocab_size_padded);\n    }\n    else {\n        block.x = std::min((int)vocab_size, 1024);\n        grid.x  = batch_size * max_input_length;\n        /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n        biasLogSoftmaxKernel<T><<<grid, block, 0, stream>>>(\n            log_probs, logits, bias, max_input_length, batch_size, vocab_size, vocab_size_padded);\n    }\n}\n\ntemplate void invokeBiasLogSoftmax<float>(float*       log_probs,\n                                          const float* logits,\n                                          const float* bias,\n                                          const int*   lengths,\n                                          const size_t max_input_length,\n                                          const size_t batch_size,\n                                          const size_t vocab_size,\n                                          const size_t vocab_size_padded,\n                                          bool         batch_first,\n                                          cudaStream_t stream);\ntemplate void invokeBiasLogSoftmax<half>(float*       log_probs,\n                                         const half*  logits,\n                                         const half*  bias,\n                                         const int*   lengths,\n                                         const size_t max_input_length,\n                                         const size_t batch_size,\n                                         const size_t vocab_size,\n                                         const size_t vocab_size_padded,\n                                         bool         batch_first,\n                                         cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBiasLogSoftmax<__nv_bfloat16>(float*               log_probs,\n                                                  const __nv_bfloat16* logits,\n                                                  const __nv_bfloat16* bias,\n                                                  const int*           lengths,\n                                                  const size_t         max_input_length,\n                                                  const size_t         batch_size,\n                                                  const size_t         vocab_size,\n                                                  const size_t         vocab_size_padded,\n                                                  bool                 batch_first,\n                                                  cudaStream_t         stream);\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2022.  Authored by Yuqing Ding.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/models/wenet/WenetKernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\nnamespace {\nconstexpr auto EPS = 1e-6f;  // 1e-5;\n}\n\ntemplate<typename T>\n__inline__ __device__ T sigmoid(T x)\n{\n    return T(1.0f) / (T(1.0f) + exp(-x));\n    // return T(1.0f) / (T(1.0f) + __expf(-x));\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void add_bias_mul(T* out, const T* __restrict bias, T scale, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        out[id] = (out[id] + (T)ldg(&bias[id % n])) * scale;\n    }\n}\n\ntemplate<>\n__global__ void add_bias_mul(half* out, const half* __restrict bias, half scale, int m, int n)\n{\n    int real_n = 2 * n;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * real_n; id += blockDim.x * gridDim.x) {\n        out[id] = __hmul(__hadd(out[id], bias[id % real_n]), scale);\n    }\n}\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void\nadd_bias_mul(__hip_bfloat16* out, const __hip_bfloat16* __restrict bias, __hip_bfloat16 scale, int m, int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddBiasMul(T* out, const T* bias, const T scale, const int m, const int n, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    add_bias_mul<T><<<grid, block, 0, stream>>>(out, bias, scale, m, n / data_type_factor);\n}\n\ntemplate void\ninvokeAddBiasMul(float* out, const float* bias, const float scale, const int m, const int n, hipStream_t stream);\ntemplate void\ninvokeAddBiasMul(half* out, const half* bias, const half scale, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasMul(__hip_bfloat16*       out,\n                               const __hip_bfloat16* bias,\n                               const __hip_bfloat16  scale,\n                               const int            m,\n                               const int            n,\n                               hipStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void cmvn(T* out, const T* __restrict in, const T* __restrict mean, const T* __restrict istd, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        out[id] = (in[id] - mean[id % n]) * istd[id % n];\n    }\n}\n\ntemplate<>\n__global__ void\ncmvn(half* out, const half* __restrict in, const half* __restrict mean, const half* __restrict istd, int m, int n)\n{\n    int real_n = 2 * n;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * real_n; id += blockDim.x * gridDim.x) {\n        out[id] = __hmul(__hsub(in[id], mean[id % real_n]), istd[id % real_n]);\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void cmvn(__hip_bfloat16* out,\n                     const __hip_bfloat16* __restrict in,\n                     const __hip_bfloat16* __restrict mean,\n                     const __hip_bfloat16* __restrict istd,\n                     int m,\n                     int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeCMVN(T* out, const T* in, const T* mean, const T* istd, const int m, const int n, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    cmvn<T><<<grid, block, 0, stream>>>(out, in, mean, istd, m, n / data_type_factor);\n}\n\ntemplate void invokeCMVN(\n    float* out, const float* in, const float* mean, const float* istd, const int m, const int n, hipStream_t stream);\ntemplate void invokeCMVN(\n    half* out, const half* in, const half* mean, const half* istd, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCMVN(__hip_bfloat16*       out,\n                         const __hip_bfloat16* in,\n                         const __hip_bfloat16* mean,\n                         const __hip_bfloat16* istd,\n                         const int            m,\n                         const int            n,\n                         hipStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void slice(T* out, const T* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n    // in: (1, 5000, hidden_unit)\n    // out: (batch_size, seq_len, hidden_unit)\n    int seq_id  = blockIdx.x % seq_len;\n    int in_id   = seq_id * blockDim.x + threadIdx.x;\n    int out_id  = blockIdx.x * blockDim.x + threadIdx.x;\n    out[out_id] = (T)ldg(&in[in_id]);\n}\n\ntemplate<>\n__global__ void slice(half* out, const half* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n    half2*       out_ptr = (half2*)out;\n    const half2* in_ptr  = (half2*)in;\n    int          seq_id  = blockIdx.x % seq_len;\n    int          in_id   = seq_id * blockDim.x + threadIdx.x;\n    int          out_id  = blockIdx.x * blockDim.x + threadIdx.x;\n    out_ptr[out_id]      = __ldg(&in_ptr[in_id]);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void\nslice(__hip_bfloat16* out, const __hip_bfloat16* __restrict in, int batch_size, int seq_len, int hidden_unit)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeSlice(\n    T* out, const T* in, const int batch_size, const int seq_len, const int hidden_unit, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    const int m                = batch_size * seq_len;\n    dim3      block, grid;\n    if (hidden_unit / data_type_factor <= 1024) {\n        block.x = hidden_unit / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * hidden_unit / 1024.);\n    }\n    slice<T><<<grid, block, 0, stream>>>(out, in, batch_size, seq_len, hidden_unit / data_type_factor);\n}\n\ntemplate void invokeSlice(\n    float* out, const float* in, const int batch_size, const int seq_len, const int hidden_unit, hipStream_t stream);\ntemplate void invokeSlice(\n    half* out, const half* in, const int batch_size, const int seq_len, const int hidden_unit, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeSlice(__hip_bfloat16*       out,\n                          const __hip_bfloat16* in,\n                          const int            batch_size,\n                          const int            seq_len,\n                          const int            hidden_unit,\n                          hipStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void\ntranspose0213(T* src, T* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int batch_id       = blockIdx.x / (head_num * seq_len);\n    int seq_id         = blockIdx.x % seq_len;\n    int head_id        = (blockIdx.x % (head_num * seq_len)) / seq_len;\n    dst[batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head + head_id * size_per_head\n        + threadIdx.x] = src[blockIdx.x * size_per_head + threadIdx.x];\n}\n\ntemplate<>\n__global__ void transpose0213(\n    half* src, half* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\ntemplate<>\n__global__ void transpose0213(\n    half2* src, half2* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void transpose0213(__hip_bfloat16* src,\n                              __hip_bfloat16* dst,\n                              const int      batch_size,\n                              const int      seq_len,\n                              const int      head_num,\n                              const int      size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n\ntemplate<>\n__global__ void transpose0213(__hip_bfloat162* src,\n                              __hip_bfloat162* dst,\n                              const int       batch_size,\n                              const int       seq_len,\n                              const int       head_num,\n                              const int       size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    dst[target_id] = src[tid];\n}\n#endif\n\ntemplate<typename T>\nvoid invokeTranspose0213(T*           dst,\n                         T*           src,\n                         const int    batch_size,\n                         const int    seq_len,\n                         const int    head_num,\n                         const int    size_per_head,\n                         hipStream_t stream)\n{\n    // src: [batch_size, head_num, seq_len, size_per_head]\n    // dst: [batch_size, seq_len, head_num, size_per_head]\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        int seq_per_block = 1;\n        grid.x            = batch_size * head_num * seq_len / seq_per_block;\n        if (seq_per_block * size_per_head % 2 == 0) {\n            block.x = seq_per_block * size_per_head / 2;\n            if (std::is_same<T, half>::value) {\n                transpose0213<half2><<<grid, block, 0, stream>>>(\n                    (half2*)src, (half2*)dst, batch_size, seq_len, head_num, size_per_head / 2);\n            }\n#ifdef ENABLE_BF16\n            else {\n                transpose0213<__hip_bfloat162><<<grid, block, 0, stream>>>(\n                    (__hip_bfloat162*)src, (__hip_bfloat162*)dst, batch_size, seq_len, head_num, size_per_head / 2);\n            }\n#endif\n        }\n        else {\n            block.x = seq_per_block * size_per_head;\n            transpose0213<T><<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n        }\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transpose0213<T><<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n    }\n}\n\ntemplate void invokeTranspose0213(float*       src,\n                                  float*       dst,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  hipStream_t stream);\n\ntemplate void invokeTranspose0213(half*        src,\n                                  half*        dst,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose0213(__hip_bfloat16* src,\n                                  __hip_bfloat16* dst,\n                                  const int      batch_size,\n                                  const int      seq_len,\n                                  const int      head_num,\n                                  const int      size_per_head,\n                                  hipStream_t   stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void embedDecoderInput(T* out,\n                                  const int* __restrict in,\n                                  const T* __restrict embed_weights,\n                                  const T* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n    // blockDim: (bs * beam_size * 63), threadDim: (256)\n    // in: (bs * beam_size, 64)\n    // embed_weights: (4233, 256)\n    // encoding_weights: (1, 5000, 256)\n    // out: (bs * beam_size, 63, n = 256)\n    int in_row       = blockIdx.x / seq_len;\n    int in_col       = blockIdx.x % seq_len;\n    int id           = blockIdx.x * blockDim.x + threadIdx.x;\n    int embed_row    = (int)ldg(&in[in_row * (seq_len + 1) + in_col]);\n    int embed_id     = embed_row * blockDim.x + threadIdx.x;\n    int encoding_row = blockIdx.x % seq_len;\n    int encoding_id  = encoding_row * blockDim.x + threadIdx.x;\n    T   scale        = (T)sqrtf((float)n);\n    out[id]          = (T)ldg(&embed_weights[embed_id]) * scale;\n    T encoding_data  = (T)ldg(&encoding_weights[encoding_id]);\n    out[id] += encoding_data;\n}\n\ntemplate<>\n__global__ void embedDecoderInput(half* out,\n                                  const int* __restrict in,\n                                  const half* __restrict embed_weights,\n                                  const half* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n    int  in_row       = blockIdx.x / seq_len;\n    int  in_col       = blockIdx.x % seq_len;\n    int  id           = blockIdx.x * blockDim.x + threadIdx.x;\n    int  embed_row    = (int)ldg(&in[in_row * (seq_len + 1) + in_col]);\n    int  embed_id     = embed_row * blockDim.x + threadIdx.x;\n    int  encoding_row = blockIdx.x % seq_len;\n    int  encoding_id  = encoding_row * blockDim.x + threadIdx.x;\n    half scale        = (half)sqrtf((float)n);\n    out[id]           = __hadd(__hmul(embed_weights[embed_id], scale), encoding_weights[encoding_id]);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void embedDecoderInput(__hip_bfloat16* out,\n                                  const int* __restrict in,\n                                  const __hip_bfloat16* __restrict embed_weights,\n                                  const __hip_bfloat16* __restrict encoding_weights,\n                                  int vocab_size,\n                                  int seq_len,\n                                  int max_len,\n                                  int m,\n                                  int n)\n{\n}\n#endif\n\ntemplate<typename T>\nvoid invokeEmbedDecoderInput(T*           out,\n                             const int*   in,\n                             const T*     embed_weights,\n                             const T*     encoding_weights,\n                             const int    vocab_size,\n                             const int    max_len,\n                             const int    batch_size,\n                             const int    seq_len,\n                             const int    hidden_units,\n                             hipStream_t stream)\n{\n    const int data_type_factor = 1;\n    const int m                = batch_size * seq_len;\n    const int n                = hidden_units;\n    dim3      block, grid;\n    if (n / data_type_factor <= 1024) {\n        block.x = n / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    embedDecoderInput<T><<<grid, block, 0, stream>>>(\n        out, in, embed_weights, encoding_weights, vocab_size, seq_len, max_len, m, n / data_type_factor);\n}\n\ntemplate void invokeEmbedDecoderInput(float*       out,\n                                      const int*   in,\n                                      const float* embed_weights,\n                                      const float* encoding_weights,\n                                      const int    vocab_size,\n                                      const int    max_len,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_units,\n                                      hipStream_t stream);\ntemplate void invokeEmbedDecoderInput(half*        out,\n                                      const int*   in,\n                                      const half*  embed_weights,\n                                      const half*  encoding_weights,\n                                      const int    vocab_size,\n                                      const int    max_len,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_units,\n                                      hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeEmbedDecoderInput(__hip_bfloat16*       out,\n                                      const int*           in,\n                                      const __hip_bfloat16* embed_weights,\n                                      const __hip_bfloat16* encoding_weights,\n                                      const int            vocab_size,\n                                      const int            max_len,\n                                      const int            batch_size,\n                                      const int            seq_len,\n                                      const int            hidden_units,\n                                      hipStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void maskDecoderOutput(float* score,\n                                  const T* __restrict decoder_output,\n                                  const int* __restrict decoder_sequence_length,\n                                  const int* __restrict decoder_input,\n                                  int max_seq_len,\n                                  int vocab_size)\n{\n    // gridDim: (bs * beam_size), blockDim: (max_seq_len=63)\n    // decoder_output: [batch_size, beam_size, max_seq_len, vocab_size]\n    // decoder_sequence_length: [bs * beam_size]\n    // decoder_input: [bs * beam_size, max_seq_len+1=64]\n    // score: [bs * beam_size]\n    const int len  = decoder_sequence_length[blockIdx.x];\n    T         mask = (T)(0.0f);\n    if (threadIdx.x < len)\n        mask = (T)(1.0f);\n\n    int in_id     = blockIdx.x * (max_seq_len + 1) + threadIdx.x + 1;\n    int in_value  = decoder_input[in_id];\n    int vocab_id  = in_value * (int)mask;\n    int out_id    = (blockIdx.x * blockDim.x + threadIdx.x) * vocab_size + vocab_id;\n    T   out_value = decoder_output[out_id] * mask;\n\n    float sum = blockReduceSum((float)(out_value));\n    if (threadIdx.x == 0)\n        score[blockIdx.x] = sum;\n}\n\ntemplate<typename T>\nvoid invokeMaskDecoderOutput(float*       score,\n                             const T*     decoder_output,\n                             const int*   decoder_sequence_length,\n                             const int*   decoder_input,\n                             const int    batch_size,\n                             const int    max_seq_len,\n                             const int    vocab_size,\n                             hipStream_t stream)\n{\n    maskDecoderOutput<T><<<batch_size, std::min(1024, max_seq_len), 0, stream>>>(\n        score, decoder_output, decoder_sequence_length, decoder_input, max_seq_len, vocab_size);\n}\n\ntemplate void invokeMaskDecoderOutput(float*       score,\n                                      const float* decoder_output,\n                                      const int*   decoder_sequence_length,\n                                      const int*   decoder_input,\n                                      const int    batch_size,\n                                      const int    max_seq_len,\n                                      const int    vocab_size,\n                                      hipStream_t stream);\ntemplate void invokeMaskDecoderOutput(float*       score,\n                                      const half*  decoder_output,\n                                      const int*   decoder_sequence_length,\n                                      const int*   decoder_input,\n                                      const int    batch_size,\n                                      const int    max_seq_len,\n                                      const int    vocab_size,\n                                      hipStream_t stream);\n// #ifdef ENABLE_BF16\n// template void\n// invokeMaskDecoderOutput(__nv_bfloat16* score, const __nv_bfloat16* decoder_output, const int*\n// decoder_sequence_length, const int* decoder_input, const int batch_size, const int max_seq_len, cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void buildBestIndex(\n    int* best_index, const float* __restrict decoder_score, const T* __restrict ctc_score, T ctc_weight, int beam_size)\n{\n    // gridDim: (bs), blockDim: (beam_size=10)\n    // decoder_score: [bs, 10]\n    // ctc_score: [bs, 10]\n    // best_index: [bs,]\n    int id  = blockIdx.x * blockDim.x + threadIdx.x;\n    T   val = (T)ldg(&decoder_score[id]) + (T)ldg(&ctc_score[id]) * ctc_weight;\n\n    extern __shared__ float score[];\n    score[threadIdx.x] = (float)val;\n    __syncthreads();\n\n    int   index = 0;\n    float max   = score[0];\n    for (int i = 1; i < blockDim.x; i++) {\n        if (score[i] > max) {\n            max   = score[i];\n            index = i;\n        }\n    }\n    if (threadIdx.x == 0) {\n        best_index[blockIdx.x] = index;\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildBestIndex(int*         best_index,\n                          const float* decoder_score,\n                          const T*     ctc_score,\n                          const T      ctc_weight,\n                          const int    batch_size,\n                          const int    beam_size,\n                          hipStream_t stream)\n{\n    buildBestIndex<T><<<batch_size, std::min(1024, beam_size), beam_size * sizeof(float), stream>>>(\n        best_index, decoder_score, ctc_score, ctc_weight, beam_size);\n}\n\ntemplate void invokeBuildBestIndex(int*         best_index,\n                                   const float* decoder_score,\n                                   const float* ctc_score,\n                                   const float  ctc_weight,\n                                   const int    batch_size,\n                                   const int    beam_size,\n                                   hipStream_t stream);\ntemplate void invokeBuildBestIndex(int*         best_index,\n                                   const float* decoder_score,\n                                   const half*  ctc_score,\n                                   const half   ctc_weight,\n                                   const int    batch_size,\n                                   const int    beam_size,\n                                   hipStream_t stream);\n// #ifdef ENABLE_BF16\n// template void\n// invokeBuildBestIndex(__nv_bfloat16* score, const __nv_bfloat16* decoder_output, const int* decoder_sequence_length,\n// const int* decoder_input, const int batch_size, const int max_seq_len, cudaStream_t stream); #endif\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void mask_bias_glu(\n    T* out, const T* __restrict in, const T* __restrict bias, int m, int n, const T* __restrict attr_mask, int seq_len)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id     = id / n;\n        int n_id     = id % n;\n        int s_id     = m_id % seq_len;\n        int b_id     = m_id / seq_len;\n        T   cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        // in = in + m_id * 2 * n;\n        const T* in_ptr = in + m_id * 2 * n;\n\n        float val1 = 0.f;\n        float val2 = 0.f;\n\n        if (cur_mask != T(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            val1 = val1 + ldg(&bias[n_id]);\n            val2 = val2 + ldg(&bias[n_id + n]);\n        }\n        out[id] = val1 * sigmoid<float>(val2);\n    }\n}\n\ntemplate<>\n__global__ void mask_bias_glu(half* out,\n                              const half* __restrict in,\n                              const half* __restrict bias,\n                              int m,\n                              int n,\n                              const half* __restrict attr_mask,\n                              int seq_len)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id = id / n;\n        int n_id = id % n;\n        int s_id = m_id % seq_len;\n        int b_id = m_id / seq_len;\n        // const half* attr_mask = attr_mask + ;\n        half cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n\n        const half2* in_ptr = (half2*)in + m_id * 2 * n;\n        // in = in + m_id * 2 * n;\n\n        half2 val1 = cuda_cast<half2>(0.f);\n        half2 val2 = cuda_cast<half2>(0.f);\n\n        if (cur_mask != half(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            half2 bias1 = __ldg(&bias_ptr[n_id]);\n            half2 bias2 = __ldg(&bias_ptr[n_id + n]);\n            val1        = hadd2(val1, bias1);\n            val2        = hadd2(val2, bias2);\n        }\n        half2 local_out;\n        local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n        local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n        out_ptr[id] = local_out;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void mask_bias_glu(__hip_bfloat16* out,\n                              const __hip_bfloat16* __restrict in,\n                              const __hip_bfloat16* __restrict bias,\n                              int m,\n                              int n,\n                              const __hip_bfloat16* __restrict attr_mask,\n                              int seq_len)\n{\n    __hip_bfloat162*       out_ptr  = (__hip_bfloat162*)out;\n    const __hip_bfloat162* bias_ptr = (__hip_bfloat162*)bias;\n    // const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int           m_id     = id / n;\n        int           n_id     = id % n;\n        int           s_id     = m_id % seq_len;\n        int           b_id     = m_id / seq_len;\n        __hip_bfloat16 cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n\n        // in = in + m_id * 2 * n;\n        const __hip_bfloat162* in_ptr = (__hip_bfloat162*)in + m_id * 2 * n;\n\n        __hip_bfloat162 val1 = cuda_cast<__hip_bfloat162>(0.f);\n        __hip_bfloat162 val2 = cuda_cast<__hip_bfloat162>(0.f);\n\n        if (cur_mask != __hip_bfloat16(0.f)) {\n            val1 = in_ptr[n_id];\n            val2 = in_ptr[n_id + n];\n        }\n\n        if (bias != nullptr) {\n            __hip_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n            __hip_bfloat162 bias2 = ldg(&bias_ptr[n_id + n]);\n            val1                 = bf16hadd2(val1, bias1);\n            val2                 = bf16hadd2(val2, bias2);\n        }\n        __hip_bfloat162 local_out;\n        local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n        local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n        out_ptr[id] = local_out;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeMaskBiasGlu(T*           out,\n                       const T*     in,\n                       const T*     bias,\n                       const int    m,\n                       const int    n,\n                       const T*     attr_mask,\n                       const int    seq_len,\n                       hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    mask_bias_glu<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor, attr_mask, seq_len);\n}\n\ntemplate void invokeMaskBiasGlu(float*       out,\n                                const float* in,\n                                const float* bias,\n                                const int    m,\n                                const int    n,\n                                const float* attr_mask,\n                                const int    seq_len,\n                                hipStream_t stream);\ntemplate void invokeMaskBiasGlu(half*        out,\n                                const half*  in,\n                                const half*  bias,\n                                const int    m,\n                                const int    n,\n                                const half*  attr_mask,\n                                const int    seq_len,\n                                hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMaskBiasGlu(__hip_bfloat16*       out,\n                                const __hip_bfloat16* in,\n                                const __hip_bfloat16* bias,\n                                const int            m,\n                                const int            n,\n                                const __hip_bfloat16* attr_mask,\n                                const int            seq_len,\n                                hipStream_t         stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void bias_glu(T* out, const T* __restrict in, const T* __restrict bias, int m, int n)\n// {\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n//         // in = in + m_id * 2 * n;\n//         const T* in_ptr = in + m_id * 2 * n;\n\n//         float val1 = in_ptr[n_id];\n//         float val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             val1 = val1 + ldg(&bias[n_id]);\n//             val2 = val2 + ldg(&bias[n_id + n]);\n//         }\n//         out[id] = val1 * sigmoid<float>(val2);\n//     }\n// }\n\n// template<>\n// __global__ void bias_glu(half* out, const half* __restrict in, const half* __restrict bias, int m, int n)\n// {\n//     half2* out_ptr = (half2*)out;\n//     const half2* bias_ptr = (half2*)bias;\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n//         // const half* attr_mask = attr_mask + ;\n\n//         const half2* in_ptr = (half2*)in + m_id * 2 * n;\n//         // in = in + m_id * 2 * n;\n\n//         half2 val1 = in_ptr[n_id];\n//         half2 val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             half2 bias1 = __ldg(&bias_ptr[n_id]);\n//             half2 bias2 = __ldg(&bias_ptr[n_id + n]);\n//             val1 = hadd2(val1, bias1);\n//             val2 = hadd2(val2, bias2);\n//         }\n//         half2 local_out;\n//         local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n//         local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n//         out_ptr[id] = local_out;\n//     }\n// }\n\n// #ifdef ENABLE_BF16\n// template<>\n// __global__ void\n// bias_glu(__nv_bfloat16* out, const __nv_bfloat16* __restrict in, const __nv_bfloat16* __restrict bias, int m, int n)\n// {\n//     __nv_bfloat162* out_ptr = (__nv_bfloat162*)out;\n//     const __nv_bfloat162* bias_ptr = (__nv_bfloat162*)bias;\n//     // const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in;\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n//         int m_id = id / n;\n//         int n_id = id % n;\n\n//         // in = in + m_id * 2 * n;\n//         const __nv_bfloat162* in_ptr = (__nv_bfloat162*)in + m_id * 2 * n;\n\n//         __nv_bfloat162 val1 = in_ptr[n_id];\n//         __nv_bfloat162 val2 = in_ptr[n_id + n];\n\n//         if (bias != nullptr) {\n//             __nv_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n//             __nv_bfloat162 bias2 = ldg(&bias_ptr[n_id + n]);\n//             val1 = bf16hadd2(val1, bias1);\n//             val2 = bf16hadd2(val2, bias2);\n//         }\n//         __nv_bfloat162 local_out;\n//         local_out.x = (float)val1.x * sigmoid<float>((float)val2.x);\n//         local_out.y = (float)val1.y * sigmoid<float>((float)val2.y);\n\n//         out_ptr[id] = local_out;\n//     }\n// }\n// #endif\n\n// template<typename T>\n// void invokeBiasGlu(T* out, const T* in, const T* bias, const int m, const int n, cudaStream_t stream)\n// {\n//     const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n//     dim3 block, grid;\n//     if (n / 4 / data_type_factor <= 1024) {\n//         block.x = n / 4 / data_type_factor;\n//         grid.x = m;\n//     }\n//     else {\n//         block.x = 1024;\n//         grid.x = ceil(m * n / 1024.);\n//     }\n//     bias_glu<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor);\n// }\n\n// template void\n// invokeBiasGlu(float* out, const float* in, const float* bias, const int m, const int n, cudaStream_t stream);\n// template void invokeBiasGlu(half* out, const half* in, const half* bias, const int m, const int n, cudaStream_t\n// stream); #ifdef ENABLE_BF16 template void invokeBiasGlu(__nv_bfloat16* out,\n//                             const __nv_bfloat16* in,\n//                             const __nv_bfloat16* bias,\n//                             const int m,\n//                             const int n,\n//                             cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void mask_bias(\n    T* out, const T* __restrict in, const T* __restrict bias, int m, int n, const T* __restrict attr_mask, int seq_len)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int m_id = id / n;\n        int n_id = id % n;\n        int s_id = m_id % seq_len;\n        int b_id = m_id / seq_len;\n\n        T cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        // const T* in_ptr = in + m_id * n;\n\n        float val1 = 0.f;\n\n        if (cur_mask != T(0.f)) {\n            val1 = in[id];\n\n            if (bias != nullptr) {\n                val1 = val1 + ldg(&bias[n_id]);\n            }\n        }\n        out[id] = val1;\n    }\n}\n\ntemplate<>\n__global__ void mask_bias(half* out,\n                          const half* __restrict in,\n                          const half* __restrict bias,\n                          int m,\n                          int n,\n                          const half* __restrict attr_mask,\n                          int seq_len)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int          m_id     = id / n;  // TODO optimize / and % with +-\n        int          n_id     = id % n;\n        int          s_id     = m_id % seq_len;\n        int          b_id     = m_id / seq_len;\n        half         cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        const half2* in_ptr   = (const half2*)in + m_id * n;  // TODO optimize\n\n        half2 val1 = cuda_cast<half2>(0.f);\n\n        if (cur_mask != half(0.f)) {\n            val1 = in_ptr[n_id];\n\n            if (bias != nullptr) {\n                half2 bias1 = __ldg(&bias_ptr[n_id]);\n                val1        = hadd2(val1, bias1);\n            }\n        }\n        out_ptr[id] = val1;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void mask_bias(__hip_bfloat16* out,\n                          const __hip_bfloat16* __restrict in,\n                          const __hip_bfloat16* __restrict bias,\n                          int m,\n                          int n,\n                          const __hip_bfloat16* __restrict attr_mask,\n                          int seq_len)\n{\n    __hip_bfloat162*       out_ptr  = (__hip_bfloat162*)out;\n    const __hip_bfloat162* bias_ptr = (__hip_bfloat162*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int                   m_id     = id / n;\n        int                   n_id     = id % n;\n        int                   s_id     = m_id % seq_len;\n        int                   b_id     = m_id / seq_len;\n        __hip_bfloat16         cur_mask = attr_mask[b_id * seq_len * seq_len + s_id];\n        const __hip_bfloat162* in_ptr   = (const __hip_bfloat162*)in + m_id * n;\n\n        __hip_bfloat162 val1 = cuda_cast<__hip_bfloat162>(0.f);\n\n        if (cur_mask != __hip_bfloat16(0.f)) {\n            val1 = in_ptr[n_id];\n\n            if (bias != nullptr) {\n                __hip_bfloat162 bias1 = ldg(&bias_ptr[n_id]);\n                val1                 = bf16hadd2(val1, bias1);\n            }\n        }\n        out_ptr[id] = val1;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeMaskBias(T*           out,\n                    const T*     in,\n                    const T*     bias,\n                    const int    m,\n                    const int    n,\n                    const T*     attr_mask,\n                    const int    seq_len,\n                    hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    mask_bias<T><<<grid, block, 0, stream>>>(out, in, bias, m, n / data_type_factor, attr_mask, seq_len);\n}\n\ntemplate void invokeMaskBias(float*       out,\n                             const float* in,\n                             const float* bias,\n                             const int    m,\n                             const int    n,\n                             const float* attr_mask,\n                             const int    seq_len,\n                             hipStream_t stream);\ntemplate void invokeMaskBias(half*        out,\n                             const half*  in,\n                             const half*  bias,\n                             const int    m,\n                             const int    n,\n                             const half*  attr_mask,\n                             const int    seq_len,\n                             hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMaskBias(__hip_bfloat16*       out,\n                             const __hip_bfloat16* in,\n                             const __hip_bfloat16* bias,\n                             const int            m,\n                             const int            n,\n                             const __hip_bfloat16* attr_mask,\n                             const int            seq_len,\n                             hipStream_t         stream);\n#endif\n\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, bool IS_RESIDUAL, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalScaleAddBiasResidualLayerNormOpt(T* normed_output,\n                                                        T* output,\n                                                        const T* __restrict bias,\n                                                        const T* __restrict residual,\n                                                        const T* __restrict gamma,\n                                                        const T* __restrict beta,\n                                                        int   m,\n                                                        int   n,\n                                                        float scale_input,\n                                                        float scale_residual)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T local_sum            = cuda_cast<T>(0.0f);\n    T local_scale_input    = cuda_cast<T>(scale_input);\n    T local_scale_residual = cuda_cast<T>(scale_residual);\n#pragma unroll\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = cuda_cast<T>(0.0f);\n\n        if (IS_OUTPUT) {\n            val = hadd2(val, output[index]);\n        }\n\n        if (IS_BIAS) {\n            val = hadd2(val, ldg(&bias[i]));\n        }\n        val = hmul2(val, local_scale_input);\n        if (IS_RESIDUAL) {\n            val = hadd2(val, hmul2(ldg(&residual[index]), local_scale_residual));\n        }\n\n        output[index] = val;\n\n        local_sum = hadd2(local_sum, val);\n    }\n\n    mean = blockReduceSum((float)(local_sum.x + local_sum.y));\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n / 2;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        T     val    = output[blockIdx.x * n + i];\n        float diff_1 = (float)(val.x) - s_mean;\n        float diff_2 = (float)(val.y) - s_mean;\n        local_var_sum += (diff_1 * diff_1 + diff_2 * diff_2);\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n / 2 + EPS);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hmul2(hsub2(output[index], mean_2), var_2), ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n        normed_output[index] = val;\n    }\n}\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, bool IS_RESIDUAL, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalScaleAddBiasResidualLayerNormOpt2(T* normed_output,\n                                                         T* output,\n                                                         const T* __restrict bias,\n                                                         const T* __restrict residual,\n                                                         const T* __restrict gamma,\n                                                         const T* __restrict beta,\n                                                         int   m,\n                                                         int   n,\n                                                         float scale_input,\n                                                         float scale_residual)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            x_sum    = 0.0f;\n    float            x2_sum   = 0.0f;\n    const int        b_offset = blockIdx.x * n;\n    using T1                  = typename TypeConverter<T>::Type;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        float     val_1 = 0.0f;\n        float     val_2 = 0.0f;\n        T         tmp;\n\n        if (IS_OUTPUT) {\n            tmp = ldg(&output[index]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n\n        if (IS_BIAS) {\n            tmp = ldg(&bias[i]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        val_1 *= scale_input;\n        val_2 *= scale_input;\n\n        if (IS_RESIDUAL) {\n            tmp = ldg(&residual[index]);\n            val_1 += static_cast<float>(tmp.x) * scale_residual;\n            val_2 += static_cast<float>(tmp.y) * scale_residual;\n        }\n\n        tmp.x         = cuda_cast<T1>(val_1);\n        tmp.y         = cuda_cast<T1>(val_2);\n        output[index] = tmp;\n        x_sum += val_1 + val_2;\n        x2_sum += val_1 * val_1 + val_2 * val_2;\n    }\n    float sums[2];\n    sums[0] = x_sum;\n    sums[1] = x2_sum;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / n / 2;\n        s_variance = rsqrtf(sums[1] / n / 2 - s_mean * s_mean + EPS);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        T         val   = hmul2(hmul2(hsub2(output[index], mean_2), var_2), ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n        normed_output[index] = val;\n    }\n}\n\n#define HALF_LAYERNORM_OPT(UNROLL_FACTOR)                                                                              \\\n    if (bias != nullptr)                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt<T2, true, true, true, true, UNROLL_FACTOR>                             \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);                                                              \\\n    else                                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt<T2, true, false, true, true, UNROLL_FACTOR>                            \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);\n\n#define HALF_LAYERNORM_OPT2(UNROLL_FACTOR)                                                                             \\\n    if (bias != nullptr)                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt2<T2, true, true, true, true, UNROLL_FACTOR>                            \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);                                                              \\\n    else                                                                                                               \\\n        generalScaleAddBiasResidualLayerNormOpt2<T2, true, false, true, true, UNROLL_FACTOR>                           \\\n            <<<grid, block, 0, stream>>>((T2*)norm_output,                                                             \\\n                                         (T2*)output,                                                                  \\\n                                         (const T2*)bias,                                                              \\\n                                         (const T2*)input,                                                             \\\n                                         (const T2*)gamma,                                                             \\\n                                         (const T2*)beta,                                                              \\\n                                         m,                                                                            \\\n                                         half_n,                                                                       \\\n                                         scale_input,                                                                  \\\n                                         scale_residual);\n\ntemplate<typename T>\n__global__ void generalScaleAddBiasResidualLayerNorm(const T* __restrict input,\n                                                     const T* __restrict gamma,\n                                                     const T* __restrict beta,\n                                                     const T* __restrict bias,\n                                                     T*    output,\n                                                     T*    norm_output,\n                                                     int   m,\n                                                     int   n,\n                                                     float scale_input,\n                                                     float scale_residual)\n\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float local_out = (float)(output[blockIdx.x * n + i]);\n        if (bias != nullptr) {\n            local_out += (float)(ldg(&bias[i]));\n        }\n        local_out *= scale_input;\n        local_out += (float)(ldg(&input[blockIdx.x * n + i])) * scale_residual;\n\n        output[blockIdx.x * n + i] = (T)local_out;\n        local_sum += local_out;\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(output[blockIdx.x * n + i]) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + EPS);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(ldg(&beta[i]));\n        if (norm_output != nullptr)\n            norm_output[blockIdx.x * n + i] =\n                (T)((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralScaleAddBiasResidualPreLayerNorm(T*           output,\n                                                   T*           norm_output,\n                                                   const T*     input,\n                                                   const T*     gamma,\n                                                   const T*     beta,\n                                                   const T*     bias,\n                                                   int          m,\n                                                   int          n,\n                                                   hipStream_t stream,\n                                                   int          opt_version,\n                                                   float        scale_input,\n                                                   float        scale_residual)\n\n{\n    if (opt_version > 0 && sizeof(T) == 2 && n % 2 == 0) {\n        dim3 grid(m);\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n        if (opt_version == 1) {\n            if (unroll_factor == 1) {\n                HALF_LAYERNORM_OPT(1);\n            }\n            else if (unroll_factor == 2) {\n                HALF_LAYERNORM_OPT(2);\n            }\n            else if (unroll_factor == 3) {\n                HALF_LAYERNORM_OPT(3);\n            }\n            else if (unroll_factor == 4) {\n                HALF_LAYERNORM_OPT(4);\n            }\n            else if (unroll_factor == 8) {\n                HALF_LAYERNORM_OPT(8);\n            }\n        }\n        else {\n            if (unroll_factor == 1) {\n                HALF_LAYERNORM_OPT2(1);\n            }\n            else if (unroll_factor == 2) {\n                HALF_LAYERNORM_OPT2(2);\n            }\n            else if (unroll_factor == 3) {\n                HALF_LAYERNORM_OPT2(3);\n            }\n            else if (unroll_factor == 4) {\n                HALF_LAYERNORM_OPT2(4);\n            }\n            else if (unroll_factor == 8) {\n                HALF_LAYERNORM_OPT2(8);\n            }\n        }\n    }\n    else {\n\n        dim3 grid(m);\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n\n        if (n % 32 != 0) {\n            block.x = 1024;\n        }\n\n        block.x = block.x / (4 / sizeof(T));  // if using half, only need half of block.x\n\n        /* should pay attention to the rsqrt precision*/\n        generalScaleAddBiasResidualLayerNorm<T><<<grid, block, 0, stream>>>(\n            input, gamma, beta, bias, output, norm_output, m, n, scale_input, scale_residual);  // For gpt-3\n    }\n}\n\n#undef HALF_LAYERNORM_OPT\n#undef HALF_LAYERNORM_OPT2\n\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(float*       output,\n                                                            float*       norm_output,\n                                                            const float* input,\n                                                            const float* gamma,\n                                                            const float* beta,\n                                                            const float* bias,\n                                                            int          m,\n                                                            int          n,\n                                                            hipStream_t stream,\n                                                            int          opt_version,\n                                                            float        scale_input,\n                                                            float        scale_residual);\n\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(half*        output,\n                                                            half*        norm_output,\n                                                            const half*  input,\n                                                            const half*  gamma,\n                                                            const half*  beta,\n                                                            const half*  bias,\n                                                            int          m,\n                                                            int          n,\n                                                            hipStream_t stream,\n                                                            int          opt_version,\n                                                            float        scale_input,\n                                                            float        scale_residual);\n\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralScaleAddBiasResidualPreLayerNorm(__hip_bfloat16*       output,\n                                                            __hip_bfloat16*       norm_output,\n                                                            const __hip_bfloat16* input,\n                                                            const __hip_bfloat16* gamma,\n                                                            const __hip_bfloat16* beta,\n                                                            const __hip_bfloat16* bias,\n                                                            int                  m,\n                                                            int                  n,\n                                                            hipStream_t         stream,\n                                                            int                  opt_version,\n                                                            float                scale_input,\n                                                            float                scale_residual);\n\n#endif\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void scaleAddBiasResidual(\n    T* output, const T* input, const T* bias, const int m, const int n, float scale_input, float scale_residual)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        float local_val = output[blockIdx.x * n + col_index];\n        float bias_val  = (bias == nullptr) ? (0.0f) : (float)bias[col_index];\n        local_val += bias_val;\n        local_val *= scale_input;\n        local_val += (float)input[blockIdx.x * n + col_index] * scale_residual;\n        output[blockIdx.x * n + col_index] = local_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeScaleAddBiasResidual(T*           output,\n                                const T*     input,\n                                const T*     bias,\n                                const int    m,\n                                const int    n,\n                                hipStream_t stream,\n                                float        scale_input,\n                                float        scale_residual)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    scaleAddBiasResidual<<<grid, block, 0, stream>>>(output, input, bias, m, n, scale_input, scale_residual);\n}\n\ntemplate void invokeScaleAddBiasResidual(float*       output,\n                                         const float* input,\n                                         const float* bias,\n                                         const int    m,\n                                         const int    n,\n                                         hipStream_t stream,\n                                         float        scale_input,\n                                         float        scale_residual);\n\ntemplate void invokeScaleAddBiasResidual(half*        output,\n                                         const half*  input,\n                                         const half*  bias,\n                                         const int    m,\n                                         const int    n,\n                                         hipStream_t stream,\n                                         float        scale_input,\n                                         float        scale_residual);\n\n#ifdef ENABLE_BF16\ntemplate void invokeScaleAddBiasResidual(__hip_bfloat16*       output,\n                                         const __hip_bfloat16* input,\n                                         const __hip_bfloat16* bias,\n                                         const int            m,\n                                         const int            n,\n                                         hipStream_t         stream,\n                                         float                scale_input,\n                                         float                scale_residual);\n#endif\n\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////\ntemplate<typename T>\n__global__ void ConformerDepthwiseConvBias(T*        out,\n                                           const T*  in,\n                                           const T*  weight,\n                                           const T*  bias,\n                                           const int batch_size,\n                                           const int seq_len,\n                                           const int hidden_unit,\n                                           const int kernel_size,\n                                           const int pad_size)\n{\n    int c_id = threadIdx.x;\n    int s_id = blockIdx.x % seq_len;\n    int b_id = blockIdx.x / seq_len;\n\n    int s_start = s_id - pad_size;\n    int s_end   = min(s_start + kernel_size, seq_len);\n    s_start     = max(s_start, 0);\n\n    int k_start = max(pad_size - s_id, 0);\n\n    in     = in + b_id * seq_len * hidden_unit + c_id;\n    weight = weight + c_id;\n\n    float val = 0.0f;\n    for (int i = s_start; i < s_end; ++i) {\n        val += (float)in[i * hidden_unit] * (float)weight[(k_start + i - s_start) * hidden_unit];\n    }\n    val = val + (float)bias[c_id];\n    // val = val * sigmoid<float>(val);\n\n    out[blockIdx.x * hidden_unit + c_id] = (T)val;\n}\n\ntemplate<typename T>\nvoid invokeConformerDepthwiseConvBias(T*           out,\n                                      const T*     in,\n                                      const T*     weight,\n                                      const T*     bias,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    hidden_unit,\n                                      const int    kernel_size,\n                                      const int    pad_size,\n                                      hipStream_t stream)\n{\n    FT_CHECK(hidden_unit <= 1024);\n    ConformerDepthwiseConvBias<T><<<batch_size * seq_len, hidden_unit, 0, stream>>>(\n        out, in, weight, bias, batch_size, seq_len, hidden_unit, kernel_size, pad_size);\n}\ntemplate void invokeConformerDepthwiseConvBias(float*       out,\n                                               const float* in,\n                                               const float* weight,\n                                               const float* bias,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    hidden_unit,\n                                               const int    kernel_size,\n                                               const int    pad_size,\n                                               hipStream_t stream);\n\ntemplate void invokeConformerDepthwiseConvBias(half*        out,\n                                               const half*  in,\n                                               const half*  weight,\n                                               const half*  bias,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    hidden_unit,\n                                               const int    kernel_size,\n                                               const int    pad_size,\n                                               hipStream_t stream);\n\n#ifdef ENABLE_BF16\n\ntemplate void invokeConformerDepthwiseConvBias(__hip_bfloat16*       out,\n                                               const __hip_bfloat16* in,\n                                               const __hip_bfloat16* weight,\n                                               const __hip_bfloat16* bias,\n                                               const int            batch_size,\n                                               const int            seq_len,\n                                               const int            hidden_unit,\n                                               const int            kernel_size,\n                                               const int            pad_size,\n                                               hipStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void ConformerDepthwiseConvBiasSilu(T*        out,\n                                               const T*  in,\n                                               const T*  weight,\n                                               const T*  bias,\n                                               const int batch_size,\n                                               const int seq_len,\n                                               const int hidden_unit,\n                                               const int kernel_size,\n                                               const int pad_size)\n{\n    int c_id = threadIdx.x;\n    int s_id = blockIdx.x % seq_len;\n    int b_id = blockIdx.x / seq_len;\n\n    int s_start = s_id - pad_size;\n    int s_end   = min(s_start + kernel_size, seq_len);\n    s_start     = max(s_start, 0);\n\n    int k_start = max(pad_size - s_id, 0);\n\n    in     = in + b_id * seq_len * hidden_unit;\n    weight = weight + c_id;\n\n    float val = 0.0f;\n    for (int i = s_start; i < s_end; ++i) {\n        val += (float)in[i * hidden_unit + c_id] * (float)weight[(k_start + i - s_start) * hidden_unit];\n    }\n    val = val + (float)bias[c_id];\n    val = val * sigmoid<float>(val);\n\n    out[blockIdx.x * hidden_unit + c_id] = val;\n}\n\ntemplate<typename T>\nvoid invokeConformerDepthwiseConvBiasSilu(T*           out,\n                                          const T*     in,\n                                          const T*     weight,\n                                          const T*     bias,\n                                          const int    batch_size,\n                                          const int    seq_len,\n                                          const int    hidden_unit,\n                                          const int    kernel_size,\n                                          const int    pad_size,\n                                          hipStream_t stream)\n{\n    FT_CHECK(hidden_unit <= 1024);\n    ConformerDepthwiseConvBiasSilu<T><<<batch_size * seq_len, hidden_unit, 0, stream>>>(\n        out, in, weight, bias, batch_size, seq_len, hidden_unit, kernel_size, pad_size);\n}\ntemplate void invokeConformerDepthwiseConvBiasSilu(float*       out,\n                                                   const float* in,\n                                                   const float* weight,\n                                                   const float* bias,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    hidden_unit,\n                                                   const int    kernel_size,\n                                                   const int    pad_size,\n                                                   hipStream_t stream);\n\ntemplate void invokeConformerDepthwiseConvBiasSilu(half*        out,\n                                                   const half*  in,\n                                                   const half*  weight,\n                                                   const half*  bias,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    hidden_unit,\n                                                   const int    kernel_size,\n                                                   const int    pad_size,\n                                                   hipStream_t stream);\n\n#ifdef ENABLE_BF16\n\ntemplate void invokeConformerDepthwiseConvBiasSilu(__hip_bfloat16*       out,\n                                                   const __hip_bfloat16* in,\n                                                   const __hip_bfloat16* weight,\n                                                   const __hip_bfloat16* bias,\n                                                   const int            batch_size,\n                                                   const int            seq_len,\n                                                   const int            hidden_unit,\n                                                   const int            kernel_size,\n                                                   const int            pad_size,\n                                                   hipStream_t         stream);\n#endif\n\n/////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void VarLenConformerDepthwiseConvBiasSilu(T* out,\n//                                                      const T* in,\n//                                                      const T* weight,\n//                                                      const T* bias,\n//                                                      const int* bid_start_end,\n//                                                      const T* bias_before_glu,\n//                                                      const int m,\n//                                                      const int batch_size,\n//                                                      const int seq_len,\n//                                                      const int hidden_unit,\n//                                                      const int kernel_size,\n//                                                      const int pad_size)\n// {\n//     __shared__ int b_start, b_end;\n//     if (threadIdx.x == 0) {\n//         // b_id = bid_start_end[blockIdx.x * 3];\n//         b_start = bid_start_end[blockIdx.x * 3 + 1];\n//         b_end = bid_start_end[blockIdx.x * 3 + 2];\n//     }\n//     __syncthreads();\n\n//     int cur_len = b_end - b_start;\n\n//     int c_id = threadIdx.x;\n//     int s_id = blockIdx.x - b_start;\n\n//     int s_start = s_id - pad_size;\n//     int s_end = min(s_start + kernel_size, seq_len);\n//     s_start = max(s_start, 0);\n\n//     int k_start = max(pad_size - s_id, 0);\n\n//     in = in + b_start * hidden_unit;\n//     weight = weight + c_id;\n\n//     float val = 0.0f;\n//     for (int i = s_start; i < s_end; ++i) {\n//         float cur_in = 0.f;\n//         if (i < cur_len)\n//             cur_in = (float)in[i * hidden_unit + c_id];\n//         else {\n//             // glu(0+bias)\n//             cur_in =\n//                 (float)ldg(&bias_before_glu[c_id]) * sigmoid<float>((float)ldg(&bias_before_glu[c_id +\n//                 hidden_unit]));\n//         }\n//         val += cur_in * (float)weight[(k_start + i - s_start) * hidden_unit];\n//     }\n//     val = val + (float)bias[c_id];\n//     val = val * sigmoid<float>(val);\n\n//     out[blockIdx.x * hidden_unit + c_id] = val;\n// }\n\n// template<typename T>\n// void invokeVarLenConformerDepthwiseConvBiasSilu(T* out,\n//                                                 const T* in,\n//                                                 const T* weight,\n//                                                 const T* bias,\n//                                                 const int* bid_start_end,\n//                                                 const T* bias_before_glu,\n//                                                 const int m,\n//                                                 const int batch_size,\n//                                                 const int seq_len,\n//                                                 const int hidden_unit,\n//                                                 const int kernel_size,\n//                                                 const int pad_size,\n//                                                 cudaStream_t stream)\n// {\n//     FT_CHECK(hidden_unit <= 1024);\n//     VarLenConformerDepthwiseConvBiasSilu<T><<<m, hidden_unit, 0, stream>>>(out,\n//                                                                            in,\n//                                                                            weight,\n//                                                                            bias,\n//                                                                            bid_start_end,\n//                                                                            bias_before_glu,\n//                                                                            m,\n//                                                                            batch_size,\n//                                                                            seq_len,\n//                                                                            hidden_unit,\n//                                                                            kernel_size,\n//                                                                            pad_size);\n// }\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(float* out,\n//                                                          const float* in,\n//                                                          const float* weight,\n//                                                          const float* bias,\n//                                                          const int* bid_start_end,\n//                                                          const float* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(half* out,\n//                                                          const half* in,\n//                                                          const half* weight,\n//                                                          const half* bias,\n//                                                          const int* bid_start_end,\n//                                                          const half* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n\n// #ifdef ENABLE_BF16\n\n// template void invokeVarLenConformerDepthwiseConvBiasSilu(__nv_bfloat16* out,\n//                                                          const __nv_bfloat16* in,\n//                                                          const __nv_bfloat16* weight,\n//                                                          const __nv_bfloat16* bias,\n//                                                          const int* bid_start_end,\n//                                                          const __nv_bfloat16* bias_before_glu,\n//                                                          const int m,\n//                                                          const int batch_size,\n//                                                          const int seq_len,\n//                                                          const int hidden_unit,\n//                                                          const int kernel_size,\n//                                                          const int pad_size,\n//                                                          cudaStream_t stream);\n// #endif\n\n////////////////////////////////////////////////////////////////////////////\n\n// __global__ void\n// getBatchIDStartEndKernel(int* bid_start_end, const int* sequence_length, const int batch_size, const int seq_len)\n// {\n//     int index = 0;\n//     int total_seq_len = 0;\n//     for (int i = 0; i < batch_size; i++) {\n//         const int seq_len = sequence_length[i];\n//         for (int j = 0; j < seq_len; j++) {\n//             bid_start_end[index * 3] = i;\n//             bid_start_end[index * 3 + 1] = total_seq_len;\n//             bid_start_end[index * 3 + 2] = total_seq_len + seq_len;\n//             index++;\n//         }\n//         total_seq_len += seq_len;\n//     }\n// }\n\n// void invokeGetBatchIDStartEnd(\n//     int* bid_start_end, const int* sequence_length, const int batch_size, const int seq_len, cudaStream_t stream)\n// {\n//     getBatchIDStartEndKernel<<<1, 1, 0, stream>>>(bid_start_end, sequence_length, batch_size, seq_len);\n// }\n\n////////////////////////////////////////////////////////////////////////////\n\n// template<typename T>\n// __global__ void bias_rebuild_padding(const T* src, T* dst, const T* bias, const int* padding_offset, const int n)\n// {\n//     const int tid = threadIdx.x;\n//     const int bid = blockIdx.x;\n//     const int dst_seq_id = bid + padding_offset[bid];\n//     const int src_seq_id = bid;\n\n//     for (int i = tid; i < n; i += blockDim.x) {\n//         if (bias != nullptr)\n//             dst[dst_seq_id * n + i] = src[src_seq_id * n + i] + bias[i];\n//         else\n//             dst[dst_seq_id * n + i] = src[src_seq_id * n + i];\n//     }\n// }\n\n// template<typename T>\n// void invokeBiasRebuildPadding(\n//     T* dst, const T* src, const T* bias, const int* padding_offset, const int m, const int n, cudaStream_t stream)\n// {\n//     // src: [token_num, hidden_dim]\n//     // dst: [batch_size*max_seq_len, hidden_dim]\n//     bias_rebuild_padding<<<m, 256, 0, stream>>>(src, dst, bias, padding_offset, n);\n// }\n\n// template void invokeBiasRebuildPadding(float* dst,\n//                                        const float* src,\n//                                        const float* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n\n// template void invokeBiasRebuildPadding(half* dst,\n//                                        const half* src,\n//                                        const half* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n\n// #ifdef ENABLE_BF16\n// template void invokeBiasRebuildPadding(__nv_bfloat16* dst,\n//                                        const __nv_bfloat16* src,\n//                                        const __nv_bfloat16* bias,\n//                                        const int* padding_offset,\n//                                        const int token_num,\n//                                        const int hidden_dim,\n//                                        cudaStream_t stream);\n// #endif  // ENABLE_BF16\n\n// Encoder\ntemplate<typename T>\n__global__ void addQKVPBiasTranspose(T* q_out,\n                                     T* k_out,\n                                     T* v_out,\n                                     const T* __restrict q_in,\n                                     const T* __restrict bias_q,\n                                     const T* __restrict k_in,\n                                     const T* __restrict bias_k,\n                                     const T* __restrict v_in,\n                                     const T* __restrict bias_v,\n                                     T*        p_buf,\n                                     const T*  P,\n                                     T*        q_buf_bias_v,\n                                     const T*  pos_bias_u,\n                                     const T*  pos_bias_v,\n                                     const int batch_size,\n                                     const int seq_len,\n                                     const int head_num,\n                                     const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n        /*\n                q_out[target_id] = __ldg(&q_in[src_id]);\n                q_out[target_id] = q_out[target_id] + __ldg(&bias_q[col_id]);\n\n                k_out[target_id] = __ldg(&k_in[src_id]);\n                k_out[target_id] = k_out[target_id] + __ldg(&bias_k[col_id]);\n\n                v_out[target_id] = __ldg(&v_in[src_id]);\n                v_out[target_id] = v_out[target_id] + __ldg(&bias_v[col_id]);\n        */\n        T q_val          = __ldg(&q_in[src_id]) + __ldg(&bias_q[col_id]);\n        q_out[target_id] = q_val + __ldg(&pos_bias_u[col_id]);\n\n        k_out[target_id] = __ldg(&k_in[src_id]) + __ldg(&bias_k[col_id]);\n\n        v_out[target_id] = __ldg(&v_in[src_id]) + __ldg(&bias_v[col_id]);\n\n        p_buf[target_id] = __ldg(&P[src_id]);\n\n        q_buf_bias_v[target_id] = q_val + __ldg(&pos_bias_v[col_id]);\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVPTranspose(T* q_out,\n                              T* k_out,\n                              T* v_out,\n                              const T* __restrict q_in,\n                              const T* __restrict k_in,\n                              const T* __restrict v_in,\n                              T*        p_buf,\n                              const T*  P,\n                              T*        q_buf_bias_v,\n                              const T*  pos_bias_u,\n                              const T*  pos_bias_v,\n                              const int batch_size,\n                              const int seq_len,\n                              const int head_num,\n                              const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        T q_val          = __ldg(&q_in[src_id]);\n        q_out[target_id] = q_val + __ldg(&pos_bias_u[col_id]);\n\n        k_out[target_id] = __ldg(&k_in[src_id]);\n        v_out[target_id] = __ldg(&v_in[src_id]);\n\n        p_buf[target_id]        = __ldg(&P[src_id]);\n        q_buf_bias_v[target_id] = q_val + __ldg(&pos_bias_v[col_id]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVPBiasTranspose(T*           q_buf,\n                                T*           k_buf,\n                                T*           v_buf,\n                                T*           Q,\n                                const T*     bias_Q,\n                                T*           K,\n                                const T*     bias_K,\n                                T*           V,\n                                const T*     bias_V,\n                                T*           p_buf,\n                                T*           P,\n                                T*           q_buf_bias_v,\n                                const T*     pos_bias_u,\n                                const T*     pos_bias_v,\n                                const int    batch_size,\n                                const int    seq_len,\n                                const int    head_num,\n                                const int    size_per_head,\n                                hipStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, seq_len);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVPBiasTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                                k_buf,\n                                                                v_buf,\n                                                                Q,\n                                                                bias_Q,\n                                                                K,\n                                                                bias_K,\n                                                                V,\n                                                                bias_V,\n                                                                p_buf,\n                                                                P,\n                                                                q_buf_bias_v,\n                                                                pos_bias_u,\n                                                                pos_bias_v,\n                                                                batch_size,\n                                                                seq_len,\n                                                                head_num,\n                                                                size_per_head);\n        }\n        else {\n            QKVPTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                         k_buf,\n                                                         v_buf,\n                                                         Q,\n                                                         K,\n                                                         V,\n                                                         p_buf,\n                                                         P,\n                                                         q_buf_bias_v,\n                                                         pos_bias_u,\n                                                         pos_bias_v,\n                                                         batch_size,\n                                                         seq_len,\n                                                         head_num,\n                                                         size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVPBiasTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                                    (half2*)k_buf,\n                                                                    (half2*)v_buf,\n                                                                    (const half2*)Q,\n                                                                    (const half2*)bias_Q,\n                                                                    (const half2*)K,\n                                                                    (const half2*)bias_K,\n                                                                    (const half2*)V,\n                                                                    (const half2*)bias_V,\n                                                                    (half2*)p_buf,\n                                                                    (const half2*)P,\n                                                                    (half2*)q_buf_bias_v,\n                                                                    (const half2*)pos_bias_u,\n                                                                    (const half2*)pos_bias_v,\n                                                                    batch_size,\n                                                                    seq_len,\n                                                                    head_num,\n                                                                    size_per_head / 2);\n        }\n        else {\n            QKVPTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                             (half2*)k_buf,\n                                                             (half2*)v_buf,\n                                                             (const half2*)Q,\n                                                             (const half2*)K,\n                                                             (const half2*)V,\n                                                             (half2*)p_buf,\n                                                             (const half2*)P,\n                                                             (half2*)q_buf_bias_v,\n                                                             (const half2*)pos_bias_u,\n                                                             (const half2*)pos_bias_v,\n                                                             batch_size,\n                                                             seq_len,\n                                                             head_num,\n                                                             size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\ntemplate void invokeAddQKVPBiasTranspose(float*       q_buf,\n                                         float*       k_buf,\n                                         float*       v_buf,\n                                         float*       Q,\n                                         const float* bias_Q,\n                                         float*       K,\n                                         const float* bias_K,\n                                         float*       V,\n                                         const float* bias_V,\n                                         float*       p_buf,\n                                         float*       P,\n                                         float*       q_buf_bias_v,\n                                         const float* pos_bias_u,\n                                         const float* pos_bias_v,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         hipStream_t stream);\n\ntemplate void invokeAddQKVPBiasTranspose(half*        q_buf,\n                                         half*        k_buf,\n                                         half*        v_buf,\n                                         half*        Q,\n                                         const half*  bias_Q,\n                                         half*        K,\n                                         const half*  bias_K,\n                                         half*        V,\n                                         const half*  bias_V,\n                                         half*        p_buf,\n                                         half*        P,\n                                         half*        q_buf_bias_v,\n                                         const half*  pos_bias_u,\n                                         const half*  pos_bias_v,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         hipStream_t stream);\n\n// TODO(bhsueh) Rename the softmax_kernel_v4 to softmax_kernel\ntemplate<int ITEMS_PER_THREAD, typename T, typename T_IN>\n__global__ void add_softmax_kernel_v4(T*          qk_buf_,\n                                      const T_IN* qk_buf_src,\n                                      const T_IN* qp_buf_src,\n                                      const T*    attr_mask,\n                                      const int   batch_size,\n                                      const int   head_num,\n                                      const int   seq_len,\n                                      const T     scalar)\n{\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        float            data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n\n            float qk = static_cast<float>(qk_buf_src[qk_offset]);\n            qk += static_cast<float>(qp_buf_src[qk_offset]);\n\n            float mask_val = static_cast<float>(ldg(&attr_mask[mask_offset]));\n\n            mask_val = (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk * static_cast<float>(scalar) + mask_val;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            qk_buf_[qk_offset] = (T)(data[i] * s_mean);\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD>\n__global__ void add_softmax_kernel_v4_half2(T*        qk_buf_,\n                                            const T*  qp_buf_,\n                                            const T*  attr_mask,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int seq_len,\n                                            const T   scalar)\n{\n    using T2                  = typename TypeConverter<T>::Type;\n    T2*       qk_buf_half2    = (T2*)qk_buf_;\n    T2*       qp_buf_half2    = (T2*)qp_buf_;\n    const T2* attr_mask_half2 = (const T2*)attr_mask;\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        T2               data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i\n                        + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n\n            T2 qk       = qk_buf_half2[qk_offset];\n            qk          = hadd2<T2>(qk, qp_buf_half2[qk_offset]);\n            T2 mask_val = ldg(&attr_mask_half2[mask_offset]);\n            mask_val    = hmul2<T2>(hsub2<T2>(cuda_cast<T2>(1.0f), mask_val), cuda_cast<T2>(-10000.0f));\n\n            data[i] = hadd2<T2>(hmul2<T2>(qk, cuda_cast<T2>(scalar)), mask_val);\n\n            local_max = fmax(local_max, fmax((float)data[i].x, (float)data[i].y));\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            data[i] = hexp2<T2>(hsub2<T2>(data[i], cuda_cast<T2>(s_max)));\n            local_sum += (float)(data[i].x + data[i].y);\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * (seq_len / 2) + blockDim.x * i\n                        + threadIdx.x;\n            qk_buf_half2[qk_offset] = hmul2<T2>(data[i], cuda_cast<T2>(s_mean));\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int NUM>\n__global__ void add_softmax_kernel_v5_half2(T*        qk_buf_,\n                                            const T*  qp_buf_,\n                                            const T*  attr_mask,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int seq_len,\n                                            const T   scalar)\n{\n    using T2         = typename TypeConverter<T>::Type;\n    T2* qk_buf_half2 = (T2*)qk_buf_;\n    T2* qp_buf_half2 = (T2*)qp_buf_;\n\n    const T2* attr_mask_half2 = (const T2*)attr_mask;\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x * NUM) {\n        T2 data[NUM][ITEMS_PER_THREAD];\n\n        int qk_offset[NUM];\n\n        __shared__ float s_sum[NUM], s_max[NUM];\n        float            local_max[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n            int mask_offset[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n                mask_offset[j] =\n                    (blockIdx.y * seq_len + seq_id + j * gridDim.x) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n            }\n\n            T2 mask_val[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = ldg(&attr_mask_half2[mask_offset[j]]);\n            }\n\n            T2 qk[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk[j] = qk_buf_half2[qk_offset[j]];\n                qk[j] = hadd2<T2>(qk[j], qp_buf_half2[qk_offset[j]]);\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = hmul2<T2>(hsub2<T2>(cuda_cast<T2>(1.0f), mask_val[j]), cuda_cast<T2>(-10000.0f));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i]   = hadd2<T2>(hmul2<T2>(qk[j], cuda_cast<T2>(scalar)), mask_val[j]);\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, NUM>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, NUM>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i] = hexp2<T2>(hsub2<T2>(data[j][i], cuda_cast<T2>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, NUM>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, NUM>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_buf_half2[qk_offset[j]] = hmul2<T2>(data[j][i], cuda_cast<T2>(s_sum[j]));\n            }\n        }\n    }\n}\n\n#define SOFTMAX_KERNEL(ITEMS_PER_THREAD)                                                                               \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            add_softmax_kernel_v5_half2<half, ITEMS_PER_THREAD, 4><<<grid, block, 0, stream>>>((half*)buffer,          \\\n                                                                                               (const half*)qp_buf,    \\\n                                                                                               (const half*)attr_mask, \\\n                                                                                               batch_size,             \\\n                                                                                               head_num,               \\\n                                                                                               seq_len,                \\\n                                                                                               (const half)scalar);    \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            add_softmax_kernel_v4_half2<half, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>((half*)buffer,             \\\n                                                                                            (const half*)qp_buf,       \\\n                                                                                            (const half*)attr_mask,    \\\n                                                                                            batch_size,                \\\n                                                                                            head_num,                  \\\n                                                                                            seq_len,                   \\\n                                                                                            (const half)scalar);       \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        add_softmax_kernel_v4<ITEMS_PER_THREAD, T, T_IN><<<grid, block, 0, stream>>>(                                  \\\n            buffer, buffer_src, qp_buf, attr_mask, batch_size, head_num, seq_len, scalar);                             \\\n    }\n\n#ifdef ENABLE_BF16\n#define SOFTMAX_KERNEL_BF16(ITEMS_PER_THREAD)                                                                          \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            add_softmax_kernel_v5_half2<__hip_bfloat16, ITEMS_PER_THREAD, 4>                                            \\\n                <<<grid, block, 0, stream>>>((__hip_bfloat16*)buffer,                                                   \\\n                                             (const __hip_bfloat16*)qp_buf,                                             \\\n                                             (const __hip_bfloat16*)attr_mask,                                          \\\n                                             batch_size,                                                               \\\n                                             head_num,                                                                 \\\n                                             seq_len,                                                                  \\\n                                             (const __hip_bfloat16)scalar);                                             \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            add_softmax_kernel_v4_half2<__hip_bfloat16, ITEMS_PER_THREAD>                                               \\\n                <<<grid, block, 0, stream>>>((__hip_bfloat16*)buffer,                                                   \\\n                                             (const __hip_bfloat16*)qp_buf,                                             \\\n                                             (const __hip_bfloat16*)attr_mask,                                          \\\n                                             batch_size,                                                               \\\n                                             head_num,                                                                 \\\n                                             seq_len,                                                                  \\\n                                             (const __hip_bfloat16)scalar);                                             \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        add_softmax_kernel_v4<ITEMS_PER_THREAD, __hip_bfloat16, T_IN><<<grid, block, 0, stream>>>(                      \\\n            buffer, buffer_src, qp_buf, attr_mask, batch_size, head_num, seq_len, scalar);                             \\\n    }\n#endif  // ENABLE_BF16\n\ntemplate<typename T, typename T_IN>\nvoid invokeAddMaskedSoftMax(T*           buffer,\n                            const T_IN*  buffer_src,\n                            const T_IN*  qp_buf,\n                            const T*     attr_mask,\n                            const int    batch_size,\n                            const int    seq_len,\n                            const int    head_num,\n                            const T      scalar,\n                            hipStream_t stream)\n{\n\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && seq_len % 2 == 0;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\nvoid invokeAddMaskedSoftMax(__hip_bfloat16*       buffer,\n                            const __hip_bfloat16* buffer_src,\n                            const __hip_bfloat16* qp_buf,\n                            const __hip_bfloat16* attr_mask,\n                            const int            batch_size,\n                            const int            seq_len,\n                            const int            head_num,\n                            const __hip_bfloat16  scalar,\n                            hipStream_t         stream)\n{\n\n    using T_IN = __hip_bfloat16;\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = seq_len % 2 == 0;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL_BF16(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL_BF16(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL_BF16(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL_BF16(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n\ntemplate<>\nvoid invokeAddMaskedSoftMax(__hip_bfloat16*       buffer,\n                            const float*         buffer_src,\n                            const float*         qp_buf,\n                            const __hip_bfloat16* attr_mask,\n                            const int            batch_size,\n                            const int            seq_len,\n                            const int            head_num,\n                            const __hip_bfloat16  scalar,\n                            hipStream_t         stream)\n{\n    using T_IN = float;\n    dim3 grid(seq_len, batch_size, head_num);\n    if (batch_size * head_num > 360) {\n        grid.x = ceil(float(seq_len) / 32.0f);\n    }\n\n    bool is_half2 = false;\n    dim3 block((seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 3072 && block.x <= 4096) {\n        SOFTMAX_KERNEL_BF16(4)\n    }\n    if (block.x > 2048) {\n        SOFTMAX_KERNEL_BF16(3)\n    }\n    else if (block.x > 1024) {\n        SOFTMAX_KERNEL_BF16(2)\n    }\n    else if (block.x > 0) {\n        SOFTMAX_KERNEL_BF16(1)\n    }\n    else {\n        FT_CHECK(seq_len <= 4096);\n    }\n}\n#endif  // ENABLE_BF16\n\ntemplate void invokeAddMaskedSoftMax(float*       buffer,\n                                     const float* buffer_src,\n                                     const float* qp_buf,\n                                     const float* attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const float  scalar,\n                                     hipStream_t stream);\n\ntemplate void invokeAddMaskedSoftMax(half*        buffer,\n                                     const float* buffer_src,\n                                     const float* qp_buf,\n                                     const half*  attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const half   scalar,\n                                     hipStream_t stream);\n\ntemplate void invokeAddMaskedSoftMax(half*        buffer,\n                                     const half*  buffer_src,\n                                     const half*  qp_buf,\n                                     const half*  attr_mask,\n                                     const int    batch_size,\n                                     const int    seq_len,\n                                     const int    head_num,\n                                     const half   scalar,\n                                     hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddMaskedSoftMax(__hip_bfloat16*       buffer,\n                                     const __hip_bfloat16* buffer_src,\n                                     const __hip_bfloat16* qp_buf,\n                                     const __hip_bfloat16* attr_mask,\n                                     const int            batch_size,\n                                     const int            seq_len,\n                                     const int            head_num,\n                                     const __hip_bfloat16  scalar,\n                                     hipStream_t         stream);\n\ntemplate void invokeAddMaskedSoftMax(__hip_bfloat16*       buffer,\n                                     const float*         buffer_src,\n                                     const float*         qp_buf,\n                                     const __hip_bfloat16* attr_mask,\n                                     const int            batch_size,\n                                     const int            seq_len,\n                                     const int            head_num,\n                                     const __hip_bfloat16  scalar,\n                                     hipStream_t         stream);\n#endif  // ENABLE_BF16\n\n// Decoder invokeAddQKVBiasTranspose\ntemplate<typename T>\n__global__ void addQKVBiasTranspose(T* q_out,\n                                    T* k_out,\n                                    T* v_out,\n                                    const T* __restrict q_in,\n                                    const T* __restrict bias_q,\n                                    const T* __restrict k_in,\n                                    const T* __restrict bias_k,\n                                    const T* __restrict v_in,\n                                    const T* __restrict bias_v,\n                                    const int batch_size,\n                                    const int seq_len1,\n                                    const int seq_len2,\n                                    const int head_num,\n                                    const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id1  = batch_id * seq_len1 + word_id;\n    const int row_id2  = batch_id * seq_len2 + word_id;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id    = col_id / size_per_head;\n        const int size_id    = col_id % size_per_head;\n        const int target_id1 = batch_id * (head_num * seq_len1 * size_per_head) + head_id * seq_len1 * size_per_head\n                               + word_id * size_per_head + size_id;\n        const int target_id2 = batch_id * (head_num * seq_len2 * size_per_head) + head_id * seq_len2 * size_per_head\n                               + word_id * size_per_head + size_id;\n\n        const int src_id1 = row_id1 * n + col_id;\n        const int src_id2 = row_id2 * n + col_id;\n\n        if (word_id < seq_len1)\n            q_out[target_id1] = __ldg(&q_in[src_id1]) + __ldg(&bias_q[col_id]);\n\n        if (word_id < seq_len2) {\n            k_out[target_id2] = __ldg(&k_in[src_id2]) + __ldg(&bias_k[col_id]);\n\n            v_out[target_id2] = __ldg(&v_in[src_id2]) + __ldg(&bias_v[col_id]);\n        }\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVTranspose(T* q_out,\n                             T* k_out,\n                             T* v_out,\n                             const T* __restrict q_in,\n                             const T* __restrict k_in,\n                             const T* __restrict v_in,\n                             const int batch_size,\n                             const int seq_len1,\n                             const int seq_len2,\n                             const int head_num,\n                             const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id1  = batch_id * seq_len1 + word_id;\n    const int row_id2  = batch_id * seq_len2 + word_id;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id    = col_id / size_per_head;\n        const int size_id    = col_id % size_per_head;\n        const int target_id1 = batch_id * (head_num * seq_len1 * size_per_head) + head_id * seq_len1 * size_per_head\n                               + word_id * size_per_head + size_id;\n        const int target_id2 = batch_id * (head_num * seq_len2 * size_per_head) + head_id * seq_len2 * size_per_head\n                               + word_id * size_per_head + size_id;\n\n        const int src_id1 = row_id1 * n + col_id;\n        const int src_id2 = row_id2 * n + col_id;\n\n        if (word_id < seq_len1)\n            q_out[target_id1] = __ldg(&q_in[src_id1]);\n\n        if (word_id < seq_len2) {\n            k_out[target_id2] = __ldg(&k_in[src_id2]);\n            v_out[target_id2] = __ldg(&v_in[src_id2]);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasTranspose(T*           q_buf,\n                               T*           k_buf,\n                               T*           v_buf,\n                               T*           Q,\n                               const T*     bias_Q,\n                               T*           K,\n                               const T*     bias_K,\n                               T*           V,\n                               const T*     bias_V,\n                               const int    batch_size,\n                               const int    seq_len1,\n                               const int    seq_len2,\n                               const int    head_num,\n                               const int    size_per_head,\n                               hipStream_t stream)\n{\n    const int k         = head_num * size_per_head;\n    int       seq_len12 = max(seq_len1, seq_len2);\n    dim3      grid(batch_size, seq_len12);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVBiasTranspose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                               k_buf,\n                                                               v_buf,\n                                                               Q,\n                                                               bias_Q,\n                                                               K,\n                                                               bias_K,\n                                                               V,\n                                                               bias_V,\n                                                               batch_size,\n                                                               seq_len1,\n                                                               seq_len2,\n                                                               head_num,\n                                                               size_per_head);\n        }\n        else {\n            QKVTranspose<T><<<grid, block, 0, stream>>>(\n                q_buf, k_buf, v_buf, Q, K, V, batch_size, seq_len1, seq_len2, head_num, size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVBiasTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                                   (half2*)k_buf,\n                                                                   (half2*)v_buf,\n                                                                   (const half2*)Q,\n                                                                   (const half2*)bias_Q,\n                                                                   (const half2*)K,\n                                                                   (const half2*)bias_K,\n                                                                   (const half2*)V,\n                                                                   (const half2*)bias_V,\n                                                                   batch_size,\n                                                                   seq_len1,\n                                                                   seq_len2,\n                                                                   head_num,\n                                                                   size_per_head / 2);\n        }\n        else {\n            QKVTranspose<half2><<<grid, block, 0, stream>>>((half2*)q_buf,\n                                                            (half2*)k_buf,\n                                                            (half2*)v_buf,\n                                                            (const half2*)Q,\n                                                            (const half2*)K,\n                                                            (const half2*)V,\n                                                            batch_size,\n                                                            seq_len1,\n                                                            seq_len2,\n                                                            head_num,\n                                                            size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\ntemplate void invokeAddQKVBiasTranspose(float*       q_buf,\n                                        float*       k_buf,\n                                        float*       v_buf,\n                                        float*       Q,\n                                        const float* bias_Q,\n                                        float*       K,\n                                        const float* bias_K,\n                                        float*       V,\n                                        const float* bias_V,\n                                        const int    batch_size,\n                                        const int    seq_len1,\n                                        const int    seq_len2,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        hipStream_t stream);\n\ntemplate void invokeAddQKVBiasTranspose(half*        q_buf,\n                                        half*        k_buf,\n                                        half*        v_buf,\n                                        half*        Q,\n                                        const half*  bias_Q,\n                                        half*        K,\n                                        const half*  bias_K,\n                                        half*        V,\n                                        const half*  bias_V,\n                                        const int    batch_size,\n                                        const int    seq_len1,\n                                        const int    seq_len2,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        hipStream_t stream);\n\n// Tile\ntemplate<typename T>\n__global__ void repeat_beam_size(T* out, const T* __restrict in, int beam_size, int n)\n{\n    int bid = blockIdx.x / beam_size;\n    out     = out + blockIdx.x * n;\n    in      = in + bid * n;\n    for (int id = threadIdx.x; id < n; id += blockDim.x) {\n        out[id] = in[id];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRepeatBeamSize(T* out, const T* in, const int m, const int n, const int beam_size, hipStream_t stream)\n{\n    dim3 block, grid;\n    block.x = std::min(1024, n);\n    grid.x  = m * beam_size;\n    repeat_beam_size<T><<<grid, block, 0, stream>>>(out, in, beam_size, n);\n}\n\ntemplate void\ninvokeRepeatBeamSize(float* out, const float* in, const int m, const int n, const int beam_size, hipStream_t stream);\ntemplate void\ninvokeRepeatBeamSize(half* out, const half* in, const int m, const int n, const int beam_size, hipStream_t stream);\ntemplate void\ninvokeRepeatBeamSize(int* out, const int* in, const int m, const int n, const int beam_size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeRepeatBeamSize(\n    __hip_bfloat16* out, const __hip_bfloat16* in, const int m, const int n, const int beam_size, hipStream_t stream);\n#endif\n\n////////////////////////////////////////////////////////////////////////////\n__global__ void getWenetOutLensKernel(int* out, const int* in, const int batch_size, const int max_seq_len)\n{\n    if (threadIdx.x < batch_size) {\n        int cur      = in[threadIdx.x];\n        int next_max = max_seq_len;\n\n        next_max = (next_max - 1) / 2;\n        cur      = (cur + 1) / 2;\n        cur      = min(cur, next_max);\n\n        next_max = (next_max - 1) / 2;\n        cur      = (cur + 1) / 2;\n        cur      = min(cur, next_max);\n\n        out[threadIdx.x] = cur;\n    }\n}\n\nvoid invokeGetWenetOutLens(int* out, const int* in, const int batch_size, const int max_seq_len, hipStream_t stream)\n{\n    FT_CHECK(batch_size <= 1024);\n    getWenetOutLensKernel<<<1, batch_size, 0, stream>>>(out, in, batch_size, max_seq_len);\n    sync_check_cuda_error();\n}\n////////////////////////////////////////////////////////////////////////////\n// __global__ void getPaddingOffsetKernelV2(size_t* valid_word_num,\n//                                          int* tmp_mask_offset,\n//                                          const int* sequence_length,\n//                                          const int batch_size,\n//                                          const int max_seq_len)\n// {\n//     // do cumulated sum\n//     int total_seq_len = 0;\n//     int cum_offset = 0;\n//     int index = 0;\n//     for (int i = 0; i < batch_size; i++) {\n//         const int seq_len = sequence_length[i];\n//         for (int j = 0; j < seq_len; j++) {\n//             tmp_mask_offset[index] = cum_offset;\n//             index++;\n//         }\n//         cum_offset += max_seq_len - seq_len;\n//         total_seq_len += seq_len;\n//     }\n//     valid_word_num[0] = (size_t)total_seq_len;\n// }\n\n// void invokeGetPaddingOffset(size_t* d_token_num,\n//                             int* tmp_mask_offset,\n//                             const int* sequence_lengths,\n//                             const int batch_size,\n//                             const int max_seq_len,\n//                             cudaStream_t stream)\n// {\n//     getPaddingOffsetKernelV2<<<1, 1, 0, stream>>>(\n//         d_token_num, tmp_mask_offset, sequence_lengths, batch_size, max_seq_len);\n//     sync_check_cuda_error();\n// }\n\n////////////////////////////////////////////////////////////////////////////\ntemplate<typename T, bool IS_CROSS>\n__global__ void buildDecoderAttentionMaskKernel(T*         attention_mask,\n                                                const int* sequence_lengths1,\n                                                const int  max_seq_len1,\n                                                const int* sequence_lengths2,\n                                                const int  max_seq_len2)\n{\n    // sequence_lengths1: [batch_size]\n    // sequence_lengths2: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len1, max_seq_len2]\n    const int s1_id = blockIdx.x % max_seq_len1;\n    const int b_id  = blockIdx.x / max_seq_len1;\n    attention_mask += (b_id * max_seq_len1 + s1_id) * max_seq_len2;\n\n    const int len1 = sequence_lengths1[b_id];\n    int       len2 = 0;\n    if (IS_CROSS)\n        len2 = sequence_lengths2[b_id];\n\n    for (int i = threadIdx.x; i < max_seq_len2; i += blockDim.x) {\n        T val = (T)(0.0f);\n        if (IS_CROSS) {\n            if (i < len2)\n                val = (T)(1.0f);\n        }\n        else {\n            if (i < len1 && i <= s1_id)\n                val = (T)(1.0f);\n        }\n        attention_mask[i] = val;\n    }\n}\n\ntemplate<typename T, bool IS_CROSS>\nvoid invokeBuildDecoderAttentionMask(T*           attention_mask,\n                                     const int*   sequence_lengths1,\n                                     const int*   sequence_lengths2,\n                                     const int    batch_size,\n                                     const int    max_seq_len1,\n                                     const int    max_seq_len2,\n                                     hipStream_t stream)\n{\n    buildDecoderAttentionMaskKernel<T, IS_CROSS>\n        <<<batch_size * max_seq_len1, std::min(1024, max_seq_len2), 0, stream>>>(\n            attention_mask, sequence_lengths1, max_seq_len1, sequence_lengths2, max_seq_len2);\n}\n\ntemplate void invokeBuildDecoderAttentionMask<float, false>(float*       attention_mask,\n                                                            const int*   sequence_lengths1,\n                                                            const int*   sequence_lengths2,\n                                                            const int    batch_size,\n                                                            const int    max_seq_len1,\n                                                            const int    max_seq_len2,\n                                                            hipStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<float, true>(float*       attention_mask,\n                                                           const int*   sequence_lengths1,\n                                                           const int*   sequence_lengths2,\n                                                           const int    batch_size,\n                                                           const int    max_seq_len1,\n                                                           const int    max_seq_len2,\n                                                           hipStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<half, false>(half*        attention_mask,\n                                                           const int*   sequence_lengths1,\n                                                           const int*   sequence_lengths2,\n                                                           const int    batch_size,\n                                                           const int    max_seq_len1,\n                                                           const int    max_seq_len2,\n                                                           hipStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask<half, true>(half*        attention_mask,\n                                                          const int*   sequence_lengths1,\n                                                          const int*   sequence_lengths2,\n                                                          const int    batch_size,\n                                                          const int    max_seq_len1,\n                                                          const int    max_seq_len2,\n                                                          hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildDecoderAttentionMask<__hip_bfloat16, false>(__hip_bfloat16* attention_mask,\n                                                                    const int*     sequence_lengths1,\n                                                                    const int*     sequence_lengths2,\n                                                                    const int      batch_size,\n                                                                    const int      max_seq_len1,\n                                                                    const int      max_seq_len2,\n                                                                    hipStream_t   stream);\ntemplate void invokeBuildDecoderAttentionMask<__hip_bfloat16, true>(__hip_bfloat16* attention_mask,\n                                                                   const int*     sequence_lengths1,\n                                                                   const int*     sequence_lengths2,\n                                                                   const int      batch_size,\n                                                                   const int      max_seq_len1,\n                                                                   const int      max_seq_len2,\n                                                                   hipStream_t   stream);\n\n#endif\n//////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T, int EPT>\n__global__ void biasLogSoftmaxKernel(float*       log_probs,\n                                     const T*     logits,\n                                     const T*     bias,\n                                     const int*   lengths,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const size_t vocab_size,\n                                     const size_t vocab_size_padded)\n{\n    constexpr bool IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int tidx = threadIdx.x;  // vocab dim\n    int bidx = blockIdx.x;   // batch dim\n    int step = blockIdx.y;   // step dim\n\n    __shared__ float s_max_logit, s_sum_logit;\n\n    bool is_valid = (bidx < batch_size) && (step < max_input_length);\n    if (lengths != nullptr)\n        is_valid = is_valid && (step < lengths[bidx]);\n    if (is_valid) {\n        // reposition logits to data for the current batch.\n        logits += bidx * max_input_length * vocab_size_padded + step * vocab_size_padded;\n        log_probs += bidx * max_input_length * vocab_size + step * vocab_size;\n\n        // load and add bias\n        T local_logit[EPT];\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                local_logit[i] = logits[cur_idx];\n                if (bias != nullptr)\n                    local_logit[i] = (float)local_logit[i] + (float)bias[cur_idx];\n            }\n            // else\n            //     local_logit[i] = -MAX_T_VAL;\n        }\n        // Find max(logits).\n        float local_max = -MAX_T_VAL;\n        float val       = -MAX_T_VAL;\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                val       = static_cast<float>(local_logit[i]);\n                local_max = fmax(local_max, val);\n            }\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (tidx == 0) {\n            s_max_logit = max_val;\n        }\n        __syncthreads();\n\n        // Calculate the denominator: sum_i exp(logits[i])\n        float local_sum_exp = 0.0f;\n// val = 0.0f;\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size) {\n                val = __expf(static_cast<float>(local_logit[i]) - s_max_logit);\n                local_sum_exp += val;\n            }\n        }\n\n        float sum_exp = blockDim.x <= 32 ? warpReduceSum(local_sum_exp) : blockReduceSum<float>(local_sum_exp);\n        if (tidx == 0) {\n            s_sum_logit = sum_exp;\n        }\n        __syncthreads();\n\n#pragma unroll\n        for (int i = 0; i < EPT; ++i) {\n            size_t cur_idx = tidx + i * blockDim.x;\n            if (cur_idx < vocab_size)\n                log_probs[cur_idx] = static_cast<float>(local_logit[i]) - s_max_logit - __logf(s_sum_logit + 1e-9f);\n        }\n    }\n}\n\ntemplate<typename T>\n__global__ void biasLogSoftmaxKernel(float*       log_probs,\n                                     const T*     logits,\n                                     const T*     bias,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const size_t vocab_size,\n                                     const size_t vocab_size_padded)\n{\n    constexpr bool IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int bid    = blockIdx.x;\n    int offset = bid * vocab_size_padded;\n\n    float local_max = -MAX_T_VAL;\n    float val       = -MAX_T_VAL;\n\n    __shared__ float s_max_logit;\n    __shared__ float s_sum_logit;\n\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        if (tid < vocab_size) {\n            float bias_val          = (bias != nullptr) ? bias[tid] : (T)0.0f;\n            log_probs[offset + tid] = (float)logits[offset + tid] + bias_val;\n        }\n        // else {\n        //     log_probs[offset + tid] = -MAX_T_VAL;\n        // }\n        local_max = fmax(local_max, (float)log_probs[offset + tid]);\n    }\n\n    float max_val = blockReduceMax<float>(local_max);\n    if (threadIdx.x == 0) {\n        s_max_logit = max_val;\n    }\n    __syncthreads();\n\n    float local_sum_exp = 0.0f;\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        val = __expf((float)log_probs[offset + tid] - s_max_logit);\n        local_sum_exp += val;\n    }\n\n    float sum_exp = blockReduceSum<float>(local_sum_exp);\n    if (threadIdx.x == 0) {\n        s_sum_logit = sum_exp;\n    }\n    __syncthreads();\n\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        log_probs[offset + tid] = (float)log_probs[offset + tid] - s_max_logit - __logf(s_sum_logit + 1e-9f);\n    }\n}\n\ntemplate<typename T>\nvoid invokeBiasLogSoftmax(float*       log_probs,\n                          const T*     logits,\n                          const T*     bias,\n                          const int*   lengths,\n                          const size_t max_input_length,\n                          const size_t batch_size,\n                          const size_t vocab_size,\n                          const size_t vocab_size_padded,\n                          bool         batch_first,\n                          hipStream_t stream)\n{\n    // FT_CHECK(vocab_size <= 768 * 6);\n    dim3 block, grid;\n\n    // Better perf at the cost of register pressure\n    if (vocab_size > 768 * 5 && vocab_size <= 768 * 6) {\n        block.x = 768;\n        grid.x  = batch_size;\n        grid.y  = max_input_length;\n        biasLogSoftmaxKernel<T, 6><<<grid, block, 0, stream>>>(\n            log_probs, logits, bias, lengths, max_input_length, batch_size, vocab_size, vocab_size_padded);\n    }\n    else {\n        block.x = std::min((int)vocab_size, 1024);\n        grid.x  = batch_size * max_input_length;\n        /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n        biasLogSoftmaxKernel<T><<<grid, block, 0, stream>>>(\n            log_probs, logits, bias, max_input_length, batch_size, vocab_size, vocab_size_padded);\n    }\n}\n\ntemplate void invokeBiasLogSoftmax<float>(float*       log_probs,\n                                          const float* logits,\n                                          const float* bias,\n                                          const int*   lengths,\n                                          const size_t max_input_length,\n                                          const size_t batch_size,\n                                          const size_t vocab_size,\n                                          const size_t vocab_size_padded,\n                                          bool         batch_first,\n                                          hipStream_t stream);\ntemplate void invokeBiasLogSoftmax<half>(float*       log_probs,\n                                         const half*  logits,\n                                         const half*  bias,\n                                         const int*   lengths,\n                                         const size_t max_input_length,\n                                         const size_t batch_size,\n                                         const size_t vocab_size,\n                                         const size_t vocab_size_padded,\n                                         bool         batch_first,\n                                         hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBiasLogSoftmax<__hip_bfloat16>(float*               log_probs,\n                                                  const __hip_bfloat16* logits,\n                                                  const __hip_bfloat16* bias,\n                                                  const int*           lengths,\n                                                  const size_t         max_input_length,\n                                                  const size_t         batch_size,\n                                                  const size_t         vocab_size,\n                                                  const size_t         vocab_size_padded,\n                                                  bool                 batch_first,\n                                                  hipStream_t         stream);\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/models/wenet/WenetKernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/layers/beam_search_layers/OnlineBeamSearchLayer.h\"\n\nnamespace fastertransformer {\n\nstatic const int SMALL_TOP_K_SOFTMAX_MAX_VOC_PARTS = 128;\nstatic const int MAX_K                             = 4;\n\ntemplate<typename T>\n__global__ void update_kernel(bool*          finished,\n                              int*           parent_ids,\n                              int*           sequence_length,\n                              int*           word_ids,\n                              int*           output_ids,\n                              BeamHypotheses beam_hyps,\n                              const int      vocab_size,\n                              const int*     end_ids,\n                              const int      local_batch_size,\n                              const int      beam_width)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < local_batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n\n        int batch_id           = index / beam_width;\n        sequence_length[index] = finished[index] ? sequence_length[index] : sequence_length[index] + 1;\n\n        int beam_id = (word_ids[index] / vocab_size) % beam_width;\n        int word_id = word_ids[index] % vocab_size;\n\n        sequence_length[index] = sequence_length[batch_id * beam_width + beam_id];\n        finished[index]        = word_id == end_ids[index / beam_width] ? 1 : 0;\n        parent_ids[index]      = beam_id;\n        word_ids[index]        = word_id;\n        output_ids[index]      = word_id;\n\n        if (beam_hyps.num_beams != nullptr) {\n            if (beam_hyps.num_beams[beam_hyps.ite * beam_hyps.local_batch_size + batch_id] == beam_width) {\n                for (int i = 0; i < beam_width; i++) {\n                    finished[batch_id * beam_width + i] = true;\n                }\n            }\n        }\n    }\n}\n\nvoid invokeUpdate(bool*           finished,\n                  int*            parent_ids,\n                  int*            sequence_length,\n                  int*            word_ids,\n                  int*            output_ids,\n                  BeamHypotheses* beam_hyps,\n                  const int       local_batch_size,\n                  const int       beam_width,\n                  const int       vocab_size_padded,\n                  const int*      end_ids,\n                  cudaStream_t    stream)\n{\n    dim3 grid((int)ceil(local_batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    update_kernel<float><<<grid, block, 0, stream>>>(finished,\n                                                     parent_ids,\n                                                     sequence_length,\n                                                     word_ids,\n                                                     output_ids,\n                                                     *beam_hyps,\n                                                     vocab_size_padded,\n                                                     end_ids,\n                                                     local_batch_size,\n                                                     beam_width);\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::invokeSoftMax(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size, beam_width]\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 6);\n\n    const int   batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int   beam_width       = output_tensors->at(\"output_ids\").shape[2];\n    const int   step             = input_tensors->at(\"step\").getVal<int>();\n    const int   ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int   local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const float diversity_rate   = input_tensors->isExist(\"beam_search_diversity_rate\") ?\n                                       input_tensors->at(\"beam_search_diversity_rate\").getVal<float>() :\n                                       0.0f;\n    const float length_penalty =\n        input_tensors->isExist(\"len_penalty\") ? input_tensors->at(\"len_penalty\").getVal<float>() : 0.0f;\n\n    const int id_offset = step * batch_size * beam_width + local_batch_size * ite * beam_width;\n\n    BeamHypotheses beam_hyps;\n    if (output_tensors->isExist(\"beam_hyps\")) {\n        beam_hyps                      = *((BeamHypotheses*)(output_tensors->at(\"beam_hyps\").getPtr<void>()));\n        beam_hyps.step                 = step;\n        beam_hyps.ite                  = ite;\n        beam_hyps.local_batch_size     = local_batch_size;\n        beam_hyps.batch_size           = output_tensors->at(\"output_ids\").shape[1];\n        beam_hyps.max_seq_len          = output_tensors->at(\"output_ids\").shape[0];\n        beam_hyps.output_ids_src       = output_tensors->at(\"output_ids\").getPtr<int>();\n        beam_hyps.parent_ids_src       = output_tensors->at(\"parent_ids\").getPtr<int>();\n        beam_hyps.sequence_lengths_src = output_tensors->at(\"sequence_length\").getPtr<int>();\n        beam_hyps.log_probs_src        = output_tensors->getPtr<float>(\"output_log_probs\", nullptr);\n        beam_hyps.length_penalty       = length_penalty;\n        beam_hyps.end_ids              = input_tensors->at(\"end_id\").getPtr<int>();\n    }\n\n    invokeTopkSoftMax(input_tensors->at(\"logits\").getPtr<T>(),\n                      (const T*)(nullptr),\n                      output_tensors->at(\"finished\").getPtr<bool>(),\n                      output_tensors->at(\"sequence_length\").getPtr<int>(),\n                      output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                      output_tensors->getPtrWithOffset<float>(\"output_log_probs\", id_offset, nullptr),\n                      output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                      topk_softmax_workspace_,\n                      topk_softmax_workspace_size_,\n                      &beam_hyps,\n                      local_batch_size,\n                      beam_width,\n                      vocab_size_padded_,\n                      input_tensors->at(\"end_id\").getPtr<int>(),\n                      diversity_rate,\n                      length_penalty,\n                      stream_);\n    sync_check_cuda_error();\n\n    invokeUpdate(output_tensors->at(\"finished\").getPtr<bool>(),\n                 output_tensors->at(\"parent_ids\").getPtrWithOffset<int>(id_offset),\n                 output_tensors->at(\"sequence_length\").getPtr<int>(),\n                 output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                 output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                 &beam_hyps,\n                 local_batch_size,\n                 beam_width,\n                 vocab_size_padded_,\n                 input_tensors->at(\"end_id\").getPtr<const int>(),\n                 stream_);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::allocateBuffer(size_t batch_size, size_t beam_width)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    // we need to check 2 * beam_width candidates each time\n    // 64 is the max beam width we support now.\n    topk_softmax_workspace_size_ =\n        (size_t)(ceil(batch_size * 64 * (64 * 2) / 4.) * 4 * 2\n                 + ceil(batch_size * (64 * 2) * SMALL_TOP_K_SOFTMAX_MAX_VOC_PARTS * (2 * (MAX_K * 2) + 2) / 4.) * 4);\n\n    topk_softmax_workspace_ = reinterpret_cast<float*>(\n        allocator_->reMalloc(topk_softmax_workspace_, sizeof(float) * topk_softmax_workspace_size_, true));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::OnlineBeamSearchLayer(size_t           max_batch_size,\n                                                size_t           head_num,\n                                                size_t           size_per_head,\n                                                size_t           beam_width,\n                                                size_t           vocab_size,\n                                                size_t           vocab_size_padded,\n                                                int              end_id,\n                                                float            diversity_rate,\n                                                float            temperature,\n                                                float            len_penalty,\n                                                float            repetition_penalty,\n                                                cudaStream_t     stream,\n                                                cublasMMWrapper* cublas_wrapper,\n                                                IAllocator*      allocator,\n                                                bool             is_free_buffer_after_forward):\n    BaseBeamSearchLayer<T>(max_batch_size,\n                           head_num,\n                           size_per_head,\n                           beam_width,\n                           vocab_size,\n                           vocab_size_padded,\n                           end_id,\n                           diversity_rate,\n                           temperature,\n                           len_penalty,\n                           repetition_penalty,\n                           stream,\n                           cublas_wrapper,\n                           allocator,\n                           is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::OnlineBeamSearchLayer(OnlineBeamSearchLayer<T> const& beam_search_layer):\n    BaseBeamSearchLayer<T>(beam_search_layer)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::~OnlineBeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate class OnlineBeamSearchLayer<float>;\ntemplate class OnlineBeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/layers/beam_search_layers/OnlineBeamSearchLayer.h\"\n\nnamespace fastertransformer {\n\nstatic const int SMALL_TOP_K_SOFTMAX_MAX_VOC_PARTS = 128;\nstatic const int MAX_K                             = 4;\n\ntemplate<typename T>\n__global__ void update_kernel(bool*          finished,\n                              int*           parent_ids,\n                              int*           sequence_length,\n                              int*           word_ids,\n                              int*           output_ids,\n                              BeamHypotheses beam_hyps,\n                              const int      vocab_size,\n                              const int*     end_ids,\n                              const int      local_batch_size,\n                              const int      beam_width)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < local_batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n\n        int batch_id           = index / beam_width;\n        sequence_length[index] = finished[index] ? sequence_length[index] : sequence_length[index] + 1;\n\n        int beam_id = (word_ids[index] / vocab_size) % beam_width;\n        int word_id = word_ids[index] % vocab_size;\n\n        sequence_length[index] = sequence_length[batch_id * beam_width + beam_id];\n        finished[index]        = word_id == end_ids[index / beam_width] ? 1 : 0;\n        parent_ids[index]      = beam_id;\n        word_ids[index]        = word_id;\n        output_ids[index]      = word_id;\n\n        if (beam_hyps.num_beams != nullptr) {\n            if (beam_hyps.num_beams[beam_hyps.ite * beam_hyps.local_batch_size + batch_id] == beam_width) {\n                for (int i = 0; i < beam_width; i++) {\n                    finished[batch_id * beam_width + i] = true;\n                }\n            }\n        }\n    }\n}\n\nvoid invokeUpdate(bool*           finished,\n                  int*            parent_ids,\n                  int*            sequence_length,\n                  int*            word_ids,\n                  int*            output_ids,\n                  BeamHypotheses* beam_hyps,\n                  const int       local_batch_size,\n                  const int       beam_width,\n                  const int       vocab_size_padded,\n                  const int*      end_ids,\n                  hipStream_t    stream)\n{\n    dim3 grid((int)ceil(local_batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    update_kernel<float><<<grid, block, 0, stream>>>(finished,\n                                                     parent_ids,\n                                                     sequence_length,\n                                                     word_ids,\n                                                     output_ids,\n                                                     *beam_hyps,\n                                                     vocab_size_padded,\n                                                     end_ids,\n                                                     local_batch_size,\n                                                     beam_width);\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::invokeSoftMax(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size, beam_width]\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 6);\n\n    const int   batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int   beam_width       = output_tensors->at(\"output_ids\").shape[2];\n    const int   step             = input_tensors->at(\"step\").getVal<int>();\n    const int   ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int   local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const float diversity_rate   = input_tensors->isExist(\"beam_search_diversity_rate\") ?\n                                       input_tensors->at(\"beam_search_diversity_rate\").getVal<float>() :\n                                       0.0f;\n    const float length_penalty =\n        input_tensors->isExist(\"len_penalty\") ? input_tensors->at(\"len_penalty\").getVal<float>() : 0.0f;\n\n    const int id_offset = step * batch_size * beam_width + local_batch_size * ite * beam_width;\n\n    BeamHypotheses beam_hyps;\n    if (output_tensors->isExist(\"beam_hyps\")) {\n        beam_hyps                      = *((BeamHypotheses*)(output_tensors->at(\"beam_hyps\").getPtr<void>()));\n        beam_hyps.step                 = step;\n        beam_hyps.ite                  = ite;\n        beam_hyps.local_batch_size     = local_batch_size;\n        beam_hyps.batch_size           = output_tensors->at(\"output_ids\").shape[1];\n        beam_hyps.max_seq_len          = output_tensors->at(\"output_ids\").shape[0];\n        beam_hyps.output_ids_src       = output_tensors->at(\"output_ids\").getPtr<int>();\n        beam_hyps.parent_ids_src       = output_tensors->at(\"parent_ids\").getPtr<int>();\n        beam_hyps.sequence_lengths_src = output_tensors->at(\"sequence_length\").getPtr<int>();\n        beam_hyps.log_probs_src        = output_tensors->getPtr<float>(\"output_log_probs\", nullptr);\n        beam_hyps.length_penalty       = length_penalty;\n        beam_hyps.end_ids              = input_tensors->at(\"end_id\").getPtr<int>();\n    }\n\n    invokeTopkSoftMax(input_tensors->at(\"logits\").getPtr<T>(),\n                      (const T*)(nullptr),\n                      output_tensors->at(\"finished\").getPtr<bool>(),\n                      output_tensors->at(\"sequence_length\").getPtr<int>(),\n                      output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                      output_tensors->getPtrWithOffset<float>(\"output_log_probs\", id_offset, nullptr),\n                      output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                      topk_softmax_workspace_,\n                      topk_softmax_workspace_size_,\n                      &beam_hyps,\n                      local_batch_size,\n                      beam_width,\n                      vocab_size_padded_,\n                      input_tensors->at(\"end_id\").getPtr<int>(),\n                      diversity_rate,\n                      length_penalty,\n                      stream_);\n    sync_check_cuda_error();\n\n    invokeUpdate(output_tensors->at(\"finished\").getPtr<bool>(),\n                 output_tensors->at(\"parent_ids\").getPtrWithOffset<int>(id_offset),\n                 output_tensors->at(\"sequence_length\").getPtr<int>(),\n                 output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                 output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                 &beam_hyps,\n                 local_batch_size,\n                 beam_width,\n                 vocab_size_padded_,\n                 input_tensors->at(\"end_id\").getPtr<const int>(),\n                 stream_);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid OnlineBeamSearchLayer<T>::allocateBuffer(size_t batch_size, size_t beam_width)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    // we need to check 2 * beam_width candidates each time\n    // 64 is the max beam width we support now.\n    topk_softmax_workspace_size_ =\n        (size_t)(ceil(batch_size * 64 * (64 * 2) / 4.) * 4 * 2\n                 + ceil(batch_size * (64 * 2) * SMALL_TOP_K_SOFTMAX_MAX_VOC_PARTS * (2 * (MAX_K * 2) + 2) / 4.) * 4);\n\n    topk_softmax_workspace_ = reinterpret_cast<float*>(\n        allocator_->reMalloc(topk_softmax_workspace_, sizeof(float) * topk_softmax_workspace_size_, true));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::OnlineBeamSearchLayer(size_t           max_batch_size,\n                                                size_t           head_num,\n                                                size_t           size_per_head,\n                                                size_t           beam_width,\n                                                size_t           vocab_size,\n                                                size_t           vocab_size_padded,\n                                                int              end_id,\n                                                float            diversity_rate,\n                                                float            temperature,\n                                                float            len_penalty,\n                                                float            repetition_penalty,\n                                                hipStream_t     stream,\n                                                cublasMMWrapper* cublas_wrapper,\n                                                IAllocator*      allocator,\n                                                bool             is_free_buffer_after_forward):\n    BaseBeamSearchLayer<T>(max_batch_size,\n                           head_num,\n                           size_per_head,\n                           beam_width,\n                           vocab_size,\n                           vocab_size_padded,\n                           end_id,\n                           diversity_rate,\n                           temperature,\n                           len_penalty,\n                           repetition_penalty,\n                           stream,\n                           cublas_wrapper,\n                           allocator,\n                           is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::OnlineBeamSearchLayer(OnlineBeamSearchLayer<T> const& beam_search_layer):\n    BaseBeamSearchLayer<T>(beam_search_layer)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate<typename T>\nOnlineBeamSearchLayer<T>::~OnlineBeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate class OnlineBeamSearchLayer<float>;\ntemplate class OnlineBeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/beam_search_layers/OnlineBeamSearchLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/layers/beam_search_layers/BeamSearchLayer.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void logProbAddCumLogProb(float*       log_probs,\n                                     const T*     logits,\n                                     const float* cum_log_probs,\n                                     const int*   end_ids,\n                                     const bool*  finished,\n                                     const int    beam_width,\n                                     const int    n)\n{\n    int  bid    = blockIdx.x;\n    bool finish = finished != nullptr ? finished[bid] : false;\n    int  offset = bid * n;\n\n    float            max_val = -1 * FLT_MAX;\n    __shared__ float s_max_val;\n    __shared__ float s_sum_val;\n\n    if (finish) {\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = (tid == end_ids[bid / beam_width]) ? cum_log_probs[bid] : -FLT_MAX;\n        }\n    }\n    else {\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = (float)(logits[offset + tid]);\n            max_val                 = max(max_val, log_probs[offset + tid]);\n        }\n\n        max_val = blockReduceMax(max_val);\n        if (threadIdx.x == 0) {\n            s_max_val = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = __expf(log_probs[offset + tid] - s_max_val);\n            sum_val += log_probs[offset + tid];\n        }\n\n        sum_val = blockReduceSum(sum_val);\n        if (threadIdx.x == 0) {\n            s_sum_val = sum_val + 1e-6f;\n        }\n        __syncthreads();\n\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = logf(log_probs[offset + tid] / s_sum_val) + cum_log_probs[bid];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLogProbAddCumLogProb(float*       log_probs,\n                                const T*     logits,\n                                const float* cum_log_probs,\n                                const int*   end_ids,\n                                const bool*  finished,\n                                const int    m,\n                                const int    beam_width,\n                                const int    n,\n                                cudaStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    logProbAddCumLogProb<<<grid, block, 0, stream>>>(\n        log_probs, logits, cum_log_probs, end_ids, finished, beam_width, n);\n}\n\ntemplate<typename T>\n__global__ void updateStatesKernel(T*             log_probs,\n                                   T*             cum_log_probs,\n                                   float*         output_log_probs,\n                                   bool*          finished,\n                                   int*           parent_ids,\n                                   int*           sequence_length,\n                                   int*           word_ids,\n                                   int*           output_ids,\n                                   BeamHypotheses beam_hyps,\n                                   const int      local_batch_size,\n                                   const int      beam_width,\n                                   const int      vocab_size,\n                                   const int*     end_ids)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < local_batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n\n        int batch_id           = index / beam_width;\n        sequence_length[index] = finished[index] ? sequence_length[index] : sequence_length[index] + 1;\n\n        int beam_id = (word_ids[index] / vocab_size) % beam_width;\n        int word_id = word_ids[index] % vocab_size;\n\n        if (output_log_probs != nullptr) {\n            // get the cum_log_probs of previous run\n            output_log_probs[index] = log_probs[batch_id * beam_width * vocab_size + beam_id * vocab_size + word_id]\n                                      - cum_log_probs[batch_id * beam_width + beam_id];\n        }\n        cum_log_probs[index]   = log_probs[batch_id * beam_width * vocab_size + beam_id * vocab_size + word_id];\n        sequence_length[index] = sequence_length[batch_id * beam_width + beam_id];\n        finished[index]        = word_id == end_ids[batch_id] ? 1 : 0;\n        parent_ids[index]      = beam_id;\n        word_ids[index]        = word_id;\n        output_ids[index]      = word_id;\n\n        if (beam_hyps.num_beams != nullptr) {\n            if (beam_hyps.num_beams[beam_hyps.ite * beam_hyps.local_batch_size + batch_id] == beam_width) {\n                for (int i = 0; i < beam_width; i++) {\n                    finished[batch_id * beam_width + i] = true;\n                }\n            }\n        }\n    }\n}\n\nvoid invokeUpdateStates(float*          log_probs,\n                        float*          cum_log_probs,\n                        float*          output_log_probs,\n                        bool*           finished,\n                        int*            parent_ids,\n                        int*            sequence_length,\n                        int*            word_ids,\n                        int*            output_ids,\n                        BeamHypotheses* beam_hyps,\n                        const int       local_batch_size,\n                        const int       beam_width,\n                        const int       vocab_size,\n                        const int*      end_ids,\n                        cudaStream_t    stream)\n{\n    dim3 grid((int)ceil(local_batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    updateStatesKernel<float><<<grid, block, 0, stream>>>(log_probs,\n                                                          cum_log_probs,\n                                                          output_log_probs,\n                                                          finished,\n                                                          parent_ids,\n                                                          sequence_length,\n                                                          word_ids,\n                                                          output_ids,\n                                                          *beam_hyps,\n                                                          local_batch_size,\n                                                          beam_width,\n                                                          vocab_size,\n                                                          end_ids);\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::invokeSoftMax(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size * beam_width], optional\n    //      beam_hyps, optional\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 6);\n\n    const int   batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int   beam_width       = output_tensors->at(\"output_ids\").shape[2];\n    const int   step             = input_tensors->at(\"step\").getVal<int>();\n    const int   ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int   local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const float diversity_rate   = input_tensors->isExist(\"beam_search_diversity_rate\") ?\n                                       input_tensors->at(\"beam_search_diversity_rate\").getVal<float>() :\n                                       0.0f;\n    const float length_penalty =\n        input_tensors->isExist(\"len_penalty\") ? input_tensors->at(\"len_penalty\").getVal<float>() : 0.0f;\n\n    const int id_offset = step * batch_size * beam_width + ite * local_batch_size * beam_width;\n    invokeLogProbAddCumLogProb(float_log_prob_buf_,\n                               input_tensors->at(\"logits\").getPtr<T>(),\n                               output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                               input_tensors->at(\"end_id\").getPtr<const int>(),\n                               output_tensors->at(\"finished\").getPtr<bool>(),\n                               local_batch_size * beam_width,\n                               beam_width,\n                               vocab_size_padded_,\n                               stream_);\n    sync_check_cuda_error();\n\n    BeamHypotheses beam_hyps;\n    if (output_tensors->isExist(\"beam_hyps\") && diversity_rate == 0.0f) {\n        beam_hyps                      = *((BeamHypotheses*)(output_tensors->at(\"beam_hyps\").getPtr<void>()));\n        beam_hyps.step                 = step;\n        beam_hyps.ite                  = ite;\n        beam_hyps.local_batch_size     = local_batch_size;\n        beam_hyps.batch_size           = output_tensors->at(\"output_ids\").shape[1];\n        beam_hyps.max_seq_len          = output_tensors->at(\"output_ids\").shape[0];\n        beam_hyps.output_ids_src       = output_tensors->at(\"output_ids\").getPtr<int>();\n        beam_hyps.parent_ids_src       = output_tensors->at(\"parent_ids\").getPtr<int>();\n        beam_hyps.sequence_lengths_src = output_tensors->at(\"sequence_length\").getPtr<int>();\n        beam_hyps.length_penalty       = length_penalty;\n    }\n\n    invokeTopkBeamSearch<float>(topk_softmax_workspace_,\n                                topk_softmax_workspace_size_,\n                                float_log_prob_buf_,\n                                output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                                &beam_hyps,\n                                output_tensors->at(\"finished\").getPtr<bool>(),\n                                output_tensors->isExist(\"sequence_length\") ?\n                                    output_tensors->at(\"sequence_length\").getPtr<int>() :\n                                    (int*)nullptr,\n                                local_batch_size,\n                                beam_width,\n                                vocab_size_padded_,\n                                diversity_rate,\n                                length_penalty,\n                                input_tensors->at(\"end_id\").getPtr<const int>(),\n                                stream_);\n    sync_check_cuda_error();\n\n    invokeUpdateStates(float_log_prob_buf_,\n                       output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                       output_tensors->getPtrWithOffset<float>(\"output_log_probs\", id_offset, nullptr),\n                       output_tensors->at(\"finished\").getPtr<bool>(),\n                       output_tensors->at(\"parent_ids\").getPtrWithOffset<int>(id_offset),\n                       output_tensors->at(\"sequence_length\").getPtr<int>(),\n                       output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                       output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                       &beam_hyps,\n                       local_batch_size,\n                       beam_width,\n                       vocab_size_padded_,\n                       input_tensors->at(\"end_id\").getPtr<const int>(),\n                       stream_);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::allocateBuffer(size_t batch_size, size_t beam_width)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n\n    invokeTopkBeamSearch<float>(nullptr,\n                                topk_softmax_workspace_size_,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                batch_size,\n                                beam_width,\n                                vocab_size_padded_,\n                                0.0f,  // diversity rate\n                                0.0f,  // length penalty\n                                nullptr,\n                                stream_);\n    topk_softmax_workspace_ = reinterpret_cast<float*>(allocator_->reMalloc(\n        topk_softmax_workspace_,\n        topk_softmax_workspace_size_ + sizeof(float) * batch_size * beam_width * vocab_size_padded_,\n        false));\n    float_log_prob_buf_     = (float*)((char*)topk_softmax_workspace_ + topk_softmax_workspace_size_);\n    is_allocate_buffer_     = true;\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::BeamSearchLayer(size_t           max_batch_size,\n                                    size_t           head_num,\n                                    size_t           size_per_head,\n                                    size_t           beam_width,\n                                    size_t           vocab_size,\n                                    size_t           vocab_size_padded,\n                                    int              end_id,\n                                    float            diversity_rate,\n                                    float            temperature,\n                                    float            len_penalty,\n                                    float            repetition_penalty,\n                                    cudaStream_t     stream,\n                                    cublasMMWrapper* cublas_wrapper,\n                                    IAllocator*      allocator,\n                                    bool             is_free_buffer_after_forward):\n    BaseBeamSearchLayer<T>(max_batch_size,\n                           head_num,\n                           size_per_head,\n                           beam_width,\n                           vocab_size,\n                           vocab_size_padded,\n                           end_id,\n                           diversity_rate,\n                           temperature,\n                           len_penalty,\n                           repetition_penalty,\n                           stream,\n                           cublas_wrapper,\n                           allocator,\n                           is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::BeamSearchLayer(BeamSearchLayer<T> const& beam_search_layer):\n    BaseBeamSearchLayer<T>(beam_search_layer)\n{\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::~BeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate class BeamSearchLayer<float>;\ntemplate class BeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/layers/beam_search_layers/BeamSearchLayer.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void logProbAddCumLogProb(float*       log_probs,\n                                     const T*     logits,\n                                     const float* cum_log_probs,\n                                     const int*   end_ids,\n                                     const bool*  finished,\n                                     const int    beam_width,\n                                     const int    n)\n{\n    int  bid    = blockIdx.x;\n    bool finish = finished != nullptr ? finished[bid] : false;\n    int  offset = bid * n;\n\n    float            max_val = -1 * FLT_MAX;\n    __shared__ float s_max_val;\n    __shared__ float s_sum_val;\n\n    if (finish) {\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = (tid == end_ids[bid / beam_width]) ? cum_log_probs[bid] : -FLT_MAX;\n        }\n    }\n    else {\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = (float)(logits[offset + tid]);\n            max_val                 = max(max_val, log_probs[offset + tid]);\n        }\n\n        max_val = blockReduceMax(max_val);\n        if (threadIdx.x == 0) {\n            s_max_val = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = __expf(log_probs[offset + tid] - s_max_val);\n            sum_val += log_probs[offset + tid];\n        }\n\n        sum_val = blockReduceSum(sum_val);\n        if (threadIdx.x == 0) {\n            s_sum_val = sum_val + 1e-6f;\n        }\n        __syncthreads();\n\n        for (int tid = threadIdx.x; tid < n; tid += blockDim.x) {\n            log_probs[offset + tid] = logf(log_probs[offset + tid] / s_sum_val) + cum_log_probs[bid];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLogProbAddCumLogProb(float*       log_probs,\n                                const T*     logits,\n                                const float* cum_log_probs,\n                                const int*   end_ids,\n                                const bool*  finished,\n                                const int    m,\n                                const int    beam_width,\n                                const int    n,\n                                hipStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    logProbAddCumLogProb<<<grid, block, 0, stream>>>(\n        log_probs, logits, cum_log_probs, end_ids, finished, beam_width, n);\n}\n\ntemplate<typename T>\n__global__ void updateStatesKernel(T*             log_probs,\n                                   T*             cum_log_probs,\n                                   float*         output_log_probs,\n                                   bool*          finished,\n                                   int*           parent_ids,\n                                   int*           sequence_length,\n                                   int*           word_ids,\n                                   int*           output_ids,\n                                   BeamHypotheses beam_hyps,\n                                   const int      local_batch_size,\n                                   const int      beam_width,\n                                   const int      vocab_size,\n                                   const int*     end_ids)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < local_batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n\n        int batch_id           = index / beam_width;\n        sequence_length[index] = finished[index] ? sequence_length[index] : sequence_length[index] + 1;\n\n        int beam_id = (word_ids[index] / vocab_size) % beam_width;\n        int word_id = word_ids[index] % vocab_size;\n\n        if (output_log_probs != nullptr) {\n            // get the cum_log_probs of previous run\n            output_log_probs[index] = log_probs[batch_id * beam_width * vocab_size + beam_id * vocab_size + word_id]\n                                      - cum_log_probs[batch_id * beam_width + beam_id];\n        }\n        cum_log_probs[index]   = log_probs[batch_id * beam_width * vocab_size + beam_id * vocab_size + word_id];\n        sequence_length[index] = sequence_length[batch_id * beam_width + beam_id];\n        finished[index]        = word_id == end_ids[batch_id] ? 1 : 0;\n        parent_ids[index]      = beam_id;\n        word_ids[index]        = word_id;\n        output_ids[index]      = word_id;\n\n        if (beam_hyps.num_beams != nullptr) {\n            if (beam_hyps.num_beams[beam_hyps.ite * beam_hyps.local_batch_size + batch_id] == beam_width) {\n                for (int i = 0; i < beam_width; i++) {\n                    finished[batch_id * beam_width + i] = true;\n                }\n            }\n        }\n    }\n}\n\nvoid invokeUpdateStates(float*          log_probs,\n                        float*          cum_log_probs,\n                        float*          output_log_probs,\n                        bool*           finished,\n                        int*            parent_ids,\n                        int*            sequence_length,\n                        int*            word_ids,\n                        int*            output_ids,\n                        BeamHypotheses* beam_hyps,\n                        const int       local_batch_size,\n                        const int       beam_width,\n                        const int       vocab_size,\n                        const int*      end_ids,\n                        hipStream_t    stream)\n{\n    dim3 grid((int)ceil(local_batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    updateStatesKernel<float><<<grid, block, 0, stream>>>(log_probs,\n                                                          cum_log_probs,\n                                                          output_log_probs,\n                                                          finished,\n                                                          parent_ids,\n                                                          sequence_length,\n                                                          word_ids,\n                                                          output_ids,\n                                                          *beam_hyps,\n                                                          local_batch_size,\n                                                          beam_width,\n                                                          vocab_size,\n                                                          end_ids);\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::invokeSoftMax(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size * beam_width], optional\n    //      beam_hyps, optional\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 6);\n\n    const int   batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int   beam_width       = output_tensors->at(\"output_ids\").shape[2];\n    const int   step             = input_tensors->at(\"step\").getVal<int>();\n    const int   ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int   local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const float diversity_rate   = input_tensors->isExist(\"beam_search_diversity_rate\") ?\n                                       input_tensors->at(\"beam_search_diversity_rate\").getVal<float>() :\n                                       0.0f;\n    const float length_penalty =\n        input_tensors->isExist(\"len_penalty\") ? input_tensors->at(\"len_penalty\").getVal<float>() : 0.0f;\n\n    const int id_offset = step * batch_size * beam_width + ite * local_batch_size * beam_width;\n    invokeLogProbAddCumLogProb(float_log_prob_buf_,\n                               input_tensors->at(\"logits\").getPtr<T>(),\n                               output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                               input_tensors->at(\"end_id\").getPtr<const int>(),\n                               output_tensors->at(\"finished\").getPtr<bool>(),\n                               local_batch_size * beam_width,\n                               beam_width,\n                               vocab_size_padded_,\n                               stream_);\n    sync_check_cuda_error();\n\n    BeamHypotheses beam_hyps;\n    if (output_tensors->isExist(\"beam_hyps\") && diversity_rate == 0.0f) {\n        beam_hyps                      = *((BeamHypotheses*)(output_tensors->at(\"beam_hyps\").getPtr<void>()));\n        beam_hyps.step                 = step;\n        beam_hyps.ite                  = ite;\n        beam_hyps.local_batch_size     = local_batch_size;\n        beam_hyps.batch_size           = output_tensors->at(\"output_ids\").shape[1];\n        beam_hyps.max_seq_len          = output_tensors->at(\"output_ids\").shape[0];\n        beam_hyps.output_ids_src       = output_tensors->at(\"output_ids\").getPtr<int>();\n        beam_hyps.parent_ids_src       = output_tensors->at(\"parent_ids\").getPtr<int>();\n        beam_hyps.sequence_lengths_src = output_tensors->at(\"sequence_length\").getPtr<int>();\n        beam_hyps.length_penalty       = length_penalty;\n    }\n\n    invokeTopkBeamSearch<float>(topk_softmax_workspace_,\n                                topk_softmax_workspace_size_,\n                                float_log_prob_buf_,\n                                output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                                &beam_hyps,\n                                output_tensors->at(\"finished\").getPtr<bool>(),\n                                output_tensors->isExist(\"sequence_length\") ?\n                                    output_tensors->at(\"sequence_length\").getPtr<int>() :\n                                    (int*)nullptr,\n                                local_batch_size,\n                                beam_width,\n                                vocab_size_padded_,\n                                diversity_rate,\n                                length_penalty,\n                                input_tensors->at(\"end_id\").getPtr<const int>(),\n                                stream_);\n    sync_check_cuda_error();\n\n    invokeUpdateStates(float_log_prob_buf_,\n                       output_tensors->at(\"cum_log_probs\").getPtr<float>(),\n                       output_tensors->getPtrWithOffset<float>(\"output_log_probs\", id_offset, nullptr),\n                       output_tensors->at(\"finished\").getPtr<bool>(),\n                       output_tensors->at(\"parent_ids\").getPtrWithOffset<int>(id_offset),\n                       output_tensors->at(\"sequence_length\").getPtr<int>(),\n                       output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                       output_tensors->at(\"output_ids\").getPtrWithOffset<int>(id_offset),\n                       &beam_hyps,\n                       local_batch_size,\n                       beam_width,\n                       vocab_size_padded_,\n                       input_tensors->at(\"end_id\").getPtr<const int>(),\n                       stream_);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid BeamSearchLayer<T>::allocateBuffer(size_t batch_size, size_t beam_width)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n\n    invokeTopkBeamSearch<float>(nullptr,\n                                topk_softmax_workspace_size_,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                nullptr,\n                                batch_size,\n                                beam_width,\n                                vocab_size_padded_,\n                                0.0f,  // diversity rate\n                                0.0f,  // length penalty\n                                nullptr,\n                                stream_);\n    topk_softmax_workspace_ = reinterpret_cast<float*>(allocator_->reMalloc(\n        topk_softmax_workspace_,\n        topk_softmax_workspace_size_ + sizeof(float) * batch_size * beam_width * vocab_size_padded_,\n        false));\n    float_log_prob_buf_     = (float*)((char*)topk_softmax_workspace_ + topk_softmax_workspace_size_);\n    is_allocate_buffer_     = true;\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::BeamSearchLayer(size_t           max_batch_size,\n                                    size_t           head_num,\n                                    size_t           size_per_head,\n                                    size_t           beam_width,\n                                    size_t           vocab_size,\n                                    size_t           vocab_size_padded,\n                                    int              end_id,\n                                    float            diversity_rate,\n                                    float            temperature,\n                                    float            len_penalty,\n                                    float            repetition_penalty,\n                                    hipStream_t     stream,\n                                    cublasMMWrapper* cublas_wrapper,\n                                    IAllocator*      allocator,\n                                    bool             is_free_buffer_after_forward):\n    BaseBeamSearchLayer<T>(max_batch_size,\n                           head_num,\n                           size_per_head,\n                           beam_width,\n                           vocab_size,\n                           vocab_size_padded,\n                           end_id,\n                           diversity_rate,\n                           temperature,\n                           len_penalty,\n                           repetition_penalty,\n                           stream,\n                           cublas_wrapper,\n                           allocator,\n                           is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::BeamSearchLayer(BeamSearchLayer<T> const& beam_search_layer):\n    BaseBeamSearchLayer<T>(beam_search_layer)\n{\n}\n\ntemplate<typename T>\nBeamSearchLayer<T>::~BeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate class BeamSearchLayer<float>;\ntemplate class BeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/beam_search_layers/BeamSearchLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/beam_search_penalty_kernels.h\"\n#include \"src/fastertransformer/layers/beam_search_layers/BaseBeamSearchLayer.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__global__ void update_indir_cache_kernel(int*        tgt_indir_cache,\n                                          const int*  src_indir_cache,\n                                          const int*  beam_ids,\n                                          const bool* finished,\n                                          int         start_step,\n                                          int         batch_dim,\n                                          int         local_batch_size,\n                                          int         beam_width,\n                                          int         max_seq_len,\n                                          int         step)\n{\n    int       time_step = threadIdx.x + blockIdx.x * blockDim.x;\n    int       bb_id     = threadIdx.y + blockIdx.y * blockDim.y;\n    const int batch_id  = bb_id / beam_width;\n    const int beam_id   = bb_id % beam_width;\n\n    if (bb_id >= beam_width * local_batch_size || time_step >= min(step + 1, max_seq_len) || finished[bb_id]) {\n        return;\n    }\n    time_step += start_step;\n    const int time_step_circ = time_step % max_seq_len;\n\n    const int src_beam = beam_ids[batch_id * beam_width + beam_id];\n\n    const uint tgt_offset = batch_id * beam_width * max_seq_len + beam_id * max_seq_len + time_step_circ;\n    const uint src_offset = batch_id * beam_width * max_seq_len + src_beam * max_seq_len + time_step_circ;\n\n    tgt_indir_cache[tgt_offset] = (time_step == step) ? beam_id : src_indir_cache[src_offset];\n}\n\nvoid update_indir_cache_kernelLauncher(int*         tgt_indir_cache,\n                                       const int*   src_indir_cache,\n                                       const int*   beam_ids,\n                                       const bool*  finished,\n                                       int          batch_dim,\n                                       int          local_batch_size,\n                                       int          beam_width,\n                                       int          max_seq_len,\n                                       int          step,\n                                       cudaStream_t stream)\n{\n    const dim3 block(32);\n    const int  start_step = max(0, step + 1 - max_seq_len);\n    const int  num_steps  = min(step + 1, max_seq_len);\n    // Update indirections steps [start_step, step], included\n    const dim3 grid((num_steps + block.x - 1) / block.x, local_batch_size * beam_width);\n    update_indir_cache_kernel<<<grid, block, 0, stream>>>(tgt_indir_cache,\n                                                          src_indir_cache,\n                                                          beam_ids,\n                                                          finished,\n                                                          start_step,\n                                                          batch_dim,\n                                                          local_batch_size,\n                                                          beam_width,\n                                                          max_seq_len,\n                                                          step);\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::BaseBeamSearchLayer(size_t           max_batch_size,\n                                            size_t           head_num,\n                                            size_t           size_per_head,\n                                            size_t           beam_width,\n                                            size_t           vocab_size,\n                                            size_t           vocab_size_padded,\n                                            int              end_id,\n                                            float            diversity_rate,\n                                            float            temperature,\n                                            float            len_penalty,\n                                            float            repetition_penalty,\n                                            cudaStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward):\n    DynamicDecodeBaseLayer(stream, cublas_wrapper, allocator, is_free_buffer_after_forward, nullptr),\n    vocab_size_(vocab_size),\n    vocab_size_padded_(vocab_size_padded)\n{\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::BaseBeamSearchLayer(BaseBeamSearchLayer<T> const& beam_search_layer):\n    DynamicDecodeBaseLayer(beam_search_layer),\n    vocab_size_(beam_search_layer.vocab_size_),\n    vocab_size_padded_(beam_search_layer.vocab_size_padded_),\n    topk_softmax_workspace_size_(beam_search_layer.topk_softmax_workspace_size_)\n{\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::~BaseBeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::freeBuffer()\n{\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&topk_softmax_workspace_));\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    // do nothing.\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(std::vector<Tensor>* output_tensors, const std::vector<Tensor>* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n\n    std::unordered_map<std::string, Tensor> input_tensors_map{{\"logits\", input_tensors->at(0)},\n                                                              {\"embedding_bias\", input_tensors->at(1)},\n                                                              {\"step\", input_tensors->at(2)},\n                                                              {\"src_cache_indirection\", input_tensors->at(4)},\n                                                              {\"max_input_length\", input_tensors->at(5)},\n                                                              {\"input_lengths\", input_tensors->at(6)},\n                                                              {\"ite\", input_tensors->at(7)}};\n\n    std::unordered_map<std::string, Tensor> output_tensors_map{{\"output_ids\", output_tensors->at(0)},\n                                                               {\"finished\", output_tensors->at(1)},\n                                                               {\"cum_log_probs\", output_tensors->at(2)},\n                                                               {\"parent_ids\", output_tensors->at(3)},\n                                                               {\"sequence_length\", output_tensors->at(4)},\n                                                               {\"tgt_cache_indirection\", output_tensors->at(5)}};\n    forward(&output_tensors_map, &input_tensors_map);\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(std::unordered_map<std::string, Tensor>*       output_tensors,\n                                     const std::unordered_map<std::string, Tensor>* input_tensors)\n{\n    TensorMap input_map(*input_tensors);\n    TensorMap output_map(*output_tensors);\n    forward(&output_map, &input_map);\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      end_id [local_batch_size]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width], optional\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n    //      presence_penalty [1] on cpu, optional\n    //          Only one of repetition and presence penalties is allowed.\n    //      min_length [1] on cpu, int, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width], optional\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width], optional\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size, beam_width], optional\n    //      beam_hyps, optional\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 5);\n    const int batch_size = output_tensors->at(\"output_ids\").shape[1];\n    const int beam_width = output_tensors->at(\"output_ids\").shape[2];\n    allocateBuffer(batch_size, beam_width);\n\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n\n    const float temperature    = input_tensors->getVal<float>(\"temperature\", 1.0f);\n    const T*    embedding_bias = input_tensors->getPtr<const T>(\"embedding_bias\", nullptr);\n\n    RepetitionPenaltyType repetition_penalty_type = RepetitionPenaltyType::None;\n    float                 repetition_penalty      = getDefaultPenaltyValue(repetition_penalty_type);\n    if (input_tensors->isExist(\"repetition_penalty\") || input_tensors->isExist(\"presence_penalty\")) {\n        FT_CHECK_WITH_INFO(\n            !(input_tensors->isExist(\"repetition_penalty\") && input_tensors->isExist(\"presence_penalty\")),\n            \"Found ambiguous parameters repetition_penalty and presence_penalty which are mutually exclusive. \"\n            \"Please provide one of repetition_penalty or presence_penalty.\");\n        repetition_penalty_type = input_tensors->isExist(\"repetition_penalty\") ? RepetitionPenaltyType::Multiplicative :\n                                                                                 RepetitionPenaltyType::Additive;\n        repetition_penalty      = repetition_penalty_type == RepetitionPenaltyType::Multiplicative ?\n                                      input_tensors->getVal<float>(\"repetition_penalty\") :\n                                      input_tensors->getVal<float>(\"presence_penalty\");\n    }\n\n    invokeAddBiasApplyPenalties(\n        step,\n        input_tensors->at(\"logits\").getPtr<T>(),\n        output_tensors->at(\"output_ids\")\n            .getPtrWithOffset<const int>((step - 1) * batch_size * beam_width + ite * local_batch_size * beam_width),\n        output_tensors->getPtr<const int>(\"output_ids\"),\n        output_tensors->getPtr<const int>(\"parent_ids\"),\n        input_tensors->getPtr<const int>(\"input_lengths\", nullptr),\n        output_tensors->getPtr<const int>(\"sequence_length\", nullptr),\n        embedding_bias,\n        ite,\n        input_tensors->getVal<int>(\"max_input_length\"),\n        local_batch_size,\n        batch_size,\n        beam_width,\n        vocab_size_,\n        vocab_size_padded_,\n        input_tensors->getPtr<const int>(\"end_id\", nullptr),\n        temperature,\n        repetition_penalty,\n        repetition_penalty_type,\n        input_tensors->getVal<const int>(\"min_length\", 0),\n        stream_);\n    sync_check_cuda_error();\n\n    invokeSoftMax(output_tensors, input_tensors);\n\n    if (beam_width > 1) {\n        const int max_seq_len = output_tensors->at(\"output_ids\").shape[0];\n\n        update_indir_cache_kernelLauncher(\n            output_tensors->at(\"tgt_cache_indirection\").getPtr<int>(),\n            input_tensors->at(\"src_cache_indirection\").getPtr<const int>(),\n            output_tensors->at(\"parent_ids\")\n                .getPtrWithOffset<const int>(+step * beam_width * batch_size + ite * local_batch_size * beam_width),\n            output_tensors->at(\"finished\").getPtr<const bool>(),\n            batch_size,\n            local_batch_size,\n            beam_width,\n            max_seq_len,\n            step,\n            stream_);\n        sync_check_cuda_error();\n    }\n    sync_check_cuda_error();\n    if (is_free_buffer_after_forward_) {\n        freeBuffer();\n    }\n    sync_check_cuda_error();\n}\n\ntemplate class BaseBeamSearchLayer<float>;\ntemplate class BaseBeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/beam_search_penalty_kernels.h\"\n#include \"src/fastertransformer/layers/beam_search_layers/BaseBeamSearchLayer.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__global__ void update_indir_cache_kernel(int*        tgt_indir_cache,\n                                          const int*  src_indir_cache,\n                                          const int*  beam_ids,\n                                          const bool* finished,\n                                          int         start_step,\n                                          int         batch_dim,\n                                          int         local_batch_size,\n                                          int         beam_width,\n                                          int         max_seq_len,\n                                          int         step)\n{\n    int       time_step = threadIdx.x + blockIdx.x * blockDim.x;\n    int       bb_id     = threadIdx.y + blockIdx.y * blockDim.y;\n    const int batch_id  = bb_id / beam_width;\n    const int beam_id   = bb_id % beam_width;\n\n    if (bb_id >= beam_width * local_batch_size || time_step >= min(step + 1, max_seq_len) || finished[bb_id]) {\n        return;\n    }\n    time_step += start_step;\n    const int time_step_circ = time_step % max_seq_len;\n\n    const int src_beam = beam_ids[batch_id * beam_width + beam_id];\n\n    const uint tgt_offset = batch_id * beam_width * max_seq_len + beam_id * max_seq_len + time_step_circ;\n    const uint src_offset = batch_id * beam_width * max_seq_len + src_beam * max_seq_len + time_step_circ;\n\n    tgt_indir_cache[tgt_offset] = (time_step == step) ? beam_id : src_indir_cache[src_offset];\n}\n\nvoid update_indir_cache_kernelLauncher(int*         tgt_indir_cache,\n                                       const int*   src_indir_cache,\n                                       const int*   beam_ids,\n                                       const bool*  finished,\n                                       int          batch_dim,\n                                       int          local_batch_size,\n                                       int          beam_width,\n                                       int          max_seq_len,\n                                       int          step,\n                                       hipStream_t stream)\n{\n    const dim3 block(32);\n    const int  start_step = max(0, step + 1 - max_seq_len);\n    const int  num_steps  = min(step + 1, max_seq_len);\n    // Update indirections steps [start_step, step], included\n    const dim3 grid((num_steps + block.x - 1) / block.x, local_batch_size * beam_width);\n    update_indir_cache_kernel<<<grid, block, 0, stream>>>(tgt_indir_cache,\n                                                          src_indir_cache,\n                                                          beam_ids,\n                                                          finished,\n                                                          start_step,\n                                                          batch_dim,\n                                                          local_batch_size,\n                                                          beam_width,\n                                                          max_seq_len,\n                                                          step);\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::BaseBeamSearchLayer(size_t           max_batch_size,\n                                            size_t           head_num,\n                                            size_t           size_per_head,\n                                            size_t           beam_width,\n                                            size_t           vocab_size,\n                                            size_t           vocab_size_padded,\n                                            int              end_id,\n                                            float            diversity_rate,\n                                            float            temperature,\n                                            float            len_penalty,\n                                            float            repetition_penalty,\n                                            hipStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward):\n    DynamicDecodeBaseLayer(stream, cublas_wrapper, allocator, is_free_buffer_after_forward, nullptr),\n    vocab_size_(vocab_size),\n    vocab_size_padded_(vocab_size_padded)\n{\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::BaseBeamSearchLayer(BaseBeamSearchLayer<T> const& beam_search_layer):\n    DynamicDecodeBaseLayer(beam_search_layer),\n    vocab_size_(beam_search_layer.vocab_size_),\n    vocab_size_padded_(beam_search_layer.vocab_size_padded_),\n    topk_softmax_workspace_size_(beam_search_layer.topk_softmax_workspace_size_)\n{\n}\n\ntemplate<typename T>\nBaseBeamSearchLayer<T>::~BaseBeamSearchLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::freeBuffer()\n{\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&topk_softmax_workspace_));\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    // do nothing.\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(std::vector<Tensor>* output_tensors, const std::vector<Tensor>* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width]\n    //      ite [1] on cpu\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width]\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width]\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n\n    std::unordered_map<std::string, Tensor> input_tensors_map{{\"logits\", input_tensors->at(0)},\n                                                              {\"embedding_bias\", input_tensors->at(1)},\n                                                              {\"step\", input_tensors->at(2)},\n                                                              {\"src_cache_indirection\", input_tensors->at(4)},\n                                                              {\"max_input_length\", input_tensors->at(5)},\n                                                              {\"input_lengths\", input_tensors->at(6)},\n                                                              {\"ite\", input_tensors->at(7)}};\n\n    std::unordered_map<std::string, Tensor> output_tensors_map{{\"output_ids\", output_tensors->at(0)},\n                                                               {\"finished\", output_tensors->at(1)},\n                                                               {\"cum_log_probs\", output_tensors->at(2)},\n                                                               {\"parent_ids\", output_tensors->at(3)},\n                                                               {\"sequence_length\", output_tensors->at(4)},\n                                                               {\"tgt_cache_indirection\", output_tensors->at(5)}};\n    forward(&output_tensors_map, &input_tensors_map);\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(std::unordered_map<std::string, Tensor>*       output_tensors,\n                                     const std::unordered_map<std::string, Tensor>* input_tensors)\n{\n    TensorMap input_map(*input_tensors);\n    TensorMap output_map(*output_tensors);\n    forward(&output_map, &input_map);\n}\n\ntemplate<typename T>\nvoid BaseBeamSearchLayer<T>::forward(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, beam_width, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded]\n    //      step [1] on cpu\n    //      src_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      end_id [local_batch_size]\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size * beam_width], optional\n    //      ite [1] on cpu\n    //      beam_search_diversity_rate [1] on cpu, optional\n    //      temperature [1] on cpu, optional\n    //      len_penalty [1] on cpu, optional\n    //      repetition_penalty [1] on cpu, optional\n    //      presence_penalty [1] on cpu, optional\n    //          Only one of repetition and presence penalties is allowed.\n    //      min_length [1] on cpu, int, optional\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size, beam_width]\n    //      finished [local_batch_size * beam_width], optional\n    //      cum_log_probs [local_batch_size * beam_width]\n    //      parent_ids [max_seq_len, batch_size * beam_width]\n    //      sequence_length [local_batch_size * beam_width], optional\n    //      tgt_cache_indirection [local_batch_size, beam_width, max_seq_len]\n    //      output_log_probs [max_seq_len, batch_size, beam_width], optional\n    //      beam_hyps, optional\n\n    FT_CHECK(input_tensors->size() >= 7);\n    FT_CHECK(output_tensors->size() >= 5);\n    const int batch_size = output_tensors->at(\"output_ids\").shape[1];\n    const int beam_width = output_tensors->at(\"output_ids\").shape[2];\n    allocateBuffer(batch_size, beam_width);\n\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n\n    const float temperature    = input_tensors->getVal<float>(\"temperature\", 1.0f);\n    const T*    embedding_bias = input_tensors->getPtr<const T>(\"embedding_bias\", nullptr);\n\n    RepetitionPenaltyType repetition_penalty_type = RepetitionPenaltyType::None;\n    float                 repetition_penalty      = getDefaultPenaltyValue(repetition_penalty_type);\n    if (input_tensors->isExist(\"repetition_penalty\") || input_tensors->isExist(\"presence_penalty\")) {\n        FT_CHECK_WITH_INFO(\n            !(input_tensors->isExist(\"repetition_penalty\") && input_tensors->isExist(\"presence_penalty\")),\n            \"Found ambiguous parameters repetition_penalty and presence_penalty which are mutually exclusive. \"\n            \"Please provide one of repetition_penalty or presence_penalty.\");\n        repetition_penalty_type = input_tensors->isExist(\"repetition_penalty\") ? RepetitionPenaltyType::Multiplicative :\n                                                                                 RepetitionPenaltyType::Additive;\n        repetition_penalty      = repetition_penalty_type == RepetitionPenaltyType::Multiplicative ?\n                                      input_tensors->getVal<float>(\"repetition_penalty\") :\n                                      input_tensors->getVal<float>(\"presence_penalty\");\n    }\n\n    invokeAddBiasApplyPenalties(\n        step,\n        input_tensors->at(\"logits\").getPtr<T>(),\n        output_tensors->at(\"output_ids\")\n            .getPtrWithOffset<const int>((step - 1) * batch_size * beam_width + ite * local_batch_size * beam_width),\n        output_tensors->getPtr<const int>(\"output_ids\"),\n        output_tensors->getPtr<const int>(\"parent_ids\"),\n        input_tensors->getPtr<const int>(\"input_lengths\", nullptr),\n        output_tensors->getPtr<const int>(\"sequence_length\", nullptr),\n        embedding_bias,\n        ite,\n        input_tensors->getVal<int>(\"max_input_length\"),\n        local_batch_size,\n        batch_size,\n        beam_width,\n        vocab_size_,\n        vocab_size_padded_,\n        input_tensors->getPtr<const int>(\"end_id\", nullptr),\n        temperature,\n        repetition_penalty,\n        repetition_penalty_type,\n        input_tensors->getVal<const int>(\"min_length\", 0),\n        stream_);\n    sync_check_cuda_error();\n\n    invokeSoftMax(output_tensors, input_tensors);\n\n    if (beam_width > 1) {\n        const int max_seq_len = output_tensors->at(\"output_ids\").shape[0];\n\n        update_indir_cache_kernelLauncher(\n            output_tensors->at(\"tgt_cache_indirection\").getPtr<int>(),\n            input_tensors->at(\"src_cache_indirection\").getPtr<const int>(),\n            output_tensors->at(\"parent_ids\")\n                .getPtrWithOffset<const int>(+step * beam_width * batch_size + ite * local_batch_size * beam_width),\n            output_tensors->at(\"finished\").getPtr<const bool>(),\n            batch_size,\n            local_batch_size,\n            beam_width,\n            max_seq_len,\n            step,\n            stream_);\n        sync_check_cuda_error();\n    }\n    sync_check_cuda_error();\n    if (is_free_buffer_after_forward_) {\n        freeBuffer();\n    }\n    sync_check_cuda_error();\n}\n\ntemplate class BaseBeamSearchLayer<float>;\ntemplate class BaseBeamSearchLayer<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/beam_search_layers/BaseBeamSearchLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopPSamplingLayer.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\nstatic __global__ void set_topp_runtime_args(int             batch_size,\n                                             uint            top_k,\n                                             uint*           top_ks,\n                                             int             top_ks_size,\n                                             float           top_p,\n                                             float*          top_ps,\n                                             int             top_ps_size,\n                                             bool*           skip_decode,\n                                             float*          initial_top_p_buf,\n                                             float*          top_p_decay_buf,\n                                             const float*    top_p_decay,\n                                             float*          top_p_min_buf,\n                                             const float*    top_p_min,\n                                             int32_t*        top_p_reset_ids_buf,\n                                             const uint32_t* top_p_reset_ids)\n{\n    /**\n     * @brief Setup the runtime arguments for topp, broadcasting top_p to top_ps\n                and top_k to top_ks, copying top_p_decay/top_p_min/top_p_reset_ids\n                to internal buffers.\n     *\n     * \\param batch_size            [batch_size]\n     * \\param op_k                  [batch_size]\n     * \\param top_ks                [batch_size]\n     * \\param top_ks_size           [batch_size]\n     * \\param top_p                 [batch_size]\n     * \\param top_ps                [batch_size]\n     * \\param top_ps_size           [batch_size]\n     * \\param skip_decode           [batch_size]\n     * \\param initial_top_p_buf     [batch_size]\n     * \\param top_p_decay_buf       [batch_size]\n     * \\param top_p_decay           [batch_size], optional, must between [0, 1]\n     * \\param top_p_min_buf         [batch_size]\n     * \\param top_p_min             [batch_size], optional, must between [0, 1]\n     * \\param top_p_reset_ids_buf    [batch_size]\n     * \\param top_p_reset_ids        [batch_size], optional\n     *\n     */\n\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = index; i < batch_size; i += gridDim.x * blockDim.x) {\n        uint  k = top_ks_size > 1 ? top_ks[i] : top_k;\n        float p = top_ps_size > 1 ? top_ps[i] : top_p;\n        if (k == 0 && p == 0.0f) {\n            // FT's topp implementation does not support topp = 0.0f, but it equivalent to greedy search.\n            // So, we set the topk = 1 as an alternative solution.\n            k = 1;\n        }\n        top_ks[i] = k;\n        // Clip p value if it is out of range. range = [0.0, 1.0].\n        top_ps[i] = p < 0.0f ? 0.0f : (p > 1.0f ? 1.0f : p);\n        if (p < 0.0f || p > 1.0f) {\n            printf(\"[WARNING] topp (%f) is out of range ([0.0, 1.0f]) for token %d\"\n                   \" clip to closest number %f.\\n\",\n                   p,\n                   i,\n                   top_ps[i]);\n        }\n        skip_decode[i] = k > 0;\n\n        initial_top_p_buf[i] = top_ps[i];\n        top_p_decay_buf[i]   = top_p_decay == nullptr ? 1.0f : top_p_decay[i];\n        if (top_p_decay_buf[i] > 1.0f || top_p_decay_buf[i] <= 0.0f) {\n            printf(\"[WARNING] top_p_decay_buf (%f) is out of range ([0.0, 1.0f]) for token %d,\"\n                   \" change to 1.0f.\\n\",\n                   top_p_decay_buf[i],\n                   i);\n            top_p_decay_buf[i] = 1.0f;\n        }\n        top_p_min_buf[i] = top_p_min == nullptr ? 1e-6f : top_p_min[i];  // prevent topp becoming 0.0\n        if (top_p_min_buf[i] > 1.0f || top_p_min_buf[i] <= 0.0f) {\n            printf(\"[WARNING] top_p_min_buf (%f) is out of range ([0.0, 1.0f]) for token %d,\"\n                   \" change to 0.5f.\\n\",\n                   top_p_min_buf[i],\n                   i);\n            top_p_min_buf[i] = 0.5f;\n        }\n        top_p_reset_ids_buf[i] = (int32_t)(top_p_reset_ids == nullptr ? -1 : top_p_reset_ids[i]);\n    }\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::allocateBuffer(size_t batch_size, Tensor top_k, Tensor top_p)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::allocateBuffer(batch_size, top_k, top_p);\n    invokeTopPSampling<T>(nullptr,  // workspace\n                          sampling_workspace_size_,\n                          cub_temp_storage_size_,\n                          nullptr,  // output_ids\n                          nullptr,  // sequence_length\n                          nullptr,  // finished_buffer\n                          nullptr,  // cum_log_probs\n                          nullptr,  // output_log_probs\n                          nullptr,  // log_probs\n                          topp_id_vals_buf_,\n                          topp_offset_buf_,\n                          begin_topp_offset_buf_,\n                          curandstate_buf_,\n                          batch_size,\n                          vocab_size_padded_,\n                          nullptr,\n                          top_p.size() > 0 ? top_p.max<float>() : 0.0f,\n                          stream_,\n                          cuda_device_prop_,\n                          skip_decode_buf_);\n    sampling_workspace_ = allocator_->reMalloc(sampling_workspace_, sampling_workspace_size_, true);\n    runtime_top_k_buf_ =\n        reinterpret_cast<uint*>(allocator_->reMalloc(runtime_top_k_buf_, sizeof(uint) * batch_size, false));\n    runtime_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(runtime_top_p_buf_, sizeof(float) * batch_size, false));\n    initial_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(initial_top_p_buf_, sizeof(float) * batch_size, false));\n    top_p_decay_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(top_p_decay_buf_, sizeof(float) * batch_size, false));\n    top_p_min_buf_ = reinterpret_cast<float*>(allocator_->reMalloc(top_p_min_buf_, sizeof(float) * batch_size, false));\n    top_p_reset_ids_buf_ =\n        reinterpret_cast<int32_t*>(allocator_->reMalloc(top_p_reset_ids_buf_, sizeof(int32_t) * batch_size, false));\n    topp_id_vals_buf_ = reinterpret_cast<int*>(\n        allocator_->reMalloc(topp_id_vals_buf_, sizeof(int) * batch_size * vocab_size_padded_, false));\n    topp_offset_buf_ =\n        reinterpret_cast<int*>(allocator_->reMalloc(topp_offset_buf_, sizeof(int) * (batch_size + 1), false));\n    begin_topp_offset_buf_ =\n        reinterpret_cast<int*>(allocator_->reMalloc(begin_topp_offset_buf_, sizeof(int) * (batch_size + 1), false));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&sampling_workspace_));\n        allocator_->free((void**)(&topp_id_vals_buf_));\n        allocator_->free((void**)(&topp_offset_buf_));\n        allocator_->free((void**)(&begin_topp_offset_buf_));\n        allocator_->free((void**)(&runtime_top_k_buf_));\n        allocator_->free((void**)(&runtime_top_p_buf_));\n        allocator_->free((void**)(&initial_top_p_buf_));\n        allocator_->free((void**)(&top_p_decay_buf_));\n        allocator_->free((void**)(&top_p_min_buf_));\n        allocator_->free((void**)(&top_p_reset_ids_buf_));\n    }\n    BaseSamplingLayer<T>::freeBuffer();\n    is_allocate_buffer_ = false;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    /**\n    * @brief Set up the sampling layer for given runtime arguments.\n\n    * runtime_args:\n    *   \\param  runtime_top_k [1] or [batch_size] on cpu, optional.\n    *   \\param  runtime_top_p [1] or [batch_size] on cpu, optional\n    *   \\param  temperature [1] or [batch_size] on cpu, optional\n    *   \\param  repetition_penalty [1] or [batch_size] on cpu, optional\n    *   \\param  top_p_decay [batch_size] on gpu, float, optional\n    *   \\param  top_p_min [batch_size] on gpu, float, optional\n    *   \\param  top_p_reset_ids [batch_size] on gpu, uint32, optional\n    **/\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::setup(batch_size, beam_width, runtime_args);\n    const Tensor runtime_top_p = runtime_args->isExist(\"runtime_top_p\") ? runtime_args->at(\"runtime_top_p\") : Tensor();\n    const size_t runtime_top_p_size = runtime_top_p.size();\n    if (runtime_top_p_size == 0) {\n        std::fill_n(skip_decode_, batch_size, true);\n        return;\n    }\n\n    uint         tmp_top_k          = 0;\n    const Tensor runtime_top_k      = runtime_args->isExist(\"runtime_top_k\") ?\n                                          runtime_args->at(\"runtime_top_k\") :\n                                          Tensor(MEMORY_CPU, TYPE_UINT32, {1}, &tmp_top_k);\n    const size_t runtime_top_k_size = runtime_top_k.size();\n\n    uint  top_k = runtime_top_k.getVal<uint>();\n    float top_p = runtime_top_p.getVal<float>();\n\n    if (runtime_top_k_size > 1) {\n        FT_CHECK(runtime_top_k.size() == batch_size);\n        cudaH2Dcpy(runtime_top_k_buf_, runtime_top_k.getPtr<uint>(), batch_size);\n    }\n    if (runtime_top_p_size > 1) {\n        FT_CHECK(runtime_top_p.size() == batch_size);\n        cudaH2Dcpy(runtime_top_p_buf_, runtime_top_p.getPtr<float>(), batch_size);\n    }\n\n    dim3 block(std::min((int)batch_size, 256));\n    dim3 grid(div_up((int)batch_size, (int)block.x));\n\n    const float*    top_p_decay     = runtime_args->getPtr<float>(\"top_p_decay\", nullptr);\n    const float*    top_p_min       = runtime_args->getPtr<float>(\"top_p_min\", nullptr);\n    const uint32_t* top_p_reset_ids = runtime_args->getPtr<uint32_t>(\"top_p_reset_ids\", nullptr);\n    set_topp_runtime_args<<<grid, block, 0, stream_>>>(batch_size,\n                                                       top_k,\n                                                       runtime_top_k_buf_,\n                                                       runtime_top_k_size,\n                                                       top_p,\n                                                       runtime_top_p_buf_,\n                                                       runtime_top_p_size,\n                                                       skip_decode_buf_,\n                                                       initial_top_p_buf_,\n                                                       top_p_decay_buf_,\n                                                       top_p_decay,\n                                                       top_p_min_buf_,\n                                                       top_p_min,\n                                                       top_p_reset_ids_buf_,\n                                                       top_p_reset_ids);\n    sync_check_cuda_error();\n    cudaAutoCpy(skip_decode_, skip_decode_buf_, batch_size, stream_);\n    float* runtime_top_ps = new float[batch_size];\n    cudaAutoCpy(runtime_top_ps, runtime_top_p_buf_, batch_size, stream_);\n    runtime_max_top_p_ = *std::max_element(runtime_top_ps, runtime_top_ps + batch_size);\n    delete[] runtime_top_ps;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::runSampling(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    /**\n    * input_tensors:\n    *   \\param  logits [local_batch_size, vocab_size_padded]\n    *   \\param  embedding_bias [vocab_size_padded], optional\n    *   \\param  step [1] on cpu\n    *   \\param  max_input_length [1] on cpu\n    *   \\param  input_lengths [local_batch_size], optional\n    *   \\param  ite [1] on cpu\n\n    * output_tensors:\n    *   \\param  output_ids [max_seq_len, batch_size]\n    *   \\param  finished [local_batch_size], optional\n    *   \\param  sequence_length [local_batch_size], optional\n    *   \\param  cum_log_probs [batch_size], must be float*, optional\n    *   \\param  The cumultative log probability of generated tokens.\n    *   \\param  output_log_probs [local_batch_size], must be float*, optional\n                    log probs at the current step.\n    **/\n\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    FT_CHECK(input_tensors->size() >= 4);\n    FT_CHECK(output_tensors->size() >= 1);\n\n    const int batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n\n    // in case of skip any, the logit value is already copied and processed.\n    T* logits = !skip_any_ ? input_tensors->at(\"logits\").getPtr<T>() : runtime_logits_buf_;\n\n    invokeTopPInitialize(\n        topp_id_vals_buf_, topp_offset_buf_, begin_topp_offset_buf_, local_batch_size, vocab_size_padded_, stream_);\n    sync_check_cuda_error();\n\n    invokeAddBiasSoftMax(logits,\n                         (T*)(nullptr),\n                         input_tensors->at(\"end_id\").getPtr<int>(),\n                         output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n                         local_batch_size,\n                         vocab_size_padded_,\n                         vocab_size_,\n                         stream_);\n    sync_check_cuda_error();\n\n    float* cum_log_probs =\n        output_tensors->isExist(\"cum_log_probs\") ? output_tensors->at(\"cum_log_probs\").getPtr<float>() : nullptr;\n    float* output_log_probs =\n        output_tensors->isExist(\"output_log_probs\") ? output_tensors->at(\"output_log_probs\").getPtr<float>() : nullptr;\n\n    invokeBatchTopPSampling<T>(\n        sampling_workspace_,\n        sampling_workspace_size_,\n        cub_temp_storage_size_,\n        output_tensors->at(\"output_ids\").getPtrWithOffset<int>(step * batch_size + ite * local_batch_size),\n        output_tensors->at(\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<int>(),\n        output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n        cum_log_probs,\n        output_log_probs,\n        logits,\n        topp_id_vals_buf_,\n        topp_offset_buf_,\n        begin_topp_offset_buf_,\n        curandstate_buf_ + ite * local_batch_size,\n        local_batch_size,\n        vocab_size_padded_,\n        input_tensors->at(\"end_id\").getPtr<int>(),\n        runtime_max_top_p_,\n        runtime_top_p_buf_ + ite * local_batch_size,\n        stream_,\n        cuda_device_prop_,\n        skip_decode_buf_ + ite * local_batch_size);\n    sync_check_cuda_error();\n\n    invokeComputeToppDecay(\n        runtime_top_p_buf_ + ite * local_batch_size,\n        initial_top_p_buf_ + ite * local_batch_size,\n        output_tensors->getPtrWithOffset<int>(\"output_ids\", step * batch_size + ite * local_batch_size),\n        top_p_decay_buf_ + ite * local_batch_size,\n        top_p_min_buf_ + ite * local_batch_size,\n        top_p_reset_ids_buf_ + ite * local_batch_size,\n        local_batch_size,\n        stream_);\n    sync_check_cuda_error();\n    FT_LOG_DEBUG(\"%s stop\", __PRETTY_FUNCTION__);\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::TopPSamplingLayer(size_t             max_batch_size,\n                                        size_t             vocab_size,\n                                        size_t             vocab_size_padded,\n                                        int                end_id,\n                                        float              top_p,\n                                        unsigned long long random_seed,\n                                        float              temperature,\n                                        float              len_penalty,\n                                        float              repetition_penalty,\n                                        cudaStream_t       stream,\n                                        cublasMMWrapper*   cublas_wrapper,\n                                        IAllocator*        allocator,\n                                        bool               is_free_buffer_after_forward,\n                                        cudaDeviceProp*    cuda_device_prop):\n    BaseSamplingLayer<T>(max_batch_size,\n                         vocab_size,\n                         vocab_size_padded,\n                         end_id,\n                         0,\n                         top_p,\n                         random_seed,\n                         temperature,\n                         len_penalty,\n                         repetition_penalty,\n                         stream,\n                         cublas_wrapper,\n                         allocator,\n                         is_free_buffer_after_forward,\n                         cuda_device_prop)\n{\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::TopPSamplingLayer(TopPSamplingLayer<T> const& top_p_sampling_layer):\n    BaseSamplingLayer<T>(top_p_sampling_layer)\n{\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::~TopPSamplingLayer()\n{\n    freeBuffer();\n}\n\ntemplate class TopPSamplingLayer<float>;\ntemplate class TopPSamplingLayer<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopPSamplingLayer.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\nstatic __global__ void set_topp_runtime_args(int             batch_size,\n                                             uint            top_k,\n                                             uint*           top_ks,\n                                             int             top_ks_size,\n                                             float           top_p,\n                                             float*          top_ps,\n                                             int             top_ps_size,\n                                             bool*           skip_decode,\n                                             float*          initial_top_p_buf,\n                                             float*          top_p_decay_buf,\n                                             const float*    top_p_decay,\n                                             float*          top_p_min_buf,\n                                             const float*    top_p_min,\n                                             int32_t*        top_p_reset_ids_buf,\n                                             const uint32_t* top_p_reset_ids)\n{\n    /**\n     * @brief Setup the runtime arguments for topp, broadcasting top_p to top_ps\n                and top_k to top_ks, copying top_p_decay/top_p_min/top_p_reset_ids\n                to internal buffers.\n     *\n     * \\param batch_size            [batch_size]\n     * \\param op_k                  [batch_size]\n     * \\param top_ks                [batch_size]\n     * \\param top_ks_size           [batch_size]\n     * \\param top_p                 [batch_size]\n     * \\param top_ps                [batch_size]\n     * \\param top_ps_size           [batch_size]\n     * \\param skip_decode           [batch_size]\n     * \\param initial_top_p_buf     [batch_size]\n     * \\param top_p_decay_buf       [batch_size]\n     * \\param top_p_decay           [batch_size], optional, must between [0, 1]\n     * \\param top_p_min_buf         [batch_size]\n     * \\param top_p_min             [batch_size], optional, must between [0, 1]\n     * \\param top_p_reset_ids_buf    [batch_size]\n     * \\param top_p_reset_ids        [batch_size], optional\n     *\n     */\n\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = index; i < batch_size; i += gridDim.x * blockDim.x) {\n        uint  k = top_ks_size > 1 ? top_ks[i] : top_k;\n        float p = top_ps_size > 1 ? top_ps[i] : top_p;\n        if (k == 0 && p == 0.0f) {\n            // FT's topp implementation does not support topp = 0.0f, but it equivalent to greedy search.\n            // So, we set the topk = 1 as an alternative solution.\n            k = 1;\n        }\n        top_ks[i] = k;\n        // Clip p value if it is out of range. range = [0.0, 1.0].\n        top_ps[i] = p < 0.0f ? 0.0f : (p > 1.0f ? 1.0f : p);\n        if (p < 0.0f || p > 1.0f) {\n            printf(\"[WARNING] topp (%f) is out of range ([0.0, 1.0f]) for token %d\"\n                   \" clip to closest number %f.\\n\",\n                   p,\n                   i,\n                   top_ps[i]);\n        }\n        skip_decode[i] = k > 0;\n\n        initial_top_p_buf[i] = top_ps[i];\n        top_p_decay_buf[i]   = top_p_decay == nullptr ? 1.0f : top_p_decay[i];\n        if (top_p_decay_buf[i] > 1.0f || top_p_decay_buf[i] <= 0.0f) {\n            printf(\"[WARNING] top_p_decay_buf (%f) is out of range ([0.0, 1.0f]) for token %d,\"\n                   \" change to 1.0f.\\n\",\n                   top_p_decay_buf[i],\n                   i);\n            top_p_decay_buf[i] = 1.0f;\n        }\n        top_p_min_buf[i] = top_p_min == nullptr ? 1e-6f : top_p_min[i];  // prevent topp becoming 0.0\n        if (top_p_min_buf[i] > 1.0f || top_p_min_buf[i] <= 0.0f) {\n            printf(\"[WARNING] top_p_min_buf (%f) is out of range ([0.0, 1.0f]) for token %d,\"\n                   \" change to 0.5f.\\n\",\n                   top_p_min_buf[i],\n                   i);\n            top_p_min_buf[i] = 0.5f;\n        }\n        top_p_reset_ids_buf[i] = (int32_t)(top_p_reset_ids == nullptr ? -1 : top_p_reset_ids[i]);\n    }\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::allocateBuffer(size_t batch_size, Tensor top_k, Tensor top_p)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::allocateBuffer(batch_size, top_k, top_p);\n    invokeTopPSampling<T>(nullptr,  // workspace\n                          sampling_workspace_size_,\n                          cub_temp_storage_size_,\n                          nullptr,  // output_ids\n                          nullptr,  // sequence_length\n                          nullptr,  // finished_buffer\n                          nullptr,  // cum_log_probs\n                          nullptr,  // output_log_probs\n                          nullptr,  // log_probs\n                          topp_id_vals_buf_,\n                          topp_offset_buf_,\n                          begin_topp_offset_buf_,\n                          curandstate_buf_,\n                          batch_size,\n                          vocab_size_padded_,\n                          nullptr,\n                          top_p.size() > 0 ? top_p.max<float>() : 0.0f,\n                          stream_,\n                          cuda_device_prop_,\n                          skip_decode_buf_);\n    sampling_workspace_ = allocator_->reMalloc(sampling_workspace_, sampling_workspace_size_, true);\n    runtime_top_k_buf_ =\n        reinterpret_cast<uint*>(allocator_->reMalloc(runtime_top_k_buf_, sizeof(uint) * batch_size, false));\n    runtime_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(runtime_top_p_buf_, sizeof(float) * batch_size, false));\n    initial_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(initial_top_p_buf_, sizeof(float) * batch_size, false));\n    top_p_decay_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(top_p_decay_buf_, sizeof(float) * batch_size, false));\n    top_p_min_buf_ = reinterpret_cast<float*>(allocator_->reMalloc(top_p_min_buf_, sizeof(float) * batch_size, false));\n    top_p_reset_ids_buf_ =\n        reinterpret_cast<int32_t*>(allocator_->reMalloc(top_p_reset_ids_buf_, sizeof(int32_t) * batch_size, false));\n    topp_id_vals_buf_ = reinterpret_cast<int*>(\n        allocator_->reMalloc(topp_id_vals_buf_, sizeof(int) * batch_size * vocab_size_padded_, false));\n    topp_offset_buf_ =\n        reinterpret_cast<int*>(allocator_->reMalloc(topp_offset_buf_, sizeof(int) * (batch_size + 1), false));\n    begin_topp_offset_buf_ =\n        reinterpret_cast<int*>(allocator_->reMalloc(begin_topp_offset_buf_, sizeof(int) * (batch_size + 1), false));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&sampling_workspace_));\n        allocator_->free((void**)(&topp_id_vals_buf_));\n        allocator_->free((void**)(&topp_offset_buf_));\n        allocator_->free((void**)(&begin_topp_offset_buf_));\n        allocator_->free((void**)(&runtime_top_k_buf_));\n        allocator_->free((void**)(&runtime_top_p_buf_));\n        allocator_->free((void**)(&initial_top_p_buf_));\n        allocator_->free((void**)(&top_p_decay_buf_));\n        allocator_->free((void**)(&top_p_min_buf_));\n        allocator_->free((void**)(&top_p_reset_ids_buf_));\n    }\n    BaseSamplingLayer<T>::freeBuffer();\n    is_allocate_buffer_ = false;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    /**\n    * @brief Set up the sampling layer for given runtime arguments.\n\n    * runtime_args:\n    *   \\param  runtime_top_k [1] or [batch_size] on cpu, optional.\n    *   \\param  runtime_top_p [1] or [batch_size] on cpu, optional\n    *   \\param  temperature [1] or [batch_size] on cpu, optional\n    *   \\param  repetition_penalty [1] or [batch_size] on cpu, optional\n    *   \\param  top_p_decay [batch_size] on gpu, float, optional\n    *   \\param  top_p_min [batch_size] on gpu, float, optional\n    *   \\param  top_p_reset_ids [batch_size] on gpu, uint32, optional\n    **/\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::setup(batch_size, beam_width, runtime_args);\n    const Tensor runtime_top_p = runtime_args->isExist(\"runtime_top_p\") ? runtime_args->at(\"runtime_top_p\") : Tensor();\n    const size_t runtime_top_p_size = runtime_top_p.size();\n    if (runtime_top_p_size == 0) {\n        std::fill_n(skip_decode_, batch_size, true);\n        return;\n    }\n\n    uint         tmp_top_k          = 0;\n    const Tensor runtime_top_k      = runtime_args->isExist(\"runtime_top_k\") ?\n                                          runtime_args->at(\"runtime_top_k\") :\n                                          Tensor(MEMORY_CPU, TYPE_UINT32, {1}, &tmp_top_k);\n    const size_t runtime_top_k_size = runtime_top_k.size();\n\n    uint  top_k = runtime_top_k.getVal<uint>();\n    float top_p = runtime_top_p.getVal<float>();\n\n    if (runtime_top_k_size > 1) {\n        FT_CHECK(runtime_top_k.size() == batch_size);\n        cudaH2Dcpy(runtime_top_k_buf_, runtime_top_k.getPtr<uint>(), batch_size);\n    }\n    if (runtime_top_p_size > 1) {\n        FT_CHECK(runtime_top_p.size() == batch_size);\n        cudaH2Dcpy(runtime_top_p_buf_, runtime_top_p.getPtr<float>(), batch_size);\n    }\n\n    dim3 block(std::min((int)batch_size, 256));\n    dim3 grid(div_up((int)batch_size, (int)block.x));\n\n    const float*    top_p_decay     = runtime_args->getPtr<float>(\"top_p_decay\", nullptr);\n    const float*    top_p_min       = runtime_args->getPtr<float>(\"top_p_min\", nullptr);\n    const uint32_t* top_p_reset_ids = runtime_args->getPtr<uint32_t>(\"top_p_reset_ids\", nullptr);\n    set_topp_runtime_args<<<grid, block, 0, stream_>>>(batch_size,\n                                                       top_k,\n                                                       runtime_top_k_buf_,\n                                                       runtime_top_k_size,\n                                                       top_p,\n                                                       runtime_top_p_buf_,\n                                                       runtime_top_p_size,\n                                                       skip_decode_buf_,\n                                                       initial_top_p_buf_,\n                                                       top_p_decay_buf_,\n                                                       top_p_decay,\n                                                       top_p_min_buf_,\n                                                       top_p_min,\n                                                       top_p_reset_ids_buf_,\n                                                       top_p_reset_ids);\n    sync_check_cuda_error();\n    cudaAutoCpy(skip_decode_, skip_decode_buf_, batch_size, stream_);\n    float* runtime_top_ps = new float[batch_size];\n    cudaAutoCpy(runtime_top_ps, runtime_top_p_buf_, batch_size, stream_);\n    runtime_max_top_p_ = *std::max_element(runtime_top_ps, runtime_top_ps + batch_size);\n    delete[] runtime_top_ps;\n}\n\ntemplate<typename T>\nvoid TopPSamplingLayer<T>::runSampling(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    /**\n    * input_tensors:\n    *   \\param  logits [local_batch_size, vocab_size_padded]\n    *   \\param  embedding_bias [vocab_size_padded], optional\n    *   \\param  step [1] on cpu\n    *   \\param  max_input_length [1] on cpu\n    *   \\param  input_lengths [local_batch_size], optional\n    *   \\param  ite [1] on cpu\n\n    * output_tensors:\n    *   \\param  output_ids [max_seq_len, batch_size]\n    *   \\param  finished [local_batch_size], optional\n    *   \\param  sequence_length [local_batch_size], optional\n    *   \\param  cum_log_probs [batch_size], must be float*, optional\n    *   \\param  The cumultative log probability of generated tokens.\n    *   \\param  output_log_probs [local_batch_size], must be float*, optional\n                    log probs at the current step.\n    **/\n\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    FT_CHECK(input_tensors->size() >= 4);\n    FT_CHECK(output_tensors->size() >= 1);\n\n    const int batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n\n    // in case of skip any, the logit value is already copied and processed.\n    T* logits = !skip_any_ ? input_tensors->at(\"logits\").getPtr<T>() : runtime_logits_buf_;\n\n    invokeTopPInitialize(\n        topp_id_vals_buf_, topp_offset_buf_, begin_topp_offset_buf_, local_batch_size, vocab_size_padded_, stream_);\n    sync_check_cuda_error();\n\n    invokeAddBiasSoftMax(logits,\n                         (T*)(nullptr),\n                         input_tensors->at(\"end_id\").getPtr<int>(),\n                         output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n                         local_batch_size,\n                         vocab_size_padded_,\n                         vocab_size_,\n                         stream_);\n    sync_check_cuda_error();\n\n    float* cum_log_probs =\n        output_tensors->isExist(\"cum_log_probs\") ? output_tensors->at(\"cum_log_probs\").getPtr<float>() : nullptr;\n    float* output_log_probs =\n        output_tensors->isExist(\"output_log_probs\") ? output_tensors->at(\"output_log_probs\").getPtr<float>() : nullptr;\n\n    invokeBatchTopPSampling<T>(\n        sampling_workspace_,\n        sampling_workspace_size_,\n        cub_temp_storage_size_,\n        output_tensors->at(\"output_ids\").getPtrWithOffset<int>(step * batch_size + ite * local_batch_size),\n        output_tensors->at(\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<int>(),\n        output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n        cum_log_probs,\n        output_log_probs,\n        logits,\n        topp_id_vals_buf_,\n        topp_offset_buf_,\n        begin_topp_offset_buf_,\n        curandstate_buf_ + ite * local_batch_size,\n        local_batch_size,\n        vocab_size_padded_,\n        input_tensors->at(\"end_id\").getPtr<int>(),\n        runtime_max_top_p_,\n        runtime_top_p_buf_ + ite * local_batch_size,\n        stream_,\n        cuda_device_prop_,\n        skip_decode_buf_ + ite * local_batch_size);\n    sync_check_cuda_error();\n\n    invokeComputeToppDecay(\n        runtime_top_p_buf_ + ite * local_batch_size,\n        initial_top_p_buf_ + ite * local_batch_size,\n        output_tensors->getPtrWithOffset<int>(\"output_ids\", step * batch_size + ite * local_batch_size),\n        top_p_decay_buf_ + ite * local_batch_size,\n        top_p_min_buf_ + ite * local_batch_size,\n        top_p_reset_ids_buf_ + ite * local_batch_size,\n        local_batch_size,\n        stream_);\n    sync_check_cuda_error();\n    FT_LOG_DEBUG(\"%s stop\", __PRETTY_FUNCTION__);\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::TopPSamplingLayer(size_t             max_batch_size,\n                                        size_t             vocab_size,\n                                        size_t             vocab_size_padded,\n                                        int                end_id,\n                                        float              top_p,\n                                        unsigned long long random_seed,\n                                        float              temperature,\n                                        float              len_penalty,\n                                        float              repetition_penalty,\n                                        hipStream_t       stream,\n                                        cublasMMWrapper*   cublas_wrapper,\n                                        IAllocator*        allocator,\n                                        bool               is_free_buffer_after_forward,\n                                        hipDeviceProp_t*    cuda_device_prop):\n    BaseSamplingLayer<T>(max_batch_size,\n                         vocab_size,\n                         vocab_size_padded,\n                         end_id,\n                         0,\n                         top_p,\n                         random_seed,\n                         temperature,\n                         len_penalty,\n                         repetition_penalty,\n                         stream,\n                         cublas_wrapper,\n                         allocator,\n                         is_free_buffer_after_forward,\n                         cuda_device_prop)\n{\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::TopPSamplingLayer(TopPSamplingLayer<T> const& top_p_sampling_layer):\n    BaseSamplingLayer<T>(top_p_sampling_layer)\n{\n}\n\ntemplate<typename T>\nTopPSamplingLayer<T>::~TopPSamplingLayer()\n{\n    freeBuffer();\n}\n\ntemplate class TopPSamplingLayer<float>;\ntemplate class TopPSamplingLayer<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/sampling_layers/TopPSamplingLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<uint TOP_K_MAX>\n__global__ void setup_topk_runtime_args(int    batch_size,\n                                        uint   top_k,\n                                        uint*  top_ks,\n                                        int    top_ks_size,\n                                        float  top_p,\n                                        float* top_ps,\n                                        int    top_ps_size,\n                                        bool*  skip_decode)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = index; i < batch_size; i += gridDim.x * blockDim.x) {\n        uint  k = top_ks_size > 1 ? top_ks[i] : top_k;\n        float p = top_ps_size > 1 ? top_ps[i] : top_p;\n        if (k == 0 && p == 0.0f) {\n            // FT's topp implementation does not support topp = 0.0f, but it equivalent to greedy search.\n            // So, we set the topk = 1 as an alternative solution.\n            k = 1;\n        }\n        if (k > 0 && p == 0.0f) {\n            // for compatibility <= FT5.0.\n            // This case corresponds to the old topk sampling, which is equivalent to\n            // the old topk_topp sampling with topp=1.0f. TopKSamplingLayer and\n            // TopKTopPSamplingLayer are now merged by TopKSamplingLayer. Thus, we\n            // replace the case topk>0 and topp=0.0f by topk>0 and topp=1.0f for the\n            // compatibility.\n            p = 1.0f;\n        }\n        // Clip k value. A topk sampling kernel supports up to TOP_K_MAX=64.\n        top_ks[i] = k > TOP_K_MAX ? TOP_K_MAX : k;\n        if (k > TOP_K_MAX) {\n            printf(\"[WARNING] topk (%d) is larger than max supported number (%d) for token %d\"\n                   \" clip to max supported number %d. \\n\",\n                   k,\n                   TOP_K_MAX,\n                   i,\n                   top_ks[i]);\n        }\n        // Clip p value if it is out of range. range = [0.0, 1.0].\n        top_ps[i] = p < 0.0f ? 0.0f : (p > 1.0f ? 1.0f : p);\n        if (p < 0.0f || p > 1.0f) {\n            printf(\"[WARNING] topp (%f) is out of range ([0.0, 1.0f]) for token %d\"\n                   \" clip to closest number %f.\\n\",\n                   p,\n                   i,\n                   top_ps[i]);\n        }\n        skip_decode[i] = k == 0;\n    }\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::allocateBuffer(size_t batch_size, Tensor top_k, Tensor top_p)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::allocateBuffer(batch_size, top_k, top_p);\n    uint max_top_k = top_k.size() > 0 ? top_k.max<uint>() : 1;\n    if (max_top_k == 0) {\n        // for safety. TopKSamplingLayer handles a case of top_k=0 and top_p=0 as\n        // a greedy decode, i.e. top_k=1, although such case has max_top_k=0.\n        max_top_k = 1;\n    }\n    invokeTopKSampling<T>(nullptr,\n                          sampling_workspace_size_,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          max_top_k,\n                          1.0f,\n                          vocab_size_padded_,\n                          nullptr,\n                          stream_,\n                          batch_size,\n                          skip_decode_buf_);\n    sampling_workspace_ = allocator_->reMalloc(sampling_workspace_, sampling_workspace_size_, false);\n    runtime_top_k_buf_ =\n        reinterpret_cast<uint*>(allocator_->reMalloc(runtime_top_k_buf_, sizeof(uint) * batch_size, false));\n    runtime_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(runtime_top_p_buf_, sizeof(float) * batch_size, false));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&sampling_workspace_));\n        allocator_->free((void**)(&runtime_top_k_buf_));\n        allocator_->free((void**)(&runtime_top_p_buf_));\n    }\n    BaseSamplingLayer<T>::freeBuffer();\n    is_allocate_buffer_ = false;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    // Setup runtime topk and topp arguments.\n    //\n    // runtime_args:\n    //     runtime_top_k [1] or [batch_size] on cpu, optional, uint.\n    //     runtime_top_p [1] or [batch_size] on cpu, optional, float.\n    //     temperature [1] or [batch_size] on cpu, optional\n    //     repetition_penalty [1] or [batch_size] on cpu, optional\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::setup(batch_size, beam_width, runtime_args);\n\n    uint         tmp_top_k     = 0;\n    const Tensor runtime_top_k = runtime_args->isExist(\"runtime_top_k\") ?\n                                     runtime_args->at(\"runtime_top_k\") :\n                                     Tensor(MEMORY_CPU, TYPE_UINT32, {1}, &tmp_top_k);\n    const Tensor runtime_top_p = runtime_args->isExist(\"runtime_top_p\") ? runtime_args->at(\"runtime_top_p\") : Tensor();\n    const size_t runtime_top_k_size = runtime_top_k.size();\n    const size_t runtime_top_p_size = runtime_top_p.size();\n\n    uint  top_k = runtime_top_k.max<uint>();\n    float top_p = runtime_top_p_size == 0 ? 0.0f : runtime_top_p.getVal<float>();\n\n    if (runtime_top_k_size > 1) {\n        FT_CHECK_WITH_INFO(\n            runtime_top_k.size() == batch_size,\n            fmtstr(\"runtime_top_k.size() (%d) == batch_size (%d) is not satisfied!\", runtime_top_k.size(), batch_size));\n        cudaAutoCpy(runtime_top_k_buf_, runtime_top_k.getPtr<uint>(), batch_size, stream_);\n    }\n    if (runtime_top_p_size > 1) {\n        FT_CHECK_WITH_INFO(\n            runtime_top_p.size() == batch_size,\n            fmtstr(\"runtime_top_p.size() (%d) == batch_size (%d) is not satisfied!\", runtime_top_p.size(), batch_size));\n        cudaAutoCpy(runtime_top_p_buf_, runtime_top_p.getPtr<float>(), batch_size, stream_);\n    }\n\n    dim3 block(std::min((int)batch_size, 256));\n    dim3 grid(div_up((int)batch_size, (int)block.x));\n    // support top_k up to 1024.\n    setup_topk_runtime_args<1024><<<grid, block, 0, stream_>>>(batch_size,\n                                                               top_k,\n                                                               runtime_top_k_buf_,\n                                                               runtime_top_k_size,\n                                                               top_p,\n                                                               runtime_top_p_buf_,\n                                                               runtime_top_p_size,\n                                                               skip_decode_buf_);\n    cudaAutoCpy(skip_decode_, skip_decode_buf_, batch_size, stream_);\n    uint* runtime_top_ks = new uint[batch_size];\n    cudaAutoCpy(runtime_top_ks, runtime_top_k_buf_, batch_size, stream_);\n    runtime_max_top_k_ = static_cast<int>(*std::max_element(runtime_top_ks, runtime_top_ks + batch_size));\n    delete[] runtime_top_ks;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::runSampling(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded], optional\n    //      step [1] on cpu\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size], optional\n    //      ite [1] on cpu\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size]\n    //      finished [local_batch_size], optional\n    //      sequence_length [local_batch_size], optional\n    //      cum_log_probs [batch_size], must be float*, optional\n    //          The cumultative log probability of generated tokens.\n    //      output_log_probs [local_batch_size], must be float*, optional\n    //          The log probs at the current step.\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    FT_CHECK(input_tensors->size() >= 4);\n    FT_CHECK(output_tensors->size() >= 1);\n\n    const int batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n\n    // in case of skip any, the logit value is already copied and processed.\n    T* logits = !skip_any_ ? input_tensors->at(\"logits\").getPtr<T>() : runtime_logits_buf_;\n\n    invokeAddBiasEndMask(logits,\n                         (T*)(nullptr),\n                         input_tensors->at(\"end_id\").getPtr<const int>(),\n                         output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n                         local_batch_size,\n                         vocab_size_,\n                         vocab_size_padded_,\n                         stream_);\n    sync_check_cuda_error();\n\n    float* cum_log_probs =\n        output_tensors->isExist(\"cum_log_probs\") ? output_tensors->at(\"cum_log_probs\").getPtr<float>() : nullptr;\n    float* output_log_probs =\n        output_tensors->isExist(\"output_log_probs\") ? output_tensors->at(\"output_log_probs\").getPtr<float>() : nullptr;\n\n    if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n        invokeAddBiasSoftMax(\n            logits,\n            (T*)(nullptr),\n            input_tensors->at(\"end_id\").getPtr<int>(),\n            output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n            local_batch_size,\n            vocab_size_padded_,\n            vocab_size_,\n            stream_);\n        sync_check_cuda_error();\n    }\n\n    invokeBatchTopKSampling(\n        sampling_workspace_,\n        sampling_workspace_size_,\n        logits,\n        output_tensors->at(\"output_ids\").getPtrWithOffset<int>(step * batch_size + ite * local_batch_size),\n        output_tensors->at(\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<int>(),\n        output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n        cum_log_probs,\n        output_log_probs,\n        curandstate_buf_ + ite * local_batch_size,\n        (int)runtime_max_top_k_,  // useless because runtime_top_k_buf_ is never nullptr. Keep for legacy.\n        (int*)(runtime_top_k_buf_ + ite * local_batch_size),\n        1.0f,  // useless because runtime_top_p_buf_ is never nullptr. Keep for legacy.\n        runtime_top_p_buf_ + ite * local_batch_size,\n        vocab_size_padded_,\n        input_tensors->at(\"end_id\").getPtr<int>(),\n        stream_,\n        local_batch_size,\n        skip_decode_buf_ + ite * local_batch_size);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::TopKSamplingLayer(size_t             max_batch_size,\n                                        size_t             vocab_size,\n                                        size_t             vocab_size_padded,\n                                        int                end_id,\n                                        size_t             top_k,\n                                        unsigned long long random_seed,\n                                        float              temperature,\n                                        float              len_penalty,\n                                        float              repetition_penalty,\n                                        cudaStream_t       stream,\n                                        cublasMMWrapper*   cublas_wrapper,\n                                        IAllocator*        allocator,\n                                        bool               is_free_buffer_after_forward):\n    BaseSamplingLayer<T>(max_batch_size,\n                         vocab_size,\n                         vocab_size_padded,\n                         end_id,\n                         top_k,\n                         0.0f,\n                         random_seed,\n                         temperature,\n                         len_penalty,\n                         repetition_penalty,\n                         stream,\n                         cublas_wrapper,\n                         allocator,\n                         is_free_buffer_after_forward,\n                         nullptr)\n{\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::TopKSamplingLayer(TopKSamplingLayer<T> const& top_k_sampling_layer):\n    BaseSamplingLayer<T>(top_k_sampling_layer)\n{\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::~TopKSamplingLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    freeBuffer();\n}\n\ntemplate class TopKSamplingLayer<float>;\ntemplate class TopKSamplingLayer<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<uint TOP_K_MAX>\n__global__ void setup_topk_runtime_args(int    batch_size,\n                                        uint   top_k,\n                                        uint*  top_ks,\n                                        int    top_ks_size,\n                                        float  top_p,\n                                        float* top_ps,\n                                        int    top_ps_size,\n                                        bool*  skip_decode)\n{\n    int index = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = index; i < batch_size; i += gridDim.x * blockDim.x) {\n        uint  k = top_ks_size > 1 ? top_ks[i] : top_k;\n        float p = top_ps_size > 1 ? top_ps[i] : top_p;\n        if (k == 0 && p == 0.0f) {\n            // FT's topp implementation does not support topp = 0.0f, but it equivalent to greedy search.\n            // So, we set the topk = 1 as an alternative solution.\n            k = 1;\n        }\n        if (k > 0 && p == 0.0f) {\n            // for compatibility <= FT5.0.\n            // This case corresponds to the old topk sampling, which is equivalent to\n            // the old topk_topp sampling with topp=1.0f. TopKSamplingLayer and\n            // TopKTopPSamplingLayer are now merged by TopKSamplingLayer. Thus, we\n            // replace the case topk>0 and topp=0.0f by topk>0 and topp=1.0f for the\n            // compatibility.\n            p = 1.0f;\n        }\n        // Clip k value. A topk sampling kernel supports up to TOP_K_MAX=64.\n        top_ks[i] = k > TOP_K_MAX ? TOP_K_MAX : k;\n        if (k > TOP_K_MAX) {\n            printf(\"[WARNING] topk (%d) is larger than max supported number (%d) for token %d\"\n                   \" clip to max supported number %d. \\n\",\n                   k,\n                   TOP_K_MAX,\n                   i,\n                   top_ks[i]);\n        }\n        // Clip p value if it is out of range. range = [0.0, 1.0].\n        top_ps[i] = p < 0.0f ? 0.0f : (p > 1.0f ? 1.0f : p);\n        if (p < 0.0f || p > 1.0f) {\n            printf(\"[WARNING] topp (%f) is out of range ([0.0, 1.0f]) for token %d\"\n                   \" clip to closest number %f.\\n\",\n                   p,\n                   i,\n                   top_ps[i]);\n        }\n        skip_decode[i] = k == 0;\n    }\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::allocateBuffer(size_t batch_size, Tensor top_k, Tensor top_p)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::allocateBuffer(batch_size, top_k, top_p);\n    uint max_top_k = top_k.size() > 0 ? top_k.max<uint>() : 1;\n    if (max_top_k == 0) {\n        // for safety. TopKSamplingLayer handles a case of top_k=0 and top_p=0 as\n        // a greedy decode, i.e. top_k=1, although such case has max_top_k=0.\n        max_top_k = 1;\n    }\n    invokeTopKSampling<T>(nullptr,\n                          sampling_workspace_size_,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          nullptr,\n                          max_top_k,\n                          1.0f,\n                          vocab_size_padded_,\n                          nullptr,\n                          stream_,\n                          batch_size,\n                          skip_decode_buf_);\n    sampling_workspace_ = allocator_->reMalloc(sampling_workspace_, sampling_workspace_size_, false);\n    runtime_top_k_buf_ =\n        reinterpret_cast<uint*>(allocator_->reMalloc(runtime_top_k_buf_, sizeof(uint) * batch_size, false));\n    runtime_top_p_buf_ =\n        reinterpret_cast<float*>(allocator_->reMalloc(runtime_top_p_buf_, sizeof(float) * batch_size, false));\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&sampling_workspace_));\n        allocator_->free((void**)(&runtime_top_k_buf_));\n        allocator_->free((void**)(&runtime_top_p_buf_));\n    }\n    BaseSamplingLayer<T>::freeBuffer();\n    is_allocate_buffer_ = false;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::setup(const size_t batch_size, const size_t beam_width, TensorMap* runtime_args)\n{\n    // Setup runtime topk and topp arguments.\n    //\n    // runtime_args:\n    //     runtime_top_k [1] or [batch_size] on cpu, optional, uint.\n    //     runtime_top_p [1] or [batch_size] on cpu, optional, float.\n    //     temperature [1] or [batch_size] on cpu, optional\n    //     repetition_penalty [1] or [batch_size] on cpu, optional\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    BaseSamplingLayer<T>::setup(batch_size, beam_width, runtime_args);\n\n    uint         tmp_top_k     = 0;\n    const Tensor runtime_top_k = runtime_args->isExist(\"runtime_top_k\") ?\n                                     runtime_args->at(\"runtime_top_k\") :\n                                     Tensor(MEMORY_CPU, TYPE_UINT32, {1}, &tmp_top_k);\n    const Tensor runtime_top_p = runtime_args->isExist(\"runtime_top_p\") ? runtime_args->at(\"runtime_top_p\") : Tensor();\n    const size_t runtime_top_k_size = runtime_top_k.size();\n    const size_t runtime_top_p_size = runtime_top_p.size();\n\n    uint  top_k = runtime_top_k.max<uint>();\n    float top_p = runtime_top_p_size == 0 ? 0.0f : runtime_top_p.getVal<float>();\n\n    if (runtime_top_k_size > 1) {\n        FT_CHECK_WITH_INFO(\n            runtime_top_k.size() == batch_size,\n            fmtstr(\"runtime_top_k.size() (%d) == batch_size (%d) is not satisfied!\", runtime_top_k.size(), batch_size));\n        cudaAutoCpy(runtime_top_k_buf_, runtime_top_k.getPtr<uint>(), batch_size, stream_);\n    }\n    if (runtime_top_p_size > 1) {\n        FT_CHECK_WITH_INFO(\n            runtime_top_p.size() == batch_size,\n            fmtstr(\"runtime_top_p.size() (%d) == batch_size (%d) is not satisfied!\", runtime_top_p.size(), batch_size));\n        cudaAutoCpy(runtime_top_p_buf_, runtime_top_p.getPtr<float>(), batch_size, stream_);\n    }\n\n    dim3 block(std::min((int)batch_size, 256));\n    dim3 grid(div_up((int)batch_size, (int)block.x));\n    // support top_k up to 1024.\n    setup_topk_runtime_args<1024><<<grid, block, 0, stream_>>>(batch_size,\n                                                               top_k,\n                                                               runtime_top_k_buf_,\n                                                               runtime_top_k_size,\n                                                               top_p,\n                                                               runtime_top_p_buf_,\n                                                               runtime_top_p_size,\n                                                               skip_decode_buf_);\n    cudaAutoCpy(skip_decode_, skip_decode_buf_, batch_size, stream_);\n    uint* runtime_top_ks = new uint[batch_size];\n    cudaAutoCpy(runtime_top_ks, runtime_top_k_buf_, batch_size, stream_);\n    runtime_max_top_k_ = static_cast<int>(*std::max_element(runtime_top_ks, runtime_top_ks + batch_size));\n    delete[] runtime_top_ks;\n}\n\ntemplate<typename T>\nvoid TopKSamplingLayer<T>::runSampling(TensorMap* output_tensors, TensorMap* input_tensors)\n{\n    // input_tensors:\n    //      logits [local_batch_size, vocab_size_padded]\n    //      embedding_bias [vocab_size_padded], optional\n    //      step [1] on cpu\n    //      max_input_length [1] on cpu\n    //      input_lengths [local_batch_size], optional\n    //      ite [1] on cpu\n\n    // output_tensors:\n    //      output_ids [max_seq_len, batch_size]\n    //      finished [local_batch_size], optional\n    //      sequence_length [local_batch_size], optional\n    //      cum_log_probs [batch_size], must be float*, optional\n    //          The cumultative log probability of generated tokens.\n    //      output_log_probs [local_batch_size], must be float*, optional\n    //          The log probs at the current step.\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    FT_CHECK(input_tensors->size() >= 4);\n    FT_CHECK(output_tensors->size() >= 1);\n\n    const int batch_size       = output_tensors->at(\"output_ids\").shape[1];\n    const int local_batch_size = input_tensors->at(\"logits\").shape[0];\n    const int ite              = input_tensors->at(\"ite\").getVal<int>();\n    const int step             = input_tensors->at(\"step\").getVal<int>();\n\n    // in case of skip any, the logit value is already copied and processed.\n    T* logits = !skip_any_ ? input_tensors->at(\"logits\").getPtr<T>() : runtime_logits_buf_;\n\n    invokeAddBiasEndMask(logits,\n                         (T*)(nullptr),\n                         input_tensors->at(\"end_id\").getPtr<const int>(),\n                         output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n                         local_batch_size,\n                         vocab_size_,\n                         vocab_size_padded_,\n                         stream_);\n    sync_check_cuda_error();\n\n    float* cum_log_probs =\n        output_tensors->isExist(\"cum_log_probs\") ? output_tensors->at(\"cum_log_probs\").getPtr<float>() : nullptr;\n    float* output_log_probs =\n        output_tensors->isExist(\"output_log_probs\") ? output_tensors->at(\"output_log_probs\").getPtr<float>() : nullptr;\n\n    if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n        invokeAddBiasSoftMax(\n            logits,\n            (T*)(nullptr),\n            input_tensors->at(\"end_id\").getPtr<int>(),\n            output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n            local_batch_size,\n            vocab_size_padded_,\n            vocab_size_,\n            stream_);\n        sync_check_cuda_error();\n    }\n\n    invokeBatchTopKSampling(\n        sampling_workspace_,\n        sampling_workspace_size_,\n        logits,\n        output_tensors->at(\"output_ids\").getPtrWithOffset<int>(step * batch_size + ite * local_batch_size),\n        output_tensors->at(\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<int>(),\n        output_tensors->at(\"finished\", Tensor{MEMORY_GPU, TYPE_INVALID, {}, nullptr}).getPtr<bool>(),\n        cum_log_probs,\n        output_log_probs,\n        curandstate_buf_ + ite * local_batch_size,\n        (int)runtime_max_top_k_,  // useless because runtime_top_k_buf_ is never nullptr. Keep for legacy.\n        (int*)(runtime_top_k_buf_ + ite * local_batch_size),\n        1.0f,  // useless because runtime_top_p_buf_ is never nullptr. Keep for legacy.\n        runtime_top_p_buf_ + ite * local_batch_size,\n        vocab_size_padded_,\n        input_tensors->at(\"end_id\").getPtr<int>(),\n        stream_,\n        local_batch_size,\n        skip_decode_buf_ + ite * local_batch_size);\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::TopKSamplingLayer(size_t             max_batch_size,\n                                        size_t             vocab_size,\n                                        size_t             vocab_size_padded,\n                                        int                end_id,\n                                        size_t             top_k,\n                                        unsigned long long random_seed,\n                                        float              temperature,\n                                        float              len_penalty,\n                                        float              repetition_penalty,\n                                        hipStream_t       stream,\n                                        cublasMMWrapper*   cublas_wrapper,\n                                        IAllocator*        allocator,\n                                        bool               is_free_buffer_after_forward):\n    BaseSamplingLayer<T>(max_batch_size,\n                         vocab_size,\n                         vocab_size_padded,\n                         end_id,\n                         top_k,\n                         0.0f,\n                         random_seed,\n                         temperature,\n                         len_penalty,\n                         repetition_penalty,\n                         stream,\n                         cublas_wrapper,\n                         allocator,\n                         is_free_buffer_after_forward,\n                         nullptr)\n{\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::TopKSamplingLayer(TopKSamplingLayer<T> const& top_k_sampling_layer):\n    BaseSamplingLayer<T>(top_k_sampling_layer)\n{\n}\n\ntemplate<typename T>\nTopKSamplingLayer<T>::~TopKSamplingLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    freeBuffer();\n}\n\ntemplate class TopKSamplingLayer<float>;\ntemplate class TopKSamplingLayer<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/layers/attention_layers/DecoderCrossAttentionLayer.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\nconst int  WARP_SIZE            = 32;\nconst bool ATTENION_OPT         = true;\nconst int  ATTENTION_BLOCK_SIZE = 256;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<int HALF_ELEMENTS_PER_WARP_LOAD>\nusing Copy_half_t = typename std::conditional<\n    HALF_ELEMENTS_PER_WARP_LOAD == 32,\n    half,\n    typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 64,\n                              int,\n                              typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 128, int2, int4>::type>::type>::\n    type;\n\ntemplate<typename T, int ELEMENTS_PER_WARP_LOAD>\nusing Copy_t = Copy_half_t<sizeof(T) / sizeof(half) * ELEMENTS_PER_WARP_LOAD>;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void cross_attention_kernel(T*          query_buf,\n                                       const T*    Q_bias,\n                                       T*          key_cache,\n                                       const T*    K_bias,\n                                       T*          value_cache,\n                                       const T*    V_bias,\n                                       const int*  length_per_sample,\n                                       T*          context_buf,\n                                       const bool* finished,\n                                       int         batch_size,\n                                       int         head_num,\n                                       int         size_per_head,\n                                       int         step,\n                                       const int   seq_len,\n                                       const T     scalar,\n                                       const int*  ia3_tasks,\n                                       const T*    ia3_key_weights,\n                                       const T*    ia3_value_weights)\n{\n    if (finished != nullptr && finished[blockIdx.x / head_num] == true) {\n        return;\n    }\n    int tid     = threadIdx.x;\n    int bid     = blockIdx.x / head_num;\n    int head_id = blockIdx.x % head_num;\n\n    const bool do_ia3   = step == 1 && ia3_tasks != nullptr;\n    const int  ia3_task = do_ia3 ? ia3_tasks[bid] : 0;\n\n    extern __shared__ __align__(sizeof(float)) unsigned s_buf[];  // align on largest type\n    T*                                                  sq     = reinterpret_cast<T*>(s_buf);\n    T*                                                  logits = reinterpret_cast<T*>(&sq[size_per_head]);\n\n    int length = __ldg(&length_per_sample[bid]);\n\n    int qkv_id      = bid * head_num * size_per_head + head_id * size_per_head + tid;\n    int qkv_bias_id = head_id * size_per_head + tid;\n\n    if (tid < size_per_head) {\n        sq[tid] = add(query_buf[qkv_id], Q_bias[qkv_bias_id]);\n    }\n    __syncthreads();\n\n    for (int ite = 0; ite < length; ++ite) {\n        int key_id = bid * (seq_len * head_num * size_per_head) + ite * (head_num * size_per_head)\n                     + head_id * size_per_head + tid;\n\n        T key = tid < size_per_head ? key_cache[key_id] : (T)(0.0f);\n\n        // For the first step, we should add bias to key memory cache.\n        // The KV memory cache only need to be updated at the first step.\n        if (step == 1 && tid < size_per_head) {\n            key = add(key, K_bias[head_id * size_per_head + tid]);\n            if (do_ia3) {\n                key = mmha::mul<T, T, T>(key, ia3_key_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n            }\n            key_cache[key_id] = key;\n        }\n\n        T val = (tid < size_per_head) ? mul(key, sq[tid], scalar) : (T)(0.0f);\n        T qk  = blockReduceSum(val);\n        if (threadIdx.x == 0) {\n            logits[ite] = qk;\n        }\n        __syncthreads();  // try to remove\n    }\n    __syncthreads();\n\n    __shared__ float s_max_val, s_sum;\n\n    float local_i = tid < length ? (float)logits[tid] : -1e20f;\n    float max_val = blockReduceMax(local_i);\n    if (tid == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    local_i -= s_max_val;\n    float local_o = tid < length ? __expf(local_i) : 0.0f;\n    float val     = blockReduceSum(local_o);\n\n    if (tid == 0) {\n        s_sum = val + 1e-6;\n    }\n    __syncthreads();\n    if (tid < length) {\n        logits[tid] = local_o / s_sum;\n    }\n    __syncthreads();\n\n    if (tid < size_per_head) {\n        T sum = (T)0.0f;\n        for (int ite = 0; ite < length; ++ite) {\n            int value_id = bid * seq_len * head_num * size_per_head + ite * head_num * size_per_head\n                           + head_id * size_per_head + tid;\n\n            T value = value_cache[value_id];\n\n            // for the first step, we should add bias to key memory cache\n            if (step == 1) {\n                value = add(value, V_bias[head_id * size_per_head + tid]);\n                if (do_ia3) {\n                    value = mmha::mul<T, T, T>(\n                        value, ia3_value_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n                value_cache[value_id] = value;\n            }\n            sum = fma(value, logits[ite], sum);\n        }\n        context_buf[bid * head_num * size_per_head + head_id * size_per_head + tid] = sum;\n    }\n}\n\ntemplate<typename T, int size_per_head, int block_sz>\n__global__ void cross_attention_kernel_opt(T* __restrict query_buf,\n                                           const T* __restrict Q_bias,\n                                           T* __restrict key_cache,\n                                           const T* __restrict K_bias,\n                                           T* __restrict value_cache,\n                                           const T* __restrict V_bias,\n                                           const int* length_per_sample,\n                                           T* __restrict context_buf,\n                                           const bool* finished,\n                                           int         batch_size,\n                                           int         head_num,\n                                           const int   step,\n                                           const int   seq_len,\n                                           const float scalar,\n                                           const int*  ia3_tasks,\n                                           const T*    ia3_key_weights,\n                                           const T*    ia3_value_weights)\n{\n    if (finished != nullptr && finished[blockIdx.x / head_num] == true) {\n        return;\n    }\n    typedef Copy_t<T, size_per_head> copy_t;\n    const int                        elems_per_thread = size_per_head / WARP_SIZE;\n    union Access_t {\n        copy_t v;\n        T      x[elems_per_thread];  // supported size 1,2,4\n    };\n    typedef struct Float_n_t {\n        float x[elems_per_thread];  // supported size 1,2,4\n    } float_n_t;\n\n    __shared__ float_n_t    sq[block_sz];\n    extern __shared__ float logits[];  // use to store the logits from [0~step]\n\n    const int warp_id  = threadIdx.x / WARP_SIZE;\n    const int warp_num = block_sz / WARP_SIZE;\n\n    typedef cub::BlockReduce<float, block_sz>          MaxValBlockReduce;\n    typedef cub::BlockReduce<float, block_sz>          BlockReduce;\n    __shared__ typename MaxValBlockReduce::TempStorage max_val_block_temp_storage;\n    __shared__ typename BlockReduce::TempStorage       block_temp_storage;\n\n    __shared__ typename cub::WarpReduce<float>::TempStorage temp_storage[warp_num];\n\n    const int tid     = threadIdx.x;\n    const int bid     = blockIdx.x / head_num;\n    const int head_id = blockIdx.x % head_num;\n\n    int length = __ldg(&length_per_sample[bid]);\n\n    const int lane_id = tid % WARP_SIZE;\n\n    int qkv_id      = bid * head_num * size_per_head + head_id * size_per_head;\n    int qkv_bias_id = head_id * size_per_head;\n\n    int key_value_id = bid * (seq_len * head_num * size_per_head) + +head_id * size_per_head;\n\n    const bool do_ia3   = step == 1 && ia3_tasks != nullptr;\n    const int  ia3_task = do_ia3 ? ia3_tasks[bid] : 0;\n\n    query_buf   = &query_buf[qkv_id];\n    K_bias      = &K_bias[qkv_bias_id];\n    key_cache   = &key_cache[key_value_id];\n    Q_bias      = &Q_bias[qkv_bias_id];\n    V_bias      = &V_bias[qkv_bias_id];\n    value_cache = &value_cache[key_value_id];\n    context_buf = &context_buf[qkv_id];\n\n    Access_t bias_r, key_val_r, query_buf_r;\n\n    // each warp will have its own copy of sq\n    query_buf_r.v = *((copy_t*)query_buf + lane_id);\n    bias_r.v      = *((copy_t*)Q_bias + lane_id);\n    float qb_r[elems_per_thread];\n    for (int i = 0; i < elems_per_thread; ++i) {\n        qb_r[i] = (float)query_buf_r.x[i] + (float)bias_r.x[i];\n    }\n\n    // offset for each step\n    int offset = head_num * size_per_head;\n\n    bias_r.v = *((copy_t*)K_bias + lane_id);\n    for (int ite = warp_id; ite < length; ite += warp_num) {\n        key_val_r.v = *((copy_t*)&key_cache[ite * offset] + lane_id);\n\n        // For the first step, we should add bias to key memory cache.\n        // The KV memory cache only need to be updated at the first step.\n        if (step == 1) {\n            for (int i = 0; i < elems_per_thread; i++) {\n                key_val_r.x[i] = (float)key_val_r.x[i] + (float)bias_r.x[i];\n                if (do_ia3) {\n                    key_val_r.x[i] = (float)key_val_r.x[i]\n                                     * (float)(ia3_key_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n            }\n            *((copy_t*)&key_cache[ite * offset] + lane_id) = key_val_r.v;\n        }\n        float val = 0.f;\n        for (int i = 0; i < elems_per_thread; i++) {\n            val = val + (float)key_val_r.x[i] * qb_r[i] * scalar;\n        }\n        float qk = cub::WarpReduce<float>(temp_storage[warp_id]).Sum(val);\n        if (lane_id == 0) {\n            logits[ite] = qk;\n        }\n    }\n    __syncthreads();\n\n    __shared__ float s_max_val, s_sum;\n    float            local_i = -1e20f;\n    for (int i = tid; i < length; i += blockDim.x) {\n        local_i = max(local_i, logits[i]);\n    }\n\n    float max_val = MaxValBlockReduce(max_val_block_temp_storage).Reduce(local_i, cub::Max());\n    if (tid == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    float local_o = 0.0f;\n    for (int i = tid; i < length; i += blockDim.x) {\n        logits[i] = __expf(logits[i] - s_max_val);\n        local_o += logits[i];\n    }\n    float val = BlockReduce(block_temp_storage).Sum(local_o);\n\n    if (tid == 0) {\n        s_sum = val + 1e-6;\n    }\n    __syncthreads();\n\n    float s_sum_inverse = __fdividef(1.0f, s_sum);\n    for (int i = tid; i < length; i += blockDim.x) {\n        logits[i] = logits[i] * s_sum_inverse;\n    }\n    __syncthreads();\n\n    // This optimization introduces discrepancy because of different order in FP32 summation\n    float sum_r[elems_per_thread] = {0.f};\n    bias_r.v                      = *((copy_t*)V_bias + lane_id);\n    for (int ite = warp_id; ite < length; ite += warp_num) {\n        key_val_r.v = *((copy_t*)&value_cache[ite * offset] + lane_id);\n\n        // For the first step, we should add bias to key memory cache.\n        if (step == 1) {\n            for (int i = 0; i < elems_per_thread; i++) {\n                key_val_r.x[i] = (float)key_val_r.x[i] + (float)bias_r.x[i];\n                if (do_ia3) {\n                    key_val_r.x[i] =\n                        (float)key_val_r.x[i]\n                        * (float)(ia3_value_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n            }\n            *((copy_t*)&value_cache[ite * offset] + lane_id) = key_val_r.v;\n        }\n        for (int i = 0; i < elems_per_thread; ++i) {\n            sum_r[i] += (float)key_val_r.x[i] * logits[ite];\n        }\n    }\n    for (int i = 0; i < elems_per_thread; i++) {\n        sq[warp_id * WARP_SIZE + lane_id].x[i] = sum_r[i];\n    }\n    __syncthreads();\n    if (threadIdx.x < WARP_SIZE) {\n#pragma unroll\n        for (int j = 1; j < warp_num; j++) {\n            for (int i = 0; i < elems_per_thread; ++i) {\n                sum_r[i] = sum_r[i] + (float)sq[j * WARP_SIZE + threadIdx.x].x[i];\n            }\n        }\n    }\n    __syncthreads();\n#pragma unroll\n    for (int i = 0; i < elems_per_thread; i++) {\n        key_val_r.x[i] = sum_r[i];\n    }\n    if (threadIdx.x < WARP_SIZE) {\n        *((copy_t*)context_buf + lane_id) = key_val_r.v;\n    }\n}\n\ntemplate<typename T>\nstruct CATypeConverter {\n    using Type = T;\n};\n\ntemplate<>\nstruct CATypeConverter<half> {\n    using Type = uint16_t;\n};\n\ntemplate<typename T>\nvoid cross_attention_dispatch(T*                               query_buf,\n                              const T*                         Q_bias,\n                              T*                               key_cache,\n                              const T*                         K_bias,\n                              T*                               value_cache,\n                              const T*                         V_bias,\n                              const int*                       length,\n                              T*                               context_buf,\n                              const bool*                      finished,\n                              const int                        max_batch_size,\n                              const int                        inference_batch_size,\n                              const int                        head_num,\n                              const int                        size_per_head,\n                              const int                        step,\n                              const int                        memory_max_len,\n                              const bool                       batch_major_cache,\n                              const float                      q_scaling,\n                              outputCrossAttentionParam<float> output_cross_attention_params,\n                              const int*                       ia3_tasks,\n                              const T*                         ia3_key_weights,\n                              const T*                         ia3_value_weights,\n                              cudaStream_t                     stream)\n{\n    if (!batch_major_cache) {\n        const int block_sz = ATTENTION_BLOCK_SIZE;\n        float     scalar   = 1.f / (sqrtf(size_per_head * 1.0f) * q_scaling);\n\n        dim3 grid(inference_batch_size * head_num);\n\n        int cond = size_per_head * ((ATTENION_OPT) ? 1 : 0);\n        switch (cond) {\n            case 32:\n                cross_attention_kernel_opt<T, 32, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            case 64:\n                cross_attention_kernel_opt<T, 64, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            case 128:\n                cross_attention_kernel_opt<T, 128, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            default:\n                // default path\n\n                int block_size = 128;\n\n                if (memory_max_len <= 64) {\n                    block_size = 64;\n                }\n                else if (memory_max_len <= 128 && memory_max_len > size_per_head) {\n                    block_size = 128;\n                }\n                else if (memory_max_len > 128 && memory_max_len <= 256) {\n                    block_size = 256;\n                }\n                else if (memory_max_len > 256 && memory_max_len <= 512) {\n                    block_size = 512;\n                }\n                else {\n                    block_size = 1024;\n                }\n\n                if (block_size < size_per_head) {\n                    block_size = size_per_head;\n                }\n\n                assert(block_size <= 1024);\n                dim3 block(block_size);\n\n                int shared_size = sizeof(T) * (size_per_head + memory_max_len);\n                cross_attention_kernel<T><<<grid, block, shared_size, stream>>>(query_buf,\n                                                                                Q_bias,\n                                                                                key_cache,\n                                                                                K_bias,\n                                                                                value_cache,\n                                                                                V_bias,\n                                                                                length,\n                                                                                context_buf,\n                                                                                finished,\n                                                                                max_batch_size,\n                                                                                head_num,\n                                                                                size_per_head,\n                                                                                step,\n                                                                                memory_max_len,\n                                                                                scalar,\n                                                                                ia3_tasks,\n                                                                                ia3_key_weights,\n                                                                                ia3_value_weights);\n        }\n    }\n    else {\n        assert(step > 0);\n        // assert(size_per_head == 32 || size_per_head == 64 || size_per_head == 128);\n        // using DataType = typename std::conditional<sizeof(T) == 4, float, uint16_t>::type;\n        using DataType = typename CATypeConverter<T>::Type;\n        // Prepare the parameters.\n        Cross_multihead_attention_params<DataType> params;\n        params.q_bias = reinterpret_cast<const DataType*>(Q_bias);\n        params.k_bias = reinterpret_cast<const DataType*>(K_bias);\n        params.v_bias = reinterpret_cast<const DataType*>(V_bias);\n\n        // Set the output buffer.\n        params.out = reinterpret_cast<DataType*>(context_buf);\n\n        // Set the input buffers.\n        params.q        = reinterpret_cast<const DataType*>(query_buf);\n        params.k        = nullptr;\n        params.v        = nullptr;\n        params.stride   = 0;\n        params.finished = const_cast<bool*>(finished);\n\n        params.memory_length_per_sample = const_cast<int*>(length);\n\n        params.k_cache    = reinterpret_cast<DataType*>(key_cache);\n        params.v_cache    = reinterpret_cast<DataType*>(value_cache);\n        params.batch_size = inference_batch_size;\n        // TODO(bhsueh) We can use batch but not batch * beam_width in k/v cache in cross attention\n        // because they are same for all beams.\n        params.beam_width           = 1;  // We don't care the beam_width in cross attention, set to 1 is enough.\n        params.memory_max_len       = memory_max_len;\n        params.timestep             = step - 1;\n        params.num_heads            = head_num;\n        params.hidden_size_per_head = size_per_head;\n        params.inv_sqrt_dh          = 1.F / (sqrtf((float)params.hidden_size_per_head) * q_scaling);\n\n        // output cross attentions\n        params.max_decoder_seq_len        = output_cross_attention_params.max_decoder_seq_len;\n        params.cross_attention_out        = output_cross_attention_params.cross_attention_out;\n        params.is_return_cross_attentions = output_cross_attention_params.is_return_cross_attentions;\n\n        params.ia3_tasks         = ia3_tasks;\n        params.ia3_key_weights   = reinterpret_cast<const DataType*>(ia3_key_weights);\n        params.ia3_value_weights = reinterpret_cast<const DataType*>(ia3_value_weights);\n\n        cross_multihead_attention(params, stream);\n    }\n}\n\ntemplate void cross_attention_dispatch(float*                           query_buf,\n                                       const float*                     Q_bias,\n                                       float*                           key_cache,\n                                       const float*                     K_bias,\n                                       float*                           value_cache,\n                                       const float*                     V_bias,\n                                       const int*                       length,\n                                       float*                           context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const float*                     ia3_key_weights,\n                                       const float*                     ia3_value_weights,\n                                       cudaStream_t                     stream);\n\ntemplate void cross_attention_dispatch(half*                            query_buf,\n                                       const half*                      Q_bias,\n                                       half*                            key_cache,\n                                       const half*                      K_bias,\n                                       half*                            value_cache,\n                                       const half*                      V_bias,\n                                       const int*                       length,\n                                       half*                            context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const half*                      ia3_key_weights,\n                                       const half*                      ia3_value_weights,\n                                       cudaStream_t                     stream);\n\n#ifdef ENABLE_BF16\ntemplate void cross_attention_dispatch(__nv_bfloat16*                   query_buf,\n                                       const __nv_bfloat16*             Q_bias,\n                                       __nv_bfloat16*                   key_cache,\n                                       const __nv_bfloat16*             K_bias,\n                                       __nv_bfloat16*                   value_cache,\n                                       const __nv_bfloat16*             V_bias,\n                                       const int*                       length,\n                                       __nv_bfloat16*                   context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const __nv_bfloat16*             ia3_key_weights,\n                                       const __nv_bfloat16*             ia3_value_weights,\n                                       cudaStream_t                     stream);\n#endif\n\n// Currently need to transpose at the first step in Cross attention\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_mem_k_cache(\n    T* k_dst, const T* k_src, const int head_num, const int size_per_head, const int max_seq_len)\n{\n    // B, L, H, Dh -> B, H, Dh/x, L, x\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = (sizeof(T) == 4) ? 4 : 8;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * max_seq_len\n                                                  + head_id * size_per_head);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    key_dst[out_idx] = key_src[k_seq_len_id * head_num * size_per_head_div_x + k_head_size_id];\n}\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_mem_v_cache(\n    T* v_dst, const T* v_src, const int head_num, const int size_per_head, const int max_seq_len)\n{\n    // B, L, H, Dh -> B, H, L, Dh\n    const int batch_id = blockIdx.y;\n    const int head_id  = blockIdx.z;\n\n    // 16 byte loads will handle \"x\" dimension\n    auto val_src = reinterpret_cast<const uint4*>(v_src + batch_id * head_num * size_per_head * max_seq_len\n                                                  + head_id * size_per_head);\n    auto val_dst = reinterpret_cast<uint4*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int out_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    constexpr int X_ELEMS             = (sizeof(T) == 4) ? 4 : 8;\n    const int     size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int v_head_size_id = idx % size_per_head_div_x;\n    idx                      = (idx - v_head_size_id) / size_per_head_div_x;\n    const int v_seq_len_id   = idx % max_seq_len;\n\n    val_dst[out_idx] = val_src[v_seq_len_id * head_num * size_per_head_div_x + v_head_size_id];\n}\n\ntemplate<typename T>\nvoid transpose_4d_batch_major_memory_kernelLauncher(T*           dst,\n                                                    const T*     src,\n                                                    const int    local_batch_size,\n                                                    const int    max_seq_len,\n                                                    const int    size_per_head,\n                                                    const int    local_head_num,\n                                                    const bool   k_cache,\n                                                    cudaStream_t stream)\n{\n    constexpr int block_sz = 128;\n\n    constexpr int x    = (sizeof(T) == 4) ? 4 : 8;\n    int           size = max_seq_len * size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n\n    if (k_cache) {\n        transpose_4d_batch_major_mem_k_cache<<<grid, block_sz, 0, stream>>>(\n            dst, src, local_head_num, size_per_head, max_seq_len);\n    }\n    else {\n        transpose_4d_batch_major_mem_v_cache<<<grid, block_sz, 0, stream>>>(\n            dst, src, local_head_num, size_per_head, max_seq_len);\n    }\n}\n\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(float*       dst,\n                                                             const float* src,\n                                                             const int    local_batch_size,\n                                                             const int    max_seq_len,\n                                                             const int    size_per_head,\n                                                             const int    local_head_num,\n                                                             const bool   k_cache,\n                                                             cudaStream_t stream);\n\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(half*        dst,\n                                                             const half*  src,\n                                                             const int    local_batch_size,\n                                                             const int    max_seq_len,\n                                                             const int    size_per_head,\n                                                             const int    local_head_num,\n                                                             const bool   k_cache,\n                                                             cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(__nv_bfloat16*       dst,\n                                                             const __nv_bfloat16* src,\n                                                             const int            local_batch_size,\n                                                             const int            max_seq_len,\n                                                             const int            size_per_head,\n                                                             const int            local_head_num,\n                                                             const bool           k_cache,\n                                                             cudaStream_t         stream);\n#endif\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n    if (is_allocate_buffer_ == false) {\n        q_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(q_buf_, sizeof(T) * max_batch_size_ * hidden_units_, false));\n        context_buf_ = reinterpret_cast<T*>(\n            allocator_->reMalloc(context_buf_, sizeof(T) * max_batch_size_ * hidden_units_, false));\n\n        if (is_batch_major_cache_) {\n            mem_cache_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(\n                mem_cache_buf_, sizeof(T) * max_batch_size_ * max_mem_seq_len_ * hidden_units_, false));\n        }\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::allocateBuffer(size_t batch_size, size_t max_mem_seq_len)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    q_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(q_buf_, sizeof(T) * batch_size * hidden_units_, false));\n    context_buf_ =\n        reinterpret_cast<T*>(allocator_->reMalloc(context_buf_, sizeof(T) * batch_size * hidden_units_, false));\n\n    if (is_batch_major_cache_) {\n        mem_cache_buf_ = reinterpret_cast<T*>(\n            allocator_->reMalloc(mem_cache_buf_, sizeof(T) * batch_size * max_mem_seq_len * hidden_units_, false));\n    }\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::freeBuffer()\n{\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&q_buf_));\n        allocator_->free((void**)(&context_buf_));\n        if (is_batch_major_cache_) {\n            allocator_->free((void**)(&mem_cache_buf_));\n        }\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          size_t           d_model,\n                                                          const float      q_scaling,\n                                                          cudaStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_size_(max_batch_size),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    hidden_units_(head_num_ * size_per_head_),\n    d_model_(d_model),\n    q_scaling_(q_scaling)\n{\n    FT_CHECK(size_per_head_ == 32 || size_per_head_ == 48 || size_per_head_ == 64 || size_per_head_ == 80\n             || size_per_head_ == 96 || size_per_head_ == 112 || size_per_head_ == 128 || size_per_head_ == 144\n             || size_per_head_ == 160 || size_per_head_ == 192 || size_per_head_ == 224 || size_per_head_ == 256);\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          cudaStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    DecoderCrossAttentionLayer<T>(max_batch_size,\n                                  head_num,\n                                  size_per_head,\n                                  head_num * size_per_head,\n                                  1.0f,\n                                  stream,\n                                  cublas_wrapper,\n                                  allocator,\n                                  is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          const float      q_scaling,\n                                                          cudaStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    DecoderCrossAttentionLayer<T>(max_batch_size,\n                                  head_num,\n                                  size_per_head,\n                                  head_num * size_per_head,\n                                  q_scaling,\n                                  stream,\n                                  cublas_wrapper,\n                                  allocator,\n                                  is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(DecoderCrossAttentionLayer<T> const& attention_layer):\n    DecoderCrossAttentionLayer(attention_layer.max_batch_size_,\n                               attention_layer.head_num_,\n                               attention_layer.size_per_head_,\n                               attention_layer.d_model_,\n                               attention_layer.q_scaling_,\n                               attention_layer.stream_,\n                               attention_layer.cublas_wrapper_,\n                               attention_layer.allocator_,\n                               attention_layer.is_free_buffer_after_forward_)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::~DecoderCrossAttentionLayer()\n{\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::forward(TensorMap*                output_tensors,\n                                            TensorMap*                input_tensors,\n                                            const AttentionWeight<T>* attention_weights)\n{\n    // input tensors:\n    //      attention_input [batch_size, d_model],\n    //      encoder_output [batch_size, mem_max_seq_len, memory_d_model],\n    //      encoder_sequence_length [batch_size],\n    //      step [1] on cpu\n    //      finished [batch_size] (optional)\n    //      ia3_tasks [batch_size] (optional)\n\n    // output tensors:\n    //      decoder_layer_output [batch_size, d_model],\n    //      key_mem_cache [batch_size, head_num, size_per_head // x, mem_max_seq_len, x], where x = 16 / sizeof(T)\n    //      value_mem_cache [batch_size, head_num, mem_max_seq_len, size_per_head]\n    //      cross_attentions [batch_size, head_num, max_decoder_seq_len, mem_max_seq_len] optional float*\n    FT_LOG_DEBUG(\"%s\", __PRETTY_FUNCTION__);\n    allocateBuffer(input_tensors->at(\"input_query\").shape[0], input_tensors->at(\"encoder_output\").shape[1]);\n\n    const T*    attention_input        = input_tensors->getPtr<T>(\"input_query\");\n    Tensor      encoder_output_tensor  = input_tensors->at(\"encoder_output\");\n    const int*  memory_sequence_length = input_tensors->getPtr<int>(\"encoder_sequence_length\");\n    const int   step                   = input_tensors->getVal<int>(\"step\");\n    const bool* finished               = input_tensors->getPtr<bool>(\"finished\", nullptr);\n    const bool  has_ia3                = input_tensors->isExist(\"ia3_tasks\");\n\n    T* attention_out   = output_tensors->getPtr<T>(\"hidden_features\");\n    T* key_mem_cache   = output_tensors->getPtr<T>(\"key_cache\");\n    T* value_mem_cache = output_tensors->getPtr<T>(\"value_cache\");\n\n    const bool output_cross_attentions = output_tensors->isExist(\"cross_attentions\");\n    const int  max_decoder_seq_len     = output_cross_attentions ? output_tensors->at(\"cross_attentions\").shape[2] : 0;\n\n    const int batch_size      = input_tensors->at(\"input_query\").shape[0];\n    const int mem_max_seq_len = encoder_output_tensor.shape[1];\n    cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                          CUBLAS_OP_N,\n                          hidden_units_,  // n\n                          batch_size,\n                          d_model_,  // k\n                          attention_weights->query_weight.kernel,\n                          hidden_units_,  // n\n                          attention_input,\n                          d_model_,  // k\n                          q_buf_,\n                          hidden_units_ /* n */);\n\n    if (step == 1) {\n        if (is_batch_major_cache_) {\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->key_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  mem_cache_buf_,\n                                  hidden_units_);\n            transpose_4d_batch_major_memory_kernelLauncher<T>(\n                key_mem_cache, mem_cache_buf_, batch_size, mem_max_seq_len, size_per_head_, head_num_, true, stream_);\n            sync_check_cuda_error();\n\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->value_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  mem_cache_buf_,\n                                  hidden_units_);\n            transpose_4d_batch_major_memory_kernelLauncher<T>(value_mem_cache,\n                                                              mem_cache_buf_,\n                                                              batch_size,\n                                                              mem_max_seq_len,\n                                                              size_per_head_,\n                                                              head_num_,\n                                                              false,\n                                                              stream_);\n            sync_check_cuda_error();\n        }\n        else {\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->key_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  key_mem_cache,\n                                  hidden_units_);\n\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->value_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  value_mem_cache,\n                                  hidden_units_);\n        }\n    }\n    sync_check_cuda_error();\n\n    outputCrossAttentionParam<float> output_attention_param{};\n    // output cross attentions\n    if (output_cross_attentions) {\n        output_attention_param.max_decoder_seq_len        = max_decoder_seq_len;\n        output_attention_param.cross_attention_out        = output_tensors->at(\"cross_attentions\").getPtr<float>();\n        output_attention_param.is_return_cross_attentions = true;\n    }\n\n    cross_attention_dispatch<T>(q_buf_,\n                                attention_weights->query_weight.bias,\n                                key_mem_cache,\n                                attention_weights->key_weight.bias,\n                                value_mem_cache,\n                                attention_weights->value_weight.bias,\n                                memory_sequence_length,\n                                context_buf_,\n                                finished,\n                                batch_size,\n                                batch_size,\n                                head_num_,\n                                size_per_head_,\n                                step,\n                                mem_max_seq_len,\n                                is_batch_major_cache_,\n                                q_scaling_,\n                                output_attention_param,\n                                has_ia3 ? input_tensors->at(\"ia3_tasks\").getPtr<const int>() : nullptr,\n                                has_ia3 ? attention_weights->ia3_key_weight.kernel : nullptr,\n                                has_ia3 ? attention_weights->ia3_value_weight.kernel : nullptr,\n                                stream_);\n    sync_check_cuda_error();\n    cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                          CUBLAS_OP_N,\n                          d_model_,  // n\n                          batch_size,\n                          hidden_units_,  // k\n                          attention_weights->attention_output_weight.kernel,\n                          d_model_,  // n\n                          context_buf_,\n                          hidden_units_,  // k\n                          attention_out,\n                          d_model_ /* n */);\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate class DecoderCrossAttentionLayer<float>;\ntemplate class DecoderCrossAttentionLayer<half>;\n#ifdef ENABLE_BF16\ntemplate class DecoderCrossAttentionLayer<__nv_bfloat16>;\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/layers/attention_layers/DecoderCrossAttentionLayer.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\nconst int  WARP_SIZE            = 32;\nconst bool ATTENION_OPT         = true;\nconst int  ATTENTION_BLOCK_SIZE = 256;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<int HALF_ELEMENTS_PER_WARP_LOAD>\nusing Copy_half_t = typename std::conditional<\n    HALF_ELEMENTS_PER_WARP_LOAD == 32,\n    half,\n    typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 64,\n                              int,\n                              typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 128, int2, int4>::type>::type>::\n    type;\n\ntemplate<typename T, int ELEMENTS_PER_WARP_LOAD>\nusing Copy_t = Copy_half_t<sizeof(T) / sizeof(half) * ELEMENTS_PER_WARP_LOAD>;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\n__global__ void cross_attention_kernel(T*          query_buf,\n                                       const T*    Q_bias,\n                                       T*          key_cache,\n                                       const T*    K_bias,\n                                       T*          value_cache,\n                                       const T*    V_bias,\n                                       const int*  length_per_sample,\n                                       T*          context_buf,\n                                       const bool* finished,\n                                       int         batch_size,\n                                       int         head_num,\n                                       int         size_per_head,\n                                       int         step,\n                                       const int   seq_len,\n                                       const T     scalar,\n                                       const int*  ia3_tasks,\n                                       const T*    ia3_key_weights,\n                                       const T*    ia3_value_weights)\n{\n    if (finished != nullptr && finished[blockIdx.x / head_num] == true) {\n        return;\n    }\n    int tid     = threadIdx.x;\n    int bid     = blockIdx.x / head_num;\n    int head_id = blockIdx.x % head_num;\n\n    const bool do_ia3   = step == 1 && ia3_tasks != nullptr;\n    const int  ia3_task = do_ia3 ? ia3_tasks[bid] : 0;\n\n    extern __shared__ __align__(sizeof(float)) unsigned s_buf[];  // align on largest type\n    T*                                                  sq     = reinterpret_cast<T*>(s_buf);\n    T*                                                  logits = reinterpret_cast<T*>(&sq[size_per_head]);\n\n    int length = __ldg(&length_per_sample[bid]);\n\n    int qkv_id      = bid * head_num * size_per_head + head_id * size_per_head + tid;\n    int qkv_bias_id = head_id * size_per_head + tid;\n\n    if (tid < size_per_head) {\n        sq[tid] = add(query_buf[qkv_id], Q_bias[qkv_bias_id]);\n    }\n    __syncthreads();\n\n    for (int ite = 0; ite < length; ++ite) {\n        int key_id = bid * (seq_len * head_num * size_per_head) + ite * (head_num * size_per_head)\n                     + head_id * size_per_head + tid;\n\n        T key = tid < size_per_head ? key_cache[key_id] : (T)(0.0f);\n\n        // For the first step, we should add bias to key memory cache.\n        // The KV memory cache only need to be updated at the first step.\n        if (step == 1 && tid < size_per_head) {\n            key = add(key, K_bias[head_id * size_per_head + tid]);\n            if (do_ia3) {\n                key = mmha::mul<T, T, T>(key, ia3_key_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n            }\n            key_cache[key_id] = key;\n        }\n\n        T val = (tid < size_per_head) ? mul(key, sq[tid], scalar) : (T)(0.0f);\n        T qk  = blockReduceSum(val);\n        if (threadIdx.x == 0) {\n            logits[ite] = qk;\n        }\n        __syncthreads();  // try to remove\n    }\n    __syncthreads();\n\n    __shared__ float s_max_val, s_sum;\n\n    float local_i = tid < length ? (float)logits[tid] : -1e20f;\n    float max_val = blockReduceMax(local_i);\n    if (tid == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    local_i -= s_max_val;\n    float local_o = tid < length ? __expf(local_i) : 0.0f;\n    float val     = blockReduceSum(local_o);\n\n    if (tid == 0) {\n        s_sum = val + 1e-6;\n    }\n    __syncthreads();\n    if (tid < length) {\n        logits[tid] = local_o / s_sum;\n    }\n    __syncthreads();\n\n    if (tid < size_per_head) {\n        T sum = (T)0.0f;\n        for (int ite = 0; ite < length; ++ite) {\n            int value_id = bid * seq_len * head_num * size_per_head + ite * head_num * size_per_head\n                           + head_id * size_per_head + tid;\n\n            T value = value_cache[value_id];\n\n            // for the first step, we should add bias to key memory cache\n            if (step == 1) {\n                value = add(value, V_bias[head_id * size_per_head + tid]);\n                if (do_ia3) {\n                    value = mmha::mul<T, T, T>(\n                        value, ia3_value_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n                value_cache[value_id] = value;\n            }\n            sum = fma(value, logits[ite], sum);\n        }\n        context_buf[bid * head_num * size_per_head + head_id * size_per_head + tid] = sum;\n    }\n}\n\ntemplate<typename T, int size_per_head, int block_sz>\n__global__ void cross_attention_kernel_opt(T* __restrict query_buf,\n                                           const T* __restrict Q_bias,\n                                           T* __restrict key_cache,\n                                           const T* __restrict K_bias,\n                                           T* __restrict value_cache,\n                                           const T* __restrict V_bias,\n                                           const int* length_per_sample,\n                                           T* __restrict context_buf,\n                                           const bool* finished,\n                                           int         batch_size,\n                                           int         head_num,\n                                           const int   step,\n                                           const int   seq_len,\n                                           const float scalar,\n                                           const int*  ia3_tasks,\n                                           const T*    ia3_key_weights,\n                                           const T*    ia3_value_weights)\n{\n    if (finished != nullptr && finished[blockIdx.x / head_num] == true) {\n        return;\n    }\n    typedef Copy_t<T, size_per_head> copy_t;\n    const int                        elems_per_thread = size_per_head / WARP_SIZE;\n    union Access_t {\n        copy_t v;\n        T      x[elems_per_thread];  // supported size 1,2,4\n    };\n    typedef struct Float_n_t {\n        float x[elems_per_thread];  // supported size 1,2,4\n    } float_n_t;\n\n    __shared__ float_n_t    sq[block_sz];\n    extern __shared__ float logits[];  // use to store the logits from [0~step]\n\n    const int warp_id  = threadIdx.x / WARP_SIZE;\n    const int warp_num = block_sz / WARP_SIZE;\n\n    typedef hipcub::BlockReduce<float, block_sz>          MaxValBlockReduce;\n    typedef hipcub::BlockReduce<float, block_sz>          BlockReduce;\n    __shared__ typename MaxValBlockReduce::TempStorage max_val_block_temp_storage;\n    __shared__ typename BlockReduce::TempStorage       block_temp_storage;\n\n    __shared__ typename hipcub::WarpReduce<float>::TempStorage temp_storage[warp_num];\n\n    const int tid     = threadIdx.x;\n    const int bid     = blockIdx.x / head_num;\n    const int head_id = blockIdx.x % head_num;\n\n    int length = __ldg(&length_per_sample[bid]);\n\n    const int lane_id = tid % WARP_SIZE;\n\n    int qkv_id      = bid * head_num * size_per_head + head_id * size_per_head;\n    int qkv_bias_id = head_id * size_per_head;\n\n    int key_value_id = bid * (seq_len * head_num * size_per_head) + +head_id * size_per_head;\n\n    const bool do_ia3   = step == 1 && ia3_tasks != nullptr;\n    const int  ia3_task = do_ia3 ? ia3_tasks[bid] : 0;\n\n    query_buf   = &query_buf[qkv_id];\n    K_bias      = &K_bias[qkv_bias_id];\n    key_cache   = &key_cache[key_value_id];\n    Q_bias      = &Q_bias[qkv_bias_id];\n    V_bias      = &V_bias[qkv_bias_id];\n    value_cache = &value_cache[key_value_id];\n    context_buf = &context_buf[qkv_id];\n\n    Access_t bias_r, key_val_r, query_buf_r;\n\n    // each warp will have its own copy of sq\n    query_buf_r.v = *((copy_t*)query_buf + lane_id);\n    bias_r.v      = *((copy_t*)Q_bias + lane_id);\n    float qb_r[elems_per_thread];\n    for (int i = 0; i < elems_per_thread; ++i) {\n        qb_r[i] = (float)query_buf_r.x[i] + (float)bias_r.x[i];\n    }\n\n    // offset for each step\n    int offset = head_num * size_per_head;\n\n    bias_r.v = *((copy_t*)K_bias + lane_id);\n    for (int ite = warp_id; ite < length; ite += warp_num) {\n        key_val_r.v = *((copy_t*)&key_cache[ite * offset] + lane_id);\n\n        // For the first step, we should add bias to key memory cache.\n        // The KV memory cache only need to be updated at the first step.\n        if (step == 1) {\n            for (int i = 0; i < elems_per_thread; i++) {\n                key_val_r.x[i] = (float)key_val_r.x[i] + (float)bias_r.x[i];\n                if (do_ia3) {\n                    key_val_r.x[i] = (float)key_val_r.x[i]\n                                     * (float)(ia3_key_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n            }\n            *((copy_t*)&key_cache[ite * offset] + lane_id) = key_val_r.v;\n        }\n        float val = 0.f;\n        for (int i = 0; i < elems_per_thread; i++) {\n            val = val + (float)key_val_r.x[i] * qb_r[i] * scalar;\n        }\n        float qk = hipcub::WarpReduce<float>(temp_storage[warp_id]).Sum(val);\n        if (lane_id == 0) {\n            logits[ite] = qk;\n        }\n    }\n    __syncthreads();\n\n    __shared__ float s_max_val, s_sum;\n    float            local_i = -1e20f;\n    for (int i = tid; i < length; i += blockDim.x) {\n        local_i = max(local_i, logits[i]);\n    }\n\n    float max_val = MaxValBlockReduce(max_val_block_temp_storage).Reduce(local_i, hipcub::Max());\n    if (tid == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    float local_o = 0.0f;\n    for (int i = tid; i < length; i += blockDim.x) {\n        logits[i] = __expf(logits[i] - s_max_val);\n        local_o += logits[i];\n    }\n    float val = BlockReduce(block_temp_storage).Sum(local_o);\n\n    if (tid == 0) {\n        s_sum = val + 1e-6;\n    }\n    __syncthreads();\n\n    float s_sum_inverse = __fdividef(1.0f, s_sum);\n    for (int i = tid; i < length; i += blockDim.x) {\n        logits[i] = logits[i] * s_sum_inverse;\n    }\n    __syncthreads();\n\n    // This optimization introduces discrepancy because of different order in FP32 summation\n    float sum_r[elems_per_thread] = {0.f};\n    bias_r.v                      = *((copy_t*)V_bias + lane_id);\n    for (int ite = warp_id; ite < length; ite += warp_num) {\n        key_val_r.v = *((copy_t*)&value_cache[ite * offset] + lane_id);\n\n        // For the first step, we should add bias to key memory cache.\n        if (step == 1) {\n            for (int i = 0; i < elems_per_thread; i++) {\n                key_val_r.x[i] = (float)key_val_r.x[i] + (float)bias_r.x[i];\n                if (do_ia3) {\n                    key_val_r.x[i] =\n                        (float)key_val_r.x[i]\n                        * (float)(ia3_value_weights[(ia3_task * head_num + head_id) * size_per_head + tid]);\n                }\n            }\n            *((copy_t*)&value_cache[ite * offset] + lane_id) = key_val_r.v;\n        }\n        for (int i = 0; i < elems_per_thread; ++i) {\n            sum_r[i] += (float)key_val_r.x[i] * logits[ite];\n        }\n    }\n    for (int i = 0; i < elems_per_thread; i++) {\n        sq[warp_id * WARP_SIZE + lane_id].x[i] = sum_r[i];\n    }\n    __syncthreads();\n    if (threadIdx.x < WARP_SIZE) {\n#pragma unroll\n        for (int j = 1; j < warp_num; j++) {\n            for (int i = 0; i < elems_per_thread; ++i) {\n                sum_r[i] = sum_r[i] + (float)sq[j * WARP_SIZE + threadIdx.x].x[i];\n            }\n        }\n    }\n    __syncthreads();\n#pragma unroll\n    for (int i = 0; i < elems_per_thread; i++) {\n        key_val_r.x[i] = sum_r[i];\n    }\n    if (threadIdx.x < WARP_SIZE) {\n        *((copy_t*)context_buf + lane_id) = key_val_r.v;\n    }\n}\n\ntemplate<typename T>\nstruct CATypeConverter {\n    using Type = T;\n};\n\ntemplate<>\nstruct CATypeConverter<half> {\n    using Type = uint16_t;\n};\n\ntemplate<typename T>\nvoid cross_attention_dispatch(T*                               query_buf,\n                              const T*                         Q_bias,\n                              T*                               key_cache,\n                              const T*                         K_bias,\n                              T*                               value_cache,\n                              const T*                         V_bias,\n                              const int*                       length,\n                              T*                               context_buf,\n                              const bool*                      finished,\n                              const int                        max_batch_size,\n                              const int                        inference_batch_size,\n                              const int                        head_num,\n                              const int                        size_per_head,\n                              const int                        step,\n                              const int                        memory_max_len,\n                              const bool                       batch_major_cache,\n                              const float                      q_scaling,\n                              outputCrossAttentionParam<float> output_cross_attention_params,\n                              const int*                       ia3_tasks,\n                              const T*                         ia3_key_weights,\n                              const T*                         ia3_value_weights,\n                              hipStream_t                     stream)\n{\n    if (!batch_major_cache) {\n        const int block_sz = ATTENTION_BLOCK_SIZE;\n        float     scalar   = 1.f / (sqrtf(size_per_head * 1.0f) * q_scaling);\n\n        dim3 grid(inference_batch_size * head_num);\n\n        int cond = size_per_head * ((ATTENION_OPT) ? 1 : 0);\n        switch (cond) {\n            case 32:\n                cross_attention_kernel_opt<T, 32, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            case 64:\n                cross_attention_kernel_opt<T, 64, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            case 128:\n                cross_attention_kernel_opt<T, 128, block_sz>\n                    <<<grid, block_sz, sizeof(float) * memory_max_len, stream>>>(query_buf,\n                                                                                 Q_bias,\n                                                                                 key_cache,\n                                                                                 K_bias,\n                                                                                 value_cache,\n                                                                                 V_bias,\n                                                                                 length,\n                                                                                 context_buf,\n                                                                                 finished,\n                                                                                 max_batch_size,\n                                                                                 head_num,\n                                                                                 step,\n                                                                                 memory_max_len,\n                                                                                 scalar,\n                                                                                 ia3_tasks,\n                                                                                 ia3_key_weights,\n                                                                                 ia3_value_weights);\n                break;\n            default:\n                // default path\n\n                int block_size = 128;\n\n                if (memory_max_len <= 64) {\n                    block_size = 64;\n                }\n                else if (memory_max_len <= 128 && memory_max_len > size_per_head) {\n                    block_size = 128;\n                }\n                else if (memory_max_len > 128 && memory_max_len <= 256) {\n                    block_size = 256;\n                }\n                else if (memory_max_len > 256 && memory_max_len <= 512) {\n                    block_size = 512;\n                }\n                else {\n                    block_size = 1024;\n                }\n\n                if (block_size < size_per_head) {\n                    block_size = size_per_head;\n                }\n\n                assert(block_size <= 1024);\n                dim3 block(block_size);\n\n                int shared_size = sizeof(T) * (size_per_head + memory_max_len);\n                cross_attention_kernel<T><<<grid, block, shared_size, stream>>>(query_buf,\n                                                                                Q_bias,\n                                                                                key_cache,\n                                                                                K_bias,\n                                                                                value_cache,\n                                                                                V_bias,\n                                                                                length,\n                                                                                context_buf,\n                                                                                finished,\n                                                                                max_batch_size,\n                                                                                head_num,\n                                                                                size_per_head,\n                                                                                step,\n                                                                                memory_max_len,\n                                                                                scalar,\n                                                                                ia3_tasks,\n                                                                                ia3_key_weights,\n                                                                                ia3_value_weights);\n        }\n    }\n    else {\n        assert(step > 0);\n        // assert(size_per_head == 32 || size_per_head == 64 || size_per_head == 128);\n        // using DataType = typename std::conditional<sizeof(T) == 4, float, uint16_t>::type;\n        using DataType = typename CATypeConverter<T>::Type;\n        // Prepare the parameters.\n        Cross_multihead_attention_params<DataType> params;\n        params.q_bias = reinterpret_cast<const DataType*>(Q_bias);\n        params.k_bias = reinterpret_cast<const DataType*>(K_bias);\n        params.v_bias = reinterpret_cast<const DataType*>(V_bias);\n\n        // Set the output buffer.\n        params.out = reinterpret_cast<DataType*>(context_buf);\n\n        // Set the input buffers.\n        params.q        = reinterpret_cast<const DataType*>(query_buf);\n        params.k        = nullptr;\n        params.v        = nullptr;\n        params.stride   = 0;\n        params.finished = const_cast<bool*>(finished);\n\n        params.memory_length_per_sample = const_cast<int*>(length);\n\n        params.k_cache    = reinterpret_cast<DataType*>(key_cache);\n        params.v_cache    = reinterpret_cast<DataType*>(value_cache);\n        params.batch_size = inference_batch_size;\n        // TODO(bhsueh) We can use batch but not batch * beam_width in k/v cache in cross attention\n        // because they are same for all beams.\n        params.beam_width           = 1;  // We don't care the beam_width in cross attention, set to 1 is enough.\n        params.memory_max_len       = memory_max_len;\n        params.timestep             = step - 1;\n        params.num_heads            = head_num;\n        params.hidden_size_per_head = size_per_head;\n        params.inv_sqrt_dh          = 1.F / (sqrtf((float)params.hidden_size_per_head) * q_scaling);\n\n        // output cross attentions\n        params.max_decoder_seq_len        = output_cross_attention_params.max_decoder_seq_len;\n        params.cross_attention_out        = output_cross_attention_params.cross_attention_out;\n        params.is_return_cross_attentions = output_cross_attention_params.is_return_cross_attentions;\n\n        params.ia3_tasks         = ia3_tasks;\n        params.ia3_key_weights   = reinterpret_cast<const DataType*>(ia3_key_weights);\n        params.ia3_value_weights = reinterpret_cast<const DataType*>(ia3_value_weights);\n\n        cross_multihead_attention(params, stream);\n    }\n}\n\ntemplate void cross_attention_dispatch(float*                           query_buf,\n                                       const float*                     Q_bias,\n                                       float*                           key_cache,\n                                       const float*                     K_bias,\n                                       float*                           value_cache,\n                                       const float*                     V_bias,\n                                       const int*                       length,\n                                       float*                           context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const float*                     ia3_key_weights,\n                                       const float*                     ia3_value_weights,\n                                       hipStream_t                     stream);\n\ntemplate void cross_attention_dispatch(half*                            query_buf,\n                                       const half*                      Q_bias,\n                                       half*                            key_cache,\n                                       const half*                      K_bias,\n                                       half*                            value_cache,\n                                       const half*                      V_bias,\n                                       const int*                       length,\n                                       half*                            context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const half*                      ia3_key_weights,\n                                       const half*                      ia3_value_weights,\n                                       hipStream_t                     stream);\n\n#ifdef ENABLE_BF16\ntemplate void cross_attention_dispatch(__hip_bfloat16*                   query_buf,\n                                       const __hip_bfloat16*             Q_bias,\n                                       __hip_bfloat16*                   key_cache,\n                                       const __hip_bfloat16*             K_bias,\n                                       __hip_bfloat16*                   value_cache,\n                                       const __hip_bfloat16*             V_bias,\n                                       const int*                       length,\n                                       __hip_bfloat16*                   context_buf,\n                                       const bool*                      finished,\n                                       const int                        max_batch_size,\n                                       const int                        inference_batch_size,\n                                       const int                        head_num,\n                                       const int                        size_per_head,\n                                       const int                        step,\n                                       const int                        memory_max_len,\n                                       const bool                       batch_major_cache,\n                                       const float                      q_scaling,\n                                       outputCrossAttentionParam<float> output_cross_attention_params,\n                                       const int*                       ia3_tasks,\n                                       const __hip_bfloat16*             ia3_key_weights,\n                                       const __hip_bfloat16*             ia3_value_weights,\n                                       hipStream_t                     stream);\n#endif\n\n// Currently need to transpose at the first step in Cross attention\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_mem_k_cache(\n    T* k_dst, const T* k_src, const int head_num, const int size_per_head, const int max_seq_len)\n{\n    // B, L, H, Dh -> B, H, Dh/x, L, x\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = (sizeof(T) == 4) ? 4 : 8;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * max_seq_len\n                                                  + head_id * size_per_head);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    key_dst[out_idx] = key_src[k_seq_len_id * head_num * size_per_head_div_x + k_head_size_id];\n}\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_mem_v_cache(\n    T* v_dst, const T* v_src, const int head_num, const int size_per_head, const int max_seq_len)\n{\n    // B, L, H, Dh -> B, H, L, Dh\n    const int batch_id = blockIdx.y;\n    const int head_id  = blockIdx.z;\n\n    // 16 byte loads will handle \"x\" dimension\n    auto val_src = reinterpret_cast<const uint4*>(v_src + batch_id * head_num * size_per_head * max_seq_len\n                                                  + head_id * size_per_head);\n    auto val_dst = reinterpret_cast<uint4*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int out_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    constexpr int X_ELEMS             = (sizeof(T) == 4) ? 4 : 8;\n    const int     size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int v_head_size_id = idx % size_per_head_div_x;\n    idx                      = (idx - v_head_size_id) / size_per_head_div_x;\n    const int v_seq_len_id   = idx % max_seq_len;\n\n    val_dst[out_idx] = val_src[v_seq_len_id * head_num * size_per_head_div_x + v_head_size_id];\n}\n\ntemplate<typename T>\nvoid transpose_4d_batch_major_memory_kernelLauncher(T*           dst,\n                                                    const T*     src,\n                                                    const int    local_batch_size,\n                                                    const int    max_seq_len,\n                                                    const int    size_per_head,\n                                                    const int    local_head_num,\n                                                    const bool   k_cache,\n                                                    hipStream_t stream)\n{\n    constexpr int block_sz = 128;\n\n    constexpr int x    = (sizeof(T) == 4) ? 4 : 8;\n    int           size = max_seq_len * size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n\n    if (k_cache) {\n        transpose_4d_batch_major_mem_k_cache<<<grid, block_sz, 0, stream>>>(\n            dst, src, local_head_num, size_per_head, max_seq_len);\n    }\n    else {\n        transpose_4d_batch_major_mem_v_cache<<<grid, block_sz, 0, stream>>>(\n            dst, src, local_head_num, size_per_head, max_seq_len);\n    }\n}\n\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(float*       dst,\n                                                             const float* src,\n                                                             const int    local_batch_size,\n                                                             const int    max_seq_len,\n                                                             const int    size_per_head,\n                                                             const int    local_head_num,\n                                                             const bool   k_cache,\n                                                             hipStream_t stream);\n\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(half*        dst,\n                                                             const half*  src,\n                                                             const int    local_batch_size,\n                                                             const int    max_seq_len,\n                                                             const int    size_per_head,\n                                                             const int    local_head_num,\n                                                             const bool   k_cache,\n                                                             hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void transpose_4d_batch_major_memory_kernelLauncher(__hip_bfloat16*       dst,\n                                                             const __hip_bfloat16* src,\n                                                             const int            local_batch_size,\n                                                             const int            max_seq_len,\n                                                             const int            size_per_head,\n                                                             const int            local_head_num,\n                                                             const bool           k_cache,\n                                                             hipStream_t         stream);\n#endif\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n    if (is_allocate_buffer_ == false) {\n        q_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(q_buf_, sizeof(T) * max_batch_size_ * hidden_units_, false));\n        context_buf_ = reinterpret_cast<T*>(\n            allocator_->reMalloc(context_buf_, sizeof(T) * max_batch_size_ * hidden_units_, false));\n\n        if (is_batch_major_cache_) {\n            mem_cache_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(\n                mem_cache_buf_, sizeof(T) * max_batch_size_ * max_mem_seq_len_ * hidden_units_, false));\n        }\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::allocateBuffer(size_t batch_size, size_t max_mem_seq_len)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    q_buf_ = reinterpret_cast<T*>(allocator_->reMalloc(q_buf_, sizeof(T) * batch_size * hidden_units_, false));\n    context_buf_ =\n        reinterpret_cast<T*>(allocator_->reMalloc(context_buf_, sizeof(T) * batch_size * hidden_units_, false));\n\n    if (is_batch_major_cache_) {\n        mem_cache_buf_ = reinterpret_cast<T*>(\n            allocator_->reMalloc(mem_cache_buf_, sizeof(T) * batch_size * max_mem_seq_len * hidden_units_, false));\n    }\n    is_allocate_buffer_ = true;\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::freeBuffer()\n{\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&q_buf_));\n        allocator_->free((void**)(&context_buf_));\n        if (is_batch_major_cache_) {\n            allocator_->free((void**)(&mem_cache_buf_));\n        }\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          size_t           d_model,\n                                                          const float      q_scaling,\n                                                          hipStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_size_(max_batch_size),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    hidden_units_(head_num_ * size_per_head_),\n    d_model_(d_model),\n    q_scaling_(q_scaling)\n{\n    FT_CHECK(size_per_head_ == 32 || size_per_head_ == 48 || size_per_head_ == 64 || size_per_head_ == 80\n             || size_per_head_ == 96 || size_per_head_ == 112 || size_per_head_ == 128 || size_per_head_ == 144\n             || size_per_head_ == 160 || size_per_head_ == 192 || size_per_head_ == 224 || size_per_head_ == 256);\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          hipStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    DecoderCrossAttentionLayer<T>(max_batch_size,\n                                  head_num,\n                                  size_per_head,\n                                  head_num * size_per_head,\n                                  1.0f,\n                                  stream,\n                                  cublas_wrapper,\n                                  allocator,\n                                  is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(size_t           max_batch_size,\n                                                          size_t           head_num,\n                                                          size_t           size_per_head,\n                                                          const float      q_scaling,\n                                                          hipStream_t     stream,\n                                                          cublasMMWrapper* cublas_wrapper,\n                                                          IAllocator*      allocator,\n                                                          bool             is_free_buffer_after_forward):\n    DecoderCrossAttentionLayer<T>(max_batch_size,\n                                  head_num,\n                                  size_per_head,\n                                  head_num * size_per_head,\n                                  q_scaling,\n                                  stream,\n                                  cublas_wrapper,\n                                  allocator,\n                                  is_free_buffer_after_forward)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::DecoderCrossAttentionLayer(DecoderCrossAttentionLayer<T> const& attention_layer):\n    DecoderCrossAttentionLayer(attention_layer.max_batch_size_,\n                               attention_layer.head_num_,\n                               attention_layer.size_per_head_,\n                               attention_layer.d_model_,\n                               attention_layer.q_scaling_,\n                               attention_layer.stream_,\n                               attention_layer.cublas_wrapper_,\n                               attention_layer.allocator_,\n                               attention_layer.is_free_buffer_after_forward_)\n{\n}\n\ntemplate<typename T>\nDecoderCrossAttentionLayer<T>::~DecoderCrossAttentionLayer()\n{\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid DecoderCrossAttentionLayer<T>::forward(TensorMap*                output_tensors,\n                                            TensorMap*                input_tensors,\n                                            const AttentionWeight<T>* attention_weights)\n{\n    // input tensors:\n    //      attention_input [batch_size, d_model],\n    //      encoder_output [batch_size, mem_max_seq_len, memory_d_model],\n    //      encoder_sequence_length [batch_size],\n    //      step [1] on cpu\n    //      finished [batch_size] (optional)\n    //      ia3_tasks [batch_size] (optional)\n\n    // output tensors:\n    //      decoder_layer_output [batch_size, d_model],\n    //      key_mem_cache [batch_size, head_num, size_per_head // x, mem_max_seq_len, x], where x = 16 / sizeof(T)\n    //      value_mem_cache [batch_size, head_num, mem_max_seq_len, size_per_head]\n    //      cross_attentions [batch_size, head_num, max_decoder_seq_len, mem_max_seq_len] optional float*\n    FT_LOG_DEBUG(\"%s\", __PRETTY_FUNCTION__);\n    allocateBuffer(input_tensors->at(\"input_query\").shape[0], input_tensors->at(\"encoder_output\").shape[1]);\n\n    const T*    attention_input        = input_tensors->getPtr<T>(\"input_query\");\n    Tensor      encoder_output_tensor  = input_tensors->at(\"encoder_output\");\n    const int*  memory_sequence_length = input_tensors->getPtr<int>(\"encoder_sequence_length\");\n    const int   step                   = input_tensors->getVal<int>(\"step\");\n    const bool* finished               = input_tensors->getPtr<bool>(\"finished\", nullptr);\n    const bool  has_ia3                = input_tensors->isExist(\"ia3_tasks\");\n\n    T* attention_out   = output_tensors->getPtr<T>(\"hidden_features\");\n    T* key_mem_cache   = output_tensors->getPtr<T>(\"key_cache\");\n    T* value_mem_cache = output_tensors->getPtr<T>(\"value_cache\");\n\n    const bool output_cross_attentions = output_tensors->isExist(\"cross_attentions\");\n    const int  max_decoder_seq_len     = output_cross_attentions ? output_tensors->at(\"cross_attentions\").shape[2] : 0;\n\n    const int batch_size      = input_tensors->at(\"input_query\").shape[0];\n    const int mem_max_seq_len = encoder_output_tensor.shape[1];\n    cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                          HIPBLAS_OP_N,\n                          hidden_units_,  // n\n                          batch_size,\n                          d_model_,  // k\n                          attention_weights->query_weight.kernel,\n                          hidden_units_,  // n\n                          attention_input,\n                          d_model_,  // k\n                          q_buf_,\n                          hidden_units_ /* n */);\n\n    if (step == 1) {\n        if (is_batch_major_cache_) {\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->key_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  mem_cache_buf_,\n                                  hidden_units_);\n            transpose_4d_batch_major_memory_kernelLauncher<T>(\n                key_mem_cache, mem_cache_buf_, batch_size, mem_max_seq_len, size_per_head_, head_num_, true, stream_);\n            sync_check_cuda_error();\n\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->value_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  mem_cache_buf_,\n                                  hidden_units_);\n            transpose_4d_batch_major_memory_kernelLauncher<T>(value_mem_cache,\n                                                              mem_cache_buf_,\n                                                              batch_size,\n                                                              mem_max_seq_len,\n                                                              size_per_head_,\n                                                              head_num_,\n                                                              false,\n                                                              stream_);\n            sync_check_cuda_error();\n        }\n        else {\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->key_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  key_mem_cache,\n                                  hidden_units_);\n\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  hidden_units_,\n                                  batch_size * mem_max_seq_len,\n                                  encoder_output_tensor.shape[2],\n                                  attention_weights->value_weight.kernel,\n                                  hidden_units_,\n                                  encoder_output_tensor.getPtr<T>(),\n                                  encoder_output_tensor.shape[2],\n                                  value_mem_cache,\n                                  hidden_units_);\n        }\n    }\n    sync_check_cuda_error();\n\n    outputCrossAttentionParam<float> output_attention_param{};\n    // output cross attentions\n    if (output_cross_attentions) {\n        output_attention_param.max_decoder_seq_len        = max_decoder_seq_len;\n        output_attention_param.cross_attention_out        = output_tensors->at(\"cross_attentions\").getPtr<float>();\n        output_attention_param.is_return_cross_attentions = true;\n    }\n\n    cross_attention_dispatch<T>(q_buf_,\n                                attention_weights->query_weight.bias,\n                                key_mem_cache,\n                                attention_weights->key_weight.bias,\n                                value_mem_cache,\n                                attention_weights->value_weight.bias,\n                                memory_sequence_length,\n                                context_buf_,\n                                finished,\n                                batch_size,\n                                batch_size,\n                                head_num_,\n                                size_per_head_,\n                                step,\n                                mem_max_seq_len,\n                                is_batch_major_cache_,\n                                q_scaling_,\n                                output_attention_param,\n                                has_ia3 ? input_tensors->at(\"ia3_tasks\").getPtr<const int>() : nullptr,\n                                has_ia3 ? attention_weights->ia3_key_weight.kernel : nullptr,\n                                has_ia3 ? attention_weights->ia3_value_weight.kernel : nullptr,\n                                stream_);\n    sync_check_cuda_error();\n    cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                          HIPBLAS_OP_N,\n                          d_model_,  // n\n                          batch_size,\n                          hidden_units_,  // k\n                          attention_weights->attention_output_weight.kernel,\n                          d_model_,  // n\n                          context_buf_,\n                          hidden_units_,  // k\n                          attention_out,\n                          d_model_ /* n */);\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate class DecoderCrossAttentionLayer<float>;\ntemplate class DecoderCrossAttentionLayer<half>;\n#ifdef ENABLE_BF16\ntemplate class DecoderCrossAttentionLayer<__hip_bfloat16>;\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/attention_layers/DecoderCrossAttentionLayer.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/layers/attention_layers/FusedAttentionLayer.h\"\n\nnamespace fastertransformer {\n\n__global__ void trt_add_QKV_bias(half2*       qkv_buf,\n                                 const half2* Q,\n                                 const half2* bias_Q,\n                                 const half2* K,\n                                 const half2* bias_K,\n                                 const half2* V,\n                                 const half2* bias_V,\n                                 const int    valid_word_num,\n                                 const int    head_num,\n                                 const int    size_per_head)\n{\n    // Add bias, and then transpose from\n    // [3, valid_word_num, head, size] -> [valid_word_num, head, 3, size]\n\n    // const int seq_id = blockIdx.x % valid_word_num;\n    // const int qkv_id = (blockIdx.x - seq_id) / valid_word_num;\n    const int seq_id = blockIdx.x;\n\n    for (int index = threadIdx.x; index < head_num * size_per_head; index += blockDim.x) {\n        const int size_id = index % size_per_head;\n        const int head_id = (index - size_id) / size_per_head;\n\n        const int target_offset = blockIdx.x * head_num * 3 * size_per_head + head_id * 3 * size_per_head;\n        const int src_id        = seq_id * head_num * size_per_head + index;\n\n        qkv_buf[target_offset + 0 * size_per_head + size_id] = Q[src_id] + bias_Q[index];\n        qkv_buf[target_offset + 1 * size_per_head + size_id] = K[src_id] + bias_K[index];\n        qkv_buf[target_offset + 2 * size_per_head + size_id] = V[src_id] + bias_V[index];\n    }\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::invokeTrtAddQkvBias(size_t token_num, const AttentionWeight<T>* attention_weights)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    dim3 grid(token_num);\n    dim3 block(min((int)(head_num_ * size_per_head_ / 2), 512));\n\n    trt_add_QKV_bias<<<grid, block, 0, stream_>>>((half2*)qkv_buf_,\n                                                  (const half2*)q_buf_,\n                                                  (const half2*)attention_weights->query_weight.bias,\n                                                  (const half2*)k_buf_,\n                                                  (const half2*)attention_weights->key_weight.bias,\n                                                  (const half2*)v_buf_,\n                                                  (const half2*)attention_weights->value_weight.bias,\n                                                  token_num,\n                                                  head_num_,\n                                                  size_per_head_ / 2);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::forward(TensorMap*                output_tensors,\n                                     TensorMap*                input_tensors,\n                                     const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors: [input_query (h_token_num, d_model),\n    //                 attention_mask (batch, 1, seqlen, seqlen),\n    //                 padding_offset (batch + 1 or batch * 2 + 1))]\n    // If padding_offset.data is nullptr, then not remove padding\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n\n    const int request_batch_size = input_tensors->at(\"attention_mask\").shape[0];\n    const int request_seq_len    = input_tensors->at(\"attention_mask\").shape[2];\n    allocateBuffer(request_batch_size, request_seq_len);\n\n    T*         attention_out  = output_tensors->getPtr<T>(\"hidden_features\");\n    const T*   from_tensor    = input_tensors->getPtr<T>(\"input_query\");\n    const T*   attention_mask = input_tensors->getPtr<T>(\"attention_mask\");\n    const int* padding_offset = input_tensors->getPtr<int>(\"padding_offset\");\n\n    size_t m_tmp = input_tensors->at(\"input_query\").shape[0];\n    if (m_tmp % 8 != 0) {\n        m_tmp = (m_tmp / 8 + 1) * 8;\n    }\n    const size_t m = input_tensors->at(\"input_query\").shape[0];\n    int          k = d_model_;\n    int          n = hidden_units_;\n\n#ifdef SPARSITY_ENABLED\n    const size_t m_padded = m_tmp;\n    if (sparse_ && cublas_wrapper_->isUseSparse(1, n, m, k)) {\n        cublas_wrapper_->SpGemm(\n            CUBLAS_OP_N, CUBLAS_OP_N, n, m_padded, k, attention_weights->query_weight.sp_kernel, from_tensor, q_buf_);\n        cublas_wrapper_->SpGemm(\n            CUBLAS_OP_N, CUBLAS_OP_N, n, m_padded, k, attention_weights->key_weight.sp_kernel, from_tensor, k_buf_);\n        cublas_wrapper_->SpGemm(\n            CUBLAS_OP_N, CUBLAS_OP_N, n, m_padded, k, attention_weights->value_weight.sp_kernel, from_tensor, v_buf_);\n    }\n    else {\n#endif\n        const bool is_batched_QKV_ = cublas_wrapper_->isFuseBatchGemm(3, n, m, k);\n        if (is_batched_QKV_) {\n            const T* hA[]{attention_weights->query_weight.kernel,\n                          attention_weights->key_weight.kernel,\n                          attention_weights->value_weight.kernel,\n                          nullptr,\n                          from_tensor,\n                          from_tensor,\n                          from_tensor,\n                          nullptr,\n                          q_buf_,\n                          k_buf_,\n                          v_buf_,\n                          nullptr};\n            // Note: Here, we assume the weights of each time may be different.\n            // If we can preprocess these weights before inference, we can reduce the overhead\n            // caused by cudaMemcpyAsync\n            check_cuda_error(\n                cudaMemcpyAsync((void*)batch_qkv_kernel_ptr_, hA, sizeof(T*) * 12, cudaMemcpyHostToDevice, stream_));\n            cublas_wrapper_->batchedGemm(CUBLAS_OP_N,\n                                         CUBLAS_OP_N,\n                                         n,\n                                         m,\n                                         k,\n                                         (const void* const*)batch_qkv_kernel_ptr_,\n                                         n,\n                                         (const void* const*)batch_qkv_input_ptr_,\n                                         k,\n                                         (void* const*)batch_qkv_buf_ptr_,\n                                         n,\n                                         3);\n        }\n        else {\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  n,\n                                  m,\n                                  k,\n                                  attention_weights->query_weight.kernel,\n                                  n,\n                                  from_tensor,\n                                  k,\n                                  q_buf_,\n                                  n);\n            cublas_wrapper_->Gemm(\n                CUBLAS_OP_N, CUBLAS_OP_N, n, m, k, attention_weights->key_weight.kernel, n, from_tensor, k, k_buf_, n);\n            cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                                  CUBLAS_OP_N,\n                                  n,\n                                  m,\n                                  k,\n                                  attention_weights->value_weight.kernel,\n                                  n,\n                                  from_tensor,\n                                  k,\n                                  v_buf_,\n                                  n);\n        }\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    invokeTrtAddQkvBias(m, attention_weights);\n    sync_check_cuda_error();\n\n    int S = dispatcher_fp16->getSFromMaxSeqLen(request_seq_len);\n    FT_CHECK(dispatcher_fp16->isValid(S, false));\n    const int B = input_tensors->at(\"padding_offset\").shape[0] - 1;\n    dispatcher_fp16->setup(S, B);\n    dispatcher_fp16->run(qkv_buf_, nullptr, padding_offset, attn_workspace_, qkv_buf_2_, stream_);\n    sync_check_cuda_error();\n\n    k = hidden_units_;\n    n = d_model_;\n#ifdef SPARSITY_ENABLED\n    if (sparse_ && cublas_wrapper_->isUseSparse(1, n, m, k)) {\n        cublas_wrapper_->SpGemm(CUBLAS_OP_N,\n                                CUBLAS_OP_N,\n                                n,\n                                m_padded,\n                                k,\n                                attention_weights->attention_output_weight.sp_kernel,\n                                qkv_buf_2_,\n                                attention_out);\n    }\n    else {\n#endif\n        cublas_wrapper_->Gemm(CUBLAS_OP_N,\n                              CUBLAS_OP_N,\n                              n,\n                              m,\n                              k,\n                              attention_weights->attention_output_weight.kernel,\n                              n,\n                              qkv_buf_2_,\n                              k,\n                              attention_out,\n                              n);\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::FusedAttentionLayer(size_t           max_batch_size,\n                                            size_t           max_seq_len,\n                                            size_t           head_num,\n                                            size_t           size_per_head,\n                                            size_t           d_model,\n                                            int              sm,\n                                            float            q_scaling,\n                                            cudaStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward,\n                                            bool             sparse):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    d_model_(d_model),\n    sm_(sm),\n    q_scaling_(q_scaling),\n    sparse_(sparse)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (((sm_ == kSM_70 || sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72) && size_per_head_ == 64)\n        || ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75) && size_per_head_ == 32)) {\n        dispatcher_fp16.reset(new FusedMHARunnerFP16v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayer not support \\n\"));\n    }\n    hidden_units_ = head_num_ * size_per_head_;\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::FusedAttentionLayer(FusedAttentionLayer<T> const& attention_layer):\n    FusedAttentionLayer(0,\n                        0,\n                        attention_layer.head_num_,\n                        attention_layer.size_per_head_,\n                        attention_layer.d_model_,\n                        attention_layer.sm_,\n                        attention_layer.q_scaling_,\n                        attention_layer.stream_,\n                        attention_layer.cublas_wrapper_,\n                        attention_layer.allocator_,\n                        attention_layer.is_free_buffer_after_forward_,\n                        attention_layer.sparse_)\n{\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::~FusedAttentionLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::allocateBuffer(size_t batch_size, size_t seq_len)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    q_buf_          = (T*)allocator_->reMalloc(q_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    k_buf_          = (T*)allocator_->reMalloc(k_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    v_buf_          = (T*)allocator_->reMalloc(v_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    qkv_buf_        = (T*)allocator_->reMalloc(qkv_buf_, sizeof(T) * 3 * batch_size * seq_len * hidden_units_, false);\n    qkv_buf_2_      = (T*)allocator_->reMalloc(qkv_buf_2_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    attn_workspace_ = (T*)allocator_->reMalloc(attn_workspace_, dispatcher_fp16->getWorkspaceSize(), false);\n\n    batch_qkv_kernel_ptr_ = (T**)allocator_->reMalloc(batch_qkv_kernel_ptr_, sizeof(T*) * 12, false);\n    batch_qkv_input_ptr_  = batch_qkv_kernel_ptr_ + 4;\n    batch_qkv_buf_ptr_    = batch_qkv_input_ptr_ + 4;\n    is_allocate_buffer_   = true;\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&q_buf_));\n        allocator_->free((void**)(&k_buf_));\n        allocator_->free((void**)(&v_buf_));\n        allocator_->free((void**)(&qkv_buf_));\n        allocator_->free((void**)(&qkv_buf_2_));\n        allocator_->free((void**)(&attn_workspace_));\n        allocator_->free((void**)(&batch_qkv_kernel_ptr_));\n        sync_check_cuda_error();\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayer<T>::isValidSeqLen(const size_t seq_len)\n{\n    return true;\n}\n\ntemplate class FusedAttentionLayer<float>;\ntemplate class FusedAttentionLayer<half>;\n#ifdef ENABLE_BF16\ntemplate class FusedAttentionLayer<__nv_bfloat16>;\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/layers/attention_layers/FusedAttentionLayer.h\"\n\nnamespace fastertransformer {\n\n__global__ void trt_add_QKV_bias(half2*       qkv_buf,\n                                 const half2* Q,\n                                 const half2* bias_Q,\n                                 const half2* K,\n                                 const half2* bias_K,\n                                 const half2* V,\n                                 const half2* bias_V,\n                                 const int    valid_word_num,\n                                 const int    head_num,\n                                 const int    size_per_head)\n{\n    // Add bias, and then transpose from\n    // [3, valid_word_num, head, size] -> [valid_word_num, head, 3, size]\n\n    // const int seq_id = blockIdx.x % valid_word_num;\n    // const int qkv_id = (blockIdx.x - seq_id) / valid_word_num;\n    const int seq_id = blockIdx.x;\n\n    for (int index = threadIdx.x; index < head_num * size_per_head; index += blockDim.x) {\n        const int size_id = index % size_per_head;\n        const int head_id = (index - size_id) / size_per_head;\n\n        const int target_offset = blockIdx.x * head_num * 3 * size_per_head + head_id * 3 * size_per_head;\n        const int src_id        = seq_id * head_num * size_per_head + index;\n\n        qkv_buf[target_offset + 0 * size_per_head + size_id] = Q[src_id] + bias_Q[index];\n        qkv_buf[target_offset + 1 * size_per_head + size_id] = K[src_id] + bias_K[index];\n        qkv_buf[target_offset + 2 * size_per_head + size_id] = V[src_id] + bias_V[index];\n    }\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::invokeTrtAddQkvBias(size_t token_num, const AttentionWeight<T>* attention_weights)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    dim3 grid(token_num);\n    dim3 block(min((int)(head_num_ * size_per_head_ / 2), 512));\n\n    trt_add_QKV_bias<<<grid, block, 0, stream_>>>((half2*)qkv_buf_,\n                                                  (const half2*)q_buf_,\n                                                  (const half2*)attention_weights->query_weight.bias,\n                                                  (const half2*)k_buf_,\n                                                  (const half2*)attention_weights->key_weight.bias,\n                                                  (const half2*)v_buf_,\n                                                  (const half2*)attention_weights->value_weight.bias,\n                                                  token_num,\n                                                  head_num_,\n                                                  size_per_head_ / 2);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::forward(TensorMap*                output_tensors,\n                                     TensorMap*                input_tensors,\n                                     const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors: [input_query (h_token_num, d_model),\n    //                 attention_mask (batch, 1, seqlen, seqlen),\n    //                 padding_offset (batch + 1 or batch * 2 + 1))]\n    // If padding_offset.data is nullptr, then not remove padding\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n\n    const int request_batch_size = input_tensors->at(\"attention_mask\").shape[0];\n    const int request_seq_len    = input_tensors->at(\"attention_mask\").shape[2];\n    allocateBuffer(request_batch_size, request_seq_len);\n\n    T*         attention_out  = output_tensors->getPtr<T>(\"hidden_features\");\n    const T*   from_tensor    = input_tensors->getPtr<T>(\"input_query\");\n    const T*   attention_mask = input_tensors->getPtr<T>(\"attention_mask\");\n    const int* padding_offset = input_tensors->getPtr<int>(\"padding_offset\");\n\n    size_t m_tmp = input_tensors->at(\"input_query\").shape[0];\n    if (m_tmp % 8 != 0) {\n        m_tmp = (m_tmp / 8 + 1) * 8;\n    }\n    const size_t m = input_tensors->at(\"input_query\").shape[0];\n    int          k = d_model_;\n    int          n = hidden_units_;\n\n#ifdef SPARSITY_ENABLED\n    const size_t m_padded = m_tmp;\n    if (sparse_ && cublas_wrapper_->isUseSparse(1, n, m, k)) {\n        cublas_wrapper_->SpGemm(\n            HIPBLAS_OP_N, HIPBLAS_OP_N, n, m_padded, k, attention_weights->query_weight.sp_kernel, from_tensor, q_buf_);\n        cublas_wrapper_->SpGemm(\n            HIPBLAS_OP_N, HIPBLAS_OP_N, n, m_padded, k, attention_weights->key_weight.sp_kernel, from_tensor, k_buf_);\n        cublas_wrapper_->SpGemm(\n            HIPBLAS_OP_N, HIPBLAS_OP_N, n, m_padded, k, attention_weights->value_weight.sp_kernel, from_tensor, v_buf_);\n    }\n    else {\n#endif\n        const bool is_batched_QKV_ = cublas_wrapper_->isFuseBatchGemm(3, n, m, k);\n        if (is_batched_QKV_) {\n            const T* hA[]{attention_weights->query_weight.kernel,\n                          attention_weights->key_weight.kernel,\n                          attention_weights->value_weight.kernel,\n                          nullptr,\n                          from_tensor,\n                          from_tensor,\n                          from_tensor,\n                          nullptr,\n                          q_buf_,\n                          k_buf_,\n                          v_buf_,\n                          nullptr};\n            // Note: Here, we assume the weights of each time may be different.\n            // If we can preprocess these weights before inference, we can reduce the overhead\n            // caused by cudaMemcpyAsync\n            check_cuda_error(\n                hipMemcpyAsync((void*)batch_qkv_kernel_ptr_, hA, sizeof(T*) * 12, hipMemcpyHostToDevice, stream_));\n            cublas_wrapper_->batchedGemm(HIPBLAS_OP_N,\n                                         HIPBLAS_OP_N,\n                                         n,\n                                         m,\n                                         k,\n                                         (const void* const*)batch_qkv_kernel_ptr_,\n                                         n,\n                                         (const void* const*)batch_qkv_input_ptr_,\n                                         k,\n                                         (void* const*)batch_qkv_buf_ptr_,\n                                         n,\n                                         3);\n        }\n        else {\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  n,\n                                  m,\n                                  k,\n                                  attention_weights->query_weight.kernel,\n                                  n,\n                                  from_tensor,\n                                  k,\n                                  q_buf_,\n                                  n);\n            cublas_wrapper_->Gemm(\n                HIPBLAS_OP_N, HIPBLAS_OP_N, n, m, k, attention_weights->key_weight.kernel, n, from_tensor, k, k_buf_, n);\n            cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                                  HIPBLAS_OP_N,\n                                  n,\n                                  m,\n                                  k,\n                                  attention_weights->value_weight.kernel,\n                                  n,\n                                  from_tensor,\n                                  k,\n                                  v_buf_,\n                                  n);\n        }\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    invokeTrtAddQkvBias(m, attention_weights);\n    sync_check_cuda_error();\n\n    int S = dispatcher_fp16->getSFromMaxSeqLen(request_seq_len);\n    FT_CHECK(dispatcher_fp16->isValid(S, false));\n    const int B = input_tensors->at(\"padding_offset\").shape[0] - 1;\n    dispatcher_fp16->setup(S, B);\n    dispatcher_fp16->run(qkv_buf_, nullptr, padding_offset, attn_workspace_, qkv_buf_2_, stream_);\n    sync_check_cuda_error();\n\n    k = hidden_units_;\n    n = d_model_;\n#ifdef SPARSITY_ENABLED\n    if (sparse_ && cublas_wrapper_->isUseSparse(1, n, m, k)) {\n        cublas_wrapper_->SpGemm(HIPBLAS_OP_N,\n                                HIPBLAS_OP_N,\n                                n,\n                                m_padded,\n                                k,\n                                attention_weights->attention_output_weight.sp_kernel,\n                                qkv_buf_2_,\n                                attention_out);\n    }\n    else {\n#endif\n        cublas_wrapper_->Gemm(HIPBLAS_OP_N,\n                              HIPBLAS_OP_N,\n                              n,\n                              m,\n                              k,\n                              attention_weights->attention_output_weight.kernel,\n                              n,\n                              qkv_buf_2_,\n                              k,\n                              attention_out,\n                              n);\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::FusedAttentionLayer(size_t           max_batch_size,\n                                            size_t           max_seq_len,\n                                            size_t           head_num,\n                                            size_t           size_per_head,\n                                            size_t           d_model,\n                                            int              sm,\n                                            float            q_scaling,\n                                            hipStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward,\n                                            bool             sparse):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    d_model_(d_model),\n    sm_(sm),\n    q_scaling_(q_scaling),\n    sparse_(sparse)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (((sm_ == kSM_70 || sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72) && size_per_head_ == 64)\n        || ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75) && size_per_head_ == 32)) {\n        dispatcher_fp16.reset(new FusedMHARunnerFP16v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayer not support \\n\"));\n    }\n    hidden_units_ = head_num_ * size_per_head_;\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::FusedAttentionLayer(FusedAttentionLayer<T> const& attention_layer):\n    FusedAttentionLayer(0,\n                        0,\n                        attention_layer.head_num_,\n                        attention_layer.size_per_head_,\n                        attention_layer.d_model_,\n                        attention_layer.sm_,\n                        attention_layer.q_scaling_,\n                        attention_layer.stream_,\n                        attention_layer.cublas_wrapper_,\n                        attention_layer.allocator_,\n                        attention_layer.is_free_buffer_after_forward_,\n                        attention_layer.sparse_)\n{\n}\n\ntemplate<typename T>\nFusedAttentionLayer<T>::~FusedAttentionLayer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::allocateBuffer()\n{\n    FT_CHECK(false);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::allocateBuffer(size_t batch_size, size_t seq_len)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    q_buf_          = (T*)allocator_->reMalloc(q_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    k_buf_          = (T*)allocator_->reMalloc(k_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    v_buf_          = (T*)allocator_->reMalloc(v_buf_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    qkv_buf_        = (T*)allocator_->reMalloc(qkv_buf_, sizeof(T) * 3 * batch_size * seq_len * hidden_units_, false);\n    qkv_buf_2_      = (T*)allocator_->reMalloc(qkv_buf_2_, sizeof(T) * batch_size * seq_len * hidden_units_, false);\n    attn_workspace_ = (T*)allocator_->reMalloc(attn_workspace_, dispatcher_fp16->getWorkspaceSize(), false);\n\n    batch_qkv_kernel_ptr_ = (T**)allocator_->reMalloc(batch_qkv_kernel_ptr_, sizeof(T*) * 12, false);\n    batch_qkv_input_ptr_  = batch_qkv_kernel_ptr_ + 4;\n    batch_qkv_buf_ptr_    = batch_qkv_input_ptr_ + 4;\n    is_allocate_buffer_   = true;\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayer<T>::freeBuffer()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (is_allocate_buffer_) {\n        allocator_->free((void**)(&q_buf_));\n        allocator_->free((void**)(&k_buf_));\n        allocator_->free((void**)(&v_buf_));\n        allocator_->free((void**)(&qkv_buf_));\n        allocator_->free((void**)(&qkv_buf_2_));\n        allocator_->free((void**)(&attn_workspace_));\n        allocator_->free((void**)(&batch_qkv_kernel_ptr_));\n        sync_check_cuda_error();\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayer<T>::isValidSeqLen(const size_t seq_len)\n{\n    return true;\n}\n\ntemplate class FusedAttentionLayer<float>;\ntemplate class FusedAttentionLayer<half>;\n#ifdef ENABLE_BF16\ntemplate class FusedAttentionLayer<__hip_bfloat16>;\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/attention_layers/FusedAttentionLayer.cu"}
{"input": "/*\n * Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/layers/attention_layers_int8/WindowAttentionINT8.h\"\n\nnamespace fastertransformer {\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void swin_trt_add_QKV_bias_COL32_int8IO(char4*       output,\n                                                   const char4* QKV,\n                                                   const T*     bias_Q,\n                                                   const T*     bias_K,\n                                                   const T*     bias_V,\n                                                   const float* q_bias_QFactor_ptr,\n                                                   const float* k_bias_QFactor_ptr,\n                                                   const float* v_bias_QFactor_ptr,\n                                                   const float  qkv_deQFactor,\n                                                   const int    valid_word_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n    const int col_id     = qkv_id * head_num_x_size_per_head + hidden_id;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* bias_QFactor_ptr =\n            (qkv_id == 0) ? q_bias_QFactor_ptr : ((qkv_id == 1) ? k_bias_QFactor_ptr : v_bias_QFactor_ptr);\n        const float qkv_output_scale = __ldg(bias_QFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = ((col_id >> 5) << 5) * valid_word_num + (seq_id << 5) + (col_id & 31);\n\n        char4 tmp = __ldg(QKV + (input_id >> 2));\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeSwinTrtAddQkvBiasInt8IO(int8_t*            output,\n                                   const int8_t*      Q,\n                                   const T*           bias_Q,\n                                   const T*           bias_K,\n                                   const T*           bias_V,\n                                   const size_t       token_num,\n                                   const size_t       head_num,\n                                   const size_t       size_per_head,\n                                   const float*       q_bias_QFactor_ptr,\n                                   const float*       k_bias_QFactor_ptr,\n                                   const float*       v_bias_QFactor_ptr,\n                                   const float        qkv_deQFactor,\n                                   const cudaStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    swin_trt_add_QKV_bias_COL32_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                                   (const char4*)Q,\n                                                                   bias_Q,\n                                                                   bias_K,\n                                                                   bias_V,\n                                                                   q_bias_QFactor_ptr,\n                                                                   k_bias_QFactor_ptr,\n                                                                   v_bias_QFactor_ptr,\n                                                                   qkv_deQFactor,\n                                                                   token_num,\n                                                                   head_num,\n                                                                   size_per_head,\n                                                                   head_num_x_size_per_head);\n}\n\ntemplate<typename T>\nint WindowAttentionINT8<T>::trt_getS(const int actual_seqlen)\n{\n    int S = 384;\n    if (actual_seqlen <= 64) {\n        S = 64;\n    }\n    else if (actual_seqlen <= 256) {\n        S = 256;\n    }\n    return S;\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::allocateBuffer()\n{\n    assert(false && \"WindowAttentionINT8<T>::allocateBuffer() not implemented\");\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::allocateBuffer(int batch, int window_num, int window_len, int embed_dim, int num_head)\n{\n    if (is_allocate_buffer_ == false) {\n        FT_LOG_DEBUG(\"WindowAttentionINT8<T>::allocateBuffer()\");\n        if (use_trt_) {\n            Q_buf_ = (int8_t*)allocator_->reMalloc(\n                Q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            K_buf_ = Q_buf_ + batch * window_num * window_len * embed_dim;\n            V_buf_ = K_buf_ + batch * window_num * window_len * embed_dim;\n            q_buf_ = (int8_t*)allocator_->reMalloc(\n                q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            k_buf_   = q_buf_ + batch * window_num * window_len * embed_dim;\n            v_buf_   = k_buf_ + batch * window_num * window_len * embed_dim;\n            dst_buf_ = (int8_t*)allocator_->reMalloc(\n                dst_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n        }\n        else {\n            int padded_winlen = (window_len + 31) / 32 * 32;\n            Q_buf_            = (int8_t*)allocator_->reMalloc(\n                Q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            K_buf_ = Q_buf_ + batch * window_num * window_len * embed_dim;\n            V_buf_ = K_buf_ + batch * window_num * window_len * embed_dim;\n            q_buf_ = (int8_t*)allocator_->reMalloc(\n                q_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            k_buf_ = (int8_t*)allocator_->reMalloc(\n                k_buf_, batch * window_num * padded_winlen * embed_dim * sizeof(int8_t), false);\n            v_buf_ = (int8_t*)allocator_->reMalloc(\n                v_buf_, batch * window_num * padded_winlen * embed_dim * sizeof(int8_t), false);\n            qk_buf_ = (int8_t*)allocator_->reMalloc(\n                qk_buf_, batch * window_num * num_head * window_len * padded_winlen, false);\n            dst_buf_ = (int8_t*)allocator_->reMalloc(\n                dst_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n        }\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::freeBuffer()\n{\n    if (is_allocate_buffer_ == true) {\n        if (use_trt_) {\n            allocator_->free((void**)(&Q_buf_));\n            allocator_->free((void**)(&q_buf_));\n            allocator_->free((void**)(&dst_buf_));\n        }\n        else {\n            allocator_->free((void**)(&Q_buf_));\n            allocator_->free((void**)(&q_buf_));\n            allocator_->free((void**)(&k_buf_));\n            allocator_->free((void**)(&v_buf_));\n            allocator_->free((void**)(&qk_buf_));\n            allocator_->free((void**)(&dst_buf_));\n        }\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nWindowAttentionINT8<T>::WindowAttentionINT8(int              max_batch,\n                                            int              window_size,\n                                            cudaStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward,\n                                            bool             qkv_bias,\n                                            float            qk_scale,\n                                            int              version):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_(max_batch),\n    window_size_(window_size),\n    qkv_bias_(qkv_bias),\n    qk_scale_(qk_scale),\n    version_(version)\n{\n}\n\ntemplate<typename T>\nWindowAttentionINT8<T>::~WindowAttentionINT8()\n{\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::forward(TensorMap*                output_tensors,\n                                     TensorMap*                input_tensors,\n                                     const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors:\n    //      input [batch * window_num * window_len, dim]\n    //      attention_mask [window_num, window_len, window_len]\n    //      trt_attention_mask [window_num, window_len, window_len]\n    //      attention_relative_position_bias [num_head, window_len, window_len]\n    //      trt_relative_position_bias [num_head, window_len, window_len]\n    //      attention_logit_scale [num_head]\n    //      additional_params [9] {batch, dim, input_resolution, num_head, shift_size, sm, window_size_in_use,\n    //      basic_layer_id, block_id}\n    // output_tensors:\n    //      hidden_features [batch * window_num * window_len, dim]\n\n    cublasINT8MMWrapper* cublas_wrapper              = (cublasINT8MMWrapper*)cublas_wrapper_;\n    int8_t*              attention_out               = output_tensors->getPtr<int8_t>(\"hidden_features\");\n    const int8_t*        from_tensor                 = input_tensors->getPtr<int8_t>(\"input_query\");\n    const T*             attention_mask              = input_tensors->getPtr<T>(\"attention_mask\", nullptr);\n    const T*             trt_attention_mask          = input_tensors->getPtr<T>(\"trt_attention_mask\", nullptr);\n    const T*             attention_relative_pos_bias = input_tensors->getPtr<T>(\"attention_relative_position_bias\");\n    const T*             trt_relative_position_bias  = input_tensors->getPtr<T>(\"trt_relative_position_bias\", nullptr);\n    const int            window_len_in_use           = input_tensors->at(\"attention_relative_position_bias\").shape[1];\n    const T*             attention_logit_scale       = input_tensors->getPtr<T>(\"attn_logit_scale\", nullptr);\n    const int*           additional_params           = input_tensors->getPtr<int>(\"additional_params\");\n    const int            batch                       = additional_params[0];\n    const int            dim                         = additional_params[1];\n    const int            input_resolution            = additional_params[2];\n    const int            num_head                    = additional_params[3];\n    const int            shift_size                  = additional_params[4];\n    const int            sm                          = additional_params[5];\n    const int            window_size_in_use          = additional_params[6];\n    // const int            basic_layer_id              = additional_params[7]; // not used\n    // const int            block_id                    = additional_params[8]; // not used\n\n    int use_ORDER_COL32_2R_4R4 = (sm >= 80 ? 1 : 0);\n\n    int size_per_head = dim / num_head;\n    int trt_S         = 1024;\n    // we should decide whether to use trt fmha based on window_size_ * window_size_\n    if ((sm == 75 || sm == 80 || sm == 86) && size_per_head == 32 && window_size_ * window_size_ <= TRT_MAX_LEN\n        && std::is_same<T, half>::value) {\n        trt_S    = trt_getS(window_len_in_use);\n        use_trt_ = true;\n    }\n    int window_num = (input_resolution / window_size_in_use) * (input_resolution / window_size_in_use);\n    allocateBuffer(batch, window_num, window_len_in_use, dim, num_head);\n\n    float scale = (version_ == 1) ? (1.0f / sqrt(size_per_head)) : 1.0f;\n    if (fabs(qk_scale_ - 1.0f) > 0.0001) {\n        scale = qk_scale_;\n    }\n\n    if (use_trt_) {\n        if (dispatcher_int8_.get() && num_head == dispatcher_int8_num_head_) {}\n        else {\n            float fmha_qk_scaling = (version_ == 1) ? 1.0f : (1.0f / sqrt(size_per_head));\n            dispatcher_int8_.reset(new FusedMHARunnerInt8v2(num_head, size_per_head, sm, fmha_qk_scaling));\n            dispatcher_int8_num_head_ = num_head;\n        }\n    }\n\n    const ScaleList* scale_list = ((const AttentionINT8Weight<T>*)attention_weights)->scale_list_ptr;\n\n    cublas_wrapper->Gemm(Q_buf_,\n                         1,\n                         batch * window_num * window_len_in_use,\n                         3 * dim,\n                         dim,\n                         0,\n                         0,\n                         0,\n                         scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                         from_tensor,\n                         (int8_t*)attention_weights->query_weight.kernel);\n\n    int S;\n    if (use_trt_ && dispatcher_int8_.get()) {\n        S = trt_S;\n    }\n    if (use_trt_ && dispatcher_int8_.get() && dispatcher_int8_->isValid(S, true)) {\n        const T* bias_Q = attention_weights->query_weight.bias;\n        const T* bias_K = attention_weights->query_weight.bias + dim;\n        const T* bias_V = attention_weights->query_weight.bias + (dim << 1);\n        invokeSwinTrtAddQkvBiasInt8IO(q_buf_,\n                                      Q_buf_,\n                                      bias_Q,\n                                      bias_K,\n                                      bias_V,\n                                      batch * window_num * window_len_in_use,\n                                      num_head,\n                                      size_per_head,\n                                      &(scale_list->d_scale_list_[16 + 3]),\n                                      &(scale_list->d_scale_list_[20 + 3]),\n                                      &(scale_list->d_scale_list_[24 + 3]),\n                                      scale_list->h_scale_list_[4 + 1],\n                                      stream_);\n\n        const int B = batch * window_num;\n\n        dispatcher_int8_->setScaleList(scale_list->h_scale_list_[scale_list->p4_offset_],\n                                       scale_list->h_scale_list_[scale_list->p4_offset_ + 1],\n                                       scale_list->h_scale_list_[scale_list->p4_offset_ + 2]);\n        dispatcher_int8_->setup(S, B, window_num);\n\n        if (version_ == 2) {\n            invokeNormalizeForFMHA(q_buf_,\n                                   attention_logit_scale,\n                                   B,\n                                   window_len_in_use,\n                                   num_head,\n                                   size_per_head,\n                                   stream_,\n                                   scale_list->h_scale_list_[16 + 1],\n                                   scale_list->h_scale_list_[20 + 1],\n                                   scale_list->h_scale_list_[52 + 3],\n                                   scale_list->h_scale_list_[56 + 3]);\n        }\n\n        dispatcher_int8_->run(q_buf_,\n                              shift_size != 0 ? trt_attention_mask : nullptr,\n                              trt_relative_position_bias,\n                              window_len_in_use,\n                              nullptr,\n                              dst_buf_,\n                              stream_);\n\n        invokeRowMajorToCOL32(v_buf_, dst_buf_, batch * window_num * window_len_in_use, dim, stream_);\n\n        cublas_wrapper->Gemm(q_buf_,\n                             1,\n                             batch * window_num * window_len_in_use,\n                             dim,\n                             dim,\n                             0,\n                             0,\n                             0,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                             v_buf_,\n                             (int8_t*)attention_weights->attention_output_weight.kernel);\n    }\n    else {\n        const T* bias_Q = attention_weights->query_weight.bias;\n        const T* bias_K = attention_weights->query_weight.bias + dim;\n        const T* bias_V = attention_weights->query_weight.bias + 2 * dim;\n        invokeAddQKBiasTransform(q_buf_,\n                                 k_buf_,\n                                 Q_buf_,\n                                 bias_Q,\n                                 K_buf_,\n                                 bias_K,\n                                 batch * window_num,\n                                 window_len_in_use,\n                                 num_head,\n                                 size_per_head,\n                                 &(scale_list->d_scale_list_[4 + 1]),\n                                 &(scale_list->d_scale_list_[4 + 1]),\n                                 &(scale_list->d_scale_list_[16 + 3]),\n                                 &(scale_list->d_scale_list_[20 + 3]),\n                                 use_ORDER_COL32_2R_4R4,\n                                 stream_);\n\n        invokeAddVBiasTransform(v_buf_,\n                                V_buf_,\n                                bias_V,\n                                batch * window_num,\n                                window_len_in_use,\n                                num_head,\n                                size_per_head,\n                                &(scale_list->d_scale_list_[4 + 1]),\n                                &(scale_list->d_scale_list_[24 + 3]),\n                                use_ORDER_COL32_2R_4R4,\n                                stream_);\n\n        int padded_winlen = (window_len_in_use + 31) / 32 * 32;\n        if (version_ == 2) {\n            invokeNormalize(q_buf_,\n                            attention_logit_scale,\n                            batch * window_num,\n                            window_len_in_use,\n                            num_head,\n                            size_per_head,\n                            stream_,\n                            scale_list->h_scale_list_[16 + 1],\n                            scale_list->h_scale_list_[52 + 3]);\n            invokeNormalize(k_buf_,\n                            (const T*)nullptr,\n                            batch * window_num,\n                            padded_winlen,\n                            num_head,\n                            size_per_head,\n                            stream_,\n                            scale_list->h_scale_list_[20 + 1],\n                            scale_list->h_scale_list_[56 + 3]);\n        }\n\n        cublas_wrapper->Gemm(qk_buf_,\n                             batch * window_num * num_head,\n                             window_len_in_use,\n                             padded_winlen,\n                             size_per_head,\n                             window_len_in_use * size_per_head,\n                             padded_winlen * size_per_head,\n                             window_len_in_use * padded_winlen,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 4],\n                             q_buf_,\n                             k_buf_);\n\n        if (shift_size != 0) {\n            invokeSoftmaxWithRelPosBiasCOL32(qk_buf_,\n                                             qk_buf_,\n                                             attention_mask,\n                                             attention_relative_pos_bias,\n                                             batch,\n                                             num_head,\n                                             window_num,\n                                             window_len_in_use,\n                                             scale,\n                                             &(scale_list->d_scale_list_[32 + 1]),\n                                             &(scale_list->d_scale_list_[28 + 3]),\n                                             stream_);\n        }\n        else {\n            const T* attn_mask_tmp = nullptr;\n            invokeSoftmaxWithRelPosBiasCOL32(qk_buf_,\n                                             qk_buf_,\n                                             attn_mask_tmp,\n                                             attention_relative_pos_bias,\n                                             batch,\n                                             num_head,\n                                             window_num,\n                                             window_len_in_use,\n                                             scale,\n                                             &(scale_list->d_scale_list_[32 + 1]),\n                                             &(scale_list->d_scale_list_[28 + 3]),\n                                             stream_);\n        }\n\n        cublas_wrapper->Gemm(dst_buf_,\n                             batch * window_num * num_head,\n                             window_len_in_use,\n                             size_per_head,\n                             padded_winlen,\n                             window_len_in_use * padded_winlen,\n                             size_per_head * padded_winlen,\n                             window_len_in_use * size_per_head,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                             qk_buf_,\n                             v_buf_);\n\n        invokeTransposeCOL32(v_buf_,\n                             dst_buf_,\n                             batch * window_num,\n                             window_len_in_use,\n                             num_head,\n                             size_per_head,\n                             &(scale_list->d_scale_list_[36 + 1]),\n                             &(scale_list->d_scale_list_[36 + 3]),\n                             stream_);\n\n        cublas_wrapper->Gemm(q_buf_,\n                             1,\n                             batch * window_num * window_len_in_use,\n                             dim,\n                             dim,\n                             0,\n                             0,\n                             0,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                             v_buf_,\n                             (int8_t*)attention_weights->attention_output_weight.kernel);\n    }\n\n    invokeReverseRollCol32(attention_out,\n                           q_buf_,\n                           batch,\n                           window_num,\n                           window_len_in_use,\n                           window_size_in_use,\n                           input_resolution,\n                           input_resolution,\n                           dim,\n                           shift_size,\n                           stream_);\n\n    if (is_free_buffer_after_forward_) {\n        freeBuffer();\n    }\n}\n\ntemplate class WindowAttentionINT8<float>;\ntemplate class WindowAttentionINT8<half>;\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/layers/attention_layers_int8/WindowAttentionINT8.h\"\n\nnamespace fastertransformer {\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void swin_trt_add_QKV_bias_COL32_int8IO(char4*       output,\n                                                   const char4* QKV,\n                                                   const T*     bias_Q,\n                                                   const T*     bias_K,\n                                                   const T*     bias_V,\n                                                   const float* q_bias_QFactor_ptr,\n                                                   const float* k_bias_QFactor_ptr,\n                                                   const float* v_bias_QFactor_ptr,\n                                                   const float  qkv_deQFactor,\n                                                   const int    valid_word_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n    const int col_id     = qkv_id * head_num_x_size_per_head + hidden_id;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* bias_QFactor_ptr =\n            (qkv_id == 0) ? q_bias_QFactor_ptr : ((qkv_id == 1) ? k_bias_QFactor_ptr : v_bias_QFactor_ptr);\n        const float qkv_output_scale = __ldg(bias_QFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = ((col_id >> 5) << 5) * valid_word_num + (seq_id << 5) + (col_id & 31);\n\n        char4 tmp = __ldg(QKV + (input_id >> 2));\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * qkv_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeSwinTrtAddQkvBiasInt8IO(int8_t*            output,\n                                   const int8_t*      Q,\n                                   const T*           bias_Q,\n                                   const T*           bias_K,\n                                   const T*           bias_V,\n                                   const size_t       token_num,\n                                   const size_t       head_num,\n                                   const size_t       size_per_head,\n                                   const float*       q_bias_QFactor_ptr,\n                                   const float*       k_bias_QFactor_ptr,\n                                   const float*       v_bias_QFactor_ptr,\n                                   const float        qkv_deQFactor,\n                                   const hipStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    swin_trt_add_QKV_bias_COL32_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                                   (const char4*)Q,\n                                                                   bias_Q,\n                                                                   bias_K,\n                                                                   bias_V,\n                                                                   q_bias_QFactor_ptr,\n                                                                   k_bias_QFactor_ptr,\n                                                                   v_bias_QFactor_ptr,\n                                                                   qkv_deQFactor,\n                                                                   token_num,\n                                                                   head_num,\n                                                                   size_per_head,\n                                                                   head_num_x_size_per_head);\n}\n\ntemplate<typename T>\nint WindowAttentionINT8<T>::trt_getS(const int actual_seqlen)\n{\n    int S = 384;\n    if (actual_seqlen <= 64) {\n        S = 64;\n    }\n    else if (actual_seqlen <= 256) {\n        S = 256;\n    }\n    return S;\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::allocateBuffer()\n{\n    assert(false && \"WindowAttentionINT8<T>::allocateBuffer() not implemented\");\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::allocateBuffer(int batch, int window_num, int window_len, int embed_dim, int num_head)\n{\n    if (is_allocate_buffer_ == false) {\n        FT_LOG_DEBUG(\"WindowAttentionINT8<T>::allocateBuffer()\");\n        if (use_trt_) {\n            Q_buf_ = (int8_t*)allocator_->reMalloc(\n                Q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            K_buf_ = Q_buf_ + batch * window_num * window_len * embed_dim;\n            V_buf_ = K_buf_ + batch * window_num * window_len * embed_dim;\n            q_buf_ = (int8_t*)allocator_->reMalloc(\n                q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            k_buf_   = q_buf_ + batch * window_num * window_len * embed_dim;\n            v_buf_   = k_buf_ + batch * window_num * window_len * embed_dim;\n            dst_buf_ = (int8_t*)allocator_->reMalloc(\n                dst_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n        }\n        else {\n            int padded_winlen = (window_len + 31) / 32 * 32;\n            Q_buf_            = (int8_t*)allocator_->reMalloc(\n                Q_buf_, 3 * batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            K_buf_ = Q_buf_ + batch * window_num * window_len * embed_dim;\n            V_buf_ = K_buf_ + batch * window_num * window_len * embed_dim;\n            q_buf_ = (int8_t*)allocator_->reMalloc(\n                q_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n            k_buf_ = (int8_t*)allocator_->reMalloc(\n                k_buf_, batch * window_num * padded_winlen * embed_dim * sizeof(int8_t), false);\n            v_buf_ = (int8_t*)allocator_->reMalloc(\n                v_buf_, batch * window_num * padded_winlen * embed_dim * sizeof(int8_t), false);\n            qk_buf_ = (int8_t*)allocator_->reMalloc(\n                qk_buf_, batch * window_num * num_head * window_len * padded_winlen, false);\n            dst_buf_ = (int8_t*)allocator_->reMalloc(\n                dst_buf_, batch * window_num * window_len * embed_dim * sizeof(int8_t), false);\n        }\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::freeBuffer()\n{\n    if (is_allocate_buffer_ == true) {\n        if (use_trt_) {\n            allocator_->free((void**)(&Q_buf_));\n            allocator_->free((void**)(&q_buf_));\n            allocator_->free((void**)(&dst_buf_));\n        }\n        else {\n            allocator_->free((void**)(&Q_buf_));\n            allocator_->free((void**)(&q_buf_));\n            allocator_->free((void**)(&k_buf_));\n            allocator_->free((void**)(&v_buf_));\n            allocator_->free((void**)(&qk_buf_));\n            allocator_->free((void**)(&dst_buf_));\n        }\n        is_allocate_buffer_ = false;\n    }\n}\n\ntemplate<typename T>\nWindowAttentionINT8<T>::WindowAttentionINT8(int              max_batch,\n                                            int              window_size,\n                                            hipStream_t     stream,\n                                            cublasMMWrapper* cublas_wrapper,\n                                            IAllocator*      allocator,\n                                            bool             is_free_buffer_after_forward,\n                                            bool             qkv_bias,\n                                            float            qk_scale,\n                                            int              version):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_(max_batch),\n    window_size_(window_size),\n    qkv_bias_(qkv_bias),\n    qk_scale_(qk_scale),\n    version_(version)\n{\n}\n\ntemplate<typename T>\nWindowAttentionINT8<T>::~WindowAttentionINT8()\n{\n}\n\ntemplate<typename T>\nvoid WindowAttentionINT8<T>::forward(TensorMap*                output_tensors,\n                                     TensorMap*                input_tensors,\n                                     const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors:\n    //      input [batch * window_num * window_len, dim]\n    //      attention_mask [window_num, window_len, window_len]\n    //      trt_attention_mask [window_num, window_len, window_len]\n    //      attention_relative_position_bias [num_head, window_len, window_len]\n    //      trt_relative_position_bias [num_head, window_len, window_len]\n    //      attention_logit_scale [num_head]\n    //      additional_params [9] {batch, dim, input_resolution, num_head, shift_size, sm, window_size_in_use,\n    //      basic_layer_id, block_id}\n    // output_tensors:\n    //      hidden_features [batch * window_num * window_len, dim]\n\n    cublasINT8MMWrapper* cublas_wrapper              = (cublasINT8MMWrapper*)cublas_wrapper_;\n    int8_t*              attention_out               = output_tensors->getPtr<int8_t>(\"hidden_features\");\n    const int8_t*        from_tensor                 = input_tensors->getPtr<int8_t>(\"input_query\");\n    const T*             attention_mask              = input_tensors->getPtr<T>(\"attention_mask\", nullptr);\n    const T*             trt_attention_mask          = input_tensors->getPtr<T>(\"trt_attention_mask\", nullptr);\n    const T*             attention_relative_pos_bias = input_tensors->getPtr<T>(\"attention_relative_position_bias\");\n    const T*             trt_relative_position_bias  = input_tensors->getPtr<T>(\"trt_relative_position_bias\", nullptr);\n    const int            window_len_in_use           = input_tensors->at(\"attention_relative_position_bias\").shape[1];\n    const T*             attention_logit_scale       = input_tensors->getPtr<T>(\"attn_logit_scale\", nullptr);\n    const int*           additional_params           = input_tensors->getPtr<int>(\"additional_params\");\n    const int            batch                       = additional_params[0];\n    const int            dim                         = additional_params[1];\n    const int            input_resolution            = additional_params[2];\n    const int            num_head                    = additional_params[3];\n    const int            shift_size                  = additional_params[4];\n    const int            sm                          = additional_params[5];\n    const int            window_size_in_use          = additional_params[6];\n    // const int            basic_layer_id              = additional_params[7]; // not used\n    // const int            block_id                    = additional_params[8]; // not used\n\n    int use_ORDER_COL32_2R_4R4 = (sm >= 80 ? 1 : 0);\n\n    int size_per_head = dim / num_head;\n    int trt_S         = 1024;\n    // we should decide whether to use trt fmha based on window_size_ * window_size_\n    if ((sm == 75 || sm == 80 || sm == 86) && size_per_head == 32 && window_size_ * window_size_ <= TRT_MAX_LEN\n        && std::is_same<T, half>::value) {\n        trt_S    = trt_getS(window_len_in_use);\n        use_trt_ = true;\n    }\n    int window_num = (input_resolution / window_size_in_use) * (input_resolution / window_size_in_use);\n    allocateBuffer(batch, window_num, window_len_in_use, dim, num_head);\n\n    float scale = (version_ == 1) ? (1.0f / sqrt(size_per_head)) : 1.0f;\n    if (fabs(qk_scale_ - 1.0f) > 0.0001) {\n        scale = qk_scale_;\n    }\n\n    if (use_trt_) {\n        if (dispatcher_int8_.get() && num_head == dispatcher_int8_num_head_) {}\n        else {\n            float fmha_qk_scaling = (version_ == 1) ? 1.0f : (1.0f / sqrt(size_per_head));\n            dispatcher_int8_.reset(new FusedMHARunnerInt8v2(num_head, size_per_head, sm, fmha_qk_scaling));\n            dispatcher_int8_num_head_ = num_head;\n        }\n    }\n\n    const ScaleList* scale_list = ((const AttentionINT8Weight<T>*)attention_weights)->scale_list_ptr;\n\n    cublas_wrapper->Gemm(Q_buf_,\n                         1,\n                         batch * window_num * window_len_in_use,\n                         3 * dim,\n                         dim,\n                         0,\n                         0,\n                         0,\n                         scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                         from_tensor,\n                         (int8_t*)attention_weights->query_weight.kernel);\n\n    int S;\n    if (use_trt_ && dispatcher_int8_.get()) {\n        S = trt_S;\n    }\n    if (use_trt_ && dispatcher_int8_.get() && dispatcher_int8_->isValid(S, true)) {\n        const T* bias_Q = attention_weights->query_weight.bias;\n        const T* bias_K = attention_weights->query_weight.bias + dim;\n        const T* bias_V = attention_weights->query_weight.bias + (dim << 1);\n        invokeSwinTrtAddQkvBiasInt8IO(q_buf_,\n                                      Q_buf_,\n                                      bias_Q,\n                                      bias_K,\n                                      bias_V,\n                                      batch * window_num * window_len_in_use,\n                                      num_head,\n                                      size_per_head,\n                                      &(scale_list->d_scale_list_[16 + 3]),\n                                      &(scale_list->d_scale_list_[20 + 3]),\n                                      &(scale_list->d_scale_list_[24 + 3]),\n                                      scale_list->h_scale_list_[4 + 1],\n                                      stream_);\n\n        const int B = batch * window_num;\n\n        dispatcher_int8_->setScaleList(scale_list->h_scale_list_[scale_list->p4_offset_],\n                                       scale_list->h_scale_list_[scale_list->p4_offset_ + 1],\n                                       scale_list->h_scale_list_[scale_list->p4_offset_ + 2]);\n        dispatcher_int8_->setup(S, B, window_num);\n\n        if (version_ == 2) {\n            invokeNormalizeForFMHA(q_buf_,\n                                   attention_logit_scale,\n                                   B,\n                                   window_len_in_use,\n                                   num_head,\n                                   size_per_head,\n                                   stream_,\n                                   scale_list->h_scale_list_[16 + 1],\n                                   scale_list->h_scale_list_[20 + 1],\n                                   scale_list->h_scale_list_[52 + 3],\n                                   scale_list->h_scale_list_[56 + 3]);\n        }\n\n        dispatcher_int8_->run(q_buf_,\n                              shift_size != 0 ? trt_attention_mask : nullptr,\n                              trt_relative_position_bias,\n                              window_len_in_use,\n                              nullptr,\n                              dst_buf_,\n                              stream_);\n\n        invokeRowMajorToCOL32(v_buf_, dst_buf_, batch * window_num * window_len_in_use, dim, stream_);\n\n        cublas_wrapper->Gemm(q_buf_,\n                             1,\n                             batch * window_num * window_len_in_use,\n                             dim,\n                             dim,\n                             0,\n                             0,\n                             0,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                             v_buf_,\n                             (int8_t*)attention_weights->attention_output_weight.kernel);\n    }\n    else {\n        const T* bias_Q = attention_weights->query_weight.bias;\n        const T* bias_K = attention_weights->query_weight.bias + dim;\n        const T* bias_V = attention_weights->query_weight.bias + 2 * dim;\n        invokeAddQKBiasTransform(q_buf_,\n                                 k_buf_,\n                                 Q_buf_,\n                                 bias_Q,\n                                 K_buf_,\n                                 bias_K,\n                                 batch * window_num,\n                                 window_len_in_use,\n                                 num_head,\n                                 size_per_head,\n                                 &(scale_list->d_scale_list_[4 + 1]),\n                                 &(scale_list->d_scale_list_[4 + 1]),\n                                 &(scale_list->d_scale_list_[16 + 3]),\n                                 &(scale_list->d_scale_list_[20 + 3]),\n                                 use_ORDER_COL32_2R_4R4,\n                                 stream_);\n\n        invokeAddVBiasTransform(v_buf_,\n                                V_buf_,\n                                bias_V,\n                                batch * window_num,\n                                window_len_in_use,\n                                num_head,\n                                size_per_head,\n                                &(scale_list->d_scale_list_[4 + 1]),\n                                &(scale_list->d_scale_list_[24 + 3]),\n                                use_ORDER_COL32_2R_4R4,\n                                stream_);\n\n        int padded_winlen = (window_len_in_use + 31) / 32 * 32;\n        if (version_ == 2) {\n            invokeNormalize(q_buf_,\n                            attention_logit_scale,\n                            batch * window_num,\n                            window_len_in_use,\n                            num_head,\n                            size_per_head,\n                            stream_,\n                            scale_list->h_scale_list_[16 + 1],\n                            scale_list->h_scale_list_[52 + 3]);\n            invokeNormalize(k_buf_,\n                            (const T*)nullptr,\n                            batch * window_num,\n                            padded_winlen,\n                            num_head,\n                            size_per_head,\n                            stream_,\n                            scale_list->h_scale_list_[20 + 1],\n                            scale_list->h_scale_list_[56 + 3]);\n        }\n\n        cublas_wrapper->Gemm(qk_buf_,\n                             batch * window_num * num_head,\n                             window_len_in_use,\n                             padded_winlen,\n                             size_per_head,\n                             window_len_in_use * size_per_head,\n                             padded_winlen * size_per_head,\n                             window_len_in_use * padded_winlen,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 4],\n                             q_buf_,\n                             k_buf_);\n\n        if (shift_size != 0) {\n            invokeSoftmaxWithRelPosBiasCOL32(qk_buf_,\n                                             qk_buf_,\n                                             attention_mask,\n                                             attention_relative_pos_bias,\n                                             batch,\n                                             num_head,\n                                             window_num,\n                                             window_len_in_use,\n                                             scale,\n                                             &(scale_list->d_scale_list_[32 + 1]),\n                                             &(scale_list->d_scale_list_[28 + 3]),\n                                             stream_);\n        }\n        else {\n            const T* attn_mask_tmp = nullptr;\n            invokeSoftmaxWithRelPosBiasCOL32(qk_buf_,\n                                             qk_buf_,\n                                             attn_mask_tmp,\n                                             attention_relative_pos_bias,\n                                             batch,\n                                             num_head,\n                                             window_num,\n                                             window_len_in_use,\n                                             scale,\n                                             &(scale_list->d_scale_list_[32 + 1]),\n                                             &(scale_list->d_scale_list_[28 + 3]),\n                                             stream_);\n        }\n\n        cublas_wrapper->Gemm(dst_buf_,\n                             batch * window_num * num_head,\n                             window_len_in_use,\n                             size_per_head,\n                             padded_winlen,\n                             window_len_in_use * padded_winlen,\n                             size_per_head * padded_winlen,\n                             window_len_in_use * size_per_head,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                             qk_buf_,\n                             v_buf_);\n\n        invokeTransposeCOL32(v_buf_,\n                             dst_buf_,\n                             batch * window_num,\n                             window_len_in_use,\n                             num_head,\n                             size_per_head,\n                             &(scale_list->d_scale_list_[36 + 1]),\n                             &(scale_list->d_scale_list_[36 + 3]),\n                             stream_);\n\n        cublas_wrapper->Gemm(q_buf_,\n                             1,\n                             batch * window_num * window_len_in_use,\n                             dim,\n                             dim,\n                             0,\n                             0,\n                             0,\n                             scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                             v_buf_,\n                             (int8_t*)attention_weights->attention_output_weight.kernel);\n    }\n\n    invokeReverseRollCol32(attention_out,\n                           q_buf_,\n                           batch,\n                           window_num,\n                           window_len_in_use,\n                           window_size_in_use,\n                           input_resolution,\n                           input_resolution,\n                           dim,\n                           shift_size,\n                           stream_);\n\n    if (is_free_buffer_after_forward_) {\n        freeBuffer();\n    }\n}\n\ntemplate class WindowAttentionINT8<float>;\ntemplate class WindowAttentionINT8<half>;\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/attention_layers_int8/WindowAttentionINT8.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/layout_transformer_int8_kernels.h\"\n#include \"src/fastertransformer/layers/attention_layers_int8/FusedAttentionLayerINT8.h\"\n#include \"src/fastertransformer/utils/nvtx_utils.h\"\n\nnamespace fastertransformer {\n\n// add bias and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// input is INT32 && per axis quantization for weight\n// output is INT8 && per tensor quantization\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\n// using char4 as output, int4 as input\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_COL32_int32IInt8O(char4*       output,\n                                                   const int4*  QKV,\n                                                   const T*     bias_Q,\n                                                   const T*     bias_K,\n                                                   const T*     bias_V,\n                                                   const float* input_deQFactor_div127_ptr,\n                                                   const float* q_weight_amax,\n                                                   const float* k_weight_amax,\n                                                   const float* v_weight_amax,\n                                                   const float  qkv_output_scale,\n                                                   const int    valid_word_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    int       hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* weight_amax = qkv_id == 0 ? q_weight_amax : (qkv_id == 1 ? k_weight_amax : v_weight_amax);\n        const float  input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = (qkv_id * valid_word_num * head_num_x_size_per_head\n                              + ((hidden_id & 0xffffffe0) * valid_word_num + (seq_id << 5) + (hidden_id & 31)))\n                             >> 2;\n\n        char4      tmp;\n        const int4 tmp_int4 = __ldg(QKV + input_id);\n\n        tmp.x =\n            float_to_int8_rn((static_cast<float>(tmp_int4.x) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.y =\n            float_to_int8_rn((static_cast<float>(tmp_int4.y) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.z =\n            float_to_int8_rn((static_cast<float>(tmp_int4.z) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.w =\n            float_to_int8_rn((static_cast<float>(tmp_int4.w) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        // const int output_id = (seq_id * 3 * head_num_x_size_per_head + head_id * 3 * size_per_head + qkv_id *\n        // size_per_head + size_id) >> 2;\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt32Iint8O(int8_t*            output,\n                                    const int32_t*     Q,\n                                    const T*           bias_Q,\n                                    const T*           bias_K,\n                                    const T*           bias_V,\n                                    const size_t       token_num,\n                                    const size_t       head_num,\n                                    const size_t       size_per_head,\n                                    const float*       input_deQFactor_div127_ptr,\n                                    const float*       q_weight_amax,\n                                    const float*       k_weight_amax,\n                                    const float*       v_weight_amax,\n                                    const float        mScaleQkv,\n                                    const cudaStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_COL32_int32IInt8O<<<grid, block, 0, stream>>>((char4*)output,\n                                                                   (const int4*)Q,\n                                                                   bias_Q,\n                                                                   bias_K,\n                                                                   bias_V,\n                                                                   input_deQFactor_div127_ptr,\n                                                                   q_weight_amax,\n                                                                   k_weight_amax,\n                                                                   v_weight_amax,\n                                                                   1.f / mScaleQkv,\n                                                                   token_num,\n                                                                   head_num,\n                                                                   size_per_head,\n                                                                   head_num_x_size_per_head);\n}\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_COL32_int8IO(char4*       output,\n                                              const char4* QKV,\n                                              const T*     bias_Q,\n                                              const T*     bias_K,\n                                              const T*     bias_V,\n                                              const float* q_input_deQFactor_ptr,\n                                              const float* k_input_deQFactor_ptr,\n                                              const float* v_input_deQFactor_ptr,\n                                              const float  qkv_output_scale,\n                                              const int    valid_word_num,\n                                              const int    head_num,\n                                              const int    size_per_head,\n                                              const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* input_deQFactor_ptr =\n            (qkv_id == 0) ? q_input_deQFactor_ptr : ((qkv_id == 1) ? k_input_deQFactor_ptr : v_input_deQFactor_ptr);\n        const float input_deQFactor = __ldg(input_deQFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = (qkv_id * valid_word_num * head_num_x_size_per_head\n                              + ((hidden_id & 0xffffffe0) * valid_word_num + (seq_id << 5) + (hidden_id & 31)))\n                             >> 2;\n\n        char4 tmp = __ldg(QKV + input_id);\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        // const int output_id = (seq_id * 3 * head_num_x_size_per_head + head_id * 3 * size_per_head + qkv_id *\n        // size_per_head + size_id) >> 2;\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt8IO(int8_t*            output,\n                               const int8_t*      Q,\n                               const T*           bias_Q,\n                               const T*           bias_K,\n                               const T*           bias_V,\n                               const size_t       token_num,\n                               const size_t       head_num,\n                               const size_t       size_per_head,\n                               const float*       q_input_deQFactor_ptr,\n                               const float*       k_input_deQFactor_ptr,\n                               const float*       v_input_deQFactor_ptr,\n                               const float        mScaleQkv,\n                               const cudaStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_COL32_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                              (const char4*)Q,\n                                                              bias_Q,\n                                                              bias_K,\n                                                              bias_V,\n                                                              q_input_deQFactor_ptr,\n                                                              k_input_deQFactor_ptr,\n                                                              v_input_deQFactor_ptr,\n                                                              1.0f / mScaleQkv,\n                                                              token_num,\n                                                              head_num,\n                                                              size_per_head,\n                                                              head_num_x_size_per_head);\n}\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + row-major) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_ROW_int8IO(char4*       output,\n                                            const char4* QKV,\n                                            const T*     bias_Q,\n                                            const T*     bias_K,\n                                            const T*     bias_V,\n                                            const float* q_input_deQFactor_ptr,\n                                            const float* k_input_deQFactor_ptr,\n                                            const float* v_input_deQFactor_ptr,\n                                            const float  qkv_output_scale,\n                                            const int    valid_word_num,\n                                            const int    head_num,\n                                            const int    size_per_head,\n                                            const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* input_deQFactor_ptr =\n            (qkv_id == 0) ? q_input_deQFactor_ptr : ((qkv_id == 1) ? k_input_deQFactor_ptr : v_input_deQFactor_ptr);\n        const float input_deQFactor = __ldg(input_deQFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id =\n            (qkv_id * valid_word_num * head_num_x_size_per_head + seq_id * head_num_x_size_per_head + hidden_id) >> 2;\n\n        char4 tmp = __ldg(QKV + input_id);\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt8IORow(int8_t*            output,\n                                  const int8_t*      Q,\n                                  const T*           bias_Q,\n                                  const T*           bias_K,\n                                  const T*           bias_V,\n                                  const size_t       token_num,\n                                  const size_t       head_num,\n                                  const size_t       size_per_head,\n                                  const float*       q_input_deQFactor_ptr,\n                                  const float*       k_input_deQFactor_ptr,\n                                  const float*       v_input_deQFactor_ptr,\n                                  const float        mScaleQkv,\n                                  const cudaStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_ROW_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                            (const char4*)Q,\n                                                            bias_Q,\n                                                            bias_K,\n                                                            bias_V,\n                                                            q_input_deQFactor_ptr,\n                                                            k_input_deQFactor_ptr,\n                                                            v_input_deQFactor_ptr,\n                                                            1.0f / mScaleQkv,\n                                                            token_num,\n                                                            head_num,\n                                                            size_per_head,\n                                                            head_num_x_size_per_head);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::forward(TensorMap*                output_tensors,\n                                         TensorMap*                input_tensors,\n                                         const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors:\n    //      input_query [token_num, hidden_dimension],\n    //      attention_mask [batch, 1, seqlen, seqlen],\n    //      padding_offset [token_num]\n    // output_tensors:\n    //      hidden_features (token_num, hidden_dimension)\n\n    const ScaleList*     scale_list     = ((const AttentionINT8Weight<T>*)attention_weights)->scale_list_ptr;\n    cublasINT8MMWrapper* cublas_wrapper = (cublasINT8MMWrapper*)cublas_wrapper_;\n\n    FT_CHECK(isValidBatchSize(input_tensors->at(\"attention_mask\").shape[0]));\n    FT_CHECK(isValidSeqLen(input_tensors->at(\"attention_mask\").shape[2]));\n    allocateBuffer();\n\n    int32_t*      attention_out  = output_tensors->getPtr<int32_t>(\"hidden_features\");\n    const int8_t* from_tensor    = input_tensors->getPtr<int8_t>(\"input_query\");\n    const T*      attention_mask = input_tensors->getPtr<T>(\"attention_mask\");\n    const int*    padding_offset = input_tensors->getPtr<int>(\"padding_offset\");\n\n    const int request_batch_size = input_tensors->at(\"attention_mask\").shape[0];\n    const int request_seq_len    = input_tensors->at(\"attention_mask\").shape[2];\n    const int m                  = input_tensors->at(\"input_query\").shape[0];\n    const int k                  = hidden_units_;\n    const int n                  = hidden_units_;\n#ifdef SPARSITY_ENABLED\n    int m_tmp = m;\n    if (m_tmp % 16 != 0) {\n        m_tmp = (m_tmp / 16 + 1) * 16;\n    }\n    const int m_padded = m_tmp;\n#endif\n    const int fusedINT8QKV_type = cublas_wrapper->getFusedINT8QKVType(k, n, attention_weights);\n    if (int8_mode_ == 1) {\n        // K_int_buf_ V_int_buf_ should point to correct buffer according to m\n        K_int_buf_ = (int*)Q_int_buf_ + m * head_num_ * size_per_head_;\n        V_int_buf_ = (int*)K_int_buf_ + m * head_num_ * size_per_head_;\n        PUSH_RANGE(\"qkv_gemm\");\n        if (fusedINT8QKV_type == 0) {\n            cublas_wrapper->Gemm(\n                Q_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->query_weight.kernel));\n            cublas_wrapper->Gemm(\n                K_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->key_weight.kernel));\n            cublas_wrapper->Gemm(\n                V_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->value_weight.kernel));\n        }\n        else {\n            int strideFactor = (fusedINT8QKV_type == 1) ? (sizeof(T) / sizeof(int8_t)) : 1;\n            cublas_wrapper->Gemm(Q_int_buf_,\n                                 3,\n                                 m,\n                                 n,\n                                 k,\n                                 0,\n                                 n * k * strideFactor,\n                                 n * m,\n                                 from_tensor,\n                                 (int8_t*)(attention_weights->query_weight.kernel));\n        }\n        POP_RANGE;\n\n        PUSH_RANGE(\"invokeTrtAddQkvBiasInt32Iint8O\");\n        invokeTrtAddQkvBiasInt32Iint8O(qkv_buf_,\n                                       Q_int_buf_,\n                                       attention_weights->query_weight.bias,\n                                       attention_weights->key_weight.bias,\n                                       attention_weights->value_weight.bias,\n                                       m,\n                                       head_num_,\n                                       size_per_head_,\n                                       &(scale_list->d_scale_list_[2]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_ + hidden_units_]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_ + 2 * hidden_units_]),\n                                       scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                       stream_);\n        POP_RANGE;\n    }\n    else if (int8_mode_ == 2 || int8_mode_ == 3) {\n        // K_int_buf_ V_int_buf_ should point to correct buffer according to m\n        K_int_buf_ = (int*)((int8_t*)Q_int_buf_ + m * head_num_ * size_per_head_);\n        V_int_buf_ = (int*)((int8_t*)K_int_buf_ + m * head_num_ * size_per_head_);\n\n#ifdef SPARSITY_ENABLED\n        if (sparse_) {\n            PUSH_RANGE(\"qkv_gemm\");\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                   (int8_t*)(attention_weights->query_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)Q_int_buf_);\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                                   (int8_t*)(attention_weights->key_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)K_int_buf_);\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 2],\n                                   (int8_t*)(attention_weights->value_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)V_int_buf_);\n            POP_RANGE;\n        }\n        else {\n#endif\n            PUSH_RANGE(\"qkv_gemm\");\n            if (fusedINT8QKV_type == 0) {\n                cublas_wrapper->Gemm((int8_t*)Q_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->query_weight.kernel));\n                cublas_wrapper->Gemm((int8_t*)K_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->key_weight.kernel));\n                cublas_wrapper->Gemm((int8_t*)V_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 2],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->value_weight.kernel));\n            }\n            else {\n                int strideFactor = (fusedINT8QKV_type == 1) ? (sizeof(T) / sizeof(int8_t)) : 1;\n                cublas_wrapper->Gemm((int8_t*)Q_int_buf_,\n                                     3,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     n * k * strideFactor,\n                                     n * m,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->query_weight.kernel));\n            }\n            POP_RANGE;\n#ifdef SPARSITY_ENABLED\n        }\n        if (sparse_) {\n            PUSH_RANGE(\"invokeTrtAddQkvBiasInt8IO\");\n            invokeTrtAddQkvBiasInt8IORow(qkv_buf_,\n                                         (int8_t*)Q_int_buf_,\n                                         attention_weights->query_weight.bias,\n                                         attention_weights->key_weight.bias,\n                                         attention_weights->value_weight.bias,\n                                         m,\n                                         head_num_,\n                                         size_per_head_,\n                                         &(scale_list->d_scale_list_[4 + 1]),\n                                         &(scale_list->d_scale_list_[12 + 1]),\n                                         &(scale_list->d_scale_list_[20 + 1]),\n                                         scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                         stream_);\n            POP_RANGE;\n        }\n        else {\n#endif\n            PUSH_RANGE(\"invokeTrtAddQkvBiasInt8IO\");\n            invokeTrtAddQkvBiasInt8IO(qkv_buf_,\n                                      (int8_t*)Q_int_buf_,\n                                      attention_weights->query_weight.bias,\n                                      attention_weights->key_weight.bias,\n                                      attention_weights->value_weight.bias,\n                                      m,\n                                      head_num_,\n                                      size_per_head_,\n                                      &(scale_list->d_scale_list_[4 + 1]),\n                                      &(scale_list->d_scale_list_[12 + 1]),\n                                      &(scale_list->d_scale_list_[20 + 1]),\n                                      scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                      stream_);\n            POP_RANGE;\n#ifdef SPARSITY_ENABLED\n        }\n#endif\n    }\n\n    int S = dispatcher_int8_->getSFromMaxSeqLen(request_seq_len);\n    FT_CHECK(dispatcher_int8_->isValid(S, false));\n    const int B = input_tensors->at(\"padding_offset\").shape[0] - 1;\n    // setScaleList should be executed before setup\n    dispatcher_int8_->setScaleList(scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                   scale_list->h_scale_list_[scale_list->p4_offset_ + 1] / 127.0f,\n                                   scale_list->h_scale_list_[scale_list->p4_offset_ + 2] / 127.0f);\n    dispatcher_int8_->setup(S, B);\n    PUSH_RANGE(\"fused mha\");\n    dispatcher_int8_->run(qkv_buf_, nullptr, padding_offset, attn_workspace_, qkv_buf_2_, stream_);\n    POP_RANGE;\n    sync_check_cuda_error();\n\n#ifdef SPARSITY_ENABLED\n    if (!sparse_) {\n#endif\n        // qkv_buf_2_ is [batch*seqlen, hidden_dim] row-major\n        invokeRowMajorToCOL32(qkv_buf_, qkv_buf_2_, m, hidden_units_, stream_);\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    PUSH_RANGE(\"proj gemm\");\n    if (int8_mode_ == 1) {\n        cublas_wrapper->Gemm(\n            attention_out, 1, m, n, k, 0, 0, 0, qkv_buf_, (int8_t*)(attention_weights->attention_output_weight.kernel));\n    }\n    else if (int8_mode_ == 2 || int8_mode_ == 3) {\n#ifdef SPARSITY_ENABLED\n        if (sparse_) {\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                                   (int8_t*)(attention_weights->attention_output_weight.sp_kernel),\n                                   qkv_buf_2_,\n                                   (int8_t*)attention_out);\n        }\n        else {\n#endif\n            cublas_wrapper->Gemm((int8_t*)attention_out,\n                                 1,\n                                 m,\n                                 n,\n                                 k,\n                                 0,\n                                 0,\n                                 0,\n                                 scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                                 qkv_buf_,\n                                 (int8_t*)(attention_weights->attention_output_weight.kernel));\n#ifdef SPARSITY_ENABLED\n        }\n#endif\n    }\n    POP_RANGE;\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::FusedAttentionLayerINT8(size_t           max_batch_size,\n                                                    size_t           max_seq_len,\n                                                    size_t           head_num,\n                                                    size_t           size_per_head,\n                                                    int              sm,\n                                                    float            q_scaling,\n                                                    int              int8_mode,\n                                                    cudaStream_t     stream,\n                                                    cublasMMWrapper* cublas_wrapper,\n                                                    IAllocator*      allocator,\n                                                    bool             is_free_buffer_after_forward,\n                                                    bool             sparse):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_size_(max_batch_size),\n    max_seq_len_(max_seq_len),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    sm_(sm),\n    q_scaling_(q_scaling),\n    int8_mode_(int8_mode),\n    sparse_(sparse)\n{\n    if ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72)\n        && (size_per_head_ == 64 || size_per_head_ == 32)) {\n        dispatcher_int8_.reset(new FusedMHARunnerInt8v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayerINT8 not support \\n\"));\n    }\n    hidden_units_ = head_num_ * size_per_head_;\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::FusedAttentionLayerINT8(FusedAttentionLayerINT8<T> const& attention_layer):\n    BaseAttentionLayer<T>(attention_layer.stream_,\n                          attention_layer.cublas_wrapper_,\n                          attention_layer.allocator_,\n                          attention_layer.is_free_buffer_after_forward_),\n    max_batch_size_(attention_layer.max_batch_size_),\n    max_seq_len_(attention_layer.max_seq_len_),\n    head_num_(attention_layer.head_num_),\n    size_per_head_(attention_layer.size_per_head_),\n    hidden_units_(attention_layer.hidden_units_),\n    sm_(attention_layer.sm_),\n    q_scaling_(attention_layer.q_scaling_),\n    int8_mode_(attention_layer.int8_mode_),\n    sparse_(attention_layer.sparse_)\n{\n    if ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72)\n        && (size_per_head_ == 64 || size_per_head_ == 32)) {\n        dispatcher_int8_.reset(new FusedMHARunnerInt8v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayerINT8 not support \\n\"));\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::~FusedAttentionLayerINT8()\n{\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::allocateBuffer()\n{\n    if (is_allocate_buffer_ == false) {\n        Q_int_buf_ = (int32_t*)allocator_->reMalloc(\n            Q_int_buf_, sizeof(int32_t) * max_batch_size_ * max_seq_len_ * hidden_units_ * 3, false);\n        K_int_buf_ = Q_int_buf_ + max_batch_size_ * max_seq_len_ * hidden_units_;\n        V_int_buf_ = K_int_buf_ + max_batch_size_ * max_seq_len_ * hidden_units_;\n        qkv_buf_   = (int8_t*)allocator_->reMalloc(\n            qkv_buf_, (sizeof(int8_t) * 3 * max_batch_size_ * max_seq_len_ * hidden_units_ + 3) / 4 * 4, false);\n        qkv_buf_2_ = (int8_t*)allocator_->reMalloc(\n            qkv_buf_2_, (sizeof(int8_t) * max_batch_size_ * max_seq_len_ * hidden_units_ + 3) / 4 * 4, false);\n        attn_workspace_ = (T*)allocator_->reMalloc(attn_workspace_, dispatcher_int8_->getWorkspaceSize(), false);\n\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::freeBuffer()\n{\n    if (is_allocate_buffer_ == true) {\n        allocator_->free((void**)(&Q_int_buf_));\n        allocator_->free((void**)(&qkv_buf_));\n        allocator_->free((void**)(&qkv_buf_2_));\n        allocator_->free((void**)(&attn_workspace_));\n\n        is_allocate_buffer_ = false;\n        sync_check_cuda_error();\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayerINT8<T>::isValidBatchSize(size_t batch_size)\n{\n    if (max_batch_size_ == 0) {\n        max_batch_size_ = batch_size;\n        return true;\n    }\n    else {\n        return batch_size <= max_batch_size_;\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayerINT8<T>::isValidSeqLen(size_t seq_len)\n{\n    if (max_seq_len_ == 0) {\n        max_seq_len_ = seq_len;\n    }\n    if (sm_ < 75 || (sm_ == 75 && size_per_head_ == 64))\n        return seq_len <= max_seq_len_ && seq_len <= 384;\n    else\n        return seq_len <= max_seq_len_ && seq_len <= 512;\n}\n\ntemplate class FusedAttentionLayerINT8<float>;\ntemplate class FusedAttentionLayerINT8<half>;\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/layout_transformer_int8_kernels.h\"\n#include \"src/fastertransformer/layers/attention_layers_int8/FusedAttentionLayerINT8.h\"\n#include \"src/fastertransformer/utils/nvtx_utils.h\"\n\nnamespace fastertransformer {\n\n// add bias and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// input is INT32 && per axis quantization for weight\n// output is INT8 && per tensor quantization\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\n// using char4 as output, int4 as input\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_COL32_int32IInt8O(char4*       output,\n                                                   const int4*  QKV,\n                                                   const T*     bias_Q,\n                                                   const T*     bias_K,\n                                                   const T*     bias_V,\n                                                   const float* input_deQFactor_div127_ptr,\n                                                   const float* q_weight_amax,\n                                                   const float* k_weight_amax,\n                                                   const float* v_weight_amax,\n                                                   const float  qkv_output_scale,\n                                                   const int    valid_word_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    int       hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* weight_amax = qkv_id == 0 ? q_weight_amax : (qkv_id == 1 ? k_weight_amax : v_weight_amax);\n        const float  input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = (qkv_id * valid_word_num * head_num_x_size_per_head\n                              + ((hidden_id & 0xffffffe0) * valid_word_num + (seq_id << 5) + (hidden_id & 31)))\n                             >> 2;\n\n        char4      tmp;\n        const int4 tmp_int4 = __ldg(QKV + input_id);\n\n        tmp.x =\n            float_to_int8_rn((static_cast<float>(tmp_int4.x) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.y =\n            float_to_int8_rn((static_cast<float>(tmp_int4.y) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.z =\n            float_to_int8_rn((static_cast<float>(tmp_int4.z) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        hidden_id += 1;\n        tmp.w =\n            float_to_int8_rn((static_cast<float>(tmp_int4.w) * __ldg(weight_amax + hidden_id) * input_deQFactor_div127\n                              + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n                             * qkv_output_scale);\n\n        // const int output_id = (seq_id * 3 * head_num_x_size_per_head + head_id * 3 * size_per_head + qkv_id *\n        // size_per_head + size_id) >> 2;\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt32Iint8O(int8_t*            output,\n                                    const int32_t*     Q,\n                                    const T*           bias_Q,\n                                    const T*           bias_K,\n                                    const T*           bias_V,\n                                    const size_t       token_num,\n                                    const size_t       head_num,\n                                    const size_t       size_per_head,\n                                    const float*       input_deQFactor_div127_ptr,\n                                    const float*       q_weight_amax,\n                                    const float*       k_weight_amax,\n                                    const float*       v_weight_amax,\n                                    const float        mScaleQkv,\n                                    const hipStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_COL32_int32IInt8O<<<grid, block, 0, stream>>>((char4*)output,\n                                                                   (const int4*)Q,\n                                                                   bias_Q,\n                                                                   bias_K,\n                                                                   bias_V,\n                                                                   input_deQFactor_div127_ptr,\n                                                                   q_weight_amax,\n                                                                   k_weight_amax,\n                                                                   v_weight_amax,\n                                                                   1.f / mScaleQkv,\n                                                                   token_num,\n                                                                   head_num,\n                                                                   size_per_head,\n                                                                   head_num_x_size_per_head);\n}\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + CUBLASLT_ORDER_COL32) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_COL32_int8IO(char4*       output,\n                                              const char4* QKV,\n                                              const T*     bias_Q,\n                                              const T*     bias_K,\n                                              const T*     bias_V,\n                                              const float* q_input_deQFactor_ptr,\n                                              const float* k_input_deQFactor_ptr,\n                                              const float* v_input_deQFactor_ptr,\n                                              const float  qkv_output_scale,\n                                              const int    valid_word_num,\n                                              const int    head_num,\n                                              const int    size_per_head,\n                                              const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* input_deQFactor_ptr =\n            (qkv_id == 0) ? q_input_deQFactor_ptr : ((qkv_id == 1) ? k_input_deQFactor_ptr : v_input_deQFactor_ptr);\n        const float input_deQFactor = __ldg(input_deQFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id = (qkv_id * valid_word_num * head_num_x_size_per_head\n                              + ((hidden_id & 0xffffffe0) * valid_word_num + (seq_id << 5) + (hidden_id & 31)))\n                             >> 2;\n\n        char4 tmp = __ldg(QKV + input_id);\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        // const int output_id = (seq_id * 3 * head_num_x_size_per_head + head_id * 3 * size_per_head + qkv_id *\n        // size_per_head + size_id) >> 2;\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt8IO(int8_t*            output,\n                               const int8_t*      Q,\n                               const T*           bias_Q,\n                               const T*           bias_K,\n                               const T*           bias_V,\n                               const size_t       token_num,\n                               const size_t       head_num,\n                               const size_t       size_per_head,\n                               const float*       q_input_deQFactor_ptr,\n                               const float*       k_input_deQFactor_ptr,\n                               const float*       v_input_deQFactor_ptr,\n                               const float        mScaleQkv,\n                               const hipStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_COL32_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                              (const char4*)Q,\n                                                              bias_Q,\n                                                              bias_K,\n                                                              bias_V,\n                                                              q_input_deQFactor_ptr,\n                                                              k_input_deQFactor_ptr,\n                                                              v_input_deQFactor_ptr,\n                                                              1.0f / mScaleQkv,\n                                                              token_num,\n                                                              head_num,\n                                                              size_per_head,\n                                                              head_num_x_size_per_head);\n}\n\n// Add bias, and then transform from\n// 3 * ([valid_word_num, head*size] + row-major) -> [valid_word_num, head, 3, size] + row-major\n// grid((head*size + 31)/32, (valid_word_num + 31)/32, 3)\n// block(8, 32)\n// size should be a multiple of 4\ntemplate<typename T>\n__global__ void trt_add_QKV_bias_ROW_int8IO(char4*       output,\n                                            const char4* QKV,\n                                            const T*     bias_Q,\n                                            const T*     bias_K,\n                                            const T*     bias_V,\n                                            const float* q_input_deQFactor_ptr,\n                                            const float* k_input_deQFactor_ptr,\n                                            const float* v_input_deQFactor_ptr,\n                                            const float  qkv_output_scale,\n                                            const int    valid_word_num,\n                                            const int    head_num,\n                                            const int    size_per_head,\n                                            const int    head_num_x_size_per_head)\n{\n    const int qkv_id     = blockIdx.z;\n    const int seq_id     = (blockIdx.y << 5) + threadIdx.y;\n    const int threadIdx4 = threadIdx.x << 2;\n    const int hidden_id  = (blockIdx.x << 5) + threadIdx4;\n    const int size_id    = hidden_id % size_per_head;\n    const int head_id    = hidden_id / size_per_head;\n\n    const bool qual = (seq_id < valid_word_num) && (hidden_id < head_num_x_size_per_head);\n    if (qual) {\n        const float* input_deQFactor_ptr =\n            (qkv_id == 0) ? q_input_deQFactor_ptr : ((qkv_id == 1) ? k_input_deQFactor_ptr : v_input_deQFactor_ptr);\n        const float input_deQFactor = __ldg(input_deQFactor_ptr);\n\n        const T* bias_ptr = (qkv_id == 0) ? bias_Q : ((qkv_id == 1) ? bias_K : bias_V);\n\n        const int input_id =\n            (qkv_id * valid_word_num * head_num_x_size_per_head + seq_id * head_num_x_size_per_head + hidden_id) >> 2;\n\n        char4 tmp = __ldg(QKV + input_id);\n\n        tmp.x = float_to_int8_rn(\n            (static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id)))\n            * qkv_output_scale);\n\n        tmp.y = float_to_int8_rn(\n            (static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 1)))\n            * qkv_output_scale);\n\n        tmp.z = float_to_int8_rn(\n            (static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 2)))\n            * qkv_output_scale);\n\n        tmp.w = float_to_int8_rn(\n            (static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias_ptr + hidden_id + 3)))\n            * qkv_output_scale);\n\n        const int output_id =\n            ((seq_id * head_num_x_size_per_head + head_id * size_per_head) * 3 + qkv_id * size_per_head + size_id) >> 2;\n\n        output[output_id] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeTrtAddQkvBiasInt8IORow(int8_t*            output,\n                                  const int8_t*      Q,\n                                  const T*           bias_Q,\n                                  const T*           bias_K,\n                                  const T*           bias_V,\n                                  const size_t       token_num,\n                                  const size_t       head_num,\n                                  const size_t       size_per_head,\n                                  const float*       q_input_deQFactor_ptr,\n                                  const float*       k_input_deQFactor_ptr,\n                                  const float*       v_input_deQFactor_ptr,\n                                  const float        mScaleQkv,\n                                  const hipStream_t stream)\n{\n\n    int  head_num_x_size_per_head = head_num * size_per_head;\n    dim3 grid((head_num_x_size_per_head + 31) / 32, (token_num + 31) / 32, 3);\n    dim3 block(8, 32);\n\n    assert(size_per_head % 4 == 0);\n\n    trt_add_QKV_bias_ROW_int8IO<<<grid, block, 0, stream>>>((char4*)output,\n                                                            (const char4*)Q,\n                                                            bias_Q,\n                                                            bias_K,\n                                                            bias_V,\n                                                            q_input_deQFactor_ptr,\n                                                            k_input_deQFactor_ptr,\n                                                            v_input_deQFactor_ptr,\n                                                            1.0f / mScaleQkv,\n                                                            token_num,\n                                                            head_num,\n                                                            size_per_head,\n                                                            head_num_x_size_per_head);\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::forward(TensorMap*                output_tensors,\n                                         TensorMap*                input_tensors,\n                                         const AttentionWeight<T>* attention_weights)\n{\n    // input_tensors:\n    //      input_query [token_num, hidden_dimension],\n    //      attention_mask [batch, 1, seqlen, seqlen],\n    //      padding_offset [token_num]\n    // output_tensors:\n    //      hidden_features (token_num, hidden_dimension)\n\n    const ScaleList*     scale_list     = ((const AttentionINT8Weight<T>*)attention_weights)->scale_list_ptr;\n    cublasINT8MMWrapper* cublas_wrapper = (cublasINT8MMWrapper*)cublas_wrapper_;\n\n    FT_CHECK(isValidBatchSize(input_tensors->at(\"attention_mask\").shape[0]));\n    FT_CHECK(isValidSeqLen(input_tensors->at(\"attention_mask\").shape[2]));\n    allocateBuffer();\n\n    int32_t*      attention_out  = output_tensors->getPtr<int32_t>(\"hidden_features\");\n    const int8_t* from_tensor    = input_tensors->getPtr<int8_t>(\"input_query\");\n    const T*      attention_mask = input_tensors->getPtr<T>(\"attention_mask\");\n    const int*    padding_offset = input_tensors->getPtr<int>(\"padding_offset\");\n\n    const int request_batch_size = input_tensors->at(\"attention_mask\").shape[0];\n    const int request_seq_len    = input_tensors->at(\"attention_mask\").shape[2];\n    const int m                  = input_tensors->at(\"input_query\").shape[0];\n    const int k                  = hidden_units_;\n    const int n                  = hidden_units_;\n#ifdef SPARSITY_ENABLED\n    int m_tmp = m;\n    if (m_tmp % 16 != 0) {\n        m_tmp = (m_tmp / 16 + 1) * 16;\n    }\n    const int m_padded = m_tmp;\n#endif\n    const int fusedINT8QKV_type = cublas_wrapper->getFusedINT8QKVType(k, n, attention_weights);\n    if (int8_mode_ == 1) {\n        // K_int_buf_ V_int_buf_ should point to correct buffer according to m\n        K_int_buf_ = (int*)Q_int_buf_ + m * head_num_ * size_per_head_;\n        V_int_buf_ = (int*)K_int_buf_ + m * head_num_ * size_per_head_;\n        PUSH_RANGE(\"qkv_gemm\");\n        if (fusedINT8QKV_type == 0) {\n            cublas_wrapper->Gemm(\n                Q_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->query_weight.kernel));\n            cublas_wrapper->Gemm(\n                K_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->key_weight.kernel));\n            cublas_wrapper->Gemm(\n                V_int_buf_, 1, m, n, k, 0, 0, 0, from_tensor, (int8_t*)(attention_weights->value_weight.kernel));\n        }\n        else {\n            int strideFactor = (fusedINT8QKV_type == 1) ? (sizeof(T) / sizeof(int8_t)) : 1;\n            cublas_wrapper->Gemm(Q_int_buf_,\n                                 3,\n                                 m,\n                                 n,\n                                 k,\n                                 0,\n                                 n * k * strideFactor,\n                                 n * m,\n                                 from_tensor,\n                                 (int8_t*)(attention_weights->query_weight.kernel));\n        }\n        POP_RANGE;\n\n        PUSH_RANGE(\"invokeTrtAddQkvBiasInt32Iint8O\");\n        invokeTrtAddQkvBiasInt32Iint8O(qkv_buf_,\n                                       Q_int_buf_,\n                                       attention_weights->query_weight.bias,\n                                       attention_weights->key_weight.bias,\n                                       attention_weights->value_weight.bias,\n                                       m,\n                                       head_num_,\n                                       size_per_head_,\n                                       &(scale_list->d_scale_list_[2]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_ + hidden_units_]),\n                                       &(scale_list->d_scale_list_[scale_list->p2_offset_ + 2 * hidden_units_]),\n                                       scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                       stream_);\n        POP_RANGE;\n    }\n    else if (int8_mode_ == 2 || int8_mode_ == 3) {\n        // K_int_buf_ V_int_buf_ should point to correct buffer according to m\n        K_int_buf_ = (int*)((int8_t*)Q_int_buf_ + m * head_num_ * size_per_head_);\n        V_int_buf_ = (int*)((int8_t*)K_int_buf_ + m * head_num_ * size_per_head_);\n\n#ifdef SPARSITY_ENABLED\n        if (sparse_) {\n            PUSH_RANGE(\"qkv_gemm\");\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                   (int8_t*)(attention_weights->query_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)Q_int_buf_);\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                                   (int8_t*)(attention_weights->key_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)K_int_buf_);\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 2],\n                                   (int8_t*)(attention_weights->value_weight.sp_kernel),\n                                   from_tensor,\n                                   (int8_t*)V_int_buf_);\n            POP_RANGE;\n        }\n        else {\n#endif\n            PUSH_RANGE(\"qkv_gemm\");\n            if (fusedINT8QKV_type == 0) {\n                cublas_wrapper->Gemm((int8_t*)Q_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->query_weight.kernel));\n                cublas_wrapper->Gemm((int8_t*)K_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 1],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->key_weight.kernel));\n                cublas_wrapper->Gemm((int8_t*)V_int_buf_,\n                                     1,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     0,\n                                     0,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 2],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->value_weight.kernel));\n            }\n            else {\n                int strideFactor = (fusedINT8QKV_type == 1) ? (sizeof(T) / sizeof(int8_t)) : 1;\n                cublas_wrapper->Gemm((int8_t*)Q_int_buf_,\n                                     3,\n                                     m,\n                                     n,\n                                     k,\n                                     0,\n                                     n * k * strideFactor,\n                                     n * m,\n                                     scale_list->h_scale_list_[scale_list->p3_offset_ + 0],\n                                     from_tensor,\n                                     (int8_t*)(attention_weights->query_weight.kernel));\n            }\n            POP_RANGE;\n#ifdef SPARSITY_ENABLED\n        }\n        if (sparse_) {\n            PUSH_RANGE(\"invokeTrtAddQkvBiasInt8IO\");\n            invokeTrtAddQkvBiasInt8IORow(qkv_buf_,\n                                         (int8_t*)Q_int_buf_,\n                                         attention_weights->query_weight.bias,\n                                         attention_weights->key_weight.bias,\n                                         attention_weights->value_weight.bias,\n                                         m,\n                                         head_num_,\n                                         size_per_head_,\n                                         &(scale_list->d_scale_list_[4 + 1]),\n                                         &(scale_list->d_scale_list_[12 + 1]),\n                                         &(scale_list->d_scale_list_[20 + 1]),\n                                         scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                         stream_);\n            POP_RANGE;\n        }\n        else {\n#endif\n            PUSH_RANGE(\"invokeTrtAddQkvBiasInt8IO\");\n            invokeTrtAddQkvBiasInt8IO(qkv_buf_,\n                                      (int8_t*)Q_int_buf_,\n                                      attention_weights->query_weight.bias,\n                                      attention_weights->key_weight.bias,\n                                      attention_weights->value_weight.bias,\n                                      m,\n                                      head_num_,\n                                      size_per_head_,\n                                      &(scale_list->d_scale_list_[4 + 1]),\n                                      &(scale_list->d_scale_list_[12 + 1]),\n                                      &(scale_list->d_scale_list_[20 + 1]),\n                                      scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                      stream_);\n            POP_RANGE;\n#ifdef SPARSITY_ENABLED\n        }\n#endif\n    }\n\n    int S = dispatcher_int8_->getSFromMaxSeqLen(request_seq_len);\n    FT_CHECK(dispatcher_int8_->isValid(S, false));\n    const int B = input_tensors->at(\"padding_offset\").shape[0] - 1;\n    // setScaleList should be executed before setup\n    dispatcher_int8_->setScaleList(scale_list->h_scale_list_[scale_list->p4_offset_] / 127.0f,\n                                   scale_list->h_scale_list_[scale_list->p4_offset_ + 1] / 127.0f,\n                                   scale_list->h_scale_list_[scale_list->p4_offset_ + 2] / 127.0f);\n    dispatcher_int8_->setup(S, B);\n    PUSH_RANGE(\"fused mha\");\n    dispatcher_int8_->run(qkv_buf_, nullptr, padding_offset, attn_workspace_, qkv_buf_2_, stream_);\n    POP_RANGE;\n    sync_check_cuda_error();\n\n#ifdef SPARSITY_ENABLED\n    if (!sparse_) {\n#endif\n        // qkv_buf_2_ is [batch*seqlen, hidden_dim] row-major\n        invokeRowMajorToCOL32(qkv_buf_, qkv_buf_2_, m, hidden_units_, stream_);\n#ifdef SPARSITY_ENABLED\n    }\n#endif\n\n    PUSH_RANGE(\"proj gemm\");\n    if (int8_mode_ == 1) {\n        cublas_wrapper->Gemm(\n            attention_out, 1, m, n, k, 0, 0, 0, qkv_buf_, (int8_t*)(attention_weights->attention_output_weight.kernel));\n    }\n    else if (int8_mode_ == 2 || int8_mode_ == 3) {\n#ifdef SPARSITY_ENABLED\n        if (sparse_) {\n            cublas_wrapper->SpGemm(n,\n                                   m_padded,\n                                   k,\n                                   scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                                   (int8_t*)(attention_weights->attention_output_weight.sp_kernel),\n                                   qkv_buf_2_,\n                                   (int8_t*)attention_out);\n        }\n        else {\n#endif\n            cublas_wrapper->Gemm((int8_t*)attention_out,\n                                 1,\n                                 m,\n                                 n,\n                                 k,\n                                 0,\n                                 0,\n                                 0,\n                                 scale_list->h_scale_list_[scale_list->p3_offset_ + 5],\n                                 qkv_buf_,\n                                 (int8_t*)(attention_weights->attention_output_weight.kernel));\n#ifdef SPARSITY_ENABLED\n        }\n#endif\n    }\n    POP_RANGE;\n    if (is_free_buffer_after_forward_ == true) {\n        freeBuffer();\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::FusedAttentionLayerINT8(size_t           max_batch_size,\n                                                    size_t           max_seq_len,\n                                                    size_t           head_num,\n                                                    size_t           size_per_head,\n                                                    int              sm,\n                                                    float            q_scaling,\n                                                    int              int8_mode,\n                                                    hipStream_t     stream,\n                                                    cublasMMWrapper* cublas_wrapper,\n                                                    IAllocator*      allocator,\n                                                    bool             is_free_buffer_after_forward,\n                                                    bool             sparse):\n    BaseAttentionLayer<T>(stream, cublas_wrapper, allocator, is_free_buffer_after_forward),\n    max_batch_size_(max_batch_size),\n    max_seq_len_(max_seq_len),\n    head_num_(head_num),\n    size_per_head_(size_per_head),\n    sm_(sm),\n    q_scaling_(q_scaling),\n    int8_mode_(int8_mode),\n    sparse_(sparse)\n{\n    if ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72)\n        && (size_per_head_ == 64 || size_per_head_ == 32)) {\n        dispatcher_int8_.reset(new FusedMHARunnerInt8v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayerINT8 not support \\n\"));\n    }\n    hidden_units_ = head_num_ * size_per_head_;\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::FusedAttentionLayerINT8(FusedAttentionLayerINT8<T> const& attention_layer):\n    BaseAttentionLayer<T>(attention_layer.stream_,\n                          attention_layer.cublas_wrapper_,\n                          attention_layer.allocator_,\n                          attention_layer.is_free_buffer_after_forward_),\n    max_batch_size_(attention_layer.max_batch_size_),\n    max_seq_len_(attention_layer.max_seq_len_),\n    head_num_(attention_layer.head_num_),\n    size_per_head_(attention_layer.size_per_head_),\n    hidden_units_(attention_layer.hidden_units_),\n    sm_(attention_layer.sm_),\n    q_scaling_(attention_layer.q_scaling_),\n    int8_mode_(attention_layer.int8_mode_),\n    sparse_(attention_layer.sparse_)\n{\n    if ((sm_ == kSM_86 || sm_ == kSM_80 || sm_ == kSM_75 || sm_ == kSM_72)\n        && (size_per_head_ == 64 || size_per_head_ == 32)) {\n        dispatcher_int8_.reset(new FusedMHARunnerInt8v2(head_num_, size_per_head_, sm_, q_scaling_));\n    }\n    else {\n        throw std::runtime_error(std::string(\"[FT][ERROR] FusedAttentionLayerINT8 not support \\n\"));\n    }\n}\n\ntemplate<typename T>\nFusedAttentionLayerINT8<T>::~FusedAttentionLayerINT8()\n{\n    cublas_wrapper_ = nullptr;\n    freeBuffer();\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::allocateBuffer()\n{\n    if (is_allocate_buffer_ == false) {\n        Q_int_buf_ = (int32_t*)allocator_->reMalloc(\n            Q_int_buf_, sizeof(int32_t) * max_batch_size_ * max_seq_len_ * hidden_units_ * 3, false);\n        K_int_buf_ = Q_int_buf_ + max_batch_size_ * max_seq_len_ * hidden_units_;\n        V_int_buf_ = K_int_buf_ + max_batch_size_ * max_seq_len_ * hidden_units_;\n        qkv_buf_   = (int8_t*)allocator_->reMalloc(\n            qkv_buf_, (sizeof(int8_t) * 3 * max_batch_size_ * max_seq_len_ * hidden_units_ + 3) / 4 * 4, false);\n        qkv_buf_2_ = (int8_t*)allocator_->reMalloc(\n            qkv_buf_2_, (sizeof(int8_t) * max_batch_size_ * max_seq_len_ * hidden_units_ + 3) / 4 * 4, false);\n        attn_workspace_ = (T*)allocator_->reMalloc(attn_workspace_, dispatcher_int8_->getWorkspaceSize(), false);\n\n        is_allocate_buffer_ = true;\n    }\n}\n\ntemplate<typename T>\nvoid FusedAttentionLayerINT8<T>::freeBuffer()\n{\n    if (is_allocate_buffer_ == true) {\n        allocator_->free((void**)(&Q_int_buf_));\n        allocator_->free((void**)(&qkv_buf_));\n        allocator_->free((void**)(&qkv_buf_2_));\n        allocator_->free((void**)(&attn_workspace_));\n\n        is_allocate_buffer_ = false;\n        sync_check_cuda_error();\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayerINT8<T>::isValidBatchSize(size_t batch_size)\n{\n    if (max_batch_size_ == 0) {\n        max_batch_size_ = batch_size;\n        return true;\n    }\n    else {\n        return batch_size <= max_batch_size_;\n    }\n}\n\ntemplate<typename T>\nbool FusedAttentionLayerINT8<T>::isValidSeqLen(size_t seq_len)\n{\n    if (max_seq_len_ == 0) {\n        max_seq_len_ = seq_len;\n    }\n    if (sm_ < 75 || (sm_ == 75 && size_per_head_ == 64))\n        return seq_len <= max_seq_len_ && seq_len <= 384;\n    else\n        return seq_len <= max_seq_len_ && seq_len <= 512;\n}\n\ntemplate class FusedAttentionLayerINT8<float>;\ntemplate class FusedAttentionLayerINT8<half>;\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/layers/attention_layers_int8/FusedAttentionLayerINT8.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/quantization_int8_kernels.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n__global__ void quantized_kernel(char4* dst, const float4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float  scale = __ldg(scale_ptr);\n        char4        tmp;\n        const float4 floatTmp = __ldg(src + tid);\n        tmp.x                 = float_to_int8_rn(floatTmp.x * scale);\n        tmp.y                 = float_to_int8_rn(floatTmp.y * scale);\n        tmp.z                 = float_to_int8_rn(floatTmp.z * scale);\n        tmp.w                 = float_to_int8_rn(floatTmp.w * scale);\n        dst[tid]              = tmp;\n    }\n}\n\n__global__ void quantized_kernel(char4* dst, const half2* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale = __ldg(scale_ptr);\n        char4       tmp;\n        int         src_id = tid << 1;\n\n        const half2 half2Tmp = __ldg(src + src_id);\n        tmp.x                = float_to_int8_rn(static_cast<float>(half2Tmp.x) * scale);\n        tmp.y                = float_to_int8_rn(static_cast<float>(half2Tmp.y) * scale);\n\n        const half2 half2Tmp2 = __ldg(src + src_id + 1);\n        tmp.z                 = float_to_int8_rn(static_cast<float>(half2Tmp2.x) * scale);\n        tmp.w                 = float_to_int8_rn(static_cast<float>(half2Tmp2.y) * scale);\n        dst[tid]              = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeQuantization(int8_t* dst, const T* src, const int size, const float* scale_ptr, cudaStream_t stream)\n{\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        quantized_kernel<<<grid, block, 0, stream>>>((char4*)dst, (const float4*)src, size / 4, scale_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        quantized_kernel<<<grid, block, 0, stream>>>((char4*)dst, (const half2*)src, size / 4, scale_ptr);\n    }\n}\n\ntemplate void\ninvokeQuantization<float>(int8_t* dst, const float* src, const int size, const float* scale_ptr, cudaStream_t stream);\n\ntemplate void\ninvokeQuantization<half>(int8_t* dst, const half* src, const int size, const float* scale_ptr, cudaStream_t stream);\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/quantization_int8_kernels.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n__global__ void quantized_kernel(char4* dst, const float4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float  scale = __ldg(scale_ptr);\n        char4        tmp;\n        const float4 floatTmp = __ldg(src + tid);\n        tmp.x                 = float_to_int8_rn(floatTmp.x * scale);\n        tmp.y                 = float_to_int8_rn(floatTmp.y * scale);\n        tmp.z                 = float_to_int8_rn(floatTmp.z * scale);\n        tmp.w                 = float_to_int8_rn(floatTmp.w * scale);\n        dst[tid]              = tmp;\n    }\n}\n\n__global__ void quantized_kernel(char4* dst, const half2* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale = __ldg(scale_ptr);\n        char4       tmp;\n        int         src_id = tid << 1;\n\n        const half2 half2Tmp = __ldg(src + src_id);\n        tmp.x                = float_to_int8_rn(static_cast<float>(half2Tmp.x) * scale);\n        tmp.y                = float_to_int8_rn(static_cast<float>(half2Tmp.y) * scale);\n\n        const half2 half2Tmp2 = __ldg(src + src_id + 1);\n        tmp.z                 = float_to_int8_rn(static_cast<float>(half2Tmp2.x) * scale);\n        tmp.w                 = float_to_int8_rn(static_cast<float>(half2Tmp2.y) * scale);\n        dst[tid]              = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeQuantization(int8_t* dst, const T* src, const int size, const float* scale_ptr, hipStream_t stream)\n{\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        quantized_kernel<<<grid, block, 0, stream>>>((char4*)dst, (const float4*)src, size / 4, scale_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        quantized_kernel<<<grid, block, 0, stream>>>((char4*)dst, (const half2*)src, size / 4, scale_ptr);\n    }\n}\n\ntemplate void\ninvokeQuantization<float>(int8_t* dst, const float* src, const int size, const float* scale_ptr, hipStream_t stream);\n\ntemplate void\ninvokeQuantization<half>(int8_t* dst, const half* src, const int size, const float* scale_ptr, hipStream_t stream);\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/quantization_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/sampling_penalty_kernels.h\"\n\nnamespace fastertransformer {\n\n// TODO Add half2 implementation\ntemplate<typename T>\n__global__ void applyTemperaturePenalty(T*          logits,\n                                        const T*    bias,\n                                        const float temperature_inverse,\n                                        const int   m,\n                                        const int   vocab_size,\n                                        const int   vocab_size_padd)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? 65504.F : FLT_MAX;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < m * vocab_size_padd;\n         index += blockDim.x * gridDim.x) {\n        T bias_val = bias == nullptr ? (T)(0.0f) : bias[index % vocab_size_padd];\n        if (index % vocab_size_padd < vocab_size) {\n            logits[index] = (logits[index] + bias_val) * (T)temperature_inverse;\n        }\n        else {\n            logits[index] = -MAX_T_VAL;\n        }\n    }\n}\n\ntemplate<>\n__global__ void applyTemperaturePenalty(half2*       logits,\n                                        const half2* bias,\n                                        const float  temperature_inverse,\n                                        const int    batch_size,\n                                        const int    vocab_size,\n                                        const int    vocab_size_padded)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n    const half2 mask_val = __float2half2_rn(-65504.0f);\n    const half2 temp_inv = __float2half2_rn(temperature_inverse);\n\n    const int half_vocab_size        = vocab_size / 2;\n    const int half_vocab_size_padded = vocab_size_padded / 2;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * half_vocab_size_padded;\n         index += blockDim.x * gridDim.x) {\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logits[index] = __hmul2(logit, temp_inv);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeApplyTemperaturePenalty(T*           logits,\n                                   const T*     bias,\n                                   const float  temperature,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padd,\n                                   cudaStream_t stream)\n{\n    dim3    block(min(vocab_size_padd, 1024));\n    dim3    grid(min(batch_size * vocab_size_padd / block.x, 65536));\n    const T temperature_inverse = (T)(1.f / (temperature + 1e-6f));\n    if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padd % 2 == 0) {\n        applyTemperaturePenalty<<<grid, block, 0, stream>>>(reinterpret_cast<half2*>(logits),\n                                                            reinterpret_cast<const half2*>(bias),\n                                                            temperature_inverse,\n                                                            batch_size,\n                                                            vocab_size,\n                                                            vocab_size_padd);\n    }\n    else {\n        applyTemperaturePenalty<T>\n            <<<grid, block, 0, stream>>>(logits, bias, temperature_inverse, batch_size, vocab_size, vocab_size_padd);\n    }\n}\n\ntemplate void invokeApplyTemperaturePenalty(float*       logits,\n                                            const float* bias,\n                                            const float  temperature,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int    vocab_size_padd,\n                                            cudaStream_t stream);\n\ntemplate void invokeApplyTemperaturePenalty(half*        logits,\n                                            const half*  bias,\n                                            const float  temperature,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int    vocab_size_padd,\n                                            cudaStream_t stream);\n\ntemplate<typename T>\n__global__ void batchApplyTemperaturePenalty(T*           logits,\n                                             const T*     bias,\n                                             const float* temperatures,\n                                             const int    batch_size,\n                                             const int    vocab_size,\n                                             const int    vocab_size_padd)\n{\n    // TODO: Add macro or device function to get MAX_T_VAL.\n    const bool              IS_FP16   = std::is_same<T, half>::value;\n    const T                 MAX_T_VAL = (IS_FP16) ? 65504.F : FLT_MAX;\n    extern __shared__ float inv_temperatures[];\n    if (threadIdx.x < batch_size) {\n        inv_temperatures[threadIdx.x] = 1.0f / (temperatures[threadIdx.x] + 1e-6f);\n    }\n    __syncthreads();\n\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * vocab_size_padd;\n         index += blockDim.x * gridDim.x) {\n        int batch_idx = index / vocab_size_padd;\n        int vocab_idx = index % vocab_size_padd;\n        T   logit     = (vocab_idx < vocab_size) ? logits[index] : -MAX_T_VAL;\n        if (vocab_idx < vocab_size) {\n            if (bias != nullptr) {\n                logit += bias[vocab_idx];\n            }\n            logit *= inv_temperatures[batch_idx];\n        }\n        logits[index] = logit;\n    }\n}\n\n__global__ void batchApplyTemperaturePenalty_h2(half2*       logits,\n                                                const half2* bias,\n                                                const float* temperatures,\n                                                const int    batch_size,\n                                                const int    vocab_size,\n                                                const int    vocab_size_padded)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n    extern __shared__ half2 h2_inv_temperatures[];\n    if (threadIdx.x < batch_size) {\n        h2_inv_temperatures[threadIdx.x] = __float2half2_rn(1.f / (temperatures[threadIdx.x] + 1e-6f));\n    }\n    __syncthreads();\n\n    const half2 mask_val               = __float2half2_rn(-65504.0f);\n    const int   half_vocab_size        = vocab_size / 2;\n    const int   half_vocab_size_padded = vocab_size_padded / 2;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * half_vocab_size_padded;\n         index += blockDim.x * gridDim.x) {\n        int   batch_idx = index / half_vocab_size_padded;\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logits[index] = __hmul2(logit, h2_inv_temperatures[batch_idx]);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchApplyTemperaturePenalty(T*           logits,\n                                        const T*     bias,\n                                        const float* temperatures,\n                                        const int    batch_size,\n                                        const int    vocab_size,\n                                        const int    vocab_size_padd,\n                                        cudaStream_t stream)\n{\n    dim3 block(min(vocab_size_padd, 1024));\n    dim3 grid(min(batch_size * vocab_size_padd / block.x, 65536));\n    if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padd % 2 == 0) {\n        size_t smem_size = sizeof(half2) * batch_size;\n        batchApplyTemperaturePenalty_h2<<<grid, block, smem_size, stream>>>(reinterpret_cast<half2*>(logits),\n                                                                            reinterpret_cast<const half2*>(bias),\n                                                                            temperatures,\n                                                                            batch_size,\n                                                                            vocab_size,\n                                                                            vocab_size_padd);\n    }\n    else {\n        size_t smem_size = sizeof(float) * batch_size;\n        batchApplyTemperaturePenalty<T>\n            <<<grid, block, smem_size, stream>>>(logits, bias, temperatures, batch_size, vocab_size, vocab_size_padd);\n    }\n}\n\ntemplate void invokeBatchApplyTemperaturePenalty(float*       logits,\n                                                 const float* bias,\n                                                 const float* temperatures,\n                                                 const int    batch_size,\n                                                 const int    vocab_size,\n                                                 const int    vocab_size_padd,\n                                                 cudaStream_t stream);\n\ntemplate void invokeBatchApplyTemperaturePenalty(half*        logits,\n                                                 const half*  bias,\n                                                 const float* temperatures,\n                                                 const int    batch_size,\n                                                 const int    vocab_size,\n                                                 const int    vocab_size_padd,\n                                                 cudaStream_t stream);\n\ntemplate<typename T, RepetitionPenaltyType penalty_type>\n__global__ void applyRepetitionPenalty(T*          logits,\n                                       const float penalty,\n                                       const int*  start_ids,\n                                       int*        output_ids,\n                                       const int   batch_size,\n                                       const int   local_batch_size,\n                                       const int   vocab_size,\n                                       const int   vocab_size_padd,\n                                       const int*  input_lengths,\n                                       const int   max_input_len,\n                                       const int   step)\n{\n    extern __shared__ float penalty_logits[];\n    int*                    penalty_indices = (int*)(penalty_logits + step);\n\n    logits                 = logits + blockIdx.x * vocab_size_padd;\n    const int input_length = input_lengths != nullptr ? input_lengths[blockIdx.x] : max_input_len;\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n\n        if (index >= input_length && index < max_input_len) {\n            continue;\n        }\n\n        // output_ids shape: (input_len + output_len, batch_size)\n        int penalty_index = output_ids[index * batch_size + blockIdx.x];\n        if (penalty_index >= vocab_size) {\n            continue;\n        }\n        penalty_indices[index] = penalty_index;\n        float logit            = (float)logits[penalty_index];\n        if (penalty_type == RepetitionPenaltyType::Additive) {\n            penalty_logits[index] = logit - penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n            penalty_logits[index] = logit < 0.0f ? logit * penalty : logit / penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::None) {\n            penalty_logits[index] = logit;\n        }\n        else {\n            // Unsupported type\n            assert(false);\n        }\n    }\n\n    if (blockDim.x > 32) {\n        __syncthreads();\n    }\n\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n\n        if (index >= input_length && index < max_input_len) {\n            continue;\n        }\n\n        // output_ids shape: (input_len + output_len, batch_size)\n        if (penalty_indices[index] >= vocab_size) {\n            continue;\n        }\n        logits[penalty_indices[index]] = penalty_logits[index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeApplyRepetitionPenalty(T*                          logits,\n                                  const float                 penalty,\n                                  const int*                  start_ids,\n                                  int*                        output_ids,\n                                  const int                   batch_size,\n                                  const int                   local_batch_size,\n                                  const int                   vocab_size,\n                                  const int                   vocab_size_padd,\n                                  const int*                  input_lengths,\n                                  const int                   max_input_len,\n                                  const int                   step,\n                                  const RepetitionPenaltyType penalty_type,\n                                  cudaStream_t                stream)\n{\n    dim3   block(min(step, 1024));\n    dim3   grid(local_batch_size);\n    size_t smem_size = step * (sizeof(float) + sizeof(int));\n\n    if (penalty_type == RepetitionPenaltyType::Additive) {\n        applyRepetitionPenalty<T, RepetitionPenaltyType::Additive><<<grid, block, smem_size, stream>>>(logits,\n                                                                                                       penalty,\n                                                                                                       start_ids,\n                                                                                                       output_ids,\n                                                                                                       batch_size,\n                                                                                                       local_batch_size,\n                                                                                                       vocab_size,\n                                                                                                       vocab_size_padd,\n                                                                                                       input_lengths,\n                                                                                                       max_input_len,\n                                                                                                       step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n        applyRepetitionPenalty<T, RepetitionPenaltyType::Multiplicative>\n            <<<grid, block, smem_size, stream>>>(logits,\n                                                 penalty,\n                                                 start_ids,\n                                                 output_ids,\n                                                 batch_size,\n                                                 local_batch_size,\n                                                 vocab_size,\n                                                 vocab_size_padd,\n                                                 input_lengths,\n                                                 max_input_len,\n                                                 step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::None) {\n        // do nothing\n    }\n}\n\ntemplate void invokeApplyRepetitionPenalty(float*                      logits,\n                                           const float                 penalty,\n                                           const int*                  start_ids,\n                                           int*                        output_ids,\n                                           const int                   batch_size,\n                                           const int                   local_batch_size,\n                                           const int                   vocab_size,\n                                           const int                   vocab_size_padd,\n                                           const int*                  input_lengths,\n                                           const int                   max_input_len,\n                                           const int                   step,\n                                           const RepetitionPenaltyType penalty_type,\n                                           cudaStream_t                stream);\n\ntemplate void invokeApplyRepetitionPenalty(half*                       logits,\n                                           const float                 penalty,\n                                           const int*                  start_ids,\n                                           int*                        output_ids,\n                                           const int                   batch_size,\n                                           const int                   local_batch_size,\n                                           const int                   vocab_size,\n                                           const int                   vocab_size_padd,\n                                           const int*                  input_lengths,\n                                           const int                   max_input_len,\n                                           const int                   step,\n                                           const RepetitionPenaltyType penalty_type,\n                                           cudaStream_t                stream);\n\ntemplate<typename T, RepetitionPenaltyType penalty_type>\n__global__ void batchApplyRepetitionPenalty(T*           logits,\n                                            const float* penalties,\n                                            const int*   output_ids,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int*   input_lengths,\n                                            const int    max_input_length,\n                                            const int    step)\n{\n    extern __shared__ float penalty_logits[];\n    int*                    penalty_indices = (int*)(penalty_logits + step);\n    const int               batch_idx       = blockIdx.x;\n    const float             penalty         = penalties[batch_idx];\n    const int               input_length    = input_lengths != nullptr ? input_lengths[batch_idx] : max_input_length;\n\n    logits += batch_idx * vocab_size;\n\n    // Phase 1. Find indices to penalize and keep the penalized values.\n    // A vocab id can appear multiple times but should be penalized once.\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n        // Skip the padding tokens in input sequences.\n        if (index >= input_length && index < max_input_length) {\n            continue;\n        }\n        // output_ids shape: (input_len + output_len, batch_size)\n        int penalty_index = output_ids[index * batch_size + batch_idx];\n        assert(penalty_index < vocab_size);\n        penalty_indices[index] = penalty_index;\n        float logit            = (float)logits[penalty_index];\n        if (penalty_type == RepetitionPenaltyType::Additive) {\n            penalty_logits[index] = logit - penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n            penalty_logits[index] = logit < 0.0f ? logit * penalty : logit / penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::None) {\n            penalty_logits[index] = logit;\n        }\n        else {\n            // Unsupported type\n            assert(false);\n        }\n    }\n\n    if (blockDim.x > 32) {\n        __syncthreads();\n    }\n\n    // Phase 2. Replace a logit value by the penalized one.\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n        // Skip the padding tokens in input sequences.\n        if (index >= input_length && index < max_input_length) {\n            continue;\n        }\n        logits[penalty_indices[index]] = penalty_logits[index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchApplyRepetitionPenalty(T*                    logits,\n                                       const float*          penalties,\n                                       const int*            output_ids,\n                                       const int             batch_size,\n                                       const int             local_batch_size,\n                                       const int             vocab_size,\n                                       const int*            input_lengths,\n                                       const int             max_input_length,\n                                       const int             step,\n                                       RepetitionPenaltyType penalty_type,\n                                       cudaStream_t          stream)\n{\n    // Inputs\n    //   logits [local_batch_size, vocab_size] : logit values.\n    //   penalties [local_batch_size] : repetition penalty factors.\n    //   output_ids [step, batch_size] : output token ids (with offset ite * local_batch_size).\n    //   input_lengths [local_batch_size], input lengths (optional).\n    //      Padding tokens at [input_length, max_input_length) of input will not be penalized.\n    dim3   block(min(step, 1024));\n    dim3   grid(local_batch_size);\n    size_t smem_size = step * (sizeof(float) + sizeof(int));\n    if (penalty_type == RepetitionPenaltyType::Additive) {\n        batchApplyRepetitionPenalty<T, RepetitionPenaltyType::Additive><<<grid, block, smem_size, stream>>>(\n            logits, penalties, output_ids, batch_size, vocab_size, input_lengths, max_input_length, step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n        batchApplyRepetitionPenalty<T, RepetitionPenaltyType::Multiplicative><<<grid, block, smem_size, stream>>>(\n            logits, penalties, output_ids, batch_size, vocab_size, input_lengths, max_input_length, step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::None) {\n        // do nothing\n    }\n}\n\ntemplate void invokeBatchApplyRepetitionPenalty(float*                logits,\n                                                const float*          penalties,\n                                                const int*            output_ids,\n                                                const int             batch_size,\n                                                const int             local_batch_size,\n                                                const int             vocab_size,\n                                                const int*            input_lengths,\n                                                const int             max_input_length,\n                                                const int             step,\n                                                RepetitionPenaltyType penalty_type,\n                                                cudaStream_t          stream);\n\ntemplate void invokeBatchApplyRepetitionPenalty(half*                 logits,\n                                                const float*          penalties,\n                                                const int*            output_ids,\n                                                const int             batch_size,\n                                                const int             local_batch_size,\n                                                const int             vocab_size,\n                                                const int*            input_lengths,\n                                                const int             max_input_length,\n                                                const int             step,\n                                                RepetitionPenaltyType penalty_type,\n                                                cudaStream_t          stream);\n\ntemplate<typename T>\n__global__ void batchApplyMinLengthPenalty(T*         logits,\n                                           const int* min_lengths,\n                                           const int* end_ids,\n                                           const int* sequence_lengths,\n                                           const int  max_input_length,\n                                           const int  vocab_size_padded)\n{\n    int bid = threadIdx.x + blockIdx.x * blockDim.x;  // batch index\n    // We need +1 because sequence_lengths = max_input_length + num_gen_tokens - 1,\n    // which is equal to the length of k/v caches.\n    if (sequence_lengths[bid] + 1 - max_input_length < min_lengths[bid]) {\n        T mask_val                                     = (std::is_same<T, half>::value) ? -65504.0f : -FLT_MAX;\n        logits[bid * vocab_size_padded + end_ids[bid]] = mask_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeMinLengthPenalty(T*           logits,\n                            const int*   min_lengths,\n                            const int*   end_ids,\n                            const int*   sequnece_lengths,\n                            const int    max_input_length,\n                            const int    batch_size,\n                            const int    vocab_size_padded,\n                            cudaStream_t stream)\n\n{\n    const int block_size = min(batch_size, 1024);\n    const int grid_size  = (batch_size + block_size - 1) / block_size;\n    batchApplyMinLengthPenalty<<<grid_size, block_size, 0, stream>>>(\n        logits, min_lengths, end_ids, sequnece_lengths, max_input_length, vocab_size_padded);\n}\n\ntemplate void invokeMinLengthPenalty(float*       logits,\n                                     const int*   min_lengths,\n                                     const int*   end_ids,\n                                     const int*   sequnece_lengths,\n                                     const int    max_input_length,\n                                     const int    batch_size,\n                                     const int    vocab_size_padded,\n                                     cudaStream_t stream);\n\ntemplate void invokeMinLengthPenalty(half*        logits,\n                                     const int*   min_lengths,\n                                     const int*   end_ids,\n                                     const int*   sequnece_lengths,\n                                     const int    max_input_length,\n                                     const int    batch_size,\n                                     const int    vocab_size_padded,\n                                     cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n#include <float.h>\n\n#include \"src/fastertransformer/kernels/sampling_penalty_kernels.h\"\n\nnamespace fastertransformer {\n\n// TODO Add half2 implementation\ntemplate<typename T>\n__global__ void applyTemperaturePenalty(T*          logits,\n                                        const T*    bias,\n                                        const float temperature_inverse,\n                                        const int   m,\n                                        const int   vocab_size,\n                                        const int   vocab_size_padd)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? 65504.F : FLT_MAX;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < m * vocab_size_padd;\n         index += blockDim.x * gridDim.x) {\n        T bias_val = bias == nullptr ? (T)(0.0f) : bias[index % vocab_size_padd];\n        if (index % vocab_size_padd < vocab_size) {\n            logits[index] = (logits[index] + bias_val) * (T)temperature_inverse;\n        }\n        else {\n            logits[index] = -MAX_T_VAL;\n        }\n    }\n}\n\ntemplate<>\n__global__ void applyTemperaturePenalty(half2*       logits,\n                                        const half2* bias,\n                                        const float  temperature_inverse,\n                                        const int    batch_size,\n                                        const int    vocab_size,\n                                        const int    vocab_size_padded)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n    const half2 mask_val = __float2half2_rn(-65504.0f);\n    const half2 temp_inv = __float2half2_rn(temperature_inverse);\n\n    const int half_vocab_size        = vocab_size / 2;\n    const int half_vocab_size_padded = vocab_size_padded / 2;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * half_vocab_size_padded;\n         index += blockDim.x * gridDim.x) {\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logits[index] = __hmul2(logit, temp_inv);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeApplyTemperaturePenalty(T*           logits,\n                                   const T*     bias,\n                                   const float  temperature,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padd,\n                                   hipStream_t stream)\n{\n    dim3    block(min(vocab_size_padd, 1024));\n    dim3    grid(min(batch_size * vocab_size_padd / block.x, 65536));\n    const T temperature_inverse = (T)(1.f / (temperature + 1e-6f));\n    if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padd % 2 == 0) {\n        applyTemperaturePenalty<<<grid, block, 0, stream>>>(reinterpret_cast<half2*>(logits),\n                                                            reinterpret_cast<const half2*>(bias),\n                                                            temperature_inverse,\n                                                            batch_size,\n                                                            vocab_size,\n                                                            vocab_size_padd);\n    }\n    else {\n        applyTemperaturePenalty<T>\n            <<<grid, block, 0, stream>>>(logits, bias, temperature_inverse, batch_size, vocab_size, vocab_size_padd);\n    }\n}\n\ntemplate void invokeApplyTemperaturePenalty(float*       logits,\n                                            const float* bias,\n                                            const float  temperature,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int    vocab_size_padd,\n                                            hipStream_t stream);\n\ntemplate void invokeApplyTemperaturePenalty(half*        logits,\n                                            const half*  bias,\n                                            const float  temperature,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int    vocab_size_padd,\n                                            hipStream_t stream);\n\ntemplate<typename T>\n__global__ void batchApplyTemperaturePenalty(T*           logits,\n                                             const T*     bias,\n                                             const float* temperatures,\n                                             const int    batch_size,\n                                             const int    vocab_size,\n                                             const int    vocab_size_padd)\n{\n    // TODO: Add macro or device function to get MAX_T_VAL.\n    const bool              IS_FP16   = std::is_same<T, half>::value;\n    const T                 MAX_T_VAL = (IS_FP16) ? 65504.F : FLT_MAX;\n    extern __shared__ float inv_temperatures[];\n    if (threadIdx.x < batch_size) {\n        inv_temperatures[threadIdx.x] = 1.0f / (temperatures[threadIdx.x] + 1e-6f);\n    }\n    __syncthreads();\n\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * vocab_size_padd;\n         index += blockDim.x * gridDim.x) {\n        int batch_idx = index / vocab_size_padd;\n        int vocab_idx = index % vocab_size_padd;\n        T   logit     = (vocab_idx < vocab_size) ? logits[index] : -MAX_T_VAL;\n        if (vocab_idx < vocab_size) {\n            if (bias != nullptr) {\n                logit += bias[vocab_idx];\n            }\n            logit *= inv_temperatures[batch_idx];\n        }\n        logits[index] = logit;\n    }\n}\n\n__global__ void batchApplyTemperaturePenalty_h2(half2*       logits,\n                                                const half2* bias,\n                                                const float* temperatures,\n                                                const int    batch_size,\n                                                const int    vocab_size,\n                                                const int    vocab_size_padded)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n    extern __shared__ half2 h2_inv_temperatures[];\n    if (threadIdx.x < batch_size) {\n        h2_inv_temperatures[threadIdx.x] = __float2half2_rn(1.f / (temperatures[threadIdx.x] + 1e-6f));\n    }\n    __syncthreads();\n\n    const half2 mask_val               = __float2half2_rn(-65504.0f);\n    const int   half_vocab_size        = vocab_size / 2;\n    const int   half_vocab_size_padded = vocab_size_padded / 2;\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * half_vocab_size_padded;\n         index += blockDim.x * gridDim.x) {\n        int   batch_idx = index / half_vocab_size_padded;\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logits[index] = __hmul2(logit, h2_inv_temperatures[batch_idx]);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchApplyTemperaturePenalty(T*           logits,\n                                        const T*     bias,\n                                        const float* temperatures,\n                                        const int    batch_size,\n                                        const int    vocab_size,\n                                        const int    vocab_size_padd,\n                                        hipStream_t stream)\n{\n    dim3 block(min(vocab_size_padd, 1024));\n    dim3 grid(min(batch_size * vocab_size_padd / block.x, 65536));\n    if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padd % 2 == 0) {\n        size_t smem_size = sizeof(half2) * batch_size;\n        batchApplyTemperaturePenalty_h2<<<grid, block, smem_size, stream>>>(reinterpret_cast<half2*>(logits),\n                                                                            reinterpret_cast<const half2*>(bias),\n                                                                            temperatures,\n                                                                            batch_size,\n                                                                            vocab_size,\n                                                                            vocab_size_padd);\n    }\n    else {\n        size_t smem_size = sizeof(float) * batch_size;\n        batchApplyTemperaturePenalty<T>\n            <<<grid, block, smem_size, stream>>>(logits, bias, temperatures, batch_size, vocab_size, vocab_size_padd);\n    }\n}\n\ntemplate void invokeBatchApplyTemperaturePenalty(float*       logits,\n                                                 const float* bias,\n                                                 const float* temperatures,\n                                                 const int    batch_size,\n                                                 const int    vocab_size,\n                                                 const int    vocab_size_padd,\n                                                 hipStream_t stream);\n\ntemplate void invokeBatchApplyTemperaturePenalty(half*        logits,\n                                                 const half*  bias,\n                                                 const float* temperatures,\n                                                 const int    batch_size,\n                                                 const int    vocab_size,\n                                                 const int    vocab_size_padd,\n                                                 hipStream_t stream);\n\ntemplate<typename T, RepetitionPenaltyType penalty_type>\n__global__ void applyRepetitionPenalty(T*          logits,\n                                       const float penalty,\n                                       const int*  start_ids,\n                                       int*        output_ids,\n                                       const int   batch_size,\n                                       const int   local_batch_size,\n                                       const int   vocab_size,\n                                       const int   vocab_size_padd,\n                                       const int*  input_lengths,\n                                       const int   max_input_len,\n                                       const int   step)\n{\n    extern __shared__ float penalty_logits[];\n    int*                    penalty_indices = (int*)(penalty_logits + step);\n\n    logits                 = logits + blockIdx.x * vocab_size_padd;\n    const int input_length = input_lengths != nullptr ? input_lengths[blockIdx.x] : max_input_len;\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n\n        if (index >= input_length && index < max_input_len) {\n            continue;\n        }\n\n        // output_ids shape: (input_len + output_len, batch_size)\n        int penalty_index = output_ids[index * batch_size + blockIdx.x];\n        if (penalty_index >= vocab_size) {\n            continue;\n        }\n        penalty_indices[index] = penalty_index;\n        float logit            = (float)logits[penalty_index];\n        if (penalty_type == RepetitionPenaltyType::Additive) {\n            penalty_logits[index] = logit - penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n            penalty_logits[index] = logit < 0.0f ? logit * penalty : logit / penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::None) {\n            penalty_logits[index] = logit;\n        }\n        else {\n            // Unsupported type\n            assert(false);\n        }\n    }\n\n    if (blockDim.x > 32) {\n        __syncthreads();\n    }\n\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n\n        if (index >= input_length && index < max_input_len) {\n            continue;\n        }\n\n        // output_ids shape: (input_len + output_len, batch_size)\n        if (penalty_indices[index] >= vocab_size) {\n            continue;\n        }\n        logits[penalty_indices[index]] = penalty_logits[index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeApplyRepetitionPenalty(T*                          logits,\n                                  const float                 penalty,\n                                  const int*                  start_ids,\n                                  int*                        output_ids,\n                                  const int                   batch_size,\n                                  const int                   local_batch_size,\n                                  const int                   vocab_size,\n                                  const int                   vocab_size_padd,\n                                  const int*                  input_lengths,\n                                  const int                   max_input_len,\n                                  const int                   step,\n                                  const RepetitionPenaltyType penalty_type,\n                                  hipStream_t                stream)\n{\n    dim3   block(min(step, 1024));\n    dim3   grid(local_batch_size);\n    size_t smem_size = step * (sizeof(float) + sizeof(int));\n\n    if (penalty_type == RepetitionPenaltyType::Additive) {\n        applyRepetitionPenalty<T, RepetitionPenaltyType::Additive><<<grid, block, smem_size, stream>>>(logits,\n                                                                                                       penalty,\n                                                                                                       start_ids,\n                                                                                                       output_ids,\n                                                                                                       batch_size,\n                                                                                                       local_batch_size,\n                                                                                                       vocab_size,\n                                                                                                       vocab_size_padd,\n                                                                                                       input_lengths,\n                                                                                                       max_input_len,\n                                                                                                       step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n        applyRepetitionPenalty<T, RepetitionPenaltyType::Multiplicative>\n            <<<grid, block, smem_size, stream>>>(logits,\n                                                 penalty,\n                                                 start_ids,\n                                                 output_ids,\n                                                 batch_size,\n                                                 local_batch_size,\n                                                 vocab_size,\n                                                 vocab_size_padd,\n                                                 input_lengths,\n                                                 max_input_len,\n                                                 step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::None) {\n        // do nothing\n    }\n}\n\ntemplate void invokeApplyRepetitionPenalty(float*                      logits,\n                                           const float                 penalty,\n                                           const int*                  start_ids,\n                                           int*                        output_ids,\n                                           const int                   batch_size,\n                                           const int                   local_batch_size,\n                                           const int                   vocab_size,\n                                           const int                   vocab_size_padd,\n                                           const int*                  input_lengths,\n                                           const int                   max_input_len,\n                                           const int                   step,\n                                           const RepetitionPenaltyType penalty_type,\n                                           hipStream_t                stream);\n\ntemplate void invokeApplyRepetitionPenalty(half*                       logits,\n                                           const float                 penalty,\n                                           const int*                  start_ids,\n                                           int*                        output_ids,\n                                           const int                   batch_size,\n                                           const int                   local_batch_size,\n                                           const int                   vocab_size,\n                                           const int                   vocab_size_padd,\n                                           const int*                  input_lengths,\n                                           const int                   max_input_len,\n                                           const int                   step,\n                                           const RepetitionPenaltyType penalty_type,\n                                           hipStream_t                stream);\n\ntemplate<typename T, RepetitionPenaltyType penalty_type>\n__global__ void batchApplyRepetitionPenalty(T*           logits,\n                                            const float* penalties,\n                                            const int*   output_ids,\n                                            const int    batch_size,\n                                            const int    vocab_size,\n                                            const int*   input_lengths,\n                                            const int    max_input_length,\n                                            const int    step)\n{\n    extern __shared__ float penalty_logits[];\n    int*                    penalty_indices = (int*)(penalty_logits + step);\n    const int               batch_idx       = blockIdx.x;\n    const float             penalty         = penalties[batch_idx];\n    const int               input_length    = input_lengths != nullptr ? input_lengths[batch_idx] : max_input_length;\n\n    logits += batch_idx * vocab_size;\n\n    // Phase 1. Find indices to penalize and keep the penalized values.\n    // A vocab id can appear multiple times but should be penalized once.\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n        // Skip the padding tokens in input sequences.\n        if (index >= input_length && index < max_input_length) {\n            continue;\n        }\n        // output_ids shape: (input_len + output_len, batch_size)\n        int penalty_index = output_ids[index * batch_size + batch_idx];\n        assert(penalty_index < vocab_size);\n        penalty_indices[index] = penalty_index;\n        float logit            = (float)logits[penalty_index];\n        if (penalty_type == RepetitionPenaltyType::Additive) {\n            penalty_logits[index] = logit - penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n            penalty_logits[index] = logit < 0.0f ? logit * penalty : logit / penalty;\n        }\n        else if (penalty_type == RepetitionPenaltyType::None) {\n            penalty_logits[index] = logit;\n        }\n        else {\n            // Unsupported type\n            assert(false);\n        }\n    }\n\n    if (blockDim.x > 32) {\n        __syncthreads();\n    }\n\n    // Phase 2. Replace a logit value by the penalized one.\n    for (int index = threadIdx.x; index < step; index += blockDim.x) {\n        // Skip the padding tokens in input sequences.\n        if (index >= input_length && index < max_input_length) {\n            continue;\n        }\n        logits[penalty_indices[index]] = penalty_logits[index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchApplyRepetitionPenalty(T*                    logits,\n                                       const float*          penalties,\n                                       const int*            output_ids,\n                                       const int             batch_size,\n                                       const int             local_batch_size,\n                                       const int             vocab_size,\n                                       const int*            input_lengths,\n                                       const int             max_input_length,\n                                       const int             step,\n                                       RepetitionPenaltyType penalty_type,\n                                       hipStream_t          stream)\n{\n    // Inputs\n    //   logits [local_batch_size, vocab_size] : logit values.\n    //   penalties [local_batch_size] : repetition penalty factors.\n    //   output_ids [step, batch_size] : output token ids (with offset ite * local_batch_size).\n    //   input_lengths [local_batch_size], input lengths (optional).\n    //      Padding tokens at [input_length, max_input_length) of input will not be penalized.\n    dim3   block(min(step, 1024));\n    dim3   grid(local_batch_size);\n    size_t smem_size = step * (sizeof(float) + sizeof(int));\n    if (penalty_type == RepetitionPenaltyType::Additive) {\n        batchApplyRepetitionPenalty<T, RepetitionPenaltyType::Additive><<<grid, block, smem_size, stream>>>(\n            logits, penalties, output_ids, batch_size, vocab_size, input_lengths, max_input_length, step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::Multiplicative) {\n        batchApplyRepetitionPenalty<T, RepetitionPenaltyType::Multiplicative><<<grid, block, smem_size, stream>>>(\n            logits, penalties, output_ids, batch_size, vocab_size, input_lengths, max_input_length, step);\n    }\n    else if (penalty_type == RepetitionPenaltyType::None) {\n        // do nothing\n    }\n}\n\ntemplate void invokeBatchApplyRepetitionPenalty(float*                logits,\n                                                const float*          penalties,\n                                                const int*            output_ids,\n                                                const int             batch_size,\n                                                const int             local_batch_size,\n                                                const int             vocab_size,\n                                                const int*            input_lengths,\n                                                const int             max_input_length,\n                                                const int             step,\n                                                RepetitionPenaltyType penalty_type,\n                                                hipStream_t          stream);\n\ntemplate void invokeBatchApplyRepetitionPenalty(half*                 logits,\n                                                const float*          penalties,\n                                                const int*            output_ids,\n                                                const int             batch_size,\n                                                const int             local_batch_size,\n                                                const int             vocab_size,\n                                                const int*            input_lengths,\n                                                const int             max_input_length,\n                                                const int             step,\n                                                RepetitionPenaltyType penalty_type,\n                                                hipStream_t          stream);\n\ntemplate<typename T>\n__global__ void batchApplyMinLengthPenalty(T*         logits,\n                                           const int* min_lengths,\n                                           const int* end_ids,\n                                           const int* sequence_lengths,\n                                           const int  max_input_length,\n                                           const int  vocab_size_padded)\n{\n    int bid = threadIdx.x + blockIdx.x * blockDim.x;  // batch index\n    // We need +1 because sequence_lengths = max_input_length + num_gen_tokens - 1,\n    // which is equal to the length of k/v caches.\n    if (sequence_lengths[bid] + 1 - max_input_length < min_lengths[bid]) {\n        T mask_val                                     = (std::is_same<T, half>::value) ? -65504.0f : -FLT_MAX;\n        logits[bid * vocab_size_padded + end_ids[bid]] = mask_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeMinLengthPenalty(T*           logits,\n                            const int*   min_lengths,\n                            const int*   end_ids,\n                            const int*   sequnece_lengths,\n                            const int    max_input_length,\n                            const int    batch_size,\n                            const int    vocab_size_padded,\n                            hipStream_t stream)\n\n{\n    const int block_size = min(batch_size, 1024);\n    const int grid_size  = (batch_size + block_size - 1) / block_size;\n    batchApplyMinLengthPenalty<<<grid_size, block_size, 0, stream>>>(\n        logits, min_lengths, end_ids, sequnece_lengths, max_input_length, vocab_size_padded);\n}\n\ntemplate void invokeMinLengthPenalty(float*       logits,\n                                     const int*   min_lengths,\n                                     const int*   end_ids,\n                                     const int*   sequnece_lengths,\n                                     const int    max_input_length,\n                                     const int    batch_size,\n                                     const int    vocab_size_padded,\n                                     hipStream_t stream);\n\ntemplate void invokeMinLengthPenalty(half*        logits,\n                                     const int*   min_lengths,\n                                     const int*   end_ids,\n                                     const int*   sequnece_lengths,\n                                     const int    max_input_length,\n                                     const int    batch_size,\n                                     const int    vocab_size_padded,\n                                     hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/sampling_penalty_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/ban_bad_words.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void ban_bad_words(T*         logits,\n                              const int* output_ids_buf,\n                              const int* parent_ids_buf,\n                              int        batch_size,\n                              int        beam_width,\n                              const int* bad_words,\n                              size_t     bad_words_len,\n                              bool       share_words,\n                              int        id_offset,\n                              int        vocab_size_padded,\n                              size_t     step)\n{\n    const int id        = blockIdx.x * blockDim.x + threadIdx.x;\n    const int batch_idx = blockIdx.y / beam_width;\n    const int beam_idx  = blockIdx.y % beam_width;\n\n    const int* base_bad_words         = share_words ? bad_words : bad_words + batch_idx * 2 * bad_words_len;\n    const int* base_bad_words_offsets = base_bad_words + bad_words_len;\n\n    if (id >= bad_words_len || base_bad_words_offsets[id] < 0) {\n        return;\n    }\n\n    const int item_end   = base_bad_words_offsets[id];\n    const int item_start = (id > 0) ? base_bad_words_offsets[id - 1] : 0;\n    const int item_size  = item_end - item_start;\n\n    /* The single-token case unconditionally bans the token */\n    bool should_ban = item_size == 1;\n\n    /* Multi-token case and enough previously generated tokens to look for a match */\n    if (item_size > 1 && step >= item_size - 1) {\n        should_ban             = true;\n        int        parent_id   = beam_idx;\n        const bool gather_beam = beam_width > 1;\n\n        for (int token_idx = item_size - 2; token_idx >= 0; token_idx--) {\n            const int previous_token = output_ids_buf[(step - (item_size - 1) + token_idx) * batch_size * beam_width\n                                                      + id_offset + batch_idx * beam_width + parent_id];\n\n            if (previous_token != base_bad_words[item_start + token_idx]) {\n                should_ban = false;\n                break;\n            }\n            if (gather_beam) {\n                parent_id = parent_ids_buf[(step - (item_size - 1) + token_idx) * beam_width * batch_size + id_offset\n                                           + batch_idx * beam_width + parent_id];\n\n                if (parent_id < 0 || parent_id >= beam_width) {\n                    should_ban = false;\n                    break;\n                }\n            }\n        }\n    }\n\n    if (should_ban) {\n        int banned_token = base_bad_words[item_end - 1];\n        if (0 < banned_token && banned_token < vocab_size_padded) {\n            logits[batch_idx * beam_width * vocab_size_padded + beam_idx * vocab_size_padded + banned_token] =\n                static_cast<T>(-INFINITY);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBanBadWords(T*           logits,\n                       const int*   output_ids_buf,\n                       const int*   parent_ids_buf,\n                       int          batch_size,\n                       int          local_batch_size,\n                       int          beam_width,\n                       const int*   bad_words,\n                       bool         share_words,\n                       size_t       bad_words_len,\n                       int          id_offset,\n                       int          vocab_size_padded,\n                       size_t       step,\n                       cudaStream_t stream)\n{\n    dim3 block, grid;\n    block.x = min(((bad_words_len + 32 - 1) / 32) * 32, 256UL);\n    grid.x  = (bad_words_len + block.x - 1) / block.x;\n    grid.y  = local_batch_size * beam_width;\n\n    ban_bad_words<<<grid, block, 0, stream>>>(logits,\n                                              output_ids_buf,\n                                              parent_ids_buf,\n                                              batch_size,\n                                              beam_width,\n                                              bad_words,\n                                              bad_words_len,\n                                              share_words,\n                                              id_offset,\n                                              vocab_size_padded,\n                                              step);\n    sync_check_cuda_error();\n}\n\ntemplate void invokeBanBadWords(half*        logits,\n                                const int*   output_ids_buf,\n                                const int*   parent_ids_buf,\n                                int          batch_size,\n                                int          local_batch_size,\n                                int          beam_width,\n                                const int*   bad_words,\n                                bool         share_words,\n                                size_t       bad_words_len,\n                                int          id_offset,\n                                int          vocab_size_padded,\n                                size_t       step,\n                                cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBanBadWords(__nv_bfloat16* logits,\n                                const int*     output_ids_buf,\n                                const int*     parent_ids_buf,\n                                int            batch_size,\n                                int            local_batch_size,\n                                int            beam_width,\n                                const int*     bad_words,\n                                bool           share_words,\n                                size_t         bad_words_len,\n                                int            id_offset,\n                                int            vocab_size_padded,\n                                size_t         step,\n                                cudaStream_t   stream);\n#endif\ntemplate void invokeBanBadWords(float*       logits,\n                                const int*   output_ids_buf,\n                                const int*   parent_ids_buf,\n                                int          batch_size,\n                                int          local_batch_size,\n                                int          beam_width,\n                                const int*   bad_words,\n                                bool         share_words,\n                                size_t       bad_words_len,\n                                int          id_offset,\n                                int          vocab_size_padded,\n                                size_t       step,\n                                cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/ban_bad_words.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void ban_bad_words(T*         logits,\n                              const int* output_ids_buf,\n                              const int* parent_ids_buf,\n                              int        batch_size,\n                              int        beam_width,\n                              const int* bad_words,\n                              size_t     bad_words_len,\n                              bool       share_words,\n                              int        id_offset,\n                              int        vocab_size_padded,\n                              size_t     step)\n{\n    const int id        = blockIdx.x * blockDim.x + threadIdx.x;\n    const int batch_idx = blockIdx.y / beam_width;\n    const int beam_idx  = blockIdx.y % beam_width;\n\n    const int* base_bad_words         = share_words ? bad_words : bad_words + batch_idx * 2 * bad_words_len;\n    const int* base_bad_words_offsets = base_bad_words + bad_words_len;\n\n    if (id >= bad_words_len || base_bad_words_offsets[id] < 0) {\n        return;\n    }\n\n    const int item_end   = base_bad_words_offsets[id];\n    const int item_start = (id > 0) ? base_bad_words_offsets[id - 1] : 0;\n    const int item_size  = item_end - item_start;\n\n    /* The single-token case unconditionally bans the token */\n    bool should_ban = item_size == 1;\n\n    /* Multi-token case and enough previously generated tokens to look for a match */\n    if (item_size > 1 && step >= item_size - 1) {\n        should_ban             = true;\n        int        parent_id   = beam_idx;\n        const bool gather_beam = beam_width > 1;\n\n        for (int token_idx = item_size - 2; token_idx >= 0; token_idx--) {\n            const int previous_token = output_ids_buf[(step - (item_size - 1) + token_idx) * batch_size * beam_width\n                                                      + id_offset + batch_idx * beam_width + parent_id];\n\n            if (previous_token != base_bad_words[item_start + token_idx]) {\n                should_ban = false;\n                break;\n            }\n            if (gather_beam) {\n                parent_id = parent_ids_buf[(step - (item_size - 1) + token_idx) * beam_width * batch_size + id_offset\n                                           + batch_idx * beam_width + parent_id];\n\n                if (parent_id < 0 || parent_id >= beam_width) {\n                    should_ban = false;\n                    break;\n                }\n            }\n        }\n    }\n\n    if (should_ban) {\n        int banned_token = base_bad_words[item_end - 1];\n        if (0 < banned_token && banned_token < vocab_size_padded) {\n            logits[batch_idx * beam_width * vocab_size_padded + beam_idx * vocab_size_padded + banned_token] =\n                static_cast<T>(-INFINITY);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBanBadWords(T*           logits,\n                       const int*   output_ids_buf,\n                       const int*   parent_ids_buf,\n                       int          batch_size,\n                       int          local_batch_size,\n                       int          beam_width,\n                       const int*   bad_words,\n                       bool         share_words,\n                       size_t       bad_words_len,\n                       int          id_offset,\n                       int          vocab_size_padded,\n                       size_t       step,\n                       hipStream_t stream)\n{\n    dim3 block, grid;\n    block.x = min(((bad_words_len + 32 - 1) / 32) * 32, 256UL);\n    grid.x  = (bad_words_len + block.x - 1) / block.x;\n    grid.y  = local_batch_size * beam_width;\n\n    ban_bad_words<<<grid, block, 0, stream>>>(logits,\n                                              output_ids_buf,\n                                              parent_ids_buf,\n                                              batch_size,\n                                              beam_width,\n                                              bad_words,\n                                              bad_words_len,\n                                              share_words,\n                                              id_offset,\n                                              vocab_size_padded,\n                                              step);\n    sync_check_cuda_error();\n}\n\ntemplate void invokeBanBadWords(half*        logits,\n                                const int*   output_ids_buf,\n                                const int*   parent_ids_buf,\n                                int          batch_size,\n                                int          local_batch_size,\n                                int          beam_width,\n                                const int*   bad_words,\n                                bool         share_words,\n                                size_t       bad_words_len,\n                                int          id_offset,\n                                int          vocab_size_padded,\n                                size_t       step,\n                                hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBanBadWords(__hip_bfloat16* logits,\n                                const int*     output_ids_buf,\n                                const int*     parent_ids_buf,\n                                int            batch_size,\n                                int            local_batch_size,\n                                int            beam_width,\n                                const int*     bad_words,\n                                bool           share_words,\n                                size_t         bad_words_len,\n                                int            id_offset,\n                                int            vocab_size_padded,\n                                size_t         step,\n                                hipStream_t   stream);\n#endif\ntemplate void invokeBanBadWords(float*       logits,\n                                const int*   output_ids_buf,\n                                const int*   parent_ids_buf,\n                                int          batch_size,\n                                int          local_batch_size,\n                                int          beam_width,\n                                const int*   bad_words,\n                                bool         share_words,\n                                size_t       bad_words_len,\n                                int          id_offset,\n                                int          vocab_size_padded,\n                                size_t       step,\n                                hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/ban_bad_words.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reduce_kernel_utils.cuh\"\n#include \"transform_mask_kernels.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n/*******************  invokeTransformMask  ***********************/\n\n// transform mask [B, S, S](half) into [B, S2*S2/64, 64](half), S2 is the actual seqlen used in fmha row-major\n// in one MMA (16*16 elements calculated by a warp), each thread calculates 8 elements\n// the offsets of elements calculated by each thread are : for n, +0 +1 +8 +9; for m, +0 +8 (M_XMMAS*N_XMMAS times)\n// in transformed_mask, the masks of one warp are stored in 4 continuous rows ([4, 64]), with two elements of one thread\n// stored in 2 continuous halves. one cta calculates warps_m*warps_n mma == 16*warps_m*16*warps_n elements grid(B,\n// S2*S2/64) block(32)\n__global__ void transform_mask_kernel(half2*         tranformed_mask,\n                                      const half2*   mask,\n                                      const uint32_t warps_m,\n                                      const uint32_t warps_n,\n                                      const uint32_t B,\n                                      const uint32_t S,\n                                      const uint32_t S2)\n{\n    const int bi = blockIdx.x;\n    const int r  = blockIdx.y;\n\n    const int    N_per_XMMAS       = warps_n << 4;\n    const int    M_per_XMMAS       = warps_m << 4;\n    const int    N_XMMAS           = (S2 + N_per_XMMAS - 1) / (N_per_XMMAS);\n    const int    warps_in_XMMAS    = warps_m * warps_n;\n    const half2* mask_b            = mask + ((bi * S * S) >> 1);\n    half2*       tranformed_mask_b = tranformed_mask + (bi * gridDim.y << 5);  //((bi * gridDim.y << 6) >> 1);\n\n    half2 tmp = {half(-30000.0f), half(-30000.0f)};\n\n    int c               = threadIdx.x * 2;\n    int elt_offset      = c % 2;\n    int warp_id         = r / 4;\n    int elt_in_thread   = (r % 4) * 2 + elt_offset;\n    int noffset_in_warp = (((elt_in_thread & 3) >> 1) << 3) + (elt_in_thread & 1);\n    int moffset_in_warp = ((elt_in_thread >> 2) & 1) << 3;\n\n    int XMMAS_mi         = warp_id / (N_XMMAS * warps_in_XMMAS);\n    int XMMAS_ni         = warp_id % (N_XMMAS * warps_in_XMMAS) / warps_in_XMMAS;\n    int warp_id_in_XMMAS = warp_id - (XMMAS_mi * N_XMMAS + XMMAS_ni) * warps_in_XMMAS;\n    int warp_mi          = warp_id_in_XMMAS % warps_m;\n    int warp_ni          = warp_id_in_XMMAS / warps_m;\n    int noffset          = XMMAS_ni * N_per_XMMAS + (warp_ni << 4) + noffset_in_warp;\n    int moffset          = XMMAS_mi * M_per_XMMAS + (warp_mi << 4) + moffset_in_warp;\n\n    int mi = moffset + (c >> 3);\n    int ni = noffset + (((c >> 1) & 3) << 1);\n\n    if (mi < S && ni < S) {\n        tmp = __ldg(mask_b + ((mi * S + ni) >> 1));\n    }\n\n    tranformed_mask_b[(r << 5) + threadIdx.x] = tmp;\n}\n\n// transform mask [B, S, S](half) into [B, S2*S2/64, 64](half), S2 is the actual seqlen used in fmha row-major\n// in one MMA (16*16 elements calculated by a warp), each thread calculates 8 elements\n// the offsets of elements calculated by each thread are : for n, +0 +1 +8 +9; for m, +0 +8 (M_XMMAS*N_XMMAS times)\n// in transformed_mask, the masks of one warp are stored in 4 continuous rows ([4, 64]), with two elements of one thread\n// stored in 2 continuous halves. one cta calculates warps_m*warps_n mma == 16*warps_m*16*warps_n elements grid(B,\n// S2*S2/64) block(32)\n__global__ void transform_mask_kernel(half*          tranformed_mask,\n                                      const half*    mask,\n                                      const uint32_t warps_m,\n                                      const uint32_t warps_n,\n                                      const uint32_t B,\n                                      const uint32_t S,\n                                      const uint32_t S2)\n{\n    const int bi = blockIdx.x;\n    const int r  = blockIdx.y;\n\n    const int N_per_XMMAS       = warps_n << 4;\n    const int M_per_XMMAS       = warps_m << 4;\n    const int N_XMMAS           = (S2 + N_per_XMMAS - 1) / (N_per_XMMAS);\n    const int warps_in_XMMAS    = warps_m * warps_n;\n    half2*    tranformed_mask_b = (half2*)(tranformed_mask + (bi * gridDim.y << 6));\n\n    half2 tmp = {half(-30000.0f), half(-30000.0f)};\n\n    int c               = threadIdx.x * 2;\n    int elt_offset      = c % 2;\n    int warp_id         = r / 4;\n    int elt_in_thread   = (r % 4) * 2 + elt_offset;\n    int noffset_in_warp = (((elt_in_thread & 3) >> 1) << 3) + (elt_in_thread & 1);\n    int moffset_in_warp = ((elt_in_thread >> 2) & 1) << 3;\n\n    int XMMAS_mi         = warp_id / (N_XMMAS * warps_in_XMMAS);\n    int XMMAS_ni         = warp_id % (N_XMMAS * warps_in_XMMAS) / warps_in_XMMAS;\n    int warp_id_in_XMMAS = warp_id - (XMMAS_mi * N_XMMAS + XMMAS_ni) * warps_in_XMMAS;\n    int warp_mi          = warp_id_in_XMMAS % warps_m;\n    int warp_ni          = warp_id_in_XMMAS / warps_m;\n    int noffset          = XMMAS_ni * N_per_XMMAS + (warp_ni << 4) + noffset_in_warp;\n    int moffset          = XMMAS_mi * M_per_XMMAS + (warp_mi << 4) + moffset_in_warp;\n\n    int mi = moffset + (c >> 3);\n    int ni = noffset + (((c >> 1) & 3) << 1);\n\n    if (mi < S) {\n        mask += bi * S * S;\n        int idx = mi * S + ni;\n        if (ni < S) {\n            tmp.x = __ldg(mask + idx);\n        }\n        if (ni + 1 < S) {\n            tmp.y = __ldg(mask + idx + 1);\n        }\n    }\n\n    tranformed_mask_b[(r << 5) + threadIdx.x] = tmp;\n}\n\nvoid invokeTransformMask(\n    half* tranformed_mask, const half* mask, const uint32_t B, const uint32_t S, cudaStream_t stream)\n{\n    uint32_t S2;\n    uint32_t warps_m = 2, warps_n = 2;\n    if (S <= 64) {\n        S2 = 64;\n    }\n    else if (S <= 128) {\n        S2 = 128;\n    }\n    else if (S <= 256) {\n        S2      = 256;\n        warps_m = 1;\n        warps_n = 4;\n    }\n    else if (S <= 384) {\n        S2      = 384;\n        warps_m = 1;\n        warps_n = 8;\n    }\n    else {\n        printf(\"[ERROR][invokeTransformMask]unsupported seq_len %d\\n\", S);\n        exit(-1);\n    }\n    assert(S2 * S2 % 64 == 0);\n    dim3 grid(B, S2 * S2 / 64);\n    dim3 block(32);\n    if (S % 2 == 0) {\n        transform_mask_kernel<<<grid, block, 0, stream>>>(\n            (half2*)tranformed_mask, (const half2*)mask, warps_m, warps_n, B, S, S2);\n    }\n    else {\n        transform_mask_kernel<<<grid, block, 0, stream>>>(tranformed_mask, mask, warps_m, warps_n, B, S, S2);\n    }\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"transform_mask_kernels.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n/*******************  invokeTransformMask  ***********************/\n\n// transform mask [B, S, S](half) into [B, S2*S2/64, 64](half), S2 is the actual seqlen used in fmha row-major\n// in one MMA (16*16 elements calculated by a warp), each thread calculates 8 elements\n// the offsets of elements calculated by each thread are : for n, +0 +1 +8 +9; for m, +0 +8 (M_XMMAS*N_XMMAS times)\n// in transformed_mask, the masks of one warp are stored in 4 continuous rows ([4, 64]), with two elements of one thread\n// stored in 2 continuous halves. one cta calculates warps_m*warps_n mma == 16*warps_m*16*warps_n elements grid(B,\n// S2*S2/64) block(32)\n__global__ void transform_mask_kernel(half2*         tranformed_mask,\n                                      const half2*   mask,\n                                      const uint32_t warps_m,\n                                      const uint32_t warps_n,\n                                      const uint32_t B,\n                                      const uint32_t S,\n                                      const uint32_t S2)\n{\n    const int bi = blockIdx.x;\n    const int r  = blockIdx.y;\n\n    const int    N_per_XMMAS       = warps_n << 4;\n    const int    M_per_XMMAS       = warps_m << 4;\n    const int    N_XMMAS           = (S2 + N_per_XMMAS - 1) / (N_per_XMMAS);\n    const int    warps_in_XMMAS    = warps_m * warps_n;\n    const half2* mask_b            = mask + ((bi * S * S) >> 1);\n    half2*       tranformed_mask_b = tranformed_mask + (bi * gridDim.y << 5);  //((bi * gridDim.y << 6) >> 1);\n\n    half2 tmp = {half(-30000.0f), half(-30000.0f)};\n\n    int c               = threadIdx.x * 2;\n    int elt_offset      = c % 2;\n    int warp_id         = r / 4;\n    int elt_in_thread   = (r % 4) * 2 + elt_offset;\n    int noffset_in_warp = (((elt_in_thread & 3) >> 1) << 3) + (elt_in_thread & 1);\n    int moffset_in_warp = ((elt_in_thread >> 2) & 1) << 3;\n\n    int XMMAS_mi         = warp_id / (N_XMMAS * warps_in_XMMAS);\n    int XMMAS_ni         = warp_id % (N_XMMAS * warps_in_XMMAS) / warps_in_XMMAS;\n    int warp_id_in_XMMAS = warp_id - (XMMAS_mi * N_XMMAS + XMMAS_ni) * warps_in_XMMAS;\n    int warp_mi          = warp_id_in_XMMAS % warps_m;\n    int warp_ni          = warp_id_in_XMMAS / warps_m;\n    int noffset          = XMMAS_ni * N_per_XMMAS + (warp_ni << 4) + noffset_in_warp;\n    int moffset          = XMMAS_mi * M_per_XMMAS + (warp_mi << 4) + moffset_in_warp;\n\n    int mi = moffset + (c >> 3);\n    int ni = noffset + (((c >> 1) & 3) << 1);\n\n    if (mi < S && ni < S) {\n        tmp = __ldg(mask_b + ((mi * S + ni) >> 1));\n    }\n\n    tranformed_mask_b[(r << 5) + threadIdx.x] = tmp;\n}\n\n// transform mask [B, S, S](half) into [B, S2*S2/64, 64](half), S2 is the actual seqlen used in fmha row-major\n// in one MMA (16*16 elements calculated by a warp), each thread calculates 8 elements\n// the offsets of elements calculated by each thread are : for n, +0 +1 +8 +9; for m, +0 +8 (M_XMMAS*N_XMMAS times)\n// in transformed_mask, the masks of one warp are stored in 4 continuous rows ([4, 64]), with two elements of one thread\n// stored in 2 continuous halves. one cta calculates warps_m*warps_n mma == 16*warps_m*16*warps_n elements grid(B,\n// S2*S2/64) block(32)\n__global__ void transform_mask_kernel(half*          tranformed_mask,\n                                      const half*    mask,\n                                      const uint32_t warps_m,\n                                      const uint32_t warps_n,\n                                      const uint32_t B,\n                                      const uint32_t S,\n                                      const uint32_t S2)\n{\n    const int bi = blockIdx.x;\n    const int r  = blockIdx.y;\n\n    const int N_per_XMMAS       = warps_n << 4;\n    const int M_per_XMMAS       = warps_m << 4;\n    const int N_XMMAS           = (S2 + N_per_XMMAS - 1) / (N_per_XMMAS);\n    const int warps_in_XMMAS    = warps_m * warps_n;\n    half2*    tranformed_mask_b = (half2*)(tranformed_mask + (bi * gridDim.y << 6));\n\n    half2 tmp = {half(-30000.0f), half(-30000.0f)};\n\n    int c               = threadIdx.x * 2;\n    int elt_offset      = c % 2;\n    int warp_id         = r / 4;\n    int elt_in_thread   = (r % 4) * 2 + elt_offset;\n    int noffset_in_warp = (((elt_in_thread & 3) >> 1) << 3) + (elt_in_thread & 1);\n    int moffset_in_warp = ((elt_in_thread >> 2) & 1) << 3;\n\n    int XMMAS_mi         = warp_id / (N_XMMAS * warps_in_XMMAS);\n    int XMMAS_ni         = warp_id % (N_XMMAS * warps_in_XMMAS) / warps_in_XMMAS;\n    int warp_id_in_XMMAS = warp_id - (XMMAS_mi * N_XMMAS + XMMAS_ni) * warps_in_XMMAS;\n    int warp_mi          = warp_id_in_XMMAS % warps_m;\n    int warp_ni          = warp_id_in_XMMAS / warps_m;\n    int noffset          = XMMAS_ni * N_per_XMMAS + (warp_ni << 4) + noffset_in_warp;\n    int moffset          = XMMAS_mi * M_per_XMMAS + (warp_mi << 4) + moffset_in_warp;\n\n    int mi = moffset + (c >> 3);\n    int ni = noffset + (((c >> 1) & 3) << 1);\n\n    if (mi < S) {\n        mask += bi * S * S;\n        int idx = mi * S + ni;\n        if (ni < S) {\n            tmp.x = __ldg(mask + idx);\n        }\n        if (ni + 1 < S) {\n            tmp.y = __ldg(mask + idx + 1);\n        }\n    }\n\n    tranformed_mask_b[(r << 5) + threadIdx.x] = tmp;\n}\n\nvoid invokeTransformMask(\n    half* tranformed_mask, const half* mask, const uint32_t B, const uint32_t S, hipStream_t stream)\n{\n    uint32_t S2;\n    uint32_t warps_m = 2, warps_n = 2;\n    if (S <= 64) {\n        S2 = 64;\n    }\n    else if (S <= 128) {\n        S2 = 128;\n    }\n    else if (S <= 256) {\n        S2      = 256;\n        warps_m = 1;\n        warps_n = 4;\n    }\n    else if (S <= 384) {\n        S2      = 384;\n        warps_m = 1;\n        warps_n = 8;\n    }\n    else {\n        printf(\"[ERROR][invokeTransformMask]unsupported seq_len %d\\n\", S);\n        exit(-1);\n    }\n    assert(S2 * S2 % 64 == 0);\n    dim3 grid(B, S2 * S2 / 64);\n    dim3 block(32);\n    if (S % 2 == 0) {\n        transform_mask_kernel<<<grid, block, 0, stream>>>(\n            (half2*)tranformed_mask, (const half2*)mask, warps_m, warps_n, B, S, S2);\n    }\n    else {\n        transform_mask_kernel<<<grid, block, 0, stream>>>(tranformed_mask, mask, warps_m, warps_n, B, S, S2);\n    }\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/transform_mask_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/activation_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#endif\n\nnamespace fastertransformer {\n\n/* Gelu Activation */\n\n__forceinline__ __device__ float copysignf_pos(float a, float b)\n{\n    float r;\n    r = __int_as_float(__float_as_int(a) | (__float_as_int(b) & 0x80000000));\n    return r;\n}\n\n__inline__ __device__ float tanh_opt(float x)\n{\n#if (__CUDA_ARCH__ >= 750 && CUDART_VERSION >= 11000)\n    float r;\n    asm(\"tanh.approx.f32 %0,%1; \\n\\t\" : \"=f\"(r) : \"f\"(x));\n    return r;\n#else\n    const float exp_val = -1.f * fabs(2 * x);\n    return copysignf_pos((1.0f - __expf(exp_val)) / (__expf(exp_val) + 1.0f), x);\n#endif\n}\n\ntemplate<typename T>\nstruct GeluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        const float cdf = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (val + 0.044715f * val * val * val))));\n        return val * cdf;\n    }\n};\n\ntemplate<>\nstruct GeluActivation<half2> {\n    using return_type = half2;\n    static __device__ __forceinline__ half2 apply(const half2& val)\n    {\n        half2  val_pow3 = __hmul2(val, __hmul2(val, val));\n        float2 tmp_pow  = __half22float2(val_pow3);\n        float2 tmp      = __half22float2(val);\n\n        tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n        tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n        return __hmul2(val, __float22half2_rn(tmp));\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct GeluActivation<__nv_bfloat162> {\n    using return_type = __nv_bfloat162;\n    static __device__ __forceinline__ __nv_bfloat162 apply(const __nv_bfloat162& val)\n    {\n        __nv_bfloat162 val_pow3 = bf16hmul2(val, bf16hmul2(val, val));\n        float2         tmp_pow  = bf1622float2(val_pow3);\n        float2         tmp      = bf1622float2(val);\n\n        tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n        tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n        return bf16hmul2(val, __floats2bfloat162_rn(tmp.x, tmp.y));\n    }\n};\n#endif\n\n/* Relu Activation */\n\ntemplate<typename T>\nstruct ReluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return val > static_cast<T>(0.0f) ? val : static_cast<T>(0.0f);\n    }\n};\n\ntemplate<>\nstruct ReluActivation<half2> {\n    using return_type = half2;\n    static __device__ __forceinline__ half2 apply(const half2& val)\n    {\n        const half zero_half = static_cast<half>(0.0f);\n        return make_half2(val.x > zero_half ? val.x : zero_half, val.y > zero_half ? val.y : zero_half);\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct ReluActivation<__nv_bfloat162> {\n    using return_type = __nv_bfloat162;\n    static __device__ __forceinline__ __nv_bfloat162 apply(const __nv_bfloat162& val)\n    {\n        const __nv_bfloat16 zero_bf16 = static_cast<__nv_bfloat16>(0.0f);\n        return make_bfloat162(val.x > zero_bf16 ? val.x : zero_bf16, val.y > zero_bf16 ? val.y : zero_bf16);\n    }\n};\n#endif\n\n/* Silu Activation */\n\ntemplate<typename T>\nstruct SiluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return (T)((float)val / (1.0f + __expf((float)-val)));\n    }\n};\n\ntemplate<>\nstruct SiluActivation<half2> {\n    using return_type = float2;\n    static __device__ __forceinline__ float2 apply(const half2& val)\n    {\n        return make_float2(SiluActivation<float>::apply(val.x), SiluActivation<float>::apply(val.y));\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct SiluActivation<__nv_bfloat162> {\n    using return_type = float2;\n    static __device__ __forceinline__ float2 apply(const __nv_bfloat162& val)\n    {\n        return make_float2(SiluActivation<float>::apply(val.x), SiluActivation<float>::apply(val.y));\n    }\n};\n#endif  // ENABLE_BF16\n\n/* Identity Activation (= no activation) */\n\ntemplate<typename T>\nstruct IdentityActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return val;\n    }\n};\n\n// clang-format off\ntemplate<template<typename T> class Activation, typename T, typename BT>\n__global__ void generic_activation(T*                      out,\n                                   const BT*  __restrict   bias,\n                                   const T*   __restrict   gated_weights,\n                                   const BT*  __restrict   gated_bias,\n                                   const int* __restrict   ia3_tasks,\n                                   const T*   __restrict   ia3_weights,\n                                   const int               int8_mode,\n                                   const float* __restrict activation_in,\n                                   const float* __restrict activation_out,\n                                   const int* __restrict padding_offset,\n                                   const int seq_len,\n                                   int m,\n                                   int n)\n{\n    constexpr size_t packed_elems = num_elems<T>::value;\n\n    const bool with_bias = bias != nullptr;\n    const bool with_gate = gated_weights != nullptr;\n    const bool with_ia3  = ia3_tasks != nullptr;\n\n    using Act_T         = typename Activation<T>::return_type;\n    using Float_T       = typename packed_as<float, packed_elems>::type;\n    using Packed_Int8_t = typename packed_as<int8_t, packed_elems>::type;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        T val;\n        if (int8_mode == 2) {\n            val = cuda_cast<T>(cuda_cast<Float_T>(reinterpret_cast<Packed_Int8_t*>(out)[id]) * activation_in[0]);\n        }\n        else {\n            val = out[id];\n        }\n\n        T gated_val;\n        if (with_gate) {\n            gated_val = gated_weights[id];\n        }\n\n        if (with_bias) {\n            const T reg_bias = static_cast<T>(bias[id % n]);\n            val              = val + reg_bias;\n\n            if (with_gate) {\n                const T reg_gated_bias = static_cast<T>(gated_bias[id % n]);\n                gated_val              = gated_val + reg_gated_bias;\n            }\n        }\n\n        if (with_gate) {\n            val = cuda_cast<T>(Activation<T>::apply(val) * cuda_cast<Act_T>(gated_val));\n        }\n        else {\n            val = cuda_cast<T>(Activation<T>::apply(val));\n        }\n\n        if (with_ia3) {\n            const int word_id = id / n;\n            const int offset = padding_offset == nullptr ? 0 : padding_offset[word_id];\n            const int batch_id = (word_id + offset) / seq_len;\n            const int task = ia3_tasks[batch_id];\n            val            = val * ia3_weights[task * n + (id % n)];\n        }\n\n        if (int8_mode != 2) {\n            out[id] = val;\n        }\n        else {\n            reinterpret_cast<Packed_Int8_t*>(out)[id] =\n                cuda_cast<Packed_Int8_t>(cuda_cast<Float_T>(val) * activation_out[0]);\n        }\n    }\n}\n// clang-format on\n\ntemplate<template<typename T> class Activation, typename T, typename BT>\nvoid invokeGenericActivation(T*           out,\n                             const BT*    bias,\n                             const T*     gated_weights,\n                             const BT*    gated_bias,\n                             const int*   ia3_tasks,\n                             const T*     ia3_weights,\n                             const int    m,\n                             const int    n,\n                             const int    int8_mode,\n                             const float* activation_in,\n                             const float* activation_out,\n                             const int*   padding_offset,\n                             const int    seq_len,\n                             cudaStream_t stream)\n{\n    using PT                   = typename packed_type<T>::type;\n    constexpr int packed_elems = num_elems<PT>::value;\n    using PBT                  = typename packed_as<BT, packed_elems>::type;\n\n    dim3 block, grid;\n    if (n / 4 / packed_elems <= 1024) {\n        block.x = n / 4 / packed_elems;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    generic_activation<Activation><<<grid, block, 0, stream>>>(reinterpret_cast<PT*>(out),\n                                                               reinterpret_cast<const PBT*>(bias),\n                                                               reinterpret_cast<const PT*>(gated_weights),\n                                                               reinterpret_cast<const PBT*>(gated_bias),\n                                                               ia3_tasks,\n                                                               reinterpret_cast<const PT*>(ia3_weights),\n                                                               int8_mode,\n                                                               activation_in,\n                                                               activation_out,\n                                                               padding_offset,\n                                                               seq_len,\n                                                               m,\n                                                               n / packed_elems);\n}\n\n#define INSTANTIATE_GENERIC_ACTIVATION(Activation, T, BT)                                                              \\\n    template void invokeGenericActivation<Activation, T, BT>(T * out,                                                  \\\n                                                             const BT*    bias,                                        \\\n                                                             const T*     gated_weights,                               \\\n                                                             const BT*    gated_bias,                                  \\\n                                                             const int*   ia3_tasks,                                   \\\n                                                             const T*     ia3_weights,                                 \\\n                                                             const int    m,                                           \\\n                                                             const int    n,                                           \\\n                                                             const int    int8_mode,                                   \\\n                                                             const float* activation_in,                               \\\n                                                             const float* activation_out,                              \\\n                                                             const int*   padding_offset,                              \\\n                                                             const int    seq_len,                                     \\\n                                                             cudaStream_t stream);\n\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, __nv_bfloat16, __nv_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, __nv_bfloat16, __nv_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, __nv_bfloat16, __nv_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, half, half);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, __nv_bfloat16, __nv_bfloat16);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, __nv_bfloat16);\n#endif\n#undef INSTANCIATE_GENERIC_ACTIVATION\n\ntemplate<typename T>\n__global__ void add_bias_tanh(T* out, const T* __restrict bias, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        T val = out[id];\n        if (bias != nullptr) {\n            val = val + ldg(&bias[id % n]);\n        }\n        out[id] = tanhf(val);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_tanh(half* out, const half* __restrict bias, int m, int n)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        half2 val = out_ptr[id];\n        if (bias != nullptr) {\n            val = val + __ldg(&bias_ptr[id % n]);\n        }\n        val.x       = tanhf(val.x);\n        val.y       = tanhf(val.y);\n        out_ptr[id] = val;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void add_bias_tanh(__nv_bfloat16* out, const __nv_bfloat16* __restrict bias, int m, int n)\n{\n    __nv_bfloat162*       out_ptr  = (__nv_bfloat162*)out;\n    const __nv_bfloat162* bias_ptr = (__nv_bfloat162*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        __nv_bfloat162 val = out_ptr[id];\n        if (bias != nullptr) {\n            val = bf16hadd2(val, ldg(&bias_ptr[id % n]));\n        }\n        val.x       = tanhf(val.x);\n        val.y       = tanhf(val.y);\n        out_ptr[id] = val;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddBiasTanh(T* out, const T* bias, const int m, const int n, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    add_bias_tanh<T><<<grid, block, 0, stream>>>(out, bias, m, n / data_type_factor);\n}\n\ntemplate void invokeAddBiasTanh(float* out, const float* bias, const int m, const int n, cudaStream_t stream);\ntemplate void invokeAddBiasTanh(half* out, const half* bias, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void\ninvokeAddBiasTanh(__nv_bfloat16* out, const __nv_bfloat16* bias, const int m, const int n, cudaStream_t stream);\n#endif\n\ntemplate<typename T2, int N>\n__global__ void addBiasGeluV2(T2* out,\n                              const T2* __restrict bias,\n                              const int* ia3_tasks,\n                              const T2*  ia3_weights,\n                              const int  size,\n                              const int* padding_offset,\n                              const int  seq_len)\n{\n    const bool with_ia3 = ia3_tasks != nullptr;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < size; id += blockDim.x * gridDim.x) {\n        T2 val = out[id];\n        if (bias != nullptr) {\n            T2 reg_bias = ldg(&bias[id % N]);\n            val         = hadd2(val, reg_bias);\n        }\n        val = GeluActivation<T2>::apply(val);\n        if (with_ia3) {\n            const int word_id  = id / N;\n            const int offset   = padding_offset == nullptr ? 0 : padding_offset[word_id];\n            const int batch_id = (word_id + offset) / seq_len;\n            const int task     = ia3_tasks[batch_id];\n            val                = val * ia3_weights[task * N + (id % N)];\n        }\n        out[id] = val;\n    }\n}\n\ntemplate<typename T2, int N, int ELEMENT_PER_ROUND>\n__global__ void addBiasGeluV3(T2* out,\n                              const T2* __restrict bias,\n                              const int* ia3_tasks,\n                              const T2*  ia3_weights,\n                              const int  size,\n                              const int* padding_offset,\n                              const int  seq_len)\n{\n    const bool with_ia3 = ia3_tasks != nullptr;\n    T2         buffer[ELEMENT_PER_ROUND];\n    T2         tmp_bias[ELEMENT_PER_ROUND];\n    for (int id = blockIdx.x * blockDim.x * ELEMENT_PER_ROUND + threadIdx.x * ELEMENT_PER_ROUND; id < size;\n         id += blockDim.x * gridDim.x * ELEMENT_PER_ROUND) {\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_ROUND; i++) {\n            buffer[i] = out[id + i];\n            if (bias != nullptr) {\n                tmp_bias[i] = ldg(&bias[(id + i) % N]);\n            }\n        }\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_ROUND; i++) {\n            if (bias != nullptr) {\n                buffer[i] = hadd2(buffer[i], tmp_bias[i]);\n            }\n            buffer[i] = GeluActivation<T2>::apply(buffer[i]);\n            if (with_ia3) {\n                const int word_id  = (id + i) / N;\n                const int offset   = padding_offset == nullptr ? 0 : padding_offset[word_id];\n                const int batch_id = (word_id + offset) / seq_len;\n                const int task     = ia3_tasks[batch_id];\n                buffer[i]          = buffer[i] * ia3_weights[task * N + ((id + i) % N)];\n            }\n            out[id + i] = buffer[i];\n        }\n    }\n}\n\n#define ADD_BIAS_GELU(HALF_N, ELEMENT_PER_ROUND)                                                                       \\\n    case HALF_N:                                                                                                       \\\n        if (ELEMENT_PER_ROUND > 1) {                                                                                   \\\n            grid.x = grid.x / ELEMENT_PER_ROUND;                                                                       \\\n            addBiasGeluV3<T2, HALF_N, ELEMENT_PER_ROUND><<<grid, block, 0, stream>>>(                                  \\\n                (T2*)out, (const T2*)bias, ia3_tasks, (T2*)ia3_weights, m * half_n, padding_offset, seq_len);          \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            addBiasGeluV2<T2, HALF_N><<<grid, block, 0, stream>>>(                                                     \\\n                (T2*)out, (const T2*)bias, ia3_tasks, (T2*)ia3_weights, m * half_n, padding_offset, seq_len);          \\\n        }                                                                                                              \\\n        break;\n\ntemplate<typename T>\nvoid invokeAddBiasGeluV2(T*           out,\n                         const T*     bias,\n                         const int*   ia3_tasks,\n                         const T*     ia3_weights,\n                         const int*   padding_offset,\n                         const int    seq_len,\n                         const int    m,\n                         const int    n,\n                         cudaStream_t stream)\n{\n    if (n % 2 == 0 && sizeof(T) == 2) {\n        const int half_n = n / 2;\n        dim3      block, grid;\n        block.x  = std::min(half_n, 512);\n        grid.x   = (m * half_n + (block.x - 1)) / block.x;\n        using T2 = typename TypeConverter<T>::Type;\n\n        if (grid.x >= 512) {\n            switch (half_n) {\n                ADD_BIAS_GELU(256, 1)\n                ADD_BIAS_GELU(512, 1)\n                ADD_BIAS_GELU(1024, 1)\n                ADD_BIAS_GELU(1536, 1)\n                ADD_BIAS_GELU(2048, 1)\n                ADD_BIAS_GELU(4096, 2)\n                ADD_BIAS_GELU(8192, 2)\n                ADD_BIAS_GELU(16384, 2)\n                ADD_BIAS_GELU(24576, 2)\n                ADD_BIAS_GELU(40960, 4)\n                default:\n                    invokeGenericActivation<GeluActivation>(out,\n                                                            bias,\n                                                            (T*)nullptr,\n                                                            (T*)nullptr,\n                                                            ia3_tasks,\n                                                            ia3_weights,\n                                                            m,\n                                                            n,\n                                                            0,\n                                                            (float*)nullptr,\n                                                            (float*)nullptr,\n                                                            padding_offset,\n                                                            seq_len,\n                                                            stream);\n                    break;\n            }\n        }\n        else {\n            switch (half_n) {\n                ADD_BIAS_GELU(256, 1)\n                ADD_BIAS_GELU(512, 1)\n                ADD_BIAS_GELU(1024, 1)\n                ADD_BIAS_GELU(1536, 1)\n                ADD_BIAS_GELU(2048, 1)\n                ADD_BIAS_GELU(4096, 1)\n                ADD_BIAS_GELU(8192, 2)\n                ADD_BIAS_GELU(16384, 2)\n                ADD_BIAS_GELU(24576, 2)\n                ADD_BIAS_GELU(40960, 2)\n                default:\n                    invokeGenericActivation<GeluActivation>(out,\n                                                            bias,\n                                                            (T*)nullptr,\n                                                            (T*)nullptr,\n                                                            ia3_tasks,\n                                                            ia3_weights,\n                                                            m,\n                                                            n,\n                                                            0,\n                                                            (float*)nullptr,\n                                                            (float*)nullptr,\n                                                            padding_offset,\n                                                            seq_len,\n                                                            stream);\n                    break;\n            }\n        }\n    }\n    else {\n        invokeGenericActivation<GeluActivation>(out,\n                                                bias,\n                                                (T*)nullptr,\n                                                (T*)nullptr,\n                                                ia3_tasks,\n                                                ia3_weights,\n                                                m,\n                                                n,\n                                                0,\n                                                (float*)nullptr,\n                                                (float*)nullptr,\n                                                padding_offset,\n                                                seq_len,\n                                                stream);\n    }\n}\n\n#undef ADD_BIAS_GELU\n\ntemplate void invokeAddBiasGeluV2(float*       out,\n                                  const float* bias,\n                                  const int*   ia3_tasks,\n                                  const float* ia3_weights,\n                                  const int*   padding_offset,\n                                  const int    seq_len,\n                                  const int    m,\n                                  const int    n,\n                                  cudaStream_t stream);\ntemplate void invokeAddBiasGeluV2(half*        out,\n                                  const half*  bias,\n                                  const int*   ia3_tasks,\n                                  const half*  ia3_weights,\n                                  const int*   padding_offset,\n                                  const int    seq_len,\n                                  const int    m,\n                                  const int    n,\n                                  cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasGeluV2(__nv_bfloat16*       out,\n                                  const __nv_bfloat16* bias,\n                                  const int*           ia3_tasks,\n                                  const __nv_bfloat16* ia3_weights,\n                                  const int*           padding_offset,\n                                  const int            seq_len,\n                                  const int            m,\n                                  const int            n,\n                                  cudaStream_t         stream);\n#endif  // ENABLE_BF16\n\ntemplate<typename T>\n__global__ void sigmoid_kernel(T* data, const int size, const float scale)\n{\n    const int index = (blockIdx.y * gridDim.x + blockIdx.x) * blockDim.x + threadIdx.x;\n    if (index < size) {\n        float val   = cuda_cast<float>(data[index]);\n        val         = 1.0f / (1.0f + exp(-val)) * scale;\n        data[index] = T(val);\n    }\n}\n\ntemplate<>\n__global__ void sigmoid_kernel(half2* data, const int size, const float scale)\n{\n    const int index = (blockIdx.y * gridDim.x + blockIdx.x) * blockDim.x + threadIdx.x;\n    if (index < size / 2) {\n        half2  val        = data[index];\n        float2 val_float2 = cuda_cast<float2>(val);\n        val_float2.x      = 1.0f / (1.0f + exp(-val_float2.x)) * scale;\n        val_float2.y      = 1.0f / (1.0f + exp(-val_float2.y)) * scale;\n        data[index]       = cuda_cast<half2>(val_float2);\n    }\n}\n\ntemplate<typename T>\nvoid invokeSigmoid(T* data, const int size, const float scale, cudaStream_t stream)\n{\n    if (std::is_same<T, float>::value || (size % 2 != 0)) {\n        dim3 block(128);\n        dim3 grid((size + 127) / 128);\n        sigmoid_kernel<<<grid, block, 0, stream>>>(data, size, scale);\n    }\n    else {\n        dim3 block(128);\n        dim3 grid((size + 255) / 256);\n        sigmoid_kernel<<<grid, block, 0, stream>>>((half2*)data, size, scale);\n    }\n}\n\ntemplate void invokeSigmoid(float* data, const int size, const float scale, cudaStream_t stream);\ntemplate void invokeSigmoid(half* data, const int size, const float scale, cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/activation_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#endif\n\nnamespace fastertransformer {\n\n/* Gelu Activation */\n\n__forceinline__ __device__ float copysignf_pos(float a, float b)\n{\n    float r;\n    r = __int_as_float(__float_as_int(a) | (__float_as_int(b) & 0x80000000));\n    return r;\n}\n\n__inline__ __device__ float tanh_opt(float x)\n{\n#if (__CUDA_ARCH__ >= 750 && CUDART_VERSION >= 11000)\n    float r;\n    asm(\"tanh.approx.f32 %0,%1; \\n\\t\" : \"=f\"(r) : \"f\"(x));\n    return r;\n#else\n    const float exp_val = -1.f * fabs(2 * x);\n    return copysignf_pos((1.0f - __expf(exp_val)) / (__expf(exp_val) + 1.0f), x);\n#endif\n}\n\ntemplate<typename T>\nstruct GeluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        const float cdf = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (val + 0.044715f * val * val * val))));\n        return val * cdf;\n    }\n};\n\ntemplate<>\nstruct GeluActivation<half2> {\n    using return_type = half2;\n    static __device__ __forceinline__ half2 apply(const half2& val)\n    {\n        half2  val_pow3 = __hmul2(val, __hmul2(val, val));\n        float2 tmp_pow  = __half22float2(val_pow3);\n        float2 tmp      = __half22float2(val);\n\n        tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n        tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n        return __hmul2(val, __float22half2_rn(tmp));\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct GeluActivation<__hip_bfloat162> {\n    using return_type = __hip_bfloat162;\n    static __device__ __forceinline__ __hip_bfloat162 apply(const __hip_bfloat162& val)\n    {\n        __hip_bfloat162 val_pow3 = bf16hmul2(val, bf16hmul2(val, val));\n        float2         tmp_pow  = bf1622float2(val_pow3);\n        float2         tmp      = bf1622float2(val);\n\n        tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n        tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n        return bf16hmul2(val, __floats2bfloat162_rn(tmp.x, tmp.y));\n    }\n};\n#endif\n\n/* Relu Activation */\n\ntemplate<typename T>\nstruct ReluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return val > static_cast<T>(0.0f) ? val : static_cast<T>(0.0f);\n    }\n};\n\ntemplate<>\nstruct ReluActivation<half2> {\n    using return_type = half2;\n    static __device__ __forceinline__ half2 apply(const half2& val)\n    {\n        const half zero_half = static_cast<half>(0.0f);\n        return make_half2(val.x > zero_half ? val.x : zero_half, val.y > zero_half ? val.y : zero_half);\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct ReluActivation<__hip_bfloat162> {\n    using return_type = __hip_bfloat162;\n    static __device__ __forceinline__ __hip_bfloat162 apply(const __hip_bfloat162& val)\n    {\n        const __hip_bfloat16 zero_bf16 = static_cast<__hip_bfloat16>(0.0f);\n        return make_bfloat162(val.x > zero_bf16 ? val.x : zero_bf16, val.y > zero_bf16 ? val.y : zero_bf16);\n    }\n};\n#endif\n\n/* Silu Activation */\n\ntemplate<typename T>\nstruct SiluActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return (T)((float)val / (1.0f + __expf((float)-val)));\n    }\n};\n\ntemplate<>\nstruct SiluActivation<half2> {\n    using return_type = float2;\n    static __device__ __forceinline__ float2 apply(const half2& val)\n    {\n        return make_float2(SiluActivation<float>::apply(val.x), SiluActivation<float>::apply(val.y));\n    }\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct SiluActivation<__hip_bfloat162> {\n    using return_type = float2;\n    static __device__ __forceinline__ float2 apply(const __hip_bfloat162& val)\n    {\n        return make_float2(SiluActivation<float>::apply(val.x), SiluActivation<float>::apply(val.y));\n    }\n};\n#endif  // ENABLE_BF16\n\n/* Identity Activation (= no activation) */\n\ntemplate<typename T>\nstruct IdentityActivation {\n    using return_type = T;\n    static __device__ __forceinline__ T apply(const T& val)\n    {\n        return val;\n    }\n};\n\n// clang-format off\ntemplate<template<typename T> class Activation, typename T, typename BT>\n__global__ void generic_activation(T*                      out,\n                                   const BT*  __restrict   bias,\n                                   const T*   __restrict   gated_weights,\n                                   const BT*  __restrict   gated_bias,\n                                   const int* __restrict   ia3_tasks,\n                                   const T*   __restrict   ia3_weights,\n                                   const int               int8_mode,\n                                   const float* __restrict activation_in,\n                                   const float* __restrict activation_out,\n                                   const int* __restrict padding_offset,\n                                   const int seq_len,\n                                   int m,\n                                   int n)\n{\n    constexpr size_t packed_elems = num_elems<T>::value;\n\n    const bool with_bias = bias != nullptr;\n    const bool with_gate = gated_weights != nullptr;\n    const bool with_ia3  = ia3_tasks != nullptr;\n\n    using Act_T         = typename Activation<T>::return_type;\n    using Float_T       = typename packed_as<float, packed_elems>::type;\n    using Packed_Int8_t = typename packed_as<int8_t, packed_elems>::type;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        T val;\n        if (int8_mode == 2) {\n            val = cuda_cast<T>(cuda_cast<Float_T>(reinterpret_cast<Packed_Int8_t*>(out)[id]) * activation_in[0]);\n        }\n        else {\n            val = out[id];\n        }\n\n        T gated_val;\n        if (with_gate) {\n            gated_val = gated_weights[id];\n        }\n\n        if (with_bias) {\n            const T reg_bias = static_cast<T>(bias[id % n]);\n            val              = val + reg_bias;\n\n            if (with_gate) {\n                const T reg_gated_bias = static_cast<T>(gated_bias[id % n]);\n                gated_val              = gated_val + reg_gated_bias;\n            }\n        }\n\n        if (with_gate) {\n            val = cuda_cast<T>(Activation<T>::apply(val) * cuda_cast<Act_T>(gated_val));\n        }\n        else {\n            val = cuda_cast<T>(Activation<T>::apply(val));\n        }\n\n        if (with_ia3) {\n            const int word_id = id / n;\n            const int offset = padding_offset == nullptr ? 0 : padding_offset[word_id];\n            const int batch_id = (word_id + offset) / seq_len;\n            const int task = ia3_tasks[batch_id];\n            val            = val * ia3_weights[task * n + (id % n)];\n        }\n\n        if (int8_mode != 2) {\n            out[id] = val;\n        }\n        else {\n            reinterpret_cast<Packed_Int8_t*>(out)[id] =\n                cuda_cast<Packed_Int8_t>(cuda_cast<Float_T>(val) * activation_out[0]);\n        }\n    }\n}\n// clang-format on\n\ntemplate<template<typename T> class Activation, typename T, typename BT>\nvoid invokeGenericActivation(T*           out,\n                             const BT*    bias,\n                             const T*     gated_weights,\n                             const BT*    gated_bias,\n                             const int*   ia3_tasks,\n                             const T*     ia3_weights,\n                             const int    m,\n                             const int    n,\n                             const int    int8_mode,\n                             const float* activation_in,\n                             const float* activation_out,\n                             const int*   padding_offset,\n                             const int    seq_len,\n                             hipStream_t stream)\n{\n    using PT                   = typename packed_type<T>::type;\n    constexpr int packed_elems = num_elems<PT>::value;\n    using PBT                  = typename packed_as<BT, packed_elems>::type;\n\n    dim3 block, grid;\n    if (n / 4 / packed_elems <= 1024) {\n        block.x = n / 4 / packed_elems;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    generic_activation<Activation><<<grid, block, 0, stream>>>(reinterpret_cast<PT*>(out),\n                                                               reinterpret_cast<const PBT*>(bias),\n                                                               reinterpret_cast<const PT*>(gated_weights),\n                                                               reinterpret_cast<const PBT*>(gated_bias),\n                                                               ia3_tasks,\n                                                               reinterpret_cast<const PT*>(ia3_weights),\n                                                               int8_mode,\n                                                               activation_in,\n                                                               activation_out,\n                                                               padding_offset,\n                                                               seq_len,\n                                                               m,\n                                                               n / packed_elems);\n}\n\n#define INSTANTIATE_GENERIC_ACTIVATION(Activation, T, BT)                                                              \\\n    template void invokeGenericActivation<Activation, T, BT>(T * out,                                                  \\\n                                                             const BT*    bias,                                        \\\n                                                             const T*     gated_weights,                               \\\n                                                             const BT*    gated_bias,                                  \\\n                                                             const int*   ia3_tasks,                                   \\\n                                                             const T*     ia3_weights,                                 \\\n                                                             const int    m,                                           \\\n                                                             const int    n,                                           \\\n                                                             const int    int8_mode,                                   \\\n                                                             const float* activation_in,                               \\\n                                                             const float* activation_out,                              \\\n                                                             const int*   padding_offset,                              \\\n                                                             const int    seq_len,                                     \\\n                                                             hipStream_t stream);\n\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(GeluActivation, __hip_bfloat16, __hip_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(ReluActivation, __hip_bfloat16, __hip_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, half, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(SiluActivation, __hip_bfloat16, __hip_bfloat16);\n#endif\n\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, float);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, half, half);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, half);\n#ifdef ENABLE_BF16\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, __hip_bfloat16, __hip_bfloat16);\nINSTANTIATE_GENERIC_ACTIVATION(IdentityActivation, float, __hip_bfloat16);\n#endif\n#undef INSTANCIATE_GENERIC_ACTIVATION\n\ntemplate<typename T>\n__global__ void add_bias_tanh(T* out, const T* __restrict bias, int m, int n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        T val = out[id];\n        if (bias != nullptr) {\n            val = val + ldg(&bias[id % n]);\n        }\n        out[id] = tanhf(val);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_tanh(half* out, const half* __restrict bias, int m, int n)\n{\n    half2*       out_ptr  = (half2*)out;\n    const half2* bias_ptr = (half2*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        half2 val = out_ptr[id];\n        if (bias != nullptr) {\n            val = val + __ldg(&bias_ptr[id % n]);\n        }\n        val.x       = tanhf(val.x);\n        val.y       = tanhf(val.y);\n        out_ptr[id] = val;\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void add_bias_tanh(__hip_bfloat16* out, const __hip_bfloat16* __restrict bias, int m, int n)\n{\n    __hip_bfloat162*       out_ptr  = (__hip_bfloat162*)out;\n    const __hip_bfloat162* bias_ptr = (__hip_bfloat162*)bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        __hip_bfloat162 val = out_ptr[id];\n        if (bias != nullptr) {\n            val = bf16hadd2(val, ldg(&bias_ptr[id % n]));\n        }\n        val.x       = tanhf(val.x);\n        val.y       = tanhf(val.y);\n        out_ptr[id] = val;\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddBiasTanh(T* out, const T* bias, const int m, const int n, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16 and bf16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(m * n / 1024.);\n    }\n    add_bias_tanh<T><<<grid, block, 0, stream>>>(out, bias, m, n / data_type_factor);\n}\n\ntemplate void invokeAddBiasTanh(float* out, const float* bias, const int m, const int n, hipStream_t stream);\ntemplate void invokeAddBiasTanh(half* out, const half* bias, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void\ninvokeAddBiasTanh(__hip_bfloat16* out, const __hip_bfloat16* bias, const int m, const int n, hipStream_t stream);\n#endif\n\ntemplate<typename T2, int N>\n__global__ void addBiasGeluV2(T2* out,\n                              const T2* __restrict bias,\n                              const int* ia3_tasks,\n                              const T2*  ia3_weights,\n                              const int  size,\n                              const int* padding_offset,\n                              const int  seq_len)\n{\n    const bool with_ia3 = ia3_tasks != nullptr;\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < size; id += blockDim.x * gridDim.x) {\n        T2 val = out[id];\n        if (bias != nullptr) {\n            T2 reg_bias = ldg(&bias[id % N]);\n            val         = hadd2(val, reg_bias);\n        }\n        val = GeluActivation<T2>::apply(val);\n        if (with_ia3) {\n            const int word_id  = id / N;\n            const int offset   = padding_offset == nullptr ? 0 : padding_offset[word_id];\n            const int batch_id = (word_id + offset) / seq_len;\n            const int task     = ia3_tasks[batch_id];\n            val                = val * ia3_weights[task * N + (id % N)];\n        }\n        out[id] = val;\n    }\n}\n\ntemplate<typename T2, int N, int ELEMENT_PER_ROUND>\n__global__ void addBiasGeluV3(T2* out,\n                              const T2* __restrict bias,\n                              const int* ia3_tasks,\n                              const T2*  ia3_weights,\n                              const int  size,\n                              const int* padding_offset,\n                              const int  seq_len)\n{\n    const bool with_ia3 = ia3_tasks != nullptr;\n    T2         buffer[ELEMENT_PER_ROUND];\n    T2         tmp_bias[ELEMENT_PER_ROUND];\n    for (int id = blockIdx.x * blockDim.x * ELEMENT_PER_ROUND + threadIdx.x * ELEMENT_PER_ROUND; id < size;\n         id += blockDim.x * gridDim.x * ELEMENT_PER_ROUND) {\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_ROUND; i++) {\n            buffer[i] = out[id + i];\n            if (bias != nullptr) {\n                tmp_bias[i] = ldg(&bias[(id + i) % N]);\n            }\n        }\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_ROUND; i++) {\n            if (bias != nullptr) {\n                buffer[i] = hadd2(buffer[i], tmp_bias[i]);\n            }\n            buffer[i] = GeluActivation<T2>::apply(buffer[i]);\n            if (with_ia3) {\n                const int word_id  = (id + i) / N;\n                const int offset   = padding_offset == nullptr ? 0 : padding_offset[word_id];\n                const int batch_id = (word_id + offset) / seq_len;\n                const int task     = ia3_tasks[batch_id];\n                buffer[i]          = buffer[i] * ia3_weights[task * N + ((id + i) % N)];\n            }\n            out[id + i] = buffer[i];\n        }\n    }\n}\n\n#define ADD_BIAS_GELU(HALF_N, ELEMENT_PER_ROUND)                                                                       \\\n    case HALF_N:                                                                                                       \\\n        if (ELEMENT_PER_ROUND > 1) {                                                                                   \\\n            grid.x = grid.x / ELEMENT_PER_ROUND;                                                                       \\\n            addBiasGeluV3<T2, HALF_N, ELEMENT_PER_ROUND><<<grid, block, 0, stream>>>(                                  \\\n                (T2*)out, (const T2*)bias, ia3_tasks, (T2*)ia3_weights, m * half_n, padding_offset, seq_len);          \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            addBiasGeluV2<T2, HALF_N><<<grid, block, 0, stream>>>(                                                     \\\n                (T2*)out, (const T2*)bias, ia3_tasks, (T2*)ia3_weights, m * half_n, padding_offset, seq_len);          \\\n        }                                                                                                              \\\n        break;\n\ntemplate<typename T>\nvoid invokeAddBiasGeluV2(T*           out,\n                         const T*     bias,\n                         const int*   ia3_tasks,\n                         const T*     ia3_weights,\n                         const int*   padding_offset,\n                         const int    seq_len,\n                         const int    m,\n                         const int    n,\n                         hipStream_t stream)\n{\n    if (n % 2 == 0 && sizeof(T) == 2) {\n        const int half_n = n / 2;\n        dim3      block, grid;\n        block.x  = std::min(half_n, 512);\n        grid.x   = (m * half_n + (block.x - 1)) / block.x;\n        using T2 = typename TypeConverter<T>::Type;\n\n        if (grid.x >= 512) {\n            switch (half_n) {\n                ADD_BIAS_GELU(256, 1)\n                ADD_BIAS_GELU(512, 1)\n                ADD_BIAS_GELU(1024, 1)\n                ADD_BIAS_GELU(1536, 1)\n                ADD_BIAS_GELU(2048, 1)\n                ADD_BIAS_GELU(4096, 2)\n                ADD_BIAS_GELU(8192, 2)\n                ADD_BIAS_GELU(16384, 2)\n                ADD_BIAS_GELU(24576, 2)\n                ADD_BIAS_GELU(40960, 4)\n                default:\n                    invokeGenericActivation<GeluActivation>(out,\n                                                            bias,\n                                                            (T*)nullptr,\n                                                            (T*)nullptr,\n                                                            ia3_tasks,\n                                                            ia3_weights,\n                                                            m,\n                                                            n,\n                                                            0,\n                                                            (float*)nullptr,\n                                                            (float*)nullptr,\n                                                            padding_offset,\n                                                            seq_len,\n                                                            stream);\n                    break;\n            }\n        }\n        else {\n            switch (half_n) {\n                ADD_BIAS_GELU(256, 1)\n                ADD_BIAS_GELU(512, 1)\n                ADD_BIAS_GELU(1024, 1)\n                ADD_BIAS_GELU(1536, 1)\n                ADD_BIAS_GELU(2048, 1)\n                ADD_BIAS_GELU(4096, 1)\n                ADD_BIAS_GELU(8192, 2)\n                ADD_BIAS_GELU(16384, 2)\n                ADD_BIAS_GELU(24576, 2)\n                ADD_BIAS_GELU(40960, 2)\n                default:\n                    invokeGenericActivation<GeluActivation>(out,\n                                                            bias,\n                                                            (T*)nullptr,\n                                                            (T*)nullptr,\n                                                            ia3_tasks,\n                                                            ia3_weights,\n                                                            m,\n                                                            n,\n                                                            0,\n                                                            (float*)nullptr,\n                                                            (float*)nullptr,\n                                                            padding_offset,\n                                                            seq_len,\n                                                            stream);\n                    break;\n            }\n        }\n    }\n    else {\n        invokeGenericActivation<GeluActivation>(out,\n                                                bias,\n                                                (T*)nullptr,\n                                                (T*)nullptr,\n                                                ia3_tasks,\n                                                ia3_weights,\n                                                m,\n                                                n,\n                                                0,\n                                                (float*)nullptr,\n                                                (float*)nullptr,\n                                                padding_offset,\n                                                seq_len,\n                                                stream);\n    }\n}\n\n#undef ADD_BIAS_GELU\n\ntemplate void invokeAddBiasGeluV2(float*       out,\n                                  const float* bias,\n                                  const int*   ia3_tasks,\n                                  const float* ia3_weights,\n                                  const int*   padding_offset,\n                                  const int    seq_len,\n                                  const int    m,\n                                  const int    n,\n                                  hipStream_t stream);\ntemplate void invokeAddBiasGeluV2(half*        out,\n                                  const half*  bias,\n                                  const int*   ia3_tasks,\n                                  const half*  ia3_weights,\n                                  const int*   padding_offset,\n                                  const int    seq_len,\n                                  const int    m,\n                                  const int    n,\n                                  hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasGeluV2(__hip_bfloat16*       out,\n                                  const __hip_bfloat16* bias,\n                                  const int*           ia3_tasks,\n                                  const __hip_bfloat16* ia3_weights,\n                                  const int*           padding_offset,\n                                  const int            seq_len,\n                                  const int            m,\n                                  const int            n,\n                                  hipStream_t         stream);\n#endif  // ENABLE_BF16\n\ntemplate<typename T>\n__global__ void sigmoid_kernel(T* data, const int size, const float scale)\n{\n    const int index = (blockIdx.y * gridDim.x + blockIdx.x) * blockDim.x + threadIdx.x;\n    if (index < size) {\n        float val   = cuda_cast<float>(data[index]);\n        val         = 1.0f / (1.0f + exp(-val)) * scale;\n        data[index] = T(val);\n    }\n}\n\ntemplate<>\n__global__ void sigmoid_kernel(half2* data, const int size, const float scale)\n{\n    const int index = (blockIdx.y * gridDim.x + blockIdx.x) * blockDim.x + threadIdx.x;\n    if (index < size / 2) {\n        half2  val        = data[index];\n        float2 val_float2 = cuda_cast<float2>(val);\n        val_float2.x      = 1.0f / (1.0f + exp(-val_float2.x)) * scale;\n        val_float2.y      = 1.0f / (1.0f + exp(-val_float2.y)) * scale;\n        data[index]       = cuda_cast<half2>(val_float2);\n    }\n}\n\ntemplate<typename T>\nvoid invokeSigmoid(T* data, const int size, const float scale, hipStream_t stream)\n{\n    if (std::is_same<T, float>::value || (size % 2 != 0)) {\n        dim3 block(128);\n        dim3 grid((size + 127) / 128);\n        sigmoid_kernel<<<grid, block, 0, stream>>>(data, size, scale);\n    }\n    else {\n        dim3 block(128);\n        dim3 grid((size + 255) / 256);\n        sigmoid_kernel<<<grid, block, 0, stream>>>((half2*)data, size, scale);\n    }\n}\n\ntemplate void invokeSigmoid(float* data, const int size, const float scale, hipStream_t stream);\ntemplate void invokeSigmoid(half* data, const int size, const float scale, hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/activation_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/image_shift_partition_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeShiftPartition  ***********************/\n\n// applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void\nshift_partition(T2* out_ptr, const T2* input_ptr, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    int       tid                = threadIdx.x;\n\n    int id = bid * n + tid;\n    if (tid < n) {\n        out_ptr[output_bid * n + tid] = input_ptr[id];\n    }\n}\n\n// applied to float\ntemplate<>\n__global__ void\nshift_partition<float>(float* out, const float* input, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    if (tid < n) {\n        out[output_bid * n + tid] = (float)(__ldg(input + bid * n + tid));\n    }\n}\n\n// Applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void shift_partition_v2(\n    T2* out_ptr, const T2* __restrict input_ptr, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    using T1                     = typename TypeConverter<T2>::Type;  // half2 to half, bfloat162 to bfloat\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out_ptr[output_offset + col_id] = ldg(input_ptr + offset + col_id);\n        }\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_v2<float>(\n    float* out, const float* __restrict input, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[output_offset + col_id] = (float)(__ldg(input + offset + col_id));\n        }\n    }\n}\n\n// Applied to half or Bfloat16\ntemplate<typename T>\nvoid invokeShiftPartition(\n    T* out, const T* input, int batch, int H, int W, int n, int shift_size, int window_size, cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    using T2 = typename TypeConverter<T>::Type;  // bf162 or half2\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2<T2>\n            <<<grid, blockSize, 0, stream>>>((T2*)out, (const T2*)input, batch, H, W, n / 2, shift_size, window_size);\n    }\n    else {\n        shift_partition<T2>\n            <<<grid, blockSize, 0, stream>>>((T2*)out, (const T2*)input, batch, H, W, n / 2, shift_size, window_size);\n    }\n}\n\ntemplate<>\nvoid invokeShiftPartition<float>(float*       out,\n                                 const float* input,\n                                 int          batch,\n                                 int          H,\n                                 int          W,\n                                 int          n,\n                                 int          shift_size,\n                                 int          window_size,\n                                 cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2<float><<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, shift_size, window_size);\n    }\n    else {\n        shift_partition<float><<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, shift_size, window_size);\n    }\n}\n\ntemplate void invokeShiftPartition<float>(float*       out,\n                                          const float* input,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          int          shift_size,\n                                          int          window_size,\n                                          cudaStream_t stream);\n\ntemplate void invokeShiftPartition<half>(\n    half* out, const half* input, int batch, int H, int W, int n, int shift_size, int window_size, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeShiftPartition<__nv_bfloat16>(__nv_bfloat16*       out,\n                                                  const __nv_bfloat16* input,\n                                                  int                  batch,\n                                                  int                  H,\n                                                  int                  W,\n                                                  int                  n,\n                                                  int                  shift_size,\n                                                  int                  window_size,\n                                                  cudaStream_t         stream);\n#endif\n\n/*******************  invokeShiftPartitionCol32  ***********************/\n\ntemplate<typename T>\n__global__ void shift_partition_COL32(int8_t*      out,\n                                      const T*     input,\n                                      int          batch,\n                                      int          H,\n                                      int          W,\n                                      int          n,\n                                      const float* norm_scale_ptr,\n                                      int          shift_size,\n                                      int          window_size)\n{\n    float     norm_scale         = __ldg(norm_scale_ptr);\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int m                  = gridDim.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    if (tid < n) {\n        const int offset_col32_in  = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n        float     local_out        = (float)(__ldg(input + offset_col32_in));\n        const int offset_col32_out = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        out[offset_col32_out]      = float_to_int8_rn(norm_scale * local_out);\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_COL32(int8_t*      out_ptr,\n                                      const half4* input_ptr,\n                                      int          batch,\n                                      int          H,\n                                      int          W,\n                                      int          n,\n                                      const float* norm_scale_ptr,\n                                      int          shift_size,\n                                      int          window_size)\n{\n    float norm_scale = __ldg(norm_scale_ptr);\n\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    int    tid        = threadIdx.x << 2;\n    char4* output_ptr = (char4*)out_ptr;\n    char4  int8_buf;\n\n    if (tid < n) {\n        const int offset_col32     = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n        half4     inputTmp         = input_ptr[offset_col32 >> 2];\n        const int offset_col32_out = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        // const int offset_colMajor_out = output_bid * n + tid;\n        // const int offset_out = index_CUBLASLT_ORDER_COL32_2R_4R4(tid, output_bid, m << 5);\n        int8_buf.x                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.x));\n        int8_buf.y                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.y));\n        int8_buf.z                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.z));\n        int8_buf.w                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.w));\n        output_ptr[offset_col32_out >> 2] = int8_buf;\n    }\n}\n\ntemplate<typename T>\n__global__ void shift_partition_v2_COL32(int8_t* out,\n                                         const T* __restrict input,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         const float* norm_scale_ptr,\n                                         int          shift_size,\n                                         int          window_size)\n{\n    float     norm_scale         = __ldg(norm_scale_ptr);\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int m                  = gridDim.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            const int offset_col32_in  = (col_id & 0xffffffe0) * m + (bid << 5) + (col_id & 31);\n            float     local_out        = (float)(__ldg(input + offset_col32_in));\n            const int offset_col32_out = (col_id & 0xffffffe0) * m + (output_bid << 5) + (col_id & 31);\n            out[offset_col32_out]      = float_to_int8_rn(norm_scale * local_out);\n        }\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_v2_COL32(int8_t* out_ptr,\n                                         const half2* __restrict input_ptr,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         const float* norm_scale_ptr,\n                                         int          shift_size,\n                                         int          window_size)\n{\n    float     norm_scale   = __ldg(norm_scale_ptr);\n    const int ite          = 4;\n    const int tid          = threadIdx.x;\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    char2* output_ptr = (char2*)out_ptr;\n    char2  int8_buf;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = (i * blockDim.x + tid) << 1;\n        if (col_id < n) {\n            const int offset_col32            = (col_id & 0xffffffe0) * m + (bid << 5) + (col_id & 31);\n            half2     outVal                  = __ldg(input_ptr + (offset_col32 >> 1));\n            const int offset_col32_out        = (col_id & 0xffffffe0) * m + (output_bid << 5) + (col_id & 31);\n            int8_buf.x                        = float_to_int8_rn(norm_scale * static_cast<float>(outVal.x));\n            int8_buf.y                        = float_to_int8_rn(norm_scale * static_cast<float>(outVal.y));\n            output_ptr[offset_col32_out >> 1] = int8_buf;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeShiftPartitionCol32(int8_t*      out,\n                               const T*     input,\n                               int          batch,\n                               int          H,\n                               int          W,\n                               int          n,\n                               const float* norm_scale_ptr,\n                               int          shift_size,\n                               int          window_size,\n                               cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2_COL32<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, norm_scale_ptr, shift_size, window_size);\n    }\n    else {\n        shift_partition_COL32<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, norm_scale_ptr, shift_size, window_size);\n    }\n}\n\ntemplate<>\nvoid invokeShiftPartitionCol32(int8_t*      out,\n                               const half*  input,\n                               int          batch,\n                               int          H,\n                               int          W,\n                               int          n,\n                               const float* scale_ptr,\n                               int          shift_size,\n                               int          window_size,\n                               cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2_COL32<<<grid, blockSize, 0, stream>>>(\n            out, (const half2*)input, batch, H, W, n, scale_ptr, shift_size, window_size);\n    }\n    else {\n        blockSize = (n / 4 + 32) / 32 * 32;\n        shift_partition_COL32<<<grid, blockSize, 0, stream>>>(\n            out, (const half4*)input, batch, H, W, n, scale_ptr, shift_size, window_size);\n    }\n}\n\ntemplate void invokeShiftPartitionCol32(int8_t*      out,\n                                        const float* input,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float* scale_ptr,\n                                        int          shift_size,\n                                        int          window_size,\n                                        cudaStream_t stream);\n\ntemplate void invokeShiftPartitionCol32(int8_t*      out,\n                                        const half*  input,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float* scale_ptr,\n                                        int          shift_size,\n                                        int          window_size,\n                                        cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/image_shift_partition_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeShiftPartition  ***********************/\n\n// applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void\nshift_partition(T2* out_ptr, const T2* input_ptr, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    int       tid                = threadIdx.x;\n\n    int id = bid * n + tid;\n    if (tid < n) {\n        out_ptr[output_bid * n + tid] = input_ptr[id];\n    }\n}\n\n// applied to float\ntemplate<>\n__global__ void\nshift_partition<float>(float* out, const float* input, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    if (tid < n) {\n        out[output_bid * n + tid] = (float)(__ldg(input + bid * n + tid));\n    }\n}\n\n// Applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void shift_partition_v2(\n    T2* out_ptr, const T2* __restrict input_ptr, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    using T1                     = typename TypeConverter<T2>::Type;  // half2 to half, bfloat162 to bfloat\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out_ptr[output_offset + col_id] = ldg(input_ptr + offset + col_id);\n        }\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_v2<float>(\n    float* out, const float* __restrict input, int batch, int H, int W, int n, int shift_size, int window_size)\n{\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[output_offset + col_id] = (float)(__ldg(input + offset + col_id));\n        }\n    }\n}\n\n// Applied to half or Bfloat16\ntemplate<typename T>\nvoid invokeShiftPartition(\n    T* out, const T* input, int batch, int H, int W, int n, int shift_size, int window_size, hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    using T2 = typename TypeConverter<T>::Type;  // bf162 or half2\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2<T2>\n            <<<grid, blockSize, 0, stream>>>((T2*)out, (const T2*)input, batch, H, W, n / 2, shift_size, window_size);\n    }\n    else {\n        shift_partition<T2>\n            <<<grid, blockSize, 0, stream>>>((T2*)out, (const T2*)input, batch, H, W, n / 2, shift_size, window_size);\n    }\n}\n\ntemplate<>\nvoid invokeShiftPartition<float>(float*       out,\n                                 const float* input,\n                                 int          batch,\n                                 int          H,\n                                 int          W,\n                                 int          n,\n                                 int          shift_size,\n                                 int          window_size,\n                                 hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2<float><<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, shift_size, window_size);\n    }\n    else {\n        shift_partition<float><<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, shift_size, window_size);\n    }\n}\n\ntemplate void invokeShiftPartition<float>(float*       out,\n                                          const float* input,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          int          shift_size,\n                                          int          window_size,\n                                          hipStream_t stream);\n\ntemplate void invokeShiftPartition<half>(\n    half* out, const half* input, int batch, int H, int W, int n, int shift_size, int window_size, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeShiftPartition<__hip_bfloat16>(__hip_bfloat16*       out,\n                                                  const __hip_bfloat16* input,\n                                                  int                  batch,\n                                                  int                  H,\n                                                  int                  W,\n                                                  int                  n,\n                                                  int                  shift_size,\n                                                  int                  window_size,\n                                                  hipStream_t         stream);\n#endif\n\n/*******************  invokeShiftPartitionCol32  ***********************/\n\ntemplate<typename T>\n__global__ void shift_partition_COL32(int8_t*      out,\n                                      const T*     input,\n                                      int          batch,\n                                      int          H,\n                                      int          W,\n                                      int          n,\n                                      const float* norm_scale_ptr,\n                                      int          shift_size,\n                                      int          window_size)\n{\n    float     norm_scale         = __ldg(norm_scale_ptr);\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int m                  = gridDim.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    if (tid < n) {\n        const int offset_col32_in  = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n        float     local_out        = (float)(__ldg(input + offset_col32_in));\n        const int offset_col32_out = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        out[offset_col32_out]      = float_to_int8_rn(norm_scale * local_out);\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_COL32(int8_t*      out_ptr,\n                                      const half4* input_ptr,\n                                      int          batch,\n                                      int          H,\n                                      int          W,\n                                      int          n,\n                                      const float* norm_scale_ptr,\n                                      int          shift_size,\n                                      int          window_size)\n{\n    float norm_scale = __ldg(norm_scale_ptr);\n\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    int    tid        = threadIdx.x << 2;\n    char4* output_ptr = (char4*)out_ptr;\n    char4  int8_buf;\n\n    if (tid < n) {\n        const int offset_col32     = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n        half4     inputTmp         = input_ptr[offset_col32 >> 2];\n        const int offset_col32_out = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        // const int offset_colMajor_out = output_bid * n + tid;\n        // const int offset_out = index_CUBLASLT_ORDER_COL32_2R_4R4(tid, output_bid, m << 5);\n        int8_buf.x                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.x));\n        int8_buf.y                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.y));\n        int8_buf.z                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.z));\n        int8_buf.w                        = float_to_int8_rn(norm_scale * static_cast<float>(inputTmp.w));\n        output_ptr[offset_col32_out >> 2] = int8_buf;\n    }\n}\n\ntemplate<typename T>\n__global__ void shift_partition_v2_COL32(int8_t* out,\n                                         const T* __restrict input,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         const float* norm_scale_ptr,\n                                         int          shift_size,\n                                         int          window_size)\n{\n    float     norm_scale         = __ldg(norm_scale_ptr);\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int m                  = gridDim.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            const int offset_col32_in  = (col_id & 0xffffffe0) * m + (bid << 5) + (col_id & 31);\n            float     local_out        = (float)(__ldg(input + offset_col32_in));\n            const int offset_col32_out = (col_id & 0xffffffe0) * m + (output_bid << 5) + (col_id & 31);\n            out[offset_col32_out]      = float_to_int8_rn(norm_scale * local_out);\n        }\n    }\n}\n\ntemplate<>\n__global__ void shift_partition_v2_COL32(int8_t* out_ptr,\n                                         const half2* __restrict input_ptr,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         const float* norm_scale_ptr,\n                                         int          shift_size,\n                                         int          window_size)\n{\n    float     norm_scale   = __ldg(norm_scale_ptr);\n    const int ite          = 4;\n    const int tid          = threadIdx.x;\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    char2* output_ptr = (char2*)out_ptr;\n    char2  int8_buf;\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = (i * blockDim.x + tid) << 1;\n        if (col_id < n) {\n            const int offset_col32            = (col_id & 0xffffffe0) * m + (bid << 5) + (col_id & 31);\n            half2     outVal                  = __ldg(input_ptr + (offset_col32 >> 1));\n            const int offset_col32_out        = (col_id & 0xffffffe0) * m + (output_bid << 5) + (col_id & 31);\n            int8_buf.x                        = float_to_int8_rn(norm_scale * static_cast<float>(outVal.x));\n            int8_buf.y                        = float_to_int8_rn(norm_scale * static_cast<float>(outVal.y));\n            output_ptr[offset_col32_out >> 1] = int8_buf;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeShiftPartitionCol32(int8_t*      out,\n                               const T*     input,\n                               int          batch,\n                               int          H,\n                               int          W,\n                               int          n,\n                               const float* norm_scale_ptr,\n                               int          shift_size,\n                               int          window_size,\n                               hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2_COL32<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, norm_scale_ptr, shift_size, window_size);\n    }\n    else {\n        shift_partition_COL32<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, batch, H, W, n, norm_scale_ptr, shift_size, window_size);\n    }\n}\n\ntemplate<>\nvoid invokeShiftPartitionCol32(int8_t*      out,\n                               const half*  input,\n                               int          batch,\n                               int          H,\n                               int          W,\n                               int          n,\n                               const float* scale_ptr,\n                               int          shift_size,\n                               int          window_size,\n                               hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        shift_partition_v2_COL32<<<grid, blockSize, 0, stream>>>(\n            out, (const half2*)input, batch, H, W, n, scale_ptr, shift_size, window_size);\n    }\n    else {\n        blockSize = (n / 4 + 32) / 32 * 32;\n        shift_partition_COL32<<<grid, blockSize, 0, stream>>>(\n            out, (const half4*)input, batch, H, W, n, scale_ptr, shift_size, window_size);\n    }\n}\n\ntemplate void invokeShiftPartitionCol32(int8_t*      out,\n                                        const float* input,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float* scale_ptr,\n                                        int          shift_size,\n                                        int          window_size,\n                                        hipStream_t stream);\n\ntemplate void invokeShiftPartitionCol32(int8_t*      out,\n                                        const half*  input,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float* scale_ptr,\n                                        int          shift_size,\n                                        int          window_size,\n                                        hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/image_shift_partition_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cublas_v2.h\"\n#include \"gen_relative_pos_bias.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/activation_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n/*******************  invokeGenRelativePosBias  ***********************/\n// relative_position_bias_table is [(2*window_size-1)*(2*window_size-1), headNum]\n// relative_position_bias is [head_num, window_size^2, window_size^2]\n// grid(window_size*window_size, head_num)\n// block(window_size*window_size)\n\ntemplate<typename T, typename Tindex>\n__global__ void gen_relative_pos_bias(T*            relative_position_bias,\n                                      const T*      relative_position_bias_table,\n                                      const Tindex* relative_position_bias_index,\n                                      const int     window_size,\n                                      const int     head_num)\n{\n    const int    h_in_window           = blockIdx.x / window_size;\n    const int    w_in_window           = blockIdx.x % window_size;\n    const int    h_in_token            = threadIdx.x / window_size;\n    const int    w_in_token            = threadIdx.x % window_size;\n    const int    head_idx              = blockIdx.y;\n    const int    elements_per_window   = window_size * window_size;\n    const size_t elements_per_window_2 = elements_per_window * elements_per_window;\n    const size_t output_idx = head_idx * elements_per_window_2 + blockIdx.x * elements_per_window + threadIdx.x;\n    if (output_idx < head_num * elements_per_window_2) {\n        const Tindex idx_in_table =\n            relative_position_bias_index[(h_in_window * window_size + w_in_window) * elements_per_window\n                                         + h_in_token * window_size + w_in_token];\n        relative_position_bias[output_idx] = relative_position_bias_table[idx_in_table * head_num + head_idx];\n    }\n}\n\ntemplate<typename T, typename Tindex>\nvoid invokeGenRelativePosBias(T*            relative_position_bias,\n                              const T*      relative_position_bias_table,\n                              const Tindex* relative_position_bias_index,\n                              const int     window_size,\n                              const int     head_num,\n                              cudaStream_t  stream)\n{\n    dim3 grid(window_size * window_size, head_num);\n    dim3 block(window_size * window_size);\n\n    if (block.x > 1024) {\n        printf(\"[ERROR][invokeGenRelativePosBias] window_size*window_size > 1024.\\n\");\n        exit(-1);\n    }\n\n    gen_relative_pos_bias<<<grid, block, 0, stream>>>(\n        relative_position_bias, relative_position_bias_table, relative_position_bias_index, window_size, head_num);\n}\n\n/*******************  invokeGenRelativePosBiasV2  ***********************/\ntemplate<typename T, typename Tindex>\nvoid invokeGenRelativePosBiasV2(T*            relative_position_bias,\n                                const T*      relative_coords_table,\n                                const Tindex* relative_position_bias_index,\n                                const T*      cpb_mlp_weight1,\n                                const T*      cpb_mlp_bias1,\n                                const T*      cpb_mlp_weight2,\n                                const int     window_size,\n                                const int     cpb_mlp_in_dim,\n                                const int     cpb_mlp_out_dim,\n                                const int     head_num,\n                                cudaStream_t  stream)\n{\n\n    dim3 grid(window_size * window_size, head_num);\n    dim3 block(window_size * window_size);\n\n    if (block.x > 1024) {\n        printf(\"[ERROR][invokeGenRelativePosBias] window_size*window_size > 1024.\\n\");\n        exit(-1);\n    }\n\n    T* relative_position_bias_table;\n    check_cuda_error(cudaMalloc(&relative_position_bias_table,\n                                ((2 * window_size - 1) * (2 * window_size - 1) * head_num) * sizeof(T)));\n    T* cpb_mlp_1;\n    check_cuda_error(\n        cudaMalloc(&cpb_mlp_1, ((2 * window_size - 1) * (2 * window_size - 1) * cpb_mlp_out_dim) * sizeof(T)));\n    cublasHandle_t cublas_handle;\n    check_cuda_error(cublasCreate(&cublas_handle));\n\n    int            m     = (2 * window_size - 1) * (2 * window_size - 1);\n    T              alpha = (T)1.0f;\n    T              beta  = (T)0.0f;\n    cudaDataType_t type  = std::is_same<float, T>::value ? CUDA_R_32F : CUDA_R_16F;\n#if (CUDART_VERSION >= 11000)\n    cublasComputeType_t compute_type = std::is_same<float, T>::value ? CUBLAS_COMPUTE_32F : CUBLAS_COMPUTE_16F;\n#else\n    cudaDataType_t compute_type = std::is_same<float, T>::value ? CUDA_R_32F : CUDA_R_16F;\n#endif\n    cublasGemmAlgo_t algo = std::is_same<float, T>::value ? CUBLAS_GEMM_DEFAULT : CUBLAS_GEMM_DEFAULT_TENSOR_OP;\n    check_cuda_error(cublasGemmEx(cublas_handle,\n                                  CUBLAS_OP_T,\n                                  CUBLAS_OP_N,\n                                  cpb_mlp_out_dim,\n                                  m,\n                                  cpb_mlp_in_dim,\n                                  &alpha,\n                                  cpb_mlp_weight1,\n                                  type,\n                                  cpb_mlp_in_dim,\n                                  relative_coords_table,\n                                  type,\n                                  cpb_mlp_in_dim,\n                                  &beta,\n                                  cpb_mlp_1,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  compute_type,\n                                  algo));\n\n    invokeGenericActivation<ReluActivation, T, T>(\n        cpb_mlp_1, cpb_mlp_bias1, nullptr, nullptr, nullptr, nullptr, m, cpb_mlp_out_dim, 0, nullptr, nullptr, stream);\n\n    check_cuda_error(cublasGemmEx(cublas_handle,\n                                  CUBLAS_OP_T,\n                                  CUBLAS_OP_N,\n                                  head_num,\n                                  m,\n                                  cpb_mlp_out_dim,\n                                  &alpha,\n                                  cpb_mlp_weight2,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  cpb_mlp_1,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  &beta,\n                                  relative_position_bias_table,\n                                  type,\n                                  head_num,\n                                  compute_type,\n                                  algo));\n\n    gen_relative_pos_bias<<<grid, block, 0, stream>>>(\n        relative_position_bias, relative_position_bias_table, relative_position_bias_index, window_size, head_num);\n\n    invokeSigmoid(\n        relative_position_bias, window_size * window_size * window_size * window_size * head_num, 16.0f, stream);\n    check_cuda_error(cudaFree(relative_position_bias_table));\n    check_cuda_error(cudaFree(cpb_mlp_1));\n    check_cuda_error(cublasDestroy(cublas_handle));\n}\n\n/*******************  instantiation  ***********************/\n\ntemplate void invokeGenRelativePosBias(float*       relative_position_bias,\n                                       const float* relative_position_bias_table,\n                                       const int*   relative_position_bias_index,\n                                       const int    window_size,\n                                       const int    head_num,\n                                       cudaStream_t stream);\n\ntemplate void invokeGenRelativePosBias(half*        relative_position_bias,\n                                       const half*  relative_position_bias_table,\n                                       const int*   relative_position_bias_index,\n                                       const int    window_size,\n                                       const int    head_num,\n                                       cudaStream_t stream);\n\ntemplate void invokeGenRelativePosBias(float*         relative_position_bias,\n                                       const float*   relative_position_bias_table,\n                                       const int64_t* relative_position_bias_index,\n                                       const int      window_size,\n                                       const int      head_num,\n                                       cudaStream_t   stream);\n\ntemplate void invokeGenRelativePosBias(half*          relative_position_bias,\n                                       const half*    relative_position_bias_table,\n                                       const int64_t* relative_position_bias_index,\n                                       const int      window_size,\n                                       const int      head_num,\n                                       cudaStream_t   stream);\n\n__host__ __device__ uint32_t pow2_rounddown(uint32_t x)\n{\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    x >>= 1;\n    return x + 1;\n}\n\ntemplate<typename T>\n__global__ void generate_alibi_slopes(T* alibi_slopes, const size_t num_heads)\n{\n    if (threadIdx.x < num_heads) {\n        // The nearest power of 2 greater than num_heads followed by HF's implementation.\n        int num_heads_pow2 = pow2_rounddown(num_heads);\n        // Loop over the attention head.\n        for (int h = threadIdx.x; h < num_heads; h += blockDim.x) {\n            if (h < num_heads_pow2) {\n                alibi_slopes[h] = static_cast<T>(powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2) - 3.f)), h + 1));\n            }\n            else {\n                alibi_slopes[h] = static_cast<T>(\n                    powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2 << 1) - 3.f)), (h - num_heads_pow2) * 2 + 1));\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildAlibiSlopes(T* alibi_slopes, const size_t num_heads, cudaStream_t stream)\n{\n    // Generate the slopes of a linear attention linear bias.\n    //\n    // Paper: https://arxiv.org/abs/2108.12409\n    // HF's implementation\n    //   https://github.com/huggingface/transformers/blob/56ef0ba44765162f830873c140bd40bdc975cc34/src/transformers/models/bloom/modeling_bloom.py#L86\n    // Author's implementation\n    //   https://github.com/ofirpress/attention_with_linear_biases/blob/02aa87e7a29e9340efd28d6d169018eafb3aa57a/fairseq/models/transformer.py#L760\n    //\n    // alibi_slopes: [num_heads],\n    //     strictly follows how HF implements. which treats power-of-2 heads, and non-power-of-2 heads differently.\n    //     what paper generates differs with HF's when number of heads is not a power of 2.\n    // num_heads: the number of attention heads.\n    // stream: a cuda stream.\n\n    dim3 block(min((int)num_heads, 512));\n    generate_alibi_slopes<<<1, block, 0, stream>>>(alibi_slopes, num_heads);\n}\n\ntemplate void invokeBuildAlibiSlopes(float* alibi_slopes, const size_t num_heads, cudaStream_t stream);\ntemplate void invokeBuildAlibiSlopes(half* alibi_slopes, const size_t num_heads, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildAlibiSlopes(__nv_bfloat16* alibi_slopes, const size_t num_heads, cudaStream_t stream);\n#endif\n\ntemplate void invokeGenRelativePosBiasV2(float*       relative_position_bias,\n                                         const float* relative_coords_table,\n                                         const int*   relative_position_bias_index,\n                                         const float* cpb_mlp_weight1,\n                                         const float* cpb_mlp_bias1,\n                                         const float* cpb_mlp_weight2,\n                                         const int    window_size,\n                                         const int    cpb_mlp_in_dim,\n                                         const int    cpb_mlp_out_dim,\n                                         const int    head_num,\n                                         cudaStream_t stream);\n\ntemplate void invokeGenRelativePosBiasV2(half*        relative_position_bias,\n                                         const half*  relative_coords_table,\n                                         const int*   relative_position_bias_index,\n                                         const half*  cpb_mlp_weight1,\n                                         const half*  cpb_mlp_bias1,\n                                         const half*  cpb_mlp_weight2,\n                                         const int    window_size,\n                                         const int    cpb_mlp_in_dim,\n                                         const int    cpb_mlp_out_dim,\n                                         const int    head_num,\n                                         cudaStream_t stream);\n\ntemplate void invokeGenRelativePosBiasV2(float*         relative_position_bias,\n                                         const float*   relative_coords_table,\n                                         const int64_t* relative_position_bias_index,\n                                         const float*   cpb_mlp_weight1,\n                                         const float*   cpb_mlp_bias1,\n                                         const float*   cpb_mlp_weight2,\n                                         const int      window_size,\n                                         const int      cpb_mlp_in_dim,\n                                         const int      cpb_mlp_out_dim,\n                                         const int      head_num,\n                                         cudaStream_t   stream);\n\ntemplate void invokeGenRelativePosBiasV2(half*          relative_position_bias,\n                                         const half*    relative_coords_table,\n                                         const int64_t* relative_position_bias_index,\n                                         const half*    cpb_mlp_weight1,\n                                         const half*    cpb_mlp_bias1,\n                                         const half*    cpb_mlp_weight2,\n                                         const int      window_size,\n                                         const int      cpb_mlp_in_dim,\n                                         const int      cpb_mlp_out_dim,\n                                         const int      head_num,\n                                         cudaStream_t   stream);\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"hipblas.h\"\n#include \"gen_relative_pos_bias.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/activation_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n/*******************  invokeGenRelativePosBias  ***********************/\n// relative_position_bias_table is [(2*window_size-1)*(2*window_size-1), headNum]\n// relative_position_bias is [head_num, window_size^2, window_size^2]\n// grid(window_size*window_size, head_num)\n// block(window_size*window_size)\n\ntemplate<typename T, typename Tindex>\n__global__ void gen_relative_pos_bias(T*            relative_position_bias,\n                                      const T*      relative_position_bias_table,\n                                      const Tindex* relative_position_bias_index,\n                                      const int     window_size,\n                                      const int     head_num)\n{\n    const int    h_in_window           = blockIdx.x / window_size;\n    const int    w_in_window           = blockIdx.x % window_size;\n    const int    h_in_token            = threadIdx.x / window_size;\n    const int    w_in_token            = threadIdx.x % window_size;\n    const int    head_idx              = blockIdx.y;\n    const int    elements_per_window   = window_size * window_size;\n    const size_t elements_per_window_2 = elements_per_window * elements_per_window;\n    const size_t output_idx = head_idx * elements_per_window_2 + blockIdx.x * elements_per_window + threadIdx.x;\n    if (output_idx < head_num * elements_per_window_2) {\n        const Tindex idx_in_table =\n            relative_position_bias_index[(h_in_window * window_size + w_in_window) * elements_per_window\n                                         + h_in_token * window_size + w_in_token];\n        relative_position_bias[output_idx] = relative_position_bias_table[idx_in_table * head_num + head_idx];\n    }\n}\n\ntemplate<typename T, typename Tindex>\nvoid invokeGenRelativePosBias(T*            relative_position_bias,\n                              const T*      relative_position_bias_table,\n                              const Tindex* relative_position_bias_index,\n                              const int     window_size,\n                              const int     head_num,\n                              hipStream_t  stream)\n{\n    dim3 grid(window_size * window_size, head_num);\n    dim3 block(window_size * window_size);\n\n    if (block.x > 1024) {\n        printf(\"[ERROR][invokeGenRelativePosBias] window_size*window_size > 1024.\\n\");\n        exit(-1);\n    }\n\n    gen_relative_pos_bias<<<grid, block, 0, stream>>>(\n        relative_position_bias, relative_position_bias_table, relative_position_bias_index, window_size, head_num);\n}\n\n/*******************  invokeGenRelativePosBiasV2  ***********************/\ntemplate<typename T, typename Tindex>\nvoid invokeGenRelativePosBiasV2(T*            relative_position_bias,\n                                const T*      relative_coords_table,\n                                const Tindex* relative_position_bias_index,\n                                const T*      cpb_mlp_weight1,\n                                const T*      cpb_mlp_bias1,\n                                const T*      cpb_mlp_weight2,\n                                const int     window_size,\n                                const int     cpb_mlp_in_dim,\n                                const int     cpb_mlp_out_dim,\n                                const int     head_num,\n                                hipStream_t  stream)\n{\n\n    dim3 grid(window_size * window_size, head_num);\n    dim3 block(window_size * window_size);\n\n    if (block.x > 1024) {\n        printf(\"[ERROR][invokeGenRelativePosBias] window_size*window_size > 1024.\\n\");\n        exit(-1);\n    }\n\n    T* relative_position_bias_table;\n    check_cuda_error(hipMalloc(&relative_position_bias_table,\n                                ((2 * window_size - 1) * (2 * window_size - 1) * head_num) * sizeof(T)));\n    T* cpb_mlp_1;\n    check_cuda_error(\n        hipMalloc(&cpb_mlp_1, ((2 * window_size - 1) * (2 * window_size - 1) * cpb_mlp_out_dim) * sizeof(T)));\n    hipblasHandle_t cublas_handle;\n    check_cuda_error(hipblasCreate(&cublas_handle));\n\n    int            m     = (2 * window_size - 1) * (2 * window_size - 1);\n    T              alpha = (T)1.0f;\n    T              beta  = (T)0.0f;\n    hipDataType type  = std::is_same<float, T>::value ? HIP_R_32F : HIP_R_16F;\n#if (CUDART_VERSION >= 11000)\n    hipblasComputeType_t compute_type = std::is_same<float, T>::value ? HIPBLAS_COMPUTE_32F : HIPBLAS_COMPUTE_16F;\n#else\n    hipDataType compute_type = std::is_same<float, T>::value ? HIP_R_32F : HIP_R_16F;\n#endif\n    hipblasGemmAlgo_t algo = std::is_same<float, T>::value ? HIPBLAS_GEMM_DEFAULT : CUBLAS_GEMM_DEFAULT_TENSOR_OP;\n    check_cuda_error(hipblasGemmEx(cublas_handle,\n                                  HIPBLAS_OP_T,\n                                  HIPBLAS_OP_N,\n                                  cpb_mlp_out_dim,\n                                  m,\n                                  cpb_mlp_in_dim,\n                                  &alpha,\n                                  cpb_mlp_weight1,\n                                  type,\n                                  cpb_mlp_in_dim,\n                                  relative_coords_table,\n                                  type,\n                                  cpb_mlp_in_dim,\n                                  &beta,\n                                  cpb_mlp_1,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  compute_type,\n                                  algo));\n\n    invokeGenericActivation<ReluActivation, T, T>(\n        cpb_mlp_1, cpb_mlp_bias1, nullptr, nullptr, nullptr, nullptr, m, cpb_mlp_out_dim, 0, nullptr, nullptr, stream);\n\n    check_cuda_error(hipblasGemmEx(cublas_handle,\n                                  HIPBLAS_OP_T,\n                                  HIPBLAS_OP_N,\n                                  head_num,\n                                  m,\n                                  cpb_mlp_out_dim,\n                                  &alpha,\n                                  cpb_mlp_weight2,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  cpb_mlp_1,\n                                  type,\n                                  cpb_mlp_out_dim,\n                                  &beta,\n                                  relative_position_bias_table,\n                                  type,\n                                  head_num,\n                                  compute_type,\n                                  algo));\n\n    gen_relative_pos_bias<<<grid, block, 0, stream>>>(\n        relative_position_bias, relative_position_bias_table, relative_position_bias_index, window_size, head_num);\n\n    invokeSigmoid(\n        relative_position_bias, window_size * window_size * window_size * window_size * head_num, 16.0f, stream);\n    check_cuda_error(hipFree(relative_position_bias_table));\n    check_cuda_error(hipFree(cpb_mlp_1));\n    check_cuda_error(hipblasDestroy(cublas_handle));\n}\n\n/*******************  instantiation  ***********************/\n\ntemplate void invokeGenRelativePosBias(float*       relative_position_bias,\n                                       const float* relative_position_bias_table,\n                                       const int*   relative_position_bias_index,\n                                       const int    window_size,\n                                       const int    head_num,\n                                       hipStream_t stream);\n\ntemplate void invokeGenRelativePosBias(half*        relative_position_bias,\n                                       const half*  relative_position_bias_table,\n                                       const int*   relative_position_bias_index,\n                                       const int    window_size,\n                                       const int    head_num,\n                                       hipStream_t stream);\n\ntemplate void invokeGenRelativePosBias(float*         relative_position_bias,\n                                       const float*   relative_position_bias_table,\n                                       const int64_t* relative_position_bias_index,\n                                       const int      window_size,\n                                       const int      head_num,\n                                       hipStream_t   stream);\n\ntemplate void invokeGenRelativePosBias(half*          relative_position_bias,\n                                       const half*    relative_position_bias_table,\n                                       const int64_t* relative_position_bias_index,\n                                       const int      window_size,\n                                       const int      head_num,\n                                       hipStream_t   stream);\n\n__host__ __device__ uint32_t pow2_rounddown(uint32_t x)\n{\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    x >>= 1;\n    return x + 1;\n}\n\ntemplate<typename T>\n__global__ void generate_alibi_slopes(T* alibi_slopes, const size_t num_heads)\n{\n    if (threadIdx.x < num_heads) {\n        // The nearest power of 2 greater than num_heads followed by HF's implementation.\n        int num_heads_pow2 = pow2_rounddown(num_heads);\n        // Loop over the attention head.\n        for (int h = threadIdx.x; h < num_heads; h += blockDim.x) {\n            if (h < num_heads_pow2) {\n                alibi_slopes[h] = static_cast<T>(powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2) - 3.f)), h + 1));\n            }\n            else {\n                alibi_slopes[h] = static_cast<T>(\n                    powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2 << 1) - 3.f)), (h - num_heads_pow2) * 2 + 1));\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildAlibiSlopes(T* alibi_slopes, const size_t num_heads, hipStream_t stream)\n{\n    // Generate the slopes of a linear attention linear bias.\n    //\n    // Paper: https://arxiv.org/abs/2108.12409\n    // HF's implementation\n    //   https://github.com/huggingface/transformers/blob/56ef0ba44765162f830873c140bd40bdc975cc34/src/transformers/models/bloom/modeling_bloom.py#L86\n    // Author's implementation\n    //   https://github.com/ofirpress/attention_with_linear_biases/blob/02aa87e7a29e9340efd28d6d169018eafb3aa57a/fairseq/models/transformer.py#L760\n    //\n    // alibi_slopes: [num_heads],\n    //     strictly follows how HF implements. which treats power-of-2 heads, and non-power-of-2 heads differently.\n    //     what paper generates differs with HF's when number of heads is not a power of 2.\n    // num_heads: the number of attention heads.\n    // stream: a cuda stream.\n\n    dim3 block(min((int)num_heads, 512));\n    generate_alibi_slopes<<<1, block, 0, stream>>>(alibi_slopes, num_heads);\n}\n\ntemplate void invokeBuildAlibiSlopes(float* alibi_slopes, const size_t num_heads, hipStream_t stream);\ntemplate void invokeBuildAlibiSlopes(half* alibi_slopes, const size_t num_heads, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildAlibiSlopes(__hip_bfloat16* alibi_slopes, const size_t num_heads, hipStream_t stream);\n#endif\n\ntemplate void invokeGenRelativePosBiasV2(float*       relative_position_bias,\n                                         const float* relative_coords_table,\n                                         const int*   relative_position_bias_index,\n                                         const float* cpb_mlp_weight1,\n                                         const float* cpb_mlp_bias1,\n                                         const float* cpb_mlp_weight2,\n                                         const int    window_size,\n                                         const int    cpb_mlp_in_dim,\n                                         const int    cpb_mlp_out_dim,\n                                         const int    head_num,\n                                         hipStream_t stream);\n\ntemplate void invokeGenRelativePosBiasV2(half*        relative_position_bias,\n                                         const half*  relative_coords_table,\n                                         const int*   relative_position_bias_index,\n                                         const half*  cpb_mlp_weight1,\n                                         const half*  cpb_mlp_bias1,\n                                         const half*  cpb_mlp_weight2,\n                                         const int    window_size,\n                                         const int    cpb_mlp_in_dim,\n                                         const int    cpb_mlp_out_dim,\n                                         const int    head_num,\n                                         hipStream_t stream);\n\ntemplate void invokeGenRelativePosBiasV2(float*         relative_position_bias,\n                                         const float*   relative_coords_table,\n                                         const int64_t* relative_position_bias_index,\n                                         const float*   cpb_mlp_weight1,\n                                         const float*   cpb_mlp_bias1,\n                                         const float*   cpb_mlp_weight2,\n                                         const int      window_size,\n                                         const int      cpb_mlp_in_dim,\n                                         const int      cpb_mlp_out_dim,\n                                         const int      head_num,\n                                         hipStream_t   stream);\n\ntemplate void invokeGenRelativePosBiasV2(half*          relative_position_bias,\n                                         const half*    relative_coords_table,\n                                         const int64_t* relative_position_bias_index,\n                                         const half*    cpb_mlp_weight1,\n                                         const half*    cpb_mlp_bias1,\n                                         const half*    cpb_mlp_weight2,\n                                         const int      window_size,\n                                         const int      cpb_mlp_in_dim,\n                                         const int      cpb_mlp_out_dim,\n                                         const int      head_num,\n                                         hipStream_t   stream);\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/gen_relative_pos_bias.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/layernorm_fp8_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_bf16_wrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <cuda_fp16.h>\n\nnamespace fastertransformer {\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\nquatizeVectorE4M3(__nv_fp8_e4m3* output, float const* input_qua_amax_ptr, T const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = __nv_fp8_e4m3((float)(input[i]) / __ldg(input_qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = __nv_fp8_e4m3((float)(input[i]) / __ldg(input_qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeQuatizeVectorE4M3(__nv_fp8_e4m3* output,\n                             float const*   input_qua_amax_ptr,\n                             T const*       input,\n                             uint32_t       size,\n                             uint32_t       n,\n                             cudaStream_t   stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    quatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, input_qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeQuatizeVectorE4M3<float, 0>(__nv_fp8_e4m3* output,\n                                                float const*   input_qua_amax_ptr,\n                                                float const*   input,\n                                                uint32_t       size,\n                                                uint32_t       n,\n                                                cudaStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<half, 0>(__nv_fp8_e4m3* output,\n                                               float const*   input_qua_amax_ptr,\n                                               half const*    input,\n                                               uint32_t       size,\n                                               uint32_t       n,\n                                               cudaStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<__nv_bfloat16, 0>(__nv_fp8_e4m3*       output,\n                                                        float const*         input_qua_amax_ptr,\n                                                        __nv_bfloat16 const* input,\n                                                        uint32_t             size,\n                                                        uint32_t             n,\n                                                        cudaStream_t         stream);\n\ntemplate void invokeQuatizeVectorE4M3<float, 1>(__nv_fp8_e4m3* output,\n                                                float const*   input_qua_amax_ptr,\n                                                float const*   input,\n                                                uint32_t       size,\n                                                uint32_t       n,\n                                                cudaStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<half, 1>(__nv_fp8_e4m3* output,\n                                               float const*   input_qua_amax_ptr,\n                                               half const*    input,\n                                               uint32_t       size,\n                                               uint32_t       n,\n                                               cudaStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<__nv_bfloat16, 1>(__nv_fp8_e4m3*       output,\n                                                        float const*         input_qua_amax_ptr,\n                                                        __nv_bfloat16 const* input,\n                                                        uint32_t             size,\n                                                        uint32_t             n,\n                                                        cudaStream_t         stream);\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\ndequatizeVectorE4M3(T* output, float const* qua_amax_ptr, __nv_fp8_e4m3 const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = float(input[i]) * __ldg(qua_amax_ptr + (i % n));\n        }\n        else {\n            output[i] = float(input[i]) * __ldg(qua_amax_ptr);\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeDequatizeVectorE4M3(\n    T* output, float const* qua_amax_ptr, __nv_fp8_e4m3 const* input, uint32_t size, uint32_t n, cudaStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    dequatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeDequatizeVectorE4M3<float, 0>(float*               output,\n                                                  float const*         qua_amax_ptr,\n                                                  __nv_fp8_e4m3 const* input,\n                                                  uint32_t             size,\n                                                  uint32_t             n,\n                                                  cudaStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<half, 0>(half*                output,\n                                                 float const*         qua_amax_ptr,\n                                                 __nv_fp8_e4m3 const* input,\n                                                 uint32_t             size,\n                                                 uint32_t             n,\n                                                 cudaStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<__nv_bfloat16, 0>(__nv_bfloat16*       output,\n                                                          float const*         qua_amax_ptr,\n                                                          __nv_fp8_e4m3 const* input,\n                                                          uint32_t             size,\n                                                          uint32_t             n,\n                                                          cudaStream_t         stream);\n\ntemplate void invokeDequatizeVectorE4M3<float, 1>(float*               output,\n                                                  float const*         qua_amax_ptr,\n                                                  __nv_fp8_e4m3 const* input,\n                                                  uint32_t             size,\n                                                  uint32_t             n,\n                                                  cudaStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<half, 1>(half*                output,\n                                                 float const*         qua_amax_ptr,\n                                                 __nv_fp8_e4m3 const* input,\n                                                 uint32_t             size,\n                                                 uint32_t             n,\n                                                 cudaStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<__nv_bfloat16, 1>(__nv_bfloat16*       output,\n                                                          float const*         qua_amax_ptr,\n                                                          __nv_fp8_e4m3 const* input,\n                                                          uint32_t             size,\n                                                          uint32_t             n,\n                                                          cudaStream_t         stream);\n\n// IDEA: bfloat162 computation ?\ntemplate<typename T1, typename T2, int QUANTIZE_MODE, int PACKED_SIZE>\n__global__ void LayerNorm(FP8LayerNormParam<T1, T2> param)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum     = 0.0f;\n    using PackedInType  = typename PackType<T2, PACKED_SIZE>::type;\n    using PackedOutType = typename PackType<T1, PACKED_SIZE>::type;\n    float local_outs[PACKED_SIZE];\n\n    float input_scalar  = 1.0f;\n    float output_scalar = 1.0f;\n    if (QUANTIZE_MODE == 0) {\n        // per channel\n        input_scalar = __ldg(param.input_deq_ptr + threadIdx.x);\n        // output_scalar = __ldg(param.output_qua_ptr + threadIdx.x);\n        output_scalar = __ldg(param.output_qua_ptr);  // must per tensor because it is quantize of input tensor of GEMM\n    }\n    else if (QUANTIZE_MODE == 1) {\n        // For per tensor quantization, assume x = input, s = input_scalar, x' = x * s\n        // Then Norm(x') = E[x'] / sqrt(V[x']).\n        // Because E[x'] = E[sx] = sE[x], V[X'] = V[sx] = s^2 * V[x]\n        // E[x'] / sqrt(V[x']) = (sE[x]) / sqrt(s^2 V[x]) = (sE[x]) / (s sqrt(V[x]))\n        // = E[x] / sqrt(V[x]) = Norm(x)\n        // So, we can skip the input_scalar to prevent the useless computation cost and memory\n        // cost. But suggest to add to flag to open/close to prevent issue due to precision.\n\n        // Besides, we can consdier to multiply the output_scalar into gamma and beta\n        // when loading the weight to prevent the additional computation coat and memory\n        // cost.\n\n        // input_scalar = __ldg(param.input_deq_ptr);\n        input_scalar  = 1.0f;  // We can skip the input scalar by above proof\n        output_scalar = __ldg(param.output_qua_ptr);\n    }\n\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n        const int    offset    = j * PACKED_SIZE * blockDim.x;\n        PackedInType packed_in = reinterpret_cast<const PackedInType*>(\n            &param.input[blockIdx.x * param.n + offset + threadIdx.x * PACKED_SIZE])[0];\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i++) {\n            local_outs[packed_i] = (float)(packed_in.array[packed_i]) * input_scalar;\n            local_sum += local_outs[packed_i];\n        }\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0)\n        s_mean = mean / param.n;\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i += 1) {\n            local_var_sum += (local_outs[packed_i] - s_mean) * (local_outs[packed_i] - s_mean);\n        }\n    }\n\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / param.n + 1e-6);\n    __syncthreads();\n\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n        const int     offset = j * PACKED_SIZE * blockDim.x;\n        PackedOutType packed_out;\n        PackedInType  packed_gamma =\n            reinterpret_cast<const PackedInType*>(&param.gamma[threadIdx.x * PACKED_SIZE + offset])[0];\n        PackedInType packed_beta =\n            reinterpret_cast<const PackedInType*>(&param.beta[threadIdx.x * PACKED_SIZE + offset])[0];\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i += 1) {\n            packed_out.array[packed_i] =\n                (T1)((((local_outs[packed_i] - s_mean) * s_variance * (float)(packed_gamma.array[packed_i])\n                       + (float)packed_beta.array[packed_i]))\n                     * output_scalar);\n        }\n        reinterpret_cast<PackedOutType*>(\n            &param.normed_output[blockIdx.x * param.n + offset + threadIdx.x * PACKED_SIZE])[0] = packed_out;\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\n__global__ void LayerNormE4M3x4(FP8LayerNormParam<T1, T2> param)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    __nv_bfloat162* input1     = (__nv_bfloat162*)(param.input + blockIdx.x * param.n + 4 * threadIdx.x);\n    __nv_bfloat162* input2     = (__nv_bfloat162*)(param.input + blockIdx.x * param.n + 4 * threadIdx.x + 2);\n    __nv_bfloat162  local_out1 = __nv_bfloat162(*input1);\n    __nv_bfloat162  local_out2 = __nv_bfloat162(*input2);\n    // float input_scalar;\n    // float output_scalar;\n\n    if (QUANTIZE_MODE == 0) {\n        // For per channel quantization.\n        local_out1.x = local_out1.x * (__nv_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 0);\n        local_out1.y = local_out1.y * (__nv_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 1);\n        local_out2.x = local_out2.x * (__nv_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 2);\n        local_out2.y = local_out2.y * (__nv_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 3);\n    }\n    else if (QUANTIZE_MODE == 1) {\n        // For per tensor quantization, assume x = input, s = input_scalar, x' = x * s\n        // Then Norm(x') = E[x'] / sqrt(V[x']).\n        // Because E[x'] = E[sx] = sE[x], V[X'] = V[sx] = s^2 * V[x]\n        // E[x'] / sqrt(V[x']) = (sE[x]) / sqrt(s^2 V[x]) = (sE[x]) / (s sqrt(V[x]))\n        // = E[x] / sqrt(V[x]) = Norm(x)\n        // So, we can skip the input_scalar to prevent the useless computation cost and memory\n        // cost. But suggest to add to flag to open/close to prevent issue due to precision.\n\n        // Besides, we can consdier to multiply the output_scalar into gamma and beta\n        // when loading the weight to prevent the additional computation coat and memory\n        // cost.\n\n        // __nv_bfloat16 input_scalar = __ldg(param.input_deq_ptr);\n        // __nv_bfloat16 input_scalar = 1.0f; // We can skip the input scalar by above proof\n        // local_out1.x = local_out1.x * input_scalar;\n        // local_out1.y = local_out1.y * input_scalar;\n        // local_out2.x = local_out2.x * input_scalar;\n        // local_out2.y = local_out2.y * input_scalar;\n    }\n\n    float local_sum = 0.0f;\n    local_sum       = (float)(local_out1.x + local_out1.y + local_out2.x + local_out2.y);\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0)\n        s_mean = mean / param.n;\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    float diff1         = (float)local_out1.x - s_mean;\n    float diff2         = (float)local_out1.y - s_mean;\n    float diff3         = (float)local_out2.y - s_mean;\n    float diff4         = (float)local_out2.y - s_mean;\n    local_var_sum += diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + diff4 * diff4;\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / param.n + 1e-6);\n    __syncthreads();\n\n    float output_scalar[4];\n    if (QUANTIZE_MODE == 0 && false) {  // must per tensor because it is quantize of input tensor of GEMM\n        // For per channel quantization.\n        output_scalar[0] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 0);\n        output_scalar[1] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 1);\n        output_scalar[2] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 2);\n        output_scalar[3] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 3);\n    }\n    else if (QUANTIZE_MODE == 1 || true) {\n        output_scalar[0] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[1] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[2] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[3] = (float)__ldg(param.output_qua_ptr);\n    }\n\n    __nv_bfloat162 result1;\n    __nv_bfloat162 result2;\n    result1.x = (__nv_bfloat16)((((float)local_out1.x - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 0]\n                                 + (float)param.beta[threadIdx.x * 4 + 0])\n                                * output_scalar[0]);\n    result1.y = (__nv_bfloat16)((((float)local_out1.y - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 1]\n                                 + (float)param.beta[threadIdx.x * 4 + 1])\n                                * output_scalar[1]);\n    result2.x = (__nv_bfloat16)((((float)local_out2.x - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 2]\n                                 + (float)param.beta[threadIdx.x * 4 + 2])\n                                * output_scalar[2]);\n    result2.y = (__nv_bfloat16)((((float)local_out2.y - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 3]\n                                 + (float)param.beta[threadIdx.x * 4 + 3])\n                                * output_scalar[3]);\n\n    __nv_fp8x4_e4m3  output_val = __nv_fp8x4_e4m3(result1, result2);\n    __nv_fp8x4_e4m3* output_ptr = (__nv_fp8x4_e4m3*)(param.normed_output + blockIdx.x * param.n);\n    output_ptr[threadIdx.x]     = output_val;\n}\n\n#define LN_KERNEL(PACKED_SIZE)                                                                                         \\\n    dim3 grid(param.m);                                                                                                \\\n    dim3 block(min(param.n / PACKED_SIZE, 1024));                                                                      \\\n    LayerNorm<T1, T2, QUANTIZE_MODE, PACKED_SIZE><<<grid, block, 0, param.stream>>>(param);\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeFP8LayerNorm(FP8LayerNormParam<T1, T2> param)\n{\n    assert(param.n % 2 == 0);\n    if (param.n % 8 == 0) {\n        LN_KERNEL(8);\n    }\n    else if (param.n % 4 == 0) {\n        LN_KERNEL(4);\n    }\n    else if (param.n % 2 == 0) {\n        LN_KERNEL(2);\n    }\n}\n\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, float, 0>(FP8LayerNormParam<__nv_fp8_e4m3, float> param);\n// template void invokeFP8LayerNorm<float, float, 0>(FP8LayerNormParam<float, float> param);\n// template void invokeFP8LayerNorm<half, half, 0>(FP8LayerNormParam<half, half> param);\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, half, 0>(FP8LayerNormParam<__nv_fp8_e4m3, half> param);\ntemplate void\ninvokeFP8LayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 0>(FP8LayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, float, 1>(FP8LayerNormParam<__nv_fp8_e4m3, float> param);\n// template void invokeFP8LayerNorm<float, float, 1>(FP8LayerNormParam<float, float> param);\n// template void invokeFP8LayerNorm<half, half, 1>(FP8LayerNormParam<half, half> param);\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, half, 1>(FP8LayerNormParam<__nv_fp8_e4m3, half> param);\n// template void\n// invokeFP8LayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 1>(FP8LayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8IOPostLayerNorm(T1*       normed_output,\n                                          const T1* input,\n                                          const T2* __restrict gamma,\n                                          const T2* __restrict beta,\n                                          const float* input_scalar,\n                                          const float* output_scalar,\n                                          int          m,\n                                          int          n)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        float local_out = (float)(input[blockIdx.x * n + i]);\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / n;\n        s_variance = rsqrtf((variance / n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(beta[i]);\n        normed_output[blockIdx.x * n + i] =\n            (T1)(((((float)input[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(gamma[i]) + beta_val)\n                 * (float)(__ldg(output_scalar)));\n        // (T1)((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(__ldg(&gamma[i])) + beta_val);\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8IOPostLayerNorm(GeneralFP8IOPostLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    /* should pay attention to the rsqrt precision*/\n    generalFP8IOPostLayerNorm<T1, T2><<<grid, block, 0, param.stream>>>(param.normed_output,\n                                                                        param.input,\n                                                                        param.gamma,\n                                                                        param.beta,\n                                                                        param.input_deq_ptr,\n                                                                        param.output_qua_ptr,\n                                                                        param.m,\n                                                                        param.n);  // For gpt-3\n}\n\ntemplate void invokeGeneralFP8IOPostLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 0>(\n    GeneralFP8IOPostLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate void invokeGeneralFP8IOPostLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 1>(\n    GeneralFP8IOPostLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8AddBiasResidualLayerNorm(const T2* __restrict input,\n                                                   const T2* __restrict gamma,\n                                                   const T2* __restrict beta,\n                                                   const T2* __restrict bias,\n                                                   T2*          output,\n                                                   T1*          norm_output,\n                                                   const float* input_scale,\n                                                   const float* output_scale,\n                                                   int          m,\n                                                   int          n)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float input_scale_val  = input_scale == nullptr ? 1.0f : __ldg(input_scale);\n    float output_scale_val = output_scale == nullptr ? 1.0f : __ldg(output_scale);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        float local_out = (float)(input[blockIdx.x * n + i]);\n        local_out += (float)(output[blockIdx.x * n + i]) * input_scale_val;\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out += (float)(bias[i]);\n        }\n        output[blockIdx.x * n + i] = (T2)local_out;\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / n;\n        s_variance = rsqrtf((variance / n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(beta[i]);\n        norm_output[blockIdx.x * n + i] =\n            (T1)(((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(gamma[i]) + beta_val)\n                 * output_scale_val);\n    }\n}\n\n// TODO: implement T2 = half2\ntemplate<>\n__global__ void generalFP8AddBiasResidualLayerNorm(const __nv_bfloat162_2_xy* __restrict input,\n                                                   const __nv_bfloat162_2_xy* __restrict gamma,\n                                                   const __nv_bfloat162_2_xy* __restrict beta,\n                                                   const __nv_bfloat162_2_xy* __restrict bias,\n                                                   __nv_bfloat162_2_xy* output,\n                                                   __nv_fp8x4_e4m3*     norm_output,\n                                                   const float*         input_scale,\n                                                   const float*         output_scale,\n                                                   int                  m,\n                                                   int                  n)\n{\n    using bf16_4 = __nv_bfloat162_2_xy;\n    using bf16_2 = __nv_bfloat162;\n    using fp8_4  = __nv_fp8x4_e4m3;\n    int tid      = threadIdx.x;\n    int real_n   = n * 4;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    bf16_2 input_scale_val = input_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(input_scale));\n    bf16_2 output_scale_val =\n        output_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(output_scale));\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        bf16_4 local_out = input[blockIdx.x * n + i];\n        local_out.x      = hadd2(hmul2(output[blockIdx.x * n + i].x, input_scale_val), local_out.x);\n        local_out.y      = hadd2(hmul2(output[blockIdx.x * n + i].y, input_scale_val), local_out.y);\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out.x = hadd2(local_out.x, bias[i].x);\n            local_out.y = hadd2(local_out.y, bias[i].y);\n        }\n        output[blockIdx.x * n + i] = local_out;\n        // NOTE: need float accum here, or low task accuracy (summarization)\n        local_sum += (float)(local_out.x.x) + (float)(local_out.x.y) + (float)(local_out.y.x) + (float)(local_out.y.y);\n        local_sum_sq += (float)local_out.x.x * (float)local_out.x.x + (float)local_out.y.x * (float)local_out.y.x\n                        + (float)local_out.x.y * (float)local_out.x.y + (float)local_out.y.y * (float)local_out.y.y;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / real_n;\n        s_variance = rsqrtf((variance / real_n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    bf16_2 s_mean_2 = cuda_cast<bf16_2>(s_mean);\n    bf16_2 s_var_2  = cuda_cast<bf16_2>(s_variance);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        bf16_4 norm_output_val;\n        norm_output_val.x =\n            hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i].x, s_mean_2), s_var_2), gamma[i].x), beta[i].x),\n                  output_scale_val);\n        norm_output_val.y =\n            hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i].y, s_mean_2), s_var_2), gamma[i].y), beta[i].y),\n                  output_scale_val);\n        norm_output[blockIdx.x * n + i] = fp8_4(norm_output_val.x, norm_output_val.y);\n    }\n}\n\n// TODO: implement T2 = half2\ntemplate<>\n__global__ void generalFP8AddBiasResidualLayerNorm(const __nv_bfloat162* __restrict input,\n                                                   const __nv_bfloat162* __restrict gamma,\n                                                   const __nv_bfloat162* __restrict beta,\n                                                   const __nv_bfloat162* __restrict bias,\n                                                   __nv_bfloat162*  output,\n                                                   __nv_fp8x2_e4m3* norm_output,\n                                                   const float*     input_scale,\n                                                   const float*     output_scale,\n                                                   int              m,\n                                                   int              n)\n{\n    using bf16_2 = __nv_bfloat162;\n    using fp8_2  = __nv_fp8x2_e4m3;\n    int tid      = threadIdx.x;\n    int real_n   = n * 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    bf16_2 input_scale_val = input_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(input_scale));\n    bf16_2 output_scale_val =\n        output_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(output_scale));\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        bf16_2 local_out = input[blockIdx.x * n + i];\n        local_out        = hadd2(hmul2(output[blockIdx.x * n + i], input_scale_val), local_out);\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out = hadd2(local_out, bias[i]);\n        }\n        output[blockIdx.x * n + i] = local_out;\n        local_sum += (float)(local_out.x) + (float)(local_out.y);\n        local_sum_sq += (float)local_out.x * (float)local_out.x + (float)local_out.y * (float)local_out.y;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / real_n;\n        s_variance = rsqrtf((variance / real_n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    bf16_2 s_mean_2 = cuda_cast<bf16_2>(s_mean);\n    bf16_2 s_var_2  = cuda_cast<bf16_2>(s_variance);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        norm_output[blockIdx.x * n + i] =\n            fp8_2(hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i], s_mean_2), s_var_2), gamma[i]), beta[i]),\n                        output_scale_val));\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8AddBiasResidualPreLayerNorm(GeneralFP8AddBiasResidualPreLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n    // NOTE: only T2 = bfloat supported yet\n    if (param.n % 4 == 0) {\n        dim3 block(min(param.n / 4, 1024));\n        generalFP8AddBiasResidualLayerNorm<<<grid, block, 0, param.stream>>>((const __nv_bfloat162_2_xy*)param.residual,\n                                                                             (const __nv_bfloat162_2_xy*)param.gamma,\n                                                                             (const __nv_bfloat162_2_xy*)param.beta,\n                                                                             (const __nv_bfloat162_2_xy*)param.bias,\n                                                                             (__nv_bfloat162_2_xy*)param.output,\n                                                                             (__nv_fp8x4_e4m3*)param.normed_output,\n                                                                             param.input_deq_ptr,\n                                                                             param.output_qua_ptr,\n                                                                             param.m,\n                                                                             param.n / 4);  // For gpt-3\n    }\n    else if (param.n % 2 == 0) {\n        dim3 block(min(param.n / 2, 1024));\n        generalFP8AddBiasResidualLayerNorm<<<grid, block, 0, param.stream>>>((const __nv_bfloat162*)param.residual,\n                                                                             (const __nv_bfloat162*)param.gamma,\n                                                                             (const __nv_bfloat162*)param.beta,\n                                                                             (const __nv_bfloat162*)param.bias,\n                                                                             (__nv_bfloat162*)param.output,\n                                                                             (__nv_fp8x2_e4m3*)param.normed_output,\n                                                                             param.input_deq_ptr,\n                                                                             param.output_qua_ptr,\n                                                                             param.m,\n                                                                             param.n / 2);  // For gpt-3\n    }\n    else {\n        dim3 block(min(param.n, 1024));\n        if (param.n % 32 != 0) {\n            block.x = 1024;\n        }\n        // const int vec_n = 4 / sizeof(T2);\n        // block.x = block.x / vec_n; // We don't need this setting because we don't have bfloat162 implementation now\n\n        /* should pay attention to the rsqrt precision*/\n        generalFP8AddBiasResidualLayerNorm<T1, T2><<<grid, block, 0, param.stream>>>(param.residual,\n                                                                                     param.gamma,\n                                                                                     param.beta,\n                                                                                     param.bias,\n                                                                                     param.output,\n                                                                                     param.normed_output,\n                                                                                     param.input_deq_ptr,\n                                                                                     param.output_qua_ptr,\n                                                                                     param.m,\n                                                                                     param.n);  // For gpt-3\n    }\n    return;\n}\n\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, float, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<float, float, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<float, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<half, half, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<half, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, half, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, float, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<float, float, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<float, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<half, half, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<half, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, half, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV1(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float input_scalar;\n    float output_scalar;\n    if (QUANTIZE_MODE == 0) {\n        // per channel\n        input_scalar  = __ldg(param.input_scale + threadIdx.x);\n        output_scalar = __ldg(param.output_scale);  // must per tensor because it is quantize of input tensor of GEMM\n    }\n    else if (QUANTIZE_MODE == 1) {\n        input_scalar  = __ldg(param.input_scale);\n        output_scalar = __ldg(param.output_scale);\n    }\n    else if (QUANTIZE_MODE == QUANTIZE_MODE::PER_CHANNEL_WEIGHT_PER_TENSOR_ACT) {\n        input_scalar = __ldg(param.input_scale) * __ldg(param.input_scale_2 + threadIdx.x)\n                       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n        output_scalar = __ldg(param.output_scale);\n    }\n\n    for (int i = tid; i < param.n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&param.input[blockIdx.x * param.n + i])) * input_scalar;\n        float local_out = (float)(param.input[blockIdx.x * param.n + i]) * input_scalar;\n        local_out       = local_out + (float)(param.residual[blockIdx.x * param.n + i]) * __ldg(param.residual_scale);\n        if (param.bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out += (float)(param.bias[i]);\n        }\n\n        param.normed_output[blockIdx.x * param.n + i] = (T1)local_out;  // TODO This conversion has bug\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / param.n;\n        s_variance = rsqrtf(sums[1] / param.n - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < param.n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (param.beta == nullptr) ? 0.0f : (float)(param.beta[i]);\n        param.normed_output[blockIdx.x * param.n + i] =\n            (T1)(((((float)param.normed_output[blockIdx.x * param.n + i] - s_mean) * s_variance)\n                      * (float)(param.gamma[i])\n                  + beta_val)\n                 * output_scalar);\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV2(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    using T1_4 = __nv_fp8x4_e4m3;\n    using T2_2 = typename TypeConverter<T2>::Type;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 input_scalar    = cuda_cast<T2_2>(__ldg(param.input_scale));\n    T2_2 output_scalar   = cuda_cast<T2_2>(__ldg(param.output_scale));\n    T2_2 residual_scalar = cuda_cast<T2_2>(__ldg(param.residual_scale));\n\n    const int n = param.n / 4;\n\n    T1_4* input_ptr         = (T1_4*)(param.input);\n    T1_4* residual_ptr      = (T1_4*)(param.residual);\n    T1_4* normed_output_ptr = (T1_4*)(param.normed_output);\n    T2_2* bias_ptr          = (T2_2*)(param.bias);\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    T2_2 local_outs[2];\n\n    T2_2      val_0, val_1;\n    const int index_0 = 2 * threadIdx.x;\n    const int index_1 = index_0 + 1;\n\n    fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &input_ptr[blockIdx.x * n + threadIdx.x]);\n    val_0         = hmul2(val_0, input_scalar);\n    val_1         = hmul2(val_1, input_scalar);\n    local_outs[0] = val_0;\n    local_outs[1] = val_1;\n\n    fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &residual_ptr[blockIdx.x * n + threadIdx.x]);\n    val_0 = hmul2(val_0, residual_scalar);\n    val_1 = hmul2(val_1, residual_scalar);\n    local_outs[0] += val_0;\n    local_outs[1] += val_1;\n\n    if (bias_ptr != nullptr) {\n        local_outs[0] = hadd2(local_outs[0], bias_ptr[index_0]);\n        local_outs[1] = hadd2(local_outs[1], bias_ptr[index_1]);\n    }\n\n    local_sum += (float)(local_outs[0].x + local_outs[0].y + local_outs[1].x + local_outs[1].y);\n    local_sum_sq += (float)local_outs[0].x * (float)local_outs[0].x + (float)local_outs[0].y * (float)local_outs[0].y\n                    + (float)local_outs[1].x * (float)local_outs[1].x + (float)local_outs[1].y * (float)local_outs[1].y;\n\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / param.n;\n        s_variance = rsqrtf(sums[1] / param.n - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(s_mean);\n    T2_2 s_var_2  = cuda_cast<T2_2>(s_variance);\n\n    // {[(x - mean) * var * gamma] + beta} * output_scale\n    local_outs[0] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[0], s_mean_2), s_var_2), gamma_ptr[index_0]), beta_ptr[index_0]),\n              output_scalar);\n    local_outs[1] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[1], s_mean_2), s_var_2), gamma_ptr[index_1]), beta_ptr[index_1]),\n              output_scalar);\n\n    normed_output_ptr[blockIdx.x * n + threadIdx.x] = T1_4(local_outs[0], local_outs[1]);\n}\n\ntemplate<typename T1, typename T2, int ELEMENT_PER_THREAD, int WARP_NUM>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV3(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    // Each warp handle one row. So, we can save the cost of sync of block.\n    // But when param.m is small, the launched blocks are too small the performance is worse\n    // than V2.\n    using T1_4 = __nv_fp8x4_e4m3;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 input_scalar    = cuda_cast<T2_2>(__ldg(param.input_scale));\n    T2_2 output_scalar   = cuda_cast<T2_2>(__ldg(param.output_scale));\n    T2_2 residual_scalar = cuda_cast<T2_2>(__ldg(param.residual_scale));\n\n    const int n = param.n / 4;\n\n    T1_4* input_ptr         = (T1_4*)(param.input);\n    T1_4* residual_ptr      = (T1_4*)(param.residual);\n    T1_4* normed_output_ptr = (T1_4*)(param.normed_output);\n    T2_2* bias_ptr          = (T2_2*)(param.bias);\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    T2_2 local_outs[ELEMENT_PER_THREAD][2];\n\n    const int row_id = blockIdx.x * blockDim.y + threadIdx.y;\n    if (row_id > param.m) {\n        return;\n    }\n\n    T2_2 val_0, val_1;\n\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n\n        fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &input_ptr[row_id * n + i * blockDim.x + threadIdx.x]);\n        val_0            = hmul2(val_0, input_scalar);\n        val_1            = hmul2(val_1, input_scalar);\n        local_outs[i][0] = val_0;\n        local_outs[i][1] = val_1;\n\n        fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &residual_ptr[row_id * n + i * blockDim.x + threadIdx.x]);\n        val_0 = hmul2(val_0, residual_scalar);\n        val_1 = hmul2(val_1, residual_scalar);\n        local_outs[i][0] += val_0;\n        local_outs[i][1] += val_1;\n\n        if (bias_ptr != nullptr) {\n            local_outs[i][0] = hadd2(local_outs[i][0], __ldg(bias_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0));\n            local_outs[i][1] = hadd2(local_outs[i][1], __ldg(bias_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1));\n        }\n\n        val_0 = hadd2(local_outs[i][0], local_outs[i][1]);\n        local_sum += (float)(val_0.x + val_0.y);\n        val_0 = hmul2(local_outs[i][0], local_outs[i][0]);\n        val_1 = hmul2(local_outs[i][1], local_outs[i][1]);\n        val_1 = hadd2(val_1, val_0);\n        local_sum_sq += (float)(val_1.x + val_1.y);\n    }\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    warpReduceSumV2<float, 2>(sums);\n\n    sums[0] = sums[0] / (float)(param.n);\n    sums[1] = rsqrtf(sums[1] / (float)(param.n) - sums[0] * sums[0] + 1e-12f);\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(sums[0]);\n    T2_2 s_var_2  = cuda_cast<T2_2>(sums[1]);\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n\n        // {[(x - mean) * var * gamma] + beta} * output_scale\n        local_outs[i][0] = hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[i][0], s_mean_2), s_var_2),\n                                             ldg(gamma_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0)),\n                                       ldg(beta_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0)),\n                                 output_scalar);\n        local_outs[i][1] = hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[i][1], s_mean_2), s_var_2),\n                                             ldg(gamma_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1)),\n                                       ldg(beta_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1)),\n                                 output_scalar);\n        normed_output_ptr[row_id * n + i * blockDim.x + threadIdx.x] = T1_4(local_outs[i][0], local_outs[i][1]);\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8IOAddBiasResidualPostLayerNorm(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n    FT_CHECK(param.n <= 1024);\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    if (param.n % 4 == 0) {\n        // TODO (bhsueh) check the condition here\n        if (param.m > 1024) {\n            block.x            = 32;\n            const int WARP_NUM = 8;\n            block.y            = WARP_NUM;\n            grid.x             = (grid.x + (WARP_NUM - 1)) / WARP_NUM;\n            if (param.n == 1024) {\n                generalFP8IOAddBiasResidualPostLayerNormV3<T1, T2, 8, WARP_NUM>\n                    <<<grid, block, 0, param.stream>>>(param);\n            }\n            else if (param.n == 768) {\n                generalFP8IOAddBiasResidualPostLayerNormV3<T1, T2, 6, WARP_NUM>\n                    <<<grid, block, 0, param.stream>>>(param);\n            }\n        }\n        else {\n            block.x /= 4;\n            generalFP8IOAddBiasResidualPostLayerNormV2<T1, T2><<<grid, block, 0, param.stream>>>(param);\n        }\n    }\n    else {\n        generalFP8IOAddBiasResidualPostLayerNormV1<T1, T2, QUANTIZE_MODE><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\ntemplate void invokeGeneralFP8IOAddBiasResidualPostLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, PER_CHANNEL>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate void invokeGeneralFP8IOAddBiasResidualPostLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, PER_TENSOR>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate void\ninvokeGeneralFP8IOAddBiasResidualPostLayerNorm<__nv_fp8_e4m3, __nv_bfloat16, PER_CHANNEL_WEIGHT_PER_TENSOR_ACT>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2, int ELEMENT_PER_THREAD>\n__global__ void removePaddingEmbLookupLayerNormFP8Out(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    float local_outs[ELEMENT_PER_THREAD];\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float output_scalar = __ldg(param.output_scale);\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        int index      = blockDim.x * i + threadIdx.x;\n        int padded_row = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n        int position_id =\n            param.position_ids == nullptr ? padded_row % param.max_seq_len : param.position_ids[padded_row];\n        int   token_type_id = param.token_type_ids == nullptr ? 0 : param.token_type_ids[padded_row];\n        int   input_id      = param.input_ids[padded_row];\n        float local_out     = (input_id == 0 ? 0.0f : (float)param.word_embeddings[input_id * param.n + index])\n                          + (float)param.position_embeddings[position_id * param.n + index]\n                          + (float)param.token_type_embeddings[token_type_id * param.n + index];\n\n        local_outs[i] = local_out;\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2] = {0.0f};\n    sums[0]       = local_sum;\n    sums[1]       = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / ((float)param.n);\n        s_variance = rsqrtf(sums[1] / ((float)param.n) - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        int   index    = blockDim.x * i + threadIdx.x;\n        float beta_val = (param.beta == nullptr) ? 0.0f : (float)(param.beta[index]);\n        param.normed_output[blockIdx.x * param.n + index] =\n            (T1)((((local_outs[i] - s_mean) * s_variance) * (float)(param.gamma[index]) + beta_val) * output_scalar);\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void removePaddingEmbLookupLayerNormFP8OutV2(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    using T1_4 = __nv_fp8x4_e4m3;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    T2_2 local_outs[2];\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 output_scalar = cuda_cast<T2_2>(__ldg(param.output_scale));\n\n    T2_2* word_emb_ptr      = (T2_2*)param.word_embeddings;\n    T2_2* pos_emb_ptr       = (T2_2*)param.position_embeddings;\n    T2_2* type_emb_ptr      = (T2_2*)param.token_type_embeddings;\n    T1_4* normed_output_ptr = (T1_4*)param.normed_output;\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    int n_div_2 = param.n / 2;\n    int n_div_4 = param.n / 4;\n\n    const int index_0 = 2 * threadIdx.x;\n    const int index_1 = index_0 + 1;\n\n    int padded_row    = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n    int position_id   = param.position_ids == nullptr ? padded_row % param.max_seq_len : param.position_ids[padded_row];\n    int token_type_id = param.token_type_ids == nullptr ? 0 : param.token_type_ids[padded_row];\n    int input_id      = param.input_ids[padded_row];\n\n    local_outs[0] = (input_id == 0 ? cuda_cast<T2_2>(0.0f) : word_emb_ptr[input_id * n_div_2 + index_0])\n                    + pos_emb_ptr[position_id * n_div_2 + index_0] + type_emb_ptr[token_type_id * n_div_2 + index_0];\n    local_outs[1] = (input_id == 0 ? cuda_cast<T2_2>(0.0f) : word_emb_ptr[input_id * n_div_2 + index_1])\n                    + pos_emb_ptr[position_id * n_div_2 + index_1] + type_emb_ptr[token_type_id * n_div_2 + index_1];\n\n    local_sum += (float)(local_outs[0].x + local_outs[0].y + local_outs[1].x + local_outs[1].y);\n    local_sum_sq += (float)local_outs[0].x * (float)local_outs[0].x + (float)local_outs[0].y * (float)local_outs[0].y\n                    + (float)local_outs[1].x * (float)local_outs[1].x + (float)local_outs[1].y * (float)local_outs[1].y;\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2] = {0.0f};\n    sums[0]       = local_sum;\n    sums[1]       = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / ((float)param.n);\n        s_variance = rsqrtf(sums[1] / ((float)param.n) - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(s_mean);\n    T2_2 s_var_2  = cuda_cast<T2_2>(s_variance);\n\n    // {[(x - mean) * var * gamma] + beta} * output_scale\n    local_outs[0] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[0], s_mean_2), s_var_2), gamma_ptr[index_0]), beta_ptr[index_0]),\n              output_scalar);\n    local_outs[1] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[1], s_mean_2), s_var_2), gamma_ptr[index_1]), beta_ptr[index_1]),\n              output_scalar);\n\n    normed_output_ptr[blockIdx.x * n_div_4 + threadIdx.x] = T1_4(local_outs[0], local_outs[1]);\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeRemovePaddingEmbLookupLayerNormFP8Out(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n    FT_CHECK(param.n <= 1024);\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    if (param.n <= 1024) {\n        if (block.x % 4 == 0) {\n            block.x /= 4;\n            removePaddingEmbLookupLayerNormFP8OutV2<T1, T2><<<grid, block, 0, param.stream>>>(param);\n        }\n        else {\n            removePaddingEmbLookupLayerNormFP8Out<T1, T2, 1><<<grid, block, 0, param.stream>>>(param);\n        }\n    }\n}\n\ntemplate void invokeRemovePaddingEmbLookupLayerNormFP8Out<__nv_fp8_e4m3, float>(\n    RemovePaddingEmbLookupLayerNormFP8OutParam<__nv_fp8_e4m3, float> param);\ntemplate void invokeRemovePaddingEmbLookupLayerNormFP8Out<__nv_fp8_e4m3, __nv_bfloat16>(\n    RemovePaddingEmbLookupLayerNormFP8OutParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/layernorm_fp8_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_bf16_wrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <hip/hip_fp16.h>\n\nnamespace fastertransformer {\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\nquatizeVectorE4M3(__hip_fp8_e4m3_fnuz* output, float const* input_qua_amax_ptr, T const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = __hip_fp8_e4m3_fnuz((float)(input[i]) / __ldg(input_qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = __hip_fp8_e4m3_fnuz((float)(input[i]) / __ldg(input_qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeQuatizeVectorE4M3(__hip_fp8_e4m3_fnuz* output,\n                             float const*   input_qua_amax_ptr,\n                             T const*       input,\n                             uint32_t       size,\n                             uint32_t       n,\n                             hipStream_t   stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    quatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, input_qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeQuatizeVectorE4M3<float, 0>(__hip_fp8_e4m3_fnuz* output,\n                                                float const*   input_qua_amax_ptr,\n                                                float const*   input,\n                                                uint32_t       size,\n                                                uint32_t       n,\n                                                hipStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<half, 0>(__hip_fp8_e4m3_fnuz* output,\n                                               float const*   input_qua_amax_ptr,\n                                               half const*    input,\n                                               uint32_t       size,\n                                               uint32_t       n,\n                                               hipStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<__hip_bfloat16, 0>(__hip_fp8_e4m3_fnuz*       output,\n                                                        float const*         input_qua_amax_ptr,\n                                                        __hip_bfloat16 const* input,\n                                                        uint32_t             size,\n                                                        uint32_t             n,\n                                                        hipStream_t         stream);\n\ntemplate void invokeQuatizeVectorE4M3<float, 1>(__hip_fp8_e4m3_fnuz* output,\n                                                float const*   input_qua_amax_ptr,\n                                                float const*   input,\n                                                uint32_t       size,\n                                                uint32_t       n,\n                                                hipStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<half, 1>(__hip_fp8_e4m3_fnuz* output,\n                                               float const*   input_qua_amax_ptr,\n                                               half const*    input,\n                                               uint32_t       size,\n                                               uint32_t       n,\n                                               hipStream_t   stream);\ntemplate void invokeQuatizeVectorE4M3<__hip_bfloat16, 1>(__hip_fp8_e4m3_fnuz*       output,\n                                                        float const*         input_qua_amax_ptr,\n                                                        __hip_bfloat16 const* input,\n                                                        uint32_t             size,\n                                                        uint32_t             n,\n                                                        hipStream_t         stream);\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\ndequatizeVectorE4M3(T* output, float const* qua_amax_ptr, __hip_fp8_e4m3_fnuz const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = float(input[i]) * __ldg(qua_amax_ptr + (i % n));\n        }\n        else {\n            output[i] = float(input[i]) * __ldg(qua_amax_ptr);\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeDequatizeVectorE4M3(\n    T* output, float const* qua_amax_ptr, __hip_fp8_e4m3_fnuz const* input, uint32_t size, uint32_t n, hipStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    dequatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeDequatizeVectorE4M3<float, 0>(float*               output,\n                                                  float const*         qua_amax_ptr,\n                                                  __hip_fp8_e4m3_fnuz const* input,\n                                                  uint32_t             size,\n                                                  uint32_t             n,\n                                                  hipStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<half, 0>(half*                output,\n                                                 float const*         qua_amax_ptr,\n                                                 __hip_fp8_e4m3_fnuz const* input,\n                                                 uint32_t             size,\n                                                 uint32_t             n,\n                                                 hipStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<__hip_bfloat16, 0>(__hip_bfloat16*       output,\n                                                          float const*         qua_amax_ptr,\n                                                          __hip_fp8_e4m3_fnuz const* input,\n                                                          uint32_t             size,\n                                                          uint32_t             n,\n                                                          hipStream_t         stream);\n\ntemplate void invokeDequatizeVectorE4M3<float, 1>(float*               output,\n                                                  float const*         qua_amax_ptr,\n                                                  __hip_fp8_e4m3_fnuz const* input,\n                                                  uint32_t             size,\n                                                  uint32_t             n,\n                                                  hipStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<half, 1>(half*                output,\n                                                 float const*         qua_amax_ptr,\n                                                 __hip_fp8_e4m3_fnuz const* input,\n                                                 uint32_t             size,\n                                                 uint32_t             n,\n                                                 hipStream_t         stream);\ntemplate void invokeDequatizeVectorE4M3<__hip_bfloat16, 1>(__hip_bfloat16*       output,\n                                                          float const*         qua_amax_ptr,\n                                                          __hip_fp8_e4m3_fnuz const* input,\n                                                          uint32_t             size,\n                                                          uint32_t             n,\n                                                          hipStream_t         stream);\n\n// IDEA: bfloat162 computation ?\ntemplate<typename T1, typename T2, int QUANTIZE_MODE, int PACKED_SIZE>\n__global__ void LayerNorm(FP8LayerNormParam<T1, T2> param)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum     = 0.0f;\n    using PackedInType  = typename PackType<T2, PACKED_SIZE>::type;\n    using PackedOutType = typename PackType<T1, PACKED_SIZE>::type;\n    float local_outs[PACKED_SIZE];\n\n    float input_scalar  = 1.0f;\n    float output_scalar = 1.0f;\n    if (QUANTIZE_MODE == 0) {\n        // per channel\n        input_scalar = __ldg(param.input_deq_ptr + threadIdx.x);\n        // output_scalar = __ldg(param.output_qua_ptr + threadIdx.x);\n        output_scalar = __ldg(param.output_qua_ptr);  // must per tensor because it is quantize of input tensor of GEMM\n    }\n    else if (QUANTIZE_MODE == 1) {\n        // For per tensor quantization, assume x = input, s = input_scalar, x' = x * s\n        // Then Norm(x') = E[x'] / sqrt(V[x']).\n        // Because E[x'] = E[sx] = sE[x], V[X'] = V[sx] = s^2 * V[x]\n        // E[x'] / sqrt(V[x']) = (sE[x]) / sqrt(s^2 V[x]) = (sE[x]) / (s sqrt(V[x]))\n        // = E[x] / sqrt(V[x]) = Norm(x)\n        // So, we can skip the input_scalar to prevent the useless computation cost and memory\n        // cost. But suggest to add to flag to open/close to prevent issue due to precision.\n\n        // Besides, we can consdier to multiply the output_scalar into gamma and beta\n        // when loading the weight to prevent the additional computation coat and memory\n        // cost.\n\n        // input_scalar = __ldg(param.input_deq_ptr);\n        input_scalar  = 1.0f;  // We can skip the input scalar by above proof\n        output_scalar = __ldg(param.output_qua_ptr);\n    }\n\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n        const int    offset    = j * PACKED_SIZE * blockDim.x;\n        PackedInType packed_in = reinterpret_cast<const PackedInType*>(\n            &param.input[blockIdx.x * param.n + offset + threadIdx.x * PACKED_SIZE])[0];\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i++) {\n            local_outs[packed_i] = (float)(packed_in.array[packed_i]) * input_scalar;\n            local_sum += local_outs[packed_i];\n        }\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0)\n        s_mean = mean / param.n;\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i += 1) {\n            local_var_sum += (local_outs[packed_i] - s_mean) * (local_outs[packed_i] - s_mean);\n        }\n    }\n\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / param.n + 1e-6);\n    __syncthreads();\n\n    for (int j = 0; threadIdx.x * PACKED_SIZE + j * PACKED_SIZE * blockDim.x < param.n; j++) {\n        const int     offset = j * PACKED_SIZE * blockDim.x;\n        PackedOutType packed_out;\n        PackedInType  packed_gamma =\n            reinterpret_cast<const PackedInType*>(&param.gamma[threadIdx.x * PACKED_SIZE + offset])[0];\n        PackedInType packed_beta =\n            reinterpret_cast<const PackedInType*>(&param.beta[threadIdx.x * PACKED_SIZE + offset])[0];\n#pragma unroll\n        for (int packed_i = 0; packed_i < PACKED_SIZE; packed_i += 1) {\n            packed_out.array[packed_i] =\n                (T1)((((local_outs[packed_i] - s_mean) * s_variance * (float)(packed_gamma.array[packed_i])\n                       + (float)packed_beta.array[packed_i]))\n                     * output_scalar);\n        }\n        reinterpret_cast<PackedOutType*>(\n            &param.normed_output[blockIdx.x * param.n + offset + threadIdx.x * PACKED_SIZE])[0] = packed_out;\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\n__global__ void LayerNormE4M3x4(FP8LayerNormParam<T1, T2> param)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    __hip_bfloat162* input1     = (__hip_bfloat162*)(param.input + blockIdx.x * param.n + 4 * threadIdx.x);\n    __hip_bfloat162* input2     = (__hip_bfloat162*)(param.input + blockIdx.x * param.n + 4 * threadIdx.x + 2);\n    __hip_bfloat162  local_out1 = __hip_bfloat162(*input1);\n    __hip_bfloat162  local_out2 = __hip_bfloat162(*input2);\n    // float input_scalar;\n    // float output_scalar;\n\n    if (QUANTIZE_MODE == 0) {\n        // For per channel quantization.\n        local_out1.x = local_out1.x * (__hip_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 0);\n        local_out1.y = local_out1.y * (__hip_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 1);\n        local_out2.x = local_out2.x * (__hip_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 2);\n        local_out2.y = local_out2.y * (__hip_bfloat16)__ldg(param.input_deq_ptr + threadIdx.x * 4 + 3);\n    }\n    else if (QUANTIZE_MODE == 1) {\n        // For per tensor quantization, assume x = input, s = input_scalar, x' = x * s\n        // Then Norm(x') = E[x'] / sqrt(V[x']).\n        // Because E[x'] = E[sx] = sE[x], V[X'] = V[sx] = s^2 * V[x]\n        // E[x'] / sqrt(V[x']) = (sE[x]) / sqrt(s^2 V[x]) = (sE[x]) / (s sqrt(V[x]))\n        // = E[x] / sqrt(V[x]) = Norm(x)\n        // So, we can skip the input_scalar to prevent the useless computation cost and memory\n        // cost. But suggest to add to flag to open/close to prevent issue due to precision.\n\n        // Besides, we can consdier to multiply the output_scalar into gamma and beta\n        // when loading the weight to prevent the additional computation coat and memory\n        // cost.\n\n        // __nv_bfloat16 input_scalar = __ldg(param.input_deq_ptr);\n        // __nv_bfloat16 input_scalar = 1.0f; // We can skip the input scalar by above proof\n        // local_out1.x = local_out1.x * input_scalar;\n        // local_out1.y = local_out1.y * input_scalar;\n        // local_out2.x = local_out2.x * input_scalar;\n        // local_out2.y = local_out2.y * input_scalar;\n    }\n\n    float local_sum = 0.0f;\n    local_sum       = (float)(local_out1.x + local_out1.y + local_out2.x + local_out2.y);\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0)\n        s_mean = mean / param.n;\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    float diff1         = (float)local_out1.x - s_mean;\n    float diff2         = (float)local_out1.y - s_mean;\n    float diff3         = (float)local_out2.y - s_mean;\n    float diff4         = (float)local_out2.y - s_mean;\n    local_var_sum += diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + diff4 * diff4;\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / param.n + 1e-6);\n    __syncthreads();\n\n    float output_scalar[4];\n    if (QUANTIZE_MODE == 0 && false) {  // must per tensor because it is quantize of input tensor of GEMM\n        // For per channel quantization.\n        output_scalar[0] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 0);\n        output_scalar[1] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 1);\n        output_scalar[2] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 2);\n        output_scalar[3] = (float)__ldg(param.output_qua_ptr + threadIdx.x * 4 + 3);\n    }\n    else if (QUANTIZE_MODE == 1 || true) {\n        output_scalar[0] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[1] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[2] = (float)__ldg(param.output_qua_ptr);\n        output_scalar[3] = (float)__ldg(param.output_qua_ptr);\n    }\n\n    __hip_bfloat162 result1;\n    __hip_bfloat162 result2;\n    result1.x = (__hip_bfloat16)((((float)local_out1.x - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 0]\n                                 + (float)param.beta[threadIdx.x * 4 + 0])\n                                * output_scalar[0]);\n    result1.y = (__hip_bfloat16)((((float)local_out1.y - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 1]\n                                 + (float)param.beta[threadIdx.x * 4 + 1])\n                                * output_scalar[1]);\n    result2.x = (__hip_bfloat16)((((float)local_out2.x - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 2]\n                                 + (float)param.beta[threadIdx.x * 4 + 2])\n                                * output_scalar[2]);\n    result2.y = (__hip_bfloat16)((((float)local_out2.y - s_mean) * s_variance * (float)param.gamma[threadIdx.x * 4 + 3]\n                                 + (float)param.beta[threadIdx.x * 4 + 3])\n                                * output_scalar[3]);\n\n    __hip_fp8x4_e4m3_fnuz  output_val = __hip_fp8x4_e4m3_fnuz(result1, result2);\n    __hip_fp8x4_e4m3_fnuz* output_ptr = (__hip_fp8x4_e4m3_fnuz*)(param.normed_output + blockIdx.x * param.n);\n    output_ptr[threadIdx.x]     = output_val;\n}\n\n#define LN_KERNEL(PACKED_SIZE)                                                                                         \\\n    dim3 grid(param.m);                                                                                                \\\n    dim3 block(min(param.n / PACKED_SIZE, 1024));                                                                      \\\n    LayerNorm<T1, T2, QUANTIZE_MODE, PACKED_SIZE><<<grid, block, 0, param.stream>>>(param);\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeFP8LayerNorm(FP8LayerNormParam<T1, T2> param)\n{\n    assert(param.n % 2 == 0);\n    if (param.n % 8 == 0) {\n        LN_KERNEL(8);\n    }\n    else if (param.n % 4 == 0) {\n        LN_KERNEL(4);\n    }\n    else if (param.n % 2 == 0) {\n        LN_KERNEL(2);\n    }\n}\n\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, float, 0>(FP8LayerNormParam<__nv_fp8_e4m3, float> param);\n// template void invokeFP8LayerNorm<float, float, 0>(FP8LayerNormParam<float, float> param);\n// template void invokeFP8LayerNorm<half, half, 0>(FP8LayerNormParam<half, half> param);\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, half, 0>(FP8LayerNormParam<__nv_fp8_e4m3, half> param);\ntemplate void\ninvokeFP8LayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, 0>(FP8LayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, float, 1>(FP8LayerNormParam<__nv_fp8_e4m3, float> param);\n// template void invokeFP8LayerNorm<float, float, 1>(FP8LayerNormParam<float, float> param);\n// template void invokeFP8LayerNorm<half, half, 1>(FP8LayerNormParam<half, half> param);\n// template void invokeFP8LayerNorm<__nv_fp8_e4m3, half, 1>(FP8LayerNormParam<__nv_fp8_e4m3, half> param);\n// template void\n// invokeFP8LayerNorm<__nv_fp8_e4m3, __nv_bfloat16, 1>(FP8LayerNormParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8IOPostLayerNorm(T1*       normed_output,\n                                          const T1* input,\n                                          const T2* __restrict gamma,\n                                          const T2* __restrict beta,\n                                          const float* input_scalar,\n                                          const float* output_scalar,\n                                          int          m,\n                                          int          n)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        float local_out = (float)(input[blockIdx.x * n + i]);\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / n;\n        s_variance = rsqrtf((variance / n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(beta[i]);\n        normed_output[blockIdx.x * n + i] =\n            (T1)(((((float)input[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(gamma[i]) + beta_val)\n                 * (float)(__ldg(output_scalar)));\n        // (T1)((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(__ldg(&gamma[i])) + beta_val);\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8IOPostLayerNorm(GeneralFP8IOPostLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    /* should pay attention to the rsqrt precision*/\n    generalFP8IOPostLayerNorm<T1, T2><<<grid, block, 0, param.stream>>>(param.normed_output,\n                                                                        param.input,\n                                                                        param.gamma,\n                                                                        param.beta,\n                                                                        param.input_deq_ptr,\n                                                                        param.output_qua_ptr,\n                                                                        param.m,\n                                                                        param.n);  // For gpt-3\n}\n\ntemplate void invokeGeneralFP8IOPostLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, 0>(\n    GeneralFP8IOPostLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate void invokeGeneralFP8IOPostLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, 1>(\n    GeneralFP8IOPostLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8AddBiasResidualLayerNorm(const T2* __restrict input,\n                                                   const T2* __restrict gamma,\n                                                   const T2* __restrict beta,\n                                                   const T2* __restrict bias,\n                                                   T2*          output,\n                                                   T1*          norm_output,\n                                                   const float* input_scale,\n                                                   const float* output_scale,\n                                                   int          m,\n                                                   int          n)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float input_scale_val  = input_scale == nullptr ? 1.0f : __ldg(input_scale);\n    float output_scale_val = output_scale == nullptr ? 1.0f : __ldg(output_scale);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        float local_out = (float)(input[blockIdx.x * n + i]);\n        local_out += (float)(output[blockIdx.x * n + i]) * input_scale_val;\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out += (float)(bias[i]);\n        }\n        output[blockIdx.x * n + i] = (T2)local_out;\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / n;\n        s_variance = rsqrtf((variance / n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (beta == nullptr) ? 0.0f : (float)(beta[i]);\n        norm_output[blockIdx.x * n + i] =\n            (T1)(((((float)output[blockIdx.x * n + i] - s_mean) * s_variance) * (float)(gamma[i]) + beta_val)\n                 * output_scale_val);\n    }\n}\n\n// TODO: implement T2 = half2\ntemplate<>\n__global__ void generalFP8AddBiasResidualLayerNorm(const __nv_bfloat162_2_xy* __restrict input,\n                                                   const __nv_bfloat162_2_xy* __restrict gamma,\n                                                   const __nv_bfloat162_2_xy* __restrict beta,\n                                                   const __nv_bfloat162_2_xy* __restrict bias,\n                                                   __nv_bfloat162_2_xy* output,\n                                                   __hip_fp8x4_e4m3_fnuz*     norm_output,\n                                                   const float*         input_scale,\n                                                   const float*         output_scale,\n                                                   int                  m,\n                                                   int                  n)\n{\n    using bf16_4 = __nv_bfloat162_2_xy;\n    using bf16_2 = __hip_bfloat162;\n    using fp8_4  = __hip_fp8x4_e4m3_fnuz;\n    int tid      = threadIdx.x;\n    int real_n   = n * 4;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    bf16_2 input_scale_val = input_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(input_scale));\n    bf16_2 output_scale_val =\n        output_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(output_scale));\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        bf16_4 local_out = input[blockIdx.x * n + i];\n        local_out.x      = hadd2(hmul2(output[blockIdx.x * n + i].x, input_scale_val), local_out.x);\n        local_out.y      = hadd2(hmul2(output[blockIdx.x * n + i].y, input_scale_val), local_out.y);\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out.x = hadd2(local_out.x, bias[i].x);\n            local_out.y = hadd2(local_out.y, bias[i].y);\n        }\n        output[blockIdx.x * n + i] = local_out;\n        // NOTE: need float accum here, or low task accuracy (summarization)\n        local_sum += (float)(local_out.x.x) + (float)(local_out.x.y) + (float)(local_out.y.x) + (float)(local_out.y.y);\n        local_sum_sq += (float)local_out.x.x * (float)local_out.x.x + (float)local_out.y.x * (float)local_out.y.x\n                        + (float)local_out.x.y * (float)local_out.x.y + (float)local_out.y.y * (float)local_out.y.y;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / real_n;\n        s_variance = rsqrtf((variance / real_n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    bf16_2 s_mean_2 = cuda_cast<bf16_2>(s_mean);\n    bf16_2 s_var_2  = cuda_cast<bf16_2>(s_variance);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        bf16_4 norm_output_val;\n        norm_output_val.x =\n            hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i].x, s_mean_2), s_var_2), gamma[i].x), beta[i].x),\n                  output_scale_val);\n        norm_output_val.y =\n            hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i].y, s_mean_2), s_var_2), gamma[i].y), beta[i].y),\n                  output_scale_val);\n        norm_output[blockIdx.x * n + i] = fp8_4(norm_output_val.x, norm_output_val.y);\n    }\n}\n\n// TODO: implement T2 = half2\ntemplate<>\n__global__ void generalFP8AddBiasResidualLayerNorm(const __hip_bfloat162* __restrict input,\n                                                   const __hip_bfloat162* __restrict gamma,\n                                                   const __hip_bfloat162* __restrict beta,\n                                                   const __hip_bfloat162* __restrict bias,\n                                                   __hip_bfloat162*  output,\n                                                   __hip_fp8x2_e4m3_fnuz* norm_output,\n                                                   const float*     input_scale,\n                                                   const float*     output_scale,\n                                                   int              m,\n                                                   int              n)\n{\n    using bf16_2 = __hip_bfloat162;\n    using fp8_2  = __hip_fp8x2_e4m3_fnuz;\n    int tid      = threadIdx.x;\n    int real_n   = n * 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    bf16_2 input_scale_val = input_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(input_scale));\n    bf16_2 output_scale_val =\n        output_scale == nullptr ? cuda_cast<bf16_2>(1.0f) : cuda_cast<bf16_2>(__ldg(output_scale));\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&input[blockIdx.x * n + i]));\n        bf16_2 local_out = input[blockIdx.x * n + i];\n        local_out        = hadd2(hmul2(output[blockIdx.x * n + i], input_scale_val), local_out);\n        if (bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out = hadd2(local_out, bias[i]);\n        }\n        output[blockIdx.x * n + i] = local_out;\n        local_sum += (float)(local_out.x) + (float)(local_out.y);\n        local_sum_sq += (float)local_out.x * (float)local_out.x + (float)local_out.y * (float)local_out.y;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    mean = blockReduceSum(local_sum);\n    __syncthreads();  // TODO check where should we put sync\n    variance = blockReduceSum(local_sum_sq);\n    __syncthreads();  // TODO check where should we put sync\n\n    if (threadIdx.x == 0) {\n        s_mean     = mean / real_n;\n        s_variance = rsqrtf((variance / real_n) - (s_mean * s_mean) + 1e-6);\n    }\n    __syncthreads();\n\n    bf16_2 s_mean_2 = cuda_cast<bf16_2>(s_mean);\n    bf16_2 s_var_2  = cuda_cast<bf16_2>(s_variance);\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        norm_output[blockIdx.x * n + i] =\n            fp8_2(hmul2(hadd2(hmul2(hmul2(hsub2(output[blockIdx.x * n + i], s_mean_2), s_var_2), gamma[i]), beta[i]),\n                        output_scale_val));\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8AddBiasResidualPreLayerNorm(GeneralFP8AddBiasResidualPreLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n    // NOTE: only T2 = bfloat supported yet\n    if (param.n % 4 == 0) {\n        dim3 block(min(param.n / 4, 1024));\n        generalFP8AddBiasResidualLayerNorm<<<grid, block, 0, param.stream>>>((const __nv_bfloat162_2_xy*)param.residual,\n                                                                             (const __nv_bfloat162_2_xy*)param.gamma,\n                                                                             (const __nv_bfloat162_2_xy*)param.beta,\n                                                                             (const __nv_bfloat162_2_xy*)param.bias,\n                                                                             (__nv_bfloat162_2_xy*)param.output,\n                                                                             (__hip_fp8x4_e4m3_fnuz*)param.normed_output,\n                                                                             param.input_deq_ptr,\n                                                                             param.output_qua_ptr,\n                                                                             param.m,\n                                                                             param.n / 4);  // For gpt-3\n    }\n    else if (param.n % 2 == 0) {\n        dim3 block(min(param.n / 2, 1024));\n        generalFP8AddBiasResidualLayerNorm<<<grid, block, 0, param.stream>>>((const __hip_bfloat162*)param.residual,\n                                                                             (const __hip_bfloat162*)param.gamma,\n                                                                             (const __hip_bfloat162*)param.beta,\n                                                                             (const __hip_bfloat162*)param.bias,\n                                                                             (__hip_bfloat162*)param.output,\n                                                                             (__hip_fp8x2_e4m3_fnuz*)param.normed_output,\n                                                                             param.input_deq_ptr,\n                                                                             param.output_qua_ptr,\n                                                                             param.m,\n                                                                             param.n / 2);  // For gpt-3\n    }\n    else {\n        dim3 block(min(param.n, 1024));\n        if (param.n % 32 != 0) {\n            block.x = 1024;\n        }\n        // const int vec_n = 4 / sizeof(T2);\n        // block.x = block.x / vec_n; // We don't need this setting because we don't have bfloat162 implementation now\n\n        /* should pay attention to the rsqrt precision*/\n        generalFP8AddBiasResidualLayerNorm<T1, T2><<<grid, block, 0, param.stream>>>(param.residual,\n                                                                                     param.gamma,\n                                                                                     param.beta,\n                                                                                     param.bias,\n                                                                                     param.output,\n                                                                                     param.normed_output,\n                                                                                     param.input_deq_ptr,\n                                                                                     param.output_qua_ptr,\n                                                                                     param.m,\n                                                                                     param.n);  // For gpt-3\n    }\n    return;\n}\n\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, float, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<float, float, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<float, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<half, half, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<half, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, half, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, 0>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, float, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<float, float, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<float, float> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<half, half, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<half, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, half, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, half> param);\ntemplate void invokeGeneralFP8AddBiasResidualPreLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, 1>(\n    GeneralFP8AddBiasResidualPreLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV1(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float input_scalar;\n    float output_scalar;\n    if (QUANTIZE_MODE == 0) {\n        // per channel\n        input_scalar  = __ldg(param.input_scale + threadIdx.x);\n        output_scalar = __ldg(param.output_scale);  // must per tensor because it is quantize of input tensor of GEMM\n    }\n    else if (QUANTIZE_MODE == 1) {\n        input_scalar  = __ldg(param.input_scale);\n        output_scalar = __ldg(param.output_scale);\n    }\n    else if (QUANTIZE_MODE == QUANTIZE_MODE::PER_CHANNEL_WEIGHT_PER_TENSOR_ACT) {\n        input_scalar = __ldg(param.input_scale) * __ldg(param.input_scale_2 + threadIdx.x)\n                       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n        output_scalar = __ldg(param.output_scale);\n    }\n\n    for (int i = tid; i < param.n; i += blockDim.x) {\n        // float local_out = (float)(__ldg(&param.input[blockIdx.x * param.n + i])) * input_scalar;\n        float local_out = (float)(param.input[blockIdx.x * param.n + i]) * input_scalar;\n        local_out       = local_out + (float)(param.residual[blockIdx.x * param.n + i]) * __ldg(param.residual_scale);\n        if (param.bias != nullptr) {\n            // local_out += (float)(__ldg(&bias[i]));\n            local_out += (float)(param.bias[i]);\n        }\n\n        param.normed_output[blockIdx.x * param.n + i] = (T1)local_out;  // TODO This conversion has bug\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / param.n;\n        s_variance = rsqrtf(sums[1] / param.n - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < param.n; i += blockDim.x) {\n        // float beta_val = (beta == nullptr) ? 0.0f : (float)(__ldg(&beta[i]));\n        float beta_val = (param.beta == nullptr) ? 0.0f : (float)(param.beta[i]);\n        param.normed_output[blockIdx.x * param.n + i] =\n            (T1)(((((float)param.normed_output[blockIdx.x * param.n + i] - s_mean) * s_variance)\n                      * (float)(param.gamma[i])\n                  + beta_val)\n                 * output_scalar);\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV2(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    using T1_4 = __hip_fp8x4_e4m3_fnuz;\n    using T2_2 = typename TypeConverter<T2>::Type;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 input_scalar    = cuda_cast<T2_2>(__ldg(param.input_scale));\n    T2_2 output_scalar   = cuda_cast<T2_2>(__ldg(param.output_scale));\n    T2_2 residual_scalar = cuda_cast<T2_2>(__ldg(param.residual_scale));\n\n    const int n = param.n / 4;\n\n    T1_4* input_ptr         = (T1_4*)(param.input);\n    T1_4* residual_ptr      = (T1_4*)(param.residual);\n    T1_4* normed_output_ptr = (T1_4*)(param.normed_output);\n    T2_2* bias_ptr          = (T2_2*)(param.bias);\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    T2_2 local_outs[2];\n\n    T2_2      val_0, val_1;\n    const int index_0 = 2 * threadIdx.x;\n    const int index_1 = index_0 + 1;\n\n    fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &input_ptr[blockIdx.x * n + threadIdx.x]);\n    val_0         = hmul2(val_0, input_scalar);\n    val_1         = hmul2(val_1, input_scalar);\n    local_outs[0] = val_0;\n    local_outs[1] = val_1;\n\n    fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &residual_ptr[blockIdx.x * n + threadIdx.x]);\n    val_0 = hmul2(val_0, residual_scalar);\n    val_1 = hmul2(val_1, residual_scalar);\n    local_outs[0] += val_0;\n    local_outs[1] += val_1;\n\n    if (bias_ptr != nullptr) {\n        local_outs[0] = hadd2(local_outs[0], bias_ptr[index_0]);\n        local_outs[1] = hadd2(local_outs[1], bias_ptr[index_1]);\n    }\n\n    local_sum += (float)(local_outs[0].x + local_outs[0].y + local_outs[1].x + local_outs[1].y);\n    local_sum_sq += (float)local_outs[0].x * (float)local_outs[0].x + (float)local_outs[0].y * (float)local_outs[0].y\n                    + (float)local_outs[1].x * (float)local_outs[1].x + (float)local_outs[1].y * (float)local_outs[1].y;\n\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / param.n;\n        s_variance = rsqrtf(sums[1] / param.n - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(s_mean);\n    T2_2 s_var_2  = cuda_cast<T2_2>(s_variance);\n\n    // {[(x - mean) * var * gamma] + beta} * output_scale\n    local_outs[0] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[0], s_mean_2), s_var_2), gamma_ptr[index_0]), beta_ptr[index_0]),\n              output_scalar);\n    local_outs[1] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[1], s_mean_2), s_var_2), gamma_ptr[index_1]), beta_ptr[index_1]),\n              output_scalar);\n\n    normed_output_ptr[blockIdx.x * n + threadIdx.x] = T1_4(local_outs[0], local_outs[1]);\n}\n\ntemplate<typename T1, typename T2, int ELEMENT_PER_THREAD, int WARP_NUM>\n__global__ void generalFP8IOAddBiasResidualPostLayerNormV3(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    // Each warp handle one row. So, we can save the cost of sync of block.\n    // But when param.m is small, the launched blocks are too small the performance is worse\n    // than V2.\n    using T1_4 = __hip_fp8x4_e4m3_fnuz;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 input_scalar    = cuda_cast<T2_2>(__ldg(param.input_scale));\n    T2_2 output_scalar   = cuda_cast<T2_2>(__ldg(param.output_scale));\n    T2_2 residual_scalar = cuda_cast<T2_2>(__ldg(param.residual_scale));\n\n    const int n = param.n / 4;\n\n    T1_4* input_ptr         = (T1_4*)(param.input);\n    T1_4* residual_ptr      = (T1_4*)(param.residual);\n    T1_4* normed_output_ptr = (T1_4*)(param.normed_output);\n    T2_2* bias_ptr          = (T2_2*)(param.bias);\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    T2_2 local_outs[ELEMENT_PER_THREAD][2];\n\n    const int row_id = blockIdx.x * blockDim.y + threadIdx.y;\n    if (row_id > param.m) {\n        return;\n    }\n\n    T2_2 val_0, val_1;\n\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n\n        fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &input_ptr[row_id * n + i * blockDim.x + threadIdx.x]);\n        val_0            = hmul2(val_0, input_scalar);\n        val_1            = hmul2(val_1, input_scalar);\n        local_outs[i][0] = val_0;\n        local_outs[i][1] = val_1;\n\n        fp8x4_e4m3_to_bfloat2(&val_0, &val_1, &residual_ptr[row_id * n + i * blockDim.x + threadIdx.x]);\n        val_0 = hmul2(val_0, residual_scalar);\n        val_1 = hmul2(val_1, residual_scalar);\n        local_outs[i][0] += val_0;\n        local_outs[i][1] += val_1;\n\n        if (bias_ptr != nullptr) {\n            local_outs[i][0] = hadd2(local_outs[i][0], __ldg(bias_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0));\n            local_outs[i][1] = hadd2(local_outs[i][1], __ldg(bias_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1));\n        }\n\n        val_0 = hadd2(local_outs[i][0], local_outs[i][1]);\n        local_sum += (float)(val_0.x + val_0.y);\n        val_0 = hmul2(local_outs[i][0], local_outs[i][0]);\n        val_1 = hmul2(local_outs[i][1], local_outs[i][1]);\n        val_1 = hadd2(val_1, val_0);\n        local_sum_sq += (float)(val_1.x + val_1.y);\n    }\n\n    float sums[2];\n    sums[0] = local_sum;\n    sums[1] = local_sum_sq;\n    warpReduceSumV2<float, 2>(sums);\n\n    sums[0] = sums[0] / (float)(param.n);\n    sums[1] = rsqrtf(sums[1] / (float)(param.n) - sums[0] * sums[0] + 1e-12f);\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(sums[0]);\n    T2_2 s_var_2  = cuda_cast<T2_2>(sums[1]);\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n\n        // {[(x - mean) * var * gamma] + beta} * output_scale\n        local_outs[i][0] = hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[i][0], s_mean_2), s_var_2),\n                                             ldg(gamma_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0)),\n                                       ldg(beta_ptr + 2 * (i * blockDim.x + threadIdx.x) + 0)),\n                                 output_scalar);\n        local_outs[i][1] = hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[i][1], s_mean_2), s_var_2),\n                                             ldg(gamma_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1)),\n                                       ldg(beta_ptr + 2 * (i * blockDim.x + threadIdx.x) + 1)),\n                                 output_scalar);\n        normed_output_ptr[row_id * n + i * blockDim.x + threadIdx.x] = T1_4(local_outs[i][0], local_outs[i][1]);\n    }\n}\n\ntemplate<typename T1, typename T2, int QUANTIZE_MODE>\nvoid invokeGeneralFP8IOAddBiasResidualPostLayerNorm(GeneralFP8IOAddBiasResidualPostLayerNormParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n    FT_CHECK(param.n <= 1024);\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    if (param.n % 4 == 0) {\n        // TODO (bhsueh) check the condition here\n        if (param.m > 1024) {\n            block.x            = 32;\n            const int WARP_NUM = 8;\n            block.y            = WARP_NUM;\n            grid.x             = (grid.x + (WARP_NUM - 1)) / WARP_NUM;\n            if (param.n == 1024) {\n                generalFP8IOAddBiasResidualPostLayerNormV3<T1, T2, 8, WARP_NUM>\n                    <<<grid, block, 0, param.stream>>>(param);\n            }\n            else if (param.n == 768) {\n                generalFP8IOAddBiasResidualPostLayerNormV3<T1, T2, 6, WARP_NUM>\n                    <<<grid, block, 0, param.stream>>>(param);\n            }\n        }\n        else {\n            block.x /= 4;\n            generalFP8IOAddBiasResidualPostLayerNormV2<T1, T2><<<grid, block, 0, param.stream>>>(param);\n        }\n    }\n    else {\n        generalFP8IOAddBiasResidualPostLayerNormV1<T1, T2, QUANTIZE_MODE><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\ntemplate void invokeGeneralFP8IOAddBiasResidualPostLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, PER_CHANNEL>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate void invokeGeneralFP8IOAddBiasResidualPostLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, PER_TENSOR>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate void\ninvokeGeneralFP8IOAddBiasResidualPostLayerNorm<__hip_fp8_e4m3_fnuz, __hip_bfloat16, PER_CHANNEL_WEIGHT_PER_TENSOR_ACT>(\n    GeneralFP8IOAddBiasResidualPostLayerNormParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<typename T1, typename T2, int ELEMENT_PER_THREAD>\n__global__ void removePaddingEmbLookupLayerNormFP8Out(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    float local_outs[ELEMENT_PER_THREAD];\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    float output_scalar = __ldg(param.output_scale);\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        int index      = blockDim.x * i + threadIdx.x;\n        int padded_row = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n        int position_id =\n            param.position_ids == nullptr ? padded_row % param.max_seq_len : param.position_ids[padded_row];\n        int   token_type_id = param.token_type_ids == nullptr ? 0 : param.token_type_ids[padded_row];\n        int   input_id      = param.input_ids[padded_row];\n        float local_out     = (input_id == 0 ? 0.0f : (float)param.word_embeddings[input_id * param.n + index])\n                          + (float)param.position_embeddings[position_id * param.n + index]\n                          + (float)param.token_type_embeddings[token_type_id * param.n + index];\n\n        local_outs[i] = local_out;\n        local_sum += local_out;\n        local_sum_sq += local_out * local_out;\n    }\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2] = {0.0f};\n    sums[0]       = local_sum;\n    sums[1]       = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / ((float)param.n);\n        s_variance = rsqrtf(sums[1] / ((float)param.n) - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        int   index    = blockDim.x * i + threadIdx.x;\n        float beta_val = (param.beta == nullptr) ? 0.0f : (float)(param.beta[index]);\n        param.normed_output[blockIdx.x * param.n + index] =\n            (T1)((((local_outs[i] - s_mean) * s_variance) * (float)(param.gamma[index]) + beta_val) * output_scalar);\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void removePaddingEmbLookupLayerNormFP8OutV2(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    using T1_4 = __hip_fp8x4_e4m3_fnuz;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    T2_2 local_outs[2];\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_sum    = 0.0f;\n    float local_sum_sq = 0.0f;\n\n    T2_2 output_scalar = cuda_cast<T2_2>(__ldg(param.output_scale));\n\n    T2_2* word_emb_ptr      = (T2_2*)param.word_embeddings;\n    T2_2* pos_emb_ptr       = (T2_2*)param.position_embeddings;\n    T2_2* type_emb_ptr      = (T2_2*)param.token_type_embeddings;\n    T1_4* normed_output_ptr = (T1_4*)param.normed_output;\n    T2_2* gamma_ptr         = (T2_2*)(param.gamma);\n    T2_2* beta_ptr          = (T2_2*)(param.beta);\n\n    int n_div_2 = param.n / 2;\n    int n_div_4 = param.n / 4;\n\n    const int index_0 = 2 * threadIdx.x;\n    const int index_1 = index_0 + 1;\n\n    int padded_row    = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n    int position_id   = param.position_ids == nullptr ? padded_row % param.max_seq_len : param.position_ids[padded_row];\n    int token_type_id = param.token_type_ids == nullptr ? 0 : param.token_type_ids[padded_row];\n    int input_id      = param.input_ids[padded_row];\n\n    local_outs[0] = (input_id == 0 ? cuda_cast<T2_2>(0.0f) : word_emb_ptr[input_id * n_div_2 + index_0])\n                    + pos_emb_ptr[position_id * n_div_2 + index_0] + type_emb_ptr[token_type_id * n_div_2 + index_0];\n    local_outs[1] = (input_id == 0 ? cuda_cast<T2_2>(0.0f) : word_emb_ptr[input_id * n_div_2 + index_1])\n                    + pos_emb_ptr[position_id * n_div_2 + index_1] + type_emb_ptr[token_type_id * n_div_2 + index_1];\n\n    local_sum += (float)(local_outs[0].x + local_outs[0].y + local_outs[1].x + local_outs[1].y);\n    local_sum_sq += (float)local_outs[0].x * (float)local_outs[0].x + (float)local_outs[0].y * (float)local_outs[0].y\n                    + (float)local_outs[1].x * (float)local_outs[1].x + (float)local_outs[1].y * (float)local_outs[1].y;\n    __syncthreads();  // TODO check where should we put sync\n\n    float sums[2] = {0.0f};\n    sums[0]       = local_sum;\n    sums[1]       = local_sum_sq;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / ((float)param.n);\n        s_variance = rsqrtf(sums[1] / ((float)param.n) - s_mean * s_mean + 1e-12f);\n    }\n    __syncthreads();\n\n    T2_2 s_mean_2 = cuda_cast<T2_2>(s_mean);\n    T2_2 s_var_2  = cuda_cast<T2_2>(s_variance);\n\n    // {[(x - mean) * var * gamma] + beta} * output_scale\n    local_outs[0] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[0], s_mean_2), s_var_2), gamma_ptr[index_0]), beta_ptr[index_0]),\n              output_scalar);\n    local_outs[1] =\n        hmul2(hadd2(hmul2(hmul2(hsub2(local_outs[1], s_mean_2), s_var_2), gamma_ptr[index_1]), beta_ptr[index_1]),\n              output_scalar);\n\n    normed_output_ptr[blockIdx.x * n_div_4 + threadIdx.x] = T1_4(local_outs[0], local_outs[1]);\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeRemovePaddingEmbLookupLayerNormFP8Out(RemovePaddingEmbLookupLayerNormFP8OutParam<T1, T2> param)\n{\n    dim3 grid(param.m);\n    dim3 block(min(param.n, 1024));\n    FT_CHECK(param.n <= 1024);\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (param.n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    if (param.n <= 1024) {\n        if (block.x % 4 == 0) {\n            block.x /= 4;\n            removePaddingEmbLookupLayerNormFP8OutV2<T1, T2><<<grid, block, 0, param.stream>>>(param);\n        }\n        else {\n            removePaddingEmbLookupLayerNormFP8Out<T1, T2, 1><<<grid, block, 0, param.stream>>>(param);\n        }\n    }\n}\n\ntemplate void invokeRemovePaddingEmbLookupLayerNormFP8Out<__hip_fp8_e4m3_fnuz, float>(\n    RemovePaddingEmbLookupLayerNormFP8OutParam<__hip_fp8_e4m3_fnuz, float> param);\ntemplate void invokeRemovePaddingEmbLookupLayerNormFP8Out<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(\n    RemovePaddingEmbLookupLayerNormFP8OutParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/layernorm_fp8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/vit_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void add_bias_slice(\n    const T* __restrict in, T* __restrict out, const T* __restrict bias, int m, int n, int s, bool on_top = true)\n{\n    // s = slice_cnt, input_slice_size = s*n,\n    // n = element size after concate axis. for example: [2,3,4,5] concate_axis = 1, then n = 4*5=20\n    // on_top: concate slice on top or not.\n    const int offset = on_top ? 1 : 0;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int slice_id                      = id / (s * n);\n        out[id + (slice_id + offset) * n] = __ldg(&in[id]) + __ldg(&bias[id % n]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_slice(\n    const half* __restrict in, half* __restrict out, const half* __restrict bias, int m, int n, int s, bool on_top)\n{\n    const int    offset   = on_top ? 1 : 0;\n    const half2* in_ptr   = (half2*)in;\n    const half2* bias_ptr = (half2*)bias;\n    half2*       out_ptr  = (half2*)out;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        half2 d1                              = __ldg(&in_ptr[id]);\n        half2 d2                              = __ldg(&bias_ptr[id % n]);\n        int   slice_id                        = id / (s * n);\n        out_ptr[id + (slice_id + offset) * n] = __hadd2(d1, d2);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasSlice(T* in, T* out, const T* bias, const int m, const int n, const int s, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_slice<<<grid, block, 0, stream>>>(in, out, bias, m, n / data_type_factor, s);\n}\n\ntemplate<typename T>\n__global__ void add_bias_concat_clstoken_add_posembed(const T* __restrict in,         // b*h*w*n\n                                                      T* __restrict out,              // b*(h*w+1)*n\n                                                      const T* __restrict bias,       // n\n                                                      const T* __restrict cls_token,  // n\n                                                      const T* __restrict pos_embed,  // (h*w+1)*n\n                                                      const int m,                    // b * (h*w+1)\n                                                      const int n,\n                                                      const int s,  // h*w+1\n                                                      bool      on_top = true)\n{\n    const int concat_row_idx = on_top ? 0 : (s - 1);\n    const int offset         = on_top ? 1 : 0;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int col_idx       = id % n;\n        int row_idx       = id / n;\n        int slice_row_idx = row_idx % s;\n        int slice_idx     = row_idx / s;\n        int idx_s         = slice_row_idx * n + col_idx;\n        int idx_i         = (slice_row_idx - offset + slice_idx * (s - 1)) * n + col_idx;\n\n        if (slice_row_idx == concat_row_idx) {\n            out[id] = __ldg(&cls_token[col_idx]) + __ldg(&pos_embed[idx_s]);\n        }\n        else {\n            out[id] = __ldg(&in[idx_i]) + __ldg(&bias[col_idx]) + __ldg(&pos_embed[idx_s]);\n        }\n    }\n}\n\ntemplate<>\n__global__ void add_bias_concat_clstoken_add_posembed(const half* __restrict in,         // b*h*w*n\n                                                      half* __restrict out,              // b*(h*w+1)*n\n                                                      const half* __restrict bias,       // n\n                                                      const half* __restrict cls_token,  // n\n                                                      const half* __restrict pos_embed,  // (h*w+1)*n\n                                                      const int m,                       // b * (h*w+1)\n                                                      const int n,\n                                                      const int s,  // h*w+1\n                                                      bool      on_top)\n{\n    const int    concat_row_idx = on_top ? 0 : (s - 1);\n    const int    offset         = on_top ? 1 : 0;\n    half2*       out_ptr        = (half2*)out;\n    const half2* in_ptr         = (half2*)in;\n    const half2* bias_ptr       = (half2*)bias;\n    const half2* token_ptr      = (half2*)cls_token;\n    const half2* embed_ptr      = (half2*)pos_embed;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int col_idx       = id % n;\n        int row_idx       = id / n;\n        int slice_row_idx = row_idx % s;\n        int slice_idx     = row_idx / s;\n        int idx_s         = slice_row_idx * n + col_idx;\n        int idx_i         = (slice_row_idx - offset + slice_idx * (s - 1)) * n + col_idx;\n\n        if (slice_row_idx == concat_row_idx) {\n            half2 d1    = __ldg(&token_ptr[col_idx]);\n            half2 d2    = __ldg(&embed_ptr[idx_s]);\n            out_ptr[id] = __hadd2(d1, d2);\n        }\n        else {\n            half2 d1    = __ldg(&in_ptr[idx_i]);\n            half2 d2    = __ldg(&bias_ptr[col_idx]);\n            half2 d3    = __ldg(&embed_ptr[idx_s]);\n            out_ptr[id] = __hadd2(d3, __hadd2(d1, d2));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasConcatClsTokenAddPosEmbed(const T*     in,\n                                            T*           out,\n                                            const T*     bias,\n                                            const T*     cls_token,\n                                            const T*     pos_embed,\n                                            const int    m,\n                                            const int    n,\n                                            const int    s,\n                                            cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_concat_clstoken_add_posembed<<<grid, block, 0, stream>>>(\n        in, out, bias, cls_token, pos_embed, m, n / data_type_factor, s);\n}\n\ntemplate void invokeAddBiasConcatClsTokenAddPosEmbed(const float* in,\n                                                     float*       out,\n                                                     const float* bias,\n                                                     const float* cls_token,\n                                                     const float* pos_embed,\n                                                     const int    m,\n                                                     const int    n,\n                                                     const int    s,\n                                                     cudaStream_t stream);\n\ntemplate void invokeAddBiasConcatClsTokenAddPosEmbed(const half*  in,\n                                                     half*        out,\n                                                     const half*  bias,\n                                                     const half*  cls_token,\n                                                     const half*  pos_embed,\n                                                     const int    m,\n                                                     const int    n,\n                                                     const int    s,\n                                                     cudaStream_t stream);\n\ntemplate<typename T>\n__global__ void\nslice_copy(const T* __restrict in, T* __restrict out, const int m, const int n, const int s, const int offset_s)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx > m * n) {\n        return;\n    }\n\n    int m_idx  = idx / n;\n    int col    = idx % n;\n    int in_idx = (m_idx * s + offset_s) * n + col;\n\n    out[idx] = __ldg(&in[in_idx]);\n}\n\ntemplate<>\n__global__ void\nslice_copy(const half* __restrict in, half* __restrict out, const int m, const int n, const int s, const int offset_s)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx > m * n) {\n        return;\n    }\n\n    int m_idx  = idx / n;\n    int col    = idx % n;\n    int in_idx = (m_idx * s + offset_s) * n + col;\n\n    half2*       out_ptr = (half2*)out;\n    const half2* in_ptr  = (half2*)in;\n\n    out_ptr[idx] = __ldg(&in_ptr[in_idx]);\n}\n\ntemplate<typename T>\nvoid invokeSliceCopy(\n    const T* in, T* out, const int m, const int n, const int s, const int offset_s, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / data_type_factor <= 1024) {\n        block.x = n / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    slice_copy<<<grid, block, 0, stream>>>(in, out, m, n / data_type_factor, s, offset_s);\n}\n\ntemplate void invokeSliceCopy(\n    const float* in, float* out, const int m, const int n, const int s, const int offset_s, cudaStream_t stream);\ntemplate void invokeSliceCopy(\n    const half* in, half* out, const int m, const int n, const int s, const int offset_s, cudaStream_t stream);\n\ntemplate<typename T>\n__global__ void add_bias_add_posembed(T* __restrict out,              // b*(h*w)*n\n                                      const T* __restrict bias,       // n\n                                      const T* __restrict pos_embed,  // (h*w)*n\n                                      const int m,                    // b * (h*w)\n                                      const int n,\n                                      const int s  // h*w*n\n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int b_idx = id % n;\n        int p_idx = id % s;\n\n        out[id] += __ldg(&bias[b_idx]) + __ldg(&pos_embed[p_idx]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_add_posembed(half* __restrict out,              // b*(h*w+1)*n\n                                      const half* __restrict bias,       // n\n                                      const half* __restrict pos_embed,  // (h*w)*n\n                                      const int m,                       // b * (h*w)\n                                      const int n,\n                                      const int s  // h*w *n\n)\n{\n    half2*       out_ptr   = (half2*)out;\n    const half2* bias_ptr  = (half2*)bias;\n    const half2* embed_ptr = (half2*)pos_embed;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int   b_idx = id % n;\n        int   p_idx = id % s;\n        half2 d1    = __ldg(&bias_ptr[b_idx]);\n        half2 d2    = __ldg(&embed_ptr[p_idx]);\n        out_ptr[id] = __hadd2(out_ptr[id], __hadd2(d1, d2));\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasAddPosEmbed(\n    T* out, const T* bias, const T* pos_embed, const int m, const int n, const int s, cudaStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_add_posembed<<<grid, block, 0, stream>>>(out, bias, pos_embed, m, n / data_type_factor, s);\n}\n\ntemplate void invokeAddBiasAddPosEmbed(\n    float* out, const float* bias, const float* pos_embed, const int m, const int n, const int s, cudaStream_t stream);\n\ntemplate void invokeAddBiasAddPosEmbed(\n    half* out, const half* bias, const half* pos_embed, const int m, const int n, const int s, cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/vit_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void add_bias_slice(\n    const T* __restrict in, T* __restrict out, const T* __restrict bias, int m, int n, int s, bool on_top = true)\n{\n    // s = slice_cnt, input_slice_size = s*n,\n    // n = element size after concate axis. for example: [2,3,4,5] concate_axis = 1, then n = 4*5=20\n    // on_top: concate slice on top or not.\n    const int offset = on_top ? 1 : 0;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int slice_id                      = id / (s * n);\n        out[id + (slice_id + offset) * n] = __ldg(&in[id]) + __ldg(&bias[id % n]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_slice(\n    const half* __restrict in, half* __restrict out, const half* __restrict bias, int m, int n, int s, bool on_top)\n{\n    const int    offset   = on_top ? 1 : 0;\n    const half2* in_ptr   = (half2*)in;\n    const half2* bias_ptr = (half2*)bias;\n    half2*       out_ptr  = (half2*)out;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        half2 d1                              = __ldg(&in_ptr[id]);\n        half2 d2                              = __ldg(&bias_ptr[id % n]);\n        int   slice_id                        = id / (s * n);\n        out_ptr[id + (slice_id + offset) * n] = __hadd2(d1, d2);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasSlice(T* in, T* out, const T* bias, const int m, const int n, const int s, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_slice<<<grid, block, 0, stream>>>(in, out, bias, m, n / data_type_factor, s);\n}\n\ntemplate<typename T>\n__global__ void add_bias_concat_clstoken_add_posembed(const T* __restrict in,         // b*h*w*n\n                                                      T* __restrict out,              // b*(h*w+1)*n\n                                                      const T* __restrict bias,       // n\n                                                      const T* __restrict cls_token,  // n\n                                                      const T* __restrict pos_embed,  // (h*w+1)*n\n                                                      const int m,                    // b * (h*w+1)\n                                                      const int n,\n                                                      const int s,  // h*w+1\n                                                      bool      on_top = true)\n{\n    const int concat_row_idx = on_top ? 0 : (s - 1);\n    const int offset         = on_top ? 1 : 0;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int col_idx       = id % n;\n        int row_idx       = id / n;\n        int slice_row_idx = row_idx % s;\n        int slice_idx     = row_idx / s;\n        int idx_s         = slice_row_idx * n + col_idx;\n        int idx_i         = (slice_row_idx - offset + slice_idx * (s - 1)) * n + col_idx;\n\n        if (slice_row_idx == concat_row_idx) {\n            out[id] = __ldg(&cls_token[col_idx]) + __ldg(&pos_embed[idx_s]);\n        }\n        else {\n            out[id] = __ldg(&in[idx_i]) + __ldg(&bias[col_idx]) + __ldg(&pos_embed[idx_s]);\n        }\n    }\n}\n\ntemplate<>\n__global__ void add_bias_concat_clstoken_add_posembed(const half* __restrict in,         // b*h*w*n\n                                                      half* __restrict out,              // b*(h*w+1)*n\n                                                      const half* __restrict bias,       // n\n                                                      const half* __restrict cls_token,  // n\n                                                      const half* __restrict pos_embed,  // (h*w+1)*n\n                                                      const int m,                       // b * (h*w+1)\n                                                      const int n,\n                                                      const int s,  // h*w+1\n                                                      bool      on_top)\n{\n    const int    concat_row_idx = on_top ? 0 : (s - 1);\n    const int    offset         = on_top ? 1 : 0;\n    half2*       out_ptr        = (half2*)out;\n    const half2* in_ptr         = (half2*)in;\n    const half2* bias_ptr       = (half2*)bias;\n    const half2* token_ptr      = (half2*)cls_token;\n    const half2* embed_ptr      = (half2*)pos_embed;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int col_idx       = id % n;\n        int row_idx       = id / n;\n        int slice_row_idx = row_idx % s;\n        int slice_idx     = row_idx / s;\n        int idx_s         = slice_row_idx * n + col_idx;\n        int idx_i         = (slice_row_idx - offset + slice_idx * (s - 1)) * n + col_idx;\n\n        if (slice_row_idx == concat_row_idx) {\n            half2 d1    = __ldg(&token_ptr[col_idx]);\n            half2 d2    = __ldg(&embed_ptr[idx_s]);\n            out_ptr[id] = __hadd2(d1, d2);\n        }\n        else {\n            half2 d1    = __ldg(&in_ptr[idx_i]);\n            half2 d2    = __ldg(&bias_ptr[col_idx]);\n            half2 d3    = __ldg(&embed_ptr[idx_s]);\n            out_ptr[id] = __hadd2(d3, __hadd2(d1, d2));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasConcatClsTokenAddPosEmbed(const T*     in,\n                                            T*           out,\n                                            const T*     bias,\n                                            const T*     cls_token,\n                                            const T*     pos_embed,\n                                            const int    m,\n                                            const int    n,\n                                            const int    s,\n                                            hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_concat_clstoken_add_posembed<<<grid, block, 0, stream>>>(\n        in, out, bias, cls_token, pos_embed, m, n / data_type_factor, s);\n}\n\ntemplate void invokeAddBiasConcatClsTokenAddPosEmbed(const float* in,\n                                                     float*       out,\n                                                     const float* bias,\n                                                     const float* cls_token,\n                                                     const float* pos_embed,\n                                                     const int    m,\n                                                     const int    n,\n                                                     const int    s,\n                                                     hipStream_t stream);\n\ntemplate void invokeAddBiasConcatClsTokenAddPosEmbed(const half*  in,\n                                                     half*        out,\n                                                     const half*  bias,\n                                                     const half*  cls_token,\n                                                     const half*  pos_embed,\n                                                     const int    m,\n                                                     const int    n,\n                                                     const int    s,\n                                                     hipStream_t stream);\n\ntemplate<typename T>\n__global__ void\nslice_copy(const T* __restrict in, T* __restrict out, const int m, const int n, const int s, const int offset_s)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx > m * n) {\n        return;\n    }\n\n    int m_idx  = idx / n;\n    int col    = idx % n;\n    int in_idx = (m_idx * s + offset_s) * n + col;\n\n    out[idx] = __ldg(&in[in_idx]);\n}\n\ntemplate<>\n__global__ void\nslice_copy(const half* __restrict in, half* __restrict out, const int m, const int n, const int s, const int offset_s)\n{\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (idx > m * n) {\n        return;\n    }\n\n    int m_idx  = idx / n;\n    int col    = idx % n;\n    int in_idx = (m_idx * s + offset_s) * n + col;\n\n    half2*       out_ptr = (half2*)out;\n    const half2* in_ptr  = (half2*)in;\n\n    out_ptr[idx] = __ldg(&in_ptr[in_idx]);\n}\n\ntemplate<typename T>\nvoid invokeSliceCopy(\n    const T* in, T* out, const int m, const int n, const int s, const int offset_s, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / data_type_factor <= 1024) {\n        block.x = n / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    slice_copy<<<grid, block, 0, stream>>>(in, out, m, n / data_type_factor, s, offset_s);\n}\n\ntemplate void invokeSliceCopy(\n    const float* in, float* out, const int m, const int n, const int s, const int offset_s, hipStream_t stream);\ntemplate void invokeSliceCopy(\n    const half* in, half* out, const int m, const int n, const int s, const int offset_s, hipStream_t stream);\n\ntemplate<typename T>\n__global__ void add_bias_add_posembed(T* __restrict out,              // b*(h*w)*n\n                                      const T* __restrict bias,       // n\n                                      const T* __restrict pos_embed,  // (h*w)*n\n                                      const int m,                    // b * (h*w)\n                                      const int n,\n                                      const int s  // h*w*n\n)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int b_idx = id % n;\n        int p_idx = id % s;\n\n        out[id] += __ldg(&bias[b_idx]) + __ldg(&pos_embed[p_idx]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_add_posembed(half* __restrict out,              // b*(h*w+1)*n\n                                      const half* __restrict bias,       // n\n                                      const half* __restrict pos_embed,  // (h*w)*n\n                                      const int m,                       // b * (h*w)\n                                      const int n,\n                                      const int s  // h*w *n\n)\n{\n    half2*       out_ptr   = (half2*)out;\n    const half2* bias_ptr  = (half2*)bias;\n    const half2* embed_ptr = (half2*)pos_embed;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < m * n; id += blockDim.x * gridDim.x) {\n        int   b_idx = id % n;\n        int   p_idx = id % s;\n        half2 d1    = __ldg(&bias_ptr[b_idx]);\n        half2 d2    = __ldg(&embed_ptr[p_idx]);\n        out_ptr[id] = __hadd2(out_ptr[id], __hadd2(d1, d2));\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasAddPosEmbed(\n    T* out, const T* bias, const T* pos_embed, const int m, const int n, const int s, hipStream_t stream)\n{\n    const int data_type_factor = 4 / sizeof(T);  // 1 for fp32, 2 for fp16\n    dim3      block, grid;\n    if (n / 4 / data_type_factor <= 1024) {\n        block.x = n / 4 / data_type_factor;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = (m * n + 1023) / 1024;\n    }\n    add_bias_add_posembed<<<grid, block, 0, stream>>>(out, bias, pos_embed, m, n / data_type_factor, s);\n}\n\ntemplate void invokeAddBiasAddPosEmbed(\n    float* out, const float* bias, const float* pos_embed, const int m, const int n, const int s, hipStream_t stream);\n\ntemplate void invokeAddBiasAddPosEmbed(\n    half* out, const half* bias, const half* pos_embed, const int m, const int n, const int s, hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/vit_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\n// build a mapping for fullData to removePaddingData\n// grid((valid_word_num+63)/64)\n// block(64)\n__global__ void mappingRemovePaddingData(int* mapping, const int* sequence_id_offset, const int valid_word_num)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < valid_word_num) {\n        mapping[idx + __ldg(sequence_id_offset + idx)] = idx;\n    }\n}\n\nvoid invokeMappingRemovePaddingData(const int    batch_size,\n                                    const int    seq_len,\n                                    const int    valid_word_num,\n                                    int*         mapping,\n                                    const int*   sequence_id_offset,\n                                    cudaStream_t stream)\n{\n    cudaMemsetAsync(mapping, -1, batch_size * seq_len * sizeof(int), stream);\n    mappingRemovePaddingData<<<dim3((valid_word_num + 63) / 64), dim3(64), 0, stream>>>(\n        mapping, sequence_id_offset, valid_word_num);\n}\n\n// add_QK_bias_transform for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform(int8_t*        q_buf_,\n                                      int8_t*        k_buf_,\n                                      const int32_t* Q,\n                                      const T*       bias_Q,\n                                      const int32_t* K,\n                                      const T*       bias_K,\n                                      const int      m,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      int            stride,\n                                      const float*   q_weight_amax,\n                                      const float*   q_input_deQFactor_div127_ptr,\n                                      const float*   k_weight_amax,\n                                      const float*   k_input_deQFactor_div127_ptr,\n                                      const float*   q_output_scale_ptr,\n                                      const float*   k_output_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / m;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform_varlen for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen(int8_t*        q_buf_,\n                                             int8_t*        k_buf_,\n                                             const int32_t* Q,\n                                             const T*       bias_Q,\n                                             const int32_t* K,\n                                             const T*       bias_K,\n                                             const int      m,\n                                             const int      batch_size,\n                                             const int      seq_len,\n                                             const int      head_num,\n                                             const int      size_per_head,\n                                             const int      seq_len_padded,\n                                             int            stride_q,\n                                             int            stride_k,\n                                             const float*   q_weight_amax,\n                                             const float*   q_input_deQFactor_div127_ptr,\n                                             const float*   k_weight_amax,\n                                             const float*   k_input_deQFactor_div127_ptr,\n                                             const float*   q_output_scale_ptr,\n                                             const float*   k_output_scale_ptr,\n                                             bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / m;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransform(int8_t*        q_buf,\n                              int8_t*        k_buf,\n                              const int32_t* Q,\n                              const T*       bias_Q,\n                              const int32_t* K,\n                              const T*       bias_K,\n                              const int      batch_size,\n                              const int      seq_len,\n                              const int      head_num,\n                              const int      size_per_head,\n                              const float*   q_weight_amax,\n                              const float*   q_input_deQFactor_div127_ptr,\n                              const float*   k_weight_amax,\n                              const float*   k_input_deQFactor_div127_ptr,\n                              const float*   q_output_scale_ptr,\n                              const float*   k_output_scale_ptr,\n                              bool           use_ORDER_COL32_2R_4R4,\n                              cudaStream_t   stream)\n{\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform<<<dim3(batch_size * seq_len * 2), dim3((head_num * size_per_head) / 4), 0, stream>>>(\n            q_buf,\n            k_buf,\n            Q,\n            bias_Q,\n            K,\n            bias_K,\n            batch_size * seq_len,\n            batch_size,\n            seq_len,\n            head_num,\n            size_per_head,\n            seq_len * size_per_head,\n            q_weight_amax,\n            q_input_deQFactor_div127_ptr,\n            k_weight_amax,\n            k_input_deQFactor_div127_ptr,\n            q_output_scale_ptr,\n            k_output_scale_ptr,\n            use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len_padded,\n                                                 seq_len * size_per_head,\n                                                 seq_len_padded * size_per_head,\n                                                 q_weight_amax,\n                                                 q_input_deQFactor_div127_ptr,\n                                                 k_weight_amax,\n                                                 k_input_deQFactor_div127_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddQKBiasTransform(int8_t*        q_buf,\n                                       int8_t*        k_buf,\n                                       const int32_t* Q,\n                                       const float*   bias_Q,\n                                       const int32_t* K,\n                                       const float*   bias_K,\n                                       const int      batch_size,\n                                       const int      seq_len,\n                                       const int      head_num,\n                                       const int      size_per_head,\n                                       const float*   q_weight_amax,\n                                       const float*   q_input_deQFactor_div127_ptr,\n                                       const float*   k_weight_amax,\n                                       const float*   k_input_deQFactor_div127_ptr,\n                                       const float*   q_output_scale_ptr,\n                                       const float*   k_output_scale_ptr,\n                                       bool           use_ORDER_COL32_2R_4R4,\n                                       cudaStream_t   stream);\n\ntemplate void invokeAddQKBiasTransform(int8_t*        q_buf,\n                                       int8_t*        k_buf,\n                                       const int32_t* Q,\n                                       const half*    bias_Q,\n                                       const int32_t* K,\n                                       const half*    bias_K,\n                                       const int      batch_size,\n                                       const int      seq_len,\n                                       const int      head_num,\n                                       const int      size_per_head,\n                                       const float*   q_weight_amax,\n                                       const float*   q_input_deQFactor_div127_ptr,\n                                       const float*   k_weight_amax,\n                                       const float*   k_input_deQFactor_div127_ptr,\n                                       const float*   q_output_scale_ptr,\n                                       const float*   k_output_scale_ptr,\n                                       bool           use_ORDER_COL32_2R_4R4,\n                                       cudaStream_t   stream);\n\n// add_QK_bias_padding_transform for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.padding seq_len in k_buf_ to a multiple of 32 named seq_len_padded\n// 3.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen(int8_t*       q_buf_,\n                                             int8_t*       k_buf_,\n                                             const int8_t* Q,\n                                             const T*      bias_Q,\n                                             const int8_t* K,\n                                             const T*      bias_K,\n                                             const int     m,\n                                             const int     batch_size,\n                                             const int     seq_len,\n                                             const int     head_num,\n                                             const int     size_per_head,\n                                             const int     seq_len_padded,\n                                             const int     stride_q,\n                                             const int     stride_k,\n                                             const float*  q_input_deQFactor_ptr,\n                                             const float*  k_input_deQFactor_ptr,\n                                             const float*  q_output_scale_ptr,\n                                             const float*  k_output_scale_ptr,\n                                             bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len/seq_len_padded, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len / COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform(int8_t*       q_buf_,\n                                      int8_t*       k_buf_,\n                                      const int8_t* Q,\n                                      const T*      bias_Q,\n                                      const int8_t* K,\n                                      const T*      bias_K,\n                                      const int     m,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      int           stride,\n                                      const float*  q_input_deQFactor_ptr,\n                                      const float*  k_input_deQFactor_ptr,\n                                      const float*  q_output_scale_ptr,\n                                      const float*  k_output_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransform(int8_t*       q_buf,\n                              int8_t*       k_buf,\n                              const int8_t* Q,\n                              const T*      bias_Q,\n                              const int8_t* K,\n                              const T*      bias_K,\n                              const int     batch_size,\n                              const int     seq_len,\n                              const int     head_num,\n                              const int     size_per_head,\n                              const float*  q_input_deQFactor_ptr,\n                              const float*  k_input_deQFactor_ptr,\n                              const float*  q_output_scale_ptr,\n                              const float*  k_output_scale_ptr,\n                              bool          use_ORDER_COL32_2R_4R4,\n                              cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len,\n                                                 seq_len * size_per_head,\n                                                 seq_len * size_per_head,\n                                                 q_input_deQFactor_ptr,\n                                                 k_input_deQFactor_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        // The padding words will not be considered in softmax, so we don't need memset for k_buf_\n        // cudaMemsetAsync(k_buf, 0, batch_size * head_num * seq_len_padded * size_per_head * sizeof(int8_t), stream);\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len_padded,\n                                                 seq_len * size_per_head,\n                                                 seq_len_padded * size_per_head,\n                                                 q_input_deQFactor_ptr,\n                                                 k_input_deQFactor_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddQKBiasTransform(int8_t*       q_buf,\n                                       int8_t*       k_buf,\n                                       const int8_t* Q,\n                                       const float*  bias_Q,\n                                       const int8_t* K,\n                                       const float*  bias_K,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  q_input_deQFactor_ptr,\n                                       const float*  k_input_deQFactor_ptr,\n                                       const float*  q_output_scale_ptr,\n                                       const float*  k_output_scale_ptr,\n                                       bool          use_ORDER_COL32_2R_4R4,\n                                       cudaStream_t  stream);\n\ntemplate void invokeAddQKBiasTransform(int8_t*       q_buf,\n                                       int8_t*       k_buf,\n                                       const int8_t* Q,\n                                       const half*   bias_Q,\n                                       const int8_t* K,\n                                       const half*   bias_K,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  q_input_deQFactor_ptr,\n                                       const float*  k_input_deQFactor_ptr,\n                                       const float*  q_output_scale_ptr,\n                                       const float*  k_output_scale_ptr,\n                                       bool          use_ORDER_COL32_2R_4R4,\n                                       cudaStream_t  stream);\n\n// add_QK_bias_padding_transform for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.padding seq_len in k_buf_ to a multiple of 32 named seq_len_padded\n// 3.transform each Q K row-major matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are row-major matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen_row(int8_t*       q_buf_,\n                                                 int8_t*       k_buf_,\n                                                 const int8_t* Q,\n                                                 const T*      bias_Q,\n                                                 const int8_t* K,\n                                                 const T*      bias_K,\n                                                 const int     m,\n                                                 const int     batch_size,\n                                                 const int     seq_len,\n                                                 const int     head_num,\n                                                 const int     size_per_head,\n                                                 const int     seq_len_padded,\n                                                 const int     stride_q,\n                                                 const int     stride_k,\n                                                 const float*  q_input_deQFactor_ptr,\n                                                 const float*  k_input_deQFactor_ptr,\n                                                 const float*  q_output_scale_ptr,\n                                                 const float*  k_output_scale_ptr,\n                                                 bool          use_ORDER_COL32_2R_4R4,\n                                                 const int     head_num_x_size_per_head)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4   = threadIdx.x << 2;\n    int batch_seq_id = blockIdx.x % m;\n    int batch_id     = (batch_seq_id) / seq_len;\n    int head_id      = threadIdx4 / size_per_head;\n    int id_in_head   = threadIdx4 % size_per_head;\n    int word_id      = blockIdx.x % seq_len;\n\n    int data_id = (batch_seq_id * head_num_x_size_per_head + threadIdx4) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len/seq_len_padded, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len / COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                 int8_t*       k_buf,\n                                 const int8_t* Q,\n                                 const T*      bias_Q,\n                                 const int8_t* K,\n                                 const T*      bias_K,\n                                 const int     batch_size,\n                                 const int     seq_len,\n                                 const int     head_num,\n                                 const int     size_per_head,\n                                 const float*  q_input_deQFactor_ptr,\n                                 const float*  k_input_deQFactor_ptr,\n                                 const float*  q_output_scale_ptr,\n                                 const float*  k_output_scale_ptr,\n                                 bool          use_ORDER_COL32_2R_4R4,\n                                 cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform_varlen_row<<<dim3(batch_size * seq_len * 2),\n                                           dim3((head_num * size_per_head) / 4),\n                                           0,\n                                           stream>>>(q_buf,\n                                                     k_buf,\n                                                     Q,\n                                                     bias_Q,\n                                                     K,\n                                                     bias_K,\n                                                     batch_size * seq_len,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len,\n                                                     seq_len * size_per_head,\n                                                     seq_len * size_per_head,\n                                                     q_input_deQFactor_ptr,\n                                                     k_input_deQFactor_ptr,\n                                                     q_output_scale_ptr,\n                                                     k_output_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4,\n                                                     head_num * size_per_head);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        // The padding words will not be considered in softmax, so we don't need memset for k_buf_\n        // cudaMemsetAsync(k_buf, 0, batch_size * head_num * seq_len_padded * size_per_head * sizeof(int8_t), stream);\n        add_QK_bias_transform_varlen_row<<<dim3(batch_size * seq_len * 2),\n                                           dim3((head_num * size_per_head) / 4),\n                                           0,\n                                           stream>>>(q_buf,\n                                                     k_buf,\n                                                     Q,\n                                                     bias_Q,\n                                                     K,\n                                                     bias_K,\n                                                     batch_size * seq_len,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len_padded,\n                                                     seq_len * size_per_head,\n                                                     seq_len_padded * size_per_head,\n                                                     q_input_deQFactor_ptr,\n                                                     k_input_deQFactor_ptr,\n                                                     q_output_scale_ptr,\n                                                     k_output_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4,\n                                                     head_num * size_per_head);\n    }\n}\n\ntemplate void invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                          int8_t*       k_buf,\n                                          const int8_t* Q,\n                                          const float*  bias_Q,\n                                          const int8_t* K,\n                                          const float*  bias_K,\n                                          const int     batch_size,\n                                          const int     seq_len,\n                                          const int     head_num,\n                                          const int     size_per_head,\n                                          const float*  q_input_deQFactor_ptr,\n                                          const float*  k_input_deQFactor_ptr,\n                                          const float*  q_output_scale_ptr,\n                                          const float*  k_output_scale_ptr,\n                                          bool          use_ORDER_COL32_2R_4R4,\n                                          cudaStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                          int8_t*       k_buf,\n                                          const int8_t* Q,\n                                          const half*   bias_Q,\n                                          const int8_t* K,\n                                          const half*   bias_K,\n                                          const int     batch_size,\n                                          const int     seq_len,\n                                          const int     head_num,\n                                          const int     size_per_head,\n                                          const float*  q_input_deQFactor_ptr,\n                                          const float*  k_input_deQFactor_ptr,\n                                          const float*  q_output_scale_ptr,\n                                          const float*  k_output_scale_ptr,\n                                          bool          use_ORDER_COL32_2R_4R4,\n                                          cudaStream_t  stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C or\n//  CUBLASLT_ORDER_COL32_2R_4R4\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding(int8_t*        q_buf_,\n                                                      int8_t*        k_buf_,\n                                                      const int32_t* Q,\n                                                      const T*       bias_Q,\n                                                      const int32_t* K,\n                                                      const T*       bias_K,\n                                                      const int*     sequence_id_offset,\n                                                      const int      valid_word_num,\n                                                      const int      m,\n                                                      const int      batch_size,\n                                                      const int      seq_len,\n                                                      const int      head_num,\n                                                      const int      size_per_head,\n                                                      int            stride,\n                                                      const float*   q_weight_amax,\n                                                      const float*   q_input_deQFactor_div127_ptr,\n                                                      const float*   k_weight_amax,\n                                                      const float*   k_input_deQFactor_div127_ptr,\n                                                      const float*   q_output_scale_ptr,\n                                                      const float*   k_output_scale_ptr,\n                                                      bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                            int8_t*        k_buf,\n                                            const int32_t* Q,\n                                            const T*       bias_Q,\n                                            const int32_t* K,\n                                            const T*       bias_K,\n                                            const int*     sequence_id_offset,\n                                            const int      valid_word_num,\n                                            const int      batch_size,\n                                            const int      seq_len,\n                                            const int      head_num,\n                                            const int      size_per_head,\n                                            const float*   q_weight_amax,\n                                            const float*   q_input_deQFactor_div127_ptr,\n                                            const float*   k_weight_amax,\n                                            const float*   k_input_deQFactor_div127_ptr,\n                                            const float*   q_output_scale_ptr,\n                                            const float*   k_output_scale_ptr,\n                                            bool           use_ORDER_COL32_2R_4R4,\n                                            cudaStream_t   stream)\n{\n    add_QK_bias_transform_rebuild_padding<<<dim3(valid_word_num * 2),\n                                            dim3((head_num * size_per_head) / 4),\n                                            0,\n                                            stream>>>(q_buf,\n                                                      k_buf,\n                                                      Q,\n                                                      bias_Q,\n                                                      K,\n                                                      bias_K,\n                                                      sequence_id_offset,\n                                                      valid_word_num,\n                                                      batch_size * seq_len,\n                                                      batch_size,\n                                                      seq_len,\n                                                      head_num,\n                                                      size_per_head,\n                                                      seq_len * size_per_head,\n                                                      q_weight_amax,\n                                                      q_input_deQFactor_div127_ptr,\n                                                      k_weight_amax,\n                                                      k_input_deQFactor_div127_ptr,\n                                                      q_output_scale_ptr,\n                                                      k_output_scale_ptr,\n                                                      use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                                     int8_t*        k_buf,\n                                                     const int32_t* Q,\n                                                     const float*   bias_Q,\n                                                     const int32_t* K,\n                                                     const float*   bias_K,\n                                                     const int*     sequence_id_offset,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     const float*   q_weight_amax,\n                                                     const float*   q_input_deQFactor_div127_ptr,\n                                                     const float*   k_weight_amax,\n                                                     const float*   k_input_deQFactor_div127_ptr,\n                                                     const float*   q_output_scale_ptr,\n                                                     const float*   k_output_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4,\n                                                     cudaStream_t   stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                                     int8_t*        k_buf,\n                                                     const int32_t* Q,\n                                                     const half*    bias_Q,\n                                                     const int32_t* K,\n                                                     const half*    bias_K,\n                                                     const int*     sequence_id_offset,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     const float*   q_weight_amax,\n                                                     const float*   q_input_deQFactor_div127_ptr,\n                                                     const float*   k_weight_amax,\n                                                     const float*   k_input_deQFactor_div127_ptr,\n                                                     const float*   q_output_scale_ptr,\n                                                     const float*   k_output_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4,\n                                                     cudaStream_t   stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C or\n//  CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding(int8_t*       q_buf_,\n                                                      int8_t*       k_buf_,\n                                                      const int8_t* Q,\n                                                      const T*      bias_Q,\n                                                      const int8_t* K,\n                                                      const T*      bias_K,\n                                                      const int*    sequence_id_offset,\n                                                      const int     valid_word_num,\n                                                      const int     m,\n                                                      const int     batch_size,\n                                                      const int     seq_len,\n                                                      const int     head_num,\n                                                      const int     size_per_head,\n                                                      int           stride,\n                                                      const float*  q_deQFactor_ptr,\n                                                      const float*  k_deQFactor_ptr,\n                                                      const float*  q_output_scale_ptr,\n                                                      const float*  k_output_scale_ptr,\n                                                      bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  seq_len_padded = (seq_len + 31)/32*32;\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n//  or CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding_varlen(int8_t*       q_buf_,\n                                                             int8_t*       k_buf_,\n                                                             const int8_t* Q,\n                                                             const T*      bias_Q,\n                                                             const int8_t* K,\n                                                             const T*      bias_K,\n                                                             const int*    sequence_id_offset,\n                                                             const int     valid_word_num,\n                                                             const int     m,\n                                                             const int     batch_size,\n                                                             const int     seq_len,\n                                                             const int     seq_len_padded,\n                                                             const int     head_num,\n                                                             const int     size_per_head,\n                                                             int           stride_q,\n                                                             int           stride_k,\n                                                             const float*  q_deQFactor_ptr,\n                                                             const float*  k_deQFactor_ptr,\n                                                             const float*  q_output_scale_ptr,\n                                                             const float*  k_output_scale_ptr,\n                                                             bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len or seq_len_padded, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len) or (COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int stride = (qk_id != 1) ? stride_q : stride_k;\n    const int len    = (qk_id != 1) ? seq_len : seq_len_padded;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                            int8_t*       k_buf,\n                                            const int8_t* Q,\n                                            const T*      bias_Q,\n                                            const int8_t* K,\n                                            const T*      bias_K,\n                                            const int*    sequence_id_offset,\n                                            const int     valid_word_num,\n                                            const int     batch_size,\n                                            const int     seq_len,\n                                            const int     head_num,\n                                            const int     size_per_head,\n                                            const float*  q_deQFactor_ptr,\n                                            const float*  k_deQFactor_ptr,\n                                            const float*  q_output_scale_ptr,\n                                            const float*  k_output_scale_ptr,\n                                            bool          use_ORDER_COL32_2R_4R4,\n                                            cudaStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_QK_bias_transform_rebuild_padding_varlen<<<dim3(valid_word_num * 2),\n                                                   dim3((head_num * size_per_head) / 4),\n                                                   0,\n                                                   stream>>>(q_buf,\n                                                             k_buf,\n                                                             Q,\n                                                             bias_Q,\n                                                             K,\n                                                             bias_K,\n                                                             sequence_id_offset,\n                                                             valid_word_num,\n                                                             batch_size * seq_len,\n                                                             batch_size,\n                                                             seq_len,\n                                                             seq_len_padded,\n                                                             head_num,\n                                                             size_per_head,\n                                                             seq_len * size_per_head,\n                                                             seq_len_padded * size_per_head,\n                                                             q_deQFactor_ptr,\n                                                             k_deQFactor_ptr,\n                                                             q_output_scale_ptr,\n                                                             k_output_scale_ptr,\n                                                             use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                                     int8_t*       k_buf,\n                                                     const int8_t* Q,\n                                                     const float*  bias_Q,\n                                                     const int8_t* K,\n                                                     const float*  bias_K,\n                                                     const int*    sequence_id_offset,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     const float*  q_deQFactor_ptr,\n                                                     const float*  k_deQFactor_ptr,\n                                                     const float*  q_output_scale_ptr,\n                                                     const float*  k_output_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4,\n                                                     cudaStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                                     int8_t*       k_buf,\n                                                     const int8_t* Q,\n                                                     const half*   bias_Q,\n                                                     const int8_t* K,\n                                                     const half*   bias_K,\n                                                     const int*    sequence_id_offset,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     const float*  q_deQFactor_ptr,\n                                                     const float*  k_deQFactor_ptr,\n                                                     const float*  q_output_scale_ptr,\n                                                     const float*  k_output_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4,\n                                                     cudaStream_t  stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K row-major matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  seq_len_padded = (seq_len + 31)/32*32;\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n//  or CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding_varlen_row(int8_t*       q_buf_,\n                                                                 int8_t*       k_buf_,\n                                                                 const int8_t* Q,\n                                                                 const T*      bias_Q,\n                                                                 const int8_t* K,\n                                                                 const T*      bias_K,\n                                                                 const int*    sequence_id_offset,\n                                                                 const int     valid_word_num,\n                                                                 const int     m,\n                                                                 const int     batch_size,\n                                                                 const int     seq_len,\n                                                                 const int     seq_len_padded,\n                                                                 const int     head_num,\n                                                                 const int     size_per_head,\n                                                                 int           stride_q,\n                                                                 int           stride_k,\n                                                                 const float*  q_deQFactor_ptr,\n                                                                 const float*  k_deQFactor_ptr,\n                                                                 const float*  q_output_scale_ptr,\n                                                                 const float*  k_output_scale_ptr,\n                                                                 bool          use_ORDER_COL32_2R_4R4,\n                                                                 const int     head_num_x_size_per_head)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4   = threadIdx.x << 2;\n    int batch_seq_id = blockIdx.x % valid_word_num;\n    int m_full_idx   = (valid_word_num != m) ? (batch_seq_id + __ldg(sequence_id_offset + batch_seq_id)) : batch_seq_id;\n    int batch_id     = m_full_idx / seq_len;\n    int head_id      = threadIdx4 / size_per_head;\n    int id_in_head   = threadIdx4 % size_per_head;\n    int word_id      = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id = (batch_seq_id * head_num_x_size_per_head + threadIdx4) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len or seq_len_padded, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len) or (COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int stride = (qk_id != 1) ? stride_q : stride_k;\n    const int len    = (qk_id != 1) ? seq_len : seq_len_padded;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                               int8_t*       k_buf,\n                                               const int8_t* Q,\n                                               const T*      bias_Q,\n                                               const int8_t* K,\n                                               const T*      bias_K,\n                                               const int*    sequence_id_offset,\n                                               const int     valid_word_num,\n                                               const int     batch_size,\n                                               const int     seq_len,\n                                               const int     head_num,\n                                               const int     size_per_head,\n                                               const float*  q_deQFactor_ptr,\n                                               const float*  k_deQFactor_ptr,\n                                               const float*  q_output_scale_ptr,\n                                               const float*  k_output_scale_ptr,\n                                               bool          use_ORDER_COL32_2R_4R4,\n                                               cudaStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_QK_bias_transform_rebuild_padding_varlen_row<<<dim3(valid_word_num * 2),\n                                                       dim3((head_num * size_per_head) / 4),\n                                                       0,\n                                                       stream>>>(q_buf,\n                                                                 k_buf,\n                                                                 Q,\n                                                                 bias_Q,\n                                                                 K,\n                                                                 bias_K,\n                                                                 sequence_id_offset,\n                                                                 valid_word_num,\n                                                                 batch_size * seq_len,\n                                                                 batch_size,\n                                                                 seq_len,\n                                                                 seq_len_padded,\n                                                                 head_num,\n                                                                 size_per_head,\n                                                                 seq_len * size_per_head,\n                                                                 seq_len_padded * size_per_head,\n                                                                 q_deQFactor_ptr,\n                                                                 k_deQFactor_ptr,\n                                                                 q_output_scale_ptr,\n                                                                 k_output_scale_ptr,\n                                                                 use_ORDER_COL32_2R_4R4,\n                                                                 head_num * size_per_head);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                                        int8_t*       k_buf,\n                                                        const int8_t* Q,\n                                                        const float*  bias_Q,\n                                                        const int8_t* K,\n                                                        const float*  bias_K,\n                                                        const int*    sequence_id_offset,\n                                                        const int     valid_word_num,\n                                                        const int     batch_size,\n                                                        const int     seq_len,\n                                                        const int     head_num,\n                                                        const int     size_per_head,\n                                                        const float*  q_deQFactor_ptr,\n                                                        const float*  k_deQFactor_ptr,\n                                                        const float*  q_output_scale_ptr,\n                                                        const float*  k_output_scale_ptr,\n                                                        bool          use_ORDER_COL32_2R_4R4,\n                                                        cudaStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                                        int8_t*       k_buf,\n                                                        const int8_t* Q,\n                                                        const half*   bias_Q,\n                                                        const int8_t* K,\n                                                        const half*   bias_K,\n                                                        const int*    sequence_id_offset,\n                                                        const int     valid_word_num,\n                                                        const int     batch_size,\n                                                        const int     seq_len,\n                                                        const int     head_num,\n                                                        const int     size_per_head,\n                                                        const float*  q_deQFactor_ptr,\n                                                        const float*  k_deQFactor_ptr,\n                                                        const float*  q_output_scale_ptr,\n                                                        const float*  k_output_scale_ptr,\n                                                        bool          use_ORDER_COL32_2R_4R4,\n                                                        cudaStream_t  stream);\n\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int32_t Input int8_t Output seq_len, size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per axis quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform(int8_t*        v_buf_,\n                                     const int32_t* V,\n                                     const T*       V_bias,\n                                     const int      batch_size,\n                                     const int      seq_len,\n                                     const int      head_num,\n                                     const int      size_per_head,\n                                     int            stride,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr,\n                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = (((col >> 5) << 5) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    float tmp;\n    float scale;\n\n    // const half2* bias_ptr2 = (const half2*)bias_ptr;\n    // half2 tmp2;\n\n    // tmp2 = __ldg(&bias_ptr2[col >> 1]);\n\n    scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n    tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n    shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n    shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n    // tmp2 = __ldg(&bias_ptr2[(col >> 1) + 1]);\n\n    scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n    shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n    shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<>\n__global__ void add_V_bias_transform(int8_t*        v_buf_,\n                                     const int32_t* V,\n                                     const half*    V_bias,\n                                     const int      batch_size,\n                                     const int      seq_len,\n                                     const int      head_num,\n                                     const int      size_per_head,\n                                     int            stride,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr,\n                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = ((col & 0xffffffe0) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    int   col_2 = col >> 1;\n    float scale;\n\n    const half2* bias_ptr2 = (const half2*)V_bias;\n    half2        tmp2;\n\n    tmp2 = __ldg(bias_ptr2 + col_2);\n\n    scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n    scale               = scale + static_cast<float>(tmp2.x);\n    shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.y);\n    shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n    tmp2 = __ldg(bias_ptr2 + col_2 + 1);\n\n    scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.x);\n    shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.y);\n    shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen(int8_t*        v_buf_,\n                                            const int32_t* V,\n                                            const T*       V_bias,\n                                            const int      batch_size,\n                                            const int      seq_len,\n                                            const int      head_num,\n                                            const int      size_per_head,\n                                            int            stride,\n                                            const float*   weight_amax,\n                                            const float*   input_deQFactor_div127_ptr,\n                                            const float*   out_scale_ptr,\n                                            bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = ((col & 0xffffffe0) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    float scale;\n\n    if (word_id < seq_len) {\n        const T* bias_ptr = V_bias;\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        scale               = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransform(int8_t*        v_buf,\n                             const int32_t* V,\n                             const T*       V_bias,\n                             const int      batch_size,\n                             const int      seq_len,\n                             const int      head_num,\n                             const int      size_per_head,\n                             const float*   weight_amax,\n                             const float*   input_deQFactor_div127_ptr,\n                             const float*   out_scale_ptr,\n                             bool           use_ORDER_COL32_2R_4R4,\n                             cudaStream_t   stream)\n{\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num), dim3(8, 32), 0, stream>>>(\n            v_buf,\n            V,\n            V_bias,\n            batch_size,\n            seq_len,\n            head_num,\n            size_per_head,\n            seq_len * size_per_head,\n            weight_amax,\n            input_deQFactor_div127_ptr,\n            out_scale_ptr,\n            use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len_padded * size_per_head,\n                                                weight_amax,\n                                                input_deQFactor_div127_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddVBiasTransform(int8_t*        v_buf,\n                                      const int32_t* V,\n                                      const float*   V_bias,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      const float*   weight_amax,\n                                      const float*   input_deQFactor_div127_ptr,\n                                      const float*   out_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4,\n                                      cudaStream_t   stream);\n\ntemplate void invokeAddVBiasTransform(int8_t*        v_buf,\n                                      const int32_t* V,\n                                      const half*    V_bias,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      const float*   weight_amax,\n                                      const float*   input_deQFactor_div127_ptr,\n                                      const float*   out_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4,\n                                      cudaStream_t   stream);\n\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen(int8_t*       v_buf_,\n                                            const int8_t* V,\n                                            const T*      V_bias,\n                                            const int     batch_size,\n                                            const int     seq_len,\n                                            const int     head_num,\n                                            const int     size_per_head,\n                                            const int     seq_len_padded,\n                                            int           stride,\n                                            const float*  input_deQFactor_ptr,\n                                            const float*  out_scale_ptr,\n                                            bool          use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float       out_scale       = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    int col, row;\n    // for shm row-major\n    int   sh_col = threadIdx4;\n    int   sh_row = threadIdx.y;\n    char4 dataTmp;\n    if (word_id < seq_len) {\n        // for V layout (batch_size*seq_len, head_num*size_per_head)\n        col       = head_id * size_per_head + id_in_size;\n        row       = batch_id * seq_len + word_id;\n        int inIdx = (((col >> 5) << 5) * batch_size * seq_len + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * input_deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransform(int8_t*       v_buf,\n                             const int8_t* V,\n                             const T*      V_bias,\n                             const int     batch_size,\n                             const int     seq_len,\n                             const int     head_num,\n                             const int     size_per_head,\n                             const float*  input_deQFactor_ptr,\n                             const float*  out_scale_ptr,\n                             bool          use_ORDER_COL32_2R_4R4,\n                             cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len,\n                                                seq_len * size_per_head,\n                                                input_deQFactor_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len_padded,\n                                                seq_len_padded * size_per_head,\n                                                input_deQFactor_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddVBiasTransform(int8_t*       v_buf,\n                                      const int8_t* V,\n                                      const float*  V_bias,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      const float*  input_deQFactor_ptr,\n                                      const float*  out_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4,\n                                      cudaStream_t  stream);\n\ntemplate void invokeAddVBiasTransform(int8_t*       v_buf,\n                                      const int8_t* V,\n                                      const half*   V_bias,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      const float*  input_deQFactor_ptr,\n                                      const float*  out_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4,\n                                      cudaStream_t  stream);\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, row major\n// seq_len_padded = (seq_len+31)/32*32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen_row(int8_t*       v_buf_,\n                                                const int8_t* V,\n                                                const T*      V_bias,\n                                                const int     batch_size,\n                                                const int     seq_len,\n                                                const int     head_num,\n                                                const int     size_per_head,\n                                                const int     seq_len_padded,\n                                                int           stride,\n                                                const float*  input_deQFactor_ptr,\n                                                const float*  out_scale_ptr,\n                                                bool          use_ORDER_COL32_2R_4R4,\n                                                const int     head_num_x_size_per_head)\n{\n    const float       input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float       out_scale       = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    int col, row;\n    // for shm row-major\n    int   sh_col = threadIdx4;\n    int   sh_row = threadIdx.y;\n    char4 dataTmp;\n    if (word_id < seq_len) {\n        // for V layout (batch_size*seq_len, head_num*size_per_head)\n        col       = head_id * size_per_head + id_in_size;\n        row       = batch_id * seq_len + word_id;\n        int inIdx = (row * head_num_x_size_per_head + col) >> 2;\n\n        float tmp;\n        float scale;\n\n        dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * input_deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10)\n            + (((((((row_in_tile % 8) / 2) * 4) + (row_in_tile / 8)) * 2) + (row_in_tile % 2)) * 32) + col_in_tile\n            // (((((((row_in_tile%8)>>1)<<2)+(row_in_tile>>3))<<1)+(row_in_tile&1))<<5)+col_in_tile\n        );\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                const int8_t* V,\n                                const T*      V_bias,\n                                const int     batch_size,\n                                const int     seq_len,\n                                const int     head_num,\n                                const int     size_per_head,\n                                const float*  input_deQFactor_ptr,\n                                const float*  out_scale_ptr,\n                                bool          use_ORDER_COL32_2R_4R4,\n                                cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform_varlen_row<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                          dim3(8, 32),\n                                          0,\n                                          stream>>>(v_buf,\n                                                    V,\n                                                    V_bias,\n                                                    batch_size,\n                                                    seq_len,\n                                                    head_num,\n                                                    size_per_head,\n                                                    seq_len,\n                                                    seq_len * size_per_head,\n                                                    input_deQFactor_ptr,\n                                                    out_scale_ptr,\n                                                    use_ORDER_COL32_2R_4R4,\n                                                    head_num * size_per_head);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen_row<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                          dim3(8, 32),\n                                          0,\n                                          stream>>>(v_buf,\n                                                    V,\n                                                    V_bias,\n                                                    batch_size,\n                                                    seq_len,\n                                                    head_num,\n                                                    size_per_head,\n                                                    seq_len_padded,\n                                                    seq_len_padded * size_per_head,\n                                                    input_deQFactor_ptr,\n                                                    out_scale_ptr,\n                                                    use_ORDER_COL32_2R_4R4,\n                                                    head_num * size_per_head);\n    }\n}\n\ntemplate void invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                         const int8_t* V,\n                                         const float*  V_bias,\n                                         const int     batch_size,\n                                         const int     seq_len,\n                                         const int     head_num,\n                                         const int     size_per_head,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr,\n                                         bool          use_ORDER_COL32_2R_4R4,\n                                         cudaStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                         const int8_t* V,\n                                         const half*   V_bias,\n                                         const int     batch_size,\n                                         const int     seq_len,\n                                         const int     head_num,\n                                         const int     size_per_head,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr,\n                                         bool          use_ORDER_COL32_2R_4R4,\n                                         cudaStream_t  stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int32_t Input int8_t Output seq_len, size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per axis quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*        v_buf_,\n                                                     const int32_t* V,\n                                                     const T*       V_bias,\n                                                     const int*     sequence_id_map,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     int            stride,\n                                                     const float*   weight_amax,\n                                                     const float*   input_deQFactor_div127_ptr,\n                                                     const float*   out_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31)));\n\n        float tmp;\n        float scale;\n\n        const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n        const float out_scale              = __ldg(out_scale_ptr);\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*        v_buf_,\n                                                     const int32_t* V,\n                                                     const half*    V_bias,\n                                                     const int*     sequence_id_map,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     int            stride,\n                                                     const float*   weight_amax,\n                                                     const float*   input_deQFactor_div127_ptr,\n                                                     const float*   out_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row >= 0) {\n        const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n        const float out_scale              = __ldg(out_scale_ptr);\n        col                                = head_id * size_per_head + id_in_size;\n        int   inIdx                        = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31)));\n        int   col_2                        = col >> 1;\n        float scale;\n\n        const half2* bias_ptr2 = (const half2*)V_bias;\n        half2        tmp2;\n\n        tmp2 = __ldg(bias_ptr2 + col_2);\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        scale               = scale + static_cast<float>(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n        tmp2 = __ldg(bias_ptr2 + col_2 + 1);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                           const int32_t* V,\n                                           const T*       V_bias,\n                                           const int*     sequence_id_map,\n                                           const int      valid_word_num,\n                                           const int      batch_size,\n                                           const int      seq_len,\n                                           const int      head_num,\n                                           const int      size_per_head,\n                                           const float*   weight_amax,\n                                           const float*   input_deQFactor_div127_ptr,\n                                           const float*   out_scale_ptr,\n                                           bool           use_ORDER_COL32_2R_4R4,\n                                           cudaStream_t   stream)\n{\n    add_V_bias_transform_rebuild_padding<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                           dim3(8, 32),\n                                           0,\n                                           stream>>>(v_buf,\n                                                     V,\n                                                     V_bias,\n                                                     sequence_id_map,\n                                                     valid_word_num,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len * size_per_head,\n                                                     weight_amax,\n                                                     input_deQFactor_div127_ptr,\n                                                     out_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                                    const int32_t* V,\n                                                    const float*   V_bias,\n                                                    const int*     sequence_id_map,\n                                                    const int      valid_word_num,\n                                                    const int      batch_size,\n                                                    const int      seq_len,\n                                                    const int      head_num,\n                                                    const int      size_per_head,\n                                                    const float*   weight_amax,\n                                                    const float*   input_deQFactor_div127_ptr,\n                                                    const float*   out_scale_ptr,\n                                                    bool           use_ORDER_COL32_2R_4R4,\n                                                    cudaStream_t   stream);\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                                    const int32_t* V,\n                                                    const half*    V_bias,\n                                                    const int*     sequence_id_map,\n                                                    const int      valid_word_num,\n                                                    const int      batch_size,\n                                                    const int      seq_len,\n                                                    const int      head_num,\n                                                    const int      size_per_head,\n                                                    const float*   weight_amax,\n                                                    const float*   input_deQFactor_div127_ptr,\n                                                    const float*   out_scale_ptr,\n                                                    bool           use_ORDER_COL32_2R_4R4,\n                                                    cudaStream_t   stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a multiple of 32 grid =\n// (size_per_head/32, seq_len/32, batch_size*head_num) block = (8, 32); using char4 per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*       v_buf_,\n                                                     const int8_t* V,\n                                                     const T*      V_bias,\n                                                     const int*    sequence_id_map,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     int           stride,\n                                                     const float*  deQFactor_ptr,\n                                                     const float*  out_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a\n// multiple of 32 grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num) block = (8, 32); using char4 per\n// tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding_varlen(int8_t*       v_buf_,\n                                                            const int8_t* V,\n                                                            const T*      V_bias,\n                                                            const int*    sequence_id_map,\n                                                            const int     valid_word_num,\n                                                            const int     batch_size,\n                                                            const int     seq_len,\n                                                            const int     seq_len_padded,\n                                                            const int     head_num,\n                                                            const int     size_per_head,\n                                                            int           stride,\n                                                            const float*  deQFactor_ptr,\n                                                            const float*  out_scale_ptr,\n                                                            bool          use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = word_id < seq_len ? __ldg(sequence_id_map + batch_id * seq_len + word_id) : -1;\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                           const int8_t* V,\n                                           const T*      V_bias,\n                                           const int*    sequence_id_map,\n                                           const int     valid_word_num,\n                                           const int     batch_size,\n                                           const int     seq_len,\n                                           const int     head_num,\n                                           const int     size_per_head,\n                                           const float*  deQFactor_ptr,\n                                           const float*  out_scale_ptr,\n                                           bool          use_ORDER_COL32_2R_4R4,\n                                           cudaStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_V_bias_transform_rebuild_padding_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                                  dim3(8, 32),\n                                                  0,\n                                                  stream>>>(v_buf,\n                                                            V,\n                                                            V_bias,\n                                                            sequence_id_map,\n                                                            valid_word_num,\n                                                            batch_size,\n                                                            seq_len,\n                                                            seq_len_padded,\n                                                            head_num,\n                                                            size_per_head,\n                                                            seq_len_padded * size_per_head,\n                                                            deQFactor_ptr,\n                                                            out_scale_ptr,\n                                                            use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                                    const int8_t* V,\n                                                    const float*  V_bias,\n                                                    const int*    sequence_id_map,\n                                                    const int     valid_word_num,\n                                                    const int     batch_size,\n                                                    const int     seq_len,\n                                                    const int     head_num,\n                                                    const int     size_per_head,\n                                                    const float*  deQFactor_ptr,\n                                                    const float*  out_scale_ptr,\n                                                    bool          use_ORDER_COL32_2R_4R4,\n                                                    cudaStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                                    const int8_t* V,\n                                                    const half*   V_bias,\n                                                    const int*    sequence_id_map,\n                                                    const int     valid_word_num,\n                                                    const int     batch_size,\n                                                    const int     seq_len,\n                                                    const int     head_num,\n                                                    const int     size_per_head,\n                                                    const float*  deQFactor_ptr,\n                                                    const float*  out_scale_ptr,\n                                                    bool          use_ORDER_COL32_2R_4R4,\n                                                    cudaStream_t  stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, row major\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a\n// multiple of 32 grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num) block = (8, 32); using char4 per\n// tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding_varlen_row(int8_t*       v_buf_,\n                                                                const int8_t* V,\n                                                                const T*      V_bias,\n                                                                const int*    sequence_id_map,\n                                                                const int     valid_word_num,\n                                                                const int     batch_size,\n                                                                const int     seq_len,\n                                                                const int     seq_len_padded,\n                                                                const int     head_num,\n                                                                const int     size_per_head,\n                                                                int           stride,\n                                                                const float*  deQFactor_ptr,\n                                                                const float*  out_scale_ptr,\n                                                                bool          use_ORDER_COL32_2R_4R4,\n                                                                const int     head_num_x_size_per_head)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = word_id < seq_len ? __ldg(sequence_id_map + batch_id * seq_len + word_id) : -1;\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = (row * head_num_x_size_per_head + col) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10)\n            + (((((((row_in_tile % 8) / 2) * 4) + (row_in_tile / 8)) * 2) + (row_in_tile % 2)) * 32) + col_in_tile\n            // (((((((row_in_tile%8)>>1)<<2)+(row_in_tile>>3))<<1)+(row_in_tile&1))<<5)+col_in_tile\n        );\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                              const int8_t* V,\n                                              const T*      V_bias,\n                                              const int*    sequence_id_map,\n                                              const int     valid_word_num,\n                                              const int     batch_size,\n                                              const int     seq_len,\n                                              const int     head_num,\n                                              const int     size_per_head,\n                                              const float*  deQFactor_ptr,\n                                              const float*  out_scale_ptr,\n                                              bool          use_ORDER_COL32_2R_4R4,\n                                              cudaStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_V_bias_transform_rebuild_padding_varlen_row<<<\n        dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n        dim3(8, 32),\n        0,\n        stream>>>(v_buf,\n                  V,\n                  V_bias,\n                  sequence_id_map,\n                  valid_word_num,\n                  batch_size,\n                  seq_len,\n                  seq_len_padded,\n                  head_num,\n                  size_per_head,\n                  seq_len_padded * size_per_head,\n                  deQFactor_ptr,\n                  out_scale_ptr,\n                  use_ORDER_COL32_2R_4R4,\n                  head_num * size_per_head);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                                       const int8_t* V,\n                                                       const float*  V_bias,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  deQFactor_ptr,\n                                                       const float*  out_scale_ptr,\n                                                       bool          use_ORDER_COL32_2R_4R4,\n                                                       cudaStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                                       const int8_t* V,\n                                                       const half*   V_bias,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  deQFactor_ptr,\n                                                       const float*  out_scale_ptr,\n                                                       bool          use_ORDER_COL32_2R_4R4,\n                                                       cudaStream_t  stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\n// build a mapping for fullData to removePaddingData\n// grid((valid_word_num+63)/64)\n// block(64)\n__global__ void mappingRemovePaddingData(int* mapping, const int* sequence_id_offset, const int valid_word_num)\n{\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx < valid_word_num) {\n        mapping[idx + __ldg(sequence_id_offset + idx)] = idx;\n    }\n}\n\nvoid invokeMappingRemovePaddingData(const int    batch_size,\n                                    const int    seq_len,\n                                    const int    valid_word_num,\n                                    int*         mapping,\n                                    const int*   sequence_id_offset,\n                                    hipStream_t stream)\n{\n    hipMemsetAsync(mapping, -1, batch_size * seq_len * sizeof(int), stream);\n    mappingRemovePaddingData<<<dim3((valid_word_num + 63) / 64), dim3(64), 0, stream>>>(\n        mapping, sequence_id_offset, valid_word_num);\n}\n\n// add_QK_bias_transform for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform(int8_t*        q_buf_,\n                                      int8_t*        k_buf_,\n                                      const int32_t* Q,\n                                      const T*       bias_Q,\n                                      const int32_t* K,\n                                      const T*       bias_K,\n                                      const int      m,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      int            stride,\n                                      const float*   q_weight_amax,\n                                      const float*   q_input_deQFactor_div127_ptr,\n                                      const float*   k_weight_amax,\n                                      const float*   k_input_deQFactor_div127_ptr,\n                                      const float*   q_output_scale_ptr,\n                                      const float*   k_output_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / m;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform_varlen for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen(int8_t*        q_buf_,\n                                             int8_t*        k_buf_,\n                                             const int32_t* Q,\n                                             const T*       bias_Q,\n                                             const int32_t* K,\n                                             const T*       bias_K,\n                                             const int      m,\n                                             const int      batch_size,\n                                             const int      seq_len,\n                                             const int      head_num,\n                                             const int      size_per_head,\n                                             const int      seq_len_padded,\n                                             int            stride_q,\n                                             int            stride_k,\n                                             const float*   q_weight_amax,\n                                             const float*   q_input_deQFactor_div127_ptr,\n                                             const float*   k_weight_amax,\n                                             const float*   k_input_deQFactor_div127_ptr,\n                                             const float*   q_output_scale_ptr,\n                                             const float*   k_output_scale_ptr,\n                                             bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / m;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransform(int8_t*        q_buf,\n                              int8_t*        k_buf,\n                              const int32_t* Q,\n                              const T*       bias_Q,\n                              const int32_t* K,\n                              const T*       bias_K,\n                              const int      batch_size,\n                              const int      seq_len,\n                              const int      head_num,\n                              const int      size_per_head,\n                              const float*   q_weight_amax,\n                              const float*   q_input_deQFactor_div127_ptr,\n                              const float*   k_weight_amax,\n                              const float*   k_input_deQFactor_div127_ptr,\n                              const float*   q_output_scale_ptr,\n                              const float*   k_output_scale_ptr,\n                              bool           use_ORDER_COL32_2R_4R4,\n                              hipStream_t   stream)\n{\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform<<<dim3(batch_size * seq_len * 2), dim3((head_num * size_per_head) / 4), 0, stream>>>(\n            q_buf,\n            k_buf,\n            Q,\n            bias_Q,\n            K,\n            bias_K,\n            batch_size * seq_len,\n            batch_size,\n            seq_len,\n            head_num,\n            size_per_head,\n            seq_len * size_per_head,\n            q_weight_amax,\n            q_input_deQFactor_div127_ptr,\n            k_weight_amax,\n            k_input_deQFactor_div127_ptr,\n            q_output_scale_ptr,\n            k_output_scale_ptr,\n            use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len_padded,\n                                                 seq_len * size_per_head,\n                                                 seq_len_padded * size_per_head,\n                                                 q_weight_amax,\n                                                 q_input_deQFactor_div127_ptr,\n                                                 k_weight_amax,\n                                                 k_input_deQFactor_div127_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddQKBiasTransform(int8_t*        q_buf,\n                                       int8_t*        k_buf,\n                                       const int32_t* Q,\n                                       const float*   bias_Q,\n                                       const int32_t* K,\n                                       const float*   bias_K,\n                                       const int      batch_size,\n                                       const int      seq_len,\n                                       const int      head_num,\n                                       const int      size_per_head,\n                                       const float*   q_weight_amax,\n                                       const float*   q_input_deQFactor_div127_ptr,\n                                       const float*   k_weight_amax,\n                                       const float*   k_input_deQFactor_div127_ptr,\n                                       const float*   q_output_scale_ptr,\n                                       const float*   k_output_scale_ptr,\n                                       bool           use_ORDER_COL32_2R_4R4,\n                                       hipStream_t   stream);\n\ntemplate void invokeAddQKBiasTransform(int8_t*        q_buf,\n                                       int8_t*        k_buf,\n                                       const int32_t* Q,\n                                       const half*    bias_Q,\n                                       const int32_t* K,\n                                       const half*    bias_K,\n                                       const int      batch_size,\n                                       const int      seq_len,\n                                       const int      head_num,\n                                       const int      size_per_head,\n                                       const float*   q_weight_amax,\n                                       const float*   q_input_deQFactor_div127_ptr,\n                                       const float*   k_weight_amax,\n                                       const float*   k_input_deQFactor_div127_ptr,\n                                       const float*   q_output_scale_ptr,\n                                       const float*   k_output_scale_ptr,\n                                       bool           use_ORDER_COL32_2R_4R4,\n                                       hipStream_t   stream);\n\n// add_QK_bias_padding_transform for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.padding seq_len in k_buf_ to a multiple of 32 named seq_len_padded\n// 3.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen(int8_t*       q_buf_,\n                                             int8_t*       k_buf_,\n                                             const int8_t* Q,\n                                             const T*      bias_Q,\n                                             const int8_t* K,\n                                             const T*      bias_K,\n                                             const int     m,\n                                             const int     batch_size,\n                                             const int     seq_len,\n                                             const int     head_num,\n                                             const int     size_per_head,\n                                             const int     seq_len_padded,\n                                             const int     stride_q,\n                                             const int     stride_k,\n                                             const float*  q_input_deQFactor_ptr,\n                                             const float*  k_input_deQFactor_ptr,\n                                             const float*  q_output_scale_ptr,\n                                             const float*  k_output_scale_ptr,\n                                             bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len/seq_len_padded, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len / COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform(int8_t*       q_buf_,\n                                      int8_t*       k_buf_,\n                                      const int8_t* Q,\n                                      const T*      bias_Q,\n                                      const int8_t* K,\n                                      const T*      bias_K,\n                                      const int     m,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      int           stride,\n                                      const float*  q_input_deQFactor_ptr,\n                                      const float*  k_input_deQFactor_ptr,\n                                      const float*  q_output_scale_ptr,\n                                      const float*  k_output_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = (blockIdx.x % m) / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = blockIdx.x % seq_len;\n\n    int data_id = (((threadIdx4 >> 5) << 5) * m + ((blockIdx.x % m) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransform(int8_t*       q_buf,\n                              int8_t*       k_buf,\n                              const int8_t* Q,\n                              const T*      bias_Q,\n                              const int8_t* K,\n                              const T*      bias_K,\n                              const int     batch_size,\n                              const int     seq_len,\n                              const int     head_num,\n                              const int     size_per_head,\n                              const float*  q_input_deQFactor_ptr,\n                              const float*  k_input_deQFactor_ptr,\n                              const float*  q_output_scale_ptr,\n                              const float*  k_output_scale_ptr,\n                              bool          use_ORDER_COL32_2R_4R4,\n                              hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len,\n                                                 seq_len * size_per_head,\n                                                 seq_len * size_per_head,\n                                                 q_input_deQFactor_ptr,\n                                                 k_input_deQFactor_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        // The padding words will not be considered in softmax, so we don't need memset for k_buf_\n        // cudaMemsetAsync(k_buf, 0, batch_size * head_num * seq_len_padded * size_per_head * sizeof(int8_t), stream);\n        add_QK_bias_transform_varlen<<<dim3(batch_size * seq_len * 2),\n                                       dim3((head_num * size_per_head) / 4),\n                                       0,\n                                       stream>>>(q_buf,\n                                                 k_buf,\n                                                 Q,\n                                                 bias_Q,\n                                                 K,\n                                                 bias_K,\n                                                 batch_size * seq_len,\n                                                 batch_size,\n                                                 seq_len,\n                                                 head_num,\n                                                 size_per_head,\n                                                 seq_len_padded,\n                                                 seq_len * size_per_head,\n                                                 seq_len_padded * size_per_head,\n                                                 q_input_deQFactor_ptr,\n                                                 k_input_deQFactor_ptr,\n                                                 q_output_scale_ptr,\n                                                 k_output_scale_ptr,\n                                                 use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddQKBiasTransform(int8_t*       q_buf,\n                                       int8_t*       k_buf,\n                                       const int8_t* Q,\n                                       const float*  bias_Q,\n                                       const int8_t* K,\n                                       const float*  bias_K,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  q_input_deQFactor_ptr,\n                                       const float*  k_input_deQFactor_ptr,\n                                       const float*  q_output_scale_ptr,\n                                       const float*  k_output_scale_ptr,\n                                       bool          use_ORDER_COL32_2R_4R4,\n                                       hipStream_t  stream);\n\ntemplate void invokeAddQKBiasTransform(int8_t*       q_buf,\n                                       int8_t*       k_buf,\n                                       const int8_t* Q,\n                                       const half*   bias_Q,\n                                       const int8_t* K,\n                                       const half*   bias_K,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  q_input_deQFactor_ptr,\n                                       const float*  k_input_deQFactor_ptr,\n                                       const float*  q_output_scale_ptr,\n                                       const float*  k_output_scale_ptr,\n                                       bool          use_ORDER_COL32_2R_4R4,\n                                       hipStream_t  stream);\n\n// add_QK_bias_padding_transform for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.padding seq_len in k_buf_ to a multiple of 32 named seq_len_padded\n// 3.transform each Q K row-major matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are row-major matrixes of m = batch_size * seq_len, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n// only for int8 IO\n// size_per_head must be a multiple of 32\n// grid.x = batch_size * seq_len * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_varlen_row(int8_t*       q_buf_,\n                                                 int8_t*       k_buf_,\n                                                 const int8_t* Q,\n                                                 const T*      bias_Q,\n                                                 const int8_t* K,\n                                                 const T*      bias_K,\n                                                 const int     m,\n                                                 const int     batch_size,\n                                                 const int     seq_len,\n                                                 const int     head_num,\n                                                 const int     size_per_head,\n                                                 const int     seq_len_padded,\n                                                 const int     stride_q,\n                                                 const int     stride_k,\n                                                 const float*  q_input_deQFactor_ptr,\n                                                 const float*  k_input_deQFactor_ptr,\n                                                 const float*  q_output_scale_ptr,\n                                                 const float*  k_output_scale_ptr,\n                                                 bool          use_ORDER_COL32_2R_4R4,\n                                                 const int     head_num_x_size_per_head)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / m;\n\n    data_ptr                    = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4                    = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr                    = qk_id == 0 ? bias_Q : bias_K;\n    const float input_deQFactor = qk_id == 0 ? __ldg(q_input_deQFactor_ptr) : __ldg(k_input_deQFactor_ptr);\n    const float output_scale    = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int threadIdx4   = threadIdx.x << 2;\n    int batch_seq_id = blockIdx.x % m;\n    int batch_id     = (batch_seq_id) / seq_len;\n    int head_id      = threadIdx4 / size_per_head;\n    int id_in_head   = threadIdx4 % size_per_head;\n    int word_id      = blockIdx.x % seq_len;\n\n    int data_id = (batch_seq_id * head_num_x_size_per_head + threadIdx4) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4 = __ldg(data_ptr + data_id);\n    scale      = static_cast<float>(tmp4.x) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * input_deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * input_deQFactor;\n    ;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len/seq_len_padded, n = size_per_head), column-major\n\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len / COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int act_seq_len = (qk_id == 0) ? seq_len : seq_len_padded;\n    const int stride      = (qk_id == 0) ? stride_q : stride_k;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * act_seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                 int8_t*       k_buf,\n                                 const int8_t* Q,\n                                 const T*      bias_Q,\n                                 const int8_t* K,\n                                 const T*      bias_K,\n                                 const int     batch_size,\n                                 const int     seq_len,\n                                 const int     head_num,\n                                 const int     size_per_head,\n                                 const float*  q_input_deQFactor_ptr,\n                                 const float*  k_input_deQFactor_ptr,\n                                 const float*  q_output_scale_ptr,\n                                 const float*  k_output_scale_ptr,\n                                 bool          use_ORDER_COL32_2R_4R4,\n                                 hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_QK_bias_transform_varlen_row<<<dim3(batch_size * seq_len * 2),\n                                           dim3((head_num * size_per_head) / 4),\n                                           0,\n                                           stream>>>(q_buf,\n                                                     k_buf,\n                                                     Q,\n                                                     bias_Q,\n                                                     K,\n                                                     bias_K,\n                                                     batch_size * seq_len,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len,\n                                                     seq_len * size_per_head,\n                                                     seq_len * size_per_head,\n                                                     q_input_deQFactor_ptr,\n                                                     k_input_deQFactor_ptr,\n                                                     q_output_scale_ptr,\n                                                     k_output_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4,\n                                                     head_num * size_per_head);\n    }\n    else {\n        int seq_len_padded = (seq_len + 31) / 32 * 32;\n        // The padding words will not be considered in softmax, so we don't need memset for k_buf_\n        // cudaMemsetAsync(k_buf, 0, batch_size * head_num * seq_len_padded * size_per_head * sizeof(int8_t), stream);\n        add_QK_bias_transform_varlen_row<<<dim3(batch_size * seq_len * 2),\n                                           dim3((head_num * size_per_head) / 4),\n                                           0,\n                                           stream>>>(q_buf,\n                                                     k_buf,\n                                                     Q,\n                                                     bias_Q,\n                                                     K,\n                                                     bias_K,\n                                                     batch_size * seq_len,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len_padded,\n                                                     seq_len * size_per_head,\n                                                     seq_len_padded * size_per_head,\n                                                     q_input_deQFactor_ptr,\n                                                     k_input_deQFactor_ptr,\n                                                     q_output_scale_ptr,\n                                                     k_output_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4,\n                                                     head_num * size_per_head);\n    }\n}\n\ntemplate void invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                          int8_t*       k_buf,\n                                          const int8_t* Q,\n                                          const float*  bias_Q,\n                                          const int8_t* K,\n                                          const float*  bias_K,\n                                          const int     batch_size,\n                                          const int     seq_len,\n                                          const int     head_num,\n                                          const int     size_per_head,\n                                          const float*  q_input_deQFactor_ptr,\n                                          const float*  k_input_deQFactor_ptr,\n                                          const float*  q_output_scale_ptr,\n                                          const float*  k_output_scale_ptr,\n                                          bool          use_ORDER_COL32_2R_4R4,\n                                          hipStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRow(int8_t*       q_buf,\n                                          int8_t*       k_buf,\n                                          const int8_t* Q,\n                                          const half*   bias_Q,\n                                          const int8_t* K,\n                                          const half*   bias_K,\n                                          const int     batch_size,\n                                          const int     seq_len,\n                                          const int     head_num,\n                                          const int     size_per_head,\n                                          const float*  q_input_deQFactor_ptr,\n                                          const float*  k_input_deQFactor_ptr,\n                                          const float*  q_output_scale_ptr,\n                                          const float*  k_output_scale_ptr,\n                                          bool          use_ORDER_COL32_2R_4R4,\n                                          hipStream_t  stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per axis quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C or\n//  CUBLASLT_ORDER_COL32_2R_4R4\n// only for int32 input & int8 output\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding(int8_t*        q_buf_,\n                                                      int8_t*        k_buf_,\n                                                      const int32_t* Q,\n                                                      const T*       bias_Q,\n                                                      const int32_t* K,\n                                                      const T*       bias_K,\n                                                      const int*     sequence_id_offset,\n                                                      const int      valid_word_num,\n                                                      const int      m,\n                                                      const int      batch_size,\n                                                      const int      seq_len,\n                                                      const int      head_num,\n                                                      const int      size_per_head,\n                                                      int            stride,\n                                                      const float*   q_weight_amax,\n                                                      const float*   q_input_deQFactor_div127_ptr,\n                                                      const float*   k_weight_amax,\n                                                      const float*   k_input_deQFactor_div127_ptr,\n                                                      const float*   q_output_scale_ptr,\n                                                      const float*   k_output_scale_ptr,\n                                                      bool           use_ORDER_COL32_2R_4R4)\n{\n    const int32_t* data_ptr;\n    char4*         buf_ptr4;\n    const T*       bias_ptr;\n    const float*   weight_amax;\n    int            qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? Q : K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float input_deQFactor_div127 =\n        qk_id == 0 ? __ldg(q_input_deQFactor_div127_ptr) : __ldg(k_input_deQFactor_div127_ptr);\n    weight_amax              = qk_id == 0 ? q_weight_amax : k_weight_amax;\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31));\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z = float_to_int8_rn(tmp * output_scale);\n\n    data_id    = data_id + 1;\n    threadIdx4 = threadIdx4 + 1;\n    scale  = static_cast<float>(__ldg(data_ptr + data_id)) * __ldg(weight_amax + threadIdx4) * input_deQFactor_div127;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                            int8_t*        k_buf,\n                                            const int32_t* Q,\n                                            const T*       bias_Q,\n                                            const int32_t* K,\n                                            const T*       bias_K,\n                                            const int*     sequence_id_offset,\n                                            const int      valid_word_num,\n                                            const int      batch_size,\n                                            const int      seq_len,\n                                            const int      head_num,\n                                            const int      size_per_head,\n                                            const float*   q_weight_amax,\n                                            const float*   q_input_deQFactor_div127_ptr,\n                                            const float*   k_weight_amax,\n                                            const float*   k_input_deQFactor_div127_ptr,\n                                            const float*   q_output_scale_ptr,\n                                            const float*   k_output_scale_ptr,\n                                            bool           use_ORDER_COL32_2R_4R4,\n                                            hipStream_t   stream)\n{\n    add_QK_bias_transform_rebuild_padding<<<dim3(valid_word_num * 2),\n                                            dim3((head_num * size_per_head) / 4),\n                                            0,\n                                            stream>>>(q_buf,\n                                                      k_buf,\n                                                      Q,\n                                                      bias_Q,\n                                                      K,\n                                                      bias_K,\n                                                      sequence_id_offset,\n                                                      valid_word_num,\n                                                      batch_size * seq_len,\n                                                      batch_size,\n                                                      seq_len,\n                                                      head_num,\n                                                      size_per_head,\n                                                      seq_len * size_per_head,\n                                                      q_weight_amax,\n                                                      q_input_deQFactor_div127_ptr,\n                                                      k_weight_amax,\n                                                      k_input_deQFactor_div127_ptr,\n                                                      q_output_scale_ptr,\n                                                      k_output_scale_ptr,\n                                                      use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                                     int8_t*        k_buf,\n                                                     const int32_t* Q,\n                                                     const float*   bias_Q,\n                                                     const int32_t* K,\n                                                     const float*   bias_K,\n                                                     const int*     sequence_id_offset,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     const float*   q_weight_amax,\n                                                     const float*   q_input_deQFactor_div127_ptr,\n                                                     const float*   k_weight_amax,\n                                                     const float*   k_input_deQFactor_div127_ptr,\n                                                     const float*   q_output_scale_ptr,\n                                                     const float*   k_output_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4,\n                                                     hipStream_t   stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*        q_buf,\n                                                     int8_t*        k_buf,\n                                                     const int32_t* Q,\n                                                     const half*    bias_Q,\n                                                     const int32_t* K,\n                                                     const half*    bias_K,\n                                                     const int*     sequence_id_offset,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     const float*   q_weight_amax,\n                                                     const float*   q_input_deQFactor_div127_ptr,\n                                                     const float*   k_weight_amax,\n                                                     const float*   k_input_deQFactor_div127_ptr,\n                                                     const float*   q_output_scale_ptr,\n                                                     const float*   k_output_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4,\n                                                     hipStream_t   stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C or\n//  CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding(int8_t*       q_buf_,\n                                                      int8_t*       k_buf_,\n                                                      const int8_t* Q,\n                                                      const T*      bias_Q,\n                                                      const int8_t* K,\n                                                      const T*      bias_K,\n                                                      const int*    sequence_id_offset,\n                                                      const int     valid_word_num,\n                                                      const int     m,\n                                                      const int     batch_size,\n                                                      const int     seq_len,\n                                                      const int     head_num,\n                                                      const int     size_per_head,\n                                                      int           stride,\n                                                      const float*  q_deQFactor_ptr,\n                                                      const float*  k_deQFactor_ptr,\n                                                      const float*  q_output_scale_ptr,\n                                                      const float*  k_output_scale_ptr,\n                                                      bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * seq_len + new_row) >> 2)] = tmp4;\n}\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K CUBLASLT_ORDER_COL32 matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  seq_len_padded = (seq_len + 31)/32*32;\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n//  or CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding_varlen(int8_t*       q_buf_,\n                                                             int8_t*       k_buf_,\n                                                             const int8_t* Q,\n                                                             const T*      bias_Q,\n                                                             const int8_t* K,\n                                                             const T*      bias_K,\n                                                             const int*    sequence_id_offset,\n                                                             const int     valid_word_num,\n                                                             const int     m,\n                                                             const int     batch_size,\n                                                             const int     seq_len,\n                                                             const int     seq_len_padded,\n                                                             const int     head_num,\n                                                             const int     size_per_head,\n                                                             int           stride_q,\n                                                             int           stride_k,\n                                                             const float*  q_deQFactor_ptr,\n                                                             const float*  k_deQFactor_ptr,\n                                                             const float*  q_output_scale_ptr,\n                                                             const float*  k_output_scale_ptr,\n                                                             bool          use_ORDER_COL32_2R_4R4)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4 = threadIdx.x << 2;\n    int m_full_idx = blockIdx.x % valid_word_num;\n    m_full_idx     = (valid_word_num != m) ? (m_full_idx + __ldg(sequence_id_offset + m_full_idx)) : m_full_idx;\n    int batch_id   = m_full_idx / seq_len;\n    int head_id    = threadIdx4 / size_per_head;\n    int id_in_head = threadIdx4 % size_per_head;\n    int word_id    = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id =\n        (((threadIdx4 >> 5) << 5) * valid_word_num + ((blockIdx.x % valid_word_num) << 5) + (threadIdx4 & 31)) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len or seq_len_padded, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len) or (COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int stride = (qk_id != 1) ? stride_q : stride_k;\n    const int len    = (qk_id != 1) ? seq_len : seq_len_padded;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                            int8_t*       k_buf,\n                                            const int8_t* Q,\n                                            const T*      bias_Q,\n                                            const int8_t* K,\n                                            const T*      bias_K,\n                                            const int*    sequence_id_offset,\n                                            const int     valid_word_num,\n                                            const int     batch_size,\n                                            const int     seq_len,\n                                            const int     head_num,\n                                            const int     size_per_head,\n                                            const float*  q_deQFactor_ptr,\n                                            const float*  k_deQFactor_ptr,\n                                            const float*  q_output_scale_ptr,\n                                            const float*  k_output_scale_ptr,\n                                            bool          use_ORDER_COL32_2R_4R4,\n                                            hipStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_QK_bias_transform_rebuild_padding_varlen<<<dim3(valid_word_num * 2),\n                                                   dim3((head_num * size_per_head) / 4),\n                                                   0,\n                                                   stream>>>(q_buf,\n                                                             k_buf,\n                                                             Q,\n                                                             bias_Q,\n                                                             K,\n                                                             bias_K,\n                                                             sequence_id_offset,\n                                                             valid_word_num,\n                                                             batch_size * seq_len,\n                                                             batch_size,\n                                                             seq_len,\n                                                             seq_len_padded,\n                                                             head_num,\n                                                             size_per_head,\n                                                             seq_len * size_per_head,\n                                                             seq_len_padded * size_per_head,\n                                                             q_deQFactor_ptr,\n                                                             k_deQFactor_ptr,\n                                                             q_output_scale_ptr,\n                                                             k_output_scale_ptr,\n                                                             use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                                     int8_t*       k_buf,\n                                                     const int8_t* Q,\n                                                     const float*  bias_Q,\n                                                     const int8_t* K,\n                                                     const float*  bias_K,\n                                                     const int*    sequence_id_offset,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     const float*  q_deQFactor_ptr,\n                                                     const float*  k_deQFactor_ptr,\n                                                     const float*  q_output_scale_ptr,\n                                                     const float*  k_output_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4,\n                                                     hipStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPadding(int8_t*       q_buf,\n                                                     int8_t*       k_buf,\n                                                     const int8_t* Q,\n                                                     const half*   bias_Q,\n                                                     const int8_t* K,\n                                                     const half*   bias_K,\n                                                     const int*    sequence_id_offset,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     const float*  q_deQFactor_ptr,\n                                                     const float*  k_deQFactor_ptr,\n                                                     const float*  q_output_scale_ptr,\n                                                     const float*  k_output_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4,\n                                                     hipStream_t  stream);\n\n// add_QK_bias_transform & rebuild padding for batch int8 cublasLtMatmul & per tensor quantization for weight\n// 1.add QK bias\n// 2.transform each Q K row-major matrixes into a series of sub-matrix (with\n// CUBLASLT_ORDER_COL32/CUBLASLT_ORDER_COL4_4R2_8C layout)\n//  Q, K are CUBLASLT_ORDER_COL32 matrixes of m = valid_word_num, n = head_num * size_per_head\n//  q_buf_ is of batchCount = batch_size * head_num, m = seq_len, n = size_per_head, CUBLASLT_ORDER_COL32\n//  seq_len_padded = (seq_len + 31)/32*32;\n//  k_buf_ is of batchCount = batch_size * head_num, m = seq_len_padded, n = size_per_head, CUBLASLT_ORDER_COL4_4R2_8C\n//  or CUBLASLT_ORDER_COL32_2R_4R4\n// only for int8 IO\n// seq_len, size_per_head must be a multiple of 32\n// grid.x = valid_word_num * 2;\n// block.x = head_num * size_per_head / 4;\n// using char4\ntemplate<typename T>\n__global__ void add_QK_bias_transform_rebuild_padding_varlen_row(int8_t*       q_buf_,\n                                                                 int8_t*       k_buf_,\n                                                                 const int8_t* Q,\n                                                                 const T*      bias_Q,\n                                                                 const int8_t* K,\n                                                                 const T*      bias_K,\n                                                                 const int*    sequence_id_offset,\n                                                                 const int     valid_word_num,\n                                                                 const int     m,\n                                                                 const int     batch_size,\n                                                                 const int     seq_len,\n                                                                 const int     seq_len_padded,\n                                                                 const int     head_num,\n                                                                 const int     size_per_head,\n                                                                 int           stride_q,\n                                                                 int           stride_k,\n                                                                 const float*  q_deQFactor_ptr,\n                                                                 const float*  k_deQFactor_ptr,\n                                                                 const float*  q_output_scale_ptr,\n                                                                 const float*  k_output_scale_ptr,\n                                                                 bool          use_ORDER_COL32_2R_4R4,\n                                                                 const int     head_num_x_size_per_head)\n{\n    const char4* data_ptr;\n    char4*       buf_ptr4;\n    const T*     bias_ptr;\n    int          qk_id = blockIdx.x / valid_word_num;\n\n    data_ptr = qk_id == 0 ? (const char4*)Q : (const char4*)K;\n    buf_ptr4 = qk_id == 0 ? (char4*)q_buf_ : (char4*)k_buf_;\n    bias_ptr = qk_id == 0 ? bias_Q : bias_K;\n\n    int threadIdx4   = threadIdx.x << 2;\n    int batch_seq_id = blockIdx.x % valid_word_num;\n    int m_full_idx   = (valid_word_num != m) ? (batch_seq_id + __ldg(sequence_id_offset + batch_seq_id)) : batch_seq_id;\n    int batch_id     = m_full_idx / seq_len;\n    int head_id      = threadIdx4 / size_per_head;\n    int id_in_head   = threadIdx4 % size_per_head;\n    int word_id      = m_full_idx % seq_len;\n\n    const float deQFactor    = qk_id == 0 ? __ldg(q_deQFactor_ptr) : __ldg(k_deQFactor_ptr);\n    const float output_scale = qk_id == 0 ? __ldg(q_output_scale_ptr) : __ldg(k_output_scale_ptr);\n\n    int data_id = (batch_seq_id * head_num_x_size_per_head + threadIdx4) >> 2;\n\n    float scale;\n    float tmp;\n    char4 tmp4;\n\n    tmp4 = __ldg(data_ptr + data_id);\n\n    scale  = static_cast<float>(tmp4.x) * deQFactor;\n    tmp    = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.x = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.y) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.y     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.z) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.z     = float_to_int8_rn(tmp * output_scale);\n\n    threadIdx4 = threadIdx4 + 1;\n    scale      = static_cast<float>(tmp4.w) * deQFactor;\n    tmp        = static_cast<float>(__ldg(bias_ptr + threadIdx4)) + scale;\n    tmp4.w     = float_to_int8_rn(tmp * output_scale);\n\n    // row_id, col_id of sub-matrix (m = seq_len or seq_len_padded, n = size_per_head), column-major\n    int row_id = word_id;\n    int col_id = id_in_head;\n    // new (row, rol) of LtTrans COL32/COL4 sub-matrix, leading dim = (COL32_ * seq_len) or (COL32_ * seq_len_padded)\n    int new_col = col_id >> 5;\n    int new_row;\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = row_id & 31;\n        int col_in_tile = col_id & 31;\n        new_row         = (qk_id != 1) ?\n                              // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                              // COL32_2R_4R4\n                      (((row_id >> 5) << 10) +\n                       //(((row%8)/2*4+row/8)*2+row%2)*32+col\n                       (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5)\n                       + col_in_tile);\n    }\n    else {\n        new_row = (qk_id != 1) ?\n                      // COL32\n                      ((row_id << 5) + (col_id & 31)) :\n                      // COL4\n                      ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                      ////row_id%2 is even row, otherwise odd row\n                      ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n                      (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id % 32) >> 3)) << 5) +\n                       ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n                       ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n                       (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id % 8) >> 1)) << 2) +\n                       ////col_id%4 is the id of 4 cols\n                       (col_id & 3));\n    }\n\n    const int stride = (qk_id != 1) ? stride_q : stride_k;\n    const int len    = (qk_id != 1) ? seq_len : seq_len_padded;\n    buf_ptr4[(((batch_id * head_num + head_id) * stride + (new_col << 5) * len + new_row) >> 2)] = tmp4;\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                               int8_t*       k_buf,\n                                               const int8_t* Q,\n                                               const T*      bias_Q,\n                                               const int8_t* K,\n                                               const T*      bias_K,\n                                               const int*    sequence_id_offset,\n                                               const int     valid_word_num,\n                                               const int     batch_size,\n                                               const int     seq_len,\n                                               const int     head_num,\n                                               const int     size_per_head,\n                                               const float*  q_deQFactor_ptr,\n                                               const float*  k_deQFactor_ptr,\n                                               const float*  q_output_scale_ptr,\n                                               const float*  k_output_scale_ptr,\n                                               bool          use_ORDER_COL32_2R_4R4,\n                                               hipStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_QK_bias_transform_rebuild_padding_varlen_row<<<dim3(valid_word_num * 2),\n                                                       dim3((head_num * size_per_head) / 4),\n                                                       0,\n                                                       stream>>>(q_buf,\n                                                                 k_buf,\n                                                                 Q,\n                                                                 bias_Q,\n                                                                 K,\n                                                                 bias_K,\n                                                                 sequence_id_offset,\n                                                                 valid_word_num,\n                                                                 batch_size * seq_len,\n                                                                 batch_size,\n                                                                 seq_len,\n                                                                 seq_len_padded,\n                                                                 head_num,\n                                                                 size_per_head,\n                                                                 seq_len * size_per_head,\n                                                                 seq_len_padded * size_per_head,\n                                                                 q_deQFactor_ptr,\n                                                                 k_deQFactor_ptr,\n                                                                 q_output_scale_ptr,\n                                                                 k_output_scale_ptr,\n                                                                 use_ORDER_COL32_2R_4R4,\n                                                                 head_num * size_per_head);\n}\n\ntemplate void invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                                        int8_t*       k_buf,\n                                                        const int8_t* Q,\n                                                        const float*  bias_Q,\n                                                        const int8_t* K,\n                                                        const float*  bias_K,\n                                                        const int*    sequence_id_offset,\n                                                        const int     valid_word_num,\n                                                        const int     batch_size,\n                                                        const int     seq_len,\n                                                        const int     head_num,\n                                                        const int     size_per_head,\n                                                        const float*  q_deQFactor_ptr,\n                                                        const float*  k_deQFactor_ptr,\n                                                        const float*  q_output_scale_ptr,\n                                                        const float*  k_output_scale_ptr,\n                                                        bool          use_ORDER_COL32_2R_4R4,\n                                                        hipStream_t  stream);\n\ntemplate void invokeAddQKBiasTransformRebuildPaddingRow(int8_t*       q_buf,\n                                                        int8_t*       k_buf,\n                                                        const int8_t* Q,\n                                                        const half*   bias_Q,\n                                                        const int8_t* K,\n                                                        const half*   bias_K,\n                                                        const int*    sequence_id_offset,\n                                                        const int     valid_word_num,\n                                                        const int     batch_size,\n                                                        const int     seq_len,\n                                                        const int     head_num,\n                                                        const int     size_per_head,\n                                                        const float*  q_deQFactor_ptr,\n                                                        const float*  k_deQFactor_ptr,\n                                                        const float*  q_output_scale_ptr,\n                                                        const float*  k_output_scale_ptr,\n                                                        bool          use_ORDER_COL32_2R_4R4,\n                                                        hipStream_t  stream);\n\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int32_t Input int8_t Output seq_len, size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per axis quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform(int8_t*        v_buf_,\n                                     const int32_t* V,\n                                     const T*       V_bias,\n                                     const int      batch_size,\n                                     const int      seq_len,\n                                     const int      head_num,\n                                     const int      size_per_head,\n                                     int            stride,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr,\n                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = (((col >> 5) << 5) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    float tmp;\n    float scale;\n\n    // const half2* bias_ptr2 = (const half2*)bias_ptr;\n    // half2 tmp2;\n\n    // tmp2 = __ldg(&bias_ptr2[col >> 1]);\n\n    scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n    tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n    shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n    shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n    // tmp2 = __ldg(&bias_ptr2[(col >> 1) + 1]);\n\n    scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n    shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n    tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n    shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<>\n__global__ void add_V_bias_transform(int8_t*        v_buf_,\n                                     const int32_t* V,\n                                     const half*    V_bias,\n                                     const int      batch_size,\n                                     const int      seq_len,\n                                     const int      head_num,\n                                     const int      size_per_head,\n                                     int            stride,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr,\n                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = ((col & 0xffffffe0) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    int   col_2 = col >> 1;\n    float scale;\n\n    const half2* bias_ptr2 = (const half2*)V_bias;\n    half2        tmp2;\n\n    tmp2 = __ldg(bias_ptr2 + col_2);\n\n    scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n    scale               = scale + static_cast<float>(tmp2.x);\n    shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.y);\n    shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n    tmp2 = __ldg(bias_ptr2 + col_2 + 1);\n\n    scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.x);\n    shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n    scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n    scale                   = scale + static_cast<float>(tmp2.y);\n    shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen(int8_t*        v_buf_,\n                                            const int32_t* V,\n                                            const T*       V_bias,\n                                            const int      batch_size,\n                                            const int      seq_len,\n                                            const int      head_num,\n                                            const int      size_per_head,\n                                            int            stride,\n                                            const float*   weight_amax,\n                                            const float*   input_deQFactor_div127_ptr,\n                                            const float*   out_scale_ptr,\n                                            bool           use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float       out_scale              = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col   = head_id * size_per_head + id_in_size;\n    int row   = batch_id * seq_len + word_id;\n    int inIdx = ((col & 0xffffffe0) * batch_size * seq_len + ((row << 5) + (col & 31)));\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    float scale;\n\n    if (word_id < seq_len) {\n        const T* bias_ptr = V_bias;\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        scale               = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    char4 dataTmp;\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransform(int8_t*        v_buf,\n                             const int32_t* V,\n                             const T*       V_bias,\n                             const int      batch_size,\n                             const int      seq_len,\n                             const int      head_num,\n                             const int      size_per_head,\n                             const float*   weight_amax,\n                             const float*   input_deQFactor_div127_ptr,\n                             const float*   out_scale_ptr,\n                             bool           use_ORDER_COL32_2R_4R4,\n                             hipStream_t   stream)\n{\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num), dim3(8, 32), 0, stream>>>(\n            v_buf,\n            V,\n            V_bias,\n            batch_size,\n            seq_len,\n            head_num,\n            size_per_head,\n            seq_len * size_per_head,\n            weight_amax,\n            input_deQFactor_div127_ptr,\n            out_scale_ptr,\n            use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len_padded * size_per_head,\n                                                weight_amax,\n                                                input_deQFactor_div127_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddVBiasTransform(int8_t*        v_buf,\n                                      const int32_t* V,\n                                      const float*   V_bias,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      const float*   weight_amax,\n                                      const float*   input_deQFactor_div127_ptr,\n                                      const float*   out_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4,\n                                      hipStream_t   stream);\n\ntemplate void invokeAddVBiasTransform(int8_t*        v_buf,\n                                      const int32_t* V,\n                                      const half*    V_bias,\n                                      const int      batch_size,\n                                      const int      seq_len,\n                                      const int      head_num,\n                                      const int      size_per_head,\n                                      const float*   weight_amax,\n                                      const float*   input_deQFactor_div127_ptr,\n                                      const float*   out_scale_ptr,\n                                      bool           use_ORDER_COL32_2R_4R4,\n                                      hipStream_t   stream);\n\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen(int8_t*       v_buf_,\n                                            const int8_t* V,\n                                            const T*      V_bias,\n                                            const int     batch_size,\n                                            const int     seq_len,\n                                            const int     head_num,\n                                            const int     size_per_head,\n                                            const int     seq_len_padded,\n                                            int           stride,\n                                            const float*  input_deQFactor_ptr,\n                                            const float*  out_scale_ptr,\n                                            bool          use_ORDER_COL32_2R_4R4)\n{\n    const float       input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float       out_scale       = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    int col, row;\n    // for shm row-major\n    int   sh_col = threadIdx4;\n    int   sh_row = threadIdx.y;\n    char4 dataTmp;\n    if (word_id < seq_len) {\n        // for V layout (batch_size*seq_len, head_num*size_per_head)\n        col       = head_id * size_per_head + id_in_size;\n        row       = batch_id * seq_len + word_id;\n        int inIdx = (((col >> 5) << 5) * batch_size * seq_len + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * input_deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransform(int8_t*       v_buf,\n                             const int8_t* V,\n                             const T*      V_bias,\n                             const int     batch_size,\n                             const int     seq_len,\n                             const int     head_num,\n                             const int     size_per_head,\n                             const float*  input_deQFactor_ptr,\n                             const float*  out_scale_ptr,\n                             bool          use_ORDER_COL32_2R_4R4,\n                             hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len,\n                                                seq_len * size_per_head,\n                                                input_deQFactor_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                      dim3(8, 32),\n                                      0,\n                                      stream>>>(v_buf,\n                                                V,\n                                                V_bias,\n                                                batch_size,\n                                                seq_len,\n                                                head_num,\n                                                size_per_head,\n                                                seq_len_padded,\n                                                seq_len_padded * size_per_head,\n                                                input_deQFactor_ptr,\n                                                out_scale_ptr,\n                                                use_ORDER_COL32_2R_4R4);\n    }\n}\n\ntemplate void invokeAddVBiasTransform(int8_t*       v_buf,\n                                      const int8_t* V,\n                                      const float*  V_bias,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      const float*  input_deQFactor_ptr,\n                                      const float*  out_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4,\n                                      hipStream_t  stream);\n\ntemplate void invokeAddVBiasTransform(int8_t*       v_buf,\n                                      const int8_t* V,\n                                      const half*   V_bias,\n                                      const int     batch_size,\n                                      const int     seq_len,\n                                      const int     head_num,\n                                      const int     size_per_head,\n                                      const float*  input_deQFactor_ptr,\n                                      const float*  out_scale_ptr,\n                                      bool          use_ORDER_COL32_2R_4R4,\n                                      hipStream_t  stream);\n// input matrix a matrix of m = batch_size*seq_len , n = head_num*size_per_head, row major\n// seq_len_padded = (seq_len+31)/32*32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_varlen_row(int8_t*       v_buf_,\n                                                const int8_t* V,\n                                                const T*      V_bias,\n                                                const int     batch_size,\n                                                const int     seq_len,\n                                                const int     head_num,\n                                                const int     size_per_head,\n                                                const int     seq_len_padded,\n                                                int           stride,\n                                                const float*  input_deQFactor_ptr,\n                                                const float*  out_scale_ptr,\n                                                bool          use_ORDER_COL32_2R_4R4,\n                                                const int     head_num_x_size_per_head)\n{\n    const float       input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float       out_scale       = __ldg(out_scale_ptr);\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    int col, row;\n    // for shm row-major\n    int   sh_col = threadIdx4;\n    int   sh_row = threadIdx.y;\n    char4 dataTmp;\n    if (word_id < seq_len) {\n        // for V layout (batch_size*seq_len, head_num*size_per_head)\n        col       = head_id * size_per_head + id_in_size;\n        row       = batch_id * seq_len + word_id;\n        int inIdx = (row * head_num_x_size_per_head + col) >> 2;\n\n        float tmp;\n        float scale;\n\n        dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * input_deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));  //(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));  //(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));  //(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * input_deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));  //(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n\n    __syncthreads();\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10)\n            + (((((((row_in_tile % 8) / 2) * 4) + (row_in_tile / 8)) * 2) + (row_in_tile % 2)) * 32) + col_in_tile\n            // (((((((row_in_tile%8)>>1)<<2)+(row_in_tile>>3))<<1)+(row_in_tile&1))<<5)+col_in_tile\n        );\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            ((((id_in_size >> 3) << 3) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    dataTmp.x                                                               = shm[sh_col][sh_row];\n    dataTmp.y                                                               = shm[sh_col + 1][sh_row];\n    dataTmp.z                                                               = shm[sh_col + 2][sh_row];\n    dataTmp.w                                                               = shm[sh_col + 3][sh_row];\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                const int8_t* V,\n                                const T*      V_bias,\n                                const int     batch_size,\n                                const int     seq_len,\n                                const int     head_num,\n                                const int     size_per_head,\n                                const float*  input_deQFactor_ptr,\n                                const float*  out_scale_ptr,\n                                bool          use_ORDER_COL32_2R_4R4,\n                                hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    if (seq_len % 32 == 0) {\n        add_V_bias_transform_varlen_row<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                          dim3(8, 32),\n                                          0,\n                                          stream>>>(v_buf,\n                                                    V,\n                                                    V_bias,\n                                                    batch_size,\n                                                    seq_len,\n                                                    head_num,\n                                                    size_per_head,\n                                                    seq_len,\n                                                    seq_len * size_per_head,\n                                                    input_deQFactor_ptr,\n                                                    out_scale_ptr,\n                                                    use_ORDER_COL32_2R_4R4,\n                                                    head_num * size_per_head);\n    }\n    else {\n        const int seq_len_padded = (seq_len + 31) / 32 * 32;\n        add_V_bias_transform_varlen_row<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                          dim3(8, 32),\n                                          0,\n                                          stream>>>(v_buf,\n                                                    V,\n                                                    V_bias,\n                                                    batch_size,\n                                                    seq_len,\n                                                    head_num,\n                                                    size_per_head,\n                                                    seq_len_padded,\n                                                    seq_len_padded * size_per_head,\n                                                    input_deQFactor_ptr,\n                                                    out_scale_ptr,\n                                                    use_ORDER_COL32_2R_4R4,\n                                                    head_num * size_per_head);\n    }\n}\n\ntemplate void invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                         const int8_t* V,\n                                         const float*  V_bias,\n                                         const int     batch_size,\n                                         const int     seq_len,\n                                         const int     head_num,\n                                         const int     size_per_head,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr,\n                                         bool          use_ORDER_COL32_2R_4R4,\n                                         hipStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRow(int8_t*       v_buf,\n                                         const int8_t* V,\n                                         const half*   V_bias,\n                                         const int     batch_size,\n                                         const int     seq_len,\n                                         const int     head_num,\n                                         const int     size_per_head,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr,\n                                         bool          use_ORDER_COL32_2R_4R4,\n                                         hipStream_t  stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int32_t Input int8_t Output seq_len, size_per_head must be a multiple of 32\n// grid = (size_per_head/32, seq_len/32, batch_size*head_num)\n// block = (8, 32);\n// using char4\n// per axis quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*        v_buf_,\n                                                     const int32_t* V,\n                                                     const T*       V_bias,\n                                                     const int*     sequence_id_map,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     int            stride,\n                                                     const float*   weight_amax,\n                                                     const float*   input_deQFactor_div127_ptr,\n                                                     const float*   out_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31)));\n\n        float tmp;\n        float scale;\n\n        const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n        const float out_scale              = __ldg(out_scale_ptr);\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*        v_buf_,\n                                                     const int32_t* V,\n                                                     const half*    V_bias,\n                                                     const int*     sequence_id_map,\n                                                     const int      valid_word_num,\n                                                     const int      batch_size,\n                                                     const int      seq_len,\n                                                     const int      head_num,\n                                                     const int      size_per_head,\n                                                     int            stride,\n                                                     const float*   weight_amax,\n                                                     const float*   input_deQFactor_div127_ptr,\n                                                     const float*   out_scale_ptr,\n                                                     bool           use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const int32_t*    data_ptr = V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id = blockIdx.z / head_num;\n    int head_id  = blockIdx.z % head_num;\n\n    int blockIdy32 = (blockIdx.y << 5);\n    int blockIdx32 = (blockIdx.x << 5);\n    int word_id    = blockIdy32 + threadIdx.y;\n    int id_in_size = blockIdx32 + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row >= 0) {\n        const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n        const float out_scale              = __ldg(out_scale_ptr);\n        col                                = head_id * size_per_head + id_in_size;\n        int   inIdx                        = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31)));\n        int   col_2                        = col >> 1;\n        float scale;\n\n        const half2* bias_ptr2 = (const half2*)V_bias;\n        half2        tmp2;\n\n        tmp2 = __ldg(bias_ptr2 + col_2);\n\n        scale               = __ldg(data_ptr + inIdx) * __ldg(weight_amax + col) * input_deQFactor_div127;\n        scale               = scale + static_cast<float>(tmp2.x);\n        shm[sh_row][sh_col] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 1) * __ldg(weight_amax + col + 1) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.y);\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(scale * out_scale);\n\n        tmp2 = __ldg(bias_ptr2 + col_2 + 1);\n\n        scale                   = __ldg(data_ptr + inIdx + 2) * __ldg(weight_amax + col + 2) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.x);\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(scale * out_scale);\n\n        scale                   = __ldg(data_ptr + inIdx + 3) * __ldg(weight_amax + col + 3) * input_deQFactor_div127;\n        scale                   = scale + static_cast<float>(tmp2.y);\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(scale * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = blockIdy32 + threadIdx4;\n    id_in_size = blockIdx32 + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                           const int32_t* V,\n                                           const T*       V_bias,\n                                           const int*     sequence_id_map,\n                                           const int      valid_word_num,\n                                           const int      batch_size,\n                                           const int      seq_len,\n                                           const int      head_num,\n                                           const int      size_per_head,\n                                           const float*   weight_amax,\n                                           const float*   input_deQFactor_div127_ptr,\n                                           const float*   out_scale_ptr,\n                                           bool           use_ORDER_COL32_2R_4R4,\n                                           hipStream_t   stream)\n{\n    add_V_bias_transform_rebuild_padding<<<dim3(size_per_head / 32, seq_len / 32, batch_size * head_num),\n                                           dim3(8, 32),\n                                           0,\n                                           stream>>>(v_buf,\n                                                     V,\n                                                     V_bias,\n                                                     sequence_id_map,\n                                                     valid_word_num,\n                                                     batch_size,\n                                                     seq_len,\n                                                     head_num,\n                                                     size_per_head,\n                                                     seq_len * size_per_head,\n                                                     weight_amax,\n                                                     input_deQFactor_div127_ptr,\n                                                     out_scale_ptr,\n                                                     use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                                    const int32_t* V,\n                                                    const float*   V_bias,\n                                                    const int*     sequence_id_map,\n                                                    const int      valid_word_num,\n                                                    const int      batch_size,\n                                                    const int      seq_len,\n                                                    const int      head_num,\n                                                    const int      size_per_head,\n                                                    const float*   weight_amax,\n                                                    const float*   input_deQFactor_div127_ptr,\n                                                    const float*   out_scale_ptr,\n                                                    bool           use_ORDER_COL32_2R_4R4,\n                                                    hipStream_t   stream);\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*        v_buf,\n                                                    const int32_t* V,\n                                                    const half*    V_bias,\n                                                    const int*     sequence_id_map,\n                                                    const int      valid_word_num,\n                                                    const int      batch_size,\n                                                    const int      seq_len,\n                                                    const int      head_num,\n                                                    const int      size_per_head,\n                                                    const float*   weight_amax,\n                                                    const float*   input_deQFactor_div127_ptr,\n                                                    const float*   out_scale_ptr,\n                                                    bool           use_ORDER_COL32_2R_4R4,\n                                                    hipStream_t   stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len , CUBLASLT_ORDER_COL4_4R2_8C\n// or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a multiple of 32 grid =\n// (size_per_head/32, seq_len/32, batch_size*head_num) block = (8, 32); using char4 per tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding(int8_t*       v_buf_,\n                                                     const int8_t* V,\n                                                     const T*      V_bias,\n                                                     const int*    sequence_id_map,\n                                                     const int     valid_word_num,\n                                                     const int     batch_size,\n                                                     const int     seq_len,\n                                                     const int     head_num,\n                                                     const int     size_per_head,\n                                                     int           stride,\n                                                     const float*  deQFactor_ptr,\n                                                     const float*  out_scale_ptr,\n                                                     bool          use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = __ldg(sequence_id_map + batch_id * seq_len + word_id);\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a\n// multiple of 32 grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num) block = (8, 32); using char4 per\n// tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding_varlen(int8_t*       v_buf_,\n                                                            const int8_t* V,\n                                                            const T*      V_bias,\n                                                            const int*    sequence_id_map,\n                                                            const int     valid_word_num,\n                                                            const int     batch_size,\n                                                            const int     seq_len,\n                                                            const int     seq_len_padded,\n                                                            const int     head_num,\n                                                            const int     size_per_head,\n                                                            int           stride,\n                                                            const float*  deQFactor_ptr,\n                                                            const float*  out_scale_ptr,\n                                                            bool          use_ORDER_COL32_2R_4R4)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = word_id < seq_len ? __ldg(sequence_id_map + batch_id * seq_len + word_id) : -1;\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = ((col & 0xffffffe0) * valid_word_num + ((row << 5) + (col & 31))) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10) +\n            //(((row%8)/2*4+row/8)*2+row%2)*32+col\n            (((((((row_in_tile % 8) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                           const int8_t* V,\n                                           const T*      V_bias,\n                                           const int*    sequence_id_map,\n                                           const int     valid_word_num,\n                                           const int     batch_size,\n                                           const int     seq_len,\n                                           const int     head_num,\n                                           const int     size_per_head,\n                                           const float*  deQFactor_ptr,\n                                           const float*  out_scale_ptr,\n                                           bool          use_ORDER_COL32_2R_4R4,\n                                           hipStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_V_bias_transform_rebuild_padding_varlen<<<dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n                                                  dim3(8, 32),\n                                                  0,\n                                                  stream>>>(v_buf,\n                                                            V,\n                                                            V_bias,\n                                                            sequence_id_map,\n                                                            valid_word_num,\n                                                            batch_size,\n                                                            seq_len,\n                                                            seq_len_padded,\n                                                            head_num,\n                                                            size_per_head,\n                                                            seq_len_padded * size_per_head,\n                                                            deQFactor_ptr,\n                                                            out_scale_ptr,\n                                                            use_ORDER_COL32_2R_4R4);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                                    const int8_t* V,\n                                                    const float*  V_bias,\n                                                    const int*    sequence_id_map,\n                                                    const int     valid_word_num,\n                                                    const int     batch_size,\n                                                    const int     seq_len,\n                                                    const int     head_num,\n                                                    const int     size_per_head,\n                                                    const float*  deQFactor_ptr,\n                                                    const float*  out_scale_ptr,\n                                                    bool          use_ORDER_COL32_2R_4R4,\n                                                    hipStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRebuildPadding(int8_t*       v_buf,\n                                                    const int8_t* V,\n                                                    const half*   V_bias,\n                                                    const int*    sequence_id_map,\n                                                    const int     valid_word_num,\n                                                    const int     batch_size,\n                                                    const int     seq_len,\n                                                    const int     head_num,\n                                                    const int     size_per_head,\n                                                    const float*  deQFactor_ptr,\n                                                    const float*  out_scale_ptr,\n                                                    bool          use_ORDER_COL32_2R_4R4,\n                                                    hipStream_t  stream);\n\n// add bias into V & rebuild padding\n// input matrix a matrix of m = valid_word_num, n = head_num*size_per_head, row major\n// output matrixes are a series of sub-matrixes with size of m = size_per_head, n = seq_len_padded ,\n// CUBLASLT_ORDER_COL4_4R2_8C or CUBLASLT_ORDER_COL32_2R_4R4 only for int8_t IO seq_len, size_per_head must be a\n// multiple of 32 grid = (size_per_head/32, seq_len_padded/32, batch_size*head_num) block = (8, 32); using char4 per\n// tensor quantization for weight\ntemplate<typename T>\n__global__ void add_V_bias_transform_rebuild_padding_varlen_row(int8_t*       v_buf_,\n                                                                const int8_t* V,\n                                                                const T*      V_bias,\n                                                                const int*    sequence_id_map,\n                                                                const int     valid_word_num,\n                                                                const int     batch_size,\n                                                                const int     seq_len,\n                                                                const int     seq_len_padded,\n                                                                const int     head_num,\n                                                                const int     size_per_head,\n                                                                int           stride,\n                                                                const float*  deQFactor_ptr,\n                                                                const float*  out_scale_ptr,\n                                                                bool          use_ORDER_COL32_2R_4R4,\n                                                                const int     head_num_x_size_per_head)\n{\n    __shared__ int8_t shm[32][33];\n    const char4*      data_ptr = (const char4*)V;\n    char4*            buf_ptr4 = (char4*)v_buf_;\n    const T*          bias_ptr = V_bias;\n\n    int threadIdx4 = threadIdx.x << 2;\n\n    // for src of (seq_len, size_per_head)\n    int batch_id   = blockIdx.z / head_num;\n    int head_id    = blockIdx.z % head_num;\n    int word_id    = (blockIdx.y << 5) + threadIdx.y;\n    int id_in_size = (blockIdx.x << 5) + threadIdx4;\n\n    // for shm row-major\n    int sh_col = threadIdx4;\n    int sh_row = threadIdx.y;\n\n    // for V layout (batch_size*seq_len, head_num*size_per_head)\n    int col;\n    int row = word_id < seq_len ? __ldg(sequence_id_map + batch_id * seq_len + word_id) : -1;\n\n    if (row != -1) {\n        col       = head_id * size_per_head + id_in_size;\n        int inIdx = (row * head_num_x_size_per_head + col) >> 2;\n\n        float tmp;\n        float scale;\n\n        const float deQFactor = __ldg(deQFactor_ptr);\n        const float out_scale = __ldg(out_scale_ptr);\n\n        char4 dataTmp = __ldg(data_ptr + inIdx);\n\n        scale               = dataTmp.x * deQFactor;\n        tmp                 = scale + static_cast<float>(__ldg(bias_ptr + col));\n        shm[sh_row][sh_col] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.y * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 1));\n        shm[sh_row][sh_col + 1] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.z * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 2));\n        shm[sh_row][sh_col + 2] = float_to_int8_rn(tmp * out_scale);\n\n        scale                   = dataTmp.w * deQFactor;\n        tmp                     = scale + static_cast<float>(__ldg(bias_ptr + col + 3));\n        shm[sh_row][sh_col + 3] = float_to_int8_rn(tmp * out_scale);\n    }\n    else {\n        shm[sh_row][sh_col] = shm[sh_row][sh_col + 1] = shm[sh_row][sh_col + 2] = shm[sh_row][sh_col + 3] = 0;\n    }\n    __syncthreads();\n\n    char4 dataTmp;\n    dataTmp.x = shm[sh_col][sh_row];\n    dataTmp.y = shm[sh_col + 1][sh_row];\n    dataTmp.z = shm[sh_col + 2][sh_row];\n    dataTmp.w = shm[sh_col + 3][sh_row];\n\n    // for dst of (size_per_head, seq_len_padded)\n    word_id    = (blockIdx.y << 5) + threadIdx4;\n    id_in_size = (blockIdx.x << 5) + threadIdx.y;\n    col        = (word_id >> 5);\n\n    if (use_ORDER_COL32_2R_4R4) {\n        int row_in_tile = id_in_size & 31;\n        int col_in_tile = word_id & 31;\n        row             = (\n            // COL32_2R_4R4\n            ((id_in_size >> 5) << 10)\n            + (((((((row_in_tile % 8) / 2) * 4) + (row_in_tile / 8)) * 2) + (row_in_tile % 2)) * 32) + col_in_tile\n            // (((((((row_in_tile%8)>>1)<<2)+(row_in_tile>>3))<<1)+(row_in_tile&1))<<5)+col_in_tile\n        );\n    }\n    else {\n        row = (\n            // COL4\n            ////id_in_size/8 is the number of tile of (8 rows 32 columns) -- column-major\n            ////id_in_size%2 is even row, otherwise odd row\n            ////word_id%COL32_/8 is the number tile of (8 rows 8 columns)\n            (((id_in_size & 0xfffffff8) + ((id_in_size & 1) << 2) + ((word_id % 32) >> 3)) << 5) +\n            ////word_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n            ////(id_in_size%8/2) is (the row id of alternating 4 rows) - 1\n            (((((word_id & 7) >= 4) ? 4 : 0) + ((id_in_size % 8) >> 1)) << 2) +\n            ////word_id%4 is the id of 4 cols\n            (word_id & 3));\n    }\n\n    buf_ptr4[(blockIdx.z * stride + (col << 5) * size_per_head + row) >> 2] = dataTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                              const int8_t* V,\n                                              const T*      V_bias,\n                                              const int*    sequence_id_map,\n                                              const int     valid_word_num,\n                                              const int     batch_size,\n                                              const int     seq_len,\n                                              const int     head_num,\n                                              const int     size_per_head,\n                                              const float*  deQFactor_ptr,\n                                              const float*  out_scale_ptr,\n                                              bool          use_ORDER_COL32_2R_4R4,\n                                              hipStream_t  stream)\n{\n    int seq_len_padded = (seq_len + 31) / 32 * 32;\n    add_V_bias_transform_rebuild_padding_varlen_row<<<\n        dim3(size_per_head / 32, seq_len_padded / 32, batch_size * head_num),\n        dim3(8, 32),\n        0,\n        stream>>>(v_buf,\n                  V,\n                  V_bias,\n                  sequence_id_map,\n                  valid_word_num,\n                  batch_size,\n                  seq_len,\n                  seq_len_padded,\n                  head_num,\n                  size_per_head,\n                  seq_len_padded * size_per_head,\n                  deQFactor_ptr,\n                  out_scale_ptr,\n                  use_ORDER_COL32_2R_4R4,\n                  head_num * size_per_head);\n}\n\ntemplate void invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                                       const int8_t* V,\n                                                       const float*  V_bias,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  deQFactor_ptr,\n                                                       const float*  out_scale_ptr,\n                                                       bool          use_ORDER_COL32_2R_4R4,\n                                                       hipStream_t  stream);\n\ntemplate void invokeAddVBiasTransformRebuildPaddingRow(int8_t*       v_buf,\n                                                       const int8_t* V,\n                                                       const half*   V_bias,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  deQFactor_ptr,\n                                                       const float*  out_scale_ptr,\n                                                       bool          use_ORDER_COL32_2R_4R4,\n                                                       hipStream_t  stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/unfused_attention_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/matrix_transpose_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n\nnamespace fastertransformer {\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\n__global__ void matrix_transpose(T* dst, const T* src, const int k, const int n)\n{\n    __shared__ T shm[32][33];\n    const int    tidx  = threadIdx.x;\n    const int    tidy  = threadIdx.y;\n    int          n_idx = blockIdx.x * 32 + tidx;\n    int          k_idx = blockIdx.y * 32 + tidy;\n    if (n_idx < n && k_idx < k) {\n        shm[tidx][tidy] = src[k_idx * n + n_idx];\n    }\n\n    __syncthreads();\n    n_idx = blockIdx.x * 32 + tidy;\n    k_idx = blockIdx.y * 32 + tidx;\n    if (n_idx < n && k_idx < k) {\n        dst[n_idx * k + k_idx] = shm[tidy][tidx];\n    }\n}\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\nvoid invokeMatrixTranspose(T* dst, const T* src, const int k, const int n, cudaStream_t stream)\n{\n    dim3 grid(n / 32, k / 32);\n    dim3 block(32, 32);\n    matrix_transpose<<<grid, block, 0, stream>>>(dst, src, k, n);\n}\n\ntemplate void invokeMatrixTranspose(float* dst, const float* src, const int m, const int n, cudaStream_t stream);\ntemplate void invokeMatrixTranspose(half* dst, const half* src, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void\ninvokeMatrixTranspose(__nv_fp8_e4m3* dst, const __nv_fp8_e4m3* src, const int m, const int n, cudaStream_t stream);\n#endif  // ENABLE_FP8\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/matrix_transpose_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n\nnamespace fastertransformer {\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\n__global__ void matrix_transpose(T* dst, const T* src, const int k, const int n)\n{\n    __shared__ T shm[32][33];\n    const int    tidx  = threadIdx.x;\n    const int    tidy  = threadIdx.y;\n    int          n_idx = blockIdx.x * 32 + tidx;\n    int          k_idx = blockIdx.y * 32 + tidy;\n    if (n_idx < n && k_idx < k) {\n        shm[tidx][tidy] = src[k_idx * n + n_idx];\n    }\n\n    __syncthreads();\n    n_idx = blockIdx.x * 32 + tidy;\n    k_idx = blockIdx.y * 32 + tidx;\n    if (n_idx < n && k_idx < k) {\n        dst[n_idx * k + k_idx] = shm[tidy][tidx];\n    }\n}\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\nvoid invokeMatrixTranspose(T* dst, const T* src, const int k, const int n, hipStream_t stream)\n{\n    dim3 grid(n / 32, k / 32);\n    dim3 block(32, 32);\n    matrix_transpose<<<grid, block, 0, stream>>>(dst, src, k, n);\n}\n\ntemplate void invokeMatrixTranspose(float* dst, const float* src, const int m, const int n, hipStream_t stream);\ntemplate void invokeMatrixTranspose(half* dst, const half* src, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void\ninvokeMatrixTranspose(__hip_fp8_e4m3_fnuz* dst, const __hip_fp8_e4m3_fnuz* src, const int m, const int n, hipStream_t stream);\n#endif  // ENABLE_FP8\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/matrix_transpose_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/image_shift_partition_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeNormalizeForFMHA  ***********************/\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(2*num_head, seqlen, batch)\n// block((size_per_head/2 + 31)/31*32)\n__global__ void normalize_for_FMHA_kernel(\n    half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, float factor_of_fmha)\n{\n    const int        batch_seqlen_id = blockIdx.z * seqlen + blockIdx.y;\n    const int        head_id         = blockIdx.x / 2;\n    const int        qkv_id          = blockIdx.x % 2;\n    const int        size_id         = threadIdx.x;\n    const size_t     input_idx   = ((batch_seqlen_id * num_head + head_id) * 3 + qkv_id) * size_per_head / 2 + size_id;\n    const float      logit_scale = ((qkv_id == 0) ? float(logit_scales[head_id]) * factor_of_fmha : 1.0f);\n    const half2      zero        = {half(0.0f), half(0.0f)};\n    const bool       flag        = size_id < size_per_head / 2;\n    half2            input_val   = (flag) ? data[input_idx] : zero;\n    float2           input_val_float2 = __half22float2(input_val);\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    input_val = __float22half2_rn(input_val_float2);\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\n// This kernel is designed for size_per_head = 32, typical case in swin\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(batch*seqlen*num_head/(LOOP*HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK) each warp load 64*LOOP*HEADS_PER_WARP elements (LOOP*HEADS_PER_WARP heads of q&k) and do\n// normlization\ntemplate<int LOOP, int HEADS_PER_WARP, int WARPS_PER_BLOCK>\n__global__ void normalize_for_FMHA_headz32_kernel(\n    half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, float factor_of_fmha)\n{\n\n    __shared__ half2 data_shm[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 32];\n    __shared__ float norm_factor[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 2];\n    const int        batch_seqlen_head_offset         = blockIdx.x * LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int        seqlen_head_offset               = batch_seqlen_head_offset % (seqlen * num_head);\n    const int        tid                              = threadIdx.x;\n    const int        warp_id                          = tid / 32;\n    const int        tid_in_warp                      = tid % 32;\n    const int        HEADS_PER_WARP_x_WARPS_PER_BLOCK = HEADS_PER_WARP * WARPS_PER_BLOCK;\n\n// load from gmem to smem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // one warp loads one head (32 threads load 32 half2)\n            const size_t input_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            // we need to ensure no out of memory address when launch kernel.\n            const half2 input_val = data[input_idx];\n            const int   shm_idx   = (head_offset + head_i) * 32 + tid_in_warp;\n            data_shm[shm_idx]     = input_val;\n        }\n    }\n    __syncthreads();\n\n    // we use one warp to deal with HEADS_PER_WARP heads at one time,\n    // so one thread deals with part of one single head at one time\n    float     local_sums[LOOP];\n    const int threads_per_head = 32 / HEADS_PER_WARP;\n    // each head has 32 half2\n    const int half2Size_per_thread = 32 / threads_per_head;\n    const int head_in_warp         = tid_in_warp / threads_per_head;\n    const int id_offset_in_head    = tid_in_warp % threads_per_head;\n    const int size_offset_in_head  = half2Size_per_thread * id_offset_in_head;\n\n    const int head_offset_of_warp = warp_id * HEADS_PER_WARP + head_in_warp;\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        float     local_sum = 0.0f;\n        const int shm_offset =\n            (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp) * 32 + size_offset_in_head;\n#pragma unroll\n        for (int size_i = 0; size_i < half2Size_per_thread; size_i++) {\n            const int    shm_idx = shm_offset + size_i;\n            const float2 tmp     = __half22float2(data_shm[shm_idx]);\n            local_sum += tmp.x * tmp.x + tmp.y * tmp.y;\n        }\n        local_sums[loop_i] = local_sum;\n    }\n\n    const int  threads_per_head_2 = threads_per_head / 2;\n    const bool is_q               = id_offset_in_head < threads_per_head_2;\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int seqlen_head_id = seqlen_head_offset + loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp;\n        const int head_id        = seqlen_head_id % num_head;\n        float     local_sum      = local_sums[loop_i];\n#pragma unroll\n        for (int i = 1; i < threads_per_head_2; i <<= 1) {\n            local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n        }\n        if (id_offset_in_head % threads_per_head_2 == 0) {\n            const float logit_scale     = is_q ? float(logit_scales[head_id]) * factor_of_fmha : 1.f;\n            const int   norm_factor_idx = 2 * (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp)\n                                        + id_offset_in_head / threads_per_head_2;\n            norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n        }\n    }\n    __syncthreads();\n\n// normalize and store to gmem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // we need to ensure no out of memory address when launch kernel, one warp deals with one head\n            const size_t output_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            const int head_idx        = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP + head_i;\n            const int shm_idx         = (head_idx)*32 + tid_in_warp;\n            const int norm_factor_idx = 2 * (head_idx) + tid_in_warp / 16;\n            float     norm_factor_    = norm_factor[norm_factor_idx];\n            half2     input_val       = data_shm[shm_idx];\n            float2    input_val_float = __half22float2(input_val);\n            input_val_float.x *= norm_factor_;\n            input_val_float.y *= norm_factor_;\n            data[output_idx] = __float22half2_rn(input_val_float);\n        }\n    }\n}\n\n#define NORMALIZE_FMHA_HEAD32_MACRO(LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK)                                             \\\n    dim3 grid(batch* seqlen_num_head / (LOOP * HEADS_PER_WARP * WARPS_PER_BLOCK));                                     \\\n    dim3 block(32 * WARPS_PER_BLOCK);                                                                                  \\\n    normalize_for_FMHA_headz32_kernel<LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK><<<grid, block, 0, stream>>>(              \\\n        (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, sqrt(size_per_head));\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\ntemplate<typename T>\nvoid invokeNormalizeForFMHA(\n    T* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head, cudaStream_t stream)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head == 32) {\n            const int seqlen_num_head = seqlen * num_head;\n            // LOOP = 2, HEADS_PER_WARP = 4, WARPS_PER_BLOCK = 2\n            if (seqlen_num_head % (2 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(2, 4, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 4, WARPS_PER_BLOCK = 2\n            else if (seqlen_num_head % (1 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 4, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 2, WARPS_PER_BLOCK = 2\n            else if (seqlen_num_head % (1 * 2 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 2, WARPS_PER_BLOCK = 1\n            else if (seqlen_num_head % (1 * 2 * 1) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 1)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 1, WARPS_PER_BLOCK = 1\n            else {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 1, 1)\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(2 * num_head, seqlen, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_for_FMHA_kernel<<<grid, block, 0, stream>>>(\n                (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, sqrt(size_per_head));\n        }\n        else {\n            printf(\"[ERROR][invokeNormalizeForFMHA] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalizeForFMHA] only supports half I/O!\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_FMHA_HEAD32_MACRO\n\ntemplate void invokeNormalizeForFMHA<half>(\n    half* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, cudaStream_t stream);\n\ntemplate void invokeNormalizeForFMHA<float>(float*       data,\n                                            const float* logit_scales,\n                                            int          batch,\n                                            int          seqlen,\n                                            int          num_head,\n                                            int          size_per_head,\n                                            cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeNormalizeForFMHA<__nv_bfloat16>(__nv_bfloat16*       data,\n                                                    const __nv_bfloat16* logit_scales,\n                                                    int                  batch,\n                                                    int                  seqlen,\n                                                    int                  num_head,\n                                                    int                  size_per_head,\n                                                    cudaStream_t         stream);\n\n#endif\n\n// This kernel is designed for size_per_head = 32, typical case in swin\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(batch*seqlen*num_head/(LOOP*HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK) each warp load 64*LOOP*HEADS_PER_WARP elements (LOOP*HEADS_PER_WARP heads of q&k) and do\n// normlization\ntemplate<int LOOP, int HEADS_PER_WARP, int WARPS_PER_BLOCK>\n__global__ void normalize_for_FMHA_headz32_INT8_kernel(char2*      data,\n                                                       const half* logit_scales,\n                                                       int         batch,\n                                                       int         seqlen,\n                                                       int         num_head,\n                                                       int         size_per_head,\n                                                       const float query_deQ_scale,\n                                                       const float key_deQ_scale,\n                                                       const float query_Q_scale,\n                                                       const float key_Q_scale)\n{\n\n    __shared__ char2 data_shm[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 32];\n    __shared__ float norm_factor[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 2];\n    const int        batch_seqlen_head_offset         = blockIdx.x * LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int        seqlen_head_offset               = batch_seqlen_head_offset % (seqlen * num_head);\n    const int        tid                              = threadIdx.x;\n    const int        warp_id                          = tid / 32;\n    const int        tid_in_warp                      = tid % 32;\n    const int        HEADS_PER_WARP_x_WARPS_PER_BLOCK = HEADS_PER_WARP * WARPS_PER_BLOCK;\n\n// load from gmem to smem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // one warp loads one head (32 threads load 32 half2)\n            const size_t input_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            // we need to ensure no out of memory address when launch kernel.\n            const char2 input_val = data[input_idx];\n            const int   shm_idx   = (head_offset + head_i) * 32 + tid_in_warp;\n            data_shm[shm_idx]     = input_val;\n        }\n    }\n    __syncthreads();\n\n    // we use one warp to deal with HEADS_PER_WARP heads at one time,\n    // so one thread deals with part of one single head at one time\n    float     local_sums[LOOP];\n    const int threads_per_head = 32 / HEADS_PER_WARP;\n    // each head has 32 half2\n    const int half2Size_per_thread = 32 / threads_per_head;\n    const int head_in_warp         = tid_in_warp / threads_per_head;\n    const int id_offset_in_head    = tid_in_warp % threads_per_head;\n    const int size_offset_in_head  = half2Size_per_thread * id_offset_in_head;\n\n    const int head_offset_of_warp = warp_id * HEADS_PER_WARP + head_in_warp;\n\n    const int  threads_per_head_2 = threads_per_head / 2;\n    const bool is_q               = id_offset_in_head < threads_per_head_2;\n    float      deQ_scale          = is_q ? query_deQ_scale : key_deQ_scale;\n    float      Q_scale            = is_q ? query_Q_scale : key_Q_scale;\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        float     local_sum = 0.0f;\n        const int shm_offset =\n            (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp) * 32 + size_offset_in_head;\n#pragma unroll\n        for (int size_i = 0; size_i < half2Size_per_thread; size_i++) {\n            const int   shm_idx = shm_offset + size_i;\n            const char2 tmp     = data_shm[shm_idx];\n            float2      tmpFloat;\n            tmpFloat.x = static_cast<float>(tmp.x) * deQ_scale;\n            tmpFloat.y = static_cast<float>(tmp.y) * deQ_scale;\n            local_sum += tmpFloat.x * tmpFloat.x + tmpFloat.y * tmpFloat.y;\n        }\n        local_sums[loop_i] = local_sum;\n    }\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int seqlen_head_id = seqlen_head_offset + loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp;\n        const int head_id        = seqlen_head_id % num_head;\n        float     local_sum      = local_sums[loop_i];\n#pragma unroll\n        for (int i = 1; i < threads_per_head_2; i <<= 1) {\n            local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n        }\n        if (id_offset_in_head % threads_per_head_2 == 0) {\n            const float logit_scale     = is_q ? float(logit_scales[head_id]) : 1.f;\n            const int   norm_factor_idx = 2 * (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp)\n                                        + id_offset_in_head / threads_per_head_2;\n            norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n        }\n    }\n    __syncthreads();\n\n// normalize and store to gmem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // we need to ensure no out of memory address when launch kernel, one warp deals with one head\n            const size_t output_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            const int head_idx        = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP + head_i;\n            const int shm_idx         = (head_idx)*32 + tid_in_warp;\n            const int norm_factor_idx = 2 * (head_idx) + tid_in_warp / 16;\n            deQ_scale                 = (tid_in_warp < 16) ? query_deQ_scale : key_deQ_scale;\n            Q_scale                   = (tid_in_warp < 16) ? query_Q_scale : key_Q_scale;\n            float  norm_factor_       = norm_factor[norm_factor_idx];\n            char2  input_val          = data_shm[shm_idx];\n            float2 input_val_float;\n            input_val_float.x = static_cast<float>(input_val.x) * deQ_scale * norm_factor_ * Q_scale;\n            input_val_float.y = static_cast<float>(input_val.y) * deQ_scale * norm_factor_ * Q_scale;\n            input_val.x       = float_to_int8_rn(input_val_float.x);\n            input_val.y       = float_to_int8_rn(input_val_float.y);\n            data[output_idx]  = input_val;\n        }\n    }\n}\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(2*num_head, seqlen, batch)\n// block((size_per_head/2 + 31)/31*32)\n__global__ void normalize_for_FMHA_INT8_kernel(char2*      data,\n                                               const half* logit_scales,\n                                               int         batch,\n                                               int         seqlen,\n                                               int         num_head,\n                                               int         size_per_head,\n                                               const float query_deQ_scale,\n                                               const float key_deQ_scale,\n                                               const float query_Q_scale,\n                                               const float key_Q_scale)\n{\n    const int    batch_seqlen_id  = blockIdx.z * seqlen + blockIdx.y;\n    const int    head_id          = blockIdx.x / 2;\n    const int    qkv_id           = blockIdx.x % 2;\n    const int    size_id          = threadIdx.x;\n    const size_t input_idx        = ((batch_seqlen_id * num_head + head_id) * 3 + qkv_id) * size_per_head / 2 + size_id;\n    const float  logit_scale      = ((qkv_id == 0) ? float(logit_scales[head_id]) : 1.0f);\n    const float  deQ_scale        = (qkv_id == 0) ? query_deQ_scale : key_deQ_scale;\n    const float  Q_scale          = (qkv_id == 0) ? query_Q_scale : key_Q_scale;\n    float2       input_val_float2 = {0.0f, 0.0f};\n    const bool   flag             = size_id < size_per_head / 2;\n    if (flag) {\n        char2 input_data   = data[input_idx];\n        input_val_float2.x = static_cast<float>(input_data.x) * deQ_scale;\n        input_val_float2.y = static_cast<float>(input_data.y) * deQ_scale;\n    }\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale * Q_scale;\n    }\n    __syncthreads();\n    if (flag) {\n        char2 output_val;\n        output_val.x    = float_to_int8_rn(input_val_float2.x * norm_factor);\n        output_val.y    = float_to_int8_rn(input_val_float2.y * norm_factor);\n        data[input_idx] = output_val;\n    }\n}\n\n#define NORMALIZE_FMHA_HEAD32_MACRO(LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK)                                             \\\n    dim3 grid(batch* seqlen_num_head / (LOOP * HEADS_PER_WARP * WARPS_PER_BLOCK));                                     \\\n    dim3 block(32 * WARPS_PER_BLOCK);                                                                                  \\\n    normalize_for_FMHA_headz32_INT8_kernel<LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK>                                      \\\n        <<<grid, block, 0, stream>>>((char2*)data,                                                                     \\\n                                     (const half*)logit_scales,                                                        \\\n                                     batch,                                                                            \\\n                                     seqlen,                                                                           \\\n                                     num_head,                                                                         \\\n                                     size_per_head,                                                                    \\\n                                     query_deQ_scale,                                                                  \\\n                                     key_deQ_scale,                                                                    \\\n                                     query_Q_scale,                                                                    \\\n                                     key_Q_scale);\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\ntemplate<typename T>\nvoid invokeNormalizeForFMHA(int8_t*      data,\n                            const T*     logit_scales,\n                            int          batch,\n                            int          seqlen,\n                            int          num_head,\n                            int          size_per_head,\n                            cudaStream_t stream,\n                            const float  query_deQ_scale,\n                            const float  key_deQ_scale,\n                            const float  query_Q_scale,\n                            const float  key_Q_scale)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head == 32) {\n            const int seqlen_num_head = seqlen * num_head;\n            if (seqlen_num_head % (2 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(2, 4, 2)\n            }\n            else if (seqlen_num_head % (1 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 4, 2)\n            }\n            else if (seqlen_num_head % (1 * 2 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 2)\n            }\n            else if (seqlen_num_head % (1 * 2 * 1) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 1)\n            }\n            else {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 1, 1)\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(2 * num_head, seqlen, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_for_FMHA_INT8_kernel<<<grid, block, 0, stream>>>((char2*)data,\n                                                                       (const half*)logit_scales,\n                                                                       batch,\n                                                                       seqlen,\n                                                                       num_head,\n                                                                       size_per_head,\n                                                                       query_deQ_scale,\n                                                                       key_deQ_scale,\n                                                                       query_Q_scale,\n                                                                       key_Q_scale);\n        }\n        else {\n            printf(\"[ERROR][invokeNormalizeForFMHA(INT8 version)] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalizeForFMHA(INT8 version)] only supports half Input!\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_FMHA_HEAD32_MACRO\n\ntemplate void invokeNormalizeForFMHA<float>(int8_t*      data,\n                                            const float* logit_scales,\n                                            int          batch,\n                                            int          seqlen,\n                                            int          num_head,\n                                            int          size_per_head,\n                                            cudaStream_t stream,\n                                            const float  query_deQ_scale,\n                                            const float  key_deQ_scale,\n                                            const float  query_Q_scale,\n                                            const float  key_Q_scale);\n\ntemplate void invokeNormalizeForFMHA<half>(int8_t*      data,\n                                           const half*  logit_scales,\n                                           int          batch,\n                                           int          seqlen,\n                                           int          num_head,\n                                           int          size_per_head,\n                                           cudaStream_t stream,\n                                           const float  query_deQ_scale,\n                                           const float  key_deQ_scale,\n                                           const float  query_Q_scale,\n                                           const float  key_Q_scale);\n\n/*******************  invokeNormalize  ***********************/\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(seqlen, num_head, batch)\n// block((size_per_head + 31)/31*32)\n// TODO : the trick of normalize_for_FMHA_headz32_kernel can be used here\ntemplate<typename T, typename T2>\n__global__ void\nnormalize_kernel(T* data, const T2* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? static_cast<float>(logit_scales[head_id]) : 1.0f;\n    const bool       flag        = size_id < size_per_head;\n    float            input_val   = (flag) ? static_cast<float>(data[input_idx]) : 0.0f;\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val * input_val;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrt(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val *= norm_factor;\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\n// grid(seqlen, num_head, batch)\n// block((size_per_head/2 + 31)/31*32)\n// TODO : the trick of normalize_for_FMHA_headz32_kernel can be used here\ntemplate<>\n__global__ void\nnormalize_kernel(half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head / 2 + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? float(logit_scales[head_id]) : 1.0f;\n    const half2      zero        = {half(0.0f), half(0.0f)};\n    const bool       flag        = size_id < size_per_head / 2;\n    half2            input_val   = (flag) ? data[input_idx] : zero;\n    float2           input_val_float2 = __half22float2(input_val);\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    input_val = __float22half2_rn(input_val_float2);\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\nextern __shared__ char normalize_kernel_v2_shm[];\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(batch*num_head*seqlen/(HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK)\n// size_per_head % ELEMENT_PER_LDG == 0\ntemplate<typename T_IO, typename T, int HEADS_PER_WARP, int WARPS_PER_BLOCK, int ELEMENT_PER_LDG>\n__global__ void\nnormalize_kernel_v2(T_IO* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int   tidx                 = threadIdx.x;\n    const int   bidx                 = blockIdx.x;\n    const int   threads_per_head     = 32 / HEADS_PER_WARP;\n    const int   lane_id              = tidx % 32;\n    const int   warp_id              = tidx / 32;\n    const int   head_offset_of_grid  = bidx * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int   head_offset_of_block = warp_id * HEADS_PER_WARP;\n    const int   head_id_in_warp      = lane_id / threads_per_head;\n    const int   head_id              = head_offset_of_grid + head_offset_of_block + head_id_in_warp;\n    const int   num_head_id          = (head_id / seqlen) % num_head;\n    const int   ldg_per_head         = size_per_head / ELEMENT_PER_LDG;\n    const float logit_scale          = (logit_scales != NULL) ? static_cast<float>(logit_scales[num_head_id]) : 1.0f;\n\n    T_IO*            normalize_shm = (T_IO*)normalize_kernel_v2_shm;\n    __shared__ float norm_factor[WARPS_PER_BLOCK * HEADS_PER_WARP];  // one factor for one head\n    const int        input_offset = head_offset_of_grid * ldg_per_head;\n    // load from gmem to smem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int input_idx = input_offset + i;\n        normalize_shm[i]    = data[input_idx];\n    }\n    __syncthreads();\n\n    // local sum\n    float     local_sum           = 0.0f;\n    const int elements_per_thread = (size_per_head + threads_per_head - 1) / threads_per_head;\n    const int thread_id_in_head   = tidx % threads_per_head;\n    const int size_offset_in_head = elements_per_thread * thread_id_in_head;\n    const int shm_offset          = (head_offset_of_block + head_id_in_warp) * size_per_head + size_offset_in_head;\n    const T*  shm_ptr             = (const T*)normalize_shm;\n#pragma unroll\n    for (int size_i = 0; size_i < elements_per_thread && (size_i + size_offset_in_head < size_per_head); size_i++) {\n        const int   shm_idx = shm_offset + size_i;\n        const float tmp     = static_cast<float>(shm_ptr[shm_idx]);\n        local_sum += tmp * tmp;\n    }\n\n    // reduction to get norm_factor\n#pragma unroll\n    for (int i = 1; i < threads_per_head; i <<= 1) {\n        local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n    }\n    if (thread_id_in_head == 0) {\n        const int norm_factor_idx    = head_offset_of_block + head_id_in_warp;\n        norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n\n// normalize and sts to gmem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int   norm_factor_idx = i / ldg_per_head;\n        const float norm_factor_val = norm_factor[norm_factor_idx];\n        T_IO        val             = normalize_shm[i];\n        T*          val_ptr         = (T*)(&val);\n#pragma unroll\n        for (int ei = 0; ei < ELEMENT_PER_LDG; ei++) {\n            val_ptr[ei] = T(static_cast<float>(val_ptr[ei]) * norm_factor_val);\n        }\n        const int input_idx = input_offset + i;\n        data[input_idx]     = val;\n    }\n}\n\n#define NORMALIZE_MACRO(HEADS_PER_WARP_, T_4, T_2, T)                                                                  \\\n    dim3      grid(total_head_count / (HEADS_PER_WARP_ * WARPS_PER_BLOCK));                                            \\\n    dim3      block(32 * WARPS_PER_BLOCK);                                                                             \\\n    const int shm_size = HEADS_PER_WARP_ * WARPS_PER_BLOCK * size_per_head * sizeof(T);                                \\\n    if (size_per_head % 4 == 0) {                                                                                      \\\n        normalize_kernel_v2<T_4, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 4><<<grid, block, shm_size, stream>>>(           \\\n            (T_4*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                               \\\n    }                                                                                                                  \\\n    else if (size_per_head % 2 == 0) {                                                                                 \\\n        normalize_kernel_v2<T_2, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 2><<<grid, block, shm_size, stream>>>(           \\\n            (T_2*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                               \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        normalize_kernel_v2<T, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 1><<<grid, block, shm_size, stream>>>(             \\\n            (T*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                                 \\\n    }\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\ntemplate<typename T>\nvoid invokeNormalize(\n    T* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head, cudaStream_t stream)\n{\n    const int WARPS_PER_BLOCK  = 4;\n    const int total_head_count = batch * num_head * seqlen;\n    if (std::is_same<T, float>::value) {\n        // WARPS_PER_BLOCK = 4, HEADS_PER_WARP = 4\n        if (total_head_count % (WARPS_PER_BLOCK * 4) == 0) {\n            NORMALIZE_MACRO(4, float4, float2, float);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 2\n        else if (total_head_count % (WARPS_PER_BLOCK * 2) == 0) {\n            NORMALIZE_MACRO(2, float4, float2, float);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 1\n        else if (total_head_count % WARPS_PER_BLOCK == 0) {\n            NORMALIZE_MACRO(1, float4, float2, float);\n        }\n        else {\n            dim3 grid(seqlen, num_head, batch);\n            dim3 block((size_per_head + 31) / 32 * 32);\n            normalize_kernel<<<grid, block, 0, stream>>>(\n                (float*)data, (const float*)logit_scales, batch, seqlen, num_head, size_per_head);\n        }\n    }\n    else if (std::is_same<T, half>::value) {\n        // WARPS_PER_BLOCK = 4, HEADS_PER_WARP = 4\n        if (total_head_count % (WARPS_PER_BLOCK * 4) == 0) {\n            NORMALIZE_MACRO(4, half4, half2, half);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 2\n        else if (total_head_count % (WARPS_PER_BLOCK * 2) == 0) {\n            NORMALIZE_MACRO(2, half4, half2, half);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 1\n        else if (total_head_count % WARPS_PER_BLOCK == 0) {\n            NORMALIZE_MACRO(1, half4, half2, half);\n        }\n        else {\n            if (size_per_head % 2 == 0) {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head / 2 + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>(\n                    (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head);\n            }\n            else {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>(\n                    (half*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head);\n            }\n        }\n    }\n#ifdef ENABLE_BF16\n    else {\n        dim3 grid(seqlen, num_head, batch);\n        dim3 block((size_per_head + 31) / 32 * 32);\n        normalize_kernel<<<grid, block, 0, stream>>>(data, logit_scales, batch, seqlen, num_head, size_per_head);\n    }\n#endif\n}\n\n#undef NORMALIZE_MACRO\n\ntemplate void invokeNormalize<half>(\n    half* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, cudaStream_t stream);\n\ntemplate void invokeNormalize<float>(float*       data,\n                                     const float* logit_scales,\n                                     int          batch,\n                                     int          seqlen,\n                                     int          num_head,\n                                     int          size_per_head,\n                                     cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeNormalize<__nv_bfloat16>(__nv_bfloat16*       data,\n                                             const __nv_bfloat16* logit_scales,\n                                             int                  batch,\n                                             int                  seqlen,\n                                             int                  num_head,\n                                             int                  size_per_head,\n                                             cudaStream_t         stream);\n\n#endif\n\n/*******************  invokeNormalize  ***********************/\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(seqlen, num_head, batch)\n// block((size_per_head + 31)/31*32)\ntemplate<typename T>\n__global__ void normalize_kernel(int8_t*     data,\n                                 const T*    logit_scales,\n                                 int         batch,\n                                 int         seqlen,\n                                 int         num_head,\n                                 int         size_per_head,\n                                 const float deQ_scale,\n                                 const float Q_scale)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? static_cast<float>(logit_scales[head_id]) : 1.0f;\n    const bool       flag        = size_id < size_per_head;\n    float            input_val   = (flag) ? static_cast<float>(data[input_idx]) * deQ_scale : 0.0f;\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val * input_val;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrt(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val *= norm_factor;\n    if (flag) {\n        data[input_idx] = float_to_int8_rn(input_val * Q_scale);\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\n// grid(seqlen, num_head, batch)\n// block((size_per_head/2 + 31)/31*32)\ntemplate<>\n__global__ void normalize_kernel(int8_t*     data,\n                                 const half* logit_scales,\n                                 int         batch,\n                                 int         seqlen,\n                                 int         num_head,\n                                 int         size_per_head,\n                                 const float deQ_scale,\n                                 const float Q_scale)\n{\n    const int   batch_id         = blockIdx.z;\n    const int   head_id          = blockIdx.y;\n    const int   seq_id           = blockIdx.x;\n    const int   size_id          = threadIdx.x;\n    const int   input_idx        = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head / 2 + size_id;\n    const float logit_scale      = (logit_scales != NULL) ? float(logit_scales[head_id]) : 1.0f;\n    float2      input_val_float2 = {0.0f, 0.0f};\n    const bool  flag             = size_id < size_per_head / 2;\n    char2*      dataPtr          = (char2*)data;\n    if (flag) {\n        char2 dataTmp      = dataPtr[input_idx];\n        input_val_float2.x = static_cast<float>(dataTmp.x) * deQ_scale;\n        input_val_float2.y = static_cast<float>(dataTmp.y) * deQ_scale;\n    }\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    if (flag) {\n        char2 dataTmp;\n        dataTmp.x          = float_to_int8_rn(input_val_float2.x * Q_scale);\n        dataTmp.y          = float_to_int8_rn(input_val_float2.y * Q_scale);\n        dataPtr[input_idx] = dataTmp;\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(batch*num_head*seqlen/(HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK)\n// size_per_head % ELEMENT_PER_LDG == 0\ntemplate<int HEADS_PER_WARP, int WARPS_PER_BLOCK, int ELEMENT_PER_LDG>\n__global__ void normalize_kernel_v2(char4*      data,\n                                    const half* logit_scales,\n                                    int         batch,\n                                    int         seqlen,\n                                    int         num_head,\n                                    int         size_per_head,\n                                    const float deQ_scale,\n                                    const float Q_scale)\n{\n    const int   tidx                 = threadIdx.x;\n    const int   bidx                 = blockIdx.x;\n    const int   threads_per_head     = 32 / HEADS_PER_WARP;\n    const int   lane_id              = tidx % 32;\n    const int   warp_id              = tidx / 32;\n    const int   head_offset_of_grid  = bidx * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int   head_offset_of_block = warp_id * HEADS_PER_WARP;\n    const int   head_id_in_warp      = lane_id / threads_per_head;\n    const int   head_id              = head_offset_of_grid + head_offset_of_block + head_id_in_warp;\n    const int   num_head_id          = (head_id / seqlen) % num_head;\n    const int   ldg_per_head         = size_per_head / ELEMENT_PER_LDG;\n    const float logit_scale          = (logit_scales != NULL) ? static_cast<float>(logit_scales[num_head_id]) : 1.0f;\n\n    char4*           normalize_shm = (char4*)normalize_kernel_v2_shm;\n    __shared__ float norm_factor[WARPS_PER_BLOCK * HEADS_PER_WARP];  // one factor for one head\n    const int        input_offset = head_offset_of_grid * ldg_per_head;\n    // load from gmem to smem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int input_idx = input_offset + i;\n        normalize_shm[i]    = data[input_idx];\n    }\n    __syncthreads();\n\n    // local sum\n    float         local_sum           = 0.0f;\n    const int     elements_per_thread = (size_per_head + threads_per_head - 1) / threads_per_head;\n    const int     thread_id_in_head   = tidx % threads_per_head;\n    const int     size_offset_in_head = elements_per_thread * thread_id_in_head;\n    const int     shm_offset          = (head_offset_of_block + head_id_in_warp) * size_per_head + size_offset_in_head;\n    const int8_t* shm_ptr             = (const int8_t*)normalize_shm;\n#pragma unroll\n    for (int size_i = 0; size_i < elements_per_thread && (size_i + size_offset_in_head < size_per_head); size_i++) {\n        const int   shm_idx = shm_offset + size_i;\n        const float tmp     = static_cast<float>(shm_ptr[shm_idx]) * deQ_scale;\n        local_sum += tmp * tmp;\n    }\n\n    // reduction to get norm_factor\n#pragma unroll\n    for (int i = 1; i < threads_per_head; i <<= 1) {\n        local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n    }\n    if (thread_id_in_head == 0) {\n        const int norm_factor_idx    = head_offset_of_block + head_id_in_warp;\n        norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n\n// normalize and sts to gmem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int   norm_factor_idx = i / ldg_per_head;\n        const float norm_factor_val = norm_factor[norm_factor_idx];\n        char4       val             = normalize_shm[i];\n        int8_t*     val_ptr         = (int8_t*)(&val);\n#pragma unroll\n        for (int ei = 0; ei < ELEMENT_PER_LDG; ei++) {\n            val_ptr[ei] = float_to_int8_rn(static_cast<float>(val_ptr[ei]) * deQ_scale * norm_factor_val * Q_scale);\n        }\n        const int input_idx = input_offset + i;\n        data[input_idx]     = val;\n    }\n}\n\n#define NORMALIZE_MACRO                                                                                                \\\n    const int shm_size = WARPS_PER_BLOCK * HEADS_PER_WARP * size_per_head;                                             \\\n    dim3      grid(total_head_count / (WARPS_PER_BLOCK * HEADS_PER_WARP));                                             \\\n    dim3      block(32 * WARPS_PER_BLOCK);                                                                             \\\n    normalize_kernel_v2<HEADS_PER_WARP, WARPS_PER_BLOCK, 4><<<grid, block, shm_size, stream>>>(                        \\\n        (char4*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, deQ_scale, Q_scale);\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\ntemplate<typename T>\nvoid invokeNormalize(int8_t*      data,\n                     const T*     logit_scales,\n                     int          batch,\n                     int          seqlen,\n                     int          num_head,\n                     int          size_per_head,\n                     cudaStream_t stream,\n                     const float  deQ_scale,\n                     const float  Q_scale)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head % 4 == 0) {\n            const int HEADS_PER_WARP   = 4;\n            const int total_head_count = seqlen * num_head * batch;\n            if (total_head_count % (HEADS_PER_WARP * 4) == 0) {\n                const int WARPS_PER_BLOCK = 4;\n                NORMALIZE_MACRO\n            }\n            else if (total_head_count % (HEADS_PER_WARP * 2) == 0) {\n                const int WARPS_PER_BLOCK = 2;\n                NORMALIZE_MACRO\n            }\n            else if (total_head_count % (HEADS_PER_WARP * 1) == 0) {\n                const int WARPS_PER_BLOCK = 1;\n                NORMALIZE_MACRO\n            }\n            else {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head / 2 + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>((int8_t*)data,\n                                                             (const half*)logit_scales,\n                                                             batch,\n                                                             seqlen,\n                                                             num_head,\n                                                             size_per_head,\n                                                             deQ_scale,\n                                                             Q_scale);\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(seqlen, num_head, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_kernel<<<grid, block, 0, stream>>>(\n                (int8_t*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, deQ_scale, Q_scale);\n        }\n        else {\n            printf(\"[ERROR][invokeNormalize(INT8 version)] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalize(INT8 version)] only supports [T=half] !\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_MACRO\n\ntemplate void invokeNormalize<half>(int8_t*      data,\n                                    const half*  logit_scales,\n                                    int          batch,\n                                    int          seqlen,\n                                    int          num_head,\n                                    int          size_per_head,\n                                    cudaStream_t stream,\n                                    const float  deQ_scale,\n                                    const float  Q_scale);\n\ntemplate void invokeNormalize<float>(int8_t*      data,\n                                     const float* logit_scales,\n                                     int          batch,\n                                     int          seqlen,\n                                     int          num_head,\n                                     int          size_per_head,\n                                     cudaStream_t stream,\n                                     const float  deQ_scale,\n                                     const float  Q_scale);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/image_shift_partition_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeNormalizeForFMHA  ***********************/\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(2*num_head, seqlen, batch)\n// block((size_per_head/2 + 31)/31*32)\n__global__ void normalize_for_FMHA_kernel(\n    half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, float factor_of_fmha)\n{\n    const int        batch_seqlen_id = blockIdx.z * seqlen + blockIdx.y;\n    const int        head_id         = blockIdx.x / 2;\n    const int        qkv_id          = blockIdx.x % 2;\n    const int        size_id         = threadIdx.x;\n    const size_t     input_idx   = ((batch_seqlen_id * num_head + head_id) * 3 + qkv_id) * size_per_head / 2 + size_id;\n    const float      logit_scale = ((qkv_id == 0) ? float(logit_scales[head_id]) * factor_of_fmha : 1.0f);\n    const half2      zero        = {half(0.0f), half(0.0f)};\n    const bool       flag        = size_id < size_per_head / 2;\n    half2            input_val   = (flag) ? data[input_idx] : zero;\n    float2           input_val_float2 = __half22float2(input_val);\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    input_val = __float22half2_rn(input_val_float2);\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\n// This kernel is designed for size_per_head = 32, typical case in swin\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(batch*seqlen*num_head/(LOOP*HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK) each warp load 64*LOOP*HEADS_PER_WARP elements (LOOP*HEADS_PER_WARP heads of q&k) and do\n// normlization\ntemplate<int LOOP, int HEADS_PER_WARP, int WARPS_PER_BLOCK>\n__global__ void normalize_for_FMHA_headz32_kernel(\n    half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, float factor_of_fmha)\n{\n\n    __shared__ half2 data_shm[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 32];\n    __shared__ float norm_factor[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 2];\n    const int        batch_seqlen_head_offset         = blockIdx.x * LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int        seqlen_head_offset               = batch_seqlen_head_offset % (seqlen * num_head);\n    const int        tid                              = threadIdx.x;\n    const int        warp_id                          = tid / 32;\n    const int        tid_in_warp                      = tid % 32;\n    const int        HEADS_PER_WARP_x_WARPS_PER_BLOCK = HEADS_PER_WARP * WARPS_PER_BLOCK;\n\n// load from gmem to smem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // one warp loads one head (32 threads load 32 half2)\n            const size_t input_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            // we need to ensure no out of memory address when launch kernel.\n            const half2 input_val = data[input_idx];\n            const int   shm_idx   = (head_offset + head_i) * 32 + tid_in_warp;\n            data_shm[shm_idx]     = input_val;\n        }\n    }\n    __syncthreads();\n\n    // we use one warp to deal with HEADS_PER_WARP heads at one time,\n    // so one thread deals with part of one single head at one time\n    float     local_sums[LOOP];\n    const int threads_per_head = 32 / HEADS_PER_WARP;\n    // each head has 32 half2\n    const int half2Size_per_thread = 32 / threads_per_head;\n    const int head_in_warp         = tid_in_warp / threads_per_head;\n    const int id_offset_in_head    = tid_in_warp % threads_per_head;\n    const int size_offset_in_head  = half2Size_per_thread * id_offset_in_head;\n\n    const int head_offset_of_warp = warp_id * HEADS_PER_WARP + head_in_warp;\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        float     local_sum = 0.0f;\n        const int shm_offset =\n            (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp) * 32 + size_offset_in_head;\n#pragma unroll\n        for (int size_i = 0; size_i < half2Size_per_thread; size_i++) {\n            const int    shm_idx = shm_offset + size_i;\n            const float2 tmp     = __half22float2(data_shm[shm_idx]);\n            local_sum += tmp.x * tmp.x + tmp.y * tmp.y;\n        }\n        local_sums[loop_i] = local_sum;\n    }\n\n    const int  threads_per_head_2 = threads_per_head / 2;\n    const bool is_q               = id_offset_in_head < threads_per_head_2;\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int seqlen_head_id = seqlen_head_offset + loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp;\n        const int head_id        = seqlen_head_id % num_head;\n        float     local_sum      = local_sums[loop_i];\n#pragma unroll\n        for (int i = 1; i < threads_per_head_2; i <<= 1) {\n            local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n        }\n        if (id_offset_in_head % threads_per_head_2 == 0) {\n            const float logit_scale     = is_q ? float(logit_scales[head_id]) * factor_of_fmha : 1.f;\n            const int   norm_factor_idx = 2 * (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp)\n                                        + id_offset_in_head / threads_per_head_2;\n            norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n        }\n    }\n    __syncthreads();\n\n// normalize and store to gmem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // we need to ensure no out of memory address when launch kernel, one warp deals with one head\n            const size_t output_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            const int head_idx        = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP + head_i;\n            const int shm_idx         = (head_idx)*32 + tid_in_warp;\n            const int norm_factor_idx = 2 * (head_idx) + tid_in_warp / 16;\n            float     norm_factor_    = norm_factor[norm_factor_idx];\n            half2     input_val       = data_shm[shm_idx];\n            float2    input_val_float = __half22float2(input_val);\n            input_val_float.x *= norm_factor_;\n            input_val_float.y *= norm_factor_;\n            data[output_idx] = __float22half2_rn(input_val_float);\n        }\n    }\n}\n\n#define NORMALIZE_FMHA_HEAD32_MACRO(LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK)                                             \\\n    dim3 grid(batch* seqlen_num_head / (LOOP * HEADS_PER_WARP * WARPS_PER_BLOCK));                                     \\\n    dim3 block(32 * WARPS_PER_BLOCK);                                                                                  \\\n    normalize_for_FMHA_headz32_kernel<LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK><<<grid, block, 0, stream>>>(              \\\n        (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, sqrt(size_per_head));\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\ntemplate<typename T>\nvoid invokeNormalizeForFMHA(\n    T* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head, hipStream_t stream)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head == 32) {\n            const int seqlen_num_head = seqlen * num_head;\n            // LOOP = 2, HEADS_PER_WARP = 4, WARPS_PER_BLOCK = 2\n            if (seqlen_num_head % (2 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(2, 4, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 4, WARPS_PER_BLOCK = 2\n            else if (seqlen_num_head % (1 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 4, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 2, WARPS_PER_BLOCK = 2\n            else if (seqlen_num_head % (1 * 2 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 2)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 2, WARPS_PER_BLOCK = 1\n            else if (seqlen_num_head % (1 * 2 * 1) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 1)\n            }\n            // LOOP = 1, HEADS_PER_WARP = 1, WARPS_PER_BLOCK = 1\n            else {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 1, 1)\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(2 * num_head, seqlen, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_for_FMHA_kernel<<<grid, block, 0, stream>>>(\n                (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, sqrt(size_per_head));\n        }\n        else {\n            printf(\"[ERROR][invokeNormalizeForFMHA] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalizeForFMHA] only supports half I/O!\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_FMHA_HEAD32_MACRO\n\ntemplate void invokeNormalizeForFMHA<half>(\n    half* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, hipStream_t stream);\n\ntemplate void invokeNormalizeForFMHA<float>(float*       data,\n                                            const float* logit_scales,\n                                            int          batch,\n                                            int          seqlen,\n                                            int          num_head,\n                                            int          size_per_head,\n                                            hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeNormalizeForFMHA<__hip_bfloat16>(__hip_bfloat16*       data,\n                                                    const __hip_bfloat16* logit_scales,\n                                                    int                  batch,\n                                                    int                  seqlen,\n                                                    int                  num_head,\n                                                    int                  size_per_head,\n                                                    hipStream_t         stream);\n\n#endif\n\n// This kernel is designed for size_per_head = 32, typical case in swin\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(batch*seqlen*num_head/(LOOP*HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK) each warp load 64*LOOP*HEADS_PER_WARP elements (LOOP*HEADS_PER_WARP heads of q&k) and do\n// normlization\ntemplate<int LOOP, int HEADS_PER_WARP, int WARPS_PER_BLOCK>\n__global__ void normalize_for_FMHA_headz32_INT8_kernel(char2*      data,\n                                                       const half* logit_scales,\n                                                       int         batch,\n                                                       int         seqlen,\n                                                       int         num_head,\n                                                       int         size_per_head,\n                                                       const float query_deQ_scale,\n                                                       const float key_deQ_scale,\n                                                       const float query_Q_scale,\n                                                       const float key_Q_scale)\n{\n\n    __shared__ char2 data_shm[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 32];\n    __shared__ float norm_factor[LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP * 2];\n    const int        batch_seqlen_head_offset         = blockIdx.x * LOOP * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int        seqlen_head_offset               = batch_seqlen_head_offset % (seqlen * num_head);\n    const int        tid                              = threadIdx.x;\n    const int        warp_id                          = tid / 32;\n    const int        tid_in_warp                      = tid % 32;\n    const int        HEADS_PER_WARP_x_WARPS_PER_BLOCK = HEADS_PER_WARP * WARPS_PER_BLOCK;\n\n// load from gmem to smem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // one warp loads one head (32 threads load 32 half2)\n            const size_t input_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            // we need to ensure no out of memory address when launch kernel.\n            const char2 input_val = data[input_idx];\n            const int   shm_idx   = (head_offset + head_i) * 32 + tid_in_warp;\n            data_shm[shm_idx]     = input_val;\n        }\n    }\n    __syncthreads();\n\n    // we use one warp to deal with HEADS_PER_WARP heads at one time,\n    // so one thread deals with part of one single head at one time\n    float     local_sums[LOOP];\n    const int threads_per_head = 32 / HEADS_PER_WARP;\n    // each head has 32 half2\n    const int half2Size_per_thread = 32 / threads_per_head;\n    const int head_in_warp         = tid_in_warp / threads_per_head;\n    const int id_offset_in_head    = tid_in_warp % threads_per_head;\n    const int size_offset_in_head  = half2Size_per_thread * id_offset_in_head;\n\n    const int head_offset_of_warp = warp_id * HEADS_PER_WARP + head_in_warp;\n\n    const int  threads_per_head_2 = threads_per_head / 2;\n    const bool is_q               = id_offset_in_head < threads_per_head_2;\n    float      deQ_scale          = is_q ? query_deQ_scale : key_deQ_scale;\n    float      Q_scale            = is_q ? query_Q_scale : key_Q_scale;\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        float     local_sum = 0.0f;\n        const int shm_offset =\n            (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp) * 32 + size_offset_in_head;\n#pragma unroll\n        for (int size_i = 0; size_i < half2Size_per_thread; size_i++) {\n            const int   shm_idx = shm_offset + size_i;\n            const char2 tmp     = data_shm[shm_idx];\n            float2      tmpFloat;\n            tmpFloat.x = static_cast<float>(tmp.x) * deQ_scale;\n            tmpFloat.y = static_cast<float>(tmp.y) * deQ_scale;\n            local_sum += tmpFloat.x * tmpFloat.x + tmpFloat.y * tmpFloat.y;\n        }\n        local_sums[loop_i] = local_sum;\n    }\n\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int seqlen_head_id = seqlen_head_offset + loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp;\n        const int head_id        = seqlen_head_id % num_head;\n        float     local_sum      = local_sums[loop_i];\n#pragma unroll\n        for (int i = 1; i < threads_per_head_2; i <<= 1) {\n            local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n        }\n        if (id_offset_in_head % threads_per_head_2 == 0) {\n            const float logit_scale     = is_q ? float(logit_scales[head_id]) : 1.f;\n            const int   norm_factor_idx = 2 * (loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + head_offset_of_warp)\n                                        + id_offset_in_head / threads_per_head_2;\n            norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n        }\n    }\n    __syncthreads();\n\n// normalize and store to gmem\n#pragma unroll\n    for (int loop_i = 0; loop_i < LOOP; loop_i++) {\n        const int head_offset = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP;\n#pragma unroll\n        for (int head_i = 0; head_i < HEADS_PER_WARP; head_i++) {\n            // we need to ensure no out of memory address when launch kernel, one warp deals with one head\n            const size_t output_idx =\n                ((batch_seqlen_head_offset + head_offset + head_i) * 3) * size_per_head / 2 + tid_in_warp;\n            const int head_idx        = loop_i * HEADS_PER_WARP_x_WARPS_PER_BLOCK + warp_id * HEADS_PER_WARP + head_i;\n            const int shm_idx         = (head_idx)*32 + tid_in_warp;\n            const int norm_factor_idx = 2 * (head_idx) + tid_in_warp / 16;\n            deQ_scale                 = (tid_in_warp < 16) ? query_deQ_scale : key_deQ_scale;\n            Q_scale                   = (tid_in_warp < 16) ? query_Q_scale : key_Q_scale;\n            float  norm_factor_       = norm_factor[norm_factor_idx];\n            char2  input_val          = data_shm[shm_idx];\n            float2 input_val_float;\n            input_val_float.x = static_cast<float>(input_val.x) * deQ_scale * norm_factor_ * Q_scale;\n            input_val_float.y = static_cast<float>(input_val.y) * deQ_scale * norm_factor_ * Q_scale;\n            input_val.x       = float_to_int8_rn(input_val_float.x);\n            input_val.y       = float_to_int8_rn(input_val_float.y);\n            data[output_idx]  = input_val;\n        }\n    }\n}\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\n// grid(2*num_head, seqlen, batch)\n// block((size_per_head/2 + 31)/31*32)\n__global__ void normalize_for_FMHA_INT8_kernel(char2*      data,\n                                               const half* logit_scales,\n                                               int         batch,\n                                               int         seqlen,\n                                               int         num_head,\n                                               int         size_per_head,\n                                               const float query_deQ_scale,\n                                               const float key_deQ_scale,\n                                               const float query_Q_scale,\n                                               const float key_Q_scale)\n{\n    const int    batch_seqlen_id  = blockIdx.z * seqlen + blockIdx.y;\n    const int    head_id          = blockIdx.x / 2;\n    const int    qkv_id           = blockIdx.x % 2;\n    const int    size_id          = threadIdx.x;\n    const size_t input_idx        = ((batch_seqlen_id * num_head + head_id) * 3 + qkv_id) * size_per_head / 2 + size_id;\n    const float  logit_scale      = ((qkv_id == 0) ? float(logit_scales[head_id]) : 1.0f);\n    const float  deQ_scale        = (qkv_id == 0) ? query_deQ_scale : key_deQ_scale;\n    const float  Q_scale          = (qkv_id == 0) ? query_Q_scale : key_Q_scale;\n    float2       input_val_float2 = {0.0f, 0.0f};\n    const bool   flag             = size_id < size_per_head / 2;\n    if (flag) {\n        char2 input_data   = data[input_idx];\n        input_val_float2.x = static_cast<float>(input_data.x) * deQ_scale;\n        input_val_float2.y = static_cast<float>(input_data.y) * deQ_scale;\n    }\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale * Q_scale;\n    }\n    __syncthreads();\n    if (flag) {\n        char2 output_val;\n        output_val.x    = float_to_int8_rn(input_val_float2.x * norm_factor);\n        output_val.y    = float_to_int8_rn(input_val_float2.y * norm_factor);\n        data[input_idx] = output_val;\n    }\n}\n\n#define NORMALIZE_FMHA_HEAD32_MACRO(LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK)                                             \\\n    dim3 grid(batch* seqlen_num_head / (LOOP * HEADS_PER_WARP * WARPS_PER_BLOCK));                                     \\\n    dim3 block(32 * WARPS_PER_BLOCK);                                                                                  \\\n    normalize_for_FMHA_headz32_INT8_kernel<LOOP, HEADS_PER_WARP, WARPS_PER_BLOCK>                                      \\\n        <<<grid, block, 0, stream>>>((char2*)data,                                                                     \\\n                                     (const half*)logit_scales,                                                        \\\n                                     batch,                                                                            \\\n                                     seqlen,                                                                           \\\n                                     num_head,                                                                         \\\n                                     size_per_head,                                                                    \\\n                                     query_deQ_scale,                                                                  \\\n                                     key_deQ_scale,                                                                    \\\n                                     query_Q_scale,                                                                    \\\n                                     key_Q_scale);\n\n// input should be the qkv for trt fmha kernels with shape of [batch, seqlen, num_head, 3, size_per_head]\n// do normlization on size_per_head for q & k [-, -, -, 0, *] && [-, -, -, 1, *] && *(logit_scale/sqrt(size_per_head))\n// for q (since fmha will divide sqrt(size_per_head), we multiple sqrt(size_per_head) first)\ntemplate<typename T>\nvoid invokeNormalizeForFMHA(int8_t*      data,\n                            const T*     logit_scales,\n                            int          batch,\n                            int          seqlen,\n                            int          num_head,\n                            int          size_per_head,\n                            hipStream_t stream,\n                            const float  query_deQ_scale,\n                            const float  key_deQ_scale,\n                            const float  query_Q_scale,\n                            const float  key_Q_scale)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head == 32) {\n            const int seqlen_num_head = seqlen * num_head;\n            if (seqlen_num_head % (2 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(2, 4, 2)\n            }\n            else if (seqlen_num_head % (1 * 4 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 4, 2)\n            }\n            else if (seqlen_num_head % (1 * 2 * 2) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 2)\n            }\n            else if (seqlen_num_head % (1 * 2 * 1) == 0) {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 2, 1)\n            }\n            else {\n                NORMALIZE_FMHA_HEAD32_MACRO(1, 1, 1)\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(2 * num_head, seqlen, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_for_FMHA_INT8_kernel<<<grid, block, 0, stream>>>((char2*)data,\n                                                                       (const half*)logit_scales,\n                                                                       batch,\n                                                                       seqlen,\n                                                                       num_head,\n                                                                       size_per_head,\n                                                                       query_deQ_scale,\n                                                                       key_deQ_scale,\n                                                                       query_Q_scale,\n                                                                       key_Q_scale);\n        }\n        else {\n            printf(\"[ERROR][invokeNormalizeForFMHA(INT8 version)] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalizeForFMHA(INT8 version)] only supports half Input!\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_FMHA_HEAD32_MACRO\n\ntemplate void invokeNormalizeForFMHA<float>(int8_t*      data,\n                                            const float* logit_scales,\n                                            int          batch,\n                                            int          seqlen,\n                                            int          num_head,\n                                            int          size_per_head,\n                                            hipStream_t stream,\n                                            const float  query_deQ_scale,\n                                            const float  key_deQ_scale,\n                                            const float  query_Q_scale,\n                                            const float  key_Q_scale);\n\ntemplate void invokeNormalizeForFMHA<half>(int8_t*      data,\n                                           const half*  logit_scales,\n                                           int          batch,\n                                           int          seqlen,\n                                           int          num_head,\n                                           int          size_per_head,\n                                           hipStream_t stream,\n                                           const float  query_deQ_scale,\n                                           const float  key_deQ_scale,\n                                           const float  query_Q_scale,\n                                           const float  key_Q_scale);\n\n/*******************  invokeNormalize  ***********************/\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(seqlen, num_head, batch)\n// block((size_per_head + 31)/31*32)\n// TODO : the trick of normalize_for_FMHA_headz32_kernel can be used here\ntemplate<typename T, typename T2>\n__global__ void\nnormalize_kernel(T* data, const T2* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? static_cast<float>(logit_scales[head_id]) : 1.0f;\n    const bool       flag        = size_id < size_per_head;\n    float            input_val   = (flag) ? static_cast<float>(data[input_idx]) : 0.0f;\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val * input_val;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrt(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val *= norm_factor;\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\n// grid(seqlen, num_head, batch)\n// block((size_per_head/2 + 31)/31*32)\n// TODO : the trick of normalize_for_FMHA_headz32_kernel can be used here\ntemplate<>\n__global__ void\nnormalize_kernel(half2* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head / 2 + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? float(logit_scales[head_id]) : 1.0f;\n    const half2      zero        = {half(0.0f), half(0.0f)};\n    const bool       flag        = size_id < size_per_head / 2;\n    half2            input_val   = (flag) ? data[input_idx] : zero;\n    float2           input_val_float2 = __half22float2(input_val);\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    input_val = __float22half2_rn(input_val_float2);\n    if (flag) {\n        data[input_idx] = input_val;\n    }\n}\n\nextern __shared__ char normalize_kernel_v2_shm[];\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(batch*num_head*seqlen/(HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK)\n// size_per_head % ELEMENT_PER_LDG == 0\ntemplate<typename T_IO, typename T, int HEADS_PER_WARP, int WARPS_PER_BLOCK, int ELEMENT_PER_LDG>\n__global__ void\nnormalize_kernel_v2(T_IO* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head)\n{\n    const int   tidx                 = threadIdx.x;\n    const int   bidx                 = blockIdx.x;\n    const int   threads_per_head     = 32 / HEADS_PER_WARP;\n    const int   lane_id              = tidx % 32;\n    const int   warp_id              = tidx / 32;\n    const int   head_offset_of_grid  = bidx * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int   head_offset_of_block = warp_id * HEADS_PER_WARP;\n    const int   head_id_in_warp      = lane_id / threads_per_head;\n    const int   head_id              = head_offset_of_grid + head_offset_of_block + head_id_in_warp;\n    const int   num_head_id          = (head_id / seqlen) % num_head;\n    const int   ldg_per_head         = size_per_head / ELEMENT_PER_LDG;\n    const float logit_scale          = (logit_scales != NULL) ? static_cast<float>(logit_scales[num_head_id]) : 1.0f;\n\n    T_IO*            normalize_shm = (T_IO*)normalize_kernel_v2_shm;\n    __shared__ float norm_factor[WARPS_PER_BLOCK * HEADS_PER_WARP];  // one factor for one head\n    const int        input_offset = head_offset_of_grid * ldg_per_head;\n    // load from gmem to smem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int input_idx = input_offset + i;\n        normalize_shm[i]    = data[input_idx];\n    }\n    __syncthreads();\n\n    // local sum\n    float     local_sum           = 0.0f;\n    const int elements_per_thread = (size_per_head + threads_per_head - 1) / threads_per_head;\n    const int thread_id_in_head   = tidx % threads_per_head;\n    const int size_offset_in_head = elements_per_thread * thread_id_in_head;\n    const int shm_offset          = (head_offset_of_block + head_id_in_warp) * size_per_head + size_offset_in_head;\n    const T*  shm_ptr             = (const T*)normalize_shm;\n#pragma unroll\n    for (int size_i = 0; size_i < elements_per_thread && (size_i + size_offset_in_head < size_per_head); size_i++) {\n        const int   shm_idx = shm_offset + size_i;\n        const float tmp     = static_cast<float>(shm_ptr[shm_idx]);\n        local_sum += tmp * tmp;\n    }\n\n    // reduction to get norm_factor\n#pragma unroll\n    for (int i = 1; i < threads_per_head; i <<= 1) {\n        local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n    }\n    if (thread_id_in_head == 0) {\n        const int norm_factor_idx    = head_offset_of_block + head_id_in_warp;\n        norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n\n// normalize and sts to gmem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int   norm_factor_idx = i / ldg_per_head;\n        const float norm_factor_val = norm_factor[norm_factor_idx];\n        T_IO        val             = normalize_shm[i];\n        T*          val_ptr         = (T*)(&val);\n#pragma unroll\n        for (int ei = 0; ei < ELEMENT_PER_LDG; ei++) {\n            val_ptr[ei] = T(static_cast<float>(val_ptr[ei]) * norm_factor_val);\n        }\n        const int input_idx = input_offset + i;\n        data[input_idx]     = val;\n    }\n}\n\n#define NORMALIZE_MACRO(HEADS_PER_WARP_, T_4, T_2, T)                                                                  \\\n    dim3      grid(total_head_count / (HEADS_PER_WARP_ * WARPS_PER_BLOCK));                                            \\\n    dim3      block(32 * WARPS_PER_BLOCK);                                                                             \\\n    const int shm_size = HEADS_PER_WARP_ * WARPS_PER_BLOCK * size_per_head * sizeof(T);                                \\\n    if (size_per_head % 4 == 0) {                                                                                      \\\n        normalize_kernel_v2<T_4, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 4><<<grid, block, shm_size, stream>>>(           \\\n            (T_4*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                               \\\n    }                                                                                                                  \\\n    else if (size_per_head % 2 == 0) {                                                                                 \\\n        normalize_kernel_v2<T_2, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 2><<<grid, block, shm_size, stream>>>(           \\\n            (T_2*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                               \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        normalize_kernel_v2<T, T, HEADS_PER_WARP_, WARPS_PER_BLOCK, 1><<<grid, block, shm_size, stream>>>(             \\\n            (T*)data, (const T*)logit_scales, batch, seqlen, num_head, size_per_head);                                 \\\n    }\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\ntemplate<typename T>\nvoid invokeNormalize(\n    T* data, const T* logit_scales, int batch, int seqlen, int num_head, int size_per_head, hipStream_t stream)\n{\n    const int WARPS_PER_BLOCK  = 4;\n    const int total_head_count = batch * num_head * seqlen;\n    if (std::is_same<T, float>::value) {\n        // WARPS_PER_BLOCK = 4, HEADS_PER_WARP = 4\n        if (total_head_count % (WARPS_PER_BLOCK * 4) == 0) {\n            NORMALIZE_MACRO(4, float4, float2, float);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 2\n        else if (total_head_count % (WARPS_PER_BLOCK * 2) == 0) {\n            NORMALIZE_MACRO(2, float4, float2, float);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 1\n        else if (total_head_count % WARPS_PER_BLOCK == 0) {\n            NORMALIZE_MACRO(1, float4, float2, float);\n        }\n        else {\n            dim3 grid(seqlen, num_head, batch);\n            dim3 block((size_per_head + 31) / 32 * 32);\n            normalize_kernel<<<grid, block, 0, stream>>>(\n                (float*)data, (const float*)logit_scales, batch, seqlen, num_head, size_per_head);\n        }\n    }\n    else if (std::is_same<T, half>::value) {\n        // WARPS_PER_BLOCK = 4, HEADS_PER_WARP = 4\n        if (total_head_count % (WARPS_PER_BLOCK * 4) == 0) {\n            NORMALIZE_MACRO(4, half4, half2, half);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 2\n        else if (total_head_count % (WARPS_PER_BLOCK * 2) == 0) {\n            NORMALIZE_MACRO(2, half4, half2, half);\n        }\n        // WARPS_PER_BLOCK = 4, HEAD_PER_WARPS = 1\n        else if (total_head_count % WARPS_PER_BLOCK == 0) {\n            NORMALIZE_MACRO(1, half4, half2, half);\n        }\n        else {\n            if (size_per_head % 2 == 0) {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head / 2 + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>(\n                    (half2*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head);\n            }\n            else {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>(\n                    (half*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head);\n            }\n        }\n    }\n#ifdef ENABLE_BF16\n    else {\n        dim3 grid(seqlen, num_head, batch);\n        dim3 block((size_per_head + 31) / 32 * 32);\n        normalize_kernel<<<grid, block, 0, stream>>>(data, logit_scales, batch, seqlen, num_head, size_per_head);\n    }\n#endif\n}\n\n#undef NORMALIZE_MACRO\n\ntemplate void invokeNormalize<half>(\n    half* data, const half* logit_scales, int batch, int seqlen, int num_head, int size_per_head, hipStream_t stream);\n\ntemplate void invokeNormalize<float>(float*       data,\n                                     const float* logit_scales,\n                                     int          batch,\n                                     int          seqlen,\n                                     int          num_head,\n                                     int          size_per_head,\n                                     hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeNormalize<__hip_bfloat16>(__hip_bfloat16*       data,\n                                             const __hip_bfloat16* logit_scales,\n                                             int                  batch,\n                                             int                  seqlen,\n                                             int                  num_head,\n                                             int                  size_per_head,\n                                             hipStream_t         stream);\n\n#endif\n\n/*******************  invokeNormalize  ***********************/\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(seqlen, num_head, batch)\n// block((size_per_head + 31)/31*32)\ntemplate<typename T>\n__global__ void normalize_kernel(int8_t*     data,\n                                 const T*    logit_scales,\n                                 int         batch,\n                                 int         seqlen,\n                                 int         num_head,\n                                 int         size_per_head,\n                                 const float deQ_scale,\n                                 const float Q_scale)\n{\n    const int        batch_id    = blockIdx.z;\n    const int        head_id     = blockIdx.y;\n    const int        seq_id      = blockIdx.x;\n    const int        size_id     = threadIdx.x;\n    const int        input_idx   = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head + size_id;\n    const float      logit_scale = (logit_scales != NULL) ? static_cast<float>(logit_scales[head_id]) : 1.0f;\n    const bool       flag        = size_id < size_per_head;\n    float            input_val   = (flag) ? static_cast<float>(data[input_idx]) * deQ_scale : 0.0f;\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val * input_val;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrt(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val *= norm_factor;\n    if (flag) {\n        data[input_idx] = float_to_int8_rn(input_val * Q_scale);\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\n// grid(seqlen, num_head, batch)\n// block((size_per_head/2 + 31)/31*32)\ntemplate<>\n__global__ void normalize_kernel(int8_t*     data,\n                                 const half* logit_scales,\n                                 int         batch,\n                                 int         seqlen,\n                                 int         num_head,\n                                 int         size_per_head,\n                                 const float deQ_scale,\n                                 const float Q_scale)\n{\n    const int   batch_id         = blockIdx.z;\n    const int   head_id          = blockIdx.y;\n    const int   seq_id           = blockIdx.x;\n    const int   size_id          = threadIdx.x;\n    const int   input_idx        = ((batch_id * num_head + head_id) * seqlen + seq_id) * size_per_head / 2 + size_id;\n    const float logit_scale      = (logit_scales != NULL) ? float(logit_scales[head_id]) : 1.0f;\n    float2      input_val_float2 = {0.0f, 0.0f};\n    const bool  flag             = size_id < size_per_head / 2;\n    char2*      dataPtr          = (char2*)data;\n    if (flag) {\n        char2 dataTmp      = dataPtr[input_idx];\n        input_val_float2.x = static_cast<float>(dataTmp.x) * deQ_scale;\n        input_val_float2.y = static_cast<float>(dataTmp.y) * deQ_scale;\n    }\n    __shared__ float norm_factor;\n    const float      local_sum     = input_val_float2.x * input_val_float2.x + input_val_float2.y * input_val_float2.y;\n    const float      local_sum_all = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        norm_factor = rsqrtf(local_sum_all + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n    input_val_float2.x *= norm_factor;\n    input_val_float2.y *= norm_factor;\n    if (flag) {\n        char2 dataTmp;\n        dataTmp.x          = float_to_int8_rn(input_val_float2.x * Q_scale);\n        dataTmp.y          = float_to_int8_rn(input_val_float2.y * Q_scale);\n        dataPtr[input_idx] = dataTmp;\n    }\n}\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale)\n// grid(batch*num_head*seqlen/(HEADS_PER_WARP*WARPS_PER_BLOCK))\n// block(32*WARPS_PER_BLOCK)\n// size_per_head % ELEMENT_PER_LDG == 0\ntemplate<int HEADS_PER_WARP, int WARPS_PER_BLOCK, int ELEMENT_PER_LDG>\n__global__ void normalize_kernel_v2(char4*      data,\n                                    const half* logit_scales,\n                                    int         batch,\n                                    int         seqlen,\n                                    int         num_head,\n                                    int         size_per_head,\n                                    const float deQ_scale,\n                                    const float Q_scale)\n{\n    const int   tidx                 = threadIdx.x;\n    const int   bidx                 = blockIdx.x;\n    const int   threads_per_head     = 32 / HEADS_PER_WARP;\n    const int   lane_id              = tidx % 32;\n    const int   warp_id              = tidx / 32;\n    const int   head_offset_of_grid  = bidx * WARPS_PER_BLOCK * HEADS_PER_WARP;\n    const int   head_offset_of_block = warp_id * HEADS_PER_WARP;\n    const int   head_id_in_warp      = lane_id / threads_per_head;\n    const int   head_id              = head_offset_of_grid + head_offset_of_block + head_id_in_warp;\n    const int   num_head_id          = (head_id / seqlen) % num_head;\n    const int   ldg_per_head         = size_per_head / ELEMENT_PER_LDG;\n    const float logit_scale          = (logit_scales != NULL) ? static_cast<float>(logit_scales[num_head_id]) : 1.0f;\n\n    char4*           normalize_shm = (char4*)normalize_kernel_v2_shm;\n    __shared__ float norm_factor[WARPS_PER_BLOCK * HEADS_PER_WARP];  // one factor for one head\n    const int        input_offset = head_offset_of_grid * ldg_per_head;\n    // load from gmem to smem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int input_idx = input_offset + i;\n        normalize_shm[i]    = data[input_idx];\n    }\n    __syncthreads();\n\n    // local sum\n    float         local_sum           = 0.0f;\n    const int     elements_per_thread = (size_per_head + threads_per_head - 1) / threads_per_head;\n    const int     thread_id_in_head   = tidx % threads_per_head;\n    const int     size_offset_in_head = elements_per_thread * thread_id_in_head;\n    const int     shm_offset          = (head_offset_of_block + head_id_in_warp) * size_per_head + size_offset_in_head;\n    const int8_t* shm_ptr             = (const int8_t*)normalize_shm;\n#pragma unroll\n    for (int size_i = 0; size_i < elements_per_thread && (size_i + size_offset_in_head < size_per_head); size_i++) {\n        const int   shm_idx = shm_offset + size_i;\n        const float tmp     = static_cast<float>(shm_ptr[shm_idx]) * deQ_scale;\n        local_sum += tmp * tmp;\n    }\n\n    // reduction to get norm_factor\n#pragma unroll\n    for (int i = 1; i < threads_per_head; i <<= 1) {\n        local_sum += __shfl_xor_sync(FINAL_MASK, local_sum, i, 32);\n    }\n    if (thread_id_in_head == 0) {\n        const int norm_factor_idx    = head_offset_of_block + head_id_in_warp;\n        norm_factor[norm_factor_idx] = rsqrtf(local_sum + 1e-6) * logit_scale;\n    }\n    __syncthreads();\n\n// normalize and sts to gmem\n#pragma unroll\n    for (int i = tidx; i < HEADS_PER_WARP * WARPS_PER_BLOCK * ldg_per_head; i += blockDim.x) {\n        const int   norm_factor_idx = i / ldg_per_head;\n        const float norm_factor_val = norm_factor[norm_factor_idx];\n        char4       val             = normalize_shm[i];\n        int8_t*     val_ptr         = (int8_t*)(&val);\n#pragma unroll\n        for (int ei = 0; ei < ELEMENT_PER_LDG; ei++) {\n            val_ptr[ei] = float_to_int8_rn(static_cast<float>(val_ptr[ei]) * deQ_scale * norm_factor_val * Q_scale);\n        }\n        const int input_idx = input_offset + i;\n        data[input_idx]     = val;\n    }\n}\n\n#define NORMALIZE_MACRO                                                                                                \\\n    const int shm_size = WARPS_PER_BLOCK * HEADS_PER_WARP * size_per_head;                                             \\\n    dim3      grid(total_head_count / (WARPS_PER_BLOCK * HEADS_PER_WARP));                                             \\\n    dim3      block(32 * WARPS_PER_BLOCK);                                                                             \\\n    normalize_kernel_v2<HEADS_PER_WARP, WARPS_PER_BLOCK, 4><<<grid, block, shm_size, stream>>>(                        \\\n        (char4*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, deQ_scale, Q_scale);\n\n// input should be [batch, num_head, seqlen, size_per_head]\n// do normlization on size_per_head && *(logit_scale/sqrt(size_per_head))\ntemplate<typename T>\nvoid invokeNormalize(int8_t*      data,\n                     const T*     logit_scales,\n                     int          batch,\n                     int          seqlen,\n                     int          num_head,\n                     int          size_per_head,\n                     hipStream_t stream,\n                     const float  deQ_scale,\n                     const float  Q_scale)\n{\n    if (std::is_same<T, half>::value) {\n        if (size_per_head % 4 == 0) {\n            const int HEADS_PER_WARP   = 4;\n            const int total_head_count = seqlen * num_head * batch;\n            if (total_head_count % (HEADS_PER_WARP * 4) == 0) {\n                const int WARPS_PER_BLOCK = 4;\n                NORMALIZE_MACRO\n            }\n            else if (total_head_count % (HEADS_PER_WARP * 2) == 0) {\n                const int WARPS_PER_BLOCK = 2;\n                NORMALIZE_MACRO\n            }\n            else if (total_head_count % (HEADS_PER_WARP * 1) == 0) {\n                const int WARPS_PER_BLOCK = 1;\n                NORMALIZE_MACRO\n            }\n            else {\n                dim3 grid(seqlen, num_head, batch);\n                dim3 block((size_per_head / 2 + 31) / 32 * 32);\n                normalize_kernel<<<grid, block, 0, stream>>>((int8_t*)data,\n                                                             (const half*)logit_scales,\n                                                             batch,\n                                                             seqlen,\n                                                             num_head,\n                                                             size_per_head,\n                                                             deQ_scale,\n                                                             Q_scale);\n            }\n        }\n        else if (size_per_head % 2 == 0) {\n            dim3 grid(seqlen, num_head, batch);\n            dim3 block((size_per_head / 2 + 31) / 32 * 32);\n            normalize_kernel<<<grid, block, 0, stream>>>(\n                (int8_t*)data, (const half*)logit_scales, batch, seqlen, num_head, size_per_head, deQ_scale, Q_scale);\n        }\n        else {\n            printf(\"[ERROR][invokeNormalize(INT8 version)] only supports size_per_head %% 2 == 0!\\n\");\n            exit(-1);\n        }\n    }\n    else {\n        printf(\"[ERROR][invokeNormalize(INT8 version)] only supports [T=half] !\\n\");\n        exit(-1);\n    }\n}\n\n#undef NORMALIZE_MACRO\n\ntemplate void invokeNormalize<half>(int8_t*      data,\n                                    const half*  logit_scales,\n                                    int          batch,\n                                    int          seqlen,\n                                    int          num_head,\n                                    int          size_per_head,\n                                    hipStream_t stream,\n                                    const float  deQ_scale,\n                                    const float  Q_scale);\n\ntemplate void invokeNormalize<float>(int8_t*      data,\n                                     const float* logit_scales,\n                                     int          batch,\n                                     int          seqlen,\n                                     int          num_head,\n                                     int          size_per_head,\n                                     hipStream_t stream,\n                                     const float  deQ_scale,\n                                     const float  Q_scale);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/normalize_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_fp8_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\nstruct Vec_t {\n};\ntemplate<>\nstruct Vec_t<float> {\n    using Type = float2;\n};\ntemplate<>\nstruct Vec_t<half> {\n    using Type = half2;\n};\n\ntemplate<>\nstruct Vec_t<__nv_fp8_e4m3> {\n    using Type = __nv_fp8x2_e4m3;\n};\n\ntemplate<>\nstruct Vec_t<__nv_bfloat16> {\n    using Type = nv_bfloat162;\n};\n\ntemplate<typename Vec1, typename Vec2>\n__device__ __inline__ Vec1 convert_vec(Vec2 in_vec)\n{\n    return Vec1(in_vec);\n}\n\ntemplate<>\n__device__ __inline__ float2 convert_vec(half2 in_vec)\n{\n    return float2{(float)in_vec.x, (float)in_vec.y};\n}\n\ntemplate<>\n__device__ __inline__ float2 convert_vec(__nv_bfloat162 in_vec)\n{\n    return float2{(float)in_vec.x, (float)in_vec.y};\n}\n\ntemplate<>\n__device__ __inline__ __nv_bfloat162 convert_vec(half2 in_vec)\n{\n    __nv_bfloat162 out = cuda_cast<__nv_bfloat162, half2>(in_vec);\n    return out;\n}\n\n#ifdef ENABLE_FP8\ntemplate<>\n__device__ __inline__ float2 convert_vec(__nv_fp8x2_e4m3 in_vec)\n{\n    return (float2)in_vec;\n}\n\ntemplate<>\n__device__ __inline__ __nv_bfloat162 convert_vec(__nv_fp8x2_e4m3 in_vec)\n{\n    return fp8x2_e4m3_to_bfloat2(&in_vec);\n}\n#endif\n\n// This optimization has bug\n// #define OPT_TRANSPOSE\n\n#ifdef OPT_TRANSPOSE\ntemplate<typename T1, typename T2, int SEQ_GROUP_SIZE, int SIZE_PER_HEAD>\n__global__ void FP8AddFusedQKVBiasRebuildPaddingKernel(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    using T1_4 = __nv_fp8x4_e4m3;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    __shared__ T2 src_v[SEQ_GROUP_SIZE * (SIZE_PER_HEAD + 2)];\n    T2_2*         src_v_2 = (T2_2*)(src_v);\n\n    const int batch_idx = blockIdx.x;\n    const int seq_idx   = blockIdx.y * SEQ_GROUP_SIZE + threadIdx.y;\n    const int head_idx  = blockIdx.z;\n    const int tidx      = threadIdx.x;\n    int       Dh_div_4  = param.size_per_head / 4;\n    bool      is_valid_seq_idx =\n        param.padding_offset_prefix_sum == nullptr ?\n                 seq_idx < param.seq_len :\n                 seq_idx < (param.padding_offset_prefix_sum[batch_idx + 1] - param.padding_offset_prefix_sum[batch_idx]);\n\n    if (tidx < Dh_div_4 && is_valid_seq_idx) {\n\n        const int sentence_idx =\n            (param.padding_offset_prefix_sum == nullptr ? param.seq_len * batch_idx :\n                                                          param.padding_offset_prefix_sum[batch_idx])\n            + seq_idx;\n\n        int Dh_div_2 = param.size_per_head / 2;\n        int n_div_4  = param.head_num * Dh_div_4;\n        int n_div_2  = param.head_num * Dh_div_2;\n\n        const int hidden_idx = head_idx * Dh_div_4 + tidx;\n\n        const int q_idx = sentence_idx * 3 * n_div_4 + hidden_idx;\n        const int k_idx = sentence_idx * 3 * n_div_4 + hidden_idx + n_div_4;\n        const int v_idx = sentence_idx * 3 * n_div_4 + hidden_idx + 2 * n_div_4;\n\n        T1_4* qkv_ptr = (T1_4*)(param.QKV_T1);\n        T2_2  q[2];\n        T2_2  k[2];\n        T2_2  v[2];\n\n        fp8x4_e4m3_to_bfloat2(&q[0], &q[1], &qkv_ptr[q_idx]);\n        fp8x4_e4m3_to_bfloat2(&k[0], &k[1], &qkv_ptr[k_idx]);\n        fp8x4_e4m3_to_bfloat2(&v[0], &v[1], &qkv_ptr[v_idx]);\n        T2_2 input_scale2 = cuda_cast<T2_2>(param.input_scale == nullptr ? 1.0f : __ldg(param.input_scale));\n\n        if (param.input_scale_2 != nullptr) {\n            // q.x = q.x * input_scale * __ldg(param.input_scale_2 + hidden_idx)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // q.y = q.y * input_scale * __ldg(param.input_scale_2 + hidden_idx)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // k.x = k.x * input_scale * __ldg(param.input_scale_2 + hidden_idx + n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // k.y = k.y * input_scale * __ldg(param.input_scale_2 + hidden_idx + n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // v.x = v.x * input_scale * __ldg(param.input_scale_2 + hidden_idx + 2 * n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // v.y = v.y * input_scale * __ldg(param.input_scale_2 + hidden_idx + 2 * n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n        }\n        else {\n            q[0] = hmul2(q[0], input_scale2);\n            q[1] = hmul2(q[1], input_scale2);\n            k[0] = hmul2(k[0], input_scale2);\n            k[1] = hmul2(k[1], input_scale2);\n            v[0] = hmul2(v[0], input_scale2);\n            v[1] = hmul2(v[1], input_scale2);\n        }\n\n        T2_2* bias_ptr = (T2_2*)(param.qkv_bias);\n\n        q[0] = mmha::add(q[0], bias_ptr[head_idx * Dh_div_2 + 2 * tidx]);\n        q[1] = mmha::add(q[1], bias_ptr[head_idx * Dh_div_2 + 2 * tidx + 1]);\n        k[0] = mmha::add(k[0], bias_ptr[head_idx * Dh_div_2 + 1 * n_div_2 + 2 * tidx]);\n        k[1] = mmha::add(k[1], bias_ptr[head_idx * Dh_div_2 + 1 * n_div_2 + 2 * tidx + 1]);\n        v[0] = mmha::add(v[0], bias_ptr[head_idx * Dh_div_2 + 2 * n_div_2 + 2 * tidx]);\n        v[1] = mmha::add(v[1], bias_ptr[head_idx * Dh_div_2 + 2 * n_div_2 + 2 * tidx + 1]);\n\n        mmha::apply_rotary_embedding(q[0], k[0], 2 * tidx, param.rotary_embedding_dim, seq_idx);\n        mmha::apply_rotary_embedding(q[1], k[1], 2 * tidx + 1, param.rotary_embedding_dim, seq_idx);\n\n        T2_2 output_scale2 = cuda_cast<T2_2>(param.output_scale == nullptr ? 1.0f : __ldg(param.output_scale));\n\n        q[0] = hmul2(q[0], output_scale2);\n        q[1] = hmul2(q[1], output_scale2);\n        k[0] = hmul2(k[0], output_scale2);\n        k[1] = hmul2(k[1], output_scale2);\n        v[0] = hmul2(v[0], output_scale2);\n        v[1] = hmul2(v[1], output_scale2);\n\n        T1_4* q_out_ptr = (T1_4*)(param.q_buf);\n        T1_4* k_out_ptr = (T1_4*)(param.k_buf);\n\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 0] = v[0];\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 1] = v[1];\n\n        // q_buf, k_buf: [batch, head_num, seq_len_paaded, size_per_head]\n        const int dest_idx = n_div_4 * param.seq_len_padded * batch_idx + Dh_div_4 * param.seq_len_padded * head_idx\n                             + Dh_div_4 * seq_idx + tidx;\n\n        q_out_ptr[dest_idx] = T1_4(q[0], q[1]);\n        k_out_ptr[dest_idx] = T1_4(k[0], k[1]);\n    }\n    else {\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 0] = cuda_cast<T2_2>(0.0f);\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 1] = cuda_cast<T2_2>(0.0f);\n    }\n\n    __syncthreads();\n\n    const int seq_group_id = seq_idx / SEQ_GROUP_SIZE;\n    // v_buf: [batch, head_num, size_per_head, seq_len_paaded]\n    T1_4* v_out_ptr = (T1_4*)(param.v_buf);\n    for (int new_id = threadIdx.x * blockDim.y + threadIdx.y; new_id < SIZE_PER_HEAD * (SEQ_GROUP_SIZE / 4);\n         new_id += blockDim.x * blockDim.y) {\n        int new_size_id = new_id / (SEQ_GROUP_SIZE / 4);\n        int new_seq_id  = new_id % (SEQ_GROUP_SIZE / 4);\n\n        T2_2 val_1;\n        T2_2 val_2;\n        val_1.x = src_v[(4 * new_seq_id + 0) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_1.y = src_v[(4 * new_seq_id + 1) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_2.x = src_v[(4 * new_seq_id + 2) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_2.y = src_v[(4 * new_seq_id + 3) * (SIZE_PER_HEAD + 2) + new_size_id];\n\n        v_out_ptr[param.head_num * param.size_per_head * (param.seq_len_padded / 4) * batch_idx\n                  + param.size_per_head * (param.seq_len_padded / 4) * head_idx\n                  + new_size_id * (param.seq_len_padded / 4) + seq_group_id * (SEQ_GROUP_SIZE / 4) + new_seq_id] =\n            T1_4(val_1, val_2);\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddFusedQKVBiasRebuildPadding(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    // To implement rotary embeddings, each thread processes two QKV elems:\n    const int seq_group_size = 64;\n    dim3      block((param.size_per_head / 4 + 1) / 2 * 2, seq_group_size);\n    dim3      grid(param.batch_size, (param.seq_len + seq_group_size - 1) / seq_group_size, param.head_num);\n\n    FT_CHECK(block.x * block.y <= 1024);\n    if (param.size_per_head == 64) {\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, seq_group_size, 64><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        FT_CHECK(false);\n    }\n}\n\n// template void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, __nv_fp8_e4m3>(\n//     FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\n// template void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, half>(\n//     FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, half> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, __nv_bfloat16>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n#else\ntemplate<typename T1, typename T2, bool INPUT_T1>\n__global__ void FP8AddFusedQKVBiasRebuildPaddingKernel(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    using VecFP8_t                   = typename Vec_t<T1>::Type;\n    using VecBias_t                  = typename Vec_t<T2>::Type;\n    using PACKED_BF16                = __nv_bfloat162_2;\n    using PACKED_FP8                 = __nv_fp8x2_x2_e4m3;\n    using ARRAY_FP8                  = __nv_fp8_4_e4m3;\n    using ARRAY_BF16                 = __nv_bfloat164;\n    using VecBF16_t                  = typename Vec_t<__nv_bfloat16>::Type;\n    constexpr int ELEMENT_PER_THREAD = 4;\n    constexpr int NUM_PACKS          = 2;\n    // const int sentence_idx = blockIdx.z;\n    // const int padded_row_id = sentence_idx + (param.padding_offset == nullptr ? 0 :\n    // param.padding_offset[sentence_idx]);\n    const int  batch_idx         = blockIdx.z / param.head_num;\n    const int  head_idx          = blockIdx.z % param.head_num;\n    const int  seq_idx           = threadIdx.y + blockIdx.y * blockDim.y;\n    const int  sentence_idx      = batch_idx * param.seq_len + seq_idx;\n    const int  size_per_head_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    const bool V_CACHE_STORE     = param.v_cache != nullptr;\n    const int  v_dest_idx_0      = param.size_per_head * param.seq_len_padded * param.head_num * batch_idx\n                             + param.size_per_head * param.seq_len_padded * head_idx;\n    __shared__ T1 sdata[32 * 33 * ELEMENT_PER_THREAD];\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        sdata[blockIdx.y * 33 * ELEMENT_PER_THREAD + blockIdx.x * ELEMENT_PER_THREAD + i] = (T1)0.0f;\n    }\n\n    if (size_per_head_idx * ELEMENT_PER_THREAD < param.size_per_head && seq_idx < param.seq_len_padded) {\n        const int hidden_idx = head_idx * param.size_per_head + size_per_head_idx * ELEMENT_PER_THREAD;\n        const int n          = param.head_num * param.size_per_head;\n\n        // src QKV: [token_num, 3, head, hidden]\n        const int q_idx = sentence_idx * 3 * n + hidden_idx;\n        const int k_idx = sentence_idx * 3 * n + hidden_idx + n;\n        const int v_idx = sentence_idx * 3 * n + hidden_idx + 2 * n;\n\n        PACKED_BF16 q, k, v;\n\n        if (INPUT_T1) {\n            PACKED_FP8 q_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[q_idx]);\n            PACKED_FP8 k_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[k_idx]);\n            PACKED_FP8 v_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[v_idx]);\n#pragma unroll\n            for (int i = 0; i < NUM_PACKS; i++) {\n                q.array[i] = convert_vec<VecBF16_t, VecFP8_t>(q_input.array[i]);\n                k.array[i] = convert_vec<VecBF16_t, VecFP8_t>(k_input.array[i]);\n                v.array[i] = convert_vec<VecBF16_t, VecFP8_t>(v_input.array[i]);\n            }\n        }\n        else {\n            q = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[q_idx]);\n            k = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[k_idx]);\n            v = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[v_idx]);\n        }\n\n        __nv_bfloat162 input_scale2 =\n            __float2bfloat162_rn(param.input_scale == nullptr ? 1.0f : __ldg(param.input_scale));\n\n        PACKED_BF16 q_bias, k_bias, v_bias;\n        q_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx]);\n        k_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx + n]);\n        v_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx + 2 * n]);\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q.array[i] = mmha::add(hmul2(q.array[i], input_scale2), q_bias.array[i]);\n            k.array[i] = mmha::add(hmul2(k.array[i], input_scale2), k_bias.array[i]);\n            v.array[i] = mmha::add(hmul2(v.array[i], input_scale2), v_bias.array[i]);\n        }\n\n        // mmha::apply_rotary_embedding(q, k, tidx, param.rotary_embedding_dim, seq_idx);\n        const int v_cache_idx = param.size_per_head * param.max_seq_len * param.head_num * batch_idx\n                                + param.size_per_head * param.max_seq_len * head_idx + seq_idx * param.size_per_head\n                                + size_per_head_idx * ELEMENT_PER_THREAD;\n#ifndef FP8_MHA\n        if (V_CACHE_STORE && seq_idx < param.seq_len) {\n            *reinterpret_cast<PACKED_BF16*>(&param.v_cache[v_cache_idx]) = v;\n        }\n#endif\n\n        __nv_bfloat162 output_scale2 =\n            __float2bfloat162_rn(param.output_scale == nullptr ? 1.0f : __ldg(param.output_scale));\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q.array[i] = hmul2(q.array[i], output_scale2);\n            k.array[i] = hmul2(k.array[i], output_scale2);\n            v.array[i] = hmul2(v.array[i], output_scale2);\n#ifdef FP8_MHA\n            if (V_CACHE_STORE && seq_idx < param.seq_len) {\n                reinterpret_cast<VecFP8_t*>(&param.v_cache[v_cache_idx])[i] =\n                    convert_vec<VecFP8_t, VecBF16_t>(v.array[i]);\n            }\n#endif\n        }\n\n        // q_buf, k_buf: [batch, head_num, seq_len_paaded, size_per_head]\n        const int dest_idx = param.size_per_head * param.seq_len_padded * param.head_num * batch_idx\n                             + param.size_per_head * param.seq_len_padded * head_idx + param.size_per_head * seq_idx\n                             + size_per_head_idx * ELEMENT_PER_THREAD;\n\n        PACKED_FP8 q_output, k_output;\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q_output.array[i] = convert_vec<VecFP8_t, VecBF16_t>(q.array[i]);\n            k_output.array[i] = convert_vec<VecFP8_t, VecBF16_t>(k.array[i]);\n        }\n        *reinterpret_cast<PACKED_FP8*>(&param.q_buf[dest_idx]) = q_output;\n        *reinterpret_cast<PACKED_FP8*>(&param.k_buf[dest_idx]) = k_output;\n\n        ARRAY_BF16 v_val = *reinterpret_cast<ARRAY_BF16*>(&v);\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n            sdata[threadIdx.y * 33 * ELEMENT_PER_THREAD + (threadIdx.x * ELEMENT_PER_THREAD + i)] = (T1)v_val.array[i];\n        }\n    }\n\n    __syncthreads();\n\n    const int trans_size_per_head_idx = threadIdx.y + blockIdx.x * blockDim.x;\n    const int trans_seq_idx           = threadIdx.x + blockIdx.y * blockDim.y;\n    if (trans_size_per_head_idx * ELEMENT_PER_THREAD < param.size_per_head && trans_seq_idx < param.seq_len_padded) {\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n            param.v_buf[v_dest_idx_0 + (trans_size_per_head_idx * ELEMENT_PER_THREAD + i) * param.seq_len_padded\n                        + trans_seq_idx] =\n                sdata[threadIdx.x * 33 * ELEMENT_PER_THREAD + threadIdx.y * ELEMENT_PER_THREAD + i];\n        }\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddFusedQKVBiasRebuildPadding(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    // To implement rotary embeddings, each thread processes two QKV elems:\n    // dim3 block((param.size_per_head / 2 + 31) / 32 * 32);\n    // dim3 grid(param.head_num, param.token_num);\n    const int pack_size = 4;\n    FT_CHECK(param.size_per_head % pack_size == 0);\n    dim3 block(32, 32);\n    dim3 grid((param.size_per_head / pack_size + 31) / 32,\n              (param.seq_len_padded + 31) / 32,\n              param.head_num * param.batch_size);\n    // FT_CHECK(block.x * block.y <= 1024);\n    // shared memory configuration\n    const int carveout = 50;  // prefer shared memory capacity 50% of maximum\n    if (param.QKV_T1 == nullptr) {\n        assert(param.QKV_T2 != nullptr);\n        cudaFuncSetAttribute((const void*)FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, false>,\n                             cudaFuncAttributePreferredSharedMemoryCarveout,\n                             carveout);\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, false><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        cudaFuncSetAttribute((const void*)FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, true>,\n                             cudaFuncAttributePreferredSharedMemoryCarveout,\n                             carveout);\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, true><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, __nv_fp8_e4m3>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, half>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, half> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, __nv_bfloat16>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\n#endif\n\ntemplate<typename T1, typename T2>\n__global__ void FP8TrtAddQKVBiasKernel(FP8TrtAddQKVBiasParam<T1, T2> param)\n{\n    // Add bias ([3, head, size]), and then transpose from\n    // [valid_word_num, 3, head, size] -> [valid_word_num, head, 3, size]\n\n    const T1* qkv_src_ptr = param.qkv_src + blockIdx.x * 3 * param.hidden_unit;\n    const T2* bias_ptr    = param.qkv_bias;\n    T1*       qkv_tgt_ptr = param.qkv_tgt + blockIdx.x * 3 * param.hidden_unit;\n\n    qkv_tgt_ptr[threadIdx.x * 3 * param.size_per_head + blockIdx.y * param.size_per_head + threadIdx.y] =\n        (T1)(((float)qkv_src_ptr[blockIdx.y * param.hidden_unit + threadIdx.x * param.size_per_head + threadIdx.y]\n                  * __ldg(param.input_scale)\n              + (float)bias_ptr[blockIdx.y * param.hidden_unit + threadIdx.x * param.size_per_head + threadIdx.y])\n             * __ldg(param.output_scale));\n}\n\ntemplate<>\n__global__ void FP8TrtAddQKVBiasKernel(FP8TrtAddQKVBiasParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n{\n    // Add bias ([3, head, size]), and then transpose from\n    // [valid_word_num, 3, head, size] -> [valid_word_num, head, 3, size]\n\n    using T1_4 = __nv_fp8x4_e4m3;\n    using T2_2 = typename TypeConverter<__nv_bfloat16>::Type;\n\n    const T1_4* qkv_src_ptr = (T1_4*)(param.qkv_src + blockIdx.x * 3 * param.hidden_unit);\n    const T2_2* bias_ptr    = (T2_2*)param.qkv_bias;\n    T1_4*       qkv_tgt_ptr = (T1_4*)(param.qkv_tgt + blockIdx.x * 3 * param.hidden_unit);\n\n    const int size_div_4   = param.size_per_head / 4;\n    const int hidden_div_4 = param.hidden_unit / 4;\n    const int src_id       = threadIdx.z * hidden_div_4 + threadIdx.y * size_div_4 + threadIdx.x;\n\n    T2_2 val1, val2;\n    fp8x4_e4m3_to_bfloat2(&val1, &val2, &qkv_src_ptr[src_id]);\n    T2_2      input_scale_2  = cuda_cast<T2_2, float>(__ldg(param.input_scale));\n    T2_2      output_scale_2 = cuda_cast<T2_2, float>(__ldg(param.output_scale));\n    const int bias_id_0      = src_id * 2;\n    val1                     = hmul2(hadd2(hmul2(val1, input_scale_2), bias_ptr[bias_id_0]), output_scale_2);\n    val2                     = hmul2(hadd2(hmul2(val2, input_scale_2), bias_ptr[bias_id_0 + 1]), output_scale_2);\n\n    qkv_tgt_ptr[(threadIdx.y * 3 * size_div_4 + threadIdx.z * size_div_4) + threadIdx.x] = __nv_fp8x4_e4m3(val1, val2);\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8TrtAddQKVBias(FP8TrtAddQKVBiasParam<T1, T2> param)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (std::is_same<T1, __nv_fp8_e4m3>::value && std::is_same<T2, __nv_bfloat16>::value) {\n        dim3 grid(param.valid_word_num);\n        dim3 block(param.size_per_head / 4, param.head_num, 3);\n        FP8TrtAddQKVBiasKernel<T1, T2><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        dim3 grid(param.valid_word_num, 3);\n        dim3 block(param.head_num, param.size_per_head);\n\n        FP8TrtAddQKVBiasKernel<T1, T2><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\n#ifdef ENABLE_FP8\ntemplate void\ninvokeFP8TrtAddQKVBias<__nv_fp8_e4m3, __nv_bfloat16>(FP8TrtAddQKVBiasParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n#endif\n\ntemplate<typename T1, typename T2>\n__global__ void transpose_4d_batch_major_k_cache(T2*          k_dst,\n                                                 const T1*    k_src,\n                                                 const float* scale,\n                                                 const int    head_num,\n                                                 const int    size_per_head,\n                                                 const int    seq_len,\n                                                 const int    max_seq_len,\n                                                 const int    seq_len_padded)\n{\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = 16;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * seq_len_padded\n                                                  + head_id * size_per_head * seq_len_padded);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        key_dst[out_idx] = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n    }\n}\n\ntemplate<>\n__global__ void transpose_4d_batch_major_k_cache(__nv_bfloat16*       k_dst,\n                                                 const __nv_fp8_e4m3* k_src,\n                                                 const float*         scale,\n                                                 const int            head_num,\n                                                 const int            size_per_head,\n                                                 const int            seq_len,\n                                                 const int            max_seq_len,\n                                                 const int            seq_len_padded)\n{\n    const int     batch_id  = blockIdx.y;\n    const int     head_id   = blockIdx.z;\n    constexpr int X_ELEMS   = 8;\n    const float   scale_val = scale[0];\n    using fp8_8             = __nv_fp8_8_e4m3;\n    using bf16_8            = __nv_bfloat168;\n\n    auto key_src = reinterpret_cast<const fp8_8*>(k_src + batch_id * head_num * size_per_head * seq_len_padded\n                                                  + head_id * size_per_head * seq_len_padded);\n    auto key_dst = reinterpret_cast<bf16_8*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                             + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        fp8_8  src_val = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n        bf16_8 dst_val;\n#pragma unroll\n        for (int i = 0; i < X_ELEMS; i++) {\n            dst_val.array[i] = __float2bfloat16_rn((float)src_val.array[i] * scale_val);\n        }\n        key_dst[out_idx] = dst_val;\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void transpose_4d_batch_major_v_cache(T2*          v_dst,\n                                                 const T1*    v_src,\n                                                 const float* scale,\n                                                 const int    head_num,\n                                                 const int    size_per_head,\n                                                 const int    seq_len,\n                                                 const int    max_seq_len,\n                                                 const int    seq_len_padded)\n{\n    const int      batch_id    = blockIdx.y;\n    const int      head_id     = blockIdx.z;\n    constexpr bool BF16_OUTPUT = std::is_same<T2, __nv_bfloat16>::value;\n    float          scale_val   = BF16_OUTPUT ? scale[0] : 1.0f;\n\n    // 16 byte loads will handle \"x\" dimension\n    // NOTE: need transpose, so cannot take x dimension\n    auto val_src = reinterpret_cast<const T1*>(v_src + batch_id * head_num * size_per_head * seq_len_padded\n                                               + head_id * size_per_head * seq_len_padded);\n    auto val_dst = reinterpret_cast<T2*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                         + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx >= size_per_head * seq_len)\n        return;\n\n    int seq_idx       = idx / size_per_head;\n    int head_size_idx = idx % size_per_head;\n    val_dst[idx]      = (T2)((float)val_src[head_size_idx * seq_len_padded + seq_idx] * scale_val);\n}\n\n// template<typename T1, typename T2>\n// __global__ void transpose_4d_batch_major_v_cache(T2* v_dst,\n//                                                  const T1* v_src,\n//                                                  const float* scale,\n//                                                  const int head_num,\n//                                                  const int size_per_head,\n//                                                  const int seq_len,\n//                                                  const int max_seq_len,\n//                                                  const int seq_len_padded)\n// {\n//     const int batch_id = blockIdx.z / head_num;\n//     const int head_id = blockIdx.z % head_num;\n//     constexpr bool BF16_OUTPUT = std::is_same<T2, __nv_bfloat16>::value;\n//     float scale_val = BF16_OUTPUT ? scale[0] : 1.0f;\n//     __shared__ T2 s_transpose_buffer[32][33];\n\n//     // 16 byte loads will handle \"x\" dimension\n//     // NOTE: need transpose, so cannot take x dimension\n//     auto val_src = reinterpret_cast<const T1*>(v_src + batch_id * head_num * size_per_head * seq_len_padded\n//                                                   + head_id * size_per_head * seq_len_padded);\n//     auto val_dst = reinterpret_cast<T2*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n//                                             + head_id * size_per_head * max_seq_len);\n\n//     // idx is over output dimension L * size_per_head / x for values\n//     for (int y_block_id = 0; y_block_id * blockDim.x < size_per_head; y_block_id ++) {\n//         for (int x_block_id = 0; x_block_id * blockDim.x < seq_len; x_block_id ++) {\n//             int seq_block_id = threadIdx.x + blockDim.x * x_block_id;\n//             int head_size_id = threadIdx.y + blockDim.y * y_block_id;\n//             if (seq_block_id < seq_len && head_size_id < size_per_head) {\n//                 s_transpose_buffer[threadIdx.y][threadIdx.x] = (T2) ((float) val_src[head_size_id * seq_len_padded +\n//                 seq_block_id] * scale_val);\n//             }\n//             __syncthreads();\n//             int transposed_head_size_id = threadIdx.x + blockDim.y * y_block_id;\n//             int transposed_seq_block_id = threadIdx.y + blockDim.x * x_block_id;\n//             if (transposed_head_size_id < size_per_head && transposed_seq_block_id < seq_len) {\n//                 val_dst[transposed_seq_block_id * size_per_head + transposed_head_size_id] =\n//                 s_transpose_buffer[threadIdx.x][threadIdx.y];\n//             }\n//         }\n//     }\n// }\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8Transpose4dBatchMajor(FP8Transpose4dBatchMajorParam<T1, T2> param)\n{\n    constexpr int block_sz = 128;\n    constexpr int x        = std::is_same<T2, __nv_bfloat16>::value ? 8 : 16;\n    int           size     = param.max_seq_len * param.size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, param.local_batch_size, param.local_head_num);\n    dim3          grid_v(\n        (param.seq_len * param.size_per_head + block_sz - 1) / block_sz, param.local_batch_size, param.local_head_num);\n\n    transpose_4d_batch_major_k_cache<<<grid, block_sz, 0, param.stream>>>(param.k_dst,\n                                                                          param.k_src,\n                                                                          param.scale,\n                                                                          param.local_head_num,\n                                                                          param.size_per_head,\n                                                                          param.seq_len,\n                                                                          param.max_seq_len,\n                                                                          param.seq_len_padded);\n\n    // transpose_4d_batch_major_v_cache<<<grid_v, block_sz, 0, param.stream>>>(param.v_dst,\n    //                                                                         param.v_src,\n    //                                                                         param.scale,\n    //                                                                         param.local_head_num,\n    //                                                                         param.size_per_head,\n    //                                                                         param.seq_len,\n    //                                                                         param.max_seq_len,\n    //                                                                         param.seq_len_padded);\n}\n\ntemplate void invokeFP8Transpose4dBatchMajor<__nv_fp8_e4m3, __nv_fp8_e4m3>(\n    FP8Transpose4dBatchMajorParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\ntemplate void invokeFP8Transpose4dBatchMajor<__nv_fp8_e4m3, __nv_bfloat16>(\n    FP8Transpose4dBatchMajorParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<int ITEMS_PER_THREAD, typename T, typename T_IN>\n__global__ void softmax_kernel(T* qk_buf_,\n                               const T_IN* __restrict__ qk_buf_src,\n                               const T* __restrict__ attr_mask,\n                               const int    batch_size,\n                               const int    head_num,\n                               const int    seq_len,\n                               const float  scalar,\n                               const float* input_scale,\n                               const float* output_scale)\n{\n    float input_scale_val  = input_scale == nullptr ? 1.0f : __ldg(input_scale);\n    float output_scale_val = output_scale == nullptr ? 1.0f : __ldg(output_scale);\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        float            data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n\n            float qk       = static_cast<float>(qk_buf_src[qk_offset]) * input_scale_val;\n            float mask_val = static_cast<float>(attr_mask[mask_offset]);\n\n            mask_val = (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk * static_cast<float>(scalar) + mask_val;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            qk_buf_[qk_offset] = (T)(data[i] * s_mean * output_scale_val);\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, typename T_COMPUTE, int ITEMS_PER_THREAD, int NUM>\n__global__ void softmax_kernel_v5_half2(T_OUT*       qk_buf,\n                                        const T_IN*  qk_buf_src,\n                                        const T_OUT* attr_mask,\n                                        const int*   padding_offset_prefix_sum,\n                                        const int    batch_size,\n                                        const int    head_num,\n                                        const int    seq_len,\n                                        const float  scalar,\n                                        const float* input_scale,\n                                        const float* output_scale)\n{\n    using T2_OUT     = typename Vec_t<T_OUT>::Type;\n    using T2_IN      = typename Vec_t<T_IN>::Type;\n    using T2_COMPUTE = typename Vec_t<T_COMPUTE>::Type;\n\n    T2_OUT*       qk_buf_out2     = (T2_OUT*)qk_buf;\n    const T2_OUT* attr_mask_half2 = (const T2_OUT*)attr_mask;\n    T2_IN*        qk_buf_in2      = (T2_IN*)qk_buf_src;\n\n    T2_COMPUTE input_scale_2  = cuda_cast<T2_COMPUTE, float>(input_scale == nullptr ? 1.0f : __ldg(input_scale));\n    T2_COMPUTE output_scale_2 = cuda_cast<T2_COMPUTE, float>(output_scale == nullptr ? 1.0f : __ldg(output_scale));\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x * NUM) {\n        if ((padding_offset_prefix_sum != nullptr)\n            && (seq_id >= padding_offset_prefix_sum[blockIdx.y + 1] - padding_offset_prefix_sum[blockIdx.y])) {\n\n            T2_COMPUTE zero_val;\n            zero_val.x       = 0.0f;\n            zero_val.y       = 0.0f;\n            const int offset = (blockIdx.y * head_num * seq_len + blockIdx.z * seq_len + seq_id) * seq_len / 2;\n            for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2); i++) {\n                qk_buf_out2[offset + i] = T2_OUT(zero_val);\n            }\n            continue;\n        }\n\n        T2_COMPUTE data[NUM][ITEMS_PER_THREAD];\n\n        int qk_offset[NUM];\n\n        __shared__ float s_sum[NUM], s_max[NUM];\n        float            local_max[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n            T2_COMPUTE mask_val[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                const int mask_offset =\n                    (blockIdx.y * seq_len + seq_id + j * gridDim.x) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n                mask_val[j] = cuda_cast<T2_COMPUTE>(attr_mask_half2[mask_offset]);\n            }\n\n            T2_COMPUTE qk[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk[j] = hmul2<T2_COMPUTE>(cuda_cast<T2_COMPUTE>(qk_buf_in2[qk_offset[j]]), input_scale_2);\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = hmul2<T2_COMPUTE>(hsub2<T2_COMPUTE>(cuda_cast<T2_COMPUTE>(1.0f), mask_val[j]),\n                                                cuda_cast<T2_COMPUTE>(-10000.0f));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i]   = hadd2<T2_COMPUTE>(hmul2<T2_COMPUTE>(qk[j], cuda_cast<T2_COMPUTE>(scalar)), mask_val[j]);\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, NUM>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, NUM>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i] = hexp2<T2_COMPUTE>(hsub2<T2_COMPUTE>(data[j][i], cuda_cast<T2_COMPUTE>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, NUM>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, NUM>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_buf_out2[qk_offset[j]] = T2_OUT(\n                    hmul2<T2_COMPUTE>(hmul2<T2_COMPUTE>(data[j][i], cuda_cast<T2_COMPUTE>(s_sum[j])), output_scale_2));\n            }\n        }\n    }\n}\n\n#define SOFTMAX_KERNEL_HALF2(ITEMS_PER_THREAD)                                                                         \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    grid.x /= 4;                                                                                                       \\\n    softmax_kernel_v5_half2<T, T_IN, __nv_bfloat16, ITEMS_PER_THREAD, 4>                                               \\\n        <<<grid, block, 0, param.stream>>>((T*)param.buffer,                                                           \\\n                                           (T_IN*)param.buffer_src,                                                    \\\n                                           (const T*)param.attr_mask,                                                  \\\n                                           param.padding_offset_prefix_sum,                                            \\\n                                           param.batch_size,                                                           \\\n                                           param.head_num,                                                             \\\n                                           param.seq_len,                                                              \\\n                                           param.scalar,                                                               \\\n                                           param.input_scale,                                                          \\\n                                           param.output_scale);\n\ntemplate<typename T, typename T_IN>\nvoid invokeFP8MaskedSoftMax(FP8MaskedSoftMaxParam<T, T_IN> param)\n{\n    dim3 grid(param.seq_len, param.batch_size, param.head_num);\n    if (param.batch_size * param.head_num > 360) {\n        grid.x = ceil(float(param.seq_len) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 1 && sizeof(T_IN) == 2 && param.seq_len % 2 == 0;\n\n    if (is_half2 && grid.x % 4 == 0) {\n        dim3 block((param.seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n        if (block.x > 2048 && block.x <= 4096) {\n            SOFTMAX_KERNEL_HALF2(4)\n        }\n        else if (block.x > 1024) {\n            SOFTMAX_KERNEL_HALF2(2)\n        }\n        else if (block.x > 0) {\n            SOFTMAX_KERNEL_HALF2(1)\n        }\n        else {\n            FT_CHECK(param.seq_len <= 4096);\n        }\n    }\n    else {\n        dim3 block((param.seq_len + 31) / 32 * 32);\n        if (block.x > 2048 && block.x <= 4096) {\n            softmax_kernel<4, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else if (block.x > 1024) {\n            softmax_kernel<2, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else if (block.x > 0) {\n            softmax_kernel<1, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else {\n            FT_CHECK(param.seq_len <= 4096);\n        }\n    }\n}\n\ntemplate void\ninvokeFP8MaskedSoftMax<__nv_fp8_e4m3, __nv_fp8_e4m3>(FP8MaskedSoftMaxParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\n\ntemplate void invokeFP8MaskedSoftMax<__nv_fp8_e4m3, float>(FP8MaskedSoftMaxParam<__nv_fp8_e4m3, float> param);\n\ntemplate void\ninvokeFP8MaskedSoftMax<__nv_fp8_e4m3, __nv_bfloat16>(FP8MaskedSoftMaxParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T>\nstruct Pack4_type {\n    using type = __nv_bfloat164;\n};\n\ntemplate<>\nstruct Pack4_type<__nv_fp8_e4m3> {\n    using type = __nv_fp8_4_e4m3;\n};\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void FP8TransposeAttentionOutRemovePadding(T_OUT*       dst,\n                                                      const T_IN*  src,\n                                                      const float* scale_ptr,\n                                                      const int    batch_size,\n                                                      const int    seq_len,\n                                                      const int    head_num,\n                                                      const int    size_per_head,\n                                                      const int*   padding_offset)\n{\n    // transpose from [batch_size, head_num, seq_len, size_per_head] to [batch_size, seq_len, head_num, size_per_head]\n    using pack4_in                = typename Pack4_type<T_IN>::type;\n    using pack4_out               = typename Pack4_type<T_OUT>::type;\n    constexpr int   pack_size     = 4;\n    const int       padded_row_id = blockIdx.x + (padding_offset == nullptr ? 0 : padding_offset[blockIdx.x]);\n    const int       src_batch_id  = padded_row_id / seq_len;\n    const int       src_seq_id    = padded_row_id % seq_len;\n    const pack4_in* src_packed    = reinterpret_cast<const pack4_in*>(src);\n    pack4_out*      dst_packed    = reinterpret_cast<pack4_out*>(dst);\n\n    const float scale = (scale_ptr == nullptr) ? 1.0f : scale_ptr[0];\n\n    for (int idx = threadIdx.x; idx < head_num * size_per_head; idx += blockDim.x) {\n        const int head_id   = idx / size_per_head;\n        const int hidden_id = idx % size_per_head;\n        pack4_out out_val;\n#pragma unroll\n        for (int i = 0; i < pack_size; i++) {\n            // TODO: bfloat162 computation ?\n            out_val.array[i] =\n                (T_OUT)((float)(src_packed[src_batch_id * head_num * seq_len * size_per_head\n                                           + head_id * seq_len * size_per_head + src_seq_id * size_per_head + hidden_id]\n                                    .array[i])\n                        * scale);\n        }\n        dst_packed[blockIdx.x * head_num * size_per_head + idx] = out_val;\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeFP8TransposeAttentionOutRemovePadding(FP8TransposeAttentionOutRemovePaddingParam<T_IN, T_OUT> param)\n{\n    // NOTE: fp8_4_t optimization\n    assert(param.size_per_head % 4 == 0);\n    int block_size = param.head_num * param.size_per_head;\n    block_size     = std::min(block_size, 512);\n    FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>((T_OUT*)param.dst,\n                                                                                                 (const T_IN*)param.src,\n                                                                                                 param.scale,\n                                                                                                 param.batch_size,\n                                                                                                 param.seq_len,\n                                                                                                 param.head_num,\n                                                                                                 param.size_per_head\n                                                                                                     / 4,\n                                                                                                 param.padding_offset);\n    // if (std::is_same<T_IN, __nv_fp8_e4m3>::value && std::is_same<T_OUT, __nv_fp8_e4m3>::value) {\n    //     int block_size = param.head_num * (param.size_per_head);\n    //     block_size = std::min(block_size, 512);\n    //     FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>(\n    //         (__nv_fp8_e4m3*)param.dst,\n    //         (__nv_fp8_e4m3*)param.src,\n    //         (const float*) nullptr,\n    //         param.batch_size,\n    //         param.seq_len,\n    //         param.head_num,\n    //         param.size_per_head / 4,\n    //         param.padding_offset);\n    // }\n    // else {\n    //     int block_size = param.head_num * param.size_per_head;\n    //     block_size = std::min(block_size, 512);\n    //     FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>(\n    //         (T_OUT*) param.dst,\n    //         (const T_IN*) param.src,\n    //         param.scale,\n    //         param.batch_size,\n    //         param.seq_len,\n    //         param.head_num,\n    //         param.size_per_head,\n    //         param.padding_offset);\n    // }\n}\n\ntemplate void invokeFP8TransposeAttentionOutRemovePadding<__nv_fp8_e4m3, __nv_fp8_e4m3>(\n    FP8TransposeAttentionOutRemovePaddingParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\n\ntemplate void invokeFP8TransposeAttentionOutRemovePadding<__nv_bfloat16, __nv_fp8_e4m3>(\n    FP8TransposeAttentionOutRemovePaddingParam<__nv_bfloat16, __nv_fp8_e4m3> param);\n\n__global__ void tmpHanldKCache(__nv_bfloat16* dst_k,\n                               __nv_fp8_e4m3* src_k,\n                               const float*   scale,\n                               int            batch_size,\n                               int            seq_len,\n                               int            padded_seq_len,\n                               int            head_num,\n                               int            size_per_head)\n{\n    int batch_id = blockIdx.y;\n    int head_id  = blockIdx.z;\n\n    __nv_bfloat16* dst_k_ptr =\n        dst_k + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head;\n    __nv_fp8_e4m3* src_k_ptr =\n        src_k + batch_id * head_num * padded_seq_len * size_per_head + head_id * padded_seq_len * size_per_head;\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx >= seq_len * size_per_head) {\n        return;\n    }\n\n    float scale_val    = scale == nullptr ? 1.0 : __ldg(scale);\n    int   out_idx      = idx;\n    dst_k_ptr[out_idx] = (__nv_bfloat16)((float)(src_k_ptr[out_idx]) * scale_val);\n}\n\nvoid invokeTmpHanldKCache(__nv_bfloat16* dst_k,\n                          __nv_fp8_e4m3* src_k,\n                          const float*   scale,\n                          int            batch_size,\n                          int            seq_len,\n                          int            padded_seq_len,\n                          int            head_num,\n                          int            size_per_head,\n                          cudaStream_t   stream)\n{\n    // from [batch, head_num, seq_len_paaded, size_per_head] to [batch, head_num, seq_len, size_per_head]\n\n    int  block_sz = 128;\n    dim3 grid((seq_len * size_per_head + block_sz - 1) / block_sz, batch_size, head_num);\n\n    tmpHanldKCache<<<grid, block_sz, 0, stream>>>(\n        dst_k, src_k, scale, batch_size, seq_len, padded_seq_len, head_num, size_per_head);\n}\n\n__global__ void tmpHanldVCache(__nv_bfloat16* dst_v,\n                               __nv_fp8_e4m3* src_v,\n                               const float*   scale,\n                               int            batch_size,\n                               int            seq_len,\n                               int            padded_seq_len,\n                               int            head_num,\n                               int            size_per_head)\n{\n    int batch_id = blockIdx.y;\n    int head_id  = blockIdx.z;\n\n    __nv_bfloat16* dst_v_ptr =\n        dst_v + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head;\n    __nv_fp8_e4m3* src_v_ptr =\n        src_v + batch_id * head_num * padded_seq_len * size_per_head + head_id * padded_seq_len * size_per_head;\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx >= seq_len * size_per_head) {\n        return;\n    }\n\n    float scale_val     = scale == nullptr ? 1.0 : __ldg(scale);\n    int   out_idx       = idx;\n    int   seq_idx       = idx / size_per_head;\n    int   head_size_idx = idx % size_per_head;\n    dst_v_ptr[out_idx]  = (__nv_bfloat16)((float)(src_v_ptr[head_size_idx * padded_seq_len + seq_idx]) * scale_val);\n}\n\nvoid invokeTmpHanldVCache(__nv_bfloat16* dst_v,\n                          __nv_fp8_e4m3* src_v,\n                          const float*   scale,\n                          int            batch_size,\n                          int            seq_len,\n                          int            padded_seq_len,\n                          int            head_num,\n                          int            size_per_head,\n                          cudaStream_t   stream)\n{\n    // from [batch, head_num, size_per_head, seq_len_paaded] to [batch, head_num, seq_len, size_per_head]\n    int  block_sz = 128;\n    dim3 grid((seq_len * size_per_head + block_sz - 1) / block_sz, batch_size, head_num);\n\n    tmpHanldVCache<<<grid, block_sz, 0, stream>>>(\n        dst_v, src_v, scale, batch_size, seq_len, padded_seq_len, head_num, size_per_head);\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_fp8_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\nstruct Vec_t {\n};\ntemplate<>\nstruct Vec_t<float> {\n    using Type = float2;\n};\ntemplate<>\nstruct Vec_t<half> {\n    using Type = half2;\n};\n\ntemplate<>\nstruct Vec_t<__hip_fp8_e4m3_fnuz> {\n    using Type = __hip_fp8x2_e4m3_fnuz;\n};\n\ntemplate<>\nstruct Vec_t<__hip_bfloat16> {\n    using Type = nv_bfloat162;\n};\n\ntemplate<typename Vec1, typename Vec2>\n__device__ __inline__ Vec1 convert_vec(Vec2 in_vec)\n{\n    return Vec1(in_vec);\n}\n\ntemplate<>\n__device__ __inline__ float2 convert_vec(half2 in_vec)\n{\n    return float2{(float)in_vec.x, (float)in_vec.y};\n}\n\ntemplate<>\n__device__ __inline__ float2 convert_vec(__hip_bfloat162 in_vec)\n{\n    return float2{(float)in_vec.x, (float)in_vec.y};\n}\n\ntemplate<>\n__device__ __inline__ __hip_bfloat162 convert_vec(half2 in_vec)\n{\n    __hip_bfloat162 out = cuda_cast<__hip_bfloat162, half2>(in_vec);\n    return out;\n}\n\n#ifdef ENABLE_FP8\ntemplate<>\n__device__ __inline__ float2 convert_vec(__hip_fp8x2_e4m3_fnuz in_vec)\n{\n    return (float2)in_vec;\n}\n\ntemplate<>\n__device__ __inline__ __hip_bfloat162 convert_vec(__hip_fp8x2_e4m3_fnuz in_vec)\n{\n    return fp8x2_e4m3_to_bfloat2(&in_vec);\n}\n#endif\n\n// This optimization has bug\n// #define OPT_TRANSPOSE\n\n#ifdef OPT_TRANSPOSE\ntemplate<typename T1, typename T2, int SEQ_GROUP_SIZE, int SIZE_PER_HEAD>\n__global__ void FP8AddFusedQKVBiasRebuildPaddingKernel(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    using T1_4 = __hip_fp8x4_e4m3_fnuz;\n    using T2_2 = typename TypeConverter<T2>::Type;\n\n    __shared__ T2 src_v[SEQ_GROUP_SIZE * (SIZE_PER_HEAD + 2)];\n    T2_2*         src_v_2 = (T2_2*)(src_v);\n\n    const int batch_idx = blockIdx.x;\n    const int seq_idx   = blockIdx.y * SEQ_GROUP_SIZE + threadIdx.y;\n    const int head_idx  = blockIdx.z;\n    const int tidx      = threadIdx.x;\n    int       Dh_div_4  = param.size_per_head / 4;\n    bool      is_valid_seq_idx =\n        param.padding_offset_prefix_sum == nullptr ?\n                 seq_idx < param.seq_len :\n                 seq_idx < (param.padding_offset_prefix_sum[batch_idx + 1] - param.padding_offset_prefix_sum[batch_idx]);\n\n    if (tidx < Dh_div_4 && is_valid_seq_idx) {\n\n        const int sentence_idx =\n            (param.padding_offset_prefix_sum == nullptr ? param.seq_len * batch_idx :\n                                                          param.padding_offset_prefix_sum[batch_idx])\n            + seq_idx;\n\n        int Dh_div_2 = param.size_per_head / 2;\n        int n_div_4  = param.head_num * Dh_div_4;\n        int n_div_2  = param.head_num * Dh_div_2;\n\n        const int hidden_idx = head_idx * Dh_div_4 + tidx;\n\n        const int q_idx = sentence_idx * 3 * n_div_4 + hidden_idx;\n        const int k_idx = sentence_idx * 3 * n_div_4 + hidden_idx + n_div_4;\n        const int v_idx = sentence_idx * 3 * n_div_4 + hidden_idx + 2 * n_div_4;\n\n        T1_4* qkv_ptr = (T1_4*)(param.QKV_T1);\n        T2_2  q[2];\n        T2_2  k[2];\n        T2_2  v[2];\n\n        fp8x4_e4m3_to_bfloat2(&q[0], &q[1], &qkv_ptr[q_idx]);\n        fp8x4_e4m3_to_bfloat2(&k[0], &k[1], &qkv_ptr[k_idx]);\n        fp8x4_e4m3_to_bfloat2(&v[0], &v[1], &qkv_ptr[v_idx]);\n        T2_2 input_scale2 = cuda_cast<T2_2>(param.input_scale == nullptr ? 1.0f : __ldg(param.input_scale));\n\n        if (param.input_scale_2 != nullptr) {\n            // q.x = q.x * input_scale * __ldg(param.input_scale_2 + hidden_idx)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // q.y = q.y * input_scale * __ldg(param.input_scale_2 + hidden_idx)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // k.x = k.x * input_scale * __ldg(param.input_scale_2 + hidden_idx + n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // k.y = k.y * input_scale * __ldg(param.input_scale_2 + hidden_idx + n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // v.x = v.x * input_scale * __ldg(param.input_scale_2 + hidden_idx + 2 * n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n            // v.y = v.y * input_scale * __ldg(param.input_scale_2 + hidden_idx + 2 * n)\n            //       * (param.input_scale_2_min == nullptr ? 1.0f : ldg(param.input_scale_2_min));\n        }\n        else {\n            q[0] = hmul2(q[0], input_scale2);\n            q[1] = hmul2(q[1], input_scale2);\n            k[0] = hmul2(k[0], input_scale2);\n            k[1] = hmul2(k[1], input_scale2);\n            v[0] = hmul2(v[0], input_scale2);\n            v[1] = hmul2(v[1], input_scale2);\n        }\n\n        T2_2* bias_ptr = (T2_2*)(param.qkv_bias);\n\n        q[0] = mmha::add(q[0], bias_ptr[head_idx * Dh_div_2 + 2 * tidx]);\n        q[1] = mmha::add(q[1], bias_ptr[head_idx * Dh_div_2 + 2 * tidx + 1]);\n        k[0] = mmha::add(k[0], bias_ptr[head_idx * Dh_div_2 + 1 * n_div_2 + 2 * tidx]);\n        k[1] = mmha::add(k[1], bias_ptr[head_idx * Dh_div_2 + 1 * n_div_2 + 2 * tidx + 1]);\n        v[0] = mmha::add(v[0], bias_ptr[head_idx * Dh_div_2 + 2 * n_div_2 + 2 * tidx]);\n        v[1] = mmha::add(v[1], bias_ptr[head_idx * Dh_div_2 + 2 * n_div_2 + 2 * tidx + 1]);\n\n        mmha::apply_rotary_embedding(q[0], k[0], 2 * tidx, param.rotary_embedding_dim, seq_idx);\n        mmha::apply_rotary_embedding(q[1], k[1], 2 * tidx + 1, param.rotary_embedding_dim, seq_idx);\n\n        T2_2 output_scale2 = cuda_cast<T2_2>(param.output_scale == nullptr ? 1.0f : __ldg(param.output_scale));\n\n        q[0] = hmul2(q[0], output_scale2);\n        q[1] = hmul2(q[1], output_scale2);\n        k[0] = hmul2(k[0], output_scale2);\n        k[1] = hmul2(k[1], output_scale2);\n        v[0] = hmul2(v[0], output_scale2);\n        v[1] = hmul2(v[1], output_scale2);\n\n        T1_4* q_out_ptr = (T1_4*)(param.q_buf);\n        T1_4* k_out_ptr = (T1_4*)(param.k_buf);\n\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 0] = v[0];\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 1] = v[1];\n\n        // q_buf, k_buf: [batch, head_num, seq_len_paaded, size_per_head]\n        const int dest_idx = n_div_4 * param.seq_len_padded * batch_idx + Dh_div_4 * param.seq_len_padded * head_idx\n                             + Dh_div_4 * seq_idx + tidx;\n\n        q_out_ptr[dest_idx] = T1_4(q[0], q[1]);\n        k_out_ptr[dest_idx] = T1_4(k[0], k[1]);\n    }\n    else {\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 0] = cuda_cast<T2_2>(0.0f);\n        src_v_2[threadIdx.y * (SIZE_PER_HEAD / 2 + 1) + tidx * 2 + 1] = cuda_cast<T2_2>(0.0f);\n    }\n\n    __syncthreads();\n\n    const int seq_group_id = seq_idx / SEQ_GROUP_SIZE;\n    // v_buf: [batch, head_num, size_per_head, seq_len_paaded]\n    T1_4* v_out_ptr = (T1_4*)(param.v_buf);\n    for (int new_id = threadIdx.x * blockDim.y + threadIdx.y; new_id < SIZE_PER_HEAD * (SEQ_GROUP_SIZE / 4);\n         new_id += blockDim.x * blockDim.y) {\n        int new_size_id = new_id / (SEQ_GROUP_SIZE / 4);\n        int new_seq_id  = new_id % (SEQ_GROUP_SIZE / 4);\n\n        T2_2 val_1;\n        T2_2 val_2;\n        val_1.x = src_v[(4 * new_seq_id + 0) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_1.y = src_v[(4 * new_seq_id + 1) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_2.x = src_v[(4 * new_seq_id + 2) * (SIZE_PER_HEAD + 2) + new_size_id];\n        val_2.y = src_v[(4 * new_seq_id + 3) * (SIZE_PER_HEAD + 2) + new_size_id];\n\n        v_out_ptr[param.head_num * param.size_per_head * (param.seq_len_padded / 4) * batch_idx\n                  + param.size_per_head * (param.seq_len_padded / 4) * head_idx\n                  + new_size_id * (param.seq_len_padded / 4) + seq_group_id * (SEQ_GROUP_SIZE / 4) + new_seq_id] =\n            T1_4(val_1, val_2);\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddFusedQKVBiasRebuildPadding(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    // To implement rotary embeddings, each thread processes two QKV elems:\n    const int seq_group_size = 64;\n    dim3      block((param.size_per_head / 4 + 1) / 2 * 2, seq_group_size);\n    dim3      grid(param.batch_size, (param.seq_len + seq_group_size - 1) / seq_group_size, param.head_num);\n\n    FT_CHECK(block.x * block.y <= 1024);\n    if (param.size_per_head == 64) {\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, seq_group_size, 64><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        FT_CHECK(false);\n    }\n}\n\n// template void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, __nv_fp8_e4m3>(\n//     FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, __nv_fp8_e4m3> param);\n// template void invokeFP8AddFusedQKVBiasRebuildPadding<__nv_fp8_e4m3, half>(\n//     FP8AddFusedQKVBiasRebuildPaddingParam<__nv_fp8_e4m3, half> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n#else\ntemplate<typename T1, typename T2, bool INPUT_T1>\n__global__ void FP8AddFusedQKVBiasRebuildPaddingKernel(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    using VecFP8_t                   = typename Vec_t<T1>::Type;\n    using VecBias_t                  = typename Vec_t<T2>::Type;\n    using PACKED_BF16                = __nv_bfloat162_2;\n    using PACKED_FP8                 = __nv_fp8x2_x2_e4m3;\n    using ARRAY_FP8                  = __nv_fp8_4_e4m3;\n    using ARRAY_BF16                 = __nv_bfloat164;\n    using VecBF16_t                  = typename Vec_t<__hip_bfloat16>::Type;\n    constexpr int ELEMENT_PER_THREAD = 4;\n    constexpr int NUM_PACKS          = 2;\n    // const int sentence_idx = blockIdx.z;\n    // const int padded_row_id = sentence_idx + (param.padding_offset == nullptr ? 0 :\n    // param.padding_offset[sentence_idx]);\n    const int  batch_idx         = blockIdx.z / param.head_num;\n    const int  head_idx          = blockIdx.z % param.head_num;\n    const int  seq_idx           = threadIdx.y + blockIdx.y * blockDim.y;\n    const int  sentence_idx      = batch_idx * param.seq_len + seq_idx;\n    const int  size_per_head_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    const bool V_CACHE_STORE     = param.v_cache != nullptr;\n    const int  v_dest_idx_0      = param.size_per_head * param.seq_len_padded * param.head_num * batch_idx\n                             + param.size_per_head * param.seq_len_padded * head_idx;\n    __shared__ T1 sdata[32 * 33 * ELEMENT_PER_THREAD];\n#pragma unroll\n    for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n        sdata[blockIdx.y * 33 * ELEMENT_PER_THREAD + blockIdx.x * ELEMENT_PER_THREAD + i] = (T1)0.0f;\n    }\n\n    if (size_per_head_idx * ELEMENT_PER_THREAD < param.size_per_head && seq_idx < param.seq_len_padded) {\n        const int hidden_idx = head_idx * param.size_per_head + size_per_head_idx * ELEMENT_PER_THREAD;\n        const int n          = param.head_num * param.size_per_head;\n\n        // src QKV: [token_num, 3, head, hidden]\n        const int q_idx = sentence_idx * 3 * n + hidden_idx;\n        const int k_idx = sentence_idx * 3 * n + hidden_idx + n;\n        const int v_idx = sentence_idx * 3 * n + hidden_idx + 2 * n;\n\n        PACKED_BF16 q, k, v;\n\n        if (INPUT_T1) {\n            PACKED_FP8 q_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[q_idx]);\n            PACKED_FP8 k_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[k_idx]);\n            PACKED_FP8 v_input = *reinterpret_cast<const PACKED_FP8*>(&param.QKV_T1[v_idx]);\n#pragma unroll\n            for (int i = 0; i < NUM_PACKS; i++) {\n                q.array[i] = convert_vec<VecBF16_t, VecFP8_t>(q_input.array[i]);\n                k.array[i] = convert_vec<VecBF16_t, VecFP8_t>(k_input.array[i]);\n                v.array[i] = convert_vec<VecBF16_t, VecFP8_t>(v_input.array[i]);\n            }\n        }\n        else {\n            q = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[q_idx]);\n            k = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[k_idx]);\n            v = *reinterpret_cast<const PACKED_BF16*>(&param.QKV_T2[v_idx]);\n        }\n\n        __hip_bfloat162 input_scale2 =\n            __float2bfloat162_rn(param.input_scale == nullptr ? 1.0f : __ldg(param.input_scale));\n\n        PACKED_BF16 q_bias, k_bias, v_bias;\n        q_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx]);\n        k_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx + n]);\n        v_bias = *reinterpret_cast<const PACKED_BF16*>(&param.qkv_bias[hidden_idx + 2 * n]);\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q.array[i] = mmha::add(hmul2(q.array[i], input_scale2), q_bias.array[i]);\n            k.array[i] = mmha::add(hmul2(k.array[i], input_scale2), k_bias.array[i]);\n            v.array[i] = mmha::add(hmul2(v.array[i], input_scale2), v_bias.array[i]);\n        }\n\n        // mmha::apply_rotary_embedding(q, k, tidx, param.rotary_embedding_dim, seq_idx);\n        const int v_cache_idx = param.size_per_head * param.max_seq_len * param.head_num * batch_idx\n                                + param.size_per_head * param.max_seq_len * head_idx + seq_idx * param.size_per_head\n                                + size_per_head_idx * ELEMENT_PER_THREAD;\n#ifndef FP8_MHA\n        if (V_CACHE_STORE && seq_idx < param.seq_len) {\n            *reinterpret_cast<PACKED_BF16*>(&param.v_cache[v_cache_idx]) = v;\n        }\n#endif\n\n        __hip_bfloat162 output_scale2 =\n            __float2bfloat162_rn(param.output_scale == nullptr ? 1.0f : __ldg(param.output_scale));\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q.array[i] = hmul2(q.array[i], output_scale2);\n            k.array[i] = hmul2(k.array[i], output_scale2);\n            v.array[i] = hmul2(v.array[i], output_scale2);\n#ifdef FP8_MHA\n            if (V_CACHE_STORE && seq_idx < param.seq_len) {\n                reinterpret_cast<VecFP8_t*>(&param.v_cache[v_cache_idx])[i] =\n                    convert_vec<VecFP8_t, VecBF16_t>(v.array[i]);\n            }\n#endif\n        }\n\n        // q_buf, k_buf: [batch, head_num, seq_len_paaded, size_per_head]\n        const int dest_idx = param.size_per_head * param.seq_len_padded * param.head_num * batch_idx\n                             + param.size_per_head * param.seq_len_padded * head_idx + param.size_per_head * seq_idx\n                             + size_per_head_idx * ELEMENT_PER_THREAD;\n\n        PACKED_FP8 q_output, k_output;\n#pragma unroll\n        for (int i = 0; i < NUM_PACKS; i++) {\n            q_output.array[i] = convert_vec<VecFP8_t, VecBF16_t>(q.array[i]);\n            k_output.array[i] = convert_vec<VecFP8_t, VecBF16_t>(k.array[i]);\n        }\n        *reinterpret_cast<PACKED_FP8*>(&param.q_buf[dest_idx]) = q_output;\n        *reinterpret_cast<PACKED_FP8*>(&param.k_buf[dest_idx]) = k_output;\n\n        ARRAY_BF16 v_val = *reinterpret_cast<ARRAY_BF16*>(&v);\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n            sdata[threadIdx.y * 33 * ELEMENT_PER_THREAD + (threadIdx.x * ELEMENT_PER_THREAD + i)] = (T1)v_val.array[i];\n        }\n    }\n\n    __syncthreads();\n\n    const int trans_size_per_head_idx = threadIdx.y + blockIdx.x * blockDim.x;\n    const int trans_seq_idx           = threadIdx.x + blockIdx.y * blockDim.y;\n    if (trans_size_per_head_idx * ELEMENT_PER_THREAD < param.size_per_head && trans_seq_idx < param.seq_len_padded) {\n#pragma unroll\n        for (int i = 0; i < ELEMENT_PER_THREAD; i++) {\n            param.v_buf[v_dest_idx_0 + (trans_size_per_head_idx * ELEMENT_PER_THREAD + i) * param.seq_len_padded\n                        + trans_seq_idx] =\n                sdata[threadIdx.x * 33 * ELEMENT_PER_THREAD + threadIdx.y * ELEMENT_PER_THREAD + i];\n        }\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddFusedQKVBiasRebuildPadding(FP8AddFusedQKVBiasRebuildPaddingParam<T1, T2> param)\n{\n    // To implement rotary embeddings, each thread processes two QKV elems:\n    // dim3 block((param.size_per_head / 2 + 31) / 32 * 32);\n    // dim3 grid(param.head_num, param.token_num);\n    const int pack_size = 4;\n    FT_CHECK(param.size_per_head % pack_size == 0);\n    dim3 block(32, 32);\n    dim3 grid((param.size_per_head / pack_size + 31) / 32,\n              (param.seq_len_padded + 31) / 32,\n              param.head_num * param.batch_size);\n    // FT_CHECK(block.x * block.y <= 1024);\n    // shared memory configuration\n    const int carveout = 50;  // prefer shared memory capacity 50% of maximum\n    if (param.QKV_T1 == nullptr) {\n        assert(param.QKV_T2 != nullptr);\n        hipFuncSetAttribute(reinterpret_cast<const void*>((const void*)FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, false>),\n                             hipFuncAttributePreferredSharedMemoryCarveout,\n                             carveout);\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, false><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        hipFuncSetAttribute(reinterpret_cast<const void*>((const void*)FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, true>),\n                             hipFuncAttributePreferredSharedMemoryCarveout,\n                             carveout);\n        FP8AddFusedQKVBiasRebuildPaddingKernel<T1, T2, true><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__hip_fp8_e4m3_fnuz, half>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__hip_fp8_e4m3_fnuz, half> param);\ntemplate void invokeFP8AddFusedQKVBiasRebuildPadding<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(\n    FP8AddFusedQKVBiasRebuildPaddingParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\n#endif\n\ntemplate<typename T1, typename T2>\n__global__ void FP8TrtAddQKVBiasKernel(FP8TrtAddQKVBiasParam<T1, T2> param)\n{\n    // Add bias ([3, head, size]), and then transpose from\n    // [valid_word_num, 3, head, size] -> [valid_word_num, head, 3, size]\n\n    const T1* qkv_src_ptr = param.qkv_src + blockIdx.x * 3 * param.hidden_unit;\n    const T2* bias_ptr    = param.qkv_bias;\n    T1*       qkv_tgt_ptr = param.qkv_tgt + blockIdx.x * 3 * param.hidden_unit;\n\n    qkv_tgt_ptr[threadIdx.x * 3 * param.size_per_head + blockIdx.y * param.size_per_head + threadIdx.y] =\n        (T1)(((float)qkv_src_ptr[blockIdx.y * param.hidden_unit + threadIdx.x * param.size_per_head + threadIdx.y]\n                  * __ldg(param.input_scale)\n              + (float)bias_ptr[blockIdx.y * param.hidden_unit + threadIdx.x * param.size_per_head + threadIdx.y])\n             * __ldg(param.output_scale));\n}\n\ntemplate<>\n__global__ void FP8TrtAddQKVBiasKernel(FP8TrtAddQKVBiasParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param)\n{\n    // Add bias ([3, head, size]), and then transpose from\n    // [valid_word_num, 3, head, size] -> [valid_word_num, head, 3, size]\n\n    using T1_4 = __hip_fp8x4_e4m3_fnuz;\n    using T2_2 = typename TypeConverter<__hip_bfloat16>::Type;\n\n    const T1_4* qkv_src_ptr = (T1_4*)(param.qkv_src + blockIdx.x * 3 * param.hidden_unit);\n    const T2_2* bias_ptr    = (T2_2*)param.qkv_bias;\n    T1_4*       qkv_tgt_ptr = (T1_4*)(param.qkv_tgt + blockIdx.x * 3 * param.hidden_unit);\n\n    const int size_div_4   = param.size_per_head / 4;\n    const int hidden_div_4 = param.hidden_unit / 4;\n    const int src_id       = threadIdx.z * hidden_div_4 + threadIdx.y * size_div_4 + threadIdx.x;\n\n    T2_2 val1, val2;\n    fp8x4_e4m3_to_bfloat2(&val1, &val2, &qkv_src_ptr[src_id]);\n    T2_2      input_scale_2  = cuda_cast<T2_2, float>(__ldg(param.input_scale));\n    T2_2      output_scale_2 = cuda_cast<T2_2, float>(__ldg(param.output_scale));\n    const int bias_id_0      = src_id * 2;\n    val1                     = hmul2(hadd2(hmul2(val1, input_scale_2), bias_ptr[bias_id_0]), output_scale_2);\n    val2                     = hmul2(hadd2(hmul2(val2, input_scale_2), bias_ptr[bias_id_0 + 1]), output_scale_2);\n\n    qkv_tgt_ptr[(threadIdx.y * 3 * size_div_4 + threadIdx.z * size_div_4) + threadIdx.x] = __hip_fp8x4_e4m3_fnuz(val1, val2);\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8TrtAddQKVBias(FP8TrtAddQKVBiasParam<T1, T2> param)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    if (std::is_same<T1, __hip_fp8_e4m3_fnuz>::value && std::is_same<T2, __hip_bfloat16>::value) {\n        dim3 grid(param.valid_word_num);\n        dim3 block(param.size_per_head / 4, param.head_num, 3);\n        FP8TrtAddQKVBiasKernel<T1, T2><<<grid, block, 0, param.stream>>>(param);\n    }\n    else {\n        dim3 grid(param.valid_word_num, 3);\n        dim3 block(param.head_num, param.size_per_head);\n\n        FP8TrtAddQKVBiasKernel<T1, T2><<<grid, block, 0, param.stream>>>(param);\n    }\n}\n\n#ifdef ENABLE_FP8\ntemplate void\ninvokeFP8TrtAddQKVBias<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(FP8TrtAddQKVBiasParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n#endif\n\ntemplate<typename T1, typename T2>\n__global__ void transpose_4d_batch_major_k_cache(T2*          k_dst,\n                                                 const T1*    k_src,\n                                                 const float* scale,\n                                                 const int    head_num,\n                                                 const int    size_per_head,\n                                                 const int    seq_len,\n                                                 const int    max_seq_len,\n                                                 const int    seq_len_padded)\n{\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = 16;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * seq_len_padded\n                                                  + head_id * size_per_head * seq_len_padded);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        key_dst[out_idx] = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n    }\n}\n\ntemplate<>\n__global__ void transpose_4d_batch_major_k_cache(__hip_bfloat16*       k_dst,\n                                                 const __hip_fp8_e4m3_fnuz* k_src,\n                                                 const float*         scale,\n                                                 const int            head_num,\n                                                 const int            size_per_head,\n                                                 const int            seq_len,\n                                                 const int            max_seq_len,\n                                                 const int            seq_len_padded)\n{\n    const int     batch_id  = blockIdx.y;\n    const int     head_id   = blockIdx.z;\n    constexpr int X_ELEMS   = 8;\n    const float   scale_val = scale[0];\n    using fp8_8             = __nv_fp8_8_e4m3;\n    using bf16_8            = __nv_bfloat168;\n\n    auto key_src = reinterpret_cast<const fp8_8*>(k_src + batch_id * head_num * size_per_head * seq_len_padded\n                                                  + head_id * size_per_head * seq_len_padded);\n    auto key_dst = reinterpret_cast<bf16_8*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                             + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        fp8_8  src_val = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n        bf16_8 dst_val;\n#pragma unroll\n        for (int i = 0; i < X_ELEMS; i++) {\n            dst_val.array[i] = __float2bfloat16_rn((float)src_val.array[i] * scale_val);\n        }\n        key_dst[out_idx] = dst_val;\n    }\n}\n\ntemplate<typename T1, typename T2>\n__global__ void transpose_4d_batch_major_v_cache(T2*          v_dst,\n                                                 const T1*    v_src,\n                                                 const float* scale,\n                                                 const int    head_num,\n                                                 const int    size_per_head,\n                                                 const int    seq_len,\n                                                 const int    max_seq_len,\n                                                 const int    seq_len_padded)\n{\n    const int      batch_id    = blockIdx.y;\n    const int      head_id     = blockIdx.z;\n    constexpr bool BF16_OUTPUT = std::is_same<T2, __hip_bfloat16>::value;\n    float          scale_val   = BF16_OUTPUT ? scale[0] : 1.0f;\n\n    // 16 byte loads will handle \"x\" dimension\n    // NOTE: need transpose, so cannot take x dimension\n    auto val_src = reinterpret_cast<const T1*>(v_src + batch_id * head_num * size_per_head * seq_len_padded\n                                               + head_id * size_per_head * seq_len_padded);\n    auto val_dst = reinterpret_cast<T2*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                         + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (idx >= size_per_head * seq_len)\n        return;\n\n    int seq_idx       = idx / size_per_head;\n    int head_size_idx = idx % size_per_head;\n    val_dst[idx]      = (T2)((float)val_src[head_size_idx * seq_len_padded + seq_idx] * scale_val);\n}\n\n// template<typename T1, typename T2>\n// __global__ void transpose_4d_batch_major_v_cache(T2* v_dst,\n//                                                  const T1* v_src,\n//                                                  const float* scale,\n//                                                  const int head_num,\n//                                                  const int size_per_head,\n//                                                  const int seq_len,\n//                                                  const int max_seq_len,\n//                                                  const int seq_len_padded)\n// {\n//     const int batch_id = blockIdx.z / head_num;\n//     const int head_id = blockIdx.z % head_num;\n//     constexpr bool BF16_OUTPUT = std::is_same<T2, __nv_bfloat16>::value;\n//     float scale_val = BF16_OUTPUT ? scale[0] : 1.0f;\n//     __shared__ T2 s_transpose_buffer[32][33];\n\n//     // 16 byte loads will handle \"x\" dimension\n//     // NOTE: need transpose, so cannot take x dimension\n//     auto val_src = reinterpret_cast<const T1*>(v_src + batch_id * head_num * size_per_head * seq_len_padded\n//                                                   + head_id * size_per_head * seq_len_padded);\n//     auto val_dst = reinterpret_cast<T2*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n//                                             + head_id * size_per_head * max_seq_len);\n\n//     // idx is over output dimension L * size_per_head / x for values\n//     for (int y_block_id = 0; y_block_id * blockDim.x < size_per_head; y_block_id ++) {\n//         for (int x_block_id = 0; x_block_id * blockDim.x < seq_len; x_block_id ++) {\n//             int seq_block_id = threadIdx.x + blockDim.x * x_block_id;\n//             int head_size_id = threadIdx.y + blockDim.y * y_block_id;\n//             if (seq_block_id < seq_len && head_size_id < size_per_head) {\n//                 s_transpose_buffer[threadIdx.y][threadIdx.x] = (T2) ((float) val_src[head_size_id * seq_len_padded +\n//                 seq_block_id] * scale_val);\n//             }\n//             __syncthreads();\n//             int transposed_head_size_id = threadIdx.x + blockDim.y * y_block_id;\n//             int transposed_seq_block_id = threadIdx.y + blockDim.x * x_block_id;\n//             if (transposed_head_size_id < size_per_head && transposed_seq_block_id < seq_len) {\n//                 val_dst[transposed_seq_block_id * size_per_head + transposed_head_size_id] =\n//                 s_transpose_buffer[threadIdx.x][threadIdx.y];\n//             }\n//         }\n//     }\n// }\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8Transpose4dBatchMajor(FP8Transpose4dBatchMajorParam<T1, T2> param)\n{\n    constexpr int block_sz = 128;\n    constexpr int x        = std::is_same<T2, __hip_bfloat16>::value ? 8 : 16;\n    int           size     = param.max_seq_len * param.size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, param.local_batch_size, param.local_head_num);\n    dim3          grid_v(\n        (param.seq_len * param.size_per_head + block_sz - 1) / block_sz, param.local_batch_size, param.local_head_num);\n\n    transpose_4d_batch_major_k_cache<<<grid, block_sz, 0, param.stream>>>(param.k_dst,\n                                                                          param.k_src,\n                                                                          param.scale,\n                                                                          param.local_head_num,\n                                                                          param.size_per_head,\n                                                                          param.seq_len,\n                                                                          param.max_seq_len,\n                                                                          param.seq_len_padded);\n\n    // transpose_4d_batch_major_v_cache<<<grid_v, block_sz, 0, param.stream>>>(param.v_dst,\n    //                                                                         param.v_src,\n    //                                                                         param.scale,\n    //                                                                         param.local_head_num,\n    //                                                                         param.size_per_head,\n    //                                                                         param.seq_len,\n    //                                                                         param.max_seq_len,\n    //                                                                         param.seq_len_padded);\n}\n\ntemplate void invokeFP8Transpose4dBatchMajor<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz>(\n    FP8Transpose4dBatchMajorParam<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz> param);\ntemplate void invokeFP8Transpose4dBatchMajor<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(\n    FP8Transpose4dBatchMajorParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<int ITEMS_PER_THREAD, typename T, typename T_IN>\n__global__ void softmax_kernel(T* qk_buf_,\n                               const T_IN* __restrict__ qk_buf_src,\n                               const T* __restrict__ attr_mask,\n                               const int    batch_size,\n                               const int    head_num,\n                               const int    seq_len,\n                               const float  scalar,\n                               const float* input_scale,\n                               const float* output_scale)\n{\n    float input_scale_val  = input_scale == nullptr ? 1.0f : __ldg(input_scale);\n    float output_scale_val = output_scale == nullptr ? 1.0f : __ldg(output_scale);\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        float            data[ITEMS_PER_THREAD];\n        int              qk_offset;\n        __shared__ float s_mean, s_max;\n        float            local_max = -1e20f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            int mask_offset = (blockIdx.y * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n\n            float qk       = static_cast<float>(qk_buf_src[qk_offset]) * input_scale_val;\n            float mask_val = static_cast<float>(attr_mask[mask_offset]);\n\n            mask_val = (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk * static_cast<float>(scalar) + mask_val;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < seq_len; i++) {\n            qk_offset =\n                ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id) * seq_len + blockDim.x * i + threadIdx.x;\n            qk_buf_[qk_offset] = (T)(data[i] * s_mean * output_scale_val);\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, typename T_COMPUTE, int ITEMS_PER_THREAD, int NUM>\n__global__ void softmax_kernel_v5_half2(T_OUT*       qk_buf,\n                                        const T_IN*  qk_buf_src,\n                                        const T_OUT* attr_mask,\n                                        const int*   padding_offset_prefix_sum,\n                                        const int    batch_size,\n                                        const int    head_num,\n                                        const int    seq_len,\n                                        const float  scalar,\n                                        const float* input_scale,\n                                        const float* output_scale)\n{\n    using T2_OUT     = typename Vec_t<T_OUT>::Type;\n    using T2_IN      = typename Vec_t<T_IN>::Type;\n    using T2_COMPUTE = typename Vec_t<T_COMPUTE>::Type;\n\n    T2_OUT*       qk_buf_out2     = (T2_OUT*)qk_buf;\n    const T2_OUT* attr_mask_half2 = (const T2_OUT*)attr_mask;\n    T2_IN*        qk_buf_in2      = (T2_IN*)qk_buf_src;\n\n    T2_COMPUTE input_scale_2  = cuda_cast<T2_COMPUTE, float>(input_scale == nullptr ? 1.0f : __ldg(input_scale));\n    T2_COMPUTE output_scale_2 = cuda_cast<T2_COMPUTE, float>(output_scale == nullptr ? 1.0f : __ldg(output_scale));\n\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x * NUM) {\n        if ((padding_offset_prefix_sum != nullptr)\n            && (seq_id >= padding_offset_prefix_sum[blockIdx.y + 1] - padding_offset_prefix_sum[blockIdx.y])) {\n\n            T2_COMPUTE zero_val;\n            zero_val.x       = 0.0f;\n            zero_val.y       = 0.0f;\n            const int offset = (blockIdx.y * head_num * seq_len + blockIdx.z * seq_len + seq_id) * seq_len / 2;\n            for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2); i++) {\n                qk_buf_out2[offset + i] = T2_OUT(zero_val);\n            }\n            continue;\n        }\n\n        T2_COMPUTE data[NUM][ITEMS_PER_THREAD];\n\n        int qk_offset[NUM];\n\n        __shared__ float s_sum[NUM], s_max[NUM];\n        float            local_max[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n            T2_COMPUTE mask_val[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                const int mask_offset =\n                    (blockIdx.y * seq_len + seq_id + j * gridDim.x) * (seq_len / 2) + blockDim.x * i + threadIdx.x;\n                mask_val[j] = cuda_cast<T2_COMPUTE>(attr_mask_half2[mask_offset]);\n            }\n\n            T2_COMPUTE qk[NUM];\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk[j] = hmul2<T2_COMPUTE>(cuda_cast<T2_COMPUTE>(qk_buf_in2[qk_offset[j]]), input_scale_2);\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                mask_val[j] = hmul2<T2_COMPUTE>(hsub2<T2_COMPUTE>(cuda_cast<T2_COMPUTE>(1.0f), mask_val[j]),\n                                                cuda_cast<T2_COMPUTE>(-10000.0f));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i]   = hadd2<T2_COMPUTE>(hmul2<T2_COMPUTE>(qk[j], cuda_cast<T2_COMPUTE>(scalar)), mask_val[j]);\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, NUM>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, NUM>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[NUM];\n#pragma unroll\n        for (int j = 0; j < NUM; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                data[j][i] = hexp2<T2_COMPUTE>(hsub2<T2_COMPUTE>(data[j][i], cuda_cast<T2_COMPUTE>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, NUM>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, NUM>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (seq_len / 2) && i < ITEMS_PER_THREAD; i++) {\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_offset[j] = ((blockIdx.y * head_num + blockIdx.z) * seq_len + seq_id + j * gridDim.x) * (seq_len / 2)\n                               + blockDim.x * i + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < NUM; j++) {\n                qk_buf_out2[qk_offset[j]] = T2_OUT(\n                    hmul2<T2_COMPUTE>(hmul2<T2_COMPUTE>(data[j][i], cuda_cast<T2_COMPUTE>(s_sum[j])), output_scale_2));\n            }\n        }\n    }\n}\n\n#define SOFTMAX_KERNEL_HALF2(ITEMS_PER_THREAD)                                                                         \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    assert(block.x <= 1024);                                                                                           \\\n    grid.x /= 4;                                                                                                       \\\n    softmax_kernel_v5_half2<T, T_IN, __hip_bfloat16, ITEMS_PER_THREAD, 4>                                               \\\n        <<<grid, block, 0, param.stream>>>((T*)param.buffer,                                                           \\\n                                           (T_IN*)param.buffer_src,                                                    \\\n                                           (const T*)param.attr_mask,                                                  \\\n                                           param.padding_offset_prefix_sum,                                            \\\n                                           param.batch_size,                                                           \\\n                                           param.head_num,                                                             \\\n                                           param.seq_len,                                                              \\\n                                           param.scalar,                                                               \\\n                                           param.input_scale,                                                          \\\n                                           param.output_scale);\n\ntemplate<typename T, typename T_IN>\nvoid invokeFP8MaskedSoftMax(FP8MaskedSoftMaxParam<T, T_IN> param)\n{\n    dim3 grid(param.seq_len, param.batch_size, param.head_num);\n    if (param.batch_size * param.head_num > 360) {\n        grid.x = ceil(float(param.seq_len) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 1 && sizeof(T_IN) == 2 && param.seq_len % 2 == 0;\n\n    if (is_half2 && grid.x % 4 == 0) {\n        dim3 block((param.seq_len / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n        if (block.x > 2048 && block.x <= 4096) {\n            SOFTMAX_KERNEL_HALF2(4)\n        }\n        else if (block.x > 1024) {\n            SOFTMAX_KERNEL_HALF2(2)\n        }\n        else if (block.x > 0) {\n            SOFTMAX_KERNEL_HALF2(1)\n        }\n        else {\n            FT_CHECK(param.seq_len <= 4096);\n        }\n    }\n    else {\n        dim3 block((param.seq_len + 31) / 32 * 32);\n        if (block.x > 2048 && block.x <= 4096) {\n            softmax_kernel<4, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else if (block.x > 1024) {\n            softmax_kernel<2, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else if (block.x > 0) {\n            softmax_kernel<1, T, T_IN><<<grid, block, 0, param.stream>>>(param.buffer,\n                                                                         param.buffer_src,\n                                                                         param.attr_mask,\n                                                                         param.batch_size,\n                                                                         param.head_num,\n                                                                         param.seq_len,\n                                                                         param.scalar,\n                                                                         param.input_scale,\n                                                                         param.output_scale);\n        }\n        else {\n            FT_CHECK(param.seq_len <= 4096);\n        }\n    }\n}\n\ntemplate void\ninvokeFP8MaskedSoftMax<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz>(FP8MaskedSoftMaxParam<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz> param);\n\ntemplate void invokeFP8MaskedSoftMax<__hip_fp8_e4m3_fnuz, float>(FP8MaskedSoftMaxParam<__hip_fp8_e4m3_fnuz, float> param);\n\ntemplate void\ninvokeFP8MaskedSoftMax<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(FP8MaskedSoftMaxParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<typename T>\nstruct Pack4_type {\n    using type = __nv_bfloat164;\n};\n\ntemplate<>\nstruct Pack4_type<__hip_fp8_e4m3_fnuz> {\n    using type = __nv_fp8_4_e4m3;\n};\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void FP8TransposeAttentionOutRemovePadding(T_OUT*       dst,\n                                                      const T_IN*  src,\n                                                      const float* scale_ptr,\n                                                      const int    batch_size,\n                                                      const int    seq_len,\n                                                      const int    head_num,\n                                                      const int    size_per_head,\n                                                      const int*   padding_offset)\n{\n    // transpose from [batch_size, head_num, seq_len, size_per_head] to [batch_size, seq_len, head_num, size_per_head]\n    using pack4_in                = typename Pack4_type<T_IN>::type;\n    using pack4_out               = typename Pack4_type<T_OUT>::type;\n    constexpr int   pack_size     = 4;\n    const int       padded_row_id = blockIdx.x + (padding_offset == nullptr ? 0 : padding_offset[blockIdx.x]);\n    const int       src_batch_id  = padded_row_id / seq_len;\n    const int       src_seq_id    = padded_row_id % seq_len;\n    const pack4_in* src_packed    = reinterpret_cast<const pack4_in*>(src);\n    pack4_out*      dst_packed    = reinterpret_cast<pack4_out*>(dst);\n\n    const float scale = (scale_ptr == nullptr) ? 1.0f : scale_ptr[0];\n\n    for (int idx = threadIdx.x; idx < head_num * size_per_head; idx += blockDim.x) {\n        const int head_id   = idx / size_per_head;\n        const int hidden_id = idx % size_per_head;\n        pack4_out out_val;\n#pragma unroll\n        for (int i = 0; i < pack_size; i++) {\n            // TODO: bfloat162 computation ?\n            out_val.array[i] =\n                (T_OUT)((float)(src_packed[src_batch_id * head_num * seq_len * size_per_head\n                                           + head_id * seq_len * size_per_head + src_seq_id * size_per_head + hidden_id]\n                                    .array[i])\n                        * scale);\n        }\n        dst_packed[blockIdx.x * head_num * size_per_head + idx] = out_val;\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeFP8TransposeAttentionOutRemovePadding(FP8TransposeAttentionOutRemovePaddingParam<T_IN, T_OUT> param)\n{\n    // NOTE: fp8_4_t optimization\n    assert(param.size_per_head % 4 == 0);\n    int block_size = param.head_num * param.size_per_head;\n    block_size     = std::min(block_size, 512);\n    FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>((T_OUT*)param.dst,\n                                                                                                 (const T_IN*)param.src,\n                                                                                                 param.scale,\n                                                                                                 param.batch_size,\n                                                                                                 param.seq_len,\n                                                                                                 param.head_num,\n                                                                                                 param.size_per_head\n                                                                                                     / 4,\n                                                                                                 param.padding_offset);\n    // if (std::is_same<T_IN, __nv_fp8_e4m3>::value && std::is_same<T_OUT, __nv_fp8_e4m3>::value) {\n    //     int block_size = param.head_num * (param.size_per_head);\n    //     block_size = std::min(block_size, 512);\n    //     FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>(\n    //         (__nv_fp8_e4m3*)param.dst,\n    //         (__nv_fp8_e4m3*)param.src,\n    //         (const float*) nullptr,\n    //         param.batch_size,\n    //         param.seq_len,\n    //         param.head_num,\n    //         param.size_per_head / 4,\n    //         param.padding_offset);\n    // }\n    // else {\n    //     int block_size = param.head_num * param.size_per_head;\n    //     block_size = std::min(block_size, 512);\n    //     FP8TransposeAttentionOutRemovePadding<<<param.valid_word_num, block_size, 0, param.stream>>>(\n    //         (T_OUT*) param.dst,\n    //         (const T_IN*) param.src,\n    //         param.scale,\n    //         param.batch_size,\n    //         param.seq_len,\n    //         param.head_num,\n    //         param.size_per_head,\n    //         param.padding_offset);\n    // }\n}\n\ntemplate void invokeFP8TransposeAttentionOutRemovePadding<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz>(\n    FP8TransposeAttentionOutRemovePaddingParam<__hip_fp8_e4m3_fnuz, __hip_fp8_e4m3_fnuz> param);\n\ntemplate void invokeFP8TransposeAttentionOutRemovePadding<__hip_bfloat16, __hip_fp8_e4m3_fnuz>(\n    FP8TransposeAttentionOutRemovePaddingParam<__hip_bfloat16, __hip_fp8_e4m3_fnuz> param);\n\n__global__ void tmpHanldKCache(__hip_bfloat16* dst_k,\n                               __hip_fp8_e4m3_fnuz* src_k,\n                               const float*   scale,\n                               int            batch_size,\n                               int            seq_len,\n                               int            padded_seq_len,\n                               int            head_num,\n                               int            size_per_head)\n{\n    int batch_id = blockIdx.y;\n    int head_id  = blockIdx.z;\n\n    __hip_bfloat16* dst_k_ptr =\n        dst_k + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head;\n    __hip_fp8_e4m3_fnuz* src_k_ptr =\n        src_k + batch_id * head_num * padded_seq_len * size_per_head + head_id * padded_seq_len * size_per_head;\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx >= seq_len * size_per_head) {\n        return;\n    }\n\n    float scale_val    = scale == nullptr ? 1.0 : __ldg(scale);\n    int   out_idx      = idx;\n    dst_k_ptr[out_idx] = (__hip_bfloat16)((float)(src_k_ptr[out_idx]) * scale_val);\n}\n\nvoid invokeTmpHanldKCache(__hip_bfloat16* dst_k,\n                          __hip_fp8_e4m3_fnuz* src_k,\n                          const float*   scale,\n                          int            batch_size,\n                          int            seq_len,\n                          int            padded_seq_len,\n                          int            head_num,\n                          int            size_per_head,\n                          hipStream_t   stream)\n{\n    // from [batch, head_num, seq_len_paaded, size_per_head] to [batch, head_num, seq_len, size_per_head]\n\n    int  block_sz = 128;\n    dim3 grid((seq_len * size_per_head + block_sz - 1) / block_sz, batch_size, head_num);\n\n    tmpHanldKCache<<<grid, block_sz, 0, stream>>>(\n        dst_k, src_k, scale, batch_size, seq_len, padded_seq_len, head_num, size_per_head);\n}\n\n__global__ void tmpHanldVCache(__hip_bfloat16* dst_v,\n                               __hip_fp8_e4m3_fnuz* src_v,\n                               const float*   scale,\n                               int            batch_size,\n                               int            seq_len,\n                               int            padded_seq_len,\n                               int            head_num,\n                               int            size_per_head)\n{\n    int batch_id = blockIdx.y;\n    int head_id  = blockIdx.z;\n\n    __hip_bfloat16* dst_v_ptr =\n        dst_v + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head;\n    __hip_fp8_e4m3_fnuz* src_v_ptr =\n        src_v + batch_id * head_num * padded_seq_len * size_per_head + head_id * padded_seq_len * size_per_head;\n\n    int idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (idx >= seq_len * size_per_head) {\n        return;\n    }\n\n    float scale_val     = scale == nullptr ? 1.0 : __ldg(scale);\n    int   out_idx       = idx;\n    int   seq_idx       = idx / size_per_head;\n    int   head_size_idx = idx % size_per_head;\n    dst_v_ptr[out_idx]  = (__hip_bfloat16)((float)(src_v_ptr[head_size_idx * padded_seq_len + seq_idx]) * scale_val);\n}\n\nvoid invokeTmpHanldVCache(__hip_bfloat16* dst_v,\n                          __hip_fp8_e4m3_fnuz* src_v,\n                          const float*   scale,\n                          int            batch_size,\n                          int            seq_len,\n                          int            padded_seq_len,\n                          int            head_num,\n                          int            size_per_head,\n                          hipStream_t   stream)\n{\n    // from [batch, head_num, size_per_head, seq_len_paaded] to [batch, head_num, seq_len, size_per_head]\n    int  block_sz = 128;\n    dim3 grid((seq_len * size_per_head + block_sz - 1) / block_sz, batch_size, head_num);\n\n    tmpHanldVCache<<<grid, block_sz, 0, stream>>>(\n        dst_v, src_v, scale, batch_size, seq_len, padded_seq_len, head_num, size_per_head);\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/unfused_attention_fp8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/image_merge_kernels.h\"\n#include \"src/fastertransformer/kernels/layernorm_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeImageMerge  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (min(n, 1024))\ntemplate<typename T>\n__global__ void image_merge_kernel(T* out, const T* __restrict input, int batch, int H, int W, int n)\n{\n    const int    tid             = threadIdx.x;\n    const int    W_idx           = blockIdx.x;\n    const int    H_idx           = blockIdx.y;\n    const size_t batch_offset    = blockIdx.z * H * W * n;\n    const int    input_H_stride  = W * n / 2;\n    const int    output_H_stride = W * n;\n    const int    n_4             = n >> 2;\n    const int    bdim            = blockDim.x;\n\n#pragma unroll\n    for (int col_id = tid; col_id < n; col_id += bdim) {\n        int    part_id     = col_id / n_4;\n        int    offset_in_W = part_id / 2;\n        int    offset_in_H = part_id % 2;\n        size_t input_id    = batch_offset + (2 * H_idx + offset_in_H) * input_H_stride + (2 * W_idx + offset_in_W) * n_4\n                          + (col_id % n_4);\n        size_t output_idx = batch_offset + H_idx * output_H_stride + W_idx * n + col_id;\n        out[output_idx]   = ldg(input + input_id);\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeImageMerge(T* output, const T* input, int batch, int H, int W, int n, cudaStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeImageMerge] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = (4 * n < 1024) ? (4 * n) : 1024;\n    image_merge_kernel<T><<<grid, blockSize, 0, stream>>>(output, input, batch, H / 2, W / 2, n * 4);\n}\n\ntemplate void\ninvokeImageMerge<float>(float* output, const float* input, int batch, int H, int W, int n, cudaStream_t stream);\n\ntemplate void\ninvokeImageMerge<half>(half* output, const half* input, int batch, int H, int W, int n, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeImageMerge<__nv_bfloat16>(\n    __nv_bfloat16* output, const __nv_bfloat16* input, int batch, int H, int W, int n, cudaStream_t stream);\n#endif\n\n/*******************  invokeMergeLayerNormCol32  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (min(n, 1024))\ntemplate<typename T>\n__global__ void image_merge_col32_kernel(\n    int8_t* out, const T* __restrict input, int batch, int H, int W, int n, const float* merge_inFactor)\n{\n    // const int   ite       = 4; // not used\n    const int   tid       = threadIdx.x;\n    const int   W_idx     = blockIdx.x;\n    const int   H_idx     = blockIdx.y;\n    const float out_scale = __ldg(merge_inFactor);\n    const int   n_4       = n >> 2;\n    const int   m         = batch * 4 * H * W;\n    const int   bdim      = blockDim.x;\n\n#pragma unroll\n    for (int col_id = tid; col_id < n; col_id += bdim) {\n        int part_id     = col_id / n_4;\n        int offset_in_W = part_id / 2;\n        int offset_in_H = part_id % 2;\n\n        int   col_input       = col_id % n_4;\n        int   row_input       = blockIdx.z * H * W * 4 + (2 * H_idx + offset_in_H) * W * 2 + (2 * W_idx + offset_in_W);\n        int   input_idx_col32 = ((col_input >> 5) << 5) * m + (row_input << 5) + (col_input & 31);\n        float local_out       = (float)(__ldg(input + input_idx_col32));\n\n        int col_output        = col_id;\n        int row_output        = blockIdx.z * H * W + H_idx * W + W_idx;\n        int output_idx_col32  = ((col_output >> 5) << 5) * (m >> 2) + (row_output << 5) + (col_output & 31);\n        out[output_idx_col32] = float_to_int8_rn(out_scale * local_out);\n    }\n}\n\ntemplate<typename T>\nvoid invokeImageMergeCol32(\n    int8_t* output, const T* input, int batch, int H, int W, int n, const float* merge_inFactor, cudaStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeImageMergeCol32] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = ((blockSize / 4) + 31) / 32 * 32;\n    blockSize      = (blockSize < 1024) ? blockSize : 1024;\n    image_merge_col32_kernel<T>\n        <<<grid, blockSize, 0, stream>>>(output, input, batch, H / 2, W / 2, n * 4, merge_inFactor);\n}\n\ntemplate void invokeImageMergeCol32(int8_t*      output,\n                                    const float* input,\n                                    int          batch,\n                                    int          H,\n                                    int          W,\n                                    int          n,\n                                    const float* merge_inFactor,\n                                    cudaStream_t stream);\n\ntemplate void invokeImageMergeCol32(int8_t*      output,\n                                    const half*  input,\n                                    int          batch,\n                                    int          H,\n                                    int          W,\n                                    int          n,\n                                    const float* merge_inFactor,\n                                    cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/image_merge_kernels.h\"\n#include \"src/fastertransformer/kernels/layernorm_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/*******************  invokeImageMerge  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (min(n, 1024))\ntemplate<typename T>\n__global__ void image_merge_kernel(T* out, const T* __restrict input, int batch, int H, int W, int n)\n{\n    const int    tid             = threadIdx.x;\n    const int    W_idx           = blockIdx.x;\n    const int    H_idx           = blockIdx.y;\n    const size_t batch_offset    = blockIdx.z * H * W * n;\n    const int    input_H_stride  = W * n / 2;\n    const int    output_H_stride = W * n;\n    const int    n_4             = n >> 2;\n    const int    bdim            = blockDim.x;\n\n#pragma unroll\n    for (int col_id = tid; col_id < n; col_id += bdim) {\n        int    part_id     = col_id / n_4;\n        int    offset_in_W = part_id / 2;\n        int    offset_in_H = part_id % 2;\n        size_t input_id    = batch_offset + (2 * H_idx + offset_in_H) * input_H_stride + (2 * W_idx + offset_in_W) * n_4\n                          + (col_id % n_4);\n        size_t output_idx = batch_offset + H_idx * output_H_stride + W_idx * n + col_id;\n        out[output_idx]   = ldg(input + input_id);\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeImageMerge(T* output, const T* input, int batch, int H, int W, int n, hipStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeImageMerge] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = (4 * n < 1024) ? (4 * n) : 1024;\n    image_merge_kernel<T><<<grid, blockSize, 0, stream>>>(output, input, batch, H / 2, W / 2, n * 4);\n}\n\ntemplate void\ninvokeImageMerge<float>(float* output, const float* input, int batch, int H, int W, int n, hipStream_t stream);\n\ntemplate void\ninvokeImageMerge<half>(half* output, const half* input, int batch, int H, int W, int n, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeImageMerge<__hip_bfloat16>(\n    __hip_bfloat16* output, const __hip_bfloat16* input, int batch, int H, int W, int n, hipStream_t stream);\n#endif\n\n/*******************  invokeMergeLayerNormCol32  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (min(n, 1024))\ntemplate<typename T>\n__global__ void image_merge_col32_kernel(\n    int8_t* out, const T* __restrict input, int batch, int H, int W, int n, const float* merge_inFactor)\n{\n    // const int   ite       = 4; // not used\n    const int   tid       = threadIdx.x;\n    const int   W_idx     = blockIdx.x;\n    const int   H_idx     = blockIdx.y;\n    const float out_scale = __ldg(merge_inFactor);\n    const int   n_4       = n >> 2;\n    const int   m         = batch * 4 * H * W;\n    const int   bdim      = blockDim.x;\n\n#pragma unroll\n    for (int col_id = tid; col_id < n; col_id += bdim) {\n        int part_id     = col_id / n_4;\n        int offset_in_W = part_id / 2;\n        int offset_in_H = part_id % 2;\n\n        int   col_input       = col_id % n_4;\n        int   row_input       = blockIdx.z * H * W * 4 + (2 * H_idx + offset_in_H) * W * 2 + (2 * W_idx + offset_in_W);\n        int   input_idx_col32 = ((col_input >> 5) << 5) * m + (row_input << 5) + (col_input & 31);\n        float local_out       = (float)(__ldg(input + input_idx_col32));\n\n        int col_output        = col_id;\n        int row_output        = blockIdx.z * H * W + H_idx * W + W_idx;\n        int output_idx_col32  = ((col_output >> 5) << 5) * (m >> 2) + (row_output << 5) + (col_output & 31);\n        out[output_idx_col32] = float_to_int8_rn(out_scale * local_out);\n    }\n}\n\ntemplate<typename T>\nvoid invokeImageMergeCol32(\n    int8_t* output, const T* input, int batch, int H, int W, int n, const float* merge_inFactor, hipStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeImageMergeCol32] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = ((blockSize / 4) + 31) / 32 * 32;\n    blockSize      = (blockSize < 1024) ? blockSize : 1024;\n    image_merge_col32_kernel<T>\n        <<<grid, blockSize, 0, stream>>>(output, input, batch, H / 2, W / 2, n * 4, merge_inFactor);\n}\n\ntemplate void invokeImageMergeCol32(int8_t*      output,\n                                    const float* input,\n                                    int          batch,\n                                    int          H,\n                                    int          W,\n                                    int          n,\n                                    const float* merge_inFactor,\n                                    hipStream_t stream);\n\ntemplate void invokeImageMergeCol32(int8_t*      output,\n                                    const half*  input,\n                                    int          batch,\n                                    int          H,\n                                    int          W,\n                                    int          n,\n                                    const float* merge_inFactor,\n                                    hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/image_merge_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/activation_fp8_kernels.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__forceinline__ __device__ float copysignf_pos(float a, float b)\n{\n    float r;\n    r = __int_as_float(__float_as_int(a) | (__float_as_int(b) & 0x80000000));\n    return r;\n}\n\n__inline__ __device__ float tanh_opt(float x)\n{\n    return tanh(x);\n#if (__CUDA_ARCH__ >= 750)\n    float r;\n    asm(\"tanh.approx.f32 %0,%1; \\n\\t\" : \"=f\"(r) : \"f\"(x));\n    return r;\n#else\n    const float exp_val = -1.f * fabs(2 * x);\n    return copysignf_pos((1.0f - __expf(exp_val)) / (__expf(exp_val) + 1.0f), x);\n#endif\n}\n\ntemplate<typename T>\n__inline__ __device__ T gelu(T x)\n{\n    float f_x = (float)(x);\n    float cdf = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (f_x + 0.044715f * f_x * f_x * f_x))));\n    return (T)(f_x * cdf);\n}\n\ntemplate<>\n__inline__ __device__ __nv_bfloat162 gelu(__nv_bfloat162 val)\n{\n    __nv_bfloat162 val_pow3 = __hmul2(val, __hmul2(val, val));\n    float2         tmp_pow  = cuda_cast<float2>(val_pow3);\n    float2         tmp      = cuda_cast<float2>(val);\n\n    tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n    tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n    return __hmul2(val, __float22bfloat162_rn(tmp));\n}\n\ntemplate<typename T1, typename T2>\n__global__ void FP8AddBiasGelu(FP8ActivationParam<T1, T2> param)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * param.n; id += blockDim.x * gridDim.x) {\n        T2 val = (T2)((float)(param.out[id]) * __ldg(param.input_scale));\n        // TODO(bhsueh) check do we need it\n        // if (param.input_scale_2 != nullptr) {\n        //     val = (float)val * __ldg(param.input_scale_2 + (id % param.n)) / __ldg(param.input_scale_2_min);\n        // }\n        if (param.bias != nullptr) {\n            T2 reg_bias = __ldg(&param.bias[id % param.n]);\n            val         = val + reg_bias;\n        }\n        param.out[id] = (T1)((float)gelu(val) * __ldg(param.output_scale));\n    }\n}\n\n// template<>\n// __global__ void FP8AddBiasGelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n// {\n//     float input_scale = __ldg(param.input_scale);\n//     float output_scale = __ldg(param.output_scale);\n//     __nv_fp8x4_e4m3* out_ptr = (__nv_fp8x4_e4m3*)param.out;\n//     __nv_bfloat162* bias_ptr = (__nv_bfloat162*)param.bias;\n\n//     __nv_bfloat162 input_scale_2 = __floats2bfloat162_rn(input_scale, input_scale);\n//     __nv_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 4); id += blockDim.x * gridDim.x)\n//     {\n//         __nv_bfloat162 val_1;\n//         __nv_bfloat162 val_2;\n\n//         fp8x4_e4m3_to_bfloat2(&val_1, &val_2, &out_ptr[id]);\n\n//         __nv_bfloat162 bias_val_1 = bias_ptr[(id * 2) % (param.n / 2)];\n//         __nv_bfloat162 bias_val_2 = bias_ptr[(id * 2 + 1) % (param.n / 2)];\n\n//         val_1 = hmul2(val_1, input_scale_2);\n//         val_2 = hmul2(val_2, input_scale_2);\n//         val_1 = gelu(hadd2(val_1, bias_val_1));\n//         val_2 = gelu(hadd2(val_2, bias_val_2));\n//         val_1 = hmul2(val_1, output_scale_2);\n//         val_2 = hmul2(val_2, output_scale_2);\n\n//         out_ptr[id] = __nv_fp8x4_e4m3(val_1, val_2);\n//     }\n// }\n\n// NOTE: input is bfloat (pack8 to have 128 bit input, and 64 bit output)\ntemplate<>\n__global__ void FP8AddBiasGelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n{\n    float         input_scale     = __ldg(param.input_scale);\n    float         output_scale    = __ldg(param.output_scale);\n    constexpr int Packed_Elements = 8;\n    using packed_out              = __nv_fp8_8_e4m3;\n    using packed_bias             = __nv_bfloat168;\n    using packed_in               = __nv_bfloat168;\n    packed_out*  out_ptr          = (packed_out*)param.out;\n    packed_in*   in_ptr           = (packed_in*)param.in;\n    packed_bias* bias_ptr         = (packed_bias*)param.bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / Packed_Elements);\n         id += blockDim.x * gridDim.x) {\n        packed_in   val  = in_ptr[id];\n        packed_bias bias = bias_ptr[id % (param.n / Packed_Elements)];\n        packed_out  val_output;\n#pragma unroll\n        for (int i = 0; i < Packed_Elements; i++) {\n            val_output.array[i] = __nv_fp8_e4m3(gelu((float)val.array[i] + (float)bias.array[i]) * output_scale);\n        }\n        // f_val.x = gelu(f_val.x + f_bias.x) * output_scale;\n        // f_val.y = gelu(f_val.y + f_bias.x) * output_scale;\n        // val = hmul2(gelu(hadd2(val, bias)), output_scale_2);\n        // val = hmul2(gelu(hadd2(val, bias)), output_scale_2);\n\n        out_ptr[id] = val_output;\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddBiasGelu(FP8ActivationParam<T1, T2> param)\n{\n    FT_CHECK(param.n % 8 == 0);\n    // NOTE: input data type is bfloat now\n    const int data_type_factor = 8;  // pack 8 elements for bfloat16 input\n    const int total_packs      = (param.n * param.m) / data_type_factor;\n    dim3      block(256);\n    int       num_packs_per_thread = 1;\n    while ((total_packs + (block.x * num_packs_per_thread) - 1) / (block.x * num_packs_per_thread) > 512) {\n        num_packs_per_thread *= 2;\n    }\n    dim3 grid((total_packs + (block.x * num_packs_per_thread) - 1) / (block.x * num_packs_per_thread));\n    FP8AddBiasGelu<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeFP8AddBiasGelu<__nv_fp8_e4m3, __nv_bfloat16>(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void FP8AddBiasRelu(FP8ActivationParam<T1, T2> param)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * param.n; id += blockDim.x * gridDim.x) {\n        T2 val = (T2)((float)(param.out[id]) * __ldg(param.input_scale));\n        // TODO(bhsueh) check do we need it\n        // if (param.input_scale_2 != nullptr) {\n        //     val = (float)val * __ldg(param.input_scale_2 + (id % param.n)) / __ldg(param.input_scale_2_min);\n        // }\n        if (param.bias != nullptr) {\n            T2 reg_bias = __ldg(&param.bias[id % param.n]);\n            val         = val + reg_bias;\n        }\n        val           = (T2)(((float)val) * __ldg(param.output_scale));\n        param.out[id] = (T1)((val > (T2)0.0f) ? val : (T2)0.0f);\n    }\n}\n\n// template<>\n// __global__ void FP8AddBiasRelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n// {\n//     float input_scale = __ldg(param.input_scale);\n//     float output_scale = __ldg(param.output_scale);\n//     __nv_fp8x4_e4m3* out_ptr = (__nv_fp8x4_e4m3*)param.out;\n//     __nv_bfloat162* bias_ptr = (__nv_bfloat162*)param.bias;\n\n//     __nv_bfloat162 input_scale_2 = __floats2bfloat162_rn(input_scale, input_scale);\n//     __nv_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 4); id += blockDim.x * gridDim.x)\n//     {\n//         __nv_bfloat162 val_1;\n//         __nv_bfloat162 val_2;\n\n//         fp8x4_e4m3_to_bfloat2(&val_1, &val_2, &out_ptr[id]);\n\n//         __nv_bfloat162 bias_val_1 = bias_ptr[(id * 2) % (param.n / 2)];\n//         __nv_bfloat162 bias_val_2 = bias_ptr[(id * 2 + 1) % (param.n / 2)];\n\n//         val_1 = hmul2(val_1, input_scale_2);\n//         val_2 = hmul2(val_2, input_scale_2);\n//         val_1 = hadd2(val_1, bias_val_1);\n//         val_2 = hadd2(val_2, bias_val_2);\n//         val_1.x = val_1.x > (__nv_bfloat16)0.0f ? val_1.x : (__nv_bfloat16)0.0f;\n//         val_1.y = val_1.y > (__nv_bfloat16)0.0f ? val_1.y : (__nv_bfloat16)0.0f;\n//         val_2.x = val_2.x > (__nv_bfloat16)0.0f ? val_2.x : (__nv_bfloat16)0.0f;\n//         val_2.y = val_2.y > (__nv_bfloat16)0.0f ? val_2.y : (__nv_bfloat16)0.0f;\n//         val_1 = hmul2(val_1, output_scale_2);\n//         val_2 = hmul2(val_2, output_scale_2);\n\n//         out_ptr[id] = __nv_fp8x4_e4m3(val_1, val_2);\n//     }\n// }\n\n// NOTE: input is bfloat\ntemplate<>\n__global__ void FP8AddBiasRelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n{\n    float            input_scale  = __ldg(param.input_scale);\n    float            output_scale = __ldg(param.output_scale);\n    __nv_fp8x2_e4m3* out_ptr      = (__nv_fp8x2_e4m3*)param.out;\n    __nv_bfloat162*  in_ptr       = (__nv_bfloat162*)param.in;\n    __nv_bfloat162*  bias_ptr     = (__nv_bfloat162*)param.bias;\n\n    __nv_bfloat162 input_scale_2  = __floats2bfloat162_rn(input_scale, input_scale);\n    __nv_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 2); id += blockDim.x * gridDim.x) {\n        __nv_bfloat162 val  = in_ptr[id];\n        __nv_bfloat162 bias = bias_ptr[id % (param.n / 2)];\n        val                 = hadd2(val, bias);\n        val.x               = val.x > (__nv_bfloat16)0.0f ? val.x : (__nv_bfloat16)0.0f;\n        val.y               = val.y > (__nv_bfloat16)0.0f ? val.y : (__nv_bfloat16)0.0f;\n        val                 = hmul2(val, output_scale_2);\n\n        out_ptr[id] = __nv_fp8x2_e4m3(val);\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddBiasRelu(FP8ActivationParam<T1, T2> param)\n{\n    FT_CHECK(param.n % 2 == 0);\n    // NOTE: input data type is bfloat now\n    const int data_type_factor = 2;  // 1 for fp32, 2 for fp16/bf16, 4 for fp86\n    dim3      block, grid;\n    if (param.n / 4 / data_type_factor <= 1024) {\n        block.x = param.n / 4 / data_type_factor;\n        grid.x  = param.m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(param.m * param.n / 1024.);\n    }\n    FP8AddBiasRelu<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeFP8AddBiasRelu<__nv_fp8_e4m3, __nv_bfloat16>(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param);\n\n// template<typename T1, typename T2>\n// void invokeFP8AddBias(FP8ActivationParam<T1, T2> param) {\n//     // TODO FP8\n// }\n\n// template void\n// invokeFP8AddBias<__nv_fp8_e4m3, half>(FP8ActivationParam<__nv_fp8_e4m3, half> param);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/activation_fp8_kernels.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__forceinline__ __device__ float copysignf_pos(float a, float b)\n{\n    float r;\n    r = __int_as_float(__float_as_int(a) | (__float_as_int(b) & 0x80000000));\n    return r;\n}\n\n__inline__ __device__ float tanh_opt(float x)\n{\n    return tanh(x);\n#if (__CUDA_ARCH__ >= 750)\n    float r;\n    asm(\"tanh.approx.f32 %0,%1; \\n\\t\" : \"=f\"(r) : \"f\"(x));\n    return r;\n#else\n    const float exp_val = -1.f * fabs(2 * x);\n    return copysignf_pos((1.0f - __expf(exp_val)) / (__expf(exp_val) + 1.0f), x);\n#endif\n}\n\ntemplate<typename T>\n__inline__ __device__ T gelu(T x)\n{\n    float f_x = (float)(x);\n    float cdf = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (f_x + 0.044715f * f_x * f_x * f_x))));\n    return (T)(f_x * cdf);\n}\n\ntemplate<>\n__inline__ __device__ __hip_bfloat162 gelu(__hip_bfloat162 val)\n{\n    __hip_bfloat162 val_pow3 = __hmul2(val, __hmul2(val, val));\n    float2         tmp_pow  = cuda_cast<float2>(val_pow3);\n    float2         tmp      = cuda_cast<float2>(val);\n\n    tmp.x = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n    tmp.y = 0.5f * (1.0f + tanh_opt((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n    return __hmul2(val, __float22bfloat162_rn(tmp));\n}\n\ntemplate<typename T1, typename T2>\n__global__ void FP8AddBiasGelu(FP8ActivationParam<T1, T2> param)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * param.n; id += blockDim.x * gridDim.x) {\n        T2 val = (T2)((float)(param.out[id]) * __ldg(param.input_scale));\n        // TODO(bhsueh) check do we need it\n        // if (param.input_scale_2 != nullptr) {\n        //     val = (float)val * __ldg(param.input_scale_2 + (id % param.n)) / __ldg(param.input_scale_2_min);\n        // }\n        if (param.bias != nullptr) {\n            T2 reg_bias = __ldg(&param.bias[id % param.n]);\n            val         = val + reg_bias;\n        }\n        param.out[id] = (T1)((float)gelu(val) * __ldg(param.output_scale));\n    }\n}\n\n// template<>\n// __global__ void FP8AddBiasGelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n// {\n//     float input_scale = __ldg(param.input_scale);\n//     float output_scale = __ldg(param.output_scale);\n//     __nv_fp8x4_e4m3* out_ptr = (__nv_fp8x4_e4m3*)param.out;\n//     __nv_bfloat162* bias_ptr = (__nv_bfloat162*)param.bias;\n\n//     __nv_bfloat162 input_scale_2 = __floats2bfloat162_rn(input_scale, input_scale);\n//     __nv_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 4); id += blockDim.x * gridDim.x)\n//     {\n//         __nv_bfloat162 val_1;\n//         __nv_bfloat162 val_2;\n\n//         fp8x4_e4m3_to_bfloat2(&val_1, &val_2, &out_ptr[id]);\n\n//         __nv_bfloat162 bias_val_1 = bias_ptr[(id * 2) % (param.n / 2)];\n//         __nv_bfloat162 bias_val_2 = bias_ptr[(id * 2 + 1) % (param.n / 2)];\n\n//         val_1 = hmul2(val_1, input_scale_2);\n//         val_2 = hmul2(val_2, input_scale_2);\n//         val_1 = gelu(hadd2(val_1, bias_val_1));\n//         val_2 = gelu(hadd2(val_2, bias_val_2));\n//         val_1 = hmul2(val_1, output_scale_2);\n//         val_2 = hmul2(val_2, output_scale_2);\n\n//         out_ptr[id] = __nv_fp8x4_e4m3(val_1, val_2);\n//     }\n// }\n\n// NOTE: input is bfloat (pack8 to have 128 bit input, and 64 bit output)\ntemplate<>\n__global__ void FP8AddBiasGelu(FP8ActivationParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param)\n{\n    float         input_scale     = __ldg(param.input_scale);\n    float         output_scale    = __ldg(param.output_scale);\n    constexpr int Packed_Elements = 8;\n    using packed_out              = __nv_fp8_8_e4m3;\n    using packed_bias             = __nv_bfloat168;\n    using packed_in               = __nv_bfloat168;\n    packed_out*  out_ptr          = (packed_out*)param.out;\n    packed_in*   in_ptr           = (packed_in*)param.in;\n    packed_bias* bias_ptr         = (packed_bias*)param.bias;\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / Packed_Elements);\n         id += blockDim.x * gridDim.x) {\n        packed_in   val  = in_ptr[id];\n        packed_bias bias = bias_ptr[id % (param.n / Packed_Elements)];\n        packed_out  val_output;\n#pragma unroll\n        for (int i = 0; i < Packed_Elements; i++) {\n            val_output.array[i] = __hip_fp8_e4m3_fnuz(gelu((float)val.array[i] + (float)bias.array[i]) * output_scale);\n        }\n        // f_val.x = gelu(f_val.x + f_bias.x) * output_scale;\n        // f_val.y = gelu(f_val.y + f_bias.x) * output_scale;\n        // val = hmul2(gelu(hadd2(val, bias)), output_scale_2);\n        // val = hmul2(gelu(hadd2(val, bias)), output_scale_2);\n\n        out_ptr[id] = val_output;\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddBiasGelu(FP8ActivationParam<T1, T2> param)\n{\n    FT_CHECK(param.n % 8 == 0);\n    // NOTE: input data type is bfloat now\n    const int data_type_factor = 8;  // pack 8 elements for bfloat16 input\n    const int total_packs      = (param.n * param.m) / data_type_factor;\n    dim3      block(256);\n    int       num_packs_per_thread = 1;\n    while ((total_packs + (block.x * num_packs_per_thread) - 1) / (block.x * num_packs_per_thread) > 512) {\n        num_packs_per_thread *= 2;\n    }\n    dim3 grid((total_packs + (block.x * num_packs_per_thread) - 1) / (block.x * num_packs_per_thread));\n    FP8AddBiasGelu<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeFP8AddBiasGelu<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(FP8ActivationParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\ntemplate<typename T1, typename T2>\n__global__ void FP8AddBiasRelu(FP8ActivationParam<T1, T2> param)\n{\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * param.n; id += blockDim.x * gridDim.x) {\n        T2 val = (T2)((float)(param.out[id]) * __ldg(param.input_scale));\n        // TODO(bhsueh) check do we need it\n        // if (param.input_scale_2 != nullptr) {\n        //     val = (float)val * __ldg(param.input_scale_2 + (id % param.n)) / __ldg(param.input_scale_2_min);\n        // }\n        if (param.bias != nullptr) {\n            T2 reg_bias = __ldg(&param.bias[id % param.n]);\n            val         = val + reg_bias;\n        }\n        val           = (T2)(((float)val) * __ldg(param.output_scale));\n        param.out[id] = (T1)((val > (T2)0.0f) ? val : (T2)0.0f);\n    }\n}\n\n// template<>\n// __global__ void FP8AddBiasRelu(FP8ActivationParam<__nv_fp8_e4m3, __nv_bfloat16> param)\n// {\n//     float input_scale = __ldg(param.input_scale);\n//     float output_scale = __ldg(param.output_scale);\n//     __nv_fp8x4_e4m3* out_ptr = (__nv_fp8x4_e4m3*)param.out;\n//     __nv_bfloat162* bias_ptr = (__nv_bfloat162*)param.bias;\n\n//     __nv_bfloat162 input_scale_2 = __floats2bfloat162_rn(input_scale, input_scale);\n//     __nv_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n//     for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 4); id += blockDim.x * gridDim.x)\n//     {\n//         __nv_bfloat162 val_1;\n//         __nv_bfloat162 val_2;\n\n//         fp8x4_e4m3_to_bfloat2(&val_1, &val_2, &out_ptr[id]);\n\n//         __nv_bfloat162 bias_val_1 = bias_ptr[(id * 2) % (param.n / 2)];\n//         __nv_bfloat162 bias_val_2 = bias_ptr[(id * 2 + 1) % (param.n / 2)];\n\n//         val_1 = hmul2(val_1, input_scale_2);\n//         val_2 = hmul2(val_2, input_scale_2);\n//         val_1 = hadd2(val_1, bias_val_1);\n//         val_2 = hadd2(val_2, bias_val_2);\n//         val_1.x = val_1.x > (__nv_bfloat16)0.0f ? val_1.x : (__nv_bfloat16)0.0f;\n//         val_1.y = val_1.y > (__nv_bfloat16)0.0f ? val_1.y : (__nv_bfloat16)0.0f;\n//         val_2.x = val_2.x > (__nv_bfloat16)0.0f ? val_2.x : (__nv_bfloat16)0.0f;\n//         val_2.y = val_2.y > (__nv_bfloat16)0.0f ? val_2.y : (__nv_bfloat16)0.0f;\n//         val_1 = hmul2(val_1, output_scale_2);\n//         val_2 = hmul2(val_2, output_scale_2);\n\n//         out_ptr[id] = __nv_fp8x4_e4m3(val_1, val_2);\n//     }\n// }\n\n// NOTE: input is bfloat\ntemplate<>\n__global__ void FP8AddBiasRelu(FP8ActivationParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param)\n{\n    float            input_scale  = __ldg(param.input_scale);\n    float            output_scale = __ldg(param.output_scale);\n    __hip_fp8x2_e4m3_fnuz* out_ptr      = (__hip_fp8x2_e4m3_fnuz*)param.out;\n    __hip_bfloat162*  in_ptr       = (__hip_bfloat162*)param.in;\n    __hip_bfloat162*  bias_ptr     = (__hip_bfloat162*)param.bias;\n\n    __hip_bfloat162 input_scale_2  = __floats2bfloat162_rn(input_scale, input_scale);\n    __hip_bfloat162 output_scale_2 = __floats2bfloat162_rn(output_scale, output_scale);\n\n    for (int id = blockIdx.x * blockDim.x + threadIdx.x; id < param.m * (param.n / 2); id += blockDim.x * gridDim.x) {\n        __hip_bfloat162 val  = in_ptr[id];\n        __hip_bfloat162 bias = bias_ptr[id % (param.n / 2)];\n        val                 = hadd2(val, bias);\n        val.x               = val.x > (__hip_bfloat16)0.0f ? val.x : (__hip_bfloat16)0.0f;\n        val.y               = val.y > (__hip_bfloat16)0.0f ? val.y : (__hip_bfloat16)0.0f;\n        val                 = hmul2(val, output_scale_2);\n\n        out_ptr[id] = __hip_fp8x2_e4m3_fnuz(val);\n    }\n}\n\ntemplate<typename T1, typename T2>\nvoid invokeFP8AddBiasRelu(FP8ActivationParam<T1, T2> param)\n{\n    FT_CHECK(param.n % 2 == 0);\n    // NOTE: input data type is bfloat now\n    const int data_type_factor = 2;  // 1 for fp32, 2 for fp16/bf16, 4 for fp86\n    dim3      block, grid;\n    if (param.n / 4 / data_type_factor <= 1024) {\n        block.x = param.n / 4 / data_type_factor;\n        grid.x  = param.m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = ceil(param.m * param.n / 1024.);\n    }\n    FP8AddBiasRelu<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeFP8AddBiasRelu<__hip_fp8_e4m3_fnuz, __hip_bfloat16>(FP8ActivationParam<__hip_fp8_e4m3_fnuz, __hip_bfloat16> param);\n\n// template<typename T1, typename T2>\n// void invokeFP8AddBias(FP8ActivationParam<T1, T2> param) {\n//     // TODO FP8\n// }\n\n// template void\n// invokeFP8AddBias<__nv_fp8_e4m3, half>(FP8ActivationParam<__nv_fp8_e4m3, half> param);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/activation_fp8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cmath>\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"longformer_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\n#include <stdio.h>\n\nnamespace fastertransformer {\n\n// when sm < 80, cub device partition flagged bf16 not supported\n// error: more than one operator \"=\" matches these operands: __nv_bfloat16::operator=(float),\n// __nv_bfloat16::operator=(double)\ntemplate<typename T>\nstruct CubBF16FallBackType {\n    using Type = T;\n};\n\n#ifdef ENABLE_BF16\n\n// global attn mask can be converted to uint16_t\ntemplate<>\nstruct CubBF16FallBackType<__nv_bfloat16> {\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800\n    using Type = uint16_t;\n#else\n    using Type = __nv_bfloat16;\n#endif\n};\n\n#endif  // ENABLE_BF16\n\n__global__ void initSeqIdxKernel(int* seq_idx, int seq_len)\n{\n    int offset = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = offset; i < seq_len; i += blockDim.x) {\n        seq_idx[i] = i;\n    }\n}\n\ntemplate<typename T>\nsize_t getInitLongformerCubStorage(const int seq_len)\n{\n    size_t tmp_storage_bytes = 0;\n    int *  seq_idx = NULL, *global_idx = NULL, *global_token_nums = NULL;\n    void*  global_attn_mask = NULL;\n\n    using AttenMaskFBType = typename CubBF16FallBackType<T>::Type;\n\n    check_cuda_error(cub::DevicePartition::Flagged(\n        NULL, tmp_storage_bytes, seq_idx, (AttenMaskFBType*)global_attn_mask, global_idx, global_token_nums, seq_len));\n\n    return tmp_storage_bytes;\n}\n\ntemplate<typename T>\n__global__ void localAttnMaskShiftKernel(T* local_attn_mask, T* out, int thread_block_repeat, int total_len)\n{\n    int i   = blockIdx.x * blockDim.x * thread_block_repeat + threadIdx.x;\n    int end = i + thread_block_repeat * blockDim.x;\n    for (; i < end && i < total_len; i += blockDim.x) {\n        out[i] = fma(local_attn_mask[i], (T)10000.f, (T)(-10000.f));\n    }\n}\n\ntemplate<typename T>\nvoid invokeLocalAttnMaskShift(T* local_attn_mask, T* out, int batch_size, int seq_len, cudaStream_t stream)\n{\n    const int thread_block_repeat = 4;\n    const int block_dim           = 128;\n    int       block_num           = std::ceil(batch_size * seq_len / (float)block_dim / (float)thread_block_repeat);\n    localAttnMaskShiftKernel<<<block_num, block_dim, 0, stream>>>(\n        local_attn_mask, out, thread_block_repeat, batch_size * seq_len);\n}\n\ntemplate void\ninvokeLocalAttnMaskShift(float* local_attn_mask, float* out, int batch_size, int seq_len, cudaStream_t stream);\n\ntemplate void\ninvokeLocalAttnMaskShift(half* local_attn_mask, half* out, int batch_size, int seq_len, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLocalAttnMaskShift(\n    __nv_bfloat16* local_attn_mask, __nv_bfloat16* out, int batch_size, int seq_len, cudaStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid invokeInitLongformerIdx(T*           global_attn_mask,\n                             int*         seq_idx,\n                             int*         global_idx,\n                             int*         global_token_nums,\n                             int          seq_len,\n                             int          batch_size,\n                             void*        cub_storage,\n                             cudaStream_t stream)\n{\n    const int threads = 1024;\n    int       blocks  = std::ceil(seq_len / float(threads));\n    initSeqIdxKernel<<<blocks, threads, 0, stream>>>(seq_idx, seq_len);\n    sync_check_cuda_error();\n\n    size_t storages_bytes = getInitLongformerCubStorage<T>(seq_len);\n    using AttenMaskFBType = typename CubBF16FallBackType<T>::Type;\n    for (int i = 0; i < batch_size; ++i) {\n        check_cuda_error(cub::DevicePartition::Flagged(cub_storage,\n                                                       storages_bytes,\n                                                       seq_idx,\n                                                       (AttenMaskFBType*)(global_attn_mask + i * seq_len),\n                                                       global_idx + i * seq_len,\n                                                       global_token_nums + i,\n                                                       seq_len,\n                                                       stream));\n    }\n}\n\ntemplate void invokeInitLongformerIdx(float*       global_attn_mask,\n                                      int*         seq_idx,\n                                      int*         global_idx,\n                                      int*         global_token_nums,\n                                      int          seq_len,\n                                      int          batch_size,\n                                      void*        cub_storage,\n                                      cudaStream_t stream);\n\ntemplate void invokeInitLongformerIdx(half*        global_attn_mask,\n                                      int*         seq_idx,\n                                      int*         global_idx,\n                                      int*         global_token_nums,\n                                      int          seq_len,\n                                      int          batch_size,\n                                      void*        cub_storage,\n                                      cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInitLongformerIdx(__nv_bfloat16* global_attn_mask,\n                                      int*           seq_idx,\n                                      int*           global_idx,\n                                      int*           global_token_nums,\n                                      int            seq_len,\n                                      int            batch_size,\n                                      void*          cub_storage,\n                                      cudaStream_t   stream);\n#endif\n\n// Apply softmax to local and global attention. Rewrite the result to the same buffer in-place\ntemplate<typename T, int blockSize>\n__launch_bounds__(blockSize) __global__ void longformerMHASoftmaxKernel(const T*   global_attn,\n                                                                        const int* global_idx,\n                                                                        const int* global_token_nums,\n                                                                        void*      input_ptrs,\n                                                                        const T*   attn_mask,\n                                                                        float      scaler,\n                                                                        int        seq_len,\n                                                                        int        head_num,\n                                                                        int        attn_window_size)\n{\n    typedef cub::BlockReduce<float, blockSize>   BlockReduce;\n    __shared__ typename BlockReduce::TempStorage breduce_temp;\n\n    size_t* p_inputs = (size_t*)(input_ptrs);\n    // use input buffer as output buffer\n    size_t* p_outputs     = (size_t*)(input_ptrs);\n    size_t* input_sizes   = (size_t*)(input_ptrs) + 5;\n    size_t* input_strides = (size_t*)(input_ptrs) + 10;\n\n    int       tid       = threadIdx.x;\n    const int batch_idx = blockIdx.x / (seq_len * head_num);\n    const int row_idx   = blockIdx.x % seq_len;\n    const int head_idx  = (blockIdx.x / seq_len) % head_num;\n\n    // adjust the pointers for the batch\n    const T*   mask_blk       = attn_mask + seq_len * batch_idx;\n    const int  global_num     = global_token_nums[batch_idx];\n    const int* global_idx_blk = global_idx + seq_len * batch_idx;\n\n    T* inputs[5];\n    T* outputs[5];\n    for (int i = 0; i < 5; ++i) {\n        inputs[i]  = (T*)p_inputs[i] + batch_idx * head_num * input_sizes[i];\n        outputs[i] = (T*)p_outputs[i] + batch_idx * head_num * input_sizes[i];\n    }\n\n    int col_start = 0;\n    int col_end   = seq_len;\n\n    // is it local attention token\n    int is_local_row = global_attn[row_idx + seq_len * batch_idx] == (T)0.f;\n\n    // if local token\n    if (is_local_row) {\n        col_start = row_idx - attn_window_size;\n        col_end   = row_idx + attn_window_size + 1;\n    }\n\n    if (col_start < 0) {\n        col_start = 0;\n    }\n    if (col_end > seq_len) {\n        col_end = seq_len;\n    }\n\n    // if mask is set then set everything to zero to match Python implementation\n    if (mask_blk[row_idx] != (T)0.f) {\n        if (is_local_row) {\n            T*  output_blk   = nullptr;\n            T*  output_glb   = nullptr;\n            int local_offset = row_idx % attn_window_size;\n            int local_start  = 0;\n            int local_end    = 3 * attn_window_size;\n            if (row_idx < attn_window_size) {\n                local_start = 0;\n                local_end   = 2 * attn_window_size;\n                output_blk  = outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n            }\n            else if (row_idx < seq_len - attn_window_size) {\n                output_blk = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n            }\n            else {\n                local_start = 0;\n                local_end   = 2 * attn_window_size;\n                output_blk  = outputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n            }\n\n            for (int i = local_start + tid; i < local_end; i += blockSize) {\n                output_blk[i] = (T)0.f;\n            }\n\n            if ((row_idx - 2 * attn_window_size) >= 0) {\n                output_glb = outputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n            }\n\n            if (output_glb != nullptr) {\n                for (int i = tid; i < global_num; i += blockSize) {\n                    output_glb[i] = (T)0.f;\n                }\n            }\n        }\n        else {\n            T* output_blk = outputs[4];\n            for (int i = tid; i < seq_len; i += blockSize) {\n                output_blk[i] = (T)0.f;\n            }\n        }\n        return;\n    }\n\n    __shared__ float sum_shared;\n    float            sum_input = 0.;\n    // calculate max input\n    float            max_input = -FLT_MAX;\n    __shared__ float max_shared;\n\n    if (is_local_row) {\n        const T* input_blk    = nullptr;\n        T*       output_blk   = nullptr;\n        T*       output_glb   = nullptr;\n        int      local_offset = row_idx % attn_window_size;\n        int      local_start  = local_offset;\n        int      local_end    = local_start + 2 * attn_window_size + 1;\n        int      zero_start   = 0;\n        int      zero_end     = 3 * attn_window_size;\n        if (row_idx < attn_window_size) {\n            local_start = 0;\n            local_end   = local_offset + attn_window_size + 1;\n            zero_end    = 2 * attn_window_size;\n\n            input_blk  = inputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n            output_blk = outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n        }\n        else if (row_idx < seq_len - attn_window_size) {\n            input_blk  = inputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n            output_blk = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n        }\n        else {\n            local_start = local_offset;\n            local_end   = 2 * attn_window_size;\n            zero_end    = 2 * attn_window_size;\n\n            input_blk  = inputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n            output_blk = outputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n        }\n\n        const T* input_glb    = nullptr;\n        int      local_global = row_idx - attn_window_size;\n        if (local_global > global_num) {\n            local_global = global_num;\n        }\n        if (local_global > 0) {\n            input_glb = inputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n        }\n\n        if (row_idx < attn_window_size) {\n            output_glb = (T*)outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n        }\n        else if (row_idx < 2 * attn_window_size) {\n            output_glb = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n        }\n        else {\n            output_glb = outputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n        }\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x = (float)input_blk[i];\n            x       = x * scaler + (float)mask_blk[ii];\n            if (max_input < x) {\n                max_input = x;\n            }\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid; i < local_global; i += blockSize) {\n                float x = (float)input_glb[global_idx_blk[i]];\n                x       = x * scaler + (float)mask_blk[global_idx_blk[i]];\n                if (max_input < x) {\n                    max_input = x;\n                }\n            }\n        }\n\n        float max_blk = BlockReduce(breduce_temp).Reduce(max_input, cub::Max());\n        if (tid == 0) {\n            max_shared = max_blk;\n        }\n        __syncthreads();\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x = (float)input_blk[i];\n            x       = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n            sum_input += x;\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid, ii = col_start + tid; i < local_global; i += blockSize, ii += blockSize) {\n                float x = (float)input_glb[global_idx_blk[i]];\n                x       = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n                sum_input += x;\n            }\n        }\n\n        float sum_blk = BlockReduce(breduce_temp).Reduce(sum_input, cub::Sum());\n        if (tid == 0) {\n            sum_shared = sum_blk;\n        }\n        __syncthreads();\n        float recip_sum = 1.f / sum_shared;\n\n        for (int i = tid + zero_start; i < local_start; i += blockSize) {\n            output_blk[i] = (T)(0.);\n        }\n\n        for (int i = tid + local_end; i < zero_end; i += blockSize) {\n            output_blk[i] = (T)(0.);\n        }\n\n        __syncthreads();\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x       = (float)input_blk[i];\n            x             = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n            output_blk[i] = (T)(recip_sum * x);\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid; i < local_global; i += blockSize) {\n                float x       = (float)input_glb[global_idx_blk[i]];\n                x             = expf(x * scaler + (float)mask_blk[global_idx_blk[i]] - max_shared);\n                output_glb[i] = (T)(recip_sum * x);\n            }\n        }\n    }\n    else {\n        // global tokens\n        const T* input_blk  = inputs[4] + row_idx * input_strides[4] + head_idx * input_sizes[4];\n        T*       output_blk = outputs[4] + row_idx * input_strides[4] + head_idx * input_sizes[4];\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x = (float)input_blk[i];\n            x       = x * scaler + (float)mask_blk[i];\n            if (max_input < x) {\n                max_input = x;\n            }\n        }\n\n        float max_blk = BlockReduce(breduce_temp).Reduce(max_input, cub::Max());\n        if (tid == 0) {\n            max_shared = max_blk;\n        }\n        __syncthreads();\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x = (float)input_blk[i];\n            x       = expf(x * scaler + (float)mask_blk[i] - max_shared);\n            sum_input += x;\n        }\n\n        float sum_blk = BlockReduce(breduce_temp).Reduce(sum_input, cub::Sum());\n        if (tid == 0) {\n            sum_shared = sum_blk;\n        }\n        __syncthreads();\n        float recip_sum = 1.f / sum_shared;\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x       = (float)input_blk[i];\n            x             = expf(x * scaler + (float)mask_blk[i] - max_shared);\n            output_blk[i] = (T)(recip_sum * x);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLongformerMHASoftmax(const T*     global_attn_mask,\n                                const int*   global_idx,\n                                const int*   global_token_nums,\n                                void*        input_ptrs,\n                                const T*     local_attn_mask,\n                                float        scaler,\n                                int          seq_len,\n                                int          head_num,\n                                int          batch_size,\n                                int          local_attn_window_size,\n                                cudaStream_t stream)\n{\n    const int block_size = 64;\n    const int grid_size  = seq_len * head_num * batch_size;\n    longformerMHASoftmaxKernel<T, block_size><<<grid_size, block_size, 0, stream>>>(global_attn_mask,\n                                                                                    global_idx,\n                                                                                    global_token_nums,\n                                                                                    input_ptrs,\n                                                                                    local_attn_mask,\n                                                                                    scaler,\n                                                                                    seq_len,\n                                                                                    head_num,\n                                                                                    local_attn_window_size);\n}\n\ntemplate void invokeLongformerMHASoftmax(const float* global_attn_mask,\n                                         const int*   global_idx,\n                                         const int*   global_token_nums,\n                                         void*        input_ptrs,\n                                         const float* local_attn_mask,\n                                         float        scaler,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          batch_size,\n                                         int          local_attn_window_size,\n                                         cudaStream_t stream);\n\ntemplate void invokeLongformerMHASoftmax(const half*  global_attn_mask,\n                                         const int*   global_idx,\n                                         const int*   global_token_nums,\n                                         void*        input_ptrs,\n                                         const half*  local_attn_mask,\n                                         float        scaler,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          batch_size,\n                                         int          local_attn_window_size,\n                                         cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLongformerMHASoftmax(const __nv_bfloat16* global_attn_mask,\n                                         const int*           global_idx,\n                                         const int*           global_token_nums,\n                                         void*                input_ptrs,\n                                         const __nv_bfloat16* local_attn_mask,\n                                         float                scaler,\n                                         int                  seq_len,\n                                         int                  head_num,\n                                         int                  batch_size,\n                                         int                  local_attn_window_size,\n                                         cudaStream_t         stream);\n#endif\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"hip/hip_runtime.h\"\n#include <cmath>\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"longformer_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\n#include <stdio.h>\n\nnamespace fastertransformer {\n\n// when sm < 80, cub device partition flagged bf16 not supported\n// error: more than one operator \"=\" matches these operands: __nv_bfloat16::operator=(float),\n// __nv_bfloat16::operator=(double)\ntemplate<typename T>\nstruct CubBF16FallBackType {\n    using Type = T;\n};\n\n#ifdef ENABLE_BF16\n\n// global attn mask can be converted to uint16_t\ntemplate<>\nstruct CubBF16FallBackType<__hip_bfloat16> {\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 800\n    using Type = uint16_t;\n#else\n    using Type = __hip_bfloat16;\n#endif\n};\n\n#endif  // ENABLE_BF16\n\n__global__ void initSeqIdxKernel(int* seq_idx, int seq_len)\n{\n    int offset = blockIdx.x * blockDim.x + threadIdx.x;\n    for (int i = offset; i < seq_len; i += blockDim.x) {\n        seq_idx[i] = i;\n    }\n}\n\ntemplate<typename T>\nsize_t getInitLongformerCubStorage(const int seq_len)\n{\n    size_t tmp_storage_bytes = 0;\n    int *  seq_idx = NULL, *global_idx = NULL, *global_token_nums = NULL;\n    void*  global_attn_mask = NULL;\n\n    using AttenMaskFBType = typename CubBF16FallBackType<T>::Type;\n\n    check_cuda_error(hipcub::DevicePartition::Flagged(\n        NULL, tmp_storage_bytes, seq_idx, (AttenMaskFBType*)global_attn_mask, global_idx, global_token_nums, seq_len));\n\n    return tmp_storage_bytes;\n}\n\ntemplate<typename T>\n__global__ void localAttnMaskShiftKernel(T* local_attn_mask, T* out, int thread_block_repeat, int total_len)\n{\n    int i   = blockIdx.x * blockDim.x * thread_block_repeat + threadIdx.x;\n    int end = i + thread_block_repeat * blockDim.x;\n    for (; i < end && i < total_len; i += blockDim.x) {\n        out[i] = fma(local_attn_mask[i], (T)10000.f, (T)(-10000.f));\n    }\n}\n\ntemplate<typename T>\nvoid invokeLocalAttnMaskShift(T* local_attn_mask, T* out, int batch_size, int seq_len, hipStream_t stream)\n{\n    const int thread_block_repeat = 4;\n    const int block_dim           = 128;\n    int       block_num           = std::ceil(batch_size * seq_len / (float)block_dim / (float)thread_block_repeat);\n    localAttnMaskShiftKernel<<<block_num, block_dim, 0, stream>>>(\n        local_attn_mask, out, thread_block_repeat, batch_size * seq_len);\n}\n\ntemplate void\ninvokeLocalAttnMaskShift(float* local_attn_mask, float* out, int batch_size, int seq_len, hipStream_t stream);\n\ntemplate void\ninvokeLocalAttnMaskShift(half* local_attn_mask, half* out, int batch_size, int seq_len, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLocalAttnMaskShift(\n    __hip_bfloat16* local_attn_mask, __hip_bfloat16* out, int batch_size, int seq_len, hipStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid invokeInitLongformerIdx(T*           global_attn_mask,\n                             int*         seq_idx,\n                             int*         global_idx,\n                             int*         global_token_nums,\n                             int          seq_len,\n                             int          batch_size,\n                             void*        cub_storage,\n                             hipStream_t stream)\n{\n    const int threads = 1024;\n    int       blocks  = std::ceil(seq_len / float(threads));\n    initSeqIdxKernel<<<blocks, threads, 0, stream>>>(seq_idx, seq_len);\n    sync_check_cuda_error();\n\n    size_t storages_bytes = getInitLongformerCubStorage<T>(seq_len);\n    using AttenMaskFBType = typename CubBF16FallBackType<T>::Type;\n    for (int i = 0; i < batch_size; ++i) {\n        check_cuda_error(hipcub::DevicePartition::Flagged(cub_storage,\n                                                       storages_bytes,\n                                                       seq_idx,\n                                                       (AttenMaskFBType*)(global_attn_mask + i * seq_len),\n                                                       global_idx + i * seq_len,\n                                                       global_token_nums + i,\n                                                       seq_len,\n                                                       stream));\n    }\n}\n\ntemplate void invokeInitLongformerIdx(float*       global_attn_mask,\n                                      int*         seq_idx,\n                                      int*         global_idx,\n                                      int*         global_token_nums,\n                                      int          seq_len,\n                                      int          batch_size,\n                                      void*        cub_storage,\n                                      hipStream_t stream);\n\ntemplate void invokeInitLongformerIdx(half*        global_attn_mask,\n                                      int*         seq_idx,\n                                      int*         global_idx,\n                                      int*         global_token_nums,\n                                      int          seq_len,\n                                      int          batch_size,\n                                      void*        cub_storage,\n                                      hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInitLongformerIdx(__hip_bfloat16* global_attn_mask,\n                                      int*           seq_idx,\n                                      int*           global_idx,\n                                      int*           global_token_nums,\n                                      int            seq_len,\n                                      int            batch_size,\n                                      void*          cub_storage,\n                                      hipStream_t   stream);\n#endif\n\n// Apply softmax to local and global attention. Rewrite the result to the same buffer in-place\ntemplate<typename T, int blockSize>\n__launch_bounds__(blockSize) __global__ void longformerMHASoftmaxKernel(const T*   global_attn,\n                                                                        const int* global_idx,\n                                                                        const int* global_token_nums,\n                                                                        void*      input_ptrs,\n                                                                        const T*   attn_mask,\n                                                                        float      scaler,\n                                                                        int        seq_len,\n                                                                        int        head_num,\n                                                                        int        attn_window_size)\n{\n    typedef hipcub::BlockReduce<float, blockSize>   BlockReduce;\n    __shared__ typename BlockReduce::TempStorage breduce_temp;\n\n    size_t* p_inputs = (size_t*)(input_ptrs);\n    // use input buffer as output buffer\n    size_t* p_outputs     = (size_t*)(input_ptrs);\n    size_t* input_sizes   = (size_t*)(input_ptrs) + 5;\n    size_t* input_strides = (size_t*)(input_ptrs) + 10;\n\n    int       tid       = threadIdx.x;\n    const int batch_idx = blockIdx.x / (seq_len * head_num);\n    const int row_idx   = blockIdx.x % seq_len;\n    const int head_idx  = (blockIdx.x / seq_len) % head_num;\n\n    // adjust the pointers for the batch\n    const T*   mask_blk       = attn_mask + seq_len * batch_idx;\n    const int  global_num     = global_token_nums[batch_idx];\n    const int* global_idx_blk = global_idx + seq_len * batch_idx;\n\n    T* inputs[5];\n    T* outputs[5];\n    for (int i = 0; i < 5; ++i) {\n        inputs[i]  = (T*)p_inputs[i] + batch_idx * head_num * input_sizes[i];\n        outputs[i] = (T*)p_outputs[i] + batch_idx * head_num * input_sizes[i];\n    }\n\n    int col_start = 0;\n    int col_end   = seq_len;\n\n    // is it local attention token\n    int is_local_row = global_attn[row_idx + seq_len * batch_idx] == (T)0.f;\n\n    // if local token\n    if (is_local_row) {\n        col_start = row_idx - attn_window_size;\n        col_end   = row_idx + attn_window_size + 1;\n    }\n\n    if (col_start < 0) {\n        col_start = 0;\n    }\n    if (col_end > seq_len) {\n        col_end = seq_len;\n    }\n\n    // if mask is set then set everything to zero to match Python implementation\n    if (mask_blk[row_idx] != (T)0.f) {\n        if (is_local_row) {\n            T*  output_blk   = nullptr;\n            T*  output_glb   = nullptr;\n            int local_offset = row_idx % attn_window_size;\n            int local_start  = 0;\n            int local_end    = 3 * attn_window_size;\n            if (row_idx < attn_window_size) {\n                local_start = 0;\n                local_end   = 2 * attn_window_size;\n                output_blk  = outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n            }\n            else if (row_idx < seq_len - attn_window_size) {\n                output_blk = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n            }\n            else {\n                local_start = 0;\n                local_end   = 2 * attn_window_size;\n                output_blk  = outputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n            }\n\n            for (int i = local_start + tid; i < local_end; i += blockSize) {\n                output_blk[i] = (T)0.f;\n            }\n\n            if ((row_idx - 2 * attn_window_size) >= 0) {\n                output_glb = outputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n            }\n\n            if (output_glb != nullptr) {\n                for (int i = tid; i < global_num; i += blockSize) {\n                    output_glb[i] = (T)0.f;\n                }\n            }\n        }\n        else {\n            T* output_blk = outputs[4];\n            for (int i = tid; i < seq_len; i += blockSize) {\n                output_blk[i] = (T)0.f;\n            }\n        }\n        return;\n    }\n\n    __shared__ float sum_shared;\n    float            sum_input = 0.;\n    // calculate max input\n    float            max_input = -FLT_MAX;\n    __shared__ float max_shared;\n\n    if (is_local_row) {\n        const T* input_blk    = nullptr;\n        T*       output_blk   = nullptr;\n        T*       output_glb   = nullptr;\n        int      local_offset = row_idx % attn_window_size;\n        int      local_start  = local_offset;\n        int      local_end    = local_start + 2 * attn_window_size + 1;\n        int      zero_start   = 0;\n        int      zero_end     = 3 * attn_window_size;\n        if (row_idx < attn_window_size) {\n            local_start = 0;\n            local_end   = local_offset + attn_window_size + 1;\n            zero_end    = 2 * attn_window_size;\n\n            input_blk  = inputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n            output_blk = outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n        }\n        else if (row_idx < seq_len - attn_window_size) {\n            input_blk  = inputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n            output_blk = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n        }\n        else {\n            local_start = local_offset;\n            local_end   = 2 * attn_window_size;\n            zero_end    = 2 * attn_window_size;\n\n            input_blk  = inputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n            output_blk = outputs[2] + local_offset * input_strides[2] + head_idx * input_sizes[2];\n        }\n\n        const T* input_glb    = nullptr;\n        int      local_global = row_idx - attn_window_size;\n        if (local_global > global_num) {\n            local_global = global_num;\n        }\n        if (local_global > 0) {\n            input_glb = inputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n        }\n\n        if (row_idx < attn_window_size) {\n            output_glb = (T*)outputs[0] + row_idx * input_strides[0] + head_idx * input_sizes[0];\n        }\n        else if (row_idx < 2 * attn_window_size) {\n            output_glb = outputs[1] + (row_idx - attn_window_size) * input_strides[1] + head_idx * input_sizes[1];\n        }\n        else {\n            output_glb = outputs[3] + (row_idx - attn_window_size) * input_strides[3] + head_idx * input_sizes[3];\n        }\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x = (float)input_blk[i];\n            x       = x * scaler + (float)mask_blk[ii];\n            if (max_input < x) {\n                max_input = x;\n            }\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid; i < local_global; i += blockSize) {\n                float x = (float)input_glb[global_idx_blk[i]];\n                x       = x * scaler + (float)mask_blk[global_idx_blk[i]];\n                if (max_input < x) {\n                    max_input = x;\n                }\n            }\n        }\n\n        float max_blk = BlockReduce(breduce_temp).Reduce(max_input, hipcub::Max());\n        if (tid == 0) {\n            max_shared = max_blk;\n        }\n        __syncthreads();\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x = (float)input_blk[i];\n            x       = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n            sum_input += x;\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid, ii = col_start + tid; i < local_global; i += blockSize, ii += blockSize) {\n                float x = (float)input_glb[global_idx_blk[i]];\n                x       = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n                sum_input += x;\n            }\n        }\n\n        float sum_blk = BlockReduce(breduce_temp).Reduce(sum_input, hipcub::Sum());\n        if (tid == 0) {\n            sum_shared = sum_blk;\n        }\n        __syncthreads();\n        float recip_sum = 1.f / sum_shared;\n\n        for (int i = tid + zero_start; i < local_start; i += blockSize) {\n            output_blk[i] = (T)(0.);\n        }\n\n        for (int i = tid + local_end; i < zero_end; i += blockSize) {\n            output_blk[i] = (T)(0.);\n        }\n\n        __syncthreads();\n\n        for (int i = local_start + tid, ii = col_start + tid; i < local_end; i += blockSize, ii += blockSize) {\n            float x       = (float)input_blk[i];\n            x             = expf(x * scaler + (float)mask_blk[ii] - max_shared);\n            output_blk[i] = (T)(recip_sum * x);\n        }\n\n        if (input_glb != nullptr) {\n            for (int i = tid; i < local_global; i += blockSize) {\n                float x       = (float)input_glb[global_idx_blk[i]];\n                x             = expf(x * scaler + (float)mask_blk[global_idx_blk[i]] - max_shared);\n                output_glb[i] = (T)(recip_sum * x);\n            }\n        }\n    }\n    else {\n        // global tokens\n        const T* input_blk  = inputs[4] + row_idx * input_strides[4] + head_idx * input_sizes[4];\n        T*       output_blk = outputs[4] + row_idx * input_strides[4] + head_idx * input_sizes[4];\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x = (float)input_blk[i];\n            x       = x * scaler + (float)mask_blk[i];\n            if (max_input < x) {\n                max_input = x;\n            }\n        }\n\n        float max_blk = BlockReduce(breduce_temp).Reduce(max_input, hipcub::Max());\n        if (tid == 0) {\n            max_shared = max_blk;\n        }\n        __syncthreads();\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x = (float)input_blk[i];\n            x       = expf(x * scaler + (float)mask_blk[i] - max_shared);\n            sum_input += x;\n        }\n\n        float sum_blk = BlockReduce(breduce_temp).Reduce(sum_input, hipcub::Sum());\n        if (tid == 0) {\n            sum_shared = sum_blk;\n        }\n        __syncthreads();\n        float recip_sum = 1.f / sum_shared;\n\n        for (int i = tid; i < seq_len; i += blockSize) {\n            float x       = (float)input_blk[i];\n            x             = expf(x * scaler + (float)mask_blk[i] - max_shared);\n            output_blk[i] = (T)(recip_sum * x);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLongformerMHASoftmax(const T*     global_attn_mask,\n                                const int*   global_idx,\n                                const int*   global_token_nums,\n                                void*        input_ptrs,\n                                const T*     local_attn_mask,\n                                float        scaler,\n                                int          seq_len,\n                                int          head_num,\n                                int          batch_size,\n                                int          local_attn_window_size,\n                                hipStream_t stream)\n{\n    const int block_size = 64;\n    const int grid_size  = seq_len * head_num * batch_size;\n    longformerMHASoftmaxKernel<T, block_size><<<grid_size, block_size, 0, stream>>>(global_attn_mask,\n                                                                                    global_idx,\n                                                                                    global_token_nums,\n                                                                                    input_ptrs,\n                                                                                    local_attn_mask,\n                                                                                    scaler,\n                                                                                    seq_len,\n                                                                                    head_num,\n                                                                                    local_attn_window_size);\n}\n\ntemplate void invokeLongformerMHASoftmax(const float* global_attn_mask,\n                                         const int*   global_idx,\n                                         const int*   global_token_nums,\n                                         void*        input_ptrs,\n                                         const float* local_attn_mask,\n                                         float        scaler,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          batch_size,\n                                         int          local_attn_window_size,\n                                         hipStream_t stream);\n\ntemplate void invokeLongformerMHASoftmax(const half*  global_attn_mask,\n                                         const int*   global_idx,\n                                         const int*   global_token_nums,\n                                         void*        input_ptrs,\n                                         const half*  local_attn_mask,\n                                         float        scaler,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          batch_size,\n                                         int          local_attn_window_size,\n                                         hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLongformerMHASoftmax(const __hip_bfloat16* global_attn_mask,\n                                         const int*           global_idx,\n                                         const int*           global_token_nums,\n                                         void*                input_ptrs,\n                                         const __hip_bfloat16* local_attn_mask,\n                                         float                scaler,\n                                         int                  seq_len,\n                                         int                  head_num,\n                                         int                  batch_size,\n                                         int                  local_attn_window_size,\n                                         hipStream_t         stream);\n#endif\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/longformer_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/stop_criteria_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\n__global__ void stop_words_criterion(const int* output_ids,\n                                     const int* parent_ids,\n                                     const int* stop_words,\n                                     bool*      finished,\n                                     size_t     id_offset,\n                                     size_t     stop_words_len,\n                                     int        batch_size,\n                                     int        beam_width,\n                                     int        step)\n{\n    const int id        = blockIdx.x * blockDim.x + threadIdx.x;\n    const int batch_idx = blockIdx.y / beam_width;\n    const int beam_idx  = blockIdx.y % beam_width;\n\n    const int* base_stop_words = stop_words + batch_idx * 2 * stop_words_len;\n    const int* base_offsets    = base_stop_words + stop_words_len;\n\n    if (id >= stop_words_len || base_offsets[id] < 0) {\n        return;\n    }\n\n    const int item_end   = base_offsets[id];\n    const int item_start = (id > 0) ? base_offsets[id - 1] : 0;\n    const int item_size  = item_end - item_start;\n\n    /* The single-token case unconditionally bans the token */\n    bool should_stop = false;\n\n    /* Enough previously generated tokens to look for a match */\n    if (step + 1 >= item_size) {\n        should_stop            = true;\n        int        parent_id   = beam_idx;\n        const bool gather_beam = beam_width > 1;\n\n        for (int token_idx = item_size - 1; token_idx >= 0; token_idx--) {\n            const int previous_token = output_ids[(step - (item_size - 1) + token_idx) * batch_size * beam_width\n                                                  + id_offset + batch_idx * beam_width + parent_id];\n\n            if (previous_token != base_stop_words[item_start + token_idx]) {\n                should_stop = false;\n                break;\n            }\n            if (gather_beam) {\n                parent_id = parent_ids[(step - (item_size - 1) + token_idx) * beam_width * batch_size + id_offset\n                                       + batch_idx * beam_width + parent_id];\n\n                if (parent_id < 0 || parent_id >= beam_width) {\n                    should_stop = false;\n                    break;\n                }\n            }\n        }\n    }\n\n    if (should_stop) {\n        finished[batch_idx * beam_width + beam_idx] = true;\n    }\n}\n\nvoid invokeStopWordsCriterion(const int*   output_ids,\n                              const int*   parent_ids,\n                              const int*   stop_words,\n                              bool*        finished,\n                              size_t       id_offset,\n                              size_t       stop_words_len,\n                              int          batch_size,\n                              int          beam_width,\n                              int          step,\n                              cudaStream_t stream)\n{\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    // Check if we have sampled a word from the stop_words list. If so, stop the sequence.\n    dim3 block, grid;\n    block.x = min(((stop_words_len + 32 - 1) / 32) * 32, 256UL);\n    grid.x  = (stop_words_len + block.x - 1) / block.x;\n    grid.y  = batch_size * beam_width;\n\n    stop_words_criterion<<<grid, block, 0, stream>>>(\n        output_ids, parent_ids, stop_words, finished, id_offset, stop_words_len, batch_size, beam_width, step);\n    sync_check_cuda_error();\n}\n\n__global__ void length_criterion(bool*           finished,\n                                 bool*           should_stop,\n                                 int*            finished_sum,\n                                 const uint32_t* sequence_limit_length,\n                                 int             batch_size,\n                                 int             beam_width,\n                                 int             step)\n{\n    int thread_finished_count = 0;\n    for (int index = threadIdx.x; index < batch_size * beam_width; index += blockDim.x) {\n        const int batch_idx = index / beam_width;\n\n        finished[index] |= step >= sequence_limit_length[batch_idx];\n        thread_finished_count += finished[index] ? 1 : 0;\n    }\n    int block_finished_count = 0;\n    if (blockDim.x <= 32) {\n        block_finished_count = warpReduceSum(thread_finished_count);\n    }\n    else {\n        block_finished_count = blockReduceSum(thread_finished_count);\n    }\n    __syncthreads();\n\n    if (threadIdx.x == 0) {\n        finished_sum[0] = block_finished_count;\n    }\n}\n\nvoid invokeLengthCriterion(bool*           finished,\n                           bool*           should_stop,\n                           int*            h_pinned_finished_sum_,\n                           const uint32_t* sequence_limit_length,\n                           int             batch_size,\n                           int             beam_width,\n                           int             step,\n                           cudaStream_t    stream)\n{\n    // Check if we have attained the sequence length limit. If so, stop the sequence.\n    // In addition, check if all sequences are stopped and return the result in should_stop\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    dim3 block{min(512, uint32_t(batch_size * beam_width))};\n    dim3 grid{1};\n    h_pinned_finished_sum_[0] = -1;\n\n    length_criterion<<<grid, block, 0, stream>>>(\n        finished, should_stop, h_pinned_finished_sum_, sequence_limit_length, batch_size, beam_width, step);\n    while (((volatile int*)h_pinned_finished_sum_)[0] == -1) {};\n    sync_check_cuda_error();\n\n    *should_stop = h_pinned_finished_sum_[0] == batch_size * beam_width;\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/stop_criteria_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\n__global__ void stop_words_criterion(const int* output_ids,\n                                     const int* parent_ids,\n                                     const int* stop_words,\n                                     bool*      finished,\n                                     size_t     id_offset,\n                                     size_t     stop_words_len,\n                                     int        batch_size,\n                                     int        beam_width,\n                                     int        step)\n{\n    const int id        = blockIdx.x * blockDim.x + threadIdx.x;\n    const int batch_idx = blockIdx.y / beam_width;\n    const int beam_idx  = blockIdx.y % beam_width;\n\n    const int* base_stop_words = stop_words + batch_idx * 2 * stop_words_len;\n    const int* base_offsets    = base_stop_words + stop_words_len;\n\n    if (id >= stop_words_len || base_offsets[id] < 0) {\n        return;\n    }\n\n    const int item_end   = base_offsets[id];\n    const int item_start = (id > 0) ? base_offsets[id - 1] : 0;\n    const int item_size  = item_end - item_start;\n\n    /* The single-token case unconditionally bans the token */\n    bool should_stop = false;\n\n    /* Enough previously generated tokens to look for a match */\n    if (step + 1 >= item_size) {\n        should_stop            = true;\n        int        parent_id   = beam_idx;\n        const bool gather_beam = beam_width > 1;\n\n        for (int token_idx = item_size - 1; token_idx >= 0; token_idx--) {\n            const int previous_token = output_ids[(step - (item_size - 1) + token_idx) * batch_size * beam_width\n                                                  + id_offset + batch_idx * beam_width + parent_id];\n\n            if (previous_token != base_stop_words[item_start + token_idx]) {\n                should_stop = false;\n                break;\n            }\n            if (gather_beam) {\n                parent_id = parent_ids[(step - (item_size - 1) + token_idx) * beam_width * batch_size + id_offset\n                                       + batch_idx * beam_width + parent_id];\n\n                if (parent_id < 0 || parent_id >= beam_width) {\n                    should_stop = false;\n                    break;\n                }\n            }\n        }\n    }\n\n    if (should_stop) {\n        finished[batch_idx * beam_width + beam_idx] = true;\n    }\n}\n\nvoid invokeStopWordsCriterion(const int*   output_ids,\n                              const int*   parent_ids,\n                              const int*   stop_words,\n                              bool*        finished,\n                              size_t       id_offset,\n                              size_t       stop_words_len,\n                              int          batch_size,\n                              int          beam_width,\n                              int          step,\n                              hipStream_t stream)\n{\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    // Check if we have sampled a word from the stop_words list. If so, stop the sequence.\n    dim3 block, grid;\n    block.x = min(((stop_words_len + 32 - 1) / 32) * 32, 256UL);\n    grid.x  = (stop_words_len + block.x - 1) / block.x;\n    grid.y  = batch_size * beam_width;\n\n    stop_words_criterion<<<grid, block, 0, stream>>>(\n        output_ids, parent_ids, stop_words, finished, id_offset, stop_words_len, batch_size, beam_width, step);\n    sync_check_cuda_error();\n}\n\n__global__ void length_criterion(bool*           finished,\n                                 bool*           should_stop,\n                                 int*            finished_sum,\n                                 const uint32_t* sequence_limit_length,\n                                 int             batch_size,\n                                 int             beam_width,\n                                 int             step)\n{\n    int thread_finished_count = 0;\n    for (int index = threadIdx.x; index < batch_size * beam_width; index += blockDim.x) {\n        const int batch_idx = index / beam_width;\n\n        finished[index] |= step >= sequence_limit_length[batch_idx];\n        thread_finished_count += finished[index] ? 1 : 0;\n    }\n    int block_finished_count = 0;\n    if (blockDim.x <= 32) {\n        block_finished_count = warpReduceSum(thread_finished_count);\n    }\n    else {\n        block_finished_count = blockReduceSum(thread_finished_count);\n    }\n    __syncthreads();\n\n    if (threadIdx.x == 0) {\n        finished_sum[0] = block_finished_count;\n    }\n}\n\nvoid invokeLengthCriterion(bool*           finished,\n                           bool*           should_stop,\n                           int*            h_pinned_finished_sum_,\n                           const uint32_t* sequence_limit_length,\n                           int             batch_size,\n                           int             beam_width,\n                           int             step,\n                           hipStream_t    stream)\n{\n    // Check if we have attained the sequence length limit. If so, stop the sequence.\n    // In addition, check if all sequences are stopped and return the result in should_stop\n    FT_LOG_DEBUG(\"%s start\", __PRETTY_FUNCTION__);\n    dim3 block{min(512, uint32_t(batch_size * beam_width))};\n    dim3 grid{1};\n    h_pinned_finished_sum_[0] = -1;\n\n    length_criterion<<<grid, block, 0, stream>>>(\n        finished, should_stop, h_pinned_finished_sum_, sequence_limit_length, batch_size, beam_width, step);\n    while (((volatile int*)h_pinned_finished_sum_)[0] == -1) {};\n    sync_check_cuda_error();\n\n    *should_stop = h_pinned_finished_sum_[0] == batch_size * beam_width;\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/stop_criteria_kernels.cu"}
{"input": "/*\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/disentangled_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <assert.h>\n\nnamespace fastertransformer {\n\n#define kDISENTANGLED_VERSION 2\n// Version 1: regular relative position index\n// Version 2: log bucket relative position index\nconstexpr int32_t kDISENTANGLED_TILESIZE_V1  = 32;\nconstexpr int32_t kDISENTANGLED_BLOCKDIMY_V1 = 8;\nconstexpr int32_t kDISENTANGLED_TILESIZE_V2  = 64;\nconstexpr int32_t kDISENTANGLED_BLOCKDIMY_V2 = 4;\n\ntemplate<typename T>\n__global__ void addQKBiasTransposeRepeat(T* q_out,\n                                         T* k_out,\n                                         const T* __restrict q_in,\n                                         const T* __restrict bias_q,\n                                         const T* __restrict k_in,\n                                         const T* __restrict bias_k,\n                                         const int batch_size,\n                                         const int attention_span,\n                                         const int head_num,\n                                         const int size_per_head)\n{\n    const int n           = head_num * size_per_head;\n    const int batch_id    = blockIdx.x;\n    const int position_id = blockIdx.y;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * attention_span * size_per_head)\n                              + head_id * (attention_span * size_per_head) + position_id * size_per_head + size_id;\n        const int src_id = position_id * n + col_id;\n\n        T q              = add(ldg(&q_in[src_id]), ldg(&bias_q[col_id]));\n        q_out[target_id] = q;\n\n        T k              = add(ldg(&k_in[src_id]), ldg(&bias_k[col_id]));\n        k_out[target_id] = k;\n    }\n}\n\ntemplate<typename T>\n__global__ void QKTransposeRepeat(T* q_out,\n                                  T* k_out,\n                                  const T* __restrict q_in,\n                                  const T* __restrict k_in,\n                                  const int batch_size,\n                                  const int attention_span,\n                                  const int head_num,\n                                  const int size_per_head)\n{\n    const int n           = head_num * size_per_head;\n    const int batch_id    = blockIdx.x;\n    const int position_id = blockIdx.y;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * attention_span * size_per_head)\n                              + head_id * attention_span * size_per_head + position_id * size_per_head + size_id;\n        const int src_id = position_id * n + col_id;\n\n        q_out[target_id] = ldg(&q_in[src_id]);\n        k_out[target_id] = ldg(&k_in[src_id]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransposeRepeat(T*           q_buf,\n                                    T*           k_buf,\n                                    T*           Q,\n                                    const T*     bias_Q,\n                                    T*           K,\n                                    const T*     bias_K,\n                                    const int    batch_size,\n                                    const int    attention_span,\n                                    const int    head_num,\n                                    const int    size_per_head,\n                                    cudaStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, attention_span);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKBiasTransposeRepeat<T><<<grid, block, 0, stream>>>(\n                q_buf, k_buf, Q, bias_Q, K, bias_K, batch_size, attention_span, head_num, size_per_head);\n        }\n        else {\n            QKTransposeRepeat<T>\n                <<<grid, block, 0, stream>>>(q_buf, k_buf, Q, K, batch_size, attention_span, head_num, size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKBiasTransposeRepeat<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                                     (T2*)k_buf,\n                                                                     (const T2*)Q,\n                                                                     (const T2*)bias_Q,\n                                                                     (const T2*)K,\n                                                                     (const T2*)bias_K,\n                                                                     batch_size,\n                                                                     attention_span,\n                                                                     head_num,\n                                                                     size_per_head / 2);\n        }\n        else {\n            QKTransposeRepeat<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                              (T2*)k_buf,\n                                                              (const T2*)Q,\n                                                              (const T2*)K,\n                                                              batch_size,\n                                                              attention_span,\n                                                              head_num,\n                                                              size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\n#define INSTANTIATEADDQKBIASTRANSPOSEREPEAT(T)                                                                         \\\n    template void invokeAddQKBiasTransposeRepeat(T*           q_buf,                                                   \\\n                                                 T*           k_buf,                                                   \\\n                                                 T*           Q,                                                       \\\n                                                 const T*     bias_Q,                                                  \\\n                                                 T*           K,                                                       \\\n                                                 const T*     bias_K,                                                  \\\n                                                 const int    batch_size,                                              \\\n                                                 const int    attention_span,                                          \\\n                                                 const int    head_num,                                                \\\n                                                 const int    size_per_head,                                           \\\n                                                 cudaStream_t stream)\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(float);\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDQKBIASTRANSPOSEREPEAT\n\n// template specialization for double/float\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, double>::value\n                                     || std::is_same<std::decay_t<TDataType>, float>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n\n// template specialization for half\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, __half>::value\n                                     || std::is_same<std::decay_t<TDataType>, half>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n#if __CUDA_ARCH__ >= 530\n    // __hmul only supported >= sm_53\n    res = __hadd(res0, __hadd(res1, res2));\n#else\n    // for < sm_53, workaround/fallback is convert to float and downconvert\n    res = __float2half(__half2float(res0) + __half2float(res1) + __half2float(res2));\n#endif\n}\n\n#ifdef ENABLE_BF16\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, nv_bfloat16>::value, TDataType>::type* dummy =\n             nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n#endif\n\n// template specialization for int8\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, int8_t>::value\n                                     || std::is_same<std::decay_t<TDataType>, uint8_t>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n\n/**\n * Fused kernel for Disentangled Attention design (first proposed in Microsoft DeBERTa). Implementation refactored from\n * previous TensorRT plugin GatherAddGatherTransposeAddMul_fused kernel implementation by (1) removing the scaling\n * factor because each attention matrix has already been applied scaling in cuBLAS (2) add BF16 support.\n *\n * @tparam TDataType type of the input data\n * @tparam tTileSize dimension of the shared memory tile (square) and also the BlockDimX. Need for compile-time shared\n * memory size\n * @tparam tBlockDimY 2D thread block is (tTileSize, tBlockDimY)\n * @param result attention result\n * @param data0 content-to-content (\"c2c\") attention QcKc^T\n * @param data1 content-to-position (\"c2p\") attention QcKr^T\n * @param data2 position-to-content (\"p2c\") attention KcQr^T\n * @param batch_dim flattened batch dimension is (batch_size * num_heads)\n * @param seq_dim sequence dimension [seq_len]\n * @param span relative distance hyper-parameter, k, in Disentangled attention\n */\ntemplate<typename TDataType, int32_t tTileSize, int32_t tBlockDimY>\n__global__ void disentangled_attention_kernel(TDataType*       result,\n                                              TDataType*       data0,\n                                              TDataType const* data1,\n                                              TDataType const* data2,\n                                              int32_t          batch_dim,\n                                              int32_t          seq_dim,\n                                              int32_t          span)\n{\n    // Tile size should be a multiple of number of block rows\n    assert(blockDim.y * (blockDim.x / blockDim.y) == blockDim.x);\n\n    // map block to the output (result)\n    int32_t   i;\n    int32_t   j;\n    int32_t   k;\n    int32_t   ty;\n    int32_t   c;\n    int32_t   pos_dim = span * 2;\n    TDataType res0;\n    TDataType res1;\n    TDataType res2;\n    TDataType res;\n\n#if kDISENTANGLED_VERSION == 2\n    int32_t bucket;\n    int32_t mid = span / 2;\n    int32_t index;\n\n    // tmp values are precomputed for re-use; must be at least float to ensure accuracy\n    float tmp1 = logf(mid);\n\n    // Multiply by (1 - epsilon) to ensure that taking the ceil of approximately an integer\n    // results in that integer when computing the bucket later on.\n    // This corrects for the mathematical imprecision from using float.\n    constexpr float kEPSILON = 1e-7;\n    float           tmp      = (mid - 1) / (logf(pos_dim - 1) - tmp1) * (1 - kEPSILON);\n#endif\n\n    __shared__ TDataType T[tTileSize][tTileSize + 1];  // +1 to avoid bank conflict\n\n    // (i,j,k) location of data2 (transposed)\n    i = blockIdx.z;\n    j = blockIdx.x * tTileSize + threadIdx.y;\n    k = blockIdx.y * tTileSize + threadIdx.x;\n\n    // (j+ty, k) is the location in the index matrix (implicit), where the element values of the index matrix are\n    // determinisitic based on its location. Index matrix dimension is [batch_size*num_heads, seq_len, seq_len], add\n    // boundary check accordingly c2p & p2c matrix dimension is [batch_size*num_heads, seq_len, 2*k], within-boundary is\n    // guaranteed by the index function design, but can still add check due to floating point precision\n\n// gather data2\n#pragma unroll\n    for (c = 0, ty = 0; c < tTileSize / tBlockDimY && (j + ty) < seq_dim && k < seq_dim; c++, ty += tBlockDimY) {\n#if kDISENTANGLED_VERSION == 1\n        // relative position -- version 1\n        if (k - (j + ty) >= span) {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + pos_dim - 1];\n        }\n        else if (k - (j + ty) <= -span) {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + 0];\n        }\n        else {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + k - (j + ty) + span];  // compute index on the fly\n        }\n        T[ty + threadIdx.y][threadIdx.x] = res2;\n#elif kDISENTANGLED_VERSION == 2\n        // relative position w/ log bucket -- version 2\n        if (k - (j + ty) >= -mid && k - (j + ty) <= mid) {\n            // preserved region, (i - j) + span\n            bucket = k - (j + ty);\n        }\n        else {\n            // log bucket region, bucket(i,j) + span\n            bucket = ceilf((logf(fabsf(k - (j + ty))) - tmp1) * tmp) + mid;\n            bucket = k - (j + ty) < 0 ? -bucket : bucket;\n        }\n        // clamp [0,2k]. Although this is guaranteed by equation, but numerically the floating precision can still break\n        // boundary\n        index                            = bucket + span;\n        index                            = min(max(0, index), pos_dim - 1);\n        res2                             = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + index];\n        T[ty + threadIdx.y][threadIdx.x] = res2;\n#endif\n    }\n\n    __syncthreads();\n\n    // (i,j,k) location of data1 (non-transposed) and output. i unchanged\n    j = blockIdx.y * tTileSize + threadIdx.y;\n    k = blockIdx.x * tTileSize + threadIdx.x;\n\n// read data0 + gather data1 + add all + write\n#pragma unroll\n    for (c = 0, ty = 0; c < tTileSize / tBlockDimY && (j + ty) < seq_dim && k < seq_dim; c++, ty += tBlockDimY) {\n#if kDISENTANGLED_VERSION == 1\n        // relative position -- version 1\n        // for non-transposed matrix 1, just fetch element at the transposed location & add to the result)\n        if (j + ty - k <= -span) {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + 0];\n        }\n        else if (j + ty - k >= span) {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + pos_dim - 1];\n        }\n        else {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + j + ty - k + span];  // compute index on the fly\n        }\n#elif kDISENTANGLED_VERSION == 2\n        // relative position w/ log bucket -- version 2\n        if (j + ty - k >= -mid && j + ty - k <= mid) {\n            // preserved region, (i - j) + span\n            bucket = j + ty - k;\n        }\n        else {\n            // log bucket region, bucket(i,j) + span\n            bucket = ceilf((logf(fabsf((j + ty) - k)) - tmp1) * tmp) + mid;\n            bucket = (j + ty) - k < 0 ? -bucket : bucket;\n        }\n        // clamp [0,2k]. Although this is guaranteed by equation, but numerically the floating precision can still break\n        // boundary\n        index = bucket + span;\n        index = min(max(0, index), pos_dim - 1);\n        res1  = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + index];\n#endif\n\n        // for non-tranposed matrix 0, same as matrix 1\n        res0 = data0[i * seq_dim * seq_dim + (j + ty) * seq_dim + k];\n\n        // (res0 + res1 + res2)\n#if __cplusplus >= 201703L\n        // C++ 17 has more convenient `if constexpr` for conditional implementation at compile time; before C++ 17,\n        // switch to template specialization\n        if constexpr (std::is_same<TDataType, double>::value || std::is_same<TDataType, float>::value) {\n            // double, float32\n            res = res0 + res1 + T[threadIdx.x][ty + threadIdx.y];\n        }\n        else if constexpr (std::is_same<TDataType, __half>::value || std::is_same<TDataType, half>::value) {\n            // fp16\n#if __CUDA_ARCH__ >= 530\n            // half ops only supported >= sm_53\n            res = __hadd(res0, __hadd(res1, T[threadIdx.x][ty + threadIdx.y]));\n#else\n            // for < sm_53, workaround/fallback is convert to float and downconvert\n            res =\n                __float2half(__half2float(res0) + __half2float(res1) + __half2float(T[threadIdx.x][ty + threadIdx.y]));\n#endif\n        }\n#ifdef ENABLE_BF16\n        else if constexpr (std::is_same<TDataType, __nv_bfloat16>::value) {\n            // bf16\n            res = __hadd(res0, __hadd(res1, T[threadIdx.x][ty + threadIdx.y]));\n        }\n#endif\n        else if constexpr (std::is_same<TDataType, int8_t>::value || std::is_same<TDataType, uint8_t>::value) {\n            // int8_t\n            res = res0 + res1 + T[threadIdx.x][ty + threadIdx.y];\n        }\n#else\n        // before C++ 17, use template specialization\n        compute_attention<TDataType>(res, res0, res1, T[threadIdx.x][ty + threadIdx.y]);\n#endif\n        // write\n        result[i * seq_dim * seq_dim + (j + ty) * seq_dim + k] = res;\n    }\n}\n\n#define INSTANTIATEDISENTANGLEDKERNEL(T)                                                                               \\\n    template __global__ void disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1>(  \\\n        T*, T*, T const*, T const*, int32_t, int32_t, int32_t);                                                        \\\n    template __global__ void disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2>(  \\\n        T*, T*, T const*, T const*, int32_t, int32_t, int32_t);\nINSTANTIATEDISENTANGLEDKERNEL(float)\nINSTANTIATEDISENTANGLEDKERNEL(half)\nINSTANTIATEDISENTANGLEDKERNEL(int8_t)\n#ifdef ENABLE_BF16\nINSTANTIATEDISENTANGLEDKERNEL(__nv_bfloat16)\n#endif\n#undef INSTANTIATEDISENTANGLEDKERNEL\n\ntemplate<typename T>\nvoid invokeDisentangledAttention(\n    T* result, T* c2c, T* c2p, T* p2c, const int batch_dim, const int seq_dim, const int span, cudaStream_t stream)\n{\n#if kDISENTANGLED_VERSION == 1\n    dim3 block_optimized(kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1);\n    dim3 grid_optimized(\n        (seq_dim - 1) / kDISENTANGLED_TILESIZE_V1 + 1, (seq_dim - 1) / kDISENTANGLED_TILESIZE_V1 + 1, batch_dim);\n    disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1>\n        <<<grid_optimized, block_optimized, 0, stream>>>(result, c2c, c2p, p2c, batch_dim, seq_dim, span);\n\n#elif kDISENTANGLED_VERSION == 2\n    dim3 block_optimized(kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2);\n    dim3 grid_optimized(\n        (seq_dim - 1) / kDISENTANGLED_TILESIZE_V2 + 1, (seq_dim - 1) / kDISENTANGLED_TILESIZE_V2 + 1, batch_dim);\n    disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2>\n        <<<grid_optimized, block_optimized, 0, stream>>>(result, c2c, c2p, p2c, batch_dim, seq_dim, span);\n\n#endif\n}\n\n#define INSTANTIATEDISENTANGLEDATTENTION(T)                                                                            \\\n    template void invokeDisentangledAttention(T*           result,                                                     \\\n                                              T*           c2c,                                                        \\\n                                              T*           c2p,                                                        \\\n                                              T*           p2c,                                                        \\\n                                              const int    batch_dim,                                                  \\\n                                              const int    seq_dim,                                                    \\\n                                              const int    span,                                                       \\\n                                              cudaStream_t stream);\nINSTANTIATEDISENTANGLEDATTENTION(float)\nINSTANTIATEDISENTANGLEDATTENTION(half)\nINSTANTIATEDISENTANGLEDATTENTION(int8_t)\n#ifdef ENABLE_BF16\nINSTANTIATEDISENTANGLEDATTENTION(__nv_bfloat16)\n#endif\n#undef INSTANTIATEDISENTANGLEDATTENTION\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/disentangled_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include <assert.h>\n\nnamespace fastertransformer {\n\n#define kDISENTANGLED_VERSION 2\n// Version 1: regular relative position index\n// Version 2: log bucket relative position index\nconstexpr int32_t kDISENTANGLED_TILESIZE_V1  = 32;\nconstexpr int32_t kDISENTANGLED_BLOCKDIMY_V1 = 8;\nconstexpr int32_t kDISENTANGLED_TILESIZE_V2  = 64;\nconstexpr int32_t kDISENTANGLED_BLOCKDIMY_V2 = 4;\n\ntemplate<typename T>\n__global__ void addQKBiasTransposeRepeat(T* q_out,\n                                         T* k_out,\n                                         const T* __restrict q_in,\n                                         const T* __restrict bias_q,\n                                         const T* __restrict k_in,\n                                         const T* __restrict bias_k,\n                                         const int batch_size,\n                                         const int attention_span,\n                                         const int head_num,\n                                         const int size_per_head)\n{\n    const int n           = head_num * size_per_head;\n    const int batch_id    = blockIdx.x;\n    const int position_id = blockIdx.y;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * attention_span * size_per_head)\n                              + head_id * (attention_span * size_per_head) + position_id * size_per_head + size_id;\n        const int src_id = position_id * n + col_id;\n\n        T q              = add(ldg(&q_in[src_id]), ldg(&bias_q[col_id]));\n        q_out[target_id] = q;\n\n        T k              = add(ldg(&k_in[src_id]), ldg(&bias_k[col_id]));\n        k_out[target_id] = k;\n    }\n}\n\ntemplate<typename T>\n__global__ void QKTransposeRepeat(T* q_out,\n                                  T* k_out,\n                                  const T* __restrict q_in,\n                                  const T* __restrict k_in,\n                                  const int batch_size,\n                                  const int attention_span,\n                                  const int head_num,\n                                  const int size_per_head)\n{\n    const int n           = head_num * size_per_head;\n    const int batch_id    = blockIdx.x;\n    const int position_id = blockIdx.y;\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * attention_span * size_per_head)\n                              + head_id * attention_span * size_per_head + position_id * size_per_head + size_id;\n        const int src_id = position_id * n + col_id;\n\n        q_out[target_id] = ldg(&q_in[src_id]);\n        k_out[target_id] = ldg(&k_in[src_id]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKBiasTransposeRepeat(T*           q_buf,\n                                    T*           k_buf,\n                                    T*           Q,\n                                    const T*     bias_Q,\n                                    T*           K,\n                                    const T*     bias_K,\n                                    const int    batch_size,\n                                    const int    attention_span,\n                                    const int    head_num,\n                                    const int    size_per_head,\n                                    hipStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, attention_span);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKBiasTransposeRepeat<T><<<grid, block, 0, stream>>>(\n                q_buf, k_buf, Q, bias_Q, K, bias_K, batch_size, attention_span, head_num, size_per_head);\n        }\n        else {\n            QKTransposeRepeat<T>\n                <<<grid, block, 0, stream>>>(q_buf, k_buf, Q, K, batch_size, attention_span, head_num, size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKBiasTransposeRepeat<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                                     (T2*)k_buf,\n                                                                     (const T2*)Q,\n                                                                     (const T2*)bias_Q,\n                                                                     (const T2*)K,\n                                                                     (const T2*)bias_K,\n                                                                     batch_size,\n                                                                     attention_span,\n                                                                     head_num,\n                                                                     size_per_head / 2);\n        }\n        else {\n            QKTransposeRepeat<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                              (T2*)k_buf,\n                                                              (const T2*)Q,\n                                                              (const T2*)K,\n                                                              batch_size,\n                                                              attention_span,\n                                                              head_num,\n                                                              size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\n#define INSTANTIATEADDQKBIASTRANSPOSEREPEAT(T)                                                                         \\\n    template void invokeAddQKBiasTransposeRepeat(T*           q_buf,                                                   \\\n                                                 T*           k_buf,                                                   \\\n                                                 T*           Q,                                                       \\\n                                                 const T*     bias_Q,                                                  \\\n                                                 T*           K,                                                       \\\n                                                 const T*     bias_K,                                                  \\\n                                                 const int    batch_size,                                              \\\n                                                 const int    attention_span,                                          \\\n                                                 const int    head_num,                                                \\\n                                                 const int    size_per_head,                                           \\\n                                                 hipStream_t stream)\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(float);\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKBIASTRANSPOSEREPEAT(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDQKBIASTRANSPOSEREPEAT\n\n// template specialization for double/float\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, double>::value\n                                     || std::is_same<std::decay_t<TDataType>, float>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n\n// template specialization for half\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, __half>::value\n                                     || std::is_same<std::decay_t<TDataType>, half>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n#if __CUDA_ARCH__ >= 530\n    // __hmul only supported >= sm_53\n    res = __hadd(res0, __hadd(res1, res2));\n#else\n    // for < sm_53, workaround/fallback is convert to float and downconvert\n    res = __float2half(__half2float(res0) + __half2float(res1) + __half2float(res2));\n#endif\n}\n\n#ifdef ENABLE_BF16\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, hip_bfloat16>::value, TDataType>::type* dummy =\n             nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n#endif\n\n// template specialization for int8\ntemplate<typename TDataType,\n         typename std::enable_if<std::is_same<std::decay_t<TDataType>, int8_t>::value\n                                     || std::is_same<std::decay_t<TDataType>, uint8_t>::value,\n                                 TDataType>::type* dummy = nullptr>\n__forceinline__ __device__ void\ncompute_attention(TDataType& res, const TDataType& res0, const TDataType& res1, const TDataType& res2)\n{\n    res = res0 + res1 + res2;\n}\n\n/**\n * Fused kernel for Disentangled Attention design (first proposed in Microsoft DeBERTa). Implementation refactored from\n * previous TensorRT plugin GatherAddGatherTransposeAddMul_fused kernel implementation by (1) removing the scaling\n * factor because each attention matrix has already been applied scaling in cuBLAS (2) add BF16 support.\n *\n * @tparam TDataType type of the input data\n * @tparam tTileSize dimension of the shared memory tile (square) and also the BlockDimX. Need for compile-time shared\n * memory size\n * @tparam tBlockDimY 2D thread block is (tTileSize, tBlockDimY)\n * @param result attention result\n * @param data0 content-to-content (\"c2c\") attention QcKc^T\n * @param data1 content-to-position (\"c2p\") attention QcKr^T\n * @param data2 position-to-content (\"p2c\") attention KcQr^T\n * @param batch_dim flattened batch dimension is (batch_size * num_heads)\n * @param seq_dim sequence dimension [seq_len]\n * @param span relative distance hyper-parameter, k, in Disentangled attention\n */\ntemplate<typename TDataType, int32_t tTileSize, int32_t tBlockDimY>\n__global__ void disentangled_attention_kernel(TDataType*       result,\n                                              TDataType*       data0,\n                                              TDataType const* data1,\n                                              TDataType const* data2,\n                                              int32_t          batch_dim,\n                                              int32_t          seq_dim,\n                                              int32_t          span)\n{\n    // Tile size should be a multiple of number of block rows\n    assert(blockDim.y * (blockDim.x / blockDim.y) == blockDim.x);\n\n    // map block to the output (result)\n    int32_t   i;\n    int32_t   j;\n    int32_t   k;\n    int32_t   ty;\n    int32_t   c;\n    int32_t   pos_dim = span * 2;\n    TDataType res0;\n    TDataType res1;\n    TDataType res2;\n    TDataType res;\n\n#if kDISENTANGLED_VERSION == 2\n    int32_t bucket;\n    int32_t mid = span / 2;\n    int32_t index;\n\n    // tmp values are precomputed for re-use; must be at least float to ensure accuracy\n    float tmp1 = logf(mid);\n\n    // Multiply by (1 - epsilon) to ensure that taking the ceil of approximately an integer\n    // results in that integer when computing the bucket later on.\n    // This corrects for the mathematical imprecision from using float.\n    constexpr float kEPSILON = 1e-7;\n    float           tmp      = (mid - 1) / (logf(pos_dim - 1) - tmp1) * (1 - kEPSILON);\n#endif\n\n    __shared__ TDataType T[tTileSize][tTileSize + 1];  // +1 to avoid bank conflict\n\n    // (i,j,k) location of data2 (transposed)\n    i = blockIdx.z;\n    j = blockIdx.x * tTileSize + threadIdx.y;\n    k = blockIdx.y * tTileSize + threadIdx.x;\n\n    // (j+ty, k) is the location in the index matrix (implicit), where the element values of the index matrix are\n    // determinisitic based on its location. Index matrix dimension is [batch_size*num_heads, seq_len, seq_len], add\n    // boundary check accordingly c2p & p2c matrix dimension is [batch_size*num_heads, seq_len, 2*k], within-boundary is\n    // guaranteed by the index function design, but can still add check due to floating point precision\n\n// gather data2\n#pragma unroll\n    for (c = 0, ty = 0; c < tTileSize / tBlockDimY && (j + ty) < seq_dim && k < seq_dim; c++, ty += tBlockDimY) {\n#if kDISENTANGLED_VERSION == 1\n        // relative position -- version 1\n        if (k - (j + ty) >= span) {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + pos_dim - 1];\n        }\n        else if (k - (j + ty) <= -span) {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + 0];\n        }\n        else {\n            res2 = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + k - (j + ty) + span];  // compute index on the fly\n        }\n        T[ty + threadIdx.y][threadIdx.x] = res2;\n#elif kDISENTANGLED_VERSION == 2\n        // relative position w/ log bucket -- version 2\n        if (k - (j + ty) >= -mid && k - (j + ty) <= mid) {\n            // preserved region, (i - j) + span\n            bucket = k - (j + ty);\n        }\n        else {\n            // log bucket region, bucket(i,j) + span\n            bucket = ceilf((logf(fabsf(k - (j + ty))) - tmp1) * tmp) + mid;\n            bucket = k - (j + ty) < 0 ? -bucket : bucket;\n        }\n        // clamp [0,2k]. Although this is guaranteed by equation, but numerically the floating precision can still break\n        // boundary\n        index                            = bucket + span;\n        index                            = min(max(0, index), pos_dim - 1);\n        res2                             = data2[i * seq_dim * pos_dim + (j + ty) * pos_dim + index];\n        T[ty + threadIdx.y][threadIdx.x] = res2;\n#endif\n    }\n\n    __syncthreads();\n\n    // (i,j,k) location of data1 (non-transposed) and output. i unchanged\n    j = blockIdx.y * tTileSize + threadIdx.y;\n    k = blockIdx.x * tTileSize + threadIdx.x;\n\n// read data0 + gather data1 + add all + write\n#pragma unroll\n    for (c = 0, ty = 0; c < tTileSize / tBlockDimY && (j + ty) < seq_dim && k < seq_dim; c++, ty += tBlockDimY) {\n#if kDISENTANGLED_VERSION == 1\n        // relative position -- version 1\n        // for non-transposed matrix 1, just fetch element at the transposed location & add to the result)\n        if (j + ty - k <= -span) {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + 0];\n        }\n        else if (j + ty - k >= span) {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + pos_dim - 1];\n        }\n        else {\n            res1 = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + j + ty - k + span];  // compute index on the fly\n        }\n#elif kDISENTANGLED_VERSION == 2\n        // relative position w/ log bucket -- version 2\n        if (j + ty - k >= -mid && j + ty - k <= mid) {\n            // preserved region, (i - j) + span\n            bucket = j + ty - k;\n        }\n        else {\n            // log bucket region, bucket(i,j) + span\n            bucket = ceilf((logf(fabsf((j + ty) - k)) - tmp1) * tmp) + mid;\n            bucket = (j + ty) - k < 0 ? -bucket : bucket;\n        }\n        // clamp [0,2k]. Although this is guaranteed by equation, but numerically the floating precision can still break\n        // boundary\n        index = bucket + span;\n        index = min(max(0, index), pos_dim - 1);\n        res1  = data1[i * seq_dim * pos_dim + (j + ty) * pos_dim + index];\n#endif\n\n        // for non-tranposed matrix 0, same as matrix 1\n        res0 = data0[i * seq_dim * seq_dim + (j + ty) * seq_dim + k];\n\n        // (res0 + res1 + res2)\n#if __cplusplus >= 201703L\n        // C++ 17 has more convenient `if constexpr` for conditional implementation at compile time; before C++ 17,\n        // switch to template specialization\n        if constexpr (std::is_same<TDataType, double>::value || std::is_same<TDataType, float>::value) {\n            // double, float32\n            res = res0 + res1 + T[threadIdx.x][ty + threadIdx.y];\n        }\n        else if constexpr (std::is_same<TDataType, __half>::value || std::is_same<TDataType, half>::value) {\n            // fp16\n#if __CUDA_ARCH__ >= 530\n            // half ops only supported >= sm_53\n            res = __hadd(res0, __hadd(res1, T[threadIdx.x][ty + threadIdx.y]));\n#else\n            // for < sm_53, workaround/fallback is convert to float and downconvert\n            res =\n                __float2half(__half2float(res0) + __half2float(res1) + __half2float(T[threadIdx.x][ty + threadIdx.y]));\n#endif\n        }\n#ifdef ENABLE_BF16\n        else if constexpr (std::is_same<TDataType, __hip_bfloat16>::value) {\n            // bf16\n            res = __hadd(res0, __hadd(res1, T[threadIdx.x][ty + threadIdx.y]));\n        }\n#endif\n        else if constexpr (std::is_same<TDataType, int8_t>::value || std::is_same<TDataType, uint8_t>::value) {\n            // int8_t\n            res = res0 + res1 + T[threadIdx.x][ty + threadIdx.y];\n        }\n#else\n        // before C++ 17, use template specialization\n        compute_attention<TDataType>(res, res0, res1, T[threadIdx.x][ty + threadIdx.y]);\n#endif\n        // write\n        result[i * seq_dim * seq_dim + (j + ty) * seq_dim + k] = res;\n    }\n}\n\n#define INSTANTIATEDISENTANGLEDKERNEL(T)                                                                               \\\n    template __global__ void disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1>(  \\\n        T*, T*, T const*, T const*, int32_t, int32_t, int32_t);                                                        \\\n    template __global__ void disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2>(  \\\n        T*, T*, T const*, T const*, int32_t, int32_t, int32_t);\nINSTANTIATEDISENTANGLEDKERNEL(float)\nINSTANTIATEDISENTANGLEDKERNEL(half)\nINSTANTIATEDISENTANGLEDKERNEL(int8_t)\n#ifdef ENABLE_BF16\nINSTANTIATEDISENTANGLEDKERNEL(__hip_bfloat16)\n#endif\n#undef INSTANTIATEDISENTANGLEDKERNEL\n\ntemplate<typename T>\nvoid invokeDisentangledAttention(\n    T* result, T* c2c, T* c2p, T* p2c, const int batch_dim, const int seq_dim, const int span, hipStream_t stream)\n{\n#if kDISENTANGLED_VERSION == 1\n    dim3 block_optimized(kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1);\n    dim3 grid_optimized(\n        (seq_dim - 1) / kDISENTANGLED_TILESIZE_V1 + 1, (seq_dim - 1) / kDISENTANGLED_TILESIZE_V1 + 1, batch_dim);\n    disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V1, kDISENTANGLED_BLOCKDIMY_V1>\n        <<<grid_optimized, block_optimized, 0, stream>>>(result, c2c, c2p, p2c, batch_dim, seq_dim, span);\n\n#elif kDISENTANGLED_VERSION == 2\n    dim3 block_optimized(kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2);\n    dim3 grid_optimized(\n        (seq_dim - 1) / kDISENTANGLED_TILESIZE_V2 + 1, (seq_dim - 1) / kDISENTANGLED_TILESIZE_V2 + 1, batch_dim);\n    disentangled_attention_kernel<T, kDISENTANGLED_TILESIZE_V2, kDISENTANGLED_BLOCKDIMY_V2>\n        <<<grid_optimized, block_optimized, 0, stream>>>(result, c2c, c2p, p2c, batch_dim, seq_dim, span);\n\n#endif\n}\n\n#define INSTANTIATEDISENTANGLEDATTENTION(T)                                                                            \\\n    template void invokeDisentangledAttention(T*           result,                                                     \\\n                                              T*           c2c,                                                        \\\n                                              T*           c2p,                                                        \\\n                                              T*           p2c,                                                        \\\n                                              const int    batch_dim,                                                  \\\n                                              const int    seq_dim,                                                    \\\n                                              const int    span,                                                       \\\n                                              hipStream_t stream);\nINSTANTIATEDISENTANGLEDATTENTION(float)\nINSTANTIATEDISENTANGLEDATTENTION(half)\nINSTANTIATEDISENTANGLEDATTENTION(int8_t)\n#ifdef ENABLE_BF16\nINSTANTIATEDISENTANGLEDATTENTION(__hip_bfloat16)\n#endif\n#undef INSTANTIATEDISENTANGLEDATTENTION\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/disentangled_attention_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"xlnet_preprocess_kernels.h\"\n\nnamespace fastertransformer {\n/*************Device Function**************/\ntemplate<typename T>\nint numPerThread()\n{\n    return sizeof(float) / sizeof(T);\n}\n/********************** Kernels ************************/\n\n//    Applied to half or bfloat16\n//    dim3 grid(batch_size, seq_len);\n//    getWordEmdK<<<grid, hidden_dim/2,0, stream>>>(word_emb_k, params_word_emb_k, inp_k, seq_len, hidden_dim);\ntemplate<typename T>\nvoid __global__ getWordEmdK(T* word_emb_k, T* params_word_emb_k, int* inp_k, int seq_len, int hidden_dim)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int col   = threadIdx.x;                      // the index of column\n    int row   = blockIdx.y;                       // the index of row\n    int batch = blockIdx.x;                       // the index of batch\n\n    int index = ldg(inp_k + batch * seq_len + row);\n    T2  data  = ((T2*)params_word_emb_k)[(index * hidden_dim + col * 2) >> 1];\n\n    ((T2*)word_emb_k)[(batch * seq_len * hidden_dim + row * hidden_dim + col * 2) >> 1] = data;\n}\n\ntemplate<>\nvoid __global__ getWordEmdK(float* word_emb_k, float* params_word_emb_k, int* inp_k, int seq_len, int hidden_dim)\n{\n\n    int col   = threadIdx.x;  // the index of column\n    int row   = blockIdx.y;   // the index of row\n    int batch = blockIdx.x;   // the index of batch\n\n    int   index = inp_k[batch * seq_len + row];\n    float data  = params_word_emb_k[index * hidden_dim + col];\n\n    word_emb_k[batch * seq_len * hidden_dim + row * hidden_dim + col] = data;\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid __global__ getAttnMask(T* attn_mask, float* input_mask, int seq_len)\n{\n    using T2     = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int in_index = blockIdx.y * blockDim.x + threadIdx.x;\n    int col      = in_index % (seq_len / 2) * 2;\n    int row      = in_index / (seq_len / 2);\n    int batch    = blockIdx.x;\n\n    float2 tmp;\n    if (row < seq_len && col < seq_len - 1) {\n        float data = 1;\n        if (col == row) {\n            data = 0;\n        }\n        tmp.x = input_mask[batch * seq_len + col] * data;\n\n        col += 1;\n        data = 1;\n        if (col == row) {\n            data = 0;\n        }\n        tmp.y = input_mask[batch * seq_len + col] * data;\n\n        int out_index               = (batch * seq_len * seq_len + row * seq_len + col) >> 1;\n        ((T2*)attn_mask)[out_index] = cuda_cast<T2>(tmp);\n    }\n}\n\ntemplate<>\nvoid __global__ getAttnMask(float* attn_mask, float* input_mask, int seq_len)\n{\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    float data = 1;\n    if (col == row) {\n        data = 0;\n    }\n    float mask                                                 = input_mask[batch * seq_len + col];\n    attn_mask[batch * seq_len * seq_len + row * seq_len + col] = cuda_cast<float>(data * mask);\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid __global__ getSegMat(T* seg_mat, int* seg_id, int seq_len)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    int w[4] = {0, 1, 1, 0};\n    int d1   = seg_id[batch * seq_len + col];\n    int d2   = seg_id[batch * seq_len + row];\n    int d    = 0;\n\n    d = int(floor(exp(-1 * abs(double(d1 - d2)))));\n\n    int    index = batch * seq_len * seq_len + row * seq_len + col;\n    float2 tmp_w;\n    tmp_w.x = w[d * 2 + 0];\n    tmp_w.y = w[d * 2 + 1];\n\n    ((T2*)seg_mat)[index] = cuda_cast<T2>(tmp_w);\n}\n\ntemplate<>\nvoid __global__ getSegMat(float* seg_mat, int* seg_id, int seq_len)\n{\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    int w[4] = {0, 1, 1, 0};\n    int d1   = seg_id[batch * seq_len + col];\n    int d2   = seg_id[batch * seq_len + row];\n    int d    = 0;\n\n    d = int(floor(exp(-1 * abs(double(d1 - d2)))));\n\n    int index              = batch * seq_len * seq_len + row * seq_len + col;\n    seg_mat[index * 2]     = w[d * 2 + 0];\n    seg_mat[index * 2 + 1] = w[d * 2 + 1];\n}\n\ntemplate<typename T>\nvoid __global__ relativePosition(T* attr_k_head_r, int hidden_dim, int seq_len)\n{\n    int row = blockIdx.x;   //(0,256)\n    int col = threadIdx.x;  //(0,384)\n\n    float freq_seq = col * 2;\n    float inv_freq = 1 / (pow(10000, freq_seq / (hidden_dim)));\n\n    float fwd_pos_seq = seq_len - row;\n\n    float pos_emd = inv_freq * fwd_pos_seq;\n    float s       = sinf(pos_emd);\n    float c       = cosf(pos_emd);\n\n    attr_k_head_r[row * hidden_dim + col]                  = cuda_cast<T>(s);\n    attr_k_head_r[row * hidden_dim + hidden_dim / 2 + col] = cuda_cast<T>(c);\n}\n\n/***********************Pre-Process************************/\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid blockAttnMask<T>(dim3& grid, dim3& block, int batch_size, int seq_len)\n{\n    int numThreads = 512;\n    int numBlocky  = (seq_len * seq_len / 2 - 1) / numThreads + 1;\n    grid.x         = batch_size;\n    grid.y         = numBlocky;\n    block.x        = numThreads;\n}\n\ntemplate<>\nvoid blockAttnMask<float>(dim3& grid, dim3& block, int batch_size, int seq_len)\n{\n    grid.x  = batch_size;\n    grid.y  = seq_len;\n    block.x = seq_len;\n}\n\ntemplate<typename T>\nvoid genWordEmdK(\n    int batch_size, int seq_len, int hidden_dim, T* word_emb_k, T* params_word_emb_k, int* inp_k, cudaStream_t stream)\n{\n    dim3 grid_word_emd_k(batch_size, seq_len);\n    dim3 block_word_emd_k(hidden_dim / numPerThread<T>());\n\n    getWordEmdK<<<grid_word_emd_k, block_word_emd_k, 0, stream>>>(\n        word_emb_k, params_word_emb_k, inp_k, seq_len, hidden_dim);\n}\n\ntemplate<typename T>\nvoid preProcess(int          batch_size,\n                int          seq_len,\n                int          hidden_dim,\n                T*           attn_mask,\n                float*       input_mask,\n                T*           seg_mat,\n                int*         seg_id,\n                T*           attr_k_head_r,\n                cudaStream_t stream)\n{\n    dim3 grid_attn_mask;\n    dim3 block_attn_mask;\n    blockAttnMask<T>(grid_attn_mask, block_attn_mask, batch_size, seq_len);\n    getAttnMask<<<grid_attn_mask, block_attn_mask, 0, stream>>>(attn_mask, input_mask, seq_len);\n\n    dim3 grid_seg_mat(batch_size, seq_len);\n    dim3 block_seg_mat(seq_len);\n    getSegMat<<<grid_seg_mat, block_seg_mat, 0, stream>>>(seg_mat, seg_id, seq_len);\n\n    // relative_positional_encoding\n    dim3 grid_rel_position(seq_len * 2);\n    dim3 block_rel_position(hidden_dim / 2);\n    relativePosition<<<grid_rel_position, block_rel_position, 0, stream>>>(attr_k_head_r, hidden_dim, seq_len);\n}\n\ntemplate void preProcess<float>(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                float*       attn_mask,\n                                float*       input_mask,\n                                float*       seg_mat,\n                                int*         seg_id,\n                                float*       attr_k_head_r,\n                                cudaStream_t stream);\n\ntemplate void preProcess<half>(int          batch_size,\n                               int          seq_len,\n                               int          hidden_dim,\n                               half*        attn_mask,\n                               float*       input_mask,\n                               half*        seg_mat,\n                               int*         seg_id,\n                               half*        attr_k_head_r,\n                               cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void preProcess<__nv_bfloat16>(int            batch_size,\n                                        int            seq_len,\n                                        int            hidden_dim,\n                                        __nv_bfloat16* attn_mask,\n                                        float*         input_mask,\n                                        __nv_bfloat16* seg_mat,\n                                        int*           seg_id,\n                                        __nv_bfloat16* attr_k_head_r,\n                                        cudaStream_t   stream);\n#endif\n\ntemplate void genWordEmdK<float>(int          batch_size,\n                                 int          seq_len,\n                                 int          hidden_dim,\n                                 float*       word_emb_k,\n                                 float*       params_word_emb_k,\n                                 int*         inp_k,\n                                 cudaStream_t stream);\ntemplate void genWordEmdK<half>(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                half*        word_emb_k,\n                                half*        params_word_emb_k,\n                                int*         inp_k,\n                                cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void genWordEmdK<__nv_bfloat16>(int            batch_size,\n                                         int            seq_len,\n                                         int            hidden_dim,\n                                         __nv_bfloat16* word_emb_k,\n                                         __nv_bfloat16* params_word_emb_k,\n                                         int*           inp_k,\n                                         cudaStream_t   stream);\n#endif\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"xlnet_preprocess_kernels.h\"\n\nnamespace fastertransformer {\n/*************Device Function**************/\ntemplate<typename T>\nint numPerThread()\n{\n    return sizeof(float) / sizeof(T);\n}\n/********************** Kernels ************************/\n\n//    Applied to half or bfloat16\n//    dim3 grid(batch_size, seq_len);\n//    getWordEmdK<<<grid, hidden_dim/2,0, stream>>>(word_emb_k, params_word_emb_k, inp_k, seq_len, hidden_dim);\ntemplate<typename T>\nvoid __global__ getWordEmdK(T* word_emb_k, T* params_word_emb_k, int* inp_k, int seq_len, int hidden_dim)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int col   = threadIdx.x;                      // the index of column\n    int row   = blockIdx.y;                       // the index of row\n    int batch = blockIdx.x;                       // the index of batch\n\n    int index = ldg(inp_k + batch * seq_len + row);\n    T2  data  = ((T2*)params_word_emb_k)[(index * hidden_dim + col * 2) >> 1];\n\n    ((T2*)word_emb_k)[(batch * seq_len * hidden_dim + row * hidden_dim + col * 2) >> 1] = data;\n}\n\ntemplate<>\nvoid __global__ getWordEmdK(float* word_emb_k, float* params_word_emb_k, int* inp_k, int seq_len, int hidden_dim)\n{\n\n    int col   = threadIdx.x;  // the index of column\n    int row   = blockIdx.y;   // the index of row\n    int batch = blockIdx.x;   // the index of batch\n\n    int   index = inp_k[batch * seq_len + row];\n    float data  = params_word_emb_k[index * hidden_dim + col];\n\n    word_emb_k[batch * seq_len * hidden_dim + row * hidden_dim + col] = data;\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid __global__ getAttnMask(T* attn_mask, float* input_mask, int seq_len)\n{\n    using T2     = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int in_index = blockIdx.y * blockDim.x + threadIdx.x;\n    int col      = in_index % (seq_len / 2) * 2;\n    int row      = in_index / (seq_len / 2);\n    int batch    = blockIdx.x;\n\n    float2 tmp;\n    if (row < seq_len && col < seq_len - 1) {\n        float data = 1;\n        if (col == row) {\n            data = 0;\n        }\n        tmp.x = input_mask[batch * seq_len + col] * data;\n\n        col += 1;\n        data = 1;\n        if (col == row) {\n            data = 0;\n        }\n        tmp.y = input_mask[batch * seq_len + col] * data;\n\n        int out_index               = (batch * seq_len * seq_len + row * seq_len + col) >> 1;\n        ((T2*)attn_mask)[out_index] = cuda_cast<T2>(tmp);\n    }\n}\n\ntemplate<>\nvoid __global__ getAttnMask(float* attn_mask, float* input_mask, int seq_len)\n{\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    float data = 1;\n    if (col == row) {\n        data = 0;\n    }\n    float mask                                                 = input_mask[batch * seq_len + col];\n    attn_mask[batch * seq_len * seq_len + row * seq_len + col] = cuda_cast<float>(data * mask);\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid __global__ getSegMat(T* seg_mat, int* seg_id, int seq_len)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    int w[4] = {0, 1, 1, 0};\n    int d1   = seg_id[batch * seq_len + col];\n    int d2   = seg_id[batch * seq_len + row];\n    int d    = 0;\n\n    d = int(floor(exp(-1 * abs(double(d1 - d2)))));\n\n    int    index = batch * seq_len * seq_len + row * seq_len + col;\n    float2 tmp_w;\n    tmp_w.x = w[d * 2 + 0];\n    tmp_w.y = w[d * 2 + 1];\n\n    ((T2*)seg_mat)[index] = cuda_cast<T2>(tmp_w);\n}\n\ntemplate<>\nvoid __global__ getSegMat(float* seg_mat, int* seg_id, int seq_len)\n{\n    int col   = threadIdx.x;\n    int row   = blockIdx.y;\n    int batch = blockIdx.x;\n\n    int w[4] = {0, 1, 1, 0};\n    int d1   = seg_id[batch * seq_len + col];\n    int d2   = seg_id[batch * seq_len + row];\n    int d    = 0;\n\n    d = int(floor(exp(-1 * abs(double(d1 - d2)))));\n\n    int index              = batch * seq_len * seq_len + row * seq_len + col;\n    seg_mat[index * 2]     = w[d * 2 + 0];\n    seg_mat[index * 2 + 1] = w[d * 2 + 1];\n}\n\ntemplate<typename T>\nvoid __global__ relativePosition(T* attr_k_head_r, int hidden_dim, int seq_len)\n{\n    int row = blockIdx.x;   //(0,256)\n    int col = threadIdx.x;  //(0,384)\n\n    float freq_seq = col * 2;\n    float inv_freq = 1 / (pow(10000, freq_seq / (hidden_dim)));\n\n    float fwd_pos_seq = seq_len - row;\n\n    float pos_emd = inv_freq * fwd_pos_seq;\n    float s       = sinf(pos_emd);\n    float c       = cosf(pos_emd);\n\n    attr_k_head_r[row * hidden_dim + col]                  = cuda_cast<T>(s);\n    attr_k_head_r[row * hidden_dim + hidden_dim / 2 + col] = cuda_cast<T>(c);\n}\n\n/***********************Pre-Process************************/\n// Applied to half or bfloat16\ntemplate<typename T>\nvoid blockAttnMask<T>(dim3& grid, dim3& block, int batch_size, int seq_len)\n{\n    int numThreads = 512;\n    int numBlocky  = (seq_len * seq_len / 2 - 1) / numThreads + 1;\n    grid.x         = batch_size;\n    grid.y         = numBlocky;\n    block.x        = numThreads;\n}\n\ntemplate<>\nvoid blockAttnMask<float>(dim3& grid, dim3& block, int batch_size, int seq_len)\n{\n    grid.x  = batch_size;\n    grid.y  = seq_len;\n    block.x = seq_len;\n}\n\ntemplate<typename T>\nvoid genWordEmdK(\n    int batch_size, int seq_len, int hidden_dim, T* word_emb_k, T* params_word_emb_k, int* inp_k, hipStream_t stream)\n{\n    dim3 grid_word_emd_k(batch_size, seq_len);\n    dim3 block_word_emd_k(hidden_dim / numPerThread<T>());\n\n    getWordEmdK<<<grid_word_emd_k, block_word_emd_k, 0, stream>>>(\n        word_emb_k, params_word_emb_k, inp_k, seq_len, hidden_dim);\n}\n\ntemplate<typename T>\nvoid preProcess(int          batch_size,\n                int          seq_len,\n                int          hidden_dim,\n                T*           attn_mask,\n                float*       input_mask,\n                T*           seg_mat,\n                int*         seg_id,\n                T*           attr_k_head_r,\n                hipStream_t stream)\n{\n    dim3 grid_attn_mask;\n    dim3 block_attn_mask;\n    blockAttnMask<T>(grid_attn_mask, block_attn_mask, batch_size, seq_len);\n    getAttnMask<<<grid_attn_mask, block_attn_mask, 0, stream>>>(attn_mask, input_mask, seq_len);\n\n    dim3 grid_seg_mat(batch_size, seq_len);\n    dim3 block_seg_mat(seq_len);\n    getSegMat<<<grid_seg_mat, block_seg_mat, 0, stream>>>(seg_mat, seg_id, seq_len);\n\n    // relative_positional_encoding\n    dim3 grid_rel_position(seq_len * 2);\n    dim3 block_rel_position(hidden_dim / 2);\n    relativePosition<<<grid_rel_position, block_rel_position, 0, stream>>>(attr_k_head_r, hidden_dim, seq_len);\n}\n\ntemplate void preProcess<float>(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                float*       attn_mask,\n                                float*       input_mask,\n                                float*       seg_mat,\n                                int*         seg_id,\n                                float*       attr_k_head_r,\n                                hipStream_t stream);\n\ntemplate void preProcess<half>(int          batch_size,\n                               int          seq_len,\n                               int          hidden_dim,\n                               half*        attn_mask,\n                               float*       input_mask,\n                               half*        seg_mat,\n                               int*         seg_id,\n                               half*        attr_k_head_r,\n                               hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void preProcess<__hip_bfloat16>(int            batch_size,\n                                        int            seq_len,\n                                        int            hidden_dim,\n                                        __hip_bfloat16* attn_mask,\n                                        float*         input_mask,\n                                        __hip_bfloat16* seg_mat,\n                                        int*           seg_id,\n                                        __hip_bfloat16* attr_k_head_r,\n                                        hipStream_t   stream);\n#endif\n\ntemplate void genWordEmdK<float>(int          batch_size,\n                                 int          seq_len,\n                                 int          hidden_dim,\n                                 float*       word_emb_k,\n                                 float*       params_word_emb_k,\n                                 int*         inp_k,\n                                 hipStream_t stream);\ntemplate void genWordEmdK<half>(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                half*        word_emb_k,\n                                half*        params_word_emb_k,\n                                int*         inp_k,\n                                hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void genWordEmdK<__hip_bfloat16>(int            batch_size,\n                                         int            seq_len,\n                                         int            hidden_dim,\n                                         __hip_bfloat16* word_emb_k,\n                                         __hip_bfloat16* params_word_emb_k,\n                                         int*           inp_k,\n                                         hipStream_t   stream);\n#endif\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/xlnet_preprocess_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reduce_kernel_utils.cuh\"\n#include \"reverse_roll_kernels.h\"\n\nnamespace fastertransformer {\n\n/*******************  invokeReverseRollCol32  ***********************/\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\n\n__global__ void reverse_roll_col32(int8_t*       dst,\n                                   const int8_t* src,\n                                   const int     batch,\n                                   const int     window_num,\n                                   const int     window_len,\n                                   const int     window_size,\n                                   const int     H,\n                                   const int     W,\n                                   const int     shift_size,\n                                   const int     dim)\n{\n    const int batch_idx = blockIdx.z;\n    const int HW_idx    = (blockIdx.y << 5) + threadIdx.y;\n    if (HW_idx < H * W) {\n        const int H_idx         = HW_idx / W;\n        const int W_idx         = HW_idx % W;\n        const int H_idx_shifted = (H_idx + shift_size) % H;\n        const int W_idx_shifted = (W_idx + shift_size) % W;\n\n        const int window_idx          = H_idx / window_size * (W / window_size) + W_idx / window_size;\n        const int idx_in_window       = (H_idx % window_size) * window_size + (W_idx % window_size);\n        const int input_offset        = (batch_idx * window_num + window_idx) * window_len + idx_in_window;\n        const int output_offset       = (batch_idx * H + H_idx_shifted) * W + W_idx_shifted;\n        const int m                   = H * W * batch;\n        char4*    inPtr               = (char4*)src;\n        char4*    outPtr              = (char4*)dst;\n        const int col_start           = (blockIdx.x << 5) + (threadIdx.x << 2);\n        const int offset_col32_in     = (col_start & 0xffffffe0) * m + (input_offset << 5) + (col_start & 31);\n        const int offset_col32_out    = (col_start & 0xffffffe0) * m + (output_offset << 5) + (col_start & 31);\n        outPtr[offset_col32_out >> 2] = inPtr[offset_col32_in >> 2];\n    }\n}\n\nvoid invokeReverseRollCol32(int8_t*       dst,\n                            const int8_t* src,\n                            int           batch,\n                            int           window_num,\n                            int           window_len,\n                            int           window_size,\n                            int           H,\n                            int           W,\n                            int           dim,\n                            int           shift_size,\n                            cudaStream_t  stream)\n{\n    dim3 grid((dim + 31) / 32, (H * W + 31) / 32, batch);\n    dim3 block(8, 32);\n    reverse_roll_col32<<<grid, block, 0, stream>>>(\n        dst, src, batch, window_num, window_len, window_size, H, W, shift_size, dim);\n}\n\n/*******************  invokeReverseRoll  ***********************/\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\n\ntemplate<typename T>\n__global__ void reverse_roll(T*        dst,\n                             const T*  src,\n                             const int batch,\n                             const int window_num,\n                             const int window_len,\n                             const int window_size,\n                             const int H,\n                             const int W,\n                             const int shift_size,\n                             const int dim)\n{\n    const int batch_idx     = blockIdx.z;\n    const int H_idx_shifted = (blockIdx.y + shift_size) % H;\n    const int W_idx_shifted = (blockIdx.x + shift_size) % W;\n    const int H_idx         = blockIdx.y;\n    const int W_idx         = blockIdx.x;\n    const int window_idx    = H_idx / window_size * (W / window_size) + W_idx / window_size;\n    const int idx_in_window = (H_idx % window_size) * window_size + (W_idx % window_size);\n    const int input_offset  = (batch_idx * window_num + window_idx) * window_len + idx_in_window;\n    const int output_offset = (batch_idx * H + H_idx_shifted) * W + W_idx_shifted;\n    for (int tid = threadIdx.x; tid < dim; tid += blockDim.x) {\n        dst[output_offset * dim + tid] = src[input_offset * dim + tid];\n    }\n}\n\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\ntemplate<typename T>\nvoid invokeReverseRoll(T*           dst,\n                       const T*     src,\n                       int          batch,\n                       int          window_num,\n                       int          window_len,\n                       int          window_size,\n                       int          H,\n                       int          W,\n                       int          dim,\n                       int          shift_size,\n                       cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = dim;\n#ifdef ENABLE_BF16\n    if ((std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value) && (dim % 2 == 0)) {\n#else\n    if (std::is_same<T, half>::value && (dim % 2 == 0)) {\n#endif\n        blockSize = dim / 2;\n        if (blockSize > 1024) {\n            blockSize = 1024;\n        }\n        using T2 = typename TypeConverter<T>::Type;  // bfloat162 or half2\n        reverse_roll<<<grid, blockSize, 0, stream>>>(\n            (T2*)dst, (const T2*)src, batch, window_num, window_len, window_size, H, W, shift_size, dim / 2);\n    }\n    else {\n        if (blockSize > 1024) {\n            blockSize = 1024;\n        }\n        reverse_roll<<<grid, blockSize, 0, stream>>>(\n            dst, src, batch, window_num, window_len, window_size, H, W, shift_size, dim);\n    }\n}\n\ntemplate void invokeReverseRoll(float*       dst,\n                                const float* src,\n                                int          batch,\n                                int          window_num,\n                                int          window_len,\n                                int          window_size,\n                                int          H,\n                                int          W,\n                                int          dim,\n                                int          shift_size,\n                                cudaStream_t stream);\n\ntemplate void invokeReverseRoll(half*        dst,\n                                const half*  src,\n                                int          batch,\n                                int          window_num,\n                                int          window_len,\n                                int          window_size,\n                                int          H,\n                                int          W,\n                                int          dim,\n                                int          shift_size,\n                                cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeReverseRoll(__nv_bfloat16*       dst,\n                                const __nv_bfloat16* src,\n                                int                  batch,\n                                int                  window_num,\n                                int                  window_len,\n                                int                  window_size,\n                                int                  H,\n                                int                  W,\n                                int                  dim,\n                                int                  shift_size,\n                                cudaStream_t         stream);\n#endif\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"reverse_roll_kernels.h\"\n\nnamespace fastertransformer {\n\n/*******************  invokeReverseRollCol32  ***********************/\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\n\n__global__ void reverse_roll_col32(int8_t*       dst,\n                                   const int8_t* src,\n                                   const int     batch,\n                                   const int     window_num,\n                                   const int     window_len,\n                                   const int     window_size,\n                                   const int     H,\n                                   const int     W,\n                                   const int     shift_size,\n                                   const int     dim)\n{\n    const int batch_idx = blockIdx.z;\n    const int HW_idx    = (blockIdx.y << 5) + threadIdx.y;\n    if (HW_idx < H * W) {\n        const int H_idx         = HW_idx / W;\n        const int W_idx         = HW_idx % W;\n        const int H_idx_shifted = (H_idx + shift_size) % H;\n        const int W_idx_shifted = (W_idx + shift_size) % W;\n\n        const int window_idx          = H_idx / window_size * (W / window_size) + W_idx / window_size;\n        const int idx_in_window       = (H_idx % window_size) * window_size + (W_idx % window_size);\n        const int input_offset        = (batch_idx * window_num + window_idx) * window_len + idx_in_window;\n        const int output_offset       = (batch_idx * H + H_idx_shifted) * W + W_idx_shifted;\n        const int m                   = H * W * batch;\n        char4*    inPtr               = (char4*)src;\n        char4*    outPtr              = (char4*)dst;\n        const int col_start           = (blockIdx.x << 5) + (threadIdx.x << 2);\n        const int offset_col32_in     = (col_start & 0xffffffe0) * m + (input_offset << 5) + (col_start & 31);\n        const int offset_col32_out    = (col_start & 0xffffffe0) * m + (output_offset << 5) + (col_start & 31);\n        outPtr[offset_col32_out >> 2] = inPtr[offset_col32_in >> 2];\n    }\n}\n\nvoid invokeReverseRollCol32(int8_t*       dst,\n                            const int8_t* src,\n                            int           batch,\n                            int           window_num,\n                            int           window_len,\n                            int           window_size,\n                            int           H,\n                            int           W,\n                            int           dim,\n                            int           shift_size,\n                            hipStream_t  stream)\n{\n    dim3 grid((dim + 31) / 32, (H * W + 31) / 32, batch);\n    dim3 block(8, 32);\n    reverse_roll_col32<<<grid, block, 0, stream>>>(\n        dst, src, batch, window_num, window_len, window_size, H, W, shift_size, dim);\n}\n\n/*******************  invokeReverseRoll  ***********************/\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\n\ntemplate<typename T>\n__global__ void reverse_roll(T*        dst,\n                             const T*  src,\n                             const int batch,\n                             const int window_num,\n                             const int window_len,\n                             const int window_size,\n                             const int H,\n                             const int W,\n                             const int shift_size,\n                             const int dim)\n{\n    const int batch_idx     = blockIdx.z;\n    const int H_idx_shifted = (blockIdx.y + shift_size) % H;\n    const int W_idx_shifted = (blockIdx.x + shift_size) % W;\n    const int H_idx         = blockIdx.y;\n    const int W_idx         = blockIdx.x;\n    const int window_idx    = H_idx / window_size * (W / window_size) + W_idx / window_size;\n    const int idx_in_window = (H_idx % window_size) * window_size + (W_idx % window_size);\n    const int input_offset  = (batch_idx * window_num + window_idx) * window_len + idx_in_window;\n    const int output_offset = (batch_idx * H + H_idx_shifted) * W + W_idx_shifted;\n    for (int tid = threadIdx.x; tid < dim; tid += blockDim.x) {\n        dst[output_offset * dim + tid] = src[input_offset * dim + tid];\n    }\n}\n\n// src is [batch*window_num, window_len, dim]\n// dst is [batch, H, W, dim] + rolled\n// grid(W, H, batch)\n// block(min(1024, dim))\ntemplate<typename T>\nvoid invokeReverseRoll(T*           dst,\n                       const T*     src,\n                       int          batch,\n                       int          window_num,\n                       int          window_len,\n                       int          window_size,\n                       int          H,\n                       int          W,\n                       int          dim,\n                       int          shift_size,\n                       hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = dim;\n#ifdef ENABLE_BF16\n    if ((std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value) && (dim % 2 == 0)) {\n#else\n    if (std::is_same<T, half>::value && (dim % 2 == 0)) {\n#endif\n        blockSize = dim / 2;\n        if (blockSize > 1024) {\n            blockSize = 1024;\n        }\n        using T2 = typename TypeConverter<T>::Type;  // bfloat162 or half2\n        reverse_roll<<<grid, blockSize, 0, stream>>>(\n            (T2*)dst, (const T2*)src, batch, window_num, window_len, window_size, H, W, shift_size, dim / 2);\n    }\n    else {\n        if (blockSize > 1024) {\n            blockSize = 1024;\n        }\n        reverse_roll<<<grid, blockSize, 0, stream>>>(\n            dst, src, batch, window_num, window_len, window_size, H, W, shift_size, dim);\n    }\n}\n\ntemplate void invokeReverseRoll(float*       dst,\n                                const float* src,\n                                int          batch,\n                                int          window_num,\n                                int          window_len,\n                                int          window_size,\n                                int          H,\n                                int          W,\n                                int          dim,\n                                int          shift_size,\n                                hipStream_t stream);\n\ntemplate void invokeReverseRoll(half*        dst,\n                                const half*  src,\n                                int          batch,\n                                int          window_num,\n                                int          window_len,\n                                int          window_size,\n                                int          H,\n                                int          W,\n                                int          dim,\n                                int          shift_size,\n                                hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeReverseRoll(__hip_bfloat16*       dst,\n                                const __hip_bfloat16* src,\n                                int                  batch,\n                                int                  window_num,\n                                int                  window_len,\n                                int                  window_size,\n                                int                  H,\n                                int                  W,\n                                int                  dim,\n                                int                  shift_size,\n                                hipStream_t         stream);\n#endif\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/reverse_roll_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bert_preprocess_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n__global__ void getPaddingOffsetAndCuSeqLensKernel(size_t*    h_valid_word_num,\n                                                   int*       tmp_mask_offset,\n                                                   int*       cu_seqlens,\n                                                   const int* sequence_length,\n                                                   const int  batch_size,\n                                                   const int  max_seq_len)\n{\n    // do cumulated sum\n    int        total_seq_len        = 0;\n    int        cum_offset           = 0;\n    int        index                = 0;\n    const bool calculate_cu_seqlens = cu_seqlens != nullptr;\n    for (int i = 0; i < batch_size; i++) {\n        const int seq_len = sequence_length[i];\n        if (calculate_cu_seqlens) {\n            cu_seqlens[i] = total_seq_len;\n        }\n        for (int j = 0; j < seq_len; j++) {\n            tmp_mask_offset[index] = cum_offset;\n            index++;\n        }\n        cum_offset += max_seq_len - seq_len;\n        total_seq_len += seq_len;\n    }\n    if (calculate_cu_seqlens) {\n        cu_seqlens[batch_size] = total_seq_len;\n    }\n    h_valid_word_num[0] = (size_t)total_seq_len;\n}\n\nvoid invokeGetPaddingOffsetAndCuSeqLens(size_t*      h_pinned_token_num,\n                                        size_t*      h_token_num,\n                                        int*         tmp_mask_offset,\n                                        int*         cu_seqlens,\n                                        const int*   sequence_lengths,\n                                        const int    batch_size,\n                                        const int    max_seq_len,\n                                        cudaStream_t stream)\n{\n    h_pinned_token_num[0] = 0;\n    getPaddingOffsetAndCuSeqLensKernel<<<1, 1, 0, stream>>>(\n        h_pinned_token_num, tmp_mask_offset, cu_seqlens, sequence_lengths, batch_size, max_seq_len);\n    while (((volatile size_t*)h_pinned_token_num)[0] == 0) {};\n    h_token_num[0] = h_pinned_token_num[0];\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\n__global__ void buildEncoderAttentionMaskKernel(T* attention_mask, const int* sequence_lengths, const int max_seq_len)\n{\n    // sequence_lengths: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len, max_seq_len]\n    attention_mask += blockIdx.x * max_seq_len * max_seq_len;\n    const int length = sequence_lengths[blockIdx.x];\n    for (int i = threadIdx.x; i < max_seq_len * max_seq_len; i += blockDim.x) {\n        // int row_id = i / max_seq_len;\n        int col_id = i % max_seq_len;\n        // if (row_id < length && col_id < length) {\n        // TODO (bhsueh) check this modification is ok or not on other rmodel\n        if (col_id < length) {\n            attention_mask[i] = (T)(1.0f);\n        }\n        else {\n            attention_mask[i] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildEncoderAttentionMask(\n    T* attention_mask, const int* sequence_lengths, const int batch_size, const int max_seq_len, cudaStream_t stream)\n{\n    buildEncoderAttentionMaskKernel<<<batch_size, 256, 0, stream>>>(attention_mask, sequence_lengths, max_seq_len);\n}\n\ntemplate void invokeBuildEncoderAttentionMask(float*       attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              cudaStream_t stream);\ntemplate void invokeBuildEncoderAttentionMask(half*        attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              cudaStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeBuildEncoderAttentionMask(__nv_fp8_e4m3* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              cudaStream_t   stream);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeBuildEncoderAttentionMask(__nv_bfloat16* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              cudaStream_t   stream);\n#endif\n\n__global__ void getTrtPaddingOffsetKernel(int* trt_mha_padding_offset, const int* sequence_length, const int batch_size)\n{\n    // use for get tensorrt fused mha padding offset\n    // when we remove the padding\n\n    extern __shared__ int tmp_offset[];\n    if (threadIdx.x == 0) {\n        tmp_offset[0] = 0;\n        for (int i = 0; i < batch_size; i++) {\n            tmp_offset[i + 1] = tmp_offset[i] + sequence_length[i];\n        }\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < batch_size + 1; i += blockDim.x) {\n        trt_mha_padding_offset[i] = tmp_offset[i];\n    }\n}\n\nvoid invokeGetTrtPaddingOffset(int*         trt_mha_padding_offset,\n                               const int*   sequence_length,\n                               const int    batch_size,\n                               cudaStream_t stream)\n{\n    getTrtPaddingOffsetKernel<<<1, 256, sizeof(int) * (batch_size + 1), stream>>>(\n        trt_mha_padding_offset, sequence_length, batch_size);\n}\n\n__global__ void getTrtPaddingOffsetKernel(int*       trt_mha_padding_offset,\n                                          const int* sequence_length,\n                                          const int  request_batch_size,\n                                          const int  request_seq_len)\n{\n    // use for get tensorrt fused mha padding offset\n    // when we keep the padding\n\n    extern __shared__ int tmp_offset[];\n    if (threadIdx.x == 0) {\n        tmp_offset[0] = 0;\n        for (int i = 0; i < request_batch_size; i++) {\n            tmp_offset[i * 2 + 1] = tmp_offset[i * 2] + sequence_length[i];\n            tmp_offset[i * 2 + 2] = request_seq_len * (i + 1);\n        }\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < 2 * request_batch_size + 1; i += blockDim.x) {\n        trt_mha_padding_offset[i] = tmp_offset[i];\n    }\n}\n\nvoid invokeGetTrtPaddingOffset(int*         trt_mha_padding_offset,\n                               const int*   sequence_length,\n                               const int    request_batch_size,\n                               const int    request_seq_len,\n                               cudaStream_t stream)\n{\n    getTrtPaddingOffsetKernel<<<1, 256, sizeof(int) * (2 * request_batch_size + 1), stream>>>(\n        trt_mha_padding_offset, sequence_length, request_batch_size, request_seq_len);\n}\n\ntemplate<typename T>\n__global__ void rebuild_sequence_length_padding(const T* src, T* dst, const int* padding_offset, const int n)\n{\n    const int tid        = threadIdx.x;\n    const int bid        = blockIdx.x;\n    const int dst_seq_id = bid + padding_offset[bid];\n    const int src_seq_id = bid;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        dst[dst_seq_id * n + i] = src[src_seq_id * n + i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRebuildPadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, cudaStream_t stream)\n{\n    // src: [token_num, hidden_dim]\n    // dst: [batch_size*max_seq_len, hidden_dim]\n    rebuild_sequence_length_padding<<<token_num, 256, 0, stream>>>(src, dst, padding_offset, hidden_dim);\n}\n\ntemplate<typename T>\nvoid invokeRebuildPadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, cudaStream_t stream);\ntemplate void invokeRebuildPadding(float*       dst,\n                                   const float* src,\n                                   const int*   padding_offset,\n                                   const int    token_num,\n                                   const int    hidden_dim,\n                                   cudaStream_t stream);\ntemplate void invokeRebuildPadding(half*        dst,\n                                   const half*  src,\n                                   const int*   padding_offset,\n                                   const int    token_num,\n                                   const int    hidden_dim,\n                                   cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeRebuildPadding(__nv_bfloat16*       dst,\n                                   const __nv_bfloat16* src,\n                                   const int*           padding_offset,\n                                   const int            token_num,\n                                   const int            hidden_dim,\n                                   cudaStream_t         stream);\n#endif  // ENABLE_BF16\n\n#ifdef ENABLE_FP8\ntemplate void invokeRebuildPadding(__nv_fp8_e4m3*       dst,\n                                   const __nv_fp8_e4m3* src,\n                                   const int*           padding_offset,\n                                   const int            token_num,\n                                   const int            hidden_dim,\n                                   cudaStream_t         stream);\n#endif  // ENABLE_FP8\n\ntemplate<typename T>\n__global__ void remove_padding(T* tgt, const T* src, const int* padding_offset, const int n)\n{\n    const int tid        = threadIdx.x;\n    const int bid        = blockIdx.x;\n    const int src_seq_id = bid + padding_offset[bid];\n    const int tgt_seq_id = bid;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        tgt[tgt_seq_id * n + i] = src[src_seq_id * n + i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRemovePadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, cudaStream_t stream)\n{\n    remove_padding<<<token_num, 256, 0, stream>>>(dst, src, padding_offset, hidden_dim);\n}\n\ntemplate void invokeRemovePadding(float*       dst,\n                                  const float* src,\n                                  const int*   padding_offset,\n                                  const int    token_num,\n                                  const int    hidden_dim,\n                                  cudaStream_t stream);\n\ntemplate void invokeRemovePadding(half*        dst,\n                                  const half*  src,\n                                  const int*   padding_offset,\n                                  const int    token_num,\n                                  const int    hidden_dim,\n                                  cudaStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeRemovePadding(__nv_fp8_e4m3*       dst,\n                                  const __nv_fp8_e4m3* src,\n                                  const int*           padding_offset,\n                                  const int            token_num,\n                                  const int            hidden_dim,\n                                  cudaStream_t         stream);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeRemovePadding(__nv_bfloat16*       dst,\n                                  const __nv_bfloat16* src,\n                                  const int*           padding_offset,\n                                  const int            token_num,\n                                  const int            hidden_dim,\n                                  cudaStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void buildRelativeAttentionBias(T*         relative_attention_bias,\n                                           const T*   relative_attention_bias_table,\n                                           const int  head_num,\n                                           const int  seq_len,\n                                           const int  num_bucket,\n                                           const bool is_bidirectional,\n                                           const int  max_distance)\n{\n\n    const int head_id = blockIdx.x;\n    for (int seq_id = threadIdx.x; seq_id < seq_len * seq_len; seq_id += blockDim.x) {\n        int row_id = seq_id / seq_len;\n        int col_id = seq_id % seq_len;\n\n        int relative_position = col_id - row_id;\n\n        int relative_buckets = 0;\n        int tmp_num_bucket   = num_bucket;\n        if (is_bidirectional) {\n            tmp_num_bucket /= 2;\n            if (relative_position > 0) {\n                relative_buckets += tmp_num_bucket;\n            }\n            else {\n                relative_position *= -1;\n            }\n        }\n        else {\n            relative_position = abs(relative_position);\n        }\n\n        int  max_exact = tmp_num_bucket / 2;\n        bool is_small  = relative_position < max_exact;\n\n        int relative_position_if_large =\n            max_exact\n            + (int)(logf(relative_position * 1.0f / max_exact) / logf((float)max_distance / max_exact)\n                    * (tmp_num_bucket - max_exact));\n\n        relative_position_if_large = min(relative_position_if_large, tmp_num_bucket - 1);\n\n        relative_buckets += is_small ? relative_position : relative_position_if_large;\n\n        relative_attention_bias[head_id * seq_len * seq_len + seq_id] =\n            relative_attention_bias_table[head_id * num_bucket + relative_buckets];\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildRelativeAttentionBias(T*                          relative_attention_bias,\n                                      const T*                    relative_attention_bias_table,\n                                      const int                   head_num,\n                                      const int                   seq_len,\n                                      const int                   num_bucket,\n                                      const bool                  is_bidirectional,\n                                      const int                   max_distance,\n                                      const PositionEmbeddingType position_embedding_type,\n                                      cudaStream_t                stream)\n{\n    if (position_embedding_type == PositionEmbeddingType::absolute) {\n        return;\n    }\n    dim3 grid(head_num);\n    dim3 block(256);\n    buildRelativeAttentionBias<<<grid, block, 0, stream>>>(relative_attention_bias,\n                                                           relative_attention_bias_table,\n                                                           head_num,\n                                                           seq_len,\n                                                           num_bucket,\n                                                           is_bidirectional,\n                                                           max_distance);\n}\n\ntemplate void invokeBuildRelativeAttentionBias(float*                      relative_attention_bias,\n                                               const float*                relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               cudaStream_t                stream);\n\ntemplate void invokeBuildRelativeAttentionBias(half*                       relative_attention_bias,\n                                               const half*                 relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               cudaStream_t                stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeBuildRelativeAttentionBias(__nv_bfloat16*              relative_attention_bias,\n                                               const __nv_bfloat16*        relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               cudaStream_t                stream);\n#endif\n\n#ifdef ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void getLastTokenDequantize(getLastTokenDequantizeParam<T_OUT, T_IN> param)\n{\n    param.output[blockIdx.x * param.d_model + threadIdx.x] =\n        (T_OUT)((float)param.input[blockIdx.x * param.max_seq_len * param.d_model + threadIdx.x]\n                * __ldg(param.input_scale));\n}\n\ntemplate<typename T_OUT, typename T_IN>\nvoid invokeGetLastTokenDequantize(getLastTokenDequantizeParam<T_OUT, T_IN> param)\n{\n    FT_CHECK(param.d_model <= 1024);\n    getLastTokenDequantize<T_OUT, T_IN><<<param.batch_size, param.d_model, 0, param.stream>>>(param);\n}\n\ntemplate void invokeGetLastTokenDequantize<__nv_bfloat16, __nv_fp8_e4m3>(\n    getLastTokenDequantizeParam<__nv_bfloat16, __nv_fp8_e4m3> param);\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\n__global__ void quantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<T_OUT, T_IN, quantize_mode> param)\n{\n    for (int i = threadIdx.x; i < param.d_model; i += blockDim.x) {\n        int padded_row_id = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n        if (quantize_mode == QUANTIZE_MODE::PER_TENSOR) {\n            param.dst[padded_row_id * param.d_model + i] =\n                (T_OUT)((float)param.src[blockIdx.x * param.d_model + i] * __ldg(param.scale));\n        }\n        else if (quantize_mode == QUANTIZE_MODE::PER_CHANNEL) {\n            param.dst[padded_row_id * param.d_model + i] =\n                (T_OUT)((float)param.src[blockIdx.x * param.d_model + i] * __ldg(param.scale + i));\n        }\n    }\n}\n\ntemplate<>\n__global__ void\nquantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<half, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR> param)\n{\n    int padded_row_id = blockIdx.x + (param.padding_offset == nullptr ? 0 : __ldg(&param.padding_offset[blockIdx.x]));\n    __nv_fp8x4_e4m3* src_ptr = ((__nv_fp8x4_e4m3*)param.src) + blockIdx.x * (param.d_model / 4);\n    half2*           dst_ptr = ((half2*)param.dst) + padded_row_id * (param.d_model / 2);\n    half2            scale   = cuda_cast<half2>(__ldg(param.scale));\n    for (int i = threadIdx.x; i < param.d_model / 4; i += blockDim.x) {\n        half2 val_0;\n        half2 val_1;\n        fp8x4_e4m3_to_half2(&val_0, &val_1, src_ptr + i);\n\n        val_0 = hmul2(val_0, scale);\n        val_1 = hmul2(val_1, scale);\n\n        dst_ptr[2 * i + 0] = val_0;\n        dst_ptr[2 * i + 1] = val_1;\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\nvoid invokeQuantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<T_OUT, T_IN, quantize_mode> param)\n{\n    dim3 grid(param.token_num);\n    dim3 block(param.d_model);\n    FT_CHECK(block.x <= 1024);\n    if (block.x % 4 == 0) {\n        block.x /= 4;\n    }\n    quantizeMatrixRebuildPadding<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void invokeQuantizeMatrixRebuildPadding<half, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR>(\n    QuantizeMatrixRebuildPaddingParam<half, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR> param);\n\n#endif\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"bert_preprocess_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_bf16_fallbacks.cuh\"\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n__global__ void getPaddingOffsetAndCuSeqLensKernel(size_t*    h_valid_word_num,\n                                                   int*       tmp_mask_offset,\n                                                   int*       cu_seqlens,\n                                                   const int* sequence_length,\n                                                   const int  batch_size,\n                                                   const int  max_seq_len)\n{\n    // do cumulated sum\n    int        total_seq_len        = 0;\n    int        cum_offset           = 0;\n    int        index                = 0;\n    const bool calculate_cu_seqlens = cu_seqlens != nullptr;\n    for (int i = 0; i < batch_size; i++) {\n        const int seq_len = sequence_length[i];\n        if (calculate_cu_seqlens) {\n            cu_seqlens[i] = total_seq_len;\n        }\n        for (int j = 0; j < seq_len; j++) {\n            tmp_mask_offset[index] = cum_offset;\n            index++;\n        }\n        cum_offset += max_seq_len - seq_len;\n        total_seq_len += seq_len;\n    }\n    if (calculate_cu_seqlens) {\n        cu_seqlens[batch_size] = total_seq_len;\n    }\n    h_valid_word_num[0] = (size_t)total_seq_len;\n}\n\nvoid invokeGetPaddingOffsetAndCuSeqLens(size_t*      h_pinned_token_num,\n                                        size_t*      h_token_num,\n                                        int*         tmp_mask_offset,\n                                        int*         cu_seqlens,\n                                        const int*   sequence_lengths,\n                                        const int    batch_size,\n                                        const int    max_seq_len,\n                                        hipStream_t stream)\n{\n    h_pinned_token_num[0] = 0;\n    getPaddingOffsetAndCuSeqLensKernel<<<1, 1, 0, stream>>>(\n        h_pinned_token_num, tmp_mask_offset, cu_seqlens, sequence_lengths, batch_size, max_seq_len);\n    while (((volatile size_t*)h_pinned_token_num)[0] == 0) {};\n    h_token_num[0] = h_pinned_token_num[0];\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\n__global__ void buildEncoderAttentionMaskKernel(T* attention_mask, const int* sequence_lengths, const int max_seq_len)\n{\n    // sequence_lengths: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len, max_seq_len]\n    attention_mask += blockIdx.x * max_seq_len * max_seq_len;\n    const int length = sequence_lengths[blockIdx.x];\n    for (int i = threadIdx.x; i < max_seq_len * max_seq_len; i += blockDim.x) {\n        // int row_id = i / max_seq_len;\n        int col_id = i % max_seq_len;\n        // if (row_id < length && col_id < length) {\n        // TODO (bhsueh) check this modification is ok or not on other rmodel\n        if (col_id < length) {\n            attention_mask[i] = (T)(1.0f);\n        }\n        else {\n            attention_mask[i] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildEncoderAttentionMask(\n    T* attention_mask, const int* sequence_lengths, const int batch_size, const int max_seq_len, hipStream_t stream)\n{\n    buildEncoderAttentionMaskKernel<<<batch_size, 256, 0, stream>>>(attention_mask, sequence_lengths, max_seq_len);\n}\n\ntemplate void invokeBuildEncoderAttentionMask(float*       attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              hipStream_t stream);\ntemplate void invokeBuildEncoderAttentionMask(half*        attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              hipStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeBuildEncoderAttentionMask(__hip_fp8_e4m3_fnuz* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              hipStream_t   stream);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeBuildEncoderAttentionMask(__hip_bfloat16* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              hipStream_t   stream);\n#endif\n\n__global__ void getTrtPaddingOffsetKernel(int* trt_mha_padding_offset, const int* sequence_length, const int batch_size)\n{\n    // use for get tensorrt fused mha padding offset\n    // when we remove the padding\n\n    extern __shared__ int tmp_offset[];\n    if (threadIdx.x == 0) {\n        tmp_offset[0] = 0;\n        for (int i = 0; i < batch_size; i++) {\n            tmp_offset[i + 1] = tmp_offset[i] + sequence_length[i];\n        }\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < batch_size + 1; i += blockDim.x) {\n        trt_mha_padding_offset[i] = tmp_offset[i];\n    }\n}\n\nvoid invokeGetTrtPaddingOffset(int*         trt_mha_padding_offset,\n                               const int*   sequence_length,\n                               const int    batch_size,\n                               hipStream_t stream)\n{\n    getTrtPaddingOffsetKernel<<<1, 256, sizeof(int) * (batch_size + 1), stream>>>(\n        trt_mha_padding_offset, sequence_length, batch_size);\n}\n\n__global__ void getTrtPaddingOffsetKernel(int*       trt_mha_padding_offset,\n                                          const int* sequence_length,\n                                          const int  request_batch_size,\n                                          const int  request_seq_len)\n{\n    // use for get tensorrt fused mha padding offset\n    // when we keep the padding\n\n    extern __shared__ int tmp_offset[];\n    if (threadIdx.x == 0) {\n        tmp_offset[0] = 0;\n        for (int i = 0; i < request_batch_size; i++) {\n            tmp_offset[i * 2 + 1] = tmp_offset[i * 2] + sequence_length[i];\n            tmp_offset[i * 2 + 2] = request_seq_len * (i + 1);\n        }\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < 2 * request_batch_size + 1; i += blockDim.x) {\n        trt_mha_padding_offset[i] = tmp_offset[i];\n    }\n}\n\nvoid invokeGetTrtPaddingOffset(int*         trt_mha_padding_offset,\n                               const int*   sequence_length,\n                               const int    request_batch_size,\n                               const int    request_seq_len,\n                               hipStream_t stream)\n{\n    getTrtPaddingOffsetKernel<<<1, 256, sizeof(int) * (2 * request_batch_size + 1), stream>>>(\n        trt_mha_padding_offset, sequence_length, request_batch_size, request_seq_len);\n}\n\ntemplate<typename T>\n__global__ void rebuild_sequence_length_padding(const T* src, T* dst, const int* padding_offset, const int n)\n{\n    const int tid        = threadIdx.x;\n    const int bid        = blockIdx.x;\n    const int dst_seq_id = bid + padding_offset[bid];\n    const int src_seq_id = bid;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        dst[dst_seq_id * n + i] = src[src_seq_id * n + i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRebuildPadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, hipStream_t stream)\n{\n    // src: [token_num, hidden_dim]\n    // dst: [batch_size*max_seq_len, hidden_dim]\n    rebuild_sequence_length_padding<<<token_num, 256, 0, stream>>>(src, dst, padding_offset, hidden_dim);\n}\n\ntemplate<typename T>\nvoid invokeRebuildPadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, hipStream_t stream);\ntemplate void invokeRebuildPadding(float*       dst,\n                                   const float* src,\n                                   const int*   padding_offset,\n                                   const int    token_num,\n                                   const int    hidden_dim,\n                                   hipStream_t stream);\ntemplate void invokeRebuildPadding(half*        dst,\n                                   const half*  src,\n                                   const int*   padding_offset,\n                                   const int    token_num,\n                                   const int    hidden_dim,\n                                   hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeRebuildPadding(__hip_bfloat16*       dst,\n                                   const __hip_bfloat16* src,\n                                   const int*           padding_offset,\n                                   const int            token_num,\n                                   const int            hidden_dim,\n                                   hipStream_t         stream);\n#endif  // ENABLE_BF16\n\n#ifdef ENABLE_FP8\ntemplate void invokeRebuildPadding(__hip_fp8_e4m3_fnuz*       dst,\n                                   const __hip_fp8_e4m3_fnuz* src,\n                                   const int*           padding_offset,\n                                   const int            token_num,\n                                   const int            hidden_dim,\n                                   hipStream_t         stream);\n#endif  // ENABLE_FP8\n\ntemplate<typename T>\n__global__ void remove_padding(T* tgt, const T* src, const int* padding_offset, const int n)\n{\n    const int tid        = threadIdx.x;\n    const int bid        = blockIdx.x;\n    const int src_seq_id = bid + padding_offset[bid];\n    const int tgt_seq_id = bid;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        tgt[tgt_seq_id * n + i] = src[src_seq_id * n + i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeRemovePadding(\n    T* dst, const T* src, const int* padding_offset, const int token_num, const int hidden_dim, hipStream_t stream)\n{\n    remove_padding<<<token_num, 256, 0, stream>>>(dst, src, padding_offset, hidden_dim);\n}\n\ntemplate void invokeRemovePadding(float*       dst,\n                                  const float* src,\n                                  const int*   padding_offset,\n                                  const int    token_num,\n                                  const int    hidden_dim,\n                                  hipStream_t stream);\n\ntemplate void invokeRemovePadding(half*        dst,\n                                  const half*  src,\n                                  const int*   padding_offset,\n                                  const int    token_num,\n                                  const int    hidden_dim,\n                                  hipStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeRemovePadding(__hip_fp8_e4m3_fnuz*       dst,\n                                  const __hip_fp8_e4m3_fnuz* src,\n                                  const int*           padding_offset,\n                                  const int            token_num,\n                                  const int            hidden_dim,\n                                  hipStream_t         stream);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeRemovePadding(__hip_bfloat16*       dst,\n                                  const __hip_bfloat16* src,\n                                  const int*           padding_offset,\n                                  const int            token_num,\n                                  const int            hidden_dim,\n                                  hipStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void buildRelativeAttentionBias(T*         relative_attention_bias,\n                                           const T*   relative_attention_bias_table,\n                                           const int  head_num,\n                                           const int  seq_len,\n                                           const int  num_bucket,\n                                           const bool is_bidirectional,\n                                           const int  max_distance)\n{\n\n    const int head_id = blockIdx.x;\n    for (int seq_id = threadIdx.x; seq_id < seq_len * seq_len; seq_id += blockDim.x) {\n        int row_id = seq_id / seq_len;\n        int col_id = seq_id % seq_len;\n\n        int relative_position = col_id - row_id;\n\n        int relative_buckets = 0;\n        int tmp_num_bucket   = num_bucket;\n        if (is_bidirectional) {\n            tmp_num_bucket /= 2;\n            if (relative_position > 0) {\n                relative_buckets += tmp_num_bucket;\n            }\n            else {\n                relative_position *= -1;\n            }\n        }\n        else {\n            relative_position = abs(relative_position);\n        }\n\n        int  max_exact = tmp_num_bucket / 2;\n        bool is_small  = relative_position < max_exact;\n\n        int relative_position_if_large =\n            max_exact\n            + (int)(logf(relative_position * 1.0f / max_exact) / logf((float)max_distance / max_exact)\n                    * (tmp_num_bucket - max_exact));\n\n        relative_position_if_large = min(relative_position_if_large, tmp_num_bucket - 1);\n\n        relative_buckets += is_small ? relative_position : relative_position_if_large;\n\n        relative_attention_bias[head_id * seq_len * seq_len + seq_id] =\n            relative_attention_bias_table[head_id * num_bucket + relative_buckets];\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildRelativeAttentionBias(T*                          relative_attention_bias,\n                                      const T*                    relative_attention_bias_table,\n                                      const int                   head_num,\n                                      const int                   seq_len,\n                                      const int                   num_bucket,\n                                      const bool                  is_bidirectional,\n                                      const int                   max_distance,\n                                      const PositionEmbeddingType position_embedding_type,\n                                      hipStream_t                stream)\n{\n    if (position_embedding_type == PositionEmbeddingType::absolute) {\n        return;\n    }\n    dim3 grid(head_num);\n    dim3 block(256);\n    buildRelativeAttentionBias<<<grid, block, 0, stream>>>(relative_attention_bias,\n                                                           relative_attention_bias_table,\n                                                           head_num,\n                                                           seq_len,\n                                                           num_bucket,\n                                                           is_bidirectional,\n                                                           max_distance);\n}\n\ntemplate void invokeBuildRelativeAttentionBias(float*                      relative_attention_bias,\n                                               const float*                relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               hipStream_t                stream);\n\ntemplate void invokeBuildRelativeAttentionBias(half*                       relative_attention_bias,\n                                               const half*                 relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               hipStream_t                stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeBuildRelativeAttentionBias(__hip_bfloat16*              relative_attention_bias,\n                                               const __hip_bfloat16*        relative_attention_bias_table,\n                                               const int                   head_num,\n                                               const int                   seq_len,\n                                               const int                   num_bucket,\n                                               const bool                  is_bidirectional,\n                                               const int                   max_distance,\n                                               const PositionEmbeddingType position_embedding_type,\n                                               hipStream_t                stream);\n#endif\n\n#ifdef ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void getLastTokenDequantize(getLastTokenDequantizeParam<T_OUT, T_IN> param)\n{\n    param.output[blockIdx.x * param.d_model + threadIdx.x] =\n        (T_OUT)((float)param.input[blockIdx.x * param.max_seq_len * param.d_model + threadIdx.x]\n                * __ldg(param.input_scale));\n}\n\ntemplate<typename T_OUT, typename T_IN>\nvoid invokeGetLastTokenDequantize(getLastTokenDequantizeParam<T_OUT, T_IN> param)\n{\n    FT_CHECK(param.d_model <= 1024);\n    getLastTokenDequantize<T_OUT, T_IN><<<param.batch_size, param.d_model, 0, param.stream>>>(param);\n}\n\ntemplate void invokeGetLastTokenDequantize<__hip_bfloat16, __hip_fp8_e4m3_fnuz>(\n    getLastTokenDequantizeParam<__hip_bfloat16, __hip_fp8_e4m3_fnuz> param);\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\n__global__ void quantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<T_OUT, T_IN, quantize_mode> param)\n{\n    for (int i = threadIdx.x; i < param.d_model; i += blockDim.x) {\n        int padded_row_id = blockIdx.x + (param.padding_offset == nullptr ? 0 : param.padding_offset[blockIdx.x]);\n        if (quantize_mode == QUANTIZE_MODE::PER_TENSOR) {\n            param.dst[padded_row_id * param.d_model + i] =\n                (T_OUT)((float)param.src[blockIdx.x * param.d_model + i] * __ldg(param.scale));\n        }\n        else if (quantize_mode == QUANTIZE_MODE::PER_CHANNEL) {\n            param.dst[padded_row_id * param.d_model + i] =\n                (T_OUT)((float)param.src[blockIdx.x * param.d_model + i] * __ldg(param.scale + i));\n        }\n    }\n}\n\ntemplate<>\n__global__ void\nquantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<half, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR> param)\n{\n    int padded_row_id = blockIdx.x + (param.padding_offset == nullptr ? 0 : __ldg(&param.padding_offset[blockIdx.x]));\n    __hip_fp8x4_e4m3_fnuz* src_ptr = ((__hip_fp8x4_e4m3_fnuz*)param.src) + blockIdx.x * (param.d_model / 4);\n    half2*           dst_ptr = ((half2*)param.dst) + padded_row_id * (param.d_model / 2);\n    half2            scale   = cuda_cast<half2>(__ldg(param.scale));\n    for (int i = threadIdx.x; i < param.d_model / 4; i += blockDim.x) {\n        half2 val_0;\n        half2 val_1;\n        fp8x4_e4m3_to_half2(&val_0, &val_1, src_ptr + i);\n\n        val_0 = hmul2(val_0, scale);\n        val_1 = hmul2(val_1, scale);\n\n        dst_ptr[2 * i + 0] = val_0;\n        dst_ptr[2 * i + 1] = val_1;\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\nvoid invokeQuantizeMatrixRebuildPadding(QuantizeMatrixRebuildPaddingParam<T_OUT, T_IN, quantize_mode> param)\n{\n    dim3 grid(param.token_num);\n    dim3 block(param.d_model);\n    FT_CHECK(block.x <= 1024);\n    if (block.x % 4 == 0) {\n        block.x /= 4;\n    }\n    quantizeMatrixRebuildPadding<<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void invokeQuantizeMatrixRebuildPadding<half, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR>(\n    QuantizeMatrixRebuildPaddingParam<half, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR> param);\n\n#endif\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/bert_preprocess_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/beam_search_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__device__ __forceinline__ T apply_length_penalty(T log_prob, int length, float length_penalty)\n{\n    // score = log(prob) / (length)^length_penalty.\n    if (length_penalty == 0.0f || length == 1) {\n        return log_prob;\n    }\n    return log_prob / static_cast<T>(powf((float)length, length_penalty));\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_topK_kernel(const T*    log_probs,\n                                                                     int*        topk_tmp_id_buf,\n                                                                     T*          topk_tmp_val_buf,\n                                                                     const bool* finished,\n                                                                     const int*  sequence_lengths,\n                                                                     const int   vocab_size,\n                                                                     T           diversity_rate,\n                                                                     float       length_penalty)\n{\n    typedef cub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;  // batch beam index.\n    TopK<T, MAX_K> partial;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n#pragma unroll\n    for (int elem_id = thread_id; elem_id < vocab_size; elem_id += THREADBLOCK_SIZE) {\n        int index = elem_id + block_id * vocab_size;\n        T   score = length_penalty == 0.0f ? log_probs[index] :\n                                             apply_length_penalty(log_probs[index],\n                                                                finished[block_id] ? sequence_lengths[block_id] :\n                                                                                       sequence_lengths[block_id] + 1,\n                                                                length_penalty);\n        partial.insert(score, index);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        int index = block_id * MAX_K;\n\n#pragma unroll\n        for (int i = 0; i < MAX_K; ++i) {\n            topk_tmp_id_buf[index + i]  = total.p[i];\n            topk_tmp_val_buf[index + i] = total.u[i] + diversity_rate * (T)i;\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    const bool     IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -MAX_T_VAL;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i] = partial.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel_v2(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    typedef cub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n\n    int            tid = threadIdx.x;\n    int            bid = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    const bool     IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n    int ite = MAX_K * MAX_K / THREADBLOCK_SIZE;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int index = bid * MAX_K * MAX_K + i * THREADBLOCK_SIZE + tid;\n        partial.insert((T)topk_tmp_val_buf[index], topk_tmp_id_buf[index]);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (tid == 0) {\n#pragma unroll\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[bid * MAX_K + i] = total.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage_1_opt3(const T* __restrict log_probs,\n                                  T*          tmp_log_probs,\n                                  int*        topk_tmp_id_buf,\n                                  T*          topk_tmp_val_buf,\n                                  const bool* finished,\n                                  const int*  sequence_lengths,\n                                  const int   k,\n                                  const int   vocab_size,\n                                  const float length_penalty,\n                                  const int*  end_ids)\n{\n    typedef cub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    const int  row_id             = bid / BLOCKS_PER_BEAM_;  // row id for log_probs (batchbeam index)\n    const int  block_lane         = bid % BLOCKS_PER_BEAM_;  // block id for a beam\n    const int  tmp_log_buf_index  = row_id * vocab_size;\n    const int  tmp_topk_buf_index = row_id * BLOCKS_PER_BEAM_ * k + block_lane * k;\n    TopK_2<T>  partial;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    if (finished != nullptr && finished[row_id] == true) {\n        if (tid < k) {\n            const int index = tmp_topk_buf_index + tid;\n            if (block_lane == 0 && tid == 0) {\n                const int end_id        = end_ids[row_id / k];\n                topk_tmp_id_buf[index]  = tmp_log_buf_index + end_id;\n                topk_tmp_val_buf[index] = log_probs[tmp_log_buf_index + end_id];\n            }\n            else {\n                topk_tmp_id_buf[index]  = -1;\n                topk_tmp_val_buf[index] = -MAX_T_VAL;\n            }\n        }\n        return;\n    }\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n         elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage_2_opt3(const int* __restrict topk_tmp_id_buf,\n                                  T*             topk_tmp_val_buf,\n                                  int*           ids,\n                                  BeamHypotheses beam_hyps,\n                                  const int*     end_ids,\n                                  const int      vocab_size,\n                                  const int      k)\n{\n    const int  size      = k * k * BLOCKS_PER_BEAM_;\n    const int  tid       = threadIdx.x;\n    const int  batch_id  = blockIdx.x;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    typedef cub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n    extern __shared__ char                           array[];\n    T*                                               s_val = topk_tmp_val_buf + batch_id * size;\n    int*                                             s_id  = (int*)(array);\n\n    __shared__ int  selected_beams;\n    __shared__ bool is_stop;\n\n    if (tid == 0) {\n        selected_beams = 0;\n        is_stop        = false;\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && tid == 0) {\n            // initialize the buffer\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == k) {\n            return;\n        }\n    }\n\n    TopK_2<T> partial;\n\n    // In some cases, we may encounter k finished sentences, but scores are bad. So, the max iteration\n    // is 2*k here\n    for (int ite = 0; ite < 2 * k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE_) {\n            partial.insert(s_val[i], i);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            if (beam_hyps.num_beams != nullptr\n                && topk_tmp_id_buf[batch_id * size + total.p] % vocab_size == end_ids[batch_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (ite >= k) {\n                    s_val[total.p] = -MAX_T_VAL;\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n                    const float normed_score =\n                        apply_length_penalty(s_val[total.p], beam_hyps.step, beam_hyps.length_penalty);\n                    const int num_beam = beam_hyps.num_beams[global_batch_idx];\n                    int       beam_idx = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == k) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = k;\n                            is_stop        = true;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < k; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * k + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]     = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * k + j] = normed_score;\n                                    for (int l = 0; l < k; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * k + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset = ((batch_id + beam_hyps.ite * beam_hyps.local_batch_size) * k + beam_idx)\n                                              * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = end_ids[batch_id];\n\n                    int prev_id = (topk_tmp_id_buf[batch_id * size + total.p] / vocab_size) % k;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * k\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * k + batch_id * k + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        prev_id                                     = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * k + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    s_val[total.p] = -MAX_T_VAL;\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                }\n            }\n            else {\n                s_id[selected_beams] = total.p;\n                s_val[total.p]       = -MAX_T_VAL;\n                selected_beams++;\n            }\n        }\n        __syncthreads();\n        if (selected_beams >= k) {\n            break;\n        }\n    }\n    if (tid < k && is_stop == false) {\n        ids[batch_id * k + tid] = topk_tmp_id_buf[batch_id * size + s_id[tid]];\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE, int BLOCKS_PER_BEAM>\n__global__ void topk_stage_1_opt2_general(const T* __restrict log_probs,\n                                          T*          tmp_log_probs,\n                                          int*        topk_tmp_id_buf,\n                                          T*          topk_tmp_val_buf,\n                                          const bool* finished,\n                                          const int*  sequence_lengths,\n                                          const int   k,\n                                          const int   vocab_size,\n                                          const float length_penalty)\n{\n    const bool                                      IS_FP16   = std::is_same<T, half>::value;\n    const T                                         MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    typedef cub::BlockReduce<TopK_2<T>, BLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage    temp_storage;\n\n    const int tid                = threadIdx.x;\n    const int bid                = blockIdx.x;\n    const int row_id             = bid / BLOCKS_PER_BEAM;  // row id for log_probs\n    const int block_lane         = bid % BLOCKS_PER_BEAM;  // block id for a beam\n    const int tmp_log_buf_index  = row_id * vocab_size;\n    const int tmp_topk_buf_index = row_id * BLOCKS_PER_BEAM * k + block_lane * k;\n    TopK_2<T> partial;\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE; elem_id < vocab_size; elem_id += BLOCK_SIZE * BLOCKS_PER_BEAM) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE * BLOCKS_PER_BEAM) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE, int BLOCKS_PER_BEAM>\n__global__ void topk_stage_2_opt2_general(const int* __restrict topk_tmp_id_buf,\n                                          T*             topk_tmp_val_buf,\n                                          int*           ids,\n                                          BeamHypotheses beam_hyps,\n                                          const int*     end_ids,\n                                          const int      k,\n                                          const int      vocab_size)\n{\n    const int  size      = k * k * BLOCKS_PER_BEAM;\n    const int  tid       = threadIdx.x;\n    const int  batch_id  = blockIdx.x;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    typedef cub::BlockReduce<TopK_2<T>, BLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage    temp_storage;\n    extern __shared__ char                          array[];\n    T*                                              s_val = topk_tmp_val_buf + batch_id * size;\n    int*                                            s_id  = (int*)(array);\n\n    __shared__ int  selected_beams;\n    __shared__ bool is_stop;\n\n    if (tid == 0) {\n        selected_beams = 0;\n        is_stop        = false;\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && tid == 0) {\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == k) {\n            return;\n        }\n    }\n\n    TopK_2<T> partial;\n\n    // In some cases, we may encounter k finished sentences, but scores are bad. So, the max iteration\n    // is 2*k here\n    for (int ite = 0; ite < 2 * k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE) {\n            partial.insert(s_val[i], i);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            if (beam_hyps.num_beams != nullptr\n                && topk_tmp_id_buf[batch_id * size + total.p] % vocab_size == end_ids[batch_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (ite >= k) {\n                    s_val[total.p] = -MAX_T_VAL;\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n                    const float normed_score =\n                        apply_length_penalty(s_val[total.p], beam_hyps.step, beam_hyps.length_penalty);\n                    const int num_beam = beam_hyps.num_beams[global_batch_idx];\n                    int       beam_idx = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == k) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = k;\n                            is_stop        = true;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < k; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * k + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]     = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * k + j] = normed_score;\n                                    for (int l = 0; l < k; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * k + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset = ((batch_id + beam_hyps.ite * beam_hyps.local_batch_size) * k + beam_idx)\n                                              * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = end_ids[batch_id];\n\n                    int prev_id = (topk_tmp_id_buf[batch_id * size + total.p] / vocab_size) % k;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * k\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * k + batch_id * k + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        prev_id                                     = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * k + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    s_val[total.p] = -MAX_T_VAL;\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                }\n            }\n            else {\n                s_id[selected_beams] = total.p;\n                s_val[total.p]       = -MAX_T_VAL;\n                selected_beams++;\n            }\n        }\n        __syncthreads();\n        if (selected_beams >= k) {\n            break;\n        }\n    }\n    if (tid < k && is_stop == false) {\n        ids[batch_id * k + tid] = topk_tmp_id_buf[batch_id * size + s_id[tid]];\n    }\n}\n\n#define CASE_K_DIV(K, BLOCK_SIZE_1, BLOCK_SIZE_2)                                                                      \\\n    case K:                                                                                                            \\\n        beam_topK_kernel<T, K, BLOCK_SIZE_2><<<batch_size * beam_width, BLOCK_SIZE_2, 0, stream>>>(log_probs,          \\\n                                                                                                   topk_tmp_id_buf,    \\\n                                                                                                   topk_tmp_val_buf,   \\\n                                                                                                   finished,           \\\n                                                                                                   sequence_lengths,   \\\n                                                                                                   vocab_size,         \\\n                                                                                                   diversity_rate,     \\\n                                                                                                   length_penalty);    \\\n        if (K < 10)                                                                                                    \\\n            batch_topK_kernel<T, K, BLOCK_SIZE_1>                                                                      \\\n                <<<batch_size, BLOCK_SIZE_1, 0, stream>>>(topk_tmp_id_buf, topk_tmp_val_buf, ids);                     \\\n        else                                                                                                           \\\n            batch_topK_kernel_v2<T, K, 32><<<batch_size, 32, 0, stream>>>(topk_tmp_id_buf, topk_tmp_val_buf, ids);     \\\n        break;\n\n#define CASE_K(K, BLOCK_SIZE_1_, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_)                                                      \\\n    case K:                                                                                                            \\\n        topk_stage_1_opt3<float, BLOCK_SIZE_1_, BLOCKS_PER_BEAM_>                                                      \\\n            <<<batch_size * K * BLOCKS_PER_BEAM_, BLOCK_SIZE_1_, 0, stream>>>(log_probs,                               \\\n                                                                              temp_log_probs,                          \\\n                                                                              topk_tmp_id_buf,                         \\\n                                                                              topk_tmp_val_buf,                        \\\n                                                                              finished,                                \\\n                                                                              sequence_lengths,                        \\\n                                                                              beam_width,                              \\\n                                                                              vocab_size,                              \\\n                                                                              length_penalty,                          \\\n                                                                              end_ids);                                \\\n        topk_stage_2_opt3<float, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_>                                                      \\\n            <<<batch_size, BLOCK_SIZE_2_, K * sizeof(int), stream>>>(                                                  \\\n                topk_tmp_id_buf, topk_tmp_val_buf, ids, *beam_hyps, end_ids, vocab_size, beam_width);                  \\\n        sync_check_cuda_error();                                                                                       \\\n        break;\n\ntemplate<typename T>\nvoid invokeTopkBeamSearch(void*           workspace,\n                          size_t&         workspace_size,\n                          T*              log_probs,\n                          int*            ids,\n                          BeamHypotheses* beam_hyps,\n                          const bool*     finished,\n                          const int*      sequence_lengths,\n                          const int       batch_size,\n                          const int       beam_width,\n                          const int       vocab_size_padded_,\n                          const T         diversity_rate,\n                          const float     length_penalty,\n                          const int*      end_ids,\n                          cudaStream_t    stream)\n{\n    FT_LOG_DEBUG(\"%s\", __PRETTY_FUNCTION__);\n    // log_probs: (batch, beam, vocab) cumulative log_probs of beams ending with a token.\n    const int vocab_size = vocab_size_padded_;\n    // Beam size should be less than or equal to vocab size.\n    assert(beam_width <= vocab_size);\n    // Beam search needs the sequence lengths of beams to apply length penalty.\n    assert(length_penalty == 0.0f || sequence_lengths != nullptr);\n    const int max_block_per_beam      = 8;\n    int       temp_log_probs_buf_size = batch_size * beam_width * vocab_size;                       // type float\n    int       topk_tmp_ids_buf_size   = batch_size * beam_width * beam_width * max_block_per_beam;  // type int\n    int       topk_tmp_val_buf_size   = batch_size * beam_width * beam_width * max_block_per_beam;  // type float\n\n    // prevent memory misaligned address\n    temp_log_probs_buf_size = (int)(ceil(temp_log_probs_buf_size / 4.)) * 4;\n    topk_tmp_ids_buf_size   = (int)(ceil(topk_tmp_ids_buf_size / 4.)) * 4;\n    topk_tmp_val_buf_size   = (int)(ceil(topk_tmp_val_buf_size / 4.)) * 4;\n\n    if (workspace == nullptr) {\n        workspace_size = sizeof(float) * temp_log_probs_buf_size + sizeof(int) * topk_tmp_ids_buf_size\n                         + sizeof(float) * topk_tmp_val_buf_size;\n        return;\n    }\n    else {\n        T*   temp_log_probs   = (T*)workspace;\n        int* topk_tmp_id_buf  = (int*)(temp_log_probs + temp_log_probs_buf_size);\n        T*   topk_tmp_val_buf = (T*)(topk_tmp_id_buf + topk_tmp_ids_buf_size);\n        if (diversity_rate == 0.0f) {\n            switch (beam_width) {\n                CASE_K(1, 128, 128, 8);\n                CASE_K(4, 128, 128, 8);\n                CASE_K(10, 128, 128, 8);\n                CASE_K(16, 128, 128, 5);\n                CASE_K(32, 256, 128, 1);\n                CASE_K(64, 256, 256, 1);\n                default:\n                    topk_stage_1_opt2_general<T, 128, 1>\n                        <<<batch_size * beam_width * 1, 128, 0, stream>>>(log_probs,\n                                                                          temp_log_probs,\n                                                                          topk_tmp_id_buf,\n                                                                          topk_tmp_val_buf,\n                                                                          finished,\n                                                                          sequence_lengths,\n                                                                          beam_width,\n                                                                          vocab_size,\n                                                                          length_penalty);\n                    topk_stage_2_opt2_general<T, 128, 1>\n                        <<<batch_size,\n                           128,\n                           beam_width * beam_width * 1 * sizeof(float) + beam_width * sizeof(int),\n                           stream>>>(\n                            topk_tmp_id_buf, topk_tmp_val_buf, ids, *beam_hyps, end_ids, beam_width, vocab_size);\n                    break;\n            }\n        }\n        else {\n            switch (beam_width) {\n                CASE_K_DIV(1, 256, 256);\n                CASE_K_DIV(4, 256, 256);\n                CASE_K_DIV(16, 256, 64);\n                CASE_K_DIV(32, 256, 64);\n                CASE_K_DIV(64, 256, 64);\n                default:\n                    FT_CHECK_WITH_INFO(false, fmtstr(\"Topk kernel does not support beamwidth = %d \\n\", beam_width));\n                    break;\n            }\n        }\n        return;\n    }\n}\n\n#undef CASE_K\n#undef CASE_K_DIV\n\ntemplate void invokeTopkBeamSearch(void*           workspace,\n                                   size_t&         workspace_size,\n                                   float*          log_probs,\n                                   int*            ids,\n                                   BeamHypotheses* beam_hyps,\n                                   const bool*     finished,\n                                   const int*      sequence_lengths,\n                                   const int       batch_size,\n                                   const int       beam_width,\n                                   const int       vocab_size_padded_,\n                                   const float     diversity_rate,\n                                   const float     length_penalty,\n                                   const int*      end_ids,\n                                   cudaStream_t    stream);\n\ntemplate<typename T>\n__global__ void tileEncoderResults(T*         tiled_output,\n                                   int*       tiled_sequence_length,\n                                   const T*   output,\n                                   const int* sequence_length,\n                                   const uint batch_size,\n                                   const uint beam_width,\n                                   const uint d_model)\n{\n    if (blockIdx.x == 0) {\n        for (uint i = threadIdx.x; i < batch_size * beam_width; i += blockDim.x) {\n            tiled_sequence_length[i] = sequence_length[i / beam_width];\n        }\n    }\n\n    int tgt_offset =\n        blockIdx.x * gridDim.y * gridDim.z * d_model + blockIdx.y * gridDim.z * d_model + blockIdx.z * d_model;\n    int src_offset = blockIdx.x * gridDim.z * d_model + blockIdx.z * d_model;\n    for (uint i = threadIdx.x; i < d_model; i += blockDim.x) {\n        tiled_output[i + tgt_offset] = output[i + src_offset];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTileEncoderResults(T*           tiled_output,\n                              int*         tiled_sequence_length,\n                              const T*     output,\n                              const int*   sequence_length,\n                              const size_t batch_size,\n                              const size_t beam_width,\n                              const size_t mem_max_seq_len,\n                              const size_t d_model,\n                              cudaStream_t stream)\n{\n    // tiled_output: [batch_size, beam_width, mem_max_seq_len, d_model]\n    // tiled_sequence_length: [batch_size, beam_width]\n\n    // output: [batch_size, mem_max_seq_len, d_model]\n    // sequence_length [batch_size]\n\n    dim3 grid(batch_size, beam_width, mem_max_seq_len);\n    bool is_half2 = (std::is_same<T, half>::value) && (d_model % 2 == 0);\n\n    if (is_half2) {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(512, (int)(d_model / 2)));\n        tileEncoderResults<T2><<<grid, block, 0, stream>>>((T2*)tiled_output,\n                                                           tiled_sequence_length,\n                                                           (const T2*)output,\n                                                           sequence_length,\n                                                           batch_size,\n                                                           beam_width,\n                                                           d_model / 2);\n    }\n    else {\n        dim3 block(min(512, (int)d_model));\n        tileEncoderResults<T><<<grid, block, 0, stream>>>(\n            tiled_output, tiled_sequence_length, output, sequence_length, batch_size, beam_width, d_model);\n    }\n}\n\ntemplate void invokeTileEncoderResults(float*       tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const float* output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       cudaStream_t stream);\n\ntemplate void invokeTileEncoderResults(half*        tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const half*  output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       cudaStream_t stream);\n\ntemplate void invokeTileEncoderResults(half2*       tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const half2* output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeTileEncoderResults(__nv_bfloat16*       tiled_output,\n                                       int*                 tiled_sequence_length,\n                                       const __nv_bfloat16* output,\n                                       const int*           sequence_length,\n                                       const size_t         batch_size,\n                                       const size_t         beam_width,\n                                       const size_t         mem_max_seq_len,\n                                       const size_t         d_model,\n                                       cudaStream_t         stream);\n#endif\n\n__global__ void insertUnfinishedPath(BeamHypotheses beam_hyps,\n                                     const bool*    finished,\n                                     const float*   cum_log_probs,\n                                     const int      batch_size,\n                                     const int      beam_width)\n{\n    const int bid           = blockIdx.x;\n    const int tgt_start_idx = beam_hyps.num_beams[bid];\n    if (beam_hyps.is_done[bid]) {\n        return;\n    }\n    for (int i = 0; i < beam_width; i++) {\n        if (threadIdx.x == 0) {\n            const int src_beam_idx = bid * beam_width + i;\n            const int tgt_beam_idx = bid * beam_width * 2 + i + tgt_start_idx;\n\n            const int length = beam_hyps.sequence_lengths_src[src_beam_idx];\n\n            beam_hyps.output_ids_tgt[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + length] =\n                beam_hyps.output_ids_src[length * batch_size * beam_width + src_beam_idx];\n            if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                beam_hyps.log_probs[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + length] =\n                    beam_hyps.log_probs_src[length * batch_size * beam_width + src_beam_idx];\n            }\n            int prev_id = beam_hyps.parent_ids_src[length * batch_size * beam_width + src_beam_idx];\n            for (int j = length - 1; j >= 0; j--) {\n                // output_ids_tgt need to use max_seq_len + 1 because its shape is\n                // [bs, beam_width, max_seq_len + 1]\n                beam_hyps.output_ids_tgt[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + j] =\n                    beam_hyps.output_ids_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n                if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                    beam_hyps.log_probs[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + j] =\n                        beam_hyps.log_probs_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n                }\n                prev_id = beam_hyps.parent_ids_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n            }\n            beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = length;\n\n            beam_hyps.normed_scores[tgt_beam_idx] = apply_length_penalty(\n                cum_log_probs[src_beam_idx], finished[src_beam_idx] ? length + 1 : length, beam_hyps.length_penalty);\n            beam_hyps.cum_log_probs[tgt_beam_idx] = cum_log_probs[src_beam_idx];\n\n            beam_hyps.num_beams[bid]++;\n        }\n    }\n}\n\nvoid invokeInsertUnfinishedPath(BeamHypotheses beam_hyps,\n                                const bool*    finished,\n                                const float*   cum_log_probs,\n                                const int      batch_size,\n                                const int      beam_width,\n                                cudaStream_t   stream)\n{\n    insertUnfinishedPath<<<batch_size, 256, 0, stream>>>(beam_hyps, finished, cum_log_probs, batch_size, beam_width);\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/beam_search_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/logger.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__device__ __forceinline__ T apply_length_penalty(T log_prob, int length, float length_penalty)\n{\n    // score = log(prob) / (length)^length_penalty.\n    if (length_penalty == 0.0f || length == 1) {\n        return log_prob;\n    }\n    return log_prob / static_cast<T>(powf((float)length, length_penalty));\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_topK_kernel(const T*    log_probs,\n                                                                     int*        topk_tmp_id_buf,\n                                                                     T*          topk_tmp_val_buf,\n                                                                     const bool* finished,\n                                                                     const int*  sequence_lengths,\n                                                                     const int   vocab_size,\n                                                                     T           diversity_rate,\n                                                                     float       length_penalty)\n{\n    typedef hipcub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;  // batch beam index.\n    TopK<T, MAX_K> partial;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n#pragma unroll\n    for (int elem_id = thread_id; elem_id < vocab_size; elem_id += THREADBLOCK_SIZE) {\n        int index = elem_id + block_id * vocab_size;\n        T   score = length_penalty == 0.0f ? log_probs[index] :\n                                             apply_length_penalty(log_probs[index],\n                                                                finished[block_id] ? sequence_lengths[block_id] :\n                                                                                       sequence_lengths[block_id] + 1,\n                                                                length_penalty);\n        partial.insert(score, index);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        int index = block_id * MAX_K;\n\n#pragma unroll\n        for (int i = 0; i < MAX_K; ++i) {\n            topk_tmp_id_buf[index + i]  = total.p[i];\n            topk_tmp_val_buf[index + i] = total.u[i] + diversity_rate * (T)i;\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    const bool     IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -MAX_T_VAL;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i] = partial.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel_v2(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    typedef hipcub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n\n    int            tid = threadIdx.x;\n    int            bid = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    const bool     IS_FP16   = std::is_same<T, half>::value;\n    const T        MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n    int ite = MAX_K * MAX_K / THREADBLOCK_SIZE;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int index = bid * MAX_K * MAX_K + i * THREADBLOCK_SIZE + tid;\n        partial.insert((T)topk_tmp_val_buf[index], topk_tmp_id_buf[index]);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (tid == 0) {\n#pragma unroll\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[bid * MAX_K + i] = total.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage_1_opt3(const T* __restrict log_probs,\n                                  T*          tmp_log_probs,\n                                  int*        topk_tmp_id_buf,\n                                  T*          topk_tmp_val_buf,\n                                  const bool* finished,\n                                  const int*  sequence_lengths,\n                                  const int   k,\n                                  const int   vocab_size,\n                                  const float length_penalty,\n                                  const int*  end_ids)\n{\n    typedef hipcub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    const int  row_id             = bid / BLOCKS_PER_BEAM_;  // row id for log_probs (batchbeam index)\n    const int  block_lane         = bid % BLOCKS_PER_BEAM_;  // block id for a beam\n    const int  tmp_log_buf_index  = row_id * vocab_size;\n    const int  tmp_topk_buf_index = row_id * BLOCKS_PER_BEAM_ * k + block_lane * k;\n    TopK_2<T>  partial;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    if (finished != nullptr && finished[row_id] == true) {\n        if (tid < k) {\n            const int index = tmp_topk_buf_index + tid;\n            if (block_lane == 0 && tid == 0) {\n                const int end_id        = end_ids[row_id / k];\n                topk_tmp_id_buf[index]  = tmp_log_buf_index + end_id;\n                topk_tmp_val_buf[index] = log_probs[tmp_log_buf_index + end_id];\n            }\n            else {\n                topk_tmp_id_buf[index]  = -1;\n                topk_tmp_val_buf[index] = -MAX_T_VAL;\n            }\n        }\n        return;\n    }\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n         elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage_2_opt3(const int* __restrict topk_tmp_id_buf,\n                                  T*             topk_tmp_val_buf,\n                                  int*           ids,\n                                  BeamHypotheses beam_hyps,\n                                  const int*     end_ids,\n                                  const int      vocab_size,\n                                  const int      k)\n{\n    const int  size      = k * k * BLOCKS_PER_BEAM_;\n    const int  tid       = threadIdx.x;\n    const int  batch_id  = blockIdx.x;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    typedef hipcub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n    extern __shared__ char                           array[];\n    T*                                               s_val = topk_tmp_val_buf + batch_id * size;\n    int*                                             s_id  = (int*)(array);\n\n    __shared__ int  selected_beams;\n    __shared__ bool is_stop;\n\n    if (tid == 0) {\n        selected_beams = 0;\n        is_stop        = false;\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && tid == 0) {\n            // initialize the buffer\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == k) {\n            return;\n        }\n    }\n\n    TopK_2<T> partial;\n\n    // In some cases, we may encounter k finished sentences, but scores are bad. So, the max iteration\n    // is 2*k here\n    for (int ite = 0; ite < 2 * k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE_) {\n            partial.insert(s_val[i], i);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            if (beam_hyps.num_beams != nullptr\n                && topk_tmp_id_buf[batch_id * size + total.p] % vocab_size == end_ids[batch_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (ite >= k) {\n                    s_val[total.p] = -MAX_T_VAL;\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n                    const float normed_score =\n                        apply_length_penalty(s_val[total.p], beam_hyps.step, beam_hyps.length_penalty);\n                    const int num_beam = beam_hyps.num_beams[global_batch_idx];\n                    int       beam_idx = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == k) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = k;\n                            is_stop        = true;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < k; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * k + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]     = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * k + j] = normed_score;\n                                    for (int l = 0; l < k; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * k + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset = ((batch_id + beam_hyps.ite * beam_hyps.local_batch_size) * k + beam_idx)\n                                              * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = end_ids[batch_id];\n\n                    int prev_id = (topk_tmp_id_buf[batch_id * size + total.p] / vocab_size) % k;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * k\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * k + batch_id * k + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        prev_id                                     = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * k + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    s_val[total.p] = -MAX_T_VAL;\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                }\n            }\n            else {\n                s_id[selected_beams] = total.p;\n                s_val[total.p]       = -MAX_T_VAL;\n                selected_beams++;\n            }\n        }\n        __syncthreads();\n        if (selected_beams >= k) {\n            break;\n        }\n    }\n    if (tid < k && is_stop == false) {\n        ids[batch_id * k + tid] = topk_tmp_id_buf[batch_id * size + s_id[tid]];\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE, int BLOCKS_PER_BEAM>\n__global__ void topk_stage_1_opt2_general(const T* __restrict log_probs,\n                                          T*          tmp_log_probs,\n                                          int*        topk_tmp_id_buf,\n                                          T*          topk_tmp_val_buf,\n                                          const bool* finished,\n                                          const int*  sequence_lengths,\n                                          const int   k,\n                                          const int   vocab_size,\n                                          const float length_penalty)\n{\n    const bool                                      IS_FP16   = std::is_same<T, half>::value;\n    const T                                         MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    typedef hipcub::BlockReduce<TopK_2<T>, BLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage    temp_storage;\n\n    const int tid                = threadIdx.x;\n    const int bid                = blockIdx.x;\n    const int row_id             = bid / BLOCKS_PER_BEAM;  // row id for log_probs\n    const int block_lane         = bid % BLOCKS_PER_BEAM;  // block id for a beam\n    const int tmp_log_buf_index  = row_id * vocab_size;\n    const int tmp_topk_buf_index = row_id * BLOCKS_PER_BEAM * k + block_lane * k;\n    TopK_2<T> partial;\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE; elem_id < vocab_size; elem_id += BLOCK_SIZE * BLOCKS_PER_BEAM) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE * BLOCKS_PER_BEAM) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE, int BLOCKS_PER_BEAM>\n__global__ void topk_stage_2_opt2_general(const int* __restrict topk_tmp_id_buf,\n                                          T*             topk_tmp_val_buf,\n                                          int*           ids,\n                                          BeamHypotheses beam_hyps,\n                                          const int*     end_ids,\n                                          const int      k,\n                                          const int      vocab_size)\n{\n    const int  size      = k * k * BLOCKS_PER_BEAM;\n    const int  tid       = threadIdx.x;\n    const int  batch_id  = blockIdx.x;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    typedef hipcub::BlockReduce<TopK_2<T>, BLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage    temp_storage;\n    extern __shared__ char                          array[];\n    T*                                              s_val = topk_tmp_val_buf + batch_id * size;\n    int*                                            s_id  = (int*)(array);\n\n    __shared__ int  selected_beams;\n    __shared__ bool is_stop;\n\n    if (tid == 0) {\n        selected_beams = 0;\n        is_stop        = false;\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && tid == 0) {\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == k) {\n            return;\n        }\n    }\n\n    TopK_2<T> partial;\n\n    // In some cases, we may encounter k finished sentences, but scores are bad. So, the max iteration\n    // is 2*k here\n    for (int ite = 0; ite < 2 * k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE) {\n            partial.insert(s_val[i], i);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            if (beam_hyps.num_beams != nullptr\n                && topk_tmp_id_buf[batch_id * size + total.p] % vocab_size == end_ids[batch_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (ite >= k) {\n                    s_val[total.p] = -MAX_T_VAL;\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + batch_id;\n                    const float normed_score =\n                        apply_length_penalty(s_val[total.p], beam_hyps.step, beam_hyps.length_penalty);\n                    const int num_beam = beam_hyps.num_beams[global_batch_idx];\n                    int       beam_idx = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == k) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = k;\n                            is_stop        = true;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < k; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * k + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]     = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * k + j] = normed_score;\n                                    for (int l = 0; l < k; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * k + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset = ((batch_id + beam_hyps.ite * beam_hyps.local_batch_size) * k + beam_idx)\n                                              * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = end_ids[batch_id];\n\n                    int prev_id = (topk_tmp_id_buf[batch_id * size + total.p] / vocab_size) % k;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * k\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * k + batch_id * k + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        prev_id                                     = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * k + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    s_val[total.p] = -MAX_T_VAL;\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                }\n            }\n            else {\n                s_id[selected_beams] = total.p;\n                s_val[total.p]       = -MAX_T_VAL;\n                selected_beams++;\n            }\n        }\n        __syncthreads();\n        if (selected_beams >= k) {\n            break;\n        }\n    }\n    if (tid < k && is_stop == false) {\n        ids[batch_id * k + tid] = topk_tmp_id_buf[batch_id * size + s_id[tid]];\n    }\n}\n\n#define CASE_K_DIV(K, BLOCK_SIZE_1, BLOCK_SIZE_2)                                                                      \\\n    case K:                                                                                                            \\\n        beam_topK_kernel<T, K, BLOCK_SIZE_2><<<batch_size * beam_width, BLOCK_SIZE_2, 0, stream>>>(log_probs,          \\\n                                                                                                   topk_tmp_id_buf,    \\\n                                                                                                   topk_tmp_val_buf,   \\\n                                                                                                   finished,           \\\n                                                                                                   sequence_lengths,   \\\n                                                                                                   vocab_size,         \\\n                                                                                                   diversity_rate,     \\\n                                                                                                   length_penalty);    \\\n        if (K < 10)                                                                                                    \\\n            batch_topK_kernel<T, K, BLOCK_SIZE_1>                                                                      \\\n                <<<batch_size, BLOCK_SIZE_1, 0, stream>>>(topk_tmp_id_buf, topk_tmp_val_buf, ids);                     \\\n        else                                                                                                           \\\n            batch_topK_kernel_v2<T, K, 32><<<batch_size, 32, 0, stream>>>(topk_tmp_id_buf, topk_tmp_val_buf, ids);     \\\n        break;\n\n#define CASE_K(K, BLOCK_SIZE_1_, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_)                                                      \\\n    case K:                                                                                                            \\\n        topk_stage_1_opt3<float, BLOCK_SIZE_1_, BLOCKS_PER_BEAM_>                                                      \\\n            <<<batch_size * K * BLOCKS_PER_BEAM_, BLOCK_SIZE_1_, 0, stream>>>(log_probs,                               \\\n                                                                              temp_log_probs,                          \\\n                                                                              topk_tmp_id_buf,                         \\\n                                                                              topk_tmp_val_buf,                        \\\n                                                                              finished,                                \\\n                                                                              sequence_lengths,                        \\\n                                                                              beam_width,                              \\\n                                                                              vocab_size,                              \\\n                                                                              length_penalty,                          \\\n                                                                              end_ids);                                \\\n        topk_stage_2_opt3<float, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_>                                                      \\\n            <<<batch_size, BLOCK_SIZE_2_, K * sizeof(int), stream>>>(                                                  \\\n                topk_tmp_id_buf, topk_tmp_val_buf, ids, *beam_hyps, end_ids, vocab_size, beam_width);                  \\\n        sync_check_cuda_error();                                                                                       \\\n        break;\n\ntemplate<typename T>\nvoid invokeTopkBeamSearch(void*           workspace,\n                          size_t&         workspace_size,\n                          T*              log_probs,\n                          int*            ids,\n                          BeamHypotheses* beam_hyps,\n                          const bool*     finished,\n                          const int*      sequence_lengths,\n                          const int       batch_size,\n                          const int       beam_width,\n                          const int       vocab_size_padded_,\n                          const T         diversity_rate,\n                          const float     length_penalty,\n                          const int*      end_ids,\n                          hipStream_t    stream)\n{\n    FT_LOG_DEBUG(\"%s\", __PRETTY_FUNCTION__);\n    // log_probs: (batch, beam, vocab) cumulative log_probs of beams ending with a token.\n    const int vocab_size = vocab_size_padded_;\n    // Beam size should be less than or equal to vocab size.\n    assert(beam_width <= vocab_size);\n    // Beam search needs the sequence lengths of beams to apply length penalty.\n    assert(length_penalty == 0.0f || sequence_lengths != nullptr);\n    const int max_block_per_beam      = 8;\n    int       temp_log_probs_buf_size = batch_size * beam_width * vocab_size;                       // type float\n    int       topk_tmp_ids_buf_size   = batch_size * beam_width * beam_width * max_block_per_beam;  // type int\n    int       topk_tmp_val_buf_size   = batch_size * beam_width * beam_width * max_block_per_beam;  // type float\n\n    // prevent memory misaligned address\n    temp_log_probs_buf_size = (int)(ceil(temp_log_probs_buf_size / 4.)) * 4;\n    topk_tmp_ids_buf_size   = (int)(ceil(topk_tmp_ids_buf_size / 4.)) * 4;\n    topk_tmp_val_buf_size   = (int)(ceil(topk_tmp_val_buf_size / 4.)) * 4;\n\n    if (workspace == nullptr) {\n        workspace_size = sizeof(float) * temp_log_probs_buf_size + sizeof(int) * topk_tmp_ids_buf_size\n                         + sizeof(float) * topk_tmp_val_buf_size;\n        return;\n    }\n    else {\n        T*   temp_log_probs   = (T*)workspace;\n        int* topk_tmp_id_buf  = (int*)(temp_log_probs + temp_log_probs_buf_size);\n        T*   topk_tmp_val_buf = (T*)(topk_tmp_id_buf + topk_tmp_ids_buf_size);\n        if (diversity_rate == 0.0f) {\n            switch (beam_width) {\n                CASE_K(1, 128, 128, 8);\n                CASE_K(4, 128, 128, 8);\n                CASE_K(10, 128, 128, 8);\n                CASE_K(16, 128, 128, 5);\n                CASE_K(32, 256, 128, 1);\n                CASE_K(64, 256, 256, 1);\n                default:\n                    topk_stage_1_opt2_general<T, 128, 1>\n                        <<<batch_size * beam_width * 1, 128, 0, stream>>>(log_probs,\n                                                                          temp_log_probs,\n                                                                          topk_tmp_id_buf,\n                                                                          topk_tmp_val_buf,\n                                                                          finished,\n                                                                          sequence_lengths,\n                                                                          beam_width,\n                                                                          vocab_size,\n                                                                          length_penalty);\n                    topk_stage_2_opt2_general<T, 128, 1>\n                        <<<batch_size,\n                           128,\n                           beam_width * beam_width * 1 * sizeof(float) + beam_width * sizeof(int),\n                           stream>>>(\n                            topk_tmp_id_buf, topk_tmp_val_buf, ids, *beam_hyps, end_ids, beam_width, vocab_size);\n                    break;\n            }\n        }\n        else {\n            switch (beam_width) {\n                CASE_K_DIV(1, 256, 256);\n                CASE_K_DIV(4, 256, 256);\n                CASE_K_DIV(16, 256, 64);\n                CASE_K_DIV(32, 256, 64);\n                CASE_K_DIV(64, 256, 64);\n                default:\n                    FT_CHECK_WITH_INFO(false, fmtstr(\"Topk kernel does not support beamwidth = %d \\n\", beam_width));\n                    break;\n            }\n        }\n        return;\n    }\n}\n\n#undef CASE_K\n#undef CASE_K_DIV\n\ntemplate void invokeTopkBeamSearch(void*           workspace,\n                                   size_t&         workspace_size,\n                                   float*          log_probs,\n                                   int*            ids,\n                                   BeamHypotheses* beam_hyps,\n                                   const bool*     finished,\n                                   const int*      sequence_lengths,\n                                   const int       batch_size,\n                                   const int       beam_width,\n                                   const int       vocab_size_padded_,\n                                   const float     diversity_rate,\n                                   const float     length_penalty,\n                                   const int*      end_ids,\n                                   hipStream_t    stream);\n\ntemplate<typename T>\n__global__ void tileEncoderResults(T*         tiled_output,\n                                   int*       tiled_sequence_length,\n                                   const T*   output,\n                                   const int* sequence_length,\n                                   const uint batch_size,\n                                   const uint beam_width,\n                                   const uint d_model)\n{\n    if (blockIdx.x == 0) {\n        for (uint i = threadIdx.x; i < batch_size * beam_width; i += blockDim.x) {\n            tiled_sequence_length[i] = sequence_length[i / beam_width];\n        }\n    }\n\n    int tgt_offset =\n        blockIdx.x * gridDim.y * gridDim.z * d_model + blockIdx.y * gridDim.z * d_model + blockIdx.z * d_model;\n    int src_offset = blockIdx.x * gridDim.z * d_model + blockIdx.z * d_model;\n    for (uint i = threadIdx.x; i < d_model; i += blockDim.x) {\n        tiled_output[i + tgt_offset] = output[i + src_offset];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTileEncoderResults(T*           tiled_output,\n                              int*         tiled_sequence_length,\n                              const T*     output,\n                              const int*   sequence_length,\n                              const size_t batch_size,\n                              const size_t beam_width,\n                              const size_t mem_max_seq_len,\n                              const size_t d_model,\n                              hipStream_t stream)\n{\n    // tiled_output: [batch_size, beam_width, mem_max_seq_len, d_model]\n    // tiled_sequence_length: [batch_size, beam_width]\n\n    // output: [batch_size, mem_max_seq_len, d_model]\n    // sequence_length [batch_size]\n\n    dim3 grid(batch_size, beam_width, mem_max_seq_len);\n    bool is_half2 = (std::is_same<T, half>::value) && (d_model % 2 == 0);\n\n    if (is_half2) {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(512, (int)(d_model / 2)));\n        tileEncoderResults<T2><<<grid, block, 0, stream>>>((T2*)tiled_output,\n                                                           tiled_sequence_length,\n                                                           (const T2*)output,\n                                                           sequence_length,\n                                                           batch_size,\n                                                           beam_width,\n                                                           d_model / 2);\n    }\n    else {\n        dim3 block(min(512, (int)d_model));\n        tileEncoderResults<T><<<grid, block, 0, stream>>>(\n            tiled_output, tiled_sequence_length, output, sequence_length, batch_size, beam_width, d_model);\n    }\n}\n\ntemplate void invokeTileEncoderResults(float*       tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const float* output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       hipStream_t stream);\n\ntemplate void invokeTileEncoderResults(half*        tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const half*  output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       hipStream_t stream);\n\ntemplate void invokeTileEncoderResults(half2*       tiled_output,\n                                       int*         tiled_sequence_length,\n                                       const half2* output,\n                                       const int*   sequence_length,\n                                       const size_t batch_size,\n                                       const size_t beam_width,\n                                       const size_t mem_max_seq_len,\n                                       const size_t d_model,\n                                       hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeTileEncoderResults(__hip_bfloat16*       tiled_output,\n                                       int*                 tiled_sequence_length,\n                                       const __hip_bfloat16* output,\n                                       const int*           sequence_length,\n                                       const size_t         batch_size,\n                                       const size_t         beam_width,\n                                       const size_t         mem_max_seq_len,\n                                       const size_t         d_model,\n                                       hipStream_t         stream);\n#endif\n\n__global__ void insertUnfinishedPath(BeamHypotheses beam_hyps,\n                                     const bool*    finished,\n                                     const float*   cum_log_probs,\n                                     const int      batch_size,\n                                     const int      beam_width)\n{\n    const int bid           = blockIdx.x;\n    const int tgt_start_idx = beam_hyps.num_beams[bid];\n    if (beam_hyps.is_done[bid]) {\n        return;\n    }\n    for (int i = 0; i < beam_width; i++) {\n        if (threadIdx.x == 0) {\n            const int src_beam_idx = bid * beam_width + i;\n            const int tgt_beam_idx = bid * beam_width * 2 + i + tgt_start_idx;\n\n            const int length = beam_hyps.sequence_lengths_src[src_beam_idx];\n\n            beam_hyps.output_ids_tgt[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + length] =\n                beam_hyps.output_ids_src[length * batch_size * beam_width + src_beam_idx];\n            if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                beam_hyps.log_probs[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + length] =\n                    beam_hyps.log_probs_src[length * batch_size * beam_width + src_beam_idx];\n            }\n            int prev_id = beam_hyps.parent_ids_src[length * batch_size * beam_width + src_beam_idx];\n            for (int j = length - 1; j >= 0; j--) {\n                // output_ids_tgt need to use max_seq_len + 1 because its shape is\n                // [bs, beam_width, max_seq_len + 1]\n                beam_hyps.output_ids_tgt[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + j] =\n                    beam_hyps.output_ids_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n                if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                    beam_hyps.log_probs[(tgt_beam_idx) * (beam_hyps.max_seq_len + 1) + j] =\n                        beam_hyps.log_probs_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n                }\n                prev_id = beam_hyps.parent_ids_src[j * batch_size * beam_width + bid * beam_width + prev_id];\n            }\n            beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = length;\n\n            beam_hyps.normed_scores[tgt_beam_idx] = apply_length_penalty(\n                cum_log_probs[src_beam_idx], finished[src_beam_idx] ? length + 1 : length, beam_hyps.length_penalty);\n            beam_hyps.cum_log_probs[tgt_beam_idx] = cum_log_probs[src_beam_idx];\n\n            beam_hyps.num_beams[bid]++;\n        }\n    }\n}\n\nvoid invokeInsertUnfinishedPath(BeamHypotheses beam_hyps,\n                                const bool*    finished,\n                                const float*   cum_log_probs,\n                                const int      batch_size,\n                                const int      beam_width,\n                                hipStream_t   stream)\n{\n    insertUnfinishedPath<<<batch_size, 256, 0, stream>>>(beam_hyps, finished, cum_log_probs, batch_size, beam_width);\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/beam_search_topk_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/softmax_int8_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len/4 + 31)/32*32)\n// for int32_t I; int8 O;\ntemplate<typename T>\n__global__ void softmax_COL32(int8_t*        output,\n                              const int32_t* input,\n                              const T*       attr_mask,\n                              const int      batch_size,\n                              const int      head_num,\n                              const int      seq_len,\n                              const float    scalar1a,\n                              const float*   scalar1b,\n                              const float*   scalar1c,\n                              const float*   amax_ptr,\n                              const int      head_num_x_seq_len,\n                              const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdx4 = threadIdx.x << 2;\n\n    char4* buf4Ptr = (char4*)output;\n\n    bool qual = threadIdx4 < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char4 tmp4  = {0, 0, 0, 0};\n        int   inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdx4 & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdx4 & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                buf4Ptr[inIdx >> 2] = tmp4;\n            }\n            continue;\n        }\n\n        float4 floatTmp4 = {0.0f, 0.0f, 0.0f, 0.0f};\n\n        if (qual) {\n            floatTmp4.x = static_cast<float>(__ldg(input + inIdx)) * scalar1;\n            floatTmp4.y = static_cast<float>(__ldg(input + inIdx + 1)) * scalar1;\n            floatTmp4.z = static_cast<float>(__ldg(input + inIdx + 2)) * scalar1;\n            floatTmp4.w = static_cast<float>(__ldg(input + inIdx + 3)) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx4 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp4.x = floatTmp4.x + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.x);\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp4.y = floatTmp4.y + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.y);\n\n            // for z\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 2))) * -10000.0f;\n            floatTmp4.z = floatTmp4.z + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.z);\n\n            // for w\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 3))) * -10000.0f;\n            floatTmp4.w = floatTmp4.w + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.w);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp4.x = __expf(floatTmp4.x - s_max);\n            sum_val += floatTmp4.x;\n            floatTmp4.y = __expf(floatTmp4.y - s_max);\n            sum_val += floatTmp4.y;\n            floatTmp4.z = __expf(floatTmp4.z - s_max);\n            sum_val += floatTmp4.z;\n            floatTmp4.w = __expf(floatTmp4.w - s_max);\n            sum_val += floatTmp4.w;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n\n            tmp4.x = float_to_int8_rn(floatTmp4.x * s_sum);\n            tmp4.y = float_to_int8_rn(floatTmp4.y * s_sum);\n            tmp4.z = float_to_int8_rn(floatTmp4.z * s_sum);\n            tmp4.w = float_to_int8_rn(floatTmp4.w * s_sum);\n\n            buf4Ptr[inIdx >> 2] = tmp4;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len_padded/4 + 31)/32*32)\n// for int8_t IO;\ntemplate<typename T>\n__global__ void softmax_COL32_varlen(int8_t*       output,\n                                     const int8_t* input,\n                                     const T*      attr_mask,\n                                     const int     batch_size,\n                                     const int     head_num,\n                                     const int     seq_len,\n                                     const int     seq_len_padded,\n                                     const float   scalar1a,\n                                     const float*  scalar1b,\n                                     const float*  amax_ptr,\n                                     const int     seq_len_x_seq_len,\n                                     const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdx4 = threadIdx.x << 2;\n\n    char4*       buf4Ptr   = (char4*)output;\n    const char4* inBuf4Ptr = (const char4*)input;\n\n    const bool qual        = threadIdx4 < seq_len;\n    const bool qual_padded = threadIdx4 < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n\n        char4 tmp4  = {0, 0, 0, 0};\n        int   inIdx = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                     + (threadIdx4 & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdx4 & 31))\n                    >> 2;\n\n        // set softmax of padding word in rows to 0\n        const float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                buf4Ptr[inIdx] = tmp4;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float4 floatTmp4 = {0.0f, 0.0f, 0.0f, 0.0f};\n        if (qual) {\n            tmp4        = __ldg(inBuf4Ptr + inIdx);\n            floatTmp4.x = static_cast<float>(tmp4.x) * scalar1;\n            floatTmp4.y = static_cast<float>(tmp4.y) * scalar1;\n            floatTmp4.z = static_cast<float>(tmp4.z) * scalar1;\n            floatTmp4.w = static_cast<float>(tmp4.w) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx4 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp4.x = floatTmp4.x + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.x);\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp4.y = floatTmp4.y + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.y);\n\n            // for z\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 2))) * -10000.0f;\n            floatTmp4.z = floatTmp4.z + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.z);\n\n            // for w\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 3))) * -10000.0f;\n            floatTmp4.w = floatTmp4.w + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.w);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp4.x = __expf(floatTmp4.x - s_max);\n            sum_val += floatTmp4.x;\n            floatTmp4.y = __expf(floatTmp4.y - s_max);\n            sum_val += floatTmp4.y;\n            floatTmp4.z = __expf(floatTmp4.z - s_max);\n            sum_val += floatTmp4.z;\n            floatTmp4.w = __expf(floatTmp4.w - s_max);\n            sum_val += floatTmp4.w;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n\n            tmp4.x = qual ? float_to_int8_rn(floatTmp4.x * s_sum) : static_cast<int8_t>(0);\n            tmp4.y = qual ? float_to_int8_rn(floatTmp4.y * s_sum) : static_cast<int8_t>(0);\n            tmp4.z = qual ? float_to_int8_rn(floatTmp4.z * s_sum) : static_cast<int8_t>(0);\n            tmp4.w = qual ? float_to_int8_rn(floatTmp4.w * s_sum) : static_cast<int8_t>(0);\n\n            buf4Ptr[inIdx] = tmp4;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len_padded + 31)/32*32)\n// for int8_t IO, I/O with int8_t element;\ntemplate<typename T>\n__global__ void softmax_COL32_perElement_varlen(int8_t*       output,\n                                                const int8_t* input,\n                                                const T*      attr_mask,\n                                                const int     batch_size,\n                                                const int     head_num,\n                                                const int     seq_len,\n                                                const int     seq_len_padded,\n                                                const float   scalar1a,\n                                                const float*  scalar1b,\n                                                const float*  amax_ptr,\n                                                const int     seq_len_x_seq_len,\n                                                const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    const int   tidx = threadIdx.x;\n\n    const bool qual        = tidx < seq_len;\n    const bool qual_padded = tidx < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n\n        int8_t tmp = 0;\n        int inIdx  = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded) + (tidx & 0xffffffe0) * seq_len\n                     + (seq_id << 5) + (tidx & 31));\n\n        // set softmax of padding word in rows to 0\n        const float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                output[inIdx] = tmp;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float floatTmp = qual ? (static_cast<float>(__ldg(input + inIdx)) * scalar1) : 0.0f;\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id  = tidx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            mask_val = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp = floatTmp + mask_val;\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(floatTmp) : blockReduceMax<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : floatTmp;\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            tmp           = qual ? float_to_int8_rn(floatTmp * s_sum) : static_cast<int8_t>(0);\n            output[inIdx] = tmp;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = (seq_len + 31)/32\n// for int32_t I; int8 O;\n// for seq_len <= 32\ntemplate<typename T>\n__global__ void softmax_COL32_LE32(int8_t*        output,\n                                   const int32_t* input,\n                                   const T*       attr_mask,\n                                   const int      batch_size,\n                                   const int      head_num,\n                                   const int      seq_len,\n                                   const float    scalar1a,\n                                   const float*   scalar1b,\n                                   const float*   scalar1c,\n                                   const float*   amax_ptr,\n                                   const int      head_num_x_seq_len,\n                                   const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdxx = threadIdx.x;\n    bool        qual       = threadIdxx < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        int inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdxx & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdxx & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                output[inIdx] = 0;\n            }\n            continue;\n        }\n\n        float floatTmp = qual ? static_cast<float>(__ldg(input + inIdx)) * scalar1 : 0.0f;\n\n        float mask_val, max_val;\n\n        __shared__ float s_max, s_sum;\n\n        mask_id  = qual ? threadIdxx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len : 0;\n        mask_val = qual ? (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f : 0.0f;\n        floatTmp = qual ? floatTmp + mask_val : 0.0f;\n        max_val  = qual ? floatTmp : -1e20f;\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n            output[inIdx] = float_to_int8_rn(floatTmp * s_sum);\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// attr_mask is [batch_size, seq_len, seq_len]\n// grid = (seq_len, batch_size, head_num)\n// block.x = seq_len_padded\n// for int8_t IO;\n// for seq_len_padded == 32\ntemplate<typename T>\n__global__ void softmax_COL32_LE32_varlen(int8_t*       output,\n                                          const int8_t* input,\n                                          const T*      attr_mask,\n                                          const int     batch_size,\n                                          const int     head_num,\n                                          const int     seq_len,\n                                          const int     seq_len_padded,\n                                          const float   scalar1a,\n                                          const float*  scalar1b,\n                                          const float*  amax_ptr,\n                                          const int     seq_len_x_seq_len,\n                                          const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdxx  = threadIdx.x;\n    const bool  qual        = threadIdxx < seq_len;\n    const bool  qual_padded = threadIdxx < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        int inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                    + (threadIdxx & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdxx & 31);\n\n        // set softmax of padding word in rows to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                output[inIdx] = 0;\n            }\n            continue;\n        }\n\n        float            mask_val, max_val;\n        __shared__ float s_max, s_sum;\n\n        // set softmax of padding word in cols to 0\n        float floatTmp = qual ? static_cast<float>(__ldg(input + inIdx)) * scalar1 : 0.0f;\n        mask_id        = qual ? threadIdxx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len : 0;\n        mask_val       = qual ? (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f : 0.0f;\n        floatTmp       = qual ? floatTmp + mask_val : 0.0f;\n        max_val        = qual ? floatTmp : -1e20f;\n\n        max_val = warpReduceMax(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            output[inIdx] = qual ? float_to_int8_rn(floatTmp * s_sum) : static_cast<int8_t>(0);\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len/2 + 31)/32*32)\n// for int32_t I; int8 O;\n// for seq_len in (32, 64]\ntemplate<typename T>\n__global__ void softmax_COL32_LE64(int8_t*        output,\n                                   const int32_t* input,\n                                   const T*       attr_mask,\n                                   const int      batch_size,\n                                   const int      head_num,\n                                   const int      seq_len,\n                                   const float    scalar1a,\n                                   const float*   scalar1b,\n                                   const float*   scalar1c,\n                                   const float*   amax_ptr,\n                                   const int      head_num_x_seq_len,\n                                   const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdx2 = threadIdx.x << 1;\n\n    char2* buf2Ptr = (char2*)output;\n\n    bool qual = threadIdx2 < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char2 tmp2  = {0, 0};\n        int   inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdx2 & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdx2 & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                buf2Ptr[inIdx >> 1] = tmp2;\n            }\n            continue;\n        }\n\n        float2 floatTmp2 = {0.0f, 0.0f};\n        if (qual) {\n            floatTmp2.x = static_cast<float>(__ldg(input + inIdx)) * scalar1;\n            floatTmp2.y = static_cast<float>(__ldg(input + inIdx + 1)) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx2 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp2.x = floatTmp2.x + mask_val;\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp2.y = floatTmp2.y + mask_val;\n\n            max_val = fmaxf(floatTmp2.x, floatTmp2.y);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp2.x = __expf(floatTmp2.x - s_max);\n            sum_val += floatTmp2.x;\n            floatTmp2.y = __expf(floatTmp2.y - s_max);\n            sum_val += floatTmp2.y;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n            tmp2.x              = float_to_int8_rn(floatTmp2.x * s_sum);\n            tmp2.y              = float_to_int8_rn(floatTmp2.y * s_sum);\n            buf2Ptr[inIdx >> 1] = tmp2;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = 32\n// for int8_t IO\n// for seq_len in (32, 64]\ntemplate<typename T>\n__global__ void softmax_COL32_LE64_varlen(int8_t*       output,\n                                          const int8_t* input,\n                                          const T*      attr_mask,\n                                          const int     batch_size,\n                                          const int     head_num,\n                                          const int     seq_len,\n                                          const int     seq_len_padded,\n                                          const float   scalar1a,\n                                          const float*  scalar1b,\n                                          const float*  amax_ptr,\n                                          const int     seq_len_x_seq_len,\n                                          const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdx2 = threadIdx.x << 1;\n\n    char2*       buf2Ptr   = (char2*)output;\n    const char2* inBuf2Ptr = (const char2*)input;\n\n    const bool qual        = threadIdx2 < seq_len;\n    const bool qual_padded = threadIdx2 < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char2 tmp2  = {0, 0};\n        int   inIdx = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                     + (threadIdx2 & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdx2 & 31))\n                    >> 1;\n\n        // set softmax of padding word in rows to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                buf2Ptr[inIdx] = tmp2;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float2 floatTmp2 = {0.0f, 0.0f};\n        if (qual) {\n            tmp2        = __ldg(inBuf2Ptr + inIdx);\n            floatTmp2.x = static_cast<float>(tmp2.x) * scalar1;\n            floatTmp2.y = static_cast<float>(tmp2.y) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx2 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp2.x = floatTmp2.x + mask_val;\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp2.y = floatTmp2.y + mask_val;\n\n            max_val = fmaxf(floatTmp2.x, floatTmp2.y);\n        }\n\n        max_val = warpReduceMax(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp2.x = __expf(floatTmp2.x - s_max);\n            sum_val += floatTmp2.x;\n            floatTmp2.y = __expf(floatTmp2.y - s_max);\n            sum_val += floatTmp2.y;\n        }\n\n        sum_val = warpReduceSum(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            tmp2.x         = qual ? float_to_int8_rn(floatTmp2.x * s_sum) : static_cast<int8_t>(0);\n            tmp2.y         = qual ? float_to_int8_rn(floatTmp2.y * s_sum) : static_cast<int8_t>(0);\n            buf2Ptr[inIdx] = tmp2;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeSoftmaxCOL32(int8_t*        output,\n                        const int32_t* input,\n                        const T*       attr_mask,\n                        const int      batch_size,\n                        const int      head_num,\n                        const int      seq_len,\n                        const float    scalar1a,\n                        const float*   scalar1b,\n                        const float*   scalar1c,\n                        const float*   amax_ptr,\n                        cudaStream_t   stream)\n{\n    dim3 grid, block;\n    grid.x = seq_len;\n    grid.y = batch_size;\n    grid.z = head_num;\n\n    if (seq_len <= 32) {\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        block.x = (seq_len + 31) / 32 * 32;\n        softmax_COL32_LE32<<<grid, block, 0, stream>>>(output,\n                                                       input,\n                                                       attr_mask,\n                                                       batch_size,\n                                                       head_num,\n                                                       seq_len,\n                                                       scalar1a,\n                                                       scalar1b,\n                                                       scalar1c,\n                                                       amax_ptr,\n                                                       seq_len * head_num,\n                                                       seq_len * seq_len);\n    }\n    else if (seq_len <= 64) {\n        assert(seq_len % 2 == 0);\n        block.x = (seq_len / 2 + 31) / 32 * 32;\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        softmax_COL32_LE64<<<grid, block, 0, stream>>>(output,\n                                                       input,\n                                                       attr_mask,\n                                                       batch_size,\n                                                       head_num,\n                                                       seq_len,\n                                                       scalar1a,\n                                                       scalar1b,\n                                                       scalar1c,\n                                                       amax_ptr,\n                                                       seq_len * head_num,\n                                                       seq_len * seq_len);\n    }\n    else {\n        assert(seq_len % 4 == 0);\n        block.x = (seq_len / 4 + 31) / 32 * 32;\n        softmax_COL32<<<grid, block, 0, stream>>>(output,\n                                                  input,\n                                                  attr_mask,\n                                                  batch_size,\n                                                  head_num,\n                                                  seq_len,\n                                                  scalar1a,\n                                                  scalar1b,\n                                                  scalar1c,\n                                                  amax_ptr,\n                                                  seq_len * head_num,\n                                                  seq_len * seq_len);\n    }\n}\n\ntemplate void invokeSoftmaxCOL32(int8_t*        output,\n                                 const int32_t* input,\n                                 const float*   attr_mask,\n                                 const int      batch_size,\n                                 const int      head_num,\n                                 const int      seq_len,\n                                 const float    scalar1a,\n                                 const float*   scalar1b,\n                                 const float*   scalar1c,\n                                 const float*   amax_ptr,\n                                 cudaStream_t   stream);\n\ntemplate void invokeSoftmaxCOL32(int8_t*        output,\n                                 const int32_t* input,\n                                 const half*    attr_mask,\n                                 const int      batch_size,\n                                 const int      head_num,\n                                 const int      seq_len,\n                                 const float    scalar1a,\n                                 const float*   scalar1b,\n                                 const float*   scalar1c,\n                                 const float*   amax_ptr,\n                                 cudaStream_t   stream);\n\ntemplate<typename T>\nvoid invokeSoftmaxCOL32(int8_t*       output,\n                        const int8_t* input,\n                        const T*      attr_mask,\n                        const int     batch_size,\n                        const int     head_num,\n                        const int     seq_len,\n                        const float   scalar1a,\n                        const float*  scalar1b,\n                        const float*  amax_ptr,\n                        cudaStream_t  stream)\n{\n    dim3 grid, block;\n    grid.x                   = seq_len;\n    grid.y                   = batch_size;\n    grid.z                   = head_num;\n    const int seq_len_padded = (seq_len + 31) / 32 * 32;\n\n    if (seq_len <= 32) {\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        block.x = seq_len_padded;\n        softmax_COL32_LE32_varlen<<<grid, block, 0, stream>>>(output,\n                                                              input,\n                                                              attr_mask,\n                                                              batch_size,\n                                                              head_num,\n                                                              seq_len,\n                                                              seq_len_padded,\n                                                              scalar1a,\n                                                              scalar1b,\n                                                              amax_ptr,\n                                                              seq_len * seq_len,\n                                                              seq_len * seq_len_padded);\n    }\n    else if (seq_len <= 64 && (seq_len % 2 == 0)) {\n        block.x = 32;\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        softmax_COL32_LE64_varlen<<<grid, block, 0, stream>>>(output,\n                                                              input,\n                                                              attr_mask,\n                                                              batch_size,\n                                                              head_num,\n                                                              seq_len,\n                                                              seq_len_padded,\n                                                              scalar1a,\n                                                              scalar1b,\n                                                              amax_ptr,\n                                                              seq_len * seq_len,\n                                                              seq_len * seq_len_padded);\n    }\n    else if (seq_len > 64 && (seq_len % 4 == 0)) {\n        block.x = (seq_len_padded / 4 + 31) / 32 * 32;\n        softmax_COL32_varlen<<<grid, block, 0, stream>>>(output,\n                                                         input,\n                                                         attr_mask,\n                                                         batch_size,\n                                                         head_num,\n                                                         seq_len,\n                                                         seq_len_padded,\n                                                         scalar1a,\n                                                         scalar1b,\n                                                         amax_ptr,\n                                                         seq_len * seq_len,\n                                                         seq_len * seq_len_padded);\n    }\n    else {\n        block.x = (seq_len_padded + 31) / 32 * 32;\n        softmax_COL32_perElement_varlen<<<grid, block, 0, stream>>>(output,\n                                                                    input,\n                                                                    attr_mask,\n                                                                    batch_size,\n                                                                    head_num,\n                                                                    seq_len,\n                                                                    seq_len_padded,\n                                                                    scalar1a,\n                                                                    scalar1b,\n                                                                    amax_ptr,\n                                                                    seq_len * seq_len,\n                                                                    seq_len * seq_len_padded);\n    }\n}\n\ntemplate void invokeSoftmaxCOL32(int8_t*       output,\n                                 const int8_t* input,\n                                 const float*  attr_mask,\n                                 const int     batch_size,\n                                 const int     head_num,\n                                 const int     seq_len,\n                                 const float   scalar1a,\n                                 const float*  scalar1b,\n                                 const float*  amax_ptr,\n                                 cudaStream_t  stream);\n\ntemplate void invokeSoftmaxCOL32(int8_t*       output,\n                                 const int8_t* input,\n                                 const half*   attr_mask,\n                                 const int     batch_size,\n                                 const int     head_num,\n                                 const int     seq_len,\n                                 const float   scalar1a,\n                                 const float*  scalar1b,\n                                 const float*  amax_ptr,\n                                 cudaStream_t  stream);\n\n/*******************  invokeSoftmaxCOL32  ***********************/\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T>\n__global__ void softmax_INT8IO_kernel_COL32(int8_t*      a_buf,\n                                            int8_t*      qk_buf_int8,\n                                            const T*     attn_mask,\n                                            const T*     relative_pos_bias,\n                                            const int    batch_size,\n                                            const int    num_head,\n                                            const int    window_num,\n                                            const int    window_len,\n                                            const int    window_len_x_window_len,\n                                            const float  scalar,\n                                            const float* deQ_scale_ptr,\n                                            const float* out_scale_ptr)\n{\n\n    bool      qual          = threadIdx.x < window_len;\n    const int padded_winlen = (window_len + 31) / 32 * 32;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len * padded_winlen\n                        + ((threadIdx.x >> 5) << 5) * window_len + (window_id << 5) + (threadIdx.x & 31);\n        ;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + threadIdx.x;\n\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            float     mask_val =\n                (attn_mask == nullptr) ?\n                        0.0f :\n                        static_cast<float>(\n                        __ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window)));\n            tmp = scalar * static_cast<float>(qk_buf_int8[qk_offset]) * __ldg(deQ_scale_ptr) + mask_val\n                  + static_cast<float>(__ldg(relative_pos_bias + relative_pos_bias_offset));\n        }\n\n        float max_val = blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float qk_tmp  = qual ? __expf(tmp - s_max) : 0.0f;\n        float sum_val = blockReduceSum<float>(qk_tmp);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        a_buf[qk_offset] = qual ? float_to_int8_rn(qk_tmp * s_mean * __ldg(out_scale_ptr)) : 0;\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = (window_len / 4 + 31)/32*32\n// a_buf/qk_buf is [batch, window_num, num_head, window_len, window_len] + COL32\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-major\ntemplate<typename T4, typename T>\n__global__ void softmax_INT8IO_kernel_COL32_element4(int8_t*      a_buf,\n                                                     int8_t*      qk_buf_int8,\n                                                     const T4*    attn_mask,\n                                                     const T4*    relative_pos_bias,\n                                                     const int    batch_size,\n                                                     const int    num_head,\n                                                     const int    window_num,\n                                                     const int    window_len,\n                                                     const int    window_len_x_window_len,\n                                                     const float  scalar,\n                                                     const float* deQ_scale_ptr,\n                                                     const float* out_scale_ptr)\n{\n\n    const int   padded_winlen = (window_len + 31) / 32 * 32;\n    const int   col_id        = threadIdx.x << 2;\n    bool        qual          = col_id < window_len;\n    const T4    zero          = {T(0.0f), T(0.0f), T(0.0f), T(0.0f)};\n    const float deQ_scale     = __ldg(deQ_scale_ptr);\n    const float out_scale     = __ldg(out_scale_ptr);\n    char4*      inputPtr      = (char4*)qk_buf_int8;\n    char4*      outputPtr     = (char4*)a_buf;\n\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        char4            qk_val;\n        float4           local_qk_val;\n        __shared__ float s_mean, s_max;\n        int              qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len * padded_winlen\n                        + ((col_id >> 5) << 5) * window_len + (window_id << 5) + (col_id & 31);\n        if (qual) {\n            const int offset_in_window         = window_id * window_len + col_id;\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            const int attn_mask_offset         = (blockIdx.y / num_head) * window_len_x_window_len + offset_in_window;\n\n            const T4 bias_val = relative_pos_bias[relative_pos_bias_offset >> 2];\n            const T4 mask_val = (attn_mask == nullptr) ? zero : attn_mask[attn_mask_offset >> 2];\n            qk_val            = inputPtr[qk_offset >> 2];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            local_qk_val.z    = static_cast<float>(qk_val.z);\n            local_qk_val.w    = static_cast<float>(qk_val.w);\n\n            local_qk_val.x =\n                scalar * local_qk_val.x * deQ_scale + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                scalar * local_qk_val.y * deQ_scale + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            local_qk_val.z =\n                scalar * local_qk_val.z * deQ_scale + static_cast<float>(mask_val.z) + static_cast<float>(bias_val.z);\n            local_qk_val.w =\n                scalar * local_qk_val.w * deQ_scale + static_cast<float>(mask_val.w) + static_cast<float>(bias_val.w);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n            tmp = tmp > local_qk_val.z ? tmp : local_qk_val.z;\n            tmp = tmp > local_qk_val.w ? tmp : local_qk_val.w;\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n        local_qk_val.z = qual ? __expf(local_qk_val.z - s_max) : 0.0f;\n        local_qk_val.w = qual ? __expf(local_qk_val.w - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ?\n                            warpReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w) :\n                            blockReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            char4 outTmp;\n            outTmp.x                  = float_to_int8_rn(local_qk_val.x * s_mean * out_scale);\n            outTmp.y                  = float_to_int8_rn(local_qk_val.y * s_mean * out_scale);\n            outTmp.z                  = float_to_int8_rn(local_qk_val.z * s_mean * out_scale);\n            outTmp.w                  = float_to_int8_rn(local_qk_val.w * s_mean * out_scale);\n            outputPtr[qk_offset >> 2] = outTmp;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                      int8_t*      qk_buf_int8,\n                                      const T*     attn_mask,\n                                      const T*     relative_pos_bias,\n                                      const int    batch_size,\n                                      const int    num_head,\n                                      const int    window_num,\n                                      const int    window_len,\n                                      const float  scalar,\n                                      const float* deQ_scale_ptr,\n                                      const float* out_scale_ptr,\n                                      cudaStream_t stream)\n{\n    dim3 grid(window_len, window_num * num_head, batch_size);\n    if (window_len % 4 == 0 && window_len / 4 >= 32) {\n        dim3 block((window_len / 4 + 31) / 32 * 32);\n        if (batch_size * window_num * num_head > 960) {\n            grid.x = ceil(float(window_len) / 32.0f);\n        }\n        if (std::is_same<T, float>::value) {\n            softmax_INT8IO_kernel_COL32_element4<float4, float>\n                <<<grid, block, 0, stream>>>(a_buf,\n                                             qk_buf_int8,\n                                             (const float4*)attn_mask,\n                                             (const float4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             scalar,\n                                             deQ_scale_ptr,\n                                             out_scale_ptr);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_INT8IO_kernel_COL32_element4<half4, half>\n                <<<grid, block, 0, stream>>>(a_buf,\n                                             qk_buf_int8,\n                                             (const half4*)attn_mask,\n                                             (const half4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             scalar,\n                                             deQ_scale_ptr,\n                                             out_scale_ptr);\n        }\n    }\n    else {\n        dim3 block((window_len + 31) / 32 * 32);\n        softmax_INT8IO_kernel_COL32<<<grid, block, 0, stream>>>(a_buf,\n                                                                qk_buf_int8,\n                                                                attn_mask,\n                                                                relative_pos_bias,\n                                                                batch_size,\n                                                                num_head,\n                                                                window_num,\n                                                                window_len,\n                                                                window_len * window_len,\n                                                                scalar,\n                                                                deQ_scale_ptr,\n                                                                out_scale_ptr);\n    }\n}\n\ntemplate void invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                               int8_t*      qk_buf_int8,\n                                               const float* attn_mask,\n                                               const float* relative_pos_bias,\n                                               const int    batch_size,\n                                               const int    num_head,\n                                               const int    window_num,\n                                               const int    window_len,\n                                               const float  scalar,\n                                               const float* deQ_scale_ptr,\n                                               const float* output_scale_ptr,\n                                               cudaStream_t stream);\n\ntemplate void invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                               int8_t*      qk_buf_int8,\n                                               const half*  attn_mask,\n                                               const half*  relative_pos_bias,\n                                               const int    batch_size,\n                                               const int    num_head,\n                                               const int    window_num,\n                                               const int    window_len,\n                                               const float  scalar,\n                                               const float* deQ_scale_ptr,\n                                               const float* output_scale_ptr,\n                                               cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"int8_utils.cuh\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/softmax_int8_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len/4 + 31)/32*32)\n// for int32_t I; int8 O;\ntemplate<typename T>\n__global__ void softmax_COL32(int8_t*        output,\n                              const int32_t* input,\n                              const T*       attr_mask,\n                              const int      batch_size,\n                              const int      head_num,\n                              const int      seq_len,\n                              const float    scalar1a,\n                              const float*   scalar1b,\n                              const float*   scalar1c,\n                              const float*   amax_ptr,\n                              const int      head_num_x_seq_len,\n                              const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdx4 = threadIdx.x << 2;\n\n    char4* buf4Ptr = (char4*)output;\n\n    bool qual = threadIdx4 < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char4 tmp4  = {0, 0, 0, 0};\n        int   inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdx4 & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdx4 & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                buf4Ptr[inIdx >> 2] = tmp4;\n            }\n            continue;\n        }\n\n        float4 floatTmp4 = {0.0f, 0.0f, 0.0f, 0.0f};\n\n        if (qual) {\n            floatTmp4.x = static_cast<float>(__ldg(input + inIdx)) * scalar1;\n            floatTmp4.y = static_cast<float>(__ldg(input + inIdx + 1)) * scalar1;\n            floatTmp4.z = static_cast<float>(__ldg(input + inIdx + 2)) * scalar1;\n            floatTmp4.w = static_cast<float>(__ldg(input + inIdx + 3)) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx4 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp4.x = floatTmp4.x + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.x);\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp4.y = floatTmp4.y + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.y);\n\n            // for z\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 2))) * -10000.0f;\n            floatTmp4.z = floatTmp4.z + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.z);\n\n            // for w\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 3))) * -10000.0f;\n            floatTmp4.w = floatTmp4.w + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.w);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp4.x = __expf(floatTmp4.x - s_max);\n            sum_val += floatTmp4.x;\n            floatTmp4.y = __expf(floatTmp4.y - s_max);\n            sum_val += floatTmp4.y;\n            floatTmp4.z = __expf(floatTmp4.z - s_max);\n            sum_val += floatTmp4.z;\n            floatTmp4.w = __expf(floatTmp4.w - s_max);\n            sum_val += floatTmp4.w;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n\n            tmp4.x = float_to_int8_rn(floatTmp4.x * s_sum);\n            tmp4.y = float_to_int8_rn(floatTmp4.y * s_sum);\n            tmp4.z = float_to_int8_rn(floatTmp4.z * s_sum);\n            tmp4.w = float_to_int8_rn(floatTmp4.w * s_sum);\n\n            buf4Ptr[inIdx >> 2] = tmp4;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len_padded/4 + 31)/32*32)\n// for int8_t IO;\ntemplate<typename T>\n__global__ void softmax_COL32_varlen(int8_t*       output,\n                                     const int8_t* input,\n                                     const T*      attr_mask,\n                                     const int     batch_size,\n                                     const int     head_num,\n                                     const int     seq_len,\n                                     const int     seq_len_padded,\n                                     const float   scalar1a,\n                                     const float*  scalar1b,\n                                     const float*  amax_ptr,\n                                     const int     seq_len_x_seq_len,\n                                     const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdx4 = threadIdx.x << 2;\n\n    char4*       buf4Ptr   = (char4*)output;\n    const char4* inBuf4Ptr = (const char4*)input;\n\n    const bool qual        = threadIdx4 < seq_len;\n    const bool qual_padded = threadIdx4 < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n\n        char4 tmp4  = {0, 0, 0, 0};\n        int   inIdx = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                     + (threadIdx4 & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdx4 & 31))\n                    >> 2;\n\n        // set softmax of padding word in rows to 0\n        const float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                buf4Ptr[inIdx] = tmp4;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float4 floatTmp4 = {0.0f, 0.0f, 0.0f, 0.0f};\n        if (qual) {\n            tmp4        = __ldg(inBuf4Ptr + inIdx);\n            floatTmp4.x = static_cast<float>(tmp4.x) * scalar1;\n            floatTmp4.y = static_cast<float>(tmp4.y) * scalar1;\n            floatTmp4.z = static_cast<float>(tmp4.z) * scalar1;\n            floatTmp4.w = static_cast<float>(tmp4.w) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx4 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp4.x = floatTmp4.x + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.x);\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp4.y = floatTmp4.y + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.y);\n\n            // for z\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 2))) * -10000.0f;\n            floatTmp4.z = floatTmp4.z + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.z);\n\n            // for w\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 3))) * -10000.0f;\n            floatTmp4.w = floatTmp4.w + mask_val;\n            max_val     = fmaxf(max_val, floatTmp4.w);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp4.x = __expf(floatTmp4.x - s_max);\n            sum_val += floatTmp4.x;\n            floatTmp4.y = __expf(floatTmp4.y - s_max);\n            sum_val += floatTmp4.y;\n            floatTmp4.z = __expf(floatTmp4.z - s_max);\n            sum_val += floatTmp4.z;\n            floatTmp4.w = __expf(floatTmp4.w - s_max);\n            sum_val += floatTmp4.w;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n\n            tmp4.x = qual ? float_to_int8_rn(floatTmp4.x * s_sum) : static_cast<int8_t>(0);\n            tmp4.y = qual ? float_to_int8_rn(floatTmp4.y * s_sum) : static_cast<int8_t>(0);\n            tmp4.z = qual ? float_to_int8_rn(floatTmp4.z * s_sum) : static_cast<int8_t>(0);\n            tmp4.w = qual ? float_to_int8_rn(floatTmp4.w * s_sum) : static_cast<int8_t>(0);\n\n            buf4Ptr[inIdx] = tmp4;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len_padded + 31)/32*32)\n// for int8_t IO, I/O with int8_t element;\ntemplate<typename T>\n__global__ void softmax_COL32_perElement_varlen(int8_t*       output,\n                                                const int8_t* input,\n                                                const T*      attr_mask,\n                                                const int     batch_size,\n                                                const int     head_num,\n                                                const int     seq_len,\n                                                const int     seq_len_padded,\n                                                const float   scalar1a,\n                                                const float*  scalar1b,\n                                                const float*  amax_ptr,\n                                                const int     seq_len_x_seq_len,\n                                                const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    const int   tidx = threadIdx.x;\n\n    const bool qual        = tidx < seq_len;\n    const bool qual_padded = tidx < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n\n        int8_t tmp = 0;\n        int inIdx  = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded) + (tidx & 0xffffffe0) * seq_len\n                     + (seq_id << 5) + (tidx & 31));\n\n        // set softmax of padding word in rows to 0\n        const float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                output[inIdx] = tmp;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float floatTmp = qual ? (static_cast<float>(__ldg(input + inIdx)) * scalar1) : 0.0f;\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id  = tidx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            mask_val = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp = floatTmp + mask_val;\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(floatTmp) : blockReduceMax<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : floatTmp;\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            tmp           = qual ? float_to_int8_rn(floatTmp * s_sum) : static_cast<int8_t>(0);\n            output[inIdx] = tmp;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = (seq_len + 31)/32\n// for int32_t I; int8 O;\n// for seq_len <= 32\ntemplate<typename T>\n__global__ void softmax_COL32_LE32(int8_t*        output,\n                                   const int32_t* input,\n                                   const T*       attr_mask,\n                                   const int      batch_size,\n                                   const int      head_num,\n                                   const int      seq_len,\n                                   const float    scalar1a,\n                                   const float*   scalar1b,\n                                   const float*   scalar1c,\n                                   const float*   amax_ptr,\n                                   const int      head_num_x_seq_len,\n                                   const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdxx = threadIdx.x;\n    bool        qual       = threadIdxx < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        int inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdxx & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdxx & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                output[inIdx] = 0;\n            }\n            continue;\n        }\n\n        float floatTmp = qual ? static_cast<float>(__ldg(input + inIdx)) * scalar1 : 0.0f;\n\n        float mask_val, max_val;\n\n        __shared__ float s_max, s_sum;\n\n        mask_id  = qual ? threadIdxx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len : 0;\n        mask_val = qual ? (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f : 0.0f;\n        floatTmp = qual ? floatTmp + mask_val : 0.0f;\n        max_val  = qual ? floatTmp : -1e20f;\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n            output[inIdx] = float_to_int8_rn(floatTmp * s_sum);\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// attr_mask is [batch_size, seq_len, seq_len]\n// grid = (seq_len, batch_size, head_num)\n// block.x = seq_len_padded\n// for int8_t IO;\n// for seq_len_padded == 32\ntemplate<typename T>\n__global__ void softmax_COL32_LE32_varlen(int8_t*       output,\n                                          const int8_t* input,\n                                          const T*      attr_mask,\n                                          const int     batch_size,\n                                          const int     head_num,\n                                          const int     seq_len,\n                                          const int     seq_len_padded,\n                                          const float   scalar1a,\n                                          const float*  scalar1b,\n                                          const float*  amax_ptr,\n                                          const int     seq_len_x_seq_len,\n                                          const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdxx  = threadIdx.x;\n    const bool  qual        = threadIdxx < seq_len;\n    const bool  qual_padded = threadIdxx < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        int inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                    + (threadIdxx & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdxx & 31);\n\n        // set softmax of padding word in rows to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                output[inIdx] = 0;\n            }\n            continue;\n        }\n\n        float            mask_val, max_val;\n        __shared__ float s_max, s_sum;\n\n        // set softmax of padding word in cols to 0\n        float floatTmp = qual ? static_cast<float>(__ldg(input + inIdx)) * scalar1 : 0.0f;\n        mask_id        = qual ? threadIdxx + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len : 0;\n        mask_val       = qual ? (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f : 0.0f;\n        floatTmp       = qual ? floatTmp + mask_val : 0.0f;\n        max_val        = qual ? floatTmp : -1e20f;\n\n        max_val = warpReduceMax(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        floatTmp = qual ? __expf(floatTmp - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(floatTmp) : blockReduceSum<float>(floatTmp);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            output[inIdx] = qual ? float_to_int8_rn(floatTmp * s_sum) : static_cast<int8_t>(0);\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len, CUBLASLT_ORDER_COL32\n// grid = (seq_len, batch_size, head_num)\n// block.x = max(32, (seq_len/2 + 31)/32*32)\n// for int32_t I; int8 O;\n// for seq_len in (32, 64]\ntemplate<typename T>\n__global__ void softmax_COL32_LE64(int8_t*        output,\n                                   const int32_t* input,\n                                   const T*       attr_mask,\n                                   const int      batch_size,\n                                   const int      head_num,\n                                   const int      seq_len,\n                                   const float    scalar1a,\n                                   const float*   scalar1b,\n                                   const float*   scalar1c,\n                                   const float*   amax_ptr,\n                                   const int      head_num_x_seq_len,\n                                   const int      seq_len_x_seq_len)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b) * __ldg(scalar1c);\n    int         mask_id;\n    int         threadIdx2 = threadIdx.x << 1;\n\n    char2* buf2Ptr = (char2*)output;\n\n    bool qual = threadIdx2 < seq_len;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char2 tmp2  = {0, 0};\n        int   inIdx = (blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len) + (threadIdx2 & 0xffffffe0) * seq_len\n                    + (seq_id << 5) + (threadIdx2 & 31);\n\n        // set softmax of padding word to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual) {\n                buf2Ptr[inIdx >> 1] = tmp2;\n            }\n            continue;\n        }\n\n        float2 floatTmp2 = {0.0f, 0.0f};\n        if (qual) {\n            floatTmp2.x = static_cast<float>(__ldg(input + inIdx)) * scalar1;\n            floatTmp2.y = static_cast<float>(__ldg(input + inIdx + 1)) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx2 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp2.x = floatTmp2.x + mask_val;\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp2.y = floatTmp2.y + mask_val;\n\n            max_val = fmaxf(floatTmp2.x, floatTmp2.y);\n        }\n\n        max_val = blockDim.x <= 32 ? warpReduceMax(max_val) : blockReduceMax<float>(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp2.x = __expf(floatTmp2.x - s_max);\n            sum_val += floatTmp2.x;\n            floatTmp2.y = __expf(floatTmp2.y - s_max);\n            sum_val += floatTmp2.y;\n        }\n\n        sum_val = blockDim.x <= 32 ? warpReduceSum(sum_val) : blockReduceSum<float>(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual) {\n            tmp2.x              = float_to_int8_rn(floatTmp2.x * s_sum);\n            tmp2.y              = float_to_int8_rn(floatTmp2.y * s_sum);\n            buf2Ptr[inIdx >> 1] = tmp2;\n        }\n    }\n}\n\n// input are a series of sub-matrixes of m = seq_len, n = seq_len_padded, CUBLASLT_ORDER_COL32\n// seq_len_padded = (seq_len+31)/32*32\n// grid = (seq_len, batch_size, head_num)\n// block.x = 32\n// for int8_t IO\n// for seq_len in (32, 64]\ntemplate<typename T>\n__global__ void softmax_COL32_LE64_varlen(int8_t*       output,\n                                          const int8_t* input,\n                                          const T*      attr_mask,\n                                          const int     batch_size,\n                                          const int     head_num,\n                                          const int     seq_len,\n                                          const int     seq_len_padded,\n                                          const float   scalar1a,\n                                          const float*  scalar1b,\n                                          const float*  amax_ptr,\n                                          const int     seq_len_x_seq_len,\n                                          const int     seq_len_x_seq_len_padded)\n{\n    const float amax    = __ldg(amax_ptr);\n    const float scalar1 = scalar1a * __ldg(scalar1b);\n    int         mask_id;\n    int         threadIdx2 = threadIdx.x << 1;\n\n    char2*       buf2Ptr   = (char2*)output;\n    const char2* inBuf2Ptr = (const char2*)input;\n\n    const bool qual        = threadIdx2 < seq_len;\n    const bool qual_padded = threadIdx2 < seq_len_padded;\n    for (int seq_id = blockIdx.x; seq_id < seq_len; seq_id += gridDim.x) {\n        char2 tmp2  = {0, 0};\n        int   inIdx = ((blockIdx.y * head_num + blockIdx.z) * (seq_len_x_seq_len_padded)\n                     + (threadIdx2 & 0xffffffe0) * seq_len + (seq_id << 5) + (threadIdx2 & 31))\n                    >> 1;\n\n        // set softmax of padding word in rows to 0\n        float mask_in_seq = static_cast<float>(__ldg(attr_mask + (blockIdx.y * seq_len_x_seq_len + seq_id)));\n        if (mask_in_seq < 0.1f) {\n            if (qual_padded) {\n                buf2Ptr[inIdx] = tmp2;\n            }\n            continue;\n        }\n\n        // set softmax of padding word in cols to 0\n        float2 floatTmp2 = {0.0f, 0.0f};\n        if (qual) {\n            tmp2        = __ldg(inBuf2Ptr + inIdx);\n            floatTmp2.x = static_cast<float>(tmp2.x) * scalar1;\n            floatTmp2.y = static_cast<float>(tmp2.y) * scalar1;\n        }\n\n        float mask_val, max_val;\n        max_val = -1e20f;\n\n        __shared__ float s_max, s_sum;\n\n        if (qual) {\n            mask_id = threadIdx2 + blockIdx.y * seq_len_x_seq_len + seq_id * seq_len;\n            // for x\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id))) * -10000.0f;\n            floatTmp2.x = floatTmp2.x + mask_val;\n\n            // for y\n            mask_val    = (1.0f - static_cast<float>(__ldg(attr_mask + mask_id + 1))) * -10000.0f;\n            floatTmp2.y = floatTmp2.y + mask_val;\n\n            max_val = fmaxf(floatTmp2.x, floatTmp2.y);\n        }\n\n        max_val = warpReduceMax(max_val);\n\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float sum_val = 0.0f;\n\n        if (qual) {\n            floatTmp2.x = __expf(floatTmp2.x - s_max);\n            sum_val += floatTmp2.x;\n            floatTmp2.y = __expf(floatTmp2.y - s_max);\n            sum_val += floatTmp2.y;\n        }\n\n        sum_val = warpReduceSum(sum_val);\n\n        if (threadIdx.x == 0) {\n            s_sum = __fdividef(127.0f, (sum_val + 1e-6f));\n            s_sum = __fdividef(s_sum, amax);\n        }\n        __syncthreads();\n\n        if (qual_padded) {\n            tmp2.x         = qual ? float_to_int8_rn(floatTmp2.x * s_sum) : static_cast<int8_t>(0);\n            tmp2.y         = qual ? float_to_int8_rn(floatTmp2.y * s_sum) : static_cast<int8_t>(0);\n            buf2Ptr[inIdx] = tmp2;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeSoftmaxCOL32(int8_t*        output,\n                        const int32_t* input,\n                        const T*       attr_mask,\n                        const int      batch_size,\n                        const int      head_num,\n                        const int      seq_len,\n                        const float    scalar1a,\n                        const float*   scalar1b,\n                        const float*   scalar1c,\n                        const float*   amax_ptr,\n                        hipStream_t   stream)\n{\n    dim3 grid, block;\n    grid.x = seq_len;\n    grid.y = batch_size;\n    grid.z = head_num;\n\n    if (seq_len <= 32) {\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        block.x = (seq_len + 31) / 32 * 32;\n        softmax_COL32_LE32<<<grid, block, 0, stream>>>(output,\n                                                       input,\n                                                       attr_mask,\n                                                       batch_size,\n                                                       head_num,\n                                                       seq_len,\n                                                       scalar1a,\n                                                       scalar1b,\n                                                       scalar1c,\n                                                       amax_ptr,\n                                                       seq_len * head_num,\n                                                       seq_len * seq_len);\n    }\n    else if (seq_len <= 64) {\n        assert(seq_len % 2 == 0);\n        block.x = (seq_len / 2 + 31) / 32 * 32;\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        softmax_COL32_LE64<<<grid, block, 0, stream>>>(output,\n                                                       input,\n                                                       attr_mask,\n                                                       batch_size,\n                                                       head_num,\n                                                       seq_len,\n                                                       scalar1a,\n                                                       scalar1b,\n                                                       scalar1c,\n                                                       amax_ptr,\n                                                       seq_len * head_num,\n                                                       seq_len * seq_len);\n    }\n    else {\n        assert(seq_len % 4 == 0);\n        block.x = (seq_len / 4 + 31) / 32 * 32;\n        softmax_COL32<<<grid, block, 0, stream>>>(output,\n                                                  input,\n                                                  attr_mask,\n                                                  batch_size,\n                                                  head_num,\n                                                  seq_len,\n                                                  scalar1a,\n                                                  scalar1b,\n                                                  scalar1c,\n                                                  amax_ptr,\n                                                  seq_len * head_num,\n                                                  seq_len * seq_len);\n    }\n}\n\ntemplate void invokeSoftmaxCOL32(int8_t*        output,\n                                 const int32_t* input,\n                                 const float*   attr_mask,\n                                 const int      batch_size,\n                                 const int      head_num,\n                                 const int      seq_len,\n                                 const float    scalar1a,\n                                 const float*   scalar1b,\n                                 const float*   scalar1c,\n                                 const float*   amax_ptr,\n                                 hipStream_t   stream);\n\ntemplate void invokeSoftmaxCOL32(int8_t*        output,\n                                 const int32_t* input,\n                                 const half*    attr_mask,\n                                 const int      batch_size,\n                                 const int      head_num,\n                                 const int      seq_len,\n                                 const float    scalar1a,\n                                 const float*   scalar1b,\n                                 const float*   scalar1c,\n                                 const float*   amax_ptr,\n                                 hipStream_t   stream);\n\ntemplate<typename T>\nvoid invokeSoftmaxCOL32(int8_t*       output,\n                        const int8_t* input,\n                        const T*      attr_mask,\n                        const int     batch_size,\n                        const int     head_num,\n                        const int     seq_len,\n                        const float   scalar1a,\n                        const float*  scalar1b,\n                        const float*  amax_ptr,\n                        hipStream_t  stream)\n{\n    dim3 grid, block;\n    grid.x                   = seq_len;\n    grid.y                   = batch_size;\n    grid.z                   = head_num;\n    const int seq_len_padded = (seq_len + 31) / 32 * 32;\n\n    if (seq_len <= 32) {\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        block.x = seq_len_padded;\n        softmax_COL32_LE32_varlen<<<grid, block, 0, stream>>>(output,\n                                                              input,\n                                                              attr_mask,\n                                                              batch_size,\n                                                              head_num,\n                                                              seq_len,\n                                                              seq_len_padded,\n                                                              scalar1a,\n                                                              scalar1b,\n                                                              amax_ptr,\n                                                              seq_len * seq_len,\n                                                              seq_len * seq_len_padded);\n    }\n    else if (seq_len <= 64 && (seq_len % 2 == 0)) {\n        block.x = 32;\n        if (batch_size * head_num > 960) {\n            grid.x = ceil(float(seq_len) / 32.0f);\n        }\n        softmax_COL32_LE64_varlen<<<grid, block, 0, stream>>>(output,\n                                                              input,\n                                                              attr_mask,\n                                                              batch_size,\n                                                              head_num,\n                                                              seq_len,\n                                                              seq_len_padded,\n                                                              scalar1a,\n                                                              scalar1b,\n                                                              amax_ptr,\n                                                              seq_len * seq_len,\n                                                              seq_len * seq_len_padded);\n    }\n    else if (seq_len > 64 && (seq_len % 4 == 0)) {\n        block.x = (seq_len_padded / 4 + 31) / 32 * 32;\n        softmax_COL32_varlen<<<grid, block, 0, stream>>>(output,\n                                                         input,\n                                                         attr_mask,\n                                                         batch_size,\n                                                         head_num,\n                                                         seq_len,\n                                                         seq_len_padded,\n                                                         scalar1a,\n                                                         scalar1b,\n                                                         amax_ptr,\n                                                         seq_len * seq_len,\n                                                         seq_len * seq_len_padded);\n    }\n    else {\n        block.x = (seq_len_padded + 31) / 32 * 32;\n        softmax_COL32_perElement_varlen<<<grid, block, 0, stream>>>(output,\n                                                                    input,\n                                                                    attr_mask,\n                                                                    batch_size,\n                                                                    head_num,\n                                                                    seq_len,\n                                                                    seq_len_padded,\n                                                                    scalar1a,\n                                                                    scalar1b,\n                                                                    amax_ptr,\n                                                                    seq_len * seq_len,\n                                                                    seq_len * seq_len_padded);\n    }\n}\n\ntemplate void invokeSoftmaxCOL32(int8_t*       output,\n                                 const int8_t* input,\n                                 const float*  attr_mask,\n                                 const int     batch_size,\n                                 const int     head_num,\n                                 const int     seq_len,\n                                 const float   scalar1a,\n                                 const float*  scalar1b,\n                                 const float*  amax_ptr,\n                                 hipStream_t  stream);\n\ntemplate void invokeSoftmaxCOL32(int8_t*       output,\n                                 const int8_t* input,\n                                 const half*   attr_mask,\n                                 const int     batch_size,\n                                 const int     head_num,\n                                 const int     seq_len,\n                                 const float   scalar1a,\n                                 const float*  scalar1b,\n                                 const float*  amax_ptr,\n                                 hipStream_t  stream);\n\n/*******************  invokeSoftmaxCOL32  ***********************/\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T>\n__global__ void softmax_INT8IO_kernel_COL32(int8_t*      a_buf,\n                                            int8_t*      qk_buf_int8,\n                                            const T*     attn_mask,\n                                            const T*     relative_pos_bias,\n                                            const int    batch_size,\n                                            const int    num_head,\n                                            const int    window_num,\n                                            const int    window_len,\n                                            const int    window_len_x_window_len,\n                                            const float  scalar,\n                                            const float* deQ_scale_ptr,\n                                            const float* out_scale_ptr)\n{\n\n    bool      qual          = threadIdx.x < window_len;\n    const int padded_winlen = (window_len + 31) / 32 * 32;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len * padded_winlen\n                        + ((threadIdx.x >> 5) << 5) * window_len + (window_id << 5) + (threadIdx.x & 31);\n        ;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + threadIdx.x;\n\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            float     mask_val =\n                (attn_mask == nullptr) ?\n                        0.0f :\n                        static_cast<float>(\n                        __ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window)));\n            tmp = scalar * static_cast<float>(qk_buf_int8[qk_offset]) * __ldg(deQ_scale_ptr) + mask_val\n                  + static_cast<float>(__ldg(relative_pos_bias + relative_pos_bias_offset));\n        }\n\n        float max_val = blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float qk_tmp  = qual ? __expf(tmp - s_max) : 0.0f;\n        float sum_val = blockReduceSum<float>(qk_tmp);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        a_buf[qk_offset] = qual ? float_to_int8_rn(qk_tmp * s_mean * __ldg(out_scale_ptr)) : 0;\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = (window_len / 4 + 31)/32*32\n// a_buf/qk_buf is [batch, window_num, num_head, window_len, window_len] + COL32\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-major\ntemplate<typename T4, typename T>\n__global__ void softmax_INT8IO_kernel_COL32_element4(int8_t*      a_buf,\n                                                     int8_t*      qk_buf_int8,\n                                                     const T4*    attn_mask,\n                                                     const T4*    relative_pos_bias,\n                                                     const int    batch_size,\n                                                     const int    num_head,\n                                                     const int    window_num,\n                                                     const int    window_len,\n                                                     const int    window_len_x_window_len,\n                                                     const float  scalar,\n                                                     const float* deQ_scale_ptr,\n                                                     const float* out_scale_ptr)\n{\n\n    const int   padded_winlen = (window_len + 31) / 32 * 32;\n    const int   col_id        = threadIdx.x << 2;\n    bool        qual          = col_id < window_len;\n    const T4    zero          = {T(0.0f), T(0.0f), T(0.0f), T(0.0f)};\n    const float deQ_scale     = __ldg(deQ_scale_ptr);\n    const float out_scale     = __ldg(out_scale_ptr);\n    char4*      inputPtr      = (char4*)qk_buf_int8;\n    char4*      outputPtr     = (char4*)a_buf;\n\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        char4            qk_val;\n        float4           local_qk_val;\n        __shared__ float s_mean, s_max;\n        int              qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len * padded_winlen\n                        + ((col_id >> 5) << 5) * window_len + (window_id << 5) + (col_id & 31);\n        if (qual) {\n            const int offset_in_window         = window_id * window_len + col_id;\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            const int attn_mask_offset         = (blockIdx.y / num_head) * window_len_x_window_len + offset_in_window;\n\n            const T4 bias_val = relative_pos_bias[relative_pos_bias_offset >> 2];\n            const T4 mask_val = (attn_mask == nullptr) ? zero : attn_mask[attn_mask_offset >> 2];\n            qk_val            = inputPtr[qk_offset >> 2];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            local_qk_val.z    = static_cast<float>(qk_val.z);\n            local_qk_val.w    = static_cast<float>(qk_val.w);\n\n            local_qk_val.x =\n                scalar * local_qk_val.x * deQ_scale + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                scalar * local_qk_val.y * deQ_scale + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            local_qk_val.z =\n                scalar * local_qk_val.z * deQ_scale + static_cast<float>(mask_val.z) + static_cast<float>(bias_val.z);\n            local_qk_val.w =\n                scalar * local_qk_val.w * deQ_scale + static_cast<float>(mask_val.w) + static_cast<float>(bias_val.w);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n            tmp = tmp > local_qk_val.z ? tmp : local_qk_val.z;\n            tmp = tmp > local_qk_val.w ? tmp : local_qk_val.w;\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n        local_qk_val.z = qual ? __expf(local_qk_val.z - s_max) : 0.0f;\n        local_qk_val.w = qual ? __expf(local_qk_val.w - s_max) : 0.0f;\n\n        float sum_val = blockDim.x <= 32 ?\n                            warpReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w) :\n                            blockReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            char4 outTmp;\n            outTmp.x                  = float_to_int8_rn(local_qk_val.x * s_mean * out_scale);\n            outTmp.y                  = float_to_int8_rn(local_qk_val.y * s_mean * out_scale);\n            outTmp.z                  = float_to_int8_rn(local_qk_val.z * s_mean * out_scale);\n            outTmp.w                  = float_to_int8_rn(local_qk_val.w * s_mean * out_scale);\n            outputPtr[qk_offset >> 2] = outTmp;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                      int8_t*      qk_buf_int8,\n                                      const T*     attn_mask,\n                                      const T*     relative_pos_bias,\n                                      const int    batch_size,\n                                      const int    num_head,\n                                      const int    window_num,\n                                      const int    window_len,\n                                      const float  scalar,\n                                      const float* deQ_scale_ptr,\n                                      const float* out_scale_ptr,\n                                      hipStream_t stream)\n{\n    dim3 grid(window_len, window_num * num_head, batch_size);\n    if (window_len % 4 == 0 && window_len / 4 >= 32) {\n        dim3 block((window_len / 4 + 31) / 32 * 32);\n        if (batch_size * window_num * num_head > 960) {\n            grid.x = ceil(float(window_len) / 32.0f);\n        }\n        if (std::is_same<T, float>::value) {\n            softmax_INT8IO_kernel_COL32_element4<float4, float>\n                <<<grid, block, 0, stream>>>(a_buf,\n                                             qk_buf_int8,\n                                             (const float4*)attn_mask,\n                                             (const float4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             scalar,\n                                             deQ_scale_ptr,\n                                             out_scale_ptr);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_INT8IO_kernel_COL32_element4<half4, half>\n                <<<grid, block, 0, stream>>>(a_buf,\n                                             qk_buf_int8,\n                                             (const half4*)attn_mask,\n                                             (const half4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             scalar,\n                                             deQ_scale_ptr,\n                                             out_scale_ptr);\n        }\n    }\n    else {\n        dim3 block((window_len + 31) / 32 * 32);\n        softmax_INT8IO_kernel_COL32<<<grid, block, 0, stream>>>(a_buf,\n                                                                qk_buf_int8,\n                                                                attn_mask,\n                                                                relative_pos_bias,\n                                                                batch_size,\n                                                                num_head,\n                                                                window_num,\n                                                                window_len,\n                                                                window_len * window_len,\n                                                                scalar,\n                                                                deQ_scale_ptr,\n                                                                out_scale_ptr);\n    }\n}\n\ntemplate void invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                               int8_t*      qk_buf_int8,\n                                               const float* attn_mask,\n                                               const float* relative_pos_bias,\n                                               const int    batch_size,\n                                               const int    num_head,\n                                               const int    window_num,\n                                               const int    window_len,\n                                               const float  scalar,\n                                               const float* deQ_scale_ptr,\n                                               const float* output_scale_ptr,\n                                               hipStream_t stream);\n\ntemplate void invokeSoftmaxWithRelPosBiasCOL32(int8_t*      a_buf,\n                                               int8_t*      qk_buf_int8,\n                                               const half*  attn_mask,\n                                               const half*  relative_pos_bias,\n                                               const int    batch_size,\n                                               const int    num_head,\n                                               const int    window_num,\n                                               const int    window_len,\n                                               const float  scalar,\n                                               const float* deQ_scale_ptr,\n                                               const float* output_scale_ptr,\n                                               hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/softmax_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/layernorm_int8_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n// input1/input2/output matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n)\n// for per_channel_quantization for weight\n__global__ void add_bias_input_layernorm_COL32_int32I_DataTypeO(float*         output,\n                                                                const int32_t* input1,\n                                                                const float*   input2,\n                                                                const float*   bias,\n                                                                const float*   gamma,\n                                                                const float*   beta,\n                                                                int            m,\n                                                                int            n,\n                                                                const float*   weight_amax,\n                                                                const float*   input1_amax_ptr)\n{\n    const float input1_amax = __ldg(input1_amax_ptr);\n    int         col_start   = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31));\n\n    float tmp = static_cast<float>(__ldg(input1 + outIdx)) * __ldg(weight_amax + col_start) * input1_amax\n                * 0.000062f;  //(1/127/127);\n    float inputTmp = __ldg(input2 + outIdx);\n\n    local_out = tmp + inputTmp + __ldg(bias + col_start);\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = __fdividef(mean, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n    if (threadIdx.x == 0) {\n        s_variance = __fdividef(variance, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * __ldg(gamma + col_start) + __ldg(beta + col_start);\n\n    output[outIdx] = local_out;\n}\n\n__global__ void add_bias_input_layernorm_COL32_int32I_DataTypeO(half2*        output,\n                                                                const int2*   input1,\n                                                                const half2*  input2,\n                                                                const half2*  bias,\n                                                                const half2*  gamma,\n                                                                const half2*  beta,\n                                                                int           m,\n                                                                int           n,\n                                                                const float2* weight_amax,\n                                                                const float*  input1_amax_ptr)\n{\n    int col_start = threadIdx.x << 1;\n\n    const float input1_amax = __ldg(input1_amax_ptr);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    const int2   input1Tmp = __ldg(input1 + outIdx);\n    const float2 weightTmp = __ldg(weight_amax + threadIdx.x);\n\n    float2 addTmp2;\n    addTmp2.x = static_cast<float>(input1Tmp.x) * weightTmp.x * input1_amax * 0.000062f;  //(1/127/127);\n    addTmp2.y = static_cast<float>(input1Tmp.y) * weightTmp.y * input1_amax * 0.000062f;  //(1/127/127);\n\n    const half2 inputTmp = __ldg(input2 + outIdx);\n    const half2 biasTmp  = __ldg(bias + threadIdx.x);\n\n    local_out   = __half22float2(__hadd2(inputTmp, biasTmp));\n    local_out.x = local_out.x + addTmp2.x;\n    local_out.y = local_out.y + addTmp2.y;\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = __fdividef(mean, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n    if (threadIdx.x == 0) {\n        s_variance = __fdividef(variance, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    float2      outputTmp;\n    const half2 gammaTmp = __ldg(gamma + threadIdx.x);\n    const half2 betaTmp  = __ldg(beta + threadIdx.x);\n\n    outputTmp.x = (local_out.x * s_variance) * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n    outputTmp.y = (local_out.y * s_variance) * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n\n    output[outIdx] = __float22half2_rn(outputTmp);\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(T*             output,\n                                         const int32_t* input1,\n                                         const T*       input2,\n                                         const T*       bias,\n                                         const T*       gamma,\n                                         const T*       beta,\n                                         int            m,\n                                         int            n,\n                                         cudaStream_t   stream,\n                                         const float*   weight_amax,\n                                         const float*   input1_amax_ptr)\n{\n\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        block.x /= 2;\n        assert(block.x <= 1024);\n        add_bias_input_layernorm_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                    (const int2*)input1,\n                                                                                    (const half2*)input2,\n                                                                                    (const half2*)bias,\n                                                                                    (const half2*)gamma,\n                                                                                    (const half2*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    (const float2*)weight_amax,\n                                                                                    input1_amax_ptr);\n    }\n    else {\n        assert(block.x <= 1024);\n        add_bias_input_layernorm_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((float*)output,\n                                                                                    input1,\n                                                                                    (const float*)input2,\n                                                                                    (const float*)bias,\n                                                                                    (const float*)gamma,\n                                                                                    (const float*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    weight_amax,\n                                                                                    input1_amax_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32(float*         output,\n                                                  const int32_t* input1,\n                                                  const float*   input2,\n                                                  const float*   bias,\n                                                  const float*   gamma,\n                                                  const float*   beta,\n                                                  int            m,\n                                                  int            n,\n                                                  cudaStream_t   stream,\n                                                  const float*   weight_amax,\n                                                  const float*   input1_amax_ptr);\ntemplate void invokeAddBiasResidualLayerNormCol32(half*          output,\n                                                  const int32_t* input1,\n                                                  const half*    input2,\n                                                  const half*    bias,\n                                                  const half*    gamma,\n                                                  const half*    beta,\n                                                  int            m,\n                                                  int            n,\n                                                  cudaStream_t   stream,\n                                                  const float*   weight_amax,\n                                                  const float*   input1_amax_ptr);\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const T*      bias,\n                                                      const T*      gamma,\n                                                      const T*      beta,\n                                                      int           m,\n                                                      int           n,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr,\n                                                      const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const half2*  bias,\n                                                      const half2*  gamma,\n                                                      const half2*  beta,\n                                                      int           m,\n                                                      int           n,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr,\n                                                      const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int   col_start_tmp = col_start;\n    half2 biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[0]        = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    col_start_tmp = col_start_tmp + 1;\n    biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    half2 betaTmp = __ldg(beta + col_start_tmp);\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.x  = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.y  = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start     = col_start + 1;\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    betaTmp       = __ldg(beta + col_start_tmp);\n    local_out[2]  = (local_out[2] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.z   = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.w  = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                         const int8_t* input1,\n                                         const int8_t* input2,\n                                         const T*      bias,\n                                         const T*      gamma,\n                                         const T*      beta,\n                                         int           m,\n                                         int           n,\n                                         cudaStream_t  stream,\n                                         const float*  input1_deQFactor_ptr,\n                                         const float*  input2_deQFactor_ptr,\n                                         const float*  output_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(n <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_input_layernorm_COL32_int8IO<<<grid, block, 0, stream>>>(output,\n                                                                          input1,\n                                                                          input2,\n                                                                          (const half2*)bias,\n                                                                          (const half2*)gamma,\n                                                                          (const half2*)beta,\n                                                                          m,\n                                                                          n,\n                                                                          input1_deQFactor_ptr,\n                                                                          input2_deQFactor_ptr,\n                                                                          output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_COL32_int8IO<T><<<grid, block, 0, stream>>>(output,\n                                                                             input1,\n                                                                             input2,\n                                                                             bias,\n                                                                             gamma,\n                                                                             beta,\n                                                                             m,\n                                                                             n,\n                                                                             input1_deQFactor_ptr,\n                                                                             input2_deQFactor_ptr,\n                                                                             output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                                  const int8_t* input1,\n                                                  const int8_t* input2,\n                                                  const float*  bias,\n                                                  const float*  gamma,\n                                                  const float*  beta,\n                                                  int           m,\n                                                  int           n,\n                                                  cudaStream_t  stream,\n                                                  const float*  input1_deQFactor_ptr,\n                                                  const float*  input2_deQFactor_ptr,\n                                                  const float*  output_scale_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                                  const int8_t* input1,\n                                                  const int8_t* input2,\n                                                  const half*   bias,\n                                                  const half*   gamma,\n                                                  const half*   beta,\n                                                  int           m,\n                                                  int           n,\n                                                  cudaStream_t  stream,\n                                                  const float*  input1_deQFactor_ptr,\n                                                  const float*  input2_deQFactor_ptr,\n                                                  const float*  output_scale_ptr);\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n)\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8I_DataTypeO(T*            output,\n                                                               const int8_t* input1,\n                                                               const int8_t* input2,\n                                                               const T*      bias,\n                                                               const T*      gamma,\n                                                               const T*      beta,\n                                                               int           m,\n                                                               int           n,\n                                                               const float*  input1_deQFactor_ptr,\n                                                               const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   idx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31));\n\n    local_out = static_cast<float>(__ldg(input2 + idx)) * input2_deQFactor\n                + static_cast<float>(__ldg(input1 + idx)) * input1_deQFactor\n                + static_cast<float>(__ldg(bias + col_start));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                + static_cast<float>(__ldg(beta + col_start));\n\n    output[idx] = local_out;\n}\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/2)\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8I_DataTypeO(half2*        output,\n                                                               const int8_t* input1,\n                                                               const int8_t* input2,\n                                                               const half2*  bias,\n                                                               const half2*  gamma,\n                                                               const half2*  beta,\n                                                               int           m,\n                                                               int           n,\n                                                               const float*  input1_deQFactor_ptr,\n                                                               const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 1;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    idx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    const char2* input1_ptr2 = (const char2*)input1;\n    const char2* input2_ptr2 = (const char2*)input2;\n    char2        input_tmp1  = __ldg(input1_ptr2 + idx);\n    char2        input_tmp2  = __ldg(input2_ptr2 + idx);\n\n    half2 bias_tmp = __ldg(bias + threadIdx.x);\n\n    local_out.x = static_cast<float>(input_tmp1.x) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.x) * input2_deQFactor + static_cast<float>(bias_tmp.x);\n\n    local_out.y = static_cast<float>(input_tmp1.y) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.y) * input2_deQFactor + static_cast<float>(bias_tmp.y);\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    half2 gamma_tmp = __ldg(gamma + threadIdx.x);\n    half2 beta_tmp  = __ldg(beta + threadIdx.x);\n\n    local_out.x = (local_out.x * s_variance) * static_cast<float>(gamma_tmp.x) + static_cast<float>(beta_tmp.x);\n    local_out.y = (local_out.y * s_variance) * static_cast<float>(gamma_tmp.y) + static_cast<float>(beta_tmp.y);\n\n    bias_tmp.x = half(local_out.x);\n    bias_tmp.y = half(local_out.y);\n\n    output[idx] = bias_tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(T*            output,\n                                         const int8_t* input1,\n                                         const int8_t* input2,\n                                         const T*      bias,\n                                         const T*      gamma,\n                                         const T*      beta,\n                                         int           m,\n                                         int           n,\n                                         cudaStream_t  stream,\n                                         const float*  input1_deQFactor_ptr,\n                                         const float*  input2_deQFactor_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        assert(n / 2 <= 1024 && n % 2 == 0);\n        block.x = n / 2;\n        add_bias_input_layernorm_COL32_int8I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                   input1,\n                                                                                   input2,\n                                                                                   (const half2*)bias,\n                                                                                   (const half2*)gamma,\n                                                                                   (const half2*)beta,\n                                                                                   m,\n                                                                                   n,\n                                                                                   input1_deQFactor_ptr,\n                                                                                   input2_deQFactor_ptr);\n    }\n    else {\n        assert(n <= 1024);\n        add_bias_input_layernorm_COL32_int8I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, input1_deQFactor_ptr, input2_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32<float>(float*        output,\n                                                         const int8_t* input1,\n                                                         const int8_t* input2,\n                                                         const float*  bias,\n                                                         const float*  gamma,\n                                                         const float*  beta,\n                                                         int           m,\n                                                         int           n,\n                                                         cudaStream_t  stream,\n                                                         const float*  input1_deQFactor_ptr,\n                                                         const float*  input2_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormCol32<half>(half*         output,\n                                                        const int8_t* input1,\n                                                        const int8_t* input2,\n                                                        const half*   bias,\n                                                        const half*   gamma,\n                                                        const half*   beta,\n                                                        int           m,\n                                                        int           n,\n                                                        cudaStream_t  stream,\n                                                        const float*  input1_deQFactor_ptr,\n                                                        const float*  input2_deQFactor_ptr);\n\n/*******************  invokeAddBiasLayernormAddRes  ***********************/\ntemplate<typename T, int T_per_thread>\n__global__ void add_bias_layernorm_add_res(int8_t*       out_int8,\n                                           const int8_t* out,\n                                           T*            residual_input,\n                                           const T*      bias,\n                                           const T*      gamma,\n                                           const T*      beta,\n                                           const float   layernorm_eps,\n                                           int           m,\n                                           int           n,\n                                           const float*  input_deQFactor_ptr,\n                                           const float*  out_QFactor_ptr)\n{\n    const float      input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float      out_QFactor     = out_QFactor_ptr == nullptr ? -1.0f : __ldg(out_QFactor_ptr);\n    int              tid             = threadIdx.x;\n    const int        bdim            = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_val[T_per_thread];\n\n    // float sums[2] = {0.0f, 0.0f};\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n) {\n            T bias_val = bias[col_start];\n\n            int inIdx    = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n            T   out_val  = static_cast<T>(out[inIdx]) * static_cast<T>(input_deQFactor);\n            T   tmp      = out_val + bias_val;\n            local_val[i] = tmp;\n            local_sum += local_val[i];\n        }\n    }\n\n    blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        local_sum += (local_val[i] - s_mean) * (local_val[i] - s_mean);\n    }\n\n    blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(local_sum * __fdividef(1.0f, n) + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n) {\n            T gamma_val = gamma[col_start];\n            T beta_val  = beta[col_start];\n\n            int   outIdx  = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n            T     res_val = residual_input[outIdx];\n            float output_val =\n                (local_val[i] - s_mean) * s_variance * (float)(gamma_val) + (float)(beta_val) + (float)(res_val);\n\n            if (out_QFactor_ptr != nullptr) {\n                residual_input[outIdx] = (T)output_val;\n                out_int8[outIdx]       = float_to_int8_rn(output_val * out_QFactor);\n            }\n            else {\n                T* out_T_ptr      = reinterpret_cast<T*>(out_int8);\n                out_T_ptr[outIdx] = (T)output_val;\n            }\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(char2*       out_int8,\n                                              const char2* out,\n                                              half2*       residual_input,\n                                              const half2* bias,\n                                              const half2* gamma,\n                                              const half2* beta,\n                                              const float  layernorm_eps,\n                                              int          m,\n                                              int          n,\n                                              const float* input_deQFactor_ptr,\n                                              const float* out_QFactor_ptr)\n{\n    const float      input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float      out_QFactor     = out_QFactor_ptr == nullptr ? -1.0f : __ldg(out_QFactor_ptr);\n    int              tid             = threadIdx.x;\n    const int        bdim            = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    const int        n_2 = n >> 1;\n\n    half2 local_val[T_per_thread];\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        // int index = i * bdim + tid;\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            half2 bias_val = bias[col_start];\n\n            int   col_id = col_start << 1;\n            int   inIdx  = ((col_id & 0xffffffe0) * m + (blockIdx.x << 5) + (col_id & 31)) >> 1;\n            char2 outTmp = out[inIdx];\n            half2 out_val;\n            out_val.x = static_cast<half>(outTmp.x) * static_cast<half>(input_deQFactor);\n            out_val.y = static_cast<half>(outTmp.y) * static_cast<half>(input_deQFactor);\n\n            half2 tmp         = __hadd2(out_val, bias_val);\n            local_val[i]      = tmp;\n            float2 tmp_float2 = __half22float2(tmp);\n            local_sum += tmp_float2.x + tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            local_sum +=\n                (tmp_float2.x - s_mean) * (tmp_float2.x - s_mean) + (tmp_float2.y - s_mean) * (tmp_float2.y - s_mean);\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(local_sum * __fdividef(1.0f, n) + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            float2 gamma_val = __half22float2(gamma[col_start]);\n            float2 beta_val  = __half22float2(beta[col_start]);\n\n            int    col_id  = col_start << 1;\n            int    outIdx  = ((col_id & 0xffffffe0) * m + (blockIdx.x << 5) + (col_id & 31)) >> 1;\n            float2 res_val = __half22float2(residual_input[outIdx]);\n            float2 output_val;\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            output_val.x      = (tmp_float2.x - s_mean) * s_variance * gamma_val.x + beta_val.x + res_val.x;\n            output_val.y      = (tmp_float2.y - s_mean) * s_variance * gamma_val.y + beta_val.y + res_val.y;\n            if (out_QFactor_ptr != nullptr) {\n                residual_input[outIdx] = __float22half2_rn(output_val);\n                char2 outTmp;\n                outTmp.x         = float_to_int8_rn(output_val.x * out_QFactor);\n                outTmp.y         = float_to_int8_rn(output_val.y * out_QFactor);\n                out_int8[outIdx] = outTmp;\n            }\n            else {\n                half2* out_ptr  = reinterpret_cast<half2*>(out_int8);\n                out_ptr[outIdx] = __float22half2_rn(output_val);\n            }\n        }\n    }\n}\n\n#define MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec)                                                                          \\\n    blockSize = (blockSize + TVec_per_thread - 1) / TVec_per_thread;                                                   \\\n    if (T_per_TVec == 2) {                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((char2*)out_int8,              \\\n                                                                                        (const char2*)out,             \\\n                                                                                        (half2*)residual_input,        \\\n                                                                                        (const half2*)bias,            \\\n                                                                                        (const half2*)gamma,           \\\n                                                                                        (const half2*)beta,            \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        m,                             \\\n                                                                                        n,                             \\\n                                                                                        input_deQFactor_ptr,           \\\n                                                                                        out_QFactor_ptr);              \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");                         \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res<half, TVec_per_thread><<<m, blockSize, 0, stream>>>((int8_t*)out_int8,          \\\n                                                                                           (const int8_t*)out,         \\\n                                                                                           (half*)residual_input,      \\\n                                                                                           (const half*)bias,          \\\n                                                                                           (const half*)gamma,         \\\n                                                                                           (const half*)beta,          \\\n                                                                                           layernorm_eps,              \\\n                                                                                           m,                          \\\n                                                                                           n,                          \\\n                                                                                           input_deQFactor_ptr,        \\\n                                                                                           out_QFactor_ptr);           \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");                         \\\n        }                                                                                                              \\\n    }\n\ntemplate<typename T>\nvoid invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                       int8_t*      out,\n                                       T*           residual_input,\n                                       const T*     bias,\n                                       const T*     gamma,\n                                       const T*     beta,\n                                       const float  layernorm_eps,\n                                       int          m,\n                                       int          n,\n                                       cudaStream_t stream,\n                                       const float* input_deQFactor_ptr,\n                                       const float* out_QFactor_ptr)\n{\n    if (n % 2 == 0) {\n        const int T_per_TVec = 2;\n        int       blockSize  = (n / 2 + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");\n        }\n    }\n    else {\n        const int T_per_TVec = 1;\n        int       blockSize  = (n + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 32768) {\n            const int TVec_per_thread = 32;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                                int8_t*      out,\n                                                float*       residual_input,\n                                                const float* bias,\n                                                const float* gamma,\n                                                const float* beta,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                cudaStream_t stream,\n                                                const float* input_deQFactor_ptr,\n                                                const float* out_QFactor_ptr);\n\ntemplate void invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                                int8_t*      out,\n                                                half*        residual_input,\n                                                const half*  bias,\n                                                const half*  gamma,\n                                                const half*  beta,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                cudaStream_t stream,\n                                                const float* input_deQFactor_ptr,\n                                                const float* out_QFactor_ptr);\n\n/*******************  invokeGeneralAddBiasResidualPreLayerNormCol32  ***********************/\n\n// each warp process 1 row, and increase the num of warps per block to increase occupancy\ntemplate<int WPB, int VPT, bool WRITE_RESIDUAL, bool IS_BIAS, bool IS_INPUT>\n__global__ void add_bias_input_layernorm_COL32_int8IO_warpReduce(int8_t*       output,\n                                                                 int8_t*       input1,\n                                                                 __half*       residual,\n                                                                 const __half* bias,\n                                                                 const __half* gamma,\n                                                                 const __half* beta,\n                                                                 int           m,\n                                                                 const int     n,\n                                                                 const float   dqScaleIn,\n                                                                 const float   qScale)\n{\n    const int lane_id   = threadIdx.x % 32;\n    const int warp_id   = threadIdx.x / 32;\n    const int col_start = lane_id * VPT;\n    const int row_id    = blockIdx.x * WPB + warp_id;\n\n    const int idx = (col_start & 0xffffffe0) * m + (row_id << 5) + (col_start & 31);\n\n    if (row_id >= m || col_start >= n)\n        return;\n\n    int8_t in_local[VPT];\n\n    __half in_local_dq[VPT];\n    __half bias_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&residual[idx], in_local_dq);\n    if (IS_INPUT) {\n        copy<sizeof(int8_t) * VPT>(&input1[idx], in_local);\n    }\n    if (IS_BIAS) {\n        copy<sizeof(__half) * VPT>(&bias[col_start], bias_local);\n    }\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        // DQ input\n        if (IS_INPUT) {\n            in_local_dq[it] += dqScaleIn * in_local[it];\n        }\n        if (IS_BIAS) {\n            in_local_dq[it] += bias_local[it];\n        }\n        local_sum += (float)in_local_dq[it];\n    }\n    // load parameters\n    copy<sizeof(__half) * VPT>(&beta[col_start], bias_local);\n    copy<sizeof(__half) * VPT>(&gamma[col_start], gamma_local);\n\n    __shared__ float mu[WPB];      // mean\n    __shared__ float rsigma[WPB];  // 1 / std.dev.\n\n    local_sum = warpReduceSum<float>(local_sum);\n\n    if (lane_id == 0) {\n        mu[warp_id] = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += ((float)in_local_dq[it] - mu[warp_id]) * ((float)in_local_dq[it] - mu[warp_id]);\n    }\n    local_sum = warpReduceSum<float>(local_sum);\n    if (lane_id == 0) {\n        rsigma[warp_id] = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT / 4; it++) {\n        const float tmp0 =\n            (float)gamma_local[it * 4 + 0] * ((float)in_local_dq[it * 4 + 0] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 0];\n        const float tmp1 =\n            (float)gamma_local[it * 4 + 1] * ((float)in_local_dq[it * 4 + 1] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 1];\n        const float tmp2 =\n            (float)gamma_local[it * 4 + 2] * ((float)in_local_dq[it * 4 + 2] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 2];\n        const float tmp3 =\n            (float)gamma_local[it * 4 + 3] * ((float)in_local_dq[it * 4 + 3] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 3];\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &output[idx]);\n    if (WRITE_RESIDUAL)\n        copy<sizeof(__half) * VPT>(in_local_dq, &residual[idx]);\n}\n\ntemplate<int VPT, bool WRITE_RESIDUAL, bool IS_BIAS, bool IS_INPUT>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      int8_t*       input1,\n                                                      __half*       residual,\n                                                      const __half* bias,\n                                                      const __half* gamma,\n                                                      const __half* beta,\n                                                      int           m,\n                                                      const int     n,\n                                                      const float   dqScaleIn,\n                                                      const float   qScale)\n{\n    // compute idx based on COL32\n    // [m,n]-COL32 (blockIdx.x, threadIdx.x * VPT)\n    const int col_start = threadIdx.x * VPT;\n    const int idx       = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n\n    int8_t in_local[VPT];\n\n    __half in_local_dq[VPT];\n    __half bias_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&residual[idx], in_local_dq);\n    if (IS_INPUT) {\n        copy<sizeof(int8_t) * VPT>(&input1[idx], in_local);\n    }\n    if (IS_BIAS) {\n        copy<sizeof(__half) * VPT>(&bias[col_start], bias_local);\n    }\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        // DQ input\n        if (IS_INPUT) {\n            in_local_dq[it] += dqScaleIn * in_local[it];\n        }\n        if (IS_BIAS) {\n            in_local_dq[it] += bias_local[it];\n        }\n        local_sum += (float)in_local_dq[it];\n    }\n    // load parameters\n    copy<sizeof(__half) * VPT>(&beta[col_start], bias_local);\n    copy<sizeof(__half) * VPT>(&gamma[col_start], gamma_local);\n\n    __shared__ float mu;      // mean\n    __shared__ float rsigma;  // 1 / std.dev.\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        mu = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += ((float)in_local_dq[it] - mu) * ((float)in_local_dq[it] - mu);\n    }\n    local_sum = blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        rsigma = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT / 4; it++) {\n        const float tmp0 = (float)gamma_local[it * 4 + 0] * ((float)in_local_dq[it * 4 + 0] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 0];\n        const float tmp1 = (float)gamma_local[it * 4 + 1] * ((float)in_local_dq[it * 4 + 1] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 1];\n        const float tmp2 = (float)gamma_local[it * 4 + 2] * ((float)in_local_dq[it * 4 + 2] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 2];\n        const float tmp3 = (float)gamma_local[it * 4 + 3] * ((float)in_local_dq[it * 4 + 3] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 3];\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &output[idx]);\n    if (WRITE_RESIDUAL)\n        copy<sizeof(__half) * VPT>(in_local_dq, &residual[idx]);\n}\n\nvoid invokeAddBiasResidualPreLayerNormCol32(int8_t*       output,\n                                            int8_t*       input1,\n                                            __half*       input2,\n                                            const __half* bias,\n                                            const __half* gamma,\n                                            const __half* beta,\n                                            int           m,\n                                            const int     n,\n                                            cudaStream_t  stream,\n                                            const float   dqScaleIn,\n                                            const float   qScale)\n{\n    dim3          grid(m);\n    constexpr int VPT = 16 / sizeof(__half);\n    if (n <= VPT * 32 && n % VPT == 0) {\n        constexpr int TPB  = 1024 / VPT;\n        constexpr int WPB  = (TPB + 31) / 32;\n        int           grid = m / WPB;\n        add_bias_input_layernorm_COL32_int8IO_warpReduce<WPB, VPT, true, true, true>\n            <<<grid, TPB, 0, stream>>>(output, input1, input2, bias, gamma, beta, m, n, dqScaleIn, qScale);\n    }\n    else if (n / VPT <= 1024 && n % VPT == 0) {\n        const int blockSize = n / VPT;\n        add_bias_input_layernorm_COL32_int8IO<VPT, true, true, true>\n            <<<grid, blockSize, 0, stream>>>(output, input1, input2, bias, gamma, beta, m, n, dqScaleIn, qScale);\n    }\n    else {\n        printf(\"[FT ERROR]Unsupported dimension n=%d for layernorm, because n / 8 > 1024 || n %% 8 != 0. \\n\", n);\n        exit(0);\n    }\n}\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8IO_noRes(int8_t*      output,\n                                                            int32_t*     input1,\n                                                            T*           input2,\n                                                            const T*     bias,\n                                                            const T*     gamma,\n                                                            const T*     beta,\n                                                            int          m,\n                                                            int          n,\n                                                            const float* weight_amax,\n                                                            const float* input1_amax_ptr,\n                                                            const float* output_scale_ptr)\n{\n    const float input1_amax  = __ldg(input1_amax_ptr);\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 2;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp;\n    if (qual) {\n        input1Tmp         = __ldg(input1TmpPtr + outIdx);\n        int col_start_tmp = col_start;\n        // NOTE: 0.000062f = 1 / 127/.0f / 127.0f\n        local_out[0] = static_cast<float>(input1Tmp.x) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 0]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 0] = local_out[0];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[1]  = static_cast<float>(input1Tmp.y) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 1]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 1] = local_out[1];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[2]  = static_cast<float>(input1Tmp.z) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 2]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 2] = local_out[2];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[3]  = static_cast<float>(input1Tmp.w) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 3]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 3] = local_out[3];\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    char4* outputTmpPtr = (char4*)output;\n    char4  outputTmp;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n        outputTmpPtr[outIdx] = outputTmp;\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8IO_noRes(int8_t*      output,\n                                                            int32_t*     input1,\n                                                            half2*       input2,\n                                                            const half2* bias,\n                                                            const half2* gamma,\n                                                            const half2* beta,\n                                                            int          m,\n                                                            int          n,\n                                                            const float* weight_amax,\n                                                            const float* input1_amax_ptr,\n                                                            const float* output_scale_ptr)\n{\n    const float2* weight_scale_ptr = (const float2*)weight_amax;\n    const float   input1_amax      = __ldg(input1_amax_ptr);\n    const float   output_scale     = __ldg(output_scale_ptr);\n    int           col_start        = threadIdx.x << 1;\n    bool          qual             = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_out[2];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n    int2* input1TmpPtr = (int2*)input1;\n    int2  input1Tmp;\n    if (qual) {\n        const float2 weight_scale = __ldg(weight_scale_ptr + threadIdx.x);\n        input1Tmp                 = input1TmpPtr[outIdx];\n        half2 biasTmp             = bias[threadIdx.x];\n        half2 input2Tmp           = input2[outIdx];\n        // NOTE: 0.000062f = 1 / 127/.0f / 127.0f\n        local_out[0] =\n            static_cast<float>(input1Tmp.x) * input1_amax * weight_scale.x * 0.000062f + static_cast<float>(biasTmp.x);\n        local_out[1] =\n            static_cast<float>(input1Tmp.y) * input1_amax * weight_scale.y * 0.000062f + static_cast<float>(biasTmp.y);\n\n        local_out[0] += static_cast<float>(input2Tmp.x);\n        local_out[1] += static_cast<float>(input2Tmp.y);\n\n        input2Tmp.x    = local_out[0];\n        input2Tmp.y    = local_out[1];\n        input2[outIdx] = input2Tmp;\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    char2* outputTmpPtr = (char2*)output;\n    char2  outputTmp;\n    if (qual) {\n        half2 gammaTmp = gamma[threadIdx.x];\n        half2 betaTmp  = beta[threadIdx.x];\n        local_out[0] =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outputTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        local_out[1] =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n        outputTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        outputTmpPtr[outIdx] = outputTmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                            int32_t*     input1,\n                                            T*           input2,\n                                            const T*     bias,\n                                            const T*     gamma,\n                                            const T*     beta,\n                                            int          m,\n                                            int          n,\n                                            cudaStream_t stream,\n                                            const float* weight_amax,\n                                            const float* input1_amax_ptr,\n                                            const float* output_scale_ptr)\n{\n    dim3 grid(m);\n    int  blockSize = (n / 4 + 31) / 32 * 32;\n    dim3 block(blockSize);\n    assert(blockSize <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        blockSize = (n / 2 + 31) / 32 * 32;\n        assert(blockSize <= 1024);\n        add_bias_input_layernorm_COL32_int8IO_noRes<<<grid, blockSize, 0, stream>>>(output,\n                                                                                    input1,\n                                                                                    (half2*)input2,\n                                                                                    (const half2*)bias,\n                                                                                    (const half2*)gamma,\n                                                                                    (const half2*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    weight_amax,\n                                                                                    input1_amax_ptr,\n                                                                                    output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_COL32_int8IO_noRes<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, weight_amax, input1_amax_ptr, output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                                     int32_t*     input1,\n                                                     float*       input2,\n                                                     const float* bias,\n                                                     const float* gamma,\n                                                     const float* beta,\n                                                     int          m,\n                                                     int          n,\n                                                     cudaStream_t stream,\n                                                     const float* weight_amax,\n                                                     const float* input1_amax_ptr,\n                                                     const float* output_scale_ptr);\n\ntemplate void invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                                     int32_t*     input1,\n                                                     half*        input2,\n                                                     const half*  bias,\n                                                     const half*  gamma,\n                                                     const half*  beta,\n                                                     int          m,\n                                                     int          n,\n                                                     cudaStream_t stream,\n                                                     const float* weight_amax,\n                                                     const float* input1_amax_ptr,\n                                                     const float* output_scale_ptr);\n\n/*******************  invokeLayernormCol32  ***********************/\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void layernorm_COL32_DataTypeI_int8O(\n    int8_t* out, const T* input, const T* gamma, const T* beta, int m, int n, const float* output_scale_ptr)\n{\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 2;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n\n    if (qual) {\n        local_out[0] = static_cast<float>(input[(outIdx << 2) + 0]);\n        local_out[1] = static_cast<float>(input[(outIdx << 2) + 1]);\n        local_out[2] = static_cast<float>(input[(outIdx << 2) + 2]);\n        local_out[3] = static_cast<float>(input[(outIdx << 2) + 3]);\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    char4  outTmp;\n    char4* outPtr = (char4*)out;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<>\n__global__ void layernorm_COL32_DataTypeI_int8O(\n    int8_t* out, const half2* input, const half2* gamma, const half2* beta, int m, int n, const float* output_scale_ptr)\n{\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 1;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_out[2];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    if (qual) {\n        half2 inputTmp = input[outIdx];\n        local_out[0]   = static_cast<float>(inputTmp.x);\n        local_out[1]   = static_cast<float>(inputTmp.y);\n\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    char2  outTmp;\n    char2* outPtr = (char2*)out;\n    if (qual) {\n        half2 gammaTmp = gamma[threadIdx.x];\n        half2 betaTmp  = beta[threadIdx.x];\n        local_out[0] =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        local_out[1] =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n        outTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeLayernormCol32(int8_t*      out,\n                          const T*     input,\n                          const T*     gamma,\n                          const T*     beta,\n                          int          m,\n                          int          n,\n                          const float* output_scale_ptr,\n                          cudaStream_t stream)\n{\n    dim3 grid(m);\n    int  blockSize = (n / 4 + 31) / 32 * 32;\n    dim3 block(blockSize);\n    assert(blockSize <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        blockSize = (n / 2 + 31) / 32 * 32;\n        assert(blockSize <= 1024);\n        layernorm_COL32_DataTypeI_int8O<<<grid, blockSize, 0, stream>>>(\n            out, (const half2*)input, (const half2*)gamma, (const half2*)beta, m, n, output_scale_ptr);\n    }\n    else {\n        layernorm_COL32_DataTypeI_int8O<T><<<grid, block, 0, stream>>>(out, input, gamma, beta, m, n, output_scale_ptr);\n    }\n}\n\ntemplate void invokeLayernormCol32(int8_t*      out,\n                                   const float* input,\n                                   const float* gamma,\n                                   const float* beta,\n                                   int          m,\n                                   int          n,\n                                   const float* output_scale_ptr,\n                                   cudaStream_t stream);\n\ntemplate void invokeLayernormCol32(int8_t*      out,\n                                   const half*  input,\n                                   const half*  gamma,\n                                   const half*  beta,\n                                   int          m,\n                                   int          n,\n                                   const float* output_scale_ptr,\n                                   cudaStream_t stream);\n\n/*******************  invokeLayernormCol32  ***********************/\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void layernorm_COL32_INT8I_DataTypeO(\n    T* out, const int8_t* input, const T* gamma, const T* beta, int m, int n, const float* input_deQ_ptr)\n{\n    const float input_deQ = __ldg(input_deQ_ptr);\n    int         col_start = threadIdx.x << 2;\n    bool        qual      = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n\n    char4* inputPtr = (char4*)input;\n    if (qual) {\n        char4 inputTmp = inputPtr[outIdx];\n        local_out[0]   = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]   = static_cast<float>(inputTmp.y) * input_deQ;\n        local_out[2]   = static_cast<float>(inputTmp.z) * input_deQ;\n        local_out[3]   = static_cast<float>(inputTmp.w) * input_deQ;\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    float4  outTmp;\n    float4* outPtr = (float4*)out;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.x = local_out[0];\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.y = local_out[1];\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.z = local_out[2];\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.w = local_out[3];\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<>\n__global__ void layernorm_COL32_INT8I_DataTypeO(\n    half2* out, const int8_t* input, const half2* gamma, const half2* beta, int m, int n, const float* input_deQ_ptr)\n{\n    const float input_deQ = __ldg(input_deQ_ptr);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n / 2; i += blockDim.x) {\n        int    col_start = i << 1;\n        float  local_out[2];\n        int    outIdx   = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n        char2* inputPtr = (char2*)input;\n        char2  inputTmp = inputPtr[outIdx];\n        local_out[0]    = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]    = static_cast<float>(inputTmp.y) * input_deQ;\n\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    float2 outTmp;\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n / 2; i += blockDim.x) {\n        int    col_start = i << 1;\n        float  local_out[2];\n        int    outIdx   = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n        char2* inputPtr = (char2*)input;\n        char2  inputTmp = inputPtr[outIdx];\n        local_out[0]    = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]    = static_cast<float>(inputTmp.y) * input_deQ;\n\n        half2 gammaTmp = gamma[i];\n        half2 betaTmp  = beta[i];\n        outTmp.x =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outTmp.y =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n\n        out[outIdx] = __float22half2_rn(outTmp);\n    }\n}\n\ntemplate<typename T>\nvoid invokeLayernormCol32(T*            out,\n                          const int8_t* input,\n                          const T*      gamma,\n                          const T*      beta,\n                          int           m,\n                          int           n,\n                          const float*  input_deQ_ptr,\n                          cudaStream_t  stream)\n{\n    dim3 grid(m);\n    if (sizeof(T) == sizeof(half)) {\n        int blockSize = (n / 2 + 31) / 32 * 32;\n        blockSize     = (blockSize <= 1024) ? blockSize : 1024;\n        layernorm_COL32_INT8I_DataTypeO<<<grid, blockSize, 0, stream>>>(\n            (half2*)out, input, (const half2*)gamma, (const half2*)beta, m, n, input_deQ_ptr);\n    }\n    else {\n        int blockSize = (n / 4 + 31) / 32 * 32;\n        blockSize     = (blockSize <= 1024) ? blockSize : 1024;\n        layernorm_COL32_INT8I_DataTypeO<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, gamma, beta, m, n, input_deQ_ptr);\n    }\n}\n\ntemplate void invokeLayernormCol32(float*        out,\n                                   const int8_t* input,\n                                   const float*  gamma,\n                                   const float*  beta,\n                                   int           m,\n                                   int           n,\n                                   const float*  input_deQ_ptr,\n                                   cudaStream_t  stream);\n\ntemplate void invokeLayernormCol32(half*         out,\n                                   const int8_t* input,\n                                   const half*   gamma,\n                                   const half*   beta,\n                                   int           m,\n                                   int           n,\n                                   const float*  input_deQ_ptr,\n                                   cudaStream_t  stream);\n\n/*******************  invokeLayernormShiftPartitionCol32  ***********************/\n\n// each warp process 1 row, and increase the num of warps per block to increase occupancy\ntemplate<int WPB, int VPT>\n__global__ void layernorm_shift_partition_COL32_warpReduce(int8_t*       out_ptr,\n                                                           const __half* input_ptr,\n                                                           const __half* gamma_ptr,\n                                                           const __half* beta_ptr,\n                                                           int           batch,\n                                                           int           H,\n                                                           int           W,\n                                                           int           n,\n                                                           const float   qScale,\n                                                           int           shift_size,\n                                                           int           window_size)\n{\n    const int lane_id   = threadIdx.x % 32;\n    const int warp_id   = threadIdx.x / 32;\n    const int col_start = lane_id * VPT;\n    const int row_id    = blockIdx.x * WPB + warp_id;\n    if (row_id >= batch * H * W || col_start >= n)\n        return;\n\n    const int blockIdx_z = row_id / (H * W);\n    const int blockIdx_y = (row_id / W) % H;\n    const int blockIdx_x = row_id % W;\n\n    const int batch_offset = blockIdx_z * H * W;\n    const int m            = batch * H * W;\n\n    const int shifted_H_idx = (shift_size != 0) ? ((blockIdx_y - shift_size + H) % H) : blockIdx_y;\n    const int shifted_W_idx = (shift_size != 0) ? ((blockIdx_x - shift_size + W) % W) : blockIdx_x;\n    const int window_H_idx  = shifted_H_idx / window_size;\n    const int window_W_idx  = shifted_W_idx / window_size;\n    const int window_idx    = window_H_idx * (W / window_size) + window_W_idx;\n    const int idx_in_window = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid    = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    const int idx    = (col_start & 0xffffffe0) * m + (row_id << 5) + (col_start & 31);\n    const int outIdx = (col_start & 0xffffffe0) * m + (output_bid << 5) + (col_start & 31);\n\n    float  in_local_dq[VPT];\n    __half in_local[VPT];\n    __half beta_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&input_ptr[idx], in_local);\n    copy<sizeof(__half) * VPT>(&gamma_ptr[col_start], gamma_local);\n    copy<sizeof(__half) * VPT>(&beta_ptr[col_start], beta_local);\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        in_local_dq[it] = in_local[it];\n        local_sum += in_local_dq[it];\n    }\n\n    __shared__ float mu[WPB];\n    __shared__ float rsigma[WPB];\n\n    local_sum = warpReduceSum<float>(local_sum);\n\n    if (lane_id == 0) {\n        mu[warp_id] = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += (in_local_dq[it] - mu[warp_id]) * (in_local_dq[it] - mu[warp_id]);\n    }\n    local_sum = warpReduceSum<float>(local_sum);\n    if (lane_id == 0) {\n        rsigma[warp_id] = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        const float tmp0 =\n            (in_local_dq[it * 4 + 0] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 0])\n            + static_cast<float>(beta_local[it * 4 + 0]);\n        const float tmp1 =\n            (in_local_dq[it * 4 + 1] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 1])\n            + static_cast<float>(beta_local[it * 4 + 1]);\n        const float tmp2 =\n            (in_local_dq[it * 4 + 2] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 2])\n            + static_cast<float>(beta_local[it * 4 + 2]);\n        const float tmp3 =\n            (in_local_dq[it * 4 + 3] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 3])\n            + static_cast<float>(beta_local[it * 4 + 3]);\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &out_ptr[outIdx]);\n}\n\ntemplate<int VPT>\n__global__ void layernorm_shift_partition_COL32(int8_t*       out_ptr,\n                                                const __half* input_ptr,\n                                                const __half* gamma_ptr,\n                                                const __half* beta_ptr,\n                                                int           batch,\n                                                int           H,\n                                                int           W,\n                                                int           n,\n                                                const float   qScale,\n                                                int           shift_size,\n                                                int           window_size)\n{\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    int tid = threadIdx.x * VPT;\n    if (tid >= n)\n        return;\n    __shared__ float mu;\n    __shared__ float rsigma;\n    __half           inputTmp[VPT];\n    __half           beta_local[VPT];\n    __half           gamma_local[VPT];\n    float            in_local_dq[VPT];\n\n    const int offset_col32 = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n    copy<sizeof(__half) * VPT>(&input_ptr[offset_col32], inputTmp);\n    copy<sizeof(__half) * VPT>(&gamma_ptr[tid], gamma_local);\n    copy<sizeof(__half) * VPT>(&beta_ptr[tid], beta_local);\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        in_local_dq[it] = inputTmp[it];\n        local_sum += in_local_dq[it];\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        mu = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += (in_local_dq[it] - mu) * (in_local_dq[it] - mu);\n    }\n    local_sum = blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        rsigma = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n    if (tid < n) {\n#pragma unroll\n        for (int it = 0; it < VPT / 4; it++) {\n            const float tmp0 = (in_local_dq[it * 4 + 0] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 0])\n                               + static_cast<float>(beta_local[it * 4 + 0]);\n            const float tmp1 = (in_local_dq[it * 4 + 1] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 1])\n                               + static_cast<float>(beta_local[it * 4 + 1]);\n            const float tmp2 = (in_local_dq[it * 4 + 2] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 2])\n                               + static_cast<float>(beta_local[it * 4 + 2]);\n            const float tmp3 = (in_local_dq[it * 4 + 3] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 3])\n                               + static_cast<float>(beta_local[it * 4 + 3]);\n            out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n        }\n        const int outIdx = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        copy<sizeof(int8_t) * VPT>(out_local, &out_ptr[outIdx]);\n    }\n}\n\nvoid invokeLayernormShiftPartitionCol32(int8_t*      out,\n                                        const half*  input,\n                                        const half*  gamma,\n                                        const half*  beta,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float  norm_scale,\n                                        int          shift_size,\n                                        int          window_size,\n                                        cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n\n    constexpr int VPT       = 16 / sizeof(half);\n    int           blockSize = (n / VPT + 31) / 32 * 32;\n    if (blockSize == 32 && n % VPT == 0) {\n        constexpr int TPB  = 1024 / VPT;\n        constexpr int WPB  = (TPB + 31) / 32;\n        int           grid = (batch * H * W + WPB - 1) / WPB;\n        layernorm_shift_partition_COL32_warpReduce<WPB, VPT><<<grid, TPB, 0, stream>>>(out,\n                                                                                       (const __half*)input,\n                                                                                       (const __half*)gamma,\n                                                                                       (const __half*)beta,\n                                                                                       batch,\n                                                                                       H,\n                                                                                       W,\n                                                                                       n,\n                                                                                       norm_scale,\n                                                                                       shift_size,\n                                                                                       window_size);\n    }\n    else if (n / VPT <= 1024 && n % VPT == 0) {\n        layernorm_shift_partition_COL32<VPT><<<grid, blockSize, 0, stream>>>(out,\n                                                                             (const __half*)input,\n                                                                             (const __half*)gamma,\n                                                                             (const __half*)beta,\n                                                                             batch,\n                                                                             H,\n                                                                             W,\n                                                                             n,\n                                                                             norm_scale,\n                                                                             shift_size,\n                                                                             window_size);\n    }\n    else {\n        printf(\"[FT ERROR]Unsupported dimension n=%d for layernorm, because n / 8 > 1024 || n %% 8 != 0. \\n\", n);\n        exit(0);\n    }\n}\n\n/*******************  invokeMergeLayerNormCol32  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (n)\ntemplate<typename T>\n__global__ void merge_layernorm_v2(int8_t* out,\n                                   const T* __restrict input,\n                                   const T* __restrict gamma,\n                                   const T* __restrict beta,\n                                   int          batch,\n                                   const float* merge_inFactor,\n                                   int          H,\n                                   int          W,\n                                   int          n)\n{\n    const int   ite       = 4;\n    const int   tid       = threadIdx.x;\n    const int   W_idx     = blockIdx.x;\n    const int   H_idx     = blockIdx.y;\n    const float out_scale = __ldg(merge_inFactor);\n    // const size_t batch_offset = blockIdx.z * H * W * n;\n    // const int input_H_stride = W*n/2;\n    // const int output_H_stride = W*n;\n    const int n_4 = n >> 2;\n    const int m   = batch * 4 * H * W;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            int part_id     = col_id / n_4;\n            int offset_in_W = part_id / 2;\n            int offset_in_H = part_id % 2;\n            // size_t input_id = batch_offset + (2*H_idx + offset_in_H)*input_H_stride + (2*W_idx + offset_in_W)*n_4 +\n            // (col_id % n_4);\n\n            int col_input = col_id % n_4;\n            int row_input = blockIdx.z * H * W * 4 + (2 * H_idx + offset_in_H) * W * 2 + (2 * W_idx + offset_in_W);\n            int input_idx_col32 = ((col_input >> 5) << 5) * m + (row_input << 5) + (col_input & 31);\n            local_out[i]        = (float)(__ldg(input + input_idx_col32));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = local_out[i] - s_mean;\n            var += local_out[i] * local_out[i];\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + 1e-6f);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            // size_t output_idx = batch_offset + (H_idx*W + W_idx)*n + col_id;\n\n            int col_output        = col_id;\n            int row_output        = blockIdx.z * H * W + H_idx * W + W_idx;\n            int output_idx_col32  = ((col_output >> 5) << 5) * (m >> 2) + (row_output << 5) + (col_output & 31);\n            out[output_idx_col32] = float_to_int8_rn(\n                out_scale * (local_out[i] * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id])));\n        }\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeMergeLayerNormCol32(int8_t*      output,\n                               const T*     input,\n                               const T*     gamma,\n                               const T*     beta,\n                               int          batch,\n                               const float* merge_inFactor,\n                               int          H,\n                               int          W,\n                               int          n,\n                               cudaStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeMergeLayerNormCol32] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = (blockSize + 31) / 32 * 32;\n    // TODO\n    // if (blockSize >= 768)\n    {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        merge_layernorm_v2<T>\n            <<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, merge_inFactor, H / 2, W / 2, n * 4);\n    }\n    /*\n    else\n      merge_layernorm<T><<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, H/2, W/2, n*4);\n    */\n}\n\ntemplate void invokeMergeLayerNormCol32<float>(int8_t*      output,\n                                               const float* input,\n                                               const float* gamma,\n                                               const float* beta,\n                                               int          batch,\n                                               const float* merge_inFactor,\n                                               int          H,\n                                               int          W,\n                                               int          n,\n                                               cudaStream_t stream);\n\ntemplate void invokeMergeLayerNormCol32<half>(int8_t*      output,\n                                              const half*  input,\n                                              const half*  gamma,\n                                              const half*  beta,\n                                              int          batch,\n                                              const float* merge_inFactor,\n                                              int          H,\n                                              int          W,\n                                              int          n,\n                                              cudaStream_t stream);\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_ROW_int8IO(int8_t*       output,\n                                                    const int8_t* input1,\n                                                    const int8_t* input2,\n                                                    const T*      bias,\n                                                    const T*      gamma,\n                                                    const T*      beta,\n                                                    int           m,\n                                                    int           n,\n                                                    const float*  input1_deQFactor_ptr,\n                                                    const float*  input2_deQFactor_ptr,\n                                                    const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = (blockIdx.x * n + col_start) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_ROW_int8IO(int8_t*       output,\n                                                    const int8_t* input1,\n                                                    const int8_t* input2,\n                                                    const half2*  bias,\n                                                    const half2*  gamma,\n                                                    const half2*  beta,\n                                                    int           m,\n                                                    int           n,\n                                                    const float*  input1_deQFactor_ptr,\n                                                    const float*  input2_deQFactor_ptr,\n                                                    const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = (blockIdx.x * n + col_start) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int   col_start_tmp = col_start;\n    half2 biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[0]        = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    col_start_tmp = col_start_tmp + 1;\n    biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    half2 betaTmp = __ldg(beta + col_start_tmp);\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.x  = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.y  = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start     = col_start + 1;\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    betaTmp       = __ldg(beta + col_start_tmp);\n    local_out[2]  = (local_out[2] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.z   = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.w  = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                       const int8_t* input1,\n                                       const int8_t* input2,\n                                       const T*      bias,\n                                       const T*      gamma,\n                                       const T*      beta,\n                                       int           m,\n                                       int           n,\n                                       cudaStream_t  stream,\n                                       const float*  input1_deQFactor_ptr,\n                                       const float*  input2_deQFactor_ptr,\n                                       const float*  output_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(n <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_input_layernorm_ROW_int8IO<<<grid, block, 0, stream>>>(output,\n                                                                        input1,\n                                                                        input2,\n                                                                        (const half2*)bias,\n                                                                        (const half2*)gamma,\n                                                                        (const half2*)beta,\n                                                                        m,\n                                                                        n,\n                                                                        input1_deQFactor_ptr,\n                                                                        input2_deQFactor_ptr,\n                                                                        output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_ROW_int8IO<T><<<grid, block, 0, stream>>>(output,\n                                                                           input1,\n                                                                           input2,\n                                                                           bias,\n                                                                           gamma,\n                                                                           beta,\n                                                                           m,\n                                                                           n,\n                                                                           input1_deQFactor_ptr,\n                                                                           input2_deQFactor_ptr,\n                                                                           output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                                const int8_t* input1,\n                                                const int8_t* input2,\n                                                const float*  bias,\n                                                const float*  gamma,\n                                                const float*  beta,\n                                                int           m,\n                                                int           n,\n                                                cudaStream_t  stream,\n                                                const float*  input1_deQFactor_ptr,\n                                                const float*  input2_deQFactor_ptr,\n                                                const float*  output_scale_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                                const int8_t* input1,\n                                                const int8_t* input2,\n                                                const half*   bias,\n                                                const half*   gamma,\n                                                const half*   beta,\n                                                int           m,\n                                                int           n,\n                                                cudaStream_t  stream,\n                                                const float*  input1_deQFactor_ptr,\n                                                const float*  input2_deQFactor_ptr,\n                                                const float*  output_scale_ptr);\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n)\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_ROW_int8I_DataTypeO(T*            output,\n                                                             const int8_t* input1,\n                                                             const int8_t* input2,\n                                                             const T*      bias,\n                                                             const T*      gamma,\n                                                             const T*      beta,\n                                                             int           m,\n                                                             int           n,\n                                                             const float*  input1_deQFactor_ptr,\n                                                             const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   idx = blockIdx.x * n + col_start;\n\n    local_out = static_cast<float>(__ldg(input2 + idx)) * input2_deQFactor\n                + static_cast<float>(__ldg(input1 + idx)) * input1_deQFactor\n                + static_cast<float>(__ldg(bias + col_start));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                + static_cast<float>(__ldg(beta + col_start));\n\n    output[idx] = local_out;\n}\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n/2)\ntemplate<>\n__global__ void add_bias_input_layernorm_ROW_int8I_DataTypeO(half2*        output,\n                                                             const int8_t* input1,\n                                                             const int8_t* input2,\n                                                             const half2*  bias,\n                                                             const half2*  gamma,\n                                                             const half2*  beta,\n                                                             int           m,\n                                                             int           n,\n                                                             const float*  input1_deQFactor_ptr,\n                                                             const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 1;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    idx = (blockIdx.x * n + col_start) >> 1;\n\n    const char2* input1_ptr2 = (const char2*)input1;\n    const char2* input2_ptr2 = (const char2*)input2;\n    char2        input_tmp1  = __ldg(input1_ptr2 + idx);\n    char2        input_tmp2  = __ldg(input2_ptr2 + idx);\n\n    half2 bias_tmp = __ldg(bias + threadIdx.x);\n\n    local_out.x = static_cast<float>(input_tmp1.x) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.x) * input2_deQFactor + static_cast<float>(bias_tmp.x);\n\n    local_out.y = static_cast<float>(input_tmp1.y) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.y) * input2_deQFactor + static_cast<float>(bias_tmp.y);\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    half2 gamma_tmp = __ldg(gamma + threadIdx.x);\n    half2 beta_tmp  = __ldg(beta + threadIdx.x);\n\n    local_out.x = (local_out.x * s_variance) * static_cast<float>(gamma_tmp.x) + static_cast<float>(beta_tmp.x);\n    local_out.y = (local_out.y * s_variance) * static_cast<float>(gamma_tmp.y) + static_cast<float>(beta_tmp.y);\n\n    bias_tmp.x = half(local_out.x);\n    bias_tmp.y = half(local_out.y);\n\n    output[idx] = bias_tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormRow(T*            output,\n                                       const int8_t* input1,\n                                       const int8_t* input2,\n                                       const T*      bias,\n                                       const T*      gamma,\n                                       const T*      beta,\n                                       int           m,\n                                       int           n,\n                                       cudaStream_t  stream,\n                                       const float*  input1_deQFactor_ptr,\n                                       const float*  input2_deQFactor_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        assert(n / 2 <= 1024 && n % 2 == 0);\n        block.x = n / 2;\n        add_bias_input_layernorm_ROW_int8I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                 input1,\n                                                                                 input2,\n                                                                                 (const half2*)bias,\n                                                                                 (const half2*)gamma,\n                                                                                 (const half2*)beta,\n                                                                                 m,\n                                                                                 n,\n                                                                                 input1_deQFactor_ptr,\n                                                                                 input2_deQFactor_ptr);\n    }\n    else {\n        assert(n <= 1024);\n        add_bias_input_layernorm_ROW_int8I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, input1_deQFactor_ptr, input2_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormRow<float>(float*        output,\n                                                       const int8_t* input1,\n                                                       const int8_t* input2,\n                                                       const float*  bias,\n                                                       const float*  gamma,\n                                                       const float*  beta,\n                                                       int           m,\n                                                       int           n,\n                                                       cudaStream_t  stream,\n                                                       const float*  input1_deQFactor_ptr,\n                                                       const float*  input2_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormRow<half>(half*         output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const half*   bias,\n                                                      const half*   gamma,\n                                                      const half*   beta,\n                                                      int           m,\n                                                      int           n,\n                                                      cudaStream_t  stream,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr);\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/layernorm_int8_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n// input1/input2/output matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n)\n// for per_channel_quantization for weight\n__global__ void add_bias_input_layernorm_COL32_int32I_DataTypeO(float*         output,\n                                                                const int32_t* input1,\n                                                                const float*   input2,\n                                                                const float*   bias,\n                                                                const float*   gamma,\n                                                                const float*   beta,\n                                                                int            m,\n                                                                int            n,\n                                                                const float*   weight_amax,\n                                                                const float*   input1_amax_ptr)\n{\n    const float input1_amax = __ldg(input1_amax_ptr);\n    int         col_start   = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31));\n\n    float tmp = static_cast<float>(__ldg(input1 + outIdx)) * __ldg(weight_amax + col_start) * input1_amax\n                * 0.000062f;  //(1/127/127);\n    float inputTmp = __ldg(input2 + outIdx);\n\n    local_out = tmp + inputTmp + __ldg(bias + col_start);\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = __fdividef(mean, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n    if (threadIdx.x == 0) {\n        s_variance = __fdividef(variance, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * __ldg(gamma + col_start) + __ldg(beta + col_start);\n\n    output[outIdx] = local_out;\n}\n\n__global__ void add_bias_input_layernorm_COL32_int32I_DataTypeO(half2*        output,\n                                                                const int2*   input1,\n                                                                const half2*  input2,\n                                                                const half2*  bias,\n                                                                const half2*  gamma,\n                                                                const half2*  beta,\n                                                                int           m,\n                                                                int           n,\n                                                                const float2* weight_amax,\n                                                                const float*  input1_amax_ptr)\n{\n    int col_start = threadIdx.x << 1;\n\n    const float input1_amax = __ldg(input1_amax_ptr);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    const int2   input1Tmp = __ldg(input1 + outIdx);\n    const float2 weightTmp = __ldg(weight_amax + threadIdx.x);\n\n    float2 addTmp2;\n    addTmp2.x = static_cast<float>(input1Tmp.x) * weightTmp.x * input1_amax * 0.000062f;  //(1/127/127);\n    addTmp2.y = static_cast<float>(input1Tmp.y) * weightTmp.y * input1_amax * 0.000062f;  //(1/127/127);\n\n    const half2 inputTmp = __ldg(input2 + outIdx);\n    const half2 biasTmp  = __ldg(bias + threadIdx.x);\n\n    local_out   = __half22float2(__hadd2(inputTmp, biasTmp));\n    local_out.x = local_out.x + addTmp2.x;\n    local_out.y = local_out.y + addTmp2.y;\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = __fdividef(mean, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n    if (threadIdx.x == 0) {\n        s_variance = __fdividef(variance, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    float2      outputTmp;\n    const half2 gammaTmp = __ldg(gamma + threadIdx.x);\n    const half2 betaTmp  = __ldg(beta + threadIdx.x);\n\n    outputTmp.x = (local_out.x * s_variance) * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n    outputTmp.y = (local_out.y * s_variance) * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n\n    output[outIdx] = __float22half2_rn(outputTmp);\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(T*             output,\n                                         const int32_t* input1,\n                                         const T*       input2,\n                                         const T*       bias,\n                                         const T*       gamma,\n                                         const T*       beta,\n                                         int            m,\n                                         int            n,\n                                         hipStream_t   stream,\n                                         const float*   weight_amax,\n                                         const float*   input1_amax_ptr)\n{\n\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        block.x /= 2;\n        assert(block.x <= 1024);\n        add_bias_input_layernorm_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                    (const int2*)input1,\n                                                                                    (const half2*)input2,\n                                                                                    (const half2*)bias,\n                                                                                    (const half2*)gamma,\n                                                                                    (const half2*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    (const float2*)weight_amax,\n                                                                                    input1_amax_ptr);\n    }\n    else {\n        assert(block.x <= 1024);\n        add_bias_input_layernorm_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((float*)output,\n                                                                                    input1,\n                                                                                    (const float*)input2,\n                                                                                    (const float*)bias,\n                                                                                    (const float*)gamma,\n                                                                                    (const float*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    weight_amax,\n                                                                                    input1_amax_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32(float*         output,\n                                                  const int32_t* input1,\n                                                  const float*   input2,\n                                                  const float*   bias,\n                                                  const float*   gamma,\n                                                  const float*   beta,\n                                                  int            m,\n                                                  int            n,\n                                                  hipStream_t   stream,\n                                                  const float*   weight_amax,\n                                                  const float*   input1_amax_ptr);\ntemplate void invokeAddBiasResidualLayerNormCol32(half*          output,\n                                                  const int32_t* input1,\n                                                  const half*    input2,\n                                                  const half*    bias,\n                                                  const half*    gamma,\n                                                  const half*    beta,\n                                                  int            m,\n                                                  int            n,\n                                                  hipStream_t   stream,\n                                                  const float*   weight_amax,\n                                                  const float*   input1_amax_ptr);\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const T*      bias,\n                                                      const T*      gamma,\n                                                      const T*      beta,\n                                                      int           m,\n                                                      int           n,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr,\n                                                      const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const half2*  bias,\n                                                      const half2*  gamma,\n                                                      const half2*  beta,\n                                                      int           m,\n                                                      int           n,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr,\n                                                      const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int   col_start_tmp = col_start;\n    half2 biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[0]        = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    col_start_tmp = col_start_tmp + 1;\n    biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    half2 betaTmp = __ldg(beta + col_start_tmp);\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.x  = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.y  = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start     = col_start + 1;\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    betaTmp       = __ldg(beta + col_start_tmp);\n    local_out[2]  = (local_out[2] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.z   = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.w  = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                         const int8_t* input1,\n                                         const int8_t* input2,\n                                         const T*      bias,\n                                         const T*      gamma,\n                                         const T*      beta,\n                                         int           m,\n                                         int           n,\n                                         hipStream_t  stream,\n                                         const float*  input1_deQFactor_ptr,\n                                         const float*  input2_deQFactor_ptr,\n                                         const float*  output_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(n <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_input_layernorm_COL32_int8IO<<<grid, block, 0, stream>>>(output,\n                                                                          input1,\n                                                                          input2,\n                                                                          (const half2*)bias,\n                                                                          (const half2*)gamma,\n                                                                          (const half2*)beta,\n                                                                          m,\n                                                                          n,\n                                                                          input1_deQFactor_ptr,\n                                                                          input2_deQFactor_ptr,\n                                                                          output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_COL32_int8IO<T><<<grid, block, 0, stream>>>(output,\n                                                                             input1,\n                                                                             input2,\n                                                                             bias,\n                                                                             gamma,\n                                                                             beta,\n                                                                             m,\n                                                                             n,\n                                                                             input1_deQFactor_ptr,\n                                                                             input2_deQFactor_ptr,\n                                                                             output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                                  const int8_t* input1,\n                                                  const int8_t* input2,\n                                                  const float*  bias,\n                                                  const float*  gamma,\n                                                  const float*  beta,\n                                                  int           m,\n                                                  int           n,\n                                                  hipStream_t  stream,\n                                                  const float*  input1_deQFactor_ptr,\n                                                  const float*  input2_deQFactor_ptr,\n                                                  const float*  output_scale_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormCol32(int8_t*       output,\n                                                  const int8_t* input1,\n                                                  const int8_t* input2,\n                                                  const half*   bias,\n                                                  const half*   gamma,\n                                                  const half*   beta,\n                                                  int           m,\n                                                  int           n,\n                                                  hipStream_t  stream,\n                                                  const float*  input1_deQFactor_ptr,\n                                                  const float*  input2_deQFactor_ptr,\n                                                  const float*  output_scale_ptr);\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n)\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8I_DataTypeO(T*            output,\n                                                               const int8_t* input1,\n                                                               const int8_t* input2,\n                                                               const T*      bias,\n                                                               const T*      gamma,\n                                                               const T*      beta,\n                                                               int           m,\n                                                               int           n,\n                                                               const float*  input1_deQFactor_ptr,\n                                                               const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   idx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31));\n\n    local_out = static_cast<float>(__ldg(input2 + idx)) * input2_deQFactor\n                + static_cast<float>(__ldg(input1 + idx)) * input1_deQFactor\n                + static_cast<float>(__ldg(bias + col_start));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                + static_cast<float>(__ldg(beta + col_start));\n\n    output[idx] = local_out;\n}\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/2)\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8I_DataTypeO(half2*        output,\n                                                               const int8_t* input1,\n                                                               const int8_t* input2,\n                                                               const half2*  bias,\n                                                               const half2*  gamma,\n                                                               const half2*  beta,\n                                                               int           m,\n                                                               int           n,\n                                                               const float*  input1_deQFactor_ptr,\n                                                               const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 1;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    idx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    const char2* input1_ptr2 = (const char2*)input1;\n    const char2* input2_ptr2 = (const char2*)input2;\n    char2        input_tmp1  = __ldg(input1_ptr2 + idx);\n    char2        input_tmp2  = __ldg(input2_ptr2 + idx);\n\n    half2 bias_tmp = __ldg(bias + threadIdx.x);\n\n    local_out.x = static_cast<float>(input_tmp1.x) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.x) * input2_deQFactor + static_cast<float>(bias_tmp.x);\n\n    local_out.y = static_cast<float>(input_tmp1.y) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.y) * input2_deQFactor + static_cast<float>(bias_tmp.y);\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    half2 gamma_tmp = __ldg(gamma + threadIdx.x);\n    half2 beta_tmp  = __ldg(beta + threadIdx.x);\n\n    local_out.x = (local_out.x * s_variance) * static_cast<float>(gamma_tmp.x) + static_cast<float>(beta_tmp.x);\n    local_out.y = (local_out.y * s_variance) * static_cast<float>(gamma_tmp.y) + static_cast<float>(beta_tmp.y);\n\n    bias_tmp.x = half(local_out.x);\n    bias_tmp.y = half(local_out.y);\n\n    output[idx] = bias_tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormCol32(T*            output,\n                                         const int8_t* input1,\n                                         const int8_t* input2,\n                                         const T*      bias,\n                                         const T*      gamma,\n                                         const T*      beta,\n                                         int           m,\n                                         int           n,\n                                         hipStream_t  stream,\n                                         const float*  input1_deQFactor_ptr,\n                                         const float*  input2_deQFactor_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        assert(n / 2 <= 1024 && n % 2 == 0);\n        block.x = n / 2;\n        add_bias_input_layernorm_COL32_int8I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                   input1,\n                                                                                   input2,\n                                                                                   (const half2*)bias,\n                                                                                   (const half2*)gamma,\n                                                                                   (const half2*)beta,\n                                                                                   m,\n                                                                                   n,\n                                                                                   input1_deQFactor_ptr,\n                                                                                   input2_deQFactor_ptr);\n    }\n    else {\n        assert(n <= 1024);\n        add_bias_input_layernorm_COL32_int8I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, input1_deQFactor_ptr, input2_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormCol32<float>(float*        output,\n                                                         const int8_t* input1,\n                                                         const int8_t* input2,\n                                                         const float*  bias,\n                                                         const float*  gamma,\n                                                         const float*  beta,\n                                                         int           m,\n                                                         int           n,\n                                                         hipStream_t  stream,\n                                                         const float*  input1_deQFactor_ptr,\n                                                         const float*  input2_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormCol32<half>(half*         output,\n                                                        const int8_t* input1,\n                                                        const int8_t* input2,\n                                                        const half*   bias,\n                                                        const half*   gamma,\n                                                        const half*   beta,\n                                                        int           m,\n                                                        int           n,\n                                                        hipStream_t  stream,\n                                                        const float*  input1_deQFactor_ptr,\n                                                        const float*  input2_deQFactor_ptr);\n\n/*******************  invokeAddBiasLayernormAddRes  ***********************/\ntemplate<typename T, int T_per_thread>\n__global__ void add_bias_layernorm_add_res(int8_t*       out_int8,\n                                           const int8_t* out,\n                                           T*            residual_input,\n                                           const T*      bias,\n                                           const T*      gamma,\n                                           const T*      beta,\n                                           const float   layernorm_eps,\n                                           int           m,\n                                           int           n,\n                                           const float*  input_deQFactor_ptr,\n                                           const float*  out_QFactor_ptr)\n{\n    const float      input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float      out_QFactor     = out_QFactor_ptr == nullptr ? -1.0f : __ldg(out_QFactor_ptr);\n    int              tid             = threadIdx.x;\n    const int        bdim            = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n\n    float local_val[T_per_thread];\n\n    // float sums[2] = {0.0f, 0.0f};\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n) {\n            T bias_val = bias[col_start];\n\n            int inIdx    = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n            T   out_val  = static_cast<T>(out[inIdx]) * static_cast<T>(input_deQFactor);\n            T   tmp      = out_val + bias_val;\n            local_val[i] = tmp;\n            local_sum += local_val[i];\n        }\n    }\n\n    blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        local_sum += (local_val[i] - s_mean) * (local_val[i] - s_mean);\n    }\n\n    blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(local_sum * __fdividef(1.0f, n) + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n) {\n            T gamma_val = gamma[col_start];\n            T beta_val  = beta[col_start];\n\n            int   outIdx  = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n            T     res_val = residual_input[outIdx];\n            float output_val =\n                (local_val[i] - s_mean) * s_variance * (float)(gamma_val) + (float)(beta_val) + (float)(res_val);\n\n            if (out_QFactor_ptr != nullptr) {\n                residual_input[outIdx] = (T)output_val;\n                out_int8[outIdx]       = float_to_int8_rn(output_val * out_QFactor);\n            }\n            else {\n                T* out_T_ptr      = reinterpret_cast<T*>(out_int8);\n                out_T_ptr[outIdx] = (T)output_val;\n            }\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(char2*       out_int8,\n                                              const char2* out,\n                                              half2*       residual_input,\n                                              const half2* bias,\n                                              const half2* gamma,\n                                              const half2* beta,\n                                              const float  layernorm_eps,\n                                              int          m,\n                                              int          n,\n                                              const float* input_deQFactor_ptr,\n                                              const float* out_QFactor_ptr)\n{\n    const float      input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float      out_QFactor     = out_QFactor_ptr == nullptr ? -1.0f : __ldg(out_QFactor_ptr);\n    int              tid             = threadIdx.x;\n    const int        bdim            = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    const int        n_2 = n >> 1;\n\n    half2 local_val[T_per_thread];\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        // int index = i * bdim + tid;\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            half2 bias_val = bias[col_start];\n\n            int   col_id = col_start << 1;\n            int   inIdx  = ((col_id & 0xffffffe0) * m + (blockIdx.x << 5) + (col_id & 31)) >> 1;\n            char2 outTmp = out[inIdx];\n            half2 out_val;\n            out_val.x = static_cast<half>(outTmp.x) * static_cast<half>(input_deQFactor);\n            out_val.y = static_cast<half>(outTmp.y) * static_cast<half>(input_deQFactor);\n\n            half2 tmp         = __hadd2(out_val, bias_val);\n            local_val[i]      = tmp;\n            float2 tmp_float2 = __half22float2(tmp);\n            local_sum += tmp_float2.x + tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            local_sum +=\n                (tmp_float2.x - s_mean) * (tmp_float2.x - s_mean) + (tmp_float2.y - s_mean) * (tmp_float2.y - s_mean);\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(local_sum * __fdividef(1.0f, n) + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int col_start = i * bdim + tid;\n        if (col_start < n_2) {\n            float2 gamma_val = __half22float2(gamma[col_start]);\n            float2 beta_val  = __half22float2(beta[col_start]);\n\n            int    col_id  = col_start << 1;\n            int    outIdx  = ((col_id & 0xffffffe0) * m + (blockIdx.x << 5) + (col_id & 31)) >> 1;\n            float2 res_val = __half22float2(residual_input[outIdx]);\n            float2 output_val;\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            output_val.x      = (tmp_float2.x - s_mean) * s_variance * gamma_val.x + beta_val.x + res_val.x;\n            output_val.y      = (tmp_float2.y - s_mean) * s_variance * gamma_val.y + beta_val.y + res_val.y;\n            if (out_QFactor_ptr != nullptr) {\n                residual_input[outIdx] = __float22half2_rn(output_val);\n                char2 outTmp;\n                outTmp.x         = float_to_int8_rn(output_val.x * out_QFactor);\n                outTmp.y         = float_to_int8_rn(output_val.y * out_QFactor);\n                out_int8[outIdx] = outTmp;\n            }\n            else {\n                half2* out_ptr  = reinterpret_cast<half2*>(out_int8);\n                out_ptr[outIdx] = __float22half2_rn(output_val);\n            }\n        }\n    }\n}\n\n#define MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec)                                                                          \\\n    blockSize = (blockSize + TVec_per_thread - 1) / TVec_per_thread;                                                   \\\n    if (T_per_TVec == 2) {                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((char2*)out_int8,              \\\n                                                                                        (const char2*)out,             \\\n                                                                                        (half2*)residual_input,        \\\n                                                                                        (const half2*)bias,            \\\n                                                                                        (const half2*)gamma,           \\\n                                                                                        (const half2*)beta,            \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        m,                             \\\n                                                                                        n,                             \\\n                                                                                        input_deQFactor_ptr,           \\\n                                                                                        out_QFactor_ptr);              \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");                         \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res<half, TVec_per_thread><<<m, blockSize, 0, stream>>>((int8_t*)out_int8,          \\\n                                                                                           (const int8_t*)out,         \\\n                                                                                           (half*)residual_input,      \\\n                                                                                           (const half*)bias,          \\\n                                                                                           (const half*)gamma,         \\\n                                                                                           (const half*)beta,          \\\n                                                                                           layernorm_eps,              \\\n                                                                                           m,                          \\\n                                                                                           n,                          \\\n                                                                                           input_deQFactor_ptr,        \\\n                                                                                           out_QFactor_ptr);           \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");                         \\\n        }                                                                                                              \\\n    }\n\ntemplate<typename T>\nvoid invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                       int8_t*      out,\n                                       T*           residual_input,\n                                       const T*     bias,\n                                       const T*     gamma,\n                                       const T*     beta,\n                                       const float  layernorm_eps,\n                                       int          m,\n                                       int          n,\n                                       hipStream_t stream,\n                                       const float* input_deQFactor_ptr,\n                                       const float* out_QFactor_ptr)\n{\n    if (n % 2 == 0) {\n        const int T_per_TVec = 2;\n        int       blockSize  = (n / 2 + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");\n        }\n    }\n    else {\n        const int T_per_TVec = 1;\n        int       blockSize  = (n + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 32768) {\n            const int TVec_per_thread = 32;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[invokeAddBiasLayernormAddRes] unsupported dataType.\");\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                                int8_t*      out,\n                                                float*       residual_input,\n                                                const float* bias,\n                                                const float* gamma,\n                                                const float* beta,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                hipStream_t stream,\n                                                const float* input_deQFactor_ptr,\n                                                const float* out_QFactor_ptr);\n\ntemplate void invokeAddBiasLayernormAddResCol32(int8_t*      out_int8,\n                                                int8_t*      out,\n                                                half*        residual_input,\n                                                const half*  bias,\n                                                const half*  gamma,\n                                                const half*  beta,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                hipStream_t stream,\n                                                const float* input_deQFactor_ptr,\n                                                const float* out_QFactor_ptr);\n\n/*******************  invokeGeneralAddBiasResidualPreLayerNormCol32  ***********************/\n\n// each warp process 1 row, and increase the num of warps per block to increase occupancy\ntemplate<int WPB, int VPT, bool WRITE_RESIDUAL, bool IS_BIAS, bool IS_INPUT>\n__global__ void add_bias_input_layernorm_COL32_int8IO_warpReduce(int8_t*       output,\n                                                                 int8_t*       input1,\n                                                                 __half*       residual,\n                                                                 const __half* bias,\n                                                                 const __half* gamma,\n                                                                 const __half* beta,\n                                                                 int           m,\n                                                                 const int     n,\n                                                                 const float   dqScaleIn,\n                                                                 const float   qScale)\n{\n    const int lane_id   = threadIdx.x % 32;\n    const int warp_id   = threadIdx.x / 32;\n    const int col_start = lane_id * VPT;\n    const int row_id    = blockIdx.x * WPB + warp_id;\n\n    const int idx = (col_start & 0xffffffe0) * m + (row_id << 5) + (col_start & 31);\n\n    if (row_id >= m || col_start >= n)\n        return;\n\n    int8_t in_local[VPT];\n\n    __half in_local_dq[VPT];\n    __half bias_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&residual[idx], in_local_dq);\n    if (IS_INPUT) {\n        copy<sizeof(int8_t) * VPT>(&input1[idx], in_local);\n    }\n    if (IS_BIAS) {\n        copy<sizeof(__half) * VPT>(&bias[col_start], bias_local);\n    }\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        // DQ input\n        if (IS_INPUT) {\n            in_local_dq[it] += dqScaleIn * in_local[it];\n        }\n        if (IS_BIAS) {\n            in_local_dq[it] += bias_local[it];\n        }\n        local_sum += (float)in_local_dq[it];\n    }\n    // load parameters\n    copy<sizeof(__half) * VPT>(&beta[col_start], bias_local);\n    copy<sizeof(__half) * VPT>(&gamma[col_start], gamma_local);\n\n    __shared__ float mu[WPB];      // mean\n    __shared__ float rsigma[WPB];  // 1 / std.dev.\n\n    local_sum = warpReduceSum<float>(local_sum);\n\n    if (lane_id == 0) {\n        mu[warp_id] = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += ((float)in_local_dq[it] - mu[warp_id]) * ((float)in_local_dq[it] - mu[warp_id]);\n    }\n    local_sum = warpReduceSum<float>(local_sum);\n    if (lane_id == 0) {\n        rsigma[warp_id] = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT / 4; it++) {\n        const float tmp0 =\n            (float)gamma_local[it * 4 + 0] * ((float)in_local_dq[it * 4 + 0] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 0];\n        const float tmp1 =\n            (float)gamma_local[it * 4 + 1] * ((float)in_local_dq[it * 4 + 1] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 1];\n        const float tmp2 =\n            (float)gamma_local[it * 4 + 2] * ((float)in_local_dq[it * 4 + 2] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 2];\n        const float tmp3 =\n            (float)gamma_local[it * 4 + 3] * ((float)in_local_dq[it * 4 + 3] - mu[warp_id]) * rsigma[warp_id]\n            + (float)bias_local[it * 4 + 3];\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &output[idx]);\n    if (WRITE_RESIDUAL)\n        copy<sizeof(__half) * VPT>(in_local_dq, &residual[idx]);\n}\n\ntemplate<int VPT, bool WRITE_RESIDUAL, bool IS_BIAS, bool IS_INPUT>\n__global__ void add_bias_input_layernorm_COL32_int8IO(int8_t*       output,\n                                                      int8_t*       input1,\n                                                      __half*       residual,\n                                                      const __half* bias,\n                                                      const __half* gamma,\n                                                      const __half* beta,\n                                                      int           m,\n                                                      const int     n,\n                                                      const float   dqScaleIn,\n                                                      const float   qScale)\n{\n    // compute idx based on COL32\n    // [m,n]-COL32 (blockIdx.x, threadIdx.x * VPT)\n    const int col_start = threadIdx.x * VPT;\n    const int idx       = (col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31);\n\n    int8_t in_local[VPT];\n\n    __half in_local_dq[VPT];\n    __half bias_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&residual[idx], in_local_dq);\n    if (IS_INPUT) {\n        copy<sizeof(int8_t) * VPT>(&input1[idx], in_local);\n    }\n    if (IS_BIAS) {\n        copy<sizeof(__half) * VPT>(&bias[col_start], bias_local);\n    }\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        // DQ input\n        if (IS_INPUT) {\n            in_local_dq[it] += dqScaleIn * in_local[it];\n        }\n        if (IS_BIAS) {\n            in_local_dq[it] += bias_local[it];\n        }\n        local_sum += (float)in_local_dq[it];\n    }\n    // load parameters\n    copy<sizeof(__half) * VPT>(&beta[col_start], bias_local);\n    copy<sizeof(__half) * VPT>(&gamma[col_start], gamma_local);\n\n    __shared__ float mu;      // mean\n    __shared__ float rsigma;  // 1 / std.dev.\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        mu = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += ((float)in_local_dq[it] - mu) * ((float)in_local_dq[it] - mu);\n    }\n    local_sum = blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        rsigma = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT / 4; it++) {\n        const float tmp0 = (float)gamma_local[it * 4 + 0] * ((float)in_local_dq[it * 4 + 0] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 0];\n        const float tmp1 = (float)gamma_local[it * 4 + 1] * ((float)in_local_dq[it * 4 + 1] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 1];\n        const float tmp2 = (float)gamma_local[it * 4 + 2] * ((float)in_local_dq[it * 4 + 2] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 2];\n        const float tmp3 = (float)gamma_local[it * 4 + 3] * ((float)in_local_dq[it * 4 + 3] - mu) * rsigma\n                           + (float)bias_local[it * 4 + 3];\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &output[idx]);\n    if (WRITE_RESIDUAL)\n        copy<sizeof(__half) * VPT>(in_local_dq, &residual[idx]);\n}\n\nvoid invokeAddBiasResidualPreLayerNormCol32(int8_t*       output,\n                                            int8_t*       input1,\n                                            __half*       input2,\n                                            const __half* bias,\n                                            const __half* gamma,\n                                            const __half* beta,\n                                            int           m,\n                                            const int     n,\n                                            hipStream_t  stream,\n                                            const float   dqScaleIn,\n                                            const float   qScale)\n{\n    dim3          grid(m);\n    constexpr int VPT = 16 / sizeof(__half);\n    if (n <= VPT * 32 && n % VPT == 0) {\n        constexpr int TPB  = 1024 / VPT;\n        constexpr int WPB  = (TPB + 31) / 32;\n        int           grid = m / WPB;\n        add_bias_input_layernorm_COL32_int8IO_warpReduce<WPB, VPT, true, true, true>\n            <<<grid, TPB, 0, stream>>>(output, input1, input2, bias, gamma, beta, m, n, dqScaleIn, qScale);\n    }\n    else if (n / VPT <= 1024 && n % VPT == 0) {\n        const int blockSize = n / VPT;\n        add_bias_input_layernorm_COL32_int8IO<VPT, true, true, true>\n            <<<grid, blockSize, 0, stream>>>(output, input1, input2, bias, gamma, beta, m, n, dqScaleIn, qScale);\n    }\n    else {\n        printf(\"[FT ERROR]Unsupported dimension n=%d for layernorm, because n / 8 > 1024 || n %% 8 != 0. \\n\", n);\n        exit(0);\n    }\n}\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_COL32_int8IO_noRes(int8_t*      output,\n                                                            int32_t*     input1,\n                                                            T*           input2,\n                                                            const T*     bias,\n                                                            const T*     gamma,\n                                                            const T*     beta,\n                                                            int          m,\n                                                            int          n,\n                                                            const float* weight_amax,\n                                                            const float* input1_amax_ptr,\n                                                            const float* output_scale_ptr)\n{\n    const float input1_amax  = __ldg(input1_amax_ptr);\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 2;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp;\n    if (qual) {\n        input1Tmp         = __ldg(input1TmpPtr + outIdx);\n        int col_start_tmp = col_start;\n        // NOTE: 0.000062f = 1 / 127/.0f / 127.0f\n        local_out[0] = static_cast<float>(input1Tmp.x) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 0]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 0] = local_out[0];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[1]  = static_cast<float>(input1Tmp.y) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 1]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 1] = local_out[1];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[2]  = static_cast<float>(input1Tmp.z) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 2]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 2] = local_out[2];\n\n        col_start_tmp = col_start_tmp + 1;\n        local_out[3]  = static_cast<float>(input1Tmp.w) * input1_amax * weight_amax[col_start_tmp] * 0.000062f\n                       + static_cast<float>(input2[(outIdx << 2) + 3]) + static_cast<float>(bias[col_start_tmp]);\n        input2[(outIdx << 2) + 3] = local_out[3];\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    char4* outputTmpPtr = (char4*)output;\n    char4  outputTmp;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outputTmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n        outputTmpPtr[outIdx] = outputTmp;\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_COL32_int8IO_noRes(int8_t*      output,\n                                                            int32_t*     input1,\n                                                            half2*       input2,\n                                                            const half2* bias,\n                                                            const half2* gamma,\n                                                            const half2* beta,\n                                                            int          m,\n                                                            int          n,\n                                                            const float* weight_amax,\n                                                            const float* input1_amax_ptr,\n                                                            const float* output_scale_ptr)\n{\n    const float2* weight_scale_ptr = (const float2*)weight_amax;\n    const float   input1_amax      = __ldg(input1_amax_ptr);\n    const float   output_scale     = __ldg(output_scale_ptr);\n    int           col_start        = threadIdx.x << 1;\n    bool          qual             = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_out[2];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n    int2* input1TmpPtr = (int2*)input1;\n    int2  input1Tmp;\n    if (qual) {\n        const float2 weight_scale = __ldg(weight_scale_ptr + threadIdx.x);\n        input1Tmp                 = input1TmpPtr[outIdx];\n        half2 biasTmp             = bias[threadIdx.x];\n        half2 input2Tmp           = input2[outIdx];\n        // NOTE: 0.000062f = 1 / 127/.0f / 127.0f\n        local_out[0] =\n            static_cast<float>(input1Tmp.x) * input1_amax * weight_scale.x * 0.000062f + static_cast<float>(biasTmp.x);\n        local_out[1] =\n            static_cast<float>(input1Tmp.y) * input1_amax * weight_scale.y * 0.000062f + static_cast<float>(biasTmp.y);\n\n        local_out[0] += static_cast<float>(input2Tmp.x);\n        local_out[1] += static_cast<float>(input2Tmp.y);\n\n        input2Tmp.x    = local_out[0];\n        input2Tmp.y    = local_out[1];\n        input2[outIdx] = input2Tmp;\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    char2* outputTmpPtr = (char2*)output;\n    char2  outputTmp;\n    if (qual) {\n        half2 gammaTmp = gamma[threadIdx.x];\n        half2 betaTmp  = beta[threadIdx.x];\n        local_out[0] =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outputTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        local_out[1] =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n        outputTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        outputTmpPtr[outIdx] = outputTmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                            int32_t*     input1,\n                                            T*           input2,\n                                            const T*     bias,\n                                            const T*     gamma,\n                                            const T*     beta,\n                                            int          m,\n                                            int          n,\n                                            hipStream_t stream,\n                                            const float* weight_amax,\n                                            const float* input1_amax_ptr,\n                                            const float* output_scale_ptr)\n{\n    dim3 grid(m);\n    int  blockSize = (n / 4 + 31) / 32 * 32;\n    dim3 block(blockSize);\n    assert(blockSize <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        blockSize = (n / 2 + 31) / 32 * 32;\n        assert(blockSize <= 1024);\n        add_bias_input_layernorm_COL32_int8IO_noRes<<<grid, blockSize, 0, stream>>>(output,\n                                                                                    input1,\n                                                                                    (half2*)input2,\n                                                                                    (const half2*)bias,\n                                                                                    (const half2*)gamma,\n                                                                                    (const half2*)beta,\n                                                                                    m,\n                                                                                    n,\n                                                                                    weight_amax,\n                                                                                    input1_amax_ptr,\n                                                                                    output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_COL32_int8IO_noRes<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, weight_amax, input1_amax_ptr, output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                                     int32_t*     input1,\n                                                     float*       input2,\n                                                     const float* bias,\n                                                     const float* gamma,\n                                                     const float* beta,\n                                                     int          m,\n                                                     int          n,\n                                                     hipStream_t stream,\n                                                     const float* weight_amax,\n                                                     const float* input1_amax_ptr,\n                                                     const float* output_scale_ptr);\n\ntemplate void invokeAddBiasResidualPreLayerNormCol32(int8_t*      output,\n                                                     int32_t*     input1,\n                                                     half*        input2,\n                                                     const half*  bias,\n                                                     const half*  gamma,\n                                                     const half*  beta,\n                                                     int          m,\n                                                     int          n,\n                                                     hipStream_t stream,\n                                                     const float* weight_amax,\n                                                     const float* input1_amax_ptr,\n                                                     const float* output_scale_ptr);\n\n/*******************  invokeLayernormCol32  ***********************/\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void layernorm_COL32_DataTypeI_int8O(\n    int8_t* out, const T* input, const T* gamma, const T* beta, int m, int n, const float* output_scale_ptr)\n{\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 2;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n\n    if (qual) {\n        local_out[0] = static_cast<float>(input[(outIdx << 2) + 0]);\n        local_out[1] = static_cast<float>(input[(outIdx << 2) + 1]);\n        local_out[2] = static_cast<float>(input[(outIdx << 2) + 2]);\n        local_out[3] = static_cast<float>(input[(outIdx << 2) + 3]);\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    char4  outTmp;\n    char4* outPtr = (char4*)out;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<>\n__global__ void layernorm_COL32_DataTypeI_int8O(\n    int8_t* out, const half2* input, const half2* gamma, const half2* beta, int m, int n, const float* output_scale_ptr)\n{\n    const float output_scale = __ldg(output_scale_ptr);\n    int         col_start    = threadIdx.x << 1;\n    bool        qual         = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n    // float mean = 0.0f;\n    // float variance = 0.0f;\n\n    float local_out[2];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n\n    if (qual) {\n        half2 inputTmp = input[outIdx];\n        local_out[0]   = static_cast<float>(inputTmp.x);\n        local_out[1]   = static_cast<float>(inputTmp.y);\n\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    char2  outTmp;\n    char2* outPtr = (char2*)out;\n    if (qual) {\n        half2 gammaTmp = gamma[threadIdx.x];\n        half2 betaTmp  = beta[threadIdx.x];\n        local_out[0] =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outTmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n        local_out[1] =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n        outTmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeLayernormCol32(int8_t*      out,\n                          const T*     input,\n                          const T*     gamma,\n                          const T*     beta,\n                          int          m,\n                          int          n,\n                          const float* output_scale_ptr,\n                          hipStream_t stream)\n{\n    dim3 grid(m);\n    int  blockSize = (n / 4 + 31) / 32 * 32;\n    dim3 block(blockSize);\n    assert(blockSize <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        blockSize = (n / 2 + 31) / 32 * 32;\n        assert(blockSize <= 1024);\n        layernorm_COL32_DataTypeI_int8O<<<grid, blockSize, 0, stream>>>(\n            out, (const half2*)input, (const half2*)gamma, (const half2*)beta, m, n, output_scale_ptr);\n    }\n    else {\n        layernorm_COL32_DataTypeI_int8O<T><<<grid, block, 0, stream>>>(out, input, gamma, beta, m, n, output_scale_ptr);\n    }\n}\n\ntemplate void invokeLayernormCol32(int8_t*      out,\n                                   const float* input,\n                                   const float* gamma,\n                                   const float* beta,\n                                   int          m,\n                                   int          n,\n                                   const float* output_scale_ptr,\n                                   hipStream_t stream);\n\ntemplate void invokeLayernormCol32(int8_t*      out,\n                                   const half*  input,\n                                   const half*  gamma,\n                                   const half*  beta,\n                                   int          m,\n                                   int          n,\n                                   const float* output_scale_ptr,\n                                   hipStream_t stream);\n\n/*******************  invokeLayernormCol32  ***********************/\n\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void layernorm_COL32_INT8I_DataTypeO(\n    T* out, const int8_t* input, const T* gamma, const T* beta, int m, int n, const float* input_deQ_ptr)\n{\n    const float input_deQ = __ldg(input_deQ_ptr);\n    int         col_start = threadIdx.x << 2;\n    bool        qual      = (col_start < n);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out[4];\n    int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n\n    char4* inputPtr = (char4*)input;\n    if (qual) {\n        char4 inputTmp = inputPtr[outIdx];\n        local_out[0]   = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]   = static_cast<float>(inputTmp.y) * input_deQ;\n        local_out[2]   = static_cast<float>(inputTmp.z) * input_deQ;\n        local_out[3]   = static_cast<float>(inputTmp.w) * input_deQ;\n    }\n\n    mean = blockReduceSum<float>(qual ? local_out[0] + local_out[1] + local_out[2] + local_out[3] : 0.0f);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    if (qual) {\n        local_out[0] = local_out[0] - s_mean;\n        local_out[1] = local_out[1] - s_mean;\n        local_out[2] = local_out[2] - s_mean;\n        local_out[3] = local_out[3] - s_mean;\n    }\n    variance = blockReduceSum<float>(qual ? local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                                + local_out[2] * local_out[2] + local_out[3] * local_out[3] :\n                                            0.0f);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    float4  outTmp;\n    float4* outPtr = (float4*)out;\n    if (qual) {\n        local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.x = local_out[0];\n\n        col_start    = col_start + 1;\n        local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.y = local_out[1];\n\n        col_start    = col_start + 1;\n        local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.z = local_out[2];\n\n        col_start    = col_start + 1;\n        local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                       + static_cast<float>(__ldg(beta + col_start));\n        outTmp.w = local_out[3];\n\n        outPtr[outIdx] = outTmp;\n    }\n}\n\ntemplate<>\n__global__ void layernorm_COL32_INT8I_DataTypeO(\n    half2* out, const int8_t* input, const half2* gamma, const half2* beta, int m, int n, const float* input_deQ_ptr)\n{\n    const float input_deQ = __ldg(input_deQ_ptr);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            sums[2] = {0.0f, 0.0f};\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n / 2; i += blockDim.x) {\n        int    col_start = i << 1;\n        float  local_out[2];\n        int    outIdx   = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n        char2* inputPtr = (char2*)input;\n        char2  inputTmp = inputPtr[outIdx];\n        local_out[0]    = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]    = static_cast<float>(inputTmp.y) * input_deQ;\n\n        for (int i = 0; i < 2; i++) {\n            sums[0] += local_out[i];\n            sums[1] += local_out[i] * local_out[i];\n        }\n    }\n\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] * __fdividef(1.0f, n);\n        s_variance = rsqrtf(sums[1] * __fdividef(1.0f, n) - s_mean * s_mean + 1e-6);\n    }\n    __syncthreads();\n\n    float2 outTmp;\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n / 2; i += blockDim.x) {\n        int    col_start = i << 1;\n        float  local_out[2];\n        int    outIdx   = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 1;\n        char2* inputPtr = (char2*)input;\n        char2  inputTmp = inputPtr[outIdx];\n        local_out[0]    = static_cast<float>(inputTmp.x) * input_deQ;\n        local_out[1]    = static_cast<float>(inputTmp.y) * input_deQ;\n\n        half2 gammaTmp = gamma[i];\n        half2 betaTmp  = beta[i];\n        outTmp.x =\n            (local_out[0] - s_mean) * s_variance * static_cast<float>(gammaTmp.x) + static_cast<float>(betaTmp.x);\n        outTmp.y =\n            (local_out[1] - s_mean) * s_variance * static_cast<float>(gammaTmp.y) + static_cast<float>(betaTmp.y);\n\n        out[outIdx] = __float22half2_rn(outTmp);\n    }\n}\n\ntemplate<typename T>\nvoid invokeLayernormCol32(T*            out,\n                          const int8_t* input,\n                          const T*      gamma,\n                          const T*      beta,\n                          int           m,\n                          int           n,\n                          const float*  input_deQ_ptr,\n                          hipStream_t  stream)\n{\n    dim3 grid(m);\n    if (sizeof(T) == sizeof(half)) {\n        int blockSize = (n / 2 + 31) / 32 * 32;\n        blockSize     = (blockSize <= 1024) ? blockSize : 1024;\n        layernorm_COL32_INT8I_DataTypeO<<<grid, blockSize, 0, stream>>>(\n            (half2*)out, input, (const half2*)gamma, (const half2*)beta, m, n, input_deQ_ptr);\n    }\n    else {\n        int blockSize = (n / 4 + 31) / 32 * 32;\n        blockSize     = (blockSize <= 1024) ? blockSize : 1024;\n        layernorm_COL32_INT8I_DataTypeO<T>\n            <<<grid, blockSize, 0, stream>>>(out, input, gamma, beta, m, n, input_deQ_ptr);\n    }\n}\n\ntemplate void invokeLayernormCol32(float*        out,\n                                   const int8_t* input,\n                                   const float*  gamma,\n                                   const float*  beta,\n                                   int           m,\n                                   int           n,\n                                   const float*  input_deQ_ptr,\n                                   hipStream_t  stream);\n\ntemplate void invokeLayernormCol32(half*         out,\n                                   const int8_t* input,\n                                   const half*   gamma,\n                                   const half*   beta,\n                                   int           m,\n                                   int           n,\n                                   const float*  input_deQ_ptr,\n                                   hipStream_t  stream);\n\n/*******************  invokeLayernormShiftPartitionCol32  ***********************/\n\n// each warp process 1 row, and increase the num of warps per block to increase occupancy\ntemplate<int WPB, int VPT>\n__global__ void layernorm_shift_partition_COL32_warpReduce(int8_t*       out_ptr,\n                                                           const __half* input_ptr,\n                                                           const __half* gamma_ptr,\n                                                           const __half* beta_ptr,\n                                                           int           batch,\n                                                           int           H,\n                                                           int           W,\n                                                           int           n,\n                                                           const float   qScale,\n                                                           int           shift_size,\n                                                           int           window_size)\n{\n    const int lane_id   = threadIdx.x % 32;\n    const int warp_id   = threadIdx.x / 32;\n    const int col_start = lane_id * VPT;\n    const int row_id    = blockIdx.x * WPB + warp_id;\n    if (row_id >= batch * H * W || col_start >= n)\n        return;\n\n    const int blockIdx_z = row_id / (H * W);\n    const int blockIdx_y = (row_id / W) % H;\n    const int blockIdx_x = row_id % W;\n\n    const int batch_offset = blockIdx_z * H * W;\n    const int m            = batch * H * W;\n\n    const int shifted_H_idx = (shift_size != 0) ? ((blockIdx_y - shift_size + H) % H) : blockIdx_y;\n    const int shifted_W_idx = (shift_size != 0) ? ((blockIdx_x - shift_size + W) % W) : blockIdx_x;\n    const int window_H_idx  = shifted_H_idx / window_size;\n    const int window_W_idx  = shifted_W_idx / window_size;\n    const int window_idx    = window_H_idx * (W / window_size) + window_W_idx;\n    const int idx_in_window = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid    = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    const int idx    = (col_start & 0xffffffe0) * m + (row_id << 5) + (col_start & 31);\n    const int outIdx = (col_start & 0xffffffe0) * m + (output_bid << 5) + (col_start & 31);\n\n    float  in_local_dq[VPT];\n    __half in_local[VPT];\n    __half beta_local[VPT];\n    __half gamma_local[VPT];\n    copy<sizeof(__half) * VPT>(&input_ptr[idx], in_local);\n    copy<sizeof(__half) * VPT>(&gamma_ptr[col_start], gamma_local);\n    copy<sizeof(__half) * VPT>(&beta_ptr[col_start], beta_local);\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        in_local_dq[it] = in_local[it];\n        local_sum += in_local_dq[it];\n    }\n\n    __shared__ float mu[WPB];\n    __shared__ float rsigma[WPB];\n\n    local_sum = warpReduceSum<float>(local_sum);\n\n    if (lane_id == 0) {\n        mu[warp_id] = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += (in_local_dq[it] - mu[warp_id]) * (in_local_dq[it] - mu[warp_id]);\n    }\n    local_sum = warpReduceSum<float>(local_sum);\n    if (lane_id == 0) {\n        rsigma[warp_id] = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        const float tmp0 =\n            (in_local_dq[it * 4 + 0] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 0])\n            + static_cast<float>(beta_local[it * 4 + 0]);\n        const float tmp1 =\n            (in_local_dq[it * 4 + 1] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 1])\n            + static_cast<float>(beta_local[it * 4 + 1]);\n        const float tmp2 =\n            (in_local_dq[it * 4 + 2] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 2])\n            + static_cast<float>(beta_local[it * 4 + 2]);\n        const float tmp3 =\n            (in_local_dq[it * 4 + 3] - mu[warp_id]) * rsigma[warp_id] * static_cast<float>(gamma_local[it * 4 + 3])\n            + static_cast<float>(beta_local[it * 4 + 3]);\n        out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n    }\n\n    copy<sizeof(int8_t) * VPT>(out_local, &out_ptr[outIdx]);\n}\n\ntemplate<int VPT>\n__global__ void layernorm_shift_partition_COL32(int8_t*       out_ptr,\n                                                const __half* input_ptr,\n                                                const __half* gamma_ptr,\n                                                const __half* beta_ptr,\n                                                int           batch,\n                                                int           H,\n                                                int           W,\n                                                int           n,\n                                                const float   qScale,\n                                                int           shift_size,\n                                                int           window_size)\n{\n    const int batch_offset = blockIdx.z * gridDim.y * gridDim.x;\n    const int m            = gridDim.z * gridDim.y * gridDim.x;\n    const int bid          = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n\n    int tid = threadIdx.x * VPT;\n    if (tid >= n)\n        return;\n    __shared__ float mu;\n    __shared__ float rsigma;\n    __half           inputTmp[VPT];\n    __half           beta_local[VPT];\n    __half           gamma_local[VPT];\n    float            in_local_dq[VPT];\n\n    const int offset_col32 = (tid & 0xffffffe0) * m + (bid << 5) + (tid & 31);\n    copy<sizeof(__half) * VPT>(&input_ptr[offset_col32], inputTmp);\n    copy<sizeof(__half) * VPT>(&gamma_ptr[tid], gamma_local);\n    copy<sizeof(__half) * VPT>(&beta_ptr[tid], beta_local);\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        in_local_dq[it] = inputTmp[it];\n        local_sum += in_local_dq[it];\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n\n    if (threadIdx.x == 0) {\n        mu = local_sum * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int it = 0; it < VPT; it++) {\n        local_sum += (in_local_dq[it] - mu) * (in_local_dq[it] - mu);\n    }\n    local_sum = blockReduceSum<float>(local_sum);\n    if (threadIdx.x == 0) {\n        rsigma = rsqrtf(local_sum * __fdividef(1.0f, n) + 1e-5);\n    }\n    __syncthreads();\n\n    static_assert(VPT % 4 == 0, \"\");\n    uint32_t out_local[VPT / 4];\n    if (tid < n) {\n#pragma unroll\n        for (int it = 0; it < VPT / 4; it++) {\n            const float tmp0 = (in_local_dq[it * 4 + 0] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 0])\n                               + static_cast<float>(beta_local[it * 4 + 0]);\n            const float tmp1 = (in_local_dq[it * 4 + 1] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 1])\n                               + static_cast<float>(beta_local[it * 4 + 1]);\n            const float tmp2 = (in_local_dq[it * 4 + 2] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 2])\n                               + static_cast<float>(beta_local[it * 4 + 2]);\n            const float tmp3 = (in_local_dq[it * 4 + 3] - mu) * rsigma * static_cast<float>(gamma_local[it * 4 + 3])\n                               + static_cast<float>(beta_local[it * 4 + 3]);\n            out_local[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);\n        }\n        const int outIdx = (tid & 0xffffffe0) * m + (output_bid << 5) + (tid & 31);\n        copy<sizeof(int8_t) * VPT>(out_local, &out_ptr[outIdx]);\n    }\n}\n\nvoid invokeLayernormShiftPartitionCol32(int8_t*      out,\n                                        const half*  input,\n                                        const half*  gamma,\n                                        const half*  beta,\n                                        int          batch,\n                                        int          H,\n                                        int          W,\n                                        int          n,\n                                        const float  norm_scale,\n                                        int          shift_size,\n                                        int          window_size,\n                                        hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n\n    constexpr int VPT       = 16 / sizeof(half);\n    int           blockSize = (n / VPT + 31) / 32 * 32;\n    if (blockSize == 32 && n % VPT == 0) {\n        constexpr int TPB  = 1024 / VPT;\n        constexpr int WPB  = (TPB + 31) / 32;\n        int           grid = (batch * H * W + WPB - 1) / WPB;\n        layernorm_shift_partition_COL32_warpReduce<WPB, VPT><<<grid, TPB, 0, stream>>>(out,\n                                                                                       (const __half*)input,\n                                                                                       (const __half*)gamma,\n                                                                                       (const __half*)beta,\n                                                                                       batch,\n                                                                                       H,\n                                                                                       W,\n                                                                                       n,\n                                                                                       norm_scale,\n                                                                                       shift_size,\n                                                                                       window_size);\n    }\n    else if (n / VPT <= 1024 && n % VPT == 0) {\n        layernorm_shift_partition_COL32<VPT><<<grid, blockSize, 0, stream>>>(out,\n                                                                             (const __half*)input,\n                                                                             (const __half*)gamma,\n                                                                             (const __half*)beta,\n                                                                             batch,\n                                                                             H,\n                                                                             W,\n                                                                             n,\n                                                                             norm_scale,\n                                                                             shift_size,\n                                                                             window_size);\n    }\n    else {\n        printf(\"[FT ERROR]Unsupported dimension n=%d for layernorm, because n / 8 > 1024 || n %% 8 != 0. \\n\", n);\n        exit(0);\n    }\n}\n\n/*******************  invokeMergeLayerNormCol32  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (n)\ntemplate<typename T>\n__global__ void merge_layernorm_v2(int8_t* out,\n                                   const T* __restrict input,\n                                   const T* __restrict gamma,\n                                   const T* __restrict beta,\n                                   int          batch,\n                                   const float* merge_inFactor,\n                                   int          H,\n                                   int          W,\n                                   int          n)\n{\n    const int   ite       = 4;\n    const int   tid       = threadIdx.x;\n    const int   W_idx     = blockIdx.x;\n    const int   H_idx     = blockIdx.y;\n    const float out_scale = __ldg(merge_inFactor);\n    // const size_t batch_offset = blockIdx.z * H * W * n;\n    // const int input_H_stride = W*n/2;\n    // const int output_H_stride = W*n;\n    const int n_4 = n >> 2;\n    const int m   = batch * 4 * H * W;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            int part_id     = col_id / n_4;\n            int offset_in_W = part_id / 2;\n            int offset_in_H = part_id % 2;\n            // size_t input_id = batch_offset + (2*H_idx + offset_in_H)*input_H_stride + (2*W_idx + offset_in_W)*n_4 +\n            // (col_id % n_4);\n\n            int col_input = col_id % n_4;\n            int row_input = blockIdx.z * H * W * 4 + (2 * H_idx + offset_in_H) * W * 2 + (2 * W_idx + offset_in_W);\n            int input_idx_col32 = ((col_input >> 5) << 5) * m + (row_input << 5) + (col_input & 31);\n            local_out[i]        = (float)(__ldg(input + input_idx_col32));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = local_out[i] - s_mean;\n            var += local_out[i] * local_out[i];\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + 1e-6f);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            // size_t output_idx = batch_offset + (H_idx*W + W_idx)*n + col_id;\n\n            int col_output        = col_id;\n            int row_output        = blockIdx.z * H * W + H_idx * W + W_idx;\n            int output_idx_col32  = ((col_output >> 5) << 5) * (m >> 2) + (row_output << 5) + (col_output & 31);\n            out[output_idx_col32] = float_to_int8_rn(\n                out_scale * (local_out[i] * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id])));\n        }\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeMergeLayerNormCol32(int8_t*      output,\n                               const T*     input,\n                               const T*     gamma,\n                               const T*     beta,\n                               int          batch,\n                               const float* merge_inFactor,\n                               int          H,\n                               int          W,\n                               int          n,\n                               hipStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeMergeLayerNormCol32] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = (blockSize + 31) / 32 * 32;\n    // TODO\n    // if (blockSize >= 768)\n    {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        merge_layernorm_v2<T>\n            <<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, merge_inFactor, H / 2, W / 2, n * 4);\n    }\n    /*\n    else\n      merge_layernorm<T><<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, H/2, W/2, n*4);\n    */\n}\n\ntemplate void invokeMergeLayerNormCol32<float>(int8_t*      output,\n                                               const float* input,\n                                               const float* gamma,\n                                               const float* beta,\n                                               int          batch,\n                                               const float* merge_inFactor,\n                                               int          H,\n                                               int          W,\n                                               int          n,\n                                               hipStream_t stream);\n\ntemplate void invokeMergeLayerNormCol32<half>(int8_t*      output,\n                                              const half*  input,\n                                              const half*  gamma,\n                                              const half*  beta,\n                                              int          batch,\n                                              const float* merge_inFactor,\n                                              int          H,\n                                              int          W,\n                                              int          n,\n                                              hipStream_t stream);\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_ROW_int8IO(int8_t*       output,\n                                                    const int8_t* input1,\n                                                    const int8_t* input2,\n                                                    const T*      bias,\n                                                    const T*      gamma,\n                                                    const T*      beta,\n                                                    int           m,\n                                                    int           n,\n                                                    const float*  input1_deQFactor_ptr,\n                                                    const float*  input2_deQFactor_ptr,\n                                                    const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = (blockIdx.x * n + col_start) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.x = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.y = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[2] = (local_out[2] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.z = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                   + static_cast<float>(__ldg(beta + col_start));\n    input2Tmp.w = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<>\n__global__ void add_bias_input_layernorm_ROW_int8IO(int8_t*       output,\n                                                    const int8_t* input1,\n                                                    const int8_t* input2,\n                                                    const half2*  bias,\n                                                    const half2*  gamma,\n                                                    const half2*  beta,\n                                                    int           m,\n                                                    int           n,\n                                                    const float*  input1_deQFactor_ptr,\n                                                    const float*  input2_deQFactor_ptr,\n                                                    const float*  output_scale_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    const float output_scale     = __ldg(output_scale_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float  local_out[4];\n    int    outIdx       = (blockIdx.x * n + col_start) >> 2;\n    char4* outTmpPtr    = (char4*)output;\n    char4* input1TmpPtr = (char4*)input1;\n    char4* input2TmpPtr = (char4*)input2;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n    char4  input2Tmp    = __ldg(input2TmpPtr + outIdx);\n\n    int   col_start_tmp = col_start;\n    half2 biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[0]        = static_cast<float>(input2Tmp.x) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.x) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2Tmp.y) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.y) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    col_start_tmp = col_start_tmp + 1;\n    biasTmp       = __ldg(bias + (col_start_tmp >> 1));\n    local_out[2]  = static_cast<float>(input2Tmp.z) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.z) * input1_deQFactor + static_cast<float>(biasTmp.x);\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2Tmp.w) * input2_deQFactor\n                   + static_cast<float>(input1Tmp.w) * input1_deQFactor + static_cast<float>(biasTmp.y);\n\n    mean = blockReduceSum<float>(local_out[0] + local_out[1] + local_out[2] + local_out[3]);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out[0] = local_out[0] - s_mean;\n    local_out[1] = local_out[1] - s_mean;\n    local_out[2] = local_out[2] - s_mean;\n    local_out[3] = local_out[3] - s_mean;\n    variance     = blockReduceSum<float>(local_out[0] * local_out[0] + local_out[1] * local_out[1]\n                                     + local_out[2] * local_out[2] + local_out[3] * local_out[3]);\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    half2 betaTmp = __ldg(beta + col_start_tmp);\n\n    local_out[0] = (local_out[0] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.x  = float_to_int8_rn(local_out[0] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[1] = (local_out[1] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.y  = float_to_int8_rn(local_out[1] * output_scale);\n\n    col_start     = col_start + 1;\n    col_start_tmp = col_start >> 1;\n    biasTmp       = __ldg(gamma + col_start_tmp);\n    betaTmp       = __ldg(beta + col_start_tmp);\n    local_out[2]  = (local_out[2] * s_variance) * static_cast<float>(biasTmp.x) + static_cast<float>(betaTmp.x);\n    input2Tmp.z   = float_to_int8_rn(local_out[2] * output_scale);\n\n    col_start    = col_start + 1;\n    local_out[3] = (local_out[3] * s_variance) * static_cast<float>(biasTmp.y) + static_cast<float>(betaTmp.y);\n    input2Tmp.w  = float_to_int8_rn(local_out[3] * output_scale);\n\n    outTmpPtr[outIdx] = input2Tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                       const int8_t* input1,\n                                       const int8_t* input2,\n                                       const T*      bias,\n                                       const T*      gamma,\n                                       const T*      beta,\n                                       int           m,\n                                       int           n,\n                                       hipStream_t  stream,\n                                       const float*  input1_deQFactor_ptr,\n                                       const float*  input2_deQFactor_ptr,\n                                       const float*  output_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(n <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_input_layernorm_ROW_int8IO<<<grid, block, 0, stream>>>(output,\n                                                                        input1,\n                                                                        input2,\n                                                                        (const half2*)bias,\n                                                                        (const half2*)gamma,\n                                                                        (const half2*)beta,\n                                                                        m,\n                                                                        n,\n                                                                        input1_deQFactor_ptr,\n                                                                        input2_deQFactor_ptr,\n                                                                        output_scale_ptr);\n    }\n    else {\n        add_bias_input_layernorm_ROW_int8IO<T><<<grid, block, 0, stream>>>(output,\n                                                                           input1,\n                                                                           input2,\n                                                                           bias,\n                                                                           gamma,\n                                                                           beta,\n                                                                           m,\n                                                                           n,\n                                                                           input1_deQFactor_ptr,\n                                                                           input2_deQFactor_ptr,\n                                                                           output_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                                const int8_t* input1,\n                                                const int8_t* input2,\n                                                const float*  bias,\n                                                const float*  gamma,\n                                                const float*  beta,\n                                                int           m,\n                                                int           n,\n                                                hipStream_t  stream,\n                                                const float*  input1_deQFactor_ptr,\n                                                const float*  input2_deQFactor_ptr,\n                                                const float*  output_scale_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormRow(int8_t*       output,\n                                                const int8_t* input1,\n                                                const int8_t* input2,\n                                                const half*   bias,\n                                                const half*   gamma,\n                                                const half*   beta,\n                                                int           m,\n                                                int           n,\n                                                hipStream_t  stream,\n                                                const float*  input1_deQFactor_ptr,\n                                                const float*  input2_deQFactor_ptr,\n                                                const float*  output_scale_ptr);\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n)\ntemplate<typename T>\n__global__ void add_bias_input_layernorm_ROW_int8I_DataTypeO(T*            output,\n                                                             const int8_t* input1,\n                                                             const int8_t* input2,\n                                                             const T*      bias,\n                                                             const T*      gamma,\n                                                             const T*      beta,\n                                                             int           m,\n                                                             int           n,\n                                                             const float*  input1_deQFactor_ptr,\n                                                             const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out;\n    int   idx = blockIdx.x * n + col_start;\n\n    local_out = static_cast<float>(__ldg(input2 + idx)) * input2_deQFactor\n                + static_cast<float>(__ldg(input1 + idx)) * input1_deQFactor\n                + static_cast<float>(__ldg(bias + col_start));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out = local_out - s_mean;\n\n    variance = blockReduceSum<float>(local_out * local_out);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    local_out = (local_out * s_variance) * static_cast<float>(__ldg(gamma + col_start))\n                + static_cast<float>(__ldg(beta + col_start));\n\n    output[idx] = local_out;\n}\n\n// input1/input2/out matrix with layout of row major (m*n)\n//(grid, block) must be (m, n/2)\ntemplate<>\n__global__ void add_bias_input_layernorm_ROW_int8I_DataTypeO(half2*        output,\n                                                             const int8_t* input1,\n                                                             const int8_t* input2,\n                                                             const half2*  bias,\n                                                             const half2*  gamma,\n                                                             const half2*  beta,\n                                                             int           m,\n                                                             int           n,\n                                                             const float*  input1_deQFactor_ptr,\n                                                             const float*  input2_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    const float input2_deQFactor = __ldg(input2_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 1;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float2 local_out;\n    int    idx = (blockIdx.x * n + col_start) >> 1;\n\n    const char2* input1_ptr2 = (const char2*)input1;\n    const char2* input2_ptr2 = (const char2*)input2;\n    char2        input_tmp1  = __ldg(input1_ptr2 + idx);\n    char2        input_tmp2  = __ldg(input2_ptr2 + idx);\n\n    half2 bias_tmp = __ldg(bias + threadIdx.x);\n\n    local_out.x = static_cast<float>(input_tmp1.x) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.x) * input2_deQFactor + static_cast<float>(bias_tmp.x);\n\n    local_out.y = static_cast<float>(input_tmp1.y) * input1_deQFactor\n                  + static_cast<float>(input_tmp2.y) * input2_deQFactor + static_cast<float>(bias_tmp.y);\n\n    mean = blockReduceSum<float>(local_out.x + local_out.y);\n    if (threadIdx.x == 0) {\n        s_mean = mean * __fdividef(1.0f, n);\n    }\n    __syncthreads();\n\n    local_out.x = local_out.x - s_mean;\n\n    local_out.y = local_out.y - s_mean;\n\n    variance = blockReduceSum<float>(local_out.x * local_out.x + local_out.y * local_out.y);\n\n    if (threadIdx.x == 0) {\n        s_variance = variance * __fdividef(1.0f, n) + 1e-6f;\n        s_variance = rsqrtf(s_variance);\n    }\n    __syncthreads();\n\n    half2 gamma_tmp = __ldg(gamma + threadIdx.x);\n    half2 beta_tmp  = __ldg(beta + threadIdx.x);\n\n    local_out.x = (local_out.x * s_variance) * static_cast<float>(gamma_tmp.x) + static_cast<float>(beta_tmp.x);\n    local_out.y = (local_out.y * s_variance) * static_cast<float>(gamma_tmp.y) + static_cast<float>(beta_tmp.y);\n\n    bias_tmp.x = half(local_out.x);\n    bias_tmp.y = half(local_out.y);\n\n    output[idx] = bias_tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNormRow(T*            output,\n                                       const int8_t* input1,\n                                       const int8_t* input2,\n                                       const T*      bias,\n                                       const T*      gamma,\n                                       const T*      beta,\n                                       int           m,\n                                       int           n,\n                                       hipStream_t  stream,\n                                       const float*  input1_deQFactor_ptr,\n                                       const float*  input2_deQFactor_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n);\n    if (sizeof(T) == sizeof(half)) {\n        assert(n / 2 <= 1024 && n % 2 == 0);\n        block.x = n / 2;\n        add_bias_input_layernorm_ROW_int8I_DataTypeO<<<grid, block, 0, stream>>>((half2*)output,\n                                                                                 input1,\n                                                                                 input2,\n                                                                                 (const half2*)bias,\n                                                                                 (const half2*)gamma,\n                                                                                 (const half2*)beta,\n                                                                                 m,\n                                                                                 n,\n                                                                                 input1_deQFactor_ptr,\n                                                                                 input2_deQFactor_ptr);\n    }\n    else {\n        assert(n <= 1024);\n        add_bias_input_layernorm_ROW_int8I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, gamma, beta, m, n, input1_deQFactor_ptr, input2_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNormRow<float>(float*        output,\n                                                       const int8_t* input1,\n                                                       const int8_t* input2,\n                                                       const float*  bias,\n                                                       const float*  gamma,\n                                                       const float*  beta,\n                                                       int           m,\n                                                       int           n,\n                                                       hipStream_t  stream,\n                                                       const float*  input1_deQFactor_ptr,\n                                                       const float*  input2_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualLayerNormRow<half>(half*         output,\n                                                      const int8_t* input1,\n                                                      const int8_t* input2,\n                                                      const half*   bias,\n                                                      const half*   gamma,\n                                                      const half*   beta,\n                                                      int           m,\n                                                      int           n,\n                                                      hipStream_t  stream,\n                                                      const float*  input1_deQFactor_ptr,\n                                                      const float*  input2_deQFactor_ptr);\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/layernorm_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n\n#include \"src/fastertransformer/kernels/beam_search_penalty_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void add_bias_temperature(T*          logits,\n                                     const T*    bias,\n                                     const int   batch_size,\n                                     const int   beam_width,\n                                     const int   vocab_size,\n                                     const int   vocab_size_padded,\n                                     const float temperature)\n{\n    int tid  = threadIdx.x;\n    int bid  = blockIdx.x;\n    int bbid = blockIdx.y;\n\n    logits += bbid * vocab_size_padded;\n\n    const T MASK_VAL = (std::is_same<T, half>::value) ? -HALF_FLT_MAX : -FLT_MAX;\n    const T inv_temp = static_cast<T>(1.0f / (temperature + 1e-6f));\n    for (int i = tid + bid * blockDim.x; i < vocab_size_padded; i += blockDim.x * gridDim.x) {\n        if (i < vocab_size) {\n            T bias_val = bias == nullptr ? (T)(0.0f) : bias[i];\n            logits[i]  = (logits[i] + bias_val) * inv_temp;\n        }\n        else {\n            logits[i] = MASK_VAL;\n        }\n    }\n}\n\ntemplate<>\n__global__ void add_bias_temperature(half2*       logits,\n                                     const half2* bias,\n                                     const int    batch_size,\n                                     const int    beam_width,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     const float  temperature)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n\n    const int tid  = threadIdx.x;\n    const int bid  = blockIdx.x;\n    const int bbid = blockIdx.y;\n\n    const half2 mask_val = __float2half2_rn(-HALF_FLT_MAX);\n    const half2 inv_temp = __float2half2_rn(1.0f / (temperature + 1e-6f));\n\n    const int half_vocab_size        = vocab_size / 2;\n    const int half_vocab_size_padded = vocab_size_padded / 2;\n\n    logits += bbid * half_vocab_size_padded;\n    for (int index = tid + bid * blockDim.x; index < half_vocab_size_padded; index += blockDim.x * gridDim.x) {\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logit = __hmul2(logit, inv_temp);\n        }\n        logits[index] = logit;\n    }\n}\n\ntemplate<typename T, bool IS_ADDITIVE>\n__global__ void apply_repetition_penalty(T*          logits,\n                                         const int   batch_size,\n                                         const int   beam_width,\n                                         const int   vocab_size,\n                                         const int   vocab_size_padded,\n                                         const int   step,\n                                         const int*  current_ids,\n                                         const int*  previous_ids,\n                                         const int*  parent_ids,\n                                         const int*  input_lengths,\n                                         const int   max_input_length,\n                                         const float repetition_penalty)\n{\n    assert(step > 0);\n\n    const int tid      = threadIdx.x;\n    const int bbid     = blockIdx.x;\n    const int batch_id = bbid / beam_width;\n    const int bbsize   = batch_size * beam_width;\n\n    logits += bbid * vocab_size_padded;\n    extern __shared__ char sbuf[];\n    T*                     penalty_logits = reinterpret_cast<T*>(sbuf);\n    // prevent misaligment when sizeof(T) = 2\n    int*      penalty_indices = reinterpret_cast<int*>(sbuf + (sizeof(T) * step + 31) / 32 * 32);\n    const int input_length    = (input_lengths != nullptr) ? input_lengths[bbid] : max_input_length;\n    if (tid == 0) {\n        T   repet_penalty         = static_cast<T>(repetition_penalty);\n        int prev_id               = current_ids[bbid];\n        T   prev_logit            = logits[prev_id];\n        penalty_indices[step - 1] = prev_id;\n\n        if (IS_ADDITIVE) {\n            penalty_logits[step - 1] = prev_logit - repet_penalty;\n        }\n        else {\n            penalty_logits[step - 1] = prev_logit > T(0) ? prev_logit / repet_penalty : prev_logit * repet_penalty;\n        }\n        if (step > 1) {\n            int parent_beam = bbid % beam_width;\n            for (int i = step - 2; i >= 0; --i) {\n                // Skip the padded tokens.\n                if (i >= input_length && i < max_input_length) {\n                    continue;\n                }\n                parent_beam        = parent_ids[i * bbsize + batch_id * beam_width + parent_beam];\n                prev_id            = previous_ids[i * bbsize + batch_id * beam_width + parent_beam];\n                prev_logit         = logits[prev_id];\n                penalty_indices[i] = prev_id;\n                if (IS_ADDITIVE) {\n                    penalty_logits[i] = prev_logit - repet_penalty;\n                }\n                else {\n                    penalty_logits[i] = prev_logit > T(0) ? prev_logit / repet_penalty : prev_logit * repet_penalty;\n                }\n            }\n        }\n    }\n    __syncthreads();\n    for (int i = tid; i < step; i += blockDim.x) {\n        if (i >= input_length && i < max_input_length) {\n            continue;\n        }\n        logits[penalty_indices[i]] = penalty_logits[i];\n    }\n}\n\ntemplate<typename T>\n__global__ void apply_min_length_penalty(T*         logits,\n                                         const int  min_length,\n                                         const int* end_ids,\n                                         const int* sequence_lengths,\n                                         const int  max_input_length,\n                                         const int  beam_width,\n                                         const int  vocab_size_padded)\n{\n    int bbid = threadIdx.x + blockIdx.x * blockDim.x;  // batch-beam index\n    int bid  = bbid / beam_width;                      // batch index\n    // We need +1 because sequence_lengths = max_input_length + num_gen_tokens - 1,\n    // which is equal to the length of k/v caches.\n    if (sequence_lengths[bbid] + 1 - max_input_length < min_length) {\n        T mask_val                                      = (std::is_same<T, half>::value) ? -HALF_FLT_MAX : -FLT_MAX;\n        logits[bbid * vocab_size_padded + end_ids[bid]] = mask_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasApplyPenalties(int                         step,\n                                 T*                          logits,\n                                 const int*                  current_ids,\n                                 const int*                  previous_ids,\n                                 const int*                  parent_ids,\n                                 const int*                  input_lengths,\n                                 const int*                  sequence_lengths,\n                                 const T*                    bias,\n                                 const int                   ite,\n                                 const int                   max_input_length,\n                                 const int                   local_batch_size,\n                                 const int                   batch_size,\n                                 const int                   beam_width,\n                                 const int                   vocab_size,\n                                 const int                   vocab_size_padded,\n                                 const int*                  end_ids,\n                                 const float                 temperature,\n                                 const float                 repetition_penalty,\n                                 const RepetitionPenaltyType repetition_penalty_type,\n                                 const int                   min_length,\n                                 cudaStream_t                stream)\n{\n    if (bias != nullptr || temperature != 1.0f || vocab_size != vocab_size_padded) {\n        dim3 block(512);\n        if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padded % 2 == 0) {\n            dim3 grid((vocab_size_padded / 2 + block.x - 1) / block.x, beam_width * local_batch_size);\n            add_bias_temperature<<<grid, block, 0, stream>>>(reinterpret_cast<half2*>(logits),\n                                                             reinterpret_cast<const half2*>(bias),\n                                                             batch_size,\n                                                             beam_width,\n                                                             vocab_size,\n                                                             vocab_size_padded,\n                                                             temperature);\n        }\n        else {\n            dim3 grid((vocab_size_padded + block.x - 1) / block.x, beam_width * local_batch_size);\n            add_bias_temperature<<<grid, block, 0, stream>>>(\n                logits, bias, batch_size, beam_width, vocab_size, vocab_size_padded, temperature);\n        }\n    }\n\n    if (repetition_penalty_type != RepetitionPenaltyType::None && step > 0) {\n        if (repetition_penalty != getDefaultPenaltyValue(repetition_penalty_type)) {\n            size_t smem_size = (sizeof(T) * step + 31) / 32 * 32 + sizeof(int) * step;\n            dim3   block(256);\n            dim3   grid(beam_width * local_batch_size);\n            if (repetition_penalty_type == RepetitionPenaltyType::Multiplicative) {\n                apply_repetition_penalty<T, false>\n                    <<<grid, block, smem_size, stream>>>(logits,\n                                                         batch_size,\n                                                         beam_width,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         step,\n                                                         current_ids,\n                                                         previous_ids,\n                                                         // TODO(jaedeokk):\n                                                         //   Remove (+ite ...) by getting parent_ids with offset\n                                                         //   and then remove 'ite' argument from the function.\n                                                         parent_ids + ite * beam_width * local_batch_size,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         repetition_penalty);\n            }\n            else if (repetition_penalty_type == RepetitionPenaltyType::Additive) {\n                apply_repetition_penalty<T, true>\n                    <<<grid, block, smem_size, stream>>>(logits,\n                                                         batch_size,\n                                                         beam_width,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         step,\n                                                         current_ids,\n                                                         previous_ids,\n                                                         parent_ids + ite * beam_width * local_batch_size,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         repetition_penalty);\n            }\n        }\n    }\n\n    if (step - max_input_length < min_length) {\n        FT_CHECK_WITH_INFO(sequence_lengths != nullptr, \"Need sequence_lengths to apply min length penlaty\");\n        FT_CHECK_WITH_INFO(end_ids != nullptr, \"Need end_id to apply min length penlaty\");\n\n        const int block_size = min(local_batch_size * beam_width, 1024);\n        const int grid_size  = (local_batch_size * beam_width + block_size - 1) / block_size;\n        apply_min_length_penalty<<<grid_size, block_size, 0, stream>>>(\n            logits, min_length, end_ids, sequence_lengths, max_input_length, beam_width, vocab_size_padded);\n    }\n}\n\ntemplate void invokeAddBiasApplyPenalties(int                         step,\n                                          float*                      logits,\n                                          const int*                  current_ids,\n                                          const int*                  previous_ids,\n                                          const int*                  parent_ids,\n                                          const int*                  input_lengths,\n                                          const int*                  sequence_lengths,\n                                          const float*                bias,\n                                          const int                   ite,\n                                          const int                   max_input_length,\n                                          const int                   local_batch_size,\n                                          const int                   batch_size,\n                                          const int                   beam_width,\n                                          const int                   vocab_size,\n                                          const int                   vocab_size_padded,\n                                          const int*                  end_ids,\n                                          const float                 temperature,\n                                          const float                 repetition_penalty,\n                                          const RepetitionPenaltyType repetition_penalty_type,\n                                          const int                   min_length,\n                                          cudaStream_t                stream);\n\ntemplate void invokeAddBiasApplyPenalties(int                         step,\n                                          half*                       logits,\n                                          const int*                  current_ids,\n                                          const int*                  previous_ids,\n                                          const int*                  parent_ids,\n                                          const int*                  input_lengths,\n                                          const int*                  sequence_lengths,\n                                          const half*                 bias,\n                                          const int                   ite,\n                                          const int                   max_input_length,\n                                          const int                   local_batch_size,\n                                          const int                   batch_size,\n                                          const int                   beam_width,\n                                          const int                   vocab_size,\n                                          const int                   vocab_size_padded,\n                                          const int*                  end_ids,\n                                          const float                 temperature,\n                                          const float                 repetition_penalty,\n                                          const RepetitionPenaltyType repetition_penalty_type,\n                                          const int                   min_length,\n                                          cudaStream_t                stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <assert.h>\n\n#include \"src/fastertransformer/kernels/beam_search_penalty_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void add_bias_temperature(T*          logits,\n                                     const T*    bias,\n                                     const int   batch_size,\n                                     const int   beam_width,\n                                     const int   vocab_size,\n                                     const int   vocab_size_padded,\n                                     const float temperature)\n{\n    int tid  = threadIdx.x;\n    int bid  = blockIdx.x;\n    int bbid = blockIdx.y;\n\n    logits += bbid * vocab_size_padded;\n\n    const T MASK_VAL = (std::is_same<T, half>::value) ? -HALF_FLT_MAX : -FLT_MAX;\n    const T inv_temp = static_cast<T>(1.0f / (temperature + 1e-6f));\n    for (int i = tid + bid * blockDim.x; i < vocab_size_padded; i += blockDim.x * gridDim.x) {\n        if (i < vocab_size) {\n            T bias_val = bias == nullptr ? (T)(0.0f) : bias[i];\n            logits[i]  = (logits[i] + bias_val) * inv_temp;\n        }\n        else {\n            logits[i] = MASK_VAL;\n        }\n    }\n}\n\ntemplate<>\n__global__ void add_bias_temperature(half2*       logits,\n                                     const half2* bias,\n                                     const int    batch_size,\n                                     const int    beam_width,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     const float  temperature)\n{\n    assert(vocab_size % 2 == 0);\n    assert(vocab_size_padded % 2 == 0);\n\n    const int tid  = threadIdx.x;\n    const int bid  = blockIdx.x;\n    const int bbid = blockIdx.y;\n\n    const half2 mask_val = __float2half2_rn(-HALF_FLT_MAX);\n    const half2 inv_temp = __float2half2_rn(1.0f / (temperature + 1e-6f));\n\n    const int half_vocab_size        = vocab_size / 2;\n    const int half_vocab_size_padded = vocab_size_padded / 2;\n\n    logits += bbid * half_vocab_size_padded;\n    for (int index = tid + bid * blockDim.x; index < half_vocab_size_padded; index += blockDim.x * gridDim.x) {\n        int   vocab_idx = index % half_vocab_size_padded;\n        half2 logit     = vocab_idx < half_vocab_size ? __ldg(&logits[index]) : mask_val;\n        if (vocab_idx < half_vocab_size) {\n            if (bias != nullptr) {\n                logit = __hadd2(logit, bias[vocab_idx]);\n            }\n            logit = __hmul2(logit, inv_temp);\n        }\n        logits[index] = logit;\n    }\n}\n\ntemplate<typename T, bool IS_ADDITIVE>\n__global__ void apply_repetition_penalty(T*          logits,\n                                         const int   batch_size,\n                                         const int   beam_width,\n                                         const int   vocab_size,\n                                         const int   vocab_size_padded,\n                                         const int   step,\n                                         const int*  current_ids,\n                                         const int*  previous_ids,\n                                         const int*  parent_ids,\n                                         const int*  input_lengths,\n                                         const int   max_input_length,\n                                         const float repetition_penalty)\n{\n    assert(step > 0);\n\n    const int tid      = threadIdx.x;\n    const int bbid     = blockIdx.x;\n    const int batch_id = bbid / beam_width;\n    const int bbsize   = batch_size * beam_width;\n\n    logits += bbid * vocab_size_padded;\n    extern __shared__ char sbuf[];\n    T*                     penalty_logits = reinterpret_cast<T*>(sbuf);\n    // prevent misaligment when sizeof(T) = 2\n    int*      penalty_indices = reinterpret_cast<int*>(sbuf + (sizeof(T) * step + 31) / 32 * 32);\n    const int input_length    = (input_lengths != nullptr) ? input_lengths[bbid] : max_input_length;\n    if (tid == 0) {\n        T   repet_penalty         = static_cast<T>(repetition_penalty);\n        int prev_id               = current_ids[bbid];\n        T   prev_logit            = logits[prev_id];\n        penalty_indices[step - 1] = prev_id;\n\n        if (IS_ADDITIVE) {\n            penalty_logits[step - 1] = prev_logit - repet_penalty;\n        }\n        else {\n            penalty_logits[step - 1] = prev_logit > T(0) ? prev_logit / repet_penalty : prev_logit * repet_penalty;\n        }\n        if (step > 1) {\n            int parent_beam = bbid % beam_width;\n            for (int i = step - 2; i >= 0; --i) {\n                // Skip the padded tokens.\n                if (i >= input_length && i < max_input_length) {\n                    continue;\n                }\n                parent_beam        = parent_ids[i * bbsize + batch_id * beam_width + parent_beam];\n                prev_id            = previous_ids[i * bbsize + batch_id * beam_width + parent_beam];\n                prev_logit         = logits[prev_id];\n                penalty_indices[i] = prev_id;\n                if (IS_ADDITIVE) {\n                    penalty_logits[i] = prev_logit - repet_penalty;\n                }\n                else {\n                    penalty_logits[i] = prev_logit > T(0) ? prev_logit / repet_penalty : prev_logit * repet_penalty;\n                }\n            }\n        }\n    }\n    __syncthreads();\n    for (int i = tid; i < step; i += blockDim.x) {\n        if (i >= input_length && i < max_input_length) {\n            continue;\n        }\n        logits[penalty_indices[i]] = penalty_logits[i];\n    }\n}\n\ntemplate<typename T>\n__global__ void apply_min_length_penalty(T*         logits,\n                                         const int  min_length,\n                                         const int* end_ids,\n                                         const int* sequence_lengths,\n                                         const int  max_input_length,\n                                         const int  beam_width,\n                                         const int  vocab_size_padded)\n{\n    int bbid = threadIdx.x + blockIdx.x * blockDim.x;  // batch-beam index\n    int bid  = bbid / beam_width;                      // batch index\n    // We need +1 because sequence_lengths = max_input_length + num_gen_tokens - 1,\n    // which is equal to the length of k/v caches.\n    if (sequence_lengths[bbid] + 1 - max_input_length < min_length) {\n        T mask_val                                      = (std::is_same<T, half>::value) ? -HALF_FLT_MAX : -FLT_MAX;\n        logits[bbid * vocab_size_padded + end_ids[bid]] = mask_val;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasApplyPenalties(int                         step,\n                                 T*                          logits,\n                                 const int*                  current_ids,\n                                 const int*                  previous_ids,\n                                 const int*                  parent_ids,\n                                 const int*                  input_lengths,\n                                 const int*                  sequence_lengths,\n                                 const T*                    bias,\n                                 const int                   ite,\n                                 const int                   max_input_length,\n                                 const int                   local_batch_size,\n                                 const int                   batch_size,\n                                 const int                   beam_width,\n                                 const int                   vocab_size,\n                                 const int                   vocab_size_padded,\n                                 const int*                  end_ids,\n                                 const float                 temperature,\n                                 const float                 repetition_penalty,\n                                 const RepetitionPenaltyType repetition_penalty_type,\n                                 const int                   min_length,\n                                 hipStream_t                stream)\n{\n    if (bias != nullptr || temperature != 1.0f || vocab_size != vocab_size_padded) {\n        dim3 block(512);\n        if (std::is_same<T, half>::value && vocab_size % 2 == 0 && vocab_size_padded % 2 == 0) {\n            dim3 grid((vocab_size_padded / 2 + block.x - 1) / block.x, beam_width * local_batch_size);\n            add_bias_temperature<<<grid, block, 0, stream>>>(reinterpret_cast<half2*>(logits),\n                                                             reinterpret_cast<const half2*>(bias),\n                                                             batch_size,\n                                                             beam_width,\n                                                             vocab_size,\n                                                             vocab_size_padded,\n                                                             temperature);\n        }\n        else {\n            dim3 grid((vocab_size_padded + block.x - 1) / block.x, beam_width * local_batch_size);\n            add_bias_temperature<<<grid, block, 0, stream>>>(\n                logits, bias, batch_size, beam_width, vocab_size, vocab_size_padded, temperature);\n        }\n    }\n\n    if (repetition_penalty_type != RepetitionPenaltyType::None && step > 0) {\n        if (repetition_penalty != getDefaultPenaltyValue(repetition_penalty_type)) {\n            size_t smem_size = (sizeof(T) * step + 31) / 32 * 32 + sizeof(int) * step;\n            dim3   block(256);\n            dim3   grid(beam_width * local_batch_size);\n            if (repetition_penalty_type == RepetitionPenaltyType::Multiplicative) {\n                apply_repetition_penalty<T, false>\n                    <<<grid, block, smem_size, stream>>>(logits,\n                                                         batch_size,\n                                                         beam_width,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         step,\n                                                         current_ids,\n                                                         previous_ids,\n                                                         // TODO(jaedeokk):\n                                                         //   Remove (+ite ...) by getting parent_ids with offset\n                                                         //   and then remove 'ite' argument from the function.\n                                                         parent_ids + ite * beam_width * local_batch_size,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         repetition_penalty);\n            }\n            else if (repetition_penalty_type == RepetitionPenaltyType::Additive) {\n                apply_repetition_penalty<T, true>\n                    <<<grid, block, smem_size, stream>>>(logits,\n                                                         batch_size,\n                                                         beam_width,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         step,\n                                                         current_ids,\n                                                         previous_ids,\n                                                         parent_ids + ite * beam_width * local_batch_size,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         repetition_penalty);\n            }\n        }\n    }\n\n    if (step - max_input_length < min_length) {\n        FT_CHECK_WITH_INFO(sequence_lengths != nullptr, \"Need sequence_lengths to apply min length penlaty\");\n        FT_CHECK_WITH_INFO(end_ids != nullptr, \"Need end_id to apply min length penlaty\");\n\n        const int block_size = min(local_batch_size * beam_width, 1024);\n        const int grid_size  = (local_batch_size * beam_width + block_size - 1) / block_size;\n        apply_min_length_penalty<<<grid_size, block_size, 0, stream>>>(\n            logits, min_length, end_ids, sequence_lengths, max_input_length, beam_width, vocab_size_padded);\n    }\n}\n\ntemplate void invokeAddBiasApplyPenalties(int                         step,\n                                          float*                      logits,\n                                          const int*                  current_ids,\n                                          const int*                  previous_ids,\n                                          const int*                  parent_ids,\n                                          const int*                  input_lengths,\n                                          const int*                  sequence_lengths,\n                                          const float*                bias,\n                                          const int                   ite,\n                                          const int                   max_input_length,\n                                          const int                   local_batch_size,\n                                          const int                   batch_size,\n                                          const int                   beam_width,\n                                          const int                   vocab_size,\n                                          const int                   vocab_size_padded,\n                                          const int*                  end_ids,\n                                          const float                 temperature,\n                                          const float                 repetition_penalty,\n                                          const RepetitionPenaltyType repetition_penalty_type,\n                                          const int                   min_length,\n                                          hipStream_t                stream);\n\ntemplate void invokeAddBiasApplyPenalties(int                         step,\n                                          half*                       logits,\n                                          const int*                  current_ids,\n                                          const int*                  previous_ids,\n                                          const int*                  parent_ids,\n                                          const int*                  input_lengths,\n                                          const int*                  sequence_lengths,\n                                          const half*                 bias,\n                                          const int                   ite,\n                                          const int                   max_input_length,\n                                          const int                   local_batch_size,\n                                          const int                   batch_size,\n                                          const int                   beam_width,\n                                          const int                   vocab_size,\n                                          const int                   vocab_size_padded,\n                                          const int*                  end_ids,\n                                          const float                 temperature,\n                                          const float                 repetition_penalty,\n                                          const RepetitionPenaltyType repetition_penalty_type,\n                                          const int                   min_length,\n                                          hipStream_t                stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/beam_search_penalty_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"dequantize_kernels.h\"\n#include \"reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/***********************invoke deQuantization**************************/\n__global__ void dequantized_kernel(float4* dst, const char4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = __ldg(scale_ptr);\n        char4       tmp    = __ldg(src + tid);\n        int         outIdx = tid;\n\n        float4 float4Tmp;\n        float4Tmp.x = static_cast<float>(tmp.x) * scale;\n        float4Tmp.y = static_cast<float>(tmp.y) * scale;\n        float4Tmp.z = static_cast<float>(tmp.z) * scale;\n        float4Tmp.w = static_cast<float>(tmp.w) * scale;\n        dst[outIdx] = float4Tmp;\n    }\n}\n\n__global__ void dequantized_kernel(half4* dst, const char4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = __ldg(scale_ptr);\n        char4       tmp    = __ldg(src + tid);\n        int         outIdx = tid;\n\n        half4 half4Tmp;\n        half4Tmp.x  = static_cast<half>(static_cast<float>(tmp.x) * scale);\n        half4Tmp.y  = static_cast<half>(static_cast<float>(tmp.y) * scale);\n        half4Tmp.z  = static_cast<half>(static_cast<float>(tmp.z) * scale);\n        half4Tmp.w  = static_cast<half>(static_cast<float>(tmp.w) * scale);\n        dst[outIdx] = half4Tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDequantization(T* dst, const int8_t* src, const int size, const float* scale_ptr, cudaStream_t stream)\n{\n\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        dequantized_kernel<<<grid, block, 0, stream>>>((float4*)dst, (const char4*)src, size / 4, scale_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        dequantized_kernel<<<grid, block, 0, stream>>>((half4*)dst, (const char4*)src, size / 4, scale_ptr);\n    }\n}\n\ntemplate void\ninvokeDequantization<float>(float* dst, const int8_t* src, const int size, const float* scale_ptr, cudaStream_t stream);\n\ntemplate void\ninvokeDequantization<half>(half* dst, const int8_t* src, const int size, const float* scale_ptr, cudaStream_t stream);\n\n/***********************invoke deQuantization**************************/\n__global__ void dequantized_kernel_INT32(\n    float4* dst, const int4* src, const int size_div_4, const float* input_amax_ptr, const float* weight_amax_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = (1.0f / __ldg(input_amax_ptr)) * (__ldg(weight_amax_ptr) / 127.0f);\n        int4        tmp    = src[tid];\n        int         outIdx = tid;\n\n        float4 float4Tmp;\n        float4Tmp.x = static_cast<float>(tmp.x) * scale;\n        float4Tmp.y = static_cast<float>(tmp.y) * scale;\n        float4Tmp.z = static_cast<float>(tmp.z) * scale;\n        float4Tmp.w = static_cast<float>(tmp.w) * scale;\n        dst[outIdx] = float4Tmp;\n    }\n}\n\n__global__ void dequantized_kernel_INT32(\n    half4* dst, const int4* src, const int size_div_4, const float* input_amax_ptr, const float* weight_amax_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = (1.0f / __ldg(input_amax_ptr)) * (__ldg(weight_amax_ptr) / 127.0f);\n        int4        tmp    = src[tid];\n        int         outIdx = tid;\n\n        half4 half4Tmp;\n        half4Tmp.x  = static_cast<half>(static_cast<float>(tmp.x) * scale);\n        half4Tmp.y  = static_cast<half>(static_cast<float>(tmp.y) * scale);\n        half4Tmp.z  = static_cast<half>(static_cast<float>(tmp.z) * scale);\n        half4Tmp.w  = static_cast<half>(static_cast<float>(tmp.w) * scale);\n        dst[outIdx] = half4Tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDequantization_INT32(T*             dst,\n                                const int32_t* src,\n                                const int      size,\n                                cudaStream_t   stream,\n                                const float*   input_amax_ptr,\n                                const float*   weight_amax_ptr)\n{\n\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        dequantized_kernel_INT32<<<grid, block, 0, stream>>>(\n            (float4*)dst, (const int4*)src, size / 4, input_amax_ptr, weight_amax_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        dequantized_kernel_INT32<<<grid, block, 0, stream>>>(\n            (half4*)dst, (const int4*)src, size / 4, input_amax_ptr, weight_amax_ptr);\n    }\n}\n\ntemplate void invokeDequantization_INT32<float>(float*         dst,\n                                                const int32_t* src,\n                                                const int      size,\n                                                cudaStream_t   stream,\n                                                const float*   input_amax_ptr,\n                                                const float*   weight_amax_ptr);\n\ntemplate void invokeDequantization_INT32<half>(half*          dst,\n                                               const int32_t* src,\n                                               const int      size,\n                                               cudaStream_t   stream,\n                                               const float*   input_amax_ptr,\n                                               const float*   weight_amax_ptr);\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"dequantize_kernels.h\"\n#include \"reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n/***********************invoke deQuantization**************************/\n__global__ void dequantized_kernel(float4* dst, const char4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = __ldg(scale_ptr);\n        char4       tmp    = __ldg(src + tid);\n        int         outIdx = tid;\n\n        float4 float4Tmp;\n        float4Tmp.x = static_cast<float>(tmp.x) * scale;\n        float4Tmp.y = static_cast<float>(tmp.y) * scale;\n        float4Tmp.z = static_cast<float>(tmp.z) * scale;\n        float4Tmp.w = static_cast<float>(tmp.w) * scale;\n        dst[outIdx] = float4Tmp;\n    }\n}\n\n__global__ void dequantized_kernel(half4* dst, const char4* src, const int size_div_4, const float* scale_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = __ldg(scale_ptr);\n        char4       tmp    = __ldg(src + tid);\n        int         outIdx = tid;\n\n        half4 half4Tmp;\n        half4Tmp.x  = static_cast<half>(static_cast<float>(tmp.x) * scale);\n        half4Tmp.y  = static_cast<half>(static_cast<float>(tmp.y) * scale);\n        half4Tmp.z  = static_cast<half>(static_cast<float>(tmp.z) * scale);\n        half4Tmp.w  = static_cast<half>(static_cast<float>(tmp.w) * scale);\n        dst[outIdx] = half4Tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDequantization(T* dst, const int8_t* src, const int size, const float* scale_ptr, hipStream_t stream)\n{\n\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        dequantized_kernel<<<grid, block, 0, stream>>>((float4*)dst, (const char4*)src, size / 4, scale_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        dequantized_kernel<<<grid, block, 0, stream>>>((half4*)dst, (const char4*)src, size / 4, scale_ptr);\n    }\n}\n\ntemplate void\ninvokeDequantization<float>(float* dst, const int8_t* src, const int size, const float* scale_ptr, hipStream_t stream);\n\ntemplate void\ninvokeDequantization<half>(half* dst, const int8_t* src, const int size, const float* scale_ptr, hipStream_t stream);\n\n/***********************invoke deQuantization**************************/\n__global__ void dequantized_kernel_INT32(\n    float4* dst, const int4* src, const int size_div_4, const float* input_amax_ptr, const float* weight_amax_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = (1.0f / __ldg(input_amax_ptr)) * (__ldg(weight_amax_ptr) / 127.0f);\n        int4        tmp    = src[tid];\n        int         outIdx = tid;\n\n        float4 float4Tmp;\n        float4Tmp.x = static_cast<float>(tmp.x) * scale;\n        float4Tmp.y = static_cast<float>(tmp.y) * scale;\n        float4Tmp.z = static_cast<float>(tmp.z) * scale;\n        float4Tmp.w = static_cast<float>(tmp.w) * scale;\n        dst[outIdx] = float4Tmp;\n    }\n}\n\n__global__ void dequantized_kernel_INT32(\n    half4* dst, const int4* src, const int size_div_4, const float* input_amax_ptr, const float* weight_amax_ptr)\n{\n    int tid = (blockIdx.x * blockDim.x + threadIdx.x);\n    if (tid < size_div_4) {\n        const float scale  = (1.0f / __ldg(input_amax_ptr)) * (__ldg(weight_amax_ptr) / 127.0f);\n        int4        tmp    = src[tid];\n        int         outIdx = tid;\n\n        half4 half4Tmp;\n        half4Tmp.x  = static_cast<half>(static_cast<float>(tmp.x) * scale);\n        half4Tmp.y  = static_cast<half>(static_cast<float>(tmp.y) * scale);\n        half4Tmp.z  = static_cast<half>(static_cast<float>(tmp.z) * scale);\n        half4Tmp.w  = static_cast<half>(static_cast<float>(tmp.w) * scale);\n        dst[outIdx] = half4Tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDequantization_INT32(T*             dst,\n                                const int32_t* src,\n                                const int      size,\n                                hipStream_t   stream,\n                                const float*   input_amax_ptr,\n                                const float*   weight_amax_ptr)\n{\n\n    if (size % 4 != 0) {\n        printf(\"[ERROR][invokeQuantization] size should be a multiple of 4.\\n\");\n        exit(-1);\n    }\n    dim3 grid((size + 255) / 256);\n    dim3 block(64);\n    if (sizeof(T) == sizeof(float)) {\n        dequantized_kernel_INT32<<<grid, block, 0, stream>>>(\n            (float4*)dst, (const int4*)src, size / 4, input_amax_ptr, weight_amax_ptr);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        dequantized_kernel_INT32<<<grid, block, 0, stream>>>(\n            (half4*)dst, (const int4*)src, size / 4, input_amax_ptr, weight_amax_ptr);\n    }\n}\n\ntemplate void invokeDequantization_INT32<float>(float*         dst,\n                                                const int32_t* src,\n                                                const int      size,\n                                                hipStream_t   stream,\n                                                const float*   input_amax_ptr,\n                                                const float*   weight_amax_ptr);\n\ntemplate void invokeDequantization_INT32<half>(half*          dst,\n                                               const int32_t* src,\n                                               const int      size,\n                                               hipStream_t   stream,\n                                               const float*   input_amax_ptr,\n                                               const float*   weight_amax_ptr);\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/dequantize_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n#include \"src/fastertransformer/kernels/gpt_kernels.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T, bool OUTPUT_ID, int PROMPT_SRC>\n__global__ void start_id_embedding_position_lookups_kernel(T*                    from_tensor,\n                                                           int*                  output_ids,\n                                                           const T*              embedding_table,\n                                                           const T*              pos_table,\n                                                           pPromptTuningParam<T> prompt_param,\n                                                           const int*            input_ids,\n                                                           const int             start_step,\n                                                           const int             length,\n                                                           const int             max_length,\n                                                           const int             batch_size,\n                                                           const int64_t         hidden_units)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * length * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        // transpose the input_ids [batch, length] (part of [batch, max_length]) to output_ids [length, batch]\n        if (OUTPUT_ID && index < batch_size * max_length) {\n            // for p/prompt_tuning (have prompt templates like [input1, prompt1, input2, prompt2])\n            // we have to process it to like [input1, input2, prompt1, prompt2], and then remove the prompts during post\n            // processing\n            if (PROMPT_SRC > 0) {\n                if (index < batch_size) {\n                    int no_prompt_output_seq_id = 0;\n#pragma unroll 1\n                    for (int seq_id = 0; seq_id < max_length; seq_id++) {\n                        int current_input_id = input_ids[index * max_length + seq_id];\n                        if (current_input_id < prompt_param.p_prompt_tuning_id_start) {\n                            output_ids[no_prompt_output_seq_id * batch_size + index] = current_input_id;\n                            no_prompt_output_seq_id++;\n                        }\n                    }\n                }\n            }\n            else {\n                const int seq_id   = index % max_length;\n                const int batch_id = index / max_length;\n                if (seq_id < length) {\n                    output_ids[seq_id * batch_size + batch_id] = input_ids[index];\n                }\n            }\n        }\n\n        // embedding lookup from word ids [batch, length] (part of [batch, max_length]) and [vocab, hidden] to generate\n        // embedding [batch, length, hidden]\n        const int word_index      = index / hidden_units;\n        const int word_index_row  = word_index / length;  // batch_id\n        const int word_index_col  = word_index % length;\n        const int real_word_index = word_index_row * max_length + word_index_col;\n        const int step            = start_step + word_index % length;\n        const int col_index       = index % hidden_units;\n        const int input_id        = input_ids == nullptr ? real_word_index : input_ids[real_word_index];\n        const int prompt_id       = input_id - prompt_param.p_prompt_tuning_id_start;\n        T         embedding       = (T)0.0f;\n        if (PROMPT_SRC > 0 && prompt_id >= 0) {\n            if (PROMPT_SRC == 1) {\n                // from loaded prompt embedding tables\n                embedding =\n                    prompt_param.p_prompt_tuning_batch_weights[word_index_row][prompt_id * hidden_units + col_index];\n            }\n            else {\n                // from request prompt embedding\n                embedding =\n                    prompt_param\n                        .request_prompt_embedding[word_index_row * prompt_param.request_prompt_max_length * hidden_units\n                                                  + prompt_id * hidden_units + col_index];\n            }\n        }\n        else {\n            embedding = embedding_table[input_id * hidden_units + col_index];\n        }\n        T pos_embed        = pos_table == nullptr ? (T)0.f : pos_table[(step - 1) * hidden_units + col_index];\n        from_tensor[index] = embedding + pos_embed;\n    }\n}\n\n#define WORD_POS_EMBEDDING_LOOPUP_KERNEL(OUTPUT_ID, PROMPT_SRC)                                                        \\\n    start_id_embedding_position_lookups_kernel<T, OUTPUT_ID, PROMPT_SRC><<<grid, block, 0, stream>>>(from_tensor,      \\\n                                                                                                     output_ids,       \\\n                                                                                                     embedding_table,  \\\n                                                                                                     pos_table,        \\\n                                                                                                     prompt_param,     \\\n                                                                                                     input_ids,        \\\n                                                                                                     start_step,       \\\n                                                                                                     length,           \\\n                                                                                                     max_length,       \\\n                                                                                                     batch_size,       \\\n                                                                                                     hidden_units);\n\ntemplate<typename T>\nvoid invokeInputIdsEmbeddingLookupPosEncoding(T*                    from_tensor,\n                                              int*                  output_ids,\n                                              const T*              embedding_table,  // can also be inputs_embeds\n                                              const T*              pos_table,\n                                              pPromptTuningParam<T> prompt_param,\n                                              const int*            input_ids,\n                                              const int             start_step,\n                                              const int             length,\n                                              const int             max_length,\n                                              const int             batch_size,\n                                              const int             hidden_units,\n                                              cudaStream_t          stream)\n{\n    dim3       grid(min(batch_size * length, 65536));\n    dim3       block(min(hidden_units, 512));\n    const bool has_output_ids = output_ids != nullptr;\n    FT_CHECK(!(has_output_ids && input_ids == nullptr));\n\n    if (has_output_ids) {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 1);\n        }\n        else {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 0);\n        }\n    }\n    else {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 1);\n        }\n        else {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 0);\n        }\n    }\n}\n\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(float*                    from_tensor,\n                                                       int*                      output_ids,\n                                                       const float*              embedding_table,\n                                                       const float*              pos_table,\n                                                       pPromptTuningParam<float> prompt_param,\n                                                       const int*                input_ids,\n                                                       const int                 start_step,\n                                                       const int                 length,\n                                                       const int                 max_length,\n                                                       const int                 batch_size,\n                                                       const int                 hidden_units,\n                                                       cudaStream_t              stream);\n\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(half*                    from_tensor,\n                                                       int*                     output_ids,\n                                                       const half*              embedding_table,\n                                                       const half*              pos_table,\n                                                       pPromptTuningParam<half> prompt_param,\n                                                       const int*               input_ids,\n                                                       const int                start_step,\n                                                       const int                length,\n                                                       const int                max_length,\n                                                       const int                batch_size,\n                                                       const int                hidden_units,\n                                                       cudaStream_t             stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(__nv_bfloat16*                    from_tensor,\n                                                       int*                              output_ids,\n                                                       const __nv_bfloat16*              embedding_table,\n                                                       const __nv_bfloat16*              pos_table,\n                                                       pPromptTuningParam<__nv_bfloat16> prompt_param,\n                                                       const int*                        input_ids,\n                                                       const int                         start_step,\n                                                       const int                         length,\n                                                       const int                         max_length,\n                                                       const int                         batch_size,\n                                                       const int                         hidden_units,\n                                                       cudaStream_t                      stream);\n#endif\n\ntemplate<typename T>\n__global__ void inputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<T> param)\n{\n    // 1. Copy the input ids to output ids and transpose output ids to [seq_len, batch_size, beam_width].\n    // 2. Embedding lookup by input ids and concat with soft prompt. The axis of concatenation is on axis of seq_len.\n\n    // Assume batch size is 2 and prompts are [[t1, t2], [t3], [t4, t5]], input_ids are [[s1, s2], [s3], [s4]]\n    // then the order of output_ids is\n    // [ [?, ?, s1, s2]\n    //   [?, s3, padding, padding]\n    //   [?, ?, s4, padding] ]\n    // and the order of embedding is\n    // [ [t1, t2, s1, s2]\n    //   [t3, s3, padding, padding]\n    //   [t4, t5, s4, padding] ]\n    // where \"?\" means undefined values and we should attach it.\n\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x;\n         index < param.batch_size * param.beam_width * (param.max_prefix_soft_prompt_length + param.max_input_length)\n                     * param.hidden_units;\n         index += blockDim.x * gridDim.x) {\n        // transpose the input_ids [batch, length] (part of [batch, beam, max_input_length]) to\n        // output_ids [length, batch, beam].\n        // ouptut_ids need to add padding in the beginning for soft prompting.\n\n        if (index < param.batch_size * param.beam_width * param.max_input_length) {\n            int       tmp_index = index;\n            const int seq_id    = tmp_index % param.max_input_length;\n            tmp_index           = (tmp_index - seq_id) / param.max_input_length;\n            const int beam_id   = tmp_index % param.beam_width;\n            tmp_index           = (tmp_index - beam_id) / param.beam_width;\n            const int batch_id  = tmp_index % param.batch_size;\n            if (seq_id < param.max_input_length) {\n                param.output_ids[(param.prefix_soft_prompt_lengths[batch_id] + seq_id) * param.batch_size\n                                     * param.beam_width\n                                 + batch_id * param.beam_width + beam_id] = param.input_ids[index];\n            }\n        }\n\n        // embedding lookup from word ids [batch, beam, length] (part of [batch, beam, max_input_length]), [vocab,\n        // hidden] and [batch, max_prefix_soft_prompt_length, hidden] to generate embedding [batch, beam, length +\n        // max_prefix_soft_prompt_length, hidden]\n        int       tmp_index = index;\n        const int hidden_id = tmp_index % param.hidden_units;\n        tmp_index           = (tmp_index - hidden_id) / param.hidden_units;\n        const int seq_id    = tmp_index % (param.max_prefix_soft_prompt_length + param.max_input_length);\n        tmp_index           = (tmp_index - seq_id) / (param.max_prefix_soft_prompt_length + param.max_input_length);\n        const int beam_id   = tmp_index % param.beam_width;\n        tmp_index           = (tmp_index - beam_id) / param.beam_width;\n        const int batch_id  = tmp_index % param.batch_size;\n        const int64_t hidden_units = param.hidden_units;\n        T         embedding =\n            (seq_id < param.prefix_soft_prompt_lengths[batch_id]) ?\n                        (T)param.prefix_soft_prompt_embedding[batch_id * param.max_prefix_soft_prompt_length * hidden_units\n                                                      + seq_id * hidden_units + hidden_id] :\n                            param.embedding_table[param.input_ids[batch_id * param.beam_width * param.max_input_length\n                                                      + beam_id * param.max_input_length\n                                                      + (seq_id - param.prefix_soft_prompt_lengths[batch_id])]\n                                          * hidden_units\n                                      + hidden_id];\n\n        T pos_embed              = param.pos_table == nullptr ?\n                                       (T)0.0f :\n                                       param.pos_table[(param.start_step + seq_id - 1) * hidden_units + hidden_id];\n        param.from_tensor[index] = embedding + pos_embed;\n\n        if (seq_id == 0 && hidden_id == 0) {\n            param.input_lengths[batch_id * param.beam_width + beam_id] += param.prefix_soft_prompt_lengths[batch_id];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<T> param)\n{\n    dim3 grid(min(param.batch_size * param.beam_width * (param.max_input_length + param.max_prefix_soft_prompt_length),\n                  65536));\n    dim3 block(min(param.hidden_units, 512));\n    inputIdsEmbeddingLookupPosEncodingSoftPrompt<T><<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<float> param);\n\ntemplate void\ninvokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<half> param);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(\n    inputIdsEmbeddingLookupPosEncodingSoftPromptParam<__nv_bfloat16> param);\n#endif\n\n// TODO Add half2 implementation\ntemplate<typename T>\n__global__ void transposeAxis01(T* out, T* in, const int dim0, const int dim1, const int dim2)\n{\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < dim0 * dim1 * dim2) {\n        const int input_dim2_index = index % dim2;\n        index                      = (index - input_dim2_index) / dim2;\n        const int input_dim1_index = index % dim1;\n        index                      = (index - input_dim1_index) / dim1;\n        const int input_dim0_index = index % dim0;\n\n        out[input_dim1_index * dim0 * dim2 + input_dim0_index * dim2 + input_dim2_index] =\n            in[input_dim0_index * dim1 * dim2 + input_dim1_index * dim2 + input_dim2_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAxis01(T* out, T* in, const int dim0, const int dim1, const int dim2, cudaStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(dim0 * dim1 * dim2 / 512.)));\n    transposeAxis01<<<grid, block, 0, stream>>>(out, in, dim0, dim1, dim2);\n}\n\ntemplate void\ninvokeTransposeAxis01(float* out, float* in, const int dim0, const int dim1, const int dim2, cudaStream_t stream);\n\ntemplate void\ninvokeTransposeAxis01(half* out, half* in, const int dim0, const int dim1, const int dim2, cudaStream_t stream);\n\ntemplate void\ninvokeTransposeAxis01(int* out, int* in, const int dim0, const int dim1, const int dim2, cudaStream_t stream);\n\ntemplate<typename T>\n__global__ void transposeAxis01(T* out, T* in, const int* in_skipping_dim1, const int dim0, const int dim1)\n{\n    // out: [dim1, dim0]\n    // in: [dim0, dim1]\n    // in_skipping_dim1: [dim1]\n\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < dim0 * dim1) {\n        const int input_dim1_index = index % dim1;\n        index                      = (index - input_dim1_index) / dim1;\n        const int input_dim0_index = index % dim0;\n        const int in_offset        = in_skipping_dim1 == nullptr ? 0 : in_skipping_dim1[input_dim1_index] * dim1;\n\n        out[input_dim1_index * dim0 + input_dim0_index] = in[in_offset + input_dim0_index * dim1 + input_dim1_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAxis01(\n    T* out, T* in, const int* in_skipping_dim1, const int dim0, const int dim1, cudaStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(dim0 * dim1 / 512.)));\n    transposeAxis01<<<grid, block, 0, stream>>>(out, in, in_skipping_dim1, dim0, dim1);\n}\n\ntemplate void invokeTransposeAxis01(\n    int* out, int* in, const int* in_skipping_dim1, const int dim0, const int dim1, cudaStream_t stream);\n\ntemplate<typename T, bool PREFIX_PROMPT>\n__global__ void buildDecoderAttentionMaskKernel(T*         attention_mask,\n                                                const int* sequence_lengths,\n                                                const int* prefix_prompt_lengths,\n                                                const int  max_seq_len,\n                                                const int  max_prompt_length)\n{\n    // sequence_lengths: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len, max_seq_len + max_prompt_length]\n    const int max_prompt_seq_length = max_seq_len + max_prompt_length;\n    const int mask_size_per_seq     = max_seq_len * max_prompt_seq_length;\n    attention_mask += blockIdx.x * mask_size_per_seq;\n    const int seq_length    = sequence_lengths[blockIdx.x];\n    const int prompt_length = PREFIX_PROMPT ? prefix_prompt_lengths[blockIdx.x] : 0;\n    for (int i = threadIdx.x; i < mask_size_per_seq; i += blockDim.x) {\n        int row_id = i / max_prompt_seq_length;\n        int col_id = i % max_prompt_seq_length;\n        if (row_id < seq_length && col_id <= (row_id + prompt_length)) {\n            attention_mask[i] = (T)(1.0f);\n        }\n        else {\n            attention_mask[i] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildDecoderAttentionMask(T*           attention_mask,\n                                     const int*   sequence_lengths,\n                                     const int*   prefix_prompt_lengths,\n                                     const int    batch_size,\n                                     const int    max_seq_len,\n                                     const int    max_prompt_length,\n                                     cudaStream_t stream)\n{\n    if (max_prompt_length == 0) {\n        buildDecoderAttentionMaskKernel<T, false><<<batch_size, 256, 0, stream>>>(\n            attention_mask, sequence_lengths, prefix_prompt_lengths, max_seq_len, max_prompt_length);\n    }\n    else {\n        buildDecoderAttentionMaskKernel<T, true><<<batch_size, 256, 0, stream>>>(\n            attention_mask, sequence_lengths, prefix_prompt_lengths, max_seq_len, max_prompt_length);\n    }\n}\n\ntemplate void invokeBuildDecoderAttentionMask(float*       attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int*   prefix_prompt_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              const int    max_prompt_length,\n                                              cudaStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask(half*        attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int*   prefix_prompt_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              const int    max_prompt_length,\n                                              cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildDecoderAttentionMask(__nv_bfloat16* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int*     prefix_prompt_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              const int      max_prompt_length,\n                                              cudaStream_t   stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeBuildDecoderAttentionMask(__nv_fp8_e4m3* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int*     prefix_prompt_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              const int      max_prompt_length,\n                                              cudaStream_t   stream);\n#endif\n\ntemplate<typename T>\n__launch_bounds__(1024, 1) __global__ void lookupHiddenStateOfLastToken(T*         from_tensor,\n                                                                        const T*   hidden_state,\n                                                                        const int* input_lengths,\n                                                                        const int  max_input_length,\n                                                                        const int  batch_size,\n                                                                        const int  hidden_units)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        const int col_index = index % hidden_units;\n        const int batch_id  = index / hidden_units;\n        from_tensor[index]  = hidden_state[batch_id * max_input_length * hidden_units\n                                          + (input_lengths[batch_id] - 1) * hidden_units + col_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeLookupHiddenStateOfLastToken(T*           from_tensor,\n                                        const T*     hidden_state,\n                                        const int*   input_lengths,\n                                        const int    max_input_length,\n                                        const int    batch_size,\n                                        const int    hidden_units,\n                                        cudaStream_t stream)\n{\n    const int grid_size = (int)(ceil(batch_size * hidden_units / 1024.));\n    dim3      grid(min(grid_size, 65536));\n    dim3      block(min(hidden_units, 1024));\n    lookupHiddenStateOfLastToken<T><<<grid, block, 0, stream>>>(\n        from_tensor, hidden_state, input_lengths, max_input_length, batch_size, hidden_units);\n}\n\ntemplate void invokeLookupHiddenStateOfLastToken(float*       from_tensor,\n                                                 const float* hidden_state,\n                                                 const int*   input_lengths,\n                                                 const int    max_input_length,\n                                                 const int    batch_size,\n                                                 const int    hidden_units,\n                                                 cudaStream_t stream);\n\ntemplate void invokeLookupHiddenStateOfLastToken(half*        from_tensor,\n                                                 const half*  hidden_state,\n                                                 const int*   input_lengths,\n                                                 const int    max_input_length,\n                                                 const int    batch_size,\n                                                 const int    hidden_units,\n                                                 cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLookupHiddenStateOfLastToken(__nv_bfloat16*       from_tensor,\n                                                 const __nv_bfloat16* hidden_state,\n                                                 const int*           input_lengths,\n                                                 const int            max_input_length,\n                                                 const int            batch_size,\n                                                 const int            hidden_units,\n                                                 cudaStream_t         stream);\n#endif\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void tileGptPromptInputs(int*       tiled_input_ids,\n                                    int*       tiled_input_lengths,\n                                    int*       tiled_prompt_lengths,\n                                    const int* input_ids,\n                                    const int* input_lengths,\n                                    const int* prefix_prompt_lengths,\n                                    const int  max_input_length)\n{\n    if (threadIdx.x == 0) {\n        tiled_input_lengths[blockIdx.x * gridDim.y + blockIdx.y] = input_lengths[blockIdx.x];\n        if (PREFIX_PROMPT) {\n            tiled_prompt_lengths[blockIdx.x * gridDim.y + blockIdx.y] = prefix_prompt_lengths[blockIdx.x];\n        }\n    }\n    for (int index = threadIdx.x; index < max_input_length; index += blockDim.x) {\n        tiled_input_ids[(blockIdx.x * gridDim.y + blockIdx.y) * max_input_length + index] =\n            input_ids[blockIdx.x * max_input_length + index];\n    }\n}\n\nvoid invokeTileGptPromptInputs(int*         tiled_input_ids,\n                               int*         tiled_input_lengths,\n                               int*         tiled_prompt_lengths,\n                               const int*   input_ids,\n                               const int*   input_lengths,\n                               const int*   prefix_prompt_lengths,\n                               const int    batch_size,\n                               const int    beam_width,\n                               const int    max_input_length,\n                               cudaStream_t stream)\n{\n    dim3 grid(batch_size, beam_width);\n    dim3 block(min(1024, max_input_length));\n    if (prefix_prompt_lengths != nullptr) {\n        tileGptPromptInputs<true><<<grid, block, 0, stream>>>(tiled_input_ids,\n                                                              tiled_input_lengths,\n                                                              tiled_prompt_lengths,\n                                                              input_ids,\n                                                              input_lengths,\n                                                              prefix_prompt_lengths,\n                                                              max_input_length);\n    }\n    else {\n        tileGptPromptInputs<false><<<grid, block, 0, stream>>>(tiled_input_ids,\n                                                               tiled_input_lengths,\n                                                               tiled_prompt_lengths,\n                                                               input_ids,\n                                                               input_lengths,\n                                                               prefix_prompt_lengths,\n                                                               max_input_length);\n    }\n}\n\nvoid invokeTileGptInputs(int*         tiled_input_ids,\n                         int*         tiled_input_lengths,\n                         const int*   input_ids,\n                         const int*   input_lengths,\n                         const int    batch_size,\n                         const int    beam_width,\n                         const int    max_input_length,\n                         cudaStream_t stream)\n{\n    invokeTileGptPromptInputs(tiled_input_ids,\n                              tiled_input_lengths,\n                              nullptr,\n                              input_ids,\n                              input_lengths,\n                              nullptr,\n                              batch_size,\n                              beam_width,\n                              max_input_length,\n                              stream);\n}\n\nvoid setSeqLimitLen(uint32_t* seq_len_d, Tensor seq_len, int limit_len_offset, int batch_size)\n{\n    std::vector<uint32_t> seq_len_h(batch_size);\n    for (int i = 0; i < batch_size; i++) {\n        seq_len_h[i] = seq_len.getPtr<uint32_t>()[i] + limit_len_offset;\n    }\n    cudaH2Dcpy(seq_len_d, seq_len_h.data(), batch_size);\n}\n\ntemplate<int TB_SIZE>\n__global__ void\nfind_context_dups(int* shared_contexts, const int* input_ids, const size_t batch_size, const size_t input_seq_len)\n{\n    /* We compare all context pairs (i, j), with i (tgt) < j (src) , to detect duplicate\n     * inputs. If there's a match between i and j, we store i at the\n     * j-th position of shared_context. So that we know that j can be\n     * represented by i. shared_contexts is initialized like shared_contexts[i] = i\n     * and when there's a match, we actually use shared_contexts[j] = min(shared_contexts[j], i)\n     * so that in the end, shared_contexts effectively contains an index\n     * to the match with the lowest index context.\n     * Note that shared_contexts[i] <= i, a property that will be used when uncompacting\n     * inputs.\n     */\n    typedef cub::BlockReduce<int, TB_SIZE>       BlockReduce;\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ bool                              match;\n\n    /* Each block is responsible for a (i, j) pair. To map the block space to\n     * the i < j space, we need to convert a linear addressing to a triangle, of\n     * size (batch_size * (batch_size - 1)) / 2\n     * For more information, check https://en.wikipedia.org/wiki/Triangular_number\n     */\n\n    // blockIdx = [0, 1, 2, ... n(n-1)/2] -> base_index = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, ..., n - 2]\n    const int base_index = floorf(0.5f * (sqrtf(1 + 8 * blockIdx.x) - 1));\n    const int src_idx    = base_index + 1;  // base_index \\in [1, batch_size)\n\n    const int rev_base_index = base_index * (base_index + 1) / 2;\n    const int tgt_idx        = blockIdx.x - rev_base_index;  // tgt_idx \\in [0, src_idx)\n\n    const int padded_length = TB_SIZE * ((input_seq_len + TB_SIZE - 1) / TB_SIZE);\n\n    int sum = 0;\n    for (int i = threadIdx.x; i < padded_length; i += TB_SIZE) {\n        int compare =\n            (i >= input_seq_len) ? 1 : input_ids[tgt_idx * input_seq_len + i] == input_ids[src_idx * input_seq_len + i];\n\n        sum = BlockReduce(temp_storage).Sum(compare);\n\n        if (threadIdx.x == 0) {\n            match = (sum == TB_SIZE);\n        }\n\n        __syncthreads();\n\n        if (!match) {\n            break;\n        }\n    }\n\n    if (threadIdx.x == 0 && match) {\n        atomicMin(&shared_contexts[src_idx], tgt_idx);\n    }\n}\n\nconstexpr int DUPS_INDICES_BLOCK_SIZE = 128;\n\n__global__ void generate_dups_indices(int*         batch_to_compact,\n                                      int*         compact_to_batch,\n                                      int*         compact_size,\n                                      const int*   shared_contexts,\n                                      const size_t batch_size,\n                                      const size_t beam_width,\n                                      const size_t input_seq_len)\n{\n    const int padded_batchsize = blockDim.x * ((batch_size + blockDim.x - 1) / blockDim.x);\n\n    typedef cub::BlockScan<int, DUPS_INDICES_BLOCK_SIZE, cub::BLOCK_SCAN_WARP_SCANS> BlockScan;\n    __shared__ typename BlockScan::TempStorage                                       temp_storage;\n    __shared__ int                                                                   scan_offset;\n\n    int scan = 0;\n    for (int seq_idx = threadIdx.x; seq_idx < padded_batchsize; seq_idx += blockDim.x) {\n        bool masked     = (seq_idx >= batch_size);\n        bool first_iter = seq_idx < blockDim.x;\n\n        int is_first_occur = masked ? 0 : shared_contexts[seq_idx] == seq_idx;\n        BlockScan(temp_storage).ExclusiveSum(is_first_occur, scan);\n\n        if (!masked && is_first_occur) {\n            int compact_idx = scan + (first_iter ? 0 : scan_offset);\n            // Context rep. writes initial index\n            batch_to_compact[seq_idx * beam_width] = compact_idx;\n            // input ids are tiled in context part\n            compact_to_batch[compact_idx] = seq_idx * beam_width;\n        }\n\n        __syncthreads();\n\n        if (threadIdx.x == blockDim.x - 1) {\n            scan_offset = scan + is_first_occur + (first_iter ? 0 : scan_offset);\n        }\n\n        __syncthreads();\n\n        if (!masked && !is_first_occur) {\n            // Fill the rest of batch_to_compact based on what rep. wrote\n            const int src_idx                      = batch_to_compact[shared_contexts[seq_idx] * beam_width];\n            batch_to_compact[seq_idx * beam_width] = src_idx;\n        }\n\n        if (!masked) {\n            // set same compact idx for beams\n            for (int beam_id = 1; beam_id < beam_width; ++beam_id) {\n                batch_to_compact[seq_idx * beam_width + beam_id] = batch_to_compact[seq_idx * beam_width];\n            }\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        *compact_size = scan_offset;\n    }\n}\n\n__global__ void init_shared_contexts(int* shared_contexts, const size_t batch_size)\n{\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (global_idx >= batch_size) {\n        return;\n    }\n    shared_contexts[global_idx] = global_idx;\n}\n\nvoid invokeFindContextDups(int*         shared_contexts,\n                           int*         batch_to_compact,\n                           int*         compact_to_batch,\n                           int*         compact_size,\n                           const int*   input_ids,\n                           const size_t batch_size,\n                           const size_t beam_width,\n                           const size_t input_seq_len,\n                           cudaStream_t stream)\n{\n    dim3 block{512};\n    dim3 grid{((int)batch_size + block.x - 1) / block.x};\n    // set shared_context[i]\u00a0= i\n    init_shared_contexts<<<grid, block, 0, stream>>>(shared_contexts, batch_size);\n\n    grid = dim3{(unsigned int)(batch_size * (batch_size - 1)) / 2};\n    // set shared_contexts[i] = j, where j = min{k, such that input_ids[k] == input_ids[i]}\n    if (input_seq_len <= 128) {\n        block = 128;\n        find_context_dups<128><<<grid, block, 0, stream>>>(shared_contexts, input_ids, batch_size, input_seq_len);\n    }\n    else {\n        block = 256;\n        find_context_dups<256><<<grid, block, 0, stream>>>(shared_contexts, input_ids, batch_size, input_seq_len);\n    }\n\n    // set batch_to_compact[i] = j, where j is the position of input_ids[i] in the compact_batch\n    // set compact_to_batch[i] = j, where j is such that compact_to_batch[i] = input_ids[j]\n    generate_dups_indices<<<1, DUPS_INDICES_BLOCK_SIZE, 0, stream>>>(\n        batch_to_compact, compact_to_batch, compact_size, shared_contexts, batch_size, beam_width, input_seq_len);\n}\n\ntemplate<typename T>\n__global__ void compact_inputs(T*         compact_input,\n                               T*         compact_attention_mask,\n                               int*       compact_input_lengths,\n                               const T*   decoder_input,\n                               const T*   decoder_mask,\n                               const int* input_lengths,\n                               const int* compact_idx,\n                               size_t     compact_size,\n                               size_t     seq_len,\n                               size_t     hidden_dimension)\n{\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (global_idx < compact_size * seq_len * hidden_dimension) {\n        const int h_id     = global_idx % hidden_dimension;\n        const int seq_id   = (global_idx / hidden_dimension) % seq_len;\n        const int batch_id = global_idx / (hidden_dimension * seq_len);\n\n        compact_input[global_idx] = decoder_input[(compact_idx[batch_id] * seq_len + seq_id) * hidden_dimension + h_id];\n    }\n\n    if (global_idx < compact_size * seq_len * seq_len) {\n        const int seq1_id  = global_idx % seq_len;\n        const int seq2_id  = (global_idx / seq_len) % seq_len;\n        const int batch_id = global_idx / (seq_len * seq_len);\n\n        compact_attention_mask[global_idx] =\n            decoder_mask[(compact_idx[batch_id] * seq_len + seq2_id) * seq_len + seq1_id];\n    }\n\n    if (global_idx < compact_size) {\n        compact_input_lengths[global_idx] = input_lengths[compact_idx[global_idx]];\n    }\n}\n\ntemplate<typename T>\nvoid invokeCompactInputs(T*           compact_input,\n                         T*           compact_attention_mask,\n                         int*         compact_input_lengths,\n                         const T*     decoder_input,\n                         const T*     decoder_mask,\n                         const int*   input_lengths,\n                         const int*   compact_idx,\n                         size_t       compact_size,\n                         size_t       seq_len,\n                         size_t       hidden_dimension,\n                         cudaStream_t stream)\n{\n    /* Compact relevant decoder_layer inputs based on the identical contexts.\n     * For example, decoder_input is [batch_size, seq_len, H]. It's compacted\n     * into compact_input [compact_size, seq_len, H] such that\n     * compact_input[i, ...] = decoder_input[compact_idx[i], ...] */\n    const size_t elems_n = compact_size * seq_len * max(hidden_dimension, seq_len);\n    const dim3   blockDim(512);\n    const dim3   gridDim((elems_n + 512 - 1) / 512);\n\n    compact_inputs<T><<<gridDim, blockDim, 0, stream>>>(compact_input,\n                                                        compact_attention_mask,\n                                                        compact_input_lengths,\n                                                        decoder_input,\n                                                        decoder_mask,\n                                                        input_lengths,\n                                                        compact_idx,\n                                                        compact_size,\n                                                        seq_len,\n                                                        hidden_dimension);\n}\n\n#define INSTANTIATE_INVOKE_COMPACT_INPUTS(T)                                                                           \\\n    template void invokeCompactInputs<T>(T * compact_input,                                                            \\\n                                         T * compact_attention_mask,                                                   \\\n                                         int*         compact_input_lengths,                                           \\\n                                         const T*     decoder_input,                                                   \\\n                                         const T*     decoder_mask,                                                    \\\n                                         const int*   input_lengths,                                                   \\\n                                         const int*   compact_idx,                                                     \\\n                                         size_t       compact_size,                                                    \\\n                                         size_t       seq_len,                                                         \\\n                                         size_t       hidden_dimension,                                                \\\n                                         cudaStream_t stream)\nINSTANTIATE_INVOKE_COMPACT_INPUTS(half);\nINSTANTIATE_INVOKE_COMPACT_INPUTS(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_COMPACT_INPUTS(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_COMPACT_INPUTS\n\ntemplate<typename T>\n__global__ void uncompact_outputs(T*         uncompact_buffer,\n                                  const T*   compact_buffer,\n                                  const int* batch_to_compact_idx,\n                                  size_t     batch_size,\n                                  size_t     buffer_stride)\n{\n    /* Uncompact a buffer IN of size [Compact, Stride] into OUT of size [Batch, Stride]\n     * so that \\forall i, OUT[i, :] = IN[batch_to_compact_idx[i], :]\n     */\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (global_idx >= batch_size * buffer_stride) {\n        return;\n    }\n\n    const int stride_idx = global_idx % buffer_stride;\n    const int batch_idx  = global_idx / buffer_stride;\n\n    const int src                = batch_to_compact_idx[batch_idx];\n    uncompact_buffer[global_idx] = compact_buffer[src * buffer_stride + stride_idx];\n}\n\ntemplate<typename T>\nvoid invokeUnCompactOutputs(T*           uncompact_buffer,\n                            const T*     compact_buffer,\n                            const int*   batch_to_compact_idx,\n                            size_t       batch_size,\n                            size_t       buffer_stride,\n                            cudaStream_t stream)\n{\n    const size_t num_elems = batch_size * buffer_stride;\n    const dim3   blockDim(1024);\n    const dim3   gridDim((num_elems + blockDim.x - 1) / blockDim.x);\n\n    uncompact_outputs<T><<<gridDim, blockDim, 0, stream>>>(\n        uncompact_buffer, compact_buffer, batch_to_compact_idx, batch_size, buffer_stride);\n}\n\n#define INSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(T)                                                                        \\\n    template void invokeUnCompactOutputs(T*           uncompact_buffer,                                                \\\n                                         const T*     compact_buffer,                                                  \\\n                                         const int*   batch_to_compact_idx,                                            \\\n                                         size_t       batch_size,                                                      \\\n                                         size_t       buffer_stride,                                                   \\\n                                         cudaStream_t stream)\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(half);\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS\n\ntemplate<typename T>\n__global__ void uncompact_caches(T*         uncompact_k_cache,\n                                 T*         uncompact_v_cache,\n                                 const T*   compact_k_cache,\n                                 const T*   compact_v_cache,\n                                 const int* batch_to_compact_idx,\n                                 size_t     batch_size,\n                                 size_t     num_heads,\n                                 size_t     max_seq_len,\n                                 size_t     seq_len,\n                                 size_t     size_per_head,\n                                 size_t     local_batch_size,\n                                 size_t     ite)\n{\n    const int hidden_dimension    = num_heads * size_per_head;\n    const int num_elems_per_batch = seq_len * hidden_dimension;\n    const int num_elems_cache     = batch_size * num_elems_per_batch;\n    const int x_size              = 16 / sizeof(T);\n\n    for (int global_idx = blockIdx.x * blockDim.x + threadIdx.x; global_idx < 2 * num_elems_cache;\n         global_idx += blockDim.x * gridDim.x) {\n\n        const bool     handle_k  = global_idx < num_elems_cache;\n        const T* const cache_src = handle_k ? compact_k_cache : compact_v_cache;\n        T* const       cache_dst = handle_k ? uncompact_k_cache : uncompact_v_cache;\n        const int      idx       = handle_k ? global_idx : global_idx - num_elems_cache;\n\n        const int src_offset = idx % num_elems_per_batch;\n        const int batch_idx  = idx / num_elems_per_batch;\n        const int batch_src  = batch_to_compact_idx[batch_idx] - ite * local_batch_size;\n\n        if (batch_src < 0 || batch_src >= local_batch_size) {\n            continue;\n        }\n\n        int dst_offset;\n        if (handle_k) {\n            const int i0 = idx % (x_size * seq_len);\n            const int i1 = (idx / (x_size * seq_len)) % (num_heads * size_per_head / x_size);\n            dst_offset   = i1 * max_seq_len * x_size + i0;\n        }\n        else {\n            const int i0 = idx % (size_per_head * seq_len);\n            const int i1 = (idx / (size_per_head * seq_len)) % (num_heads);\n            dst_offset   = i1 * max_seq_len * size_per_head + i0;\n        }\n\n        cache_dst[batch_idx * max_seq_len * hidden_dimension + dst_offset] =\n            cache_src[batch_src * num_elems_per_batch + src_offset];\n    }\n}\n\ntemplate<typename T>\nvoid invokeUnCompactCaches(T*           uncompact_k_cache,\n                           T*           uncompact_v_cache,\n                           const T*     compact_k_cache,\n                           const T*     compact_v_cache,\n                           const int*   batch_to_compact_idx,\n                           size_t       batch_size,\n                           size_t       num_heads,\n                           size_t       max_seq_len,\n                           size_t       seq_len,\n                           size_t       size_per_head,\n                           size_t       local_batch_size,\n                           size_t       ite,\n                           cudaStream_t stream)\n{\n    const dim3 blockDim(512);\n    const dim3 gridDim(1024);\n    uncompact_caches<T><<<gridDim, blockDim, 0, stream>>>(uncompact_k_cache,\n                                                          uncompact_v_cache,\n                                                          compact_k_cache,\n                                                          compact_v_cache,\n                                                          batch_to_compact_idx,\n                                                          batch_size,\n                                                          num_heads,\n                                                          max_seq_len,\n                                                          seq_len,\n                                                          size_per_head,\n                                                          local_batch_size,\n                                                          ite);\n}\n\n#define INSTANTIATE_INVOKE_UNCOMPACT_CACHES(T)                                                                         \\\n    template void invokeUnCompactCaches(T*           uncompact_k_cache,                                                \\\n                                        T*           uncompact_v_cache,                                                \\\n                                        const T*     compact_k_cache,                                                  \\\n                                        const T*     compact_v_cache,                                                  \\\n                                        const int*   batch_to_compact_idx,                                             \\\n                                        size_t       batch_size,                                                       \\\n                                        size_t       num_heads,                                                        \\\n                                        size_t       max_seq_len,                                                      \\\n                                        size_t       seq_len,                                                          \\\n                                        size_t       size_per_head,                                                    \\\n                                        size_t       local_batch_size,                                                 \\\n                                        size_t       ite,                                                              \\\n                                        cudaStream_t stream)\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(half);\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_UNCOMPACT_CACHES\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void update_padding_count(int*       total_padding_count,\n                                     const int* input_lengths,\n                                     const int* tiled_prompt_lengths,\n                                     size_t     max_input_length,\n                                     size_t     max_prompt_length,\n                                     size_t     batch_size,\n                                     size_t     beam_width)\n{\n    const int gidx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (gidx >= batch_size * beam_width) {\n        return;\n    }\n\n    const int batch_idx = gidx / beam_width;\n\n    total_padding_count[gidx] +=\n        PREFIX_PROMPT ? (max_input_length + max_prompt_length - input_lengths[batch_idx] - tiled_prompt_lengths[gidx]) :\n                        (max_input_length - input_lengths[batch_idx]);\n}\n\nvoid invokeUpdatePaddingCount(int*         total_padding_count,\n                              const int*   input_lengths,\n                              const int*   tiled_prompt_lengths,\n                              size_t       max_input_length,\n                              size_t       max_prompt_length,\n                              size_t       batch_size,\n                              size_t       beam_width,\n                              cudaStream_t stream)\n{\n    dim3 blockSize(256);\n    dim3 gridSize((batch_size * beam_width + blockSize.x - 1) / blockSize.x);\n\n    if (tiled_prompt_lengths != nullptr) {\n        update_padding_count<true><<<gridSize, blockSize, 0, stream>>>(total_padding_count,\n                                                                       input_lengths,\n                                                                       tiled_prompt_lengths,\n                                                                       max_input_length,\n                                                                       max_prompt_length,\n                                                                       batch_size,\n                                                                       beam_width);\n    }\n    else {\n        update_padding_count<false><<<gridSize, blockSize, 0, stream>>>(total_padding_count,\n                                                                        input_lengths,\n                                                                        tiled_prompt_lengths,\n                                                                        max_input_length,\n                                                                        max_prompt_length,\n                                                                        batch_size,\n                                                                        beam_width);\n    }\n}\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void mask_padding_tokens(bool*        masked_tokens,\n                                    const int*   input_lengths,\n                                    const int*   tiled_prefix_prompt_lengths,\n                                    const size_t memory_len,\n                                    const size_t max_input_length,\n                                    const size_t initial_step,\n                                    size_t       beam_width)\n{\n    const int seq_len = PREFIX_PROMPT ?\n                            (input_lengths[blockIdx.x / beam_width] + tiled_prefix_prompt_lengths[blockIdx.x]) :\n                            input_lengths[blockIdx.x / beam_width];\n    for (int step = initial_step + seq_len + threadIdx.x; step < initial_step + max_input_length; step += blockDim.x) {\n        masked_tokens[blockIdx.x * memory_len + step % memory_len] = true;\n    }\n}\n\nvoid invokeMaskPaddingTokens(bool*        masked_tokens,\n                             const int*   input_lengths,\n                             const int*   tiled_prefix_prompt_lengths,\n                             const size_t memory_len,\n                             const size_t max_input_length,\n                             const size_t initial_step,\n                             size_t       batch_size,\n                             size_t       beam_width,\n                             cudaStream_t stream)\n{\n    dim3 blockSize(128);\n    dim3 gridSize(batch_size * beam_width);\n    if (tiled_prefix_prompt_lengths != nullptr) {\n        mask_padding_tokens<true><<<gridSize, blockSize, 0, stream>>>(masked_tokens,\n                                                                      input_lengths,\n                                                                      tiled_prefix_prompt_lengths,\n                                                                      memory_len,\n                                                                      max_input_length,\n                                                                      initial_step,\n                                                                      beam_width);\n    }\n    else {\n        mask_padding_tokens<false><<<gridSize, blockSize, 0, stream>>>(masked_tokens,\n                                                                       input_lengths,\n                                                                       tiled_prefix_prompt_lengths,\n                                                                       memory_len,\n                                                                       max_input_length,\n                                                                       initial_step,\n                                                                       beam_width);\n    }\n}\n\ntemplate<typename T>\n__global__ void sum_length_dimension(\n    float* out_buf, const T* in_buf, const size_t batch_size, const size_t input_length, const size_t hidden_dim)\n{\n    const int bidx = blockIdx.x;\n\n    for (int hidx = threadIdx.x; hidx < hidden_dim; hidx += blockDim.x) {\n        float accum = 0.0f;\n        for (int step = 0; step < input_length; step++) {\n            accum += static_cast<float>(in_buf[(bidx * input_length + step) * hidden_dim + hidx]);\n        }\n        out_buf[bidx * hidden_dim + hidx] = accum;\n    }\n}\n\ntemplate<typename T>\nvoid invokeSumLengthDimension(float*       out_buf,\n                              const T*     in_buf,\n                              const size_t batch_size,\n                              const size_t input_length,\n                              const size_t hidden_dim,\n                              cudaStream_t stream)\n{\n    dim3 gridSize(batch_size);\n    dim3 blockSize(256);\n\n    sum_length_dimension<<<gridSize, blockSize, 0, stream>>>(out_buf, in_buf, batch_size, input_length, hidden_dim);\n}\n\n#define INSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(T)                                                                     \\\n    template void invokeSumLengthDimension(float*       out_buf,                                                       \\\n                                           const T*     in_buf,                                                        \\\n                                           const size_t batch_size,                                                    \\\n                                           const size_t input_length,                                                  \\\n                                           const size_t hidden_dim,                                                    \\\n                                           cudaStream_t stream)\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(half);\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/utils/cuda_fp8_utils.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n#include \"src/fastertransformer/kernels/gpt_kernels.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\nnamespace fastertransformer {\n\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T, bool OUTPUT_ID, int PROMPT_SRC>\n__global__ void start_id_embedding_position_lookups_kernel(T*                    from_tensor,\n                                                           int*                  output_ids,\n                                                           const T*              embedding_table,\n                                                           const T*              pos_table,\n                                                           pPromptTuningParam<T> prompt_param,\n                                                           const int*            input_ids,\n                                                           const int             start_step,\n                                                           const int             length,\n                                                           const int             max_length,\n                                                           const int             batch_size,\n                                                           const int64_t         hidden_units)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * length * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        // transpose the input_ids [batch, length] (part of [batch, max_length]) to output_ids [length, batch]\n        if (OUTPUT_ID && index < batch_size * max_length) {\n            // for p/prompt_tuning (have prompt templates like [input1, prompt1, input2, prompt2])\n            // we have to process it to like [input1, input2, prompt1, prompt2], and then remove the prompts during post\n            // processing\n            if (PROMPT_SRC > 0) {\n                if (index < batch_size) {\n                    int no_prompt_output_seq_id = 0;\n#pragma unroll 1\n                    for (int seq_id = 0; seq_id < max_length; seq_id++) {\n                        int current_input_id = input_ids[index * max_length + seq_id];\n                        if (current_input_id < prompt_param.p_prompt_tuning_id_start) {\n                            output_ids[no_prompt_output_seq_id * batch_size + index] = current_input_id;\n                            no_prompt_output_seq_id++;\n                        }\n                    }\n                }\n            }\n            else {\n                const int seq_id   = index % max_length;\n                const int batch_id = index / max_length;\n                if (seq_id < length) {\n                    output_ids[seq_id * batch_size + batch_id] = input_ids[index];\n                }\n            }\n        }\n\n        // embedding lookup from word ids [batch, length] (part of [batch, max_length]) and [vocab, hidden] to generate\n        // embedding [batch, length, hidden]\n        const int word_index      = index / hidden_units;\n        const int word_index_row  = word_index / length;  // batch_id\n        const int word_index_col  = word_index % length;\n        const int real_word_index = word_index_row * max_length + word_index_col;\n        const int step            = start_step + word_index % length;\n        const int col_index       = index % hidden_units;\n        const int input_id        = input_ids == nullptr ? real_word_index : input_ids[real_word_index];\n        const int prompt_id       = input_id - prompt_param.p_prompt_tuning_id_start;\n        T         embedding       = (T)0.0f;\n        if (PROMPT_SRC > 0 && prompt_id >= 0) {\n            if (PROMPT_SRC == 1) {\n                // from loaded prompt embedding tables\n                embedding =\n                    prompt_param.p_prompt_tuning_batch_weights[word_index_row][prompt_id * hidden_units + col_index];\n            }\n            else {\n                // from request prompt embedding\n                embedding =\n                    prompt_param\n                        .request_prompt_embedding[word_index_row * prompt_param.request_prompt_max_length * hidden_units\n                                                  + prompt_id * hidden_units + col_index];\n            }\n        }\n        else {\n            embedding = embedding_table[input_id * hidden_units + col_index];\n        }\n        T pos_embed        = pos_table == nullptr ? (T)0.f : pos_table[(step - 1) * hidden_units + col_index];\n        from_tensor[index] = embedding + pos_embed;\n    }\n}\n\n#define WORD_POS_EMBEDDING_LOOPUP_KERNEL(OUTPUT_ID, PROMPT_SRC)                                                        \\\n    start_id_embedding_position_lookups_kernel<T, OUTPUT_ID, PROMPT_SRC><<<grid, block, 0, stream>>>(from_tensor,      \\\n                                                                                                     output_ids,       \\\n                                                                                                     embedding_table,  \\\n                                                                                                     pos_table,        \\\n                                                                                                     prompt_param,     \\\n                                                                                                     input_ids,        \\\n                                                                                                     start_step,       \\\n                                                                                                     length,           \\\n                                                                                                     max_length,       \\\n                                                                                                     batch_size,       \\\n                                                                                                     hidden_units);\n\ntemplate<typename T>\nvoid invokeInputIdsEmbeddingLookupPosEncoding(T*                    from_tensor,\n                                              int*                  output_ids,\n                                              const T*              embedding_table,  // can also be inputs_embeds\n                                              const T*              pos_table,\n                                              pPromptTuningParam<T> prompt_param,\n                                              const int*            input_ids,\n                                              const int             start_step,\n                                              const int             length,\n                                              const int             max_length,\n                                              const int             batch_size,\n                                              const int             hidden_units,\n                                              hipStream_t          stream)\n{\n    dim3       grid(min(batch_size * length, 65536));\n    dim3       block(min(hidden_units, 512));\n    const bool has_output_ids = output_ids != nullptr;\n    FT_CHECK(!(has_output_ids && input_ids == nullptr));\n\n    if (has_output_ids) {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 1);\n        }\n        else {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(true, 0);\n        }\n    }\n    else {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 1);\n        }\n        else {\n            WORD_POS_EMBEDDING_LOOPUP_KERNEL(false, 0);\n        }\n    }\n}\n\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(float*                    from_tensor,\n                                                       int*                      output_ids,\n                                                       const float*              embedding_table,\n                                                       const float*              pos_table,\n                                                       pPromptTuningParam<float> prompt_param,\n                                                       const int*                input_ids,\n                                                       const int                 start_step,\n                                                       const int                 length,\n                                                       const int                 max_length,\n                                                       const int                 batch_size,\n                                                       const int                 hidden_units,\n                                                       hipStream_t              stream);\n\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(half*                    from_tensor,\n                                                       int*                     output_ids,\n                                                       const half*              embedding_table,\n                                                       const half*              pos_table,\n                                                       pPromptTuningParam<half> prompt_param,\n                                                       const int*               input_ids,\n                                                       const int                start_step,\n                                                       const int                length,\n                                                       const int                max_length,\n                                                       const int                batch_size,\n                                                       const int                hidden_units,\n                                                       hipStream_t             stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInputIdsEmbeddingLookupPosEncoding(__hip_bfloat16*                    from_tensor,\n                                                       int*                              output_ids,\n                                                       const __hip_bfloat16*              embedding_table,\n                                                       const __hip_bfloat16*              pos_table,\n                                                       pPromptTuningParam<__hip_bfloat16> prompt_param,\n                                                       const int*                        input_ids,\n                                                       const int                         start_step,\n                                                       const int                         length,\n                                                       const int                         max_length,\n                                                       const int                         batch_size,\n                                                       const int                         hidden_units,\n                                                       hipStream_t                      stream);\n#endif\n\ntemplate<typename T>\n__global__ void inputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<T> param)\n{\n    // 1. Copy the input ids to output ids and transpose output ids to [seq_len, batch_size, beam_width].\n    // 2. Embedding lookup by input ids and concat with soft prompt. The axis of concatenation is on axis of seq_len.\n\n    // Assume batch size is 2 and prompts are [[t1, t2], [t3], [t4, t5]], input_ids are [[s1, s2], [s3], [s4]]\n    // then the order of output_ids is\n    // [ [?, ?, s1, s2]\n    //   [?, s3, padding, padding]\n    //   [?, ?, s4, padding] ]\n    // and the order of embedding is\n    // [ [t1, t2, s1, s2]\n    //   [t3, s3, padding, padding]\n    //   [t4, t5, s4, padding] ]\n    // where \"?\" means undefined values and we should attach it.\n\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x;\n         index < param.batch_size * param.beam_width * (param.max_prefix_soft_prompt_length + param.max_input_length)\n                     * param.hidden_units;\n         index += blockDim.x * gridDim.x) {\n        // transpose the input_ids [batch, length] (part of [batch, beam, max_input_length]) to\n        // output_ids [length, batch, beam].\n        // ouptut_ids need to add padding in the beginning for soft prompting.\n\n        if (index < param.batch_size * param.beam_width * param.max_input_length) {\n            int       tmp_index = index;\n            const int seq_id    = tmp_index % param.max_input_length;\n            tmp_index           = (tmp_index - seq_id) / param.max_input_length;\n            const int beam_id   = tmp_index % param.beam_width;\n            tmp_index           = (tmp_index - beam_id) / param.beam_width;\n            const int batch_id  = tmp_index % param.batch_size;\n            if (seq_id < param.max_input_length) {\n                param.output_ids[(param.prefix_soft_prompt_lengths[batch_id] + seq_id) * param.batch_size\n                                     * param.beam_width\n                                 + batch_id * param.beam_width + beam_id] = param.input_ids[index];\n            }\n        }\n\n        // embedding lookup from word ids [batch, beam, length] (part of [batch, beam, max_input_length]), [vocab,\n        // hidden] and [batch, max_prefix_soft_prompt_length, hidden] to generate embedding [batch, beam, length +\n        // max_prefix_soft_prompt_length, hidden]\n        int       tmp_index = index;\n        const int hidden_id = tmp_index % param.hidden_units;\n        tmp_index           = (tmp_index - hidden_id) / param.hidden_units;\n        const int seq_id    = tmp_index % (param.max_prefix_soft_prompt_length + param.max_input_length);\n        tmp_index           = (tmp_index - seq_id) / (param.max_prefix_soft_prompt_length + param.max_input_length);\n        const int beam_id   = tmp_index % param.beam_width;\n        tmp_index           = (tmp_index - beam_id) / param.beam_width;\n        const int batch_id  = tmp_index % param.batch_size;\n        const int64_t hidden_units = param.hidden_units;\n        T         embedding =\n            (seq_id < param.prefix_soft_prompt_lengths[batch_id]) ?\n                        (T)param.prefix_soft_prompt_embedding[batch_id * param.max_prefix_soft_prompt_length * hidden_units\n                                                      + seq_id * hidden_units + hidden_id] :\n                            param.embedding_table[param.input_ids[batch_id * param.beam_width * param.max_input_length\n                                                      + beam_id * param.max_input_length\n                                                      + (seq_id - param.prefix_soft_prompt_lengths[batch_id])]\n                                          * hidden_units\n                                      + hidden_id];\n\n        T pos_embed              = param.pos_table == nullptr ?\n                                       (T)0.0f :\n                                       param.pos_table[(param.start_step + seq_id - 1) * hidden_units + hidden_id];\n        param.from_tensor[index] = embedding + pos_embed;\n\n        if (seq_id == 0 && hidden_id == 0) {\n            param.input_lengths[batch_id * param.beam_width + beam_id] += param.prefix_soft_prompt_lengths[batch_id];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<T> param)\n{\n    dim3 grid(min(param.batch_size * param.beam_width * (param.max_input_length + param.max_prefix_soft_prompt_length),\n                  65536));\n    dim3 block(min(param.hidden_units, 512));\n    inputIdsEmbeddingLookupPosEncodingSoftPrompt<T><<<grid, block, 0, param.stream>>>(param);\n}\n\ntemplate void\ninvokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<float> param);\n\ntemplate void\ninvokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(inputIdsEmbeddingLookupPosEncodingSoftPromptParam<half> param);\n\n#ifdef ENABLE_BF16\ntemplate void invokeInputIdsEmbeddingLookupPosEncodingSoftPrompt(\n    inputIdsEmbeddingLookupPosEncodingSoftPromptParam<__hip_bfloat16> param);\n#endif\n\n// TODO Add half2 implementation\ntemplate<typename T>\n__global__ void transposeAxis01(T* out, T* in, const int dim0, const int dim1, const int dim2)\n{\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < dim0 * dim1 * dim2) {\n        const int input_dim2_index = index % dim2;\n        index                      = (index - input_dim2_index) / dim2;\n        const int input_dim1_index = index % dim1;\n        index                      = (index - input_dim1_index) / dim1;\n        const int input_dim0_index = index % dim0;\n\n        out[input_dim1_index * dim0 * dim2 + input_dim0_index * dim2 + input_dim2_index] =\n            in[input_dim0_index * dim1 * dim2 + input_dim1_index * dim2 + input_dim2_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAxis01(T* out, T* in, const int dim0, const int dim1, const int dim2, hipStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(dim0 * dim1 * dim2 / 512.)));\n    transposeAxis01<<<grid, block, 0, stream>>>(out, in, dim0, dim1, dim2);\n}\n\ntemplate void\ninvokeTransposeAxis01(float* out, float* in, const int dim0, const int dim1, const int dim2, hipStream_t stream);\n\ntemplate void\ninvokeTransposeAxis01(half* out, half* in, const int dim0, const int dim1, const int dim2, hipStream_t stream);\n\ntemplate void\ninvokeTransposeAxis01(int* out, int* in, const int dim0, const int dim1, const int dim2, hipStream_t stream);\n\ntemplate<typename T>\n__global__ void transposeAxis01(T* out, T* in, const int* in_skipping_dim1, const int dim0, const int dim1)\n{\n    // out: [dim1, dim0]\n    // in: [dim0, dim1]\n    // in_skipping_dim1: [dim1]\n\n    int index = threadIdx.x + blockIdx.x * blockDim.x;\n    if (index < dim0 * dim1) {\n        const int input_dim1_index = index % dim1;\n        index                      = (index - input_dim1_index) / dim1;\n        const int input_dim0_index = index % dim0;\n        const int in_offset        = in_skipping_dim1 == nullptr ? 0 : in_skipping_dim1[input_dim1_index] * dim1;\n\n        out[input_dim1_index * dim0 + input_dim0_index] = in[in_offset + input_dim0_index * dim1 + input_dim1_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAxis01(\n    T* out, T* in, const int* in_skipping_dim1, const int dim0, const int dim1, hipStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(dim0 * dim1 / 512.)));\n    transposeAxis01<<<grid, block, 0, stream>>>(out, in, in_skipping_dim1, dim0, dim1);\n}\n\ntemplate void invokeTransposeAxis01(\n    int* out, int* in, const int* in_skipping_dim1, const int dim0, const int dim1, hipStream_t stream);\n\ntemplate<typename T, bool PREFIX_PROMPT>\n__global__ void buildDecoderAttentionMaskKernel(T*         attention_mask,\n                                                const int* sequence_lengths,\n                                                const int* prefix_prompt_lengths,\n                                                const int  max_seq_len,\n                                                const int  max_prompt_length)\n{\n    // sequence_lengths: [batch_size]\n    // attention_mask: [batch_size, 1, max_seq_len, max_seq_len + max_prompt_length]\n    const int max_prompt_seq_length = max_seq_len + max_prompt_length;\n    const int mask_size_per_seq     = max_seq_len * max_prompt_seq_length;\n    attention_mask += blockIdx.x * mask_size_per_seq;\n    const int seq_length    = sequence_lengths[blockIdx.x];\n    const int prompt_length = PREFIX_PROMPT ? prefix_prompt_lengths[blockIdx.x] : 0;\n    for (int i = threadIdx.x; i < mask_size_per_seq; i += blockDim.x) {\n        int row_id = i / max_prompt_seq_length;\n        int col_id = i % max_prompt_seq_length;\n        if (row_id < seq_length && col_id <= (row_id + prompt_length)) {\n            attention_mask[i] = (T)(1.0f);\n        }\n        else {\n            attention_mask[i] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBuildDecoderAttentionMask(T*           attention_mask,\n                                     const int*   sequence_lengths,\n                                     const int*   prefix_prompt_lengths,\n                                     const int    batch_size,\n                                     const int    max_seq_len,\n                                     const int    max_prompt_length,\n                                     hipStream_t stream)\n{\n    if (max_prompt_length == 0) {\n        buildDecoderAttentionMaskKernel<T, false><<<batch_size, 256, 0, stream>>>(\n            attention_mask, sequence_lengths, prefix_prompt_lengths, max_seq_len, max_prompt_length);\n    }\n    else {\n        buildDecoderAttentionMaskKernel<T, true><<<batch_size, 256, 0, stream>>>(\n            attention_mask, sequence_lengths, prefix_prompt_lengths, max_seq_len, max_prompt_length);\n    }\n}\n\ntemplate void invokeBuildDecoderAttentionMask(float*       attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int*   prefix_prompt_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              const int    max_prompt_length,\n                                              hipStream_t stream);\ntemplate void invokeBuildDecoderAttentionMask(half*        attention_mask,\n                                              const int*   sequence_lengths,\n                                              const int*   prefix_prompt_lengths,\n                                              const int    batch_size,\n                                              const int    max_seq_len,\n                                              const int    max_prompt_length,\n                                              hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeBuildDecoderAttentionMask(__hip_bfloat16* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int*     prefix_prompt_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              const int      max_prompt_length,\n                                              hipStream_t   stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeBuildDecoderAttentionMask(__hip_fp8_e4m3_fnuz* attention_mask,\n                                              const int*     sequence_lengths,\n                                              const int*     prefix_prompt_lengths,\n                                              const int      batch_size,\n                                              const int      max_seq_len,\n                                              const int      max_prompt_length,\n                                              hipStream_t   stream);\n#endif\n\ntemplate<typename T>\n__launch_bounds__(1024, 1) __global__ void lookupHiddenStateOfLastToken(T*         from_tensor,\n                                                                        const T*   hidden_state,\n                                                                        const int* input_lengths,\n                                                                        const int  max_input_length,\n                                                                        const int  batch_size,\n                                                                        const int  hidden_units)\n{\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        const int col_index = index % hidden_units;\n        const int batch_id  = index / hidden_units;\n        from_tensor[index]  = hidden_state[batch_id * max_input_length * hidden_units\n                                          + (input_lengths[batch_id] - 1) * hidden_units + col_index];\n    }\n}\n\ntemplate<typename T>\nvoid invokeLookupHiddenStateOfLastToken(T*           from_tensor,\n                                        const T*     hidden_state,\n                                        const int*   input_lengths,\n                                        const int    max_input_length,\n                                        const int    batch_size,\n                                        const int    hidden_units,\n                                        hipStream_t stream)\n{\n    const int grid_size = (int)(ceil(batch_size * hidden_units / 1024.));\n    dim3      grid(min(grid_size, 65536));\n    dim3      block(min(hidden_units, 1024));\n    lookupHiddenStateOfLastToken<T><<<grid, block, 0, stream>>>(\n        from_tensor, hidden_state, input_lengths, max_input_length, batch_size, hidden_units);\n}\n\ntemplate void invokeLookupHiddenStateOfLastToken(float*       from_tensor,\n                                                 const float* hidden_state,\n                                                 const int*   input_lengths,\n                                                 const int    max_input_length,\n                                                 const int    batch_size,\n                                                 const int    hidden_units,\n                                                 hipStream_t stream);\n\ntemplate void invokeLookupHiddenStateOfLastToken(half*        from_tensor,\n                                                 const half*  hidden_state,\n                                                 const int*   input_lengths,\n                                                 const int    max_input_length,\n                                                 const int    batch_size,\n                                                 const int    hidden_units,\n                                                 hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLookupHiddenStateOfLastToken(__hip_bfloat16*       from_tensor,\n                                                 const __hip_bfloat16* hidden_state,\n                                                 const int*           input_lengths,\n                                                 const int            max_input_length,\n                                                 const int            batch_size,\n                                                 const int            hidden_units,\n                                                 hipStream_t         stream);\n#endif\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void tileGptPromptInputs(int*       tiled_input_ids,\n                                    int*       tiled_input_lengths,\n                                    int*       tiled_prompt_lengths,\n                                    const int* input_ids,\n                                    const int* input_lengths,\n                                    const int* prefix_prompt_lengths,\n                                    const int  max_input_length)\n{\n    if (threadIdx.x == 0) {\n        tiled_input_lengths[blockIdx.x * gridDim.y + blockIdx.y] = input_lengths[blockIdx.x];\n        if (PREFIX_PROMPT) {\n            tiled_prompt_lengths[blockIdx.x * gridDim.y + blockIdx.y] = prefix_prompt_lengths[blockIdx.x];\n        }\n    }\n    for (int index = threadIdx.x; index < max_input_length; index += blockDim.x) {\n        tiled_input_ids[(blockIdx.x * gridDim.y + blockIdx.y) * max_input_length + index] =\n            input_ids[blockIdx.x * max_input_length + index];\n    }\n}\n\nvoid invokeTileGptPromptInputs(int*         tiled_input_ids,\n                               int*         tiled_input_lengths,\n                               int*         tiled_prompt_lengths,\n                               const int*   input_ids,\n                               const int*   input_lengths,\n                               const int*   prefix_prompt_lengths,\n                               const int    batch_size,\n                               const int    beam_width,\n                               const int    max_input_length,\n                               hipStream_t stream)\n{\n    dim3 grid(batch_size, beam_width);\n    dim3 block(min(1024, max_input_length));\n    if (prefix_prompt_lengths != nullptr) {\n        tileGptPromptInputs<true><<<grid, block, 0, stream>>>(tiled_input_ids,\n                                                              tiled_input_lengths,\n                                                              tiled_prompt_lengths,\n                                                              input_ids,\n                                                              input_lengths,\n                                                              prefix_prompt_lengths,\n                                                              max_input_length);\n    }\n    else {\n        tileGptPromptInputs<false><<<grid, block, 0, stream>>>(tiled_input_ids,\n                                                               tiled_input_lengths,\n                                                               tiled_prompt_lengths,\n                                                               input_ids,\n                                                               input_lengths,\n                                                               prefix_prompt_lengths,\n                                                               max_input_length);\n    }\n}\n\nvoid invokeTileGptInputs(int*         tiled_input_ids,\n                         int*         tiled_input_lengths,\n                         const int*   input_ids,\n                         const int*   input_lengths,\n                         const int    batch_size,\n                         const int    beam_width,\n                         const int    max_input_length,\n                         hipStream_t stream)\n{\n    invokeTileGptPromptInputs(tiled_input_ids,\n                              tiled_input_lengths,\n                              nullptr,\n                              input_ids,\n                              input_lengths,\n                              nullptr,\n                              batch_size,\n                              beam_width,\n                              max_input_length,\n                              stream);\n}\n\nvoid setSeqLimitLen(uint32_t* seq_len_d, Tensor seq_len, int limit_len_offset, int batch_size)\n{\n    std::vector<uint32_t> seq_len_h(batch_size);\n    for (int i = 0; i < batch_size; i++) {\n        seq_len_h[i] = seq_len.getPtr<uint32_t>()[i] + limit_len_offset;\n    }\n    cudaH2Dcpy(seq_len_d, seq_len_h.data(), batch_size);\n}\n\ntemplate<int TB_SIZE>\n__global__ void\nfind_context_dups(int* shared_contexts, const int* input_ids, const size_t batch_size, const size_t input_seq_len)\n{\n    /* We compare all context pairs (i, j), with i (tgt) < j (src) , to detect duplicate\n     * inputs. If there's a match between i and j, we store i at the\n     * j-th position of shared_context. So that we know that j can be\n     * represented by i. shared_contexts is initialized like shared_contexts[i] = i\n     * and when there's a match, we actually use shared_contexts[j] = min(shared_contexts[j], i)\n     * so that in the end, shared_contexts effectively contains an index\n     * to the match with the lowest index context.\n     * Note that shared_contexts[i] <= i, a property that will be used when uncompacting\n     * inputs.\n     */\n    typedef hipcub::BlockReduce<int, TB_SIZE>       BlockReduce;\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ bool                              match;\n\n    /* Each block is responsible for a (i, j) pair. To map the block space to\n     * the i < j space, we need to convert a linear addressing to a triangle, of\n     * size (batch_size * (batch_size - 1)) / 2\n     * For more information, check https://en.wikipedia.org/wiki/Triangular_number\n     */\n\n    // blockIdx = [0, 1, 2, ... n(n-1)/2] -> base_index = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, ..., n - 2]\n    const int base_index = floorf(0.5f * (sqrtf(1 + 8 * blockIdx.x) - 1));\n    const int src_idx    = base_index + 1;  // base_index \\in [1, batch_size)\n\n    const int rev_base_index = base_index * (base_index + 1) / 2;\n    const int tgt_idx        = blockIdx.x - rev_base_index;  // tgt_idx \\in [0, src_idx)\n\n    const int padded_length = TB_SIZE * ((input_seq_len + TB_SIZE - 1) / TB_SIZE);\n\n    int sum = 0;\n    for (int i = threadIdx.x; i < padded_length; i += TB_SIZE) {\n        int compare =\n            (i >= input_seq_len) ? 1 : input_ids[tgt_idx * input_seq_len + i] == input_ids[src_idx * input_seq_len + i];\n\n        sum = BlockReduce(temp_storage).Sum(compare);\n\n        if (threadIdx.x == 0) {\n            match = (sum == TB_SIZE);\n        }\n\n        __syncthreads();\n\n        if (!match) {\n            break;\n        }\n    }\n\n    if (threadIdx.x == 0 && match) {\n        atomicMin(&shared_contexts[src_idx], tgt_idx);\n    }\n}\n\nconstexpr int DUPS_INDICES_BLOCK_SIZE = 128;\n\n__global__ void generate_dups_indices(int*         batch_to_compact,\n                                      int*         compact_to_batch,\n                                      int*         compact_size,\n                                      const int*   shared_contexts,\n                                      const size_t batch_size,\n                                      const size_t beam_width,\n                                      const size_t input_seq_len)\n{\n    const int padded_batchsize = blockDim.x * ((batch_size + blockDim.x - 1) / blockDim.x);\n\n    typedef hipcub::BlockScan<int, DUPS_INDICES_BLOCK_SIZE, hipcub::BLOCK_SCAN_WARP_SCANS> BlockScan;\n    __shared__ typename BlockScan::TempStorage                                       temp_storage;\n    __shared__ int                                                                   scan_offset;\n\n    int scan = 0;\n    for (int seq_idx = threadIdx.x; seq_idx < padded_batchsize; seq_idx += blockDim.x) {\n        bool masked     = (seq_idx >= batch_size);\n        bool first_iter = seq_idx < blockDim.x;\n\n        int is_first_occur = masked ? 0 : shared_contexts[seq_idx] == seq_idx;\n        BlockScan(temp_storage).ExclusiveSum(is_first_occur, scan);\n\n        if (!masked && is_first_occur) {\n            int compact_idx = scan + (first_iter ? 0 : scan_offset);\n            // Context rep. writes initial index\n            batch_to_compact[seq_idx * beam_width] = compact_idx;\n            // input ids are tiled in context part\n            compact_to_batch[compact_idx] = seq_idx * beam_width;\n        }\n\n        __syncthreads();\n\n        if (threadIdx.x == blockDim.x - 1) {\n            scan_offset = scan + is_first_occur + (first_iter ? 0 : scan_offset);\n        }\n\n        __syncthreads();\n\n        if (!masked && !is_first_occur) {\n            // Fill the rest of batch_to_compact based on what rep. wrote\n            const int src_idx                      = batch_to_compact[shared_contexts[seq_idx] * beam_width];\n            batch_to_compact[seq_idx * beam_width] = src_idx;\n        }\n\n        if (!masked) {\n            // set same compact idx for beams\n            for (int beam_id = 1; beam_id < beam_width; ++beam_id) {\n                batch_to_compact[seq_idx * beam_width + beam_id] = batch_to_compact[seq_idx * beam_width];\n            }\n        }\n    }\n\n    if (threadIdx.x == 0) {\n        *compact_size = scan_offset;\n    }\n}\n\n__global__ void init_shared_contexts(int* shared_contexts, const size_t batch_size)\n{\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n    if (global_idx >= batch_size) {\n        return;\n    }\n    shared_contexts[global_idx] = global_idx;\n}\n\nvoid invokeFindContextDups(int*         shared_contexts,\n                           int*         batch_to_compact,\n                           int*         compact_to_batch,\n                           int*         compact_size,\n                           const int*   input_ids,\n                           const size_t batch_size,\n                           const size_t beam_width,\n                           const size_t input_seq_len,\n                           hipStream_t stream)\n{\n    dim3 block{512};\n    dim3 grid{((int)batch_size + block.x - 1) / block.x};\n    // set shared_context[i]\u00a0= i\n    init_shared_contexts<<<grid, block, 0, stream>>>(shared_contexts, batch_size);\n\n    grid = dim3{(unsigned int)(batch_size * (batch_size - 1)) / 2};\n    // set shared_contexts[i] = j, where j = min{k, such that input_ids[k] == input_ids[i]}\n    if (input_seq_len <= 128) {\n        block = 128;\n        find_context_dups<128><<<grid, block, 0, stream>>>(shared_contexts, input_ids, batch_size, input_seq_len);\n    }\n    else {\n        block = 256;\n        find_context_dups<256><<<grid, block, 0, stream>>>(shared_contexts, input_ids, batch_size, input_seq_len);\n    }\n\n    // set batch_to_compact[i] = j, where j is the position of input_ids[i] in the compact_batch\n    // set compact_to_batch[i] = j, where j is such that compact_to_batch[i] = input_ids[j]\n    generate_dups_indices<<<1, DUPS_INDICES_BLOCK_SIZE, 0, stream>>>(\n        batch_to_compact, compact_to_batch, compact_size, shared_contexts, batch_size, beam_width, input_seq_len);\n}\n\ntemplate<typename T>\n__global__ void compact_inputs(T*         compact_input,\n                               T*         compact_attention_mask,\n                               int*       compact_input_lengths,\n                               const T*   decoder_input,\n                               const T*   decoder_mask,\n                               const int* input_lengths,\n                               const int* compact_idx,\n                               size_t     compact_size,\n                               size_t     seq_len,\n                               size_t     hidden_dimension)\n{\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (global_idx < compact_size * seq_len * hidden_dimension) {\n        const int h_id     = global_idx % hidden_dimension;\n        const int seq_id   = (global_idx / hidden_dimension) % seq_len;\n        const int batch_id = global_idx / (hidden_dimension * seq_len);\n\n        compact_input[global_idx] = decoder_input[(compact_idx[batch_id] * seq_len + seq_id) * hidden_dimension + h_id];\n    }\n\n    if (global_idx < compact_size * seq_len * seq_len) {\n        const int seq1_id  = global_idx % seq_len;\n        const int seq2_id  = (global_idx / seq_len) % seq_len;\n        const int batch_id = global_idx / (seq_len * seq_len);\n\n        compact_attention_mask[global_idx] =\n            decoder_mask[(compact_idx[batch_id] * seq_len + seq2_id) * seq_len + seq1_id];\n    }\n\n    if (global_idx < compact_size) {\n        compact_input_lengths[global_idx] = input_lengths[compact_idx[global_idx]];\n    }\n}\n\ntemplate<typename T>\nvoid invokeCompactInputs(T*           compact_input,\n                         T*           compact_attention_mask,\n                         int*         compact_input_lengths,\n                         const T*     decoder_input,\n                         const T*     decoder_mask,\n                         const int*   input_lengths,\n                         const int*   compact_idx,\n                         size_t       compact_size,\n                         size_t       seq_len,\n                         size_t       hidden_dimension,\n                         hipStream_t stream)\n{\n    /* Compact relevant decoder_layer inputs based on the identical contexts.\n     * For example, decoder_input is [batch_size, seq_len, H]. It's compacted\n     * into compact_input [compact_size, seq_len, H] such that\n     * compact_input[i, ...] = decoder_input[compact_idx[i], ...] */\n    const size_t elems_n = compact_size * seq_len * max(hidden_dimension, seq_len);\n    const dim3   blockDim(512);\n    const dim3   gridDim((elems_n + 512 - 1) / 512);\n\n    compact_inputs<T><<<gridDim, blockDim, 0, stream>>>(compact_input,\n                                                        compact_attention_mask,\n                                                        compact_input_lengths,\n                                                        decoder_input,\n                                                        decoder_mask,\n                                                        input_lengths,\n                                                        compact_idx,\n                                                        compact_size,\n                                                        seq_len,\n                                                        hidden_dimension);\n}\n\n#define INSTANTIATE_INVOKE_COMPACT_INPUTS(T)                                                                           \\\n    template void invokeCompactInputs<T>(T * compact_input,                                                            \\\n                                         T * compact_attention_mask,                                                   \\\n                                         int*         compact_input_lengths,                                           \\\n                                         const T*     decoder_input,                                                   \\\n                                         const T*     decoder_mask,                                                    \\\n                                         const int*   input_lengths,                                                   \\\n                                         const int*   compact_idx,                                                     \\\n                                         size_t       compact_size,                                                    \\\n                                         size_t       seq_len,                                                         \\\n                                         size_t       hidden_dimension,                                                \\\n                                         hipStream_t stream)\nINSTANTIATE_INVOKE_COMPACT_INPUTS(half);\nINSTANTIATE_INVOKE_COMPACT_INPUTS(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_COMPACT_INPUTS(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_COMPACT_INPUTS\n\ntemplate<typename T>\n__global__ void uncompact_outputs(T*         uncompact_buffer,\n                                  const T*   compact_buffer,\n                                  const int* batch_to_compact_idx,\n                                  size_t     batch_size,\n                                  size_t     buffer_stride)\n{\n    /* Uncompact a buffer IN of size [Compact, Stride] into OUT of size [Batch, Stride]\n     * so that \\forall i, OUT[i, :] = IN[batch_to_compact_idx[i], :]\n     */\n    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (global_idx >= batch_size * buffer_stride) {\n        return;\n    }\n\n    const int stride_idx = global_idx % buffer_stride;\n    const int batch_idx  = global_idx / buffer_stride;\n\n    const int src                = batch_to_compact_idx[batch_idx];\n    uncompact_buffer[global_idx] = compact_buffer[src * buffer_stride + stride_idx];\n}\n\ntemplate<typename T>\nvoid invokeUnCompactOutputs(T*           uncompact_buffer,\n                            const T*     compact_buffer,\n                            const int*   batch_to_compact_idx,\n                            size_t       batch_size,\n                            size_t       buffer_stride,\n                            hipStream_t stream)\n{\n    const size_t num_elems = batch_size * buffer_stride;\n    const dim3   blockDim(1024);\n    const dim3   gridDim((num_elems + blockDim.x - 1) / blockDim.x);\n\n    uncompact_outputs<T><<<gridDim, blockDim, 0, stream>>>(\n        uncompact_buffer, compact_buffer, batch_to_compact_idx, batch_size, buffer_stride);\n}\n\n#define INSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(T)                                                                        \\\n    template void invokeUnCompactOutputs(T*           uncompact_buffer,                                                \\\n                                         const T*     compact_buffer,                                                  \\\n                                         const int*   batch_to_compact_idx,                                            \\\n                                         size_t       batch_size,                                                      \\\n                                         size_t       buffer_stride,                                                   \\\n                                         hipStream_t stream)\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(half);\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_UNCOMPACT_OUTPUTS\n\ntemplate<typename T>\n__global__ void uncompact_caches(T*         uncompact_k_cache,\n                                 T*         uncompact_v_cache,\n                                 const T*   compact_k_cache,\n                                 const T*   compact_v_cache,\n                                 const int* batch_to_compact_idx,\n                                 size_t     batch_size,\n                                 size_t     num_heads,\n                                 size_t     max_seq_len,\n                                 size_t     seq_len,\n                                 size_t     size_per_head,\n                                 size_t     local_batch_size,\n                                 size_t     ite)\n{\n    const int hidden_dimension    = num_heads * size_per_head;\n    const int num_elems_per_batch = seq_len * hidden_dimension;\n    const int num_elems_cache     = batch_size * num_elems_per_batch;\n    const int x_size              = 16 / sizeof(T);\n\n    for (int global_idx = blockIdx.x * blockDim.x + threadIdx.x; global_idx < 2 * num_elems_cache;\n         global_idx += blockDim.x * gridDim.x) {\n\n        const bool     handle_k  = global_idx < num_elems_cache;\n        const T* const cache_src = handle_k ? compact_k_cache : compact_v_cache;\n        T* const       cache_dst = handle_k ? uncompact_k_cache : uncompact_v_cache;\n        const int      idx       = handle_k ? global_idx : global_idx - num_elems_cache;\n\n        const int src_offset = idx % num_elems_per_batch;\n        const int batch_idx  = idx / num_elems_per_batch;\n        const int batch_src  = batch_to_compact_idx[batch_idx] - ite * local_batch_size;\n\n        if (batch_src < 0 || batch_src >= local_batch_size) {\n            continue;\n        }\n\n        int dst_offset;\n        if (handle_k) {\n            const int i0 = idx % (x_size * seq_len);\n            const int i1 = (idx / (x_size * seq_len)) % (num_heads * size_per_head / x_size);\n            dst_offset   = i1 * max_seq_len * x_size + i0;\n        }\n        else {\n            const int i0 = idx % (size_per_head * seq_len);\n            const int i1 = (idx / (size_per_head * seq_len)) % (num_heads);\n            dst_offset   = i1 * max_seq_len * size_per_head + i0;\n        }\n\n        cache_dst[batch_idx * max_seq_len * hidden_dimension + dst_offset] =\n            cache_src[batch_src * num_elems_per_batch + src_offset];\n    }\n}\n\ntemplate<typename T>\nvoid invokeUnCompactCaches(T*           uncompact_k_cache,\n                           T*           uncompact_v_cache,\n                           const T*     compact_k_cache,\n                           const T*     compact_v_cache,\n                           const int*   batch_to_compact_idx,\n                           size_t       batch_size,\n                           size_t       num_heads,\n                           size_t       max_seq_len,\n                           size_t       seq_len,\n                           size_t       size_per_head,\n                           size_t       local_batch_size,\n                           size_t       ite,\n                           hipStream_t stream)\n{\n    const dim3 blockDim(512);\n    const dim3 gridDim(1024);\n    uncompact_caches<T><<<gridDim, blockDim, 0, stream>>>(uncompact_k_cache,\n                                                          uncompact_v_cache,\n                                                          compact_k_cache,\n                                                          compact_v_cache,\n                                                          batch_to_compact_idx,\n                                                          batch_size,\n                                                          num_heads,\n                                                          max_seq_len,\n                                                          seq_len,\n                                                          size_per_head,\n                                                          local_batch_size,\n                                                          ite);\n}\n\n#define INSTANTIATE_INVOKE_UNCOMPACT_CACHES(T)                                                                         \\\n    template void invokeUnCompactCaches(T*           uncompact_k_cache,                                                \\\n                                        T*           uncompact_v_cache,                                                \\\n                                        const T*     compact_k_cache,                                                  \\\n                                        const T*     compact_v_cache,                                                  \\\n                                        const int*   batch_to_compact_idx,                                             \\\n                                        size_t       batch_size,                                                       \\\n                                        size_t       num_heads,                                                        \\\n                                        size_t       max_seq_len,                                                      \\\n                                        size_t       seq_len,                                                          \\\n                                        size_t       size_per_head,                                                    \\\n                                        size_t       local_batch_size,                                                 \\\n                                        size_t       ite,                                                              \\\n                                        hipStream_t stream)\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(half);\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_UNCOMPACT_CACHES(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_UNCOMPACT_CACHES\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void update_padding_count(int*       total_padding_count,\n                                     const int* input_lengths,\n                                     const int* tiled_prompt_lengths,\n                                     size_t     max_input_length,\n                                     size_t     max_prompt_length,\n                                     size_t     batch_size,\n                                     size_t     beam_width)\n{\n    const int gidx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (gidx >= batch_size * beam_width) {\n        return;\n    }\n\n    const int batch_idx = gidx / beam_width;\n\n    total_padding_count[gidx] +=\n        PREFIX_PROMPT ? (max_input_length + max_prompt_length - input_lengths[batch_idx] - tiled_prompt_lengths[gidx]) :\n                        (max_input_length - input_lengths[batch_idx]);\n}\n\nvoid invokeUpdatePaddingCount(int*         total_padding_count,\n                              const int*   input_lengths,\n                              const int*   tiled_prompt_lengths,\n                              size_t       max_input_length,\n                              size_t       max_prompt_length,\n                              size_t       batch_size,\n                              size_t       beam_width,\n                              hipStream_t stream)\n{\n    dim3 blockSize(256);\n    dim3 gridSize((batch_size * beam_width + blockSize.x - 1) / blockSize.x);\n\n    if (tiled_prompt_lengths != nullptr) {\n        update_padding_count<true><<<gridSize, blockSize, 0, stream>>>(total_padding_count,\n                                                                       input_lengths,\n                                                                       tiled_prompt_lengths,\n                                                                       max_input_length,\n                                                                       max_prompt_length,\n                                                                       batch_size,\n                                                                       beam_width);\n    }\n    else {\n        update_padding_count<false><<<gridSize, blockSize, 0, stream>>>(total_padding_count,\n                                                                        input_lengths,\n                                                                        tiled_prompt_lengths,\n                                                                        max_input_length,\n                                                                        max_prompt_length,\n                                                                        batch_size,\n                                                                        beam_width);\n    }\n}\n\ntemplate<bool PREFIX_PROMPT>\n__global__ void mask_padding_tokens(bool*        masked_tokens,\n                                    const int*   input_lengths,\n                                    const int*   tiled_prefix_prompt_lengths,\n                                    const size_t memory_len,\n                                    const size_t max_input_length,\n                                    const size_t initial_step,\n                                    size_t       beam_width)\n{\n    const int seq_len = PREFIX_PROMPT ?\n                            (input_lengths[blockIdx.x / beam_width] + tiled_prefix_prompt_lengths[blockIdx.x]) :\n                            input_lengths[blockIdx.x / beam_width];\n    for (int step = initial_step + seq_len + threadIdx.x; step < initial_step + max_input_length; step += blockDim.x) {\n        masked_tokens[blockIdx.x * memory_len + step % memory_len] = true;\n    }\n}\n\nvoid invokeMaskPaddingTokens(bool*        masked_tokens,\n                             const int*   input_lengths,\n                             const int*   tiled_prefix_prompt_lengths,\n                             const size_t memory_len,\n                             const size_t max_input_length,\n                             const size_t initial_step,\n                             size_t       batch_size,\n                             size_t       beam_width,\n                             hipStream_t stream)\n{\n    dim3 blockSize(128);\n    dim3 gridSize(batch_size * beam_width);\n    if (tiled_prefix_prompt_lengths != nullptr) {\n        mask_padding_tokens<true><<<gridSize, blockSize, 0, stream>>>(masked_tokens,\n                                                                      input_lengths,\n                                                                      tiled_prefix_prompt_lengths,\n                                                                      memory_len,\n                                                                      max_input_length,\n                                                                      initial_step,\n                                                                      beam_width);\n    }\n    else {\n        mask_padding_tokens<false><<<gridSize, blockSize, 0, stream>>>(masked_tokens,\n                                                                       input_lengths,\n                                                                       tiled_prefix_prompt_lengths,\n                                                                       memory_len,\n                                                                       max_input_length,\n                                                                       initial_step,\n                                                                       beam_width);\n    }\n}\n\ntemplate<typename T>\n__global__ void sum_length_dimension(\n    float* out_buf, const T* in_buf, const size_t batch_size, const size_t input_length, const size_t hidden_dim)\n{\n    const int bidx = blockIdx.x;\n\n    for (int hidx = threadIdx.x; hidx < hidden_dim; hidx += blockDim.x) {\n        float accum = 0.0f;\n        for (int step = 0; step < input_length; step++) {\n            accum += static_cast<float>(in_buf[(bidx * input_length + step) * hidden_dim + hidx]);\n        }\n        out_buf[bidx * hidden_dim + hidx] = accum;\n    }\n}\n\ntemplate<typename T>\nvoid invokeSumLengthDimension(float*       out_buf,\n                              const T*     in_buf,\n                              const size_t batch_size,\n                              const size_t input_length,\n                              const size_t hidden_dim,\n                              hipStream_t stream)\n{\n    dim3 gridSize(batch_size);\n    dim3 blockSize(256);\n\n    sum_length_dimension<<<gridSize, blockSize, 0, stream>>>(out_buf, in_buf, batch_size, input_length, hidden_dim);\n}\n\n#define INSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(T)                                                                     \\\n    template void invokeSumLengthDimension(float*       out_buf,                                                       \\\n                                           const T*     in_buf,                                                        \\\n                                           const size_t batch_size,                                                    \\\n                                           const size_t input_length,                                                  \\\n                                           const size_t hidden_dim,                                                    \\\n                                           hipStream_t stream)\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(half);\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(float);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_SUM_LENGTH_DIMENSION\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/gpt_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cassert>\n#include <cmath>\n#include <cuda_fp16.h>\n#include <stdint.h>\n\n#include \"add_bias_transpose_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void addBiasTransposeToMultiHead(const T*  matrices,\n                                            const T*  biases,\n                                            T*        output,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int size_per_head,\n                                            const int seq_len,\n                                            const int matrices_num,\n                                            const int x_repeat_per_block,\n                                            const int y_repeat_per_block)\n{\n    for (int j = 0; j < y_repeat_per_block; j++) {\n        int y_offset = blockIdx.y * blockDim.y * y_repeat_per_block + j * blockDim.y + threadIdx.y;\n        int bias_id  = -1;\n        T   bias_element;\n        for (int i = 0; i < x_repeat_per_block; i++) {\n            int x_offset    = blockIdx.x * blockDim.x * x_repeat_per_block + i * blockDim.x + threadIdx.x;\n            int bias_id_new = x_offset / (batch_size * seq_len);\n            if (bias_id_new != bias_id) {\n                bias_element = biases[bias_id_new * head_num * size_per_head + y_offset];\n                bias_id      = bias_id_new;\n            }\n            if (x_offset < batch_size * seq_len * matrices_num && y_offset < head_num * size_per_head) {\n                int matrix_id     = x_offset / (batch_size * seq_len);\n                int batch_id      = (x_offset % (batch_size * seq_len)) / seq_len;\n                int seq_id        = x_offset % seq_len;\n                int head_id       = y_offset / size_per_head;\n                int head_y_offset = y_offset % size_per_head;\n\n                int output_offset = matrix_id * batch_size * head_num * seq_len * size_per_head\n                                    + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head\n                                    + seq_id * size_per_head + head_y_offset;\n\n                output[output_offset] = matrices[x_offset * head_num * size_per_head + y_offset] + bias_element;\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasTransposeToMultiHead(const T*           matrices,\n                                       const T*           biases,\n                                       T*                 output,\n                                       const int          batch_size,\n                                       const int          head_num,\n                                       const int          size_per_head,\n                                       const int          seq_len,\n                                       const int          matrices_num,\n                                       const cudaStream_t stream)\n{\n    /*\n        Matrices are q k v, optionally global k, global k and global q for longformer(so matrices_num matrices in\n       total), and each is a (batch_size, seq_len, head_num * size_per_head). tensor and the store order should also\n       meets the shape. The bias should be a (matrices_num, head_num * size_per_head). This kernel will split and\n       transpose single head (matrices_num, batch_size, seq_len, head_num * size_per_head) into (matrices_num,\n       batch_size, head_num, seq_len, size_per_head) and add bias accordingly\n    */\n\n    // x direction is along the (matrices_num, batch_size, seq_len) direction\n    // y direction is along the (head_num * size_per_head) direction\n\n    const int x_repeat_per_block = 8;\n    const int y_repeat_per_block = 1;\n    const int block_dim_x        = 1;\n    const int block_dim_y        = 32;\n\n    const int x_total_len = matrices_num * batch_size * seq_len;\n    const int y_total_len = head_num * size_per_head;\n\n    dim3 grid((int)std::ceil((float)x_total_len / (x_repeat_per_block * block_dim_x)),\n              (int)std::ceil((float)y_total_len / (y_repeat_per_block * block_dim_y)));\n    dim3 block(block_dim_x, block_dim_y);\n\n    addBiasTransposeToMultiHead<T><<<grid, block, 0, stream>>>(matrices,\n                                                               biases,\n                                                               output,\n                                                               batch_size,\n                                                               head_num,\n                                                               size_per_head,\n                                                               seq_len,\n                                                               matrices_num,\n                                                               x_repeat_per_block,\n                                                               y_repeat_per_block);\n}\n\ntemplate void invokeAddBiasTransposeToMultiHead(const float*       matrices,\n                                                const float*       biases,\n                                                float*             output,\n                                                const int          batch_size,\n                                                const int          head_num,\n                                                const int          size_per_head,\n                                                const int          seq_len,\n                                                const int          matrices_num,\n                                                const cudaStream_t stream);\n\ntemplate void invokeAddBiasTransposeToMultiHead(const half*        matrices,\n                                                const half*        biases,\n                                                half*              output,\n                                                const int          batch_size,\n                                                const int          head_num,\n                                                const int          size_per_head,\n                                                const int          seq_len,\n                                                const int          matrices_num,\n                                                const cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasTransposeToMultiHead(const __nv_bfloat16* matrices,\n                                                const __nv_bfloat16* biases,\n                                                __nv_bfloat16*       output,\n                                                const int            batch_size,\n                                                const int            head_num,\n                                                const int            size_per_head,\n                                                const int            seq_len,\n                                                const int            matrices_num,\n                                                const cudaStream_t   stream);\n#endif\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\ntemplate<typename T>\n__global__ void transposeMultiHeadToSingleKernel(\n    T* src, T* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int       target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n    uint32_t* src_ptr   = (uint32_t*)src;\n    uint32_t* dst_ptr   = (uint32_t*)dst;\n\n    dst_ptr[target_id] = src_ptr[tid];\n}\n\ntemplate<>\n__global__ void transposeMultiHeadToSingleKernel<float>(\n    float* src, float* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int batch_id       = blockIdx.x / (head_num * seq_len);\n    int seq_id         = blockIdx.x % seq_len;\n    int head_id        = (blockIdx.x % (head_num * seq_len)) / seq_len;\n    dst[batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head + head_id * size_per_head\n        + threadIdx.x] = src[blockIdx.x * size_per_head + threadIdx.x];\n}\n\ntemplate<typename T>\nvoid invokeTransposeMultiHeadToSingle(T*           dst,\n                                      T*           src,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    head_num,\n                                      const int    size_per_head,\n                                      cudaStream_t stream)\n{\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        const int seq_per_block = 4;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head / 2;\n\n        assert(grid.x * seq_per_block == batch_size * head_num * seq_len);\n\n        transposeMultiHeadToSingleKernel<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head / 2);\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transposeMultiHeadToSingleKernel<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n    }\n}\n\ntemplate void invokeTransposeMultiHeadToSingle(float*       dst,\n                                               float*       src,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    head_num,\n                                               const int    size_per_head,\n                                               cudaStream_t stream);\n\ntemplate void invokeTransposeMultiHeadToSingle(half*        dst,\n                                               half*        src,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    head_num,\n                                               const int    size_per_head,\n                                               cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTransposeMultiHeadToSingle(__nv_bfloat16* dst,\n                                               __nv_bfloat16* src,\n                                               const int      batch_size,\n                                               const int      seq_len,\n                                               const int      head_num,\n                                               const int      size_per_head,\n                                               cudaStream_t   stream);\n#endif\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cassert>\n#include <cmath>\n#include <hip/hip_fp16.h>\n#include <stdint.h>\n\n#include \"add_bias_transpose_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void addBiasTransposeToMultiHead(const T*  matrices,\n                                            const T*  biases,\n                                            T*        output,\n                                            const int batch_size,\n                                            const int head_num,\n                                            const int size_per_head,\n                                            const int seq_len,\n                                            const int matrices_num,\n                                            const int x_repeat_per_block,\n                                            const int y_repeat_per_block)\n{\n    for (int j = 0; j < y_repeat_per_block; j++) {\n        int y_offset = blockIdx.y * blockDim.y * y_repeat_per_block + j * blockDim.y + threadIdx.y;\n        int bias_id  = -1;\n        T   bias_element;\n        for (int i = 0; i < x_repeat_per_block; i++) {\n            int x_offset    = blockIdx.x * blockDim.x * x_repeat_per_block + i * blockDim.x + threadIdx.x;\n            int bias_id_new = x_offset / (batch_size * seq_len);\n            if (bias_id_new != bias_id) {\n                bias_element = biases[bias_id_new * head_num * size_per_head + y_offset];\n                bias_id      = bias_id_new;\n            }\n            if (x_offset < batch_size * seq_len * matrices_num && y_offset < head_num * size_per_head) {\n                int matrix_id     = x_offset / (batch_size * seq_len);\n                int batch_id      = (x_offset % (batch_size * seq_len)) / seq_len;\n                int seq_id        = x_offset % seq_len;\n                int head_id       = y_offset / size_per_head;\n                int head_y_offset = y_offset % size_per_head;\n\n                int output_offset = matrix_id * batch_size * head_num * seq_len * size_per_head\n                                    + batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head\n                                    + seq_id * size_per_head + head_y_offset;\n\n                output[output_offset] = matrices[x_offset * head_num * size_per_head + y_offset] + bias_element;\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasTransposeToMultiHead(const T*           matrices,\n                                       const T*           biases,\n                                       T*                 output,\n                                       const int          batch_size,\n                                       const int          head_num,\n                                       const int          size_per_head,\n                                       const int          seq_len,\n                                       const int          matrices_num,\n                                       const hipStream_t stream)\n{\n    /*\n        Matrices are q k v, optionally global k, global k and global q for longformer(so matrices_num matrices in\n       total), and each is a (batch_size, seq_len, head_num * size_per_head). tensor and the store order should also\n       meets the shape. The bias should be a (matrices_num, head_num * size_per_head). This kernel will split and\n       transpose single head (matrices_num, batch_size, seq_len, head_num * size_per_head) into (matrices_num,\n       batch_size, head_num, seq_len, size_per_head) and add bias accordingly\n    */\n\n    // x direction is along the (matrices_num, batch_size, seq_len) direction\n    // y direction is along the (head_num * size_per_head) direction\n\n    const int x_repeat_per_block = 8;\n    const int y_repeat_per_block = 1;\n    const int block_dim_x        = 1;\n    const int block_dim_y        = 32;\n\n    const int x_total_len = matrices_num * batch_size * seq_len;\n    const int y_total_len = head_num * size_per_head;\n\n    dim3 grid((int)std::ceil((float)x_total_len / (x_repeat_per_block * block_dim_x)),\n              (int)std::ceil((float)y_total_len / (y_repeat_per_block * block_dim_y)));\n    dim3 block(block_dim_x, block_dim_y);\n\n    addBiasTransposeToMultiHead<T><<<grid, block, 0, stream>>>(matrices,\n                                                               biases,\n                                                               output,\n                                                               batch_size,\n                                                               head_num,\n                                                               size_per_head,\n                                                               seq_len,\n                                                               matrices_num,\n                                                               x_repeat_per_block,\n                                                               y_repeat_per_block);\n}\n\ntemplate void invokeAddBiasTransposeToMultiHead(const float*       matrices,\n                                                const float*       biases,\n                                                float*             output,\n                                                const int          batch_size,\n                                                const int          head_num,\n                                                const int          size_per_head,\n                                                const int          seq_len,\n                                                const int          matrices_num,\n                                                const hipStream_t stream);\n\ntemplate void invokeAddBiasTransposeToMultiHead(const half*        matrices,\n                                                const half*        biases,\n                                                half*              output,\n                                                const int          batch_size,\n                                                const int          head_num,\n                                                const int          size_per_head,\n                                                const int          seq_len,\n                                                const int          matrices_num,\n                                                const hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasTransposeToMultiHead(const __hip_bfloat16* matrices,\n                                                const __hip_bfloat16* biases,\n                                                __hip_bfloat16*       output,\n                                                const int            batch_size,\n                                                const int            head_num,\n                                                const int            size_per_head,\n                                                const int            seq_len,\n                                                const int            matrices_num,\n                                                const hipStream_t   stream);\n#endif\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\ntemplate<typename T>\n__global__ void transposeMultiHeadToSingleKernel(\n    T* src, T* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int       target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n    uint32_t* src_ptr   = (uint32_t*)src;\n    uint32_t* dst_ptr   = (uint32_t*)dst;\n\n    dst_ptr[target_id] = src_ptr[tid];\n}\n\ntemplate<>\n__global__ void transposeMultiHeadToSingleKernel<float>(\n    float* src, float* dst, const int batch_size, const int seq_len, const int head_num, const int size_per_head)\n{\n    int batch_id       = blockIdx.x / (head_num * seq_len);\n    int seq_id         = blockIdx.x % seq_len;\n    int head_id        = (blockIdx.x % (head_num * seq_len)) / seq_len;\n    dst[batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head + head_id * size_per_head\n        + threadIdx.x] = src[blockIdx.x * size_per_head + threadIdx.x];\n}\n\ntemplate<typename T>\nvoid invokeTransposeMultiHeadToSingle(T*           dst,\n                                      T*           src,\n                                      const int    batch_size,\n                                      const int    seq_len,\n                                      const int    head_num,\n                                      const int    size_per_head,\n                                      hipStream_t stream)\n{\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        const int seq_per_block = 4;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head / 2;\n\n        assert(grid.x * seq_per_block == batch_size * head_num * seq_len);\n\n        transposeMultiHeadToSingleKernel<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head / 2);\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transposeMultiHeadToSingleKernel<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head);\n    }\n}\n\ntemplate void invokeTransposeMultiHeadToSingle(float*       dst,\n                                               float*       src,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    head_num,\n                                               const int    size_per_head,\n                                               hipStream_t stream);\n\ntemplate void invokeTransposeMultiHeadToSingle(half*        dst,\n                                               half*        src,\n                                               const int    batch_size,\n                                               const int    seq_len,\n                                               const int    head_num,\n                                               const int    size_per_head,\n                                               hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTransposeMultiHeadToSingle(__hip_bfloat16* dst,\n                                               __hip_bfloat16* src,\n                                               const int      batch_size,\n                                               const int      seq_len,\n                                               const int      head_num,\n                                               const int      size_per_head,\n                                               hipStream_t   stream);\n#endif\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/add_bias_transpose_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/quantize_weight.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n__device__ __host__ int index_CUBLASLT_ORDER_COL4_4R2_8C(int col_id, int row_id, int m_32)\n{\n    int new_col = col_id >> 5;\n    int new_row =  // CUBLASLT_ORDER_COL4_4R2_8C\n                   ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                   ////row_id%2 is even row, otherwise odd row\n                   ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n        (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id & 31) >> 3)) << 5) +\n         ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n         ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n         (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id & 7) >> 1)) << 2) +\n         ////col_id%4 is the id of 4 cols\n         (col_id & 3));\n    return new_col * m_32 + new_row;\n}\n\n__device__ __host__ int index_CUBLASLT_ORDER_COL32_2R_4R4(int col_id, int row_id, int m_32)\n{\n    int new_col     = col_id >> 5;\n    int row_in_tile = row_id & 31;\n    int col_in_tile = col_id & 31;\n    int new_row     =  // CUBLASLT_ORDER_COL32_2R_4R4\n        (((row_id >> 5) << 10) +\n         //(((row%8)/2*4+row/8)*2+row%2)*32+col\n         (((((((row_in_tile & 7) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    return new_col * m_32 + new_row;\n}\n\n__global__ void quantize_weight_kernel(int8_t*      dst,\n                                       const float* src,\n                                       const float* amax,\n                                       const int    n,\n                                       const int    k,\n                                       const int    format,\n                                       const int    scale_is_vector)\n{\n    int tid     = (blockIdx.x * blockDim.x + threadIdx.x);\n    int col_idx = tid / n;\n    int row_idx = tid - col_idx * n;\n    int new_idx;\n    if (format == 0) {\n        new_idx = row_idx * k + col_idx;\n    }\n    else if (format == 1) {\n        new_idx = index_CUBLASLT_ORDER_COL32_2R_4R4(col_idx, row_idx, 32 * n);\n    }\n    else if (format == 2) {\n        new_idx = index_CUBLASLT_ORDER_COL4_4R2_8C(col_idx, row_idx, 32 * n);\n    }\n    if (tid < n * k) {\n        int8_t v     = float_to_int8_rn(src[tid] * 127.0 / amax[row_idx * scale_is_vector]);\n        dst[new_idx] = v;\n    }\n}\n\n__global__ void quantize_weight_kernel(int8_t*      dst,\n                                       const half*  src,\n                                       const float* amax,\n                                       const int    n,\n                                       const int    k,\n                                       const int    format,\n                                       const int    scale_is_vector)\n{\n    int tid     = (blockIdx.x * blockDim.x + threadIdx.x);\n    int col_idx = tid / n;\n    int row_idx = tid - col_idx * n;\n    int new_idx;\n    if (format == 0) {\n        new_idx = row_idx * k + col_idx;\n    }\n    else if (format == 1) {\n        new_idx = index_CUBLASLT_ORDER_COL32_2R_4R4(col_idx, row_idx, 32 * n);\n    }\n    else if (format == 2) {\n        new_idx = index_CUBLASLT_ORDER_COL4_4R2_8C(col_idx, row_idx, 32 * n);\n    }\n    if (tid < n * k) {\n        int8_t v     = float_to_int8_rn(__half2float(src[tid]) * 127.0 / amax[row_idx * scale_is_vector]);\n        dst[new_idx] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeQuantizeWeight(int8_t*      dst,\n                          const T*     src,\n                          const float* amax,\n                          const int    n,\n                          const int    k,\n                          const int    format,\n                          cudaStream_t stream,\n                          const int    scale_is_vector)\n{\n    if (format != 0 && format != 1 & format != 2) {\n        printf(\"[ERROR][invokeQuantizeWeight] format must be one of 0, 1, 2. current value: %d\\n\", format);\n        exit(-1);\n    }\n    if (scale_is_vector != 0 && scale_is_vector != 1) {\n        printf(\"[ERROR][invokeQuantizeWeight] scale_is_vector must be either 0 or 1. current value: %d\\n\",\n               scale_is_vector);\n        exit(-1);\n    }\n    dim3 grid((n * k + 255) / 256);\n    dim3 block(256);\n    if (sizeof(T) == sizeof(float)) {\n        quantize_weight_kernel<<<grid, block, 0, stream>>>(dst, (const float*)src, amax, n, k, format, scale_is_vector);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        quantize_weight_kernel<<<grid, block, 0, stream>>>(dst, (const half*)src, amax, n, k, format, scale_is_vector);\n    }\n}\n\ntemplate void invokeQuantizeWeight<float>(int8_t*      dst,\n                                          const float* src,\n                                          const float* amax,\n                                          const int    n,\n                                          const int    k,\n                                          const int    format,\n                                          cudaStream_t stream,\n                                          const int    scale_is_vector);\n\ntemplate void invokeQuantizeWeight<half>(int8_t*      dst,\n                                         const half*  src,\n                                         const float* amax,\n                                         const int    n,\n                                         const int    k,\n                                         const int    format,\n                                         cudaStream_t stream,\n                                         const int    scale_is_vector);\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/quantize_weight.h\"\n#include <cstdio>\n\nnamespace fastertransformer {\n\n__device__ __host__ int index_CUBLASLT_ORDER_COL4_4R2_8C(int col_id, int row_id, int m_32)\n{\n    int new_col = col_id >> 5;\n    int new_row =  // CUBLASLT_ORDER_COL4_4R2_8C\n                   ////row_id/8 is the number of tile of (8 rows 32 columns) -- column-major\n                   ////row_id%2 is even row, otherwise odd row\n                   ////col_id%COL32_/8 is the number tile of (8 rows 8 columns)\n        (((((row_id >> 3) << 3) + ((row_id & 1) << 2) + ((col_id & 31) >> 3)) << 5) +\n         ////col_id%8 >= 4 is the right half of (8 rows 8 columns) tile\n         ////(row_id%8/2) is (the row id of alternating 4 rows) - 1\n         (((((col_id & 7) >= 4) ? 4 : 0) + ((row_id & 7) >> 1)) << 2) +\n         ////col_id%4 is the id of 4 cols\n         (col_id & 3));\n    return new_col * m_32 + new_row;\n}\n\n__device__ __host__ int index_CUBLASLT_ORDER_COL32_2R_4R4(int col_id, int row_id, int m_32)\n{\n    int new_col     = col_id >> 5;\n    int row_in_tile = row_id & 31;\n    int col_in_tile = col_id & 31;\n    int new_row     =  // CUBLASLT_ORDER_COL32_2R_4R4\n        (((row_id >> 5) << 10) +\n         //(((row%8)/2*4+row/8)*2+row%2)*32+col\n         (((((((row_in_tile & 7) >> 1) << 2) + (row_in_tile >> 3)) << 1) + (row_in_tile & 1)) << 5) + col_in_tile);\n    return new_col * m_32 + new_row;\n}\n\n__global__ void quantize_weight_kernel(int8_t*      dst,\n                                       const float* src,\n                                       const float* amax,\n                                       const int    n,\n                                       const int    k,\n                                       const int    format,\n                                       const int    scale_is_vector)\n{\n    int tid     = (blockIdx.x * blockDim.x + threadIdx.x);\n    int col_idx = tid / n;\n    int row_idx = tid - col_idx * n;\n    int new_idx;\n    if (format == 0) {\n        new_idx = row_idx * k + col_idx;\n    }\n    else if (format == 1) {\n        new_idx = index_CUBLASLT_ORDER_COL32_2R_4R4(col_idx, row_idx, 32 * n);\n    }\n    else if (format == 2) {\n        new_idx = index_CUBLASLT_ORDER_COL4_4R2_8C(col_idx, row_idx, 32 * n);\n    }\n    if (tid < n * k) {\n        int8_t v     = float_to_int8_rn(src[tid] * 127.0 / amax[row_idx * scale_is_vector]);\n        dst[new_idx] = v;\n    }\n}\n\n__global__ void quantize_weight_kernel(int8_t*      dst,\n                                       const half*  src,\n                                       const float* amax,\n                                       const int    n,\n                                       const int    k,\n                                       const int    format,\n                                       const int    scale_is_vector)\n{\n    int tid     = (blockIdx.x * blockDim.x + threadIdx.x);\n    int col_idx = tid / n;\n    int row_idx = tid - col_idx * n;\n    int new_idx;\n    if (format == 0) {\n        new_idx = row_idx * k + col_idx;\n    }\n    else if (format == 1) {\n        new_idx = index_CUBLASLT_ORDER_COL32_2R_4R4(col_idx, row_idx, 32 * n);\n    }\n    else if (format == 2) {\n        new_idx = index_CUBLASLT_ORDER_COL4_4R2_8C(col_idx, row_idx, 32 * n);\n    }\n    if (tid < n * k) {\n        int8_t v     = float_to_int8_rn(__half2float(src[tid]) * 127.0 / amax[row_idx * scale_is_vector]);\n        dst[new_idx] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeQuantizeWeight(int8_t*      dst,\n                          const T*     src,\n                          const float* amax,\n                          const int    n,\n                          const int    k,\n                          const int    format,\n                          hipStream_t stream,\n                          const int    scale_is_vector)\n{\n    if (format != 0 && format != 1 & format != 2) {\n        printf(\"[ERROR][invokeQuantizeWeight] format must be one of 0, 1, 2. current value: %d\\n\", format);\n        exit(-1);\n    }\n    if (scale_is_vector != 0 && scale_is_vector != 1) {\n        printf(\"[ERROR][invokeQuantizeWeight] scale_is_vector must be either 0 or 1. current value: %d\\n\",\n               scale_is_vector);\n        exit(-1);\n    }\n    dim3 grid((n * k + 255) / 256);\n    dim3 block(256);\n    if (sizeof(T) == sizeof(float)) {\n        quantize_weight_kernel<<<grid, block, 0, stream>>>(dst, (const float*)src, amax, n, k, format, scale_is_vector);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        quantize_weight_kernel<<<grid, block, 0, stream>>>(dst, (const half*)src, amax, n, k, format, scale_is_vector);\n    }\n}\n\ntemplate void invokeQuantizeWeight<float>(int8_t*      dst,\n                                          const float* src,\n                                          const float* amax,\n                                          const int    n,\n                                          const int    k,\n                                          const int    format,\n                                          hipStream_t stream,\n                                          const int    scale_is_vector);\n\ntemplate void invokeQuantizeWeight<half>(int8_t*      dst,\n                                         const half*  src,\n                                         const float* amax,\n                                         const int    n,\n                                         const int    k,\n                                         const int    format,\n                                         hipStream_t stream,\n                                         const int    scale_is_vector);\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/quantize_weight.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/decoding_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n// static const float HALF_FLT_MAX = 65504.F;\n\ntemplate<typename T>\n__global__ void decodingInitialize(bool*      finished,\n                                   int*       sequence_length,\n                                   int*       word_ids,\n                                   T*         cum_log_probs,\n                                   const int* sentence_ids,\n                                   const int  batch_size,\n                                   const int  beam_width,\n                                   const int  max_input_length)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? (T)HALF_FLT_MAX : (T)1e20f;  // BF16 and FP32 have the same dynamic range\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n        finished[index]        = false;\n        sequence_length[index] = max_input_length;\n        if (word_ids != nullptr) {\n            word_ids[index] = sentence_ids[index / beam_width];\n        }\n        cum_log_probs[index] = (index % beam_width == 0) ? (T)0.0f : (T)-MAX_T_VAL;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDecodingInitialize(bool*        finished,\n                              int*         sequence_length,\n                              int*         word_ids,\n                              T*           cum_log_probs,\n                              const int*   sentence_ids,\n                              const int    batch_size,\n                              const int    beam_width,\n                              const int    max_input_length,\n                              cudaStream_t stream)\n{\n    dim3 grid((int)ceil(batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    decodingInitialize<T><<<grid, block, 0, stream>>>(\n        finished, sequence_length, word_ids, cum_log_probs, sentence_ids, batch_size, beam_width, max_input_length);\n}\n\ntemplate void invokeDecodingInitialize(bool*        finished,\n                                       int*         sequence_length,\n                                       int*         word_ids,\n                                       float*       cum_log_probs,\n                                       const int*   sentence_ids,\n                                       const int    batch_size,\n                                       const int    beam_width,\n                                       const int    max_input_length,\n                                       cudaStream_t stream);\n\ntemplate void invokeDecodingInitialize(bool*        finished,\n                                       int*         sequence_length,\n                                       int*         word_ids,\n                                       half*        cum_log_probs,\n                                       const int*   sentence_ids,\n                                       const int    batch_size,\n                                       const int    beam_width,\n                                       const int    max_input_length,\n                                       cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeDecodingInitialize(bool*          finished,\n                                       int*           sequence_length,\n                                       int*           word_ids,\n                                       __nv_bfloat16* cum_log_probs,\n                                       const int*     sentence_ids,\n                                       const int      batch_size,\n                                       const int      beam_width,\n                                       const int      max_input_length,\n                                       cudaStream_t   stream);\n#endif\n\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T>\n__global__ void embeddingLookupPosEncoding(T*             from_tensor,\n                                           const T*       embedding_table,\n                                           const T*       position_encoding,\n                                           const int*     all_ids,\n                                           const int*     padding_count,\n                                           const int*     input_lengths,\n                                           const int      local_token_num,\n                                           const int64_t  hidden_units,\n                                           const int      step,\n                                           const int      max_input_length,\n                                           const int      token_num,\n                                           const int      ite,\n                                           const T        scale)\n{\n    // 1. lookup from embedding table\n    // 2. multiply scale\n    // 3. add the position encoding\n    const int id_offset = step * token_num + ite * local_token_num;\n\n    const bool use_padding_count = padding_count != nullptr;\n    const bool use_input_len     = input_lengths != nullptr;\n\n    for (int64_t index = blockIdx.x * blockDim.x + threadIdx.x; index < local_token_num * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        const int row_index   = index / hidden_units;\n        const int col_index   = index % hidden_units;\n        int       step_offset = step;\n        if (use_padding_count) {\n            step_offset -= padding_count[row_index];\n        }\n        else if (use_input_len) {\n            step_offset -= max_input_length - input_lengths[row_index];\n        }\n        step_offset *= hidden_units;\n\n        T val = embedding_table[all_ids[id_offset + row_index] * hidden_units + col_index] * scale;\n        val   = val + position_encoding[step_offset + col_index];\n\n        from_tensor[index] = val;\n    }\n}\n\n// No absolute position embedding\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T, int PROMPT_SRC>\n__global__ void embeddingLookup(T*                    from_tensor,\n                                const T*              embedding_table,\n                                const int*            all_ids,\n                                pPromptTuningParam<T> prompt_param,\n                                const int             local_token_num,\n                                const int64_t         hidden_units,\n                                const int             step,\n                                const int             token_num,\n                                const int             ite,\n                                const int             seq_len,\n                                const T               scale)\n{\n    // 1. lookup from embedding table\n    // 2. multiply scale\n    const int id_offset = step * token_num + ite * local_token_num;\n\n    for (int64_t index = blockIdx.x * blockDim.x + threadIdx.x; index < local_token_num * hidden_units;\n         index += blockDim.x * gridDim.x) {\n\n        const int word_index     = index / hidden_units;\n        const int word_index_row = word_index / seq_len;  // batch_id\n        const int col_index      = index % hidden_units;\n        const int input_id       = all_ids == nullptr ? word_index : all_ids[id_offset + word_index];\n        const int prompt_id      = input_id - prompt_param.p_prompt_tuning_id_start;\n        T         embedding      = (T)0.0f;\n        if (PROMPT_SRC > 0 && prompt_id >= 0) {\n            if (PROMPT_SRC == 1) {\n                // from loaded prompt embedding tables\n                embedding =\n                    prompt_param.p_prompt_tuning_batch_weights[word_index_row][prompt_id * hidden_units + col_index];\n            }\n            else {\n                // from request prompt embedding\n                embedding =\n                    prompt_param\n                        .request_prompt_embedding[word_index_row * prompt_param.request_prompt_max_length * hidden_units\n                                                  + prompt_id * hidden_units + col_index];\n            }\n        }\n        else {\n            embedding = embedding_table[input_id * hidden_units + col_index];\n        }\n        from_tensor[index] = embedding * scale;\n    }\n}\n\n#define EMBEDDING_LOOKUP(PROMPT_SRC)                                                                                   \\\n    embeddingLookup<T, PROMPT_SRC><<<grid, block, 0, stream>>>(from_tensor,                                            \\\n                                                               embedding_table,                                        \\\n                                                               all_ids,                                                \\\n                                                               prompt_param,                                           \\\n                                                               local_token_num,                                        \\\n                                                               hidden_units,                                           \\\n                                                               step,                                                   \\\n                                                               token_num,                                              \\\n                                                               ite,                                                    \\\n                                                               seq_len,                                                \\\n                                                               scale);\n\n/* Adapter function for invokeEmbeddingLookupPosEncoding{PadCount,InputLen} */\ntemplate<typename T>\nvoid invokeEmbeddingLookupPosEncoding(T*                    from_tensor,\n                                      const T*              embedding_table,\n                                      const T*              position_encoding,\n                                      const int*            all_ids,\n                                      const int*            padding_count,\n                                      const int*            input_lengths,\n                                      pPromptTuningParam<T> prompt_param,\n                                      const int             local_token_num,\n                                      const int             hidden_units,\n                                      const T               scale,\n                                      const int             step,\n                                      const int             max_input_length,\n                                      const int             token_num,\n                                      const int             ite,\n                                      const int             seq_len,\n                                      cudaStream_t          stream)\n{\n    dim3 grid(min(local_token_num, 65536));\n    dim3 block(min(hidden_units, 1024));\n    if (position_encoding != nullptr) {\n        FT_CHECK_WITH_INFO(prompt_param.use_request_p_prompt_embedding == false\n                               && prompt_param.p_prompt_tuning_batch_weights == nullptr,\n                           fmtstr(\"embeddingLookupPosEncoding still not support prompt tuning\"));\n        embeddingLookupPosEncoding<T><<<grid, block, 0, stream>>>(from_tensor,\n                                                                  embedding_table,\n                                                                  position_encoding,\n                                                                  all_ids,\n                                                                  padding_count,\n                                                                  input_lengths,\n                                                                  local_token_num,\n                                                                  hidden_units,\n                                                                  step,\n                                                                  max_input_length,\n                                                                  token_num,\n                                                                  ite,\n                                                                  scale);\n    }\n    else {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            EMBEDDING_LOOKUP(2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            EMBEDDING_LOOKUP(1);\n        }\n        else {\n            EMBEDDING_LOOKUP(0);\n        }\n    }\n}\n\n#undef EMBEDDING_LOOKUP\n\ntemplate<typename T>\nvoid invokeEmbeddingLookupPosEncodingPadCount(T*                    from_tensor,\n                                              const T*              embedding_table,\n                                              const T*              position_encoding,\n                                              const int*            all_ids,\n                                              const int*            pad_count,\n                                              pPromptTuningParam<T> prompt_param,\n                                              const int             local_token_num,\n                                              const int             hidden_units,\n                                              const T               scale,\n                                              const int             step,\n                                              const int             token_num,\n                                              const int             ite,\n                                              const int             seq_len,\n                                              cudaStream_t          stream)\n{\n    invokeEmbeddingLookupPosEncoding<T>(from_tensor,\n                                        embedding_table,\n                                        position_encoding,\n                                        all_ids,\n                                        pad_count,\n                                        nullptr,\n                                        prompt_param,\n                                        local_token_num,\n                                        hidden_units,\n                                        scale,\n                                        step,\n                                        0,\n                                        token_num,\n                                        ite,\n                                        seq_len,\n                                        stream);\n}\n\n#define INSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(T)                                                                   \\\n    template void invokeEmbeddingLookupPosEncodingPadCount(T*                    from_tensor,                          \\\n                                                           const T*              embedding_table,                      \\\n                                                           const T*              position_encoding,                    \\\n                                                           const int*            all_ids,                              \\\n                                                           const int*            pad_count,                            \\\n                                                           pPromptTuningParam<T> prompt_param,                         \\\n                                                           const int             local_token_num,                      \\\n                                                           const int             hidden_units,                         \\\n                                                           const T               scale,                                \\\n                                                           const int             step,                                 \\\n                                                           const int             token_num,                            \\\n                                                           const int             ite,                                  \\\n                                                           const int             seq_len,                              \\\n                                                           cudaStream_t          stream)\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(float);\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT\n\ntemplate<typename T>\n__global__ void paddingEmbedding(T*            padded_embedding_kernel,\n                                 T*            padded_embedding_bias,\n                                 const T*      embedding_kernel,\n                                 const T*      embedding_bias,\n                                 const int64_t hidden_unit,\n                                 const int64_t vocab_size,\n                                 const int64_t vocab_size_padded)\n{\n    for (int64_t id = threadIdx.x + blockIdx.x * blockDim.x; id < hidden_unit * vocab_size_padded;\n         id += blockDim.x * gridDim.x) {\n        int row_id = id / vocab_size_padded;\n        int col_id = id % vocab_size_padded;\n        if (col_id < vocab_size) {\n            padded_embedding_kernel[id] = embedding_kernel[row_id * vocab_size + col_id];\n        }\n        else {\n            padded_embedding_kernel[id] = (T)(0.0f);\n        }\n    }\n\n    for (int id = threadIdx.x + blockIdx.x * blockDim.x; id < vocab_size_padded; id += blockDim.x * gridDim.x) {\n        if (id < vocab_size) {\n            padded_embedding_bias[id] = embedding_bias[id];\n        }\n        else {\n            padded_embedding_bias[id] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokePaddingEmbedding(T*           padded_embedding_kernel,\n                            T*           padded_embedding_bias,\n                            const T*     embedding_kernel,\n                            const T*     embedding_bias,\n                            const int    hidden_unit,\n                            const int    vocab_size,\n                            const int    vocab_size_padded,\n                            cudaStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(hidden_unit * vocab_size_padded / 512.)));\n    paddingEmbedding<<<grid, block, 0, stream>>>(padded_embedding_kernel,\n                                                 padded_embedding_bias,\n                                                 embedding_kernel,\n                                                 embedding_bias,\n                                                 hidden_unit,\n                                                 vocab_size,\n                                                 vocab_size_padded);\n}\n\ntemplate void invokePaddingEmbedding(float*       padded_embedding_kernel,\n                                     float*       padded_embedding_bias,\n                                     const float* embedding_kernel,\n                                     const float* embedding_bias,\n                                     const int    hidden_unit,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     cudaStream_t stream);\n\ntemplate void invokePaddingEmbedding(half*        padded_embedding_kernel,\n                                     half*        padded_embedding_bias,\n                                     const half*  embedding_kernel,\n                                     const half*  embedding_bias,\n                                     const int    hidden_unit,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokePaddingEmbedding(__nv_bfloat16*       padded_embedding_kernel,\n                                     __nv_bfloat16*       padded_embedding_bias,\n                                     const __nv_bfloat16* embedding_kernel,\n                                     const __nv_bfloat16* embedding_bias,\n                                     const int            hidden_unit,\n                                     const int            vocab_size,\n                                     const int            vocab_size_padded,\n                                     cudaStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void paddingEmbeddingKernel(T*        padded_embedding_kernel,\n                                       const T*  embedding_kernel,\n                                       const int hidden_unit,\n                                       const int vocab_size,\n                                       const int vocab_size_padded)\n{\n    for (int id = threadIdx.x + blockIdx.x * blockDim.x; id < hidden_unit * vocab_size_padded;\n         id += blockDim.x * gridDim.x) {\n        int row_id = id / hidden_unit;\n        int col_id = id % hidden_unit;\n        if (row_id < vocab_size) {\n            padded_embedding_kernel[id] = embedding_kernel[row_id * hidden_unit + col_id];\n        }\n        else {\n            padded_embedding_kernel[id] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokePaddingEmbeddingKernel(T*           padded_embedding_kernel,\n                                  const T*     embedding_kernel,\n                                  const int    hidden_unit,\n                                  const int    vocab_size,\n                                  const int    vocab_size_padded,\n                                  cudaStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(hidden_unit * vocab_size_padded / 512.)));\n    paddingEmbeddingKernel<<<grid, block, 0, stream>>>(\n        padded_embedding_kernel, embedding_kernel, hidden_unit, vocab_size, vocab_size_padded);\n}\n\ntemplate void invokePaddingEmbeddingKernel(float*       padded_embedding_kernel,\n                                           const float* embedding_kernel,\n                                           const int    hidden_unit,\n                                           const int    vocab_size,\n                                           const int    vocab_size_padded,\n                                           cudaStream_t stream);\n\ntemplate void invokePaddingEmbeddingKernel(half*        padded_embedding_kernel,\n                                           const half*  embedding_kernel,\n                                           const int    hidden_unit,\n                                           const int    vocab_size,\n                                           const int    vocab_size_padded,\n                                           cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokePaddingEmbeddingKernel(__nv_bfloat16*       padded_embedding_kernel,\n                                           const __nv_bfloat16* embedding_kernel,\n                                           const int            hidden_unit,\n                                           const int            vocab_size,\n                                           const int            vocab_size_padded,\n                                           cudaStream_t         stream);\n#endif\n\n__global__ void gatherTree(gatherTreeParam param)\n{\n    //  PREFIX SOFT PROMPT\n    //  beam: have six parts\n    //      [prompt | input | input_padding | prompt_padding | generated output | padding (use end_token)]\n    //  parents: have five parts\n    //      [prompt | input | input_padding | prompt_padding | generated output | padding (use 0)]\n    //  step_ids: need to remove prompt, input_padding and prompt_padding\n    //      the shape is [input_length + requested_output_length, bs, beam_width]\n    //      need to transpose to output_ids [bs, beam_width, input_length + requested_output_length]\n    //  max_input_length: input + input_padding + prompt_padding\n\n    //  P/PROMPT TUNING\n    //  NOTE: input (real ids | prompt virtual ids) have already been preprocessed during embedding lookup, no prompt\n    //  templates now beam: [input (real ids | prompt virtual ids) | input_padding | generated output | padding (use\n    //  end_token)] parents: [input (real ids | prompt virtual ids) | input_padding | generated output | padding (use\n    //  0)] step_ids: need to remove virtual prompt ids in input ids\n    //      the shape is [input_length (real input length, prompt length) + requested_output_length, bs, beam_width]\n    //      need to transpose to output_ids [bs, beam_width, input_length + requested_output_length]\n    //  max_input_length: input (real ids | prompt virtual ids) + input_padding\n\n    const int max_input_length = param.input_lengths == nullptr ? 0 : param.max_input_length;\n\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < param.batch_size * param.beam_width;\n         i += gridDim.x * blockDim.x) {\n        const int batch = i / param.beam_width;\n        const int beam  = i % param.beam_width;\n        const int prompt_len =\n            param.prefix_soft_prompt_lengths == nullptr ? 0 : param.prefix_soft_prompt_lengths[batch];\n        int input_len = param.input_lengths == nullptr ? 0 : param.input_lengths[i];\n        // virtual prompts mean the prompt embedded in input ids (with prompt templates) [p/prompt tuning]\n        const int virtual_prompt_length =\n            param.p_prompt_tuning_prompt_lengths == nullptr ? 0 : param.p_prompt_tuning_prompt_lengths[batch];\n        // real input length (without virtual prompts) [p/prompt tuning]\n        input_len -= virtual_prompt_length;\n\n        const int* parent_ids = param.parent_ids;\n        const int* step_ids   = param.step_ids;\n\n        // TODO(bhsueh) optimize the reduce_max operation for large beam_width\n        int  max_len                      = -1;\n        bool update_response_input_length = param.response_input_lengths != nullptr;\n        // int selected_beam_index = 0;\n        for (int j = 0; j < param.beam_width; j++) {\n            int tmp_len =\n                param.max_sequence_lengths[batch * param.beam_width + j] + param.max_sequence_length_final_step;\n            // also remove the length of the soft prompts, p_prompt_tuning\n            param.max_sequence_lengths[batch * param.beam_width + j] =\n                tmp_len - param.max_prefix_soft_prompt_length\n                - (param.max_input_length - param.max_input_without_prompt_length);\n            // update the response input length\n            if (update_response_input_length) {\n                param.response_input_lengths[batch * param.beam_width + j] = input_len - prompt_len;\n            }\n            if (tmp_len > max_len) {\n                max_len = tmp_len;\n                // selected_beam_index = j;\n            }\n        }\n        const int max_seq_len_b = min(param.max_time, max_len);\n        if (max_seq_len_b <= 0) {\n            continue;\n        }\n\n#define GET_IX(time_ix, beam_ix)                                                                                       \\\n    (param.batch_size * param.beam_width * (time_ix) + param.beam_width * batch + (beam_ix))\n\n        const int padding_offset_and_prompt_offset = max_input_length - input_len + prompt_len;\n        const int initial_tgt_ix                   = GET_IX(max_seq_len_b - 1 - padding_offset_and_prompt_offset, beam);\n        const int initial_parent_ix                = GET_IX(max_seq_len_b - 1, beam);\n        param.beams[initial_tgt_ix]                = __ldg(step_ids + initial_parent_ix);\n        int  parent    = parent_ids == nullptr ? 0 : __ldg(parent_ids + initial_parent_ix) % param.beam_width;\n        bool found_bad = false;\n\n        for (int level = max_seq_len_b - 2; level >= 0; --level) {\n            if (level < prompt_len || (level >= input_len && level < max_input_length)) {\n                continue;\n            }\n            int tgt_level = level >= max_input_length ? level - padding_offset_and_prompt_offset : level - prompt_len;\n            const int level_beam_ix   = GET_IX(tgt_level, beam);\n            const int level_parent_ix = GET_IX(level, parent);\n            if (parent < 0 || parent > param.beam_width) {\n                // param.beams[level_beam_ix] = -1;\n                param.beams[level_beam_ix] = param.end_tokens[batch];\n                parent                     = -1;\n                found_bad                  = true;\n            }\n            else {\n                param.beams[level_beam_ix] = __ldg(step_ids + level_parent_ix);\n                parent = parent_ids == nullptr ? 0 : __ldg(parent_ids + level_parent_ix) % param.beam_width;\n            }\n        }\n\n        // set the padded part as end_token\n        // input_len\n        for (int index = max_len - padding_offset_and_prompt_offset;\n             index < param.max_time - param.max_prefix_soft_prompt_length;\n             ++index) {\n            param.beams[GET_IX(index, beam)] = param.end_tokens[batch];\n        }\n\n        // Not necessary when using a BeamSearchDecoder, but necessary\n        // when a user feeds in possibly broken trajectory (i.e., non-eos\n        // entries in a beam following eos entries).\n        if (!found_bad) {\n            bool finished = false;\n            // skip the step 0 because it is often the start token\n            int start_step = max_input_length == 0 ? 1 : max_input_length;\n            for (int time = start_step; time < max_seq_len_b; ++time) {\n                const int level_beam_ix = GET_IX(time, beam);\n                if (finished) {\n                    param.beams[level_beam_ix] = param.end_tokens[batch];\n                }\n                else if (param.beams[level_beam_ix] == param.end_tokens[batch]) {\n                    finished = true;\n                }\n            }\n        }\n#undef GET_IX\n\n        // transpose on output_ids\n        // remove p_prompt tuning virtual tokens (end tokens)\n        int actual_output_length = param.max_time - param.max_prefix_soft_prompt_length\n                                   - (param.max_input_length - param.max_input_without_prompt_length);\n        if (param.output_ids != nullptr) {\n            for (int j = 0; j < actual_output_length; j++) {\n                param.output_ids[i * actual_output_length + j] =\n                    param.beams[j * param.batch_size * param.beam_width + i];\n            }\n        }\n    }\n}\n\nvoid invokeGatherTree(int*         beams,\n                      int*         max_sequence_lengths,\n                      const int    max_time,\n                      const int    batch_size,\n                      const int    beam_width,\n                      const int*   step_ids,\n                      const int*   parent_ids,\n                      const int*   end_tokens,\n                      cudaStream_t stream)\n{\n    gatherTreeParam param;\n    param.beams                      = beams;\n    param.max_sequence_lengths       = max_sequence_lengths;\n    param.max_time                   = max_time;\n    param.batch_size                 = batch_size;\n    param.beam_width                 = beam_width;\n    param.step_ids                   = step_ids;\n    param.parent_ids                 = parent_ids;\n    param.end_tokens                 = end_tokens;\n    param.max_input_length           = 1;\n    param.prefix_soft_prompt_lengths = nullptr;\n    param.stream                     = stream;\n    invokeGatherTree(param);\n}\n\nvoid invokeGatherTree(int*         beams,\n                      int*         max_sequence_lengths,\n                      const int    max_time,\n                      const int    batch_size,\n                      const int    beam_width,\n                      const int*   step_ids,\n                      const int*   parent_ids,\n                      const int*   end_tokens,\n                      const int    max_input_length,\n                      cudaStream_t stream)\n{\n    gatherTreeParam param;\n    param.beams                      = beams;\n    param.max_sequence_lengths       = max_sequence_lengths;\n    param.max_time                   = max_time;\n    param.batch_size                 = batch_size;\n    param.beam_width                 = beam_width;\n    param.step_ids                   = step_ids;\n    param.parent_ids                 = parent_ids;\n    param.end_tokens                 = end_tokens;\n    param.max_input_length           = max_input_length;\n    param.prefix_soft_prompt_lengths = nullptr;\n    param.stream                     = stream;\n    invokeGatherTree(param);\n}\n\nvoid invokeGatherTree(gatherTreeParam param)\n{\n    int  batchbeam = param.batch_size * param.beam_width;\n    dim3 grid(1), block(batchbeam);\n    // though decoder do not support > 1024 for now\n    if (batchbeam > 1024) {\n        grid.x  = ceil(param.batch_size * param.beam_width / 1024.);\n        block.x = 1024;\n    }\n    gatherTree<<<grid, block, 0, param.stream>>>(param);\n}\n\n__global__ void minusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < token_num; i += blockDim.x * gridDim.x) {\n        if (finished[i] == false) {\n            sequence_lengths[i] -= 1;\n        }\n    }\n}\n\nvoid invokeMinusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num, cudaStream_t stream)\n{\n    dim3 block(min(256, token_num));\n    dim3 grid(ceil(token_num / 256.));\n    minusUnfinishedSeqlen<<<block, grid, 0, stream>>>(sequence_lengths, finished, token_num);\n}\n\n__global__ void plusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < token_num; i += blockDim.x * gridDim.x) {\n        if (finished[i] == false) {\n            sequence_lengths[i] += 1;\n        }\n    }\n}\n\nvoid invokePlusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num, cudaStream_t stream)\n{\n    dim3 block(min(256, token_num));\n    dim3 grid(ceil(token_num / 256.));\n    plusUnfinishedSeqlen<<<block, grid, 0, stream>>>(sequence_lengths, finished, token_num);\n}\n\ntemplate<typename T>\n__global__ void plusScalar(T* buf, const T val, const int size)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        buf[i] += val;\n    }\n}\n\ntemplate<typename T>\nvoid invokePlusScalar(T* buf, const T val, const int size, cudaStream_t stream)\n{\n    dim3 block(min(256, size));\n    dim3 grid(ceil(size / 256.));\n    plusScalar<<<block, grid, 0, stream>>>(buf, val, size);\n}\n\ntemplate void invokePlusScalar(int* buf, const int val, const int size, cudaStream_t stream);\n\n__global__ void finalize(int*         output_ids,\n                         int*         sequence_lengths,\n                         float*       cum_log_probs,\n                         float*       output_log_probs,\n                         const int*   topk_output_ids,\n                         const int*   topk_sequence_lengths,\n                         const float* scores,\n                         const float* topk_cum_log_probs,\n                         const float* topk_log_probs,\n                         const int*   num_beams,\n                         const int    beam_width,\n                         const int    max_seq_len)\n{\n    // output_ids: [bs, beam_width, max_seq_len]\n    // sequence_lengths: [bs, beam_width]\n    // cum_log_probs: [bs, beam_width]\n    // output_log_probs: [bs, beam_width, max_seq_len]\n    // topk_output_ids: [bs, 2 * beam_width, max_seq_len + 1]\n    // topk_sequence_lengths: [bs, 2 * beam_width]\n    // scores: [bs, 2 * beam_width]\n    // topk_cum_log_probs: [bs, 2 * beam_width]\n    // topk_log_probs: [bs, 2 * beam_width, max_seq_len + 1]\n    // num_beams: [bs]\n\n    // This kernel do a sorting for scores first, and then put the topk_output_ids\n    // into output_ids by the rank of scores.\n    // Note that we remove the start_token (the id at first position) from topk_output_ids\n\n    extern __shared__ char array[];\n    int*                   rank     = (int*)(array);\n    float*                 s_scores = (float*)(rank + beam_width);\n    if (threadIdx.x < num_beams[blockIdx.x]) {\n        s_scores[threadIdx.x] = scores[blockIdx.x * beam_width * 2 + threadIdx.x];\n    }\n    __syncthreads();\n\n    for (int i = 0; i < beam_width; i++) {\n        float score     = threadIdx.x < num_beams[blockIdx.x] ? s_scores[threadIdx.x] : -FLT_MAX;\n        float max_score = blockReduceMax<float>(score);\n\n        if (threadIdx.x == 0) {\n            for (int j = 0; j < beam_width * 2; j++) {\n                if (s_scores[j] == max_score) {\n                    rank[i]     = j;\n                    s_scores[j] = -FLT_MAX;\n                    break;\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x < beam_width) {\n        sequence_lengths[blockIdx.x * beam_width + threadIdx.x] =\n            topk_sequence_lengths[blockIdx.x * beam_width * 2 + rank[threadIdx.x]];\n        if (cum_log_probs != nullptr) {\n            cum_log_probs[blockIdx.x * beam_width + threadIdx.x] =\n                topk_cum_log_probs[blockIdx.x * beam_width * 2 + rank[threadIdx.x]];\n        }\n    }\n    for (int beam_idx = 0; beam_idx < beam_width; beam_idx++) {\n        // start from step 1 to skip the start token\n        for (int i = threadIdx.x; i < sequence_lengths[blockIdx.x * beam_width + beam_idx]; i += blockDim.x) {\n            output_ids[blockIdx.x * beam_width * max_seq_len + beam_idx * max_seq_len + i] =\n                topk_output_ids[blockIdx.x * (beam_width * 2) * (max_seq_len + 1) + rank[beam_idx] * (max_seq_len + 1)\n                                + (i + 1)];\n            if (output_log_probs != nullptr) {\n                output_log_probs[blockIdx.x * beam_width * max_seq_len + beam_idx * max_seq_len + i] =\n                    topk_log_probs[blockIdx.x * (beam_width * 2) * (max_seq_len + 1)\n                                   + rank[beam_idx] * (max_seq_len + 1) + (i + 1)];\n            }\n        }\n    }\n}\n\nvoid invokeFinalize(int*         output_ids,\n                    int*         sequence_lengths,\n                    float*       cum_log_probs,\n                    float*       output_log_probs,\n                    const int*   topk_output_ids,\n                    const int*   topk_sequence_lengths,\n                    const float* scores,\n                    const float* topk_cum_log_probs,\n                    const float* topk_log_probs,\n                    const int*   num_beams,\n                    const int    beam_width,\n                    const int    max_seq_len,\n                    const int    batch_size,\n                    cudaStream_t stream)\n{\n    dim3 block(beam_width * 2);\n    block.x = (block.x + 31) / 32 * 32;\n    FT_CHECK(block.x < 1024);\n    finalize<<<batch_size, block, beam_width * sizeof(int) + (beam_width * 2) * sizeof(float), stream>>>(\n        output_ids,\n        sequence_lengths,\n        cum_log_probs,\n        output_log_probs,\n        topk_output_ids,\n        topk_sequence_lengths,\n        scores,\n        topk_cum_log_probs,\n        topk_log_probs,\n        num_beams,\n        beam_width,\n        max_seq_len);\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/decoding_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n// static const float HALF_FLT_MAX = 65504.F;\n\ntemplate<typename T>\n__global__ void decodingInitialize(bool*      finished,\n                                   int*       sequence_length,\n                                   int*       word_ids,\n                                   T*         cum_log_probs,\n                                   const int* sentence_ids,\n                                   const int  batch_size,\n                                   const int  beam_width,\n                                   const int  max_input_length)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? (T)HALF_FLT_MAX : (T)1e20f;  // BF16 and FP32 have the same dynamic range\n    for (int index = blockIdx.x * blockDim.x + threadIdx.x; index < batch_size * beam_width;\n         index += blockDim.x * gridDim.x) {\n        finished[index]        = false;\n        sequence_length[index] = max_input_length;\n        if (word_ids != nullptr) {\n            word_ids[index] = sentence_ids[index / beam_width];\n        }\n        cum_log_probs[index] = (index % beam_width == 0) ? (T)0.0f : (T)-MAX_T_VAL;\n    }\n}\n\ntemplate<typename T>\nvoid invokeDecodingInitialize(bool*        finished,\n                              int*         sequence_length,\n                              int*         word_ids,\n                              T*           cum_log_probs,\n                              const int*   sentence_ids,\n                              const int    batch_size,\n                              const int    beam_width,\n                              const int    max_input_length,\n                              hipStream_t stream)\n{\n    dim3 grid((int)ceil(batch_size * beam_width * 1.0 / 256));\n    dim3 block(256);\n\n    decodingInitialize<T><<<grid, block, 0, stream>>>(\n        finished, sequence_length, word_ids, cum_log_probs, sentence_ids, batch_size, beam_width, max_input_length);\n}\n\ntemplate void invokeDecodingInitialize(bool*        finished,\n                                       int*         sequence_length,\n                                       int*         word_ids,\n                                       float*       cum_log_probs,\n                                       const int*   sentence_ids,\n                                       const int    batch_size,\n                                       const int    beam_width,\n                                       const int    max_input_length,\n                                       hipStream_t stream);\n\ntemplate void invokeDecodingInitialize(bool*        finished,\n                                       int*         sequence_length,\n                                       int*         word_ids,\n                                       half*        cum_log_probs,\n                                       const int*   sentence_ids,\n                                       const int    batch_size,\n                                       const int    beam_width,\n                                       const int    max_input_length,\n                                       hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeDecodingInitialize(bool*          finished,\n                                       int*           sequence_length,\n                                       int*           word_ids,\n                                       __hip_bfloat16* cum_log_probs,\n                                       const int*     sentence_ids,\n                                       const int      batch_size,\n                                       const int      beam_width,\n                                       const int      max_input_length,\n                                       hipStream_t   stream);\n#endif\n\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T>\n__global__ void embeddingLookupPosEncoding(T*             from_tensor,\n                                           const T*       embedding_table,\n                                           const T*       position_encoding,\n                                           const int*     all_ids,\n                                           const int*     padding_count,\n                                           const int*     input_lengths,\n                                           const int      local_token_num,\n                                           const int64_t  hidden_units,\n                                           const int      step,\n                                           const int      max_input_length,\n                                           const int      token_num,\n                                           const int      ite,\n                                           const T        scale)\n{\n    // 1. lookup from embedding table\n    // 2. multiply scale\n    // 3. add the position encoding\n    const int id_offset = step * token_num + ite * local_token_num;\n\n    const bool use_padding_count = padding_count != nullptr;\n    const bool use_input_len     = input_lengths != nullptr;\n\n    for (int64_t index = blockIdx.x * blockDim.x + threadIdx.x; index < local_token_num * hidden_units;\n         index += blockDim.x * gridDim.x) {\n        const int row_index   = index / hidden_units;\n        const int col_index   = index % hidden_units;\n        int       step_offset = step;\n        if (use_padding_count) {\n            step_offset -= padding_count[row_index];\n        }\n        else if (use_input_len) {\n            step_offset -= max_input_length - input_lengths[row_index];\n        }\n        step_offset *= hidden_units;\n\n        T val = embedding_table[all_ids[id_offset + row_index] * hidden_units + col_index] * scale;\n        val   = val + position_encoding[step_offset + col_index];\n\n        from_tensor[index] = val;\n    }\n}\n\n// No absolute position embedding\n// PROMPT_SRC: 0 --> no prompts, 1 --> from loaded prompts, 2 --> from request prompts\ntemplate<typename T, int PROMPT_SRC>\n__global__ void embeddingLookup(T*                    from_tensor,\n                                const T*              embedding_table,\n                                const int*            all_ids,\n                                pPromptTuningParam<T> prompt_param,\n                                const int             local_token_num,\n                                const int64_t         hidden_units,\n                                const int             step,\n                                const int             token_num,\n                                const int             ite,\n                                const int             seq_len,\n                                const T               scale)\n{\n    // 1. lookup from embedding table\n    // 2. multiply scale\n    const int id_offset = step * token_num + ite * local_token_num;\n\n    for (int64_t index = blockIdx.x * blockDim.x + threadIdx.x; index < local_token_num * hidden_units;\n         index += blockDim.x * gridDim.x) {\n\n        const int word_index     = index / hidden_units;\n        const int word_index_row = word_index / seq_len;  // batch_id\n        const int col_index      = index % hidden_units;\n        const int input_id       = all_ids == nullptr ? word_index : all_ids[id_offset + word_index];\n        const int prompt_id      = input_id - prompt_param.p_prompt_tuning_id_start;\n        T         embedding      = (T)0.0f;\n        if (PROMPT_SRC > 0 && prompt_id >= 0) {\n            if (PROMPT_SRC == 1) {\n                // from loaded prompt embedding tables\n                embedding =\n                    prompt_param.p_prompt_tuning_batch_weights[word_index_row][prompt_id * hidden_units + col_index];\n            }\n            else {\n                // from request prompt embedding\n                embedding =\n                    prompt_param\n                        .request_prompt_embedding[word_index_row * prompt_param.request_prompt_max_length * hidden_units\n                                                  + prompt_id * hidden_units + col_index];\n            }\n        }\n        else {\n            embedding = embedding_table[input_id * hidden_units + col_index];\n        }\n        from_tensor[index] = embedding * scale;\n    }\n}\n\n#define EMBEDDING_LOOKUP(PROMPT_SRC)                                                                                   \\\n    embeddingLookup<T, PROMPT_SRC><<<grid, block, 0, stream>>>(from_tensor,                                            \\\n                                                               embedding_table,                                        \\\n                                                               all_ids,                                                \\\n                                                               prompt_param,                                           \\\n                                                               local_token_num,                                        \\\n                                                               hidden_units,                                           \\\n                                                               step,                                                   \\\n                                                               token_num,                                              \\\n                                                               ite,                                                    \\\n                                                               seq_len,                                                \\\n                                                               scale);\n\n/* Adapter function for invokeEmbeddingLookupPosEncoding{PadCount,InputLen} */\ntemplate<typename T>\nvoid invokeEmbeddingLookupPosEncoding(T*                    from_tensor,\n                                      const T*              embedding_table,\n                                      const T*              position_encoding,\n                                      const int*            all_ids,\n                                      const int*            padding_count,\n                                      const int*            input_lengths,\n                                      pPromptTuningParam<T> prompt_param,\n                                      const int             local_token_num,\n                                      const int             hidden_units,\n                                      const T               scale,\n                                      const int             step,\n                                      const int             max_input_length,\n                                      const int             token_num,\n                                      const int             ite,\n                                      const int             seq_len,\n                                      hipStream_t          stream)\n{\n    dim3 grid(min(local_token_num, 65536));\n    dim3 block(min(hidden_units, 1024));\n    if (position_encoding != nullptr) {\n        FT_CHECK_WITH_INFO(prompt_param.use_request_p_prompt_embedding == false\n                               && prompt_param.p_prompt_tuning_batch_weights == nullptr,\n                           fmtstr(\"embeddingLookupPosEncoding still not support prompt tuning\"));\n        embeddingLookupPosEncoding<T><<<grid, block, 0, stream>>>(from_tensor,\n                                                                  embedding_table,\n                                                                  position_encoding,\n                                                                  all_ids,\n                                                                  padding_count,\n                                                                  input_lengths,\n                                                                  local_token_num,\n                                                                  hidden_units,\n                                                                  step,\n                                                                  max_input_length,\n                                                                  token_num,\n                                                                  ite,\n                                                                  scale);\n    }\n    else {\n        if (prompt_param.use_request_p_prompt_embedding) {\n            EMBEDDING_LOOKUP(2);\n        }\n        else if (prompt_param.p_prompt_tuning_batch_weights != nullptr) {\n            EMBEDDING_LOOKUP(1);\n        }\n        else {\n            EMBEDDING_LOOKUP(0);\n        }\n    }\n}\n\n#undef EMBEDDING_LOOKUP\n\ntemplate<typename T>\nvoid invokeEmbeddingLookupPosEncodingPadCount(T*                    from_tensor,\n                                              const T*              embedding_table,\n                                              const T*              position_encoding,\n                                              const int*            all_ids,\n                                              const int*            pad_count,\n                                              pPromptTuningParam<T> prompt_param,\n                                              const int             local_token_num,\n                                              const int             hidden_units,\n                                              const T               scale,\n                                              const int             step,\n                                              const int             token_num,\n                                              const int             ite,\n                                              const int             seq_len,\n                                              hipStream_t          stream)\n{\n    invokeEmbeddingLookupPosEncoding<T>(from_tensor,\n                                        embedding_table,\n                                        position_encoding,\n                                        all_ids,\n                                        pad_count,\n                                        nullptr,\n                                        prompt_param,\n                                        local_token_num,\n                                        hidden_units,\n                                        scale,\n                                        step,\n                                        0,\n                                        token_num,\n                                        ite,\n                                        seq_len,\n                                        stream);\n}\n\n#define INSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(T)                                                                   \\\n    template void invokeEmbeddingLookupPosEncodingPadCount(T*                    from_tensor,                          \\\n                                                           const T*              embedding_table,                      \\\n                                                           const T*              position_encoding,                    \\\n                                                           const int*            all_ids,                              \\\n                                                           const int*            pad_count,                            \\\n                                                           pPromptTuningParam<T> prompt_param,                         \\\n                                                           const int             local_token_num,                      \\\n                                                           const int             hidden_units,                         \\\n                                                           const T               scale,                                \\\n                                                           const int             step,                                 \\\n                                                           const int             token_num,                            \\\n                                                           const int             ite,                                  \\\n                                                           const int             seq_len,                              \\\n                                                           hipStream_t          stream)\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(float);\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_LOOKUP_POS_ENCODING_PAD_COUNT\n\ntemplate<typename T>\n__global__ void paddingEmbedding(T*            padded_embedding_kernel,\n                                 T*            padded_embedding_bias,\n                                 const T*      embedding_kernel,\n                                 const T*      embedding_bias,\n                                 const int64_t hidden_unit,\n                                 const int64_t vocab_size,\n                                 const int64_t vocab_size_padded)\n{\n    for (int64_t id = threadIdx.x + blockIdx.x * blockDim.x; id < hidden_unit * vocab_size_padded;\n         id += blockDim.x * gridDim.x) {\n        int row_id = id / vocab_size_padded;\n        int col_id = id % vocab_size_padded;\n        if (col_id < vocab_size) {\n            padded_embedding_kernel[id] = embedding_kernel[row_id * vocab_size + col_id];\n        }\n        else {\n            padded_embedding_kernel[id] = (T)(0.0f);\n        }\n    }\n\n    for (int id = threadIdx.x + blockIdx.x * blockDim.x; id < vocab_size_padded; id += blockDim.x * gridDim.x) {\n        if (id < vocab_size) {\n            padded_embedding_bias[id] = embedding_bias[id];\n        }\n        else {\n            padded_embedding_bias[id] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokePaddingEmbedding(T*           padded_embedding_kernel,\n                            T*           padded_embedding_bias,\n                            const T*     embedding_kernel,\n                            const T*     embedding_bias,\n                            const int    hidden_unit,\n                            const int    vocab_size,\n                            const int    vocab_size_padded,\n                            hipStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(hidden_unit * vocab_size_padded / 512.)));\n    paddingEmbedding<<<grid, block, 0, stream>>>(padded_embedding_kernel,\n                                                 padded_embedding_bias,\n                                                 embedding_kernel,\n                                                 embedding_bias,\n                                                 hidden_unit,\n                                                 vocab_size,\n                                                 vocab_size_padded);\n}\n\ntemplate void invokePaddingEmbedding(float*       padded_embedding_kernel,\n                                     float*       padded_embedding_bias,\n                                     const float* embedding_kernel,\n                                     const float* embedding_bias,\n                                     const int    hidden_unit,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     hipStream_t stream);\n\ntemplate void invokePaddingEmbedding(half*        padded_embedding_kernel,\n                                     half*        padded_embedding_bias,\n                                     const half*  embedding_kernel,\n                                     const half*  embedding_bias,\n                                     const int    hidden_unit,\n                                     const int    vocab_size,\n                                     const int    vocab_size_padded,\n                                     hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokePaddingEmbedding(__hip_bfloat16*       padded_embedding_kernel,\n                                     __hip_bfloat16*       padded_embedding_bias,\n                                     const __hip_bfloat16* embedding_kernel,\n                                     const __hip_bfloat16* embedding_bias,\n                                     const int            hidden_unit,\n                                     const int            vocab_size,\n                                     const int            vocab_size_padded,\n                                     hipStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void paddingEmbeddingKernel(T*        padded_embedding_kernel,\n                                       const T*  embedding_kernel,\n                                       const int hidden_unit,\n                                       const int vocab_size,\n                                       const int vocab_size_padded)\n{\n    for (int id = threadIdx.x + blockIdx.x * blockDim.x; id < hidden_unit * vocab_size_padded;\n         id += blockDim.x * gridDim.x) {\n        int row_id = id / hidden_unit;\n        int col_id = id % hidden_unit;\n        if (row_id < vocab_size) {\n            padded_embedding_kernel[id] = embedding_kernel[row_id * hidden_unit + col_id];\n        }\n        else {\n            padded_embedding_kernel[id] = (T)(0.0f);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokePaddingEmbeddingKernel(T*           padded_embedding_kernel,\n                                  const T*     embedding_kernel,\n                                  const int    hidden_unit,\n                                  const int    vocab_size,\n                                  const int    vocab_size_padded,\n                                  hipStream_t stream)\n{\n    dim3 block(512);\n    dim3 grid((int)(ceil(hidden_unit * vocab_size_padded / 512.)));\n    paddingEmbeddingKernel<<<grid, block, 0, stream>>>(\n        padded_embedding_kernel, embedding_kernel, hidden_unit, vocab_size, vocab_size_padded);\n}\n\ntemplate void invokePaddingEmbeddingKernel(float*       padded_embedding_kernel,\n                                           const float* embedding_kernel,\n                                           const int    hidden_unit,\n                                           const int    vocab_size,\n                                           const int    vocab_size_padded,\n                                           hipStream_t stream);\n\ntemplate void invokePaddingEmbeddingKernel(half*        padded_embedding_kernel,\n                                           const half*  embedding_kernel,\n                                           const int    hidden_unit,\n                                           const int    vocab_size,\n                                           const int    vocab_size_padded,\n                                           hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokePaddingEmbeddingKernel(__hip_bfloat16*       padded_embedding_kernel,\n                                           const __hip_bfloat16* embedding_kernel,\n                                           const int            hidden_unit,\n                                           const int            vocab_size,\n                                           const int            vocab_size_padded,\n                                           hipStream_t         stream);\n#endif\n\n__global__ void gatherTree(gatherTreeParam param)\n{\n    //  PREFIX SOFT PROMPT\n    //  beam: have six parts\n    //      [prompt | input | input_padding | prompt_padding | generated output | padding (use end_token)]\n    //  parents: have five parts\n    //      [prompt | input | input_padding | prompt_padding | generated output | padding (use 0)]\n    //  step_ids: need to remove prompt, input_padding and prompt_padding\n    //      the shape is [input_length + requested_output_length, bs, beam_width]\n    //      need to transpose to output_ids [bs, beam_width, input_length + requested_output_length]\n    //  max_input_length: input + input_padding + prompt_padding\n\n    //  P/PROMPT TUNING\n    //  NOTE: input (real ids | prompt virtual ids) have already been preprocessed during embedding lookup, no prompt\n    //  templates now beam: [input (real ids | prompt virtual ids) | input_padding | generated output | padding (use\n    //  end_token)] parents: [input (real ids | prompt virtual ids) | input_padding | generated output | padding (use\n    //  0)] step_ids: need to remove virtual prompt ids in input ids\n    //      the shape is [input_length (real input length, prompt length) + requested_output_length, bs, beam_width]\n    //      need to transpose to output_ids [bs, beam_width, input_length + requested_output_length]\n    //  max_input_length: input (real ids | prompt virtual ids) + input_padding\n\n    const int max_input_length = param.input_lengths == nullptr ? 0 : param.max_input_length;\n\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < param.batch_size * param.beam_width;\n         i += gridDim.x * blockDim.x) {\n        const int batch = i / param.beam_width;\n        const int beam  = i % param.beam_width;\n        const int prompt_len =\n            param.prefix_soft_prompt_lengths == nullptr ? 0 : param.prefix_soft_prompt_lengths[batch];\n        int input_len = param.input_lengths == nullptr ? 0 : param.input_lengths[i];\n        // virtual prompts mean the prompt embedded in input ids (with prompt templates) [p/prompt tuning]\n        const int virtual_prompt_length =\n            param.p_prompt_tuning_prompt_lengths == nullptr ? 0 : param.p_prompt_tuning_prompt_lengths[batch];\n        // real input length (without virtual prompts) [p/prompt tuning]\n        input_len -= virtual_prompt_length;\n\n        const int* parent_ids = param.parent_ids;\n        const int* step_ids   = param.step_ids;\n\n        // TODO(bhsueh) optimize the reduce_max operation for large beam_width\n        int  max_len                      = -1;\n        bool update_response_input_length = param.response_input_lengths != nullptr;\n        // int selected_beam_index = 0;\n        for (int j = 0; j < param.beam_width; j++) {\n            int tmp_len =\n                param.max_sequence_lengths[batch * param.beam_width + j] + param.max_sequence_length_final_step;\n            // also remove the length of the soft prompts, p_prompt_tuning\n            param.max_sequence_lengths[batch * param.beam_width + j] =\n                tmp_len - param.max_prefix_soft_prompt_length\n                - (param.max_input_length - param.max_input_without_prompt_length);\n            // update the response input length\n            if (update_response_input_length) {\n                param.response_input_lengths[batch * param.beam_width + j] = input_len - prompt_len;\n            }\n            if (tmp_len > max_len) {\n                max_len = tmp_len;\n                // selected_beam_index = j;\n            }\n        }\n        const int max_seq_len_b = min(param.max_time, max_len);\n        if (max_seq_len_b <= 0) {\n            continue;\n        }\n\n#define GET_IX(time_ix, beam_ix)                                                                                       \\\n    (param.batch_size * param.beam_width * (time_ix) + param.beam_width * batch + (beam_ix))\n\n        const int padding_offset_and_prompt_offset = max_input_length - input_len + prompt_len;\n        const int initial_tgt_ix                   = GET_IX(max_seq_len_b - 1 - padding_offset_and_prompt_offset, beam);\n        const int initial_parent_ix                = GET_IX(max_seq_len_b - 1, beam);\n        param.beams[initial_tgt_ix]                = __ldg(step_ids + initial_parent_ix);\n        int  parent    = parent_ids == nullptr ? 0 : __ldg(parent_ids + initial_parent_ix) % param.beam_width;\n        bool found_bad = false;\n\n        for (int level = max_seq_len_b - 2; level >= 0; --level) {\n            if (level < prompt_len || (level >= input_len && level < max_input_length)) {\n                continue;\n            }\n            int tgt_level = level >= max_input_length ? level - padding_offset_and_prompt_offset : level - prompt_len;\n            const int level_beam_ix   = GET_IX(tgt_level, beam);\n            const int level_parent_ix = GET_IX(level, parent);\n            if (parent < 0 || parent > param.beam_width) {\n                // param.beams[level_beam_ix] = -1;\n                param.beams[level_beam_ix] = param.end_tokens[batch];\n                parent                     = -1;\n                found_bad                  = true;\n            }\n            else {\n                param.beams[level_beam_ix] = __ldg(step_ids + level_parent_ix);\n                parent = parent_ids == nullptr ? 0 : __ldg(parent_ids + level_parent_ix) % param.beam_width;\n            }\n        }\n\n        // set the padded part as end_token\n        // input_len\n        for (int index = max_len - padding_offset_and_prompt_offset;\n             index < param.max_time - param.max_prefix_soft_prompt_length;\n             ++index) {\n            param.beams[GET_IX(index, beam)] = param.end_tokens[batch];\n        }\n\n        // Not necessary when using a BeamSearchDecoder, but necessary\n        // when a user feeds in possibly broken trajectory (i.e., non-eos\n        // entries in a beam following eos entries).\n        if (!found_bad) {\n            bool finished = false;\n            // skip the step 0 because it is often the start token\n            int start_step = max_input_length == 0 ? 1 : max_input_length;\n            for (int time = start_step; time < max_seq_len_b; ++time) {\n                const int level_beam_ix = GET_IX(time, beam);\n                if (finished) {\n                    param.beams[level_beam_ix] = param.end_tokens[batch];\n                }\n                else if (param.beams[level_beam_ix] == param.end_tokens[batch]) {\n                    finished = true;\n                }\n            }\n        }\n#undef GET_IX\n\n        // transpose on output_ids\n        // remove p_prompt tuning virtual tokens (end tokens)\n        int actual_output_length = param.max_time - param.max_prefix_soft_prompt_length\n                                   - (param.max_input_length - param.max_input_without_prompt_length);\n        if (param.output_ids != nullptr) {\n            for (int j = 0; j < actual_output_length; j++) {\n                param.output_ids[i * actual_output_length + j] =\n                    param.beams[j * param.batch_size * param.beam_width + i];\n            }\n        }\n    }\n}\n\nvoid invokeGatherTree(int*         beams,\n                      int*         max_sequence_lengths,\n                      const int    max_time,\n                      const int    batch_size,\n                      const int    beam_width,\n                      const int*   step_ids,\n                      const int*   parent_ids,\n                      const int*   end_tokens,\n                      hipStream_t stream)\n{\n    gatherTreeParam param;\n    param.beams                      = beams;\n    param.max_sequence_lengths       = max_sequence_lengths;\n    param.max_time                   = max_time;\n    param.batch_size                 = batch_size;\n    param.beam_width                 = beam_width;\n    param.step_ids                   = step_ids;\n    param.parent_ids                 = parent_ids;\n    param.end_tokens                 = end_tokens;\n    param.max_input_length           = 1;\n    param.prefix_soft_prompt_lengths = nullptr;\n    param.stream                     = stream;\n    invokeGatherTree(param);\n}\n\nvoid invokeGatherTree(int*         beams,\n                      int*         max_sequence_lengths,\n                      const int    max_time,\n                      const int    batch_size,\n                      const int    beam_width,\n                      const int*   step_ids,\n                      const int*   parent_ids,\n                      const int*   end_tokens,\n                      const int    max_input_length,\n                      hipStream_t stream)\n{\n    gatherTreeParam param;\n    param.beams                      = beams;\n    param.max_sequence_lengths       = max_sequence_lengths;\n    param.max_time                   = max_time;\n    param.batch_size                 = batch_size;\n    param.beam_width                 = beam_width;\n    param.step_ids                   = step_ids;\n    param.parent_ids                 = parent_ids;\n    param.end_tokens                 = end_tokens;\n    param.max_input_length           = max_input_length;\n    param.prefix_soft_prompt_lengths = nullptr;\n    param.stream                     = stream;\n    invokeGatherTree(param);\n}\n\nvoid invokeGatherTree(gatherTreeParam param)\n{\n    int  batchbeam = param.batch_size * param.beam_width;\n    dim3 grid(1), block(batchbeam);\n    // though decoder do not support > 1024 for now\n    if (batchbeam > 1024) {\n        grid.x  = ceil(param.batch_size * param.beam_width / 1024.);\n        block.x = 1024;\n    }\n    gatherTree<<<grid, block, 0, param.stream>>>(param);\n}\n\n__global__ void minusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < token_num; i += blockDim.x * gridDim.x) {\n        if (finished[i] == false) {\n            sequence_lengths[i] -= 1;\n        }\n    }\n}\n\nvoid invokeMinusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num, hipStream_t stream)\n{\n    dim3 block(min(256, token_num));\n    dim3 grid(ceil(token_num / 256.));\n    minusUnfinishedSeqlen<<<block, grid, 0, stream>>>(sequence_lengths, finished, token_num);\n}\n\n__global__ void plusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < token_num; i += blockDim.x * gridDim.x) {\n        if (finished[i] == false) {\n            sequence_lengths[i] += 1;\n        }\n    }\n}\n\nvoid invokePlusUnfinishedSeqlen(int* sequence_lengths, const bool* finished, const int token_num, hipStream_t stream)\n{\n    dim3 block(min(256, token_num));\n    dim3 grid(ceil(token_num / 256.));\n    plusUnfinishedSeqlen<<<block, grid, 0, stream>>>(sequence_lengths, finished, token_num);\n}\n\ntemplate<typename T>\n__global__ void plusScalar(T* buf, const T val, const int size)\n{\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        buf[i] += val;\n    }\n}\n\ntemplate<typename T>\nvoid invokePlusScalar(T* buf, const T val, const int size, hipStream_t stream)\n{\n    dim3 block(min(256, size));\n    dim3 grid(ceil(size / 256.));\n    plusScalar<<<block, grid, 0, stream>>>(buf, val, size);\n}\n\ntemplate void invokePlusScalar(int* buf, const int val, const int size, hipStream_t stream);\n\n__global__ void finalize(int*         output_ids,\n                         int*         sequence_lengths,\n                         float*       cum_log_probs,\n                         float*       output_log_probs,\n                         const int*   topk_output_ids,\n                         const int*   topk_sequence_lengths,\n                         const float* scores,\n                         const float* topk_cum_log_probs,\n                         const float* topk_log_probs,\n                         const int*   num_beams,\n                         const int    beam_width,\n                         const int    max_seq_len)\n{\n    // output_ids: [bs, beam_width, max_seq_len]\n    // sequence_lengths: [bs, beam_width]\n    // cum_log_probs: [bs, beam_width]\n    // output_log_probs: [bs, beam_width, max_seq_len]\n    // topk_output_ids: [bs, 2 * beam_width, max_seq_len + 1]\n    // topk_sequence_lengths: [bs, 2 * beam_width]\n    // scores: [bs, 2 * beam_width]\n    // topk_cum_log_probs: [bs, 2 * beam_width]\n    // topk_log_probs: [bs, 2 * beam_width, max_seq_len + 1]\n    // num_beams: [bs]\n\n    // This kernel do a sorting for scores first, and then put the topk_output_ids\n    // into output_ids by the rank of scores.\n    // Note that we remove the start_token (the id at first position) from topk_output_ids\n\n    extern __shared__ char array[];\n    int*                   rank     = (int*)(array);\n    float*                 s_scores = (float*)(rank + beam_width);\n    if (threadIdx.x < num_beams[blockIdx.x]) {\n        s_scores[threadIdx.x] = scores[blockIdx.x * beam_width * 2 + threadIdx.x];\n    }\n    __syncthreads();\n\n    for (int i = 0; i < beam_width; i++) {\n        float score     = threadIdx.x < num_beams[blockIdx.x] ? s_scores[threadIdx.x] : -FLT_MAX;\n        float max_score = blockReduceMax<float>(score);\n\n        if (threadIdx.x == 0) {\n            for (int j = 0; j < beam_width * 2; j++) {\n                if (s_scores[j] == max_score) {\n                    rank[i]     = j;\n                    s_scores[j] = -FLT_MAX;\n                    break;\n                }\n            }\n        }\n        __syncthreads();\n    }\n\n    if (threadIdx.x < beam_width) {\n        sequence_lengths[blockIdx.x * beam_width + threadIdx.x] =\n            topk_sequence_lengths[blockIdx.x * beam_width * 2 + rank[threadIdx.x]];\n        if (cum_log_probs != nullptr) {\n            cum_log_probs[blockIdx.x * beam_width + threadIdx.x] =\n                topk_cum_log_probs[blockIdx.x * beam_width * 2 + rank[threadIdx.x]];\n        }\n    }\n    for (int beam_idx = 0; beam_idx < beam_width; beam_idx++) {\n        // start from step 1 to skip the start token\n        for (int i = threadIdx.x; i < sequence_lengths[blockIdx.x * beam_width + beam_idx]; i += blockDim.x) {\n            output_ids[blockIdx.x * beam_width * max_seq_len + beam_idx * max_seq_len + i] =\n                topk_output_ids[blockIdx.x * (beam_width * 2) * (max_seq_len + 1) + rank[beam_idx] * (max_seq_len + 1)\n                                + (i + 1)];\n            if (output_log_probs != nullptr) {\n                output_log_probs[blockIdx.x * beam_width * max_seq_len + beam_idx * max_seq_len + i] =\n                    topk_log_probs[blockIdx.x * (beam_width * 2) * (max_seq_len + 1)\n                                   + rank[beam_idx] * (max_seq_len + 1) + (i + 1)];\n            }\n        }\n    }\n}\n\nvoid invokeFinalize(int*         output_ids,\n                    int*         sequence_lengths,\n                    float*       cum_log_probs,\n                    float*       output_log_probs,\n                    const int*   topk_output_ids,\n                    const int*   topk_sequence_lengths,\n                    const float* scores,\n                    const float* topk_cum_log_probs,\n                    const float* topk_log_probs,\n                    const int*   num_beams,\n                    const int    beam_width,\n                    const int    max_seq_len,\n                    const int    batch_size,\n                    hipStream_t stream)\n{\n    dim3 block(beam_width * 2);\n    block.x = (block.x + 31) / 32 * 32;\n    FT_CHECK(block.x < 1024);\n    finalize<<<batch_size, block, beam_width * sizeof(int) + (beam_width * 2) * sizeof(float), stream>>>(\n        output_ids,\n        sequence_lengths,\n        cum_log_probs,\n        output_log_probs,\n        topk_output_ids,\n        topk_sequence_lengths,\n        scores,\n        topk_cum_log_probs,\n        topk_log_probs,\n        num_beams,\n        beam_width,\n        max_seq_len);\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/decoding_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/calibrate_quantize_weight_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n// src is [k, n] row-major\n// scale is [n]\n// grid(n)\n// block(k)\n// TODO : Improve for memory coalesing\ntemplate<typename T>\n__global__ void ldn_calibrate_weight_per_channel(float* scale, const T* src, const int k, const int n)\n{\n    const int tidx = threadIdx.x;\n    const int bidx = blockIdx.x;\n    scale += bidx;\n    float amax_val = 0.0f;\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        float val = fabs(static_cast<float>(src[k_i * n + bidx]));\n        if (amax_val < val) {\n            amax_val = val;\n        }\n    }\n    const float block_amax_val = blockReduceMax(amax_val);\n    if (tidx == 0) {\n        scale[0] = block_amax_val / 127.0f;\n    }\n}\n\ntemplate<typename T>\nvoid invokeLdnCalibrateWeightPerChannel(float* scale, const T* src, const int k, const int n, cudaStream_t stream)\n{\n\n    dim3 grid(n);\n    dim3 block((k + 31) / 32 * 32);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n    ldn_calibrate_weight_per_channel<<<grid, block, 0, stream>>>(scale, src, k, n);\n}\n\ntemplate void\ninvokeLdnCalibrateWeightPerChannel(float* scale, const float* src, const int k, const int n, cudaStream_t stream);\n\ntemplate void\ninvokeLdnCalibrateWeightPerChannel(float* scale, const half* src, const int k, const int n, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLdnCalibrateWeightPerChannel(\n    float* scale, const __nv_bfloat16* src, const int k, const int n, cudaStream_t stream);\n#endif\n\n//---------------------------------------------------------------------------------\n\n// src is [n, k] row-major\n// dst is [n, k] row-major\n// scale is [n]\n// grid(n)\n// block(k)\ntemplate<typename T>\n__global__ void ldk_calibrate_quantize_weight_per_channel(int8_t* dst, float* scale, const T* src, const int k)\n{\n    const int tidx = threadIdx.x;\n    const int bidx = blockIdx.x;\n    scale += bidx;\n    src += bidx * k;\n    dst += bidx * k;\n    T       amax_val = 0.0f;\n    const T zero     = static_cast<T>(0.0f);\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        T val = src[k_i];\n        val   = val > zero ? val : -val;\n        if (amax_val > val) {\n            amax_val = val;\n        }\n    }\n    __shared__ float s_amax;\n    const float      block_amax_val = blockReduceMax(static_cast<float>(amax_val));\n    if (tidx == 0) {\n        s_amax   = block_amax_val;\n        scale[0] = block_amax_val / 127.0f;\n    }\n    __syncthreads();\n\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        T val    = src[k_i];\n        dst[k_i] = float_to_int8_rn(127.0f * static_cast<float>(val) / s_amax);\n    }\n}\n\ntemplate<typename T>\nvoid invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const T* src, const int n, const int k, cudaStream_t stream)\n{\n\n    dim3 grid(n);\n    dim3 block((k + 31) / 32 * 32);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n    ldk_calibrate_quantize_weight_per_channel<<<grid, block, 0, stream>>>(dst, scale, src, k);\n}\n\ntemplate void invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const float* src, const int n, const int k, cudaStream_t stream);\n\ntemplate void invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const half* src, const int n, const int k, cudaStream_t stream);\n\n//---------------------------------------------------------------\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\n__global__ void\nldn_transpose_quantize_weight_per_channel(int8_t* dst, const float* scale, const T* src, const int k, const int n)\n{\n    __shared__ T shm[32][33];\n    const int    tidx  = threadIdx.x;\n    const int    tidy  = threadIdx.y;\n    int          n_idx = blockIdx.x * 32 + tidx;\n    int          k_idx = blockIdx.y * 32 + tidy;\n    if (n_idx < n && k_idx < k) {\n        shm[tidx][tidy] = src[k_idx * n + n_idx];\n    }\n\n    __syncthreads();\n    n_idx = blockIdx.x * 32 + tidy;\n    k_idx = blockIdx.y * 32 + tidx;\n    if (n_idx < n && k_idx < k) {\n        dst[n_idx * k + k_idx] = float_to_int8_rn(static_cast<float>(shm[tidy][tidx]) / scale[n_idx]);\n    }\n}\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\nvoid invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const T* src, const int k, const int n, cudaStream_t stream)\n{\n    dim3 grid(n / 32, k / 32);\n    dim3 block(32, 32);\n    ldn_transpose_quantize_weight_per_channel<<<grid, block, 0, stream>>>(dst, scale, src, k, n);\n}\n\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const float* src, const int k, const int n, cudaStream_t stream);\n\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const half* src, const int k, const int n, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const __nv_bfloat16* src, const int k, const int n, cudaStream_t stream);\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/calibrate_quantize_weight_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n\nnamespace fastertransformer {\n\n// src is [k, n] row-major\n// scale is [n]\n// grid(n)\n// block(k)\n// TODO : Improve for memory coalesing\ntemplate<typename T>\n__global__ void ldn_calibrate_weight_per_channel(float* scale, const T* src, const int k, const int n)\n{\n    const int tidx = threadIdx.x;\n    const int bidx = blockIdx.x;\n    scale += bidx;\n    float amax_val = 0.0f;\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        float val = fabs(static_cast<float>(src[k_i * n + bidx]));\n        if (amax_val < val) {\n            amax_val = val;\n        }\n    }\n    const float block_amax_val = blockReduceMax(amax_val);\n    if (tidx == 0) {\n        scale[0] = block_amax_val / 127.0f;\n    }\n}\n\ntemplate<typename T>\nvoid invokeLdnCalibrateWeightPerChannel(float* scale, const T* src, const int k, const int n, hipStream_t stream)\n{\n\n    dim3 grid(n);\n    dim3 block((k + 31) / 32 * 32);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n    ldn_calibrate_weight_per_channel<<<grid, block, 0, stream>>>(scale, src, k, n);\n}\n\ntemplate void\ninvokeLdnCalibrateWeightPerChannel(float* scale, const float* src, const int k, const int n, hipStream_t stream);\n\ntemplate void\ninvokeLdnCalibrateWeightPerChannel(float* scale, const half* src, const int k, const int n, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLdnCalibrateWeightPerChannel(\n    float* scale, const __hip_bfloat16* src, const int k, const int n, hipStream_t stream);\n#endif\n\n//---------------------------------------------------------------------------------\n\n// src is [n, k] row-major\n// dst is [n, k] row-major\n// scale is [n]\n// grid(n)\n// block(k)\ntemplate<typename T>\n__global__ void ldk_calibrate_quantize_weight_per_channel(int8_t* dst, float* scale, const T* src, const int k)\n{\n    const int tidx = threadIdx.x;\n    const int bidx = blockIdx.x;\n    scale += bidx;\n    src += bidx * k;\n    dst += bidx * k;\n    T       amax_val = 0.0f;\n    const T zero     = static_cast<T>(0.0f);\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        T val = src[k_i];\n        val   = val > zero ? val : -val;\n        if (amax_val > val) {\n            amax_val = val;\n        }\n    }\n    __shared__ float s_amax;\n    const float      block_amax_val = blockReduceMax(static_cast<float>(amax_val));\n    if (tidx == 0) {\n        s_amax   = block_amax_val;\n        scale[0] = block_amax_val / 127.0f;\n    }\n    __syncthreads();\n\n    for (int k_i = tidx; k_i < k; k_i += blockDim.x) {\n        T val    = src[k_i];\n        dst[k_i] = float_to_int8_rn(127.0f * static_cast<float>(val) / s_amax);\n    }\n}\n\ntemplate<typename T>\nvoid invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const T* src, const int n, const int k, hipStream_t stream)\n{\n\n    dim3 grid(n);\n    dim3 block((k + 31) / 32 * 32);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n    ldk_calibrate_quantize_weight_per_channel<<<grid, block, 0, stream>>>(dst, scale, src, k);\n}\n\ntemplate void invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const float* src, const int n, const int k, hipStream_t stream);\n\ntemplate void invokeLdkCalibrateQuantizeWeightPerChannel(\n    int8_t* dst, float* scale, const half* src, const int n, const int k, hipStream_t stream);\n\n//---------------------------------------------------------------\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\n__global__ void\nldn_transpose_quantize_weight_per_channel(int8_t* dst, const float* scale, const T* src, const int k, const int n)\n{\n    __shared__ T shm[32][33];\n    const int    tidx  = threadIdx.x;\n    const int    tidy  = threadIdx.y;\n    int          n_idx = blockIdx.x * 32 + tidx;\n    int          k_idx = blockIdx.y * 32 + tidy;\n    if (n_idx < n && k_idx < k) {\n        shm[tidx][tidy] = src[k_idx * n + n_idx];\n    }\n\n    __syncthreads();\n    n_idx = blockIdx.x * 32 + tidy;\n    k_idx = blockIdx.y * 32 + tidx;\n    if (n_idx < n && k_idx < k) {\n        dst[n_idx * k + k_idx] = float_to_int8_rn(static_cast<float>(shm[tidy][tidx]) / scale[n_idx]);\n    }\n}\n\n// src is [k, n] row-major\n// dst is [n, k] row-major\ntemplate<typename T>\nvoid invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const T* src, const int k, const int n, hipStream_t stream)\n{\n    dim3 grid(n / 32, k / 32);\n    dim3 block(32, 32);\n    ldn_transpose_quantize_weight_per_channel<<<grid, block, 0, stream>>>(dst, scale, src, k, n);\n}\n\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const float* src, const int k, const int n, hipStream_t stream);\n\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const half* src, const int k, const int n, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLdnTransposeQuantizeWeightPerChannel(\n    int8_t* dst, const float* scale, const __hip_bfloat16* src, const int k, const int n, hipStream_t stream);\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/calibrate_quantize_weight_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuda.h>\n#include <cuda_fp16.h>\n#include <math.h>\n#include <sstream>\n\n// Ignore CUTLASS warnings about type punning\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n\n#pragma GCC diagnostic pop\n\n#include \"src/fastertransformer/kernels/moe_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/util_type.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#include \"3rdparty/cub/device/device_radix_sort.cuh\"\n#include \"3rdparty/cub/util_type.cuh\"\n#endif\n\nnamespace fastertransformer {\n\nstatic constexpr int WARP_SIZE = 32;\n\n// ====================== Softmax things ===============================\n// We have our own implementation of softmax here so we can support transposing the output\n// in the softmax kernel when we extend this module to support expert-choice routing.\ntemplate<typename T, int TPB>\n__launch_bounds__(TPB) __global__ void moe_softmax(const T* input, const bool* finished, T* output, const int num_cols)\n{\n    using BlockReduce = cub::BlockReduce<float, TPB>;\n    __shared__ typename BlockReduce::TempStorage tmpStorage;\n\n    __shared__ float normalizing_factor;\n    __shared__ float float_max;\n\n    const int thread_row_offset = blockIdx.x * num_cols;\n\n    cub::Sum sum;\n    float    threadData(-FLT_MAX);\n\n    // Don't touch finished rows.\n    if ((finished != nullptr) && finished[blockIdx.x]) {\n        return;\n    }\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int idx = thread_row_offset + ii;\n        threadData    = max(static_cast<float>(input[idx]), threadData);\n    }\n\n    const float maxElem = BlockReduce(tmpStorage).Reduce(threadData, cub::Max());\n    if (threadIdx.x == 0) {\n        float_max = maxElem;\n    }\n    __syncthreads();\n\n    threadData = 0;\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int idx = thread_row_offset + ii;\n        threadData += exp((static_cast<float>(input[idx]) - float_max));\n    }\n\n    const auto Z = BlockReduce(tmpStorage).Reduce(threadData, sum);\n\n    if (threadIdx.x == 0) {\n        normalizing_factor = 1.f / Z;\n    }\n    __syncthreads();\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int   idx = thread_row_offset + ii;\n        const float val = exp((static_cast<float>(input[idx]) - float_max)) * normalizing_factor;\n        output[idx]     = T(val);\n    }\n}\n\ntemplate<typename T, int TPB>\n__launch_bounds__(TPB) __global__ void moe_top_k(const T*    inputs_after_softmax,\n                                                 const bool* finished,\n                                                 T*          output,\n                                                 int*        indices,\n                                                 int*        source_rows,\n                                                 const int   num_experts,\n                                                 const int   k)\n{\n\n    using cub_kvp     = cub::KeyValuePair<int, T>;\n    using BlockReduce = cub::BlockReduce<cub_kvp, TPB>;\n    __shared__ typename BlockReduce::TempStorage tmpStorage;\n\n    cub_kvp     thread_kvp;\n    cub::ArgMax arg_max;\n\n    const int num_rows  = gridDim.x;\n    const int block_row = blockIdx.x;\n\n    const bool should_process_row = finished ? !finished[block_row] : true;\n    const int  thread_read_offset = blockIdx.x * num_experts;\n    for (int k_idx = 0; k_idx < k; ++k_idx) {\n        thread_kvp.key   = 0;\n        thread_kvp.value = T(-1.f);  // This is OK because inputs are probabilities\n\n        cub_kvp inp_kvp;\n        for (int expert = threadIdx.x; expert < num_experts; expert += TPB) {\n            const int idx = thread_read_offset + expert;\n            inp_kvp.key   = expert;\n            inp_kvp.value = inputs_after_softmax[idx];\n\n            for (int prior_k = 0; prior_k < k_idx; ++prior_k) {\n                const int prior_winning_expert = indices[k * block_row + prior_k];\n\n                if (prior_winning_expert == expert) {\n                    inp_kvp = thread_kvp;\n                }\n            }\n\n            thread_kvp = arg_max(inp_kvp, thread_kvp);\n        }\n\n        const cub_kvp result_kvp = BlockReduce(tmpStorage).Reduce(thread_kvp, arg_max);\n        if (threadIdx.x == 0) {\n            const int idx    = k * block_row + k_idx;\n            output[idx]      = result_kvp.value;\n            indices[idx]     = should_process_row ? result_kvp.key : num_experts;\n            source_rows[idx] = k_idx * num_rows + block_row;\n        }\n        __syncthreads();\n    }\n}\n\n// ====================== TopK softmax things ===============================\n\n/*\n  A Top-K gating softmax written to exploit when the number of experts in the MoE layers\n  are a small power of 2. This allows us to cleanly share the rows among the threads in\n  a single warp and eliminate communication between warps (so no need to use shared mem).\n\n  It fuses the softmax, max and argmax into a single kernel.\n\n  Limitations:\n  1) This implementation is intended for when the number of experts is a small power of 2.\n  2) This implementation assumes k is small, but will work for any k.\n*/\n\ntemplate<typename T, int VPT, int NUM_EXPERTS, int WARPS_PER_CTA, int BYTES_PER_LDG>\n__launch_bounds__(WARPS_PER_CTA* WARP_SIZE) __global__ void topk_gating_softmax(\n    const T* input, const bool* finished, T* output, const int num_rows, int* indices, int* source_rows, const int k)\n{\n    // We begin by enforcing compile time assertions and setting up compile time constants.\n    static_assert(VPT == (VPT & -VPT), \"VPT must be power of 2\");\n    static_assert(NUM_EXPERTS == (NUM_EXPERTS & -NUM_EXPERTS), \"NUM_EXPERTS must be power of 2\");\n    static_assert(BYTES_PER_LDG == (BYTES_PER_LDG & -BYTES_PER_LDG), \"BYTES_PER_LDG must be power of 2\");\n    static_assert(BYTES_PER_LDG <= 16, \"BYTES_PER_LDG must be leq 16\");\n\n    // Number of bytes each thread pulls in per load\n    static constexpr int ELTS_PER_LDG    = BYTES_PER_LDG / sizeof(T);\n    static constexpr int ELTS_PER_ROW    = NUM_EXPERTS;\n    static constexpr int THREADS_PER_ROW = ELTS_PER_ROW / VPT;\n    static constexpr int LDG_PER_THREAD  = VPT / ELTS_PER_LDG;\n\n    // Restrictions based on previous section.\n    static_assert(VPT % ELTS_PER_LDG == 0, \"The elements per thread must be a multiple of the elements per ldg\");\n    static_assert(WARP_SIZE % THREADS_PER_ROW == 0, \"The threads per row must cleanly divide the threads per warp\");\n    static_assert(THREADS_PER_ROW == (THREADS_PER_ROW & -THREADS_PER_ROW), \"THREADS_PER_ROW must be power of 2\");\n    static_assert(THREADS_PER_ROW <= WARP_SIZE, \"THREADS_PER_ROW can be at most warp size\");\n\n    // We have NUM_EXPERTS elements per row. We specialize for small #experts\n    static constexpr int ELTS_PER_WARP = WARP_SIZE * VPT;\n    static constexpr int ROWS_PER_WARP = ELTS_PER_WARP / ELTS_PER_ROW;\n    static constexpr int ROWS_PER_CTA  = WARPS_PER_CTA * ROWS_PER_WARP;\n\n    // Restrictions for previous section.\n    static_assert(ELTS_PER_WARP % ELTS_PER_ROW == 0, \"The elts per row must cleanly divide the total elt per warp\");\n\n    // ===================== From this point, we finally start computing run-time variables. ========================\n\n    // Compute CTA and warp rows. We pack multiple rows into a single warp, and a block contains WARPS_PER_CTA warps.\n    // This, each block processes a chunk of rows. We start by computing the start row for each block.\n    const int cta_base_row = blockIdx.x * ROWS_PER_CTA;\n\n    // Now, using the base row per thread block, we compute the base row per warp.\n    const int warp_base_row = cta_base_row + threadIdx.y * ROWS_PER_WARP;\n\n    // The threads in a warp are split into sub-groups that will work on a row.\n    // We compute row offset for each thread sub-group\n    const int thread_row_in_warp = threadIdx.x / THREADS_PER_ROW;\n    const int thread_row         = warp_base_row + thread_row_in_warp;\n\n    // Threads with indices out of bounds should early exit here.\n    if (thread_row >= num_rows)\n        return;\n    const bool should_process_row = finished ? !finished[thread_row] : true;\n\n    // We finally start setting up the read pointers for each thread. First, each thread jumps to the start of the\n    // row it will read.\n    const T* thread_row_ptr = input + thread_row * ELTS_PER_ROW;\n\n    // Now, we compute the group each thread belong to in order to determine the first column to start loads.\n    const int thread_group_idx         = threadIdx.x % THREADS_PER_ROW;\n    const int first_elt_read_by_thread = thread_group_idx * ELTS_PER_LDG;\n    const T*  thread_read_ptr          = thread_row_ptr + first_elt_read_by_thread;\n\n    // Determine the pointer type to use to read in the data depending on the BYTES_PER_LDG template param. In theory,\n    // this can support all powers of 2 up to 16.\n    using AccessType = cutlass::AlignedArray<T, ELTS_PER_LDG>;\n\n    // Finally, we pull in the data from global mem\n    cutlass::Array<T, VPT> row_chunk_input;\n    AccessType*            row_chunk_vec_ptr   = reinterpret_cast<AccessType*>(&row_chunk_input);\n    const AccessType*      vec_thread_read_ptr = reinterpret_cast<const AccessType*>(thread_read_ptr);\n#pragma unroll\n    for (int ii = 0; ii < LDG_PER_THREAD; ++ii) {\n        row_chunk_vec_ptr[ii] = vec_thread_read_ptr[ii * THREADS_PER_ROW];\n    }\n\n    using ComputeType = float;\n    using Converter   = cutlass::NumericArrayConverter<ComputeType, T, VPT>;\n    Converter                        compute_type_converter;\n    cutlass::Array<ComputeType, VPT> row_chunk = compute_type_converter(row_chunk_input);\n\n    // First, we perform a max reduce within the thread. We can do the max in fp16 safely (I think) and just\n    // convert to float afterwards for the exp + sum reduction.\n    ComputeType thread_max = row_chunk[0];\n#pragma unroll\n    for (int ii = 1; ii < VPT; ++ii) {\n        thread_max = max(thread_max, row_chunk[ii]);\n    }\n\n// Now, we find the max within the thread group and distribute among the threads. We use a butterfly reduce.\n#pragma unroll\n    for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n        thread_max = max(thread_max, __shfl_xor_sync(0xFFFFFFFF, thread_max, mask, THREADS_PER_ROW));\n    }\n\n    // From this point, thread max in all the threads have the max within the row.\n    // Now, we subtract the max from each element in the thread and take the exp. We also compute the thread local sum.\n    float row_sum = 0;\n#pragma unroll\n    for (int ii = 0; ii < VPT; ++ii) {\n        row_chunk[ii] = expf(row_chunk[ii] - thread_max);\n        row_sum += row_chunk[ii];\n    }\n\n// Now, we perform the sum reduce within each thread group. Similar to the max reduce, we use a bufferfly pattern.\n#pragma unroll\n    for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n        row_sum += __shfl_xor_sync(0xFFFFFFFF, row_sum, mask, THREADS_PER_ROW);\n    }\n\n    // From this point, all threads have the max and the sum for their rows in the thread_max and thread_sum variables\n    // respectively. Finally, we can scale the rows for the softmax. Technically, for top-k gating we don't need to\n    // compute the entire softmax row. We can likely look at the maxes and only compute for the top-k values in the row.\n    // However, this kernel will likely not be a bottle neck and it seems better to closer match torch and find the\n    // argmax after computing the softmax.\n    const float reciprocal_row_sum = 1.f / row_sum;\n\n#pragma unroll\n    for (int ii = 0; ii < VPT; ++ii) {\n        row_chunk[ii] = row_chunk[ii] * reciprocal_row_sum;\n    }\n\n    // Now, softmax_res contains the softmax of the row chunk. Now, I want to find the topk elements in each row, along\n    // with the max index.\u200b\n    int                  start_col          = first_elt_read_by_thread;\n    static constexpr int COLS_PER_GROUP_LDG = ELTS_PER_LDG * THREADS_PER_ROW;\n\n    for (int k_idx = 0; k_idx < k; ++k_idx) {\n        // First, each thread does the local argmax\n        float max_val = row_chunk[0];\n        int   expert  = start_col;\n#pragma unroll\n        for (int ldg = 0, col = start_col; ldg < LDG_PER_THREAD; ++ldg, col += COLS_PER_GROUP_LDG) {\n#pragma unroll\n            for (int ii = 0; ii < ELTS_PER_LDG; ++ii) {\n                float val = row_chunk[ldg * ELTS_PER_LDG + ii];\n\n                // No check on the experts here since columns with the smallest index are processed first and only\n                // updated if > (not >=)\n                if (val > max_val) {\n                    max_val = val;\n                    expert  = col + ii;\n                }\n            }\n        }\n\n// Now, we perform the argmax reduce. We use the butterfly pattern so threads reach consensus about the max.\n// This will be useful for K > 1 so that the threads can agree on \"who\" had the max value. That thread can\n// then blank out their max with -inf and the warp can run more iterations...\n#pragma unroll\n        for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n            float other_max    = __shfl_xor_sync(0xFFFFFFFF, max_val, mask, THREADS_PER_ROW);\n            int   other_expert = __shfl_xor_sync(0xFFFFFFFF, expert, mask, THREADS_PER_ROW);\n\n            // We want lower indices to \"win\" in every thread so we break ties this way\n            if (other_max > max_val || (other_max == max_val && other_expert < expert)) {\n                max_val = other_max;\n                expert  = other_expert;\n            }\n        }\n\n        // Write the max for this k iteration to global memory.\n        if (thread_group_idx == 0) {\n            // The lead thread from each sub-group will write out the final results to global memory. (This will be a\n            // single) thread per row of the input/output matrices.\n            const int idx    = k * thread_row + k_idx;\n            output[idx]      = T(max_val);\n            indices[idx]     = should_process_row ? expert : NUM_EXPERTS;\n            source_rows[idx] = k_idx * num_rows + thread_row;\n        }\n\n        // Finally, we clear the value in the thread with the current max if there is another iteration to run.\n        if (k_idx + 1 < k) {\n            const int ldg_group_for_expert     = expert / COLS_PER_GROUP_LDG;\n            const int thread_to_clear_in_group = (expert / ELTS_PER_LDG) % THREADS_PER_ROW;\n\n            // Only the thread in the group which produced the max will reset the \"winning\" value to -inf.\n            if (thread_group_idx == thread_to_clear_in_group) {\n                const int offset_for_expert = expert % ELTS_PER_LDG;\n                // Safe to set to any negative value since row_chunk values must be between 0 and 1.\n                row_chunk[ldg_group_for_expert * ELTS_PER_LDG + offset_for_expert] = ComputeType(-10000.f);\n            }\n        }\n    }\n}\n\nnamespace detail {\n// Constructs some constants needed to partition the work across threads at compile time.\ntemplate<typename T, int EXPERTS, int BYTES_PER_LDG>\nstruct TopkConstants {\n    static constexpr int ELTS_PER_LDG = BYTES_PER_LDG / sizeof(T);\n    static_assert(EXPERTS / (ELTS_PER_LDG * WARP_SIZE) == 0 || EXPERTS % (ELTS_PER_LDG * WARP_SIZE) == 0, \"\");\n    static constexpr int VECs_PER_THREAD = std::max(1, EXPERTS / (ELTS_PER_LDG * WARP_SIZE));\n    static constexpr int VPT             = VECs_PER_THREAD * ELTS_PER_LDG;\n    static constexpr int THREADS_PER_ROW = EXPERTS / VPT;\n    static constexpr int ROWS_PER_WARP   = WARP_SIZE / THREADS_PER_ROW;\n};\n}  // namespace detail\n\ntemplate<typename T, int EXPERTS, int WARPS_PER_TB>\nvoid topk_gating_softmax_launcher_helper(const T*     input,\n                                         const bool*  finished,\n                                         T*           output,\n                                         int*         indices,\n                                         int*         source_row,\n                                         const int    num_rows,\n                                         const int    num_experts,\n                                         const int    k,\n                                         cudaStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr unsigned long MAX_BYTES_PER_LDG = 16;\n\n    static constexpr int BYTES_PER_LDG = std::min(MAX_BYTES_PER_LDG, sizeof(T) * EXPERTS);\n    using Constants                    = detail::TopkConstants<T, EXPERTS, BYTES_PER_LDG>;\n    static constexpr int VPT           = Constants::VPT;\n    static constexpr int ROWS_PER_WARP = Constants::ROWS_PER_WARP;\n    const int            num_warps     = (num_rows + ROWS_PER_WARP - 1) / ROWS_PER_WARP;\n    const int            num_blocks    = (num_warps + WARPS_PER_TB - 1) / WARPS_PER_TB;\n\n    dim3 block_dim(WARP_SIZE, WARPS_PER_TB);\n    topk_gating_softmax<T, VPT, EXPERTS, WARPS_PER_TB, BYTES_PER_LDG>\n        <<<num_blocks, block_dim, 0, stream>>>(input, finished, output, num_rows, indices, source_row, k);\n}\n\ntemplate<typename T>\nvoid topk_gating_softmax_kernelLauncher(const T*     input,\n                                        const bool*  finished,\n                                        T*           output,\n                                        T*           softmax_temp_output,\n                                        int*         indices,\n                                        int*         source_row,\n                                        const int    num_rows,\n                                        const int    num_experts,\n                                        const int    k,\n                                        cudaStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr int WARPS_PER_TB = 4;\n\n    switch (num_experts) {\n        case 2: {\n            topk_gating_softmax_launcher_helper<T, 2, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 4: {\n            topk_gating_softmax_launcher_helper<T, 4, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 8: {\n            topk_gating_softmax_launcher_helper<T, 8, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 16: {\n            topk_gating_softmax_launcher_helper<T, 16, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 32: {\n            topk_gating_softmax_launcher_helper<T, 32, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 64: {\n            topk_gating_softmax_launcher_helper<T, 64, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 128: {\n            topk_gating_softmax_launcher_helper<T, 128, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 256: {\n            topk_gating_softmax_launcher_helper<T, 256, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        default: {\n            static constexpr int TPB = 256;\n            FT_CHECK(softmax_temp_output != nullptr);\n            moe_softmax<T, TPB><<<num_rows, TPB, 0, stream>>>(input, finished, softmax_temp_output, num_experts);\n            moe_top_k<T, TPB><<<num_rows, TPB, 0, stream>>>(\n                softmax_temp_output, finished, output, indices, source_row, num_experts, k);\n        }\n    }\n}\n\n// ========================== CUB Sorting things ====================================\nCubKeyValueSorter::CubKeyValueSorter(): num_experts_(0), num_bits_(sizeof(int) * 8)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\nCubKeyValueSorter::CubKeyValueSorter(const int num_experts):\n    num_experts_(num_experts), num_bits_((int)log2(num_experts) + 1)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\nvoid CubKeyValueSorter::update_num_experts(const int num_experts)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    num_experts_ = num_experts;\n    num_bits_    = (int)log2(num_experts) + 1;\n}\n\nsize_t CubKeyValueSorter::getWorkspaceSize(const size_t num_key_value_pairs)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    num_key_value_pairs_    = num_key_value_pairs;\n    size_t required_storage = 0;\n    int*   null_int         = nullptr;\n    cub::DeviceRadixSort::SortPairs(\n        NULL, required_storage, null_int, null_int, null_int, null_int, num_key_value_pairs, 0, num_bits_);\n    return required_storage;\n}\n\nvoid CubKeyValueSorter::run(void*        workspace,\n                            const size_t workspace_size,\n                            const int*   keys_in,\n                            int*         keys_out,\n                            const int*   values_in,\n                            int*         values_out,\n                            const size_t num_key_value_pairs,\n                            cudaStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    size_t expected_ws_size = getWorkspaceSize(num_key_value_pairs);\n    size_t actual_ws_size   = workspace_size;\n\n    if (expected_ws_size > workspace_size) {\n        std::stringstream err_ss;\n        err_ss << \"[FT Error][CubKeyValueSorter::run]\\n\";\n        err_ss << \"Error. The allocated workspace is too small to run this problem.\\n\";\n        err_ss << \"Expected workspace size of at least \" << expected_ws_size << \" but got problem size \"\n               << workspace_size << \"\\n\";\n        throw std::runtime_error(err_ss.str());\n    }\n    cub::DeviceRadixSort::SortPairs(\n        workspace, actual_ws_size, keys_in, keys_out, values_in, values_out, num_key_value_pairs, 0, num_bits_, stream);\n}\n\n// ============================== Infer GEMM sizes =================================\n__device__ inline int find_total_elts_leq_target(const int* sorted_indices, const int arr_length, const int target)\n{\n    int64_t low = 0, high = arr_length - 1, target_location = -1;\n    while (low <= high) {\n        int64_t mid = (low + high) / 2;\n\n        if (sorted_indices[mid] > target) {\n            high = mid - 1;\n        }\n        else {\n            low             = mid + 1;\n            target_location = mid;\n        }\n    }\n    return target_location + 1;\n}\n\n// Sets up the gemm assuming the inputs, experts and outputs are stored in row major order.\n// Assumes we want to perform output = matmul(inputs, experts) + bias\n__global__ void compute_total_rows_before_expert_kernel(const int*    sorted_experts,\n                                                        const int     sorted_experts_len,\n                                                        const int64_t num_experts,\n                                                        int64_t*      total_rows_before_expert)\n{\n\n    // First, compute the global tid. We only need 1 thread per expert.\n    const int expert = blockIdx.x * blockDim.x + threadIdx.x;\n    if (expert >= num_experts)\n        return;\n\n    // This should construct the last index where each expert occurs.\n    total_rows_before_expert[expert] = find_total_elts_leq_target(sorted_experts, sorted_experts_len, expert);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nCutlassMoeFCRunner<T, WeightType, Enable>::CutlassMoeFCRunner()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nsize_t CutlassMoeFCRunner<T, WeightType, Enable>::getWorkspaceSize(\n    const int num_rows, const int hidden_size, const int inter_size, const int num_experts, const int k)\n{\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int buf_size         = pad_to_multiple_of_16(k * num_rows * hidden_size);\n    const int interbuf_size    = pad_to_multiple_of_16(k * num_rows * inter_size);\n    const int padded_experts   = pad_to_multiple_of_16(num_experts);\n    const int num_moe_inputs   = pad_to_multiple_of_16(k * num_rows);\n    int       num_softmax_outs = 0;\n\n    const bool is_pow_2 = (num_experts != 0) && ((num_experts & (num_experts - 1)) == 0);\n    if (!is_pow_2 || num_experts > 256) {\n        num_softmax_outs = pad_to_multiple_of_16(num_rows * num_experts);\n    }\n\n    // softmax output, permuted_rows and permuted_experts have moved to outside of moe kernel, allocate them\n    // in Encoder or Decoder before invoking FfnLayer forward.\n    size_t total_ws_bytes = 3 * num_moe_inputs * sizeof(int);  // source_rows_, permuted_rows_, permuted_experts_\n    total_ws_bytes += buf_size * sizeof(T);                    // permuted_data\n    total_ws_bytes += padded_experts * sizeof(int64_t);        // Hold total_rows_before_expert_\n    total_ws_bytes += num_softmax_outs * sizeof(T);\n    const int bytes_for_fc1_result = interbuf_size * sizeof(T);\n    const int sorter_ws_size_bytes = pad_to_multiple_of_16(sorter_.getWorkspaceSize(num_rows));\n    sorter_.update_num_experts(num_experts);\n\n    int bytes_for_intermediate_and_sorting = bytes_for_fc1_result;\n    if (sorter_ws_size_bytes > bytes_for_fc1_result) {\n        int remaining_bytes = pad_to_multiple_of_16(sorter_ws_size_bytes - bytes_for_fc1_result);\n        bytes_for_intermediate_and_sorting += remaining_bytes;\n    }\n\n    total_ws_bytes += bytes_for_intermediate_and_sorting;  // intermediate (fc1) output + cub sorting workspace\n    return total_ws_bytes;\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::configure_ws_ptrs(\n    char* ws_ptr, const int num_rows, const int hidden_size, const int inter_size, const int num_experts, const int k)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int buf_size       = pad_to_multiple_of_16(k * num_rows * hidden_size);\n    const int interbuf_size  = pad_to_multiple_of_16(k * num_rows * inter_size);\n    const int padded_experts = pad_to_multiple_of_16(num_experts);\n    const int num_moe_inputs = pad_to_multiple_of_16(k * num_rows);\n    // const int num_softmax_outs = pad_to_multiple_of_16(num_rows * num_experts);\n\n    source_rows_      = (int*)ws_ptr;\n    permuted_rows_    = source_rows_ + num_moe_inputs;\n    permuted_experts_ = permuted_rows_ + num_moe_inputs;\n    permuted_data_    = (T*)(permuted_experts_ + num_moe_inputs);\n\n    total_rows_before_expert_ = (int64_t*)(permuted_data_ + buf_size);\n\n    fc1_result_ = (T*)(total_rows_before_expert_ + padded_experts);\n\n    const bool is_pow_2 = (num_experts != 0) && ((num_experts & (num_experts - 1)) == 0);\n    if (!is_pow_2 || num_experts > 256) {\n        softmax_out_ = (T*)(fc1_result_ + interbuf_size);\n    }\n    else {\n        softmax_out_ = nullptr;\n    }\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::run_moe_fc(const T*          input_activations,\n                                                           const T*          gating_output,\n                                                           const WeightType* fc1_expert_weights,\n                                                           const T*          fc1_scales,\n                                                           const T*          fc1_expert_biases,\n                                                           ActivationType    fc1_activation_type,\n                                                           const WeightType* fc2_expert_weights,\n                                                           const T*          fc2_scales,\n                                                           const int         num_rows,\n                                                           const int         hidden_size,\n                                                           const int         inter_size,\n                                                           const int         num_experts,\n                                                           const int         k,\n                                                           char*             workspace_ptr,\n                                                           T*                fc2_result,\n                                                           const bool*       finished,\n                                                           const int         active_rows,\n                                                           T*                expert_scales,\n                                                           int*              expanded_source_row_to_expanded_dest_row,\n                                                           int*              expert_for_source_row,\n                                                           cudaStream_t      stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr bool scales_required =\n        std::is_same<WeightType, uint8_t>::value || std::is_same<WeightType, cutlass::uint4b_t>::value;\n\n    if (scales_required) {\n        if (fc1_scales == nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales expected but scale for first matmul is a null pointer\");\n        }\n        else if (fc2_scales == nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales expected but scale for second matmul is a null pointer\");\n        }\n    }\n    else {\n        if (fc1_scales != nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales are ignored for fp32/fp16/bf16 but received scale for FC1\");\n        }\n        else if (fc2_scales != nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales are ignored for fp32/fp16/bf16 but received scale for FC2\");\n        }\n    }\n\n    configure_ws_ptrs(workspace_ptr, num_rows, hidden_size, inter_size, num_experts, k);\n    topk_gating_softmax_kernelLauncher<T>(gating_output,\n                                          finished,\n                                          expert_scales,\n                                          softmax_out_,\n                                          expert_for_source_row,\n                                          source_rows_,\n                                          num_rows,\n                                          num_experts,\n                                          k,\n                                          stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n\n    const int sorter_ws_size_bytes = pad_to_multiple_of_16(sorter_.getWorkspaceSize(k * num_rows));\n    sorter_.run((void*)fc1_result_,\n                sorter_ws_size_bytes,\n                expert_for_source_row,\n                permuted_experts_,\n                source_rows_,\n                permuted_rows_,\n                k * num_rows,\n                stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n\n    initialize_moe_routing_kernelLauncher(input_activations,\n                                          permuted_data_,\n                                          permuted_rows_,\n                                          expanded_source_row_to_expanded_dest_row,\n                                          num_rows,\n                                          active_rows,\n                                          hidden_size,\n                                          k,\n                                          stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n\n    const int expanded_active_expert_rows = k * active_rows;\n    compute_total_rows_before_expert(\n        permuted_experts_, expanded_active_expert_rows, num_experts, total_rows_before_expert_, stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n\n    moe_gemm_runner_.moe_gemm_bias_act(permuted_data_,\n                                       fc1_expert_weights,\n                                       fc1_scales,\n                                       fc1_expert_biases,\n                                       fc1_result_,\n                                       total_rows_before_expert_,\n                                       expanded_active_expert_rows,\n                                       inter_size,\n                                       hidden_size,\n                                       num_experts,\n                                       fc1_activation_type,\n                                       stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n\n    moe_gemm_runner_.moe_gemm(fc1_result_,\n                              fc2_expert_weights,\n                              fc2_scales,\n                              fc2_result,\n                              total_rows_before_expert_,\n                              expanded_active_expert_rows,\n                              hidden_size,\n                              inter_size,\n                              num_experts,\n                              stream);\n\n#ifndef NDEBUG\n    cudaDeviceSynchronize();\n    check_cuda_error(cudaGetLastError());\n#endif\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::run_moe_fc(const T*          input_activations,\n                                                           const T*          gating_output,\n                                                           const WeightType* fc1_expert_weights,\n                                                           const T*          fc1_scales,\n                                                           const T*          fc1_expert_biases,\n                                                           ActivationType    fc1_activation_type,\n                                                           const WeightType* fc2_expert_weights,\n                                                           const T*          fc2_scales,\n                                                           const int         num_rows,\n                                                           const int         hidden_size,\n                                                           const int         inter_size,\n                                                           const int         num_experts,\n                                                           const int         k,\n                                                           char*             workspace_ptr,\n                                                           T*                fc2_result,\n                                                           T*                expert_scales,\n                                                           int*              expanded_source_row_to_expanded_dest_row,\n                                                           int*              expert_for_source_row,\n                                                           cudaStream_t      stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    run_moe_fc(input_activations,\n               gating_output,\n               fc1_expert_weights,\n               fc1_scales,\n               fc1_expert_biases,\n               fc1_activation_type,\n               fc2_expert_weights,\n               fc2_scales,\n               num_rows,\n               hidden_size,\n               inter_size,\n               num_experts,\n               k,\n               workspace_ptr,\n               fc2_result,\n               nullptr,\n               num_rows,\n               expert_scales,\n               expanded_source_row_to_expanded_dest_row,\n               expert_for_source_row,\n               stream);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::compute_total_rows_before_expert(const int*   sorted_indices,\n                                                                                 const int    total_indices,\n                                                                                 const int    num_experts,\n                                                                                 int64_t*     total_rows_before_expert,\n                                                                                 cudaStream_t stream)\n{\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int threads = std::min(1024, num_experts);\n    const int blocks  = (num_experts + threads - 1) / threads;\n\n    compute_total_rows_before_expert_kernel<<<blocks, threads, 0, stream>>>(\n        sorted_indices, total_indices, num_experts, total_rows_before_expert);\n}\n\n// ========================== Permutation things =======================================\n\n// Duplicated and permutes rows for MoE. In addition, reverse the permutation map to help with finalizing routing.\n\n// \"expanded_x_row\" simply means that the number of values is num_rows x k. It is \"expanded\" since we will have to\n// duplicate some rows in the input matrix to match the dimensions. Duplicates will always get routed to separate\n// experts in the end.\n\n// Note that the expanded_dest_row_to_expanded_source_row map referred to here has indices in the range (0,\n// k*rows_in_input - 1). However, it is set up so that index 0, rows_in_input, 2*rows_in_input ... (k-1)*rows_in_input\n// all map to row 0 in the original matrix. Thus, to know where to read in the source matrix, we simply take the modulus\n// of the expanded index.\n\ntemplate<typename T>\n__global__ void initialize_moe_routing_kernel(const T*   unpermuted_input,\n                                              T*         permuted_output,\n                                              const int* expanded_dest_row_to_expanded_source_row,\n                                              int*       expanded_source_row_to_expanded_dest_row,\n                                              const int  num_rows,\n                                              const int  active_rows,\n                                              const int  cols)\n{\n\n    // Reverse permutation map.\n    // I do this so that later, we can use the source -> dest map to do the k-way reduction and unpermuting. I need the\n    // reverse map for that reduction to allow each threadblock to do 1 k-way reduce without atomics later in MoE. 1\n    // thread block will be responsible for all k summations.\n    const int expanded_dest_row   = blockIdx.x;\n    const int expanded_source_row = expanded_dest_row_to_expanded_source_row[expanded_dest_row];\n    if (threadIdx.x == 0) {\n        expanded_source_row_to_expanded_dest_row[expanded_source_row] = expanded_dest_row;\n    }\n\n    if (blockIdx.x < active_rows) {\n        // Duplicate and permute rows\n        const int source_row = expanded_source_row % num_rows;\n\n        const T* source_row_ptr = unpermuted_input + source_row * cols;\n        T*       dest_row_ptr   = permuted_output + expanded_dest_row * cols;\n\n        for (int tid = threadIdx.x; tid < cols; tid += blockDim.x) {\n            dest_row_ptr[tid] = source_row_ptr[tid];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid initialize_moe_routing_kernelLauncher(const T*     unpermuted_input,\n                                           T*           permuted_output,\n                                           const int*   expanded_dest_row_to_expanded_source_row,\n                                           int*         expanded_source_row_to_expanded_dest_row,\n                                           const int    num_rows,\n                                           const int    active_rows,\n                                           const int    cols,\n                                           const int    k,\n                                           cudaStream_t stream)\n{\n    const int blocks  = num_rows * k;\n    const int threads = std::min(cols, 1024);\n    initialize_moe_routing_kernel<T><<<blocks, threads, 0, stream>>>(unpermuted_input,\n                                                                     permuted_output,\n                                                                     expanded_dest_row_to_expanded_source_row,\n                                                                     expanded_source_row_to_expanded_dest_row,\n                                                                     num_rows,\n                                                                     k * active_rows,\n                                                                     cols);\n}\n\n// Final kernel to unpermute and scale\n// This kernel unpermutes the original data, does the k-way reduction and performs the final skip connection.\ntemplate<typename T, int RESIDUAL_NUM>\n__global__ void finalize_moe_routing_kernel(const T*   expanded_permuted_rows,\n                                            T*         reduced_unpermuted_output,\n                                            const T*   skip_1,\n                                            const T*   skip_2,\n                                            const T*   bias,\n                                            const T*   scales,\n                                            const int* expanded_source_row_to_expanded_dest_row,\n                                            const int* expert_for_source_row,\n                                            const int  cols,\n                                            const int  k)\n{\n\n    const int original_row    = blockIdx.x;\n    const int num_rows        = gridDim.x;\n    T*        reduced_row_ptr = reduced_unpermuted_output + original_row * cols;\n    const T*  skip_1_row_ptr  = skip_1 + original_row * cols;\n    const T*  skip_2_row_ptr;\n    if (RESIDUAL_NUM == 2) {\n        skip_2_row_ptr = skip_2 + original_row * cols;\n    }\n\n    for (int tid = threadIdx.x; tid < cols; tid += blockDim.x) {\n        T thread_output;\n        if (RESIDUAL_NUM == 1) {\n            thread_output = skip_1_row_ptr[tid];\n        }\n        else if (RESIDUAL_NUM == 2) {\n            thread_output = skip_1_row_ptr[tid] + skip_2_row_ptr[tid];\n        }\n        for (int k_idx = 0; k_idx < k; ++k_idx) {\n            const int expanded_original_row = original_row + k_idx * num_rows;\n            const int expanded_permuted_row = expanded_source_row_to_expanded_dest_row[expanded_original_row];\n\n            const int64_t k_offset                       = original_row * k + k_idx;\n            const T       row_scale                      = scales[k_offset];\n            const T*      expanded_permuted_rows_row_ptr = expanded_permuted_rows + expanded_permuted_row * cols;\n\n            const int expert_idx = expert_for_source_row[k_offset];\n            const T*  bias_ptr   = bias + expert_idx * cols;\n\n            thread_output = thread_output + row_scale * (expanded_permuted_rows_row_ptr[tid] + bias_ptr[tid]);\n        }\n        reduced_row_ptr[tid] = thread_output;\n    }\n}\n\ntemplate<typename T>\nvoid finalize_moe_routing_kernelLauncher(const T*     expanded_permuted_rows,\n                                         T*           reduced_unpermuted_output,\n                                         const T*     skip,\n                                         const T*     bias,\n                                         const T*     scales,\n                                         const int*   expanded_source_row_to_expanded_dest_row,\n                                         const int*   expert_for_source_row,\n                                         const int    num_rows,\n                                         const int    cols,\n                                         const int    k,\n                                         cudaStream_t stream)\n{\n    const int blocks  = num_rows;\n    const int threads = std::min(cols, 1024);\n    finalize_moe_routing_kernel<T, 1><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                      reduced_unpermuted_output,\n                                                                      skip,\n                                                                      nullptr,\n                                                                      bias,\n                                                                      scales,\n                                                                      expanded_source_row_to_expanded_dest_row,\n                                                                      expert_for_source_row,\n                                                                      cols,\n                                                                      k);\n}\n\ntemplate<typename T>\nvoid finalize_moe_routing_kernelLauncher(const T*     expanded_permuted_rows,\n                                         T*           reduced_unpermuted_output,\n                                         const T*     skip_1,\n                                         const T*     skip_2,\n                                         const T*     bias,\n                                         const T*     scales,\n                                         const int*   expanded_source_row_to_expanded_dest_row,\n                                         const int*   expert_for_source_row,\n                                         const int    num_rows,\n                                         const int    cols,\n                                         const int    k,\n                                         cudaStream_t stream)\n{\n    const int blocks  = num_rows;\n    const int threads = std::min(cols, 1024);\n    if (skip_2 == nullptr) {\n        finalize_moe_routing_kernel<T, 1><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                          reduced_unpermuted_output,\n                                                                          skip_1,\n                                                                          skip_2,\n                                                                          bias,\n                                                                          scales,\n                                                                          expanded_source_row_to_expanded_dest_row,\n                                                                          expert_for_source_row,\n                                                                          cols,\n                                                                          k);\n    }\n    else {\n        finalize_moe_routing_kernel<T, 2><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                          reduced_unpermuted_output,\n                                                                          skip_1,\n                                                                          skip_2,\n                                                                          bias,\n                                                                          scales,\n                                                                          expanded_source_row_to_expanded_dest_row,\n                                                                          expert_for_source_row,\n                                                                          cols,\n                                                                          k);\n    }\n}\n\n// ========================= TopK Softmax specializations ===========================\ntemplate void topk_gating_softmax_kernelLauncher(\n    const float*, const bool*, float*, float*, int*, int*, const int, const int, const int, cudaStream_t);\ntemplate void topk_gating_softmax_kernelLauncher(\n    const half*, const bool*, half*, half*, int*, int*, const int, const int, const int, cudaStream_t);\n\n#ifdef ENABLE_BF16\ntemplate void topk_gating_softmax_kernelLauncher(const __nv_bfloat16*,\n                                                 const bool*,\n                                                 __nv_bfloat16*,\n                                                 __nv_bfloat16*,\n                                                 int*,\n                                                 int*,\n                                                 const int,\n                                                 const int,\n                                                 const int,\n                                                 cudaStream_t);\n#endif\n\n// ==================== Variable batched GEMM specializations ==================================\ntemplate class CutlassMoeFCRunner<float, float>;\n\n#ifdef ENABLE_BF16\ntemplate class CutlassMoeFCRunner<__nv_bfloat16, __nv_bfloat16>;\ntemplate class CutlassMoeFCRunner<__nv_bfloat16, uint8_t>;\ntemplate class CutlassMoeFCRunner<__nv_bfloat16, cutlass::uint4b_t>;\n#endif\n\ntemplate class CutlassMoeFCRunner<half, half>;\ntemplate class CutlassMoeFCRunner<half, uint8_t>;\ntemplate class CutlassMoeFCRunner<half, cutlass::uint4b_t>;\n\n// ===================== Specializations for init routing =========================\ntemplate void initialize_moe_routing_kernelLauncher(\n    const float*, float*, const int*, int*, const int, const int, const int, const int, cudaStream_t);\ntemplate void initialize_moe_routing_kernelLauncher(\n    const half*, half*, const int*, int*, const int, const int, const int, const int, cudaStream_t);\n#ifdef ENABLE_BF16\ntemplate void initialize_moe_routing_kernelLauncher(\n    const __nv_bfloat16*, __nv_bfloat16*, const int*, int*, const int, const int, const int, const int, cudaStream_t);\n#endif\n\n// ==================== Specializations for final routing ===================================\ntemplate void finalize_moe_routing_kernelLauncher(const float*,\n                                                  float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const half*,\n                                                  half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const float*,\n                                                  float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const half*,\n                                                  half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\n#ifdef ENABLE_BF16\ntemplate void finalize_moe_routing_kernelLauncher(const __nv_bfloat16*,\n                                                  __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const __nv_bfloat16*,\n                                                  __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const __nv_bfloat16*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  cudaStream_t);\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_fp16.h>\n#include <math.h>\n#include <sstream>\n\n// Ignore CUTLASS warnings about type punning\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/numeric_types.h\"\n\n#pragma GCC diagnostic pop\n\n#include \"src/fastertransformer/kernels/moe_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/util_type.cuh>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#include \"3rdparty/cub/device/device_radix_sort.cuh\"\n#include \"3rdparty/cub/util_type.cuh\"\n#endif\n\nnamespace fastertransformer {\n\nstatic constexpr int WARP_SIZE = 32;\n\n// ====================== Softmax things ===============================\n// We have our own implementation of softmax here so we can support transposing the output\n// in the softmax kernel when we extend this module to support expert-choice routing.\ntemplate<typename T, int TPB>\n__launch_bounds__(TPB) __global__ void moe_softmax(const T* input, const bool* finished, T* output, const int num_cols)\n{\n    using BlockReduce = hipcub::BlockReduce<float, TPB>;\n    __shared__ typename BlockReduce::TempStorage tmpStorage;\n\n    __shared__ float normalizing_factor;\n    __shared__ float float_max;\n\n    const int thread_row_offset = blockIdx.x * num_cols;\n\n    hipcub::Sum sum;\n    float    threadData(-FLT_MAX);\n\n    // Don't touch finished rows.\n    if ((finished != nullptr) && finished[blockIdx.x]) {\n        return;\n    }\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int idx = thread_row_offset + ii;\n        threadData    = max(static_cast<float>(input[idx]), threadData);\n    }\n\n    const float maxElem = BlockReduce(tmpStorage).Reduce(threadData, hipcub::Max());\n    if (threadIdx.x == 0) {\n        float_max = maxElem;\n    }\n    __syncthreads();\n\n    threadData = 0;\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int idx = thread_row_offset + ii;\n        threadData += exp((static_cast<float>(input[idx]) - float_max));\n    }\n\n    const auto Z = BlockReduce(tmpStorage).Reduce(threadData, sum);\n\n    if (threadIdx.x == 0) {\n        normalizing_factor = 1.f / Z;\n    }\n    __syncthreads();\n\n    for (int ii = threadIdx.x; ii < num_cols; ii += TPB) {\n        const int   idx = thread_row_offset + ii;\n        const float val = exp((static_cast<float>(input[idx]) - float_max)) * normalizing_factor;\n        output[idx]     = T(val);\n    }\n}\n\ntemplate<typename T, int TPB>\n__launch_bounds__(TPB) __global__ void moe_top_k(const T*    inputs_after_softmax,\n                                                 const bool* finished,\n                                                 T*          output,\n                                                 int*        indices,\n                                                 int*        source_rows,\n                                                 const int   num_experts,\n                                                 const int   k)\n{\n\n    using cub_kvp     = hipcub::KeyValuePair<int, T>;\n    using BlockReduce = hipcub::BlockReduce<cub_kvp, TPB>;\n    __shared__ typename BlockReduce::TempStorage tmpStorage;\n\n    cub_kvp     thread_kvp;\n    hipcub::ArgMax arg_max;\n\n    const int num_rows  = gridDim.x;\n    const int block_row = blockIdx.x;\n\n    const bool should_process_row = finished ? !finished[block_row] : true;\n    const int  thread_read_offset = blockIdx.x * num_experts;\n    for (int k_idx = 0; k_idx < k; ++k_idx) {\n        thread_kvp.key   = 0;\n        thread_kvp.value = T(-1.f);  // This is OK because inputs are probabilities\n\n        cub_kvp inp_kvp;\n        for (int expert = threadIdx.x; expert < num_experts; expert += TPB) {\n            const int idx = thread_read_offset + expert;\n            inp_kvp.key   = expert;\n            inp_kvp.value = inputs_after_softmax[idx];\n\n            for (int prior_k = 0; prior_k < k_idx; ++prior_k) {\n                const int prior_winning_expert = indices[k * block_row + prior_k];\n\n                if (prior_winning_expert == expert) {\n                    inp_kvp = thread_kvp;\n                }\n            }\n\n            thread_kvp = arg_max(inp_kvp, thread_kvp);\n        }\n\n        const cub_kvp result_kvp = BlockReduce(tmpStorage).Reduce(thread_kvp, arg_max);\n        if (threadIdx.x == 0) {\n            const int idx    = k * block_row + k_idx;\n            output[idx]      = result_kvp.value;\n            indices[idx]     = should_process_row ? result_kvp.key : num_experts;\n            source_rows[idx] = k_idx * num_rows + block_row;\n        }\n        __syncthreads();\n    }\n}\n\n// ====================== TopK softmax things ===============================\n\n/*\n  A Top-K gating softmax written to exploit when the number of experts in the MoE layers\n  are a small power of 2. This allows us to cleanly share the rows among the threads in\n  a single warp and eliminate communication between warps (so no need to use shared mem).\n\n  It fuses the softmax, max and argmax into a single kernel.\n\n  Limitations:\n  1) This implementation is intended for when the number of experts is a small power of 2.\n  2) This implementation assumes k is small, but will work for any k.\n*/\n\ntemplate<typename T, int VPT, int NUM_EXPERTS, int WARPS_PER_CTA, int BYTES_PER_LDG>\n__launch_bounds__(WARPS_PER_CTA* WARP_SIZE) __global__ void topk_gating_softmax(\n    const T* input, const bool* finished, T* output, const int num_rows, int* indices, int* source_rows, const int k)\n{\n    // We begin by enforcing compile time assertions and setting up compile time constants.\n    static_assert(VPT == (VPT & -VPT), \"VPT must be power of 2\");\n    static_assert(NUM_EXPERTS == (NUM_EXPERTS & -NUM_EXPERTS), \"NUM_EXPERTS must be power of 2\");\n    static_assert(BYTES_PER_LDG == (BYTES_PER_LDG & -BYTES_PER_LDG), \"BYTES_PER_LDG must be power of 2\");\n    static_assert(BYTES_PER_LDG <= 16, \"BYTES_PER_LDG must be leq 16\");\n\n    // Number of bytes each thread pulls in per load\n    static constexpr int ELTS_PER_LDG    = BYTES_PER_LDG / sizeof(T);\n    static constexpr int ELTS_PER_ROW    = NUM_EXPERTS;\n    static constexpr int THREADS_PER_ROW = ELTS_PER_ROW / VPT;\n    static constexpr int LDG_PER_THREAD  = VPT / ELTS_PER_LDG;\n\n    // Restrictions based on previous section.\n    static_assert(VPT % ELTS_PER_LDG == 0, \"The elements per thread must be a multiple of the elements per ldg\");\n    static_assert(WARP_SIZE % THREADS_PER_ROW == 0, \"The threads per row must cleanly divide the threads per warp\");\n    static_assert(THREADS_PER_ROW == (THREADS_PER_ROW & -THREADS_PER_ROW), \"THREADS_PER_ROW must be power of 2\");\n    static_assert(THREADS_PER_ROW <= WARP_SIZE, \"THREADS_PER_ROW can be at most warp size\");\n\n    // We have NUM_EXPERTS elements per row. We specialize for small #experts\n    static constexpr int ELTS_PER_WARP = WARP_SIZE * VPT;\n    static constexpr int ROWS_PER_WARP = ELTS_PER_WARP / ELTS_PER_ROW;\n    static constexpr int ROWS_PER_CTA  = WARPS_PER_CTA * ROWS_PER_WARP;\n\n    // Restrictions for previous section.\n    static_assert(ELTS_PER_WARP % ELTS_PER_ROW == 0, \"The elts per row must cleanly divide the total elt per warp\");\n\n    // ===================== From this point, we finally start computing run-time variables. ========================\n\n    // Compute CTA and warp rows. We pack multiple rows into a single warp, and a block contains WARPS_PER_CTA warps.\n    // This, each block processes a chunk of rows. We start by computing the start row for each block.\n    const int cta_base_row = blockIdx.x * ROWS_PER_CTA;\n\n    // Now, using the base row per thread block, we compute the base row per warp.\n    const int warp_base_row = cta_base_row + threadIdx.y * ROWS_PER_WARP;\n\n    // The threads in a warp are split into sub-groups that will work on a row.\n    // We compute row offset for each thread sub-group\n    const int thread_row_in_warp = threadIdx.x / THREADS_PER_ROW;\n    const int thread_row         = warp_base_row + thread_row_in_warp;\n\n    // Threads with indices out of bounds should early exit here.\n    if (thread_row >= num_rows)\n        return;\n    const bool should_process_row = finished ? !finished[thread_row] : true;\n\n    // We finally start setting up the read pointers for each thread. First, each thread jumps to the start of the\n    // row it will read.\n    const T* thread_row_ptr = input + thread_row * ELTS_PER_ROW;\n\n    // Now, we compute the group each thread belong to in order to determine the first column to start loads.\n    const int thread_group_idx         = threadIdx.x % THREADS_PER_ROW;\n    const int first_elt_read_by_thread = thread_group_idx * ELTS_PER_LDG;\n    const T*  thread_read_ptr          = thread_row_ptr + first_elt_read_by_thread;\n\n    // Determine the pointer type to use to read in the data depending on the BYTES_PER_LDG template param. In theory,\n    // this can support all powers of 2 up to 16.\n    using AccessType = cutlass::AlignedArray<T, ELTS_PER_LDG>;\n\n    // Finally, we pull in the data from global mem\n    cutlass::Array<T, VPT> row_chunk_input;\n    AccessType*            row_chunk_vec_ptr   = reinterpret_cast<AccessType*>(&row_chunk_input);\n    const AccessType*      vec_thread_read_ptr = reinterpret_cast<const AccessType*>(thread_read_ptr);\n#pragma unroll\n    for (int ii = 0; ii < LDG_PER_THREAD; ++ii) {\n        row_chunk_vec_ptr[ii] = vec_thread_read_ptr[ii * THREADS_PER_ROW];\n    }\n\n    using ComputeType = float;\n    using Converter   = cutlass::NumericArrayConverter<ComputeType, T, VPT>;\n    Converter                        compute_type_converter;\n    cutlass::Array<ComputeType, VPT> row_chunk = compute_type_converter(row_chunk_input);\n\n    // First, we perform a max reduce within the thread. We can do the max in fp16 safely (I think) and just\n    // convert to float afterwards for the exp + sum reduction.\n    ComputeType thread_max = row_chunk[0];\n#pragma unroll\n    for (int ii = 1; ii < VPT; ++ii) {\n        thread_max = max(thread_max, row_chunk[ii]);\n    }\n\n// Now, we find the max within the thread group and distribute among the threads. We use a butterfly reduce.\n#pragma unroll\n    for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n        thread_max = max(thread_max, __shfl_xor_sync(0xFFFFFFFF, thread_max, mask, THREADS_PER_ROW));\n    }\n\n    // From this point, thread max in all the threads have the max within the row.\n    // Now, we subtract the max from each element in the thread and take the exp. We also compute the thread local sum.\n    float row_sum = 0;\n#pragma unroll\n    for (int ii = 0; ii < VPT; ++ii) {\n        row_chunk[ii] = expf(row_chunk[ii] - thread_max);\n        row_sum += row_chunk[ii];\n    }\n\n// Now, we perform the sum reduce within each thread group. Similar to the max reduce, we use a bufferfly pattern.\n#pragma unroll\n    for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n        row_sum += __shfl_xor_sync(0xFFFFFFFF, row_sum, mask, THREADS_PER_ROW);\n    }\n\n    // From this point, all threads have the max and the sum for their rows in the thread_max and thread_sum variables\n    // respectively. Finally, we can scale the rows for the softmax. Technically, for top-k gating we don't need to\n    // compute the entire softmax row. We can likely look at the maxes and only compute for the top-k values in the row.\n    // However, this kernel will likely not be a bottle neck and it seems better to closer match torch and find the\n    // argmax after computing the softmax.\n    const float reciprocal_row_sum = 1.f / row_sum;\n\n#pragma unroll\n    for (int ii = 0; ii < VPT; ++ii) {\n        row_chunk[ii] = row_chunk[ii] * reciprocal_row_sum;\n    }\n\n    // Now, softmax_res contains the softmax of the row chunk. Now, I want to find the topk elements in each row, along\n    // with the max index.\u200b\n    int                  start_col          = first_elt_read_by_thread;\n    static constexpr int COLS_PER_GROUP_LDG = ELTS_PER_LDG * THREADS_PER_ROW;\n\n    for (int k_idx = 0; k_idx < k; ++k_idx) {\n        // First, each thread does the local argmax\n        float max_val = row_chunk[0];\n        int   expert  = start_col;\n#pragma unroll\n        for (int ldg = 0, col = start_col; ldg < LDG_PER_THREAD; ++ldg, col += COLS_PER_GROUP_LDG) {\n#pragma unroll\n            for (int ii = 0; ii < ELTS_PER_LDG; ++ii) {\n                float val = row_chunk[ldg * ELTS_PER_LDG + ii];\n\n                // No check on the experts here since columns with the smallest index are processed first and only\n                // updated if > (not >=)\n                if (val > max_val) {\n                    max_val = val;\n                    expert  = col + ii;\n                }\n            }\n        }\n\n// Now, we perform the argmax reduce. We use the butterfly pattern so threads reach consensus about the max.\n// This will be useful for K > 1 so that the threads can agree on \"who\" had the max value. That thread can\n// then blank out their max with -inf and the warp can run more iterations...\n#pragma unroll\n        for (int mask = THREADS_PER_ROW / 2; mask > 0; mask /= 2) {\n            float other_max    = __shfl_xor_sync(0xFFFFFFFF, max_val, mask, THREADS_PER_ROW);\n            int   other_expert = __shfl_xor_sync(0xFFFFFFFF, expert, mask, THREADS_PER_ROW);\n\n            // We want lower indices to \"win\" in every thread so we break ties this way\n            if (other_max > max_val || (other_max == max_val && other_expert < expert)) {\n                max_val = other_max;\n                expert  = other_expert;\n            }\n        }\n\n        // Write the max for this k iteration to global memory.\n        if (thread_group_idx == 0) {\n            // The lead thread from each sub-group will write out the final results to global memory. (This will be a\n            // single) thread per row of the input/output matrices.\n            const int idx    = k * thread_row + k_idx;\n            output[idx]      = T(max_val);\n            indices[idx]     = should_process_row ? expert : NUM_EXPERTS;\n            source_rows[idx] = k_idx * num_rows + thread_row;\n        }\n\n        // Finally, we clear the value in the thread with the current max if there is another iteration to run.\n        if (k_idx + 1 < k) {\n            const int ldg_group_for_expert     = expert / COLS_PER_GROUP_LDG;\n            const int thread_to_clear_in_group = (expert / ELTS_PER_LDG) % THREADS_PER_ROW;\n\n            // Only the thread in the group which produced the max will reset the \"winning\" value to -inf.\n            if (thread_group_idx == thread_to_clear_in_group) {\n                const int offset_for_expert = expert % ELTS_PER_LDG;\n                // Safe to set to any negative value since row_chunk values must be between 0 and 1.\n                row_chunk[ldg_group_for_expert * ELTS_PER_LDG + offset_for_expert] = ComputeType(-10000.f);\n            }\n        }\n    }\n}\n\nnamespace detail {\n// Constructs some constants needed to partition the work across threads at compile time.\ntemplate<typename T, int EXPERTS, int BYTES_PER_LDG>\nstruct TopkConstants {\n    static constexpr int ELTS_PER_LDG = BYTES_PER_LDG / sizeof(T);\n    static_assert(EXPERTS / (ELTS_PER_LDG * WARP_SIZE) == 0 || EXPERTS % (ELTS_PER_LDG * WARP_SIZE) == 0, \"\");\n    static constexpr int VECs_PER_THREAD = std::max(1, EXPERTS / (ELTS_PER_LDG * WARP_SIZE));\n    static constexpr int VPT             = VECs_PER_THREAD * ELTS_PER_LDG;\n    static constexpr int THREADS_PER_ROW = EXPERTS / VPT;\n    static constexpr int ROWS_PER_WARP   = WARP_SIZE / THREADS_PER_ROW;\n};\n}  // namespace detail\n\ntemplate<typename T, int EXPERTS, int WARPS_PER_TB>\nvoid topk_gating_softmax_launcher_helper(const T*     input,\n                                         const bool*  finished,\n                                         T*           output,\n                                         int*         indices,\n                                         int*         source_row,\n                                         const int    num_rows,\n                                         const int    num_experts,\n                                         const int    k,\n                                         hipStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr unsigned long MAX_BYTES_PER_LDG = 16;\n\n    static constexpr int BYTES_PER_LDG = std::min(MAX_BYTES_PER_LDG, sizeof(T) * EXPERTS);\n    using Constants                    = detail::TopkConstants<T, EXPERTS, BYTES_PER_LDG>;\n    static constexpr int VPT           = Constants::VPT;\n    static constexpr int ROWS_PER_WARP = Constants::ROWS_PER_WARP;\n    const int            num_warps     = (num_rows + ROWS_PER_WARP - 1) / ROWS_PER_WARP;\n    const int            num_blocks    = (num_warps + WARPS_PER_TB - 1) / WARPS_PER_TB;\n\n    dim3 block_dim(WARP_SIZE, WARPS_PER_TB);\n    topk_gating_softmax<T, VPT, EXPERTS, WARPS_PER_TB, BYTES_PER_LDG>\n        <<<num_blocks, block_dim, 0, stream>>>(input, finished, output, num_rows, indices, source_row, k);\n}\n\ntemplate<typename T>\nvoid topk_gating_softmax_kernelLauncher(const T*     input,\n                                        const bool*  finished,\n                                        T*           output,\n                                        T*           softmax_temp_output,\n                                        int*         indices,\n                                        int*         source_row,\n                                        const int    num_rows,\n                                        const int    num_experts,\n                                        const int    k,\n                                        hipStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr int WARPS_PER_TB = 4;\n\n    switch (num_experts) {\n        case 2: {\n            topk_gating_softmax_launcher_helper<T, 2, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 4: {\n            topk_gating_softmax_launcher_helper<T, 4, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 8: {\n            topk_gating_softmax_launcher_helper<T, 8, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 16: {\n            topk_gating_softmax_launcher_helper<T, 16, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 32: {\n            topk_gating_softmax_launcher_helper<T, 32, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 64: {\n            topk_gating_softmax_launcher_helper<T, 64, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 128: {\n            topk_gating_softmax_launcher_helper<T, 128, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        case 256: {\n            topk_gating_softmax_launcher_helper<T, 256, WARPS_PER_TB>(\n                input, finished, output, indices, source_row, num_rows, num_experts, k, stream);\n            break;\n        }\n        default: {\n            static constexpr int TPB = 256;\n            FT_CHECK(softmax_temp_output != nullptr);\n            moe_softmax<T, TPB><<<num_rows, TPB, 0, stream>>>(input, finished, softmax_temp_output, num_experts);\n            moe_top_k<T, TPB><<<num_rows, TPB, 0, stream>>>(\n                softmax_temp_output, finished, output, indices, source_row, num_experts, k);\n        }\n    }\n}\n\n// ========================== CUB Sorting things ====================================\nCubKeyValueSorter::CubKeyValueSorter(): num_experts_(0), num_bits_(sizeof(int) * 8)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\nCubKeyValueSorter::CubKeyValueSorter(const int num_experts):\n    num_experts_(num_experts), num_bits_((int)log2(num_experts) + 1)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\nvoid CubKeyValueSorter::update_num_experts(const int num_experts)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    num_experts_ = num_experts;\n    num_bits_    = (int)log2(num_experts) + 1;\n}\n\nsize_t CubKeyValueSorter::getWorkspaceSize(const size_t num_key_value_pairs)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    num_key_value_pairs_    = num_key_value_pairs;\n    size_t required_storage = 0;\n    int*   null_int         = nullptr;\n    hipcub::DeviceRadixSort::SortPairs(\n        NULL, required_storage, null_int, null_int, null_int, null_int, num_key_value_pairs, 0, num_bits_);\n    return required_storage;\n}\n\nvoid CubKeyValueSorter::run(void*        workspace,\n                            const size_t workspace_size,\n                            const int*   keys_in,\n                            int*         keys_out,\n                            const int*   values_in,\n                            int*         values_out,\n                            const size_t num_key_value_pairs,\n                            hipStream_t stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    size_t expected_ws_size = getWorkspaceSize(num_key_value_pairs);\n    size_t actual_ws_size   = workspace_size;\n\n    if (expected_ws_size > workspace_size) {\n        std::stringstream err_ss;\n        err_ss << \"[FT Error][CubKeyValueSorter::run]\\n\";\n        err_ss << \"Error. The allocated workspace is too small to run this problem.\\n\";\n        err_ss << \"Expected workspace size of at least \" << expected_ws_size << \" but got problem size \"\n               << workspace_size << \"\\n\";\n        throw std::runtime_error(err_ss.str());\n    }\n    hipcub::DeviceRadixSort::SortPairs(\n        workspace, actual_ws_size, keys_in, keys_out, values_in, values_out, num_key_value_pairs, 0, num_bits_, stream);\n}\n\n// ============================== Infer GEMM sizes =================================\n__device__ inline int find_total_elts_leq_target(const int* sorted_indices, const int arr_length, const int target)\n{\n    int64_t low = 0, high = arr_length - 1, target_location = -1;\n    while (low <= high) {\n        int64_t mid = (low + high) / 2;\n\n        if (sorted_indices[mid] > target) {\n            high = mid - 1;\n        }\n        else {\n            low             = mid + 1;\n            target_location = mid;\n        }\n    }\n    return target_location + 1;\n}\n\n// Sets up the gemm assuming the inputs, experts and outputs are stored in row major order.\n// Assumes we want to perform output = matmul(inputs, experts) + bias\n__global__ void compute_total_rows_before_expert_kernel(const int*    sorted_experts,\n                                                        const int     sorted_experts_len,\n                                                        const int64_t num_experts,\n                                                        int64_t*      total_rows_before_expert)\n{\n\n    // First, compute the global tid. We only need 1 thread per expert.\n    const int expert = blockIdx.x * blockDim.x + threadIdx.x;\n    if (expert >= num_experts)\n        return;\n\n    // This should construct the last index where each expert occurs.\n    total_rows_before_expert[expert] = find_total_elts_leq_target(sorted_experts, sorted_experts_len, expert);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nCutlassMoeFCRunner<T, WeightType, Enable>::CutlassMoeFCRunner()\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nsize_t CutlassMoeFCRunner<T, WeightType, Enable>::getWorkspaceSize(\n    const int num_rows, const int hidden_size, const int inter_size, const int num_experts, const int k)\n{\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int buf_size         = pad_to_multiple_of_16(k * num_rows * hidden_size);\n    const int interbuf_size    = pad_to_multiple_of_16(k * num_rows * inter_size);\n    const int padded_experts   = pad_to_multiple_of_16(num_experts);\n    const int num_moe_inputs   = pad_to_multiple_of_16(k * num_rows);\n    int       num_softmax_outs = 0;\n\n    const bool is_pow_2 = (num_experts != 0) && ((num_experts & (num_experts - 1)) == 0);\n    if (!is_pow_2 || num_experts > 256) {\n        num_softmax_outs = pad_to_multiple_of_16(num_rows * num_experts);\n    }\n\n    // softmax output, permuted_rows and permuted_experts have moved to outside of moe kernel, allocate them\n    // in Encoder or Decoder before invoking FfnLayer forward.\n    size_t total_ws_bytes = 3 * num_moe_inputs * sizeof(int);  // source_rows_, permuted_rows_, permuted_experts_\n    total_ws_bytes += buf_size * sizeof(T);                    // permuted_data\n    total_ws_bytes += padded_experts * sizeof(int64_t);        // Hold total_rows_before_expert_\n    total_ws_bytes += num_softmax_outs * sizeof(T);\n    const int bytes_for_fc1_result = interbuf_size * sizeof(T);\n    const int sorter_ws_size_bytes = pad_to_multiple_of_16(sorter_.getWorkspaceSize(num_rows));\n    sorter_.update_num_experts(num_experts);\n\n    int bytes_for_intermediate_and_sorting = bytes_for_fc1_result;\n    if (sorter_ws_size_bytes > bytes_for_fc1_result) {\n        int remaining_bytes = pad_to_multiple_of_16(sorter_ws_size_bytes - bytes_for_fc1_result);\n        bytes_for_intermediate_and_sorting += remaining_bytes;\n    }\n\n    total_ws_bytes += bytes_for_intermediate_and_sorting;  // intermediate (fc1) output + cub sorting workspace\n    return total_ws_bytes;\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::configure_ws_ptrs(\n    char* ws_ptr, const int num_rows, const int hidden_size, const int inter_size, const int num_experts, const int k)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int buf_size       = pad_to_multiple_of_16(k * num_rows * hidden_size);\n    const int interbuf_size  = pad_to_multiple_of_16(k * num_rows * inter_size);\n    const int padded_experts = pad_to_multiple_of_16(num_experts);\n    const int num_moe_inputs = pad_to_multiple_of_16(k * num_rows);\n    // const int num_softmax_outs = pad_to_multiple_of_16(num_rows * num_experts);\n\n    source_rows_      = (int*)ws_ptr;\n    permuted_rows_    = source_rows_ + num_moe_inputs;\n    permuted_experts_ = permuted_rows_ + num_moe_inputs;\n    permuted_data_    = (T*)(permuted_experts_ + num_moe_inputs);\n\n    total_rows_before_expert_ = (int64_t*)(permuted_data_ + buf_size);\n\n    fc1_result_ = (T*)(total_rows_before_expert_ + padded_experts);\n\n    const bool is_pow_2 = (num_experts != 0) && ((num_experts & (num_experts - 1)) == 0);\n    if (!is_pow_2 || num_experts > 256) {\n        softmax_out_ = (T*)(fc1_result_ + interbuf_size);\n    }\n    else {\n        softmax_out_ = nullptr;\n    }\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::run_moe_fc(const T*          input_activations,\n                                                           const T*          gating_output,\n                                                           const WeightType* fc1_expert_weights,\n                                                           const T*          fc1_scales,\n                                                           const T*          fc1_expert_biases,\n                                                           ActivationType    fc1_activation_type,\n                                                           const WeightType* fc2_expert_weights,\n                                                           const T*          fc2_scales,\n                                                           const int         num_rows,\n                                                           const int         hidden_size,\n                                                           const int         inter_size,\n                                                           const int         num_experts,\n                                                           const int         k,\n                                                           char*             workspace_ptr,\n                                                           T*                fc2_result,\n                                                           const bool*       finished,\n                                                           const int         active_rows,\n                                                           T*                expert_scales,\n                                                           int*              expanded_source_row_to_expanded_dest_row,\n                                                           int*              expert_for_source_row,\n                                                           hipStream_t      stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    static constexpr bool scales_required =\n        std::is_same<WeightType, uint8_t>::value || std::is_same<WeightType, cutlass::uint4b_t>::value;\n\n    if (scales_required) {\n        if (fc1_scales == nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales expected but scale for first matmul is a null pointer\");\n        }\n        else if (fc2_scales == nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales expected but scale for second matmul is a null pointer\");\n        }\n    }\n    else {\n        if (fc1_scales != nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales are ignored for fp32/fp16/bf16 but received scale for FC1\");\n        }\n        else if (fc2_scales != nullptr) {\n            throw std::runtime_error(\n                \"[FT Error][Run MoE FC] Scales are ignored for fp32/fp16/bf16 but received scale for FC2\");\n        }\n    }\n\n    configure_ws_ptrs(workspace_ptr, num_rows, hidden_size, inter_size, num_experts, k);\n    topk_gating_softmax_kernelLauncher<T>(gating_output,\n                                          finished,\n                                          expert_scales,\n                                          softmax_out_,\n                                          expert_for_source_row,\n                                          source_rows_,\n                                          num_rows,\n                                          num_experts,\n                                          k,\n                                          stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n\n    const int sorter_ws_size_bytes = pad_to_multiple_of_16(sorter_.getWorkspaceSize(k * num_rows));\n    sorter_.run((void*)fc1_result_,\n                sorter_ws_size_bytes,\n                expert_for_source_row,\n                permuted_experts_,\n                source_rows_,\n                permuted_rows_,\n                k * num_rows,\n                stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n\n    initialize_moe_routing_kernelLauncher(input_activations,\n                                          permuted_data_,\n                                          permuted_rows_,\n                                          expanded_source_row_to_expanded_dest_row,\n                                          num_rows,\n                                          active_rows,\n                                          hidden_size,\n                                          k,\n                                          stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n\n    const int expanded_active_expert_rows = k * active_rows;\n    compute_total_rows_before_expert(\n        permuted_experts_, expanded_active_expert_rows, num_experts, total_rows_before_expert_, stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n\n    moe_gemm_runner_.moe_gemm_bias_act(permuted_data_,\n                                       fc1_expert_weights,\n                                       fc1_scales,\n                                       fc1_expert_biases,\n                                       fc1_result_,\n                                       total_rows_before_expert_,\n                                       expanded_active_expert_rows,\n                                       inter_size,\n                                       hidden_size,\n                                       num_experts,\n                                       fc1_activation_type,\n                                       stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n\n    moe_gemm_runner_.moe_gemm(fc1_result_,\n                              fc2_expert_weights,\n                              fc2_scales,\n                              fc2_result,\n                              total_rows_before_expert_,\n                              expanded_active_expert_rows,\n                              hidden_size,\n                              inter_size,\n                              num_experts,\n                              stream);\n\n#ifndef NDEBUG\n    hipDeviceSynchronize();\n    check_cuda_error(hipGetLastError());\n#endif\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::run_moe_fc(const T*          input_activations,\n                                                           const T*          gating_output,\n                                                           const WeightType* fc1_expert_weights,\n                                                           const T*          fc1_scales,\n                                                           const T*          fc1_expert_biases,\n                                                           ActivationType    fc1_activation_type,\n                                                           const WeightType* fc2_expert_weights,\n                                                           const T*          fc2_scales,\n                                                           const int         num_rows,\n                                                           const int         hidden_size,\n                                                           const int         inter_size,\n                                                           const int         num_experts,\n                                                           const int         k,\n                                                           char*             workspace_ptr,\n                                                           T*                fc2_result,\n                                                           T*                expert_scales,\n                                                           int*              expanded_source_row_to_expanded_dest_row,\n                                                           int*              expert_for_source_row,\n                                                           hipStream_t      stream)\n{\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    run_moe_fc(input_activations,\n               gating_output,\n               fc1_expert_weights,\n               fc1_scales,\n               fc1_expert_biases,\n               fc1_activation_type,\n               fc2_expert_weights,\n               fc2_scales,\n               num_rows,\n               hidden_size,\n               inter_size,\n               num_experts,\n               k,\n               workspace_ptr,\n               fc2_result,\n               nullptr,\n               num_rows,\n               expert_scales,\n               expanded_source_row_to_expanded_dest_row,\n               expert_for_source_row,\n               stream);\n}\n\ntemplate<typename T, typename WeightType, typename Enable>\nvoid CutlassMoeFCRunner<T, WeightType, Enable>::compute_total_rows_before_expert(const int*   sorted_indices,\n                                                                                 const int    total_indices,\n                                                                                 const int    num_experts,\n                                                                                 int64_t*     total_rows_before_expert,\n                                                                                 hipStream_t stream)\n{\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    const int threads = std::min(1024, num_experts);\n    const int blocks  = (num_experts + threads - 1) / threads;\n\n    compute_total_rows_before_expert_kernel<<<blocks, threads, 0, stream>>>(\n        sorted_indices, total_indices, num_experts, total_rows_before_expert);\n}\n\n// ========================== Permutation things =======================================\n\n// Duplicated and permutes rows for MoE. In addition, reverse the permutation map to help with finalizing routing.\n\n// \"expanded_x_row\" simply means that the number of values is num_rows x k. It is \"expanded\" since we will have to\n// duplicate some rows in the input matrix to match the dimensions. Duplicates will always get routed to separate\n// experts in the end.\n\n// Note that the expanded_dest_row_to_expanded_source_row map referred to here has indices in the range (0,\n// k*rows_in_input - 1). However, it is set up so that index 0, rows_in_input, 2*rows_in_input ... (k-1)*rows_in_input\n// all map to row 0 in the original matrix. Thus, to know where to read in the source matrix, we simply take the modulus\n// of the expanded index.\n\ntemplate<typename T>\n__global__ void initialize_moe_routing_kernel(const T*   unpermuted_input,\n                                              T*         permuted_output,\n                                              const int* expanded_dest_row_to_expanded_source_row,\n                                              int*       expanded_source_row_to_expanded_dest_row,\n                                              const int  num_rows,\n                                              const int  active_rows,\n                                              const int  cols)\n{\n\n    // Reverse permutation map.\n    // I do this so that later, we can use the source -> dest map to do the k-way reduction and unpermuting. I need the\n    // reverse map for that reduction to allow each threadblock to do 1 k-way reduce without atomics later in MoE. 1\n    // thread block will be responsible for all k summations.\n    const int expanded_dest_row   = blockIdx.x;\n    const int expanded_source_row = expanded_dest_row_to_expanded_source_row[expanded_dest_row];\n    if (threadIdx.x == 0) {\n        expanded_source_row_to_expanded_dest_row[expanded_source_row] = expanded_dest_row;\n    }\n\n    if (blockIdx.x < active_rows) {\n        // Duplicate and permute rows\n        const int source_row = expanded_source_row % num_rows;\n\n        const T* source_row_ptr = unpermuted_input + source_row * cols;\n        T*       dest_row_ptr   = permuted_output + expanded_dest_row * cols;\n\n        for (int tid = threadIdx.x; tid < cols; tid += blockDim.x) {\n            dest_row_ptr[tid] = source_row_ptr[tid];\n        }\n    }\n}\n\ntemplate<typename T>\nvoid initialize_moe_routing_kernelLauncher(const T*     unpermuted_input,\n                                           T*           permuted_output,\n                                           const int*   expanded_dest_row_to_expanded_source_row,\n                                           int*         expanded_source_row_to_expanded_dest_row,\n                                           const int    num_rows,\n                                           const int    active_rows,\n                                           const int    cols,\n                                           const int    k,\n                                           hipStream_t stream)\n{\n    const int blocks  = num_rows * k;\n    const int threads = std::min(cols, 1024);\n    initialize_moe_routing_kernel<T><<<blocks, threads, 0, stream>>>(unpermuted_input,\n                                                                     permuted_output,\n                                                                     expanded_dest_row_to_expanded_source_row,\n                                                                     expanded_source_row_to_expanded_dest_row,\n                                                                     num_rows,\n                                                                     k * active_rows,\n                                                                     cols);\n}\n\n// Final kernel to unpermute and scale\n// This kernel unpermutes the original data, does the k-way reduction and performs the final skip connection.\ntemplate<typename T, int RESIDUAL_NUM>\n__global__ void finalize_moe_routing_kernel(const T*   expanded_permuted_rows,\n                                            T*         reduced_unpermuted_output,\n                                            const T*   skip_1,\n                                            const T*   skip_2,\n                                            const T*   bias,\n                                            const T*   scales,\n                                            const int* expanded_source_row_to_expanded_dest_row,\n                                            const int* expert_for_source_row,\n                                            const int  cols,\n                                            const int  k)\n{\n\n    const int original_row    = blockIdx.x;\n    const int num_rows        = gridDim.x;\n    T*        reduced_row_ptr = reduced_unpermuted_output + original_row * cols;\n    const T*  skip_1_row_ptr  = skip_1 + original_row * cols;\n    const T*  skip_2_row_ptr;\n    if (RESIDUAL_NUM == 2) {\n        skip_2_row_ptr = skip_2 + original_row * cols;\n    }\n\n    for (int tid = threadIdx.x; tid < cols; tid += blockDim.x) {\n        T thread_output;\n        if (RESIDUAL_NUM == 1) {\n            thread_output = skip_1_row_ptr[tid];\n        }\n        else if (RESIDUAL_NUM == 2) {\n            thread_output = skip_1_row_ptr[tid] + skip_2_row_ptr[tid];\n        }\n        for (int k_idx = 0; k_idx < k; ++k_idx) {\n            const int expanded_original_row = original_row + k_idx * num_rows;\n            const int expanded_permuted_row = expanded_source_row_to_expanded_dest_row[expanded_original_row];\n\n            const int64_t k_offset                       = original_row * k + k_idx;\n            const T       row_scale                      = scales[k_offset];\n            const T*      expanded_permuted_rows_row_ptr = expanded_permuted_rows + expanded_permuted_row * cols;\n\n            const int expert_idx = expert_for_source_row[k_offset];\n            const T*  bias_ptr   = bias + expert_idx * cols;\n\n            thread_output = thread_output + row_scale * (expanded_permuted_rows_row_ptr[tid] + bias_ptr[tid]);\n        }\n        reduced_row_ptr[tid] = thread_output;\n    }\n}\n\ntemplate<typename T>\nvoid finalize_moe_routing_kernelLauncher(const T*     expanded_permuted_rows,\n                                         T*           reduced_unpermuted_output,\n                                         const T*     skip,\n                                         const T*     bias,\n                                         const T*     scales,\n                                         const int*   expanded_source_row_to_expanded_dest_row,\n                                         const int*   expert_for_source_row,\n                                         const int    num_rows,\n                                         const int    cols,\n                                         const int    k,\n                                         hipStream_t stream)\n{\n    const int blocks  = num_rows;\n    const int threads = std::min(cols, 1024);\n    finalize_moe_routing_kernel<T, 1><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                      reduced_unpermuted_output,\n                                                                      skip,\n                                                                      nullptr,\n                                                                      bias,\n                                                                      scales,\n                                                                      expanded_source_row_to_expanded_dest_row,\n                                                                      expert_for_source_row,\n                                                                      cols,\n                                                                      k);\n}\n\ntemplate<typename T>\nvoid finalize_moe_routing_kernelLauncher(const T*     expanded_permuted_rows,\n                                         T*           reduced_unpermuted_output,\n                                         const T*     skip_1,\n                                         const T*     skip_2,\n                                         const T*     bias,\n                                         const T*     scales,\n                                         const int*   expanded_source_row_to_expanded_dest_row,\n                                         const int*   expert_for_source_row,\n                                         const int    num_rows,\n                                         const int    cols,\n                                         const int    k,\n                                         hipStream_t stream)\n{\n    const int blocks  = num_rows;\n    const int threads = std::min(cols, 1024);\n    if (skip_2 == nullptr) {\n        finalize_moe_routing_kernel<T, 1><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                          reduced_unpermuted_output,\n                                                                          skip_1,\n                                                                          skip_2,\n                                                                          bias,\n                                                                          scales,\n                                                                          expanded_source_row_to_expanded_dest_row,\n                                                                          expert_for_source_row,\n                                                                          cols,\n                                                                          k);\n    }\n    else {\n        finalize_moe_routing_kernel<T, 2><<<blocks, threads, 0, stream>>>(expanded_permuted_rows,\n                                                                          reduced_unpermuted_output,\n                                                                          skip_1,\n                                                                          skip_2,\n                                                                          bias,\n                                                                          scales,\n                                                                          expanded_source_row_to_expanded_dest_row,\n                                                                          expert_for_source_row,\n                                                                          cols,\n                                                                          k);\n    }\n}\n\n// ========================= TopK Softmax specializations ===========================\ntemplate void topk_gating_softmax_kernelLauncher(\n    const float*, const bool*, float*, float*, int*, int*, const int, const int, const int, hipStream_t);\ntemplate void topk_gating_softmax_kernelLauncher(\n    const half*, const bool*, half*, half*, int*, int*, const int, const int, const int, hipStream_t);\n\n#ifdef ENABLE_BF16\ntemplate void topk_gating_softmax_kernelLauncher(const __hip_bfloat16*,\n                                                 const bool*,\n                                                 __hip_bfloat16*,\n                                                 __hip_bfloat16*,\n                                                 int*,\n                                                 int*,\n                                                 const int,\n                                                 const int,\n                                                 const int,\n                                                 hipStream_t);\n#endif\n\n// ==================== Variable batched GEMM specializations ==================================\ntemplate class CutlassMoeFCRunner<float, float>;\n\n#ifdef ENABLE_BF16\ntemplate class CutlassMoeFCRunner<__hip_bfloat16, __hip_bfloat16>;\ntemplate class CutlassMoeFCRunner<__hip_bfloat16, uint8_t>;\ntemplate class CutlassMoeFCRunner<__hip_bfloat16, cutlass::uint4b_t>;\n#endif\n\ntemplate class CutlassMoeFCRunner<half, half>;\ntemplate class CutlassMoeFCRunner<half, uint8_t>;\ntemplate class CutlassMoeFCRunner<half, cutlass::uint4b_t>;\n\n// ===================== Specializations for init routing =========================\ntemplate void initialize_moe_routing_kernelLauncher(\n    const float*, float*, const int*, int*, const int, const int, const int, const int, hipStream_t);\ntemplate void initialize_moe_routing_kernelLauncher(\n    const half*, half*, const int*, int*, const int, const int, const int, const int, hipStream_t);\n#ifdef ENABLE_BF16\ntemplate void initialize_moe_routing_kernelLauncher(\n    const __hip_bfloat16*, __hip_bfloat16*, const int*, int*, const int, const int, const int, const int, hipStream_t);\n#endif\n\n// ==================== Specializations for final routing ===================================\ntemplate void finalize_moe_routing_kernelLauncher(const float*,\n                                                  float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const half*,\n                                                  half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const float*,\n                                                  float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const float*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const half*,\n                                                  half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const half*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\n#ifdef ENABLE_BF16\ntemplate void finalize_moe_routing_kernelLauncher(const __hip_bfloat16*,\n                                                  __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\ntemplate void finalize_moe_routing_kernelLauncher(const __hip_bfloat16*,\n                                                  __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const __hip_bfloat16*,\n                                                  const int*,\n                                                  const int*,\n                                                  const int,\n                                                  const int,\n                                                  const int,\n                                                  hipStream_t);\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/moe_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/online_softmax_beamsearch_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n#define DO_SPLIT_SMALL_TOP_K_SOFTMAX\nstatic const int SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE = 256;\n\n#define TOPK_FP16_STORAGE 0\n\ntemplate<typename T>\n__device__ __forceinline__ T apply_length_penalty(T log_prob, int length, float length_penalty)\n{\n    // score = log(prob) / (length)^length_penalty.\n    if (length_penalty == 0.0f || length == 1) {\n        return log_prob;\n    }\n    return log_prob / static_cast<T>(powf(length, length_penalty));\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -FLT_MAX;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i] = partial.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void batch_topK_kernel(const int* __restrict topk_tmp_id_buf,\n                                                                      const T* __restrict topk_tmp_val_buf,\n                                                                      int* __restrict id_buf,\n                                                                      T* __restrict val_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -FLT_MAX;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i]  = partial.p[i];\n            val_buf[index + i] = partial.u[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void batch_topk_kernel(const int* __restrict x,\n                                                                      const T* __restrict y,\n                                                                      int* __restrict z,\n                                                                      float* __restrict v,\n                                                                      float*         output_log_probs,\n                                                                      const bool*    finished,\n                                                                      const int*     sequence_lengths,\n                                                                      BeamHypotheses beam_hyps,\n                                                                      const int      V,\n                                                                      const int      K,\n                                                                      const int      vocab_size,\n                                                                      const float    length_penalty,\n                                                                      const T        diversity_rate)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;\n\n    // reposition x, y to data for the current vector\n    x += vector_id * V;\n    y += vector_id * V;\n\n    typedef cub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ int                               selected_beams;\n    __shared__ float                             old_cum_log_probs[MAX_K];\n\n    if (thread_id == 0) {\n        selected_beams = 0;\n    }\n    if (thread_id < K) {\n        old_cum_log_probs[thread_id] = v[vector_id * K + thread_id];\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + vector_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && thread_id == 0) {\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == K) {\n            return;\n        }\n    }\n\n    TopK<T, MAX_K> partial;\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -FLT_MAX;\n    }\n\n    for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n        int i    = elem_id % K;\n        T   elem = length_penalty == 0.0f ? y[elem_id] :\n                                            apply_length_penalty(y[elem_id],\n                                                               finished[vector_id] ? sequence_lengths[vector_id] :\n                                                                                       sequence_lengths[vector_id] + 1,\n                                                               length_penalty);\n        elem += diversity_rate * (T)i;\n        int elem_idx = elem_id;  // x[elem_id];\n        partial.insert(elem, elem_idx);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * K;\n        v += vector_id * K;\n\n        for (int i = 0; i < MAX_K; ++i) {\n            if (beam_hyps.num_beams != nullptr && x[total.p[i]] % vocab_size == beam_hyps.end_ids[vector_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (i >= K) {\n                    // do nothing\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + vector_id;\n                    const float normed_score     = (float)total.u[i];\n                    const int   num_beam         = beam_hyps.num_beams[global_batch_idx];\n                    int         beam_idx         = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == K) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = K;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < K; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * (K * 2) + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]           = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * (K * 2) + j] = normed_score;\n                                    for (int l = 0; l < K; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * (K * 2) + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset =\n                        ((vector_id + beam_hyps.ite * beam_hyps.local_batch_size) * (K * 2) + beam_idx)\n                        * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = beam_hyps.end_ids[vector_id];\n                    if (beam_hyps.log_probs != nullptr) {\n                        beam_hyps.log_probs[tgt_id_offset + beam_hyps.step] =\n                            (float)y[total.p[i]] - old_cum_log_probs[(x[total.p[i]] / vocab_size) % K];\n                    }\n\n                    int prev_id = (x[total.p[i]] / vocab_size) % K;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * K\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * K + vector_id * K + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                            beam_hyps.log_probs[tgt_id_offset + j] = beam_hyps.log_probs_src[src_idx];\n                        }\n                        prev_id = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * (K * 2) + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                    beam_hyps.cum_log_probs[tgt_beam_idx] = (float)y[total.p[i]];\n                }\n            }\n            else if ((beam_hyps.num_beams != nullptr && i < 2 * K) || (beam_hyps.num_beams == nullptr && i < K)) {\n                z[selected_beams] = x[total.p[i]];\n                if (output_log_probs != nullptr) {\n                    output_log_probs[vector_id * K + selected_beams] =\n                        (float)y[total.p[i]] - old_cum_log_probs[(z[selected_beams] / vocab_size) % K];\n                }\n                v[selected_beams] = (float)y[total.p[i]];\n                selected_beams++;\n            }\n            __syncthreads();\n            if (selected_beams >= K) {\n                break;\n            }\n        }\n    }\n    if (threadIdx.x == 0 && beam_hyps.num_beams != nullptr) {\n        if (beam_hyps.num_beams[blockIdx.x] < K) {\n            beam_hyps.is_done[blockIdx.x] = false;\n        }\n        else if (beam_hyps.early_stopping) {\n            beam_hyps.is_done[blockIdx.x] = true;\n        }\n    }\n}\n\nstruct __align__(8) MD\n{\n    float m;\n    float d;\n};\n\n__device__ __forceinline__ MD reduce_md_op(MD a, MD b)\n{\n    bool a_bigger  = (a.m > b.m);\n    MD   bigger_m  = a_bigger ? a : b;\n    MD   smaller_m = a_bigger ? b : a;\n    MD   res;\n    res.d = bigger_m.d + smaller_m.d * __expf(smaller_m.m - bigger_m.m);\n    res.m = bigger_m.m;\n    return res;\n}\n\ntemplate<typename T, int MAX_K>\nstruct TopKMD {\n    MD             md;\n    TopK<T, MAX_K> topk;\n};\n\ntemplate<typename T, int MAX_K>\n__device__ __forceinline__ TopKMD<T, MAX_K> reduce_topk_md_op(const TopKMD<T, MAX_K>& a, const TopKMD<T, MAX_K>& b)\n{\n    TopKMD<T, MAX_K> res;\n    res.md   = reduce_md_op(a.md, b.md);\n    res.topk = reduce_topk_op(a.topk, b.topk);\n    return res;\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_online_softmax_topk_kernel(const T* __restrict x,\n                                                                                    const T* __restrict b,\n                                                                                    const float* __restrict c,\n                                                                                    const bool* __restrict finished,\n                                                                                    int* __restrict z,\n                                                                                    T* __restrict v,\n                                                                                    int V,\n                                                                                    int K,\n                                                                                    const int* __restrict end_ids)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    // reposition y to data for the current vector\n    x += vector_id * V;\n\n    typedef cub::BlockReduce<TopKMD<float, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage                     temp_storage;\n\n    TopKMD<float, MAX_K> partial;\n    bool                 finish = finished[vector_id];\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    if (finish) {\n        for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n            float elem = (elem_id == end_ids[vector_id / K]) ? MAX_T_VAL : -MAX_T_VAL;\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n            // if (elem_id > THREADBLOCK_SIZE * MAX_K && (elem_id == E)) break;\n        }\n    }\n    else {\n        for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n            float elem = x[elem_id] + b[elem_id];\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n\n    TopKMD<float, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<float, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * K;\n        v += vector_id * K;\n        c += vector_id;\n\n        // float d_total_inverse = __fdividef(1.0F, total.md.d);\n        float d_total_log = logf(total.md.d);\n        for (int i = 0; i < MAX_K; ++i) {\n            // float val = __expf(total.topk.u[i] - total.md.m) * d_total_inverse;\n            float val = total.topk.u[i] - total.md.m - d_total_log;\n            if (i < K) {\n                z[i] = total.topk.p[i] + vector_id * V;  // faster transformer needs absolute id\n                v[i] = val + c[0];\n            }\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE, 1) __global__\n    void beam_online_softmax_topk_stage1_kernel(const T* __restrict x,\n                                                const T* __restrict b,\n                                                const bool* __restrict finished,\n                                                float* __restrict t,\n                                                int V,\n                                                int K,\n                                                const int* __restrict end_ids)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;  // batch beam index.\n\n    const int PACKED_TOP_KMD_SIZE = 2 * MAX_K + 2;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    // one will have multiple sections per V\n    const int v_local       = (V + gridDim.y - 1) / gridDim.y;\n    const int section_start = v_local * blockIdx.y;\n    int       section_end   = section_start + v_local;\n    section_end             = (section_end > V) ? V : section_end;\n\n    // reposition x to data for the current vector\n    x += vector_id * V;\n#if TOPK_FP16_STORAGE == 1\n    typedef cub::BlockReduce<TopKMD<__half, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n#else\n    typedef cub::BlockReduce<TopKMD<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n#endif\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ float                             buf_s[PACKED_TOP_KMD_SIZE];  // save intermediate result\n\n#if TOPK_FP16_STORAGE == 1\n    TopKMD<__half, MAX_K> partial;\n#else\n    TopKMD<T, MAX_K>                                             partial;\n#endif\n    bool finish = finished[vector_id];\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    if (finish) {\n#pragma unroll 1\n        for (int elem_id = section_start + thread_id; elem_id < section_end; elem_id += THREADBLOCK_SIZE) {\n            float elem = (elem_id == end_ids[vector_id / K]) ? MAX_T_VAL : -MAX_T_VAL;\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n    else {\n#pragma unroll 1\n        for (int elem_id = section_start + thread_id; elem_id < section_end; elem_id += THREADBLOCK_SIZE) {\n            T  bias = b == nullptr ? (T)0.0f : b[elem_id];  // gpt-2 does not use bias\n            T  elem = x[elem_id] + bias;\n            MD new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n\n#if TOPK_FP16_STORAGE == 1\n    TopKMD<__half, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<__half, MAX_K>);\n#else\n    TopKMD<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<T, MAX_K>);\n#endif\n\n    if (thread_id == 0) {\n        for (int i = 0; i < 2 * K; i++) {\n            reinterpret_cast<int*>(buf_s)[i] = total.topk.p[i] + vector_id * V;  // faster transformer needs absolute id\n            buf_s[MAX_K + i]                 = total.topk.u[i];\n        }\n        buf_s[2 * MAX_K]     = total.md.d;\n        buf_s[2 * MAX_K + 1] = total.md.m;\n    }\n    __syncthreads();\n    for (int elem_id = thread_id; elem_id < PACKED_TOP_KMD_SIZE; elem_id += THREADBLOCK_SIZE) {\n        t[blockIdx.x * PACKED_TOP_KMD_SIZE * gridDim.y + blockIdx.y * PACKED_TOP_KMD_SIZE + elem_id] = buf_s[elem_id];\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_online_softmax_topk_stage2_kernel(\n    const float* __restrict x, const float* __restrict c, int* __restrict z, T* __restrict v, int K, int parts_per_beam)\n{\n    const int vector_id           = blockIdx.x;\n    const int thread_id           = threadIdx.x;\n    const int PACKED_TOP_KMD_SIZE = 2 * MAX_K + 2;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    extern __shared__ char buf_s_[];  // intermediate result\n    float*                 buf_s = reinterpret_cast<float*>(buf_s_);\n    //__shared__ float buf_s[PACKED_TOP_KMD_SIZE * THREADBLOCK_SIZE]; // intermediate result\n\n    typedef cub::BlockReduce<TopKMD<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage                 temp_storage;\n\n    x += vector_id * PACKED_TOP_KMD_SIZE * parts_per_beam;\n\n    TopKMD<T, MAX_K> partial;\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    // load and unpack into registers through smem\n    for (int idx = thread_id; idx < PACKED_TOP_KMD_SIZE * parts_per_beam; idx += THREADBLOCK_SIZE) {\n        buf_s[idx] = x[idx];\n    }\n    __syncthreads();\n\n    if (threadIdx.x < parts_per_beam) {\n        float* b_s = buf_s + thread_id * PACKED_TOP_KMD_SIZE;\n        for (int i = 0; i < 2 * K; i++) {\n            partial.topk.p[i] = reinterpret_cast<int*>(b_s)[i];\n            partial.topk.u[i] = b_s[MAX_K + i];\n        }\n        partial.md.d = b_s[2 * MAX_K];\n        partial.md.m = b_s[2 * MAX_K + 1];\n    }\n    __syncthreads();\n\n    TopKMD<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * 2 * K;\n        v += vector_id * 2 * K;\n        c += vector_id;\n\n        float d_total_log = logf(total.md.d);\n        for (int i = 0; i < MAX_K; ++i) {\n            float val = (float)total.topk.u[i] - total.md.m - d_total_log;\n            if (i < 2 * K) {\n                z[i] = total.topk.p[i];\n                v[i] = (float)val + (float)c[0];\n            }\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K>\nvoid beam_online_softmax_topk_stage2_kernelLauncher(const float* temp_storage,\n                                                    const float* cum_log_probs,\n                                                    int*         ids,\n                                                    T*           vals,\n                                                    int          batch_size,\n                                                    int          beam_width,\n                                                    int          parts_per_beam,\n                                                    cudaStream_t stream)\n{\n    // might rewrite beam_online_softmax_topk_stage2_kernel no to depend on constant block size\n    // in oreder to reduce compilation time\n    int smem_stage2_size = parts_per_beam * (2 * MAX_K + 2) * sizeof(float);\n\n    if (parts_per_beam <= 32) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 32><<<batch_size * beam_width, 32, smem_stage2_size, stream>>>(\n            temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    if (parts_per_beam <= 64) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 64><<<batch_size * beam_width, 64, smem_stage2_size, stream>>>(\n            temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    if (parts_per_beam <= 128) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 128>\n            <<<batch_size * beam_width, 128, smem_stage2_size, stream>>>(\n                temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    assert(0);\n}\n\ntemplate<typename T, int MAX_K>\nvoid topK_softMax_kernelLauncher(const T*        log_probs,\n                                 const T*        bias,\n                                 const bool*     finished,\n                                 const int*      sequence_lengths,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 int*            ids,\n                                 void*           temp_storage,\n                                 const int       temp_storage_size,\n                                 BeamHypotheses* beam_hyps,\n                                 const int       batch_size,\n                                 const int       beam_width,\n                                 const int       vocab_size,\n                                 const int*      end_ids,\n                                 T               diversity_rate,\n                                 const float     length_penalty,\n                                 cudaStream_t    stream)\n{\n    const int items_per_thread = 1;\n    const int block_sz         = (MAX_K < 16) ? (MAX_K < 8) ? SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE : 128 : 64;\n    // const int block_sz = SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE;\n\n    assert(temp_storage_size % 2 == 0);\n    assert(temp_storage_size >= 2 * batch_size * beam_width * beam_width * 2);\n    // Beam search needs the sequence lengths of beams to apply length penalty.\n    assert(length_penalty == 0.0f || sequence_lengths != nullptr);\n\n    const int topk_buf_offset  = ceil(batch_size * beam_width * beam_width * 2 / 4.) * 4;\n    int*      topk_tmp_id_buf  = reinterpret_cast<int*>(temp_storage);\n    T*        topk_tmp_val_buf = reinterpret_cast<T*>(topk_tmp_id_buf + topk_buf_offset);\n    float*    tmp_buffer       = reinterpret_cast<float*>(topk_tmp_val_buf + topk_buf_offset);\n\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n    int voc_parts = 4;\n    if (batch_size * beam_width < 256) {\n        // Volta has 80 SMs, so we aim for three waves\n        voc_parts = (240 + batch_size * beam_width - 1) / (batch_size * beam_width);\n        voc_parts = std::min(128, voc_parts);  // we implement up to 128\n    }\n    dim3 grid(batch_size * beam_width, voc_parts);\n    cudaFuncSetAttribute(beam_online_softmax_topk_stage1_kernel<T, items_per_thread, 2 * MAX_K, block_sz>,\n                         cudaFuncAttributePreferredSharedMemoryCarveout,\n                         cudaSharedmemCarveoutMaxL1);\n    beam_online_softmax_topk_stage1_kernel<T, items_per_thread, 2 * MAX_K, block_sz>\n        <<<grid, block_sz, 0, stream>>>(log_probs, bias, finished, tmp_buffer, vocab_size, beam_width, end_ids);\n    sync_check_cuda_error();\n#endif\n    if (beam_width > 1) {\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n        beam_online_softmax_topk_stage2_kernelLauncher<T, 2 * MAX_K>(\n            tmp_buffer, cum_log_probs, topk_tmp_id_buf, topk_tmp_val_buf, batch_size, beam_width, voc_parts, stream);\n        sync_check_cuda_error();\n#else\n        beam_online_softmax_topk_kernel<T, items_per_thread, MAX_K, block_sz>\n            <<<batch_size * beam_width, block_sz, 0, stream>>>(log_probs,\n                                                               bias,\n                                                               cum_log_probs,\n                                                               finished,\n                                                               topk_tmp_id_buf,\n                                                               topk_tmp_val_buf,\n                                                               vocab_size,\n                                                               beam_width,\n                                                               end_ids);\n#endif\n#if 0\n            // wrong result with diversity_rate != 0.f\n            batch_topK_kernel<T, MAX_K, 32><<<batch_size, 32, 0, stream>>>\n                                (topk_tmp_id_buf, topk_tmp_val_buf, ids, cum_log_probs);\n#else\n        // We need 2*MAX_K candidates because at most k candidates are finished, and we\n        // will not put them into next iteration\n        batch_topk_kernel<T, MAX_K * 2, 32><<<batch_size, 32, 0, stream>>>(topk_tmp_id_buf,\n                                                                           topk_tmp_val_buf,\n                                                                           ids,\n                                                                           cum_log_probs,\n                                                                           output_log_probs,\n                                                                           finished,\n                                                                           sequence_lengths,\n                                                                           *beam_hyps,\n                                                                           beam_width * beam_width * 2,\n                                                                           beam_width,\n                                                                           vocab_size,\n                                                                           length_penalty,\n                                                                           diversity_rate);\n        sync_check_cuda_error();\n#endif\n    }\n    else {\n        FT_CHECK(false);\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n        beam_online_softmax_topk_stage2_kernelLauncher<float, MAX_K>(\n            tmp_buffer, cum_log_probs, ids, cum_log_probs, batch_size, beam_width, voc_parts, stream);\n#else\n        beam_online_softmax_topk_kernel<T, items_per_thread, MAX_K, block_sz>\n            <<<batch_size * beam_width, block_sz, 0, stream>>>(\n                log_probs, bias, cum_log_probs, finished, ids, cum_log_probs, vocab_size, beam_width, end_ids);\n#endif\n    }\n}\n\n#define CASE_K(K, MAX_K)                                                                                               \\\n    case K ... MAX_K:                                                                                                  \\\n        topK_softMax_kernelLauncher<T, MAX_K>(log_probs,                                                               \\\n                                              bias,                                                                    \\\n                                              finished,                                                                \\\n                                              sequence_lengths,                                                        \\\n                                              cum_log_probs,                                                           \\\n                                              output_log_probs,                                                        \\\n                                              ids,                                                                     \\\n                                              temp_storage,                                                            \\\n                                              temp_storage_size,                                                       \\\n                                              beam_hyps,                                                               \\\n                                              batch_size,                                                              \\\n                                              beam_width,                                                              \\\n                                              vocab_size,                                                              \\\n                                              end_ids,                                                                 \\\n                                              diversity_rate,                                                          \\\n                                              length_penalty,                                                          \\\n                                              stream);                                                                 \\\n        break;\n\ntemplate<typename T>\nvoid invokeTopkSoftMax(const T*        log_probs,\n                       const T*        bias,\n                       const bool*     finished,\n                       const int*      sequence_lengths,\n                       float*          cum_log_probs,\n                       float*          output_log_probs,\n                       int*            ids,\n                       void*           temp_storage,\n                       const int       temp_storage_size,\n                       BeamHypotheses* beam_hyps,\n                       const int       batch_size,\n                       const int       beam_width,\n                       const int       vocab_size,\n                       const int*      end_ids,\n                       const float     diversity_rate,\n                       const float     length_penalty,\n                       cudaStream_t    stream)\n{\n    switch (beam_width) {\n        CASE_K(1, 4);\n        CASE_K(5, 8);\n        CASE_K(9, 16);\n        CASE_K(17, 32);\n        CASE_K(33, 64);\n        default:\n            throw std::runtime_error(fmtstr(\"Topk kernel of beam search does not support beam_width=%d\", beam_width));\n    }\n}\n\n#undef CASE_K\n\ntemplate void invokeTopkSoftMax<float>(const float*    log_probs,\n                                       const float*    bias,\n                                       const bool*     finished,\n                                       const int*      sequence_lengths,\n                                       float*          cum_log_probs,\n                                       float*          output_log_probs,\n                                       int*            ids,\n                                       void*           tmp_storage,\n                                       const int       temp_storage_size,\n                                       BeamHypotheses* beam_hyps,\n                                       const int       batch_size,\n                                       const int       beam_width,\n                                       const int       vocab_size,\n                                       const int*      end_ids,\n                                       const float     diversity_rate,\n                                       const float     length_penalty,\n                                       cudaStream_t    stream);\n\ntemplate void invokeTopkSoftMax<half>(const half*     log_probs,\n                                      const half*     bias,\n                                      const bool*     finished,\n                                      const int*      sequence_lengths,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      int*            ids,\n                                      void*           tmp_storage,\n                                      const int       temp_storage_size,\n                                      BeamHypotheses* beam_hyps,\n                                      const int       batch_size,\n                                      const int       beam_width,\n                                      const int       vocab_size,\n                                      const int*      end_ids,\n                                      const float     diversity_rate,\n                                      const float     length_penalty,\n                                      cudaStream_t    stream);\n\n}  // end of namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/online_softmax_beamsearch_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n#define DO_SPLIT_SMALL_TOP_K_SOFTMAX\nstatic const int SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE = 256;\n\n#define TOPK_FP16_STORAGE 0\n\ntemplate<typename T>\n__device__ __forceinline__ T apply_length_penalty(T log_prob, int length, float length_penalty)\n{\n    // score = log(prob) / (length)^length_penalty.\n    if (length_penalty == 0.0f || length == 1) {\n        return log_prob;\n    }\n    return log_prob / static_cast<T>(powf(length, length_penalty));\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__\n    void batch_topK_kernel(int* topk_tmp_id_buf, T* topk_tmp_val_buf, int* id_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -FLT_MAX;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i] = partial.p[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void batch_topK_kernel(const int* __restrict topk_tmp_id_buf,\n                                                                      const T* __restrict topk_tmp_val_buf,\n                                                                      int* __restrict id_buf,\n                                                                      T* __restrict val_buf)\n{\n    int            thread_id = threadIdx.x;\n    int            block_id  = blockIdx.x;\n    TopK<T, MAX_K> partial;\n    if (thread_id == 0) {\n        for (int i = 0; i < MAX_K; ++i) {\n            partial.p[i] = -1;\n            partial.u[i] = -FLT_MAX;\n        }\n\n        int index = block_id * MAX_K * MAX_K;\n        for (int i = 0; i < MAX_K * MAX_K; i++) {\n            partial.insert((T)topk_tmp_val_buf[index + i], topk_tmp_id_buf[index + i]);\n        }\n\n        index = block_id * MAX_K;\n        for (int i = 0; i < MAX_K; i++) {\n            id_buf[index + i]  = partial.p[i];\n            val_buf[index + i] = partial.u[i];\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void batch_topk_kernel(const int* __restrict x,\n                                                                      const T* __restrict y,\n                                                                      int* __restrict z,\n                                                                      float* __restrict v,\n                                                                      float*         output_log_probs,\n                                                                      const bool*    finished,\n                                                                      const int*     sequence_lengths,\n                                                                      BeamHypotheses beam_hyps,\n                                                                      const int      V,\n                                                                      const int      K,\n                                                                      const int      vocab_size,\n                                                                      const float    length_penalty,\n                                                                      const T        diversity_rate)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;\n\n    // reposition x, y to data for the current vector\n    x += vector_id * V;\n    y += vector_id * V;\n\n    typedef hipcub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ int                               selected_beams;\n    __shared__ float                             old_cum_log_probs[MAX_K];\n\n    if (thread_id == 0) {\n        selected_beams = 0;\n    }\n    if (thread_id < K) {\n        old_cum_log_probs[thread_id] = v[vector_id * K + thread_id];\n    }\n    __syncthreads();\n    if (beam_hyps.num_beams != nullptr) {\n        const int global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + vector_id;\n        if (beam_hyps.num_beams[global_batch_idx] == 0 && thread_id == 0) {\n            beam_hyps.min_normed_scores[global_batch_idx] = FLT_MAX;\n        }\n        else if (beam_hyps.num_beams[global_batch_idx] == K) {\n            return;\n        }\n    }\n\n    TopK<T, MAX_K> partial;\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -FLT_MAX;\n    }\n\n    for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n        int i    = elem_id % K;\n        T   elem = length_penalty == 0.0f ? y[elem_id] :\n                                            apply_length_penalty(y[elem_id],\n                                                               finished[vector_id] ? sequence_lengths[vector_id] :\n                                                                                       sequence_lengths[vector_id] + 1,\n                                                               length_penalty);\n        elem += diversity_rate * (T)i;\n        int elem_idx = elem_id;  // x[elem_id];\n        partial.insert(elem, elem_idx);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * K;\n        v += vector_id * K;\n\n        for (int i = 0; i < MAX_K; ++i) {\n            if (beam_hyps.num_beams != nullptr && x[total.p[i]] % vocab_size == beam_hyps.end_ids[vector_id]) {\n                // if beam_token does not belong to top num_beams tokens, it should not be added. Refer from\n                // https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/generation_beam_search.py#L257\n                if (i >= K) {\n                    // do nothing\n                }\n                else {\n                    const int   global_batch_idx = beam_hyps.ite * beam_hyps.local_batch_size + vector_id;\n                    const float normed_score     = (float)total.u[i];\n                    const int   num_beam         = beam_hyps.num_beams[global_batch_idx];\n                    int         beam_idx         = num_beam;\n                    // If there are beam_width finished sentences, check that the score of selected candidatet\n                    // is higher than min_normed_score or not. If current score is better, replace worst one\n                    // and update the min_normed_score.\n                    if (num_beam == K) {\n                        if (normed_score < beam_hyps.min_normed_scores[global_batch_idx]) {\n                            // end the tracing and exist this for loop\n                            selected_beams = K;\n                            break;\n                        }\n                        else {\n                            // find the beam index which's score = min_normed_score, erase it.\n                            for (int j = 0; j < K; j++) {\n                                if (beam_hyps.normed_scores[global_batch_idx * (K * 2) + j]\n                                    == beam_hyps.min_normed_scores[global_batch_idx]) {\n                                    beam_idx = j;\n                                    beam_hyps.num_beams[global_batch_idx]--;\n\n                                    beam_hyps.min_normed_scores[global_batch_idx]           = FLT_MAX;\n                                    beam_hyps.normed_scores[global_batch_idx * (K * 2) + j] = normed_score;\n                                    for (int l = 0; l < K; l++) {\n                                        beam_hyps.min_normed_scores[global_batch_idx] =\n                                            min(beam_hyps.min_normed_scores[global_batch_idx],\n                                                beam_hyps.normed_scores[global_batch_idx * (K * 2) + l]);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    const int tgt_id_offset =\n                        ((vector_id + beam_hyps.ite * beam_hyps.local_batch_size) * (K * 2) + beam_idx)\n                        * (beam_hyps.max_seq_len);\n                    beam_hyps.output_ids_tgt[tgt_id_offset + beam_hyps.step] = beam_hyps.end_ids[vector_id];\n                    if (beam_hyps.log_probs != nullptr) {\n                        beam_hyps.log_probs[tgt_id_offset + beam_hyps.step] =\n                            (float)y[total.p[i]] - old_cum_log_probs[(x[total.p[i]] / vocab_size) % K];\n                    }\n\n                    int prev_id = (x[total.p[i]] / vocab_size) % K;\n                    for (int j = beam_hyps.step - 1; j >= 0; j--) {\n                        const int src_idx = j * beam_hyps.batch_size * K\n                                            + beam_hyps.ite * beam_hyps.local_batch_size * K + vector_id * K + prev_id;\n\n                        beam_hyps.output_ids_tgt[tgt_id_offset + j] = beam_hyps.output_ids_src[src_idx];\n                        if (beam_hyps.log_probs != nullptr && beam_hyps.log_probs_src != nullptr) {\n                            beam_hyps.log_probs[tgt_id_offset + j] = beam_hyps.log_probs_src[src_idx];\n                        }\n                        prev_id = beam_hyps.parent_ids_src[src_idx];\n                    }\n                    const int tgt_beam_idx                       = global_batch_idx * (K * 2) + beam_idx;\n                    beam_hyps.sequence_lengths_tgt[tgt_beam_idx] = beam_hyps.step;\n                    beam_hyps.normed_scores[tgt_beam_idx]        = normed_score;\n                    beam_hyps.min_normed_scores[global_batch_idx] =\n                        min(beam_hyps.min_normed_scores[global_batch_idx], beam_hyps.normed_scores[tgt_beam_idx]);\n\n                    beam_hyps.num_beams[global_batch_idx]++;\n                    beam_hyps.cum_log_probs[tgt_beam_idx] = (float)y[total.p[i]];\n                }\n            }\n            else if ((beam_hyps.num_beams != nullptr && i < 2 * K) || (beam_hyps.num_beams == nullptr && i < K)) {\n                z[selected_beams] = x[total.p[i]];\n                if (output_log_probs != nullptr) {\n                    output_log_probs[vector_id * K + selected_beams] =\n                        (float)y[total.p[i]] - old_cum_log_probs[(z[selected_beams] / vocab_size) % K];\n                }\n                v[selected_beams] = (float)y[total.p[i]];\n                selected_beams++;\n            }\n            __syncthreads();\n            if (selected_beams >= K) {\n                break;\n            }\n        }\n    }\n    if (threadIdx.x == 0 && beam_hyps.num_beams != nullptr) {\n        if (beam_hyps.num_beams[blockIdx.x] < K) {\n            beam_hyps.is_done[blockIdx.x] = false;\n        }\n        else if (beam_hyps.early_stopping) {\n            beam_hyps.is_done[blockIdx.x] = true;\n        }\n    }\n}\n\nstruct __align__(8) MD\n{\n    float m;\n    float d;\n};\n\n__device__ __forceinline__ MD reduce_md_op(MD a, MD b)\n{\n    bool a_bigger  = (a.m > b.m);\n    MD   bigger_m  = a_bigger ? a : b;\n    MD   smaller_m = a_bigger ? b : a;\n    MD   res;\n    res.d = bigger_m.d + smaller_m.d * __expf(smaller_m.m - bigger_m.m);\n    res.m = bigger_m.m;\n    return res;\n}\n\ntemplate<typename T, int MAX_K>\nstruct TopKMD {\n    MD             md;\n    TopK<T, MAX_K> topk;\n};\n\ntemplate<typename T, int MAX_K>\n__device__ __forceinline__ TopKMD<T, MAX_K> reduce_topk_md_op(const TopKMD<T, MAX_K>& a, const TopKMD<T, MAX_K>& b)\n{\n    TopKMD<T, MAX_K> res;\n    res.md   = reduce_md_op(a.md, b.md);\n    res.topk = reduce_topk_op(a.topk, b.topk);\n    return res;\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_online_softmax_topk_kernel(const T* __restrict x,\n                                                                                    const T* __restrict b,\n                                                                                    const float* __restrict c,\n                                                                                    const bool* __restrict finished,\n                                                                                    int* __restrict z,\n                                                                                    T* __restrict v,\n                                                                                    int V,\n                                                                                    int K,\n                                                                                    const int* __restrict end_ids)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    // reposition y to data for the current vector\n    x += vector_id * V;\n\n    typedef hipcub::BlockReduce<TopKMD<float, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage                     temp_storage;\n\n    TopKMD<float, MAX_K> partial;\n    bool                 finish = finished[vector_id];\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    if (finish) {\n        for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n            float elem = (elem_id == end_ids[vector_id / K]) ? MAX_T_VAL : -MAX_T_VAL;\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n            // if (elem_id > THREADBLOCK_SIZE * MAX_K && (elem_id == E)) break;\n        }\n    }\n    else {\n        for (int elem_id = thread_id; elem_id < V; elem_id += THREADBLOCK_SIZE) {\n            float elem = x[elem_id] + b[elem_id];\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n\n    TopKMD<float, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<float, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * K;\n        v += vector_id * K;\n        c += vector_id;\n\n        // float d_total_inverse = __fdividef(1.0F, total.md.d);\n        float d_total_log = logf(total.md.d);\n        for (int i = 0; i < MAX_K; ++i) {\n            // float val = __expf(total.topk.u[i] - total.md.m) * d_total_inverse;\n            float val = total.topk.u[i] - total.md.m - d_total_log;\n            if (i < K) {\n                z[i] = total.topk.p[i] + vector_id * V;  // faster transformer needs absolute id\n                v[i] = val + c[0];\n            }\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE, 1) __global__\n    void beam_online_softmax_topk_stage1_kernel(const T* __restrict x,\n                                                const T* __restrict b,\n                                                const bool* __restrict finished,\n                                                float* __restrict t,\n                                                int V,\n                                                int K,\n                                                const int* __restrict end_ids)\n{\n    int thread_id = threadIdx.x;\n    int vector_id = blockIdx.x;  // batch beam index.\n\n    const int PACKED_TOP_KMD_SIZE = 2 * MAX_K + 2;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    // one will have multiple sections per V\n    const int v_local       = (V + gridDim.y - 1) / gridDim.y;\n    const int section_start = v_local * blockIdx.y;\n    int       section_end   = section_start + v_local;\n    section_end             = (section_end > V) ? V : section_end;\n\n    // reposition x to data for the current vector\n    x += vector_id * V;\n#if TOPK_FP16_STORAGE == 1\n    typedef hipcub::BlockReduce<TopKMD<__half, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n#else\n    typedef hipcub::BlockReduce<TopKMD<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n#endif\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n    __shared__ float                             buf_s[PACKED_TOP_KMD_SIZE];  // save intermediate result\n\n#if TOPK_FP16_STORAGE == 1\n    TopKMD<__half, MAX_K> partial;\n#else\n    TopKMD<T, MAX_K>                                             partial;\n#endif\n    bool finish = finished[vector_id];\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    if (finish) {\n#pragma unroll 1\n        for (int elem_id = section_start + thread_id; elem_id < section_end; elem_id += THREADBLOCK_SIZE) {\n            float elem = (elem_id == end_ids[vector_id / K]) ? MAX_T_VAL : -MAX_T_VAL;\n            MD    new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n    else {\n#pragma unroll 1\n        for (int elem_id = section_start + thread_id; elem_id < section_end; elem_id += THREADBLOCK_SIZE) {\n            T  bias = b == nullptr ? (T)0.0f : b[elem_id];  // gpt-2 does not use bias\n            T  elem = x[elem_id] + bias;\n            MD new_elem{elem, 1.0F};\n            partial.md = reduce_md_op(partial.md, new_elem);\n            partial.topk.insert(elem, elem_id);\n        }\n    }\n\n#if TOPK_FP16_STORAGE == 1\n    TopKMD<__half, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<__half, MAX_K>);\n#else\n    TopKMD<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<T, MAX_K>);\n#endif\n\n    if (thread_id == 0) {\n        for (int i = 0; i < 2 * K; i++) {\n            reinterpret_cast<int*>(buf_s)[i] = total.topk.p[i] + vector_id * V;  // faster transformer needs absolute id\n            buf_s[MAX_K + i]                 = total.topk.u[i];\n        }\n        buf_s[2 * MAX_K]     = total.md.d;\n        buf_s[2 * MAX_K + 1] = total.md.m;\n    }\n    __syncthreads();\n    for (int elem_id = thread_id; elem_id < PACKED_TOP_KMD_SIZE; elem_id += THREADBLOCK_SIZE) {\n        t[blockIdx.x * PACKED_TOP_KMD_SIZE * gridDim.y + blockIdx.y * PACKED_TOP_KMD_SIZE + elem_id] = buf_s[elem_id];\n    }\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void beam_online_softmax_topk_stage2_kernel(\n    const float* __restrict x, const float* __restrict c, int* __restrict z, T* __restrict v, int K, int parts_per_beam)\n{\n    const int vector_id           = blockIdx.x;\n    const int thread_id           = threadIdx.x;\n    const int PACKED_TOP_KMD_SIZE = 2 * MAX_K + 2;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    extern __shared__ char buf_s_[];  // intermediate result\n    float*                 buf_s = reinterpret_cast<float*>(buf_s_);\n    //__shared__ float buf_s[PACKED_TOP_KMD_SIZE * THREADBLOCK_SIZE]; // intermediate result\n\n    typedef hipcub::BlockReduce<TopKMD<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage                 temp_storage;\n\n    x += vector_id * PACKED_TOP_KMD_SIZE * parts_per_beam;\n\n    TopKMD<T, MAX_K> partial;\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.topk.p[i] = -1;\n        partial.topk.u[i] = -MAX_T_VAL;\n    }\n    partial.md.m = -MAX_T_VAL;\n    partial.md.d = 0.0F;\n\n    // load and unpack into registers through smem\n    for (int idx = thread_id; idx < PACKED_TOP_KMD_SIZE * parts_per_beam; idx += THREADBLOCK_SIZE) {\n        buf_s[idx] = x[idx];\n    }\n    __syncthreads();\n\n    if (threadIdx.x < parts_per_beam) {\n        float* b_s = buf_s + thread_id * PACKED_TOP_KMD_SIZE;\n        for (int i = 0; i < 2 * K; i++) {\n            partial.topk.p[i] = reinterpret_cast<int*>(b_s)[i];\n            partial.topk.u[i] = b_s[MAX_K + i];\n        }\n        partial.md.d = b_s[2 * MAX_K];\n        partial.md.m = b_s[2 * MAX_K + 1];\n    }\n    __syncthreads();\n\n    TopKMD<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_md_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        z += vector_id * 2 * K;\n        v += vector_id * 2 * K;\n        c += vector_id;\n\n        float d_total_log = logf(total.md.d);\n        for (int i = 0; i < MAX_K; ++i) {\n            float val = (float)total.topk.u[i] - total.md.m - d_total_log;\n            if (i < 2 * K) {\n                z[i] = total.topk.p[i];\n                v[i] = (float)val + (float)c[0];\n            }\n        }\n    }\n}\n\ntemplate<typename T, int MAX_K>\nvoid beam_online_softmax_topk_stage2_kernelLauncher(const float* temp_storage,\n                                                    const float* cum_log_probs,\n                                                    int*         ids,\n                                                    T*           vals,\n                                                    int          batch_size,\n                                                    int          beam_width,\n                                                    int          parts_per_beam,\n                                                    hipStream_t stream)\n{\n    // might rewrite beam_online_softmax_topk_stage2_kernel no to depend on constant block size\n    // in oreder to reduce compilation time\n    int smem_stage2_size = parts_per_beam * (2 * MAX_K + 2) * sizeof(float);\n\n    if (parts_per_beam <= 32) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 32><<<batch_size * beam_width, 32, smem_stage2_size, stream>>>(\n            temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    if (parts_per_beam <= 64) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 64><<<batch_size * beam_width, 64, smem_stage2_size, stream>>>(\n            temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    if (parts_per_beam <= 128) {\n        beam_online_softmax_topk_stage2_kernel<T, MAX_K, 128>\n            <<<batch_size * beam_width, 128, smem_stage2_size, stream>>>(\n                temp_storage, cum_log_probs, ids, vals, beam_width, parts_per_beam);\n        return;\n    }\n    assert(0);\n}\n\ntemplate<typename T, int MAX_K>\nvoid topK_softMax_kernelLauncher(const T*        log_probs,\n                                 const T*        bias,\n                                 const bool*     finished,\n                                 const int*      sequence_lengths,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 int*            ids,\n                                 void*           temp_storage,\n                                 const int       temp_storage_size,\n                                 BeamHypotheses* beam_hyps,\n                                 const int       batch_size,\n                                 const int       beam_width,\n                                 const int       vocab_size,\n                                 const int*      end_ids,\n                                 T               diversity_rate,\n                                 const float     length_penalty,\n                                 hipStream_t    stream)\n{\n    const int items_per_thread = 1;\n    const int block_sz         = (MAX_K < 16) ? (MAX_K < 8) ? SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE : 128 : 64;\n    // const int block_sz = SMALL_TOP_K_SOFTMAX_THREADBLOCK_SIZE;\n\n    assert(temp_storage_size % 2 == 0);\n    assert(temp_storage_size >= 2 * batch_size * beam_width * beam_width * 2);\n    // Beam search needs the sequence lengths of beams to apply length penalty.\n    assert(length_penalty == 0.0f || sequence_lengths != nullptr);\n\n    const int topk_buf_offset  = ceil(batch_size * beam_width * beam_width * 2 / 4.) * 4;\n    int*      topk_tmp_id_buf  = reinterpret_cast<int*>(temp_storage);\n    T*        topk_tmp_val_buf = reinterpret_cast<T*>(topk_tmp_id_buf + topk_buf_offset);\n    float*    tmp_buffer       = reinterpret_cast<float*>(topk_tmp_val_buf + topk_buf_offset);\n\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n    int voc_parts = 4;\n    if (batch_size * beam_width < 256) {\n        // Volta has 80 SMs, so we aim for three waves\n        voc_parts = (240 + batch_size * beam_width - 1) / (batch_size * beam_width);\n        voc_parts = std::min(128, voc_parts);  // we implement up to 128\n    }\n    dim3 grid(batch_size * beam_width, voc_parts);\n    hipFuncSetAttribute(reinterpret_cast<const void*>(beam_online_softmax_topk_stage1_kernel<T, items_per_thread, 2 * MAX_K, block_sz>),\n                         hipFuncAttributePreferredSharedMemoryCarveout,\n                         cudaSharedmemCarveoutMaxL1);\n    beam_online_softmax_topk_stage1_kernel<T, items_per_thread, 2 * MAX_K, block_sz>\n        <<<grid, block_sz, 0, stream>>>(log_probs, bias, finished, tmp_buffer, vocab_size, beam_width, end_ids);\n    sync_check_cuda_error();\n#endif\n    if (beam_width > 1) {\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n        beam_online_softmax_topk_stage2_kernelLauncher<T, 2 * MAX_K>(\n            tmp_buffer, cum_log_probs, topk_tmp_id_buf, topk_tmp_val_buf, batch_size, beam_width, voc_parts, stream);\n        sync_check_cuda_error();\n#else\n        beam_online_softmax_topk_kernel<T, items_per_thread, MAX_K, block_sz>\n            <<<batch_size * beam_width, block_sz, 0, stream>>>(log_probs,\n                                                               bias,\n                                                               cum_log_probs,\n                                                               finished,\n                                                               topk_tmp_id_buf,\n                                                               topk_tmp_val_buf,\n                                                               vocab_size,\n                                                               beam_width,\n                                                               end_ids);\n#endif\n#if 0\n            // wrong result with diversity_rate != 0.f\n            batch_topK_kernel<T, MAX_K, 32><<<batch_size, 32, 0, stream>>>\n                                (topk_tmp_id_buf, topk_tmp_val_buf, ids, cum_log_probs);\n#else\n        // We need 2*MAX_K candidates because at most k candidates are finished, and we\n        // will not put them into next iteration\n        batch_topk_kernel<T, MAX_K * 2, 32><<<batch_size, 32, 0, stream>>>(topk_tmp_id_buf,\n                                                                           topk_tmp_val_buf,\n                                                                           ids,\n                                                                           cum_log_probs,\n                                                                           output_log_probs,\n                                                                           finished,\n                                                                           sequence_lengths,\n                                                                           *beam_hyps,\n                                                                           beam_width * beam_width * 2,\n                                                                           beam_width,\n                                                                           vocab_size,\n                                                                           length_penalty,\n                                                                           diversity_rate);\n        sync_check_cuda_error();\n#endif\n    }\n    else {\n        FT_CHECK(false);\n#ifdef DO_SPLIT_SMALL_TOP_K_SOFTMAX\n        beam_online_softmax_topk_stage2_kernelLauncher<float, MAX_K>(\n            tmp_buffer, cum_log_probs, ids, cum_log_probs, batch_size, beam_width, voc_parts, stream);\n#else\n        beam_online_softmax_topk_kernel<T, items_per_thread, MAX_K, block_sz>\n            <<<batch_size * beam_width, block_sz, 0, stream>>>(\n                log_probs, bias, cum_log_probs, finished, ids, cum_log_probs, vocab_size, beam_width, end_ids);\n#endif\n    }\n}\n\n#define CASE_K(K, MAX_K)                                                                                               \\\n    case K ... MAX_K:                                                                                                  \\\n        topK_softMax_kernelLauncher<T, MAX_K>(log_probs,                                                               \\\n                                              bias,                                                                    \\\n                                              finished,                                                                \\\n                                              sequence_lengths,                                                        \\\n                                              cum_log_probs,                                                           \\\n                                              output_log_probs,                                                        \\\n                                              ids,                                                                     \\\n                                              temp_storage,                                                            \\\n                                              temp_storage_size,                                                       \\\n                                              beam_hyps,                                                               \\\n                                              batch_size,                                                              \\\n                                              beam_width,                                                              \\\n                                              vocab_size,                                                              \\\n                                              end_ids,                                                                 \\\n                                              diversity_rate,                                                          \\\n                                              length_penalty,                                                          \\\n                                              stream);                                                                 \\\n        break;\n\ntemplate<typename T>\nvoid invokeTopkSoftMax(const T*        log_probs,\n                       const T*        bias,\n                       const bool*     finished,\n                       const int*      sequence_lengths,\n                       float*          cum_log_probs,\n                       float*          output_log_probs,\n                       int*            ids,\n                       void*           temp_storage,\n                       const int       temp_storage_size,\n                       BeamHypotheses* beam_hyps,\n                       const int       batch_size,\n                       const int       beam_width,\n                       const int       vocab_size,\n                       const int*      end_ids,\n                       const float     diversity_rate,\n                       const float     length_penalty,\n                       hipStream_t    stream)\n{\n    switch (beam_width) {\n        CASE_K(1, 4);\n        CASE_K(5, 8);\n        CASE_K(9, 16);\n        CASE_K(17, 32);\n        CASE_K(33, 64);\n        default:\n            throw std::runtime_error(fmtstr(\"Topk kernel of beam search does not support beam_width=%d\", beam_width));\n    }\n}\n\n#undef CASE_K\n\ntemplate void invokeTopkSoftMax<float>(const float*    log_probs,\n                                       const float*    bias,\n                                       const bool*     finished,\n                                       const int*      sequence_lengths,\n                                       float*          cum_log_probs,\n                                       float*          output_log_probs,\n                                       int*            ids,\n                                       void*           tmp_storage,\n                                       const int       temp_storage_size,\n                                       BeamHypotheses* beam_hyps,\n                                       const int       batch_size,\n                                       const int       beam_width,\n                                       const int       vocab_size,\n                                       const int*      end_ids,\n                                       const float     diversity_rate,\n                                       const float     length_penalty,\n                                       hipStream_t    stream);\n\ntemplate void invokeTopkSoftMax<half>(const half*     log_probs,\n                                      const half*     bias,\n                                      const bool*     finished,\n                                      const int*      sequence_lengths,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      int*            ids,\n                                      void*           tmp_storage,\n                                      const int       temp_storage_size,\n                                      BeamHypotheses* beam_hyps,\n                                      const int       batch_size,\n                                      const int       beam_width,\n                                      const int       vocab_size,\n                                      const int*      end_ids,\n                                      const float     diversity_rate,\n                                      const float     length_penalty,\n                                      hipStream_t    stream);\n\n}  // end of namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/online_softmax_beamsearch_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\ntemplate<typename T>\n__global__ void addQKVBiasIA3Transpose(T* q_out,\n                                       T* k_out,\n                                       T* v_out,\n                                       const T* __restrict q_in,\n                                       const T* __restrict bias_q,\n                                       const T* __restrict k_in,\n                                       const T* __restrict bias_k,\n                                       const T* __restrict v_in,\n                                       const T* __restrict bias_v,\n                                       const int* ia3_tasks,\n                                       const T*   ia3_key_weights,\n                                       const T*   ia3_value_weights,\n                                       const int  batch_size,\n                                       const int  seq_len,\n                                       const int  head_num,\n                                       const int  size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        T q              = ldg(&q_in[src_id]);\n        q_out[target_id] = add(q, ldg(&bias_q[col_id]));\n\n        T k = add(ldg(&k_in[src_id]), ldg(&bias_k[col_id]));\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + col_id];\n        }\n        k_out[target_id] = k;\n\n        T v = add(ldg(&v_in[src_id]), ldg(&bias_v[col_id]));\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + col_id];\n        }\n        v_out[target_id] = v;\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVIA3Transpose(T* q_out,\n                                T* k_out,\n                                T* v_out,\n                                const T* __restrict q_in,\n                                const T* __restrict k_in,\n                                const T* __restrict v_in,\n                                const int* ia3_tasks,\n                                const T* __restrict ia3_key_weights,\n                                const T* __restrict ia3_value_weights,\n                                const int batch_size,\n                                const int seq_len,\n                                const int head_num,\n                                const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        q_out[target_id] = ldg(&q_in[src_id]);\n\n        T k = ldg(&k_in[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + col_id];\n        }\n        k_out[target_id] = k;\n\n        T v = ldg(&v_in[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + col_id];\n        }\n        v_out[target_id] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasIA3Transpose(T*           q_buf,\n                                  T*           k_buf,\n                                  T*           v_buf,\n                                  T*           Q,\n                                  const T*     bias_Q,\n                                  T*           K,\n                                  const T*     bias_K,\n                                  T*           V,\n                                  const T*     bias_V,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  const int*   ia3_tasks,\n                                  const T*     ia3_key_weights,\n                                  const T*     ia3_value_weights,\n                                  cudaStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, seq_len);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVBiasIA3Transpose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                                  k_buf,\n                                                                  v_buf,\n                                                                  Q,\n                                                                  bias_Q,\n                                                                  K,\n                                                                  bias_K,\n                                                                  V,\n                                                                  bias_V,\n                                                                  ia3_tasks,\n                                                                  ia3_key_weights,\n                                                                  ia3_value_weights,\n                                                                  batch_size,\n                                                                  seq_len,\n                                                                  head_num,\n                                                                  size_per_head);\n        }\n        else {\n            QKVIA3Transpose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                           k_buf,\n                                                           v_buf,\n                                                           Q,\n                                                           K,\n                                                           V,\n                                                           ia3_tasks,\n                                                           ia3_key_weights,\n                                                           ia3_value_weights,\n                                                           batch_size,\n                                                           seq_len,\n                                                           head_num,\n                                                           size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVBiasIA3Transpose<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                                   (T2*)k_buf,\n                                                                   (T2*)v_buf,\n                                                                   (const T2*)Q,\n                                                                   (const T2*)bias_Q,\n                                                                   (const T2*)K,\n                                                                   (const T2*)bias_K,\n                                                                   (const T2*)V,\n                                                                   (const T2*)bias_V,\n                                                                   ia3_tasks,\n                                                                   (const T2*)ia3_key_weights,\n                                                                   (const T2*)ia3_value_weights,\n                                                                   batch_size,\n                                                                   seq_len,\n                                                                   head_num,\n                                                                   size_per_head / 2);\n        }\n        else {\n            QKVIA3Transpose<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                            (T2*)k_buf,\n                                                            (T2*)v_buf,\n                                                            (const T2*)Q,\n                                                            (const T2*)K,\n                                                            (const T2*)V,\n                                                            ia3_tasks,\n                                                            (const T2*)ia3_key_weights,\n                                                            (const T2*)ia3_value_weights,\n                                                            batch_size,\n                                                            seq_len,\n                                                            head_num,\n                                                            size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\n#define INSTANTIATEADDQKVBIASIA3TRANSPOSE(T)                                                                           \\\n    template void invokeAddQKVBiasIA3Transpose(T*           q_buf,                                                     \\\n                                               T*           k_buf,                                                     \\\n                                               T*           v_buf,                                                     \\\n                                               T*           Q,                                                         \\\n                                               const T*     bias_Q,                                                    \\\n                                               T*           K,                                                         \\\n                                               const T*     bias_K,                                                    \\\n                                               T*           V,                                                         \\\n                                               const T*     bias_V,                                                    \\\n                                               const int    batch_size,                                                \\\n                                               const int    seq_len,                                                   \\\n                                               const int    head_num,                                                  \\\n                                               const int    size_per_head,                                             \\\n                                               const int*   ia3_tasks,                                                 \\\n                                               const T*     ia3_key_weights,                                           \\\n                                               const T*     ia3_value_weights,                                         \\\n                                               cudaStream_t stream)\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(float);\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDQKVBIASTRANSPOSE\n\ntemplate<typename T, typename T_IN, int ITEMS_PER_THREAD>\n__global__ void softmax_kernel(T*          attn_score,\n                               const T_IN* qk,\n                               const T*    attn_mask,\n                               const T*    linear_bias_slopes,\n                               const int   batch_size,\n                               const int   head_num,\n                               const int   q_length,\n                               const int   k_length,\n                               const float qk_scale)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    const int64_t bi = blockIdx.y;  // Batch index.\n    const int64_t hi = blockIdx.z;  // Head index.\n\n    __shared__ float s_mean, s_max;\n\n    const float linear_bias_slope = linear_bias_slopes != nullptr ? (float)linear_bias_slopes[hi] : 0.0f;\n\n    // Loop along with Q dimension.\n    for (int64_t qi = blockIdx.x; qi < q_length; qi += gridDim.x) {\n\n        float data[ITEMS_PER_THREAD];\n        int64_t   qk_offset;\n        float local_max = -1e20f;\n\n        // Loop along with K dimension.\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            int64_t ki    = blockDim.x * i + threadIdx.x;  // Index of K dimension.\n            qk_offset = ((bi * head_num + hi) * q_length + qi) * k_length + ki;\n\n            float qk_val  = static_cast<float>(qk[qk_offset]);\n            float qk_bias = 0.0f;\n\n            if (linear_bias_slopes != nullptr) {\n                // We don't handle the upper diagonal (ki > qi) separately, whose values\n                // are negligible due to the negative infinity mask. And it matches with\n                // the HF's implementation.\n                qk_bias += static_cast<float>(linear_bias_slope * (ki - qi));\n            }\n\n            int64_t   mask_offset = (bi * q_length + qi) * k_length + ki;\n            float mask_val    = static_cast<float>(ldg(&attn_mask[mask_offset]));\n            qk_bias += (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk_scale * qk_val + qk_bias;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            qk_offset             = ((bi * head_num + hi) * q_length + qi) * k_length + blockDim.x * i + threadIdx.x;\n            attn_score[qk_offset] = (T)(data[i] * s_mean);\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD>\n__global__ void softmax_kernel_h2(T*        attn_score,\n                                  const T*  qk_buf,\n                                  const T*  attn_mask,\n                                  const T*  linear_bias_slopes,\n                                  const int batch_size,\n                                  const int head_num,\n                                  const int q_length,\n                                  const int k_length,\n                                  const T   qk_scale)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    using T2 = typename TypeConverter<T>::Type;\n\n    T2*       attn_score_h2 = reinterpret_cast<T2*>(attn_score);\n    const T2* qk_buf_h2     = reinterpret_cast<const T2*>(qk_buf);\n    const T2* attn_mask_h2  = reinterpret_cast<const T2*>(attn_mask);\n\n    const int bi = blockIdx.y;  // Batch index\n    const int hi = blockIdx.z;  // Head index.\n\n    __shared__ float s_mean, s_max;\n\n    // Constant values that will be used repeately in the q/k loop.\n    const T2 ONE       = cuda_cast<T2>(1.0f);\n    const T2 ZERO      = cuda_cast<T2>(0.0f);\n    const T2 NEG_INFTY = cuda_cast<T2>(-10000.0f);\n\n    // The normalization factor of QK.\n    const T2 qk_scale_h2 = cuda_cast<T2>(qk_scale);\n    // The slope of a linear position bias of the current attention head.\n    const T2 linear_bias_slope = linear_bias_slopes != nullptr ? cuda_cast<T2>(linear_bias_slopes[hi]) : ZERO;\n\n    // Loop over q dimension.\n    for (int qi = blockIdx.x; qi < q_length; qi += gridDim.x) {\n        T2    data[ITEMS_PER_THREAD];\n        int   qk_offset;\n        float local_max = -1e20f;\n\n        // Loop over k dimension.\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            // The half of the index of k dimension. We will use the elements at {2 * ki, 2 * ki + 1}.\n            int ki          = blockDim.x * i + threadIdx.x;\n            qk_offset       = ((bi * head_num + hi) * q_length + qi) * (k_length / 2) + ki;\n            int mask_offset = (bi * q_length + qi) * (k_length / 2) + ki;\n\n            // The value of QK^T matrix at (qi, ki).\n            T2 qk = qk_buf_h2[qk_offset];\n            // The bias value to the position (qi, ki) including both mask and positional bias.\n            T2 qk_bias = ZERO;\n\n            if (linear_bias_slopes != nullptr) {\n                // The position bias depends on the distance between qi/ki and is zero if qi >= 2*ki\n                // or qi >= 2*ki+1. For T2 vectorization, we should handle every two elements along\n                // with k-dim simultaneously. To do this, we check qi / 2 > ki at ones instead of\n                // qi >= 2*ki or 2*ki+1. It works because an diagonal element for an odd qi will be\n                // zero due to slope * (qi - 2*ki+1) = 0. Thus, we don't handle the upper diagonal\n                // separately, whose values are negligible due to the negative infinity mask.\n                T2 dist(2.0f * ki - qi, 2.0f * ki + 1 - qi);\n                qk_bias = hadd2<T2>(qk_bias, hmul2<T2>(linear_bias_slope, dist));\n            }\n\n            T2 mask_val = ldg(&attn_mask_h2[mask_offset]);\n            qk_bias     = hadd2<T2>(qk_bias, hmul2<T2>(hsub2<T2>(ONE, mask_val), NEG_INFTY));\n\n            data[i]   = hadd2<T2>(hmul2<T2>(qk, qk_scale_h2), qk_bias);\n            local_max = fmax(local_max, fmax((float)data[i].x, (float)data[i].y));\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0.0f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            data[i] = hexp2<T2>(hsub2<T2>(data[i], cuda_cast<T2>(s_max)));\n            local_sum += (float)(data[i].x + data[i].y);\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((bi * head_num + hi) * q_length + qi) * (k_length / 2) + blockDim.x * i + threadIdx.x;\n            attn_score_h2[qk_offset] = hmul2<T2>(data[i], cuda_cast<T2>(s_mean));\n        }\n    }\n}\n\ntemplate<typename T, int K_ITEMS_PER_THREAD, int Q_ITEMS_PER_THREAD>\n__global__ void softmax_kernel_h2_v2(T*        attn_score,\n                                     const T*  qk_buf,\n                                     const T*  attn_mask,\n                                     const T*  linear_bias_slopes,\n                                     const int batch_size,\n                                     const int head_num,\n                                     const int q_length,\n                                     const int k_length,\n                                     const T   scalar)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    using T2 = typename TypeConverter<T>::Type;\n\n    // QK^T matrix of shape (batch_size, head_num, q_length, k_length / 2)\n    T2*       attn_score_h2 = reinterpret_cast<T2*>(attn_score);\n    const T2* qk_buf_h2     = reinterpret_cast<const T2*>(qk_buf);\n    const T2* attn_mask_h2  = reinterpret_cast<const T2*>(attn_mask);\n\n    const int bi = blockIdx.y;  // Batch index\n    const int hi = blockIdx.z;  // Head index.\n\n    // Constant values that will be used repeately in the q/k loop.\n    const T2 ONE       = cuda_cast<T2>(1.0f);\n    const T2 ZERO      = cuda_cast<T2>(0.0f);\n    const T2 NEG_INFTY = cuda_cast<T2>(-10000.0f);\n\n    // The normalization factor of QK.\n    const T2 qk_scale = cuda_cast<T2>(scalar);\n    // The slope of a linear position bias of the current attention head.\n    const T2 linear_bias_slope = linear_bias_slopes != nullptr ? cuda_cast<T2>(linear_bias_slopes[hi]) : ZERO;\n\n    __shared__ float s_sum[Q_ITEMS_PER_THREAD], s_max[Q_ITEMS_PER_THREAD];\n\n    // Loop over q dimension.\n    for (int qi = blockIdx.x; qi < q_length; qi += gridDim.x * Q_ITEMS_PER_THREAD) {\n        T2 data[Q_ITEMS_PER_THREAD][K_ITEMS_PER_THREAD];\n\n        int qk_offset[Q_ITEMS_PER_THREAD];\n\n        float local_max[Q_ITEMS_PER_THREAD];\n#pragma unroll\n        for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        // Loop over k dimension.\n        const int Q_ITEMS = min((q_length - qi + gridDim.x - 1) / gridDim.x, Q_ITEMS_PER_THREAD);\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n            // The half of the index of k dimension. We will use the elements at {2 * ki, 2 * ki + 1}.\n            int ki = blockDim.x * i + threadIdx.x;\n\n            int mask_offset[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk_offset[j]   = ((bi * head_num + hi) * q_length + qi + j * gridDim.x) * (k_length / 2) + ki;\n                mask_offset[j] = (bi * q_length + qi + j * gridDim.x) * (k_length / 2) + ki;\n            }\n\n            T2 mask_val[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                mask_val[j] = ldg(&attn_mask_h2[mask_offset[j]]);\n            }\n\n            T2 qk[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk[j] = qk_buf_h2[qk_offset[j]];\n            }\n\n            T2 pos_bias[Q_ITEMS_PER_THREAD];\n            if (linear_bias_slopes != nullptr) {\n#pragma unroll\n                for (int j = 0; j < Q_ITEMS; j++) {\n                    // The position bias depends on the distance between qi/ki and is zero if qi >= 2*ki\n                    // or qi >= 2*ki+1. For T2 vectorization, we should handle every two elements along\n                    // with k-dim simultaneously. To do this, we check qi / 2 > ki at ones instead of\n                    // qi >= 2*ki or 2*ki+1. It works because an diagonal element for an odd qi will be\n                    // zero due to slope * (qi - 2*ki+1) = 0. Thus, we don't handle the upper diagonal\n                    // separately, whose values are negligible due to the negative infinity mask.\n                    int qidx = qi + j * gridDim.x;\n                    T2  dist(2.0f * ki - qidx, 2.0f * ki + 1 - qidx);\n                    pos_bias[j] = hmul2<T2>(linear_bias_slope, dist);\n                }\n            }\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                mask_val[j] = hmul2<T2>(hsub2<T2>(ONE, mask_val[j]), NEG_INFTY);\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                T2 val = hadd2<T2>(hmul2<T2>(qk_scale, qk[j]), mask_val[j]);\n                if (linear_bias_slopes != nullptr) {\n                    val = hadd2<T2>(val, pos_bias[j]);\n                }\n                data[j][i]   = val;\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, Q_ITEMS_PER_THREAD>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, Q_ITEMS_PER_THREAD>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[Q_ITEMS_PER_THREAD];\n#pragma unroll\n        for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; ++j) {\n                data[j][i] = hexp2<T2>(hsub2<T2>(data[j][i], cuda_cast<T2>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, Q_ITEMS_PER_THREAD>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, Q_ITEMS_PER_THREAD>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk_offset[j] = ((bi * head_num + hi) * q_length + qi + j * gridDim.x) * (k_length / 2) + blockDim.x * i\n                               + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                attn_score_h2[qk_offset[j]] = hmul2<T2>(data[j][i], cuda_cast<T2>(s_sum[j]));\n            }\n        }\n    }\n}\n\n#define LAUNCH_MAKSED_SOFTMAX_(T_, ITEMS_PER_THREAD)                                                                   \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    block.x = (block.x + 31) / 32 * 32;                                                                                \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            softmax_kernel_h2_v2<T_, ITEMS_PER_THREAD, 4>                                                              \\\n                <<<grid, block, 0, stream>>>((T_*)param.attention_score,                                               \\\n                                             (const T_*)param.qk,                                                      \\\n                                             (const T_*)param.attention_mask,                                          \\\n                                             (const T_*)param.linear_bias_slopes,                                      \\\n                                             param.batch_size,                                                         \\\n                                             param.num_heads,                                                          \\\n                                             param.q_length,                                                           \\\n                                             param.k_length,                                                           \\\n                                             (const T_)param.qk_scale);                                                \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            softmax_kernel_h2<T_, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>((T_*)param.attention_score,            \\\n                                                                                (const T_*)param.qk,                   \\\n                                                                                (const T_*)param.attention_mask,       \\\n                                                                                (const T_*)param.linear_bias_slopes,   \\\n                                                                                param.batch_size,                      \\\n                                                                                param.num_heads,                       \\\n                                                                                param.q_length,                        \\\n                                                                                param.k_length,                        \\\n                                                                                (const T_)param.qk_scale);             \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        softmax_kernel<T, T_IN, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>(param.attention_score,                   \\\n                                                                              param.qk,                                \\\n                                                                              param.attention_mask,                    \\\n                                                                              param.linear_bias_slopes,                \\\n                                                                              param.batch_size,                        \\\n                                                                              param.num_heads,                         \\\n                                                                              param.q_length,                          \\\n                                                                              param.k_length,                          \\\n                                                                              param.qk_scale);                         \\\n    }\n\n#define LAUNCH_MAKSED_SOFTMAX(ITEMS_PER_THREAD) LAUNCH_MAKSED_SOFTMAX_(half, ITEMS_PER_THREAD)\n\ntemplate<typename T, typename T_IN>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<T, T_IN>& param, cudaStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX(4)\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX(2)\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX(1)\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\n\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<float, float>& param, cudaStream_t stream);\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<half, float>& param, cudaStream_t stream);\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<half, half>& param, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate<>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<__nv_bfloat16, float>& param, cudaStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    using T    = __nv_bfloat16;\n    using T_IN = float;\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 4);\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 2);\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 1);\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\ntemplate<>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<__nv_bfloat16, __nv_bfloat16>& param, cudaStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    using T    = __nv_bfloat16;\n    using T_IN = __nv_bfloat16;\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 4);\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 2);\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX_(__nv_bfloat16, 1);\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\n\n#endif\n\n#undef LAUNCH_MAKSED_SOFTMAX\n#undef LAUNCH_MAKSED_SOFTMAX_\n\ntemplate<typename T>\n__global__ void transpose(const T*     src,\n                          T*           dst,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* scale,\n                          int          int8_mode)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    if (int8_mode == 2) {\n        using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n        using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n\n        const Float_Packed_T scale_val = cuda_cast<Float_Packed_T>(*scale);\n        reinterpret_cast<Int8_Packed_T*>(dst)[target_id] =\n            cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(src[tid]) * scale_val);\n    }\n    else {\n        dst[target_id] = src[tid];\n    }\n}\n\ntemplate<>\n__global__ void transpose(const float* src,\n                          float*       dst,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* scale,\n                          int          int8_mode)\n{\n    int batch_id = blockIdx.x / (head_num * seq_len);\n    int seq_id   = blockIdx.x % seq_len;\n    int head_id  = (blockIdx.x % (head_num * seq_len)) / seq_len;\n\n    const int target_id = batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head\n                          + head_id * size_per_head + threadIdx.x;\n    const int src_id = blockIdx.x * size_per_head + threadIdx.x;\n\n    if (int8_mode == 2) {\n        const float scale_val                     = *scale;\n        reinterpret_cast<int8_t*>(dst)[target_id] = cuda_cast<int8_t>(src[src_id] * scale_val);\n    }\n    else {\n        dst[target_id] = src[src_id];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeQKV(T*           dst,\n                        T*           src,\n                        const int    batch_size,\n                        const int    seq_len,\n                        const int    head_num,\n                        const int    size_per_head,\n                        const float* scale,\n                        const int    int8_mode,\n                        cudaStream_t stream)\n{\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        int seq_per_block = 1;\n        grid.x            = batch_size * head_num * seq_len / seq_per_block;\n        while (seq_per_block < 4 && grid.x % 2 == 0) {\n            grid.x /= 2;\n            seq_per_block *= 2;\n        }\n\n        FT_CHECK(grid.x * seq_per_block == (size_t)batch_size * head_num * seq_len);\n\n        if (seq_per_block * size_per_head % 2 == 0) {\n            block.x = seq_per_block * size_per_head / 2;\n            if (std::is_same<T, half>::value) {\n                transpose<half2><<<grid, block, 0, stream>>>(\n                    (half2*)src, (half2*)dst, batch_size, seq_len, head_num, size_per_head / 2, scale, int8_mode);\n            }\n#ifdef ENABLE_BF16\n            else {\n                transpose<__nv_bfloat162><<<grid, block, 0, stream>>>((__nv_bfloat162*)src,\n                                                                      (__nv_bfloat162*)dst,\n                                                                      batch_size,\n                                                                      seq_len,\n                                                                      head_num,\n                                                                      size_per_head / 2,\n                                                                      scale,\n                                                                      int8_mode);\n            }\n#endif\n        }\n        else {\n            block.x = seq_per_block * size_per_head;\n            transpose<T>\n                <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head, scale, int8_mode);\n        }\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transpose<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head, scale, int8_mode);\n    }\n}\n\n#define INSTANTIATETRANSPOSEQKV(T)                                                                                     \\\n    template void invokeTransposeQKV(T*           src,                                                                 \\\n                                     T*           dst,                                                                 \\\n                                     const int    batch_size,                                                          \\\n                                     const int    seq_len,                                                             \\\n                                     const int    head_num,                                                            \\\n                                     const int    size_per_head,                                                       \\\n                                     const float* scale,                                                               \\\n                                     const int    int8_mode,                                                           \\\n                                     cudaStream_t stream)\nINSTANTIATETRANSPOSEQKV(float);\nINSTANTIATETRANSPOSEQKV(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEQKV(__nv_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEQKV\n\ntemplate<typename T>\n__global__ void add_QKV_bias_rebuild_padding_ia3(const T*   Q,\n                                                 const T*   bias_Q,\n                                                 const T*   K,\n                                                 const T*   bias_K,\n                                                 const T*   V,\n                                                 const T*   bias_V,\n                                                 T*         q_buf_,\n                                                 T*         k_buf_,\n                                                 T*         v_buf_,\n                                                 const int* ia3_tasks,\n                                                 const T*   ia3_key_weights,\n                                                 const T*   ia3_value_weights,\n                                                 const int  batch_size,\n                                                 const int  seq_len,\n                                                 const int  head_num,\n                                                 const int  size_per_head,\n                                                 const int* mask_offset)\n{\n    const int bid = blockIdx.x;\n\n    const int tgt_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int tgt_seq_id   = (bid + mask_offset[bid]) % seq_len;\n    const int n            = head_num * size_per_head;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[tgt_batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        const int tgt_head_id   = idx / size_per_head;\n        const int tgt_hidden_id = idx % size_per_head;\n\n        const int src_id = bid * n + idx;\n        const int tgt_id = tgt_batch_id * head_num * seq_len * size_per_head + tgt_head_id * seq_len * size_per_head\n                           + tgt_seq_id * size_per_head + tgt_hidden_id;\n\n        q_buf_[tgt_id] = add(ldg(&Q[src_id]), ldg(&bias_Q[idx]));\n\n        T k = ldg(&K[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + idx];\n        }\n        k_buf_[tgt_id] = add(k, ldg(&bias_K[idx]));\n\n        T v = ldg(&V[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + idx];\n        }\n        v_buf_[tgt_id] = add(v, ldg(&bias_V[idx]));\n    }\n}\n\ntemplate<typename T>\n__global__ void rebuild_padding_ia3(const T*   Q,\n                                    const T*   K,\n                                    const T*   V,\n                                    T*         q_buf_,\n                                    T*         k_buf_,\n                                    T*         v_buf_,\n                                    const int* ia3_tasks,\n                                    const T*   ia3_key_weights,\n                                    const T*   ia3_value_weights,\n                                    const int  batch_size,\n                                    const int  seq_len,\n                                    const int  head_num,\n                                    const int  size_per_head,\n                                    const int* mask_offset)\n{\n    const int bid = blockIdx.x;\n\n    const int tgt_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int tgt_seq_id   = (bid + mask_offset[bid]) % seq_len;\n    const int n            = head_num * size_per_head;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[tgt_batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        const int tgt_head_id   = idx / size_per_head;\n        const int tgt_hidden_id = idx % size_per_head;\n\n        const int src_id = bid * n + idx;\n        const int tgt_id = tgt_batch_id * head_num * seq_len * size_per_head + tgt_head_id * seq_len * size_per_head\n                           + tgt_seq_id * size_per_head + tgt_hidden_id;\n\n        q_buf_[tgt_id] = ldg(&Q[src_id]);\n\n        T k = ldg(&K[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + idx];\n        }\n        k_buf_[tgt_id] = k;\n\n        T v = ldg(&V[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + idx];\n        }\n        v_buf_[tgt_id] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasIA3RebuildPadding(T*           Q,\n                                       const T*     bias_Q,\n                                       T*           K,\n                                       const T*     bias_K,\n                                       T*           V,\n                                       const T*     bias_V,\n                                       T*           q_buf,\n                                       T*           k_buf,\n                                       T*           v_buf,\n                                       const int    batch_size,\n                                       const int    seq_len,\n                                       const int    head_num,\n                                       const int    size_per_head,\n                                       const int    valid_word_num,\n                                       const int*   mask_offset,\n                                       const int*   ia3_tasks,\n                                       const T*     ia3_key_weights,\n                                       const T*     ia3_value_weights,\n                                       cudaStream_t stream)\n{\n#ifdef ENABLE_BF16\n    bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value) && (size_per_head % 2 == 0);\n#else\n    bool       is_half2 = (std::is_same<T, half>::value) && (size_per_head % 2 == 0);\n#endif\n    using T2       = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n    int block_size = head_num * size_per_head;\n    if (is_half2) {\n        while (block_size > 512) {\n            if (block_size % 2 == 0) {\n                block_size /= 2;\n            }\n            else {\n                is_half2   = false;\n                block_size = std::min(block_size, 512);\n                break;\n            }\n        }\n    }\n    else {\n        block_size = std::min(block_size, 512);\n    }\n\n    if (bias_Q == nullptr && bias_K == nullptr && bias_V == nullptr) {\n        if (is_half2) {\n            rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>((T2*)Q,\n                                                                           (T2*)K,\n                                                                           (T2*)V,\n                                                                           (T2*)q_buf,\n                                                                           (T2*)k_buf,\n                                                                           (T2*)v_buf,\n                                                                           ia3_tasks,\n                                                                           (const T2*)ia3_key_weights,\n                                                                           (const T2*)ia3_value_weights,\n                                                                           batch_size,\n                                                                           seq_len,\n                                                                           head_num,\n                                                                           size_per_head / 2,\n                                                                           mask_offset);\n        }\n        else {\n            rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>(Q,\n                                                                           K,\n                                                                           V,\n                                                                           q_buf,\n                                                                           k_buf,\n                                                                           v_buf,\n                                                                           ia3_tasks,\n                                                                           ia3_key_weights,\n                                                                           ia3_value_weights,\n                                                                           batch_size,\n                                                                           seq_len,\n                                                                           head_num,\n                                                                           size_per_head,\n                                                                           mask_offset);\n        }\n    }\n    else if (bias_Q != nullptr && bias_K != nullptr && bias_V != nullptr) {\n        if (is_half2) {\n            add_QKV_bias_rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>((T2*)Q,\n                                                                                        (const T2*)bias_Q,\n                                                                                        (T2*)K,\n                                                                                        (const T2*)bias_K,\n                                                                                        (T2*)V,\n                                                                                        (const T2*)bias_V,\n                                                                                        (T2*)q_buf,\n                                                                                        (T2*)k_buf,\n                                                                                        (T2*)v_buf,\n                                                                                        ia3_tasks,\n                                                                                        (const T2*)ia3_key_weights,\n                                                                                        (const T2*)ia3_value_weights,\n                                                                                        batch_size,\n                                                                                        seq_len,\n                                                                                        head_num,\n                                                                                        size_per_head / 2,\n                                                                                        mask_offset);\n        }\n        else {\n            add_QKV_bias_rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>(Q,\n                                                                                        bias_Q,\n                                                                                        K,\n                                                                                        bias_K,\n                                                                                        V,\n                                                                                        bias_V,\n                                                                                        q_buf,\n                                                                                        k_buf,\n                                                                                        v_buf,\n                                                                                        ia3_tasks,\n                                                                                        ia3_key_weights,\n                                                                                        ia3_value_weights,\n                                                                                        batch_size,\n                                                                                        seq_len,\n                                                                                        head_num,\n                                                                                        size_per_head,\n                                                                                        mask_offset);\n        }\n    }\n    else {\n        FT_CHECK(false);\n    }\n}\n\n#define INSTANTIATEADDQKVBIASIA3REBUILDPADDING(T)                                                                      \\\n    template void invokeAddQKVBiasIA3RebuildPadding(T*           Q,                                                    \\\n                                                    const T*     bias_Q,                                               \\\n                                                    T*           K,                                                    \\\n                                                    const T*     bias_K,                                               \\\n                                                    T*           V,                                                    \\\n                                                    const T*     bias_V,                                               \\\n                                                    T*           q_buf,                                                \\\n                                                    T*           k_buf,                                                \\\n                                                    T*           v_buf,                                                \\\n                                                    const int    batch_size,                                           \\\n                                                    const int    seq_len,                                              \\\n                                                    const int    head_num,                                             \\\n                                                    const int    size_per_head,                                        \\\n                                                    const int    valid_word_num,                                       \\\n                                                    const int*   mask_offset,                                          \\\n                                                    const int*   ia3_tasks,                                            \\\n                                                    const T*     ia3_key_weights,                                      \\\n                                                    const T*     ia3_value_weights,                                    \\\n                                                    cudaStream_t stream)\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(float);\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDQKVBIASREBUILDPADDING\n\ntemplate<typename T>\n__global__ void transpose_remove_padding(const T*     src,\n                                         T*           dst,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         const int*   mask_offset,\n                                         const float* scale,\n                                         const int    int8_mode)\n{\n    // TODO: optimize this kernel?\n    // do remove_sequence_length_padding\n    const int bid = blockIdx.x;  // batch * seq_len or valid_word_num\n\n    const int src_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int src_seq_id   = (bid + mask_offset[bid]) % seq_len;\n\n    const int dst_seq_id = bid;\n\n    const int src_offset_base = src_batch_id * seq_len * head_num * size_per_head + src_seq_id * size_per_head;\n    const int dst_offset_base = dst_seq_id * head_num * size_per_head;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    const Float_Packed_T scale_val =\n        int8_mode == 2 ? cuda_cast<Float_Packed_T>(*scale) : cuda_cast<Float_Packed_T>(0.0f);\n\n    for (int idx = threadIdx.x; idx < head_num * size_per_head; idx += blockDim.x) {\n        const int head_id   = idx / size_per_head;\n        const int hidden_id = idx % size_per_head;\n        const T   src_elem  = ldg(&src[src_offset_base + head_id * seq_len * size_per_head + hidden_id]);\n        if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(dst)[dst_offset_base + idx] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(src_elem) * scale_val);\n        }\n        else {\n            dst[dst_offset_base + idx] = src_elem;\n        }\n    }\n}\n\n// clang-format off\ntemplate<typename T>\nvoid invokeTransposeAttentionOutRemovePadding(T*           src,\n                                              T*           dst,\n                                              const int    valid_word_num,\n                                              const int    batch_size,\n                                              const int    seq_len,\n                                              const int    head_num,\n                                              const int    size_per_head,\n                                              const int*   mask_offset,\n                                              const float* scale,\n                                              const int    int8_mode,\n                                              cudaStream_t stream)\n{\n#ifdef ENABLE_BF16\n    bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value) && (size_per_head % 2 == 0);\n#else\n    bool is_half2 = (std::is_same<T, half>::value) && (size_per_head % 2 == 0);\n#endif\n    using T2       = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n    int block_size = head_num * size_per_head;\n    if (is_half2) {\n        while (block_size > 512) {\n            if (block_size % 2 == 0) {\n                block_size /= 2;\n            }\n            else {\n                is_half2   = false;\n                block_size = std::min(block_size, 1024);\n                break;\n            }\n        }\n    }\n    else {\n        block_size = std::min(block_size, 1024);\n    }\n\n    if (is_half2) {\n        transpose_remove_padding<T2><<<valid_word_num, block_size, 0, stream>>>(\n            (T2*)src, (T2*)dst, batch_size, seq_len, head_num, size_per_head / 2, mask_offset, scale, int8_mode);\n    }\n    else {\n        transpose_remove_padding<<<valid_word_num, block_size, 0, stream>>>(\n            src, dst, batch_size, seq_len, head_num, size_per_head, mask_offset, scale, int8_mode);\n    }\n}\n// clang-format on\n\n#define INSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(T)                                                               \\\n    template void invokeTransposeAttentionOutRemovePadding(T*           src,                                           \\\n                                                           T*           dst,                                           \\\n                                                           const int    valid_word_num,                                \\\n                                                           const int    batch_size,                                    \\\n                                                           const int    seq_len,                                       \\\n                                                           const int    head_num,                                      \\\n                                                           const int    size_per_head,                                 \\\n                                                           const int*   mask_offset,                                   \\\n                                                           const float* scale,                                         \\\n                                                           const int    int8_mode,                                     \\\n                                                           cudaStream_t stream)\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(float);\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(__nv_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING\n\ntemplate<typename T>\n__global__ void add_fusedQKV_bias_transpose_kernel(T* q_buf,\n                                                   T* k_buf,\n                                                   T* v_buf,\n                                                   T* QKV,\n                                                   const T* __restrict qkv_bias,\n                                                   const int*   padding_offset,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    token_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const float* scale,\n                                                   const int    int8_mode)\n{\n    // QKV: [token_num, 3, n]\n    // qkv_bias: [3, n]\n    // q_buf, k_buf, v_buf: [batch, head_num, seq_len, size_per_head]\n\n    T*        qkv_ptr[3] = {q_buf, k_buf, v_buf};\n    const int n          = head_num * size_per_head;\n    for (int index = blockDim.x * blockIdx.x + threadIdx.x; index < token_num * 3 * n;\n         index += gridDim.x * blockDim.x) {\n        const int bias_id = index % (3 * n);\n\n        const int token_idx        = index / (3 * n);\n        const int token_padded_idx = token_idx + (padding_offset == nullptr ? 0 : padding_offset[token_idx]);\n        const int target_batch_id  = token_padded_idx / seq_len;\n        const int seq_id           = token_padded_idx % seq_len;\n\n        const int qkv_id  = (index % (3 * n)) / n;\n        const int head_id = (index % n) / size_per_head;\n        const int size_id = index % size_per_head;\n\n        T val;\n        if (int8_mode == 2) {\n            val = cuda_cast<T>(cuda_cast<float>(reinterpret_cast<const int8_t*>(QKV)[index]) * scale[qkv_id]);\n        }\n        else {\n            val = ldg(&QKV[index]);\n        }\n        val = val + ldg(&qkv_bias[bias_id]);\n\n        if (int8_mode == 2) {\n            // TODO(mseznec): add support for int8 BMM with FusedAtt\n        }\n        else {\n            QKV[index] = val;\n        }\n\n        qkv_ptr[qkv_id][target_batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head\n                        + seq_id * size_per_head + size_id] = val;\n    }\n}\n\ntemplate<typename T>\nstruct Vec_t {\n    static constexpr int size = 0;\n};\n\ntemplate<>\nstruct Vec_t<float> {\n    using Type                = float2;\n    static constexpr int size = 2;\n};\n\ntemplate<>\nstruct Vec_t<half> {\n    using Type                = uint32_t;\n    static constexpr int size = 2;\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct Vec_t<__nv_bfloat16> {\n    using Type                = __nv_bfloat162;\n    static constexpr int size = 2;\n};\n#endif\n\ntemplate<typename T, bool PREFIX_PROMPT>\n__global__ void add_fusedQKV_bias_transpose_kernel(T*                               q_buf,\n                                                   T*                               k_buf,\n                                                   T*                               v_buf,\n                                                   PrefixPromptBatchWeightsParam<T> param,\n                                                   T*                               QKV,\n                                                   const T* __restrict qkv_bias,\n                                                   const int* padding_offset,\n                                                   const int  batch_size,\n                                                   const int  seq_len,\n                                                   const int  head_num,\n                                                   const int  size_per_head,\n                                                   const int  rotary_embedding_dim,\n                                                   const bool neox_rotary_style)\n{\n    // This kernel add bias to QKV, which has shape [batch_size, seq_len, 3, head_num, size_per_head], and\n    // QKV split to 3 split buffer q, k, v and transpose them to [batch_size, head_num, seq_len, size_per_head].\n    // For q and k, also apply the rotary embedding.\n\n    // When we pass prefix prompt, this kernel also concatenate the prefix prompt and key/value along\n    // seq_len dimension like [prompt, key/value].\n    // So, the final shape of q is same ([batch_size, head_num, seq_len, size_per_head]), but\n    // the shapes of key and values become [batch_size, head_num, max_prefix_prompt_length + seq_len, size_per_head].\n\n    // NOTE: QKV src shape (batch_size, seq_len, 3, head_num, size_per_head)\n    //  QKV dst shape (3, batch_size, head_num, seq_len, size_per_head)\n    extern __shared__ __align__(sizeof(float2)) char smem_[];  // align on largest vector type\n\n    constexpr int vec_size         = Vec_t<T>::size;\n    using Vec_t                    = typename Vec_t<T>::Type;\n    const int token_idx            = blockIdx.x - batch_size * param.max_prefix_prompt_length;\n    const int token_padding_offset = (padding_offset == nullptr || token_idx < 0) ? 0 : padding_offset[token_idx];\n    const int tgt_token_idx        = token_idx + token_padding_offset;\n\n    const int batch_idx = tgt_token_idx / seq_len;\n    const int seq_idx   = tgt_token_idx % seq_len;\n\n    const int head_idx = blockIdx.y;\n    const int tidx     = threadIdx.x;\n\n    const int total_seq_len = param.max_prefix_prompt_length + seq_len;\n\n    const bool is_masked = tidx * vec_size >= size_per_head;\n    // NOTE: blockIdx.x < batch_size * param.max_prefix_prompt_length really handles prefix prompts\n    if (PREFIX_PROMPT && token_idx < 0) {\n        const int prompt_batch_idx = blockIdx.x / param.max_prefix_prompt_length;\n        const int prompt_seq_idx   = blockIdx.x % param.max_prefix_prompt_length;\n        const int prompt_length    = param.d_prefix_prompt_lengths[prompt_batch_idx];\n\n        if (prompt_seq_idx < prompt_length) {\n            const int dest_kv_idx = prompt_batch_idx * size_per_head * total_seq_len * head_num\n                                    + head_idx * size_per_head * total_seq_len + prompt_seq_idx * size_per_head\n                                    + tidx * vec_size;\n            const int prefix_kv_idx =\n                size_per_head * prompt_length * head_idx + size_per_head * prompt_seq_idx + tidx * vec_size;\n\n            const T* prefix_prompt_k = param.d_prefix_prompt_batch[prompt_batch_idx]\n                                       + param.prefix_prompt_layer_offset_per_seq * prompt_length;\n            const T* prefix_prompt_v = prefix_prompt_k + prompt_length * head_num * size_per_head;\n            if (!is_masked) {\n                *reinterpret_cast<Vec_t*>(&k_buf[dest_kv_idx]) =\n                    *reinterpret_cast<const Vec_t*>(&prefix_prompt_k[prefix_kv_idx]);\n                *reinterpret_cast<Vec_t*>(&v_buf[dest_kv_idx]) =\n                    *reinterpret_cast<const Vec_t*>(&prefix_prompt_v[prefix_kv_idx]);\n            }\n        }\n        return;\n    }\n\n    const int prefix_prompt_length = PREFIX_PROMPT ? param.d_prefix_prompt_lengths[batch_idx] : 0;\n    const int hidden_idx           = head_idx * size_per_head + tidx * vec_size;\n    const int n                    = head_num * size_per_head;\n\n    // the [0..seq_len) indices really handle KV [max_pp_len..seq_len+max_pp_len)\n    // and Q [0..seq_len)\n    // Note: if !PREFIX_PROMPT, max_pp_len = 0, so it's no-op\n    const int dst_kv_seq_idx = seq_idx + prefix_prompt_length;\n\n    // NOTE: q has seq len excluding prefix prompt\n    // src QKV: [batch, time, 3, head, hidden]\n    const int src_q_idx = token_idx * 3 * n + hidden_idx;\n    const int src_k_idx = token_idx * 3 * n + hidden_idx + n;\n    const int src_v_idx = token_idx * 3 * n + hidden_idx + 2 * n;\n\n    Vec_t q, k, v;\n    Vec_t q_bias, k_bias, v_bias;\n    if (!is_masked) {\n        q = *reinterpret_cast<const Vec_t*>(&QKV[src_q_idx]);\n        k = *reinterpret_cast<const Vec_t*>(&QKV[src_k_idx]);\n        v = *reinterpret_cast<const Vec_t*>(&QKV[src_v_idx]);\n\n        q_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx]);\n        k_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx + n]);\n        v_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx + 2 * n]);\n    }\n\n    q = mmha::add(q, q_bias);\n    k = mmha::add(k, k_bias);\n    v = mmha::add(v, v_bias);\n\n    if (!neox_rotary_style) {\n        mmha::apply_rotary_embedding(q, k, tidx, rotary_embedding_dim, dst_kv_seq_idx);\n    }\n    else {\n        const bool do_rotary = !is_masked && vec_size * tidx < rotary_embedding_dim;\n\n        T* q_smem = reinterpret_cast<T*>(smem_);\n        T* k_smem = q_smem + rotary_embedding_dim;\n\n        const int half_rotary_dim = rotary_embedding_dim / 2;\n        const int half_idx        = (tidx * vec_size) / half_rotary_dim;\n        const int intra_half_idx  = (tidx * vec_size) % half_rotary_dim;\n        const int smem_pitch      = half_rotary_dim;  // TODO: adjust for bank conflicts?\n\n        if (do_rotary) {\n            *reinterpret_cast<Vec_t*>(q_smem + half_idx * smem_pitch + intra_half_idx) = q;\n            *reinterpret_cast<Vec_t*>(k_smem + half_idx * smem_pitch + intra_half_idx) = k;\n        }\n\n        __syncthreads();\n\n        const int     transpose_idx = half_idx * (half_rotary_dim / 2) + intra_half_idx / 2;\n        constexpr int tidx_factor   = vec_size / 2;\n        if (do_rotary) {\n            mmha::vec_from_smem_transpose(q, q_smem, transpose_idx, smem_pitch);\n            mmha::vec_from_smem_transpose(k, k_smem, transpose_idx, smem_pitch);\n\n            mmha::apply_rotary_embedding(q, k, transpose_idx / tidx_factor, rotary_embedding_dim, dst_kv_seq_idx);\n\n            mmha::write_smem_transpose(q, q_smem, transpose_idx, smem_pitch);\n            mmha::write_smem_transpose(k, k_smem, transpose_idx, smem_pitch);\n        }\n\n        __syncthreads();\n\n        if (do_rotary) {\n            q = *reinterpret_cast<Vec_t*>(q_smem + half_idx * smem_pitch + intra_half_idx);\n            k = *reinterpret_cast<Vec_t*>(k_smem + half_idx * smem_pitch + intra_half_idx);\n        }\n    }\n    if (!is_masked) {\n        *reinterpret_cast<Vec_t*>(&QKV[src_q_idx]) = q;\n        *reinterpret_cast<Vec_t*>(&QKV[src_k_idx]) = k;\n        *reinterpret_cast<Vec_t*>(&QKV[src_v_idx]) = v;\n    }\n\n    const int dest_q_idx = batch_idx * size_per_head * seq_len * head_num + head_idx * size_per_head * seq_len\n                           + seq_idx * size_per_head + tidx * vec_size;\n\n    const int dest_kv_idx = batch_idx * size_per_head * total_seq_len * head_num\n                            + head_idx * size_per_head * total_seq_len + dst_kv_seq_idx * size_per_head\n                            + tidx * vec_size;\n\n    if (!is_masked) {\n        *reinterpret_cast<Vec_t*>(&q_buf[dest_q_idx])  = q;\n        *reinterpret_cast<Vec_t*>(&k_buf[dest_kv_idx]) = k;\n        *reinterpret_cast<Vec_t*>(&v_buf[dest_kv_idx]) = v;\n    }\n}\n\n#define FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, PREFIX_PROMPT)                                                              \\\n    add_fusedQKV_bias_transpose_kernel<T, PREFIX_PROMPT><<<grid, block, smem_size, stream>>>(q_buf,                    \\\n                                                                                             k_buf,                    \\\n                                                                                             v_buf,                    \\\n                                                                                             param,                    \\\n                                                                                             QKV,                      \\\n                                                                                             qkv_bias,                 \\\n                                                                                             padding_offset,           \\\n                                                                                             batch_size,               \\\n                                                                                             seq_len,                  \\\n                                                                                             head_num,                 \\\n                                                                                             size_per_head,            \\\n                                                                                             rotary_embedding_dim,     \\\n                                                                                             neox_rotary_style);\n\ntemplate<typename T>\nvoid invokeAddFusedQKVBiasTranspose(T*                               q_buf,\n                                    T*                               k_buf,\n                                    T*                               v_buf,\n                                    PrefixPromptBatchWeightsParam<T> param,\n                                    T*                               QKV,\n                                    const T*                         qkv_bias,\n                                    const int*                       padding_offset,\n                                    const int                        batch_size,\n                                    const int                        seq_len,\n                                    const int                        token_num,\n                                    const int                        head_num,\n                                    const int                        size_per_head,\n                                    const int                        rotary_embedding_dim,\n                                    const int                        neox_rotary_style,\n                                    const float*                     scale,\n                                    const int                        int8_mode,\n                                    cudaStream_t                     stream)\n{\n    // [bs, seq_len, 3, head, Dh]\n    if (rotary_embedding_dim == 0 && param.max_prefix_prompt_length == 0) {\n        const int m = token_num;\n        const int n = head_num * size_per_head;\n        dim3      block(384);\n        dim3      grid((int)(ceil(1.0 * m * n / 384)));\n        add_fusedQKV_bias_transpose_kernel<<<grid, block, 0, stream>>>(q_buf,\n                                                                       k_buf,\n                                                                       v_buf,\n                                                                       QKV,\n                                                                       qkv_bias,\n                                                                       padding_offset,\n                                                                       batch_size,\n                                                                       seq_len,\n                                                                       token_num,\n                                                                       head_num,\n                                                                       size_per_head,\n                                                                       scale,\n                                                                       int8_mode);\n    }\n    else {\n        FT_CHECK_WITH_INFO(int8_mode != 2, \"w8a8 not yet implemented with prefix prompt\");  // TODO(mseznec)\n        // To implement rotary embeddings, each thread processes two QKV elems:\n        dim3   block((size_per_head / Vec_t<T>::size + 31) / 32 * 32);\n        dim3   grid(token_num + batch_size * param.max_prefix_prompt_length, head_num);\n        size_t smem_size = neox_rotary_style ? 2 * rotary_embedding_dim * sizeof(T) : 0;\n        // NOTE: add offset for rotary embedding\n        //  add_fusedQKV_bias_transpose_kernel<<<grid, block, 0, stream>>>(\n        //      q_buf, k_buf, v_buf, param, QKV, qkv_bias, batch_size, seq_len, head_num, size_per_head,\n        //      rotary_embedding_dim);\n        if (param.max_prefix_prompt_length == 0) {\n            FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, false);\n        }\n        else {\n            FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, true);\n        }\n    }\n}\n\n#define INSTANTIATEADDFUSEDQKVBIASTRANSPOSE(T)                                                                         \\\n    template void invokeAddFusedQKVBiasTranspose(T*                               q_buf,                               \\\n                                                 T*                               k_buf,                               \\\n                                                 T*                               v_buf,                               \\\n                                                 PrefixPromptBatchWeightsParam<T> param,                               \\\n                                                 T*                               QKV,                                 \\\n                                                 const T*                         qkv_bias,                            \\\n                                                 const int*                       padding_offset,                      \\\n                                                 const int                        batch_size,                          \\\n                                                 const int                        seq_len,                             \\\n                                                 const int                        token_num,                           \\\n                                                 const int                        head_num,                            \\\n                                                 const int                        size_per_head,                       \\\n                                                 const int                        rotary_embedding_dim,                \\\n                                                 const int                        neox_rotary_style,                   \\\n                                                 const float*                     scale,                               \\\n                                                 const int                        int8_mode,                           \\\n                                                 cudaStream_t                     stream)\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(float);\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDFUSEDQKVBIASTRANSPOSE\n\ntemplate<typename T>\n__global__ void transpose_4d(T*        dst,\n                             T*        src,\n                             const int dim0,\n                             const int dim1,\n                             const int dim2,\n                             const int dim3,\n                             const int dim0_leading_dim,\n                             const int ite)\n{\n    // transpose from [dim0, dim1, dim2, dim3] to [dim2, X, dim1, dim3]\n    // where the dimension of X is dim0_leading_dim, and offset is ite * dim0\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < dim0 * dim1 * dim2 * dim3; i += blockDim.x * gridDim.x) {\n        int       index = i;\n        const int d3    = index % dim3;\n        index           = (index - d3) / dim3;\n        const int d2    = index % dim2;\n        index           = (index - d2) / dim2;\n        const int d1    = index % dim1;\n        index           = (index - d1) / dim1;\n        const int d0    = index % dim0;\n        index           = (index - d0) / dim0;\n        dst[d2 * dim0_leading_dim * dim1 * dim3 + (d0 + dim0 * ite) * dim1 * dim3 + d1 * dim3 + d3] = src[i];\n    }\n}\n\ntemplate<>\n__global__ void transpose_4d(half*     dst,\n                             half*     src,\n                             const int dim0,\n                             const int dim1,\n                             const int dim2,\n                             const int dim3,\n                             const int dim0_leading_dim,\n                             const int ite)\n{\n    half2*    dst_ptr   = (half2*)dst;\n    half2*    src_ptr   = (half2*)src;\n    const int half_dim3 = dim3 / 2;\n    // transpose from [dim0, dim1, dim2, half_dim3] to [dim2, dim0, dim1, half_dim3]\n    // where the dimension of X is dim0_leading_dim, and offset is ite * dim0\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < dim0 * dim1 * dim2 * half_dim3;\n         i += blockDim.x * gridDim.x) {\n        int       index = i;\n        const int d3    = index % half_dim3;\n        index           = (index - d3) / half_dim3;\n        const int d2    = index % dim2;\n        index           = (index - d2) / dim2;\n        const int d1    = index % dim1;\n        index           = (index - d1) / dim1;\n        const int d0    = index % dim0;\n        index           = (index - d0) / dim0;\n        dst_ptr[d2 * dim0_leading_dim * dim1 * half_dim3 + (d0 + dim0 * ite) * dim1 * half_dim3 + d1 * half_dim3 + d3] =\n            src_ptr[i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTranspose4d(T*           dst,\n                       T*           src,\n                       const int    local_batch_size,\n                       const int    seq_len,\n                       const int    size_per_head,\n                       const int    local_hidden_units,\n                       const int    local_head_num,\n                       const int    batch_size,\n                       const int    ite,\n                       cudaStream_t stream)\n{\n    transpose_4d<<<local_batch_size * seq_len * local_hidden_units / 512, 512 / (4 / (sizeof(T))), 0, stream>>>(\n        dst, src, local_batch_size, local_head_num, seq_len, size_per_head, batch_size, ite);\n}\n\n#define INSTANTIATETRANSPOSE4D(T)                                                                                      \\\n    template void invokeTranspose4d(T*           dst,                                                                  \\\n                                    T*           src,                                                                  \\\n                                    const int    local_batch_size,                                                     \\\n                                    const int    seq_len,                                                              \\\n                                    const int    size_per_head,                                                        \\\n                                    const int    local_hidden_units,                                                   \\\n                                    const int    local_head_num,                                                       \\\n                                    const int    batch_size,                                                           \\\n                                    const int    ite,                                                                  \\\n                                    cudaStream_t stream)\nINSTANTIATETRANSPOSE4D(float);\nINSTANTIATETRANSPOSE4D(half);\n#undef INSTANTIATETRANSPOSE4D\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_k_cache(\n    T* k_dst, const T* k_src, const int head_num, const int size_per_head, const int seq_len, const int max_seq_len)\n{\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = (sizeof(T) == 4) ? 4 : 8;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * seq_len\n                                                  + head_id * size_per_head * seq_len);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        key_dst[out_idx] = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n    }\n}\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_v_cache(\n    T* v_dst, const T* v_src, const int head_num, const int size_per_head, const int seq_len, const int max_seq_len)\n{\n    const int batch_id = blockIdx.y;\n    const int head_id  = blockIdx.z;\n\n    // 16 byte loads will handle \"x\" dimension\n    auto val_src = reinterpret_cast<const uint4*>(v_src + batch_id * head_num * size_per_head * seq_len\n                                                  + head_id * size_per_head * seq_len);\n    auto val_dst = reinterpret_cast<uint4*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    constexpr int X_ELEMS             = (sizeof(T) == 4) ? 4 : 8;\n    const int     size_per_head_div_x = size_per_head / X_ELEMS;\n\n    if (idx >= size_per_head_div_x * seq_len) {\n        return;\n    }\n\n    val_dst[idx] = val_src[idx];\n}\n\ntemplate<typename T>\nvoid invokeTranspose4dBatchMajor(T*           k_dst,\n                                 T*           v_dst,\n                                 const T*     k_src,\n                                 const T*     v_src,\n                                 const int    local_batch_size,\n                                 const int    seq_len,\n                                 const int    max_seq_len,\n                                 const int    size_per_head,\n                                 const int    local_head_num,\n                                 cudaStream_t stream)\n{\n    constexpr int block_sz = 128;\n    constexpr int x        = (sizeof(T) == 4) ? 4 : 8;\n    int           size     = max_seq_len * size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n    dim3          grid_v((seq_len * size_per_head / x + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n\n    transpose_4d_batch_major_k_cache<<<grid, block_sz, 0, stream>>>(\n        k_dst, k_src, local_head_num, size_per_head, seq_len, max_seq_len);\n\n    transpose_4d_batch_major_v_cache<<<grid_v, block_sz, 0, stream>>>(\n        v_dst, v_src, local_head_num, size_per_head, seq_len, max_seq_len);\n}\n\n#define INSTANTIATETRANSPOSE4DBATCHMAJOR(T)                                                                            \\\n    template void invokeTranspose4dBatchMajor(T*           k_dst,                                                      \\\n                                              T*           v_dst,                                                      \\\n                                              const T*     k_src,                                                      \\\n                                              const T*     v_src,                                                      \\\n                                              const int    local_batch_size,                                           \\\n                                              const int    seq_len,                                                    \\\n                                              const int    max_seq_len,                                                \\\n                                              const int    size_per_head,                                              \\\n                                              const int    local_head_num,                                             \\\n                                              cudaStream_t stream)\nINSTANTIATETRANSPOSE4DBATCHMAJOR(float);\nINSTANTIATETRANSPOSE4DBATCHMAJOR(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSE4DBATCHMAJOR(__nv_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSE4DBATCHMAJOR\n\ntemplate<typename T>\n__global__ void addRelativeAttentionBias(\n    T* qk_buf, const T* relative_attention_bias, const int batch_size, const int head_num, const int seq_len)\n{\n    for (int i = threadIdx.x; i < batch_size * seq_len; i += blockDim.x) {\n        int batch_id = i / seq_len;\n        int seq_id   = i % seq_len;\n\n        const int bias_index = blockIdx.x * seq_len + seq_id;\n        const int qk_index   = batch_id * gridDim.x * seq_len + bias_index;\n        qk_buf[qk_index]     = add(qk_buf[qk_index], relative_attention_bias[bias_index]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddRelativeAttentionBias(T*           qk_buf,\n                                    const T*     relative_attention_bias,\n                                    const int    batch_size,\n                                    const int    head_num,\n                                    const int    seq_len,\n                                    cudaStream_t stream)\n{\n    // qk_buf: [batch_size, head_num, seq_len, seq_len]\n    // relative_attention_bias: [1, head_num, seq_len, seq_len]\n    dim3 grid(head_num * seq_len);\n    dim3 block(512);\n    using T2 = typename TypeConverter<T>::Type;\n#ifdef ENABLE_BF16\n    const bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value) && (seq_len % 2 == 0);\n#else\n    const bool is_half2 = (std::is_same<T, half>::value) && (seq_len % 2 == 0);\n#endif\n    if (is_half2) {\n        addRelativeAttentionBias<T2><<<grid, block, 0, stream>>>(\n            (T2*)qk_buf, (const T2*)relative_attention_bias, batch_size, head_num, seq_len / 2);\n    }\n    else {\n        addRelativeAttentionBias<<<grid, block, 0, stream>>>(\n            qk_buf, relative_attention_bias, batch_size, head_num, seq_len);\n    }\n}\n\n#define INSTANTIATEADDRELATIVEATTENTIONBIAS(T)                                                                         \\\n    template void invokeAddRelativeAttentionBias(T*           qk_buf,                                                  \\\n                                                 const T*     relative_attention_bias,                                 \\\n                                                 const int    batch_size,                                              \\\n                                                 const int    head_num,                                                \\\n                                                 const int    seq_len,                                                 \\\n                                                 cudaStream_t stream)\nINSTANTIATEADDRELATIVEATTENTIONBIAS(float);\nINSTANTIATEADDRELATIVEATTENTIONBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDRELATIVEATTENTIONBIAS(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDRELATIVEATTENTIONBIAS\n\n/*******************  invokeAddHead3SizeQKVBias  ***********************/\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, 3*batch);\n// block(num_head * size_per_head)\ntemplate<typename T>\n__global__ void add_head3Size_QKV_bias(const T*  mm_qkv,\n                                       const T*  bias_qkv,\n                                       T*        q_buf_,\n                                       T*        k_buf_,\n                                       T*        v_buf_,\n                                       const int batch,\n                                       const int window_num,\n                                       const int window_len,\n                                       const int num_head,\n                                       const int size_per_head)\n{\n\n    T*  buf_ptr;\n    int qkv_id = blockIdx.z / batch;\n    if (qkv_id == 0) {\n        buf_ptr = q_buf_;\n    }\n    else if (qkv_id == 1) {\n        buf_ptr = k_buf_;\n    }\n    else {\n        buf_ptr = v_buf_;\n    }\n\n    const int batch_id   = blockIdx.z % batch;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int bias_idx = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    const T   bias     = ldg(bias_qkv + bias_idx);\n\n    const int input_idx =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head + bias_idx;\n    T tmp = mm_qkv[input_idx] + bias;\n\n    int target_id = (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n                    + id_in_head;\n    ;\n    buf_ptr[target_id] = tmp;\n}\n\n// for float2, size_per_head /= 2\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, 3*batch);\n// block(num_head * size_per_head)\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const float2* mm_qkv,\n                                       const float2* bias_qkv,\n                                       float2*       q_buf_,\n                                       float2*       k_buf_,\n                                       float2*       v_buf_,\n                                       const int     batch,\n                                       const int     window_num,\n                                       const int     window_len,\n                                       const int     num_head,\n                                       const int     size_per_head)\n{\n\n    float2* buf_ptr;\n    int     qkv_id = blockIdx.z / batch;\n    if (qkv_id == 0) {\n        buf_ptr = q_buf_;\n    }\n    else if (qkv_id == 1) {\n        buf_ptr = k_buf_;\n    }\n    else {\n        buf_ptr = v_buf_;\n    }\n\n    const int batch_id   = blockIdx.z % batch;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int    bias_idx = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    const float2 bias     = ldg(bias_qkv + bias_idx);\n\n    const int input_idx =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head + bias_idx;\n    float2 tmp = mm_qkv[input_idx];\n    tmp.x += bias.x;\n    tmp.y += bias.y;\n\n    int target_id = (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n                    + id_in_head;\n    ;\n    buf_ptr[target_id] = tmp;\n}\n\n// for half2, size_per_head /= 2\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, batch);\n// block(num_head * size_per_head)\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const half2* mm_qkv,\n                                       const half2* bias_qkv,\n                                       half2*       q_buf_,\n                                       half2*       k_buf_,\n                                       half2*       v_buf_,\n                                       const int    batch,\n                                       const int    window_num,\n                                       const int    window_len,\n                                       const int    num_head,\n                                       const int    size_per_head)\n{\n\n    const int batch_id   = blockIdx.z;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int input_offset =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head;\n    const int target_id =\n        (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n        + id_in_head;\n\n    int   qkv_id      = 0;\n    int   bias_idx    = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    half2 bias        = __ldg(bias_qkv + bias_idx);\n    int   input_idx   = input_offset + bias_idx;\n    half2 tmp         = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    q_buf_[target_id] = tmp;\n\n    qkv_id            = 1;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = __ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    k_buf_[target_id] = tmp;\n\n    qkv_id            = 2;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = __ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    v_buf_[target_id] = tmp;\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const __nv_bfloat162* mm_qkv,\n                                       const __nv_bfloat162* bias_qkv,\n                                       __nv_bfloat162*       q_buf_,\n                                       __nv_bfloat162*       k_buf_,\n                                       __nv_bfloat162*       v_buf_,\n                                       const int             batch,\n                                       const int             window_num,\n                                       const int             window_len,\n                                       const int             num_head,\n                                       const int             size_per_head)\n{\n\n    const int batch_id   = blockIdx.z;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int input_offset =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head;\n    const int target_id =\n        (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n        + id_in_head;\n\n    int            qkv_id    = 0;\n    int            bias_idx  = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    __nv_bfloat162 bias      = ldg(bias_qkv + bias_idx);\n    int            input_idx = input_offset + bias_idx;\n    __nv_bfloat162 tmp       = mm_qkv[input_idx];\n    tmp                      = bf16hadd2(tmp, bias);\n    q_buf_[target_id]        = tmp;\n\n    qkv_id            = 1;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = bf16hadd2(tmp, bias);\n    k_buf_[target_id] = tmp;\n\n    qkv_id            = 2;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = bf16hadd2(tmp, bias);\n    v_buf_[target_id] = tmp;\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddHead3SizeQKVBias(const T*     mm_qkv,\n                               const T*     bias_qkv,\n                               T*           q_buf_,\n                               T*           k_buf_,\n                               T*           v_buf_,\n                               const int    batch,\n                               const int    window_num,\n                               const int    window_len,\n                               const int    num_head,\n                               const int    size_per_head,\n                               cudaStream_t stream)\n{\n    if (std::is_same<T, float>::value) {\n        dim3 grid(window_len, window_num, 3 * batch);\n        dim3 block(num_head * size_per_head);\n\n        if (block.x < 1024) {\n            add_head3Size_QKV_bias<<<grid, block, 0, stream>>>(\n                mm_qkv, bias_qkv, q_buf_, k_buf_, v_buf_, batch, window_num, window_len, num_head, size_per_head);\n        }\n        else if ((block.x % 2 == 0) && (block.x / 2 < 1024)) {\n            block.x /= 2;\n            add_head3Size_QKV_bias<<<grid, block, 0, stream>>>((const float2*)mm_qkv,\n                                                               (const float2*)bias_qkv,\n                                                               (float2*)q_buf_,\n                                                               (float2*)k_buf_,\n                                                               (float2*)v_buf_,\n                                                               batch,\n                                                               window_num,\n                                                               window_len,\n                                                               num_head,\n                                                               size_per_head / 2);\n        }\n        else {\n            printf(\"[ERROR][invokeAddHead3SizeQKVBias] unsupported block.x!\\n\");\n            exit(-1);\n        }\n    }\n#ifdef ENABLE_BF16\n    else if (std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value) {\n#else\n    else if (std::is_same<T, half>::value) {\n#endif\n        dim3 grid(window_len, window_num, batch);\n        dim3 block(num_head * size_per_head / 2);\n\n        using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat16\n\n        if (block.x > 1024) {\n            printf(\"[ERROR][invokeAddHead3SizeQKVBias] block.x > 1024!\\n\");\n            exit(-1);\n        }\n\n        add_head3Size_QKV_bias<<<grid, block, 0, stream>>>((const T2*)mm_qkv,\n                                                           (const T2*)bias_qkv,\n                                                           (T2*)q_buf_,\n                                                           (T2*)k_buf_,\n                                                           (T2*)v_buf_,\n                                                           batch,\n                                                           window_num,\n                                                           window_len,\n                                                           num_head,\n                                                           size_per_head / 2);\n    }\n}\n\n#define INSTANTIATEADDHEAD3SIZEQKVBIAS(T)                                                                              \\\n    template void invokeAddHead3SizeQKVBias<T>(const T*     mm_qkv,                                                    \\\n                                               const T*     bias_qkv,                                                  \\\n                                               T*           q_buf_,                                                    \\\n                                               T*           k_buf_,                                                    \\\n                                               T*           v_buf_,                                                    \\\n                                               const int    batch,                                                     \\\n                                               const int    window_num,                                                \\\n                                               const int    window_len,                                                \\\n                                               const int    num_head,                                                  \\\n                                               const int    size_per_head,                                             \\\n                                               cudaStream_t stream)\nINSTANTIATEADDHEAD3SIZEQKVBIAS(float);\nINSTANTIATEADDHEAD3SIZEQKVBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDHEAD3SIZEQKVBIAS(__nv_bfloat16);\n#endif\n#undef INSTANTIATEADDHEAD3SIZEQKVBIAS\n\n/*******************  invokeMaskedSoftMaxWithRelPosBias  ***********************/\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T>\n__global__ void softmax_withRelPosBias_element1_kernel(T*          qk_buf,\n                                                       const T*    attn_mask,\n                                                       const T*    relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n\n    bool qual = threadIdx.x < window_len;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + threadIdx.x;\n            qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window;\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            float     mask_val =\n                (attn_mask == nullptr) ?\n                        0.0f :\n                        static_cast<float>(\n                        ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window)));\n            tmp = qk_scale * static_cast<float>(qk_buf[qk_offset]) + mask_val\n                  + static_cast<float>(ldg(relative_pos_bias + relative_pos_bias_offset));\n        }\n\n        float max_val = blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float qk_tmp  = qual ? __expf(tmp - s_max) : 0.0f;\n        float sum_val = blockReduceSum<float>(qk_tmp);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            qk_buf[qk_offset] = (T)(qk_tmp * s_mean);\n        }\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len/2 + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T2, typename T>\n__global__ void softmax_withRelPosBias_element2_kernel(T2*         qk_buf,\n                                                       const T2*   attn_mask,\n                                                       const T2*   relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n    const int window_len_2 = window_len / 2;\n    const int tidx         = threadIdx.x;\n    bool      qual         = tidx < window_len_2;\n    const T2  zero         = {T(0.0f), T(0.0f)};\n    const int bdim         = blockDim.x;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        float2           local_qk_val;\n        T2               qk_val;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + 2 * tidx;\n            qk_offset = ((blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window) / 2;\n            const int relative_pos_bias_offset =\n                ((blockIdx.y % num_head) * window_len_x_window_len + offset_in_window) / 2;\n            T2 mask_val =\n                (attn_mask == nullptr) ?\n                    zero :\n                    ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window) / 2);\n            qk_val            = qk_buf[qk_offset];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            const T2 bias_val = ldg(relative_pos_bias + relative_pos_bias_offset);\n            local_qk_val.x =\n                qk_scale * local_qk_val.x + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                qk_scale * local_qk_val.y + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n        }\n\n        float max_val = bdim <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (tidx == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n\n        float sum_val = bdim <= 32 ? warpReduceSum<float>(local_qk_val.x + local_qk_val.y) :\n                                     blockReduceSum<float>(local_qk_val.x + local_qk_val.y);\n        if (tidx == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            local_qk_val.x    = local_qk_val.x * s_mean;\n            local_qk_val.y    = local_qk_val.y * s_mean;\n            qk_val.x          = T(local_qk_val.x);\n            qk_val.y          = T(local_qk_val.y);\n            qk_buf[qk_offset] = qk_val;\n        }\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len/4 + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T4, typename T>\n__global__ void softmax_withRelPosBias_element4_kernel(T4*         qk_buf,\n                                                       const T4*   attn_mask,\n                                                       const T4*   relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n    const int window_len_4 = window_len / 4;\n    const int tidx         = threadIdx.x;\n    bool      qual         = tidx < window_len_4;\n    const T4  zero         = {T(0.0f), T(0.0f), T(0.0f), T(0.0f)};\n    const int bdim         = blockDim.x;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        float4           local_qk_val;\n        T4               qk_val;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + 4 * tidx;\n            qk_offset = ((blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window) / 4;\n            const int relative_pos_bias_offset =\n                ((blockIdx.y % num_head) * window_len_x_window_len + offset_in_window) / 4;\n            T4 mask_val       = (attn_mask == nullptr) ?\n                                    zero :\n                                    attn_mask[((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window) / 4];\n            qk_val            = qk_buf[qk_offset];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            local_qk_val.z    = static_cast<float>(qk_val.z);\n            local_qk_val.w    = static_cast<float>(qk_val.w);\n            const T4 bias_val = relative_pos_bias[relative_pos_bias_offset];\n            local_qk_val.x =\n                qk_scale * local_qk_val.x + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                qk_scale * local_qk_val.y + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            local_qk_val.z =\n                qk_scale * local_qk_val.z + static_cast<float>(mask_val.z) + static_cast<float>(bias_val.z);\n            local_qk_val.w =\n                qk_scale * local_qk_val.w + static_cast<float>(mask_val.w) + static_cast<float>(bias_val.w);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n            tmp = tmp > local_qk_val.z ? tmp : local_qk_val.z;\n            tmp = tmp > local_qk_val.w ? tmp : local_qk_val.w;\n        }\n\n        float max_val = bdim <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (tidx == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n        local_qk_val.z = qual ? __expf(local_qk_val.z - s_max) : 0.0f;\n        local_qk_val.w = qual ? __expf(local_qk_val.w - s_max) : 0.0f;\n\n        float sum_val = bdim <= 32 ?\n                            warpReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w) :\n                            blockReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w);\n        if (tidx == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            local_qk_val.x    = local_qk_val.x * s_mean;\n            local_qk_val.y    = local_qk_val.y * s_mean;\n            local_qk_val.z    = local_qk_val.z * s_mean;\n            local_qk_val.w    = local_qk_val.w * s_mean;\n            qk_val.x          = T(local_qk_val.x);\n            qk_val.y          = T(local_qk_val.y);\n            qk_val.z          = T(local_qk_val.z);\n            qk_val.w          = T(local_qk_val.w);\n            qk_buf[qk_offset] = qk_val;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeMaskedSoftMaxWithRelPosBias(T*           qk_buf,\n                                       const T*     attn_mask,\n                                       const T*     relative_pos_bias,\n                                       const int    batch_size,\n                                       const int    num_head,\n                                       const int    window_num,\n                                       const int    window_len,\n                                       float        qk_scale,\n                                       cudaStream_t stream)\n{\n    const int word_per_thread = 1;\n    dim3      grid((window_len + word_per_thread - 1) / word_per_thread, window_num * num_head, batch_size);\n    if ((window_len % 4 == 0) && window_len / 4 >= 32) {\n        dim3 block((window_len / 4 + 31) / 32 * 32);\n        if (std::is_same<T, float>::value) {\n            softmax_withRelPosBias_element4_kernel<float4, float>\n                <<<grid, block, 0, stream>>>((float4*)qk_buf,\n                                             (const float4*)attn_mask,\n                                             (const float4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_withRelPosBias_element4_kernel<half4, half>\n                <<<grid, block, 0, stream>>>((half4*)qk_buf,\n                                             (const half4*)attn_mask,\n                                             (const half4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n#ifdef ENABLE_BF16\n        else {\n            dim3 block((window_len + 31) / 32 * 32);\n            softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                               attn_mask,\n                                                                               relative_pos_bias,\n                                                                               batch_size,\n                                                                               num_head,\n                                                                               window_num,\n                                                                               window_len,\n                                                                               window_len * window_len,\n                                                                               qk_scale);\n        }\n#endif\n    }\n    else if (window_len % 2 == 0) {\n        dim3 block((window_len / 2 + 31) / 32 * 32);\n        if (std::is_same<T, float>::value) {\n            softmax_withRelPosBias_element2_kernel<float2, float>\n                <<<grid, block, 0, stream>>>((float2*)qk_buf,\n                                             (const float2*)attn_mask,\n                                             (const float2*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_withRelPosBias_element2_kernel<half2, half>\n                <<<grid, block, 0, stream>>>((half2*)qk_buf,\n                                             (const half2*)attn_mask,\n                                             (const half2*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n#ifdef ENABLE_BF16\n        else {\n            dim3 block((window_len + 31) / 32 * 32);\n            softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                               attn_mask,\n                                                                               relative_pos_bias,\n                                                                               batch_size,\n                                                                               num_head,\n                                                                               window_num,\n                                                                               window_len,\n                                                                               window_len * window_len,\n                                                                               qk_scale);\n        }\n#endif\n    }\n    else {\n        dim3 block((window_len + 31) / 32 * 32);\n        softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                           attn_mask,\n                                                                           relative_pos_bias,\n                                                                           batch_size,\n                                                                           num_head,\n                                                                           window_num,\n                                                                           window_len,\n                                                                           window_len * window_len,\n                                                                           qk_scale);\n    }\n}\n\n#define INSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(T)                                                                      \\\n    template void invokeMaskedSoftMaxWithRelPosBias(T*           qk_buf,                                               \\\n                                                    const T*     attn_mask,                                            \\\n                                                    const T*     relative_pos_bias,                                    \\\n                                                    const int    batch_size,                                           \\\n                                                    const int    num_head,                                             \\\n                                                    const int    window_num,                                           \\\n                                                    const int    window_len,                                           \\\n                                                    const float  qk_scale,                                             \\\n                                                    cudaStream_t stream)\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(float);\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(__nv_bfloat16);\n#endif\n#undef INSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS\n\ntemplate<typename T>\n__global__ void transpose_attentions(\n    T* attentions_out, const T* attentions_in, size_t batch_size, size_t num_layers, size_t num_heads, size_t seq_len)\n{\n    // attentions_in  shape [B, H, S, S]\n    // attentions_out shape [B, L, H, S, S].\n    // Note that we write the L dimension as if it was index 0.\n    // In reality, the pointer has already been shifted to point to the correct layer.\n\n    const auto batch_idx = blockIdx.x;\n    const auto head_idx  = blockIdx.y;\n\n    const auto dst_offset = (batch_idx * num_layers * num_heads + head_idx) * seq_len * seq_len;\n    const auto src_offset = (batch_idx * num_heads + head_idx) * seq_len * seq_len;\n\n    for (auto x = threadIdx.x; x < seq_len * seq_len; x += blockDim.x) {\n        attentions_out[dst_offset + x] = attentions_in[src_offset + x];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAttentions(Tensor& attentions_out, const Tensor& attentions_in, cudaStream_t stream)\n{\n    const size_t batch_size = attentions_in.shape[0];\n    const size_t num_heads  = attentions_in.shape[1];\n    const size_t seq_len    = attentions_in.shape[2];\n    const size_t num_layers = attentions_out.shape[1];\n\n    const dim3 gridSize(batch_size, num_heads);\n    const dim3 blockSize(512);\n\n    transpose_attentions<<<gridSize, blockSize, 0, stream>>>(\n        attentions_out.getPtr<T>(), attentions_in.getPtr<const T>(), batch_size, num_layers, num_heads, seq_len);\n}\n\n#define INSTANTIATETRANSPOSEATTENTIONS(T)                                                                              \\\n    template void invokeTransposeAttentions<T>(                                                                        \\\n        Tensor & attentions_out, const Tensor& attentions_in, cudaStream_t stream)\nINSTANTIATETRANSPOSEATTENTIONS(float);\nINSTANTIATETRANSPOSEATTENTIONS(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEATTENTIONS(__nv_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEATTENTIONS\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/decoder_masked_multihead_attention_utils.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/unfused_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nnamespace fastertransformer {\n\n__inline__ __device__ int target_index(int id1, int id2, int id3, int id4, int dim_1, int dim_2, int dim_3, int dim_4)\n{\n    return id1 * (dim_2 * dim_3 * dim_4) + id3 * (dim_2 * dim_4) + id2 * dim_4 + id4;\n}\n\ntemplate<typename T>\n__global__ void addQKVBiasIA3Transpose(T* q_out,\n                                       T* k_out,\n                                       T* v_out,\n                                       const T* __restrict q_in,\n                                       const T* __restrict bias_q,\n                                       const T* __restrict k_in,\n                                       const T* __restrict bias_k,\n                                       const T* __restrict v_in,\n                                       const T* __restrict bias_v,\n                                       const int* ia3_tasks,\n                                       const T*   ia3_key_weights,\n                                       const T*   ia3_value_weights,\n                                       const int  batch_size,\n                                       const int  seq_len,\n                                       const int  head_num,\n                                       const int  size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        T q              = ldg(&q_in[src_id]);\n        q_out[target_id] = add(q, ldg(&bias_q[col_id]));\n\n        T k = add(ldg(&k_in[src_id]), ldg(&bias_k[col_id]));\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + col_id];\n        }\n        k_out[target_id] = k;\n\n        T v = add(ldg(&v_in[src_id]), ldg(&bias_v[col_id]));\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + col_id];\n        }\n        v_out[target_id] = v;\n    }\n}\n\ntemplate<typename T>\n__global__ void QKVIA3Transpose(T* q_out,\n                                T* k_out,\n                                T* v_out,\n                                const T* __restrict q_in,\n                                const T* __restrict k_in,\n                                const T* __restrict v_in,\n                                const int* ia3_tasks,\n                                const T* __restrict ia3_key_weights,\n                                const T* __restrict ia3_value_weights,\n                                const int batch_size,\n                                const int seq_len,\n                                const int head_num,\n                                const int size_per_head)\n{\n    const int n        = head_num * size_per_head;\n    const int batch_id = blockIdx.x;\n    const int word_id  = blockIdx.y;\n    const int row_id   = batch_id * seq_len + word_id;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n\n    for (int col_id = threadIdx.x; col_id < n; col_id += blockDim.x) {\n        const int head_id   = col_id / size_per_head;\n        const int size_id   = col_id % size_per_head;\n        const int target_id = batch_id * (head_num * seq_len * size_per_head) + head_id * seq_len * size_per_head\n                              + word_id * size_per_head + size_id;\n        const int src_id = row_id * n + col_id;\n\n        q_out[target_id] = ldg(&q_in[src_id]);\n\n        T k = ldg(&k_in[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + col_id];\n        }\n        k_out[target_id] = k;\n\n        T v = ldg(&v_in[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + col_id];\n        }\n        v_out[target_id] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasIA3Transpose(T*           q_buf,\n                                  T*           k_buf,\n                                  T*           v_buf,\n                                  T*           Q,\n                                  const T*     bias_Q,\n                                  T*           K,\n                                  const T*     bias_K,\n                                  T*           V,\n                                  const T*     bias_V,\n                                  const int    batch_size,\n                                  const int    seq_len,\n                                  const int    head_num,\n                                  const int    size_per_head,\n                                  const int*   ia3_tasks,\n                                  const T*     ia3_key_weights,\n                                  const T*     ia3_value_weights,\n                                  hipStream_t stream)\n{\n    const int k = head_num * size_per_head;\n    dim3      grid(batch_size, seq_len);\n    bool      is_add_bias = bias_Q != nullptr;\n    if (sizeof(T) == 4 || k % 2 != 0) {\n        dim3 block(min(k, 512));\n        if (is_add_bias) {\n            addQKVBiasIA3Transpose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                                  k_buf,\n                                                                  v_buf,\n                                                                  Q,\n                                                                  bias_Q,\n                                                                  K,\n                                                                  bias_K,\n                                                                  V,\n                                                                  bias_V,\n                                                                  ia3_tasks,\n                                                                  ia3_key_weights,\n                                                                  ia3_value_weights,\n                                                                  batch_size,\n                                                                  seq_len,\n                                                                  head_num,\n                                                                  size_per_head);\n        }\n        else {\n            QKVIA3Transpose<T><<<grid, block, 0, stream>>>(q_buf,\n                                                           k_buf,\n                                                           v_buf,\n                                                           Q,\n                                                           K,\n                                                           V,\n                                                           ia3_tasks,\n                                                           ia3_key_weights,\n                                                           ia3_value_weights,\n                                                           batch_size,\n                                                           seq_len,\n                                                           head_num,\n                                                           size_per_head);\n        }\n        sync_check_cuda_error();\n    }\n    else {\n        using T2 = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n        dim3 block(min(k / 2, 512));\n        if (is_add_bias) {\n            addQKVBiasIA3Transpose<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                                   (T2*)k_buf,\n                                                                   (T2*)v_buf,\n                                                                   (const T2*)Q,\n                                                                   (const T2*)bias_Q,\n                                                                   (const T2*)K,\n                                                                   (const T2*)bias_K,\n                                                                   (const T2*)V,\n                                                                   (const T2*)bias_V,\n                                                                   ia3_tasks,\n                                                                   (const T2*)ia3_key_weights,\n                                                                   (const T2*)ia3_value_weights,\n                                                                   batch_size,\n                                                                   seq_len,\n                                                                   head_num,\n                                                                   size_per_head / 2);\n        }\n        else {\n            QKVIA3Transpose<T2><<<grid, block, 0, stream>>>((T2*)q_buf,\n                                                            (T2*)k_buf,\n                                                            (T2*)v_buf,\n                                                            (const T2*)Q,\n                                                            (const T2*)K,\n                                                            (const T2*)V,\n                                                            ia3_tasks,\n                                                            (const T2*)ia3_key_weights,\n                                                            (const T2*)ia3_value_weights,\n                                                            batch_size,\n                                                            seq_len,\n                                                            head_num,\n                                                            size_per_head / 2);\n        }\n        sync_check_cuda_error();\n    }\n}\n\n#define INSTANTIATEADDQKVBIASIA3TRANSPOSE(T)                                                                           \\\n    template void invokeAddQKVBiasIA3Transpose(T*           q_buf,                                                     \\\n                                               T*           k_buf,                                                     \\\n                                               T*           v_buf,                                                     \\\n                                               T*           Q,                                                         \\\n                                               const T*     bias_Q,                                                    \\\n                                               T*           K,                                                         \\\n                                               const T*     bias_K,                                                    \\\n                                               T*           V,                                                         \\\n                                               const T*     bias_V,                                                    \\\n                                               const int    batch_size,                                                \\\n                                               const int    seq_len,                                                   \\\n                                               const int    head_num,                                                  \\\n                                               const int    size_per_head,                                             \\\n                                               const int*   ia3_tasks,                                                 \\\n                                               const T*     ia3_key_weights,                                           \\\n                                               const T*     ia3_value_weights,                                         \\\n                                               hipStream_t stream)\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(float);\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKVBIASIA3TRANSPOSE(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDQKVBIASTRANSPOSE\n\ntemplate<typename T, typename T_IN, int ITEMS_PER_THREAD>\n__global__ void softmax_kernel(T*          attn_score,\n                               const T_IN* qk,\n                               const T*    attn_mask,\n                               const T*    linear_bias_slopes,\n                               const int   batch_size,\n                               const int   head_num,\n                               const int   q_length,\n                               const int   k_length,\n                               const float qk_scale)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    const int64_t bi = blockIdx.y;  // Batch index.\n    const int64_t hi = blockIdx.z;  // Head index.\n\n    __shared__ float s_mean, s_max;\n\n    const float linear_bias_slope = linear_bias_slopes != nullptr ? (float)linear_bias_slopes[hi] : 0.0f;\n\n    // Loop along with Q dimension.\n    for (int64_t qi = blockIdx.x; qi < q_length; qi += gridDim.x) {\n\n        float data[ITEMS_PER_THREAD];\n        int64_t   qk_offset;\n        float local_max = -1e20f;\n\n        // Loop along with K dimension.\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            int64_t ki    = blockDim.x * i + threadIdx.x;  // Index of K dimension.\n            qk_offset = ((bi * head_num + hi) * q_length + qi) * k_length + ki;\n\n            float qk_val  = static_cast<float>(qk[qk_offset]);\n            float qk_bias = 0.0f;\n\n            if (linear_bias_slopes != nullptr) {\n                // We don't handle the upper diagonal (ki > qi) separately, whose values\n                // are negligible due to the negative infinity mask. And it matches with\n                // the HF's implementation.\n                qk_bias += static_cast<float>(linear_bias_slope * (ki - qi));\n            }\n\n            int64_t   mask_offset = (bi * q_length + qi) * k_length + ki;\n            float mask_val    = static_cast<float>(ldg(&attn_mask[mask_offset]));\n            qk_bias += (1.0f - mask_val) * -10000.0f;\n\n            data[i]   = qk_scale * qk_val + qk_bias;\n            local_max = fmax(local_max, data[i]);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0;\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            data[i] = __expf(data[i] - s_max);\n            local_sum += data[i];\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int64_t i = 0; blockDim.x * i + threadIdx.x < k_length; i++) {\n            qk_offset             = ((bi * head_num + hi) * q_length + qi) * k_length + blockDim.x * i + threadIdx.x;\n            attn_score[qk_offset] = (T)(data[i] * s_mean);\n        }\n    }\n}\n\ntemplate<typename T, int ITEMS_PER_THREAD>\n__global__ void softmax_kernel_h2(T*        attn_score,\n                                  const T*  qk_buf,\n                                  const T*  attn_mask,\n                                  const T*  linear_bias_slopes,\n                                  const int batch_size,\n                                  const int head_num,\n                                  const int q_length,\n                                  const int k_length,\n                                  const T   qk_scale)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    using T2 = typename TypeConverter<T>::Type;\n\n    T2*       attn_score_h2 = reinterpret_cast<T2*>(attn_score);\n    const T2* qk_buf_h2     = reinterpret_cast<const T2*>(qk_buf);\n    const T2* attn_mask_h2  = reinterpret_cast<const T2*>(attn_mask);\n\n    const int bi = blockIdx.y;  // Batch index\n    const int hi = blockIdx.z;  // Head index.\n\n    __shared__ float s_mean, s_max;\n\n    // Constant values that will be used repeately in the q/k loop.\n    const T2 ONE       = cuda_cast<T2>(1.0f);\n    const T2 ZERO      = cuda_cast<T2>(0.0f);\n    const T2 NEG_INFTY = cuda_cast<T2>(-10000.0f);\n\n    // The normalization factor of QK.\n    const T2 qk_scale_h2 = cuda_cast<T2>(qk_scale);\n    // The slope of a linear position bias of the current attention head.\n    const T2 linear_bias_slope = linear_bias_slopes != nullptr ? cuda_cast<T2>(linear_bias_slopes[hi]) : ZERO;\n\n    // Loop over q dimension.\n    for (int qi = blockIdx.x; qi < q_length; qi += gridDim.x) {\n        T2    data[ITEMS_PER_THREAD];\n        int   qk_offset;\n        float local_max = -1e20f;\n\n        // Loop over k dimension.\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            // The half of the index of k dimension. We will use the elements at {2 * ki, 2 * ki + 1}.\n            int ki          = blockDim.x * i + threadIdx.x;\n            qk_offset       = ((bi * head_num + hi) * q_length + qi) * (k_length / 2) + ki;\n            int mask_offset = (bi * q_length + qi) * (k_length / 2) + ki;\n\n            // The value of QK^T matrix at (qi, ki).\n            T2 qk = qk_buf_h2[qk_offset];\n            // The bias value to the position (qi, ki) including both mask and positional bias.\n            T2 qk_bias = ZERO;\n\n            if (linear_bias_slopes != nullptr) {\n                // The position bias depends on the distance between qi/ki and is zero if qi >= 2*ki\n                // or qi >= 2*ki+1. For T2 vectorization, we should handle every two elements along\n                // with k-dim simultaneously. To do this, we check qi / 2 > ki at ones instead of\n                // qi >= 2*ki or 2*ki+1. It works because an diagonal element for an odd qi will be\n                // zero due to slope * (qi - 2*ki+1) = 0. Thus, we don't handle the upper diagonal\n                // separately, whose values are negligible due to the negative infinity mask.\n                T2 dist(2.0f * ki - qi, 2.0f * ki + 1 - qi);\n                qk_bias = hadd2<T2>(qk_bias, hmul2<T2>(linear_bias_slope, dist));\n            }\n\n            T2 mask_val = ldg(&attn_mask_h2[mask_offset]);\n            qk_bias     = hadd2<T2>(qk_bias, hmul2<T2>(hsub2<T2>(ONE, mask_val), NEG_INFTY));\n\n            data[i]   = hadd2<T2>(hmul2<T2>(qk, qk_scale_h2), qk_bias);\n            local_max = fmax(local_max, fmax((float)data[i].x, (float)data[i].y));\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float local_sum = 0.0f;\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            data[i] = hexp2<T2>(hsub2<T2>(data[i], cuda_cast<T2>(s_max)));\n            local_sum += (float)(data[i].x + data[i].y);\n        }\n\n        float sum_val = blockDim.x <= 32 ? warpReduceSum(local_sum) : blockReduceSum<float>(local_sum);\n\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < (k_length / 2) && i < ITEMS_PER_THREAD; i++) {\n            qk_offset = ((bi * head_num + hi) * q_length + qi) * (k_length / 2) + blockDim.x * i + threadIdx.x;\n            attn_score_h2[qk_offset] = hmul2<T2>(data[i], cuda_cast<T2>(s_mean));\n        }\n    }\n}\n\ntemplate<typename T, int K_ITEMS_PER_THREAD, int Q_ITEMS_PER_THREAD>\n__global__ void softmax_kernel_h2_v2(T*        attn_score,\n                                     const T*  qk_buf,\n                                     const T*  attn_mask,\n                                     const T*  linear_bias_slopes,\n                                     const int batch_size,\n                                     const int head_num,\n                                     const int q_length,\n                                     const int k_length,\n                                     const T   scalar)\n{\n    // attn_score, [batch_size, num_heads, q_length, k_length]\n    // qk, [batch_size, num_heads, q_length, k_length]\n    // attn_mask, [batch_size, q_length, k_length]\n    // linear_bias_slopes, [num_heads]\n\n    using T2 = typename TypeConverter<T>::Type;\n\n    // QK^T matrix of shape (batch_size, head_num, q_length, k_length / 2)\n    T2*       attn_score_h2 = reinterpret_cast<T2*>(attn_score);\n    const T2* qk_buf_h2     = reinterpret_cast<const T2*>(qk_buf);\n    const T2* attn_mask_h2  = reinterpret_cast<const T2*>(attn_mask);\n\n    const int bi = blockIdx.y;  // Batch index\n    const int hi = blockIdx.z;  // Head index.\n\n    // Constant values that will be used repeately in the q/k loop.\n    const T2 ONE       = cuda_cast<T2>(1.0f);\n    const T2 ZERO      = cuda_cast<T2>(0.0f);\n    const T2 NEG_INFTY = cuda_cast<T2>(-10000.0f);\n\n    // The normalization factor of QK.\n    const T2 qk_scale = cuda_cast<T2>(scalar);\n    // The slope of a linear position bias of the current attention head.\n    const T2 linear_bias_slope = linear_bias_slopes != nullptr ? cuda_cast<T2>(linear_bias_slopes[hi]) : ZERO;\n\n    __shared__ float s_sum[Q_ITEMS_PER_THREAD], s_max[Q_ITEMS_PER_THREAD];\n\n    // Loop over q dimension.\n    for (int qi = blockIdx.x; qi < q_length; qi += gridDim.x * Q_ITEMS_PER_THREAD) {\n        T2 data[Q_ITEMS_PER_THREAD][K_ITEMS_PER_THREAD];\n\n        int qk_offset[Q_ITEMS_PER_THREAD];\n\n        float local_max[Q_ITEMS_PER_THREAD];\n#pragma unroll\n        for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n            local_max[j] = -1e20f;\n        }\n\n        // Loop over k dimension.\n        const int Q_ITEMS = min((q_length - qi + gridDim.x - 1) / gridDim.x, Q_ITEMS_PER_THREAD);\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n            // The half of the index of k dimension. We will use the elements at {2 * ki, 2 * ki + 1}.\n            int ki = blockDim.x * i + threadIdx.x;\n\n            int mask_offset[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk_offset[j]   = ((bi * head_num + hi) * q_length + qi + j * gridDim.x) * (k_length / 2) + ki;\n                mask_offset[j] = (bi * q_length + qi + j * gridDim.x) * (k_length / 2) + ki;\n            }\n\n            T2 mask_val[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                mask_val[j] = ldg(&attn_mask_h2[mask_offset[j]]);\n            }\n\n            T2 qk[Q_ITEMS_PER_THREAD];\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk[j] = qk_buf_h2[qk_offset[j]];\n            }\n\n            T2 pos_bias[Q_ITEMS_PER_THREAD];\n            if (linear_bias_slopes != nullptr) {\n#pragma unroll\n                for (int j = 0; j < Q_ITEMS; j++) {\n                    // The position bias depends on the distance between qi/ki and is zero if qi >= 2*ki\n                    // or qi >= 2*ki+1. For T2 vectorization, we should handle every two elements along\n                    // with k-dim simultaneously. To do this, we check qi / 2 > ki at ones instead of\n                    // qi >= 2*ki or 2*ki+1. It works because an diagonal element for an odd qi will be\n                    // zero due to slope * (qi - 2*ki+1) = 0. Thus, we don't handle the upper diagonal\n                    // separately, whose values are negligible due to the negative infinity mask.\n                    int qidx = qi + j * gridDim.x;\n                    T2  dist(2.0f * ki - qidx, 2.0f * ki + 1 - qidx);\n                    pos_bias[j] = hmul2<T2>(linear_bias_slope, dist);\n                }\n            }\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                mask_val[j] = hmul2<T2>(hsub2<T2>(ONE, mask_val[j]), NEG_INFTY);\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                T2 val = hadd2<T2>(hmul2<T2>(qk_scale, qk[j]), mask_val[j]);\n                if (linear_bias_slopes != nullptr) {\n                    val = hadd2<T2>(val, pos_bias[j]);\n                }\n                data[j][i]   = val;\n                local_max[j] = fmax(local_max[j], fmax((float)data[j][i].x, (float)data[j][i].y));\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceMaxV2<float, Q_ITEMS_PER_THREAD>(local_max);\n        }\n        else {\n            blockReduceMaxV2<float, Q_ITEMS_PER_THREAD>(local_max);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n                s_max[j] = local_max[j];\n            }\n        }\n        __syncthreads();\n\n        float local_sum[Q_ITEMS_PER_THREAD];\n#pragma unroll\n        for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n            local_sum[j] = {0.f};\n        }\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; ++j) {\n                data[j][i] = hexp2<T2>(hsub2<T2>(data[j][i], cuda_cast<T2>(s_max[j])));\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                local_sum[j] += (float)(data[j][i].x + data[j][i].y);\n            }\n        }\n\n        if (blockDim.x <= 32) {\n            warpReduceSumV2<float, Q_ITEMS_PER_THREAD>(local_sum);\n        }\n        else {\n            blockReduceSumV2<float, Q_ITEMS_PER_THREAD>(local_sum);\n        }\n\n        if (threadIdx.x == 0) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS_PER_THREAD; j++) {\n                s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);\n            }\n        }\n        __syncthreads();\n\n        for (int i = 0; blockDim.x * i + threadIdx.x < k_length / 2 && i < K_ITEMS_PER_THREAD; ++i) {\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                qk_offset[j] = ((bi * head_num + hi) * q_length + qi + j * gridDim.x) * (k_length / 2) + blockDim.x * i\n                               + threadIdx.x;\n            }\n\n#pragma unroll\n            for (int j = 0; j < Q_ITEMS; j++) {\n                attn_score_h2[qk_offset[j]] = hmul2<T2>(data[j][i], cuda_cast<T2>(s_sum[j]));\n            }\n        }\n    }\n}\n\n#define LAUNCH_MAKSED_SOFTMAX_(T_, ITEMS_PER_THREAD)                                                                   \\\n    block.x /= ITEMS_PER_THREAD;                                                                                       \\\n    block.x = (block.x + 31) / 32 * 32;                                                                                \\\n    assert(block.x <= 1024);                                                                                           \\\n    if (is_half2) {                                                                                                    \\\n        if (grid.x % 4 == 0) {                                                                                         \\\n            grid.x /= 4;                                                                                               \\\n            softmax_kernel_h2_v2<T_, ITEMS_PER_THREAD, 4>                                                              \\\n                <<<grid, block, 0, stream>>>((T_*)param.attention_score,                                               \\\n                                             (const T_*)param.qk,                                                      \\\n                                             (const T_*)param.attention_mask,                                          \\\n                                             (const T_*)param.linear_bias_slopes,                                      \\\n                                             param.batch_size,                                                         \\\n                                             param.num_heads,                                                          \\\n                                             param.q_length,                                                           \\\n                                             param.k_length,                                                           \\\n                                             (const T_)param.qk_scale);                                                \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            softmax_kernel_h2<T_, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>((T_*)param.attention_score,            \\\n                                                                                (const T_*)param.qk,                   \\\n                                                                                (const T_*)param.attention_mask,       \\\n                                                                                (const T_*)param.linear_bias_slopes,   \\\n                                                                                param.batch_size,                      \\\n                                                                                param.num_heads,                       \\\n                                                                                param.q_length,                        \\\n                                                                                param.k_length,                        \\\n                                                                                (const T_)param.qk_scale);             \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        softmax_kernel<T, T_IN, ITEMS_PER_THREAD><<<grid, block, 0, stream>>>(param.attention_score,                   \\\n                                                                              param.qk,                                \\\n                                                                              param.attention_mask,                    \\\n                                                                              param.linear_bias_slopes,                \\\n                                                                              param.batch_size,                        \\\n                                                                              param.num_heads,                         \\\n                                                                              param.q_length,                          \\\n                                                                              param.k_length,                          \\\n                                                                              param.qk_scale);                         \\\n    }\n\n#define LAUNCH_MAKSED_SOFTMAX(ITEMS_PER_THREAD) LAUNCH_MAKSED_SOFTMAX_(half, ITEMS_PER_THREAD)\n\ntemplate<typename T, typename T_IN>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<T, T_IN>& param, hipStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX(4)\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX(2)\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX(1)\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\n\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<float, float>& param, hipStream_t stream);\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<half, float>& param, hipStream_t stream);\ntemplate void invokeMaskedSoftmax(MaskedSoftmaxParam<half, half>& param, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate<>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<__hip_bfloat16, float>& param, hipStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    using T    = __hip_bfloat16;\n    using T_IN = float;\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 4);\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 2);\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 1);\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\ntemplate<>\nvoid invokeMaskedSoftmax(MaskedSoftmaxParam<__hip_bfloat16, __hip_bfloat16>& param, hipStream_t stream)\n{\n    // attention_score,    (batch_size, head_num, q_length, k_length), softmax output.\n    // qk,                 (batch_size, head_num, q_length, k_length), QK^T.\n    // attention_mask,     (batch_size, q_length, k_length), attention mask.\n    // linear_bias_slopes, (head_num,) the slopes of the linear position bias.\n\n    using T    = __hip_bfloat16;\n    using T_IN = __hip_bfloat16;\n\n    dim3 grid(param.q_length, param.batch_size, param.num_heads);\n    if (param.batch_size * param.num_heads > 360) {\n        grid.x = ceil(float(param.q_length) / 32.0f);\n    }\n\n    bool is_half2 = sizeof(T) == 2 && sizeof(T_IN) == 2 && param.k_length % 2 == 0;\n    dim3 block((param.k_length / (is_half2 ? 2 : 1) + 31) / 32 * 32);\n\n    if (block.x > 2048 && block.x <= 4096) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 4);\n    }\n    else if (block.x > 1024) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 2);\n    }\n    else if (block.x > 0) {\n        LAUNCH_MAKSED_SOFTMAX_(__hip_bfloat16, 1);\n    }\n    else {\n        FT_CHECK(param.k_length <= 4096);\n    }\n}\n\n#endif\n\n#undef LAUNCH_MAKSED_SOFTMAX\n#undef LAUNCH_MAKSED_SOFTMAX_\n\ntemplate<typename T>\n__global__ void transpose(const T*     src,\n                          T*           dst,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* scale,\n                          int          int8_mode)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    int batch_id = tid / (head_num * seq_len * size_per_head);\n    int head_id  = (tid % (head_num * seq_len * size_per_head)) / (seq_len * size_per_head);\n    int seq_id   = (tid % (seq_len * size_per_head)) / size_per_head;\n    int id       = tid % size_per_head;\n\n    int target_id = target_index(batch_id, head_id, seq_id, id, batch_size, head_num, seq_len, size_per_head);\n\n    if (int8_mode == 2) {\n        using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n        using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n\n        const Float_Packed_T scale_val = cuda_cast<Float_Packed_T>(*scale);\n        reinterpret_cast<Int8_Packed_T*>(dst)[target_id] =\n            cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(src[tid]) * scale_val);\n    }\n    else {\n        dst[target_id] = src[tid];\n    }\n}\n\ntemplate<>\n__global__ void transpose(const float* src,\n                          float*       dst,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* scale,\n                          int          int8_mode)\n{\n    int batch_id = blockIdx.x / (head_num * seq_len);\n    int seq_id   = blockIdx.x % seq_len;\n    int head_id  = (blockIdx.x % (head_num * seq_len)) / seq_len;\n\n    const int target_id = batch_id * (head_num * seq_len * size_per_head) + seq_id * head_num * size_per_head\n                          + head_id * size_per_head + threadIdx.x;\n    const int src_id = blockIdx.x * size_per_head + threadIdx.x;\n\n    if (int8_mode == 2) {\n        const float scale_val                     = *scale;\n        reinterpret_cast<int8_t*>(dst)[target_id] = cuda_cast<int8_t>(src[src_id] * scale_val);\n    }\n    else {\n        dst[target_id] = src[src_id];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeQKV(T*           dst,\n                        T*           src,\n                        const int    batch_size,\n                        const int    seq_len,\n                        const int    head_num,\n                        const int    size_per_head,\n                        const float* scale,\n                        const int    int8_mode,\n                        hipStream_t stream)\n{\n    dim3 grid, block;\n    if (sizeof(T) == 2) {\n        int seq_per_block = 1;\n        grid.x            = batch_size * head_num * seq_len / seq_per_block;\n        while (seq_per_block < 4 && grid.x % 2 == 0) {\n            grid.x /= 2;\n            seq_per_block *= 2;\n        }\n\n        FT_CHECK(grid.x * seq_per_block == (size_t)batch_size * head_num * seq_len);\n\n        if (seq_per_block * size_per_head % 2 == 0) {\n            block.x = seq_per_block * size_per_head / 2;\n            if (std::is_same<T, half>::value) {\n                transpose<half2><<<grid, block, 0, stream>>>(\n                    (half2*)src, (half2*)dst, batch_size, seq_len, head_num, size_per_head / 2, scale, int8_mode);\n            }\n#ifdef ENABLE_BF16\n            else {\n                transpose<__hip_bfloat162><<<grid, block, 0, stream>>>((__hip_bfloat162*)src,\n                                                                      (__hip_bfloat162*)dst,\n                                                                      batch_size,\n                                                                      seq_len,\n                                                                      head_num,\n                                                                      size_per_head / 2,\n                                                                      scale,\n                                                                      int8_mode);\n            }\n#endif\n        }\n        else {\n            block.x = seq_per_block * size_per_head;\n            transpose<T>\n                <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head, scale, int8_mode);\n        }\n    }\n    else {\n        const int seq_per_block = 1;\n        grid.x                  = batch_size * head_num * seq_len / seq_per_block;\n        block.x                 = seq_per_block * size_per_head;\n        transpose<T>\n            <<<grid, block, 0, stream>>>(src, dst, batch_size, seq_len, head_num, size_per_head, scale, int8_mode);\n    }\n}\n\n#define INSTANTIATETRANSPOSEQKV(T)                                                                                     \\\n    template void invokeTransposeQKV(T*           src,                                                                 \\\n                                     T*           dst,                                                                 \\\n                                     const int    batch_size,                                                          \\\n                                     const int    seq_len,                                                             \\\n                                     const int    head_num,                                                            \\\n                                     const int    size_per_head,                                                       \\\n                                     const float* scale,                                                               \\\n                                     const int    int8_mode,                                                           \\\n                                     hipStream_t stream)\nINSTANTIATETRANSPOSEQKV(float);\nINSTANTIATETRANSPOSEQKV(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEQKV(__hip_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEQKV\n\ntemplate<typename T>\n__global__ void add_QKV_bias_rebuild_padding_ia3(const T*   Q,\n                                                 const T*   bias_Q,\n                                                 const T*   K,\n                                                 const T*   bias_K,\n                                                 const T*   V,\n                                                 const T*   bias_V,\n                                                 T*         q_buf_,\n                                                 T*         k_buf_,\n                                                 T*         v_buf_,\n                                                 const int* ia3_tasks,\n                                                 const T*   ia3_key_weights,\n                                                 const T*   ia3_value_weights,\n                                                 const int  batch_size,\n                                                 const int  seq_len,\n                                                 const int  head_num,\n                                                 const int  size_per_head,\n                                                 const int* mask_offset)\n{\n    const int bid = blockIdx.x;\n\n    const int tgt_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int tgt_seq_id   = (bid + mask_offset[bid]) % seq_len;\n    const int n            = head_num * size_per_head;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[tgt_batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        const int tgt_head_id   = idx / size_per_head;\n        const int tgt_hidden_id = idx % size_per_head;\n\n        const int src_id = bid * n + idx;\n        const int tgt_id = tgt_batch_id * head_num * seq_len * size_per_head + tgt_head_id * seq_len * size_per_head\n                           + tgt_seq_id * size_per_head + tgt_hidden_id;\n\n        q_buf_[tgt_id] = add(ldg(&Q[src_id]), ldg(&bias_Q[idx]));\n\n        T k = ldg(&K[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + idx];\n        }\n        k_buf_[tgt_id] = add(k, ldg(&bias_K[idx]));\n\n        T v = ldg(&V[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + idx];\n        }\n        v_buf_[tgt_id] = add(v, ldg(&bias_V[idx]));\n    }\n}\n\ntemplate<typename T>\n__global__ void rebuild_padding_ia3(const T*   Q,\n                                    const T*   K,\n                                    const T*   V,\n                                    T*         q_buf_,\n                                    T*         k_buf_,\n                                    T*         v_buf_,\n                                    const int* ia3_tasks,\n                                    const T*   ia3_key_weights,\n                                    const T*   ia3_value_weights,\n                                    const int  batch_size,\n                                    const int  seq_len,\n                                    const int  head_num,\n                                    const int  size_per_head,\n                                    const int* mask_offset)\n{\n    const int bid = blockIdx.x;\n\n    const int tgt_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int tgt_seq_id   = (bid + mask_offset[bid]) % seq_len;\n    const int n            = head_num * size_per_head;\n\n    const bool use_ia3       = ia3_tasks != nullptr;\n    const int  ia3_task      = use_ia3 ? ia3_tasks[tgt_batch_id] : 0;\n    const bool use_ia3_key   = use_ia3 && (ia3_key_weights != nullptr);\n    const bool use_ia3_value = use_ia3 && (ia3_value_weights != nullptr);\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        const int tgt_head_id   = idx / size_per_head;\n        const int tgt_hidden_id = idx % size_per_head;\n\n        const int src_id = bid * n + idx;\n        const int tgt_id = tgt_batch_id * head_num * seq_len * size_per_head + tgt_head_id * seq_len * size_per_head\n                           + tgt_seq_id * size_per_head + tgt_hidden_id;\n\n        q_buf_[tgt_id] = ldg(&Q[src_id]);\n\n        T k = ldg(&K[src_id]);\n        if (use_ia3_key) {\n            k = k * ia3_key_weights[ia3_task * n + idx];\n        }\n        k_buf_[tgt_id] = k;\n\n        T v = ldg(&V[src_id]);\n        if (use_ia3_value) {\n            v = v * ia3_value_weights[ia3_task * n + idx];\n        }\n        v_buf_[tgt_id] = v;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddQKVBiasIA3RebuildPadding(T*           Q,\n                                       const T*     bias_Q,\n                                       T*           K,\n                                       const T*     bias_K,\n                                       T*           V,\n                                       const T*     bias_V,\n                                       T*           q_buf,\n                                       T*           k_buf,\n                                       T*           v_buf,\n                                       const int    batch_size,\n                                       const int    seq_len,\n                                       const int    head_num,\n                                       const int    size_per_head,\n                                       const int    valid_word_num,\n                                       const int*   mask_offset,\n                                       const int*   ia3_tasks,\n                                       const T*     ia3_key_weights,\n                                       const T*     ia3_value_weights,\n                                       hipStream_t stream)\n{\n#ifdef ENABLE_BF16\n    bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value) && (size_per_head % 2 == 0);\n#else\n    bool       is_half2 = (std::is_same<T, half>::value) && (size_per_head % 2 == 0);\n#endif\n    using T2       = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n    int block_size = head_num * size_per_head;\n    if (is_half2) {\n        while (block_size > 512) {\n            if (block_size % 2 == 0) {\n                block_size /= 2;\n            }\n            else {\n                is_half2   = false;\n                block_size = std::min(block_size, 512);\n                break;\n            }\n        }\n    }\n    else {\n        block_size = std::min(block_size, 512);\n    }\n\n    if (bias_Q == nullptr && bias_K == nullptr && bias_V == nullptr) {\n        if (is_half2) {\n            rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>((T2*)Q,\n                                                                           (T2*)K,\n                                                                           (T2*)V,\n                                                                           (T2*)q_buf,\n                                                                           (T2*)k_buf,\n                                                                           (T2*)v_buf,\n                                                                           ia3_tasks,\n                                                                           (const T2*)ia3_key_weights,\n                                                                           (const T2*)ia3_value_weights,\n                                                                           batch_size,\n                                                                           seq_len,\n                                                                           head_num,\n                                                                           size_per_head / 2,\n                                                                           mask_offset);\n        }\n        else {\n            rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>(Q,\n                                                                           K,\n                                                                           V,\n                                                                           q_buf,\n                                                                           k_buf,\n                                                                           v_buf,\n                                                                           ia3_tasks,\n                                                                           ia3_key_weights,\n                                                                           ia3_value_weights,\n                                                                           batch_size,\n                                                                           seq_len,\n                                                                           head_num,\n                                                                           size_per_head,\n                                                                           mask_offset);\n        }\n    }\n    else if (bias_Q != nullptr && bias_K != nullptr && bias_V != nullptr) {\n        if (is_half2) {\n            add_QKV_bias_rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>((T2*)Q,\n                                                                                        (const T2*)bias_Q,\n                                                                                        (T2*)K,\n                                                                                        (const T2*)bias_K,\n                                                                                        (T2*)V,\n                                                                                        (const T2*)bias_V,\n                                                                                        (T2*)q_buf,\n                                                                                        (T2*)k_buf,\n                                                                                        (T2*)v_buf,\n                                                                                        ia3_tasks,\n                                                                                        (const T2*)ia3_key_weights,\n                                                                                        (const T2*)ia3_value_weights,\n                                                                                        batch_size,\n                                                                                        seq_len,\n                                                                                        head_num,\n                                                                                        size_per_head / 2,\n                                                                                        mask_offset);\n        }\n        else {\n            add_QKV_bias_rebuild_padding_ia3<<<valid_word_num, block_size, 0, stream>>>(Q,\n                                                                                        bias_Q,\n                                                                                        K,\n                                                                                        bias_K,\n                                                                                        V,\n                                                                                        bias_V,\n                                                                                        q_buf,\n                                                                                        k_buf,\n                                                                                        v_buf,\n                                                                                        ia3_tasks,\n                                                                                        ia3_key_weights,\n                                                                                        ia3_value_weights,\n                                                                                        batch_size,\n                                                                                        seq_len,\n                                                                                        head_num,\n                                                                                        size_per_head,\n                                                                                        mask_offset);\n        }\n    }\n    else {\n        FT_CHECK(false);\n    }\n}\n\n#define INSTANTIATEADDQKVBIASIA3REBUILDPADDING(T)                                                                      \\\n    template void invokeAddQKVBiasIA3RebuildPadding(T*           Q,                                                    \\\n                                                    const T*     bias_Q,                                               \\\n                                                    T*           K,                                                    \\\n                                                    const T*     bias_K,                                               \\\n                                                    T*           V,                                                    \\\n                                                    const T*     bias_V,                                               \\\n                                                    T*           q_buf,                                                \\\n                                                    T*           k_buf,                                                \\\n                                                    T*           v_buf,                                                \\\n                                                    const int    batch_size,                                           \\\n                                                    const int    seq_len,                                              \\\n                                                    const int    head_num,                                             \\\n                                                    const int    size_per_head,                                        \\\n                                                    const int    valid_word_num,                                       \\\n                                                    const int*   mask_offset,                                          \\\n                                                    const int*   ia3_tasks,                                            \\\n                                                    const T*     ia3_key_weights,                                      \\\n                                                    const T*     ia3_value_weights,                                    \\\n                                                    hipStream_t stream)\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(float);\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDQKVBIASIA3REBUILDPADDING(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDQKVBIASREBUILDPADDING\n\ntemplate<typename T>\n__global__ void transpose_remove_padding(const T*     src,\n                                         T*           dst,\n                                         const int    batch_size,\n                                         const int    seq_len,\n                                         const int    head_num,\n                                         const int    size_per_head,\n                                         const int*   mask_offset,\n                                         const float* scale,\n                                         const int    int8_mode)\n{\n    // TODO: optimize this kernel?\n    // do remove_sequence_length_padding\n    const int bid = blockIdx.x;  // batch * seq_len or valid_word_num\n\n    const int src_batch_id = (bid + mask_offset[bid]) / seq_len;\n    const int src_seq_id   = (bid + mask_offset[bid]) % seq_len;\n\n    const int dst_seq_id = bid;\n\n    const int src_offset_base = src_batch_id * seq_len * head_num * size_per_head + src_seq_id * size_per_head;\n    const int dst_offset_base = dst_seq_id * head_num * size_per_head;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    const Float_Packed_T scale_val =\n        int8_mode == 2 ? cuda_cast<Float_Packed_T>(*scale) : cuda_cast<Float_Packed_T>(0.0f);\n\n    for (int idx = threadIdx.x; idx < head_num * size_per_head; idx += blockDim.x) {\n        const int head_id   = idx / size_per_head;\n        const int hidden_id = idx % size_per_head;\n        const T   src_elem  = ldg(&src[src_offset_base + head_id * seq_len * size_per_head + hidden_id]);\n        if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(dst)[dst_offset_base + idx] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(src_elem) * scale_val);\n        }\n        else {\n            dst[dst_offset_base + idx] = src_elem;\n        }\n    }\n}\n\n// clang-format off\ntemplate<typename T>\nvoid invokeTransposeAttentionOutRemovePadding(T*           src,\n                                              T*           dst,\n                                              const int    valid_word_num,\n                                              const int    batch_size,\n                                              const int    seq_len,\n                                              const int    head_num,\n                                              const int    size_per_head,\n                                              const int*   mask_offset,\n                                              const float* scale,\n                                              const int    int8_mode,\n                                              hipStream_t stream)\n{\n#ifdef ENABLE_BF16\n    bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value) && (size_per_head % 2 == 0);\n#else\n    bool is_half2 = (std::is_same<T, half>::value) && (size_per_head % 2 == 0);\n#endif\n    using T2       = typename TypeConverter<T>::Type;  // fp16 to half2, bf16 to bf162\n    int block_size = head_num * size_per_head;\n    if (is_half2) {\n        while (block_size > 512) {\n            if (block_size % 2 == 0) {\n                block_size /= 2;\n            }\n            else {\n                is_half2   = false;\n                block_size = std::min(block_size, 1024);\n                break;\n            }\n        }\n    }\n    else {\n        block_size = std::min(block_size, 1024);\n    }\n\n    if (is_half2) {\n        transpose_remove_padding<T2><<<valid_word_num, block_size, 0, stream>>>(\n            (T2*)src, (T2*)dst, batch_size, seq_len, head_num, size_per_head / 2, mask_offset, scale, int8_mode);\n    }\n    else {\n        transpose_remove_padding<<<valid_word_num, block_size, 0, stream>>>(\n            src, dst, batch_size, seq_len, head_num, size_per_head, mask_offset, scale, int8_mode);\n    }\n}\n// clang-format on\n\n#define INSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(T)                                                               \\\n    template void invokeTransposeAttentionOutRemovePadding(T*           src,                                           \\\n                                                           T*           dst,                                           \\\n                                                           const int    valid_word_num,                                \\\n                                                           const int    batch_size,                                    \\\n                                                           const int    seq_len,                                       \\\n                                                           const int    head_num,                                      \\\n                                                           const int    size_per_head,                                 \\\n                                                           const int*   mask_offset,                                   \\\n                                                           const float* scale,                                         \\\n                                                           const int    int8_mode,                                     \\\n                                                           hipStream_t stream)\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(float);\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING(__hip_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEATTENTIONOUTREMOVEPADDING\n\ntemplate<typename T>\n__global__ void add_fusedQKV_bias_transpose_kernel(T* q_buf,\n                                                   T* k_buf,\n                                                   T* v_buf,\n                                                   T* QKV,\n                                                   const T* __restrict qkv_bias,\n                                                   const int*   padding_offset,\n                                                   const int    batch_size,\n                                                   const int    seq_len,\n                                                   const int    token_num,\n                                                   const int    head_num,\n                                                   const int    size_per_head,\n                                                   const float* scale,\n                                                   const int    int8_mode)\n{\n    // QKV: [token_num, 3, n]\n    // qkv_bias: [3, n]\n    // q_buf, k_buf, v_buf: [batch, head_num, seq_len, size_per_head]\n\n    T*        qkv_ptr[3] = {q_buf, k_buf, v_buf};\n    const int n          = head_num * size_per_head;\n    for (int index = blockDim.x * blockIdx.x + threadIdx.x; index < token_num * 3 * n;\n         index += gridDim.x * blockDim.x) {\n        const int bias_id = index % (3 * n);\n\n        const int token_idx        = index / (3 * n);\n        const int token_padded_idx = token_idx + (padding_offset == nullptr ? 0 : padding_offset[token_idx]);\n        const int target_batch_id  = token_padded_idx / seq_len;\n        const int seq_id           = token_padded_idx % seq_len;\n\n        const int qkv_id  = (index % (3 * n)) / n;\n        const int head_id = (index % n) / size_per_head;\n        const int size_id = index % size_per_head;\n\n        T val;\n        if (int8_mode == 2) {\n            val = cuda_cast<T>(cuda_cast<float>(reinterpret_cast<const int8_t*>(QKV)[index]) * scale[qkv_id]);\n        }\n        else {\n            val = ldg(&QKV[index]);\n        }\n        val = val + ldg(&qkv_bias[bias_id]);\n\n        if (int8_mode == 2) {\n            // TODO(mseznec): add support for int8 BMM with FusedAtt\n        }\n        else {\n            QKV[index] = val;\n        }\n\n        qkv_ptr[qkv_id][target_batch_id * head_num * seq_len * size_per_head + head_id * seq_len * size_per_head\n                        + seq_id * size_per_head + size_id] = val;\n    }\n}\n\ntemplate<typename T>\nstruct Vec_t {\n    static constexpr int size = 0;\n};\n\ntemplate<>\nstruct Vec_t<float> {\n    using Type                = float2;\n    static constexpr int size = 2;\n};\n\ntemplate<>\nstruct Vec_t<half> {\n    using Type                = uint32_t;\n    static constexpr int size = 2;\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct Vec_t<__hip_bfloat16> {\n    using Type                = __hip_bfloat162;\n    static constexpr int size = 2;\n};\n#endif\n\ntemplate<typename T, bool PREFIX_PROMPT>\n__global__ void add_fusedQKV_bias_transpose_kernel(T*                               q_buf,\n                                                   T*                               k_buf,\n                                                   T*                               v_buf,\n                                                   PrefixPromptBatchWeightsParam<T> param,\n                                                   T*                               QKV,\n                                                   const T* __restrict qkv_bias,\n                                                   const int* padding_offset,\n                                                   const int  batch_size,\n                                                   const int  seq_len,\n                                                   const int  head_num,\n                                                   const int  size_per_head,\n                                                   const int  rotary_embedding_dim,\n                                                   const bool neox_rotary_style)\n{\n    // This kernel add bias to QKV, which has shape [batch_size, seq_len, 3, head_num, size_per_head], and\n    // QKV split to 3 split buffer q, k, v and transpose them to [batch_size, head_num, seq_len, size_per_head].\n    // For q and k, also apply the rotary embedding.\n\n    // When we pass prefix prompt, this kernel also concatenate the prefix prompt and key/value along\n    // seq_len dimension like [prompt, key/value].\n    // So, the final shape of q is same ([batch_size, head_num, seq_len, size_per_head]), but\n    // the shapes of key and values become [batch_size, head_num, max_prefix_prompt_length + seq_len, size_per_head].\n\n    // NOTE: QKV src shape (batch_size, seq_len, 3, head_num, size_per_head)\n    //  QKV dst shape (3, batch_size, head_num, seq_len, size_per_head)\n    extern __shared__ __align__(sizeof(float2)) char smem_[];  // align on largest vector type\n\n    constexpr int vec_size         = Vec_t<T>::size;\n    using Vec_t                    = typename Vec_t<T>::Type;\n    const int token_idx            = blockIdx.x - batch_size * param.max_prefix_prompt_length;\n    const int token_padding_offset = (padding_offset == nullptr || token_idx < 0) ? 0 : padding_offset[token_idx];\n    const int tgt_token_idx        = token_idx + token_padding_offset;\n\n    const int batch_idx = tgt_token_idx / seq_len;\n    const int seq_idx   = tgt_token_idx % seq_len;\n\n    const int head_idx = blockIdx.y;\n    const int tidx     = threadIdx.x;\n\n    const int total_seq_len = param.max_prefix_prompt_length + seq_len;\n\n    const bool is_masked = tidx * vec_size >= size_per_head;\n    // NOTE: blockIdx.x < batch_size * param.max_prefix_prompt_length really handles prefix prompts\n    if (PREFIX_PROMPT && token_idx < 0) {\n        const int prompt_batch_idx = blockIdx.x / param.max_prefix_prompt_length;\n        const int prompt_seq_idx   = blockIdx.x % param.max_prefix_prompt_length;\n        const int prompt_length    = param.d_prefix_prompt_lengths[prompt_batch_idx];\n\n        if (prompt_seq_idx < prompt_length) {\n            const int dest_kv_idx = prompt_batch_idx * size_per_head * total_seq_len * head_num\n                                    + head_idx * size_per_head * total_seq_len + prompt_seq_idx * size_per_head\n                                    + tidx * vec_size;\n            const int prefix_kv_idx =\n                size_per_head * prompt_length * head_idx + size_per_head * prompt_seq_idx + tidx * vec_size;\n\n            const T* prefix_prompt_k = param.d_prefix_prompt_batch[prompt_batch_idx]\n                                       + param.prefix_prompt_layer_offset_per_seq * prompt_length;\n            const T* prefix_prompt_v = prefix_prompt_k + prompt_length * head_num * size_per_head;\n            if (!is_masked) {\n                *reinterpret_cast<Vec_t*>(&k_buf[dest_kv_idx]) =\n                    *reinterpret_cast<const Vec_t*>(&prefix_prompt_k[prefix_kv_idx]);\n                *reinterpret_cast<Vec_t*>(&v_buf[dest_kv_idx]) =\n                    *reinterpret_cast<const Vec_t*>(&prefix_prompt_v[prefix_kv_idx]);\n            }\n        }\n        return;\n    }\n\n    const int prefix_prompt_length = PREFIX_PROMPT ? param.d_prefix_prompt_lengths[batch_idx] : 0;\n    const int hidden_idx           = head_idx * size_per_head + tidx * vec_size;\n    const int n                    = head_num * size_per_head;\n\n    // the [0..seq_len) indices really handle KV [max_pp_len..seq_len+max_pp_len)\n    // and Q [0..seq_len)\n    // Note: if !PREFIX_PROMPT, max_pp_len = 0, so it's no-op\n    const int dst_kv_seq_idx = seq_idx + prefix_prompt_length;\n\n    // NOTE: q has seq len excluding prefix prompt\n    // src QKV: [batch, time, 3, head, hidden]\n    const int src_q_idx = token_idx * 3 * n + hidden_idx;\n    const int src_k_idx = token_idx * 3 * n + hidden_idx + n;\n    const int src_v_idx = token_idx * 3 * n + hidden_idx + 2 * n;\n\n    Vec_t q, k, v;\n    Vec_t q_bias, k_bias, v_bias;\n    if (!is_masked) {\n        q = *reinterpret_cast<const Vec_t*>(&QKV[src_q_idx]);\n        k = *reinterpret_cast<const Vec_t*>(&QKV[src_k_idx]);\n        v = *reinterpret_cast<const Vec_t*>(&QKV[src_v_idx]);\n\n        q_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx]);\n        k_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx + n]);\n        v_bias = *reinterpret_cast<const Vec_t*>(&qkv_bias[hidden_idx + 2 * n]);\n    }\n\n    q = mmha::add(q, q_bias);\n    k = mmha::add(k, k_bias);\n    v = mmha::add(v, v_bias);\n\n    if (!neox_rotary_style) {\n        mmha::apply_rotary_embedding(q, k, tidx, rotary_embedding_dim, dst_kv_seq_idx);\n    }\n    else {\n        const bool do_rotary = !is_masked && vec_size * tidx < rotary_embedding_dim;\n\n        T* q_smem = reinterpret_cast<T*>(smem_);\n        T* k_smem = q_smem + rotary_embedding_dim;\n\n        const int half_rotary_dim = rotary_embedding_dim / 2;\n        const int half_idx        = (tidx * vec_size) / half_rotary_dim;\n        const int intra_half_idx  = (tidx * vec_size) % half_rotary_dim;\n        const int smem_pitch      = half_rotary_dim;  // TODO: adjust for bank conflicts?\n\n        if (do_rotary) {\n            *reinterpret_cast<Vec_t*>(q_smem + half_idx * smem_pitch + intra_half_idx) = q;\n            *reinterpret_cast<Vec_t*>(k_smem + half_idx * smem_pitch + intra_half_idx) = k;\n        }\n\n        __syncthreads();\n\n        const int     transpose_idx = half_idx * (half_rotary_dim / 2) + intra_half_idx / 2;\n        constexpr int tidx_factor   = vec_size / 2;\n        if (do_rotary) {\n            mmha::vec_from_smem_transpose(q, q_smem, transpose_idx, smem_pitch);\n            mmha::vec_from_smem_transpose(k, k_smem, transpose_idx, smem_pitch);\n\n            mmha::apply_rotary_embedding(q, k, transpose_idx / tidx_factor, rotary_embedding_dim, dst_kv_seq_idx);\n\n            mmha::write_smem_transpose(q, q_smem, transpose_idx, smem_pitch);\n            mmha::write_smem_transpose(k, k_smem, transpose_idx, smem_pitch);\n        }\n\n        __syncthreads();\n\n        if (do_rotary) {\n            q = *reinterpret_cast<Vec_t*>(q_smem + half_idx * smem_pitch + intra_half_idx);\n            k = *reinterpret_cast<Vec_t*>(k_smem + half_idx * smem_pitch + intra_half_idx);\n        }\n    }\n    if (!is_masked) {\n        *reinterpret_cast<Vec_t*>(&QKV[src_q_idx]) = q;\n        *reinterpret_cast<Vec_t*>(&QKV[src_k_idx]) = k;\n        *reinterpret_cast<Vec_t*>(&QKV[src_v_idx]) = v;\n    }\n\n    const int dest_q_idx = batch_idx * size_per_head * seq_len * head_num + head_idx * size_per_head * seq_len\n                           + seq_idx * size_per_head + tidx * vec_size;\n\n    const int dest_kv_idx = batch_idx * size_per_head * total_seq_len * head_num\n                            + head_idx * size_per_head * total_seq_len + dst_kv_seq_idx * size_per_head\n                            + tidx * vec_size;\n\n    if (!is_masked) {\n        *reinterpret_cast<Vec_t*>(&q_buf[dest_q_idx])  = q;\n        *reinterpret_cast<Vec_t*>(&k_buf[dest_kv_idx]) = k;\n        *reinterpret_cast<Vec_t*>(&v_buf[dest_kv_idx]) = v;\n    }\n}\n\n#define FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, PREFIX_PROMPT)                                                              \\\n    add_fusedQKV_bias_transpose_kernel<T, PREFIX_PROMPT><<<grid, block, smem_size, stream>>>(q_buf,                    \\\n                                                                                             k_buf,                    \\\n                                                                                             v_buf,                    \\\n                                                                                             param,                    \\\n                                                                                             QKV,                      \\\n                                                                                             qkv_bias,                 \\\n                                                                                             padding_offset,           \\\n                                                                                             batch_size,               \\\n                                                                                             seq_len,                  \\\n                                                                                             head_num,                 \\\n                                                                                             size_per_head,            \\\n                                                                                             rotary_embedding_dim,     \\\n                                                                                             neox_rotary_style);\n\ntemplate<typename T>\nvoid invokeAddFusedQKVBiasTranspose(T*                               q_buf,\n                                    T*                               k_buf,\n                                    T*                               v_buf,\n                                    PrefixPromptBatchWeightsParam<T> param,\n                                    T*                               QKV,\n                                    const T*                         qkv_bias,\n                                    const int*                       padding_offset,\n                                    const int                        batch_size,\n                                    const int                        seq_len,\n                                    const int                        token_num,\n                                    const int                        head_num,\n                                    const int                        size_per_head,\n                                    const int                        rotary_embedding_dim,\n                                    const int                        neox_rotary_style,\n                                    const float*                     scale,\n                                    const int                        int8_mode,\n                                    hipStream_t                     stream)\n{\n    // [bs, seq_len, 3, head, Dh]\n    if (rotary_embedding_dim == 0 && param.max_prefix_prompt_length == 0) {\n        const int m = token_num;\n        const int n = head_num * size_per_head;\n        dim3      block(384);\n        dim3      grid((int)(ceil(1.0 * m * n / 384)));\n        add_fusedQKV_bias_transpose_kernel<<<grid, block, 0, stream>>>(q_buf,\n                                                                       k_buf,\n                                                                       v_buf,\n                                                                       QKV,\n                                                                       qkv_bias,\n                                                                       padding_offset,\n                                                                       batch_size,\n                                                                       seq_len,\n                                                                       token_num,\n                                                                       head_num,\n                                                                       size_per_head,\n                                                                       scale,\n                                                                       int8_mode);\n    }\n    else {\n        FT_CHECK_WITH_INFO(int8_mode != 2, \"w8a8 not yet implemented with prefix prompt\");  // TODO(mseznec)\n        // To implement rotary embeddings, each thread processes two QKV elems:\n        dim3   block((size_per_head / Vec_t<T>::size + 31) / 32 * 32);\n        dim3   grid(token_num + batch_size * param.max_prefix_prompt_length, head_num);\n        size_t smem_size = neox_rotary_style ? 2 * rotary_embedding_dim * sizeof(T) : 0;\n        // NOTE: add offset for rotary embedding\n        //  add_fusedQKV_bias_transpose_kernel<<<grid, block, 0, stream>>>(\n        //      q_buf, k_buf, v_buf, param, QKV, qkv_bias, batch_size, seq_len, head_num, size_per_head,\n        //      rotary_embedding_dim);\n        if (param.max_prefix_prompt_length == 0) {\n            FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, false);\n        }\n        else {\n            FUSED_QKV_BIAS_TRANSPOSE_LAUNCH(T, true);\n        }\n    }\n}\n\n#define INSTANTIATEADDFUSEDQKVBIASTRANSPOSE(T)                                                                         \\\n    template void invokeAddFusedQKVBiasTranspose(T*                               q_buf,                               \\\n                                                 T*                               k_buf,                               \\\n                                                 T*                               v_buf,                               \\\n                                                 PrefixPromptBatchWeightsParam<T> param,                               \\\n                                                 T*                               QKV,                                 \\\n                                                 const T*                         qkv_bias,                            \\\n                                                 const int*                       padding_offset,                      \\\n                                                 const int                        batch_size,                          \\\n                                                 const int                        seq_len,                             \\\n                                                 const int                        token_num,                           \\\n                                                 const int                        head_num,                            \\\n                                                 const int                        size_per_head,                       \\\n                                                 const int                        rotary_embedding_dim,                \\\n                                                 const int                        neox_rotary_style,                   \\\n                                                 const float*                     scale,                               \\\n                                                 const int                        int8_mode,                           \\\n                                                 hipStream_t                     stream)\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(float);\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDFUSEDQKVBIASTRANSPOSE(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDFUSEDQKVBIASTRANSPOSE\n\ntemplate<typename T>\n__global__ void transpose_4d(T*        dst,\n                             T*        src,\n                             const int dim0,\n                             const int dim1,\n                             const int dim2,\n                             const int dim3,\n                             const int dim0_leading_dim,\n                             const int ite)\n{\n    // transpose from [dim0, dim1, dim2, dim3] to [dim2, X, dim1, dim3]\n    // where the dimension of X is dim0_leading_dim, and offset is ite * dim0\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < dim0 * dim1 * dim2 * dim3; i += blockDim.x * gridDim.x) {\n        int       index = i;\n        const int d3    = index % dim3;\n        index           = (index - d3) / dim3;\n        const int d2    = index % dim2;\n        index           = (index - d2) / dim2;\n        const int d1    = index % dim1;\n        index           = (index - d1) / dim1;\n        const int d0    = index % dim0;\n        index           = (index - d0) / dim0;\n        dst[d2 * dim0_leading_dim * dim1 * dim3 + (d0 + dim0 * ite) * dim1 * dim3 + d1 * dim3 + d3] = src[i];\n    }\n}\n\ntemplate<>\n__global__ void transpose_4d(half*     dst,\n                             half*     src,\n                             const int dim0,\n                             const int dim1,\n                             const int dim2,\n                             const int dim3,\n                             const int dim0_leading_dim,\n                             const int ite)\n{\n    half2*    dst_ptr   = (half2*)dst;\n    half2*    src_ptr   = (half2*)src;\n    const int half_dim3 = dim3 / 2;\n    // transpose from [dim0, dim1, dim2, half_dim3] to [dim2, dim0, dim1, half_dim3]\n    // where the dimension of X is dim0_leading_dim, and offset is ite * dim0\n    for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < dim0 * dim1 * dim2 * half_dim3;\n         i += blockDim.x * gridDim.x) {\n        int       index = i;\n        const int d3    = index % half_dim3;\n        index           = (index - d3) / half_dim3;\n        const int d2    = index % dim2;\n        index           = (index - d2) / dim2;\n        const int d1    = index % dim1;\n        index           = (index - d1) / dim1;\n        const int d0    = index % dim0;\n        index           = (index - d0) / dim0;\n        dst_ptr[d2 * dim0_leading_dim * dim1 * half_dim3 + (d0 + dim0 * ite) * dim1 * half_dim3 + d1 * half_dim3 + d3] =\n            src_ptr[i];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTranspose4d(T*           dst,\n                       T*           src,\n                       const int    local_batch_size,\n                       const int    seq_len,\n                       const int    size_per_head,\n                       const int    local_hidden_units,\n                       const int    local_head_num,\n                       const int    batch_size,\n                       const int    ite,\n                       hipStream_t stream)\n{\n    transpose_4d<<<local_batch_size * seq_len * local_hidden_units / 512, 512 / (4 / (sizeof(T))), 0, stream>>>(\n        dst, src, local_batch_size, local_head_num, seq_len, size_per_head, batch_size, ite);\n}\n\n#define INSTANTIATETRANSPOSE4D(T)                                                                                      \\\n    template void invokeTranspose4d(T*           dst,                                                                  \\\n                                    T*           src,                                                                  \\\n                                    const int    local_batch_size,                                                     \\\n                                    const int    seq_len,                                                              \\\n                                    const int    size_per_head,                                                        \\\n                                    const int    local_hidden_units,                                                   \\\n                                    const int    local_head_num,                                                       \\\n                                    const int    batch_size,                                                           \\\n                                    const int    ite,                                                                  \\\n                                    hipStream_t stream)\nINSTANTIATETRANSPOSE4D(float);\nINSTANTIATETRANSPOSE4D(half);\n#undef INSTANTIATETRANSPOSE4D\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_k_cache(\n    T* k_dst, const T* k_src, const int head_num, const int size_per_head, const int seq_len, const int max_seq_len)\n{\n    const int     batch_id = blockIdx.y;\n    const int     head_id  = blockIdx.z;\n    constexpr int X_ELEMS  = (sizeof(T) == 4) ? 4 : 8;\n\n    auto key_src = reinterpret_cast<const uint4*>(k_src + batch_id * head_num * size_per_head * seq_len\n                                                  + head_id * size_per_head * seq_len);\n    auto key_dst = reinterpret_cast<uint4*>(k_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    const int out_idx             = blockIdx.x * blockDim.x + threadIdx.x;\n    int       size_per_head_div_x = size_per_head / X_ELEMS;\n    if (out_idx >= size_per_head_div_x * max_seq_len) {\n        return;\n    }\n\n    int       idx            = out_idx;\n    const int k_seq_len_id   = idx % max_seq_len;\n    idx                      = (idx - k_seq_len_id) / max_seq_len;\n    const int k_head_size_id = idx % size_per_head_div_x;\n\n    if (k_seq_len_id < seq_len) {\n        key_dst[out_idx] = key_src[k_seq_len_id * size_per_head_div_x + k_head_size_id];\n    }\n}\n\ntemplate<typename T>\n__global__ void transpose_4d_batch_major_v_cache(\n    T* v_dst, const T* v_src, const int head_num, const int size_per_head, const int seq_len, const int max_seq_len)\n{\n    const int batch_id = blockIdx.y;\n    const int head_id  = blockIdx.z;\n\n    // 16 byte loads will handle \"x\" dimension\n    auto val_src = reinterpret_cast<const uint4*>(v_src + batch_id * head_num * size_per_head * seq_len\n                                                  + head_id * size_per_head * seq_len);\n    auto val_dst = reinterpret_cast<uint4*>(v_dst + batch_id * head_num * size_per_head * max_seq_len\n                                            + head_id * size_per_head * max_seq_len);\n\n    // idx is over output dimension L * size_per_head / x for values\n    const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n\n    constexpr int X_ELEMS             = (sizeof(T) == 4) ? 4 : 8;\n    const int     size_per_head_div_x = size_per_head / X_ELEMS;\n\n    if (idx >= size_per_head_div_x * seq_len) {\n        return;\n    }\n\n    val_dst[idx] = val_src[idx];\n}\n\ntemplate<typename T>\nvoid invokeTranspose4dBatchMajor(T*           k_dst,\n                                 T*           v_dst,\n                                 const T*     k_src,\n                                 const T*     v_src,\n                                 const int    local_batch_size,\n                                 const int    seq_len,\n                                 const int    max_seq_len,\n                                 const int    size_per_head,\n                                 const int    local_head_num,\n                                 hipStream_t stream)\n{\n    constexpr int block_sz = 128;\n    constexpr int x        = (sizeof(T) == 4) ? 4 : 8;\n    int           size     = max_seq_len * size_per_head / x;\n    dim3          grid((size + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n    dim3          grid_v((seq_len * size_per_head / x + block_sz - 1) / block_sz, local_batch_size, local_head_num);\n\n    transpose_4d_batch_major_k_cache<<<grid, block_sz, 0, stream>>>(\n        k_dst, k_src, local_head_num, size_per_head, seq_len, max_seq_len);\n\n    transpose_4d_batch_major_v_cache<<<grid_v, block_sz, 0, stream>>>(\n        v_dst, v_src, local_head_num, size_per_head, seq_len, max_seq_len);\n}\n\n#define INSTANTIATETRANSPOSE4DBATCHMAJOR(T)                                                                            \\\n    template void invokeTranspose4dBatchMajor(T*           k_dst,                                                      \\\n                                              T*           v_dst,                                                      \\\n                                              const T*     k_src,                                                      \\\n                                              const T*     v_src,                                                      \\\n                                              const int    local_batch_size,                                           \\\n                                              const int    seq_len,                                                    \\\n                                              const int    max_seq_len,                                                \\\n                                              const int    size_per_head,                                              \\\n                                              const int    local_head_num,                                             \\\n                                              hipStream_t stream)\nINSTANTIATETRANSPOSE4DBATCHMAJOR(float);\nINSTANTIATETRANSPOSE4DBATCHMAJOR(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSE4DBATCHMAJOR(__hip_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSE4DBATCHMAJOR\n\ntemplate<typename T>\n__global__ void addRelativeAttentionBias(\n    T* qk_buf, const T* relative_attention_bias, const int batch_size, const int head_num, const int seq_len)\n{\n    for (int i = threadIdx.x; i < batch_size * seq_len; i += blockDim.x) {\n        int batch_id = i / seq_len;\n        int seq_id   = i % seq_len;\n\n        const int bias_index = blockIdx.x * seq_len + seq_id;\n        const int qk_index   = batch_id * gridDim.x * seq_len + bias_index;\n        qk_buf[qk_index]     = add(qk_buf[qk_index], relative_attention_bias[bias_index]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddRelativeAttentionBias(T*           qk_buf,\n                                    const T*     relative_attention_bias,\n                                    const int    batch_size,\n                                    const int    head_num,\n                                    const int    seq_len,\n                                    hipStream_t stream)\n{\n    // qk_buf: [batch_size, head_num, seq_len, seq_len]\n    // relative_attention_bias: [1, head_num, seq_len, seq_len]\n    dim3 grid(head_num * seq_len);\n    dim3 block(512);\n    using T2 = typename TypeConverter<T>::Type;\n#ifdef ENABLE_BF16\n    const bool is_half2 = (std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value) && (seq_len % 2 == 0);\n#else\n    const bool is_half2 = (std::is_same<T, half>::value) && (seq_len % 2 == 0);\n#endif\n    if (is_half2) {\n        addRelativeAttentionBias<T2><<<grid, block, 0, stream>>>(\n            (T2*)qk_buf, (const T2*)relative_attention_bias, batch_size, head_num, seq_len / 2);\n    }\n    else {\n        addRelativeAttentionBias<<<grid, block, 0, stream>>>(\n            qk_buf, relative_attention_bias, batch_size, head_num, seq_len);\n    }\n}\n\n#define INSTANTIATEADDRELATIVEATTENTIONBIAS(T)                                                                         \\\n    template void invokeAddRelativeAttentionBias(T*           qk_buf,                                                  \\\n                                                 const T*     relative_attention_bias,                                 \\\n                                                 const int    batch_size,                                              \\\n                                                 const int    head_num,                                                \\\n                                                 const int    seq_len,                                                 \\\n                                                 hipStream_t stream)\nINSTANTIATEADDRELATIVEATTENTIONBIAS(float);\nINSTANTIATEADDRELATIVEATTENTIONBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDRELATIVEATTENTIONBIAS(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDRELATIVEATTENTIONBIAS\n\n/*******************  invokeAddHead3SizeQKVBias  ***********************/\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, 3*batch);\n// block(num_head * size_per_head)\ntemplate<typename T>\n__global__ void add_head3Size_QKV_bias(const T*  mm_qkv,\n                                       const T*  bias_qkv,\n                                       T*        q_buf_,\n                                       T*        k_buf_,\n                                       T*        v_buf_,\n                                       const int batch,\n                                       const int window_num,\n                                       const int window_len,\n                                       const int num_head,\n                                       const int size_per_head)\n{\n\n    T*  buf_ptr;\n    int qkv_id = blockIdx.z / batch;\n    if (qkv_id == 0) {\n        buf_ptr = q_buf_;\n    }\n    else if (qkv_id == 1) {\n        buf_ptr = k_buf_;\n    }\n    else {\n        buf_ptr = v_buf_;\n    }\n\n    const int batch_id   = blockIdx.z % batch;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int bias_idx = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    const T   bias     = ldg(bias_qkv + bias_idx);\n\n    const int input_idx =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head + bias_idx;\n    T tmp = mm_qkv[input_idx] + bias;\n\n    int target_id = (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n                    + id_in_head;\n    ;\n    buf_ptr[target_id] = tmp;\n}\n\n// for float2, size_per_head /= 2\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, 3*batch);\n// block(num_head * size_per_head)\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const float2* mm_qkv,\n                                       const float2* bias_qkv,\n                                       float2*       q_buf_,\n                                       float2*       k_buf_,\n                                       float2*       v_buf_,\n                                       const int     batch,\n                                       const int     window_num,\n                                       const int     window_len,\n                                       const int     num_head,\n                                       const int     size_per_head)\n{\n\n    float2* buf_ptr;\n    int     qkv_id = blockIdx.z / batch;\n    if (qkv_id == 0) {\n        buf_ptr = q_buf_;\n    }\n    else if (qkv_id == 1) {\n        buf_ptr = k_buf_;\n    }\n    else {\n        buf_ptr = v_buf_;\n    }\n\n    const int batch_id   = blockIdx.z % batch;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int    bias_idx = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    const float2 bias     = ldg(bias_qkv + bias_idx);\n\n    const int input_idx =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head + bias_idx;\n    float2 tmp = mm_qkv[input_idx];\n    tmp.x += bias.x;\n    tmp.y += bias.y;\n\n    int target_id = (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n                    + id_in_head;\n    ;\n    buf_ptr[target_id] = tmp;\n}\n\n// for half2, size_per_head /= 2\n// m = batch*window_num*window_len\n// mm_qkv is [m, head*3*size_per_head] row-major\n// bias_qkv is [head*3*size_per_head]\n// q_buf_, k_buf_, v_buf_ is [batch*window_num, num_head, window_len, size_per_head] row-major\n// grid(window_len, window_num, batch);\n// block(num_head * size_per_head)\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const half2* mm_qkv,\n                                       const half2* bias_qkv,\n                                       half2*       q_buf_,\n                                       half2*       k_buf_,\n                                       half2*       v_buf_,\n                                       const int    batch,\n                                       const int    window_num,\n                                       const int    window_len,\n                                       const int    num_head,\n                                       const int    size_per_head)\n{\n\n    const int batch_id   = blockIdx.z;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int input_offset =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head;\n    const int target_id =\n        (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n        + id_in_head;\n\n    int   qkv_id      = 0;\n    int   bias_idx    = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    half2 bias        = __ldg(bias_qkv + bias_idx);\n    int   input_idx   = input_offset + bias_idx;\n    half2 tmp         = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    q_buf_[target_id] = tmp;\n\n    qkv_id            = 1;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = __ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    k_buf_[target_id] = tmp;\n\n    qkv_id            = 2;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = __ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = __hadd2(tmp, bias);\n    v_buf_[target_id] = tmp;\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\n__global__ void add_head3Size_QKV_bias(const __hip_bfloat162* mm_qkv,\n                                       const __hip_bfloat162* bias_qkv,\n                                       __hip_bfloat162*       q_buf_,\n                                       __hip_bfloat162*       k_buf_,\n                                       __hip_bfloat162*       v_buf_,\n                                       const int             batch,\n                                       const int             window_num,\n                                       const int             window_len,\n                                       const int             num_head,\n                                       const int             size_per_head)\n{\n\n    const int batch_id   = blockIdx.z;\n    const int token_id   = blockIdx.x;\n    const int window_id  = blockIdx.y;\n    const int head_id    = threadIdx.x / size_per_head;\n    const int id_in_head = threadIdx.x % size_per_head;\n\n    const int input_offset =\n        ((batch_id * window_num + window_id) * window_len + token_id) * num_head * 3 * size_per_head;\n    const int target_id =\n        (((batch_id * window_num + window_id) * num_head + head_id) * window_len + token_id) * size_per_head\n        + id_in_head;\n\n    int            qkv_id    = 0;\n    int            bias_idx  = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    __hip_bfloat162 bias      = ldg(bias_qkv + bias_idx);\n    int            input_idx = input_offset + bias_idx;\n    __hip_bfloat162 tmp       = mm_qkv[input_idx];\n    tmp                      = bf16hadd2(tmp, bias);\n    q_buf_[target_id]        = tmp;\n\n    qkv_id            = 1;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = bf16hadd2(tmp, bias);\n    k_buf_[target_id] = tmp;\n\n    qkv_id            = 2;\n    bias_idx          = (head_id * 3 + qkv_id) * size_per_head + id_in_head;\n    bias              = ldg(bias_qkv + bias_idx);\n    input_idx         = input_offset + bias_idx;\n    tmp               = mm_qkv[input_idx];\n    tmp               = bf16hadd2(tmp, bias);\n    v_buf_[target_id] = tmp;\n}\n#endif\n\ntemplate<typename T>\nvoid invokeAddHead3SizeQKVBias(const T*     mm_qkv,\n                               const T*     bias_qkv,\n                               T*           q_buf_,\n                               T*           k_buf_,\n                               T*           v_buf_,\n                               const int    batch,\n                               const int    window_num,\n                               const int    window_len,\n                               const int    num_head,\n                               const int    size_per_head,\n                               hipStream_t stream)\n{\n    if (std::is_same<T, float>::value) {\n        dim3 grid(window_len, window_num, 3 * batch);\n        dim3 block(num_head * size_per_head);\n\n        if (block.x < 1024) {\n            add_head3Size_QKV_bias<<<grid, block, 0, stream>>>(\n                mm_qkv, bias_qkv, q_buf_, k_buf_, v_buf_, batch, window_num, window_len, num_head, size_per_head);\n        }\n        else if ((block.x % 2 == 0) && (block.x / 2 < 1024)) {\n            block.x /= 2;\n            add_head3Size_QKV_bias<<<grid, block, 0, stream>>>((const float2*)mm_qkv,\n                                                               (const float2*)bias_qkv,\n                                                               (float2*)q_buf_,\n                                                               (float2*)k_buf_,\n                                                               (float2*)v_buf_,\n                                                               batch,\n                                                               window_num,\n                                                               window_len,\n                                                               num_head,\n                                                               size_per_head / 2);\n        }\n        else {\n            printf(\"[ERROR][invokeAddHead3SizeQKVBias] unsupported block.x!\\n\");\n            exit(-1);\n        }\n    }\n#ifdef ENABLE_BF16\n    else if (std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value) {\n#else\n    else if (std::is_same<T, half>::value) {\n#endif\n        dim3 grid(window_len, window_num, batch);\n        dim3 block(num_head * size_per_head / 2);\n\n        using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat16\n\n        if (block.x > 1024) {\n            printf(\"[ERROR][invokeAddHead3SizeQKVBias] block.x > 1024!\\n\");\n            exit(-1);\n        }\n\n        add_head3Size_QKV_bias<<<grid, block, 0, stream>>>((const T2*)mm_qkv,\n                                                           (const T2*)bias_qkv,\n                                                           (T2*)q_buf_,\n                                                           (T2*)k_buf_,\n                                                           (T2*)v_buf_,\n                                                           batch,\n                                                           window_num,\n                                                           window_len,\n                                                           num_head,\n                                                           size_per_head / 2);\n    }\n}\n\n#define INSTANTIATEADDHEAD3SIZEQKVBIAS(T)                                                                              \\\n    template void invokeAddHead3SizeQKVBias<T>(const T*     mm_qkv,                                                    \\\n                                               const T*     bias_qkv,                                                  \\\n                                               T*           q_buf_,                                                    \\\n                                               T*           k_buf_,                                                    \\\n                                               T*           v_buf_,                                                    \\\n                                               const int    batch,                                                     \\\n                                               const int    window_num,                                                \\\n                                               const int    window_len,                                                \\\n                                               const int    num_head,                                                  \\\n                                               const int    size_per_head,                                             \\\n                                               hipStream_t stream)\nINSTANTIATEADDHEAD3SIZEQKVBIAS(float);\nINSTANTIATEADDHEAD3SIZEQKVBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEADDHEAD3SIZEQKVBIAS(__hip_bfloat16);\n#endif\n#undef INSTANTIATEADDHEAD3SIZEQKVBIAS\n\n/*******************  invokeMaskedSoftMaxWithRelPosBias  ***********************/\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T>\n__global__ void softmax_withRelPosBias_element1_kernel(T*          qk_buf,\n                                                       const T*    attn_mask,\n                                                       const T*    relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n\n    bool qual = threadIdx.x < window_len;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + threadIdx.x;\n            qk_offset = (blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window;\n            const int relative_pos_bias_offset = (blockIdx.y % num_head) * window_len_x_window_len + offset_in_window;\n            float     mask_val =\n                (attn_mask == nullptr) ?\n                        0.0f :\n                        static_cast<float>(\n                        ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window)));\n            tmp = qk_scale * static_cast<float>(qk_buf[qk_offset]) + mask_val\n                  + static_cast<float>(ldg(relative_pos_bias + relative_pos_bias_offset));\n        }\n\n        float max_val = blockReduceMax<float>(tmp);\n        if (threadIdx.x == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        float qk_tmp  = qual ? __expf(tmp - s_max) : 0.0f;\n        float sum_val = blockReduceSum<float>(qk_tmp);\n        if (threadIdx.x == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            qk_buf[qk_offset] = (T)(qk_tmp * s_mean);\n        }\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len/2 + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T2, typename T>\n__global__ void softmax_withRelPosBias_element2_kernel(T2*         qk_buf,\n                                                       const T2*   attn_mask,\n                                                       const T2*   relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n    const int window_len_2 = window_len / 2;\n    const int tidx         = threadIdx.x;\n    bool      qual         = tidx < window_len_2;\n    const T2  zero         = {T(0.0f), T(0.0f)};\n    const int bdim         = blockDim.x;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        float2           local_qk_val;\n        T2               qk_val;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + 2 * tidx;\n            qk_offset = ((blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window) / 2;\n            const int relative_pos_bias_offset =\n                ((blockIdx.y % num_head) * window_len_x_window_len + offset_in_window) / 2;\n            T2 mask_val =\n                (attn_mask == nullptr) ?\n                    zero :\n                    ldg(attn_mask + ((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window) / 2);\n            qk_val            = qk_buf[qk_offset];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            const T2 bias_val = ldg(relative_pos_bias + relative_pos_bias_offset);\n            local_qk_val.x =\n                qk_scale * local_qk_val.x + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                qk_scale * local_qk_val.y + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n        }\n\n        float max_val = bdim <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (tidx == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n\n        float sum_val = bdim <= 32 ? warpReduceSum<float>(local_qk_val.x + local_qk_val.y) :\n                                     blockReduceSum<float>(local_qk_val.x + local_qk_val.y);\n        if (tidx == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            local_qk_val.x    = local_qk_val.x * s_mean;\n            local_qk_val.y    = local_qk_val.y * s_mean;\n            qk_val.x          = T(local_qk_val.x);\n            qk_val.y          = T(local_qk_val.y);\n            qk_buf[qk_offset] = qk_val;\n        }\n    }\n}\n\n// grid = (window_len/word_per_thread, window_num*num_head, batch_size)\n// block.x = max(32, (window_len/4 + 31)/32*32)\n// qk_buf is [batch, window_num, num_head, window_len, window_len]\n// attn_mask is [window_num, window_len, window_len] + row-major\n// relative_pos_bias is [num_head, window_len, window_len] + row-majot\ntemplate<typename T4, typename T>\n__global__ void softmax_withRelPosBias_element4_kernel(T4*         qk_buf,\n                                                       const T4*   attn_mask,\n                                                       const T4*   relative_pos_bias,\n                                                       const int   batch_size,\n                                                       const int   num_head,\n                                                       const int   window_num,\n                                                       const int   window_len,\n                                                       const int   window_len_x_window_len,\n                                                       const float qk_scale)\n{\n    const int window_len_4 = window_len / 4;\n    const int tidx         = threadIdx.x;\n    bool      qual         = tidx < window_len_4;\n    const T4  zero         = {T(0.0f), T(0.0f), T(0.0f), T(0.0f)};\n    const int bdim         = blockDim.x;\n    for (int window_id = blockIdx.x; window_id < window_len; window_id += gridDim.x) {\n        float            tmp = -1e20f;\n        __shared__ float s_mean, s_max;\n        int              qk_offset;\n        float4           local_qk_val;\n        T4               qk_val;\n        if (qual) {\n            const int offset_in_window = window_id * window_len + 4 * tidx;\n            qk_offset = ((blockIdx.z * gridDim.y + blockIdx.y) * window_len_x_window_len + offset_in_window) / 4;\n            const int relative_pos_bias_offset =\n                ((blockIdx.y % num_head) * window_len_x_window_len + offset_in_window) / 4;\n            T4 mask_val       = (attn_mask == nullptr) ?\n                                    zero :\n                                    attn_mask[((blockIdx.y / num_head) * window_len_x_window_len + offset_in_window) / 4];\n            qk_val            = qk_buf[qk_offset];\n            local_qk_val.x    = static_cast<float>(qk_val.x);\n            local_qk_val.y    = static_cast<float>(qk_val.y);\n            local_qk_val.z    = static_cast<float>(qk_val.z);\n            local_qk_val.w    = static_cast<float>(qk_val.w);\n            const T4 bias_val = relative_pos_bias[relative_pos_bias_offset];\n            local_qk_val.x =\n                qk_scale * local_qk_val.x + static_cast<float>(mask_val.x) + static_cast<float>(bias_val.x);\n            local_qk_val.y =\n                qk_scale * local_qk_val.y + static_cast<float>(mask_val.y) + static_cast<float>(bias_val.y);\n            local_qk_val.z =\n                qk_scale * local_qk_val.z + static_cast<float>(mask_val.z) + static_cast<float>(bias_val.z);\n            local_qk_val.w =\n                qk_scale * local_qk_val.w + static_cast<float>(mask_val.w) + static_cast<float>(bias_val.w);\n            tmp = local_qk_val.x > local_qk_val.y ? local_qk_val.x : local_qk_val.y;\n            tmp = tmp > local_qk_val.z ? tmp : local_qk_val.z;\n            tmp = tmp > local_qk_val.w ? tmp : local_qk_val.w;\n        }\n\n        float max_val = bdim <= 32 ? warpReduceMax<float>(tmp) : blockReduceMax<float>(tmp);\n        if (tidx == 0) {\n            s_max = max_val;\n        }\n        __syncthreads();\n\n        local_qk_val.x = qual ? __expf(local_qk_val.x - s_max) : 0.0f;\n        local_qk_val.y = qual ? __expf(local_qk_val.y - s_max) : 0.0f;\n        local_qk_val.z = qual ? __expf(local_qk_val.z - s_max) : 0.0f;\n        local_qk_val.w = qual ? __expf(local_qk_val.w - s_max) : 0.0f;\n\n        float sum_val = bdim <= 32 ?\n                            warpReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w) :\n                            blockReduceSum<float>(local_qk_val.x + local_qk_val.y + local_qk_val.z + local_qk_val.w);\n        if (tidx == 0) {\n            s_mean = sum_val + 1e-6f;\n            s_mean = __fdividef(1.0f, s_mean);\n        }\n        __syncthreads();\n        if (qual) {\n            local_qk_val.x    = local_qk_val.x * s_mean;\n            local_qk_val.y    = local_qk_val.y * s_mean;\n            local_qk_val.z    = local_qk_val.z * s_mean;\n            local_qk_val.w    = local_qk_val.w * s_mean;\n            qk_val.x          = T(local_qk_val.x);\n            qk_val.y          = T(local_qk_val.y);\n            qk_val.z          = T(local_qk_val.z);\n            qk_val.w          = T(local_qk_val.w);\n            qk_buf[qk_offset] = qk_val;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeMaskedSoftMaxWithRelPosBias(T*           qk_buf,\n                                       const T*     attn_mask,\n                                       const T*     relative_pos_bias,\n                                       const int    batch_size,\n                                       const int    num_head,\n                                       const int    window_num,\n                                       const int    window_len,\n                                       float        qk_scale,\n                                       hipStream_t stream)\n{\n    const int word_per_thread = 1;\n    dim3      grid((window_len + word_per_thread - 1) / word_per_thread, window_num * num_head, batch_size);\n    if ((window_len % 4 == 0) && window_len / 4 >= 32) {\n        dim3 block((window_len / 4 + 31) / 32 * 32);\n        if (std::is_same<T, float>::value) {\n            softmax_withRelPosBias_element4_kernel<float4, float>\n                <<<grid, block, 0, stream>>>((float4*)qk_buf,\n                                             (const float4*)attn_mask,\n                                             (const float4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_withRelPosBias_element4_kernel<half4, half>\n                <<<grid, block, 0, stream>>>((half4*)qk_buf,\n                                             (const half4*)attn_mask,\n                                             (const half4*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n#ifdef ENABLE_BF16\n        else {\n            dim3 block((window_len + 31) / 32 * 32);\n            softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                               attn_mask,\n                                                                               relative_pos_bias,\n                                                                               batch_size,\n                                                                               num_head,\n                                                                               window_num,\n                                                                               window_len,\n                                                                               window_len * window_len,\n                                                                               qk_scale);\n        }\n#endif\n    }\n    else if (window_len % 2 == 0) {\n        dim3 block((window_len / 2 + 31) / 32 * 32);\n        if (std::is_same<T, float>::value) {\n            softmax_withRelPosBias_element2_kernel<float2, float>\n                <<<grid, block, 0, stream>>>((float2*)qk_buf,\n                                             (const float2*)attn_mask,\n                                             (const float2*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n        else if (std::is_same<T, half>::value) {\n            softmax_withRelPosBias_element2_kernel<half2, half>\n                <<<grid, block, 0, stream>>>((half2*)qk_buf,\n                                             (const half2*)attn_mask,\n                                             (const half2*)relative_pos_bias,\n                                             batch_size,\n                                             num_head,\n                                             window_num,\n                                             window_len,\n                                             window_len * window_len,\n                                             qk_scale);\n        }\n#ifdef ENABLE_BF16\n        else {\n            dim3 block((window_len + 31) / 32 * 32);\n            softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                               attn_mask,\n                                                                               relative_pos_bias,\n                                                                               batch_size,\n                                                                               num_head,\n                                                                               window_num,\n                                                                               window_len,\n                                                                               window_len * window_len,\n                                                                               qk_scale);\n        }\n#endif\n    }\n    else {\n        dim3 block((window_len + 31) / 32 * 32);\n        softmax_withRelPosBias_element1_kernel<<<grid, block, 0, stream>>>(qk_buf,\n                                                                           attn_mask,\n                                                                           relative_pos_bias,\n                                                                           batch_size,\n                                                                           num_head,\n                                                                           window_num,\n                                                                           window_len,\n                                                                           window_len * window_len,\n                                                                           qk_scale);\n    }\n}\n\n#define INSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(T)                                                                      \\\n    template void invokeMaskedSoftMaxWithRelPosBias(T*           qk_buf,                                               \\\n                                                    const T*     attn_mask,                                            \\\n                                                    const T*     relative_pos_bias,                                    \\\n                                                    const int    batch_size,                                           \\\n                                                    const int    num_head,                                             \\\n                                                    const int    window_num,                                           \\\n                                                    const int    window_len,                                           \\\n                                                    const float  qk_scale,                                             \\\n                                                    hipStream_t stream)\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(float);\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(half);\n#ifdef ENABLE_BF16\nINSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS(__hip_bfloat16);\n#endif\n#undef INSTANTIATEMASKEDSOFTMAXWITHRELPOSBIAS\n\ntemplate<typename T>\n__global__ void transpose_attentions(\n    T* attentions_out, const T* attentions_in, size_t batch_size, size_t num_layers, size_t num_heads, size_t seq_len)\n{\n    // attentions_in  shape [B, H, S, S]\n    // attentions_out shape [B, L, H, S, S].\n    // Note that we write the L dimension as if it was index 0.\n    // In reality, the pointer has already been shifted to point to the correct layer.\n\n    const auto batch_idx = blockIdx.x;\n    const auto head_idx  = blockIdx.y;\n\n    const auto dst_offset = (batch_idx * num_layers * num_heads + head_idx) * seq_len * seq_len;\n    const auto src_offset = (batch_idx * num_heads + head_idx) * seq_len * seq_len;\n\n    for (auto x = threadIdx.x; x < seq_len * seq_len; x += blockDim.x) {\n        attentions_out[dst_offset + x] = attentions_in[src_offset + x];\n    }\n}\n\ntemplate<typename T>\nvoid invokeTransposeAttentions(Tensor& attentions_out, const Tensor& attentions_in, hipStream_t stream)\n{\n    const size_t batch_size = attentions_in.shape[0];\n    const size_t num_heads  = attentions_in.shape[1];\n    const size_t seq_len    = attentions_in.shape[2];\n    const size_t num_layers = attentions_out.shape[1];\n\n    const dim3 gridSize(batch_size, num_heads);\n    const dim3 blockSize(512);\n\n    transpose_attentions<<<gridSize, blockSize, 0, stream>>>(\n        attentions_out.getPtr<T>(), attentions_in.getPtr<const T>(), batch_size, num_layers, num_heads, seq_len);\n}\n\n#define INSTANTIATETRANSPOSEATTENTIONS(T)                                                                              \\\n    template void invokeTransposeAttentions<T>(                                                                        \\\n        Tensor & attentions_out, const Tensor& attentions_in, hipStream_t stream)\nINSTANTIATETRANSPOSEATTENTIONS(float);\nINSTANTIATETRANSPOSEATTENTIONS(half);\n#ifdef ENABLE_BF16\nINSTANTIATETRANSPOSEATTENTIONS(__hip_bfloat16);\n#endif\n#undef INSTANTIATETRANSPOSEATTENTIONS\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/unfused_attention_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/activation_int8_kernels.h\"\nnamespace fastertransformer {\n\n__inline__ __device__ half fast_tanh(half x)\n{\n#if defined(__CUDA_ARCH__) && (__CUDACC_VER_MAJOR__ >= 11) && (__CUDA_ARCH__ >= 750)\n    uint16_t raw = reinterpret_cast<uint16_t const&>(x);\n    asm volatile(\"tanh.approx.f16 %0, %1;\" : \"=h\"(raw) : \"h\"(raw));\n    half ret = reinterpret_cast<half const&>(raw);\n    return ret;\n#else\n    return half(tanhf(float(x)));\n#endif\n}\n\ntemplate<typename T>\n__inline__ __device__ T gelu(T x)\n{\n    float cdf = 0.5f * (1.0f + tanhf((0.7978845608028654f * (x + 0.044715f * x * x * x))));\n    return x * cdf;\n}\n\ntemplate<>\n__inline__ __device__ half gelu(half x)\n{\n    half val      = half(0.7978845608028654) * (x + half(0.044715) * x * x * x);\n    half fast_val = fast_tanh(val);\n    half cdf      = half(0.5) * (half(1.0) + fast_val);\n    return x * cdf;\n}\n\ntemplate<>\n__inline__ __device__ half2 gelu(half2 val)\n{\n    half2  val_pow3 = __hmul2(val, __hmul2(val, val));\n    float2 tmp_pow  = __half22float2(val_pow3);\n    float2 tmp      = __half22float2(val);\n\n    tmp.x = 0.5f * (1.0f + tanhf((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n    tmp.y = 0.5f * (1.0f + tanhf((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n    return __hmul2(val, __float22half2_rn(tmp));\n}\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4 as output\n// for per-channel-quantization weight\n__global__ void add_bias_gelu_COL32_int32I_int8O(int8_t*        out,\n                                                 const int32_t* input,\n                                                 const float*   bias,\n                                                 const int      m,\n                                                 const int      n,\n                                                 const float*   weight_amax,\n                                                 const float*   input_deQFactor_div127_ptr,\n                                                 const float*   out_scale_ptr)\n{\n\n    const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float out_scale              = __ldg(out_scale_ptr);\n\n    int    col_start = threadIdx.x << 2;\n    char4* outTmpPtr = (char4*)out;\n    char4  tmp;\n    int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    float  val;\n\n    const int4   input4  = __ldg(((const int4*)input) + outIdx);\n    const float4 weight4 = __ldg(((const float4*)weight_amax) + threadIdx.x);\n    const float4 bias4   = __ldg(((const float4*)bias) + threadIdx.x);\n\n    val   = static_cast<float>(input4.x) * weight4.x * input_deQFactor_div127 + bias4.x;\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(val * out_scale);\n\n    val   = static_cast<float>(input4.y) * weight4.y * input_deQFactor_div127 + bias4.y;\n    val   = gelu(val);\n    tmp.y = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(input4.z) * weight4.z * input_deQFactor_div127 + bias4.z;\n    val       = gelu(val);\n    tmp.z     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(input4.w) * weight4.w * input_deQFactor_div127 + bias4.w;\n    val       = gelu(val);\n    tmp.w     = float_to_int8_rn(val * out_scale);\n\n    outTmpPtr[outIdx] = tmp;\n}\n\n__global__ void add_bias_gelu_COL32_int32I_int8O(char4*        out,\n                                                 const int4*   input,\n                                                 const half2*  bias,\n                                                 const int     m,\n                                                 const int     n,\n                                                 const float4* weight_amax,\n                                                 const float*  input_deQFactor_div127_ptr,\n                                                 const float*  out_scale_ptr)\n{\n    const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float out_scale              = __ldg(out_scale_ptr);\n    int         col_start              = threadIdx.x << 2;\n    int         threadIdx2             = threadIdx.x << 1;\n    char4       tmp;\n    int         outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    float       val;\n\n    const int4   input4   = __ldg(input + outIdx);\n    const float4 weight4  = __ldg(weight_amax + threadIdx.x);\n    const half2  biasTmp  = __ldg(bias + threadIdx2);\n    const half2  biasTmp2 = __ldg(bias + threadIdx2 + 1);\n\n    val   = static_cast<float>(input4.x) * weight4.x * input_deQFactor_div127 + static_cast<float>(biasTmp.x);\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.y) * weight4.y * input_deQFactor_div127 + static_cast<float>(biasTmp.y);\n    val   = gelu(val);\n    tmp.y = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.z) * weight4.z * input_deQFactor_div127 + static_cast<float>(biasTmp2.x);\n    val   = gelu(val);\n    tmp.z = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.w) * weight4.w * input_deQFactor_div127 + static_cast<float>(biasTmp2.y);\n    val   = gelu(val);\n    tmp.w = float_to_int8_rn(out_scale * val);\n\n    out[outIdx] = tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32(int8_t*        out,\n                            const int32_t* in,\n                            const T*       bias,\n                            const int      m,\n                            const int      n,\n                            cudaStream_t   stream,\n                            const float*   weight_amax,\n                            const float*   input_deQFactor_div127_ptr,\n                            const float*   out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_gelu_COL32_int32I_int8O<<<grid, block, 0, stream>>>((char4*)out,\n                                                                     (const int4*)in,\n                                                                     (const half2*)bias,\n                                                                     m,\n                                                                     n,\n                                                                     (const float4*)weight_amax,\n                                                                     input_deQFactor_div127_ptr,\n                                                                     out_scale_ptr);\n    }\n    else {\n        add_bias_gelu_COL32_int32I_int8O<<<grid, block, 0, stream>>>(\n            out, in, (const float*)bias, m, n, weight_amax, input_deQFactor_div127_ptr, out_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasGeluCol32(int8_t*        out,\n                                     const int32_t* in,\n                                     const float*   bias,\n                                     const int      m,\n                                     const int      n,\n                                     cudaStream_t   stream,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr);\ntemplate void invokeAddBiasGeluCol32(int8_t*        out,\n                                     const int32_t* in,\n                                     const half*    bias,\n                                     const int      m,\n                                     const int      n,\n                                     cudaStream_t   stream,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr);\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_COL32_int8IO(int8_t*       out,\n                                           const int8_t* input,\n                                           const T*      bias,\n                                           const int     m,\n                                           const int     n,\n                                           const float*  input_deQFactor_ptr,\n                                           const float*  out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    // int col_start = threadIdx.x << 2;\n    char4* outTmpPtr   = (char4*)out;\n    char4* inputTmpPtr = (char4*)input;\n    char4  tmp;\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        float val;\n        tmp   = __ldg(inputTmpPtr + outIdx);\n        val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.y     = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.z     = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.w     = float_to_int8_rn(val * out_scale);\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32(int8_t*       out,\n                            const int8_t* in,\n                            const T*      bias,\n                            const int     m,\n                            const int     n,\n                            cudaStream_t  stream,\n                            const float*  input_deQFactor_ptr,\n                            const float*  out_scale_ptr)\n{\n    dim3 grid;\n    dim3 block;\n    if (n / 4 <= 1024) {\n        block.x = n / 4;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = m;\n    }\n\n    add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, in, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n}\n\ntemplate void invokeAddBiasGeluCol32(int8_t*       out,\n                                     const int8_t* in,\n                                     const float*  bias,\n                                     const int     m,\n                                     const int     n,\n                                     cudaStream_t  stream,\n                                     const float*  input_deQFactor_ptr,\n                                     const float*  out_scale_ptr);\n\ntemplate void invokeAddBiasGeluCol32(int8_t*       out,\n                                     const int8_t* in,\n                                     const half*   bias,\n                                     const int     m,\n                                     const int     n,\n                                     cudaStream_t  stream,\n                                     const float*  input_deQFactor_ptr,\n                                     const float*  out_scale_ptr);\n\n/*******************  invokeAddBiasGeluCol32_v2  ***********************/\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_COL32_int8IO(\n    int8_t* out, const T* bias, const int m, const int n, const float* input_deQFactor_ptr, const float* out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        char4* outTmpPtr = (char4*)out;\n        char4  tmp;\n        int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        float  val;\n        tmp   = __ldg(outTmpPtr + outIdx);\n        val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 1));\n        val   = gelu(val);\n        tmp.y = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 2));\n        val   = gelu(val);\n        tmp.z = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 3));\n        val   = gelu(val);\n        tmp.w = float_to_int8_rn(val * out_scale);\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<>\n__global__ void add_bias_gelu_COL32_int8IO(int8_t*      out,\n                                           const half*  bias,\n                                           const int    m,\n                                           const int    n,\n                                           const float* input_deQFactor_ptr,\n                                           const float* out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        char4* outTmpPtr   = (char4*)out;\n        char4* inputTmpPtr = (char4*)out;\n        char4  tmp;\n        int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        half   val;\n        half   input_deQFactor_half = half(input_deQFactor);\n        half   out_scale_half       = half(out_scale);\n        tmp                         = __ldg(inputTmpPtr + outIdx);\n\n        val   = static_cast<half>(tmp.x) * input_deQFactor_half + (__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.y) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.y     = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.z) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.z     = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.w) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.w     = float_to_int8_rn(float(val * out_scale_half));\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32_v2(int8_t*      out,\n                               const T*     bias,\n                               const int    m,\n                               const int    n,\n                               cudaStream_t stream,\n                               const float* input_deQFactor_ptr,\n                               const float* out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n    }\n    else {\n        add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasGeluCol32_v2(int8_t*      out,\n                                        const float* bias,\n                                        const int    m,\n                                        const int    n,\n                                        cudaStream_t stream,\n                                        const float* input_deQFactor_ptr,\n                                        const float* out_scale_ptr);\n\ntemplate void invokeAddBiasGeluCol32_v2(int8_t*      out,\n                                        const half*  bias,\n                                        const int    m,\n                                        const int    n,\n                                        cudaStream_t stream,\n                                        const float* input_deQFactor_ptr,\n                                        const float* out_scale_ptr);\n\n// add bias to matrix of m * n, row major\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_ROW_int8IO(int8_t*       out,\n                                         const int8_t* input,\n                                         const T*      bias,\n                                         const int     m,\n                                         const int     n,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    int    col_start   = threadIdx.x << 2;\n    char4* outTmpPtr   = (char4*)out;\n    char4* inputTmpPtr = (char4*)input;\n    char4  tmp;\n    int    outIdx = (blockIdx.x * n + col_start) >> 2;\n    float  val;\n    tmp   = __ldg(inputTmpPtr + outIdx);\n    val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.y     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.z     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.w     = float_to_int8_rn(val * out_scale);\n\n    outTmpPtr[outIdx] = tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluRow(int8_t*       out,\n                          const int8_t* in,\n                          const T*      bias,\n                          const int     m,\n                          const int     n,\n                          cudaStream_t  stream,\n                          const float*  input_deQFactor_ptr,\n                          const float*  out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n\n    add_bias_gelu_ROW_int8IO<<<grid, block, 0, stream>>>(out, in, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n}\n\ntemplate void invokeAddBiasGeluRow(int8_t*       out,\n                                   const int8_t* in,\n                                   const float*  bias,\n                                   const int     m,\n                                   const int     n,\n                                   cudaStream_t  stream,\n                                   const float*  input_deQFactor_ptr,\n                                   const float*  out_scale_ptr);\n\ntemplate void invokeAddBiasGeluRow(int8_t*       out,\n                                   const int8_t* in,\n                                   const half*   bias,\n                                   const int     m,\n                                   const int     n,\n                                   cudaStream_t  stream,\n                                   const float*  input_deQFactor_ptr,\n                                   const float*  out_scale_ptr);\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/activation_int8_kernels.h\"\nnamespace fastertransformer {\n\n__inline__ __device__ half fast_tanh(half x)\n{\n#if defined(__CUDA_ARCH__) && (__CUDACC_VER_MAJOR__ >= 11) && (__CUDA_ARCH__ >= 750)\n    uint16_t raw = reinterpret_cast<uint16_t const&>(x);\n    asm volatile(\"tanh.approx.f16 %0, %1;\" : \"=h\"(raw) : \"h\"(raw));\n    half ret = reinterpret_cast<half const&>(raw);\n    return ret;\n#else\n    return half(tanhf(float(x)));\n#endif\n}\n\ntemplate<typename T>\n__inline__ __device__ T gelu(T x)\n{\n    float cdf = 0.5f * (1.0f + tanhf((0.7978845608028654f * (x + 0.044715f * x * x * x))));\n    return x * cdf;\n}\n\ntemplate<>\n__inline__ __device__ half gelu(half x)\n{\n    half val      = half(0.7978845608028654) * (x + half(0.044715) * x * x * x);\n    half fast_val = fast_tanh(val);\n    half cdf      = half(0.5) * (half(1.0) + fast_val);\n    return x * cdf;\n}\n\ntemplate<>\n__inline__ __device__ half2 gelu(half2 val)\n{\n    half2  val_pow3 = __hmul2(val, __hmul2(val, val));\n    float2 tmp_pow  = __half22float2(val_pow3);\n    float2 tmp      = __half22float2(val);\n\n    tmp.x = 0.5f * (1.0f + tanhf((0.7978845608028654f * (tmp.x + 0.044715f * tmp_pow.x))));\n    tmp.y = 0.5f * (1.0f + tanhf((0.7978845608028654f * (tmp.y + 0.044715f * tmp_pow.y))));\n    return __hmul2(val, __float22half2_rn(tmp));\n}\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4 as output\n// for per-channel-quantization weight\n__global__ void add_bias_gelu_COL32_int32I_int8O(int8_t*        out,\n                                                 const int32_t* input,\n                                                 const float*   bias,\n                                                 const int      m,\n                                                 const int      n,\n                                                 const float*   weight_amax,\n                                                 const float*   input_deQFactor_div127_ptr,\n                                                 const float*   out_scale_ptr)\n{\n\n    const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float out_scale              = __ldg(out_scale_ptr);\n\n    int    col_start = threadIdx.x << 2;\n    char4* outTmpPtr = (char4*)out;\n    char4  tmp;\n    int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    float  val;\n\n    const int4   input4  = __ldg(((const int4*)input) + outIdx);\n    const float4 weight4 = __ldg(((const float4*)weight_amax) + threadIdx.x);\n    const float4 bias4   = __ldg(((const float4*)bias) + threadIdx.x);\n\n    val   = static_cast<float>(input4.x) * weight4.x * input_deQFactor_div127 + bias4.x;\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(val * out_scale);\n\n    val   = static_cast<float>(input4.y) * weight4.y * input_deQFactor_div127 + bias4.y;\n    val   = gelu(val);\n    tmp.y = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(input4.z) * weight4.z * input_deQFactor_div127 + bias4.z;\n    val       = gelu(val);\n    tmp.z     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(input4.w) * weight4.w * input_deQFactor_div127 + bias4.w;\n    val       = gelu(val);\n    tmp.w     = float_to_int8_rn(val * out_scale);\n\n    outTmpPtr[outIdx] = tmp;\n}\n\n__global__ void add_bias_gelu_COL32_int32I_int8O(char4*        out,\n                                                 const int4*   input,\n                                                 const half2*  bias,\n                                                 const int     m,\n                                                 const int     n,\n                                                 const float4* weight_amax,\n                                                 const float*  input_deQFactor_div127_ptr,\n                                                 const float*  out_scale_ptr)\n{\n    const float input_deQFactor_div127 = __ldg(input_deQFactor_div127_ptr);\n    const float out_scale              = __ldg(out_scale_ptr);\n    int         col_start              = threadIdx.x << 2;\n    int         threadIdx2             = threadIdx.x << 1;\n    char4       tmp;\n    int         outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    float       val;\n\n    const int4   input4   = __ldg(input + outIdx);\n    const float4 weight4  = __ldg(weight_amax + threadIdx.x);\n    const half2  biasTmp  = __ldg(bias + threadIdx2);\n    const half2  biasTmp2 = __ldg(bias + threadIdx2 + 1);\n\n    val   = static_cast<float>(input4.x) * weight4.x * input_deQFactor_div127 + static_cast<float>(biasTmp.x);\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.y) * weight4.y * input_deQFactor_div127 + static_cast<float>(biasTmp.y);\n    val   = gelu(val);\n    tmp.y = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.z) * weight4.z * input_deQFactor_div127 + static_cast<float>(biasTmp2.x);\n    val   = gelu(val);\n    tmp.z = float_to_int8_rn(out_scale * val);\n\n    val   = static_cast<float>(input4.w) * weight4.w * input_deQFactor_div127 + static_cast<float>(biasTmp2.y);\n    val   = gelu(val);\n    tmp.w = float_to_int8_rn(out_scale * val);\n\n    out[outIdx] = tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32(int8_t*        out,\n                            const int32_t* in,\n                            const T*       bias,\n                            const int      m,\n                            const int      n,\n                            hipStream_t   stream,\n                            const float*   weight_amax,\n                            const float*   input_deQFactor_div127_ptr,\n                            const float*   out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_gelu_COL32_int32I_int8O<<<grid, block, 0, stream>>>((char4*)out,\n                                                                     (const int4*)in,\n                                                                     (const half2*)bias,\n                                                                     m,\n                                                                     n,\n                                                                     (const float4*)weight_amax,\n                                                                     input_deQFactor_div127_ptr,\n                                                                     out_scale_ptr);\n    }\n    else {\n        add_bias_gelu_COL32_int32I_int8O<<<grid, block, 0, stream>>>(\n            out, in, (const float*)bias, m, n, weight_amax, input_deQFactor_div127_ptr, out_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasGeluCol32(int8_t*        out,\n                                     const int32_t* in,\n                                     const float*   bias,\n                                     const int      m,\n                                     const int      n,\n                                     hipStream_t   stream,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr);\ntemplate void invokeAddBiasGeluCol32(int8_t*        out,\n                                     const int32_t* in,\n                                     const half*    bias,\n                                     const int      m,\n                                     const int      n,\n                                     hipStream_t   stream,\n                                     const float*   weight_amax,\n                                     const float*   input_deQFactor_div127_ptr,\n                                     const float*   out_scale_ptr);\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_COL32_int8IO(int8_t*       out,\n                                           const int8_t* input,\n                                           const T*      bias,\n                                           const int     m,\n                                           const int     n,\n                                           const float*  input_deQFactor_ptr,\n                                           const float*  out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    // int col_start = threadIdx.x << 2;\n    char4* outTmpPtr   = (char4*)out;\n    char4* inputTmpPtr = (char4*)input;\n    char4  tmp;\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        int   outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        float val;\n        tmp   = __ldg(inputTmpPtr + outIdx);\n        val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.y     = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.z     = float_to_int8_rn(val * out_scale);\n\n        col_start = col_start + 1;\n        val       = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.w     = float_to_int8_rn(val * out_scale);\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32(int8_t*       out,\n                            const int8_t* in,\n                            const T*      bias,\n                            const int     m,\n                            const int     n,\n                            hipStream_t  stream,\n                            const float*  input_deQFactor_ptr,\n                            const float*  out_scale_ptr)\n{\n    dim3 grid;\n    dim3 block;\n    if (n / 4 <= 1024) {\n        block.x = n / 4;\n        grid.x  = m;\n    }\n    else {\n        block.x = 1024;\n        grid.x  = m;\n    }\n\n    add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, in, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n}\n\ntemplate void invokeAddBiasGeluCol32(int8_t*       out,\n                                     const int8_t* in,\n                                     const float*  bias,\n                                     const int     m,\n                                     const int     n,\n                                     hipStream_t  stream,\n                                     const float*  input_deQFactor_ptr,\n                                     const float*  out_scale_ptr);\n\ntemplate void invokeAddBiasGeluCol32(int8_t*       out,\n                                     const int8_t* in,\n                                     const half*   bias,\n                                     const int     m,\n                                     const int     n,\n                                     hipStream_t  stream,\n                                     const float*  input_deQFactor_ptr,\n                                     const float*  out_scale_ptr);\n\n/*******************  invokeAddBiasGeluCol32_v2  ***********************/\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_COL32_int8IO(\n    int8_t* out, const T* bias, const int m, const int n, const float* input_deQFactor_ptr, const float* out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        char4* outTmpPtr = (char4*)out;\n        char4  tmp;\n        int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        float  val;\n        tmp   = __ldg(outTmpPtr + outIdx);\n        val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 1));\n        val   = gelu(val);\n        tmp.y = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 2));\n        val   = gelu(val);\n        tmp.z = float_to_int8_rn(val * out_scale);\n\n        val   = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start + 3));\n        val   = gelu(val);\n        tmp.w = float_to_int8_rn(val * out_scale);\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\n// add bias to matrix of m * n, CUBLASLT_ORDER_COL32\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<>\n__global__ void add_bias_gelu_COL32_int8IO(int8_t*      out,\n                                           const half*  bias,\n                                           const int    m,\n                                           const int    n,\n                                           const float* input_deQFactor_ptr,\n                                           const float* out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    for (int col_start = threadIdx.x << 2; col_start < n; col_start += (blockDim.x << 2)) {\n        char4* outTmpPtr   = (char4*)out;\n        char4* inputTmpPtr = (char4*)out;\n        char4  tmp;\n        int    outIdx = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n        half   val;\n        half   input_deQFactor_half = half(input_deQFactor);\n        half   out_scale_half       = half(out_scale);\n        tmp                         = __ldg(inputTmpPtr + outIdx);\n\n        val   = static_cast<half>(tmp.x) * input_deQFactor_half + (__ldg(bias + col_start));\n        val   = gelu(val);\n        tmp.x = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.y) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.y     = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.z) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.z     = float_to_int8_rn(float(val * out_scale_half));\n\n        col_start = col_start + 1;\n        val       = static_cast<half>(tmp.w) * input_deQFactor_half + (__ldg(bias + col_start));\n        val       = gelu(val);\n        tmp.w     = float_to_int8_rn(float(val * out_scale_half));\n\n        outTmpPtr[outIdx] = tmp;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluCol32_v2(int8_t*      out,\n                               const T*     bias,\n                               const int    m,\n                               const int    n,\n                               hipStream_t stream,\n                               const float* input_deQFactor_ptr,\n                               const float* out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    if (block.x > 1024) {\n        block.x = 1024;\n    }\n\n    if (sizeof(T) == sizeof(half)) {\n        add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n    }\n    else {\n        add_bias_gelu_COL32_int8IO<<<grid, block, 0, stream>>>(out, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n    }\n}\n\ntemplate void invokeAddBiasGeluCol32_v2(int8_t*      out,\n                                        const float* bias,\n                                        const int    m,\n                                        const int    n,\n                                        hipStream_t stream,\n                                        const float* input_deQFactor_ptr,\n                                        const float* out_scale_ptr);\n\ntemplate void invokeAddBiasGeluCol32_v2(int8_t*      out,\n                                        const half*  bias,\n                                        const int    m,\n                                        const int    n,\n                                        hipStream_t stream,\n                                        const float* input_deQFactor_ptr,\n                                        const float* out_scale_ptr);\n\n// add bias to matrix of m * n, row major\n// grid, thread = (m), (n/4)\n// using char4\n// for per-tensor-quantization weight\ntemplate<typename T>\n__global__ void add_bias_gelu_ROW_int8IO(int8_t*       out,\n                                         const int8_t* input,\n                                         const T*      bias,\n                                         const int     m,\n                                         const int     n,\n                                         const float*  input_deQFactor_ptr,\n                                         const float*  out_scale_ptr)\n{\n\n    const float input_deQFactor = __ldg(input_deQFactor_ptr);\n    const float out_scale       = __ldg(out_scale_ptr);\n\n    int    col_start   = threadIdx.x << 2;\n    char4* outTmpPtr   = (char4*)out;\n    char4* inputTmpPtr = (char4*)input;\n    char4  tmp;\n    int    outIdx = (blockIdx.x * n + col_start) >> 2;\n    float  val;\n    tmp   = __ldg(inputTmpPtr + outIdx);\n    val   = static_cast<float>(tmp.x) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val   = gelu(val);\n    tmp.x = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.y) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.y     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.z) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.z     = float_to_int8_rn(val * out_scale);\n\n    col_start = col_start + 1;\n    val       = static_cast<float>(tmp.w) * input_deQFactor + static_cast<float>(__ldg(bias + col_start));\n    val       = gelu(val);\n    tmp.w     = float_to_int8_rn(val * out_scale);\n\n    outTmpPtr[outIdx] = tmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasGeluRow(int8_t*       out,\n                          const int8_t* in,\n                          const T*      bias,\n                          const int     m,\n                          const int     n,\n                          hipStream_t  stream,\n                          const float*  input_deQFactor_ptr,\n                          const float*  out_scale_ptr)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n\n    add_bias_gelu_ROW_int8IO<<<grid, block, 0, stream>>>(out, in, bias, m, n, input_deQFactor_ptr, out_scale_ptr);\n}\n\ntemplate void invokeAddBiasGeluRow(int8_t*       out,\n                                   const int8_t* in,\n                                   const float*  bias,\n                                   const int     m,\n                                   const int     n,\n                                   hipStream_t  stream,\n                                   const float*  input_deQFactor_ptr,\n                                   const float*  out_scale_ptr);\n\ntemplate void invokeAddBiasGeluRow(int8_t*       out,\n                                   const int8_t* in,\n                                   const half*   bias,\n                                   const int     m,\n                                   const int     n,\n                                   hipStream_t  stream,\n                                   const float*  input_deQFactor_ptr,\n                                   const float*  out_scale_ptr);\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/activation_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <cuda_fp16.h>\n#include <cuda_runtime.h>\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/logprob_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/logger.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void log_probs_kernel(float*       log_probs,\n                                 const T*     logits,\n                                 const int*   ids,\n                                 const int*   lengths,\n                                 const size_t max_input_length,\n                                 const size_t batch_size,\n                                 const size_t vocab_size,\n                                 const size_t vocab_size_padded,\n                                 bool         batch_first)\n{\n    // Calculate the log probability from logits.\n    //   log_probs[t, :] = log(softmax(logits))[ids[t + 1, :]]\n    //\n    // log_probs: [max_length - 1, batch_size] or [batch_size, max_length -1],\n    //     log probabilities of each token.\n    // logits: [max_length, batch_size, vocab_size_padded] or [batch_size, max_length, vocab_size_padded]\n    // lengths: [batch_size], sequence lengths\n    // ids: [max_length, batch_size], token ids.\n    // batch_size: [1], batch_size. in case of beam > 1, batch x beam.\n    // vocab_size: [1], vocab_size,\n    // vocab_size: [1], vocab_size_padded, padded vocab size.\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int tidx = threadIdx.x;                            // vocab dim\n    int bidx = batch_first ? blockIdx.x : blockIdx.y;  // batch dim\n    int step = batch_first ? blockIdx.y : blockIdx.x;  // step dim\n\n    __shared__ float s_max_logit;\n\n    if (bidx < batch_size && step < lengths[bidx] - 1) {\n        // reposition logits to data for the current batch.\n        int step_offset  = batch_first ? step * vocab_size_padded : step * batch_size * vocab_size_padded;\n        int batch_offset = batch_first ? bidx * max_input_length * vocab_size_padded : bidx * vocab_size_padded;\n        logits += step_offset + batch_offset;\n\n        // Find max(logits).\n        float local_max = -MAX_T_VAL;\n        float val       = -MAX_T_VAL;\n        for (int i = tidx; i < vocab_size; i += blockDim.x) {\n            val       = static_cast<float>(logits[i]);\n            local_max = fmax(local_max, val);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (tidx == 0) {\n            s_max_logit = max_val;\n        }\n        __syncthreads();\n\n        // Calculate the denominator: sum_i exp(logits[i])\n        float local_sum_exp = 0.0f;\n        for (int i = tidx; i < vocab_size; i += blockDim.x) {\n            val = __expf(static_cast<float>(logits[i]) - s_max_logit);\n            local_sum_exp += val;\n        }\n\n        float sum_exp = blockDim.x <= 32 ? warpReduceSum(local_sum_exp) : blockReduceSum<float>(local_sum_exp);\n        if (tidx == 0) {\n            int idx = batch_first ? step + bidx * (max_input_length - 1) : step * batch_size + bidx;\n            // log_probs[step, ...] is the log probability of a token at step t + 1.\n            int token_idx  = batch_first ? step + 1 + bidx * max_input_length : (step + 1) * batch_size + bidx;\n            log_probs[idx] = static_cast<float>(logits[ids[token_idx]]) - s_max_logit - __logf(sum_exp + 1e-9f);\n        }\n    }\n}\n\n__global__ void accumulate_log_probs(float*       cum_log_probs,\n                                     const float* log_probs,\n                                     const int*   lengths,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const bool   batch_first)\n{\n    // Accumulate the log probability along with the sequence dimension.\n    //   cum_log_probs[j] = sum_i log(softmax(logits))[ids[i,j]]\n    //\n    // cum_log_probs: [batch_size], cumulative log probability\n    // log_probs: [max_length - 1, batch_size] or [batch_size, max_length - 1],\n    //   log probability of each token\n    // lengths: [batch_size], sequence lengths\n    // batch_size: [1], batch_size. in case of beam > 1, batch x beam.\n\n    int bidx = blockIdx.x;   // batch dim\n    int tidx = threadIdx.x;  // step dim\n\n    if (bidx < batch_size) {\n        int length = lengths[bidx];\n        // reposition logits to data for the current batch.\n        log_probs += batch_first ? bidx * (max_input_length - 1) : bidx;\n        int   stride      = batch_first ? 1 : batch_size;  // stride along with seq dim.\n        float local_accum = 0.0f;\n        for (int step = tidx; step < length - 1; step += blockDim.x) {\n            local_accum += static_cast<float>(log_probs[step * stride]);\n        }\n        float accum = blockDim.x <= 32 ? warpReduceSum(local_accum) : blockReduceSum<float>(local_accum);\n        if (tidx == 0) {\n            cum_log_probs[bidx] = accum;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLogProbFromLogits(float*       cum_log_probs,\n                             const T*     logits,\n                             const int*   input_ids,\n                             const int*   input_lengths,\n                             const size_t max_input_length,\n                             const size_t batch_size,\n                             const size_t vocab_size,\n                             const size_t vocab_size_padded,\n                             void*        workspace,\n                             const size_t workspace_size,\n                             cudaStream_t stream,\n                             const bool   batch_first)\n{\n    // A batched version of log prob computation.\n    //\n    // cum_log_probs: [batch_size]\n    // logits: [max_input_length, batch_size, vocab_size] or [batch_size, max_input_length, vocab_size]\n    // input_ids: [max_input_length, batch_size] or [max_input_length, batch_size]\n    // input_lengths: [batch_size]\n    // workspace: workspace buffer of size at least sizeof(float) * max_input_length * batch_size.\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    // block_size should be multiple of 32 to use warpReduceMax.\n    const int block_size = vocab_size < 1024 ? (vocab_size + 31) / 32 * 32 : 1024;\n    assert(block_size % 32 == 0);\n    assert(workspace != nullptr && workspace_size >= sizeof(float) * max_input_length * batch_size);\n    assert(vocab_size <= vocab_size_padded);\n\n    float* log_probs = reinterpret_cast<float*>(workspace);\n    int    gx        = batch_first ? batch_size : max_input_length - 1;\n    int    gy        = batch_first ? max_input_length - 1 : batch_size;\n    dim3   grid(gx, gy);\n    log_probs_kernel<T><<<grid, block_size, 0, stream>>>(log_probs,\n                                                         logits,\n                                                         input_ids,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         batch_size,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         batch_first);\n    accumulate_log_probs<<<batch_size, block_size, 0, stream>>>(\n        cum_log_probs, log_probs, input_lengths, max_input_length, batch_size, batch_first);\n}\n\ntemplate void invokeLogProbFromLogits(float*       cum_log_probs,\n                                      const float* logits,\n                                      const int*   input_ids,\n                                      const int*   input_lengths,\n                                      const size_t max_input_length,\n                                      const size_t batch_size,\n                                      const size_t vocab_size,\n                                      const size_t vocab_size_padded,\n                                      void*        workspace,\n                                      const size_t workspace_size,\n                                      cudaStream_t stream,\n                                      const bool   batch_first);\n\ntemplate void invokeLogProbFromLogits(float*       cum_log_probs,\n                                      const half*  logits,\n                                      const int*   input_ids,\n                                      const int*   input_lengths,\n                                      const size_t max_input_length,\n                                      const size_t batch_size,\n                                      const size_t vocab_size,\n                                      const size_t vocab_size_padded,\n                                      void*        workspace,\n                                      const size_t workspace_size,\n                                      cudaStream_t stream,\n                                      const bool   batch_first);\n}  // end of namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <hip/hip_fp16.h>\n#include <hip/hip_runtime.h>\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/logprob_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/logger.h\"\n\nnamespace fastertransformer {\n\ntemplate<typename T>\n__global__ void log_probs_kernel(float*       log_probs,\n                                 const T*     logits,\n                                 const int*   ids,\n                                 const int*   lengths,\n                                 const size_t max_input_length,\n                                 const size_t batch_size,\n                                 const size_t vocab_size,\n                                 const size_t vocab_size_padded,\n                                 bool         batch_first)\n{\n    // Calculate the log probability from logits.\n    //   log_probs[t, :] = log(softmax(logits))[ids[t + 1, :]]\n    //\n    // log_probs: [max_length - 1, batch_size] or [batch_size, max_length -1],\n    //     log probabilities of each token.\n    // logits: [max_length, batch_size, vocab_size_padded] or [batch_size, max_length, vocab_size_padded]\n    // lengths: [batch_size], sequence lengths\n    // ids: [max_length, batch_size], token ids.\n    // batch_size: [1], batch_size. in case of beam > 1, batch x beam.\n    // vocab_size: [1], vocab_size,\n    // vocab_size: [1], vocab_size_padded, padded vocab size.\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    int tidx = threadIdx.x;                            // vocab dim\n    int bidx = batch_first ? blockIdx.x : blockIdx.y;  // batch dim\n    int step = batch_first ? blockIdx.y : blockIdx.x;  // step dim\n\n    __shared__ float s_max_logit;\n\n    if (bidx < batch_size && step < lengths[bidx] - 1) {\n        // reposition logits to data for the current batch.\n        int step_offset  = batch_first ? step * vocab_size_padded : step * batch_size * vocab_size_padded;\n        int batch_offset = batch_first ? bidx * max_input_length * vocab_size_padded : bidx * vocab_size_padded;\n        logits += step_offset + batch_offset;\n\n        // Find max(logits).\n        float local_max = -MAX_T_VAL;\n        float val       = -MAX_T_VAL;\n        for (int i = tidx; i < vocab_size; i += blockDim.x) {\n            val       = static_cast<float>(logits[i]);\n            local_max = fmax(local_max, val);\n        }\n\n        float max_val = blockDim.x <= 32 ? warpReduceMax(local_max) : blockReduceMax<float>(local_max);\n        if (tidx == 0) {\n            s_max_logit = max_val;\n        }\n        __syncthreads();\n\n        // Calculate the denominator: sum_i exp(logits[i])\n        float local_sum_exp = 0.0f;\n        for (int i = tidx; i < vocab_size; i += blockDim.x) {\n            val = __expf(static_cast<float>(logits[i]) - s_max_logit);\n            local_sum_exp += val;\n        }\n\n        float sum_exp = blockDim.x <= 32 ? warpReduceSum(local_sum_exp) : blockReduceSum<float>(local_sum_exp);\n        if (tidx == 0) {\n            int idx = batch_first ? step + bidx * (max_input_length - 1) : step * batch_size + bidx;\n            // log_probs[step, ...] is the log probability of a token at step t + 1.\n            int token_idx  = batch_first ? step + 1 + bidx * max_input_length : (step + 1) * batch_size + bidx;\n            log_probs[idx] = static_cast<float>(logits[ids[token_idx]]) - s_max_logit - __logf(sum_exp + 1e-9f);\n        }\n    }\n}\n\n__global__ void accumulate_log_probs(float*       cum_log_probs,\n                                     const float* log_probs,\n                                     const int*   lengths,\n                                     const size_t max_input_length,\n                                     const size_t batch_size,\n                                     const bool   batch_first)\n{\n    // Accumulate the log probability along with the sequence dimension.\n    //   cum_log_probs[j] = sum_i log(softmax(logits))[ids[i,j]]\n    //\n    // cum_log_probs: [batch_size], cumulative log probability\n    // log_probs: [max_length - 1, batch_size] or [batch_size, max_length - 1],\n    //   log probability of each token\n    // lengths: [batch_size], sequence lengths\n    // batch_size: [1], batch_size. in case of beam > 1, batch x beam.\n\n    int bidx = blockIdx.x;   // batch dim\n    int tidx = threadIdx.x;  // step dim\n\n    if (bidx < batch_size) {\n        int length = lengths[bidx];\n        // reposition logits to data for the current batch.\n        log_probs += batch_first ? bidx * (max_input_length - 1) : bidx;\n        int   stride      = batch_first ? 1 : batch_size;  // stride along with seq dim.\n        float local_accum = 0.0f;\n        for (int step = tidx; step < length - 1; step += blockDim.x) {\n            local_accum += static_cast<float>(log_probs[step * stride]);\n        }\n        float accum = blockDim.x <= 32 ? warpReduceSum(local_accum) : blockReduceSum<float>(local_accum);\n        if (tidx == 0) {\n            cum_log_probs[bidx] = accum;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeLogProbFromLogits(float*       cum_log_probs,\n                             const T*     logits,\n                             const int*   input_ids,\n                             const int*   input_lengths,\n                             const size_t max_input_length,\n                             const size_t batch_size,\n                             const size_t vocab_size,\n                             const size_t vocab_size_padded,\n                             void*        workspace,\n                             const size_t workspace_size,\n                             hipStream_t stream,\n                             const bool   batch_first)\n{\n    // A batched version of log prob computation.\n    //\n    // cum_log_probs: [batch_size]\n    // logits: [max_input_length, batch_size, vocab_size] or [batch_size, max_input_length, vocab_size]\n    // input_ids: [max_input_length, batch_size] or [max_input_length, batch_size]\n    // input_lengths: [batch_size]\n    // workspace: workspace buffer of size at least sizeof(float) * max_input_length * batch_size.\n\n    FT_LOG_DEBUG(__PRETTY_FUNCTION__);\n    // block_size should be multiple of 32 to use warpReduceMax.\n    const int block_size = vocab_size < 1024 ? (vocab_size + 31) / 32 * 32 : 1024;\n    assert(block_size % 32 == 0);\n    assert(workspace != nullptr && workspace_size >= sizeof(float) * max_input_length * batch_size);\n    assert(vocab_size <= vocab_size_padded);\n\n    float* log_probs = reinterpret_cast<float*>(workspace);\n    int    gx        = batch_first ? batch_size : max_input_length - 1;\n    int    gy        = batch_first ? max_input_length - 1 : batch_size;\n    dim3   grid(gx, gy);\n    log_probs_kernel<T><<<grid, block_size, 0, stream>>>(log_probs,\n                                                         logits,\n                                                         input_ids,\n                                                         input_lengths,\n                                                         max_input_length,\n                                                         batch_size,\n                                                         vocab_size,\n                                                         vocab_size_padded,\n                                                         batch_first);\n    accumulate_log_probs<<<batch_size, block_size, 0, stream>>>(\n        cum_log_probs, log_probs, input_lengths, max_input_length, batch_size, batch_first);\n}\n\ntemplate void invokeLogProbFromLogits(float*       cum_log_probs,\n                                      const float* logits,\n                                      const int*   input_ids,\n                                      const int*   input_lengths,\n                                      const size_t max_input_length,\n                                      const size_t batch_size,\n                                      const size_t vocab_size,\n                                      const size_t vocab_size_padded,\n                                      void*        workspace,\n                                      const size_t workspace_size,\n                                      hipStream_t stream,\n                                      const bool   batch_first);\n\ntemplate void invokeLogProbFromLogits(float*       cum_log_probs,\n                                      const half*  logits,\n                                      const int*   input_ids,\n                                      const int*   input_lengths,\n                                      const size_t max_input_length,\n                                      const size_t batch_size,\n                                      const size_t vocab_size,\n                                      const size_t vocab_size_padded,\n                                      void*        workspace,\n                                      const size_t workspace_size,\n                                      hipStream_t stream,\n                                      const bool   batch_first);\n}  // end of namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/logprob_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/transpose_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_kernel(char4*       dst,\n                                       const int4*  src,\n                                       const int    batch_size,\n                                       const int    seq_len,\n                                       const int    head_num,\n                                       const int    size_per_head,\n                                       const float* v_buf_addBias_deQFactor,\n                                       const float* qk_afterSM_deQFactor,\n                                       const float* out_scale_ptr,\n                                       const int    batch_size_x_seq_len,\n                                       const int    seq_len_x_size_per_head)\n{\n    const float scale      = __ldg(v_buf_addBias_deQFactor) * __ldg(qk_afterSM_deQFactor) * __ldg(out_scale_ptr);\n    int         threadIdx4 = threadIdx.x << 2;\n    int         batch_id   = blockIdx.y;\n    int         seq_id     = blockIdx.x;\n    int         head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m = 32*batch_size*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    // int COL32_col = mk_col >> 5;\n    int outIdx = ((mk_col & 0xffffffe0) * batch_size_x_seq_len + COL32_row) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    COL32_row = (mk_row << 5) + (mk_col & 31);\n    // COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (mk_col & 0xffffffe0) * seq_len + COL32_row) >> 2;\n    char4 tmp;\n\n    int4 srcTmp4 = __ldg(src + inIdx);\n    tmp.x        = float_to_int8_rn(srcTmp4.x * scale);\n    tmp.y        = float_to_int8_rn(srcTmp4.y * scale);\n    tmp.z        = float_to_int8_rn(srcTmp4.z * scale);\n    tmp.w        = float_to_int8_rn(srcTmp4.w * scale);\n    dst[outIdx]  = tmp;\n}\n\nvoid invokeTransposeCOL32(int8_t*      dst,\n                          const int*   src,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* v_buf_addBias_deQFactor,\n                          const float* qk_afterSM_deQFactor,\n                          const float* out_scale_ptr,\n                          cudaStream_t stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        (char4*)dst,\n        (const int4*)src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        v_buf_addBias_deQFactor,\n        qk_afterSM_deQFactor,\n        out_scale_ptr,\n        batch_size * seq_len,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_kernel(int8_t*       dst,\n                                       const int8_t* src,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  bmm2_deQFactor,\n                                       const float*  out_scale_ptr,\n                                       const int     batch_size_x_seq_len,\n                                       const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m = 32*batch_size*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    int COL32_col = mk_col >> 5;\n    int outIdx    = ((COL32_col << 5) * batch_size_x_seq_len + COL32_row) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    COL32_row = (mk_row << 5) + (mk_col & 31);\n    COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n    const char4* src_ptr4 = (const char4*)src;\n    char4*       dst_ptr4 = (char4*)dst;\n    dst_ptr4[outIdx]      = __ldg(src_ptr4 + inIdx);\n}\n\nvoid invokeTransposeCOL32(int8_t*       dst,\n                          const int8_t* src,\n                          const int     batch_size,\n                          const int     seq_len,\n                          const int     head_num,\n                          const int     size_per_head,\n                          const float*  bmm2_deQFactor,\n                          const float*  out_scale_ptr,\n                          cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        batch_size * seq_len,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_rebuild_padding_kernel(int8_t*        dst,\n                                                       const int32_t* src,\n                                                       const int*     sequence_id_map,\n                                                       const int      valid_word_num,\n                                                       const int      batch_size,\n                                                       const int      seq_len,\n                                                       const int      head_num,\n                                                       const int      size_per_head,\n                                                       const float*   v_buf_addBias_deQFactor,\n                                                       const float*   qk_afterSM_deQFactor,\n                                                       const float*   out_scale_ptr,\n                                                       const int      seq_len_x_size_per_head)\n{\n    const float scale      = __ldg(v_buf_addBias_deQFactor) * __ldg(qk_afterSM_deQFactor) * __ldg(out_scale_ptr);\n    int         threadIdx4 = threadIdx.x << 2;\n    int         batch_id   = blockIdx.y;\n    int         seq_id     = blockIdx.x;\n    int         head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m = 32*valid_word_num\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n        int outIdx    = ((COL32_col << 5) * valid_word_num + COL32_row) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        COL32_row = (mk_row << 5) + (mk_col & 31);\n        COL32_col = mk_col >> 5;\n\n        int inIdx = (batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row;\n        char4 tmp;\n        tmp.x            = float_to_int8_rn(__ldg(src + inIdx) * scale);\n        tmp.y            = float_to_int8_rn(__ldg(src + inIdx + 1) * scale);\n        tmp.z            = float_to_int8_rn(__ldg(src + inIdx + 2) * scale);\n        tmp.w            = float_to_int8_rn(__ldg(src + inIdx + 3) * scale);\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = tmp;\n    }\n}\n\nvoid invokeTransposeCOL32RebuildPadding(int8_t*      dst,\n                                        const int*   src,\n                                        const int*   sequence_id_map,\n                                        const int    valid_word_num,\n                                        const int    batch_size,\n                                        const int    seq_len,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        const float* v_buf_addBias_deQFactor,\n                                        const float* qk_afterSM_deQFactor,\n                                        const float* out_scale_ptr,\n                                        cudaStream_t stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_rebuild_padding_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        sequence_id_map,\n        valid_word_num,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        v_buf_addBias_deQFactor,\n        qk_afterSM_deQFactor,\n        out_scale_ptr,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_rebuild_padding_kernel(int8_t*       dst,\n                                                       const int8_t* src,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  bmm2_deQFactor,\n                                                       const float*  out_scale_ptr,\n                                                       const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m = 32*valid_word_num\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n        int outIdx    = ((COL32_col << 5) * valid_word_num + COL32_row) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        COL32_row = (mk_row << 5) + (mk_col & 31);\n        COL32_col = mk_col >> 5;\n\n        int inIdx =\n            ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n\n        const char4* src_ptr4 = (const char4*)src;\n\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = __ldg(src_ptr4 + inIdx);\n    }\n}\n\nvoid invokeTransposeCOL32RebuildPadding(int8_t*       dst,\n                                        const int8_t* src,\n                                        const int*    sequence_id_map,\n                                        const int     valid_word_num,\n                                        const int     batch_size,\n                                        const int     seq_len,\n                                        const int     head_num,\n                                        const int     size_per_head,\n                                        const float*  bmm2_deQFactor,\n                                        const float*  out_scale_ptr,\n                                        cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_rebuild_padding_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        sequence_id_map,\n        valid_word_num,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, row major\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_ROW_kernel(int8_t*       dst,\n                                           const int8_t* src,\n                                           const int     batch_size,\n                                           const int     seq_len,\n                                           const int     head_num,\n                                           const int     size_per_head,\n                                           const float*  bmm2_deQFactor,\n                                           const float*  out_scale_ptr,\n                                           const int     head_num_x_size_per_head,\n                                           const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    int outIdx = (mk_row * head_num_x_size_per_head + mk_col) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    int COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n    const char4* src_ptr4 = (const char4*)src;\n    char4*       dst_ptr4 = (char4*)dst;\n    dst_ptr4[outIdx]      = __ldg(src_ptr4 + inIdx);\n}\n\nvoid invokeTransposeCOL32ToRow(int8_t*       dst,\n                               const int8_t* src,\n                               const int     batch_size,\n                               const int     seq_len,\n                               const int     head_num,\n                               const int     size_per_head,\n                               const float*  bmm2_deQFactor,\n                               const float*  out_scale_ptr,\n                               cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_ROW_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        head_num * size_per_head,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, row major\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_ROW_rebuild_padding_kernel(int8_t*       dst,\n                                                           const int8_t* src,\n                                                           const int*    sequence_id_map,\n                                                           const int     valid_word_num,\n                                                           const int     batch_size,\n                                                           const int     seq_len,\n                                                           const int     head_num,\n                                                           const int     size_per_head,\n                                                           const float*  bmm2_deQFactor,\n                                                           const float*  out_scale_ptr,\n                                                           const int     seq_len_x_size_per_head,\n                                                           const int     head_num_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        int outIdx = (mk_row * head_num_x_size_per_head + mk_col) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n\n        int inIdx =\n            ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n\n        const char4* src_ptr4 = (const char4*)src;\n\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = __ldg(src_ptr4 + inIdx);\n    }\n}\n\nvoid invokeTransposeCOL32ToRowRebuildPadding(int8_t*       dst,\n                                             const int8_t* src,\n                                             const int*    sequence_id_map,\n                                             const int     valid_word_num,\n                                             const int     batch_size,\n                                             const int     seq_len,\n                                             const int     head_num,\n                                             const int     size_per_head,\n                                             const float*  bmm2_deQFactor,\n                                             const float*  out_scale_ptr,\n                                             cudaStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_ROW_rebuild_padding_kernel<<<dim3(seq_len, batch_size),\n                                                 dim3(size_per_head / 4, head_num),\n                                                 0,\n                                                 stream>>>(dst,\n                                                           src,\n                                                           sequence_id_map,\n                                                           valid_word_num,\n                                                           batch_size,\n                                                           seq_len,\n                                                           head_num,\n                                                           size_per_head,\n                                                           bmm2_deQFactor,\n                                                           out_scale_ptr,\n                                                           seq_len * size_per_head,\n                                                           head_num * size_per_head);\n}\n\n__global__ void transpose_int8_tensor(int8_t* arr_t, const int8_t* arr, size_t dim_0, size_t dim_1)\n{\n    extern __shared__ int8_t shmem[];\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    int local_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n    if (x >= dim_1 || y >= dim_0) {\n        return;\n    }\n\n    shmem[local_id] = arr[y * dim_1 + x];\n    __syncthreads();\n\n    int new_x = local_id % blockDim.y;\n    int new_y = local_id / blockDim.y;\n\n    int x_t                  = blockIdx.y * blockDim.y + new_x;\n    int y_t                  = blockIdx.x * blockDim.x + new_y;\n    arr_t[y_t * dim_0 + x_t] = shmem[new_x * blockDim.x + new_y];\n}\n\nvoid invokeTransposeInt8Tensor(const Tensor& x_t, const Tensor& x, cudaStream_t stream)\n{\n    dim3 block_dim(32, 16);\n    dim3 grid_dim((x.shape[1] + block_dim.x - 1) / block_dim.x, (x.shape[0] + block_dim.y - 1) / block_dim.y);\n\n    const size_t shmem_size = block_dim.x * block_dim.y * sizeof(int8_t);\n\n    transpose_int8_tensor<<<grid_dim, block_dim, shmem_size, stream>>>(\n        x_t.getPtr<int8_t>(), x.getPtr<int8_t>(), x.shape[0], x.shape[1]);\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/transpose_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_kernel(char4*       dst,\n                                       const int4*  src,\n                                       const int    batch_size,\n                                       const int    seq_len,\n                                       const int    head_num,\n                                       const int    size_per_head,\n                                       const float* v_buf_addBias_deQFactor,\n                                       const float* qk_afterSM_deQFactor,\n                                       const float* out_scale_ptr,\n                                       const int    batch_size_x_seq_len,\n                                       const int    seq_len_x_size_per_head)\n{\n    const float scale      = __ldg(v_buf_addBias_deQFactor) * __ldg(qk_afterSM_deQFactor) * __ldg(out_scale_ptr);\n    int         threadIdx4 = threadIdx.x << 2;\n    int         batch_id   = blockIdx.y;\n    int         seq_id     = blockIdx.x;\n    int         head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m = 32*batch_size*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    // int COL32_col = mk_col >> 5;\n    int outIdx = ((mk_col & 0xffffffe0) * batch_size_x_seq_len + COL32_row) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    COL32_row = (mk_row << 5) + (mk_col & 31);\n    // COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (mk_col & 0xffffffe0) * seq_len + COL32_row) >> 2;\n    char4 tmp;\n\n    int4 srcTmp4 = __ldg(src + inIdx);\n    tmp.x        = float_to_int8_rn(srcTmp4.x * scale);\n    tmp.y        = float_to_int8_rn(srcTmp4.y * scale);\n    tmp.z        = float_to_int8_rn(srcTmp4.z * scale);\n    tmp.w        = float_to_int8_rn(srcTmp4.w * scale);\n    dst[outIdx]  = tmp;\n}\n\nvoid invokeTransposeCOL32(int8_t*      dst,\n                          const int*   src,\n                          const int    batch_size,\n                          const int    seq_len,\n                          const int    head_num,\n                          const int    size_per_head,\n                          const float* v_buf_addBias_deQFactor,\n                          const float* qk_afterSM_deQFactor,\n                          const float* out_scale_ptr,\n                          hipStream_t stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        (char4*)dst,\n        (const int4*)src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        v_buf_addBias_deQFactor,\n        qk_afterSM_deQFactor,\n        out_scale_ptr,\n        batch_size * seq_len,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_kernel(int8_t*       dst,\n                                       const int8_t* src,\n                                       const int     batch_size,\n                                       const int     seq_len,\n                                       const int     head_num,\n                                       const int     size_per_head,\n                                       const float*  bmm2_deQFactor,\n                                       const float*  out_scale_ptr,\n                                       const int     batch_size_x_seq_len,\n                                       const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m = 32*batch_size*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    int COL32_col = mk_col >> 5;\n    int outIdx    = ((COL32_col << 5) * batch_size_x_seq_len + COL32_row) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    COL32_row = (mk_row << 5) + (mk_col & 31);\n    COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n    const char4* src_ptr4 = (const char4*)src;\n    char4*       dst_ptr4 = (char4*)dst;\n    dst_ptr4[outIdx]      = __ldg(src_ptr4 + inIdx);\n}\n\nvoid invokeTransposeCOL32(int8_t*       dst,\n                          const int8_t* src,\n                          const int     batch_size,\n                          const int     seq_len,\n                          const int     head_num,\n                          const int     size_per_head,\n                          const float*  bmm2_deQFactor,\n                          const float*  out_scale_ptr,\n                          hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        batch_size * seq_len,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_rebuild_padding_kernel(int8_t*        dst,\n                                                       const int32_t* src,\n                                                       const int*     sequence_id_map,\n                                                       const int      valid_word_num,\n                                                       const int      batch_size,\n                                                       const int      seq_len,\n                                                       const int      head_num,\n                                                       const int      size_per_head,\n                                                       const float*   v_buf_addBias_deQFactor,\n                                                       const float*   qk_afterSM_deQFactor,\n                                                       const float*   out_scale_ptr,\n                                                       const int      seq_len_x_size_per_head)\n{\n    const float scale      = __ldg(v_buf_addBias_deQFactor) * __ldg(qk_afterSM_deQFactor) * __ldg(out_scale_ptr);\n    int         threadIdx4 = threadIdx.x << 2;\n    int         batch_id   = blockIdx.y;\n    int         seq_id     = blockIdx.x;\n    int         head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m = 32*valid_word_num\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n        int outIdx    = ((COL32_col << 5) * valid_word_num + COL32_row) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        COL32_row = (mk_row << 5) + (mk_col & 31);\n        COL32_col = mk_col >> 5;\n\n        int inIdx = (batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row;\n        char4 tmp;\n        tmp.x            = float_to_int8_rn(__ldg(src + inIdx) * scale);\n        tmp.y            = float_to_int8_rn(__ldg(src + inIdx + 1) * scale);\n        tmp.z            = float_to_int8_rn(__ldg(src + inIdx + 2) * scale);\n        tmp.w            = float_to_int8_rn(__ldg(src + inIdx + 3) * scale);\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = tmp;\n    }\n}\n\nvoid invokeTransposeCOL32RebuildPadding(int8_t*      dst,\n                                        const int*   src,\n                                        const int*   sequence_id_map,\n                                        const int    valid_word_num,\n                                        const int    batch_size,\n                                        const int    seq_len,\n                                        const int    head_num,\n                                        const int    size_per_head,\n                                        const float* v_buf_addBias_deQFactor,\n                                        const float* qk_afterSM_deQFactor,\n                                        const float* out_scale_ptr,\n                                        hipStream_t stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_rebuild_padding_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        sequence_id_map,\n        valid_word_num,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        v_buf_addBias_deQFactor,\n        qk_afterSM_deQFactor,\n        out_scale_ptr,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, CUBLASLT_ORDER_COL32\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_rebuild_padding_kernel(int8_t*       dst,\n                                                       const int8_t* src,\n                                                       const int*    sequence_id_map,\n                                                       const int     valid_word_num,\n                                                       const int     batch_size,\n                                                       const int     seq_len,\n                                                       const int     head_num,\n                                                       const int     size_per_head,\n                                                       const float*  bmm2_deQFactor,\n                                                       const float*  out_scale_ptr,\n                                                       const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m = 32*valid_word_num\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n        int outIdx    = ((COL32_col << 5) * valid_word_num + COL32_row) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        COL32_row = (mk_row << 5) + (mk_col & 31);\n        COL32_col = mk_col >> 5;\n\n        int inIdx =\n            ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n\n        const char4* src_ptr4 = (const char4*)src;\n\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = __ldg(src_ptr4 + inIdx);\n    }\n}\n\nvoid invokeTransposeCOL32RebuildPadding(int8_t*       dst,\n                                        const int8_t* src,\n                                        const int*    sequence_id_map,\n                                        const int     valid_word_num,\n                                        const int     batch_size,\n                                        const int     seq_len,\n                                        const int     head_num,\n                                        const int     size_per_head,\n                                        const float*  bmm2_deQFactor,\n                                        const float*  out_scale_ptr,\n                                        hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_rebuild_padding_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        sequence_id_map,\n        valid_word_num,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = batch_size*seq_len, k(n) = head_num*size_per_head, row major\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_ROW_kernel(int8_t*       dst,\n                                           const int8_t* src,\n                                           const int     batch_size,\n                                           const int     seq_len,\n                                           const int     head_num,\n                                           const int     size_per_head,\n                                           const float*  bmm2_deQFactor,\n                                           const float*  out_scale_ptr,\n                                           const int     head_num_x_size_per_head,\n                                           const int     seq_len_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = batch_size*seq_len\n    // k = head_num*size_per_head\n    int mk_row = batch_id * seq_len + seq_id;\n    int mk_col = head_id * size_per_head + threadIdx4;\n    int outIdx = (mk_row * head_num_x_size_per_head + mk_col) >> 2;\n\n    // get the (row, col) input layout of m'*k'\n    // m' = seq_len\n    // k' = size_per_head\n    mk_row = seq_id;\n    mk_col = threadIdx4;\n    // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n    int COL32_row = (mk_row << 5) + (mk_col & 31);\n    int COL32_col = mk_col >> 5;\n\n    int inIdx =\n        ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n    const char4* src_ptr4 = (const char4*)src;\n    char4*       dst_ptr4 = (char4*)dst;\n    dst_ptr4[outIdx]      = __ldg(src_ptr4 + inIdx);\n}\n\nvoid invokeTransposeCOL32ToRow(int8_t*       dst,\n                               const int8_t* src,\n                               const int     batch_size,\n                               const int     seq_len,\n                               const int     head_num,\n                               const int     size_per_head,\n                               const float*  bmm2_deQFactor,\n                               const float*  out_scale_ptr,\n                               hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_ROW_kernel<<<dim3(seq_len, batch_size), dim3(size_per_head / 4, head_num), 0, stream>>>(\n        dst,\n        src,\n        batch_size,\n        seq_len,\n        head_num,\n        size_per_head,\n        bmm2_deQFactor,\n        out_scale_ptr,\n        head_num * size_per_head,\n        seq_len * size_per_head);\n}\n\n// src is the result of batch MM, whose size is batch_size*head_num*(seq_len, size_per_head), CUBLASLT_ORDER_COL32\n// dst is of m = valid_word_num, k(n) = head_num*size_per_head, row major\n// grid(seq_len, batch_size)\n// block(size_per_head/4, head_num)\n// assume size_per_head is multiples of 32\n__global__ void transpose_COL32_ROW_rebuild_padding_kernel(int8_t*       dst,\n                                                           const int8_t* src,\n                                                           const int*    sequence_id_map,\n                                                           const int     valid_word_num,\n                                                           const int     batch_size,\n                                                           const int     seq_len,\n                                                           const int     head_num,\n                                                           const int     size_per_head,\n                                                           const float*  bmm2_deQFactor,\n                                                           const float*  out_scale_ptr,\n                                                           const int     seq_len_x_size_per_head,\n                                                           const int     head_num_x_size_per_head)\n{\n    int threadIdx4 = threadIdx.x << 2;\n    int batch_id   = blockIdx.y;\n    int seq_id     = blockIdx.x;\n    int head_id    = threadIdx.y;\n    // get the (row, col) output layout of m*k\n    // m = valid_word_num\n    // k = head_num*size_per_head\n    int mk_row = __ldg(sequence_id_map + batch_id * seq_len + seq_id);\n    if (mk_row >= 0) {\n        int mk_col = head_id * size_per_head + threadIdx4;\n        int outIdx = (mk_row * head_num_x_size_per_head + mk_col) >> 2;\n\n        // get the (row, col) input layout of m'*k'\n        // m' = seq_len\n        // k' = size_per_head\n        mk_row = seq_id;\n        mk_col = threadIdx4;\n        // get the (row, col) layout of COL32; leading dimension = 32*m' = 32*seq_len\n        int COL32_row = (mk_row << 5) + (mk_col & 31);\n        int COL32_col = mk_col >> 5;\n\n        int inIdx =\n            ((batch_id * head_num + head_id) * seq_len_x_size_per_head + (COL32_col << 5) * seq_len + COL32_row) >> 2;\n\n        const char4* src_ptr4 = (const char4*)src;\n\n        char4* dst_ptr4  = (char4*)dst;\n        dst_ptr4[outIdx] = __ldg(src_ptr4 + inIdx);\n    }\n}\n\nvoid invokeTransposeCOL32ToRowRebuildPadding(int8_t*       dst,\n                                             const int8_t* src,\n                                             const int*    sequence_id_map,\n                                             const int     valid_word_num,\n                                             const int     batch_size,\n                                             const int     seq_len,\n                                             const int     head_num,\n                                             const int     size_per_head,\n                                             const float*  bmm2_deQFactor,\n                                             const float*  out_scale_ptr,\n                                             hipStream_t  stream)\n{\n    assert(size_per_head % 32 == 0);\n    transpose_COL32_ROW_rebuild_padding_kernel<<<dim3(seq_len, batch_size),\n                                                 dim3(size_per_head / 4, head_num),\n                                                 0,\n                                                 stream>>>(dst,\n                                                           src,\n                                                           sequence_id_map,\n                                                           valid_word_num,\n                                                           batch_size,\n                                                           seq_len,\n                                                           head_num,\n                                                           size_per_head,\n                                                           bmm2_deQFactor,\n                                                           out_scale_ptr,\n                                                           seq_len * size_per_head,\n                                                           head_num * size_per_head);\n}\n\n__global__ void transpose_int8_tensor(int8_t* arr_t, const int8_t* arr, size_t dim_0, size_t dim_1)\n{\n    extern __shared__ int8_t shmem[];\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    int local_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n    if (x >= dim_1 || y >= dim_0) {\n        return;\n    }\n\n    shmem[local_id] = arr[y * dim_1 + x];\n    __syncthreads();\n\n    int new_x = local_id % blockDim.y;\n    int new_y = local_id / blockDim.y;\n\n    int x_t                  = blockIdx.y * blockDim.y + new_x;\n    int y_t                  = blockIdx.x * blockDim.x + new_y;\n    arr_t[y_t * dim_0 + x_t] = shmem[new_x * blockDim.x + new_y];\n}\n\nvoid invokeTransposeInt8Tensor(const Tensor& x_t, const Tensor& x, hipStream_t stream)\n{\n    dim3 block_dim(32, 16);\n    dim3 grid_dim((x.shape[1] + block_dim.x - 1) / block_dim.x, (x.shape[0] + block_dim.y - 1) / block_dim.y);\n\n    const size_t shmem_size = block_dim.x * block_dim.y * sizeof(int8_t);\n\n    transpose_int8_tensor<<<grid_dim, block_dim, shmem_size, stream>>>(\n        x_t.getPtr<int8_t>(), x.getPtr<int8_t>(), x.shape[0], x.shape[1]);\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/transpose_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/add_residual_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T, int RESIDUAL_NUM, typename T2 = T>\n__global__ void addBiasResidual(T*           output,\n                                const T2*    input,\n                                const T*     residual1,\n                                const T*     residual2,\n                                const T*     bias,\n                                const float* scale_inter,\n                                const float* scale_out,\n                                const int    m,\n                                const int    n)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        T bias_val = (bias == nullptr) ? (T)(0.0f) : bias[col_index];\n        T in;\n        if (std::is_same<T, T2>::value) {\n            in = cuda_cast<T>(input[blockIdx.x * n + col_index]);  // cast required for compilation when T != T2\n        }\n        else {\n            in = cuda_cast<float>(input[blockIdx.x * n + col_index]) * (*scale_inter) * (*scale_out);\n        }\n\n        if (RESIDUAL_NUM == 1) {\n            output[blockIdx.x * n + col_index] = in + residual1[blockIdx.x * n + col_index] + bias_val;\n        }\n        else if (RESIDUAL_NUM == 2) {\n            output[blockIdx.x * n + col_index] =\n                in + residual1[blockIdx.x * n + col_index] + residual2[blockIdx.x * n + col_index] + bias_val;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidual(T*           output,\n                           const T*     input,\n                           const T*     residual1,\n                           const T*     residual2,\n                           const T*     bias,\n                           const float* scale_inter,\n                           const float* scale_out,\n                           const int    m,\n                           const int    n,\n                           cudaStream_t stream)\n{\n    FT_CHECK_WITH_INFO(!((scale_inter == nullptr) ^ (scale_out == nullptr)),\n                       \"Cannot use `scale_inter` without `scale_out`\");\n    const bool should_scale_input = scale_inter != nullptr;\n    int        blocks_per_row     = ceil(float(n) / 1024);\n    dim3       grid(m, blocks_per_row);\n    dim3       block(min(n, 1024));\n    if (residual2 == nullptr) {\n        if (should_scale_input) {\n            addBiasResidual<T, 1><<<grid, block, 0, stream>>>(output,\n                                                              reinterpret_cast<const int32_t*>(input),\n                                                              residual1,\n                                                              residual2,\n                                                              bias,\n                                                              scale_inter,\n                                                              scale_out,\n                                                              m,\n                                                              n);\n        }\n        else {\n            addBiasResidual<T, 1>\n                <<<grid, block, 0, stream>>>(output, input, residual1, residual2, bias, nullptr, nullptr, m, n);\n        }\n    }\n    else {\n        if (should_scale_input) {\n            addBiasResidual<T, 2><<<grid, block, 0, stream>>>(output,\n                                                              reinterpret_cast<const int32_t*>(input),\n                                                              residual1,\n                                                              residual2,\n                                                              bias,\n                                                              scale_inter,\n                                                              scale_out,\n                                                              m,\n                                                              n);\n        }\n        else {\n            addBiasResidual<T, 2>\n                <<<grid, block, 0, stream>>>(output, input, residual1, residual2, bias, nullptr, nullptr, m, n);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidual(\n    T* output, const T* residual1, const T* residual2, const T* bias, const int m, const int n, cudaStream_t stream)\n{\n    invokeAddBiasResidual(output, output, residual1, residual2, bias, nullptr, nullptr, m, n, stream);\n}\n\ntemplate<typename T>\n__global__ void addBiasAttentionFfnResidual(T*        block_output,\n                                            const T*  ffn_output,\n                                            const T*  attn_output,\n                                            const T*  block_input,\n                                            const T*  bias,\n                                            const int m,\n                                            const int n,\n                                            const int block_input_tp_split)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        block_output[blockIdx.x * n + col_index] =\n            ffn_output[blockIdx.x * n + col_index] + attn_output[blockIdx.x * n + col_index] + bias[col_index]\n            + ((block_input != nullptr) ?\n                   cuda_cast<T>((float)block_input[blockIdx.x * n + col_index] / (float)block_input_tp_split) :\n                   static_cast<T>(0.0f));\n    }\n}\n\ntemplate<typename T>\n__global__ void addBiasAttentionFfnResidual(T*        block_output,\n                                            const T*  ffn_output,\n                                            const T*  attn_output,\n                                            const T*  bias,\n                                            const int m,\n                                            const int n,\n                                            const int block_input_tp_split)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        const int global_index     = blockIdx.x * n + col_index;\n        block_output[global_index] = add(cuda_cast<T>((float)block_output[global_index] / (float)block_input_tp_split),\n                                         ffn_output[global_index],\n                                         attn_output[global_index],\n                                         bias[col_index]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasAttentionFfnResidual(T*           block_output,\n                                       const T*     ffn_output,\n                                       const T*     attn_output,\n                                       const T*     block_input,\n                                       const T*     bias,\n                                       const int    m,\n                                       const int    n,\n                                       const int    block_input_tp_split,\n                                       cudaStream_t stream)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    if (block_output == block_input) {\n        addBiasAttentionFfnResidual<<<grid, block, 0, stream>>>(\n            block_output, ffn_output, attn_output, bias, m, n, block_input_tp_split);\n    }\n    else {\n        addBiasAttentionFfnResidual<<<grid, block, 0, stream>>>(\n            block_output, ffn_output, attn_output, block_input, bias, m, n, block_input_tp_split);\n    }\n}\n\n#define INSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(T)                                                                        \\\n    template void invokeAddBiasResidual(T*           output,                                                           \\\n                                        const T*     input,                                                            \\\n                                        const T*     residual1,                                                        \\\n                                        const T*     residual2,                                                        \\\n                                        const T*     bias,                                                             \\\n                                        const float* scale_inter,                                                      \\\n                                        const float* scale_out,                                                        \\\n                                        const int    m,                                                                \\\n                                        const int    n,                                                                \\\n                                        cudaStream_t stream)\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(float);\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL\n\ntemplate void invokeAddBiasResidual(float*       output,\n                                    const float* residual1,\n                                    const float* residual2,\n                                    const float* bias,\n                                    const int    m,\n                                    const int    n,\n                                    cudaStream_t stream);\n\ntemplate void invokeAddBiasResidual(half*        output,\n                                    const half*  residual1,\n                                    const half*  residual2,\n                                    const half*  bias,\n                                    const int    m,\n                                    const int    n,\n                                    cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasResidual(__nv_bfloat16*       output,\n                                    const __nv_bfloat16* residual1,\n                                    const __nv_bfloat16* residual2,\n                                    const __nv_bfloat16* bias,\n                                    const int            m,\n                                    const int            n,\n                                    cudaStream_t         stream);\n#endif\n\ntemplate void invokeAddBiasAttentionFfnResidual(float*       block_output,\n                                                const float* ffn_output,\n                                                const float* attn_output,\n                                                const float* input,\n                                                const float* bias,\n                                                const int    m,\n                                                const int    n,\n                                                const int    block_input_tp_split,\n                                                cudaStream_t stream);\n\ntemplate void invokeAddBiasAttentionFfnResidual(half*        block_output,\n                                                const half*  ffn_output,\n                                                const half*  attn_output,\n                                                const half*  input,\n                                                const half*  bias,\n                                                const int    m,\n                                                const int    n,\n                                                const int    block_input_tp_split,\n                                                cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasAttentionFfnResidual(__nv_bfloat16*       block_output,\n                                                const __nv_bfloat16* ffn_output,\n                                                const __nv_bfloat16* attn_output,\n                                                const __nv_bfloat16* input,\n                                                const __nv_bfloat16* bias,\n                                                const int            m,\n                                                const int            n,\n                                                const int            block_input_tp_split,\n                                                cudaStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void T5addResidual(T* output, const T* input, const int m, const int n)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        float out_val = (float)output[blockIdx.x * n + col_index] + (float)input[blockIdx.x * n + col_index];\n        output[blockIdx.x * n + col_index] =\n            (T)((std::is_same<T, half>::value && (out_val > 64512 || out_val < -64512)) ?\n                    (out_val > 0 ? 64512 : -64512) :\n                    out_val);\n    }\n}\n\ntemplate<typename T>\nvoid invokeT5AddResidual(T* output, const T* input, const int m, const int n, cudaStream_t stream)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    T5addResidual<<<grid, block, 0, stream>>>(output, input, m, n);\n}\n\ntemplate void invokeT5AddResidual(float* output, const float* input, const int m, const int n, cudaStream_t stream);\ntemplate void invokeT5AddResidual(half* output, const half* input, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void\ninvokeT5AddResidual(__nv_bfloat16* output, const __nv_bfloat16* input, const int m, const int n, cudaStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid invokeT5AddBiasResidual(T* output, const T* input, const T* bias, const int m, const int n, cudaStream_t stream)\n{\n    if (bias != nullptr) {\n        invokeAddBiasResidual(output, input, bias, m, n, stream);\n    }\n    else {\n        invokeT5AddResidual(output, input, m, n, stream);\n    }\n    return;\n}\n\ntemplate void invokeT5AddBiasResidual(\n    float* output, const float* input, const float* bias, const int m, const int n, cudaStream_t stream);\ntemplate void invokeT5AddBiasResidual(\n    half* output, const half* input, const half* bias, const int m, const int n, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeT5AddBiasResidual(__nv_bfloat16*       output,\n                                      const __nv_bfloat16* input,\n                                      const __nv_bfloat16* bias,\n                                      const int            m,\n                                      const int            n,\n                                      cudaStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasResidualCol32  ***********************/\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_COL32_int8I_DataTypeO(\n    T* output, const int8_t* input1, const T* input2, const T* bias, int m, int n, const float* input1_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* input1TmpPtr = (char4*)input1;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0] = static_cast<float>(input2[(outIdx << 2) + 0]) + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2[(outIdx << 2) + 1]) + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2[(outIdx << 2) + 2]) + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2[(outIdx << 2) + 3]) + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    for (int i = 0; i < 4; i++) {\n        output[(outIdx << 2) + i] = static_cast<T>(local_out[i]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_COL32_int8I_DataTypeO(half4*        output,\n                                                     const int8_t* input1,\n                                                     const half4*  input2,\n                                                     const half4*  bias,\n                                                     int           m,\n                                                     int           n,\n                                                     const float*  input1_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    int         col_start        = (blockIdx.x << 5) + (threadIdx.x << 2);\n    int         row_start        = (blockIdx.y << 5) + (threadIdx.y);\n\n    if (col_start < n && row_start < m) {\n        half4  local_out;\n        int    outIdx       = ((col_start & 0xffffffe0) * m + (row_start << 5) + (col_start & 31)) >> 2;\n        char4* input1TmpPtr = (char4*)input1;\n        char4  input1Tmp    = input1TmpPtr[outIdx];\n        half4  input2Tmp    = input2[outIdx];\n        half4  biasTmp      = bias[col_start >> 2];\n\n        local_out.x = static_cast<half>((float)input1Tmp.x * input1_deQFactor + (float)biasTmp.x + (float)input2Tmp.x);\n        local_out.y = static_cast<half>((float)input1Tmp.y * input1_deQFactor + (float)biasTmp.y + (float)input2Tmp.y);\n        local_out.z = static_cast<half>((float)input1Tmp.z * input1_deQFactor + (float)biasTmp.z + (float)input2Tmp.z);\n        local_out.w = static_cast<half>((float)input1Tmp.w * input1_deQFactor + (float)biasTmp.w + (float)input2Tmp.w);\n        output[outIdx] = local_out;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualCol32(T*            output,\n                                const int8_t* input1,\n                                const T*      input2,\n                                const T*      bias,\n                                int           m,\n                                int           n,\n                                cudaStream_t  stream,\n                                const float*  input1_deQFactor_ptr)\n{\n    dim3 grid((n + 31) / 32, (m + 31) / 32);\n    dim3 block(8, 32);\n    assert(block.x <= 1024);\n    if (sizeof(T) == 2) {\n        add_bias_input_COL32_int8I_DataTypeO<<<grid, block, 0, stream>>>(\n            (half4*)output, input1, (const half4*)input2, (const half4*)bias, m, n, input1_deQFactor_ptr);\n    }\n    else {\n        add_bias_input_COL32_int8I_DataTypeO<T>\n            <<<grid, block, 0, stream>>>(output, input1, input2, bias, m, n, input1_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualCol32(float*        output,\n                                         const int8_t* input1,\n                                         const float*  input2,\n                                         const float*  bias,\n                                         int           m,\n                                         int           n,\n                                         cudaStream_t  stream,\n                                         const float*  input1_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualCol32(half*         output,\n                                         const int8_t* input1,\n                                         const half*   input2,\n                                         const half*   bias,\n                                         int           m,\n                                         int           n,\n                                         cudaStream_t  stream,\n                                         const float*  input1_deQFactor_ptr);\n\n/*******************  invokeAddBiasResidualCol32  ***********************/\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_COL32_int32I_DataTypeO(T*             output,\n                                                      const int32_t* input1,\n                                                      const T*       input2,\n                                                      const T*       bias,\n                                                      int            m,\n                                                      int            n,\n                                                      const float*   weight_amax,\n                                                      const float*   input1_amax_ptr,\n                                                      const int      scale_is_vector)\n{\n    int           col_start        = threadIdx.x << 2;\n    const float4* weight_scale_ptr = (const float4*)weight_amax;\n    const float4  weight_scale     = __ldg(weight_scale_ptr + threadIdx.x * scale_is_vector);\n    const float   input1_deQ       = __ldg(input1_amax_ptr) / 127.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp    = input1TmpPtr[outIdx];\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2[(outIdx << 2) + 0])\n                   + static_cast<float>(input1Tmp.x) * input1_deQ * weight_scale.x / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2[(outIdx << 2) + 1])\n                   + static_cast<float>(input1Tmp.y) * input1_deQ * weight_scale.y / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2[(outIdx << 2) + 2])\n                   + static_cast<float>(input1Tmp.z) * input1_deQ * weight_scale.z / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2[(outIdx << 2) + 3])\n                   + static_cast<float>(input1Tmp.w) * input1_deQ * weight_scale.w / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    for (int i = 0; i < 4; i++) {\n        output[(outIdx << 2) + i] = static_cast<T>(local_out[i]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_COL32_int32I_DataTypeO(half4*         output,\n                                                      const int32_t* input1,\n                                                      const half4*   input2,\n                                                      const half4*   bias,\n                                                      int            m,\n                                                      int            n,\n                                                      const float*   weight_amax,\n                                                      const float*   input1_amax_ptr,\n                                                      const int      scale_is_vector)\n{\n    int           col_start           = threadIdx.x << 2;\n    const float4* weight_scale_ptr    = (const float4*)weight_amax;\n    const float   weight_scale_single = __ldg(weight_amax);\n    const float4  weight_scale =\n        scale_is_vector == 1 ?\n             __ldg(weight_scale_ptr + threadIdx.x * scale_is_vector) :\n             make_float4(weight_scale_single, weight_scale_single, weight_scale_single, weight_scale_single);\n    const float input1_deQ = __ldg(input1_amax_ptr) / 127.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp    = input1TmpPtr[outIdx];\n    half4 input2Tmp    = input2[outIdx];\n    half4 biasTmp      = bias[threadIdx.x];\n\n    local_out[0] = static_cast<float>(input2Tmp.x)\n                   + static_cast<float>(input1Tmp.x) * input1_deQ * weight_scale.x / 127.0f\n                   + static_cast<float>(biasTmp.x);\n    local_out[1] = static_cast<float>(input2Tmp.y)\n                   + static_cast<float>(input1Tmp.y) * input1_deQ * weight_scale.y / 127.0f\n                   + static_cast<float>(biasTmp.y);\n    local_out[2] = static_cast<float>(input2Tmp.z)\n                   + static_cast<float>(input1Tmp.z) * input1_deQ * weight_scale.z / 127.0f\n                   + static_cast<float>(biasTmp.z);\n    local_out[3] = static_cast<float>(input2Tmp.w)\n                   + static_cast<float>(input1Tmp.w) * input1_deQ * weight_scale.w / 127.0f\n                   + static_cast<float>(biasTmp.w);\n\n    half4 outTmp;\n    outTmp.x = static_cast<half>(local_out[0]);\n    outTmp.y = static_cast<half>(local_out[1]);\n    outTmp.z = static_cast<half>(local_out[2]);\n    outTmp.w = static_cast<half>(local_out[3]);\n\n    output[outIdx] = outTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualCol32(T*             output,\n                                const int32_t* input1,\n                                const T*       input2,\n                                const T*       bias,\n                                int            m,\n                                int            n,\n                                cudaStream_t   stream,\n                                const float*   weight_amax,\n                                const float*   input1_amax_ptr,\n                                const int      scale_is_vector)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n    if (sizeof(T) == 2) {\n        add_bias_input_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((half4*)output,\n                                                                          input1,\n                                                                          (const half4*)input2,\n                                                                          (const half4*)bias,\n                                                                          m,\n                                                                          n,\n                                                                          weight_amax,\n                                                                          input1_amax_ptr,\n                                                                          scale_is_vector);\n    }\n    else {\n        add_bias_input_COL32_int32I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, m, n, weight_amax, input1_amax_ptr, scale_is_vector);\n    }\n}\n\ntemplate void invokeAddBiasResidualCol32(float*       output,\n                                         const int*   input1,\n                                         const float* input2,\n                                         const float* bias,\n                                         int          m,\n                                         int          n,\n                                         cudaStream_t stream,\n                                         const float* weight_amax,\n                                         const float* input1_amax_ptr,\n                                         const int    scale_is_vector);\n\ntemplate void invokeAddBiasResidualCol32(half*        output,\n                                         const int*   input1,\n                                         const half*  input2,\n                                         const half*  bias,\n                                         int          m,\n                                         int          n,\n                                         cudaStream_t stream,\n                                         const float* weight_amax,\n                                         const float* input1_amax_ptr,\n                                         const int    scale_is_vector);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/add_residual_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntemplate<typename T, int RESIDUAL_NUM, typename T2 = T>\n__global__ void addBiasResidual(T*           output,\n                                const T2*    input,\n                                const T*     residual1,\n                                const T*     residual2,\n                                const T*     bias,\n                                const float* scale_inter,\n                                const float* scale_out,\n                                const int    m,\n                                const int    n)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        T bias_val = (bias == nullptr) ? (T)(0.0f) : bias[col_index];\n        T in;\n        if (std::is_same<T, T2>::value) {\n            in = cuda_cast<T>(input[blockIdx.x * n + col_index]);  // cast required for compilation when T != T2\n        }\n        else {\n            in = cuda_cast<float>(input[blockIdx.x * n + col_index]) * (*scale_inter) * (*scale_out);\n        }\n\n        if (RESIDUAL_NUM == 1) {\n            output[blockIdx.x * n + col_index] = in + residual1[blockIdx.x * n + col_index] + bias_val;\n        }\n        else if (RESIDUAL_NUM == 2) {\n            output[blockIdx.x * n + col_index] =\n                in + residual1[blockIdx.x * n + col_index] + residual2[blockIdx.x * n + col_index] + bias_val;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidual(T*           output,\n                           const T*     input,\n                           const T*     residual1,\n                           const T*     residual2,\n                           const T*     bias,\n                           const float* scale_inter,\n                           const float* scale_out,\n                           const int    m,\n                           const int    n,\n                           hipStream_t stream)\n{\n    FT_CHECK_WITH_INFO(!((scale_inter == nullptr) ^ (scale_out == nullptr)),\n                       \"Cannot use `scale_inter` without `scale_out`\");\n    const bool should_scale_input = scale_inter != nullptr;\n    int        blocks_per_row     = ceil(float(n) / 1024);\n    dim3       grid(m, blocks_per_row);\n    dim3       block(min(n, 1024));\n    if (residual2 == nullptr) {\n        if (should_scale_input) {\n            addBiasResidual<T, 1><<<grid, block, 0, stream>>>(output,\n                                                              reinterpret_cast<const int32_t*>(input),\n                                                              residual1,\n                                                              residual2,\n                                                              bias,\n                                                              scale_inter,\n                                                              scale_out,\n                                                              m,\n                                                              n);\n        }\n        else {\n            addBiasResidual<T, 1>\n                <<<grid, block, 0, stream>>>(output, input, residual1, residual2, bias, nullptr, nullptr, m, n);\n        }\n    }\n    else {\n        if (should_scale_input) {\n            addBiasResidual<T, 2><<<grid, block, 0, stream>>>(output,\n                                                              reinterpret_cast<const int32_t*>(input),\n                                                              residual1,\n                                                              residual2,\n                                                              bias,\n                                                              scale_inter,\n                                                              scale_out,\n                                                              m,\n                                                              n);\n        }\n        else {\n            addBiasResidual<T, 2>\n                <<<grid, block, 0, stream>>>(output, input, residual1, residual2, bias, nullptr, nullptr, m, n);\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidual(\n    T* output, const T* residual1, const T* residual2, const T* bias, const int m, const int n, hipStream_t stream)\n{\n    invokeAddBiasResidual(output, output, residual1, residual2, bias, nullptr, nullptr, m, n, stream);\n}\n\ntemplate<typename T>\n__global__ void addBiasAttentionFfnResidual(T*        block_output,\n                                            const T*  ffn_output,\n                                            const T*  attn_output,\n                                            const T*  block_input,\n                                            const T*  bias,\n                                            const int m,\n                                            const int n,\n                                            const int block_input_tp_split)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        block_output[blockIdx.x * n + col_index] =\n            ffn_output[blockIdx.x * n + col_index] + attn_output[blockIdx.x * n + col_index] + bias[col_index]\n            + ((block_input != nullptr) ?\n                   cuda_cast<T>((float)block_input[blockIdx.x * n + col_index] / (float)block_input_tp_split) :\n                   static_cast<T>(0.0f));\n    }\n}\n\ntemplate<typename T>\n__global__ void addBiasAttentionFfnResidual(T*        block_output,\n                                            const T*  ffn_output,\n                                            const T*  attn_output,\n                                            const T*  bias,\n                                            const int m,\n                                            const int n,\n                                            const int block_input_tp_split)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        const int global_index     = blockIdx.x * n + col_index;\n        block_output[global_index] = add(cuda_cast<T>((float)block_output[global_index] / (float)block_input_tp_split),\n                                         ffn_output[global_index],\n                                         attn_output[global_index],\n                                         bias[col_index]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasAttentionFfnResidual(T*           block_output,\n                                       const T*     ffn_output,\n                                       const T*     attn_output,\n                                       const T*     block_input,\n                                       const T*     bias,\n                                       const int    m,\n                                       const int    n,\n                                       const int    block_input_tp_split,\n                                       hipStream_t stream)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    if (block_output == block_input) {\n        addBiasAttentionFfnResidual<<<grid, block, 0, stream>>>(\n            block_output, ffn_output, attn_output, bias, m, n, block_input_tp_split);\n    }\n    else {\n        addBiasAttentionFfnResidual<<<grid, block, 0, stream>>>(\n            block_output, ffn_output, attn_output, block_input, bias, m, n, block_input_tp_split);\n    }\n}\n\n#define INSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(T)                                                                        \\\n    template void invokeAddBiasResidual(T*           output,                                                           \\\n                                        const T*     input,                                                            \\\n                                        const T*     residual1,                                                        \\\n                                        const T*     residual2,                                                        \\\n                                        const T*     bias,                                                             \\\n                                        const float* scale_inter,                                                      \\\n                                        const float* scale_out,                                                        \\\n                                        const int    m,                                                                \\\n                                        const int    n,                                                                \\\n                                        hipStream_t stream)\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(float);\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_ADD_BIAS_RESIDUAL\n\ntemplate void invokeAddBiasResidual(float*       output,\n                                    const float* residual1,\n                                    const float* residual2,\n                                    const float* bias,\n                                    const int    m,\n                                    const int    n,\n                                    hipStream_t stream);\n\ntemplate void invokeAddBiasResidual(half*        output,\n                                    const half*  residual1,\n                                    const half*  residual2,\n                                    const half*  bias,\n                                    const int    m,\n                                    const int    n,\n                                    hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasResidual(__hip_bfloat16*       output,\n                                    const __hip_bfloat16* residual1,\n                                    const __hip_bfloat16* residual2,\n                                    const __hip_bfloat16* bias,\n                                    const int            m,\n                                    const int            n,\n                                    hipStream_t         stream);\n#endif\n\ntemplate void invokeAddBiasAttentionFfnResidual(float*       block_output,\n                                                const float* ffn_output,\n                                                const float* attn_output,\n                                                const float* input,\n                                                const float* bias,\n                                                const int    m,\n                                                const int    n,\n                                                const int    block_input_tp_split,\n                                                hipStream_t stream);\n\ntemplate void invokeAddBiasAttentionFfnResidual(half*        block_output,\n                                                const half*  ffn_output,\n                                                const half*  attn_output,\n                                                const half*  input,\n                                                const half*  bias,\n                                                const int    m,\n                                                const int    n,\n                                                const int    block_input_tp_split,\n                                                hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasAttentionFfnResidual(__hip_bfloat16*       block_output,\n                                                const __hip_bfloat16* ffn_output,\n                                                const __hip_bfloat16* attn_output,\n                                                const __hip_bfloat16* input,\n                                                const __hip_bfloat16* bias,\n                                                const int            m,\n                                                const int            n,\n                                                const int            block_input_tp_split,\n                                                hipStream_t         stream);\n#endif\n\ntemplate<typename T>\n__global__ void T5addResidual(T* output, const T* input, const int m, const int n)\n{\n    const int col_index = blockIdx.y * blockDim.x + threadIdx.x;\n    if (col_index < n) {\n        float out_val = (float)output[blockIdx.x * n + col_index] + (float)input[blockIdx.x * n + col_index];\n        output[blockIdx.x * n + col_index] =\n            (T)((std::is_same<T, half>::value && (out_val > 64512 || out_val < -64512)) ?\n                    (out_val > 0 ? 64512 : -64512) :\n                    out_val);\n    }\n}\n\ntemplate<typename T>\nvoid invokeT5AddResidual(T* output, const T* input, const int m, const int n, hipStream_t stream)\n{\n    int  blocks_per_row = ceil(float(n) / 1024);\n    dim3 grid(m, blocks_per_row);\n    dim3 block(min(n, 1024));\n    T5addResidual<<<grid, block, 0, stream>>>(output, input, m, n);\n}\n\ntemplate void invokeT5AddResidual(float* output, const float* input, const int m, const int n, hipStream_t stream);\ntemplate void invokeT5AddResidual(half* output, const half* input, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void\ninvokeT5AddResidual(__hip_bfloat16* output, const __hip_bfloat16* input, const int m, const int n, hipStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid invokeT5AddBiasResidual(T* output, const T* input, const T* bias, const int m, const int n, hipStream_t stream)\n{\n    if (bias != nullptr) {\n        invokeAddBiasResidual(output, input, bias, m, n, stream);\n    }\n    else {\n        invokeT5AddResidual(output, input, m, n, stream);\n    }\n    return;\n}\n\ntemplate void invokeT5AddBiasResidual(\n    float* output, const float* input, const float* bias, const int m, const int n, hipStream_t stream);\ntemplate void invokeT5AddBiasResidual(\n    half* output, const half* input, const half* bias, const int m, const int n, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeT5AddBiasResidual(__hip_bfloat16*       output,\n                                      const __hip_bfloat16* input,\n                                      const __hip_bfloat16* bias,\n                                      const int            m,\n                                      const int            n,\n                                      hipStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasResidualCol32  ***********************/\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_COL32_int8I_DataTypeO(\n    T* output, const int8_t* input1, const T* input2, const T* bias, int m, int n, const float* input1_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    int         col_start        = threadIdx.x << 2;\n\n    float  local_out[4];\n    int    outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    char4* input1TmpPtr = (char4*)input1;\n    char4  input1Tmp    = __ldg(input1TmpPtr + outIdx);\n\n    int col_start_tmp = col_start;\n    local_out[0] = static_cast<float>(input2[(outIdx << 2) + 0]) + static_cast<float>(input1Tmp.x) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2[(outIdx << 2) + 1]) + static_cast<float>(input1Tmp.y) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2[(outIdx << 2) + 2]) + static_cast<float>(input1Tmp.z) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2[(outIdx << 2) + 3]) + static_cast<float>(input1Tmp.w) * input1_deQFactor\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    for (int i = 0; i < 4; i++) {\n        output[(outIdx << 2) + i] = static_cast<T>(local_out[i]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_COL32_int8I_DataTypeO(half4*        output,\n                                                     const int8_t* input1,\n                                                     const half4*  input2,\n                                                     const half4*  bias,\n                                                     int           m,\n                                                     int           n,\n                                                     const float*  input1_deQFactor_ptr)\n{\n    const float input1_deQFactor = __ldg(input1_deQFactor_ptr);\n    int         col_start        = (blockIdx.x << 5) + (threadIdx.x << 2);\n    int         row_start        = (blockIdx.y << 5) + (threadIdx.y);\n\n    if (col_start < n && row_start < m) {\n        half4  local_out;\n        int    outIdx       = ((col_start & 0xffffffe0) * m + (row_start << 5) + (col_start & 31)) >> 2;\n        char4* input1TmpPtr = (char4*)input1;\n        char4  input1Tmp    = input1TmpPtr[outIdx];\n        half4  input2Tmp    = input2[outIdx];\n        half4  biasTmp      = bias[col_start >> 2];\n\n        local_out.x = static_cast<half>((float)input1Tmp.x * input1_deQFactor + (float)biasTmp.x + (float)input2Tmp.x);\n        local_out.y = static_cast<half>((float)input1Tmp.y * input1_deQFactor + (float)biasTmp.y + (float)input2Tmp.y);\n        local_out.z = static_cast<half>((float)input1Tmp.z * input1_deQFactor + (float)biasTmp.z + (float)input2Tmp.z);\n        local_out.w = static_cast<half>((float)input1Tmp.w * input1_deQFactor + (float)biasTmp.w + (float)input2Tmp.w);\n        output[outIdx] = local_out;\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualCol32(T*            output,\n                                const int8_t* input1,\n                                const T*      input2,\n                                const T*      bias,\n                                int           m,\n                                int           n,\n                                hipStream_t  stream,\n                                const float*  input1_deQFactor_ptr)\n{\n    dim3 grid((n + 31) / 32, (m + 31) / 32);\n    dim3 block(8, 32);\n    assert(block.x <= 1024);\n    if (sizeof(T) == 2) {\n        add_bias_input_COL32_int8I_DataTypeO<<<grid, block, 0, stream>>>(\n            (half4*)output, input1, (const half4*)input2, (const half4*)bias, m, n, input1_deQFactor_ptr);\n    }\n    else {\n        add_bias_input_COL32_int8I_DataTypeO<T>\n            <<<grid, block, 0, stream>>>(output, input1, input2, bias, m, n, input1_deQFactor_ptr);\n    }\n}\n\ntemplate void invokeAddBiasResidualCol32(float*        output,\n                                         const int8_t* input1,\n                                         const float*  input2,\n                                         const float*  bias,\n                                         int           m,\n                                         int           n,\n                                         hipStream_t  stream,\n                                         const float*  input1_deQFactor_ptr);\n\ntemplate void invokeAddBiasResidualCol32(half*         output,\n                                         const int8_t* input1,\n                                         const half*   input2,\n                                         const half*   bias,\n                                         int           m,\n                                         int           n,\n                                         hipStream_t  stream,\n                                         const float*  input1_deQFactor_ptr);\n\n/*******************  invokeAddBiasResidualCol32  ***********************/\n// input1/input2/out matrix with layout of cublasLt CUBLASLT_ORDER_COL32 (m*n)\n//(grid, block) must be (m, n/4)\n// using char4\ntemplate<typename T>\n__global__ void add_bias_input_COL32_int32I_DataTypeO(T*             output,\n                                                      const int32_t* input1,\n                                                      const T*       input2,\n                                                      const T*       bias,\n                                                      int            m,\n                                                      int            n,\n                                                      const float*   weight_amax,\n                                                      const float*   input1_amax_ptr,\n                                                      const int      scale_is_vector)\n{\n    int           col_start        = threadIdx.x << 2;\n    const float4* weight_scale_ptr = (const float4*)weight_amax;\n    const float4  weight_scale     = __ldg(weight_scale_ptr + threadIdx.x * scale_is_vector);\n    const float   input1_deQ       = __ldg(input1_amax_ptr) / 127.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp    = input1TmpPtr[outIdx];\n\n    int col_start_tmp = col_start;\n    local_out[0]      = static_cast<float>(input2[(outIdx << 2) + 0])\n                   + static_cast<float>(input1Tmp.x) * input1_deQ * weight_scale.x / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[1]  = static_cast<float>(input2[(outIdx << 2) + 1])\n                   + static_cast<float>(input1Tmp.y) * input1_deQ * weight_scale.y / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[2]  = static_cast<float>(input2[(outIdx << 2) + 2])\n                   + static_cast<float>(input1Tmp.z) * input1_deQ * weight_scale.z / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n    col_start_tmp = col_start_tmp + 1;\n    local_out[3]  = static_cast<float>(input2[(outIdx << 2) + 3])\n                   + static_cast<float>(input1Tmp.w) * input1_deQ * weight_scale.w / 127.0f\n                   + static_cast<float>(__ldg(bias + col_start_tmp));\n\n    for (int i = 0; i < 4; i++) {\n        output[(outIdx << 2) + i] = static_cast<T>(local_out[i]);\n    }\n}\n\ntemplate<>\n__global__ void add_bias_input_COL32_int32I_DataTypeO(half4*         output,\n                                                      const int32_t* input1,\n                                                      const half4*   input2,\n                                                      const half4*   bias,\n                                                      int            m,\n                                                      int            n,\n                                                      const float*   weight_amax,\n                                                      const float*   input1_amax_ptr,\n                                                      const int      scale_is_vector)\n{\n    int           col_start           = threadIdx.x << 2;\n    const float4* weight_scale_ptr    = (const float4*)weight_amax;\n    const float   weight_scale_single = __ldg(weight_amax);\n    const float4  weight_scale =\n        scale_is_vector == 1 ?\n             __ldg(weight_scale_ptr + threadIdx.x * scale_is_vector) :\n             make_float4(weight_scale_single, weight_scale_single, weight_scale_single, weight_scale_single);\n    const float input1_deQ = __ldg(input1_amax_ptr) / 127.0f;\n\n    float local_out[4];\n    int   outIdx       = ((col_start & 0xffffffe0) * m + (blockIdx.x << 5) + (col_start & 31)) >> 2;\n    int4* input1TmpPtr = (int4*)input1;\n    int4  input1Tmp    = input1TmpPtr[outIdx];\n    half4 input2Tmp    = input2[outIdx];\n    half4 biasTmp      = bias[threadIdx.x];\n\n    local_out[0] = static_cast<float>(input2Tmp.x)\n                   + static_cast<float>(input1Tmp.x) * input1_deQ * weight_scale.x / 127.0f\n                   + static_cast<float>(biasTmp.x);\n    local_out[1] = static_cast<float>(input2Tmp.y)\n                   + static_cast<float>(input1Tmp.y) * input1_deQ * weight_scale.y / 127.0f\n                   + static_cast<float>(biasTmp.y);\n    local_out[2] = static_cast<float>(input2Tmp.z)\n                   + static_cast<float>(input1Tmp.z) * input1_deQ * weight_scale.z / 127.0f\n                   + static_cast<float>(biasTmp.z);\n    local_out[3] = static_cast<float>(input2Tmp.w)\n                   + static_cast<float>(input1Tmp.w) * input1_deQ * weight_scale.w / 127.0f\n                   + static_cast<float>(biasTmp.w);\n\n    half4 outTmp;\n    outTmp.x = static_cast<half>(local_out[0]);\n    outTmp.y = static_cast<half>(local_out[1]);\n    outTmp.z = static_cast<half>(local_out[2]);\n    outTmp.w = static_cast<half>(local_out[3]);\n\n    output[outIdx] = outTmp;\n}\n\ntemplate<typename T>\nvoid invokeAddBiasResidualCol32(T*             output,\n                                const int32_t* input1,\n                                const T*       input2,\n                                const T*       bias,\n                                int            m,\n                                int            n,\n                                hipStream_t   stream,\n                                const float*   weight_amax,\n                                const float*   input1_amax_ptr,\n                                const int      scale_is_vector)\n{\n    dim3 grid(m);\n    dim3 block(n / 4);\n    assert(block.x <= 1024);\n    if (sizeof(T) == 2) {\n        add_bias_input_COL32_int32I_DataTypeO<<<grid, block, 0, stream>>>((half4*)output,\n                                                                          input1,\n                                                                          (const half4*)input2,\n                                                                          (const half4*)bias,\n                                                                          m,\n                                                                          n,\n                                                                          weight_amax,\n                                                                          input1_amax_ptr,\n                                                                          scale_is_vector);\n    }\n    else {\n        add_bias_input_COL32_int32I_DataTypeO<T><<<grid, block, 0, stream>>>(\n            output, input1, input2, bias, m, n, weight_amax, input1_amax_ptr, scale_is_vector);\n    }\n}\n\ntemplate void invokeAddBiasResidualCol32(float*       output,\n                                         const int*   input1,\n                                         const float* input2,\n                                         const float* bias,\n                                         int          m,\n                                         int          n,\n                                         hipStream_t stream,\n                                         const float* weight_amax,\n                                         const float* input1_amax_ptr,\n                                         const int    scale_is_vector);\n\ntemplate void invokeAddBiasResidualCol32(half*        output,\n                                         const int*   input1,\n                                         const half*  input2,\n                                         const half*  bias,\n                                         int          m,\n                                         int          n,\n                                         hipStream_t stream,\n                                         const float* weight_amax,\n                                         const float* input1_amax_ptr,\n                                         const int    scale_is_vector);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/add_residual_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/layernorm_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int RESIDUAL_NUM, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalAddBiasResidualLayerNormOpt(T* normed_output,\n                                                   T* output,\n                                                   const T* __restrict input,\n                                                   const T* __restrict bias,\n                                                   const T* __restrict residual1,\n                                                   const T* __restrict residual2,\n                                                   const T* __restrict gamma,\n                                                   const T* __restrict beta,\n                                                   const float  layernorm_eps,\n                                                   int          m,\n                                                   int          n,\n                                                   const float* scale_inter,\n                                                   const float* scale_out,\n                                                   const float* scale,\n                                                   float*       dynamic_scale,\n                                                   const int    int8_mode)\n{\n    extern __shared__ __align__(sizeof(float)) char _shmem[];  // Align on largest type\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool scale_input     = int8_mode == 2 && scale_inter != nullptr;\n    const bool dynamic_scaling = dynamic_scale != nullptr;\n\n    T local_sum = cuda_cast<T>(0.0f);\n\n    const Float_Packed_T scale_from_int = cuda_cast<Float_Packed_T>(scale_input ? (*scale_inter) * (*scale_out) : 0.0f);\n    const Float_Packed_T scale_to_int   = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = cuda_cast<T>(0.0f);\n\n        if (IS_BIAS) {\n            val = hadd2(val, ldg(&bias[i]));\n        }\n        if (RESIDUAL_NUM == 1) {\n            val = hadd2(val, ldg(&residual1[index]));\n        }\n        else if (RESIDUAL_NUM == 2) {\n            val = hadd2(hadd2(val, ldg(&residual1[index])), ldg(&residual2[index]));\n        }\n\n        if (IS_OUTPUT) {\n            T in_val;\n            if (scale_input) {\n                in_val = cuda_cast<T>(cuda_cast<Float_Packed_T>(reinterpret_cast<const Int32_Packed_T*>(input)[index])\n                                      * scale_from_int);\n            }\n            else {\n                in_val = input[index];\n            }\n            val = hadd2(val, in_val);\n        }\n        shmem[i]      = val;\n        output[index] = val;\n        local_sum     = hadd2(local_sum, val);\n    }\n\n    mean = blockReduceSum((float)(local_sum.x + local_sum.y));\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n / 2;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        T     val    = input[blockIdx.x * n + i];\n        float diff_1 = (float)(val.x) - s_mean;\n        float diff_2 = (float)(val.y) - s_mean;\n        local_var_sum += (diff_1 * diff_1 + diff_2 * diff_2);\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n / 2 + layernorm_eps);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n    Scalar_T abs_max = 1e-6f;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hsub2(shmem[i], mean_2), var_2, ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_to_int);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = threadIdx.x; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int RESIDUAL_NUM, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalAddBiasResidualLayerNormOpt2(T* normed_output,\n                                                    T* output,\n                                                    const T* __restrict input,\n                                                    const T* __restrict bias,\n                                                    const T* __restrict residual1,\n                                                    const T* __restrict residual2,\n                                                    const T* __restrict gamma,\n                                                    const T* __restrict beta,\n                                                    const float  layernorm_eps,\n                                                    int          m,\n                                                    int          n,\n                                                    const float* scale_inter,\n                                                    const float* scale_out,\n                                                    const float* scale,\n                                                    float*       dynamic_scale,\n                                                    const int    int8_mode)\n{\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            x_sum    = 0.0f;\n    float            x2_sum   = 0.0f;\n    const int        b_offset = blockIdx.x * n;\n\n    using T1             = typename TypeConverter<T>::Type;\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool           scale_input  = int8_mode == 2 && scale_inter != nullptr;\n    const Float_Packed_T scale_vec_in = cuda_cast<Float_Packed_T>(scale_input ? (*scale_inter) * (*scale_out) : 0.0f);\n    const Float_Packed_T scale_vec    = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n    const bool           dynamic_scaling = dynamic_scale != nullptr;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        float     val_1 = 0.0f;\n        float     val_2 = 0.0f;\n        T         tmp;\n\n        if (IS_BIAS) {\n            tmp = ldg(&bias[i]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        if (RESIDUAL_NUM == 1) {\n            tmp = ldg(&residual1[index]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        else if (RESIDUAL_NUM == 2) {\n            tmp    = ldg(&residual1[index]);\n            T tmp2 = ldg(&residual2[index]);\n            val_1 += (static_cast<float>(tmp.x) + static_cast<float>(tmp2.x));\n            val_2 += (static_cast<float>(tmp.y) + static_cast<float>(tmp2.y));\n        }\n\n        if (IS_OUTPUT) {\n            if (scale_input) {\n                tmp = cuda_cast<T>(cuda_cast<Float_Packed_T>(reinterpret_cast<const Int32_Packed_T*>(input)[index])\n                                   * scale_vec_in);\n            }\n            else {\n                tmp = ldg(&input[index]);\n            }\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        tmp.x         = cuda_cast<T1>(val_1);\n        tmp.y         = cuda_cast<T1>(val_2);\n        shmem[i]      = tmp;\n        output[index] = tmp;\n        x_sum += val_1 + val_2;\n        x2_sum += val_1 * val_1 + val_2 * val_2;\n    }\n    float sums[2];\n    sums[0] = x_sum;\n    sums[1] = x2_sum;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / n / 2;\n        s_variance = rsqrtf(sums[1] / n / 2 - s_mean * s_mean + layernorm_eps);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n    Scalar_T abs_max = 1e-6f;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hsub2(shmem[i], mean_2), var_2, ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_vec);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = threadIdx.x; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\n// TODO(bhsueh) add half2 implementation\ntemplate<typename T, int N>\n__global__ void addBiasResidualPostLayerNorm(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int m, int n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out_cache[N];\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = (float)(add(out[blockIdx.x * n + idx], input[blockIdx.x * n + idx], ldg(&bias[idx])));\n        mean += local_out;\n        // save local_out to local_out_cache to save some recompute\n        local_out_cache[i] = local_out;\n        idx += blockDim.x;\n    }\n\n    mean = blockReduceSum<float>(mean);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = local_out_cache[i];\n        variance += (local_out - s_mean) * (local_out - s_mean);\n        idx += blockDim.x;\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = variance / n + layernorm_eps;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = local_out_cache[i];\n        out[blockIdx.x * n + idx] =\n            (T)(((local_out - s_mean) * rsqrtf(s_variance)) * (float)(ldg(&gamma[idx])) + (float)(ldg(&beta[idx])));\n        idx += blockDim.x;\n    }\n}\n\ntemplate<int N>\n__global__ void addBiasResidualPostLayerNormHalf(half*       out,\n                                                 const half* input,\n                                                 const half* bias,\n                                                 const half* gamma,\n                                                 const half* beta,\n                                                 const float layernorm_eps,\n                                                 int         m,\n                                                 int         n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    half2*       out_ptr   = (half2*)out;\n    const half2* input_ptr = (const half2*)input;\n    const half2* bias_ptr  = (const half2*)bias;\n    const half2* gamma_ptr = (const half2*)gamma;\n    const half2* beta_ptr  = (const half2*)beta;\n\n    float2 out_fp2_cache[N];\n\n    float local_out = 0.0f;\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n / 2 && i < N; ++i) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = __half22float2(__hadd2(__hadd2(out_ptr[id], input_ptr[id]), __ldg(&bias_ptr[idx])));\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n        // save local_out_fp2 to out_fp2_cache to save some recomputation\n        out_fp2_cache[i] = local_out_fp2;\n        idx += blockDim.x;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; i < N && idx < n / 2; ++i) {\n        float2 local_out_fp2 = out_fp2_cache[i];\n        variance += (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n        idx += blockDim.x;\n    }\n\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; i < N && idx < n / 2; ++i) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = out_fp2_cache[i];\n        float2 gamma_val     = __half22float2(__ldg(&gamma_ptr[idx]));\n        float2 beta_val      = __half22float2(__ldg(&beta_ptr[idx]));\n        local_out_fp2.x      = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y      = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[id]          = __float22half2_rn(local_out_fp2);\n        idx += blockDim.x;\n    }\n}\n\n// Optimization for fp16 and fp16 (bf162 and half2)\ntemplate<typename T>\n__global__ void generalAddBiasResidualPostLayerNorm(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int m, int n)\n{\n    using T2 = typename TypeConverter<T>::Type;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    float local_out = 0.0f;\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        T2     tmp           = hadd2(hadd2(out_ptr[id], input_ptr[id]), ldg(&bias_ptr[idx]));\n        float2 local_out_fp2 = cuda_cast<float2>(tmp);\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n        // save tmp to out_ptr to save some recomputation\n        out_ptr[id] = tmp;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = cuda_cast<float2>(out_ptr[id]);\n        variance += (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    }\n\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = cuda_cast<float2>(out_ptr[id]);\n        float2 gamma_val     = cuda_cast<float2>(ldg(&gamma_ptr[idx]));\n        float2 beta_val      = cuda_cast<float2>(ldg(&beta_ptr[idx]));\n        local_out_fp2.x      = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y      = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[id]          = cuda_cast<T2>(local_out_fp2);\n    }\n}\n\ntemplate<>\n__global__ void generalAddBiasResidualPostLayerNorm(float*       out,\n                                                    const float* input,\n                                                    const float* bias,\n                                                    const float* gamma,\n                                                    const float* beta,\n                                                    const float  layernorm_eps,\n                                                    int          m,\n                                                    int          n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = (float)(out[blockIdx.x * n + idx] + input[blockIdx.x * n + idx] + __ldg(&bias[idx]));\n        mean += local_out;\n        // save local_out to out to save some recompute\n        out[blockIdx.x * n + idx] = local_out;\n    }\n\n    mean = blockReduceSum<float>(mean);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = out[blockIdx.x * n + idx];\n        variance += (local_out - s_mean) * (local_out - s_mean);\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = out[blockIdx.x * n + idx];\n        out[blockIdx.x * n + idx] =\n            (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[idx])) + (float)(__ldg(&beta[idx])));\n    }\n}\n\n// applied to half and b16\ntemplate<typename T>\n__global__ void addBiasResidualPostLayerNormV2(T* out,\n                                               const T* __restrict input,\n                                               const T* __restrict bias,\n                                               const T* __restrict gamma,\n                                               const T* __restrict beta,\n                                               const float layernorm_eps,\n                                               int         n)\n{\n    using T2             = typename TypeConverter<T>::Type;\n    const int        ite = 4;\n    const int        tid = threadIdx.x;\n    const int        bid = blockIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    T2               local_out_half2[ite];\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    // float sum = 0.0f;\n    T2 sum = cuda_cast<T2>(0.0f);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id         = i * blockDim.x + tid;\n        int id             = bid * n / 2 + col_id;\n        local_out_half2[i] = add(out_ptr[id], ldg(&input_ptr[id]), ldg(&bias_ptr[col_id]));\n        sum                = add(sum, local_out_half2[i]);\n    }\n\n    mean = blockReduceSum<float>((float)(sum.x + sum.y));\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var      = 0.0f;\n    T2    s_mean_2 = cuda_cast<T2>(s_mean);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        local_out_half2[i] = hsub2(local_out_half2[i], s_mean_2);\n        float v1           = (float)local_out_half2[i].x;\n        float v2           = (float)local_out_half2[i].y;\n        var += v1 * v1 + v2 * v2;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    T2 s_var_2 = cuda_cast<T2>(s_variance);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id  = i * blockDim.x + tid;\n        int id      = bid * n / 2 + col_id;\n        out_ptr[id] = fma(local_out_half2[i], s_var_2, ldg(&gamma_ptr[col_id]), ldg(&beta_ptr[col_id]));\n    }\n}\n\ntemplate<>\n__global__ void addBiasResidualPostLayerNormV2(float* out,\n                                               const float* __restrict input,\n                                               const float* __restrict bias,\n                                               const float* __restrict gamma,\n                                               const float* __restrict beta,\n                                               const float layernorm_eps,\n                                               int         n)\n{\n    const int ite = 4;\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id   = i * blockDim.x + tid;\n        int id       = bid * n + col_id;\n        local_out[i] = (float)(out[id] + __ldg(&input[id]) + __ldg(&bias[col_id]));\n        sum += local_out[i];\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        float diff = local_out[i] - s_mean;\n        var += diff * diff;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        int id     = bid * n + col_id;\n        out[id] =\n            (float)((local_out[i] - s_mean) * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id]));\n    }\n}\n\n// bf16 and half data type\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNorm(T*           out,\n                                    const T*     input,\n                                    const T*     bias,\n                                    const T*     gamma,\n                                    const T*     beta,\n                                    const float  layernorm_eps,\n                                    int          m,\n                                    int          n,\n                                    cudaStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(std::min(n, 1024));\n\n    if (m >= 512 && (n == 768 || n == 1024)) {\n        addBiasResidualPostLayerNormV2<T><<<grid, n / 8, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, n);\n    }\n    else {\n        block.x       = std::min(n, 1024);\n        int num_trips = (n + block.x - 1) / block.x;\n        if (num_trips == 1) {\n            addBiasResidualPostLayerNorm<T, 1>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else if (num_trips == 2) {\n            addBiasResidualPostLayerNorm<T, 2>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else {\n            generalAddBiasResidualPostLayerNorm<T>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n    }\n}\n\ntemplate<>\nvoid invokeAddBiasResidualLayerNorm(float*       out,\n                                    const float* input,\n                                    const float* bias,\n                                    const float* gamma,\n                                    const float* beta,\n                                    const float  layernorm_eps,\n                                    int          m,\n                                    int          n,\n                                    cudaStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(std::min(n, 1024));\n    if (n == 768 || n == 1024) {\n        addBiasResidualPostLayerNormV2<float>\n            <<<grid, n / 4, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, n);\n    }\n    else {\n        block.x       = std::min(n, 1024);\n        int num_trips = (n + block.x - 1) / block.x;\n        if (num_trips == 1) {\n            addBiasResidualPostLayerNorm<float, 1>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else if (num_trips == 2) {\n            addBiasResidualPostLayerNorm<float, 2>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else {\n            generalAddBiasResidualPostLayerNorm<float>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNorm(float*       out,\n                                             const float* input,\n                                             const float* bias,\n                                             const float* gamma,\n                                             const float* beta,\n                                             const float  layernorm_eps,\n                                             int          m,\n                                             int          n,\n                                             cudaStream_t stream);\ntemplate void invokeAddBiasResidualLayerNorm(half*        out,\n                                             const half*  input,\n                                             const half*  bias,\n                                             const half*  gamma,\n                                             const half*  beta,\n                                             const float  layernorm_eps,\n                                             int          m,\n                                             int          n,\n                                             cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasResidualLayerNorm(__nv_bfloat16*       out,\n                                             const __nv_bfloat16* input,\n                                             const __nv_bfloat16* bias,\n                                             const __nv_bfloat16* gamma,\n                                             const __nv_bfloat16* beta,\n                                             const float          layernorm_eps,\n                                             int                  m,\n                                             int                  n,\n                                             cudaStream_t         stream);\n#endif\n\ntemplate<typename T, int RESIDUAL_NUM>\n__global__ void generalAddBiasResidualLayerNorm(const T* __restrict input,\n                                                const T* __restrict residual1,\n                                                const T* __restrict residual2,\n                                                const T* __restrict gamma,\n                                                const T* __restrict beta,\n                                                const T* __restrict bias,\n                                                T*           output,\n                                                T*           norm_output,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                const float* scale_inter,\n                                                const float* scale_out,\n                                                const float* scale,\n                                                float*       dynamic_scale,\n                                                const int    int8_mode)\n{\n    int tid = threadIdx.x;\n\n    // NOTE: float shmem may exceed the shared memory limit\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool dynamic_scaling = dynamic_scale != nullptr;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    const bool  is_input_i32  = int8_mode == 2 && scale_inter != nullptr && scale_out != nullptr;\n    const float scale_out_val = is_input_i32 ? (*scale_inter) * (*scale_out) : 0.0f;\n    float       local_sum     = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float local_out = 0.0f;\n        if (RESIDUAL_NUM == 1) {\n            local_out = (float)(ldg(&residual1[blockIdx.x * n + i]));\n        }\n        else if (RESIDUAL_NUM == 2) {\n            local_out = (float)(ldg(&residual1[blockIdx.x * n + i])) + float(ldg(&residual2[blockIdx.x * n + i]));\n        }\n        if (is_input_i32) {\n            local_out += cuda_cast<float>(reinterpret_cast<const int32_t*>(input)[blockIdx.x * n + i]) * scale_out_val;\n        }\n        else {\n            local_out += (float)(input[blockIdx.x * n + i]);\n        }\n\n        if (bias != nullptr) {\n            local_out += (float)(ldg(&bias[i]));\n        }\n        shmem[i]                   = (T)local_out;\n        output[blockIdx.x * n + i] = (T)local_out;\n        local_sum += local_out;\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(output[blockIdx.x * n + i]) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    Scalar_T abs_max = 1e-6f;\n\n    const float scale_val = int8_mode == 2 ? *scale : 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float       beta_val = (beta == nullptr) ? 0.0f : (float)(ldg(&beta[i]));\n        const float val      = ((((float)shmem[i] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T, float>(cuda_abs(val)), abs_max);\n            shmem[i] = (T)val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<int8_t*>(norm_output)[blockIdx.x * n + i] = cuda_cast<int8_t>(val * scale_val);\n        }\n        else {\n            norm_output[blockIdx.x * n + i] = (T)val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = tid; i < n; i += blockDim.x) {\n            const int index                                      = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(norm_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_opt_version(T*           norm_output,\n                                                             T*           output,\n                                                             const T*     input,\n                                                             const T*     bias,\n                                                             const T*     residual1,\n                                                             const T*     residual2,\n                                                             const T*     gamma,\n                                                             const T*     beta,\n                                                             float        layernorm_eps,\n                                                             int          m,\n                                                             int          half_n,\n                                                             const float* scale_inter,\n                                                             const float* scale_out,\n                                                             const float* scale,\n                                                             float*       dynamic_scale,\n                                                             int          int8_mode,\n                                                             dim3         grid,\n                                                             dim3         block,\n                                                             cudaStream_t stream,\n                                                             int          opt_version)\n{\n    size_t maxbytes = half_n * sizeof(T);\n    if (opt_version == 1) {\n        if (maxbytes >= (48 << 10)) {\n            check_cuda_error(cudaFuncSetAttribute(\n                generalAddBiasResidualLayerNormOpt<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>,\n                cudaFuncAttributeMaxDynamicSharedMemorySize,\n                maxbytes));\n        }\n        generalAddBiasResidualLayerNormOpt<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>\n            <<<grid, block, maxbytes, stream>>>(norm_output,\n                                                output,\n                                                input,\n                                                bias,\n                                                residual1,\n                                                residual2,\n                                                gamma,\n                                                beta,\n                                                layernorm_eps,\n                                                m,\n                                                half_n,\n                                                scale_inter,\n                                                scale_out,\n                                                scale,\n                                                dynamic_scale,\n                                                int8_mode);\n    }\n    else if (opt_version == 2) {\n        if (maxbytes >= (48 << 10)) {\n            check_cuda_error(cudaFuncSetAttribute(\n                generalAddBiasResidualLayerNormOpt2<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>,\n                cudaFuncAttributeMaxDynamicSharedMemorySize,\n                maxbytes));\n        }\n        generalAddBiasResidualLayerNormOpt2<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>\n            <<<grid, block, maxbytes, stream>>>(norm_output,\n                                                output,\n                                                input,\n                                                bias,\n                                                residual1,\n                                                residual2,\n                                                gamma,\n                                                beta,\n                                                layernorm_eps,\n                                                m,\n                                                half_n,\n                                                scale_inter,\n                                                scale_out,\n                                                scale,\n                                                dynamic_scale,\n                                                int8_mode);\n    }\n    else {\n        FT_CHECK_WITH_INFO(false, \"opt_num must be 1 or 2\");\n    }\n}\n\ntemplate<typename T, bool IS_BIAS, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_is_output(T*           norm_output,\n                                                           T*           output,\n                                                           const T*     input,\n                                                           const T*     bias,\n                                                           const T*     residual1,\n                                                           const T*     residual2,\n                                                           const T*     gamma,\n                                                           const T*     beta,\n                                                           float        layernorm_eps,\n                                                           int          m,\n                                                           int          half_n,\n                                                           const float* scale_inter,\n                                                           const float* scale_out,\n                                                           const float* scale,\n                                                           float*       dynamic_scale,\n                                                           int          int8_mode,\n                                                           dim3         grid,\n                                                           dim3         block,\n                                                           cudaStream_t stream,\n                                                           int          opt_version,\n                                                           bool         is_output)\n{\n    if (is_output) {\n        dispatch_generalAddBiasResidualLayerNormOpt_opt_version<T, true, IS_BIAS, UNROLL_FACTOR, RESIDUAL_NUM>(\n            norm_output,\n            output,\n            input,\n            bias,\n            residual1,\n            residual2,\n            gamma,\n            beta,\n            layernorm_eps,\n            m,\n            half_n,\n            scale_inter,\n            scale_out,\n            scale,\n            dynamic_scale,\n            int8_mode,\n            grid,\n            block,\n            stream,\n            opt_version);\n    }\n    else {\n        dispatch_generalAddBiasResidualLayerNormOpt_opt_version<T, false, IS_BIAS, UNROLL_FACTOR, RESIDUAL_NUM>(\n            norm_output,\n            output,\n            input,\n            bias,\n            residual1,\n            residual2,\n            gamma,\n            beta,\n            layernorm_eps,\n            m,\n            half_n,\n            scale_inter,\n            scale_out,\n            scale,\n            dynamic_scale,\n            int8_mode,\n            grid,\n            block,\n            stream,\n            opt_version);\n    }\n}\n\ntemplate<typename T, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_bias(T*           norm_output,\n                                                      T*           output,\n                                                      const T*     input,\n                                                      const T*     bias,\n                                                      const T*     residual1,\n                                                      const T*     residual2,\n                                                      const T*     gamma,\n                                                      const T*     beta,\n                                                      float        layernorm_eps,\n                                                      int          m,\n                                                      int          half_n,\n                                                      const float* scale_inter,\n                                                      const float* scale_out,\n                                                      const float* scale,\n                                                      float*       dynamic_scale,\n                                                      int          int8_mode,\n                                                      dim3         grid,\n                                                      dim3         block,\n                                                      cudaStream_t stream,\n                                                      int          opt_version,\n                                                      bool         is_output)\n{\n    if (bias != nullptr) {\n        dispatch_generalAddBiasResidualLayerNormOpt_is_output<T, true, UNROLL_FACTOR, RESIDUAL_NUM>(norm_output,\n                                                                                                    output,\n                                                                                                    input,\n                                                                                                    bias,\n                                                                                                    residual1,\n                                                                                                    residual2,\n                                                                                                    gamma,\n                                                                                                    beta,\n                                                                                                    layernorm_eps,\n                                                                                                    m,\n                                                                                                    half_n,\n                                                                                                    scale_inter,\n                                                                                                    scale_out,\n                                                                                                    scale,\n                                                                                                    dynamic_scale,\n                                                                                                    int8_mode,\n                                                                                                    grid,\n                                                                                                    block,\n                                                                                                    stream,\n                                                                                                    opt_version,\n                                                                                                    is_output);\n    }\n    else {\n        dispatch_generalAddBiasResidualLayerNormOpt_is_output<T, false, UNROLL_FACTOR, RESIDUAL_NUM>(norm_output,\n                                                                                                     output,\n                                                                                                     input,\n                                                                                                     bias,\n                                                                                                     residual1,\n                                                                                                     residual2,\n                                                                                                     gamma,\n                                                                                                     beta,\n                                                                                                     layernorm_eps,\n                                                                                                     m,\n                                                                                                     half_n,\n                                                                                                     scale_inter,\n                                                                                                     scale_out,\n                                                                                                     scale,\n                                                                                                     dynamic_scale,\n                                                                                                     int8_mode,\n                                                                                                     grid,\n                                                                                                     block,\n                                                                                                     stream,\n                                                                                                     opt_version,\n                                                                                                     is_output);\n    }\n}\n\ntemplate<typename T, int UNROLL_FACTOR>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_residual_num(T*           norm_output,\n                                                              T*           output,\n                                                              const T*     input,\n                                                              const T*     bias,\n                                                              const T*     residual1,\n                                                              const T*     residual2,\n                                                              const T*     gamma,\n                                                              const T*     beta,\n                                                              float        layernorm_eps,\n                                                              int          m,\n                                                              int          half_n,\n                                                              const float* scale_inter,\n                                                              const float* scale_out,\n                                                              const float* scale,\n                                                              float*       dynamic_scale,\n                                                              int          int8_mode,\n                                                              dim3         grid,\n                                                              dim3         block,\n                                                              cudaStream_t stream,\n                                                              int          opt_version,\n                                                              bool         is_output,\n                                                              int          residual_num)\n{\n    if (residual_num == 1) {\n        dispatch_generalAddBiasResidualLayerNormOpt_bias<T, UNROLL_FACTOR, 1>(norm_output,\n                                                                              output,\n                                                                              input,\n                                                                              bias,\n                                                                              residual1,\n                                                                              residual2,\n                                                                              gamma,\n                                                                              beta,\n                                                                              layernorm_eps,\n                                                                              m,\n                                                                              half_n,\n                                                                              scale_inter,\n                                                                              scale_out,\n                                                                              scale,\n                                                                              dynamic_scale,\n                                                                              int8_mode,\n                                                                              grid,\n                                                                              block,\n                                                                              stream,\n                                                                              opt_version,\n                                                                              is_output);\n    }\n    else if (residual_num == 2) {\n        dispatch_generalAddBiasResidualLayerNormOpt_bias<T, UNROLL_FACTOR, 2>(norm_output,\n                                                                              output,\n                                                                              input,\n                                                                              bias,\n                                                                              residual1,\n                                                                              residual2,\n                                                                              gamma,\n                                                                              beta,\n                                                                              layernorm_eps,\n                                                                              m,\n                                                                              half_n,\n                                                                              scale_inter,\n                                                                              scale_out,\n                                                                              scale,\n                                                                              dynamic_scale,\n                                                                              int8_mode,\n                                                                              grid,\n                                                                              block,\n                                                                              stream,\n                                                                              opt_version,\n                                                                              is_output);\n    }\n    else {\n        FT_CHECK_WITH_INFO(false, \"residual_num must be 1 or 2\");\n    }\n}\n\ntemplate<typename T>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor(T*           norm_output,\n                                                               T*           output,\n                                                               const T*     input,\n                                                               const T*     bias,\n                                                               const T*     residual1,\n                                                               const T*     residual2,\n                                                               const T*     gamma,\n                                                               const T*     beta,\n                                                               float        layernorm_eps,\n                                                               int          m,\n                                                               int          half_n,\n                                                               const float* scale_inter,\n                                                               const float* scale_out,\n                                                               const float* scale,\n                                                               float*       dynamic_scale,\n                                                               int          int8_mode,\n                                                               dim3         grid,\n                                                               dim3         block,\n                                                               cudaStream_t stream,\n                                                               int          opt_version,\n                                                               bool         is_output,\n                                                               int          residual_num,\n                                                               int          unroll_factor)\n{\n    switch (unroll_factor) {\n        case 1:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 1>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 2:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 2>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 4:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 4>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 8:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 8>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        default:\n            FT_CHECK_WITH_INFO(false, \"unroll_factor must be 1, 2, 4 or 8\");\n    }\n}\n\n/* output      <- output + bias + residual_1 + residual_2\n * output_norm <- LN(output) */\ntemplate<typename T>\nvoid invokeGeneralAddBiasResidualPreLayerNorm(T*           output,\n                                              T*           norm_output,\n                                              const T*     input,\n                                              const T*     residual1,\n                                              const T*     residual2,\n                                              const T*     gamma,\n                                              const T*     beta,\n                                              const T*     bias,\n                                              const float  layernorm_eps,\n                                              int          m,\n                                              int          n,\n                                              const float* scale_inter,\n                                              const float* scale_out,\n                                              float*       scale,\n                                              float*       dynamic_scale,\n                                              const int    int8_mode,\n                                              cudaStream_t stream,\n                                              int          opt_version)\n{\n    const int  residual_num  = residual2 == nullptr ? 1 : 2;\n    const bool dynamic_quant = dynamic_scale != nullptr;\n\n    if (opt_version > 0 && sizeof(T) == 2 && n % 2 == 0) {\n        dim3 grid(m);\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)norm_output,\n                                                                  (T2*)output,\n                                                                  (const T2*)input,\n                                                                  (const T2*)bias,\n                                                                  (const T2*)residual1,\n                                                                  (const T2*)residual2,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  scale_inter,\n                                                                  scale_out,\n                                                                  scale,\n                                                                  dynamic_scale,\n                                                                  int8_mode,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  true,  // is_output\n                                                                  residual_num,\n                                                                  unroll_factor);\n    }\n    else {\n\n        dim3 grid(m);\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n        block.x = (block.x + 31) / 32 * 32;\n\n        size_t maxbytes = n * sizeof(T);\n        if (residual_num == 1) {\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(cudaFuncSetAttribute(\n                    generalAddBiasResidualLayerNorm<T, 1>, cudaFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalAddBiasResidualLayerNorm<T, 1><<<grid, block, maxbytes, stream>>>(input,\n                                                                                     residual1,\n                                                                                     residual2,\n                                                                                     gamma,\n                                                                                     beta,\n                                                                                     bias,\n                                                                                     output,\n                                                                                     norm_output,\n                                                                                     layernorm_eps,\n                                                                                     m,\n                                                                                     n,\n                                                                                     scale_inter,\n                                                                                     scale_out,\n                                                                                     scale,\n                                                                                     dynamic_scale,\n                                                                                     int8_mode);\n        }\n        else if (residual_num == 2) {\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(cudaFuncSetAttribute(\n                    generalAddBiasResidualLayerNorm<T, 2>, cudaFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalAddBiasResidualLayerNorm<T, 2><<<grid, block, maxbytes, stream>>>(input,\n                                                                                     residual1,\n                                                                                     residual2,\n                                                                                     gamma,\n                                                                                     beta,\n                                                                                     bias,\n                                                                                     output,\n                                                                                     norm_output,\n                                                                                     layernorm_eps,\n                                                                                     m,\n                                                                                     n,\n                                                                                     scale_inter,\n                                                                                     scale_out,\n                                                                                     scale,\n                                                                                     dynamic_scale,\n                                                                                     int8_mode);\n        }\n    }\n}\n\n#define INSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(T)                                                 \\\n    template void invokeGeneralAddBiasResidualPreLayerNorm(T*           output,                                        \\\n                                                           T*           norm_output,                                   \\\n                                                           const T*     input,                                         \\\n                                                           const T*     residual1,                                     \\\n                                                           const T*     residual2,                                     \\\n                                                           const T*     gamma,                                         \\\n                                                           const T*     beta,                                          \\\n                                                           const T*     bias,                                          \\\n                                                           const float  layernorm_eps,                                 \\\n                                                           int          m,                                             \\\n                                                           int          n,                                             \\\n                                                           const float* scale_inter,                                   \\\n                                                           const float* scale_out,                                     \\\n                                                           float*       scale,                                         \\\n                                                           float*       dynamic_scale,                                 \\\n                                                           const int    int8_mode,                                     \\\n                                                           cudaStream_t stream,                                        \\\n                                                           int          opt_version)\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(float);\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(__nv_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM\n\ntemplate<typename T>\n__global__ void generalAddResidualT5LayerNorm(const T* __restrict input,\n                                              const T* __restrict gamma,\n                                              T*          output,\n                                              T*          norm_output,\n                                              const float layernorm_eps,\n                                              int         m,\n                                              int         n)\n{\n    // layernorm module in the T5 style No bias and no subtraction of mean.\n    __shared__ float s_variance;\n    float            variance = 0.0f;\n\n    float local_var_sum = 0.0f;\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((float)ldg(&input[blockIdx.x * n + i]) + (float)output[blockIdx.x * n + i]);\n\n        float diff = (float)(output[blockIdx.x * n + i]);\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (float)n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        norm_output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((((float)output[blockIdx.x * n + i]) * s_variance) * (float)(ldg(&gamma[i])));\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralAddResidualT5PreLayerNorm(T*           output,\n                                            T*           norm_output,\n                                            const T*     input,\n                                            const T*     gamma,\n                                            const float  layernorm_eps,\n                                            int          m,\n                                            int          n,\n                                            cudaStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    // TODO(bhsueh) add 16bitx2 implementation\n    /* should pay attention to the rsqrt precision*/\n    generalAddResidualT5LayerNorm<T>\n        <<<grid, block, 0, stream>>>(input, gamma, output, norm_output, layernorm_eps, m, n);\n}\n\ntemplate void invokeGeneralAddResidualT5PreLayerNorm(float*       output,\n                                                     float*       norm_output,\n                                                     const float* input,\n                                                     const float* gamma,\n                                                     const float  layernorm_eps,\n                                                     int          m,\n                                                     int          n,\n                                                     cudaStream_t stream);\n\ntemplate void invokeGeneralAddResidualT5PreLayerNorm(half*        output,\n                                                     half*        norm_output,\n                                                     const half*  input,\n                                                     const half*  gamma,\n                                                     const float  layernorm_eps,\n                                                     int          m,\n                                                     int          n,\n                                                     cudaStream_t stream);\n\ntemplate<typename T>\nvoid invokeGeneralAddBiasResidualT5PreLayerNorm(T*           output,\n                                                T*           norm_output,\n                                                const T*     input,\n                                                const T*     gamma,\n                                                const T*     beta,\n                                                const T*     bias,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                cudaStream_t stream)\n{\n    if (beta != nullptr) {\n        invokeGeneralAddBiasResidualPreLayerNorm(output,\n                                                 norm_output,\n                                                 output,\n                                                 input,\n                                                 (const T*)nullptr,\n                                                 gamma,\n                                                 beta,\n                                                 bias,\n                                                 layernorm_eps,\n                                                 m,\n                                                 n,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 0,\n                                                 stream);\n    }\n    else {\n        FT_CHECK_WITH_INFO(bias == nullptr, \"bias should be nullptr when beta is nullptr\");\n        invokeGeneralAddResidualT5PreLayerNorm(output, norm_output, input, gamma, layernorm_eps, m, n, stream);\n    }\n    return;\n}\n\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(float*       output,\n                                                         float*       norm_output,\n                                                         const float* input,\n                                                         const float* gamma,\n                                                         const float* beta,\n                                                         const float* bias,\n                                                         const float  layernorm_eps,\n                                                         int          m,\n                                                         int          n,\n                                                         cudaStream_t stream);\n\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(half*        output,\n                                                         half*        norm_output,\n                                                         const half*  input,\n                                                         const half*  gamma,\n                                                         const half*  beta,\n                                                         const half*  bias,\n                                                         const float  layernorm_eps,\n                                                         int          m,\n                                                         int          n,\n                                                         cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(__nv_bfloat16*       output,\n                                                         __nv_bfloat16*       norm_output,\n                                                         const __nv_bfloat16* input,\n                                                         const __nv_bfloat16* gamma,\n                                                         const __nv_bfloat16* beta,\n                                                         const __nv_bfloat16* bias,\n                                                         const float          layernorm_eps,\n                                                         int                  m,\n                                                         int                  n,\n                                                         cudaStream_t         stream);\n#endif\n\ntemplate<typename T, bool DYNAMIC_SCALING = false>\n__global__ void generalLayerNorm(const T* __restrict input,\n                                 const T* __restrict gamma,\n                                 const T* __restrict beta,\n                                 T*          normed_output,\n                                 const float layernorm_eps,\n                                 int         m,\n                                 int         n,\n                                 float*      scale,\n                                 float*      dynamic_scale,\n                                 const int   int8_mode)\n{\n    const int tid = threadIdx.x;\n\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const Float_Packed_T scale_to_int = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n\n    float local_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        local_sum += (float)(ldg(&input[blockIdx.x * n + i]));\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(ldg(&input[blockIdx.x * n + i])) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    Scalar_T abs_max = 1e-6f;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        const int index    = blockIdx.x * n + i;\n        float     beta_val = (beta == nullptr) ? 0.0f : (float)ldg(&beta[i]);\n        T         val      = (T)((((float)input[index] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n\n        if (DYNAMIC_SCALING) {\n            abs_max  = cuda_max(cuda_max<Scalar_T, T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_to_int);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (DYNAMIC_SCALING) {\n        float          abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const Scalar_T dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = tid; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralLayerNorm(T*           out,\n                            const T*     input,\n                            const T*     gamma,\n                            const T*     beta,\n                            const float  layernorm_eps,\n                            const int    m,\n                            const int    n,\n                            float*       scale,\n                            float*       dynamic_scale,\n                            const int    int8_mode,\n                            cudaStream_t stream,\n                            int          opt_version)\n{\n    dim3       grid(m);\n    const bool dynamic_quant = dynamic_scale != nullptr;\n#ifdef ENABLE_BF16\n    if (n % 2 == 0 && (std::is_same<T, half>::value || std::is_same<T, __nv_bfloat16>::value)\n#else\n    if (n % 2 == 0 && (std::is_same<T, half>::value)\n#endif\n        && opt_version > 0) {\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)out,\n                                                                  (T2*)out,\n                                                                  (const T2*)out,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)input,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  scale,\n                                                                  dynamic_scale,\n                                                                  int8_mode,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  false,  // is_output\n                                                                  1,      // residual_num\n                                                                  unroll_factor);\n    }\n    else {\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n            Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n        if (n % 32 != 0) {\n            block.x = 1024;\n        }\n\n        /* should pay attention to the rsqrt precision*/\n        if (dynamic_quant) {\n            size_t maxbytes = n * sizeof(T);\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(cudaFuncSetAttribute(\n                    generalLayerNorm<T, true>, cudaFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalLayerNorm<T, true><<<grid, block, maxbytes, stream>>>(\n                input, gamma, beta, out, layernorm_eps, m, n, scale, dynamic_scale, int8_mode);  // For gpt-3\n        }\n        else {\n            generalLayerNorm<T, false><<<grid, block, 0, stream>>>(\n                input, gamma, beta, out, layernorm_eps, m, n, scale, dynamic_scale, int8_mode);  // For gpt-3\n        }\n    }\n}\n\ntemplate void invokeGeneralLayerNorm(float*       out,\n                                     const float* input,\n                                     const float* gamma,\n                                     const float* beta,\n                                     const float  layernorm_eps,\n                                     const int    m,\n                                     const int    n,\n                                     float*       scale,\n                                     float*       dynamic_scale,\n                                     const int    int8_mode,\n                                     cudaStream_t stream,\n                                     int          opt_version);\ntemplate void invokeGeneralLayerNorm(half*        out,\n                                     const half*  input,\n                                     const half*  gamma,\n                                     const half*  beta,\n                                     const float  layernorm_eps,\n                                     const int    m,\n                                     const int    n,\n                                     float*       scale,\n                                     float*       dynamic_scale,\n                                     const int    int8_mode,\n                                     cudaStream_t stream,\n                                     int          opt_version);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralLayerNorm(__nv_bfloat16*       out,\n                                     const __nv_bfloat16* input,\n                                     const __nv_bfloat16* gamma,\n                                     const __nv_bfloat16* beta,\n                                     const float          layernorm_eps,\n                                     const int            m,\n                                     const int            n,\n                                     float*               scale,\n                                     float*               dynamic_scale,\n                                     const int            int8_mode,\n                                     cudaStream_t         stream,\n                                     int                  opt_version);\n#endif\n\ntemplate<typename T>\n__global__ void generalT5LayerNorm(\n    const T* __restrict input, const T* __restrict gamma, T* output, const float layernorm_eps, int m, int n)\n{\n    // layernorm module in the T5 style No bias and no subtraction of mean.\n    const int tid = threadIdx.x;\n\n    __shared__ float s_variance;\n    float            variance = 0.0f;\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(ldg(&input[blockIdx.x * n + i]));\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (float)n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((((float)input[blockIdx.x * n + i]) * s_variance) * (float)(ldg(&gamma[i])));\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralT5LayerNorm(T*           out,\n                              const T*     input,\n                              const T*     gamma,\n                              const T*     beta,\n                              const float  layernorm_eps,\n                              const int    m,\n                              const int    n,\n                              cudaStream_t stream)\n{\n    if (beta != nullptr) {\n        invokeGeneralLayerNorm(out, input, gamma, beta, layernorm_eps, m, n, (float*)nullptr, 0, stream);\n        return;\n    }\n\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n    if (n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    block.x = block.x / (4 / sizeof(T));  // if using half, only need half of block.x\n\n    /* should pay attention to the rsqrt precision*/\n    generalT5LayerNorm<T><<<grid, block, 0, stream>>>(input, gamma, out, layernorm_eps, m, n);  // For gpt-3\n}\n\ntemplate void invokeGeneralT5LayerNorm(float*       out,\n                                       const float* input,\n                                       const float* gamma,\n                                       const float* beta,\n                                       const float  layernorm_eps,\n                                       const int    m,\n                                       const int    n,\n                                       cudaStream_t stream);\ntemplate void invokeGeneralT5LayerNorm(half*        out,\n                                       const half*  input,\n                                       const half*  gamma,\n                                       const half*  beta,\n                                       const float  layernorm_eps,\n                                       const int    m,\n                                       const int    n,\n                                       cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralT5LayerNorm(__nv_bfloat16*       out,\n                                       const __nv_bfloat16* input,\n                                       const __nv_bfloat16* gamma,\n                                       const __nv_bfloat16* beta,\n                                       const float          layernorm_eps,\n                                       const int            m,\n                                       const int            n,\n                                       cudaStream_t         stream);\n#endif\n\n/*******************  invokeLayernormShiftPartition  ***********************/\n\n// applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void layernorm_shift_partition(T2*         out_ptr,\n                                          const T2*   input_ptr,\n                                          const T2*   gamma_ptr,\n                                          const T2*   beta_ptr,\n                                          const float layernorm_eps,\n                                          int         batch,\n                                          int         H,\n                                          int         W,\n                                          int         n,\n                                          int         shift_size,\n                                          int         window_size)\n{\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    int       tid                = threadIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float2           local_out_fp2;\n\n    float local_out = 0.0f;\n    int   id        = bid * n + tid;\n    if (tid < n) {\n        local_out_fp2 = cuda_cast<float2>(ldg(input_ptr + id));\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0)\n        s_mean = mean / (n * 2);\n    __syncthreads();\n\n    if (tid < n) {\n        variance = (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (n * 2) + layernorm_eps);\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        float2 gamma_val              = cuda_cast<float2>(ldg(&gamma_ptr[tid]));\n        float2 beta_val               = cuda_cast<float2>(ldg(&beta_ptr[tid]));\n        local_out_fp2.x               = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y               = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[output_bid * n + tid] = cuda_cast<T2>(local_out_fp2);\n    }\n}\n\n// applied to float\ntemplate<>\n__global__ void layernorm_shift_partition<float>(float*       out,\n                                                 const float* input,\n                                                 const float* gamma,\n                                                 const float* beta,\n                                                 const float  layernorm_eps,\n                                                 int          batch,\n                                                 int          H,\n                                                 int          W,\n                                                 int          n,\n                                                 int          shift_size,\n                                                 int          window_size)\n{\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = (tid < n) ? (float)(__ldg(input + bid * n + tid)) : 0.0f;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = (tid < n) ? (local_out - s_mean) : 0.0f;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        out[output_bid * n + tid] =\n            (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[tid])) + (float)(__ldg(&beta[tid])));\n    }\n}\n\n// Applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void layernorm_shift_partition_v2(T2* out_ptr,\n                                             const T2* __restrict input_ptr,\n                                             const T2* __restrict gamma_ptr,\n                                             const T2* __restrict beta_ptr,\n                                             const float layernorm_eps,\n                                             int         batch,\n                                             int         H,\n                                             int         W,\n                                             int         n,\n                                             int         shift_size,\n                                             int         window_size)\n{\n    using T1                     = typename TypeConverter<T2>::Type;  // half2 to half, bfloat162 to bfloat\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    T2               local_out_half2[ite];\n    const T2         zero = {static_cast<T1>(0.0f), static_cast<T1>(0.0f)};\n\n    // float sum = 0.0f;\n    T2 sum = cuda_cast<T2>(0.0f);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out_half2[i] = ldg(input_ptr + offset + col_id);\n            sum                = add(sum, local_out_half2[i]);\n        }\n    }\n\n    mean = blockReduceSum<float>((float)(sum.x + sum.y));\n    if (threadIdx.x == 0) {\n        s_mean = mean / (n * 2);\n    }\n    __syncthreads();\n\n    float var      = 0.0f;\n    T2    s_mean_2 = cuda_cast<T2>(s_mean);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out_half2[i] = hsub2(local_out_half2[i], s_mean_2);\n            float v1           = (float)local_out_half2[i].x;\n            float v2           = (float)local_out_half2[i].y;\n            var += v1 * v1 + v2 * v2;\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / (n * 2) + layernorm_eps);\n    }\n    __syncthreads();\n\n    T2 s_var_2 = cuda_cast<T2>(s_variance);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out_ptr[output_offset + col_id] =\n                fma(local_out_half2[i], s_var_2, ldg(&gamma_ptr[col_id]), ldg(&beta_ptr[col_id]));\n        }\n    }\n}\n\ntemplate<>\n__global__ void layernorm_shift_partition_v2<float>(float* out,\n                                                    const float* __restrict input,\n                                                    const float* __restrict gamma,\n                                                    const float* __restrict beta,\n                                                    const float layernorm_eps,\n                                                    int         batch,\n                                                    int         H,\n                                                    int         W,\n                                                    int         n,\n                                                    int         shift_size,\n                                                    int         window_size)\n{\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = (float)(__ldg(input + offset + col_id));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            float diff   = local_out[i] - s_mean;\n            local_out[i] = diff;\n            var += diff * diff;\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[output_offset + col_id] =\n                (float)(local_out[i] * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id]));\n        }\n    }\n}\n\n// Applied to half or Bfloat16\ntemplate<typename T>\nvoid invokeLayernormShiftPartition(T*           out,\n                                   const T*     input,\n                                   const T*     gamma,\n                                   const T*     beta,\n                                   const float  layernorm_eps,\n                                   int          batch,\n                                   int          H,\n                                   int          W,\n                                   int          n,\n                                   int          shift_size,\n                                   int          window_size,\n                                   cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    using T2 = typename TypeConverter<T>::Type;  // bf162 or half2\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        layernorm_shift_partition_v2<T2><<<grid, blockSize, 0, stream>>>((T2*)out,\n                                                                         (const T2*)input,\n                                                                         (const T2*)gamma,\n                                                                         (const T2*)beta,\n                                                                         layernorm_eps,\n                                                                         batch,\n                                                                         H,\n                                                                         W,\n                                                                         n / 2,\n                                                                         shift_size,\n                                                                         window_size);\n    }\n    else {\n        layernorm_shift_partition<T2><<<grid, blockSize, 0, stream>>>((T2*)out,\n                                                                      (const T2*)input,\n                                                                      (const T2*)gamma,\n                                                                      (const T2*)beta,\n                                                                      layernorm_eps,\n                                                                      batch,\n                                                                      H,\n                                                                      W,\n                                                                      n / 2,\n                                                                      shift_size,\n                                                                      window_size);\n    }\n}\n\ntemplate<>\nvoid invokeLayernormShiftPartition<float>(float*       out,\n                                          const float* input,\n                                          const float* gamma,\n                                          const float* beta,\n                                          const float  layernorm_eps,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          int          shift_size,\n                                          int          window_size,\n                                          cudaStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        layernorm_shift_partition_v2<float><<<grid, blockSize, 0, stream>>>(\n            out, input, gamma, beta, layernorm_eps, batch, H, W, n, shift_size, window_size);\n    }\n    else {\n        layernorm_shift_partition<float><<<grid, blockSize, 0, stream>>>(\n            out, input, gamma, beta, layernorm_eps, batch, H, W, n, shift_size, window_size);\n    }\n}\n\ntemplate void invokeLayernormShiftPartition<float>(float*       out,\n                                                   const float* input,\n                                                   const float* gamma,\n                                                   const float* beta,\n                                                   const float  layernorm_eps,\n                                                   int          batch,\n                                                   int          H,\n                                                   int          W,\n                                                   int          n,\n                                                   int          shift_size,\n                                                   int          window_size,\n                                                   cudaStream_t stream);\n\ntemplate void invokeLayernormShiftPartition<half>(half*        out,\n                                                  const half*  input,\n                                                  const half*  gamma,\n                                                  const half*  beta,\n                                                  const float  layernorm_eps,\n                                                  int          batch,\n                                                  int          H,\n                                                  int          W,\n                                                  int          n,\n                                                  int          shift_size,\n                                                  int          window_size,\n                                                  cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLayernormShiftPartition<__nv_bfloat16>(__nv_bfloat16*       out,\n                                                           const __nv_bfloat16* input,\n                                                           const __nv_bfloat16* gamma,\n                                                           const __nv_bfloat16* beta,\n                                                           const float          layernorm_eps,\n                                                           int                  batch,\n                                                           int                  H,\n                                                           int                  W,\n                                                           int                  n,\n                                                           int                  shift_size,\n                                                           int                  window_size,\n                                                           cudaStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasLayernorm  ***********************/\n\ntemplate<typename T>\n__global__ void add_bias_layernorm(T* out, const T* bias, const T* gamma, const T* beta, float layernorm_eps, int n)\n{\n    int              tid = threadIdx.x;\n    const int        bid = blockIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = (tid < n) ? (float)(out[bid * n + tid] + ldg(&bias[tid])) : 0.0f;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = (tid < n) ? (local_out - s_mean) : 0.0f;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = variance / n + layernorm_eps;\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        out[bid * n + tid] =\n            (T)(((local_out - s_mean) * rsqrtf(s_variance)) * (float)(ldg(&gamma[tid])) + (float)(ldg(&beta[tid])));\n    }\n}\n\ntemplate<typename T>\n__global__ void add_bias_layernorm_v2(\n    T* out, const T* __restrict bias, const T* __restrict gamma, const T* __restrict beta, float layernorm_eps, int n)\n{\n    const int ite    = 4;\n    const int tid    = threadIdx.x;\n    const int bid    = blockIdx.x;\n    const int offset = bid * n;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id   = i * blockDim.x + tid;\n        local_out[i] = (col_id < n) ? (float)(out[offset + col_id] + ldg(&bias[col_id])) : 0.0f;\n        sum += local_out[i];\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int   col_id = i * blockDim.x + tid;\n        float diff   = (col_id < n) ? (local_out[i] - s_mean) : 0.0f;\n        var += diff * diff;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[offset + col_id] =\n                (T)((local_out[i] - s_mean) * s_variance * (float)ldg(&gamma[col_id]) + (float)ldg(&beta[col_id]));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasLayernorm(T*           out,\n                            const T*     bias,\n                            const T*     gamma,\n                            const T*     beta,\n                            float        layernorm_eps,\n                            int          m,\n                            int          n,\n                            cudaStream_t stream,\n                            int          opt_version)\n{\n    dim3 grid(m);\n    if (n % 2 == 0 && std::is_same<T, half>::value && opt_version > 0) {\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)out,\n                                                                  (T2*)out,\n                                                                  (const T2*)out,\n                                                                  (const T2*)bias,\n                                                                  (const T2*)out,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  0,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  false,  // is_output\n                                                                  1,      // residual_num\n                                                                  unroll_factor);\n    }\n    else {\n        int blockSize = (n + 31) / 32 * 32;\n        if (blockSize >= 768) {\n            blockSize = ((blockSize / 4) + 31) / 32 * 32;\n            add_bias_layernorm_v2<T><<<grid, blockSize, 0, stream>>>(out, bias, gamma, beta, layernorm_eps, n);\n        }\n        else {\n            add_bias_layernorm<T><<<grid, blockSize, 0, stream>>>(out, bias, gamma, beta, layernorm_eps, n);\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernorm<float>(float*       out,\n                                            const float* bias,\n                                            const float* gamma,\n                                            const float* beta,\n                                            const float  layernorm_eps,\n                                            int          m,\n                                            int          n,\n                                            cudaStream_t stream,\n                                            int          opt_version);\n\ntemplate void invokeAddBiasLayernorm<half>(half*        out,\n                                           const half*  bias,\n                                           const half*  gamma,\n                                           const half*  beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           cudaStream_t stream,\n                                           int          opt_version);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasLayernorm<__nv_bfloat16>(__nv_bfloat16*       out,\n                                                    const __nv_bfloat16* bias,\n                                                    const __nv_bfloat16* gamma,\n                                                    const __nv_bfloat16* beta,\n                                                    const float          layernorm_eps,\n                                                    int                  m,\n                                                    int                  n,\n                                                    cudaStream_t         stream,\n                                                    int                  opt_version);\n#endif\n\n/*******************  invokeMergeLayernorm  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (n)\ntemplate<typename T>\n__global__ void merge_layernorm_v2(T* out,\n                                   const T* __restrict input,\n                                   const T* __restrict gamma,\n                                   const T* __restrict beta,\n                                   const float layernorm_eps,\n                                   int         batch,\n                                   int         H,\n                                   int         W,\n                                   int         n)\n{\n    const int    ite             = 4;\n    const int    tid             = threadIdx.x;\n    const int    W_idx           = blockIdx.x;\n    const int    H_idx           = blockIdx.y;\n    const size_t batch_offset    = blockIdx.z * H * W * n;\n    const int    input_H_stride  = W * n / 2;\n    const int    output_H_stride = W * n;\n    const int    n_4             = n >> 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            int    part_id     = col_id / n_4;\n            int    offset_in_W = part_id / 2;\n            int    offset_in_H = part_id % 2;\n            size_t input_id    = batch_offset + (2 * H_idx + offset_in_H) * input_H_stride\n                              + (2 * W_idx + offset_in_W) * n_4 + (col_id % n_4);\n            local_out[i] = (float)(ldg(input + input_id));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = local_out[i] - s_mean;\n            var += local_out[i] * local_out[i];\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            size_t output_idx = batch_offset + H_idx * output_H_stride + W_idx * n + col_id;\n            out[output_idx]   = (T)(local_out[i] * s_variance * (float)ldg(&gamma[col_id]) + (float)ldg(&beta[col_id]));\n        }\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeMergeLayernorm(T*           output,\n                          const T*     input,\n                          const T*     gamma,\n                          const T*     beta,\n                          float        layernorm_eps,\n                          int          batch,\n                          int          H,\n                          int          W,\n                          int          n,\n                          cudaStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeMergeLayernorm] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = (blockSize + 31) / 32 * 32;\n    // TODO\n    // if (blockSize >= 768)\n    {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        merge_layernorm_v2<T>\n            <<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, layernorm_eps, batch, H / 2, W / 2, n * 4);\n    }\n    /*\n    else\n      merge_layernorm<T><<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, H/2, W/2, n*4);\n    */\n}\n\ntemplate void invokeMergeLayernorm<float>(float*       output,\n                                          const float* input,\n                                          const float* gamma,\n                                          const float* beta,\n                                          float        layernorm_eps,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          cudaStream_t stream);\n\ntemplate void invokeMergeLayernorm<half>(half*        output,\n                                         const half*  input,\n                                         const half*  gamma,\n                                         const half*  beta,\n                                         float        layernorm_eps,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeMergeLayernorm<__nv_bfloat16>(__nv_bfloat16*       output,\n                                                  const __nv_bfloat16* input,\n                                                  const __nv_bfloat16* gamma,\n                                                  const __nv_bfloat16* beta,\n                                                  const float          layernorm_eps,\n                                                  int                  batch,\n                                                  int                  H,\n                                                  int                  W,\n                                                  int                  n,\n                                                  cudaStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasLayernormAddRes  ***********************/\ntemplate<typename T, int T_per_thread>\n__global__ void add_bias_layernorm_add_res(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T   local_val[T_per_thread];\n    int b_offset = bid * n;\n    input        = input + b_offset;\n    out          = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            T out_val    = out[index];\n            T bias_val   = bias[index];\n            T tmp        = out_val + bias_val;\n            local_val[i] = tmp;\n            local_sum += float(tmp);\n        }\n    }\n\n    mean = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            float tmp = float(local_val[i]) - s_mean;\n            local_sum += tmp * tmp;\n        }\n    }\n\n    variance = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            T gamma_val  = gamma[index];\n            T beta_val   = beta[index];\n            T input_val  = input[index];\n            T output_val = T((float(local_val[i]) - s_mean) * s_variance * (float)(gamma_val) + (float)(beta_val)\n                             + (float)(input_val));\n            out[index]   = output_val;\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(half2*       out,\n                                              const half2* input,\n                                              const half2* bias,\n                                              const half2* gamma,\n                                              const half2* beta,\n                                              const float  layernorm_eps,\n                                              int          n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    const int        n_2 = n >> 1;\n\n    half2 local_val[T_per_thread];\n    int   b_offset = bid * n_2;\n    input          = input + b_offset;\n    out            = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            half2 out_val     = out[index];\n            half2 bias_val    = bias[index];\n            half2 tmp         = __hadd2(out_val, bias_val);\n            local_val[i]      = tmp;\n            float2 tmp_float2 = __half22float2(tmp);\n            local_sum += tmp_float2.x + tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = local_sum / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            tmp_float2.x      = tmp_float2.x - s_mean;\n            tmp_float2.y      = tmp_float2.y - s_mean;\n            local_sum += tmp_float2.x * tmp_float2.x + tmp_float2.y * tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(local_sum / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 gamma_val = __half22float2(gamma[index]);\n            float2 beta_val  = __half22float2(beta[index]);\n            float2 input_val = __half22float2(input[index]);\n            float2 output_val;\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            output_val.x      = (tmp_float2.x - s_mean) * s_variance * gamma_val.x + beta_val.x + input_val.x;\n            output_val.y      = (tmp_float2.y - s_mean) * s_variance * gamma_val.y + beta_val.y + input_val.y;\n            out[index]        = __float22half2_rn(output_val);\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(float2*       out,\n                                              const float2* input,\n                                              const float2* bias,\n                                              const float2* gamma,\n                                              const float2* beta,\n                                              const float   layernorm_eps,\n                                              int           n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    const int        n_2      = n >> 1;\n\n    float2 local_val[T_per_thread];\n    int    b_offset = bid * n_2;\n    input           = input + b_offset;\n    out             = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 out_val  = out[index];\n            float2 bias_val = bias[index];\n            float2 tmp;\n            tmp.x        = out_val.x + bias_val.x;\n            tmp.y        = out_val.y + bias_val.y;\n            local_val[i] = tmp;\n            local_sum += tmp.x + tmp.y;\n        }\n    }\n\n    mean = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 tmp = local_val[i];\n            tmp.x      = tmp.x - s_mean;\n            tmp.y      = tmp.y - s_mean;\n            local_sum += tmp.x * tmp.x + tmp.y * tmp.y;\n        }\n    }\n\n    variance = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 gamma_val = gamma[index];\n            float2 beta_val  = beta[index];\n            float2 input_val = input[index];\n            float2 output_val;\n            float2 tmp   = local_val[i];\n            output_val.x = (tmp.x - s_mean) * s_variance * gamma_val.x + beta_val.x + input_val.x;\n            output_val.y = (tmp.y - s_mean) * s_variance * gamma_val.y + beta_val.y + input_val.y;\n            out[index]   = output_val;\n        }\n    }\n}\n\n#define MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec)                                                                          \\\n    blockSize = (blockSize + TVec_per_thread - 1) / TVec_per_thread;                                                   \\\n    if (T_per_TVec == 2) {                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((half2*)out,                   \\\n                                                                                        (const half2*)input,           \\\n                                                                                        (const half2*)bias,            \\\n                                                                                        (const half2*)gamma,           \\\n                                                                                        (const half2*)beta,            \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        n);                            \\\n        }                                                                                                              \\\n        else if (std::is_same<T, float>::value) {                                                                      \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((float2*)out,                  \\\n                                                                                        (const float2*)input,          \\\n                                                                                        (const float2*)bias,           \\\n                                                                                        (const float2*)gamma,          \\\n                                                                                        (const float2*)beta,           \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        n);                            \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported dataType.\\n\");                \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res<half, TVec_per_thread><<<m, blockSize, 0, stream>>>((half*)out,                 \\\n                                                                                           (const half*)input,         \\\n                                                                                           (const half*)bias,          \\\n                                                                                           (const half*)gamma,         \\\n                                                                                           (const half*)beta,          \\\n                                                                                           layernorm_eps,              \\\n                                                                                           n);                         \\\n        }                                                                                                              \\\n        else if (std::is_same<T, float>::value) {                                                                      \\\n            add_bias_layernorm_add_res<float, TVec_per_thread><<<m, blockSize, 0, stream>>>((float*)out,               \\\n                                                                                            (const float*)input,       \\\n                                                                                            (const float*)bias,        \\\n                                                                                            (const float*)gamma,       \\\n                                                                                            (const float*)beta,        \\\n                                                                                            layernorm_eps,             \\\n                                                                                            n);                        \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported dataType.\\n\");                \\\n        }                                                                                                              \\\n    }\n\ntemplate<typename T>\nvoid invokeAddBiasLayernormAddRes(T*           out,\n                                  const T*     input,\n                                  const T*     bias,\n                                  const T*     gamma,\n                                  const T*     beta,\n                                  const float  layernorm_eps,\n                                  int          m,\n                                  int          n,\n                                  cudaStream_t stream)\n{\n    if (n % 2 == 0) {\n        const int T_per_TVec = 2;\n        int       blockSize  = (n / 2 + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported n.\\n\");\n        }\n    }\n    else {\n        const int T_per_TVec = 1;\n        int       blockSize  = (n + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 32768) {\n            const int TVec_per_thread = 32;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported n.\\n\");\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernormAddRes(float*       out,\n                                           const float* input,\n                                           const float* bias,\n                                           const float* gamma,\n                                           const float* beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           cudaStream_t stream);\n\ntemplate void invokeAddBiasLayernormAddRes(half*        out,\n                                           const half*  input,\n                                           const half*  bias,\n                                           const half*  gamma,\n                                           const half*  beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasLayernormAddRes(__nv_bfloat16*       out,\n                                           const __nv_bfloat16* input,\n                                           const __nv_bfloat16* bias,\n                                           const __nv_bfloat16* gamma,\n                                           const __nv_bfloat16* beta,\n                                           const float          layernorm_eps,\n                                           int                  m,\n                                           int                  n,\n                                           cudaStream_t         stream);\n#endif\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/layernorm_kernels.h\"\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int RESIDUAL_NUM, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalAddBiasResidualLayerNormOpt(T* normed_output,\n                                                   T* output,\n                                                   const T* __restrict input,\n                                                   const T* __restrict bias,\n                                                   const T* __restrict residual1,\n                                                   const T* __restrict residual2,\n                                                   const T* __restrict gamma,\n                                                   const T* __restrict beta,\n                                                   const float  layernorm_eps,\n                                                   int          m,\n                                                   int          n,\n                                                   const float* scale_inter,\n                                                   const float* scale_out,\n                                                   const float* scale,\n                                                   float*       dynamic_scale,\n                                                   const int    int8_mode)\n{\n    extern __shared__ __align__(sizeof(float)) char _shmem[];  // Align on largest type\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool scale_input     = int8_mode == 2 && scale_inter != nullptr;\n    const bool dynamic_scaling = dynamic_scale != nullptr;\n\n    T local_sum = cuda_cast<T>(0.0f);\n\n    const Float_Packed_T scale_from_int = cuda_cast<Float_Packed_T>(scale_input ? (*scale_inter) * (*scale_out) : 0.0f);\n    const Float_Packed_T scale_to_int   = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n\n#pragma unroll\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = cuda_cast<T>(0.0f);\n\n        if (IS_BIAS) {\n            val = hadd2(val, ldg(&bias[i]));\n        }\n        if (RESIDUAL_NUM == 1) {\n            val = hadd2(val, ldg(&residual1[index]));\n        }\n        else if (RESIDUAL_NUM == 2) {\n            val = hadd2(hadd2(val, ldg(&residual1[index])), ldg(&residual2[index]));\n        }\n\n        if (IS_OUTPUT) {\n            T in_val;\n            if (scale_input) {\n                in_val = cuda_cast<T>(cuda_cast<Float_Packed_T>(reinterpret_cast<const Int32_Packed_T*>(input)[index])\n                                      * scale_from_int);\n            }\n            else {\n                in_val = input[index];\n            }\n            val = hadd2(val, in_val);\n        }\n        shmem[i]      = val;\n        output[index] = val;\n        local_sum     = hadd2(local_sum, val);\n    }\n\n    mean = blockReduceSum((float)(local_sum.x + local_sum.y));\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n / 2;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        T     val    = input[blockIdx.x * n + i];\n        float diff_1 = (float)(val.x) - s_mean;\n        float diff_2 = (float)(val.y) - s_mean;\n        local_var_sum += (diff_1 * diff_1 + diff_2 * diff_2);\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n / 2 + layernorm_eps);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n    Scalar_T abs_max = 1e-6f;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hsub2(shmem[i], mean_2), var_2, ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_to_int);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = threadIdx.x; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\n// * Note that typename T is half2 or bfloat2 type\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int RESIDUAL_NUM, bool IS_BETA, int UNROLL_FACTOR>\n__global__ void generalAddBiasResidualLayerNormOpt2(T* normed_output,\n                                                    T* output,\n                                                    const T* __restrict input,\n                                                    const T* __restrict bias,\n                                                    const T* __restrict residual1,\n                                                    const T* __restrict residual2,\n                                                    const T* __restrict gamma,\n                                                    const T* __restrict beta,\n                                                    const float  layernorm_eps,\n                                                    int          m,\n                                                    int          n,\n                                                    const float* scale_inter,\n                                                    const float* scale_out,\n                                                    const float* scale,\n                                                    float*       dynamic_scale,\n                                                    const int    int8_mode)\n{\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            x_sum    = 0.0f;\n    float            x2_sum   = 0.0f;\n    const int        b_offset = blockIdx.x * n;\n\n    using T1             = typename TypeConverter<T>::Type;\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool           scale_input  = int8_mode == 2 && scale_inter != nullptr;\n    const Float_Packed_T scale_vec_in = cuda_cast<Float_Packed_T>(scale_input ? (*scale_inter) * (*scale_out) : 0.0f);\n    const Float_Packed_T scale_vec    = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n    const bool           dynamic_scaling = dynamic_scale != nullptr;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = b_offset + i;\n        float     val_1 = 0.0f;\n        float     val_2 = 0.0f;\n        T         tmp;\n\n        if (IS_BIAS) {\n            tmp = ldg(&bias[i]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        if (RESIDUAL_NUM == 1) {\n            tmp = ldg(&residual1[index]);\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        else if (RESIDUAL_NUM == 2) {\n            tmp    = ldg(&residual1[index]);\n            T tmp2 = ldg(&residual2[index]);\n            val_1 += (static_cast<float>(tmp.x) + static_cast<float>(tmp2.x));\n            val_2 += (static_cast<float>(tmp.y) + static_cast<float>(tmp2.y));\n        }\n\n        if (IS_OUTPUT) {\n            if (scale_input) {\n                tmp = cuda_cast<T>(cuda_cast<Float_Packed_T>(reinterpret_cast<const Int32_Packed_T*>(input)[index])\n                                   * scale_vec_in);\n            }\n            else {\n                tmp = ldg(&input[index]);\n            }\n            val_1 += static_cast<float>(tmp.x);\n            val_2 += static_cast<float>(tmp.y);\n        }\n        tmp.x         = cuda_cast<T1>(val_1);\n        tmp.y         = cuda_cast<T1>(val_2);\n        shmem[i]      = tmp;\n        output[index] = tmp;\n        x_sum += val_1 + val_2;\n        x2_sum += val_1 * val_1 + val_2 * val_2;\n    }\n    float sums[2];\n    sums[0] = x_sum;\n    sums[1] = x2_sum;\n    blockReduceSumV2<float, 2>(sums);\n\n    if (threadIdx.x == 0) {\n        s_mean     = sums[0] / n / 2;\n        s_variance = rsqrtf(sums[1] / n / 2 - s_mean * s_mean + layernorm_eps);\n    }\n    __syncthreads();\n\n    T mean_2 = cuda_cast<T>(s_mean);\n    T var_2  = cuda_cast<T>(s_variance);\n\n    Scalar_T abs_max = 1e-6f;\n\n#pragma unroll UNROLL_FACTOR\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        const int index = blockIdx.x * n + i;\n        T         val   = hmul2(hsub2(shmem[i], mean_2), var_2, ldg(&gamma[i]));\n        if (IS_BETA) {\n            val = hadd2(val, ldg(&beta[i]));\n        }\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_vec);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = threadIdx.x; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\n// TODO(bhsueh) add half2 implementation\ntemplate<typename T, int N>\n__global__ void addBiasResidualPostLayerNorm(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int m, int n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out_cache[N];\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = (float)(add(out[blockIdx.x * n + idx], input[blockIdx.x * n + idx], ldg(&bias[idx])));\n        mean += local_out;\n        // save local_out to local_out_cache to save some recompute\n        local_out_cache[i] = local_out;\n        idx += blockDim.x;\n    }\n\n    mean = blockReduceSum<float>(mean);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = local_out_cache[i];\n        variance += (local_out - s_mean) * (local_out - s_mean);\n        idx += blockDim.x;\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = variance / n + layernorm_eps;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n && i < N; ++i) {\n        float local_out = local_out_cache[i];\n        out[blockIdx.x * n + idx] =\n            (T)(((local_out - s_mean) * rsqrtf(s_variance)) * (float)(ldg(&gamma[idx])) + (float)(ldg(&beta[idx])));\n        idx += blockDim.x;\n    }\n}\n\ntemplate<int N>\n__global__ void addBiasResidualPostLayerNormHalf(half*       out,\n                                                 const half* input,\n                                                 const half* bias,\n                                                 const half* gamma,\n                                                 const half* beta,\n                                                 const float layernorm_eps,\n                                                 int         m,\n                                                 int         n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    half2*       out_ptr   = (half2*)out;\n    const half2* input_ptr = (const half2*)input;\n    const half2* bias_ptr  = (const half2*)bias;\n    const half2* gamma_ptr = (const half2*)gamma;\n    const half2* beta_ptr  = (const half2*)beta;\n\n    float2 out_fp2_cache[N];\n\n    float local_out = 0.0f;\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; idx < n / 2 && i < N; ++i) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = __half22float2(__hadd2(__hadd2(out_ptr[id], input_ptr[id]), __ldg(&bias_ptr[idx])));\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n        // save local_out_fp2 to out_fp2_cache to save some recomputation\n        out_fp2_cache[i] = local_out_fp2;\n        idx += blockDim.x;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; i < N && idx < n / 2; ++i) {\n        float2 local_out_fp2 = out_fp2_cache[i];\n        variance += (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n        idx += blockDim.x;\n    }\n\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll N\n    for (int idx = threadIdx.x, i = 0; i < N && idx < n / 2; ++i) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = out_fp2_cache[i];\n        float2 gamma_val     = __half22float2(__ldg(&gamma_ptr[idx]));\n        float2 beta_val      = __half22float2(__ldg(&beta_ptr[idx]));\n        local_out_fp2.x      = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y      = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[id]          = __float22half2_rn(local_out_fp2);\n        idx += blockDim.x;\n    }\n}\n\n// Optimization for fp16 and fp16 (bf162 and half2)\ntemplate<typename T>\n__global__ void generalAddBiasResidualPostLayerNorm(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int m, int n)\n{\n    using T2 = typename TypeConverter<T>::Type;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    float local_out = 0.0f;\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        T2     tmp           = hadd2(hadd2(out_ptr[id], input_ptr[id]), ldg(&bias_ptr[idx]));\n        float2 local_out_fp2 = cuda_cast<float2>(tmp);\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n        // save tmp to out_ptr to save some recomputation\n        out_ptr[id] = tmp;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = cuda_cast<float2>(out_ptr[id]);\n        variance += (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    }\n\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n / 2; idx += blockDim.x) {\n        int    id            = blockIdx.x * n / 2 + idx;\n        float2 local_out_fp2 = cuda_cast<float2>(out_ptr[id]);\n        float2 gamma_val     = cuda_cast<float2>(ldg(&gamma_ptr[idx]));\n        float2 beta_val      = cuda_cast<float2>(ldg(&beta_ptr[idx]));\n        local_out_fp2.x      = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y      = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[id]          = cuda_cast<T2>(local_out_fp2);\n    }\n}\n\ntemplate<>\n__global__ void generalAddBiasResidualPostLayerNorm(float*       out,\n                                                    const float* input,\n                                                    const float* bias,\n                                                    const float* gamma,\n                                                    const float* beta,\n                                                    const float  layernorm_eps,\n                                                    int          m,\n                                                    int          n)\n{\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = (float)(out[blockIdx.x * n + idx] + input[blockIdx.x * n + idx] + __ldg(&bias[idx]));\n        mean += local_out;\n        // save local_out to out to save some recompute\n        out[blockIdx.x * n + idx] = local_out;\n    }\n\n    mean = blockReduceSum<float>(mean);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = out[blockIdx.x * n + idx];\n        variance += (local_out - s_mean) * (local_out - s_mean);\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int idx = threadIdx.x; idx < n; idx += blockDim.x) {\n        float local_out = out[blockIdx.x * n + idx];\n        out[blockIdx.x * n + idx] =\n            (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[idx])) + (float)(__ldg(&beta[idx])));\n    }\n}\n\n// applied to half and b16\ntemplate<typename T>\n__global__ void addBiasResidualPostLayerNormV2(T* out,\n                                               const T* __restrict input,\n                                               const T* __restrict bias,\n                                               const T* __restrict gamma,\n                                               const T* __restrict beta,\n                                               const float layernorm_eps,\n                                               int         n)\n{\n    using T2             = typename TypeConverter<T>::Type;\n    const int        ite = 4;\n    const int        tid = threadIdx.x;\n    const int        bid = blockIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    T2               local_out_half2[ite];\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    // float sum = 0.0f;\n    T2 sum = cuda_cast<T2>(0.0f);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id         = i * blockDim.x + tid;\n        int id             = bid * n / 2 + col_id;\n        local_out_half2[i] = add(out_ptr[id], ldg(&input_ptr[id]), ldg(&bias_ptr[col_id]));\n        sum                = add(sum, local_out_half2[i]);\n    }\n\n    mean = blockReduceSum<float>((float)(sum.x + sum.y));\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var      = 0.0f;\n    T2    s_mean_2 = cuda_cast<T2>(s_mean);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        local_out_half2[i] = hsub2(local_out_half2[i], s_mean_2);\n        float v1           = (float)local_out_half2[i].x;\n        float v2           = (float)local_out_half2[i].y;\n        var += v1 * v1 + v2 * v2;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    T2 s_var_2 = cuda_cast<T2>(s_variance);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id  = i * blockDim.x + tid;\n        int id      = bid * n / 2 + col_id;\n        out_ptr[id] = fma(local_out_half2[i], s_var_2, ldg(&gamma_ptr[col_id]), ldg(&beta_ptr[col_id]));\n    }\n}\n\ntemplate<>\n__global__ void addBiasResidualPostLayerNormV2(float* out,\n                                               const float* __restrict input,\n                                               const float* __restrict bias,\n                                               const float* __restrict gamma,\n                                               const float* __restrict beta,\n                                               const float layernorm_eps,\n                                               int         n)\n{\n    const int ite = 4;\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id   = i * blockDim.x + tid;\n        int id       = bid * n + col_id;\n        local_out[i] = (float)(out[id] + __ldg(&input[id]) + __ldg(&bias[col_id]));\n        sum += local_out[i];\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        float diff = local_out[i] - s_mean;\n        var += diff * diff;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        int id     = bid * n + col_id;\n        out[id] =\n            (float)((local_out[i] - s_mean) * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id]));\n    }\n}\n\n// bf16 and half data type\ntemplate<typename T>\nvoid invokeAddBiasResidualLayerNorm(T*           out,\n                                    const T*     input,\n                                    const T*     bias,\n                                    const T*     gamma,\n                                    const T*     beta,\n                                    const float  layernorm_eps,\n                                    int          m,\n                                    int          n,\n                                    hipStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(std::min(n, 1024));\n\n    if (m >= 512 && (n == 768 || n == 1024)) {\n        addBiasResidualPostLayerNormV2<T><<<grid, n / 8, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, n);\n    }\n    else {\n        block.x       = std::min(n, 1024);\n        int num_trips = (n + block.x - 1) / block.x;\n        if (num_trips == 1) {\n            addBiasResidualPostLayerNorm<T, 1>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else if (num_trips == 2) {\n            addBiasResidualPostLayerNorm<T, 2>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else {\n            generalAddBiasResidualPostLayerNorm<T>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n    }\n}\n\ntemplate<>\nvoid invokeAddBiasResidualLayerNorm(float*       out,\n                                    const float* input,\n                                    const float* bias,\n                                    const float* gamma,\n                                    const float* beta,\n                                    const float  layernorm_eps,\n                                    int          m,\n                                    int          n,\n                                    hipStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(std::min(n, 1024));\n    if (n == 768 || n == 1024) {\n        addBiasResidualPostLayerNormV2<float>\n            <<<grid, n / 4, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, n);\n    }\n    else {\n        block.x       = std::min(n, 1024);\n        int num_trips = (n + block.x - 1) / block.x;\n        if (num_trips == 1) {\n            addBiasResidualPostLayerNorm<float, 1>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else if (num_trips == 2) {\n            addBiasResidualPostLayerNorm<float, 2>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n        else {\n            generalAddBiasResidualPostLayerNorm<float>\n                <<<grid, block, 0, stream>>>(out, input, bias, gamma, beta, layernorm_eps, m, n);\n        }\n    }\n}\n\ntemplate void invokeAddBiasResidualLayerNorm(float*       out,\n                                             const float* input,\n                                             const float* bias,\n                                             const float* gamma,\n                                             const float* beta,\n                                             const float  layernorm_eps,\n                                             int          m,\n                                             int          n,\n                                             hipStream_t stream);\ntemplate void invokeAddBiasResidualLayerNorm(half*        out,\n                                             const half*  input,\n                                             const half*  bias,\n                                             const half*  gamma,\n                                             const half*  beta,\n                                             const float  layernorm_eps,\n                                             int          m,\n                                             int          n,\n                                             hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasResidualLayerNorm(__hip_bfloat16*       out,\n                                             const __hip_bfloat16* input,\n                                             const __hip_bfloat16* bias,\n                                             const __hip_bfloat16* gamma,\n                                             const __hip_bfloat16* beta,\n                                             const float          layernorm_eps,\n                                             int                  m,\n                                             int                  n,\n                                             hipStream_t         stream);\n#endif\n\ntemplate<typename T, int RESIDUAL_NUM>\n__global__ void generalAddBiasResidualLayerNorm(const T* __restrict input,\n                                                const T* __restrict residual1,\n                                                const T* __restrict residual2,\n                                                const T* __restrict gamma,\n                                                const T* __restrict beta,\n                                                const T* __restrict bias,\n                                                T*           output,\n                                                T*           norm_output,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                const float* scale_inter,\n                                                const float* scale_out,\n                                                const float* scale,\n                                                float*       dynamic_scale,\n                                                const int    int8_mode)\n{\n    int tid = threadIdx.x;\n\n    // NOTE: float shmem may exceed the shared memory limit\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const bool dynamic_scaling = dynamic_scale != nullptr;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    const bool  is_input_i32  = int8_mode == 2 && scale_inter != nullptr && scale_out != nullptr;\n    const float scale_out_val = is_input_i32 ? (*scale_inter) * (*scale_out) : 0.0f;\n    float       local_sum     = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float local_out = 0.0f;\n        if (RESIDUAL_NUM == 1) {\n            local_out = (float)(ldg(&residual1[blockIdx.x * n + i]));\n        }\n        else if (RESIDUAL_NUM == 2) {\n            local_out = (float)(ldg(&residual1[blockIdx.x * n + i])) + float(ldg(&residual2[blockIdx.x * n + i]));\n        }\n        if (is_input_i32) {\n            local_out += cuda_cast<float>(reinterpret_cast<const int32_t*>(input)[blockIdx.x * n + i]) * scale_out_val;\n        }\n        else {\n            local_out += (float)(input[blockIdx.x * n + i]);\n        }\n\n        if (bias != nullptr) {\n            local_out += (float)(ldg(&bias[i]));\n        }\n        shmem[i]                   = (T)local_out;\n        output[blockIdx.x * n + i] = (T)local_out;\n        local_sum += local_out;\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(output[blockIdx.x * n + i]) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    Scalar_T abs_max = 1e-6f;\n\n    const float scale_val = int8_mode == 2 ? *scale : 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float       beta_val = (beta == nullptr) ? 0.0f : (float)(ldg(&beta[i]));\n        const float val      = ((((float)shmem[i] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n\n        if (dynamic_scaling) {\n            abs_max  = cuda_max(cuda_max<Scalar_T, float>(cuda_abs(val)), abs_max);\n            shmem[i] = (T)val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<int8_t*>(norm_output)[blockIdx.x * n + i] = cuda_cast<int8_t>(val * scale_val);\n        }\n        else {\n            norm_output[blockIdx.x * n + i] = (T)val;\n        }\n    }\n\n    if (dynamic_scaling) {\n        float       abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const float dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = tid; i < n; i += blockDim.x) {\n            const int index                                      = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(norm_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\ntemplate<typename T, bool IS_OUTPUT, bool IS_BIAS, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_opt_version(T*           norm_output,\n                                                             T*           output,\n                                                             const T*     input,\n                                                             const T*     bias,\n                                                             const T*     residual1,\n                                                             const T*     residual2,\n                                                             const T*     gamma,\n                                                             const T*     beta,\n                                                             float        layernorm_eps,\n                                                             int          m,\n                                                             int          half_n,\n                                                             const float* scale_inter,\n                                                             const float* scale_out,\n                                                             const float* scale,\n                                                             float*       dynamic_scale,\n                                                             int          int8_mode,\n                                                             dim3         grid,\n                                                             dim3         block,\n                                                             hipStream_t stream,\n                                                             int          opt_version)\n{\n    size_t maxbytes = half_n * sizeof(T);\n    if (opt_version == 1) {\n        if (maxbytes >= (48 << 10)) {\n            check_cuda_error(hipFuncSetAttribute(reinterpret_cast<const void*>(generalAddBiasResidualLayerNormOpt<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>),\n                hipFuncAttributeMaxDynamicSharedMemorySize,\n                maxbytes));\n        }\n        generalAddBiasResidualLayerNormOpt<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>\n            <<<grid, block, maxbytes, stream>>>(norm_output,\n                                                output,\n                                                input,\n                                                bias,\n                                                residual1,\n                                                residual2,\n                                                gamma,\n                                                beta,\n                                                layernorm_eps,\n                                                m,\n                                                half_n,\n                                                scale_inter,\n                                                scale_out,\n                                                scale,\n                                                dynamic_scale,\n                                                int8_mode);\n    }\n    else if (opt_version == 2) {\n        if (maxbytes >= (48 << 10)) {\n            check_cuda_error(hipFuncSetAttribute(reinterpret_cast<const void*>(generalAddBiasResidualLayerNormOpt2<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>),\n                hipFuncAttributeMaxDynamicSharedMemorySize,\n                maxbytes));\n        }\n        generalAddBiasResidualLayerNormOpt2<T, IS_OUTPUT, IS_BIAS, RESIDUAL_NUM, true, UNROLL_FACTOR>\n            <<<grid, block, maxbytes, stream>>>(norm_output,\n                                                output,\n                                                input,\n                                                bias,\n                                                residual1,\n                                                residual2,\n                                                gamma,\n                                                beta,\n                                                layernorm_eps,\n                                                m,\n                                                half_n,\n                                                scale_inter,\n                                                scale_out,\n                                                scale,\n                                                dynamic_scale,\n                                                int8_mode);\n    }\n    else {\n        FT_CHECK_WITH_INFO(false, \"opt_num must be 1 or 2\");\n    }\n}\n\ntemplate<typename T, bool IS_BIAS, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_is_output(T*           norm_output,\n                                                           T*           output,\n                                                           const T*     input,\n                                                           const T*     bias,\n                                                           const T*     residual1,\n                                                           const T*     residual2,\n                                                           const T*     gamma,\n                                                           const T*     beta,\n                                                           float        layernorm_eps,\n                                                           int          m,\n                                                           int          half_n,\n                                                           const float* scale_inter,\n                                                           const float* scale_out,\n                                                           const float* scale,\n                                                           float*       dynamic_scale,\n                                                           int          int8_mode,\n                                                           dim3         grid,\n                                                           dim3         block,\n                                                           hipStream_t stream,\n                                                           int          opt_version,\n                                                           bool         is_output)\n{\n    if (is_output) {\n        dispatch_generalAddBiasResidualLayerNormOpt_opt_version<T, true, IS_BIAS, UNROLL_FACTOR, RESIDUAL_NUM>(\n            norm_output,\n            output,\n            input,\n            bias,\n            residual1,\n            residual2,\n            gamma,\n            beta,\n            layernorm_eps,\n            m,\n            half_n,\n            scale_inter,\n            scale_out,\n            scale,\n            dynamic_scale,\n            int8_mode,\n            grid,\n            block,\n            stream,\n            opt_version);\n    }\n    else {\n        dispatch_generalAddBiasResidualLayerNormOpt_opt_version<T, false, IS_BIAS, UNROLL_FACTOR, RESIDUAL_NUM>(\n            norm_output,\n            output,\n            input,\n            bias,\n            residual1,\n            residual2,\n            gamma,\n            beta,\n            layernorm_eps,\n            m,\n            half_n,\n            scale_inter,\n            scale_out,\n            scale,\n            dynamic_scale,\n            int8_mode,\n            grid,\n            block,\n            stream,\n            opt_version);\n    }\n}\n\ntemplate<typename T, int UNROLL_FACTOR, int RESIDUAL_NUM>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_bias(T*           norm_output,\n                                                      T*           output,\n                                                      const T*     input,\n                                                      const T*     bias,\n                                                      const T*     residual1,\n                                                      const T*     residual2,\n                                                      const T*     gamma,\n                                                      const T*     beta,\n                                                      float        layernorm_eps,\n                                                      int          m,\n                                                      int          half_n,\n                                                      const float* scale_inter,\n                                                      const float* scale_out,\n                                                      const float* scale,\n                                                      float*       dynamic_scale,\n                                                      int          int8_mode,\n                                                      dim3         grid,\n                                                      dim3         block,\n                                                      hipStream_t stream,\n                                                      int          opt_version,\n                                                      bool         is_output)\n{\n    if (bias != nullptr) {\n        dispatch_generalAddBiasResidualLayerNormOpt_is_output<T, true, UNROLL_FACTOR, RESIDUAL_NUM>(norm_output,\n                                                                                                    output,\n                                                                                                    input,\n                                                                                                    bias,\n                                                                                                    residual1,\n                                                                                                    residual2,\n                                                                                                    gamma,\n                                                                                                    beta,\n                                                                                                    layernorm_eps,\n                                                                                                    m,\n                                                                                                    half_n,\n                                                                                                    scale_inter,\n                                                                                                    scale_out,\n                                                                                                    scale,\n                                                                                                    dynamic_scale,\n                                                                                                    int8_mode,\n                                                                                                    grid,\n                                                                                                    block,\n                                                                                                    stream,\n                                                                                                    opt_version,\n                                                                                                    is_output);\n    }\n    else {\n        dispatch_generalAddBiasResidualLayerNormOpt_is_output<T, false, UNROLL_FACTOR, RESIDUAL_NUM>(norm_output,\n                                                                                                     output,\n                                                                                                     input,\n                                                                                                     bias,\n                                                                                                     residual1,\n                                                                                                     residual2,\n                                                                                                     gamma,\n                                                                                                     beta,\n                                                                                                     layernorm_eps,\n                                                                                                     m,\n                                                                                                     half_n,\n                                                                                                     scale_inter,\n                                                                                                     scale_out,\n                                                                                                     scale,\n                                                                                                     dynamic_scale,\n                                                                                                     int8_mode,\n                                                                                                     grid,\n                                                                                                     block,\n                                                                                                     stream,\n                                                                                                     opt_version,\n                                                                                                     is_output);\n    }\n}\n\ntemplate<typename T, int UNROLL_FACTOR>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_residual_num(T*           norm_output,\n                                                              T*           output,\n                                                              const T*     input,\n                                                              const T*     bias,\n                                                              const T*     residual1,\n                                                              const T*     residual2,\n                                                              const T*     gamma,\n                                                              const T*     beta,\n                                                              float        layernorm_eps,\n                                                              int          m,\n                                                              int          half_n,\n                                                              const float* scale_inter,\n                                                              const float* scale_out,\n                                                              const float* scale,\n                                                              float*       dynamic_scale,\n                                                              int          int8_mode,\n                                                              dim3         grid,\n                                                              dim3         block,\n                                                              hipStream_t stream,\n                                                              int          opt_version,\n                                                              bool         is_output,\n                                                              int          residual_num)\n{\n    if (residual_num == 1) {\n        dispatch_generalAddBiasResidualLayerNormOpt_bias<T, UNROLL_FACTOR, 1>(norm_output,\n                                                                              output,\n                                                                              input,\n                                                                              bias,\n                                                                              residual1,\n                                                                              residual2,\n                                                                              gamma,\n                                                                              beta,\n                                                                              layernorm_eps,\n                                                                              m,\n                                                                              half_n,\n                                                                              scale_inter,\n                                                                              scale_out,\n                                                                              scale,\n                                                                              dynamic_scale,\n                                                                              int8_mode,\n                                                                              grid,\n                                                                              block,\n                                                                              stream,\n                                                                              opt_version,\n                                                                              is_output);\n    }\n    else if (residual_num == 2) {\n        dispatch_generalAddBiasResidualLayerNormOpt_bias<T, UNROLL_FACTOR, 2>(norm_output,\n                                                                              output,\n                                                                              input,\n                                                                              bias,\n                                                                              residual1,\n                                                                              residual2,\n                                                                              gamma,\n                                                                              beta,\n                                                                              layernorm_eps,\n                                                                              m,\n                                                                              half_n,\n                                                                              scale_inter,\n                                                                              scale_out,\n                                                                              scale,\n                                                                              dynamic_scale,\n                                                                              int8_mode,\n                                                                              grid,\n                                                                              block,\n                                                                              stream,\n                                                                              opt_version,\n                                                                              is_output);\n    }\n    else {\n        FT_CHECK_WITH_INFO(false, \"residual_num must be 1 or 2\");\n    }\n}\n\ntemplate<typename T>\nvoid dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor(T*           norm_output,\n                                                               T*           output,\n                                                               const T*     input,\n                                                               const T*     bias,\n                                                               const T*     residual1,\n                                                               const T*     residual2,\n                                                               const T*     gamma,\n                                                               const T*     beta,\n                                                               float        layernorm_eps,\n                                                               int          m,\n                                                               int          half_n,\n                                                               const float* scale_inter,\n                                                               const float* scale_out,\n                                                               const float* scale,\n                                                               float*       dynamic_scale,\n                                                               int          int8_mode,\n                                                               dim3         grid,\n                                                               dim3         block,\n                                                               hipStream_t stream,\n                                                               int          opt_version,\n                                                               bool         is_output,\n                                                               int          residual_num,\n                                                               int          unroll_factor)\n{\n    switch (unroll_factor) {\n        case 1:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 1>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 2:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 2>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 4:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 4>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        case 8:\n            dispatch_generalAddBiasResidualLayerNormOpt_residual_num<T, 8>(norm_output,\n                                                                           output,\n                                                                           input,\n                                                                           bias,\n                                                                           residual1,\n                                                                           residual2,\n                                                                           gamma,\n                                                                           beta,\n                                                                           layernorm_eps,\n                                                                           m,\n                                                                           half_n,\n                                                                           scale_inter,\n                                                                           scale_out,\n                                                                           scale,\n                                                                           dynamic_scale,\n                                                                           int8_mode,\n                                                                           grid,\n                                                                           block,\n                                                                           stream,\n                                                                           opt_version,\n                                                                           is_output,\n                                                                           residual_num);\n            break;\n        default:\n            FT_CHECK_WITH_INFO(false, \"unroll_factor must be 1, 2, 4 or 8\");\n    }\n}\n\n/* output      <- output + bias + residual_1 + residual_2\n * output_norm <- LN(output) */\ntemplate<typename T>\nvoid invokeGeneralAddBiasResidualPreLayerNorm(T*           output,\n                                              T*           norm_output,\n                                              const T*     input,\n                                              const T*     residual1,\n                                              const T*     residual2,\n                                              const T*     gamma,\n                                              const T*     beta,\n                                              const T*     bias,\n                                              const float  layernorm_eps,\n                                              int          m,\n                                              int          n,\n                                              const float* scale_inter,\n                                              const float* scale_out,\n                                              float*       scale,\n                                              float*       dynamic_scale,\n                                              const int    int8_mode,\n                                              hipStream_t stream,\n                                              int          opt_version)\n{\n    const int  residual_num  = residual2 == nullptr ? 1 : 2;\n    const bool dynamic_quant = dynamic_scale != nullptr;\n\n    if (opt_version > 0 && sizeof(T) == 2 && n % 2 == 0) {\n        dim3 grid(m);\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)norm_output,\n                                                                  (T2*)output,\n                                                                  (const T2*)input,\n                                                                  (const T2*)bias,\n                                                                  (const T2*)residual1,\n                                                                  (const T2*)residual2,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  scale_inter,\n                                                                  scale_out,\n                                                                  scale,\n                                                                  dynamic_scale,\n                                                                  int8_mode,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  true,  // is_output\n                                                                  residual_num,\n                                                                  unroll_factor);\n    }\n    else {\n\n        dim3 grid(m);\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n        block.x = (block.x + 31) / 32 * 32;\n\n        size_t maxbytes = n * sizeof(T);\n        if (residual_num == 1) {\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(hipFuncSetAttribute(reinterpret_cast<const void*>(generalAddBiasResidualLayerNorm<T, 1>), hipFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalAddBiasResidualLayerNorm<T, 1><<<grid, block, maxbytes, stream>>>(input,\n                                                                                     residual1,\n                                                                                     residual2,\n                                                                                     gamma,\n                                                                                     beta,\n                                                                                     bias,\n                                                                                     output,\n                                                                                     norm_output,\n                                                                                     layernorm_eps,\n                                                                                     m,\n                                                                                     n,\n                                                                                     scale_inter,\n                                                                                     scale_out,\n                                                                                     scale,\n                                                                                     dynamic_scale,\n                                                                                     int8_mode);\n        }\n        else if (residual_num == 2) {\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(hipFuncSetAttribute(reinterpret_cast<const void*>(generalAddBiasResidualLayerNorm<T, 2>), hipFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalAddBiasResidualLayerNorm<T, 2><<<grid, block, maxbytes, stream>>>(input,\n                                                                                     residual1,\n                                                                                     residual2,\n                                                                                     gamma,\n                                                                                     beta,\n                                                                                     bias,\n                                                                                     output,\n                                                                                     norm_output,\n                                                                                     layernorm_eps,\n                                                                                     m,\n                                                                                     n,\n                                                                                     scale_inter,\n                                                                                     scale_out,\n                                                                                     scale,\n                                                                                     dynamic_scale,\n                                                                                     int8_mode);\n        }\n    }\n}\n\n#define INSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(T)                                                 \\\n    template void invokeGeneralAddBiasResidualPreLayerNorm(T*           output,                                        \\\n                                                           T*           norm_output,                                   \\\n                                                           const T*     input,                                         \\\n                                                           const T*     residual1,                                     \\\n                                                           const T*     residual2,                                     \\\n                                                           const T*     gamma,                                         \\\n                                                           const T*     beta,                                          \\\n                                                           const T*     bias,                                          \\\n                                                           const float  layernorm_eps,                                 \\\n                                                           int          m,                                             \\\n                                                           int          n,                                             \\\n                                                           const float* scale_inter,                                   \\\n                                                           const float* scale_out,                                     \\\n                                                           float*       scale,                                         \\\n                                                           float*       dynamic_scale,                                 \\\n                                                           const int    int8_mode,                                     \\\n                                                           hipStream_t stream,                                        \\\n                                                           int          opt_version)\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(float);\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(half);\n#ifdef ENABLE_BF16\nINSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM(__hip_bfloat16);\n#endif\n#undef INSTANTIATE_INVOKE_GENERAL_ADD_BIAS_RESIDUAL_PRE_LAYER_NORM\n\ntemplate<typename T>\n__global__ void generalAddResidualT5LayerNorm(const T* __restrict input,\n                                              const T* __restrict gamma,\n                                              T*          output,\n                                              T*          norm_output,\n                                              const float layernorm_eps,\n                                              int         m,\n                                              int         n)\n{\n    // layernorm module in the T5 style No bias and no subtraction of mean.\n    __shared__ float s_variance;\n    float            variance = 0.0f;\n\n    float local_var_sum = 0.0f;\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((float)ldg(&input[blockIdx.x * n + i]) + (float)output[blockIdx.x * n + i]);\n\n        float diff = (float)(output[blockIdx.x * n + i]);\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (float)n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int i = threadIdx.x; i < n; i += blockDim.x) {\n        norm_output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((((float)output[blockIdx.x * n + i]) * s_variance) * (float)(ldg(&gamma[i])));\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralAddResidualT5PreLayerNorm(T*           output,\n                                            T*           norm_output,\n                                            const T*     input,\n                                            const T*     gamma,\n                                            const float  layernorm_eps,\n                                            int          m,\n                                            int          n,\n                                            hipStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n    Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n\n    if (n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    // TODO(bhsueh) add 16bitx2 implementation\n    /* should pay attention to the rsqrt precision*/\n    generalAddResidualT5LayerNorm<T>\n        <<<grid, block, 0, stream>>>(input, gamma, output, norm_output, layernorm_eps, m, n);\n}\n\ntemplate void invokeGeneralAddResidualT5PreLayerNorm(float*       output,\n                                                     float*       norm_output,\n                                                     const float* input,\n                                                     const float* gamma,\n                                                     const float  layernorm_eps,\n                                                     int          m,\n                                                     int          n,\n                                                     hipStream_t stream);\n\ntemplate void invokeGeneralAddResidualT5PreLayerNorm(half*        output,\n                                                     half*        norm_output,\n                                                     const half*  input,\n                                                     const half*  gamma,\n                                                     const float  layernorm_eps,\n                                                     int          m,\n                                                     int          n,\n                                                     hipStream_t stream);\n\ntemplate<typename T>\nvoid invokeGeneralAddBiasResidualT5PreLayerNorm(T*           output,\n                                                T*           norm_output,\n                                                const T*     input,\n                                                const T*     gamma,\n                                                const T*     beta,\n                                                const T*     bias,\n                                                const float  layernorm_eps,\n                                                int          m,\n                                                int          n,\n                                                hipStream_t stream)\n{\n    if (beta != nullptr) {\n        invokeGeneralAddBiasResidualPreLayerNorm(output,\n                                                 norm_output,\n                                                 output,\n                                                 input,\n                                                 (const T*)nullptr,\n                                                 gamma,\n                                                 beta,\n                                                 bias,\n                                                 layernorm_eps,\n                                                 m,\n                                                 n,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 (float*)nullptr,\n                                                 0,\n                                                 stream);\n    }\n    else {\n        FT_CHECK_WITH_INFO(bias == nullptr, \"bias should be nullptr when beta is nullptr\");\n        invokeGeneralAddResidualT5PreLayerNorm(output, norm_output, input, gamma, layernorm_eps, m, n, stream);\n    }\n    return;\n}\n\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(float*       output,\n                                                         float*       norm_output,\n                                                         const float* input,\n                                                         const float* gamma,\n                                                         const float* beta,\n                                                         const float* bias,\n                                                         const float  layernorm_eps,\n                                                         int          m,\n                                                         int          n,\n                                                         hipStream_t stream);\n\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(half*        output,\n                                                         half*        norm_output,\n                                                         const half*  input,\n                                                         const half*  gamma,\n                                                         const half*  beta,\n                                                         const half*  bias,\n                                                         const float  layernorm_eps,\n                                                         int          m,\n                                                         int          n,\n                                                         hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralAddBiasResidualT5PreLayerNorm(__hip_bfloat16*       output,\n                                                         __hip_bfloat16*       norm_output,\n                                                         const __hip_bfloat16* input,\n                                                         const __hip_bfloat16* gamma,\n                                                         const __hip_bfloat16* beta,\n                                                         const __hip_bfloat16* bias,\n                                                         const float          layernorm_eps,\n                                                         int                  m,\n                                                         int                  n,\n                                                         hipStream_t         stream);\n#endif\n\ntemplate<typename T, bool DYNAMIC_SCALING = false>\n__global__ void generalLayerNorm(const T* __restrict input,\n                                 const T* __restrict gamma,\n                                 const T* __restrict beta,\n                                 T*          normed_output,\n                                 const float layernorm_eps,\n                                 int         m,\n                                 int         n,\n                                 float*      scale,\n                                 float*      dynamic_scale,\n                                 const int   int8_mode)\n{\n    const int tid = threadIdx.x;\n\n    extern __shared__ __align__(sizeof(float)) char _shmem[];\n    T*                                              shmem = reinterpret_cast<T*>(_shmem);\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    using Int8_Packed_T  = typename packed_as<int8_t, num_elems<T>::value>::type;\n    using Int32_Packed_T = typename packed_as<int32_t, num_elems<T>::value>::type;\n    using Float_Packed_T = typename packed_as<float, num_elems<T>::value>::type;\n    using Scalar_T       = typename packed_as<T, 1>::type;\n\n    const Float_Packed_T scale_to_int = cuda_cast<Float_Packed_T>(int8_mode == 2 ? *scale : 0.0f);\n\n    float local_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        local_sum += (float)(ldg(&input[blockIdx.x * n + i]));\n    }\n\n    mean = blockReduceSum(local_sum);\n\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(ldg(&input[blockIdx.x * n + i])) - s_mean;\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    Scalar_T abs_max = 1e-6f;\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        const int index    = blockIdx.x * n + i;\n        float     beta_val = (beta == nullptr) ? 0.0f : (float)ldg(&beta[i]);\n        T         val      = (T)((((float)input[index] - s_mean) * s_variance) * (float)(ldg(&gamma[i])) + beta_val);\n\n        if (DYNAMIC_SCALING) {\n            abs_max  = cuda_max(cuda_max<Scalar_T, T>(cuda_abs(val)), abs_max);\n            shmem[i] = val;\n        }\n        else if (int8_mode == 2) {\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] =\n                cuda_cast<Int8_Packed_T>(cuda_cast<Float_Packed_T>(val) * scale_to_int);\n        }\n        else {\n            normed_output[index] = val;\n        }\n    }\n\n    if (DYNAMIC_SCALING) {\n        float          abs_max_f               = blockAllReduceMax(cuda_cast<float>(abs_max));\n        const Scalar_T dynamic_per_token_scale = 127. / abs_max_f;\n        for (int i = tid; i < n; i += blockDim.x) {\n            const int index                                        = blockIdx.x * n + i;\n            reinterpret_cast<Int8_Packed_T*>(normed_output)[index] = cuda_cast<Int8_Packed_T>(\n                cuda_cast<Float_Packed_T>(shmem[i]) * cuda_cast<Float_Packed_T>(dynamic_per_token_scale));\n        }\n        if (threadIdx.x == 0) {\n            dynamic_scale[blockIdx.x] = (*scale * abs_max_f) / 127.f;\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralLayerNorm(T*           out,\n                            const T*     input,\n                            const T*     gamma,\n                            const T*     beta,\n                            const float  layernorm_eps,\n                            const int    m,\n                            const int    n,\n                            float*       scale,\n                            float*       dynamic_scale,\n                            const int    int8_mode,\n                            hipStream_t stream,\n                            int          opt_version)\n{\n    dim3       grid(m);\n    const bool dynamic_quant = dynamic_scale != nullptr;\n#ifdef ENABLE_BF16\n    if (n % 2 == 0 && (std::is_same<T, half>::value || std::is_same<T, __hip_bfloat16>::value)\n#else\n    if (n % 2 == 0 && (std::is_same<T, half>::value)\n#endif\n        && opt_version > 0) {\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)out,\n                                                                  (T2*)out,\n                                                                  (const T2*)out,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)input,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  scale,\n                                                                  dynamic_scale,\n                                                                  int8_mode,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  false,  // is_output\n                                                                  1,      // residual_num\n                                                                  unroll_factor);\n    }\n    else {\n        dim3 block(min(n, 1024));\n\n        /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n            Since we have warp shuffle inside the code, block.x % 32 should be 0.\n        */\n        if (n % 32 != 0) {\n            block.x = 1024;\n        }\n\n        /* should pay attention to the rsqrt precision*/\n        if (dynamic_quant) {\n            size_t maxbytes = n * sizeof(T);\n            if (maxbytes >= (48 << 10)) {\n                check_cuda_error(hipFuncSetAttribute(reinterpret_cast<const void*>(generalLayerNorm<T, true>), hipFuncAttributeMaxDynamicSharedMemorySize, maxbytes));\n            }\n            generalLayerNorm<T, true><<<grid, block, maxbytes, stream>>>(\n                input, gamma, beta, out, layernorm_eps, m, n, scale, dynamic_scale, int8_mode);  // For gpt-3\n        }\n        else {\n            generalLayerNorm<T, false><<<grid, block, 0, stream>>>(\n                input, gamma, beta, out, layernorm_eps, m, n, scale, dynamic_scale, int8_mode);  // For gpt-3\n        }\n    }\n}\n\ntemplate void invokeGeneralLayerNorm(float*       out,\n                                     const float* input,\n                                     const float* gamma,\n                                     const float* beta,\n                                     const float  layernorm_eps,\n                                     const int    m,\n                                     const int    n,\n                                     float*       scale,\n                                     float*       dynamic_scale,\n                                     const int    int8_mode,\n                                     hipStream_t stream,\n                                     int          opt_version);\ntemplate void invokeGeneralLayerNorm(half*        out,\n                                     const half*  input,\n                                     const half*  gamma,\n                                     const half*  beta,\n                                     const float  layernorm_eps,\n                                     const int    m,\n                                     const int    n,\n                                     float*       scale,\n                                     float*       dynamic_scale,\n                                     const int    int8_mode,\n                                     hipStream_t stream,\n                                     int          opt_version);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralLayerNorm(__hip_bfloat16*       out,\n                                     const __hip_bfloat16* input,\n                                     const __hip_bfloat16* gamma,\n                                     const __hip_bfloat16* beta,\n                                     const float          layernorm_eps,\n                                     const int            m,\n                                     const int            n,\n                                     float*               scale,\n                                     float*               dynamic_scale,\n                                     const int            int8_mode,\n                                     hipStream_t         stream,\n                                     int                  opt_version);\n#endif\n\ntemplate<typename T>\n__global__ void generalT5LayerNorm(\n    const T* __restrict input, const T* __restrict gamma, T* output, const float layernorm_eps, int m, int n)\n{\n    // layernorm module in the T5 style No bias and no subtraction of mean.\n    const int tid = threadIdx.x;\n\n    __shared__ float s_variance;\n    float            variance = 0.0f;\n\n    float local_var_sum = 0.0f;\n    for (int i = tid; i < n; i += blockDim.x) {\n        float diff = (float)(ldg(&input[blockIdx.x * n + i]));\n        local_var_sum += diff * diff;\n    }\n    variance = blockReduceSum(local_var_sum);\n\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (float)n + layernorm_eps);\n    }\n    __syncthreads();\n\n    for (int i = tid; i < n; i += blockDim.x) {\n        output[blockIdx.x * n + i] =\n            clamp_inf_for_half<T>((((float)input[blockIdx.x * n + i]) * s_variance) * (float)(ldg(&gamma[i])));\n    }\n}\n\ntemplate<typename T>\nvoid invokeGeneralT5LayerNorm(T*           out,\n                              const T*     input,\n                              const T*     gamma,\n                              const T*     beta,\n                              const float  layernorm_eps,\n                              const int    m,\n                              const int    n,\n                              hipStream_t stream)\n{\n    if (beta != nullptr) {\n        invokeGeneralLayerNorm(out, input, gamma, beta, layernorm_eps, m, n, (float*)nullptr, 0, stream);\n        return;\n    }\n\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n\n    /* For general cases, n is equal to hidden_units, e.g., 512/1024.\n        Since we have warp shuffle inside the code, block.x % 32 should be 0.\n    */\n    if (n % 32 != 0) {\n        block.x = 1024;\n    }\n\n    block.x = block.x / (4 / sizeof(T));  // if using half, only need half of block.x\n\n    /* should pay attention to the rsqrt precision*/\n    generalT5LayerNorm<T><<<grid, block, 0, stream>>>(input, gamma, out, layernorm_eps, m, n);  // For gpt-3\n}\n\ntemplate void invokeGeneralT5LayerNorm(float*       out,\n                                       const float* input,\n                                       const float* gamma,\n                                       const float* beta,\n                                       const float  layernorm_eps,\n                                       const int    m,\n                                       const int    n,\n                                       hipStream_t stream);\ntemplate void invokeGeneralT5LayerNorm(half*        out,\n                                       const half*  input,\n                                       const half*  gamma,\n                                       const half*  beta,\n                                       const float  layernorm_eps,\n                                       const int    m,\n                                       const int    n,\n                                       hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeGeneralT5LayerNorm(__hip_bfloat16*       out,\n                                       const __hip_bfloat16* input,\n                                       const __hip_bfloat16* gamma,\n                                       const __hip_bfloat16* beta,\n                                       const float          layernorm_eps,\n                                       const int            m,\n                                       const int            n,\n                                       hipStream_t         stream);\n#endif\n\n/*******************  invokeLayernormShiftPartition  ***********************/\n\n// applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void layernorm_shift_partition(T2*         out_ptr,\n                                          const T2*   input_ptr,\n                                          const T2*   gamma_ptr,\n                                          const T2*   beta_ptr,\n                                          const float layernorm_eps,\n                                          int         batch,\n                                          int         H,\n                                          int         W,\n                                          int         n,\n                                          int         shift_size,\n                                          int         window_size)\n{\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    int       tid                = threadIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float2           local_out_fp2;\n\n    float local_out = 0.0f;\n    int   id        = bid * n + tid;\n    if (tid < n) {\n        local_out_fp2 = cuda_cast<float2>(ldg(input_ptr + id));\n        local_out += local_out_fp2.x;\n        local_out += local_out_fp2.y;\n    }\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0)\n        s_mean = mean / (n * 2);\n    __syncthreads();\n\n    if (tid < n) {\n        variance = (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n        variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    }\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / (n * 2) + layernorm_eps);\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        float2 gamma_val              = cuda_cast<float2>(ldg(&gamma_ptr[tid]));\n        float2 beta_val               = cuda_cast<float2>(ldg(&beta_ptr[tid]));\n        local_out_fp2.x               = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n        local_out_fp2.y               = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n        out_ptr[output_bid * n + tid] = cuda_cast<T2>(local_out_fp2);\n    }\n}\n\n// applied to float\ntemplate<>\n__global__ void layernorm_shift_partition<float>(float*       out,\n                                                 const float* input,\n                                                 const float* gamma,\n                                                 const float* beta,\n                                                 const float  layernorm_eps,\n                                                 int          batch,\n                                                 int          H,\n                                                 int          W,\n                                                 int          n,\n                                                 int          shift_size,\n                                                 int          window_size)\n{\n    int       tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = (tid < n) ? (float)(__ldg(input + bid * n + tid)) : 0.0f;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = (tid < n) ? (local_out - s_mean) : 0.0f;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        out[output_bid * n + tid] =\n            (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[tid])) + (float)(__ldg(&beta[tid])));\n    }\n}\n\n// Applied to half2 and bfloat162\ntemplate<typename T2>\n__global__ void layernorm_shift_partition_v2(T2* out_ptr,\n                                             const T2* __restrict input_ptr,\n                                             const T2* __restrict gamma_ptr,\n                                             const T2* __restrict beta_ptr,\n                                             const float layernorm_eps,\n                                             int         batch,\n                                             int         H,\n                                             int         W,\n                                             int         n,\n                                             int         shift_size,\n                                             int         window_size)\n{\n    using T1                     = typename TypeConverter<T2>::Type;  // half2 to half, bfloat162 to bfloat\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    T2               local_out_half2[ite];\n    const T2         zero = {static_cast<T1>(0.0f), static_cast<T1>(0.0f)};\n\n    // float sum = 0.0f;\n    T2 sum = cuda_cast<T2>(0.0f);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out_half2[i] = ldg(input_ptr + offset + col_id);\n            sum                = add(sum, local_out_half2[i]);\n        }\n    }\n\n    mean = blockReduceSum<float>((float)(sum.x + sum.y));\n    if (threadIdx.x == 0) {\n        s_mean = mean / (n * 2);\n    }\n    __syncthreads();\n\n    float var      = 0.0f;\n    T2    s_mean_2 = cuda_cast<T2>(s_mean);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out_half2[i] = hsub2(local_out_half2[i], s_mean_2);\n            float v1           = (float)local_out_half2[i].x;\n            float v2           = (float)local_out_half2[i].y;\n            var += v1 * v1 + v2 * v2;\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / (n * 2) + layernorm_eps);\n    }\n    __syncthreads();\n\n    T2 s_var_2 = cuda_cast<T2>(s_variance);\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out_ptr[output_offset + col_id] =\n                fma(local_out_half2[i], s_var_2, ldg(&gamma_ptr[col_id]), ldg(&beta_ptr[col_id]));\n        }\n    }\n}\n\ntemplate<>\n__global__ void layernorm_shift_partition_v2<float>(float* out,\n                                                    const float* __restrict input,\n                                                    const float* __restrict gamma,\n                                                    const float* __restrict beta,\n                                                    const float layernorm_eps,\n                                                    int         batch,\n                                                    int         H,\n                                                    int         W,\n                                                    int         n,\n                                                    int         shift_size,\n                                                    int         window_size)\n{\n    const int ite                = 4;\n    const int tid                = threadIdx.x;\n    const int batch_offset       = blockIdx.z * gridDim.y * gridDim.x;\n    const int bid                = batch_offset + blockIdx.y * gridDim.x + blockIdx.x;\n    const int shifted_H_idx      = (shift_size != 0) ? ((blockIdx.y - shift_size + gridDim.y) % gridDim.y) : blockIdx.y;\n    const int shifted_W_idx      = (shift_size != 0) ? ((blockIdx.x - shift_size + gridDim.x) % gridDim.x) : blockIdx.x;\n    const int window_H_idx       = shifted_H_idx / window_size;\n    const int window_W_idx       = shifted_W_idx / window_size;\n    const int stride_of_window_H = W / window_size;\n    const int window_idx         = window_H_idx * stride_of_window_H + window_W_idx;\n    const int idx_in_window      = (shifted_H_idx % window_size) * window_size + (shifted_W_idx % window_size);\n    const int output_bid         = batch_offset + window_idx * window_size * window_size + idx_in_window;\n    const int offset             = bid * n;\n    const int output_offset      = output_bid * n;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = (float)(__ldg(input + offset + col_id));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            float diff   = local_out[i] - s_mean;\n            local_out[i] = diff;\n            var += diff * diff;\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[output_offset + col_id] =\n                (float)(local_out[i] * s_variance * (float)__ldg(&gamma[col_id]) + (float)__ldg(&beta[col_id]));\n        }\n    }\n}\n\n// Applied to half or Bfloat16\ntemplate<typename T>\nvoid invokeLayernormShiftPartition(T*           out,\n                                   const T*     input,\n                                   const T*     gamma,\n                                   const T*     beta,\n                                   const float  layernorm_eps,\n                                   int          batch,\n                                   int          H,\n                                   int          W,\n                                   int          n,\n                                   int          shift_size,\n                                   int          window_size,\n                                   hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = n / 2;\n    blockSize      = (blockSize + 31) / 32 * 32;\n\n    using T2 = typename TypeConverter<T>::Type;  // bf162 or half2\n\n    if ((batch * H * W >= 512 && blockSize >= 768) || blockSize > 1024) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        layernorm_shift_partition_v2<T2><<<grid, blockSize, 0, stream>>>((T2*)out,\n                                                                         (const T2*)input,\n                                                                         (const T2*)gamma,\n                                                                         (const T2*)beta,\n                                                                         layernorm_eps,\n                                                                         batch,\n                                                                         H,\n                                                                         W,\n                                                                         n / 2,\n                                                                         shift_size,\n                                                                         window_size);\n    }\n    else {\n        layernorm_shift_partition<T2><<<grid, blockSize, 0, stream>>>((T2*)out,\n                                                                      (const T2*)input,\n                                                                      (const T2*)gamma,\n                                                                      (const T2*)beta,\n                                                                      layernorm_eps,\n                                                                      batch,\n                                                                      H,\n                                                                      W,\n                                                                      n / 2,\n                                                                      shift_size,\n                                                                      window_size);\n    }\n}\n\ntemplate<>\nvoid invokeLayernormShiftPartition<float>(float*       out,\n                                          const float* input,\n                                          const float* gamma,\n                                          const float* beta,\n                                          const float  layernorm_eps,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          int          shift_size,\n                                          int          window_size,\n                                          hipStream_t stream)\n{\n    dim3 grid(W, H, batch);\n    int  blockSize = (n + 31) / 32 * 32;\n    if (blockSize >= 768) {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        layernorm_shift_partition_v2<float><<<grid, blockSize, 0, stream>>>(\n            out, input, gamma, beta, layernorm_eps, batch, H, W, n, shift_size, window_size);\n    }\n    else {\n        layernorm_shift_partition<float><<<grid, blockSize, 0, stream>>>(\n            out, input, gamma, beta, layernorm_eps, batch, H, W, n, shift_size, window_size);\n    }\n}\n\ntemplate void invokeLayernormShiftPartition<float>(float*       out,\n                                                   const float* input,\n                                                   const float* gamma,\n                                                   const float* beta,\n                                                   const float  layernorm_eps,\n                                                   int          batch,\n                                                   int          H,\n                                                   int          W,\n                                                   int          n,\n                                                   int          shift_size,\n                                                   int          window_size,\n                                                   hipStream_t stream);\n\ntemplate void invokeLayernormShiftPartition<half>(half*        out,\n                                                  const half*  input,\n                                                  const half*  gamma,\n                                                  const half*  beta,\n                                                  const float  layernorm_eps,\n                                                  int          batch,\n                                                  int          H,\n                                                  int          W,\n                                                  int          n,\n                                                  int          shift_size,\n                                                  int          window_size,\n                                                  hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeLayernormShiftPartition<__hip_bfloat16>(__hip_bfloat16*       out,\n                                                           const __hip_bfloat16* input,\n                                                           const __hip_bfloat16* gamma,\n                                                           const __hip_bfloat16* beta,\n                                                           const float          layernorm_eps,\n                                                           int                  batch,\n                                                           int                  H,\n                                                           int                  W,\n                                                           int                  n,\n                                                           int                  shift_size,\n                                                           int                  window_size,\n                                                           hipStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasLayernorm  ***********************/\n\ntemplate<typename T>\n__global__ void add_bias_layernorm(T* out, const T* bias, const T* gamma, const T* beta, float layernorm_eps, int n)\n{\n    int              tid = threadIdx.x;\n    const int        bid = blockIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = (tid < n) ? (float)(out[bid * n + tid] + ldg(&bias[tid])) : 0.0f;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = (tid < n) ? (local_out - s_mean) : 0.0f;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = variance / n + layernorm_eps;\n    }\n    __syncthreads();\n\n    if (tid < n) {\n        out[bid * n + tid] =\n            (T)(((local_out - s_mean) * rsqrtf(s_variance)) * (float)(ldg(&gamma[tid])) + (float)(ldg(&beta[tid])));\n    }\n}\n\ntemplate<typename T>\n__global__ void add_bias_layernorm_v2(\n    T* out, const T* __restrict bias, const T* __restrict gamma, const T* __restrict beta, float layernorm_eps, int n)\n{\n    const int ite    = 4;\n    const int tid    = threadIdx.x;\n    const int bid    = blockIdx.x;\n    const int offset = bid * n;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id   = i * blockDim.x + tid;\n        local_out[i] = (col_id < n) ? (float)(out[offset + col_id] + ldg(&bias[col_id])) : 0.0f;\n        sum += local_out[i];\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int   col_id = i * blockDim.x + tid;\n        float diff   = (col_id < n) ? (local_out[i] - s_mean) : 0.0f;\n        var += diff * diff;\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            out[offset + col_id] =\n                (T)((local_out[i] - s_mean) * s_variance * (float)ldg(&gamma[col_id]) + (float)ldg(&beta[col_id]));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasLayernorm(T*           out,\n                            const T*     bias,\n                            const T*     gamma,\n                            const T*     beta,\n                            float        layernorm_eps,\n                            int          m,\n                            int          n,\n                            hipStream_t stream,\n                            int          opt_version)\n{\n    dim3 grid(m);\n    if (n % 2 == 0 && std::is_same<T, half>::value && opt_version > 0) {\n        int  half_n    = n / 2;\n        int  half_n_32 = (half_n + 31) / 32 * 32;\n        dim3 block(min(half_n_32, 512));\n        int  rolls_per_thread = half_n / block.x;\n        int  unroll_factor    = 8;\n        while (unroll_factor > rolls_per_thread && unroll_factor > 1) {\n            unroll_factor /= 2;\n        }\n        using T2 = typename TypeConverter<T>::Type;\n\n        /* we launch (and instantiate) the kernel by specializing for unroll_factor -> residual_num -> is_bias ->\n         * opt_version */\n        dispatch_generalAddBiasResidualLayerNormOpt_unroll_factor((T2*)out,\n                                                                  (T2*)out,\n                                                                  (const T2*)out,\n                                                                  (const T2*)bias,\n                                                                  (const T2*)out,\n                                                                  (const T2*)nullptr,\n                                                                  (const T2*)gamma,\n                                                                  (const T2*)beta,\n                                                                  layernorm_eps,\n                                                                  m,\n                                                                  half_n,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  nullptr,\n                                                                  0,\n                                                                  grid,\n                                                                  block,\n                                                                  stream,\n                                                                  opt_version,\n                                                                  false,  // is_output\n                                                                  1,      // residual_num\n                                                                  unroll_factor);\n    }\n    else {\n        int blockSize = (n + 31) / 32 * 32;\n        if (blockSize >= 768) {\n            blockSize = ((blockSize / 4) + 31) / 32 * 32;\n            add_bias_layernorm_v2<T><<<grid, blockSize, 0, stream>>>(out, bias, gamma, beta, layernorm_eps, n);\n        }\n        else {\n            add_bias_layernorm<T><<<grid, blockSize, 0, stream>>>(out, bias, gamma, beta, layernorm_eps, n);\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernorm<float>(float*       out,\n                                            const float* bias,\n                                            const float* gamma,\n                                            const float* beta,\n                                            const float  layernorm_eps,\n                                            int          m,\n                                            int          n,\n                                            hipStream_t stream,\n                                            int          opt_version);\n\ntemplate void invokeAddBiasLayernorm<half>(half*        out,\n                                           const half*  bias,\n                                           const half*  gamma,\n                                           const half*  beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           hipStream_t stream,\n                                           int          opt_version);\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasLayernorm<__hip_bfloat16>(__hip_bfloat16*       out,\n                                                    const __hip_bfloat16* bias,\n                                                    const __hip_bfloat16* gamma,\n                                                    const __hip_bfloat16* beta,\n                                                    const float          layernorm_eps,\n                                                    int                  m,\n                                                    int                  n,\n                                                    hipStream_t         stream,\n                                                    int                  opt_version);\n#endif\n\n/*******************  invokeMergeLayernorm  ***********************/\n\n// input is [batch, 2*H, 2*W, n/4]\n// output is [batch, H, W, n]\n// grid (W, H, batch)\n// block (n)\ntemplate<typename T>\n__global__ void merge_layernorm_v2(T* out,\n                                   const T* __restrict input,\n                                   const T* __restrict gamma,\n                                   const T* __restrict beta,\n                                   const float layernorm_eps,\n                                   int         batch,\n                                   int         H,\n                                   int         W,\n                                   int         n)\n{\n    const int    ite             = 4;\n    const int    tid             = threadIdx.x;\n    const int    W_idx           = blockIdx.x;\n    const int    H_idx           = blockIdx.y;\n    const size_t batch_offset    = blockIdx.z * H * W * n;\n    const int    input_H_stride  = W * n / 2;\n    const int    output_H_stride = W * n;\n    const int    n_4             = n >> 2;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float            local_out[ite];\n\n    float sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            int    part_id     = col_id / n_4;\n            int    offset_in_W = part_id / 2;\n            int    offset_in_H = part_id % 2;\n            size_t input_id    = batch_offset + (2 * H_idx + offset_in_H) * input_H_stride\n                              + (2 * W_idx + offset_in_W) * n_4 + (col_id % n_4);\n            local_out[i] = (float)(ldg(input + input_id));\n            sum += local_out[i];\n        }\n    }\n\n    mean = blockReduceSum<float>(sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float var = 0.0f;\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            local_out[i] = local_out[i] - s_mean;\n            var += local_out[i] * local_out[i];\n        }\n    }\n\n    variance = blockReduceSum<float>(var);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < ite; i++) {\n        int col_id = i * blockDim.x + tid;\n        if (col_id < n) {\n            size_t output_idx = batch_offset + H_idx * output_H_stride + W_idx * n + col_id;\n            out[output_idx]   = (T)(local_out[i] * s_variance * (float)ldg(&gamma[col_id]) + (float)ldg(&beta[col_id]));\n        }\n    }\n}\n\n// TODO : accelerate with half2\ntemplate<typename T>\nvoid invokeMergeLayernorm(T*           output,\n                          const T*     input,\n                          const T*     gamma,\n                          const T*     beta,\n                          float        layernorm_eps,\n                          int          batch,\n                          int          H,\n                          int          W,\n                          int          n,\n                          hipStream_t stream)\n{\n    if ((W % 2 != 0) || (H % 2 != 0)) {\n        printf(\"[ERROR][invokeMergeLayernorm] H(W) should be a multiple of 2.\\n\");\n        return;\n    }\n    dim3 grid(W / 2, H / 2, batch);\n    int  blockSize = 4 * n;\n    blockSize      = (blockSize + 31) / 32 * 32;\n    // TODO\n    // if (blockSize >= 768)\n    {\n        blockSize = ((blockSize / 4) + 31) / 32 * 32;\n        merge_layernorm_v2<T>\n            <<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, layernorm_eps, batch, H / 2, W / 2, n * 4);\n    }\n    /*\n    else\n      merge_layernorm<T><<<grid, blockSize, 0, stream>>>(output, input, gamma, beta, batch, H/2, W/2, n*4);\n    */\n}\n\ntemplate void invokeMergeLayernorm<float>(float*       output,\n                                          const float* input,\n                                          const float* gamma,\n                                          const float* beta,\n                                          float        layernorm_eps,\n                                          int          batch,\n                                          int          H,\n                                          int          W,\n                                          int          n,\n                                          hipStream_t stream);\n\ntemplate void invokeMergeLayernorm<half>(half*        output,\n                                         const half*  input,\n                                         const half*  gamma,\n                                         const half*  beta,\n                                         float        layernorm_eps,\n                                         int          batch,\n                                         int          H,\n                                         int          W,\n                                         int          n,\n                                         hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeMergeLayernorm<__hip_bfloat16>(__hip_bfloat16*       output,\n                                                  const __hip_bfloat16* input,\n                                                  const __hip_bfloat16* gamma,\n                                                  const __hip_bfloat16* beta,\n                                                  const float          layernorm_eps,\n                                                  int                  batch,\n                                                  int                  H,\n                                                  int                  W,\n                                                  int                  n,\n                                                  hipStream_t         stream);\n#endif\n\n/*******************  invokeAddBiasLayernormAddRes  ***********************/\ntemplate<typename T, int T_per_thread>\n__global__ void add_bias_layernorm_add_res(\n    T* out, const T* input, const T* bias, const T* gamma, const T* beta, const float layernorm_eps, int n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    T   local_val[T_per_thread];\n    int b_offset = bid * n;\n    input        = input + b_offset;\n    out          = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            T out_val    = out[index];\n            T bias_val   = bias[index];\n            T tmp        = out_val + bias_val;\n            local_val[i] = tmp;\n            local_sum += float(tmp);\n        }\n    }\n\n    mean = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            float tmp = float(local_val[i]) - s_mean;\n            local_sum += tmp * tmp;\n        }\n    }\n\n    variance = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n) {\n            T gamma_val  = gamma[index];\n            T beta_val   = beta[index];\n            T input_val  = input[index];\n            T output_val = T((float(local_val[i]) - s_mean) * s_variance * (float)(gamma_val) + (float)(beta_val)\n                             + (float)(input_val));\n            out[index]   = output_val;\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(half2*       out,\n                                              const half2* input,\n                                              const half2* bias,\n                                              const half2* gamma,\n                                              const half2* beta,\n                                              const float  layernorm_eps,\n                                              int          n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    const int        n_2 = n >> 1;\n\n    half2 local_val[T_per_thread];\n    int   b_offset = bid * n_2;\n    input          = input + b_offset;\n    out            = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            half2 out_val     = out[index];\n            half2 bias_val    = bias[index];\n            half2 tmp         = __hadd2(out_val, bias_val);\n            local_val[i]      = tmp;\n            float2 tmp_float2 = __half22float2(tmp);\n            local_sum += tmp_float2.x + tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = local_sum / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            tmp_float2.x      = tmp_float2.x - s_mean;\n            tmp_float2.y      = tmp_float2.y - s_mean;\n            local_sum += tmp_float2.x * tmp_float2.x + tmp_float2.y * tmp_float2.y;\n        }\n    }\n\n    local_sum = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(local_sum / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 gamma_val = __half22float2(gamma[index]);\n            float2 beta_val  = __half22float2(beta[index]);\n            float2 input_val = __half22float2(input[index]);\n            float2 output_val;\n            float2 tmp_float2 = __half22float2(local_val[i]);\n            output_val.x      = (tmp_float2.x - s_mean) * s_variance * gamma_val.x + beta_val.x + input_val.x;\n            output_val.y      = (tmp_float2.y - s_mean) * s_variance * gamma_val.y + beta_val.y + input_val.y;\n            out[index]        = __float22half2_rn(output_val);\n        }\n    }\n}\n\ntemplate<int T_per_thread>\n__global__ void add_bias_layernorm_add_res_e2(float2*       out,\n                                              const float2* input,\n                                              const float2* bias,\n                                              const float2* gamma,\n                                              const float2* beta,\n                                              const float   layernorm_eps,\n                                              int           n)\n{\n    int              tid  = threadIdx.x;\n    const int        bid  = blockIdx.x;\n    const int        bdim = blockDim.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    const int        n_2      = n >> 1;\n\n    float2 local_val[T_per_thread];\n    int    b_offset = bid * n_2;\n    input           = input + b_offset;\n    out             = out + b_offset;\n\n    float local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 out_val  = out[index];\n            float2 bias_val = bias[index];\n            float2 tmp;\n            tmp.x        = out_val.x + bias_val.x;\n            tmp.y        = out_val.y + bias_val.y;\n            local_val[i] = tmp;\n            local_sum += tmp.x + tmp.y;\n        }\n    }\n\n    mean = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    local_sum = 0.0f;\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 tmp = local_val[i];\n            tmp.x      = tmp.x - s_mean;\n            tmp.y      = tmp.y - s_mean;\n            local_sum += tmp.x * tmp.x + tmp.y * tmp.y;\n        }\n    }\n\n    variance = blockReduceSum<float>(local_sum);\n    if (tid == 0) {\n        s_variance = rsqrtf(variance / n + layernorm_eps);\n    }\n    __syncthreads();\n\n#pragma unroll\n    for (int i = 0; i < T_per_thread; i++) {\n        int index = i * bdim + tid;\n        if (index < n_2) {\n            float2 gamma_val = gamma[index];\n            float2 beta_val  = beta[index];\n            float2 input_val = input[index];\n            float2 output_val;\n            float2 tmp   = local_val[i];\n            output_val.x = (tmp.x - s_mean) * s_variance * gamma_val.x + beta_val.x + input_val.x;\n            output_val.y = (tmp.y - s_mean) * s_variance * gamma_val.y + beta_val.y + input_val.y;\n            out[index]   = output_val;\n        }\n    }\n}\n\n#define MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec)                                                                          \\\n    blockSize = (blockSize + TVec_per_thread - 1) / TVec_per_thread;                                                   \\\n    if (T_per_TVec == 2) {                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((half2*)out,                   \\\n                                                                                        (const half2*)input,           \\\n                                                                                        (const half2*)bias,            \\\n                                                                                        (const half2*)gamma,           \\\n                                                                                        (const half2*)beta,            \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        n);                            \\\n        }                                                                                                              \\\n        else if (std::is_same<T, float>::value) {                                                                      \\\n            add_bias_layernorm_add_res_e2<TVec_per_thread><<<m, blockSize, 0, stream>>>((float2*)out,                  \\\n                                                                                        (const float2*)input,          \\\n                                                                                        (const float2*)bias,           \\\n                                                                                        (const float2*)gamma,          \\\n                                                                                        (const float2*)beta,           \\\n                                                                                        layernorm_eps,                 \\\n                                                                                        n);                            \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported dataType.\\n\");                \\\n        }                                                                                                              \\\n    }                                                                                                                  \\\n    else {                                                                                                             \\\n        if (std::is_same<T, half>::value) {                                                                            \\\n            add_bias_layernorm_add_res<half, TVec_per_thread><<<m, blockSize, 0, stream>>>((half*)out,                 \\\n                                                                                           (const half*)input,         \\\n                                                                                           (const half*)bias,          \\\n                                                                                           (const half*)gamma,         \\\n                                                                                           (const half*)beta,          \\\n                                                                                           layernorm_eps,              \\\n                                                                                           n);                         \\\n        }                                                                                                              \\\n        else if (std::is_same<T, float>::value) {                                                                      \\\n            add_bias_layernorm_add_res<float, TVec_per_thread><<<m, blockSize, 0, stream>>>((float*)out,               \\\n                                                                                            (const float*)input,       \\\n                                                                                            (const float*)bias,        \\\n                                                                                            (const float*)gamma,       \\\n                                                                                            (const float*)beta,        \\\n                                                                                            layernorm_eps,             \\\n                                                                                            n);                        \\\n        }                                                                                                              \\\n        else {                                                                                                         \\\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported dataType.\\n\");                \\\n        }                                                                                                              \\\n    }\n\ntemplate<typename T>\nvoid invokeAddBiasLayernormAddRes(T*           out,\n                                  const T*     input,\n                                  const T*     bias,\n                                  const T*     gamma,\n                                  const T*     beta,\n                                  const float  layernorm_eps,\n                                  int          m,\n                                  int          n,\n                                  hipStream_t stream)\n{\n    if (n % 2 == 0) {\n        const int T_per_TVec = 2;\n        int       blockSize  = (n / 2 + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported n.\\n\");\n        }\n    }\n    else {\n        const int T_per_TVec = 1;\n        int       blockSize  = (n + 31) / 32 * 32;\n        if (blockSize <= 1024) {\n            const int TVec_per_thread = 1;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 2048) {\n            const int TVec_per_thread = 2;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 4096) {\n            const int TVec_per_thread = 4;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 8192) {\n            const int TVec_per_thread = 8;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 16384) {\n            const int TVec_per_thread = 16;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else if (blockSize <= 32768) {\n            const int TVec_per_thread = 32;\n            MACRO_ADD_BIAS_LN_ADD_RES(T_per_TVec);\n        }\n        else {\n            FT_CHECK_WITH_INFO(false, \"[ERROR][invokeAddBiasLayernormAddRes] unsupported n.\\n\");\n        }\n    }\n}\n\ntemplate void invokeAddBiasLayernormAddRes(float*       out,\n                                           const float* input,\n                                           const float* bias,\n                                           const float* gamma,\n                                           const float* beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           hipStream_t stream);\n\ntemplate void invokeAddBiasLayernormAddRes(half*        out,\n                                           const half*  input,\n                                           const half*  bias,\n                                           const half*  gamma,\n                                           const half*  beta,\n                                           const float  layernorm_eps,\n                                           int          m,\n                                           int          n,\n                                           hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeAddBiasLayernormAddRes(__hip_bfloat16*       out,\n                                           const __hip_bfloat16* input,\n                                           const __hip_bfloat16* bias,\n                                           const __hip_bfloat16* gamma,\n                                           const __hip_bfloat16* beta,\n                                           const float          layernorm_eps,\n                                           int                  m,\n                                           int                  n,\n                                           hipStream_t         stream);\n#endif\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/layernorm_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdexcept>\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n\nnamespace fastertransformer {\n\n__global__ void curandInitialize(curandState_t* state, const int size, const unsigned long long random_seed)\n{\n    if (threadIdx.x + blockIdx.x * blockDim.x < size) {\n        curand_init(random_seed, 0, 0, &state[blockIdx.x * blockDim.x + threadIdx.x]);\n    }\n}\n\nvoid invokeCurandInitialize(curandState_t*           state,\n                            const size_t             batch_size,\n                            const unsigned long long random_seed,\n                            cudaStream_t             stream)\n{\n    dim3 block(256);\n    dim3 grid((int)(ceil(batch_size * 1.0 / 256)));\n    curandInitialize<<<grid, block, 0, stream>>>(state, batch_size, random_seed);\n}\n\n__global__ void curandBatchInitialize(curandState_t* states, const int size, const unsigned long long* random_seeds)\n{\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < size) {\n        curand_init(random_seeds[idx], 0, 0, &states[idx]);\n    }\n}\n\nvoid invokeCurandBatchInitialize(curandState_t*            states,\n                                 const size_t              batch_size,\n                                 const unsigned long long* random_seeds,\n                                 cudaStream_t              stream)\n{\n    dim3 block(256);\n    dim3 grid((int)(ceil(batch_size * 1.0 / 256)));\n    curandBatchInitialize<<<grid, block, 0, stream>>>(states, batch_size, random_seeds);\n}\n\ntemplate<typename T>\n__global__ void addBiasEndMask(T*          logits,\n                               const T*    bias,\n                               const int*  end_ids,\n                               const bool* finished,\n                               const int   vocab_size,\n                               const int   vocab_size_padded)\n{\n    int  bid    = blockIdx.x;\n    bool finish = finished != nullptr ? finished[bid] : false;\n    int  offset = bid * vocab_size_padded;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        if (tid >= vocab_size) {\n            logits[offset + tid] = -MAX_T_VAL;\n        }\n        else if (finish) {\n            logits[offset + tid] = (tid == end_ids[bid]) ? MAX_T_VAL : -MAX_T_VAL;\n        }\n        else {\n            if (bias != nullptr) {\n                logits[offset + tid] += bias[tid];\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasEndMask(T*           logits,\n                          const T*     bias,\n                          const int*   end_ids,\n                          const bool*  finished,\n                          const int    batch_size,\n                          const int    vocab_size,\n                          const int    vocab_size_padded,\n                          cudaStream_t stream)\n{\n    dim3 grid(batch_size);\n    dim3 block(min(vocab_size_padded, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    addBiasEndMask<<<grid, block, 0, stream>>>(logits, bias, end_ids, finished, vocab_size, vocab_size_padded);\n}\n\ntemplate void invokeAddBiasEndMask(float*       logits,\n                                   const float* bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padded,\n                                   cudaStream_t stream);\n\ntemplate void invokeAddBiasEndMask(half*        logits,\n                                   const half*  bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padded,\n                                   cudaStream_t stream);\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage1(const T* __restrict log_probs,\n                            T*          tmp_log_probs,\n                            int*        topk_tmp_id_buf,\n                            T*          topk_tmp_val_buf,\n                            const bool* finished,\n                            const int   max_top_k,\n                            const int*  top_ks,\n                            const int   vocab_size,\n                            const int*  end_ids,\n                            const bool* skip_decode)\n{\n    typedef cub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    const int batch_id = bid / BLOCKS_PER_BEAM_;  // row id for log_probs\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n    const int block_lane = bid % BLOCKS_PER_BEAM_;                              // block id for a beam\n    const int k          = (top_ks != nullptr) ? top_ks[batch_id] : max_top_k;  // batch_id = batch index\n\n    const int tmp_log_buf_index  = batch_id * vocab_size;\n    const int tmp_topk_buf_index = batch_id * BLOCKS_PER_BEAM_ * max_top_k + block_lane * k;\n\n    TopK_2<T>  partial;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    if (finished != nullptr && finished[batch_id] == true) {\n        if (tid < k) {\n            const int index = tmp_topk_buf_index + tid;\n            if (block_lane == 0 && tid == 0) {\n                const int end_id        = end_ids[batch_id];\n                topk_tmp_id_buf[index]  = tmp_log_buf_index + end_id;\n                topk_tmp_val_buf[index] = log_probs[tmp_log_buf_index + end_id];\n            }\n            else {\n                topk_tmp_id_buf[index]  = -1;\n                topk_tmp_val_buf[index] = -MAX_T_VAL;\n            }\n        }\n        return;\n    }\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n         elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage2_sampling(const int* __restrict topk_tmp_id_buf,\n                                     T*             topk_tmp_val_buf,\n                                     int*           ids,\n                                     int*           sequence_length,\n                                     bool*          finished,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     const int      max_top_k,\n                                     const int*     top_ks,\n                                     const float    top_p,\n                                     const float*   top_ps,\n                                     curandState_t* curandstate,\n                                     const int*     end_ids,\n                                     const int      vocab_size,\n                                     const bool*    skip_decode)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    const int tid      = threadIdx.x;\n    const int batch_id = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n\n    const int   k              = (top_ks != nullptr) ? top_ks[batch_id] : max_top_k;\n    const float prob_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n    const int   size           = k * BLOCKS_PER_BEAM_;\n    const int   stride         = max_top_k * BLOCKS_PER_BEAM_;\n\n    typedef cub::BlockReduce<TopK_2<float>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage         temp_storage;\n    extern __shared__ char                               array[];\n    __shared__ float                                     rand_num;\n    __shared__ float                                     s_sum;\n    __shared__ float                                     s_max;\n    T*                                                   s_val = topk_tmp_val_buf + batch_id * stride;\n    int*                                                 s_id  = reinterpret_cast<int*>(array);\n    if (tid == 0) {\n        s_sum = 0.0f;\n    }\n    TopK_2<float> partial;\n\n    if (finished != nullptr && finished[batch_id] == true) {\n        ids[batch_id] = end_ids[batch_id];\n        return;\n    }\n\n    float* s_val2 = reinterpret_cast<float*>(s_id + k);\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE_) {\n            partial.insert((float)s_val[i], i);\n        }\n\n        TopK_2<float> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<float>);\n\n        if (tid == 0) {\n            if (ite == 0) {\n                s_max = total.u;\n            }\n            s_id[ite]      = total.p;\n            s_val[total.p] = -MAX_T_VAL;\n\n            // when cum_log_probs are computed, topk_tmp_val_buf (logits_buf_) are already pre-processed by\n            // softmax_kernel\n            if (cum_log_probs == nullptr && output_log_probs == nullptr) {\n                total.u = __expf(total.u - s_max);\n            }\n            s_val2[ite] = total.u;\n            s_sum += total.u;\n        }\n        __syncthreads();\n    }\n\n    if (tid == 0) {\n        rand_num = (float)curand_uniform(curandstate + blockIdx.x) * prob_threshold * s_sum;\n        for (int i = 0; i < k; i++) {\n            float exp_logit = s_val2[i];\n            rand_num        = rand_num - exp_logit;\n            if (rand_num <= 0.0f || i == k - 1) {\n                ids[batch_id] = topk_tmp_id_buf[batch_id * stride + s_id[i]] % vocab_size;\n                if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                    float log_prob = logf(exp_logit);\n                    if (cum_log_probs != nullptr) {\n                        cum_log_probs[batch_id] += log_prob;\n                    }\n                    if (output_log_probs != nullptr) {\n                        // 'output_log_probs' is the probability induced by the top-k sampling.\n                        // We normalize the probability 'exp_logit' of the selected token by\n                        // the probability 's_sum' of a set of top-k tokens, meaning the log_prob\n                        // is the probability of the selected token, conditioned on the event that\n                        // it is selected, i.e.,\n                        //   log_prob = log P(i | i is in top-k) = log(exp_logit / s_sum).\n                        output_log_probs[batch_id] = log_prob - logf(s_sum);\n                    }\n                }\n                break;\n            }\n        }\n        if (sequence_length != nullptr && finished != nullptr) {\n            sequence_length[batch_id] = finished[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n            finished[batch_id]        = ids[batch_id] == end_ids[batch_id] ? true : false;\n        }\n    }\n}\n\n#define CASE_K(K_MIN, K_MAX, BLOCK_SIZE_1_, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_)                                           \\\n    case K_MIN ... K_MAX:                                                                                              \\\n        topk_stage1<T, BLOCK_SIZE_1_, BLOCKS_PER_BEAM_>                                                                \\\n            <<<batch_size * BLOCKS_PER_BEAM_, BLOCK_SIZE_1_, 0, stream>>>(log_probs,                                   \\\n                                                                          temp_log_probs,                              \\\n                                                                          topk_tmp_id_buf,                             \\\n                                                                          topk_tmp_val_buf,                            \\\n                                                                          finished,                                    \\\n                                                                          max_top_k,                                   \\\n                                                                          top_ks,                                      \\\n                                                                          vocab_size,                                  \\\n                                                                          end_ids,                                     \\\n                                                                          skip_decode);                                \\\n        topk_stage2_sampling<T, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_>                                                       \\\n            <<<batch_size, BLOCK_SIZE_2_, K_MAX * sizeof(int) + K_MAX * sizeof(float), stream>>>(topk_tmp_id_buf,      \\\n                                                                                                 topk_tmp_val_buf,     \\\n                                                                                                 ids,                  \\\n                                                                                                 sequence_length,      \\\n                                                                                                 finished,             \\\n                                                                                                 cum_log_probs,        \\\n                                                                                                 output_log_probs,     \\\n                                                                                                 max_top_k,            \\\n                                                                                                 top_ks,               \\\n                                                                                                 top_p,                \\\n                                                                                                 top_ps,               \\\n                                                                                                 curandstate,          \\\n                                                                                                 end_ids,              \\\n                                                                                                 vocab_size,           \\\n                                                                                                 skip_decode);         \\\n        break;\n\ntemplate<typename T>\nvoid invokeBatchTopKSampling(void*          workspace,\n                             size_t&        workspace_size,\n                             const T*       log_probs,\n                             int*           ids,\n                             int*           sequence_length,\n                             bool*          finished,\n                             float*         cum_log_probs,\n                             float*         output_log_probs,\n                             curandState_t* curandstate,\n                             const int      max_top_k,\n                             const int*     top_ks,\n                             const float    top_p,\n                             const float*   top_ps,\n                             const int      vocab_size_padded,\n                             const int*     end_ids,\n                             cudaStream_t   stream,\n                             const int      batch_size,\n                             const bool*    skip_decode)\n{\n    // Not allow an ambiguous inputs top_p and top_ps.\n    assert(top_p == 1.0f || top_ps == nullptr);\n    const int vocab_size              = vocab_size_padded;\n    const int max_block_per_beam      = 8;\n    int       temp_log_probs_buf_size = batch_size * vocab_size;                      // type float\n    int       topk_tmp_ids_buf_size   = batch_size * max_top_k * max_block_per_beam;  // type int\n    int       topk_tmp_val_buf_size   = batch_size * max_top_k * max_block_per_beam;  // type float\n\n    // prevent memory misaligned address\n    temp_log_probs_buf_size = (int)(ceil(temp_log_probs_buf_size / 4.)) * 4;\n    topk_tmp_ids_buf_size   = (int)(ceil(topk_tmp_ids_buf_size / 4.)) * 4;\n    topk_tmp_val_buf_size   = (int)(ceil(topk_tmp_val_buf_size / 4.)) * 4;\n\n    if (workspace == nullptr) {\n        workspace_size = sizeof(T) * temp_log_probs_buf_size + sizeof(int) * topk_tmp_ids_buf_size\n                         + sizeof(T) * topk_tmp_val_buf_size;\n        return;\n    }\n\n    T*   temp_log_probs   = (T*)workspace;\n    int* topk_tmp_id_buf  = (int*)(temp_log_probs + temp_log_probs_buf_size);\n    T*   topk_tmp_val_buf = (T*)(topk_tmp_id_buf + topk_tmp_ids_buf_size);\n\n    switch (max_top_k) {\n        CASE_K(1, 16, 128, 128, 8);\n        CASE_K(17, 32, 256, 128, 8);\n        CASE_K(33, 64, 256, 256, 8);\n        CASE_K(65, 1024, 256, 256, 8);\n        default:\n            throw std::domain_error(fmtstr(\"top-k kernel supports 1<=k<=1024 but got k=%d\", max_top_k));\n    }\n}\n\n#undef CASE_K\n\ntemplate void invokeBatchTopKSampling(void*          workspace,\n                                      size_t&        workspace_size,\n                                      const float*   log_probs,\n                                      int*           ids,\n                                      int*           sequence_length,\n                                      bool*          finished_buf,\n                                      float*         cum_log_probs,\n                                      float*         output_log_probs,\n                                      curandState_t* curandstate,\n                                      const int      max_top_k,\n                                      const int*     top_ks,\n                                      const float    top_p,\n                                      const float*   top_ps,\n                                      const int      vocab_size_padded,\n                                      const int*     end_ids,\n                                      cudaStream_t   stream,\n                                      const int      batch_size,\n                                      const bool*    skip_decode);\n\ntemplate void invokeBatchTopKSampling(void*          workspace,\n                                      size_t&        workspace_size,\n                                      const half*    log_probs,\n                                      int*           ids,\n                                      int*           sequence_length,\n                                      bool*          finished_buf,\n                                      float*         cum_log_probs,\n                                      float*         output_log_probs,\n                                      curandState_t* curandstate,\n                                      const int      max_top_k,\n                                      const int*     top_ks,\n                                      const float    top_p,\n                                      const float*   top_ps,\n                                      const int      vocab_size_padded,\n                                      const int*     end_ids,\n                                      cudaStream_t   stream,\n                                      const int      batch_size,\n                                      const bool*    skip_decode);\n\ntemplate<typename T>\nvoid invokeTopKSampling(void*          workspace,\n                        size_t&        workspace_size,\n                        const T*       log_probs,\n                        int*           ids,\n                        int*           sequence_length,\n                        bool*          finished_buf,\n                        float*         cum_log_probs,\n                        float*         output_log_probs,\n                        curandState_t* curandstate,\n                        const int      top_k,\n                        const float    top_p,\n                        const int      vocab_size_padded,\n                        const int*     end_ids,\n                        cudaStream_t   stream,\n                        const int      batch_size,\n                        const bool*    skip_decode)\n{\n    invokeBatchTopKSampling(workspace,\n                            workspace_size,\n                            log_probs,\n                            ids,\n                            sequence_length,\n                            finished_buf,\n                            cum_log_probs,\n                            output_log_probs,\n                            curandstate,\n                            top_k,\n                            nullptr,\n                            top_p,\n                            nullptr,\n                            vocab_size_padded,\n                            end_ids,\n                            stream,\n                            batch_size,\n                            skip_decode);\n}\n\ntemplate void invokeTopKSampling(void*          workspace,\n                                 size_t&        workspace_size,\n                                 const float*   log_probs,\n                                 int*           ids,\n                                 int*           sequence_length,\n                                 bool*          finished_buf,\n                                 float*         cum_log_probs,\n                                 float*         output_log_probs,\n                                 curandState_t* curandstate,\n                                 const int      top_k,\n                                 const float    top_p,\n                                 const int      vocab_size_padded,\n                                 const int*     end_ids,\n                                 cudaStream_t   stream,\n                                 const int      batch_size,\n                                 const bool*    skip_decode);\n\ntemplate void invokeTopKSampling(void*          workspace,\n                                 size_t&        workspace_size,\n                                 const half*    log_probs,\n                                 int*           ids,\n                                 int*           sequence_length,\n                                 bool*          finished_buf,\n                                 float*         cum_log_probs,\n                                 float*         output_log_probs,\n                                 curandState_t* curandstate,\n                                 const int      top_k,\n                                 const float    top_p,\n                                 const int      vocab_size_padded,\n                                 const int*     end_ids,\n                                 cudaStream_t   stream,\n                                 const int      batch_size,\n                                 const bool*    skip_decode);\n\ntemplate<typename T>\nvoid invokeTopKTopPSampling(void*          workspace,\n                            size_t&        workspace_size,\n                            int*           output_ids,\n                            const T*       logits,\n                            int*           sequence_length,\n                            bool*          finished_buf,\n                            float*         cum_log_probs,\n                            float*         output_log_probs,\n                            curandState_t* curandstate,\n                            const int      batch_size,\n                            const int      top_k,\n                            const float    top_p,\n                            const int      vocab_size_padded,\n                            const int*     end_ids,\n                            cudaStream_t   stream)\n{\n    // invokeTopKTopPSampling will be deprecated. Please use invokeTopKSampling instead.\n    invokeTopKSampling(workspace,\n                       workspace_size,\n                       logits,\n                       output_ids,\n                       sequence_length,\n                       finished_buf,\n                       cum_log_probs,\n                       output_log_probs,\n                       curandstate,\n                       top_k,\n                       top_p,\n                       vocab_size_padded,\n                       end_ids,\n                       stream,\n                       batch_size,\n                       nullptr);\n}\n\ntemplate void invokeTopKTopPSampling(void*          workspace,\n                                     size_t&        workspace_size,\n                                     int*           output_ids,\n                                     const float*   logits,\n                                     int*           sequence_length,\n                                     bool*          finished_buf,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     curandState_t* curandstate,\n                                     const int      batch_size,\n                                     const int      top_k,\n                                     const float    top_p,\n                                     const int      vocab_size_padded,\n                                     const int*     end_ids,\n                                     cudaStream_t   stream);\n\ntemplate void invokeTopKTopPSampling(void*          workspace,\n                                     size_t&        workspace_size,\n                                     int*           output_ids,\n                                     const half*    logits,\n                                     int*           sequence_length,\n                                     bool*          finished_buf,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     curandState_t* curandstate,\n                                     const int      batch_size,\n                                     const int      top_k,\n                                     const float    top_p,\n                                     const int      vocab_size_padded,\n                                     const int*     end_ids,\n                                     cudaStream_t   stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n * Copyright (c) 2021, NAVER Corp.  Authored by CLOVA.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdexcept>\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n\nnamespace fastertransformer {\n\n__global__ void curandInitialize(hiprandState_t* state, const int size, const unsigned long long random_seed)\n{\n    if (threadIdx.x + blockIdx.x * blockDim.x < size) {\n        hiprand_init(random_seed, 0, 0, &state[blockIdx.x * blockDim.x + threadIdx.x]);\n    }\n}\n\nvoid invokeCurandInitialize(hiprandState_t*           state,\n                            const size_t             batch_size,\n                            const unsigned long long random_seed,\n                            hipStream_t             stream)\n{\n    dim3 block(256);\n    dim3 grid((int)(ceil(batch_size * 1.0 / 256)));\n    curandInitialize<<<grid, block, 0, stream>>>(state, batch_size, random_seed);\n}\n\n__global__ void curandBatchInitialize(hiprandState_t* states, const int size, const unsigned long long* random_seeds)\n{\n    int idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (idx < size) {\n        hiprand_init(random_seeds[idx], 0, 0, &states[idx]);\n    }\n}\n\nvoid invokeCurandBatchInitialize(hiprandState_t*            states,\n                                 const size_t              batch_size,\n                                 const unsigned long long* random_seeds,\n                                 hipStream_t              stream)\n{\n    dim3 block(256);\n    dim3 grid((int)(ceil(batch_size * 1.0 / 256)));\n    curandBatchInitialize<<<grid, block, 0, stream>>>(states, batch_size, random_seeds);\n}\n\ntemplate<typename T>\n__global__ void addBiasEndMask(T*          logits,\n                               const T*    bias,\n                               const int*  end_ids,\n                               const bool* finished,\n                               const int   vocab_size,\n                               const int   vocab_size_padded)\n{\n    int  bid    = blockIdx.x;\n    bool finish = finished != nullptr ? finished[bid] : false;\n    int  offset = bid * vocab_size_padded;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    for (int tid = threadIdx.x; tid < vocab_size_padded; tid += blockDim.x) {\n        if (tid >= vocab_size) {\n            logits[offset + tid] = -MAX_T_VAL;\n        }\n        else if (finish) {\n            logits[offset + tid] = (tid == end_ids[bid]) ? MAX_T_VAL : -MAX_T_VAL;\n        }\n        else {\n            if (bias != nullptr) {\n                logits[offset + tid] += bias[tid];\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasEndMask(T*           logits,\n                          const T*     bias,\n                          const int*   end_ids,\n                          const bool*  finished,\n                          const int    batch_size,\n                          const int    vocab_size,\n                          const int    vocab_size_padded,\n                          hipStream_t stream)\n{\n    dim3 grid(batch_size);\n    dim3 block(min(vocab_size_padded, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    addBiasEndMask<<<grid, block, 0, stream>>>(logits, bias, end_ids, finished, vocab_size, vocab_size_padded);\n}\n\ntemplate void invokeAddBiasEndMask(float*       logits,\n                                   const float* bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padded,\n                                   hipStream_t stream);\n\ntemplate void invokeAddBiasEndMask(half*        logits,\n                                   const half*  bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    batch_size,\n                                   const int    vocab_size,\n                                   const int    vocab_size_padded,\n                                   hipStream_t stream);\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage1(const T* __restrict log_probs,\n                            T*          tmp_log_probs,\n                            int*        topk_tmp_id_buf,\n                            T*          topk_tmp_val_buf,\n                            const bool* finished,\n                            const int   max_top_k,\n                            const int*  top_ks,\n                            const int   vocab_size,\n                            const int*  end_ids,\n                            const bool* skip_decode)\n{\n    typedef hipcub::BlockReduce<TopK_2<T>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage     temp_storage;\n\n    const int tid = threadIdx.x;\n    const int bid = blockIdx.x;\n\n    const int batch_id = bid / BLOCKS_PER_BEAM_;  // row id for log_probs\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n    const int block_lane = bid % BLOCKS_PER_BEAM_;                              // block id for a beam\n    const int k          = (top_ks != nullptr) ? top_ks[batch_id] : max_top_k;  // batch_id = batch index\n\n    const int tmp_log_buf_index  = batch_id * vocab_size;\n    const int tmp_topk_buf_index = batch_id * BLOCKS_PER_BEAM_ * max_top_k + block_lane * k;\n\n    TopK_2<T>  partial;\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    if (finished != nullptr && finished[batch_id] == true) {\n        if (tid < k) {\n            const int index = tmp_topk_buf_index + tid;\n            if (block_lane == 0 && tid == 0) {\n                const int end_id        = end_ids[batch_id];\n                topk_tmp_id_buf[index]  = tmp_log_buf_index + end_id;\n                topk_tmp_val_buf[index] = log_probs[tmp_log_buf_index + end_id];\n            }\n            else {\n                topk_tmp_id_buf[index]  = -1;\n                topk_tmp_val_buf[index] = -MAX_T_VAL;\n            }\n        }\n        return;\n    }\n\n    for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n         elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n        int index            = elem_id + tmp_log_buf_index;\n        tmp_log_probs[index] = log_probs[index];\n    }\n\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int elem_id = tid + block_lane * BLOCK_SIZE_; elem_id < vocab_size;\n             elem_id += BLOCK_SIZE_ * BLOCKS_PER_BEAM_) {\n            int index = elem_id + tmp_log_buf_index;\n            partial.insert(tmp_log_probs[index], index);\n        }\n\n        TopK_2<T> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<T>);\n\n        if (tid == 0) {\n            const int index         = tmp_topk_buf_index + ite;\n            topk_tmp_id_buf[index]  = total.p;\n            topk_tmp_val_buf[index] = total.u;\n            tmp_log_probs[total.p]  = -MAX_T_VAL;\n        }\n        __syncthreads();\n    }\n}\n\ntemplate<typename T, int BLOCK_SIZE_, int BLOCKS_PER_BEAM_>\n__global__ void topk_stage2_sampling(const int* __restrict topk_tmp_id_buf,\n                                     T*             topk_tmp_val_buf,\n                                     int*           ids,\n                                     int*           sequence_length,\n                                     bool*          finished,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     const int      max_top_k,\n                                     const int*     top_ks,\n                                     const float    top_p,\n                                     const float*   top_ps,\n                                     hiprandState_t* curandstate,\n                                     const int*     end_ids,\n                                     const int      vocab_size,\n                                     const bool*    skip_decode)\n{\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n    const int tid      = threadIdx.x;\n    const int batch_id = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n\n    const int   k              = (top_ks != nullptr) ? top_ks[batch_id] : max_top_k;\n    const float prob_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n    const int   size           = k * BLOCKS_PER_BEAM_;\n    const int   stride         = max_top_k * BLOCKS_PER_BEAM_;\n\n    typedef hipcub::BlockReduce<TopK_2<float>, BLOCK_SIZE_> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage         temp_storage;\n    extern __shared__ char                               array[];\n    __shared__ float                                     rand_num;\n    __shared__ float                                     s_sum;\n    __shared__ float                                     s_max;\n    T*                                                   s_val = topk_tmp_val_buf + batch_id * stride;\n    int*                                                 s_id  = reinterpret_cast<int*>(array);\n    if (tid == 0) {\n        s_sum = 0.0f;\n    }\n    TopK_2<float> partial;\n\n    if (finished != nullptr && finished[batch_id] == true) {\n        ids[batch_id] = end_ids[batch_id];\n        return;\n    }\n\n    float* s_val2 = reinterpret_cast<float*>(s_id + k);\n    for (int ite = 0; ite < k; ite++) {\n        partial.init();\n#pragma unroll\n        for (int i = tid; i < size; i += BLOCK_SIZE_) {\n            partial.insert((float)s_val[i], i);\n        }\n\n        TopK_2<float> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op_2<float>);\n\n        if (tid == 0) {\n            if (ite == 0) {\n                s_max = total.u;\n            }\n            s_id[ite]      = total.p;\n            s_val[total.p] = -MAX_T_VAL;\n\n            // when cum_log_probs are computed, topk_tmp_val_buf (logits_buf_) are already pre-processed by\n            // softmax_kernel\n            if (cum_log_probs == nullptr && output_log_probs == nullptr) {\n                total.u = __expf(total.u - s_max);\n            }\n            s_val2[ite] = total.u;\n            s_sum += total.u;\n        }\n        __syncthreads();\n    }\n\n    if (tid == 0) {\n        rand_num = (float)hiprand_uniform(curandstate + blockIdx.x) * prob_threshold * s_sum;\n        for (int i = 0; i < k; i++) {\n            float exp_logit = s_val2[i];\n            rand_num        = rand_num - exp_logit;\n            if (rand_num <= 0.0f || i == k - 1) {\n                ids[batch_id] = topk_tmp_id_buf[batch_id * stride + s_id[i]] % vocab_size;\n                if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                    float log_prob = logf(exp_logit);\n                    if (cum_log_probs != nullptr) {\n                        cum_log_probs[batch_id] += log_prob;\n                    }\n                    if (output_log_probs != nullptr) {\n                        // 'output_log_probs' is the probability induced by the top-k sampling.\n                        // We normalize the probability 'exp_logit' of the selected token by\n                        // the probability 's_sum' of a set of top-k tokens, meaning the log_prob\n                        // is the probability of the selected token, conditioned on the event that\n                        // it is selected, i.e.,\n                        //   log_prob = log P(i | i is in top-k) = log(exp_logit / s_sum).\n                        output_log_probs[batch_id] = log_prob - logf(s_sum);\n                    }\n                }\n                break;\n            }\n        }\n        if (sequence_length != nullptr && finished != nullptr) {\n            sequence_length[batch_id] = finished[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n            finished[batch_id]        = ids[batch_id] == end_ids[batch_id] ? true : false;\n        }\n    }\n}\n\n#define CASE_K(K_MIN, K_MAX, BLOCK_SIZE_1_, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_)                                           \\\n    case K_MIN ... K_MAX:                                                                                              \\\n        topk_stage1<T, BLOCK_SIZE_1_, BLOCKS_PER_BEAM_>                                                                \\\n            <<<batch_size * BLOCKS_PER_BEAM_, BLOCK_SIZE_1_, 0, stream>>>(log_probs,                                   \\\n                                                                          temp_log_probs,                              \\\n                                                                          topk_tmp_id_buf,                             \\\n                                                                          topk_tmp_val_buf,                            \\\n                                                                          finished,                                    \\\n                                                                          max_top_k,                                   \\\n                                                                          top_ks,                                      \\\n                                                                          vocab_size,                                  \\\n                                                                          end_ids,                                     \\\n                                                                          skip_decode);                                \\\n        topk_stage2_sampling<T, BLOCK_SIZE_2_, BLOCKS_PER_BEAM_>                                                       \\\n            <<<batch_size, BLOCK_SIZE_2_, K_MAX * sizeof(int) + K_MAX * sizeof(float), stream>>>(topk_tmp_id_buf,      \\\n                                                                                                 topk_tmp_val_buf,     \\\n                                                                                                 ids,                  \\\n                                                                                                 sequence_length,      \\\n                                                                                                 finished,             \\\n                                                                                                 cum_log_probs,        \\\n                                                                                                 output_log_probs,     \\\n                                                                                                 max_top_k,            \\\n                                                                                                 top_ks,               \\\n                                                                                                 top_p,                \\\n                                                                                                 top_ps,               \\\n                                                                                                 curandstate,          \\\n                                                                                                 end_ids,              \\\n                                                                                                 vocab_size,           \\\n                                                                                                 skip_decode);         \\\n        break;\n\ntemplate<typename T>\nvoid invokeBatchTopKSampling(void*          workspace,\n                             size_t&        workspace_size,\n                             const T*       log_probs,\n                             int*           ids,\n                             int*           sequence_length,\n                             bool*          finished,\n                             float*         cum_log_probs,\n                             float*         output_log_probs,\n                             hiprandState_t* curandstate,\n                             const int      max_top_k,\n                             const int*     top_ks,\n                             const float    top_p,\n                             const float*   top_ps,\n                             const int      vocab_size_padded,\n                             const int*     end_ids,\n                             hipStream_t   stream,\n                             const int      batch_size,\n                             const bool*    skip_decode)\n{\n    // Not allow an ambiguous inputs top_p and top_ps.\n    assert(top_p == 1.0f || top_ps == nullptr);\n    const int vocab_size              = vocab_size_padded;\n    const int max_block_per_beam      = 8;\n    int       temp_log_probs_buf_size = batch_size * vocab_size;                      // type float\n    int       topk_tmp_ids_buf_size   = batch_size * max_top_k * max_block_per_beam;  // type int\n    int       topk_tmp_val_buf_size   = batch_size * max_top_k * max_block_per_beam;  // type float\n\n    // prevent memory misaligned address\n    temp_log_probs_buf_size = (int)(ceil(temp_log_probs_buf_size / 4.)) * 4;\n    topk_tmp_ids_buf_size   = (int)(ceil(topk_tmp_ids_buf_size / 4.)) * 4;\n    topk_tmp_val_buf_size   = (int)(ceil(topk_tmp_val_buf_size / 4.)) * 4;\n\n    if (workspace == nullptr) {\n        workspace_size = sizeof(T) * temp_log_probs_buf_size + sizeof(int) * topk_tmp_ids_buf_size\n                         + sizeof(T) * topk_tmp_val_buf_size;\n        return;\n    }\n\n    T*   temp_log_probs   = (T*)workspace;\n    int* topk_tmp_id_buf  = (int*)(temp_log_probs + temp_log_probs_buf_size);\n    T*   topk_tmp_val_buf = (T*)(topk_tmp_id_buf + topk_tmp_ids_buf_size);\n\n    switch (max_top_k) {\n        CASE_K(1, 16, 128, 128, 8);\n        CASE_K(17, 32, 256, 128, 8);\n        CASE_K(33, 64, 256, 256, 8);\n        CASE_K(65, 1024, 256, 256, 8);\n        default:\n            throw std::domain_error(fmtstr(\"top-k kernel supports 1<=k<=1024 but got k=%d\", max_top_k));\n    }\n}\n\n#undef CASE_K\n\ntemplate void invokeBatchTopKSampling(void*          workspace,\n                                      size_t&        workspace_size,\n                                      const float*   log_probs,\n                                      int*           ids,\n                                      int*           sequence_length,\n                                      bool*          finished_buf,\n                                      float*         cum_log_probs,\n                                      float*         output_log_probs,\n                                      hiprandState_t* curandstate,\n                                      const int      max_top_k,\n                                      const int*     top_ks,\n                                      const float    top_p,\n                                      const float*   top_ps,\n                                      const int      vocab_size_padded,\n                                      const int*     end_ids,\n                                      hipStream_t   stream,\n                                      const int      batch_size,\n                                      const bool*    skip_decode);\n\ntemplate void invokeBatchTopKSampling(void*          workspace,\n                                      size_t&        workspace_size,\n                                      const half*    log_probs,\n                                      int*           ids,\n                                      int*           sequence_length,\n                                      bool*          finished_buf,\n                                      float*         cum_log_probs,\n                                      float*         output_log_probs,\n                                      hiprandState_t* curandstate,\n                                      const int      max_top_k,\n                                      const int*     top_ks,\n                                      const float    top_p,\n                                      const float*   top_ps,\n                                      const int      vocab_size_padded,\n                                      const int*     end_ids,\n                                      hipStream_t   stream,\n                                      const int      batch_size,\n                                      const bool*    skip_decode);\n\ntemplate<typename T>\nvoid invokeTopKSampling(void*          workspace,\n                        size_t&        workspace_size,\n                        const T*       log_probs,\n                        int*           ids,\n                        int*           sequence_length,\n                        bool*          finished_buf,\n                        float*         cum_log_probs,\n                        float*         output_log_probs,\n                        hiprandState_t* curandstate,\n                        const int      top_k,\n                        const float    top_p,\n                        const int      vocab_size_padded,\n                        const int*     end_ids,\n                        hipStream_t   stream,\n                        const int      batch_size,\n                        const bool*    skip_decode)\n{\n    invokeBatchTopKSampling(workspace,\n                            workspace_size,\n                            log_probs,\n                            ids,\n                            sequence_length,\n                            finished_buf,\n                            cum_log_probs,\n                            output_log_probs,\n                            curandstate,\n                            top_k,\n                            nullptr,\n                            top_p,\n                            nullptr,\n                            vocab_size_padded,\n                            end_ids,\n                            stream,\n                            batch_size,\n                            skip_decode);\n}\n\ntemplate void invokeTopKSampling(void*          workspace,\n                                 size_t&        workspace_size,\n                                 const float*   log_probs,\n                                 int*           ids,\n                                 int*           sequence_length,\n                                 bool*          finished_buf,\n                                 float*         cum_log_probs,\n                                 float*         output_log_probs,\n                                 hiprandState_t* curandstate,\n                                 const int      top_k,\n                                 const float    top_p,\n                                 const int      vocab_size_padded,\n                                 const int*     end_ids,\n                                 hipStream_t   stream,\n                                 const int      batch_size,\n                                 const bool*    skip_decode);\n\ntemplate void invokeTopKSampling(void*          workspace,\n                                 size_t&        workspace_size,\n                                 const half*    log_probs,\n                                 int*           ids,\n                                 int*           sequence_length,\n                                 bool*          finished_buf,\n                                 float*         cum_log_probs,\n                                 float*         output_log_probs,\n                                 hiprandState_t* curandstate,\n                                 const int      top_k,\n                                 const float    top_p,\n                                 const int      vocab_size_padded,\n                                 const int*     end_ids,\n                                 hipStream_t   stream,\n                                 const int      batch_size,\n                                 const bool*    skip_decode);\n\ntemplate<typename T>\nvoid invokeTopKTopPSampling(void*          workspace,\n                            size_t&        workspace_size,\n                            int*           output_ids,\n                            const T*       logits,\n                            int*           sequence_length,\n                            bool*          finished_buf,\n                            float*         cum_log_probs,\n                            float*         output_log_probs,\n                            hiprandState_t* curandstate,\n                            const int      batch_size,\n                            const int      top_k,\n                            const float    top_p,\n                            const int      vocab_size_padded,\n                            const int*     end_ids,\n                            hipStream_t   stream)\n{\n    // invokeTopKTopPSampling will be deprecated. Please use invokeTopKSampling instead.\n    invokeTopKSampling(workspace,\n                       workspace_size,\n                       logits,\n                       output_ids,\n                       sequence_length,\n                       finished_buf,\n                       cum_log_probs,\n                       output_log_probs,\n                       curandstate,\n                       top_k,\n                       top_p,\n                       vocab_size_padded,\n                       end_ids,\n                       stream,\n                       batch_size,\n                       nullptr);\n}\n\ntemplate void invokeTopKTopPSampling(void*          workspace,\n                                     size_t&        workspace_size,\n                                     int*           output_ids,\n                                     const float*   logits,\n                                     int*           sequence_length,\n                                     bool*          finished_buf,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     hiprandState_t* curandstate,\n                                     const int      batch_size,\n                                     const int      top_k,\n                                     const float    top_p,\n                                     const int      vocab_size_padded,\n                                     const int*     end_ids,\n                                     hipStream_t   stream);\n\ntemplate void invokeTopKTopPSampling(void*          workspace,\n                                     size_t&        workspace_size,\n                                     int*           output_ids,\n                                     const half*    logits,\n                                     int*           sequence_length,\n                                     bool*          finished_buf,\n                                     float*         cum_log_probs,\n                                     float*         output_log_probs,\n                                     hiprandState_t* curandstate,\n                                     const int      batch_size,\n                                     const int      top_k,\n                                     const float    top_p,\n                                     const int      vocab_size_padded,\n                                     const int*     end_ids,\n                                     hipStream_t   stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/sampling_topk_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"stdio.h\"\n#include <cassert>\n#include <cmath>\n#include <cuda_fp16.h>\n\n#include \"matrix_vector_multiplication.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_bf16_wrapper.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntypedef struct half4 {\n    half x, y, z, w;\n} half4;\n\n#ifdef ENABLE_BF16\ntypedef struct bf164 {\n    __nv_bfloat16 x, y, z, w;\n} bf164;\n#endif\n\ntemplate<int NUM, typename T>\nstruct ARRAY {\n    T data[NUM];\n};\n\nextern __shared__ float cgBlockReduceSumElements_shm[];\n\n// T = float4\n// weight is int8 [n, k] row-major\n// input is [k]\n// scale_list is [n] for per_channel quantization.\n// output is [n]\n// each thread deals with at least 4 cols (k)\n// each block deals with nPerThread rows (n)\n// assume n % nPerThread == 0 && k % 4 == 0\n// grid(n/nPerThread)\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const float4* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array       = struct ARRAY<nPerThread, float>;\n    const array scale = *((const array*)scale_list + bidx);\n    array       sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        float4 input_val[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            input_val[m_i] = input[k_idx + m_i * k_4];\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val = weight[b_offset + i * k_4 + k_idx];\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                sum_list[m_i].data[i] += ((static_cast<float>(weight_val.x) * input_val[m_i].x)\n                                          + (static_cast<float>(weight_val.y) * input_val[m_i].y)\n                                          + (static_cast<float>(weight_val.z) * input_val[m_i].z)\n                                          + (static_cast<float>(weight_val.w) * input_val[m_i].w))\n                                         * scale.data[i];\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        for (int m_i = 0; m_i < m; m_i++) {\n            *((array*)output + bidx + m_i * gridDim.x) = sum_list[m_i];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////\n// FP16 & FP32 accumulators\n// for T = half4\n// weight is int8 [n, k] row-major\n// input is [m, k]\n// scale_list is [n] for per_channel quantization.\n// output is [m, n]\n// each thread deals with at least m * 4 cols (k)\n// each block deals with nPerThread m * rows (n)\n// assume n % nPerThread == 0 && k % 4 == 0\n// grid(n/nPerThread)\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const half4* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array = struct ARRAY<nPerThread, float>;\n    array sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        half2 input_val_0[m];\n        half2 input_val_1[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            const half4 input_val = input[k_idx + m_i * k_4];\n            input_val_0[m_i]      = {input_val.x, input_val.y};\n            input_val_1[m_i]      = {input_val.z, input_val.w};\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val   = weight[b_offset + i * k_4 + k_idx];\n            const half2 weight_val_0 = {static_cast<half>(weight_val.x), static_cast<half>(weight_val.y)};\n            const half2 weight_val_1 = {static_cast<half>(weight_val.z), static_cast<half>(weight_val.w)};\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                const half2 weight_val_2 =\n                    __hadd2(__hmul2(input_val_0[m_i], weight_val_0), __hmul2(input_val_1[m_i], weight_val_1));\n                sum_list[m_i].data[i] += static_cast<float>(weight_val_2.x + weight_val_2.y);\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        using array_half  = struct ARRAY<nPerThread, half>;\n        const array scale = *((const array*)scale_list + bidx);\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            array_half sum_list_half;\n#pragma unroll\n            for (int i = 0; i < nPerThread; i++) {\n                sum_list_half.data[i] = __float2half_rn(sum_list[m_i].data[i] * scale.data[i]);\n            }\n            *((array_half*)output + bidx + m_i * gridDim.x) = sum_list_half;\n        }\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const bf164* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array = struct ARRAY<nPerThread, float>;\n    array sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        __nv_bfloat162 input_val_0[m];\n        __nv_bfloat162 input_val_1[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            const bf164 input_val = input[k_idx + m_i * k_4];\n            input_val_0[m_i]      = {input_val.x, input_val.y};\n            input_val_1[m_i]      = {input_val.z, input_val.w};\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val = weight[b_offset + i * k_4 + k_idx];\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800\n            const __nv_bfloat162 weight_val_0 = {static_cast<__nv_bfloat16>(weight_val.x),\n                                                 static_cast<__nv_bfloat16>(weight_val.y)};\n            const __nv_bfloat162 weight_val_1 = {static_cast<__nv_bfloat16>(weight_val.z),\n                                                 static_cast<__nv_bfloat16>(weight_val.w)};\n#else\n            const __nv_bfloat162 weight_val_0 = {__float2bfloat16(static_cast<float>(weight_val.x)),\n                                                 __float2bfloat16(static_cast<float>(weight_val.y))};\n            const __nv_bfloat162 weight_val_1 = {__float2bfloat16(static_cast<float>(weight_val.z)),\n                                                 __float2bfloat16(static_cast<float>(weight_val.w))};\n#endif\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                const __nv_bfloat162 weight_val_2 =\n                    bf16hadd2(bf16hmul2(input_val_0[m_i], weight_val_0), bf16hmul2(input_val_1[m_i], weight_val_1));\n                sum_list[m_i].data[i] += static_cast<float>(weight_val_2.x + weight_val_2.y);\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        using array_half  = struct ARRAY<nPerThread, __nv_bfloat16>;\n        const array scale = *((const array*)scale_list + bidx);\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            array_half sum_list_half;\n#pragma unroll\n            for (int i = 0; i < nPerThread; i++) {\n                sum_list_half.data[i] = __float2bfloat16_rn(sum_list[m_i].data[i] * scale.data[i]);\n            }\n            *((array_half*)output + bidx + m_i * gridDim.x) = sum_list_half;\n        }\n    }\n}\n#endif\n///////////////////////////////////////////////////////////////////////\n\n#define RUN(M, TYPE)                                                                                                   \\\n    int8WeightPerChannelLdkMultiplication<M, nPerThread><<<grid, block, shm_size, stream>>>(                           \\\n        (const char4*)weight, (const TYPE*)input, scale_list, (void*)output, k / 4);\n\ntemplate<typename T>\nvoid int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* weight,\n                                                   const T*      input,\n                                                   const float*  scale_list,\n                                                   T*            output,\n                                                   const int     m,\n                                                   const int     n,\n                                                   const int     k,\n                                                   cudaStream_t  stream)\n{\n    const int nPerThread = 2;\n    if ((n % nPerThread != 0) || (k % 4 != 0)) {\n        printf(\"[ERROR][int8WeightPerChannelLdkMultiplicationLauncher] (%d %% %d != 0) || (%d %% 4 != 0).\\n\",\n               n,\n               nPerThread,\n               k);\n        exit(-1);\n    }\n\n    dim3 grid(n / nPerThread);\n    dim3 block;\n    // block size tuned with gpt-3 parameter\n    if (k > 10000) {\n        block.x = 256;\n    }\n    else if (k > 2000) {\n        block.x = 128;\n    }\n    else {\n        block.x = 64;\n    }\n    while (block.x * 4 > (size_t)k) {\n        block.x /= 2;\n    }\n    block.x               = (block.x + 31) / 32 * 32;\n    const size_t shm_size = block.x * nPerThread * sizeof(float);\n    if (m == 1) {\n        if (std::is_same<T, half>::value) {\n            RUN(1, half4)\n        }\n#ifdef ENABLE_BF16\n        else if (std::is_same<T, __nv_bfloat16>::value) {\n            RUN(1, bf164);\n        }\n#endif\n        else {\n            RUN(1, float4)\n        }\n    }\n    else if (m == 2) {\n        if (std::is_same<T, half>::value) {\n            RUN(2, half4)\n        }\n#ifdef ENABLE_BF16\n        else if (std::is_same<T, __nv_bfloat16>::value) {\n            RUN(2, bf164);\n        }\n#endif\n        else {\n            RUN(2, float4)\n        }\n    }\n    else {\n        printf(\"[ERROR][int8WeightPerChannelLdkMultiplicationLauncher] not support m == %d.\\n\", m);\n        exit(-1);\n    }\n}\n\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* matrix,\n                                                            const float*  vector,\n                                                            const float*  scale_list,\n                                                            float*        output,\n                                                            const int     m,\n                                                            const int     n,\n                                                            const int     k,\n                                                            cudaStream_t  stream);\n\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* matrix,\n                                                            const half*   vector,\n                                                            const float*  scale_list,\n                                                            half*         output,\n                                                            const int     m,\n                                                            const int     n,\n                                                            const int     k,\n                                                            cudaStream_t  stream);\n\n#ifdef ENABLE_BF16\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t*        matrix,\n                                                            const __nv_bfloat16* vector,\n                                                            const float*         scale_list,\n                                                            __nv_bfloat16*       output,\n                                                            const int            m,\n                                                            const int            n,\n                                                            const int            k,\n                                                            cudaStream_t         stream);\n#endif\n/////////////////////////////////////////////////////////////////////\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"stdio.h\"\n#include <cassert>\n#include <cmath>\n#include <hip/hip_fp16.h>\n\n#include \"matrix_vector_multiplication.h\"\n#include \"reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/utils/cuda_bf16_wrapper.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\ntypedef struct half4 {\n    half x, y, z, w;\n} half4;\n\n#ifdef ENABLE_BF16\ntypedef struct bf164 {\n    __hip_bfloat16 x, y, z, w;\n} bf164;\n#endif\n\ntemplate<int NUM, typename T>\nstruct ARRAY {\n    T data[NUM];\n};\n\nextern __shared__ float cgBlockReduceSumElements_shm[];\n\n// T = float4\n// weight is int8 [n, k] row-major\n// input is [k]\n// scale_list is [n] for per_channel quantization.\n// output is [n]\n// each thread deals with at least 4 cols (k)\n// each block deals with nPerThread rows (n)\n// assume n % nPerThread == 0 && k % 4 == 0\n// grid(n/nPerThread)\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const float4* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array       = struct ARRAY<nPerThread, float>;\n    const array scale = *((const array*)scale_list + bidx);\n    array       sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        float4 input_val[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            input_val[m_i] = input[k_idx + m_i * k_4];\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val = weight[b_offset + i * k_4 + k_idx];\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                sum_list[m_i].data[i] += ((static_cast<float>(weight_val.x) * input_val[m_i].x)\n                                          + (static_cast<float>(weight_val.y) * input_val[m_i].y)\n                                          + (static_cast<float>(weight_val.z) * input_val[m_i].z)\n                                          + (static_cast<float>(weight_val.w) * input_val[m_i].w))\n                                         * scale.data[i];\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        for (int m_i = 0; m_i < m; m_i++) {\n            *((array*)output + bidx + m_i * gridDim.x) = sum_list[m_i];\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////\n// FP16 & FP32 accumulators\n// for T = half4\n// weight is int8 [n, k] row-major\n// input is [m, k]\n// scale_list is [n] for per_channel quantization.\n// output is [m, n]\n// each thread deals with at least m * 4 cols (k)\n// each block deals with nPerThread m * rows (n)\n// assume n % nPerThread == 0 && k % 4 == 0\n// grid(n/nPerThread)\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const half4* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array = struct ARRAY<nPerThread, float>;\n    array sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        half2 input_val_0[m];\n        half2 input_val_1[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            const half4 input_val = input[k_idx + m_i * k_4];\n            input_val_0[m_i]      = {input_val.x, input_val.y};\n            input_val_1[m_i]      = {input_val.z, input_val.w};\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val   = weight[b_offset + i * k_4 + k_idx];\n            const half2 weight_val_0 = {static_cast<half>(weight_val.x), static_cast<half>(weight_val.y)};\n            const half2 weight_val_1 = {static_cast<half>(weight_val.z), static_cast<half>(weight_val.w)};\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                const half2 weight_val_2 =\n                    __hadd2(__hmul2(input_val_0[m_i], weight_val_0), __hmul2(input_val_1[m_i], weight_val_1));\n                sum_list[m_i].data[i] += static_cast<float>(weight_val_2.x + weight_val_2.y);\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        using array_half  = struct ARRAY<nPerThread, half>;\n        const array scale = *((const array*)scale_list + bidx);\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            array_half sum_list_half;\n#pragma unroll\n            for (int i = 0; i < nPerThread; i++) {\n                sum_list_half.data[i] = __float2half_rn(sum_list[m_i].data[i] * scale.data[i]);\n            }\n            *((array_half*)output + bidx + m_i * gridDim.x) = sum_list_half;\n        }\n    }\n}\n\n#ifdef ENABLE_BF16\ntemplate<int m, int nPerThread>\n__global__ void int8WeightPerChannelLdkMultiplication(\n    const char4* weight, const bf164* input, const float* scale_list, void* output, const int k_4)\n{\n\n    const int    tidx     = threadIdx.x;\n    const int    bidx     = blockIdx.x;\n    const int    row_idx  = bidx * nPerThread;\n    const size_t b_offset = row_idx * k_4;\n\n    using array = struct ARRAY<nPerThread, float>;\n    array sum_list[m];\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            sum_list[m_i].data[i] = 0.0f;\n        }\n    }\n\n    for (int k_idx = tidx; k_idx < k_4; k_idx += blockDim.x) {\n        __hip_bfloat162 input_val_0[m];\n        __hip_bfloat162 input_val_1[m];\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            const bf164 input_val = input[k_idx + m_i * k_4];\n            input_val_0[m_i]      = {input_val.x, input_val.y};\n            input_val_1[m_i]      = {input_val.z, input_val.w};\n        }\n#pragma unroll\n        for (int i = 0; i < nPerThread; i++) {\n            const char4 weight_val = weight[b_offset + i * k_4 + k_idx];\n#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 800\n            const __hip_bfloat162 weight_val_0 = {static_cast<__hip_bfloat16>(weight_val.x),\n                                                 static_cast<__hip_bfloat16>(weight_val.y)};\n            const __hip_bfloat162 weight_val_1 = {static_cast<__hip_bfloat16>(weight_val.z),\n                                                 static_cast<__hip_bfloat16>(weight_val.w)};\n#else\n            const __hip_bfloat162 weight_val_0 = {__float2bfloat16(static_cast<float>(weight_val.x)),\n                                                 __float2bfloat16(static_cast<float>(weight_val.y))};\n            const __hip_bfloat162 weight_val_1 = {__float2bfloat16(static_cast<float>(weight_val.z)),\n                                                 __float2bfloat16(static_cast<float>(weight_val.w))};\n#endif\n#pragma unroll\n            for (int m_i = 0; m_i < m; m_i++) {\n                const __hip_bfloat162 weight_val_2 =\n                    bf16hadd2(bf16hmul2(input_val_0[m_i], weight_val_0), bf16hmul2(input_val_1[m_i], weight_val_1));\n                sum_list[m_i].data[i] += static_cast<float>(weight_val_2.x + weight_val_2.y);\n            }\n        }\n    }\n#pragma unroll\n    for (int m_i = 0; m_i < m; m_i++) {\n        cgBlockReduceSumElements<nPerThread>(sum_list[m_i].data, cgBlockReduceSumElements_shm);\n        __syncthreads();\n    }\n    if (tidx == 0) {\n        using array_half  = struct ARRAY<nPerThread, __hip_bfloat16>;\n        const array scale = *((const array*)scale_list + bidx);\n#pragma unroll\n        for (int m_i = 0; m_i < m; m_i++) {\n            array_half sum_list_half;\n#pragma unroll\n            for (int i = 0; i < nPerThread; i++) {\n                sum_list_half.data[i] = __float2bfloat16_rn(sum_list[m_i].data[i] * scale.data[i]);\n            }\n            *((array_half*)output + bidx + m_i * gridDim.x) = sum_list_half;\n        }\n    }\n}\n#endif\n///////////////////////////////////////////////////////////////////////\n\n#define RUN(M, TYPE)                                                                                                   \\\n    int8WeightPerChannelLdkMultiplication<M, nPerThread><<<grid, block, shm_size, stream>>>(                           \\\n        (const char4*)weight, (const TYPE*)input, scale_list, (void*)output, k / 4);\n\ntemplate<typename T>\nvoid int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* weight,\n                                                   const T*      input,\n                                                   const float*  scale_list,\n                                                   T*            output,\n                                                   const int     m,\n                                                   const int     n,\n                                                   const int     k,\n                                                   hipStream_t  stream)\n{\n    const int nPerThread = 2;\n    if ((n % nPerThread != 0) || (k % 4 != 0)) {\n        printf(\"[ERROR][int8WeightPerChannelLdkMultiplicationLauncher] (%d %% %d != 0) || (%d %% 4 != 0).\\n\",\n               n,\n               nPerThread,\n               k);\n        exit(-1);\n    }\n\n    dim3 grid(n / nPerThread);\n    dim3 block;\n    // block size tuned with gpt-3 parameter\n    if (k > 10000) {\n        block.x = 256;\n    }\n    else if (k > 2000) {\n        block.x = 128;\n    }\n    else {\n        block.x = 64;\n    }\n    while (block.x * 4 > (size_t)k) {\n        block.x /= 2;\n    }\n    block.x               = (block.x + 31) / 32 * 32;\n    const size_t shm_size = block.x * nPerThread * sizeof(float);\n    if (m == 1) {\n        if (std::is_same<T, half>::value) {\n            RUN(1, half4)\n        }\n#ifdef ENABLE_BF16\n        else if (std::is_same<T, __hip_bfloat16>::value) {\n            RUN(1, bf164);\n        }\n#endif\n        else {\n            RUN(1, float4)\n        }\n    }\n    else if (m == 2) {\n        if (std::is_same<T, half>::value) {\n            RUN(2, half4)\n        }\n#ifdef ENABLE_BF16\n        else if (std::is_same<T, __hip_bfloat16>::value) {\n            RUN(2, bf164);\n        }\n#endif\n        else {\n            RUN(2, float4)\n        }\n    }\n    else {\n        printf(\"[ERROR][int8WeightPerChannelLdkMultiplicationLauncher] not support m == %d.\\n\", m);\n        exit(-1);\n    }\n}\n\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* matrix,\n                                                            const float*  vector,\n                                                            const float*  scale_list,\n                                                            float*        output,\n                                                            const int     m,\n                                                            const int     n,\n                                                            const int     k,\n                                                            hipStream_t  stream);\n\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t* matrix,\n                                                            const half*   vector,\n                                                            const float*  scale_list,\n                                                            half*         output,\n                                                            const int     m,\n                                                            const int     n,\n                                                            const int     k,\n                                                            hipStream_t  stream);\n\n#ifdef ENABLE_BF16\ntemplate void int8WeightPerChannelLdkMultiplicationLauncher(const int8_t*        matrix,\n                                                            const __hip_bfloat16* vector,\n                                                            const float*         scale_list,\n                                                            __hip_bfloat16*       output,\n                                                            const int            m,\n                                                            const int            n,\n                                                            const int            k,\n                                                            hipStream_t         stream);\n#endif\n/////////////////////////////////////////////////////////////////////\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/matrix_vector_multiplication.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"custom_ar_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ uint32_t hadd2(const uint32_t& a, const uint32_t& b)\n{\n    uint32_t c;\n    asm volatile(\"add.f16x2 %0, %1, %2;\\n\" : \"=r\"(c) : \"r\"(a), \"r\"(b));\n    return c;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ uint32_t fadd(const uint32_t& a, const uint32_t& b)\n{\n    uint32_t c;\n    asm volatile(\"add.f32 %0, %1, %2;\\n\" : \"=r\"(c) : \"r\"(a), \"r\"(b));\n    return c;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ void st_flag_release(uint32_t& flag, uint32_t* flag_addr)\n{\n#if __CUDA_ARCH__ >= 700\n    asm volatile(\"st.global.release.sys.b32 [%1], %0;\" ::\"r\"(flag), \"l\"(flag_addr));\n#else\n    __threadfence_system();\n    asm volatile(\"st.global.volatile.b32 [%1], %0;\" ::\"r\"(flag), \"l\"(flag_addr));\n#endif\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ void ld_flag_acquire(uint32_t& flag, uint32_t* flag_addr)\n{\n#if __CUDA_ARCH__ >= 700\n    asm volatile(\"ld.global.acquire.sys.b32 %0, [%1];\" : \"=r\"(flag) : \"l\"(flag_addr));\n#else\n    asm volatile(\"ld.global.volatile.b32 %0, [%1];\" : \"=r\"(flag) : \"l\"(flag_addr));\n#endif\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Type Converter that packs data format to 128 bits data type\ntemplate<typename T>\nstruct ARTypeConverter {\n    using Type = uint4;\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct ARTypeConverter<__nv_bfloat16> {\n    using Type = bf168;\n};\n#endif\n\n// add two 128b data\ntemplate<typename T_IN, typename T_COMP>\ninline __device__ T_IN add128b(T_IN a, T_IN b);\n\ntemplate<>\ninline __device__ uint4 add128b<uint4, uint16_t>(uint4 a, uint4 b)\n{\n    uint4 c;\n    c.x = hadd2(a.x, b.x);\n    c.y = hadd2(a.y, b.y);\n    c.z = hadd2(a.z, b.z);\n    c.w = hadd2(a.w, b.w);\n    return c;\n}\n\ntemplate<>\ninline __device__ uint4 add128b<uint4, uint32_t>(uint4 a, uint4 b)\n{\n    uint4 c;\n    c.x = fadd(a.x, b.x);\n    c.y = fadd(a.y, b.y);\n    c.z = fadd(a.z, b.z);\n    c.w = fadd(a.w, b.w);\n    return c;\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\ninline __device__ bf168 add128b<bf168, __nv_bfloat16>(bf168 a, bf168 b)\n{\n    bf168 c;\n    c.x = bf16hadd2(a.x, b.x);\n    c.y = bf16hadd2(a.y, b.y);\n    c.z = bf16hadd2(a.z, b.z);\n    c.w = bf16hadd2(a.w, b.w);\n    return c;\n}\n#endif\n\n// init 128bits data with 0\ntemplate<typename T>\ninline __device__ T init_packed_type();\n\ntemplate<>\ninline __device__ uint4 init_packed_type()\n{\n    return make_uint4(0u, 0u, 0u, 0u);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\ninline __device__ bf168 init_packed_type()\n{\n    bf168  val;\n    uint4& val_u = reinterpret_cast<uint4&>(val);\n    val_u        = make_uint4(0u, 0u, 0u, 0u);\n    return val;\n}\n#endif\n\ntemplate<typename T>\nstatic __global__ void oneShotAllReduceKernel(AllReduceParams<T> params)\n{\n    // The block index.\n    const int bidx = blockIdx.x;\n    // The thread index with the block.\n    const int tidx = threadIdx.x;\n\n    // The number of elements packed into one for comms\n    static constexpr int NUM_ELTS = std::is_same<T, uint32_t>::value ? 4 : 8;\n\n    // Packed data type for comms\n    using PackedType = typename ARTypeConverter<T>::Type;\n\n    // The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).\n    size_t offset = bidx * params.elts_per_block + tidx * NUM_ELTS;\n    // The end of the segment computed by that block.\n    size_t max_offset = std::min((bidx + 1) * params.elts_per_block, params.elts_per_rank);\n\n    // Synchronize the ranks.\n    volatile uint32_t* barrier_d = params.peer_barrier_ptrs[params.local_rank];\n    if (tidx < RANKS_PER_NODE) {\n        // The 1st block notifies the other ranks.\n        if (bidx == 0) {\n            params.peer_barrier_ptrs[tidx][params.local_rank] = params.barrier_flag;\n        }\n\n        // Busy-wait until all ranks are ready.\n        while (barrier_d[tidx] < params.barrier_flag) {}\n    }\n\n    // Make sure we can move on...\n    __syncthreads();\n\n    // The source pointers. Distributed round-robin for the different warps.\n    const T* src_d[RANKS_PER_NODE];\n#pragma unroll\n    for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n        int rank  = (params.local_rank + ii) % RANKS_PER_NODE;\n        src_d[ii] = params.peer_comm_buffer_ptrs[rank];\n    }\n\n    // Each block accumulates the values from the different GPUs on the same node.\n    for (size_t iter_offset = offset; iter_offset < max_offset; iter_offset += blockDim.x * NUM_ELTS) {\n        // Iterate over the different ranks/devices on the node to load the values.\n        PackedType vals[RANKS_PER_NODE];\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            vals[ii] = reinterpret_cast<const PackedType*>(&src_d[ii][iter_offset])[0];\n        }\n\n        // Sum the values from the different ranks.\n        PackedType sums = init_packed_type<PackedType>();\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            sums = add128b<PackedType, T>(sums, vals[ii]);\n        }\n\n        // Store to the destination buffer.\n        reinterpret_cast<PackedType*>(&params.local_output_buffer_ptr[iter_offset])[0] = sums;\n    }\n}\n\ntemplate<typename T>\nstatic __global__ void twoShotAllReduceKernel(AllReduceParams<T> params)\n{\n\n    // The block index.\n    const int bidx = blockIdx.x;\n    // The thread index with the block.\n    const int tidx = threadIdx.x;\n\n    // The number of elements packed into one for comms\n    static constexpr int NUM_ELTS = std::is_same<T, uint32_t>::value ? 4 : 8;\n\n    // Packed data type for comms\n    using PackedType = typename ARTypeConverter<T>::Type;\n\n    // The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).\n    size_t offset = bidx * params.elts_per_block + tidx * NUM_ELTS + params.rank_offset;\n    // The end of the segment computed by that block.\n    size_t max_offset = min(offset + params.elts_per_block, params.elts_total);\n\n    // Synchronize the ranks.\n    volatile uint32_t* barrier_d = params.peer_barrier_ptrs[params.local_rank];\n    if (tidx < RANKS_PER_NODE) {\n        // The 1st block notifies the other ranks.\n        if (bidx == 0) {\n            params.peer_barrier_ptrs[tidx][params.local_rank] = params.barrier_flag;\n        }\n\n        // Busy-wait until all ranks are ready.\n        while (barrier_d[tidx] < params.barrier_flag) {}\n    }\n\n    // Make sure we can move on...\n    __syncthreads();\n\n    // The source pointers. Distributed round-robin for the different warps.\n    T* src_d[RANKS_PER_NODE];\n    // The destination ranks for round-robin gathering\n    size_t dst_rank[RANKS_PER_NODE];\n#pragma unroll\n    for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n        int rank     = (params.local_rank + ii) % RANKS_PER_NODE;\n        src_d[ii]    = params.peer_comm_buffer_ptrs[rank];\n        dst_rank[ii] = rank;\n    }\n\n    // Each block accumulates the values from the different GPUs on the same node.\n    for (size_t local_offset = offset; local_offset < max_offset; local_offset += blockDim.x * NUM_ELTS) {\n\n        // Iterate over the different ranks/devices on the node to load the values.\n        PackedType vals[RANKS_PER_NODE];\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            vals[ii] = reinterpret_cast<const PackedType*>(&src_d[ii][local_offset])[0];\n        }\n\n        // Sum the values from the different ranks.\n        PackedType sums = init_packed_type<PackedType>();\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            sums = add128b<PackedType, T>(sums, vals[ii]);\n        }\n\n        // Store to the local buffer.\n        reinterpret_cast<PackedType*>(&src_d[0][local_offset])[0] = sums;\n    }\n\n    // sync threads to make sure all block threads have the sums\n    __syncthreads();\n\n    // barreris among the blocks with the same idx (release-acuqire semantics)\n    if (tidx < RANKS_PER_NODE) {\n        // The all blocks notifies the other ranks.\n        uint32_t flag_block_offset = RANKS_PER_NODE + bidx * RANKS_PER_NODE;\n        st_flag_release(params.barrier_flag, params.peer_barrier_ptrs[tidx] + flag_block_offset + params.local_rank);\n\n        // Busy-wait until all ranks are ready.\n        uint32_t  rank_barrier   = 0;\n        uint32_t* peer_barrier_d = params.peer_barrier_ptrs[params.local_rank] + flag_block_offset + tidx;\n        do {\n            ld_flag_acquire(rank_barrier, peer_barrier_d);\n        } while (rank_barrier != params.barrier_flag);\n    }\n\n    // sync threads to make sure all other ranks has the final partial results\n    __syncthreads();\n\n    // Gather all needed elts from other intra-node ranks\n    for (size_t local_offset = offset; local_offset < max_offset; local_offset += blockDim.x * NUM_ELTS) {\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            // use round-robin gathering from other ranks\n            int offset_rank = local_offset + (dst_rank[ii] - params.local_rank) * params.elts_per_rank;\n            reinterpret_cast<PackedType*>(&params.local_output_buffer_ptr[offset_rank])[0] =\n                reinterpret_cast<PackedType*>(&src_d[ii][offset_rank])[0];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid kernelLaunchConfig(\n    int& blocks_per_grid, int& threads_per_block, size_t elts, int kernel_algo, size_t data_type_bytes)\n{\n    assert(data_type_bytes == 2 || data_type_bytes == 4);\n    // NOTE: need to support FP16 and FP32\n    size_t elts_per_thread = 16 / data_type_bytes;\n    size_t elts_per_warp   = (16 * WARP_SIZE) / data_type_bytes;\n    switch (kernel_algo) {\n        case 0: {  // one stage all reduce algo\n            assert(elts % elts_per_warp == 0);\n            if (elts < (elts_per_thread * DEFAULT_BLOCK_SIZE)) {  // local reduce\n                threads_per_block = ((elts + elts_per_warp - 1) / elts_per_warp) * WARP_SIZE;\n                blocks_per_grid   = 1;\n            }\n            else {  // local reduce\n                if (elts % (elts_per_thread * threads_per_block) == 0) {\n                    blocks_per_grid =\n                        (elts + elts_per_thread * threads_per_block - 1) / (elts_per_thread * threads_per_block);\n                    // NOTE: need to adjust here\n                    if (blocks_per_grid > MAX_ALL_REDUCE_BLOCKS) {\n                        int iter_factor = 1;\n                        while (blocks_per_grid / iter_factor > MAX_ALL_REDUCE_BLOCKS || blocks_per_grid % iter_factor) {\n                            iter_factor += 1;\n                        }\n                        blocks_per_grid /= iter_factor;\n                    }\n                }\n                else {\n                    int total_threads = elts / elts_per_thread;\n                    blocks_per_grid   = 1;\n                    while (total_threads % blocks_per_grid != 0\n                           || total_threads / blocks_per_grid > DEFAULT_BLOCK_SIZE) {\n                        blocks_per_grid += 1;\n                    }\n                    threads_per_block = total_threads / blocks_per_grid;\n                }\n            }\n            break;\n        }\n        case 1: {  // two stage all reduce algo\n            int total_threads = elts / RANKS_PER_NODE / RANKS_PER_NODE;\n            assert(elts / RANKS_PER_NODE % RANKS_PER_NODE == 0 && total_threads % WARP_SIZE == 0);\n\n            while (total_threads % blocks_per_grid != 0 || total_threads / blocks_per_grid > DEFAULT_BLOCK_SIZE) {\n                blocks_per_grid += 1;\n            }\n\n            threads_per_block = total_threads / blocks_per_grid;\n\n            // NOTE: need to adjust here\n            if (blocks_per_grid > MAX_ALL_REDUCE_BLOCKS) {\n                int iter_factor = 1;\n                while (blocks_per_grid / iter_factor > MAX_ALL_REDUCE_BLOCKS || blocks_per_grid % iter_factor) {\n                    iter_factor += 1;\n                }\n                blocks_per_grid /= iter_factor;\n            }\n            break;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\nvoid invokeOneOrTwoShotAllReduceKernel(AllReduceParams<T>& param, cudaStream_t stream)\n{\n    size_t elts_total      = param.elts_total;\n    int    blocks_per_grid = 1, threads_per_block = DEFAULT_BLOCK_SIZE;\n    int    kernel_algo = 1;\n    if (elts_total * sizeof(T) <= DEFALUT_ALGO_AR_SIZE_THRESHOLD) {\n        kernel_algo = 0;\n    }\n\n    kernelLaunchConfig(blocks_per_grid, threads_per_block, elts_total, kernel_algo, sizeof(T));\n\n    if (kernel_algo == 0) {\n        param.elts_per_rank  = elts_total;\n        param.elts_per_block = param.elts_per_rank / blocks_per_grid;\n        oneShotAllReduceKernel<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);\n    }\n    else {\n        param.elts_per_rank  = param.elts_total / RANKS_PER_NODE;\n        param.elts_per_block = param.elts_per_rank / blocks_per_grid;\n        param.rank_offset    = param.rank * param.elts_per_rank;\n        twoShotAllReduceKernel<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);\n    }\n}\n\n// Template instantiation\ntemplate void invokeOneOrTwoShotAllReduceKernel<uint16_t>(AllReduceParams<uint16_t>& param, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeOneOrTwoShotAllReduceKernel<__nv_bfloat16>(AllReduceParams<__nv_bfloat16>& param,\n                                                               cudaStream_t                    stream);\n#endif\ntemplate void invokeOneOrTwoShotAllReduceKernel<uint32_t>(AllReduceParams<uint32_t>& param, cudaStream_t stream);\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"custom_ar_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n\nnamespace fastertransformer {\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ uint32_t hadd2(const uint32_t& a, const uint32_t& b)\n{\n    uint32_t c;\n    asm volatile(\"add.f16x2 %0, %1, %2;\\n\" : \"=r\"(c) : \"r\"(a), \"r\"(b));\n    return c;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ uint32_t fadd(const uint32_t& a, const uint32_t& b)\n{\n    uint32_t c;\n    asm volatile(\"add.f32 %0, %1, %2;\\n\" : \"=r\"(c) : \"r\"(a), \"r\"(b));\n    return c;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ void st_flag_release(uint32_t& flag, uint32_t* flag_addr)\n{\n#if __CUDA_ARCH__ >= 700\n    asm volatile(\"st.global.release.sys.b32 [%1], %0;\" ::\"r\"(flag), \"l\"(flag_addr));\n#else\n    __threadfence_system();\n    asm volatile(\"st.global.volatile.b32 [%1], %0;\" ::\"r\"(flag), \"l\"(flag_addr));\n#endif\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ void ld_flag_acquire(uint32_t& flag, uint32_t* flag_addr)\n{\n#if __CUDA_ARCH__ >= 700\n    asm volatile(\"ld.global.acquire.sys.b32 %0, [%1];\" : \"=r\"(flag) : \"l\"(flag_addr));\n#else\n    asm volatile(\"ld.global.volatile.b32 %0, [%1];\" : \"=r\"(flag) : \"l\"(flag_addr));\n#endif\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Type Converter that packs data format to 128 bits data type\ntemplate<typename T>\nstruct ARTypeConverter {\n    using Type = uint4;\n};\n\n#ifdef ENABLE_BF16\ntemplate<>\nstruct ARTypeConverter<__hip_bfloat16> {\n    using Type = bf168;\n};\n#endif\n\n// add two 128b data\ntemplate<typename T_IN, typename T_COMP>\ninline __device__ T_IN add128b(T_IN a, T_IN b);\n\ntemplate<>\ninline __device__ uint4 add128b<uint4, uint16_t>(uint4 a, uint4 b)\n{\n    uint4 c;\n    c.x = hadd2(a.x, b.x);\n    c.y = hadd2(a.y, b.y);\n    c.z = hadd2(a.z, b.z);\n    c.w = hadd2(a.w, b.w);\n    return c;\n}\n\ntemplate<>\ninline __device__ uint4 add128b<uint4, uint32_t>(uint4 a, uint4 b)\n{\n    uint4 c;\n    c.x = fadd(a.x, b.x);\n    c.y = fadd(a.y, b.y);\n    c.z = fadd(a.z, b.z);\n    c.w = fadd(a.w, b.w);\n    return c;\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\ninline __device__ bf168 add128b<bf168, __hip_bfloat16>(bf168 a, bf168 b)\n{\n    bf168 c;\n    c.x = bf16hadd2(a.x, b.x);\n    c.y = bf16hadd2(a.y, b.y);\n    c.z = bf16hadd2(a.z, b.z);\n    c.w = bf16hadd2(a.w, b.w);\n    return c;\n}\n#endif\n\n// init 128bits data with 0\ntemplate<typename T>\ninline __device__ T init_packed_type();\n\ntemplate<>\ninline __device__ uint4 init_packed_type()\n{\n    return make_uint4(0u, 0u, 0u, 0u);\n}\n\n#ifdef ENABLE_BF16\ntemplate<>\ninline __device__ bf168 init_packed_type()\n{\n    bf168  val;\n    uint4& val_u = reinterpret_cast<uint4&>(val);\n    val_u        = make_uint4(0u, 0u, 0u, 0u);\n    return val;\n}\n#endif\n\ntemplate<typename T>\nstatic __global__ void oneShotAllReduceKernel(AllReduceParams<T> params)\n{\n    // The block index.\n    const int bidx = blockIdx.x;\n    // The thread index with the block.\n    const int tidx = threadIdx.x;\n\n    // The number of elements packed into one for comms\n    static constexpr int NUM_ELTS = std::is_same<T, uint32_t>::value ? 4 : 8;\n\n    // Packed data type for comms\n    using PackedType = typename ARTypeConverter<T>::Type;\n\n    // The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).\n    size_t offset = bidx * params.elts_per_block + tidx * NUM_ELTS;\n    // The end of the segment computed by that block.\n    size_t max_offset = std::min((bidx + 1) * params.elts_per_block, params.elts_per_rank);\n\n    // Synchronize the ranks.\n    volatile uint32_t* barrier_d = params.peer_barrier_ptrs[params.local_rank];\n    if (tidx < RANKS_PER_NODE) {\n        // The 1st block notifies the other ranks.\n        if (bidx == 0) {\n            params.peer_barrier_ptrs[tidx][params.local_rank] = params.barrier_flag;\n        }\n\n        // Busy-wait until all ranks are ready.\n        while (barrier_d[tidx] < params.barrier_flag) {}\n    }\n\n    // Make sure we can move on...\n    __syncthreads();\n\n    // The source pointers. Distributed round-robin for the different warps.\n    const T* src_d[RANKS_PER_NODE];\n#pragma unroll\n    for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n        int rank  = (params.local_rank + ii) % RANKS_PER_NODE;\n        src_d[ii] = params.peer_comm_buffer_ptrs[rank];\n    }\n\n    // Each block accumulates the values from the different GPUs on the same node.\n    for (size_t iter_offset = offset; iter_offset < max_offset; iter_offset += blockDim.x * NUM_ELTS) {\n        // Iterate over the different ranks/devices on the node to load the values.\n        PackedType vals[RANKS_PER_NODE];\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            vals[ii] = reinterpret_cast<const PackedType*>(&src_d[ii][iter_offset])[0];\n        }\n\n        // Sum the values from the different ranks.\n        PackedType sums = init_packed_type<PackedType>();\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            sums = add128b<PackedType, T>(sums, vals[ii]);\n        }\n\n        // Store to the destination buffer.\n        reinterpret_cast<PackedType*>(&params.local_output_buffer_ptr[iter_offset])[0] = sums;\n    }\n}\n\ntemplate<typename T>\nstatic __global__ void twoShotAllReduceKernel(AllReduceParams<T> params)\n{\n\n    // The block index.\n    const int bidx = blockIdx.x;\n    // The thread index with the block.\n    const int tidx = threadIdx.x;\n\n    // The number of elements packed into one for comms\n    static constexpr int NUM_ELTS = std::is_same<T, uint32_t>::value ? 4 : 8;\n\n    // Packed data type for comms\n    using PackedType = typename ARTypeConverter<T>::Type;\n\n    // The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).\n    size_t offset = bidx * params.elts_per_block + tidx * NUM_ELTS + params.rank_offset;\n    // The end of the segment computed by that block.\n    size_t max_offset = min(offset + params.elts_per_block, params.elts_total);\n\n    // Synchronize the ranks.\n    volatile uint32_t* barrier_d = params.peer_barrier_ptrs[params.local_rank];\n    if (tidx < RANKS_PER_NODE) {\n        // The 1st block notifies the other ranks.\n        if (bidx == 0) {\n            params.peer_barrier_ptrs[tidx][params.local_rank] = params.barrier_flag;\n        }\n\n        // Busy-wait until all ranks are ready.\n        while (barrier_d[tidx] < params.barrier_flag) {}\n    }\n\n    // Make sure we can move on...\n    __syncthreads();\n\n    // The source pointers. Distributed round-robin for the different warps.\n    T* src_d[RANKS_PER_NODE];\n    // The destination ranks for round-robin gathering\n    size_t dst_rank[RANKS_PER_NODE];\n#pragma unroll\n    for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n        int rank     = (params.local_rank + ii) % RANKS_PER_NODE;\n        src_d[ii]    = params.peer_comm_buffer_ptrs[rank];\n        dst_rank[ii] = rank;\n    }\n\n    // Each block accumulates the values from the different GPUs on the same node.\n    for (size_t local_offset = offset; local_offset < max_offset; local_offset += blockDim.x * NUM_ELTS) {\n\n        // Iterate over the different ranks/devices on the node to load the values.\n        PackedType vals[RANKS_PER_NODE];\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            vals[ii] = reinterpret_cast<const PackedType*>(&src_d[ii][local_offset])[0];\n        }\n\n        // Sum the values from the different ranks.\n        PackedType sums = init_packed_type<PackedType>();\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            sums = add128b<PackedType, T>(sums, vals[ii]);\n        }\n\n        // Store to the local buffer.\n        reinterpret_cast<PackedType*>(&src_d[0][local_offset])[0] = sums;\n    }\n\n    // sync threads to make sure all block threads have the sums\n    __syncthreads();\n\n    // barreris among the blocks with the same idx (release-acuqire semantics)\n    if (tidx < RANKS_PER_NODE) {\n        // The all blocks notifies the other ranks.\n        uint32_t flag_block_offset = RANKS_PER_NODE + bidx * RANKS_PER_NODE;\n        st_flag_release(params.barrier_flag, params.peer_barrier_ptrs[tidx] + flag_block_offset + params.local_rank);\n\n        // Busy-wait until all ranks are ready.\n        uint32_t  rank_barrier   = 0;\n        uint32_t* peer_barrier_d = params.peer_barrier_ptrs[params.local_rank] + flag_block_offset + tidx;\n        do {\n            ld_flag_acquire(rank_barrier, peer_barrier_d);\n        } while (rank_barrier != params.barrier_flag);\n    }\n\n    // sync threads to make sure all other ranks has the final partial results\n    __syncthreads();\n\n    // Gather all needed elts from other intra-node ranks\n    for (size_t local_offset = offset; local_offset < max_offset; local_offset += blockDim.x * NUM_ELTS) {\n#pragma unroll\n        for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {\n            // use round-robin gathering from other ranks\n            int offset_rank = local_offset + (dst_rank[ii] - params.local_rank) * params.elts_per_rank;\n            reinterpret_cast<PackedType*>(&params.local_output_buffer_ptr[offset_rank])[0] =\n                reinterpret_cast<PackedType*>(&src_d[ii][offset_rank])[0];\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid kernelLaunchConfig(\n    int& blocks_per_grid, int& threads_per_block, size_t elts, int kernel_algo, size_t data_type_bytes)\n{\n    assert(data_type_bytes == 2 || data_type_bytes == 4);\n    // NOTE: need to support FP16 and FP32\n    size_t elts_per_thread = 16 / data_type_bytes;\n    size_t elts_per_warp   = (16 * WARP_SIZE) / data_type_bytes;\n    switch (kernel_algo) {\n        case 0: {  // one stage all reduce algo\n            assert(elts % elts_per_warp == 0);\n            if (elts < (elts_per_thread * DEFAULT_BLOCK_SIZE)) {  // local reduce\n                threads_per_block = ((elts + elts_per_warp - 1) / elts_per_warp) * WARP_SIZE;\n                blocks_per_grid   = 1;\n            }\n            else {  // local reduce\n                if (elts % (elts_per_thread * threads_per_block) == 0) {\n                    blocks_per_grid =\n                        (elts + elts_per_thread * threads_per_block - 1) / (elts_per_thread * threads_per_block);\n                    // NOTE: need to adjust here\n                    if (blocks_per_grid > MAX_ALL_REDUCE_BLOCKS) {\n                        int iter_factor = 1;\n                        while (blocks_per_grid / iter_factor > MAX_ALL_REDUCE_BLOCKS || blocks_per_grid % iter_factor) {\n                            iter_factor += 1;\n                        }\n                        blocks_per_grid /= iter_factor;\n                    }\n                }\n                else {\n                    int total_threads = elts / elts_per_thread;\n                    blocks_per_grid   = 1;\n                    while (total_threads % blocks_per_grid != 0\n                           || total_threads / blocks_per_grid > DEFAULT_BLOCK_SIZE) {\n                        blocks_per_grid += 1;\n                    }\n                    threads_per_block = total_threads / blocks_per_grid;\n                }\n            }\n            break;\n        }\n        case 1: {  // two stage all reduce algo\n            int total_threads = elts / RANKS_PER_NODE / RANKS_PER_NODE;\n            assert(elts / RANKS_PER_NODE % RANKS_PER_NODE == 0 && total_threads % WARP_SIZE == 0);\n\n            while (total_threads % blocks_per_grid != 0 || total_threads / blocks_per_grid > DEFAULT_BLOCK_SIZE) {\n                blocks_per_grid += 1;\n            }\n\n            threads_per_block = total_threads / blocks_per_grid;\n\n            // NOTE: need to adjust here\n            if (blocks_per_grid > MAX_ALL_REDUCE_BLOCKS) {\n                int iter_factor = 1;\n                while (blocks_per_grid / iter_factor > MAX_ALL_REDUCE_BLOCKS || blocks_per_grid % iter_factor) {\n                    iter_factor += 1;\n                }\n                blocks_per_grid /= iter_factor;\n            }\n            break;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\nvoid invokeOneOrTwoShotAllReduceKernel(AllReduceParams<T>& param, hipStream_t stream)\n{\n    size_t elts_total      = param.elts_total;\n    int    blocks_per_grid = 1, threads_per_block = DEFAULT_BLOCK_SIZE;\n    int    kernel_algo = 1;\n    if (elts_total * sizeof(T) <= DEFALUT_ALGO_AR_SIZE_THRESHOLD) {\n        kernel_algo = 0;\n    }\n\n    kernelLaunchConfig(blocks_per_grid, threads_per_block, elts_total, kernel_algo, sizeof(T));\n\n    if (kernel_algo == 0) {\n        param.elts_per_rank  = elts_total;\n        param.elts_per_block = param.elts_per_rank / blocks_per_grid;\n        oneShotAllReduceKernel<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);\n    }\n    else {\n        param.elts_per_rank  = param.elts_total / RANKS_PER_NODE;\n        param.elts_per_block = param.elts_per_rank / blocks_per_grid;\n        param.rank_offset    = param.rank * param.elts_per_rank;\n        twoShotAllReduceKernel<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);\n    }\n}\n\n// Template instantiation\ntemplate void invokeOneOrTwoShotAllReduceKernel<uint16_t>(AllReduceParams<uint16_t>& param, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeOneOrTwoShotAllReduceKernel<__hip_bfloat16>(AllReduceParams<__hip_bfloat16>& param,\n                                                               hipStream_t                    stream);\n#endif\ntemplate void invokeOneOrTwoShotAllReduceKernel<uint32_t>(AllReduceParams<uint32_t>& param, hipStream_t stream);\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/custom_ar_kernels.cu"}
{"input": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/xlnet_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\nnamespace fastertransformer {\ntemplate<typename T>\nint numPerThread()\n{\n    return sizeof(float) / sizeof(T);\n}\n\nint next_pow2(int a)\n{\n    int rval = 32;\n    if (a > 32) {\n        while (rval < a) {\n            rval <<= 1;\n        }\n    }\n    return rval;\n}\n\n__device__ half2 cH2(const half* ptr, int offset)\n{\n    return __ldg((half2*)(ptr + offset));\n}\n\n#ifdef ENABLE_BF16\n__device__ __nv_bfloat162 cH2(const __nv_bfloat16* ptr, int offset)\n{\n    return ldg((__nv_bfloat162*)(ptr + offset));\n}\n#endif\n\n__forceinline__ __device__ unsigned lane_id()\n{\n    unsigned ret;\n    asm volatile(\"mov.u32 %0, %laneid;\" : \"=r\"(ret));\n    return ret;\n}\n\n__forceinline__ __device__ unsigned warp_id()\n{\n    // this is not equal to threadIdx.x / 32\n    unsigned ret;\n    asm volatile(\"mov.u32 %0, %warpid;\" : \"=r\"(ret));\n    return ret;\n}\n\ntemplate<typename T>\n__inline__ __device__ T warpReduceSum(T val)\n{\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        val += __shfl_xor_sync(FINAL_MASK, val, mask, 32);\n    }\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T warpReduceMax(T val)\n{\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        val = max(val, __shfl_xor_sync(FINAL_MASK, val, mask, 32));\n    }\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T blockReduceSum(T val)\n{\n    static __shared__ T shared[32];\n    int                 lane = threadIdx.x & 0x1f;\n    int                 wid  = threadIdx.x >> 5;\n\n    val = warpReduceSum<T>(val);\n\n    if (lane == 0) {\n        shared[wid] = val;\n    }\n\n    __syncthreads();\n\n    val = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : (T)(0.0f);\n    val = warpReduceSum<T>(val);\n\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T blockReduceMax(T val)\n{\n    static __shared__ T shared[32];\n    int                 lane = threadIdx.x & 0x1f;  // in-warp idx\n    int                 wid  = threadIdx.x >> 5;    // warp idx\n\n    val = warpReduceMax(val);  // get maxx in each warp\n\n    if (lane == 0) {  // record in-warp maxx by warp Idx\n        shared[wid] = val;\n    }\n\n    __syncthreads();\n\n    val = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : -1e30f;\n    val = warpReduceMax(val);\n\n    return val;\n}\n\n/********************** Kernels ************************/\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ prepareMatrixes(T*        q_buf,\n                                T*        q_buf_bd,\n                                T*        q_buf_ef,\n                                T*        k_buf,\n                                T*        k_buf_bd,\n                                T*        k_buf_ef,\n                                const T*  query_buf,\n                                const T*  key_buf,\n                                const T*  k_head_r,\n                                const T*  attr_seg_embed,\n                                const T*  attr_bias_Q_w,\n                                const T*  attr_bias_Q_r,\n                                const T*  attr_bias_Q_s,\n                                const int off0,\n                                const int i_off1,\n                                const int o_off1,\n                                int       off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n\n    int batch    = blockIdx.y;\n    int seq      = blockIdx.x;\n    int head_loc = threadIdx.x * 2;\n\n    T2 tmp;\n    if (head_loc < i_off1) {\n        int head     = head_loc / off2;\n        int loc      = head_loc % off2;\n        int h2_index = (batch * off0 + seq * i_off1 + head_loc) >> 1;\n\n        tmp              = ((T2*)query_buf)[h2_index];\n        int h2_index_out = (batch * off0 + head * o_off1 + seq * off2 + loc) >> 1;\n        // left matrix\n        ((T2*)q_buf)[h2_index_out]    = hadd2(tmp, cH2(attr_bias_Q_w, head_loc));\n        ((T2*)q_buf_bd)[h2_index_out] = hadd2(tmp, cH2(attr_bias_Q_r, head_loc));\n        ((T2*)q_buf_ef)[h2_index_out] = hadd2(tmp, cH2(attr_bias_Q_s, head_loc));\n\n        // right matrix\n        ((T2*)k_buf)[h2_index_out] = ((T2*)key_buf)[h2_index];  // ac\n\n        // bd\n        h2_index     = (seq * i_off1 + head_loc) >> 1;  //(seq, head_loc)\n        tmp          = ((T2*)k_head_r)[h2_index];\n        h2_index_out = (batch * off0 * 2 + head * o_off1 * 2 + seq * off2 + loc) >> 1;  //(batch, head,seq,loc)\n        ((T2*)k_buf_bd)[h2_index_out] = tmp;\n\n        h2_index = (seq * i_off1 + head_loc + off0) >> 1;  //(seq+seq_len, head_loc)\n        tmp      = ((T2*)k_head_r)[h2_index];\n        h2_index_out =\n            (batch * off0 * 2 + (head * 2 + 1) * o_off1 + seq * off2 + loc) >> 1;  //(batch, head,seq+seq_len,loc)\n        ((T2*)k_buf_bd)[h2_index_out] = tmp;\n\n        // ef\n        if (seq <= 1) {\n            h2_index                      = (seq * i_off1 + head_loc) >> 1;  //(seq, head, loc)\n            tmp                           = ((T2*)attr_seg_embed)[h2_index];\n            h2_index_out                  = (batch * 2 * i_off1 + (head * 2 + seq) * off2 + loc) >> 1;  //(head,seq,loc)\n            ((T2*)k_buf_ef)[h2_index_out] = tmp;\n        }\n    }\n}\n\ntemplate<>\nvoid __global__ prepareMatrixes(float*       q_buf,\n                                float*       q_buf_bd,\n                                float*       q_buf_ef,\n                                float*       k_buf,\n                                float*       k_buf_bd,\n                                float*       k_buf_ef,\n                                const float* query_buf,\n                                const float* key_buf,\n                                const float* k_head_r,\n                                const float* attr_seg_embed,\n                                const float* attr_bias_Q_w,\n                                const float* attr_bias_Q_r,\n                                const float* attr_bias_Q_s,\n                                const int    off0,\n                                const int    i_off1,\n                                const int    o_off1,\n                                int          off2)\n{\n    int batch    = blockIdx.y;\n    int seq      = blockIdx.x;\n    int head_loc = threadIdx.x;\n\n    float tmp;\n    if (head_loc < i_off1) {\n        int head = head_loc / off2;\n        int loc  = head_loc % off2;\n\n        int index     = batch * off0 + seq * i_off1 + head_loc;\n        tmp           = query_buf[index];\n        int index_out = batch * off0 + head * o_off1 + seq * off2 + loc;\n        // left matrix\n        q_buf[index_out]    = tmp + __ldg(attr_bias_Q_w + head_loc);  // tex2D(t_attr_bias_Q_w, loc, head);\n        q_buf_bd[index_out] = tmp + __ldg(attr_bias_Q_r + head_loc);  // tex2D(t_attr_bias_Q_r, loc, head);\n        q_buf_ef[index_out] = tmp + __ldg(attr_bias_Q_s + head_loc);  // tex2D(t_attr_bias_Q_s, loc, head);\n\n        // right matrix\n        k_buf[index_out] = key_buf[index];  // ac\n\n        // bd\n        index               = seq * i_off1 + head_loc;  //(seq, head_loc)\n        tmp                 = k_head_r[index];\n        index_out           = index_out + batch * off0 + head * o_off1;  //(batch, head,seq,loc)\n        k_buf_bd[index_out] = tmp;\n\n        index               = index + off0;  //(seq+seq_len, head_loc)\n        tmp                 = k_head_r[index];\n        index_out           = index_out + o_off1;  //(batch, head,seq+seq_len,loc)\n        k_buf_bd[index_out] = tmp;\n\n        // ef\n        if (seq <= 1) {\n            index               = seq * i_off1 + head_loc;  //(seq, head, loc)\n            tmp                 = attr_seg_embed[index];\n            index_out           = batch * 2 * i_off1 + (head * 2 + seq) * off2 + loc;  //(head,seq,loc)\n            k_buf_ef[index_out] = tmp;\n        }\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ transpose102(T* dst, const T* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int x[4] = {0};\n    x[0]     = blockIdx.x;   //[0,7]\n    x[1]     = blockIdx.y;   //[0,11]\n    x[2]     = threadIdx.x;  //[0,127]\n\n    int input_index = (x[0] * off0 + x[1] * i_off1 + x[2] * off2) >> 1;  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n\n    int out_index = (x[0] * off0 + x[2] * o_off1 + x[1] * off2) >> 1;  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n    ((T2*)dst)[out_index] = ((T2*)src)[input_index];\n}\n\ntemplate<>\nvoid __global__\ntranspose102(float* dst, const float* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    int x[4] = {0};\n    x[0]     = blockIdx.x;   //[0,7]\n    x[1]     = blockIdx.y;   //[0,11]\n    x[2]     = threadIdx.x;  //[0,127]\n    x[3]     = threadIdx.y;  //[0,1]\n\n    int input_index = x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3];  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n\n    int out_index = x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3];  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n    dst[out_index] = src[input_index];\n}\n\ntemplate<typename T>\nvoid __global__ transpose201(\n    T* dst, const T* src, const int off0, const int i_off1, const int head_num, const int o_off1, const int seq_len)\n{\n    int batch = blockIdx.x;\n    int d0    = blockIdx.y;\n    int d1    = threadIdx.x;\n\n    extern __shared__ float sdata[];\n    int                     i = 0;\n\n    // Read data into shared memory\n    int index  = batch * off0 + d0 * i_off1;  // d1*i_off2+d2\n    int offset = d1;\n    src        = src + index;\n    int row    = offset / head_num;\n    int col    = offset % head_num;\n    for (i = 0; i < head_num; i++) {\n        sdata[row * (head_num + 1) + col] = cuda_cast<float>(src[offset]);\n        offset += seq_len;\n        row = offset / head_num;\n        col = offset % head_num;\n    }\n\n    __syncthreads();\n\n    index  = batch * off0 + d0 * seq_len + d1;\n    offset = 0;\n    dst    = dst + index;\n    for (i = 0; i < head_num; i++) {\n        dst[offset] = cuda_cast<T>(sdata[d1 * (head_num + 1) + i]);\n        offset += o_off1;\n    }\n}\n\n/*int threads=512;\n  seq_dim1=threads/seq_len\n  seq_dim2=seq_len/dimx\n  dim3 grid_rel(batch_size, head_num, seq_dim2);\n  dim3 block_rel(seq_dim1, seq_len);*/\ntemplate<typename T>\nvoid __global__ relShiftBd(T* outMatrix, const T* inputMatrix, const int off0, const int off1, const int seq_len)\n{\n    using T2  = typename TypeConverter<T>::Type;        // half2 or bfloat162\n    int batch = blockIdx.x;                             //[0,7]\n    int head  = blockIdx.y;                             //[0,11]\n    int row   = blockIdx.z * blockDim.x + threadIdx.x;  //[0,127]\n    int col   = threadIdx.y * 2;                        //[0,255]\n\n    int input_index = (batch * off0 + head * off1 + row * seq_len) * 2 + col;\n    int out_index;\n    int out_row;\n    int out_col;\n    int tmp_index;\n    T2  idata;\n    if (col >= seq_len || row != 0) {\n        idata = ((T2*)inputMatrix)[input_index >> 1];\n        // int tmp_index=row*(2*seq_len-1)+row+col-seq_len;\n        tmp_index = row * 2 * seq_len + col - seq_len;\n        out_row   = tmp_index / (seq_len * 2 - 1);\n        out_col   = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            out_index            = (batch * off0 + head * off1 + out_row * seq_len + out_col);\n            outMatrix[out_index] = idata.x;\n        }\n        tmp_index += 1;\n        out_row = tmp_index / (seq_len * 2 - 1);\n        out_col = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            out_index            = (batch * off0 + head * off1 + out_row * seq_len + out_col);\n            outMatrix[out_index] = idata.y;\n        }\n    }\n}\n\n/*dim3 grid_shift(batch_size, head_num, seq_len);\n  dim3 block_shift(seq_len*2);\n  int off0=head_num*seq_len*seq_len;\n  int off1=seq_len*seq_len; */\ntemplate<>\nvoid __global__\nrelShiftBd(float* outMatrix, const float* inputMatrix, const int off0, const int off1, const int seq_len)\n{\n    int batch = blockIdx.x;   //[0,7]\n    int head  = blockIdx.y;   //[0,11]\n    int row   = blockIdx.z;   //[0,127]\n    int col   = threadIdx.x;  //[0,255]\n\n    int input_index = (batch * off0 + head * off1 + row * seq_len) * 2 + col;\n    if (col >= seq_len || row != 0) {\n        float idata = inputMatrix[input_index];\n        // int tmp_index=row*(2*seq_len-1)+row+col-seq_len;\n        int tmp_index = row * 2 * seq_len + col - seq_len;\n        int out_row   = tmp_index / (seq_len * 2 - 1);\n        int out_col   = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            int out_index        = batch * off0 + head * off1 + out_row * seq_len + out_col;\n            outMatrix[out_index] = idata;\n        }\n    }\n}\n\n/*dim3 grid_score(batch_size,head_num,seq_len);\n  dim3 block_score(next_pow2(seq_len));\n  int off0=head_num*seq_len*seq_len;\n  int off1=seq_len*seq_len;\n  float p=(1/(pow(size_per_head,0.5)));\n\n  int voff0=head_num*seq_len*size_per_head;\n  int v_o_off1=seq_len*size_per_head;\n  int voff2=size_per_head;\n  int v_i_off1=head_num*size_per_head;*/\ntemplate<typename T>\n__global__ void calAttnScore_valueBuf(T*          attn_score,\n                                      const T*    ac,\n                                      const T*    bd,\n                                      const T*    ef,\n                                      const T*    attn_mask,\n                                      const int   off0,\n                                      const int   off1,\n                                      const int   seq_len,\n                                      const float p,\n                                      T*          value_buf_trans,\n                                      const T*    value_buf,\n                                      const int   voff0,\n                                      const int   v_i_off1,\n                                      const int   v_o_off1,\n                                      const int   voff2)\n{\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z;\n    int seq2  = threadIdx.x;\n\n    int offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int index  = offset + seq2;\n    int out_index;\n    T   score;\n    T   mask;\n    T   large_value = -1e4;\n    if (sizeof(T) == 4) {\n        large_value = -1e30f;\n    }\n    if (seq2 < seq_len) {\n        score = ac[index] + bd[index] + ef[index];\n        score = score * p;\n\n        out_index = batch * off1 + seq1 * seq_len + seq2;\n        mask      = attn_mask[out_index] * (large_value);\n        score     = score + mask;\n    }\n    // softmax(attn_score+offset,seq_len, seq2);\n    __shared__ float s_sum, s_max;\n    float            tmp     = seq2 < seq_len ? score : large_value;\n    float            max_val = blockReduceMax<float>(tmp);\n    if (seq2 == 0) {\n        s_max = max_val;\n    }\n    __syncthreads();\n    float qk_tmp  = seq2 < seq_len ? __expf((float)(tmp - s_max)) : 0.0f;\n    float sum_val = blockReduceSum<float>(qk_tmp);\n    __syncthreads();\n    if (seq2 == 0) {\n        s_sum = sum_val;\n    }\n    __syncthreads();\n    if (seq2 < seq_len) {\n        attn_score[index] = (T)(qk_tmp / s_sum);\n    }\n    // end softmax\n\n    offset = seq2;\n    while (offset < voff2) {\n        out_index                  = batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset;\n        index                      = batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset;\n        value_buf_trans[out_index] = value_buf[index];\n        offset += seq_len;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ calAttnScore_valueBuf_small(T*          attn_score,\n                                            const T*    ac,\n                                            const T*    bd,\n                                            const T*    ef,\n                                            const T*    attn_mask,\n                                            const int   off0,\n                                            const int   off1,\n                                            const int   seq_len,\n                                            int         n_seq1,\n                                            const float p,\n                                            T*          value_buf_trans,\n                                            const T*    value_buf,\n                                            const int   voff0,\n                                            const int   v_i_off1,\n                                            const int   v_o_off1,\n                                            const int   voff2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int lid  = lane_id();\n    int tid  = threadIdx.x;\n    int wid  = tid / 32;\n    int seq2 = lid << 1;\n\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z * n_seq1 + wid;\n\n    int    offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int    index  = (offset + seq2) >> 1;\n    int    out_index;\n    float2 tmp1, tmp2;\n\n    // Data prepare section\n    if (seq2 < seq_len) {\n        tmp1 = cuda_cast<float2>(((T2*)ac)[index]);\n        tmp2 = cuda_cast<float2>(((T2*)bd)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n        // tmp1=__hadd2(tmp1,tmp2);\n        tmp2 = cuda_cast<float2>(((T2*)ef)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n\n        // half2 score=__hadd2(tmp1, tmp2);\n        tmp1.x = tmp1.x * p;\n        tmp1.y = tmp1.y * p;\n\n        out_index = (batch * off1 + seq1 * seq_len + seq2) >> 1;\n        tmp2      = cuda_cast<float2>(((T2*)attn_mask)[out_index]);\n\n        tmp1.x = tmp1.x + -1e4 * tmp2.x;\n        tmp1.y = tmp1.y + -1e4 * tmp2.y;\n    }\n    else {\n        tmp1.x = tmp1.y = -1e4f;\n    }\n\n    // Softmax section\n    float tmp = tmp1.x > tmp1.y ? tmp1.x : tmp1.y;\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n    }\n    tmp = __shfl_sync(FINAL_MASK, tmp, 0);\n\n    /// normalize the input\n    tmp1.x = seq2 < seq_len ? __expf((float)(tmp1.x - tmp)) : 0.0f;\n    tmp1.y = seq2 < seq_len ? __expf((float)(tmp1.y - tmp)) : 0.0f;\n    tmp    = tmp1.x + tmp1.y;\n    /// get sum of the normalized value\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n    }\n    if (seq2 == 0) {\n        tmp = tmp;\n    }\n    tmp = __shfl_sync(FINAL_MASK, tmp, 0);\n\n    /// set the value\n    if (seq2 < seq_len) {\n        tmp1.x                   = tmp1.x / tmp;\n        tmp1.y                   = tmp1.y / tmp;\n        ((T2*)attn_score)[index] = cuda_cast<T2>(tmp1);\n    }\n\n    // value_buf section\n    offset = seq2;\n    while (offset < voff2) {\n        index = (batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset) >> 1;\n        T2 v  = ((T2*)value_buf)[index];\n\n        out_index                         = (batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset) >> 1;\n        ((T2*)value_buf_trans)[out_index] = v;\n        offset += seq_len;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ calAttnScore_valueBuf_large(T*          attn_score,\n                                            const T*    ac,\n                                            const T*    bd,\n                                            const T*    ef,\n                                            const T*    attn_mask,\n                                            const int   off0,\n                                            const int   off1,\n                                            const int   seq_len,\n                                            const float p,\n                                            T*          value_buf_trans,\n                                            const T*    value_buf,\n                                            const int   voff0,\n                                            const int   v_i_off1,\n                                            const int   v_o_off1,\n                                            const int   voff2)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z;\n\n    int lid  = lane_id();\n    int tid  = threadIdx.x;\n    int wid  = tid / 32;\n    int seq2 = tid << 1;\n\n    int              offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int              index  = (offset + seq2) >> 1;\n    int              out_index;\n    float2           tmp1, tmp2;\n    __shared__ float sdata[32];\n    __shared__ float s_max;\n    __shared__ float s_sum;\n    // Data prepare section\n    if (seq2 < seq_len) {\n        tmp1 = cuda_cast<float2>(((T2*)ac)[index]);\n        tmp2 = cuda_cast<float2>(((T2*)bd)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n        // tmp1=__hadd2(tmp1,tmp2);\n        tmp2 = cuda_cast<float2>(((T2*)ef)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n\n        // half2 score=__hadd2(tmp1, tmp2);\n        tmp1.x = tmp1.x * p;\n        tmp1.y = tmp1.y * p;\n\n        out_index = (batch * off1 + seq1 * seq_len + seq2) >> 1;\n        tmp2      = cuda_cast<float2>(((T2*)attn_mask)[out_index]);\n\n        tmp1.x = tmp1.x + -1e4 * tmp2.x;\n        tmp1.y = tmp1.y + -1e4 * tmp2.y;\n    }\n    else {\n        tmp1.x = tmp1.y = -1e4f;\n    }\n    // Softmax section\n    float tmp = tmp1.x > tmp1.y ? tmp1.x : tmp1.y;\n    for (int mask = 16; mask > 0; mask /= 2) {\n        tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n    }\n    if (wid == 0) {\n        sdata[lid] = -1e4f;\n    }\n    __syncthreads();\n    if (lid == 0) {\n        sdata[wid] = tmp;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n        tmp = sdata[lid];\n        for (int mask = 16; mask > 0; mask /= 2) {\n            tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n        }\n    }\n    if (tid == 0) {\n        s_max = tmp;\n    }\n    __syncthreads();\n\n    /// normalize the input\n\n    tmp1.x = seq2 < seq_len ? __expf((float)(tmp1.x - s_max)) : 0.0f;\n    tmp1.y = seq2 < seq_len ? __expf((float)(tmp1.y - s_max)) : 0.0f;\n    tmp    = tmp1.x + tmp1.y;\n\n    /// get sum of the normalized value\n    for (int mask = 16; mask > 0; mask /= 2) {\n        tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n    }\n    if (wid == 0) {\n        sdata[lid] = 0;\n    }\n    __syncthreads();\n    if (lid == 0) {\n        sdata[wid] = tmp;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n        tmp = sdata[tid];\n        for (int mask = 16; mask > 0; mask /= 2) {\n            tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n        }\n    }\n    if (tid == 0) {\n        s_sum = tmp;\n    }\n    __syncthreads();\n\n    /// set the value\n    if (seq2 < seq_len) {\n        tmp1.x                   = tmp1.x / s_sum;\n        tmp1.y                   = tmp1.y / s_sum;\n        ((T2*)attn_score)[index] = cuda_cast<T2>(tmp1);\n    }\n    // value_buf section\n    offset = seq2;\n    while (offset < voff2) {\n        index = (batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset) >> 1;\n        T2 v  = ((T2*)value_buf)[index];\n\n        out_index                         = (batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset) >> 1;\n        ((T2*)value_buf_trans)[out_index] = v;\n        offset += seq_len;\n    }\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\n__global__ void\ntranspose102_v2(T* dst, const T* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int x[4] = {0};\n    x[0]     = blockIdx.x;              //[0,7] batch_size\n    x[1]     = threadIdx.x * 2 / off2;  // head_num\n    x[2]     = blockIdx.y;              // seq_len\n    x[3]     = threadIdx.x * 2 % off2;  //[0,63] size_per_head\n\n    if (x[3] < off2) {\n        T2  tmp;\n        int in_index  = (x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3]) >> 1;  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n        tmp           = ((T2*)src)[in_index];\n        int out_index = (x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3]) >> 1;  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n        ((T2*)dst)[out_index] = tmp;\n    }\n}\n\n// dim3 grid_trans_v(batch_size,seq_len, head_num);\n// dim3 block_trans_v(size_per_head);\ntemplate<>\n__global__ void\ntranspose102_v2(float* dst, const float* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    int x[4] = {0};\n    x[0]     = blockIdx.x;\n    x[1]     = threadIdx.x / off2;\n    x[2]     = blockIdx.y;          //[0,128] seq_len\n    x[3]     = threadIdx.x % off2;  //[0,31] size_per_head\n\n    float tmp;\n    if (x[3] < off2) {\n        int input_index = x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3];  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n        tmp             = src[input_index];\n        int out_index   = x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3];  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n        dst[out_index] = tmp;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\n__global__ void\naddBias_layerNorm(T* out, const T* input, const T* bias, const T* gamma, const T* beta, int m, int n, float epsilon)\n{\n    using T2             = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int              tid = threadIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float2           local_out_fp2;\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;  // blockIdx.x * n + i\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    float local_out = 0.0f;\n    int   id        = (blockIdx.x * n + tid * 2) >> 1;\n    local_out_fp2   = cuda_cast<float2>(hadd2(input_ptr[id], ldg(&bias_ptr[id])));\n\n    local_out += local_out_fp2.x;\n    local_out += local_out_fp2.y;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    variance = (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n    variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / n + epsilon);\n    __syncthreads();\n\n    float2 gamma_val = cuda_cast<float2>(ldg(&gamma_ptr[tid]));\n    float2 beta_val  = cuda_cast<float2>(ldg(&beta_ptr[tid]));\n    local_out_fp2.x  = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n    local_out_fp2.y  = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n    out_ptr[id]      = cuda_cast<T2>(local_out_fp2);\n}\n\ntemplate<>\n__global__ void addBias_layerNorm(float*       out,\n                                  const float* input,\n                                  const float* bias,\n                                  const float* gamma,\n                                  const float* beta,\n                                  int          m,\n                                  int          n,\n                                  float        epsilon)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = 0.0f;\n    int   id        = blockIdx.x * n + tid;\n    local_out += (float)(input[id]);\n    local_out += (float)(__ldg(&bias[id]));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = local_out - s_mean;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + epsilon);\n    }\n    __syncthreads();\n\n    out[id] = (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[tid])) + (float)(__ldg(&beta[tid])));\n}\n\n/*width=hidden_dim_ff;\n  height=seq_len;\n  dim3 block(1024);\n  dim3 grid(batch_size, seq_len);\n  gelu_bias_loop<<<grid, block, 0, stream>>>(output_fc1, output_fc1, attr_fc1_bias, hidden_dim_ff,seq_len); */\ntemplate<typename T>\n__global__ void gelu_bias_loop(T* src, const T* bias, int width, int height)\n{\n    int batch = blockIdx.x;\n    int x     = blockIdx.y;\n    int y     = threadIdx.x;\n\n    if (x < height) {\n        int   index = batch * width * height + x * width;\n        float v_src;\n        float v_bias;\n        float v;\n        for (; y < width; y = y + blockDim.x) {\n            v_bias = bias[y];\n            v_src  = src[index + y];\n            v      = v_src + v_bias;\n\n            src[index + y] = (T)(0.5f * v * (1.0f + tanhf(0.79788456f * (v + 0.044715f * v * v * v))));\n        }\n    }\n}\ntemplate<>\n__global__ void gelu_bias_loop(__half* src, const __half* bias, int width, int height)\n{\n    int batch = blockIdx.x;\n    int x     = blockIdx.y;\n    int y     = threadIdx.x * 2;\n\n    if (x < height) {\n        int    index = batch * width * height + x * width;\n        half2  v_src;\n        half2  v_bias;\n        half2  v;\n        float2 t;\n        for (; y < width; y = y + blockDim.x * 2) {\n            v_bias = ((half2*)bias)[y >> 1];\n            v_src  = ((half2*)src)[(index + y) >> 1];\n            v      = __hadd2(v_src, v_bias);\n            t      = __half22float2(v);\n            t.x    = (0.5f * t.x * (1.0f + tanhf(0.79788456f * (t.x + 0.044715f * t.x * t.x * t.x))));\n            t.y    = (0.5f * t.y * (1.0f + tanhf(0.79788456f * (t.y + 0.044715f * t.y * t.y * t.y))));\n\n            ((half2*)src)[(index + y) >> 1] = __float22half2_rn(t);\n        }\n    }\n}\n\n/*********************Invoke Functions***********************/\n\ntemplate<typename T>\nvoid invokePrepareMatrixes(int          batch_size,\n                           int          seq_len,\n                           int          hidden_dim,\n                           int          size_per_head,\n                           T*           q_buf,\n                           T*           q_buf_bd,\n                           T*           q_buf_ef,\n                           T*           k_buf,\n                           T*           k_buf_bd,\n                           T*           k_buf_ef,\n                           T*           query_buf,\n                           T*           key_buf,\n                           T*           k_head_r,\n                           T*           attr_seg_embed,\n                           T*           attr_bias_Q_w,\n                           T*           attr_bias_Q_r,\n                           T*           attr_bias_Q_s,\n                           cudaStream_t stream)\n{\n    int off0   = seq_len * hidden_dim;  // seq_len*head_num*size_per_head\n    int i_off1 = hidden_dim;            // head_num*size_per_head\n    int o_off1 = seq_len * size_per_head;\n    int off2   = size_per_head;\n\n    dim3 grid(seq_len, batch_size);\n    dim3 block(next_pow2(hidden_dim) / numPerThread<T>());\n    prepareMatrixes<<<grid, block, 0, stream>>>(q_buf,\n                                                q_buf_bd,\n                                                q_buf_ef,\n                                                k_buf,\n                                                k_buf_bd,\n                                                k_buf_ef,\n                                                query_buf,\n                                                key_buf,\n                                                k_head_r,\n                                                attr_seg_embed,\n                                                attr_bias_Q_w,\n                                                attr_bias_Q_r,\n                                                attr_bias_Q_s,\n                                                off0,\n                                                i_off1,\n                                                o_off1,\n                                                off2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose102(\n    int batch_size, int seq_len, int head_num, T* qk_buf_ef_trans, T* qk_buf_ef, cudaStream_t stream)\n{\n    dim3 grid_trans_v(batch_size, head_num);\n\n    // dim3 block_trans_v(seq_len,2);//float\n    // dim3 block_trans_v(seq_len);//__half\n    dim3 block_trans_v(seq_len, 2 / (numPerThread<T>()));\n\n    int toff0   = head_num * seq_len * 2;\n    int ti_off1 = seq_len * 2;\n    int to_off1 = head_num * 2;\n    int toff2   = 2;\n\n    transpose102<<<grid_trans_v, block_trans_v, 0, stream>>>(\n        qk_buf_ef_trans, qk_buf_ef, toff0, ti_off1, to_off1, toff2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose201(\n    int batch_size, int seq_len, int head_num, T* qk_buf_ef_seg_trans, T* qk_buf_ef_seg, cudaStream_t stream)\n{\n    dim3 grid_trans2(batch_size, seq_len);\n    dim3 block_trans2(seq_len);\n    int  t2_off0   = seq_len * seq_len * head_num;\n    int  t2_i_off1 = seq_len * head_num;\n    int  t2_o_off1 = seq_len * seq_len;\n    int  t2_i_off2 = head_num;\n    int  t2_o_off2 = seq_len;\n\n    transpose201<<<grid_trans2, block_trans2, seq_len*(head_num + 1) * sizeof(float), stream>>>(\n        qk_buf_ef_seg_trans, qk_buf_ef_seg, t2_off0, t2_i_off1, t2_i_off2, t2_o_off1, t2_o_off2);\n}\n\n// applied to half and bfloat16\ntemplate<typename T>\nvoid blockRelShiftBd(dim3& grid, dim3& block, int batch_size, int head_num, int seq_len)\n{\n    int threads  = 512;\n    int seq_dim1 = threads / seq_len;\n    int seq_dim2 = seq_len / seq_dim1;\n\n    grid.x = batch_size;\n    grid.y = head_num;\n    grid.z = seq_dim2;\n\n    block.x = seq_dim1;\n    block.y = seq_len;\n}\n\ntemplate<>\nvoid blockRelShiftBd<float>(dim3& grid, dim3& block, int batch_size, int head_num, int seq_len)\n{\n    grid.x = batch_size;\n    grid.y = head_num;\n    grid.z = seq_len;\n\n    block.x = seq_len * 2;\n}\n\ntemplate<typename T>\nvoid invokeRelShiftBd(int batch_size, int head_num, int seq_len, T* qk_buf_bd_shift, T* qk_buf_bd, cudaStream_t stream)\n{\n    dim3 grid_shift;\n    dim3 block_shift;\n    blockRelShiftBd<T>(grid_shift, block_shift, batch_size, head_num, seq_len);\n\n    int off0 = head_num * seq_len * seq_len;\n    int off1 = seq_len * seq_len;\n\n    relShiftBd<<<grid_shift, block_shift, 0, stream>>>(qk_buf_bd_shift, qk_buf_bd, off0, off1, seq_len);\n}\n\ntemplate<typename T>\nvoid invokeCalAttnScore(int          batch_size,\n                        int          head_num,\n                        int          seq_len,\n                        int          size_per_head,\n                        float        q_scaling,\n                        T*           attn_score,\n                        T*           qk_buf,\n                        T*           qk_buf_bd_shift,\n                        T*           qk_buf_ef_seg_trans,\n                        T*           attn_mask,\n                        T*           value_buf_trans,\n                        T*           value_buf,\n                        cudaStream_t stream)\n{\n    int   off0 = head_num * seq_len * seq_len;\n    int   off1 = seq_len * seq_len;\n    float p    = 1 / ((pow(size_per_head, 0.5)) * q_scaling);\n\n    int voff0    = head_num * seq_len * size_per_head;\n    int v_o_off1 = seq_len * size_per_head;\n    int voff2    = size_per_head;\n    int v_i_off1 = head_num * size_per_head;\n    if (seq_len <= 32) {\n        dim3 grid_score(batch_size, head_num, 2);\n        dim3 block_score(seq_len / 2 * next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_small<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            seq_len / 2,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n    else if (seq_len <= 64) {\n        dim3 grid_score(batch_size, head_num, seq_len / 2);\n        dim3 block_score(2 * next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_small<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            2,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n    else {\n        dim3 grid_score(batch_size, head_num, seq_len);\n        dim3 block_score(next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_large<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n}\n\ntemplate<>\nvoid invokeCalAttnScore(int          batch_size,\n                        int          head_num,\n                        int          seq_len,\n                        int          size_per_head,\n                        float        q_scaling,\n                        float*       attn_score,\n                        float*       qk_buf,\n                        float*       qk_buf_bd_shift,\n                        float*       qk_buf_ef_seg_trans,\n                        float*       attn_mask,\n                        float*       value_buf_trans,\n                        float*       value_buf,\n                        cudaStream_t stream)\n{\n    int   off0 = head_num * seq_len * seq_len;\n    int   off1 = seq_len * seq_len;\n    float p    = 1 / ((pow(size_per_head, 0.5)) * q_scaling);\n\n    int voff0    = head_num * seq_len * size_per_head;\n    int v_o_off1 = seq_len * size_per_head;\n    int voff2    = size_per_head;\n    int v_i_off1 = head_num * size_per_head;\n\n    dim3 grid_score(batch_size, head_num, seq_len);\n    dim3 block_score(next_pow2(seq_len));\n    calAttnScore_valueBuf<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                  qk_buf,\n                                                                  qk_buf_bd_shift,\n                                                                  qk_buf_ef_seg_trans,\n                                                                  attn_mask,\n                                                                  off0,\n                                                                  off1,\n                                                                  seq_len,\n                                                                  p,\n                                                                  value_buf_trans,\n                                                                  value_buf,\n                                                                  voff0,\n                                                                  v_i_off1,\n                                                                  v_o_off1,\n                                                                  voff2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose102v2(\n    int batch_size, int seq_len, int head_num, int size_per_head, T* attn_vec_trans, T* attn_vec, cudaStream_t stream)\n{\n    dim3 grid_trans_v(batch_size, seq_len);\n    dim3 block_trans_v(head_num * size_per_head / numPerThread<T>());\n\n    int off0   = head_num * seq_len * size_per_head;\n    int i_off1 = seq_len * size_per_head;\n    int o_off1 = head_num * size_per_head;\n    int off2   = size_per_head;\n\n    transpose102_v2<<<grid_trans_v, block_trans_v, 0, stream>>>(attn_vec_trans, attn_vec, off0, i_off1, o_off1, off2);\n}\n\ntemplate<typename T>\nvoid invokeAddResidualLayerNorm(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                T*           attn_layernorm,\n                                T*           attn_out,\n                                const T*     in_tensor,\n                                const T*     attr_layernorm_gamma,\n                                const T*     attr_layernorm_beta,\n                                cudaStream_t stream)\n{\n    dim3 grid(batch_size * seq_len);\n    dim3 block(hidden_dim / numPerThread<T>());\n    addBias_layerNorm<T><<<grid, block, 0, stream>>>(attn_layernorm,\n                                                     attn_out,\n                                                     in_tensor,\n                                                     attr_layernorm_gamma,\n                                                     attr_layernorm_beta,\n                                                     batch_size * seq_len,\n                                                     hidden_dim,\n                                                     epsilon);\n}\n\ntemplate<typename T>\nvoid invokeGelu(\n    int batch_size, int seq_len, int hidden_dim_ff, T* output_fc1, const T* attr_fc1_bias, cudaStream_t stream)\n{\n    dim3 block(1024 / numPerThread<T>());\n    dim3 grid(batch_size, seq_len);\n    gelu_bias_loop<<<grid, block, 0, stream>>>(output_fc1, attr_fc1_bias, hidden_dim_ff, seq_len);\n}\n\n/*********************The explicit instantiation part***********************/\ntemplate void invokePrepareMatrixes<float>(int          batch_size,\n                                           int          seq_len,\n                                           int          hidden_dim,\n                                           int          size_per_head,\n                                           float*       q_buf,\n                                           float*       q_buf_bd,\n                                           float*       q_buf_ef,\n                                           float*       k_buf,\n                                           float*       k_buf_bd,\n                                           float*       k_buf_ef,\n                                           float*       query_buf,\n                                           float*       key_buf,\n                                           float*       k_head_r,\n                                           float*       attr_seg_embed,\n                                           float*       attr_bias_Q_w,\n                                           float*       attr_bias_Q_r,\n                                           float*       attr_bias_Q_s,\n                                           cudaStream_t stream);\n\ntemplate void invokePrepareMatrixes<half>(int          batch_size,\n                                          int          seq_len,\n                                          int          hidden_dim,\n                                          int          size_per_head,\n                                          half*        q_buf,\n                                          half*        q_buf_bd,\n                                          half*        q_buf_ef,\n                                          half*        k_buf,\n                                          half*        k_buf_bd,\n                                          half*        k_buf_ef,\n                                          half*        query_buf,\n                                          half*        key_buf,\n                                          half*        k_head_r,\n                                          half*        attr_seg_embed,\n                                          half*        attr_bias_Q_w,\n                                          half*        attr_bias_Q_r,\n                                          half*        attr_bias_Q_s,\n                                          cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokePrepareMatrixes<__nv_bfloat16>(int            batch_size,\n                                                   int            seq_len,\n                                                   int            hidden_dim,\n                                                   int            size_per_head,\n                                                   __nv_bfloat16* q_buf,\n                                                   __nv_bfloat16* q_buf_bd,\n                                                   __nv_bfloat16* q_buf_ef,\n                                                   __nv_bfloat16* k_buf,\n                                                   __nv_bfloat16* k_buf_bd,\n                                                   __nv_bfloat16* k_buf_ef,\n                                                   __nv_bfloat16* query_buf,\n                                                   __nv_bfloat16* key_buf,\n                                                   __nv_bfloat16* k_head_r,\n                                                   __nv_bfloat16* attr_seg_embed,\n                                                   __nv_bfloat16* attr_bias_Q_w,\n                                                   __nv_bfloat16* attr_bias_Q_r,\n                                                   __nv_bfloat16* attr_bias_Q_s,\n                                                   cudaStream_t   stream);\n#endif\n\ntemplate void invokeTranspose102<float>(\n    int batch_size, int seq_len, int head_num, float* qk_buf_ef_trans, float* qk_buf_ef, cudaStream_t stream);\n\ntemplate void invokeTranspose102<half>(\n    int batch_size, int seq_len, int head_num, half* qk_buf_ef_trans, half* qk_buf_ef, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose102<__nv_bfloat16>(int            batch_size,\n                                                int            seq_len,\n                                                int            head_num,\n                                                __nv_bfloat16* qk_buf_ef_trans,\n                                                __nv_bfloat16* qk_buf_ef,\n                                                cudaStream_t   stream);\n#endif\n\ntemplate void invokeTranspose201<float>(\n    int batch_size, int seq_len, int head_num, float* qk_buf_ef_seg_trans, float* qk_buf_ef_seg, cudaStream_t stream);\n\ntemplate void invokeTranspose201<half>(\n    int batch_size, int seq_len, int head_num, half* qk_buf_ef_seg_trans, half* qk_buf_ef_seg, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose201<__nv_bfloat16>(int            batch_size,\n                                                int            seq_len,\n                                                int            head_num,\n                                                __nv_bfloat16* qk_buf_ef_seg_trans,\n                                                __nv_bfloat16* qk_buf_ef_seg,\n                                                cudaStream_t   stream);\n#endif\n\ntemplate void invokeRelShiftBd<float>(\n    int batch_size, int head_num, int seq_len, float* qk_buf_bd_shift, float* qk_buf_bd, cudaStream_t stream);\n\ntemplate void invokeRelShiftBd<half>(\n    int batch_size, int head_num, int seq_len, half* qk_buf_bd_shift, half* qk_buf_bd, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeRelShiftBd<__nv_bfloat16>(int            batch_size,\n                                              int            head_num,\n                                              int            seq_len,\n                                              __nv_bfloat16* qk_buf_bd_shift,\n                                              __nv_bfloat16* qk_buf_bd,\n                                              cudaStream_t   stream);\n#endif\n\ntemplate void invokeCalAttnScore<float>(int          batch_size,\n                                        int          head_num,\n                                        int          seq_len,\n                                        int          size_per_head,\n                                        float        q_scaling,\n                                        float*       attn_score,\n                                        float*       qk_buf,\n                                        float*       qk_buf_bd_shift,\n                                        float*       qk_buf_ef_seg_trans,\n                                        float*       attn_mask,\n                                        float*       value_buf_trans,\n                                        float*       value_buf,\n                                        cudaStream_t stream);\n\ntemplate void invokeCalAttnScore<half>(int          batch_size,\n                                       int          head_num,\n                                       int          seq_len,\n                                       int          size_per_head,\n                                       float        q_scaling,\n                                       half*        attn_score,\n                                       half*        qk_buf,\n                                       half*        qk_buf_bd_shift,\n                                       half*        qk_buf_ef_seg_trans,\n                                       half*        attn_mask,\n                                       half*        value_buf_trans,\n                                       half*        value_buf,\n                                       cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeCalAttnScore<__nv_bfloat16>(int            batch_size,\n                                                int            head_num,\n                                                int            seq_len,\n                                                int            size_per_head,\n                                                float          q_scaling,\n                                                __nv_bfloat16* attn_score,\n                                                __nv_bfloat16* qk_buf,\n                                                __nv_bfloat16* qk_buf_bd_shift,\n                                                __nv_bfloat16* qk_buf_ef_seg_trans,\n                                                __nv_bfloat16* attn_mask,\n                                                __nv_bfloat16* value_buf_trans,\n                                                __nv_bfloat16* value_buf,\n                                                cudaStream_t   stream);\n#endif\n\ntemplate void invokeTranspose102v2<float>(int          batch_size,\n                                          int          seq_len,\n                                          int          head_num,\n                                          int          size_per_head,\n                                          float*       attn_vec_trans,\n                                          float*       attn_vec,\n                                          cudaStream_t stream);\ntemplate void invokeTranspose102v2<half>(int          batch_size,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          size_per_head,\n                                         half*        attn_vec_trans,\n                                         half*        attn_vec,\n                                         cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose102v2<__nv_bfloat16>(int            batch_size,\n                                                  int            seq_len,\n                                                  int            head_num,\n                                                  int            size_per_head,\n                                                  __nv_bfloat16* attn_vec_trans,\n                                                  __nv_bfloat16* attn_vec,\n                                                  cudaStream_t   stream);\n#endif\n\ntemplate void invokeAddResidualLayerNorm<float>(int          batch_size,\n                                                int          seq_len,\n                                                int          hidden_dim,\n                                                float*       attn_layernorm,\n                                                float*       attn_out,\n                                                const float* in_tensor,\n                                                const float* attr_layernorm_gamma,\n                                                const float* attr_layernorm_beta,\n                                                cudaStream_t stream);\n\ntemplate void invokeAddResidualLayerNorm<half>(int          batch_size,\n                                               int          seq_len,\n                                               int          hidden_dim,\n                                               half*        attn_layernorm,\n                                               half*        attn_out,\n                                               const half*  in_tensor,\n                                               const half*  attr_layernorm_gamma,\n                                               const half*  attr_layernorm_beta,\n                                               cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddResidualLayerNorm<__nv_bfloat16>(int                  batch_size,\n                                                        int                  seq_len,\n                                                        int                  hidden_dim,\n                                                        __nv_bfloat16*       attn_layernorm,\n                                                        __nv_bfloat16*       attn_out,\n                                                        const __nv_bfloat16* in_tensor,\n                                                        const __nv_bfloat16* attr_layernorm_gamma,\n                                                        const __nv_bfloat16* attr_layernorm_beta,\n                                                        cudaStream_t         stream);\n#endif\n\ntemplate void invokeGelu<float>(\n    int batch_size, int seq_len, int hidden_dim_ff, float* output_fc1, const float* attr_fc1_bias, cudaStream_t stream);\n\ntemplate void invokeGelu<half>(\n    int batch_size, int seq_len, int hidden_dim_ff, half* output_fc1, const half* attr_fc1_bias, cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2020-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/xlnet_attention_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\nnamespace fastertransformer {\ntemplate<typename T>\nint numPerThread()\n{\n    return sizeof(float) / sizeof(T);\n}\n\nint next_pow2(int a)\n{\n    int rval = 32;\n    if (a > 32) {\n        while (rval < a) {\n            rval <<= 1;\n        }\n    }\n    return rval;\n}\n\n__device__ half2 cH2(const half* ptr, int offset)\n{\n    return __ldg((half2*)(ptr + offset));\n}\n\n#ifdef ENABLE_BF16\n__device__ __hip_bfloat162 cH2(const __hip_bfloat16* ptr, int offset)\n{\n    return ldg((__hip_bfloat162*)(ptr + offset));\n}\n#endif\n\n__forceinline__ __device__ unsigned lane_id()\n{\n    unsigned ret;\n    asm volatile(\"mov.u32 %0, %laneid;\" : \"=r\"(ret));\n    return ret;\n}\n\n__forceinline__ __device__ unsigned warp_id()\n{\n    // this is not equal to threadIdx.x / 32\n    unsigned ret;\n    asm volatile(\"mov.u32 %0, %warpid;\" : \"=r\"(ret));\n    return ret;\n}\n\ntemplate<typename T>\n__inline__ __device__ T warpReduceSum(T val)\n{\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        val += __shfl_xor_sync(FINAL_MASK, val, mask, 32);\n    }\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T warpReduceMax(T val)\n{\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        val = max(val, __shfl_xor_sync(FINAL_MASK, val, mask, 32));\n    }\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T blockReduceSum(T val)\n{\n    static __shared__ T shared[32];\n    int                 lane = threadIdx.x & 0x1f;\n    int                 wid  = threadIdx.x >> 5;\n\n    val = warpReduceSum<T>(val);\n\n    if (lane == 0) {\n        shared[wid] = val;\n    }\n\n    __syncthreads();\n\n    val = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : (T)(0.0f);\n    val = warpReduceSum<T>(val);\n\n    return val;\n}\n\ntemplate<typename T>\n__inline__ __device__ T blockReduceMax(T val)\n{\n    static __shared__ T shared[32];\n    int                 lane = threadIdx.x & 0x1f;  // in-warp idx\n    int                 wid  = threadIdx.x >> 5;    // warp idx\n\n    val = warpReduceMax(val);  // get maxx in each warp\n\n    if (lane == 0) {  // record in-warp maxx by warp Idx\n        shared[wid] = val;\n    }\n\n    __syncthreads();\n\n    val = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : -1e30f;\n    val = warpReduceMax(val);\n\n    return val;\n}\n\n/********************** Kernels ************************/\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ prepareMatrixes(T*        q_buf,\n                                T*        q_buf_bd,\n                                T*        q_buf_ef,\n                                T*        k_buf,\n                                T*        k_buf_bd,\n                                T*        k_buf_ef,\n                                const T*  query_buf,\n                                const T*  key_buf,\n                                const T*  k_head_r,\n                                const T*  attr_seg_embed,\n                                const T*  attr_bias_Q_w,\n                                const T*  attr_bias_Q_r,\n                                const T*  attr_bias_Q_s,\n                                const int off0,\n                                const int i_off1,\n                                const int o_off1,\n                                int       off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n\n    int batch    = blockIdx.y;\n    int seq      = blockIdx.x;\n    int head_loc = threadIdx.x * 2;\n\n    T2 tmp;\n    if (head_loc < i_off1) {\n        int head     = head_loc / off2;\n        int loc      = head_loc % off2;\n        int h2_index = (batch * off0 + seq * i_off1 + head_loc) >> 1;\n\n        tmp              = ((T2*)query_buf)[h2_index];\n        int h2_index_out = (batch * off0 + head * o_off1 + seq * off2 + loc) >> 1;\n        // left matrix\n        ((T2*)q_buf)[h2_index_out]    = hadd2(tmp, cH2(attr_bias_Q_w, head_loc));\n        ((T2*)q_buf_bd)[h2_index_out] = hadd2(tmp, cH2(attr_bias_Q_r, head_loc));\n        ((T2*)q_buf_ef)[h2_index_out] = hadd2(tmp, cH2(attr_bias_Q_s, head_loc));\n\n        // right matrix\n        ((T2*)k_buf)[h2_index_out] = ((T2*)key_buf)[h2_index];  // ac\n\n        // bd\n        h2_index     = (seq * i_off1 + head_loc) >> 1;  //(seq, head_loc)\n        tmp          = ((T2*)k_head_r)[h2_index];\n        h2_index_out = (batch * off0 * 2 + head * o_off1 * 2 + seq * off2 + loc) >> 1;  //(batch, head,seq,loc)\n        ((T2*)k_buf_bd)[h2_index_out] = tmp;\n\n        h2_index = (seq * i_off1 + head_loc + off0) >> 1;  //(seq+seq_len, head_loc)\n        tmp      = ((T2*)k_head_r)[h2_index];\n        h2_index_out =\n            (batch * off0 * 2 + (head * 2 + 1) * o_off1 + seq * off2 + loc) >> 1;  //(batch, head,seq+seq_len,loc)\n        ((T2*)k_buf_bd)[h2_index_out] = tmp;\n\n        // ef\n        if (seq <= 1) {\n            h2_index                      = (seq * i_off1 + head_loc) >> 1;  //(seq, head, loc)\n            tmp                           = ((T2*)attr_seg_embed)[h2_index];\n            h2_index_out                  = (batch * 2 * i_off1 + (head * 2 + seq) * off2 + loc) >> 1;  //(head,seq,loc)\n            ((T2*)k_buf_ef)[h2_index_out] = tmp;\n        }\n    }\n}\n\ntemplate<>\nvoid __global__ prepareMatrixes(float*       q_buf,\n                                float*       q_buf_bd,\n                                float*       q_buf_ef,\n                                float*       k_buf,\n                                float*       k_buf_bd,\n                                float*       k_buf_ef,\n                                const float* query_buf,\n                                const float* key_buf,\n                                const float* k_head_r,\n                                const float* attr_seg_embed,\n                                const float* attr_bias_Q_w,\n                                const float* attr_bias_Q_r,\n                                const float* attr_bias_Q_s,\n                                const int    off0,\n                                const int    i_off1,\n                                const int    o_off1,\n                                int          off2)\n{\n    int batch    = blockIdx.y;\n    int seq      = blockIdx.x;\n    int head_loc = threadIdx.x;\n\n    float tmp;\n    if (head_loc < i_off1) {\n        int head = head_loc / off2;\n        int loc  = head_loc % off2;\n\n        int index     = batch * off0 + seq * i_off1 + head_loc;\n        tmp           = query_buf[index];\n        int index_out = batch * off0 + head * o_off1 + seq * off2 + loc;\n        // left matrix\n        q_buf[index_out]    = tmp + __ldg(attr_bias_Q_w + head_loc);  // tex2D(t_attr_bias_Q_w, loc, head);\n        q_buf_bd[index_out] = tmp + __ldg(attr_bias_Q_r + head_loc);  // tex2D(t_attr_bias_Q_r, loc, head);\n        q_buf_ef[index_out] = tmp + __ldg(attr_bias_Q_s + head_loc);  // tex2D(t_attr_bias_Q_s, loc, head);\n\n        // right matrix\n        k_buf[index_out] = key_buf[index];  // ac\n\n        // bd\n        index               = seq * i_off1 + head_loc;  //(seq, head_loc)\n        tmp                 = k_head_r[index];\n        index_out           = index_out + batch * off0 + head * o_off1;  //(batch, head,seq,loc)\n        k_buf_bd[index_out] = tmp;\n\n        index               = index + off0;  //(seq+seq_len, head_loc)\n        tmp                 = k_head_r[index];\n        index_out           = index_out + o_off1;  //(batch, head,seq+seq_len,loc)\n        k_buf_bd[index_out] = tmp;\n\n        // ef\n        if (seq <= 1) {\n            index               = seq * i_off1 + head_loc;  //(seq, head, loc)\n            tmp                 = attr_seg_embed[index];\n            index_out           = batch * 2 * i_off1 + (head * 2 + seq) * off2 + loc;  //(head,seq,loc)\n            k_buf_ef[index_out] = tmp;\n        }\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ transpose102(T* dst, const T* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int x[4] = {0};\n    x[0]     = blockIdx.x;   //[0,7]\n    x[1]     = blockIdx.y;   //[0,11]\n    x[2]     = threadIdx.x;  //[0,127]\n\n    int input_index = (x[0] * off0 + x[1] * i_off1 + x[2] * off2) >> 1;  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n\n    int out_index = (x[0] * off0 + x[2] * o_off1 + x[1] * off2) >> 1;  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n    ((T2*)dst)[out_index] = ((T2*)src)[input_index];\n}\n\ntemplate<>\nvoid __global__\ntranspose102(float* dst, const float* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    int x[4] = {0};\n    x[0]     = blockIdx.x;   //[0,7]\n    x[1]     = blockIdx.y;   //[0,11]\n    x[2]     = threadIdx.x;  //[0,127]\n    x[3]     = threadIdx.y;  //[0,1]\n\n    int input_index = x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3];  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n\n    int out_index = x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3];  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n    dst[out_index] = src[input_index];\n}\n\ntemplate<typename T>\nvoid __global__ transpose201(\n    T* dst, const T* src, const int off0, const int i_off1, const int head_num, const int o_off1, const int seq_len)\n{\n    int batch = blockIdx.x;\n    int d0    = blockIdx.y;\n    int d1    = threadIdx.x;\n\n    extern __shared__ float sdata[];\n    int                     i = 0;\n\n    // Read data into shared memory\n    int index  = batch * off0 + d0 * i_off1;  // d1*i_off2+d2\n    int offset = d1;\n    src        = src + index;\n    int row    = offset / head_num;\n    int col    = offset % head_num;\n    for (i = 0; i < head_num; i++) {\n        sdata[row * (head_num + 1) + col] = cuda_cast<float>(src[offset]);\n        offset += seq_len;\n        row = offset / head_num;\n        col = offset % head_num;\n    }\n\n    __syncthreads();\n\n    index  = batch * off0 + d0 * seq_len + d1;\n    offset = 0;\n    dst    = dst + index;\n    for (i = 0; i < head_num; i++) {\n        dst[offset] = cuda_cast<T>(sdata[d1 * (head_num + 1) + i]);\n        offset += o_off1;\n    }\n}\n\n/*int threads=512;\n  seq_dim1=threads/seq_len\n  seq_dim2=seq_len/dimx\n  dim3 grid_rel(batch_size, head_num, seq_dim2);\n  dim3 block_rel(seq_dim1, seq_len);*/\ntemplate<typename T>\nvoid __global__ relShiftBd(T* outMatrix, const T* inputMatrix, const int off0, const int off1, const int seq_len)\n{\n    using T2  = typename TypeConverter<T>::Type;        // half2 or bfloat162\n    int batch = blockIdx.x;                             //[0,7]\n    int head  = blockIdx.y;                             //[0,11]\n    int row   = blockIdx.z * blockDim.x + threadIdx.x;  //[0,127]\n    int col   = threadIdx.y * 2;                        //[0,255]\n\n    int input_index = (batch * off0 + head * off1 + row * seq_len) * 2 + col;\n    int out_index;\n    int out_row;\n    int out_col;\n    int tmp_index;\n    T2  idata;\n    if (col >= seq_len || row != 0) {\n        idata = ((T2*)inputMatrix)[input_index >> 1];\n        // int tmp_index=row*(2*seq_len-1)+row+col-seq_len;\n        tmp_index = row * 2 * seq_len + col - seq_len;\n        out_row   = tmp_index / (seq_len * 2 - 1);\n        out_col   = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            out_index            = (batch * off0 + head * off1 + out_row * seq_len + out_col);\n            outMatrix[out_index] = idata.x;\n        }\n        tmp_index += 1;\n        out_row = tmp_index / (seq_len * 2 - 1);\n        out_col = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            out_index            = (batch * off0 + head * off1 + out_row * seq_len + out_col);\n            outMatrix[out_index] = idata.y;\n        }\n    }\n}\n\n/*dim3 grid_shift(batch_size, head_num, seq_len);\n  dim3 block_shift(seq_len*2);\n  int off0=head_num*seq_len*seq_len;\n  int off1=seq_len*seq_len; */\ntemplate<>\nvoid __global__\nrelShiftBd(float* outMatrix, const float* inputMatrix, const int off0, const int off1, const int seq_len)\n{\n    int batch = blockIdx.x;   //[0,7]\n    int head  = blockIdx.y;   //[0,11]\n    int row   = blockIdx.z;   //[0,127]\n    int col   = threadIdx.x;  //[0,255]\n\n    int input_index = (batch * off0 + head * off1 + row * seq_len) * 2 + col;\n    if (col >= seq_len || row != 0) {\n        float idata = inputMatrix[input_index];\n        // int tmp_index=row*(2*seq_len-1)+row+col-seq_len;\n        int tmp_index = row * 2 * seq_len + col - seq_len;\n        int out_row   = tmp_index / (seq_len * 2 - 1);\n        int out_col   = tmp_index % (seq_len * 2 - 1);\n        if (out_col < seq_len) {\n            int out_index        = batch * off0 + head * off1 + out_row * seq_len + out_col;\n            outMatrix[out_index] = idata;\n        }\n    }\n}\n\n/*dim3 grid_score(batch_size,head_num,seq_len);\n  dim3 block_score(next_pow2(seq_len));\n  int off0=head_num*seq_len*seq_len;\n  int off1=seq_len*seq_len;\n  float p=(1/(pow(size_per_head,0.5)));\n\n  int voff0=head_num*seq_len*size_per_head;\n  int v_o_off1=seq_len*size_per_head;\n  int voff2=size_per_head;\n  int v_i_off1=head_num*size_per_head;*/\ntemplate<typename T>\n__global__ void calAttnScore_valueBuf(T*          attn_score,\n                                      const T*    ac,\n                                      const T*    bd,\n                                      const T*    ef,\n                                      const T*    attn_mask,\n                                      const int   off0,\n                                      const int   off1,\n                                      const int   seq_len,\n                                      const float p,\n                                      T*          value_buf_trans,\n                                      const T*    value_buf,\n                                      const int   voff0,\n                                      const int   v_i_off1,\n                                      const int   v_o_off1,\n                                      const int   voff2)\n{\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z;\n    int seq2  = threadIdx.x;\n\n    int offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int index  = offset + seq2;\n    int out_index;\n    T   score;\n    T   mask;\n    T   large_value = -1e4;\n    if (sizeof(T) == 4) {\n        large_value = -1e30f;\n    }\n    if (seq2 < seq_len) {\n        score = ac[index] + bd[index] + ef[index];\n        score = score * p;\n\n        out_index = batch * off1 + seq1 * seq_len + seq2;\n        mask      = attn_mask[out_index] * (large_value);\n        score     = score + mask;\n    }\n    // softmax(attn_score+offset,seq_len, seq2);\n    __shared__ float s_sum, s_max;\n    float            tmp     = seq2 < seq_len ? score : large_value;\n    float            max_val = blockReduceMax<float>(tmp);\n    if (seq2 == 0) {\n        s_max = max_val;\n    }\n    __syncthreads();\n    float qk_tmp  = seq2 < seq_len ? __expf((float)(tmp - s_max)) : 0.0f;\n    float sum_val = blockReduceSum<float>(qk_tmp);\n    __syncthreads();\n    if (seq2 == 0) {\n        s_sum = sum_val;\n    }\n    __syncthreads();\n    if (seq2 < seq_len) {\n        attn_score[index] = (T)(qk_tmp / s_sum);\n    }\n    // end softmax\n\n    offset = seq2;\n    while (offset < voff2) {\n        out_index                  = batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset;\n        index                      = batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset;\n        value_buf_trans[out_index] = value_buf[index];\n        offset += seq_len;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ calAttnScore_valueBuf_small(T*          attn_score,\n                                            const T*    ac,\n                                            const T*    bd,\n                                            const T*    ef,\n                                            const T*    attn_mask,\n                                            const int   off0,\n                                            const int   off1,\n                                            const int   seq_len,\n                                            int         n_seq1,\n                                            const float p,\n                                            T*          value_buf_trans,\n                                            const T*    value_buf,\n                                            const int   voff0,\n                                            const int   v_i_off1,\n                                            const int   v_o_off1,\n                                            const int   voff2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int lid  = lane_id();\n    int tid  = threadIdx.x;\n    int wid  = tid / 32;\n    int seq2 = lid << 1;\n\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z * n_seq1 + wid;\n\n    int    offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int    index  = (offset + seq2) >> 1;\n    int    out_index;\n    float2 tmp1, tmp2;\n\n    // Data prepare section\n    if (seq2 < seq_len) {\n        tmp1 = cuda_cast<float2>(((T2*)ac)[index]);\n        tmp2 = cuda_cast<float2>(((T2*)bd)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n        // tmp1=__hadd2(tmp1,tmp2);\n        tmp2 = cuda_cast<float2>(((T2*)ef)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n\n        // half2 score=__hadd2(tmp1, tmp2);\n        tmp1.x = tmp1.x * p;\n        tmp1.y = tmp1.y * p;\n\n        out_index = (batch * off1 + seq1 * seq_len + seq2) >> 1;\n        tmp2      = cuda_cast<float2>(((T2*)attn_mask)[out_index]);\n\n        tmp1.x = tmp1.x + -1e4 * tmp2.x;\n        tmp1.y = tmp1.y + -1e4 * tmp2.y;\n    }\n    else {\n        tmp1.x = tmp1.y = -1e4f;\n    }\n\n    // Softmax section\n    float tmp = tmp1.x > tmp1.y ? tmp1.x : tmp1.y;\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n    }\n    tmp = __shfl_sync(FINAL_MASK, tmp, 0);\n\n    /// normalize the input\n    tmp1.x = seq2 < seq_len ? __expf((float)(tmp1.x - tmp)) : 0.0f;\n    tmp1.y = seq2 < seq_len ? __expf((float)(tmp1.y - tmp)) : 0.0f;\n    tmp    = tmp1.x + tmp1.y;\n    /// get sum of the normalized value\n    for (int mask = 16; mask > 0; mask >>= 1) {\n        tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n    }\n    if (seq2 == 0) {\n        tmp = tmp;\n    }\n    tmp = __shfl_sync(FINAL_MASK, tmp, 0);\n\n    /// set the value\n    if (seq2 < seq_len) {\n        tmp1.x                   = tmp1.x / tmp;\n        tmp1.y                   = tmp1.y / tmp;\n        ((T2*)attn_score)[index] = cuda_cast<T2>(tmp1);\n    }\n\n    // value_buf section\n    offset = seq2;\n    while (offset < voff2) {\n        index = (batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset) >> 1;\n        T2 v  = ((T2*)value_buf)[index];\n\n        out_index                         = (batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset) >> 1;\n        ((T2*)value_buf_trans)[out_index] = v;\n        offset += seq_len;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\nvoid __global__ calAttnScore_valueBuf_large(T*          attn_score,\n                                            const T*    ac,\n                                            const T*    bd,\n                                            const T*    ef,\n                                            const T*    attn_mask,\n                                            const int   off0,\n                                            const int   off1,\n                                            const int   seq_len,\n                                            const float p,\n                                            T*          value_buf_trans,\n                                            const T*    value_buf,\n                                            const int   voff0,\n                                            const int   v_i_off1,\n                                            const int   v_o_off1,\n                                            const int   voff2)\n{\n    using T2  = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int batch = blockIdx.x;\n    int head  = blockIdx.y;\n    int seq1  = blockIdx.z;\n\n    int lid  = lane_id();\n    int tid  = threadIdx.x;\n    int wid  = tid / 32;\n    int seq2 = tid << 1;\n\n    int              offset = batch * off0 + head * off1 + seq1 * seq_len;\n    int              index  = (offset + seq2) >> 1;\n    int              out_index;\n    float2           tmp1, tmp2;\n    __shared__ float sdata[32];\n    __shared__ float s_max;\n    __shared__ float s_sum;\n    // Data prepare section\n    if (seq2 < seq_len) {\n        tmp1 = cuda_cast<float2>(((T2*)ac)[index]);\n        tmp2 = cuda_cast<float2>(((T2*)bd)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n        // tmp1=__hadd2(tmp1,tmp2);\n        tmp2 = cuda_cast<float2>(((T2*)ef)[index]);\n        tmp1.x += tmp2.x;\n        tmp1.y += tmp2.y;\n\n        // half2 score=__hadd2(tmp1, tmp2);\n        tmp1.x = tmp1.x * p;\n        tmp1.y = tmp1.y * p;\n\n        out_index = (batch * off1 + seq1 * seq_len + seq2) >> 1;\n        tmp2      = cuda_cast<float2>(((T2*)attn_mask)[out_index]);\n\n        tmp1.x = tmp1.x + -1e4 * tmp2.x;\n        tmp1.y = tmp1.y + -1e4 * tmp2.y;\n    }\n    else {\n        tmp1.x = tmp1.y = -1e4f;\n    }\n    // Softmax section\n    float tmp = tmp1.x > tmp1.y ? tmp1.x : tmp1.y;\n    for (int mask = 16; mask > 0; mask /= 2) {\n        tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n    }\n    if (wid == 0) {\n        sdata[lid] = -1e4f;\n    }\n    __syncthreads();\n    if (lid == 0) {\n        sdata[wid] = tmp;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n        tmp = sdata[lid];\n        for (int mask = 16; mask > 0; mask /= 2) {\n            tmp = max(tmp, __shfl_xor_sync(FINAL_MASK, tmp, mask, 32));\n        }\n    }\n    if (tid == 0) {\n        s_max = tmp;\n    }\n    __syncthreads();\n\n    /// normalize the input\n\n    tmp1.x = seq2 < seq_len ? __expf((float)(tmp1.x - s_max)) : 0.0f;\n    tmp1.y = seq2 < seq_len ? __expf((float)(tmp1.y - s_max)) : 0.0f;\n    tmp    = tmp1.x + tmp1.y;\n\n    /// get sum of the normalized value\n    for (int mask = 16; mask > 0; mask /= 2) {\n        tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n    }\n    if (wid == 0) {\n        sdata[lid] = 0;\n    }\n    __syncthreads();\n    if (lid == 0) {\n        sdata[wid] = tmp;\n    }\n    __syncthreads();\n\n    if (wid == 0) {\n        tmp = sdata[tid];\n        for (int mask = 16; mask > 0; mask /= 2) {\n            tmp = tmp + __shfl_xor_sync(FINAL_MASK, tmp, mask, 32);\n        }\n    }\n    if (tid == 0) {\n        s_sum = tmp;\n    }\n    __syncthreads();\n\n    /// set the value\n    if (seq2 < seq_len) {\n        tmp1.x                   = tmp1.x / s_sum;\n        tmp1.y                   = tmp1.y / s_sum;\n        ((T2*)attn_score)[index] = cuda_cast<T2>(tmp1);\n    }\n    // value_buf section\n    offset = seq2;\n    while (offset < voff2) {\n        index = (batch * voff0 + seq1 * v_i_off1 + head * voff2 + offset) >> 1;\n        T2 v  = ((T2*)value_buf)[index];\n\n        out_index                         = (batch * voff0 + head * v_o_off1 + seq1 * voff2 + offset) >> 1;\n        ((T2*)value_buf_trans)[out_index] = v;\n        offset += seq_len;\n    }\n}\n\n// Applied to half or bfloat16\ntemplate<typename T>\n__global__ void\ntranspose102_v2(T* dst, const T* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    using T2 = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int x[4] = {0};\n    x[0]     = blockIdx.x;              //[0,7] batch_size\n    x[1]     = threadIdx.x * 2 / off2;  // head_num\n    x[2]     = blockIdx.y;              // seq_len\n    x[3]     = threadIdx.x * 2 % off2;  //[0,63] size_per_head\n\n    if (x[3] < off2) {\n        T2  tmp;\n        int in_index  = (x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3]) >> 1;  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n        tmp           = ((T2*)src)[in_index];\n        int out_index = (x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3]) >> 1;  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n        ((T2*)dst)[out_index] = tmp;\n    }\n}\n\n// dim3 grid_trans_v(batch_size,seq_len, head_num);\n// dim3 block_trans_v(size_per_head);\ntemplate<>\n__global__ void\ntranspose102_v2(float* dst, const float* src, const int off0, const int i_off1, const int o_off1, const int off2)\n{\n    int x[4] = {0};\n    x[0]     = blockIdx.x;\n    x[1]     = threadIdx.x / off2;\n    x[2]     = blockIdx.y;          //[0,128] seq_len\n    x[3]     = threadIdx.x % off2;  //[0,31] size_per_head\n\n    float tmp;\n    if (x[3] < off2) {\n        int input_index = x[0] * off0 + x[1] * i_off1 + x[2] * off2 + x[3];  // [batch, 0, 1, 2]=[d0,d1,d2,d3]\n        tmp             = src[input_index];\n        int out_index   = x[0] * off0 + x[2] * o_off1 + x[1] * off2 + x[3];  // [batch, 1, 0, 2]=[d0,d2,d1,d3]\n\n        dst[out_index] = tmp;\n    }\n}\n\n// Applied to half and bfloat16\ntemplate<typename T>\n__global__ void\naddBias_layerNorm(T* out, const T* input, const T* bias, const T* gamma, const T* beta, int m, int n, float epsilon)\n{\n    using T2             = typename TypeConverter<T>::Type;  // half2 or bfloat162\n    int              tid = threadIdx.x;\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n    float2           local_out_fp2;\n\n    T2*       out_ptr   = (T2*)out;\n    const T2* input_ptr = (const T2*)input;\n    const T2* bias_ptr  = (const T2*)bias;  // blockIdx.x * n + i\n    const T2* gamma_ptr = (const T2*)gamma;\n    const T2* beta_ptr  = (const T2*)beta;\n\n    float local_out = 0.0f;\n    int   id        = (blockIdx.x * n + tid * 2) >> 1;\n    local_out_fp2   = cuda_cast<float2>(hadd2(input_ptr[id], ldg(&bias_ptr[id])));\n\n    local_out += local_out_fp2.x;\n    local_out += local_out_fp2.y;\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    variance = (local_out_fp2.x - s_mean) * (local_out_fp2.x - s_mean);\n    variance += (local_out_fp2.y - s_mean) * (local_out_fp2.y - s_mean);\n    variance = blockReduceSum<float>(variance);\n    if (threadIdx.x == 0)\n        s_variance = rsqrtf(variance / n + epsilon);\n    __syncthreads();\n\n    float2 gamma_val = cuda_cast<float2>(ldg(&gamma_ptr[tid]));\n    float2 beta_val  = cuda_cast<float2>(ldg(&beta_ptr[tid]));\n    local_out_fp2.x  = (local_out_fp2.x - s_mean) * s_variance * gamma_val.x + beta_val.x;\n    local_out_fp2.y  = (local_out_fp2.y - s_mean) * s_variance * gamma_val.y + beta_val.y;\n    out_ptr[id]      = cuda_cast<T2>(local_out_fp2);\n}\n\ntemplate<>\n__global__ void addBias_layerNorm(float*       out,\n                                  const float* input,\n                                  const float* bias,\n                                  const float* gamma,\n                                  const float* beta,\n                                  int          m,\n                                  int          n,\n                                  float        epsilon)\n{\n    int tid = threadIdx.x;\n\n    __shared__ float s_mean;\n    __shared__ float s_variance;\n    float            mean     = 0.0f;\n    float            variance = 0.0f;\n\n    float local_out = 0.0f;\n    int   id        = blockIdx.x * n + tid;\n    local_out += (float)(input[id]);\n    local_out += (float)(__ldg(&bias[id]));\n\n    mean = blockReduceSum<float>(local_out);\n    if (threadIdx.x == 0) {\n        s_mean = mean / n;\n    }\n    __syncthreads();\n\n    float diff = local_out - s_mean;\n    variance   = blockReduceSum<float>(diff * diff);\n    if (threadIdx.x == 0) {\n        s_variance = rsqrtf(variance / n + epsilon);\n    }\n    __syncthreads();\n\n    out[id] = (float)(((local_out - s_mean) * s_variance) * (float)(__ldg(&gamma[tid])) + (float)(__ldg(&beta[tid])));\n}\n\n/*width=hidden_dim_ff;\n  height=seq_len;\n  dim3 block(1024);\n  dim3 grid(batch_size, seq_len);\n  gelu_bias_loop<<<grid, block, 0, stream>>>(output_fc1, output_fc1, attr_fc1_bias, hidden_dim_ff,seq_len); */\ntemplate<typename T>\n__global__ void gelu_bias_loop(T* src, const T* bias, int width, int height)\n{\n    int batch = blockIdx.x;\n    int x     = blockIdx.y;\n    int y     = threadIdx.x;\n\n    if (x < height) {\n        int   index = batch * width * height + x * width;\n        float v_src;\n        float v_bias;\n        float v;\n        for (; y < width; y = y + blockDim.x) {\n            v_bias = bias[y];\n            v_src  = src[index + y];\n            v      = v_src + v_bias;\n\n            src[index + y] = (T)(0.5f * v * (1.0f + tanhf(0.79788456f * (v + 0.044715f * v * v * v))));\n        }\n    }\n}\ntemplate<>\n__global__ void gelu_bias_loop(__half* src, const __half* bias, int width, int height)\n{\n    int batch = blockIdx.x;\n    int x     = blockIdx.y;\n    int y     = threadIdx.x * 2;\n\n    if (x < height) {\n        int    index = batch * width * height + x * width;\n        half2  v_src;\n        half2  v_bias;\n        half2  v;\n        float2 t;\n        for (; y < width; y = y + blockDim.x * 2) {\n            v_bias = ((half2*)bias)[y >> 1];\n            v_src  = ((half2*)src)[(index + y) >> 1];\n            v      = __hadd2(v_src, v_bias);\n            t      = __half22float2(v);\n            t.x    = (0.5f * t.x * (1.0f + tanhf(0.79788456f * (t.x + 0.044715f * t.x * t.x * t.x))));\n            t.y    = (0.5f * t.y * (1.0f + tanhf(0.79788456f * (t.y + 0.044715f * t.y * t.y * t.y))));\n\n            ((half2*)src)[(index + y) >> 1] = __float22half2_rn(t);\n        }\n    }\n}\n\n/*********************Invoke Functions***********************/\n\ntemplate<typename T>\nvoid invokePrepareMatrixes(int          batch_size,\n                           int          seq_len,\n                           int          hidden_dim,\n                           int          size_per_head,\n                           T*           q_buf,\n                           T*           q_buf_bd,\n                           T*           q_buf_ef,\n                           T*           k_buf,\n                           T*           k_buf_bd,\n                           T*           k_buf_ef,\n                           T*           query_buf,\n                           T*           key_buf,\n                           T*           k_head_r,\n                           T*           attr_seg_embed,\n                           T*           attr_bias_Q_w,\n                           T*           attr_bias_Q_r,\n                           T*           attr_bias_Q_s,\n                           hipStream_t stream)\n{\n    int off0   = seq_len * hidden_dim;  // seq_len*head_num*size_per_head\n    int i_off1 = hidden_dim;            // head_num*size_per_head\n    int o_off1 = seq_len * size_per_head;\n    int off2   = size_per_head;\n\n    dim3 grid(seq_len, batch_size);\n    dim3 block(next_pow2(hidden_dim) / numPerThread<T>());\n    prepareMatrixes<<<grid, block, 0, stream>>>(q_buf,\n                                                q_buf_bd,\n                                                q_buf_ef,\n                                                k_buf,\n                                                k_buf_bd,\n                                                k_buf_ef,\n                                                query_buf,\n                                                key_buf,\n                                                k_head_r,\n                                                attr_seg_embed,\n                                                attr_bias_Q_w,\n                                                attr_bias_Q_r,\n                                                attr_bias_Q_s,\n                                                off0,\n                                                i_off1,\n                                                o_off1,\n                                                off2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose102(\n    int batch_size, int seq_len, int head_num, T* qk_buf_ef_trans, T* qk_buf_ef, hipStream_t stream)\n{\n    dim3 grid_trans_v(batch_size, head_num);\n\n    // dim3 block_trans_v(seq_len,2);//float\n    // dim3 block_trans_v(seq_len);//__half\n    dim3 block_trans_v(seq_len, 2 / (numPerThread<T>()));\n\n    int toff0   = head_num * seq_len * 2;\n    int ti_off1 = seq_len * 2;\n    int to_off1 = head_num * 2;\n    int toff2   = 2;\n\n    transpose102<<<grid_trans_v, block_trans_v, 0, stream>>>(\n        qk_buf_ef_trans, qk_buf_ef, toff0, ti_off1, to_off1, toff2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose201(\n    int batch_size, int seq_len, int head_num, T* qk_buf_ef_seg_trans, T* qk_buf_ef_seg, hipStream_t stream)\n{\n    dim3 grid_trans2(batch_size, seq_len);\n    dim3 block_trans2(seq_len);\n    int  t2_off0   = seq_len * seq_len * head_num;\n    int  t2_i_off1 = seq_len * head_num;\n    int  t2_o_off1 = seq_len * seq_len;\n    int  t2_i_off2 = head_num;\n    int  t2_o_off2 = seq_len;\n\n    transpose201<<<grid_trans2, block_trans2, seq_len*(head_num + 1) * sizeof(float), stream>>>(\n        qk_buf_ef_seg_trans, qk_buf_ef_seg, t2_off0, t2_i_off1, t2_i_off2, t2_o_off1, t2_o_off2);\n}\n\n// applied to half and bfloat16\ntemplate<typename T>\nvoid blockRelShiftBd(dim3& grid, dim3& block, int batch_size, int head_num, int seq_len)\n{\n    int threads  = 512;\n    int seq_dim1 = threads / seq_len;\n    int seq_dim2 = seq_len / seq_dim1;\n\n    grid.x = batch_size;\n    grid.y = head_num;\n    grid.z = seq_dim2;\n\n    block.x = seq_dim1;\n    block.y = seq_len;\n}\n\ntemplate<>\nvoid blockRelShiftBd<float>(dim3& grid, dim3& block, int batch_size, int head_num, int seq_len)\n{\n    grid.x = batch_size;\n    grid.y = head_num;\n    grid.z = seq_len;\n\n    block.x = seq_len * 2;\n}\n\ntemplate<typename T>\nvoid invokeRelShiftBd(int batch_size, int head_num, int seq_len, T* qk_buf_bd_shift, T* qk_buf_bd, hipStream_t stream)\n{\n    dim3 grid_shift;\n    dim3 block_shift;\n    blockRelShiftBd<T>(grid_shift, block_shift, batch_size, head_num, seq_len);\n\n    int off0 = head_num * seq_len * seq_len;\n    int off1 = seq_len * seq_len;\n\n    relShiftBd<<<grid_shift, block_shift, 0, stream>>>(qk_buf_bd_shift, qk_buf_bd, off0, off1, seq_len);\n}\n\ntemplate<typename T>\nvoid invokeCalAttnScore(int          batch_size,\n                        int          head_num,\n                        int          seq_len,\n                        int          size_per_head,\n                        float        q_scaling,\n                        T*           attn_score,\n                        T*           qk_buf,\n                        T*           qk_buf_bd_shift,\n                        T*           qk_buf_ef_seg_trans,\n                        T*           attn_mask,\n                        T*           value_buf_trans,\n                        T*           value_buf,\n                        hipStream_t stream)\n{\n    int   off0 = head_num * seq_len * seq_len;\n    int   off1 = seq_len * seq_len;\n    float p    = 1 / ((pow(size_per_head, 0.5)) * q_scaling);\n\n    int voff0    = head_num * seq_len * size_per_head;\n    int v_o_off1 = seq_len * size_per_head;\n    int voff2    = size_per_head;\n    int v_i_off1 = head_num * size_per_head;\n    if (seq_len <= 32) {\n        dim3 grid_score(batch_size, head_num, 2);\n        dim3 block_score(seq_len / 2 * next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_small<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            seq_len / 2,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n    else if (seq_len <= 64) {\n        dim3 grid_score(batch_size, head_num, seq_len / 2);\n        dim3 block_score(2 * next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_small<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            2,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n    else {\n        dim3 grid_score(batch_size, head_num, seq_len);\n        dim3 block_score(next_pow2(seq_len / 2));\n\n        calAttnScore_valueBuf_large<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                            qk_buf,\n                                                                            qk_buf_bd_shift,\n                                                                            qk_buf_ef_seg_trans,\n                                                                            attn_mask,\n                                                                            off0,\n                                                                            off1,\n                                                                            seq_len,\n                                                                            p,\n                                                                            value_buf_trans,\n                                                                            value_buf,\n                                                                            voff0,\n                                                                            v_i_off1,\n                                                                            v_o_off1,\n                                                                            voff2);\n    }\n}\n\ntemplate<>\nvoid invokeCalAttnScore(int          batch_size,\n                        int          head_num,\n                        int          seq_len,\n                        int          size_per_head,\n                        float        q_scaling,\n                        float*       attn_score,\n                        float*       qk_buf,\n                        float*       qk_buf_bd_shift,\n                        float*       qk_buf_ef_seg_trans,\n                        float*       attn_mask,\n                        float*       value_buf_trans,\n                        float*       value_buf,\n                        hipStream_t stream)\n{\n    int   off0 = head_num * seq_len * seq_len;\n    int   off1 = seq_len * seq_len;\n    float p    = 1 / ((pow(size_per_head, 0.5)) * q_scaling);\n\n    int voff0    = head_num * seq_len * size_per_head;\n    int v_o_off1 = seq_len * size_per_head;\n    int voff2    = size_per_head;\n    int v_i_off1 = head_num * size_per_head;\n\n    dim3 grid_score(batch_size, head_num, seq_len);\n    dim3 block_score(next_pow2(seq_len));\n    calAttnScore_valueBuf<<<grid_score, block_score, 0, stream>>>(attn_score,\n                                                                  qk_buf,\n                                                                  qk_buf_bd_shift,\n                                                                  qk_buf_ef_seg_trans,\n                                                                  attn_mask,\n                                                                  off0,\n                                                                  off1,\n                                                                  seq_len,\n                                                                  p,\n                                                                  value_buf_trans,\n                                                                  value_buf,\n                                                                  voff0,\n                                                                  v_i_off1,\n                                                                  v_o_off1,\n                                                                  voff2);\n}\n\ntemplate<typename T>\nvoid invokeTranspose102v2(\n    int batch_size, int seq_len, int head_num, int size_per_head, T* attn_vec_trans, T* attn_vec, hipStream_t stream)\n{\n    dim3 grid_trans_v(batch_size, seq_len);\n    dim3 block_trans_v(head_num * size_per_head / numPerThread<T>());\n\n    int off0   = head_num * seq_len * size_per_head;\n    int i_off1 = seq_len * size_per_head;\n    int o_off1 = head_num * size_per_head;\n    int off2   = size_per_head;\n\n    transpose102_v2<<<grid_trans_v, block_trans_v, 0, stream>>>(attn_vec_trans, attn_vec, off0, i_off1, o_off1, off2);\n}\n\ntemplate<typename T>\nvoid invokeAddResidualLayerNorm(int          batch_size,\n                                int          seq_len,\n                                int          hidden_dim,\n                                T*           attn_layernorm,\n                                T*           attn_out,\n                                const T*     in_tensor,\n                                const T*     attr_layernorm_gamma,\n                                const T*     attr_layernorm_beta,\n                                hipStream_t stream)\n{\n    dim3 grid(batch_size * seq_len);\n    dim3 block(hidden_dim / numPerThread<T>());\n    addBias_layerNorm<T><<<grid, block, 0, stream>>>(attn_layernorm,\n                                                     attn_out,\n                                                     in_tensor,\n                                                     attr_layernorm_gamma,\n                                                     attr_layernorm_beta,\n                                                     batch_size * seq_len,\n                                                     hidden_dim,\n                                                     epsilon);\n}\n\ntemplate<typename T>\nvoid invokeGelu(\n    int batch_size, int seq_len, int hidden_dim_ff, T* output_fc1, const T* attr_fc1_bias, hipStream_t stream)\n{\n    dim3 block(1024 / numPerThread<T>());\n    dim3 grid(batch_size, seq_len);\n    gelu_bias_loop<<<grid, block, 0, stream>>>(output_fc1, attr_fc1_bias, hidden_dim_ff, seq_len);\n}\n\n/*********************The explicit instantiation part***********************/\ntemplate void invokePrepareMatrixes<float>(int          batch_size,\n                                           int          seq_len,\n                                           int          hidden_dim,\n                                           int          size_per_head,\n                                           float*       q_buf,\n                                           float*       q_buf_bd,\n                                           float*       q_buf_ef,\n                                           float*       k_buf,\n                                           float*       k_buf_bd,\n                                           float*       k_buf_ef,\n                                           float*       query_buf,\n                                           float*       key_buf,\n                                           float*       k_head_r,\n                                           float*       attr_seg_embed,\n                                           float*       attr_bias_Q_w,\n                                           float*       attr_bias_Q_r,\n                                           float*       attr_bias_Q_s,\n                                           hipStream_t stream);\n\ntemplate void invokePrepareMatrixes<half>(int          batch_size,\n                                          int          seq_len,\n                                          int          hidden_dim,\n                                          int          size_per_head,\n                                          half*        q_buf,\n                                          half*        q_buf_bd,\n                                          half*        q_buf_ef,\n                                          half*        k_buf,\n                                          half*        k_buf_bd,\n                                          half*        k_buf_ef,\n                                          half*        query_buf,\n                                          half*        key_buf,\n                                          half*        k_head_r,\n                                          half*        attr_seg_embed,\n                                          half*        attr_bias_Q_w,\n                                          half*        attr_bias_Q_r,\n                                          half*        attr_bias_Q_s,\n                                          hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokePrepareMatrixes<__hip_bfloat16>(int            batch_size,\n                                                   int            seq_len,\n                                                   int            hidden_dim,\n                                                   int            size_per_head,\n                                                   __hip_bfloat16* q_buf,\n                                                   __hip_bfloat16* q_buf_bd,\n                                                   __hip_bfloat16* q_buf_ef,\n                                                   __hip_bfloat16* k_buf,\n                                                   __hip_bfloat16* k_buf_bd,\n                                                   __hip_bfloat16* k_buf_ef,\n                                                   __hip_bfloat16* query_buf,\n                                                   __hip_bfloat16* key_buf,\n                                                   __hip_bfloat16* k_head_r,\n                                                   __hip_bfloat16* attr_seg_embed,\n                                                   __hip_bfloat16* attr_bias_Q_w,\n                                                   __hip_bfloat16* attr_bias_Q_r,\n                                                   __hip_bfloat16* attr_bias_Q_s,\n                                                   hipStream_t   stream);\n#endif\n\ntemplate void invokeTranspose102<float>(\n    int batch_size, int seq_len, int head_num, float* qk_buf_ef_trans, float* qk_buf_ef, hipStream_t stream);\n\ntemplate void invokeTranspose102<half>(\n    int batch_size, int seq_len, int head_num, half* qk_buf_ef_trans, half* qk_buf_ef, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose102<__hip_bfloat16>(int            batch_size,\n                                                int            seq_len,\n                                                int            head_num,\n                                                __hip_bfloat16* qk_buf_ef_trans,\n                                                __hip_bfloat16* qk_buf_ef,\n                                                hipStream_t   stream);\n#endif\n\ntemplate void invokeTranspose201<float>(\n    int batch_size, int seq_len, int head_num, float* qk_buf_ef_seg_trans, float* qk_buf_ef_seg, hipStream_t stream);\n\ntemplate void invokeTranspose201<half>(\n    int batch_size, int seq_len, int head_num, half* qk_buf_ef_seg_trans, half* qk_buf_ef_seg, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose201<__hip_bfloat16>(int            batch_size,\n                                                int            seq_len,\n                                                int            head_num,\n                                                __hip_bfloat16* qk_buf_ef_seg_trans,\n                                                __hip_bfloat16* qk_buf_ef_seg,\n                                                hipStream_t   stream);\n#endif\n\ntemplate void invokeRelShiftBd<float>(\n    int batch_size, int head_num, int seq_len, float* qk_buf_bd_shift, float* qk_buf_bd, hipStream_t stream);\n\ntemplate void invokeRelShiftBd<half>(\n    int batch_size, int head_num, int seq_len, half* qk_buf_bd_shift, half* qk_buf_bd, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeRelShiftBd<__hip_bfloat16>(int            batch_size,\n                                              int            head_num,\n                                              int            seq_len,\n                                              __hip_bfloat16* qk_buf_bd_shift,\n                                              __hip_bfloat16* qk_buf_bd,\n                                              hipStream_t   stream);\n#endif\n\ntemplate void invokeCalAttnScore<float>(int          batch_size,\n                                        int          head_num,\n                                        int          seq_len,\n                                        int          size_per_head,\n                                        float        q_scaling,\n                                        float*       attn_score,\n                                        float*       qk_buf,\n                                        float*       qk_buf_bd_shift,\n                                        float*       qk_buf_ef_seg_trans,\n                                        float*       attn_mask,\n                                        float*       value_buf_trans,\n                                        float*       value_buf,\n                                        hipStream_t stream);\n\ntemplate void invokeCalAttnScore<half>(int          batch_size,\n                                       int          head_num,\n                                       int          seq_len,\n                                       int          size_per_head,\n                                       float        q_scaling,\n                                       half*        attn_score,\n                                       half*        qk_buf,\n                                       half*        qk_buf_bd_shift,\n                                       half*        qk_buf_ef_seg_trans,\n                                       half*        attn_mask,\n                                       half*        value_buf_trans,\n                                       half*        value_buf,\n                                       hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeCalAttnScore<__hip_bfloat16>(int            batch_size,\n                                                int            head_num,\n                                                int            seq_len,\n                                                int            size_per_head,\n                                                float          q_scaling,\n                                                __hip_bfloat16* attn_score,\n                                                __hip_bfloat16* qk_buf,\n                                                __hip_bfloat16* qk_buf_bd_shift,\n                                                __hip_bfloat16* qk_buf_ef_seg_trans,\n                                                __hip_bfloat16* attn_mask,\n                                                __hip_bfloat16* value_buf_trans,\n                                                __hip_bfloat16* value_buf,\n                                                hipStream_t   stream);\n#endif\n\ntemplate void invokeTranspose102v2<float>(int          batch_size,\n                                          int          seq_len,\n                                          int          head_num,\n                                          int          size_per_head,\n                                          float*       attn_vec_trans,\n                                          float*       attn_vec,\n                                          hipStream_t stream);\ntemplate void invokeTranspose102v2<half>(int          batch_size,\n                                         int          seq_len,\n                                         int          head_num,\n                                         int          size_per_head,\n                                         half*        attn_vec_trans,\n                                         half*        attn_vec,\n                                         hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeTranspose102v2<__hip_bfloat16>(int            batch_size,\n                                                  int            seq_len,\n                                                  int            head_num,\n                                                  int            size_per_head,\n                                                  __hip_bfloat16* attn_vec_trans,\n                                                  __hip_bfloat16* attn_vec,\n                                                  hipStream_t   stream);\n#endif\n\ntemplate void invokeAddResidualLayerNorm<float>(int          batch_size,\n                                                int          seq_len,\n                                                int          hidden_dim,\n                                                float*       attn_layernorm,\n                                                float*       attn_out,\n                                                const float* in_tensor,\n                                                const float* attr_layernorm_gamma,\n                                                const float* attr_layernorm_beta,\n                                                hipStream_t stream);\n\ntemplate void invokeAddResidualLayerNorm<half>(int          batch_size,\n                                               int          seq_len,\n                                               int          hidden_dim,\n                                               half*        attn_layernorm,\n                                               half*        attn_out,\n                                               const half*  in_tensor,\n                                               const half*  attr_layernorm_gamma,\n                                               const half*  attr_layernorm_beta,\n                                               hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeAddResidualLayerNorm<__hip_bfloat16>(int                  batch_size,\n                                                        int                  seq_len,\n                                                        int                  hidden_dim,\n                                                        __hip_bfloat16*       attn_layernorm,\n                                                        __hip_bfloat16*       attn_out,\n                                                        const __hip_bfloat16* in_tensor,\n                                                        const __hip_bfloat16* attr_layernorm_gamma,\n                                                        const __hip_bfloat16* attr_layernorm_beta,\n                                                        hipStream_t         stream);\n#endif\n\ntemplate void invokeGelu<float>(\n    int batch_size, int seq_len, int hidden_dim_ff, float* output_fc1, const float* attr_fc1_bias, hipStream_t stream);\n\ntemplate void invokeGelu<half>(\n    int batch_size, int seq_len, int hidden_dim_ff, half* output_fc1, const half* attr_fc1_bias, hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/xlnet_attention_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <cub/cub.cuh>\n#else\n#include \"3rdparty/cub/cub.cuh\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nconstexpr int   ENABLE_SINGLE_PASS_TOP_P = 0;\nconstexpr float SINGLE_PASS_THRESHOLD    = 0.9;\n\nnamespace fastertransformer {\n\nnamespace segmented_topp_impl {\n\ntemplate<int HALF_ELEMENTS_PER_WARP_LOAD>\nusing Copy_half_t = typename std::conditional<\n    HALF_ELEMENTS_PER_WARP_LOAD == 32,\n    half,\n    typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 64,\n                              int,\n                              typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 128, int2, int4>::type>::type>::\n    type;\n\ntemplate<typename T, int ELEMENTS_PER_WARP_LOAD>\nusing Copy_t = Copy_half_t<sizeof(T) / sizeof(half) * ELEMENTS_PER_WARP_LOAD>;\n\ntemplate<typename T>\nstruct Float_as_int_ {\n};\ntemplate<>\nstruct Float_as_int_<float> {\n    using Type = uint32_t;\n};\ntemplate<>\nstruct Float_as_int_<__half> {\n    using Type = uint16_t;\n};\n\nusing kernel_params_float   = Segmented_topk_kernel_params<float, int32_t, 256, 2>;\nusing kernel_params_float_1 = Segmented_topk_kernel_params<float, int32_t, 256, 1>;\nusing kernel_params_half    = Segmented_topk_kernel_params<__half, int32_t, 256, 4>;\nusing kernel_params_half_1  = Segmented_topk_kernel_params<__half, int32_t, 256, 1>;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ float to_float(uint32_t src)\n{\n    return __int_as_float(src);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ float to_float(uint16_t src)\n{\n    __half dst = __ushort_as_half(src);\n    return __half2float(dst);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// sort one segment per cta\ntemplate<typename T_SCORE, int BLOCK_THREADS, int ELEMENTS_PER_THREAD>\n__global__ void blockSortKernel(const T_SCORE* d_keys_in,\n                                T_SCORE*       d_keys_out,\n                                const int32_t* d_values_in,\n                                int32_t*       d_values_out,\n                                const int32_t* active_counts,\n                                int            num_items_,\n                                int            stride_items,\n                                int            num_segments)\n{\n    // Specialize BlockRadixSort for a 1D block\n    typedef cub::BlockRadixSort<T_SCORE, BLOCK_THREADS, ELEMENTS_PER_THREAD, int32_t> BlockRadixSort;\n\n    // Allocate shared memory for BlockRadixSort\n    __shared__ typename BlockRadixSort::TempStorage temp_storage;\n\n    if (blockIdx.x >= num_segments) {\n        return;\n    }\n\n    int num_items = active_counts[blockIdx.x];  // > num_items_ ? num_items_ : active_counts[blockIdx.x];\n\n    if (num_items == 0) {\n        return;\n    }\n\n    // Obtain a segment of consecutive items that are blocked across threads\n    T_SCORE thread_keys[ELEMENTS_PER_THREAD];\n    int32_t thread_values[ELEMENTS_PER_THREAD];\n\n    int32_t block_offset = blockIdx.x * stride_items;\n    cub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_keys_out + block_offset, thread_keys, num_items, 0);\n    cub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_values_out + block_offset, thread_values, num_items, -1);\n    __syncthreads();\n\n    // Collectively sort the keys and values among block threads\n    BlockRadixSort(temp_storage).SortDescendingBlockedToStriped(thread_keys, thread_values);\n\n    // Store output in striped fashion\n    cub::StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_keys_out + block_offset, thread_keys, num_items);\n    cub::StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_values_out + block_offset, thread_values, num_items);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// block sort kernel\ntemplate<typename T_SCORE>\nvoid blockSort(const T_SCORE* d_keys_in,\n               T_SCORE*       d_keys_out,\n               const int32_t* d_values_in,\n               int32_t*       d_values_out,\n               const int32_t* active_counts,\n               int            num_items,\n               int            stride_items,\n               int            num_segments,\n               cudaStream_t   stream)\n{\n    if (num_items == 0) {\n        return;\n    }\n\n    int kernel_index  = div_up(num_items, 128) - 1;\n    int warps_per_cta = (kernel_index + 1) * 128 / 32;\n    if (kernel_index > 7) {\n        kernel_index  = 7 + div_up(num_items, 1024) - 1;\n        warps_per_cta = 1024 / 32;\n    }\n    assert(warps_per_cta <= 32);\n\n    dim3 block(warps_per_cta * 32);\n    dim3 grid(num_segments);\n\n    using kernel_func = void (*)(const T_SCORE* d_keys_in,\n                                 T_SCORE*       d_keys_out,\n                                 const int32_t* d_values_in,\n                                 int32_t*       d_values_out,\n                                 const int32_t* active_counts,\n                                 int            num_items,\n                                 int            stride_items,\n                                 int            num_segments);\n\n    static const kernel_func kernel_funcs[] = {\n        &blockSortKernel<T_SCORE, 128, 1>,\n        &blockSortKernel<T_SCORE, 256, 1>,\n        &blockSortKernel<T_SCORE, 384, 1>,\n        &blockSortKernel<T_SCORE, 512, 1>,\n        &blockSortKernel<T_SCORE, 640, 1>,\n        &blockSortKernel<T_SCORE, 768, 1>,\n        &blockSortKernel<T_SCORE, 896, 1>,\n        &blockSortKernel<T_SCORE, 1024, 1>,\n        &blockSortKernel<T_SCORE, 1024, 2>,\n        &blockSortKernel<T_SCORE, 1024, 4>,\n        //&blockSortKernel<T_SCORE, 1024, 6>,\n    };\n    kernel_funcs[kernel_index]<<<grid, block, 0, stream>>>(\n        d_keys_in, d_keys_out, d_values_in, d_values_out, active_counts, num_items, stride_items, num_segments);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstruct BlockPrefixCallbackOp {\n    // Running prefix\n    int running_total;\n    // Constructor\n    __device__ BlockPrefixCallbackOp(uint32_t running_total): running_total(running_total) {}\n    // Callback operator to be entered by the first warp of threads in the block.\n    // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n    __device__ int operator()(uint32_t block_aggregate)\n    {\n        uint32_t old_prefix = running_total;\n        running_total += block_aggregate;\n        return old_prefix;\n    }\n};\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define DO_DEBUG_PRINT 0\n\n// governs the split between regs and smem\nconstexpr float SMEM_FRACTION = 0.5F;\nconstexpr float P_EPSILON     = 0.01F;\n\nconstexpr int MAX_TOP_K = 3072;\nconstexpr int WARP_SZ   = 32;\n\ntemplate<typename Kernel_params, int ITEMS_PER_THREAD>\n__global__ __launch_bounds__(Kernel_params::BLOCK_THREADS,\n                             1) void segmented_top_p_single_pass(TopKPerSegmentParams params)\n{\n#if DO_DEBUG_PRINT\n    constexpr int debug_block_id = 26;\n#endif\n\n    using Key_Data_Type     = typename Kernel_params::Key_Data_Type;\n    using Int_Key_Data_Type = typename Float_as_int_<Key_Data_Type>::Type;\n\n    // 4 fp16 keys or 2 fp32 keys\n    constexpr int                                         KEYS_PER_LDG = Kernel_params::KEYS_PER_LDG;\n    typedef Copy_t<Key_Data_Type, WARP_SZ * KEYS_PER_LDG> copy_t;\n    union access_t {\n        copy_t            v;\n        Int_Key_Data_Type x[KEYS_PER_LDG];  // supported size 1,2,4\n    };\n\n    constexpr int BLOCK_THREADS = Kernel_params::BLOCK_THREADS;\n\n    constexpr int ITEMS_PER_THREAD_IN_REGS = ITEMS_PER_THREAD * (1.0F - SMEM_FRACTION);\n    constexpr int ITEMS_PER_THREAD_IN_SMEM = ITEMS_PER_THREAD - ITEMS_PER_THREAD_IN_REGS;\n\n#if DO_DEBUG_PRINT == 1\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n        printf(\"ITEMS_PER_THREAD, ITEMS_PER_THREAD_IN_REGS, ITEMS_PER_THREAD_IN_SMEM = %d, %d, %d\\n\",\n               ITEMS_PER_THREAD,\n               ITEMS_PER_THREAD_IN_REGS,\n               ITEMS_PER_THREAD_IN_SMEM);\n    }\n#endif\n\n    constexpr int          MIN_KEY            = 0;\n    constexpr int          ENABLED_PER_THREAD = (ITEMS_PER_THREAD + 32 - 1) / 32;\n    extern __shared__ int2 dynamic_smem[];\n    int2*                  smem_selected_elements = dynamic_smem;\n    Int_Key_Data_Type*     smem_thread_items = reinterpret_cast<Int_Key_Data_Type*>(smem_selected_elements + MAX_TOP_K);\n\n    __shared__ unsigned int smem_selected_count;\n\n    // Specialize BlockScan type for our thread block\n    typedef cub::BlockScan<uint32_t, BLOCK_THREADS> BlockScan;\n\n    // Specialize BlockScan type for our thread block\n    typedef cub::BlockReduce<float, BLOCK_THREADS> BlockReduce;\n    __shared__ float                               smem_p_sum_total;\n\n    __shared__ union {\n        typename BlockScan::TempStorage scan;\n\n        typename BlockReduce::TempStorage reduce;\n    } temp_storage;\n    // Initialize running total\n    BlockPrefixCallbackOp prefix_op(0);\n\n    unsigned int old_selected_count;\n\n    uint32_t segment = blockIdx.y * gridDim.x + blockIdx.x;\n\n    // Preceding TopK has shortcutted this segment\n    if (params.gmem_begin_offsets[segment] == params.gmem_end_offsets[segment]) {\n        if (threadIdx.x == 0) {\n            params.gmem_active_count_per_segment[segment] = 1;\n            atomicMax(params.gmem_active_count_total, 1);\n        }\n        return;\n    }\n\n    Int_Key_Data_Type* gmem_src_keys = reinterpret_cast<Int_Key_Data_Type*>(params.gmem_src_keys);\n    Int_Key_Data_Type* gmem_dst_keys = reinterpret_cast<Int_Key_Data_Type*>(params.gmem_dst_keys);\n    int32_t*           gmem_dst_vals = reinterpret_cast<int32_t*>(params.gmem_dst_vals);\n\n    constexpr int BITS_IN_KEY = sizeof(Key_Data_Type) * 8;\n\n    int items       = params.num_items / params.num_segments;\n    int first_index = segment * items;\n    gmem_src_keys += first_index;\n    gmem_dst_keys += first_index;\n    gmem_dst_vals += first_index;\n\n    int               index_limit                            = items;\n    Int_Key_Data_Type thread_items[ITEMS_PER_THREAD_IN_REGS] = {0};\n\n    // Load all keys into registers and smem\n    const int     lane_id   = threadIdx.x % WARP_SZ;\n    const int     warp_id   = threadIdx.x / WARP_SZ;\n    constexpr int NUM_WARPS = BLOCK_THREADS / WARP_SZ;\n\n    access_t ZERO;\n    for (int i = 0; i < KEYS_PER_LDG; i++) {\n        ZERO.x[i] = MIN_KEY;\n    }\n\n    // registers\n    for (int iter = 0; iter < ITEMS_PER_THREAD_IN_REGS; iter++) {\n        int offset         = (iter + threadIdx.x * ITEMS_PER_THREAD);\n        thread_items[iter] = (offset < index_limit) ? gmem_src_keys[offset] : MIN_KEY;\n    }\n\n    // shared memory\n    for (int c = warp_id; c < BLOCK_THREADS; c += NUM_WARPS) {\n        for (int iter = lane_id * KEYS_PER_LDG; iter < ITEMS_PER_THREAD_IN_SMEM; iter += WARP_SZ * KEYS_PER_LDG) {\n            int      offset = iter + c * ITEMS_PER_THREAD + ITEMS_PER_THREAD_IN_REGS;\n            access_t val;\n            val.v = (offset < index_limit) ? *reinterpret_cast<copy_t*>(&gmem_src_keys[offset]) : ZERO.v;\n            for (int i = 0; i < KEYS_PER_LDG; i++) {\n                smem_thread_items[c + (iter + i) * BLOCK_THREADS] = val.x[i];\n            }\n            // smem_thread_items[c  + iter * BLOCK_THREADS] = (offset < index_limit)? gmem_src_keys[offset] : MIN_KEY;\n        }\n    }\n\n    Int_Key_Data_Type select_mask = 0;\n    Int_Key_Data_Type save_mask   = 0;\n\n    // Int_Key_Data_Type save_bit = 0;\n    // set to true when we finish with too few keys, so we go back to last_save_mask one more time\n    bool is_last_iter = false;\n\n    if (threadIdx.x == 0) {\n        smem_selected_count = 0;\n        old_selected_count  = 0;\n    }\n\n    // iterate over bits.\n    // skip the first two bits,\n    // * bit 31 is the sign bit. all values are positive\n    // * bit 30 is only set for values >= 2, but the input consists only of values in\n    // the range of [0,1]\n    constexpr int               START_BIT = BITS_IN_KEY - 1;\n    constexpr int               SKIP_BITS = 2;\n    constexpr Int_Key_Data_Type ONE       = (Int_Key_Data_Type)1;\n    uint32_t                    selected;\n    uint32_t                    sc;\n    float                       p_sum_total     = 0.0F;\n    float                       old_p_sum_total = 0.0F;\n    uint32_t                    offset          = 0;\n    for (Int_Key_Data_Type bit = START_BIT - SKIP_BITS; true; --bit) {\n        __syncthreads();\n        Int_Key_Data_Type bit_mask = select_mask | (ONE << bit);\n\n        uint32_t enabled[ENABLED_PER_THREAD] = {0};\n        float    thread_sum                  = 0.0F;\n\n        for (int item = 0; item < ITEMS_PER_THREAD_IN_REGS; ++item) {\n            // check if all the bits from bit mask are contained in the thread_item. If yes, set respective\n            // bit of enabled\n            auto     val        = thread_items[item];\n            uint32_t is_enabled = uint32_t(((val ^ bit_mask) & bit_mask) == 0);\n            // thread_sum += (is_enabled)? to_float(val) : 0.0F;\n            thread_sum += is_enabled * to_float(val);\n            enabled[item / 32] |= is_enabled << (item % 32);\n        }\n\n        for (int item = 0; item < ITEMS_PER_THREAD_IN_SMEM; ++item) {\n            int idx = threadIdx.x + item * BLOCK_THREADS;\n            // int idx = item + ITEMS_PER_THREAD_IN_SMEM * threadIdx.x;\n            auto     val        = smem_thread_items[idx];\n            uint32_t is_enabled = uint32_t(((val ^ bit_mask) & bit_mask) == 0);\n            // thread_sum += (is_enabled)? to_float(val) : 0.0F;\n            thread_sum += is_enabled * to_float(val);\n            enabled[(ITEMS_PER_THREAD_IN_REGS + item) / 32] |= is_enabled << ((ITEMS_PER_THREAD_IN_REGS + item) % 32);\n        }\n\n        selected = 0;\n#pragma unroll\n        for (int i = 0; i < ENABLED_PER_THREAD; i++) {\n            selected += __popc(enabled[i]);\n        }\n\n        float p_sum = BlockReduce(temp_storage.reduce).Sum(thread_sum);\n\n        if (threadIdx.x == 0) {\n            p_sum_total += p_sum;\n            smem_p_sum_total = p_sum_total;\n        }\n\n        __syncthreads();\n        p_sum_total = smem_p_sum_total;\n        __syncthreads();\n\n        BlockScan(temp_storage.scan).ExclusiveSum(selected, offset, prefix_op);\n\n        if (threadIdx.x == 0) {\n            smem_selected_count = prefix_op.running_total;\n        }\n\n        __syncthreads();\n        sc = smem_selected_count;\n        __syncthreads();\n\n        // float p_diff = params.top_p - p_sum_total;\n        float p_diff = p_sum_total - params.top_p;\n\n        if ((p_sum_total <= params.top_p + P_EPSILON && p_sum_total > 0)\n            || (p_sum_total > params.top_p && sc <= MAX_TOP_K) || (bit == 0 && p_sum_total > 0) || is_last_iter) {\n\n#if DO_DEBUG_PRINT == 1\n            __syncthreads();\n            if (threadIdx.x == 0 && blockIdx.x == debug_block_id) {\n                sc = smem_selected_count;\n                printf(\"bit %d bit_mask %d offset %d (%d, %d), sc = %d, p_sum = %f, p_sum_total = %f\\n\",\n                       bit,\n                       bit_mask,\n                       offset,\n                       blockIdx.x,\n                       threadIdx.x,\n                       sc,\n                       p_sum,\n                       p_sum_total);\n            }\n            __syncthreads();\n#endif\n\n            for (int item = 0; item < ITEMS_PER_THREAD_IN_REGS; ++item) {\n                // last condition should not trigger with well trained weights, but we will get\n                // illegal mewmory access if we do not have one in those rare cases\n                if (enabled[item / 32] & (ONE << (item % 32)) && offset < MAX_TOP_K) {\n                    smem_selected_elements[offset] =\n                        make_int2(thread_items[item], item + threadIdx.x * ITEMS_PER_THREAD);\n                    ++offset;\n                    thread_items[item] = MIN_KEY;\n                }\n            }\n\n            for (int item = 0; item < ITEMS_PER_THREAD_IN_SMEM; ++item) {\n                if (enabled[(item + ITEMS_PER_THREAD_IN_REGS) / 32] & (ONE << ((item + ITEMS_PER_THREAD_IN_REGS) % 32))\n                    && offset < MAX_TOP_K) {\n                    int idx = threadIdx.x + item * BLOCK_THREADS;\n                    // int idx = item + ITEMS_PER_THREAD_IN_SMEM * threadIdx.x;\n                    // if (idx <  params.num_items_per_segment_in_smem)\n                    {\n                        smem_selected_elements[offset] = make_int2(\n                            smem_thread_items[idx], item + threadIdx.x * ITEMS_PER_THREAD + ITEMS_PER_THREAD_IN_REGS);\n                        ++offset;\n                        smem_thread_items[idx] = MIN_KEY;\n                    }\n                }\n            }\n        }\n\n#if DO_DEBUG_PRINT == 1\n        if (threadIdx.x == 0 && blockIdx.x == debug_block_id) {\n            printf(\"!!!! bit %d bit_mask %d offset %d (%d, %d), sc = %d, p_sum = %f, p_sum_total = %f\\n\",\n                   bit,\n                   bit_mask,\n                   offset,\n                   blockIdx.x,\n                   threadIdx.x,\n                   sc,\n                   p_sum,\n                   p_sum_total);\n        }\n#endif\n\n        if (p_diff <= P_EPSILON && p_diff >= 0 || (p_sum_total > params.top_p && sc <= MAX_TOP_K) || bit == 0) {\n\n            break;\n        }\n        // p > top_p\n        else if (p_diff > P_EPSILON) {\n            // There are too many bits in the current selection\n            // Save the current state and go to the next bit\n            // If there are not enough items left using the next bit\n            // it's necessary to restart here with the current bit not set\n            save_mask = bit_mask;\n            select_mask |= bit_mask;\n\n            if (threadIdx.x == 0) {\n                smem_selected_count = old_selected_count;\n                p_sum_total         = old_p_sum_total;\n\n                prefix_op.running_total = old_selected_count;\n            }\n        }\n        else {\n            // sc < num_top_k branch\n            if (save_mask) {\n                select_mask = save_mask;\n\n                save_mask = 0;\n            }\n            if (threadIdx.x == 0) {\n                old_selected_count = smem_selected_count;\n                old_p_sum_total    = p_sum_total;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // store data to global memory\n    sc = (p_sum_total < params.top_p) ? params.num_items / params.num_segments : smem_selected_count;\n    if (threadIdx.x == 0) {\n        params.gmem_active_count_per_segment[segment] = sc;\n        atomicMax(params.gmem_active_count_total, sc);\n    }\n    if (sc >= MAX_TOP_K) {\n        return;\n    }\n    for (int i = threadIdx.x; i < sc; i += blockDim.x) {\n        int2 selected_element = smem_selected_elements[i];\n        gmem_dst_keys[i]      = selected_element.x;\n        gmem_dst_vals[i]      = selected_element.y;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nint getSmemSizeAndCheck(const TopKPerSegmentContext& context, const TopKPerSegmentParams& params)\n{\n    constexpr int BLOCK_THREADS         = Kernel_params::BLOCK_THREADS;\n    using Key_Data_Type                 = typename Kernel_params::Key_Data_Type;\n    int           num_items_per_segment = params.num_items / params.num_segments;\n    constexpr int ITEMS_INCREMENT       = Kernel_params::ITEMS_INCREMENT;\n    int           kernel_index          = div_up(num_items_per_segment, BLOCK_THREADS * ITEMS_INCREMENT) - 1;\n\n    int       smem_size                = MAX_TOP_K * sizeof(int2);\n    const int items_per_thread         = (kernel_index + 1) * ITEMS_INCREMENT;\n    const int items_per_thread_in_regs = items_per_thread * (1.0F - SMEM_FRACTION);\n    const int items_per_thread_in_smem = items_per_thread - items_per_thread_in_regs;\n\n    smem_size += items_per_thread_in_smem * BLOCK_THREADS * sizeof(typename Float_as_int_<Key_Data_Type>::Type);\n\n    int keys_per_ldg = 2 * sizeof(Key_Data_Type) / 2;\n    if (smem_size + BLOCK_THREADS * sizeof(float) > (size_t)context.sm_shared_size ||  // dynamic + static memory\n        items_per_thread_in_regs + items_per_thread_in_smem != items_per_thread || params.top_p + P_EPSILON > 1.0F\n        || items_per_thread_in_regs % keys_per_ldg != 0 || items_per_thread_in_smem % keys_per_ldg != 0\n        || num_items_per_segment % keys_per_ldg != 0) {\n        return -1;\n    }\n\n    return smem_size;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint getSmemSizeAndCheck(const TopKPerSegmentContext& context,\n                        const TopKPerSegmentParams&  params,\n                        const DType_t                DT_SCORE)\n{\n    int num_items_per_segment = params.num_items / params.num_segments;\n    if (DT_SCORE == kFLOAT) {\n        if (num_items_per_segment % 2 == 0) {\n            return getSmemSizeAndCheck<kernel_params_float>(context, params);\n        }\n        else {\n            return getSmemSizeAndCheck<kernel_params_float_1>(context, params);\n        }\n    }\n    else {\n        if (num_items_per_segment % 4 == 0) {\n            return getSmemSizeAndCheck<kernel_params_half>(context, params);\n        }\n        else {\n            return getSmemSizeAndCheck<kernel_params_half_1>(context, params);\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nvoid segmentedTopPSinglePass_dispatch(const TopKPerSegmentParams&  params,\n                                      const TopKPerSegmentContext& context,\n                                      cudaStream_t                 stream)\n{\n\n    constexpr int BLOCK_THREADS = Kernel_params::BLOCK_THREADS;\n    using Key_Data_Type         = typename Kernel_params::Key_Data_Type;\n    using Value_Data_Type       = typename Kernel_params::Value_Data_Type;\n\n    int num_items_per_segment = params.num_items / params.num_segments;\n\n    constexpr int ITEMS_INCREMENT = Kernel_params::ITEMS_INCREMENT;\n    int           kernel_index    = div_up(num_items_per_segment, BLOCK_THREADS * ITEMS_INCREMENT) - 1;\n\n#define KERNEL_RUN(INDEX)                                                                                              \\\n    {                                                                                                                  \\\n        if (smem_size > 0)                                                                                             \\\n            check_cuda_error(                                                                                          \\\n                cudaFuncSetAttribute(segmented_top_p_single_pass<Kernel_params, ITEMS_INCREMENT*(INDEX + 1)>,          \\\n                                     cudaFuncAttributeMaxDynamicSharedMemorySize,                                      \\\n                                     smem_size));                                                                      \\\n        segmented_top_p_single_pass<Kernel_params, ITEMS_INCREMENT*(INDEX + 1)>                                        \\\n            <<<grid_dim, Kernel_params::BLOCK_THREADS, smem_size, stream>>>(params);                                   \\\n    }\n\n    int smem_size = getSmemSizeAndCheck<Kernel_params>(context, params);\n\n    dim3 grid_dim(params.num_segments, 1);\n\n    switch (kernel_index) {\n        case 0:\n            KERNEL_RUN(0) break;\n        case 1:\n            KERNEL_RUN(1) break;\n        case 2:\n            KERNEL_RUN(2) break;\n        case 3:\n            KERNEL_RUN(3) break;\n        case 4:\n            KERNEL_RUN(4) break;\n        case 5:\n            KERNEL_RUN(5) break;\n        case 6:\n            KERNEL_RUN(6) break;\n        case 7:\n            KERNEL_RUN(7) break;\n        default:\n            exit(1);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nvoid topPPerSegment_dispatch(const TopKPerSegmentContext& context,\n                             TopKPerSegmentParams&        params,\n                             void*                        temp_storage,\n                             size_t&                      temp_storage_bytes,\n                             cudaStream_t                 stream)\n{\n\n    using Key_Data_Type   = typename Kernel_params::Key_Data_Type;\n    using Value_Data_Type = typename Kernel_params::Value_Data_Type;\n\n    if (temp_storage == nullptr) {\n        if (params.num_segments > 1) {\n            cub::DeviceSegmentedRadixSort::SortPairsDescending(temp_storage,\n                                                               temp_storage_bytes,\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                               params.num_items,\n                                                               params.num_segments,\n                                                               params.gmem_begin_offsets,\n                                                               params.gmem_end_offsets,\n                                                               0,\n                                                               sizeof(Key_Data_Type) * 8,\n                                                               stream);\n        }\n        else {\n            cub::DeviceRadixSort::SortPairsDescending(temp_storage,\n                                                      temp_storage_bytes,\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                      params.num_items,\n                                                      0,\n                                                      sizeof(Key_Data_Type) * 8,\n                                                      stream);\n        }\n        temp_storage_bytes = div_up(temp_storage_bytes, 256) * 256;\n        // total active counts\n        temp_storage_bytes += div_up(sizeof(int), 256) * 256;\n        // storage for gmem_end_offsets\n        temp_storage_bytes += div_up(sizeof(int) * params.num_segments, 256) * 256;\n        return;\n    }\n\n    size_t cub_temp_storage_bytes =\n        temp_storage_bytes - div_up(sizeof(int), 256) * 256 - div_up(sizeof(int) * params.num_segments, 256) * 256;\n    void* cub_temp_storage         = temp_storage;\n    params.gmem_active_count_total = reinterpret_cast<int*>((char*)temp_storage + cub_temp_storage_bytes);\n    params.gmem_active_count_per_segment =\n        reinterpret_cast<int*>((char*)params.gmem_active_count_total + div_up(sizeof(int), 256) * 256);\n\n    int num_items_per_segment = params.num_items / params.num_segments;\n\n    cudaMemsetAsync(params.gmem_active_count_total, 0, sizeof(int), stream);\n    cudaMemsetAsync(params.gmem_dst_keys, 0, params.num_items * sizeof(Key_Data_Type), stream);\n    segmentedTopPSinglePass_dispatch<Kernel_params>(params, context, stream);\n\n    int max_num_items = 0;\n    cudaMemcpyAsync(&max_num_items, params.gmem_active_count_total, sizeof(int), cudaMemcpyDeviceToHost, stream);\n\n    cudaStreamSynchronize(stream);\n\n    if (max_num_items >= MAX_TOP_K || max_num_items == 0) {\n        if (params.num_segments > 1) {\n            cub::DeviceSegmentedRadixSort::SortPairsDescending(cub_temp_storage,\n                                                               cub_temp_storage_bytes,\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                               params.num_items,\n                                                               params.num_segments,\n                                                               params.gmem_begin_offsets,\n                                                               params.gmem_end_offsets,\n                                                               0,\n                                                               sizeof(Key_Data_Type) * 8,\n                                                               stream);\n        }\n        else {\n            cub::DeviceRadixSort::SortPairsDescending(cub_temp_storage,\n                                                      cub_temp_storage_bytes,\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                      params.num_items,\n                                                      0,\n                                                      sizeof(Key_Data_Type) * 8,\n                                                      stream);\n        }\n    }\n    else {\n        // run at max supported value\n        blockSort<Key_Data_Type>((const Key_Data_Type*)(params.gmem_dst_keys),\n                                 (Key_Data_Type*)(params.gmem_dst_keys),\n                                 (const Value_Data_Type*)(params.gmem_dst_vals),\n                                 (Value_Data_Type*)(params.gmem_dst_vals),\n                                 params.gmem_active_count_per_segment,\n                                 max_num_items,\n                                 num_items_per_segment,\n                                 params.num_segments,\n                                 stream);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint topPPerSegment(const TopKPerSegmentContext& context,\n                   TopKPerSegmentParams&        params,\n                   const DType_t                DT_SCORE,\n                   void*                        temp_storage,\n                   size_t&                      temp_storage_bytes,\n                   cudaStream_t                 stream)\n{\n    int num_items_per_segment = params.num_items / params.num_segments;\n    if (DT_SCORE == kFLOAT) {\n        if (num_items_per_segment % 2 == 0) {\n            topPPerSegment_dispatch<kernel_params_float>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n        else {\n            topPPerSegment_dispatch<kernel_params_float_1>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n    }\n    else {\n        if (num_items_per_segment % 4 == 0) {\n            topPPerSegment_dispatch<kernel_params_half>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n        else {\n            topPPerSegment_dispatch<kernel_params_half_1>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n    }\n\n    return 0;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n}  // namespace segmented_topp_impl\n\n__global__ void topPInitialize(\n    int* topp_id_val_buf, int* topp_offset_buf, int* begin_topp_offset_buf_, const int batch_size, const int n)\n{\n    int tid = threadIdx.x;\n    int bid = blockIdx.x;\n\n    if (bid == 0) {\n        for (int i = tid; i < batch_size + 1; i += blockDim.x) {\n            topp_offset_buf[i]        = i * n;\n            begin_topp_offset_buf_[i] = topp_offset_buf[i];\n        }\n    }\n\n    int index = tid + bid * blockDim.x;\n\n    while (index < batch_size * n) {\n        topp_id_val_buf[index] = index % n;\n        index += blockDim.x * gridDim.x;\n    }\n}\n\nvoid invokeTopPInitialize(int*         topp_id_val_buf,\n                          int*         topp_offset_buf,\n                          int*         begin_topp_offset_buf_,\n                          const size_t batch_size,\n                          const int    n,\n                          cudaStream_t stream)\n{\n    // n: the column number of logits_buffer for top_p sampling\n    topPInitialize<<<32, 512, 0, stream>>>(topp_id_val_buf, topp_offset_buf, begin_topp_offset_buf_, batch_size, n);\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void topp_beam_topk_kernel(const T*     log_probs,  // prob.\n                                                                          int*         topk_tmp_id_buf,\n                                                                          T*           topk_tmp_val_buf,\n                                                                          const int    vocab_size,\n                                                                          int*         offset_buf,\n                                                                          int*         begin_offset_buf,\n                                                                          const float  top_p,\n                                                                          const float* top_ps,\n                                                                          const bool*  skip_decode)\n{\n    int thread_id = threadIdx.x;\n    int batch_id  = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n    float p_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n\n    typedef cub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n    TopK<T, MAX_K>                                             partial;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n#pragma unroll\n    for (int elem_id = thread_id; elem_id < vocab_size; elem_id += THREADBLOCK_SIZE) {\n        int index = elem_id + batch_id * vocab_size;\n        partial.insert(log_probs[index], index);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        begin_offset_buf[batch_id] = offset_buf[batch_id];\n        T sum_prob                 = (T)(0.0f);\n\n#pragma unroll\n        for (int i = 0; i < MAX_K; i++) {\n            sum_prob += total.u[i];\n        }\n\n        if ((float)sum_prob >= p_threshold) {\n            begin_offset_buf[batch_id] += vocab_size;\n            int index = batch_id * vocab_size;\n\n#pragma unroll\n            for (int i = 0; i < MAX_K; ++i) {\n                topk_tmp_id_buf[index + i]  = total.p[i] % vocab_size;\n                topk_tmp_val_buf[index + i] = total.u[i];\n            }\n        }\n    }\n}\n\nstruct BlockPrefixCallbackOp {\n    // Running prefix\n    float running_total;\n    // Constructor\n    __device__ BlockPrefixCallbackOp(float running_total): running_total(running_total) {}\n    // Callback operator to be entered by the first warp of threads in the block.\n    // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n    __device__ float operator()(float block_aggregate)\n    {\n        float old_prefix = running_total;\n        running_total += block_aggregate;\n        return old_prefix;\n    }\n};\n\ntemplate<typename T, int BLOCK_SIZE>\n__global__ void topp_sampling(T*             sorted_log_probs,\n                              int*           sorted_id_vals,\n                              int*           ids,\n                              int*           sequence_length,\n                              bool*          finished_buf,\n                              float*         cum_log_probs,\n                              float*         output_log_probs,\n                              const int*     begin_offset_buf,\n                              const int*     offset_buf,\n                              const int      vocab_size,\n                              curandState_t* curandstate,\n                              const float    top_p,\n                              const float*   top_ps,\n                              const int*     end_ids,\n                              const int      batch_size,\n                              const bool*    skip_decode)\n{\n    __shared__ int   stop_shared;\n    __shared__ float rand_num_s;\n\n    const int tid      = threadIdx.x;\n    const int batch_id = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n\n    constexpr int WARP_SIZE      = 32;\n    constexpr int NUM_WARPS      = BLOCK_SIZE / WARP_SIZE;\n    const int     lane_id        = threadIdx.x % WARP_SIZE;\n    const int     warp_id        = threadIdx.x / WARP_SIZE;\n    const float   prob_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n\n    if (threadIdx.x == 0) {\n        stop_shared = 0;\n        rand_num_s  = curand_uniform(curandstate + blockIdx.x) * prob_threshold;\n    }\n\n    // if begin_offset_buf and offset_buf of sorting have same value,\n    // this means that we have find best one in beam_topK_kernel_for_topP\n    // and skip the sorting. So, we can skip then during sampling.\n    if (begin_offset_buf[batch_id] == offset_buf[batch_id]) {\n        if (tid == 0) {\n            int offset    = batch_id * vocab_size;\n            ids[batch_id] = sorted_id_vals[offset];\n\n            if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                float lprob = logf(sorted_log_probs[offset]);\n                if (cum_log_probs != nullptr) {\n                    cum_log_probs[batch_id] += lprob;\n                }\n                if (output_log_probs != nullptr) {\n                    output_log_probs[batch_id] = lprob;\n                }\n            }\n            if (sequence_length != nullptr && finished_buf != nullptr) {\n                sequence_length[batch_id] =\n                    finished_buf[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n                finished_buf[batch_id] = ids[batch_id] == end_ids[batch_id] ? 1 : 0;\n            }\n        }\n        return;\n    }\n\n    typedef cub::BlockScan<float, BLOCK_SIZE>  BlockScan;\n    __shared__ typename BlockScan::TempStorage temp_storage;\n    __shared__ uint32_t                        selected_shared[NUM_WARPS];\n    // Initialize running total\n    BlockPrefixCallbackOp prefix_op(0);\n\n    if (lane_id == 0) {\n        selected_shared[warp_id] = 0;\n    }\n\n    __syncthreads();\n\n    int offset          = batch_id * vocab_size;\n    ids[batch_id]       = sorted_id_vals[offset];\n    int   end           = ((vocab_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;\n    int   i_active      = 0;\n    float thread_offset = 0;\n    for (int i = tid; i < end; i += BLOCK_SIZE) {\n        float thread_count = (i < vocab_size) ? (float)sorted_log_probs[offset + i] : 0.f;\n        BlockScan(temp_storage).InclusiveSum(thread_count, thread_offset, prefix_op);\n\n        uint32_t active_mask = __ballot_sync(0xFFFFFFFF, rand_num_s <= thread_offset);\n\n        i_active = i;\n        if (active_mask != 0) {\n            if (lane_id == 0) {\n                atomicAdd(&stop_shared, 1);\n                selected_shared[warp_id] = active_mask;\n            }\n        }\n        __syncthreads();\n        if (stop_shared > 0) {\n            break;\n        }\n    };\n\n    // select first active warp\n    bool skip = (selected_shared[warp_id] > 0) ? false : true;\n    for (int i = 0; i < warp_id; i++) {\n        if (selected_shared[i] != 0) {\n            skip = true;\n        }\n    }\n    if (!skip) {\n        int active_lane_id = WARP_SIZE - __popc(selected_shared[warp_id]);\n        if (lane_id == active_lane_id) {\n            ids[batch_id] = sorted_id_vals[offset + i_active];\n            if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                float lprob = logf(sorted_log_probs[offset + i_active]);\n                if (cum_log_probs != nullptr) {\n                    cum_log_probs[batch_id] += lprob;\n                }\n                if (output_log_probs != nullptr) {\n                    output_log_probs[batch_id] = lprob;\n                }\n            }\n            if (sequence_length != nullptr && finished_buf != nullptr) {\n                sequence_length[batch_id] =\n                    finished_buf[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n                finished_buf[batch_id] = ids[batch_id] == end_ids[batch_id] ? 1 : 0;\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchTopPSampling(void*           workspace,\n                             size_t&         workspace_size,\n                             size_t&         cub_temp_storage_size,\n                             int*            output_ids,\n                             int*            sequence_length,\n                             bool*           finished_buf,\n                             float*          cum_log_probs,\n                             float*          output_log_probs,\n                             const T*        log_probs,\n                             const int*      id_vals,\n                             int*            offset_buf,\n                             int*            begin_offset_buf,\n                             curandState_t*  curandstate,\n                             const int       batch_size,\n                             const size_t    vocab_size_padded,\n                             const int*      end_ids,\n                             const float     max_top_p,\n                             const float*    top_ps,\n                             cudaStream_t    stream,\n                             cudaDeviceProp* cuda_device_prop,\n                             const bool*     skip_decode)\n{\n    // Here, we put batch size as an argument because the batch size of initialization\n    // and inference may be different due to pipeline parallelism.\n    const int vocab_size = vocab_size_padded;\n    const int block_size = 256;\n\n    size_t sorted_log_prob_buf_size = batch_size * vocab_size * sizeof(T);    // type T\n    size_t sorted_id_vals_buf_size  = batch_size * vocab_size * sizeof(int);  // type int\n    sorted_log_prob_buf_size        = div_up(sorted_log_prob_buf_size, 256) * 256;\n    sorted_id_vals_buf_size         = div_up(sorted_id_vals_buf_size, 256) * 256;\n\n    void* cub_temp_storage = workspace;\n    T*    sorted_log_probs = (T*)((char*)cub_temp_storage + cub_temp_storage_size);\n    int*  sorted_id_vals   = (int*)((char*)sorted_log_probs + sorted_log_prob_buf_size);\n\n    bool do_radix_sort = (ENABLE_SINGLE_PASS_TOP_P == 0 || max_top_p >= SINGLE_PASS_THRESHOLD);\n    int  smem_size     = -1;\n\n    segmented_topp_impl::TopKPerSegmentContext context;\n    segmented_topp_impl::TopKPerSegmentParams  params;\n    segmented_topp_impl::DType_t               dataTypeKind =\n        (std::is_same<T, float>::value) ? segmented_topp_impl::kFLOAT : segmented_topp_impl::kHALF;\n\n    if (!do_radix_sort) {\n        FT_CHECK(cuda_device_prop != nullptr);\n        memset(&context, 0, sizeof(context));\n        context.sm_count       = cuda_device_prop->multiProcessorCount;\n        context.sm_shared_size = cuda_device_prop->sharedMemPerMultiprocessor;\n        context.sm_version     = cuda_device_prop->major * 100 + cuda_device_prop->minor * 10;\n\n        memset(&params, 0, sizeof(params));\n        params.gmem_src_keys        = reinterpret_cast<void*>(const_cast<T*>(log_probs));\n        params.gmem_dst_keys        = sorted_log_probs;\n        params.gmem_src_vals        = reinterpret_cast<void*>(const_cast<int*>(id_vals));\n        params.gmem_dst_vals        = reinterpret_cast<void*>(sorted_id_vals);\n        params.gmem_begin_offsets   = begin_offset_buf;\n        params.gmem_end_offsets     = offset_buf + 1;\n        params.workspace            = nullptr;\n        params.num_items            = vocab_size * batch_size;\n        params.num_segments         = batch_size;\n        params.top_p                = max_top_p;\n        params.confidence_threshold = 0.0F;\n\n        smem_size     = getSmemSizeAndCheck(context, params, dataTypeKind);\n        do_radix_sort = smem_size < 0;\n    }\n\n    if (do_radix_sort) {\n        if (workspace == nullptr) {\n            check_cuda_error(\n                cub::DeviceSegmentedRadixSort::SortPairsDescending(nullptr,\n                                                                   cub_temp_storage_size,\n                                                                   log_probs,\n                                                                   (T*)nullptr,\n                                                                   id_vals,\n                                                                   (int*)nullptr,\n                                                                   vocab_size * batch_size,\n                                                                   batch_size,\n                                                                   begin_offset_buf,\n                                                                   offset_buf + 1,\n                                                                   0,              // begin_bit\n                                                                   sizeof(T) * 8,  // end_bit = sizeof(KeyT) * 8\n                                                                   stream));       // cudaStream_t\n            cub_temp_storage_size = div_up(cub_temp_storage_size, 256) * 256;\n            workspace_size        = sorted_log_prob_buf_size + sorted_id_vals_buf_size + cub_temp_storage_size;\n            return;\n        }\n\n        topp_beam_topk_kernel<T, 1, block_size><<<batch_size, block_size, 0, stream>>>(log_probs,\n                                                                                       sorted_id_vals,\n                                                                                       sorted_log_probs,\n                                                                                       vocab_size,\n                                                                                       offset_buf,\n                                                                                       begin_offset_buf,\n                                                                                       max_top_p,\n                                                                                       top_ps,\n                                                                                       skip_decode);\n\n        check_cuda_error(\n            cub::DeviceSegmentedRadixSort::SortPairsDescending(cub_temp_storage,\n                                                               cub_temp_storage_size,\n                                                               log_probs,\n                                                               sorted_log_probs,\n                                                               id_vals,\n                                                               sorted_id_vals,\n                                                               vocab_size * batch_size,\n                                                               batch_size,\n                                                               begin_offset_buf,\n                                                               offset_buf + 1,\n                                                               0,              // begin_bit\n                                                               sizeof(T) * 8,  // end_bit = sizeof(KeyT) * 8\n                                                               stream));       // cudaStream_t\n    }\n    else {\n        if (workspace == nullptr) {\n            segmented_topp_impl::topPPerSegment(\n                context, params, dataTypeKind, cub_temp_storage, cub_temp_storage_size, stream);\n            workspace_size = sorted_log_prob_buf_size + sorted_id_vals_buf_size + cub_temp_storage_size;\n            return;\n        }\n        else {\n            topp_beam_topk_kernel<T, 1, block_size><<<batch_size, block_size, 0, stream>>>(log_probs,\n                                                                                           sorted_id_vals,\n                                                                                           sorted_log_probs,\n                                                                                           vocab_size,\n                                                                                           offset_buf,\n                                                                                           begin_offset_buf,\n                                                                                           max_top_p,\n                                                                                           top_ps,\n                                                                                           skip_decode);\n            segmented_topp_impl::topPPerSegment(\n                context, params, dataTypeKind, cub_temp_storage, cub_temp_storage_size, stream);\n        }\n    }\n\n    constexpr int SAMPLING_BLOCK_SIZE = 256;\n    dim3          grid(batch_size);\n    topp_sampling<T, SAMPLING_BLOCK_SIZE><<<grid, SAMPLING_BLOCK_SIZE, 0, stream>>>(sorted_log_probs,\n                                                                                    sorted_id_vals,\n                                                                                    output_ids,\n                                                                                    sequence_length,\n                                                                                    finished_buf,\n                                                                                    cum_log_probs,\n                                                                                    output_log_probs,\n                                                                                    begin_offset_buf,\n                                                                                    offset_buf + 1,\n                                                                                    vocab_size,\n                                                                                    curandstate,\n                                                                                    max_top_p,\n                                                                                    top_ps,\n                                                                                    end_ids,\n                                                                                    batch_size,\n                                                                                    skip_decode);\n}\n\ntemplate void invokeBatchTopPSampling(void*           workspace,\n                                      size_t&         workspace_size,\n                                      size_t&         cub_temp_storage_size,\n                                      int*            output_ids,\n                                      int*            sequence_length,\n                                      bool*           finished_buf,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      const float*    log_probs,\n                                      const int*      id_vals,\n                                      int*            offset_buf,\n                                      int*            begin_offset_buf,\n                                      curandState_t*  curandstate,\n                                      const int       batch_size,\n                                      const size_t    vocab_size_padded,\n                                      const int*      end_ids,\n                                      const float     max_top_p,\n                                      const float*    top_ps,\n                                      cudaStream_t    stream,\n                                      cudaDeviceProp* cuda_device_prop,\n                                      const bool*     skip_decode);\n\ntemplate void invokeBatchTopPSampling(void*           workspace,\n                                      size_t&         workspace_size,\n                                      size_t&         cub_temp_storage_size,\n                                      int*            output_ids,\n                                      int*            sequence_length,\n                                      bool*           finished_buf,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      const half*     log_probs,\n                                      const int*      id_vals,\n                                      int*            offset_buf,\n                                      int*            begin_offset_buf,\n                                      curandState_t*  curandstate,\n                                      const int       batch_size,\n                                      const size_t    vocab_size_padded,\n                                      const int*      end_ids,\n                                      const float     max_top_p,\n                                      const float*    top_ps,\n                                      cudaStream_t    stream,\n                                      cudaDeviceProp* cuda_device_prop,\n                                      const bool*     skip_decode);\n\ntemplate<typename T>\nvoid invokeTopPSampling(void*           workspace,\n                        size_t&         workspace_size,\n                        size_t&         cub_temp_storage_size,\n                        int*            output_ids,\n                        int*            sequence_length,\n                        bool*           finished_buf,\n                        float*          cum_log_probs,\n                        float*          output_log_probs,\n                        const T*        log_probs,\n                        const int*      id_vals,\n                        int*            offset_buf,\n                        int*            begin_offset_buf,\n                        curandState_t*  curandstate,\n                        const int       batch_size,\n                        const size_t    vocab_size_padded,\n                        const int*      end_ids,\n                        const float     top_p,\n                        cudaStream_t    stream,\n                        cudaDeviceProp* cuda_device_prop,\n                        const bool*     skip_decode)\n{\n    invokeBatchTopPSampling(workspace,\n                            workspace_size,\n                            cub_temp_storage_size,\n                            output_ids,\n                            sequence_length,\n                            finished_buf,\n                            cum_log_probs,\n                            output_log_probs,\n                            log_probs,\n                            id_vals,\n                            offset_buf,\n                            begin_offset_buf,\n                            curandstate,\n                            batch_size,\n                            vocab_size_padded,\n                            end_ids,\n                            top_p,\n                            nullptr,\n                            stream,\n                            cuda_device_prop,\n                            skip_decode);\n}\n\ntemplate void invokeTopPSampling(void*           workspace,\n                                 size_t&         workspace_size,\n                                 size_t&         cub_temp_storage_size,\n                                 int*            output_ids,\n                                 int*            sequence_length,\n                                 bool*           finished_buf,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 const float*    log_probs,\n                                 const int*      id_vals,\n                                 int*            offset_buf,\n                                 int*            begin_offset_buf,\n                                 curandState_t*  curandstate,\n                                 const int       batch_size,\n                                 const size_t    vocab_size_padded,\n                                 const int*      end_ids,\n                                 const float     top_p,\n                                 cudaStream_t    stream,\n                                 cudaDeviceProp* cuda_device_prop,\n                                 const bool*     skip_decode);\n\ntemplate void invokeTopPSampling(void*           workspace,\n                                 size_t&         workspace_size,\n                                 size_t&         cub_temp_storage_size,\n                                 int*            output_ids,\n                                 int*            sequence_length,\n                                 bool*           finished_buf,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 const half*     log_probs,\n                                 const int*      id_vals,\n                                 int*            offset_buf,\n                                 int*            begin_offset_buf,\n                                 curandState_t*  curandstate,\n                                 const int       batch_size,\n                                 const size_t    vocab_size_padded,\n                                 const int*      end_ids,\n                                 const float     top_p,\n                                 cudaStream_t    stream,\n                                 cudaDeviceProp* cuda_device_prop,\n                                 const bool*     skip_decode);\n\ntemplate<typename T>\n__global__ void\naddBiasSoftMax(T* logits, const T* bias, const int* end_ids, const bool* finished, const int n_padded, const int n)\n{\n    int  bid    = blockIdx.x;\n    bool finish = (finished != nullptr) ? finished[bid] : false;\n    int  offset = bid * n_padded;\n\n    float            max_val   = -1 * FLT_MAX;\n    const bool       IS_FP16   = std::is_same<T, half>::value;\n    const T          MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    __shared__ float s_max_val;\n    __shared__ float s_sum_val;\n\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        if (tid < n) {\n            if (finish) {\n                logits[offset + tid] = (tid == end_ids[bid]) ? MAX_T_VAL : -MAX_T_VAL;\n            }\n            else {\n                T bias_val = (bias != nullptr) ? bias[tid] : (T)0.0f;\n                logits[offset + tid] += bias_val;\n            }\n        }\n        else {\n            logits[offset + tid] = -MAX_T_VAL;\n        }\n        max_val = max(max_val, (float)logits[offset + tid]);\n    }\n\n    max_val = blockReduceMax<float>((float)max_val);\n    if (threadIdx.x == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    float sum_val = 0.0f;\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        logits[offset + tid] = __expf((float)logits[offset + tid] - s_max_val);\n        sum_val += (float)logits[offset + tid];\n    }\n\n    sum_val = blockReduceSum<float>(sum_val);\n    if (threadIdx.x == 0) {\n        s_sum_val = sum_val;\n    }\n    __syncthreads();\n\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        logits[offset + tid] = ((float)logits[offset + tid] / (s_sum_val + 1e-6f));\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasSoftMax(T*           logits,\n                          const T*     bias,\n                          const int*   end_ids,\n                          const bool*  finished,\n                          const int    m,\n                          const int    n_padded,\n                          const int    n,\n                          cudaStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    addBiasSoftMax<<<grid, block, 0, stream>>>(logits, bias, end_ids, finished, n_padded, n);\n}\n\ntemplate void invokeAddBiasSoftMax(float*       logits,\n                                   const float* bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    m,\n                                   const int    n_padded,\n                                   const int    n,\n                                   cudaStream_t stream);\n\ntemplate void invokeAddBiasSoftMax(half*        logits,\n                                   const half*  bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    m,\n                                   const int    n_padded,\n                                   const int    n,\n                                   cudaStream_t stream);\n\n__global__ void computeToppDecay(float*         runtime_top_p,\n                                 const float*   runtime_initial_top_p,\n                                 const int*     output_ids,\n                                 const float*   top_p_decay,\n                                 const float*   top_p_min,\n                                 const int32_t* top_p_reset_ids,\n                                 const int      local_batch_size)\n{\n    /**\n     * @brief Compute the topp decay by https://arxiv.org/pdf/2206.04624.pdf\n     *        In short, the formula is\n     *          runtime_top_p = max(runtime_top_p * top_p_decay, top_p_min)\n     *        If generating the top_p_reset_ids, then reset the runtime_top_p.\n     *\n     * \\param runtime_top_p          [local_batch_size]\n     * \\param runtime_initial_top_p  [local_batch_size]\n     * \\param output_ids             [local_batch_size]\n     * \\param top_p_decay            [local_batch_size]\n     * \\param top_p_min              [local_batch_size]\n     * \\param top_p_reset_ids         [local_batch_size]\n     * \\param local_batch_size\n     *\n     */\n\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (output_ids[idx] == top_p_reset_ids[idx]) {\n        runtime_top_p[idx] = runtime_initial_top_p[idx];\n    }\n    else {\n        runtime_top_p[idx] = max(runtime_top_p[idx] * top_p_decay[idx], top_p_min[idx]);\n    }\n}\n\nvoid invokeComputeToppDecay(float*         runtime_top_p,\n                            const float*   runtime_initial_top_p,\n                            const int*     output_ids,\n                            const float*   top_p_decay,\n                            const float*   top_p_min,\n                            const int32_t* top_p_reset_ids,\n                            const int      local_batch_size,\n                            cudaStream_t   stream)\n{\n    dim3 block(min(local_batch_size, 512));\n    dim3 grid((local_batch_size + block.x - 1) / block.x);\n    computeToppDecay<<<grid, block, 0, stream>>>(\n        runtime_top_p, runtime_initial_top_p, output_ids, top_p_decay, top_p_min, top_p_reset_ids, local_batch_size);\n}\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#ifndef CUDART_VERSION\n#error CUDART_VERSION Undefined!\n#elif (CUDART_VERSION >= 11050)\n#include <hipcub/hipcub.hpp>\n#else\n#include \"3rdparty/hipcub/hipcub.hpp\"\n#endif\n\n#include \"src/fastertransformer/kernels/reduce_kernel_utils.cuh\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n\nconstexpr int   ENABLE_SINGLE_PASS_TOP_P = 0;\nconstexpr float SINGLE_PASS_THRESHOLD    = 0.9;\n\nnamespace fastertransformer {\n\nnamespace segmented_topp_impl {\n\ntemplate<int HALF_ELEMENTS_PER_WARP_LOAD>\nusing Copy_half_t = typename std::conditional<\n    HALF_ELEMENTS_PER_WARP_LOAD == 32,\n    half,\n    typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 64,\n                              int,\n                              typename std::conditional<HALF_ELEMENTS_PER_WARP_LOAD == 128, int2, int4>::type>::type>::\n    type;\n\ntemplate<typename T, int ELEMENTS_PER_WARP_LOAD>\nusing Copy_t = Copy_half_t<sizeof(T) / sizeof(half) * ELEMENTS_PER_WARP_LOAD>;\n\ntemplate<typename T>\nstruct Float_as_int_ {\n};\ntemplate<>\nstruct Float_as_int_<float> {\n    using Type = uint32_t;\n};\ntemplate<>\nstruct Float_as_int_<__half> {\n    using Type = uint16_t;\n};\n\nusing kernel_params_float   = Segmented_topk_kernel_params<float, int32_t, 256, 2>;\nusing kernel_params_float_1 = Segmented_topk_kernel_params<float, int32_t, 256, 1>;\nusing kernel_params_half    = Segmented_topk_kernel_params<__half, int32_t, 256, 4>;\nusing kernel_params_half_1  = Segmented_topk_kernel_params<__half, int32_t, 256, 1>;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ float to_float(uint32_t src)\n{\n    return __int_as_float(src);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstatic inline __device__ float to_float(uint16_t src)\n{\n    __half dst = __ushort_as_half(src);\n    return __half2float(dst);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// sort one segment per cta\ntemplate<typename T_SCORE, int BLOCK_THREADS, int ELEMENTS_PER_THREAD>\n__global__ void blockSortKernel(const T_SCORE* d_keys_in,\n                                T_SCORE*       d_keys_out,\n                                const int32_t* d_values_in,\n                                int32_t*       d_values_out,\n                                const int32_t* active_counts,\n                                int            num_items_,\n                                int            stride_items,\n                                int            num_segments)\n{\n    // Specialize BlockRadixSort for a 1D block\n    typedef hipcub::BlockRadixSort<T_SCORE, BLOCK_THREADS, ELEMENTS_PER_THREAD, int32_t> BlockRadixSort;\n\n    // Allocate shared memory for BlockRadixSort\n    __shared__ typename BlockRadixSort::TempStorage temp_storage;\n\n    if (blockIdx.x >= num_segments) {\n        return;\n    }\n\n    int num_items = active_counts[blockIdx.x];  // > num_items_ ? num_items_ : active_counts[blockIdx.x];\n\n    if (num_items == 0) {\n        return;\n    }\n\n    // Obtain a segment of consecutive items that are blocked across threads\n    T_SCORE thread_keys[ELEMENTS_PER_THREAD];\n    int32_t thread_values[ELEMENTS_PER_THREAD];\n\n    int32_t block_offset = blockIdx.x * stride_items;\n    hipcub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_keys_out + block_offset, thread_keys, num_items, 0);\n    hipcub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_values_out + block_offset, thread_values, num_items, -1);\n    __syncthreads();\n\n    // Collectively sort the keys and values among block threads\n    BlockRadixSort(temp_storage).SortDescendingBlockedToStriped(thread_keys, thread_values);\n\n    // Store output in striped fashion\n    hipcub::StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_keys_out + block_offset, thread_keys, num_items);\n    hipcub::StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_values_out + block_offset, thread_values, num_items);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// block sort kernel\ntemplate<typename T_SCORE>\nvoid blockSort(const T_SCORE* d_keys_in,\n               T_SCORE*       d_keys_out,\n               const int32_t* d_values_in,\n               int32_t*       d_values_out,\n               const int32_t* active_counts,\n               int            num_items,\n               int            stride_items,\n               int            num_segments,\n               hipStream_t   stream)\n{\n    if (num_items == 0) {\n        return;\n    }\n\n    int kernel_index  = div_up(num_items, 128) - 1;\n    int warps_per_cta = (kernel_index + 1) * 128 / 32;\n    if (kernel_index > 7) {\n        kernel_index  = 7 + div_up(num_items, 1024) - 1;\n        warps_per_cta = 1024 / 32;\n    }\n    assert(warps_per_cta <= 32);\n\n    dim3 block(warps_per_cta * 32);\n    dim3 grid(num_segments);\n\n    using kernel_func = void (*)(const T_SCORE* d_keys_in,\n                                 T_SCORE*       d_keys_out,\n                                 const int32_t* d_values_in,\n                                 int32_t*       d_values_out,\n                                 const int32_t* active_counts,\n                                 int            num_items,\n                                 int            stride_items,\n                                 int            num_segments);\n\n    static const kernel_func kernel_funcs[] = {\n        &blockSortKernel<T_SCORE, 128, 1>,\n        &blockSortKernel<T_SCORE, 256, 1>,\n        &blockSortKernel<T_SCORE, 384, 1>,\n        &blockSortKernel<T_SCORE, 512, 1>,\n        &blockSortKernel<T_SCORE, 640, 1>,\n        &blockSortKernel<T_SCORE, 768, 1>,\n        &blockSortKernel<T_SCORE, 896, 1>,\n        &blockSortKernel<T_SCORE, 1024, 1>,\n        &blockSortKernel<T_SCORE, 1024, 2>,\n        &blockSortKernel<T_SCORE, 1024, 4>,\n        //&blockSortKernel<T_SCORE, 1024, 6>,\n    };\n    kernel_funcs[kernel_index]<<<grid, block, 0, stream>>>(\n        d_keys_in, d_keys_out, d_values_in, d_values_out, active_counts, num_items, stride_items, num_segments);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nstruct BlockPrefixCallbackOp {\n    // Running prefix\n    int running_total;\n    // Constructor\n    __device__ BlockPrefixCallbackOp(uint32_t running_total): running_total(running_total) {}\n    // Callback operator to be entered by the first warp of threads in the block.\n    // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n    __device__ int operator()(uint32_t block_aggregate)\n    {\n        uint32_t old_prefix = running_total;\n        running_total += block_aggregate;\n        return old_prefix;\n    }\n};\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define DO_DEBUG_PRINT 0\n\n// governs the split between regs and smem\nconstexpr float SMEM_FRACTION = 0.5F;\nconstexpr float P_EPSILON     = 0.01F;\n\nconstexpr int MAX_TOP_K = 3072;\nconstexpr int WARP_SZ   = 32;\n\ntemplate<typename Kernel_params, int ITEMS_PER_THREAD>\n__global__ __launch_bounds__(Kernel_params::BLOCK_THREADS,\n                             1) void segmented_top_p_single_pass(TopKPerSegmentParams params)\n{\n#if DO_DEBUG_PRINT\n    constexpr int debug_block_id = 26;\n#endif\n\n    using Key_Data_Type     = typename Kernel_params::Key_Data_Type;\n    using Int_Key_Data_Type = typename Float_as_int_<Key_Data_Type>::Type;\n\n    // 4 fp16 keys or 2 fp32 keys\n    constexpr int                                         KEYS_PER_LDG = Kernel_params::KEYS_PER_LDG;\n    typedef Copy_t<Key_Data_Type, WARP_SZ * KEYS_PER_LDG> copy_t;\n    union access_t {\n        copy_t            v;\n        Int_Key_Data_Type x[KEYS_PER_LDG];  // supported size 1,2,4\n    };\n\n    constexpr int BLOCK_THREADS = Kernel_params::BLOCK_THREADS;\n\n    constexpr int ITEMS_PER_THREAD_IN_REGS = ITEMS_PER_THREAD * (1.0F - SMEM_FRACTION);\n    constexpr int ITEMS_PER_THREAD_IN_SMEM = ITEMS_PER_THREAD - ITEMS_PER_THREAD_IN_REGS;\n\n#if DO_DEBUG_PRINT == 1\n    if (blockIdx.x == 0 && threadIdx.x == 0) {\n        printf(\"ITEMS_PER_THREAD, ITEMS_PER_THREAD_IN_REGS, ITEMS_PER_THREAD_IN_SMEM = %d, %d, %d\\n\",\n               ITEMS_PER_THREAD,\n               ITEMS_PER_THREAD_IN_REGS,\n               ITEMS_PER_THREAD_IN_SMEM);\n    }\n#endif\n\n    constexpr int          MIN_KEY            = 0;\n    constexpr int          ENABLED_PER_THREAD = (ITEMS_PER_THREAD + 32 - 1) / 32;\n    extern __shared__ int2 dynamic_smem[];\n    int2*                  smem_selected_elements = dynamic_smem;\n    Int_Key_Data_Type*     smem_thread_items = reinterpret_cast<Int_Key_Data_Type*>(smem_selected_elements + MAX_TOP_K);\n\n    __shared__ unsigned int smem_selected_count;\n\n    // Specialize BlockScan type for our thread block\n    typedef hipcub::BlockScan<uint32_t, BLOCK_THREADS> BlockScan;\n\n    // Specialize BlockScan type for our thread block\n    typedef hipcub::BlockReduce<float, BLOCK_THREADS> BlockReduce;\n    __shared__ float                               smem_p_sum_total;\n\n    __shared__ union {\n        typename BlockScan::TempStorage scan;\n\n        typename BlockReduce::TempStorage reduce;\n    } temp_storage;\n    // Initialize running total\n    BlockPrefixCallbackOp prefix_op(0);\n\n    unsigned int old_selected_count;\n\n    uint32_t segment = blockIdx.y * gridDim.x + blockIdx.x;\n\n    // Preceding TopK has shortcutted this segment\n    if (params.gmem_begin_offsets[segment] == params.gmem_end_offsets[segment]) {\n        if (threadIdx.x == 0) {\n            params.gmem_active_count_per_segment[segment] = 1;\n            atomicMax(params.gmem_active_count_total, 1);\n        }\n        return;\n    }\n\n    Int_Key_Data_Type* gmem_src_keys = reinterpret_cast<Int_Key_Data_Type*>(params.gmem_src_keys);\n    Int_Key_Data_Type* gmem_dst_keys = reinterpret_cast<Int_Key_Data_Type*>(params.gmem_dst_keys);\n    int32_t*           gmem_dst_vals = reinterpret_cast<int32_t*>(params.gmem_dst_vals);\n\n    constexpr int BITS_IN_KEY = sizeof(Key_Data_Type) * 8;\n\n    int items       = params.num_items / params.num_segments;\n    int first_index = segment * items;\n    gmem_src_keys += first_index;\n    gmem_dst_keys += first_index;\n    gmem_dst_vals += first_index;\n\n    int               index_limit                            = items;\n    Int_Key_Data_Type thread_items[ITEMS_PER_THREAD_IN_REGS] = {0};\n\n    // Load all keys into registers and smem\n    const int     lane_id   = threadIdx.x % WARP_SZ;\n    const int     warp_id   = threadIdx.x / WARP_SZ;\n    constexpr int NUM_WARPS = BLOCK_THREADS / WARP_SZ;\n\n    access_t ZERO;\n    for (int i = 0; i < KEYS_PER_LDG; i++) {\n        ZERO.x[i] = MIN_KEY;\n    }\n\n    // registers\n    for (int iter = 0; iter < ITEMS_PER_THREAD_IN_REGS; iter++) {\n        int offset         = (iter + threadIdx.x * ITEMS_PER_THREAD);\n        thread_items[iter] = (offset < index_limit) ? gmem_src_keys[offset] : MIN_KEY;\n    }\n\n    // shared memory\n    for (int c = warp_id; c < BLOCK_THREADS; c += NUM_WARPS) {\n        for (int iter = lane_id * KEYS_PER_LDG; iter < ITEMS_PER_THREAD_IN_SMEM; iter += WARP_SZ * KEYS_PER_LDG) {\n            int      offset = iter + c * ITEMS_PER_THREAD + ITEMS_PER_THREAD_IN_REGS;\n            access_t val;\n            val.v = (offset < index_limit) ? *reinterpret_cast<copy_t*>(&gmem_src_keys[offset]) : ZERO.v;\n            for (int i = 0; i < KEYS_PER_LDG; i++) {\n                smem_thread_items[c + (iter + i) * BLOCK_THREADS] = val.x[i];\n            }\n            // smem_thread_items[c  + iter * BLOCK_THREADS] = (offset < index_limit)? gmem_src_keys[offset] : MIN_KEY;\n        }\n    }\n\n    Int_Key_Data_Type select_mask = 0;\n    Int_Key_Data_Type save_mask   = 0;\n\n    // Int_Key_Data_Type save_bit = 0;\n    // set to true when we finish with too few keys, so we go back to last_save_mask one more time\n    bool is_last_iter = false;\n\n    if (threadIdx.x == 0) {\n        smem_selected_count = 0;\n        old_selected_count  = 0;\n    }\n\n    // iterate over bits.\n    // skip the first two bits,\n    // * bit 31 is the sign bit. all values are positive\n    // * bit 30 is only set for values >= 2, but the input consists only of values in\n    // the range of [0,1]\n    constexpr int               START_BIT = BITS_IN_KEY - 1;\n    constexpr int               SKIP_BITS = 2;\n    constexpr Int_Key_Data_Type ONE       = (Int_Key_Data_Type)1;\n    uint32_t                    selected;\n    uint32_t                    sc;\n    float                       p_sum_total     = 0.0F;\n    float                       old_p_sum_total = 0.0F;\n    uint32_t                    offset          = 0;\n    for (Int_Key_Data_Type bit = START_BIT - SKIP_BITS; true; --bit) {\n        __syncthreads();\n        Int_Key_Data_Type bit_mask = select_mask | (ONE << bit);\n\n        uint32_t enabled[ENABLED_PER_THREAD] = {0};\n        float    thread_sum                  = 0.0F;\n\n        for (int item = 0; item < ITEMS_PER_THREAD_IN_REGS; ++item) {\n            // check if all the bits from bit mask are contained in the thread_item. If yes, set respective\n            // bit of enabled\n            auto     val        = thread_items[item];\n            uint32_t is_enabled = uint32_t(((val ^ bit_mask) & bit_mask) == 0);\n            // thread_sum += (is_enabled)? to_float(val) : 0.0F;\n            thread_sum += is_enabled * to_float(val);\n            enabled[item / 32] |= is_enabled << (item % 32);\n        }\n\n        for (int item = 0; item < ITEMS_PER_THREAD_IN_SMEM; ++item) {\n            int idx = threadIdx.x + item * BLOCK_THREADS;\n            // int idx = item + ITEMS_PER_THREAD_IN_SMEM * threadIdx.x;\n            auto     val        = smem_thread_items[idx];\n            uint32_t is_enabled = uint32_t(((val ^ bit_mask) & bit_mask) == 0);\n            // thread_sum += (is_enabled)? to_float(val) : 0.0F;\n            thread_sum += is_enabled * to_float(val);\n            enabled[(ITEMS_PER_THREAD_IN_REGS + item) / 32] |= is_enabled << ((ITEMS_PER_THREAD_IN_REGS + item) % 32);\n        }\n\n        selected = 0;\n#pragma unroll\n        for (int i = 0; i < ENABLED_PER_THREAD; i++) {\n            selected += __popc(enabled[i]);\n        }\n\n        float p_sum = BlockReduce(temp_storage.reduce).Sum(thread_sum);\n\n        if (threadIdx.x == 0) {\n            p_sum_total += p_sum;\n            smem_p_sum_total = p_sum_total;\n        }\n\n        __syncthreads();\n        p_sum_total = smem_p_sum_total;\n        __syncthreads();\n\n        BlockScan(temp_storage.scan).ExclusiveSum(selected, offset, prefix_op);\n\n        if (threadIdx.x == 0) {\n            smem_selected_count = prefix_op.running_total;\n        }\n\n        __syncthreads();\n        sc = smem_selected_count;\n        __syncthreads();\n\n        // float p_diff = params.top_p - p_sum_total;\n        float p_diff = p_sum_total - params.top_p;\n\n        if ((p_sum_total <= params.top_p + P_EPSILON && p_sum_total > 0)\n            || (p_sum_total > params.top_p && sc <= MAX_TOP_K) || (bit == 0 && p_sum_total > 0) || is_last_iter) {\n\n#if DO_DEBUG_PRINT == 1\n            __syncthreads();\n            if (threadIdx.x == 0 && blockIdx.x == debug_block_id) {\n                sc = smem_selected_count;\n                printf(\"bit %d bit_mask %d offset %d (%d, %d), sc = %d, p_sum = %f, p_sum_total = %f\\n\",\n                       bit,\n                       bit_mask,\n                       offset,\n                       blockIdx.x,\n                       threadIdx.x,\n                       sc,\n                       p_sum,\n                       p_sum_total);\n            }\n            __syncthreads();\n#endif\n\n            for (int item = 0; item < ITEMS_PER_THREAD_IN_REGS; ++item) {\n                // last condition should not trigger with well trained weights, but we will get\n                // illegal mewmory access if we do not have one in those rare cases\n                if (enabled[item / 32] & (ONE << (item % 32)) && offset < MAX_TOP_K) {\n                    smem_selected_elements[offset] =\n                        make_int2(thread_items[item], item + threadIdx.x * ITEMS_PER_THREAD);\n                    ++offset;\n                    thread_items[item] = MIN_KEY;\n                }\n            }\n\n            for (int item = 0; item < ITEMS_PER_THREAD_IN_SMEM; ++item) {\n                if (enabled[(item + ITEMS_PER_THREAD_IN_REGS) / 32] & (ONE << ((item + ITEMS_PER_THREAD_IN_REGS) % 32))\n                    && offset < MAX_TOP_K) {\n                    int idx = threadIdx.x + item * BLOCK_THREADS;\n                    // int idx = item + ITEMS_PER_THREAD_IN_SMEM * threadIdx.x;\n                    // if (idx <  params.num_items_per_segment_in_smem)\n                    {\n                        smem_selected_elements[offset] = make_int2(\n                            smem_thread_items[idx], item + threadIdx.x * ITEMS_PER_THREAD + ITEMS_PER_THREAD_IN_REGS);\n                        ++offset;\n                        smem_thread_items[idx] = MIN_KEY;\n                    }\n                }\n            }\n        }\n\n#if DO_DEBUG_PRINT == 1\n        if (threadIdx.x == 0 && blockIdx.x == debug_block_id) {\n            printf(\"!!!! bit %d bit_mask %d offset %d (%d, %d), sc = %d, p_sum = %f, p_sum_total = %f\\n\",\n                   bit,\n                   bit_mask,\n                   offset,\n                   blockIdx.x,\n                   threadIdx.x,\n                   sc,\n                   p_sum,\n                   p_sum_total);\n        }\n#endif\n\n        if (p_diff <= P_EPSILON && p_diff >= 0 || (p_sum_total > params.top_p && sc <= MAX_TOP_K) || bit == 0) {\n\n            break;\n        }\n        // p > top_p\n        else if (p_diff > P_EPSILON) {\n            // There are too many bits in the current selection\n            // Save the current state and go to the next bit\n            // If there are not enough items left using the next bit\n            // it's necessary to restart here with the current bit not set\n            save_mask = bit_mask;\n            select_mask |= bit_mask;\n\n            if (threadIdx.x == 0) {\n                smem_selected_count = old_selected_count;\n                p_sum_total         = old_p_sum_total;\n\n                prefix_op.running_total = old_selected_count;\n            }\n        }\n        else {\n            // sc < num_top_k branch\n            if (save_mask) {\n                select_mask = save_mask;\n\n                save_mask = 0;\n            }\n            if (threadIdx.x == 0) {\n                old_selected_count = smem_selected_count;\n                old_p_sum_total    = p_sum_total;\n            }\n        }\n    }\n\n    __syncthreads();\n\n    // store data to global memory\n    sc = (p_sum_total < params.top_p) ? params.num_items / params.num_segments : smem_selected_count;\n    if (threadIdx.x == 0) {\n        params.gmem_active_count_per_segment[segment] = sc;\n        atomicMax(params.gmem_active_count_total, sc);\n    }\n    if (sc >= MAX_TOP_K) {\n        return;\n    }\n    for (int i = threadIdx.x; i < sc; i += blockDim.x) {\n        int2 selected_element = smem_selected_elements[i];\n        gmem_dst_keys[i]      = selected_element.x;\n        gmem_dst_vals[i]      = selected_element.y;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nint getSmemSizeAndCheck(const TopKPerSegmentContext& context, const TopKPerSegmentParams& params)\n{\n    constexpr int BLOCK_THREADS         = Kernel_params::BLOCK_THREADS;\n    using Key_Data_Type                 = typename Kernel_params::Key_Data_Type;\n    int           num_items_per_segment = params.num_items / params.num_segments;\n    constexpr int ITEMS_INCREMENT       = Kernel_params::ITEMS_INCREMENT;\n    int           kernel_index          = div_up(num_items_per_segment, BLOCK_THREADS * ITEMS_INCREMENT) - 1;\n\n    int       smem_size                = MAX_TOP_K * sizeof(int2);\n    const int items_per_thread         = (kernel_index + 1) * ITEMS_INCREMENT;\n    const int items_per_thread_in_regs = items_per_thread * (1.0F - SMEM_FRACTION);\n    const int items_per_thread_in_smem = items_per_thread - items_per_thread_in_regs;\n\n    smem_size += items_per_thread_in_smem * BLOCK_THREADS * sizeof(typename Float_as_int_<Key_Data_Type>::Type);\n\n    int keys_per_ldg = 2 * sizeof(Key_Data_Type) / 2;\n    if (smem_size + BLOCK_THREADS * sizeof(float) > (size_t)context.sm_shared_size ||  // dynamic + static memory\n        items_per_thread_in_regs + items_per_thread_in_smem != items_per_thread || params.top_p + P_EPSILON > 1.0F\n        || items_per_thread_in_regs % keys_per_ldg != 0 || items_per_thread_in_smem % keys_per_ldg != 0\n        || num_items_per_segment % keys_per_ldg != 0) {\n        return -1;\n    }\n\n    return smem_size;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint getSmemSizeAndCheck(const TopKPerSegmentContext& context,\n                        const TopKPerSegmentParams&  params,\n                        const DType_t                DT_SCORE)\n{\n    int num_items_per_segment = params.num_items / params.num_segments;\n    if (DT_SCORE == kFLOAT) {\n        if (num_items_per_segment % 2 == 0) {\n            return getSmemSizeAndCheck<kernel_params_float>(context, params);\n        }\n        else {\n            return getSmemSizeAndCheck<kernel_params_float_1>(context, params);\n        }\n    }\n    else {\n        if (num_items_per_segment % 4 == 0) {\n            return getSmemSizeAndCheck<kernel_params_half>(context, params);\n        }\n        else {\n            return getSmemSizeAndCheck<kernel_params_half_1>(context, params);\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nvoid segmentedTopPSinglePass_dispatch(const TopKPerSegmentParams&  params,\n                                      const TopKPerSegmentContext& context,\n                                      hipStream_t                 stream)\n{\n\n    constexpr int BLOCK_THREADS = Kernel_params::BLOCK_THREADS;\n    using Key_Data_Type         = typename Kernel_params::Key_Data_Type;\n    using Value_Data_Type       = typename Kernel_params::Value_Data_Type;\n\n    int num_items_per_segment = params.num_items / params.num_segments;\n\n    constexpr int ITEMS_INCREMENT = Kernel_params::ITEMS_INCREMENT;\n    int           kernel_index    = div_up(num_items_per_segment, BLOCK_THREADS * ITEMS_INCREMENT) - 1;\n\n#define KERNEL_RUN(INDEX)                                                                                              \\\n    {                                                                                                                  \\\n        if (smem_size > 0)                                                                                             \\\n            check_cuda_error(                                                                                          \\\n                hipFuncSetAttribute(reinterpret_cast<const void*>(segmented_top_p_single_pass<Kernel_params, ITEMS_INCREMENT*(INDEX + 1)>),          \\\n                                     hipFuncAttributeMaxDynamicSharedMemorySize,                                      \\\n                                     smem_size));                                                                      \\\n        segmented_top_p_single_pass<Kernel_params, ITEMS_INCREMENT*(INDEX + 1)>                                        \\\n            <<<grid_dim, Kernel_params::BLOCK_THREADS, smem_size, stream>>>(params);                                   \\\n    }\n\n    int smem_size = getSmemSizeAndCheck<Kernel_params>(context, params);\n\n    dim3 grid_dim(params.num_segments, 1);\n\n    switch (kernel_index) {\n        case 0:\n            KERNEL_RUN(0) break;\n        case 1:\n            KERNEL_RUN(1) break;\n        case 2:\n            KERNEL_RUN(2) break;\n        case 3:\n            KERNEL_RUN(3) break;\n        case 4:\n            KERNEL_RUN(4) break;\n        case 5:\n            KERNEL_RUN(5) break;\n        case 6:\n            KERNEL_RUN(6) break;\n        case 7:\n            KERNEL_RUN(7) break;\n        default:\n            exit(1);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Kernel_params>\nvoid topPPerSegment_dispatch(const TopKPerSegmentContext& context,\n                             TopKPerSegmentParams&        params,\n                             void*                        temp_storage,\n                             size_t&                      temp_storage_bytes,\n                             hipStream_t                 stream)\n{\n\n    using Key_Data_Type   = typename Kernel_params::Key_Data_Type;\n    using Value_Data_Type = typename Kernel_params::Value_Data_Type;\n\n    if (temp_storage == nullptr) {\n        if (params.num_segments > 1) {\n            hipcub::DeviceSegmentedRadixSort::SortPairsDescending(temp_storage,\n                                                               temp_storage_bytes,\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                               params.num_items,\n                                                               params.num_segments,\n                                                               params.gmem_begin_offsets,\n                                                               params.gmem_end_offsets,\n                                                               0,\n                                                               sizeof(Key_Data_Type) * 8,\n                                                               stream);\n        }\n        else {\n            hipcub::DeviceRadixSort::SortPairsDescending(temp_storage,\n                                                      temp_storage_bytes,\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                      params.num_items,\n                                                      0,\n                                                      sizeof(Key_Data_Type) * 8,\n                                                      stream);\n        }\n        temp_storage_bytes = div_up(temp_storage_bytes, 256) * 256;\n        // total active counts\n        temp_storage_bytes += div_up(sizeof(int), 256) * 256;\n        // storage for gmem_end_offsets\n        temp_storage_bytes += div_up(sizeof(int) * params.num_segments, 256) * 256;\n        return;\n    }\n\n    size_t cub_temp_storage_bytes =\n        temp_storage_bytes - div_up(sizeof(int), 256) * 256 - div_up(sizeof(int) * params.num_segments, 256) * 256;\n    void* cub_temp_storage         = temp_storage;\n    params.gmem_active_count_total = reinterpret_cast<int*>((char*)temp_storage + cub_temp_storage_bytes);\n    params.gmem_active_count_per_segment =\n        reinterpret_cast<int*>((char*)params.gmem_active_count_total + div_up(sizeof(int), 256) * 256);\n\n    int num_items_per_segment = params.num_items / params.num_segments;\n\n    hipMemsetAsync(params.gmem_active_count_total, 0, sizeof(int), stream);\n    hipMemsetAsync(params.gmem_dst_keys, 0, params.num_items * sizeof(Key_Data_Type), stream);\n    segmentedTopPSinglePass_dispatch<Kernel_params>(params, context, stream);\n\n    int max_num_items = 0;\n    hipMemcpyAsync(&max_num_items, params.gmem_active_count_total, sizeof(int), hipMemcpyDeviceToHost, stream);\n\n    hipStreamSynchronize(stream);\n\n    if (max_num_items >= MAX_TOP_K || max_num_items == 0) {\n        if (params.num_segments > 1) {\n            hipcub::DeviceSegmentedRadixSort::SortPairsDescending(cub_temp_storage,\n                                                               cub_temp_storage_bytes,\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                               reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                               reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                               params.num_items,\n                                                               params.num_segments,\n                                                               params.gmem_begin_offsets,\n                                                               params.gmem_end_offsets,\n                                                               0,\n                                                               sizeof(Key_Data_Type) * 8,\n                                                               stream);\n        }\n        else {\n            hipcub::DeviceRadixSort::SortPairsDescending(cub_temp_storage,\n                                                      cub_temp_storage_bytes,\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_src_keys),\n                                                      reinterpret_cast<Key_Data_Type*>(params.gmem_dst_keys),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_src_vals),\n                                                      reinterpret_cast<Value_Data_Type*>(params.gmem_dst_vals),\n                                                      params.num_items,\n                                                      0,\n                                                      sizeof(Key_Data_Type) * 8,\n                                                      stream);\n        }\n    }\n    else {\n        // run at max supported value\n        blockSort<Key_Data_Type>((const Key_Data_Type*)(params.gmem_dst_keys),\n                                 (Key_Data_Type*)(params.gmem_dst_keys),\n                                 (const Value_Data_Type*)(params.gmem_dst_vals),\n                                 (Value_Data_Type*)(params.gmem_dst_vals),\n                                 params.gmem_active_count_per_segment,\n                                 max_num_items,\n                                 num_items_per_segment,\n                                 params.num_segments,\n                                 stream);\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint topPPerSegment(const TopKPerSegmentContext& context,\n                   TopKPerSegmentParams&        params,\n                   const DType_t                DT_SCORE,\n                   void*                        temp_storage,\n                   size_t&                      temp_storage_bytes,\n                   hipStream_t                 stream)\n{\n    int num_items_per_segment = params.num_items / params.num_segments;\n    if (DT_SCORE == kFLOAT) {\n        if (num_items_per_segment % 2 == 0) {\n            topPPerSegment_dispatch<kernel_params_float>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n        else {\n            topPPerSegment_dispatch<kernel_params_float_1>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n    }\n    else {\n        if (num_items_per_segment % 4 == 0) {\n            topPPerSegment_dispatch<kernel_params_half>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n        else {\n            topPPerSegment_dispatch<kernel_params_half_1>(context, params, temp_storage, temp_storage_bytes, stream);\n        }\n    }\n\n    return 0;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n}  // namespace segmented_topp_impl\n\n__global__ void topPInitialize(\n    int* topp_id_val_buf, int* topp_offset_buf, int* begin_topp_offset_buf_, const int batch_size, const int n)\n{\n    int tid = threadIdx.x;\n    int bid = blockIdx.x;\n\n    if (bid == 0) {\n        for (int i = tid; i < batch_size + 1; i += blockDim.x) {\n            topp_offset_buf[i]        = i * n;\n            begin_topp_offset_buf_[i] = topp_offset_buf[i];\n        }\n    }\n\n    int index = tid + bid * blockDim.x;\n\n    while (index < batch_size * n) {\n        topp_id_val_buf[index] = index % n;\n        index += blockDim.x * gridDim.x;\n    }\n}\n\nvoid invokeTopPInitialize(int*         topp_id_val_buf,\n                          int*         topp_offset_buf,\n                          int*         begin_topp_offset_buf_,\n                          const size_t batch_size,\n                          const int    n,\n                          hipStream_t stream)\n{\n    // n: the column number of logits_buffer for top_p sampling\n    topPInitialize<<<32, 512, 0, stream>>>(topp_id_val_buf, topp_offset_buf, begin_topp_offset_buf_, batch_size, n);\n}\n\ntemplate<typename T, int MAX_K, int THREADBLOCK_SIZE>\n__launch_bounds__(THREADBLOCK_SIZE) __global__ void topp_beam_topk_kernel(const T*     log_probs,  // prob.\n                                                                          int*         topk_tmp_id_buf,\n                                                                          T*           topk_tmp_val_buf,\n                                                                          const int    vocab_size,\n                                                                          int*         offset_buf,\n                                                                          int*         begin_offset_buf,\n                                                                          const float  top_p,\n                                                                          const float* top_ps,\n                                                                          const bool*  skip_decode)\n{\n    int thread_id = threadIdx.x;\n    int batch_id  = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n    float p_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n\n    typedef hipcub::BlockReduce<TopK<T, MAX_K>, THREADBLOCK_SIZE> BlockReduce;\n    __shared__ typename BlockReduce::TempStorage               temp_storage;\n    TopK<T, MAX_K>                                             partial;\n\n    const bool IS_FP16   = std::is_same<T, half>::value;\n    const T    MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n\n#pragma unroll\n    for (int i = 0; i < MAX_K; ++i) {\n        partial.p[i] = -1;\n        partial.u[i] = -MAX_T_VAL;\n    }\n\n#pragma unroll\n    for (int elem_id = thread_id; elem_id < vocab_size; elem_id += THREADBLOCK_SIZE) {\n        int index = elem_id + batch_id * vocab_size;\n        partial.insert(log_probs[index], index);\n    }\n\n    TopK<T, MAX_K> total = BlockReduce(temp_storage).Reduce(partial, reduce_topk_op<T, MAX_K>);\n\n    if (thread_id == 0) {\n        begin_offset_buf[batch_id] = offset_buf[batch_id];\n        T sum_prob                 = (T)(0.0f);\n\n#pragma unroll\n        for (int i = 0; i < MAX_K; i++) {\n            sum_prob += total.u[i];\n        }\n\n        if ((float)sum_prob >= p_threshold) {\n            begin_offset_buf[batch_id] += vocab_size;\n            int index = batch_id * vocab_size;\n\n#pragma unroll\n            for (int i = 0; i < MAX_K; ++i) {\n                topk_tmp_id_buf[index + i]  = total.p[i] % vocab_size;\n                topk_tmp_val_buf[index + i] = total.u[i];\n            }\n        }\n    }\n}\n\nstruct BlockPrefixCallbackOp {\n    // Running prefix\n    float running_total;\n    // Constructor\n    __device__ BlockPrefixCallbackOp(float running_total): running_total(running_total) {}\n    // Callback operator to be entered by the first warp of threads in the block.\n    // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n    __device__ float operator()(float block_aggregate)\n    {\n        float old_prefix = running_total;\n        running_total += block_aggregate;\n        return old_prefix;\n    }\n};\n\ntemplate<typename T, int BLOCK_SIZE>\n__global__ void topp_sampling(T*             sorted_log_probs,\n                              int*           sorted_id_vals,\n                              int*           ids,\n                              int*           sequence_length,\n                              bool*          finished_buf,\n                              float*         cum_log_probs,\n                              float*         output_log_probs,\n                              const int*     begin_offset_buf,\n                              const int*     offset_buf,\n                              const int      vocab_size,\n                              hiprandState_t* curandstate,\n                              const float    top_p,\n                              const float*   top_ps,\n                              const int*     end_ids,\n                              const int      batch_size,\n                              const bool*    skip_decode)\n{\n    __shared__ int   stop_shared;\n    __shared__ float rand_num_s;\n\n    const int tid      = threadIdx.x;\n    const int batch_id = blockIdx.x;\n    if (skip_decode != nullptr && skip_decode[batch_id]) {\n        return;\n    }\n\n    constexpr int WARP_SIZE      = 32;\n    constexpr int NUM_WARPS      = BLOCK_SIZE / WARP_SIZE;\n    const int     lane_id        = threadIdx.x % WARP_SIZE;\n    const int     warp_id        = threadIdx.x / WARP_SIZE;\n    const float   prob_threshold = (top_ps != nullptr) ? top_ps[batch_id] : top_p;\n\n    if (threadIdx.x == 0) {\n        stop_shared = 0;\n        rand_num_s  = hiprand_uniform(curandstate + blockIdx.x) * prob_threshold;\n    }\n\n    // if begin_offset_buf and offset_buf of sorting have same value,\n    // this means that we have find best one in beam_topK_kernel_for_topP\n    // and skip the sorting. So, we can skip then during sampling.\n    if (begin_offset_buf[batch_id] == offset_buf[batch_id]) {\n        if (tid == 0) {\n            int offset    = batch_id * vocab_size;\n            ids[batch_id] = sorted_id_vals[offset];\n\n            if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                float lprob = logf(sorted_log_probs[offset]);\n                if (cum_log_probs != nullptr) {\n                    cum_log_probs[batch_id] += lprob;\n                }\n                if (output_log_probs != nullptr) {\n                    output_log_probs[batch_id] = lprob;\n                }\n            }\n            if (sequence_length != nullptr && finished_buf != nullptr) {\n                sequence_length[batch_id] =\n                    finished_buf[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n                finished_buf[batch_id] = ids[batch_id] == end_ids[batch_id] ? 1 : 0;\n            }\n        }\n        return;\n    }\n\n    typedef hipcub::BlockScan<float, BLOCK_SIZE>  BlockScan;\n    __shared__ typename BlockScan::TempStorage temp_storage;\n    __shared__ uint32_t                        selected_shared[NUM_WARPS];\n    // Initialize running total\n    BlockPrefixCallbackOp prefix_op(0);\n\n    if (lane_id == 0) {\n        selected_shared[warp_id] = 0;\n    }\n\n    __syncthreads();\n\n    int offset          = batch_id * vocab_size;\n    ids[batch_id]       = sorted_id_vals[offset];\n    int   end           = ((vocab_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;\n    int   i_active      = 0;\n    float thread_offset = 0;\n    for (int i = tid; i < end; i += BLOCK_SIZE) {\n        float thread_count = (i < vocab_size) ? (float)sorted_log_probs[offset + i] : 0.f;\n        BlockScan(temp_storage).InclusiveSum(thread_count, thread_offset, prefix_op);\n\n        uint32_t active_mask = __ballot_sync(0xFFFFFFFF, rand_num_s <= thread_offset);\n\n        i_active = i;\n        if (active_mask != 0) {\n            if (lane_id == 0) {\n                atomicAdd(&stop_shared, 1);\n                selected_shared[warp_id] = active_mask;\n            }\n        }\n        __syncthreads();\n        if (stop_shared > 0) {\n            break;\n        }\n    };\n\n    // select first active warp\n    bool skip = (selected_shared[warp_id] > 0) ? false : true;\n    for (int i = 0; i < warp_id; i++) {\n        if (selected_shared[i] != 0) {\n            skip = true;\n        }\n    }\n    if (!skip) {\n        int active_lane_id = WARP_SIZE - __popc(selected_shared[warp_id]);\n        if (lane_id == active_lane_id) {\n            ids[batch_id] = sorted_id_vals[offset + i_active];\n            if (cum_log_probs != nullptr || output_log_probs != nullptr) {\n                float lprob = logf(sorted_log_probs[offset + i_active]);\n                if (cum_log_probs != nullptr) {\n                    cum_log_probs[batch_id] += lprob;\n                }\n                if (output_log_probs != nullptr) {\n                    output_log_probs[batch_id] = lprob;\n                }\n            }\n            if (sequence_length != nullptr && finished_buf != nullptr) {\n                sequence_length[batch_id] =\n                    finished_buf[batch_id] ? sequence_length[batch_id] : sequence_length[batch_id] + 1;\n                finished_buf[batch_id] = ids[batch_id] == end_ids[batch_id] ? 1 : 0;\n            }\n        }\n    }\n}\n\ntemplate<typename T>\nvoid invokeBatchTopPSampling(void*           workspace,\n                             size_t&         workspace_size,\n                             size_t&         cub_temp_storage_size,\n                             int*            output_ids,\n                             int*            sequence_length,\n                             bool*           finished_buf,\n                             float*          cum_log_probs,\n                             float*          output_log_probs,\n                             const T*        log_probs,\n                             const int*      id_vals,\n                             int*            offset_buf,\n                             int*            begin_offset_buf,\n                             hiprandState_t*  curandstate,\n                             const int       batch_size,\n                             const size_t    vocab_size_padded,\n                             const int*      end_ids,\n                             const float     max_top_p,\n                             const float*    top_ps,\n                             hipStream_t    stream,\n                             hipDeviceProp_t* cuda_device_prop,\n                             const bool*     skip_decode)\n{\n    // Here, we put batch size as an argument because the batch size of initialization\n    // and inference may be different due to pipeline parallelism.\n    const int vocab_size = vocab_size_padded;\n    const int block_size = 256;\n\n    size_t sorted_log_prob_buf_size = batch_size * vocab_size * sizeof(T);    // type T\n    size_t sorted_id_vals_buf_size  = batch_size * vocab_size * sizeof(int);  // type int\n    sorted_log_prob_buf_size        = div_up(sorted_log_prob_buf_size, 256) * 256;\n    sorted_id_vals_buf_size         = div_up(sorted_id_vals_buf_size, 256) * 256;\n\n    void* cub_temp_storage = workspace;\n    T*    sorted_log_probs = (T*)((char*)cub_temp_storage + cub_temp_storage_size);\n    int*  sorted_id_vals   = (int*)((char*)sorted_log_probs + sorted_log_prob_buf_size);\n\n    bool do_radix_sort = (ENABLE_SINGLE_PASS_TOP_P == 0 || max_top_p >= SINGLE_PASS_THRESHOLD);\n    int  smem_size     = -1;\n\n    segmented_topp_impl::TopKPerSegmentContext context;\n    segmented_topp_impl::TopKPerSegmentParams  params;\n    segmented_topp_impl::DType_t               dataTypeKind =\n        (std::is_same<T, float>::value) ? segmented_topp_impl::kFLOAT : segmented_topp_impl::kHALF;\n\n    if (!do_radix_sort) {\n        FT_CHECK(cuda_device_prop != nullptr);\n        memset(&context, 0, sizeof(context));\n        context.sm_count       = cuda_device_prop->multiProcessorCount;\n        context.sm_shared_size = cuda_device_prop->sharedMemPerMultiprocessor;\n        context.sm_version     = cuda_device_prop->major * 100 + cuda_device_prop->minor * 10;\n\n        memset(&params, 0, sizeof(params));\n        params.gmem_src_keys        = reinterpret_cast<void*>(const_cast<T*>(log_probs));\n        params.gmem_dst_keys        = sorted_log_probs;\n        params.gmem_src_vals        = reinterpret_cast<void*>(const_cast<int*>(id_vals));\n        params.gmem_dst_vals        = reinterpret_cast<void*>(sorted_id_vals);\n        params.gmem_begin_offsets   = begin_offset_buf;\n        params.gmem_end_offsets     = offset_buf + 1;\n        params.workspace            = nullptr;\n        params.num_items            = vocab_size * batch_size;\n        params.num_segments         = batch_size;\n        params.top_p                = max_top_p;\n        params.confidence_threshold = 0.0F;\n\n        smem_size     = getSmemSizeAndCheck(context, params, dataTypeKind);\n        do_radix_sort = smem_size < 0;\n    }\n\n    if (do_radix_sort) {\n        if (workspace == nullptr) {\n            check_cuda_error(\n                hipcub::DeviceSegmentedRadixSort::SortPairsDescending(nullptr,\n                                                                   cub_temp_storage_size,\n                                                                   log_probs,\n                                                                   (T*)nullptr,\n                                                                   id_vals,\n                                                                   (int*)nullptr,\n                                                                   vocab_size * batch_size,\n                                                                   batch_size,\n                                                                   begin_offset_buf,\n                                                                   offset_buf + 1,\n                                                                   0,              // begin_bit\n                                                                   sizeof(T) * 8,  // end_bit = sizeof(KeyT) * 8\n                                                                   stream));       // cudaStream_t\n            cub_temp_storage_size = div_up(cub_temp_storage_size, 256) * 256;\n            workspace_size        = sorted_log_prob_buf_size + sorted_id_vals_buf_size + cub_temp_storage_size;\n            return;\n        }\n\n        topp_beam_topk_kernel<T, 1, block_size><<<batch_size, block_size, 0, stream>>>(log_probs,\n                                                                                       sorted_id_vals,\n                                                                                       sorted_log_probs,\n                                                                                       vocab_size,\n                                                                                       offset_buf,\n                                                                                       begin_offset_buf,\n                                                                                       max_top_p,\n                                                                                       top_ps,\n                                                                                       skip_decode);\n\n        check_cuda_error(\n            hipcub::DeviceSegmentedRadixSort::SortPairsDescending(cub_temp_storage,\n                                                               cub_temp_storage_size,\n                                                               log_probs,\n                                                               sorted_log_probs,\n                                                               id_vals,\n                                                               sorted_id_vals,\n                                                               vocab_size * batch_size,\n                                                               batch_size,\n                                                               begin_offset_buf,\n                                                               offset_buf + 1,\n                                                               0,              // begin_bit\n                                                               sizeof(T) * 8,  // end_bit = sizeof(KeyT) * 8\n                                                               stream));       // cudaStream_t\n    }\n    else {\n        if (workspace == nullptr) {\n            segmented_topp_impl::topPPerSegment(\n                context, params, dataTypeKind, cub_temp_storage, cub_temp_storage_size, stream);\n            workspace_size = sorted_log_prob_buf_size + sorted_id_vals_buf_size + cub_temp_storage_size;\n            return;\n        }\n        else {\n            topp_beam_topk_kernel<T, 1, block_size><<<batch_size, block_size, 0, stream>>>(log_probs,\n                                                                                           sorted_id_vals,\n                                                                                           sorted_log_probs,\n                                                                                           vocab_size,\n                                                                                           offset_buf,\n                                                                                           begin_offset_buf,\n                                                                                           max_top_p,\n                                                                                           top_ps,\n                                                                                           skip_decode);\n            segmented_topp_impl::topPPerSegment(\n                context, params, dataTypeKind, cub_temp_storage, cub_temp_storage_size, stream);\n        }\n    }\n\n    constexpr int SAMPLING_BLOCK_SIZE = 256;\n    dim3          grid(batch_size);\n    topp_sampling<T, SAMPLING_BLOCK_SIZE><<<grid, SAMPLING_BLOCK_SIZE, 0, stream>>>(sorted_log_probs,\n                                                                                    sorted_id_vals,\n                                                                                    output_ids,\n                                                                                    sequence_length,\n                                                                                    finished_buf,\n                                                                                    cum_log_probs,\n                                                                                    output_log_probs,\n                                                                                    begin_offset_buf,\n                                                                                    offset_buf + 1,\n                                                                                    vocab_size,\n                                                                                    curandstate,\n                                                                                    max_top_p,\n                                                                                    top_ps,\n                                                                                    end_ids,\n                                                                                    batch_size,\n                                                                                    skip_decode);\n}\n\ntemplate void invokeBatchTopPSampling(void*           workspace,\n                                      size_t&         workspace_size,\n                                      size_t&         cub_temp_storage_size,\n                                      int*            output_ids,\n                                      int*            sequence_length,\n                                      bool*           finished_buf,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      const float*    log_probs,\n                                      const int*      id_vals,\n                                      int*            offset_buf,\n                                      int*            begin_offset_buf,\n                                      hiprandState_t*  curandstate,\n                                      const int       batch_size,\n                                      const size_t    vocab_size_padded,\n                                      const int*      end_ids,\n                                      const float     max_top_p,\n                                      const float*    top_ps,\n                                      hipStream_t    stream,\n                                      hipDeviceProp_t* cuda_device_prop,\n                                      const bool*     skip_decode);\n\ntemplate void invokeBatchTopPSampling(void*           workspace,\n                                      size_t&         workspace_size,\n                                      size_t&         cub_temp_storage_size,\n                                      int*            output_ids,\n                                      int*            sequence_length,\n                                      bool*           finished_buf,\n                                      float*          cum_log_probs,\n                                      float*          output_log_probs,\n                                      const half*     log_probs,\n                                      const int*      id_vals,\n                                      int*            offset_buf,\n                                      int*            begin_offset_buf,\n                                      hiprandState_t*  curandstate,\n                                      const int       batch_size,\n                                      const size_t    vocab_size_padded,\n                                      const int*      end_ids,\n                                      const float     max_top_p,\n                                      const float*    top_ps,\n                                      hipStream_t    stream,\n                                      hipDeviceProp_t* cuda_device_prop,\n                                      const bool*     skip_decode);\n\ntemplate<typename T>\nvoid invokeTopPSampling(void*           workspace,\n                        size_t&         workspace_size,\n                        size_t&         cub_temp_storage_size,\n                        int*            output_ids,\n                        int*            sequence_length,\n                        bool*           finished_buf,\n                        float*          cum_log_probs,\n                        float*          output_log_probs,\n                        const T*        log_probs,\n                        const int*      id_vals,\n                        int*            offset_buf,\n                        int*            begin_offset_buf,\n                        hiprandState_t*  curandstate,\n                        const int       batch_size,\n                        const size_t    vocab_size_padded,\n                        const int*      end_ids,\n                        const float     top_p,\n                        hipStream_t    stream,\n                        hipDeviceProp_t* cuda_device_prop,\n                        const bool*     skip_decode)\n{\n    invokeBatchTopPSampling(workspace,\n                            workspace_size,\n                            cub_temp_storage_size,\n                            output_ids,\n                            sequence_length,\n                            finished_buf,\n                            cum_log_probs,\n                            output_log_probs,\n                            log_probs,\n                            id_vals,\n                            offset_buf,\n                            begin_offset_buf,\n                            curandstate,\n                            batch_size,\n                            vocab_size_padded,\n                            end_ids,\n                            top_p,\n                            nullptr,\n                            stream,\n                            cuda_device_prop,\n                            skip_decode);\n}\n\ntemplate void invokeTopPSampling(void*           workspace,\n                                 size_t&         workspace_size,\n                                 size_t&         cub_temp_storage_size,\n                                 int*            output_ids,\n                                 int*            sequence_length,\n                                 bool*           finished_buf,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 const float*    log_probs,\n                                 const int*      id_vals,\n                                 int*            offset_buf,\n                                 int*            begin_offset_buf,\n                                 hiprandState_t*  curandstate,\n                                 const int       batch_size,\n                                 const size_t    vocab_size_padded,\n                                 const int*      end_ids,\n                                 const float     top_p,\n                                 hipStream_t    stream,\n                                 hipDeviceProp_t* cuda_device_prop,\n                                 const bool*     skip_decode);\n\ntemplate void invokeTopPSampling(void*           workspace,\n                                 size_t&         workspace_size,\n                                 size_t&         cub_temp_storage_size,\n                                 int*            output_ids,\n                                 int*            sequence_length,\n                                 bool*           finished_buf,\n                                 float*          cum_log_probs,\n                                 float*          output_log_probs,\n                                 const half*     log_probs,\n                                 const int*      id_vals,\n                                 int*            offset_buf,\n                                 int*            begin_offset_buf,\n                                 hiprandState_t*  curandstate,\n                                 const int       batch_size,\n                                 const size_t    vocab_size_padded,\n                                 const int*      end_ids,\n                                 const float     top_p,\n                                 hipStream_t    stream,\n                                 hipDeviceProp_t* cuda_device_prop,\n                                 const bool*     skip_decode);\n\ntemplate<typename T>\n__global__ void\naddBiasSoftMax(T* logits, const T* bias, const int* end_ids, const bool* finished, const int n_padded, const int n)\n{\n    int  bid    = blockIdx.x;\n    bool finish = (finished != nullptr) ? finished[bid] : false;\n    int  offset = bid * n_padded;\n\n    float            max_val   = -1 * FLT_MAX;\n    const bool       IS_FP16   = std::is_same<T, half>::value;\n    const T          MAX_T_VAL = (IS_FP16) ? HALF_FLT_MAX : FLT_MAX;\n    __shared__ float s_max_val;\n    __shared__ float s_sum_val;\n\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        if (tid < n) {\n            if (finish) {\n                logits[offset + tid] = (tid == end_ids[bid]) ? MAX_T_VAL : -MAX_T_VAL;\n            }\n            else {\n                T bias_val = (bias != nullptr) ? bias[tid] : (T)0.0f;\n                logits[offset + tid] += bias_val;\n            }\n        }\n        else {\n            logits[offset + tid] = -MAX_T_VAL;\n        }\n        max_val = max(max_val, (float)logits[offset + tid]);\n    }\n\n    max_val = blockReduceMax<float>((float)max_val);\n    if (threadIdx.x == 0) {\n        s_max_val = max_val;\n    }\n    __syncthreads();\n\n    float sum_val = 0.0f;\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        logits[offset + tid] = __expf((float)logits[offset + tid] - s_max_val);\n        sum_val += (float)logits[offset + tid];\n    }\n\n    sum_val = blockReduceSum<float>(sum_val);\n    if (threadIdx.x == 0) {\n        s_sum_val = sum_val;\n    }\n    __syncthreads();\n\n    for (int tid = threadIdx.x; tid < n_padded; tid += blockDim.x) {\n        logits[offset + tid] = ((float)logits[offset + tid] / (s_sum_val + 1e-6f));\n    }\n}\n\ntemplate<typename T>\nvoid invokeAddBiasSoftMax(T*           logits,\n                          const T*     bias,\n                          const int*   end_ids,\n                          const bool*  finished,\n                          const int    m,\n                          const int    n_padded,\n                          const int    n,\n                          hipStream_t stream)\n{\n    dim3 grid(m);\n    dim3 block(min(n, 1024));\n    /*n is the vocab_size, e.g., 30000, 7000.... vocab_size is usually very big. */\n    addBiasSoftMax<<<grid, block, 0, stream>>>(logits, bias, end_ids, finished, n_padded, n);\n}\n\ntemplate void invokeAddBiasSoftMax(float*       logits,\n                                   const float* bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    m,\n                                   const int    n_padded,\n                                   const int    n,\n                                   hipStream_t stream);\n\ntemplate void invokeAddBiasSoftMax(half*        logits,\n                                   const half*  bias,\n                                   const int*   end_ids,\n                                   const bool*  finished,\n                                   const int    m,\n                                   const int    n_padded,\n                                   const int    n,\n                                   hipStream_t stream);\n\n__global__ void computeToppDecay(float*         runtime_top_p,\n                                 const float*   runtime_initial_top_p,\n                                 const int*     output_ids,\n                                 const float*   top_p_decay,\n                                 const float*   top_p_min,\n                                 const int32_t* top_p_reset_ids,\n                                 const int      local_batch_size)\n{\n    /**\n     * @brief Compute the topp decay by https://arxiv.org/pdf/2206.04624.pdf\n     *        In short, the formula is\n     *          runtime_top_p = max(runtime_top_p * top_p_decay, top_p_min)\n     *        If generating the top_p_reset_ids, then reset the runtime_top_p.\n     *\n     * \\param runtime_top_p          [local_batch_size]\n     * \\param runtime_initial_top_p  [local_batch_size]\n     * \\param output_ids             [local_batch_size]\n     * \\param top_p_decay            [local_batch_size]\n     * \\param top_p_min              [local_batch_size]\n     * \\param top_p_reset_ids         [local_batch_size]\n     * \\param local_batch_size\n     *\n     */\n\n    int idx = blockDim.x * blockIdx.x + threadIdx.x;\n    if (output_ids[idx] == top_p_reset_ids[idx]) {\n        runtime_top_p[idx] = runtime_initial_top_p[idx];\n    }\n    else {\n        runtime_top_p[idx] = max(runtime_top_p[idx] * top_p_decay[idx], top_p_min[idx]);\n    }\n}\n\nvoid invokeComputeToppDecay(float*         runtime_top_p,\n                            const float*   runtime_initial_top_p,\n                            const int*     output_ids,\n                            const float*   top_p_decay,\n                            const float*   top_p_min,\n                            const int32_t* top_p_reset_ids,\n                            const int      local_batch_size,\n                            hipStream_t   stream)\n{\n    dim3 block(min(local_batch_size, 512));\n    dim3 grid((local_batch_size + block.x - 1) / block.x);\n    computeToppDecay<<<grid, block, 0, stream>>>(\n        runtime_top_p, runtime_initial_top_p, output_ids, top_p_decay, top_p_min, top_p_reset_ids, local_batch_size);\n}\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/sampling_topp_kernels.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/layout_transformer_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\n// grid((n+31)/32, (m+31)/32)\n// block(32, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_COL32ToColMajor_kernel(T* dst, const T* src, const int m, const int n)\n{\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < n) && (y < m));\n    // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n    // COL32_idx = (COL32_col << 5) * m + COL32_row = (x & 0xffffffe0)*m + (y << 5) + (x & 31)\n    if (check) {\n        dst[y * n + x] = __ldg(src + ((x & 0xffffffe0) * m + (y << 5) + (x & 31)));\n    }\n}\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\n// grid((n+31)/32, (m+31)/32)\n// block(16, 32)\ntemplate<>\n__global__ void transposeMatrix_COL32ToColMajor_kernel(half2* dst, const half2* src, const int m, const int n)\n{\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 1;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < n) && (y < m));\n    // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n    // COL32_idx = (COL32_col << 5) * m + COL32_row = (x & 0xffffffe0)*m + (y << 5) + (x & 31)\n    if (check) {\n        dst[(y * n + x) >> 1] = __ldg(src + (((x & 0xffffffe0) * m + (y << 5) + (x & 31)) >> 1));\n    }\n}\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\ntemplate<typename T>\nvoid invokeTransposeMatrixCOL32ToColMajor(T* dst, const T* src, const int m, const int n, cudaStream_t stream)\n{\n    assert(n % 32 == 0);\n    if (sizeof(T) == sizeof(half)) {\n        transposeMatrix_COL32ToColMajor_kernel<<<dim3((n + 31) / 32, (m + 31) / 32), dim3(16, 32), 0, stream>>>(\n            (half2*)dst, (const half2*)src, m, n);\n    }\n    else {\n        transposeMatrix_COL32ToColMajor_kernel<T>\n            <<<dim3((n + 31) / 32, (m + 31) / 32), dim3(32, 32), 0, stream>>>(dst, src, m, n);\n    }\n}\ntemplate void invokeTransposeMatrixCOL32ToColMajor<float>(\n    float* dst, const float* src, const int m, const int n, cudaStream_t stream);\n\ntemplate void\ninvokeTransposeMatrixCOL32ToColMajor<half>(half* dst, const half* src, const int m, const int n, cudaStream_t stream);\n\ntemplate void invokeTransposeMatrixCOL32ToColMajor<int8_t>(\n    int8_t* dst, const int8_t* src, const int m, const int n, cudaStream_t stream);\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(32, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_colMajorToCOL32_kernel(T* dst, const T* src, const int m, const int n)\n{\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n        dst[(x & 0xffffffe0) * n + (y << 5) + (x & 31)] = __ldg(src + y * m + x);\n    }\n}\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(16, 32)\ntemplate<>\n__global__ void transposeMatrix_colMajorToCOL32_kernel(half2* dst, const half2* src, const int m, const int n)\n{\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 1;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n        dst[((x & 0xffffffe0) * n + (y << 5) + (x & 31)) >> 1] = __ldg(src + ((y * m + x) >> 1));\n    }\n}\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\nvoid invokeTransposeMatrixColMajorToCOL32(T* dst, const T* src, const int m, const int n, cudaStream_t stream)\n{\n    assert(m % 32 == 0);\n    if (sizeof(T) == sizeof(float)) {\n        transposeMatrix_colMajorToCOL32_kernel<T>\n            <<<dim3((m + 31) / 32, (n + 31) / 32), dim3(32, 32), 0, stream>>>(dst, src, m, n);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        transposeMatrix_colMajorToCOL32_kernel<<<dim3((m + 31) / 32, (n + 31) / 32), dim3(16, 32), 0, stream>>>(\n            (half2*)dst, (const half2*)src, m, n);\n    }\n}\n\ntemplate void invokeTransposeMatrixColMajorToCOL32<float>(\n    float* dst, const float* src, const int m, const int n, cudaStream_t stream);\n\ntemplate void\ninvokeTransposeMatrixColMajorToCOL32<half>(half* dst, const half* src, const int m, const int n, cudaStream_t stream);\n\n// transpose matrix & transform col-major to COL32 & quantize\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_colMajorToCOL32_quantize_kernel(\n    char4* dst, const T* src, const int m, const int n, const float* scale_ptr)\n{\n\n    const float scale = __ldg(scale_ptr);\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 2;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n        char4 tmp4;\n        tmp4.x = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x)) * scale);\n        tmp4.y = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 1)) * scale);\n        tmp4.z = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 2)) * scale);\n        tmp4.w = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 3)) * scale);\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n\n        dst[((x & 0xffffffe0) * n + (y << 5) + (x & 31)) >> 2] = tmp4;\n    }\n}\n\n// transpose matrix & transform col-major to COL32 & quantize\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\nvoid invokeTransposeMatrixColMajorToCOL32Quantize(\n    int8_t* dst, const T* src, const int m, const int n, const float* scale_ptr, cudaStream_t stream)\n{\n    assert(m % 32 == 0);\n    transposeMatrix_colMajorToCOL32_quantize_kernel<T>\n        <<<dim3((m + 31) / 32, (n + 31) / 32), dim3(8, 32), 0, stream>>>((char4*)dst, src, m, n, scale_ptr);\n}\n\ntemplate void invokeTransposeMatrixColMajorToCOL32Quantize<float>(\n    int8_t* dst, const float* src, const int m, const int n, const float* scale_ptr, cudaStream_t stream);\n\ntemplate void invokeTransposeMatrixColMajorToCOL32Quantize<half>(\n    int8_t* dst, const half* src, const int m, const int n, const float* scale_ptr, cudaStream_t stream);\n\n// transform row-major to COL32\n// input matrix is (m, n) row-major\n// output matrix is (m, n) COL32\n// n should be a multiple of 32\n// grid((n+31)/32, (m+31)/32)\n// block(8, 32)\n__global__ void rowMajorToCOL32_kernel(char4* dst, const char4* src, const int m, const int n)\n{\n\n    int n_id = (blockIdx.x * blockDim.x + threadIdx.x) << 2;\n    int m_id = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((m_id < m) && (n_id < n));\n    if (check) {\n\n        // COL32_col = n_id >> 5 ; COL32_row = (m_id << 5) + (n_id & 31);\n        // COL32_idx = (COL32_col << 5) * m + COL32_row = (n_id & 0xffffffe0)*m + (m_id << 5) + (n_id & 31)\n        dst[((n_id & 0xffffffe0) * m + (m_id << 5) + (n_id & 31)) >> 2] = __ldg(src + ((m_id * n + n_id) >> 2));\n    }\n}\n\n// transform row-major to COL32\n// input matrix is (m, n) row-major\n// output matrix is (m, n) COL32\n// n should be a multiple of 32\n// grid((n+31)/32, (m+31)/32)\n// block(8, 32)\nvoid invokeRowMajorToCOL32(int8_t* dst, const int8_t* src, const int m, const int n, cudaStream_t stream)\n{\n    assert(n % 32 == 0);\n    rowMajorToCOL32_kernel<<<dim3((n + 31) / 32, (m + 31) / 32), dim3(8, 32), 0, stream>>>(\n        (char4*)dst, (const char4*)src, m, n);\n}\n\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/layout_transformer_int8_kernels.h\"\n\nnamespace fastertransformer {\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\n// grid((n+31)/32, (m+31)/32)\n// block(32, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_COL32ToColMajor_kernel(T* dst, const T* src, const int m, const int n)\n{\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < n) && (y < m));\n    // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n    // COL32_idx = (COL32_col << 5) * m + COL32_row = (x & 0xffffffe0)*m + (y << 5) + (x & 31)\n    if (check) {\n        dst[y * n + x] = __ldg(src + ((x & 0xffffffe0) * m + (y << 5) + (x & 31)));\n    }\n}\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\n// grid((n+31)/32, (m+31)/32)\n// block(16, 32)\ntemplate<>\n__global__ void transposeMatrix_COL32ToColMajor_kernel(half2* dst, const half2* src, const int m, const int n)\n{\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 1;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < n) && (y < m));\n    // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n    // COL32_idx = (COL32_col << 5) * m + COL32_row = (x & 0xffffffe0)*m + (y << 5) + (x & 31)\n    if (check) {\n        dst[(y * n + x) >> 1] = __ldg(src + (((x & 0xffffffe0) * m + (y << 5) + (x & 31)) >> 1));\n    }\n}\n\n// transpose matrix & transform COL32 to col-major\n// input matrix is (m n) COL32\n// output matrix is (n m) col-major\ntemplate<typename T>\nvoid invokeTransposeMatrixCOL32ToColMajor(T* dst, const T* src, const int m, const int n, hipStream_t stream)\n{\n    assert(n % 32 == 0);\n    if (sizeof(T) == sizeof(half)) {\n        transposeMatrix_COL32ToColMajor_kernel<<<dim3((n + 31) / 32, (m + 31) / 32), dim3(16, 32), 0, stream>>>(\n            (half2*)dst, (const half2*)src, m, n);\n    }\n    else {\n        transposeMatrix_COL32ToColMajor_kernel<T>\n            <<<dim3((n + 31) / 32, (m + 31) / 32), dim3(32, 32), 0, stream>>>(dst, src, m, n);\n    }\n}\ntemplate void invokeTransposeMatrixCOL32ToColMajor<float>(\n    float* dst, const float* src, const int m, const int n, hipStream_t stream);\n\ntemplate void\ninvokeTransposeMatrixCOL32ToColMajor<half>(half* dst, const half* src, const int m, const int n, hipStream_t stream);\n\ntemplate void invokeTransposeMatrixCOL32ToColMajor<int8_t>(\n    int8_t* dst, const int8_t* src, const int m, const int n, hipStream_t stream);\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(32, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_colMajorToCOL32_kernel(T* dst, const T* src, const int m, const int n)\n{\n\n    int x = blockIdx.x * blockDim.x + threadIdx.x;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n        dst[(x & 0xffffffe0) * n + (y << 5) + (x & 31)] = __ldg(src + y * m + x);\n    }\n}\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(16, 32)\ntemplate<>\n__global__ void transposeMatrix_colMajorToCOL32_kernel(half2* dst, const half2* src, const int m, const int n)\n{\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 1;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n        dst[((x & 0xffffffe0) * n + (y << 5) + (x & 31)) >> 1] = __ldg(src + ((y * m + x) >> 1));\n    }\n}\n\n// transpose matrix & transform col-major to COL32\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\nvoid invokeTransposeMatrixColMajorToCOL32(T* dst, const T* src, const int m, const int n, hipStream_t stream)\n{\n    assert(m % 32 == 0);\n    if (sizeof(T) == sizeof(float)) {\n        transposeMatrix_colMajorToCOL32_kernel<T>\n            <<<dim3((m + 31) / 32, (n + 31) / 32), dim3(32, 32), 0, stream>>>(dst, src, m, n);\n    }\n    else if (sizeof(T) == sizeof(half)) {\n        transposeMatrix_colMajorToCOL32_kernel<<<dim3((m + 31) / 32, (n + 31) / 32), dim3(16, 32), 0, stream>>>(\n            (half2*)dst, (const half2*)src, m, n);\n    }\n}\n\ntemplate void invokeTransposeMatrixColMajorToCOL32<float>(\n    float* dst, const float* src, const int m, const int n, hipStream_t stream);\n\ntemplate void\ninvokeTransposeMatrixColMajorToCOL32<half>(half* dst, const half* src, const int m, const int n, hipStream_t stream);\n\n// transpose matrix & transform col-major to COL32 & quantize\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\n__global__ void transposeMatrix_colMajorToCOL32_quantize_kernel(\n    char4* dst, const T* src, const int m, const int n, const float* scale_ptr)\n{\n\n    const float scale = __ldg(scale_ptr);\n\n    int x = (blockIdx.x * blockDim.x + threadIdx.x) << 2;\n    int y = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((x < m) && (y < n));\n    if (check) {\n        char4 tmp4;\n        tmp4.x = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x)) * scale);\n        tmp4.y = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 1)) * scale);\n        tmp4.z = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 2)) * scale);\n        tmp4.w = float_to_int8_rn(static_cast<float>(__ldg(src + y * m + x + 3)) * scale);\n\n        // COL32_col = x >> 5 ; COL32_row = (y << 5) + (x & 31);\n        // COL32_idx = (COL32_col << 5) * n + COL32_row = (x & 0xffffffe0)*n + (y << 5) + (x & 31)\n\n        dst[((x & 0xffffffe0) * n + (y << 5) + (x & 31)) >> 2] = tmp4;\n    }\n}\n\n// transpose matrix & transform col-major to COL32 & quantize\n// input matrix is (m, n) col-major\n// output matrix is (n, m) COL32, using char4 to write out\n// m should be a multiple of 32\n// grid((m+31)/32, (n+31)/32)\n// block(8, 32)\ntemplate<typename T>\nvoid invokeTransposeMatrixColMajorToCOL32Quantize(\n    int8_t* dst, const T* src, const int m, const int n, const float* scale_ptr, hipStream_t stream)\n{\n    assert(m % 32 == 0);\n    transposeMatrix_colMajorToCOL32_quantize_kernel<T>\n        <<<dim3((m + 31) / 32, (n + 31) / 32), dim3(8, 32), 0, stream>>>((char4*)dst, src, m, n, scale_ptr);\n}\n\ntemplate void invokeTransposeMatrixColMajorToCOL32Quantize<float>(\n    int8_t* dst, const float* src, const int m, const int n, const float* scale_ptr, hipStream_t stream);\n\ntemplate void invokeTransposeMatrixColMajorToCOL32Quantize<half>(\n    int8_t* dst, const half* src, const int m, const int n, const float* scale_ptr, hipStream_t stream);\n\n// transform row-major to COL32\n// input matrix is (m, n) row-major\n// output matrix is (m, n) COL32\n// n should be a multiple of 32\n// grid((n+31)/32, (m+31)/32)\n// block(8, 32)\n__global__ void rowMajorToCOL32_kernel(char4* dst, const char4* src, const int m, const int n)\n{\n\n    int n_id = (blockIdx.x * blockDim.x + threadIdx.x) << 2;\n    int m_id = blockIdx.y * blockDim.y + threadIdx.y;\n\n    bool check = ((m_id < m) && (n_id < n));\n    if (check) {\n\n        // COL32_col = n_id >> 5 ; COL32_row = (m_id << 5) + (n_id & 31);\n        // COL32_idx = (COL32_col << 5) * m + COL32_row = (n_id & 0xffffffe0)*m + (m_id << 5) + (n_id & 31)\n        dst[((n_id & 0xffffffe0) * m + (m_id << 5) + (n_id & 31)) >> 2] = __ldg(src + ((m_id * n + n_id) >> 2));\n    }\n}\n\n// transform row-major to COL32\n// input matrix is (m, n) row-major\n// output matrix is (m, n) COL32\n// n should be a multiple of 32\n// grid((n+31)/32, (m+31)/32)\n// block(8, 32)\nvoid invokeRowMajorToCOL32(int8_t* dst, const int8_t* src, const int m, const int n, hipStream_t stream)\n{\n    assert(n % 32 == 0);\n    rowMajorToCOL32_kernel<<<dim3((n + 31) / 32, (m + 31) / 32), dim3(8, 32), 0, stream>>>(\n        (char4*)dst, (const char4*)src, m, n);\n}\n\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/kernels/layout_transformer_int8_kernels.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cuda_fp8_utils.h\"\n\nnamespace fastertransformer {\n#ifdef ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\n__global__ void quantizeMatrix(T_OUT* output, float const* input_scale, T_IN const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (quantize_mode == QUANTIZE_MODE::PER_CHANNEL) {\n            output[i] = T_OUT((float)(input[i]) * __ldg(input_scale + (i % n)));\n        }\n        else {\n            output[i] = T_OUT((float)(input[i]) * __ldg(input_scale));\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\nvoid invokeQuantizeMatrix(\n    T_OUT* output, float const* input_scale, T_IN const* input, uint32_t size, uint32_t n, cudaStream_t stream)\n{\n    dim3 grid(32);\n    dim3 block(256);\n    quantizeMatrix<T_OUT, T_IN, quantize_mode><<<grid, block, 0, stream>>>(output, input_scale, input, size, n);\n}\n\n#define defineinvokeQuantizeMatrix(type_out, type_in, mode)                                                            \\\n    template void invokeQuantizeMatrix<type_out, type_in, mode>(type_out * output,                                     \\\n                                                                float const*   input_scale,                            \\\n                                                                type_in const* input,                                  \\\n                                                                uint32_t       size,                                   \\\n                                                                uint32_t       n,                                      \\\n                                                                cudaStream_t   stream);\n\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, float, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, float, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, half, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, half, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(half, __nv_fp8_e4m3, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(half, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(float, __nv_fp8_e4m3, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(float, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR);\n#ifdef ENABLE_BF16\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, __nv_bfloat16, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__nv_fp8_e4m3, __nv_bfloat16, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(__nv_bfloat16, __nv_fp8_e4m3, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__nv_bfloat16, __nv_fp8_e4m3, QUANTIZE_MODE::PER_TENSOR);\n#endif\n\ntemplate<typename T_OUT, typename T_IN, typename T_FAKE>\n__global__ void fakeQuantize(T_OUT* dst, const T_IN* src, const int size)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        T_FAKE tmp = (T_FAKE)((float)src[tid]);\n        dst[tid]   = (T_OUT)((float)tmp);\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, typename T_FAKE>\nvoid invokeFakeQuantize(T_OUT* dst, const T_IN* src, const int size, cudaStream_t stream)\n{\n    fakeQuantize<T_OUT, T_IN, T_FAKE><<<256, 256, 0, stream>>>(dst, src, size);\n}\n\ntemplate void\ninvokeFakeQuantize<float, float, __nv_fp8_e4m3>(float* dst, const float* src, const int size, cudaStream_t stream);\ntemplate void\ninvokeFakeQuantize<half, half, __nv_fp8_e4m3>(half* dst, const half* src, const int size, cudaStream_t stream);\ntemplate void invokeFakeQuantize<__nv_bfloat16, __nv_bfloat16, __nv_fp8_e4m3>(__nv_bfloat16*       dst,\n                                                                              const __nv_bfloat16* src,\n                                                                              const int            size,\n                                                                              cudaStream_t         stream);\n\ntemplate<typename T_W>\n__global__ void computeFP8QuantizeScale(float* quant_ptr, const T_W* weights, const int k, const int n)\n{\n    float max = -10000.f;\n    for (int i = 0; i < k; i++) {\n        float val = fabs((float)weights[i * n + blockIdx.x * blockDim.x + threadIdx.x]);\n        max       = max > val ? max : val;\n        if (threadIdx.x == 0 && blockIdx.x == 0 && i % 100 == 0) {\n            printf(\"max: %f, val: %f \\n\", max, val);\n        }\n    }\n    // quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = 1.0f;\n    // quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = FP8_E4M3_MAX / max;\n    quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = std::max(max / FP8_E4M3_MAX, 1.0f / 32.f);\n}\n\ntemplate<typename T_W>\nvoid invokeComputeFP8QuantizeScale(float* quant_ptr, const T_W* weights, const int k, const int n, cudaStream_t stream)\n{\n    dim3 block(256);\n    dim3 grid;\n    grid.x = (n + 255) / 256;\n    computeFP8QuantizeScale<T_W><<<grid, block, 0, stream>>>(quant_ptr, weights, k, n);\n}\n\n#ifdef ENABLE_BF16\ntemplate void invokeComputeFP8QuantizeScale(\n    float* quant_ptr, const __nv_bfloat16* weights, const int k, const int n, cudaStream_t stream);\n#endif\ntemplate void\ninvokeComputeFP8QuantizeScale(float* quant_ptr, const float* weights, const int k, const int n, cudaStream_t stream);\n\n#endif  // ENABLE_FP8\n}  // namespace fastertransformer", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"cuda_fp8_utils.h\"\n\nnamespace fastertransformer {\n#ifdef ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\n__global__ void quantizeMatrix(T_OUT* output, float const* input_scale, T_IN const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (quantize_mode == QUANTIZE_MODE::PER_CHANNEL) {\n            output[i] = T_OUT((float)(input[i]) * __ldg(input_scale + (i % n)));\n        }\n        else {\n            output[i] = T_OUT((float)(input[i]) * __ldg(input_scale));\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, QUANTIZE_MODE quantize_mode>\nvoid invokeQuantizeMatrix(\n    T_OUT* output, float const* input_scale, T_IN const* input, uint32_t size, uint32_t n, hipStream_t stream)\n{\n    dim3 grid(32);\n    dim3 block(256);\n    quantizeMatrix<T_OUT, T_IN, quantize_mode><<<grid, block, 0, stream>>>(output, input_scale, input, size, n);\n}\n\n#define defineinvokeQuantizeMatrix(type_out, type_in, mode)                                                            \\\n    template void invokeQuantizeMatrix<type_out, type_in, mode>(type_out * output,                                     \\\n                                                                float const*   input_scale,                            \\\n                                                                type_in const* input,                                  \\\n                                                                uint32_t       size,                                   \\\n                                                                uint32_t       n,                                      \\\n                                                                hipStream_t   stream);\n\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, float, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, float, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, half, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, half, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(half, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(half, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(float, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(float, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR);\n#ifdef ENABLE_BF16\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, __hip_bfloat16, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__hip_fp8_e4m3_fnuz, __hip_bfloat16, QUANTIZE_MODE::PER_TENSOR);\ndefineinvokeQuantizeMatrix(__hip_bfloat16, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_CHANNEL);\ndefineinvokeQuantizeMatrix(__hip_bfloat16, __hip_fp8_e4m3_fnuz, QUANTIZE_MODE::PER_TENSOR);\n#endif\n\ntemplate<typename T_OUT, typename T_IN, typename T_FAKE>\n__global__ void fakeQuantize(T_OUT* dst, const T_IN* src, const int size)\n{\n    for (int tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        T_FAKE tmp = (T_FAKE)((float)src[tid]);\n        dst[tid]   = (T_OUT)((float)tmp);\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, typename T_FAKE>\nvoid invokeFakeQuantize(T_OUT* dst, const T_IN* src, const int size, hipStream_t stream)\n{\n    fakeQuantize<T_OUT, T_IN, T_FAKE><<<256, 256, 0, stream>>>(dst, src, size);\n}\n\ntemplate void\ninvokeFakeQuantize<float, float, __hip_fp8_e4m3_fnuz>(float* dst, const float* src, const int size, hipStream_t stream);\ntemplate void\ninvokeFakeQuantize<half, half, __hip_fp8_e4m3_fnuz>(half* dst, const half* src, const int size, hipStream_t stream);\ntemplate void invokeFakeQuantize<__hip_bfloat16, __hip_bfloat16, __hip_fp8_e4m3_fnuz>(__hip_bfloat16*       dst,\n                                                                              const __hip_bfloat16* src,\n                                                                              const int            size,\n                                                                              hipStream_t         stream);\n\ntemplate<typename T_W>\n__global__ void computeFP8QuantizeScale(float* quant_ptr, const T_W* weights, const int k, const int n)\n{\n    float max = -10000.f;\n    for (int i = 0; i < k; i++) {\n        float val = fabs((float)weights[i * n + blockIdx.x * blockDim.x + threadIdx.x]);\n        max       = max > val ? max : val;\n        if (threadIdx.x == 0 && blockIdx.x == 0 && i % 100 == 0) {\n            printf(\"max: %f, val: %f \\n\", max, val);\n        }\n    }\n    // quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = 1.0f;\n    // quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = FP8_E4M3_MAX / max;\n    quant_ptr[blockIdx.x * blockDim.x + threadIdx.x] = std::max(max / FP8_E4M3_MAX, 1.0f / 32.f);\n}\n\ntemplate<typename T_W>\nvoid invokeComputeFP8QuantizeScale(float* quant_ptr, const T_W* weights, const int k, const int n, hipStream_t stream)\n{\n    dim3 block(256);\n    dim3 grid;\n    grid.x = (n + 255) / 256;\n    computeFP8QuantizeScale<T_W><<<grid, block, 0, stream>>>(quant_ptr, weights, k, n);\n}\n\n#ifdef ENABLE_BF16\ntemplate void invokeComputeFP8QuantizeScale(\n    float* quant_ptr, const __hip_bfloat16* weights, const int k, const int n, hipStream_t stream);\n#endif\ntemplate void\ninvokeComputeFP8QuantizeScale(float* quant_ptr, const float* weights, const int k, const int n, hipStream_t stream);\n\n#endif  // ENABLE_FP8\n}  // namespace fastertransformer", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/utils/cuda_fp8_utils.cu"}
{"input": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"src/fastertransformer/kernels/cutlass_kernels/cutlass_preprocessors.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n#include <curand_kernel.h>\n#include <sys/stat.h>\n#include <unordered_map>\n\nnamespace fastertransformer {\n\ntemplate<typename T>\nvoid deviceMalloc(T** ptr, size_t size, bool is_random_initialize)\n{\n    FT_CHECK_WITH_INFO(size >= ((size_t)0), \"Ask deviceMalloc size \" + std::to_string(size) + \"< 0 is invalid.\");\n    check_cuda_error(cudaMalloc((void**)(ptr), sizeof(T) * size));\n    if (is_random_initialize) {\n        cudaRandomUniform(*ptr, size);\n    }\n}\n\ntemplate void deviceMalloc(float** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(half** ptr, size_t size, bool is_random_initialize);\n#ifdef ENABLE_BF16\ntemplate void deviceMalloc(__nv_bfloat16** ptr, size_t size, bool is_random_initialize);\n#endif\ntemplate void deviceMalloc(uint16_t** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(int** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(bool** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(char** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(int8_t** ptr, size_t size, bool is_random_initialize);\n#ifdef ENABLE_FP8\ntemplate void deviceMalloc(__nv_fp8_e4m3** ptr, size_t size, bool is_random_initialize);\n#endif\n\ntemplate<typename T>\nvoid deviceMemSetZero(T* ptr, size_t size)\n{\n    check_cuda_error(cudaMemset(static_cast<void*>(ptr), 0, sizeof(T) * size));\n}\n\ntemplate void deviceMemSetZero(float* ptr, size_t size);\ntemplate void deviceMemSetZero(half* ptr, size_t size);\ntemplate void deviceMemSetZero(int* ptr, size_t size);\ntemplate void deviceMemSetZero(uint32_t* ptr, size_t size);\ntemplate void deviceMemSetZero(bool* ptr, size_t size);\n#ifdef ENABLE_FP8\ntemplate void deviceMemSetZero(__nv_fp8_e4m3* ptr, size_t size);\n#endif\n#ifdef ENABLE_BF16\ntemplate void deviceMemSetZero(__nv_bfloat16* ptr, size_t size);\n#endif\n\ntemplate<typename T>\nvoid deviceFree(T*& ptr)\n{\n    if (ptr != NULL) {\n        check_cuda_error(cudaFree(ptr));\n        ptr = NULL;\n    }\n}\n\ntemplate void deviceFree(float*& ptr);\ntemplate void deviceFree(half*& ptr);\n#ifdef ENABLE_BF16\ntemplate void deviceFree(__nv_bfloat16*& ptr);\n#endif\ntemplate void deviceFree(unsigned short*& ptr);\ntemplate void deviceFree(int*& ptr);\ntemplate void deviceFree(bool*& ptr);\ntemplate void deviceFree(char*& ptr);\ntemplate void deviceFree(int8_t*& ptr);\n#ifdef ENABLE_FP8\ntemplate void deviceFree(__nv_fp8_e4m3*& ptr);\n#endif\n\ntemplate<typename T>\nvoid deviceFill(T* devptr, size_t size, T value, cudaStream_t stream)\n{\n    T* arr = new T[size];\n    std::fill(arr, arr + size, value);\n    check_cuda_error(cudaMemcpyAsync(devptr, arr, sizeof(T) * size, cudaMemcpyHostToDevice, stream));\n    delete[] arr;\n}\n\ntemplate void deviceFill(float* devptr, size_t size, float value, cudaStream_t stream);\ntemplate void deviceFill(half* devptr, size_t size, half value, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void deviceFill(__nv_bfloat16* devptr, size_t size, __nv_bfloat16 value, cudaStream_t stream);\n#endif\ntemplate void deviceFill(int* devptr, size_t size, int value, cudaStream_t stream);\ntemplate void deviceFill(bool* devptr, size_t size, bool value, cudaStream_t stream);\n\ntemplate<typename T>\nvoid cudaD2Hcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(cudaMemcpy(tgt, src, sizeof(T) * size, cudaMemcpyDeviceToHost));\n}\n\ntemplate void cudaD2Hcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaD2Hcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaD2Hcpy(__nv_bfloat16* tgt, const __nv_bfloat16* src, size_t size);\n#endif\ntemplate void cudaD2Hcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaD2Hcpy(bool* tgt, const bool* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaD2Hcpy(__nv_fp8_e4m3* tgt, const __nv_fp8_e4m3* src, size_t size);\n#endif\ntemplate void cudaD2Hcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\ntemplate void cudaD2Hcpy(unsigned int* tgt, const unsigned int* src, size_t size);\ntemplate void cudaD2Hcpy(int8_t* tgt, const int8_t* src, size_t size);\n\ntemplate<typename T>\nvoid cudaH2Dcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(cudaMemcpy(tgt, src, sizeof(T) * size, cudaMemcpyHostToDevice));\n}\n\ntemplate void cudaH2Dcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaH2Dcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaH2Dcpy(__nv_bfloat16* tgt, const __nv_bfloat16* src, size_t size);\n#endif\ntemplate void cudaH2Dcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaH2Dcpy(bool* tgt, const bool* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaH2Dcpy(__nv_fp8_e4m3* tgt, const __nv_fp8_e4m3* src, size_t size);\n#endif\ntemplate void cudaH2Dcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\ntemplate void cudaH2Dcpy(unsigned int* tgt, const unsigned int* src, size_t size);\ntemplate void cudaH2Dcpy(int8_t* tgt, const int8_t* src, size_t size);\n\ntemplate<typename T>\nvoid cudaD2Dcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(cudaMemcpy(tgt, src, sizeof(T) * size, cudaMemcpyDeviceToDevice));\n}\n\ntemplate void cudaD2Dcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaD2Dcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaD2Dcpy(__nv_bfloat16* tgt, const __nv_bfloat16* src, size_t size);\n#endif\ntemplate void cudaD2Dcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaD2Dcpy(bool* tgt, const bool* src, size_t size);\ntemplate void cudaD2Dcpy(int8_t* tgt, const int8_t* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaD2Dcpy(__nv_fp8_e4m3* tgt, const __nv_fp8_e4m3* src, size_t size);\n#endif\ntemplate void cudaD2Dcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void cudaCast(T_OUT* dst, T_IN* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (T_OUT)((float)(src[tid]));\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN>\nvoid invokeCudaCast(T_OUT* dst, T_IN const* const src, const size_t size, cudaStream_t stream)\n{\n    cudaCast<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\ntemplate void invokeCudaCast(float* dst, half const* const src, const size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCudaCast(float* dst, __nv_bfloat16 const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_bfloat16* dst, float const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_bfloat16* dst, half const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(half* dst, __nv_bfloat16 const* const src, const size_t size, cudaStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeCudaCast(float* dst, __nv_fp8_e4m3 const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_bfloat16* dst, __nv_fp8_e4m3 const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(half* dst, __nv_fp8_e4m3 const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_fp8_e4m3* dst, float const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_fp8_e4m3* dst, __nv_bfloat16 const* const src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaCast(__nv_fp8_e4m3* dst, half const* const src, const size_t size, cudaStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid cudaAutoCpy(T* tgt, const T* src, const size_t size, cudaStream_t stream)\n{\n    if (stream != NULL) {\n        check_cuda_error(cudaMemcpyAsync(tgt, src, sizeof(T) * size, cudaMemcpyDefault, stream));\n    }\n    else {\n        check_cuda_error(cudaMemcpy(tgt, src, sizeof(T) * size, cudaMemcpyDefault));\n    }\n}\n\ntemplate void cudaAutoCpy(float* tgt, const float* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(half* tgt, const half* src, size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void cudaAutoCpy(__nv_bfloat16* tgt, const __nv_bfloat16* src, size_t size, cudaStream_t stream);\n#endif\ntemplate void cudaAutoCpy(int* tgt, const int* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(bool* tgt, const bool* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(int8_t* tgt, const int8_t* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(uint* tgt, const uint* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(unsigned long long* tgt, const unsigned long long* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(char* tgt, const char* src, size_t size, cudaStream_t stream);\n\ntemplate void cudaAutoCpy(float const** tgt, float const* const* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(half const** tgt, half const* const* src, size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void cudaAutoCpy(__nv_bfloat16 const** tgt, __nv_bfloat16 const* const* src, size_t size, cudaStream_t stream);\n#endif\ntemplate void cudaAutoCpy(int const** tgt, int const* const* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(bool const** tgt, bool const* const* src, size_t size, cudaStream_t stream);\ntemplate void cudaAutoCpy(int8_t const** tgt, int8_t const* const* src, size_t size, cudaStream_t stream);\ntemplate void\ncudaAutoCpy(unsigned long long const** tgt, unsigned long long const* const* src, size_t size, cudaStream_t stream);\n\ntemplate<typename T>\n__global__ void cuda_random_uniform_kernel(T* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    curandState_t local_state;\n    curand_init((unsigned long long int)1337, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = (T)(curand_uniform(&local_state) * 0.2f - 0.1f);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<int>(int* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    curandState_t local_state;\n    curand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = curand(&local_state);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<bool>(bool* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    curandState_t local_state;\n    curand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = (curand(&local_state) % 2 == 0);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<char>(char* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    curandState_t local_state;\n    curand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = curand(&local_state) % 0xFF;\n    }\n}\n\ntemplate<typename T>\nvoid cudaRandomUniform(T* buffer, const size_t size)\n{\n    static int seq_offset = 0;\n    cuda_random_uniform_kernel<T><<<256, 256>>>(buffer, size, seq_offset);\n    seq_offset += 256 * 256;\n}\n\ntemplate void cudaRandomUniform(float* buffer, const size_t size);\ntemplate void cudaRandomUniform(half* buffer, const size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaRandomUniform(__nv_bfloat16* buffer, const size_t size);\n#endif\ntemplate void cudaRandomUniform(int* buffer, const size_t size);\ntemplate void cudaRandomUniform(bool* buffer, const size_t size);\ntemplate void cudaRandomUniform(char* buffer, const size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaRandomUniform(__nv_fp8_e4m3* buffer, const size_t size);\n#endif\n\n// loads data from binary file. If it succeeds, returns a non-empty vector. If loading fails or\n// the product of the elements in shape is 0, this function will return an empty vector.\ntemplate<typename T>\nstd::vector<T> loadWeightFromBinHelper(std::vector<size_t> shape, std::string filename)\n{\n    if (shape.size() > 2) {\n        printf(\"[ERROR] shape should have less than two dims \\n\");\n        return std::vector<T>();\n    }\n    size_t dim0 = shape[0], dim1 = 1;\n    if (shape.size() == 2) {\n        dim1 = shape[1];\n    }\n    size_t size = dim0 * dim1;\n    if (size == 0) {\n        FT_LOG_WARNING(\"shape is zero, skip loading weight from file %s \\n\", filename.c_str());\n        return std::vector<T>();\n    }\n\n    std::vector<T> host_array(size);\n    std::ifstream  in(filename, std::ios::in | std::ios::binary);\n    if (!in.is_open()) {\n        FT_LOG_WARNING(\"file %s cannot be opened, loading model fails! \\n\", filename.c_str());\n        return std::vector<T>();\n    }\n\n    size_t loaded_data_size = sizeof(T) * size;\n    in.seekg(0, in.end);\n    in.seekg(0, in.beg);\n\n    FT_LOG_DEBUG(\"Read \" + std::to_string(loaded_data_size) + \" bytes from \" + filename);\n    in.read((char*)host_array.data(), loaded_data_size);\n\n    size_t in_get_size = in.gcount();\n    if (in_get_size != loaded_data_size) {\n        FT_LOG_WARNING(\"file %s only has %ld, but request %ld, loading model fails! \\n\",\n                       filename.c_str(),\n                       in_get_size,\n                       loaded_data_size);\n        return std::vector<T>();\n    }\n    in.close();\n    // If we succeed, return an array with values.\n    return host_array;\n}\n\ntemplate<typename T, typename T_IN>\nint loadWeightFromBinFunc(T* ptr, std::vector<size_t> shape, std::string filename)\n{\n    std::vector<T_IN> host_array = loadWeightFromBinHelper<T_IN>(shape, filename);\n\n    if (host_array.empty()) {\n        return 0;\n    }\n\n    if (std::is_same<T, T_IN>::value == true) {\n        cudaH2Dcpy(ptr, (T*)host_array.data(), host_array.size());\n    }\n    else {\n        T_IN* ptr_2 = nullptr;\n        deviceMalloc(&ptr_2, host_array.size(), false);\n        cudaH2Dcpy(ptr_2, host_array.data(), host_array.size());\n        invokeCudaD2DcpyConvert(ptr, ptr_2, host_array.size());\n        deviceFree(ptr_2);\n    }\n    return 0;\n}\n\ntemplate int loadWeightFromBinFunc<float, float>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, float>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<float, half>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, half>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<int8_t, int8_t>(int8_t* ptr, std::vector<size_t> shape, std::string filename);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBinFunc<__nv_bfloat16, float>(__nv_bfloat16* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int\nloadWeightFromBinFunc<__nv_bfloat16, half>(__nv_bfloat16* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<float, __nv_bfloat16>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, __nv_bfloat16>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<__nv_bfloat16, __nv_bfloat16>(__nv_bfloat16*      ptr,\n                                                                 std::vector<size_t> shape,\n                                                                 std::string         filename);\n#endif  // ENABLE_BF16\ntemplate int loadWeightFromBinFunc<int, int>(int* ptr, std::vector<size_t> shape, std::string filename);\n#ifdef ENABLE_FP8\ntemplate int\nloadWeightFromBinFunc<__nv_fp8_e4m3, float>(__nv_fp8_e4m3* ptr, std::vector<size_t> shape, std::string filename);\n#endif  // ENABLE_FP8\n\ntemplate<typename T>\nint loadWeightFromBin(T* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type)\n{\n    switch (model_file_type) {\n        case FtCudaDataType::FP32:\n            loadWeightFromBinFunc<T, float>(ptr, shape, filename);\n            break;\n        case FtCudaDataType::FP16:\n            loadWeightFromBinFunc<T, half>(ptr, shape, filename);\n            break;\n        case FtCudaDataType::INT8:\n            loadWeightFromBinFunc<T, int8_t>(ptr, shape, filename);\n            break;\n#ifdef ENABLE_BF16\n        case FtCudaDataType::BF16:\n            loadWeightFromBinFunc<T, __nv_bfloat16>(ptr, shape, filename);\n            break;\n#endif\n#ifdef ENABLE_FP8\n        case FtCudaDataType::FP8:\n            loadWeightFromBinFunc<T, float>(ptr, shape, filename);\n            break;\n#endif\n        default:\n            FT_LOG_ERROR(\"Does not support FtCudaDataType=%d\", model_file_type);\n            FT_CHECK(false);\n    }\n    return 0;\n}\n\ntemplate<>\nint loadWeightFromBin(int* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type)\n{\n    loadWeightFromBinFunc<int, int>(ptr, shape, filename);\n    return 0;\n}\n\ntemplate int\nloadWeightFromBin(float* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\ntemplate int\nloadWeightFromBin(half* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\ntemplate int\nloadWeightFromBin(int8_t* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBin(__nv_bfloat16* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#endif\n#ifdef ENABLE_FP8\ntemplate int\nloadWeightFromBin(__nv_fp8_e4m3* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#endif\ntemplate int\nloadWeightFromBin(int* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n\ntemplate<typename T, typename T_IN>\nint loadWeightFromBinAndQuantizeForWeightOnlyFunc(int8_t*             ptr,\n                                                  T*                  scales_ptr,\n                                                  std::vector<size_t> shape,\n                                                  std::string         filename)\n{\n    FT_LOG_INFO(std::string(\"Loading and quantizing weight from file: \") + filename);\n    FT_CHECK_WITH_INFO(shape.size() == 2, \"We can only use this function to dequantize a weight matrix.\");\n    std::vector<T_IN> host_array = loadWeightFromBinHelper<T_IN>(shape, filename);\n\n    if (host_array.empty()) {\n        return 0;\n    }\n\n    const size_t        num_elts = shape[0] * shape[1];\n    std::vector<int8_t> host_quantized_weight_buf(num_elts);\n    std::vector<T>      host_scales_buf(shape[1]);\n\n    // Note: This function preprocesses the weights to a special format for weight only quant!\n    symmetric_quantize<T, T_IN>(host_quantized_weight_buf.data(),\n                                host_scales_buf.data(),\n                                host_array.data(),\n                                shape,\n                                QuantType::INT8_WEIGHT_ONLY);\n\n    cudaH2Dcpy(ptr, (int8_t*)host_quantized_weight_buf.data(), host_quantized_weight_buf.size());\n    cudaH2Dcpy(scales_ptr, (T*)host_scales_buf.data(), host_scales_buf.size());\n\n    return 0;\n}\n\ntemplate<typename T>\nint loadWeightFromBinAndQuantizeForWeightOnly(int8_t*             quantized_weight_ptr,\n                                              T*                  scale_ptr,\n                                              std::vector<size_t> shape,\n                                              std::string         filename,\n                                              FtCudaDataType      model_file_type)\n{\n    switch (model_file_type) {\n        case FtCudaDataType::FP32:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, float>(quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n        case FtCudaDataType::FP16:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, half>(quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n#ifdef ENABLE_BF16\n        case FtCudaDataType::BF16:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, __nv_bfloat16>(\n                quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n#endif\n        default:\n            FT_LOG_ERROR(\"Does not support FtCudaDataType=%d\", model_file_type);\n            FT_CHECK(false);\n    }\n    return 0;\n}\n\ntemplate<>\nint loadWeightFromBinAndQuantizeForWeightOnly(int8_t*             quantized_weight_ptr,\n                                              float*              scale_ptr,\n                                              std::vector<size_t> shape,\n                                              std::string         filename,\n                                              FtCudaDataType      model_file_type)\n{\n    FT_CHECK_WITH_INFO(false, \"Weight only quant not supported with FP32 compute.\");\n    return 0;\n}\n\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, float*, std::vector<size_t>, std::string, FtCudaDataType);\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, half*, std::vector<size_t>, std::string, FtCudaDataType);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, __nv_bfloat16*, std::vector<size_t>, std::string, FtCudaDataType);\n#endif\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void cudaD2DcpyConvert(T_OUT* dst, const T_IN* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = cuda_cast<T_OUT>(src[tid]);\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeCudaD2DcpyConvert(T_OUT* tgt, const T_IN* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2DcpyConvert<<<256, 256, 0, stream>>>(tgt, src, size);\n}\n\ntemplate void invokeCudaD2DcpyConvert(int8_t* tgt, const float* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const int8_t* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const int* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(half* tgt, const int* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const float* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(half* tgt, const float* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const half* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(uint* tgt, const int* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const uint* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const float* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const half* src, const size_t size, cudaStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeCudaD2DcpyConvert(__nv_bfloat16* tgt, const float* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(__nv_bfloat16* tgt, const int* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const __nv_bfloat16* src, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const __nv_bfloat16* src, const size_t size, cudaStream_t stream);\n#endif  // ENABLE_BF16\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void\ncudaD2DScaleCpyConvert(T_OUT* dst, const T_IN* src, const float* scale, bool invert_scale, const size_t size)\n{\n    const float scale_value = invert_scale ? 1.0f / scale[0] : scale[0];\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = cuda_cast<T_OUT>(cuda_cast<float>(src[tid]) * scale_value);\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeCudaD2DScaleCpyConvert(\n    T_OUT* tgt, const T_IN* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream)\n{\n    cudaD2DScaleCpyConvert<<<256, 256, 0, stream>>>(tgt, src, scale, invert_scale, size);\n}\n\n// clang-format off\ntemplate void invokeCudaD2DScaleCpyConvert(float* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const float* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(half* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const half* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCudaD2DScaleCpyConvert(__nv_bfloat16* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const __nv_bfloat16* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\n#endif  // ENABLE_BF16\n#ifdef ENABLE_FP8\ntemplate void invokeCudaD2DScaleCpyConvert(float* tgt, const __nv_fp8_e4m3* src, const float* scale, bool invert_scale, const size_t size, cudaStream_t stream);\n#endif  // ENABLE_FP8\n// clang-format on\n\nvoid invokeCudaD2DcpyHalf2Float(float* dst, half* src, const size_t size, cudaStream_t stream)\n{\n    invokeCudaD2DcpyConvert(dst, src, size, stream);\n}\n\nvoid invokeCudaD2DcpyFloat2Half(half* dst, float* src, const size_t size, cudaStream_t stream)\n{\n    invokeCudaD2DcpyConvert(dst, src, size, stream);\n}\n\ntemplate<typename T>\nvoid saveToBinary(const T* ptr, const size_t size, std::string filename)\n{\n\n    std::vector<T> h_ptr(size);\n    cudaD2Hcpy(h_ptr.data(), ptr, size);\n    std::vector<float> float_ptr(size);\n    for (int i = 0; i < size; i++) {\n        float_ptr[i] = (float)h_ptr[i];\n    }\n\n    std::ofstream out(filename, std::ios::out | std::ios::binary);\n    FT_CHECK_WITH_INFO(out.is_open(), \"Fail to open file \" + filename);\n\n    out.write((char*)float_ptr.data(), size * sizeof(float));\n}\n\ntemplate void saveToBinary(const float* ptr, const size_t size, std::string filename);\ntemplate void saveToBinary(const half* ptr, const size_t size, std::string filename);\n#ifdef ENABLE_BF16\ntemplate void saveToBinary(const __nv_bfloat16* ptr, const size_t size, std::string filename);\n#endif  // ENABLE_BF16\n\ntemplate<>\nvoid saveToBinary(const int* ptr, const size_t size, std::string filename)\n{\n    std::vector<int> h_ptr(size);\n    cudaD2Hcpy(h_ptr.data(), ptr, size);\n    std::ofstream out(filename, std::ios::out | std::ios::binary);\n    FT_CHECK_WITH_INFO(out.is_open(), \"Fail to open file \" + filename);\n    out.write((char*)h_ptr.data(), size * sizeof(int));\n}\n\ntemplate<typename T_IN, typename T_fake_type>\n__global__ void fakeCast(T_IN* input_ptr, const size_t size)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i += blockDim.x * gridDim.x) {\n        T_fake_type tmp_val = (T_fake_type)((float)input_ptr[i]);\n        input_ptr[i]        = (T_IN)((float)tmp_val);\n    }\n}\n\ntemplate<typename T_IN, typename T_fake_type>\nvoid invokeFakeCast(T_IN* input_ptr, const size_t size, cudaStream_t stream)\n{\n    dim3 block(256);\n    dim3 grid((size + 255) / 256);\n    fakeCast<T_IN, T_fake_type><<<grid, block, 0, stream>>>(input_ptr, size);\n}\n\n#ifdef ENABLE_FP8\n__global__ void cudaD2Dcpyfp82Float(float* dst, __nv_fp8_e4m3* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (float)(src[tid]);\n    }\n}\n\nvoid invokeCudaD2Dcpyfp82Float(float* dst, __nv_fp8_e4m3* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2Dcpyfp82Float<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2Dcpyfp82Half(half* dst, __nv_fp8_e4m3* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (half)((float)(src[tid]));\n    }\n}\n\nvoid invokeCudaD2Dcpyfp82Half(half* dst, __nv_fp8_e4m3* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2Dcpyfp82Half<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyFloat2fp8(__nv_fp8_e4m3* dst, float* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__nv_fp8_e4m3)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyFloat2fp8(__nv_fp8_e4m3* dst, float* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2DcpyFloat2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyHalf2fp8(__nv_fp8_e4m3* dst, half* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__nv_fp8_e4m3)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyHalf2fp8(__nv_fp8_e4m3* dst, half* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2DcpyHalf2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyBfloat2fp8(__nv_fp8_e4m3* dst, __nv_bfloat16* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__nv_fp8_e4m3)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyBfloat2fp8(__nv_fp8_e4m3* dst, __nv_bfloat16* src, const size_t size, cudaStream_t stream)\n{\n    cudaD2DcpyBfloat2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n#endif  // ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose(T_OUT* dst, T_IN* src, const int dim0, const int dim1)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1; tid += blockDim.x * gridDim.x) {\n        const int src_col_id                = tid % dim1;\n        const int src_row_id                = tid / dim1;\n        dst[src_col_id * dim0 + src_row_id] = (T_OUT)(src[tid]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose(T* data, T* workspace, const int dim0, const int dim1)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    cudaD2Dcpy(workspace, data, dim0 * dim1);\n    transpose<<<256, 256>>>(data, workspace, dim0, dim1);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose(__nv_fp8_e4m3* data, __nv_fp8_e4m3* workspace, const int dim0, const int dim1);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose(__nv_bfloat16* data, __nv_bfloat16* workspace, const int dim0, const int dim1);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose(float* data, float* workspace, const int dim0, const int dim1);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose0213(T_OUT* dst, T_IN* src, const int dim0, const int dim1, const int dim2, const int dim3)\n{\n    // src permutation: [0, 1, 2, 3]\n    // dst permutation: [0, 2, 1, 3]\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1 * dim2 * dim3;\n         tid += blockDim.x * gridDim.x) {\n        int       tmp_idx   = tid;\n        const int dim_3_idx = tmp_idx % dim3;\n        tmp_idx             = (tmp_idx - dim_3_idx) / dim3;\n        const int dim_2_idx = tmp_idx % dim2;\n        tmp_idx             = (tmp_idx - dim_2_idx) / dim2;\n        const int dim_1_idx = tmp_idx % dim1;\n        tmp_idx             = (tmp_idx - dim_1_idx) / dim1;\n        const int dim_0_idx = tmp_idx % dim0;\n        dst[dim_0_idx * dim1 * dim2 * dim3 + dim_2_idx * dim1 * dim3 + dim_1_idx * dim3 + dim_3_idx] = src[tid];\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose0213(T* data, T* workspace, const int dim0, const int dim1, const int dim2, const int dim3)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    // Note that this kernel is used for pre-processing and not very efficient.\n    cudaD2Dcpy(workspace, data, dim0 * dim1 * dim2 * dim3);\n    transpose0213<<<256, 256>>>(data, workspace, dim0, dim1, dim2, dim3);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose0213(\n    __nv_fp8_e4m3* data, __nv_fp8_e4m3* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose0213(\n    __nv_bfloat16* data, __nv_bfloat16* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose0213(\n    float* data, float* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose102(T_OUT* dst, T_IN* src, const int dim0, const int dim1, const int dim2)\n{\n    // src permutation: [0, 1, 2]\n    // dst permutation: [1, 0, 2]\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1 * dim2; tid += blockDim.x * gridDim.x) {\n        int       tmp_idx                                           = tid;\n        const int dim_2_idx                                         = tmp_idx % dim2;\n        tmp_idx                                                     = (tmp_idx - dim_2_idx) / dim2;\n        const int dim_1_idx                                         = tmp_idx % dim1;\n        tmp_idx                                                     = (tmp_idx - dim_1_idx) / dim1;\n        const int dim_0_idx                                         = tmp_idx % dim0;\n        dst[dim_1_idx * dim0 * dim2 + dim_0_idx * dim2 + dim_2_idx] = src[tid];\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose102(T* data, T* workspace, const int dim0, const int dim1, const int dim2)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    // Note that this kernel is used for pre-processing and not very efficient.\n    cudaD2Dcpy(workspace, data, dim0 * dim1 * dim2);\n    transpose102<<<256, 256>>>(data, workspace, dim0, dim1, dim2);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose102(\n    __nv_fp8_e4m3* data, __nv_fp8_e4m3* workspace, const int dim0, const int dim1, const int dim2);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose102(\n    __nv_bfloat16* data, __nv_bfloat16* workspace, const int dim0, const int dim1, const int dim2);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose102(float* data, float* workspace, const int dim0, const int dim1, const int dim2);\n\ntemplate<typename T>\nvoid __global__ multiplyScale(T* tensor, float scale, const size_t size)\n{\n    for (size_t index = threadIdx.x + blockIdx.x * blockDim.x; index < size; index += blockDim.x * gridDim.x) {\n        tensor[index] = (T)(((float)tensor[index]) * scale);\n    }\n}\n\ntemplate<typename T>\nvoid invokeMultiplyScale(T* tensor, float scale, const size_t size, cudaStream_t stream)\n{\n    int block = 256;\n    int grid  = (size + 255) / 256;\n    multiplyScale<<<grid, block, 0, stream>>>(tensor, scale, size);\n}\n\ntemplate void invokeMultiplyScale(float* tensor, float scale, const size_t size, cudaStream_t stream);\ntemplate void invokeMultiplyScale(half* tensor, float scale, const size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMultiplyScale(__nv_bfloat16* tensor, float scale, const size_t size, cudaStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeMultiplyScale(__nv_fp8_e4m3* tensor, float scale, const size_t size, cudaStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid __global__ divideScale(T* tensor, float scale, const size_t size)\n{\n    for (size_t index = threadIdx.x + blockIdx.x * blockDim.x; index < size; index += blockDim.x * gridDim.x) {\n        tensor[index] = (T)(((float)tensor[index]) / scale);\n    }\n}\n\ntemplate<typename T>\nvoid invokeDivideScale(T* tensor, float scale, const size_t size, cudaStream_t stream)\n{\n    int block = 256;\n    int grid  = (size + 255) / 256;\n    divideScale<<<grid, block, 0, stream>>>(tensor, scale, size);\n}\n\ntemplate void invokeDivideScale(float* tensor, float scale, const size_t size, cudaStream_t stream);\ntemplate void invokeDivideScale(half* tensor, float scale, const size_t size, cudaStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeDivideScale(__nv_bfloat16* tensor, float scale, const size_t size, cudaStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeDivideScale(__nv_fp8_e4m3* tensor, float scale, const size_t size, cudaStream_t stream);\n#endif\n#ifdef ENABLE_BF16\ntemplate void invokeFakeCast<float, __nv_bfloat16>(float* input_ptr, const size_t size, cudaStream_t stream);\ntemplate void\ninvokeFakeCast<__nv_bfloat16, __nv_bfloat16>(__nv_bfloat16* input_ptr, const size_t size, cudaStream_t stream);\ntemplate void invokeFakeCast<half, __nv_bfloat16>(half* input_ptr, const size_t size, cudaStream_t stream);\n#endif\ntemplate void invokeFakeCast<float, half>(float* input_ptr, const size_t size, cudaStream_t stream);\ntemplate void invokeFakeCast<float, float>(float* input_ptr, const size_t size, cudaStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeFakeCast<float, __nv_fp8_e4m3>(float* input_ptr, const size_t size, cudaStream_t stream);\ntemplate void invokeFakeCast<half, __nv_fp8_e4m3>(half* input_ptr, const size_t size, cudaStream_t stream);\ntemplate void\ninvokeFakeCast<__nv_bfloat16, __nv_fp8_e4m3>(__nv_bfloat16* input_ptr, const size_t size, cudaStream_t stream);\n#endif\n\nsize_t cuda_datatype_size(FtCudaDataType dt)\n{\n    static const std::unordered_map<FtCudaDataType, size_t> sizes{{FtCudaDataType::FP32, sizeof(float)},\n                                                                  {FtCudaDataType::FP16, sizeof(half)}\n#ifdef ENABLE_BF16\n                                                                  ,\n                                                                  {FtCudaDataType::BF16, sizeof(__nv_bfloat16)}\n#endif\n    };\n\n    return sizes.at(dt);\n}\n\ntemplate<typename T>\n__global__ void check_range(T* buffer, size_t size, T min, T max, bool* d_within_range)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i += blockDim.x * gridDim.x) {\n        const T val = buffer[i];\n        if (val < min || val > max) {\n            *d_within_range = false;\n        }\n    }\n}\n\ntemplate<typename T>\nbool invokeCheckRange(T* buffer, const size_t size, T min, T max, bool* d_within_range, cudaStream_t stream)\n{\n    cudaMemsetAsync(d_within_range, true, sizeof(bool), stream);\n\n    dim3 block(256);\n    dim3 grid((size + 255) / 256);\n    check_range<T><<<grid, block, 0, stream>>>(buffer, size, min, max, d_within_range);\n\n    bool result;\n    cudaD2Hcpy(&result, d_within_range, 1);\n    return result;\n}\n\ntemplate bool\ninvokeCheckRange<int>(int* buffer, const size_t size, int min, int max, bool* d_within_range, cudaStream_t stream);\n\n}  // namespace fastertransformer\n", "output": "/*\n * Copyright (c) 2019-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"src/fastertransformer/kernels/cutlass_kernels/cutlass_preprocessors.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cuda_type_utils.cuh\"\n#include \"src/fastertransformer/utils/logger.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n#include <hiprand/hiprand_kernel.h>\n#include <sys/stat.h>\n#include <unordered_map>\n\nnamespace fastertransformer {\n\ntemplate<typename T>\nvoid deviceMalloc(T** ptr, size_t size, bool is_random_initialize)\n{\n    FT_CHECK_WITH_INFO(size >= ((size_t)0), \"Ask deviceMalloc size \" + std::to_string(size) + \"< 0 is invalid.\");\n    check_cuda_error(hipMalloc((void**)(ptr), sizeof(T) * size));\n    if (is_random_initialize) {\n        cudaRandomUniform(*ptr, size);\n    }\n}\n\ntemplate void deviceMalloc(float** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(half** ptr, size_t size, bool is_random_initialize);\n#ifdef ENABLE_BF16\ntemplate void deviceMalloc(__hip_bfloat16** ptr, size_t size, bool is_random_initialize);\n#endif\ntemplate void deviceMalloc(uint16_t** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(int** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(bool** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(char** ptr, size_t size, bool is_random_initialize);\ntemplate void deviceMalloc(int8_t** ptr, size_t size, bool is_random_initialize);\n#ifdef ENABLE_FP8\ntemplate void deviceMalloc(__hip_fp8_e4m3_fnuz** ptr, size_t size, bool is_random_initialize);\n#endif\n\ntemplate<typename T>\nvoid deviceMemSetZero(T* ptr, size_t size)\n{\n    check_cuda_error(hipMemset(static_cast<void*>(ptr), 0, sizeof(T) * size));\n}\n\ntemplate void deviceMemSetZero(float* ptr, size_t size);\ntemplate void deviceMemSetZero(half* ptr, size_t size);\ntemplate void deviceMemSetZero(int* ptr, size_t size);\ntemplate void deviceMemSetZero(uint32_t* ptr, size_t size);\ntemplate void deviceMemSetZero(bool* ptr, size_t size);\n#ifdef ENABLE_FP8\ntemplate void deviceMemSetZero(__hip_fp8_e4m3_fnuz* ptr, size_t size);\n#endif\n#ifdef ENABLE_BF16\ntemplate void deviceMemSetZero(__hip_bfloat16* ptr, size_t size);\n#endif\n\ntemplate<typename T>\nvoid deviceFree(T*& ptr)\n{\n    if (ptr != NULL) {\n        check_cuda_error(hipFree(ptr));\n        ptr = NULL;\n    }\n}\n\ntemplate void deviceFree(float*& ptr);\ntemplate void deviceFree(half*& ptr);\n#ifdef ENABLE_BF16\ntemplate void deviceFree(__hip_bfloat16*& ptr);\n#endif\ntemplate void deviceFree(unsigned short*& ptr);\ntemplate void deviceFree(int*& ptr);\ntemplate void deviceFree(bool*& ptr);\ntemplate void deviceFree(char*& ptr);\ntemplate void deviceFree(int8_t*& ptr);\n#ifdef ENABLE_FP8\ntemplate void deviceFree(__hip_fp8_e4m3_fnuz*& ptr);\n#endif\n\ntemplate<typename T>\nvoid deviceFill(T* devptr, size_t size, T value, hipStream_t stream)\n{\n    T* arr = new T[size];\n    std::fill(arr, arr + size, value);\n    check_cuda_error(hipMemcpyAsync(devptr, arr, sizeof(T) * size, hipMemcpyHostToDevice, stream));\n    delete[] arr;\n}\n\ntemplate void deviceFill(float* devptr, size_t size, float value, hipStream_t stream);\ntemplate void deviceFill(half* devptr, size_t size, half value, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void deviceFill(__hip_bfloat16* devptr, size_t size, __hip_bfloat16 value, hipStream_t stream);\n#endif\ntemplate void deviceFill(int* devptr, size_t size, int value, hipStream_t stream);\ntemplate void deviceFill(bool* devptr, size_t size, bool value, hipStream_t stream);\n\ntemplate<typename T>\nvoid cudaD2Hcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(hipMemcpy(tgt, src, sizeof(T) * size, hipMemcpyDeviceToHost));\n}\n\ntemplate void cudaD2Hcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaD2Hcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaD2Hcpy(__hip_bfloat16* tgt, const __hip_bfloat16* src, size_t size);\n#endif\ntemplate void cudaD2Hcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaD2Hcpy(bool* tgt, const bool* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaD2Hcpy(__hip_fp8_e4m3_fnuz* tgt, const __hip_fp8_e4m3_fnuz* src, size_t size);\n#endif\ntemplate void cudaD2Hcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\ntemplate void cudaD2Hcpy(unsigned int* tgt, const unsigned int* src, size_t size);\ntemplate void cudaD2Hcpy(int8_t* tgt, const int8_t* src, size_t size);\n\ntemplate<typename T>\nvoid cudaH2Dcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(hipMemcpy(tgt, src, sizeof(T) * size, hipMemcpyHostToDevice));\n}\n\ntemplate void cudaH2Dcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaH2Dcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaH2Dcpy(__hip_bfloat16* tgt, const __hip_bfloat16* src, size_t size);\n#endif\ntemplate void cudaH2Dcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaH2Dcpy(bool* tgt, const bool* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaH2Dcpy(__hip_fp8_e4m3_fnuz* tgt, const __hip_fp8_e4m3_fnuz* src, size_t size);\n#endif\ntemplate void cudaH2Dcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\ntemplate void cudaH2Dcpy(unsigned int* tgt, const unsigned int* src, size_t size);\ntemplate void cudaH2Dcpy(int8_t* tgt, const int8_t* src, size_t size);\n\ntemplate<typename T>\nvoid cudaD2Dcpy(T* tgt, const T* src, const size_t size)\n{\n    check_cuda_error(hipMemcpy(tgt, src, sizeof(T) * size, hipMemcpyDeviceToDevice));\n}\n\ntemplate void cudaD2Dcpy(float* tgt, const float* src, size_t size);\ntemplate void cudaD2Dcpy(half* tgt, const half* src, size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaD2Dcpy(__hip_bfloat16* tgt, const __hip_bfloat16* src, size_t size);\n#endif\ntemplate void cudaD2Dcpy(int* tgt, const int* src, size_t size);\ntemplate void cudaD2Dcpy(bool* tgt, const bool* src, size_t size);\ntemplate void cudaD2Dcpy(int8_t* tgt, const int8_t* src, size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaD2Dcpy(__hip_fp8_e4m3_fnuz* tgt, const __hip_fp8_e4m3_fnuz* src, size_t size);\n#endif\ntemplate void cudaD2Dcpy(unsigned long long* tgt, const unsigned long long* src, size_t size);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void cudaCast(T_OUT* dst, T_IN* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (T_OUT)((float)(src[tid]));\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN>\nvoid invokeCudaCast(T_OUT* dst, T_IN const* const src, const size_t size, hipStream_t stream)\n{\n    cudaCast<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\ntemplate void invokeCudaCast(float* dst, half const* const src, const size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCudaCast(float* dst, __hip_bfloat16 const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_bfloat16* dst, float const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_bfloat16* dst, half const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(half* dst, __hip_bfloat16 const* const src, const size_t size, hipStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeCudaCast(float* dst, __hip_fp8_e4m3_fnuz const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_bfloat16* dst, __hip_fp8_e4m3_fnuz const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(half* dst, __hip_fp8_e4m3_fnuz const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_fp8_e4m3_fnuz* dst, float const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_fp8_e4m3_fnuz* dst, __hip_bfloat16 const* const src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaCast(__hip_fp8_e4m3_fnuz* dst, half const* const src, const size_t size, hipStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid cudaAutoCpy(T* tgt, const T* src, const size_t size, hipStream_t stream)\n{\n    if (stream != NULL) {\n        check_cuda_error(hipMemcpyAsync(tgt, src, sizeof(T) * size, hipMemcpyDefault, stream));\n    }\n    else {\n        check_cuda_error(hipMemcpy(tgt, src, sizeof(T) * size, hipMemcpyDefault));\n    }\n}\n\ntemplate void cudaAutoCpy(float* tgt, const float* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(half* tgt, const half* src, size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void cudaAutoCpy(__hip_bfloat16* tgt, const __hip_bfloat16* src, size_t size, hipStream_t stream);\n#endif\ntemplate void cudaAutoCpy(int* tgt, const int* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(bool* tgt, const bool* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(int8_t* tgt, const int8_t* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(uint* tgt, const uint* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(unsigned long long* tgt, const unsigned long long* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(char* tgt, const char* src, size_t size, hipStream_t stream);\n\ntemplate void cudaAutoCpy(float const** tgt, float const* const* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(half const** tgt, half const* const* src, size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void cudaAutoCpy(__hip_bfloat16 const** tgt, __hip_bfloat16 const* const* src, size_t size, hipStream_t stream);\n#endif\ntemplate void cudaAutoCpy(int const** tgt, int const* const* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(bool const** tgt, bool const* const* src, size_t size, hipStream_t stream);\ntemplate void cudaAutoCpy(int8_t const** tgt, int8_t const* const* src, size_t size, hipStream_t stream);\ntemplate void\ncudaAutoCpy(unsigned long long const** tgt, unsigned long long const* const* src, size_t size, hipStream_t stream);\n\ntemplate<typename T>\n__global__ void cuda_random_uniform_kernel(T* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    hiprandState_t local_state;\n    hiprand_init((unsigned long long int)1337, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = (T)(hiprand_uniform(&local_state) * 0.2f - 0.1f);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<int>(int* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    hiprandState_t local_state;\n    hiprand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = hiprand(&local_state);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<bool>(bool* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    hiprandState_t local_state;\n    hiprand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = (hiprand(&local_state) % 2 == 0);\n    }\n}\n\ntemplate<>\n__global__ void cuda_random_uniform_kernel<char>(char* buffer, const size_t size, const int seq_offset)\n{\n    const int     idx = blockIdx.x * blockDim.x + threadIdx.x;\n    hiprandState_t local_state;\n    hiprand_init((float)1337.f, idx + seq_offset, 0, &local_state);\n    for (size_t index = idx; index < size; index += blockDim.x * gridDim.x) {\n        buffer[index] = hiprand(&local_state) % 0xFF;\n    }\n}\n\ntemplate<typename T>\nvoid cudaRandomUniform(T* buffer, const size_t size)\n{\n    static int seq_offset = 0;\n    cuda_random_uniform_kernel<T><<<256, 256>>>(buffer, size, seq_offset);\n    seq_offset += 256 * 256;\n}\n\ntemplate void cudaRandomUniform(float* buffer, const size_t size);\ntemplate void cudaRandomUniform(half* buffer, const size_t size);\n#ifdef ENABLE_BF16\ntemplate void cudaRandomUniform(__hip_bfloat16* buffer, const size_t size);\n#endif\ntemplate void cudaRandomUniform(int* buffer, const size_t size);\ntemplate void cudaRandomUniform(bool* buffer, const size_t size);\ntemplate void cudaRandomUniform(char* buffer, const size_t size);\n#ifdef ENABLE_FP8\ntemplate void cudaRandomUniform(__hip_fp8_e4m3_fnuz* buffer, const size_t size);\n#endif\n\n// loads data from binary file. If it succeeds, returns a non-empty vector. If loading fails or\n// the product of the elements in shape is 0, this function will return an empty vector.\ntemplate<typename T>\nstd::vector<T> loadWeightFromBinHelper(std::vector<size_t> shape, std::string filename)\n{\n    if (shape.size() > 2) {\n        printf(\"[ERROR] shape should have less than two dims \\n\");\n        return std::vector<T>();\n    }\n    size_t dim0 = shape[0], dim1 = 1;\n    if (shape.size() == 2) {\n        dim1 = shape[1];\n    }\n    size_t size = dim0 * dim1;\n    if (size == 0) {\n        FT_LOG_WARNING(\"shape is zero, skip loading weight from file %s \\n\", filename.c_str());\n        return std::vector<T>();\n    }\n\n    std::vector<T> host_array(size);\n    std::ifstream  in(filename, std::ios::in | std::ios::binary);\n    if (!in.is_open()) {\n        FT_LOG_WARNING(\"file %s cannot be opened, loading model fails! \\n\", filename.c_str());\n        return std::vector<T>();\n    }\n\n    size_t loaded_data_size = sizeof(T) * size;\n    in.seekg(0, in.end);\n    in.seekg(0, in.beg);\n\n    FT_LOG_DEBUG(\"Read \" + std::to_string(loaded_data_size) + \" bytes from \" + filename);\n    in.read((char*)host_array.data(), loaded_data_size);\n\n    size_t in_get_size = in.gcount();\n    if (in_get_size != loaded_data_size) {\n        FT_LOG_WARNING(\"file %s only has %ld, but request %ld, loading model fails! \\n\",\n                       filename.c_str(),\n                       in_get_size,\n                       loaded_data_size);\n        return std::vector<T>();\n    }\n    in.close();\n    // If we succeed, return an array with values.\n    return host_array;\n}\n\ntemplate<typename T, typename T_IN>\nint loadWeightFromBinFunc(T* ptr, std::vector<size_t> shape, std::string filename)\n{\n    std::vector<T_IN> host_array = loadWeightFromBinHelper<T_IN>(shape, filename);\n\n    if (host_array.empty()) {\n        return 0;\n    }\n\n    if (std::is_same<T, T_IN>::value == true) {\n        cudaH2Dcpy(ptr, (T*)host_array.data(), host_array.size());\n    }\n    else {\n        T_IN* ptr_2 = nullptr;\n        deviceMalloc(&ptr_2, host_array.size(), false);\n        cudaH2Dcpy(ptr_2, host_array.data(), host_array.size());\n        invokeCudaD2DcpyConvert(ptr, ptr_2, host_array.size());\n        deviceFree(ptr_2);\n    }\n    return 0;\n}\n\ntemplate int loadWeightFromBinFunc<float, float>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, float>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<float, half>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, half>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<int8_t, int8_t>(int8_t* ptr, std::vector<size_t> shape, std::string filename);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBinFunc<__hip_bfloat16, float>(__hip_bfloat16* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int\nloadWeightFromBinFunc<__hip_bfloat16, half>(__hip_bfloat16* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<float, __hip_bfloat16>(float* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<half, __hip_bfloat16>(half* ptr, std::vector<size_t> shape, std::string filename);\ntemplate int loadWeightFromBinFunc<__hip_bfloat16, __hip_bfloat16>(__hip_bfloat16*      ptr,\n                                                                 std::vector<size_t> shape,\n                                                                 std::string         filename);\n#endif  // ENABLE_BF16\ntemplate int loadWeightFromBinFunc<int, int>(int* ptr, std::vector<size_t> shape, std::string filename);\n#ifdef ENABLE_FP8\ntemplate int\nloadWeightFromBinFunc<__hip_fp8_e4m3_fnuz, float>(__hip_fp8_e4m3_fnuz* ptr, std::vector<size_t> shape, std::string filename);\n#endif  // ENABLE_FP8\n\ntemplate<typename T>\nint loadWeightFromBin(T* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type)\n{\n    switch (model_file_type) {\n        case FtCudaDataType::FP32:\n            loadWeightFromBinFunc<T, float>(ptr, shape, filename);\n            break;\n        case FtCudaDataType::FP16:\n            loadWeightFromBinFunc<T, half>(ptr, shape, filename);\n            break;\n        case FtCudaDataType::INT8:\n            loadWeightFromBinFunc<T, int8_t>(ptr, shape, filename);\n            break;\n#ifdef ENABLE_BF16\n        case FtCudaDataType::BF16:\n            loadWeightFromBinFunc<T, __hip_bfloat16>(ptr, shape, filename);\n            break;\n#endif\n#ifdef ENABLE_FP8\n        case FtCudaDataType::FP8:\n            loadWeightFromBinFunc<T, float>(ptr, shape, filename);\n            break;\n#endif\n        default:\n            FT_LOG_ERROR(\"Does not support FtCudaDataType=%d\", model_file_type);\n            FT_CHECK(false);\n    }\n    return 0;\n}\n\ntemplate<>\nint loadWeightFromBin(int* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type)\n{\n    loadWeightFromBinFunc<int, int>(ptr, shape, filename);\n    return 0;\n}\n\ntemplate int\nloadWeightFromBin(float* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\ntemplate int\nloadWeightFromBin(half* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\ntemplate int\nloadWeightFromBin(int8_t* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBin(__hip_bfloat16* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#endif\n#ifdef ENABLE_FP8\ntemplate int\nloadWeightFromBin(__hip_fp8_e4m3_fnuz* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n#endif\ntemplate int\nloadWeightFromBin(int* ptr, std::vector<size_t> shape, std::string filename, FtCudaDataType model_file_type);\n\ntemplate<typename T, typename T_IN>\nint loadWeightFromBinAndQuantizeForWeightOnlyFunc(int8_t*             ptr,\n                                                  T*                  scales_ptr,\n                                                  std::vector<size_t> shape,\n                                                  std::string         filename)\n{\n    FT_LOG_INFO(std::string(\"Loading and quantizing weight from file: \") + filename);\n    FT_CHECK_WITH_INFO(shape.size() == 2, \"We can only use this function to dequantize a weight matrix.\");\n    std::vector<T_IN> host_array = loadWeightFromBinHelper<T_IN>(shape, filename);\n\n    if (host_array.empty()) {\n        return 0;\n    }\n\n    const size_t        num_elts = shape[0] * shape[1];\n    std::vector<int8_t> host_quantized_weight_buf(num_elts);\n    std::vector<T>      host_scales_buf(shape[1]);\n\n    // Note: This function preprocesses the weights to a special format for weight only quant!\n    symmetric_quantize<T, T_IN>(host_quantized_weight_buf.data(),\n                                host_scales_buf.data(),\n                                host_array.data(),\n                                shape,\n                                QuantType::INT8_WEIGHT_ONLY);\n\n    cudaH2Dcpy(ptr, (int8_t*)host_quantized_weight_buf.data(), host_quantized_weight_buf.size());\n    cudaH2Dcpy(scales_ptr, (T*)host_scales_buf.data(), host_scales_buf.size());\n\n    return 0;\n}\n\ntemplate<typename T>\nint loadWeightFromBinAndQuantizeForWeightOnly(int8_t*             quantized_weight_ptr,\n                                              T*                  scale_ptr,\n                                              std::vector<size_t> shape,\n                                              std::string         filename,\n                                              FtCudaDataType      model_file_type)\n{\n    switch (model_file_type) {\n        case FtCudaDataType::FP32:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, float>(quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n        case FtCudaDataType::FP16:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, half>(quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n#ifdef ENABLE_BF16\n        case FtCudaDataType::BF16:\n            loadWeightFromBinAndQuantizeForWeightOnlyFunc<T, __hip_bfloat16>(\n                quantized_weight_ptr, scale_ptr, shape, filename);\n            break;\n#endif\n        default:\n            FT_LOG_ERROR(\"Does not support FtCudaDataType=%d\", model_file_type);\n            FT_CHECK(false);\n    }\n    return 0;\n}\n\ntemplate<>\nint loadWeightFromBinAndQuantizeForWeightOnly(int8_t*             quantized_weight_ptr,\n                                              float*              scale_ptr,\n                                              std::vector<size_t> shape,\n                                              std::string         filename,\n                                              FtCudaDataType      model_file_type)\n{\n    FT_CHECK_WITH_INFO(false, \"Weight only quant not supported with FP32 compute.\");\n    return 0;\n}\n\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, float*, std::vector<size_t>, std::string, FtCudaDataType);\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, half*, std::vector<size_t>, std::string, FtCudaDataType);\n#ifdef ENABLE_BF16\ntemplate int\nloadWeightFromBinAndQuantizeForWeightOnly(int8_t*, __hip_bfloat16*, std::vector<size_t>, std::string, FtCudaDataType);\n#endif\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void cudaD2DcpyConvert(T_OUT* dst, const T_IN* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = cuda_cast<T_OUT>(src[tid]);\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeCudaD2DcpyConvert(T_OUT* tgt, const T_IN* src, const size_t size, hipStream_t stream)\n{\n    cudaD2DcpyConvert<<<256, 256, 0, stream>>>(tgt, src, size);\n}\n\ntemplate void invokeCudaD2DcpyConvert(int8_t* tgt, const float* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const int8_t* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const int* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(half* tgt, const int* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const float* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(half* tgt, const float* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const half* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(uint* tgt, const int* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const uint* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const float* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const half* src, const size_t size, hipStream_t stream);\n\n#ifdef ENABLE_BF16\ntemplate void invokeCudaD2DcpyConvert(__hip_bfloat16* tgt, const float* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(__hip_bfloat16* tgt, const int* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(float* tgt, const __hip_bfloat16* src, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DcpyConvert(int* tgt, const __hip_bfloat16* src, const size_t size, hipStream_t stream);\n#endif  // ENABLE_BF16\n\ntemplate<typename T_IN, typename T_OUT>\n__global__ void\ncudaD2DScaleCpyConvert(T_OUT* dst, const T_IN* src, const float* scale, bool invert_scale, const size_t size)\n{\n    const float scale_value = invert_scale ? 1.0f / scale[0] : scale[0];\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = cuda_cast<T_OUT>(cuda_cast<float>(src[tid]) * scale_value);\n    }\n}\n\ntemplate<typename T_IN, typename T_OUT>\nvoid invokeCudaD2DScaleCpyConvert(\n    T_OUT* tgt, const T_IN* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream)\n{\n    cudaD2DScaleCpyConvert<<<256, 256, 0, stream>>>(tgt, src, scale, invert_scale, size);\n}\n\n// clang-format off\ntemplate void invokeCudaD2DScaleCpyConvert(float* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const float* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(half* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const half* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeCudaD2DScaleCpyConvert(__hip_bfloat16* tgt, const int32_t* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\ntemplate void invokeCudaD2DScaleCpyConvert(int32_t* tgt, const __hip_bfloat16* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\n#endif  // ENABLE_BF16\n#ifdef ENABLE_FP8\ntemplate void invokeCudaD2DScaleCpyConvert(float* tgt, const __hip_fp8_e4m3_fnuz* src, const float* scale, bool invert_scale, const size_t size, hipStream_t stream);\n#endif  // ENABLE_FP8\n// clang-format on\n\nvoid invokeCudaD2DcpyHalf2Float(float* dst, half* src, const size_t size, hipStream_t stream)\n{\n    invokeCudaD2DcpyConvert(dst, src, size, stream);\n}\n\nvoid invokeCudaD2DcpyFloat2Half(half* dst, float* src, const size_t size, hipStream_t stream)\n{\n    invokeCudaD2DcpyConvert(dst, src, size, stream);\n}\n\ntemplate<typename T>\nvoid saveToBinary(const T* ptr, const size_t size, std::string filename)\n{\n\n    std::vector<T> h_ptr(size);\n    cudaD2Hcpy(h_ptr.data(), ptr, size);\n    std::vector<float> float_ptr(size);\n    for (int i = 0; i < size; i++) {\n        float_ptr[i] = (float)h_ptr[i];\n    }\n\n    std::ofstream out(filename, std::ios::out | std::ios::binary);\n    FT_CHECK_WITH_INFO(out.is_open(), \"Fail to open file \" + filename);\n\n    out.write((char*)float_ptr.data(), size * sizeof(float));\n}\n\ntemplate void saveToBinary(const float* ptr, const size_t size, std::string filename);\ntemplate void saveToBinary(const half* ptr, const size_t size, std::string filename);\n#ifdef ENABLE_BF16\ntemplate void saveToBinary(const __hip_bfloat16* ptr, const size_t size, std::string filename);\n#endif  // ENABLE_BF16\n\ntemplate<>\nvoid saveToBinary(const int* ptr, const size_t size, std::string filename)\n{\n    std::vector<int> h_ptr(size);\n    cudaD2Hcpy(h_ptr.data(), ptr, size);\n    std::ofstream out(filename, std::ios::out | std::ios::binary);\n    FT_CHECK_WITH_INFO(out.is_open(), \"Fail to open file \" + filename);\n    out.write((char*)h_ptr.data(), size * sizeof(int));\n}\n\ntemplate<typename T_IN, typename T_fake_type>\n__global__ void fakeCast(T_IN* input_ptr, const size_t size)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i += blockDim.x * gridDim.x) {\n        T_fake_type tmp_val = (T_fake_type)((float)input_ptr[i]);\n        input_ptr[i]        = (T_IN)((float)tmp_val);\n    }\n}\n\ntemplate<typename T_IN, typename T_fake_type>\nvoid invokeFakeCast(T_IN* input_ptr, const size_t size, hipStream_t stream)\n{\n    dim3 block(256);\n    dim3 grid((size + 255) / 256);\n    fakeCast<T_IN, T_fake_type><<<grid, block, 0, stream>>>(input_ptr, size);\n}\n\n#ifdef ENABLE_FP8\n__global__ void cudaD2Dcpyfp82Float(float* dst, __hip_fp8_e4m3_fnuz* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (float)(src[tid]);\n    }\n}\n\nvoid invokeCudaD2Dcpyfp82Float(float* dst, __hip_fp8_e4m3_fnuz* src, const size_t size, hipStream_t stream)\n{\n    cudaD2Dcpyfp82Float<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2Dcpyfp82Half(half* dst, __hip_fp8_e4m3_fnuz* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (half)((float)(src[tid]));\n    }\n}\n\nvoid invokeCudaD2Dcpyfp82Half(half* dst, __hip_fp8_e4m3_fnuz* src, const size_t size, hipStream_t stream)\n{\n    cudaD2Dcpyfp82Half<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyFloat2fp8(__hip_fp8_e4m3_fnuz* dst, float* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__hip_fp8_e4m3_fnuz)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyFloat2fp8(__hip_fp8_e4m3_fnuz* dst, float* src, const size_t size, hipStream_t stream)\n{\n    cudaD2DcpyFloat2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyHalf2fp8(__hip_fp8_e4m3_fnuz* dst, half* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__hip_fp8_e4m3_fnuz)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyHalf2fp8(__hip_fp8_e4m3_fnuz* dst, half* src, const size_t size, hipStream_t stream)\n{\n    cudaD2DcpyHalf2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n__global__ void cudaD2DcpyBfloat2fp8(__hip_fp8_e4m3_fnuz* dst, __hip_bfloat16* src, const size_t size)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < size; tid += blockDim.x * gridDim.x) {\n        dst[tid] = (__hip_fp8_e4m3_fnuz)src[tid];\n    }\n}\n\nvoid invokeCudaD2DcpyBfloat2fp8(__hip_fp8_e4m3_fnuz* dst, __hip_bfloat16* src, const size_t size, hipStream_t stream)\n{\n    cudaD2DcpyBfloat2fp8<<<256, 256, 0, stream>>>(dst, src, size);\n}\n\n#endif  // ENABLE_FP8\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose(T_OUT* dst, T_IN* src, const int dim0, const int dim1)\n{\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1; tid += blockDim.x * gridDim.x) {\n        const int src_col_id                = tid % dim1;\n        const int src_row_id                = tid / dim1;\n        dst[src_col_id * dim0 + src_row_id] = (T_OUT)(src[tid]);\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose(T* data, T* workspace, const int dim0, const int dim1)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    cudaD2Dcpy(workspace, data, dim0 * dim1);\n    transpose<<<256, 256>>>(data, workspace, dim0, dim1);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose(__hip_fp8_e4m3_fnuz* data, __hip_fp8_e4m3_fnuz* workspace, const int dim0, const int dim1);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose(__hip_bfloat16* data, __hip_bfloat16* workspace, const int dim0, const int dim1);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose(float* data, float* workspace, const int dim0, const int dim1);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose0213(T_OUT* dst, T_IN* src, const int dim0, const int dim1, const int dim2, const int dim3)\n{\n    // src permutation: [0, 1, 2, 3]\n    // dst permutation: [0, 2, 1, 3]\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1 * dim2 * dim3;\n         tid += blockDim.x * gridDim.x) {\n        int       tmp_idx   = tid;\n        const int dim_3_idx = tmp_idx % dim3;\n        tmp_idx             = (tmp_idx - dim_3_idx) / dim3;\n        const int dim_2_idx = tmp_idx % dim2;\n        tmp_idx             = (tmp_idx - dim_2_idx) / dim2;\n        const int dim_1_idx = tmp_idx % dim1;\n        tmp_idx             = (tmp_idx - dim_1_idx) / dim1;\n        const int dim_0_idx = tmp_idx % dim0;\n        dst[dim_0_idx * dim1 * dim2 * dim3 + dim_2_idx * dim1 * dim3 + dim_1_idx * dim3 + dim_3_idx] = src[tid];\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose0213(T* data, T* workspace, const int dim0, const int dim1, const int dim2, const int dim3)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    // Note that this kernel is used for pre-processing and not very efficient.\n    cudaD2Dcpy(workspace, data, dim0 * dim1 * dim2 * dim3);\n    transpose0213<<<256, 256>>>(data, workspace, dim0, dim1, dim2, dim3);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose0213(\n    __hip_fp8_e4m3_fnuz* data, __hip_fp8_e4m3_fnuz* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose0213(\n    __hip_bfloat16* data, __hip_bfloat16* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose0213(\n    float* data, float* workspace, const int dim0, const int dim1, const int dim2, const int dim3);\n\ntemplate<typename T_OUT, typename T_IN>\n__global__ void transpose102(T_OUT* dst, T_IN* src, const int dim0, const int dim1, const int dim2)\n{\n    // src permutation: [0, 1, 2]\n    // dst permutation: [1, 0, 2]\n    for (size_t tid = threadIdx.x + blockIdx.x * blockDim.x; tid < dim0 * dim1 * dim2; tid += blockDim.x * gridDim.x) {\n        int       tmp_idx                                           = tid;\n        const int dim_2_idx                                         = tmp_idx % dim2;\n        tmp_idx                                                     = (tmp_idx - dim_2_idx) / dim2;\n        const int dim_1_idx                                         = tmp_idx % dim1;\n        tmp_idx                                                     = (tmp_idx - dim_1_idx) / dim1;\n        const int dim_0_idx                                         = tmp_idx % dim0;\n        dst[dim_1_idx * dim0 * dim2 + dim_0_idx * dim2 + dim_2_idx] = src[tid];\n    }\n}\n\ntemplate<typename T>\nvoid invokeInPlaceTranspose102(T* data, T* workspace, const int dim0, const int dim1, const int dim2)\n{\n    // copy data to workspace, and then transpose from workspace to data\n    // Note that this kernel is used for pre-processing and not very efficient.\n    cudaD2Dcpy(workspace, data, dim0 * dim1 * dim2);\n    transpose102<<<256, 256>>>(data, workspace, dim0, dim1, dim2);\n}\n\n#ifdef ENABLE_FP8\ntemplate void invokeInPlaceTranspose102(\n    __hip_fp8_e4m3_fnuz* data, __hip_fp8_e4m3_fnuz* workspace, const int dim0, const int dim1, const int dim2);\n#endif  // ENABLE_FP8\n#ifdef ENABLE_BF16\ntemplate void invokeInPlaceTranspose102(\n    __hip_bfloat16* data, __hip_bfloat16* workspace, const int dim0, const int dim1, const int dim2);\n#endif  // ENABLE_BF16\ntemplate void invokeInPlaceTranspose102(float* data, float* workspace, const int dim0, const int dim1, const int dim2);\n\ntemplate<typename T>\nvoid __global__ multiplyScale(T* tensor, float scale, const size_t size)\n{\n    for (size_t index = threadIdx.x + blockIdx.x * blockDim.x; index < size; index += blockDim.x * gridDim.x) {\n        tensor[index] = (T)(((float)tensor[index]) * scale);\n    }\n}\n\ntemplate<typename T>\nvoid invokeMultiplyScale(T* tensor, float scale, const size_t size, hipStream_t stream)\n{\n    int block = 256;\n    int grid  = (size + 255) / 256;\n    multiplyScale<<<grid, block, 0, stream>>>(tensor, scale, size);\n}\n\ntemplate void invokeMultiplyScale(float* tensor, float scale, const size_t size, hipStream_t stream);\ntemplate void invokeMultiplyScale(half* tensor, float scale, const size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeMultiplyScale(__hip_bfloat16* tensor, float scale, const size_t size, hipStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeMultiplyScale(__hip_fp8_e4m3_fnuz* tensor, float scale, const size_t size, hipStream_t stream);\n#endif\n\ntemplate<typename T>\nvoid __global__ divideScale(T* tensor, float scale, const size_t size)\n{\n    for (size_t index = threadIdx.x + blockIdx.x * blockDim.x; index < size; index += blockDim.x * gridDim.x) {\n        tensor[index] = (T)(((float)tensor[index]) / scale);\n    }\n}\n\ntemplate<typename T>\nvoid invokeDivideScale(T* tensor, float scale, const size_t size, hipStream_t stream)\n{\n    int block = 256;\n    int grid  = (size + 255) / 256;\n    divideScale<<<grid, block, 0, stream>>>(tensor, scale, size);\n}\n\ntemplate void invokeDivideScale(float* tensor, float scale, const size_t size, hipStream_t stream);\ntemplate void invokeDivideScale(half* tensor, float scale, const size_t size, hipStream_t stream);\n#ifdef ENABLE_BF16\ntemplate void invokeDivideScale(__hip_bfloat16* tensor, float scale, const size_t size, hipStream_t stream);\n#endif\n#ifdef ENABLE_FP8\ntemplate void invokeDivideScale(__hip_fp8_e4m3_fnuz* tensor, float scale, const size_t size, hipStream_t stream);\n#endif\n#ifdef ENABLE_BF16\ntemplate void invokeFakeCast<float, __hip_bfloat16>(float* input_ptr, const size_t size, hipStream_t stream);\ntemplate void\ninvokeFakeCast<__hip_bfloat16, __hip_bfloat16>(__hip_bfloat16* input_ptr, const size_t size, hipStream_t stream);\ntemplate void invokeFakeCast<half, __hip_bfloat16>(half* input_ptr, const size_t size, hipStream_t stream);\n#endif\ntemplate void invokeFakeCast<float, half>(float* input_ptr, const size_t size, hipStream_t stream);\ntemplate void invokeFakeCast<float, float>(float* input_ptr, const size_t size, hipStream_t stream);\n#ifdef ENABLE_FP8\ntemplate void invokeFakeCast<float, __hip_fp8_e4m3_fnuz>(float* input_ptr, const size_t size, hipStream_t stream);\ntemplate void invokeFakeCast<half, __hip_fp8_e4m3_fnuz>(half* input_ptr, const size_t size, hipStream_t stream);\ntemplate void\ninvokeFakeCast<__hip_bfloat16, __hip_fp8_e4m3_fnuz>(__hip_bfloat16* input_ptr, const size_t size, hipStream_t stream);\n#endif\n\nsize_t cuda_datatype_size(FtCudaDataType dt)\n{\n    static const std::unordered_map<FtCudaDataType, size_t> sizes{{FtCudaDataType::FP32, sizeof(float)},\n                                                                  {FtCudaDataType::FP16, sizeof(half)}\n#ifdef ENABLE_BF16\n                                                                  ,\n                                                                  {FtCudaDataType::BF16, sizeof(__hip_bfloat16)}\n#endif\n    };\n\n    return sizes.at(dt);\n}\n\ntemplate<typename T>\n__global__ void check_range(T* buffer, size_t size, T min, T max, bool* d_within_range)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i += blockDim.x * gridDim.x) {\n        const T val = buffer[i];\n        if (val < min || val > max) {\n            *d_within_range = false;\n        }\n    }\n}\n\ntemplate<typename T>\nbool invokeCheckRange(T* buffer, const size_t size, T min, T max, bool* d_within_range, hipStream_t stream)\n{\n    hipMemsetAsync(d_within_range, true, sizeof(bool), stream);\n\n    dim3 block(256);\n    dim3 grid((size + 255) / 256);\n    check_range<T><<<grid, block, 0, stream>>>(buffer, size, min, max, d_within_range);\n\n    bool result;\n    cudaD2Hcpy(&result, d_within_range, 1);\n    return result;\n}\n\ntemplate bool\ninvokeCheckRange<int>(int* buffer, const size_t size, int min, int max, bool* d_within_range, hipStream_t stream);\n\n}  // namespace fastertransformer\n", "source": "/kaggle/working/FasterTransformer/src/fastertransformer/utils/memory_utils.cu"}
{"input": "#include <algorithm>   // std::fill_n\n#include <iostream>    // snprintf\n#include <math.h>      // expf, log\n#include <stdlib.h>    // rand\n#include <string>      // std::string\n#include <vector>      // std::vector\n\n#include <cublas_v2.h>\n#include <cublasLt.h>\n#include <cuda_runtime.h>\n#include <gtest/gtest.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/DynamicDecodeLayer.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cublasMMWrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#include \"tests/unittests/gtest_utils.h\"\n\nusing namespace fastertransformer;\n\nnamespace {\n\nstruct SamplingKernelTestParam {\n    size_t batch_size;\n    size_t vocab_size;\n    size_t beam_width;\n    uint   top_k;\n    float  top_p;\n    size_t output_len;\n\n    std::string toString()\n    {\n        return fmtstr(\"SamplingKernelTestParam[batch=%ld, vocab=%ld, beam=%ld, k=%u, p=%3.1f, output_len=%ld]\",\n                      batch_size,\n                      vocab_size,\n                      beam_width,\n                      top_k,\n                      top_p,\n                      output_len);\n    }\n};\n\n/////////////////////////////////// Tests //////////////////////////////////////////\n\ntemplate<typename T>\nvoid computeProb(T* probs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size.\n    //   probs =  softmax(logits) (softmax along with vocab dimension)\n    // float is used for either T=float or half, since operations of half are\n    // not fully supported in a host function.\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float maxval = -FLT_MAX;\n        for (int i = 0; i < vocab_size; ++i) {\n            float logit = static_cast<float>(logits[bidx * vocab_size + i]);\n            if (logit > maxval) {\n                maxval = logit;\n            }\n        }\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(static_cast<float>(logits[bidx * vocab_size + i]) - maxval);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx = bidx * vocab_size + i;\n            float logit = static_cast<float>(logits[idx]) - maxval;\n            probs[idx] = static_cast<T>(expf(logit) / (sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid computeLogProb(T* logprobs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    // float is used for either T=float or half, since operations of half are\n    // not fully supported in a host function.\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float maxval = -FLT_MAX;\n        for (int i = 0; i < vocab_size; ++i) {\n            float logit = static_cast<float>(logits[bidx * vocab_size + i]);\n            if (logit > maxval) {\n                maxval = logit;\n            }\n        }\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(static_cast<float>(logits[bidx * vocab_size + i]) - maxval);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx = bidx * vocab_size + i;\n            float logit = static_cast<float>(logits[idx]) - maxval;\n            logprobs[idx] = static_cast<T>(logit - logf(sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nclass SamplingKernelTest: public testing::Test {\npublic:\n    void SetUp() override\n    {\n        check_cuda_error(cudaStreamCreate(&stream));\n        allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n        allocator->setStream(stream);\n    }\n    void TearDown() override\n    {\n        delete allocator;\n        check_cuda_error(cudaStreamDestroy(stream));\n    }\n\nprotected:\n    unsigned long long seed = 0;\n    cudaStream_t stream;\n    Allocator<AllocatorType::CUDA>* allocator;\n    curandState_t* curand_states;\n};\n\ntemplate<typename T>\nclass TopKSamplingKernelTest: public SamplingKernelTest<T> {\n\nprotected:\n    const int end_id = 0;\n    using SamplingKernelTest<T>::seed;\n    using SamplingKernelTest<T>::stream;\n    using SamplingKernelTest<T>::allocator;\n    using SamplingKernelTest<T>::curand_states;\n\npublic:\n    void runTest(SamplingKernelTestParam param)\n    {\n        size_t batch_size  = param.batch_size;\n        size_t vocab_size  = param.vocab_size;\n        size_t output_len  = param.output_len;\n        size_t max_seq_len = output_len;\n\n        uint  top_k = param.top_k;\n        float top_p = param.top_p;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        curandState_t* curand_states =\n            reinterpret_cast<curandState_t*>(allocator->malloc(sizeof(curandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        size_t workspace_size = 0;\n        // retrieve the workspace size of the top-k sampling kernel.\n        invokeTopKSampling<T>(nullptr,\n                              workspace_size,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              top_k,\n                              1.0f,\n                              vocab_size,\n                              nullptr,\n                              stream,\n                              batch_size,\n                              nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        int*  end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n        int*   output_ids    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batch_size));\n\n        // Init by zero.\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(end_ids, batch_size, end_id);\n\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, max_seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n            invokeTopKSampling(workspace,\n                               workspace_size,\n                               // Note that the kernel needs vocab probs instead of\n                               // log-prob if cum_log_probs or output_log_probs are\n                               // provided. It's because the sampling layer already\n                               // preprocesses log_prob_buf when those are provided.\n                               probs,\n                               output_ids + step * batch_size,\n                               seq_lengths,\n                               finished,\n                               cum_lprobs,\n                               output_lprobs + step * batch_size,\n                               curand_states,\n                               top_k,\n                               top_p,\n                               vocab_size,\n                               end_ids,\n                               stream,\n                               batch_size,\n                               nullptr);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                int idx = i * vocab_size + h_output_ids[i];\n                expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed);\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param, bool has_diff_runtime_args, bool use_skip_decode)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n        size_t output_len = param.output_len;\n        size_t seq_len    = output_len;\n\n        int   top_k = param.top_k;\n        float top_p = param.top_p;\n\n        int*   h_top_ks = new int[batch_size];\n        float* h_top_ps = new float[batch_size];\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_top_ks[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_k : 1;\n            h_top_ps[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_p : 0.1 * top_p;\n        }\n        int max_top_k = *std::max_element(h_top_ks, h_top_ks + batch_size);\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n        bool* h_skip_decode = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_skip_decode[i] = use_skip_decode && (i % 2 == 0);\n        }\n\n        curandState_t* curand_states =\n            reinterpret_cast<curandState_t*>(allocator->malloc(sizeof(curandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        size_t workspace_size = 0;\n        // retrieve the workspace size of the top-k sampling kernel.\n        invokeBatchTopKSampling<T>(nullptr,  // workspace\n                                   workspace_size,\n                                   nullptr,  // log_probs\n                                   nullptr,  // ids\n                                   nullptr,  // sequence_lengths\n                                   nullptr,  // finished\n                                   nullptr,  // cum_log_probs\n                                   nullptr,  // output_log_probs\n                                   nullptr,  // curandstates\n                                   max_top_k,\n                                   nullptr,  // top_ks\n                                   1.0f,\n                                   nullptr,\n                                   vocab_size,\n                                   nullptr,  // end_ids\n                                   stream,\n                                   batch_size,\n                                   nullptr);\n        void* workspace = allocator->malloc(workspace_size, false);\n\n        int*   top_ks = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        float* top_ps = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n\n        int*  end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size, true));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        // Initialize.\n        cudaH2Dcpy(top_ks, h_top_ks, batch_size);\n        cudaH2Dcpy(top_ps, h_top_ps, batch_size);\n        cudaH2Dcpy(skip_decode, h_skip_decode, batch_size);\n\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeBatchTopKSampling(workspace,\n                                    workspace_size,\n                                    // Note that the kernel needs vocab probs instead of\n                                    // log-prob if cum_log_probs or output_log_probs are\n                                    // provided. It's because the sampling layer already\n                                    // preprocesses log_prob_buf when those are provided.\n                                    probs,\n                                    output_ids + step * batch_size,\n                                    seq_lengths,\n                                    finished,\n                                    cum_lprobs,\n                                    output_lprobs + step * batch_size,\n                                    curand_states,\n                                    max_top_k,\n                                    top_ks,\n                                    1.0f,\n                                    nullptr,\n                                    vocab_size,\n                                    end_ids,\n                                    stream,\n                                    batch_size,\n                                    skip_decode);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                if (!h_skip_decode[i]) {\n                    int idx = i * vocab_size + h_output_ids[i];\n                    expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                    EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n                }\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed) << \"Fail subtest (has_diff_runtime_args: \" << has_diff_runtime_args\n                            << \", skip_decode: \" << use_skip_decode << \")\";\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n        delete[] h_top_ks;\n        delete[] h_skip_decode;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param)\n    {\n        this->runBatchTest(param, false, false);\n        this->runBatchTest(param, false, true);\n        this->runBatchTest(param, true,  false);\n        this->runBatchTest(param, true,  true);\n    }\n};\n\nTYPED_TEST_SUITE(TopKSamplingKernelTest, FloatAndHalfTypes);\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessGreedy)\n{\n    this->runTest({6, 4, 1, 1, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessAncestral)\n{\n    this->runTest({6, 4, 1, 4, 1.0f, 1});\n};\n\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessLargeK63)\n{\n    this->runTest({16, 51200, 1, 63, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessLargeK1024)\n{\n    this->runTest({16, 51200, 1, 1024, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessTopKTopP)\n{\n    this->runTest({16, 4000, 1, 63, 0.3f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, NotSupportedLargerThanK1024)\n{\n    EXPECT_THROW(this->runTest({16, 4000, 1, 1025, 1.0f, 8}), std::domain_error);\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessGreedy)\n{\n    this->runBatchTest({6, 4, 1, 1, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessAncestral)\n{\n    this->runBatchTest({6, 4, 1, 4, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessLargeK63)\n{\n    this->runBatchTest({8, 4000, 1, 63, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessLargeK1024)\n{\n    this->runBatchTest({8, 4000, 1, 1024, 0.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessTopKTopP)\n{\n    this->runBatchTest({8, 4000, 1, 63, 0.3f, 8});\n};\n\n\ntemplate<typename T>\nclass TopPSamplingKernelTest: public SamplingKernelTest<T> {\n\nprotected:\n    const int end_id = 0;\n    using SamplingKernelTest<T>::seed;\n    using SamplingKernelTest<T>::stream;\n    using SamplingKernelTest<T>::allocator;\n    using SamplingKernelTest<T>::curand_states;\n\npublic:\n    void runTest(SamplingKernelTestParam param)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n        size_t output_len = param.output_len;\n        size_t seq_len = output_len;\n\n        float top_p = param.top_p;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n\n        int device;\n        cudaGetDevice(&device);\n        struct cudaDeviceProp device_prop;\n        cudaGetDeviceProperties(&device_prop, device);\n\n        curandState_t* curand_states = reinterpret_cast<curandState_t*>(\n            allocator->malloc(sizeof(curandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        int* end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        int* begin_offsets    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* end_offsets      = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* topp_id_vals_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * vocab_size));\n\n        size_t workspace_size = 0;\n        size_t cub_temp_storage_size = 0;\n        // retrieve the workspace size of the top-p sampling kernel.\n        invokeTopPSampling<T>(nullptr,  // workspace\n                              workspace_size,\n                              cub_temp_storage_size,\n                              nullptr,  // output_ids\n                              nullptr,  // sequence_length\n                              nullptr,  // finished_buffer\n                              nullptr,  // cum_log_probs\n                              nullptr,  // output_log_probs\n                              (T*)nullptr,  // log_probs\n                              topp_id_vals_buf,\n                              end_offsets,\n                              begin_offsets,\n                              curand_states,\n                              batch_size,\n                              vocab_size,\n                              nullptr,\n                              top_p,\n                              stream,\n                              &device_prop,\n                              nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        // Initialize.\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeTopPInitialize(topp_id_vals_buf,\n                                 end_offsets,\n                                 begin_offsets,\n                                 batch_size,\n                                 vocab_size,\n                                 stream);\n\n            invokeTopPSampling<T>(workspace,\n                                  workspace_size,\n                                  cub_temp_storage_size,\n                                  output_ids + step * batch_size,\n                                  seq_lengths,\n                                  finished,\n                                  cum_lprobs,\n                                  output_lprobs + step * batch_size,\n                                  // Note that the kernel needs vocab probs instead of\n                                  // log-prob if cum_log_probs or output_log_probs are\n                                  // provided. It's because the sampling layer already\n                                  // preprocesses log_prob_buf when those are provided.\n                                  probs,\n                                  topp_id_vals_buf,\n                                  end_offsets,\n                                  begin_offsets,\n                                  curand_states,\n                                  batch_size,\n                                  vocab_size,\n                                  end_ids,\n                                  top_p,\n                                  stream,\n                                  &device_prop,\n                                  nullptr);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                int idx = i * vocab_size + h_output_ids[i];\n                expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed);\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param, bool has_diff_runtime_args, bool use_skip_decode)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n\n        float top_p = param.top_p;\n        float* h_top_ps = new float[batch_size];\n        // Initialize runtime top k values.\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_top_ps[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_p : 0.1 * top_p;\n        }\n        float max_top_p = *std::max_element(h_top_ps, h_top_ps + batch_size);\n\n        size_t output_len = param.output_len;\n        size_t seq_len = output_len;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n        bool* h_skip_decode = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_skip_decode[i] = use_skip_decode && (i % 2 == 0);\n        }\n\n        int device;\n        cudaGetDevice(&device);\n        struct cudaDeviceProp device_prop;\n        cudaGetDeviceProperties(&device_prop, device);\n\n        curandState_t* curand_states = reinterpret_cast<curandState_t*>(\n            allocator->malloc(sizeof(curandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        float* top_ps = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n\n        int* end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        int* begin_offsets    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* end_offsets      = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* topp_id_vals_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * vocab_size));\n\n        size_t workspace_size = 0;\n        size_t cub_temp_storage_size = 0;\n        // retrieve the workspace size of the top-p sampling kernel.\n        invokeBatchTopPSampling<T>(nullptr,  // workspace\n                                   workspace_size,\n                                   cub_temp_storage_size,\n                                   nullptr,  // output_ids\n                                   nullptr,  // sequence_length\n                                   nullptr,  // finished_buffer\n                                   nullptr,  // cum_log_probs\n                                   nullptr,  // output_log_probs\n                                   (T*)nullptr,  // log_probs\n                                   topp_id_vals_buf,\n                                   end_offsets,\n                                   begin_offsets,\n                                   curand_states,\n                                   batch_size,\n                                   vocab_size,\n                                   nullptr,\n                                   max_top_p,\n                                   top_ps,\n                                   stream,\n                                   &device_prop,\n                                   nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        // Initialize.\n        cudaH2Dcpy(top_ps, h_top_ps, batch_size);\n        cudaH2Dcpy(skip_decode, h_skip_decode, batch_size);\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeTopPInitialize(topp_id_vals_buf,\n                                 end_offsets,\n                                 begin_offsets,\n                                 batch_size,\n                                 vocab_size,\n                                 stream);\n\n            invokeBatchTopPSampling<T>(workspace,\n                                       workspace_size,\n                                       cub_temp_storage_size,\n                                       output_ids + step * batch_size,\n                                       seq_lengths,\n                                       finished,\n                                       cum_lprobs,\n                                       output_lprobs + step * batch_size,\n                                       // Note that the kernel needs vocab probs instead of\n                                       // log-prob if cum_log_probs or output_log_probs are\n                                       // provided. It's because the sampling layer already\n                                       // preprocesses log_prob_buf when those are provided.\n                                       probs,\n                                       topp_id_vals_buf,\n                                       end_offsets,\n                                       begin_offsets,\n                                       curand_states,\n                                       batch_size,\n                                       vocab_size,\n                                       end_ids,\n                                       max_top_p,\n                                       top_ps,\n                                       stream,\n                                       &device_prop,\n                                       skip_decode);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                if (!h_skip_decode[i]) {\n                    int idx = i * vocab_size + h_output_ids[i];\n                    expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                    EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n                }\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed) << \"Fail subtest (has_diff_runtime_args: \" << has_diff_runtime_args\n                            << \", skip_decode: \" << use_skip_decode << \")\";\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n        delete[] h_top_ps;\n        delete[] h_skip_decode;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param)\n    {\n        this->runBatchTest(param, false, false);\n        this->runBatchTest(param, false, true);\n        this->runBatchTest(param, true,  false);\n        this->runBatchTest(param, true,  true);\n    }\n};\n\nTYPED_TEST_SUITE(TopPSamplingKernelTest, FloatAndHalfTypes);\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessSmallP)\n{\n    this->runTest({6, 4, 1, 0, 0.2f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeP)\n{\n    this->runTest({6, 4, 1, 0, 0.9f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessAncestral)\n{\n    this->runTest({6, 4, 1, 0, 1.0f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeVocabSmallP)\n{\n    this->runTest({32, 51200, 1, 0, 0.2f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeVocabLargeP)\n{\n    this->runTest({32, 51200, 1, 0, 0.9f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessSmallP)\n{\n    this->runBatchTest({6, 4, 1, 0, 0.2f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessLargeP)\n{\n    this->runBatchTest({6, 4, 1, 0, 0.9f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessSmallP2)\n{\n    this->runBatchTest({8, 4000, 1, 0, 0.2f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessLargeP2)\n{\n    this->runBatchTest({8, 4000, 1, 0, 0.9f, 16});\n};\n\n__global__\nvoid generateRandomNumber(unsigned int *vals, curandState_t *states, const int batch_size) {\n    int idx = threadIdx.x;\n    if (idx < batch_size) {\n        vals[idx] = curand(states + idx);\n    }\n}\n\nTEST(SamplingKernelTest, CurandBatchInitialize) {\n    size_t batch_size = 127;\n    cudaStream_t stream;\n    cudaStreamCreate(&stream);\n\n    curandState_t* curand_states;\n    check_cuda_error(cudaMalloc(&curand_states, sizeof(curandState_t) * batch_size));\n    unsigned long long* h_random_seeds = new unsigned long long[batch_size];\n    const size_t period_size = 3;\n    for (size_t i = 0; i < batch_size; ++i) {\n        h_random_seeds[i] = i / period_size;\n    }\n    unsigned long long* d_random_seeds;\n    check_cuda_error(cudaMalloc(&d_random_seeds, sizeof(unsigned long long) * batch_size));\n    check_cuda_error(cudaMemcpy(d_random_seeds, h_random_seeds,\n                                sizeof(unsigned long long) * batch_size, cudaMemcpyHostToDevice));\n\n    // Initialize curand states.\n    invokeCurandBatchInitialize(curand_states, batch_size, d_random_seeds, stream);\n    sync_check_cuda_error();\n\n    // Generate random numbers using initialized curand states.\n    unsigned int* d_rand_vals;\n    unsigned int* h_rand_vals = new unsigned int[batch_size];\n    check_cuda_error(cudaMalloc(&d_rand_vals, sizeof(unsigned int) * batch_size));\n    generateRandomNumber<<<1, batch_size, 0, stream>>>(d_rand_vals, curand_states, batch_size);\n    check_cuda_error(cudaMemcpyAsync(\n        h_rand_vals, d_rand_vals, sizeof(unsigned int) * batch_size, cudaMemcpyDeviceToHost, stream));\n    check_cuda_error(cudaStreamSynchronize(stream));\n\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < batch_size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            EXPECT_TRUE(h_rand_vals[i] == h_rand_vals[i + j])\n                << fmtstr(\"Fail at val[%d]=%d <> val[%d]=%d\", i, h_rand_vals[i], i + j, h_rand_vals[i + j]);\n        }\n    }\n\n    delete h_rand_vals;\n    delete h_random_seeds;\n    check_cuda_error(cudaFree(d_rand_vals));\n    check_cuda_error(cudaFree(d_random_seeds));\n    check_cuda_error(cudaFree(curand_states));\n    check_cuda_error(cudaStreamDestroy(stream));\n}\n\n}  // end of namespace\n", "output": "#include <algorithm>   // std::fill_n\n#include <iostream>    // snprintf\n#include <math.h>      // expf, log\n#include <stdlib.h>    // rand\n#include <string>      // std::string\n#include <vector>      // std::vector\n\n#include <hipblas.h>\n#include <hipblaslt.h>\n#include <hip/hip_runtime.h>\n#include <gtest/gtest.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/kernels/sampling_topp_kernels.h\"\n#include \"src/fastertransformer/layers/DynamicDecodeLayer.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cublasMMWrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#include \"tests/unittests/gtest_utils.h\"\n\nusing namespace fastertransformer;\n\nnamespace {\n\nstruct SamplingKernelTestParam {\n    size_t batch_size;\n    size_t vocab_size;\n    size_t beam_width;\n    uint   top_k;\n    float  top_p;\n    size_t output_len;\n\n    std::string toString()\n    {\n        return fmtstr(\"SamplingKernelTestParam[batch=%ld, vocab=%ld, beam=%ld, k=%u, p=%3.1f, output_len=%ld]\",\n                      batch_size,\n                      vocab_size,\n                      beam_width,\n                      top_k,\n                      top_p,\n                      output_len);\n    }\n};\n\n/////////////////////////////////// Tests //////////////////////////////////////////\n\ntemplate<typename T>\nvoid computeProb(T* probs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size.\n    //   probs =  softmax(logits) (softmax along with vocab dimension)\n    // float is used for either T=float or half, since operations of half are\n    // not fully supported in a host function.\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float maxval = -FLT_MAX;\n        for (int i = 0; i < vocab_size; ++i) {\n            float logit = static_cast<float>(logits[bidx * vocab_size + i]);\n            if (logit > maxval) {\n                maxval = logit;\n            }\n        }\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(static_cast<float>(logits[bidx * vocab_size + i]) - maxval);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx = bidx * vocab_size + i;\n            float logit = static_cast<float>(logits[idx]) - maxval;\n            probs[idx] = static_cast<T>(expf(logit) / (sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid computeLogProb(T* logprobs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    // float is used for either T=float or half, since operations of half are\n    // not fully supported in a host function.\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float maxval = -FLT_MAX;\n        for (int i = 0; i < vocab_size; ++i) {\n            float logit = static_cast<float>(logits[bidx * vocab_size + i]);\n            if (logit > maxval) {\n                maxval = logit;\n            }\n        }\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(static_cast<float>(logits[bidx * vocab_size + i]) - maxval);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx = bidx * vocab_size + i;\n            float logit = static_cast<float>(logits[idx]) - maxval;\n            logprobs[idx] = static_cast<T>(logit - logf(sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nclass SamplingKernelTest: public testing::Test {\npublic:\n    void SetUp() override\n    {\n        check_cuda_error(hipStreamCreate(&stream));\n        allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n        allocator->setStream(stream);\n    }\n    void TearDown() override\n    {\n        delete allocator;\n        check_cuda_error(hipStreamDestroy(stream));\n    }\n\nprotected:\n    unsigned long long seed = 0;\n    hipStream_t stream;\n    Allocator<AllocatorType::CUDA>* allocator;\n    hiprandState_t* curand_states;\n};\n\ntemplate<typename T>\nclass TopKSamplingKernelTest: public SamplingKernelTest<T> {\n\nprotected:\n    const int end_id = 0;\n    using SamplingKernelTest<T>::seed;\n    using SamplingKernelTest<T>::stream;\n    using SamplingKernelTest<T>::allocator;\n    using SamplingKernelTest<T>::curand_states;\n\npublic:\n    void runTest(SamplingKernelTestParam param)\n    {\n        size_t batch_size  = param.batch_size;\n        size_t vocab_size  = param.vocab_size;\n        size_t output_len  = param.output_len;\n        size_t max_seq_len = output_len;\n\n        uint  top_k = param.top_k;\n        float top_p = param.top_p;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        hiprandState_t* curand_states =\n            reinterpret_cast<hiprandState_t*>(allocator->malloc(sizeof(hiprandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        size_t workspace_size = 0;\n        // retrieve the workspace size of the top-k sampling kernel.\n        invokeTopKSampling<T>(nullptr,\n                              workspace_size,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              nullptr,\n                              top_k,\n                              1.0f,\n                              vocab_size,\n                              nullptr,\n                              stream,\n                              batch_size,\n                              nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        int*  end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n        int*   output_ids    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batch_size));\n\n        // Init by zero.\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(end_ids, batch_size, end_id);\n\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, max_seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n            invokeTopKSampling(workspace,\n                               workspace_size,\n                               // Note that the kernel needs vocab probs instead of\n                               // log-prob if cum_log_probs or output_log_probs are\n                               // provided. It's because the sampling layer already\n                               // preprocesses log_prob_buf when those are provided.\n                               probs,\n                               output_ids + step * batch_size,\n                               seq_lengths,\n                               finished,\n                               cum_lprobs,\n                               output_lprobs + step * batch_size,\n                               curand_states,\n                               top_k,\n                               top_p,\n                               vocab_size,\n                               end_ids,\n                               stream,\n                               batch_size,\n                               nullptr);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                int idx = i * vocab_size + h_output_ids[i];\n                expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed);\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param, bool has_diff_runtime_args, bool use_skip_decode)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n        size_t output_len = param.output_len;\n        size_t seq_len    = output_len;\n\n        int   top_k = param.top_k;\n        float top_p = param.top_p;\n\n        int*   h_top_ks = new int[batch_size];\n        float* h_top_ps = new float[batch_size];\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_top_ks[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_k : 1;\n            h_top_ps[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_p : 0.1 * top_p;\n        }\n        int max_top_k = *std::max_element(h_top_ks, h_top_ks + batch_size);\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n        bool* h_skip_decode = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_skip_decode[i] = use_skip_decode && (i % 2 == 0);\n        }\n\n        hiprandState_t* curand_states =\n            reinterpret_cast<hiprandState_t*>(allocator->malloc(sizeof(hiprandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        size_t workspace_size = 0;\n        // retrieve the workspace size of the top-k sampling kernel.\n        invokeBatchTopKSampling<T>(nullptr,  // workspace\n                                   workspace_size,\n                                   nullptr,  // log_probs\n                                   nullptr,  // ids\n                                   nullptr,  // sequence_lengths\n                                   nullptr,  // finished\n                                   nullptr,  // cum_log_probs\n                                   nullptr,  // output_log_probs\n                                   nullptr,  // curandstates\n                                   max_top_k,\n                                   nullptr,  // top_ks\n                                   1.0f,\n                                   nullptr,\n                                   vocab_size,\n                                   nullptr,  // end_ids\n                                   stream,\n                                   batch_size,\n                                   nullptr);\n        void* workspace = allocator->malloc(workspace_size, false);\n\n        int*   top_ks = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        float* top_ps = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n\n        int*  end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int*  output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size, true));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        // Initialize.\n        cudaH2Dcpy(top_ks, h_top_ks, batch_size);\n        cudaH2Dcpy(top_ps, h_top_ps, batch_size);\n        cudaH2Dcpy(skip_decode, h_skip_decode, batch_size);\n\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeBatchTopKSampling(workspace,\n                                    workspace_size,\n                                    // Note that the kernel needs vocab probs instead of\n                                    // log-prob if cum_log_probs or output_log_probs are\n                                    // provided. It's because the sampling layer already\n                                    // preprocesses log_prob_buf when those are provided.\n                                    probs,\n                                    output_ids + step * batch_size,\n                                    seq_lengths,\n                                    finished,\n                                    cum_lprobs,\n                                    output_lprobs + step * batch_size,\n                                    curand_states,\n                                    max_top_k,\n                                    top_ks,\n                                    1.0f,\n                                    nullptr,\n                                    vocab_size,\n                                    end_ids,\n                                    stream,\n                                    batch_size,\n                                    skip_decode);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                if (!h_skip_decode[i]) {\n                    int idx = i * vocab_size + h_output_ids[i];\n                    expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                    EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n                }\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed) << \"Fail subtest (has_diff_runtime_args: \" << has_diff_runtime_args\n                            << \", skip_decode: \" << use_skip_decode << \")\";\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n        delete[] h_top_ks;\n        delete[] h_skip_decode;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param)\n    {\n        this->runBatchTest(param, false, false);\n        this->runBatchTest(param, false, true);\n        this->runBatchTest(param, true,  false);\n        this->runBatchTest(param, true,  true);\n    }\n};\n\nTYPED_TEST_SUITE(TopKSamplingKernelTest, FloatAndHalfTypes);\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessGreedy)\n{\n    this->runTest({6, 4, 1, 1, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessAncestral)\n{\n    this->runTest({6, 4, 1, 4, 1.0f, 1});\n};\n\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessLargeK63)\n{\n    this->runTest({16, 51200, 1, 63, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessLargeK1024)\n{\n    this->runTest({16, 51200, 1, 1024, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, CorrectnessTopKTopP)\n{\n    this->runTest({16, 4000, 1, 63, 0.3f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, NotSupportedLargerThanK1024)\n{\n    EXPECT_THROW(this->runTest({16, 4000, 1, 1025, 1.0f, 8}), std::domain_error);\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessGreedy)\n{\n    this->runBatchTest({6, 4, 1, 1, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessAncestral)\n{\n    this->runBatchTest({6, 4, 1, 4, 1.0f, 1});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessLargeK63)\n{\n    this->runBatchTest({8, 4000, 1, 63, 1.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessLargeK1024)\n{\n    this->runBatchTest({8, 4000, 1, 1024, 0.0f, 8});\n};\n\nTYPED_TEST(TopKSamplingKernelTest, BatchCorrectnessTopKTopP)\n{\n    this->runBatchTest({8, 4000, 1, 63, 0.3f, 8});\n};\n\n\ntemplate<typename T>\nclass TopPSamplingKernelTest: public SamplingKernelTest<T> {\n\nprotected:\n    const int end_id = 0;\n    using SamplingKernelTest<T>::seed;\n    using SamplingKernelTest<T>::stream;\n    using SamplingKernelTest<T>::allocator;\n    using SamplingKernelTest<T>::curand_states;\n\npublic:\n    void runTest(SamplingKernelTestParam param)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n        size_t output_len = param.output_len;\n        size_t seq_len = output_len;\n\n        float top_p = param.top_p;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n\n        int device;\n        hipGetDevice(&device);\n        struct hipDeviceProp_t device_prop;\n        hipGetDeviceProperties(&device_prop, device);\n\n        hiprandState_t* curand_states = reinterpret_cast<hiprandState_t*>(\n            allocator->malloc(sizeof(hiprandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        int* end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        int* begin_offsets    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* end_offsets      = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* topp_id_vals_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * vocab_size));\n\n        size_t workspace_size = 0;\n        size_t cub_temp_storage_size = 0;\n        // retrieve the workspace size of the top-p sampling kernel.\n        invokeTopPSampling<T>(nullptr,  // workspace\n                              workspace_size,\n                              cub_temp_storage_size,\n                              nullptr,  // output_ids\n                              nullptr,  // sequence_length\n                              nullptr,  // finished_buffer\n                              nullptr,  // cum_log_probs\n                              nullptr,  // output_log_probs\n                              (T*)nullptr,  // log_probs\n                              topp_id_vals_buf,\n                              end_offsets,\n                              begin_offsets,\n                              curand_states,\n                              batch_size,\n                              vocab_size,\n                              nullptr,\n                              top_p,\n                              stream,\n                              &device_prop,\n                              nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        // Initialize.\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeTopPInitialize(topp_id_vals_buf,\n                                 end_offsets,\n                                 begin_offsets,\n                                 batch_size,\n                                 vocab_size,\n                                 stream);\n\n            invokeTopPSampling<T>(workspace,\n                                  workspace_size,\n                                  cub_temp_storage_size,\n                                  output_ids + step * batch_size,\n                                  seq_lengths,\n                                  finished,\n                                  cum_lprobs,\n                                  output_lprobs + step * batch_size,\n                                  // Note that the kernel needs vocab probs instead of\n                                  // log-prob if cum_log_probs or output_log_probs are\n                                  // provided. It's because the sampling layer already\n                                  // preprocesses log_prob_buf when those are provided.\n                                  probs,\n                                  topp_id_vals_buf,\n                                  end_offsets,\n                                  begin_offsets,\n                                  curand_states,\n                                  batch_size,\n                                  vocab_size,\n                                  end_ids,\n                                  top_p,\n                                  stream,\n                                  &device_prop,\n                                  nullptr);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                int idx = i * vocab_size + h_output_ids[i];\n                expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed);\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param, bool has_diff_runtime_args, bool use_skip_decode)\n    {\n        size_t batch_size = param.batch_size;\n        size_t vocab_size = param.vocab_size;\n\n        float top_p = param.top_p;\n        float* h_top_ps = new float[batch_size];\n        // Initialize runtime top k values.\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_top_ps[i] = (!has_diff_runtime_args || i % 3 == 0) ? top_p : 0.1 * top_p;\n        }\n        float max_top_p = *std::max_element(h_top_ps, h_top_ps + batch_size);\n\n        size_t output_len = param.output_len;\n        size_t seq_len = output_len;\n\n        // Logit values in the host of shape (batch_size x vocab_size).\n        T* h_logits = new T[batch_size * vocab_size];\n        T* h_probs  = new T[batch_size * vocab_size];\n        T* h_lprobs = new T[batch_size * vocab_size];\n\n        float* expected_cum_lprobs = new float[batch_size];\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n\n        int*  h_output_ids  = new int[batch_size];\n        int*  h_seq_lengths = new int[batch_size];\n        bool* h_finished    = new bool[batch_size];\n        bool* h_skip_decode = new bool[batch_size];\n\n        initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n        std::fill_n(expected_cum_lprobs, batch_size, 0);\n        for (size_t i = 0; i < batch_size; ++i) {\n            h_skip_decode[i] = use_skip_decode && (i % 2 == 0);\n        }\n\n        int device;\n        hipGetDevice(&device);\n        struct hipDeviceProp_t device_prop;\n        hipGetDeviceProperties(&device_prop, device);\n\n        hiprandState_t* curand_states = reinterpret_cast<hiprandState_t*>(\n            allocator->malloc(sizeof(hiprandState_t) * batch_size, false));\n        invokeCurandInitialize(curand_states, batch_size, seed, stream);\n\n        float* top_ps = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n\n        int* end_ids     = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* seq_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n        int* output_ids  = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * seq_len * batch_size));\n\n        bool* finished    = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n        bool* skip_decode = reinterpret_cast<bool*>(allocator->malloc(sizeof(bool) * batch_size));\n\n        T*     probs         = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batch_size * vocab_size));\n        float* cum_lprobs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size));\n        float* output_lprobs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * output_len * batch_size));\n\n        int* begin_offsets    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* end_offsets      = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * (batch_size + 1)));\n        int* topp_id_vals_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * vocab_size));\n\n        size_t workspace_size = 0;\n        size_t cub_temp_storage_size = 0;\n        // retrieve the workspace size of the top-p sampling kernel.\n        invokeBatchTopPSampling<T>(nullptr,  // workspace\n                                   workspace_size,\n                                   cub_temp_storage_size,\n                                   nullptr,  // output_ids\n                                   nullptr,  // sequence_length\n                                   nullptr,  // finished_buffer\n                                   nullptr,  // cum_log_probs\n                                   nullptr,  // output_log_probs\n                                   (T*)nullptr,  // log_probs\n                                   topp_id_vals_buf,\n                                   end_offsets,\n                                   begin_offsets,\n                                   curand_states,\n                                   batch_size,\n                                   vocab_size,\n                                   nullptr,\n                                   max_top_p,\n                                   top_ps,\n                                   stream,\n                                   &device_prop,\n                                   nullptr);\n        void* workspace = allocator->malloc(workspace_size);\n\n        // Initialize.\n        cudaH2Dcpy(top_ps, h_top_ps, batch_size);\n        cudaH2Dcpy(skip_decode, h_skip_decode, batch_size);\n        deviceFill(end_ids, batch_size, end_id);\n        deviceFill(seq_lengths, batch_size, 0);\n        deviceFill(finished, batch_size, false);\n        deviceFill(cum_lprobs, batch_size, 0.0f);\n        deviceFill(output_lprobs, output_len * batch_size, 0.0f);\n        deviceFill(output_ids, seq_len * batch_size, 0);\n\n        for (size_t step = 0; step < output_len; ++step) {\n            initRandom(h_logits, batch_size * vocab_size, -3.0f, 3.0f);\n            computeProb(h_probs, h_logits, batch_size, vocab_size);\n            cudaH2Dcpy(probs, h_probs, batch_size * vocab_size);\n\n            invokeTopPInitialize(topp_id_vals_buf,\n                                 end_offsets,\n                                 begin_offsets,\n                                 batch_size,\n                                 vocab_size,\n                                 stream);\n\n            invokeBatchTopPSampling<T>(workspace,\n                                       workspace_size,\n                                       cub_temp_storage_size,\n                                       output_ids + step * batch_size,\n                                       seq_lengths,\n                                       finished,\n                                       cum_lprobs,\n                                       output_lprobs + step * batch_size,\n                                       // Note that the kernel needs vocab probs instead of\n                                       // log-prob if cum_log_probs or output_log_probs are\n                                       // provided. It's because the sampling layer already\n                                       // preprocesses log_prob_buf when those are provided.\n                                       probs,\n                                       topp_id_vals_buf,\n                                       end_offsets,\n                                       begin_offsets,\n                                       curand_states,\n                                       batch_size,\n                                       vocab_size,\n                                       end_ids,\n                                       max_top_p,\n                                       top_ps,\n                                       stream,\n                                       &device_prop,\n                                       skip_decode);\n\n            // Compute reference.\n            cudaD2Hcpy(h_output_ids, output_ids + step * batch_size, batch_size);\n            cudaD2Hcpy(h_seq_lengths, seq_lengths, batch_size);\n            cudaD2Hcpy(h_finished, finished, batch_size);\n            computeLogProb(h_lprobs, h_logits, batch_size, vocab_size);\n            for (size_t i = 0; i < batch_size; ++i) {\n                if (!h_skip_decode[i]) {\n                    int idx = i * vocab_size + h_output_ids[i];\n                    expected_cum_lprobs[i] += (int)step < h_seq_lengths[i] ? (float)h_lprobs[idx] : 0.0f;\n                    EXPECT_EQ(h_finished[i], h_output_ids[i] == end_id);\n                }\n            }\n        }\n        bool passed = checkResult(param.toString(), cum_lprobs, expected_cum_lprobs, batch_size);\n        EXPECT_TRUE(passed) << \"Fail subtest (has_diff_runtime_args: \" << has_diff_runtime_args\n                            << \", skip_decode: \" << use_skip_decode << \")\";\n\n        delete[] expected_cum_lprobs;\n        delete[] h_seq_lengths;\n        delete[] h_logits;\n        delete[] h_lprobs;\n        delete[] h_probs;\n        delete[] h_output_ids;\n        delete[] h_top_ps;\n        delete[] h_skip_decode;\n    }\n\n    void runBatchTest(SamplingKernelTestParam param)\n    {\n        this->runBatchTest(param, false, false);\n        this->runBatchTest(param, false, true);\n        this->runBatchTest(param, true,  false);\n        this->runBatchTest(param, true,  true);\n    }\n};\n\nTYPED_TEST_SUITE(TopPSamplingKernelTest, FloatAndHalfTypes);\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessSmallP)\n{\n    this->runTest({6, 4, 1, 0, 0.2f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeP)\n{\n    this->runTest({6, 4, 1, 0, 0.9f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessAncestral)\n{\n    this->runTest({6, 4, 1, 0, 1.0f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeVocabSmallP)\n{\n    this->runTest({32, 51200, 1, 0, 0.2f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, CorrectnessLargeVocabLargeP)\n{\n    this->runTest({32, 51200, 1, 0, 0.9f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessSmallP)\n{\n    this->runBatchTest({6, 4, 1, 0, 0.2f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessLargeP)\n{\n    this->runBatchTest({6, 4, 1, 0, 0.9f, 1});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessSmallP2)\n{\n    this->runBatchTest({8, 4000, 1, 0, 0.2f, 16});\n};\n\nTYPED_TEST(TopPSamplingKernelTest, BatchCorrectnessLargeP2)\n{\n    this->runBatchTest({8, 4000, 1, 0, 0.9f, 16});\n};\n\n__global__\nvoid generateRandomNumber(unsigned int *vals, hiprandState_t *states, const int batch_size) {\n    int idx = threadIdx.x;\n    if (idx < batch_size) {\n        vals[idx] = hiprand(states + idx);\n    }\n}\n\nTEST(SamplingKernelTest, CurandBatchInitialize) {\n    size_t batch_size = 127;\n    hipStream_t stream;\n    hipStreamCreate(&stream);\n\n    hiprandState_t* curand_states;\n    check_cuda_error(hipMalloc(&curand_states, sizeof(hiprandState_t) * batch_size));\n    unsigned long long* h_random_seeds = new unsigned long long[batch_size];\n    const size_t period_size = 3;\n    for (size_t i = 0; i < batch_size; ++i) {\n        h_random_seeds[i] = i / period_size;\n    }\n    unsigned long long* d_random_seeds;\n    check_cuda_error(hipMalloc(&d_random_seeds, sizeof(unsigned long long) * batch_size));\n    check_cuda_error(hipMemcpy(d_random_seeds, h_random_seeds,\n                                sizeof(unsigned long long) * batch_size, hipMemcpyHostToDevice));\n\n    // Initialize curand states.\n    invokeCurandBatchInitialize(curand_states, batch_size, d_random_seeds, stream);\n    sync_check_cuda_error();\n\n    // Generate random numbers using initialized curand states.\n    unsigned int* d_rand_vals;\n    unsigned int* h_rand_vals = new unsigned int[batch_size];\n    check_cuda_error(hipMalloc(&d_rand_vals, sizeof(unsigned int) * batch_size));\n    generateRandomNumber<<<1, batch_size, 0, stream>>>(d_rand_vals, curand_states, batch_size);\n    check_cuda_error(hipMemcpyAsync(\n        h_rand_vals, d_rand_vals, sizeof(unsigned int) * batch_size, hipMemcpyDeviceToHost, stream));\n    check_cuda_error(hipStreamSynchronize(stream));\n\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < batch_size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            EXPECT_TRUE(h_rand_vals[i] == h_rand_vals[i + j])\n                << fmtstr(\"Fail at val[%d]=%d <> val[%d]=%d\", i, h_rand_vals[i], i + j, h_rand_vals[i + j]);\n        }\n    }\n\n    delete h_rand_vals;\n    delete h_random_seeds;\n    check_cuda_error(hipFree(d_rand_vals));\n    check_cuda_error(hipFree(d_random_seeds));\n    check_cuda_error(hipFree(curand_states));\n    check_cuda_error(hipStreamDestroy(stream));\n}\n\n}  // end of namespace\n", "source": "/kaggle/working/FasterTransformer/tests/unittests/test_sampling_kernels.cu"}
{"input": "#include <algorithm>  // std::min, std::max\n#include <iostream>   // snprintf\n#include <math.h>     // expf, log\n#include <stdlib.h>   // rand\n#include <string>     // std::string\n#include <vector>     // std::vector\n\n#include <cublasLt.h>\n#include <cublas_v2.h>\n#include <cuda_runtime.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/layers/DynamicDecodeLayer.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cublasMMWrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#include \"tests/unittests/unittest_utils.h\"\n\nusing namespace fastertransformer;\n\nstruct TestCase {\n    std::string name;\n    size_t      batch_size;\n    size_t      vocab_size;\n    size_t      beam_width;\n    size_t      top_k;\n    float       top_p;\n    size_t      output_len;\n\n    std::string toString()\n    {\n        char buf[100];\n        snprintf(buf,\n                 sizeof(buf),\n                 \"TestCase[name=%s, batch=%ld, vocab=%ld, beam=%ld, k=%ld, p=%3.1f, output_len=%ld]\",\n                 name.c_str(),\n                 batch_size,\n                 vocab_size,\n                 beam_width,\n                 top_k,\n                 top_p,\n                 output_len);\n        return buf;\n    }\n\n    void print()\n    {\n        FT_LOG_INFO(toString());\n    }\n};\n\ntemplate<typename T>\nvoid computeProb(T* probs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size vector.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf((float)logits[bidx * vocab_size + i]);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx    = bidx * vocab_size + i;\n            probs[idx] = static_cast<T>(expf((float)logits[idx]) / (sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid computeLogProb(T* logprobs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size vector.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(logits[bidx * vocab_size + i]);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx       = bidx * vocab_size + i;\n            logprobs[idx] = static_cast<T>(logf(expf(logits[idx]) / (sum + EPSILON) + EPSILON));\n        }\n    }\n}\n\n/////////////////////////////////// Tests //////////////////////////////////////////\n\ntemplate<typename T>\nvoid testCumLogProbComputation(TestCase tc)\n{\n\n    bool is_fp32 = std::is_same<T, float>::value;\n\n    size_t             beam_width = tc.beam_width;\n    uint               top_k      = tc.top_k;\n    float              top_p      = tc.top_p;\n    unsigned long long seed       = 0;\n    // use default values having no effect.\n    float temperature        = 1.0f;\n    float len_penalty        = 0.0f;\n    float repetition_penalty = 1.0f;\n\n    size_t batch_size     = tc.batch_size;\n    size_t vocab_size     = tc.vocab_size;\n    int    end_id         = 3;\n    size_t max_input_len  = 0;  // has no effect.\n    size_t max_output_len = tc.output_len;\n    size_t max_seq_len    = max_input_len + max_output_len;\n\n    struct cudaDeviceProp prop;\n    check_cuda_error(cudaGetDeviceProperties(&prop, 0));\n\n    cudaStream_t     stream;\n    cublasHandle_t   cublas_handle;\n    cublasLtHandle_t cublaslt_handle;\n    check_cuda_error(cudaStreamCreate(&stream));\n    check_cuda_error(cublasCreate(&cublas_handle));\n    check_cuda_error(cublasLtCreate(&cublaslt_handle));\n    check_cuda_error(cublasSetStream(cublas_handle, stream));\n\n    cublasAlgoMap                   cublas_algo_map(GEMM_CONFIG);\n    Allocator<AllocatorType::CUDA>* allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n    allocator->setStream(stream);\n\n    std::mutex*      cublas_wrapper_mutex = new std::mutex();\n    cublasMMWrapper* cublas_wrapper =\n        new cublasMMWrapper(cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n\n    DynamicDecodeLayer<T>* dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                                            vocab_size,\n                                                                            end_id,\n                                                                            stream,\n                                                                            cublas_wrapper,\n                                                                            allocator,\n                                                                            false,   // is_free_buffer_after_forward\n                                                                            &prop);  // cuda_device_prop\n\n    const DataType data_type   = getTensorType<T>();\n    size_t         logits_size = batch_size * beam_width * vocab_size;\n    T*             logits_buf  = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * logits_size, true));\n\n    // Logit values in the host of shape ((batch_size x beam) x vocab_size) where beam = 1.\n    T*     h_logits               = new T[batch_size * beam_width * vocab_size];\n    T*     h_probs                = new T[batch_size * beam_width * vocab_size];\n    T*     h_log_probs            = new T[batch_size * beam_width * vocab_size];\n    float* h_cum_log_probs        = new float[batch_size * beam_width];\n    float* h_output_log_probs     = new float[max_output_len * batch_size * beam_width];\n    float* expected_cum_log_probs = new float[batch_size * beam_width];\n    initRandom(h_logits, batch_size * beam_width * vocab_size, -10.0f / vocab_size, -1.0f);\n    computeProb(h_probs, h_logits, batch_size * beam_width, vocab_size);\n    computeLogProb(h_log_probs, h_logits, batch_size * beam_width, vocab_size);\n    memset(expected_cum_log_probs, 0, sizeof(float) * batch_size * beam_width);\n\n#ifndef NDEBUG\n    FT_LOG_DEBUG(\"logit values\");\n    printMatrixWithLimit(h_logits, batch_size * beam_width, vocab_size, vocab_size, false);\n    FT_LOG_DEBUG(\"\\nprob values\");\n    printMatrixWithLimit(h_probs, batch_size * beam_width, vocab_size, vocab_size, false);\n    FT_LOG_DEBUG(\"\\nlog-prob values\");\n    printMatrixWithLimit(h_log_probs, batch_size * beam_width, vocab_size, vocab_size, false);\n#endif\n\n    int*   tiled_input_lengths_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * beam_width));\n    float* cum_log_probs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size * beam_width));\n    float* output_log_probs =\n        reinterpret_cast<float*>(allocator->malloc(sizeof(float) * max_output_len * batch_size * beam_width));\n\n    int* output_ids   = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batch_size * beam_width));\n    int* h_output_ids = new int[batch_size * beam_width];\n\n    int* end_ids = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n    deviceFill(end_ids, batch_size, end_id);\n\n    // Init by zero.\n    cudaMemset(cum_log_probs, 0, sizeof(float) * batch_size * beam_width);\n    cudaMemset(output_log_probs, 0, sizeof(float) * max_output_len * batch_size * beam_width);\n    cudaMemset(output_ids, 0, sizeof(int) * max_seq_len * batch_size * beam_width);\n\n    TensorMap input_tensors({{\"random_seed\", {MEMORY_CPU, TYPE_INT32, {1}, &seed}},\n                             {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {1}, &top_k}},\n                             {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {1}, &top_p}},\n                             {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n                             {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n                             {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n    dynamic_decode_layer->setup(batch_size, beam_width, &input_tensors);\n\n    for (size_t step = max_input_len; step < max_output_len; ++step) {\n        uint ite = 0;\n        // Reset by the test value since the sampling layer internally update the logit buffer (making it log-prob).\n        cudaH2Dcpy(logits_buf, h_logits, logits_size);\n        TensorMap dynamic_decode_input_tensors(\n            {{\"logits\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size, beam_width, vocab_size}, logits_buf}},\n             {\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}},\n             {\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}},\n             {\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}},\n             {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, tiled_input_lengths_buf}},\n             {\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}},\n             {\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &batch_size}},\n             {\"end_id\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size}, end_ids}},\n             {\"random_seed\", {MEMORY_CPU, TYPE_UINT64, {1}, &seed}},\n             {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {1}, &top_k}},\n             {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {1}, &top_p}},\n             {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n             {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n             {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n\n        // common outputs\n        TensorMap dynamic_decode_output_tensors(\n            {{\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, output_ids}},\n             {\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}},\n             {\"cum_log_probs\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size * beam_width}, cum_log_probs}},\n             {\"output_log_probs\",\n              Tensor{MEMORY_GPU, TYPE_FP32, {max_seq_len, batch_size, beam_width}, output_log_probs}},\n             {\"parent_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, nullptr}},\n             {\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}},\n             // necessary for beam search.\n             {\"tgt_cache_indirection\",\n              Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width, max_output_len}, nullptr}}});\n\n        dynamic_decode_layer->forward(&dynamic_decode_output_tensors, &dynamic_decode_input_tensors);\n\n        FT_LOG_DEBUG(\"Step %2d generated ids\", step);\n        cudaD2Hcpy(\n            h_output_ids,\n            (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(step * (batch_size * beam_width)),\n            batch_size * beam_width);\n        cudaD2Hcpy(h_cum_log_probs, cum_log_probs, batch_size * beam_width);\n        cudaD2Hcpy(h_output_log_probs, output_log_probs, max_output_len * batch_size * beam_width);\n        for (size_t i = 0; i < batch_size * beam_width; ++i) {\n            int idx = i * vocab_size + h_output_ids[i];\n            expected_cum_log_probs[i] += (float)h_log_probs[idx];\n            FT_LOG_DEBUG(\"| step %2d batch %2d idx %7d id %6d | log-prob %9.4f (expt: %9.4f) \"\n                         \"| cum-log-prob %9.4f (expt: %9.4f) | prob %9.4e\",\n                         (int)step,\n                         (int)i,\n                         (int)idx,\n                         (int)h_output_ids[i],\n                         h_output_log_probs[step * batch_size * beam_width + i],\n                         (float)h_log_probs[idx],\n                         h_cum_log_probs[i],\n                         expected_cum_log_probs[i],\n                         (float)h_probs[idx]);\n        }\n        FT_LOG_DEBUG(\"\");\n\n#ifndef NDEBUG\n        // print output ids\n        for (size_t s = max_input_len; s < max_seq_len; ++s) {\n            cudaD2Hcpy(\n                h_output_ids,\n                (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(s * (batch_size * beam_width)),\n                batch_size * beam_width);\n            printf(\"%02d \", (int)s);\n            for (size_t b = 0; b < batch_size; ++b) {\n                printf(\"%3d \", (int)h_output_ids[b]);\n            }\n            printf(\"\\n\");\n        }\n#endif\n    }\n    std::string tag    = tc.toString() + (is_fp32 ? \" (fp32)\" : \" (fp16)\");\n    bool        passed = checkResult(tag, cum_log_probs, expected_cum_log_probs, batch_size * beam_width);\n    EXPECT_TRUE(passed);\n\n    delete[] expected_cum_log_probs;\n    delete[] h_output_log_probs;\n    delete[] h_cum_log_probs;\n    delete[] h_logits;\n    delete[] h_log_probs;\n    delete[] h_probs;\n    delete[] h_output_ids;\n\n    delete dynamic_decode_layer;\n    delete cublas_wrapper;\n    delete allocator;\n    check_cuda_error(cudaStreamDestroy(stream));\n    check_cuda_error(cublasDestroy(cublas_handle));\n    check_cuda_error(cublasLtDestroy(cublaslt_handle));\n}\n\nvoid printTensors(TensorMap* map, size_t limit = 8)\n{\n    FT_LOG_INFO(\"Tensors:\");\n    for (auto& kv : *map) {\n        Tensor t = kv.second;\n        FT_LOG_INFO(\" - %-18s : %s\", kv.first.c_str(), t.toString().c_str());\n    }\n}\n\ntemplate<typename T>\nclass SamplingDecodeTest {\nprivate:\n    unsigned long long              seed           = 0;\n    const static unsigned long long max_seed       = 30;\n    const size_t                    batch_size     = 6;\n    const size_t                    beam_width     = 1;\n    const size_t                    batchxbeam     = batch_size * beam_width;\n    const size_t                    vocab_size     = 8;\n    const size_t                    max_input_len  = 0;  // has no effect.\n    const size_t                    max_output_len = 3;\n    const size_t                    max_seq_len    = max_input_len + max_output_len;\n    const int                       end_id         = vocab_size - 1;\n    const DataType                  data_type      = getTensorType<T>();\n\n    // vocab size 8 & length 3\n    T* test_input_logits;\n\n    Allocator<AllocatorType::CUDA>* allocator;\n    std::mutex*                     cublas_wrapper_mutex;\n    cublasMMWrapper*                cublas_wrapper;\n    DynamicDecodeLayer<T>*          dynamic_decode_layer;\n\n    int*   h_output_ids;\n    T*     h_logits;\n    T*     h_probs;\n    T*     h_log_probs;\n    float* h_cum_log_probs;\n    float* h_output_log_probs;\n\n    T*     d_logits;\n    int*   d_input_lengths;\n    float* d_cum_log_probs;\n    float* d_output_log_probs;\n    int*   d_output_ids;\n    int*   d_end_ids;\n\n    void setup(unsigned long long seed = 0)\n    {\n        this->seed = seed;\n        struct cudaDeviceProp prop;\n        check_cuda_error(cudaGetDeviceProperties(&prop, 0));\n        cudaStream_t     stream;\n        cublasHandle_t   cublas_handle;\n        cublasLtHandle_t cublaslt_handle;\n        check_cuda_error(cudaStreamCreate(&stream));\n        check_cuda_error(cublasCreate(&cublas_handle));\n        check_cuda_error(cublasLtCreate(&cublaslt_handle));\n        check_cuda_error(cublasSetStream(cublas_handle, stream));\n        cublasAlgoMap cublas_algo_map(GEMM_CONFIG);\n        allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n        allocator->setStream(stream);\n        cublas_wrapper_mutex = new std::mutex();\n        cublas_wrapper       = new cublasMMWrapper(\n            cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n        dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                         vocab_size,\n                                                         end_id,\n                                                         stream,\n                                                         cublas_wrapper,\n                                                         allocator,\n                                                         false,   // is_free_buffer_after_forward\n                                                         &prop);  // cuda_device_prop\n\n        h_output_ids       = new int[batchxbeam];\n        h_logits           = new T[batchxbeam * vocab_size];\n        h_probs            = new T[batchxbeam * vocab_size];\n        h_log_probs        = new T[batchxbeam * vocab_size];\n        h_cum_log_probs    = new float[batchxbeam];\n        h_output_log_probs = new float[max_output_len * batchxbeam];\n\n        // prob = (0.4, 0.3, 0.2, 0.1, ...)\n        test_input_logits = new T[24]{\n            -0.9163,  -1.2040,  -1.6094,  -2.3026,  -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX,  // step 0\n            -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -0.9163,  -1.2040,  -1.6094,  -2.3026,   // step 1\n            -FLT_MAX, -FLT_MAX, -0.9163,  -1.2040,  -1.6094,  -2.3026,  -FLT_MAX, -FLT_MAX   // step 2\n        };\n\n        d_logits           = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batchxbeam * vocab_size, true));\n        d_input_lengths    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n        d_cum_log_probs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batchxbeam));\n        d_output_log_probs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * max_output_len * batchxbeam));\n        d_output_ids       = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batchxbeam));\n        d_end_ids          = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n\n        // Init by zero.\n        cudaMemset(d_cum_log_probs, 0, sizeof(float) * batchxbeam);\n        cudaMemset(d_output_log_probs, 0, sizeof(float) * max_output_len * batchxbeam);\n        cudaMemset(d_output_ids, 0, sizeof(int) * max_seq_len * batchxbeam);\n        deviceFill(d_end_ids, batchxbeam, end_id, stream);\n    }\n\n    void teardown()\n    {\n        delete[] test_input_logits;\n        delete[] h_output_ids;\n        delete[] h_logits;\n        delete[] h_probs;\n        delete[] h_log_probs;\n        delete[] h_cum_log_probs;\n        delete[] h_output_log_probs;\n        delete dynamic_decode_layer;\n        delete cublas_wrapper;\n        delete cublas_wrapper_mutex;\n        delete allocator;\n    }\n\n    TensorMap* createInputTensors(\n        int* topk, size_t topk_size, float* topp, size_t topp_size, float* temperature, float* repetition_penalty)\n    {\n        // construct common input tensors\n        TensorMap* input_tensors = new TensorMap();\n        if (topk != nullptr) {\n            input_tensors->insert({\"runtime_top_k\", {MEMORY_CPU, TYPE_INT32, {topk_size}, topk}});\n        }\n        if (topp != nullptr) {\n            input_tensors->insert({\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {topp_size}, topp}});\n        }\n        if (temperature != nullptr) {\n            input_tensors->insert({\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, temperature}});\n        }\n        if (repetition_penalty != nullptr) {\n            input_tensors->insert({\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, repetition_penalty}});\n        }\n        input_tensors->insert(\n            {\"logits\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size, beam_width, vocab_size}, d_logits}});\n        input_tensors->insert({\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}});\n        input_tensors->insert({\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}});\n        input_tensors->insert(\n            {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, d_input_lengths}});\n        input_tensors->insert({\"end_id\", Tensor{MEMORY_CPU, TYPE_INT32, {batchxbeam}, &d_end_ids}});\n        input_tensors->insert({\"random_seed\", Tensor{MEMORY_CPU, TYPE_UINT64, {1}, &seed}});\n        return input_tensors;\n    }\n\n    TensorMap* createOutputTensors()\n    {\n        // construct common output tensors\n        TensorMap* output_tensors = new TensorMap();\n        output_tensors->insert(\n            {\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, d_output_ids}});\n        output_tensors->insert({\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}});\n        output_tensors->insert(\n            {\"cum_log_probs\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size * beam_width}, d_cum_log_probs}});\n        output_tensors->insert(\n            {\"output_log_probs\",\n             Tensor{MEMORY_GPU, TYPE_FP32, {max_seq_len, batch_size, beam_width}, d_output_log_probs}});\n        output_tensors->insert({\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}});\n        return output_tensors;\n    }\n\n    void batchH2Dcpy(T* dst, T* src, size_t m, size_t n)\n    {\n        for (size_t i = 0; i < m; ++i) {\n            cudaH2Dcpy(dst + i * n, src, n);\n        }\n    }\n\n    bool checkResult(std::string name, int* d_output_ids, std::vector<std::set<int>>& expected_ids)\n    {\n        assert(expected_ids.size() == max_seq_len * batchxbeam);\n        int* h_output_ids = new int[max_seq_len * batchxbeam];\n        cudaD2Hcpy(h_output_ids, d_output_ids, max_seq_len * batchxbeam);\n        int failures = 0;\n        for (size_t i = 0; i < max_seq_len * batchxbeam; ++i) {\n            size_t        s     = i / batchxbeam;\n            size_t        b     = i % batchxbeam;\n            std::set<int> expts = expected_ids.at(i);\n            if (expts.count(h_output_ids[i]) == 0) {\n                if (failures < 10) {\n                    std::stringstream ss;\n                    ss << \" - Fail \" << name << \" (step=\" << s << \", batch=\" << b << \") \"\n                       << \"actual=\" << h_output_ids[i] << \", expected\";\n                    for (auto& expt : expts) {\n                        ss << \" \" << expt;\n                    }\n                    FT_LOG_DEBUG(\"%s\", ss.str().c_str());\n                }\n                ++failures;\n            }\n        }\n        delete[] h_output_ids;\n        FT_LOG_DEBUG(\"check...%6s : %s (failures: %d / %d)\",\n                     failures == 0 ? \"....OK\" : \"FAILED\",\n                     name.c_str(),\n                     failures,\n                     max_seq_len * batchxbeam);\n        return failures == 0;\n    }\n\n    bool testSampling(std::string                name,\n                      std::vector<std::set<int>> expected_output_ids,\n                      int*                       top_ks,\n                      size_t                     top_k_size,\n                      float*                     top_ps,\n                      size_t                     top_p_size,\n                      float*                     temperature,\n                      float*                     repetition_penalty)\n    {\n        FT_LOG_INFO(\"Test %s\", name.c_str());\n        std::string tag    = fmtstr(\"Test %s T=%s\", name.c_str(), std::is_same<T, float>::value ? \"fp32\" : \"fp16\");\n        bool        passed = true;\n        for (unsigned long long seed = 0; seed < max_seed; ++seed) {\n            this->setup(seed);\n            size_t     step = max_input_len;\n            uint       ite  = 0;\n            TensorMap* input_tensors =\n                createInputTensors(top_ks, top_k_size, top_ps, top_p_size, temperature, repetition_penalty);\n            input_tensors->insert({\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}});\n            input_tensors->insert({\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}});\n            input_tensors->insert({\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &batch_size}});\n            TensorMap* output_tensors = createOutputTensors();\n\n            dynamic_decode_layer->setup(batch_size, beam_width, input_tensors);\n            for (step = max_input_len; step < max_output_len; ++step) {\n                // Reset by the test value since the sampling layer internally update the logit buffer.\n                batchH2Dcpy(input_tensors->at(\"logits\").getPtr<T>(),\n                            test_input_logits + step * vocab_size,\n                            batchxbeam,\n                            vocab_size);\n                dynamic_decode_layer->forward(output_tensors, input_tensors);\n            }\n            bool is_ok = checkResult(tag + fmtstr(\" seed=%lld\", seed), d_output_ids, expected_output_ids);\n            passed &= is_ok;\n#ifndef NDEBUG\n            if (!is_ok) {\n                FT_LOG_ERROR(\"actual output ids\");\n                printMatrix(d_output_ids, max_seq_len, batch_size, batch_size, true);\n            }\n#endif\n            delete output_tensors;\n            delete input_tensors;\n            this->teardown();\n        }\n        FT_LOG_INFO(\"check...%6s : %s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n        return passed;\n    }\n\n    bool testSamplingWithLocalBatch(std::string                name,\n                                    std::vector<std::set<int>> expected_output_ids,\n                                    int*                       top_ks,\n                                    size_t                     top_k_size,\n                                    float*                     top_ps,\n                                    size_t                     top_p_size,\n                                    float*                     temperature,\n                                    float*                     repetition_penalty)\n    {\n        FT_LOG_INFO(\"Test %s\", name.c_str());\n        std::string tag    = fmtstr(\"Test %s T=%s\", name.c_str(), std::is_same<T, float>::value ? \"fp32\" : \"fp16\");\n        bool        passed = true;\n        size_t      local_batch_size = 2;\n        uint        ite              = 1;\n        for (unsigned long long seed = 0; seed < max_seed; ++seed) {\n            this->setup(seed);\n            size_t     step = max_input_len;\n            TensorMap* input_tensors =\n                createInputTensors(top_ks, top_k_size, top_ps, top_p_size, temperature, repetition_penalty);\n            input_tensors->insert({\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}});\n            input_tensors->insert({\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}});\n            input_tensors->insert({\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &local_batch_size}});\n            TensorMap* output_tensors = createOutputTensors();\n\n            dynamic_decode_layer->setup(batch_size, beam_width, input_tensors);\n            for (step = max_input_len; step < max_output_len; ++step) {\n                // Reset by the test value since the sampling layer internally update the logit buffer.\n                batchH2Dcpy(input_tensors->at(\"logits\").getPtr<T>(),\n                            test_input_logits + step * vocab_size,\n                            batchxbeam,\n                            vocab_size);\n                dynamic_decode_layer->forward(output_tensors, input_tensors);\n            }\n            bool is_ok = checkResult(tag + fmtstr(\" seed=%lld\", seed), d_output_ids, expected_output_ids);\n            passed &= is_ok;\n#ifndef NDEBUG\n            if (!is_ok) {\n                FT_LOG_ERROR(\"actual output ids\");\n                printMatrix(d_output_ids, max_seq_len, batch_size, batch_size, true);\n            }\n#endif\n            delete output_tensors;\n            delete input_tensors;\n            this->teardown();\n        }\n        FT_LOG_INFO(\"check...%6s : %s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n        return passed;\n    }\n\npublic:\n    void testTopK()\n    {\n        int                        top_k = 2;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            //  0       1       2       3       4       5\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},  // step 0\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},  // step 1\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(\"TopK\", expected_output_ids, &top_k, 1, nullptr, 0, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopK()\n    {\n        int*                       top_ks = new int[batch_size]{2, 1, 1, 2, 1, 1};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            //  0    1    2       3    4    5\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4},  // step 1\n            {2, 3},\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2}  // step 2\n        };\n        bool passed =\n            this->testSampling(\"BatchTopK\", expected_output_ids, top_ks, batch_size, nullptr, 0, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopP()\n    {\n        float                      top_p = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(\"TopP\", expected_output_ids, nullptr, 0, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopP()\n    {\n        float*                     top_ps = new float[batch_size]{0.3f, 0.5f, 0.5f, 0.3f, 0.5f, 0.5f};\n        std::vector<std::set<int>> expected_output_ids{\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},  // step 0\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},  // step 1\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(\"BatchTopP\", expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopKTopP()\n    {\n        int                        top_k = 2;\n        float                      top_p = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(\"TopP\", expected_output_ids, &top_k, 1, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopKTopP()\n    {\n        std::string                name   = \"BatchTopKTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 1, 2, 2, 1};\n        float                      top_p  = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopKBatchTopP()\n    {\n        std::string                name   = \"TopKBatchTopP\";\n        int                        top_k  = 2;\n        float*                     top_ps = new float[batch_size]{0.5, 0.3, 0.5, 0.5, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testBatchTopKBatchTopP()\n    {\n        std::string                name   = \"BatchTopKBatchTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 0, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopK()\n    {\n        std::string                name  = \"InvalidArgsZeroTopK\";\n        int                        top_k = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, nullptr, 0, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopP()\n    {\n        std::string                name  = \"InvalidArgsZeroTopP\";\n        float                      top_p = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, nullptr, 0, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopKTopP()\n    {\n        std::string                name  = \"InvalidArgsZeroTopKTopP\";\n        int                        top_k = 0;\n        float                      top_p = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroBatchTopKTopP()\n    {\n        std::string                name   = \"InvalidArgsZeroBatchTopKTopP\";\n        int*                       top_ks = new int[batch_size]{0, 0, 0, 0, 0, 0};\n        float                      top_p  = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopKBatchTopP()\n    {\n        std::string                name   = \"InvalidArgsZeroTopKBatchTopP\";\n        int                        top_k  = 0;\n        float*                     top_ps = new float[batch_size]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKContainZero\";\n        int*                       top_ks = new int[batch_size]{2, 1, 0, 0, 2, 1};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0},\n            {0},\n            {0, 1},\n            {0},  // step 0\n            {4, 5},\n            {4},\n            {4},\n            {4},\n            {4, 5},\n            {4},  // step 1\n            {2, 3},\n            {2},\n            {2},\n            {2},\n            {2, 3},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, nullptr, 0, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopPContainZero\";\n        float*                     top_ps = new float[batch_size]{0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 0.3f};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},\n            {4},\n            {4},  // step 1\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKTopPContainZero\";\n        int*                       top_ks = new int[batch_size]{2, 2, 1, 0, 2, 0};\n        float                      top_p  = 0.0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},\n            {0},  // step 0\n            {4, 5},\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},\n            {4},  // step 1\n            {2, 3},\n            {2, 3},\n            {2},\n            {2},\n            {2, 3},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsTopKBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsTopKBatchTopPContainZero\";\n        int                        top_k  = 0;\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},  // step 0\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},  // step 1\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKBatchTopPContainZero\";\n        int*                       top_ks = new int[batch_size]{0, 2, 1, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.9, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4},\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2},\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testLocalBatchBatchTopP()\n    {\n        std::string                name   = \"LocalBatch_BatchTopP\";\n        float*                     top_ps = new float[batch_size]{0.3f, 0.5f, 0.5f, 0.3f, 0.5f, 0.5f};\n        std::vector<std::set<int>> expected_output_ids{\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},\n            {0},  // step 0\n            {0},\n            {0},\n            {4, 5},\n            {4},\n            {0},\n            {0},  // step 1\n            {0},\n            {0},\n            {2, 3},\n            {2},\n            {0},\n            {0}  // step 2\n        };\n        bool passed = this->testSamplingWithLocalBatch(\n            name, expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testLocalBatchBatchTopKBatchTopP()\n    {\n        std::string                name   = \"LocalBatch_BatchTopKBatchTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 0, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {0},\n            {0},\n            {4, 5},\n            {4, 5},\n            {0},\n            {0},  // step 1\n            {0},\n            {0},\n            {2, 3},\n            {2, 3},\n            {0},\n            {0}  // step 2\n        };\n        bool passed = this->testSamplingWithLocalBatch(\n            name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testAll()\n    {\n        this->testTopK();\n        this->testTopP();\n        this->testTopKTopP();\n        this->testBatchTopK();\n        this->testBatchTopP();\n        this->testBatchTopKTopP();\n        this->testTopKBatchTopP();\n        this->testBatchTopKBatchTopP();\n        this->testInvalidArgsZeroTopK();\n        this->testInvalidArgsZeroTopP();\n        this->testInvalidArgsZeroBatchTopKTopP();\n        this->testInvalidArgsZeroTopKBatchTopP();\n        this->testInvalidArgsZeroTopKTopP();\n        this->testInvalidArgsBatchTopKContainZero();\n        this->testInvalidArgsBatchTopPContainZero();\n        this->testInvalidArgsBatchTopKTopPContainZero();\n        this->testInvalidArgsTopKBatchTopPContainZero();\n        this->testInvalidArgsBatchTopKBatchTopPContainZero();\n        this->testLocalBatchBatchTopP();\n        this->testLocalBatchBatchTopKBatchTopP();\n    }\n};\n\n__global__ void generateRandomNumber(unsigned int* vals, curandState_t* states, const int batch_size)\n{\n    int idx = threadIdx.x;\n    if (idx < batch_size) {\n        vals[idx] = curand(states + idx);\n    }\n}\n\ntemplate<typename T>\nstatic inline bool isEqualInPeriod(T* vals, size_t size, size_t period_size)\n{\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            if (vals[i] != vals[i + j]) {\n                FT_LOG_INFO(\" **** *** ** * [%d] %d <> [%d] %d\", i, vals[i], i + j, vals[i + j]);\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\ntemplate<typename T>\nstatic inline bool isEqualInPeriod(T* vals, size_t size, size_t period_size, size_t except)\n{\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            if (j != except && vals[i] != vals[i + j]) {\n                FT_LOG_INFO(\" **** *** ** * [%d] %d <> [%d] %d\", i, vals[i], i + j, vals[i + j]);\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nvoid testCuandBatchInitialize(const size_t batch_size)\n{\n    cudaStream_t stream;\n    cudaStreamCreate(&stream);\n\n    curandState_t* curand_states;\n    check_cuda_error(cudaMalloc(&curand_states, sizeof(curandState_t) * batch_size));\n    unsigned long long* h_random_seeds = new unsigned long long[batch_size];\n    const size_t        period_size    = 3;\n    for (size_t i = 0; i < batch_size; ++i) {\n        h_random_seeds[i] = i / period_size;\n    }\n    unsigned long long* d_random_seeds;\n    check_cuda_error(cudaMalloc(&d_random_seeds, sizeof(unsigned long long) * batch_size));\n    check_cuda_error(\n        cudaMemcpy(d_random_seeds, h_random_seeds, sizeof(unsigned long long) * batch_size, cudaMemcpyHostToDevice));\n\n    // Initialize curand states.\n    invokeCurandBatchInitialize(curand_states, batch_size, d_random_seeds, stream);\n    sync_check_cuda_error();\n\n    // Generate random numbers using initialized curand states.\n    unsigned int* d_rand_vals;\n    unsigned int* h_rand_vals = new unsigned int[batch_size];\n    check_cuda_error(cudaMalloc(&d_rand_vals, sizeof(unsigned int) * batch_size));\n    generateRandomNumber<<<1, batch_size, 0, stream>>>(d_rand_vals, curand_states, batch_size);\n    check_cuda_error(\n        cudaMemcpyAsync(h_rand_vals, d_rand_vals, sizeof(unsigned int) * batch_size, cudaMemcpyDeviceToHost, stream));\n    check_cuda_error(cudaStreamSynchronize(stream));\n\n    // The same seed produces the same random number.\n    bool passed = isEqualInPeriod(h_rand_vals, batch_size, period_size);\n    FT_LOG_INFO(\"CuandBatchInitTest check....... : %s\", passed ? \"OK\" : \"FAILED\");\n    EXPECT_TRUE(passed);\n\n    delete h_rand_vals;\n    delete h_random_seeds;\n\n    check_cuda_error(cudaFree(d_rand_vals));\n    check_cuda_error(cudaFree(d_random_seeds));\n    check_cuda_error(cudaFree(curand_states));\n    check_cuda_error(cudaStreamDestroy(stream));\n}\n\ntemplate<typename T, bool SINGLE_RANDOM_SEED, bool HAS_DIFF_ARGS, bool USE_LOCAL_BATCH>\nvoid testSamplingLayerCurandInit(TestCase tc)\n{\n    FT_LOG_DEBUG(\"testSamplingLayerCurandInit %s\", tc.toString().c_str());\n    const DataType data_type = getTensorType<T>();\n\n    const size_t beam_width = 1;\n    const uint   top_k      = tc.top_k;\n    const float  top_p      = tc.top_p;\n    // use default values having no effect.\n    const float temperature        = 1.0f;\n    const float len_penalty        = 0.0f;\n    const float repetition_penalty = 1.0f;\n    const int   end_id             = 3;\n\n    const size_t batch_size       = tc.batch_size;\n    const size_t batchxbeam       = batch_size * beam_width;\n    const size_t local_batch_size = USE_LOCAL_BATCH ? 2 : batch_size;\n    assert(batch_size % local_batch_size == 0);\n    const size_t vocab_size     = tc.vocab_size;\n    const size_t max_input_len  = 0;  // has no effect.\n    const size_t max_output_len = tc.output_len;\n    const size_t max_seq_len    = max_input_len + max_output_len;\n\n    struct cudaDeviceProp prop;\n    check_cuda_error(cudaGetDeviceProperties(&prop, 0));\n\n    cudaStream_t     stream;\n    cublasHandle_t   cublas_handle;\n    cublasLtHandle_t cublaslt_handle;\n    check_cuda_error(cudaStreamCreate(&stream));\n    check_cuda_error(cublasCreate(&cublas_handle));\n    check_cuda_error(cublasLtCreate(&cublaslt_handle));\n    check_cuda_error(cublasSetStream(cublas_handle, stream));\n    cublasAlgoMap                   cublas_algo_map(GEMM_CONFIG);\n    std::mutex*                     cublas_wrapper_mutex = new std::mutex();\n    Allocator<AllocatorType::CUDA>* allocator            = new Allocator<AllocatorType::CUDA>(getDevice());\n    allocator->setStream(stream);\n    cublasMMWrapper* cublas_wrapper =\n        new cublasMMWrapper(cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n    DynamicDecodeLayer<T>* dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                                            vocab_size,\n                                                                            end_id,\n                                                                            stream,\n                                                                            cublas_wrapper,\n                                                                            allocator,\n                                                                            false,   // is_free_buffer_after_forward\n                                                                            &prop);  // cuda_device_prop\n\n    T*   h_logits     = reinterpret_cast<T*>(malloc(sizeof(T) * batchxbeam * vocab_size));\n    int* h_output_ids = reinterpret_cast<int*>(malloc(sizeof(int) * batchxbeam));\n\n    T*   d_logits_buf    = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batchxbeam * vocab_size));\n    int* d_input_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n    int* d_output_ids    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batchxbeam));\n\n    // Init by zero.\n    cudaMemset(d_input_lengths, 0, sizeof(int) * batchxbeam);\n    cudaMemset(d_output_ids, 0, sizeof(int) * max_seq_len * batchxbeam);\n\n    // Prepare decoding arguments\n    const size_t        random_seed_size = SINGLE_RANDOM_SEED ? 1 : batch_size;\n    const size_t        period_size      = 3;\n    unsigned long long* random_seed      = new unsigned long long[random_seed_size];\n    for (size_t i = 0; i < random_seed_size; ++i) {\n        random_seed[i] = i / period_size;\n    }\n    const bool   has_diff_runtime_args = HAS_DIFF_ARGS;\n    const size_t runtime_args_size     = has_diff_runtime_args ? batch_size : 1;\n    uint*        runtime_top_k         = new uint[runtime_args_size];\n    float*       runtime_top_p         = new float[runtime_args_size];\n    const size_t except_idx            = 1;\n    for (size_t i = 0; i < runtime_args_size; ++i) {\n        runtime_top_k[i] = (top_k > 1) && (i % period_size == except_idx) ? 1 : top_k;\n        runtime_top_p[i] = (i % period_size == except_idx) ? top_p * 0.1f : top_p;\n    }\n    int* d_end_id_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n    deviceFill(d_end_id_buf, batch_size, end_id);\n\n#ifndef NDEBUG\n    FT_LOG_DEBUG(\"Random Seeds\");\n    printMatrixWithLimit(random_seed, 1, random_seed_size, random_seed_size, false);\n#endif\n\n    bool passed = true;\n\n    TensorMap runtime_args;\n    runtime_args.insert({\"has_diff_runtime_args\", Tensor(MEMORY_CPU, TYPE_BOOL, {1}, &has_diff_runtime_args)});\n    runtime_args.insert({\"random_seed\", Tensor(MEMORY_CPU, TYPE_UINT64, {random_seed_size}, random_seed)});\n    runtime_args.insert({\"runtime_top_k\", Tensor(MEMORY_CPU, TYPE_INT32, {runtime_args_size}, runtime_top_k)});\n    runtime_args.insert({\"runtime_top_p\", Tensor(MEMORY_CPU, TYPE_FP32, {runtime_args_size}, runtime_top_p)});\n    runtime_args.insert({\"temperature\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &temperature)});\n    runtime_args.insert({\"len_penalty\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &len_penalty)});\n    runtime_args.insert({\"repetition_penalty\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty)});\n    dynamic_decode_layer->setup(batch_size, beam_width, &runtime_args);\n\n    for (size_t step = max_input_len; step < max_output_len; ++step) {\n        const size_t iteration_num = batch_size / local_batch_size;\n\n        initRandom(h_logits, beam_width * vocab_size, -10.0f / vocab_size, -1.0f);\n        tile(h_logits, batch_size, beam_width * vocab_size);\n        cudaH2Dcpy(d_logits_buf, h_logits, batchxbeam * vocab_size);\n\n#ifndef NDEBUG\n        FT_LOG_DEBUG(\"logit values\");\n        printMatrixWithLimit(h_logits, batchxbeam, vocab_size, vocab_size, false);\n#endif\n        for (uint ite = 0; ite < iteration_num; ++ite) {\n            TensorMap dynamic_decode_input_tensors(\n                {{\"logits\", Tensor{MEMORY_GPU, data_type, {batch_size, beam_width, vocab_size}, d_logits_buf}},\n                 {\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}},\n                 {\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}},\n                 {\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}},\n                 {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, d_input_lengths}},\n                 {\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}},\n                 {\"has_diff_runtime_args\", Tensor{MEMORY_CPU, TYPE_BOOL, {1}, &has_diff_runtime_args}},\n                 {\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &local_batch_size}},\n                 {\"end_id\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size}, d_end_id_buf}},\n                 {\"random_seed\", {MEMORY_CPU, TYPE_UINT64, {random_seed_size}, random_seed}},\n                 {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {runtime_args_size}, runtime_top_k}},\n                 {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {runtime_args_size}, runtime_top_p}},\n                 {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n                 {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n                 {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n\n            // common outputs\n            TensorMap dynamic_decode_output_tensors(\n                {{\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, d_output_ids}},\n                 {\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}},\n                 {\"parent_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, nullptr}},\n                 {\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}},\n                 // necessary for beam search.\n                 {\"tgt_cache_indirection\",\n                  Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width, max_output_len}, nullptr}}});\n\n            dynamic_decode_layer->forward(&dynamic_decode_output_tensors, &dynamic_decode_input_tensors);\n            sync_check_cuda_error();\n#ifndef NDEBUG\n            FT_LOG_DEBUG(\"Step %2d generated ids\", step);\n            printMatrix(d_output_ids, max_seq_len, batchxbeam, batchxbeam, true);\n            FT_LOG_DEBUG(\"\");\n#endif\n            // check results.\n            cudaD2Hcpy(h_output_ids,\n                       (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(step * batchxbeam),\n                       batchxbeam);\n        }\n        bool is_ok = isEqualInPeriod(h_output_ids, batchxbeam, period_size, except_idx);\n        passed &= is_ok;\n    }\n    std::string tag = fmtstr(\"%s (seed=%-6s has_diff_args=%-5s local_batch=%-5s T=%s)\",\n                             tc.toString().c_str(),\n                             SINGLE_RANDOM_SEED ? \"single\" : \"multi\",\n                             HAS_DIFF_ARGS ? \"true\" : \"false\",\n                             USE_LOCAL_BATCH ? \"true\" : \"false\",\n                             (std::is_same<T, float>::value ? \" fp32\" : \" fp16\"));\n    FT_LOG_INFO(\"check...%s SamplingLayerCurandInitTest %-30s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n    EXPECT_TRUE(passed);\n\n    free(h_logits);\n    free(h_output_ids);\n\n    delete dynamic_decode_layer;\n    delete runtime_top_k;\n    delete runtime_top_p;\n    delete random_seed;\n    delete cublas_wrapper;\n    delete allocator;\n    check_cuda_error(cudaStreamDestroy(stream));\n    check_cuda_error(cublasDestroy(cublas_handle));\n    check_cuda_error(cublasLtDestroy(cublaslt_handle));\n}\n\nint main()\n{\n    std::vector<TestCase> test_cases{\n        // TC: name / batch / vocab / beam / k / p / outlen\n        TestCase{\"topk\", 6, 4, 1, 1, 0.0f, 4},\n        TestCase{\"topk\", 6, 4, 1, 4, 0.0f, 4},\n        TestCase{\"topk\", 6, 51200, 1, 31, 0.0f, 16},\n        TestCase{\"topk\", 32, 51200, 1, 63, 0.0f, 16},\n        TestCase{\"topk\", 32, 51200, 1, 64, 0.0f, 16},\n        TestCase{\"topp\", 6, 4, 1, 0, 0.2f, 4},\n        TestCase{\"topp\", 6, 4, 1, 0, 0.8f, 4},\n        TestCase{\"topp\", 6, 4, 1, 0, 1.0f, 4},\n        TestCase{\"topp\", 6, 51200, 1, 0, 0.8f, 16},\n        TestCase{\"topp\", 32, 51200, 1, 0, 0.8f, 16},\n        TestCase{\"topp\", 32, 51200, 1, 0, 1.0f, 16},\n        TestCase{\"topk_topp\", 6, 4, 1, 1, 0.8f, 16},\n        TestCase{\"topk_topp\", 6, 4, 1, 4, 1.0f, 16},\n        TestCase{\"topk_topp\", 6, 51200, 1, 31, 0.8f, 16},\n        TestCase{\"topk_topp\", 32, 51200, 1, 63, 0.8f, 16},\n        TestCase{\"topk_topp\", 32, 51200, 1, 64, 1.0f, 16},\n    };\n\n    for (auto& tc : test_cases) {\n        testCumLogProbComputation<float>(tc);\n        testCumLogProbComputation<half>(tc);\n    }\n    FT_LOG_INFO(\"testCumLogProbComputation done\");\n\n    SamplingDecodeTest<float> sampling_decode_test;\n    sampling_decode_test.testAll();\n\n    testCuandBatchInitialize(127);\n    FT_LOG_INFO(\"testCuandBatchInitialize done\");\n\n#define LAUNCH_VARIANTS(T, tc, local_batch)                                                                            \\\n    testSamplingLayerCurandInit<T, true, false, local_batch>(tc);                                                      \\\n    testSamplingLayerCurandInit<T, true, true, local_batch>(tc);                                                       \\\n    testSamplingLayerCurandInit<T, false, false, local_batch>(tc);                                                     \\\n    testSamplingLayerCurandInit<T, false, true, local_batch>(tc);\n    for (auto& tc : test_cases) {\n        LAUNCH_VARIANTS(float, tc, false);  // without local batch\n        LAUNCH_VARIANTS(half, tc, false);\n        LAUNCH_VARIANTS(float, tc, true);  // with local batch\n        LAUNCH_VARIANTS(half, tc, true);\n    }\n#undef LAUNCH_VARIANTS\n    FT_LOG_INFO(\"testSamplingLayerCurandInit done\");\n\n    return 0;\n}\n", "output": "#include <algorithm>  // std::min, std::max\n#include <iostream>   // snprintf\n#include <math.h>     // expf, log\n#include <stdlib.h>   // rand\n#include <string>     // std::string\n#include <vector>     // std::vector\n\n#include <hipblaslt.h>\n#include <hipblas.h>\n#include <hip/hip_runtime.h>\n\n#include \"src/fastertransformer/kernels/sampling_topk_kernels.h\"\n#include \"src/fastertransformer/layers/DynamicDecodeLayer.h\"\n#include \"src/fastertransformer/layers/sampling_layers/TopKSamplingLayer.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n#include \"src/fastertransformer/utils/cublasMMWrapper.h\"\n#include \"src/fastertransformer/utils/cuda_utils.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n\n#include \"tests/unittests/unittest_utils.h\"\n\nusing namespace fastertransformer;\n\nstruct TestCase {\n    std::string name;\n    size_t      batch_size;\n    size_t      vocab_size;\n    size_t      beam_width;\n    size_t      top_k;\n    float       top_p;\n    size_t      output_len;\n\n    std::string toString()\n    {\n        char buf[100];\n        snprintf(buf,\n                 sizeof(buf),\n                 \"TestCase[name=%s, batch=%ld, vocab=%ld, beam=%ld, k=%ld, p=%3.1f, output_len=%ld]\",\n                 name.c_str(),\n                 batch_size,\n                 vocab_size,\n                 beam_width,\n                 top_k,\n                 top_p,\n                 output_len);\n        return buf;\n    }\n\n    void print()\n    {\n        FT_LOG_INFO(toString());\n    }\n};\n\ntemplate<typename T>\nvoid computeProb(T* probs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size vector.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf((float)logits[bidx * vocab_size + i]);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx    = bidx * vocab_size + i;\n            probs[idx] = static_cast<T>(expf((float)logits[idx]) / (sum + EPSILON));\n        }\n    }\n}\n\ntemplate<typename T>\nvoid computeLogProb(T* logprobs, T* logits, int batch_size, int vocab_size)\n{\n    // Compute the log probability from logits.\n    //   logits = batch_size x vocab_size vector.\n    //   logprobs = log(softmax(logits)) (softmax along with vocab dimension)\n    for (int bidx = 0; bidx < batch_size; ++bidx) {\n        float sum = 0.0f;\n        for (int i = 0; i < vocab_size; ++i) {\n            sum += expf(logits[bidx * vocab_size + i]);\n        }\n        for (int i = 0; i < vocab_size; ++i) {\n            int idx       = bidx * vocab_size + i;\n            logprobs[idx] = static_cast<T>(logf(expf(logits[idx]) / (sum + EPSILON) + EPSILON));\n        }\n    }\n}\n\n/////////////////////////////////// Tests //////////////////////////////////////////\n\ntemplate<typename T>\nvoid testCumLogProbComputation(TestCase tc)\n{\n\n    bool is_fp32 = std::is_same<T, float>::value;\n\n    size_t             beam_width = tc.beam_width;\n    uint               top_k      = tc.top_k;\n    float              top_p      = tc.top_p;\n    unsigned long long seed       = 0;\n    // use default values having no effect.\n    float temperature        = 1.0f;\n    float len_penalty        = 0.0f;\n    float repetition_penalty = 1.0f;\n\n    size_t batch_size     = tc.batch_size;\n    size_t vocab_size     = tc.vocab_size;\n    int    end_id         = 3;\n    size_t max_input_len  = 0;  // has no effect.\n    size_t max_output_len = tc.output_len;\n    size_t max_seq_len    = max_input_len + max_output_len;\n\n    struct hipDeviceProp_t prop;\n    check_cuda_error(hipGetDeviceProperties(&prop, 0));\n\n    hipStream_t     stream;\n    hipblasHandle_t   cublas_handle;\n    hipblasLtHandle_t cublaslt_handle;\n    check_cuda_error(hipStreamCreate(&stream));\n    check_cuda_error(hipblasCreate(&cublas_handle));\n    check_cuda_error(hipblasLtCreate(&cublaslt_handle));\n    check_cuda_error(hipblasSetStream(cublas_handle, stream));\n\n    cublasAlgoMap                   cublas_algo_map(GEMM_CONFIG);\n    Allocator<AllocatorType::CUDA>* allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n    allocator->setStream(stream);\n\n    std::mutex*      cublas_wrapper_mutex = new std::mutex();\n    cublasMMWrapper* cublas_wrapper =\n        new cublasMMWrapper(cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n\n    DynamicDecodeLayer<T>* dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                                            vocab_size,\n                                                                            end_id,\n                                                                            stream,\n                                                                            cublas_wrapper,\n                                                                            allocator,\n                                                                            false,   // is_free_buffer_after_forward\n                                                                            &prop);  // cuda_device_prop\n\n    const DataType data_type   = getTensorType<T>();\n    size_t         logits_size = batch_size * beam_width * vocab_size;\n    T*             logits_buf  = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * logits_size, true));\n\n    // Logit values in the host of shape ((batch_size x beam) x vocab_size) where beam = 1.\n    T*     h_logits               = new T[batch_size * beam_width * vocab_size];\n    T*     h_probs                = new T[batch_size * beam_width * vocab_size];\n    T*     h_log_probs            = new T[batch_size * beam_width * vocab_size];\n    float* h_cum_log_probs        = new float[batch_size * beam_width];\n    float* h_output_log_probs     = new float[max_output_len * batch_size * beam_width];\n    float* expected_cum_log_probs = new float[batch_size * beam_width];\n    initRandom(h_logits, batch_size * beam_width * vocab_size, -10.0f / vocab_size, -1.0f);\n    computeProb(h_probs, h_logits, batch_size * beam_width, vocab_size);\n    computeLogProb(h_log_probs, h_logits, batch_size * beam_width, vocab_size);\n    memset(expected_cum_log_probs, 0, sizeof(float) * batch_size * beam_width);\n\n#ifndef NDEBUG\n    FT_LOG_DEBUG(\"logit values\");\n    printMatrixWithLimit(h_logits, batch_size * beam_width, vocab_size, vocab_size, false);\n    FT_LOG_DEBUG(\"\\nprob values\");\n    printMatrixWithLimit(h_probs, batch_size * beam_width, vocab_size, vocab_size, false);\n    FT_LOG_DEBUG(\"\\nlog-prob values\");\n    printMatrixWithLimit(h_log_probs, batch_size * beam_width, vocab_size, vocab_size, false);\n#endif\n\n    int*   tiled_input_lengths_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size * beam_width));\n    float* cum_log_probs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batch_size * beam_width));\n    float* output_log_probs =\n        reinterpret_cast<float*>(allocator->malloc(sizeof(float) * max_output_len * batch_size * beam_width));\n\n    int* output_ids   = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batch_size * beam_width));\n    int* h_output_ids = new int[batch_size * beam_width];\n\n    int* end_ids = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n    deviceFill(end_ids, batch_size, end_id);\n\n    // Init by zero.\n    hipMemset(cum_log_probs, 0, sizeof(float) * batch_size * beam_width);\n    hipMemset(output_log_probs, 0, sizeof(float) * max_output_len * batch_size * beam_width);\n    hipMemset(output_ids, 0, sizeof(int) * max_seq_len * batch_size * beam_width);\n\n    TensorMap input_tensors({{\"random_seed\", {MEMORY_CPU, TYPE_INT32, {1}, &seed}},\n                             {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {1}, &top_k}},\n                             {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {1}, &top_p}},\n                             {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n                             {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n                             {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n    dynamic_decode_layer->setup(batch_size, beam_width, &input_tensors);\n\n    for (size_t step = max_input_len; step < max_output_len; ++step) {\n        uint ite = 0;\n        // Reset by the test value since the sampling layer internally update the logit buffer (making it log-prob).\n        cudaH2Dcpy(logits_buf, h_logits, logits_size);\n        TensorMap dynamic_decode_input_tensors(\n            {{\"logits\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size, beam_width, vocab_size}, logits_buf}},\n             {\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}},\n             {\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}},\n             {\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}},\n             {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, tiled_input_lengths_buf}},\n             {\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}},\n             {\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &batch_size}},\n             {\"end_id\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size}, end_ids}},\n             {\"random_seed\", {MEMORY_CPU, TYPE_UINT64, {1}, &seed}},\n             {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {1}, &top_k}},\n             {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {1}, &top_p}},\n             {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n             {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n             {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n\n        // common outputs\n        TensorMap dynamic_decode_output_tensors(\n            {{\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, output_ids}},\n             {\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}},\n             {\"cum_log_probs\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size * beam_width}, cum_log_probs}},\n             {\"output_log_probs\",\n              Tensor{MEMORY_GPU, TYPE_FP32, {max_seq_len, batch_size, beam_width}, output_log_probs}},\n             {\"parent_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, nullptr}},\n             {\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}},\n             // necessary for beam search.\n             {\"tgt_cache_indirection\",\n              Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width, max_output_len}, nullptr}}});\n\n        dynamic_decode_layer->forward(&dynamic_decode_output_tensors, &dynamic_decode_input_tensors);\n\n        FT_LOG_DEBUG(\"Step %2d generated ids\", step);\n        cudaD2Hcpy(\n            h_output_ids,\n            (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(step * (batch_size * beam_width)),\n            batch_size * beam_width);\n        cudaD2Hcpy(h_cum_log_probs, cum_log_probs, batch_size * beam_width);\n        cudaD2Hcpy(h_output_log_probs, output_log_probs, max_output_len * batch_size * beam_width);\n        for (size_t i = 0; i < batch_size * beam_width; ++i) {\n            int idx = i * vocab_size + h_output_ids[i];\n            expected_cum_log_probs[i] += (float)h_log_probs[idx];\n            FT_LOG_DEBUG(\"| step %2d batch %2d idx %7d id %6d | log-prob %9.4f (expt: %9.4f) \"\n                         \"| cum-log-prob %9.4f (expt: %9.4f) | prob %9.4e\",\n                         (int)step,\n                         (int)i,\n                         (int)idx,\n                         (int)h_output_ids[i],\n                         h_output_log_probs[step * batch_size * beam_width + i],\n                         (float)h_log_probs[idx],\n                         h_cum_log_probs[i],\n                         expected_cum_log_probs[i],\n                         (float)h_probs[idx]);\n        }\n        FT_LOG_DEBUG(\"\");\n\n#ifndef NDEBUG\n        // print output ids\n        for (size_t s = max_input_len; s < max_seq_len; ++s) {\n            cudaD2Hcpy(\n                h_output_ids,\n                (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(s * (batch_size * beam_width)),\n                batch_size * beam_width);\n            printf(\"%02d \", (int)s);\n            for (size_t b = 0; b < batch_size; ++b) {\n                printf(\"%3d \", (int)h_output_ids[b]);\n            }\n            printf(\"\\n\");\n        }\n#endif\n    }\n    std::string tag    = tc.toString() + (is_fp32 ? \" (fp32)\" : \" (fp16)\");\n    bool        passed = checkResult(tag, cum_log_probs, expected_cum_log_probs, batch_size * beam_width);\n    EXPECT_TRUE(passed);\n\n    delete[] expected_cum_log_probs;\n    delete[] h_output_log_probs;\n    delete[] h_cum_log_probs;\n    delete[] h_logits;\n    delete[] h_log_probs;\n    delete[] h_probs;\n    delete[] h_output_ids;\n\n    delete dynamic_decode_layer;\n    delete cublas_wrapper;\n    delete allocator;\n    check_cuda_error(hipStreamDestroy(stream));\n    check_cuda_error(hipblasDestroy(cublas_handle));\n    check_cuda_error(hipblasLtDestroy(cublaslt_handle));\n}\n\nvoid printTensors(TensorMap* map, size_t limit = 8)\n{\n    FT_LOG_INFO(\"Tensors:\");\n    for (auto& kv : *map) {\n        Tensor t = kv.second;\n        FT_LOG_INFO(\" - %-18s : %s\", kv.first.c_str(), t.toString().c_str());\n    }\n}\n\ntemplate<typename T>\nclass SamplingDecodeTest {\nprivate:\n    unsigned long long              seed           = 0;\n    const static unsigned long long max_seed       = 30;\n    const size_t                    batch_size     = 6;\n    const size_t                    beam_width     = 1;\n    const size_t                    batchxbeam     = batch_size * beam_width;\n    const size_t                    vocab_size     = 8;\n    const size_t                    max_input_len  = 0;  // has no effect.\n    const size_t                    max_output_len = 3;\n    const size_t                    max_seq_len    = max_input_len + max_output_len;\n    const int                       end_id         = vocab_size - 1;\n    const DataType                  data_type      = getTensorType<T>();\n\n    // vocab size 8 & length 3\n    T* test_input_logits;\n\n    Allocator<AllocatorType::CUDA>* allocator;\n    std::mutex*                     cublas_wrapper_mutex;\n    cublasMMWrapper*                cublas_wrapper;\n    DynamicDecodeLayer<T>*          dynamic_decode_layer;\n\n    int*   h_output_ids;\n    T*     h_logits;\n    T*     h_probs;\n    T*     h_log_probs;\n    float* h_cum_log_probs;\n    float* h_output_log_probs;\n\n    T*     d_logits;\n    int*   d_input_lengths;\n    float* d_cum_log_probs;\n    float* d_output_log_probs;\n    int*   d_output_ids;\n    int*   d_end_ids;\n\n    void setup(unsigned long long seed = 0)\n    {\n        this->seed = seed;\n        struct hipDeviceProp_t prop;\n        check_cuda_error(hipGetDeviceProperties(&prop, 0));\n        hipStream_t     stream;\n        hipblasHandle_t   cublas_handle;\n        hipblasLtHandle_t cublaslt_handle;\n        check_cuda_error(hipStreamCreate(&stream));\n        check_cuda_error(hipblasCreate(&cublas_handle));\n        check_cuda_error(hipblasLtCreate(&cublaslt_handle));\n        check_cuda_error(hipblasSetStream(cublas_handle, stream));\n        cublasAlgoMap cublas_algo_map(GEMM_CONFIG);\n        allocator = new Allocator<AllocatorType::CUDA>(getDevice());\n        allocator->setStream(stream);\n        cublas_wrapper_mutex = new std::mutex();\n        cublas_wrapper       = new cublasMMWrapper(\n            cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n        dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                         vocab_size,\n                                                         end_id,\n                                                         stream,\n                                                         cublas_wrapper,\n                                                         allocator,\n                                                         false,   // is_free_buffer_after_forward\n                                                         &prop);  // cuda_device_prop\n\n        h_output_ids       = new int[batchxbeam];\n        h_logits           = new T[batchxbeam * vocab_size];\n        h_probs            = new T[batchxbeam * vocab_size];\n        h_log_probs        = new T[batchxbeam * vocab_size];\n        h_cum_log_probs    = new float[batchxbeam];\n        h_output_log_probs = new float[max_output_len * batchxbeam];\n\n        // prob = (0.4, 0.3, 0.2, 0.1, ...)\n        test_input_logits = new T[24]{\n            -0.9163,  -1.2040,  -1.6094,  -2.3026,  -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX,  // step 0\n            -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX, -0.9163,  -1.2040,  -1.6094,  -2.3026,   // step 1\n            -FLT_MAX, -FLT_MAX, -0.9163,  -1.2040,  -1.6094,  -2.3026,  -FLT_MAX, -FLT_MAX   // step 2\n        };\n\n        d_logits           = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batchxbeam * vocab_size, true));\n        d_input_lengths    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n        d_cum_log_probs    = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * batchxbeam));\n        d_output_log_probs = reinterpret_cast<float*>(allocator->malloc(sizeof(float) * max_output_len * batchxbeam));\n        d_output_ids       = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batchxbeam));\n        d_end_ids          = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n\n        // Init by zero.\n        hipMemset(d_cum_log_probs, 0, sizeof(float) * batchxbeam);\n        hipMemset(d_output_log_probs, 0, sizeof(float) * max_output_len * batchxbeam);\n        hipMemset(d_output_ids, 0, sizeof(int) * max_seq_len * batchxbeam);\n        deviceFill(d_end_ids, batchxbeam, end_id, stream);\n    }\n\n    void teardown()\n    {\n        delete[] test_input_logits;\n        delete[] h_output_ids;\n        delete[] h_logits;\n        delete[] h_probs;\n        delete[] h_log_probs;\n        delete[] h_cum_log_probs;\n        delete[] h_output_log_probs;\n        delete dynamic_decode_layer;\n        delete cublas_wrapper;\n        delete cublas_wrapper_mutex;\n        delete allocator;\n    }\n\n    TensorMap* createInputTensors(\n        int* topk, size_t topk_size, float* topp, size_t topp_size, float* temperature, float* repetition_penalty)\n    {\n        // construct common input tensors\n        TensorMap* input_tensors = new TensorMap();\n        if (topk != nullptr) {\n            input_tensors->insert({\"runtime_top_k\", {MEMORY_CPU, TYPE_INT32, {topk_size}, topk}});\n        }\n        if (topp != nullptr) {\n            input_tensors->insert({\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {topp_size}, topp}});\n        }\n        if (temperature != nullptr) {\n            input_tensors->insert({\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, temperature}});\n        }\n        if (repetition_penalty != nullptr) {\n            input_tensors->insert({\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, repetition_penalty}});\n        }\n        input_tensors->insert(\n            {\"logits\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size, beam_width, vocab_size}, d_logits}});\n        input_tensors->insert({\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}});\n        input_tensors->insert({\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}});\n        input_tensors->insert(\n            {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, d_input_lengths}});\n        input_tensors->insert({\"end_id\", Tensor{MEMORY_CPU, TYPE_INT32, {batchxbeam}, &d_end_ids}});\n        input_tensors->insert({\"random_seed\", Tensor{MEMORY_CPU, TYPE_UINT64, {1}, &seed}});\n        return input_tensors;\n    }\n\n    TensorMap* createOutputTensors()\n    {\n        // construct common output tensors\n        TensorMap* output_tensors = new TensorMap();\n        output_tensors->insert(\n            {\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, d_output_ids}});\n        output_tensors->insert({\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}});\n        output_tensors->insert(\n            {\"cum_log_probs\", Tensor{MEMORY_GPU, TYPE_FP32, {batch_size * beam_width}, d_cum_log_probs}});\n        output_tensors->insert(\n            {\"output_log_probs\",\n             Tensor{MEMORY_GPU, TYPE_FP32, {max_seq_len, batch_size, beam_width}, d_output_log_probs}});\n        output_tensors->insert({\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}});\n        return output_tensors;\n    }\n\n    void batchH2Dcpy(T* dst, T* src, size_t m, size_t n)\n    {\n        for (size_t i = 0; i < m; ++i) {\n            cudaH2Dcpy(dst + i * n, src, n);\n        }\n    }\n\n    bool checkResult(std::string name, int* d_output_ids, std::vector<std::set<int>>& expected_ids)\n    {\n        assert(expected_ids.size() == max_seq_len * batchxbeam);\n        int* h_output_ids = new int[max_seq_len * batchxbeam];\n        cudaD2Hcpy(h_output_ids, d_output_ids, max_seq_len * batchxbeam);\n        int failures = 0;\n        for (size_t i = 0; i < max_seq_len * batchxbeam; ++i) {\n            size_t        s     = i / batchxbeam;\n            size_t        b     = i % batchxbeam;\n            std::set<int> expts = expected_ids.at(i);\n            if (expts.count(h_output_ids[i]) == 0) {\n                if (failures < 10) {\n                    std::stringstream ss;\n                    ss << \" - Fail \" << name << \" (step=\" << s << \", batch=\" << b << \") \"\n                       << \"actual=\" << h_output_ids[i] << \", expected\";\n                    for (auto& expt : expts) {\n                        ss << \" \" << expt;\n                    }\n                    FT_LOG_DEBUG(\"%s\", ss.str().c_str());\n                }\n                ++failures;\n            }\n        }\n        delete[] h_output_ids;\n        FT_LOG_DEBUG(\"check...%6s : %s (failures: %d / %d)\",\n                     failures == 0 ? \"....OK\" : \"FAILED\",\n                     name.c_str(),\n                     failures,\n                     max_seq_len * batchxbeam);\n        return failures == 0;\n    }\n\n    bool testSampling(std::string                name,\n                      std::vector<std::set<int>> expected_output_ids,\n                      int*                       top_ks,\n                      size_t                     top_k_size,\n                      float*                     top_ps,\n                      size_t                     top_p_size,\n                      float*                     temperature,\n                      float*                     repetition_penalty)\n    {\n        FT_LOG_INFO(\"Test %s\", name.c_str());\n        std::string tag    = fmtstr(\"Test %s T=%s\", name.c_str(), std::is_same<T, float>::value ? \"fp32\" : \"fp16\");\n        bool        passed = true;\n        for (unsigned long long seed = 0; seed < max_seed; ++seed) {\n            this->setup(seed);\n            size_t     step = max_input_len;\n            uint       ite  = 0;\n            TensorMap* input_tensors =\n                createInputTensors(top_ks, top_k_size, top_ps, top_p_size, temperature, repetition_penalty);\n            input_tensors->insert({\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}});\n            input_tensors->insert({\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}});\n            input_tensors->insert({\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &batch_size}});\n            TensorMap* output_tensors = createOutputTensors();\n\n            dynamic_decode_layer->setup(batch_size, beam_width, input_tensors);\n            for (step = max_input_len; step < max_output_len; ++step) {\n                // Reset by the test value since the sampling layer internally update the logit buffer.\n                batchH2Dcpy(input_tensors->at(\"logits\").getPtr<T>(),\n                            test_input_logits + step * vocab_size,\n                            batchxbeam,\n                            vocab_size);\n                dynamic_decode_layer->forward(output_tensors, input_tensors);\n            }\n            bool is_ok = checkResult(tag + fmtstr(\" seed=%lld\", seed), d_output_ids, expected_output_ids);\n            passed &= is_ok;\n#ifndef NDEBUG\n            if (!is_ok) {\n                FT_LOG_ERROR(\"actual output ids\");\n                printMatrix(d_output_ids, max_seq_len, batch_size, batch_size, true);\n            }\n#endif\n            delete output_tensors;\n            delete input_tensors;\n            this->teardown();\n        }\n        FT_LOG_INFO(\"check...%6s : %s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n        return passed;\n    }\n\n    bool testSamplingWithLocalBatch(std::string                name,\n                                    std::vector<std::set<int>> expected_output_ids,\n                                    int*                       top_ks,\n                                    size_t                     top_k_size,\n                                    float*                     top_ps,\n                                    size_t                     top_p_size,\n                                    float*                     temperature,\n                                    float*                     repetition_penalty)\n    {\n        FT_LOG_INFO(\"Test %s\", name.c_str());\n        std::string tag    = fmtstr(\"Test %s T=%s\", name.c_str(), std::is_same<T, float>::value ? \"fp32\" : \"fp16\");\n        bool        passed = true;\n        size_t      local_batch_size = 2;\n        uint        ite              = 1;\n        for (unsigned long long seed = 0; seed < max_seed; ++seed) {\n            this->setup(seed);\n            size_t     step = max_input_len;\n            TensorMap* input_tensors =\n                createInputTensors(top_ks, top_k_size, top_ps, top_p_size, temperature, repetition_penalty);\n            input_tensors->insert({\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}});\n            input_tensors->insert({\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}});\n            input_tensors->insert({\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &local_batch_size}});\n            TensorMap* output_tensors = createOutputTensors();\n\n            dynamic_decode_layer->setup(batch_size, beam_width, input_tensors);\n            for (step = max_input_len; step < max_output_len; ++step) {\n                // Reset by the test value since the sampling layer internally update the logit buffer.\n                batchH2Dcpy(input_tensors->at(\"logits\").getPtr<T>(),\n                            test_input_logits + step * vocab_size,\n                            batchxbeam,\n                            vocab_size);\n                dynamic_decode_layer->forward(output_tensors, input_tensors);\n            }\n            bool is_ok = checkResult(tag + fmtstr(\" seed=%lld\", seed), d_output_ids, expected_output_ids);\n            passed &= is_ok;\n#ifndef NDEBUG\n            if (!is_ok) {\n                FT_LOG_ERROR(\"actual output ids\");\n                printMatrix(d_output_ids, max_seq_len, batch_size, batch_size, true);\n            }\n#endif\n            delete output_tensors;\n            delete input_tensors;\n            this->teardown();\n        }\n        FT_LOG_INFO(\"check...%6s : %s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n        return passed;\n    }\n\npublic:\n    void testTopK()\n    {\n        int                        top_k = 2;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            //  0       1       2       3       4       5\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},\n            {0, 1},  // step 0\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},\n            {4, 5},  // step 1\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(\"TopK\", expected_output_ids, &top_k, 1, nullptr, 0, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopK()\n    {\n        int*                       top_ks = new int[batch_size]{2, 1, 1, 2, 1, 1};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            //  0    1    2       3    4    5\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4},  // step 1\n            {2, 3},\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2}  // step 2\n        };\n        bool passed =\n            this->testSampling(\"BatchTopK\", expected_output_ids, top_ks, batch_size, nullptr, 0, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopP()\n    {\n        float                      top_p = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(\"TopP\", expected_output_ids, nullptr, 0, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopP()\n    {\n        float*                     top_ps = new float[batch_size]{0.3f, 0.5f, 0.5f, 0.3f, 0.5f, 0.5f};\n        std::vector<std::set<int>> expected_output_ids{\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},  // step 0\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},  // step 1\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(\"BatchTopP\", expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopKTopP()\n    {\n        int                        top_k = 2;\n        float                      top_p = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(\"TopP\", expected_output_ids, &top_k, 1, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(true);\n    }\n\n    void testBatchTopKTopP()\n    {\n        std::string                name   = \"BatchTopKTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 1, 2, 2, 1};\n        float                      top_p  = 0.3;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testTopKBatchTopP()\n    {\n        std::string                name   = \"TopKBatchTopP\";\n        int                        top_k  = 2;\n        float*                     top_ps = new float[batch_size]{0.5, 0.3, 0.5, 0.5, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testBatchTopKBatchTopP()\n    {\n        std::string                name   = \"BatchTopKBatchTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 0, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4, 5},\n            {4},\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2, 3},\n            {2},\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopK()\n    {\n        std::string                name  = \"InvalidArgsZeroTopK\";\n        int                        top_k = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, nullptr, 0, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopP()\n    {\n        std::string                name  = \"InvalidArgsZeroTopP\";\n        float                      top_p = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, nullptr, 0, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopKTopP()\n    {\n        std::string                name  = \"InvalidArgsZeroTopKTopP\";\n        int                        top_k = 0;\n        float                      top_p = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, &top_p, 1, nullptr, nullptr);\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroBatchTopKTopP()\n    {\n        std::string                name   = \"InvalidArgsZeroBatchTopKTopP\";\n        int*                       top_ks = new int[batch_size]{0, 0, 0, 0, 0, 0};\n        float                      top_p  = 0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsZeroTopKBatchTopP()\n    {\n        std::string                name   = \"InvalidArgsZeroTopKBatchTopP\";\n        int                        top_k  = 0;\n        float*                     top_ps = new float[batch_size]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},\n            {0},  // step 0\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},\n            {4},  // step 1\n            {2},\n            {2},\n            {2},\n            {2},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKContainZero\";\n        int*                       top_ks = new int[batch_size]{2, 1, 0, 0, 2, 1};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0},\n            {0},\n            {0},\n            {0, 1},\n            {0},  // step 0\n            {4, 5},\n            {4},\n            {4},\n            {4},\n            {4, 5},\n            {4},  // step 1\n            {2, 3},\n            {2},\n            {2},\n            {2},\n            {2, 3},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, nullptr, 0, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopPContainZero\";\n        float*                     top_ps = new float[batch_size]{0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 0.3f};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0, 1},\n            {0},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {4, 5},\n            {4, 5},\n            {4},\n            {4, 5},\n            {4},\n            {4},  // step 1\n            {2, 3},\n            {2, 3},\n            {2},\n            {2, 3},\n            {2},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKTopPContainZero\";\n        int*                       top_ks = new int[batch_size]{2, 2, 1, 0, 2, 0};\n        float                      top_p  = 0.0;\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0, 1},\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},\n            {0},  // step 0\n            {4, 5},\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},\n            {4},  // step 1\n            {2, 3},\n            {2, 3},\n            {2},\n            {2},\n            {2, 3},\n            {2}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, top_ks, batch_size, &top_p, 1, nullptr, nullptr);\n        delete[] top_ks;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsTopKBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsTopKBatchTopPContainZero\";\n        int                        top_k  = 0;\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},\n            {0, 1},  // step 0\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4},\n            {4, 5},  // step 1\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed = this->testSampling(name, expected_output_ids, &top_k, 1, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testInvalidArgsBatchTopKBatchTopPContainZero()\n    {\n        std::string                name   = \"InvalidArgsBatchTopKBatchTopPContainZero\";\n        int*                       top_ks = new int[batch_size]{0, 2, 1, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.9, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0, 1},  // step 0\n            {4},\n            {4},\n            {4},\n            {4, 5},\n            {4},\n            {4, 5},  // step 1\n            {2},\n            {2},\n            {2},\n            {2, 3},\n            {2},\n            {2, 3}  // step 2\n        };\n        bool passed =\n            this->testSampling(name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testLocalBatchBatchTopP()\n    {\n        std::string                name   = \"LocalBatch_BatchTopP\";\n        float*                     top_ps = new float[batch_size]{0.3f, 0.5f, 0.5f, 0.3f, 0.5f, 0.5f};\n        std::vector<std::set<int>> expected_output_ids{\n            {0},\n            {0},\n            {0, 1},\n            {0},\n            {0},\n            {0},  // step 0\n            {0},\n            {0},\n            {4, 5},\n            {4},\n            {0},\n            {0},  // step 1\n            {0},\n            {0},\n            {2, 3},\n            {2},\n            {0},\n            {0}  // step 2\n        };\n        bool passed = this->testSamplingWithLocalBatch(\n            name, expected_output_ids, nullptr, 0, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testLocalBatchBatchTopKBatchTopP()\n    {\n        std::string                name   = \"LocalBatch_BatchTopKBatchTopP\";\n        int*                       top_ks = new int[batch_size]{2, 2, 0, 2, 2, 0};\n        float*                     top_ps = new float[batch_size]{0.0, 0.3, 0.5, 0.0, 0.3, 0.5};\n        std::vector<std::set<int>> expected_output_ids{\n            // batch\n            {0},\n            {0},\n            {0, 1},\n            {0, 1},\n            {0},\n            {0},  // step 0\n            {0},\n            {0},\n            {4, 5},\n            {4, 5},\n            {0},\n            {0},  // step 1\n            {0},\n            {0},\n            {2, 3},\n            {2, 3},\n            {0},\n            {0}  // step 2\n        };\n        bool passed = this->testSamplingWithLocalBatch(\n            name, expected_output_ids, top_ks, batch_size, top_ps, batch_size, nullptr, nullptr);\n        delete[] top_ks;\n        delete[] top_ps;\n        EXPECT_TRUE(passed);\n    }\n\n    void testAll()\n    {\n        this->testTopK();\n        this->testTopP();\n        this->testTopKTopP();\n        this->testBatchTopK();\n        this->testBatchTopP();\n        this->testBatchTopKTopP();\n        this->testTopKBatchTopP();\n        this->testBatchTopKBatchTopP();\n        this->testInvalidArgsZeroTopK();\n        this->testInvalidArgsZeroTopP();\n        this->testInvalidArgsZeroBatchTopKTopP();\n        this->testInvalidArgsZeroTopKBatchTopP();\n        this->testInvalidArgsZeroTopKTopP();\n        this->testInvalidArgsBatchTopKContainZero();\n        this->testInvalidArgsBatchTopPContainZero();\n        this->testInvalidArgsBatchTopKTopPContainZero();\n        this->testInvalidArgsTopKBatchTopPContainZero();\n        this->testInvalidArgsBatchTopKBatchTopPContainZero();\n        this->testLocalBatchBatchTopP();\n        this->testLocalBatchBatchTopKBatchTopP();\n    }\n};\n\n__global__ void generateRandomNumber(unsigned int* vals, hiprandState_t* states, const int batch_size)\n{\n    int idx = threadIdx.x;\n    if (idx < batch_size) {\n        vals[idx] = hiprand(states + idx);\n    }\n}\n\ntemplate<typename T>\nstatic inline bool isEqualInPeriod(T* vals, size_t size, size_t period_size)\n{\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            if (vals[i] != vals[i + j]) {\n                FT_LOG_INFO(\" **** *** ** * [%d] %d <> [%d] %d\", i, vals[i], i + j, vals[i + j]);\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\ntemplate<typename T>\nstatic inline bool isEqualInPeriod(T* vals, size_t size, size_t period_size, size_t except)\n{\n    // The same seed produces the same random number.\n    for (size_t i = 0; i + period_size - 1 < size; i += period_size) {\n        for (size_t j = 1; j < period_size; ++j) {\n            if (j != except && vals[i] != vals[i + j]) {\n                FT_LOG_INFO(\" **** *** ** * [%d] %d <> [%d] %d\", i, vals[i], i + j, vals[i + j]);\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nvoid testCuandBatchInitialize(const size_t batch_size)\n{\n    hipStream_t stream;\n    hipStreamCreate(&stream);\n\n    hiprandState_t* curand_states;\n    check_cuda_error(hipMalloc(&curand_states, sizeof(hiprandState_t) * batch_size));\n    unsigned long long* h_random_seeds = new unsigned long long[batch_size];\n    const size_t        period_size    = 3;\n    for (size_t i = 0; i < batch_size; ++i) {\n        h_random_seeds[i] = i / period_size;\n    }\n    unsigned long long* d_random_seeds;\n    check_cuda_error(hipMalloc(&d_random_seeds, sizeof(unsigned long long) * batch_size));\n    check_cuda_error(\n        hipMemcpy(d_random_seeds, h_random_seeds, sizeof(unsigned long long) * batch_size, hipMemcpyHostToDevice));\n\n    // Initialize curand states.\n    invokeCurandBatchInitialize(curand_states, batch_size, d_random_seeds, stream);\n    sync_check_cuda_error();\n\n    // Generate random numbers using initialized curand states.\n    unsigned int* d_rand_vals;\n    unsigned int* h_rand_vals = new unsigned int[batch_size];\n    check_cuda_error(hipMalloc(&d_rand_vals, sizeof(unsigned int) * batch_size));\n    generateRandomNumber<<<1, batch_size, 0, stream>>>(d_rand_vals, curand_states, batch_size);\n    check_cuda_error(\n        hipMemcpyAsync(h_rand_vals, d_rand_vals, sizeof(unsigned int) * batch_size, hipMemcpyDeviceToHost, stream));\n    check_cuda_error(hipStreamSynchronize(stream));\n\n    // The same seed produces the same random number.\n    bool passed = isEqualInPeriod(h_rand_vals, batch_size, period_size);\n    FT_LOG_INFO(\"CuandBatchInitTest check....... : %s\", passed ? \"OK\" : \"FAILED\");\n    EXPECT_TRUE(passed);\n\n    delete h_rand_vals;\n    delete h_random_seeds;\n\n    check_cuda_error(hipFree(d_rand_vals));\n    check_cuda_error(hipFree(d_random_seeds));\n    check_cuda_error(hipFree(curand_states));\n    check_cuda_error(hipStreamDestroy(stream));\n}\n\ntemplate<typename T, bool SINGLE_RANDOM_SEED, bool HAS_DIFF_ARGS, bool USE_LOCAL_BATCH>\nvoid testSamplingLayerCurandInit(TestCase tc)\n{\n    FT_LOG_DEBUG(\"testSamplingLayerCurandInit %s\", tc.toString().c_str());\n    const DataType data_type = getTensorType<T>();\n\n    const size_t beam_width = 1;\n    const uint   top_k      = tc.top_k;\n    const float  top_p      = tc.top_p;\n    // use default values having no effect.\n    const float temperature        = 1.0f;\n    const float len_penalty        = 0.0f;\n    const float repetition_penalty = 1.0f;\n    const int   end_id             = 3;\n\n    const size_t batch_size       = tc.batch_size;\n    const size_t batchxbeam       = batch_size * beam_width;\n    const size_t local_batch_size = USE_LOCAL_BATCH ? 2 : batch_size;\n    assert(batch_size % local_batch_size == 0);\n    const size_t vocab_size     = tc.vocab_size;\n    const size_t max_input_len  = 0;  // has no effect.\n    const size_t max_output_len = tc.output_len;\n    const size_t max_seq_len    = max_input_len + max_output_len;\n\n    struct hipDeviceProp_t prop;\n    check_cuda_error(hipGetDeviceProperties(&prop, 0));\n\n    hipStream_t     stream;\n    hipblasHandle_t   cublas_handle;\n    hipblasLtHandle_t cublaslt_handle;\n    check_cuda_error(hipStreamCreate(&stream));\n    check_cuda_error(hipblasCreate(&cublas_handle));\n    check_cuda_error(hipblasLtCreate(&cublaslt_handle));\n    check_cuda_error(hipblasSetStream(cublas_handle, stream));\n    cublasAlgoMap                   cublas_algo_map(GEMM_CONFIG);\n    std::mutex*                     cublas_wrapper_mutex = new std::mutex();\n    Allocator<AllocatorType::CUDA>* allocator            = new Allocator<AllocatorType::CUDA>(getDevice());\n    allocator->setStream(stream);\n    cublasMMWrapper* cublas_wrapper =\n        new cublasMMWrapper(cublas_handle, cublaslt_handle, stream, &cublas_algo_map, cublas_wrapper_mutex, allocator);\n    DynamicDecodeLayer<T>* dynamic_decode_layer = new DynamicDecodeLayer<T>(vocab_size,\n                                                                            vocab_size,\n                                                                            end_id,\n                                                                            stream,\n                                                                            cublas_wrapper,\n                                                                            allocator,\n                                                                            false,   // is_free_buffer_after_forward\n                                                                            &prop);  // cuda_device_prop\n\n    T*   h_logits     = reinterpret_cast<T*>(malloc(sizeof(T) * batchxbeam * vocab_size));\n    int* h_output_ids = reinterpret_cast<int*>(malloc(sizeof(int) * batchxbeam));\n\n    T*   d_logits_buf    = reinterpret_cast<T*>(allocator->malloc(sizeof(T) * batchxbeam * vocab_size));\n    int* d_input_lengths = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batchxbeam));\n    int* d_output_ids    = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * max_seq_len * batchxbeam));\n\n    // Init by zero.\n    hipMemset(d_input_lengths, 0, sizeof(int) * batchxbeam);\n    hipMemset(d_output_ids, 0, sizeof(int) * max_seq_len * batchxbeam);\n\n    // Prepare decoding arguments\n    const size_t        random_seed_size = SINGLE_RANDOM_SEED ? 1 : batch_size;\n    const size_t        period_size      = 3;\n    unsigned long long* random_seed      = new unsigned long long[random_seed_size];\n    for (size_t i = 0; i < random_seed_size; ++i) {\n        random_seed[i] = i / period_size;\n    }\n    const bool   has_diff_runtime_args = HAS_DIFF_ARGS;\n    const size_t runtime_args_size     = has_diff_runtime_args ? batch_size : 1;\n    uint*        runtime_top_k         = new uint[runtime_args_size];\n    float*       runtime_top_p         = new float[runtime_args_size];\n    const size_t except_idx            = 1;\n    for (size_t i = 0; i < runtime_args_size; ++i) {\n        runtime_top_k[i] = (top_k > 1) && (i % period_size == except_idx) ? 1 : top_k;\n        runtime_top_p[i] = (i % period_size == except_idx) ? top_p * 0.1f : top_p;\n    }\n    int* d_end_id_buf = reinterpret_cast<int*>(allocator->malloc(sizeof(int) * batch_size));\n    deviceFill(d_end_id_buf, batch_size, end_id);\n\n#ifndef NDEBUG\n    FT_LOG_DEBUG(\"Random Seeds\");\n    printMatrixWithLimit(random_seed, 1, random_seed_size, random_seed_size, false);\n#endif\n\n    bool passed = true;\n\n    TensorMap runtime_args;\n    runtime_args.insert({\"has_diff_runtime_args\", Tensor(MEMORY_CPU, TYPE_BOOL, {1}, &has_diff_runtime_args)});\n    runtime_args.insert({\"random_seed\", Tensor(MEMORY_CPU, TYPE_UINT64, {random_seed_size}, random_seed)});\n    runtime_args.insert({\"runtime_top_k\", Tensor(MEMORY_CPU, TYPE_INT32, {runtime_args_size}, runtime_top_k)});\n    runtime_args.insert({\"runtime_top_p\", Tensor(MEMORY_CPU, TYPE_FP32, {runtime_args_size}, runtime_top_p)});\n    runtime_args.insert({\"temperature\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &temperature)});\n    runtime_args.insert({\"len_penalty\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &len_penalty)});\n    runtime_args.insert({\"repetition_penalty\", Tensor(MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty)});\n    dynamic_decode_layer->setup(batch_size, beam_width, &runtime_args);\n\n    for (size_t step = max_input_len; step < max_output_len; ++step) {\n        const size_t iteration_num = batch_size / local_batch_size;\n\n        initRandom(h_logits, beam_width * vocab_size, -10.0f / vocab_size, -1.0f);\n        tile(h_logits, batch_size, beam_width * vocab_size);\n        cudaH2Dcpy(d_logits_buf, h_logits, batchxbeam * vocab_size);\n\n#ifndef NDEBUG\n        FT_LOG_DEBUG(\"logit values\");\n        printMatrixWithLimit(h_logits, batchxbeam, vocab_size, vocab_size, false);\n#endif\n        for (uint ite = 0; ite < iteration_num; ++ite) {\n            TensorMap dynamic_decode_input_tensors(\n                {{\"logits\", Tensor{MEMORY_GPU, data_type, {batch_size, beam_width, vocab_size}, d_logits_buf}},\n                 {\"embedding_bias\", Tensor{MEMORY_GPU, data_type, {vocab_size}, nullptr}},\n                 {\"step\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &step}},\n                 {\"max_input_length\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &max_input_len}},\n                 {\"input_lengths\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width}, d_input_lengths}},\n                 {\"ite\", Tensor{MEMORY_CPU, TYPE_UINT32, {1}, &ite}},\n                 {\"has_diff_runtime_args\", Tensor{MEMORY_CPU, TYPE_BOOL, {1}, &has_diff_runtime_args}},\n                 {\"local_batch_size\", Tensor{MEMORY_CPU, TYPE_INT32, {1}, &local_batch_size}},\n                 {\"end_id\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size}, d_end_id_buf}},\n                 {\"random_seed\", {MEMORY_CPU, TYPE_UINT64, {random_seed_size}, random_seed}},\n                 {\"runtime_top_k\", {MEMORY_CPU, TYPE_UINT32, {runtime_args_size}, runtime_top_k}},\n                 {\"runtime_top_p\", {MEMORY_CPU, TYPE_FP32, {runtime_args_size}, runtime_top_p}},\n                 {\"temperature\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &temperature}},\n                 {\"len_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &len_penalty}},\n                 {\"repetition_penalty\", Tensor{MEMORY_CPU, TYPE_FP32, {1}, &repetition_penalty}}});\n\n            // common outputs\n            TensorMap dynamic_decode_output_tensors(\n                {{\"output_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, d_output_ids}},\n                 {\"finished\", Tensor{MEMORY_GPU, TYPE_BOOL, {batch_size * beam_width}, nullptr}},\n                 {\"parent_ids\", Tensor{MEMORY_GPU, TYPE_INT32, {max_seq_len, batch_size, beam_width}, nullptr}},\n                 {\"sequence_length\", Tensor{MEMORY_GPU, TYPE_INT32, {batch_size * beam_width}, nullptr}},\n                 // necessary for beam search.\n                 {\"tgt_cache_indirection\",\n                  Tensor{MEMORY_GPU, TYPE_INT32, {batch_size, beam_width, max_output_len}, nullptr}}});\n\n            dynamic_decode_layer->forward(&dynamic_decode_output_tensors, &dynamic_decode_input_tensors);\n            sync_check_cuda_error();\n#ifndef NDEBUG\n            FT_LOG_DEBUG(\"Step %2d generated ids\", step);\n            printMatrix(d_output_ids, max_seq_len, batchxbeam, batchxbeam, true);\n            FT_LOG_DEBUG(\"\");\n#endif\n            // check results.\n            cudaD2Hcpy(h_output_ids,\n                       (int*)dynamic_decode_output_tensors.at(\"output_ids\").getPtrWithOffset(step * batchxbeam),\n                       batchxbeam);\n        }\n        bool is_ok = isEqualInPeriod(h_output_ids, batchxbeam, period_size, except_idx);\n        passed &= is_ok;\n    }\n    std::string tag = fmtstr(\"%s (seed=%-6s has_diff_args=%-5s local_batch=%-5s T=%s)\",\n                             tc.toString().c_str(),\n                             SINGLE_RANDOM_SEED ? \"single\" : \"multi\",\n                             HAS_DIFF_ARGS ? \"true\" : \"false\",\n                             USE_LOCAL_BATCH ? \"true\" : \"false\",\n                             (std::is_same<T, float>::value ? \" fp32\" : \" fp16\"));\n    FT_LOG_INFO(\"check...%s SamplingLayerCurandInitTest %-30s\", passed ? \"....OK\" : \"FAILED\", tag.c_str());\n    EXPECT_TRUE(passed);\n\n    free(h_logits);\n    free(h_output_ids);\n\n    delete dynamic_decode_layer;\n    delete runtime_top_k;\n    delete runtime_top_p;\n    delete random_seed;\n    delete cublas_wrapper;\n    delete allocator;\n    check_cuda_error(hipStreamDestroy(stream));\n    check_cuda_error(hipblasDestroy(cublas_handle));\n    check_cuda_error(hipblasLtDestroy(cublaslt_handle));\n}\n\nint main()\n{\n    std::vector<TestCase> test_cases{\n        // TC: name / batch / vocab / beam / k / p / outlen\n        TestCase{\"topk\", 6, 4, 1, 1, 0.0f, 4},\n        TestCase{\"topk\", 6, 4, 1, 4, 0.0f, 4},\n        TestCase{\"topk\", 6, 51200, 1, 31, 0.0f, 16},\n        TestCase{\"topk\", 32, 51200, 1, 63, 0.0f, 16},\n        TestCase{\"topk\", 32, 51200, 1, 64, 0.0f, 16},\n        TestCase{\"topp\", 6, 4, 1, 0, 0.2f, 4},\n        TestCase{\"topp\", 6, 4, 1, 0, 0.8f, 4},\n        TestCase{\"topp\", 6, 4, 1, 0, 1.0f, 4},\n        TestCase{\"topp\", 6, 51200, 1, 0, 0.8f, 16},\n        TestCase{\"topp\", 32, 51200, 1, 0, 0.8f, 16},\n        TestCase{\"topp\", 32, 51200, 1, 0, 1.0f, 16},\n        TestCase{\"topk_topp\", 6, 4, 1, 1, 0.8f, 16},\n        TestCase{\"topk_topp\", 6, 4, 1, 4, 1.0f, 16},\n        TestCase{\"topk_topp\", 6, 51200, 1, 31, 0.8f, 16},\n        TestCase{\"topk_topp\", 32, 51200, 1, 63, 0.8f, 16},\n        TestCase{\"topk_topp\", 32, 51200, 1, 64, 1.0f, 16},\n    };\n\n    for (auto& tc : test_cases) {\n        testCumLogProbComputation<float>(tc);\n        testCumLogProbComputation<half>(tc);\n    }\n    FT_LOG_INFO(\"testCumLogProbComputation done\");\n\n    SamplingDecodeTest<float> sampling_decode_test;\n    sampling_decode_test.testAll();\n\n    testCuandBatchInitialize(127);\n    FT_LOG_INFO(\"testCuandBatchInitialize done\");\n\n#define LAUNCH_VARIANTS(T, tc, local_batch)                                                                            \\\n    testSamplingLayerCurandInit<T, true, false, local_batch>(tc);                                                      \\\n    testSamplingLayerCurandInit<T, true, true, local_batch>(tc);                                                       \\\n    testSamplingLayerCurandInit<T, false, false, local_batch>(tc);                                                     \\\n    testSamplingLayerCurandInit<T, false, true, local_batch>(tc);\n    for (auto& tc : test_cases) {\n        LAUNCH_VARIANTS(float, tc, false);  // without local batch\n        LAUNCH_VARIANTS(half, tc, false);\n        LAUNCH_VARIANTS(float, tc, true);  // with local batch\n        LAUNCH_VARIANTS(half, tc, true);\n    }\n#undef LAUNCH_VARIANTS\n    FT_LOG_INFO(\"testSamplingLayerCurandInit done\");\n\n    return 0;\n}\n", "source": "/kaggle/working/FasterTransformer/tests/unittests/test_sampling.cu"}
{"input": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tests/unittests/gtest_utils.h\"\n#include \"src/fastertransformer/kernels/gen_relative_pos_bias.h\"\n#include \"src/fastertransformer/kernels/gpt_kernels.h\"\n#include \"src/fastertransformer/kernels/unfused_attention_kernels.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n#include \"src/fastertransformer/utils/nccl_utils.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n\n#include <curand.h>\n#include <sstream>\n#include <stdexcept>\n#include <vector>\n\nusing namespace fastertransformer;\n\nnamespace {\n\nstruct AttentionKernelTestParam {\n    size_t batch_size    = 4;\n    size_t q_length      = 32;\n    size_t k_length      = 32;\n    size_t head_num      = 4;\n    size_t size_per_head = 32;\n\n    bool   use_fp32_qk_buf      = false;\n    size_t rotary_embedding_dim = 0;\n    bool   neox_rotary_style    = false;\n\n    float  q_scaling = 1.0f;\n};\n\nnamespace utils {\n\n#define CHECK_CURAND(cmd) do {                                                      \\\n    curandStatus_t err = cmd;                                                       \\\n    if (err != CURAND_STATUS_SUCCESS) {                                             \\\n        throw std::runtime_error(                                                   \\\n            fmtstr(\"[FT][ERROR] curand runtime error: %d\", err)); \\\n    }} while(0)                                                                     \\\n\n__global__ void convert_and_copy(half* dst, const float* src, const size_t size)\n{\n    for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < size; idx += blockDim.x * gridDim.x) {\n        dst[idx] = __float2half(src[idx]);\n    }\n}\n\n#ifdef ENABLE_BF16\n__global__ void convert_and_copy(__nv_bfloat16* dst, const float* src, const size_t size)\n{\n    for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < size; idx += blockDim.x * gridDim.x) {\n        dst[idx] = __float2bfloat16(src[idx]);\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid normal(curandGenerator_t curng, T* buf, size_t size, float mean, float stddev)\n{\n    float* tmp_buf = nullptr;\n    deviceMalloc(&tmp_buf, size);\n\n    // Generate random values in float data type.\n    CHECK_CURAND(curandGenerateNormal(curng, tmp_buf, size / 2, mean, stddev));\n    sync_check_cuda_error();\n\n    // Convert and copy to the output buffer if it is not of type float.\n    dim3 block(512);\n    dim3 grid(min(static_cast<int>((size + block.x - 1) / block.x), 256));\n    convert_and_copy<<<grid, block>>>(buf, tmp_buf, size);\n    cudaDeviceSynchronize();\n\n    deviceFree(tmp_buf);\n    sync_check_cuda_error();\n}\n\ntemplate<>\nvoid normal(curandGenerator_t curng, float* buf, size_t size, float mean, float stddev)\n{\n    // Generate random values in float data type.\n    CHECK_CURAND(curandGenerateNormal(curng, buf, size / 2, mean, stddev));\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid normal(curandGenerator_t curng, Tensor& tensor, float mean = 0.0f, float stddev = 1.0f)\n{\n    if (tensor.size() > 0) {\n        FT_CHECK(tensor.type == getTensorType<T>());\n        normal(curng, tensor.getPtr<T>(), tensor.size(), mean, stddev);\n    }\n}\n\n__host__ uint32_t pow2_rounddown(uint32_t x)\n{\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    x >>= 1;\n    return x + 1;\n}\n\n}  // namespace utils\n\n////////////////////////////\n// Reference computation.\n////////////////////////////\n\ntemplate<typename T>\ninline T safe_add_bias(const T v, const T* bias, const size_t bias_idx)\n{\n    return bias == nullptr ? v : ::math::add(v, bias[bias_idx]);\n}\n\ntemplate<typename T>\nvoid computeQkSoftmax(T*           attn_score,\n                      const T*     qk,\n                      const T*     attn_mask,\n                      const T*     pos_bias,\n                      const size_t batch_size,\n                      const size_t num_heads,\n                      const size_t q_length,\n                      const size_t k_length,\n                      const T      qk_scale)\n{\n    // attn_score [batch_size, num_heads, q_length, k_length]\n    // qk         [batch_size, num_heads, q_length, k_length]\n    // attn_mask  [batch_size, 1, q_length, k_length]\n    // pos_bias   [1, num_heads, q_length, k_length]\n\n    // batch, head index.\n    for (size_t bhi = 0; bhi < batch_size * num_heads; ++bhi) {\n        size_t bi = bhi / num_heads;  // batch index.\n        size_t hi = bhi % num_heads;  // head index.\n        // The attention mask of the current batch.\n        const T* mask = &attn_mask[bi * q_length * k_length];\n        // The position bias of the current head.\n        const T* head_pos_bias = pos_bias != nullptr ? &pos_bias[hi * q_length * k_length] : nullptr;\n\n        for (size_t qi = 0; qi < q_length; ++qi) {\n            float maxval = -FLT_MAX;\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    if (val > maxval) {\n                        maxval = val;\n                    }\n                }\n            }\n            float sum = 0.0f;\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    sum += expf(val - maxval);\n                }\n            }\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    attn_score[qk_idx] = static_cast<T>(expf(val - maxval) / (sum + EPSILON));\n                }\n                else {\n                    attn_score[qk_idx] = T(0.0f);\n                }\n            }\n        }\n\n        // Move the data pointers to the next.\n        attn_score += q_length * k_length;\n        qk         += q_length * k_length;\n    }\n}\n\ntemplate<typename T>\nclass AttentionKernelTest : public FtTestBase {\n\nprivate:\n    using FtTestBase::stream;\n    using FtTestBase::allocator;\n\n    unsigned long long seed = 31;\n    curandGenerator_t  curng;\n\n    Tensor randomAttentionMask(const std::vector<size_t> shape)\n    {\n        // shape (batch_size, 1, max_input_length, max_input_length + max_prompt_length)\n\n        // Create a attention mask tensor and buffer.\n        Tensor attn_mask = createTensor(MEMORY_GPU, getTensorType<T>(), shape);\n\n        // Set the mask values.\n        size_t batch_size   = shape[0];\n        size_t max_q_length = shape[2];\n        size_t max_k_length = shape[3];\n        // TODO: Enable prompts.\n        size_t max_prompt_length = max_k_length - max_q_length;\n\n        Tensor h_seq_lengths    = createTensor(MEMORY_CPU, TYPE_INT32, {batch_size});\n        Tensor h_prompt_lengths = createTensor(MEMORY_CPU, TYPE_INT32, {batch_size});\n        initRandomInt(h_seq_lengths.getPtr<int>(), batch_size, max_q_length, max_q_length + 1);\n        initRandomInt(h_prompt_lengths.getPtr<int>(), batch_size, 0, max_prompt_length + 1);\n\n        Tensor d_seq_lengths    = createTensor(MEMORY_GPU, TYPE_INT32, {batch_size});\n        Tensor d_prompt_lengths = createTensor(MEMORY_GPU, TYPE_INT32, {batch_size});\n        copyTensor(d_seq_lengths, h_seq_lengths);\n        copyTensor(d_prompt_lengths, h_prompt_lengths);\n\n        // Used gpt_kernels function to build attention mask.\n        invokeBuildDecoderAttentionMask(attn_mask.getPtr<T>(),\n                                        d_seq_lengths.getPtr<int>(),\n                                        d_prompt_lengths.getPtr<int>(),\n                                        batch_size,\n                                        max_q_length,\n                                        max_prompt_length,\n                                        stream);\n        sync_check_cuda_error();\n        return attn_mask;\n    };\n\npublic:\n    void SetUp() override\n    {\n        FtTestBase::SetUp();\n        CHECK_CURAND(curandCreateGenerator(&curng, CURAND_RNG_PSEUDO_DEFAULT));\n        CHECK_CURAND(curandSetPseudoRandomGeneratorSeed(curng, seed));\n    }\n\n    void TearDown() override\n    {\n        curandDestroyGenerator(curng);\n        FtTestBase::TearDown();\n    }\n\n    void runTestMaskedSoftmax(AttentionKernelTestParam param, bool is_benchmark = false) {\n        DataType dtype = getTensorType<T>();\n\n        std::vector<size_t> qk_shape {param.batch_size, param.head_num, param.q_length, param.k_length};\n\n        bool use_fp32_qk = param.use_fp32_qk_buf && dtype != TYPE_FP32;\n\n        Tensor qk        = createTensor(MEMORY_GPU, dtype, qk_shape);\n        Tensor qk_fp32   = use_fp32_qk ? createTensor(MEMORY_GPU, TYPE_FP32, qk_shape) : Tensor();\n        Tensor attn_mask = randomAttentionMask({param.batch_size, 1, param.q_length, param.k_length});\n        // Input random initialization\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            utils::normal<float>(curng, qk_fp32);\n        }\n        else {\n            utils::normal<T>(curng, qk);\n        }\n\n        // Clone to host for reference computation if needed.\n        Tensor h_qk        = is_benchmark ? Tensor() : toHost<T>(qk);\n        Tensor h_attn_mask = is_benchmark ? Tensor() : toHost<T>(attn_mask);\n        Tensor h_qk_fp32   = is_benchmark ? Tensor() : toHost<float>(qk_fp32);\n\n        T scale = static_cast<T>(1 / sqrtf(param.size_per_head * 1.0f));\n\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            MaskedSoftmaxParam<T, float> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk_fp32.getPtr<float>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n        else {\n            MaskedSoftmaxParam<T, T> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk.getPtr<T>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n\n        if (!is_benchmark) {\n            if (use_fp32_qk) {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk_fp32.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 (T*)nullptr,\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            else {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 (T*)nullptr,\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            bool passed = checkResult(\"MaskedSoftmax\", qk.getPtr<T>(), h_qk.getPtr<T>(), qk.size());\n            EXPECT_TRUE(passed);\n        }\n    }\n\n    void runTestAlibiMaskedSoftmax(AttentionKernelTestParam param, bool is_benchmark = false) {\n        DataType dtype = getTensorType<T>();\n\n        std::vector<size_t> qk_shape {param.batch_size, param.head_num, param.q_length, param.k_length};\n\n        bool use_fp32_qk = param.use_fp32_qk_buf && dtype != TYPE_FP32;\n\n        Tensor qk           = createTensor(MEMORY_GPU, dtype, qk_shape);\n        Tensor qk_fp32      = use_fp32_qk ? createTensor(MEMORY_GPU, TYPE_FP32, qk_shape) : Tensor();\n        Tensor attn_mask    = randomAttentionMask({param.batch_size, 1, param.q_length, param.k_length});\n        Tensor alibi_slopes = createTensor(MEMORY_GPU, dtype, {param.head_num});\n\n        // Input random initialization\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            utils::normal<float>(curng, qk_fp32);\n        }\n        else {\n            utils::normal<T>(curng, qk);\n        }\n        invokeBuildAlibiSlopes(alibi_slopes.getPtr<T>(), param.head_num, stream);\n        sync_check_cuda_error();\n\n        Tensor h_alibi_slopes = createTensor(MEMORY_CPU, dtype, {param.head_num});\n        Tensor h_alibi_bias   = is_benchmark ? Tensor() :\n            createTensor(MEMORY_CPU, dtype, {param.head_num, param.q_length, param.k_length});\n        // The nearest power of 2 equal to / smaller than num_heads followed by HF's implementation.\n        T* alibi_slope_ptr = h_alibi_slopes.getPtr<T>();\n        int num_heads_pow2 = utils::pow2_rounddown(param.head_num);\n        for (size_t h = 0; h < param.head_num; ++h) {\n            // The slope of linear bias of the attention head\n            if (h < num_heads_pow2) {\n                alibi_slope_ptr[h] = static_cast<T>(powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2) - 3.f)), h + 1));\n            } else {\n                alibi_slope_ptr[h] = static_cast<T>(\n                    powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2 << 1) - 3.f)), (h - num_heads_pow2) * 2 + 1));\n            }\n            if (h_alibi_bias.size() > 0) {\n                T* alibi_bias_ptr = h_alibi_bias.getPtr<T>();\n                for (size_t qi = 0; qi < param.q_length; ++qi) {\n                    for (size_t ki = 0; ki < param.k_length; ++ki) {\n                        size_t hqk_idx = (h * param.q_length + qi) * param.k_length + ki;\n                        alibi_bias_ptr[hqk_idx] = ::math::mul(alibi_slope_ptr[h], T(0.0f + ki - qi));\n                    }\n                }\n            }\n        }\n        EXPECT_TRUE(\n            checkResult(\"CheckAlibiSlopes\", alibi_slopes.getPtr<T>(), h_alibi_slopes.getPtr<T>(), param.head_num));\n\n        // Clone to host for reference computation if needed.\n        Tensor h_qk        = is_benchmark ? Tensor() : toHost<T>(qk);\n        Tensor h_attn_mask = is_benchmark ? Tensor() : toHost<T>(attn_mask);\n        Tensor h_qk_fp32   = is_benchmark ? Tensor() : toHost<float>(qk_fp32);\n\n        T scale = static_cast<T>(1 / sqrtf(param.size_per_head * 1.0f));\n\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            MaskedSoftmaxParam<T, float> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk_fp32.getPtr<float>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.linear_bias_slopes = alibi_slopes.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n        else {\n            MaskedSoftmaxParam<T, T> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk.getPtr<T>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.linear_bias_slopes = alibi_slopes.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n\n        if (!is_benchmark) {\n            if (use_fp32_qk) {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk_fp32.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 h_alibi_bias.getPtr<T>(),\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            else {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 h_alibi_bias.getPtr<T>(),\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            bool passed = checkResult(\"AlibiMaskedSoftmax\", qk.getPtr<T>(), h_qk.getPtr<T>(), qk.size());\n            EXPECT_TRUE(passed);\n        }\n    }\n};\n\nTYPED_TEST_SUITE(AttentionKernelTest, SupportTypes);\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_NoPrompt) {\n    this->runTestMaskedSoftmax({1, 12, 12, 1, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_NoPrompt2) {\n    // q_length is not multiple of 4.\n    this->runTestMaskedSoftmax({1, 11, 11, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_HasPrompt) {\n    this->runTestMaskedSoftmax({1, 12, 24, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_HasPrompt2) {\n    this->runTestMaskedSoftmax({1, 11, 24, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence1024) {\n    this->runTestMaskedSoftmax({1, 12, 1024, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence2048) {\n    this->runTestMaskedSoftmax({1, 12, 2048, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence3072) {\n    this->runTestMaskedSoftmax({1, 12, 3072, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence4096) {\n    this->runTestMaskedSoftmax({1, 12, 4096, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence1024) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 1024, 1024, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence2048) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 2048, 2048, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence4096) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 4096, 4096, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence1) {\n    this->runTestAlibiMaskedSoftmax({1, 12, 12, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence2) {\n    // q_length is not multiple of 4.\n    this->runTestAlibiMaskedSoftmax({1, 11, 11, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence_HasPrompt1) {\n    this->runTestAlibiMaskedSoftmax({1, 12, 20, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence_HasPrompt2) {\n    // q_length is not multiple of 4.\n    this->runTestAlibiMaskedSoftmax({1, 11, 20, 4, 32, false, 0, false});\n}\n\n// Tests for long sentence generation. Assume the bloom 176B model with 8 TP.\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence1024) {\n    this->runTestAlibiMaskedSoftmax({8, 1024, 1024, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence2048) {\n    this->runTestAlibiMaskedSoftmax({8, 2048, 2048, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence3072) {\n    this->runTestAlibiMaskedSoftmax({8, 3072, 3072, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence4096) {\n    this->runTestAlibiMaskedSoftmax({4, 4096, 4096, 14, 128, false, 0, false, true}, true);\n}\n\n}  // end of namespace\n", "output": "/*\n * Copyright (c) 2022-2023, NVIDIA CORPORATION.  All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"tests/unittests/gtest_utils.h\"\n#include \"src/fastertransformer/kernels/gen_relative_pos_bias.h\"\n#include \"src/fastertransformer/kernels/gpt_kernels.h\"\n#include \"src/fastertransformer/kernels/unfused_attention_kernels.h\"\n#include \"src/fastertransformer/utils/memory_utils.h\"\n#include \"src/fastertransformer/utils/nccl_utils.h\"\n#include \"src/fastertransformer/utils/Tensor.h\"\n\n#include <hiprand/hiprand.h>\n#include <sstream>\n#include <stdexcept>\n#include <vector>\n\nusing namespace fastertransformer;\n\nnamespace {\n\nstruct AttentionKernelTestParam {\n    size_t batch_size    = 4;\n    size_t q_length      = 32;\n    size_t k_length      = 32;\n    size_t head_num      = 4;\n    size_t size_per_head = 32;\n\n    bool   use_fp32_qk_buf      = false;\n    size_t rotary_embedding_dim = 0;\n    bool   neox_rotary_style    = false;\n\n    float  q_scaling = 1.0f;\n};\n\nnamespace utils {\n\n#define CHECK_CURAND(cmd) do {                                                      \\\n    hiprandStatus_t err = cmd;                                                       \\\n    if (err != HIPRAND_STATUS_SUCCESS) {                                             \\\n        throw std::runtime_error(                                                   \\\n            fmtstr(\"[FT][ERROR] hiprand runtime error: %d\", err)); \\\n    }} while(0)                                                                     \\\n\n__global__ void convert_and_copy(half* dst, const float* src, const size_t size)\n{\n    for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < size; idx += blockDim.x * gridDim.x) {\n        dst[idx] = __float2half(src[idx]);\n    }\n}\n\n#ifdef ENABLE_BF16\n__global__ void convert_and_copy(__hip_bfloat16* dst, const float* src, const size_t size)\n{\n    for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < size; idx += blockDim.x * gridDim.x) {\n        dst[idx] = __float2bfloat16(src[idx]);\n    }\n}\n#endif\n\ntemplate<typename T>\nvoid normal(hiprandGenerator_t curng, T* buf, size_t size, float mean, float stddev)\n{\n    float* tmp_buf = nullptr;\n    deviceMalloc(&tmp_buf, size);\n\n    // Generate random values in float data type.\n    CHECK_CURAND(hiprandGenerateNormal(curng, tmp_buf, size / 2, mean, stddev));\n    sync_check_cuda_error();\n\n    // Convert and copy to the output buffer if it is not of type float.\n    dim3 block(512);\n    dim3 grid(min(static_cast<int>((size + block.x - 1) / block.x), 256));\n    convert_and_copy<<<grid, block>>>(buf, tmp_buf, size);\n    hipDeviceSynchronize();\n\n    deviceFree(tmp_buf);\n    sync_check_cuda_error();\n}\n\ntemplate<>\nvoid normal(hiprandGenerator_t curng, float* buf, size_t size, float mean, float stddev)\n{\n    // Generate random values in float data type.\n    CHECK_CURAND(hiprandGenerateNormal(curng, buf, size / 2, mean, stddev));\n    sync_check_cuda_error();\n}\n\ntemplate<typename T>\nvoid normal(hiprandGenerator_t curng, Tensor& tensor, float mean = 0.0f, float stddev = 1.0f)\n{\n    if (tensor.size() > 0) {\n        FT_CHECK(tensor.type == getTensorType<T>());\n        normal(curng, tensor.getPtr<T>(), tensor.size(), mean, stddev);\n    }\n}\n\n__host__ uint32_t pow2_rounddown(uint32_t x)\n{\n    x |= x >> 1;\n    x |= x >> 2;\n    x |= x >> 4;\n    x |= x >> 8;\n    x |= x >> 16;\n    x >>= 1;\n    return x + 1;\n}\n\n}  // namespace utils\n\n////////////////////////////\n// Reference computation.\n////////////////////////////\n\ntemplate<typename T>\ninline T safe_add_bias(const T v, const T* bias, const size_t bias_idx)\n{\n    return bias == nullptr ? v : ::math::add(v, bias[bias_idx]);\n}\n\ntemplate<typename T>\nvoid computeQkSoftmax(T*           attn_score,\n                      const T*     qk,\n                      const T*     attn_mask,\n                      const T*     pos_bias,\n                      const size_t batch_size,\n                      const size_t num_heads,\n                      const size_t q_length,\n                      const size_t k_length,\n                      const T      qk_scale)\n{\n    // attn_score [batch_size, num_heads, q_length, k_length]\n    // qk         [batch_size, num_heads, q_length, k_length]\n    // attn_mask  [batch_size, 1, q_length, k_length]\n    // pos_bias   [1, num_heads, q_length, k_length]\n\n    // batch, head index.\n    for (size_t bhi = 0; bhi < batch_size * num_heads; ++bhi) {\n        size_t bi = bhi / num_heads;  // batch index.\n        size_t hi = bhi % num_heads;  // head index.\n        // The attention mask of the current batch.\n        const T* mask = &attn_mask[bi * q_length * k_length];\n        // The position bias of the current head.\n        const T* head_pos_bias = pos_bias != nullptr ? &pos_bias[hi * q_length * k_length] : nullptr;\n\n        for (size_t qi = 0; qi < q_length; ++qi) {\n            float maxval = -FLT_MAX;\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    if (val > maxval) {\n                        maxval = val;\n                    }\n                }\n            }\n            float sum = 0.0f;\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    sum += expf(val - maxval);\n                }\n            }\n            for (size_t ki = 0; ki < k_length; ++ki) {\n                size_t qk_idx = qi * k_length + ki;\n                if (int(mask[qk_idx]) > 0) {  // mask = 0 or 1.\n                    float val = (float)safe_add_bias(::math::mul(qk_scale, qk[qk_idx]), head_pos_bias, qk_idx);\n                    attn_score[qk_idx] = static_cast<T>(expf(val - maxval) / (sum + EPSILON));\n                }\n                else {\n                    attn_score[qk_idx] = T(0.0f);\n                }\n            }\n        }\n\n        // Move the data pointers to the next.\n        attn_score += q_length * k_length;\n        qk         += q_length * k_length;\n    }\n}\n\ntemplate<typename T>\nclass AttentionKernelTest : public FtTestBase {\n\nprivate:\n    using FtTestBase::stream;\n    using FtTestBase::allocator;\n\n    unsigned long long seed = 31;\n    hiprandGenerator_t  curng;\n\n    Tensor randomAttentionMask(const std::vector<size_t> shape)\n    {\n        // shape (batch_size, 1, max_input_length, max_input_length + max_prompt_length)\n\n        // Create a attention mask tensor and buffer.\n        Tensor attn_mask = createTensor(MEMORY_GPU, getTensorType<T>(), shape);\n\n        // Set the mask values.\n        size_t batch_size   = shape[0];\n        size_t max_q_length = shape[2];\n        size_t max_k_length = shape[3];\n        // TODO: Enable prompts.\n        size_t max_prompt_length = max_k_length - max_q_length;\n\n        Tensor h_seq_lengths    = createTensor(MEMORY_CPU, TYPE_INT32, {batch_size});\n        Tensor h_prompt_lengths = createTensor(MEMORY_CPU, TYPE_INT32, {batch_size});\n        initRandomInt(h_seq_lengths.getPtr<int>(), batch_size, max_q_length, max_q_length + 1);\n        initRandomInt(h_prompt_lengths.getPtr<int>(), batch_size, 0, max_prompt_length + 1);\n\n        Tensor d_seq_lengths    = createTensor(MEMORY_GPU, TYPE_INT32, {batch_size});\n        Tensor d_prompt_lengths = createTensor(MEMORY_GPU, TYPE_INT32, {batch_size});\n        copyTensor(d_seq_lengths, h_seq_lengths);\n        copyTensor(d_prompt_lengths, h_prompt_lengths);\n\n        // Used gpt_kernels function to build attention mask.\n        invokeBuildDecoderAttentionMask(attn_mask.getPtr<T>(),\n                                        d_seq_lengths.getPtr<int>(),\n                                        d_prompt_lengths.getPtr<int>(),\n                                        batch_size,\n                                        max_q_length,\n                                        max_prompt_length,\n                                        stream);\n        sync_check_cuda_error();\n        return attn_mask;\n    };\n\npublic:\n    void SetUp() override\n    {\n        FtTestBase::SetUp();\n        CHECK_CURAND(hiprandCreateGenerator(&curng, HIPRAND_RNG_PSEUDO_DEFAULT));\n        CHECK_CURAND(hiprandSetPseudoRandomGeneratorSeed(curng, seed));\n    }\n\n    void TearDown() override\n    {\n        hiprandDestroyGenerator(curng);\n        FtTestBase::TearDown();\n    }\n\n    void runTestMaskedSoftmax(AttentionKernelTestParam param, bool is_benchmark = false) {\n        DataType dtype = getTensorType<T>();\n\n        std::vector<size_t> qk_shape {param.batch_size, param.head_num, param.q_length, param.k_length};\n\n        bool use_fp32_qk = param.use_fp32_qk_buf && dtype != TYPE_FP32;\n\n        Tensor qk        = createTensor(MEMORY_GPU, dtype, qk_shape);\n        Tensor qk_fp32   = use_fp32_qk ? createTensor(MEMORY_GPU, TYPE_FP32, qk_shape) : Tensor();\n        Tensor attn_mask = randomAttentionMask({param.batch_size, 1, param.q_length, param.k_length});\n        // Input random initialization\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            utils::normal<float>(curng, qk_fp32);\n        }\n        else {\n            utils::normal<T>(curng, qk);\n        }\n\n        // Clone to host for reference computation if needed.\n        Tensor h_qk        = is_benchmark ? Tensor() : toHost<T>(qk);\n        Tensor h_attn_mask = is_benchmark ? Tensor() : toHost<T>(attn_mask);\n        Tensor h_qk_fp32   = is_benchmark ? Tensor() : toHost<float>(qk_fp32);\n\n        T scale = static_cast<T>(1 / sqrtf(param.size_per_head * 1.0f));\n\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            MaskedSoftmaxParam<T, float> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk_fp32.getPtr<float>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n        else {\n            MaskedSoftmaxParam<T, T> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk.getPtr<T>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n\n        if (!is_benchmark) {\n            if (use_fp32_qk) {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk_fp32.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 (T*)nullptr,\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            else {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 (T*)nullptr,\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            bool passed = checkResult(\"MaskedSoftmax\", qk.getPtr<T>(), h_qk.getPtr<T>(), qk.size());\n            EXPECT_TRUE(passed);\n        }\n    }\n\n    void runTestAlibiMaskedSoftmax(AttentionKernelTestParam param, bool is_benchmark = false) {\n        DataType dtype = getTensorType<T>();\n\n        std::vector<size_t> qk_shape {param.batch_size, param.head_num, param.q_length, param.k_length};\n\n        bool use_fp32_qk = param.use_fp32_qk_buf && dtype != TYPE_FP32;\n\n        Tensor qk           = createTensor(MEMORY_GPU, dtype, qk_shape);\n        Tensor qk_fp32      = use_fp32_qk ? createTensor(MEMORY_GPU, TYPE_FP32, qk_shape) : Tensor();\n        Tensor attn_mask    = randomAttentionMask({param.batch_size, 1, param.q_length, param.k_length});\n        Tensor alibi_slopes = createTensor(MEMORY_GPU, dtype, {param.head_num});\n\n        // Input random initialization\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            utils::normal<float>(curng, qk_fp32);\n        }\n        else {\n            utils::normal<T>(curng, qk);\n        }\n        invokeBuildAlibiSlopes(alibi_slopes.getPtr<T>(), param.head_num, stream);\n        sync_check_cuda_error();\n\n        Tensor h_alibi_slopes = createTensor(MEMORY_CPU, dtype, {param.head_num});\n        Tensor h_alibi_bias   = is_benchmark ? Tensor() :\n            createTensor(MEMORY_CPU, dtype, {param.head_num, param.q_length, param.k_length});\n        // The nearest power of 2 equal to / smaller than num_heads followed by HF's implementation.\n        T* alibi_slope_ptr = h_alibi_slopes.getPtr<T>();\n        int num_heads_pow2 = utils::pow2_rounddown(param.head_num);\n        for (size_t h = 0; h < param.head_num; ++h) {\n            // The slope of linear bias of the attention head\n            if (h < num_heads_pow2) {\n                alibi_slope_ptr[h] = static_cast<T>(powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2) - 3.f)), h + 1));\n            } else {\n                alibi_slope_ptr[h] = static_cast<T>(\n                    powf(powf(0.5f, powf(0.5f, log2f(num_heads_pow2 << 1) - 3.f)), (h - num_heads_pow2) * 2 + 1));\n            }\n            if (h_alibi_bias.size() > 0) {\n                T* alibi_bias_ptr = h_alibi_bias.getPtr<T>();\n                for (size_t qi = 0; qi < param.q_length; ++qi) {\n                    for (size_t ki = 0; ki < param.k_length; ++ki) {\n                        size_t hqk_idx = (h * param.q_length + qi) * param.k_length + ki;\n                        alibi_bias_ptr[hqk_idx] = ::math::mul(alibi_slope_ptr[h], T(0.0f + ki - qi));\n                    }\n                }\n            }\n        }\n        EXPECT_TRUE(\n            checkResult(\"CheckAlibiSlopes\", alibi_slopes.getPtr<T>(), h_alibi_slopes.getPtr<T>(), param.head_num));\n\n        // Clone to host for reference computation if needed.\n        Tensor h_qk        = is_benchmark ? Tensor() : toHost<T>(qk);\n        Tensor h_attn_mask = is_benchmark ? Tensor() : toHost<T>(attn_mask);\n        Tensor h_qk_fp32   = is_benchmark ? Tensor() : toHost<float>(qk_fp32);\n\n        T scale = static_cast<T>(1 / sqrtf(param.size_per_head * 1.0f));\n\n        if (param.use_fp32_qk_buf && dtype != TYPE_FP32) {\n            MaskedSoftmaxParam<T, float> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk_fp32.getPtr<float>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.linear_bias_slopes = alibi_slopes.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n        else {\n            MaskedSoftmaxParam<T, T> softmax_param;\n            softmax_param.attention_score    = qk.getPtr<T>();\n            softmax_param.qk                 = qk.getPtr<T>();\n            softmax_param.attention_mask     = attn_mask.getPtr<T>();\n            softmax_param.linear_bias_slopes = alibi_slopes.getPtr<T>();\n            softmax_param.batch_size         = param.batch_size;\n            softmax_param.num_heads          = param.head_num;\n            softmax_param.q_length           = param.q_length;\n            softmax_param.k_length           = param.k_length;\n            softmax_param.qk_scale           = scale;\n            invokeMaskedSoftmax(softmax_param, stream);\n            sync_check_cuda_error();\n        }\n\n        if (!is_benchmark) {\n            if (use_fp32_qk) {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk_fp32.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 h_alibi_bias.getPtr<T>(),\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            else {\n                computeQkSoftmax(h_qk.getPtr<T>(),\n                                 h_qk.getPtr<T>(),\n                                 h_attn_mask.getPtr<T>(),\n                                 h_alibi_bias.getPtr<T>(),\n                                 param.batch_size,\n                                 param.head_num,\n                                 param.q_length,\n                                 param.k_length,\n                                 scale);\n            }\n            bool passed = checkResult(\"AlibiMaskedSoftmax\", qk.getPtr<T>(), h_qk.getPtr<T>(), qk.size());\n            EXPECT_TRUE(passed);\n        }\n    }\n};\n\nTYPED_TEST_SUITE(AttentionKernelTest, SupportTypes);\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_NoPrompt) {\n    this->runTestMaskedSoftmax({1, 12, 12, 1, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_NoPrompt2) {\n    // q_length is not multiple of 4.\n    this->runTestMaskedSoftmax({1, 11, 11, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_HasPrompt) {\n    this->runTestMaskedSoftmax({1, 12, 24, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_HasPrompt2) {\n    this->runTestMaskedSoftmax({1, 11, 24, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence1024) {\n    this->runTestMaskedSoftmax({1, 12, 1024, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence2048) {\n    this->runTestMaskedSoftmax({1, 12, 2048, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence3072) {\n    this->runTestMaskedSoftmax({1, 12, 3072, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, MaskedSoftmax_LongSequence4096) {\n    this->runTestMaskedSoftmax({1, 12, 4096, 2, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence1024) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 1024, 1024, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence2048) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 2048, 2048, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_MaskedSoftmax_LongSequence4096) {\n    // Assume the bloom 176B model with 8 TP.\n    this->runTestMaskedSoftmax({8, 4096, 4096, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence1) {\n    this->runTestAlibiMaskedSoftmax({1, 12, 12, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence2) {\n    // q_length is not multiple of 4.\n    this->runTestAlibiMaskedSoftmax({1, 11, 11, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence_HasPrompt1) {\n    this->runTestAlibiMaskedSoftmax({1, 12, 20, 4, 32, false, 0, false});\n}\n\nTYPED_TEST(AttentionKernelTest, AlibiMaskedSoftmax_ShortSequence_HasPrompt2) {\n    // q_length is not multiple of 4.\n    this->runTestAlibiMaskedSoftmax({1, 11, 20, 4, 32, false, 0, false});\n}\n\n// Tests for long sentence generation. Assume the bloom 176B model with 8 TP.\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence1024) {\n    this->runTestAlibiMaskedSoftmax({8, 1024, 1024, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence2048) {\n    this->runTestAlibiMaskedSoftmax({8, 2048, 2048, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence3072) {\n    this->runTestAlibiMaskedSoftmax({8, 3072, 3072, 14, 128, false, 0, false, true}, true);\n}\n\nTYPED_TEST(AttentionKernelTest, Benchmark_AlibiMaskedSoftmax_LongSequence4096) {\n    this->runTestAlibiMaskedSoftmax({4, 4096, 4096, 14, 128, false, 0, false, true}, true);\n}\n\n}  // end of namespace\n", "source": "/kaggle/working/FasterTransformer/tests/unittests/test_attention_kernels.cu"}
{"input": "#include \"cuda_utils.h\"\n\ntemplate<typename T_OUT, typename T_IN>\nvoid __global__ cudaCastKernel(T_OUT* out, T_IN const * const in, const uint32_t size)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i+= gridDim.x * blockDim.x) {\n        out[i] = (T_OUT)((T_IN)(in[i]));\n    }\n}\n\n\ntemplate<typename T_OUT, typename T_IN>\nvoid cudaCast(T_OUT* out, T_IN const * const in, const uint32_t size)\n{\n    dim3 block(256);\n    dim3 grid((size + 1) / 256 * 256);\n    cudaCastKernel<<<grid, block>>>(out, in, size);\n}\n\ntemplate void cudaCast(float* out, float const * const in, const uint32_t size);\ntemplate void cudaCast(__nv_fp8_e4m3* out, float const * const in, const uint32_t size);\n\n\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\nquatizeVectorE4M3(__nv_fp8_e4m3* output, float const* input_qua_amax_ptr, T const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = __nv_fp8_e4m3((float)(input[i]) / __ldg(input_qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = __nv_fp8_e4m3((float)(input[i]) / __ldg(input_qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeQuatizeVectorE4M3(__nv_fp8_e4m3* output,\n                             float const* input_qua_amax_ptr,\n                             T const* input,\n                             uint32_t size,\n                             uint32_t n,\n                             cudaStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    quatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, input_qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeQuatizeVectorE4M3<float, 0>(__nv_fp8_e4m3* output,\n                                                float const* input_qua_amax_ptr,\n                                                float const* input,\n                                                uint32_t size,\n                                                uint32_t n,\n                                                cudaStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<half, 0>(__nv_fp8_e4m3* output,\n                                               float const* input_qua_amax_ptr,\n                                               half const* input,\n                                               uint32_t size,\n                                               uint32_t n,\n                                               cudaStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<float, 1>(__nv_fp8_e4m3* output,\n                                                float const* input_qua_amax_ptr,\n                                                float const* input,\n                                                uint32_t size,\n                                                uint32_t n,\n                                                cudaStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<half, 1>(__nv_fp8_e4m3* output,\n                                               float const* input_qua_amax_ptr,\n                                               half const* input,\n                                               uint32_t size,\n                                               uint32_t n,\n                                               cudaStream_t stream);\n\n\ntemplate<typename T_OUT, typename T_IN, int QUANTIZE_MODE>\n__global__ void\ndequatizeVectorE4M3(T_OUT* output, float const* qua_amax_ptr, T_IN const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = T_OUT(float(input[i]) * __ldg(qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = T_OUT(float(input[i]) * __ldg(qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, int QUANTIZE_MODE>\nvoid invokeDequatizeVectorE4M3(\n    T_OUT* output, float const* qua_amax_ptr, T_IN const* input, uint32_t size, uint32_t n, cudaStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    dequatizeVectorE4M3<T_OUT, T_IN, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeDequatizeVectorE4M3<float, __nv_fp8_e4m3, 0>(float* output,\n                                                  float const* qua_amax_ptr,\n                                                  __nv_fp8_e4m3 const* input,\n                                                  uint32_t size,\n                                                  uint32_t n,\n                                                  cudaStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<half, __nv_fp8_e4m3, 0>(half* output,\n                                                 float const* qua_amax_ptr,\n                                                 __nv_fp8_e4m3 const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 cudaStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<float, __nv_fp8_e4m3, 1>(float* output,\n                                                  float const* qua_amax_ptr,\n                                                  __nv_fp8_e4m3 const* input,\n                                                  uint32_t size,\n                                                  uint32_t n,\n                                                  cudaStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<half, __nv_fp8_e4m3, 1>(half* output,\n                                                 float const* qua_amax_ptr,\n                                                 __nv_fp8_e4m3 const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 cudaStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<__nv_bfloat16, __nv_bfloat16, 0>(__nv_bfloat16* output,\n                                                 float const* qua_amax_ptr,\n                                                 __nv_bfloat16 const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 cudaStream_t stream);", "output": "#include \"hip/hip_runtime.h\"\n#include \"cuda_utils.h\"\n\ntemplate<typename T_OUT, typename T_IN>\nvoid __global__ cudaCastKernel(T_OUT* out, T_IN const * const in, const uint32_t size)\n{\n    for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < size; i+= gridDim.x * blockDim.x) {\n        out[i] = (T_OUT)((T_IN)(in[i]));\n    }\n}\n\n\ntemplate<typename T_OUT, typename T_IN>\nvoid cudaCast(T_OUT* out, T_IN const * const in, const uint32_t size)\n{\n    dim3 block(256);\n    dim3 grid((size + 1) / 256 * 256);\n    cudaCastKernel<<<grid, block>>>(out, in, size);\n}\n\ntemplate void cudaCast(float* out, float const * const in, const uint32_t size);\ntemplate void cudaCast(__hip_fp8_e4m3_fnuz* out, float const * const in, const uint32_t size);\n\n\n\ntemplate<typename T, int QUANTIZE_MODE>\n__global__ void\nquatizeVectorE4M3(__hip_fp8_e4m3_fnuz* output, float const* input_qua_amax_ptr, T const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = __hip_fp8_e4m3_fnuz((float)(input[i]) / __ldg(input_qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = __hip_fp8_e4m3_fnuz((float)(input[i]) / __ldg(input_qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T, int QUANTIZE_MODE>\nvoid invokeQuatizeVectorE4M3(__hip_fp8_e4m3_fnuz* output,\n                             float const* input_qua_amax_ptr,\n                             T const* input,\n                             uint32_t size,\n                             uint32_t n,\n                             hipStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    quatizeVectorE4M3<T, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, input_qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeQuatizeVectorE4M3<float, 0>(__hip_fp8_e4m3_fnuz* output,\n                                                float const* input_qua_amax_ptr,\n                                                float const* input,\n                                                uint32_t size,\n                                                uint32_t n,\n                                                hipStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<half, 0>(__hip_fp8_e4m3_fnuz* output,\n                                               float const* input_qua_amax_ptr,\n                                               half const* input,\n                                               uint32_t size,\n                                               uint32_t n,\n                                               hipStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<float, 1>(__hip_fp8_e4m3_fnuz* output,\n                                                float const* input_qua_amax_ptr,\n                                                float const* input,\n                                                uint32_t size,\n                                                uint32_t n,\n                                                hipStream_t stream);\ntemplate void invokeQuatizeVectorE4M3<half, 1>(__hip_fp8_e4m3_fnuz* output,\n                                               float const* input_qua_amax_ptr,\n                                               half const* input,\n                                               uint32_t size,\n                                               uint32_t n,\n                                               hipStream_t stream);\n\n\ntemplate<typename T_OUT, typename T_IN, int QUANTIZE_MODE>\n__global__ void\ndequatizeVectorE4M3(T_OUT* output, float const* qua_amax_ptr, T_IN const* input, uint32_t size, uint32_t n)\n{\n    for (uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; i < size; i += blockDim.x * gridDim.x) {\n        if (QUANTIZE_MODE == 0) {\n            output[i] = T_OUT(float(input[i]) * __ldg(qua_amax_ptr + (i % n)));\n        }\n        else {\n            output[i] = T_OUT(float(input[i]) * __ldg(qua_amax_ptr));\n        }\n    }\n}\n\ntemplate<typename T_OUT, typename T_IN, int QUANTIZE_MODE>\nvoid invokeDequatizeVectorE4M3(\n    T_OUT* output, float const* qua_amax_ptr, T_IN const* input, uint32_t size, uint32_t n, hipStream_t stream)\n{\n    dim3 grid(1);\n    dim3 block(256);\n    dequatizeVectorE4M3<T_OUT, T_IN, QUANTIZE_MODE><<<grid, block, 0, stream>>>(output, qua_amax_ptr, input, size, n);\n}\n\ntemplate void invokeDequatizeVectorE4M3<float, __hip_fp8_e4m3_fnuz, 0>(float* output,\n                                                  float const* qua_amax_ptr,\n                                                  __hip_fp8_e4m3_fnuz const* input,\n                                                  uint32_t size,\n                                                  uint32_t n,\n                                                  hipStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<half, __hip_fp8_e4m3_fnuz, 0>(half* output,\n                                                 float const* qua_amax_ptr,\n                                                 __hip_fp8_e4m3_fnuz const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 hipStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<float, __hip_fp8_e4m3_fnuz, 1>(float* output,\n                                                  float const* qua_amax_ptr,\n                                                  __hip_fp8_e4m3_fnuz const* input,\n                                                  uint32_t size,\n                                                  uint32_t n,\n                                                  hipStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<half, __hip_fp8_e4m3_fnuz, 1>(half* output,\n                                                 float const* qua_amax_ptr,\n                                                 __hip_fp8_e4m3_fnuz const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 hipStream_t stream);\ntemplate void invokeDequatizeVectorE4M3<__hip_bfloat16, __hip_bfloat16, 0>(__hip_bfloat16* output,\n                                                 float const* qua_amax_ptr,\n                                                 __hip_bfloat16 const* input,\n                                                 uint32_t size,\n                                                 uint32_t n,\n                                                 hipStream_t stream);", "source": "/kaggle/working/FasterTransformer/tests/unittests/fp8_gemm_test/cuda_utils.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\nThis is a simple example demonstrating the use of CCCL functionality from Thrust, CUB, and libcu++.\n\nThe example computes the sum of an array of integers using a simple parallel reduction. Each thread block\ncomputes the sum of a subset of the array using cuB::BlockRecuce. The sum of each block is then reduced\nto a single value using an atomic add via cuda::atomic_ref from libcu++. The result is stored in a device_vector\nfrom Thrust. The sum is then printed to the console.\n*/\n\n#include <cub/block/block_reduce.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <cuda/atomic>\n\n#include <cstdio>\n#include <iostream>\n\nconstexpr int block_size = 256;\n\n__global__ void sumKernel(int const* data, int* result, std::size_t N)\n{\n  using BlockReduce = cub::BlockReduce<int, block_size>;\n\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n\n  int sum = 0;\n  if (index < N)\n  {\n    sum += data[index];\n  }\n\n  sum = BlockReduce(temp_storage).Sum(sum);\n\n  if (threadIdx.x == 0)\n  {\n    cuda::atomic_ref<int, cuda::thread_scope_device> atomic_result(*result);\n    atomic_result.fetch_add(sum, cuda::memory_order_relaxed);\n  }\n}\n\nint main()\n{\n  std::size_t N = 1000;\n  thrust::device_vector<int> data(N, 1);\n  thrust::device_vector<int> result(1);\n\n  int num_blocks = (N + block_size - 1) / block_size;\n\n  sumKernel<<<num_blocks, block_size>>>(\n    thrust::raw_pointer_cast(data.data()), thrust::raw_pointer_cast(result.data()), N);\n\n  auto err = cudaDeviceSynchronize();\n  if (err != cudaSuccess)\n  {\n    std::cout << \"Error: \" << cudaGetErrorString(err) << '\\n';\n    return -1;\n  }\n\n  std::cout << \"Sum: \" << result[0] << '\\n';\n\n  assert(result[0] == N);\n\n  return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\nThis is a simple example demonstrating the use of CCCL functionality from Thrust, CUB, and libcu++.\n\nThe example computes the sum of an array of integers using a simple parallel reduction. Each thread block\ncomputes the sum of a subset of the array using cuB::BlockRecuce. The sum of each block is then reduced\nto a single value using an atomic add via cuda::atomic_ref from libcu++. The result is stored in a device_vector\nfrom Thrust. The sum is then printed to the console.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_reduce.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <cuda/atomic>\n\n#include <cstdio>\n#include <iostream>\n\nconstexpr int block_size = 256;\n\n__global__ void sumKernel(int const* data, int* result, std::size_t N)\n{\n  using BlockReduce = hipcub::BlockReduce<int, block_size>;\n\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n\n  int index = threadIdx.x + blockIdx.x * blockDim.x;\n\n  int sum = 0;\n  if (index < N)\n  {\n    sum += data[index];\n  }\n\n  sum = BlockReduce(temp_storage).Sum(sum);\n\n  if (threadIdx.x == 0)\n  {\n    cuda::atomic_ref<int, cuda::thread_scope_device> atomic_result(*result);\n    atomic_result.fetch_add(sum, cuda::memory_order_relaxed);\n  }\n}\n\nint main()\n{\n  std::size_t N = 1000;\n  thrust::device_vector<int> data(N, 1);\n  thrust::device_vector<int> result(1);\n\n  int num_blocks = (N + block_size - 1) / block_size;\n\n  sumKernel<<<num_blocks, block_size>>>(\n    thrust::raw_pointer_cast(data.data()), thrust::raw_pointer_cast(result.data()), N);\n\n  auto err = hipDeviceSynchronize();\n  if (err != hipSuccess)\n  {\n    std::cout << \"Error: \" << hipGetErrorString(err) << '\\n';\n    return -1;\n  }\n\n  std::cout << \"Sum: \" << result[0] << '\\n';\n\n  assert(result[0] == N);\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/examples/basic/example.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// This example demonstrates how kernels can be launched using cuda::launch.\n\n#include <cuda/devices>\n#include <cuda/hierarchy>\n#include <cuda/launch>\n#include <cuda/stream>\n\n#include <cstdio>\n#include <exception>\n\n#include \"common.cuh\"\n\n// Regular kernel.\n__global__ void kernel(KernelName kernel_name)\n{\n  // Call say_hello with this thread's index.\n  say_hello(cuda::gpu_thread.index(cuda::block), kernel_name);\n}\n\n// Regular kernel with dynamic shared memory.\n__global__ void kernel_with_dynamic_smem(KernelName kernel_name)\n{\n  // Get the dynamic shared memory handle.\n  extern __shared__ uint3 smem[];\n\n  // Get all necessary hierarchy values.\n  const auto tindex = cuda::gpu_thread.index(cuda::block);\n  const auto trank  = cuda::gpu_thread.rank(cuda::block);\n  const auto tcount = cuda::gpu_thread.count(cuda::block);\n\n  // Each thread will write it's index to the next thread's index in the shared memory.\n  smem[(trank + 1) % tcount] = tindex;\n\n  // Wait for the all threads to finish the write to shared memory.\n  __syncthreads();\n\n  // Call say_hello with previous thread's index.\n  say_hello(smem[trank], kernel_name);\n}\n\n// Kernel that takes cuda::kernel_config as the first parameter. That way the kernel has access to compile time\n// information of the block and grid dimensions which can produce better optimized kernels.\ntemplate <class Config>\n__global__ void kernel_with_config(Config config, KernelName kernel_name)\n{\n  // Call say_hello with this thread's index. Note that passing config to hierarchy queries can improve their\n  // performance since the functionality has access to the compile time specified dimensions.\n  say_hello(cuda::gpu_thread.index(cuda::block, config), kernel_name);\n}\n\n// Kernel that takes cuda::kernel_config that contains the cuda::dynamic_shared_memory_option.\ntemplate <class Config>\n__global__ void kernel_with_config_and_dynamic_smem(Config config, KernelName kernel_name)\n{\n  // Retrieve the dynamic shared memory view. Since we passed uint3[4], we will get cuda::std::span<uint3>.\n  const auto smem = cuda::dynamic_shared_memory(config);\n\n  // Get all necessary hierarchy values.\n  const auto tindex = cuda::gpu_thread.index(cuda::block, config);\n  const auto trank  = cuda::gpu_thread.rank(cuda::block, config);\n  const auto tcount = cuda::gpu_thread.count(cuda::block, config);\n\n  // Each thread will write it's index to the next thread's index in the shared memory.\n  smem[(trank + 1) % tcount] = tindex;\n\n  // Wait for the all threads to finish the write to shared memory.\n  __syncthreads();\n\n  // Call say hello with received previous thread's index.\n  say_hello(smem[trank], kernel_name);\n}\n\nint main()\ntry\n{\n  // Check we have at least one device.\n  if (cuda::devices.size() == 0)\n  {\n    std::fprintf(stderr, \"No CUDA devices found\\n\");\n    return 1;\n  }\n\n  // We will use the first device.\n  cuda::device_ref device = cuda::devices[0];\n\n  // cuda::launch always requires a work submitter, so let's create a CUDA stream.\n  cuda::stream stream{device};\n\n  // Set block and grid dimensions to be used with the kernel config. Dimensions specified as template parameters will\n  // be statically known in the kernel.\n  const auto block_dims = cuda::block_dims<2, 2>();\n  const auto grid_dims  = cuda::grid_dims(dim3{1});\n\n  // Make the kernel config.\n  const auto kernel_config = cuda::make_config(grid_dims, block_dims);\n\n  // For kernels that use dynamic shared memory, we need a dynamic shared memory option to be passed in the kernel\n  // config.\n  const auto dyn_smem_opt = cuda::dynamic_shared_memory<uint3[]>(cuda::gpu_thread.count(cuda::block, kernel_config));\n\n  // Make the kernel config with dynamic shared memory option.\n  const auto kernel_config_with_dyn_smem = cuda::make_config(grid_dims, block_dims, dyn_smem_opt);\n\n  // Launch the kernel using the kernel config.\n  cuda::launch(stream, kernel_config, kernel, KernelName{\"kernel\"});\n\n  // Launch the kernel using the kernel config with dynamic shared memory option.\n  cuda::launch(\n    stream, kernel_config_with_dyn_smem, kernel_with_dynamic_smem, KernelName{\"kernel with dynamic shared memory\"});\n\n  // Launching kernels with template parameters is more complicated. All of the template parameters must be specified to\n  // obtain the kernel address. Kernel functors can simplify this case a lot.\n  cuda::launch(stream, kernel_config, kernel_with_config<decltype(kernel_config)>, KernelName{\"kernel with config\"});\n\n  // Kernels with configs that contain dynamic shared memory option must be launched similarly.\n  cuda::launch(stream,\n               kernel_config_with_dyn_smem,\n               kernel_with_config_and_dynamic_smem<decltype(kernel_config_with_dyn_smem)>,\n               KernelName{\"kernel with config and dynamic shared memory\"});\n\n  // Wait for all of the tasks in the stream to complete.\n  stream.sync();\n}\ncatch (const cuda::cuda_error& e)\n{\n  std::fprintf(stderr, \"CUDA error: %s\\n\", e.what());\n  return 1;\n}\ncatch (const std::exception& e)\n{\n  std::fprintf(stderr, \"Error: %s\\n\", e.what());\n  return 1;\n}\ncatch (...)\n{\n  std::fprintf(stderr, \"An unknown error was encountered\\n\");\n  return 1;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// This example demonstrates how kernels can be launched using cuda::launch.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/devices>\n#include <cuda/hierarchy>\n#include <cuda/launch>\n#include <cuda/stream>\n\n#include <cstdio>\n#include <exception>\n\n#include \"common.cuh\"\n\n// Regular kernel.\n__global__ void kernel(KernelName kernel_name)\n{\n  // Call say_hello with this thread's index.\n  say_hello(cuda::gpu_thread.index(cuda::block), kernel_name);\n}\n\n// Regular kernel with dynamic shared memory.\n__global__ void kernel_with_dynamic_smem(KernelName kernel_name)\n{\n  // Get the dynamic shared memory handle.\n  extern __shared__ uint3 smem[];\n\n  // Get all necessary hierarchy values.\n  const auto tindex = cuda::gpu_thread.index(cuda::block);\n  const auto trank  = cuda::gpu_thread.rank(cuda::block);\n  const auto tcount = cuda::gpu_thread.count(cuda::block);\n\n  // Each thread will write it's index to the next thread's index in the shared memory.\n  smem[(trank + 1) % tcount] = tindex;\n\n  // Wait for the all threads to finish the write to shared memory.\n  __syncthreads();\n\n  // Call say_hello with previous thread's index.\n  say_hello(smem[trank], kernel_name);\n}\n\n// Kernel that takes cuda::kernel_config as the first parameter. That way the kernel has access to compile time\n// information of the block and grid dimensions which can produce better optimized kernels.\ntemplate <class Config>\n__global__ void kernel_with_config(Config config, KernelName kernel_name)\n{\n  // Call say_hello with this thread's index. Note that passing config to hierarchy queries can improve their\n  // performance since the functionality has access to the compile time specified dimensions.\n  say_hello(cuda::gpu_thread.index(cuda::block, config), kernel_name);\n}\n\n// Kernel that takes cuda::kernel_config that contains the cuda::dynamic_shared_memory_option.\ntemplate <class Config>\n__global__ void kernel_with_config_and_dynamic_smem(Config config, KernelName kernel_name)\n{\n  // Retrieve the dynamic shared memory view. Since we passed uint3[4], we will get cuda::std::span<uint3>.\n  const auto smem = cuda::dynamic_shared_memory(config);\n\n  // Get all necessary hierarchy values.\n  const auto tindex = cuda::gpu_thread.index(cuda::block, config);\n  const auto trank  = cuda::gpu_thread.rank(cuda::block, config);\n  const auto tcount = cuda::gpu_thread.count(cuda::block, config);\n\n  // Each thread will write it's index to the next thread's index in the shared memory.\n  smem[(trank + 1) % tcount] = tindex;\n\n  // Wait for the all threads to finish the write to shared memory.\n  __syncthreads();\n\n  // Call say hello with received previous thread's index.\n  say_hello(smem[trank], kernel_name);\n}\n\nint main()\ntry\n{\n  // Check we have at least one device.\n  if (cuda::devices.size() == 0)\n  {\n    std::fprintf(stderr, \"No CUDA devices found\\n\");\n    return 1;\n  }\n\n  // We will use the first device.\n  cuda::device_ref device = cuda::devices[0];\n\n  // cuda::launch always requires a work submitter, so let's create a CUDA stream.\n  cuda::stream stream{device};\n\n  // Set block and grid dimensions to be used with the kernel config. Dimensions specified as template parameters will\n  // be statically known in the kernel.\n  const auto block_dims = cuda::block_dims<2, 2>();\n  const auto grid_dims  = cuda::grid_dims(dim3{1});\n\n  // Make the kernel config.\n  const auto kernel_config = cuda::make_config(grid_dims, block_dims);\n\n  // For kernels that use dynamic shared memory, we need a dynamic shared memory option to be passed in the kernel\n  // config.\n  const auto dyn_smem_opt = cuda::dynamic_shared_memory<uint3[]>(cuda::gpu_thread.count(cuda::block, kernel_config));\n\n  // Make the kernel config with dynamic shared memory option.\n  const auto kernel_config_with_dyn_smem = cuda::make_config(grid_dims, block_dims, dyn_smem_opt);\n\n  // Launch the kernel using the kernel config.\n  cuda::launch(stream, kernel_config, kernel, KernelName{\"kernel\"});\n\n  // Launch the kernel using the kernel config with dynamic shared memory option.\n  cuda::launch(\n    stream, kernel_config_with_dyn_smem, kernel_with_dynamic_smem, KernelName{\"kernel with dynamic shared memory\"});\n\n  // Launching kernels with template parameters is more complicated. All of the template parameters must be specified to\n  // obtain the kernel address. Kernel functors can simplify this case a lot.\n  cuda::launch(stream, kernel_config, kernel_with_config<decltype(kernel_config)>, KernelName{\"kernel with config\"});\n\n  // Kernels with configs that contain dynamic shared memory option must be launched similarly.\n  cuda::launch(stream,\n               kernel_config_with_dyn_smem,\n               kernel_with_config_and_dynamic_smem<decltype(kernel_config_with_dyn_smem)>,\n               KernelName{\"kernel with config and dynamic shared memory\"});\n\n  // Wait for all of the tasks in the stream to complete.\n  stream.sync();\n}\ncatch (const cuda::cuda_error& e)\n{\n  std::fprintf(stderr, \"CUDA error: %s\\n\", e.what());\n  return 1;\n}\ncatch (const std::exception& e)\n{\n  std::fprintf(stderr, \"Error: %s\\n\", e.what());\n  return 1;\n}\ncatch (...)\n{\n  std::fprintf(stderr, \"An unknown error was encountered\\n\");\n  return 1;\n}\n", "source": "/kaggle/working/cccl/examples/ccclrt/kernel_launch_patterns/kernel.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <stdio.h>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <cuda/std/span>\n\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include <cuda_runtime.h>\n\n#include \"vector.cuh\"\n\nnamespace cudax = cuda::experimental;\nusing cudax::in;\nusing cudax::out;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(cudax::span<const float> A, cudax::span<const float> B, cudax::span<float> C)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < A.size())\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\n/**\n * Host main routine\n */\nint main(void)\ntry\n{\n  // A CUDA stream on which to execute the vector addition kernel\n  cudax::stream stream(cuda::devices[0]);\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host vectors\n  cudax::vector<float> A(numElements); // input\n  cudax::vector<float> B(numElements); // input\n  cudax::vector<float> C(numElements); // output\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    A[i] = rand() / (float) RAND_MAX;\n    B[i] = rand() / (float) RAND_MAX;\n  }\n\n  // Define the kernel launch parameters\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  // Launch the vectorAdd kernel\n  printf(\"CUDA kernel launch with %zu blocks of %d threads\\n\", cuda::block.count(cuda::grid, config), threadsPerBlock);\n  cudax::launch(stream, config, vectorAdd, in(A), in(B), out(C));\n\n  printf(\"waiting for the stream to finish\\n\");\n  stream.sync();\n\n  printf(\"verifying the results\\n\");\n  // Verify that the result vector is correct\n  for (int i = 0; i < numElements; ++i)\n  {\n    if (fabs(A[i] + B[i] - C[i]) > 1e-5)\n    {\n      fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  printf(\"Test PASSED\\n\");\n\n  printf(\"Done\\n\");\n  return 0;\n}\ncatch (const std::exception& e)\n{\n  printf(\"caught an exception: \\\"%s\\\"\\n\", e.what());\n}\ncatch (...)\n{\n  printf(\"caught an unknown exception\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <stdio.h>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <cuda/std/span>\n\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include <hip/hip_runtime.h>\n\n#include \"vector.cuh\"\n\nnamespace cudax = cuda::experimental;\nusing cudax::in;\nusing cudax::out;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(cudax::span<const float> A, cudax::span<const float> B, cudax::span<float> C)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < A.size())\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\n/**\n * Host main routine\n */\nint main(void)\ntry\n{\n  // A CUDA stream on which to execute the vector addition kernel\n  cudax::stream stream(cuda::devices[0]);\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host vectors\n  cudax::vector<float> A(numElements); // input\n  cudax::vector<float> B(numElements); // input\n  cudax::vector<float> C(numElements); // output\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    A[i] = rand() / (float) RAND_MAX;\n    B[i] = rand() / (float) RAND_MAX;\n  }\n\n  // Define the kernel launch parameters\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  // Launch the vectorAdd kernel\n  printf(\"CUDA kernel launch with %zu blocks of %d threads\\n\", cuda::block.count(cuda::grid, config), threadsPerBlock);\n  cudax::launch(stream, config, vectorAdd, in(A), in(B), out(C));\n\n  printf(\"waiting for the stream to finish\\n\");\n  stream.sync();\n\n  printf(\"verifying the results\\n\");\n  // Verify that the result vector is correct\n  for (int i = 0; i < numElements; ++i)\n  {\n    if (fabs(A[i] + B[i] - C[i]) > 1e-5)\n    {\n      fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  printf(\"Test PASSED\\n\");\n\n  printf(\"Done\\n\");\n  return 0;\n}\ncatch (const std::exception& e)\n{\n  printf(\"caught an exception: \\\"%s\\\"\\n\", e.what());\n}\ncatch (...)\n{\n  printf(\"caught an unknown exception\\n\");\n}\n", "source": "/kaggle/working/cccl/examples/cudax/vector_add/vector_add.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <vector>\n\n#include <cuda_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void axpy(int cnt, double a, const double* x, double* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < cnt; i += nthreads)\n  {\n    y[i] += a * x[i];\n  }\n}\n\ndouble X0(int i)\n{\n  return sin(static_cast<double>(i));\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nC2H_TEST(\"axpy with stf cuda_kernel\", \"[cuda_kernel]\")\n{\n  size_t N = 1000000;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  std::vector<double> X(N);\n  std::vector<double> Y(N);\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  const double alpha = 3.14;\n\n  stf_logical_data_handle lX = stf_logical_data(ctx, X.data(), N * sizeof(double));\n  stf_logical_data_handle lY = stf_logical_data(ctx, Y.data(), N * sizeof(double));\n  REQUIRE(lX != nullptr);\n  REQUIRE(lY != nullptr);\n\n  stf_logical_data_set_symbol(lX, \"X\");\n  stf_logical_data_set_symbol(lY, \"Y\");\n\n  stf_cuda_kernel_handle k = stf_cuda_kernel_create(ctx);\n  REQUIRE(k != nullptr);\n  stf_cuda_kernel_set_symbol(k, \"axpy\");\n  stf_cuda_kernel_add_dep(k, lX, STF_READ);\n  stf_cuda_kernel_add_dep(k, lY, STF_RW);\n  stf_cuda_kernel_start(k);\n  double* dX          = (double*) stf_cuda_kernel_get_arg(k, 0);\n  double* dY          = (double*) stf_cuda_kernel_get_arg(k, 1);\n  const void* args[4] = {&N, &alpha, &dX, &dY};\n  cudaError_t err     = stf_cuda_kernel_add_desc(k, (void*) axpy, 2, 4, 0, 4, args);\n  REQUIRE(err == cudaSuccess);\n  stf_cuda_kernel_end(k);\n  stf_cuda_kernel_destroy(k);\n\n  stf_logical_data_destroy(lX);\n  stf_logical_data_destroy(lY);\n\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <vector>\n\n#include <hip/hip_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void axpy(int cnt, double a, const double* x, double* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < cnt; i += nthreads)\n  {\n    y[i] += a * x[i];\n  }\n}\n\ndouble X0(int i)\n{\n  return sin(static_cast<double>(i));\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nC2H_TEST(\"axpy with stf cuda_kernel\", \"[cuda_kernel]\")\n{\n  size_t N = 1000000;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  std::vector<double> X(N);\n  std::vector<double> Y(N);\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  const double alpha = 3.14;\n\n  stf_logical_data_handle lX = stf_logical_data(ctx, X.data(), N * sizeof(double));\n  stf_logical_data_handle lY = stf_logical_data(ctx, Y.data(), N * sizeof(double));\n  REQUIRE(lX != nullptr);\n  REQUIRE(lY != nullptr);\n\n  stf_logical_data_set_symbol(lX, \"X\");\n  stf_logical_data_set_symbol(lY, \"Y\");\n\n  stf_cuda_kernel_handle k = stf_cuda_kernel_create(ctx);\n  REQUIRE(k != nullptr);\n  stf_cuda_kernel_set_symbol(k, \"axpy\");\n  stf_cuda_kernel_add_dep(k, lX, STF_READ);\n  stf_cuda_kernel_add_dep(k, lY, STF_RW);\n  stf_cuda_kernel_start(k);\n  double* dX          = (double*) stf_cuda_kernel_get_arg(k, 0);\n  double* dY          = (double*) stf_cuda_kernel_get_arg(k, 1);\n  const void* args[4] = {&N, &alpha, &dX, &dY};\n  hipError_t err     = stf_cuda_kernel_add_desc(k, (void*) axpy, 2, 4, 0, 4, args);\n  REQUIRE(err == hipSuccess);\n  stf_cuda_kernel_end(k);\n  stf_cuda_kernel_destroy(k);\n\n  stf_logical_data_destroy(lX);\n  stf_logical_data_destroy(lY);\n\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/c/experimental/stf/test/test_cuda_kernel.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cmath>\n\n#include <cuda_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void fill_kernel(int cnt, double* data, double value)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < cnt; i += nthreads)\n  {\n    data[i] = value + i;\n  }\n}\n\nstruct verify_args\n{\n  size_t N;\n  bool* passed;\n};\n\nstatic void verify_callback(stf_host_launch_deps_handle deps)\n{\n  auto* v = static_cast<verify_args*>(stf_host_launch_deps_get_user_data(deps));\n\n  if (stf_host_launch_deps_size(deps) != 1)\n  {\n    *v->passed = false;\n    return;\n  }\n\n  if (stf_host_launch_deps_get_size(deps, 0) != v->N * sizeof(double))\n  {\n    *v->passed = false;\n    return;\n  }\n\n  auto* data = static_cast<double*>(stf_host_launch_deps_get(deps, 0));\n  for (size_t i = 0; i < v->N; i++)\n  {\n    if (fabs(data[i] - (42.0 + i)) > 1e-10)\n    {\n      *v->passed = false;\n      return;\n    }\n  }\n  *v->passed = true;\n}\n\nC2H_TEST(\"host_launch with stream context\", \"[host_launch]\")\n{\n  const size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  double* host_data;\n  cudaMallocHost(&host_data, N * sizeof(double));\n  for (size_t i = 0; i < N; i++)\n  {\n    host_data[i] = 0.0;\n  }\n\n  stf_logical_data_handle lData = stf_logical_data(ctx, host_data, N * sizeof(double));\n  REQUIRE(lData != nullptr);\n  stf_logical_data_set_symbol(lData, \"data\");\n\n  // Fill data via a kernel task\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_set_symbol(t, \"fill\");\n  stf_task_add_dep(t, lData, STF_WRITE);\n  stf_task_start(t);\n  double* dData = (double*) stf_task_get(t, 0);\n  fill_kernel<<<2, 128, 0, (cudaStream_t) stf_task_get_custream(t)>>>((int) N, dData, 42.0);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  // Use host_launch to verify data on the host\n  bool passed = false;\n  verify_args vargs{N, &passed};\n\n  stf_host_launch_handle h = stf_host_launch_create(ctx);\n  REQUIRE(h != nullptr);\n  stf_host_launch_set_symbol(h, \"verify\");\n  stf_host_launch_add_dep(h, lData, STF_READ);\n  stf_host_launch_set_user_data(h, &vargs, sizeof(vargs), nullptr);\n  stf_host_launch_submit(h, verify_callback);\n  stf_host_launch_destroy(h);\n\n  stf_logical_data_destroy(lData);\n  stf_ctx_finalize(ctx);\n\n  REQUIRE(passed);\n\n  cudaFreeHost(host_data);\n}\n\nC2H_TEST(\"host_launch with graph context\", \"[host_launch]\")\n{\n  const size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create_graph();\n  REQUIRE(ctx != nullptr);\n\n  double* host_data;\n  cudaMallocHost(&host_data, N * sizeof(double));\n  for (size_t i = 0; i < N; i++)\n  {\n    host_data[i] = 0.0;\n  }\n\n  stf_logical_data_handle lData = stf_logical_data(ctx, host_data, N * sizeof(double));\n  REQUIRE(lData != nullptr);\n  stf_logical_data_set_symbol(lData, \"data\");\n\n  // Fill data via a generic task with stream capture\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_set_symbol(t, \"fill\");\n  stf_task_add_dep(t, lData, STF_WRITE);\n  stf_task_enable_capture(t);\n  stf_task_start(t);\n  double* dData       = (double*) stf_task_get(t, 0);\n  cudaStream_t stream = (cudaStream_t) stf_task_get_custream(t);\n  fill_kernel<<<2, 128, 0, stream>>>((int) N, dData, 42.0);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  // Use host_launch to verify data on the host\n  bool passed = false;\n  verify_args vargs{N, &passed};\n\n  stf_host_launch_handle h = stf_host_launch_create(ctx);\n  REQUIRE(h != nullptr);\n  stf_host_launch_set_symbol(h, \"verify\");\n  stf_host_launch_add_dep(h, lData, STF_READ);\n  stf_host_launch_set_user_data(h, &vargs, sizeof(vargs), nullptr);\n  stf_host_launch_submit(h, verify_callback);\n  stf_host_launch_destroy(h);\n\n  stf_logical_data_destroy(lData);\n  stf_ctx_finalize(ctx);\n\n  REQUIRE(passed);\n\n  cudaFreeHost(host_data);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cmath>\n\n#include <hip/hip_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void fill_kernel(int cnt, double* data, double value)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < cnt; i += nthreads)\n  {\n    data[i] = value + i;\n  }\n}\n\nstruct verify_args\n{\n  size_t N;\n  bool* passed;\n};\n\nstatic void verify_callback(stf_host_launch_deps_handle deps)\n{\n  auto* v = static_cast<verify_args*>(stf_host_launch_deps_get_user_data(deps));\n\n  if (stf_host_launch_deps_size(deps) != 1)\n  {\n    *v->passed = false;\n    return;\n  }\n\n  if (stf_host_launch_deps_get_size(deps, 0) != v->N * sizeof(double))\n  {\n    *v->passed = false;\n    return;\n  }\n\n  auto* data = static_cast<double*>(stf_host_launch_deps_get(deps, 0));\n  for (size_t i = 0; i < v->N; i++)\n  {\n    if (fabs(data[i] - (42.0 + i)) > 1e-10)\n    {\n      *v->passed = false;\n      return;\n    }\n  }\n  *v->passed = true;\n}\n\nC2H_TEST(\"host_launch with stream context\", \"[host_launch]\")\n{\n  const size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  double* host_data;\n  hipHostMalloc(&host_data, N * sizeof(double));\n  for (size_t i = 0; i < N; i++)\n  {\n    host_data[i] = 0.0;\n  }\n\n  stf_logical_data_handle lData = stf_logical_data(ctx, host_data, N * sizeof(double));\n  REQUIRE(lData != nullptr);\n  stf_logical_data_set_symbol(lData, \"data\");\n\n  // Fill data via a kernel task\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_set_symbol(t, \"fill\");\n  stf_task_add_dep(t, lData, STF_WRITE);\n  stf_task_start(t);\n  double* dData = (double*) stf_task_get(t, 0);\n  fill_kernel<<<2, 128, 0, (hipStream_t) stf_task_get_custream(t)>>>((int) N, dData, 42.0);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  // Use host_launch to verify data on the host\n  bool passed = false;\n  verify_args vargs{N, &passed};\n\n  stf_host_launch_handle h = stf_host_launch_create(ctx);\n  REQUIRE(h != nullptr);\n  stf_host_launch_set_symbol(h, \"verify\");\n  stf_host_launch_add_dep(h, lData, STF_READ);\n  stf_host_launch_set_user_data(h, &vargs, sizeof(vargs), nullptr);\n  stf_host_launch_submit(h, verify_callback);\n  stf_host_launch_destroy(h);\n\n  stf_logical_data_destroy(lData);\n  stf_ctx_finalize(ctx);\n\n  REQUIRE(passed);\n\n  hipHostFree(host_data);\n}\n\nC2H_TEST(\"host_launch with graph context\", \"[host_launch]\")\n{\n  const size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create_graph();\n  REQUIRE(ctx != nullptr);\n\n  double* host_data;\n  hipHostMalloc(&host_data, N * sizeof(double));\n  for (size_t i = 0; i < N; i++)\n  {\n    host_data[i] = 0.0;\n  }\n\n  stf_logical_data_handle lData = stf_logical_data(ctx, host_data, N * sizeof(double));\n  REQUIRE(lData != nullptr);\n  stf_logical_data_set_symbol(lData, \"data\");\n\n  // Fill data via a generic task with stream capture\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_set_symbol(t, \"fill\");\n  stf_task_add_dep(t, lData, STF_WRITE);\n  stf_task_enable_capture(t);\n  stf_task_start(t);\n  double* dData       = (double*) stf_task_get(t, 0);\n  hipStream_t stream = (hipStream_t) stf_task_get_custream(t);\n  fill_kernel<<<2, 128, 0, stream>>>((int) N, dData, 42.0);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  // Use host_launch to verify data on the host\n  bool passed = false;\n  verify_args vargs{N, &passed};\n\n  stf_host_launch_handle h = stf_host_launch_create(ctx);\n  REQUIRE(h != nullptr);\n  stf_host_launch_set_symbol(h, \"verify\");\n  stf_host_launch_add_dep(h, lData, STF_READ);\n  stf_host_launch_set_user_data(h, &vargs, sizeof(vargs), nullptr);\n  stf_host_launch_submit(h, verify_callback);\n  stf_host_launch_destroy(h);\n\n  stf_logical_data_destroy(lData);\n  stf_ctx_finalize(ctx);\n\n  REQUIRE(passed);\n\n  hipHostFree(host_data);\n}\n", "source": "/kaggle/working/cccl/c/experimental/stf/test/test_host_launch.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n//\n// Unit tests for stf_logical_data_with_place(): logical data with explicit\n// data place (host, pinned host, device).\n//\n//===----------------------------------------------------------------------===//\n\n#include <memory>\n#include <vector>\n\n#include <cuda_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void scale_inplace(int n, float* data, float factor)\n{\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n  {\n    data[i] *= factor;\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - host place (malloc)\", \"[logical_data_with_place]\")\n{\n  size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  std::vector<float> A(N);\n  for (size_t i = 0; i < N; ++i)\n  {\n    A[i] = static_cast<float>(i);\n  }\n\n  stf_data_place_handle host_place = stf_data_place_host();\n  stf_logical_data_handle lA       = stf_logical_data_with_place(ctx, A.data(), N * sizeof(float), host_place);\n  REQUIRE(lA != nullptr);\n  stf_data_place_destroy(host_place);\n\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_add_dep(t, lA, STF_RW);\n  stf_task_start(t);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  stf_logical_data_destroy(lA);\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(A[i] == static_cast<float>(i));\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - host place (pinned memory)\", \"[logical_data_with_place]\")\n{\n  size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  float* A_raw    = nullptr;\n  cudaError_t err = cudaMallocHost(&A_raw, N * sizeof(float));\n  REQUIRE(err == cudaSuccess);\n  std::unique_ptr<void, decltype(&cudaFreeHost)> A_owner(A_raw, cudaFreeHost);\n  float* A = static_cast<float*>(A_owner.get());\n  for (size_t i = 0; i < N; ++i)\n  {\n    A[i] = static_cast<float>(i);\n  }\n\n  stf_data_place_handle host_place = stf_data_place_host();\n  stf_logical_data_handle lA       = stf_logical_data_with_place(ctx, A, N * sizeof(float), host_place);\n  REQUIRE(lA != nullptr);\n  stf_data_place_destroy(host_place);\n\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_add_dep(t, lA, STF_RW);\n  stf_task_start(t);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  stf_logical_data_destroy(lA);\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(A[i] == static_cast<float>(i));\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - device place (data on current device)\", \"[logical_data_with_place]\")\n{\n  size_t N           = 1024;\n  const float factor = 2.0f;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  float* d_raw    = nullptr;\n  cudaError_t err = cudaMalloc(&d_raw, N * sizeof(float));\n  REQUIRE(err == cudaSuccess);\n  std::unique_ptr<void, decltype(&cudaFree)> d_data_owner(d_raw, cudaFree);\n  float* d_data = static_cast<float*>(d_data_owner.get());\n\n  std::vector<float> h_init(N);\n  for (size_t i = 0; i < N; ++i)\n  {\n    h_init[i] = static_cast<float>(i);\n  }\n  err = cudaMemcpy(d_data, h_init.data(), N * sizeof(float), cudaMemcpyHostToDevice);\n  REQUIRE(err == cudaSuccess);\n\n  stf_data_place_handle dev_place = stf_data_place_device(0);\n  stf_logical_data_handle lD      = stf_logical_data_with_place(ctx, d_data, N * sizeof(float), dev_place);\n  REQUIRE(lD != nullptr);\n  stf_data_place_destroy(dev_place);\n  stf_logical_data_set_symbol(lD, \"device_buf\");\n\n  stf_cuda_kernel_handle k = stf_cuda_kernel_create(ctx);\n  REQUIRE(k != nullptr);\n  stf_cuda_kernel_set_symbol(k, \"scale_inplace\");\n  stf_cuda_kernel_add_dep(k, lD, STF_RW);\n  stf_cuda_kernel_start(k);\n  float* arg_ptr = static_cast<float*>(stf_cuda_kernel_get_arg(k, 0));\n  REQUIRE(arg_ptr == d_data);\n  int n               = static_cast<int>(N);\n  const void* args[3] = {&n, &arg_ptr, &factor};\n  dim3 grid(4);\n  dim3 block(256);\n  err = stf_cuda_kernel_add_desc(k, reinterpret_cast<void*>(scale_inplace), grid, block, 0, 3, args);\n  REQUIRE(err == cudaSuccess);\n  stf_cuda_kernel_end(k);\n  stf_cuda_kernel_destroy(k);\n\n  stf_logical_data_destroy(lD);\n  stf_ctx_finalize(ctx);\n\n  // Copy back and verify: should be i * factor\n  std::vector<float> h_result(N);\n  err = cudaMemcpy(h_result.data(), d_data, N * sizeof(float), cudaMemcpyDeviceToHost);\n  REQUIRE(err == cudaSuccess);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(h_result[i] == static_cast<float>(i) * factor);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n//\n// Unit tests for stf_logical_data_with_place(): logical data with explicit\n// data place (host, pinned host, device).\n//\n//===----------------------------------------------------------------------===//\n\n#include <memory>\n#include <vector>\n\n#include <hip/hip_runtime.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <cccl/c/experimental/stf/stf.h>\n\n__global__ void scale_inplace(int n, float* data, float factor)\n{\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n  {\n    data[i] *= factor;\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - host place (malloc)\", \"[logical_data_with_place]\")\n{\n  size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  std::vector<float> A(N);\n  for (size_t i = 0; i < N; ++i)\n  {\n    A[i] = static_cast<float>(i);\n  }\n\n  stf_data_place_handle host_place = stf_data_place_host();\n  stf_logical_data_handle lA       = stf_logical_data_with_place(ctx, A.data(), N * sizeof(float), host_place);\n  REQUIRE(lA != nullptr);\n  stf_data_place_destroy(host_place);\n\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_add_dep(t, lA, STF_RW);\n  stf_task_start(t);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  stf_logical_data_destroy(lA);\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(A[i] == static_cast<float>(i));\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - host place (pinned memory)\", \"[logical_data_with_place]\")\n{\n  size_t N = 1024;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  float* A_raw    = nullptr;\n  hipError_t err = hipHostMalloc(&A_raw, N * sizeof(float));\n  REQUIRE(err == hipSuccess);\n  std::unique_ptr<void, decltype(&hipHostFree)> A_owner(A_raw, hipHostFree);\n  float* A = static_cast<float*>(A_owner.get());\n  for (size_t i = 0; i < N; ++i)\n  {\n    A[i] = static_cast<float>(i);\n  }\n\n  stf_data_place_handle host_place = stf_data_place_host();\n  stf_logical_data_handle lA       = stf_logical_data_with_place(ctx, A, N * sizeof(float), host_place);\n  REQUIRE(lA != nullptr);\n  stf_data_place_destroy(host_place);\n\n  stf_task_handle t = stf_task_create(ctx);\n  REQUIRE(t != nullptr);\n  stf_task_add_dep(t, lA, STF_RW);\n  stf_task_start(t);\n  stf_task_end(t);\n  stf_task_destroy(t);\n\n  stf_logical_data_destroy(lA);\n  stf_ctx_finalize(ctx);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(A[i] == static_cast<float>(i));\n  }\n}\n\nC2H_TEST(\"stf_logical_data_with_place - device place (data on current device)\", \"[logical_data_with_place]\")\n{\n  size_t N           = 1024;\n  const float factor = 2.0f;\n\n  stf_ctx_handle ctx = stf_ctx_create();\n  REQUIRE(ctx != nullptr);\n\n  float* d_raw    = nullptr;\n  hipError_t err = hipMalloc(&d_raw, N * sizeof(float));\n  REQUIRE(err == hipSuccess);\n  std::unique_ptr<void, decltype(&hipFree)> d_data_owner(d_raw, hipFree);\n  float* d_data = static_cast<float*>(d_data_owner.get());\n\n  std::vector<float> h_init(N);\n  for (size_t i = 0; i < N; ++i)\n  {\n    h_init[i] = static_cast<float>(i);\n  }\n  err = hipMemcpy(d_data, h_init.data(), N * sizeof(float), hipMemcpyHostToDevice);\n  REQUIRE(err == hipSuccess);\n\n  stf_data_place_handle dev_place = stf_data_place_device(0);\n  stf_logical_data_handle lD      = stf_logical_data_with_place(ctx, d_data, N * sizeof(float), dev_place);\n  REQUIRE(lD != nullptr);\n  stf_data_place_destroy(dev_place);\n  stf_logical_data_set_symbol(lD, \"device_buf\");\n\n  stf_cuda_kernel_handle k = stf_cuda_kernel_create(ctx);\n  REQUIRE(k != nullptr);\n  stf_cuda_kernel_set_symbol(k, \"scale_inplace\");\n  stf_cuda_kernel_add_dep(k, lD, STF_RW);\n  stf_cuda_kernel_start(k);\n  float* arg_ptr = static_cast<float*>(stf_cuda_kernel_get_arg(k, 0));\n  REQUIRE(arg_ptr == d_data);\n  int n               = static_cast<int>(N);\n  const void* args[3] = {&n, &arg_ptr, &factor};\n  dim3 grid(4);\n  dim3 block(256);\n  err = stf_cuda_kernel_add_desc(k, reinterpret_cast<void*>(scale_inplace), grid, block, 0, 3, args);\n  REQUIRE(err == hipSuccess);\n  stf_cuda_kernel_end(k);\n  stf_cuda_kernel_destroy(k);\n\n  stf_logical_data_destroy(lD);\n  stf_ctx_finalize(ctx);\n\n  // Copy back and verify: should be i * factor\n  std::vector<float> h_result(N);\n  err = hipMemcpy(h_result.data(), d_data, N * sizeof(float), hipMemcpyDeviceToHost);\n  REQUIRE(err == hipSuccess);\n\n  for (size_t i = 0; i < N; ++i)\n  {\n    REQUIRE(h_result[i] == static_cast<float>(i) * factor);\n  }\n}\n", "source": "/kaggle/working/cccl/c/experimental/stf/test/test_logical_data_with_place.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockRadixSort\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_radix_sort.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_sort.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n/// Uniform key samples\nbool g_uniform_keys;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide sorting over integers\n */\ntemplate <typename Key,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD>\n__launch_bounds__(BLOCK_THREADS) __global__\n  void BlockSortKernel(Key* d_in, // Tile of input\n                       Key* d_out, // Tile of output\n                       clock_t* d_elapsed) // Elapsed cycle count of block scan\n{\n  static constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockLoadT = BlockLoad<Key, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE>;\n\n  // Specialize BlockRadixSort type for our thread block\n  using BlockRadixSortT = BlockRadixSort<Key, BLOCK_THREADS, ITEMS_PER_THREAD>;\n\n  // Shared memory\n  __shared__ union TempStorage\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockRadixSortT::TempStorage sort;\n  } temp_storage;\n\n  // Per-thread tile items\n  Key items[ITEMS_PER_THREAD];\n\n  // Our current block's offset\n  int block_offset = blockIdx.x * TILE_SIZE;\n\n  // Load items into a blocked arrangement\n  BlockLoadT(temp_storage.load).Load(d_in + block_offset, items);\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Sort keys\n  BlockRadixSortT(temp_storage.sort).SortBlockedToStriped(items);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Store output in striped fashion\n  StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items);\n\n  // Store elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    d_elapsed[blockIdx.x] = (start > stop) ? start - stop : stop - start;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize sorting problem (and solution).\n */\ntemplate <typename Key>\nvoid Initialize(Key* h_in, Key* h_reference, int num_items, int tile_size)\n{\n  for (int i = 0; i < num_items; ++i)\n  {\n    if (g_uniform_keys)\n    {\n      h_in[i] = 0;\n    }\n    else\n    {\n      RandomBits(h_in[i]);\n    }\n    h_reference[i] = h_in[i];\n  }\n\n  // Only sort the first tile\n  std::sort(h_reference, h_reference + tile_size);\n}\n\n/**\n * Test BlockScan\n */\ntemplate <typename Key, int BLOCK_THREADS, int ITEMS_PER_THREAD>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  Key* h_in          = new Key[TILE_SIZE * g_grid_size];\n  Key* h_reference   = new Key[TILE_SIZE * g_grid_size];\n  clock_t* h_elapsed = new clock_t[g_grid_size];\n\n  // Initialize problem and reference output on host\n  Initialize(h_in, h_reference, TILE_SIZE * g_grid_size, TILE_SIZE);\n\n  // Initialize device arrays\n  Key* d_in          = nullptr;\n  Key* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  CubDebugExit(cudaMalloc((void**) &d_in, sizeof(Key) * TILE_SIZE * g_grid_size));\n  CubDebugExit(cudaMalloc((void**) &d_out, sizeof(Key) * TILE_SIZE * g_grid_size));\n  CubDebugExit(cudaMalloc((void**) &d_elapsed, sizeof(clock_t) * g_grid_size));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      std::cout << h_in[i] << \", \";\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD>, BLOCK_THREADS));\n\n  // Copy problem to device\n  CubDebugExit(cudaMemcpy(d_in, h_in, sizeof(Key) * TILE_SIZE * g_grid_size, cudaMemcpyHostToDevice));\n\n  printf(\n    \"BlockRadixSort %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n    TILE_SIZE * g_grid_size,\n    g_timing_iterations,\n    g_grid_size,\n    BLOCK_THREADS,\n    ITEMS_PER_THREAD,\n    max_sm_occupancy);\n  fflush(stdout);\n\n  // Run kernel once to prime caches and check result\n  BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Check results\n  printf(\"\\tOutput items: \");\n  int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n  fflush(stdout);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis              = 0.0;\n  unsigned long long elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    timer.Start();\n\n    // Run kernel\n    BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    CubDebugExit(cudaMemcpy(h_elapsed, d_elapsed, sizeof(clock_t) * g_grid_size, cudaMemcpyDeviceToHost));\n    for (int j = 0; j < g_grid_size; j++)\n    {\n      elapsed_clocks += h_elapsed[j];\n    }\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis           = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec    = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  double avg_clocks          = double(elapsed_clocks) / g_timing_iterations / g_grid_size;\n  double avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockRadixSort::SortBlocked clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockRadixSort::SortBlocked clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n  fflush(stdout);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_reference)\n  {\n    delete[] h_reference;\n  }\n  if (h_elapsed)\n  {\n    delete[] h_elapsed;\n  }\n  if (d_in)\n  {\n    CubDebugExit(cudaFree(d_in));\n  }\n  if (d_out)\n  {\n    CubDebugExit(cudaFree(d_out));\n  }\n  if (d_elapsed)\n  {\n    CubDebugExit(cudaFree(d_elapsed));\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose      = args.CheckCmdLineFlag(\"v\");\n  g_uniform_keys = args.CheckCmdLineFlag(\"uniform\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations (default:%d)>]\"\n           \"[--grid-size=<grid size (default:%d)>]\"\n           \"[--v] \"\n           \"\\n\",\n           argv[0],\n           g_timing_iterations,\n           g_grid_size);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n  fflush(stdout);\n\n  // Run tests\n  printf(\"\\nuint32:\\n\");\n  fflush(stdout);\n  Test<unsigned int, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  printf(\"\\nfp32:\\n\");\n  fflush(stdout);\n  Test<float, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  printf(\"\\nuint8:\\n\");\n  fflush(stdout);\n  Test<unsigned char, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockRadixSort\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_radix_sort.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_sort.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n/// Uniform key samples\nbool g_uniform_keys;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide sorting over integers\n */\ntemplate <typename Key,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD>\n__launch_bounds__(BLOCK_THREADS) __global__\n  void BlockSortKernel(Key* d_in, // Tile of input\n                       Key* d_out, // Tile of output\n                       clock_t* d_elapsed) // Elapsed cycle count of block scan\n{\n  static constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockLoadT = BlockLoad<Key, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE>;\n\n  // Specialize BlockRadixSort type for our thread block\n  using BlockRadixSortT = BlockRadixSort<Key, BLOCK_THREADS, ITEMS_PER_THREAD>;\n\n  // Shared memory\n  __shared__ union TempStorage\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockRadixSortT::TempStorage sort;\n  } temp_storage;\n\n  // Per-thread tile items\n  Key items[ITEMS_PER_THREAD];\n\n  // Our current block's offset\n  int block_offset = blockIdx.x * TILE_SIZE;\n\n  // Load items into a blocked arrangement\n  BlockLoadT(temp_storage.load).Load(d_in + block_offset, items);\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Sort keys\n  BlockRadixSortT(temp_storage.sort).SortBlockedToStriped(items);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Store output in striped fashion\n  StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items);\n\n  // Store elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    d_elapsed[blockIdx.x] = (start > stop) ? start - stop : stop - start;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize sorting problem (and solution).\n */\ntemplate <typename Key>\nvoid Initialize(Key* h_in, Key* h_reference, int num_items, int tile_size)\n{\n  for (int i = 0; i < num_items; ++i)\n  {\n    if (g_uniform_keys)\n    {\n      h_in[i] = 0;\n    }\n    else\n    {\n      RandomBits(h_in[i]);\n    }\n    h_reference[i] = h_in[i];\n  }\n\n  // Only sort the first tile\n  std::sort(h_reference, h_reference + tile_size);\n}\n\n/**\n * Test BlockScan\n */\ntemplate <typename Key, int BLOCK_THREADS, int ITEMS_PER_THREAD>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  Key* h_in          = new Key[TILE_SIZE * g_grid_size];\n  Key* h_reference   = new Key[TILE_SIZE * g_grid_size];\n  clock_t* h_elapsed = new clock_t[g_grid_size];\n\n  // Initialize problem and reference output on host\n  Initialize(h_in, h_reference, TILE_SIZE * g_grid_size, TILE_SIZE);\n\n  // Initialize device arrays\n  Key* d_in          = nullptr;\n  Key* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  CubDebugExit(hipMalloc((void**) &d_in, sizeof(Key) * TILE_SIZE * g_grid_size));\n  CubDebugExit(hipMalloc((void**) &d_out, sizeof(Key) * TILE_SIZE * g_grid_size));\n  CubDebugExit(hipMalloc((void**) &d_elapsed, sizeof(clock_t) * g_grid_size));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      std::cout << h_in[i] << \", \";\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(MaxSmOccupancy(max_sm_occupancy, BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD>, BLOCK_THREADS));\n\n  // Copy problem to device\n  CubDebugExit(hipMemcpy(d_in, h_in, sizeof(Key) * TILE_SIZE * g_grid_size, hipMemcpyHostToDevice));\n\n  printf(\n    \"BlockRadixSort %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM occupancy):\\n\",\n    TILE_SIZE * g_grid_size,\n    g_timing_iterations,\n    g_grid_size,\n    BLOCK_THREADS,\n    ITEMS_PER_THREAD,\n    max_sm_occupancy);\n  fflush(stdout);\n\n  // Run kernel once to prime caches and check result\n  BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Check results\n  printf(\"\\tOutput items: \");\n  int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n  fflush(stdout);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis              = 0.0;\n  unsigned long long elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    timer.Start();\n\n    // Run kernel\n    BlockSortKernel<Key, BLOCK_THREADS, ITEMS_PER_THREAD><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    CubDebugExit(hipMemcpy(h_elapsed, d_elapsed, sizeof(clock_t) * g_grid_size, hipMemcpyDeviceToHost));\n    for (int j = 0; j < g_grid_size; j++)\n    {\n      elapsed_clocks += h_elapsed[j];\n    }\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis           = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec    = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  double avg_clocks          = double(elapsed_clocks) / g_timing_iterations / g_grid_size;\n  double avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockRadixSort::SortBlocked clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockRadixSort::SortBlocked clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n  fflush(stdout);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_reference)\n  {\n    delete[] h_reference;\n  }\n  if (h_elapsed)\n  {\n    delete[] h_elapsed;\n  }\n  if (d_in)\n  {\n    CubDebugExit(hipFree(d_in));\n  }\n  if (d_out)\n  {\n    CubDebugExit(hipFree(d_out));\n  }\n  if (d_elapsed)\n  {\n    CubDebugExit(hipFree(d_elapsed));\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose      = args.CheckCmdLineFlag(\"v\");\n  g_uniform_keys = args.CheckCmdLineFlag(\"uniform\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations (default:%d)>]\"\n           \"[--grid-size=<grid size (default:%d)>]\"\n           \"[--v] \"\n           \"\\n\",\n           argv[0],\n           g_timing_iterations,\n           g_grid_size);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n  fflush(stdout);\n\n  // Run tests\n  printf(\"\\nuint32:\\n\");\n  fflush(stdout);\n  Test<unsigned int, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  printf(\"\\nfp32:\\n\");\n  fflush(stdout);\n  Test<float, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  printf(\"\\nuint8:\\n\");\n  fflush(stdout);\n  Test<unsigned char, 128, 13>();\n  printf(\"\\n\");\n  fflush(stdout);\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/examples/block/example_block_radix_sort.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_reduce.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          BlockReduceAlgorithm ALGORITHM>\n__global__ void BlockReduceKernel(int* d_in, // Tile of input\n                                  int* d_out, // Tile aggregate\n                                  clock_t* d_elapsed) // Elapsed cycle count of block reduction\n{\n  // Specialize BlockReduce type for our thread block\n  using BlockReduceT = BlockReduce<int, BLOCK_THREADS, ALGORITHM>;\n\n  // Shared memory\n  __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n  // Per-thread tile data\n  int data[ITEMS_PER_THREAD];\n  LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in, data);\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Compute sum\n  int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Store aggregate and elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    *d_elapsed = (start > stop) ? start - stop : stop - start;\n    *d_out     = aggregate;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD, BlockReduceAlgorithm ALGORITHM>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  int* h_in  = new int[TILE_SIZE];\n  int* h_gpu = new int[TILE_SIZE + 1];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, TILE_SIZE);\n\n  // Initialize device arrays\n  int* d_in          = nullptr;\n  int* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  cudaMalloc((void**) &d_in, sizeof(int) * TILE_SIZE);\n  cudaMalloc((void**) &d_out, sizeof(int) * 1);\n  cudaMalloc((void**) &d_elapsed, sizeof(clock_t));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(\n    MaxSmOccupancy(max_sm_occupancy, BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n  // Copy problem to device\n  cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n  printf(\"BlockReduce algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d \"\n         \"SM occupancy):\\n\",\n         (ALGORITHM == BLOCK_REDUCE_RAKING) ? \"BLOCK_REDUCE_RAKING\" : \"BLOCK_REDUCE_WARP_REDUCTIONS\",\n         TILE_SIZE,\n         g_timing_iterations,\n         g_grid_size,\n         BLOCK_THREADS,\n         ITEMS_PER_THREAD,\n         max_sm_occupancy);\n\n  // Run kernel\n  BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  int compare = CompareDeviceResults(&h_aggregate, d_out, 1, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis   = 0.0;\n  clock_t elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n    timer.Start();\n\n    // Run kernel\n    BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n      <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    clock_t clocks;\n    CubDebugExit(cudaMemcpy(&clocks, d_elapsed, sizeof(clock_t), cudaMemcpyDeviceToHost));\n    elapsed_clocks += clocks;\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis          = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec   = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  float avg_clocks          = float(elapsed_clocks) / g_timing_iterations;\n  float avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockReduce::Sum clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockReduce::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_gpu)\n  {\n    delete[] h_gpu;\n  }\n  if (d_in)\n  {\n    cudaFree(d_in);\n  }\n  if (d_out)\n  {\n    cudaFree(d_out);\n  }\n  if (d_elapsed)\n  {\n    cudaFree(d_elapsed);\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations>] \"\n           \"[--grid-size=<grid size>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Run tests\n  Test<1024, 1, BLOCK_REDUCE_RAKING>();\n  Test<512, 2, BLOCK_REDUCE_RAKING>();\n  Test<256, 4, BLOCK_REDUCE_RAKING>();\n  Test<128, 8, BLOCK_REDUCE_RAKING>();\n  Test<64, 16, BLOCK_REDUCE_RAKING>();\n  Test<32, 32, BLOCK_REDUCE_RAKING>();\n  Test<16, 64, BLOCK_REDUCE_RAKING>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<512, 2, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<256, 4, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<128, 8, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<64, 16, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<32, 32, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<16, 64, BLOCK_REDUCE_WARP_REDUCTIONS>();\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_reduce.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          BlockReduceAlgorithm ALGORITHM>\n__global__ void BlockReduceKernel(int* d_in, // Tile of input\n                                  int* d_out, // Tile aggregate\n                                  clock_t* d_elapsed) // Elapsed cycle count of block reduction\n{\n  // Specialize BlockReduce type for our thread block\n  using BlockReduceT = BlockReduce<int, BLOCK_THREADS, ALGORITHM>;\n\n  // Shared memory\n  __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n  // Per-thread tile data\n  int data[ITEMS_PER_THREAD];\n  LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in, data);\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Compute sum\n  int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Store aggregate and elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    *d_elapsed = (start > stop) ? start - stop : stop - start;\n    *d_out     = aggregate;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD, BlockReduceAlgorithm ALGORITHM>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  int* h_in  = new int[TILE_SIZE];\n  int* h_gpu = new int[TILE_SIZE + 1];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, TILE_SIZE);\n\n  // Initialize device arrays\n  int* d_in          = nullptr;\n  int* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  hipMalloc((void**) &d_in, sizeof(int) * TILE_SIZE);\n  hipMalloc((void**) &d_out, sizeof(int) * 1);\n  hipMalloc((void**) &d_elapsed, sizeof(clock_t));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(\n    MaxSmOccupancy(max_sm_occupancy, BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n  // Copy problem to device\n  hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n  printf(\"BlockReduce algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d \"\n         \"SM occupancy):\\n\",\n         (ALGORITHM == BLOCK_REDUCE_RAKING) ? \"BLOCK_REDUCE_RAKING\" : \"BLOCK_REDUCE_WARP_REDUCTIONS\",\n         TILE_SIZE,\n         g_timing_iterations,\n         g_grid_size,\n         BLOCK_THREADS,\n         ITEMS_PER_THREAD,\n         max_sm_occupancy);\n\n  // Run kernel\n  BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM><<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  int compare = CompareDeviceResults(&h_aggregate, d_out, 1, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis   = 0.0;\n  clock_t elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n    timer.Start();\n\n    // Run kernel\n    BlockReduceKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n      <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    clock_t clocks;\n    CubDebugExit(hipMemcpy(&clocks, d_elapsed, sizeof(clock_t), hipMemcpyDeviceToHost));\n    elapsed_clocks += clocks;\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis          = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec   = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  float avg_clocks          = float(elapsed_clocks) / g_timing_iterations;\n  float avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockReduce::Sum clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockReduce::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_gpu)\n  {\n    delete[] h_gpu;\n  }\n  if (d_in)\n  {\n    hipFree(d_in);\n  }\n  if (d_out)\n  {\n    hipFree(d_out);\n  }\n  if (d_elapsed)\n  {\n    hipFree(d_elapsed);\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations>] \"\n           \"[--grid-size=<grid size>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Run tests\n  Test<1024, 1, BLOCK_REDUCE_RAKING>();\n  Test<512, 2, BLOCK_REDUCE_RAKING>();\n  Test<256, 4, BLOCK_REDUCE_RAKING>();\n  Test<128, 8, BLOCK_REDUCE_RAKING>();\n  Test<64, 16, BLOCK_REDUCE_RAKING>();\n  Test<32, 32, BLOCK_REDUCE_RAKING>();\n  Test<16, 64, BLOCK_REDUCE_RAKING>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<512, 2, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<256, 4, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<128, 8, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<64, 16, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<32, 32, BLOCK_REDUCE_WARP_REDUCTIONS>();\n  Test<16, 64, BLOCK_REDUCE_WARP_REDUCTIONS>();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/examples/block/example_block_reduce.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_scan.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_scan.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          BlockScanAlgorithm ALGORITHM>\n__global__ void BlockPrefixSumKernel(int* d_in, // Tile of input\n                                     int* d_out, // Tile of output\n                                     clock_t* d_elapsed) // Elapsed cycle count of block scan\n{\n  // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockLoadT = BlockLoad<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE>;\n\n  // Specialize BlockStore type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockStoreT = BlockStore<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_STORE_WARP_TRANSPOSE>;\n\n  // Specialize BlockScan type for our thread block\n  using BlockScanT = BlockScan<int, BLOCK_THREADS, ALGORITHM>;\n\n  // Shared memory\n  __shared__ union TempStorage\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockStoreT::TempStorage store;\n    typename BlockScanT::TempStorage scan;\n  } temp_storage;\n\n  // Per-thread tile data\n  int data[ITEMS_PER_THREAD];\n\n  // Load items into a blocked arrangement\n  BlockLoadT(temp_storage.load).Load(d_in, data);\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Compute exclusive prefix sum\n  int aggregate;\n  BlockScanT(temp_storage.scan).ExclusiveSum(data, data, aggregate);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Store items from a blocked arrangement\n  BlockStoreT(temp_storage.store).Store(d_out, data);\n\n  // Store aggregate and elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    *d_elapsed                              = (start > stop) ? start - stop : stop - start;\n    d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int* h_reference, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n\n    h_reference[i] = inclusive;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block scan\n */\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD, BlockScanAlgorithm ALGORITHM>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  int* h_in        = new int[TILE_SIZE];\n  int* h_reference = new int[TILE_SIZE];\n  int* h_gpu       = new int[TILE_SIZE + 1];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n  // Initialize device arrays\n  int* d_in          = nullptr;\n  int* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  cudaMalloc((void**) &d_in, sizeof(int) * TILE_SIZE);\n  cudaMalloc((void**) &d_out, sizeof(int) * (TILE_SIZE + 1));\n  cudaMalloc((void**) &d_elapsed, sizeof(clock_t));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(\n    MaxSmOccupancy(max_sm_occupancy, BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n  // Copy problem to device\n  cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n  printf(\n    \"BlockScan algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM \"\n    \"occupancy):\\n\",\n    (ALGORITHM == BLOCK_SCAN_RAKING) ? \"BLOCK_SCAN_RAKING\"\n    : (ALGORITHM == BLOCK_SCAN_RAKING_MEMOIZE)\n      ? \"BLOCK_SCAN_RAKING_MEMOIZE\"\n      : \"BLOCK_SCAN_WARP_SCANS\",\n    TILE_SIZE,\n    g_timing_iterations,\n    g_grid_size,\n    BLOCK_THREADS,\n    ITEMS_PER_THREAD,\n    max_sm_occupancy);\n\n  // Run aggregate/prefix kernel\n  BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n    <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check results\n  printf(\"\\tOutput items: \");\n  int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  compare = CompareDeviceResults(&h_aggregate, d_out + TILE_SIZE, 1, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis   = 0.0;\n  clock_t elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    // Copy problem to device\n    cudaMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, cudaMemcpyHostToDevice);\n\n    timer.Start();\n\n    // Run aggregate/prefix kernel\n    BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n      <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    clock_t clocks;\n    CubDebugExit(cudaMemcpy(&clocks, d_elapsed, sizeof(clock_t), cudaMemcpyDeviceToHost));\n    elapsed_clocks += clocks;\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis          = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec   = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  float avg_clocks          = float(elapsed_clocks) / g_timing_iterations;\n  float avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockScan::Sum clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockScan::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_reference)\n  {\n    delete[] h_reference;\n  }\n  if (h_gpu)\n  {\n    delete[] h_gpu;\n  }\n  if (d_in)\n  {\n    cudaFree(d_in);\n  }\n  if (d_out)\n  {\n    cudaFree(d_out);\n  }\n  if (d_elapsed)\n  {\n    cudaFree(d_elapsed);\n  }\n}\n\n//---------------------------------------------------------------------\n// Documentation examples\n//---------------------------------------------------------------------\n\n// example-begin exclusive-sum-array\n__global__ void ExclusiveSumArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix sum\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-sum-array\n\n// example-begin exclusive-sum-single\n__global__ void ExclusiveSumSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix sum\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-sum-single\n\n// example-begin exclusive-sum-aggregate\n__global__ void ExclusiveSumAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-sum-aggregate\n\n// example-begin block-prefix-callback-op\n// A stateful callback functor that maintains a running prefix to be applied\n// during consecutive scan operations.\nstruct BlockPrefixCallbackOp\n{\n  // Running prefix\n  int running_total;\n\n  // Constructor\n  __device__ BlockPrefixCallbackOp(int running_total)\n      : running_total(running_total)\n  {}\n\n  // Callback operator to be entered by the first warp of threads in the block.\n  // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n  __device__ int operator()(int block_aggregate)\n  {\n    int old_prefix = running_total;\n    running_total += block_aggregate;\n    return old_prefix;\n  }\n};\n// example-end block-prefix-callback-op\n\n// example-begin exclusive-sum-prefix-callback\n__global__ void ExclusiveSumPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide exclusive prefix sum\n    BlockScanT(temp_storage.scan).ExclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end exclusive-sum-prefix-callback\n\n// example-begin exclusive-scan-single\n__global__ void ExclusiveScanSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{});\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-scan-single\n\n// example-begin inclusive-scan-prefix-callback\n__global__ void InclusiveSumPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide inclusive prefix sum\n    BlockScanT(temp_storage.scan).InclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end inclusive-scan-prefix-callback\n\n// example-begin inclusive-sum-array\n__global__ void InclusiveSumArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix sum\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-sum-array\n\n// example-begin inclusive-sum-single\n__global__ void InclusiveSumSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix sum\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-sum-single\n\n// example-begin exclusive-scan-array\n__global__ void ExclusiveScanArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{});\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-scan-array\n\n// example-begin exclusive-scan-aggregate\n__global__ void ExclusiveScanAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{}, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-scan-aggregate\n\n// example-begin block-prefix-callback-max-op\n// A stateful callback functor that maintains a running prefix to be applied\n// during consecutive scan operations.\nstruct BlockPrefixCallbackMaxOp\n{\n  // Running prefix\n  int running_total;\n\n  // Constructor\n  __device__ BlockPrefixCallbackMaxOp(int running_total)\n      : running_total(running_total)\n  {}\n\n  // Callback operator to be entered by the first warp of threads in the block.\n  // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n  __device__ int operator()(int block_aggregate)\n  {\n    int old_prefix = running_total;\n    running_total  = (block_aggregate > old_prefix) ? block_aggregate : old_prefix;\n    return old_prefix;\n  }\n};\n// example-end block-prefix-callback-max-op\n\n// example-begin exclusive-scan-prefix-callback\n__global__ void ExclusiveScanPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackMaxOp prefix_op(INT_MIN);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide exclusive prefix max scan\n    BlockScanT(temp_storage.scan).ExclusiveScan(thread_data, thread_data, cuda::maximum<>{}, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end exclusive-scan-prefix-callback\n\n// example-begin exclusive-sum-single-prefix-callback\n__global__ void ExclusiveSumSinglePrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockScan for a 1D block of 128 threads\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data = d_data[block_offset + threadIdx.x];\n\n    // Collectively compute the block-wide exclusive prefix sum\n    BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    d_data[block_offset + threadIdx.x] = thread_data;\n  }\n}\n// example-end exclusive-sum-single-prefix-callback\n\n// example-begin exclusive-sum-array-aggregate\n__global__ void ExclusiveSumArrayAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-sum-array-aggregate\n\n// example-begin inclusive-scan-array\n__global__ void InclusiveScanArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix max scan\n  BlockScan(temp_storage).InclusiveScan(thread_data, thread_data, ::cuda::maximum<>{});\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-scan-array\n\n// example-begin inclusive-scan-single\n__global__ void InclusiveScanSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix max scan\n  BlockScan(temp_storage).InclusiveScan(thread_data, thread_data, cuda::maximum<>{});\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-scan-single\n\n// example-begin inclusive-scan-prefix-callback-max\n__global__ void InclusiveScanPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackMaxOp prefix_op(INT_MIN);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide inclusive prefix max\n    BlockScanT(temp_storage.scan).InclusiveScan(thread_data, thread_data, cuda::maximum<>{}, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end inclusive-scan-prefix-callback-max\n\n// example-begin inclusive-sum-array-aggregate\n__global__ void InclusiveSumArrayAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-sum-array-aggregate\n\n// example-begin inclusive-sum-single-aggregate\n__global__ void InclusiveSumSingleAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = cub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-sum-single-aggregate\n\n/**\n * Test documentation example kernels\n */\nvoid TestDocumentationExamples()\n{\n  printf(\"Testing documentation example kernels...\\n\");\n\n  const int num_items = 128 * 4; // 512 items for array examples, 128 for single-item examples\n  int* d_data;\n  int* h_data = new int[num_items];\n  int running_max;\n  bool all_passed = true;\n\n  cudaMalloc(&d_data, num_items * sizeof(int));\n\n  // Test ExclusiveSumArrayKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveSumArrayKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int expected = i * 4 + j;\n        if (h_data[i * 4 + j] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", i * 4 + j, expected, h_data[i * 4 + j]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  // Test ExclusiveSumSingleKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, 128 * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveSumSingleKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, 128 * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  // Test ExclusiveSumAggregateKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumAggregateKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, 128 * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveSumAggregateKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, 128 * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveSumPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveSumPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      if (h_data[i] != i + 1)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i + 1, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveSumArrayKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int expected = i * 4 + j + 1;\n        if (h_data[i * 4 + j] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", i * 4 + j, expected, h_data[i * 4 + j]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    cudaMemcpy(d_data, h_data, 128 * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveSumSingleKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, 128 * sizeof(int), cudaMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i + 1)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i + 1, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = i;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveScanArrayKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int idx      = i * 4 + j;\n        int expected = running_max;\n        if (h_data[idx] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", idx, expected, h_data[idx]);\n          passed     = false;\n          all_passed = false;\n        }\n        running_max = (idx > running_max) ? idx : running_max;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanAggregateKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = i;\n    }\n    cudaMemcpy(d_data, h_data, 128 * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveScanAggregateKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, 128 * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      int expected = running_max;\n      if (h_data[i] != expected)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, expected, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n      running_max = (i > running_max) ? i : running_max;\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = (i % 2 == 0) ? i : -i;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    ExclusiveScanPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      int input_val = (i % 2 == 0) ? i : -i;\n      int expected  = running_max;\n      if (h_data[i] != expected)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, expected, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n      running_max = (input_val > running_max) ? input_val : running_max;\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = i;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveScanArrayKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int idx     = i * 4 + j;\n        running_max = (idx > running_max) ? idx : running_max;\n        if (h_data[idx] != running_max)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", idx, running_max, h_data[idx]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = i;\n    }\n    cudaMemcpy(d_data, h_data, 128 * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveScanSingleKernel<<<1, 128>>>(d_data);\n    cudaMemcpy(h_data, d_data, 128 * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      running_max = (i > running_max) ? i : running_max;\n      if (h_data[i] != running_max)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, running_max, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = (i % 2 == 0) ? i : -i;\n    }\n    cudaMemcpy(d_data, h_data, num_items * sizeof(int), cudaMemcpyHostToDevice);\n    InclusiveScanPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    cudaMemcpy(h_data, d_data, num_items * sizeof(int), cudaMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      int input_val = (i % 2 == 0) ? i : -i;\n      running_max   = (input_val > running_max) ? input_val : running_max;\n      if (h_data[i] != running_max)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, running_max, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"All documentation example tests PASSED!\\n\\n\");\n  }\n\n  delete[] h_data;\n  cudaFree(d_data);\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations (default:%d)>]\"\n           \"[--grid-size=<grid size (default:%d)>]\"\n           \"[--v] \"\n           \"\\n\",\n           argv[0],\n           g_timing_iterations,\n           g_grid_size);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Test documentation example kernels first\n  TestDocumentationExamples();\n\n  // Run tests\n  Test<1024, 1, BLOCK_SCAN_RAKING>();\n  Test<512, 2, BLOCK_SCAN_RAKING>();\n  Test<256, 4, BLOCK_SCAN_RAKING>();\n  Test<128, 8, BLOCK_SCAN_RAKING>();\n  Test<64, 16, BLOCK_SCAN_RAKING>();\n  Test<32, 32, BLOCK_SCAN_RAKING>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<512, 2, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<256, 4, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<128, 8, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<64, 16, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<32, 32, BLOCK_SCAN_RAKING_MEMOIZE>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_SCAN_WARP_SCANS>();\n  Test<512, 2, BLOCK_SCAN_WARP_SCANS>();\n  Test<256, 4, BLOCK_SCAN_WARP_SCANS>();\n  Test<128, 8, BLOCK_SCAN_WARP_SCANS>();\n  Test<64, 16, BLOCK_SCAN_WARP_SCANS>();\n  Test<32, 32, BLOCK_SCAN_WARP_SCANS>();\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockScan\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_scan.cu -I../.. -lcudart -O3\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_scan.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Timing iterations\nint g_timing_iterations = 100;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide exclusive prefix sum over integers\n */\ntemplate <int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          BlockScanAlgorithm ALGORITHM>\n__global__ void BlockPrefixSumKernel(int* d_in, // Tile of input\n                                     int* d_out, // Tile of output\n                                     clock_t* d_elapsed) // Elapsed cycle count of block scan\n{\n  // Specialize BlockLoad type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockLoadT = BlockLoad<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_WARP_TRANSPOSE>;\n\n  // Specialize BlockStore type for our thread block (uses warp-striped loads for coalescing, then transposes in shared\n  // memory to a blocked arrangement)\n  using BlockStoreT = BlockStore<int, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_STORE_WARP_TRANSPOSE>;\n\n  // Specialize BlockScan type for our thread block\n  using BlockScanT = BlockScan<int, BLOCK_THREADS, ALGORITHM>;\n\n  // Shared memory\n  __shared__ union TempStorage\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockStoreT::TempStorage store;\n    typename BlockScanT::TempStorage scan;\n  } temp_storage;\n\n  // Per-thread tile data\n  int data[ITEMS_PER_THREAD];\n\n  // Load items into a blocked arrangement\n  BlockLoadT(temp_storage.load).Load(d_in, data);\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Start cycle timer\n  clock_t start = clock();\n\n  // Compute exclusive prefix sum\n  int aggregate;\n  BlockScanT(temp_storage.scan).ExclusiveSum(data, data, aggregate);\n\n  // Stop cycle timer\n  clock_t stop = clock();\n\n  // Barrier for smem reuse\n  __syncthreads();\n\n  // Store items from a blocked arrangement\n  BlockStoreT(temp_storage.store).Store(d_out, data);\n\n  // Store aggregate and elapsed clocks\n  if (threadIdx.x == 0)\n  {\n    *d_elapsed                              = (start > stop) ? start - stop : stop - start;\n    d_out[BLOCK_THREADS * ITEMS_PER_THREAD] = aggregate;\n  }\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize exclusive prefix sum problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int* h_reference, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n\n    h_reference[i] = inclusive;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block scan\n */\ntemplate <int BLOCK_THREADS, int ITEMS_PER_THREAD, BlockScanAlgorithm ALGORITHM>\nvoid Test()\n{\n  constexpr int TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD;\n\n  // Allocate host arrays\n  int* h_in        = new int[TILE_SIZE];\n  int* h_reference = new int[TILE_SIZE];\n  int* h_gpu       = new int[TILE_SIZE + 1];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, h_reference, TILE_SIZE);\n\n  // Initialize device arrays\n  int* d_in          = nullptr;\n  int* d_out         = nullptr;\n  clock_t* d_elapsed = nullptr;\n  hipMalloc((void**) &d_in, sizeof(int) * TILE_SIZE);\n  hipMalloc((void**) &d_out, sizeof(int) * (TILE_SIZE + 1));\n  hipMalloc((void**) &d_elapsed, sizeof(clock_t));\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < TILE_SIZE; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Kernel props\n  int max_sm_occupancy;\n  CubDebugExit(\n    MaxSmOccupancy(max_sm_occupancy, BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>, BLOCK_THREADS));\n\n  // Copy problem to device\n  hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n  printf(\n    \"BlockScan algorithm %s on %d items (%d timing iterations, %d blocks, %d threads, %d items per thread, %d SM \"\n    \"occupancy):\\n\",\n    (ALGORITHM == BLOCK_SCAN_RAKING) ? \"BLOCK_SCAN_RAKING\"\n    : (ALGORITHM == BLOCK_SCAN_RAKING_MEMOIZE)\n      ? \"BLOCK_SCAN_RAKING_MEMOIZE\"\n      : \"BLOCK_SCAN_WARP_SCANS\",\n    TILE_SIZE,\n    g_timing_iterations,\n    g_grid_size,\n    BLOCK_THREADS,\n    ITEMS_PER_THREAD,\n    max_sm_occupancy);\n\n  // Run aggregate/prefix kernel\n  BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n    <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n  // Check results\n  printf(\"\\tOutput items: \");\n  int compare = CompareDeviceResults(h_reference, d_out, TILE_SIZE, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  compare = CompareDeviceResults(&h_aggregate, d_out + TILE_SIZE, 1, g_verbose, g_verbose);\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Run this several times and average the performance results\n  GpuTimer timer;\n  float elapsed_millis   = 0.0;\n  clock_t elapsed_clocks = 0;\n\n  for (int i = 0; i < g_timing_iterations; ++i)\n  {\n    // Copy problem to device\n    hipMemcpy(d_in, h_in, sizeof(int) * TILE_SIZE, hipMemcpyHostToDevice);\n\n    timer.Start();\n\n    // Run aggregate/prefix kernel\n    BlockPrefixSumKernel<BLOCK_THREADS, ITEMS_PER_THREAD, ALGORITHM>\n      <<<g_grid_size, BLOCK_THREADS>>>(d_in, d_out, d_elapsed);\n\n    timer.Stop();\n    elapsed_millis += timer.ElapsedMillis();\n\n    // Copy clocks from device\n    clock_t clocks;\n    CubDebugExit(hipMemcpy(&clocks, d_elapsed, sizeof(clock_t), hipMemcpyDeviceToHost));\n    elapsed_clocks += clocks;\n  }\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Display timing results\n  float avg_millis          = elapsed_millis / g_timing_iterations;\n  float avg_items_per_sec   = float(TILE_SIZE * g_grid_size) / avg_millis / 1000.0f;\n  float avg_clocks          = float(elapsed_clocks) / g_timing_iterations;\n  float avg_clocks_per_item = avg_clocks / TILE_SIZE;\n\n  printf(\"\\tAverage BlockScan::Sum clocks: %.3f\\n\", avg_clocks);\n  printf(\"\\tAverage BlockScan::Sum clocks per item: %.3f\\n\", avg_clocks_per_item);\n  printf(\"\\tAverage kernel millis: %.4f\\n\", avg_millis);\n  printf(\"\\tAverage million items / sec: %.4f\\n\", avg_items_per_sec);\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (h_reference)\n  {\n    delete[] h_reference;\n  }\n  if (h_gpu)\n  {\n    delete[] h_gpu;\n  }\n  if (d_in)\n  {\n    hipFree(d_in);\n  }\n  if (d_out)\n  {\n    hipFree(d_out);\n  }\n  if (d_elapsed)\n  {\n    hipFree(d_elapsed);\n  }\n}\n\n//---------------------------------------------------------------------\n// Documentation examples\n//---------------------------------------------------------------------\n\n// example-begin exclusive-sum-array\n__global__ void ExclusiveSumArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix sum\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-sum-array\n\n// example-begin exclusive-sum-single\n__global__ void ExclusiveSumSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix sum\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-sum-single\n\n// example-begin exclusive-sum-aggregate\n__global__ void ExclusiveSumAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-sum-aggregate\n\n// example-begin block-prefix-callback-op\n// A stateful callback functor that maintains a running prefix to be applied\n// during consecutive scan operations.\nstruct BlockPrefixCallbackOp\n{\n  // Running prefix\n  int running_total;\n\n  // Constructor\n  __device__ BlockPrefixCallbackOp(int running_total)\n      : running_total(running_total)\n  {}\n\n  // Callback operator to be entered by the first warp of threads in the block.\n  // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n  __device__ int operator()(int block_aggregate)\n  {\n    int old_prefix = running_total;\n    running_total += block_aggregate;\n    return old_prefix;\n  }\n};\n// example-end block-prefix-callback-op\n\n// example-begin exclusive-sum-prefix-callback\n__global__ void ExclusiveSumPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide exclusive prefix sum\n    BlockScanT(temp_storage.scan).ExclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end exclusive-sum-prefix-callback\n\n// example-begin exclusive-scan-single\n__global__ void ExclusiveScanSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{});\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-scan-single\n\n// example-begin inclusive-scan-prefix-callback\n__global__ void InclusiveSumPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide inclusive prefix sum\n    BlockScanT(temp_storage.scan).InclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end inclusive-scan-prefix-callback\n\n// example-begin inclusive-sum-array\n__global__ void InclusiveSumArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix sum\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-sum-array\n\n// example-begin inclusive-sum-single\n__global__ void InclusiveSumSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix sum\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-sum-single\n\n// example-begin exclusive-scan-array\n__global__ void ExclusiveScanArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{});\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-scan-array\n\n// example-begin exclusive-scan-aggregate\n__global__ void ExclusiveScanAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide exclusive prefix max scan\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveScan(thread_data, thread_data, INT_MIN, cuda::maximum<>{}, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end exclusive-scan-aggregate\n\n// example-begin block-prefix-callback-max-op\n// A stateful callback functor that maintains a running prefix to be applied\n// during consecutive scan operations.\nstruct BlockPrefixCallbackMaxOp\n{\n  // Running prefix\n  int running_total;\n\n  // Constructor\n  __device__ BlockPrefixCallbackMaxOp(int running_total)\n      : running_total(running_total)\n  {}\n\n  // Callback operator to be entered by the first warp of threads in the block.\n  // Thread-0 is responsible for returning a value for seeding the block-wide scan.\n  __device__ int operator()(int block_aggregate)\n  {\n    int old_prefix = running_total;\n    running_total  = (block_aggregate > old_prefix) ? block_aggregate : old_prefix;\n    return old_prefix;\n  }\n};\n// example-end block-prefix-callback-max-op\n\n// example-begin exclusive-scan-prefix-callback\n__global__ void ExclusiveScanPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackMaxOp prefix_op(INT_MIN);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide exclusive prefix max scan\n    BlockScanT(temp_storage.scan).ExclusiveScan(thread_data, thread_data, cuda::maximum<>{}, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end exclusive-scan-prefix-callback\n\n// example-begin exclusive-sum-single-prefix-callback\n__global__ void ExclusiveSumSinglePrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockScan for a 1D block of 128 threads\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackOp prefix_op(0);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data = d_data[block_offset + threadIdx.x];\n\n    // Collectively compute the block-wide exclusive prefix sum\n    BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    d_data[block_offset + threadIdx.x] = thread_data;\n  }\n}\n// example-end exclusive-sum-single-prefix-callback\n\n// example-begin exclusive-sum-array-aggregate\n__global__ void ExclusiveSumArrayAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide exclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).ExclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end exclusive-sum-array-aggregate\n\n// example-begin inclusive-scan-array\n__global__ void InclusiveScanArrayKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix max scan\n  BlockScan(temp_storage).InclusiveScan(thread_data, thread_data, ::cuda::maximum<>{});\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-scan-array\n\n// example-begin inclusive-scan-single\n__global__ void InclusiveScanSingleKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix max scan\n  BlockScan(temp_storage).InclusiveScan(thread_data, thread_data, cuda::maximum<>{});\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-scan-single\n\n// example-begin inclusive-scan-prefix-callback-max\n__global__ void InclusiveScanPrefixCallbackKernel(int* d_data, int num_items)\n{\n  // Specialize BlockLoad, BlockStore, and BlockScan for a 1D block of 128 threads, 4 ints per thread\n  using BlockLoadT  = BlockLoad<int, 128, 4, BLOCK_LOAD_TRANSPOSE>;\n  using BlockStoreT = BlockStore<int, 128, 4, BLOCK_STORE_TRANSPOSE>;\n  using BlockScanT  = BlockScan<int, 128>;\n\n  // Allocate aliased shared memory for BlockLoad, BlockStore, and BlockScan\n  __shared__ union\n  {\n    typename BlockLoadT::TempStorage load;\n    typename BlockScanT::TempStorage scan;\n    typename BlockStoreT::TempStorage store;\n  } temp_storage;\n\n  // Initialize running total\n  BlockPrefixCallbackMaxOp prefix_op(INT_MIN);\n\n  // Have the block iterate over segments of items\n  for (int block_offset = 0; block_offset < num_items; block_offset += 128 * 4)\n  {\n    // Load a segment of consecutive items that are blocked across threads\n    int thread_data[4];\n    BlockLoadT(temp_storage.load).Load(d_data + block_offset, thread_data);\n    __syncthreads();\n\n    // Collectively compute the block-wide inclusive prefix max\n    BlockScanT(temp_storage.scan).InclusiveScan(thread_data, thread_data, cuda::maximum<>{}, prefix_op);\n    __syncthreads();\n\n    // Store scanned items to output segment\n    BlockStoreT(temp_storage.store).Store(d_data + block_offset, thread_data);\n    __syncthreads();\n  }\n}\n// example-end inclusive-scan-prefix-callback-max\n\n// example-begin inclusive-sum-array-aggregate\n__global__ void InclusiveSumArrayAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  int thread_data[4];\n  for (int i = 0; i < 4; i++)\n  {\n    thread_data[i] = d_data[threadIdx.x * 4 + i];\n  }\n\n  // Collectively compute the block-wide inclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store results\n  for (int i = 0; i < 4; i++)\n  {\n    d_data[threadIdx.x * 4 + i] = thread_data[i];\n  }\n}\n// example-end inclusive-sum-array-aggregate\n\n// example-begin inclusive-sum-single-aggregate\n__global__ void InclusiveSumSingleAggregateKernel(int* d_data)\n{\n  // Specialize BlockScan for a 1D block of 128 threads of type int\n  using BlockScan = hipcub::BlockScan<int, 128>;\n\n  // Allocate shared memory for BlockScan\n  __shared__ typename BlockScan::TempStorage temp_storage;\n\n  // Obtain input item for each thread\n  int thread_data = d_data[threadIdx.x];\n\n  // Collectively compute the block-wide inclusive prefix sum\n  int block_aggregate;\n  BlockScan(temp_storage).InclusiveSum(thread_data, thread_data, block_aggregate);\n\n  // Store result\n  d_data[threadIdx.x] = thread_data;\n}\n// example-end inclusive-sum-single-aggregate\n\n/**\n * Test documentation example kernels\n */\nvoid TestDocumentationExamples()\n{\n  printf(\"Testing documentation example kernels...\\n\");\n\n  const int num_items = 128 * 4; // 512 items for array examples, 128 for single-item examples\n  int* d_data;\n  int* h_data = new int[num_items];\n  int running_max;\n  bool all_passed = true;\n\n  hipMalloc(&d_data, num_items * sizeof(int));\n\n  // Test ExclusiveSumArrayKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveSumArrayKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int expected = i * 4 + j;\n        if (h_data[i * 4 + j] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", i * 4 + j, expected, h_data[i * 4 + j]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  // Test ExclusiveSumSingleKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, 128 * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveSumSingleKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, 128 * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  // Test ExclusiveSumAggregateKernel\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumAggregateKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, 128 * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveSumAggregateKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, 128 * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveSumPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveSumPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      if (h_data[i] != i)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveSumPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      if (h_data[i] != i + 1)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i + 1, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveSumArrayKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int expected = i * 4 + j + 1;\n        if (h_data[i * 4 + j] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", i * 4 + j, expected, h_data[i * 4 + j]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveSumSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = 1;\n    }\n    hipMemcpy(d_data, h_data, 128 * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveSumSingleKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, 128 * sizeof(int), hipMemcpyDeviceToHost);\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      if (h_data[i] != i + 1)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, i + 1, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = i;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveScanArrayKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int idx      = i * 4 + j;\n        int expected = running_max;\n        if (h_data[idx] != expected)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", idx, expected, h_data[idx]);\n          passed     = false;\n          all_passed = false;\n        }\n        running_max = (idx > running_max) ? idx : running_max;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanAggregateKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = i;\n    }\n    hipMemcpy(d_data, h_data, 128 * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveScanAggregateKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, 128 * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      int expected = running_max;\n      if (h_data[i] != expected)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, expected, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n      running_max = (i > running_max) ? i : running_max;\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing ExclusiveScanPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = (i % 2 == 0) ? i : -i;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    ExclusiveScanPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      int input_val = (i % 2 == 0) ? i : -i;\n      int expected  = running_max;\n      if (h_data[i] != expected)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, expected, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n      running_max = (input_val > running_max) ? input_val : running_max;\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanArrayKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = i;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveScanArrayKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      for (int j = 0; j < 4 && passed; j++)\n      {\n        int idx     = i * 4 + j;\n        running_max = (idx > running_max) ? idx : running_max;\n        if (h_data[idx] != running_max)\n        {\n          printf(\"FAILED at [%d]: expected %d, got %d\\n\", idx, running_max, h_data[idx]);\n          passed     = false;\n          all_passed = false;\n        }\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanSingleKernel... \");\n    for (int i = 0; i < 128; i++)\n    {\n      h_data[i] = i;\n    }\n    hipMemcpy(d_data, h_data, 128 * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveScanSingleKernel<<<1, 128>>>(d_data);\n    hipMemcpy(h_data, d_data, 128 * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < 128 && passed; i++)\n    {\n      running_max = (i > running_max) ? i : running_max;\n      if (h_data[i] != running_max)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, running_max, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"  Testing InclusiveScanPrefixCallbackKernel... \");\n    for (int i = 0; i < num_items; i++)\n    {\n      h_data[i] = (i % 2 == 0) ? i : -i;\n    }\n    hipMemcpy(d_data, h_data, num_items * sizeof(int), hipMemcpyHostToDevice);\n    InclusiveScanPrefixCallbackKernel<<<1, 128>>>(d_data, num_items);\n    hipMemcpy(h_data, d_data, num_items * sizeof(int), hipMemcpyDeviceToHost);\n    running_max = INT_MIN;\n    bool passed = true;\n    for (int i = 0; i < num_items && passed; i++)\n    {\n      int input_val = (i % 2 == 0) ? i : -i;\n      running_max   = (input_val > running_max) ? input_val : running_max;\n      if (h_data[i] != running_max)\n      {\n        printf(\"FAILED at [%d]: expected %d, got %d\\n\", i, running_max, h_data[i]);\n        passed     = false;\n        all_passed = false;\n      }\n    }\n    if (passed)\n    {\n      printf(\"PASS\\n\");\n    }\n  }\n\n  if (all_passed)\n  {\n    printf(\"All documentation example tests PASSED!\\n\\n\");\n  }\n\n  delete[] h_data;\n  hipFree(d_data);\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"i\", g_timing_iterations);\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--i=<timing iterations (default:%d)>]\"\n           \"[--grid-size=<grid size (default:%d)>]\"\n           \"[--v] \"\n           \"\\n\",\n           argv[0],\n           g_timing_iterations,\n           g_grid_size);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Test documentation example kernels first\n  TestDocumentationExamples();\n\n  // Run tests\n  Test<1024, 1, BLOCK_SCAN_RAKING>();\n  Test<512, 2, BLOCK_SCAN_RAKING>();\n  Test<256, 4, BLOCK_SCAN_RAKING>();\n  Test<128, 8, BLOCK_SCAN_RAKING>();\n  Test<64, 16, BLOCK_SCAN_RAKING>();\n  Test<32, 32, BLOCK_SCAN_RAKING>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<512, 2, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<256, 4, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<128, 8, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<64, 16, BLOCK_SCAN_RAKING_MEMOIZE>();\n  Test<32, 32, BLOCK_SCAN_RAKING_MEMOIZE>();\n\n  printf(\"-------------\\n\");\n\n  Test<1024, 1, BLOCK_SCAN_WARP_SCANS>();\n  Test<512, 2, BLOCK_SCAN_WARP_SCANS>();\n  Test<256, 4, BLOCK_SCAN_WARP_SCANS>();\n  Test<128, 8, BLOCK_SCAN_WARP_SCANS>();\n  Test<64, 16, BLOCK_SCAN_WARP_SCANS>();\n  Test<32, 32, BLOCK_SCAN_WARP_SCANS>();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/examples/block/example_block_scan.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce with dynamic shared memory\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce_dyn_smem.cu -I../.. -lcudart -O3 -std=c++17\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_reduce.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace cub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS>\n__global__ void BlockReduceKernel(int* d_in, // Tile of input\n                                  int* d_out // Tile aggregate\n)\n{\n  // Specialize BlockReduce type for our thread block\n  using BlockReduceT = cub::BlockReduce<int, BLOCK_THREADS>;\n  using TempStorageT = typename BlockReduceT::TempStorage;\n\n  union ShmemLayout\n  {\n    TempStorageT reduce;\n    int aggregate;\n  };\n\n  // shared memory byte-array\n  extern __shared__ __align__(alignof(ShmemLayout)) char smem[];\n\n  // cast to lvalue reference of expected type\n  auto& temp_storage = reinterpret_cast<TempStorageT&>(smem);\n\n  int data = d_in[threadIdx.x];\n\n  // Compute sum\n  int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n  // block-wide sync barrier necessary to re-use shared mem safely\n  __syncthreads();\n  int* smem_integers = reinterpret_cast<int*>(smem);\n  if (threadIdx.x == 0)\n  {\n    smem_integers[0] = aggregate;\n  }\n\n  // sync to make new shared value available to all threads\n  __syncthreads();\n  aggregate = smem_integers[0];\n\n  // all threads write the aggregate to output\n  d_out[threadIdx.x] = aggregate;\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS>\nvoid Test()\n{\n  // Allocate host arrays\n  int* h_in = new int[BLOCK_THREADS];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, BLOCK_THREADS);\n\n  // Initialize device arrays\n  int* d_in  = nullptr;\n  int* d_out = nullptr;\n  cudaMalloc((void**) &d_in, sizeof(int) * BLOCK_THREADS);\n  cudaMalloc((void**) &d_out, sizeof(int) * BLOCK_THREADS);\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < BLOCK_THREADS; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Copy problem to device\n  cudaMemcpy(d_in, h_in, sizeof(int) * BLOCK_THREADS, cudaMemcpyHostToDevice);\n\n  // determine necessary storage size:\n  auto block_reduce_temp_bytes = sizeof(typename cub::BlockReduce<int, BLOCK_THREADS>::TempStorage);\n  // finally, we need to make sure that we can hold at least one integer\n  // needed in the kernel to exchange data after reduction\n  auto smem_size = (std::max) (1 * sizeof(int), block_reduce_temp_bytes);\n\n  // use default stream\n  cudaStream_t stream = nullptr;\n\n  // Run reduction kernel\n  BlockReduceKernel<BLOCK_THREADS><<<g_grid_size, BLOCK_THREADS, smem_size, stream>>>(d_in, d_out);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  int compare = 0;\n  for (int i = 0; i < BLOCK_THREADS; i++)\n  {\n    compare = compare || CompareDeviceResults(&h_aggregate, d_out + i, 1, g_verbose, g_verbose);\n  }\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (d_in)\n  {\n    cudaFree(d_in);\n  }\n  if (d_out)\n  {\n    cudaFree(d_out);\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--grid-size=<grid size>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Run tests\n  Test<1024>();\n  Test<512>();\n  Test<256>();\n  Test<128>();\n  Test<64>();\n  Test<32>();\n  Test<16>();\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Simple demonstration of cub::BlockReduce with dynamic shared memory\n *\n * To compile using the command line:\n *   nvcc -arch=sm_XX example_block_reduce_dyn_smem.cu -I../.. -lcudart -O3 -std=c++17\n *\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console (define before including cub.h)\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_reduce.cuh>\n#include <cub/block/block_store.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n\n#include \"../../test/test_util.h\"\n\nusing namespace hipcub;\n\n//---------------------------------------------------------------------\n// Globals, constants and aliases\n//---------------------------------------------------------------------\n\n/// Verbose output\nbool g_verbose = false;\n\n/// Default grid size\nint g_grid_size = 1;\n\n//---------------------------------------------------------------------\n// Kernels\n//---------------------------------------------------------------------\n\n/**\n * Simple kernel for performing a block-wide reduction.\n */\ntemplate <int BLOCK_THREADS>\n__global__ void BlockReduceKernel(int* d_in, // Tile of input\n                                  int* d_out // Tile aggregate\n)\n{\n  // Specialize BlockReduce type for our thread block\n  using BlockReduceT = hipcub::BlockReduce<int, BLOCK_THREADS>;\n  using TempStorageT = typename BlockReduceT::TempStorage;\n\n  union ShmemLayout\n  {\n    TempStorageT reduce;\n    int aggregate;\n  };\n\n  // shared memory byte-array\n  extern __shared__ __align__(alignof(ShmemLayout)) char smem[];\n\n  // cast to lvalue reference of expected type\n  auto& temp_storage = reinterpret_cast<TempStorageT&>(smem);\n\n  int data = d_in[threadIdx.x];\n\n  // Compute sum\n  int aggregate = BlockReduceT(temp_storage).Sum(data);\n\n  // block-wide sync barrier necessary to re-use shared mem safely\n  __syncthreads();\n  int* smem_integers = reinterpret_cast<int*>(smem);\n  if (threadIdx.x == 0)\n  {\n    smem_integers[0] = aggregate;\n  }\n\n  // sync to make new shared value available to all threads\n  __syncthreads();\n  aggregate = smem_integers[0];\n\n  // all threads write the aggregate to output\n  d_out[threadIdx.x] = aggregate;\n}\n\n//---------------------------------------------------------------------\n// Host utilities\n//---------------------------------------------------------------------\n\n/**\n * Initialize reduction problem (and solution).\n * Returns the aggregate\n */\nint Initialize(int* h_in, int num_items)\n{\n  int inclusive = 0;\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_in[i] = i % 17;\n    inclusive += h_in[i];\n  }\n\n  return inclusive;\n}\n\n/**\n * Test thread block reduction\n */\ntemplate <int BLOCK_THREADS>\nvoid Test()\n{\n  // Allocate host arrays\n  int* h_in = new int[BLOCK_THREADS];\n\n  // Initialize problem and reference output on host\n  int h_aggregate = Initialize(h_in, BLOCK_THREADS);\n\n  // Initialize device arrays\n  int* d_in  = nullptr;\n  int* d_out = nullptr;\n  hipMalloc((void**) &d_in, sizeof(int) * BLOCK_THREADS);\n  hipMalloc((void**) &d_out, sizeof(int) * BLOCK_THREADS);\n\n  // Display input problem data\n  if (g_verbose)\n  {\n    printf(\"Input data: \");\n    for (int i = 0; i < BLOCK_THREADS; i++)\n    {\n      printf(\"%d, \", h_in[i]);\n    }\n    printf(\"\\n\\n\");\n  }\n\n  // Copy problem to device\n  hipMemcpy(d_in, h_in, sizeof(int) * BLOCK_THREADS, hipMemcpyHostToDevice);\n\n  // determine necessary storage size:\n  auto block_reduce_temp_bytes = sizeof(typename hipcub::BlockReduce<int, BLOCK_THREADS>::TempStorage);\n  // finally, we need to make sure that we can hold at least one integer\n  // needed in the kernel to exchange data after reduction\n  auto smem_size = (std::max) (1 * sizeof(int), block_reduce_temp_bytes);\n\n  // use default stream\n  hipStream_t stream = nullptr;\n\n  // Run reduction kernel\n  BlockReduceKernel<BLOCK_THREADS><<<g_grid_size, BLOCK_THREADS, smem_size, stream>>>(d_in, d_out);\n\n  // Check total aggregate\n  printf(\"\\tAggregate: \");\n  int compare = 0;\n  for (int i = 0; i < BLOCK_THREADS; i++)\n  {\n    compare = compare || CompareDeviceResults(&h_aggregate, d_out + i, 1, g_verbose, g_verbose);\n  }\n  printf(\"%s\\n\", compare ? \"FAIL\" : \"PASS\");\n  AssertEquals(0, compare);\n\n  // Check for kernel errors and STDIO from the kernel, if any\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Cleanup\n  if (h_in)\n  {\n    delete[] h_in;\n  }\n  if (d_in)\n  {\n    hipFree(d_in);\n  }\n  if (d_out)\n  {\n    hipFree(d_out);\n  }\n}\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n  args.GetCmdLineArgument(\"grid-size\", g_grid_size);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--grid-size=<grid size>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Run tests\n  Test<1024>();\n  Test<512>();\n  Test<256>();\n  Test<128>();\n  Test<64>();\n  Test<32>();\n  Test<16>();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/examples/block/example_block_reduce_dyn_smem.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <iostream>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(cub::ScanTileState<MessageT> tile_state)\n{\n  using scan_op_t         = ::cuda::std::plus<>;\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n  using tile_prefix_op    = cub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  constexpr unsigned int threads_in_warp = 32;\n  const unsigned int tid                 = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // Compute block aggregate\n  MessageT block_aggregate = blockIdx.x;\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, block_aggregate);\n      printf(\"tile %d: inclusive = %d\\n\", tile_idx, block_aggregate);\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back.\n      // 1. Publish the block's local aggregate to global memory immediately.\n      //    This allows downstream blocks to include this tile's contribution without waiting for this block to fully\n      //    resolve its global prefix.\n      // 2. Block and traverse predecessor tiles (look-back) to compute the global exclusive prefix for this tile.\n      // 3. Update this tile's global state to 'Prefix' (inclusive sum), creating a checkpoint that stops the look-back\n      // of downstream blocks.\n      // Note, the invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(block_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, block_aggregate);\n        printf(\"tile %d: exclusive = %d inclusive = %d\\n\", tile_idx, exclusive_prefix, inclusive_prefix);\n      }\n    }\n  }\n}\n\ntemplate <class MessageT>\nvoid decoupled_look_back_example(int blocks_in_grid)\n{\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(blocks_in_grid, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t* d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  tile_status.Init(blocks_in_grid, d_temp_storage, temp_storage_bytes);\n  constexpr unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid       = ::cuda::ceil_div(blocks_in_grid, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, blocks_in_grid);\n\n  // Launch decoupled look-back\n  constexpr unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<blocks_in_grid, threads_in_block>>>(tile_status);\n\n  // Wait for kernel to finish\n  cudaDeviceSynchronize();\n}\n\nint main()\n{\n  decoupled_look_back_example<int>(14);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/device/device_scan.cuh>\n\n#include <thrust/device_vector.h>\n\n#include <iostream>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(hipcub::ScanTileState<MessageT> tile_state)\n{\n  using scan_op_t         = ::cuda::std::plus<>;\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n  using tile_prefix_op    = hipcub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  constexpr unsigned int threads_in_warp = 32;\n  const unsigned int tid                 = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // Compute block aggregate\n  MessageT block_aggregate = blockIdx.x;\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, block_aggregate);\n      printf(\"tile %d: inclusive = %d\\n\", tile_idx, block_aggregate);\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back.\n      // 1. Publish the block's local aggregate to global memory immediately.\n      //    This allows downstream blocks to include this tile's contribution without waiting for this block to fully\n      //    resolve its global prefix.\n      // 2. Block and traverse predecessor tiles (look-back) to compute the global exclusive prefix for this tile.\n      // 3. Update this tile's global state to 'Prefix' (inclusive sum), creating a checkpoint that stops the look-back\n      // of downstream blocks.\n      // Note, the invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(block_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, block_aggregate);\n        printf(\"tile %d: exclusive = %d inclusive = %d\\n\", tile_idx, exclusive_prefix, inclusive_prefix);\n      }\n    }\n  }\n}\n\ntemplate <class MessageT>\nvoid decoupled_look_back_example(int blocks_in_grid)\n{\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(blocks_in_grid, temp_storage_bytes);\n\n  // Allocate temporary storage\n  thrust::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t* d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  tile_status.Init(blocks_in_grid, d_temp_storage, temp_storage_bytes);\n  constexpr unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid       = ::cuda::ceil_div(blocks_in_grid, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, blocks_in_grid);\n\n  // Launch decoupled look-back\n  constexpr unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<blocks_in_grid, threads_in_block>>>(tile_status);\n\n  // Wait for kernel to finish\n  hipDeviceSynchronize();\n}\n\nint main()\n{\n  decoupled_look_back_example<int>(14);\n}\n", "source": "/kaggle/working/cccl/cub/examples/device/example_device_decoupled_look_back.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/util_arch.cuh>\n#include <cub/util_device.cuh>\n#include <cub/util_macro.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n\n#include <cuda/devices>\n#include <cuda/std/__algorithm/find_if.h>\n#include <cuda/std/array>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\nCUB_NAMESPACE_BEGIN\n\n_CCCL_KERNEL_ATTRIBUTES void write_ptx_version_kernel(int* d_kernel_cuda_cc)\n{\n  *d_kernel_cuda_cc = CUB_PTX_ARCH;\n}\n\nCUB_RUNTIME_FUNCTION static cudaError_t get_cuda_cc_from_kernel(\n  void* d_temp_storage,\n  size_t& temp_storage_bytes,\n  int* d_kernel_cuda_cc,\n  int* ptx_version,\n  cudaStream_t stream = nullptr)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return cudaSuccess;\n  }\n  write_ptx_version_kernel<<<1, 1, 0, stream>>>(d_kernel_cuda_cc);\n  return cub::PtxVersion(*ptx_version);\n}\n\nCUB_NAMESPACE_END\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\nDECLARE_LAUNCH_WRAPPER(cub::get_cuda_cc_from_kernel, get_cuda_cc_from_kernel);\n\nC2H_TEST(\"CUB correctly identifies the ptx version the kernel was compiled for\", \"[util][dispatch]\")\n{\n  constexpr std::size_t single_item = 1;\n  c2h::device_vector<int> cuda_cc(single_item);\n\n  // Query the arch the kernel was actually compiled for\n  int ptx_version = [&]() -> int {\n    int* buffer{};\n    cudaMallocHost(&buffer, sizeof(*buffer));\n    get_cuda_cc_from_kernel(thrust::raw_pointer_cast(cuda_cc.data()), buffer);\n    int result = *buffer;\n    cudaFreeHost(buffer);\n    return result;\n  }();\n\n  int kernel_cuda_cc = cuda_cc[0];\n\n  // Host cub::PtxVersion\n  int host_ptx_version{};\n  cub::PtxVersion(host_ptx_version);\n\n  // Ensure variable was properly populated\n  REQUIRE(0 != kernel_cuda_cc);\n\n  // Ensure that the ptx version corresponds to the cc the kernel was compiled for\n  REQUIRE(ptx_version == kernel_cuda_cc);\n  REQUIRE(host_ptx_version == kernel_cuda_cc);\n}\n\n#ifdef __CUDA_ARCH_LIST__\n#  define CUDA_SM_LIST       __CUDA_ARCH_LIST__\n#  define CUDA_SM_LIST_SCALE 1\n#elif defined(NV_TARGET_SM_INTEGER_LIST)\n#  define CUDA_SM_LIST       NV_TARGET_SM_INTEGER_LIST\n#  define CUDA_SM_LIST_SCALE 10\n#endif\n\n#ifdef CUDA_SM_LIST\nC2H_TEST(\"PtxVersion returns a value from __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST\", \"[util][dispatch]\")\n{\n  int ptx_version = 0;\n  REQUIRE(cub::PtxVersion(ptx_version) == cudaSuccess);\n  auto cc_list = std::vector<int>{CUDA_SM_LIST};\n  for (auto& cc : cc_list)\n  {\n    cc *= CUDA_SM_LIST_SCALE;\n  }\n  REQUIRE(std::find(cc_list.begin(), cc_list.end(), ptx_version) != cc_list.end());\n}\n#endif\n\n#define GEN_POLICY(cur, prev)                                             \\\n  struct policy##cur : cub::ChainedPolicy<cur, policy##cur, policy##prev> \\\n  {                                                                       \\\n    static constexpr int value = cur;                                     \\\n  }\n\n#ifdef CUDA_SM_LIST\n// We list policies for all virtual architectures that __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST can contain, so the\n// actual architectures the tests are compiled for should match to one of those\nstruct policy_hub_all\n{\n  // for the list of supported architectures,\n  // see libcudacxx/include/nv/target or libcudacxx/include/cuda/__device/arch_id.h\n  GEN_POLICY(500, 500);\n  GEN_POLICY(520, 500);\n  GEN_POLICY(530, 520);\n  GEN_POLICY(600, 530);\n  GEN_POLICY(610, 600);\n  GEN_POLICY(620, 610);\n  GEN_POLICY(700, 620);\n  GEN_POLICY(720, 700);\n  GEN_POLICY(750, 720);\n  GEN_POLICY(800, 750);\n  GEN_POLICY(860, 800);\n  GEN_POLICY(870, 860);\n  GEN_POLICY(880, 870);\n  GEN_POLICY(890, 880);\n  GEN_POLICY(900, 890);\n  GEN_POLICY(1000, 900);\n  GEN_POLICY(1010, 1000);\n  GEN_POLICY(1030, 1010);\n  GEN_POLICY(1100, 1030);\n  GEN_POLICY(1200, 1100);\n  GEN_POLICY(1210, 1200);\n  // add more policies here when new architectures emerge\n  GEN_POLICY(2000, 1210); // non-existing architecture, just to test pruning\n\n  using max_policy = policy2000;\n};\n\n// check that the selected policy exactly matches one of (scaled) arches we compile for\ntemplate <int SelectedPolicyCc, int... CcList>\nstruct check\n{\n  static_assert(((SelectedPolicyCc == CcList * CUDA_SM_LIST_SCALE) || ...));\n  using type = cudaError_t;\n};\n\nstruct closure_all\n{\n  int ptx_version;\n\n  template <typename ActivePolicy>\n  CUB_RUNTIME_FUNCTION auto Invoke() const -> typename check<ActivePolicy::value, CUDA_SM_LIST>::type\n  {\n    // since policy_hub_all lists all PTX virtual architectures, we can do an exact comparison here\n#  if TEST_LAUNCH == 0\n    REQUIRE(+ActivePolicy::value == ptx_version);\n#  endif // TEST_LAUNCH == 0\n    // the returned error code will be checked by the launch helper\n    return +ActivePolicy::value == ptx_version ? cudaSuccess : cudaErrorInvalidValue;\n  }\n};\n\nCUB_RUNTIME_FUNCTION cudaError_t\ncheck_chained_policy_prunes_to_cc_list(void* d_temp_storage, size_t& temp_storage_bytes, cudaStream_t = 0)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return cudaSuccess;\n  }\n  int ptx_version = 0;\n  cub::PtxVersion(ptx_version);\n  closure_all c{ptx_version};\n  return policy_hub_all::max_policy::Invoke(ptx_version, c);\n}\n\nDECLARE_LAUNCH_WRAPPER(check_chained_policy_prunes_to_cc_list, check_wrapper_all);\n\nC2H_TEST(\"ChainedPolicy prunes based on __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST\", \"[util][dispatch]\")\n{\n  check_wrapper_all();\n}\n#endif\n\ntemplate <int NumPolicies>\nstruct check_policy_closure\n{\n  int ptx_version;\n  cuda::std::array<int, NumPolicies> policies;\n\n  // quick way to get a comparator for find_if below\n  __host__ __device__ bool operator()(int policy_ver) const\n  {\n    return policy_ver <= ptx_version;\n  }\n\n  template <typename ActivePolicy>\n  CUB_RUNTIME_FUNCTION cudaError_t Invoke() const\n  {\n#define CHECK_EXPR +ActivePolicy::value == *cuda::std::find_if(policies.rbegin(), policies.rend(), *this)\n\n#if TEST_LAUNCH == 0\n    CAPTURE(ptx_version, policies);\n    REQUIRE(CHECK_EXPR);\n#else // TEST_LAUNCH == 0\n    if (!(CHECK_EXPR))\n    {\n      printf(\"Check `%s` failed!\\n  ptx_version=%d\\n  ActivePolicy::value=%d\\n  policies=\",\n             THRUST_PP_STRINGIZE(CHECK_EXPR),\n             ptx_version,\n             ActivePolicy::value);\n      for (int i = 0; i < NumPolicies; i++)\n      {\n        printf(\"%d,\", policies[i]);\n      }\n      printf(\"\\n\");\n    }\n#endif // TEST_LAUNCH == 0\n    // the returned error code will be checked by the launch helper\n    return (CHECK_EXPR) ? cudaSuccess : cudaErrorInvalidValue;\n#undef CHECK_EXPR\n  }\n};\n\ntemplate <typename PolicyHub, int NumPolicies>\nCUB_RUNTIME_FUNCTION cudaError_t check_chained_policy_selects_correct_policy(\n  void* d_temp_storage, size_t& temp_storage_bytes, cuda::std::array<int, NumPolicies> policies, cudaStream_t = nullptr)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return cudaSuccess;\n  }\n  int ptx_version = 0;\n  cub::PtxVersion(ptx_version);\n  check_policy_closure<NumPolicies> c{ptx_version, std::move(policies)};\n  return PolicyHub::max_policy::Invoke(ptx_version, c);\n}\n\nDECLARE_TMPL_LAUNCH_WRAPPER(check_chained_policy_selects_correct_policy,\n                            check_wrapper_some,\n                            ESCAPE_LIST(typename PolicyHub, int NumPolicies),\n                            ESCAPE_LIST(PolicyHub, NumPolicies));\n\nstruct policy_hub_some\n{\n  GEN_POLICY(500, 500);\n  GEN_POLICY(700, 500);\n  GEN_POLICY(900, 700);\n  GEN_POLICY(2000, 900); // non-existing architecture, just to test\n  using max_policy = policy2000;\n};\n\nstruct policy_hub_few\n{\n  GEN_POLICY(500, 500);\n  GEN_POLICY(860, 500);\n  GEN_POLICY(2000, 860); // non-existing architecture, just to test\n  using max_policy = policy2000;\n};\n\nstruct policy_hub_minimal\n{\n  GEN_POLICY(500, 500);\n  using max_policy = policy500;\n};\n\nC2H_TEST(\"ChainedPolicy invokes correct policy\", \"[util][dispatch]\")\n{\n  SECTION(\"policy_hub_some\")\n  {\n    check_wrapper_some<policy_hub_some, 4>(cuda::std::array<int, 4>{500, 700, 900, 2000});\n  }\n  SECTION(\"policy_hub_few\")\n  {\n    check_wrapper_some<policy_hub_few, 3>(cuda::std::array<int, 3>{500, 860, 2000});\n  }\n  SECTION(\"policy_hub_minimal\")\n  {\n    check_wrapper_some<policy_hub_minimal, 1>(cuda::std::array<int, 1>{500});\n  }\n}\n\n__global__ void test_max_potential_dynamic_smem_bytes_kernel()\n{\n  // use inline PTX so the variable doesn't get optimized out\n  asm volatile(\".shared .align 1 .b8 static_smem[4096];\");\n}\n\n#if defined(CUB_RDC_ENABLED)\n__global__ void test_max_potential_dynamic_smem_bytes_device(int* result)\n{\n  // Just compile on device.\n  cub::MaxPotentialDynamicSmemBytes(*result, test_max_potential_dynamic_smem_bytes_kernel);\n}\n#endif // CUB_RDC_ENABLED\n\nC2H_TEST(\"MaxPotentialDynamicSmemBytes\", \"[util][launch]\")\n{\n  cuda::device_ref device{0};\n\n  // Calculate the expected max potential dynamic shared memory size.\n  const auto max_smem_per_block_optin = device.attribute(cuda::device_attributes::max_shared_memory_per_block_optin);\n  const auto reserved_smem_per_block  = device.attribute(cuda::device_attributes::reserved_shared_memory_per_block);\n  const auto expected                 = static_cast<int>(max_smem_per_block_optin - reserved_smem_per_block - 4096);\n\n  // 1. Test positive case.\n  int dyn_smem_size{};\n  REQUIRE(\n    cub::MaxPotentialDynamicSmemBytes(dyn_smem_size, test_max_potential_dynamic_smem_bytes_kernel) == cudaSuccess);\n  REQUIRE(dyn_smem_size == expected);\n\n  // 2. Test that we return -1 if an error occurs.\n  REQUIRE(cub::MaxPotentialDynamicSmemBytes(dyn_smem_size, nullptr) != cudaSuccess);\n  REQUIRE(dyn_smem_size == -1);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_arch.cuh>\n#include <cub/util_device.cuh>\n#include <cub/util_macro.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n\n#include <cuda/devices>\n#include <cuda/std/__algorithm/find_if.h>\n#include <cuda/std/array>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\nBEGIN_HIPCUB_NAMESPACE\n\n_CCCL_KERNEL_ATTRIBUTES void write_ptx_version_kernel(int* d_kernel_cuda_cc)\n{\n  *d_kernel_cuda_cc = HIPCUB_ARCH;\n}\n\nHIPCUB_RUNTIME_FUNCTION static hipError_t get_cuda_cc_from_kernel(\n  void* d_temp_storage,\n  size_t& temp_storage_bytes,\n  int* d_kernel_cuda_cc,\n  int* ptx_version,\n  hipStream_t stream = nullptr)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return hipSuccess;\n  }\n  write_ptx_version_kernel<<<1, 1, 0, stream>>>(d_kernel_cuda_cc);\n  return hipcub::PtxVersion(*ptx_version);\n}\n\nEND_HIPCUB_NAMESPACE\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\nDECLARE_LAUNCH_WRAPPER(hipcub::get_cuda_cc_from_kernel, get_cuda_cc_from_kernel);\n\nC2H_TEST(\"CUB correctly identifies the ptx version the kernel was compiled for\", \"[util][dispatch]\")\n{\n  constexpr std::size_t single_item = 1;\n  c2h::device_vector<int> cuda_cc(single_item);\n\n  // Query the arch the kernel was actually compiled for\n  int ptx_version = [&]() -> int {\n    int* buffer{};\n    hipHostMalloc(&buffer, sizeof(*buffer));\n    get_cuda_cc_from_kernel(thrust::raw_pointer_cast(cuda_cc.data()), buffer);\n    int result = *buffer;\n    hipHostFree(buffer);\n    return result;\n  }();\n\n  int kernel_cuda_cc = cuda_cc[0];\n\n  // Host cub::PtxVersion\n  int host_ptx_version{};\n  hipcub::PtxVersion(host_ptx_version);\n\n  // Ensure variable was properly populated\n  REQUIRE(0 != kernel_cuda_cc);\n\n  // Ensure that the ptx version corresponds to the cc the kernel was compiled for\n  REQUIRE(ptx_version == kernel_cuda_cc);\n  REQUIRE(host_ptx_version == kernel_cuda_cc);\n}\n\n#ifdef __CUDA_ARCH_LIST__\n#  define CUDA_SM_LIST       __CUDA_ARCH_LIST__\n#  define CUDA_SM_LIST_SCALE 1\n#elif defined(NV_TARGET_SM_INTEGER_LIST)\n#  define CUDA_SM_LIST       NV_TARGET_SM_INTEGER_LIST\n#  define CUDA_SM_LIST_SCALE 10\n#endif\n\n#ifdef CUDA_SM_LIST\nC2H_TEST(\"PtxVersion returns a value from __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST\", \"[util][dispatch]\")\n{\n  int ptx_version = 0;\n  REQUIRE(hipcub::PtxVersion(ptx_version) == hipSuccess);\n  auto cc_list = std::vector<int>{CUDA_SM_LIST};\n  for (auto& cc : cc_list)\n  {\n    cc *= CUDA_SM_LIST_SCALE;\n  }\n  REQUIRE(std::find(cc_list.begin(), cc_list.end(), ptx_version) != cc_list.end());\n}\n#endif\n\n#define GEN_POLICY(cur, prev)                                             \\\n  struct policy##cur : hipcub::ChainedPolicy<cur, policy##cur, policy##prev> \\\n  {                                                                       \\\n    static constexpr int value = cur;                                     \\\n  }\n\n#ifdef CUDA_SM_LIST\n// We list policies for all virtual architectures that __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST can contain, so the\n// actual architectures the tests are compiled for should match to one of those\nstruct policy_hub_all\n{\n  // for the list of supported architectures,\n  // see libcudacxx/include/nv/target or libcudacxx/include/cuda/__device/arch_id.h\n  GEN_POLICY(500, 500);\n  GEN_POLICY(520, 500);\n  GEN_POLICY(530, 520);\n  GEN_POLICY(600, 530);\n  GEN_POLICY(610, 600);\n  GEN_POLICY(620, 610);\n  GEN_POLICY(700, 620);\n  GEN_POLICY(720, 700);\n  GEN_POLICY(750, 720);\n  GEN_POLICY(800, 750);\n  GEN_POLICY(860, 800);\n  GEN_POLICY(870, 860);\n  GEN_POLICY(880, 870);\n  GEN_POLICY(890, 880);\n  GEN_POLICY(900, 890);\n  GEN_POLICY(1000, 900);\n  GEN_POLICY(1010, 1000);\n  GEN_POLICY(1030, 1010);\n  GEN_POLICY(1100, 1030);\n  GEN_POLICY(1200, 1100);\n  GEN_POLICY(1210, 1200);\n  // add more policies here when new architectures emerge\n  GEN_POLICY(2000, 1210); // non-existing architecture, just to test pruning\n\n  using max_policy = policy2000;\n};\n\n// check that the selected policy exactly matches one of (scaled) arches we compile for\ntemplate <int SelectedPolicyCc, int... CcList>\nstruct check\n{\n  static_assert(((SelectedPolicyCc == CcList * CUDA_SM_LIST_SCALE) || ...));\n  using type = hipError_t;\n};\n\nstruct closure_all\n{\n  int ptx_version;\n\n  template <typename ActivePolicy>\n  HIPCUB_RUNTIME_FUNCTION auto Invoke() const -> typename check<ActivePolicy::value, CUDA_SM_LIST>::type\n  {\n    // since policy_hub_all lists all PTX virtual architectures, we can do an exact comparison here\n#  if TEST_LAUNCH == 0\n    REQUIRE(+ActivePolicy::value == ptx_version);\n#  endif // TEST_LAUNCH == 0\n    // the returned error code will be checked by the launch helper\n    return +ActivePolicy::value == ptx_version ? hipSuccess : hipErrorInvalidValue;\n  }\n};\n\nHIPCUB_RUNTIME_FUNCTION hipError_t\ncheck_chained_policy_prunes_to_cc_list(void* d_temp_storage, size_t& temp_storage_bytes, hipStream_t = 0)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return hipSuccess;\n  }\n  int ptx_version = 0;\n  hipcub::PtxVersion(ptx_version);\n  closure_all c{ptx_version};\n  return policy_hub_all::max_policy::Invoke(ptx_version, c);\n}\n\nDECLARE_LAUNCH_WRAPPER(check_chained_policy_prunes_to_cc_list, check_wrapper_all);\n\nC2H_TEST(\"ChainedPolicy prunes based on __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST\", \"[util][dispatch]\")\n{\n  check_wrapper_all();\n}\n#endif\n\ntemplate <int NumPolicies>\nstruct check_policy_closure\n{\n  int ptx_version;\n  cuda::std::array<int, NumPolicies> policies;\n\n  // quick way to get a comparator for find_if below\n  __host__ __device__ bool operator()(int policy_ver) const\n  {\n    return policy_ver <= ptx_version;\n  }\n\n  template <typename ActivePolicy>\n  HIPCUB_RUNTIME_FUNCTION hipError_t Invoke() const\n  {\n#define CHECK_EXPR +ActivePolicy::value == *cuda::std::find_if(policies.rbegin(), policies.rend(), *this)\n\n#if TEST_LAUNCH == 0\n    CAPTURE(ptx_version, policies);\n    REQUIRE(CHECK_EXPR);\n#else // TEST_LAUNCH == 0\n    if (!(CHECK_EXPR))\n    {\n      printf(\"Check `%s` failed!\\n  ptx_version=%d\\n  ActivePolicy::value=%d\\n  policies=\",\n             THRUST_PP_STRINGIZE(CHECK_EXPR),\n             ptx_version,\n             ActivePolicy::value);\n      for (int i = 0; i < NumPolicies; i++)\n      {\n        printf(\"%d,\", policies[i]);\n      }\n      printf(\"\\n\");\n    }\n#endif // TEST_LAUNCH == 0\n    // the returned error code will be checked by the launch helper\n    return (CHECK_EXPR) ? hipSuccess : hipErrorInvalidValue;\n#undef CHECK_EXPR\n  }\n};\n\ntemplate <typename PolicyHub, int NumPolicies>\nHIPCUB_RUNTIME_FUNCTION hipError_t check_chained_policy_selects_correct_policy(\n  void* d_temp_storage, size_t& temp_storage_bytes, cuda::std::array<int, NumPolicies> policies, hipStream_t = nullptr)\n{\n  if (d_temp_storage == nullptr)\n  {\n    temp_storage_bytes = 1;\n    return hipSuccess;\n  }\n  int ptx_version = 0;\n  hipcub::PtxVersion(ptx_version);\n  check_policy_closure<NumPolicies> c{ptx_version, std::move(policies)};\n  return PolicyHub::max_policy::Invoke(ptx_version, c);\n}\n\nDECLARE_TMPL_LAUNCH_WRAPPER(check_chained_policy_selects_correct_policy,\n                            check_wrapper_some,\n                            ESCAPE_LIST(typename PolicyHub, int NumPolicies),\n                            ESCAPE_LIST(PolicyHub, NumPolicies));\n\nstruct policy_hub_some\n{\n  GEN_POLICY(500, 500);\n  GEN_POLICY(700, 500);\n  GEN_POLICY(900, 700);\n  GEN_POLICY(2000, 900); // non-existing architecture, just to test\n  using max_policy = policy2000;\n};\n\nstruct policy_hub_few\n{\n  GEN_POLICY(500, 500);\n  GEN_POLICY(860, 500);\n  GEN_POLICY(2000, 860); // non-existing architecture, just to test\n  using max_policy = policy2000;\n};\n\nstruct policy_hub_minimal\n{\n  GEN_POLICY(500, 500);\n  using max_policy = policy500;\n};\n\nC2H_TEST(\"ChainedPolicy invokes correct policy\", \"[util][dispatch]\")\n{\n  SECTION(\"policy_hub_some\")\n  {\n    check_wrapper_some<policy_hub_some, 4>(cuda::std::array<int, 4>{500, 700, 900, 2000});\n  }\n  SECTION(\"policy_hub_few\")\n  {\n    check_wrapper_some<policy_hub_few, 3>(cuda::std::array<int, 3>{500, 860, 2000});\n  }\n  SECTION(\"policy_hub_minimal\")\n  {\n    check_wrapper_some<policy_hub_minimal, 1>(cuda::std::array<int, 1>{500});\n  }\n}\n\n__global__ void test_max_potential_dynamic_smem_bytes_kernel()\n{\n  // use inline PTX so the variable doesn't get optimized out\n  asm volatile(\".shared .align 1 .b8 static_smem[4096];\");\n}\n\n#if defined(CUB_RDC_ENABLED)\n__global__ void test_max_potential_dynamic_smem_bytes_device(int* result)\n{\n  // Just compile on device.\n  hipcub::MaxPotentialDynamicSmemBytes(*result, test_max_potential_dynamic_smem_bytes_kernel);\n}\n#endif // CUB_RDC_ENABLED\n\nC2H_TEST(\"MaxPotentialDynamicSmemBytes\", \"[util][launch]\")\n{\n  cuda::device_ref device{0};\n\n  // Calculate the expected max potential dynamic shared memory size.\n  const auto max_smem_per_block_optin = device.attribute(cuda::device_attributes::max_shared_memory_per_block_optin);\n  const auto reserved_smem_per_block  = device.attribute(cuda::device_attributes::reserved_shared_memory_per_block);\n  const auto expected                 = static_cast<int>(max_smem_per_block_optin - reserved_smem_per_block - 4096);\n\n  // 1. Test positive case.\n  int dyn_smem_size{};\n  REQUIRE(\n    hipcub::MaxPotentialDynamicSmemBytes(dyn_smem_size, test_max_potential_dynamic_smem_bytes_kernel) == hipSuccess);\n  REQUIRE(dyn_smem_size == expected);\n\n  // 2. Test that we return -1 if an error occurs.\n  REQUIRE(hipcub::MaxPotentialDynamicSmemBytes(dyn_smem_size, nullptr) != hipSuccess);\n  REQUIRE(dyn_smem_size == -1);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_util_device.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/util_arch.cuh>\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_scan.cuh>\n\n#include <cuda/cmath>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_combine_scan_kernel(T* in, T* inclusive_out, T* exclusive_out, ActionT action)\n{\n  using warp_scan_t = cub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T inc_out, exc_out;\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data, inc_out, exc_out);\n\n  inclusive_out[tid] = inc_out;\n  exclusive_out[tid] = exc_out;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_combine_scan(\n  c2h::device_vector<T>& in, c2h::device_vector<T>& inclusive_out, c2h::device_vector<T>& exclusive_out, ActionT action)\n{\n  warp_combine_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()),\n    thrust::raw_pointer_cast(inclusive_out.data()),\n    thrust::raw_pointer_cast(exclusive_out.data()),\n    action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_scan_kernel(T* in, T* out, ActionT action)\n{\n  using warp_scan_t = cub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_scan(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  warp_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <class WarpScanT, class T>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{}, warp_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(cub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, warp_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\nstruct min_scan_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data, T& inclusive_output, T& exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, cuda::minimum<>{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_scan_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data, T& inclusive_output, T& exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, initial_value, cuda::minimum<>{});\n  }\n};\n\ntemplate <class T, class ScanOpT>\nc2h::host_vector<T> compute_host_reference(\n  scan_mode mode, c2h::host_vector<T>& result, int logical_warp_threads, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return c2h::host_vector<T>{};\n  }\n  // TODO : assert result.size() % logical_warp_threads == 0\n\n  // The accumulator variable is used to calculate warp_aggregate without\n  // taking initial_value into consideration in both exclusive and inclusive scan.\n  int num_warps = cuda::ceil_div(static_cast<int>(result.size()), logical_warp_threads);\n  c2h::host_vector<T> warp_accumulator(num_warps);\n  if (mode == scan_mode::exclusive)\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T* output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = initial_value;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        accumulator = static_cast<T>(scan_op(accumulator, output[i]));\n        T tmp       = output[i];\n        output[i]   = current;\n        current     = static_cast<T>(scan_op(current, tmp));\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n  else\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T* output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = current;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        T tmp       = output[i];\n        current     = static_cast<T>(scan_op(current, tmp));\n        accumulator = static_cast<T>(scan_op(accumulator, tmp));\n        output[i]   = current;\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n\n  return warp_accumulator;\n}\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 2>;\nusing modes                = c2h::enum_type_list<scan_mode, scan_mode::exclusive, scan_mode::inclusive>;\n\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  uchar3,\n  short2>;\n\nusing warp_combine_type = int;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == cub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr scan_mode mode           = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nC2H_TEST(\"Warp scan works with sum\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp scan works with vec_types\", \"[scan][warp]\", vec_types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Warp scan works with custom types\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Warp scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, params::mode>{target_thread_id, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\n// TODO : Do we need all the types?\nC2H_TEST(\"Warp scan works with custom scan op\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, min_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if constexpr (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp custom op scan returns valid warp aggregate\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_aggregate_op_t<type, params::mode>{target_thread_id, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if constexpr (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nC2H_TEST(\"Warp custom op scan works with initial value\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, min_init_value_op_t<type, params::mode>{initial_value});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp custom op scan with initial value returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n  const type initial_value   = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type, params::mode>{\n      target_thread_id, initial_value, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nC2H_TEST(\"Warp combination scan works with custom scan op\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  c2h::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in, d_inclusive_out, d_exclusive_out, min_scan_op_t{});\n\n  c2h::host_vector<type> h_exclusive_out = d_in;\n  c2h::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // According to WarpScan::Scan documentation -\n  // Because no initial value is supplied, the exclusive_output computed for warp-lane0 is\n  // undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  for (size_t i = 0; i < h_exclusive_out.size(); i += logical_warp_threads)\n  {\n    d_exclusive_out[i] = h_exclusive_out[i];\n  }\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n\nC2H_TEST(\"Warp combination custom scan works with initial value\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  c2h::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = GENERATE_COPY(take(2, random(0, total_warps * logical_warp_threads)));\n\n  warp_combine_scan<logical_warp_threads, total_warps>(\n    d_in, d_inclusive_out, d_exclusive_out, min_init_value_scan_op_t<type>{initial_value});\n\n  c2h::host_vector<type> h_exclusive_out = d_in;\n  c2h::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_arch.cuh>\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_scan.cuh>\n\n#include <cuda/cmath>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_combine_scan_kernel(T* in, T* inclusive_out, T* exclusive_out, ActionT action)\n{\n  using warp_scan_t = hipcub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T inc_out, exc_out;\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data, inc_out, exc_out);\n\n  inclusive_out[tid] = inc_out;\n  exclusive_out[tid] = exc_out;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_combine_scan(\n  c2h::device_vector<T>& in, c2h::device_vector<T>& inclusive_out, c2h::device_vector<T>& exclusive_out, ActionT action)\n{\n  warp_combine_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()),\n    thrust::raw_pointer_cast(inclusive_out.data()),\n    thrust::raw_pointer_cast(exclusive_out.data()),\n    action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\n__global__ void warp_scan_kernel(T* in, T* out, ActionT action)\n{\n  using warp_scan_t = hipcub::WarpScan<T, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_scan_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  T thread_data = in[tid];\n\n  warp_scan_t scan(storage[warp_id]);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, class T, class ActionT>\nvoid warp_scan(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  warp_scan_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <class WarpScanT, class T>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, warp_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{}, warp_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T* m_d_warp_aggregate;\n\n  template <int LOGICAL_WARP_THREADS>\n  __device__ void operator()(hipcub::WarpScan<T, LOGICAL_WARP_THREADS>& scan, T& thread_data) const\n  {\n    T warp_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, warp_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, warp_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid % LOGICAL_WARP_THREADS == m_target_thread_id)\n    {\n      m_d_warp_aggregate[tid / LOGICAL_WARP_THREADS] = warp_aggregate;\n    }\n  }\n};\n\nstruct min_scan_op_t\n{\n  template <class T, class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data, T& inclusive_output, T& exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, cuda::minimum<>{});\n  }\n};\n\ntemplate <class T>\nstruct min_init_value_scan_op_t\n{\n  T initial_value;\n  template <class WarpScanT>\n  __device__ void operator()(WarpScanT& scan, T& thread_data, T& inclusive_output, T& exclusive_output) const\n  {\n    scan.Scan(thread_data, inclusive_output, exclusive_output, initial_value, cuda::minimum<>{});\n  }\n};\n\ntemplate <class T, class ScanOpT>\nc2h::host_vector<T> compute_host_reference(\n  scan_mode mode, c2h::host_vector<T>& result, int logical_warp_threads, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return c2h::host_vector<T>{};\n  }\n  // TODO : assert result.size() % logical_warp_threads == 0\n\n  // The accumulator variable is used to calculate warp_aggregate without\n  // taking initial_value into consideration in both exclusive and inclusive scan.\n  int num_warps = cuda::ceil_div(static_cast<int>(result.size()), logical_warp_threads);\n  c2h::host_vector<T> warp_accumulator(num_warps);\n  if (mode == scan_mode::exclusive)\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T* output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = initial_value;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        accumulator = static_cast<T>(scan_op(accumulator, output[i]));\n        T tmp       = output[i];\n        output[i]   = current;\n        current     = static_cast<T>(scan_op(current, tmp));\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n  else\n  {\n    for (int w = 0; w < num_warps; ++w)\n    {\n      T* output     = result.data() + w * logical_warp_threads;\n      T accumulator = output[0];\n      T current     = static_cast<T>(scan_op(initial_value, output[0]));\n      output[0]     = current;\n      for (int i = 1; i < logical_warp_threads; i++)\n      {\n        T tmp       = output[i];\n        current     = static_cast<T>(scan_op(current, tmp));\n        accumulator = static_cast<T>(scan_op(accumulator, tmp));\n        output[i]   = current;\n      }\n      warp_accumulator[w] = accumulator;\n    }\n  }\n\n  return warp_accumulator;\n}\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 2>;\nusing modes                = c2h::enum_type_list<scan_mode, scan_mode::exclusive, scan_mode::inclusive>;\n\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  uchar3,\n  short2>;\n\nusing warp_combine_type = int;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == hipcub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr scan_mode mode           = c2h::get<2, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nC2H_TEST(\"Warp scan works with sum\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp scan works with vec_types\", \"[scan][warp]\", vec_types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Warp scan works with custom types\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Warp scan returns valid warp aggregate\",\n         \"[scan][warp]\",\n         c2h::type_list<c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>>,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    sum_aggregate_op_t<type, params::mode>{target_thread_id, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(params::mode, h_out, params::logical_warp_threads, std::plus<type>{});\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\n// TODO : Do we need all the types?\nC2H_TEST(\"Warp scan works with custom scan op\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(d_in, d_out, min_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if constexpr (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp custom op scan returns valid warp aggregate\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_aggregate_op_t<type, params::mode>{target_thread_id, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // From the documentation -\n  // Computes an exclusive prefix scan using the specified binary scan functor\n  // across the calling warp. Because no initial value is supplied, the output\n  // computed for warp-lane0 is undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  if constexpr (params::mode == scan_mode::exclusive)\n  {\n    for (size_t i = 0; i < h_out.size(); i += params::logical_warp_threads)\n    {\n      d_out[i] = h_out[i];\n    }\n  }\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nC2H_TEST(\"Warp custom op scan works with initial value\", \"[scan][warp]\", types, logical_warp_threads, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, min_init_value_op_t<type, params::mode>{initial_value});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Warp custom op scan with initial value returns valid warp aggregate\",\n         \"[scan][warp]\",\n         types,\n         logical_warp_threads,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_warp_aggregates(params::total_warps);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::logical_warp_threads - 1)));\n  const type initial_value   = static_cast<type>(GENERATE_COPY(take(2, random(0, params::tile_size))));\n\n  warp_scan<params::logical_warp_threads, params::total_warps>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type, params::mode>{\n      target_thread_id, initial_value, thrust::raw_pointer_cast(d_warp_aggregates.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n\n  auto h_warp_aggregates = compute_host_reference(\n    params::mode,\n    h_out,\n    params::logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_warp_aggregates == d_warp_aggregates);\n}\n\nC2H_TEST(\"Warp combination scan works with custom scan op\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  c2h::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  warp_combine_scan<logical_warp_threads, total_warps>(d_in, d_inclusive_out, d_exclusive_out, min_scan_op_t{});\n\n  c2h::host_vector<type> h_exclusive_out = d_in;\n  c2h::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    cuda::std::numeric_limits<type>::max());\n\n  // According to WarpScan::Scan documentation -\n  // Because no initial value is supplied, the exclusive_output computed for warp-lane0 is\n  // undefined.\n\n  // When comparing device output, the corresponding undefined data points need\n  // to be fixed\n\n  for (size_t i = 0; i < h_exclusive_out.size(); i += logical_warp_threads)\n  {\n    d_exclusive_out[i] = h_exclusive_out[i];\n  }\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n\nC2H_TEST(\"Warp combination custom scan works with initial value\", \"[scan][warp]\", logical_warp_threads)\n{\n  constexpr int logical_warp_threads = c2h::get<0, TestType>();\n  constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  using type                         = int;\n\n  c2h::device_vector<type> d_inclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_exclusive_out(total_warps * logical_warp_threads);\n  c2h::device_vector<type> d_in(total_warps * logical_warp_threads);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = GENERATE_COPY(take(2, random(0, total_warps * logical_warp_threads)));\n\n  warp_combine_scan<logical_warp_threads, total_warps>(\n    d_in, d_inclusive_out, d_exclusive_out, min_init_value_scan_op_t<type>{initial_value});\n\n  c2h::host_vector<type> h_exclusive_out = d_in;\n  c2h::host_vector<type> h_inclusive_out = d_in;\n\n  compute_host_reference(\n    scan_mode::exclusive,\n    h_exclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  compute_host_reference(\n    scan_mode::inclusive,\n    h_inclusive_out,\n    logical_warp_threads,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_inclusive_out == d_inclusive_out);\n  REQUIRE(h_exclusive_out == d_exclusive_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_warp_scan.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/block/block_merge_sort.cuh>\n\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __device__ __host__ bool operator()(const DataType& lhs, const DataType& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class KeyT, class ActionT>\n__global__ void block_merge_sort_kernel(KeyT* data, int valid_items, KeyT oob_default, ActionT action)\n{\n  using BlockMergeSort = cub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_data[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? data[idx] : KeyT();\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_data, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    data[idx] = thread_data[item];\n  }\n}\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class KeyT, class ValueT, class ActionT>\n__global__ void block_merge_sort_kernel(KeyT* keys, ValueT* vals, int valid_items, KeyT oob_default, ActionT action)\n{\n  using BlockMergeSort = cub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread, ValueT>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_keys[ItemsPerThread];\n  ValueT thread_vals[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_keys[item] = idx < valid_items ? keys[idx] : KeyT{};\n    thread_vals[item] = idx < valid_items ? vals[idx] : ValueT{};\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_keys, thread_vals, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    keys[idx] = thread_keys[item];\n    vals[idx] = thread_vals[item];\n  }\n}\n\nstruct stable_sort_keys_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT& sort, KeyT& thread_data, int valid_items, DefaultT oob_default) const\n  {\n    sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\nstruct stable_sort_pairs_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void\n  operator()(BlockMergeSortT& sort, KeyT& thread_keys, ValueT& thread_vals, int valid_items, DefaultT oob_default) const\n  {\n    sort.StableSort(thread_keys, thread_vals, CustomLess{}, valid_items, oob_default);\n  }\n};\n\nstruct stable_sort_pairs_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(\n    BlockMergeSortT& sort, KeyT& thread_keys, ValueT& thread_vals, int /* valid_items */, DefaultT /* oob_default */)\n    const\n  {\n    sort.StableSort(thread_keys, thread_vals, CustomLess());\n  }\n};\n\nstruct stable_sort_keys_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void\n  operator()(BlockMergeSortT& sort, KeyT& thread_keys, int /* valid_items */, DefaultT /* oob_default */) const\n  {\n    sort.StableSort(thread_keys, CustomLess());\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class KeyT, class ActionT>\nvoid block_merge_sort(c2h::device_vector<KeyT>& keys, ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread><<<1, ThreadsInBlock>>>(\n    thrust::raw_pointer_cast(keys.data()),\n    static_cast<int>(keys.size()),\n    cuda::std::numeric_limits<KeyT>::max(),\n    action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class KeyT, class ValueT, class ActionT>\nvoid block_merge_sort(c2h::device_vector<KeyT>& keys, c2h::device_vector<ValueT>& vals, ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread><<<1, ThreadsInBlock>>>(\n    thrust::raw_pointer_cast(keys.data()),\n    thrust::raw_pointer_cast(vals.data()),\n    static_cast<int>(keys.size()),\n    cuda::std::numeric_limits<KeyT>::max(),\n    action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types        = c2h::type_list<std::int32_t, std::int64_t>;\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block merge sort can sort keys in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_keys(GENERATE_COPY(take(10, random(0, params::tile_size))));\n\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, stable_sort_keys_partial_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\n  \"Block merge sort can sort keys in full tiles\", \"[merge sort][block]\", key_types, items_per_thread, threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_keys(params::tile_size);\n\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\"Block merge sort can sort pairs in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params  = params_t<TestType>;\n  using key_t   = typename params::key_t;\n  using value_t = key_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  c2h::device_vector<key_t> d_keys(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys, d_vals, stable_sort_pairs_partial_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\n  \"Block merge sort can sort pairs in full tiles\", \"[merge sort][block]\", key_types, items_per_thread, threads_in_block)\n{\n  using params  = params_t<TestType>;\n  using key_t   = typename params::key_t;\n  using value_t = key_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  c2h::device_vector<key_t> d_keys(params::tile_size);\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, d_vals, stable_sort_pairs_full_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\"Block merge sort can sort pairs with mixed types\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t   = std::int32_t;\n  using value_t = std::int64_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, d_vals, stable_sort_pairs_full_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\"Block merge sort can sort large tiles\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t = std::uint16_t;\n\n  constexpr int items_per_thread = 2;\n\n  // Repurpose block sizes\n  constexpr int cmake_threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block       = cmake_threads_in_block < 256 ? 512 : 1024;\n\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\"Block merge sort is stable\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::less_comparable_t, c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_merge_sort.cuh>\n\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __device__ __host__ bool operator()(const DataType& lhs, const DataType& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class KeyT, class ActionT>\n__global__ void block_merge_sort_kernel(KeyT* data, int valid_items, KeyT oob_default, ActionT action)\n{\n  using BlockMergeSort = hipcub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_data[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? data[idx] : KeyT();\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_data, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    data[idx] = thread_data[item];\n  }\n}\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class KeyT, class ValueT, class ActionT>\n__global__ void block_merge_sort_kernel(KeyT* keys, ValueT* vals, int valid_items, KeyT oob_default, ActionT action)\n{\n  using BlockMergeSort = hipcub::BlockMergeSort<KeyT, ThreadsInBlock, ItemsPerThread, ValueT>;\n\n  __shared__ typename BlockMergeSort::TempStorage temp_storage_shuffle;\n\n  KeyT thread_keys[ItemsPerThread];\n  ValueT thread_vals[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_keys[item] = idx < valid_items ? keys[idx] : KeyT{};\n    thread_vals[item] = idx < valid_items ? vals[idx] : ValueT{};\n  }\n  __syncthreads();\n\n  BlockMergeSort sort(temp_storage_shuffle);\n\n  action(sort, thread_keys, thread_vals, valid_items, oob_default);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n\n    if (idx >= valid_items)\n    {\n      break;\n    }\n\n    keys[idx] = thread_keys[item];\n    vals[idx] = thread_vals[item];\n  }\n}\n\nstruct stable_sort_keys_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void operator()(BlockMergeSortT& sort, KeyT& thread_data, int valid_items, DefaultT oob_default) const\n  {\n    sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\nstruct stable_sort_pairs_partial_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void\n  operator()(BlockMergeSortT& sort, KeyT& thread_keys, ValueT& thread_vals, int valid_items, DefaultT oob_default) const\n  {\n    sort.StableSort(thread_keys, thread_vals, CustomLess{}, valid_items, oob_default);\n  }\n};\n\nstruct stable_sort_pairs_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class ValueT, class DefaultT>\n  __device__ void operator()(\n    BlockMergeSortT& sort, KeyT& thread_keys, ValueT& thread_vals, int /* valid_items */, DefaultT /* oob_default */)\n    const\n  {\n    sort.StableSort(thread_keys, thread_vals, CustomLess());\n  }\n};\n\nstruct stable_sort_keys_full_tile_t\n{\n  template <class BlockMergeSortT, class KeyT, class DefaultT>\n  __device__ void\n  operator()(BlockMergeSortT& sort, KeyT& thread_keys, int /* valid_items */, DefaultT /* oob_default */) const\n  {\n    sort.StableSort(thread_keys, CustomLess());\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class KeyT, class ActionT>\nvoid block_merge_sort(c2h::device_vector<KeyT>& keys, ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread><<<1, ThreadsInBlock>>>(\n    thrust::raw_pointer_cast(keys.data()),\n    static_cast<int>(keys.size()),\n    cuda::std::numeric_limits<KeyT>::max(),\n    action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class KeyT, class ValueT, class ActionT>\nvoid block_merge_sort(c2h::device_vector<KeyT>& keys, c2h::device_vector<ValueT>& vals, ActionT action)\n{\n  block_merge_sort_kernel<ThreadsInBlock, ItemsPerThread><<<1, ThreadsInBlock>>>(\n    thrust::raw_pointer_cast(keys.data()),\n    thrust::raw_pointer_cast(vals.data()),\n    static_cast<int>(keys.size()),\n    cuda::std::numeric_limits<KeyT>::max(),\n    action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types        = c2h::type_list<std::int32_t, std::int64_t>;\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block merge sort can sort keys in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_keys(GENERATE_COPY(take(10, random(0, params::tile_size))));\n\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, stable_sort_keys_partial_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\n  \"Block merge sort can sort keys in full tiles\", \"[merge sort][block]\", key_types, items_per_thread, threads_in_block)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_keys(params::tile_size);\n\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\"Block merge sort can sort pairs in partial tiles\",\n         \"[merge sort][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block)\n{\n  using params  = params_t<TestType>;\n  using key_t   = typename params::key_t;\n  using value_t = key_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  c2h::device_vector<key_t> d_keys(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(\n    d_keys, d_vals, stable_sort_pairs_partial_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\n  \"Block merge sort can sort pairs in full tiles\", \"[merge sort][block]\", key_types, items_per_thread, threads_in_block)\n{\n  using params  = params_t<TestType>;\n  using key_t   = typename params::key_t;\n  using value_t = key_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  c2h::device_vector<key_t> d_keys(params::tile_size);\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<params::items_per_thread, params::threads_in_block>(d_keys, d_vals, stable_sort_pairs_full_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\"Block merge sort can sort pairs with mixed types\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t   = std::int32_t;\n  using value_t = std::int64_t;\n  using pair_t  = std::pair<key_t, value_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::device_vector<value_t> d_vals(d_keys.size());\n\n  c2h::gen(C2H_SEED(5), d_keys);\n  c2h::gen(C2H_SEED(5), d_vals);\n\n  c2h::host_vector<key_t> h_keys   = d_keys;\n  c2h::host_vector<value_t> h_vals = d_vals;\n\n  c2h::host_vector<pair_t> h_ref(d_keys.size());\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_ref[idx] = std::make_pair(h_keys[idx], h_vals[idx]);\n  }\n\n  std::stable_sort(thrust::raw_pointer_cast(h_ref.data()),\n                   thrust::raw_pointer_cast(h_ref.data()) + h_ref.size(),\n                   [](pair_t l, pair_t r) -> bool {\n                     return l.first < r.first;\n                   });\n\n  for (std::size_t idx = 0; idx < h_ref.size(); idx++)\n  {\n    h_keys[idx] = h_ref[idx].first;\n    h_vals[idx] = h_ref[idx].second;\n  }\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, d_vals, stable_sort_pairs_full_tile_t{});\n\n  REQUIRE(h_keys == d_keys);\n  REQUIRE(h_vals == d_vals);\n}\n\nC2H_TEST(\"Block merge sort can sort large tiles\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t = std::uint16_t;\n\n  constexpr int items_per_thread = 2;\n\n  // Repurpose block sizes\n  constexpr int cmake_threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block       = cmake_threads_in_block < 256 ? 512 : 1024;\n\n  constexpr int tile_size = threads_in_block * items_per_thread;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n\nC2H_TEST(\"Block merge sort is stable\", \"[merge sort][block]\", threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::less_comparable_t, c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n\n  c2h::device_vector<key_t> d_keys(tile_size);\n  c2h::gen(C2H_SEED(10), d_keys);\n\n  c2h::host_vector<key_t> h_reference = d_keys;\n  std::stable_sort(thrust::raw_pointer_cast(h_reference.data()),\n                   thrust::raw_pointer_cast(h_reference.data()) + h_reference.size(),\n                   CustomLess{});\n\n  block_merge_sort<items_per_thread, threads_in_block>(d_keys, stable_sort_keys_full_tile_t{});\n\n  REQUIRE(h_reference == d_keys);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_merge_sort.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <cub/device/device_memcpy.cuh>\n#include <cub/util_macro.cuh>\n\n#include <thrust/copy.h>\n#include <thrust/fill.h>\n#include <thrust/sequence.h>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int LocigalWarpSize, typename VectorT, typename ByteOffsetT>\n__global__ void test_vectorized_copy_kernel(const void* d_in, void* d_out, ByteOffsetT copy_size)\n{\n  cub::detail::batch_memcpy::vectorized_copy<LocigalWarpSize, VectorT>(threadIdx.x, d_out, copy_size, d_in);\n}\n\nusing vector_type_list = c2h::type_list<uint32_t, uint4>;\n\nC2H_TEST(\"The vectorized copy used by DeviceMemcpy works\", \"[memcpy]\", vector_type_list)\n{\n  using vector_t                            = typename c2h::get<0, TestType>;\n  constexpr std::uint32_t threads_per_block = 8;\n\n  // Test the vectorized_copy for various aligned and misaligned input and output pointers.\n  std::size_t in_offset  = GENERATE(0, 1, sizeof(uint32_t) - 1);\n  std::size_t out_offset = GENERATE(0, 1, sizeof(vector_t) - 1);\n  std::size_t copy_size =\n    GENERATE_COPY(0, 1, sizeof(uint32_t), sizeof(vector_t), 2 * threads_per_block * sizeof(vector_t));\n  CAPTURE(in_offset, out_offset, copy_size);\n\n  // Prepare data\n  const std::size_t alloc_size_in  = in_offset + copy_size;\n  const std::size_t alloc_size_out = out_offset + copy_size;\n  c2h::device_vector<uint8_t> data_input_buffer(alloc_size_in);\n  c2h::device_vector<uint8_t> data_output_buffer(alloc_size_out);\n  thrust::sequence(c2h::device_policy, data_input_buffer.begin(), data_input_buffer.end(), static_cast<uint8_t>(0));\n  thrust::fill_n(c2h::device_policy, data_output_buffer.begin(), alloc_size_out, static_cast<uint8_t>(0x42));\n\n  auto d_in  = thrust::raw_pointer_cast(data_input_buffer.data());\n  auto d_out = thrust::raw_pointer_cast(data_output_buffer.data());\n\n  test_vectorized_copy_kernel<threads_per_block, vector_t>\n    <<<1, threads_per_block>>>(d_in + in_offset, d_out + out_offset, static_cast<int>(copy_size));\n\n  // Verify the result\n  c2h::device_vector<uint32_t> data_in(copy_size);\n  c2h::device_vector<uint32_t> data_out(copy_size);\n  thrust::copy(\n    data_input_buffer.cbegin() + in_offset, data_input_buffer.cbegin() + in_offset + copy_size, data_in.begin());\n  thrust::copy(\n    data_output_buffer.cbegin() + out_offset, data_output_buffer.cbegin() + out_offset + copy_size, data_out.begin());\n  REQUIRE(data_in == data_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <cub/device/device_memcpy.cuh>\n#include <cub/util_macro.cuh>\n\n#include <thrust/copy.h>\n#include <thrust/fill.h>\n#include <thrust/sequence.h>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int LocigalWarpSize, typename VectorT, typename ByteOffsetT>\n__global__ void test_vectorized_copy_kernel(const void* d_in, void* d_out, ByteOffsetT copy_size)\n{\n  hipcub::detail::batch_memcpy::vectorized_copy<LocigalWarpSize, VectorT>(threadIdx.x, d_out, copy_size, d_in);\n}\n\nusing vector_type_list = c2h::type_list<uint32_t, uint4>;\n\nC2H_TEST(\"The vectorized copy used by DeviceMemcpy works\", \"[memcpy]\", vector_type_list)\n{\n  using vector_t                            = typename c2h::get<0, TestType>;\n  constexpr std::uint32_t threads_per_block = 8;\n\n  // Test the vectorized_copy for various aligned and misaligned input and output pointers.\n  std::size_t in_offset  = GENERATE(0, 1, sizeof(uint32_t) - 1);\n  std::size_t out_offset = GENERATE(0, 1, sizeof(vector_t) - 1);\n  std::size_t copy_size =\n    GENERATE_COPY(0, 1, sizeof(uint32_t), sizeof(vector_t), 2 * threads_per_block * sizeof(vector_t));\n  CAPTURE(in_offset, out_offset, copy_size);\n\n  // Prepare data\n  const std::size_t alloc_size_in  = in_offset + copy_size;\n  const std::size_t alloc_size_out = out_offset + copy_size;\n  c2h::device_vector<uint8_t> data_input_buffer(alloc_size_in);\n  c2h::device_vector<uint8_t> data_output_buffer(alloc_size_out);\n  thrust::sequence(c2h::device_policy, data_input_buffer.begin(), data_input_buffer.end(), static_cast<uint8_t>(0));\n  thrust::fill_n(c2h::device_policy, data_output_buffer.begin(), alloc_size_out, static_cast<uint8_t>(0x42));\n\n  auto d_in  = thrust::raw_pointer_cast(data_input_buffer.data());\n  auto d_out = thrust::raw_pointer_cast(data_output_buffer.data());\n\n  test_vectorized_copy_kernel<threads_per_block, vector_t>\n    <<<1, threads_per_block>>>(d_in + in_offset, d_out + out_offset, static_cast<int>(copy_size));\n\n  // Verify the result\n  c2h::device_vector<uint32_t> data_in(copy_size);\n  c2h::device_vector<uint32_t> data_out(copy_size);\n  thrust::copy(\n    data_input_buffer.cbegin() + in_offset, data_input_buffer.cbegin() + in_offset + copy_size, data_in.begin());\n  thrust::copy(\n    data_output_buffer.cbegin() + out_offset, data_output_buffer.cbegin() + out_offset + copy_size, data_out.begin());\n  REQUIRE(data_in == data_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_memcpy_vectorized_copy.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/warp/warp_store.cuh>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/fill_striped.h>\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\n__global__ void warp_store_kernel(OutputIteratorT output_iterator, ActionT action)\n{\n  using warp_store_t = cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t    = typename warp_store_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  T reg[ITEMS_PER_THREAD];\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    reg[item] = static_cast<T>(tid * ITEMS_PER_THREAD + item);\n  }\n\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  warp_store_t store(storage[warp_id]);\n\n  action(store, output_iterator + (warp_id * tile_size), reg);\n}\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\nvoid warp_store(OutputIteratorT output_iterator, ActionT action)\n{\n  warp_store_kernel<StoreAlgorithm, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, T, OutputIteratorT, ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(output_iterator, action);\n}\n\nstruct guarded_store_t\n{\n  int valid_items;\n  template <cub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void operator()(cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n                             OutputIteratorT output,\n                             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg, valid_items);\n  }\n};\n\nstruct unguarded_store_t\n{\n  template <cub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void operator()(cub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n                             OutputIteratorT output,\n                             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg);\n  }\n};\n\ntemplate <cub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nc2h::device_vector<T> compute_reference(int valid_items)\n{\n  constexpr int tile_size        = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  constexpr int total_item_count = TOTAL_WARPS * tile_size;\n  c2h::device_vector<T> d_input(total_item_count);\n\n  if constexpr (StoreAlgorithm == cub::WarpStoreAlgorithm::WARP_STORE_STRIPED)\n  {\n    c2h::host_vector<T> input(total_item_count);\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD * TOTAL_WARPS>(input.begin());\n    d_input = input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{d_input.size()}, d_input);\n  }\n  if (valid_items != total_item_count)\n  {\n    for (int warp_id = 0; warp_id < TOTAL_WARPS; warp_id++)\n    {\n      thrust::fill(c2h::device_policy,\n                   d_input.begin() + warp_id * tile_size + valid_items,\n                   d_input.begin() + (warp_id + 1) * tile_size,\n                   T{});\n    }\n  }\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms =\n  c2h::enum_type_list<cub::WarpStoreAlgorithm,\n                      cub::WarpStoreAlgorithm::WARP_STORE_DIRECT,\n                      cub::WarpStoreAlgorithm::WARP_STORE_STRIPED,\n                      cub::WarpStoreAlgorithm::WARP_STORE_TRANSPOSE,\n                      cub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE>;\nusing algorithm = c2h::enum_type_list<cub::WarpStoreAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_store_modifier =\n  c2h::enum_type_list<cub::CacheStoreModifier,\n                      cub::CacheStoreModifier::STORE_DEFAULT,\n                      cub::CacheStoreModifier::STORE_WB,\n                      cub::CacheStoreModifier::STORE_CG,\n                      cub::CacheStoreModifier::STORE_CS,\n                      cub::CacheStoreModifier::STORE_WT,\n                      cub::CacheStoreModifier::STORE_VOLATILE>;\n\nconstexpr int guarded_store_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == cub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads          = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread              = c2h::get<2, TestType>::value;\n  static constexpr cub::WarpStoreAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                   = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                     = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count              = total_warps * tile_size;\n};\n\nC2H_TEST(\"Warp store guarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out              = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, guarded_store_t{valid_items});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store guarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr cub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out = cub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, guarded_store_t{valid_items});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store unguarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n  auto out                  = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, unguarded_store_t{});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store unguarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr cub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n  auto out = cub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, unguarded_store_t{});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\n#if ALGO_TYPE == 3 // cub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE\nC2H_TEST(\"Vectorized warp store with different alignment cases\",\n         \"[store][warp]\",\n         c2h::type_list<std::int32_t>,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out_ref(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n\n  const int offset_for_elements = GENERATE_COPY(0, 1, 2, 3, 4);\n  c2h::device_vector<type> d_out(params::total_item_count + offset_for_elements, type{});\n  auto out = thrust::raw_pointer_cast(d_out.data());\n\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out + offset_for_elements, unguarded_store_t{});\n  thrust::copy_n(d_out.begin() + offset_for_elements, params::total_item_count, d_out_ref.begin());\n\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n\n  REQUIRE(d_expected_output == d_out_ref);\n}\n\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/warp/warp_store.cuh>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/fill_striped.h>\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\n__global__ void warp_store_kernel(OutputIteratorT output_iterator, ActionT action)\n{\n  using warp_store_t = hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t    = typename warp_store_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  T reg[ITEMS_PER_THREAD];\n\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    reg[item] = static_cast<T>(tid * ITEMS_PER_THREAD + item);\n  }\n\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n  warp_store_t store(storage[warp_id]);\n\n  action(store, output_iterator + (warp_id * tile_size), reg);\n}\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename OutputIteratorT,\n          typename ActionT>\nvoid warp_store(OutputIteratorT output_iterator, ActionT action)\n{\n  warp_store_kernel<StoreAlgorithm, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, T, OutputIteratorT, ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(output_iterator, action);\n}\n\nstruct guarded_store_t\n{\n  int valid_items;\n  template <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void operator()(hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n                             OutputIteratorT output,\n                             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg, valid_items);\n  }\n};\n\nstruct unguarded_store_t\n{\n  template <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename OutputIteratorT>\n  __device__ void operator()(hipcub::WarpStore<T, ITEMS_PER_THREAD, StoreAlgorithm, LOGICAL_WARP_THREADS> store,\n                             OutputIteratorT output,\n                             T (&reg)[ITEMS_PER_THREAD])\n  {\n    store.Store(output, reg);\n  }\n};\n\ntemplate <hipcub::WarpStoreAlgorithm StoreAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T>\nc2h::device_vector<T> compute_reference(int valid_items)\n{\n  constexpr int tile_size        = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  constexpr int total_item_count = TOTAL_WARPS * tile_size;\n  c2h::device_vector<T> d_input(total_item_count);\n\n  if constexpr (StoreAlgorithm == hipcub::WarpStoreAlgorithm::WARP_STORE_STRIPED)\n  {\n    c2h::host_vector<T> input(total_item_count);\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD * TOTAL_WARPS>(input.begin());\n    d_input = input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{d_input.size()}, d_input);\n  }\n  if (valid_items != total_item_count)\n  {\n    for (int warp_id = 0; warp_id < TOTAL_WARPS; warp_id++)\n    {\n      thrust::fill(c2h::device_policy,\n                   d_input.begin() + warp_id * tile_size + valid_items,\n                   d_input.begin() + (warp_id + 1) * tile_size,\n                   T{});\n    }\n  }\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms =\n  c2h::enum_type_list<hipcub::WarpStoreAlgorithm,\n                      hipcub::WarpStoreAlgorithm::WARP_STORE_DIRECT,\n                      hipcub::WarpStoreAlgorithm::WARP_STORE_STRIPED,\n                      hipcub::WarpStoreAlgorithm::WARP_STORE_TRANSPOSE,\n                      hipcub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE>;\nusing algorithm = c2h::enum_type_list<hipcub::WarpStoreAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_store_modifier =\n  c2h::enum_type_list<hipcub::CacheStoreModifier,\n                      hipcub::CacheStoreModifier::STORE_DEFAULT,\n                      hipcub::CacheStoreModifier::STORE_WB,\n                      hipcub::CacheStoreModifier::STORE_CG,\n                      hipcub::CacheStoreModifier::STORE_CS,\n                      hipcub::CacheStoreModifier::STORE_WT,\n                      hipcub::CacheStoreModifier::STORE_VOLATILE>;\n\nconstexpr int guarded_store_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == hipcub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads          = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread              = c2h::get<2, TestType>::value;\n  static constexpr hipcub::WarpStoreAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                   = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                     = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count              = total_warps * tile_size;\n};\n\nC2H_TEST(\"Warp store guarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out              = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, guarded_store_t{valid_items});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store guarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr hipcub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  const int valid_items = GENERATE_COPY(take(guarded_store_tests_count, random(0, params::tile_size - 1)));\n  auto out = hipcub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, guarded_store_t{valid_items});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store unguarded range works with pointer\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n  auto out                  = thrust::raw_pointer_cast(d_out.data());\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, unguarded_store_t{});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\nC2H_TEST(\"Warp store unguarded range works with cache modified iterator\",\n         \"[store][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_store_modifier)\n{\n  using params                                     = params_t<TestType>;\n  using type                                       = typename params::type;\n  constexpr hipcub::CacheStoreModifier store_modifier = c2h::get<4, TestType>::value;\n\n  c2h::device_vector<type> d_out(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n  auto out = hipcub::CacheModifiedOutputIterator<store_modifier, type>(thrust::raw_pointer_cast(d_out.data()));\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out, unguarded_store_t{});\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n  REQUIRE(d_expected_output == d_out);\n}\n\n#if ALGO_TYPE == 3 // cub::WarpStoreAlgorithm::WARP_STORE_VECTORIZE\nC2H_TEST(\"Vectorized warp store with different alignment cases\",\n         \"[store][warp]\",\n         c2h::type_list<std::int32_t>,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out_ref(params::total_item_count, type{});\n  constexpr int valid_items = params::tile_size;\n\n  const int offset_for_elements = GENERATE_COPY(0, 1, 2, 3, 4);\n  c2h::device_vector<type> d_out(params::total_item_count + offset_for_elements, type{});\n  auto out = thrust::raw_pointer_cast(d_out.data());\n\n  warp_store<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    out + offset_for_elements, unguarded_store_t{});\n  thrust::copy_n(d_out.begin() + offset_for_elements, params::total_item_count, d_out_ref.begin());\n\n  auto d_expected_output =\n    compute_reference<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n      valid_items);\n\n  REQUIRE(d_expected_output == d_out_ref);\n}\n\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_warp_store.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cuda/__cccl_config>\n\n// We start suppressing deprecation warnings but do not stop at the end of the file. This suppresses warnings in the\n// compiler-generated `catch2_test_iterator.compute_120.cudafe1.stub.c`\n_CCCL_SUPPRESS_DEPRECATED_PUSH\n\n#include <cub/iterator/arg_index_input_iterator.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/tex_obj_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_type.cuh>\n\n#include <cuda/iterator>\n#include <cuda/std/__cccl/dialect.h>\n\n#include <cstdint>\n\n#include <c2h/catch2_test_helper.h>\n\n// %PARAM% TEST_VEC_SIZE types 1:2:3:4\n\n#if TEST_VEC_SIZE == 1\nusing types = c2h::type_list<std::int8_t, std::int16_t, std::int32_t, std::int64_t, float, double>;\n#elif TEST_VEC_SIZE == 2\nusing types = c2h::type_list<char2, short2, int2, long2, longlong2, float2, double2>;\n#elif TEST_VEC_SIZE == 3\nusing types = c2h::type_list<char3, short3, int3, long3, longlong3, float3, double3>;\n#elif TEST_VEC_SIZE == 4\nusing types =\n  c2h::type_list<char4,\n                 short4,\n                 int4,\n                 float4,\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n                 long4_16a,\n                 longlong4_16a,\n                 double4_16a,\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n                 long4,\n                 longlong4,\n                 double4,\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 c2h::custom_type_t<c2h::equal_comparable_t, c2h::accumulateable_t>>;\n#endif\n\ntemplate <typename InputIteratorT, typename T>\n__global__ void test_iterator_kernel(InputIteratorT d_in, T* d_out, InputIteratorT* d_itrs)\n{\n  d_out[0] = *d_in; // Value at offset 0\n  d_out[1] = d_in[100]; // Value at offset 100\n  d_out[2] = *(d_in + 1000); // Value at offset 1000\n  d_out[3] = *(d_in + 10000); // Value at offset 10000\n\n  d_in++;\n  d_out[4] = d_in[0]; // Value at offset 1\n\n  d_in += 20;\n  d_out[5]  = d_in[0]; // Value at offset 21\n  d_itrs[0] = d_in; // Iterator at offset 21\n\n  d_in -= 10;\n  d_out[6] = d_in[0]; // Value at offset 11;\n\n  d_in -= 11;\n  d_out[7]  = d_in[0]; // Value at offset 0\n  d_itrs[1] = d_in; // Iterator at offset 0\n}\n\ntemplate <typename InputIteratorT, typename T>\nvoid test_iterator(InputIteratorT d_in, const c2h::host_vector<T>& h_reference)\n{\n  c2h::device_vector<T> d_out(h_reference.size());\n  c2h::device_vector<InputIteratorT> d_itrs(2, d_in); // TODO(bgruber): using a raw allocation halves the compile time\n                                                      // (nvcc 12.5), because we instantiate a lot of device_vectors\n\n  test_iterator_kernel<<<1, 1>>>(d_in, thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_itrs.data()));\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  c2h::host_vector<InputIteratorT> h_itrs = d_itrs;\n  CHECK(h_reference == c2h::host_vector<T>(d_out)); // comparing host_vectors compiles a lot faster than mixed vectors\n  CHECK(d_in + 21 == h_itrs[0]);\n  CHECK(d_in == h_itrs[1]);\n}\n\nstatic_assert(cuda::std::is_void_v<cub::detail::it_value_t<cub::CacheModifiedOutputIterator<cub::STORE_DEFAULT, int>>>);\n\n// using cache_modifiers =\n//   c2h::enum_type_list<cub::CacheLoadModifier,\n//                       cub::LOAD_DEFAULT,\n//                       cub::LOAD_CA,\n//                       cub::LOAD_CG,\n//                       cub::LOAD_CS,\n//                       cub::LOAD_CV,\n//                       cub::LOAD_LDG,\n//                       cub::LOAD_VOLATILE>;\n//\n// C2H_TEST(\"Test cache modified iterator\", \"[iterator]\", types, cache_modifiers)\n// {\n//   using T                       = c2h::get<0, TestType>;\n//   constexpr auto cache_modifier = c2h::get<1, TestType>::value;\n//   constexpr int TEST_VALUES     = 11000;\n//\n//   c2h::device_vector<T> d_data(TEST_VALUES);\n//   c2h::gen(C2H_SEED(1), d_data);\n//   c2h::host_vector<T> h_data(d_data);\n//\n//   const auto h_reference = c2h::host_vector<T>{\n//     h_data[0], h_data[100], h_data[1000], h_data[10000], h_data[1], h_data[21], h_data[11], h_data[0]};\n//   test_iterator(\n//     cub::CacheModifiedInputIterator<cache_modifier, T>(const_cast<const\n//     T*>(thrust::raw_pointer_cast(d_data.data()))), h_reference);\n// }\n\ntemplate <typename T>\nstruct transform_op_t\n{\n  __host__ __device__ T operator()(T input) const\n  {\n    return input + input;\n  }\n};\n\nC2H_TEST(\"Test tex-obj texture iterator\", \"[iterator]\", types)\n{\n  using T                            = c2h::get<0, TestType>;\n  constexpr unsigned int TEST_VALUES = 11000;\n\n  c2h::device_vector<T> d_data(TEST_VALUES);\n  c2h::gen(C2H_SEED(1), d_data);\n  c2h::host_vector<T> h_data(d_data);\n\n  const auto h_reference = c2h::host_vector<T>{\n    h_data[0], h_data[100], h_data[1000], h_data[10000], h_data[1], h_data[21], h_data[11], h_data[0]};\n  cub::TexObjInputIterator<T> d_obj_itr;\n\n  CubDebugExit(\n    d_obj_itr.BindTexture(const_cast<const T*>(thrust::raw_pointer_cast(d_data.data())), sizeof(T) * TEST_VALUES));\n  test_iterator(d_obj_itr, h_reference);\n}\n\nC2H_TEST(\"Test texture transform iterator\", \"[iterator]\", types)\n{\n  using T                   = c2h::get<0, TestType>;\n  constexpr int TEST_VALUES = 11000;\n\n  c2h::device_vector<T> d_data(TEST_VALUES);\n  c2h::gen(C2H_SEED(1), d_data);\n  c2h::host_vector<T> h_data(d_data.begin(), d_data.end());\n\n  transform_op_t<T> op;\n  const auto h_reference = c2h::host_vector<T>{\n    op(h_data[0]),\n    op(h_data[100]),\n    op(h_data[1000]),\n    op(h_data[10000]),\n    op(h_data[1]),\n    op(h_data[21]),\n    op(h_data[11]),\n    op(h_data[0])};\n\n  using TextureIterator = cub::TexObjInputIterator<T>;\n  TextureIterator d_tex_itr;\n  CubDebugExit(\n    d_tex_itr.BindTexture(const_cast<const T*>(thrust::raw_pointer_cast(d_data.data())), sizeof(T) * TEST_VALUES));\n  cuda::transform_iterator<transform_op_t<T>, TextureIterator> xform_itr(d_tex_itr, op);\n  test_iterator(xform_itr, h_reference);\n  CubDebugExit(d_tex_itr.UnbindTexture());\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/__cccl_config>\n\n// We start suppressing deprecation warnings but do not stop at the end of the file. This suppresses warnings in the\n// compiler-generated `catch2_test_iterator.compute_120.cudafe1.stub.c`\n_CCCL_SUPPRESS_DEPRECATED_PUSH\n\n#include <cub/iterator/arg_index_input_iterator.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/iterator/tex_obj_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_type.cuh>\n\n#include <cuda/iterator>\n#include <cuda/std/__cccl/dialect.h>\n\n#include <cstdint>\n\n#include <c2h/catch2_test_helper.h>\n\n// %PARAM% TEST_VEC_SIZE types 1:2:3:4\n\n#if TEST_VEC_SIZE == 1\nusing types = c2h::type_list<std::int8_t, std::int16_t, std::int32_t, std::int64_t, float, double>;\n#elif TEST_VEC_SIZE == 2\nusing types = c2h::type_list<char2, short2, int2, long2, longlong2, float2, double2>;\n#elif TEST_VEC_SIZE == 3\nusing types = c2h::type_list<char3, short3, int3, long3, longlong3, float3, double3>;\n#elif TEST_VEC_SIZE == 4\nusing types =\n  c2h::type_list<char4,\n                 short4,\n                 int4,\n                 float4,\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n                 long4_16a,\n                 longlong4_16a,\n                 double4_16a,\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n                 long4,\n                 longlong4,\n                 double4,\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 c2h::custom_type_t<c2h::equal_comparable_t, c2h::accumulateable_t>>;\n#endif\n\ntemplate <typename InputIteratorT, typename T>\n__global__ void test_iterator_kernel(InputIteratorT d_in, T* d_out, InputIteratorT* d_itrs)\n{\n  d_out[0] = *d_in; // Value at offset 0\n  d_out[1] = d_in[100]; // Value at offset 100\n  d_out[2] = *(d_in + 1000); // Value at offset 1000\n  d_out[3] = *(d_in + 10000); // Value at offset 10000\n\n  d_in++;\n  d_out[4] = d_in[0]; // Value at offset 1\n\n  d_in += 20;\n  d_out[5]  = d_in[0]; // Value at offset 21\n  d_itrs[0] = d_in; // Iterator at offset 21\n\n  d_in -= 10;\n  d_out[6] = d_in[0]; // Value at offset 11;\n\n  d_in -= 11;\n  d_out[7]  = d_in[0]; // Value at offset 0\n  d_itrs[1] = d_in; // Iterator at offset 0\n}\n\ntemplate <typename InputIteratorT, typename T>\nvoid test_iterator(InputIteratorT d_in, const c2h::host_vector<T>& h_reference)\n{\n  c2h::device_vector<T> d_out(h_reference.size());\n  c2h::device_vector<InputIteratorT> d_itrs(2, d_in); // TODO(bgruber): using a raw allocation halves the compile time\n                                                      // (nvcc 12.5), because we instantiate a lot of device_vectors\n\n  test_iterator_kernel<<<1, 1>>>(d_in, thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_itrs.data()));\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  c2h::host_vector<InputIteratorT> h_itrs = d_itrs;\n  CHECK(h_reference == c2h::host_vector<T>(d_out)); // comparing host_vectors compiles a lot faster than mixed vectors\n  CHECK(d_in + 21 == h_itrs[0]);\n  CHECK(d_in == h_itrs[1]);\n}\n\nstatic_assert(cuda::std::is_void_v<hipcub::detail::it_value_t<hipcub::CacheModifiedOutputIterator<hipcub::STORE_DEFAULT, int>>>);\n\n// using cache_modifiers =\n//   c2h::enum_type_list<cub::CacheLoadModifier,\n//                       cub::LOAD_DEFAULT,\n//                       cub::LOAD_CA,\n//                       cub::LOAD_CG,\n//                       cub::LOAD_CS,\n//                       cub::LOAD_CV,\n//                       cub::LOAD_LDG,\n//                       cub::LOAD_VOLATILE>;\n//\n// C2H_TEST(\"Test cache modified iterator\", \"[iterator]\", types, cache_modifiers)\n// {\n//   using T                       = c2h::get<0, TestType>;\n//   constexpr auto cache_modifier = c2h::get<1, TestType>::value;\n//   constexpr int TEST_VALUES     = 11000;\n//\n//   c2h::device_vector<T> d_data(TEST_VALUES);\n//   c2h::gen(C2H_SEED(1), d_data);\n//   c2h::host_vector<T> h_data(d_data);\n//\n//   const auto h_reference = c2h::host_vector<T>{\n//     h_data[0], h_data[100], h_data[1000], h_data[10000], h_data[1], h_data[21], h_data[11], h_data[0]};\n//   test_iterator(\n//     cub::CacheModifiedInputIterator<cache_modifier, T>(const_cast<const\n//     T*>(thrust::raw_pointer_cast(d_data.data()))), h_reference);\n// }\n\ntemplate <typename T>\nstruct transform_op_t\n{\n  __host__ __device__ T operator()(T input) const\n  {\n    return input + input;\n  }\n};\n\nC2H_TEST(\"Test tex-obj texture iterator\", \"[iterator]\", types)\n{\n  using T                            = c2h::get<0, TestType>;\n  constexpr unsigned int TEST_VALUES = 11000;\n\n  c2h::device_vector<T> d_data(TEST_VALUES);\n  c2h::gen(C2H_SEED(1), d_data);\n  c2h::host_vector<T> h_data(d_data);\n\n  const auto h_reference = c2h::host_vector<T>{\n    h_data[0], h_data[100], h_data[1000], h_data[10000], h_data[1], h_data[21], h_data[11], h_data[0]};\n  hipcub::TexObjInputIterator<T> d_obj_itr;\n\n  CubDebugExit(\n    d_obj_itr.BindTexture(const_cast<const T*>(thrust::raw_pointer_cast(d_data.data())), sizeof(T) * TEST_VALUES));\n  test_iterator(d_obj_itr, h_reference);\n}\n\nC2H_TEST(\"Test texture transform iterator\", \"[iterator]\", types)\n{\n  using T                   = c2h::get<0, TestType>;\n  constexpr int TEST_VALUES = 11000;\n\n  c2h::device_vector<T> d_data(TEST_VALUES);\n  c2h::gen(C2H_SEED(1), d_data);\n  c2h::host_vector<T> h_data(d_data.begin(), d_data.end());\n\n  transform_op_t<T> op;\n  const auto h_reference = c2h::host_vector<T>{\n    op(h_data[0]),\n    op(h_data[100]),\n    op(h_data[1000]),\n    op(h_data[10000]),\n    op(h_data[1]),\n    op(h_data[21]),\n    op(h_data[11]),\n    op(h_data[0])};\n\n  using TextureIterator = hipcub::TexObjInputIterator<T>;\n  TextureIterator d_tex_itr;\n  CubDebugExit(\n    d_tex_itr.BindTexture(const_cast<const T*>(thrust::raw_pointer_cast(d_data.data())), sizeof(T) * TEST_VALUES));\n  cuda::transform_iterator<transform_op_t<T>, TextureIterator> xform_itr(d_tex_itr, op);\n  test_iterator(xform_itr, h_reference);\n  CubDebugExit(d_tex_itr.UnbindTexture());\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_iterator.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/config.cuh>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/util_device.cuh>\n#include <cub/util_type.cuh>\n#include <cub/util_vsmem.cuh>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\n//----------------------------------------------------------------------------\n// Helper section\n//----------------------------------------------------------------------------\ntemplate <int Size>\nstruct large_custom_t\n{\n  uint8_t data[Size];\n};\n\nstruct kernel_test_info_t\n{\n  // Whether the kernel is using virtual shared memory\n  bool uses_vsmem_ptr;\n  // Whether the kernel is using the fallback agent\n  bool uses_fallback_agent;\n  // Whether the kernel is using the fallback policy\n  bool uses_fallback_policy;\n};\n\nstruct launch_config_test_info_t\n{\n  // The tile size that's assumed during launch configuration\n  std::size_t config_assumes_tile_size;\n  // The block size that's assumed during launch configuration\n  std::size_t config_assumes_threads_per_block;\n  // The total amount of virtual shared memory that has to be allocated\n  std::size_t config_vsmem_per_block;\n};\n\n// CUB_NAMESPACE_BEGIN\n\n//----------------------------------------------------------------------------\n// Tuning policy definition\n//----------------------------------------------------------------------------\ntemplate <int ThreadsPerBlock, int ItemsPerThread>\nstruct agent_dummy_algorithm_policy_t\n{\n  static constexpr int ITEMS_PER_THREAD = ItemsPerThread;\n  static constexpr int BLOCK_THREADS    = ThreadsPerBlock;\n};\n\n//----------------------------------------------------------------------------\n// Agent template definition\n//----------------------------------------------------------------------------\ntemplate <typename ActivePolicyT, typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nstruct agent_dummy_algorithm_t\n{\n  static constexpr auto threads_per_block = ActivePolicyT::BLOCK_THREADS;\n  static constexpr auto items_per_thread  = ActivePolicyT::ITEMS_PER_THREAD;\n  static constexpr auto tile_size         = threads_per_block * items_per_thread;\n\n  using item_t = cub::detail::it_value_t<InputIteratorT>;\n\n  using block_load_t = cub::BlockLoad<item_t, threads_per_block, items_per_thread, cub::BLOCK_LOAD_TRANSPOSE>;\n\n  using block_store_t = cub::BlockStore<item_t, threads_per_block, items_per_thread, cub::BLOCK_STORE_TRANSPOSE>;\n\n  // We are intentionally not aliasing the TempStorage here to double the required shared memory of the test and be able\n  // to use a smaller `large_custom_t`, as we experienced slow compilation times for large a `large_custom_t`.\n  struct _temp_storage_t\n  {\n    typename block_load_t::TempStorage load;\n    typename block_store_t::TempStorage store;\n  };\n\n  struct TempStorage : cub::Uninitialized<_temp_storage_t>\n  {};\n\n  _temp_storage_t& temp_storage; ///< Reference to temp_storage\n  InputIteratorT d_in; ///< Input data\n  OutputIteratorT d_out; ///< Output data\n\n  __device__ __forceinline__\n  agent_dummy_algorithm_t(TempStorage& temp_storage, InputIteratorT d_in, OutputIteratorT d_out)\n      : temp_storage(temp_storage.Alias())\n      , d_in(d_in)\n      , d_out(d_out)\n  {}\n\n  __device__ __forceinline__ void consume_tile(int tile_idx, OffsetT total_num_items)\n  {\n    const OffsetT tile_offset   = static_cast<OffsetT>(tile_idx) * static_cast<OffsetT>(tile_size);\n    const OffsetT num_remaining = total_num_items - tile_offset;\n\n    // Load items into a blocked arrangement\n    item_t items[items_per_thread]{};\n    block_load_t(temp_storage.load).Load(d_in + tile_offset, items, num_remaining);\n\n    // Store items from blocked arrangement\n    block_store_t(temp_storage.store).Store(d_out + tile_offset, items, num_remaining);\n  }\n};\n\n//----------------------------------------------------------------------------\n// Kernel template definition\n//----------------------------------------------------------------------------\ntemplate <typename ChainedPolicyT, typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nvoid __global__ __launch_bounds__(\n  cub::detail::vsmem_helper_fallback_policy_t<\n    typename ChainedPolicyT::ActivePolicy::DummyAlgorithmPolicy,\n    typename ChainedPolicyT::ActivePolicy::FallbackDummyAlgorithmPolicy,\n    agent_dummy_algorithm_t,\n    InputIteratorT,\n    OutputIteratorT,\n    OffsetT>::agent_policy_t::BLOCK_THREADS)\n  dummy_algorithm_kernel(\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    cub::detail::vsmem_t vsmem)\n{\n  using active_policy_t   = typename ChainedPolicyT::ActivePolicy;\n  using default_policy_t  = typename active_policy_t::DummyAlgorithmPolicy;\n  using fallback_policy_t = typename active_policy_t::FallbackDummyAlgorithmPolicy;\n  using fallback_agent_t  = agent_dummy_algorithm_t<fallback_policy_t, InputIteratorT, OutputIteratorT, OffsetT>;\n\n  using vsmem_helper_t = cub::detail::vsmem_helper_fallback_policy_t<\n    default_policy_t,\n    fallback_policy_t,\n    agent_dummy_algorithm_t,\n    InputIteratorT,\n    OutputIteratorT,\n    OffsetT>;\n\n  using agent_t = typename vsmem_helper_t::agent_t;\n\n  // Static shared memory allocation\n  __shared__ typename vsmem_helper_t::static_temp_storage_t static_temp_storage;\n\n  // Get temporary storage\n  typename agent_t::TempStorage& temp_storage = vsmem_helper_t::get_temp_storage(static_temp_storage, vsmem);\n\n  // Populate test meta data\n  kernel_test_info->uses_vsmem_ptr =\n    (reinterpret_cast<char*>(&temp_storage)\n     == (static_cast<char*>(vsmem.gmem_ptr) + (blockIdx.x * vsmem_helper_t::vsmem_per_block)));\n  kernel_test_info->uses_fallback_agent = cuda::std::is_same_v<typename vsmem_helper_t::agent_t, fallback_agent_t>;\n  kernel_test_info->uses_fallback_policy =\n    cuda::std::is_same_v<typename vsmem_helper_t::agent_policy_t, fallback_policy_t>;\n\n  // Instantiate the algorithm's agent\n  agent_t agent(temp_storage, d_in, d_out);\n\n  // Process this thread block's tile\n  agent.consume_tile(blockIdx.x, num_items);\n\n  // If applicable, hints to discard modified cache lines for vsmem\n  vsmem_helper_t::discard_temp_storage(temp_storage);\n}\n\n//----------------------------------------------------------------------------\n// Tuning policy chain\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT>\nstruct device_dummy_algorithm_policy_t\n{\n  using item_t = cub::detail::it_value_t<InputIteratorT>;\n\n  static constexpr int FALLBACK_BLOCK_THREADS = 64;\n\n  struct policy_500 : cub::ChainedPolicy<500, policy_500, policy_500>\n  {\n    using DummyAlgorithmPolicy = agent_dummy_algorithm_policy_t<256, cub::Nominal4BItemsToItems<item_t>(17)>;\n\n    // The fallback policy that's used if there's insufficient shared memory for the default policy,\n    // yet still sufficient memory for the fallback policy\n    using FallbackDummyAlgorithmPolicy = cub::detail::policy_wrapper_t<DummyAlgorithmPolicy, FALLBACK_BLOCK_THREADS>;\n  };\n\n  /// MaxPolicy\n  using max_policy_t = policy_500;\n};\n\n//----------------------------------------------------------------------------\n// Dispatch layer\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename OffsetT,\n          typename PolicyHub = device_dummy_algorithm_policy_t<InputIteratorT>>\nstruct dispatch_dummy_algorithm_t\n{\n  using item_t = cub::detail::it_value_t<InputIteratorT>;\n\n  /// Device-accessible allocation of temporary storage. When nullptr, the required\n  /// allocation size is written to \\p temp_storage_bytes and no work is done.\n  void* d_temp_storage;\n\n  /// Reference to size in bytes of \\p d_temp_storage allocation\n  std::size_t& temp_storage_bytes;\n\n  InputIteratorT d_in;\n  OutputIteratorT d_out;\n  OffsetT num_items;\n  kernel_test_info_t* kernel_test_info;\n  launch_config_test_info_t* launch_config_info;\n  cudaStream_t stream;\n  int ptx_version;\n\n  CUB_RUNTIME_FUNCTION __forceinline__ dispatch_dummy_algorithm_t(\n    void* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    launch_config_test_info_t* launch_config_info,\n    cudaStream_t stream,\n    int ptx_version)\n      : d_temp_storage(d_temp_storage)\n      , temp_storage_bytes(temp_storage_bytes)\n      , d_in(d_in)\n      , d_out(d_out)\n      , num_items(num_items)\n      , kernel_test_info(kernel_test_info)\n      , launch_config_info(launch_config_info)\n      , stream(stream)\n      , ptx_version(ptx_version)\n  {}\n\n  /**\n   * @brief During compilation, CUB's dispatch mechanism (more specifically, `cub::ChainedPolicy`)\n   * instantiates the `Invoke` function template for *all* tunings policies that are defined in the\n   * algorithm's chain of tuning policies. At runtime, when an algorithm is invoked, of all the\n   * instantiated `Invoke`-function templates, `cub::ChainedPolicy` makes sure to *call* only the\n   * first tuning policy whose PTX version is less-than-or-equal to the GPU's SM version to which\n   * the algorithm is dispatched.\n   * Since the `Invoke`function template is instantiated for *all* tunings policies in the chain, we\n   * want to avoid making any of the algorithm's kernel template parameters depend on the\n   * `ActivePolicyT` template argument of the `Invoke` function template, as that would result in\n   * multiple kernel template instances.\n   */\n  template <typename ActivePolicyT>\n  CUB_RUNTIME_FUNCTION __forceinline__ cudaError_t Invoke()\n  {\n    using vsmem_helper_t = cub::detail::vsmem_helper_fallback_policy_t<\n      typename ActivePolicyT::DummyAlgorithmPolicy,\n      typename ActivePolicyT::FallbackDummyAlgorithmPolicy,\n      agent_dummy_algorithm_t,\n      InputIteratorT,\n      OutputIteratorT,\n      OffsetT>;\n\n    // Empty problem size\n    if (num_items == 0)\n    {\n      if (d_temp_storage)\n      {\n        return cudaSuccess;\n      }\n      else\n      {\n        temp_storage_bytes = 0;\n        return cudaSuccess;\n      }\n    }\n\n    // Compute launch configurations\n    constexpr auto threads_per_block = vsmem_helper_t::agent_policy_t::BLOCK_THREADS;\n    constexpr auto items_per_thread  = vsmem_helper_t::agent_policy_t::ITEMS_PER_THREAD;\n    constexpr auto tile_size         = threads_per_block * items_per_thread;\n    const auto num_tiles             = cuda::ceil_div(num_items, tile_size);\n    const auto total_vsmem           = num_tiles * vsmem_helper_t::vsmem_per_block;\n\n    // Get device ordinal\n    cudaError error = cudaSuccess;\n\n    // Compute temporary storage requirements\n    void* allocations[1]            = {nullptr};\n    std::size_t allocation_sizes[1] = {total_vsmem};\n    error = cub::detail::alias_temporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes);\n    if (cudaSuccess != error)\n    {\n      return error;\n    }\n\n    // Return if the caller is simply requesting the size of the storage allocation\n    if (d_temp_storage == nullptr)\n    {\n      return error;\n    }\n    launch_config_info->config_assumes_tile_size         = static_cast<std::size_t>(tile_size);\n    launch_config_info->config_assumes_threads_per_block = static_cast<std::size_t>(threads_per_block);\n    launch_config_info->config_vsmem_per_block           = vsmem_helper_t::vsmem_per_block;\n\n    THRUST_NS_QUALIFIER::cuda_cub::detail::triple_chevron(num_tiles, threads_per_block, 0, stream)\n      .doit(dummy_algorithm_kernel<typename PolicyHub::max_policy_t, InputIteratorT, OutputIteratorT, OffsetT>,\n            d_in,\n            d_out,\n            num_items,\n            kernel_test_info,\n            cub::detail::vsmem_t{allocations[0]});\n    return cudaPeekAtLastError();\n  }\n\n  /**\n   * @brief Static member function as the entry point for algorithm dispatch\n   */\n  CUB_RUNTIME_FUNCTION static cudaError_t dispatch(\n    void* d_temp_storage,\n    size_t& temp_storage_bytes,\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    launch_config_test_info_t* launch_config_info,\n    cudaStream_t stream = nullptr)\n  {\n    // Get PTX version\n    int ptx_version = 0;\n    cudaError error = cub::PtxVersion(ptx_version);\n    if (cudaSuccess != error)\n    {\n      return error;\n    }\n\n    // Create dispatch functor\n    dispatch_dummy_algorithm_t dispatch(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_in,\n      d_out,\n      num_items,\n      kernel_test_info,\n      launch_config_info,\n      stream,\n      ptx_version);\n\n    // Dispatch to chained policy\n    error = PolicyHub::max_policy_t::Invoke(ptx_version, dispatch);\n    if (cudaSuccess != error)\n    {\n      return error;\n    }\n\n    return error;\n  }\n};\n\n//----------------------------------------------------------------------------\n// Device-scope interface layer\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nCUB_RUNTIME_FUNCTION static cudaError_t device_dummy_algorithm(\n  void* d_temp_storage,\n  size_t& temp_storage_bytes,\n  InputIteratorT d_in,\n  OutputIteratorT d_out,\n  OffsetT num_items,\n  kernel_test_info_t* kernel_test_info,\n  launch_config_test_info_t* launch_config_info,\n  cudaStream_t stream = nullptr)\n{\n  using dispatch_dummy_algorithm_t = dispatch_dummy_algorithm_t<InputIteratorT, OutputIteratorT, OffsetT>;\n  return dispatch_dummy_algorithm_t::dispatch(\n    d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, kernel_test_info, launch_config_info, stream);\n}\n\n// CUB_NAMESPACE_END\n\nDECLARE_LAUNCH_WRAPPER(device_dummy_algorithm, dummy_algorithm);\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\nusing type_list = c2h::type_list<large_custom_t<1>, large_custom_t<80>, large_custom_t<128>, large_custom_t<512>>;\n\nC2H_TEST(\"Virtual shared memory works within algorithms\", \"[util][vsmem]\", type_list)\n{\n  using item_t   = typename c2h::get<0, TestType>;\n  using offset_t = int32_t;\n\n  constexpr offset_t target_size = 10000000;\n  const offset_t num_items       = target_size / sizeof(item_t);\n\n  // Prepare input and output buffers for a simple copy algorithm test\n  c2h::device_vector<uint8_t> in(num_items * sizeof(item_t));\n  c2h::device_vector<uint8_t> out(num_items * sizeof(item_t));\n  auto const in_ptr  = reinterpret_cast<item_t*>(thrust::raw_pointer_cast(in.data()));\n  auto const out_ptr = reinterpret_cast<item_t*>(thrust::raw_pointer_cast(out.data()));\n\n  // Generate some random noise input data\n  c2h::gen(C2H_SEED(1), in);\n\n  // Query default and fallback policies and agents so we can confirm vsmem\n  using default_policy_t  = typename device_dummy_algorithm_policy_t<item_t*>::policy_500::DummyAlgorithmPolicy;\n  using default_agent_t   = agent_dummy_algorithm_t<default_policy_t, item_t*, item_t*, offset_t>;\n  using fallback_policy_t = typename device_dummy_algorithm_policy_t<item_t*>::policy_500::FallbackDummyAlgorithmPolicy;\n  using fallback_agent_t  = agent_dummy_algorithm_t<fallback_policy_t, item_t*, item_t*, offset_t>;\n\n  // Get the information as it is expected from the vsmem helper to work as expected\n  std::size_t default_smem_size  = sizeof(typename default_agent_t::TempStorage);\n  std::size_t fallback_smem_size = sizeof(typename fallback_agent_t::TempStorage);\n  bool expected_to_use_fallback =\n    default_smem_size > cub::detail::max_smem_per_block && fallback_smem_size <= cub::detail::max_smem_per_block;\n  std::size_t expected_smem_per_block = expected_to_use_fallback ? fallback_smem_size : default_smem_size;\n  bool expected_needs_vsmem           = expected_smem_per_block > cub::detail::max_smem_per_block;\n  std::size_t expected_threads_per_block =\n    expected_to_use_fallback ? fallback_policy_t::BLOCK_THREADS : default_policy_t::BLOCK_THREADS;\n  std::size_t expected_items_per_thread =\n    expected_to_use_fallback ? fallback_policy_t::ITEMS_PER_THREAD : default_policy_t::ITEMS_PER_THREAD;\n  std::size_t expected_tile_size       = expected_threads_per_block * expected_items_per_thread;\n  std::size_t expected_vsmem_per_block = (expected_needs_vsmem ? expected_smem_per_block : 0ULL);\n\n  // Setup vsmem test\n  launch_config_test_info_t* launch_config_info = nullptr;\n  cudaMallocHost(&launch_config_info, sizeof(launch_config_test_info_t));\n  c2h::device_vector<kernel_test_info_t> device_kernel_test_info(1);\n  dummy_algorithm(\n    in_ptr, out_ptr, num_items, thrust::raw_pointer_cast(device_kernel_test_info.data()), launch_config_info);\n\n  // Make sure the algorithm worked correctly\n  REQUIRE(in == out);\n\n  // Make sure the kernel information retrieved from the vsmem helper is correct\n  c2h::host_vector<kernel_test_info_t> kernel_test_info = device_kernel_test_info;\n  REQUIRE(kernel_test_info[0].uses_vsmem_ptr == expected_needs_vsmem);\n  REQUIRE(kernel_test_info[0].uses_fallback_agent == expected_to_use_fallback);\n  REQUIRE(kernel_test_info[0].uses_fallback_policy == expected_to_use_fallback);\n\n  // Make sure the launch configuration information retrieved from the vsmem helper is correct\n  REQUIRE(launch_config_info->config_assumes_tile_size == expected_tile_size);\n  REQUIRE(launch_config_info->config_assumes_threads_per_block == expected_threads_per_block);\n  if (expected_vsmem_per_block == 0)\n  {\n    REQUIRE(launch_config_info->config_vsmem_per_block == 0);\n  }\n  else\n  {\n    // The virtual shared memory helper pads vsmem to a multiple of a line size, hence the range check\n    REQUIRE(launch_config_info->config_vsmem_per_block >= expected_vsmem_per_block);\n  }\n\n  cudaFreeHost(launch_config_info);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/config.cuh>\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/util_device.cuh>\n#include <cub/util_type.cuh>\n#include <cub/util_vsmem.cuh>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\n//----------------------------------------------------------------------------\n// Helper section\n//----------------------------------------------------------------------------\ntemplate <int Size>\nstruct large_custom_t\n{\n  uint8_t data[Size];\n};\n\nstruct kernel_test_info_t\n{\n  // Whether the kernel is using virtual shared memory\n  bool uses_vsmem_ptr;\n  // Whether the kernel is using the fallback agent\n  bool uses_fallback_agent;\n  // Whether the kernel is using the fallback policy\n  bool uses_fallback_policy;\n};\n\nstruct launch_config_test_info_t\n{\n  // The tile size that's assumed during launch configuration\n  std::size_t config_assumes_tile_size;\n  // The block size that's assumed during launch configuration\n  std::size_t config_assumes_threads_per_block;\n  // The total amount of virtual shared memory that has to be allocated\n  std::size_t config_vsmem_per_block;\n};\n\n// CUB_NAMESPACE_BEGIN\n\n//----------------------------------------------------------------------------\n// Tuning policy definition\n//----------------------------------------------------------------------------\ntemplate <int ThreadsPerBlock, int ItemsPerThread>\nstruct agent_dummy_algorithm_policy_t\n{\n  static constexpr int ITEMS_PER_THREAD = ItemsPerThread;\n  static constexpr int BLOCK_THREADS    = ThreadsPerBlock;\n};\n\n//----------------------------------------------------------------------------\n// Agent template definition\n//----------------------------------------------------------------------------\ntemplate <typename ActivePolicyT, typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nstruct agent_dummy_algorithm_t\n{\n  static constexpr auto threads_per_block = ActivePolicyT::BLOCK_THREADS;\n  static constexpr auto items_per_thread  = ActivePolicyT::ITEMS_PER_THREAD;\n  static constexpr auto tile_size         = threads_per_block * items_per_thread;\n\n  using item_t = hipcub::detail::it_value_t<InputIteratorT>;\n\n  using block_load_t = hipcub::BlockLoad<item_t, threads_per_block, items_per_thread, hipcub::BLOCK_LOAD_TRANSPOSE>;\n\n  using block_store_t = hipcub::BlockStore<item_t, threads_per_block, items_per_thread, hipcub::BLOCK_STORE_TRANSPOSE>;\n\n  // We are intentionally not aliasing the TempStorage here to double the required shared memory of the test and be able\n  // to use a smaller `large_custom_t`, as we experienced slow compilation times for large a `large_custom_t`.\n  struct _temp_storage_t\n  {\n    typename block_load_t::TempStorage load;\n    typename block_store_t::TempStorage store;\n  };\n\n  struct TempStorage : hipcub::Uninitialized<_temp_storage_t>\n  {};\n\n  _temp_storage_t& temp_storage; ///< Reference to temp_storage\n  InputIteratorT d_in; ///< Input data\n  OutputIteratorT d_out; ///< Output data\n\n  __device__ __forceinline__\n  agent_dummy_algorithm_t(TempStorage& temp_storage, InputIteratorT d_in, OutputIteratorT d_out)\n      : temp_storage(temp_storage.Alias())\n      , d_in(d_in)\n      , d_out(d_out)\n  {}\n\n  __device__ __forceinline__ void consume_tile(int tile_idx, OffsetT total_num_items)\n  {\n    const OffsetT tile_offset   = static_cast<OffsetT>(tile_idx) * static_cast<OffsetT>(tile_size);\n    const OffsetT num_remaining = total_num_items - tile_offset;\n\n    // Load items into a blocked arrangement\n    item_t items[items_per_thread]{};\n    block_load_t(temp_storage.load).Load(d_in + tile_offset, items, num_remaining);\n\n    // Store items from blocked arrangement\n    block_store_t(temp_storage.store).Store(d_out + tile_offset, items, num_remaining);\n  }\n};\n\n//----------------------------------------------------------------------------\n// Kernel template definition\n//----------------------------------------------------------------------------\ntemplate <typename ChainedPolicyT, typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nvoid __global__ __launch_bounds__(\n  hipcub::detail::vsmem_helper_fallback_policy_t<\n    typename ChainedPolicyT::ActivePolicy::DummyAlgorithmPolicy,\n    typename ChainedPolicyT::ActivePolicy::FallbackDummyAlgorithmPolicy,\n    agent_dummy_algorithm_t,\n    InputIteratorT,\n    OutputIteratorT,\n    OffsetT>::agent_policy_t::BLOCK_THREADS)\n  dummy_algorithm_kernel(\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    hipcub::detail::vsmem_t vsmem)\n{\n  using active_policy_t   = typename ChainedPolicyT::ActivePolicy;\n  using default_policy_t  = typename active_policy_t::DummyAlgorithmPolicy;\n  using fallback_policy_t = typename active_policy_t::FallbackDummyAlgorithmPolicy;\n  using fallback_agent_t  = agent_dummy_algorithm_t<fallback_policy_t, InputIteratorT, OutputIteratorT, OffsetT>;\n\n  using vsmem_helper_t = hipcub::detail::vsmem_helper_fallback_policy_t<\n    default_policy_t,\n    fallback_policy_t,\n    agent_dummy_algorithm_t,\n    InputIteratorT,\n    OutputIteratorT,\n    OffsetT>;\n\n  using agent_t = typename vsmem_helper_t::agent_t;\n\n  // Static shared memory allocation\n  __shared__ typename vsmem_helper_t::static_temp_storage_t static_temp_storage;\n\n  // Get temporary storage\n  typename agent_t::TempStorage& temp_storage = vsmem_helper_t::get_temp_storage(static_temp_storage, vsmem);\n\n  // Populate test meta data\n  kernel_test_info->uses_vsmem_ptr =\n    (reinterpret_cast<char*>(&temp_storage)\n     == (static_cast<char*>(vsmem.gmem_ptr) + (blockIdx.x * vsmem_helper_t::vsmem_per_block)));\n  kernel_test_info->uses_fallback_agent = cuda::std::is_same_v<typename vsmem_helper_t::agent_t, fallback_agent_t>;\n  kernel_test_info->uses_fallback_policy =\n    cuda::std::is_same_v<typename vsmem_helper_t::agent_policy_t, fallback_policy_t>;\n\n  // Instantiate the algorithm's agent\n  agent_t agent(temp_storage, d_in, d_out);\n\n  // Process this thread block's tile\n  agent.consume_tile(blockIdx.x, num_items);\n\n  // If applicable, hints to discard modified cache lines for vsmem\n  vsmem_helper_t::discard_temp_storage(temp_storage);\n}\n\n//----------------------------------------------------------------------------\n// Tuning policy chain\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT>\nstruct device_dummy_algorithm_policy_t\n{\n  using item_t = hipcub::detail::it_value_t<InputIteratorT>;\n\n  static constexpr int FALLBACK_BLOCK_THREADS = 64;\n\n  struct policy_500 : hipcub::ChainedPolicy<500, policy_500, policy_500>\n  {\n    using DummyAlgorithmPolicy = agent_dummy_algorithm_policy_t<256, hipcub::Nominal4BItemsToItems<item_t>(17)>;\n\n    // The fallback policy that's used if there's insufficient shared memory for the default policy,\n    // yet still sufficient memory for the fallback policy\n    using FallbackDummyAlgorithmPolicy = hipcub::detail::policy_wrapper_t<DummyAlgorithmPolicy, FALLBACK_BLOCK_THREADS>;\n  };\n\n  /// MaxPolicy\n  using max_policy_t = policy_500;\n};\n\n//----------------------------------------------------------------------------\n// Dispatch layer\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          typename OffsetT,\n          typename PolicyHub = device_dummy_algorithm_policy_t<InputIteratorT>>\nstruct dispatch_dummy_algorithm_t\n{\n  using item_t = hipcub::detail::it_value_t<InputIteratorT>;\n\n  /// Device-accessible allocation of temporary storage. When nullptr, the required\n  /// allocation size is written to \\p temp_storage_bytes and no work is done.\n  void* d_temp_storage;\n\n  /// Reference to size in bytes of \\p d_temp_storage allocation\n  std::size_t& temp_storage_bytes;\n\n  InputIteratorT d_in;\n  OutputIteratorT d_out;\n  OffsetT num_items;\n  kernel_test_info_t* kernel_test_info;\n  launch_config_test_info_t* launch_config_info;\n  hipStream_t stream;\n  int ptx_version;\n\n  HIPCUB_RUNTIME_FUNCTION __forceinline__ dispatch_dummy_algorithm_t(\n    void* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    launch_config_test_info_t* launch_config_info,\n    hipStream_t stream,\n    int ptx_version)\n      : d_temp_storage(d_temp_storage)\n      , temp_storage_bytes(temp_storage_bytes)\n      , d_in(d_in)\n      , d_out(d_out)\n      , num_items(num_items)\n      , kernel_test_info(kernel_test_info)\n      , launch_config_info(launch_config_info)\n      , stream(stream)\n      , ptx_version(ptx_version)\n  {}\n\n  /**\n   * @brief During compilation, CUB's dispatch mechanism (more specifically, `cub::ChainedPolicy`)\n   * instantiates the `Invoke` function template for *all* tunings policies that are defined in the\n   * algorithm's chain of tuning policies. At runtime, when an algorithm is invoked, of all the\n   * instantiated `Invoke`-function templates, `cub::ChainedPolicy` makes sure to *call* only the\n   * first tuning policy whose PTX version is less-than-or-equal to the GPU's SM version to which\n   * the algorithm is dispatched.\n   * Since the `Invoke`function template is instantiated for *all* tunings policies in the chain, we\n   * want to avoid making any of the algorithm's kernel template parameters depend on the\n   * `ActivePolicyT` template argument of the `Invoke` function template, as that would result in\n   * multiple kernel template instances.\n   */\n  template <typename ActivePolicyT>\n  HIPCUB_RUNTIME_FUNCTION __forceinline__ hipError_t Invoke()\n  {\n    using vsmem_helper_t = hipcub::detail::vsmem_helper_fallback_policy_t<\n      typename ActivePolicyT::DummyAlgorithmPolicy,\n      typename ActivePolicyT::FallbackDummyAlgorithmPolicy,\n      agent_dummy_algorithm_t,\n      InputIteratorT,\n      OutputIteratorT,\n      OffsetT>;\n\n    // Empty problem size\n    if (num_items == 0)\n    {\n      if (d_temp_storage)\n      {\n        return hipSuccess;\n      }\n      else\n      {\n        temp_storage_bytes = 0;\n        return hipSuccess;\n      }\n    }\n\n    // Compute launch configurations\n    constexpr auto threads_per_block = vsmem_helper_t::agent_policy_t::BLOCK_THREADS;\n    constexpr auto items_per_thread  = vsmem_helper_t::agent_policy_t::ITEMS_PER_THREAD;\n    constexpr auto tile_size         = threads_per_block * items_per_thread;\n    const auto num_tiles             = cuda::ceil_div(num_items, tile_size);\n    const auto total_vsmem           = num_tiles * vsmem_helper_t::vsmem_per_block;\n\n    // Get device ordinal\n    hipError_t error = hipSuccess;\n\n    // Compute temporary storage requirements\n    void* allocations[1]            = {nullptr};\n    std::size_t allocation_sizes[1] = {total_vsmem};\n    error = hipcub::detail::alias_temporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes);\n    if (hipSuccess != error)\n    {\n      return error;\n    }\n\n    // Return if the caller is simply requesting the size of the storage allocation\n    if (d_temp_storage == nullptr)\n    {\n      return error;\n    }\n    launch_config_info->config_assumes_tile_size         = static_cast<std::size_t>(tile_size);\n    launch_config_info->config_assumes_threads_per_block = static_cast<std::size_t>(threads_per_block);\n    launch_config_info->config_vsmem_per_block           = vsmem_helper_t::vsmem_per_block;\n\n    THRUST_NS_QUALIFIER::cuda_cub::detail::triple_chevron(num_tiles, threads_per_block, 0, stream)\n      .doit(dummy_algorithm_kernel<typename PolicyHub::max_policy_t, InputIteratorT, OutputIteratorT, OffsetT>,\n            d_in,\n            d_out,\n            num_items,\n            kernel_test_info,\n            hipcub::detail::vsmem_t{allocations[0]});\n    return hipPeekAtLastError();\n  }\n\n  /**\n   * @brief Static member function as the entry point for algorithm dispatch\n   */\n  HIPCUB_RUNTIME_FUNCTION static hipError_t dispatch(\n    void* d_temp_storage,\n    size_t& temp_storage_bytes,\n    InputIteratorT d_in,\n    OutputIteratorT d_out,\n    OffsetT num_items,\n    kernel_test_info_t* kernel_test_info,\n    launch_config_test_info_t* launch_config_info,\n    hipStream_t stream = nullptr)\n  {\n    // Get PTX version\n    int ptx_version = 0;\n    hipError_t error = hipcub::PtxVersion(ptx_version);\n    if (hipSuccess != error)\n    {\n      return error;\n    }\n\n    // Create dispatch functor\n    dispatch_dummy_algorithm_t dispatch(\n      d_temp_storage,\n      temp_storage_bytes,\n      d_in,\n      d_out,\n      num_items,\n      kernel_test_info,\n      launch_config_info,\n      stream,\n      ptx_version);\n\n    // Dispatch to chained policy\n    error = PolicyHub::max_policy_t::Invoke(ptx_version, dispatch);\n    if (hipSuccess != error)\n    {\n      return error;\n    }\n\n    return error;\n  }\n};\n\n//----------------------------------------------------------------------------\n// Device-scope interface layer\n//----------------------------------------------------------------------------\ntemplate <typename InputIteratorT, typename OutputIteratorT, typename OffsetT>\nHIPCUB_RUNTIME_FUNCTION static hipError_t device_dummy_algorithm(\n  void* d_temp_storage,\n  size_t& temp_storage_bytes,\n  InputIteratorT d_in,\n  OutputIteratorT d_out,\n  OffsetT num_items,\n  kernel_test_info_t* kernel_test_info,\n  launch_config_test_info_t* launch_config_info,\n  hipStream_t stream = nullptr)\n{\n  using dispatch_dummy_algorithm_t = dispatch_dummy_algorithm_t<InputIteratorT, OutputIteratorT, OffsetT>;\n  return dispatch_dummy_algorithm_t::dispatch(\n    d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, kernel_test_info, launch_config_info, stream);\n}\n\n// CUB_NAMESPACE_END\n\nDECLARE_LAUNCH_WRAPPER(device_dummy_algorithm, dummy_algorithm);\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\nusing type_list = c2h::type_list<large_custom_t<1>, large_custom_t<80>, large_custom_t<128>, large_custom_t<512>>;\n\nC2H_TEST(\"Virtual shared memory works within algorithms\", \"[util][vsmem]\", type_list)\n{\n  using item_t   = typename c2h::get<0, TestType>;\n  using offset_t = int32_t;\n\n  constexpr offset_t target_size = 10000000;\n  const offset_t num_items       = target_size / sizeof(item_t);\n\n  // Prepare input and output buffers for a simple copy algorithm test\n  c2h::device_vector<uint8_t> in(num_items * sizeof(item_t));\n  c2h::device_vector<uint8_t> out(num_items * sizeof(item_t));\n  auto const in_ptr  = reinterpret_cast<item_t*>(thrust::raw_pointer_cast(in.data()));\n  auto const out_ptr = reinterpret_cast<item_t*>(thrust::raw_pointer_cast(out.data()));\n\n  // Generate some random noise input data\n  c2h::gen(C2H_SEED(1), in);\n\n  // Query default and fallback policies and agents so we can confirm vsmem\n  using default_policy_t  = typename device_dummy_algorithm_policy_t<item_t*>::policy_500::DummyAlgorithmPolicy;\n  using default_agent_t   = agent_dummy_algorithm_t<default_policy_t, item_t*, item_t*, offset_t>;\n  using fallback_policy_t = typename device_dummy_algorithm_policy_t<item_t*>::policy_500::FallbackDummyAlgorithmPolicy;\n  using fallback_agent_t  = agent_dummy_algorithm_t<fallback_policy_t, item_t*, item_t*, offset_t>;\n\n  // Get the information as it is expected from the vsmem helper to work as expected\n  std::size_t default_smem_size  = sizeof(typename default_agent_t::TempStorage);\n  std::size_t fallback_smem_size = sizeof(typename fallback_agent_t::TempStorage);\n  bool expected_to_use_fallback =\n    default_smem_size > hipcub::detail::max_smem_per_block && fallback_smem_size <= hipcub::detail::max_smem_per_block;\n  std::size_t expected_smem_per_block = expected_to_use_fallback ? fallback_smem_size : default_smem_size;\n  bool expected_needs_vsmem           = expected_smem_per_block > hipcub::detail::max_smem_per_block;\n  std::size_t expected_threads_per_block =\n    expected_to_use_fallback ? fallback_policy_t::BLOCK_THREADS : default_policy_t::BLOCK_THREADS;\n  std::size_t expected_items_per_thread =\n    expected_to_use_fallback ? fallback_policy_t::ITEMS_PER_THREAD : default_policy_t::ITEMS_PER_THREAD;\n  std::size_t expected_tile_size       = expected_threads_per_block * expected_items_per_thread;\n  std::size_t expected_vsmem_per_block = (expected_needs_vsmem ? expected_smem_per_block : 0ULL);\n\n  // Setup vsmem test\n  launch_config_test_info_t* launch_config_info = nullptr;\n  hipHostMalloc(&launch_config_info, sizeof(launch_config_test_info_t));\n  c2h::device_vector<kernel_test_info_t> device_kernel_test_info(1);\n  dummy_algorithm(\n    in_ptr, out_ptr, num_items, thrust::raw_pointer_cast(device_kernel_test_info.data()), launch_config_info);\n\n  // Make sure the algorithm worked correctly\n  REQUIRE(in == out);\n\n  // Make sure the kernel information retrieved from the vsmem helper is correct\n  c2h::host_vector<kernel_test_info_t> kernel_test_info = device_kernel_test_info;\n  REQUIRE(kernel_test_info[0].uses_vsmem_ptr == expected_needs_vsmem);\n  REQUIRE(kernel_test_info[0].uses_fallback_agent == expected_to_use_fallback);\n  REQUIRE(kernel_test_info[0].uses_fallback_policy == expected_to_use_fallback);\n\n  // Make sure the launch configuration information retrieved from the vsmem helper is correct\n  REQUIRE(launch_config_info->config_assumes_tile_size == expected_tile_size);\n  REQUIRE(launch_config_info->config_assumes_threads_per_block == expected_threads_per_block);\n  if (expected_vsmem_per_block == 0)\n  {\n    REQUIRE(launch_config_info->config_vsmem_per_block == 0);\n  }\n  else\n  {\n    // The virtual shared memory helper pads vsmem to a multiple of a line size, hence the range check\n    REQUIRE(launch_config_info->config_vsmem_per_block >= expected_vsmem_per_block);\n  }\n\n  hipHostFree(launch_config_info);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_vsmem.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <string>\n\n#include <cuda.h>\n#include <nvrtc.h>\n#include <nvrtc_args.h>\n\n#include <c2h/catch2_test_helper.h>\n\nTEST_CASE(\"Test nvrtc\", \"[test][nvrtc]\")\n{\n  nvrtcProgram prog{};\n\n  const char* src  = R\"asdf(\n    #include <cub/agent/agent_adjacent_difference.cuh>\n    #include <cub/agent/agent_batch_memcpy.cuh>\n    #include <cub/agent/agent_for.cuh>\n    #include <cub/agent/agent_histogram.cuh>\n    #include <cub/agent/agent_merge.cuh>\n    #include <cub/agent/agent_merge_sort.cuh>\n    #include <cub/agent/agent_radix_sort_downsweep.cuh>\n    #include <cub/agent/agent_radix_sort_histogram.cuh>\n    #include <cub/agent/agent_radix_sort_onesweep.cuh>\n    #include <cub/agent/agent_radix_sort_upsweep.cuh>\n    #include <cub/agent/agent_reduce_by_key.cuh>\n    #include <cub/agent/agent_reduce.cuh>\n    #include <cub/agent/agent_rle.cuh>\n    #include <cub/agent/agent_scan_by_key.cuh>\n    #include <cub/agent/agent_scan.cuh>\n    #include <cub/agent/agent_segmented_radix_sort.cuh>\n    #include <cub/agent/agent_select_if.cuh>\n    #include <cub/agent/agent_sub_warp_merge_sort.cuh>\n    #include <cub/agent/agent_three_way_partition.cuh>\n    #include <cub/agent/agent_unique_by_key.cuh>\n    #include <cub/agent/single_pass_scan_operators.cuh>\n    #include <cub/block/block_adjacent_difference.cuh>\n    #include <cub/block/block_discontinuity.cuh>\n    #include <cub/block/block_exchange.cuh>\n    #include <cub/block/block_histogram.cuh>\n    #include <cub/block/block_load.cuh>\n    #include <cub/block/block_store.cuh>\n    #include <cub/block/block_merge_sort.cuh>\n    #include <cub/block/block_radix_rank.cuh>\n    #include <cub/block/block_radix_sort.cuh>\n    #include <cub/block/block_raking_layout.cuh>\n    #include <cub/block/block_reduce.cuh>\n    #include <cub/block/block_run_length_decode.cuh>\n    #include <cub/block/block_scan.cuh>\n    #include <cub/block/block_shuffle.cuh>\n    #include <cub/block/block_store.cuh>\n    #include <cub/block/radix_rank_sort_operations.cuh>\n    #include <cub/device/dispatch/kernels/kernel_reduce.cuh>\n    #include <cub/device/dispatch/kernels/kernel_for_each.cuh>\n    #include <cub/device/dispatch/kernels/kernel_scan.cuh>\n    #include <cub/device/dispatch/kernels/kernel_merge_sort.cuh>\n    #include <cub/device/dispatch/kernels/kernel_segmented_reduce.cuh>\n    #include <cub/device/dispatch/kernels/kernel_radix_sort.cuh>\n    #include <cub/device/dispatch/kernels/kernel_unique_by_key.cuh>\n    #include <cub/device/dispatch/kernels/kernel_transform.cuh>\n    #include <cub/device/dispatch/kernels/kernel_histogram.cuh>\n    #include <cub/device/dispatch/kernels/kernel_segmented_sort.cuh>\n    #include <cub/iterator/arg_index_input_iterator.cuh>\n    #include <cub/iterator/cache_modified_input_iterator.cuh>\n    #include <cub/iterator/cache_modified_output_iterator.cuh>\n    #include <cub/iterator/tex_obj_input_iterator.cuh>\n    #include <cub/thread/thread_load.cuh>\n    #include <cub/thread/thread_operators.cuh>\n    #include <cub/thread/thread_reduce.cuh>\n    #include <cub/thread/thread_scan.cuh>\n    #include <cub/thread/thread_sort.cuh>\n    #include <cub/thread/thread_store.cuh>\n    #include <cub/warp/warp_reduce.cuh>\n    #include <cub/warp/warp_scan.cuh>\n    #include <cub/warp/warp_exchange.cuh>\n    #include <cub/warp/warp_load.cuh>\n    #include <cub/warp/warp_store.cuh>\n    #include <cub/warp/warp_merge_sort.cuh>\n    #include <cub/util_arch.cuh>\n    #include <cub/util_cpp_dialect.cuh>\n    #include <cub/util_debug.cuh>\n    #include <cub/util_device.cuh>\n    #include <cub/util_macro.cuh>\n    #include <cub/util_math.cuh>\n    #include <cub/util_namespace.cuh>\n    #include <cub/util_policy_wrapper_t.cuh>\n    #include <cub/util_ptx.cuh>\n    #include <cub/util_temporary_storage.cuh>\n    #include <cub/util_type.cuh>\n    #include <cub/util_vsmem.cuh>\n\n    #include <thrust/iterator/constant_iterator.h>\n    #include <thrust/iterator/counting_iterator.h>\n    #include <thrust/iterator/discard_iterator.h>\n    #include <thrust/iterator/permutation_iterator.h>\n    #include <thrust/iterator/reverse_iterator.h>\n    #include <thrust/iterator/tabulate_output_iterator.h>\n    #include <thrust/iterator/transform_input_output_iterator.h>\n    #include <thrust/iterator/transform_iterator.h>\n    #include <thrust/iterator/transform_output_iterator.h>\n    #include <thrust/iterator/zip_iterator.h>\n\n    extern \"C\" __global__ void kernel(int *ptr, int *errors)\n    {\n      constexpr int items_per_thread = 4;\n      constexpr int threads_per_block = 128;\n      using warp_load_t = cub::WarpLoad<int, items_per_thread>;\n      using warp_load_storage_t = warp_load_t::TempStorage;\n\n      using warp_exchange_t = cub::WarpExchange<int, items_per_thread>;\n      using warp_exchange_storage_t = warp_exchange_t::TempStorage;\n\n      using warp_reduce_t = cub::WarpReduce<int>;\n      using warp_reduce_storage_t = warp_reduce_t::TempStorage;\n\n      using warp_merge_sort_t = cub::WarpMergeSort<int, items_per_thread>;\n      using warp_merge_sort_storage_t = warp_merge_sort_t::TempStorage;\n\n      using warp_scan_t = cub::WarpScan<int>;\n      using warp_scan_storage_t = warp_scan_t::TempStorage;\n\n      using warp_store_t = cub::WarpStore<int, items_per_thread>;\n      using warp_store_storage_t = warp_store_t::TempStorage;\n\n      __shared__ warp_load_storage_t warp_load_storage;\n      __shared__ warp_exchange_storage_t warp_exchange_storage;\n      __shared__ warp_reduce_storage_t warp_reduce_storage;\n      __shared__ warp_merge_sort_storage_t warp_merge_sort_storage;\n      __shared__ warp_scan_storage_t warp_scan_storage;\n      __shared__ warp_store_storage_t warp_store_storage;\n\n      int items[items_per_thread];\n      if (threadIdx.x < 32)\n      {\n        // Test warp load\n        warp_load_t(warp_load_storage).Load(ptr, items);\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i + threadIdx.x * items_per_thread))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp exchange\n        warp_exchange_t(warp_exchange_storage).BlockedToStriped(items, items);\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i * 32 + threadIdx.x))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp reduce\n        const int sum = warp_reduce_t(warp_reduce_storage).Sum(items[0]);\n        if (threadIdx.x == 0)\n        {\n          if (sum != (32 * (32 - 1) / 2))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp scan\n        int prefix_sum{};\n        warp_scan_t(warp_scan_storage).InclusiveSum(items[0], prefix_sum);\n        if (prefix_sum != (threadIdx.x * (threadIdx.x + 1) / 2))\n        {\n          atomicAdd(errors, 1);\n        }\n\n        // Test warp merge sort\n        warp_merge_sort_t(warp_merge_sort_storage).Sort(\n          items,\n          [](int a, int b) { return a < b; });\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i + threadIdx.x * items_per_thread))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp store\n        warp_store_t(warp_store_storage).Store(ptr, items);\n      }\n      __syncthreads();\n\n      using block_load_t = cub::BlockLoad<int, threads_per_block, items_per_thread>;\n      using block_load_storage_t = block_load_t::TempStorage;\n\n      using block_exchange_t = cub::BlockExchange<int, threads_per_block, items_per_thread>;\n      using block_exchange_storage_t = block_exchange_t::TempStorage;\n\n      using block_reduce_t = cub::BlockReduce<int, threads_per_block>;\n      using block_reduce_storage_t = block_reduce_t::TempStorage;\n\n      using block_scan_t = cub::BlockScan<int, threads_per_block>;\n      using block_scan_storage_t = block_scan_t::TempStorage;\n\n      using block_radix_sort_t = cub::BlockRadixSort<int, threads_per_block, items_per_thread>;\n      using block_radix_sort_storage_t = block_radix_sort_t::TempStorage;\n\n      using block_store_t = cub::BlockStore<int, threads_per_block, items_per_thread>;\n      using block_store_storage_t = block_store_t::TempStorage;\n\n      __shared__ block_load_storage_t block_load_storage;\n      __shared__ block_exchange_storage_t block_exchange_storage;\n      __shared__ block_reduce_storage_t block_reduce_storage;\n      __shared__ block_scan_storage_t block_scan_storage;\n      __shared__ block_radix_sort_storage_t block_radix_sort_storage;\n      __shared__ block_store_storage_t block_store_storage;\n\n      // Test block load\n      block_load_t(block_load_storage).Load(ptr, items);\n\n      for (int i = 0; i < items_per_thread; i++)\n      {\n        if (items[i] != (i + threadIdx.x * items_per_thread))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block exchange\n      block_exchange_t(block_exchange_storage).BlockedToStriped(items, items);\n\n      for (int i = 0; i < items_per_thread; i++)\n      {\n        if (items[i] != (i * threads_per_block + threadIdx.x))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block reduce\n      const int sum = block_reduce_t(block_reduce_storage).Sum(items[0]);\n      if (threadIdx.x == 0)\n      {\n        if (sum != (threads_per_block * (threads_per_block - 1) / 2))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block scan\n      int prefix_sum{};\n      block_scan_t(block_scan_storage).InclusiveSum(items[0], prefix_sum);\n      if (prefix_sum != (threadIdx.x * (threadIdx.x + 1) / 2))\n      {\n        atomicAdd(errors, 1);\n      }\n\n      // Test block radix sort\n      block_radix_sort_t(block_radix_sort_storage).SortDescending(items);\n\n      // Test block store\n      block_store_t(block_store_storage).Store(ptr, items);\n    }\n)asdf\";\n  const char* name = \"test\";\n\n  REQUIRE(NVRTC_SUCCESS == nvrtcCreateProgram(&prog, src, name, 0, nullptr, nullptr));\n\n  int ptx_version{};\n  cub::PtxVersion(ptx_version);\n  const std::string arch = std::string(\"-arch=sm_\") + std::to_string(ptx_version / 10);\n  const std::string std  = std::string(\"-std=c++\") + std::to_string(_CCCL_STD_VER - 2000);\n\n  constexpr int num_includes         = 6;\n  const char* includes[num_includes] = {\n    NVRTC_CUB_PATH, NVRTC_THRUST_PATH, NVRTC_LIBCUDACXX_PATH, NVRTC_CTK_PATH, arch.c_str(), std.c_str()};\n\n  std::size_t log_size{};\n  nvrtcResult compile_result = nvrtcCompileProgram(prog, num_includes, includes);\n\n  REQUIRE(NVRTC_SUCCESS == nvrtcGetProgramLogSize(prog, &log_size));\n\n  std::unique_ptr<char[]> log{new char[log_size]};\n  REQUIRE(NVRTC_SUCCESS == nvrtcGetProgramLog(prog, log.get()));\n  INFO(\"nvrtc log = \" << log.get());\n  REQUIRE(NVRTC_SUCCESS == compile_result);\n\n  std::size_t code_size{};\n  REQUIRE(NVRTC_SUCCESS == nvrtcGetCUBINSize(prog, &code_size));\n\n  std::unique_ptr<char[]> code{new char[code_size]};\n  REQUIRE(NVRTC_SUCCESS == nvrtcGetCUBIN(prog, code.get()));\n  REQUIRE(NVRTC_SUCCESS == nvrtcDestroyProgram(&prog));\n\n  CUcontext context{};\n  CUdevice device{};\n  CUmodule module{};\n  CUfunction kernel{};\n\n  REQUIRE(CUDA_SUCCESS == cuInit(0));\n  REQUIRE(CUDA_SUCCESS == cuDeviceGet(&device, 0));\n  REQUIRE(CUDA_SUCCESS == cuDevicePrimaryCtxRetain(&context, device));\n  REQUIRE(CUDA_SUCCESS == cuCtxSetCurrent(context));\n  REQUIRE(CUDA_SUCCESS == cuModuleLoadDataEx(&module, code.get(), 0, nullptr, nullptr));\n  REQUIRE(CUDA_SUCCESS == cuModuleGetFunction(&kernel, module, \"kernel\"));\n\n  // Generate input for execution, and create output buffers.\n  constexpr int threads_in_block = 128;\n  constexpr int items_per_thread = 4;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n\n  CUdeviceptr d_ptr{};\n  REQUIRE(CUDA_SUCCESS == cuMemAlloc(&d_ptr, tile_size * sizeof(int)));\n\n  CUdeviceptr d_err{};\n  REQUIRE(CUDA_SUCCESS == cuMemAlloc(&d_err, sizeof(int)));\n\n  int h_ptr[tile_size];\n  for (int i = 0; i < tile_size; i++)\n  {\n    h_ptr[i] = i;\n  }\n  REQUIRE(CUDA_SUCCESS == cuMemcpyHtoD(d_ptr, h_ptr, tile_size * sizeof(int)));\n\n  int h_err{0};\n  REQUIRE(CUDA_SUCCESS == cuMemcpyHtoD(d_err, &h_err, sizeof(int)));\n\n  void* args[] = {&d_ptr, &d_err};\n\n  REQUIRE(CUDA_SUCCESS == cuLaunchKernel(kernel, 1, 1, 1, threads_in_block, 1, 1, 0, nullptr, args, nullptr));\n  REQUIRE(CUDA_SUCCESS == cuCtxSynchronize());\n  REQUIRE(CUDA_SUCCESS == cuMemcpyDtoH(h_ptr, d_ptr, tile_size * sizeof(int)));\n  REQUIRE(CUDA_SUCCESS == cuMemcpyDtoH(&h_err, d_err, sizeof(int)));\n\n  REQUIRE(h_err == 0);\n  for (int i = 0; i < tile_size; i++)\n  {\n    const int actual   = h_ptr[i];\n    const int expected = tile_size - i - 1;\n    REQUIRE(actual == expected);\n  }\n\n  REQUIRE(CUDA_SUCCESS == cuMemFree(d_ptr));\n  REQUIRE(CUDA_SUCCESS == cuMemFree(d_err));\n  REQUIRE(CUDA_SUCCESS == cuModuleUnload(module));\n  REQUIRE(CUDA_SUCCESS == cuDevicePrimaryCtxRelease(device));\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <string>\n\n#include <hip/hip_runtime.h>\n#include <hip/hiprtc.h>\n#include <nvrtc_args.h>\n\n#include <c2h/catch2_test_helper.h>\n\nTEST_CASE(\"Test nvrtc\", \"[test][nvrtc]\")\n{\n  hiprtcProgram prog{};\n\n  const char* src  = R\"asdf(\n    #include <cub/agent/agent_adjacent_difference.cuh>\n    #include <cub/agent/agent_batch_memcpy.cuh>\n    #include <cub/agent/agent_for.cuh>\n    #include <cub/agent/agent_histogram.cuh>\n    #include <cub/agent/agent_merge.cuh>\n    #include <cub/agent/agent_merge_sort.cuh>\n    #include <cub/agent/agent_radix_sort_downsweep.cuh>\n    #include <cub/agent/agent_radix_sort_histogram.cuh>\n    #include <cub/agent/agent_radix_sort_onesweep.cuh>\n    #include <cub/agent/agent_radix_sort_upsweep.cuh>\n    #include <cub/agent/agent_reduce_by_key.cuh>\n    #include <cub/agent/agent_reduce.cuh>\n    #include <cub/agent/agent_rle.cuh>\n    #include <cub/agent/agent_scan_by_key.cuh>\n    #include <cub/agent/agent_scan.cuh>\n    #include <cub/agent/agent_segmented_radix_sort.cuh>\n    #include <cub/agent/agent_select_if.cuh>\n    #include <cub/agent/agent_sub_warp_merge_sort.cuh>\n    #include <cub/agent/agent_three_way_partition.cuh>\n    #include <cub/agent/agent_unique_by_key.cuh>\n    #include <cub/agent/single_pass_scan_operators.cuh>\n    #include <cub/block/block_adjacent_difference.cuh>\n    #include <cub/block/block_discontinuity.cuh>\n    #include <cub/block/block_exchange.cuh>\n    #include <cub/block/block_histogram.cuh>\n    #include <cub/block/block_load.cuh>\n    #include <cub/block/block_store.cuh>\n    #include <cub/block/block_merge_sort.cuh>\n    #include <cub/block/block_radix_rank.cuh>\n    #include <cub/block/block_radix_sort.cuh>\n    #include <cub/block/block_raking_layout.cuh>\n    #include <cub/block/block_reduce.cuh>\n    #include <cub/block/block_run_length_decode.cuh>\n    #include <cub/block/block_scan.cuh>\n    #include <cub/block/block_shuffle.cuh>\n    #include <cub/block/block_store.cuh>\n    #include <cub/block/radix_rank_sort_operations.cuh>\n    #include <cub/device/dispatch/kernels/kernel_reduce.cuh>\n    #include <cub/device/dispatch/kernels/kernel_for_each.cuh>\n    #include <cub/device/dispatch/kernels/kernel_scan.cuh>\n    #include <cub/device/dispatch/kernels/kernel_merge_sort.cuh>\n    #include <cub/device/dispatch/kernels/kernel_segmented_reduce.cuh>\n    #include <cub/device/dispatch/kernels/kernel_radix_sort.cuh>\n    #include <cub/device/dispatch/kernels/kernel_unique_by_key.cuh>\n    #include <cub/device/dispatch/kernels/kernel_transform.cuh>\n    #include <cub/device/dispatch/kernels/kernel_histogram.cuh>\n    #include <cub/device/dispatch/kernels/kernel_segmented_sort.cuh>\n    #include <cub/iterator/arg_index_input_iterator.cuh>\n    #include <cub/iterator/cache_modified_input_iterator.cuh>\n    #include <cub/iterator/cache_modified_output_iterator.cuh>\n    #include <cub/iterator/tex_obj_input_iterator.cuh>\n    #include <cub/thread/thread_load.cuh>\n    #include <cub/thread/thread_operators.cuh>\n    #include <cub/thread/thread_reduce.cuh>\n    #include <cub/thread/thread_scan.cuh>\n    #include <cub/thread/thread_sort.cuh>\n    #include <cub/thread/thread_store.cuh>\n    #include <cub/warp/warp_reduce.cuh>\n    #include <cub/warp/warp_scan.cuh>\n    #include <cub/warp/warp_exchange.cuh>\n    #include <cub/warp/warp_load.cuh>\n    #include <cub/warp/warp_store.cuh>\n    #include <cub/warp/warp_merge_sort.cuh>\n    #include <cub/util_arch.cuh>\n    #include <cub/util_cpp_dialect.cuh>\n    #include <cub/util_debug.cuh>\n    #include <cub/util_device.cuh>\n    #include <cub/util_macro.cuh>\n    #include <cub/util_math.cuh>\n    #include <cub/util_namespace.cuh>\n    #include <cub/util_policy_wrapper_t.cuh>\n    #include <cub/util_ptx.cuh>\n    #include <cub/util_temporary_storage.cuh>\n    #include <cub/util_type.cuh>\n    #include <cub/util_vsmem.cuh>\n\n    #include <thrust/iterator/constant_iterator.h>\n    #include <thrust/iterator/counting_iterator.h>\n    #include <thrust/iterator/discard_iterator.h>\n    #include <thrust/iterator/permutation_iterator.h>\n    #include <thrust/iterator/reverse_iterator.h>\n    #include <thrust/iterator/tabulate_output_iterator.h>\n    #include <thrust/iterator/transform_input_output_iterator.h>\n    #include <thrust/iterator/transform_iterator.h>\n    #include <thrust/iterator/transform_output_iterator.h>\n    #include <thrust/iterator/zip_iterator.h>\n\n    extern \"C\" __global__ void kernel(int *ptr, int *errors)\n    {\n      constexpr int items_per_thread = 4;\n      constexpr int threads_per_block = 128;\n      using warp_load_t = hipcub::WarpLoad<int, items_per_thread>;\n      using warp_load_storage_t = warp_load_t::TempStorage;\n\n      using warp_exchange_t = hipcub::WarpExchange<int, items_per_thread>;\n      using warp_exchange_storage_t = warp_exchange_t::TempStorage;\n\n      using warp_reduce_t = hipcub::WarpReduce<int>;\n      using warp_reduce_storage_t = warp_reduce_t::TempStorage;\n\n      using warp_merge_sort_t = hipcub::WarpMergeSort<int, items_per_thread>;\n      using warp_merge_sort_storage_t = warp_merge_sort_t::TempStorage;\n\n      using warp_scan_t = hipcub::WarpScan<int>;\n      using warp_scan_storage_t = warp_scan_t::TempStorage;\n\n      using warp_store_t = hipcub::WarpStore<int, items_per_thread>;\n      using warp_store_storage_t = warp_store_t::TempStorage;\n\n      __shared__ warp_load_storage_t warp_load_storage;\n      __shared__ warp_exchange_storage_t warp_exchange_storage;\n      __shared__ warp_reduce_storage_t warp_reduce_storage;\n      __shared__ warp_merge_sort_storage_t warp_merge_sort_storage;\n      __shared__ warp_scan_storage_t warp_scan_storage;\n      __shared__ warp_store_storage_t warp_store_storage;\n\n      int items[items_per_thread];\n      if (threadIdx.x < 32)\n      {\n        // Test warp load\n        warp_load_t(warp_load_storage).Load(ptr, items);\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i + threadIdx.x * items_per_thread))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp exchange\n        warp_exchange_t(warp_exchange_storage).BlockedToStriped(items, items);\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i * 32 + threadIdx.x))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp reduce\n        const int sum = warp_reduce_t(warp_reduce_storage).Sum(items[0]);\n        if (threadIdx.x == 0)\n        {\n          if (sum != (32 * (32 - 1) / 2))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp scan\n        int prefix_sum{};\n        warp_scan_t(warp_scan_storage).InclusiveSum(items[0], prefix_sum);\n        if (prefix_sum != (threadIdx.x * (threadIdx.x + 1) / 2))\n        {\n          atomicAdd(errors, 1);\n        }\n\n        // Test warp merge sort\n        warp_merge_sort_t(warp_merge_sort_storage).Sort(\n          items,\n          [](int a, int b) { return a < b; });\n\n        for (int i = 0; i < items_per_thread; i++)\n        {\n          if (items[i] != (i + threadIdx.x * items_per_thread))\n          {\n            atomicAdd(errors, 1);\n          }\n        }\n\n        // Test warp store\n        warp_store_t(warp_store_storage).Store(ptr, items);\n      }\n      __syncthreads();\n\n      using block_load_t = hipcub::BlockLoad<int, threads_per_block, items_per_thread>;\n      using block_load_storage_t = block_load_t::TempStorage;\n\n      using block_exchange_t = hipcub::BlockExchange<int, threads_per_block, items_per_thread>;\n      using block_exchange_storage_t = block_exchange_t::TempStorage;\n\n      using block_reduce_t = hipcub::BlockReduce<int, threads_per_block>;\n      using block_reduce_storage_t = block_reduce_t::TempStorage;\n\n      using block_scan_t = hipcub::BlockScan<int, threads_per_block>;\n      using block_scan_storage_t = block_scan_t::TempStorage;\n\n      using block_radix_sort_t = hipcub::BlockRadixSort<int, threads_per_block, items_per_thread>;\n      using block_radix_sort_storage_t = block_radix_sort_t::TempStorage;\n\n      using block_store_t = hipcub::BlockStore<int, threads_per_block, items_per_thread>;\n      using block_store_storage_t = block_store_t::TempStorage;\n\n      __shared__ block_load_storage_t block_load_storage;\n      __shared__ block_exchange_storage_t block_exchange_storage;\n      __shared__ block_reduce_storage_t block_reduce_storage;\n      __shared__ block_scan_storage_t block_scan_storage;\n      __shared__ block_radix_sort_storage_t block_radix_sort_storage;\n      __shared__ block_store_storage_t block_store_storage;\n\n      // Test block load\n      block_load_t(block_load_storage).Load(ptr, items);\n\n      for (int i = 0; i < items_per_thread; i++)\n      {\n        if (items[i] != (i + threadIdx.x * items_per_thread))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block exchange\n      block_exchange_t(block_exchange_storage).BlockedToStriped(items, items);\n\n      for (int i = 0; i < items_per_thread; i++)\n      {\n        if (items[i] != (i * threads_per_block + threadIdx.x))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block reduce\n      const int sum = block_reduce_t(block_reduce_storage).Sum(items[0]);\n      if (threadIdx.x == 0)\n      {\n        if (sum != (threads_per_block * (threads_per_block - 1) / 2))\n        {\n          atomicAdd(errors, 1);\n        }\n      }\n\n      // Test block scan\n      int prefix_sum{};\n      block_scan_t(block_scan_storage).InclusiveSum(items[0], prefix_sum);\n      if (prefix_sum != (threadIdx.x * (threadIdx.x + 1) / 2))\n      {\n        atomicAdd(errors, 1);\n      }\n\n      // Test block radix sort\n      block_radix_sort_t(block_radix_sort_storage).SortDescending(items);\n\n      // Test block store\n      block_store_t(block_store_storage).Store(ptr, items);\n    }\n)asdf\";\n  const char* name = \"test\";\n\n  REQUIRE(HIPRTC_SUCCESS == hiprtcCreateProgram(&prog, src, name, 0, nullptr, nullptr));\n\n  int ptx_version{};\n  hipcub::PtxVersion(ptx_version);\n  const std::string arch = std::string(\"-arch=sm_\") + std::to_string(ptx_version / 10);\n  const std::string std  = std::string(\"-std=c++\") + std::to_string(_CCCL_STD_VER - 2000);\n\n  constexpr int num_includes         = 6;\n  const char* includes[num_includes] = {\n    NVRTC_CUB_PATH, NVRTC_THRUST_PATH, NVRTC_LIBCUDACXX_PATH, NVRTC_CTK_PATH, arch.c_str(), std.c_str()};\n\n  std::size_t log_size{};\n  hiprtcResult compile_result = hiprtcCompileProgram(prog, num_includes, includes);\n\n  REQUIRE(HIPRTC_SUCCESS == hiprtcGetProgramLogSize(prog, &log_size));\n\n  std::unique_ptr<char[]> log{new char[log_size]};\n  REQUIRE(HIPRTC_SUCCESS == hiprtcGetProgramLog(prog, log.get()));\n  INFO(\"nvrtc log = \" << log.get());\n  REQUIRE(HIPRTC_SUCCESS == compile_result);\n\n  std::size_t code_size{};\n  REQUIRE(HIPRTC_SUCCESS == hiprtcGetBitcodeSize(prog, &code_size));\n\n  std::unique_ptr<char[]> code{new char[code_size]};\n  REQUIRE(HIPRTC_SUCCESS == hiprtcGetBitcode(prog, code.get()));\n  REQUIRE(HIPRTC_SUCCESS == hiprtcDestroyProgram(&prog));\n\n  hipCtx_t context{};\n  hipDevice_t device{};\n  hipModule_t module{};\n  hipFunction_t kernel{};\n\n  REQUIRE(hipSuccess == hipInit(0));\n  REQUIRE(hipSuccess == hipDeviceGet(&device, 0));\n  REQUIRE(hipSuccess == hipDevicePrimaryCtxRetain(&context, device));\n  REQUIRE(hipSuccess == hipCtxSetCurrent(context));\n  REQUIRE(hipSuccess == hipModuleLoadDataEx(&module, code.get(), 0, nullptr, nullptr));\n  REQUIRE(hipSuccess == hipModuleGetFunction(&kernel, module, \"kernel\"));\n\n  // Generate input for execution, and create output buffers.\n  constexpr int threads_in_block = 128;\n  constexpr int items_per_thread = 4;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n\n  hipDeviceptr_t d_ptr{};\n  REQUIRE(hipSuccess == hipMalloc(&d_ptr, tile_size * sizeof(int)));\n\n  hipDeviceptr_t d_err{};\n  REQUIRE(hipSuccess == hipMalloc(&d_err, sizeof(int)));\n\n  int h_ptr[tile_size];\n  for (int i = 0; i < tile_size; i++)\n  {\n    h_ptr[i] = i;\n  }\n  REQUIRE(hipSuccess == hipMemcpyHtoD(d_ptr, h_ptr, tile_size * sizeof(int)));\n\n  int h_err{0};\n  REQUIRE(hipSuccess == hipMemcpyHtoD(d_err, &h_err, sizeof(int)));\n\n  void* args[] = {&d_ptr, &d_err};\n\n  REQUIRE(hipSuccess == hipModuleLaunchKernel(kernel, 1, 1, 1, threads_in_block, 1, 1, 0, nullptr, args, nullptr));\n  REQUIRE(hipSuccess == hipCtxSynchronize());\n  REQUIRE(hipSuccess == hipMemcpyDtoH(h_ptr, d_ptr, tile_size * sizeof(int)));\n  REQUIRE(hipSuccess == hipMemcpyDtoH(&h_err, d_err, sizeof(int)));\n\n  REQUIRE(h_err == 0);\n  for (int i = 0; i < tile_size; i++)\n  {\n    const int actual   = h_ptr[i];\n    const int expected = tile_size - i - 1;\n    REQUIRE(actual == expected);\n  }\n\n  REQUIRE(hipSuccess == hipFree(d_ptr));\n  REQUIRE(hipSuccess == hipFree(d_err));\n  REQUIRE(hipSuccess == hipModuleUnload(module));\n  REQUIRE(hipSuccess == hipDevicePrimaryCtxRelease(device));\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_nvrtc.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"insert_nested_NVTX_range_guard.h\"\n\n#undef NDEBUG\n#include <cub/device/device_scan.cuh>\n\n#include <cassert>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(cub::ScanTileState<MessageT> tile_state, MessageT* tile_data)\n{\n  using scan_op_t         = cuda::std::plus<>;\n  using scan_tile_state_t = cub::ScanTileState<MessageT>;\n  using tile_prefix_op    = cub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  constexpr unsigned int threads_in_warp = 32;\n  const unsigned int tid                 = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // \"Compute\" tile aggregate\n  MessageT tile_aggregate = tile_data[tile_idx];\n\n  __syncthreads();\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, tile_aggregate);\n      tile_data[tile_idx] = tile_aggregate;\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(tile_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, tile_aggregate);\n        tile_data[tile_idx]       = inclusive_prefix;\n      }\n    }\n    __syncthreads();\n\n    assert(tile_data[tile_idx] == prefix.GetInclusivePrefix());\n    assert(tile_aggregate == prefix.GetBlockAggregate());\n  }\n}\n\nusing message_types = c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>;\n\ntemplate <class MessageT>\nc2h::host_vector<MessageT> compute_reference(const c2h::device_vector<MessageT>& tile_aggregates)\n{\n  if (tile_aggregates.empty())\n  {\n    return {};\n  }\n\n  c2h::host_vector<MessageT> reference = tile_aggregates;\n  MessageT* h_reference                = thrust::raw_pointer_cast(reference.data());\n\n  MessageT aggregate = h_reference[0];\n  for (std::size_t i = 1; i < reference.size(); i++)\n  {\n    aggregate += h_reference[i];\n    h_reference[i] = aggregate;\n  }\n\n  return reference;\n}\n\nC2H_TEST(\"Decoupled look-back works with various message types\", \"[decoupled look-back][device]\", message_types)\n{\n  using message_t         = typename c2h::get<0, TestType>;\n  using scan_tile_state_t = cub::ScanTileState<message_t>;\n\n  constexpr int max_tiles = 1024 * 1024;\n  // Use c2h::adjust_seed_count to reduce the number of runs when using sanitizers:\n  const int num_tiles = GENERATE_COPY(take(c2h::adjust_seed_count(10), random(1, max_tiles)));\n\n  c2h::device_vector<message_t> tile_data(num_tiles);\n  message_t* d_tile_data = thrust::raw_pointer_cast(tile_data.data());\n\n  c2h::gen(C2H_SEED(2), tile_data);\n  c2h::host_vector<message_t> reference = compute_reference(tile_data);\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(num_tiles, temp_storage_bytes);\n\n  // Allocate temporary storage\n  c2h::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t* d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  cudaError_t status = tile_status.Init(num_tiles, d_temp_storage, temp_storage_bytes);\n  REQUIRE(status == cudaSuccess);\n\n  constexpr unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid       = cuda::ceil_div(num_tiles, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, num_tiles);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  // Launch decoupled look-back\n  constexpr unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<num_tiles, threads_in_block>>>(tile_status, d_tile_data);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  REQUIRE(reference == tile_data);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include \"insert_nested_NVTX_range_guard.h\"\n\n#undef NDEBUG\n#include <cub/device/device_scan.cuh>\n\n#include <cassert>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <class ScanTileStateT>\n__global__ void init_kernel(ScanTileStateT tile_state, int blocks_in_grid)\n{\n  tile_state.InitializeStatus(blocks_in_grid);\n}\n\ntemplate <class MessageT>\n__global__ void decoupled_look_back_kernel(hipcub::ScanTileState<MessageT> tile_state, MessageT* tile_data)\n{\n  using scan_op_t         = cuda::std::plus<>;\n  using scan_tile_state_t = hipcub::ScanTileState<MessageT>;\n  using tile_prefix_op    = hipcub::TilePrefixCallbackOp<MessageT, scan_op_t, scan_tile_state_t>;\n  using temp_storage_t    = typename tile_prefix_op::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ temp_storage_t temp_storage;\n\n  scan_op_t scan_op{};\n  constexpr unsigned int threads_in_warp = 32;\n  const unsigned int tid                 = threadIdx.x;\n\n  // Construct prefix op\n  tile_prefix_op prefix(tile_state, temp_storage, scan_op);\n  const unsigned int tile_idx = prefix.GetTileIdx();\n\n  // \"Compute\" tile aggregate\n  MessageT tile_aggregate = tile_data[tile_idx];\n\n  __syncthreads();\n\n  if (tile_idx == 0)\n  {\n    // There are no blocks to look back to, immediately set the inclusive state\n    if (tid == 0)\n    {\n      tile_state.SetInclusive(tile_idx, tile_aggregate);\n      tile_data[tile_idx] = tile_aggregate;\n    }\n  }\n  else\n  {\n    // Only the first warp in the block can perform the look back\n    const unsigned int warp_id = tid / threads_in_warp;\n\n    if (warp_id == 0)\n    {\n      // Perform the decoupled look-back\n      // Invocation of the prefix will block until the look-back is complete.\n      MessageT exclusive_prefix = prefix(tile_aggregate);\n\n      if (tid == 0)\n      {\n        MessageT inclusive_prefix = scan_op(exclusive_prefix, tile_aggregate);\n        tile_data[tile_idx]       = inclusive_prefix;\n      }\n    }\n    __syncthreads();\n\n    assert(tile_data[tile_idx] == prefix.GetInclusivePrefix());\n    assert(tile_aggregate == prefix.GetBlockAggregate());\n  }\n}\n\nusing message_types = c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>;\n\ntemplate <class MessageT>\nc2h::host_vector<MessageT> compute_reference(const c2h::device_vector<MessageT>& tile_aggregates)\n{\n  if (tile_aggregates.empty())\n  {\n    return {};\n  }\n\n  c2h::host_vector<MessageT> reference = tile_aggregates;\n  MessageT* h_reference                = thrust::raw_pointer_cast(reference.data());\n\n  MessageT aggregate = h_reference[0];\n  for (std::size_t i = 1; i < reference.size(); i++)\n  {\n    aggregate += h_reference[i];\n    h_reference[i] = aggregate;\n  }\n\n  return reference;\n}\n\nC2H_TEST(\"Decoupled look-back works with various message types\", \"[decoupled look-back][device]\", message_types)\n{\n  using message_t         = typename c2h::get<0, TestType>;\n  using scan_tile_state_t = hipcub::ScanTileState<message_t>;\n\n  constexpr int max_tiles = 1024 * 1024;\n  // Use c2h::adjust_seed_count to reduce the number of runs when using sanitizers:\n  const int num_tiles = GENERATE_COPY(take(c2h::adjust_seed_count(10), random(1, max_tiles)));\n\n  c2h::device_vector<message_t> tile_data(num_tiles);\n  message_t* d_tile_data = thrust::raw_pointer_cast(tile_data.data());\n\n  c2h::gen(C2H_SEED(2), tile_data);\n  c2h::host_vector<message_t> reference = compute_reference(tile_data);\n\n  // Query temporary storage requirements\n  std::size_t temp_storage_bytes{};\n  scan_tile_state_t::AllocationSize(num_tiles, temp_storage_bytes);\n\n  // Allocate temporary storage\n  c2h::device_vector<std::uint8_t> temp_storage(temp_storage_bytes);\n  std::uint8_t* d_temp_storage = thrust::raw_pointer_cast(temp_storage.data());\n\n  // Initialize temporary storage\n  scan_tile_state_t tile_status;\n  hipError_t status = tile_status.Init(num_tiles, d_temp_storage, temp_storage_bytes);\n  REQUIRE(status == hipSuccess);\n\n  constexpr unsigned int threads_in_init_block = 256;\n  const unsigned int blocks_in_init_grid       = cuda::ceil_div(num_tiles, threads_in_init_block);\n  init_kernel<<<blocks_in_init_grid, threads_in_init_block>>>(tile_status, num_tiles);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  // Launch decoupled look-back\n  constexpr unsigned int threads_in_block = 256;\n  decoupled_look_back_kernel<<<num_tiles, threads_in_block>>>(tile_status, d_tile_data);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  REQUIRE(reference == tile_data);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_device_decoupled_look_back.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_reduce.cuh>\n\n#include <limits>\n#include <numeric>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <cub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__launch_bounds__(BlockDimX* BlockDimY* BlockDimZ) __global__\n  void block_reduce_kernel(T* in, T* out, int valid_items, ActionT action)\n{\n  using block_reduce_t = cub::BlockReduce<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t      = typename block_reduce_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? in[idx] : T();\n  }\n  __syncthreads();\n\n  block_reduce_t reduce(storage);\n\n  T aggregate = action(reduce, thread_data, valid_items);\n\n  if (tid == 0)\n  {\n    out[0] = aggregate;\n  }\n}\n\ntemplate <cub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_reduce(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_reduce_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT><<<1, block_dims>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), static_cast<int>(in.size()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nstruct sum_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Sum(thread_data[0], valid_items);\n  }\n};\n\nstruct sum_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int /* valid_items */) const\n  {\n    return reduce.Sum(thread_data);\n  }\n};\n\nstruct max_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Reduce(thread_data[0], cuda::maximum<>{}, valid_items);\n  }\n};\n\nstruct max_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int /* valid_items */) const\n  {\n    return reduce.Reduce(thread_data, cuda::maximum<>{});\n  }\n};\n\nusing types     = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t, float, double>;\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  uchar3,\n  short2>;\n\n// %PARAM% TEST_DIM_X dimx 1:7:32:65:128\n// %PARAM% TEST_DIM_YZ dimyz 1:2\n\nusing block_dim_xs           = c2h::enum_type_list<int, TEST_DIM_X>;\nusing block_dim_yzs          = c2h::enum_type_list<int, TEST_DIM_YZ>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 4>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithm =\n  c2h::enum_type_list<cub::BlockReduceAlgorithm,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY,\n                      cub::BlockReduceAlgorithm::BLOCK_REDUCE_WARP_REDUCTIONS>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nC2H_TEST(\n  \"Block reduce works with sum\", \"[reduce][block]\", types, items_per_thread, block_dim_xs, block_dim_yzs, algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, sum_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with sum in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  std::vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom op\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, max_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom op in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, max_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom types\", \"[reduce][block]\", block_dim_xs, block_dim_yzs, algorithm)\n{\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread                = 1;\n  constexpr int block_dim_x                     = c2h::get<0, TestType>::value;\n  constexpr int block_dim_y                     = c2h::get<1, TestType>::value;\n  constexpr int block_dim_z                     = block_dim_y;\n  constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<2, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z, type>(\n    d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\nC2H_TEST(\"Block reduce works with vec types\", \"[reduce][block]\", vec_types, block_dim_xs, block_dim_yzs, algorithm)\n{\n  using type = c2h::get<0, TestType>;\n\n  constexpr int items_per_thread                = 1;\n  constexpr int block_dim_x                     = c2h::get<1, TestType>::value;\n  constexpr int block_dim_y                     = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                     = block_dim_y;\n  constexpr cub::BlockReduceAlgorithm algorithm = c2h::get<3, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(C2H_SEED(10), d_in);\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z, type>(\n    d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_reduce.cuh>\n\n#include <limits>\n#include <numeric>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <hipcub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__launch_bounds__(BlockDimX* BlockDimY* BlockDimZ) __global__\n  void block_reduce_kernel(T* in, T* out, int valid_items, ActionT action)\n{\n  using block_reduce_t = hipcub::BlockReduce<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t      = typename block_reduce_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = idx < valid_items ? in[idx] : T();\n  }\n  __syncthreads();\n\n  block_reduce_t reduce(storage);\n\n  T aggregate = action(reduce, thread_data, valid_items);\n\n  if (tid == 0)\n  {\n    out[0] = aggregate;\n  }\n}\n\ntemplate <hipcub::BlockReduceAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_reduce(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_reduce_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT><<<1, block_dims>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), static_cast<int>(in.size()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nstruct sum_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Sum(thread_data[0], valid_items);\n  }\n};\n\nstruct sum_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int /* valid_items */) const\n  {\n    return reduce.Sum(thread_data);\n  }\n};\n\nstruct max_partial_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int valid_items) const\n  {\n    return reduce.Reduce(thread_data[0], cuda::maximum<>{}, valid_items);\n  }\n};\n\nstruct max_full_tile_op_t\n{\n  template <int ItemsPerThread, class BlockReduceT, class T>\n  __device__ T operator()(BlockReduceT& reduce, T (&thread_data)[ItemsPerThread], int /* valid_items */) const\n  {\n    return reduce.Reduce(thread_data, cuda::maximum<>{});\n  }\n};\n\nusing types     = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t, float, double>;\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  uchar3,\n  short2>;\n\n// %PARAM% TEST_DIM_X dimx 1:7:32:65:128\n// %PARAM% TEST_DIM_YZ dimyz 1:2\n\nusing block_dim_xs           = c2h::enum_type_list<int, TEST_DIM_X>;\nusing block_dim_yzs          = c2h::enum_type_list<int, TEST_DIM_YZ>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 4>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithm =\n  c2h::enum_type_list<hipcub::BlockReduceAlgorithm,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY,\n                      hipcub::BlockReduceAlgorithm::BLOCK_REDUCE_WARP_REDUCTIONS>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nC2H_TEST(\n  \"Block reduce works with sum\", \"[reduce][block]\", types, items_per_thread, block_dim_xs, block_dim_yzs, algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, sum_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with sum in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  std::vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom op\",\n         \"[reduce][block]\",\n         types,\n         items_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, max_full_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom op in partial tiles\",\n         \"[reduce][block]\",\n         types,\n         single_item_per_thread,\n         block_dim_xs,\n         block_dim_yzs,\n         algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return std::max(lhs, rhs);\n    }));\n\n  block_reduce<params::algorithm,\n               params::items_per_thread,\n               params::block_dim_x,\n               params::block_dim_y,\n               params::block_dim_z,\n               type>(d_in, d_out, max_partial_tile_op_t{});\n\n  REQUIRE_APPROX_EQ(h_reference, d_out);\n}\n\nC2H_TEST(\"Block reduce works with custom types\", \"[reduce][block]\", block_dim_xs, block_dim_yzs, algorithm)\n{\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  constexpr int items_per_thread                = 1;\n  constexpr int block_dim_x                     = c2h::get<0, TestType>::value;\n  constexpr int block_dim_y                     = c2h::get<1, TestType>::value;\n  constexpr int block_dim_z                     = block_dim_y;\n  constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<2, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(C2H_SEED(10), d_in, cuda::std::numeric_limits<type>::min());\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z, type>(\n    d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n\nC2H_TEST(\"Block reduce works with vec types\", \"[reduce][block]\", vec_types, block_dim_xs, block_dim_yzs, algorithm)\n{\n  using type = c2h::get<0, TestType>;\n\n  constexpr int items_per_thread                = 1;\n  constexpr int block_dim_x                     = c2h::get<1, TestType>::value;\n  constexpr int block_dim_y                     = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                     = block_dim_y;\n  constexpr hipcub::BlockReduceAlgorithm algorithm = c2h::get<3, TestType>::value;\n\n  constexpr int tile_size = block_dim_x * block_dim_y * block_dim_z * items_per_thread;\n\n  c2h::device_vector<type> d_out(1);\n  c2h::device_vector<type> d_in(GENERATE_COPY(take(2, random(1, tile_size))));\n  c2h::gen(C2H_SEED(10), d_in);\n\n  c2h::host_vector<type> h_in = d_in;\n  c2h::host_vector<type> h_reference(\n    1, std::accumulate(h_in.begin() + 1, h_in.end(), h_in[0], [](const type& lhs, const type& rhs) {\n      return static_cast<type>(lhs + rhs);\n    }));\n\n  block_reduce<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z, type>(\n    d_in, d_out, sum_partial_tile_op_t{});\n\n  REQUIRE(h_reference == d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_reduce.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/block/block_shuffle.cuh>\n\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int BlockDimX, int BlockDimY, int BlockDimZ, int ItemsPerThread, class T, class ActionT>\n__global__ void block_shuffle_kernel(T* data, ActionT action)\n{\n  using block_shuffle_t = cub::BlockShuffle<T, BlockDimX, BlockDimY, BlockDimZ>;\n  using temp_storage_t  = typename block_shuffle_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_data[ItemsPerThread];\n\n  data += cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ) * ItemsPerThread;\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_data[item] = data[item];\n  }\n  __syncthreads();\n\n  block_shuffle_t block_shuffle(temp_storage);\n  action(block_shuffle, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    data[item] = thread_data[item];\n  }\n}\n\nstruct up_op_t\n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Up(thread_data, thread_data);\n  }\n};\n\nstruct offset_op_t\n{\n  int m_distance;\n\n  __host__ offset_op_t(int distance)\n      : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Offset(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\nstruct rotate_op_t\n{\n  unsigned int m_distance;\n\n  __host__ rotate_op_t(unsigned int distance)\n      : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Rotate(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\ntemplate <class T>\nstruct up_with_suffix_op_t\n{\n  int m_target_thread_id;\n  T* m_d_suffix_ptr;\n\n  __host__ up_with_suffix_op_t(int target_thread_id, T* d_suffix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_suffix_ptr(d_suffix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    T suffix{};\n\n    block_shuffle.Up(thread_data, thread_data, suffix);\n\n    if (cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_suffix_ptr[0] = suffix;\n    }\n  }\n};\n\nstruct down_op_t\n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Down(thread_data, thread_data);\n  }\n};\n\ntemplate <class T>\nstruct down_with_prefix_op_t\n{\n  int m_target_thread_id;\n  T* m_d_prefix_ptr;\n\n  __host__ down_with_prefix_op_t(int target_thread_id, T* d_prefix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_prefix_ptr(d_prefix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    T prefix{};\n\n    block_shuffle.Down(thread_data, thread_data, prefix);\n\n    if (cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_prefix_ptr[0] = prefix;\n    }\n  }\n};\n\ntemplate <int ItemsPerThread, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\nvoid block_shuffle(c2h::device_vector<T>& data, ActionT action)\n{\n  dim3 block(BlockDimX, BlockDimY, BlockDimZ);\n  block_shuffle_kernel<BlockDimX, BlockDimY, BlockDimZ, ItemsPerThread>\n    <<<1, block>>>(thrust::raw_pointer_cast(data.data()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n// %PARAM% MULTI_DIM mdim 0:1\n// %PARAM% DIM_IDX dim_idx 0:1:2\n\n#if MULTI_DIM\nusing block_dim_xs = c2h::enum_type_list<int, 7, 32, 64>;\nusing block_dim_yz = c2h::enum_type_list<int, 2>;\n#else\nusing block_dim_xs = c2h::enum_type_list<int, 64, 512, 1024>;\nusing block_dim_yz = c2h::enum_type_list<int, 1>;\n#endif\n\nusing block_dim_x = c2h::enum_type_list<int, c2h::get<DIM_IDX, block_dim_xs>::value>;\n\nusing types                  = c2h::type_list<std::int32_t, std::int64_t>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 2, 15>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int threads_in_block = block_dim_x * block_dim_y * block_dim_z;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block shuffle offset works\", \"[shuffle][block]\", types, single_item_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int distance = GENERATE_COPY(take(4, random(1 - params::tile_size, params::tile_size - 1)));\n\n  c2h::host_vector<type> h_data = d_data;\n  c2h::host_vector<type> h_ref(params::tile_size);\n\n  for (int i = 0; i < static_cast<int>(h_data.size()); i++)\n  {\n    const int source = i + distance;\n    h_ref[i]         = (source >= 0) && (source < params::tile_size) ? h_data[source] : h_data[i];\n  }\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, offset_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle rotate works\", \"[shuffle][block]\", types, single_item_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref = d_data;\n\n  const unsigned int distance = GENERATE_COPY(take(4, random(0, params::tile_size - 1)));\n\n  c2h::host_vector<type> h_ref = d_data;\n  std::rotate(h_ref.begin(), h_ref.begin() + distance, h_ref.end());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, rotate_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle up works\", \"[shuffle][block]\", types, items_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, up_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle up works when suffix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  c2h::device_vector<type> d_suffix(1);\n  c2h::device_vector<type> d_suffix_ref(1);\n  thrust::copy(d_data.end() - 1, d_data.end(), d_suffix_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, up_with_suffix_op_t<type>{target_thread_id, thrust::raw_pointer_cast(d_suffix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_suffix_ref == d_suffix);\n}\n\nC2H_TEST(\"Block shuffle down works\", \"[shuffle][block]\", types, items_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, down_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle down works when prefix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  c2h::device_vector<type> d_prefix(1);\n  c2h::device_vector<type> d_prefix_ref(1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_prefix_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, down_with_prefix_op_t<type>{target_thread_id, thrust::raw_pointer_cast(d_prefix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_prefix_ref == d_prefix);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockMergeSort utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_shuffle.cuh>\n\n#include <thrust/sort.h>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int BlockDimX, int BlockDimY, int BlockDimZ, int ItemsPerThread, class T, class ActionT>\n__global__ void block_shuffle_kernel(T* data, ActionT action)\n{\n  using block_shuffle_t = hipcub::BlockShuffle<T, BlockDimX, BlockDimY, BlockDimZ>;\n  using temp_storage_t  = typename block_shuffle_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_data[ItemsPerThread];\n\n  data += hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ) * ItemsPerThread;\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_data[item] = data[item];\n  }\n  __syncthreads();\n\n  block_shuffle_t block_shuffle(temp_storage);\n  action(block_shuffle, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    data[item] = thread_data[item];\n  }\n}\n\nstruct up_op_t\n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Up(thread_data, thread_data);\n  }\n};\n\nstruct offset_op_t\n{\n  int m_distance;\n\n  __host__ offset_op_t(int distance)\n      : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Offset(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\nstruct rotate_op_t\n{\n  unsigned int m_distance;\n\n  __host__ rotate_op_t(unsigned int distance)\n      : m_distance(distance)\n  {}\n\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Rotate(thread_data[0], thread_data[0], m_distance);\n  }\n};\n\ntemplate <class T>\nstruct up_with_suffix_op_t\n{\n  int m_target_thread_id;\n  T* m_d_suffix_ptr;\n\n  __host__ up_with_suffix_op_t(int target_thread_id, T* d_suffix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_suffix_ptr(d_suffix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    T suffix{};\n\n    block_shuffle.Up(thread_data, thread_data, suffix);\n\n    if (hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_suffix_ptr[0] = suffix;\n    }\n  }\n};\n\nstruct down_op_t\n{\n  template <class BlockShuffleT, class T, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    block_shuffle.Down(thread_data, thread_data);\n  }\n};\n\ntemplate <class T>\nstruct down_with_prefix_op_t\n{\n  int m_target_thread_id;\n  T* m_d_prefix_ptr;\n\n  __host__ down_with_prefix_op_t(int target_thread_id, T* d_prefix_ptr)\n      : m_target_thread_id(target_thread_id)\n      , m_d_prefix_ptr(d_prefix_ptr)\n  {}\n\n  template <class BlockShuffleT, int ItemsPerThread>\n  __device__ void operator()(BlockShuffleT& block_shuffle, T (&thread_data)[ItemsPerThread]) const\n  {\n    T prefix{};\n\n    block_shuffle.Down(thread_data, thread_data, prefix);\n\n    if (hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z) == m_target_thread_id)\n    {\n      m_d_prefix_ptr[0] = prefix;\n    }\n  }\n};\n\ntemplate <int ItemsPerThread, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\nvoid block_shuffle(c2h::device_vector<T>& data, ActionT action)\n{\n  dim3 block(BlockDimX, BlockDimY, BlockDimZ);\n  block_shuffle_kernel<BlockDimX, BlockDimY, BlockDimZ, ItemsPerThread>\n    <<<1, block>>>(thrust::raw_pointer_cast(data.data()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n// %PARAM% MULTI_DIM mdim 0:1\n// %PARAM% DIM_IDX dim_idx 0:1:2\n\n#if MULTI_DIM\nusing block_dim_xs = c2h::enum_type_list<int, 7, 32, 64>;\nusing block_dim_yz = c2h::enum_type_list<int, 2>;\n#else\nusing block_dim_xs = c2h::enum_type_list<int, 64, 512, 1024>;\nusing block_dim_yz = c2h::enum_type_list<int, 1>;\n#endif\n\nusing block_dim_x = c2h::enum_type_list<int, c2h::get<DIM_IDX, block_dim_xs>::value>;\n\nusing types                  = c2h::type_list<std::int32_t, std::int64_t>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 2, 15>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_x      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<3, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int threads_in_block = block_dim_x * block_dim_y * block_dim_z;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block shuffle offset works\", \"[shuffle][block]\", types, single_item_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int distance = GENERATE_COPY(take(4, random(1 - params::tile_size, params::tile_size - 1)));\n\n  c2h::host_vector<type> h_data = d_data;\n  c2h::host_vector<type> h_ref(params::tile_size);\n\n  for (int i = 0; i < static_cast<int>(h_data.size()); i++)\n  {\n    const int source = i + distance;\n    h_ref[i]         = (source >= 0) && (source < params::tile_size) ? h_data[source] : h_data[i];\n  }\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, offset_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle rotate works\", \"[shuffle][block]\", types, single_item_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref = d_data;\n\n  const unsigned int distance = GENERATE_COPY(take(4, random(0, params::tile_size - 1)));\n\n  c2h::host_vector<type> h_ref = d_data;\n  std::rotate(h_ref.begin(), h_ref.begin() + distance, h_ref.end());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, rotate_op_t{distance});\n\n  REQUIRE(h_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle up works\", \"[shuffle][block]\", types, items_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, up_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle up works when suffix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin(), d_data.end() - 1, d_ref.begin() + 1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_ref.begin());\n\n  c2h::device_vector<type> d_suffix(1);\n  c2h::device_vector<type> d_suffix_ref(1);\n  thrust::copy(d_data.end() - 1, d_data.end(), d_suffix_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, up_with_suffix_op_t<type>{target_thread_id, thrust::raw_pointer_cast(d_suffix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_suffix_ref == d_suffix);\n}\n\nC2H_TEST(\"Block shuffle down works\", \"[shuffle][block]\", types, items_per_thread, block_dim_x, block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, down_op_t{});\n\n  REQUIRE(d_ref == d_data);\n}\n\nC2H_TEST(\"Block shuffle down works when prefix is required\",\n         \"[shuffle][block]\",\n         types,\n         items_per_thread,\n         block_dim_x,\n         block_dim_yz)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, params::threads_in_block - 1)));\n\n  c2h::device_vector<type> d_ref(params::tile_size);\n  thrust::copy(d_data.begin() + 1, d_data.end(), d_ref.begin());\n  thrust::copy(d_data.end() - 1, d_data.end(), d_ref.end() - 1);\n\n  c2h::device_vector<type> d_prefix(1);\n  c2h::device_vector<type> d_prefix_ref(1);\n  thrust::copy(d_data.begin(), d_data.begin() + 1, d_prefix_ref.begin());\n\n  block_shuffle<params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_data, down_with_prefix_op_t<type>{target_thread_id, thrust::raw_pointer_cast(d_prefix.data())});\n\n  REQUIRE(d_ref == d_data);\n  REQUIRE(d_prefix_ref == d_prefix);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_shuffle.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cub/thread/thread_scan.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"catch2_test_device_scan.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Scan Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename Accum, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel(\n  In d_in, Out d_out, ScanOperator scan_operator, int valid_items, Accum prefix, bool apply_prefix, Accum filler)\n{\n  using value_t  = cuda::std::iter_value_t<In>;\n  using output_t = cuda::std::iter_value_t<Out>;\n  value_t thread_input[NumItems];\n  output_t thread_output[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_input[i]  = d_in[i];\n    thread_output[i] = static_cast<output_t>(filler);\n  }\n  cub::detail::ThreadScanInclusivePartial(thread_input, thread_output, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_output[i];\n  }\n}\n\n// The following kernels are less general/complex with the added benefit that we can test doing the scan in-place\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_array(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cub::detail::ThreadScanInclusivePartial(thread_data, thread_data, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_span(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  cub::detail::ThreadScanInclusivePartial(span, span, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_mdspan(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  cub::detail::ThreadScanInclusivePartial(mdspan, mdspan, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  type_pair<half_t>,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  type_pair<bfloat16_t>\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<type_pair<cuda::std::int8_t, cuda::std::int32_t>,\n                 type_pair<cuda::std::uint16_t>,\n                 type_pair<cuda::std::int32_t>,\n                 type_pair<cuda::std::uint64_t>>;\n\nusing fp_type_list = c2h::type_list<type_pair<float>, type_pair<double>>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_xor<>, cuda::maximum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadScanInclusive Integral Type Tests\",\n         \"[scan][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using params                     = params_t<TestType>;\n  using value_t                    = typename params::item_t;\n  using output_t                   = typename params::output_t;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param          = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op           = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(),\n          c2h::type_name<output_t>(),\n          c2h::type_name<accum_t>(),\n          num_items,\n          c2h::type_name<op_t>(),\n          valid_items,\n          operator_identity,\n          prefix,\n          apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, static_cast<output_t>(filler));\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\nC2H_TEST(\"ThreadScanInclusive Floating-Point Type Tests\",\n         \"[scan][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(),\n          c2h::type_name<output_t>(),\n          c2h::type_name<accum_t>(),\n          num_items,\n          c2h::type_name<op_t>(),\n          valid_items,\n          operator_identity,\n          prefix,\n          apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanInclusive Narrow PrecisionType Tests\",\n         \"[scan][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  auto prefix =\n    static_cast<accum_t>(GENERATE_COPY(take(1, random(float{dist_param::min()}, float{dist_param::max()}))));\n  const bool apply_prefix = GENERATE(true, false);\n  auto filler =\n    static_cast<accum_t>(GENERATE(take(1, random(float{filler_dist_param::min()}, float{filler_dist_param::max()}))));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    scan_op,\n    valid_items,\n    *unwrap_it(&prefix),\n    apply_prefix,\n    *unwrap_it(&filler));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanInclusive Container Tests\", \"[scan][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size, thrust::no_init);\n  c2h::device_vector<int> d_out(max_size, thrust::no_init);\n  using dist_param = dist_interval<int, cuda::std::plus<>, max_size>;\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, max_size - 1))),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size, max_size + 1}));\n  const int bounded_valid_items          = cuda::std::min(valid_items, max_size);\n  c2h::host_vector<int> reference_result = h_in;\n  compute_inclusive_scan_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reference_result.begin(), cuda::std::plus<>{}, 0);\n\n  thread_scan_inclusive_partial_kernel_array<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_inclusive_partial_kernel_span<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n#if _CCCL_STD_VER >= 2023\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_inclusive_partial_kernel_mdspan<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadScanInclusive Invalid Test\", \"[scan][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  const bool apply_prefix       = GENERATE(true, false);\n  // Invalid prefix when not apply_prefix\n  const segment prefix{0, apply_prefix ? 1 : 0};\n  CAPTURE(valid_items, prefix, apply_prefix);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  c2h::host_vector<segment> reference_result(max_size);\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    merge_segments_op{nullptr},\n    apply_prefix ? prefix : segment{1, 1});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_scan_inclusive_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items,\n    prefix,\n    apply_prefix,\n    segment{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  REQUIRE(reference_result == d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cub/thread/thread_scan.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"catch2_test_device_scan.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Scan Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename Accum, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel(\n  In d_in, Out d_out, ScanOperator scan_operator, int valid_items, Accum prefix, bool apply_prefix, Accum filler)\n{\n  using value_t  = cuda::std::iter_value_t<In>;\n  using output_t = cuda::std::iter_value_t<Out>;\n  value_t thread_input[NumItems];\n  output_t thread_output[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_input[i]  = d_in[i];\n    thread_output[i] = static_cast<output_t>(filler);\n  }\n  hipcub::detail::ThreadScanInclusivePartial(thread_input, thread_output, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_output[i];\n  }\n}\n\n// The following kernels are less general/complex with the added benefit that we can test doing the scan in-place\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_array(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  hipcub::detail::ThreadScanInclusivePartial(thread_data, thread_data, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_span(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  hipcub::detail::ThreadScanInclusivePartial(span, span, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_inclusive_partial_kernel_mdspan(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  hipcub::detail::ThreadScanInclusivePartial(mdspan, mdspan, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  type_pair<half_t>,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  type_pair<bfloat16_t>\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<type_pair<cuda::std::int8_t, cuda::std::int32_t>,\n                 type_pair<cuda::std::uint16_t>,\n                 type_pair<cuda::std::int32_t>,\n                 type_pair<cuda::std::uint64_t>>;\n\nusing fp_type_list = c2h::type_list<type_pair<float>, type_pair<double>>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_xor<>, cuda::maximum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadScanInclusive Integral Type Tests\",\n         \"[scan][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using params                     = params_t<TestType>;\n  using value_t                    = typename params::item_t;\n  using output_t                   = typename params::output_t;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param          = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op           = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(),\n          c2h::type_name<output_t>(),\n          c2h::type_name<accum_t>(),\n          num_items,\n          c2h::type_name<op_t>(),\n          valid_items,\n          operator_identity,\n          prefix,\n          apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, static_cast<output_t>(filler));\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\nC2H_TEST(\"ThreadScanInclusive Floating-Point Type Tests\",\n         \"[scan][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(),\n          c2h::type_name<output_t>(),\n          c2h::type_name<accum_t>(),\n          num_items,\n          c2h::type_name<op_t>(),\n          valid_items,\n          operator_identity,\n          prefix,\n          apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanInclusive Narrow PrecisionType Tests\",\n         \"[scan][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  auto prefix =\n    static_cast<accum_t>(GENERATE_COPY(take(1, random(float{dist_param::min()}, float{dist_param::max()}))));\n  const bool apply_prefix = GENERATE(true, false);\n  auto filler =\n    static_cast<accum_t>(GENERATE(take(1, random(float{filler_dist_param::min()}, float{filler_dist_param::max()}))));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    scan_op,\n    apply_prefix ? prefix : operator_identity);\n\n  thread_scan_inclusive_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    scan_op,\n    valid_items,\n    *unwrap_it(&prefix),\n    apply_prefix,\n    *unwrap_it(&filler));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanInclusive Container Tests\", \"[scan][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size, thrust::no_init);\n  c2h::device_vector<int> d_out(max_size, thrust::no_init);\n  using dist_param = dist_interval<int, cuda::std::plus<>, max_size>;\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, max_size - 1))),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size, max_size + 1}));\n  const int bounded_valid_items          = cuda::std::min(valid_items, max_size);\n  c2h::host_vector<int> reference_result = h_in;\n  compute_inclusive_scan_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reference_result.begin(), cuda::std::plus<>{}, 0);\n\n  thread_scan_inclusive_partial_kernel_array<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_inclusive_partial_kernel_span<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n#if _CCCL_STD_VER >= 2023\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_inclusive_partial_kernel_mdspan<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadScanInclusive Invalid Test\", \"[scan][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  const bool apply_prefix       = GENERATE(true, false);\n  // Invalid prefix when not apply_prefix\n  const segment prefix{0, apply_prefix ? 1 : 0};\n  CAPTURE(valid_items, prefix, apply_prefix);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  c2h::host_vector<segment> reference_result(max_size);\n  compute_inclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    merge_segments_op{nullptr},\n    apply_prefix ? prefix : segment{1, 1});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_scan_inclusive_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items,\n    prefix,\n    apply_prefix,\n    segment{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  REQUIRE(reference_result == d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_thread_scan_inclusive_partial.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test evaluation for caching allocator of device memory\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/util_allocator.cuh>\n#include <cub/util_device.cuh>\n\n#include <cuda/std/cstdint>\n\n#include <cstdio>\n\n#include \"test_util.h\"\n\nusing namespace cub;\n\n// Borrowing nvbench's blocking_kernel for host-side control of kernel lifetimes.\n// This kernel does very bad things that violate the CUDA programming model, but\n// is okay for the tests here. Do not use this pattern in production code.\n\n// Once launched, this kernel will block the stream until `flag` updates to non-zero.\n__global__ void block_stream(const volatile cuda::std::int32_t* flag)\n{\n  while (!(*flag))\n  {\n  }\n}\n\nstruct blocking_kernel\n{\n  blocking_kernel(const cudaStream_t& stream)\n      : m_stream(stream)\n  {\n    CubDebugExit(cudaHostRegister(&m_host_flag, sizeof(m_host_flag), cudaHostRegisterMapped));\n    CubDebugExit(cudaHostGetDevicePointer(&m_device_flag, &m_host_flag, 0));\n  }\n  ~blocking_kernel()\n  {\n    CubDebugExit(cudaHostUnregister(&m_host_flag));\n  }\n\n  void block()\n  {\n    _CubLog(\"Blocking Stream %lld\\n\", (long long) m_stream);\n    m_host_flag = 0;\n    block_stream<<<1, 1, 0, m_stream>>>(m_device_flag);\n  }\n\n  void unblock()\n  {\n    volatile cuda::std::int32_t& flag = m_host_flag;\n    flag                              = 1;\n    _CubLog(\"Unblocking Stream %lld\\n\", (long long) m_stream);\n  }\n\nprivate:\n  cuda::std::int32_t m_host_flag{};\n  cuda::std::int32_t* m_device_flag{};\n  cudaStream_t m_stream{nullptr};\n};\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>]\"\n           \"[--bytes=<timing bytes>]\"\n           \"[--i=<timing iterations>]\"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Get number of GPUs and current GPU\n  int num_gpus;\n  int initial_gpu;\n  int timing_iterations = 10000;\n  int timing_bytes      = 1024 * 1024;\n\n  if (CubDebug(cudaGetDeviceCount(&num_gpus)))\n  {\n    exit(1);\n  }\n  if (CubDebug(cudaGetDevice(&initial_gpu)))\n  {\n    exit(1);\n  }\n  args.GetCmdLineArgument(\"i\", timing_iterations);\n  args.GetCmdLineArgument(\"bytes\", timing_bytes);\n\n  // Create default allocator (caches up to 6MB in device allocations per GPU)\n  CachingDeviceAllocator allocator;\n  allocator.debug = true;\n\n  printf(\"Running single-gpu tests...\\n\");\n  fflush(stdout);\n\n  //\n  // Test0\n  //\n\n  // Create a new stream\n  cudaStream_t other_stream;\n  CubDebugExit(cudaStreamCreate(&other_stream));\n\n  // Allocate 999 bytes on the current gpu in stream0\n  char* d_999B_stream0_a;\n  char* d_999B_stream0_b;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n\n  // Run a kernel on stream 0\n  blocking_kernel block_0_a(nullptr);\n  block_0_a.block();\n\n  // Free d_999B_stream0_a\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n\n  // Allocate another 999 bytes in stream 0\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Launch another kernel on stream 0\n  blocking_kernel block_0_b(nullptr);\n  block_0_b.block();\n\n  // Free d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Allocate 999 bytes on the current gpu in other_stream\n  char* d_999B_stream_other_a;\n  char* d_999B_stream_other_b;\n  allocator.DeviceAllocate((void**) &d_999B_stream_other_a, 999, other_stream);\n\n  // Check that that we have 1 live blocks on the initial GPU (that we allocated a new one because d_999B_stream0_b is\n  // only available for stream 0 until it becomes idle)\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have one cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  // Now run a kernel in other_stream\n  blocking_kernel block_other(other_stream);\n  block_other.block();\n\n  // Free d_999B_stream_other\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_a));\n\n  // Check that we can now use both allocations in stream 0 after unblocking both kernels:\n  block_0_a.unblock();\n  block_0_b.unblock();\n  block_other.unblock();\n  CubDebugExit(cudaDeviceSynchronize());\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Free d_999B_stream0_a and d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Check that we can now use both allocations in other_stream\n  CubDebugExit(cudaDeviceSynchronize());\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream_other_a, 999, other_stream));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream_other_b, 999, other_stream));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Run some big kernel in other_stream\n  block_other.block();\n\n  // Free d_999B_stream_other_a and d_999B_stream_other_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_b));\n\n  // Check that we can now use both allocations in stream 0 after synchronizing the device and destroying the other\n  // stream\n  block_other.unblock();\n  CubDebugExit(cudaDeviceSynchronize());\n  CubDebugExit(cudaStreamDestroy(other_stream));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Free d_999B_stream0_a and d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Free all cached\n  CubDebugExit(allocator.FreeAllCached());\n\n  //\n  // Test1\n  //\n\n  // Allocate 5 bytes on the current gpu\n  char* d_5B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_5B, 5));\n\n  // Check that that we have zero free bytes cached on the initial GPU\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, 0);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  //\n  // Test2\n  //\n\n  // Allocate 4096 bytes on the current gpu\n  char* d_4096B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_4096B, 4096));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  //\n  // Test3\n  //\n\n  // DeviceFree d_5B\n  CubDebugExit(allocator.DeviceFree(d_5B));\n\n  // Check that that we have min_bin_bytes free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test4\n  //\n\n  // DeviceFree d_4096B\n  CubDebugExit(allocator.DeviceFree(d_4096B));\n\n  // Check that that we have the 4096 + min_bin free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes + 4096);\n\n  // Check that that we have 0 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 0);\n\n  // Check that that we have 2 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 2);\n\n  //\n  // Test5\n  //\n\n  // Allocate 768 bytes on the current gpu\n  char* d_768B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_768B, 768));\n\n  // Check that that we have the min_bin free bytes cached on the initial gpu (4096 was reused)\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test6\n  //\n\n  // Allocate max_cached_bytes on the current gpu\n  char* d_max_cached;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_max_cached, allocator.max_cached_bytes));\n\n  // DeviceFree d_max_cached\n  CubDebugExit(allocator.DeviceFree(d_max_cached));\n\n  // Check that that we have the min_bin free bytes cached on the initial gpu (max cached was not returned because we\n  // went over)\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we still have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test7\n  //\n\n  // Free all cached blocks on all GPUs\n  CubDebugExit(allocator.FreeAllCached());\n\n  // Check that that we have 0 bytes cached on the initial GPU\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, 0);\n\n  // Check that that we have 0 cached blocks across all GPUs\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Check that that still we have 1 live block across all GPUs\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  //\n  // Test8\n  //\n\n  // Allocate max cached bytes + 1 on the current gpu\n  char* d_max_cached_plus;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_max_cached_plus, allocator.max_cached_bytes + 1));\n\n  // DeviceFree max cached bytes\n  CubDebugExit(allocator.DeviceFree(d_max_cached_plus));\n\n  // DeviceFree d_768B\n  CubDebugExit(allocator.DeviceFree(d_768B));\n\n  unsigned int power;\n  size_t rounded_bytes;\n  allocator.NearestPowerOf(power, rounded_bytes, allocator.bin_growth, 768);\n\n  // Check that that we have 4096 free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, rounded_bytes);\n\n  // Check that that we have 1 cached blocks across all GPUs\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  // Check that that still we have 0 live block across all GPUs\n  AssertEquals(allocator.live_blocks.size(), 0);\n\n  // BUG: find out why these tests fail when one GPU is CDP compliant and the other is not\n\n  if (num_gpus > 1)\n  {\n    printf(\"\\nRunning multi-gpu tests...\\n\");\n    fflush(stdout);\n\n    //\n    // Test9\n    //\n\n    // Allocate 768 bytes on the next gpu\n    int next_gpu = (initial_gpu + 1) % num_gpus;\n    char* d_768B_2;\n    CubDebugExit(allocator.DeviceAllocate(next_gpu, (void**) &d_768B_2, 768));\n\n    // DeviceFree d_768B on the next gpu\n    CubDebugExit(allocator.DeviceFree(next_gpu, d_768B_2));\n\n    // Re-allocate 768 bytes on the next gpu\n    CubDebugExit(allocator.DeviceAllocate(next_gpu, (void**) &d_768B_2, 768));\n\n    // Re-free d_768B on the next gpu\n    CubDebugExit(allocator.DeviceFree(next_gpu, d_768B_2));\n\n    // Check that that we have 4096 free bytes cached on the initial gpu\n    AssertEquals(allocator.cached_bytes[initial_gpu].free, rounded_bytes);\n\n    // Check that that we have 4096 free bytes cached on the second gpu\n    AssertEquals(allocator.cached_bytes[next_gpu].free, rounded_bytes);\n\n    // Check that that we have 2 cached blocks across all GPUs\n    AssertEquals(allocator.cached_blocks.size(), 2);\n\n    // Check that that still we have 0 live block across all GPUs\n    AssertEquals(allocator.live_blocks.size(), 0);\n  }\n\n  //\n  // Performance\n  //\n\n  printf(\"\\nCPU Performance (%d timing iterations, %d bytes):\\n\", timing_iterations, timing_bytes);\n  fflush(stdout);\n  fflush(stderr);\n\n  // CPU performance comparisons vs cached.  Allocate and free a 1MB block 2000 times\n  CpuTimer cpu_timer;\n  char* d_1024MB  = nullptr;\n  allocator.debug = false;\n\n  // Prime the caching allocator and the kernel\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n  CubDebugExit(allocator.DeviceFree(d_1024MB));\n  cub::detail::EmptyKernel<void><<<1, 32>>>();\n\n  // CUDA\n  cpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(cudaMalloc((void**) &d_1024MB, timing_bytes));\n    CubDebugExit(cudaFree(d_1024MB));\n  }\n  cpu_timer.Stop();\n  float cuda_malloc_elapsed_millis = cpu_timer.ElapsedMillis();\n\n  // CUB\n  cpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n    CubDebugExit(allocator.DeviceFree(d_1024MB));\n  }\n  cpu_timer.Stop();\n  float cub_calloc_elapsed_millis = cpu_timer.ElapsedMillis();\n\n  printf(\"\\t CUB CachingDeviceAllocator allocation CPU speedup: %.2f (avg cudaMalloc %.4f ms vs. avg DeviceAllocate \"\n         \"%.4f ms)\\n\",\n         cuda_malloc_elapsed_millis / cub_calloc_elapsed_millis,\n         cuda_malloc_elapsed_millis / timing_iterations,\n         cub_calloc_elapsed_millis / timing_iterations);\n\n  // GPU performance comparisons.  Allocate and free a 1MB block 2000 times\n  GpuTimer gpu_timer;\n\n  printf(\"\\nGPU Performance (%d timing iterations, %d bytes):\\n\", timing_iterations, timing_bytes);\n  fflush(stdout);\n  fflush(stderr);\n\n  // Kernel-only\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    cub::detail::EmptyKernel<void><<<1, 32>>>();\n  }\n  gpu_timer.Stop();\n  float cuda_empty_elapsed_millis = gpu_timer.ElapsedMillis();\n\n  // CUDA\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(cudaMalloc((void**) &d_1024MB, timing_bytes));\n    cub::detail::EmptyKernel<void><<<1, 32>>>();\n    CubDebugExit(cudaFree(d_1024MB));\n  }\n  gpu_timer.Stop();\n  cuda_malloc_elapsed_millis = gpu_timer.ElapsedMillis() - cuda_empty_elapsed_millis;\n\n  // CUB\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n    cub::detail::EmptyKernel<void><<<1, 32>>>();\n    CubDebugExit(allocator.DeviceFree(d_1024MB));\n  }\n  gpu_timer.Stop();\n  cub_calloc_elapsed_millis = gpu_timer.ElapsedMillis() - cuda_empty_elapsed_millis;\n\n  printf(\"\\t CUB CachingDeviceAllocator allocation GPU speedup: %.2f (avg cudaMalloc %.4f ms vs. avg DeviceAllocate \"\n         \"%.4f ms)\\n\",\n         cuda_malloc_elapsed_millis / cub_calloc_elapsed_millis,\n         cuda_malloc_elapsed_millis / timing_iterations,\n         cub_calloc_elapsed_millis / timing_iterations);\n\n  printf(\"Success\\n\");\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.\n// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test evaluation for caching allocator of device memory\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/util_allocator.cuh>\n#include <cub/util_device.cuh>\n\n#include <cuda/std/cstdint>\n\n#include <cstdio>\n\n#include \"test_util.h\"\n\nusing namespace hipcub;\n\n// Borrowing nvbench's blocking_kernel for host-side control of kernel lifetimes.\n// This kernel does very bad things that violate the CUDA programming model, but\n// is okay for the tests here. Do not use this pattern in production code.\n\n// Once launched, this kernel will block the stream until `flag` updates to non-zero.\n__global__ void block_stream(const volatile cuda::std::int32_t* flag)\n{\n  while (!(*flag))\n  {\n  }\n}\n\nstruct blocking_kernel\n{\n  blocking_kernel(const hipStream_t& stream)\n      : m_stream(stream)\n  {\n    CubDebugExit(hipHostRegister(&m_host_flag, sizeof(m_host_flag), hipHostRegisterMapped));\n    CubDebugExit(hipHostGetDevicePointer(&m_device_flag, &m_host_flag, 0));\n  }\n  ~blocking_kernel()\n  {\n    CubDebugExit(hipHostUnregister(&m_host_flag));\n  }\n\n  void block()\n  {\n    _HipcubLog(\"Blocking Stream %lld\\n\", (long long) m_stream);\n    m_host_flag = 0;\n    block_stream<<<1, 1, 0, m_stream>>>(m_device_flag);\n  }\n\n  void unblock()\n  {\n    volatile cuda::std::int32_t& flag = m_host_flag;\n    flag                              = 1;\n    _HipcubLog(\"Unblocking Stream %lld\\n\", (long long) m_stream);\n  }\n\nprivate:\n  cuda::std::int32_t m_host_flag{};\n  cuda::std::int32_t* m_device_flag{};\n  hipStream_t m_stream{nullptr};\n};\n\n//---------------------------------------------------------------------\n// Main\n//---------------------------------------------------------------------\n\n/**\n * Main\n */\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>]\"\n           \"[--bytes=<timing bytes>]\"\n           \"[--i=<timing iterations>]\"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  // Get number of GPUs and current GPU\n  int num_gpus;\n  int initial_gpu;\n  int timing_iterations = 10000;\n  int timing_bytes      = 1024 * 1024;\n\n  if (HipcubDebug(hipGetDeviceCount(&num_gpus)))\n  {\n    exit(1);\n  }\n  if (HipcubDebug(hipGetDevice(&initial_gpu)))\n  {\n    exit(1);\n  }\n  args.GetCmdLineArgument(\"i\", timing_iterations);\n  args.GetCmdLineArgument(\"bytes\", timing_bytes);\n\n  // Create default allocator (caches up to 6MB in device allocations per GPU)\n  CachingDeviceAllocator allocator;\n  allocator.debug = true;\n\n  printf(\"Running single-gpu tests...\\n\");\n  fflush(stdout);\n\n  //\n  // Test0\n  //\n\n  // Create a new stream\n  hipStream_t other_stream;\n  CubDebugExit(hipStreamCreate(&other_stream));\n\n  // Allocate 999 bytes on the current gpu in stream0\n  char* d_999B_stream0_a;\n  char* d_999B_stream0_b;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n\n  // Run a kernel on stream 0\n  blocking_kernel block_0_a(nullptr);\n  block_0_a.block();\n\n  // Free d_999B_stream0_a\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n\n  // Allocate another 999 bytes in stream 0\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Launch another kernel on stream 0\n  blocking_kernel block_0_b(nullptr);\n  block_0_b.block();\n\n  // Free d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Allocate 999 bytes on the current gpu in other_stream\n  char* d_999B_stream_other_a;\n  char* d_999B_stream_other_b;\n  allocator.DeviceAllocate((void**) &d_999B_stream_other_a, 999, other_stream);\n\n  // Check that that we have 1 live blocks on the initial GPU (that we allocated a new one because d_999B_stream0_b is\n  // only available for stream 0 until it becomes idle)\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have one cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  // Now run a kernel in other_stream\n  blocking_kernel block_other(other_stream);\n  block_other.block();\n\n  // Free d_999B_stream_other\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_a));\n\n  // Check that we can now use both allocations in stream 0 after unblocking both kernels:\n  block_0_a.unblock();\n  block_0_b.unblock();\n  block_other.unblock();\n  CubDebugExit(hipDeviceSynchronize());\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Free d_999B_stream0_a and d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Check that we can now use both allocations in other_stream\n  CubDebugExit(hipDeviceSynchronize());\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream_other_a, 999, other_stream));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream_other_b, 999, other_stream));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Run some big kernel in other_stream\n  block_other.block();\n\n  // Free d_999B_stream_other_a and d_999B_stream_other_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream_other_b));\n\n  // Check that we can now use both allocations in stream 0 after synchronizing the device and destroying the other\n  // stream\n  block_other.unblock();\n  CubDebugExit(hipDeviceSynchronize());\n  CubDebugExit(hipStreamDestroy(other_stream));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_a, 999, nullptr));\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_999B_stream0_b, 999, nullptr));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  // Check that that we have no cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Free d_999B_stream0_a and d_999B_stream0_b\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_a));\n  CubDebugExit(allocator.DeviceFree(d_999B_stream0_b));\n\n  // Free all cached\n  CubDebugExit(allocator.FreeAllCached());\n\n  //\n  // Test1\n  //\n\n  // Allocate 5 bytes on the current gpu\n  char* d_5B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_5B, 5));\n\n  // Check that that we have zero free bytes cached on the initial GPU\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, 0);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  //\n  // Test2\n  //\n\n  // Allocate 4096 bytes on the current gpu\n  char* d_4096B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_4096B, 4096));\n\n  // Check that that we have 2 live blocks on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 2);\n\n  //\n  // Test3\n  //\n\n  // DeviceFree d_5B\n  CubDebugExit(allocator.DeviceFree(d_5B));\n\n  // Check that that we have min_bin_bytes free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test4\n  //\n\n  // DeviceFree d_4096B\n  CubDebugExit(allocator.DeviceFree(d_4096B));\n\n  // Check that that we have the 4096 + min_bin free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes + 4096);\n\n  // Check that that we have 0 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 0);\n\n  // Check that that we have 2 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 2);\n\n  //\n  // Test5\n  //\n\n  // Allocate 768 bytes on the current gpu\n  char* d_768B;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_768B, 768));\n\n  // Check that that we have the min_bin free bytes cached on the initial gpu (4096 was reused)\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test6\n  //\n\n  // Allocate max_cached_bytes on the current gpu\n  char* d_max_cached;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_max_cached, allocator.max_cached_bytes));\n\n  // DeviceFree d_max_cached\n  CubDebugExit(allocator.DeviceFree(d_max_cached));\n\n  // Check that that we have the min_bin free bytes cached on the initial gpu (max cached was not returned because we\n  // went over)\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, allocator.min_bin_bytes);\n\n  // Check that that we have 1 live block on the initial GPU\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  // Check that that we still have 1 cached block on the initial GPU\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  //\n  // Test7\n  //\n\n  // Free all cached blocks on all GPUs\n  CubDebugExit(allocator.FreeAllCached());\n\n  // Check that that we have 0 bytes cached on the initial GPU\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, 0);\n\n  // Check that that we have 0 cached blocks across all GPUs\n  AssertEquals(allocator.cached_blocks.size(), 0);\n\n  // Check that that still we have 1 live block across all GPUs\n  AssertEquals(allocator.live_blocks.size(), 1);\n\n  //\n  // Test8\n  //\n\n  // Allocate max cached bytes + 1 on the current gpu\n  char* d_max_cached_plus;\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_max_cached_plus, allocator.max_cached_bytes + 1));\n\n  // DeviceFree max cached bytes\n  CubDebugExit(allocator.DeviceFree(d_max_cached_plus));\n\n  // DeviceFree d_768B\n  CubDebugExit(allocator.DeviceFree(d_768B));\n\n  unsigned int power;\n  size_t rounded_bytes;\n  allocator.NearestPowerOf(power, rounded_bytes, allocator.bin_growth, 768);\n\n  // Check that that we have 4096 free bytes cached on the initial gpu\n  AssertEquals(allocator.cached_bytes[initial_gpu].free, rounded_bytes);\n\n  // Check that that we have 1 cached blocks across all GPUs\n  AssertEquals(allocator.cached_blocks.size(), 1);\n\n  // Check that that still we have 0 live block across all GPUs\n  AssertEquals(allocator.live_blocks.size(), 0);\n\n  // BUG: find out why these tests fail when one GPU is CDP compliant and the other is not\n\n  if (num_gpus > 1)\n  {\n    printf(\"\\nRunning multi-gpu tests...\\n\");\n    fflush(stdout);\n\n    //\n    // Test9\n    //\n\n    // Allocate 768 bytes on the next gpu\n    int next_gpu = (initial_gpu + 1) % num_gpus;\n    char* d_768B_2;\n    CubDebugExit(allocator.DeviceAllocate(next_gpu, (void**) &d_768B_2, 768));\n\n    // DeviceFree d_768B on the next gpu\n    CubDebugExit(allocator.DeviceFree(next_gpu, d_768B_2));\n\n    // Re-allocate 768 bytes on the next gpu\n    CubDebugExit(allocator.DeviceAllocate(next_gpu, (void**) &d_768B_2, 768));\n\n    // Re-free d_768B on the next gpu\n    CubDebugExit(allocator.DeviceFree(next_gpu, d_768B_2));\n\n    // Check that that we have 4096 free bytes cached on the initial gpu\n    AssertEquals(allocator.cached_bytes[initial_gpu].free, rounded_bytes);\n\n    // Check that that we have 4096 free bytes cached on the second gpu\n    AssertEquals(allocator.cached_bytes[next_gpu].free, rounded_bytes);\n\n    // Check that that we have 2 cached blocks across all GPUs\n    AssertEquals(allocator.cached_blocks.size(), 2);\n\n    // Check that that still we have 0 live block across all GPUs\n    AssertEquals(allocator.live_blocks.size(), 0);\n  }\n\n  //\n  // Performance\n  //\n\n  printf(\"\\nCPU Performance (%d timing iterations, %d bytes):\\n\", timing_iterations, timing_bytes);\n  fflush(stdout);\n  fflush(stderr);\n\n  // CPU performance comparisons vs cached.  Allocate and free a 1MB block 2000 times\n  CpuTimer cpu_timer;\n  char* d_1024MB  = nullptr;\n  allocator.debug = false;\n\n  // Prime the caching allocator and the kernel\n  CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n  CubDebugExit(allocator.DeviceFree(d_1024MB));\n  hipcub::detail::EmptyKernel<void><<<1, 32>>>();\n\n  // CUDA\n  cpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(hipMalloc((void**) &d_1024MB, timing_bytes));\n    CubDebugExit(hipFree(d_1024MB));\n  }\n  cpu_timer.Stop();\n  float cuda_malloc_elapsed_millis = cpu_timer.ElapsedMillis();\n\n  // CUB\n  cpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n    CubDebugExit(allocator.DeviceFree(d_1024MB));\n  }\n  cpu_timer.Stop();\n  float cub_calloc_elapsed_millis = cpu_timer.ElapsedMillis();\n\n  printf(\"\\t CUB CachingDeviceAllocator allocation CPU speedup: %.2f (avg hipMalloc %.4f ms vs. avg DeviceAllocate \"\n         \"%.4f ms)\\n\",\n         cuda_malloc_elapsed_millis / cub_calloc_elapsed_millis,\n         cuda_malloc_elapsed_millis / timing_iterations,\n         cub_calloc_elapsed_millis / timing_iterations);\n\n  // GPU performance comparisons.  Allocate and free a 1MB block 2000 times\n  GpuTimer gpu_timer;\n\n  printf(\"\\nGPU Performance (%d timing iterations, %d bytes):\\n\", timing_iterations, timing_bytes);\n  fflush(stdout);\n  fflush(stderr);\n\n  // Kernel-only\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    hipcub::detail::EmptyKernel<void><<<1, 32>>>();\n  }\n  gpu_timer.Stop();\n  float cuda_empty_elapsed_millis = gpu_timer.ElapsedMillis();\n\n  // CUDA\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(hipMalloc((void**) &d_1024MB, timing_bytes));\n    hipcub::detail::EmptyKernel<void><<<1, 32>>>();\n    CubDebugExit(hipFree(d_1024MB));\n  }\n  gpu_timer.Stop();\n  cuda_malloc_elapsed_millis = gpu_timer.ElapsedMillis() - cuda_empty_elapsed_millis;\n\n  // CUB\n  gpu_timer.Start();\n  for (int i = 0; i < timing_iterations; ++i)\n  {\n    CubDebugExit(allocator.DeviceAllocate((void**) &d_1024MB, timing_bytes));\n    hipcub::detail::EmptyKernel<void><<<1, 32>>>();\n    CubDebugExit(allocator.DeviceFree(d_1024MB));\n  }\n  gpu_timer.Stop();\n  cub_calloc_elapsed_millis = gpu_timer.ElapsedMillis() - cuda_empty_elapsed_millis;\n\n  printf(\"\\t CUB CachingDeviceAllocator allocation GPU speedup: %.2f (avg hipMalloc %.4f ms vs. avg DeviceAllocate \"\n         \"%.4f ms)\\n\",\n         cuda_malloc_elapsed_millis / cub_calloc_elapsed_millis,\n         cuda_malloc_elapsed_millis / timing_iterations,\n         cub_calloc_elapsed_millis / timing_iterations);\n\n  printf(\"Success\\n\");\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/test/test_allocator.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockHistogram utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#define CUB_STDERR\n\n#include <cub/block/block_histogram.cuh>\n\n#include <limits>\n#include <string>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int BINS,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          cub::BlockHistogramAlgorithm ALGORITHM,\n          typename T,\n          typename HistoCounter>\n__global__ void block_histogram_kernel(T* d_samples, HistoCounter* d_histogram)\n{\n  // Parameterize BlockHistogram type for our thread block\n  using block_histogram_t = cub::BlockHistogram<T, BLOCK_THREADS, ITEMS_PER_THREAD, BINS, ALGORITHM>;\n\n  // Allocate temp storage in shared memory\n  __shared__ typename block_histogram_t::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T data[ITEMS_PER_THREAD];\n  cub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_samples, data);\n\n  // Test histo (writing directly to histogram buffer in global)\n  block_histogram_t(temp_storage).Histogram(data, d_histogram);\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, int Bins, cub::BlockHistogramAlgorithm Algorithm, typename SampleT>\nvoid block_histogram(c2h::device_vector<SampleT>& d_samples, c2h::device_vector<int>& d_histogram)\n{\n  block_histogram_kernel<Bins, ThreadsInBlock, ItemsPerThread, Algorithm>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(d_samples.data()), thrust::raw_pointer_cast(d_histogram.data()));\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n// %PARAM% TEST_BINS bins 32:256:1024\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t>;\nusing threads_in_block = c2h::enum_type_list<int, 32, 96, 128>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 5>;\nusing bins             = c2h::enum_type_list<int, TEST_BINS>;\nusing algorithms = c2h::enum_type_list<cub::BlockHistogramAlgorithm, cub::BLOCK_HISTO_SORT, cub::BLOCK_HISTO_ATOMIC>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using sample_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                   = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                   = c2h::get<2, TestType>::value;\n  static constexpr int bins                               = c2h::get<3, TestType>::value;\n  static constexpr int num_samples                        = threads_in_block * items_per_thread;\n  static constexpr cub::BlockHistogramAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nC2H_TEST(\"Block histogram can be computed with uniform input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  const sample_t uniform_value = static_cast<sample_t>(GENERATE_COPY(take(10, random(0, params::bins - 1))));\n\n  c2h::host_vector<sample_t> h_samples(params::num_samples, uniform_value);\n  c2h::host_vector<int> h_reference(params::bins);\n  h_reference[static_cast<std::size_t>(uniform_value)] = params::num_samples;\n\n  // Allocate problem device arrays\n  c2h::device_vector<sample_t> d_samples = h_samples;\n  c2h::device_vector<int> d_histogram(params::bins);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\ntemplate <typename SampleT>\nc2h::host_vector<int> compute_host_reference(int bins, const c2h::host_vector<SampleT>& h_samples)\n{\n  c2h::host_vector<int> h_reference(bins);\n  for (const SampleT& sample : h_samples)\n  {\n    h_reference[sample]++;\n  }\n\n  return h_reference;\n}\n\nC2H_TEST(\"Block histogram can be computed with modulo input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  c2h::device_vector<int> d_histogram(params::bins);\n  c2h::device_vector<sample_t> d_samples(params::num_samples);\n\n  c2h::gen(c2h::modulo_t{params::bins}, d_samples);\n\n  c2h::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference                     = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\nC2H_TEST(\"Block histogram can be computed with random input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  c2h::device_vector<int> d_histogram(params::bins);\n  c2h::device_vector<sample_t> d_samples(params::num_samples);\n\n  const sample_t min_bin = static_cast<sample_t>(0);\n  const sample_t max_bin = static_cast<sample_t>(\n    std::min(static_cast<std::int32_t>(cuda::std::numeric_limits<sample_t>::max()),\n             static_cast<std::int32_t>(params::bins - 1)));\n\n  c2h::gen(C2H_SEED(10), d_samples, min_bin, max_bin);\n\n  c2h::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference                     = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n/******************************************************************************\n * Test of BlockHistogram utilities\n ******************************************************************************/\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_histogram.cuh>\n\n#include <limits>\n#include <string>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int BINS,\n          int BLOCK_THREADS,\n          int ITEMS_PER_THREAD,\n          hipcub::BlockHistogramAlgorithm ALGORITHM,\n          typename T,\n          typename HistoCounter>\n__global__ void block_histogram_kernel(T* d_samples, HistoCounter* d_histogram)\n{\n  // Parameterize BlockHistogram type for our thread block\n  using block_histogram_t = hipcub::BlockHistogram<T, BLOCK_THREADS, ITEMS_PER_THREAD, BINS, ALGORITHM>;\n\n  // Allocate temp storage in shared memory\n  __shared__ typename block_histogram_t::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T data[ITEMS_PER_THREAD];\n  hipcub::LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_samples, data);\n\n  // Test histo (writing directly to histogram buffer in global)\n  block_histogram_t(temp_storage).Histogram(data, d_histogram);\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, int Bins, hipcub::BlockHistogramAlgorithm Algorithm, typename SampleT>\nvoid block_histogram(c2h::device_vector<SampleT>& d_samples, c2h::device_vector<int>& d_histogram)\n{\n  block_histogram_kernel<Bins, ThreadsInBlock, ItemsPerThread, Algorithm>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(d_samples.data()), thrust::raw_pointer_cast(d_histogram.data()));\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n// %PARAM% TEST_BINS bins 32:256:1024\n\nusing types            = c2h::type_list<std::uint8_t, std::uint16_t>;\nusing threads_in_block = c2h::enum_type_list<int, 32, 96, 128>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 5>;\nusing bins             = c2h::enum_type_list<int, TEST_BINS>;\nusing algorithms = c2h::enum_type_list<hipcub::BlockHistogramAlgorithm, hipcub::BLOCK_HISTO_SORT, hipcub::BLOCK_HISTO_ATOMIC>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using sample_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                   = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                   = c2h::get<2, TestType>::value;\n  static constexpr int bins                               = c2h::get<3, TestType>::value;\n  static constexpr int num_samples                        = threads_in_block * items_per_thread;\n  static constexpr hipcub::BlockHistogramAlgorithm algorithm = c2h::get<4, TestType>::value;\n};\n\nC2H_TEST(\"Block histogram can be computed with uniform input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  const sample_t uniform_value = static_cast<sample_t>(GENERATE_COPY(take(10, random(0, params::bins - 1))));\n\n  c2h::host_vector<sample_t> h_samples(params::num_samples, uniform_value);\n  c2h::host_vector<int> h_reference(params::bins);\n  h_reference[static_cast<std::size_t>(uniform_value)] = params::num_samples;\n\n  // Allocate problem device arrays\n  c2h::device_vector<sample_t> d_samples = h_samples;\n  c2h::device_vector<int> d_histogram(params::bins);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\ntemplate <typename SampleT>\nc2h::host_vector<int> compute_host_reference(int bins, const c2h::host_vector<SampleT>& h_samples)\n{\n  c2h::host_vector<int> h_reference(bins);\n  for (const SampleT& sample : h_samples)\n  {\n    h_reference[sample]++;\n  }\n\n  return h_reference;\n}\n\nC2H_TEST(\"Block histogram can be computed with modulo input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  c2h::device_vector<int> d_histogram(params::bins);\n  c2h::device_vector<sample_t> d_samples(params::num_samples);\n\n  c2h::gen(c2h::modulo_t{params::bins}, d_samples);\n\n  c2h::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference                     = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n\nC2H_TEST(\"Block histogram can be computed with random input\",\n         \"[histogram][block]\",\n         types,\n         items_per_thread,\n         threads_in_block,\n         bins,\n         algorithms)\n{\n  using params   = params_t<TestType>;\n  using sample_t = typename params::sample_t;\n\n  // Allocate problem device arrays\n  c2h::device_vector<int> d_histogram(params::bins);\n  c2h::device_vector<sample_t> d_samples(params::num_samples);\n\n  const sample_t min_bin = static_cast<sample_t>(0);\n  const sample_t max_bin = static_cast<sample_t>(\n    std::min(static_cast<std::int32_t>(cuda::std::numeric_limits<sample_t>::max()),\n             static_cast<std::int32_t>(params::bins - 1)));\n\n  c2h::gen(C2H_SEED(10), d_samples, min_bin, max_bin);\n\n  c2h::host_vector<sample_t> h_samples = d_samples;\n  auto h_reference                     = compute_host_reference(params::bins, h_samples);\n\n  // Run kernel\n  block_histogram<params::items_per_thread, params::threads_in_block, params::bins, params::algorithm>(\n    d_samples, d_histogram);\n\n  REQUIRE(h_reference == d_histogram);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_histogram.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <cub/block/block_load_to_shared.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/util_type.cuh>\n\n#include <cuda/cmath>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n\nenum struct test_mode\n{\n  single_copy,\n  multi_copy_single_phase,\n  multi_copy_multi_phase,\n  multi_copy_multi_barrier,\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, test_mode Mode, typename InputPointerT, typename OutputIteratorT>\n__global__ void kernel(InputPointerT input, OutputIteratorT output, int num_items)\n{\n  using input_t         = cub::detail::it_value_t<InputPointerT>;\n  using block_load2sh_t = cub::detail::BlockLoadToShared<ThreadsInBlock>;\n  using storage_t       = typename block_load2sh_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load2sh_t block_load2sh{storage};\n\n  constexpr int tile_size                = ItemsPerThread * ThreadsInBlock;\n  constexpr int num_copies               = Mode == test_mode::single_copy ? 1 : 2;\n  constexpr int num_buffers              = (Mode == test_mode::multi_copy_single_phase) ? 2 : 1;\n  constexpr int max_num_items_first_copy = cuda::ceil_div(tile_size, num_copies);\n  constexpr int buffer_align             = cub::detail::LoadToSharedBufferAlignBytes<input_t>();\n  constexpr int buffer_size              = cub::detail::LoadToSharedBufferSizeBytes<input_t>(max_num_items_first_copy);\n  alignas(buffer_align) __shared__ char buffer[num_buffers][buffer_size];\n  cuda::std::span<const input_t> src{input, static_cast<cuda::std::size_t>(num_items)};\n  cuda::std::span<char> dst_buff{buffer[0]};\n  const int num_items_first_copy = cuda::std::min(num_items, max_num_items_first_copy);\n\n  cuda::std::span<input_t> dst = block_load2sh.CopyAsync(dst_buff, src.first(num_items_first_copy));\n  if constexpr (Mode == test_mode::single_copy || Mode == test_mode::multi_copy_multi_phase\n                || Mode == test_mode::multi_copy_multi_barrier)\n  {\n    block_load2sh.CommitAndWait();\n\n    for (int idx = threadIdx.x; idx < num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[idx] = dst[idx];\n    }\n  }\n  else if constexpr (Mode == test_mode::multi_copy_single_phase)\n  {\n    cuda::std::span<char> dst_buff2{buffer[1]};\n    cuda::std::span<input_t> dst2 = block_load2sh.CopyAsync(dst_buff2, src.subspan(num_items_first_copy));\n\n    block_load2sh.CommitAndWait();\n\n    for (int idx = threadIdx.x; idx < num_items; idx += ThreadsInBlock)\n    {\n      output[idx] = idx < num_items_first_copy ? dst[idx] : dst2[idx - num_items_first_copy];\n    }\n  }\n\n  if constexpr (Mode == test_mode::multi_copy_multi_phase)\n  {\n    // Make sure that everyone is done reading from dst\n    __syncthreads();\n\n    dst = block_load2sh.CopyAsync(dst_buff, src.subspan(num_items_first_copy));\n    block_load2sh.CommitAndWait();\n\n    for (int idx = static_cast<int>(threadIdx.x); idx < num_items - num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[num_items_first_copy + idx] = dst[idx];\n    }\n  }\n  else if constexpr (Mode == test_mode::multi_copy_multi_barrier)\n  {\n    block_load2sh.Invalidate();\n\n    // Reuse TempStorage\n    block_load2sh_t second_block_load2sh{storage};\n\n    cuda::std::span<input_t> dst = second_block_load2sh.CopyAsync(dst_buff, src.subspan(num_items_first_copy));\n\n    second_block_load2sh.CommitAndWait();\n\n    for (int idx = static_cast<int>(threadIdx.x); idx < num_items - num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[num_items_first_copy + idx] = dst[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, test_mode Mode = test_mode::single_copy, typename T, typename InputPointerT>\nvoid test_block_load(const c2h::device_vector<T>& d_input, InputPointerT input)\n{\n  using block_load2sh_t                  = cub::detail::BlockLoadToShared<ThreadsInBlock>;\n  using storage_t                        = typename block_load2sh_t::TempStorage;\n  constexpr int tile_size                = ItemsPerThread * ThreadsInBlock;\n  constexpr int num_copies               = Mode == test_mode::single_copy ? 1 : 2;\n  constexpr int num_buffers              = (Mode == test_mode::multi_copy_single_phase) ? 2 : 1;\n  constexpr int max_num_items_first_copy = cuda::ceil_div(tile_size, num_copies);\n  constexpr int buffer_align             = cub::detail::LoadToSharedBufferAlignBytes<T>();\n  constexpr int buffer_size              = cub::detail::LoadToSharedBufferSizeBytes<T>(max_num_items_first_copy);\n  constexpr int total_smem = ::cuda::round_up(int{sizeof(storage_t)}, buffer_align) + num_buffers * buffer_size;\n  constexpr bool sufficient_resources = total_smem <= cub::detail::max_smem_per_block;\n  if constexpr (sufficient_resources)\n  {\n    CAPTURE(ThreadsInBlock, ItemsPerThread, d_input.size(), sufficient_resources, c2h::type_name<T>(), Mode);\n\n    c2h::device_vector<T> d_output(d_input.size());\n    kernel<ItemsPerThread, ThreadsInBlock, Mode>\n      <<<1, ThreadsInBlock>>>(input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n    REQUIRE(cudaSuccess == cudaPeekAtLastError());\n    REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n    REQUIRE(d_input == d_output);\n  }\n}\n\ntemplate <int ThreadsInBlockX, int ThreadsInBlockY, int ThreadsInBlockZ, typename InputPointerT, typename OutputIteratorT>\n__global__ void kernel_dyn_smem_dst(InputPointerT input, OutputIteratorT output, int num_items)\n{\n  using input_t         = cub::detail::it_value_t<InputPointerT>;\n  using block_load2sh_t = cub::detail::BlockLoadToShared<ThreadsInBlockX, ThreadsInBlockY, ThreadsInBlockZ>;\n  using storage_t       = typename block_load2sh_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load2sh_t block_load2sh{storage};\n\n  static_assert(alignof(input_t) <= cub::detail::LoadToSharedBufferAlignBytes<char>());\n  extern __shared__ char smem_buff[];\n  assert(cuda::is_aligned(smem_buff, cub::detail::LoadToSharedBufferAlignBytes<input_t>()));\n\n  constexpr int ThreadsInBlock = ThreadsInBlockX * ThreadsInBlockY * ThreadsInBlockZ;\n\n  cuda::std::span<input_t> src{input, static_cast<cuda::std::size_t>(num_items)};\n  cuda::std::span<char> dst_buff{smem_buff, cuda::std::size_t{cuda::ptx::get_sreg_dynamic_smem_size()}};\n\n  cuda::std::span<input_t> dst = block_load2sh.CopyAsync(dst_buff, src.first(num_items));\n\n  // also test separate Commit and Wait calls with token passing here\n  auto token = block_load2sh.Commit();\n  block_load2sh.Wait(::cuda::std::move(token));\n\n  for (int idx = cub::RowMajorTid(ThreadsInBlockX, ThreadsInBlockY, ThreadsInBlockZ); idx < num_items;\n       idx += ThreadsInBlock)\n  {\n    output[idx] = dst[idx];\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, typename T, typename InputPointerT>\nvoid test_block_load_dyn_smem_dst(const c2h::device_vector<T>& d_input, InputPointerT input)\n{\n  constexpr int block_dim_x = ThreadsInBlock / 4;\n  constexpr int block_dim_y = 2;\n  constexpr int block_dim_z = 2;\n  using block_load2sh_t     = cub::detail::BlockLoadToShared<block_dim_x, block_dim_y, block_dim_z>;\n  constexpr int tile_size   = ItemsPerThread * ThreadsInBlock;\n  constexpr int buffer_size = cub::detail::LoadToSharedBufferSizeBytes<T>(tile_size);\n  CAPTURE(ThreadsInBlock, ItemsPerThread, c2h::type_name<T>());\n\n  c2h::device_vector<T> d_output(d_input.size());\n  kernel_dyn_smem_dst<block_dim_x, block_dim_y, block_dim_z>\n    <<<1, dim3{block_dim_x, block_dim_y, block_dim_z}, buffer_size>>>(\n      input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(d_input == d_output);\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<cuda::std::uint8_t, cuda::std::int32_t, cuda::std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing threads_in_block = c2h::enum_type_list<int, 32, 64, 96, 128>;\nusing a_block_size     = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\n\nusing modes =\n  c2h::enum_type_list<test_mode,\n                      test_mode::single_copy,\n                      test_mode::multi_copy_single_phase,\n                      test_mode::multi_copy_multi_phase,\n                      test_mode::multi_copy_multi_barrier>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block load to shared works\", \"[load][block]\", types, items_per_thread, threads_in_block, modes)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  constexpr test_mode mode = c2h::get<3, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  test_block_load<params::items_per_thread, params::threads_in_block, mode>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with even vector types\", \"[load][block]\", vec_types, items_per_thread, a_block_size)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with custom types\", \"[load][block]\", items_per_thread)\n{\n  using type                     = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<items_per_thread, threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with dyn smem and internal TempStorage\", \"[load][block]\", items_per_thread)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load_dyn_smem_dst<items_per_thread, threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\n#if IPT == 1\nC2H_TEST(\"Block load to shared works with const and non-const datatype and different alignment cases\",\n         \"[load][block]\",\n         c2h::type_list<const int*, int*>)\n{\n  using type           = int;\n  using input_ptr_type = c2h::get<0, TestType>;\n\n  const int offset_for_elements  = GENERATE_COPY(0, 1, 2, 3, 4);\n  constexpr int items_per_thread = 4;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input_ref(tile_size);\n  c2h::gen(C2H_SEED(10), d_input_ref);\n\n  c2h::device_vector<type> d_input(tile_size + offset_for_elements);\n  thrust::copy_n(d_input_ref.begin(), tile_size, d_input.begin() + offset_for_elements);\n\n  test_block_load<items_per_thread, threads_in_block, test_mode::single_copy, type, input_ptr_type>(\n    d_input_ref, thrust::raw_pointer_cast(d_input.data()) + offset_for_elements);\n}\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_load_to_shared.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/util_type.cuh>\n\n#include <cuda/cmath>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n\nenum struct test_mode\n{\n  single_copy,\n  multi_copy_single_phase,\n  multi_copy_multi_phase,\n  multi_copy_multi_barrier,\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, test_mode Mode, typename InputPointerT, typename OutputIteratorT>\n__global__ void kernel(InputPointerT input, OutputIteratorT output, int num_items)\n{\n  using input_t         = hipcub::detail::it_value_t<InputPointerT>;\n  using block_load2sh_t = hipcub::detail::BlockLoadToShared<ThreadsInBlock>;\n  using storage_t       = typename block_load2sh_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load2sh_t block_load2sh{storage};\n\n  constexpr int tile_size                = ItemsPerThread * ThreadsInBlock;\n  constexpr int num_copies               = Mode == test_mode::single_copy ? 1 : 2;\n  constexpr int num_buffers              = (Mode == test_mode::multi_copy_single_phase) ? 2 : 1;\n  constexpr int max_num_items_first_copy = cuda::ceil_div(tile_size, num_copies);\n  constexpr int buffer_align             = hipcub::detail::LoadToSharedBufferAlignBytes<input_t>();\n  constexpr int buffer_size              = hipcub::detail::LoadToSharedBufferSizeBytes<input_t>(max_num_items_first_copy);\n  alignas(buffer_align) __shared__ char buffer[num_buffers][buffer_size];\n  cuda::std::span<const input_t> src{input, static_cast<cuda::std::size_t>(num_items)};\n  cuda::std::span<char> dst_buff{buffer[0]};\n  const int num_items_first_copy = cuda::std::min(num_items, max_num_items_first_copy);\n\n  cuda::std::span<input_t> dst = block_load2sh.CopyAsync(dst_buff, src.first(num_items_first_copy));\n  if constexpr (Mode == test_mode::single_copy || Mode == test_mode::multi_copy_multi_phase\n                || Mode == test_mode::multi_copy_multi_barrier)\n  {\n    block_load2sh.CommitAndWait();\n\n    for (int idx = threadIdx.x; idx < num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[idx] = dst[idx];\n    }\n  }\n  else if constexpr (Mode == test_mode::multi_copy_single_phase)\n  {\n    cuda::std::span<char> dst_buff2{buffer[1]};\n    cuda::std::span<input_t> dst2 = block_load2sh.CopyAsync(dst_buff2, src.subspan(num_items_first_copy));\n\n    block_load2sh.CommitAndWait();\n\n    for (int idx = threadIdx.x; idx < num_items; idx += ThreadsInBlock)\n    {\n      output[idx] = idx < num_items_first_copy ? dst[idx] : dst2[idx - num_items_first_copy];\n    }\n  }\n\n  if constexpr (Mode == test_mode::multi_copy_multi_phase)\n  {\n    // Make sure that everyone is done reading from dst\n    __syncthreads();\n\n    dst = block_load2sh.CopyAsync(dst_buff, src.subspan(num_items_first_copy));\n    block_load2sh.CommitAndWait();\n\n    for (int idx = static_cast<int>(threadIdx.x); idx < num_items - num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[num_items_first_copy + idx] = dst[idx];\n    }\n  }\n  else if constexpr (Mode == test_mode::multi_copy_multi_barrier)\n  {\n    block_load2sh.Invalidate();\n\n    // Reuse TempStorage\n    block_load2sh_t second_block_load2sh{storage};\n\n    cuda::std::span<input_t> dst = second_block_load2sh.CopyAsync(dst_buff, src.subspan(num_items_first_copy));\n\n    second_block_load2sh.CommitAndWait();\n\n    for (int idx = static_cast<int>(threadIdx.x); idx < num_items - num_items_first_copy; idx += ThreadsInBlock)\n    {\n      output[num_items_first_copy + idx] = dst[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, test_mode Mode = test_mode::single_copy, typename T, typename InputPointerT>\nvoid test_block_load(const c2h::device_vector<T>& d_input, InputPointerT input)\n{\n  using block_load2sh_t                  = hipcub::detail::BlockLoadToShared<ThreadsInBlock>;\n  using storage_t                        = typename block_load2sh_t::TempStorage;\n  constexpr int tile_size                = ItemsPerThread * ThreadsInBlock;\n  constexpr int num_copies               = Mode == test_mode::single_copy ? 1 : 2;\n  constexpr int num_buffers              = (Mode == test_mode::multi_copy_single_phase) ? 2 : 1;\n  constexpr int max_num_items_first_copy = cuda::ceil_div(tile_size, num_copies);\n  constexpr int buffer_align             = hipcub::detail::LoadToSharedBufferAlignBytes<T>();\n  constexpr int buffer_size              = hipcub::detail::LoadToSharedBufferSizeBytes<T>(max_num_items_first_copy);\n  constexpr int total_smem = ::cuda::round_up(int{sizeof(storage_t)}, buffer_align) + num_buffers * buffer_size;\n  constexpr bool sufficient_resources = total_smem <= hipcub::detail::max_smem_per_block;\n  if constexpr (sufficient_resources)\n  {\n    CAPTURE(ThreadsInBlock, ItemsPerThread, d_input.size(), sufficient_resources, c2h::type_name<T>(), Mode);\n\n    c2h::device_vector<T> d_output(d_input.size());\n    kernel<ItemsPerThread, ThreadsInBlock, Mode>\n      <<<1, ThreadsInBlock>>>(input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n    REQUIRE(hipSuccess == hipPeekAtLastError());\n    REQUIRE(hipSuccess == hipDeviceSynchronize());\n    REQUIRE(d_input == d_output);\n  }\n}\n\ntemplate <int ThreadsInBlockX, int ThreadsInBlockY, int ThreadsInBlockZ, typename InputPointerT, typename OutputIteratorT>\n__global__ void kernel_dyn_smem_dst(InputPointerT input, OutputIteratorT output, int num_items)\n{\n  using input_t         = hipcub::detail::it_value_t<InputPointerT>;\n  using block_load2sh_t = hipcub::detail::BlockLoadToShared<ThreadsInBlockX, ThreadsInBlockY, ThreadsInBlockZ>;\n  using storage_t       = typename block_load2sh_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load2sh_t block_load2sh{storage};\n\n  static_assert(alignof(input_t) <= hipcub::detail::LoadToSharedBufferAlignBytes<char>());\n  extern __shared__ char smem_buff[];\n  assert(cuda::is_aligned(smem_buff, hipcub::detail::LoadToSharedBufferAlignBytes<input_t>()));\n\n  constexpr int ThreadsInBlock = ThreadsInBlockX * ThreadsInBlockY * ThreadsInBlockZ;\n\n  cuda::std::span<input_t> src{input, static_cast<cuda::std::size_t>(num_items)};\n  cuda::std::span<char> dst_buff{smem_buff, cuda::std::size_t{cuda::ptx::get_sreg_dynamic_smem_size()}};\n\n  cuda::std::span<input_t> dst = block_load2sh.CopyAsync(dst_buff, src.first(num_items));\n\n  // also test separate Commit and Wait calls with token passing here\n  auto token = block_load2sh.Commit();\n  block_load2sh.Wait(::cuda::std::move(token));\n\n  for (int idx = hipcub::RowMajorTid(ThreadsInBlockX, ThreadsInBlockY, ThreadsInBlockZ); idx < num_items;\n       idx += ThreadsInBlock)\n  {\n    output[idx] = dst[idx];\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, typename T, typename InputPointerT>\nvoid test_block_load_dyn_smem_dst(const c2h::device_vector<T>& d_input, InputPointerT input)\n{\n  constexpr int block_dim_x = ThreadsInBlock / 4;\n  constexpr int block_dim_y = 2;\n  constexpr int block_dim_z = 2;\n  using block_load2sh_t     = hipcub::detail::BlockLoadToShared<block_dim_x, block_dim_y, block_dim_z>;\n  constexpr int tile_size   = ItemsPerThread * ThreadsInBlock;\n  constexpr int buffer_size = hipcub::detail::LoadToSharedBufferSizeBytes<T>(tile_size);\n  CAPTURE(ThreadsInBlock, ItemsPerThread, c2h::type_name<T>());\n\n  c2h::device_vector<T> d_output(d_input.size());\n  kernel_dyn_smem_dst<block_dim_x, block_dim_y, block_dim_z>\n    <<<1, dim3{block_dim_x, block_dim_y, block_dim_z}, buffer_size>>>(\n      input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(d_input == d_output);\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<cuda::std::uint8_t, cuda::std::int32_t, cuda::std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing threads_in_block = c2h::enum_type_list<int, 32, 64, 96, 128>;\nusing a_block_size     = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\n\nusing modes =\n  c2h::enum_type_list<test_mode,\n                      test_mode::single_copy,\n                      test_mode::multi_copy_single_phase,\n                      test_mode::multi_copy_multi_phase,\n                      test_mode::multi_copy_multi_barrier>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n};\n\nC2H_TEST(\"Block load to shared works\", \"[load][block]\", types, items_per_thread, threads_in_block, modes)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  constexpr test_mode mode = c2h::get<3, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  test_block_load<params::items_per_thread, params::threads_in_block, mode>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with even vector types\", \"[load][block]\", vec_types, items_per_thread, a_block_size)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with custom types\", \"[load][block]\", items_per_thread)\n{\n  using type                     = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<items_per_thread, threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load to shared works with dyn smem and internal TempStorage\", \"[load][block]\", items_per_thread)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load_dyn_smem_dst<items_per_thread, threads_in_block>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\n#if IPT == 1\nC2H_TEST(\"Block load to shared works with const and non-const datatype and different alignment cases\",\n         \"[load][block]\",\n         c2h::type_list<const int*, int*>)\n{\n  using type           = int;\n  using input_ptr_type = c2h::get<0, TestType>;\n\n  const int offset_for_elements  = GENERATE_COPY(0, 1, 2, 3, 4);\n  constexpr int items_per_thread = 4;\n  constexpr int threads_in_block = 64;\n  constexpr int tile_size        = items_per_thread * threads_in_block;\n\n  c2h::device_vector<type> d_input_ref(tile_size);\n  c2h::gen(C2H_SEED(10), d_input_ref);\n\n  c2h::device_vector<type> d_input(tile_size + offset_for_elements);\n  thrust::copy_n(d_input_ref.begin(), tile_size, d_input.begin() + offset_for_elements);\n\n  test_block_load<items_per_thread, threads_in_block, test_mode::single_copy, type, input_ptr_type>(\n    d_input_ref, thrust::raw_pointer_cast(d_input.data()) + offset_for_elements);\n}\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_load_to_shared.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#undef NDEBUG\n#include <algorithm>\n#include <cassert>\n#include <type_traits>\n#include <utility>\n\n#include \"catch2_test_block_radix_sort.cuh\"\n#include \"cub/block/radix_rank_sort_operations.cuh\"\n\n// example-begin custom-type\nstruct custom_t\n{\n  float f;\n  int unused;\n  long long int lli;\n\n  custom_t() = default;\n  __device__ custom_t(float f, long long int lli)\n      : f(f)\n      , unused(42)\n      , lli(lli)\n  {}\n};\n\nstatic __device__ bool operator==(const custom_t& lhs, const custom_t& rhs)\n{\n  return lhs.f == rhs.f && lhs.lli == rhs.lli;\n}\n\nstruct decomposer_t\n{\n  __device__ cuda::std::tuple<float&, long long int&> //\n  operator()(custom_t& key) const\n  {\n    return {key.f, key.lli};\n  }\n};\n// example-end custom-type\n\n__global__ void sort_keys()\n{\n  // example-begin keys\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2} //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5} //\n     }};\n  // example-end keys\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_bits()\n{\n  // example-begin keys-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n  // example-end keys-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_descending()\n{\n  // example-begin keys-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5} //\n     }};\n  // example-end keys-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_bits()\n{\n  // example-begin keys-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 expected keys\n     }};\n  // example-end keys-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs()\n{\n  // example-begin pairs\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3}, // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2} //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2}, // thread 0 expected values\n     {3, 4, 5}}; // thread 1 expected values\n  // example-end pairs\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_bits()\n{\n  // example-begin pairs-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1}, // thread 0 values\n     {0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_keys[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0}, // thread 0 values\n     {1}}; // thread 1 values\n  // example-end pairs-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs_descending()\n{\n  // example-begin pairs-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4}, // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2}, // thread 0 expected values\n     {4, 3, 5}}; // thread 1 expected values\n  // example-end pairs-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_bits()\n{\n  // example-begin pairs-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1}, // thread 0 values\n     {0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0}, // thread 0 expected values\n     {1}}; // thread 1 expected values\n  // example-end pairs-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_blocked_to_striped()\n{\n  // example-begin keys-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4} //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5} //\n     }};\n  // example-end keys-striped\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{// thread 0 expected keys\n      {24.2, 1ll << 59},\n      {42.4, 1ll << 61}},\n     {// thread 1 expected keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 62}}};\n  // example-end keys-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_blocked_to_striped()\n{\n  // example-begin pairs-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3}, // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4} //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 4}, // thread 0 values\n     {1, 3, 5}}; // thread 1 values\n  // example-end pairs-striped\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  int thread_values[2][2] = //\n    {{3, 2}, // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{// thread 0 expected keys\n      {24.2, 1ll << 59},\n      {42.4, 1ll << 61}},\n     {// thread 1 expected keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 62}}};\n\n  int expected_values[2][2] = //\n    {{0, 2}, // thread 0 values\n     {1, 3}}; // thread 1 values\n  // example-end pairs-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped()\n{\n  // example-begin keys-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3} //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5} //\n     }};\n  // example-end keys-striped-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{\n       // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60} //\n     },\n     {\n       // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59} //\n     }};\n  // example-end keys-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped()\n{\n  // example-begin pairs-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4}, // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3} //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 3}, // thread 0 values\n     {1, 4, 5}}; // thread 1 values\n  // example-end pairs-striped-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = cub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  int thread_values[2][2] = //\n    {{3, 2}, // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(\n      thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{\n       // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60} //\n     },\n     {\n       // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59} //\n     }};\n\n  int expected_values[2][2] = //\n    {{3, 1}, // thread 0 values\n     {2, 0}}; // thread 1 values\n  // example-end pairs-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\nTEST_CASE(\"Block radix sort works in some corner cases\", \"[radix][sort][block]\")\n{\n  sort_keys<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(cudaSuccess == cudaGetLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#undef NDEBUG\n#include <algorithm>\n#include <cassert>\n#include <type_traits>\n#include <utility>\n\n#include \"catch2_test_block_radix_sort.cuh\"\n#include \"cub/block/radix_rank_sort_operations.cuh\"\n\n// example-begin custom-type\nstruct custom_t\n{\n  float f;\n  int unused;\n  long long int lli;\n\n  custom_t() = default;\n  __device__ custom_t(float f, long long int lli)\n      : f(f)\n      , unused(42)\n      , lli(lli)\n  {}\n};\n\nstatic __device__ bool operator==(const custom_t& lhs, const custom_t& rhs)\n{\n  return lhs.f == rhs.f && lhs.lli == rhs.lli;\n}\n\nstruct decomposer_t\n{\n  __device__ cuda::std::tuple<float&, long long int&> //\n  operator()(custom_t& key) const\n  {\n    return {key.f, key.lli};\n  }\n};\n// example-end custom-type\n\n__global__ void sort_keys()\n{\n  // example-begin keys\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2} //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5} //\n     }};\n  // example-end keys\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_bits()\n{\n  // example-begin keys-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n  // example-end keys-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_descending()\n{\n  // example-begin keys-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5} //\n     }};\n  // example-end keys-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_bits()\n{\n  // example-begin keys-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 1 key each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 expected keys\n     }};\n  // example-end keys-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs()\n{\n  // example-begin pairs\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3}, // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {+0.0, 1}, //\n       {-0.0, 2} //\n     },\n     {\n       // thread 1 expected keys\n       {+1.1, 3}, //\n       {+2.5, 4}, //\n       {+3.7, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2}, // thread 0 expected values\n     {3, 4, 5}}; // thread 1 expected values\n  // example-end pairs\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_bits()\n{\n  // example-begin pairs-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {24.2, 1ll << 61} // thread 0 keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1}, // thread 0 values\n     {0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 00100000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00010000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .Sort(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_keys[2][3] = //\n    {{\n       {42.4, 1ll << 60}, // thread 0 expected keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0}, // thread 0 values\n     {1}}; // thread 1 values\n  // example-end pairs-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_pairs_descending()\n{\n  // example-begin pairs-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4}, // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_keys[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+2.5, 1}, //\n       {+1.1, 2}, //\n     },\n     {\n       // thread 1 expected keys\n       {-0.0, 4}, //\n       {+0.0, 3}, //\n       {-2.5, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 1, 2}, // thread 0 expected values\n     {4, 3, 5}}; // thread 1 expected values\n  // example-end pairs-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_keys[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_keys[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_keys[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_bits()\n{\n  // example-begin pairs-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 1, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][1] = //\n    {{\n       {42.4, 1ll << 60} // thread 0 keys\n     },\n     {\n       {24.2, 1ll << 61} // thread 1 keys\n     }};\n\n  int thread_values[2][1] = //\n    {{1}, // thread 0 values\n     {0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000010001010011001100110011010 00010000000000...0000\n  // decompose(in[1]) = 01000001110000011001100110011010 00100000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescending(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{\n       {24.2, 1ll << 61}, // thread 0 expected keys\n     },\n     {\n       {42.4, 1ll << 60} // thread 1 expected keys\n     }};\n\n  int expected_values[2][1] = //\n    {{0}, // thread 0 expected values\n     {1}}; // thread 1 expected values\n  // example-end pairs-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n}\n\n__global__ void sort_keys_blocked_to_striped()\n{\n  // example-begin keys-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4} //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5} //\n     }};\n  // example-end keys-striped\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{// thread 0 expected keys\n      {24.2, 1ll << 59},\n      {42.4, 1ll << 61}},\n     {// thread 1 expected keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 62}}};\n  // example-end keys-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_blocked_to_striped()\n{\n  // example-begin pairs-striped\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+2.5, 4}, //\n       {-2.5, 0}, //\n       {+1.1, 3}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 1}, //\n       {-0.0, 2}, //\n       {+3.7, 5} //\n     }};\n\n  int thread_values[2][3] = //\n    {{4, 0, 3}, // thread 0 values\n     {1, 2, 5}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {-2.5, 0}, //\n       {-0.0, 2}, //\n       {+2.5, 4} //\n     },\n     {\n       // thread 1 expected keys\n       {+0.0, 1}, //\n       {+1.1, 3}, //\n       {+3.7, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 4}, // thread 0 values\n     {1, 3, 5}}; // thread 1 values\n  // example-end pairs-striped\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  int thread_values[2][2] = //\n    {{3, 2}, // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][3] = //\n    {{// thread 0 expected keys\n      {24.2, 1ll << 59},\n      {42.4, 1ll << 61}},\n     {// thread 1 expected keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 62}}};\n\n  int expected_values[2][2] = //\n    {{0, 2}, // thread 0 values\n     {1, 3}}; // thread 1 values\n  // example-end pairs-striped-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped()\n{\n  // example-begin keys-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage).SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3} //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5} //\n     }};\n  // example-end keys-striped-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n}\n\n__global__ void sort_keys_descending_blocked_to_striped_bits()\n{\n  // example-begin keys-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{\n       // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60} //\n     },\n     {\n       // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59} //\n     }};\n  // example-end keys-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped()\n{\n  // example-begin pairs-striped-descending\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 3 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 3, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][3] = //\n    {{\n       // thread 0 keys\n       {+1.1, 2}, //\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n     },\n     {\n       // thread 1 keys\n       {+0.0, 3}, //\n       {-2.5, 5}, //\n       {+3.7, 0} //\n     }};\n\n  int thread_values[2][3] = //\n    {{2, 1, 4}, // thread 0 values\n     {3, 5, 0}}; // thread 1 values\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{});\n\n  custom_t expected_output[2][3] = //\n    {{\n       // thread 0 expected keys\n       {+3.7, 0}, //\n       {+1.1, 2}, //\n       {+0.0, 3} //\n     },\n     {\n       // thread 1 expected keys\n       {+2.5, 1}, //\n       {-0.0, 4}, //\n       {-2.5, 5} //\n     }};\n\n  int expected_values[2][3] = //\n    {{0, 2, 3}, // thread 0 values\n     {1, 4, 5}}; // thread 1 values\n  // example-end pairs-striped-descending\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n  assert(thread_keys[threadIdx.x][2] == expected_output[threadIdx.x][2]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n  assert(thread_values[threadIdx.x][2] == expected_values[threadIdx.x][2]);\n}\n\n__global__ void sort_pairs_descending_blocked_to_striped_bits()\n{\n  // example-begin pairs-striped-descending-bits\n  // Specialize `cub::BlockRadixSort` for a 1D block of 2 threads owning 2 keys and values each\n  using block_radix_sort_t = hipcub::BlockRadixSort<custom_t, 2, 2, int>;\n\n  // Allocate shared memory for `cub::BlockRadixSort`\n  __shared__ block_radix_sort_t::TempStorage temp_storage;\n\n  // Obtain a segment of consecutive items that are blocked across threads\n  custom_t thread_keys[2][2] = //\n    {{// thread 0 keys\n      {24.2, 1ll << 62},\n      {42.4, 1ll << 61}},\n     {// thread 1 keys\n      {42.4, 1ll << 60},\n      {24.2, 1ll << 59}}};\n\n  int thread_values[2][2] = //\n    {{3, 2}, // thread 0 values\n     {1, 0}}; // thread 1 values\n\n  constexpr int begin_bit = sizeof(long long int) * 8 - 4; // 60\n  constexpr int end_bit   = sizeof(long long int) * 8 + 4; // 68\n\n  // Decomposition orders the bits as follows:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = 01000001110000011001100110011010 01000000000000...0000\n  // decompose(in[1]) = 01000010001010011001100110011010 00100000000000...0000\n  // decompose(in[2]) = 01000001110000011001100110011010 00010000000000...0000\n  // decompose(in[3]) = 01000010001010011001100110011010 00001000000000...0000\n  //                    <-----------  higher bits  /  lower bits  ----------->\n  //\n  // The bit subrange [60, 68) specifies differentiating key bits:\n  //\n  //                    <------------- fp32 -----------> <------ int64 ------>\n  // decompose(in[0]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0100xxxxxxxxxx...xxxx\n  // decompose(in[1]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0010xxxxxxxxxx...xxxx\n  // decompose(in[2]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0001xxxxxxxxxx...xxxx\n  // decompose(in[3]) = xxxxxxxxxxxxxxxxxxxxxxxxxxxx1010 0000xxxxxxxxxx...xxxx\n  //                    <-----------  higher bits  /  lower bits  ----------->\n\n  // Collectively sort the keys\n  block_radix_sort_t(temp_storage)\n    .SortDescendingBlockedToStriped(\n      thread_keys[threadIdx.x], thread_values[threadIdx.x], decomposer_t{}, begin_bit, end_bit);\n\n  custom_t expected_output[2][2] = //\n    {{\n       // thread 0 expected keys\n       {24.2, 1ll << 62}, //\n       {42.4, 1ll << 60} //\n     },\n     {\n       // thread 1 expected keys\n       {42.4, 1ll << 61}, //\n       {24.2, 1ll << 59} //\n     }};\n\n  int expected_values[2][2] = //\n    {{3, 1}, // thread 0 values\n     {2, 0}}; // thread 1 values\n  // example-end pairs-striped-descending-bits\n\n  assert(thread_keys[threadIdx.x][0] == expected_output[threadIdx.x][0]);\n  assert(thread_keys[threadIdx.x][1] == expected_output[threadIdx.x][1]);\n\n  assert(thread_values[threadIdx.x][0] == expected_values[threadIdx.x][0]);\n  assert(thread_values[threadIdx.x][1] == expected_values[threadIdx.x][1]);\n}\n\nTEST_CASE(\"Block radix sort works in some corner cases\", \"[radix][sort][block]\")\n{\n  sort_keys<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_keys_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  sort_pairs_descending_blocked_to_striped_bits<<<1, 2>>>();\n  REQUIRE(hipSuccess == hipGetLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_radix_sort_custom.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_scan.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/std/numeric>\n\n#include <c2h/catch2_test_helper.h>\n\nconstexpr int num_items_per_thread = 2;\nconstexpr int block_num_threads    = 64;\n\n// example-begin inclusive-scan-array-init-value\n__global__ void InclusiveBlockScanKernel(int* output)\n{\n  // Specialize BlockScan for a 1D block of 64 threads of type int\n  using block_scan_t   = cub::BlockScan<int, 64>;\n  using temp_storage_t = block_scan_t::TempStorage;\n\n  // Allocate shared memory for BlockScan\n  __shared__ temp_storage_t temp_storage;\n\n  int initial_value = 1;\n  int thread_data[] = {\n    +1 * ((int) threadIdx.x * num_items_per_thread), // item 0\n    -1 * ((int) threadIdx.x * num_items_per_thread + 1) // item 1\n  };\n  //  input: {[0, -1], [2, -3],[4, -5], ... [126, -127]}\n\n  // Collectively compute the block-wide inclusive scan max\n  block_scan_t(temp_storage).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{});\n\n  // output: {[1, 1], [2, 2],[3, 3], ... [126, 126]}\n  // ...\n  // example-end inclusive-scan-array-init-value\n  output[threadIdx.x * 2]     = thread_data[0];\n  output[threadIdx.x * 2 + 1] = thread_data[1];\n}\n\nC2H_TEST(\"Block array-based inclusive scan works with initial value\", \"[scan][block]\")\n{\n  c2h::device_vector<int> d_out(block_num_threads * num_items_per_thread);\n\n  InclusiveBlockScanKernel<<<1, block_num_threads>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  for (size_t i = 0; i < expected.size() - 1; i += 2)\n  {\n    expected[i]     = static_cast<int>(i);\n    expected[i + 1] = static_cast<int>(i);\n  }\n\n  // When initial value = 1 for the given input the first two\n  // elements of the result are equal to 1.\n  expected[0] = 1;\n  expected[1] = 1;\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-scan-array-aggregate-init-value\n__global__ void InclusiveBlockScanKernelAggregate(int* output, int* d_block_aggregate)\n{\n  // Specialize BlockScan for a 1D block of 64 threads of type int\n  using block_scan_t   = cub::BlockScan<int, 64>;\n  using temp_storage_t = block_scan_t::TempStorage;\n\n  // Allocate shared memory for BlockScan\n  __shared__ temp_storage_t temp_storage;\n\n  int initial_value = 1;\n  int thread_data[] = {\n    +1 * ((int) threadIdx.x * num_items_per_thread), // item 0\n    -1 * ((int) threadIdx.x * num_items_per_thread + 1) // item 1\n  };\n  //  input: {[0, -1], [2, -3],[4, -5], ... [126, -127]}\n\n  // Collectively compute the block-wide inclusive scan max\n  int block_aggregate;\n  block_scan_t(temp_storage).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{}, block_aggregate);\n\n  // output: {[1, 1], [2, 2],[3, 3], ... [126, 126]}\n  // block_aggregate = 126;\n  // ...\n  // example-end inclusive-scan-array-aggregate-init-value\n\n  *d_block_aggregate          = block_aggregate;\n  output[threadIdx.x * 2]     = thread_data[0];\n  output[threadIdx.x * 2 + 1] = thread_data[1];\n}\n\nC2H_TEST(\"Block array-based inclusive scan with block aggregate works with initial value\", \"[scan][block]\")\n{\n  c2h::device_vector<int> d_out(block_num_threads * num_items_per_thread);\n\n  c2h::device_vector<int> d_block_aggregate(1);\n  InclusiveBlockScanKernelAggregate<<<1, block_num_threads>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_block_aggregate.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  for (size_t i = 0; i < expected.size() - 1; i += 2)\n  {\n    expected[i]     = static_cast<int>(i);\n    expected[i + 1] = static_cast<int>(i);\n  }\n\n  // When initial value = 1 for the given input the first two\n  // elements of the result are equal to 1.\n  expected[0] = 1;\n  expected[1] = 1;\n\n  REQUIRE(d_out == expected);\n  REQUIRE(d_block_aggregate[0] == 126);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_scan.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/std/numeric>\n\n#include <c2h/catch2_test_helper.h>\n\nconstexpr int num_items_per_thread = 2;\nconstexpr int block_num_threads    = 64;\n\n// example-begin inclusive-scan-array-init-value\n__global__ void InclusiveBlockScanKernel(int* output)\n{\n  // Specialize BlockScan for a 1D block of 64 threads of type int\n  using block_scan_t   = hipcub::BlockScan<int, 64>;\n  using temp_storage_t = block_scan_t::TempStorage;\n\n  // Allocate shared memory for BlockScan\n  __shared__ temp_storage_t temp_storage;\n\n  int initial_value = 1;\n  int thread_data[] = {\n    +1 * ((int) threadIdx.x * num_items_per_thread), // item 0\n    -1 * ((int) threadIdx.x * num_items_per_thread + 1) // item 1\n  };\n  //  input: {[0, -1], [2, -3],[4, -5], ... [126, -127]}\n\n  // Collectively compute the block-wide inclusive scan max\n  block_scan_t(temp_storage).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{});\n\n  // output: {[1, 1], [2, 2],[3, 3], ... [126, 126]}\n  // ...\n  // example-end inclusive-scan-array-init-value\n  output[threadIdx.x * 2]     = thread_data[0];\n  output[threadIdx.x * 2 + 1] = thread_data[1];\n}\n\nC2H_TEST(\"Block array-based inclusive scan works with initial value\", \"[scan][block]\")\n{\n  c2h::device_vector<int> d_out(block_num_threads * num_items_per_thread);\n\n  InclusiveBlockScanKernel<<<1, block_num_threads>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  for (size_t i = 0; i < expected.size() - 1; i += 2)\n  {\n    expected[i]     = static_cast<int>(i);\n    expected[i + 1] = static_cast<int>(i);\n  }\n\n  // When initial value = 1 for the given input the first two\n  // elements of the result are equal to 1.\n  expected[0] = 1;\n  expected[1] = 1;\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-scan-array-aggregate-init-value\n__global__ void InclusiveBlockScanKernelAggregate(int* output, int* d_block_aggregate)\n{\n  // Specialize BlockScan for a 1D block of 64 threads of type int\n  using block_scan_t   = hipcub::BlockScan<int, 64>;\n  using temp_storage_t = block_scan_t::TempStorage;\n\n  // Allocate shared memory for BlockScan\n  __shared__ temp_storage_t temp_storage;\n\n  int initial_value = 1;\n  int thread_data[] = {\n    +1 * ((int) threadIdx.x * num_items_per_thread), // item 0\n    -1 * ((int) threadIdx.x * num_items_per_thread + 1) // item 1\n  };\n  //  input: {[0, -1], [2, -3],[4, -5], ... [126, -127]}\n\n  // Collectively compute the block-wide inclusive scan max\n  int block_aggregate;\n  block_scan_t(temp_storage).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{}, block_aggregate);\n\n  // output: {[1, 1], [2, 2],[3, 3], ... [126, 126]}\n  // block_aggregate = 126;\n  // ...\n  // example-end inclusive-scan-array-aggregate-init-value\n\n  *d_block_aggregate          = block_aggregate;\n  output[threadIdx.x * 2]     = thread_data[0];\n  output[threadIdx.x * 2 + 1] = thread_data[1];\n}\n\nC2H_TEST(\"Block array-based inclusive scan with block aggregate works with initial value\", \"[scan][block]\")\n{\n  c2h::device_vector<int> d_out(block_num_threads * num_items_per_thread);\n\n  c2h::device_vector<int> d_block_aggregate(1);\n  InclusiveBlockScanKernelAggregate<<<1, block_num_threads>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_block_aggregate.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  for (size_t i = 0; i < expected.size() - 1; i += 2)\n  {\n    expected[i]     = static_cast<int>(i);\n    expected[i + 1] = static_cast<int>(i);\n  }\n\n  // When initial value = 1 for the given input the first two\n  // elements of the result are equal to 1.\n  expected[0] = 1;\n  expected[1] = 1;\n\n  REQUIRE(d_out == expected);\n  REQUIRE(d_block_aggregate[0] == 126);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_scan_api.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"insert_nested_NVTX_range_guard.h\"\n\n#include <cub/device/device_for.cuh>\n#include <cub/device/device_transform.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/sequence.h>\n#include <thrust/zip_function.h>\n\n#include <cuda/iterator>\n#include <cuda/std/__functional/identity.h>\n\n#include <sstream>\n\n#include \"catch2_large_problem_helper.cuh\"\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n#include <c2h/test_util_vec.h>\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\nDECLARE_LAUNCH_WRAPPER(cub::DeviceTransform::Transform, transform_many);\nDECLARE_LAUNCH_WRAPPER(cub::DeviceTransform::TransformStableArgumentAddresses, transform_many_stable);\nDECLARE_LAUNCH_WRAPPER(cub::DeviceTransform::Generate, generate);\nDECLARE_LAUNCH_WRAPPER(cub::DeviceTransform::Fill, fill);\n\nusing offset_types = c2h::type_list<std::int32_t, std::int64_t>;\n\nC2H_TEST(\"DeviceTransform::Transform BabelStream add\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, uchar3>,\n         offset_types)\n{\n  using type     = c2h::get<0, TestType>;\n  using offset_t = c2h::get<1, TestType>;\n\n  // test edge cases around 16, 128, page size, and full tile\n  const offset_t num_items = GENERATE(0, 1, 15, 16, 17, 127, 128, 129, 4095, 4096, 4097, 100'000);\n  CAPTURE(c2h::type_name<type>(), c2h::type_name<offset_t>(), num_items);\n\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), result.begin(), num_items, cuda::std::plus<type>{});\n\n  // compute reference and verify\n  c2h::host_vector<type> a_h = a;\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> reference_h(num_items, thrust::no_init);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\n// note: because this uses a fancy iterator type, it will only test the fallback kernel\nC2H_TEST(\"DeviceTransform::Transform works for large number of items\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\n{\n  using offset_t = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<offset_t>());\n  const auto num_items = detail::make_large_offset<offset_t>();\n\n  auto in_it              = cuda::counting_iterator(offset_t{0});\n  auto expected_result_it = in_it;\n\n  // Prepare helper to check results\n  auto check_result_helper = detail::large_problem_test_helper(num_items);\n  auto check_result_it     = check_result_helper.get_flagging_output_iterator(expected_result_it);\n\n  transform_many(in_it, check_result_it, num_items, cuda::std::identity{});\n\n  check_result_helper.check_all_results_correct();\n}\n\nC2H_TEST(\"DeviceTransform::Transform with multiple inputs works for large number of items\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\n{\n  using offset_t = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<offset_t>());\n  const offset_t num_items = detail::make_large_offset<offset_t>();\n\n  auto a_it               = cuda::counting_iterator(offset_t{0});\n  auto b_it               = cuda::constant_iterator(offset_t{42});\n  auto expected_result_it = cuda::counting_iterator(offset_t{42});\n\n  // Prepare helper to check results\n  auto check_result_helper = detail::large_problem_test_helper(num_items);\n  auto check_result_it     = check_result_helper.get_flagging_output_iterator(expected_result_it);\n\n  transform_many(cuda::std::make_tuple(a_it, b_it), check_result_it, num_items, cuda::std::plus<offset_t>{});\n\n  check_result_helper.check_all_results_correct();\n}\n\nstruct times_seven\n{\n  template <typename T>\n  __host__ __device__ auto operator()(T v) const -> T\n  {\n    return static_cast<T>(v * 7);\n  }\n};\n\n// Exercises the 32-bit byte-offset overflow regime in transform_kernel_ublkcp (NVIDIA/cccl#8800).\n// num_items = 2^30 +/- a few thread blocks: combined with sizeof(type) = 4, byte product\n// straddles 4 GiB (negative delta -> just under, positive -> just over). Both deltas fit in I32\n// and I64 offset types.\nC2H_TEST(\"DeviceTransform::Transform works with large input\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\ntry\n{\n  using type     = std::uint32_t;\n  using offset_t = c2h::get<0, TestType>;\n\n  const auto delta         = GENERATE(-123456, 123456);\n  const offset_t num_items = static_cast<offset_t>((offset_t{1} << 30) + delta);\n  CAPTURE(c2h::type_name<offset_t>(), num_items);\n\n  c2h::device_vector<type> input(static_cast<size_t>(num_items), thrust::no_init);\n  c2h::gen(C2H_SEED(1), input);\n\n  c2h::device_vector<type> result(static_cast<size_t>(num_items), thrust::no_init);\n  transform_many(cuda::std::make_tuple(input.begin()), result.begin(), num_items, times_seven{});\n\n  // compute reference and verify\n  c2h::host_vector<type> input_h = input;\n  c2h::host_vector<type> reference_h(static_cast<size_t>(num_items), thrust::no_init);\n  std::transform(input_h.begin(), input_h.end(), reference_h.begin(), times_seven{});\n  REQUIRE((reference_h == result));\n}\ncatch (const std::bad_alloc&)\n{\n  // allocation failure is not a test failure, so we can run tests on smaller GPUs\n}\n\ntemplate <int Alignment>\nstruct overaligned_addable_and_equal_comparable_policy\n{\n  template <typename CustomType>\n  struct alignas(Alignment) type\n  {\n    __host__ __device__ static void check(const CustomType& obj)\n    {\n      _CCCL_VERIFY(reinterpret_cast<uintptr_t>(&obj) % Alignment == 0,\n                   \"overaligned_addable_policy_t<Alignment> is not sufficiently aligned\");\n    }\n\n    __host__ __device__ friend auto operator==(const CustomType& a, const CustomType& b) -> bool\n    {\n      check(a);\n      check(b);\n      return a.key == b.key;\n    }\n\n    __host__ __device__ friend auto operator+(char u, const CustomType& b) -> CustomType\n    {\n      check(b);\n      CustomType result{};\n      result.key = static_cast<size_t>(u) + b.key;\n      result.val = b.val;\n      return result;\n    }\n  };\n};\n\ntemplate <int Alignment>\nusing overaligned_t = c2h::custom_type_t<overaligned_addable_and_equal_comparable_policy<Alignment>::template type>;\n\nusing huge_t = c2h::custom_type_t<c2h::equal_comparable_t, c2h::accumulateable_t, c2h::huge_data<666>::type>;\nstatic_assert(alignof(huge_t) == 8, \"Need a large type with alignment < 16\");\n\nusing uncommon_types = c2h::type_list<\n  // these vector types have a non-power-of-two size, and size and alignment are different\n  char3,\n  short3,\n  int3,\n  longlong3,\n  overaligned_t<32>, // exceeds the memcpy_async (Hopper/Blackwell) and bulk copy alignments (only Blackwell)\n#if !_CCCL_COMPILER(MSVC) // error C2719: [...] formal parameter with requested alignment of 256 won't be aligned\n  overaligned_t<256>, // exceeds copy alignment on Hopper\n                      // and exhausts guaranteed shared memory on Hopper (threads_per_block = 256, req. smem = 64KiB)\n  overaligned_t<512>, // exhausts guaranteed shared memory on Blackwell (threads_per_block = 128, req. smem = 64KiB)\n#endif // !_CCCL_COMPILER(MSVC)\n  huge_t>;\n\nstruct uncommon_plus\n{\n  // vector types\n  template <typename T>\n  __host__ __device__ auto operator()(int8_t a, const T& b) const -> T\n  {\n    return T{a, 0, 0} + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const huge_t& b) const -> huge_t\n  {\n    huge_t r = b;\n    r.key += static_cast<size_t>(a);\n    return r;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<32>& b) const -> overaligned_t<32>\n  {\n    return a + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<256>& b) const -> overaligned_t<256>\n  {\n    return a + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<512>& b) const -> overaligned_t<512>\n  {\n    return a + b;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform uncommon types\", \"[device][transform]\", uncommon_types)\n{\n  using type = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<type>());\n\n  const int num_items = GENERATE(0, 1, 100, 1'000, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int8_t> a(num_items, thrust::default_init); // put some bytes at the front, so SMEM has to handle\n                                                                 // padding between tiles to align them\n  c2h::device_vector<type> b(num_items, thrust::default_init);\n  c2h::gen(C2H_SEED(1), a, int8_t{0}, int8_t{100});\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::default_init);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), result.begin(), num_items, uncommon_plus{});\n\n  c2h::host_vector<int8_t> a_h = a;\n  c2h::host_vector<type> b_h   = b;\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), uncommon_plus{});\n  REQUIRE(c2h::host_vector<type>(result) == reference_h);\n}\n\nstruct non_default_constructible\n{\n  int data;\n\n  non_default_constructible()                                            = delete;\n  non_default_constructible(const non_default_constructible&)            = default;\n  non_default_constructible& operator=(const non_default_constructible&) = default;\n  ~non_default_constructible()                                           = default;\n\n  __host__ __device__ explicit non_default_constructible(int data)\n      : data(data)\n  {}\n\n  friend __host__ __device__ auto operator==(non_default_constructible a, non_default_constructible b) -> bool\n  {\n    return a.data == b.data;\n  }\n};\nstatic_assert(!cuda::std::is_trivially_default_constructible_v<non_default_constructible>);\nstatic_assert(!cuda::std::is_default_constructible_v<non_default_constructible>);\nstatic_assert(cuda::std::is_trivially_copyable_v<non_default_constructible>); // as required by the standard\nstatic_assert(thrust::is_trivially_relocatable_v<non_default_constructible>); // CUB uses this check internally\n\nC2H_TEST(\"DeviceTransform::Transform non-default constructible types\", \"[device][transform]\")\n{\n  using type          = non_default_constructible;\n  const int num_items = GENERATE(0, 1, 100, 1'000, 100'000); // try to hit the small and full tile code paths\n\n  c2h::device_vector<type> input(num_items, non_default_constructible{42});\n  c2h::device_vector<type> result(num_items, non_default_constructible{0});\n\n  transform_many(cuda::std::make_tuple(input.begin()), result.begin(), num_items, cuda::std::identity{});\n\n  c2h::host_vector<type> reference_h(num_items, non_default_constructible{42});\n  REQUIRE(c2h::host_vector<type>(result) == reference_h);\n}\n\ntemplate <typename T>\nstruct nstream_kernel\n{\n  static constexpr T scalar = 42;\n\n  __host__ __device__ T operator()(const T& ai, const T& bi, const T& ci) const\n  {\n    return ai + bi + scalar * ci;\n  }\n};\n\n// overwrites one input stream\nC2H_TEST(\"DeviceTransform::Transform BabelStream nstream\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>,\n         offset_types)\n{\n  using type     = c2h::get<0, TestType>;\n  using offset_t = c2h::get<1, TestType>;\n\n  const offset_t num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::device_vector<type> c(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n  c2h::gen(C2H_SEED(1), c);\n\n  // copy to host before changing\n  c2h::host_vector<type> a_h = a;\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> c_h = c;\n\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin(), c.begin()), a.begin(), num_items, nstream_kernel<type>{});\n\n  // compute reference and verify\n  auto z = thrust::make_zip_iterator(a_h.begin(), b_h.begin(), c_h.begin());\n  std::transform(z, z + num_items, a_h.begin(), thrust::make_zip_function(nstream_kernel<type>{}));\n  REQUIRE(a_h == a);\n}\n\nstruct sum_five\n{\n  __host__ __device__ auto operator()(std::int8_t a, std::int16_t b, std::int32_t c, std::int64_t d, float e) const\n    -> double\n  {\n    return a + b + c + d + e;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform add five streams\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<std::int8_t> a(num_items, thrust::no_init);\n  c2h::device_vector<std::int16_t> b(num_items, thrust::no_init);\n  c2h::device_vector<std::int32_t> c(num_items, thrust::no_init);\n  c2h::device_vector<std::int64_t> d(num_items, thrust::no_init);\n  c2h::device_vector<float> e(num_items, thrust::no_init);\n\n  c2h::gen(C2H_SEED(1), a, std::int8_t{10}, std::int8_t{100});\n  c2h::gen(C2H_SEED(1), b, std::int16_t{10}, std::int16_t{100});\n  c2h::gen(C2H_SEED(1), c, std::int32_t{10}, std::int32_t{100});\n  c2h::gen(C2H_SEED(1), d, std::int64_t{10}, std::int64_t{100});\n  c2h::gen(C2H_SEED(1), e, float{10}, float{100});\n\n  c2h::device_vector<double> result(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(a.begin(), b.begin(), c.begin(), d.begin(), e.begin()), result.begin(), num_items, sum_five{});\n\n  // compute reference and verify\n  c2h::host_vector<std::int8_t> a_h  = a;\n  c2h::host_vector<std::int16_t> b_h = b;\n  c2h::host_vector<std::int32_t> c_h = c;\n  c2h::host_vector<std::int64_t> d_h = d;\n  c2h::host_vector<float> e_h        = e;\n  c2h::host_vector<double> reference_h(num_items, thrust::no_init);\n  auto zip = thrust::zip_iterator{a_h.begin(), b_h.begin(), c_h.begin(), d_h.begin(), e_h.begin()};\n  std::transform(zip, zip + num_items, reference_h.begin(), thrust::zip_function{sum_five{}});\n  REQUIRE(reference_h == result);\n}\n\nstruct give_me_five\n{\n  __device__ auto operator()() const -> int\n  {\n    return 5;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Generate\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int> result(num_items, thrust::no_init);\n  generate(result.begin(), num_items, give_me_five{});\n\n  // compute reference and verify\n  c2h::device_vector<int> reference(num_items, 5);\n  REQUIRE(reference == result);\n}\n\nC2H_TEST(\"DeviceTransform::Fill\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int> result(num_items, thrust::no_init);\n  fill(result.begin(), num_items, 5);\n\n  // compute reference and verify\n  c2h::device_vector<int> reference(num_items, 5);\n  REQUIRE(reference == result);\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy input iterator types\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  cuda::counting_iterator<type> a{0};\n  cuda::counting_iterator<type> b{10};\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many(cuda::std::make_tuple(a, b), result.begin(), num_items, cuda::std::plus<type>{});\n\n  // compute reference and verify\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a, a + num_items, b, reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy output iterator type\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, 13);\n  c2h::device_vector<type> b(num_items, 35);\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n\n  using thrust::placeholders::_1;\n  auto out = cuda::transform_output_iterator(result.begin(), _1 + 4);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), out, num_items, cuda::std::plus<type>{});\n  REQUIRE(result == c2h::device_vector<type>(num_items, (13 + 35) + 4));\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy output iterator type with void value type\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, 1);\n  c2h::device_vector<type> b(num_items, 2);\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n\n  using it_t = cub::CacheModifiedOutputIterator<cub::CacheStoreModifier::STORE_DEFAULT, int>;\n  static_assert(cuda::std::is_void_v<it_t::value_type>);\n  auto out = it_t{thrust::raw_pointer_cast(result.data())};\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), out, num_items, cuda::std::plus<type>{});\n  REQUIRE(result == c2h::device_vector<type>(num_items, 3));\n}\n\nstruct plus_mul_neg\n{\n  template <typename T>\n  __host__ __device__ auto operator()(T a, T b) const\n  {\n    return cuda::std::tuple{a + b, a * b, -a};\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform mixed iterator types 2 -> 3\", \"[device][transform]\")\n{\n  using type          = unsigned; // overflow is defined\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  cuda::counting_iterator<type> a{0};\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result_a(num_items, thrust::no_init);\n  c2h::device_vector<type> result_b(num_items, thrust::no_init);\n  c2h::device_vector<type> result_c(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(a, b.begin()),\n    cuda::std::make_tuple(\n      result_a.begin(), result_b.begin(), thrust::make_transform_output_iterator(result_c.begin(), cuda::std::negate{})),\n    num_items,\n    plus_mul_neg{});\n\n  // compute reference and verify\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> reference_a_h(num_items, thrust::no_init);\n  std::transform(a, a + num_items, b_h.begin(), reference_a_h.begin(), cuda::std::plus<type>{});\n  c2h::host_vector<type> reference_b_h(num_items, thrust::no_init);\n  std::transform(a, a + num_items, b_h.begin(), reference_b_h.begin(), cuda::std::multiplies<type>{});\n  CHECK(reference_a_h == result_a);\n  CHECK(reference_b_h == result_b);\n  CHECK(thrust::equal(a, a + num_items, result_c.begin()));\n}\n\nstruct plus_needs_stable_address\n{\n  int* a;\n  int* b;\n\n  __host__ __device__ int operator()(const int& v) const\n  {\n    const auto i = &v - a;\n    return v + b[i];\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform address stability\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many_stable(\n    cuda::std::make_tuple(thrust::raw_pointer_cast(a.data())),\n    result.begin(),\n    num_items,\n    plus_needs_stable_address{thrust::raw_pointer_cast(a.data()), thrust::raw_pointer_cast(b.data())});\n\n  // compute reference and verify\n  c2h::device_vector<type> a_h = a;\n  c2h::device_vector<type> b_h = b;\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\n// Non-trivially-copyable/relocatable type which cannot be copied using std::memcpy or cudaMemcpy\nstruct non_trivial\n{\n  int data;\n\n  non_trivial() = default;\n\n  __host__ __device__ explicit non_trivial(int data)\n      : data(data)\n  {}\n\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  __host__ __device__ non_trivial(const non_trivial& nt)\n      : data(nt.data)\n  {}\n\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  __host__ __device__ auto operator=(const non_trivial& nt) -> non_trivial&\n  {\n    data = nt.data;\n    return *this;\n  }\n\n  __host__ __device__ auto operator-() const -> non_trivial\n  {\n    return non_trivial{-data};\n  }\n\n  friend __host__ __device__ auto operator==(non_trivial a, non_trivial b) -> bool\n  {\n    return a.data == b.data;\n  }\n};\nstatic_assert(!cuda::std::is_trivially_copyable_v<non_trivial>); // as required by the standard\nstatic_assert(!thrust::is_trivially_relocatable_v<non_trivial>); // CUB uses this check internally\n\n// Note(bgruber): I gave up on writing a test that checks whether the copy ctor/assignment operator is actually called\n// (e.g. by tracking/counting invocations of those), since C++ allows (but not guarantees) elision of these operations.\n// Also thrust algorithms perform a lot of copies in-between, so the test needs to use only raw allocations and\n// iteration for setup and checking.\nC2H_TEST(\"DeviceTransform::Transform not trivially relocatable\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<non_trivial> input(num_items, non_trivial{42});\n  c2h::device_vector<non_trivial> result(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(thrust::raw_pointer_cast(input.data())), result.begin(), num_items, cuda::std::negate<>{});\n\n  const auto reference = c2h::device_vector<non_trivial>(num_items, non_trivial{-42});\n  REQUIRE((reference == result));\n}\n\nC2H_TEST(\"DeviceTransform::Transform buffer start alignment\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>)\n{\n  using type          = c2h::get<0, TestType>;\n  const int num_items = GENERATE(\n    10, // try to hit sub-size-alignment, small and full tile code paths\n    130,\n    100'000,\n    128 * 22 /* blackwell u8 tilesize */ + 2,\n    128 * 11 /* blackwell u16 tilesize */ + 2,\n    128 * 6 /* blackwell u32 tilesize */ + 2,\n    128 * 3 /* blackwell u64 tilesize */ + 2);\n\n  // we should hit byte offsets up until 64 (128 is the highest bulk_copy_alignment)\n  const int offset_a = GENERATE(1, 2, 4, 8, 15, 16, 32, 64, 127);\n  const int offset_b = GENERATE(1, 2, 4, 8, 15, 16, 32, 64, 127);\n  if (num_items <= offset_a || num_items <= offset_b || offset_a * sizeof(type) > 64 || offset_b * sizeof(type) > 64)\n  {\n    return;\n  }\n  const int offset_r = offset_a;\n  CAPTURE(c2h::type_name<type>(), num_items, offset_a, offset_b);\n\n  c2h::device_vector<type> a(num_items + offset_a, thrust::no_init);\n  c2h::device_vector<type> b(num_items + offset_b, thrust::no_init);\n  thrust::sequence(a.begin(), a.end());\n  thrust::sequence(b.begin(), b.end(), num_items + offset_a);\n  c2h::device_vector<type> result(num_items + offset_r);\n  transform_many(cuda::std::make_tuple(a.begin() + offset_a, b.begin() + offset_b),\n                 result.begin() + offset_r,\n                 num_items,\n                 cuda::std::plus{});\n\n  using thrust::placeholders::_1;\n  c2h::device_vector<type> reference(num_items + offset_r);\n  thrust::tabulate(reference.begin() + offset_r, reference.end(), (_1 + offset_a) * 2 + offset_b + num_items);\n  REQUIRE(reference == result);\n}\n\nnamespace Catch\n{\ntemplate <typename T>\nstruct StringMaker<cub::detail::transform::aligned_base_ptr<T>>\n{\n  static auto convert(cub::detail::transform::aligned_base_ptr<T> abp) -> std::string\n  {\n    std::stringstream ss;\n    ss << \"{ptr: \" << abp.ptr << \", head_padding: \" << abp.head_padding << \"}\";\n    return ss.str();\n  }\n};\n} // namespace Catch\n\n// TODO(bgruber): rewrite this example using int3\nC2H_TEST(\"DeviceTransform::Transform aligned_base_ptr\", \"[device][transform]\")\n{\n  alignas(128) int arr[256];\n  using namespace cub::detail::transform;\n  CHECK(make_aligned_base_ptr(&arr[0], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 0});\n  CHECK(make_aligned_base_ptr(&arr[1], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 4});\n  CHECK(make_aligned_base_ptr(&arr[5], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 20});\n  CHECK(make_aligned_base_ptr(&arr[31], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 124});\n  CHECK(make_aligned_base_ptr(&arr[32], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[32]), 0});\n  CHECK(make_aligned_base_ptr(&arr[33], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[32]), 4});\n  CHECK(make_aligned_base_ptr(&arr[127], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[96]), 124});\n  CHECK(make_aligned_base_ptr(&arr[128], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[128]), 0});\n  CHECK(make_aligned_base_ptr(&arr[129], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[128]), 4});\n}\n\nC2H_TEST(\"DeviceTransform::Transform aligned_base_ptr\", \"[device][transform]\")\n{\n  using It         = cuda::std::reverse_iterator<thrust::detail::normal_iterator<thrust::device_ptr<int>>>;\n  using kernel_arg = cub::detail::transform::kernel_arg<It>;\n\n  STATIC_REQUIRE(cuda::std::is_constructible_v<kernel_arg>);\n  STATIC_REQUIRE(cuda::std::is_copy_constructible_v<kernel_arg>);\n}\n\n// See discussion on: https://github.com/NVIDIA/cccl/pull/4815\nC2H_TEST(\"DeviceTransform::Transform vectorized output bug\", \"[device][transform]\")\n{\n  using thrust::placeholders::_1;\n\n  int num_items = std::numeric_limits<std::uint16_t>::max() - 1;\n  c2h::device_vector<std::uint16_t> input(num_items);\n  c2h::device_vector<std::uint16_t> output(num_items);\n  thrust::sequence(input.begin(), input.end());\n\n  auto out_it = cuda::transform_output_iterator(output.begin(), _1);\n  transform_many(input.begin(), out_it, num_items, _1 + 1);\n\n  c2h::host_vector<std::uint16_t> reference(num_items);\n  thrust::generate(reference.begin(), reference.end(), [i = 0]() mutable {\n    return static_cast<std::uint16_t>(++i);\n  });\n  CHECK(output == reference);\n}\n\n// See discussion on: https://github.com/NVIDIA/cccl/pull/4815\nstruct A\n{\n  int value;\n};\n\nstruct B\n{\n  int value;\n};\n\nstruct C\n{\n  int value;\n\n  __host__ __device__ friend auto operator==(C a, C b) -> bool\n  {\n    return a.value == b.value;\n  }\n\n  friend auto operator<<(std::ostream& os, C c) -> std::ostream&\n  {\n    return os << \"C{\" << c.value << \"}\";\n  }\n};\n\nstruct AtoB\n{\n  __host__ __device__ B operator()(A a) const\n  {\n    return B{a.value + 1};\n  }\n};\n\nstruct BtoC\n{\n  __host__ __device__ C operator()(B b) const\n  {\n    return C{-b.value};\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform function/output_iter return type not convertible\", \"[device][transform]\")\n{\n  using thrust::placeholders::_1;\n\n  const int num_items = 10'000;\n  c2h::device_vector<A> input(num_items, A{42});\n  c2h::device_vector<C> output(num_items, thrust::no_init);\n\n  auto out_it = cuda::transform_output_iterator(output.begin(), BtoC{});\n  transform_many(input.begin(), out_it, num_items, AtoB{});\n\n  c2h::device_vector<C> reference(num_items, C{-43});\n  CHECK(output == reference);\n}\n\n__global__ void unrelated_kernel()\n{\n  __shared__ int ssmem; // 4 bytes\n  extern __shared__ int dsmem[]; // aligned to 16 by default, so 12 bytes padding needed\n  asm(\"\" : \"+r\"(ssmem));\n  asm(\"\" : \"+r\"(dsmem[0]));\n}\n\nC2H_TEST(\"DeviceTransform::Transform does not effect unrelated kernel's static SMEM consumption\", \"[device][transform]\")\n{\n  cudaFuncAttributes attrs;\n  REQUIRE(cudaFuncGetAttributes(&attrs, unrelated_kernel) == cudaSuccess);\n  REQUIRE(attrs.sharedSizeBytes == 4 + 12);\n}\n\n#if TEST_LAUNCH == 0\n\ntemplate <int ThreadsPerBlock, int ItemsPerPthread, typename T>\n__global__ void fill_pdl_kernel(T* data, size_t n, T value)\n{\n  // we trigger the next kernel's launch very soon and wait a bit for it to spin up before starting to write. this way\n  // we try to expose the next kernel reading uninitialized data, if it contains a bug.\n  _CCCL_PDL_GRID_DEPENDENCY_SYNC();\n  _CCCL_PDL_TRIGGER_NEXT_LAUNCH();\n  NV_IF_TARGET(NV_PROVIDES_SM_70, __nanosleep(100'000);); // must be enough to cover the next kernel's launch overhead\n\n  const int tile_size = ItemsPerPthread * ThreadsPerBlock;\n  const size_t offset = size_t{blockIdx.x} * tile_size;\n\n  data += offset;\n  n -= offset;\n\n  for (int j = 0; j < ItemsPerPthread; j++)\n  {\n    const int i = threadIdx.x + j * ThreadsPerBlock;\n    if (i < n)\n    {\n      data[i] = value;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid fill_pdl(T* data, size_t n, T value)\n{\n  constexpr auto threads_per_block = 256;\n  constexpr auto items_per_thread  = 4;\n  const auto blocks                = static_cast<unsigned>(::cuda::ceil_div(n, threads_per_block * items_per_thread));\n\n  THRUST_NS_QUALIFIER::cuda_cub::detail::triple_chevron(\n    blocks, threads_per_block, /* smem */ 0, /*stream*/ nullptr, /* pdl */ true)\n    .doit(fill_pdl_kernel<threads_per_block, items_per_thread, T>, data, n, value);\n}\n\nC2H_TEST(\"DeviceTransform::Transform PDL overlap check\", \"[device][transform]\")\n{\n  using type = int;\n  // need a warmup run to lazy load kernels and perform some setup, then a problem size that occupies 1/2 of all SMs\n  const int num_items = GENERATE(1, 50 * 128);\n\n  c2h::device_vector<type> data(num_items, thrust::no_init);\n  c2h::device_vector<bool> flags(num_items, thrust::no_init);\n  c2h::device_vector<type> result(1, thrust::no_init);\n\n  using thrust::placeholders::_1;\n\n  // completely async work of filling, 2x transforming and 1x reduction. we also avoid using the launch wrapper, since\n  // it would synchronize\n  fill_pdl(thrust::raw_pointer_cast(data.data()), num_items, 42);\n  cub::DeviceTransform::Transform(::cuda::std::make_tuple(data.begin()), data.begin(), num_items, cuda::std::negate{});\n  cub::DeviceTransform::Transform(::cuda::std::make_tuple(data.begin()), flags.begin(), num_items, _1 == -42);\n  thrust::reduce_into(\n    thrust::cuda::par_nosync, flags.begin(), flags.end(), result.begin(), true, ::cuda::std::logical_and{});\n  REQUIRE(result[0]); // access finally synchronize\n}\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include \"insert_nested_NVTX_range_guard.h\"\n\n#include <cub/device/device_for.cuh>\n#include <cub/device/device_transform.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n\n#include <thrust/iterator/transform_output_iterator.h>\n#include <thrust/sequence.h>\n#include <thrust/zip_function.h>\n\n#include <cuda/iterator>\n#include <cuda/std/__functional/identity.h>\n\n#include <sstream>\n\n#include \"catch2_large_problem_helper.cuh\"\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n#include <c2h/test_util_vec.h>\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\nDECLARE_LAUNCH_WRAPPER(hipcub::DeviceTransform::Transform, transform_many);\nDECLARE_LAUNCH_WRAPPER(hipcub::DeviceTransform::TransformStableArgumentAddresses, transform_many_stable);\nDECLARE_LAUNCH_WRAPPER(hipcub::DeviceTransform::Generate, generate);\nDECLARE_LAUNCH_WRAPPER(hipcub::DeviceTransform::Fill, fill);\n\nusing offset_types = c2h::type_list<std::int32_t, std::int64_t>;\n\nC2H_TEST(\"DeviceTransform::Transform BabelStream add\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, uchar3>,\n         offset_types)\n{\n  using type     = c2h::get<0, TestType>;\n  using offset_t = c2h::get<1, TestType>;\n\n  // test edge cases around 16, 128, page size, and full tile\n  const offset_t num_items = GENERATE(0, 1, 15, 16, 17, 127, 128, 129, 4095, 4096, 4097, 100'000);\n  CAPTURE(c2h::type_name<type>(), c2h::type_name<offset_t>(), num_items);\n\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), result.begin(), num_items, cuda::std::plus<type>{});\n\n  // compute reference and verify\n  c2h::host_vector<type> a_h = a;\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> reference_h(num_items, thrust::no_init);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\n// note: because this uses a fancy iterator type, it will only test the fallback kernel\nC2H_TEST(\"DeviceTransform::Transform works for large number of items\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\n{\n  using offset_t = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<offset_t>());\n  const auto num_items = detail::make_large_offset<offset_t>();\n\n  auto in_it              = cuda::counting_iterator(offset_t{0});\n  auto expected_result_it = in_it;\n\n  // Prepare helper to check results\n  auto check_result_helper = detail::large_problem_test_helper(num_items);\n  auto check_result_it     = check_result_helper.get_flagging_output_iterator(expected_result_it);\n\n  transform_many(in_it, check_result_it, num_items, cuda::std::identity{});\n\n  check_result_helper.check_all_results_correct();\n}\n\nC2H_TEST(\"DeviceTransform::Transform with multiple inputs works for large number of items\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\n{\n  using offset_t = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<offset_t>());\n  const offset_t num_items = detail::make_large_offset<offset_t>();\n\n  auto a_it               = cuda::counting_iterator(offset_t{0});\n  auto b_it               = cuda::constant_iterator(offset_t{42});\n  auto expected_result_it = cuda::counting_iterator(offset_t{42});\n\n  // Prepare helper to check results\n  auto check_result_helper = detail::large_problem_test_helper(num_items);\n  auto check_result_it     = check_result_helper.get_flagging_output_iterator(expected_result_it);\n\n  transform_many(cuda::std::make_tuple(a_it, b_it), check_result_it, num_items, cuda::std::plus<offset_t>{});\n\n  check_result_helper.check_all_results_correct();\n}\n\nstruct times_seven\n{\n  template <typename T>\n  __host__ __device__ auto operator()(T v) const -> T\n  {\n    return static_cast<T>(v * 7);\n  }\n};\n\n// Exercises the 32-bit byte-offset overflow regime in transform_kernel_ublkcp (NVIDIA/cccl#8800).\n// num_items = 2^30 +/- a few thread blocks: combined with sizeof(type) = 4, byte product\n// straddles 4 GiB (negative delta -> just under, positive -> just over). Both deltas fit in I32\n// and I64 offset types.\nC2H_TEST(\"DeviceTransform::Transform works with large input\",\n         \"[device][transform][skip-cs-initcheck][skip-cs-racecheck][skip-cs-synccheck]\",\n         offset_types)\ntry\n{\n  using type     = std::uint32_t;\n  using offset_t = c2h::get<0, TestType>;\n\n  const auto delta         = GENERATE(-123456, 123456);\n  const offset_t num_items = static_cast<offset_t>((offset_t{1} << 30) + delta);\n  CAPTURE(c2h::type_name<offset_t>(), num_items);\n\n  c2h::device_vector<type> input(static_cast<size_t>(num_items), thrust::no_init);\n  c2h::gen(C2H_SEED(1), input);\n\n  c2h::device_vector<type> result(static_cast<size_t>(num_items), thrust::no_init);\n  transform_many(cuda::std::make_tuple(input.begin()), result.begin(), num_items, times_seven{});\n\n  // compute reference and verify\n  c2h::host_vector<type> input_h = input;\n  c2h::host_vector<type> reference_h(static_cast<size_t>(num_items), thrust::no_init);\n  std::transform(input_h.begin(), input_h.end(), reference_h.begin(), times_seven{});\n  REQUIRE((reference_h == result));\n}\ncatch (const std::bad_alloc&)\n{\n  // allocation failure is not a test failure, so we can run tests on smaller GPUs\n}\n\ntemplate <int Alignment>\nstruct overaligned_addable_and_equal_comparable_policy\n{\n  template <typename CustomType>\n  struct alignas(Alignment) type\n  {\n    __host__ __device__ static void check(const CustomType& obj)\n    {\n      _CCCL_VERIFY(reinterpret_cast<uintptr_t>(&obj) % Alignment == 0,\n                   \"overaligned_addable_policy_t<Alignment> is not sufficiently aligned\");\n    }\n\n    __host__ __device__ friend auto operator==(const CustomType& a, const CustomType& b) -> bool\n    {\n      check(a);\n      check(b);\n      return a.key == b.key;\n    }\n\n    __host__ __device__ friend auto operator+(char u, const CustomType& b) -> CustomType\n    {\n      check(b);\n      CustomType result{};\n      result.key = static_cast<size_t>(u) + b.key;\n      result.val = b.val;\n      return result;\n    }\n  };\n};\n\ntemplate <int Alignment>\nusing overaligned_t = c2h::custom_type_t<overaligned_addable_and_equal_comparable_policy<Alignment>::template type>;\n\nusing huge_t = c2h::custom_type_t<c2h::equal_comparable_t, c2h::accumulateable_t, c2h::huge_data<666>::type>;\nstatic_assert(alignof(huge_t) == 8, \"Need a large type with alignment < 16\");\n\nusing uncommon_types = c2h::type_list<\n  // these vector types have a non-power-of-two size, and size and alignment are different\n  char3,\n  short3,\n  int3,\n  longlong3,\n  overaligned_t<32>, // exceeds the memcpy_async (Hopper/Blackwell) and bulk copy alignments (only Blackwell)\n#if !_CCCL_COMPILER(MSVC) // error C2719: [...] formal parameter with requested alignment of 256 won't be aligned\n  overaligned_t<256>, // exceeds copy alignment on Hopper\n                      // and exhausts guaranteed shared memory on Hopper (threads_per_block = 256, req. smem = 64KiB)\n  overaligned_t<512>, // exhausts guaranteed shared memory on Blackwell (threads_per_block = 128, req. smem = 64KiB)\n#endif // !_CCCL_COMPILER(MSVC)\n  huge_t>;\n\nstruct uncommon_plus\n{\n  // vector types\n  template <typename T>\n  __host__ __device__ auto operator()(int8_t a, const T& b) const -> T\n  {\n    return T{a, 0, 0} + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const huge_t& b) const -> huge_t\n  {\n    huge_t r = b;\n    r.key += static_cast<size_t>(a);\n    return r;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<32>& b) const -> overaligned_t<32>\n  {\n    return a + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<256>& b) const -> overaligned_t<256>\n  {\n    return a + b;\n  }\n\n  __host__ __device__ auto operator()(int8_t a, const overaligned_t<512>& b) const -> overaligned_t<512>\n  {\n    return a + b;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform uncommon types\", \"[device][transform]\", uncommon_types)\n{\n  using type = c2h::get<0, TestType>;\n  CAPTURE(c2h::type_name<type>());\n\n  const int num_items = GENERATE(0, 1, 100, 1'000, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int8_t> a(num_items, thrust::default_init); // put some bytes at the front, so SMEM has to handle\n                                                                 // padding between tiles to align them\n  c2h::device_vector<type> b(num_items, thrust::default_init);\n  c2h::gen(C2H_SEED(1), a, int8_t{0}, int8_t{100});\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::default_init);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), result.begin(), num_items, uncommon_plus{});\n\n  c2h::host_vector<int8_t> a_h = a;\n  c2h::host_vector<type> b_h   = b;\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), uncommon_plus{});\n  REQUIRE(c2h::host_vector<type>(result) == reference_h);\n}\n\nstruct non_default_constructible\n{\n  int data;\n\n  non_default_constructible()                                            = delete;\n  non_default_constructible(const non_default_constructible&)            = default;\n  non_default_constructible& operator=(const non_default_constructible&) = default;\n  ~non_default_constructible()                                           = default;\n\n  __host__ __device__ explicit non_default_constructible(int data)\n      : data(data)\n  {}\n\n  friend __host__ __device__ auto operator==(non_default_constructible a, non_default_constructible b) -> bool\n  {\n    return a.data == b.data;\n  }\n};\nstatic_assert(!cuda::std::is_trivially_default_constructible_v<non_default_constructible>);\nstatic_assert(!cuda::std::is_default_constructible_v<non_default_constructible>);\nstatic_assert(cuda::std::is_trivially_copyable_v<non_default_constructible>); // as required by the standard\nstatic_assert(thrust::is_trivially_relocatable_v<non_default_constructible>); // CUB uses this check internally\n\nC2H_TEST(\"DeviceTransform::Transform non-default constructible types\", \"[device][transform]\")\n{\n  using type          = non_default_constructible;\n  const int num_items = GENERATE(0, 1, 100, 1'000, 100'000); // try to hit the small and full tile code paths\n\n  c2h::device_vector<type> input(num_items, non_default_constructible{42});\n  c2h::device_vector<type> result(num_items, non_default_constructible{0});\n\n  transform_many(cuda::std::make_tuple(input.begin()), result.begin(), num_items, cuda::std::identity{});\n\n  c2h::host_vector<type> reference_h(num_items, non_default_constructible{42});\n  REQUIRE(c2h::host_vector<type>(result) == reference_h);\n}\n\ntemplate <typename T>\nstruct nstream_kernel\n{\n  static constexpr T scalar = 42;\n\n  __host__ __device__ T operator()(const T& ai, const T& bi, const T& ci) const\n  {\n    return ai + bi + scalar * ci;\n  }\n};\n\n// overwrites one input stream\nC2H_TEST(\"DeviceTransform::Transform BabelStream nstream\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>,\n         offset_types)\n{\n  using type     = c2h::get<0, TestType>;\n  using offset_t = c2h::get<1, TestType>;\n\n  const offset_t num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::device_vector<type> c(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n  c2h::gen(C2H_SEED(1), c);\n\n  // copy to host before changing\n  c2h::host_vector<type> a_h = a;\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> c_h = c;\n\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin(), c.begin()), a.begin(), num_items, nstream_kernel<type>{});\n\n  // compute reference and verify\n  auto z = thrust::make_zip_iterator(a_h.begin(), b_h.begin(), c_h.begin());\n  std::transform(z, z + num_items, a_h.begin(), thrust::make_zip_function(nstream_kernel<type>{}));\n  REQUIRE(a_h == a);\n}\n\nstruct sum_five\n{\n  __host__ __device__ auto operator()(std::int8_t a, std::int16_t b, std::int32_t c, std::int64_t d, float e) const\n    -> double\n  {\n    return a + b + c + d + e;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform add five streams\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<std::int8_t> a(num_items, thrust::no_init);\n  c2h::device_vector<std::int16_t> b(num_items, thrust::no_init);\n  c2h::device_vector<std::int32_t> c(num_items, thrust::no_init);\n  c2h::device_vector<std::int64_t> d(num_items, thrust::no_init);\n  c2h::device_vector<float> e(num_items, thrust::no_init);\n\n  c2h::gen(C2H_SEED(1), a, std::int8_t{10}, std::int8_t{100});\n  c2h::gen(C2H_SEED(1), b, std::int16_t{10}, std::int16_t{100});\n  c2h::gen(C2H_SEED(1), c, std::int32_t{10}, std::int32_t{100});\n  c2h::gen(C2H_SEED(1), d, std::int64_t{10}, std::int64_t{100});\n  c2h::gen(C2H_SEED(1), e, float{10}, float{100});\n\n  c2h::device_vector<double> result(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(a.begin(), b.begin(), c.begin(), d.begin(), e.begin()), result.begin(), num_items, sum_five{});\n\n  // compute reference and verify\n  c2h::host_vector<std::int8_t> a_h  = a;\n  c2h::host_vector<std::int16_t> b_h = b;\n  c2h::host_vector<std::int32_t> c_h = c;\n  c2h::host_vector<std::int64_t> d_h = d;\n  c2h::host_vector<float> e_h        = e;\n  c2h::host_vector<double> reference_h(num_items, thrust::no_init);\n  auto zip = thrust::zip_iterator{a_h.begin(), b_h.begin(), c_h.begin(), d_h.begin(), e_h.begin()};\n  std::transform(zip, zip + num_items, reference_h.begin(), thrust::zip_function{sum_five{}});\n  REQUIRE(reference_h == result);\n}\n\nstruct give_me_five\n{\n  __device__ auto operator()() const -> int\n  {\n    return 5;\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Generate\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int> result(num_items, thrust::no_init);\n  generate(result.begin(), num_items, give_me_five{});\n\n  // compute reference and verify\n  c2h::device_vector<int> reference(num_items, 5);\n  REQUIRE(reference == result);\n}\n\nC2H_TEST(\"DeviceTransform::Fill\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<int> result(num_items, thrust::no_init);\n  fill(result.begin(), num_items, 5);\n\n  // compute reference and verify\n  c2h::device_vector<int> reference(num_items, 5);\n  REQUIRE(reference == result);\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy input iterator types\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  cuda::counting_iterator<type> a{0};\n  cuda::counting_iterator<type> b{10};\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many(cuda::std::make_tuple(a, b), result.begin(), num_items, cuda::std::plus<type>{});\n\n  // compute reference and verify\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a, a + num_items, b, reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy output iterator type\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, 13);\n  c2h::device_vector<type> b(num_items, 35);\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n\n  using thrust::placeholders::_1;\n  auto out = cuda::transform_output_iterator(result.begin(), _1 + 4);\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), out, num_items, cuda::std::plus<type>{});\n  REQUIRE(result == c2h::device_vector<type>(num_items, (13 + 35) + 4));\n}\n\nC2H_TEST(\"DeviceTransform::Transform fancy output iterator type with void value type\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, 1);\n  c2h::device_vector<type> b(num_items, 2);\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n\n  using it_t = hipcub::CacheModifiedOutputIterator<hipcub::CacheStoreModifier::STORE_DEFAULT, int>;\n  static_assert(cuda::std::is_void_v<it_t::value_type>);\n  auto out = it_t{thrust::raw_pointer_cast(result.data())};\n  transform_many(cuda::std::make_tuple(a.begin(), b.begin()), out, num_items, cuda::std::plus<type>{});\n  REQUIRE(result == c2h::device_vector<type>(num_items, 3));\n}\n\nstruct plus_mul_neg\n{\n  template <typename T>\n  __host__ __device__ auto operator()(T a, T b) const\n  {\n    return cuda::std::tuple{a + b, a * b, -a};\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform mixed iterator types 2 -> 3\", \"[device][transform]\")\n{\n  using type          = unsigned; // overflow is defined\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  cuda::counting_iterator<type> a{0};\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result_a(num_items, thrust::no_init);\n  c2h::device_vector<type> result_b(num_items, thrust::no_init);\n  c2h::device_vector<type> result_c(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(a, b.begin()),\n    cuda::std::make_tuple(\n      result_a.begin(), result_b.begin(), thrust::make_transform_output_iterator(result_c.begin(), cuda::std::negate{})),\n    num_items,\n    plus_mul_neg{});\n\n  // compute reference and verify\n  c2h::host_vector<type> b_h = b;\n  c2h::host_vector<type> reference_a_h(num_items, thrust::no_init);\n  std::transform(a, a + num_items, b_h.begin(), reference_a_h.begin(), cuda::std::plus<type>{});\n  c2h::host_vector<type> reference_b_h(num_items, thrust::no_init);\n  std::transform(a, a + num_items, b_h.begin(), reference_b_h.begin(), cuda::std::multiplies<type>{});\n  CHECK(reference_a_h == result_a);\n  CHECK(reference_b_h == result_b);\n  CHECK(thrust::equal(a, a + num_items, result_c.begin()));\n}\n\nstruct plus_needs_stable_address\n{\n  int* a;\n  int* b;\n\n  __host__ __device__ int operator()(const int& v) const\n  {\n    const auto i = &v - a;\n    return v + b[i];\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform address stability\", \"[device][transform]\")\n{\n  using type          = int;\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<type> a(num_items, thrust::no_init);\n  c2h::device_vector<type> b(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(1), a);\n  c2h::gen(C2H_SEED(1), b);\n\n  c2h::device_vector<type> result(num_items, thrust::no_init);\n  transform_many_stable(\n    cuda::std::make_tuple(thrust::raw_pointer_cast(a.data())),\n    result.begin(),\n    num_items,\n    plus_needs_stable_address{thrust::raw_pointer_cast(a.data()), thrust::raw_pointer_cast(b.data())});\n\n  // compute reference and verify\n  c2h::device_vector<type> a_h = a;\n  c2h::device_vector<type> b_h = b;\n  c2h::host_vector<type> reference_h(num_items);\n  std::transform(a_h.begin(), a_h.end(), b_h.begin(), reference_h.begin(), std::plus<type>{});\n  REQUIRE(reference_h == result);\n}\n\n// Non-trivially-copyable/relocatable type which cannot be copied using std::memcpy or hipMemcpy\nstruct non_trivial\n{\n  int data;\n\n  non_trivial() = default;\n\n  __host__ __device__ explicit non_trivial(int data)\n      : data(data)\n  {}\n\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  __host__ __device__ non_trivial(const non_trivial& nt)\n      : data(nt.data)\n  {}\n\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  __host__ __device__ auto operator=(const non_trivial& nt) -> non_trivial&\n  {\n    data = nt.data;\n    return *this;\n  }\n\n  __host__ __device__ auto operator-() const -> non_trivial\n  {\n    return non_trivial{-data};\n  }\n\n  friend __host__ __device__ auto operator==(non_trivial a, non_trivial b) -> bool\n  {\n    return a.data == b.data;\n  }\n};\nstatic_assert(!cuda::std::is_trivially_copyable_v<non_trivial>); // as required by the standard\nstatic_assert(!thrust::is_trivially_relocatable_v<non_trivial>); // CUB uses this check internally\n\n// Note(bgruber): I gave up on writing a test that checks whether the copy ctor/assignment operator is actually called\n// (e.g. by tracking/counting invocations of those), since C++ allows (but not guarantees) elision of these operations.\n// Also thrust algorithms perform a lot of copies in-between, so the test needs to use only raw allocations and\n// iteration for setup and checking.\nC2H_TEST(\"DeviceTransform::Transform not trivially relocatable\", \"[device][transform]\")\n{\n  const int num_items = GENERATE(100, 100'000); // try to hit the small and full tile code paths\n  c2h::device_vector<non_trivial> input(num_items, non_trivial{42});\n  c2h::device_vector<non_trivial> result(num_items, thrust::no_init);\n  transform_many(\n    cuda::std::make_tuple(thrust::raw_pointer_cast(input.data())), result.begin(), num_items, cuda::std::negate<>{});\n\n  const auto reference = c2h::device_vector<non_trivial>(num_items, non_trivial{-42});\n  REQUIRE((reference == result));\n}\n\nC2H_TEST(\"DeviceTransform::Transform buffer start alignment\",\n         \"[device][transform]\",\n         c2h::type_list<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>)\n{\n  using type          = c2h::get<0, TestType>;\n  const int num_items = GENERATE(\n    10, // try to hit sub-size-alignment, small and full tile code paths\n    130,\n    100'000,\n    128 * 22 /* blackwell u8 tilesize */ + 2,\n    128 * 11 /* blackwell u16 tilesize */ + 2,\n    128 * 6 /* blackwell u32 tilesize */ + 2,\n    128 * 3 /* blackwell u64 tilesize */ + 2);\n\n  // we should hit byte offsets up until 64 (128 is the highest bulk_copy_alignment)\n  const int offset_a = GENERATE(1, 2, 4, 8, 15, 16, 32, 64, 127);\n  const int offset_b = GENERATE(1, 2, 4, 8, 15, 16, 32, 64, 127);\n  if (num_items <= offset_a || num_items <= offset_b || offset_a * sizeof(type) > 64 || offset_b * sizeof(type) > 64)\n  {\n    return;\n  }\n  const int offset_r = offset_a;\n  CAPTURE(c2h::type_name<type>(), num_items, offset_a, offset_b);\n\n  c2h::device_vector<type> a(num_items + offset_a, thrust::no_init);\n  c2h::device_vector<type> b(num_items + offset_b, thrust::no_init);\n  thrust::sequence(a.begin(), a.end());\n  thrust::sequence(b.begin(), b.end(), num_items + offset_a);\n  c2h::device_vector<type> result(num_items + offset_r);\n  transform_many(cuda::std::make_tuple(a.begin() + offset_a, b.begin() + offset_b),\n                 result.begin() + offset_r,\n                 num_items,\n                 cuda::std::plus{});\n\n  using thrust::placeholders::_1;\n  c2h::device_vector<type> reference(num_items + offset_r);\n  thrust::tabulate(reference.begin() + offset_r, reference.end(), (_1 + offset_a) * 2 + offset_b + num_items);\n  REQUIRE(reference == result);\n}\n\nnamespace Catch\n{\ntemplate <typename T>\nstruct StringMaker<hipcub::detail::transform::aligned_base_ptr<T>>\n{\n  static auto convert(hipcub::detail::transform::aligned_base_ptr<T> abp) -> std::string\n  {\n    std::stringstream ss;\n    ss << \"{ptr: \" << abp.ptr << \", head_padding: \" << abp.head_padding << \"}\";\n    return ss.str();\n  }\n};\n} // namespace Catch\n\n// TODO(bgruber): rewrite this example using int3\nC2H_TEST(\"DeviceTransform::Transform aligned_base_ptr\", \"[device][transform]\")\n{\n  alignas(128) int arr[256];\n  using namespace hipcub::detail::transform;\n  CHECK(make_aligned_base_ptr(&arr[0], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 0});\n  CHECK(make_aligned_base_ptr(&arr[1], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 4});\n  CHECK(make_aligned_base_ptr(&arr[5], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 20});\n  CHECK(make_aligned_base_ptr(&arr[31], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[0]), 124});\n  CHECK(make_aligned_base_ptr(&arr[32], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[32]), 0});\n  CHECK(make_aligned_base_ptr(&arr[33], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[32]), 4});\n  CHECK(make_aligned_base_ptr(&arr[127], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[96]), 124});\n  CHECK(make_aligned_base_ptr(&arr[128], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[128]), 0});\n  CHECK(make_aligned_base_ptr(&arr[129], 128) == aligned_base_ptr<int>{reinterpret_cast<char*>(&arr[128]), 4});\n}\n\nC2H_TEST(\"DeviceTransform::Transform aligned_base_ptr\", \"[device][transform]\")\n{\n  using It         = cuda::std::reverse_iterator<thrust::detail::normal_iterator<thrust::device_ptr<int>>>;\n  using kernel_arg = hipcub::detail::transform::kernel_arg<It>;\n\n  STATIC_REQUIRE(cuda::std::is_constructible_v<kernel_arg>);\n  STATIC_REQUIRE(cuda::std::is_copy_constructible_v<kernel_arg>);\n}\n\n// See discussion on: https://github.com/NVIDIA/cccl/pull/4815\nC2H_TEST(\"DeviceTransform::Transform vectorized output bug\", \"[device][transform]\")\n{\n  using thrust::placeholders::_1;\n\n  int num_items = std::numeric_limits<std::uint16_t>::max() - 1;\n  c2h::device_vector<std::uint16_t> input(num_items);\n  c2h::device_vector<std::uint16_t> output(num_items);\n  thrust::sequence(input.begin(), input.end());\n\n  auto out_it = cuda::transform_output_iterator(output.begin(), _1);\n  transform_many(input.begin(), out_it, num_items, _1 + 1);\n\n  c2h::host_vector<std::uint16_t> reference(num_items);\n  thrust::generate(reference.begin(), reference.end(), [i = 0]() mutable {\n    return static_cast<std::uint16_t>(++i);\n  });\n  CHECK(output == reference);\n}\n\n// See discussion on: https://github.com/NVIDIA/cccl/pull/4815\nstruct A\n{\n  int value;\n};\n\nstruct B\n{\n  int value;\n};\n\nstruct C\n{\n  int value;\n\n  __host__ __device__ friend auto operator==(C a, C b) -> bool\n  {\n    return a.value == b.value;\n  }\n\n  friend auto operator<<(std::ostream& os, C c) -> std::ostream&\n  {\n    return os << \"C{\" << c.value << \"}\";\n  }\n};\n\nstruct AtoB\n{\n  __host__ __device__ B operator()(A a) const\n  {\n    return B{a.value + 1};\n  }\n};\n\nstruct BtoC\n{\n  __host__ __device__ C operator()(B b) const\n  {\n    return C{-b.value};\n  }\n};\n\nC2H_TEST(\"DeviceTransform::Transform function/output_iter return type not convertible\", \"[device][transform]\")\n{\n  using thrust::placeholders::_1;\n\n  const int num_items = 10'000;\n  c2h::device_vector<A> input(num_items, A{42});\n  c2h::device_vector<C> output(num_items, thrust::no_init);\n\n  auto out_it = cuda::transform_output_iterator(output.begin(), BtoC{});\n  transform_many(input.begin(), out_it, num_items, AtoB{});\n\n  c2h::device_vector<C> reference(num_items, C{-43});\n  CHECK(output == reference);\n}\n\n__global__ void unrelated_kernel()\n{\n  __shared__ int ssmem; // 4 bytes\n  extern __shared__ int dsmem[]; // aligned to 16 by default, so 12 bytes padding needed\n  asm(\"\" : \"+r\"(ssmem));\n  asm(\"\" : \"+r\"(dsmem[0]));\n}\n\nC2H_TEST(\"DeviceTransform::Transform does not effect unrelated kernel's static SMEM consumption\", \"[device][transform]\")\n{\n  hipFuncAttributes attrs;\n  REQUIRE(hipFuncGetAttributes(&attrs,reinterpret_cast<const void*>(unrelated_kernel)) == hipSuccess);\n  REQUIRE(attrs.sharedSizeBytes == 4 + 12);\n}\n\n#if TEST_LAUNCH == 0\n\ntemplate <int ThreadsPerBlock, int ItemsPerPthread, typename T>\n__global__ void fill_pdl_kernel(T* data, size_t n, T value)\n{\n  // we trigger the next kernel's launch very soon and wait a bit for it to spin up before starting to write. this way\n  // we try to expose the next kernel reading uninitialized data, if it contains a bug.\n  _CCCL_PDL_GRID_DEPENDENCY_SYNC();\n  _CCCL_PDL_TRIGGER_NEXT_LAUNCH();\n  NV_IF_TARGET(NV_PROVIDES_SM_70, __nanosleep(100'000);); // must be enough to cover the next kernel's launch overhead\n\n  const int tile_size = ItemsPerPthread * ThreadsPerBlock;\n  const size_t offset = size_t{blockIdx.x} * tile_size;\n\n  data += offset;\n  n -= offset;\n\n  for (int j = 0; j < ItemsPerPthread; j++)\n  {\n    const int i = threadIdx.x + j * ThreadsPerBlock;\n    if (i < n)\n    {\n      data[i] = value;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid fill_pdl(T* data, size_t n, T value)\n{\n  constexpr auto threads_per_block = 256;\n  constexpr auto items_per_thread  = 4;\n  const auto blocks                = static_cast<unsigned>(::cuda::ceil_div(n, threads_per_block * items_per_thread));\n\n  THRUST_NS_QUALIFIER::cuda_cub::detail::triple_chevron(\n    blocks, threads_per_block, /* smem */ 0, /*stream*/ nullptr, /* pdl */ true)\n    .doit(fill_pdl_kernel<threads_per_block, items_per_thread, T>, data, n, value);\n}\n\nC2H_TEST(\"DeviceTransform::Transform PDL overlap check\", \"[device][transform]\")\n{\n  using type = int;\n  // need a warmup run to lazy load kernels and perform some setup, then a problem size that occupies 1/2 of all SMs\n  const int num_items = GENERATE(1, 50 * 128);\n\n  c2h::device_vector<type> data(num_items, thrust::no_init);\n  c2h::device_vector<bool> flags(num_items, thrust::no_init);\n  c2h::device_vector<type> result(1, thrust::no_init);\n\n  using thrust::placeholders::_1;\n\n  // completely async work of filling, 2x transforming and 1x reduction. we also avoid using the launch wrapper, since\n  // it would synchronize\n  fill_pdl(thrust::raw_pointer_cast(data.data()), num_items, 42);\n  hipcub::DeviceTransform::Transform(::cuda::std::make_tuple(data.begin()), data.begin(), num_items, cuda::std::negate{});\n  hipcub::DeviceTransform::Transform(::cuda::std::make_tuple(data.begin()), flags.begin(), num_items, _1 == -42);\n  thrust::reduce_into(\n    thrust::cuda::par_nosync, flags.begin(), flags.end(), result.begin(), true, ::cuda::std::logical_and{});\n  REQUIRE(result[0]); // access finally synchronize\n}\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_device_transform.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/util_macro.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/warp/warp_merge_sort.cuh>\n\n#include <thrust/iterator/zip_iterator.h>\n\n#include <cuda/iterator>\n#include <cuda/std/type_traits>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n\nstruct CustomLess\n{\n  template <typename T>\n  __device__ __host__ bool operator()(const T& lhs, const T& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting keys-only.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(T* in, T* out, SegmentSizeItT segment_sizes, T oob_default, ActionT action)\n{\n  using warp_merge_sort_t = cub::WarpMergeSort<T, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = threadIdx.x;\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  T thread_data[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncwarp(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, thread_data, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = (idx - warp_offset) >= valid_items ? oob_default : thread_data[item];\n  }\n}\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting key-value\n * pairs.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(\n  KeyT* keys_in,\n  KeyT* keys_out,\n  ValueT* values_in,\n  ValueT* values_out,\n  SegmentSizeItT segment_sizes,\n  KeyT oob_default,\n  ActionT action)\n{\n  using warp_merge_sort_t = cub::WarpMergeSort<KeyT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ValueT>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  KeyT keys[ITEMS_PER_THREAD];\n  ValueT values[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    keys[item]    = keys_in[idx];\n    values[item]  = values_in[idx];\n  }\n  __syncwarp(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, keys, values, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx   = thread_offset + item;\n    keys_out[idx]   = (idx - warp_offset) >= valid_items ? oob_default : keys[item];\n    values_out[idx] = (idx - warp_offset) >= valid_items ? ValueT{} : values[item];\n  }\n}\n\n// -----------------------------------------------------------\n// Dimensions being instantiated:\n// {full,partial} x {stable, 'unstable'} x {keys, kv-pairs}\n// -----------------------------------------------------------\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on keys-only\n */\nstruct warp_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int /*valid_items*/, T /*oob_default*/) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int valid_items, T oob_default) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on keys-only\n */\nstruct warp_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int /*valid_items*/, T /*oob_default*/) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int valid_items, T oob_default) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on key-value pairs\n */\nstruct warp_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int /*valid_items*/,\n    KeyT /*oob_default*/) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int valid_items,\n    KeyT oob_default) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on key-value pairs\n */\nstruct warp_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int /*valid_items*/,\n    KeyT /*oob_default*/) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int valid_items,\n    KeyT oob_default) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Dispatch helper function for sorting keys\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(\n  c2h::device_vector<T>& in, c2h::device_vector<T>& out, SegmentSizesItT segment_sizes, T oob_default, ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n      thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), segment_sizes, oob_default, action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Dispatch helper function for sorting key-value pairs\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(\n  c2h::device_vector<KeyT>& keys_in,\n  c2h::device_vector<KeyT>& keys_out,\n  c2h::device_vector<ValueT>& values_in,\n  c2h::device_vector<ValueT>& values_out,\n  SegmentSizesItT segment_sizes,\n  KeyT oob_default,\n  ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n      thrust::raw_pointer_cast(keys_in.data()),\n      thrust::raw_pointer_cast(keys_out.data()),\n      thrust::raw_pointer_cast(values_in.data()),\n      thrust::raw_pointer_cast(values_out.data()),\n      segment_sizes,\n      oob_default,\n      action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Performs a stable sort on per-warp segments of data and assigns oob_default to items that\n * are out-of-bounds.\n */\ntemplate <typename RandomItT, typename SegmentSizeItT, typename T>\nvoid compute_host_reference(\n  RandomItT h_data, SegmentSizeItT segment_sizes, unsigned int num_segments, T oob_default, int logical_warp_items)\n{\n  for (unsigned int segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    unsigned int segment_size = segment_sizes[segment_id];\n    std::stable_sort(h_data, h_data + segment_size);\n    std::fill(h_data + segment_size, h_data + logical_warp_items, oob_default);\n    h_data += logical_warp_items;\n  }\n}\n\n/**\n * @brief Stability requirement of the sorting algorithm\n */\nenum class stability\n{\n  stable,\n  unstable\n};\n\n// List of key types to test\nusing custom_t  = c2h::custom_type_t<c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing key_types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t, custom_t>;\n\n// List of value types\nusing value_types = c2h::type_list<std::int32_t, custom_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 4>;\n\n// Number of items per thread to test\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 4, 7>;\n\n// Whether the sort is required to be stable or not\nusing stability_list = c2h::enum_type_list<stability, stability::stable, stability::unstable>;\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int logical_warp_items   = logical_warp_threads * items_per_thread;\n  static constexpr int total_warps          = 2;\n  static constexpr int tile_size            = items_per_thread * total_warps * logical_warp_threads;\n  static constexpr bool is_stable           = c2h::get<3, TestType>::value == stability::stable;\n};\n\nC2H_TEST(\n  \"Warp sort on keys-only works\", \"[sort][warp]\", key_types, logical_warp_threads, items_per_thread_list, stability_list)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  using warp_sort_delegate = cuda::std::_If<params::is_stable, warp_stable_sort_keys_t, warp_sort_keys_t>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  auto segment_sizes     = cuda::constant_iterator(params::logical_warp_items);\n  const auto oob_default = cuda::std::numeric_limits<type>::max();\n  c2h::gen(C2H_SEED(10), d_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, segment_sizes, oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in_out = d_in;\n  compute_host_reference(h_in_out.begin(), segment_sizes, params::total_warps, oob_default, params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nC2H_TEST(\"Warp sort keys-only on partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n  using warp_sort_delegate =\n    cuda::std::_If<params::is_stable, warp_partial_stable_sort_keys_t, warp_partial_sort_keys_t>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = cuda::std::numeric_limits<type>::max();\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, d_segment_sizes.cbegin(), oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in_out     = d_in;\n  c2h::host_vector<int> segment_sizes = d_segment_sizes;\n  compute_host_reference(h_in_out.begin(), segment_sizes, params::total_warps, oob_default, params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nC2H_TEST(\"Warp sort on keys-value pairs works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params             = params_t<TestType>;\n  using key_type           = typename params::type;\n  using value_type         = typename c2h::get<4, TestType>;\n  using warp_sort_delegate = cuda::std::_If<params::is_stable, warp_stable_sort_pairs_t, warp_sort_pairs_t>;\n\n  // Prepare test data\n  c2h::device_vector<key_type> d_keys_in(params::tile_size);\n  c2h::device_vector<key_type> d_keys_out(params::tile_size);\n  c2h::device_vector<value_type> d_values_in(params::tile_size);\n  c2h::device_vector<value_type> d_values_out(params::tile_size);\n  auto segment_sizes     = cuda::constant_iterator(params::logical_warp_items);\n  const auto oob_default = cuda::std::numeric_limits<key_type>::max();\n  c2h::gen(C2H_SEED(10), d_keys_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in, d_keys_out, d_values_in, d_values_out, segment_sizes, oob_default, warp_stable_sort_pairs_t{});\n\n  // Prepare verification data\n  c2h::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  c2h::host_vector<value_type> h_values_in_out = d_values_in;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(\n    cpu_kv_pairs,\n    segment_sizes,\n    params::total_warps,\n    cuda::std::make_tuple(oob_default, value_type{}),\n    params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n\nC2H_TEST(\"Warp sort on key-value pairs of a partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params     = params_t<TestType>;\n  using key_type   = typename params::type;\n  using value_type = typename c2h::get<4, TestType>;\n  using warp_sort_delegate =\n    cuda::std::_If<params::is_stable, warp_partial_stable_sort_pairs_t, warp_partial_sort_pairs_t>;\n\n  // Prepare test data\n  c2h::device_vector<key_type> d_keys_in(params::tile_size);\n  c2h::device_vector<key_type> d_keys_out(params::tile_size);\n  c2h::device_vector<value_type> d_values_in(params::tile_size);\n  c2h::device_vector<value_type> d_values_out(params::tile_size);\n  c2h::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = cuda::std::numeric_limits<key_type>::max();\n  c2h::gen(C2H_SEED(5), d_keys_in);\n  c2h::gen(C2H_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in, d_keys_out, d_values_in, d_values_out, d_segment_sizes.cbegin(), oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  c2h::host_vector<value_type> h_values_in_out = d_values_in;\n  c2h::host_vector<int> segment_sizes          = d_segment_sizes;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(\n    cpu_kv_pairs,\n    segment_sizes,\n    params::total_warps,\n    cuda::std::tuple(oob_default, value_type{}),\n    params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/util_ptx.cuh>\n#include <cub/warp/warp_merge_sort.cuh>\n\n#include <thrust/iterator/zip_iterator.h>\n\n#include <cuda/iterator>\n#include <cuda/std/type_traits>\n\n#include <algorithm>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n\nstruct CustomLess\n{\n  template <typename T>\n  __device__ __host__ bool operator()(const T& lhs, const T& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting keys-only.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(T* in, T* out, SegmentSizeItT segment_sizes, T oob_default, ActionT action)\n{\n  using warp_merge_sort_t = hipcub::WarpMergeSort<T, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = threadIdx.x;\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  T thread_data[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncwarp(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, thread_data, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = (idx - warp_offset) >= valid_items ? oob_default : thread_data[item];\n  }\n}\n\n/**\n * @brief Kernel to dispatch to the appropriate WarpMergeSort member function, sorting key-value\n * pairs.\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizeItT,\n          typename ActionT>\n__global__ void warp_merge_sort_kernel(\n  KeyT* keys_in,\n  KeyT* keys_out,\n  ValueT* values_in,\n  ValueT* values_out,\n  SegmentSizeItT segment_sizes,\n  KeyT oob_default,\n  ActionT action)\n{\n  using warp_merge_sort_t = hipcub::WarpMergeSort<KeyT, ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, ValueT>;\n  using storage_t         = typename warp_merge_sort_t::TempStorage;\n\n  // Get linear thread and warp index\n  const int tid     = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n  const int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Test case of partially finished CTA\n  if (warp_id >= TOTAL_WARPS)\n  {\n    return;\n  }\n\n  // Thread-local storage & warp-scope temporary storage allocation\n  KeyT keys[ITEMS_PER_THREAD];\n  ValueT values[ITEMS_PER_THREAD];\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  // Instantiate warp-scope algorithm\n  warp_merge_sort_t warp_sort(storage[warp_id]);\n\n  const int warp_offset   = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD * warp_id;\n  const int thread_offset = warp_offset + warp_sort.get_linear_tid() * ITEMS_PER_THREAD;\n  const int valid_items   = segment_sizes[warp_id];\n\n  // Load data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx = thread_offset + item;\n    keys[item]    = keys_in[idx];\n    values[item]  = values_in[idx];\n  }\n  __syncwarp(warp_sort.get_member_mask());\n\n  // Run merge sort test\n  action(warp_sort, keys, values, valid_items, oob_default);\n\n  // Store data\n  for (int item = 0; item < ITEMS_PER_THREAD; item++)\n  {\n    const int idx   = thread_offset + item;\n    keys_out[idx]   = (idx - warp_offset) >= valid_items ? oob_default : keys[item];\n    values_out[idx] = (idx - warp_offset) >= valid_items ? ValueT{} : values[item];\n  }\n}\n\n// -----------------------------------------------------------\n// Dimensions being instantiated:\n// {full,partial} x {stable, 'unstable'} x {keys, kv-pairs}\n// -----------------------------------------------------------\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on keys-only\n */\nstruct warp_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int /*valid_items*/, T /*oob_default*/) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_stable_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int valid_items, T oob_default) const\n  {\n    warp_sort.StableSort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on keys-only\n */\nstruct warp_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int /*valid_items*/, T /*oob_default*/) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort keys-only\n */\nstruct warp_partial_sort_keys_t\n{\n  template <typename T, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void\n  operator()(WarpSortT& warp_sort, T (&thread_data)[ITEMS_PER_THREAD], int valid_items, T oob_default) const\n  {\n    warp_sort.Sort(thread_data, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::StableSort on key-value pairs\n */\nstruct warp_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int /*valid_items*/,\n    KeyT /*oob_default*/) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_stable_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int valid_items,\n    KeyT oob_default) const\n  {\n    warp_sort.StableSort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpMergeSort::Sort on key-value pairs\n */\nstruct warp_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int /*valid_items*/,\n    KeyT /*oob_default*/) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{});\n  }\n};\n\n/**\n * @brief Delegate wrapper for partial WarpMergeSort::StableSort key-value pairs\n */\nstruct warp_partial_sort_pairs_t\n{\n  template <typename KeyT, typename ValueT, int ITEMS_PER_THREAD, typename WarpSortT>\n  __device__ void operator()(\n    WarpSortT& warp_sort,\n    KeyT (&keys)[ITEMS_PER_THREAD],\n    ValueT (&values)[ITEMS_PER_THREAD],\n    int valid_items,\n    KeyT oob_default) const\n  {\n    warp_sort.Sort(keys, values, CustomLess{}, valid_items, oob_default);\n  }\n};\n\n/**\n * @brief Dispatch helper function for sorting keys\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename T,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(\n  c2h::device_vector<T>& in, c2h::device_vector<T>& out, SegmentSizesItT segment_sizes, T oob_default, ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n      thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), segment_sizes, oob_default, action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Dispatch helper function for sorting key-value pairs\n */\ntemplate <int ITEMS_PER_THREAD,\n          int LOGICAL_WARP_THREADS,\n          int TOTAL_WARPS,\n          typename KeyT,\n          typename ValueT,\n          typename SegmentSizesItT,\n          typename ActionT>\nvoid warp_merge_sort(\n  c2h::device_vector<KeyT>& keys_in,\n  c2h::device_vector<KeyT>& keys_out,\n  c2h::device_vector<ValueT>& values_in,\n  c2h::device_vector<ValueT>& values_out,\n  SegmentSizesItT segment_sizes,\n  KeyT oob_default,\n  ActionT action)\n{\n  warp_merge_sort_kernel<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, TOTAL_WARPS>\n    <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n      thrust::raw_pointer_cast(keys_in.data()),\n      thrust::raw_pointer_cast(keys_out.data()),\n      thrust::raw_pointer_cast(values_in.data()),\n      thrust::raw_pointer_cast(values_out.data()),\n      segment_sizes,\n      oob_default,\n      action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Performs a stable sort on per-warp segments of data and assigns oob_default to items that\n * are out-of-bounds.\n */\ntemplate <typename RandomItT, typename SegmentSizeItT, typename T>\nvoid compute_host_reference(\n  RandomItT h_data, SegmentSizeItT segment_sizes, unsigned int num_segments, T oob_default, int logical_warp_items)\n{\n  for (unsigned int segment_id = 0; segment_id < num_segments; segment_id++)\n  {\n    unsigned int segment_size = segment_sizes[segment_id];\n    std::stable_sort(h_data, h_data + segment_size);\n    std::fill(h_data + segment_size, h_data + logical_warp_items, oob_default);\n    h_data += logical_warp_items;\n  }\n}\n\n/**\n * @brief Stability requirement of the sorting algorithm\n */\nenum class stability\n{\n  stable,\n  unstable\n};\n\n// List of key types to test\nusing custom_t  = c2h::custom_type_t<c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing key_types = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t, custom_t>;\n\n// List of value types\nusing value_types = c2h::type_list<std::int32_t, custom_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 4>;\n\n// Number of items per thread to test\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 4, 7>;\n\n// Whether the sort is required to be stable or not\nusing stability_list = c2h::enum_type_list<stability, stability::stable, stability::unstable>;\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread     = c2h::get<2, TestType>::value;\n  static constexpr int logical_warp_items   = logical_warp_threads * items_per_thread;\n  static constexpr int total_warps          = 2;\n  static constexpr int tile_size            = items_per_thread * total_warps * logical_warp_threads;\n  static constexpr bool is_stable           = c2h::get<3, TestType>::value == stability::stable;\n};\n\nC2H_TEST(\n  \"Warp sort on keys-only works\", \"[sort][warp]\", key_types, logical_warp_threads, items_per_thread_list, stability_list)\n{\n  using params             = params_t<TestType>;\n  using type               = typename params::type;\n  using warp_sort_delegate = cuda::std::_If<params::is_stable, warp_stable_sort_keys_t, warp_sort_keys_t>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  auto segment_sizes     = cuda::constant_iterator(params::logical_warp_items);\n  const auto oob_default = cuda::std::numeric_limits<type>::max();\n  c2h::gen(C2H_SEED(10), d_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, segment_sizes, oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in_out = d_in;\n  compute_host_reference(h_in_out.begin(), segment_sizes, params::total_warps, oob_default, params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nC2H_TEST(\"Warp sort keys-only on partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n  using warp_sort_delegate =\n    cuda::std::_If<params::is_stable, warp_partial_stable_sort_keys_t, warp_partial_sort_keys_t>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = cuda::std::numeric_limits<type>::max();\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, d_segment_sizes.cbegin(), oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in_out     = d_in;\n  c2h::host_vector<int> segment_sizes = d_segment_sizes;\n  compute_host_reference(h_in_out.begin(), segment_sizes, params::total_warps, oob_default, params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_in_out == d_out);\n}\n\nC2H_TEST(\"Warp sort on keys-value pairs works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params             = params_t<TestType>;\n  using key_type           = typename params::type;\n  using value_type         = typename c2h::get<4, TestType>;\n  using warp_sort_delegate = cuda::std::_If<params::is_stable, warp_stable_sort_pairs_t, warp_sort_pairs_t>;\n\n  // Prepare test data\n  c2h::device_vector<key_type> d_keys_in(params::tile_size);\n  c2h::device_vector<key_type> d_keys_out(params::tile_size);\n  c2h::device_vector<value_type> d_values_in(params::tile_size);\n  c2h::device_vector<value_type> d_values_out(params::tile_size);\n  auto segment_sizes     = cuda::constant_iterator(params::logical_warp_items);\n  const auto oob_default = cuda::std::numeric_limits<key_type>::max();\n  c2h::gen(C2H_SEED(10), d_keys_in);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in, d_keys_out, d_values_in, d_values_out, segment_sizes, oob_default, warp_stable_sort_pairs_t{});\n\n  // Prepare verification data\n  c2h::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  c2h::host_vector<value_type> h_values_in_out = d_values_in;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(\n    cpu_kv_pairs,\n    segment_sizes,\n    params::total_warps,\n    cuda::std::make_tuple(oob_default, value_type{}),\n    params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n\nC2H_TEST(\"Warp sort on key-value pairs of a partial warp-tile works\",\n         \"[sort][warp]\",\n         key_types,\n         logical_warp_threads,\n         items_per_thread_list,\n         stability_list,\n         value_types)\n{\n  using params     = params_t<TestType>;\n  using key_type   = typename params::type;\n  using value_type = typename c2h::get<4, TestType>;\n  using warp_sort_delegate =\n    cuda::std::_If<params::is_stable, warp_partial_stable_sort_pairs_t, warp_partial_sort_pairs_t>;\n\n  // Prepare test data\n  c2h::device_vector<key_type> d_keys_in(params::tile_size);\n  c2h::device_vector<key_type> d_keys_out(params::tile_size);\n  c2h::device_vector<value_type> d_values_in(params::tile_size);\n  c2h::device_vector<value_type> d_values_out(params::tile_size);\n  c2h::device_vector<int> d_segment_sizes(params::total_warps);\n  const auto oob_default = cuda::std::numeric_limits<key_type>::max();\n  c2h::gen(C2H_SEED(5), d_keys_in);\n  c2h::gen(C2H_SEED(5), d_segment_sizes, 0, params::logical_warp_items);\n\n  // Run test\n  warp_merge_sort<params::items_per_thread, params::logical_warp_threads, params::total_warps>(\n    d_keys_in, d_keys_out, d_values_in, d_values_out, d_segment_sizes.cbegin(), oob_default, warp_sort_delegate{});\n\n  // Prepare verification data\n  c2h::host_vector<key_type> h_keys_in_out     = d_keys_in;\n  c2h::host_vector<value_type> h_values_in_out = d_values_in;\n  c2h::host_vector<int> segment_sizes          = d_segment_sizes;\n  auto cpu_kv_pairs = thrust::make_zip_iterator(h_keys_in_out.begin(), h_values_in_out.begin());\n  compute_host_reference(\n    cpu_kv_pairs,\n    segment_sizes,\n    params::total_warps,\n    cuda::std::tuple(oob_default, value_type{}),\n    params::logical_warp_items);\n\n  // Verify results\n  REQUIRE(h_keys_in_out == d_keys_out);\n  REQUIRE(h_values_in_out == d_values_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_warp_merge_sort.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/warp/warp_load.cuh>\n\n#include <thrust/sequence.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/fill_striped.h>\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\n__global__ void warp_load_kernel(InputIteratorT input_iterator, ActionT action, int* error_counter)\n{\n  using warp_load_t = cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_load_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int linear_tid = threadIdx.x;\n\n  const int warp_id = linear_tid / LOGICAL_WARP_THREADS;\n  warp_load_t load(storage[warp_id]);\n\n  // Test WarpLoad specialization\n  T reg[ITEMS_PER_THREAD];\n  action.load(load, input_iterator + (warp_id * tile_size), reg);\n\n  // Verify data was loaded as expected\n  action.verify(reg, error_counter);\n}\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\nvoid warp_load(InputIteratorT input_iterator, ActionT action, int* error_counter)\n{\n  warp_load_kernel<LoadAlgorithm, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, T, InputIteratorT, ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(input_iterator, action, error_counter);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief WarpLoad test specialisation for guarded loads\n */\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, typename T>\nstruct guarded_load_t\n{\n  int valid_items;\n  T oob_default;\n\n  template <int ITEMS_PER_THREAD, typename InputIteratorT>\n  __device__ void load(cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg, valid_items, oob_default);\n  }\n\n  template <int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int* error_counter)\n  {\n    const auto linear_tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n    const auto lane_id    = linear_tid % LOGICAL_WARP_THREADS;\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(linear_tid * ITEMS_PER_THREAD + item);\n\n      const bool is_oob = LoadAlgorithm == cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED\n                          ? item * LOGICAL_WARP_THREADS + lane_id >= valid_items\n                          : lane_id * ITEMS_PER_THREAD + item >= valid_items;\n\n      if (is_oob)\n      {\n        if (reg[item] != oob_default)\n        {\n          atomicAdd(error_counter, 1);\n        }\n      }\n      else if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\n/**\n * @brief WarpLoad test specialisation for unguarded loads\n */\nstruct unguarded_load_t\n{\n  template <cub::WarpLoadAlgorithm LoadAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename InputIteratorT>\n  __device__ void load(cub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg);\n  }\n\n  template <typename T, int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int* error_counter)\n  {\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(threadIdx.x * ITEMS_PER_THREAD + item);\n\n      if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\ntemplate <cub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, int ITEMS_PER_THREAD, int TOTAL_WARPS, typename T>\nc2h::device_vector<T> generate_input()\n{\n  constexpr int tile_size = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  constexpr int num_items = TOTAL_WARPS * tile_size;\n\n  c2h::device_vector<T> d_input(num_items);\n\n  if constexpr (LoadAlgorithm == cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED)\n  {\n    c2h::host_vector<T> h_input(num_items);\n\n    // In this case we need different stripe pattern, so the\n    // items/threads parameters are swapped\n\n    constexpr int FAKE_BLOCK_SIZE = ITEMS_PER_THREAD * TOTAL_WARPS;\n\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, FAKE_BLOCK_SIZE>(h_input.begin());\n    d_input = h_input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{num_items}, d_input);\n  }\n\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms =\n  c2h::enum_type_list<cub::WarpLoadAlgorithm,\n                      cub::WarpLoadAlgorithm::WARP_LOAD_DIRECT,\n                      cub::WarpLoadAlgorithm::WARP_LOAD_STRIPED,\n                      cub::WarpLoadAlgorithm::WARP_LOAD_TRANSPOSE,\n                      cub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE>;\nusing algorithm = c2h::enum_type_list<cub::WarpLoadAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_load_modifier =\n  c2h::enum_type_list<cub::CacheLoadModifier,\n                      cub::CacheLoadModifier::LOAD_DEFAULT,\n                      cub::CacheLoadModifier::LOAD_CA,\n                      cub::CacheLoadModifier::LOAD_CG,\n                      cub::CacheLoadModifier::LOAD_CS,\n                      cub::CacheLoadModifier::LOAD_CV,\n                      cub::CacheLoadModifier::LOAD_LDG,\n                      cub::CacheLoadModifier::LOAD_VOLATILE>;\n\nconstexpr int guarded_load_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == cub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads         = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread             = c2h::get<2, TestType>::value;\n  static constexpr cub::WarpLoadAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                  = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                    = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count             = total_warps * tile_size;\n};\n\nC2H_TEST(\n  \"Warp load guarded range works with pointer\", \"[load][warp]\", types, logical_warp_threads, items_per_thread, algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n\n  const int valid_items  = GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    thrust::raw_pointer_cast(d_in.data()),\n    delegate_t{valid_items, oob_default},\n    thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const int num_errors               = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load guarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n  constexpr cub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  const int valid_items  = GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  auto in_it = cub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    in_it, delegate_t{valid_items, oob_default}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load unguarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = unguarded_load_t;\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    thrust::raw_pointer_cast(d_in.data()), delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load unguarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params                                   = params_t<TestType>;\n  using type                                     = typename params::type;\n  using delegate_t                               = unguarded_load_t;\n  constexpr cub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  auto in_it = cub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    in_it, delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\n#if ALGO_TYPE == 3 // Test for cub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE;\nC2H_TEST(\"Vectorized warp load with const and non-const datatype and different alignment cases\",\n         \"[store][warp]\",\n         c2h::type_list<const int*, int*>,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params         = params_t<TestType>;\n  using type           = int;\n  using input_ptr_type = typename params::type;\n\n  using delegate_t              = unguarded_load_t;\n  const int offset_for_elements = GENERATE_COPY(0, 1, 2, 3, 4);\n\n  auto d_in_ref =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  c2h::device_vector<type> d_in(params::total_item_count + offset_for_elements);\n  thrust::copy_n(d_in_ref.begin(), params::total_item_count, d_in.begin() + offset_for_elements);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type,\n            input_ptr_type>(thrust::raw_pointer_cast(d_in.data()) + offset_for_elements,\n                            delegate_t{},\n                            thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_arch.cuh>\n#include <cub/warp/warp_load.cuh>\n\n#include <thrust/sequence.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/fill_striped.h>\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\n__global__ void warp_load_kernel(InputIteratorT input_iterator, ActionT action, int* error_counter)\n{\n  using warp_load_t = hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS>;\n  using storage_t   = typename warp_load_t::TempStorage;\n\n  constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int linear_tid = threadIdx.x;\n\n  const int warp_id = linear_tid / LOGICAL_WARP_THREADS;\n  warp_load_t load(storage[warp_id]);\n\n  // Test WarpLoad specialization\n  T reg[ITEMS_PER_THREAD];\n  action.load(load, input_iterator + (warp_id * tile_size), reg);\n\n  // Verify data was loaded as expected\n  action.verify(reg, error_counter);\n}\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n          int LOGICAL_WARP_THREADS,\n          int ITEMS_PER_THREAD,\n          int TOTAL_WARPS,\n          typename T,\n          typename InputIteratorT,\n          typename ActionT>\nvoid warp_load(InputIteratorT input_iterator, ActionT action, int* error_counter)\n{\n  warp_load_kernel<LoadAlgorithm, LOGICAL_WARP_THREADS, ITEMS_PER_THREAD, TOTAL_WARPS, T, InputIteratorT, ActionT>\n    <<<1, TOTAL_WARPS * LOGICAL_WARP_THREADS>>>(input_iterator, action, error_counter);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief WarpLoad test specialisation for guarded loads\n */\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, typename T>\nstruct guarded_load_t\n{\n  int valid_items;\n  T oob_default;\n\n  template <int ITEMS_PER_THREAD, typename InputIteratorT>\n  __device__ void load(hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg, valid_items, oob_default);\n  }\n\n  template <int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int* error_counter)\n  {\n    const auto linear_tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n    const auto lane_id    = linear_tid % LOGICAL_WARP_THREADS;\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(linear_tid * ITEMS_PER_THREAD + item);\n\n      const bool is_oob = LoadAlgorithm == hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED\n                          ? item * LOGICAL_WARP_THREADS + lane_id >= valid_items\n                          : lane_id * ITEMS_PER_THREAD + item >= valid_items;\n\n      if (is_oob)\n      {\n        if (reg[item] != oob_default)\n        {\n          atomicAdd(error_counter, 1);\n        }\n      }\n      else if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\n/**\n * @brief WarpLoad test specialisation for unguarded loads\n */\nstruct unguarded_load_t\n{\n  template <hipcub::WarpLoadAlgorithm LoadAlgorithm,\n            int LOGICAL_WARP_THREADS,\n            int ITEMS_PER_THREAD,\n            typename T,\n            typename InputIteratorT>\n  __device__ void load(hipcub::WarpLoad<T, ITEMS_PER_THREAD, LoadAlgorithm, LOGICAL_WARP_THREADS> load,\n                       InputIteratorT input,\n                       T (&reg)[ITEMS_PER_THREAD])\n  {\n    load.Load(input, reg);\n  }\n\n  template <typename T, int ITEMS_PER_THREAD>\n  __device__ void verify(T (&reg)[ITEMS_PER_THREAD], int* error_counter)\n  {\n    for (int item = 0; item < ITEMS_PER_THREAD; item++)\n    {\n      const auto expected_value = static_cast<T>(threadIdx.x * ITEMS_PER_THREAD + item);\n\n      if (reg[item] != expected_value)\n      {\n        atomicAdd(error_counter, 1);\n      }\n    }\n  }\n};\n\ntemplate <hipcub::WarpLoadAlgorithm LoadAlgorithm, int LOGICAL_WARP_THREADS, int ITEMS_PER_THREAD, int TOTAL_WARPS, typename T>\nc2h::device_vector<T> generate_input()\n{\n  constexpr int tile_size = LOGICAL_WARP_THREADS * ITEMS_PER_THREAD;\n  constexpr int num_items = TOTAL_WARPS * tile_size;\n\n  c2h::device_vector<T> d_input(num_items);\n\n  if constexpr (LoadAlgorithm == hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED)\n  {\n    c2h::host_vector<T> h_input(num_items);\n\n    // In this case we need different stripe pattern, so the\n    // items/threads parameters are swapped\n\n    constexpr int FAKE_BLOCK_SIZE = ITEMS_PER_THREAD * TOTAL_WARPS;\n\n    fill_striped<ITEMS_PER_THREAD, LOGICAL_WARP_THREADS, FAKE_BLOCK_SIZE>(h_input.begin());\n    d_input = h_input;\n  }\n  else\n  {\n    c2h::gen(c2h::modulo_t{num_items}, d_input);\n  }\n\n  return d_input;\n}\n\n// %PARAM% LWT lwt 4:16:32\n// %PARAM% ALGO_TYPE alg 0:1:2:3\n\nusing types                = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\nusing items_per_thread     = c2h::enum_type_list<int, 1, 4, 7>;\nusing logical_warp_threads = c2h::enum_type_list<int, LWT>;\nusing algorithms =\n  c2h::enum_type_list<hipcub::WarpLoadAlgorithm,\n                      hipcub::WarpLoadAlgorithm::WARP_LOAD_DIRECT,\n                      hipcub::WarpLoadAlgorithm::WARP_LOAD_STRIPED,\n                      hipcub::WarpLoadAlgorithm::WARP_LOAD_TRANSPOSE,\n                      hipcub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE>;\nusing algorithm = c2h::enum_type_list<hipcub::WarpLoadAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\nusing cache_load_modifier =\n  c2h::enum_type_list<hipcub::CacheLoadModifier,\n                      hipcub::CacheLoadModifier::LOAD_DEFAULT,\n                      hipcub::CacheLoadModifier::LOAD_CA,\n                      hipcub::CacheLoadModifier::LOAD_CG,\n                      hipcub::CacheLoadModifier::LOAD_CS,\n                      hipcub::CacheLoadModifier::LOAD_CV,\n                      hipcub::CacheLoadModifier::LOAD_LDG,\n                      hipcub::CacheLoadModifier::LOAD_VOLATILE>;\n\nconstexpr int guarded_load_tests_count = 30;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == hipcub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads         = c2h::get<1, TestType>::value;\n  static constexpr int items_per_thread             = c2h::get<2, TestType>::value;\n  static constexpr hipcub::WarpLoadAlgorithm algorithm = c2h::get<3, TestType>::value;\n  static constexpr int total_warps                  = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size                    = logical_warp_threads * items_per_thread;\n  static constexpr int total_item_count             = total_warps * tile_size;\n};\n\nC2H_TEST(\n  \"Warp load guarded range works with pointer\", \"[load][warp]\", types, logical_warp_threads, items_per_thread, algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n\n  const int valid_items  = GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    thrust::raw_pointer_cast(d_in.data()),\n    delegate_t{valid_items, oob_default},\n    thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const int num_errors               = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load guarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = guarded_load_t<params::algorithm, params::logical_warp_threads, type>;\n  constexpr hipcub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  const int valid_items  = GENERATE_COPY(take(guarded_load_tests_count, random(0, params::tile_size - 1)));\n  const auto oob_default = static_cast<type>(valid_items);\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  auto in_it = hipcub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    in_it, delegate_t{valid_items, oob_default}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load unguarded range works with pointer\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params     = params_t<TestType>;\n  using type       = typename params::type;\n  using delegate_t = unguarded_load_t;\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    thrust::raw_pointer_cast(d_in.data()), delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\nC2H_TEST(\"Warp load unguarded range works with cache modified iterator\",\n         \"[load][warp]\",\n         types,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm,\n         cache_load_modifier)\n{\n  using params                                   = params_t<TestType>;\n  using type                                     = typename params::type;\n  using delegate_t                               = unguarded_load_t;\n  constexpr hipcub::CacheLoadModifier load_modifier = c2h::get<4, TestType>::value;\n\n  auto d_in =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  auto in_it = hipcub::CacheModifiedInputIterator<load_modifier, type>(thrust::raw_pointer_cast(d_in.data()));\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  warp_load<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>(\n    in_it, delegate_t{}, thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n\n#if ALGO_TYPE == 3 // Test for cub::WarpLoadAlgorithm::WARP_LOAD_VECTORIZE;\nC2H_TEST(\"Vectorized warp load with const and non-const datatype and different alignment cases\",\n         \"[store][warp]\",\n         c2h::type_list<const int*, int*>,\n         logical_warp_threads,\n         items_per_thread,\n         algorithm)\n{\n  using params         = params_t<TestType>;\n  using type           = int;\n  using input_ptr_type = typename params::type;\n\n  using delegate_t              = unguarded_load_t;\n  const int offset_for_elements = GENERATE_COPY(0, 1, 2, 3, 4);\n\n  auto d_in_ref =\n    generate_input<params::algorithm, params::logical_warp_threads, params::items_per_thread, params::total_warps, type>();\n  c2h::device_vector<int> d_error_counter(1, 0);\n\n  c2h::device_vector<type> d_in(params::total_item_count + offset_for_elements);\n  thrust::copy_n(d_in_ref.begin(), params::total_item_count, d_in.begin() + offset_for_elements);\n\n  warp_load<params::algorithm,\n            params::logical_warp_threads,\n            params::items_per_thread,\n            params::total_warps,\n            type,\n            input_ptr_type>(thrust::raw_pointer_cast(d_in.data()) + offset_for_elements,\n                            delegate_t{},\n                            thrust::raw_pointer_cast(d_error_counter.data()));\n\n  const auto num_errors              = d_error_counter[0];\n  constexpr int expected_error_count = 0;\n  REQUIRE(num_errors == expected_error_count);\n}\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_warp_load.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_load.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, cub::BlockLoadAlgorithm LoadAlgorithm>\nstatic __device__ int get_output_idx(int item)\n{\n  if (LoadAlgorithm == cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n  return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm LoadAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\n__global__ void kernel(cuda::std::true_type, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t      = cub::detail::it_value_t<InputIteratorT>;\n  using block_load_t = cub::BlockLoad<input_t, ThreadsInBlock, ItemsPerThread, LoadAlgorithm>;\n  using storage_t    = typename block_load_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load_t block_load(storage);\n  input_t data[ItemsPerThread];\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_load.Load(input, data);\n  }\n  else\n  {\n    block_load.Load(input, data, num_items);\n  }\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = get_output_idx<ItemsPerThread, ThreadsInBlock, LoadAlgorithm>(i);\n    if (idx < num_items)\n    {\n      output[idx] = data[i];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockLoadAlgorithm /* LoadAlgorithm */,\n          typename InputIteratorT,\n          typename OutputIteratorT>\n__global__ void kernel(cuda::std::false_type, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = get_output_idx<ItemsPerThread, ThreadsInBlock, cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT>(i);\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, cub::BlockLoadAlgorithm LoadAlgorithm, typename T, typename InputIteratorT>\nvoid test_block_load(const c2h::device_vector<T>& d_input, InputIteratorT input)\n{\n  using block_load_t = cub::BlockLoad<T, ThreadsInBlock, ItemsPerThread, LoadAlgorithm>;\n  using storage_t    = typename block_load_t::TempStorage;\n  constexpr auto sufficient_resources =\n    cuda::std::bool_constant<sizeof(storage_t) <= cub::detail::max_smem_per_block>{};\n\n  c2h::device_vector<T> d_output(d_input.size());\n  kernel<ItemsPerThread, ThreadsInBlock, LoadAlgorithm><<<1, ThreadsInBlock>>>(\n    sufficient_resources, input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(d_input == d_output);\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block  = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size          = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing load_algorithm =\n  c2h::enum_type_list<cub::BlockLoadAlgorithm,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_load_algorithm =\n  c2h::enum_type_list<cub::BlockLoadAlgorithm,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n                      cub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                   = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                   = c2h::get<2, TestType>::value;\n  static constexpr int tile_size                          = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block load works with even block sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load works with even odd sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\n// WAR bug in vec type handling in NVCC 12.0 + GCC 11.4 + C++20\n#if !(_CCCL_CUDA_COMPILER(NVCC, ==, 12, 0) && _CCCL_COMPILER(GCC, ==, 11, 4) && _CCCL_STD_VER == 2020)\nC2H_TEST(\n  \"Block load works with even vector types\", \"[load][block]\", vec_types, items_per_thread, a_block_size, load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n#endif // !(NVCC 12.0 and GCC 11.4 and C++20)\n\nC2H_TEST(\"Block load works with custom types\", \"[load][block]\", items_per_thread, load_algorithm)\n{\n  using type                                              = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread                          = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<items_per_thread, threads_in_block, load_algorithm>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load works with caching iterators\", \"[load][block]\", items_per_thread, load_algorithm)\n{\n  using type                                              = int;\n  constexpr int items_per_thread                          = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  cub::CacheModifiedInputIterator<cub::CacheLoadModifier::LOAD_DEFAULT, type> in(\n    thrust::raw_pointer_cast(d_input.data()));\n  test_block_load<items_per_thread, threads_in_block, load_algorithm>(d_input, in);\n}\n\n#if IPT == 1\nC2H_TEST(\"Vectorized block load with const and non-const datatype and different alignment cases\",\n         \"[load][block]\",\n         c2h::type_list<const int*, int*>)\n{\n  using type           = int;\n  using input_ptr_type = c2h::get<0, TestType>;\n\n  const int offset_for_elements                           = GENERATE_COPY(0, 1, 2, 3, 4);\n  constexpr int items_per_thread                          = 4;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr cub::BlockLoadAlgorithm load_algorithm = cub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE;\n\n  c2h::device_vector<type> d_input_ref(tile_size);\n  c2h::gen(C2H_SEED(10), d_input_ref);\n\n  c2h::device_vector<type> d_input(tile_size + offset_for_elements);\n  thrust::copy_n(d_input_ref.begin(), tile_size, d_input.begin() + offset_for_elements);\n\n  test_block_load<items_per_thread, threads_in_block, load_algorithm, type, input_ptr_type>(\n    d_input_ref, thrust::raw_pointer_cast(d_input.data()) + offset_for_elements);\n}\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_load.cuh>\n#include <cub/iterator/cache_modified_input_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, hipcub::BlockLoadAlgorithm LoadAlgorithm>\nstatic __device__ int get_output_idx(int item)\n{\n  if (LoadAlgorithm == hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n  return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm LoadAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\n__global__ void kernel(cuda::std::true_type, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t      = hipcub::detail::it_value_t<InputIteratorT>;\n  using block_load_t = hipcub::BlockLoad<input_t, ThreadsInBlock, ItemsPerThread, LoadAlgorithm>;\n  using storage_t    = typename block_load_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_load_t block_load(storage);\n  input_t data[ItemsPerThread];\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_load.Load(input, data);\n  }\n  else\n  {\n    block_load.Load(input, data, num_items);\n  }\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = get_output_idx<ItemsPerThread, ThreadsInBlock, LoadAlgorithm>(i);\n    if (idx < num_items)\n    {\n      output[idx] = data[i];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockLoadAlgorithm /* LoadAlgorithm */,\n          typename InputIteratorT,\n          typename OutputIteratorT>\n__global__ void kernel(cuda::std::false_type, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = get_output_idx<ItemsPerThread, ThreadsInBlock, hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT>(i);\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, hipcub::BlockLoadAlgorithm LoadAlgorithm, typename T, typename InputIteratorT>\nvoid test_block_load(const c2h::device_vector<T>& d_input, InputIteratorT input)\n{\n  using block_load_t = hipcub::BlockLoad<T, ThreadsInBlock, ItemsPerThread, LoadAlgorithm>;\n  using storage_t    = typename block_load_t::TempStorage;\n  constexpr auto sufficient_resources =\n    cuda::std::bool_constant<sizeof(storage_t) <= hipcub::detail::max_smem_per_block>{};\n\n  c2h::device_vector<T> d_output(d_input.size());\n  kernel<ItemsPerThread, ThreadsInBlock, LoadAlgorithm><<<1, ThreadsInBlock>>>(\n    sufficient_resources, input, thrust::raw_pointer_cast(d_output.data()), static_cast<int>(d_input.size()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(d_input == d_output);\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block  = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size          = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing load_algorithm =\n  c2h::enum_type_list<hipcub::BlockLoadAlgorithm,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_load_algorithm =\n  c2h::enum_type_list<hipcub::BlockLoadAlgorithm,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_DIRECT,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_STRIPED,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE,\n                      hipcub::BlockLoadAlgorithm::BLOCK_LOAD_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                   = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                   = c2h::get<2, TestType>::value;\n  static constexpr int tile_size                          = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block load works with even block sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load works with even odd sizes\",\n         \"[load][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\n// WAR bug in vec type handling in NVCC 12.0 + GCC 11.4 + C++20\n#if !(_CCCL_CUDA_COMPILER(NVCC, ==, 12, 0) && _CCCL_COMPILER(GCC, ==, 11, 4) && _CCCL_STD_VER == 2020)\nC2H_TEST(\n  \"Block load works with even vector types\", \"[load][block]\", vec_types, items_per_thread, a_block_size, load_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<params::items_per_thread, params::threads_in_block, params::load_algorithm>(\n    d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n#endif // !(NVCC 12.0 and GCC 11.4 and C++20)\n\nC2H_TEST(\"Block load works with custom types\", \"[load][block]\", items_per_thread, load_algorithm)\n{\n  using type                                              = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread                          = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  test_block_load<items_per_thread, threads_in_block, load_algorithm>(d_input, thrust::raw_pointer_cast(d_input.data()));\n}\n\nC2H_TEST(\"Block load works with caching iterators\", \"[load][block]\", items_per_thread, load_algorithm)\n{\n  using type                                              = int;\n  constexpr int items_per_thread                          = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n  hipcub::CacheModifiedInputIterator<hipcub::CacheLoadModifier::LOAD_DEFAULT, type> in(\n    thrust::raw_pointer_cast(d_input.data()));\n  test_block_load<items_per_thread, threads_in_block, load_algorithm>(d_input, in);\n}\n\n#if IPT == 1\nC2H_TEST(\"Vectorized block load with const and non-const datatype and different alignment cases\",\n         \"[load][block]\",\n         c2h::type_list<const int*, int*>)\n{\n  using type           = int;\n  using input_ptr_type = c2h::get<0, TestType>;\n\n  const int offset_for_elements                           = GENERATE_COPY(0, 1, 2, 3, 4);\n  constexpr int items_per_thread                          = 4;\n  constexpr int threads_in_block                          = 64;\n  constexpr int tile_size                                 = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockLoadAlgorithm load_algorithm = hipcub::BlockLoadAlgorithm::BLOCK_LOAD_VECTORIZE;\n\n  c2h::device_vector<type> d_input_ref(tile_size);\n  c2h::gen(C2H_SEED(10), d_input_ref);\n\n  c2h::device_vector<type> d_input(tile_size + offset_for_elements);\n  thrust::copy_n(d_input_ref.begin(), tile_size, d_input.begin() + offset_for_elements);\n\n  test_block_load<items_per_thread, threads_in_block, load_algorithm, type, input_ptr_type>(\n    d_input_ref, thrust::raw_pointer_cast(d_input.data()) + offset_for_elements);\n}\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_load.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_adjacent_difference.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class T, class ActionT>\n__global__ void block_adj_diff_kernel(T* data, ActionT action, bool in_place)\n{\n  using block_adjacent_differencet_t = cub::BlockAdjacentDifference<T, ThreadsInBlock>;\n  using temp_storage_t               = typename block_adjacent_differencet_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_in[ItemsPerThread];\n  T thread_out[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_in[item] = data[thread_offset + item];\n  }\n  __syncthreads();\n\n  block_adjacent_differencet_t adj_diff(temp_storage);\n\n  if (in_place)\n  {\n    action(adj_diff, thread_in, thread_in);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_in[item];\n    }\n  }\n  else\n  {\n    action(adj_diff, thread_in, thread_out);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_out[item];\n    }\n  }\n}\n\ntemplate <class T>\nstruct custom_difference_t\n{\n  __host__ __device__ T operator()(const T& lhs, const T& rhs)\n  {\n    return lhs - rhs;\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct base_op_t\n{\n  template <int ItemsPerThread, typename T, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff& block_adj_diff, T (&input)[ItemsPerThread], T (&output)[ItemsPerThread]) const\n  {\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, custom_difference_t<T>{});\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, custom_difference_t<T>{});\n    }\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct last_tile_op_t\n{\n  int m_valid_items{};\n\n  __host__ last_tile_op_t(int valid_items)\n      : m_valid_items(valid_items)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename T, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeftPartialTile(input, output, diff, m_valid_items);\n    }\n    else\n    {\n      block_adj_diff.SubtractRightPartialTile(input, output, diff, m_valid_items);\n    }\n  }\n};\n\ntemplate <class T, bool ReadLeft>\nstruct middle_tile_op_t\n{\n  T m_neighbour_tile_value;\n\n  __host__ middle_tile_op_t(T neighbour_tile_value)\n      : m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, diff, m_neighbour_tile_value);\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, diff, m_neighbour_tile_value);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct last_tile_with_pred_op_t\n{\n  int m_valid_items;\n  T m_neighbour_tile_value;\n\n  __host__ last_tile_with_pred_op_t(int valid_items, T neighbour_tile_value)\n      : m_valid_items(valid_items)\n      , m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n    block_adj_diff.SubtractLeftPartialTile(input, output, diff, m_valid_items, m_neighbour_tile_value);\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class T, class ActionT>\nvoid block_adj_diff(c2h::device_vector<T>& data, bool in_place, ActionT action)\n{\n  block_adj_diff_kernel<ThreadsInBlock, ItemsPerThread, T, ActionT>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(data.data()), action, in_place);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(c2h::host_vector<T>& h_data, int valid_items)\n{\n  custom_difference_t<T> diff{};\n\n  if (ReadLeft)\n  {\n    for (int i = valid_items - 1; i > 0; i--)\n    {\n      h_data[i] = diff(h_data[i], h_data[i - 1]);\n    }\n  }\n  else\n  {\n    for (int i = 0; i < valid_items - 1; i++)\n    {\n      h_data[i] = diff(h_data[i], h_data[i + 1]);\n    }\n  }\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(c2h::host_vector<T>& h_data, int valid_items, T neighbour_value)\n{\n  custom_difference_t<T> diff{};\n\n  host_adj_diff<ReadLeft>(h_data, valid_items);\n\n  if (valid_items == 0)\n  {\n    return;\n  }\n\n  if (ReadLeft)\n  {\n    h_data[0] = diff(h_data[0], neighbour_value);\n  }\n  else\n  {\n    h_data[valid_items - 1] = diff(h_data[valid_items - 1], neighbour_value);\n  }\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types = c2h::type_list<std::uint16_t, std::int32_t, std::int64_t>;\n\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\nusing directions       = c2h::enum_type_list<bool, false, true>;\nusing left_only        = c2h::enum_type_list<bool, true>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr bool read_left       = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block adjacent difference works with full tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, params::tile_size);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(d_data, in_place, base_op_t<params::read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with last tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place   = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, valid_items);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, last_tile_op_t<params::read_left>{valid_items});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with single tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         left_only)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place      = GENERATE(false, true);\n  const int valid_items    = GENERATE_COPY(take(10, random(0, params::tile_size)));\n  constexpr bool read_left = true;\n\n  c2h::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value          = h_data[h_data.size() / 2];\n\n  host_adj_diff<read_left>(h_data, valid_items, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, last_tile_with_pred_op_t<key_t>{valid_items, neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with middle tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value          = h_data[h_data.size() / 2];\n\n  host_adj_diff<params::read_left>(h_data, params::tile_size, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, middle_tile_op_t<key_t, params::read_left>{neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference supports custom types\", \"[adjacent difference][block]\", threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::equal_comparable_t, c2h::subtractable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n  constexpr bool read_left       = true;\n  constexpr bool in_place        = true;\n\n  c2h::device_vector<key_t> d_data(tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<read_left>(h_data, tile_size);\n\n  block_adj_diff<items_per_thread, threads_in_block>(d_data, in_place, base_op_t<read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\n// TODO Test different input/output types\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_adjacent_difference.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ThreadsInBlock, int ItemsPerThread, class T, class ActionT>\n__global__ void block_adj_diff_kernel(T* data, ActionT action, bool in_place)\n{\n  using block_adjacent_differencet_t = hipcub::BlockAdjacentDifference<T, ThreadsInBlock>;\n  using temp_storage_t               = typename block_adjacent_differencet_t::TempStorage;\n\n  __shared__ temp_storage_t temp_storage;\n\n  T thread_in[ItemsPerThread];\n  T thread_out[ItemsPerThread];\n\n  const int thread_offset = static_cast<int>(threadIdx.x) * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_in[item] = data[thread_offset + item];\n  }\n  __syncthreads();\n\n  block_adjacent_differencet_t adj_diff(temp_storage);\n\n  if (in_place)\n  {\n    action(adj_diff, thread_in, thread_in);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_in[item];\n    }\n  }\n  else\n  {\n    action(adj_diff, thread_in, thread_out);\n\n    for (unsigned int item = 0; item < ItemsPerThread; item++)\n    {\n      data[thread_offset + item] = thread_out[item];\n    }\n  }\n}\n\ntemplate <class T>\nstruct custom_difference_t\n{\n  __host__ __device__ T operator()(const T& lhs, const T& rhs)\n  {\n    return lhs - rhs;\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct base_op_t\n{\n  template <int ItemsPerThread, typename T, typename BlockAdjDiff>\n  __device__ void operator()(BlockAdjDiff& block_adj_diff, T (&input)[ItemsPerThread], T (&output)[ItemsPerThread]) const\n  {\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, custom_difference_t<T>{});\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, custom_difference_t<T>{});\n    }\n  }\n};\n\ntemplate <bool ReadLeft>\nstruct last_tile_op_t\n{\n  int m_valid_items{};\n\n  __host__ last_tile_op_t(int valid_items)\n      : m_valid_items(valid_items)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename T, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeftPartialTile(input, output, diff, m_valid_items);\n    }\n    else\n    {\n      block_adj_diff.SubtractRightPartialTile(input, output, diff, m_valid_items);\n    }\n  }\n};\n\ntemplate <class T, bool ReadLeft>\nstruct middle_tile_op_t\n{\n  T m_neighbour_tile_value;\n\n  __host__ middle_tile_op_t(T neighbour_tile_value)\n      : m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n\n    if (ReadLeft)\n    {\n      block_adj_diff.SubtractLeft(input, output, diff, m_neighbour_tile_value);\n    }\n    else\n    {\n      block_adj_diff.SubtractRight(input, output, diff, m_neighbour_tile_value);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct last_tile_with_pred_op_t\n{\n  int m_valid_items;\n  T m_neighbour_tile_value;\n\n  __host__ last_tile_with_pred_op_t(int valid_items, T neighbour_tile_value)\n      : m_valid_items(valid_items)\n      , m_neighbour_tile_value(neighbour_tile_value)\n  {}\n\n  template <int ITEMS_PER_THREAD, typename BlockAdjDiff>\n  __device__ void\n  operator()(BlockAdjDiff& block_adj_diff, T (&input)[ITEMS_PER_THREAD], T (&output)[ITEMS_PER_THREAD]) const\n  {\n    custom_difference_t<T> diff{};\n    block_adj_diff.SubtractLeftPartialTile(input, output, diff, m_valid_items, m_neighbour_tile_value);\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, class T, class ActionT>\nvoid block_adj_diff(c2h::device_vector<T>& data, bool in_place, ActionT action)\n{\n  block_adj_diff_kernel<ThreadsInBlock, ItemsPerThread, T, ActionT>\n    <<<1, ThreadsInBlock>>>(thrust::raw_pointer_cast(data.data()), action, in_place);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(c2h::host_vector<T>& h_data, int valid_items)\n{\n  custom_difference_t<T> diff{};\n\n  if (ReadLeft)\n  {\n    for (int i = valid_items - 1; i > 0; i--)\n    {\n      h_data[i] = diff(h_data[i], h_data[i - 1]);\n    }\n  }\n  else\n  {\n    for (int i = 0; i < valid_items - 1; i++)\n    {\n      h_data[i] = diff(h_data[i], h_data[i + 1]);\n    }\n  }\n}\n\ntemplate <bool ReadLeft, class T>\nvoid host_adj_diff(c2h::host_vector<T>& h_data, int valid_items, T neighbour_value)\n{\n  custom_difference_t<T> diff{};\n\n  host_adj_diff<ReadLeft>(h_data, valid_items);\n\n  if (valid_items == 0)\n  {\n    return;\n  }\n\n  if (ReadLeft)\n  {\n    h_data[0] = diff(h_data[0], neighbour_value);\n  }\n  else\n  {\n    h_data[valid_items - 1] = diff(h_data[valid_items - 1], neighbour_value);\n  }\n}\n\n// %PARAM% THREADS_IN_BLOCK bs 64:256\n\nusing key_types = c2h::type_list<std::uint16_t, std::int32_t, std::int64_t>;\n\nusing threads_in_block = c2h::enum_type_list<int, THREADS_IN_BLOCK>;\nusing items_per_thread = c2h::enum_type_list<int, 1, 2, 10, 15>;\nusing directions       = c2h::enum_type_list<bool, false, true>;\nusing left_only        = c2h::enum_type_list<bool, true>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using key_t = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block = c2h::get<2, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * threads_in_block;\n  static constexpr bool read_left       = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block adjacent difference works with full tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, params::tile_size);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(d_data, in_place, base_op_t<params::read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with last tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place   = GENERATE(false, true);\n  const int valid_items = GENERATE_COPY(take(10, random(0, params::tile_size)));\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<params::read_left>(h_data, valid_items);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, last_tile_op_t<params::read_left>{valid_items});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with single tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         left_only)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place      = GENERATE(false, true);\n  const int valid_items    = GENERATE_COPY(take(10, random(0, params::tile_size)));\n  constexpr bool read_left = true;\n\n  c2h::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value          = h_data[h_data.size() / 2];\n\n  host_adj_diff<read_left>(h_data, valid_items, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, last_tile_with_pred_op_t<key_t>{valid_items, neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference works with middle tiles\",\n         \"[adjacent difference][block]\",\n         key_types,\n         items_per_thread,\n         threads_in_block,\n         directions)\n{\n  using params = params_t<TestType>;\n  using key_t  = typename params::key_t;\n\n  c2h::device_vector<key_t> d_data(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  const bool in_place = GENERATE(false, true);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  key_t neighbour_value          = h_data[h_data.size() / 2];\n\n  host_adj_diff<params::read_left>(h_data, params::tile_size, neighbour_value);\n\n  block_adj_diff<params::items_per_thread, params::threads_in_block>(\n    d_data, in_place, middle_tile_op_t<key_t, params::read_left>{neighbour_value});\n\n  REQUIRE(h_data == d_data);\n}\n\nC2H_TEST(\"Block adjacent difference supports custom types\", \"[adjacent difference][block]\", threads_in_block)\n{\n  using key_t = c2h::custom_type_t<c2h::equal_comparable_t, c2h::subtractable_t>;\n\n  constexpr int items_per_thread = 2;\n  constexpr int threads_in_block = c2h::get<0, TestType>::value;\n  constexpr int tile_size        = threads_in_block * items_per_thread;\n  constexpr bool read_left       = true;\n  constexpr bool in_place        = true;\n\n  c2h::device_vector<key_t> d_data(tile_size);\n  c2h::gen(C2H_SEED(10), d_data);\n\n  c2h::host_vector<key_t> h_data = d_data;\n  host_adj_diff<read_left>(h_data, tile_size);\n\n  block_adj_diff<items_per_thread, threads_in_block>(d_data, in_place, base_op_t<read_left>{});\n\n  REQUIRE(h_data == d_data);\n}\n\n// TODO Test different input/output types\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_adjacent_difference.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_run_length_decode.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/device/device_scan.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <cuda/iterator>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n\n/******************************************************************************\n * HELPER CLASS FOR RUN-LENGTH DECODING TESTS\n ******************************************************************************/\n\n/**\n * \\brief Class template to facilitate testing the BlockRunLengthDecode algorithm for all its\n * template parameter specialisations.\n *\n * \\tparam ItemItT The item type being run-length decoded\n * \\tparam RunLengthsItT Iterator type providing the runs' lengths\n * \\tparam RUNS_PER_THREAD The number of runs that each thread is getting assigned to\n * \\tparam DECODED_ITEMS_PER_THREAD The number of run-length decoded items that each thread is\n *         decoding \\tparam TEST_RELATIVE_OFFSETS_ Whether to also retrieve each decoded item's\n *         relative offset within its run \\tparam TEST_RUN_OFFSETS_ Whether to pass in each run's\n *         offset instead of each run's length \\tparam BLOCK_DIM_X The thread block length in\n *         threads along the X dimension\n * \\tparam BLOCK_DIM_Y The thread block length in threads along the Y dimension\n * \\tparam BLOCK_DIM_Z The thread block length in threads along the Z dimension\n */\ntemplate <typename ItemItT,\n          typename RunLengthsItT,\n          int RUNS_PER_THREAD,\n          int DECODED_ITEMS_PER_THREAD,\n          bool TEST_RELATIVE_OFFSETS_,\n          bool TEST_RUN_OFFSETS_,\n          int BLOCK_DIM_X,\n          int BLOCK_DIM_Y = 1,\n          int BLOCK_DIM_Z = 1>\nclass AgentTestBlockRunLengthDecode\n{\npublic:\n  static constexpr uint32_t BLOCK_THREADS     = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  static constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * BLOCK_THREADS;\n  static constexpr bool TEST_RELATIVE_OFFSETS = TEST_RELATIVE_OFFSETS_;\n\nprivate:\n  using RunItemT   = cub::detail::it_value_t<ItemItT>;\n  using RunLengthT = cub::detail::it_value_t<RunLengthsItT>;\n\n  using BlockRunOffsetScanT = cub::BlockScan<RunLengthT, BLOCK_DIM_X, cub::BLOCK_SCAN_RAKING, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockRunLengthDecodeT =\n    cub::BlockRunLengthDecode<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, DECODED_ITEMS_PER_THREAD>;\n\n  using BlockLoadRunItemT =\n    cub::BlockLoad<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, cub::BLOCK_LOAD_WARP_TRANSPOSE, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockLoadRunLengthsT =\n    cub::BlockLoad<RunLengthT, BLOCK_DIM_X, RUNS_PER_THREAD, cub::BLOCK_LOAD_WARP_TRANSPOSE, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  // These must both use BLOCK_STORE_DIRECT. They're called in a loop with RunLengthDecode and must not\n  // use shmem so that we can test repeatedly calling RLD without synchronizing.\n  using BlockStoreDecodedItemT =\n    cub::BlockStore<RunItemT, BLOCK_DIM_X, DECODED_ITEMS_PER_THREAD, cub::BLOCK_STORE_DIRECT, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n  using BlockStoreRelativeOffsetT =\n    cub::BlockStore<RunLengthT, BLOCK_DIM_X, DECODED_ITEMS_PER_THREAD, cub::BLOCK_STORE_DIRECT, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  __device__ __forceinline__ BlockRunLengthDecodeT InitBlockRunLengthDecode(\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    RunLengthT& decoded_size,\n    cuda::std::true_type /*test_run_offsets*/)\n  {\n    RunLengthT run_offsets[RUNS_PER_THREAD];\n    BlockRunOffsetScanT(temp_storage.run_offsets_scan_storage).ExclusiveSum(run_lengths, run_offsets, decoded_size);\n\n    // Ensure temporary shared memory can be repurposed\n    __syncthreads();\n\n    // Construct BlockRunLengthDecode and initialize with the run offsets\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage, unique_items, run_offsets);\n  }\n\n  __device__ __forceinline__ BlockRunLengthDecodeT InitBlockRunLengthDecode(\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    RunLengthT& decoded_size,\n    cuda::std::false_type /*test_run_offsets*/)\n  {\n    // Construct BlockRunLengthDecode and initialize with the run lengths\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage, unique_items, run_lengths, decoded_size);\n  }\n\n  __device__ __forceinline__ void LoadRuns(\n    ItemItT d_block_unique_items,\n    RunLengthsItT d_block_run_lengths,\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    size_t num_valid_items)\n  {\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items, num_valid_items);\n    }\n    else\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items);\n    }\n\n    // Ensure BlockLoad's temporary shared memory can be repurposed\n    __syncthreads();\n\n    // Load this block's tile of run lengths\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths, num_valid_items, static_cast<RunLengthT>(0));\n    }\n    else\n    {\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage).Load(d_block_run_lengths, run_lengths);\n    }\n\n    // Ensure temporary shared memory can be repurposed\n    __syncthreads();\n  }\n\npublic:\n  union TempStorage\n  {\n    typename BlockLoadRunItemT::TempStorage load_uniques_storage;\n    typename BlockLoadRunLengthsT::TempStorage load_run_lengths_storage;\n    cuda::std::_If<TEST_RUN_OFFSETS_, typename BlockRunOffsetScanT::TempStorage, cub::NullType> run_offsets_scan_storage;\n    struct\n    {\n      typename BlockRunLengthDecodeT::TempStorage run_length_decode_storage;\n      typename BlockStoreDecodedItemT::TempStorage store_decoded_runs_storage;\n      typename BlockStoreRelativeOffsetT::TempStorage store_relative_offsets;\n    } decode;\n  };\n\n  TempStorage& temp_storage;\n\n  __device__ __forceinline__ AgentTestBlockRunLengthDecode(TempStorage& temp_storage)\n      : temp_storage(temp_storage)\n  {}\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, and computes their \"decompressed\" (run-length\n   * decoded) size.\n   */\n  __device__ __forceinline__ uint32_t\n  GetDecodedSize(ItemItT d_block_unique_items, RunLengthsItT d_block_run_lengths, size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items, run_lengths, decoded_size, cuda::std::bool_constant<TEST_RUN_OFFSETS_>{});\n    return decoded_size;\n  }\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, run-length decodes them, and writes the results\n   * to \\p d_block_decoded_out.\n   */\n  template <typename UniqueItemOutItT, typename RelativeOffsetOutItT>\n  __device__ __forceinline__ uint32_t WriteDecodedRuns(\n    ItemItT d_block_unique_items,\n    RunLengthsItT d_block_run_lengths,\n    UniqueItemOutItT d_block_decoded_out,\n    RelativeOffsetOutItT d_block_rel_out,\n    size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items, run_lengths, decoded_size, cuda::std::bool_constant<TEST_RUN_OFFSETS_>{});\n\n    // Run-length decode (\"decompress\") the runs into a window buffer of limited size. This is\n    // repeated until all runs have been decoded.\n    uint32_t decoded_window_offset = 0U;\n    while (decoded_window_offset < decoded_size)\n    {\n      RunLengthT relative_offsets[DECODED_ITEMS_PER_THREAD];\n      RunItemT decoded_items[DECODED_ITEMS_PER_THREAD];\n\n      // The number of decoded items that are valid within this window (aka pass) of run-length\n      // decoding\n      uint32_t num_valid_items = decoded_size - decoded_window_offset;\n      run_length_decode.RunLengthDecode(decoded_items, relative_offsets, decoded_window_offset);\n\n      BlockStoreDecodedItemT(temp_storage.decode.store_decoded_runs_storage)\n        .Store(d_block_decoded_out + decoded_window_offset, decoded_items, num_valid_items);\n\n      if (TEST_RELATIVE_OFFSETS)\n      {\n        BlockStoreRelativeOffsetT(temp_storage.decode.store_relative_offsets)\n          .Store(d_block_rel_out + decoded_window_offset, relative_offsets, num_valid_items);\n      }\n\n      decoded_window_offset += DECODED_ITEMS_PER_THREAD * BLOCK_THREADS;\n    }\n    return decoded_size;\n  }\n};\n\n/******************************************************************************\n * [STAGE 1] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename OffsetT,\n          typename DecodedSizesOutT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__ void BlockRunLengthDecodeGetSizeKernel(\n  const ItemItT d_unique_items,\n  const RunLengthsItT d_run_lengths,\n  const OffsetT num_runs,\n  DecodedSizesOutT d_decoded_sizes)\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset) : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  uint64_t num_decoded_items =\n    run_length_decode_agent.GetDecodedSize(d_unique_items + block_offset, d_run_lengths + block_offset, num_valid_runs);\n\n  d_decoded_sizes[blockIdx.x] = num_decoded_items;\n}\n\n/******************************************************************************\n * [STAGE 2] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename DecodedSizesOutT,\n          typename OffsetT,\n          typename DecodedItemsOutItT,\n          typename RelativeOffsetOutItT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__ void BlockRunLengthDecodeTestKernel(\n  const ItemItT d_unique_items,\n  const RunLengthsItT d_run_lengths,\n  const DecodedSizesOutT d_decoded_offsets,\n  const OffsetT num_runs,\n  DecodedItemsOutItT d_decoded_items,\n  RelativeOffsetOutItT d_relative_offsets)\n\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset) : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  run_length_decode_agent.WriteDecodedRuns(\n    d_unique_items + block_offset,\n    d_run_lengths + block_offset,\n    d_decoded_items + d_decoded_offsets[blockIdx.x],\n    d_relative_offsets + d_decoded_offsets[blockIdx.x],\n    num_valid_runs);\n}\n\nstruct ModOp\n{\n  using T = uint32_t;\n  __host__ __device__ __forceinline__ T operator()(const T& x) const\n  {\n    return 1 + (x % 100);\n  }\n};\n\ntemplate <uint32_t RUNS_PER_THREAD,\n          uint32_t DECODED_ITEMS_PER_THREAD,\n          uint32_t BLOCK_DIM_X,\n          uint32_t BLOCK_DIM_Y,\n          uint32_t BLOCK_DIM_Z,\n          bool TEST_RUN_OFFSETS,\n          bool TEST_RELATIVE_OFFSETS>\nvoid TestAlgorithmSpecialisation()\n{\n  constexpr uint32_t THREADS_PER_BLOCK = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * THREADS_PER_BLOCK;\n\n  using RunItemT      = float;\n  using RunLengthT    = uint32_t;\n  using ItemItT       = thrust::counting_iterator<RunItemT>;\n  using RunLengthsItT = cuda::transform_iterator<ModOp, cuda::counting_iterator<RunLengthT>>;\n\n  ItemItT d_unique_items(1000U);\n  RunLengthsItT d_run_lengths(cuda::counting_iterator<RunLengthT>(0), ModOp{});\n\n  constexpr uint32_t num_runs   = 10000;\n  constexpr uint32_t num_blocks = (num_runs + (RUNS_PER_BLOCK - 1U)) / RUNS_PER_BLOCK;\n\n  size_t temp_storage_bytes      = 0ULL;\n  void* temp_storage             = nullptr;\n  uint32_t* h_num_decoded_total  = nullptr;\n  uint32_t* d_decoded_sizes      = nullptr;\n  uint32_t* d_decoded_offsets    = nullptr;\n  RunItemT* d_decoded_out        = nullptr;\n  RunLengthT* d_relative_offsets = nullptr;\n  RunItemT* h_decoded_out        = nullptr;\n  RunLengthT* h_relative_offsets = nullptr;\n\n  using AgentTestBlockRunLengthDecodeT = AgentTestBlockRunLengthDecode<\n    ItemItT,\n    RunLengthsItT,\n    RUNS_PER_THREAD,\n    DECODED_ITEMS_PER_THREAD,\n    TEST_RELATIVE_OFFSETS,\n    TEST_RUN_OFFSETS,\n    THREADS_PER_BLOCK,\n    1,\n    1>;\n\n  enum : uint32_t\n  {\n    TIMER_SIZE_BEGIN = 0,\n    TIMER_SIZE_END,\n    TIMER_DECODE_BEGIN,\n    TIMER_DECODE_END,\n    NUM_TIMERS,\n  };\n\n  // Get temporary storage requirements for the scan (for computing offsets for the per-block\n  // run-length decoded items)\n  cub::DeviceScan::InclusiveSum(nullptr, temp_storage_bytes, d_decoded_sizes, d_decoded_offsets, num_blocks);\n\n  // Allocate device memory\n  CubDebugExit(cudaMalloc(&temp_storage, temp_storage_bytes));\n  CubDebugExit(cudaMalloc(&d_decoded_sizes, num_blocks * sizeof(*d_decoded_sizes)));\n  // Allocate for the exclusive sum PLUS the overall aggregate\n  CubDebugExit(cudaMalloc(&d_decoded_offsets, (num_blocks + 1) * sizeof(*d_decoded_offsets)));\n  CubDebugExit(cudaMallocHost(&h_num_decoded_total, sizeof(*h_num_decoded_total)));\n\n  // Get the per-block number of items being decoded (i-th thread block writing size to\n  // d_decoded_sizes[i])\n  BlockRunLengthDecodeGetSizeKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items, d_run_lengths, num_runs, d_decoded_sizes);\n\n  // Compute offsets for the runs decoded by each block (exclusive sum + aggregate)\n  CubDebugExit(cudaMemsetAsync(d_decoded_offsets, 0, sizeof(d_decoded_offsets[0])));\n  CubDebugExit(cub::DeviceScan::InclusiveSum(\n    temp_storage, temp_storage_bytes, d_decoded_sizes, &d_decoded_offsets[1], num_blocks));\n\n  // Copy the total decoded size to CPU in order to allocate just the right amount of device memory\n  CubDebugExit(cudaMemcpy(\n    h_num_decoded_total, &d_decoded_offsets[num_blocks], sizeof(*h_num_decoded_total), cudaMemcpyDeviceToHost));\n\n  // Allocate device memory for the run-length decoded output\n  CubDebugExit(cudaMallocHost(&h_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  CubDebugExit(cudaMalloc(&d_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(cudaMalloc(&d_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n    CubDebugExit(cudaMallocHost(&h_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n  }\n\n  // Perform the block-wise run-length decoding (each block taking its offset from\n  // d_decoded_offsets)\n  BlockRunLengthDecodeTestKernel<AgentTestBlockRunLengthDecodeT><<<num_blocks, THREADS_PER_BLOCK, 0U>>>(\n    d_unique_items, d_run_lengths, d_decoded_offsets, num_runs, d_decoded_out, d_relative_offsets);\n\n  // Copy back results for verification\n  CubDebugExit(\n    cudaMemcpy(h_decoded_out, d_decoded_out, (*h_num_decoded_total) * sizeof(*h_decoded_out), cudaMemcpyDeviceToHost));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    // Copy back the relative offsets\n    CubDebugExit(cudaMemcpy(\n      h_relative_offsets,\n      d_relative_offsets,\n      (*h_num_decoded_total) * sizeof(*h_relative_offsets),\n      cudaMemcpyDeviceToHost));\n  }\n\n  // Generate host-side run-length decoded data for verification\n  std::vector<std::pair<RunItemT, RunLengthT>> host_golden;\n  host_golden.reserve(*h_num_decoded_total);\n  for (uint32_t run = 0; run < num_runs; run++)\n  {\n    for (RunLengthT i = 0; i < d_run_lengths[run]; i++)\n    {\n      host_golden.emplace_back(d_unique_items[run], i);\n    }\n  }\n\n  // Verify the total run-length decoded size is correct\n  REQUIRE(host_golden.size() == h_num_decoded_total[0]);\n\n  // Verify the run-length decoded data is correct\n  bool cmp_eq = true;\n  for (uint32_t i = 0; i < host_golden.size(); i++)\n  {\n    if (host_golden[i].first != h_decoded_out[i])\n    {\n      FAIL(\"Mismatch at #\" << i << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i] << \"\\n\");\n      cmp_eq = false;\n    }\n    if (TEST_RELATIVE_OFFSETS)\n    {\n      if (host_golden[i].second != h_relative_offsets[i])\n      {\n        FAIL(\"Mismatch of relative offset at #\"\n             << i << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i]\n             << \"; relative offsets: CPU: \" << host_golden[i].second << \", GPU: \" << h_relative_offsets[i] << \"\\n\");\n        cmp_eq = false;\n        break;\n      }\n    }\n  }\n\n  REQUIRE(cmp_eq == true);\n\n  // Clean up memory allocations\n  CubDebugExit(cudaFree(temp_storage));\n  CubDebugExit(cudaFree(d_decoded_sizes));\n  CubDebugExit(cudaFree(d_decoded_offsets));\n  CubDebugExit(cudaFree(d_decoded_out));\n  CubDebugExit(cudaFreeHost(h_num_decoded_total));\n  CubDebugExit(cudaFreeHost(h_decoded_out));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(cudaFree(d_relative_offsets));\n    CubDebugExit(cudaFreeHost(h_relative_offsets));\n  }\n}\n\nconstexpr bool DO_TEST_RELATIVE_OFFSETS     = true;\nconstexpr bool DO_NOT_TEST_RELATIVE_OFFSETS = false;\n\nconstexpr bool TEST_WITH_RUN_OFFSETS = true;\nconstexpr bool TEST_WITH_RUN_LENGTHS = false;\n\ntemplate <int RunsPerThread, int DecodedItemsPerThread, int BlockDimX, int BlockDimY = 1, int BlockDimZ = 1>\nstruct params_t\n{\n  static constexpr int runs_per_thread          = RunsPerThread;\n  static constexpr int decoded_items_per_thread = DecodedItemsPerThread;\n  static constexpr int block_dim_x              = BlockDimX;\n  static constexpr int block_dim_y              = BlockDimY;\n  static constexpr int block_dim_z              = BlockDimZ;\n};\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run lengths and offsets relative to each run\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run lengths and performs normal run-length \"\n  \"decoding\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run offsets and generates offsets relative to \"\n  \"each run\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run offsets and performs normal run-length \"\n  \"decoding\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_run_length_decode.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/device/device_scan.cuh>\n#include <cub/util_allocator.cuh>\n\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <cuda/iterator>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n\n/******************************************************************************\n * HELPER CLASS FOR RUN-LENGTH DECODING TESTS\n ******************************************************************************/\n\n/**\n * \\brief Class template to facilitate testing the BlockRunLengthDecode algorithm for all its\n * template parameter specialisations.\n *\n * \\tparam ItemItT The item type being run-length decoded\n * \\tparam RunLengthsItT Iterator type providing the runs' lengths\n * \\tparam RUNS_PER_THREAD The number of runs that each thread is getting assigned to\n * \\tparam DECODED_ITEMS_PER_THREAD The number of run-length decoded items that each thread is\n *         decoding \\tparam TEST_RELATIVE_OFFSETS_ Whether to also retrieve each decoded item's\n *         relative offset within its run \\tparam TEST_RUN_OFFSETS_ Whether to pass in each run's\n *         offset instead of each run's length \\tparam BLOCK_DIM_X The thread block length in\n *         threads along the X dimension\n * \\tparam BLOCK_DIM_Y The thread block length in threads along the Y dimension\n * \\tparam BLOCK_DIM_Z The thread block length in threads along the Z dimension\n */\ntemplate <typename ItemItT,\n          typename RunLengthsItT,\n          int RUNS_PER_THREAD,\n          int DECODED_ITEMS_PER_THREAD,\n          bool TEST_RELATIVE_OFFSETS_,\n          bool TEST_RUN_OFFSETS_,\n          int BLOCK_DIM_X,\n          int BLOCK_DIM_Y = 1,\n          int BLOCK_DIM_Z = 1>\nclass AgentTestBlockRunLengthDecode\n{\npublic:\n  static constexpr uint32_t BLOCK_THREADS     = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  static constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * BLOCK_THREADS;\n  static constexpr bool TEST_RELATIVE_OFFSETS = TEST_RELATIVE_OFFSETS_;\n\nprivate:\n  using RunItemT   = hipcub::detail::it_value_t<ItemItT>;\n  using RunLengthT = hipcub::detail::it_value_t<RunLengthsItT>;\n\n  using BlockRunOffsetScanT = hipcub::BlockScan<RunLengthT, BLOCK_DIM_X, hipcub::BLOCK_SCAN_RAKING, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockRunLengthDecodeT =\n    hipcub::BlockRunLengthDecode<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, DECODED_ITEMS_PER_THREAD>;\n\n  using BlockLoadRunItemT =\n    hipcub::BlockLoad<RunItemT, BLOCK_DIM_X, RUNS_PER_THREAD, hipcub::BLOCK_LOAD_WARP_TRANSPOSE, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  using BlockLoadRunLengthsT =\n    hipcub::BlockLoad<RunLengthT, BLOCK_DIM_X, RUNS_PER_THREAD, hipcub::BLOCK_LOAD_WARP_TRANSPOSE, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  // These must both use BLOCK_STORE_DIRECT. They're called in a loop with RunLengthDecode and must not\n  // use shmem so that we can test repeatedly calling RLD without synchronizing.\n  using BlockStoreDecodedItemT =\n    hipcub::BlockStore<RunItemT, BLOCK_DIM_X, DECODED_ITEMS_PER_THREAD, hipcub::BLOCK_STORE_DIRECT, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n  using BlockStoreRelativeOffsetT =\n    hipcub::BlockStore<RunLengthT, BLOCK_DIM_X, DECODED_ITEMS_PER_THREAD, hipcub::BLOCK_STORE_DIRECT, BLOCK_DIM_Y, BLOCK_DIM_Z>;\n\n  __device__ __forceinline__ BlockRunLengthDecodeT InitBlockRunLengthDecode(\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    RunLengthT& decoded_size,\n    cuda::std::true_type /*test_run_offsets*/)\n  {\n    RunLengthT run_offsets[RUNS_PER_THREAD];\n    BlockRunOffsetScanT(temp_storage.run_offsets_scan_storage).ExclusiveSum(run_lengths, run_offsets, decoded_size);\n\n    // Ensure temporary shared memory can be repurposed\n    __syncthreads();\n\n    // Construct BlockRunLengthDecode and initialize with the run offsets\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage, unique_items, run_offsets);\n  }\n\n  __device__ __forceinline__ BlockRunLengthDecodeT InitBlockRunLengthDecode(\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    RunLengthT& decoded_size,\n    cuda::std::false_type /*test_run_offsets*/)\n  {\n    // Construct BlockRunLengthDecode and initialize with the run lengths\n    return BlockRunLengthDecodeT(temp_storage.decode.run_length_decode_storage, unique_items, run_lengths, decoded_size);\n  }\n\n  __device__ __forceinline__ void LoadRuns(\n    ItemItT d_block_unique_items,\n    RunLengthsItT d_block_run_lengths,\n    RunItemT (&unique_items)[RUNS_PER_THREAD],\n    RunLengthT (&run_lengths)[RUNS_PER_THREAD],\n    size_t num_valid_items)\n  {\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items, num_valid_items);\n    }\n    else\n    {\n      BlockLoadRunItemT(temp_storage.load_uniques_storage).Load(d_block_unique_items, unique_items);\n    }\n\n    // Ensure BlockLoad's temporary shared memory can be repurposed\n    __syncthreads();\n\n    // Load this block's tile of run lengths\n    if (num_valid_items < RUNS_PER_BLOCK)\n    {\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage)\n        .Load(d_block_run_lengths, run_lengths, num_valid_items, static_cast<RunLengthT>(0));\n    }\n    else\n    {\n      BlockLoadRunLengthsT(temp_storage.load_run_lengths_storage).Load(d_block_run_lengths, run_lengths);\n    }\n\n    // Ensure temporary shared memory can be repurposed\n    __syncthreads();\n  }\n\npublic:\n  union TempStorage\n  {\n    typename BlockLoadRunItemT::TempStorage load_uniques_storage;\n    typename BlockLoadRunLengthsT::TempStorage load_run_lengths_storage;\n    cuda::std::_If<TEST_RUN_OFFSETS_, typename BlockRunOffsetScanT::TempStorage, hipcub::NullType> run_offsets_scan_storage;\n    struct\n    {\n      typename BlockRunLengthDecodeT::TempStorage run_length_decode_storage;\n      typename BlockStoreDecodedItemT::TempStorage store_decoded_runs_storage;\n      typename BlockStoreRelativeOffsetT::TempStorage store_relative_offsets;\n    } decode;\n  };\n\n  TempStorage& temp_storage;\n\n  __device__ __forceinline__ AgentTestBlockRunLengthDecode(TempStorage& temp_storage)\n      : temp_storage(temp_storage)\n  {}\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, and computes their \"decompressed\" (run-length\n   * decoded) size.\n   */\n  __device__ __forceinline__ uint32_t\n  GetDecodedSize(ItemItT d_block_unique_items, RunLengthsItT d_block_run_lengths, size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items, run_lengths, decoded_size, cuda::std::bool_constant<TEST_RUN_OFFSETS_>{});\n    return decoded_size;\n  }\n\n  /**\n   * \\brief Loads the given block (or tile) of runs, run-length decodes them, and writes the results\n   * to \\p d_block_decoded_out.\n   */\n  template <typename UniqueItemOutItT, typename RelativeOffsetOutItT>\n  __device__ __forceinline__ uint32_t WriteDecodedRuns(\n    ItemItT d_block_unique_items,\n    RunLengthsItT d_block_run_lengths,\n    UniqueItemOutItT d_block_decoded_out,\n    RelativeOffsetOutItT d_block_rel_out,\n    size_t num_valid_runs)\n  {\n    // Load this block's tile of encoded runs\n    RunItemT unique_items[RUNS_PER_THREAD];\n    RunLengthT run_lengths[RUNS_PER_THREAD];\n    LoadRuns(d_block_unique_items, d_block_run_lengths, unique_items, run_lengths, num_valid_runs);\n\n    // Init the BlockRunLengthDecode and get the total decoded size of this block's tile (i.e., the\n    // \"decompressed\" size)\n    uint32_t decoded_size = 0U;\n    BlockRunLengthDecodeT run_length_decode =\n      InitBlockRunLengthDecode(unique_items, run_lengths, decoded_size, cuda::std::bool_constant<TEST_RUN_OFFSETS_>{});\n\n    // Run-length decode (\"decompress\") the runs into a window buffer of limited size. This is\n    // repeated until all runs have been decoded.\n    uint32_t decoded_window_offset = 0U;\n    while (decoded_window_offset < decoded_size)\n    {\n      RunLengthT relative_offsets[DECODED_ITEMS_PER_THREAD];\n      RunItemT decoded_items[DECODED_ITEMS_PER_THREAD];\n\n      // The number of decoded items that are valid within this window (aka pass) of run-length\n      // decoding\n      uint32_t num_valid_items = decoded_size - decoded_window_offset;\n      run_length_decode.RunLengthDecode(decoded_items, relative_offsets, decoded_window_offset);\n\n      BlockStoreDecodedItemT(temp_storage.decode.store_decoded_runs_storage)\n        .Store(d_block_decoded_out + decoded_window_offset, decoded_items, num_valid_items);\n\n      if (TEST_RELATIVE_OFFSETS)\n      {\n        BlockStoreRelativeOffsetT(temp_storage.decode.store_relative_offsets)\n          .Store(d_block_rel_out + decoded_window_offset, relative_offsets, num_valid_items);\n      }\n\n      decoded_window_offset += DECODED_ITEMS_PER_THREAD * BLOCK_THREADS;\n    }\n    return decoded_size;\n  }\n};\n\n/******************************************************************************\n * [STAGE 1] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename OffsetT,\n          typename DecodedSizesOutT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__ void BlockRunLengthDecodeGetSizeKernel(\n  const ItemItT d_unique_items,\n  const RunLengthsItT d_run_lengths,\n  const OffsetT num_runs,\n  DecodedSizesOutT d_decoded_sizes)\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset) : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  uint64_t num_decoded_items =\n    run_length_decode_agent.GetDecodedSize(d_unique_items + block_offset, d_run_lengths + block_offset, num_valid_runs);\n\n  d_decoded_sizes[blockIdx.x] = num_decoded_items;\n}\n\n/******************************************************************************\n * [STAGE 2] RUN-LENGTH DECODING TEST KERNEL\n ******************************************************************************/\ntemplate <typename AgentTestBlockRunLengthDecode,\n          typename ItemItT,\n          typename RunLengthsItT,\n          typename DecodedSizesOutT,\n          typename OffsetT,\n          typename DecodedItemsOutItT,\n          typename RelativeOffsetOutItT>\n__launch_bounds__(AgentTestBlockRunLengthDecode::BLOCK_THREADS) __global__ void BlockRunLengthDecodeTestKernel(\n  const ItemItT d_unique_items,\n  const RunLengthsItT d_run_lengths,\n  const DecodedSizesOutT d_decoded_offsets,\n  const OffsetT num_runs,\n  DecodedItemsOutItT d_decoded_items,\n  RelativeOffsetOutItT d_relative_offsets)\n\n{\n  constexpr OffsetT RUNS_PER_BLOCK = AgentTestBlockRunLengthDecode::RUNS_PER_BLOCK;\n\n  __shared__ typename AgentTestBlockRunLengthDecode::TempStorage temp_storage;\n\n  OffsetT block_offset   = blockIdx.x * RUNS_PER_BLOCK;\n  OffsetT num_valid_runs = (block_offset + RUNS_PER_BLOCK >= num_runs) ? (num_runs - block_offset) : RUNS_PER_BLOCK;\n\n  AgentTestBlockRunLengthDecode run_length_decode_agent(temp_storage);\n  run_length_decode_agent.WriteDecodedRuns(\n    d_unique_items + block_offset,\n    d_run_lengths + block_offset,\n    d_decoded_items + d_decoded_offsets[blockIdx.x],\n    d_relative_offsets + d_decoded_offsets[blockIdx.x],\n    num_valid_runs);\n}\n\nstruct ModOp\n{\n  using T = uint32_t;\n  __host__ __device__ __forceinline__ T operator()(const T& x) const\n  {\n    return 1 + (x % 100);\n  }\n};\n\ntemplate <uint32_t RUNS_PER_THREAD,\n          uint32_t DECODED_ITEMS_PER_THREAD,\n          uint32_t BLOCK_DIM_X,\n          uint32_t BLOCK_DIM_Y,\n          uint32_t BLOCK_DIM_Z,\n          bool TEST_RUN_OFFSETS,\n          bool TEST_RELATIVE_OFFSETS>\nvoid TestAlgorithmSpecialisation()\n{\n  constexpr uint32_t THREADS_PER_BLOCK = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z;\n  constexpr uint32_t RUNS_PER_BLOCK    = RUNS_PER_THREAD * THREADS_PER_BLOCK;\n\n  using RunItemT      = float;\n  using RunLengthT    = uint32_t;\n  using ItemItT       = thrust::counting_iterator<RunItemT>;\n  using RunLengthsItT = cuda::transform_iterator<ModOp, cuda::counting_iterator<RunLengthT>>;\n\n  ItemItT d_unique_items(1000U);\n  RunLengthsItT d_run_lengths(cuda::counting_iterator<RunLengthT>(0), ModOp{});\n\n  constexpr uint32_t num_runs   = 10000;\n  constexpr uint32_t num_blocks = (num_runs + (RUNS_PER_BLOCK - 1U)) / RUNS_PER_BLOCK;\n\n  size_t temp_storage_bytes      = 0ULL;\n  void* temp_storage             = nullptr;\n  uint32_t* h_num_decoded_total  = nullptr;\n  uint32_t* d_decoded_sizes      = nullptr;\n  uint32_t* d_decoded_offsets    = nullptr;\n  RunItemT* d_decoded_out        = nullptr;\n  RunLengthT* d_relative_offsets = nullptr;\n  RunItemT* h_decoded_out        = nullptr;\n  RunLengthT* h_relative_offsets = nullptr;\n\n  using AgentTestBlockRunLengthDecodeT = AgentTestBlockRunLengthDecode<\n    ItemItT,\n    RunLengthsItT,\n    RUNS_PER_THREAD,\n    DECODED_ITEMS_PER_THREAD,\n    TEST_RELATIVE_OFFSETS,\n    TEST_RUN_OFFSETS,\n    THREADS_PER_BLOCK,\n    1,\n    1>;\n\n  enum : uint32_t\n  {\n    TIMER_SIZE_BEGIN = 0,\n    TIMER_SIZE_END,\n    TIMER_DECODE_BEGIN,\n    TIMER_DECODE_END,\n    NUM_TIMERS,\n  };\n\n  // Get temporary storage requirements for the scan (for computing offsets for the per-block\n  // run-length decoded items)\n  hipcub::DeviceScan::InclusiveSum(nullptr, temp_storage_bytes, d_decoded_sizes, d_decoded_offsets, num_blocks);\n\n  // Allocate device memory\n  CubDebugExit(hipMalloc(&temp_storage, temp_storage_bytes));\n  CubDebugExit(hipMalloc(&d_decoded_sizes, num_blocks * sizeof(*d_decoded_sizes)));\n  // Allocate for the exclusive sum PLUS the overall aggregate\n  CubDebugExit(hipMalloc(&d_decoded_offsets, (num_blocks + 1) * sizeof(*d_decoded_offsets)));\n  CubDebugExit(hipHostMalloc(&h_num_decoded_total, sizeof(*h_num_decoded_total)));\n\n  // Get the per-block number of items being decoded (i-th thread block writing size to\n  // d_decoded_sizes[i])\n  BlockRunLengthDecodeGetSizeKernel<AgentTestBlockRunLengthDecodeT>\n    <<<num_blocks, THREADS_PER_BLOCK, 0U>>>(d_unique_items, d_run_lengths, num_runs, d_decoded_sizes);\n\n  // Compute offsets for the runs decoded by each block (exclusive sum + aggregate)\n  CubDebugExit(hipMemsetAsync(d_decoded_offsets, 0, sizeof(d_decoded_offsets[0])));\n  CubDebugExit(hipcub::DeviceScan::InclusiveSum(\n    temp_storage, temp_storage_bytes, d_decoded_sizes, &d_decoded_offsets[1], num_blocks));\n\n  // Copy the total decoded size to CPU in order to allocate just the right amount of device memory\n  CubDebugExit(hipMemcpy(\n    h_num_decoded_total, &d_decoded_offsets[num_blocks], sizeof(*h_num_decoded_total), hipMemcpyDeviceToHost));\n\n  // Allocate device memory for the run-length decoded output\n  CubDebugExit(hipHostMalloc(&h_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  CubDebugExit(hipMalloc(&d_decoded_out, (*h_num_decoded_total) * sizeof(RunItemT)));\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(hipMalloc(&d_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n    CubDebugExit(hipHostMalloc(&h_relative_offsets, (*h_num_decoded_total) * sizeof(RunLengthT)));\n  }\n\n  // Perform the block-wise run-length decoding (each block taking its offset from\n  // d_decoded_offsets)\n  BlockRunLengthDecodeTestKernel<AgentTestBlockRunLengthDecodeT><<<num_blocks, THREADS_PER_BLOCK, 0U>>>(\n    d_unique_items, d_run_lengths, d_decoded_offsets, num_runs, d_decoded_out, d_relative_offsets);\n\n  // Copy back results for verification\n  CubDebugExit(\n    hipMemcpy(h_decoded_out, d_decoded_out, (*h_num_decoded_total) * sizeof(*h_decoded_out), hipMemcpyDeviceToHost));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    // Copy back the relative offsets\n    CubDebugExit(hipMemcpy(\n      h_relative_offsets,\n      d_relative_offsets,\n      (*h_num_decoded_total) * sizeof(*h_relative_offsets),\n      hipMemcpyDeviceToHost));\n  }\n\n  // Generate host-side run-length decoded data for verification\n  std::vector<std::pair<RunItemT, RunLengthT>> host_golden;\n  host_golden.reserve(*h_num_decoded_total);\n  for (uint32_t run = 0; run < num_runs; run++)\n  {\n    for (RunLengthT i = 0; i < d_run_lengths[run]; i++)\n    {\n      host_golden.emplace_back(d_unique_items[run], i);\n    }\n  }\n\n  // Verify the total run-length decoded size is correct\n  REQUIRE(host_golden.size() == h_num_decoded_total[0]);\n\n  // Verify the run-length decoded data is correct\n  bool cmp_eq = true;\n  for (uint32_t i = 0; i < host_golden.size(); i++)\n  {\n    if (host_golden[i].first != h_decoded_out[i])\n    {\n      FAIL(\"Mismatch at #\" << i << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i] << \"\\n\");\n      cmp_eq = false;\n    }\n    if (TEST_RELATIVE_OFFSETS)\n    {\n      if (host_golden[i].second != h_relative_offsets[i])\n      {\n        FAIL(\"Mismatch of relative offset at #\"\n             << i << \": CPU item: \" << host_golden[i].first << \", GPU: \" << h_decoded_out[i]\n             << \"; relative offsets: CPU: \" << host_golden[i].second << \", GPU: \" << h_relative_offsets[i] << \"\\n\");\n        cmp_eq = false;\n        break;\n      }\n    }\n  }\n\n  REQUIRE(cmp_eq == true);\n\n  // Clean up memory allocations\n  CubDebugExit(hipFree(temp_storage));\n  CubDebugExit(hipFree(d_decoded_sizes));\n  CubDebugExit(hipFree(d_decoded_offsets));\n  CubDebugExit(hipFree(d_decoded_out));\n  CubDebugExit(hipHostFree(h_num_decoded_total));\n  CubDebugExit(hipHostFree(h_decoded_out));\n\n  if (TEST_RELATIVE_OFFSETS)\n  {\n    CubDebugExit(hipFree(d_relative_offsets));\n    CubDebugExit(hipHostFree(h_relative_offsets));\n  }\n}\n\nconstexpr bool DO_TEST_RELATIVE_OFFSETS     = true;\nconstexpr bool DO_NOT_TEST_RELATIVE_OFFSETS = false;\n\nconstexpr bool TEST_WITH_RUN_OFFSETS = true;\nconstexpr bool TEST_WITH_RUN_LENGTHS = false;\n\ntemplate <int RunsPerThread, int DecodedItemsPerThread, int BlockDimX, int BlockDimY = 1, int BlockDimZ = 1>\nstruct params_t\n{\n  static constexpr int runs_per_thread          = RunsPerThread;\n  static constexpr int decoded_items_per_thread = DecodedItemsPerThread;\n  static constexpr int block_dim_x              = BlockDimX;\n  static constexpr int block_dim_y              = BlockDimY;\n  static constexpr int block_dim_z              = BlockDimZ;\n};\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run lengths and offsets relative to each run\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run lengths and performs normal run-length \"\n  \"decoding\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_LENGTHS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run offsets and generates offsets relative to \"\n  \"each run\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_TEST_RELATIVE_OFFSETS>();\n}\n\nC2H_TEST_LIST(\n  \"Block Run Length Decode works with run offsets and performs normal run-length \"\n  \"decoding\",\n  \"[rld][block]\",\n  params_t<1, 1, 64>,\n  params_t<1, 3, 32, 2, 3>,\n  params_t<1, 1, 128>,\n  params_t<1, 8, 128>,\n  params_t<3, 1, 256>,\n  params_t<1, 8, 256>,\n  params_t<8, 1, 256>,\n  params_t<1, 1, 256>,\n  params_t<2, 2, 384>)\n{\n  using params = TestType;\n\n  TestAlgorithmSpecialisation<params::runs_per_thread,\n                              params::decoded_items_per_thread,\n                              params::block_dim_x,\n                              params::block_dim_y,\n                              params::block_dim_z,\n                              TEST_WITH_RUN_OFFSETS,\n                              DO_NOT_TEST_RELATIVE_OFFSETS>();\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_run_length_decode.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/warp/warp_scan.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/std/__algorithm/fill.h>\n#include <cuda/std/__algorithm/max.h>\n#include <cuda/std/__numeric/inclusive_scan.h>\n#include <cuda/std/__numeric/iota.h>\n\n#include <c2h/catch2_test_helper.h>\n\nconstexpr int num_warps = 4;\n\nstruct max_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    return cuda::std::max(i, j);\n  }\n};\n\nstruct sum_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    return i + j;\n  }\n};\n\n// example-begin inclusive-warp-scan-init-value\n__global__ void InclusiveWarpScanKernel(int* output)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = cub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int initial_value = 3;\n  int thread_data   = threadIdx.x % 32 + warp_id;\n\n  // warp #0 input: {0, 1, 2, 3, ..., 31}\n  // warp #1 input: {1, 2, 3, 4, ..., 32}\n  // warp #2 input: {2, 3, 4, 5, ..., 33}\n  // warp #3 input: {3, 4, 5, 6, ..., 34}\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id]).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{});\n\n  // initial value = 3 (for each warp)\n  // warp #0 output: {3, 3, 3, 3, ..., 31}\n  // warp #1 output: {3, 3, 3, 4, ..., 32}\n  // warp #2 output: {3, 3, 4, 5, ..., 33}\n  // warp #3 output: {3, 4, 5, 6, ..., 34}\n  output[threadIdx.x] = thread_data;\n}\n// example-end inclusive-warp-scan-init-value\n\nC2H_TEST(\"Warp array-based inclusive scan works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n\n  InclusiveWarpScanKernel<<<1, num_warps * 32>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start = expected.begin() + i * 32;\n    auto end   = start + 32;\n\n    cuda::std::iota(start, end, i); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, end, start, max_op{}, 3);\n  }\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-warp-scan-init-value-aggregate\n__global__ void InclusiveWarpScanKernelAggr(int* output, int* d_warp_aggregate)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = cub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int initial_value = 3; // for each warp\n  int thread_data   = 1;\n  int warp_aggregate;\n\n  // warp #0 input: {1, 1, 1, 1, ..., 1}\n  // warp #1 input: {1, 1, 1, 1, ..., 1}\n  // warp #2 input: {1, 1, 1, 1, ..., 1}\n  // warp #3 input: {1, 1, 1, 1, ..., 1}\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScan(thread_data, thread_data, initial_value, cuda::std::plus<>{}, warp_aggregate);\n\n  // warp #1 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #2 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #0 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #3 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n\n  output[threadIdx.x]       = thread_data;\n  d_warp_aggregate[warp_id] = warp_aggregate;\n}\n// example-end inclusive-warp-scan-init-value-aggregate\n\nC2H_TEST(\"Warp array-based inclusive scan aggregate works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n  c2h::device_vector<int> d_warp_aggregate(num_warps);\n\n  InclusiveWarpScanKernelAggr<<<1, num_warps * 32>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_warp_aggregate.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  c2h::host_vector<int> expected_aggr{};\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start   = expected.begin() + i * 32;\n    auto end     = start + 32;\n    int init_val = 3;\n\n    cuda::std::fill(start, end, 1); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, end, start, sum_op{}, init_val);\n\n    expected_aggr.push_back(expected[i * 32 + 31] - init_val); // warp aggregate does not take\n                                                               // initial value into account\n  }\n\n  REQUIRE(expected == d_out);\n  REQUIRE(expected_aggr == d_warp_aggregate);\n}\n\n// example-begin inclusive-warp-scan-init-value-partial\nstruct custom_max_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    // Values above 31 are invalid\n    assert(cuda::std::abs(i) < 32);\n    assert(cuda::std::abs(j) < 32);\n    return cuda::std::max(i, j);\n  }\n};\n\n__global__ void InclusiveWarpScanPartialKernel(int* output)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = cub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id = threadIdx.x / 32;\n\n  int initial_value = 3;\n  int thread_data   = threadIdx.x % 32 + warp_id;\n  if (threadIdx.x % 2 == 1)\n  {\n    thread_data = -thread_data;\n  }\n  int valid_items = 40 - warp_id * 16;\n\n  // warp #0 input: {0, -1, 2, -3, ..., 28, -29, 30, -31}, valid_items: 40 (effectively 32)\n  // warp #1 input: {1, -2, 3, -4, ..., 29, -30, 31, -32}, valid_items: 24\n  // warp #2 input: {2, -3, 4, -5, ..., 30, -31, 32, -33}, valid_items:  8\n  // warp #3 input: {3, -4, 5, -6, ..., 31, -32, 33, -34}, valid_items: -8 (effectively  0)\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScanPartial(thread_data, thread_data, initial_value, custom_max_op{}, valid_items);\n\n  // initial value = 3 (for each warp)\n  // warp #0 output: {3,  3, 3,  3, ...,                       28,  28, 30,  30}\n  // warp #1 output: {3,  3, 3,  3, ..., 23, 23, 25, -26, ..., 29,  29, 31, -32}\n  // warp #2 output: {3,  3, 4,  4, ...,  8,  8, 10, -11, ..., 30,  30, 32, -33}\n  // warp #3 output: {3, -4, 5, -6, ...,                       31, -32, 33, -34}\n  output[threadIdx.x] = thread_data;\n}\n// example-end inclusive-warp-scan-init-value-partial\n\nC2H_TEST(\"Warp array-based partial inclusive scan works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n\n  InclusiveWarpScanPartialKernel<<<1, num_warps * 32>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start      = expected.begin() + i * 32;\n    auto end        = start + 32;\n    int valid_items = cuda::std::clamp(40 - i * 16, 0, 32);\n\n    cuda::std::iota(start, end, i); // initialize host input for every warp\n    cuda::std::transform(start, end, start, [i](int val) {\n      if ((val - i) % 2 == 1)\n      {\n        val = -val;\n      }\n      return val;\n    });\n\n    cuda::std::inclusive_scan(start, start + valid_items, start, max_op{}, 3);\n  }\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-warp-scan-init-value-aggregate-partial\nstruct custom_sum_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    // Values above 31 are invalid\n    assert(cuda::std::abs(i) < 32);\n    assert(cuda::std::abs(j) < 32);\n    return i + j;\n  }\n};\n\n__global__ void InclusiveWarpScanPartialKernelAggr(int* output, int* d_warp_aggregate)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = cub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int lane_id       = threadIdx.x % 32;\n  int initial_value = 3; // for each warp\n  int thread_data   = (lane_id < num_warps - 1 - warp_id) ? 0 : 1;\n  int valid_items   = 40 - warp_id * 16;\n  int warp_aggregate;\n\n  // warp #0 input: {0, 0, 0, 1, ..., 1}, valid_items: 40 (effectively 32)\n  // warp #1 input: {0, 0, 1, 1, ..., 1}, valid_items: 24\n  // warp #2 input: {0, 1, 1, 1, ..., 1}, valid_items:  8\n  // warp #4 input: {1, 1, 1, 1, ..., 1}, valid_items: -8 (effectively 0)\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScanPartial(thread_data, thread_data, initial_value, custom_sum_op{}, valid_items, warp_aggregate);\n\n  // warp #1 output: {3, 3, 3, 4, ...,       29, 30, 31, 32} - warp aggregate: 29\n  // warp #2 output: {3, 3, 4, 5, ...,    24, 25, 1, ..., 1} - warp aggregate: 22\n  // warp #0 output: {3, 4, 5, 6, ...,     9, 10, 1, ..., 1} - warp aggregate:  7\n  // warp #3 output: {1, 1, 1, 1, ...,                    1} - warp aggregate:  ?\n\n  output[threadIdx.x]       = thread_data;\n  d_warp_aggregate[warp_id] = warp_aggregate;\n}\n// example-end inclusive-warp-scan-init-value-aggregate-partial\n\nC2H_TEST(\"Warp array-based partial inclusive scan aggregate works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n  c2h::device_vector<int> d_warp_aggregate(num_warps);\n\n  InclusiveWarpScanPartialKernelAggr<<<1, num_warps * 32>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_warp_aggregate.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  c2h::host_vector<int> expected_aggr{};\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start      = expected.begin() + i * 32;\n    auto end        = start + 32;\n    int valid_items = cuda::std::clamp(40 - i * 16, 0, 32);\n    int init_val    = 3;\n\n    cuda::std::fill(start + num_warps - 1 - i, end, 1); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, start + valid_items, start, sum_op{}, init_val);\n\n    expected_aggr.push_back(expected[i * 32 + valid_items - 1] - init_val); // warp aggregate does not take\n                                                                            // initial value into account\n  }\n\n  // Last aggregate is undefined.\n  expected_aggr.back() = d_warp_aggregate.back();\n  REQUIRE(expected == d_out);\n  REQUIRE(expected_aggr == d_warp_aggregate);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/warp/warp_scan.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/std/__algorithm/fill.h>\n#include <cuda/std/__algorithm/max.h>\n#include <cuda/std/__numeric/inclusive_scan.h>\n#include <cuda/std/__numeric/iota.h>\n\n#include <c2h/catch2_test_helper.h>\n\nconstexpr int num_warps = 4;\n\nstruct max_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    return cuda::std::max(i, j);\n  }\n};\n\nstruct sum_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    return i + j;\n  }\n};\n\n// example-begin inclusive-warp-scan-init-value\n__global__ void InclusiveWarpScanKernel(int* output)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = hipcub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int initial_value = 3;\n  int thread_data   = threadIdx.x % 32 + warp_id;\n\n  // warp #0 input: {0, 1, 2, 3, ..., 31}\n  // warp #1 input: {1, 2, 3, 4, ..., 32}\n  // warp #2 input: {2, 3, 4, 5, ..., 33}\n  // warp #3 input: {3, 4, 5, 6, ..., 34}\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id]).InclusiveScan(thread_data, thread_data, initial_value, cuda::maximum<>{});\n\n  // initial value = 3 (for each warp)\n  // warp #0 output: {3, 3, 3, 3, ..., 31}\n  // warp #1 output: {3, 3, 3, 4, ..., 32}\n  // warp #2 output: {3, 3, 4, 5, ..., 33}\n  // warp #3 output: {3, 4, 5, 6, ..., 34}\n  output[threadIdx.x] = thread_data;\n}\n// example-end inclusive-warp-scan-init-value\n\nC2H_TEST(\"Warp array-based inclusive scan works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n\n  InclusiveWarpScanKernel<<<1, num_warps * 32>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start = expected.begin() + i * 32;\n    auto end   = start + 32;\n\n    cuda::std::iota(start, end, i); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, end, start, max_op{}, 3);\n  }\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-warp-scan-init-value-aggregate\n__global__ void InclusiveWarpScanKernelAggr(int* output, int* d_warp_aggregate)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = hipcub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int initial_value = 3; // for each warp\n  int thread_data   = 1;\n  int warp_aggregate;\n\n  // warp #0 input: {1, 1, 1, 1, ..., 1}\n  // warp #1 input: {1, 1, 1, 1, ..., 1}\n  // warp #2 input: {1, 1, 1, 1, ..., 1}\n  // warp #3 input: {1, 1, 1, 1, ..., 1}\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScan(thread_data, thread_data, initial_value, cuda::std::plus<>{}, warp_aggregate);\n\n  // warp #1 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #2 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #0 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n  // warp #3 output: {4, 5, 6, 7, ..., 35} - warp aggregate: 32\n\n  output[threadIdx.x]       = thread_data;\n  d_warp_aggregate[warp_id] = warp_aggregate;\n}\n// example-end inclusive-warp-scan-init-value-aggregate\n\nC2H_TEST(\"Warp array-based inclusive scan aggregate works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n  c2h::device_vector<int> d_warp_aggregate(num_warps);\n\n  InclusiveWarpScanKernelAggr<<<1, num_warps * 32>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_warp_aggregate.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  c2h::host_vector<int> expected_aggr{};\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start   = expected.begin() + i * 32;\n    auto end     = start + 32;\n    int init_val = 3;\n\n    cuda::std::fill(start, end, 1); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, end, start, sum_op{}, init_val);\n\n    expected_aggr.push_back(expected[i * 32 + 31] - init_val); // warp aggregate does not take\n                                                               // initial value into account\n  }\n\n  REQUIRE(expected == d_out);\n  REQUIRE(expected_aggr == d_warp_aggregate);\n}\n\n// example-begin inclusive-warp-scan-init-value-partial\nstruct custom_max_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    // Values above 31 are invalid\n    assert(cuda::std::abs(i) < 32);\n    assert(cuda::std::abs(j) < 32);\n    return cuda::std::max(i, j);\n  }\n};\n\n__global__ void InclusiveWarpScanPartialKernel(int* output)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = hipcub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id = threadIdx.x / 32;\n\n  int initial_value = 3;\n  int thread_data   = threadIdx.x % 32 + warp_id;\n  if (threadIdx.x % 2 == 1)\n  {\n    thread_data = -thread_data;\n  }\n  int valid_items = 40 - warp_id * 16;\n\n  // warp #0 input: {0, -1, 2, -3, ..., 28, -29, 30, -31}, valid_items: 40 (effectively 32)\n  // warp #1 input: {1, -2, 3, -4, ..., 29, -30, 31, -32}, valid_items: 24\n  // warp #2 input: {2, -3, 4, -5, ..., 30, -31, 32, -33}, valid_items:  8\n  // warp #3 input: {3, -4, 5, -6, ..., 31, -32, 33, -34}, valid_items: -8 (effectively  0)\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScanPartial(thread_data, thread_data, initial_value, custom_max_op{}, valid_items);\n\n  // initial value = 3 (for each warp)\n  // warp #0 output: {3,  3, 3,  3, ...,                       28,  28, 30,  30}\n  // warp #1 output: {3,  3, 3,  3, ..., 23, 23, 25, -26, ..., 29,  29, 31, -32}\n  // warp #2 output: {3,  3, 4,  4, ...,  8,  8, 10, -11, ..., 30,  30, 32, -33}\n  // warp #3 output: {3, -4, 5, -6, ...,                       31, -32, 33, -34}\n  output[threadIdx.x] = thread_data;\n}\n// example-end inclusive-warp-scan-init-value-partial\n\nC2H_TEST(\"Warp array-based partial inclusive scan works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n\n  InclusiveWarpScanPartialKernel<<<1, num_warps * 32>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start      = expected.begin() + i * 32;\n    auto end        = start + 32;\n    int valid_items = cuda::std::clamp(40 - i * 16, 0, 32);\n\n    cuda::std::iota(start, end, i); // initialize host input for every warp\n    cuda::std::transform(start, end, start, [i](int val) {\n      if ((val - i) % 2 == 1)\n      {\n        val = -val;\n      }\n      return val;\n    });\n\n    cuda::std::inclusive_scan(start, start + valid_items, start, max_op{}, 3);\n  }\n\n  REQUIRE(expected == d_out);\n}\n\n// example-begin inclusive-warp-scan-init-value-aggregate-partial\nstruct custom_sum_op\n{\n  __host__ __device__ int operator()(int i, int j)\n  {\n    // Values above 31 are invalid\n    assert(cuda::std::abs(i) < 32);\n    assert(cuda::std::abs(j) < 32);\n    return i + j;\n  }\n};\n\n__global__ void InclusiveWarpScanPartialKernelAggr(int* output, int* d_warp_aggregate)\n{\n  // Specialize WarpScan for type int\n  using warp_scan_t = hipcub::WarpScan<int>;\n  // Allocate WarpScan shared memory for 4 warps\n  __shared__ typename warp_scan_t::TempStorage temp_storage[num_warps];\n\n  int warp_id       = threadIdx.x / 32;\n  int lane_id       = threadIdx.x % 32;\n  int initial_value = 3; // for each warp\n  int thread_data   = (lane_id < num_warps - 1 - warp_id) ? 0 : 1;\n  int valid_items   = 40 - warp_id * 16;\n  int warp_aggregate;\n\n  // warp #0 input: {0, 0, 0, 1, ..., 1}, valid_items: 40 (effectively 32)\n  // warp #1 input: {0, 0, 1, 1, ..., 1}, valid_items: 24\n  // warp #2 input: {0, 1, 1, 1, ..., 1}, valid_items:  8\n  // warp #4 input: {1, 1, 1, 1, ..., 1}, valid_items: -8 (effectively 0)\n\n  // Collectively compute the warp-wide inclusive prefix max scan\n  warp_scan_t(temp_storage[warp_id])\n    .InclusiveScanPartial(thread_data, thread_data, initial_value, custom_sum_op{}, valid_items, warp_aggregate);\n\n  // warp #1 output: {3, 3, 3, 4, ...,       29, 30, 31, 32} - warp aggregate: 29\n  // warp #2 output: {3, 3, 4, 5, ...,    24, 25, 1, ..., 1} - warp aggregate: 22\n  // warp #0 output: {3, 4, 5, 6, ...,     9, 10, 1, ..., 1} - warp aggregate:  7\n  // warp #3 output: {1, 1, 1, 1, ...,                    1} - warp aggregate:  ?\n\n  output[threadIdx.x]       = thread_data;\n  d_warp_aggregate[warp_id] = warp_aggregate;\n}\n// example-end inclusive-warp-scan-init-value-aggregate-partial\n\nC2H_TEST(\"Warp array-based partial inclusive scan aggregate works with initial value\", \"[scan][warp]\")\n{\n  c2h::device_vector<int> d_out(num_warps * 32);\n  c2h::device_vector<int> d_warp_aggregate(num_warps);\n\n  InclusiveWarpScanPartialKernelAggr<<<1, num_warps * 32>>>(\n    thrust::raw_pointer_cast(d_out.data()), thrust::raw_pointer_cast(d_warp_aggregate.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected(d_out.size());\n  c2h::host_vector<int> expected_aggr{};\n\n  for (int i = 0; i < num_warps; ++i)\n  {\n    auto start      = expected.begin() + i * 32;\n    auto end        = start + 32;\n    int valid_items = cuda::std::clamp(40 - i * 16, 0, 32);\n    int init_val    = 3;\n\n    cuda::std::fill(start + num_warps - 1 - i, end, 1); // initialize host input for every warp\n\n    cuda::std::inclusive_scan(start, start + valid_items, start, sum_op{}, init_val);\n\n    expected_aggr.push_back(expected[i * 32 + valid_items - 1] - init_val); // warp aggregate does not take\n                                                                            // initial value into account\n  }\n\n  // Last aggregate is undefined.\n  expected_aggr.back() = d_warp_aggregate.back();\n  REQUIRE(expected == d_out);\n  REQUIRE(expected_aggr == d_warp_aggregate);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_warp_scan_api.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_scan.cuh>\n\n#include <climits>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <cub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_scan_kernel(T* in, T* out, ActionT action)\n{\n  using block_scan_t = cub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t    = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncthreads();\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = thread_data[item];\n  }\n}\n\ntemplate <cub::BlockScanAlgorithm Algorithm, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\n__global__ void block_scan_single_kernel(T* in, T* out, ActionT action)\n{\n  using block_scan_t = cub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t    = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  const int tid = static_cast<int>(cub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n\n  T thread_data = in[tid];\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <cub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_scan(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <cub::BlockScanAlgorithm Algorithm, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\nvoid block_scan_single(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_single_kernel<Algorithm, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <int ItemsPerThread, class BlockScanT, class T>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct sum_single_op_t\n{\n  template <class BlockScanT, class T>\n  __device__ void operator()(BlockScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, int (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T* m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, block_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, block_aggregate);\n    }\n\n    const int tid = cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_prefix_op_t\n{\n  T m_prefix;\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : T{};\n      prefix   = prefix + block_aggregate;\n      return retval;\n    }\n  };\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_prefix_op_t\n{\n  T m_prefix;\n  static constexpr T min_identity = cuda::std::numeric_limits<T>::max();\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : min_identity;\n      prefix   = cuda::minimum<>{}(prefix, block_aggregate);\n      return retval;\n    }\n  };\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(cub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{}, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{}, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, class ScanOpT>\nT host_scan(scan_mode mode, c2h::host_vector<T>& result, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return {};\n  }\n\n  T accumulator       = static_cast<T>(scan_op(initial_value, result[0]));\n  T block_accumulator = result[0];\n\n  if (mode == scan_mode::exclusive)\n  {\n    result[0] = initial_value;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      T tmp             = result[i];\n      result[i]         = accumulator;\n      accumulator       = static_cast<T>(scan_op(accumulator, tmp));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, tmp));\n    }\n  }\n  else\n  {\n    result[0] = accumulator;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      accumulator       = static_cast<T>(scan_op(accumulator, result[i]));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, result[i]));\n      result[i]         = accumulator;\n    }\n  }\n\n  return block_accumulator;\n}\n\n// %PARAM% ALGO_TYPE alg 0:1:2\n// %PARAM% TEST_MODE mode 0:1\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n// FIXME(bgruber): uchar3 fails the test, see #3835\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  /*uchar3,*/ short2>;\nusing block_dim_x            = c2h::enum_type_list<int, 17, 32, 65, 96>;\nusing block_dim_yz           = c2h::enum_type_list<int, 1, 2>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 9>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithms =\n  c2h::enum_type_list<cub::BlockScanAlgorithm,\n                      cub::BlockScanAlgorithm::BLOCK_SCAN_RAKING,\n                      cub::BlockScanAlgorithm::BLOCK_SCAN_WARP_SCANS,\n                      cub::BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE>;\nusing algorithm = c2h::enum_type_list<cub::BlockScanAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\n#if TEST_MODE == 0\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::inclusive>;\n#else\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive>;\n#endif\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int block_dim_x      = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int items_per_thread = c2h::get<3, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr cub::BlockScanAlgorithm algorithm = c2h::get<4, TestType>::value;\n  static constexpr scan_mode mode                    = c2h::get<5, TestType>::value;\n};\n\nC2H_TEST(\n  \"Block scan works with sum\", \"[scan][block]\", types, block_dim_x, block_dim_yz, items_per_thread, algorithm, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<params::algorithm, params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Block scan works with sum single\",\n         \"[scan][block]\",\n         types,\n         block_dim_x,\n         block_dim_yz,\n         single_item_per_thread,\n         algorithm,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan_single<params::algorithm, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_in, d_out, sum_single_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Block scan works with vec types\", \"[scan][block]\", vec_types, algorithm, modes)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 256;\n  constexpr int block_dim_y                   = 1;\n  constexpr int block_dim_z                   = 1;\n  constexpr int tile_size                     = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<1, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<2, TestType>::value;\n\n  using type = typename c2h::get<0, TestType>;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, sum_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan works with custom types\", \"[scan][block]\", algorithm, modes)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 256;\n  constexpr int block_dim_y                   = 1;\n  constexpr int block_dim_z                   = 1;\n  constexpr int tile_size                     = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, sum_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan returns valid block aggregate\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  c2h::device_vector<type> d_block_aggregate(1);\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, sum_aggregate_op_t<type, mode>{target_thread_id, thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n  type block_aggregate         = host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(block_aggregate == d_block_aggregate[0]);\n}\n\nC2H_TEST(\"Block scan supports prefix op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, sum_prefix_op_t<type, mode>{prefix});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{}, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan supports custom scan op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, min_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    INT_MAX);\n\n  if constexpr (mode == scan_mode::exclusive)\n  {\n    //! With no initial value, the output computed for *thread*\\ :sub:`0` is undefined.\n    d_out.erase(d_out.begin());\n    h_out.erase(h_out.begin());\n  }\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block custom op scan works with initial value\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, tile_size))));\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, min_init_value_op_t<type, mode>{initial_value});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block custom op scan with initial value returns valid block aggregate\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, tile_size))));\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  c2h::device_vector<type> d_block_aggregate(1);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type, mode>{\n      target_thread_id, initial_value, thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n  type h_block_aggregate       = host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_block_aggregate == d_block_aggregate[0]);\n}\n\nC2H_TEST(\"Block scan supports prefix op and custom scan op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr cub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, min_prefix_op_t<type, mode>{prefix});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type a, type b) {\n      return std::min(a, b);\n    },\n    prefix);\n\n  REQUIRE(h_out == d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_scan.cuh>\n\n#include <climits>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\n__global__ void block_scan_kernel(T* in, T* out, ActionT action)\n{\n  using block_scan_t = hipcub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t    = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  T thread_data[ItemsPerThread];\n\n  const int tid           = static_cast<int>(hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n  const int thread_offset = tid * ItemsPerThread;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx     = thread_offset + item;\n    thread_data[item] = in[idx];\n  }\n  __syncthreads();\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    const int idx = thread_offset + item;\n    out[idx]      = thread_data[item];\n  }\n}\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\n__global__ void block_scan_single_kernel(T* in, T* out, ActionT action)\n{\n  using block_scan_t = hipcub::BlockScan<T, BlockDimX, Algorithm, BlockDimY, BlockDimZ>;\n  using storage_t    = typename block_scan_t::TempStorage;\n\n  __shared__ storage_t storage;\n\n  const int tid = static_cast<int>(hipcub::RowMajorTid(BlockDimX, BlockDimY, BlockDimZ));\n\n  T thread_data = in[tid];\n\n  block_scan_t scan(storage);\n\n  action(scan, thread_data);\n\n  out[tid] = thread_data;\n}\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm,\n          int ItemsPerThread,\n          int BlockDimX,\n          int BlockDimY,\n          int BlockDimZ,\n          class T,\n          class ActionT>\nvoid block_scan(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_kernel<Algorithm, ItemsPerThread, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <hipcub::BlockScanAlgorithm Algorithm, int BlockDimX, int BlockDimY, int BlockDimZ, class T, class ActionT>\nvoid block_scan_single(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  dim3 block_dims(BlockDimX, BlockDimY, BlockDimZ);\n\n  block_scan_single_kernel<Algorithm, BlockDimX, BlockDimY, BlockDimZ, T, ActionT>\n    <<<1, block_dims>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nenum class scan_mode\n{\n  exclusive,\n  inclusive\n};\n\ntemplate <scan_mode Mode>\nstruct sum_op_t\n{\n  template <int ItemsPerThread, class BlockScanT, class T>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct sum_single_op_t\n{\n  template <class BlockScanT, class T>\n  __device__ void operator()(BlockScanT& scan, T& thread_data) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_op_t\n{\n  T initial_value;\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <scan_mode Mode>\nstruct min_op_t\n{\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, int (&thread_data)[ItemsPerThread]) const\n  {\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{});\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_init_value_aggregate_op_t\n{\n  int m_target_thread_id;\n  T initial_value;\n  T* m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, block_aggregate);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, initial_value, cuda::minimum<>{}, block_aggregate);\n    }\n\n    const int tid = hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z);\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_aggregate_op_t\n{\n  int m_target_thread_id;\n  T* m_d_block_aggregate;\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    T block_aggregate{};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, block_aggregate);\n    }\n\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n\n    if (tid == m_target_thread_id)\n    {\n      *m_d_block_aggregate = block_aggregate;\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct sum_prefix_op_t\n{\n  T m_prefix;\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : T{};\n      prefix   = prefix + block_aggregate;\n      return retval;\n    }\n  };\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveSum(thread_data, thread_data, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveSum(thread_data, thread_data, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, scan_mode Mode>\nstruct min_prefix_op_t\n{\n  T m_prefix;\n  static constexpr T min_identity = cuda::std::numeric_limits<T>::max();\n\n  struct block_prefix_op_t\n  {\n    int linear_tid;\n    T prefix;\n\n    __device__ block_prefix_op_t(int linear_tid, T prefix)\n        : linear_tid(linear_tid)\n        , prefix(prefix)\n    {}\n\n    __device__ T operator()(T block_aggregate)\n    {\n      T retval = (linear_tid == 0) ? prefix : min_identity;\n      prefix   = cuda::minimum<>{}(prefix, block_aggregate);\n      return retval;\n    }\n  };\n\n  template <int ItemsPerThread, class BlockScanT>\n  __device__ void operator()(BlockScanT& scan, T (&thread_data)[ItemsPerThread]) const\n  {\n    const int tid = static_cast<int>(hipcub::RowMajorTid(blockDim.x, blockDim.y, blockDim.z));\n    block_prefix_op_t prefix_op{tid, m_prefix};\n\n    if constexpr (Mode == scan_mode::exclusive)\n    {\n      scan.ExclusiveScan(thread_data, thread_data, cuda::minimum<>{}, prefix_op);\n    }\n    else\n    {\n      scan.InclusiveScan(thread_data, thread_data, cuda::minimum<>{}, prefix_op);\n    }\n  }\n};\n\ntemplate <class T, class ScanOpT>\nT host_scan(scan_mode mode, c2h::host_vector<T>& result, ScanOpT scan_op, T initial_value = T{})\n{\n  if (result.empty())\n  {\n    return {};\n  }\n\n  T accumulator       = static_cast<T>(scan_op(initial_value, result[0]));\n  T block_accumulator = result[0];\n\n  if (mode == scan_mode::exclusive)\n  {\n    result[0] = initial_value;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      T tmp             = result[i];\n      result[i]         = accumulator;\n      accumulator       = static_cast<T>(scan_op(accumulator, tmp));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, tmp));\n    }\n  }\n  else\n  {\n    result[0] = accumulator;\n\n    for (std::size_t i = 1; i < result.size(); i++)\n    {\n      accumulator       = static_cast<T>(scan_op(accumulator, result[i]));\n      block_accumulator = static_cast<T>(scan_op(block_accumulator, result[i]));\n      result[i]         = accumulator;\n    }\n  }\n\n  return block_accumulator;\n}\n\n// %PARAM% ALGO_TYPE alg 0:1:2\n// %PARAM% TEST_MODE mode 0:1\n\nusing types = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n// FIXME(bgruber): uchar3 fails the test, see #3835\nusing vec_types = c2h::type_list<\n#if _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n  ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n  /*uchar3,*/ short2>;\nusing block_dim_x            = c2h::enum_type_list<int, 17, 32, 65, 96>;\nusing block_dim_yz           = c2h::enum_type_list<int, 1, 2>;\nusing items_per_thread       = c2h::enum_type_list<int, 1, 9>;\nusing single_item_per_thread = c2h::enum_type_list<int, 1>;\nusing algorithms =\n  c2h::enum_type_list<hipcub::BlockScanAlgorithm,\n                      hipcub::BlockScanAlgorithm::BLOCK_SCAN_RAKING,\n                      hipcub::BlockScanAlgorithm::BLOCK_SCAN_WARP_SCANS,\n                      hipcub::BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE>;\nusing algorithm = c2h::enum_type_list<hipcub::BlockScanAlgorithm, c2h::get<ALGO_TYPE, algorithms>::value>;\n\n#if TEST_MODE == 0\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::inclusive>;\n#else\nusing modes = c2h::enum_type_list<scan_mode, scan_mode::exclusive>;\n#endif\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int block_dim_x      = c2h::get<1, TestType>::value;\n  static constexpr int block_dim_y      = c2h::get<2, TestType>::value;\n  static constexpr int block_dim_z      = block_dim_y;\n  static constexpr int items_per_thread = c2h::get<3, TestType>::value;\n  static constexpr int tile_size        = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n\n  static constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<4, TestType>::value;\n  static constexpr scan_mode mode                    = c2h::get<5, TestType>::value;\n};\n\nC2H_TEST(\n  \"Block scan works with sum\", \"[scan][block]\", types, block_dim_x, block_dim_yz, items_per_thread, algorithm, modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<params::algorithm, params::items_per_thread, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_in, d_out, sum_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Block scan works with sum single\",\n         \"[scan][block]\",\n         types,\n         block_dim_x,\n         block_dim_yz,\n         single_item_per_thread,\n         algorithm,\n         modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_out(params::tile_size);\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan_single<params::algorithm, params::block_dim_x, params::block_dim_y, params::block_dim_z>(\n    d_in, d_out, sum_single_op_t<params::mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(params::mode, h_out, std::plus<type>{});\n\n  REQUIRE_APPROX_EQ(h_out, d_out);\n}\n\nC2H_TEST(\"Block scan works with vec types\", \"[scan][block]\", vec_types, algorithm, modes)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 256;\n  constexpr int block_dim_y                   = 1;\n  constexpr int block_dim_z                   = 1;\n  constexpr int tile_size                     = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<1, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<2, TestType>::value;\n\n  using type = typename c2h::get<0, TestType>;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, sum_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan works with custom types\", \"[scan][block]\", algorithm, modes)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 256;\n  constexpr int block_dim_y                   = 1;\n  constexpr int block_dim_z                   = 1;\n  constexpr int tile_size                     = items_per_thread * block_dim_x * block_dim_y * block_dim_z;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, sum_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan returns valid block aggregate\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t>;\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  c2h::device_vector<type> d_block_aggregate(1);\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, sum_aggregate_op_t<type, mode>{target_thread_id, thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n  type block_aggregate         = host_scan(mode, h_out, std::plus<type>{});\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(block_aggregate == d_block_aggregate[0]);\n}\n\nC2H_TEST(\"Block scan supports prefix op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, sum_prefix_op_t<type, mode>{prefix});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(mode, h_out, std::plus<type>{}, prefix);\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block scan supports custom scan op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(d_in, d_out, min_op_t<mode>{});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    INT_MAX);\n\n  if constexpr (mode == scan_mode::exclusive)\n  {\n    //! With no initial value, the output computed for *thread*\\ :sub:`0` is undefined.\n    d_out.erase(d_out.begin());\n    h_out.erase(h_out.begin());\n  }\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block custom op scan works with initial value\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, tile_size))));\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, min_init_value_op_t<type, mode>{initial_value});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n}\n\nC2H_TEST(\"Block custom op scan with initial value returns valid block aggregate\",\n         \"[scan][block]\",\n         algorithm,\n         modes,\n         block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  const type initial_value = static_cast<type>(GENERATE_COPY(take(2, random(0, tile_size))));\n\n  const int target_thread_id = GENERATE_COPY(take(2, random(0, threads_in_block - 1)));\n\n  c2h::device_vector<type> d_block_aggregate(1);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in,\n    d_out,\n    min_init_value_aggregate_op_t<type, mode>{\n      target_thread_id, initial_value, thrust::raw_pointer_cast(d_block_aggregate.data())});\n\n  c2h::host_vector<type> h_out = d_in;\n  type h_block_aggregate       = host_scan(\n    mode,\n    h_out,\n    [](type l, type r) {\n      return std::min(l, r);\n    },\n    initial_value);\n\n  REQUIRE(h_out == d_out);\n  REQUIRE(h_block_aggregate == d_block_aggregate[0]);\n}\n\nC2H_TEST(\"Block scan supports prefix op and custom scan op\", \"[scan][block]\", algorithm, modes, block_dim_yz)\n{\n  constexpr int items_per_thread              = 3;\n  constexpr int block_dim_x                   = 64;\n  constexpr int block_dim_y                   = c2h::get<2, TestType>::value;\n  constexpr int block_dim_z                   = block_dim_y;\n  constexpr int threads_in_block              = block_dim_x * block_dim_y * block_dim_z;\n  constexpr int tile_size                     = items_per_thread * threads_in_block;\n  constexpr hipcub::BlockScanAlgorithm algorithm = c2h::get<0, TestType>::value;\n  constexpr scan_mode mode                    = c2h::get<1, TestType>::value;\n\n  using type = int;\n\n  const type prefix = GENERATE_COPY(take(2, random(0, tile_size)));\n\n  c2h::device_vector<type> d_out(tile_size);\n  c2h::device_vector<type> d_in(tile_size);\n  c2h::gen(C2H_SEED(10), d_in);\n\n  block_scan<algorithm, items_per_thread, block_dim_x, block_dim_y, block_dim_z>(\n    d_in, d_out, min_prefix_op_t<type, mode>{prefix});\n\n  c2h::host_vector<type> h_out = d_in;\n  host_scan(\n    mode,\n    h_out,\n    [](type a, type b) {\n      return std::min(a, b);\n    },\n    prefix);\n\n  REQUIRE(h_out == d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_scan.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n\n#include \"cub/thread/thread_sort.cuh\"\n#include <c2h/catch2_test_helper.h>\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __host__ __device__ bool operator()(DataType& lhs, DataType& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <typename KeyT, typename ValueT, int ItemsPerThread>\n__global__ void kernel(const KeyT* keys_in, KeyT* keys_out, const ValueT* values_in, ValueT* values_out)\n{\n  KeyT thread_keys[ItemsPerThread];\n  KeyT thread_values[ItemsPerThread];\n\n  const auto thread_offset = ItemsPerThread * threadIdx.x;\n  keys_in += thread_offset;\n  keys_out += thread_offset;\n  values_in += thread_offset;\n  values_out += thread_offset;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_keys[item]   = keys_in[item];\n    thread_values[item] = values_in[item];\n  }\n\n  cub::StableOddEvenSort(thread_keys, thread_values, CustomLess{});\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    keys_out[item]   = thread_keys[item];\n    values_out[item] = thread_values[item];\n  }\n}\n\nusing value_types           = c2h::type_list<std::uint32_t, std::uint64_t>;\nusing items_per_thread_list = c2h::enum_type_list<int, 2, 3, 4, 5, 7, 8, 9, 11>;\n\nC2H_TEST(\"Test\", \"[thread_sort]\", value_types, items_per_thread_list)\n{\n  using key_t                             = std::uint32_t;\n  using value_t                           = c2h::get<0, TestType>;\n  constexpr int items_per_thread          = c2h::get<1, TestType>::value;\n  constexpr unsigned int threads_in_block = 1024;\n  constexpr unsigned int elements         = threads_in_block * items_per_thread;\n\n  thrust::default_random_engine re;\n  c2h::device_vector<std::uint8_t> data_source(elements);\n\n  for (int iteration = 0; iteration < 10; iteration++)\n  {\n    c2h::gen(C2H_SEED(2), data_source);\n    c2h::device_vector<key_t> in_keys(data_source);\n    c2h::device_vector<key_t> out_keys(elements);\n\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    c2h::device_vector<value_t> in_values(data_source);\n    c2h::device_vector<value_t> out_values(elements);\n\n    c2h::host_vector<key_t> host_keys(in_keys);\n    c2h::host_vector<value_t> host_values(in_values);\n\n    kernel<key_t, value_t, items_per_thread><<<1, threads_in_block>>>(\n      thrust::raw_pointer_cast(in_keys.data()),\n      thrust::raw_pointer_cast(out_keys.data()),\n      thrust::raw_pointer_cast(in_values.data()),\n      thrust::raw_pointer_cast(out_values.data()));\n\n    for (unsigned int tid = 0; tid < threads_in_block; tid++)\n    {\n      const auto thread_begin = tid * items_per_thread;\n      const auto thread_end   = thread_begin + items_per_thread;\n\n      thrust::sort_by_key(host_keys.begin() + thread_begin,\n                          host_keys.begin() + thread_end,\n                          host_values.begin() + thread_begin,\n                          CustomLess{});\n    }\n\n    CHECK(host_keys == out_keys);\n    CHECK(host_values == out_values);\n  }\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/random.h>\n#include <thrust/sequence.h>\n#include <thrust/shuffle.h>\n#include <thrust/sort.h>\n\n#include \"cub/thread/thread_sort.cuh\"\n#include <c2h/catch2_test_helper.h>\n\nstruct CustomLess\n{\n  template <typename DataType>\n  __host__ __device__ bool operator()(DataType& lhs, DataType& rhs)\n  {\n    return lhs < rhs;\n  }\n};\n\ntemplate <typename KeyT, typename ValueT, int ItemsPerThread>\n__global__ void kernel(const KeyT* keys_in, KeyT* keys_out, const ValueT* values_in, ValueT* values_out)\n{\n  KeyT thread_keys[ItemsPerThread];\n  KeyT thread_values[ItemsPerThread];\n\n  const auto thread_offset = ItemsPerThread * threadIdx.x;\n  keys_in += thread_offset;\n  keys_out += thread_offset;\n  values_in += thread_offset;\n  values_out += thread_offset;\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    thread_keys[item]   = keys_in[item];\n    thread_values[item] = values_in[item];\n  }\n\n  hipcub::StableOddEvenSort(thread_keys, thread_values, CustomLess{});\n\n  for (int item = 0; item < ItemsPerThread; item++)\n  {\n    keys_out[item]   = thread_keys[item];\n    values_out[item] = thread_values[item];\n  }\n}\n\nusing value_types           = c2h::type_list<std::uint32_t, std::uint64_t>;\nusing items_per_thread_list = c2h::enum_type_list<int, 2, 3, 4, 5, 7, 8, 9, 11>;\n\nC2H_TEST(\"Test\", \"[thread_sort]\", value_types, items_per_thread_list)\n{\n  using key_t                             = std::uint32_t;\n  using value_t                           = c2h::get<0, TestType>;\n  constexpr int items_per_thread          = c2h::get<1, TestType>::value;\n  constexpr unsigned int threads_in_block = 1024;\n  constexpr unsigned int elements         = threads_in_block * items_per_thread;\n\n  thrust::default_random_engine re;\n  c2h::device_vector<std::uint8_t> data_source(elements);\n\n  for (int iteration = 0; iteration < 10; iteration++)\n  {\n    c2h::gen(C2H_SEED(2), data_source);\n    c2h::device_vector<key_t> in_keys(data_source);\n    c2h::device_vector<key_t> out_keys(elements);\n\n    thrust::shuffle(data_source.begin(), data_source.end(), re);\n    c2h::device_vector<value_t> in_values(data_source);\n    c2h::device_vector<value_t> out_values(elements);\n\n    c2h::host_vector<key_t> host_keys(in_keys);\n    c2h::host_vector<value_t> host_values(in_values);\n\n    kernel<key_t, value_t, items_per_thread><<<1, threads_in_block>>>(\n      thrust::raw_pointer_cast(in_keys.data()),\n      thrust::raw_pointer_cast(out_keys.data()),\n      thrust::raw_pointer_cast(in_values.data()),\n      thrust::raw_pointer_cast(out_values.data()));\n\n    for (unsigned int tid = 0; tid < threads_in_block; tid++)\n    {\n      const auto thread_begin = tid * items_per_thread;\n      const auto thread_end   = thread_begin + items_per_thread;\n\n      thrust::sort_by_key(host_keys.begin() + thread_begin,\n                          host_keys.begin() + thread_end,\n                          host_values.begin() + thread_begin,\n                          CustomLess{});\n    }\n\n    CHECK(host_keys == out_keys);\n    CHECK(host_values == out_values);\n  }\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_thread_sort.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n// Ensure printing of CUDA runtime errors to console\n#include \"cub/util_type.cuh\"\n#define CUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_rank.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/radix_rank_sort_operations.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_vsmem.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n#include <memory>\n\n#include \"test_util.h\"\n\nbool g_verbose = false;\ncub::CachingDeviceAllocator g_allocator(true);\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\n__launch_bounds__(ThreadsPerBlock, 1) __global__ void kernel(Key* d_keys, int* d_ranks)\n{\n  using block_radix_rank =\n    cub::detail::block_radix_rank_t<RankAlgorithm, ThreadsPerBlock, RadixBits, Descending, ScanAlgorithm>;\n\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ storage_t temp_storage;\n\n  // Items per thread\n  Key keys[ItemsPerThread];\n  int ranks[ItemsPerThread];\n\n  constexpr bool uses_warp_striped_arrangement =\n    RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH\n    || RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n    || RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR;\n\n  if (uses_warp_striped_arrangement)\n  {\n    cub::LoadDirectWarpStriped(threadIdx.x, d_keys, keys);\n  }\n  else\n  {\n    cub::LoadDirectBlocked(threadIdx.x, d_keys, keys);\n  }\n\n  cub::BFEDigitExtractor<Key> extractor(0, RadixBits);\n  block_radix_rank(temp_storage).RankKeys(keys, ranks, extractor);\n\n  if (uses_warp_striped_arrangement)\n  {\n    cub::StoreDirectWarpStriped(threadIdx.x, d_ranks, ranks);\n  }\n  else\n  {\n    cub::StoreDirectBlocked(threadIdx.x, d_ranks, ranks);\n  }\n}\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n/**\n * Simple key-value pairing\n */\ntemplate <typename Key>\nstruct pair_t\n{\n  Key key;\n  int value;\n\n  bool operator<(const pair_t& b) const\n  {\n    return (key < b.key);\n  }\n};\n\ntemplate <bool DESCENDING, typename Key>\nvoid Initialize(GenMode gen_mode, Key* h_keys, int* h_reference_ranks, int num_items, int num_bits)\n{\n  std::unique_ptr<pair_t<Key>[]> h_pairs_storage(new pair_t<Key>[num_items]);\n  pair_t<Key>* h_pairs = h_pairs_storage.get();\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    InitValue(gen_mode, h_keys[i], i);\n\n    // Mask off unwanted portions\n    std::uint64_t base = 0;\n    memcpy(&base, &h_keys[i], sizeof(Key));\n    base &= (1ull << num_bits) - 1;\n    memcpy(&h_keys[i], &base, sizeof(Key));\n\n    h_pairs[i].key   = h_keys[i];\n    h_pairs[i].value = i;\n  }\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  std::stable_sort(h_pairs, h_pairs + num_items);\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_reference_ranks[h_pairs[i].value] = i;\n  }\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestDriver(GenMode gen_mode)\n{\n  constexpr int tile_size = ThreadsPerBlock * ItemsPerThread;\n\n  // Allocate host arrays\n  std::unique_ptr<Key[]> h_keys(new Key[tile_size]);\n  std::unique_ptr<int[]> h_ranks(new int[tile_size]);\n  std::unique_ptr<int[]> h_reference_ranks(new int[tile_size]);\n\n  // Allocate device arrays\n  Key* d_keys  = nullptr;\n  int* d_ranks = nullptr;\n\n  CubDebugExit(g_allocator.DeviceAllocate((void**) &d_keys, sizeof(Key) * tile_size));\n  CubDebugExit(g_allocator.DeviceAllocate((void**) &d_ranks, sizeof(int) * tile_size));\n\n  // Initialize problem and solution on host\n  Initialize<Descending>(gen_mode, h_keys.get(), h_reference_ranks.get(), tile_size, RadixBits);\n\n  // Copy problem to device\n  CubDebugExit(cudaMemcpy(d_keys, h_keys.get(), sizeof(Key) * tile_size, cudaMemcpyHostToDevice));\n\n  // Run kernel\n  kernel<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>\n    <<<1, ThreadsPerBlock>>>(d_keys, d_ranks);\n\n  // Flush kernel output / errors\n  CubDebugExit(cudaPeekAtLastError());\n  CubDebugExit(cudaDeviceSynchronize());\n\n  // Check keys results\n  const bool compare = CompareDeviceResults(h_reference_ranks.get(), d_ranks, tile_size, g_verbose, g_verbose);\n  AssertEquals(0, compare);\n\n  if (d_keys)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_keys));\n  }\n\n  if (d_ranks)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_ranks));\n  }\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cuda::std::true_type /*fits_smem_capacity*/)\n{\n  TestDriver<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(UNIFORM);\n\n  TestDriver<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(INTEGER_SEED);\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cuda::std::false_type fits_smem_capacity)\n{}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          bool Descending,\n          typename Key>\nvoid Test()\n{\n  // Check size of smem storage for the target arch to make sure it will fit\n  using block_radix_rank =\n    cub::detail::block_radix_rank_t<RankAlgorithm, ThreadsPerBlock, RadixBits, Descending, ScanAlgorithm>;\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  cuda::std::bool_constant<(sizeof(storage_t) <= cub::detail::max_smem_per_block)> fits_smem_capacity;\n\n  TestValid<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    fits_smem_capacity);\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm,\n          typename Key>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, true, Key>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, false, Key>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          cub::BlockScanAlgorithm ScanAlgorithm>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint8_t>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint16_t>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock, int ItemsPerThread, int RadixBits>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, cub::BLOCK_SCAN_RAKING>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, cub::BLOCK_SCAN_WARP_SCANS>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock, int ItemsPerThread>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, 1>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, 5>();\n}\n\ntemplate <cub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, 1>();\n  Test<RankAlgorithm, ThreadsPerBlock, 4>();\n}\n\ntemplate <int ThreadsPerBlock>\nvoid Test(cuda::std::true_type /* multiple of hw warp */)\n{\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_MATCH, ThreadsPerBlock>();\n\n  // TODO(senior-zero):\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR\n}\n\ntemplate <int ThreadsPerBlock>\nvoid Test(cuda::std::false_type /* multiple of hw warp */)\n{}\n\ntemplate <int ThreadsPerBlock>\nvoid Test()\n{\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_BASIC, ThreadsPerBlock>();\n  Test<cub::RadixRankAlgorithm::RADIX_RANK_MEMOIZE, ThreadsPerBlock>();\n\n  Test<ThreadsPerBlock>(cuda::std::bool_constant < (ThreadsPerBlock % 32) == 0 > {});\n}\n\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  Test<16>();\n  Test<32>();\n  Test<128>();\n  Test<130>();\n\n  g_allocator.FreeAllCached();\n\n  return 0;\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n// Ensure printing of CUDA runtime errors to console\n#include \"hip/hip_runtime.h\"\n#include \"cub/util_type.cuh\"\n#define HIPCUB_STDERR\n\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_radix_rank.cuh>\n#include <cub/block/block_store.cuh>\n#include <cub/block/radix_rank_sort_operations.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_vsmem.cuh>\n\n#include <algorithm>\n#include <cstdio>\n#include <iostream>\n#include <memory>\n\n#include \"test_util.h\"\n\nbool g_verbose = false;\nhipcub::CachingDeviceAllocator g_allocator(true);\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\n__launch_bounds__(ThreadsPerBlock, 1) __global__ void kernel(Key* d_keys, int* d_ranks)\n{\n  using block_radix_rank =\n    hipcub::detail::block_radix_rank_t<RankAlgorithm, ThreadsPerBlock, RadixBits, Descending, ScanAlgorithm>;\n\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  // Allocate temp storage in shared memory\n  __shared__ storage_t temp_storage;\n\n  // Items per thread\n  Key keys[ItemsPerThread];\n  int ranks[ItemsPerThread];\n\n  constexpr bool uses_warp_striped_arrangement =\n    RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH\n    || RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n    || RankAlgorithm == hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR;\n\n  if (uses_warp_striped_arrangement)\n  {\n    hipcub::LoadDirectWarpStriped(threadIdx.x, d_keys, keys);\n  }\n  else\n  {\n    hipcub::LoadDirectBlocked(threadIdx.x, d_keys, keys);\n  }\n\n  hipcub::BFEDigitExtractor<Key> extractor(0, RadixBits);\n  block_radix_rank(temp_storage).RankKeys(keys, ranks, extractor);\n\n  if (uses_warp_striped_arrangement)\n  {\n    hipcub::StoreDirectWarpStriped(threadIdx.x, d_ranks, ranks);\n  }\n  else\n  {\n    hipcub::StoreDirectBlocked(threadIdx.x, d_ranks, ranks);\n  }\n}\n\n//---------------------------------------------------------------------\n// Host testing subroutines\n//---------------------------------------------------------------------\n\n/**\n * Simple key-value pairing\n */\ntemplate <typename Key>\nstruct pair_t\n{\n  Key key;\n  int value;\n\n  bool operator<(const pair_t& b) const\n  {\n    return (key < b.key);\n  }\n};\n\ntemplate <bool DESCENDING, typename Key>\nvoid Initialize(GenMode gen_mode, Key* h_keys, int* h_reference_ranks, int num_items, int num_bits)\n{\n  std::unique_ptr<pair_t<Key>[]> h_pairs_storage(new pair_t<Key>[num_items]);\n  pair_t<Key>* h_pairs = h_pairs_storage.get();\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    InitValue(gen_mode, h_keys[i], i);\n\n    // Mask off unwanted portions\n    std::uint64_t base = 0;\n    memcpy(&base, &h_keys[i], sizeof(Key));\n    base &= (1ull << num_bits) - 1;\n    memcpy(&h_keys[i], &base, sizeof(Key));\n\n    h_pairs[i].key   = h_keys[i];\n    h_pairs[i].value = i;\n  }\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  std::stable_sort(h_pairs, h_pairs + num_items);\n\n  if (DESCENDING)\n  {\n    std::reverse(h_pairs, h_pairs + num_items);\n  }\n\n  for (int i = 0; i < num_items; ++i)\n  {\n    h_reference_ranks[h_pairs[i].value] = i;\n  }\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestDriver(GenMode gen_mode)\n{\n  constexpr int tile_size = ThreadsPerBlock * ItemsPerThread;\n\n  // Allocate host arrays\n  std::unique_ptr<Key[]> h_keys(new Key[tile_size]);\n  std::unique_ptr<int[]> h_ranks(new int[tile_size]);\n  std::unique_ptr<int[]> h_reference_ranks(new int[tile_size]);\n\n  // Allocate device arrays\n  Key* d_keys  = nullptr;\n  int* d_ranks = nullptr;\n\n  CubDebugExit(g_allocator.DeviceAllocate((void**) &d_keys, sizeof(Key) * tile_size));\n  CubDebugExit(g_allocator.DeviceAllocate((void**) &d_ranks, sizeof(int) * tile_size));\n\n  // Initialize problem and solution on host\n  Initialize<Descending>(gen_mode, h_keys.get(), h_reference_ranks.get(), tile_size, RadixBits);\n\n  // Copy problem to device\n  CubDebugExit(hipMemcpy(d_keys, h_keys.get(), sizeof(Key) * tile_size, hipMemcpyHostToDevice));\n\n  // Run kernel\n  kernel<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>\n    <<<1, ThreadsPerBlock>>>(d_keys, d_ranks);\n\n  // Flush kernel output / errors\n  CubDebugExit(hipPeekAtLastError());\n  CubDebugExit(hipDeviceSynchronize());\n\n  // Check keys results\n  const bool compare = CompareDeviceResults(h_reference_ranks.get(), d_ranks, tile_size, g_verbose, g_verbose);\n  AssertEquals(0, compare);\n\n  if (d_keys)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_keys));\n  }\n\n  if (d_ranks)\n  {\n    CubDebugExit(g_allocator.DeviceFree(d_ranks));\n  }\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cuda::std::true_type /*fits_smem_capacity*/)\n{\n  TestDriver<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(UNIFORM);\n\n  TestDriver<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(INTEGER_SEED);\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          int Descending,\n          typename Key>\nvoid TestValid(cuda::std::false_type fits_smem_capacity)\n{}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          bool Descending,\n          typename Key>\nvoid Test()\n{\n  // Check size of smem storage for the target arch to make sure it will fit\n  using block_radix_rank =\n    hipcub::detail::block_radix_rank_t<RankAlgorithm, ThreadsPerBlock, RadixBits, Descending, ScanAlgorithm>;\n  using storage_t = typename block_radix_rank::TempStorage;\n\n  cuda::std::bool_constant<(sizeof(storage_t) <= hipcub::detail::max_smem_per_block)> fits_smem_capacity;\n\n  TestValid<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, Descending, Key>(\n    fits_smem_capacity);\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm,\n          typename Key>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, true, Key>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, false, Key>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm,\n          int ThreadsPerBlock,\n          int ItemsPerThread,\n          int RadixBits,\n          hipcub::BlockScanAlgorithm ScanAlgorithm>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint8_t>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, ScanAlgorithm, std::uint16_t>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock, int ItemsPerThread, int RadixBits>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, hipcub::BLOCK_SCAN_RAKING>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, RadixBits, hipcub::BLOCK_SCAN_WARP_SCANS>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock, int ItemsPerThread>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, 1>();\n  Test<RankAlgorithm, ThreadsPerBlock, ItemsPerThread, 5>();\n}\n\ntemplate <hipcub::RadixRankAlgorithm RankAlgorithm, int ThreadsPerBlock>\nvoid Test()\n{\n  Test<RankAlgorithm, ThreadsPerBlock, 1>();\n  Test<RankAlgorithm, ThreadsPerBlock, 4>();\n}\n\ntemplate <int ThreadsPerBlock>\nvoid Test(cuda::std::true_type /* multiple of hw warp */)\n{\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_MATCH, ThreadsPerBlock>();\n\n  // TODO(senior-zero):\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ANY\n  // - RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR\n}\n\ntemplate <int ThreadsPerBlock>\nvoid Test(cuda::std::false_type /* multiple of hw warp */)\n{}\n\ntemplate <int ThreadsPerBlock>\nvoid Test()\n{\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_BASIC, ThreadsPerBlock>();\n  Test<hipcub::RadixRankAlgorithm::RADIX_RANK_MEMOIZE, ThreadsPerBlock>();\n\n  Test<ThreadsPerBlock>(cuda::std::bool_constant < (ThreadsPerBlock % 32) == 0 > {});\n}\n\nint main(int argc, char** argv)\n{\n  // Initialize command line\n  CommandLineArgs args(argc, argv);\n  g_verbose = args.CheckCmdLineFlag(\"v\");\n\n  // Print usage\n  if (args.CheckCmdLineFlag(\"help\"))\n  {\n    printf(\"%s \"\n           \"[--device=<device-id>] \"\n           \"[--v] \"\n           \"\\n\",\n           argv[0]);\n    exit(0);\n  }\n\n  // Initialize device\n  CubDebugExit(args.DeviceInit());\n\n  Test<16>();\n  Test<32>();\n  Test<128>();\n  Test<130>();\n\n  g_allocator.FreeAllCached();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cub/test/test_block_radix_rank.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <thrust/count.h>\n\n#include <cuda/std/tuple>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\ntemplate <class T>\n__global__ void cub_api_example_x2_0_kernel(const T* d_in, T* d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] * T{2};\n  }\n}\n\ntemplate <class T>\n__global__ void cub_api_example_x0_5_kernel(const T* d_in, T* d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] / T{2};\n  }\n}\n\nstruct cub_api_example_t\n{\n  static constexpr int threads_in_block = 256;\n\n  template <class T, class KernelT>\n  CUB_RUNTIME_FUNCTION static cudaError_t invoke(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    KernelT kernel,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    cudaStream_t stream = nullptr)\n  {\n    constexpr bool should_be_invoked_on_device = TEST_LAUNCH == 1;\n\n    NV_IF_ELSE_TARGET(NV_IS_HOST,\n                      (if (should_be_invoked_on_device) { return cudaErrorLaunchFailure; }),\n                      (if (!should_be_invoked_on_device) { return cudaErrorLaunchFailure; }));\n\n    if (d_temp_storage == nullptr)\n    {\n      temp_storage_bytes = static_cast<std::size_t>(num_items);\n      return cudaSuccess;\n    }\n\n    if (temp_storage_bytes != static_cast<std::size_t>(num_items))\n    {\n      return cudaErrorInvalidValue;\n    }\n\n#if TEST_LAUNCH == 2\n    NV_IF_TARGET(NV_IS_HOST, ({\n                   cudaStreamCaptureStatus status{};\n                   cudaStreamIsCapturing(stream, &status);\n                   if (status != cudaStreamCaptureStatusActive)\n                   {\n                     return cudaErrorLaunchFailure;\n                   }\n                 }));\n#endif\n\n    const int blocks_in_grid = (num_items + threads_in_block - 1) / threads_in_block;\n\n    return thrust::cuda_cub::detail::triple_chevron(blocks_in_grid, threads_in_block, 0, stream)\n      .doit(kernel, d_in, d_out, num_items);\n  }\n\n  template <class T>\n  CUB_RUNTIME_FUNCTION static cudaError_t\n  x2_0(std::uint8_t* d_temp_storage,\n       std::size_t& temp_storage_bytes,\n       const T* d_in,\n       T* d_out,\n       int num_items,\n       cudaStream_t stream = nullptr)\n  {\n    return invoke(d_temp_storage, temp_storage_bytes, cub_api_example_x2_0_kernel<T>, d_in, d_out, num_items, stream);\n  }\n\n  template <class T>\n  CUB_RUNTIME_FUNCTION static cudaError_t\n  x0_5(std::uint8_t* d_temp_storage,\n       std::size_t& temp_storage_bytes,\n       const T* d_in,\n       T* d_out,\n       int num_items,\n       cudaStream_t stream = nullptr)\n  {\n    return invoke(d_temp_storage, temp_storage_bytes, cub_api_example_x0_5_kernel<T>, d_in, d_out, num_items, stream);\n  }\n};\n\nDECLARE_LAUNCH_WRAPPER(cub_api_example_t::x2_0, x2_0);\nDECLARE_LAUNCH_WRAPPER(cub_api_example_t::x0_5, x0_5);\n\nC2H_TEST(\"Launch wrapper works with predefined invocables\", \"[test][utils]\")\n{\n  INFO(\"Launch = \" << TEST_LAUNCH);\n\n  int n = 42;\n  c2h::device_vector<int> in(n, 21);\n  c2h::device_vector<int> out(n);\n\n  int* d_in  = thrust::raw_pointer_cast(in.data());\n  int* d_out = thrust::raw_pointer_cast(out.data());\n\n  {\n    x2_0(d_in, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    x0_5(d_out, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n\nstruct custom_x2_0_invocable\n{\n  template <class T>\n  CUB_RUNTIME_FUNCTION cudaError_t operator()(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    cudaStream_t stream = nullptr)\n  {\n    return cub_api_example_t::x2_0(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, stream);\n  }\n};\n\nstruct custom_x0_5_invocable\n{\n  template <class T>\n  CUB_RUNTIME_FUNCTION cudaError_t operator()(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    cudaStream_t stream = nullptr)\n  {\n    return cub_api_example_t::x0_5(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, stream);\n  }\n};\n\nC2H_TEST(\"Launch wrapper works with custom invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  c2h::device_vector<int> in(n, 21);\n  c2h::device_vector<int> out(n);\n\n  int* d_in  = thrust::raw_pointer_cast(in.data());\n  int* d_out = thrust::raw_pointer_cast(out.data());\n\n  {\n    launch(custom_x2_0_invocable{}, d_in, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    launch(custom_x0_5_invocable{}, d_out, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n\n#include <cuda/std/tuple>\n\n#include \"catch2_test_launch_helper.h\"\n#include <c2h/catch2_test_helper.h>\n\n// %PARAM% TEST_LAUNCH lid 0:1:2\n\ntemplate <class T>\n__global__ void cub_api_example_x2_0_kernel(const T* d_in, T* d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] * T{2};\n  }\n}\n\ntemplate <class T>\n__global__ void cub_api_example_x0_5_kernel(const T* d_in, T* d_out, int num_items)\n{\n  const int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n  if (i < num_items)\n  {\n    d_out[i] = d_in[i] / T{2};\n  }\n}\n\nstruct cub_api_example_t\n{\n  static constexpr int threads_in_block = 256;\n\n  template <class T, class KernelT>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t invoke(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    KernelT kernel,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    hipStream_t stream = nullptr)\n  {\n    constexpr bool should_be_invoked_on_device = TEST_LAUNCH == 1;\n\n    NV_IF_ELSE_TARGET(NV_IS_HOST,\n                      (if (should_be_invoked_on_device) { return hipErrorLaunchFailure; }),\n                      (if (!should_be_invoked_on_device) { return hipErrorLaunchFailure; }));\n\n    if (d_temp_storage == nullptr)\n    {\n      temp_storage_bytes = static_cast<std::size_t>(num_items);\n      return hipSuccess;\n    }\n\n    if (temp_storage_bytes != static_cast<std::size_t>(num_items))\n    {\n      return hipErrorInvalidValue;\n    }\n\n#if TEST_LAUNCH == 2\n    NV_IF_TARGET(NV_IS_HOST, ({\n                   hipStreamCaptureStatus status{};\n                   hipStreamIsCapturing(stream, &status);\n                   if (status != hipStreamCaptureStatusActive)\n                   {\n                     return hipErrorLaunchFailure;\n                   }\n                 }));\n#endif\n\n    const int blocks_in_grid = (num_items + threads_in_block - 1) / threads_in_block;\n\n    return thrust::cuda_cub::detail::triple_chevron(blocks_in_grid, threads_in_block, 0, stream)\n      .doit(kernel, d_in, d_out, num_items);\n  }\n\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t\n  x2_0(std::uint8_t* d_temp_storage,\n       std::size_t& temp_storage_bytes,\n       const T* d_in,\n       T* d_out,\n       int num_items,\n       hipStream_t stream = nullptr)\n  {\n    return invoke(d_temp_storage, temp_storage_bytes, cub_api_example_x2_0_kernel<T>, d_in, d_out, num_items, stream);\n  }\n\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION static hipError_t\n  x0_5(std::uint8_t* d_temp_storage,\n       std::size_t& temp_storage_bytes,\n       const T* d_in,\n       T* d_out,\n       int num_items,\n       hipStream_t stream = nullptr)\n  {\n    return invoke(d_temp_storage, temp_storage_bytes, cub_api_example_x0_5_kernel<T>, d_in, d_out, num_items, stream);\n  }\n};\n\nDECLARE_LAUNCH_WRAPPER(cub_api_example_t::x2_0, x2_0);\nDECLARE_LAUNCH_WRAPPER(cub_api_example_t::x0_5, x0_5);\n\nC2H_TEST(\"Launch wrapper works with predefined invocables\", \"[test][utils]\")\n{\n  INFO(\"Launch = \" << TEST_LAUNCH);\n\n  int n = 42;\n  c2h::device_vector<int> in(n, 21);\n  c2h::device_vector<int> out(n);\n\n  int* d_in  = thrust::raw_pointer_cast(in.data());\n  int* d_out = thrust::raw_pointer_cast(out.data());\n\n  {\n    x2_0(d_in, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    x0_5(d_out, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n\nstruct custom_x2_0_invocable\n{\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION hipError_t operator()(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    hipStream_t stream = nullptr)\n  {\n    return cub_api_example_t::x2_0(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, stream);\n  }\n};\n\nstruct custom_x0_5_invocable\n{\n  template <class T>\n  HIPCUB_RUNTIME_FUNCTION hipError_t operator()(\n    std::uint8_t* d_temp_storage,\n    std::size_t& temp_storage_bytes,\n    const T* d_in,\n    T* d_out,\n    int num_items,\n    hipStream_t stream = nullptr)\n  {\n    return cub_api_example_t::x0_5(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, stream);\n  }\n};\n\nC2H_TEST(\"Launch wrapper works with custom invocables\", \"[test][utils]\")\n{\n  int n = 42;\n  c2h::device_vector<int> in(n, 21);\n  c2h::device_vector<int> out(n);\n\n  int* d_in  = thrust::raw_pointer_cast(in.data());\n  int* d_out = thrust::raw_pointer_cast(out.data());\n\n  {\n    launch(custom_x2_0_invocable{}, d_in, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 42));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n\n  {\n    launch(custom_x0_5_invocable{}, d_out, d_out, n);\n\n    const auto actual   = static_cast<std::size_t>(thrust::count(c2h::device_policy, out.begin(), out.end(), 21));\n    const auto expected = static_cast<std::size_t>(n);\n\n    REQUIRE(actual == expected);\n  }\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_launch_wrapper.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <cub/device/device_memcpy.cuh>\n\n#include <thrust/fill.h>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <std::uint32_t NumItems,\n          std::uint32_t MaxItemValue,\n          cub::detail::batch_memcpy::prefer_power_of_two_bits_option PreferPowerOfTwoBits>\n__global__ void test_bit_packed_counter_kernel(\n  std::uint32_t* bins, std::uint32_t* increments, std::uint32_t* counts_out, std::int32_t num_items)\n{\n  using bit_packed_counter_t =\n    cub::detail::batch_memcpy::bit_packed_counter<NumItems, MaxItemValue, PreferPowerOfTwoBits>;\n  bit_packed_counter_t counter{};\n  for (std::int32_t i = 0; i < num_items; i++)\n  {\n    counter.add(bins[i], increments[i]);\n  }\n\n  for (std::uint32_t i = 0; i < NumItems; i++)\n  {\n    counts_out[i] = counter.get(i);\n  }\n}\n\ntemplate <std::uint32_t NumItems, std::uint32_t MaxItemValue>\nstruct test_spec\n{\n  static constexpr std::uint32_t num_items      = NumItems;\n  static constexpr std::uint32_t max_item_value = MaxItemValue;\n};\n\nusing use_power_of_two_bits =\n  c2h::enum_type_list<cub::detail::batch_memcpy::prefer_power_of_two_bits_option,\n                      cub::detail::batch_memcpy::prefer_power_of_two_bits_option::yes,\n                      cub::detail::batch_memcpy::prefer_power_of_two_bits_option::no>;\n\nusing test_combinations =\n  c2h::type_list<test_spec<1, 1>,\n                 test_spec<1, (0x01U << 16)>,\n                 test_spec<4, 1>,\n                 test_spec<4, 2>,\n                 test_spec<4, 255>,\n                 test_spec<4, 256>,\n                 test_spec<8, 1024>,\n                 test_spec<32, 1>,\n                 test_spec<32, 256>>;\n\nC2H_TEST(\"The bit_packed_counter used by DeviceMemcpy works\", \"[memcpy]\", test_combinations, use_power_of_two_bits)\n{\n  constexpr std::uint32_t num_items       = c2h::get<0, TestType>::num_items;\n  constexpr std::uint32_t max_item_value  = c2h::get<0, TestType>::max_item_value;\n  constexpr auto prefer_power_of_two_bits = c2h::get<1, TestType>::value;\n\n  constexpr std::uint32_t min_increment = 0;\n  constexpr std::uint32_t max_increment = 4;\n  constexpr double avg_increment =\n    static_cast<double>(min_increment) + (static_cast<double>(max_increment - min_increment) / 2.0);\n  auto const num_increments =\n    static_cast<std::int32_t>(static_cast<double>(max_item_value * num_items) / avg_increment);\n  CAPTURE(num_items, max_item_value, prefer_power_of_two_bits, num_increments);\n\n  // Initialize device-side test data\n  c2h::device_vector<std::uint32_t> bins_in(num_increments);\n  c2h::gen(C2H_SEED(2), bins_in, 0U, num_items - 1U);\n  c2h::device_vector<std::uint32_t> increments_in(num_increments);\n  c2h::gen(C2H_SEED(2), increments_in, min_increment, max_increment);\n\n  c2h::device_vector<std::uint32_t> h_bins(bins_in);\n  c2h::device_vector<std::uint32_t> h_increments(increments_in);\n  c2h::host_vector<std::uint32_t> reference_counters(num_items, 0);\n\n  // Make sure test data does not overflow any of the counters\n  for (std::int32_t i = 0; i < num_increments; i++)\n  {\n    // New increment for this bin would overflow => zero this increment\n    if (reference_counters[h_bins[i]] + h_increments[i] >= max_item_value)\n    {\n      h_increments[i] = 0;\n    }\n    else\n    {\n      reference_counters[h_bins[i]] += h_increments[i];\n    }\n  }\n\n  // Flush back the modified increments\n  increments_in = h_increments;\n\n  // Prepare verification data\n  c2h::device_vector<std::uint32_t> counts_out(num_items, 814920U);\n\n  // Run tests with densely bit-packed counters\n  test_bit_packed_counter_kernel<num_items, max_item_value, prefer_power_of_two_bits><<<1, 1>>>(\n    thrust::raw_pointer_cast(bins_in.data()),\n    thrust::raw_pointer_cast(increments_in.data()),\n    thrust::raw_pointer_cast(counts_out.data()),\n    num_increments);\n\n  // Result verification\n  REQUIRE(reference_counters == counts_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <cub/device/device_memcpy.cuh>\n\n#include <thrust/fill.h>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <std::uint32_t NumItems,\n          std::uint32_t MaxItemValue,\n          hipcub::detail::batch_memcpy::prefer_power_of_two_bits_option PreferPowerOfTwoBits>\n__global__ void test_bit_packed_counter_kernel(\n  std::uint32_t* bins, std::uint32_t* increments, std::uint32_t* counts_out, std::int32_t num_items)\n{\n  using bit_packed_counter_t =\n    hipcub::detail::batch_memcpy::bit_packed_counter<NumItems, MaxItemValue, PreferPowerOfTwoBits>;\n  bit_packed_counter_t counter{};\n  for (std::int32_t i = 0; i < num_items; i++)\n  {\n    counter.add(bins[i], increments[i]);\n  }\n\n  for (std::uint32_t i = 0; i < NumItems; i++)\n  {\n    counts_out[i] = counter.get(i);\n  }\n}\n\ntemplate <std::uint32_t NumItems, std::uint32_t MaxItemValue>\nstruct test_spec\n{\n  static constexpr std::uint32_t num_items      = NumItems;\n  static constexpr std::uint32_t max_item_value = MaxItemValue;\n};\n\nusing use_power_of_two_bits =\n  c2h::enum_type_list<hipcub::detail::batch_memcpy::prefer_power_of_two_bits_option,\n                      hipcub::detail::batch_memcpy::prefer_power_of_two_bits_option::yes,\n                      hipcub::detail::batch_memcpy::prefer_power_of_two_bits_option::no>;\n\nusing test_combinations =\n  c2h::type_list<test_spec<1, 1>,\n                 test_spec<1, (0x01U << 16)>,\n                 test_spec<4, 1>,\n                 test_spec<4, 2>,\n                 test_spec<4, 255>,\n                 test_spec<4, 256>,\n                 test_spec<8, 1024>,\n                 test_spec<32, 1>,\n                 test_spec<32, 256>>;\n\nC2H_TEST(\"The bit_packed_counter used by DeviceMemcpy works\", \"[memcpy]\", test_combinations, use_power_of_two_bits)\n{\n  constexpr std::uint32_t num_items       = c2h::get<0, TestType>::num_items;\n  constexpr std::uint32_t max_item_value  = c2h::get<0, TestType>::max_item_value;\n  constexpr auto prefer_power_of_two_bits = c2h::get<1, TestType>::value;\n\n  constexpr std::uint32_t min_increment = 0;\n  constexpr std::uint32_t max_increment = 4;\n  constexpr double avg_increment =\n    static_cast<double>(min_increment) + (static_cast<double>(max_increment - min_increment) / 2.0);\n  auto const num_increments =\n    static_cast<std::int32_t>(static_cast<double>(max_item_value * num_items) / avg_increment);\n  CAPTURE(num_items, max_item_value, prefer_power_of_two_bits, num_increments);\n\n  // Initialize device-side test data\n  c2h::device_vector<std::uint32_t> bins_in(num_increments);\n  c2h::gen(C2H_SEED(2), bins_in, 0U, num_items - 1U);\n  c2h::device_vector<std::uint32_t> increments_in(num_increments);\n  c2h::gen(C2H_SEED(2), increments_in, min_increment, max_increment);\n\n  c2h::device_vector<std::uint32_t> h_bins(bins_in);\n  c2h::device_vector<std::uint32_t> h_increments(increments_in);\n  c2h::host_vector<std::uint32_t> reference_counters(num_items, 0);\n\n  // Make sure test data does not overflow any of the counters\n  for (std::int32_t i = 0; i < num_increments; i++)\n  {\n    // New increment for this bin would overflow => zero this increment\n    if (reference_counters[h_bins[i]] + h_increments[i] >= max_item_value)\n    {\n      h_increments[i] = 0;\n    }\n    else\n    {\n      reference_counters[h_bins[i]] += h_increments[i];\n    }\n  }\n\n  // Flush back the modified increments\n  increments_in = h_increments;\n\n  // Prepare verification data\n  c2h::device_vector<std::uint32_t> counts_out(num_items, 814920U);\n\n  // Run tests with densely bit-packed counters\n  test_bit_packed_counter_kernel<num_items, max_item_value, prefer_power_of_two_bits><<<1, 1>>>(\n    thrust::raw_pointer_cast(bins_in.data()),\n    thrust::raw_pointer_cast(increments_in.data()),\n    thrust::raw_pointer_cast(counts_out.data()),\n    num_increments);\n\n  // Result verification\n  REQUIRE(reference_counters == counts_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_memcpy_bitpacked_counter.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/block/block_store.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, cub::BlockStoreAlgorithm /* StoreAlgorithm */>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread, ThreadsInBlock, cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm StoreAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t       = cub::detail::it_value_t<InputIteratorT>;\n  using block_store_t = cub::BlockStore<input_t, ThreadsInBlock, ItemsPerThread, StoreAlgorithm>;\n  using storage_t     = typename block_store_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_store_t block_store(storage);\n\n  input_t data[ItemsPerThread];\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = output_idx<ItemsPerThread, ThreadsInBlock, StoreAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      data[i] = input[idx];\n    }\n  }\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_store.Store(output, data);\n  }\n  else\n  {\n    block_store.Store(output, data, num_items);\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm /* StoreAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = output_idx<ItemsPerThread, ThreadsInBlock, cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          cub::BlockStoreAlgorithm StoreAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_store(InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t                       = cub::detail::it_value_t<InputIteratorT>;\n  using block_store_t                 = cub::BlockStore<input_t, ThreadsInBlock, ItemsPerThread, StoreAlgorithm>;\n  using storage_t                     = typename block_store_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= cub::detail::max_smem_per_block;\n\n  kernel<InputIteratorT, OutputIteratorT, ItemsPerThread, ThreadsInBlock, StoreAlgorithm>\n    <<<1, ThreadsInBlock>>>(std::integral_constant<bool, sufficient_resources>{}, input, output, num_items);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block  = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size          = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing store_algorithm =\n  c2h::enum_type_list<cub::BlockStoreAlgorithm,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_store_algorithm =\n  c2h::enum_type_list<cub::BlockStoreAlgorithm,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n                      cub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                     = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                     = c2h::get<2, TestType>::value;\n  static constexpr int tile_size                            = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block store works with even block sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nC2H_TEST(\"Block store works with even odd sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n// WAR bug in vec type handling in NVCC 12.0 + GCC 11.4 + C++20\n#if !(_CCCL_CUDA_COMPILER(NVCC, ==, 12, 0) && _CCCL_COMPILER(GCC, ==, 11, 4) && _CCCL_STD_VER == 2020)\nC2H_TEST(\"Block store works with even vector types\",\n         \"[store][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n#endif // !(NVCC 12.0 and GCC 11.4 and C++20)\n\nC2H_TEST(\"Block store works with custom types\", \"[store][block]\", items_per_thread, store_algorithm)\n{\n  using type                                                = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread                            = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nC2H_TEST(\"Block store works with caching iterators\", \"[store][block]\", items_per_thread, store_algorithm)\n{\n  using type                                                = int;\n  constexpr int items_per_thread                            = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n  cub::CacheModifiedOutputIterator<cub::CacheStoreModifier::STORE_DEFAULT, type> out(\n    thrust::raw_pointer_cast(d_output.data()));\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()), out, static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n#if IPT == 1\nC2H_TEST(\"Vectorized block store with different alignment cases\", \"[store][block]\", c2h::enum_type_list<int, 1, 4, 7>)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  ;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr cub::BlockStoreAlgorithm store_algorithm = cub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE;\n  const int offset_for_elements                             = GENERATE_COPY(0, 1, 2, 3, 4);\n\n  c2h::device_vector<type> d_input(tile_size);\n  c2h::gen(C2H_SEED(2), d_input);\n\n  CAPTURE(offset_for_elements, tile_size);\n  c2h::device_vector<type> d_input_ref(tile_size + offset_for_elements);\n  thrust::copy_n(d_input.begin(), tile_size, d_input_ref.begin() + offset_for_elements);\n  thrust::fill_n(d_input_ref.begin(), offset_for_elements, 0);\n\n  c2h::device_vector<type> d_output(d_input.size() + offset_for_elements);\n  thrust::fill_n(d_output.begin(), offset_for_elements, 0);\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()) + offset_for_elements,\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input_ref == d_output);\n}\n#endif\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/block/block_store.cuh>\n#include <cub/iterator/cache_modified_output_iterator.cuh>\n#include <cub/util_allocator.cuh>\n#include <cub/util_arch.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\ntemplate <int ItemsPerThread, int ThreadsInBlock, hipcub::BlockStoreAlgorithm /* StoreAlgorithm */>\nstruct output_idx\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) * ItemsPerThread + item;\n  }\n};\n\ntemplate <int ItemsPerThread, int ThreadsInBlock>\nstruct output_idx<ItemsPerThread, ThreadsInBlock, hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED>\n{\n  static __device__ int get(int item)\n  {\n    return static_cast<int>(threadIdx.x) + ThreadsInBlock * item;\n  }\n};\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm StoreAlgorithm>\n__global__ void kernel(std::integral_constant<bool, true>, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t       = hipcub::detail::it_value_t<InputIteratorT>;\n  using block_store_t = hipcub::BlockStore<input_t, ThreadsInBlock, ItemsPerThread, StoreAlgorithm>;\n  using storage_t     = typename block_store_t::TempStorage;\n\n  __shared__ storage_t storage;\n  block_store_t block_store(storage);\n\n  input_t data[ItemsPerThread];\n\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = output_idx<ItemsPerThread, ThreadsInBlock, StoreAlgorithm>::get(i);\n\n    if (idx < num_items)\n    {\n      data[i] = input[idx];\n    }\n  }\n\n  if (ItemsPerThread * ThreadsInBlock == num_items)\n  {\n    block_store.Store(output, data);\n  }\n  else\n  {\n    block_store.Store(output, data, num_items);\n  }\n}\n\ntemplate <typename InputIteratorT,\n          typename OutputIteratorT,\n          int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm /* StoreAlgorithm */>\n__global__ void kernel(std::integral_constant<bool, false>, InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  for (int i = 0; i < ItemsPerThread; i++)\n  {\n    const int idx = output_idx<ItemsPerThread, ThreadsInBlock, hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT>::get(i);\n\n    if (idx < num_items)\n    {\n      output[idx] = input[idx];\n    }\n  }\n}\n\ntemplate <int ItemsPerThread,\n          int ThreadsInBlock,\n          hipcub::BlockStoreAlgorithm StoreAlgorithm,\n          typename InputIteratorT,\n          typename OutputIteratorT>\nvoid block_store(InputIteratorT input, OutputIteratorT output, int num_items)\n{\n  using input_t                       = hipcub::detail::it_value_t<InputIteratorT>;\n  using block_store_t                 = hipcub::BlockStore<input_t, ThreadsInBlock, ItemsPerThread, StoreAlgorithm>;\n  using storage_t                     = typename block_store_t::TempStorage;\n  constexpr bool sufficient_resources = sizeof(storage_t) <= hipcub::detail::max_smem_per_block;\n\n  kernel<InputIteratorT, OutputIteratorT, ItemsPerThread, ThreadsInBlock, StoreAlgorithm>\n    <<<1, ThreadsInBlock>>>(std::integral_constant<bool, sufficient_resources>{}, input, output, num_items);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n// %PARAM% IPT it 1:11\n\nusing types     = c2h::type_list<std::uint8_t, std::int32_t, std::int64_t>;\nusing vec_types = c2h::type_list<long2, double2>;\n\nusing even_threads_in_block = c2h::enum_type_list<int, 32, 128>;\nusing odd_threads_in_block  = c2h::enum_type_list<int, 15, 65>;\nusing a_block_size          = c2h::enum_type_list<int, 256>;\n\nusing items_per_thread = c2h::enum_type_list<int, IPT>;\nusing store_algorithm =\n  c2h::enum_type_list<hipcub::BlockStoreAlgorithm,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED>;\n\nusing odd_store_algorithm =\n  c2h::enum_type_list<hipcub::BlockStoreAlgorithm,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_DIRECT,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_STRIPED,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE,\n                      hipcub::BlockStoreAlgorithm::BLOCK_STORE_TRANSPOSE>;\n\ntemplate <class TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int items_per_thread                     = c2h::get<1, TestType>::value;\n  static constexpr int threads_in_block                     = c2h::get<2, TestType>::value;\n  static constexpr int tile_size                            = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm = c2h::get<3, TestType>::value;\n};\n\nC2H_TEST(\"Block store works with even block sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         even_threads_in_block,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nC2H_TEST(\"Block store works with even odd sizes\",\n         \"[store][block]\",\n         types,\n         items_per_thread,\n         odd_threads_in_block,\n         odd_store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n// WAR bug in vec type handling in NVCC 12.0 + GCC 11.4 + C++20\n#if !(_CCCL_CUDA_COMPILER(NVCC, ==, 12, 0) && _CCCL_COMPILER(GCC, ==, 11, 4) && _CCCL_STD_VER == 2020)\nC2H_TEST(\"Block store works with even vector types\",\n         \"[store][block]\",\n         vec_types,\n         items_per_thread,\n         a_block_size,\n         store_algorithm)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, params::tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<params::items_per_thread, params::threads_in_block, params::store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n#endif // !(NVCC 12.0 and GCC 11.4 and C++20)\n\nC2H_TEST(\"Block store works with custom types\", \"[store][block]\", items_per_thread, store_algorithm)\n{\n  using type                                                = c2h::custom_type_t<c2h::equal_comparable_t>;\n  constexpr int items_per_thread                            = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()),\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\nC2H_TEST(\"Block store works with caching iterators\", \"[store][block]\", items_per_thread, store_algorithm)\n{\n  using type                                                = int;\n  constexpr int items_per_thread                            = c2h::get<0, TestType>::value;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm = c2h::get<1, TestType>::value;\n\n  c2h::device_vector<type> d_input(GENERATE_COPY(take(10, random(0, tile_size))));\n  c2h::gen(C2H_SEED(10), d_input);\n\n  c2h::device_vector<type> d_output(d_input.size());\n  hipcub::CacheModifiedOutputIterator<hipcub::CacheStoreModifier::STORE_DEFAULT, type> out(\n    thrust::raw_pointer_cast(d_output.data()));\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()), out, static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input == d_output);\n}\n\n#if IPT == 1\nC2H_TEST(\"Vectorized block store with different alignment cases\", \"[store][block]\", c2h::enum_type_list<int, 1, 4, 7>)\n{\n  using type                     = int;\n  constexpr int items_per_thread = c2h::get<0, TestType>::value;\n  ;\n  constexpr int threads_in_block                            = 64;\n  constexpr int tile_size                                   = items_per_thread * threads_in_block;\n  static constexpr hipcub::BlockStoreAlgorithm store_algorithm = hipcub::BlockStoreAlgorithm::BLOCK_STORE_VECTORIZE;\n  const int offset_for_elements                             = GENERATE_COPY(0, 1, 2, 3, 4);\n\n  c2h::device_vector<type> d_input(tile_size);\n  c2h::gen(C2H_SEED(2), d_input);\n\n  CAPTURE(offset_for_elements, tile_size);\n  c2h::device_vector<type> d_input_ref(tile_size + offset_for_elements);\n  thrust::copy_n(d_input.begin(), tile_size, d_input_ref.begin() + offset_for_elements);\n  thrust::fill_n(d_input_ref.begin(), offset_for_elements, 0);\n\n  c2h::device_vector<type> d_output(d_input.size() + offset_for_elements);\n  thrust::fill_n(d_output.begin(), offset_for_elements, 0);\n\n  block_store<items_per_thread, threads_in_block, store_algorithm>(\n    thrust::raw_pointer_cast(d_input.data()),\n    thrust::raw_pointer_cast(d_output.data()) + offset_for_elements,\n    static_cast<int>(d_input.size()));\n\n  REQUIRE(d_input_ref == d_output);\n}\n#endif\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_block_store.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cub/thread/thread_scan.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"catch2_test_device_scan.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Scan Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename Accum, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel(\n  In d_in, Out d_out, ScanOperator scan_operator, int valid_items, Accum prefix, bool apply_prefix, Accum filler)\n{\n  using value_t  = cuda::std::iter_value_t<In>;\n  using output_t = cuda::std::iter_value_t<Out>;\n  value_t thread_input[NumItems];\n  output_t thread_output[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_input[i]  = d_in[i];\n    thread_output[i] = static_cast<output_t>(filler);\n  }\n  cub::detail::ThreadScanExclusivePartial(thread_input, thread_output, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_output[i];\n  }\n}\n\n// The following kernels are less general/complex with the added benefit that we can test doing the scan in-place\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_array(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cub::detail::ThreadScanExclusivePartial(thread_data, thread_data, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_span(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  cub::detail::ThreadScanExclusivePartial(span, span, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_mdspan(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  cub::detail::ThreadScanExclusivePartial(mdspan, mdspan, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  type_pair<half_t>,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  type_pair<bfloat16_t>\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<type_pair<cuda::std::uint8_t, cuda::std::int32_t>,\n                 type_pair<cuda::std::int16_t>,\n                 type_pair<cuda::std::uint32_t>,\n                 type_pair<cuda::std::int64_t>>;\n\nusing fp_type_list = c2h::type_list<type_pair<float>, type_pair<double>>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_and<>, cuda::minimum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::maximum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadScanExclusive Integral Type Tests\",\n         \"[scan][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using params                     = params_t<TestType>;\n  using value_t                    = typename params::item_t;\n  using output_t                   = typename params::output_t;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param          = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op           = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, static_cast<output_t>(filler));\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\nC2H_TEST(\"ThreadScanExclusive Floating-Point Type Tests\",\n         \"[scan][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<value_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<value_t> reference_result(num_items, filler);\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanExclusive Narrow PrecisionType Tests\",\n         \"[scan][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  auto prefix =\n    static_cast<accum_t>(GENERATE_COPY(take(1, random(float{dist_param::min()}, float{dist_param::max()}))));\n  const bool apply_prefix = GENERATE(true, false);\n  auto filler =\n    static_cast<accum_t>(GENERATE(take(1, random(float{filler_dist_param::min()}, float{filler_dist_param::max()}))));\n  CAPTURE(\n    c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix, operator_identity);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    scan_op,\n    valid_items,\n    *unwrap_it(&prefix),\n    apply_prefix,\n    *unwrap_it(&filler));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanExclusive Container Tests\", \"[scan][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size, thrust::no_init);\n  c2h::device_vector<int> d_out(max_size, thrust::no_init);\n  using dist_param = dist_interval<int, cuda::std::plus<>, max_size>;\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, max_size - 2)),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size - 1, max_size, max_size + 1}));\n  const int bounded_valid_items          = cuda::std::min(valid_items, max_size);\n  c2h::host_vector<int> reference_result = h_in;\n  compute_exclusive_scan_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reference_result.begin(), 0, cuda::std::plus<>{});\n\n  thread_scan_exclusive_partial_kernel_array<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_exclusive_partial_kernel_span<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n#if _CCCL_STD_VER >= 2023\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_exclusive_partial_kernel_mdspan<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadScanExclusive Invalid Test\", \"[scan][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  const bool apply_prefix       = GENERATE(true, false);\n  // Invalid prefix when !apply_prefix\n  const segment prefix{0, apply_prefix ? 1 : 0};\n  CAPTURE(valid_items, prefix, apply_prefix);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  c2h::host_vector<segment> reference_result(max_size);\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : segment{1, 1},\n    merge_segments_op{nullptr});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_scan_exclusive_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items,\n    prefix,\n    apply_prefix,\n    segment{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  if (!apply_prefix && valid_items > 0)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cub/thread/thread_scan.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"catch2_test_device_scan.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Scan Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename Accum, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel(\n  In d_in, Out d_out, ScanOperator scan_operator, int valid_items, Accum prefix, bool apply_prefix, Accum filler)\n{\n  using value_t  = cuda::std::iter_value_t<In>;\n  using output_t = cuda::std::iter_value_t<Out>;\n  value_t thread_input[NumItems];\n  output_t thread_output[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_input[i]  = d_in[i];\n    thread_output[i] = static_cast<output_t>(filler);\n  }\n  hipcub::detail::ThreadScanExclusivePartial(thread_input, thread_output, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_output[i];\n  }\n}\n\n// The following kernels are less general/complex with the added benefit that we can test doing the scan in-place\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_array(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  hipcub::detail::ThreadScanExclusivePartial(thread_data, thread_data, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_span(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  hipcub::detail::ThreadScanExclusivePartial(span, span, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ScanOperator>\n__global__ void thread_scan_exclusive_partial_kernel_mdspan(\n  const T* d_in, T* d_out, ScanOperator scan_operator, int valid_items, T prefix, bool apply_prefix)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  hipcub::detail::ThreadScanExclusivePartial(mdspan, mdspan, scan_operator, valid_items, prefix, apply_prefix);\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    d_out[i] = thread_data[i];\n  }\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  type_pair<half_t>,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  type_pair<bfloat16_t>\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<type_pair<cuda::std::uint8_t, cuda::std::int32_t>,\n                 type_pair<cuda::std::int16_t>,\n                 type_pair<cuda::std::uint32_t>,\n                 type_pair<cuda::std::int64_t>>;\n\nusing fp_type_list = c2h::type_list<type_pair<float>, type_pair<double>>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_and<>, cuda::minimum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::maximum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadScanExclusive Integral Type Tests\",\n         \"[scan][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using params                     = params_t<TestType>;\n  using value_t                    = typename params::item_t;\n  using output_t                   = typename params::output_t;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param          = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op           = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, static_cast<output_t>(filler));\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\nC2H_TEST(\"ThreadScanExclusive Floating-Point Type Tests\",\n         \"[scan][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  const accum_t prefix          = GENERATE_COPY(take(1, random(dist_param::min(), dist_param::max())));\n  const bool apply_prefix       = GENERATE(true, false);\n  const accum_t filler          = GENERATE(take(1, random(filler_dist_param::min(), filler_dist_param::max())));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<value_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<value_t> reference_result(num_items, filler);\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    scan_op,\n    valid_items,\n    prefix,\n    apply_prefix,\n    filler);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanExclusive Narrow PrecisionType Tests\",\n         \"[scan][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using params                 = params_t<TestType>;\n  using value_t                = typename params::item_t;\n  using output_t               = typename params::output_t;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items, accum_t, output_t>;\n  using filler_dist_param      = dist_interval<accum_t, op_t, num_items, accum_t, output_t>;\n  constexpr auto scan_op       = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  const int bounded_valid_items = std::min(valid_items, num_items);\n  auto prefix =\n    static_cast<accum_t>(GENERATE_COPY(take(1, random(float{dist_param::min()}, float{dist_param::max()}))));\n  const bool apply_prefix = GENERATE(true, false);\n  auto filler =\n    static_cast<accum_t>(GENERATE(take(1, random(float{filler_dist_param::min()}, float{filler_dist_param::max()}))));\n  CAPTURE(\n    c2h::type_name<value_t>(), num_items, c2h::type_name<op_t>(), valid_items, prefix, apply_prefix, operator_identity);\n\n  c2h::device_vector<value_t> d_in(num_items, thrust::no_init);\n  c2h::device_vector<output_t> d_out(num_items, thrust::no_init);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  c2h::host_vector<output_t> reference_result(num_items, filler);\n\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : operator_identity,\n    scan_op);\n\n  thread_scan_exclusive_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    scan_op,\n    valid_items,\n    *unwrap_it(&prefix),\n    apply_prefix,\n    *unwrap_it(&filler));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  if (!apply_prefix)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadScanExclusive Container Tests\", \"[scan][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size, thrust::no_init);\n  c2h::device_vector<int> d_out(max_size, thrust::no_init);\n  using dist_param = dist_interval<int, cuda::std::plus<>, max_size>;\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, max_size - 2)),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size - 1, max_size, max_size + 1}));\n  const int bounded_valid_items          = cuda::std::min(valid_items, max_size);\n  c2h::host_vector<int> reference_result = h_in;\n  compute_exclusive_scan_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reference_result.begin(), 0, cuda::std::plus<>{});\n\n  thread_scan_exclusive_partial_kernel_array<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_exclusive_partial_kernel_span<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n\n#if _CCCL_STD_VER >= 2023\n  thrust::fill(d_out.begin(), d_out.end(), 0);\n  thread_scan_exclusive_partial_kernel_mdspan<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    cuda::std::plus<>{},\n    valid_items,\n    0,\n    true);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == d_out);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadScanExclusive Invalid Test\", \"[scan][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  const bool apply_prefix       = GENERATE(true, false);\n  // Invalid prefix when !apply_prefix\n  const segment prefix{0, apply_prefix ? 1 : 0};\n  CAPTURE(valid_items, prefix, apply_prefix);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  c2h::host_vector<segment> reference_result(max_size);\n  compute_exclusive_scan_reference(\n    h_in.cbegin(),\n    h_in.cbegin() + bounded_valid_items,\n    reference_result.begin(),\n    apply_prefix ? prefix : segment{1, 1},\n    merge_segments_op{nullptr});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_scan_exclusive_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items,\n    prefix,\n    apply_prefix,\n    segment{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  if (!apply_prefix && valid_items > 0)\n  {\n    // Undefined for exclusive scan\n    reference_result.front() = d_out.front();\n  }\n  REQUIRE(reference_result == d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/catch2_test_thread_scan_exclusive_partial.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/detail/type_traits.cuh>\n#include <cub/thread/thread_reduce.cuh>\n#include <cub/util_macro.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <cstring>\n#include <functional>\n#include <limits>\n#include <numeric>\n\n#include \"c2h/catch2_test_helper.h\"\n#include \"c2h/extended_types.h\"\n#include \"c2h/generators.h\"\n#include <catch2/matchers/catch_matchers_floating_point.hpp>\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel(const T* __restrict__ d_in, T* __restrict__ d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n#pragma unroll\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = cub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  cuda::std::array<T, NUM_ITEMS> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = cub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_span(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NUM_ITEMS> span(thread_data);\n  *d_out = cub::ThreadReduce(span, reduce_operator);\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_mdspan(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NUM_ITEMS>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NUM_ITEMS>{});\n  *d_out = cub::ThreadReduce(mdspan, reduce_operator);\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * CUB operator to STD operator\n **********************************************************************************************************************/\n\ntemplate <typename T, typename>\nstruct cub_operator_to_std;\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::plus<>>\n{\n  using type = ::std::plus<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::multiplies<>>\n{\n  using type = ::std::multiplies<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_and<>>\n{\n  using type = ::std::bit_and<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_or<>>\n{\n  using type = ::std::bit_or<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_xor<>>\n{\n  using type = ::std::bit_xor<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::minimum<>>\n{\n  using type = cuda::minimum<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::maximum<>>\n{\n  using type = cuda::maximum<>;\n};\n\ntemplate <typename T, typename Operator>\nusing cub_operator_to_std_t = typename cub_operator_to_std<T, Operator>::type;\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  __half,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  __nv_bfloat16\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<cuda::std::int8_t, cuda::std::int16_t, cuda::std::uint16_t, cuda::std::int32_t, cuda::std::int64_t>;\n\nusing fp_type_list = c2h::type_list<float, double>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>,\n                 cuda::std::multiplies<>,\n                 cuda::std::bit_and<>,\n                 cuda::std::bit_or<>,\n                 cuda::std::bit_xor<>,\n                 cuda::minimum<>,\n                 cuda::maximum<>>;\n\nusing cub_operator_fp_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>, cuda::maximum<>>;\n\n/***********************************************************************************************************************\n * Verify results and kernel launch\n **********************************************************************************************************************/\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((cuda::std::is_floating_point_v<T>) )\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE_THAT(expected_data, Catch::Matchers::WithinRel(test_results, T{0.05}));\n}\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((!cuda::std::is_floating_point_v<T>) )\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\ntemplate <typename T, typename ReduceOperator>\nvoid run_thread_reduce_kernel(\n  int num_items, const c2h::device_vector<T>& in, c2h::device_vector<T>& out, ReduceOperator reduce_operator)\n{\n  switch (num_items)\n  {\n    case 1:\n      thread_reduce_kernel<1>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 2:\n      thread_reduce_kernel<2>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 3:\n      thread_reduce_kernel<3>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 4:\n      thread_reduce_kernel<4>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 5:\n      thread_reduce_kernel<5>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 6:\n      thread_reduce_kernel<6>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 7:\n      thread_reduce_kernel<7>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 8:\n      thread_reduce_kernel<8>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 9:\n      thread_reduce_kernel<9>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 10:\n      thread_reduce_kernel<10>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 11:\n      thread_reduce_kernel<11>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 12:\n      thread_reduce_kernel<12>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 13:\n      thread_reduce_kernel<13>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 14:\n      thread_reduce_kernel<14>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 15:\n      thread_reduce_kernel<15>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 16:\n      thread_reduce_kernel<16>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    default:\n      FAIL(\"Unsupported number of items\");\n  }\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 10;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\", \"[reduce][thread]\", integral_type_list, cub_operator_integral_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto std_reduce_op     = cub_operator_to_std_t<value_t, op_t>{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), max_size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    auto reference_result = std::accumulate(h_in.begin(), h_in.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n  }\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\", \"[reduce][thread]\", fp_type_list, cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<value_t, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), max_size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    auto reference_result = std::accumulate(h_in.begin(), h_in.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n  }\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<float, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, float>();\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, value_t{1.0f}, value_t{2.0f});\n  c2h::host_vector<float> h_in_float = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>());\n    auto reference_result =\n      std::accumulate(h_in_float.begin(), h_in_float.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, float{c2h::host_vector<value_t>(d_out)[0]});\n  }\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Container Tests\", \"[reduce][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size);\n  c2h::device_vector<int> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in);\n  c2h::host_vector<int> h_in = d_in;\n  auto reference_result      = std::accumulate(h_in.begin(), h_in.end(), 0, std::plus<int>{});\n\n  thread_reduce_kernel_array<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n\n  thread_reduce_kernel_span<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n\n#if _CCCL_STD_VER >= 2023\n  thread_reduce_kernel_mdspan<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n#endif // _CCCL_STD_VER >= 2023\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/detail/type_traits.cuh>\n#include <cub/thread/thread_reduce.cuh>\n#include <cub/util_macro.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <cstring>\n#include <functional>\n#include <limits>\n#include <numeric>\n\n#include \"c2h/catch2_test_helper.h\"\n#include \"c2h/extended_types.h\"\n#include \"c2h/generators.h\"\n#include <catch2/matchers/catch_matchers_floating_point.hpp>\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel(const T* __restrict__ d_in, T* __restrict__ d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n#pragma unroll\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = hipcub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  cuda::std::array<T, NUM_ITEMS> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = hipcub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_span(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NUM_ITEMS> span(thread_data);\n  *d_out = hipcub::ThreadReduce(span, reduce_operator);\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_mdspan(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NUM_ITEMS>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NUM_ITEMS>{});\n  *d_out = hipcub::ThreadReduce(mdspan, reduce_operator);\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * CUB operator to STD operator\n **********************************************************************************************************************/\n\ntemplate <typename T, typename>\nstruct cub_operator_to_std;\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::plus<>>\n{\n  using type = ::std::plus<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::multiplies<>>\n{\n  using type = ::std::multiplies<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_and<>>\n{\n  using type = ::std::bit_and<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_or<>>\n{\n  using type = ::std::bit_or<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_xor<>>\n{\n  using type = ::std::bit_xor<T>; // T: MSVC complains about possible loss of data\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::minimum<>>\n{\n  using type = cuda::minimum<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::maximum<>>\n{\n  using type = cuda::maximum<>;\n};\n\ntemplate <typename T, typename Operator>\nusing cub_operator_to_std_t = typename cub_operator_to_std<T, Operator>::type;\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  __half,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  __hip_bfloat16\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<cuda::std::int8_t, cuda::std::int16_t, cuda::std::uint16_t, cuda::std::int32_t, cuda::std::int64_t>;\n\nusing fp_type_list = c2h::type_list<float, double>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>,\n                 cuda::std::multiplies<>,\n                 cuda::std::bit_and<>,\n                 cuda::std::bit_or<>,\n                 cuda::std::bit_xor<>,\n                 cuda::minimum<>,\n                 cuda::maximum<>>;\n\nusing cub_operator_fp_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>, cuda::maximum<>>;\n\n/***********************************************************************************************************************\n * Verify results and kernel launch\n **********************************************************************************************************************/\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((cuda::std::is_floating_point_v<T>) )\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE_THAT(expected_data, Catch::Matchers::WithinRel(test_results, T{0.05}));\n}\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((!cuda::std::is_floating_point_v<T>) )\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\ntemplate <typename T, typename ReduceOperator>\nvoid run_thread_reduce_kernel(\n  int num_items, const c2h::device_vector<T>& in, c2h::device_vector<T>& out, ReduceOperator reduce_operator)\n{\n  switch (num_items)\n  {\n    case 1:\n      thread_reduce_kernel<1>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 2:\n      thread_reduce_kernel<2>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 3:\n      thread_reduce_kernel<3>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 4:\n      thread_reduce_kernel<4>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 5:\n      thread_reduce_kernel<5>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 6:\n      thread_reduce_kernel<6>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 7:\n      thread_reduce_kernel<7>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 8:\n      thread_reduce_kernel<8>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 9:\n      thread_reduce_kernel<9>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 10:\n      thread_reduce_kernel<10>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 11:\n      thread_reduce_kernel<11>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 12:\n      thread_reduce_kernel<12>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 13:\n      thread_reduce_kernel<13>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 14:\n      thread_reduce_kernel<14>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 15:\n      thread_reduce_kernel<15>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    case 16:\n      thread_reduce_kernel<16>\n        <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n      break;\n    default:\n      FAIL(\"Unsupported number of items\");\n  }\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nconstexpr int max_size  = 16;\nconstexpr int num_seeds = 10;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\", \"[reduce][thread]\", integral_type_list, cub_operator_integral_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto std_reduce_op     = cub_operator_to_std_t<value_t, op_t>{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), max_size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    auto reference_result = std::accumulate(h_in.begin(), h_in.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n  }\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\", \"[reduce][thread]\", fp_type_list, cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<value_t, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), max_size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, cuda::std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    auto reference_result = std::accumulate(h_in.begin(), h_in.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n  }\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<float, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, float>();\n  c2h::device_vector<value_t> d_in(max_size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, value_t{1.0f}, value_t{2.0f});\n  c2h::host_vector<float> h_in_float = d_in;\n  for (int num_items = 1; num_items <= max_size; ++num_items)\n  {\n    CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>());\n    auto reference_result =\n      std::accumulate(h_in_float.begin(), h_in_float.begin() + num_items, operator_identity, std_reduce_op);\n    run_thread_reduce_kernel(num_items, d_in, d_out, reduce_op);\n    verify_results(reference_result, float{c2h::host_vector<value_t>(d_out)[0]});\n  }\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Container Tests\", \"[reduce][thread]\")\n{\n  c2h::device_vector<int> d_in(max_size);\n  c2h::device_vector<int> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in);\n  c2h::host_vector<int> h_in = d_in;\n  auto reference_result      = std::accumulate(h_in.begin(), h_in.end(), 0, std::plus<int>{});\n\n  thread_reduce_kernel_array<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n\n  thread_reduce_kernel_span<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n\n#if _CCCL_STD_VER >= 2023\n  thread_reduce_kernel_mdspan<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), cuda::std::plus<>{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  verify_results(reference_result, c2h::host_vector<int>(d_out)[0]);\n#endif // _CCCL_STD_VER >= 2023\n}\n", "source": "/kaggle/working/cccl/cub/test/thread_reduce/catch2_test_thread_reduce.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n// #define CCCL_CHECK_SASS\n\n#if defined(CCCL_CHECK_SASS)\n\n#  include <cub/detail/type_traits.cuh>\n#  include <cub/thread/thread_reduce.cuh>\n#  include <cub/util_macro.cuh>\n\n#  include <cuda/functional>\n#  include <cuda/std/functional>\n#  include <cuda/std/limits>\n#  include <cuda/std/type_traits>\n\n#  include <cstring>\n#  include <functional>\n#  include <limits>\n#  include <numeric>\n\n#  include \"c2h/catch2_test_helper.h\"\n#  include \"c2h/extended_types.h\"\n#  include \"c2h/generators.h\"\n#  include <catch2/matchers/catch_matchers_floating_point.hpp>\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel(const T* __restrict__ d_in, T* __restrict__ d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n  auto d_in_aligned = static_cast<T*>(__builtin_assume_aligned(d_in, (sizeof(T) < 4) ? 4 : sizeof(T)));\n  ::memcpy(thread_data, d_in_aligned, sizeof(thread_data));\n  *d_out = cub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  cuda::std::array<T, NUM_ITEMS> thread_data;\n#  pragma unroll\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = cub::ThreadReduce(thread_data, reduce_operator);\n}\n\n/***********************************************************************************************************************\n * CUB operator to STD operator\n **********************************************************************************************************************/\n\ntemplate <typename T, typename>\nstruct cub_operator_to_std;\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::plus<>>\n{\n  using type = ::std::plus<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::multiplies<>>\n{\n  using type = ::std::multiplies<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_xor<>>\n{\n  using type = ::std::bit_xor<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::minimum<>>\n{\n  using type = cuda::minimum<>;\n};\n\ntemplate <typename T, typename Operator>\nusing cub_operator_to_std_t = typename cub_operator_to_std<T, Operator>::type;\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#  if TEST_HALF_T()\n  __half,\n#  endif // TEST_HALF_T()\n#  if TEST_BF_T()\n  __nv_bfloat16\n#  endif // TEST_BF_T()\n  >;\n\nusing fp_type_list = c2h::type_list<float>;\n\nusing integral_type_list = c2h::type_list<cuda::std::int8_t, cuda::std::int16_t, cuda::std::int32_t>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_xor<>, cuda::minimum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::minimum<>>;\n\n/***********************************************************************************************************************\n * Verify results and kernel launch\n **********************************************************************************************************************/\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((cuda::std::is_floating_point<T>::value))\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE_THAT(expected_data, Catch::Matchers::WithinRel(test_results, T{0.05}));\n}\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((!cuda::std::is_floating_point<T>::value))\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\ntemplate <typename T, typename ReduceOperator>\nvoid run_thread_reduce_kernel(\n  const c2h::device_vector<T>& in, c2h::device_vector<T>& out, ReduceOperator reduce_operator)\n{\n  thread_reduce_kernel<18>\n    <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nconstexpr int size = 16;\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\", \"[reduce][thread]\", integral_type_list, cub_operator_integral_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto std_reduce_op     = cub_operator_to_std_t<value_t, op_t>{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  auto reference_result          = std::accumulate(h_in.begin(), h_in.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\", \"[reduce][thread]\", fp_type_list, cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<value_t, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  auto reference_result          = std::accumulate(h_in.begin(), h_in.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n}\n\n#  if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<float, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, float>();\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, value_t{1.0f}, value_t{2.0f});\n  c2h::host_vector<float> h_in_float = d_in;\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  auto reference_result =\n    std::accumulate(h_in_float.begin(), h_in_float.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, float{c2h::host_vector<value_t>(d_out)[0]});\n}\n\n#  endif // TEST_HALF_T() || TEST_BF_T()\n\n#else\n\n#  include \"c2h/catch2_test_helper.h\"\n\nC2H_TEST(\"ThreadReduce Empty Test\", \"[reduce][thread][empty]\") {}\n\n#endif // CCCL_CHECK_SASS\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n// #define CCCL_CHECK_SASS\n\n#include \"hip/hip_runtime.h\"\n#if defined(CCCL_CHECK_SASS)\n\n#  include <cub/detail/type_traits.cuh>\n#  include <cub/thread/thread_reduce.cuh>\n#  include <cub/util_macro.cuh>\n\n#  include <cuda/functional>\n#  include <cuda/std/functional>\n#  include <cuda/std/limits>\n#  include <cuda/std/type_traits>\n\n#  include <cstring>\n#  include <functional>\n#  include <limits>\n#  include <numeric>\n\n#  include \"c2h/catch2_test_helper.h\"\n#  include \"c2h/extended_types.h\"\n#  include \"c2h/generators.h\"\n#  include <catch2/matchers/catch_matchers_floating_point.hpp>\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel(const T* __restrict__ d_in, T* __restrict__ d_out, ReduceOperator reduce_operator)\n{\n  T thread_data[NUM_ITEMS];\n  auto d_in_aligned = static_cast<T*>(__builtin_assume_aligned(d_in, (sizeof(T) < 4) ? 4 : sizeof(T)));\n  ::memcpy(thread_data, d_in_aligned, sizeof(thread_data));\n  *d_out = hipcub::ThreadReduce(thread_data, reduce_operator);\n}\n\ntemplate <int NUM_ITEMS, typename T, typename ReduceOperator>\n__global__ void thread_reduce_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator)\n{\n  cuda::std::array<T, NUM_ITEMS> thread_data;\n#  pragma unroll\n  for (int i = 0; i < NUM_ITEMS; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = hipcub::ThreadReduce(thread_data, reduce_operator);\n}\n\n/***********************************************************************************************************************\n * CUB operator to STD operator\n **********************************************************************************************************************/\n\ntemplate <typename T, typename>\nstruct cub_operator_to_std;\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::plus<>>\n{\n  using type = ::std::plus<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::multiplies<>>\n{\n  using type = ::std::multiplies<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::std::bit_xor<>>\n{\n  using type = ::std::bit_xor<>;\n};\n\ntemplate <typename T>\nstruct cub_operator_to_std<T, cuda::minimum<>>\n{\n  using type = cuda::minimum<>;\n};\n\ntemplate <typename T, typename Operator>\nusing cub_operator_to_std_t = typename cub_operator_to_std<T, Operator>::type;\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#  if TEST_HALF_T()\n  __half,\n#  endif // TEST_HALF_T()\n#  if TEST_BF_T()\n  __hip_bfloat16\n#  endif // TEST_BF_T()\n  >;\n\nusing fp_type_list = c2h::type_list<float>;\n\nusing integral_type_list = c2h::type_list<cuda::std::int8_t, cuda::std::int16_t, cuda::std::int32_t>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_xor<>, cuda::minimum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::minimum<>>;\n\n/***********************************************************************************************************************\n * Verify results and kernel launch\n **********************************************************************************************************************/\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((cuda::std::is_floating_point<T>::value))\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE_THAT(expected_data, Catch::Matchers::WithinRel(test_results, T{0.05}));\n}\n\n_CCCL_TEMPLATE(typename T)\n_CCCL_REQUIRES((!cuda::std::is_floating_point<T>::value))\nvoid verify_results(const T& expected_data, const T& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\ntemplate <typename T, typename ReduceOperator>\nvoid run_thread_reduce_kernel(\n  const c2h::device_vector<T>& in, c2h::device_vector<T>& out, ReduceOperator reduce_operator)\n{\n  thread_reduce_kernel<18>\n    <<<1, 1>>>(thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), reduce_operator);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nconstexpr int size = 16;\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\", \"[reduce][thread]\", integral_type_list, cub_operator_integral_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto std_reduce_op     = cub_operator_to_std_t<value_t, op_t>{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  auto reference_result          = std::accumulate(h_in.begin(), h_in.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\", \"[reduce][thread]\", fp_type_list, cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<value_t, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, value_t>();\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, std::numeric_limits<value_t>::min());\n  c2h::host_vector<value_t> h_in = d_in;\n  auto reference_result          = std::accumulate(h_in.begin(), h_in.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, c2h::host_vector<value_t>(d_out)[0]);\n}\n\n#  if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  constexpr auto reduce_op     = op_t{};\n  constexpr auto std_reduce_op = cub_operator_to_std_t<float, op_t>{};\n  const auto operator_identity = cuda::identity_element<op_t, float>();\n  c2h::device_vector<value_t> d_in(size);\n  c2h::device_vector<value_t> d_out(1);\n  c2h::gen(C2H_SEED(1), d_in, value_t{1.0f}, value_t{2.0f});\n  c2h::host_vector<float> h_in_float = d_in;\n  CAPTURE(c2h::type_name<value_t>(), size, c2h::type_name<decltype(reduce_op)>());\n  auto reference_result =\n    std::accumulate(h_in_float.begin(), h_in_float.begin() + size, operator_identity, std_reduce_op);\n  run_thread_reduce_kernel(d_in, d_out, reduce_op);\n  verify_results(reference_result, float{c2h::host_vector<value_t>(d_out)[0]});\n}\n\n#  endif // TEST_HALF_T() || TEST_BF_T()\n\n#else\n\n#  include \"c2h/catch2_test_helper.h\"\n\nC2H_TEST(\"ThreadReduce Empty Test\", \"[reduce][thread][empty]\") {}\n\n#endif // CCCL_CHECK_SASS\n", "source": "/kaggle/working/cccl/cub/test/thread_reduce/catch2_test_thread_reduce_check_sass.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cub/detail/type_traits.cuh>\n#include <cub/thread/thread_reduce.cuh>\n#include <cub/util_macro.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n#include <catch2/matchers/catch_matchers_floating_point.hpp>\n\ninline constexpr int max_size  = 16;\ninline constexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename ReduceOperator>\n__global__ void thread_reduce_partial_kernel(In d_in, Out d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  using value_t = cuda::std::iter_value_t<In>;\n  value_t thread_data[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = cub::detail::ThreadReducePartial(thread_data, reduce_operator, valid_items);\n}\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = cub::detail::ThreadReducePartial(thread_data, reduce_operator, valid_items);\n}\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_span(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  *d_out = cub::detail::ThreadReducePartial(span, reduce_operator, valid_items);\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_mdspan(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  *d_out = cub::detail::ThreadReducePartial(mdspan, reduce_operator, valid_items);\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  half_t,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  bfloat16_t\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<cuda::std::int8_t, cuda::std::uint16_t, cuda::std::int32_t, cuda::std::uint64_t>;\n\nusing fp_type_list = c2h::type_list<float, double>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_or<>, cuda::maximum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\",\n         \"[reduce][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>(), valid_items);\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  const int bounded_valid_items  = cuda::std::min(valid_items, num_items);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n  thread_reduce_partial_kernel<num_items>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), reduce_op, valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\",\n         \"[reduce][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op     = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>(), valid_items);\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  const int bounded_valid_items  = cuda::std::min(valid_items, num_items);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n  thread_reduce_partial_kernel<num_items>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), reduce_op, valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op     = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  CAPTURE(h_in, dist_param::min(), dist_param::max());\n  CAPTURE(c2h::type_name<value_t>(), c2h::type_name<decltype(reduce_op)>(), valid_items, num_items, operator_identity);\n  const int bounded_valid_items = cuda::std::min(valid_items, num_items);\n  const value_t reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n\n  thread_reduce_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    reduce_op,\n    valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Container Tests\", \"[reduce][thread]\")\n{\n  using op_t       = cuda::std::plus<>;\n  using dist_param = dist_interval<int, op_t, max_size>;\n  c2h::device_vector<int> d_in(max_size);\n  c2h::device_vector<int> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, max_size - 2)),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size - 1, max_size, max_size + 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, op_t{}, 0);\n\n  thread_reduce_partial_kernel_array<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n\n  thread_reduce_partial_kernel_span<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n\n#if _CCCL_STD_VER >= 2023\n  thread_reduce_partial_kernel_mdspan<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadReducePartial does not invoke the reduction operator on invalid elements\", \"[reduce][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  CAPTURE(valid_items);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  auto reference_result            = compute_single_problem_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, merge_segments_op{nullptr}, segment{1, 1});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_reduce_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  if (valid_items > 0)\n  {\n    REQUIRE(reference_result == c2h::host_vector<segment>(d_out)[0]);\n  }\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cub/detail/type_traits.cuh>\n#include <cub/thread/thread_reduce.cuh>\n#include <cub/util_macro.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/__algorithm/clamp.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include \"catch2_test_device_reduce.cuh\"\n#include \"thread_reduce/catch2_test_thread_reduce_helper.cuh\"\n#include <c2h/catch2_test_helper.h>\n#include <c2h/extended_types.h>\n#include <c2h/generators.h>\n#include <c2h/operator.cuh>\n#include <catch2/matchers/catch_matchers_floating_point.hpp>\n\ninline constexpr int max_size  = 16;\ninline constexpr int num_seeds = 3;\n\n/***********************************************************************************************************************\n * Thread Reduce Wrapper Kernels\n **********************************************************************************************************************/\n\ntemplate <int NumItems, typename In, typename Out, typename ReduceOperator>\n__global__ void thread_reduce_partial_kernel(In d_in, Out d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  using value_t = cuda::std::iter_value_t<In>;\n  value_t thread_data[NumItems];\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = hipcub::detail::ThreadReducePartial(thread_data, reduce_operator, valid_items);\n}\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_array(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  cuda::std::array<T, NumItems> thread_data;\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  *d_out = hipcub::detail::ThreadReducePartial(thread_data, reduce_operator, valid_items);\n}\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_span(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  cuda::std::span<T, NumItems> span(thread_data);\n  *d_out = hipcub::detail::ThreadReducePartial(span, reduce_operator, valid_items);\n}\n\n#if _CCCL_STD_VER >= 2023\n\ntemplate <int NumItems, typename T, typename ReduceOperator>\n__global__ void\nthread_reduce_partial_kernel_mdspan(const T* d_in, T* d_out, ReduceOperator reduce_operator, int valid_items)\n{\n  T thread_data[NumItems];\n\n  _CCCL_PRAGMA_UNROLL_FULL()\n  for (int i = 0; i < NumItems; ++i)\n  {\n    thread_data[i] = d_in[i];\n  }\n  using Extent = cuda::std::extents<int, NumItems>;\n  cuda::std::mdspan<T, Extent> mdspan(thread_data, cuda::std::extents<int, NumItems>{});\n  *d_out = hipcub::detail::ThreadReducePartial(mdspan, reduce_operator, valid_items);\n}\n\n#endif // _CCCL_STD_VER >= 2023\n\n/***********************************************************************************************************************\n * Type list definition\n **********************************************************************************************************************/\n\nusing narrow_precision_type_list = c2h::type_list<\n#if TEST_HALF_T()\n  half_t,\n#endif // TEST_HALF_T()\n#if TEST_BF_T()\n  bfloat16_t\n#endif // TEST_BF_T()\n  >;\n\nusing integral_type_list =\n  c2h::type_list<cuda::std::int8_t, cuda::std::uint16_t, cuda::std::int32_t, cuda::std::uint64_t>;\n\nusing fp_type_list = c2h::type_list<float, double>;\n\nusing cub_operator_integral_list =\n  c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::std::bit_or<>, cuda::maximum<>>;\n\nusing cub_operator_fp_list = c2h::type_list<cuda::std::plus<>, cuda::std::multiplies<>, cuda::minimum<>>;\n\nstatic_assert(max_size > 4);\nusing items_per_thread_list = c2h::enum_type_list<int, 1, 3, max_size - 1, max_size>;\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"ThreadReduce Integral Type Tests\",\n         \"[reduce][thread]\",\n         integral_type_list,\n         cub_operator_integral_list,\n         items_per_thread_list)\n{\n  using value_t                    = c2h::get<0, TestType>;\n  using op_t                       = c2h::get<1, TestType>;\n  using accum_t                    = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items          = c2h::get<2, TestType>::value;\n  using dist_param                 = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op         = op_t{};\n  constexpr auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items            = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>(), valid_items);\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  const int bounded_valid_items  = cuda::std::min(valid_items, num_items);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n  thread_reduce_partial_kernel<num_items>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), reduce_op, valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\nC2H_TEST(\"ThreadReduce Floating-Point Type Tests\",\n         \"[reduce][thread]\",\n         fp_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op     = op_t{};\n  const auto operator_identity = cuda::identity_element<op_t, accum_t>();\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  CAPTURE(c2h::type_name<value_t>(), num_items, c2h::type_name<decltype(reduce_op)>(), valid_items);\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  const int bounded_valid_items  = cuda::std::min(valid_items, num_items);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n  thread_reduce_partial_kernel<num_items>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), reduce_op, valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\n#if TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Narrow PrecisionType Tests\",\n         \"[reduce][thread][narrow]\",\n         narrow_precision_type_list,\n         cub_operator_fp_list,\n         items_per_thread_list)\n{\n  using value_t                = c2h::get<0, TestType>;\n  using op_t                   = c2h::get<1, TestType>;\n  using accum_t                = cuda::std::__accumulator_t<op_t, value_t>;\n  constexpr int num_items      = c2h::get<2, TestType>::value;\n  using dist_param             = dist_interval<value_t, op_t, num_items>;\n  constexpr auto reduce_op     = unwrap_op(std::true_type{}, op_t{});\n  const auto operator_identity = identity_v<op_t, accum_t>;\n  const int valid_items        = GENERATE_COPY(\n    take(1, random(2, cuda::std::max(2, num_items - 1))),\n    take(1, random(num_items + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, num_items, num_items + 1}));\n  c2h::device_vector<value_t> d_in(num_items);\n  c2h::device_vector<accum_t> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<value_t> h_in = d_in;\n  CAPTURE(h_in, dist_param::min(), dist_param::max());\n  CAPTURE(c2h::type_name<value_t>(), c2h::type_name<decltype(reduce_op)>(), valid_items, num_items, operator_identity);\n  const int bounded_valid_items = cuda::std::min(valid_items, num_items);\n  const value_t reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, reduce_op, operator_identity);\n\n  thread_reduce_partial_kernel<num_items><<<1, 1>>>(\n    unwrap_it(thrust::raw_pointer_cast(d_in.data())),\n    unwrap_it(thrust::raw_pointer_cast(d_out.data())),\n    reduce_op,\n    valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<accum_t>(d_out)[0]);\n}\n\n#endif // TEST_HALF_T() || TEST_BF_T()\n\nC2H_TEST(\"ThreadReduce Container Tests\", \"[reduce][thread]\")\n{\n  using op_t       = cuda::std::plus<>;\n  using dist_param = dist_interval<int, op_t, max_size>;\n  c2h::device_vector<int> d_in(max_size);\n  c2h::device_vector<int> d_out(1);\n  c2h::gen(C2H_SEED(num_seeds), d_in, dist_param::min(), dist_param::max());\n  c2h::host_vector<int> h_in = d_in;\n  const int valid_items      = GENERATE_COPY(\n    take(1, random(2, max_size - 2)),\n    take(1, random(max_size + 2, cuda::std::numeric_limits<int>::max())),\n    values({1, max_size - 1, max_size, max_size + 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  auto reference_result =\n    compute_single_problem_reference(h_in.cbegin(), h_in.cbegin() + bounded_valid_items, op_t{}, 0);\n\n  thread_reduce_partial_kernel_array<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n\n  thread_reduce_partial_kernel_span<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n\n#if _CCCL_STD_VER >= 2023\n  thread_reduce_partial_kernel_mdspan<max_size>\n    <<<1, 1>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), op_t{}, valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(reference_result == c2h::host_vector<int>(d_out)[0]);\n#endif // _CCCL_STD_VER >= 2023\n}\n\nC2H_TEST(\"ThreadReducePartial does not invoke the reduction operator on invalid elements\", \"[reduce][thread]\")\n{\n  const auto in_it = cuda::make_transform_iterator(\n    thrust::make_zip_iterator(cuda::counting_iterator<segment::offset_t>{1},\n                              cuda::counting_iterator<segment::offset_t>{2}),\n    tuple_to_segment_op{});\n  const int valid_items = GENERATE_COPY(\n    take(3, random(2, max_size - 1)),\n    take(1, random(max_size + 1, cuda::std::numeric_limits<int>::max())),\n    values({-1, 0, 1}));\n  const int bounded_valid_items = cuda::std::clamp(valid_items, 0, max_size);\n  CAPTURE(valid_items);\n  // First initialize with invalid segments than overwrite the first valid_items\n  c2h::host_vector<segment> h_in(max_size);\n  thrust::copy(in_it, in_it + bounded_valid_items, h_in.begin());\n  c2h::device_vector<segment> d_in = h_in;\n  auto reference_result            = compute_single_problem_reference(\n    h_in.cbegin(), h_in.cbegin() + bounded_valid_items, merge_segments_op{nullptr}, segment{1, 1});\n\n  c2h::device_vector<segment> d_out(max_size);\n  c2h::device_vector<bool> error_flag(1, false);\n  thread_reduce_partial_kernel<max_size><<<1, 1>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    merge_segments_op{thrust::raw_pointer_cast(error_flag.data())},\n    valid_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  REQUIRE(error_flag.front() == false);\n  if (valid_items > 0)\n  {\n    REQUIRE(reference_result == c2h::host_vector<segment>(d_out)[0]);\n  }\n}\n", "source": "/kaggle/working/cccl/cub/test/thread_reduce/catch2_test_thread_reduce_partial.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n#include <cub/detail/integer_utils.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\n/***********************************************************************************************************************\n * TEST CASES\n **********************************************************************************************************************/\n\nusing integral_types =\n  c2h::type_list<int16_t,\n                 uint16_t,\n                 int32_t,\n                 uint32_t,\n                 int64_t,\n                 uint64_t\n#if TEST_INT128()\n                 ,\n                 __int128_t,\n                 __uint128_t\n#endif\n                 >;\n\nusing floating_point_types =\n  c2h::type_list<float,\n                 double\n// TODO(jfaibussowit): re-enable these, clang-tidy seems to choke on operator==() between\n// __half, __nv_bfloat16, and short below\n#if TEST_HALF_T() && !defined(_CCCL_CLANG_TIDY_INVOKED)\n                 ,\n                 __half\n#endif\n#if TEST_BF_T() && !defined(_CCCL_CLANG_TIDY_INVOKED)\n                 ,\n                 __nv_bfloat16\n#endif\n                 >;\n\nusing operator_types = c2h::type_list<cuda::minimum<>, cuda::maximum<>>;\n\ntemplate <typename T>\nstatic __global__ void test_int_kernel(const T* input, int num_items)\n{\n  auto i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < num_items)\n  {\n    auto value       = input[i];\n    auto [high, low] = cub::detail::split_integer(value);\n    static_assert(sizeof(high) == sizeof(T) / 2);\n    static_assert(sizeof(low) == sizeof(T) / 2);\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(high)>);\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(low)>);\n    auto result = cub::detail::merge_integers(high, low);\n    static_assert(sizeof(result) == sizeof(T));\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(result)>);\n    assert(value == result);\n  }\n}\n\nstatic __global__ void test_int_special_values_kernel()\n{\n  {\n    auto [high, low] = cub::detail::split_integer(0xAABBCCDD);\n    assert(high = 0xAABB);\n    assert(low = 0xCCDD);\n  }\n  {\n    auto [high, low] = cub::detail::split_integer(0xAABBCCDDEEFF1234);\n    assert(high = 0xAABBCCDD);\n    assert(low = 0xEEFF1234);\n  }\n}\n\ntemplate <typename Operation, typename T>\nstatic __device__ void test_floating_point(Operation op, T valueA, T valueB)\n{\n  auto int_valueA = cub::detail::floating_point_to_comparable_int(op, valueA);\n  auto int_valueB = cub::detail::floating_point_to_comparable_int(op, valueB);\n  static_assert(sizeof(int_valueA) == sizeof(T));\n  static_assert(sizeof(int_valueB) == sizeof(T));\n  assert(op(valueA, valueB) == op(int_valueA, int_valueB));\n  auto result_valueA = cub::detail::comparable_int_to_floating_point<T>(int_valueA);\n  auto result_valueB = cub::detail::comparable_int_to_floating_point<T>(int_valueB);\n  static_assert(cuda::std::is_same_v<T, decltype(result_valueA)>);\n  static_assert(cuda::std::is_same_v<T, decltype(result_valueB)>);\n  assert(valueA == result_valueA);\n  assert(valueB == result_valueB);\n}\n\ntemplate <typename Operation, typename T>\nstatic __global__ void test_float_kernel(Operation op, const T* input, int num_items)\n{\n  auto i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < num_items - 1)\n  {\n    test_floating_point(op, input[i], input[i + 1]);\n  }\n}\n\ntemplate <typename T, typename Operation>\nstatic __global__ void test_float_special_values_kernel(Operation op)\n{\n  test_floating_point(op, T{-0.0f}, T{0.0f});\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::max());\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::min());\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::min(), cuda::std::numeric_limits<T>::max());\n  test_floating_point(op, cuda::std::numeric_limits<T>::max(), cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::min(), cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::quiet_NaN(), cuda::std::numeric_limits<T>::quiet_NaN());\n}\n\nC2H_TEST(\"Split/Merge Integers\", \"[Split/Merge][Random]\", integral_types)\n{\n  using T              = c2h::get<0, TestType>;\n  const auto num_items = 1 << 16;\n  c2h::device_vector<T> d_in(num_items);\n  c2h::gen(C2H_SEED(1), d_in);\n  test_int_kernel<<<cuda::ceil_div(num_items, 256), 256>>>(thrust::raw_pointer_cast(d_in.data()), num_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  test_int_special_values_kernel<<<1, 1>>>();\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\nC2H_TEST(\n  \"Compare Floating-point with Integers\", \"[CompareFloatingPointWithInt][Random]\", operator_types, floating_point_types)\n{\n  using Op             = c2h::get<0, TestType>;\n  using T              = c2h::get<1, TestType>;\n  const auto num_items = 1 << 16;\n  c2h::device_vector<T> d_in(num_items);\n  c2h::gen(C2H_SEED(1), d_in);\n  test_float_kernel<<<cuda::ceil_div(num_items, 256), 256>>>(Op{}, thrust::raw_pointer_cast(d_in.data()), num_items);\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n  test_float_special_values_kernel<T><<<1, 1>>>(Op{});\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n#include \"hip/hip_runtime.h\"\n#include <cub/detail/integer_utils.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\n/***********************************************************************************************************************\n * TEST CASES\n **********************************************************************************************************************/\n\nusing integral_types =\n  c2h::type_list<int16_t,\n                 uint16_t,\n                 int32_t,\n                 uint32_t,\n                 int64_t,\n                 uint64_t\n#if TEST_INT128()\n                 ,\n                 __int128_t,\n                 __uint128_t\n#endif\n                 >;\n\nusing floating_point_types =\n  c2h::type_list<float,\n                 double\n// TODO(jfaibussowit): re-enable these, clang-tidy seems to choke on operator==() between\n// __half, __nv_bfloat16, and short below\n#if TEST_HALF_T() && !defined(_CCCL_CLANG_TIDY_INVOKED)\n                 ,\n                 __half\n#endif\n#if TEST_BF_T() && !defined(_CCCL_CLANG_TIDY_INVOKED)\n                 ,\n                 __hip_bfloat16\n#endif\n                 >;\n\nusing operator_types = c2h::type_list<cuda::minimum<>, cuda::maximum<>>;\n\ntemplate <typename T>\nstatic __global__ void test_int_kernel(const T* input, int num_items)\n{\n  auto i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < num_items)\n  {\n    auto value       = input[i];\n    auto [high, low] = hipcub::detail::split_integer(value);\n    static_assert(sizeof(high) == sizeof(T) / 2);\n    static_assert(sizeof(low) == sizeof(T) / 2);\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(high)>);\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(low)>);\n    auto result = hipcub::detail::merge_integers(high, low);\n    static_assert(sizeof(result) == sizeof(T));\n    static_assert(cuda::std::is_signed_v<T> == cuda::std::is_signed_v<decltype(result)>);\n    assert(value == result);\n  }\n}\n\nstatic __global__ void test_int_special_values_kernel()\n{\n  {\n    auto [high, low] = hipcub::detail::split_integer(0xAABBCCDD);\n    assert(high = 0xAABB);\n    assert(low = 0xCCDD);\n  }\n  {\n    auto [high, low] = hipcub::detail::split_integer(0xAABBCCDDEEFF1234);\n    assert(high = 0xAABBCCDD);\n    assert(low = 0xEEFF1234);\n  }\n}\n\ntemplate <typename Operation, typename T>\nstatic __device__ void test_floating_point(Operation op, T valueA, T valueB)\n{\n  auto int_valueA = hipcub::detail::floating_point_to_comparable_int(op, valueA);\n  auto int_valueB = hipcub::detail::floating_point_to_comparable_int(op, valueB);\n  static_assert(sizeof(int_valueA) == sizeof(T));\n  static_assert(sizeof(int_valueB) == sizeof(T));\n  assert(op(valueA, valueB) == op(int_valueA, int_valueB));\n  auto result_valueA = hipcub::detail::comparable_int_to_floating_point<T>(int_valueA);\n  auto result_valueB = hipcub::detail::comparable_int_to_floating_point<T>(int_valueB);\n  static_assert(cuda::std::is_same_v<T, decltype(result_valueA)>);\n  static_assert(cuda::std::is_same_v<T, decltype(result_valueB)>);\n  assert(valueA == result_valueA);\n  assert(valueB == result_valueB);\n}\n\ntemplate <typename Operation, typename T>\nstatic __global__ void test_float_kernel(Operation op, const T* input, int num_items)\n{\n  auto i = threadIdx.x + blockIdx.x * blockDim.x;\n  if (i < num_items - 1)\n  {\n    test_floating_point(op, input[i], input[i + 1]);\n  }\n}\n\ntemplate <typename T, typename Operation>\nstatic __global__ void test_float_special_values_kernel(Operation op)\n{\n  test_floating_point(op, T{-0.0f}, T{0.0f});\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::max());\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::min());\n  test_floating_point(op, T{1.0f}, cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::min(), cuda::std::numeric_limits<T>::max());\n  test_floating_point(op, cuda::std::numeric_limits<T>::max(), cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::min(), cuda::std::numeric_limits<T>::quiet_NaN());\n  test_floating_point(op, cuda::std::numeric_limits<T>::quiet_NaN(), cuda::std::numeric_limits<T>::quiet_NaN());\n}\n\nC2H_TEST(\"Split/Merge Integers\", \"[Split/Merge][Random]\", integral_types)\n{\n  using T              = c2h::get<0, TestType>;\n  const auto num_items = 1 << 16;\n  c2h::device_vector<T> d_in(num_items);\n  c2h::gen(C2H_SEED(1), d_in);\n  test_int_kernel<<<cuda::ceil_div(num_items, 256), 256>>>(thrust::raw_pointer_cast(d_in.data()), num_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  test_int_special_values_kernel<<<1, 1>>>();\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\nC2H_TEST(\n  \"Compare Floating-point with Integers\", \"[CompareFloatingPointWithInt][Random]\", operator_types, floating_point_types)\n{\n  using Op             = c2h::get<0, TestType>;\n  using T              = c2h::get<1, TestType>;\n  const auto num_items = 1 << 16;\n  c2h::device_vector<T> d_in(num_items);\n  c2h::gen(C2H_SEED(1), d_in);\n  test_float_kernel<<<cuda::ceil_div(num_items, 256), 256>>>(Op{}, thrust::raw_pointer_cast(d_in.data()), num_items);\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n  test_float_special_values_kernel<T><<<1, 1>>>(Op{});\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n", "source": "/kaggle/working/cccl/cub/test/internal/catch2_test_integer_utils.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\n__global__ void warp_reduce_kernel(T* in, T* out, ActionT action)\n{\n  using warp_reduce_t = cub::WarpReduce<T, LOGICAL_WARP_THREADS>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = threadIdx.x;\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Load data\n  T thread_data = in[tid];\n  // Instantiate and run warp reduction\n  warp_reduce_t warp_reduce(storage[warp_id]);\n  auto result = action(tid, warp_reduce, thread_data);\n\n  // Write warp aggregate\n  out[tid] = result;\n}\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_tail_t\n{\n  uint8_t* d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, cub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) || ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result        = warp_reduce.TailSegmentedSum(thread_data, d_flags[linear_tid]);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_head_t\n{\n  uint8_t* d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, cub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedSum(thread_data, d_flags[linear_tid]);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_tail_t\n{\n  uint8_t* d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, cub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) || ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result        = warp_reduce.TailSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_head_t\n{\n  uint8_t* d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, cub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Dispatch helper function\n */\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\nvoid warp_reduce(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  warp_reduce_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T, cuda::std::enable_if_t<cuda::std::is_floating_point_v<T>, int> = 0>\nvoid verify_results(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)\n{\n  REQUIRE_APPROX_EQ(expected_data, test_results);\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T, cuda::std::enable_if_t<!cuda::std::is_floating_point_v<T>, int> = 0>\nvoid verify_results(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\nenum class reduce_mode\n{\n  all,\n  partial,\n  head_flags,\n  tail_flags,\n};\n\ntemplate <typename InputItT, typename FlagInputItT, typename ReductionOp, typename ResultOutItT>\nvoid compute_host_reference(\n  reduce_mode mode,\n  InputItT h_in,\n  FlagInputItT h_flags,\n  int warps,\n  int logical_warp_threads,\n  int valid_warp_threads,\n  ReductionOp reduction_op,\n  ResultOutItT h_data_out)\n{\n  // Accumulate segments (lane 0 of each warp is implicitly a segment head)\n  for (int warp = 0; warp < warps; ++warp)\n  {\n    int warp_offset = warp * logical_warp_threads;\n    int item_offset = warp_offset + valid_warp_threads - 1;\n\n    // Last item in warp\n    auto head_aggregate = h_in[item_offset];\n    auto tail_aggregate = h_in[item_offset];\n\n    if (mode != reduce_mode::tail_flags && h_flags[item_offset])\n    {\n      h_data_out[item_offset] = head_aggregate;\n    }\n    item_offset--;\n\n    // Work backwards\n    while (item_offset >= warp_offset)\n    {\n      if (h_flags[item_offset + 1])\n      {\n        head_aggregate = h_in[item_offset];\n      }\n      else\n      {\n        head_aggregate = reduction_op(head_aggregate, h_in[item_offset]);\n      }\n\n      if (h_flags[item_offset])\n      {\n        if (mode == reduce_mode::head_flags)\n        {\n          h_data_out[item_offset] = head_aggregate;\n        }\n        else if (mode == reduce_mode::tail_flags)\n        {\n          h_data_out[item_offset + 1] = tail_aggregate;\n          tail_aggregate              = h_in[item_offset];\n        }\n      }\n      else\n      {\n        tail_aggregate = reduction_op(tail_aggregate, h_in[item_offset]);\n      }\n\n      item_offset--;\n    }\n\n    // Record last segment aggregate\n    if (mode == reduce_mode::tail_flags)\n    {\n      h_data_out[warp_offset] = tail_aggregate;\n    }\n    else\n    {\n      h_data_out[warp_offset] = head_aggregate;\n    }\n  }\n}\n\n// List of types to test\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing full_type_list =\n  c2h::type_list<std::uint8_t,\n                 std::uint16_t,\n                 std::int32_t,\n                 std::int64_t,\n                 custom_t,\n#if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 uchar3,\n                 short2>;\n\nusing builtin_type_list = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 7, 1>;\n\nusing segmented_modes = c2h::enum_type_list<reduce_mode, reduce_mode::head_flags, reduce_mode::tail_flags>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == cub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nC2H_TEST(\"Warp segmented sum works\", \"[reduce][warp]\", full_type_list, logical_warp_threads, segmented_modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags || segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_sum_t =\n    cuda::std::_If<(segmented_mod == reduce_mode::tail_flags), warp_seg_sum_tail_t<type>, warp_seg_sum_head_t<type>>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<uint8_t> d_flags(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  constexpr auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min      = 0;\n  constexpr uint8_t max      = 2;\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, warp_seg_sum_t{thrust::raw_pointer_cast(d_flags.data())});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in       = d_in;\n  c2h::host_vector<uint8_t> h_flags = d_flags;\n  c2h::host_vector<type> h_out      = h_in;\n  compute_host_reference(\n    segmented_mod,\n    h_in,\n    h_flags,\n    params::total_warps,\n    params::logical_warp_threads,\n    valid_items,\n    cuda::std::plus<type>{},\n    h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"Warp segmented reduction works\", \"[reduce][warp]\", builtin_type_list, logical_warp_threads, segmented_modes)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = cuda::minimum<>;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags || segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_reduction_t =\n    cuda::std::_If<(segmented_mod == reduce_mode::tail_flags),\n                   warp_seg_reduce_tail_t<type, red_op_t>,\n                   warp_seg_reduce_head_t<type, red_op_t>>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<uint8_t> d_flags(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  constexpr auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min      = 0;\n  constexpr uint8_t max      = 2;\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, warp_seg_reduction_t{thrust::raw_pointer_cast(d_flags.data()), red_op_t{}});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in       = d_in;\n  c2h::host_vector<uint8_t> h_flags = d_flags;\n  c2h::host_vector<type> h_out      = h_in;\n  compute_host_reference(\n    segmented_mod,\n    h_in,\n    h_flags,\n    params::total_warps,\n    params::logical_warp_threads,\n    valid_items,\n    red_op_t{},\n    h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/custom_type.h>\n\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\n__global__ void warp_reduce_kernel(T* in, T* out, ActionT action)\n{\n  using warp_reduce_t = hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n\n  __shared__ storage_t storage[TOTAL_WARPS];\n\n  const int tid = threadIdx.x;\n\n  // Get warp index\n  int warp_id = tid / LOGICAL_WARP_THREADS;\n\n  // Load data\n  T thread_data = in[tid];\n  // Instantiate and run warp reduction\n  warp_reduce_t warp_reduce(storage[warp_id]);\n  auto result = action(tid, warp_reduce, thread_data);\n\n  // Write warp aggregate\n  out[tid] = result;\n}\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_tail_t\n{\n  uint8_t* d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) || ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result        = warp_reduce.TailSegmentedSum(thread_data, d_flags[linear_tid]);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedSum\n */\ntemplate <typename T>\nstruct warp_seg_sum_head_t\n{\n  uint8_t* d_flags;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedSum(thread_data, d_flags[linear_tid]);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::TailSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_tail_t\n{\n  uint8_t* d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = (linear_tid % LOGICAL_WARP_THREADS == 0) || ((linear_tid == 0) ? 0 : d_flags[linear_tid - 1]);\n    auto result        = warp_reduce.TailSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return has_agg ? result : thread_data;\n  }\n};\n\n/**\n * @brief Delegate wrapper for WarpReduce::HeadSegmentedReduce\n */\ntemplate <typename T, typename ReductionOpT>\nstruct warp_seg_reduce_head_t\n{\n  uint8_t* d_flags;\n  ReductionOpT reduction_op;\n  template <int LOGICAL_WARP_THREADS>\n  __device__ T operator()(int linear_tid, hipcub::WarpReduce<T, LOGICAL_WARP_THREADS>& warp_reduce, T& thread_data) const\n  {\n    const bool has_agg = ((linear_tid % LOGICAL_WARP_THREADS == 0) || d_flags[linear_tid]);\n    auto result        = warp_reduce.HeadSegmentedReduce(thread_data, d_flags[linear_tid], reduction_op);\n    return (has_agg) ? result : thread_data;\n  }\n};\n\n/**\n * @brief Dispatch helper function\n */\ntemplate <int LOGICAL_WARP_THREADS, int TOTAL_WARPS, typename T, typename ActionT>\nvoid warp_reduce(c2h::device_vector<T>& in, c2h::device_vector<T>& out, ActionT action)\n{\n  warp_reduce_kernel<LOGICAL_WARP_THREADS, TOTAL_WARPS, T, ActionT><<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>(\n    thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T, cuda::std::enable_if_t<cuda::std::is_floating_point_v<T>, int> = 0>\nvoid verify_results(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)\n{\n  REQUIRE_APPROX_EQ(expected_data, test_results);\n}\n\n/**\n * @brief Compares the results returned from system under test against the expected results.\n */\ntemplate <typename T, cuda::std::enable_if_t<!cuda::std::is_floating_point_v<T>, int> = 0>\nvoid verify_results(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)\n{\n  REQUIRE(expected_data == test_results);\n}\n\nenum class reduce_mode\n{\n  all,\n  partial,\n  head_flags,\n  tail_flags,\n};\n\ntemplate <typename InputItT, typename FlagInputItT, typename ReductionOp, typename ResultOutItT>\nvoid compute_host_reference(\n  reduce_mode mode,\n  InputItT h_in,\n  FlagInputItT h_flags,\n  int warps,\n  int logical_warp_threads,\n  int valid_warp_threads,\n  ReductionOp reduction_op,\n  ResultOutItT h_data_out)\n{\n  // Accumulate segments (lane 0 of each warp is implicitly a segment head)\n  for (int warp = 0; warp < warps; ++warp)\n  {\n    int warp_offset = warp * logical_warp_threads;\n    int item_offset = warp_offset + valid_warp_threads - 1;\n\n    // Last item in warp\n    auto head_aggregate = h_in[item_offset];\n    auto tail_aggregate = h_in[item_offset];\n\n    if (mode != reduce_mode::tail_flags && h_flags[item_offset])\n    {\n      h_data_out[item_offset] = head_aggregate;\n    }\n    item_offset--;\n\n    // Work backwards\n    while (item_offset >= warp_offset)\n    {\n      if (h_flags[item_offset + 1])\n      {\n        head_aggregate = h_in[item_offset];\n      }\n      else\n      {\n        head_aggregate = reduction_op(head_aggregate, h_in[item_offset]);\n      }\n\n      if (h_flags[item_offset])\n      {\n        if (mode == reduce_mode::head_flags)\n        {\n          h_data_out[item_offset] = head_aggregate;\n        }\n        else if (mode == reduce_mode::tail_flags)\n        {\n          h_data_out[item_offset + 1] = tail_aggregate;\n          tail_aggregate              = h_in[item_offset];\n        }\n      }\n      else\n      {\n        tail_aggregate = reduction_op(tail_aggregate, h_in[item_offset]);\n      }\n\n      item_offset--;\n    }\n\n    // Record last segment aggregate\n    if (mode == reduce_mode::tail_flags)\n    {\n      h_data_out[warp_offset] = tail_aggregate;\n    }\n    else\n    {\n      h_data_out[warp_offset] = head_aggregate;\n    }\n  }\n}\n\n// List of types to test\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\nusing full_type_list =\n  c2h::type_list<std::uint8_t,\n                 std::uint16_t,\n                 std::int32_t,\n                 std::int64_t,\n                 custom_t,\n#if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 uchar3,\n                 short2>;\n\nusing builtin_type_list = c2h::type_list<std::uint8_t, std::uint16_t, std::int32_t, std::int64_t>;\n\n// Logical warp sizes to test\nusing logical_warp_threads = c2h::enum_type_list<int, 32, 16, 9, 7, 1>;\n\nusing segmented_modes = c2h::enum_type_list<reduce_mode, reduce_mode::head_flags, reduce_mode::tail_flags>;\n\ntemplate <int logical_warp_threads>\nstruct total_warps_t\n{\nprivate:\n  static constexpr int max_warps      = 2;\n  static constexpr bool is_arch_warp  = (logical_warp_threads == hipcub::detail::warp_threads);\n  static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0);\n  static constexpr int total_warps    = (is_arch_warp || is_pow_of_two) ? max_warps : 1;\n\npublic:\n  static constexpr int value()\n  {\n    return total_warps;\n  }\n};\n\ntemplate <typename TestType>\nstruct params_t\n{\n  using type = typename c2h::get<0, TestType>;\n\n  static constexpr int logical_warp_threads = c2h::get<1, TestType>::value;\n  static constexpr int total_warps          = total_warps_t<logical_warp_threads>::value();\n  static constexpr int tile_size            = total_warps * logical_warp_threads;\n};\n\nC2H_TEST(\"Warp segmented sum works\", \"[reduce][warp]\", full_type_list, logical_warp_threads, segmented_modes)\n{\n  using params = params_t<TestType>;\n  using type   = typename params::type;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags || segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_sum_t =\n    cuda::std::_If<(segmented_mod == reduce_mode::tail_flags), warp_seg_sum_tail_t<type>, warp_seg_sum_head_t<type>>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<uint8_t> d_flags(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  constexpr auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min      = 0;\n  constexpr uint8_t max      = 2;\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, warp_seg_sum_t{thrust::raw_pointer_cast(d_flags.data())});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in       = d_in;\n  c2h::host_vector<uint8_t> h_flags = d_flags;\n  c2h::host_vector<type> h_out      = h_in;\n  compute_host_reference(\n    segmented_mod,\n    h_in,\n    h_flags,\n    params::total_warps,\n    params::logical_warp_threads,\n    valid_items,\n    cuda::std::plus<type>{},\n    h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"Warp segmented reduction works\", \"[reduce][warp]\", builtin_type_list, logical_warp_threads, segmented_modes)\n{\n  using params   = params_t<TestType>;\n  using type     = typename params::type;\n  using red_op_t = cuda::minimum<>;\n\n  constexpr auto segmented_mod = c2h::get<2, TestType>::value;\n  static_assert(segmented_mod == reduce_mode::tail_flags || segmented_mod == reduce_mode::head_flags,\n                \"Segmented tests must either be head or tail flags\");\n  using warp_seg_reduction_t =\n    cuda::std::_If<(segmented_mod == reduce_mode::tail_flags),\n                   warp_seg_reduce_tail_t<type, red_op_t>,\n                   warp_seg_reduce_head_t<type, red_op_t>>;\n\n  // Prepare test data\n  c2h::device_vector<type> d_in(params::tile_size);\n  c2h::device_vector<uint8_t> d_flags(params::tile_size);\n  c2h::device_vector<type> d_out(params::tile_size);\n  constexpr auto valid_items = params::logical_warp_threads;\n  constexpr uint8_t min      = 0;\n  constexpr uint8_t max      = 2;\n  c2h::gen(C2H_SEED(5), d_in);\n  c2h::gen(C2H_SEED(5), d_flags, min, max);\n\n  // Run test\n  warp_reduce<params::logical_warp_threads, params::total_warps>(\n    d_in, d_out, warp_seg_reduction_t{thrust::raw_pointer_cast(d_flags.data()), red_op_t{}});\n\n  // Prepare verification data\n  c2h::host_vector<type> h_in       = d_in;\n  c2h::host_vector<uint8_t> h_flags = d_flags;\n  c2h::host_vector<type> h_out      = h_in;\n  compute_host_reference(\n    segmented_mod,\n    h_in,\n    h_flags,\n    params::total_warps,\n    params::logical_warp_threads,\n    valid_items,\n    red_op_t{},\n    h_out.begin());\n\n  // Verify results\n  verify_results(h_out, d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/warp/catch2_test_warp_segmented_reduce.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n#include <cub/warp/warp_reduce_batched.cuh>\n\n#include <cuda/cmath>\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/ptx>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/mdspan>\n#include <cuda/std/span>\n#include <cuda/std/type_traits>\n\n#include <numeric>\n\n#include <test_util.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/check_results.cuh>\n#include <c2h/custom_type.h>\n#include <c2h/operator.cuh>\n\n// %PARAM% TEST_TYPES types 0:1:2\n\ninline constexpr int warp_size  = 32;\ninline constexpr int block_size = 2 * warp_size;\n\n// 2D layout: num_batches x batch_size (row-major). Single type with dynamic extents for kernels and host.\ntemplate <typename T>\nusing input_2d_mdspan_t = cuda::std::mdspan<T, cuda::std::dextents<int, 2>>;\n\nenum class WarpReduceBatchedMode\n{\n  SingleOut,\n  ToStriped,\n  ToBlocked\n};\n\ntemplate <WarpReduceBatchedMode Mode,\n          bool SyncPhysicalWarp,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp>\n__global__ void\nwarp_reduce_batched_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output, ReductionOp reduction_op)\n{\n  using warp_reduce_batched_t = cub::WarpReduceBatched<T, Batches, LogicalWarpThreads, SyncPhysicalWarp>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n  const int tid             = threadIdx.x;\n  const int logical_warp_id = tid / LogicalWarpThreads;\n  const int lane_id         = tid % LogicalWarpThreads;\n\n  auto inputs = cuda::std::array<T, Batches>{};\n  for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n  {\n    inputs[batch_idx] = input_md(logical_warp_id * Batches + batch_idx, lane_id);\n  }\n\n  constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n  auto outputs                 = cuda::std::array<T, out_per_thread>{};\n  if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n  {\n    outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Reduce(inputs, reduction_op);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToBlocked(inputs, outputs, reduction_op);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToStriped(inputs, outputs, reduction_op);\n  }\n\n  for (int idx = 0; idx < out_per_thread; ++idx)\n  {\n    const auto batch_idx =\n      (Mode == WarpReduceBatchedMode::ToBlocked)\n        ? (idx + lane_id * out_per_thread)\n        : (idx * LogicalWarpThreads + lane_id);\n    if (batch_idx < Batches)\n    {\n      output[logical_warp_id * Batches + batch_idx] = outputs[idx];\n    }\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode, bool SyncPhysicalWarp, int Batches, int LogicalWarpThreads, typename T>\n__global__ void sum_batched_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output)\n{\n  using warp_reduce_batched_t = cub::WarpReduceBatched<T, Batches, LogicalWarpThreads, SyncPhysicalWarp>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n  const int tid             = threadIdx.x;\n  const int logical_warp_id = tid / LogicalWarpThreads;\n  const int lane_id         = tid % LogicalWarpThreads;\n\n  auto inputs = cuda::std::array<T, Batches>{};\n  for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n  {\n    inputs[batch_idx] = input_md(logical_warp_id * Batches + batch_idx, lane_id);\n  }\n\n  constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n  auto outputs                 = cuda::std::array<T, out_per_thread>{};\n  if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n  {\n    outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Sum(inputs);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.SumToBlocked(inputs, outputs);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.SumToStriped(inputs, outputs);\n  }\n\n  for (int idx = 0; idx < out_per_thread; ++idx)\n  {\n    const auto batch_idx =\n      (Mode == WarpReduceBatchedMode::ToBlocked)\n        ? (idx + lane_id * out_per_thread)\n        : (idx * LogicalWarpThreads + lane_id);\n    if (batch_idx < Batches)\n    {\n      output[logical_warp_id * Batches + batch_idx] = outputs[idx];\n    }\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode,\n          bool SyncPhysicalWarp,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp>\n__global__ void\nwarp_reduce_batched_cond_part_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output, ReductionOp reduction_op)\n{\n  static_assert(LogicalWarpThreads < warp_size, \"Need at least 2 logical warps per physical warp for this test\");\n  using warp_reduce_batched_t = cub::WarpReduceBatched<T, Batches, LogicalWarpThreads>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n\n  const int tid               = threadIdx.x;\n  const int logical_warp_id   = tid / LogicalWarpThreads;\n  const int lane_id           = tid % LogicalWarpThreads;\n  const bool is_participating = (logical_warp_id % 2 == 0);\n\n  if (is_participating)\n  {\n    const int participant_idx = logical_warp_id / 2;\n\n    auto inputs = cuda::std::array<T, Batches>{};\n    for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n    {\n      inputs[batch_idx] = input_md(participant_idx * Batches + batch_idx, lane_id);\n    }\n\n    constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n    auto outputs                 = cuda::std::array<T, out_per_thread>{};\n    if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n    {\n      outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Reduce(inputs, reduction_op);\n    }\n    if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n    {\n      warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToBlocked(inputs, outputs, reduction_op);\n    }\n    if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n    {\n      warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToStriped(inputs, outputs, reduction_op);\n    }\n\n    for (int idx = 0; idx < out_per_thread; ++idx)\n    {\n      const auto batch_idx =\n        (Mode == WarpReduceBatchedMode::ToBlocked)\n          ? (idx + lane_id * out_per_thread)\n          : (idx * LogicalWarpThreads + lane_id);\n      if (batch_idx < Batches)\n      {\n        output[participant_idx * Batches + batch_idx] = outputs[idx];\n      }\n    }\n  }\n  // When SyncPhysicalWarp is true, the non-participating threads need to exit early to avoid expected deadlocks.\n  if constexpr (!SyncPhysicalWarp)\n  {\n    // Keep non-participating threads from exiting early to check for unexpected deadlocks.\n    __syncwarp();\n  }\n}\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244) // numeric(33): C: '=': conversion from 'int' to '_Ty', possible loss of data\n\ntemplate <typename T, typename ReductionOp>\nvoid compute_host_reference(input_2d_mdspan_t<const T> input_md, cuda::std::span<T> output, ReductionOp op)\n{\n  const auto identity  = identity_v<ReductionOp, T>;\n  const int batches    = input_md.extent(0);\n  const int batch_size = input_md.extent(1);\n\n  for (int batch_idx = 0; batch_idx < batches; ++batch_idx)\n  {\n    const auto iter   = cuda::make_transform_iterator(cuda::make_counting_iterator(0), [input_md, batch_idx](int idx) {\n      return input_md(batch_idx, idx);\n    });\n    output[batch_idx] = static_cast<T>(std::accumulate(iter, iter + batch_size, identity, op));\n  }\n}\n\n_CCCL_DIAG_POP\n\ntemplate <typename T, int N>\nvoid gen_bounded_input(c2h::seed_t seed, c2h::device_vector<T>& d_input)\n{\n  if constexpr (cuda::std::is_floating_point_v<T>)\n  {\n    // Small positive range to minimize floating point error in reductions\n    c2h::gen(seed, d_input, T(0.5), T(1.5));\n  }\n  else if constexpr (cuda::std::is_integral_v<T> && cuda::std::is_signed_v<T>)\n  {\n    // Avoid signed overflow when summing N elements\n    const T gen_max = cuda::std::numeric_limits<T>::max() / static_cast<T>(N);\n    c2h::gen(seed, d_input, -gen_max, gen_max);\n  }\n  else\n  {\n    c2h::gen(seed, d_input);\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp,\n          bool ConvenienceOverload = false,\n          bool CondParticipation   = false,\n          bool SyncPhysicalWarp    = false>\nvoid test_warp_reduce_batched(ReductionOp reduction_op = ReductionOp{})\n{\n  CAPTURE(c2h::type_name<T>(), Batches, LogicalWarpThreads);\n\n  constexpr int num_logical_warps = block_size / LogicalWarpThreads;\n  constexpr int total_batches     = CondParticipation ? num_logical_warps * Batches / 2 : num_logical_warps * Batches;\n  constexpr int total_elements    = total_batches * LogicalWarpThreads;\n\n  c2h::device_vector<T> d_input(total_elements);\n  gen_bounded_input<T, LogicalWarpThreads>(C2H_SEED(10), d_input);\n\n  c2h::device_vector<T> d_output(total_batches);\n\n  input_2d_mdspan_t<T> d_input_md(thrust::raw_pointer_cast(d_input.data()), total_batches, LogicalWarpThreads);\n  cuda::std::span<T> d_output_span(thrust::raw_pointer_cast(d_output.data()), total_batches);\n  if constexpr (CondParticipation)\n  {\n    warp_reduce_batched_cond_part_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n      <<<1, block_size>>>(d_input_md, d_output_span, reduction_op);\n  }\n  else\n  {\n    if constexpr (ConvenienceOverload && cuda::std::is_same_v<ReductionOp, cuda::std::plus<>>)\n    {\n      sum_batched_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n        <<<1, block_size>>>(d_input_md, d_output_span);\n    }\n    else\n    {\n      warp_reduce_batched_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n        <<<1, block_size>>>(d_input_md, d_output_span, reduction_op);\n    }\n  }\n\n  cudaError_t err = cudaPeekAtLastError();\n  REQUIRE(err == cudaSuccess);\n\n  err = cudaDeviceSynchronize();\n  REQUIRE(err == cudaSuccess);\n\n  // Host-side: construct mdspans once; pass to reference and verify\n  c2h::host_vector<T> h_input  = d_input;\n  c2h::host_vector<T> h_output = d_output;\n\n  c2h::host_vector<T> h_reference(total_batches);\n  input_2d_mdspan_t<const T> h_input_md(h_input.data(), total_batches, LogicalWarpThreads);\n  cuda::std::span<T> h_reference_span(h_reference.data(), total_batches);\n\n  compute_host_reference(h_input_md, h_reference_span, reduction_op);\n\n  verify_results(h_reference, h_output);\n}\n\n#if TEST_TYPES == 0\nusing builtin_type_list = c2h::type_list<cuda::std::uint8_t, cuda::std::uint16_t>;\n#elif TEST_TYPES == 1\nusing builtin_type_list = c2h::type_list<cuda::std::int32_t, cuda::std::int64_t>;\n#elif TEST_TYPES == 2\nusing builtin_type_list = c2h::type_list<float, double>;\n#endif\n\n#if TEST_TYPES == 0\nusing full_type_list = c2h::type_list<cuda::std::uint8_t, cuda::std::uint16_t, uchar3, short2>;\n#elif TEST_TYPES == 1\nusing full_type_list =\n  c2h::type_list<cuda::std::int32_t,\n                 cuda::std::int64_t,\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 >;\n#elif TEST_TYPES == 2\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\n\nusing full_type_list = c2h::type_list<float, double, custom_t>;\n#endif // TEST_TYPES\n\n// (N, M) = (Batches, LogicalWarpThreads) for test parameterization\ntemplate <int X, int Y>\nstruct int_pair\n{\n  static constexpr int x = X;\n  static constexpr int y = Y;\n};\n\n// N=M configurations (best performance)\nusing equal_nm_configs =\n  c2h::type_list<int_pair<1, 1>, int_pair<2, 2>, int_pair<4, 4>, int_pair<8, 8>, int_pair<16, 16>, int_pair<32, 32>>;\n\n// N!=M configurations\nusing unequal_nm_configs = c2h::type_list<\n  int_pair<0, 32>,\n  int_pair<1, 32>,\n  int_pair<3, 32>,\n  int_pair<3, 16>,\n  int_pair<5, 16>,\n  int_pair<4, 8>,\n  int_pair<5, 8>,\n  int_pair<6, 8>,\n  int_pair<6, 4>,\n  int_pair<7, 4>,\n  int_pair<8, 4>,\n  int_pair<9, 4>,\n  int_pair<10, 4>,\n  int_pair<1, 2>,\n  int_pair<7, 2>,\n  int_pair<0, 1>,\n  int_pair<2, 1>>;\n\n// Sub-warp configurations (LogicalWarpThreads < 32, at least 2 logical warps per physical warp)\nusing sub_warp_equal_configs = c2h::type_list<int_pair<2, 2>, int_pair<4, 4>, int_pair<8, 8>, int_pair<16, 16>>;\n\nusing sub_warp_unequal_configs = c2h::type_list<int_pair<3, 16>, int_pair<4, 8>, int_pair<6, 4>, int_pair<1, 2>>;\n\nusing unequal_nm_single_out_configs = c2h::type_list<int_pair<1, 32>, int_pair<3, 16>, int_pair<4, 8>, int_pair<1, 2>>;\n\nC2H_TEST(\"WarpReduceBatched::Reduce N=M sum\", \"[warp][reduce][batched]\", full_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\n  \"WarpReduceBatched::Reduce N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_single_out_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\n  \"WarpReduceBatched::Reduce max with over-syncing\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::Reduce min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_single_out_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::Sum\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped N=M sum\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped max with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToStriped\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation and over-syncing N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation and over-syncing N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked N=M sum\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked max with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToBlocked\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation and over-syncing N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation and over-syncing N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n#include <cub/warp/warp_reduce_batched.cuh>\n\n#include <cuda/cmath>\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/ptx>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/mdspan>\n#include <cuda/std/span>\n#include <cuda/std/type_traits>\n\n#include <numeric>\n\n#include <test_util.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/check_results.cuh>\n#include <c2h/custom_type.h>\n#include <c2h/operator.cuh>\n\n// %PARAM% TEST_TYPES types 0:1:2\n\ninline constexpr int warp_size  = 32;\ninline constexpr int block_size = 2 * warp_size;\n\n// 2D layout: num_batches x batch_size (row-major). Single type with dynamic extents for kernels and host.\ntemplate <typename T>\nusing input_2d_mdspan_t = cuda::std::mdspan<T, cuda::std::dextents<int, 2>>;\n\nenum class WarpReduceBatchedMode\n{\n  SingleOut,\n  ToStriped,\n  ToBlocked\n};\n\ntemplate <WarpReduceBatchedMode Mode,\n          bool SyncPhysicalWarp,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp>\n__global__ void\nwarp_reduce_batched_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output, ReductionOp reduction_op)\n{\n  using warp_reduce_batched_t = hipcub::WarpReduceBatched<T, Batches, LogicalWarpThreads, SyncPhysicalWarp>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n  const int tid             = threadIdx.x;\n  const int logical_warp_id = tid / LogicalWarpThreads;\n  const int lane_id         = tid % LogicalWarpThreads;\n\n  auto inputs = cuda::std::array<T, Batches>{};\n  for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n  {\n    inputs[batch_idx] = input_md(logical_warp_id * Batches + batch_idx, lane_id);\n  }\n\n  constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n  auto outputs                 = cuda::std::array<T, out_per_thread>{};\n  if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n  {\n    outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Reduce(inputs, reduction_op);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToBlocked(inputs, outputs, reduction_op);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToStriped(inputs, outputs, reduction_op);\n  }\n\n  for (int idx = 0; idx < out_per_thread; ++idx)\n  {\n    const auto batch_idx =\n      (Mode == WarpReduceBatchedMode::ToBlocked)\n        ? (idx + lane_id * out_per_thread)\n        : (idx * LogicalWarpThreads + lane_id);\n    if (batch_idx < Batches)\n    {\n      output[logical_warp_id * Batches + batch_idx] = outputs[idx];\n    }\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode, bool SyncPhysicalWarp, int Batches, int LogicalWarpThreads, typename T>\n__global__ void sum_batched_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output)\n{\n  using warp_reduce_batched_t = hipcub::WarpReduceBatched<T, Batches, LogicalWarpThreads, SyncPhysicalWarp>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n  const int tid             = threadIdx.x;\n  const int logical_warp_id = tid / LogicalWarpThreads;\n  const int lane_id         = tid % LogicalWarpThreads;\n\n  auto inputs = cuda::std::array<T, Batches>{};\n  for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n  {\n    inputs[batch_idx] = input_md(logical_warp_id * Batches + batch_idx, lane_id);\n  }\n\n  constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n  auto outputs                 = cuda::std::array<T, out_per_thread>{};\n  if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n  {\n    outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Sum(inputs);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.SumToBlocked(inputs, outputs);\n  }\n  if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n  {\n    warp_reduce_batched_t{temp_storage[logical_warp_id]}.SumToStriped(inputs, outputs);\n  }\n\n  for (int idx = 0; idx < out_per_thread; ++idx)\n  {\n    const auto batch_idx =\n      (Mode == WarpReduceBatchedMode::ToBlocked)\n        ? (idx + lane_id * out_per_thread)\n        : (idx * LogicalWarpThreads + lane_id);\n    if (batch_idx < Batches)\n    {\n      output[logical_warp_id * Batches + batch_idx] = outputs[idx];\n    }\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode,\n          bool SyncPhysicalWarp,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp>\n__global__ void\nwarp_reduce_batched_cond_part_kernel(input_2d_mdspan_t<T> input_md, cuda::std::span<T> output, ReductionOp reduction_op)\n{\n  static_assert(LogicalWarpThreads < warp_size, \"Need at least 2 logical warps per physical warp for this test\");\n  using warp_reduce_batched_t = hipcub::WarpReduceBatched<T, Batches, LogicalWarpThreads>;\n  __shared__ typename warp_reduce_batched_t::TempStorage temp_storage[block_size / LogicalWarpThreads];\n\n  const int tid               = threadIdx.x;\n  const int logical_warp_id   = tid / LogicalWarpThreads;\n  const int lane_id           = tid % LogicalWarpThreads;\n  const bool is_participating = (logical_warp_id % 2 == 0);\n\n  if (is_participating)\n  {\n    const int participant_idx = logical_warp_id / 2;\n\n    auto inputs = cuda::std::array<T, Batches>{};\n    for (int batch_idx = 0; batch_idx < Batches; ++batch_idx)\n    {\n      inputs[batch_idx] = input_md(participant_idx * Batches + batch_idx, lane_id);\n    }\n\n    constexpr int out_per_thread = cuda::ceil_div(Batches, LogicalWarpThreads);\n    auto outputs                 = cuda::std::array<T, out_per_thread>{};\n    if constexpr (Mode == WarpReduceBatchedMode::SingleOut)\n    {\n      outputs[0] = warp_reduce_batched_t{temp_storage[logical_warp_id]}.Reduce(inputs, reduction_op);\n    }\n    if constexpr (Mode == WarpReduceBatchedMode::ToBlocked)\n    {\n      warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToBlocked(inputs, outputs, reduction_op);\n    }\n    if constexpr (Mode == WarpReduceBatchedMode::ToStriped)\n    {\n      warp_reduce_batched_t{temp_storage[logical_warp_id]}.ReduceToStriped(inputs, outputs, reduction_op);\n    }\n\n    for (int idx = 0; idx < out_per_thread; ++idx)\n    {\n      const auto batch_idx =\n        (Mode == WarpReduceBatchedMode::ToBlocked)\n          ? (idx + lane_id * out_per_thread)\n          : (idx * LogicalWarpThreads + lane_id);\n      if (batch_idx < Batches)\n      {\n        output[participant_idx * Batches + batch_idx] = outputs[idx];\n      }\n    }\n  }\n  // When SyncPhysicalWarp is true, the non-participating threads need to exit early to avoid expected deadlocks.\n  if constexpr (!SyncPhysicalWarp)\n  {\n    // Keep non-participating threads from exiting early to check for unexpected deadlocks.\n    __syncwarp();\n  }\n}\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244) // numeric(33): C: '=': conversion from 'int' to '_Ty', possible loss of data\n\ntemplate <typename T, typename ReductionOp>\nvoid compute_host_reference(input_2d_mdspan_t<const T> input_md, cuda::std::span<T> output, ReductionOp op)\n{\n  const auto identity  = identity_v<ReductionOp, T>;\n  const int batches    = input_md.extent(0);\n  const int batch_size = input_md.extent(1);\n\n  for (int batch_idx = 0; batch_idx < batches; ++batch_idx)\n  {\n    const auto iter   = cuda::make_transform_iterator(cuda::make_counting_iterator(0), [input_md, batch_idx](int idx) {\n      return input_md(batch_idx, idx);\n    });\n    output[batch_idx] = static_cast<T>(std::accumulate(iter, iter + batch_size, identity, op));\n  }\n}\n\n_CCCL_DIAG_POP\n\ntemplate <typename T, int N>\nvoid gen_bounded_input(c2h::seed_t seed, c2h::device_vector<T>& d_input)\n{\n  if constexpr (cuda::std::is_floating_point_v<T>)\n  {\n    // Small positive range to minimize floating point error in reductions\n    c2h::gen(seed, d_input, T(0.5), T(1.5));\n  }\n  else if constexpr (cuda::std::is_integral_v<T> && cuda::std::is_signed_v<T>)\n  {\n    // Avoid signed overflow when summing N elements\n    const T gen_max = cuda::std::numeric_limits<T>::max() / static_cast<T>(N);\n    c2h::gen(seed, d_input, -gen_max, gen_max);\n  }\n  else\n  {\n    c2h::gen(seed, d_input);\n  }\n}\n\ntemplate <WarpReduceBatchedMode Mode,\n          int Batches,\n          int LogicalWarpThreads,\n          typename T,\n          typename ReductionOp,\n          bool ConvenienceOverload = false,\n          bool CondParticipation   = false,\n          bool SyncPhysicalWarp    = false>\nvoid test_warp_reduce_batched(ReductionOp reduction_op = ReductionOp{})\n{\n  CAPTURE(c2h::type_name<T>(), Batches, LogicalWarpThreads);\n\n  constexpr int num_logical_warps = block_size / LogicalWarpThreads;\n  constexpr int total_batches     = CondParticipation ? num_logical_warps * Batches / 2 : num_logical_warps * Batches;\n  constexpr int total_elements    = total_batches * LogicalWarpThreads;\n\n  c2h::device_vector<T> d_input(total_elements);\n  gen_bounded_input<T, LogicalWarpThreads>(C2H_SEED(10), d_input);\n\n  c2h::device_vector<T> d_output(total_batches);\n\n  input_2d_mdspan_t<T> d_input_md(thrust::raw_pointer_cast(d_input.data()), total_batches, LogicalWarpThreads);\n  cuda::std::span<T> d_output_span(thrust::raw_pointer_cast(d_output.data()), total_batches);\n  if constexpr (CondParticipation)\n  {\n    warp_reduce_batched_cond_part_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n      <<<1, block_size>>>(d_input_md, d_output_span, reduction_op);\n  }\n  else\n  {\n    if constexpr (ConvenienceOverload && cuda::std::is_same_v<ReductionOp, cuda::std::plus<>>)\n    {\n      sum_batched_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n        <<<1, block_size>>>(d_input_md, d_output_span);\n    }\n    else\n    {\n      warp_reduce_batched_kernel<Mode, SyncPhysicalWarp, Batches, LogicalWarpThreads>\n        <<<1, block_size>>>(d_input_md, d_output_span, reduction_op);\n    }\n  }\n\n  hipError_t err = hipPeekAtLastError();\n  REQUIRE(err == hipSuccess);\n\n  err = hipDeviceSynchronize();\n  REQUIRE(err == hipSuccess);\n\n  // Host-side: construct mdspans once; pass to reference and verify\n  c2h::host_vector<T> h_input  = d_input;\n  c2h::host_vector<T> h_output = d_output;\n\n  c2h::host_vector<T> h_reference(total_batches);\n  input_2d_mdspan_t<const T> h_input_md(h_input.data(), total_batches, LogicalWarpThreads);\n  cuda::std::span<T> h_reference_span(h_reference.data(), total_batches);\n\n  compute_host_reference(h_input_md, h_reference_span, reduction_op);\n\n  verify_results(h_reference, h_output);\n}\n\n#if TEST_TYPES == 0\nusing builtin_type_list = c2h::type_list<cuda::std::uint8_t, cuda::std::uint16_t>;\n#elif TEST_TYPES == 1\nusing builtin_type_list = c2h::type_list<cuda::std::int32_t, cuda::std::int64_t>;\n#elif TEST_TYPES == 2\nusing builtin_type_list = c2h::type_list<float, double>;\n#endif\n\n#if TEST_TYPES == 0\nusing full_type_list = c2h::type_list<cuda::std::uint8_t, cuda::std::uint16_t, uchar3, short2>;\n#elif TEST_TYPES == 1\nusing full_type_list =\n  c2h::type_list<cuda::std::int32_t,\n                 cuda::std::int64_t,\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 >;\n#elif TEST_TYPES == 2\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\n\nusing full_type_list = c2h::type_list<float, double, custom_t>;\n#endif // TEST_TYPES\n\n// (N, M) = (Batches, LogicalWarpThreads) for test parameterization\ntemplate <int X, int Y>\nstruct int_pair\n{\n  static constexpr int x = X;\n  static constexpr int y = Y;\n};\n\n// N=M configurations (best performance)\nusing equal_nm_configs =\n  c2h::type_list<int_pair<1, 1>, int_pair<2, 2>, int_pair<4, 4>, int_pair<8, 8>, int_pair<16, 16>, int_pair<32, 32>>;\n\n// N!=M configurations\nusing unequal_nm_configs = c2h::type_list<\n  int_pair<0, 32>,\n  int_pair<1, 32>,\n  int_pair<3, 32>,\n  int_pair<3, 16>,\n  int_pair<5, 16>,\n  int_pair<4, 8>,\n  int_pair<5, 8>,\n  int_pair<6, 8>,\n  int_pair<6, 4>,\n  int_pair<7, 4>,\n  int_pair<8, 4>,\n  int_pair<9, 4>,\n  int_pair<10, 4>,\n  int_pair<1, 2>,\n  int_pair<7, 2>,\n  int_pair<0, 1>,\n  int_pair<2, 1>>;\n\n// Sub-warp configurations (LogicalWarpThreads < 32, at least 2 logical warps per physical warp)\nusing sub_warp_equal_configs = c2h::type_list<int_pair<2, 2>, int_pair<4, 4>, int_pair<8, 8>, int_pair<16, 16>>;\n\nusing sub_warp_unequal_configs = c2h::type_list<int_pair<3, 16>, int_pair<4, 8>, int_pair<6, 4>, int_pair<1, 2>>;\n\nusing unequal_nm_single_out_configs = c2h::type_list<int_pair<1, 32>, int_pair<3, 16>, int_pair<4, 8>, int_pair<1, 2>>;\n\nC2H_TEST(\"WarpReduceBatched::Reduce N=M sum\", \"[warp][reduce][batched]\", full_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\n  \"WarpReduceBatched::Reduce N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_single_out_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\n  \"WarpReduceBatched::Reduce max with over-syncing\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::Reduce min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_single_out_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::Sum\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::SingleOut,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped N=M sum\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped max with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToStriped\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation and over-syncing N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped with conditional participation and over-syncing N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToStriped,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked N=M sum\", \"[warp][reduce][batched]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked N!=M sum\", \"[warp][reduce][batched]\", builtin_type_list, unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked, num_batches, logical_warp_num_threads, value_t, op_t>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked max with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::maximum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked min with over-syncing\",\n         \"[warp][reduce][batched]\",\n         builtin_type_list,\n         unequal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::minimum<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = false;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToBlocked\", \"[warp][reduce][batched][convenience]\", builtin_type_list, equal_nm_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation and over-syncing N=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_equal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked with conditional participation and over-syncing N!=M sum\",\n         \"[warp][reduce][batched][lane_mask]\",\n         builtin_type_list,\n         sub_warp_unequal_configs)\n{\n  using value_t                          = c2h::get<0, TestType>;\n  using op_t                             = cuda::std::plus<>;\n  constexpr int num_batches              = c2h::get<1, TestType>::x;\n  constexpr int logical_warp_num_threads = c2h::get<1, TestType>::y;\n  constexpr bool convenience_overload    = false;\n  constexpr bool cond_participation      = true;\n  constexpr bool sync_physical_warp      = true;\n  test_warp_reduce_batched<WarpReduceBatchedMode::ToBlocked,\n                           num_batches,\n                           logical_warp_num_threads,\n                           value_t,\n                           op_t,\n                           convenience_overload,\n                           cond_participation,\n                           sync_physical_warp>();\n}\n", "source": "/kaggle/working/cccl/cub/test/warp/catch2_test_warp_reduce_batched.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <cub/warp/warp_reduce_batched.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/__functional/maximum.h>\n#include <cuda/std/array>\n#include <cuda/std/span>\n\n#include <c2h/catch2_test_helper.h>\n\n__global__ __launch_bounds__(64) void WarpReduceBatchedOverviewKernel(int* out)\n{\n  // example-begin warp-reduce-batched-overview\n  constexpr int num_batches = 3;\n  using WarpReduceBatched   = cub::WarpReduceBatched<int, num_batches>;\n\n  // Assume 64 threads per block, so 64 / 32 = 2 logical warps\n  // Each logical warp has its own TempStorage\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int warp_id = static_cast<int>(threadIdx.x) / 32;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  int thread_data[num_batches];\n  thread_data[0] = tid - 1;\n  thread_data[1] = tid;\n  thread_data[2] = tid + 1;\n\n  int result = WarpReduceBatched{temp_storage[warp_id]}.Reduce(thread_data, cuda::maximum{});\n  // results across threads: [30, 31, 32, ?, ?, ..., ?, 62, 63, 64, ?, ?, ..., ?]\n  // example-end warp-reduce-batched-overview\n\n  const int lane_id = static_cast<int>(threadIdx.x) % 32;\n  if (lane_id < num_batches)\n  {\n    out[warp_id * num_batches + lane_id] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched overview documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedOverviewKernel<<<1, 64>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{30, 31, 32, 62, 63, 64};\n  REQUIRE(expected == d_out);\n}\n\n__global__ void WarpReduceBatchedReduceApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce\n  // Can't allow for physical warp synchronization since only the first logical warp participates due to the\n  // conditional. The other threads (assuming there are more than 16 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 16;\n  using WarpReduceBatched            = cub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Only the first logical warp participates, so only a single TempStorage is needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  int result{};\n  if (threadIdx.x < logical_warp_threads)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n    result = WarpReduceBatched{temp_storage}.Reduce(inputs, cuda::maximum{});\n  }\n  // results across threads: [14, 15, 16, ?, ?, ..., ?, 0, 0, ..., 0]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce\n  _CCCL_ASSERT(tid < logical_warp_threads || result == 0, \"\");\n  if (threadIdx.x < num_batches)\n  {\n    out[tid] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::Reduce documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(3);\n\n  WarpReduceBatchedReduceApiKernel<<<1, 64>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{14, 15, 16};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedReduceToStripedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce-to-striped\n  // Can't allow for physical warp synchronization since only every other logical warp participates.\n  // The other threads (assuming there are more than 2 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 2;\n  using WarpReduceBatched            = cub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  // Only every other logical warp participates, so only 2 TempStorage are needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid               = static_cast<int>(threadIdx.x);\n  const int logical_warp_id   = tid / logical_warp_threads;\n  const bool is_participating = logical_warp_id % 2 == 0;\n  const int participant_idx   = logical_warp_id / 2;\n\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  cuda::std::array<int, max_out_per_thread> results{};\n  if (is_participating)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n    WarpReduceBatched{temp_storage[participant_idx]}.ReduceToStriped(inputs, results, cuda::maximum{});\n  }\n  // results across threads:\n  // [[0, 2], [1, ?], [0, 0], [0, 0], [4, 6], [5, ?], [0, 0], [0, 0]]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce-to-striped\n\n  _CCCL_ASSERT(is_participating || (results[0] == 0 && results[1] == 0), \"\");\n  if (is_participating)\n  {\n    int const logical_lane_id = tid % logical_warp_threads;\n    for (int i = 0; i < max_out_per_thread; ++i)\n    {\n      // Striped\n      const int batch_idx = i * logical_warp_threads + logical_lane_id;\n      if (batch_idx < num_batches)\n      {\n        out[participant_idx * num_batches + batch_idx] = results[i];\n      }\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedReduceToStripedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{0, 1, 2, 4, 5, 6};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedReduceToBlockedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce-to-blocked\n  // Can't allow for physical warp synchronization since only every other logical warp participates.\n  // The other threads (assuming there are more than 2 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 2;\n  using WarpReduceBatched            = cub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  // Only every other logical warp participates, so only 2 TempStorage are needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid               = static_cast<int>(threadIdx.x);\n  const int logical_warp_id   = tid / logical_warp_threads;\n  const bool is_participating = logical_warp_id % 2 == 0;\n  const int participant_idx   = logical_warp_id / 2;\n\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  cuda::std::array<int, max_out_per_thread> results{};\n  if (is_participating)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n\n    WarpReduceBatched{temp_storage[participant_idx]}.ReduceToBlocked(inputs, results, cuda::maximum{});\n  }\n  // results across threads:\n  // [[0, 1], [2, ?], [0, 0], [0, 0], [4, 5], [6, ?], [0, 0], [0, 0]]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce-to-blocked\n\n  _CCCL_ASSERT(is_participating || (results[0] == 0 && results[1] == 0), \"\");\n  if (is_participating)\n  {\n    int const logical_lane_id = tid % logical_warp_threads;\n    for (int i = 0; i < max_out_per_thread; ++i)\n    {\n      // Blocked\n      const int batch_idx = logical_lane_id * max_out_per_thread + i;\n      if (batch_idx < num_batches)\n      {\n        out[participant_idx * num_batches + batch_idx] = results[i];\n      }\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedReduceToBlockedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{0, 1, 2, 4, 5, 6};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 4;\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr bool sync_physical_warp = true;\n  using WarpReduceBatched = cub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 4 = 2 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n  int result = WarpReduceBatched{temp_storage[logical_warp_id]}.Sum(inputs);\n  // results across threads:\n  // [2, 6, 10, ?, 18, 22, 26, ?]\n  // example-end warp-reduce-batched-sum\n  const int logical_lane_id = tid % logical_warp_threads;\n  if (logical_lane_id < num_batches)\n  {\n    out[logical_warp_id * num_batches + logical_lane_id] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::Sum documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedSumApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{2, 6, 10, 18, 22, 26};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumToStripedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum-to-striped\n  constexpr int num_batches          = 5;\n  constexpr int logical_warp_threads = 2;\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr bool sync_physical_warp = true;\n  using WarpReduceBatched = cub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[4];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 2, tid - 1, tid, tid + 1, tid + 2};\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  // Use a static size span to alias the last 3 elements of inputs for results.\n  cuda::std::span<int, max_out_per_thread> results{cuda::std::end(inputs) - max_out_per_thread, max_out_per_thread};\n  WarpReduceBatched{temp_storage[logical_warp_id]}.SumToStriped(inputs, results);\n  // results across threads:\n  // [[-3, 1, 5], [-1, 3, ?], [1, 5, 9], [3, 7, ?], ..., [9, 13, 17], [11, 15, ?]]\n  // example-end warp-reduce-batched-sum-to-striped\n\n  const int logical_lane_id = tid % logical_warp_threads;\n  for (int i = 0; i < max_out_per_thread; ++i)\n  {\n    // Striped\n    const int batch_idx = i * logical_warp_threads + logical_lane_id;\n    if (batch_idx < num_batches)\n    {\n      out[logical_warp_id * num_batches + batch_idx] = results[i];\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToStriped documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(20);\n\n  WarpReduceBatchedSumToStripedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{-3, -1, 1, 3, 5, 1, 3, 5, 7, 9, 5, 7, 9, 11, 13, 9, 11, 13, 15, 17};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumToBlockedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum-to-blocked\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr int num_batches          = 5;\n  constexpr int logical_warp_threads = 2;\n  constexpr bool sync_physical_warp  = true;\n  using WarpReduceBatched = cub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[4];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 2, tid - 1, tid, tid + 1, tid + 2};\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  // Use a static size span to alias the last 3 elements of inputs for results.\n  cuda::std::span<int, max_out_per_thread> results{cuda::std::end(inputs) - max_out_per_thread, max_out_per_thread};\n  WarpReduceBatched{temp_storage[logical_warp_id]}.SumToBlocked(inputs, results);\n  // results across threads:\n  // [[-3, -1, 1], [3, 5, ?], [1, 3, 5], [7, 9, ?], ..., [9, 11, 13], [15, 17, ?]]\n  // example-end warp-reduce-batched-sum-to-blocked\n\n  const int logical_lane_id = tid % logical_warp_threads;\n  for (int i = 0; i < max_out_per_thread; ++i)\n  {\n    // Blocked\n    const int batch_idx = logical_lane_id * max_out_per_thread + i;\n    if (batch_idx < num_batches)\n    {\n      out[logical_warp_id * num_batches + batch_idx] = results[i];\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToBlocked documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(20);\n\n  WarpReduceBatchedSumToBlockedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n\n  c2h::host_vector<int> expected{-3, -1, 1, 3, 5, 1, 3, 5, 7, 9, 5, 7, 9, 11, 13, 9, 11, 13, 15, 17};\n  REQUIRE(expected == d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <cub/warp/warp_reduce_batched.cuh>\n\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n\n#include <cuda/__functional/maximum.h>\n#include <cuda/std/array>\n#include <cuda/std/span>\n\n#include <c2h/catch2_test_helper.h>\n\n__global__ __launch_bounds__(64) void WarpReduceBatchedOverviewKernel(int* out)\n{\n  // example-begin warp-reduce-batched-overview\n  constexpr int num_batches = 3;\n  using WarpReduceBatched   = hipcub::WarpReduceBatched<int, num_batches>;\n\n  // Assume 64 threads per block, so 64 / 32 = 2 logical warps\n  // Each logical warp has its own TempStorage\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int warp_id = static_cast<int>(threadIdx.x) / 32;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  int thread_data[num_batches];\n  thread_data[0] = tid - 1;\n  thread_data[1] = tid;\n  thread_data[2] = tid + 1;\n\n  int result = WarpReduceBatched{temp_storage[warp_id]}.Reduce(thread_data, cuda::maximum{});\n  // results across threads: [30, 31, 32, ?, ?, ..., ?, 62, 63, 64, ?, ?, ..., ?]\n  // example-end warp-reduce-batched-overview\n\n  const int lane_id = static_cast<int>(threadIdx.x) % 32;\n  if (lane_id < num_batches)\n  {\n    out[warp_id * num_batches + lane_id] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched overview documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedOverviewKernel<<<1, 64>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{30, 31, 32, 62, 63, 64};\n  REQUIRE(expected == d_out);\n}\n\n__global__ void WarpReduceBatchedReduceApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce\n  // Can't allow for physical warp synchronization since only the first logical warp participates due to the\n  // conditional. The other threads (assuming there are more than 16 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 16;\n  using WarpReduceBatched            = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Only the first logical warp participates, so only a single TempStorage is needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage;\n\n  const int tid = static_cast<int>(threadIdx.x);\n  int result{};\n  if (threadIdx.x < logical_warp_threads)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n    result = WarpReduceBatched{temp_storage}.Reduce(inputs, cuda::maximum{});\n  }\n  // results across threads: [14, 15, 16, ?, ?, ..., ?, 0, 0, ..., 0]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce\n  _CCCL_ASSERT(tid < logical_warp_threads || result == 0, \"\");\n  if (threadIdx.x < num_batches)\n  {\n    out[tid] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::Reduce documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(3);\n\n  WarpReduceBatchedReduceApiKernel<<<1, 64>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{14, 15, 16};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedReduceToStripedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce-to-striped\n  // Can't allow for physical warp synchronization since only every other logical warp participates.\n  // The other threads (assuming there are more than 2 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 2;\n  using WarpReduceBatched            = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  // Only every other logical warp participates, so only 2 TempStorage are needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid               = static_cast<int>(threadIdx.x);\n  const int logical_warp_id   = tid / logical_warp_threads;\n  const bool is_participating = logical_warp_id % 2 == 0;\n  const int participant_idx   = logical_warp_id / 2;\n\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  cuda::std::array<int, max_out_per_thread> results{};\n  if (is_participating)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n    WarpReduceBatched{temp_storage[participant_idx]}.ReduceToStriped(inputs, results, cuda::maximum{});\n  }\n  // results across threads:\n  // [[0, 2], [1, ?], [0, 0], [0, 0], [4, 6], [5, ?], [0, 0], [0, 0]]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce-to-striped\n\n  _CCCL_ASSERT(is_participating || (results[0] == 0 && results[1] == 0), \"\");\n  if (is_participating)\n  {\n    int const logical_lane_id = tid % logical_warp_threads;\n    for (int i = 0; i < max_out_per_thread; ++i)\n    {\n      // Striped\n      const int batch_idx = i * logical_warp_threads + logical_lane_id;\n      if (batch_idx < num_batches)\n      {\n        out[participant_idx * num_batches + batch_idx] = results[i];\n      }\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToStriped documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedReduceToStripedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{0, 1, 2, 4, 5, 6};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedReduceToBlockedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-reduce-to-blocked\n  // Can't allow for physical warp synchronization since only every other logical warp participates.\n  // The other threads (assuming there are more than 2 threads per block) can't exit early due to the\n  // barrier.\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 2;\n  using WarpReduceBatched            = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  // Only every other logical warp participates, so only 2 TempStorage are needed\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid               = static_cast<int>(threadIdx.x);\n  const int logical_warp_id   = tid / logical_warp_threads;\n  const bool is_participating = logical_warp_id % 2 == 0;\n  const int participant_idx   = logical_warp_id / 2;\n\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  cuda::std::array<int, max_out_per_thread> results{};\n  if (is_participating)\n  {\n    const cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n\n    WarpReduceBatched{temp_storage[participant_idx]}.ReduceToBlocked(inputs, results, cuda::maximum{});\n  }\n  // results across threads:\n  // [[0, 1], [2, ?], [0, 0], [0, 0], [4, 5], [6, ?], [0, 0], [0, 0]]\n  __syncthreads();\n  // Can reuse TempStorage after the barrier.\n  // example-end warp-reduce-batched-reduce-to-blocked\n\n  _CCCL_ASSERT(is_participating || (results[0] == 0 && results[1] == 0), \"\");\n  if (is_participating)\n  {\n    int const logical_lane_id = tid % logical_warp_threads;\n    for (int i = 0; i < max_out_per_thread; ++i)\n    {\n      // Blocked\n      const int batch_idx = logical_lane_id * max_out_per_thread + i;\n      if (batch_idx < num_batches)\n      {\n        out[participant_idx * num_batches + batch_idx] = results[i];\n      }\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::ReduceToBlocked documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedReduceToBlockedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{0, 1, 2, 4, 5, 6};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum\n  constexpr int num_batches          = 3;\n  constexpr int logical_warp_threads = 4;\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr bool sync_physical_warp = true;\n  using WarpReduceBatched = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 4 = 2 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[2];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 1, tid, tid + 1};\n  int result = WarpReduceBatched{temp_storage[logical_warp_id]}.Sum(inputs);\n  // results across threads:\n  // [2, 6, 10, ?, 18, 22, 26, ?]\n  // example-end warp-reduce-batched-sum\n  const int logical_lane_id = tid % logical_warp_threads;\n  if (logical_lane_id < num_batches)\n  {\n    out[logical_warp_id * num_batches + logical_lane_id] = result;\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::Sum documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(6);\n\n  WarpReduceBatchedSumApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{2, 6, 10, 18, 22, 26};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumToStripedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum-to-striped\n  constexpr int num_batches          = 5;\n  constexpr int logical_warp_threads = 2;\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr bool sync_physical_warp = true;\n  using WarpReduceBatched = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[4];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 2, tid - 1, tid, tid + 1, tid + 2};\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  // Use a static size span to alias the last 3 elements of inputs for results.\n  cuda::std::span<int, max_out_per_thread> results{cuda::std::end(inputs) - max_out_per_thread, max_out_per_thread};\n  WarpReduceBatched{temp_storage[logical_warp_id]}.SumToStriped(inputs, results);\n  // results across threads:\n  // [[-3, 1, 5], [-1, 3, ?], [1, 5, 9], [3, 7, ?], ..., [9, 13, 17], [11, 15, ?]]\n  // example-end warp-reduce-batched-sum-to-striped\n\n  const int logical_lane_id = tid % logical_warp_threads;\n  for (int i = 0; i < max_out_per_thread; ++i)\n  {\n    // Striped\n    const int batch_idx = i * logical_warp_threads + logical_lane_id;\n    if (batch_idx < num_batches)\n    {\n      out[logical_warp_id * num_batches + batch_idx] = results[i];\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToStriped documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(20);\n\n  WarpReduceBatchedSumToStripedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{-3, -1, 1, 3, 5, 1, 3, 5, 7, 9, 5, 7, 9, 11, 13, 9, 11, 13, 15, 17};\n  REQUIRE(expected == d_out);\n}\n\n__global__ __launch_bounds__(8) void WarpReduceBatchedSumToBlockedApiKernel(int* out)\n{\n  // example-begin warp-reduce-batched-sum-to-blocked\n  // We can enable physical warp synchronization since all non-exited lanes do participate in the primitive.\n  constexpr int num_batches          = 5;\n  constexpr int logical_warp_threads = 2;\n  constexpr bool sync_physical_warp  = true;\n  using WarpReduceBatched = hipcub::WarpReduceBatched<int, num_batches, logical_warp_threads, sync_physical_warp>;\n\n  // Assume 8 threads per block, so 8 / 2 = 4 logical warps\n  __shared__ typename WarpReduceBatched::TempStorage temp_storage[4];\n\n  const int tid             = static_cast<int>(threadIdx.x);\n  const int logical_warp_id = tid / logical_warp_threads;\n\n  cuda::std::array<int, num_batches> inputs{tid - 2, tid - 1, tid, tid + 1, tid + 2};\n  constexpr int max_out_per_thread = cuda::ceil_div(num_batches, logical_warp_threads);\n  // Use a static size span to alias the last 3 elements of inputs for results.\n  cuda::std::span<int, max_out_per_thread> results{cuda::std::end(inputs) - max_out_per_thread, max_out_per_thread};\n  WarpReduceBatched{temp_storage[logical_warp_id]}.SumToBlocked(inputs, results);\n  // results across threads:\n  // [[-3, -1, 1], [3, 5, ?], [1, 3, 5], [7, 9, ?], ..., [9, 11, 13], [15, 17, ?]]\n  // example-end warp-reduce-batched-sum-to-blocked\n\n  const int logical_lane_id = tid % logical_warp_threads;\n  for (int i = 0; i < max_out_per_thread; ++i)\n  {\n    // Blocked\n    const int batch_idx = logical_lane_id * max_out_per_thread + i;\n    if (batch_idx < num_batches)\n    {\n      out[logical_warp_id * num_batches + batch_idx] = results[i];\n    }\n  }\n}\n\nC2H_TEST(\"WarpReduceBatched::SumToBlocked documentation kernel\", \"[warp][reduce][batched]\")\n{\n  c2h::device_vector<int> d_out(20);\n\n  WarpReduceBatchedSumToBlockedApiKernel<<<1, 8>>>(thrust::raw_pointer_cast(d_out.data()));\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n\n  c2h::host_vector<int> expected{-3, -1, 1, 3, 5, 1, 3, 5, 7, 9, 5, 7, 9, 11, 13, 9, 11, 13, 15, 17};\n  REQUIRE(expected == d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/warp/catch2_test_warp_reduce_batched_api.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <cuda/cmath>\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/ptx>\n#include <cuda/std/__functional/invoke.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <array>\n#include <numeric>\n\n#include <test_util.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/check_results.cuh>\n#include <c2h/custom_type.h>\n#include <c2h/operator.cuh>\n\n/***********************************************************************************************************************\n * Constants\n **********************************************************************************************************************/\n\ninline constexpr int warp_size            = 32;\ninline constexpr auto total_warps         = 4u;\ninline constexpr int num_items_per_thread = 4;\n\n/***********************************************************************************************************************\n * Kernel\n **********************************************************************************************************************/\n\ntemplate <unsigned LogicalWarpThreads, bool EnableNumItems = false, typename T, typename Output, typename ReductionOp>\n__device__ void warp_reduce_function(T& thread_data, Output* output, ReductionOp reduction_op, int num_items = 0)\n{\n  using warp_reduce_t = cub::WarpReduce<Output, LogicalWarpThreads>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n  __shared__ storage_t storage[total_warps];\n  constexpr bool is_power_of_two = cuda::is_power_of_two(LogicalWarpThreads);\n  auto lane                      = cuda::ptx::get_sreg_laneid();\n  auto logical_warp              = is_power_of_two ? threadIdx.x / LogicalWarpThreads : threadIdx.x / warp_size;\n  auto logical_lane              = is_power_of_two ? threadIdx.x % LogicalWarpThreads : lane;\n  auto limit                     = EnableNumItems ? num_items : LogicalWarpThreads;\n  if (!is_power_of_two && lane >= limit)\n  {\n    return;\n  }\n  warp_reduce_t warp_reduce{storage[logical_warp]};\n  using result_t = decltype(reduction_op(warp_reduce, thread_data));\n  result_t result;\n  if constexpr (EnableNumItems)\n  {\n    result = reduction_op(warp_reduce, thread_data, num_items);\n  }\n  else\n  {\n    result = reduction_op(warp_reduce, thread_data);\n  }\n  if (logical_lane == 0)\n  {\n    output[logical_warp] = result;\n  }\n}\n\ntemplate <unsigned LogicalWarpThreads, bool EnableNumItems, typename T, typename ReductionOp>\n__global__ void warp_reduce_kernel(T* input, T* output, ReductionOp reduction_op, int num_items = 0)\n{\n  auto thread_data = input[threadIdx.x];\n  warp_reduce_function<LogicalWarpThreads, EnableNumItems>(thread_data, output, reduction_op, num_items);\n}\n\ntemplate <unsigned LogicalWarpThreads, typename T, typename ReductionOp>\n__global__ void warp_reduce_multiple_items_kernel(T* input, T* output, ReductionOp reduction_op)\n{\n  T thread_data[num_items_per_thread];\n  for (int i = 0; i < num_items_per_thread; ++i)\n  {\n    thread_data[i] = input[threadIdx.x * num_items_per_thread + i];\n  }\n  warp_reduce_function<LogicalWarpThreads>(thread_data, output, reduction_op);\n}\n\ntemplate <typename Op, typename T>\nstruct warp_reduce_t\n{\n  template <int LogicalWarpThreads>\n  __device__ auto operator()(cub::WarpReduce<T, LogicalWarpThreads> warp_reduce, T& data) const\n  {\n    return warp_reduce.Reduce(data, Op{});\n  }\n\n  template <int LogicalWarpThreads>\n  __device__ auto operator()(cub::WarpReduce<T, LogicalWarpThreads> warp_reduce, T& data, int num_items) const\n  {\n    return warp_reduce.Reduce(data, Op{}, num_items);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::std::plus<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(cub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Sum(args...);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::maximum<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(cub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Max(args...);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::minimum<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(cub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Min(args...);\n  }\n};\n\ntemplate <int LogicalWarpThreads, bool EnableNumItems = false, typename T, typename... TArgs>\nvoid warp_reduce_launch(c2h::device_vector<T>& input, c2h::device_vector<T>& output, TArgs... args)\n{\n  warp_reduce_kernel<LogicalWarpThreads, EnableNumItems><<<1, total_warps * warp_size>>>(\n    thrust::raw_pointer_cast(input.data()), thrust::raw_pointer_cast(output.data()), args...);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\ntemplate <int LogicalWarpThreads, typename T, typename... TArgs>\nvoid warp_reduce_multiple_items_launch(c2h::device_vector<T>& input, c2h::device_vector<T>& output, TArgs... args)\n{\n  warp_reduce_multiple_items_kernel<LogicalWarpThreads><<<1, total_warps * warp_size>>>(\n    thrust::raw_pointer_cast(input.data()), thrust::raw_pointer_cast(output.data()), args...);\n\n  REQUIRE(cudaSuccess == cudaPeekAtLastError());\n  REQUIRE(cudaSuccess == cudaDeviceSynchronize());\n}\n\n/***********************************************************************************************************************\n * Types\n **********************************************************************************************************************/\n\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\n\nusing full_type_list =\n  c2h::type_list<uint8_t,\n                 uint16_t,\n                 int32_t,\n                 int64_t,\n                 custom_t,\n#if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 uchar3,\n                 short2>;\n\nusing builtin_type_list = c2h::type_list<uint8_t, uint16_t, int32_t, int64_t>;\n\nusing predefined_op_list = c2h::type_list<cuda::std::plus<>, cuda::maximum<>, cuda::minimum<>>;\n\nusing logical_warp_threads = c2h::enum_type_list<unsigned, 32, 16, 9, 7, 1>;\n\n/***********************************************************************************************************************\n * Reference\n **********************************************************************************************************************/\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244) // numeric(33): C: '=': conversion from 'int' to '_Ty', possible loss of data\n\ntemplate <typename predefined_op, typename T>\nvoid compute_host_reference(\n  const c2h::host_vector<T>& h_in,\n  c2h::host_vector<T>& h_out,\n  int logical_warps,\n  int logical_warp_threads,\n  int items_per_logical_warp = 0,\n  int items_per_thread       = 1)\n{\n  const auto identity    = identity_v<predefined_op, T>;\n  items_per_logical_warp = items_per_logical_warp == 0 ? logical_warp_threads : items_per_logical_warp;\n  for (unsigned i = 0; i < total_warps; ++i)\n  {\n    for (int j = 0; j < logical_warps; ++j)\n    {\n      auto start                   = h_in.begin() + (i * warp_size + j * logical_warp_threads) * items_per_thread;\n      auto end                     = start + items_per_logical_warp * items_per_thread;\n      h_out[i * logical_warps + j] = static_cast<T>(std::accumulate(start, end, identity, predefined_op{}));\n    }\n  }\n}\n\n_CCCL_DIAG_POP\n\nstd::array<unsigned, 3> get_test_config(unsigned logical_warp_threads, unsigned items_per_thread = 1)\n{\n  bool is_power_of_two = cuda::is_power_of_two(logical_warp_threads);\n  auto logical_warps   = is_power_of_two ? warp_size / logical_warp_threads : 1;\n  auto input_size      = total_warps * warp_size * items_per_thread;\n  auto output_size     = total_warps * logical_warps;\n  return {input_size, output_size, logical_warps};\n}\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"WarpReduce::Sum, full_type_list\", \"[reduce][warp][predefined_op][full]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<cuda::std::plus<>, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min, builtin types\",\n         \"[reduce][warp][predefined_op][full]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<predefined_op, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::CustomSum\", \"[reduce][warp][generic][full]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(1), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<custom_plus, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// partial\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min Partial\",\n         \"[reduce][warp][predefined_op][partial]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  const int valid_items                         = GENERATE_COPY(take(2, random(1u, logical_warp_threads)));\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads, valid_items);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads, true>(d_in, d_out, warp_reduce_t<predefined_op, T>{}, valid_items);\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads, valid_items);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::Sum\", \"[reduce][warp][generic][partial]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  const int valid_items                         = GENERATE_COPY(take(2, random(1u, logical_warp_threads)));\n  CAPTURE(c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads, true>(d_in, d_out, warp_reduce_t<custom_plus, T>{}, valid_items);\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads, valid_items);\n  verify_results(h_out, d_out);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// multiple items per thread\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min Multiple Items Per Thread\",\n         \"[reduce][warp][predefined_op][full]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads, num_items_per_thread);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_multiple_items_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<predefined_op, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads, 0, num_items_per_thread);\n  verify_results(h_out, d_out);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: BSD-3-Clause\n\n#include \"hip/hip_runtime.h\"\n#include <cub/util_macro.cuh>\n#include <cub/warp/warp_reduce.cuh>\n\n#include <cuda/cmath>\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/ptx>\n#include <cuda/std/__functional/invoke.h>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/type_traits>\n\n#include <array>\n#include <numeric>\n\n#include <test_util.h>\n\n#include <c2h/catch2_test_helper.h>\n#include <c2h/check_results.cuh>\n#include <c2h/custom_type.h>\n#include <c2h/operator.cuh>\n\n/***********************************************************************************************************************\n * Constants\n **********************************************************************************************************************/\n\ninline constexpr int warp_size            = 32;\ninline constexpr auto total_warps         = 4u;\ninline constexpr int num_items_per_thread = 4;\n\n/***********************************************************************************************************************\n * Kernel\n **********************************************************************************************************************/\n\ntemplate <unsigned LogicalWarpThreads, bool EnableNumItems = false, typename T, typename Output, typename ReductionOp>\n__device__ void warp_reduce_function(T& thread_data, Output* output, ReductionOp reduction_op, int num_items = 0)\n{\n  using warp_reduce_t = hipcub::WarpReduce<Output, LogicalWarpThreads>;\n  using storage_t     = typename warp_reduce_t::TempStorage;\n  __shared__ storage_t storage[total_warps];\n  constexpr bool is_power_of_two = cuda::is_power_of_two(LogicalWarpThreads);\n  auto lane                      = cuda::ptx::get_sreg_laneid();\n  auto logical_warp              = is_power_of_two ? threadIdx.x / LogicalWarpThreads : threadIdx.x / warp_size;\n  auto logical_lane              = is_power_of_two ? threadIdx.x % LogicalWarpThreads : lane;\n  auto limit                     = EnableNumItems ? num_items : LogicalWarpThreads;\n  if (!is_power_of_two && lane >= limit)\n  {\n    return;\n  }\n  warp_reduce_t warp_reduce{storage[logical_warp]};\n  using result_t = decltype(reduction_op(warp_reduce, thread_data));\n  result_t result;\n  if constexpr (EnableNumItems)\n  {\n    result = reduction_op(warp_reduce, thread_data, num_items);\n  }\n  else\n  {\n    result = reduction_op(warp_reduce, thread_data);\n  }\n  if (logical_lane == 0)\n  {\n    output[logical_warp] = result;\n  }\n}\n\ntemplate <unsigned LogicalWarpThreads, bool EnableNumItems, typename T, typename ReductionOp>\n__global__ void warp_reduce_kernel(T* input, T* output, ReductionOp reduction_op, int num_items = 0)\n{\n  auto thread_data = input[threadIdx.x];\n  warp_reduce_function<LogicalWarpThreads, EnableNumItems>(thread_data, output, reduction_op, num_items);\n}\n\ntemplate <unsigned LogicalWarpThreads, typename T, typename ReductionOp>\n__global__ void warp_reduce_multiple_items_kernel(T* input, T* output, ReductionOp reduction_op)\n{\n  T thread_data[num_items_per_thread];\n  for (int i = 0; i < num_items_per_thread; ++i)\n  {\n    thread_data[i] = input[threadIdx.x * num_items_per_thread + i];\n  }\n  warp_reduce_function<LogicalWarpThreads>(thread_data, output, reduction_op);\n}\n\ntemplate <typename Op, typename T>\nstruct warp_reduce_t\n{\n  template <int LogicalWarpThreads>\n  __device__ auto operator()(hipcub::WarpReduce<T, LogicalWarpThreads> warp_reduce, T& data) const\n  {\n    return warp_reduce.Reduce(data, Op{});\n  }\n\n  template <int LogicalWarpThreads>\n  __device__ auto operator()(hipcub::WarpReduce<T, LogicalWarpThreads> warp_reduce, T& data, int num_items) const\n  {\n    return warp_reduce.Reduce(data, Op{}, num_items);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::std::plus<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(hipcub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Sum(args...);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::maximum<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(hipcub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Max(args...);\n  }\n};\n\ntemplate <typename T>\nstruct warp_reduce_t<cuda::minimum<>, T>\n{\n  template <int LogicalWarpThreads, typename... TArgs>\n  __device__ auto operator()(hipcub::WarpReduce<T, LogicalWarpThreads> warp_reduce, TArgs&&... args) const\n  {\n    return warp_reduce.Min(args...);\n  }\n};\n\ntemplate <int LogicalWarpThreads, bool EnableNumItems = false, typename T, typename... TArgs>\nvoid warp_reduce_launch(c2h::device_vector<T>& input, c2h::device_vector<T>& output, TArgs... args)\n{\n  warp_reduce_kernel<LogicalWarpThreads, EnableNumItems><<<1, total_warps * warp_size>>>(\n    thrust::raw_pointer_cast(input.data()), thrust::raw_pointer_cast(output.data()), args...);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\ntemplate <int LogicalWarpThreads, typename T, typename... TArgs>\nvoid warp_reduce_multiple_items_launch(c2h::device_vector<T>& input, c2h::device_vector<T>& output, TArgs... args)\n{\n  warp_reduce_multiple_items_kernel<LogicalWarpThreads><<<1, total_warps * warp_size>>>(\n    thrust::raw_pointer_cast(input.data()), thrust::raw_pointer_cast(output.data()), args...);\n\n  REQUIRE(hipSuccess == hipPeekAtLastError());\n  REQUIRE(hipSuccess == hipDeviceSynchronize());\n}\n\n/***********************************************************************************************************************\n * Types\n **********************************************************************************************************************/\n\nusing custom_t =\n  c2h::custom_type_t<c2h::accumulateable_t, c2h::equal_comparable_t, c2h::lexicographical_less_comparable_t>;\n\nusing full_type_list =\n  c2h::type_list<uint8_t,\n                 uint16_t,\n                 int32_t,\n                 int64_t,\n                 custom_t,\n#if _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4_16a,\n#else // _CCCL_CTK_AT_LEAST(13, 0)\n                 ulonglong4,\n#endif // _CCCL_CTK_AT_LEAST(13, 0)\n                 uchar3,\n                 short2>;\n\nusing builtin_type_list = c2h::type_list<uint8_t, uint16_t, int32_t, int64_t>;\n\nusing predefined_op_list = c2h::type_list<cuda::std::plus<>, cuda::maximum<>, cuda::minimum<>>;\n\nusing logical_warp_threads = c2h::enum_type_list<unsigned, 32, 16, 9, 7, 1>;\n\n/***********************************************************************************************************************\n * Reference\n **********************************************************************************************************************/\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244) // numeric(33): C: '=': conversion from 'int' to '_Ty', possible loss of data\n\ntemplate <typename predefined_op, typename T>\nvoid compute_host_reference(\n  const c2h::host_vector<T>& h_in,\n  c2h::host_vector<T>& h_out,\n  int logical_warps,\n  int logical_warp_threads,\n  int items_per_logical_warp = 0,\n  int items_per_thread       = 1)\n{\n  const auto identity    = identity_v<predefined_op, T>;\n  items_per_logical_warp = items_per_logical_warp == 0 ? logical_warp_threads : items_per_logical_warp;\n  for (unsigned i = 0; i < total_warps; ++i)\n  {\n    for (int j = 0; j < logical_warps; ++j)\n    {\n      auto start                   = h_in.begin() + (i * warp_size + j * logical_warp_threads) * items_per_thread;\n      auto end                     = start + items_per_logical_warp * items_per_thread;\n      h_out[i * logical_warps + j] = static_cast<T>(std::accumulate(start, end, identity, predefined_op{}));\n    }\n  }\n}\n\n_CCCL_DIAG_POP\n\nstd::array<unsigned, 3> get_test_config(unsigned logical_warp_threads, unsigned items_per_thread = 1)\n{\n  bool is_power_of_two = cuda::is_power_of_two(logical_warp_threads);\n  auto logical_warps   = is_power_of_two ? warp_size / logical_warp_threads : 1;\n  auto input_size      = total_warps * warp_size * items_per_thread;\n  auto output_size     = total_warps * logical_warps;\n  return {input_size, output_size, logical_warps};\n}\n\n/***********************************************************************************************************************\n * Test cases\n **********************************************************************************************************************/\n\nC2H_TEST(\"WarpReduce::Sum, full_type_list\", \"[reduce][warp][predefined_op][full]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<cuda::std::plus<>, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min, builtin types\",\n         \"[reduce][warp][predefined_op][full]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<predefined_op, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::CustomSum\", \"[reduce][warp][generic][full]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  CAPTURE(c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(1), d_in);\n  warp_reduce_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<custom_plus, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads);\n  verify_results(h_out, d_out);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// partial\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min Partial\",\n         \"[reduce][warp][predefined_op][partial]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  const int valid_items                         = GENERATE_COPY(take(2, random(1u, logical_warp_threads)));\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads, valid_items);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads, true>(d_in, d_out, warp_reduce_t<predefined_op, T>{}, valid_items);\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads, valid_items);\n  verify_results(h_out, d_out);\n}\n\nC2H_TEST(\"WarpReduce::Sum\", \"[reduce][warp][generic][partial]\", full_type_list, logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<1, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads);\n  const int valid_items                         = GENERATE_COPY(take(2, random(1u, logical_warp_threads)));\n  CAPTURE(c2h::type_name<T>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_launch<logical_warp_threads, true>(d_in, d_out, warp_reduce_t<custom_plus, T>{}, valid_items);\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<cuda::std::plus<>>(h_in, h_out, logical_warps, logical_warp_threads, valid_items);\n  verify_results(h_out, d_out);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n// multiple items per thread\n\nC2H_TEST(\"WarpReduce::Sum/Max/Min Multiple Items Per Thread\",\n         \"[reduce][warp][predefined_op][full]\",\n         builtin_type_list,\n         predefined_op_list,\n         logical_warp_threads)\n{\n  using T                                       = c2h::get<0, TestType>;\n  using predefined_op                           = c2h::get<1, TestType>;\n  constexpr auto logical_warp_threads           = c2h::get<2, TestType>::value;\n  auto [input_size, output_size, logical_warps] = get_test_config(logical_warp_threads, num_items_per_thread);\n  CAPTURE(c2h::type_name<T>(), c2h::type_name<predefined_op>(), logical_warp_threads);\n  c2h::device_vector<T> d_in(input_size);\n  c2h::device_vector<T> d_out(output_size);\n  c2h::gen(C2H_SEED(10), d_in);\n  warp_reduce_multiple_items_launch<logical_warp_threads>(d_in, d_out, warp_reduce_t<predefined_op, T>{});\n\n  c2h::host_vector<T> h_in = d_in;\n  c2h::host_vector<T> h_out(output_size);\n  compute_host_reference<predefined_op>(h_in, h_out, logical_warps, logical_warp_threads, 0, num_items_per_thread);\n  verify_results(h_out, d_out);\n}\n", "source": "/kaggle/working/cccl/cub/test/warp/catch2_test_warp_reduce.cu"}
{"input": "#include <thrust/detail/config.h>\n\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/system/cuda/execution_policy.h>\n\n#include <cassert>\n#include <future>\n\n// This example demonstrates two ways to achieve algorithm invocations that are asynchronous with\n// the calling thread.\n//\n// The first method wraps a call to thrust::reduce inside a __global__ function. Since __global__ function\n// launches are asynchronous with the launching thread, this achieves asynchrony. The result of the reduction\n// is stored to a pointer to CUDA global memory. The calling thread waits for the result of the reduction to\n// be ready by synchronizing with the CUDA stream on which the __global__ function is launched.\n//\n// The second method uses the C++11 library function, std::async, to create concurrency. The lambda function\n// given to std::async returns the result of thrust::reduce to a std::future. The calling thread can use the\n// std::future to wait for the result of the reduction. This method requires a compiler which supports\n// C++11-capable language and library constructs.\n\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\ntemplate <typename Iterator, typename T, typename BinaryOperation, typename Pointer>\n__global__ void reduce_kernel(Iterator first, Iterator last, T init, BinaryOperation binary_op, Pointer result)\n{\n  *result = thrust::reduce(thrust::cuda::par, first, last, init, binary_op);\n}\n#endif\n\nint main()\n{\n  size_t n = 1 << 20;\n  thrust::device_vector<unsigned int> data(n, 1);\n  thrust::device_vector<unsigned int> result(1, 0);\n\n  // method 1: call thrust::reduce from an asynchronous CUDA kernel launch\n\n  // create a CUDA stream\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // launch a CUDA kernel with only 1 thread on our stream\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\n  reduce_kernel<<<1, 1, 0, s>>>(data.begin(), data.end(), 0, cuda::std::plus<int>(), result.data());\n#else\n  result[0] = thrust::reduce(thrust::cuda::par, data.begin(), data.end(), 0, cuda::std::plus<int>());\n#endif\n\n  // wait for the stream to finish\n  cudaStreamSynchronize(s);\n\n  // our result should be ready\n  assert(result[0] == n);\n\n  cudaStreamDestroy(s);\n\n  // reset the result\n  result[0] = 0;\n\n  // method 2: use std::async to create asynchrony\n  // copy all the algorithm parameters\n  auto begin        = data.begin();\n  auto end          = data.end();\n  unsigned int init = 0;\n  auto binary_op    = cuda::std::plus<unsigned int>();\n\n  // std::async captures the algorithm parameters by value\n  // use std::launch::async to ensure the creation of a new thread\n  std::future<unsigned int> future_result = std::async(std::launch::async, [=] {\n    return thrust::reduce(begin, end, init, binary_op);\n  });\n\n  // wait on the result and check that it is correct\n  assert(future_result.get() == n);\n\n  return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/detail/config.h>\n\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/system/cuda/execution_policy.h>\n\n#include <cassert>\n#include <future>\n\n// This example demonstrates two ways to achieve algorithm invocations that are asynchronous with\n// the calling thread.\n//\n// The first method wraps a call to thrust::reduce inside a __global__ function. Since __global__ function\n// launches are asynchronous with the launching thread, this achieves asynchrony. The result of the reduction\n// is stored to a pointer to CUDA global memory. The calling thread waits for the result of the reduction to\n// be ready by synchronizing with the CUDA stream on which the __global__ function is launched.\n//\n// The second method uses the C++11 library function, std::async, to create concurrency. The lambda function\n// given to std::async returns the result of thrust::reduce to a std::future. The calling thread can use the\n// std::future to wait for the result of the reduction. This method requires a compiler which supports\n// C++11-capable language and library constructs.\n\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\ntemplate <typename Iterator, typename T, typename BinaryOperation, typename Pointer>\n__global__ void reduce_kernel(Iterator first, Iterator last, T init, BinaryOperation binary_op, Pointer result)\n{\n  *result = thrust::reduce(thrust::cuda::par, first, last, init, binary_op);\n}\n#endif\n\nint main()\n{\n  size_t n = 1 << 20;\n  thrust::device_vector<unsigned int> data(n, 1);\n  thrust::device_vector<unsigned int> result(1, 0);\n\n  // method 1: call thrust::reduce from an asynchronous CUDA kernel launch\n\n  // create a CUDA stream\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // launch a CUDA kernel with only 1 thread on our stream\n#ifdef THRUST_EXAMPLE_DEVICE_SIDE\n  reduce_kernel<<<1, 1, 0, s>>>(data.begin(), data.end(), 0, cuda::std::plus<int>(), result.data());\n#else\n  result[0] = thrust::reduce(thrust::cuda::par, data.begin(), data.end(), 0, cuda::std::plus<int>());\n#endif\n\n  // wait for the stream to finish\n  hipStreamSynchronize(s);\n\n  // our result should be ready\n  assert(result[0] == n);\n\n  hipStreamDestroy(s);\n\n  // reset the result\n  result[0] = 0;\n\n  // method 2: use std::async to create asynchrony\n  // copy all the algorithm parameters\n  auto begin        = data.begin();\n  auto end          = data.end();\n  unsigned int init = 0;\n  auto binary_op    = cuda::std::plus<unsigned int>();\n\n  // std::async captures the algorithm parameters by value\n  // use std::launch::async to ensure the creation of a new thread\n  std::future<unsigned int> future_result = std::async(std::launch::async, [=] {\n    return thrust::reduce(begin, end, init, binary_op);\n  });\n\n  // wait on the result and check that it is correct\n  assert(future_result.get() == n);\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/thrust/examples/cuda/async_reduce.cu"}
{"input": "#include <thrust/device_ptr.h>\n#include <thrust/device_vector.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <unittest/unittest.h>\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244 4267) // possible loss of data\n\ntemplate <typename Iterator1, typename Iterator2>\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n__global__\n#endif\n  void\n  simple_copy_on_device(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  while (first1 != last1)\n  {\n    *(first2++) = *(first1++);\n  }\n}\n\ntemplate <typename Iterator1, typename Iterator2>\nvoid simple_copy(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n  simple_copy_on_device<<<1, 1>>>(first1, last1, first2);\n#else\n  simple_copy_on_device(first1, last1, first2);\n#endif\n}\n\nvoid TestDeviceDereferenceDeviceVectorIterator()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  simple_copy(input.begin(), input.end(), output.begin());\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDeviceVectorIterator);\n\nvoid TestDeviceDereferenceDevicePtr()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  thrust::device_ptr<int> _first1 = &input[0];\n  thrust::device_ptr<int> _last1  = _first1 + input.size();\n  thrust::device_ptr<int> _first2 = &output[0];\n\n  simple_copy(_first1, _last1, _first2);\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDevicePtr);\n\nvoid TestDeviceDereferenceTransformIterator()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIterator);\n\nvoid TestDeviceDereferenceTransformIteratorInputConversion()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<double> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input == output, true);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIteratorInputConversion);\n\nvoid TestDeviceDereferenceTransformIteratorOutputConversion()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<double> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input == output, true);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIteratorOutputConversion);\n\nvoid TestDeviceDereferenceCountingIterator()\n{\n  thrust::counting_iterator<int> first(1);\n  thrust::counting_iterator<int> last(6);\n\n  thrust::device_vector<int> output(5);\n\n  simple_copy(first, last, output.begin());\n\n  thrust::device_vector<int> ref{1, 2, 3, 4, 5};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceCountingIterator);\n\nvoid TestDeviceDereferenceTransformedCountingIterator()\n{\n  thrust::counting_iterator<int> first(1);\n  thrust::counting_iterator<int> last(6);\n\n  thrust::device_vector<int> output(5);\n\n  simple_copy(thrust::make_transform_iterator(first, ::cuda::std::negate<int>()),\n              thrust::make_transform_iterator(last, ::cuda::std::negate<int>()),\n              output.begin());\n\n  thrust::device_vector<int> ref{-1, -2, -3, -4, -5};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformedCountingIterator);\n\n_CCCL_DIAG_POP\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/device_ptr.h>\n#include <thrust/device_vector.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <unittest/unittest.h>\n\n_CCCL_DIAG_PUSH\n_CCCL_DIAG_SUPPRESS_MSVC(4244 4267) // possible loss of data\n\ntemplate <typename Iterator1, typename Iterator2>\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n__global__\n#endif\n  void\n  simple_copy_on_device(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  while (first1 != last1)\n  {\n    *(first2++) = *(first1++);\n  }\n}\n\ntemplate <typename Iterator1, typename Iterator2>\nvoid simple_copy(Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA\n  simple_copy_on_device<<<1, 1>>>(first1, last1, first2);\n#else\n  simple_copy_on_device(first1, last1, first2);\n#endif\n}\n\nvoid TestDeviceDereferenceDeviceVectorIterator()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  simple_copy(input.begin(), input.end(), output.begin());\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDeviceVectorIterator);\n\nvoid TestDeviceDereferenceDevicePtr()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  thrust::device_ptr<int> _first1 = &input[0];\n  thrust::device_ptr<int> _last1  = _first1 + input.size();\n  thrust::device_ptr<int> _first2 = &output[0];\n\n  simple_copy(_first1, _last1, _first2);\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceDevicePtr);\n\nvoid TestDeviceDereferenceTransformIterator()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<int> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input, output);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIterator);\n\nvoid TestDeviceDereferenceTransformIteratorInputConversion()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<double> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input == output, true);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIteratorInputConversion);\n\nvoid TestDeviceDereferenceTransformIteratorOutputConversion()\n{\n  thrust::device_vector<int> input = unittest::random_integers<int>(100);\n  thrust::device_vector<double> output(input.size(), 0);\n\n  simple_copy(thrust::make_transform_iterator(input.begin(), ::cuda::std::identity{}),\n              thrust::make_transform_iterator(input.end(), ::cuda::std::identity{}),\n              output.begin());\n\n  ASSERT_EQUAL(input == output, true);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformIteratorOutputConversion);\n\nvoid TestDeviceDereferenceCountingIterator()\n{\n  thrust::counting_iterator<int> first(1);\n  thrust::counting_iterator<int> last(6);\n\n  thrust::device_vector<int> output(5);\n\n  simple_copy(first, last, output.begin());\n\n  thrust::device_vector<int> ref{1, 2, 3, 4, 5};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceCountingIterator);\n\nvoid TestDeviceDereferenceTransformedCountingIterator()\n{\n  thrust::counting_iterator<int> first(1);\n  thrust::counting_iterator<int> last(6);\n\n  thrust::device_vector<int> output(5);\n\n  simple_copy(thrust::make_transform_iterator(first, ::cuda::std::negate<int>()),\n              thrust::make_transform_iterator(last, ::cuda::std::negate<int>()),\n              output.begin());\n\n  thrust::device_vector<int> ref{-1, -2, -3, -4, -5};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestDeviceDereferenceTransformedCountingIterator);\n\n_CCCL_DIAG_POP\n", "source": "/kaggle/working/cccl/thrust/testing/dereference.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/unique.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_equal_div_10_unique\n{\n  _CCCL_HOST_DEVICE bool operator()(const T x, const T& y) const\n  {\n    return ((int) x / 10) == ((int) y / 10);\n  }\n};\n\ntemplate <typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys = {11, 11, 21, 20, 21, 21, 21, 37, 37};\n}\n\ntemplate <typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values = {0, 1, 2, 3, 4, 5, 6, 7, 8};\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void unique_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void unique_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  BinaryPredicate pred,\n  Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<typename Vector::iterator, typename Vector::iterator>;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_kernel<<<1, 1>>>(exec, keys.begin(), keys.end(), values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 5);\n  keys.erase(new_last.first, keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  values.erase(new_last.second, values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 3);\n  keys.erase(new_last.first, keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  values.erase(new_last.second, values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(values, values_ref);\n}\n\nvoid TestUniqueByKeyDeviceSeq()\n{\n  TestUniqueByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceSeq);\n\nvoid TestUniqueByKeyDeviceDevice()\n{\n  TestUniqueByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceDevice);\n\nvoid TestUniqueByKeyDeviceNoSync()\n{\n  TestUniqueByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<Vector::iterator, Vector::iterator>;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 5);\n  keys.erase(new_last.first, keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  values.erase(new_last.second, values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>());\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 3);\n  keys.erase(new_last.first, keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  values.erase(new_last.second, values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueByKeyCudaStreamsSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsSync);\n\nvoid TestUniqueByKeyCudaStreamsNoSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5>\n__global__ void unique_by_key_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename Iterator5>\n__global__ void unique_by_key_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<typename Vector::iterator, typename Vector::iterator>;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  unique_by_key_copy_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  output_keys.erase(new_last.first, output_keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  output_values.erase(new_last.second, output_values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_copy_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_unique<T>(),\n    new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n\n  output_keys.erase(new_last.first, output_keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  output_values.erase(new_last.second, output_values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n}\n\nvoid TestUniqueCopyByKeyDeviceSeq()\n{\n  TestUniqueCopyByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceSeq);\n\nvoid TestUniqueCopyByKeyDeviceDevice()\n{\n  TestUniqueCopyByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceDevice);\n\nvoid TestUniqueCopyByKeyDeviceNoSync()\n{\n  TestUniqueCopyByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<Vector::iterator, Vector::iterator>;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key_copy(\n    streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  output_keys.erase(new_last.first, output_keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  output_values.erase(new_last.second, output_values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::unique_by_key_copy(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_unique<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  output_keys.erase(new_last.first, output_keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  output_values.erase(new_last.second, output_values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCopyByKeyCudaStreamsSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsSync);\n\nvoid TestUniqueCopyByKeyCudaStreamsNoSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsNoSync);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/unique.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_equal_div_10_unique\n{\n  _CCCL_HOST_DEVICE bool operator()(const T x, const T& y) const\n  {\n    return ((int) x / 10) == ((int) y / 10);\n  }\n};\n\ntemplate <typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys = {11, 11, 21, 20, 21, 21, 21, 37, 37};\n}\n\ntemplate <typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values = {0, 1, 2, 3, 4, 5, 6, 7, 8};\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void unique_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void unique_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  BinaryPredicate pred,\n  Iterator3 result)\n{\n  *result = thrust::unique_by_key(exec, keys_first, keys_last, values_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<typename Vector::iterator, typename Vector::iterator>;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_kernel<<<1, 1>>>(exec, keys.begin(), keys.end(), values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 5);\n  keys.erase(new_last.first, keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  values.erase(new_last.second, values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 3);\n  keys.erase(new_last.first, keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  values.erase(new_last.second, values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(values, values_ref);\n}\n\nvoid TestUniqueByKeyDeviceSeq()\n{\n  TestUniqueByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceSeq);\n\nvoid TestUniqueByKeyDeviceDevice()\n{\n  TestUniqueByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceDevice);\n\nvoid TestUniqueByKeyDeviceNoSync()\n{\n  TestUniqueByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<Vector::iterator, Vector::iterator>;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 5);\n  keys.erase(new_last.first, keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 5);\n  values.erase(new_last.second, values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::unique_by_key(streampolicy, keys.begin(), keys.end(), values.begin(), is_equal_div_10_unique<T>());\n\n  ASSERT_EQUAL(new_last.first - keys.begin(), 3);\n  keys.erase(new_last.first, keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - values.begin(), 3);\n  values.erase(new_last.second, values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(values, values_ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueByKeyCudaStreamsSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsSync);\n\nvoid TestUniqueByKeyCudaStreamsNoSync()\n{\n  TestUniqueByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueByKeyCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5>\n__global__ void unique_by_key_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename Iterator5>\n__global__ void unique_by_key_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  Iterator5 result)\n{\n  *result = thrust::unique_by_key_copy(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<typename Vector::iterator, typename Vector::iterator>;\n  thrust::device_vector<iter_pair> new_last_vec(1);\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  unique_by_key_copy_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  output_keys.erase(new_last.first, output_keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  output_values.erase(new_last.second, output_values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  unique_by_key_copy_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_unique<T>(),\n    new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n\n  output_keys.erase(new_last.first, output_keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  output_values.erase(new_last.second, output_values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n}\n\nvoid TestUniqueCopyByKeyDeviceSeq()\n{\n  TestUniqueCopyByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceSeq);\n\nvoid TestUniqueCopyByKeyDeviceDevice()\n{\n  TestUniqueCopyByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceDevice);\n\nvoid TestUniqueCopyByKeyDeviceNoSync()\n{\n  TestUniqueCopyByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  using iter_pair = cuda::std::pair<Vector::iterator, Vector::iterator>;\n  iter_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_by_key_copy(\n    streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  output_keys.erase(new_last.first, output_keys.end());\n  Vector keys_ref{11, 21, 20, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  output_values.erase(new_last.second, output_values.end());\n  Vector values_ref{0, 2, 3, 4, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::unique_by_key_copy(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_unique<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  output_keys.erase(new_last.first, output_keys.end());\n  keys_ref = {11, 21, 37};\n  ASSERT_EQUAL(output_keys, keys_ref);\n\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  output_values.erase(new_last.second, output_values.end());\n  values_ref = {0, 2, 7};\n  ASSERT_EQUAL(output_values, values_ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCopyByKeyCudaStreamsSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsSync);\n\nvoid TestUniqueCopyByKeyCudaStreamsNoSync()\n{\n  TestUniqueCopyByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyByKeyCudaStreamsNoSync);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/unique_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/mismatch.h>\n\n#include <cuda/std/atomic>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nmismatch_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::mismatch(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMismatchDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a = {1, 2, 3, 4};\n  thrust::device_vector<int> b = {1, 2, 4, 3};\n\n  using pair_type =\n    cuda::std::pair<typename thrust::device_vector<int>::iterator, typename thrust::device_vector<int>::iterator>;\n\n  thrust::device_vector<pair_type> d_result(1);\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(2, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(2, ((pair_type) d_result[0]).second - b.begin());\n\n  b[2] = 3;\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(3, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(3, ((pair_type) d_result[0]).second - b.begin());\n\n  b[3] = 4;\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(4, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(4, ((pair_type) d_result[0]).second - b.begin());\n}\n\nvoid TestMismatchDeviceSeq()\n{\n  TestMismatchDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMismatchDeviceSeq);\n\nvoid TestMismatchDeviceDevice()\n{\n  TestMismatchDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMismatchDeviceDevice);\n#endif\n\nvoid TestMismatchCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector a = {1, 2, 3, 4};\n  Vector b = {1, 2, 4, 3};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 2);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 2);\n\n  b[2] = 3;\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 3);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 3);\n\n  b[3] = 4;\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 4);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMismatchCudaStreams);\n\n// see https://github.com/NVIDIA/cccl/issues/3591\ntemplate <typename T>\nclass Wrapper\n{\npublic:\n  Wrapper()\n  {\n    ++my_count;\n  }\n\n  _CCCL_HOST_DEVICE bool operator==(const Wrapper&) const\n  {\n    return true;\n  }\n\n  ~Wrapper()\n  {\n    --my_count;\n  }\n\nprivate:\n  static cuda::std::atomic<size_t> my_count;\n  T dummy;\n};\n\nvoid TestMismatchBug3591()\n{\n  using T = Wrapper<int32_t>;\n  T* p    = nullptr;\n  thrust::mismatch(thrust::device, p, p, p, cuda::std::equal_to<T>());\n}\nDECLARE_UNITTEST(TestMismatchBug3591);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/mismatch.h>\n\n#include <cuda/std/atomic>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nmismatch_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::mismatch(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMismatchDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> a = {1, 2, 3, 4};\n  thrust::device_vector<int> b = {1, 2, 4, 3};\n\n  using pair_type =\n    cuda::std::pair<typename thrust::device_vector<int>::iterator, typename thrust::device_vector<int>::iterator>;\n\n  thrust::device_vector<pair_type> d_result(1);\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(2, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(2, ((pair_type) d_result[0]).second - b.begin());\n\n  b[2] = 3;\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(3, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(3, ((pair_type) d_result[0]).second - b.begin());\n\n  b[3] = 4;\n\n  mismatch_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(4, ((pair_type) d_result[0]).first - a.begin());\n  ASSERT_EQUAL(4, ((pair_type) d_result[0]).second - b.begin());\n}\n\nvoid TestMismatchDeviceSeq()\n{\n  TestMismatchDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMismatchDeviceSeq);\n\nvoid TestMismatchDeviceDevice()\n{\n  TestMismatchDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMismatchDeviceDevice);\n#endif\n\nvoid TestMismatchCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector a = {1, 2, 3, 4};\n  Vector b = {1, 2, 4, 3};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 2);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 2);\n\n  b[2] = 3;\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 3);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 3);\n\n  b[3] = 4;\n\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).first - a.begin(), 4);\n  ASSERT_EQUAL(thrust::mismatch(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin()).second - b.begin(), 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMismatchCudaStreams);\n\n// see https://github.com/NVIDIA/cccl/issues/3591\ntemplate <typename T>\nclass Wrapper\n{\npublic:\n  Wrapper()\n  {\n    ++my_count;\n  }\n\n  _CCCL_HOST_DEVICE bool operator==(const Wrapper&) const\n  {\n    return true;\n  }\n\n  ~Wrapper()\n  {\n    --my_count;\n  }\n\nprivate:\n  static cuda::std::atomic<size_t> my_count;\n  T dummy;\n};\n\nvoid TestMismatchBug3591()\n{\n  using T = Wrapper<int32_t>;\n  T* p    = nullptr;\n  thrust::mismatch(thrust::device, p, p, p, cuda::std::equal_to<T>());\n}\nDECLARE_UNITTEST(TestMismatchBug3591);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/mismatch.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/inner_product.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__ void inner_product_kernel(\n  ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, T init, Iterator3 result)\n{\n  *result = thrust::inner_product(exec, first1, last1, first2, init);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestInnerProductDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_v1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_v2 = unittest::random_integers<int>(n);\n\n  thrust::device_vector<int> d_v1 = h_v1;\n  thrust::device_vector<int> d_v2 = h_v2;\n\n  thrust::device_vector<int> result(1);\n\n  int init = 13;\n\n  int expected = thrust::inner_product(h_v1.begin(), h_v1.end(), h_v2.begin(), init);\n\n  inner_product_kernel<<<1, 1>>>(exec, d_v1.begin(), d_v1.end(), d_v2.begin(), init, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(expected, result[0]);\n}\n\nvoid TestInnerProductDeviceSeq()\n{\n  TestInnerProductDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceSeq);\n\nvoid TestInnerProductDeviceDevice()\n{\n  TestInnerProductDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceDevice);\n#endif\n\nvoid TestInnerProductCudaStreams()\n{\n  thrust::device_vector<int> v1 = {1, -2, 3};\n  thrust::device_vector<int> v2 = {-4, 5, 6};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  int init   = 3;\n  int result = thrust::inner_product(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), init);\n  ASSERT_EQUAL(result, 7);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInnerProductCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/inner_product.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__ void inner_product_kernel(\n  ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, T init, Iterator3 result)\n{\n  *result = thrust::inner_product(exec, first1, last1, first2, init);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestInnerProductDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_v1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_v2 = unittest::random_integers<int>(n);\n\n  thrust::device_vector<int> d_v1 = h_v1;\n  thrust::device_vector<int> d_v2 = h_v2;\n\n  thrust::device_vector<int> result(1);\n\n  int init = 13;\n\n  int expected = thrust::inner_product(h_v1.begin(), h_v1.end(), h_v2.begin(), init);\n\n  inner_product_kernel<<<1, 1>>>(exec, d_v1.begin(), d_v1.end(), d_v2.begin(), init, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(expected, result[0]);\n}\n\nvoid TestInnerProductDeviceSeq()\n{\n  TestInnerProductDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceSeq);\n\nvoid TestInnerProductDeviceDevice()\n{\n  TestInnerProductDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestInnerProductDeviceDevice);\n#endif\n\nvoid TestInnerProductCudaStreams()\n{\n  thrust::device_vector<int> v1 = {1, -2, 3};\n  thrust::device_vector<int> v2 = {-4, 5, 6};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  int init   = 3;\n  int result = thrust::inner_product(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), init);\n  ASSERT_EQUAL(result, 7);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInnerProductCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/inner_product.cu"}
{"input": "#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/unique.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct div_n_equality_op\n{\n  T div;\n  __host__ __device__ bool operator()(const T x, const T& y) const\n  {\n    return (x / div) == (y / div);\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  __host__ __device__ T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\nstruct check_valid_item_op\n{\n  ::cuda::std::uint32_t* error_counter{};\n  int expected_upper_bound{};\n\n  __device__ bool operator()(const int lhs, const int rhs) const\n  {\n    if (lhs > expected_upper_bound || rhs > expected_upper_bound)\n    {\n      if (error_counter)\n      {\n        atomicAdd(error_counter, 1);\n      }\n      return false;\n    }\n    return lhs == rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__ void\nunique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  unique_kernel<<<1, 1>>>(exec, data.begin(), data.end(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  data.erase(new_last, data.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37}; // should we consider calculating ref from std::algorithm if exists?\n  ASSERT_EQUAL(data, ref);\n\n  unique_kernel<<<1, 1>>>(exec, data.begin(), new_last, div_n_equality_op<T>{10}, new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n}\n\nvoid TestUniqueDeviceSeq()\n{\n  TestUniqueDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueDeviceSeq);\n\nvoid TestUniqueDeviceDevice()\n{\n  TestUniqueDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueDeviceDevice);\n\nvoid TestUniqueDeviceNoSync()\n{\n  TestUniqueDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique(streampolicy, data.begin(), data.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  data.erase(new_last, data.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(data, ref);\n\n  new_last = thrust::unique(streampolicy, data.begin(), new_last, div_n_equality_op<T>{10});\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCudaStreamsSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsSync);\n\nvoid TestUniqueCudaStreamsNoSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nunique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void unique_copy_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, BinaryPredicate pred, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  unique_copy_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  output.erase(new_last, output.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(output, ref);\n\n  unique_copy_kernel<<<1, 1>>>(\n    exec, output.begin(), new_last, data.begin(), div_n_equality_op<T>{10}, new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n}\n\nvoid TestUniqueCopyDeviceSeq()\n{\n  TestUniqueCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceSeq);\n\nvoid TestUniqueCopyDeviceDevice()\n{\n  TestUniqueCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceDevice);\n\nvoid TestUniqueCopyDeviceNoSync()\n{\n  TestUniqueCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_copy(streampolicy, data.begin(), data.end(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  output.erase(new_last, output.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(output, ref);\n\n  new_last = thrust::unique_copy(streampolicy, output.begin(), new_last, data.begin(), div_n_equality_op<T>{10});\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCopyCudaStreamsSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsSync);\n\nvoid TestUniqueCopyCudaStreamsNoSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__ void\nunique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCountDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(1, -1);\n\n  unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 7);\n\n  unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), div_n_equality_op<T>{10}, output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 3);\n}\n\nvoid TestUniqueCountDeviceSeq()\n{\n  TestUniqueCountDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceSeq);\n\nvoid TestUniqueCountDeviceDevice()\n{\n  TestUniqueCountDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceDevice);\n\nvoid TestUniqueCountDeviceNoSync()\n{\n  TestUniqueCountDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCountCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  int result = thrust::unique_count(streampolicy, data.begin(), data.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 7);\n\n  result = thrust::unique_count(streampolicy, data.begin(), data.end(), div_n_equality_op<T>{10});\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 3);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestUniqueCountCudaStreamsSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsSync);\n\nvoid TestUniqueCountCudaStreamsNoSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsNoSync);\n\nvoid TestUniqueWithMagnitude(int magnitude)\n{\n  using offset_t      = std::int64_t;\n  using equality_op_t = div_n_equality_op<offset_t>;\n\n  offset_t run_length_of_equal_items = offset_t{10};\n  equality_op_t equality_op          = equality_op_t{run_length_of_equal_items};\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  ASSERT_EQUAL(static_cast<offset_t>(cuda::std::distance(begin, end)), num_items);\n\n  offset_t expected_num_unique = ::cuda::ceil_div(num_items, offset_t{10});\n  thrust::device_vector<offset_t> unique_out(expected_num_unique);\n  auto unique_out_end = thrust::unique_copy(begin, end, unique_out.begin(), equality_op);\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(cuda::std::distance(unique_out.begin(), unique_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_unique);\n  unique_out.resize(expected_num_unique);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{run_length_of_equal_items});\n  bool all_results_correct = thrust::equal(unique_out.begin(), unique_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestUniqueWithLargeNumberOfItems()\ntry\n{\n  for (int mag : {30, 31, 32, 33})\n  {\n    TestUniqueWithMagnitude(mag);\n  }\n}\ncatch (std::bad_alloc&)\n{\n  // if we run out of memory, just skip the test\n  return;\n}\nDECLARE_UNITTEST(TestUniqueWithLargeNumberOfItems);\n\nvoid TestUniqueWithCustomEqualityOp()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  auto constexpr num_items = 1000;\n  auto data                = thrust::make_counting_iterator(T{0});\n\n  thrust::device_vector<::cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  Vector unique_out(num_items);\n  auto unique_out_end = thrust::unique_copy(\n    data, data + num_items, unique_out.begin(), check_valid_item_op{error_counter_ptr, num_items - 1});\n\n  auto num_selected_out = cuda::std::distance(unique_out.begin(), unique_out_end);\n  ASSERT_EQUAL(num_selected_out, num_items);\n  ASSERT_EQUAL(error_counter[0], ::cuda::std::uint32_t{0});\n  bool all_results_correct = thrust::equal(unique_out.cbegin(), unique_out.cend(), data);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nDECLARE_UNITTEST(TestUniqueWithCustomEqualityOp);\n\ntemplate <typename F>\nstruct NonConstAdapter\n{\n  F f;\n  NonConstAdapter(const F& func)\n      : f(func)\n  {}\n\n  template <typename... Args>\n  __device__ auto operator()(Args&&... args) -> decltype(f(cuda::std::forward<Args>(args)...))\n  {\n    return f(cuda::std::forward<Args>(args)...);\n  }\n};\n\nvoid TestUniqueWithCustomEqualityOpMutable()\n{\n  using Vector = thrust::device_vector<int>;\n\n  thrust::device_vector<int> in = {1, 1, 2, 3, 4, 4, 5};\n  thrust::unique(thrust::cuda::par, in.begin(), in.end(), NonConstAdapter(cuda::std::equal_to<>{}));\n}\n\nDECLARE_UNITTEST(TestUniqueWithCustomEqualityOpMutable);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/detail/raw_pointer_cast.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/unique.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct div_n_equality_op\n{\n  T div;\n  __host__ __device__ bool operator()(const T x, const T& y) const\n  {\n    return (x / div) == (y / div);\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  __host__ __device__ T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\nstruct check_valid_item_op\n{\n  ::cuda::std::uint32_t* error_counter{};\n  int expected_upper_bound{};\n\n  __device__ bool operator()(const int lhs, const int rhs) const\n  {\n    if (lhs > expected_upper_bound || rhs > expected_upper_bound)\n    {\n      if (error_counter)\n      {\n        atomicAdd(error_counter, 1);\n      }\n      return false;\n    }\n    return lhs == rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void unique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__ void\nunique_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  unique_kernel<<<1, 1>>>(exec, data.begin(), data.end(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  data.erase(new_last, data.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37}; // should we consider calculating ref from std::algorithm if exists?\n  ASSERT_EQUAL(data, ref);\n\n  unique_kernel<<<1, 1>>>(exec, data.begin(), new_last, div_n_equality_op<T>{10}, new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n}\n\nvoid TestUniqueDeviceSeq()\n{\n  TestUniqueDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueDeviceSeq);\n\nvoid TestUniqueDeviceDevice()\n{\n  TestUniqueDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueDeviceDevice);\n\nvoid TestUniqueDeviceNoSync()\n{\n  TestUniqueDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique(streampolicy, data.begin(), data.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 7);\n  data.erase(new_last, data.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(data, ref);\n\n  new_last = thrust::unique(streampolicy, data.begin(), new_last, div_n_equality_op<T>{10});\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCudaStreamsSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsSync);\n\nvoid TestUniqueCudaStreamsNoSync()\n{\n  TestUniqueCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nunique_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void unique_copy_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, BinaryPredicate pred, Iterator3 result2)\n{\n  *result2 = thrust::unique_copy(exec, first, last, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  unique_copy_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  output.erase(new_last, output.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(output, ref);\n\n  unique_copy_kernel<<<1, 1>>>(\n    exec, output.begin(), new_last, data.begin(), div_n_equality_op<T>{10}, new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n}\n\nvoid TestUniqueCopyDeviceSeq()\n{\n  TestUniqueCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceSeq);\n\nvoid TestUniqueCopyDeviceDevice()\n{\n  TestUniqueCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceDevice);\n\nvoid TestUniqueCopyDeviceNoSync()\n{\n  TestUniqueCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCopyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(10, -1);\n\n  thrust::device_vector<Vector::iterator> new_last_vec(1);\n  Vector::iterator new_last;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::unique_copy(streampolicy, data.begin(), data.end(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - output.begin(), 7);\n  output.erase(new_last, output.end());\n  Vector ref{11, 12, 20, 29, 21, 31, 37};\n  ASSERT_EQUAL(output, ref);\n\n  new_last = thrust::unique_copy(streampolicy, output.begin(), new_last, data.begin(), div_n_equality_op<T>{10});\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(new_last - data.begin(), 3);\n  data.erase(new_last, data.end());\n  ref = {11, 20, 31};\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCopyCudaStreamsSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsSync);\n\nvoid TestUniqueCopyCudaStreamsNoSync()\n{\n  TestUniqueCopyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCopyCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void unique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename BinaryPredicate, typename Iterator2>\n__global__ void\nunique_count_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::unique_count(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCountDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  Vector output(1, -1);\n\n  unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 7);\n\n  unique_count_kernel<<<1, 1>>>(exec, data.begin(), data.end(), div_n_equality_op<T>{10}, output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(output[0], 3);\n}\n\nvoid TestUniqueCountDeviceSeq()\n{\n  TestUniqueCountDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceSeq);\n\nvoid TestUniqueCountDeviceDevice()\n{\n  TestUniqueCountDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceDevice);\n\nvoid TestUniqueCountDeviceNoSync()\n{\n  TestUniqueCountDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestUniqueCountCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{11, 11, 12, 20, 29, 21, 21, 31, 31, 37};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  int result = thrust::unique_count(streampolicy, data.begin(), data.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 7);\n\n  result = thrust::unique_count(streampolicy, data.begin(), data.end(), div_n_equality_op<T>{10});\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result, 3);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestUniqueCountCudaStreamsSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsSync);\n\nvoid TestUniqueCountCudaStreamsNoSync()\n{\n  TestUniqueCountCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestUniqueCountCudaStreamsNoSync);\n\nvoid TestUniqueWithMagnitude(int magnitude)\n{\n  using offset_t      = std::int64_t;\n  using equality_op_t = div_n_equality_op<offset_t>;\n\n  offset_t run_length_of_equal_items = offset_t{10};\n  equality_op_t equality_op          = equality_op_t{run_length_of_equal_items};\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  ASSERT_EQUAL(static_cast<offset_t>(cuda::std::distance(begin, end)), num_items);\n\n  offset_t expected_num_unique = ::cuda::ceil_div(num_items, offset_t{10});\n  thrust::device_vector<offset_t> unique_out(expected_num_unique);\n  auto unique_out_end = thrust::unique_copy(begin, end, unique_out.begin(), equality_op);\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(cuda::std::distance(unique_out.begin(), unique_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_unique);\n  unique_out.resize(expected_num_unique);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{run_length_of_equal_items});\n  bool all_results_correct = thrust::equal(unique_out.begin(), unique_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestUniqueWithLargeNumberOfItems()\ntry\n{\n  for (int mag : {30, 31, 32, 33})\n  {\n    TestUniqueWithMagnitude(mag);\n  }\n}\ncatch (std::bad_alloc&)\n{\n  // if we run out of memory, just skip the test\n  return;\n}\nDECLARE_UNITTEST(TestUniqueWithLargeNumberOfItems);\n\nvoid TestUniqueWithCustomEqualityOp()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  auto constexpr num_items = 1000;\n  auto data                = thrust::make_counting_iterator(T{0});\n\n  thrust::device_vector<::cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  Vector unique_out(num_items);\n  auto unique_out_end = thrust::unique_copy(\n    data, data + num_items, unique_out.begin(), check_valid_item_op{error_counter_ptr, num_items - 1});\n\n  auto num_selected_out = cuda::std::distance(unique_out.begin(), unique_out_end);\n  ASSERT_EQUAL(num_selected_out, num_items);\n  ASSERT_EQUAL(error_counter[0], ::cuda::std::uint32_t{0});\n  bool all_results_correct = thrust::equal(unique_out.cbegin(), unique_out.cend(), data);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nDECLARE_UNITTEST(TestUniqueWithCustomEqualityOp);\n\ntemplate <typename F>\nstruct NonConstAdapter\n{\n  F f;\n  NonConstAdapter(const F& func)\n      : f(func)\n  {}\n\n  template <typename... Args>\n  __device__ auto operator()(Args&&... args) -> decltype(f(cuda::std::forward<Args>(args)...))\n  {\n    return f(cuda::std::forward<Args>(args)...);\n  }\n};\n\nvoid TestUniqueWithCustomEqualityOpMutable()\n{\n  using Vector = thrust::device_vector<int>;\n\n  thrust::device_vector<int> in = {1, 1, 2, 3, 4, 4, 5};\n  thrust::unique(thrust::cuda::par, in.begin(), in.end(), NonConstAdapter(cuda::std::equal_to<>{}));\n}\n\nDECLARE_UNITTEST(TestUniqueWithCustomEqualityOpMutable);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/unique.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_symmetric_difference_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_symmetric_difference(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4, 6}, b{0, 3, 3, 4, 7};\n\n  Vector ref{2, 3, 3, 6, 7};\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_symmetric_difference_kernel<<<1, 1>>>(\n    exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetSymmetricDifferenceDeviceSeq()\n{\n  TestSetSymmetricDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceSeq);\n\nvoid TestSetSymmetricDifferenceDeviceDevice()\n{\n  TestSetSymmetricDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceDevice);\n#endif\n\nvoid TestSetSymmetricDifferenceCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4, 6}, b{0, 3, 3, 4, 7};\n\n  Vector ref{2, 3, 3, 6, 7};\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end =\n    thrust::set_symmetric_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_symmetric_difference_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_symmetric_difference(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4, 6}, b{0, 3, 3, 4, 7};\n\n  Vector ref{2, 3, 3, 6, 7};\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_symmetric_difference_kernel<<<1, 1>>>(\n    exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetSymmetricDifferenceDeviceSeq()\n{\n  TestSetSymmetricDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceSeq);\n\nvoid TestSetSymmetricDifferenceDeviceDevice()\n{\n  TestSetSymmetricDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceDeviceDevice);\n#endif\n\nvoid TestSetSymmetricDifferenceCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4, 6}, b{0, 3, 3, 4, 7};\n\n  Vector ref{2, 3, 3, 6, 7};\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end =\n    thrust::set_symmetric_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_symmetric_difference.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\ntemplate <typename ExecutionPolicy, typename InputIter, typename OutputIter, typename T>\n__global__ void reduce_into_kernel(ExecutionPolicy exec, InputIter first, InputIter last, OutputIter result, T init)\n{\n  thrust::reduce_into(exec, first, last, result, init);\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReduceIntoDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::host_vector<T> h_result(1);\n  thrust::device_vector<T> d_result(1);\n\n  T init = 13;\n\n  thrust::reduce_into(h_data.begin(), h_data.end(), h_result.begin(), init);\n\n  reduce_into_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), init);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceSeq, IntegralTypes> TestReduceIntoDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceDevice, IntegralTypes> TestReduceIntoDeviceDeviceInstance;\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceNoSync, IntegralTypes> TestReduceIntoDeviceNoSyncInstance;\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceIntoCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v = {1, -2, 3};\n\n  Vector o(1);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  thrust::reduce_into(streampolicy, v.begin(), v.end(), o.begin());\n\n  cudaStreamSynchronize(s);\n  ASSERT_EQUAL(o[0], 2);\n\n  // with initializer\n  thrust::reduce_into(streampolicy, v.begin(), v.end(), o.begin(), 10);\n\n  cudaStreamSynchronize(s);\n  ASSERT_EQUAL(o[0], 12);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestReduceIntoCudaStreamsSync()\n{\n  TestReduceIntoCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceIntoCudaStreamsSync);\n\nvoid TestReduceIntoCudaStreamsNoSync()\n{\n  TestReduceIntoCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceIntoCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceIntoLargeInput()\n{\n  using T                 = unsigned long long;\n  using OffsetT           = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  cuda::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_into_kernel<<<1, 1>>>(thrust::device, d_data, d_data + num_items, d_result.begin(), T{});\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceIntoLargeInput);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\ntemplate <typename ExecutionPolicy, typename InputIter, typename OutputIter, typename T>\n__global__ void reduce_into_kernel(ExecutionPolicy exec, InputIter first, InputIter last, OutputIter result, T init)\n{\n  thrust::reduce_into(exec, first, last, result, init);\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReduceIntoDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::host_vector<T> h_result(1);\n  thrust::device_vector<T> d_result(1);\n\n  T init = 13;\n\n  thrust::reduce_into(h_data.begin(), h_data.end(), h_result.begin(), init);\n\n  reduce_into_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), init);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceSeq, IntegralTypes> TestReduceIntoDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceDevice, IntegralTypes> TestReduceIntoDeviceDeviceInstance;\n\ntemplate <typename T>\nstruct TestReduceIntoDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceIntoDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceIntoDeviceNoSync, IntegralTypes> TestReduceIntoDeviceNoSyncInstance;\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceIntoCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v = {1, -2, 3};\n\n  Vector o(1);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  thrust::reduce_into(streampolicy, v.begin(), v.end(), o.begin());\n\n  hipStreamSynchronize(s);\n  ASSERT_EQUAL(o[0], 2);\n\n  // with initializer\n  thrust::reduce_into(streampolicy, v.begin(), v.end(), o.begin(), 10);\n\n  hipStreamSynchronize(s);\n  ASSERT_EQUAL(o[0], 12);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestReduceIntoCudaStreamsSync()\n{\n  TestReduceIntoCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceIntoCudaStreamsSync);\n\nvoid TestReduceIntoCudaStreamsNoSync()\n{\n  TestReduceIntoCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceIntoCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceIntoLargeInput()\n{\n  using T                 = unsigned long long;\n  using OffsetT           = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  cuda::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_into_kernel<<<1, 1>>>(thrust::device, d_data, d_data + num_items, d_result.begin(), T{});\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceIntoLargeInput);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/reduce_into.cu"}
{"input": "#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::copy(exec, first, last, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCopyDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T> h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n\n  thrust::copy(h_src.begin(), h_src.end(), h_dst.begin());\n  copy_kernel<<<1, 1>>>(exec, d_src.begin(), d_src.end(), d_dst.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\ntemplate <typename T>\nvoid TestCopyDeviceSeq(size_t n)\n{\n  TestCopyDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceSeq);\n\ntemplate <typename T>\nvoid TestCopyDeviceDevice(size_t n)\n{\n  TestCopyDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__ void copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::copy_n(exec, first, n, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCopyNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T> h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n\n  thrust::copy_n(h_src.begin(), h_src.size(), h_dst.begin());\n  copy_n_kernel<<<1, 1>>>(exec, d_src.begin(), d_src.size(), d_dst.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\ntemplate <typename T>\nvoid TestCopyNDeviceSeq(size_t n)\n{\n  TestCopyNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceSeq);\n\ntemplate <typename T>\nvoid TestCopyNDeviceDevice(size_t n)\n{\n  TestCopyNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceDevice);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::copy(exec, first, last, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCopyDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T> h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n\n  thrust::copy(h_src.begin(), h_src.end(), h_dst.begin());\n  copy_kernel<<<1, 1>>>(exec, d_src.begin(), d_src.end(), d_dst.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\ntemplate <typename T>\nvoid TestCopyDeviceSeq(size_t n)\n{\n  TestCopyDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceSeq);\n\ntemplate <typename T>\nvoid TestCopyDeviceDevice(size_t n)\n{\n  TestCopyDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__ void copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::copy_n(exec, first, n, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCopyNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_src = unittest::random_integers<T>(n);\n  thrust::host_vector<T> h_dst(n);\n\n  thrust::device_vector<T> d_src = h_src;\n  thrust::device_vector<T> d_dst(n);\n\n  thrust::copy_n(h_src.begin(), h_src.size(), h_dst.begin());\n  copy_n_kernel<<<1, 1>>>(exec, d_src.begin(), d_src.size(), d_dst.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_dst, d_dst);\n}\n\ntemplate <typename T>\nvoid TestCopyNDeviceSeq(size_t n)\n{\n  TestCopyNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceSeq);\n\ntemplate <typename T>\nvoid TestCopyNDeviceDevice(size_t n)\n{\n  TestCopyNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCopyNDeviceDevice);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/copy.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/scatter.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nscatter_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 result)\n{\n  thrust::scatter(exec, first, last, map_first, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestScatterDevice(ExecutionPolicy exec)\n{\n  size_t n                 = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % output_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  thrust::host_vector<int> h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n\n  thrust::scatter(h_input.begin(), h_input.end(), h_map.begin(), h_output.begin());\n\n  scatter_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_output.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterDeviceSeq()\n{\n  TestScatterDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterDeviceSeq);\n\nvoid TestScatterDeviceDevice()\n{\n  TestScatterDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Function>\n__global__ void scatter_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 map_first,\n  Iterator3 stencil_first,\n  Iterator4 result,\n  Function f)\n{\n  thrust::scatter_if(exec, first, last, map_first, stencil_first, result, f);\n}\n\ntemplate <typename T>\nstruct is_even_scatter_if\n{\n  _CCCL_HOST_DEVICE bool operator()(const T i) const\n  {\n    return (i % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestScatterIfDevice(ExecutionPolicy exec)\n{\n  size_t n                 = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % output_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  thrust::host_vector<int> h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n\n  thrust::scatter_if(\n    h_input.begin(), h_input.end(), h_map.begin(), h_map.begin(), h_output.begin(), is_even_scatter_if<unsigned int>());\n\n  scatter_if_kernel<<<1, 1>>>(\n    exec,\n    d_input.begin(),\n    d_input.end(),\n    d_map.begin(),\n    d_map.begin(),\n    d_output.begin(),\n    is_even_scatter_if<unsigned int>());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterIfDeviceSeq()\n{\n  TestScatterIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceSeq);\n\nvoid TestScatterIfDeviceDevice()\n{\n  TestScatterIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceDevice);\n#endif\n\nvoid TestScatterCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector map{6, 3, 1, 7, 2}; // scatter indices\n  Vector src{0, 1, 2, 3, 4}; // source vector\n  Vector dst(8, 0); // destination vector\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::scatter(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), dst.begin());\n\n  cudaStreamSynchronize(s);\n\n  Vector ref{0, 2, 4, 1, 0, 0, 0, 3};\n  ASSERT_EQUAL(dst, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterCudaStreams);\n\nvoid TestScatterIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector flg{0, 1, 0, 1, 0}; // predicate array\n  Vector map{6, 3, 1, 7, 2}; // scatter indices\n  Vector src{0, 1, 2, 3, 4}; // source vector\n  Vector dst(8); // destination vector\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::scatter_if(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), flg.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n\n  Vector ref{0, 0, 0, 1, 0, 0, 0, 3};\n  ASSERT_EQUAL(dst, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterIfCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/scatter.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\nscatter_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 map_first, Iterator3 result)\n{\n  thrust::scatter(exec, first, last, map_first, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestScatterDevice(ExecutionPolicy exec)\n{\n  size_t n                 = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % output_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  thrust::host_vector<int> h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n\n  thrust::scatter(h_input.begin(), h_input.end(), h_map.begin(), h_output.begin());\n\n  scatter_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_map.begin(), d_output.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterDeviceSeq()\n{\n  TestScatterDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterDeviceSeq);\n\nvoid TestScatterDeviceDevice()\n{\n  TestScatterDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Function>\n__global__ void scatter_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 map_first,\n  Iterator3 stencil_first,\n  Iterator4 result,\n  Function f)\n{\n  thrust::scatter_if(exec, first, last, map_first, stencil_first, result, f);\n}\n\ntemplate <typename T>\nstruct is_even_scatter_if\n{\n  _CCCL_HOST_DEVICE bool operator()(const T i) const\n  {\n    return (i % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestScatterIfDevice(ExecutionPolicy exec)\n{\n  size_t n                 = 1000;\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<int> h_input(n, 1);\n  thrust::device_vector<int> d_input(n, 1);\n\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % output_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  thrust::host_vector<int> h_output(output_size, 0);\n  thrust::device_vector<int> d_output(output_size, 0);\n\n  thrust::scatter_if(\n    h_input.begin(), h_input.end(), h_map.begin(), h_map.begin(), h_output.begin(), is_even_scatter_if<unsigned int>());\n\n  scatter_if_kernel<<<1, 1>>>(\n    exec,\n    d_input.begin(),\n    d_input.end(),\n    d_map.begin(),\n    d_map.begin(),\n    d_output.begin(),\n    is_even_scatter_if<unsigned int>());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\nvoid TestScatterIfDeviceSeq()\n{\n  TestScatterIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceSeq);\n\nvoid TestScatterIfDeviceDevice()\n{\n  TestScatterIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScatterIfDeviceDevice);\n#endif\n\nvoid TestScatterCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector map{6, 3, 1, 7, 2}; // scatter indices\n  Vector src{0, 1, 2, 3, 4}; // source vector\n  Vector dst(8, 0); // destination vector\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::scatter(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), dst.begin());\n\n  hipStreamSynchronize(s);\n\n  Vector ref{0, 2, 4, 1, 0, 0, 0, 3};\n  ASSERT_EQUAL(dst, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterCudaStreams);\n\nvoid TestScatterIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector flg{0, 1, 0, 1, 0}; // predicate array\n  Vector map{6, 3, 1, 7, 2}; // scatter indices\n  Vector src{0, 1, 2, 3, 4}; // source vector\n  Vector dst(8); // destination vector\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::scatter_if(thrust::cuda::par.on(s), src.begin(), src.end(), map.begin(), flg.begin(), dst.begin());\n  hipStreamSynchronize(s);\n\n  Vector ref{0, 0, 0, 1, 0, 0, 0, 3};\n  ASSERT_EQUAL(dst, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScatterIfCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/scatter.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_difference_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_difference_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4, 5}, b_key{0, 3, 3, 4, 6};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 5}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_difference_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter_pair end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetDifferenceByKeyDeviceSeq()\n{\n  TestSetDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceSeq);\n\nvoid TestSetDifferenceByKeyDeviceDevice()\n{\n  TestSetDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceDevice);\n#endif\n\nvoid TestSetDifferenceByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4, 5}, b_key{0, 3, 3, 4, 6};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 5}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_difference_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_difference_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_difference_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4, 5}, b_key{0, 3, 3, 4, 6};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 5}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_difference_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter_pair end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetDifferenceByKeyDeviceSeq()\n{\n  TestSetDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceSeq);\n\nvoid TestSetDifferenceByKeyDeviceDevice()\n{\n  TestSetDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyDeviceDevice);\n#endif\n\nvoid TestSetDifferenceByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4, 5}, b_key{0, 3, 3, 4, 6};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 5}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_difference_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_difference_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/transform_scan.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_inclusive_scan_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function1 f1,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, f2);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename T,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_inclusive_scan_init_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function1 f1,\n  T init,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename T,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_exclusive_scan_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result,\n  Function1 f1,\n  T init,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_exclusive_scan(exec, first, last, result, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformScanDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector ref{-1, -4, -2, -6, -1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  // inclusive scan\n  transform_inclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // inclusive scan with nonzero init\n  transform_inclusive_scan_init_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // exclusive scan with 0 init\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    0,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {0, -1, -4, -2, -6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // exclusive scan with nonzero init\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // inplace inclusive scan\n  input = input_copy;\n  transform_inclusive_scan_kernel<<<1, 1>>>(\n    exec, input.begin(), input.end(), input.begin(), ::cuda::std::negate<T>(), ::cuda::std::plus<T>(), iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {-1, -4, -2, -6, -1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n\n  // inplace inclusive scan with init\n  input = input_copy;\n  transform_inclusive_scan_init_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n}\n\nvoid TestTransformScanDeviceSeq()\n{\n  TestTransformScanDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceSeq);\n\nvoid TestTransformScanDeviceDevice()\n{\n  TestTransformScanDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceDevice);\n#endif\n\nvoid TestTransformScanCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector result{-1, -4, -2, -6, -1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with nonzero init\n  iter = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with 0 init\n  iter = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    0,\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {0, -1, -4, -2, -6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with nonzero init\n  iter = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter  = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {-1, -4, -2, -6, -1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace inclusive scan with init\n  input = input_copy;\n  iter  = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter  = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformScanCudaStreams);\n\nvoid TestTransformScanConstAccumulator()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector reference(5);\n  Vector output(5);\n\n  thrust::transform_inclusive_scan(\n    input.begin(), input.end(), output.begin(), ::cuda::std::identity{}, ::cuda::std::plus<T>());\n  thrust::inclusive_scan(input.begin(), input.end(), reference.begin(), ::cuda::std::plus<T>());\n\n  ASSERT_EQUAL(output, reference);\n}\nDECLARE_UNITTEST(TestTransformScanConstAccumulator);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/transform_scan.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_inclusive_scan_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function1 f1,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, f2);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename T,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_inclusive_scan_init_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function1 f1,\n  T init,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_inclusive_scan(exec, first, last, result1, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function1,\n          typename T,\n          typename Function2,\n          typename Iterator3>\n__global__ void transform_exclusive_scan_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result,\n  Function1 f1,\n  T init,\n  Function2 f2,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_exclusive_scan(exec, first, last, result, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformScanDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector ref{-1, -4, -2, -6, -1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  // inclusive scan\n  transform_inclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // inclusive scan with nonzero init\n  transform_inclusive_scan_init_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // exclusive scan with 0 init\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    0,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {0, -1, -4, -2, -6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // exclusive scan with nonzero init\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(ref, output);\n\n  // inplace inclusive scan\n  input = input_copy;\n  transform_inclusive_scan_kernel<<<1, 1>>>(\n    exec, input.begin(), input.end(), input.begin(), ::cuda::std::negate<T>(), ::cuda::std::plus<T>(), iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {-1, -4, -2, -6, -1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n\n  // inplace inclusive scan with init\n  input = input_copy;\n  transform_inclusive_scan_init_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  transform_exclusive_scan_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>(),\n    iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n  ref  = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(ref, input);\n}\n\nvoid TestTransformScanDeviceSeq()\n{\n  TestTransformScanDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceSeq);\n\nvoid TestTransformScanDeviceDevice()\n{\n  TestTransformScanDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformScanDeviceDevice);\n#endif\n\nvoid TestTransformScanCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector result{-1, -4, -2, -6, -1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // inclusive scan\n  iter = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with nonzero init\n  iter = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with 0 init\n  iter = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    0,\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {0, -1, -4, -2, -6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with nonzero init\n  iter = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter  = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {-1, -4, -2, -6, -1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace inclusive scan with init\n  input = input_copy;\n  iter  = thrust::transform_inclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {2, -1, 1, -3, 2};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter  = thrust::transform_exclusive_scan(\n    thrust::cuda::par.on(s),\n    input.begin(),\n    input.end(),\n    input.begin(),\n    ::cuda::std::negate<T>(),\n    3,\n    ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {3, 2, -1, 1, -3};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformScanCudaStreams);\n\nvoid TestTransformScanConstAccumulator()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector reference(5);\n  Vector output(5);\n\n  thrust::transform_inclusive_scan(\n    input.begin(), input.end(), output.begin(), ::cuda::std::identity{}, ::cuda::std::plus<T>());\n  thrust::inclusive_scan(input.begin(), input.end(), reference.begin(), ::cuda::std::plus<T>());\n\n  ASSERT_EQUAL(output, reference);\n}\nDECLARE_UNITTEST(TestTransformScanConstAccumulator);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/transform_scan.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/partition.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void\npartition_point_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition_point(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionPointDevice(ExecutionPolicy exec)\n{\n  size_t n                     = 1000;\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n  using iterator               = typename thrust::device_vector<int>::iterator;\n\n  iterator ref = thrust::stable_partition(v.begin(), v.end(), is_even<int>());\n\n  thrust::device_vector<iterator> result(1);\n  partition_point_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(ref - v.begin(), (iterator) result[0] - v.begin());\n}\n\nvoid TestPartitionPointDeviceSeq()\n{\n  TestPartitionPointDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceSeq);\n\nvoid TestPartitionPointDeviceDevice()\n{\n  TestPartitionPointDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceDevice);\n#endif\n\nvoid TestPartitionPointCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector v(4);\n  v[0] = 1;\n  v[1] = 1;\n  v[2] = 1;\n  v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 4;\n  Iterator ref  = first + 3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, ::cuda::std::identity{}));\n\n  last = v.begin() + 3;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, ::cuda::std::identity{}));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestPartitionPointCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/partition.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void\npartition_point_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition_point(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionPointDevice(ExecutionPolicy exec)\n{\n  size_t n                     = 1000;\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n  using iterator               = typename thrust::device_vector<int>::iterator;\n\n  iterator ref = thrust::stable_partition(v.begin(), v.end(), is_even<int>());\n\n  thrust::device_vector<iterator> result(1);\n  partition_point_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(ref - v.begin(), (iterator) result[0] - v.begin());\n}\n\nvoid TestPartitionPointDeviceSeq()\n{\n  TestPartitionPointDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceSeq);\n\nvoid TestPartitionPointDeviceDevice()\n{\n  TestPartitionPointDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionPointDeviceDevice);\n#endif\n\nvoid TestPartitionPointCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector v(4);\n  v[0] = 1;\n  v[1] = 1;\n  v[2] = 1;\n  v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 4;\n  Iterator ref  = first + 3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, ::cuda::std::identity{}));\n\n  last = v.begin() + 3;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::partition_point(thrust::cuda::par.on(s), first, last, ::cuda::std::identity{}));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestPartitionPointCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/partition_point.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n#include <thrust/functional.h>\n#include <thrust/merge.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void merge_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::merge(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMergeDevice(ExecutionPolicy exec)\n{\n  const size_t n         = 10000;\n  const size_t sizes[]   = {0, 1, n / 2, n, n + 1, 2 * n};\n  const size_t num_sizes = sizeof(sizes) / sizeof(size_t);\n\n  const thrust::host_vector<int> random =\n    unittest::random_integers<unittest::int8_t>(n + *thrust::max_element(sizes, sizes + num_sizes));\n\n  thrust::host_vector<int> h_a(random.begin(), random.begin() + n);\n  thrust::host_vector<int> h_b(random.begin() + n, random.end());\n\n  thrust::stable_sort(h_a.begin(), h_a.end());\n  thrust::stable_sort(h_b.begin(), h_b.end());\n\n  const thrust::device_vector<int> d_a = h_a;\n  const thrust::device_vector<int> d_b = h_b;\n\n  for (size_t i = 0; i < num_sizes; i++)\n  {\n    const size_t size = sizes[i];\n\n    thrust::host_vector<int> h_result(n + size);\n    thrust::device_vector<int> d_result(n + size);\n\n    using iter_type = typename thrust::device_vector<int>::iterator;\n    thrust::device_vector<iter_type> d_end(1);\n\n    const auto h_end = thrust::merge(h_a.begin(), h_a.end(), h_b.begin(), h_b.begin() + size, h_result.begin());\n    h_result.resize(h_end - h_result.begin());\n\n    merge_kernel<<<1, 1>>>(\n      exec, d_a.begin(), d_a.end(), d_b.begin(), d_b.begin() + size, d_result.begin(), d_end.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_result.resize(static_cast<iter_type>(d_end[0]) - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestMergeDeviceSeq()\n{\n  TestMergeDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeDeviceSeq);\n\nvoid TestMergeDeviceDevice()\n{\n  TestMergeDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeDeviceDevice);\n#endif\n\nvoid TestMergeCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  const Vector a{0, 2, 4}, b{0, 3, 3, 4};\n  const Vector ref{0, 0, 2, 3, 3, 4, 4};\n\n  Vector result(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  const auto end = thrust::merge(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n#include <thrust/functional.h>\n#include <thrust/merge.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void merge_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::merge(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMergeDevice(ExecutionPolicy exec)\n{\n  const size_t n         = 10000;\n  const size_t sizes[]   = {0, 1, n / 2, n, n + 1, 2 * n};\n  const size_t num_sizes = sizeof(sizes) / sizeof(size_t);\n\n  const thrust::host_vector<int> random =\n    unittest::random_integers<unittest::int8_t>(n + *thrust::max_element(sizes, sizes + num_sizes));\n\n  thrust::host_vector<int> h_a(random.begin(), random.begin() + n);\n  thrust::host_vector<int> h_b(random.begin() + n, random.end());\n\n  thrust::stable_sort(h_a.begin(), h_a.end());\n  thrust::stable_sort(h_b.begin(), h_b.end());\n\n  const thrust::device_vector<int> d_a = h_a;\n  const thrust::device_vector<int> d_b = h_b;\n\n  for (size_t i = 0; i < num_sizes; i++)\n  {\n    const size_t size = sizes[i];\n\n    thrust::host_vector<int> h_result(n + size);\n    thrust::device_vector<int> d_result(n + size);\n\n    using iter_type = typename thrust::device_vector<int>::iterator;\n    thrust::device_vector<iter_type> d_end(1);\n\n    const auto h_end = thrust::merge(h_a.begin(), h_a.end(), h_b.begin(), h_b.begin() + size, h_result.begin());\n    h_result.resize(h_end - h_result.begin());\n\n    merge_kernel<<<1, 1>>>(\n      exec, d_a.begin(), d_a.end(), d_b.begin(), d_b.begin() + size, d_result.begin(), d_end.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_result.resize(static_cast<iter_type>(d_end[0]) - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestMergeDeviceSeq()\n{\n  TestMergeDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeDeviceSeq);\n\nvoid TestMergeDeviceDevice()\n{\n  TestMergeDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeDeviceDevice);\n#endif\n\nvoid TestMergeCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  const Vector a{0, 2, 4}, b{0, 3, 3, 4};\n  const Vector ref{0, 0, 2, 3, 3, 4, 4};\n\n  Vector result(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  const auto end = thrust::merge(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/merge.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/swap.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void swap_ranges_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  thrust::swap_ranges(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSwapRangesDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n  Vector v2{5, 6, 7, 8, 9};\n  Vector v1_ref(v2);\n  Vector v2_ref(v1);\n\n  swap_ranges_kernel<<<1, 1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(v1, v1_ref);\n  ASSERT_EQUAL(v2, v2_ref);\n}\n\nvoid TestSwapRangesDeviceSeq()\n{\n  TestSwapRangesDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceSeq);\n\nvoid TestSwapRangesDeviceDevice()\n{\n  TestSwapRangesDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceDevice);\n#endif\n\nvoid TestSwapRangesCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n  Vector v2{5, 6, 7, 8, 9};\n  Vector v1_ref(v2);\n  Vector v2_ref(v1);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::swap_ranges(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v1, v1_ref);\n  ASSERT_EQUAL(v2, v2_ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSwapRangesCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/swap.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void swap_ranges_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2)\n{\n  thrust::swap_ranges(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSwapRangesDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n  Vector v2{5, 6, 7, 8, 9};\n  Vector v1_ref(v2);\n  Vector v2_ref(v1);\n\n  swap_ranges_kernel<<<1, 1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(v1, v1_ref);\n  ASSERT_EQUAL(v2, v2_ref);\n}\n\nvoid TestSwapRangesDeviceSeq()\n{\n  TestSwapRangesDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceSeq);\n\nvoid TestSwapRangesDeviceDevice()\n{\n  TestSwapRangesDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSwapRangesDeviceDevice);\n#endif\n\nvoid TestSwapRangesCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n  Vector v2{5, 6, 7, 8, 9};\n  Vector v1_ref(v2);\n  Vector v2_ref(v1);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::swap_ranges(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v1, v1_ref);\n  ASSERT_EQUAL(v2, v2_ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSwapRangesCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/swap_ranges.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/transform.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Iterator3>\n__global__ void transform_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Iterator3 result2)\n{\n  *result2 = thrust::transform(exec, first, last, result1, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformUnaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector output(3);\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_kernel<<<1, 1>>>(\n    exec, input.begin(), input.end(), output.begin(), ::cuda::std::negate<T>(), iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformUnaryDeviceSeq()\n{\n  TestTransformUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceSeq);\n\nvoid TestTransformUnaryDeviceDevice()\n{\n  TestTransformUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function,\n          typename Predicate,\n          typename Iterator3>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function f,\n  Predicate pred,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfUnaryNoStencilDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{0, -2, 0};\n  Vector output{-1, -2, -3};\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::identity{},\n    iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryNoStencilDeviceSeq()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceSeq);\n\nvoid TestTransformIfUnaryNoStencilDeviceDevice()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Function,\n          typename Predicate,\n          typename Iterator4>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result1,\n  Function f,\n  Predicate pred,\n  Iterator4 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, stencil_first, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfUnaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector stencil{1, 0, 1};\n  Vector output{1, 2, 3};\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    stencil.begin(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::identity{},\n    iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryDeviceSeq()\n{\n  TestTransformIfUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceSeq);\n\nvoid TestTransformIfUnaryDeviceDevice()\n{\n  TestTransformIfUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Function,\n          typename Iterator4>\n__global__ void transform_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator3 result1,\n  Function f,\n  Iterator4 result2)\n{\n  *result2 = thrust::transform(exec, first1, last1, first2, result1, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformBinaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector output(3);\n  Vector result{5, -7, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_kernel<<<1, 1>>>(\n    exec, input1.begin(), input1.end(), input2.begin(), output.begin(), ::cuda::std::minus<T>(), iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformBinaryDeviceSeq()\n{\n  TestTransformBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceSeq);\n\nvoid TestTransformBinaryDeviceDevice()\n{\n  TestTransformBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Function,\n          typename Predicate,\n          typename Iterator5>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator3 stencil_first,\n  Iterator4 result1,\n  Function f,\n  Predicate pred,\n  Iterator5 result2)\n{\n  *result2 = thrust::transform_if(exec, first1, last1, first2, stencil_first, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfBinaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector stencil{0, 1, 0};\n  Vector output{1, 2, 3};\n  Vector result{5, 2, -3};\n\n  ::cuda::std::identity identity;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input1.begin(),\n    input1.end(),\n    input2.begin(),\n    stencil.begin(),\n    output.begin(),\n    ::cuda::std::minus<T>(),\n    ::cuda::std::not_fn(identity),\n    iter_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfBinaryDeviceSeq()\n{\n  TestTransformIfBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceSeq);\n\nvoid TestTransformIfBinaryDeviceDevice()\n{\n  TestTransformIfBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceDevice);\n#endif\n\nvoid TestTransformUnaryCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector output(3);\n  Vector result{-1, 2, -3};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  iter =\n    thrust::transform(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), ::cuda::std::negate<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformUnaryCudaStreams);\n\nvoid TestTransformBinaryCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector output(3);\n  Vector result{5, -7, -3};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  iter = thrust::transform(\n    thrust::cuda::par.on(s), input1.begin(), input1.end(), input2.begin(), output.begin(), ::cuda::std::minus<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformBinaryCudaStreams);\n\nstruct sum_five\n{\n  _CCCL_HOST_DEVICE auto operator()(std::int8_t a, std::int16_t b, std::int32_t c, std::int64_t d, float e) const\n    -> double\n  {\n    return a + b + c + d + e;\n  }\n};\n\n// we specialize zip_function for sum_five, but do nothing in the call operator so the test below would fail if the\n// zip_function is actually called (and not unwrapped)\nTHRUST_NAMESPACE_BEGIN\ntemplate <>\nclass zip_function<sum_five>\n{\npublic:\n  _CCCL_HOST_DEVICE zip_function(sum_five func)\n      : func(func)\n  {}\n\n  _CCCL_HOST_DEVICE sum_five& underlying_function() const\n  {\n    return func;\n  }\n\n  template <typename Tuple>\n  _CCCL_HOST_DEVICE double operator()(Tuple&& t) const\n  {\n    // not calling func, so we would get a wrong result if we were called\n    return 0;\n  }\n\nprivate:\n  mutable sum_five func;\n};\nTHRUST_NAMESPACE_END\n\n// test that the cuda_cub backend of Thrust unwraps zip_iterators/zip_functions into their input streams\nvoid TestTransformThrustZipIteratorUnwrapping()\n{\n  constexpr int num_items = 100;\n  thrust::device_vector<std::int8_t> a(num_items, 1);\n  thrust::device_vector<std::int16_t> b(num_items, 2);\n  thrust::device_vector<std::int32_t> c(num_items, 3);\n  thrust::device_vector<std::int64_t> d(num_items, 4);\n  thrust::device_vector<float> e(num_items, 5);\n\n  thrust::device_vector<double> result(num_items);\n  // SECTION(\"once\") // TODO(bgruber): enable sections when we migrate to Catch2\n  {\n    const auto z = thrust::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin());\n    thrust::transform(z, z + num_items, result.begin(), thrust::make_zip_function(sum_five{}));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n  // SECTION(\"trice\")\n  {\n    const auto z = thrust::make_zip_iterator(\n      thrust::make_zip_iterator(thrust::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin())));\n    thrust::transform(z,\n                      z + num_items,\n                      result.begin(),\n                      thrust::make_zip_function(thrust::make_zip_function(thrust::make_zip_function(sum_five{}))));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n}\nDECLARE_UNITTEST(TestTransformThrustZipIteratorUnwrapping);\n\n// we specialize zip_function for sum_five, but do nothing in the call operator so the test below would fail if the\n// zip_function is actually called (and not unwrapped)\n_CCCL_BEGIN_NAMESPACE_CUDA\ntemplate <>\nclass zip_function<sum_five>\n{\nprivate:\n  sum_five __fun_;\n\npublic:\n  zip_function() = default;\n\n  _CCCL_HOST_DEVICE zip_function(sum_five&& func) noexcept\n      : __fun_(::cuda::std::move(func))\n  {}\n\n  template <typename _Tuple>\n  _CCCL_HOST_DEVICE decltype(auto) operator()(_Tuple&& __tuple) const noexcept\n  {\n    // not calling func, just return a default ctored element, so we would get a wrong result if we were called\n    return decltype(::cuda::std::apply(__fun_, ::cuda::std::forward<_Tuple>(__tuple))){};\n  }\n\n  _CCCL_HOST_DEVICE sum_five& __fun() noexcept\n  {\n    return __fun_;\n  }\n\n  _CCCL_HOST_DEVICE const sum_five& __fun() const noexcept\n  {\n    return __fun_;\n  }\n};\n_CCCL_END_NAMESPACE_CUDA\n\n// test that the cuda_cub backend of Thrust unwraps zip_iterators/zip_functions into their input streams\nvoid TestTransformCudaZipIteratorUnwrapping()\n{\n  constexpr int num_items = 100;\n  thrust::device_vector<std::int8_t> a(num_items, 1);\n  thrust::device_vector<std::int16_t> b(num_items, 2);\n  thrust::device_vector<std::int32_t> c(num_items, 3);\n  thrust::device_vector<std::int64_t> d(num_items, 4);\n  thrust::device_vector<float> e(num_items, 5);\n\n  thrust::device_vector<double> result(num_items);\n  // SECTION(\"once\") // TODO(bgruber): enable sections when we migrate to Catch2\n  {\n    const auto z = cuda::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin());\n    thrust::transform(z, z + num_items, result.begin(), cuda::zip_function(sum_five{}));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n  // SECTION(\"trice\")\n  {\n    const auto z = cuda::make_zip_iterator(\n      cuda::make_zip_iterator(cuda::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin())));\n    thrust::transform(\n      z, z + num_items, result.begin(), cuda::zip_function<cuda::zip_function<cuda::zip_function<sum_five>>>{});\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n}\nDECLARE_UNITTEST(TestTransformCudaZipIteratorUnwrapping);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/transform.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Function, typename Iterator3>\n__global__ void transform_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Function f, Iterator3 result2)\n{\n  *result2 = thrust::transform(exec, first, last, result1, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformUnaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector output(3);\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_kernel<<<1, 1>>>(\n    exec, input.begin(), input.end(), output.begin(), ::cuda::std::negate<T>(), iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformUnaryDeviceSeq()\n{\n  TestTransformUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceSeq);\n\nvoid TestTransformUnaryDeviceDevice()\n{\n  TestTransformUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformUnaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Function,\n          typename Predicate,\n          typename Iterator3>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 result1,\n  Function f,\n  Predicate pred,\n  Iterator3 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfUnaryNoStencilDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{0, -2, 0};\n  Vector output{-1, -2, -3};\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::identity{},\n    iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryNoStencilDeviceSeq()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceSeq);\n\nvoid TestTransformIfUnaryNoStencilDeviceDevice()\n{\n  TestTransformIfUnaryNoStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryNoStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Function,\n          typename Predicate,\n          typename Iterator4>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result1,\n  Function f,\n  Predicate pred,\n  Iterator4 result2)\n{\n  *result2 = thrust::transform_if(exec, first, last, stencil_first, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfUnaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector stencil{1, 0, 1};\n  Vector output{1, 2, 3};\n  Vector result{-1, 2, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input.begin(),\n    input.end(),\n    stencil.begin(),\n    output.begin(),\n    ::cuda::std::negate<T>(),\n    ::cuda::std::identity{},\n    iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfUnaryDeviceSeq()\n{\n  TestTransformIfUnaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceSeq);\n\nvoid TestTransformIfUnaryDeviceDevice()\n{\n  TestTransformIfUnaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfUnaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Function,\n          typename Iterator4>\n__global__ void transform_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator3 result1,\n  Function f,\n  Iterator4 result2)\n{\n  *result2 = thrust::transform(exec, first1, last1, first2, result1, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformBinaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector output(3);\n  Vector result{5, -7, -3};\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_kernel<<<1, 1>>>(\n    exec, input1.begin(), input1.end(), input2.begin(), output.begin(), ::cuda::std::minus<T>(), iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformBinaryDeviceSeq()\n{\n  TestTransformBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceSeq);\n\nvoid TestTransformBinaryDeviceDevice()\n{\n  TestTransformBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformBinaryDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Function,\n          typename Predicate,\n          typename Iterator5>\n__global__ void transform_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator3 stencil_first,\n  Iterator4 result1,\n  Function f,\n  Predicate pred,\n  Iterator5 result2)\n{\n  *result2 = thrust::transform_if(exec, first1, last1, first2, stencil_first, result1, f, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformIfBinaryDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  typename Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector stencil{0, 1, 0};\n  Vector output{1, 2, 3};\n  Vector result{5, 2, -3};\n\n  ::cuda::std::identity identity;\n\n  thrust::device_vector<typename Vector::iterator> iter_vec(1);\n\n  transform_if_kernel<<<1, 1>>>(\n    exec,\n    input1.begin(),\n    input1.end(),\n    input2.begin(),\n    stencil.begin(),\n    output.begin(),\n    ::cuda::std::minus<T>(),\n    ::cuda::std::not_fn(identity),\n    iter_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter = iter_vec[0];\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n}\n\nvoid TestTransformIfBinaryDeviceSeq()\n{\n  TestTransformIfBinaryDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceSeq);\n\nvoid TestTransformIfBinaryDeviceDevice()\n{\n  TestTransformIfBinaryDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformIfBinaryDeviceDevice);\n#endif\n\nvoid TestTransformUnaryCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, -2, 3};\n  Vector output(3);\n  Vector result{-1, 2, -3};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  iter =\n    thrust::transform(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), ::cuda::std::negate<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(output, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformUnaryCudaStreams);\n\nvoid TestTransformBinaryCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input1{1, -2, 3};\n  Vector input2{-4, 5, 6};\n  Vector output(3);\n  Vector result{5, -7, -3};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  iter = thrust::transform(\n    thrust::cuda::par.on(s), input1.begin(), input1.end(), input2.begin(), output.begin(), ::cuda::std::minus<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input1.size());\n  ASSERT_EQUAL(output, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformBinaryCudaStreams);\n\nstruct sum_five\n{\n  _CCCL_HOST_DEVICE auto operator()(std::int8_t a, std::int16_t b, std::int32_t c, std::int64_t d, float e) const\n    -> double\n  {\n    return a + b + c + d + e;\n  }\n};\n\n// we specialize zip_function for sum_five, but do nothing in the call operator so the test below would fail if the\n// zip_function is actually called (and not unwrapped)\nTHRUST_NAMESPACE_BEGIN\ntemplate <>\nclass zip_function<sum_five>\n{\npublic:\n  _CCCL_HOST_DEVICE zip_function(sum_five func)\n      : func(func)\n  {}\n\n  _CCCL_HOST_DEVICE sum_five& underlying_function() const\n  {\n    return func;\n  }\n\n  template <typename Tuple>\n  _CCCL_HOST_DEVICE double operator()(Tuple&& t) const\n  {\n    // not calling func, so we would get a wrong result if we were called\n    return 0;\n  }\n\nprivate:\n  mutable sum_five func;\n};\nTHRUST_NAMESPACE_END\n\n// test that the cuda_cub backend of Thrust unwraps zip_iterators/zip_functions into their input streams\nvoid TestTransformThrustZipIteratorUnwrapping()\n{\n  constexpr int num_items = 100;\n  thrust::device_vector<std::int8_t> a(num_items, 1);\n  thrust::device_vector<std::int16_t> b(num_items, 2);\n  thrust::device_vector<std::int32_t> c(num_items, 3);\n  thrust::device_vector<std::int64_t> d(num_items, 4);\n  thrust::device_vector<float> e(num_items, 5);\n\n  thrust::device_vector<double> result(num_items);\n  // SECTION(\"once\") // TODO(bgruber): enable sections when we migrate to Catch2\n  {\n    const auto z = thrust::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin());\n    thrust::transform(z, z + num_items, result.begin(), thrust::make_zip_function(sum_five{}));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n  // SECTION(\"trice\")\n  {\n    const auto z = thrust::make_zip_iterator(\n      thrust::make_zip_iterator(thrust::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin())));\n    thrust::transform(z,\n                      z + num_items,\n                      result.begin(),\n                      thrust::make_zip_function(thrust::make_zip_function(thrust::make_zip_function(sum_five{}))));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n}\nDECLARE_UNITTEST(TestTransformThrustZipIteratorUnwrapping);\n\n// we specialize zip_function for sum_five, but do nothing in the call operator so the test below would fail if the\n// zip_function is actually called (and not unwrapped)\n_CCCL_BEGIN_NAMESPACE_CUDA\ntemplate <>\nclass zip_function<sum_five>\n{\nprivate:\n  sum_five __fun_;\n\npublic:\n  zip_function() = default;\n\n  _CCCL_HOST_DEVICE zip_function(sum_five&& func) noexcept\n      : __fun_(::cuda::std::move(func))\n  {}\n\n  template <typename _Tuple>\n  _CCCL_HOST_DEVICE decltype(auto) operator()(_Tuple&& __tuple) const noexcept\n  {\n    // not calling func, just return a default ctored element, so we would get a wrong result if we were called\n    return decltype(::cuda::std::apply(__fun_, ::cuda::std::forward<_Tuple>(__tuple))){};\n  }\n\n  _CCCL_HOST_DEVICE sum_five& __fun() noexcept\n  {\n    return __fun_;\n  }\n\n  _CCCL_HOST_DEVICE const sum_five& __fun() const noexcept\n  {\n    return __fun_;\n  }\n};\n_CCCL_END_NAMESPACE_CUDA\n\n// test that the cuda_cub backend of Thrust unwraps zip_iterators/zip_functions into their input streams\nvoid TestTransformCudaZipIteratorUnwrapping()\n{\n  constexpr int num_items = 100;\n  thrust::device_vector<std::int8_t> a(num_items, 1);\n  thrust::device_vector<std::int16_t> b(num_items, 2);\n  thrust::device_vector<std::int32_t> c(num_items, 3);\n  thrust::device_vector<std::int64_t> d(num_items, 4);\n  thrust::device_vector<float> e(num_items, 5);\n\n  thrust::device_vector<double> result(num_items);\n  // SECTION(\"once\") // TODO(bgruber): enable sections when we migrate to Catch2\n  {\n    const auto z = cuda::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin());\n    thrust::transform(z, z + num_items, result.begin(), cuda::zip_function(sum_five{}));\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n  // SECTION(\"trice\")\n  {\n    const auto z = cuda::make_zip_iterator(\n      cuda::make_zip_iterator(cuda::make_zip_iterator(a.begin(), b.begin(), c.begin(), d.begin(), e.begin())));\n    thrust::transform(\n      z, z + num_items, result.begin(), cuda::zip_function<cuda::zip_function<cuda::zip_function<sum_five>>>{});\n\n    // compute reference and verify\n    thrust::device_vector<double> reference(num_items, 1 + 2 + 3 + 4 + 5);\n    ASSERT_EQUAL(reference, result);\n  }\n}\nDECLARE_UNITTEST(TestTransformCudaZipIteratorUnwrapping);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/transform.cu"}
{"input": "#include <thrust/functional.h>\n#include <thrust/scan.h>\n\n#include <cstdio>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::inclusive_scan(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Pred>\n__global__ void\ninclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init, Pred pred)\n{\n  thrust::inclusive_scan(exec, first, last, result, init, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::exclusive_scan(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T>\n__global__ void exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init)\n{\n  thrust::exclusive_scan(exec, first, last, result, init);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestScanDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_input   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11, ::cuda::std::plus<T>{});\n\n  inclusive_scan_kernel<<<1, 1>>>(\n    exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11, ::cuda::std::plus<T>{});\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11);\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::inclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1, 1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::exclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n}\n\ntemplate <typename T>\nstruct TestScanDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestScanDeviceSeq, IntegralTypes> TestScanDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestScanDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestScanDeviceDevice, IntegralTypes> TestScanDeviceDeviceInstance;\n#endif\n\nvoid TestScanCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector result{1, 4, 2, 6, 1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n  // inclusive scan\n\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 0);\n  cudaStreamSynchronize(s);\n\n  result = {0, 1, 4, 2, 6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3);\n  cudaStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with op\n  iter =\n    thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {1, 4, 2, 6, 1};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with init and op\n  iter = thrust::inclusive_scan(\n    thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {4, 7, 5, 9, 4};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init and op\n  iter = thrust::exclusive_scan(\n    thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter  = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  cudaStreamSynchronize(s);\n\n  result = {1, 4, 2, 6, 1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter  = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), 3);\n  cudaStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with implicit init=0\n  input = input_copy;\n  iter  = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  cudaStreamSynchronize(s);\n\n  result = {0, 1, 4, 2, 6};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScanCudaStreams);\n\ntemplate <typename T>\nstruct const_ref_plus_mod3\n{\n  T* table;\n\n  const_ref_plus_mod3(T* table)\n      : table(table)\n  {}\n\n  _CCCL_HOST_DEVICE const T& operator()(T a, T b)\n  {\n    return table[(int) (a + b)];\n  }\n};\n\nstatic void TestInclusiveScanWithConstAccumulator()\n{\n  // add numbers modulo 3 with external lookup table\n  thrust::device_vector<int> data{0, 1, 2, 1, 2, 0, 1};\n\n  thrust::device_vector<int> table{0, 1, 2, 0, 1, 2};\n\n  thrust::inclusive_scan(\n    data.begin(), data.end(), data.begin(), const_ref_plus_mod3<int>(thrust::raw_pointer_cast(&table[0])));\n\n  thrust::device_vector<int> ref{0, 1, 0, 1, 0, 0, 1};\n  ASSERT_EQUAL(data, ref);\n}\nDECLARE_UNITTEST(TestInclusiveScanWithConstAccumulator);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/functional.h>\n#include <thrust/scan.h>\n\n#include <cstdio>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void inclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::inclusive_scan(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Pred>\n__global__ void\ninclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init, Pred pred)\n{\n  thrust::inclusive_scan(exec, first, last, result, init, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::exclusive_scan(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T>\n__global__ void exclusive_scan_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T init)\n{\n  thrust::exclusive_scan(exec, first, last, result, init);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestScanDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_input   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::inclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11, ::cuda::std::plus<T>{});\n\n  inclusive_scan_kernel<<<1, 1>>>(\n    exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11, ::cuda::std::plus<T>{});\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan(h_input.begin(), h_input.end(), h_output.begin(), (T) 11);\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), (T) 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::inclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  inclusive_scan_kernel<<<1, 1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n\n  h_output = h_input;\n  d_output = d_input;\n\n  thrust::exclusive_scan(h_output.begin(), h_output.end(), h_output.begin());\n\n  exclusive_scan_kernel<<<1, 1>>>(exec, d_output.begin(), d_output.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_output, h_output);\n}\n\ntemplate <typename T>\nstruct TestScanDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestScanDeviceSeq, IntegralTypes> TestScanDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestScanDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestScanDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestScanDeviceDevice, IntegralTypes> TestScanDeviceDeviceInstance;\n#endif\n\nvoid TestScanCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector::iterator iter;\n\n  Vector input{1, 3, -2, 4, -5};\n  Vector result{1, 4, 2, 6, 1};\n  Vector output(5);\n\n  Vector input_copy(input);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n  // inclusive scan\n\n  iter = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 0);\n  hipStreamSynchronize(s);\n\n  result = {0, 1, 4, 2, 6};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init\n  iter = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3);\n  hipStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with op\n  iter =\n    thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {1, 4, 2, 6, 1};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inclusive scan with init and op\n  iter = thrust::inclusive_scan(\n    thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {4, 7, 5, 9, 4};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // exclusive scan with init and op\n  iter = thrust::exclusive_scan(\n    thrust::cuda::par.on(s), input.begin(), input.end(), output.begin(), 3, ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - output.begin()), input.size());\n  ASSERT_EQUAL(input, input_copy);\n  ASSERT_EQUAL(output, result);\n\n  // inplace inclusive scan\n  input = input_copy;\n  iter  = thrust::inclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  hipStreamSynchronize(s);\n\n  result = {1, 4, 2, 6, 1};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with init\n  input = input_copy;\n  iter  = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin(), 3);\n  hipStreamSynchronize(s);\n\n  result = {3, 4, 7, 5, 9};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  // inplace exclusive scan with implicit init=0\n  input = input_copy;\n  iter  = thrust::exclusive_scan(thrust::cuda::par.on(s), input.begin(), input.end(), input.begin());\n  hipStreamSynchronize(s);\n\n  result = {0, 1, 4, 2, 6};\n  ASSERT_EQUAL(std::size_t(iter - input.begin()), input.size());\n  ASSERT_EQUAL(input, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestScanCudaStreams);\n\ntemplate <typename T>\nstruct const_ref_plus_mod3\n{\n  T* table;\n\n  const_ref_plus_mod3(T* table)\n      : table(table)\n  {}\n\n  _CCCL_HOST_DEVICE const T& operator()(T a, T b)\n  {\n    return table[(int) (a + b)];\n  }\n};\n\nstatic void TestInclusiveScanWithConstAccumulator()\n{\n  // add numbers modulo 3 with external lookup table\n  thrust::device_vector<int> data{0, 1, 2, 1, 2, 0, 1};\n\n  thrust::device_vector<int> table{0, 1, 2, 0, 1, 2};\n\n  thrust::inclusive_scan(\n    data.begin(), data.end(), data.begin(), const_ref_plus_mod3<int>(thrust::raw_pointer_cast(&table[0])));\n\n  thrust::device_vector<int> ref{0, 1, 0, 1, 0, 0, 1};\n  ASSERT_EQUAL(data, ref);\n}\nDECLARE_UNITTEST(TestInclusiveScanWithConstAccumulator);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/scan.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <cuda/std/utility>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void stable_sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::stable_sort(exec, first, last);\n}\n\nstruct make_pair_functor\n{\n  template <typename T1, typename T2>\n  _CCCL_HOST_DEVICE cuda::std::pair<T1, T2> operator()(const T1& x, const T2& y)\n  {\n    return cuda::std::make_pair(x, y);\n  } // end operator()()\n}; // end make_pair_functor\n\ntemplate <typename ExecutionPolicy>\nvoid TestPairStableSortDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  using P  = cuda::std::pair<int, int>;\n\n  thrust::host_vector<int> h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P> h_pairs(n);\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  thrust::device_vector<P> d_pairs = h_pairs;\n\n  stable_sort_kernel<<<1, 1>>>(exec, d_pairs.begin(), d_pairs.end());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort(h_pairs.begin(), h_pairs.end());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n};\n\nvoid TestPairStableSortDeviceSeq()\n{\n  TestPairStableSortDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceSeq);\n\nvoid TestPairStableSortDeviceDevice()\n{\n  TestPairStableSortDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceDevice);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <cuda/std/utility>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void stable_sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::stable_sort(exec, first, last);\n}\n\nstruct make_pair_functor\n{\n  template <typename T1, typename T2>\n  _CCCL_HOST_DEVICE cuda::std::pair<T1, T2> operator()(const T1& x, const T2& y)\n  {\n    return cuda::std::make_pair(x, y);\n  } // end operator()()\n}; // end make_pair_functor\n\ntemplate <typename ExecutionPolicy>\nvoid TestPairStableSortDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  using P  = cuda::std::pair<int, int>;\n\n  thrust::host_vector<int> h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P> h_pairs(n);\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  thrust::device_vector<P> d_pairs = h_pairs;\n\n  stable_sort_kernel<<<1, 1>>>(exec, d_pairs.begin(), d_pairs.end());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort(h_pairs.begin(), h_pairs.end());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n};\n\nvoid TestPairStableSortDeviceSeq()\n{\n  TestPairStableSortDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceSeq);\n\nvoid TestPairStableSortDeviceDevice()\n{\n  TestPairStableSortDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortDeviceDevice);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/pair_sort.cu"}
{"input": "#include <thrust/copy.h>\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/sort.h>\n\n#include <cuda/std/iterator>\n#include <cuda/std/limits>\n\n#include <algorithm>\n#include <cstdint>\n#include <exception>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct my_less\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Compare>\n__global__ void sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Compare comp)\n{\n  thrust::sort(exec, first, last, comp);\n}\n\ntemplate <typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  sort_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), comp);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::sort(h_data.begin(), h_data.end(), comp);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\ntemplate <typename T>\nstruct TestComparisonSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestComparisonSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortDeviceDeviceDeviceInstance;\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestSortDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortDevice<T>(exec, n, ::cuda::std::less<T>());\n};\n\ntemplate <typename T>\nstruct TestSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestSortDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>> TestSortDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestSortDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortDeviceDeviceInstance;\n#endif\n\nvoid TestSortCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortCudaStreams);\n\nvoid TestComparisonSortCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end(), my_less<int>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end(), my_less<int>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortCudaStreams);\n\ntemplate <typename T>\nstruct TestRadixSortDispatch\n{\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<T>>::value);\n\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<>>::value);\n\n  void operator()() const {}\n};\nSimpleUnitTest<TestRadixSortDispatch,\n               unittest::concat<IntegralTypes,\n                                FloatingPointTypes\n#if _CCCL_HAS_INT128()\n                                ,\n                                unittest::type_list<__int128_t, __uint128_t>\n#endif // _CCCL_HAS_INT128()\n#if _CCCL_HAS_NVFP16()\n                                ,\n                                unittest::type_list<__half>\n#endif // _CCCL_HAS_NVFP16()\n#if _CCCL_HAS_NVBF16()\n                                ,\n                                unittest::type_list<__nv_bfloat16>\n#endif // _CCCL_HAS_NVBF16()\n                                >>\n  TestRadixSortDispatchInstance;\n\n/**\n * Copy of CUB testing utility\n */\ntemplate <typename UnsignedIntegralKeyT>\nstruct index_to_key_value_op\n{\n  static constexpr std::size_t max_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::max());\n  static constexpr std::size_t lowest_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::lowest());\n  static_assert(sizeof(UnsignedIntegralKeyT) < sizeof(std::size_t),\n                \"Calculation of num_distinct_key_values would overflow\");\n  static constexpr std::size_t num_distinct_key_values = (max_key_value - lowest_key_value + std::size_t{1ULL});\n\n  __device__ __host__ UnsignedIntegralKeyT operator()(std::size_t index)\n  {\n    return static_cast<UnsignedIntegralKeyT>(index % num_distinct_key_values);\n  }\n};\n\n/**\n * Copy of CUB testing utility\n */\ntemplate <typename UnsignedIntegralKeyT>\nclass index_to_expected_key_op\n{\nprivate:\n  static constexpr std::size_t max_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::max());\n  static constexpr std::size_t lowest_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::lowest());\n  static_assert(sizeof(UnsignedIntegralKeyT) < sizeof(std::size_t),\n                \"Calculation of num_distinct_key_values would overflow\");\n  static constexpr std::size_t num_distinct_key_values = (max_key_value - lowest_key_value + std::size_t{1ULL});\n\n  // item_count / num_distinct_key_values\n  std::size_t expected_count_per_item;\n  // num remainder items: item_count%num_distinct_key_values\n  std::size_t num_remainder_items;\n  // remainder item_count: expected_count_per_item+1\n  std::size_t remainder_item_count;\n\npublic:\n  index_to_expected_key_op(std::size_t num_total_items)\n      : expected_count_per_item(num_total_items / num_distinct_key_values)\n      , num_remainder_items(num_total_items % num_distinct_key_values)\n      , remainder_item_count(expected_count_per_item + std::size_t{1ULL})\n  {}\n\n  __device__ __host__ UnsignedIntegralKeyT operator()(std::size_t index)\n  {\n    // The first (num_remainder_items * remainder_item_count) are items that appear once more often than the items that\n    // follow remainder_items_offset\n    std::size_t remainder_items_offset = num_remainder_items * remainder_item_count;\n\n    UnsignedIntegralKeyT target_item_index =\n      (index <= remainder_items_offset)\n        ?\n        // This is one of the remainder items\n        static_cast<UnsignedIntegralKeyT>(index / remainder_item_count)\n        :\n        // This is an item that appears exactly expected_count_per_item times\n        static_cast<UnsignedIntegralKeyT>(\n          num_remainder_items + ((index - remainder_items_offset) / expected_count_per_item));\n    return target_item_index;\n  }\n};\n\nvoid TestSortWithMagnitude(int magnitude)\n{\n  try\n  {\n    const std::size_t num_items = 1ull << magnitude;\n    thrust::device_vector<std::uint8_t> vec(num_items);\n    auto counting_it   = thrust::make_counting_iterator(std::size_t{0});\n    auto key_value_it  = thrust::make_transform_iterator(counting_it, index_to_key_value_op<std::uint8_t>{});\n    auto rev_sorted_it = cuda::std::make_reverse_iterator(key_value_it + num_items);\n    thrust::copy(rev_sorted_it, rev_sorted_it + num_items, vec.begin());\n    thrust::sort(vec.begin(), vec.end());\n    auto expected_result_it = thrust::make_transform_iterator(\n      thrust::make_counting_iterator(std::size_t{}), index_to_expected_key_op<std::uint8_t>(num_items));\n    const bool ok = thrust::equal(expected_result_it, expected_result_it + num_items, vec.cbegin());\n    ASSERT_EQUAL(ok, true);\n  }\n  catch (std::bad_alloc&)\n  {}\n}\n\nvoid TestSortWithLargeNumberOfItems()\n{\n  TestSortWithMagnitude(30);\n  // These still require 64-bit dispatches when magnitude < 32.\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestSortWithMagnitude(31);\n  TestSortWithMagnitude(32);\n  TestSortWithMagnitude(33);\n  TestSortWithMagnitude(39);\n#endif\n}\nDECLARE_UNITTEST(TestSortWithLargeNumberOfItems);\n\ntemplate <typename T>\nstruct TestSortAscendingKey\n{\n  void operator()() const\n  {\n    constexpr int n = 10000;\n\n    thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n    thrust::device_vector<T> d_data = h_data;\n\n    std::sort(h_data.begin(), h_data.end(), ::cuda::std::less<T>{});\n    thrust::sort(d_data.begin(), d_data.end(), ::cuda::std::less<T>{});\n\n    ASSERT_EQUAL_QUIET(h_data, d_data);\n  }\n};\n\nSimpleUnitTest<TestSortAscendingKey,\n               unittest::concat<unittest::type_list<>\n#if _CCCL_HAS_INT128()\n                                ,\n                                unittest::type_list<__int128_t, __uint128_t>\n#endif // _CCCL_HAS_INT128()\n// CTK 12.2 offers __host__ __device__ operators for __half and __nv_bfloat16, so we can use std::sort\n#if _CCCL_CTK_AT_LEAST(12, 2)\n#  if _CCCL_HAS_NVFP16() || !defined(__CUDA_NO_HALF_OPERATORS__) && !defined(__CUDA_NO_HALF_CONVERSIONS__)\n                                ,\n                                unittest::type_list<__half>\n#  endif\n#  if _CCCL_HAS_NVBF16() || !defined(__CUDA_NO_BFLOAT16_OPERATORS__) && !defined(__CUDA_NO_BFLOAT16_CONVERSIONS__)\n                                ,\n                                unittest::type_list<__nv_bfloat16>\n#  endif\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n                                >>\n  TestSortAscendingKeyMoreTypes;\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/copy.h>\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/counting_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/sort.h>\n\n#include <cuda/std/iterator>\n#include <cuda/std/limits>\n\n#include <algorithm>\n#include <cstdint>\n#include <exception>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct my_less\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Compare>\n__global__ void sort_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Compare comp)\n{\n  thrust::sort(exec, first, last, comp);\n}\n\ntemplate <typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  sort_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), comp);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::sort(h_data.begin(), h_data.end(), comp);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\ntemplate <typename T>\nstruct TestComparisonSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestComparisonSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortDeviceDeviceDeviceInstance;\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestSortDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortDevice<T>(exec, n, ::cuda::std::less<T>());\n};\n\ntemplate <typename T>\nstruct TestSortDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestSortDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>> TestSortDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestSortDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestSortDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortDeviceDeviceInstance;\n#endif\n\nvoid TestSortCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortCudaStreams);\n\nvoid TestComparisonSortCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort(thrust::cuda::par.on(s), keys.begin(), keys.end(), my_less<int>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end(), my_less<int>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortCudaStreams);\n\ntemplate <typename T>\nstruct TestRadixSortDispatch\n{\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<T>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<T>>::value);\n\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::less<>>::value);\n  static_assert(thrust::cuda_cub::__smart_sort::can_use_primitive_sort<T, ::cuda::std::greater<>>::value);\n\n  void operator()() const {}\n};\nSimpleUnitTest<TestRadixSortDispatch,\n               unittest::concat<IntegralTypes,\n                                FloatingPointTypes\n#if _CCCL_HAS_INT128()\n                                ,\n                                unittest::type_list<__int128_t, __uint128_t>\n#endif // _CCCL_HAS_INT128()\n#if _CCCL_HAS_NVFP16()\n                                ,\n                                unittest::type_list<__half>\n#endif // _CCCL_HAS_NVFP16()\n#if _CCCL_HAS_NVBF16()\n                                ,\n                                unittest::type_list<__hip_bfloat16>\n#endif // _CCCL_HAS_NVBF16()\n                                >>\n  TestRadixSortDispatchInstance;\n\n/**\n * Copy of CUB testing utility\n */\ntemplate <typename UnsignedIntegralKeyT>\nstruct index_to_key_value_op\n{\n  static constexpr std::size_t max_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::max());\n  static constexpr std::size_t lowest_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::lowest());\n  static_assert(sizeof(UnsignedIntegralKeyT) < sizeof(std::size_t),\n                \"Calculation of num_distinct_key_values would overflow\");\n  static constexpr std::size_t num_distinct_key_values = (max_key_value - lowest_key_value + std::size_t{1ULL});\n\n  __device__ __host__ UnsignedIntegralKeyT operator()(std::size_t index)\n  {\n    return static_cast<UnsignedIntegralKeyT>(index % num_distinct_key_values);\n  }\n};\n\n/**\n * Copy of CUB testing utility\n */\ntemplate <typename UnsignedIntegralKeyT>\nclass index_to_expected_key_op\n{\nprivate:\n  static constexpr std::size_t max_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::max());\n  static constexpr std::size_t lowest_key_value =\n    static_cast<std::size_t>(::cuda::std::numeric_limits<UnsignedIntegralKeyT>::lowest());\n  static_assert(sizeof(UnsignedIntegralKeyT) < sizeof(std::size_t),\n                \"Calculation of num_distinct_key_values would overflow\");\n  static constexpr std::size_t num_distinct_key_values = (max_key_value - lowest_key_value + std::size_t{1ULL});\n\n  // item_count / num_distinct_key_values\n  std::size_t expected_count_per_item;\n  // num remainder items: item_count%num_distinct_key_values\n  std::size_t num_remainder_items;\n  // remainder item_count: expected_count_per_item+1\n  std::size_t remainder_item_count;\n\npublic:\n  index_to_expected_key_op(std::size_t num_total_items)\n      : expected_count_per_item(num_total_items / num_distinct_key_values)\n      , num_remainder_items(num_total_items % num_distinct_key_values)\n      , remainder_item_count(expected_count_per_item + std::size_t{1ULL})\n  {}\n\n  __device__ __host__ UnsignedIntegralKeyT operator()(std::size_t index)\n  {\n    // The first (num_remainder_items * remainder_item_count) are items that appear once more often than the items that\n    // follow remainder_items_offset\n    std::size_t remainder_items_offset = num_remainder_items * remainder_item_count;\n\n    UnsignedIntegralKeyT target_item_index =\n      (index <= remainder_items_offset)\n        ?\n        // This is one of the remainder items\n        static_cast<UnsignedIntegralKeyT>(index / remainder_item_count)\n        :\n        // This is an item that appears exactly expected_count_per_item times\n        static_cast<UnsignedIntegralKeyT>(\n          num_remainder_items + ((index - remainder_items_offset) / expected_count_per_item));\n    return target_item_index;\n  }\n};\n\nvoid TestSortWithMagnitude(int magnitude)\n{\n  try\n  {\n    const std::size_t num_items = 1ull << magnitude;\n    thrust::device_vector<std::uint8_t> vec(num_items);\n    auto counting_it   = thrust::make_counting_iterator(std::size_t{0});\n    auto key_value_it  = thrust::make_transform_iterator(counting_it, index_to_key_value_op<std::uint8_t>{});\n    auto rev_sorted_it = cuda::std::make_reverse_iterator(key_value_it + num_items);\n    thrust::copy(rev_sorted_it, rev_sorted_it + num_items, vec.begin());\n    thrust::sort(vec.begin(), vec.end());\n    auto expected_result_it = thrust::make_transform_iterator(\n      thrust::make_counting_iterator(std::size_t{}), index_to_expected_key_op<std::uint8_t>(num_items));\n    const bool ok = thrust::equal(expected_result_it, expected_result_it + num_items, vec.cbegin());\n    ASSERT_EQUAL(ok, true);\n  }\n  catch (std::bad_alloc&)\n  {}\n}\n\nvoid TestSortWithLargeNumberOfItems()\n{\n  TestSortWithMagnitude(30);\n  // These still require 64-bit dispatches when magnitude < 32.\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestSortWithMagnitude(31);\n  TestSortWithMagnitude(32);\n  TestSortWithMagnitude(33);\n  TestSortWithMagnitude(39);\n#endif\n}\nDECLARE_UNITTEST(TestSortWithLargeNumberOfItems);\n\ntemplate <typename T>\nstruct TestSortAscendingKey\n{\n  void operator()() const\n  {\n    constexpr int n = 10000;\n\n    thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n    thrust::device_vector<T> d_data = h_data;\n\n    std::sort(h_data.begin(), h_data.end(), ::cuda::std::less<T>{});\n    thrust::sort(d_data.begin(), d_data.end(), ::cuda::std::less<T>{});\n\n    ASSERT_EQUAL_QUIET(h_data, d_data);\n  }\n};\n\nSimpleUnitTest<TestSortAscendingKey,\n               unittest::concat<unittest::type_list<>\n#if _CCCL_HAS_INT128()\n                                ,\n                                unittest::type_list<__int128_t, __uint128_t>\n#endif // _CCCL_HAS_INT128()\n// CTK 12.2 offers __host__ __device__ operators for __half and __nv_bfloat16, so we can use std::sort\n#if _CCCL_CTK_AT_LEAST(12, 2)\n#  if _CCCL_HAS_NVFP16() || !defined(__CUDA_NO_HALF_OPERATORS__) && !defined(__CUDA_NO_HALF_CONVERSIONS__)\n                                ,\n                                unittest::type_list<__half>\n#  endif\n#  if _CCCL_HAS_NVBF16() || !defined(__CUDA_NO_BFLOAT16_OPERATORS__) && !defined(__CUDA_NO_BFLOAT16_CONVERSIONS__)\n                                ,\n                                unittest::type_list<__hip_bfloat16>\n#  endif\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n                                >>\n  TestSortAscendingKeyMoreTypes;\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/sort.cu"}
{"input": "#include <thrust/device_vector.h>\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n#include <cuda/std/functional>\n\n#include <cstdint>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename BinaryFunction,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  BinaryFunction binary_op,\n  Iterator5 result)\n{\n  *result =\n    thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred, binary_op);\n}\n#endif\n\ntemplate <typename T>\nstruct is_equal_div_10_reduce\n{\n  _CCCL_HOST_DEVICE bool operator()(const T x, const T& y) const\n  {\n    return ((int) x / 10) == ((int) y / 10);\n  }\n};\n\ntemplate <typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\ntemplate <typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0;\n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n// Checks whether the equality operator is ever invoked on out-of-bounds items\nstruct check_valid_item_op\n{\n  cuda::std::uint32_t* error_counter{};\n  int expected_upper_bound{};\n\n  __device__ bool operator()(const int lhs, const int rhs) const\n  {\n    if (lhs > expected_upper_bound || rhs > expected_upper_bound)\n    {\n      if (error_counter)\n      {\n        atomicAdd(error_counter, 1);\n      }\n      return false;\n    }\n    return lhs == rhs;\n  }\n};\n\ntemplate <typename ReturnedT>\nstruct check_accumulator_t_op\n{\n  cuda::std::uint32_t* error_counter{};\n\n  template <typename T,\n            typename U,\n            typename = cuda::std::enable_if_t<cuda::std::is_same_v<T, U> && !cuda::std::is_same_v<T, ReturnedT>>>\n  _CCCL_DEVICE ReturnedT operator()(const T lhs, const U& rhs) const\n  {\n    return static_cast<ReturnedT>(lhs + rhs);\n  }\n\n  template <typename T>\n  _CCCL_DEVICE ReturnedT operator()(const ReturnedT lhs, const T& rhs) const\n  {\n    atomicAdd(error_counter, 1);\n    return lhs + static_cast<ReturnedT>(rhs);\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy>\nvoid TestReduceByKeyDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> keys;\n  thrust::device_vector<T> values;\n\n  using iterator_pair =\n    typename cuda::std::pair<typename thrust::device_vector<T>::iterator, typename thrust::device_vector<T>::iterator>;\n\n  thrust::device_vector<iterator_pair> new_last_vec(1);\n  iterator_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  thrust::device_vector<T> output_keys(keys.size());\n  thrust::device_vector<T> output_values(values.size());\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_reduce<T>(),\n    new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);\n  initialize_values(values);\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::plus<T>(),\n    new_last_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n}\n\nvoid TestReduceByKeyDeviceSeq()\n{\n  TestReduceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceSeq);\n\nvoid TestReduceByKeyDeviceDevice()\n{\n  TestReduceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceDevice);\n\nvoid TestReduceByKeyDeviceNoSync()\n{\n  TestReduceByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  cuda::std::pair<Vector::iterator, Vector::iterator> new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_reduce<T>());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::plus<T>());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestReduceByKeyCudaStreamsSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsSync);\n\nvoid TestReduceByKeyCudaStreamsNoSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsNoSync);\n\n// Maps indices to key ids\nclass div_op\n{\n  std::int64_t m_divisor;\n\npublic:\n  _CCCL_HOST div_op(std::int64_t divisor)\n      : m_divisor(divisor)\n  {}\n\n  _CCCL_HOST_DEVICE std::int64_t operator()(std::int64_t x) const\n  {\n    return x / m_divisor;\n  }\n};\n\n// Produces unique sequence for key\nclass mod_op\n{\n  std::int64_t m_divisor;\n\npublic:\n  _CCCL_HOST mod_op(std::int64_t divisor)\n      : m_divisor(divisor)\n  {}\n\n  _CCCL_HOST_DEVICE std::int64_t operator()(std::int64_t x) const\n  {\n    // div: 2\n    // idx: 0 1   2 3   4 5\n    // key: 0 0 | 1 1 | 2 2\n    // mod: 0 1 | 0 1 | 0 1\n    // ret: 0 1   1 2   2 3\n    return (x % m_divisor) + (x / m_divisor);\n  }\n};\n\nvoid TestReduceByKeyWithBigIndexesHelper(int magnitude)\n{\n  const std::int64_t key_size_magnitude = 8;\n  ASSERT_EQUAL(true, key_size_magnitude < magnitude);\n\n  const std::int64_t num_items       = 1ll << magnitude;\n  const std::int64_t num_unique_keys = 1ll << key_size_magnitude;\n\n  // Size of each key group\n  const std::int64_t key_size = num_items / num_unique_keys;\n\n  using counting_it      = thrust::counting_iterator<std::int64_t>;\n  using transform_key_it = thrust::transform_iterator<div_op, counting_it>;\n  using transform_val_it = thrust::transform_iterator<mod_op, counting_it>;\n\n  counting_it count_begin(0ll);\n  counting_it count_end = count_begin + num_items;\n  ASSERT_EQUAL(static_cast<std::int64_t>(::cuda::std::distance(count_begin, count_end)), num_items);\n\n  transform_key_it keys_begin(count_begin, div_op{key_size});\n  transform_key_it keys_end(count_end, div_op{key_size});\n\n  transform_val_it values_begin(count_begin, mod_op{key_size});\n\n  thrust::device_vector<std::int64_t> output_keys(num_unique_keys);\n  thrust::device_vector<std::int64_t> output_values(num_unique_keys);\n\n  // example:\n  //  items:        6\n  //  unique_keys:  2\n  //  key_size:     3\n  //  keys:         0 0 0 | 1 1 1\n  //  values:       0 1 2 | 1 2 3\n  //  result:       3       6     = sum(range(key_size)) + key_size * key_id\n  thrust::reduce_by_key(keys_begin, keys_end, values_begin, output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(true, thrust::equal(output_keys.begin(), output_keys.end(), count_begin));\n\n  thrust::host_vector<std::int64_t> result = output_values;\n\n  const std::int64_t sum = (key_size - 1) * key_size / 2;\n  for (std::int64_t key_id = 0; key_id < num_unique_keys; key_id++)\n  {\n    ASSERT_EQUAL(result[key_id], sum + key_id * key_size);\n  }\n}\n\nvoid TestReduceByKeyWithBigIndexes()\n{\n  TestReduceByKeyWithBigIndexesHelper(30);\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestReduceByKeyWithBigIndexesHelper(31);\n  TestReduceByKeyWithBigIndexesHelper(32);\n  TestReduceByKeyWithBigIndexesHelper(33);\n#endif\n}\nDECLARE_UNITTEST(TestReduceByKeyWithBigIndexes);\n\nvoid TestReduceByKeyWithCustomEqualityOp()\n{\n  using key_vector_t = thrust::device_vector<cuda::std::int32_t>;\n  using val_vector_t = thrust::device_vector<cuda::std::int32_t>;\n  using key_t        = key_vector_t::value_type;\n  using val_t        = val_vector_t::value_type;\n\n  auto constexpr num_items = 1000;\n  auto keys                = cuda::make_counting_iterator(key_t{0});\n  auto values              = cuda::make_counting_iterator(val_t{42});\n\n  thrust::device_vector<cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  key_vector_t unique_out(num_items);\n  val_vector_t aggregates_out(num_items);\n  auto [unique_out_end, aggregates_out_end] = thrust::reduce_by_key(\n    keys,\n    keys + num_items,\n    values,\n    unique_out.begin(),\n    aggregates_out.begin(),\n    check_valid_item_op{error_counter_ptr, num_items - 1});\n\n  // Verify that the number of unique keys is correct\n  const auto num_unique_out     = cuda::std::distance(unique_out.begin(), unique_out_end);\n  const auto num_aggregates_out = cuda::std::distance(aggregates_out.begin(), aggregates_out_end);\n  ASSERT_EQUAL(num_unique_out, num_items);\n  ASSERT_EQUAL(num_aggregates_out, num_items);\n\n  // Verify that the equality operator was never invoked on out-of-bounds items\n  ASSERT_EQUAL(error_counter[0], cuda::std::uint32_t{0});\n\n  // Verify that unique keys are correct\n  bool all_keys_correct = thrust::equal(unique_out.cbegin(), unique_out.cend(), keys);\n  ASSERT_EQUAL(all_keys_correct, true);\n\n  // Verify that the aggregates are correct\n  bool all_values_correct = thrust::equal(aggregates_out.cbegin(), aggregates_out.cend(), values);\n  ASSERT_EQUAL(all_values_correct, true);\n}\n\nDECLARE_UNITTEST(TestReduceByKeyWithCustomEqualityOp);\n\nvoid TestReduceByKeyWithDifferentAccumulatorT()\n{\n  using key_t          = cuda::std::uint32_t;\n  using val_t          = cuda::std::uint8_t;\n  using reduction_op_t = check_accumulator_t_op<cuda::std::uint32_t>;\n\n  auto constexpr num_items            = 20000;\n  auto constexpr expected_num_uniques = 1;\n  constexpr auto unique_key           = key_t{42U};\n  auto keys                           = cuda::make_constant_iterator(unique_key);\n  auto values                         = cuda::make_counting_iterator(val_t{0});\n\n  thrust::device_vector<cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  thrust::device_vector<key_t> unique_out(expected_num_uniques);\n  thrust::device_vector<val_t> aggregates_out(expected_num_uniques);\n  auto [unique_out_end, aggregates_out_end] = thrust::reduce_by_key(\n    keys,\n    keys + num_items,\n    values,\n    unique_out.begin(),\n    aggregates_out.begin(),\n    cuda::std::equal_to<>{},\n    reduction_op_t{error_counter_ptr});\n\n  // Verify that the number of unique keys is correct\n  auto num_unique_out     = cuda::std::distance(unique_out.begin(), unique_out_end);\n  auto num_aggregates_out = cuda::std::distance(aggregates_out.begin(), aggregates_out_end);\n  ASSERT_EQUAL(num_unique_out, expected_num_uniques);\n  ASSERT_EQUAL(num_aggregates_out, expected_num_uniques);\n\n  // Verify that the equality operator was never invoked on out-of-bounds items\n  ASSERT_EQUAL(error_counter[0], cuda::std::uint32_t{0});\n\n  // Verify that the unique key is correct\n  ASSERT_EQUAL(unique_out[0], unique_key);\n\n  // // Verify that the aggregate is correct\n  constexpr auto mod_val            = 0x01 << cuda::std::numeric_limits<val_t>::digits;\n  constexpr auto sum                = ((num_items * (num_items - 1)) / 2);\n  constexpr auto expected_aggregate = static_cast<val_t>(sum % mod_val);\n  ASSERT_EQUAL(aggregates_out[0], expected_aggregate);\n}\n\nDECLARE_UNITTEST(TestReduceByKeyWithDifferentAccumulatorT);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/transform_iterator.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n#include <cuda/std/functional>\n\n#include <cstdint>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  Iterator5 result)\n{\n  *result = thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename BinaryPredicate,\n          typename BinaryFunction,\n          typename Iterator5>\n__global__ void reduce_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first,\n  Iterator1 keys_last,\n  Iterator2 values_first,\n  Iterator3 keys_result,\n  Iterator4 values_result,\n  BinaryPredicate pred,\n  BinaryFunction binary_op,\n  Iterator5 result)\n{\n  *result =\n    thrust::reduce_by_key(exec, keys_first, keys_last, values_first, keys_result, values_result, pred, binary_op);\n}\n#endif\n\ntemplate <typename T>\nstruct is_equal_div_10_reduce\n{\n  _CCCL_HOST_DEVICE bool operator()(const T x, const T& y) const\n  {\n    return ((int) x / 10) == ((int) y / 10);\n  }\n};\n\ntemplate <typename Vector>\nvoid initialize_keys(Vector& keys)\n{\n  keys.resize(9);\n  keys[0] = 11;\n  keys[1] = 11;\n  keys[2] = 21;\n  keys[3] = 20;\n  keys[4] = 21;\n  keys[5] = 21;\n  keys[6] = 21;\n  keys[7] = 37;\n  keys[8] = 37;\n}\n\ntemplate <typename Vector>\nvoid initialize_values(Vector& values)\n{\n  values.resize(9);\n  values[0] = 0;\n  values[1] = 1;\n  values[2] = 2;\n  values[3] = 3;\n  values[4] = 4;\n  values[5] = 5;\n  values[6] = 6;\n  values[7] = 7;\n  values[8] = 8;\n}\n\n// Checks whether the equality operator is ever invoked on out-of-bounds items\nstruct check_valid_item_op\n{\n  cuda::std::uint32_t* error_counter{};\n  int expected_upper_bound{};\n\n  __device__ bool operator()(const int lhs, const int rhs) const\n  {\n    if (lhs > expected_upper_bound || rhs > expected_upper_bound)\n    {\n      if (error_counter)\n      {\n        atomicAdd(error_counter, 1);\n      }\n      return false;\n    }\n    return lhs == rhs;\n  }\n};\n\ntemplate <typename ReturnedT>\nstruct check_accumulator_t_op\n{\n  cuda::std::uint32_t* error_counter{};\n\n  template <typename T,\n            typename U,\n            typename = cuda::std::enable_if_t<cuda::std::is_same_v<T, U> && !cuda::std::is_same_v<T, ReturnedT>>>\n  _CCCL_DEVICE ReturnedT operator()(const T lhs, const U& rhs) const\n  {\n    return static_cast<ReturnedT>(lhs + rhs);\n  }\n\n  template <typename T>\n  _CCCL_DEVICE ReturnedT operator()(const ReturnedT lhs, const T& rhs) const\n  {\n    atomicAdd(error_counter, 1);\n    return lhs + static_cast<ReturnedT>(rhs);\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy>\nvoid TestReduceByKeyDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> keys;\n  thrust::device_vector<T> values;\n\n  using iterator_pair =\n    typename cuda::std::pair<typename thrust::device_vector<T>::iterator, typename thrust::device_vector<T>::iterator>;\n\n  thrust::device_vector<iterator_pair> new_last_vec(1);\n  iterator_pair new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  thrust::device_vector<T> output_keys(keys.size());\n  thrust::device_vector<T> output_values(values.size());\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin(), new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_reduce<T>(),\n    new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);\n  initialize_values(values);\n\n  reduce_by_key_kernel<<<1, 1>>>(\n    exec,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::plus<T>(),\n    new_last_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  new_last = new_last_vec[0];\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n}\n\nvoid TestReduceByKeyDeviceSeq()\n{\n  TestReduceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceSeq);\n\nvoid TestReduceByKeyDeviceDevice()\n{\n  TestReduceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceDevice);\n\nvoid TestReduceByKeyDeviceNoSync()\n{\n  TestReduceByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector keys;\n  Vector values;\n\n  cuda::std::pair<Vector::iterator, Vector::iterator> new_last;\n\n  // basic test\n  initialize_keys(keys);\n  initialize_values(values);\n\n  Vector output_keys(keys.size());\n  Vector output_values(values.size());\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy, keys.begin(), keys.end(), values.begin(), output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  // test BinaryPredicate\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    is_equal_div_10_reduce<T>());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 3);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 3);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 20);\n  ASSERT_EQUAL(output_values[2], 15);\n\n  // test BinaryFunction\n  initialize_keys(keys);\n  initialize_values(values);\n\n  new_last = thrust::reduce_by_key(\n    streampolicy,\n    keys.begin(),\n    keys.end(),\n    values.begin(),\n    output_keys.begin(),\n    output_values.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::plus<T>());\n\n  ASSERT_EQUAL(new_last.first - output_keys.begin(), 5);\n  ASSERT_EQUAL(new_last.second - output_values.begin(), 5);\n  ASSERT_EQUAL(output_keys[0], 11);\n  ASSERT_EQUAL(output_keys[1], 21);\n  ASSERT_EQUAL(output_keys[2], 20);\n  ASSERT_EQUAL(output_keys[3], 21);\n  ASSERT_EQUAL(output_keys[4], 37);\n\n  ASSERT_EQUAL(output_values[0], 1);\n  ASSERT_EQUAL(output_values[1], 2);\n  ASSERT_EQUAL(output_values[2], 3);\n  ASSERT_EQUAL(output_values[3], 15);\n  ASSERT_EQUAL(output_values[4], 15);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestReduceByKeyCudaStreamsSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsSync);\n\nvoid TestReduceByKeyCudaStreamsNoSync()\n{\n  TestReduceByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceByKeyCudaStreamsNoSync);\n\n// Maps indices to key ids\nclass div_op\n{\n  std::int64_t m_divisor;\n\npublic:\n  _CCCL_HOST div_op(std::int64_t divisor)\n      : m_divisor(divisor)\n  {}\n\n  _CCCL_HOST_DEVICE std::int64_t operator()(std::int64_t x) const\n  {\n    return x / m_divisor;\n  }\n};\n\n// Produces unique sequence for key\nclass mod_op\n{\n  std::int64_t m_divisor;\n\npublic:\n  _CCCL_HOST mod_op(std::int64_t divisor)\n      : m_divisor(divisor)\n  {}\n\n  _CCCL_HOST_DEVICE std::int64_t operator()(std::int64_t x) const\n  {\n    // div: 2\n    // idx: 0 1   2 3   4 5\n    // key: 0 0 | 1 1 | 2 2\n    // mod: 0 1 | 0 1 | 0 1\n    // ret: 0 1   1 2   2 3\n    return (x % m_divisor) + (x / m_divisor);\n  }\n};\n\nvoid TestReduceByKeyWithBigIndexesHelper(int magnitude)\n{\n  const std::int64_t key_size_magnitude = 8;\n  ASSERT_EQUAL(true, key_size_magnitude < magnitude);\n\n  const std::int64_t num_items       = 1ll << magnitude;\n  const std::int64_t num_unique_keys = 1ll << key_size_magnitude;\n\n  // Size of each key group\n  const std::int64_t key_size = num_items / num_unique_keys;\n\n  using counting_it      = thrust::counting_iterator<std::int64_t>;\n  using transform_key_it = thrust::transform_iterator<div_op, counting_it>;\n  using transform_val_it = thrust::transform_iterator<mod_op, counting_it>;\n\n  counting_it count_begin(0ll);\n  counting_it count_end = count_begin + num_items;\n  ASSERT_EQUAL(static_cast<std::int64_t>(::cuda::std::distance(count_begin, count_end)), num_items);\n\n  transform_key_it keys_begin(count_begin, div_op{key_size});\n  transform_key_it keys_end(count_end, div_op{key_size});\n\n  transform_val_it values_begin(count_begin, mod_op{key_size});\n\n  thrust::device_vector<std::int64_t> output_keys(num_unique_keys);\n  thrust::device_vector<std::int64_t> output_values(num_unique_keys);\n\n  // example:\n  //  items:        6\n  //  unique_keys:  2\n  //  key_size:     3\n  //  keys:         0 0 0 | 1 1 1\n  //  values:       0 1 2 | 1 2 3\n  //  result:       3       6     = sum(range(key_size)) + key_size * key_id\n  thrust::reduce_by_key(keys_begin, keys_end, values_begin, output_keys.begin(), output_values.begin());\n\n  ASSERT_EQUAL(true, thrust::equal(output_keys.begin(), output_keys.end(), count_begin));\n\n  thrust::host_vector<std::int64_t> result = output_values;\n\n  const std::int64_t sum = (key_size - 1) * key_size / 2;\n  for (std::int64_t key_id = 0; key_id < num_unique_keys; key_id++)\n  {\n    ASSERT_EQUAL(result[key_id], sum + key_id * key_size);\n  }\n}\n\nvoid TestReduceByKeyWithBigIndexes()\n{\n  TestReduceByKeyWithBigIndexesHelper(30);\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestReduceByKeyWithBigIndexesHelper(31);\n  TestReduceByKeyWithBigIndexesHelper(32);\n  TestReduceByKeyWithBigIndexesHelper(33);\n#endif\n}\nDECLARE_UNITTEST(TestReduceByKeyWithBigIndexes);\n\nvoid TestReduceByKeyWithCustomEqualityOp()\n{\n  using key_vector_t = thrust::device_vector<cuda::std::int32_t>;\n  using val_vector_t = thrust::device_vector<cuda::std::int32_t>;\n  using key_t        = key_vector_t::value_type;\n  using val_t        = val_vector_t::value_type;\n\n  auto constexpr num_items = 1000;\n  auto keys                = cuda::make_counting_iterator(key_t{0});\n  auto values              = cuda::make_counting_iterator(val_t{42});\n\n  thrust::device_vector<cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  key_vector_t unique_out(num_items);\n  val_vector_t aggregates_out(num_items);\n  auto [unique_out_end, aggregates_out_end] = thrust::reduce_by_key(\n    keys,\n    keys + num_items,\n    values,\n    unique_out.begin(),\n    aggregates_out.begin(),\n    check_valid_item_op{error_counter_ptr, num_items - 1});\n\n  // Verify that the number of unique keys is correct\n  const auto num_unique_out     = cuda::std::distance(unique_out.begin(), unique_out_end);\n  const auto num_aggregates_out = cuda::std::distance(aggregates_out.begin(), aggregates_out_end);\n  ASSERT_EQUAL(num_unique_out, num_items);\n  ASSERT_EQUAL(num_aggregates_out, num_items);\n\n  // Verify that the equality operator was never invoked on out-of-bounds items\n  ASSERT_EQUAL(error_counter[0], cuda::std::uint32_t{0});\n\n  // Verify that unique keys are correct\n  bool all_keys_correct = thrust::equal(unique_out.cbegin(), unique_out.cend(), keys);\n  ASSERT_EQUAL(all_keys_correct, true);\n\n  // Verify that the aggregates are correct\n  bool all_values_correct = thrust::equal(aggregates_out.cbegin(), aggregates_out.cend(), values);\n  ASSERT_EQUAL(all_values_correct, true);\n}\n\nDECLARE_UNITTEST(TestReduceByKeyWithCustomEqualityOp);\n\nvoid TestReduceByKeyWithDifferentAccumulatorT()\n{\n  using key_t          = cuda::std::uint32_t;\n  using val_t          = cuda::std::uint8_t;\n  using reduction_op_t = check_accumulator_t_op<cuda::std::uint32_t>;\n\n  auto constexpr num_items            = 20000;\n  auto constexpr expected_num_uniques = 1;\n  constexpr auto unique_key           = key_t{42U};\n  auto keys                           = cuda::make_constant_iterator(unique_key);\n  auto values                         = cuda::make_counting_iterator(val_t{0});\n\n  thrust::device_vector<cuda::std::uint32_t> error_counter(1, 0);\n  auto const error_counter_ptr = thrust::raw_pointer_cast(error_counter.data());\n\n  thrust::device_vector<key_t> unique_out(expected_num_uniques);\n  thrust::device_vector<val_t> aggregates_out(expected_num_uniques);\n  auto [unique_out_end, aggregates_out_end] = thrust::reduce_by_key(\n    keys,\n    keys + num_items,\n    values,\n    unique_out.begin(),\n    aggregates_out.begin(),\n    cuda::std::equal_to<>{},\n    reduction_op_t{error_counter_ptr});\n\n  // Verify that the number of unique keys is correct\n  auto num_unique_out     = cuda::std::distance(unique_out.begin(), unique_out_end);\n  auto num_aggregates_out = cuda::std::distance(aggregates_out.begin(), aggregates_out_end);\n  ASSERT_EQUAL(num_unique_out, expected_num_uniques);\n  ASSERT_EQUAL(num_aggregates_out, expected_num_uniques);\n\n  // Verify that the equality operator was never invoked on out-of-bounds items\n  ASSERT_EQUAL(error_counter[0], cuda::std::uint32_t{0});\n\n  // Verify that the unique key is correct\n  ASSERT_EQUAL(unique_out[0], unique_key);\n\n  // // Verify that the aggregate is correct\n  constexpr auto mod_val            = 0x01 << cuda::std::numeric_limits<val_t>::digits;\n  constexpr auto sum                = ((num_items * (num_items - 1)) / 2);\n  constexpr auto expected_aggregate = static_cast<val_t>(sum % mod_val);\n  ASSERT_EQUAL(aggregates_out[0], expected_aggregate);\n}\n\nDECLARE_UNITTEST(TestReduceByKeyWithDifferentAccumulatorT);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/reduce_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/remove.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void remove_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val, Iterator2 result)\n{\n  *result = thrust::remove(exec, first, last, val);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void remove_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::remove_if(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void remove_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::remove_if(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__ void\nremove_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, T val, Iterator3 result2)\n{\n  *result2 = thrust::remove_copy(exec, first, last, result1, val);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void remove_copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, Iterator3 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, result, pred);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void remove_copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result,\n  Predicate pred,\n  Iterator4 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, stencil_first, result, pred);\n}\n#endif\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (static_cast<unsigned int>(x) & 1) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct is_true\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return x ? true : false;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  size_t h_size = thrust::remove(h_data.begin(), h_data.end(), 0) - h_data.begin();\n\n  remove_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), 0, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveDeviceSeq()\n{\n  TestRemoveDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveDeviceSeq);\n\nvoid TestRemoveDeviceDevice()\n{\n  TestRemoveDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), is_true<int>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveIfDeviceSeq()\n{\n  TestRemoveIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceSeq);\n\nvoid TestRemoveIfDeviceDevice()\n{\n  TestRemoveIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  thrust::host_vector<bool> h_stencil   = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n\n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), h_stencil.begin(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), is_true<int>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveIfStencilDeviceSeq()\n{\n  TestRemoveIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceSeq);\n\nvoid TestRemoveIfStencilDeviceDevice()\n{\n  TestRemoveIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  size_t h_size = thrust::remove_copy(h_data.begin(), h_data.end(), h_result.begin(), 0) - h_result.begin();\n\n  remove_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), 0, d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyDeviceSeq()\n{\n  TestRemoveCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceSeq);\n\nvoid TestRemoveCopyDeviceDevice()\n{\n  TestRemoveCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  size_t h_size =\n    thrust::remove_copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyIfDeviceSeq()\n{\n  TestRemoveCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceSeq);\n\nvoid TestRemoveCopyIfDeviceDevice()\n{\n  TestRemoveCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  thrust::host_vector<bool> h_stencil   = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n\n  size_t h_size =\n    thrust::remove_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_result.begin(), is_true<int>())\n    - h_result.begin();\n\n  remove_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyIfStencilDeviceSeq()\n{\n  TestRemoveCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceSeq);\n\nvoid TestRemoveCopyIfStencilDeviceDevice()\n{\n  TestRemoveCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceDevice);\n#endif\n\nvoid TestRemoveCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2);\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCudaStreams);\n\nvoid TestRemoveCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin(), (T) 2);\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyCudaStreams);\n\nvoid TestRemoveIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s), data.begin(), data.end(), is_even<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfCudaStreams);\n\nvoid TestRemoveIfStencilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::remove_if(thrust::cuda::par.on(s), data.begin(), data.end(), stencil.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilCudaStreams);\n\nvoid TestRemoveCopyIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::remove_copy_if(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin(), is_even<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfCudaStreams);\n\nvoid TestRemoveCopyIfStencilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(\n    thrust::cuda::par.on(s), data.begin(), data.end(), stencil.begin(), result.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/remove.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void remove_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val, Iterator2 result)\n{\n  *result = thrust::remove(exec, first, last, val);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void remove_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::remove_if(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void remove_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::remove_if(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Iterator3>\n__global__ void\nremove_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, T val, Iterator3 result2)\n{\n  *result2 = thrust::remove_copy(exec, first, last, result1, val);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void remove_copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, Iterator3 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, result, pred);\n}\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void remove_copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result,\n  Predicate pred,\n  Iterator4 result_end)\n{\n  *result_end = thrust::remove_copy_if(exec, first, last, stencil_first, result, pred);\n}\n#endif\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (static_cast<unsigned int>(x) & 1) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct is_true\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return x ? true : false;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  size_t h_size = thrust::remove(h_data.begin(), h_data.end(), 0) - h_data.begin();\n\n  remove_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), 0, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveDeviceSeq()\n{\n  TestRemoveDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveDeviceSeq);\n\nvoid TestRemoveDeviceDevice()\n{\n  TestRemoveDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), is_true<int>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveIfDeviceSeq()\n{\n  TestRemoveIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceSeq);\n\nvoid TestRemoveIfDeviceDevice()\n{\n  TestRemoveIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_result(1);\n\n  thrust::host_vector<bool> h_stencil   = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n\n  size_t h_size = thrust::remove_if(h_data.begin(), h_data.end(), h_stencil.begin(), is_true<int>()) - h_data.begin();\n\n  remove_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), is_true<int>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_result[0] - d_data.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_data.resize(h_size);\n  d_data.resize(d_size);\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\nvoid TestRemoveIfStencilDeviceSeq()\n{\n  TestRemoveIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceSeq);\n\nvoid TestRemoveIfStencilDeviceDevice()\n{\n  TestRemoveIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  size_t h_size = thrust::remove_copy(h_data.begin(), h_data.end(), h_result.begin(), 0) - h_result.begin();\n\n  remove_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), 0, d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyDeviceSeq()\n{\n  TestRemoveCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceSeq);\n\nvoid TestRemoveCopyDeviceDevice()\n{\n  TestRemoveCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  size_t h_size =\n    thrust::remove_copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_true<int>()) - h_result.begin();\n\n  remove_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyIfDeviceSeq()\n{\n  TestRemoveCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceSeq);\n\nvoid TestRemoveCopyIfDeviceDevice()\n{\n  TestRemoveCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy>\nvoid TestRemoveCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  using iterator = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iterator> d_new_end(1);\n\n  thrust::host_vector<bool> h_stencil   = unittest::random_integers<bool>(n);\n  thrust::device_vector<bool> d_stencil = h_stencil;\n\n  size_t h_size =\n    thrust::remove_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_result.begin(), is_true<int>())\n    - h_result.begin();\n\n  remove_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_result.begin(), is_true<int>(), d_new_end.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  size_t d_size = (iterator) d_new_end[0] - d_result.begin();\n\n  ASSERT_EQUAL(h_size, d_size);\n\n  h_result.resize(h_size);\n  d_result.resize(d_size);\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\nvoid TestRemoveCopyIfStencilDeviceSeq()\n{\n  TestRemoveCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceSeq);\n\nvoid TestRemoveCopyIfStencilDeviceDevice()\n{\n  TestRemoveCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilDeviceDevice);\n#endif\n\nvoid TestRemoveCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2);\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCudaStreams);\n\nvoid TestRemoveCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin(), (T) 2);\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyCudaStreams);\n\nvoid TestRemoveIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_if(thrust::cuda::par.on(s), data.begin(), data.end(), is_even<T>());\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfCudaStreams);\n\nvoid TestRemoveIfStencilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::remove_if(thrust::cuda::par.on(s), data.begin(), data.end(), stencil.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - data.begin(), 3);\n  data.erase(end, data.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveIfStencilCudaStreams);\n\nvoid TestRemoveCopyIfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::remove_copy_if(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin(), is_even<T>());\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfCudaStreams);\n\nvoid TestRemoveCopyIfStencilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::remove_copy_if(\n    thrust::cuda::par.on(s), data.begin(), data.end(), stencil.begin(), result.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - result.begin(), 3);\n  result.erase(end, result.end());\n\n  Vector ref{1, 1, 3};\n  ASSERT_EQUAL(result, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestRemoveCopyIfStencilCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/remove.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__ void\nmax_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> d_result(1);\n\n  typename thrust::host_vector<int>::iterator h_max = thrust::max_element(h_data.begin(), h_data.end());\n\n  max_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator h_min =\n    thrust::max_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>());\n\n  max_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n}\n\nvoid TestMaxElementDeviceSeq()\n{\n  TestMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceSeq);\n\nvoid TestMaxElementDeviceDevice()\n{\n  TestMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceDevice);\n\nvoid TestMaxElementDeviceNoSync()\n{\n  TestMaxElementDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestMaxElementCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  ASSERT_EQUAL(*thrust::max_element(streampolicy, data.begin(), data.end()), 5);\n  ASSERT_EQUAL(thrust::max_element(streampolicy, data.begin(), data.end()) - data.begin(), 1);\n\n  ASSERT_EQUAL(*thrust::max_element(streampolicy, data.begin(), data.end(), ::cuda::std::greater<T>()), 1);\n  ASSERT_EQUAL(thrust::max_element(streampolicy, data.begin(), data.end(), ::cuda::std::greater<T>()) - data.begin(),\n               2);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestMaxElementCudaStreamsSync()\n{\n  TestMaxElementCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsSync);\n\nvoid TestMaxElementCudaStreamsNoSync()\n{\n  TestMaxElementCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsNoSync);\n\nvoid TestMaxElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::max_element(thrust::device, raw_ptr, raw_ptr + n) - raw_ptr, 1);\n  ASSERT_EQUAL(thrust::max_element(thrust::device, raw_ptr, raw_ptr + n, ::cuda::std::greater<T>()) - raw_ptr, 2);\n}\nDECLARE_UNITTEST(TestMaxElementDevicePointer);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void max_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__ void\nmax_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::max_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> d_result(1);\n\n  typename thrust::host_vector<int>::iterator h_max = thrust::max_element(h_data.begin(), h_data.end());\n\n  max_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator h_min =\n    thrust::max_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>());\n\n  max_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n}\n\nvoid TestMaxElementDeviceSeq()\n{\n  TestMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceSeq);\n\nvoid TestMaxElementDeviceDevice()\n{\n  TestMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceDevice);\n\nvoid TestMaxElementDeviceNoSync()\n{\n  TestMaxElementDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestMaxElementCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  ASSERT_EQUAL(*thrust::max_element(streampolicy, data.begin(), data.end()), 5);\n  ASSERT_EQUAL(thrust::max_element(streampolicy, data.begin(), data.end()) - data.begin(), 1);\n\n  ASSERT_EQUAL(*thrust::max_element(streampolicy, data.begin(), data.end(), ::cuda::std::greater<T>()), 1);\n  ASSERT_EQUAL(thrust::max_element(streampolicy, data.begin(), data.end(), ::cuda::std::greater<T>()) - data.begin(),\n               2);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestMaxElementCudaStreamsSync()\n{\n  TestMaxElementCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsSync);\n\nvoid TestMaxElementCudaStreamsNoSync()\n{\n  TestMaxElementCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestMaxElementCudaStreamsNoSync);\n\nvoid TestMaxElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::max_element(thrust::device, raw_ptr, raw_ptr + n) - raw_ptr, 1);\n  ASSERT_EQUAL(thrust::max_element(thrust::device, raw_ptr, raw_ptr + n, ::cuda::std::greater<T>()) - raw_ptr, 2);\n}\nDECLARE_UNITTEST(TestMaxElementDevicePointer);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/max_element.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct my_less\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Compare>\n__global__ void sort_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Compare comp)\n{\n  thrust::sort_by_key(exec, keys_first, keys_last, values_first, comp);\n}\n\ntemplate <typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortByKeyDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T> h_keys   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_keys = h_keys;\n\n  thrust::host_vector<T> h_values   = h_keys;\n  thrust::device_vector<T> d_values = d_keys;\n\n  sort_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_values.begin(), comp);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::sort_by_key(h_keys.begin(), h_keys.end(), h_values.begin(), comp);\n\n  ASSERT_EQUAL(h_keys, d_keys);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\ntemplate <typename T>\nstruct TestComparisonSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortByKeyDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortByKeyDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestComparisonSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortByKeyDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortByKeyDeviceDeviceDeviceInstance;\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestSortByKeyDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortByKeyDevice<T>(exec, n, ::cuda::std::less<T>());\n};\n\ntemplate <typename T>\nstruct TestSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestSortByKeyDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortByKeyDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestSortByKeyDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortByKeyDeviceDeviceInstance;\n#endif\n\nvoid TestComparisonSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n  thrust::device_vector<int> vals{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), my_less<int>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortByKeyCudaStreams);\n\nvoid TestSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n  thrust::device_vector<int> vals{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct my_less\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& lhs, const T& rhs) const\n  {\n    return lhs < rhs;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Compare>\n__global__ void sort_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Compare comp)\n{\n  thrust::sort_by_key(exec, keys_first, keys_last, values_first, comp);\n}\n\ntemplate <typename T, typename ExecutionPolicy, typename Compare>\nvoid TestComparisonSortByKeyDevice(ExecutionPolicy exec, const size_t n, Compare comp)\n{\n  thrust::host_vector<T> h_keys   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_keys = h_keys;\n\n  thrust::host_vector<T> h_values   = h_keys;\n  thrust::device_vector<T> d_values = d_keys;\n\n  sort_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_values.begin(), comp);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::sort_by_key(h_keys.begin(), h_keys.end(), h_values.begin(), comp);\n\n  ASSERT_EQUAL(h_keys, d_keys);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\ntemplate <typename T>\nstruct TestComparisonSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::seq, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortByKeyDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortByKeyDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestComparisonSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestComparisonSortByKeyDevice<T>(thrust::device, n, my_less<T>());\n  }\n};\nVariableUnitTest<TestComparisonSortByKeyDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestComparisonSortByKeyDeviceDeviceDeviceInstance;\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestSortByKeyDevice(ExecutionPolicy exec, const size_t n)\n{\n  TestComparisonSortByKeyDevice<T>(exec, n, ::cuda::std::less<T>());\n};\n\ntemplate <typename T>\nstruct TestSortByKeyDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestSortByKeyDeviceSeq, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortByKeyDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestSortByKeyDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestSortByKeyDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestSortByKeyDeviceDevice, unittest::type_list<unittest::int8_t, unittest::int32_t>>\n  TestSortByKeyDeviceDeviceInstance;\n#endif\n\nvoid TestComparisonSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n  thrust::device_vector<int> vals{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), my_less<int>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestComparisonSortByKeyCudaStreams);\n\nvoid TestSortByKeyCudaStreams()\n{\n  thrust::device_vector<int> keys{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n  thrust::device_vector<int> vals{9, 3, 2, 0, 4, 7, 8, 1, 5, 6};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sort_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(true, thrust::is_sorted(keys.begin(), keys.end()));\n  ASSERT_EQUAL(true, thrust::is_sorted(vals.begin(), vals.end()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSortByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/sort_by_key.cu"}
{"input": "#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void count_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::count(exec, first, last, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCountDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n\n  size_t h_result = thrust::count(h_data.begin(), h_data.end(), T(5));\n\n  count_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), T(5), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nvoid TestCountDeviceSeq(const size_t n)\n{\n  TestCountDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceSeq);\n\ntemplate <typename T>\nvoid TestCountDeviceDevice(const size_t n)\n{\n  TestCountDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void count_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::count_if(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct greater_than_five\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& x) const\n  {\n    return x > 5;\n  }\n};\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCountIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n\n  size_t h_result = thrust::count_if(h_data.begin(), h_data.end(), greater_than_five<T>());\n  count_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), greater_than_five<T>(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nvoid TestCountIfDeviceSeq(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceSeq);\n\ntemplate <typename T>\nvoid TestCountIfDeviceDevice(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceDevice);\n#endif\n\nvoid TestCountCudaStreams()\n{\n  thrust::device_vector<int> data{1, 1, 0, 0, 1};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 0), 2);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 1), 3);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 2), 0);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestCountCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void count_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::count(exec, first, last, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCountDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n\n  size_t h_result = thrust::count(h_data.begin(), h_data.end(), T(5));\n\n  count_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), T(5), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nvoid TestCountDeviceSeq(const size_t n)\n{\n  TestCountDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceSeq);\n\ntemplate <typename T>\nvoid TestCountDeviceDevice(const size_t n)\n{\n  TestCountDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void count_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::count_if(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct greater_than_five\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& x) const\n  {\n    return x > 5;\n  }\n};\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestCountIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<size_t> d_result(1);\n\n  size_t h_result = thrust::count_if(h_data.begin(), h_data.end(), greater_than_five<T>());\n  count_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), greater_than_five<T>(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nvoid TestCountIfDeviceSeq(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceSeq);\n\ntemplate <typename T>\nvoid TestCountIfDeviceDevice(const size_t n)\n{\n  TestCountIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestCountIfDeviceDevice);\n#endif\n\nvoid TestCountCudaStreams()\n{\n  thrust::device_vector<int> data{1, 1, 0, 0, 1};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 0), 2);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 1), 3);\n  ASSERT_EQUAL(thrust::count(thrust::cuda::par.on(s), data.begin(), data.end(), 2), 0);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestCountCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/count.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void is_sorted_until_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::is_sorted_until(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsSortedUntilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_until_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.begin() + 1, (iter_type) result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_until_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.end(), (iter_type) result[0]);\n}\n\nvoid TestIsSortedUntilDeviceSeq()\n{\n  TestIsSortedUntilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceSeq);\n\nvoid TestIsSortedUntilDeviceDevice()\n{\n  TestIsSortedUntilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceDevice);\n#endif\n\nvoid TestIsSortedUntilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector v(4);\n  v[0] = 0;\n  v[1] = 5;\n  v[2] = 8;\n  v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 0;\n  Iterator ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 1;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 2;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::less<T>()));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::less<T>()));\n\n  last = v.begin() + 1;\n  ref  = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  first = v.begin() + 2;\n  last  = v.begin() + 4;\n  ref   = v.begin() + 4;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedUntilCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void is_sorted_until_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::is_sorted_until(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsSortedUntilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_until_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.begin() + 1, (iter_type) result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_until_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL_QUIET(v.end(), (iter_type) result[0]);\n}\n\nvoid TestIsSortedUntilDeviceSeq()\n{\n  TestIsSortedUntilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceSeq);\n\nvoid TestIsSortedUntilDeviceDevice()\n{\n  TestIsSortedUntilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedUntilDeviceDevice);\n#endif\n\nvoid TestIsSortedUntilCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector v(4);\n  v[0] = 0;\n  v[1] = 5;\n  v[2] = 8;\n  v[3] = 0;\n\n  Iterator first = v.begin();\n\n  Iterator last = v.begin() + 0;\n  Iterator ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 1;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 2;\n  ref  = last;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last));\n\n  last = v.begin() + 3;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::less<T>()));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 3;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::less<T>()));\n\n  last = v.begin() + 1;\n  ref  = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  last = v.begin() + 4;\n  ref  = v.begin() + 1;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  first = v.begin() + 2;\n  last  = v.begin() + 4;\n  ref   = v.begin() + 4;\n  ASSERT_EQUAL_QUIET(ref, thrust::is_sorted_until(thrust::cuda::par.on(s), first, last, ::cuda::std::greater<T>()));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedUntilCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/is_sorted_until.cu"}
{"input": "#include <thrust/distance.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/offset_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <cuda/std/iterator>\n\n#include <unittest/unittest.h>\n\nstruct device_only_iterator\n{\n  using iterator_category = cuda::std::random_access_iterator_tag;\n  using difference_type   = cuda::std::ptrdiff_t;\n  using value_type        = int;\n  using pointer           = int*;\n  using reference         = int&;\n\n  _CCCL_HOST_DEVICE device_only_iterator(pointer ptr)\n      : m_ptr(ptr)\n  {}\n\n  _CCCL_DEVICE reference operator*() const\n  {\n    return *m_ptr;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator++()\n  {\n    ++m_ptr;\n    return *this;\n  }\n\n  _CCCL_DEVICE device_only_iterator operator++(int)\n  {\n    device_only_iterator tmp = *this;\n    ++*this;\n    return tmp;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator--()\n  {\n    --m_ptr;\n    return *this;\n  }\n\n  _CCCL_DEVICE device_only_iterator operator--(int)\n  {\n    device_only_iterator tmp = *this;\n    --*this;\n    return tmp;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator+=(difference_type n)\n  {\n    m_ptr += n;\n    return *this;\n  }\n\n  _CCCL_DEVICE friend device_only_iterator operator+(device_only_iterator it, difference_type d)\n  {\n    return it += d;\n  }\n\n  _CCCL_DEVICE friend difference_type operator-(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr - b.m_ptr;\n  }\n\n  _CCCL_DEVICE friend bool operator==(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr == b.m_ptr;\n  }\n\n  _CCCL_DEVICE friend bool operator!=(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr != b.m_ptr;\n  }\n\nprivate:\n  pointer m_ptr;\n};\n\n_CCCL_HOST_DEVICE void TestOffsetIteratorBoth(thrust::offset_iterator<device_only_iterator> iter)\n{\n  assert(iter.offset() == 0);\n  ++iter;\n  assert(iter.offset() == 1);\n  iter++;\n  assert(iter.offset() == 2);\n  --iter;\n  assert(iter.offset() == 1);\n  iter--;\n  assert(iter.offset() == 0);\n  iter += 100;\n  assert(iter.offset() == 100);\n}\n\n__global__ void TestOffsetIteratorDevice(thrust::offset_iterator<device_only_iterator> iter)\n{\n  TestOffsetIteratorBoth(iter);\n\n  // access\n  assert(*iter == 1);\n\n  auto iter2 = iter;\n  iter2 += 3;\n  assert(*iter2 == 1);\n\n  // difference\n  assert(iter2 - iter == 3);\n\n  // comparison\n  assert(!(iter2 == iter));\n  assert(iter2 != iter);\n}\n\nvoid TestOffsetIteratorWithDeviceOnlyIterator()\n{\n  thrust::device_vector<int> v{1, 2, 3, 4, 5};\n  device_only_iterator base(thrust::raw_pointer_cast(v.data()));\n  thrust::offset_iterator iter(base);\n  TestOffsetIteratorBoth(iter);\n  TestOffsetIteratorDevice<<<1, 1>>>(iter);\n}\nDECLARE_UNITTEST(TestOffsetIteratorWithDeviceOnlyIterator);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/distance.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/offset_iterator.h>\n#include <thrust/iterator/transform_iterator.h>\n\n#include <cuda/std/iterator>\n\n#include <unittest/unittest.h>\n\nstruct device_only_iterator\n{\n  using iterator_category = cuda::std::random_access_iterator_tag;\n  using difference_type   = cuda::std::ptrdiff_t;\n  using value_type        = int;\n  using pointer           = int*;\n  using reference         = int&;\n\n  _CCCL_HOST_DEVICE device_only_iterator(pointer ptr)\n      : m_ptr(ptr)\n  {}\n\n  _CCCL_DEVICE reference operator*() const\n  {\n    return *m_ptr;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator++()\n  {\n    ++m_ptr;\n    return *this;\n  }\n\n  _CCCL_DEVICE device_only_iterator operator++(int)\n  {\n    device_only_iterator tmp = *this;\n    ++*this;\n    return tmp;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator--()\n  {\n    --m_ptr;\n    return *this;\n  }\n\n  _CCCL_DEVICE device_only_iterator operator--(int)\n  {\n    device_only_iterator tmp = *this;\n    --*this;\n    return tmp;\n  }\n\n  _CCCL_DEVICE device_only_iterator& operator+=(difference_type n)\n  {\n    m_ptr += n;\n    return *this;\n  }\n\n  _CCCL_DEVICE friend device_only_iterator operator+(device_only_iterator it, difference_type d)\n  {\n    return it += d;\n  }\n\n  _CCCL_DEVICE friend difference_type operator-(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr - b.m_ptr;\n  }\n\n  _CCCL_DEVICE friend bool operator==(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr == b.m_ptr;\n  }\n\n  _CCCL_DEVICE friend bool operator!=(const device_only_iterator& a, const device_only_iterator& b)\n  {\n    return a.m_ptr != b.m_ptr;\n  }\n\nprivate:\n  pointer m_ptr;\n};\n\n_CCCL_HOST_DEVICE void TestOffsetIteratorBoth(thrust::offset_iterator<device_only_iterator> iter)\n{\n  assert(iter.offset() == 0);\n  ++iter;\n  assert(iter.offset() == 1);\n  iter++;\n  assert(iter.offset() == 2);\n  --iter;\n  assert(iter.offset() == 1);\n  iter--;\n  assert(iter.offset() == 0);\n  iter += 100;\n  assert(iter.offset() == 100);\n}\n\n__global__ void TestOffsetIteratorDevice(thrust::offset_iterator<device_only_iterator> iter)\n{\n  TestOffsetIteratorBoth(iter);\n\n  // access\n  assert(*iter == 1);\n\n  auto iter2 = iter;\n  iter2 += 3;\n  assert(*iter2 == 1);\n\n  // difference\n  assert(iter2 - iter == 3);\n\n  // comparison\n  assert(!(iter2 == iter));\n  assert(iter2 != iter);\n}\n\nvoid TestOffsetIteratorWithDeviceOnlyIterator()\n{\n  thrust::device_vector<int> v{1, 2, 3, 4, 5};\n  device_only_iterator base(thrust::raw_pointer_cast(v.data()));\n  thrust::offset_iterator iter(base);\n  TestOffsetIteratorBoth(iter);\n  TestOffsetIteratorDevice<<<1, 1>>>(iter);\n}\nDECLARE_UNITTEST(TestOffsetIteratorWithDeviceOnlyIterator);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/offset_iterator.cu"}
{"input": "#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate>\n__global__ void\nminmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMinMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_min;\n  typename thrust::host_vector<int>::iterator h_max;\n  typename thrust::device_vector<int>::iterator d_min;\n  typename thrust::device_vector<int>::iterator d_max;\n\n  using pair_type =\n    cuda::std::pair<typename thrust::device_vector<int>::iterator, typename thrust::device_vector<int>::iterator>;\n\n  thrust::device_vector<pair_type> d_result(1);\n\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end()).first;\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end()).second;\n\n  d_min = thrust::minmax_element(d_data.begin(), d_data.end()).first;\n  d_max = thrust::minmax_element(d_data.begin(), d_data.end()).second;\n\n  minmax_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  d_min = ((pair_type) d_result[0]).first;\n  d_max = ((pair_type) d_result[0]).second;\n\n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>()).first;\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>()).second;\n\n  minmax_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  d_max = ((pair_type) d_result[0]).first;\n  d_min = ((pair_type) d_result[0]).second;\n\n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n}\n\nvoid TestMinMaxElementDeviceSeq()\n{\n  TestMinMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceSeq);\n\nvoid TestMinMaxElementDeviceDevice()\n{\n  TestMinMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceDevice);\n#endif\n\nvoid TestMinMaxElementCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(*thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first, 1);\n  ASSERT_EQUAL(*thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second, 5);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first - data.begin(), 2);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second - data.begin(), 1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinMaxElementCudaStreams);\n\nvoid TestMinMaxElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::minmax_element(thrust::device, raw_ptr, raw_ptr + n).first - raw_ptr, 2);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::device, raw_ptr, raw_ptr + n).second - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinMaxElementDevicePointer);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void minmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate>\n__global__ void\nminmax_element_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::minmax_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMinMaxElementDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_min;\n  typename thrust::host_vector<int>::iterator h_max;\n  typename thrust::device_vector<int>::iterator d_min;\n  typename thrust::device_vector<int>::iterator d_max;\n\n  using pair_type =\n    cuda::std::pair<typename thrust::device_vector<int>::iterator, typename thrust::device_vector<int>::iterator>;\n\n  thrust::device_vector<pair_type> d_result(1);\n\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end()).first;\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end()).second;\n\n  d_min = thrust::minmax_element(d_data.begin(), d_data.end()).first;\n  d_max = thrust::minmax_element(d_data.begin(), d_data.end()).second;\n\n  minmax_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  d_min = ((pair_type) d_result[0]).first;\n  d_max = ((pair_type) d_result[0]).second;\n\n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n\n  h_max = thrust::minmax_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>()).first;\n  h_min = thrust::minmax_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>()).second;\n\n  minmax_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  d_max = ((pair_type) d_result[0]).first;\n  d_min = ((pair_type) d_result[0]).second;\n\n  ASSERT_EQUAL(h_min - h_data.begin(), d_min - d_data.begin());\n  ASSERT_EQUAL(h_max - h_data.begin(), d_max - d_data.begin());\n}\n\nvoid TestMinMaxElementDeviceSeq()\n{\n  TestMinMaxElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceSeq);\n\nvoid TestMinMaxElementDeviceDevice()\n{\n  TestMinMaxElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinMaxElementDeviceDevice);\n#endif\n\nvoid TestMinMaxElementCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(*thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first, 1);\n  ASSERT_EQUAL(*thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second, 5);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).first - data.begin(), 2);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::cuda::par.on(s), data.begin(), data.end()).second - data.begin(), 1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinMaxElementCudaStreams);\n\nvoid TestMinMaxElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::minmax_element(thrust::device, raw_ptr, raw_ptr + n).first - raw_ptr, 2);\n  ASSERT_EQUAL(thrust::minmax_element(thrust::device, raw_ptr, raw_ptr + n).second - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinMaxElementDevicePointer);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/minmax_element.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_symmetric_difference_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_symmetric_difference_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4, 6}, b_key{0, 3, 3, 4, 7};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 3, 3, 6, 7}, ref_val{0, 1, 1, 0, 1};\n  Vector result_key(5), result_val(5);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_symmetric_difference_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  iter_pair end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetSymmetricDifferenceByKeyDeviceSeq()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceSeq);\n\nvoid TestSetSymmetricDifferenceByKeyDeviceDevice()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceDevice);\n#endif\n\nvoid TestSetSymmetricDifferenceByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4, 6}, b_key{0, 3, 3, 4, 7};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 3, 3, 6, 7}, ref_val{0, 1, 1, 0, 1};\n  Vector result_key(5), result_val(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_symmetric_difference_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_symmetric_difference_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_symmetric_difference_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetSymmetricDifferenceByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4, 6}, b_key{0, 3, 3, 4, 7};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 3, 3, 6, 7}, ref_val{0, 1, 1, 0, 1};\n  Vector result_key(5), result_val(5);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_symmetric_difference_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  iter_pair end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetSymmetricDifferenceByKeyDeviceSeq()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceSeq);\n\nvoid TestSetSymmetricDifferenceByKeyDeviceDevice()\n{\n  TestSetSymmetricDifferenceByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyDeviceDevice);\n#endif\n\nvoid TestSetSymmetricDifferenceByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4, 6}, b_key{0, 3, 3, 4, 7};\n  Vector a_val(4, 0), b_val(5, 1);\n\n  Vector ref_key{2, 3, 3, 6, 7}, ref_val{0, 1, 1, 0, 1};\n  Vector result_key(5), result_val(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_symmetric_difference_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetSymmetricDifferenceByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_symmetric_difference_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/scan.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void inclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::inclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void exclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename T>\n__global__ void exclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result, T init)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result, init);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestScanByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_keys(n);\n  for (size_t i = 0, k = 0; i < n; i++)\n  {\n    h_keys[i] = static_cast<int>(k);\n    if (rand() % 10 == 0)\n    {\n      k++;\n    }\n  }\n  thrust::device_vector<int> d_keys = h_keys;\n\n  thrust::host_vector<int> h_vals = unittest::random_integers<int>(n);\n  for (size_t i = 0; i < n; i++)\n  {\n    h_vals[i] = i % 10;\n  }\n  thrust::device_vector<int> d_vals = h_vals;\n\n  thrust::host_vector<int> h_output(n);\n  thrust::device_vector<int> d_output(n);\n\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: in/out values aliasing\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: keys/values aliasing\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n\n  d_keys = h_keys;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin(), 11);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n}\n\nvoid TestScanByKeyDeviceSeq()\n{\n  TestScanByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceSeq);\n\nvoid TestScanByKeyDeviceDevice()\n{\n  TestScanByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceDevice);\n#endif\n\nvoid TestInclusiveScanByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector keys{0, 1, 1, 1, 2, 3, 3};\n  Vector vals{1, 2, 3, 4, 5, 6, 7};\n\n  Vector output(7, 0);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator iter =\n    thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  Vector ref{1, 2, 5, 9, 5, 6, 13};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::inclusive_scan_by_key(\n    thrust::cuda::par.on(s),\n    keys.begin(),\n    keys.end(),\n    vals.begin(),\n    output.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::multiplies<T>());\n  cudaStreamSynchronize(s);\n\n  ref = {1, 2, 6, 24, 5, 6, 42};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::inclusive_scan_by_key(\n    thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), ::cuda::std::equal_to<T>());\n  cudaStreamSynchronize(s);\n\n  ref = {1, 2, 5, 9, 5, 6, 13};\n  ASSERT_EQUAL(output, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInclusiveScanByKeyCudaStreams);\n\nvoid TestExclusiveScanByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector keys{0, 1, 1, 1, 2, 3, 3};\n  Vector vals{1, 2, 3, 4, 5, 6, 7};\n\n  Vector output(7, 0);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator iter =\n    thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  Vector ref{0, 0, 2, 5, 0, 0, 6};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10));\n  cudaStreamSynchronize(s);\n\n  ref = {10, 10, 12, 15, 10, 10, 16};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(\n    thrust::cuda::par.on(s),\n    keys.begin(),\n    keys.end(),\n    vals.begin(),\n    output.begin(),\n    T(10),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::multiplies<T>());\n  cudaStreamSynchronize(s);\n\n  ref = {10, 10, 20, 60, 10, 10, 60};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(\n    thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), ::cuda::std::equal_to<T>());\n  cudaStreamSynchronize(s);\n\n  ref = {10, 10, 12, 15, 10, 10, 16};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestExclusiveScanByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/scan.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void inclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::inclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void exclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename T>\n__global__ void exclusive_scan_by_key_kernel(\n  ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first, Iterator3 result, T init)\n{\n  thrust::exclusive_scan_by_key(exec, keys_first, keys_last, values_first, result, init);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestScanByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::host_vector<int> h_keys(n);\n  for (size_t i = 0, k = 0; i < n; i++)\n  {\n    h_keys[i] = static_cast<int>(k);\n    if (rand() % 10 == 0)\n    {\n      k++;\n    }\n  }\n  thrust::device_vector<int> d_keys = h_keys;\n\n  thrust::host_vector<int> h_vals = unittest::random_integers<int>(n);\n  for (size_t i = 0; i < n; i++)\n  {\n    h_vals[i] = i % 10;\n  }\n  thrust::device_vector<int> d_vals = h_vals;\n\n  thrust::host_vector<int> h_output(n);\n  thrust::device_vector<int> d_output(n);\n\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_output.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: in/out values aliasing\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  h_output = h_vals;\n  d_output = d_vals;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_output.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_output.begin(), d_output.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_output, h_output);\n\n  // in-place scans: keys/values aliasing\n  thrust::inclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin());\n  inclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n\n  d_keys = h_keys;\n  thrust::exclusive_scan_by_key(h_keys.begin(), h_keys.end(), h_vals.begin(), h_output.begin(), 11);\n  exclusive_scan_by_key_kernel<<<1, 1>>>(exec, d_keys.begin(), d_keys.end(), d_vals.begin(), d_keys.begin(), 11);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  ASSERT_EQUAL(d_keys, h_output);\n}\n\nvoid TestScanByKeyDeviceSeq()\n{\n  TestScanByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceSeq);\n\nvoid TestScanByKeyDeviceDevice()\n{\n  TestScanByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestScanByKeyDeviceDevice);\n#endif\n\nvoid TestInclusiveScanByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector keys{0, 1, 1, 1, 2, 3, 3};\n  Vector vals{1, 2, 3, 4, 5, 6, 7};\n\n  Vector output(7, 0);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator iter =\n    thrust::inclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  Vector ref{1, 2, 5, 9, 5, 6, 13};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::inclusive_scan_by_key(\n    thrust::cuda::par.on(s),\n    keys.begin(),\n    keys.end(),\n    vals.begin(),\n    output.begin(),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::multiplies<T>());\n  hipStreamSynchronize(s);\n\n  ref = {1, 2, 6, 24, 5, 6, 42};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::inclusive_scan_by_key(\n    thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), ::cuda::std::equal_to<T>());\n  hipStreamSynchronize(s);\n\n  ref = {1, 2, 5, 9, 5, 6, 13};\n  ASSERT_EQUAL(output, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestInclusiveScanByKeyCudaStreams);\n\nvoid TestExclusiveScanByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector keys{0, 1, 1, 1, 2, 3, 3};\n  Vector vals{1, 2, 3, 4, 5, 6, 7};\n\n  Vector output(7, 0);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator iter =\n    thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(iter, output.end());\n\n  Vector ref{0, 0, 2, 5, 0, 0, 6};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10));\n  hipStreamSynchronize(s);\n\n  ref = {10, 10, 12, 15, 10, 10, 16};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(\n    thrust::cuda::par.on(s),\n    keys.begin(),\n    keys.end(),\n    vals.begin(),\n    output.begin(),\n    T(10),\n    ::cuda::std::equal_to<T>(),\n    ::cuda::std::multiplies<T>());\n  hipStreamSynchronize(s);\n\n  ref = {10, 10, 20, 60, 10, 10, 60};\n  ASSERT_EQUAL(output, ref);\n\n  thrust::exclusive_scan_by_key(\n    thrust::cuda::par.on(s), keys.begin(), keys.end(), vals.begin(), output.begin(), T(10), ::cuda::std::equal_to<T>());\n  hipStreamSynchronize(s);\n\n  ref = {10, 10, 12, 15, 10, 10, 16};\n  ASSERT_EQUAL(output, ref);\n}\nDECLARE_UNITTEST(TestExclusiveScanByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/scan_by_key.cu"}
{"input": "#include <thrust/universal_vector.h>\n\n#include <unittest/unittest.h>\n\ntemplate <class VecInT, class VecOutT>\n_CCCL_HOST_DEVICE void universal_vector_access(VecInT& in, VecOutT& out)\n{\n  const int expected_front = 4;\n  const int expected_back  = 2;\n\n  out[0] = in.size() == 2 && //\n           in[0] == expected_front && //\n           in.front() == expected_front && //\n         *in.data() == expected_front && //\n           in[1] == expected_back && //\n           in.back() == expected_back;\n}\n\n#if defined(THRUST_TEST_DEVICE_SIDE)\ntemplate <class VecInT, class VecOutT>\n__global__ void universal_vector_device_access_kernel(VecInT& vec, VecOutT& out)\n{\n  universal_vector_access(vec, out);\n}\n\ntemplate <class VecInT, class VecOutT>\nvoid test_universal_vector_access(VecInT& vec, VecOutT& out)\n{\n  universal_vector_device_access_kernel<<<1, 1>>>(vec, out);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n  ASSERT_EQUAL(out[0], true);\n}\n#else\ntemplate <class VecInT, class VecOutT>\nvoid test_universal_vector_access(VecInT& vec, VecOutT& out)\n{\n  universal_vector_access(vec, out);\n  ASSERT_EQUAL(out[0], true);\n}\n#endif\n\ntemplate <typename UniversalIntVector, typename UniversalBoolVector>\nvoid TestDeviceAccess()\n{\n  using in_vector_t  = UniversalIntVector;\n  using out_vector_t = UniversalBoolVector;\n\n  in_vector_t* in_ptr{};\n  cudaMallocManaged(&in_ptr, sizeof(*in_ptr));\n  new (in_ptr) in_vector_t(1);\n\n  auto& in = *in_ptr;\n  in.resize(2);\n  in = {4, 2};\n\n  out_vector_t* out_ptr{};\n  cudaMallocManaged(&out_ptr, sizeof(*out_ptr));\n  new (out_ptr) out_vector_t(1);\n  auto& out = *out_ptr;\n\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n  const auto& const_in = *in_ptr;\n  test_universal_vector_access(const_in, out);\n\n  cudaFree(in_ptr);\n  cudaFree(out_ptr);\n}\nDECLARE_UNITTEST_WITH_NAME((TestDeviceAccess<thrust::universal_vector<int>, thrust::universal_vector<bool>>),\n                           TestUniversalVectorDeviceAccess);\nDECLARE_UNITTEST_WITH_NAME(\n  (TestDeviceAccess<thrust::universal_host_pinned_vector<int>, thrust::universal_host_pinned_vector<bool>>),\n  TestUniversalHPVectorDeviceAccess);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/universal_vector.h>\n\n#include <unittest/unittest.h>\n\ntemplate <class VecInT, class VecOutT>\n_CCCL_HOST_DEVICE void universal_vector_access(VecInT& in, VecOutT& out)\n{\n  const int expected_front = 4;\n  const int expected_back  = 2;\n\n  out[0] = in.size() == 2 && //\n           in[0] == expected_front && //\n           in.front() == expected_front && //\n         *in.data() == expected_front && //\n           in[1] == expected_back && //\n           in.back() == expected_back;\n}\n\n#if defined(THRUST_TEST_DEVICE_SIDE)\ntemplate <class VecInT, class VecOutT>\n__global__ void universal_vector_device_access_kernel(VecInT& vec, VecOutT& out)\n{\n  universal_vector_access(vec, out);\n}\n\ntemplate <class VecInT, class VecOutT>\nvoid test_universal_vector_access(VecInT& vec, VecOutT& out)\n{\n  universal_vector_device_access_kernel<<<1, 1>>>(vec, out);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n  ASSERT_EQUAL(out[0], true);\n}\n#else\ntemplate <class VecInT, class VecOutT>\nvoid test_universal_vector_access(VecInT& vec, VecOutT& out)\n{\n  universal_vector_access(vec, out);\n  ASSERT_EQUAL(out[0], true);\n}\n#endif\n\ntemplate <typename UniversalIntVector, typename UniversalBoolVector>\nvoid TestDeviceAccess()\n{\n  using in_vector_t  = UniversalIntVector;\n  using out_vector_t = UniversalBoolVector;\n\n  in_vector_t* in_ptr{};\n  hipMallocManaged(&in_ptr, sizeof(*in_ptr));\n  new (in_ptr) in_vector_t(1);\n\n  auto& in = *in_ptr;\n  in.resize(2);\n  in = {4, 2};\n\n  out_vector_t* out_ptr{};\n  hipMallocManaged(&out_ptr, sizeof(*out_ptr));\n  new (out_ptr) out_vector_t(1);\n  auto& out = *out_ptr;\n\n  out.resize(1);\n  out[0] = false;\n\n  test_universal_vector_access(in, out);\n  const auto& const_in = *in_ptr;\n  test_universal_vector_access(const_in, out);\n\n  hipFree(in_ptr);\n  hipFree(out_ptr);\n}\nDECLARE_UNITTEST_WITH_NAME((TestDeviceAccess<thrust::universal_vector<int>, thrust::universal_vector<bool>>),\n                           TestUniversalVectorDeviceAccess);\nDECLARE_UNITTEST_WITH_NAME(\n  (TestDeviceAccess<thrust::universal_host_pinned_vector<int>, thrust::universal_host_pinned_vector<bool>>),\n  TestUniversalHPVectorDeviceAccess);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/device_side_universal_vector.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_union_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_union(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetUnionDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 2, 3, 3, 4};\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_union_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetUnionDeviceSeq()\n{\n  TestSetUnionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceSeq);\n\nvoid TestSetUnionDeviceDevice()\n{\n  TestSetUnionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceDevice);\n#endif\n\nvoid TestSetUnionCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 2, 3, 3, 4};\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end = thrust::set_union(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_union_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_union(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetUnionDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 2, 3, 3, 4};\n  Vector result(5);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_union_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetUnionDeviceSeq()\n{\n  TestSetUnionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceSeq);\n\nvoid TestSetUnionDeviceDevice()\n{\n  TestSetUnionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionDeviceDevice);\n#endif\n\nvoid TestSetUnionCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 2, 3, 3, 4};\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end = thrust::set_union(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_union.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/reverse.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void reverse_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::reverse(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReverseDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::reverse(h_data.begin(), h_data.end());\n\n  reverse_kernel<<<1, 1>>>(exec, raw_pointer_cast(d_data.data()), raw_pointer_cast(d_data.data() + d_data.size()));\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\nvoid TestReverseDeviceSeq()\n{\n  TestReverseDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseDeviceSeq);\n\nvoid TestReverseDeviceDevice()\n{\n  TestReverseDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void reverse_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::reverse_copy(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReverseCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  thrust::reverse_copy(h_data.begin(), h_data.end(), h_result.begin());\n\n  reverse_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n};\n\nvoid TestReverseCopyDeviceSeq()\n{\n  TestReverseCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceSeq);\n\nvoid TestReverseCopyDeviceDevice()\n{\n  TestReverseCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceDevice);\n#endif\n\nvoid TestReverseCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  Vector data{1, 2, 3, 4, 5};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::reverse(thrust::cuda::par.on(s), data.begin(), data.end());\n\n  cudaStreamSynchronize(s);\n\n  Vector ref{5, 4, 3, 2, 1};\n\n  ASSERT_EQUAL(ref, data);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCudaStreams);\n\nvoid TestReverseCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  Vector data{1, 2, 3, 4, 5};\n\n  Vector result(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::reverse_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin());\n\n  cudaStreamSynchronize(s);\n\n  Vector ref{5, 4, 3, 2, 1};\n\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCopyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/reverse.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void reverse_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::reverse(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReverseDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::reverse(h_data.begin(), h_data.end());\n\n  reverse_kernel<<<1, 1>>>(exec, raw_pointer_cast(d_data.data()), raw_pointer_cast(d_data.data() + d_data.size()));\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_data, d_data);\n};\n\nvoid TestReverseDeviceSeq()\n{\n  TestReverseDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseDeviceSeq);\n\nvoid TestReverseDeviceDevice()\n{\n  TestReverseDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void reverse_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::reverse_copy(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReverseCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_result(n);\n  thrust::device_vector<int> d_result(n);\n\n  thrust::reverse_copy(h_data.begin(), h_data.end(), h_result.begin());\n\n  reverse_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result);\n};\n\nvoid TestReverseCopyDeviceSeq()\n{\n  TestReverseCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceSeq);\n\nvoid TestReverseCopyDeviceDevice()\n{\n  TestReverseCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReverseCopyDeviceDevice);\n#endif\n\nvoid TestReverseCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  Vector data{1, 2, 3, 4, 5};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::reverse(thrust::cuda::par.on(s), data.begin(), data.end());\n\n  hipStreamSynchronize(s);\n\n  Vector ref{5, 4, 3, 2, 1};\n\n  ASSERT_EQUAL(ref, data);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCudaStreams);\n\nvoid TestReverseCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  Vector data{1, 2, 3, 4, 5};\n\n  Vector result(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::reverse_copy(thrust::cuda::par.on(s), data.begin(), data.end(), result.begin());\n\n  hipStreamSynchronize(s);\n\n  Vector ref{5, 4, 3, 2, 1};\n\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReverseCopyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/reverse.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/uninitialized_fill.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void uninitialized_fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val)\n{\n  thrust::uninitialized_fill(exec, first, last, val);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedFillDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 4, sub);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  Vector ref{0, sub, sub, sub, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 8;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 3, sub);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {sub, sub, sub, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 9;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 2, v.end(), sub);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {8, 8, sub, sub, 9};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 1;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin(), v.end(), sub);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = Vector(5, sub);\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestUninitializedFillDeviceSeq()\n{\n  TestUninitializedFillDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceSeq);\n\nvoid TestUninitializedFillDeviceDevice()\n{\n  TestUninitializedFillDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceDevice);\n#endif\n\nvoid TestUninitializedFillCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_fill(thrust::cuda::par.on(s), v.begin(), v.end(), sub);\n  cudaStreamSynchronize(s);\n\n  Vector ref(v.size(), sub);\n  ASSERT_EQUAL(v, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename T, typename Iterator2>\n__global__ void uninitialized_fill_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, T val, Iterator2 result)\n{\n  *result = thrust::uninitialized_fill_n(exec, first, n, val);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedFillNDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  thrust::device_vector<Vector::iterator> iter_vec(1);\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 1, 3, sub, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  Vector::iterator iter = iter_vec[0];\n\n  Vector ref{0, sub, sub, sub, 4};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.begin() + 4, iter);\n\n  sub = 8;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 0, 3, sub, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = {sub, sub, sub, 7, 4};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.begin() + 3, iter);\n\n  sub = 9;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 2, 3, sub, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = {8, 8, sub, sub, 9};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n\n  sub = 1;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin(), v.size(), sub, iter_vec.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = Vector(5, sub);\n\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n}\n\nvoid TestUninitializedFillNDeviceSeq()\n{\n  TestUninitializedFillNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceSeq);\n\nvoid TestUninitializedFillNDeviceDevice()\n{\n  TestUninitializedFillNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceDevice);\n#endif\n\nvoid TestUninitializedFillNCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_fill_n(thrust::cuda::par.on(s), v.begin(), v.size(), sub);\n  cudaStreamSynchronize(s);\n\n  Vector ref(5, sub);\n  ASSERT_EQUAL(v, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillNCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/uninitialized_fill.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void uninitialized_fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T val)\n{\n  thrust::uninitialized_fill(exec, first, last, val);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedFillDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 4, sub);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  Vector ref{0, sub, sub, sub, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 8;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 3, sub);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {sub, sub, sub, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 9;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin() + 2, v.end(), sub);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {8, 8, sub, sub, 9};\n  ASSERT_EQUAL(v, ref);\n\n  sub = 1;\n\n  uninitialized_fill_kernel<<<1, 1>>>(exec, v.begin(), v.end(), sub);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = Vector(5, sub);\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestUninitializedFillDeviceSeq()\n{\n  TestUninitializedFillDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceSeq);\n\nvoid TestUninitializedFillDeviceDevice()\n{\n  TestUninitializedFillDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillDeviceDevice);\n#endif\n\nvoid TestUninitializedFillCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_fill(thrust::cuda::par.on(s), v.begin(), v.end(), sub);\n  hipStreamSynchronize(s);\n\n  Vector ref(v.size(), sub);\n  ASSERT_EQUAL(v, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename T, typename Iterator2>\n__global__ void uninitialized_fill_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, T val, Iterator2 result)\n{\n  *result = thrust::uninitialized_fill_n(exec, first, n, val);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedFillNDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  thrust::device_vector<Vector::iterator> iter_vec(1);\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 1, 3, sub, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  Vector::iterator iter = iter_vec[0];\n\n  Vector ref{0, sub, sub, sub, 4};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.begin() + 4, iter);\n\n  sub = 8;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 0, 3, sub, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = {sub, sub, sub, 7, 4};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.begin() + 3, iter);\n\n  sub = 9;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin() + 2, 3, sub, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = {8, 8, sub, sub, 9};\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n\n  sub = 1;\n\n  uninitialized_fill_n_kernel<<<1, 1>>>(exec, v.begin(), v.size(), sub, iter_vec.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  iter = iter_vec[0];\n\n  ref = Vector(5, sub);\n\n  ASSERT_EQUAL(v, ref);\n  ASSERT_EQUAL_QUIET(v.end(), iter);\n}\n\nvoid TestUninitializedFillNDeviceSeq()\n{\n  TestUninitializedFillNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceSeq);\n\nvoid TestUninitializedFillNDeviceDevice()\n{\n  TestUninitializedFillNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedFillNDeviceDevice);\n#endif\n\nvoid TestUninitializedFillNCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v{0, 1, 2, 3, 4};\n  T sub(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_fill_n(thrust::cuda::par.on(s), v.begin(), v.size(), sub);\n  hipStreamSynchronize(s);\n\n  Vector ref(5, sub);\n  ASSERT_EQUAL(v, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedFillNCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/uninitialized_fill.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/merge.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void merge_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::merge_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMergeByKeyDevice(ExecutionPolicy exec)\n{\n  // clang-format off\n  thrust::device_vector<int> a_key(3), a_val(3), b_key(4), b_val(4);\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  thrust::device_vector<int> ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n  // clang-format on\n\n  thrust::device_vector<int> result_key(7), result_val(7);\n\n  using Iterator = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<cuda::std::pair<Iterator, Iterator>> result_ends(1);\n\n  merge_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    result_ends.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> ends = result_ends[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestMergeByKeyDeviceSeq()\n{\n  TestMergeByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceSeq);\n\nvoid TestMergeByKeyDeviceDevice()\n{\n  TestMergeByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceDevice);\n#endif\n\nvoid TestMergeByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  // clang-format off\n  Vector a_key(3), a_val(3), b_key(4), b_val(4);\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  Vector ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n  // clang-format on\n\n  Vector result_key(7), result_val(7);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> ends = thrust::merge_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/merge.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void merge_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::merge_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMergeByKeyDevice(ExecutionPolicy exec)\n{\n  // clang-format off\n  thrust::device_vector<int> a_key(3), a_val(3), b_key(4), b_val(4);\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  thrust::device_vector<int> ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n  // clang-format on\n\n  thrust::device_vector<int> result_key(7), result_val(7);\n\n  using Iterator = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<cuda::std::pair<Iterator, Iterator>> result_ends(1);\n\n  merge_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    result_ends.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> ends = result_ends[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestMergeByKeyDeviceSeq()\n{\n  TestMergeByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceSeq);\n\nvoid TestMergeByKeyDeviceDevice()\n{\n  TestMergeByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMergeByKeyDeviceDevice);\n#endif\n\nvoid TestMergeByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  // clang-format off\n  Vector a_key(3), a_val(3), b_key(4), b_val(4);\n  a_key[0] = 0;  a_key[1] = 2; a_key[2] = 4;\n  a_val[0] = 13; a_val[1] = 7; a_val[2] = 42;\n\n  b_key[0] = 0 ; b_key[1] = 3;  b_key[2] = 3; b_key[3] = 4;\n  b_val[0] = 42; b_val[1] = 42; b_val[2] = 7; b_val[3] = 13;\n\n  Vector ref_key(7), ref_val(7);\n  ref_key[0] = 0; ref_val[0] = 13;\n  ref_key[1] = 0; ref_val[1] = 42;\n  ref_key[2] = 2; ref_val[2] = 7;\n  ref_key[3] = 3; ref_val[3] = 42;\n  ref_key[4] = 3; ref_val[4] = 7;\n  ref_key[5] = 4; ref_val[5] = 42;\n  ref_key[6] = 4; ref_val[6] = 13;\n  // clang-format on\n\n  Vector result_key(7), result_val(7);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> ends = thrust::merge_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), ends.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), ends.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMergeByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/merge_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_union_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_union_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetUnionByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0), b_val(4, 1);\n\n  Vector ref_key{0, 2, 3, 3, 4}, ref_val{0, 0, 1, 1, 0};\n  Vector result_key(5), result_val(5);\n\n  thrust::device_vector<cuda::std::pair<Iterator, Iterator>> end_vec(1);\n\n  set_union_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetUnionByKeyDeviceSeq()\n{\n  TestSetUnionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceSeq);\n\nvoid TestSetUnionByKeyDeviceDevice()\n{\n  TestSetUnionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceDevice);\n#endif\n\nvoid TestSetUnionByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0), b_val(4, 1);\n\n  Vector ref_key{0, 2, 3, 3, 4}, ref_val{0, 0, 1, 1, 0};\n  Vector result_key(5), result_val(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_union_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6,\n          typename Iterator7>\n__global__ void set_union_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 values_first2,\n  Iterator5 keys_result,\n  Iterator6 values_result,\n  Iterator7 result)\n{\n  *result = thrust::set_union_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, values_first2, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetUnionByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0), b_val(4, 1);\n\n  Vector ref_key{0, 2, 3, 3, 4}, ref_val{0, 0, 1, 1, 0};\n  Vector result_key(5), result_val(5);\n\n  thrust::device_vector<cuda::std::pair<Iterator, Iterator>> end_vec(1);\n\n  set_union_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> end = end_vec[0];\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetUnionByKeyDeviceSeq()\n{\n  TestSetUnionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceSeq);\n\nvoid TestSetUnionByKeyDeviceDevice()\n{\n  TestSetUnionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyDeviceDevice);\n#endif\n\nvoid TestSetUnionByKeyCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0), b_val(4, 1);\n\n  Vector ref_key{0, 2, 3, 3, 4}, ref_val{0, 0, 1, 1, 0};\n  Vector result_key(5), result_val(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_union_by_key(\n    thrust::cuda::par.on(s),\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    b_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetUnionByKeyCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_union_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__ void\nmin_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMinElementDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> d_result(1);\n\n  typename thrust::host_vector<int>::iterator h_min = thrust::min_element(h_data.begin(), h_data.end());\n\n  min_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator h_max =\n    thrust::min_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>());\n\n  min_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n}\n\nvoid TestMinElementDeviceSeq()\n{\n  TestMinElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinElementDeviceSeq);\n\nvoid TestMinElementDeviceDevice()\n{\n  TestMinElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinElementDeviceDevice);\n#endif\n\nvoid TestMinElementCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(*thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()), 1);\n  ASSERT_EQUAL(thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()) - data.begin(), 2);\n\n  ASSERT_EQUAL(*thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::greater<T>()), 5);\n  ASSERT_EQUAL(\n    thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::greater<T>()) - data.begin(),\n    1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinElementCudaStreams);\n\nvoid TestMinElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::min_element(thrust::device, raw_ptr, raw_ptr + n) - raw_ptr, 2);\n  ASSERT_EQUAL(thrust::min_element(thrust::device, raw_ptr, raw_ptr + n, ::cuda::std::greater<T>()) - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinElementDevicePointer);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/extrema.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void min_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename BinaryPredicate, typename Iterator2>\n__global__ void\nmin_element_kernel(ExecutionPolicy exec, Iterator first, Iterator last, BinaryPredicate pred, Iterator2 result)\n{\n  *result = thrust::min_element(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestMinElementDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n\n  thrust::device_vector<iter_type> d_result(1);\n\n  typename thrust::host_vector<int>::iterator h_min = thrust::min_element(h_data.begin(), h_data.end());\n\n  min_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_min - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  typename thrust::host_vector<int>::iterator h_max =\n    thrust::min_element(h_data.begin(), h_data.end(), ::cuda::std::greater<int>());\n\n  min_element_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), ::cuda::std::greater<int>(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_max - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n}\n\nvoid TestMinElementDeviceSeq()\n{\n  TestMinElementDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestMinElementDeviceSeq);\n\nvoid TestMinElementDeviceDevice()\n{\n  TestMinElementDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestMinElementDeviceDevice);\n#endif\n\nvoid TestMinElementCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(*thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()), 1);\n  ASSERT_EQUAL(thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end()) - data.begin(), 2);\n\n  ASSERT_EQUAL(*thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::greater<T>()), 5);\n  ASSERT_EQUAL(\n    thrust::min_element(thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::greater<T>()) - data.begin(),\n    1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestMinElementCudaStreams);\n\nvoid TestMinElementDevicePointer()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data(6);\n  data[0] = 3;\n  data[1] = 5;\n  data[2] = 1;\n  data[3] = 2;\n  data[4] = 5;\n  data[5] = 1;\n\n  T* raw_ptr = thrust::raw_pointer_cast(data.data());\n  size_t n   = data.size();\n  ASSERT_EQUAL(thrust::min_element(thrust::device, raw_ptr, raw_ptr + n) - raw_ptr, 2);\n  ASSERT_EQUAL(thrust::min_element(thrust::device, raw_ptr, raw_ptr + n, ::cuda::std::greater<T>()) - raw_ptr, 1);\n}\nDECLARE_UNITTEST(TestMinElementDevicePointer);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/min_element.cu"}
{"input": "#include <thrust/extrema.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_intersection_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_intersection(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 4};\n  Vector result(2);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_intersection_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetIntersectionDeviceSeq()\n{\n  TestSetIntersectionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceSeq);\n\nvoid TestSetIntersectionDeviceDevice()\n{\n  TestSetIntersectionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceDevice);\n\nvoid TestSetIntersectionDeviceNoSync()\n{\n  TestSetIntersectionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 4};\n  Vector result(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator end = thrust::set_intersection(streampolicy, a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestSetIntersectionCudaStreamsSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsSync);\n\nvoid TestSetIntersectionCudaStreamsNoSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsNoSync);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/extrema.h>\n#include <thrust/functional.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/set_operations.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_intersection_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_intersection(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 4};\n  Vector result(2);\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_intersection_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetIntersectionDeviceSeq()\n{\n  TestSetIntersectionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceSeq);\n\nvoid TestSetIntersectionDeviceDevice()\n{\n  TestSetIntersectionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceDevice);\n\nvoid TestSetIntersectionDeviceNoSync()\n{\n  TestSetIntersectionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4}, b{0, 3, 3, 4};\n\n  Vector ref{0, 4};\n  Vector result(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator end = thrust::set_intersection(streampolicy, a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestSetIntersectionCudaStreamsSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsSync);\n\nvoid TestSetIntersectionCudaStreamsNoSync()\n{\n  TestSetIntersectionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionCudaStreamsNoSync);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_intersection.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6>\n__global__ void set_intersection_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 keys_result,\n  Iterator5 values_result,\n  Iterator6 result)\n{\n  *result = thrust::set_intersection_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0);\n\n  Vector ref_key{0, 4}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_intersection_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetIntersectionByKeyDeviceSeq()\n{\n  TestSetIntersectionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceSeq);\n\nvoid TestSetIntersectionByKeyDeviceDevice()\n{\n  TestSetIntersectionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceDevice);\n\nvoid TestSetIntersectionByKeyDeviceNoSync()\n{\n  TestSetIntersectionByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0);\n\n  Vector ref_key{0, 4}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_intersection_by_key(\n    streampolicy,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestSetIntersectionByKeyCudaStreamsSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsSync);\n\nvoid TestSetIntersectionByKeyCudaStreamsNoSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsNoSync);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Iterator5,\n          typename Iterator6>\n__global__ void set_intersection_by_key_kernel(\n  ExecutionPolicy exec,\n  Iterator1 keys_first1,\n  Iterator1 keys_last1,\n  Iterator2 keys_first2,\n  Iterator2 keys_last2,\n  Iterator3 values_first1,\n  Iterator4 keys_result,\n  Iterator5 values_result,\n  Iterator6 result)\n{\n  *result = thrust::set_intersection_by_key(\n    exec, keys_first1, keys_last1, keys_first2, keys_last2, values_first1, keys_result, values_result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0);\n\n  Vector ref_key{0, 4}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  using iter_pair = cuda::std::pair<Iterator, Iterator>;\n  thrust::device_vector<iter_pair> end_vec(1);\n\n  set_intersection_by_key_kernel<<<1, 1>>>(\n    exec,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    result_key.begin(),\n    result_val.begin(),\n    end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  cuda::std::pair<Iterator, Iterator> end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n}\n\nvoid TestSetIntersectionByKeyDeviceSeq()\n{\n  TestSetIntersectionByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceSeq);\n\nvoid TestSetIntersectionByKeyDeviceDevice()\n{\n  TestSetIntersectionByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceDevice);\n\nvoid TestSetIntersectionByKeyDeviceNoSync()\n{\n  TestSetIntersectionByKeyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetIntersectionByKeyCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a_key{0, 2, 4}, b_key{0, 3, 3, 4};\n  Vector a_val(3, 0);\n\n  Vector ref_key{0, 4}, ref_val{0, 0};\n  Vector result_key(2), result_val(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  cuda::std::pair<Iterator, Iterator> end = thrust::set_intersection_by_key(\n    streampolicy,\n    a_key.begin(),\n    a_key.end(),\n    b_key.begin(),\n    b_key.end(),\n    a_val.begin(),\n    result_key.begin(),\n    result_val.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result_key.end(), end.first);\n  ASSERT_EQUAL_QUIET(result_val.end(), end.second);\n  ASSERT_EQUAL(ref_key, result_key);\n  ASSERT_EQUAL(ref_val, result_val);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestSetIntersectionByKeyCudaStreamsSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsSync);\n\nvoid TestSetIntersectionByKeyCudaStreamsNoSync()\n{\n  TestSetIntersectionByKeyCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestSetIntersectionByKeyCudaStreamsNoSync);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_intersection_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void reduce_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, Iterator2 result)\n{\n  *result = thrust::reduce(exec, first, last, init);\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReduceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<T> d_result(1);\n\n  T init = 13;\n\n  T h_result = thrust::reduce(h_data.begin(), h_data.end(), init);\n\n  reduce_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), init, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nstruct TestReduceDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceSeq, IntegralTypes> TestReduceDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestReduceDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceDevice, IntegralTypes> TestReduceDeviceDeviceInstance;\n\ntemplate <typename T>\nstruct TestReduceDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceNoSync, IntegralTypes> TestReduceDeviceNoSyncInstance;\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v(3);\n  v[0] = 1;\n  v[1] = -2;\n  v[2] = 3;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end()), 2);\n\n  // with initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end(), 10), 12);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestReduceCudaStreamsSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsSync);\n\nvoid TestReduceCudaStreamsNoSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceLargeInput()\n{\n  using T                 = unsigned long long;\n  using OffsetT           = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  cuda::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_kernel<<<1, 1>>>(thrust::device, d_data, d_data + num_items, T{}, d_result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceLargeInput);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/reduce.h>\n\n#include <cuda/iterator>\n\n#include <unittest/unittest.h>\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void reduce_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, Iterator2 result)\n{\n  *result = thrust::reduce(exec, first, last, init);\n}\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReduceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::device_vector<T> d_result(1);\n\n  T init = 13;\n\n  T h_result = thrust::reduce(h_data.begin(), h_data.end(), init);\n\n  reduce_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), init, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_result, d_result[0]);\n}\n\ntemplate <typename T>\nstruct TestReduceDeviceSeq\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::seq, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceSeq, IntegralTypes> TestReduceDeviceSeqInstance;\n\ntemplate <typename T>\nstruct TestReduceDeviceDevice\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::device, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceDevice, IntegralTypes> TestReduceDeviceDeviceInstance;\n\ntemplate <typename T>\nstruct TestReduceDeviceNoSync\n{\n  void operator()(const size_t n)\n  {\n    TestReduceDevice<T>(thrust::cuda::par_nosync, n);\n  }\n};\nVariableUnitTest<TestReduceDeviceNoSync, IntegralTypes> TestReduceDeviceNoSyncInstance;\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestReduceCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v(3);\n  v[0] = 1;\n  v[1] = -2;\n  v[2] = 3;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  // no initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end()), 2);\n\n  // with initializer\n  ASSERT_EQUAL(thrust::reduce(streampolicy, v.begin(), v.end(), 10), 12);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestReduceCudaStreamsSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsSync);\n\nvoid TestReduceCudaStreamsNoSync()\n{\n  TestReduceCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestReduceCudaStreamsNoSync);\n\n#if defined(THRUST_RDC_ENABLED)\nvoid TestReduceLargeInput()\n{\n  using T                 = unsigned long long;\n  using OffsetT           = std::size_t;\n  const OffsetT num_items = 1ull << 32;\n\n  cuda::constant_iterator<T> d_data(T{1});\n  thrust::device_vector<T> d_result(1);\n\n  reduce_kernel<<<1, 1>>>(thrust::device, d_data, d_data + num_items, T{}, d_result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(num_items, d_result[0]);\n}\nDECLARE_UNITTEST(TestReduceLargeInput);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/reduce.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/generate.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct return_value\n{\n  T val;\n\n  return_value() = default;\n  return_value(T v)\n      : val(v)\n  {}\n\n  _CCCL_HOST_DEVICE T operator()()\n  {\n    return val;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void generate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::generate(exec, first, last, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGenerateDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n\n  T value = 13;\n  return_value<T> f(value);\n\n  thrust::generate(h_result.begin(), h_result.end(), f);\n\n  generate_kernel<<<1, 1>>>(exec, d_result.begin(), d_result.end(), f);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nvoid TestGenerateDeviceSeq(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceSeq);\n\ntemplate <typename T>\nvoid TestGenerateDeviceDevice(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceDevice);\n#endif\n\nvoid TestGenerateCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n\n  int value = 13;\n\n  return_value<int> f(value);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::generate(thrust::cuda::par.on(s), result.begin(), result.end(), f);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__ void generate_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::generate_n(exec, first, n, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGenerateNDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n\n  T value = 13;\n  return_value<T> f(value);\n\n  thrust::generate_n(h_result.begin(), h_result.size(), f);\n\n  generate_n_kernel<<<1, 1>>>(exec, d_result.begin(), d_result.size(), f);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nvoid TestGenerateNDeviceSeq(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceSeq);\n\ntemplate <typename T>\nvoid TestGenerateNDeviceDevice(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceDevice);\n#endif\n\nvoid TestGenerateNCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n\n  int value = 13;\n\n  return_value<int> f(value);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::generate_n(thrust::cuda::par.on(s), result.begin(), result.size(), f);\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateNCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/generate.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct return_value\n{\n  T val;\n\n  return_value() = default;\n  return_value(T v)\n      : val(v)\n  {}\n\n  _CCCL_HOST_DEVICE T operator()()\n  {\n    return val;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void generate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::generate(exec, first, last, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGenerateDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n\n  T value = 13;\n  return_value<T> f(value);\n\n  thrust::generate(h_result.begin(), h_result.end(), f);\n\n  generate_kernel<<<1, 1>>>(exec, d_result.begin(), d_result.end(), f);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nvoid TestGenerateDeviceSeq(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceSeq);\n\ntemplate <typename T>\nvoid TestGenerateDeviceDevice(const size_t n)\n{\n  TestGenerateDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateDeviceDevice);\n#endif\n\nvoid TestGenerateCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n\n  int value = 13;\n\n  return_value<int> f(value);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::generate(thrust::cuda::par.on(s), result.begin(), result.end(), f);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__ void generate_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::generate_n(exec, first, n, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGenerateNDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_result(n);\n  thrust::device_vector<T> d_result(n);\n\n  T value = 13;\n  return_value<T> f(value);\n\n  thrust::generate_n(h_result.begin(), h_result.size(), f);\n\n  generate_n_kernel<<<1, 1>>>(exec, d_result.begin(), d_result.size(), f);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_result, d_result);\n}\n\ntemplate <typename T>\nvoid TestGenerateNDeviceSeq(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceSeq);\n\ntemplate <typename T>\nvoid TestGenerateNDeviceDevice(const size_t n)\n{\n  TestGenerateNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGenerateNDeviceDevice);\n#endif\n\nvoid TestGenerateNCudaStreams()\n{\n  thrust::device_vector<int> result(5);\n\n  int value = 13;\n\n  return_value<int> f(value);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::generate_n(thrust::cuda::par.on(s), result.begin(), result.size(), f);\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(result[0], value);\n  ASSERT_EQUAL(result[1], value);\n  ASSERT_EQUAL(result[2], value);\n  ASSERT_EQUAL(result[3], value);\n  ASSERT_EQUAL(result[4], value);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGenerateNCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/generate.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/uninitialized_copy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void uninitialized_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::uninitialized_copy(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedCopyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_kernel<<<1, 1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v2, ref);\n}\n\nvoid TestUninitializedCopyDeviceSeq()\n{\n  TestUninitializedCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceSeq);\n\nvoid TestUninitializedCopyDeviceDevice()\n{\n  TestUninitializedCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceDevice);\n#endif\n\nvoid TestUninitializedCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_copy(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2, v1);\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__ void uninitialized_copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::uninitialized_copy_n(exec, first, n, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedCopyNDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_n_kernel<<<1, 1>>>(exec, v1.begin(), v1.size(), v2.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(v2, v1);\n}\n\nvoid TestUninitializedCopyNDeviceSeq()\n{\n  TestUninitializedCopyNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceSeq);\n\nvoid TestUninitializedCopyNDeviceDevice()\n{\n  TestUninitializedCopyNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceDevice);\n#endif\n\nvoid TestUninitializedCopyNCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::uninitialized_copy_n(thrust::cuda::par.on(s), v1.begin(), v1.size(), v2.begin());\n  cudaStreamSynchronize(s);\n  ASSERT_EQUAL(v2, v1);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/uninitialized_copy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void uninitialized_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::uninitialized_copy(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedCopyDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_kernel<<<1, 1>>>(exec, v1.begin(), v1.end(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v2, ref);\n}\n\nvoid TestUninitializedCopyDeviceSeq()\n{\n  TestUninitializedCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceSeq);\n\nvoid TestUninitializedCopyDeviceDevice()\n{\n  TestUninitializedCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyDeviceDevice);\n#endif\n\nvoid TestUninitializedCopyCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_copy(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(v2, v1);\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Size, typename Iterator2>\n__global__ void uninitialized_copy_n_kernel(ExecutionPolicy exec, Iterator1 first, Size n, Iterator2 result)\n{\n  thrust::uninitialized_copy_n(exec, first, n, result);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestUninitializedCopyNDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n  uninitialized_copy_n_kernel<<<1, 1>>>(exec, v1.begin(), v1.size(), v2.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(v2, v1);\n}\n\nvoid TestUninitializedCopyNDeviceSeq()\n{\n  TestUninitializedCopyNDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceSeq);\n\nvoid TestUninitializedCopyNDeviceDevice()\n{\n  TestUninitializedCopyNDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNDeviceDevice);\n#endif\n\nvoid TestUninitializedCopyNCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v1{0, 1, 2, 3, 4};\n\n  // copy to Vector\n  Vector v2(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::uninitialized_copy_n(thrust::cuda::par.on(s), v1.begin(), v1.size(), v2.begin());\n  hipStreamSynchronize(s);\n  ASSERT_EQUAL(v2, v1);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestUninitializedCopyNCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/uninitialized_copy.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n\n#include <cuda/std/utility>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void\nstable_sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first)\n{\n  thrust::stable_sort_by_key(exec, keys_first, keys_last, values_first);\n}\n\nstruct make_pair_functor\n{\n  template <typename T1, typename T2>\n  _CCCL_HOST_DEVICE cuda::std::pair<T1, T2> operator()(const T1& x, const T2& y)\n  {\n    return cuda::std::make_pair(x, y);\n  } // end operator()()\n}; // end make_pair_functor\n\ntemplate <typename ExecutionPolicy>\nvoid TestPairStableSortByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  using P  = cuda::std::pair<int, int>;\n\n  // host arrays\n  thrust::host_vector<int> h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P> h_pairs(n);\n\n  thrust::host_vector<int> h_values(n);\n  thrust::sequence(h_values.begin(), h_values.end());\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  // device arrays\n  thrust::device_vector<P> d_pairs    = h_pairs;\n  thrust::device_vector<int> d_values = h_values;\n\n  // sort on the device\n  stable_sort_by_key_kernel<<<1, 1>>>(exec, d_pairs.begin(), d_pairs.end(), d_values.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort_by_key(h_pairs.begin(), h_pairs.end(), h_values.begin());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\nvoid TestPairStableSortByKeyDeviceSeq()\n{\n  TestPairStableSortByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceSeq);\n\nvoid TestPairStableSortByKeyDeviceDevice()\n{\n  TestPairStableSortByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceDevice);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n#include <thrust/sort.h>\n#include <thrust/transform.h>\n\n#include <cuda/std/utility>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void\nstable_sort_by_key_kernel(ExecutionPolicy exec, Iterator1 keys_first, Iterator1 keys_last, Iterator2 values_first)\n{\n  thrust::stable_sort_by_key(exec, keys_first, keys_last, values_first);\n}\n\nstruct make_pair_functor\n{\n  template <typename T1, typename T2>\n  _CCCL_HOST_DEVICE cuda::std::pair<T1, T2> operator()(const T1& x, const T2& y)\n  {\n    return cuda::std::make_pair(x, y);\n  } // end operator()()\n}; // end make_pair_functor\n\ntemplate <typename ExecutionPolicy>\nvoid TestPairStableSortByKeyDevice(ExecutionPolicy exec)\n{\n  size_t n = 10000;\n  using P  = cuda::std::pair<int, int>;\n\n  // host arrays\n  thrust::host_vector<int> h_p1 = unittest::random_integers<int>(n);\n  thrust::host_vector<int> h_p2 = unittest::random_integers<int>(n);\n  thrust::host_vector<P> h_pairs(n);\n\n  thrust::host_vector<int> h_values(n);\n  thrust::sequence(h_values.begin(), h_values.end());\n\n  // zip up pairs on the host\n  thrust::transform(h_p1.begin(), h_p1.end(), h_p2.begin(), h_pairs.begin(), make_pair_functor());\n\n  // device arrays\n  thrust::device_vector<P> d_pairs    = h_pairs;\n  thrust::device_vector<int> d_values = h_values;\n\n  // sort on the device\n  stable_sort_by_key_kernel<<<1, 1>>>(exec, d_pairs.begin(), d_pairs.end(), d_values.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  // sort on the host\n  thrust::stable_sort_by_key(h_pairs.begin(), h_pairs.end(), h_values.begin());\n\n  ASSERT_EQUAL_QUIET(h_pairs, d_pairs);\n  ASSERT_EQUAL(h_values, d_values);\n};\n\nvoid TestPairStableSortByKeyDeviceSeq()\n{\n  TestPairStableSortByKeyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceSeq);\n\nvoid TestPairStableSortByKeyDeviceDevice()\n{\n  TestPairStableSortByKeyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPairStableSortByKeyDeviceDevice);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/pair_sort_by_key.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/transform_reduce.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Function1, typename T, typename Function2, typename Iterator2>\n__global__ void transform_reduce_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Function1 f1, T init, Function2 f2, Iterator2 result)\n{\n  *result = thrust::transform_reduce(exec, first, last, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformReduceDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  Vector data{1, -2, 3};\n  T init = 10;\n\n  thrust::device_vector<T> result(1);\n\n  transform_reduce_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), ::cuda::std::negate<T>(), init, ::cuda::std::plus<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(8, (T) result[0]);\n}\n\nvoid TestTransformReduceDeviceSeq()\n{\n  TestTransformReduceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceSeq);\n\nvoid TestTransformReduceDeviceDevice()\n{\n  TestTransformReduceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceDevice);\n#endif\n\nvoid TestTransformReduceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, -2, 3};\n  T init = 10;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  T result = thrust::transform_reduce(\n    thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::negate<T>(), init, ::cuda::std::plus<T>());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL(8, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformReduceCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/transform_reduce.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Function1, typename T, typename Function2, typename Iterator2>\n__global__ void transform_reduce_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Function1 f1, T init, Function2 f2, Iterator2 result)\n{\n  *result = thrust::transform_reduce(exec, first, last, f1, init, f2);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTransformReduceDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = typename Vector::value_type;\n\n  Vector data{1, -2, 3};\n  T init = 10;\n\n  thrust::device_vector<T> result(1);\n\n  transform_reduce_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), ::cuda::std::negate<T>(), init, ::cuda::std::plus<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(8, (T) result[0]);\n}\n\nvoid TestTransformReduceDeviceSeq()\n{\n  TestTransformReduceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceSeq);\n\nvoid TestTransformReduceDeviceDevice()\n{\n  TestTransformReduceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTransformReduceDeviceDevice);\n#endif\n\nvoid TestTransformReduceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, -2, 3};\n  T init = 10;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  T result = thrust::transform_reduce(\n    thrust::cuda::par.on(s), data.begin(), data.end(), ::cuda::std::negate<T>(), init, ::cuda::std::plus<T>());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL(8, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestTransformReduceCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/transform_reduce.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_difference_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_difference(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetDifferenceDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4, 5}, b{0, 3, 3, 4, 6};\n\n  Vector ref{2, 5};\n  Vector result(2);\n\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_difference_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetDifferenceDeviceSeq()\n{\n  TestSetDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceSeq);\n\nvoid TestSetDifferenceDeviceDevice()\n{\n  TestSetDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceDevice);\n#endif\n\nvoid TestSetDifferenceCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4, 5}, b{0, 3, 3, 4, 6};\n\n  Vector ref{2, 5};\n  Vector result(2);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Iterator end =\n    thrust::set_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  cudaStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/set_operations.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3, typename Iterator4>\n__global__ void set_difference_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first1,\n  Iterator1 last1,\n  Iterator2 first2,\n  Iterator2 last2,\n  Iterator3 result1,\n  Iterator4 result2)\n{\n  *result2 = thrust::set_difference(exec, first1, last1, first2, last2, result1);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSetDifferenceDevice(ExecutionPolicy exec)\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = typename Vector::iterator;\n\n  Vector a{0, 2, 4, 5}, b{0, 3, 3, 4, 6};\n\n  Vector ref{2, 5};\n  Vector result(2);\n\n  thrust::device_vector<Iterator> end_vec(1);\n\n  set_difference_kernel<<<1, 1>>>(exec, a.begin(), a.end(), b.begin(), b.end(), result.begin(), end_vec.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  Iterator end = end_vec.front();\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n}\n\nvoid TestSetDifferenceDeviceSeq()\n{\n  TestSetDifferenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceSeq);\n\nvoid TestSetDifferenceDeviceDevice()\n{\n  TestSetDifferenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSetDifferenceDeviceDevice);\n#endif\n\nvoid TestSetDifferenceCudaStreams()\n{\n  using Vector   = thrust::device_vector<int>;\n  using Iterator = Vector::iterator;\n\n  Vector a{0, 2, 4, 5}, b{0, 3, 3, 4, 6};\n\n  Vector ref{2, 5};\n  Vector result(2);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Iterator end =\n    thrust::set_difference(thrust::cuda::par.on(s), a.begin(), a.end(), b.begin(), b.end(), result.begin());\n  hipStreamSynchronize(s);\n\n  ASSERT_EQUAL_QUIET(result.end(), end);\n  ASSERT_EQUAL(ref, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSetDifferenceCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/set_difference.cu"}
{"input": "#include <thrust/adjacent_difference.h>\n#include <thrust/device_free.h>\n#include <thrust/device_malloc.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::adjacent_difference(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryFunction>\n__global__ void\nadjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, BinaryFunction f)\n{\n  thrust::adjacent_difference(exec, first, last, result, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestAdjacentDifferenceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_input   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin(), ::cuda::std::plus<T>());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), ::cuda::std::plus<T>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  // in-place operation\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_input.begin(), ::cuda::std::plus<T>());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_input.begin(), ::cuda::std::plus<T>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_input, h_output); // computed previously\n  ASSERT_EQUAL(d_input, d_output); // computed previously\n}\n\ntemplate <typename T>\nvoid TestAdjacentDifferenceDeviceSeq(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceSeq);\n\ntemplate <typename T>\nvoid TestAdjacentDifferenceDeviceDevice(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceDevice);\n#endif\n\nvoid TestAdjacentDifferenceCudaStreams()\n{\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::device_vector<int> input{1, 4, 6};\n  thrust::device_vector<int> output(input.size());\n\n  thrust::adjacent_difference(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  cudaStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{1, 3, 2};\n\n  ASSERT_EQUAL(output, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceCudaStreams);\n\nstruct detect_wrong_difference\n{\n  using difference_type   = void;\n  using value_type        = long long;\n  using pointer           = void;\n  using reference         = detect_wrong_difference;\n  using iterator_category = ::cuda::std::output_iterator_tag;\n\n  bool* flag;\n\n  _CCCL_HOST_DEVICE detect_wrong_difference operator++() const\n  {\n    return *this;\n  }\n  _CCCL_HOST_DEVICE detect_wrong_difference operator*() const\n  {\n    return *this;\n  }\n  template <typename Difference>\n  _CCCL_HOST_DEVICE detect_wrong_difference operator+(Difference) const\n  {\n    return *this;\n  }\n  template <typename Index>\n  _CCCL_HOST_DEVICE detect_wrong_difference operator[](Index) const\n  {\n    return *this;\n  }\n\n  _CCCL_DEVICE void operator=(long long difference) const\n  {\n    if (difference != 1)\n    {\n      *flag = false;\n    }\n  }\n};\n\nvoid TestAdjacentDifferenceWithBigIndexesHelper(int magnitude)\n{\n  thrust::counting_iterator<long long> begin(1);\n  thrust::counting_iterator<long long> end = begin + (1ll << magnitude);\n  ASSERT_EQUAL(::cuda::std::distance(begin, end), 1ll << magnitude);\n\n  thrust::device_ptr<bool> all_differences_correct = thrust::device_malloc<bool>(1);\n  *all_differences_correct                         = true;\n\n  detect_wrong_difference out = {thrust::raw_pointer_cast(all_differences_correct)};\n\n  thrust::adjacent_difference(thrust::device, begin, end, out);\n\n  bool all_differences_correct_h = *all_differences_correct;\n  thrust::device_free(all_differences_correct);\n\n  ASSERT_EQUAL(all_differences_correct_h, true);\n}\n\nvoid TestAdjacentDifferenceWithBigIndexes()\n{\n  TestAdjacentDifferenceWithBigIndexesHelper(30);\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestAdjacentDifferenceWithBigIndexesHelper(31);\n  TestAdjacentDifferenceWithBigIndexesHelper(32);\n  TestAdjacentDifferenceWithBigIndexesHelper(33);\n#endif\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceWithBigIndexes);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/adjacent_difference.h>\n#include <thrust/device_free.h>\n#include <thrust/device_malloc.h>\n#include <thrust/execution_policy.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2>\n__global__ void adjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result)\n{\n  thrust::adjacent_difference(exec, first, last, result);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryFunction>\n__global__ void\nadjacent_difference_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, BinaryFunction f)\n{\n  thrust::adjacent_difference(exec, first, last, result, f);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestAdjacentDifferenceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_input   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_output.begin(), ::cuda::std::plus<T>());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_output.begin(), ::cuda::std::plus<T>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n\n  // in-place operation\n  thrust::adjacent_difference(h_input.begin(), h_input.end(), h_input.begin(), ::cuda::std::plus<T>());\n  adjacent_difference_kernel<<<1, 1>>>(exec, d_input.begin(), d_input.end(), d_input.begin(), ::cuda::std::plus<T>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_input, h_output); // computed previously\n  ASSERT_EQUAL(d_input, d_output); // computed previously\n}\n\ntemplate <typename T>\nvoid TestAdjacentDifferenceDeviceSeq(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceSeq);\n\ntemplate <typename T>\nvoid TestAdjacentDifferenceDeviceDevice(const size_t n)\n{\n  TestAdjacentDifferenceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestAdjacentDifferenceDeviceDevice);\n#endif\n\nvoid TestAdjacentDifferenceCudaStreams()\n{\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::device_vector<int> input{1, 4, 6};\n  thrust::device_vector<int> output(input.size());\n\n  thrust::adjacent_difference(thrust::cuda::par.on(s), input.begin(), input.end(), output.begin());\n\n  hipStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{1, 3, 2};\n\n  ASSERT_EQUAL(output, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceCudaStreams);\n\nstruct detect_wrong_difference\n{\n  using difference_type   = void;\n  using value_type        = long long;\n  using pointer           = void;\n  using reference         = detect_wrong_difference;\n  using iterator_category = ::cuda::std::output_iterator_tag;\n\n  bool* flag;\n\n  _CCCL_HOST_DEVICE detect_wrong_difference operator++() const\n  {\n    return *this;\n  }\n  _CCCL_HOST_DEVICE detect_wrong_difference operator*() const\n  {\n    return *this;\n  }\n  template <typename Difference>\n  _CCCL_HOST_DEVICE detect_wrong_difference operator+(Difference) const\n  {\n    return *this;\n  }\n  template <typename Index>\n  _CCCL_HOST_DEVICE detect_wrong_difference operator[](Index) const\n  {\n    return *this;\n  }\n\n  _CCCL_DEVICE void operator=(long long difference) const\n  {\n    if (difference != 1)\n    {\n      *flag = false;\n    }\n  }\n};\n\nvoid TestAdjacentDifferenceWithBigIndexesHelper(int magnitude)\n{\n  thrust::counting_iterator<long long> begin(1);\n  thrust::counting_iterator<long long> end = begin + (1ll << magnitude);\n  ASSERT_EQUAL(::cuda::std::distance(begin, end), 1ll << magnitude);\n\n  thrust::device_ptr<bool> all_differences_correct = thrust::device_malloc<bool>(1);\n  *all_differences_correct                         = true;\n\n  detect_wrong_difference out = {thrust::raw_pointer_cast(all_differences_correct)};\n\n  thrust::adjacent_difference(thrust::device, begin, end, out);\n\n  bool all_differences_correct_h = *all_differences_correct;\n  thrust::device_free(all_differences_correct);\n\n  ASSERT_EQUAL(all_differences_correct_h, true);\n}\n\nvoid TestAdjacentDifferenceWithBigIndexes()\n{\n  TestAdjacentDifferenceWithBigIndexesHelper(30);\n#ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestAdjacentDifferenceWithBigIndexesHelper(31);\n  TestAdjacentDifferenceWithBigIndexesHelper(32);\n  TestAdjacentDifferenceWithBigIndexesHelper(33);\n#endif\n}\nDECLARE_UNITTEST(TestAdjacentDifferenceWithBigIndexes);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/adjacent_difference.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/replace.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct less_than_five\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& val) const\n  {\n    return val < 5;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T1, typename T2>\n__global__ void replace_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T1 old_value, T2 new_value)\n{\n  thrust::replace(exec, first, last, old_value, new_value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReplaceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  T old_value = 0;\n  T new_value = 1;\n\n  thrust::replace(h_data.begin(), h_data.end(), old_value, new_value);\n\n  replace_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), old_value, new_value);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestReplaceDeviceSeq(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceSeq);\n\ntemplate <typename T>\nvoid TestReplaceDeviceDevice(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T1, typename T2>\n__global__ void\nreplace_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T1 old_value, T2 new_value)\n{\n  thrust::replace_copy(exec, first, last, result, old_value, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  int old_value = 0;\n  int new_value = 1;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy(h_data.begin(), h_data.end(), h_dest.begin(), old_value, new_value);\n\n  replace_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), old_value, new_value);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyDeviceSeq()\n{\n  TestReplaceCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceSeq);\n\nvoid TestReplaceCopyDeviceDevice()\n{\n  TestReplaceCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename T>\n__global__ void replace_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::replace_if(h_data.begin(), h_data.end(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfDeviceSeq()\n{\n  TestReplaceIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceSeq);\n\nvoid TestReplaceIfDeviceDevice()\n{\n  TestReplaceIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__ void replace_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, stencil_first, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_stencil   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n\n  thrust::replace_if(h_data.begin(), h_data.end(), h_stencil.begin(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfStencilDeviceSeq()\n{\n  TestReplaceIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceSeq);\n\nvoid TestReplaceIfStencilDeviceDevice()\n{\n  TestReplaceIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__ void replace_copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, result, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfDeviceSeq()\n{\n  TestReplaceCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceSeq);\n\nvoid TestReplaceCopyIfDeviceDevice()\n{\n  TestReplaceCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename T>\n__global__ void replace_copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result,\n  Predicate pred,\n  T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, stencil_first, result, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_stencil   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_dest.begin(), less_than_five<int>(), 0);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfStencilDeviceSeq()\n{\n  TestReplaceCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceSeq);\n\nvoid TestReplaceCopyIfStencilDeviceDevice()\n{\n  TestReplaceCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceDevice);\n#endif\n\nvoid TestReplaceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 1, (T) 4);\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2, (T) 5);\n\n  cudaStreamSynchronize(s);\n\n  Vector result{4, 5, 4, 3, 5};\n\n  ASSERT_EQUAL(data, result);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReplaceCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/replace.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct less_than_five\n{\n  _CCCL_HOST_DEVICE bool operator()(const T& val) const\n  {\n    return val < 5;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T1, typename T2>\n__global__ void replace_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T1 old_value, T2 new_value)\n{\n  thrust::replace(exec, first, last, old_value, new_value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestReplaceDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  T old_value = 0;\n  T new_value = 1;\n\n  thrust::replace(h_data.begin(), h_data.end(), old_value, new_value);\n\n  replace_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), old_value, new_value);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestReplaceDeviceSeq(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceSeq);\n\ntemplate <typename T>\nvoid TestReplaceDeviceDevice(const size_t n)\n{\n  TestReplaceDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestReplaceDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T1, typename T2>\n__global__ void\nreplace_copy_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, T1 old_value, T2 new_value)\n{\n  thrust::replace_copy(exec, first, last, result, old_value, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  int old_value = 0;\n  int new_value = 1;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy(h_data.begin(), h_data.end(), h_dest.begin(), old_value, new_value);\n\n  replace_copy_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), old_value, new_value);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyDeviceSeq()\n{\n  TestReplaceCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceSeq);\n\nvoid TestReplaceCopyDeviceDevice()\n{\n  TestReplaceCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename T>\n__global__ void replace_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::replace_if(h_data.begin(), h_data.end(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfDeviceSeq()\n{\n  TestReplaceIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceSeq);\n\nvoid TestReplaceIfDeviceDevice()\n{\n  TestReplaceIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__ void replace_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, T new_value)\n{\n  thrust::replace_if(exec, first, last, stencil_first, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_stencil   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n\n  thrust::replace_if(h_data.begin(), h_data.end(), h_stencil.begin(), less_than_five<int>(), 0);\n\n  replace_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_stencil.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n}\n\nvoid TestReplaceIfStencilDeviceSeq()\n{\n  TestReplaceIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceSeq);\n\nvoid TestReplaceIfStencilDeviceDevice()\n{\n  TestReplaceIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceIfStencilDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename T>\n__global__ void replace_copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result, Predicate pred, T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, result, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_dest.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfDeviceSeq()\n{\n  TestReplaceCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceSeq);\n\nvoid TestReplaceCopyIfDeviceDevice()\n{\n  TestReplaceCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename T>\n__global__ void replace_copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result,\n  Predicate pred,\n  T new_value)\n{\n  thrust::replace_copy_if(exec, first, last, stencil_first, result, pred, new_value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestReplaceCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  thrust::host_vector<int> h_stencil   = unittest::random_samples<int>(n);\n  thrust::device_vector<int> d_stencil = h_stencil;\n\n  thrust::host_vector<int> h_dest(n);\n  thrust::device_vector<int> d_dest(n);\n\n  thrust::replace_copy_if(h_data.begin(), h_data.end(), h_stencil.begin(), h_dest.begin(), less_than_five<int>(), 0);\n\n  replace_copy_if_kernel<<<1, 1>>>(\n    exec, d_data.begin(), d_data.end(), d_stencil.begin(), d_dest.begin(), less_than_five<int>(), 0);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_ALMOST_EQUAL(h_data, d_data);\n  ASSERT_ALMOST_EQUAL(h_dest, d_dest);\n}\n\nvoid TestReplaceCopyIfStencilDeviceSeq()\n{\n  TestReplaceCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceSeq);\n\nvoid TestReplaceCopyIfStencilDeviceDevice()\n{\n  TestReplaceCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestReplaceCopyIfStencilDeviceDevice);\n#endif\n\nvoid TestReplaceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 1, (T) 4);\n  thrust::replace(thrust::cuda::par.on(s), data.begin(), data.end(), (T) 2, (T) 5);\n\n  hipStreamSynchronize(s);\n\n  Vector result{4, 5, 4, 3, 5};\n\n  ASSERT_EQUAL(data, result);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestReplaceCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/replace.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/tabulate.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void tabulate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::tabulate(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTabulateDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using namespace thrust::placeholders;\n  using T = typename Vector::value_type;\n\n  Vector v(5);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{});\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), -_1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {0, -1, -2, -3, -4};\n  ASSERT_EQUAL(v, ref);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), _1 * _1 * _1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {0, 1, 8, 27, 64};\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestTabulateDeviceSeq()\n{\n  TestTabulateDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTabulateDeviceSeq);\n\nvoid TestTabulateDeviceDevice()\n{\n  TestTabulateDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTabulateDeviceDevice);\n#endif\n\nvoid TestTabulateCudaStreams()\n{\n  using namespace thrust::placeholders;\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{});\n  cudaStreamSynchronize(s);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), -_1);\n  cudaStreamSynchronize(s);\n\n  ref = {0, -1, -2, -3, -4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), _1 * _1 * _1);\n  cudaStreamSynchronize(s);\n\n  ref = {0, 1, 8, 27, 64};\n  ASSERT_EQUAL(v, ref);\n\n  cudaStreamSynchronize(s);\n}\nDECLARE_UNITTEST(TestTabulateCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/tabulate.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void tabulate_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::tabulate(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestTabulateDevice(ExecutionPolicy exec)\n{\n  using Vector = thrust::device_vector<int>;\n  using namespace thrust::placeholders;\n  using T = typename Vector::value_type;\n\n  Vector v(5);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{});\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), -_1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {0, -1, -2, -3, -4};\n  ASSERT_EQUAL(v, ref);\n\n  tabulate_kernel<<<1, 1>>>(exec, v.begin(), v.end(), _1 * _1 * _1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {0, 1, 8, 27, 64};\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestTabulateDeviceSeq()\n{\n  TestTabulateDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestTabulateDeviceSeq);\n\nvoid TestTabulateDeviceDevice()\n{\n  TestTabulateDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestTabulateDeviceDevice);\n#endif\n\nvoid TestTabulateCudaStreams()\n{\n  using namespace thrust::placeholders;\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{});\n  hipStreamSynchronize(s);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), -_1);\n  hipStreamSynchronize(s);\n\n  ref = {0, -1, -2, -3, -4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::tabulate(thrust::cuda::par.on(s), v.begin(), v.end(), _1 * _1 * _1);\n  hipStreamSynchronize(s);\n\n  ref = {0, 1, 8, 27, 64};\n  ASSERT_EQUAL(v, ref);\n\n  hipStreamSynchronize(s);\n}\nDECLARE_UNITTEST(TestTabulateCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/tabulate.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/find.h>\n#include <thrust/functional.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void find_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::find(exec, first, last, value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  h_iter = thrust::find(h_data.begin(), h_data.end(), int(0));\n\n  find_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), int(0), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find(h_data.begin(), h_data.end(), sample);\n\n    find_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), sample, d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindDeviceSeq()\n{\n  TestFindDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindDeviceSeq);\n\nvoid TestFindDeviceDevice()\n{\n  TestFindDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  using thrust::placeholders::_1;\n  h_iter = thrust::find_if(h_data.begin(), h_data.end(), _1 == 0);\n\n  find_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 == 0, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if(h_data.begin(), h_data.end(), _1 == sample);\n\n    find_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 == sample, d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindIfDeviceSeq()\n{\n  TestFindIfDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfDeviceSeq);\n\nvoid TestFindIfDeviceDevice()\n{\n  TestFindIfDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_not_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if_not(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindIfNotDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 100;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  using thrust::placeholders::_1;\n  h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), _1 != 0);\n\n  find_if_not_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 != 0, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), _1 != sample);\n\n    find_if_not_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 != sample, d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindIfNotDeviceSeq()\n{\n  TestFindIfNotDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceSeq);\n\nvoid TestFindIfNotDeviceDevice()\n{\n  TestFindIfNotDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceDevice);\n#endif\n\nvoid TestFindCudaStreams()\n{\n  thrust::device_vector<int> vec{1, 2, 3, 3, 5};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 0) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 1) - vec.begin(), 0);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 2) - vec.begin(), 1);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 3) - vec.begin(), 2);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 4) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 5) - vec.begin(), 4);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFindCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/find.h>\n#include <thrust/functional.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T, typename Iterator2>\n__global__ void find_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value, Iterator2 result)\n{\n  *result = thrust::find(exec, first, last, value);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  h_iter = thrust::find(h_data.begin(), h_data.end(), int(0));\n\n  find_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), int(0), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find(h_data.begin(), h_data.end(), sample);\n\n    find_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), sample, d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindDeviceSeq()\n{\n  TestFindDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindDeviceSeq);\n\nvoid TestFindDeviceDevice()\n{\n  TestFindDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindIfDevice(ExecutionPolicy exec)\n{\n  size_t n = 100;\n\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  using thrust::placeholders::_1;\n  h_iter = thrust::find_if(h_data.begin(), h_data.end(), _1 == 0);\n\n  find_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 == 0, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if(h_data.begin(), h_data.end(), _1 == sample);\n\n    find_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 == sample, d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindIfDeviceSeq()\n{\n  TestFindIfDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfDeviceSeq);\n\nvoid TestFindIfDeviceDevice()\n{\n  TestFindIfDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void find_if_not_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::find_if_not(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestFindIfNotDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 100;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_iter;\n\n  using iter_type = typename thrust::device_vector<int>::iterator;\n  thrust::device_vector<iter_type> d_result(1);\n\n  using thrust::placeholders::_1;\n  h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), _1 != 0);\n\n  find_if_not_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 != 0, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n\n  for (size_t i = 1; i < n; i *= 2)\n  {\n    int sample = h_data[i];\n\n    h_iter = thrust::find_if_not(h_data.begin(), h_data.end(), _1 != sample);\n\n    find_if_not_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), _1 != sample, d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(h_iter - h_data.begin(), (iter_type) d_result[0] - d_data.begin());\n  }\n}\n\nvoid TestFindIfNotDeviceSeq()\n{\n  TestFindIfNotDevice(thrust::seq);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceSeq);\n\nvoid TestFindIfNotDeviceDevice()\n{\n  TestFindIfNotDevice(thrust::device);\n};\nDECLARE_UNITTEST(TestFindIfNotDeviceDevice);\n#endif\n\nvoid TestFindCudaStreams()\n{\n  thrust::device_vector<int> vec{1, 2, 3, 3, 5};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 0) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 1) - vec.begin(), 0);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 2) - vec.begin(), 1);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 3) - vec.begin(), 2);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 4) - vec.begin(), 5);\n  ASSERT_EQUAL(thrust::find(thrust::cuda::par.on(s), vec.begin(), vec.end(), 5) - vec.begin(), 4);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFindCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/find.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/for_each.h>\n\n#include <algorithm>\n\n#include \"thrust/device_vector.h\"\n#include <unittest/unittest.h>\n\nstatic const size_t NUM_REGISTERS = 64;\n\ntemplate <size_t N>\n_CCCL_HOST_DEVICE void f(int* x)\n{\n  int temp = *x;\n  f<N - 1>(x + 1);\n  *x = temp;\n};\ntemplate <>\n_CCCL_HOST_DEVICE void f<0>(int* /*x*/)\n{}\ntemplate <size_t N>\nstruct CopyFunctorWithManyRegisters\n{\n  _CCCL_HOST_DEVICE void operator()(int* ptr)\n  {\n    f<N>(ptr);\n  }\n};\n\nvoid TestForEachLargeRegisterFootprint()\n{\n  int current_device = -1;\n  cudaGetDevice(&current_device);\n  cudaDeviceProp prop;\n  cudaGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int*> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n\n  thrust::for_each(input.begin(), input.end(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachLargeRegisterFootprint);\n\nvoid TestForEachNLargeRegisterFootprint()\n{\n  int current_device = -1;\n  cudaGetDevice(&current_device);\n  cudaDeviceProp prop;\n  cudaGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int*> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n\n  thrust::for_each_n(input.begin(), input.size(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachNLargeRegisterFootprint);\n\ntemplate <typename T>\nstruct mark_present_for_each\n{\n  T* ptr;\n  _CCCL_HOST_DEVICE void operator()(T x)\n  {\n    ptr[(int) x] = 1;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void for_each_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::for_each(exec, first, last, f);\n}\n\ntemplate <typename T>\nvoid TestForEachDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n\n  for_each_kernel<<<1, 1>>>(thrust::seq, d_input.begin(), d_input.end(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceSeq);\n\ntemplate <typename T>\nvoid TestForEachDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n\n  for_each_kernel<<<1, 1>>>(thrust::device, d_input.begin(), d_input.end(), d_f);\n  {\n    cudaError_t const err = cudaGetLastError();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__ void for_each_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::for_each_n(exec, first, n, f);\n}\n\ntemplate <typename T>\nvoid TestForEachNDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = static_cast<T>(((size_t) h_input[i]) % output_size);\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n\n  for_each_n_kernel<<<1, 1>>>(thrust::seq, d_input.begin(), d_input.size(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceSeq);\n\ntemplate <typename T>\nvoid TestForEachNDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = static_cast<T>(((size_t) h_input[i]) % output_size);\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n\n  for_each_n_kernel<<<1, 1>>>(thrust::device, d_input.begin(), d_input.size(), d_f);\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceDevice);\n#endif\n\nvoid TestForEachCudaStreams()\n{\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::device_vector<int> input{3, 2, 3, 4, 6};\n  thrust::device_vector<int> output(7, 0);\n\n  mark_present_for_each<int> f;\n  f.ptr = thrust::raw_pointer_cast(output.data());\n\n  thrust::for_each(thrust::cuda::par.on(s), input.begin(), input.end(), f);\n\n  cudaStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 0, 1, 1, 1, 0, 1};\n  ASSERT_EQUAL(output, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestForEachCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/for_each.h>\n\n#include <algorithm>\n\n#include \"thrust/device_vector.h\"\n#include <unittest/unittest.h>\n\nstatic const size_t NUM_REGISTERS = 64;\n\ntemplate <size_t N>\n_CCCL_HOST_DEVICE void f(int* x)\n{\n  int temp = *x;\n  f<N - 1>(x + 1);\n  *x = temp;\n};\ntemplate <>\n_CCCL_HOST_DEVICE void f<0>(int* /*x*/)\n{}\ntemplate <size_t N>\nstruct CopyFunctorWithManyRegisters\n{\n  _CCCL_HOST_DEVICE void operator()(int* ptr)\n  {\n    f<N>(ptr);\n  }\n};\n\nvoid TestForEachLargeRegisterFootprint()\n{\n  int current_device = -1;\n  hipGetDevice(&current_device);\n  hipDeviceProp_t prop;\n  hipGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int*> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n\n  thrust::for_each(input.begin(), input.end(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachLargeRegisterFootprint);\n\nvoid TestForEachNLargeRegisterFootprint()\n{\n  int current_device = -1;\n  hipGetDevice(&current_device);\n  hipDeviceProp_t prop;\n  hipGetDeviceProperties(&prop, current_device);\n\n  thrust::device_vector<int> data(NUM_REGISTERS, 12345);\n\n  thrust::device_vector<int*> input(1, thrust::raw_pointer_cast(&data[0])); // length is irrelevant\n\n  thrust::for_each_n(input.begin(), input.size(), CopyFunctorWithManyRegisters<NUM_REGISTERS>());\n}\nDECLARE_UNITTEST(TestForEachNLargeRegisterFootprint);\n\ntemplate <typename T>\nstruct mark_present_for_each\n{\n  T* ptr;\n  _CCCL_HOST_DEVICE void operator()(T x)\n  {\n    ptr[(int) x] = 1;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function>\n__global__ void for_each_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f)\n{\n  thrust::for_each(exec, first, last, f);\n}\n\ntemplate <typename T>\nvoid TestForEachDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n\n  for_each_kernel<<<1, 1>>>(thrust::seq, d_input.begin(), d_input.end(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceSeq);\n\ntemplate <typename T>\nvoid TestForEachDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = ((size_t) h_input[i]) % output_size;\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each(h_input.begin(), h_input.end(), h_f);\n\n  for_each_kernel<<<1, 1>>>(thrust::device, d_input.begin(), d_input.end(), d_f);\n  {\n    hipError_t const err = hipGetLastError();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename Function>\n__global__ void for_each_n_kernel(ExecutionPolicy exec, Iterator first, Size n, Function f)\n{\n  thrust::for_each_n(exec, first, n, f);\n}\n\ntemplate <typename T>\nvoid TestForEachNDeviceSeq(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = static_cast<T>(((size_t) h_input[i]) % output_size);\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n\n  for_each_n_kernel<<<1, 1>>>(thrust::seq, d_input.begin(), d_input.size(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceSeq);\n\ntemplate <typename T>\nvoid TestForEachNDeviceDevice(const size_t n)\n{\n  const size_t output_size = std::min((size_t) 10, 2 * n);\n\n  thrust::host_vector<T> h_input = unittest::random_integers<T>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_input[i] = static_cast<T>(((size_t) h_input[i]) % output_size);\n  }\n\n  thrust::device_vector<T> d_input = h_input;\n\n  thrust::host_vector<T> h_output(output_size, (T) 0);\n  thrust::device_vector<T> d_output(output_size, (T) 0);\n\n  mark_present_for_each<T> h_f;\n  mark_present_for_each<T> d_f;\n  h_f.ptr = &h_output[0];\n  d_f.ptr = (&d_output[0]).get();\n\n  thrust::for_each_n(h_input.begin(), h_input.size(), h_f);\n\n  for_each_n_kernel<<<1, 1>>>(thrust::device, d_input.begin(), d_input.size(), d_f);\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  ASSERT_EQUAL(h_output, d_output);\n}\nDECLARE_VARIABLE_UNITTEST(TestForEachNDeviceDevice);\n#endif\n\nvoid TestForEachCudaStreams()\n{\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::device_vector<int> input{3, 2, 3, 4, 6};\n  thrust::device_vector<int> output(7, 0);\n\n  mark_present_for_each<int> f;\n  f.ptr = thrust::raw_pointer_cast(output.data());\n\n  thrust::for_each(thrust::cuda::par.on(s), input.begin(), input.end(), f);\n\n  hipStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 0, 1, 1, 1, 0, 1};\n  ASSERT_EQUAL(output, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestForEachCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/for_each.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/gather.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\ngather_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 elements_first, Iterator3 result)\n{\n  thrust::gather(exec, map_first, map_last, elements_first, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGatherDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n\n  // source vectors to gather from\n  thrust::host_vector<T> h_source   = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n\n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % source_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  // gather destination\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::gather(h_map.begin(), h_map.end(), h_source.begin(), h_output.begin());\n\n  gather_kernel<<<1, 1>>>(exec, d_map.begin(), d_map.end(), d_source.begin(), d_output.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate <typename T>\nvoid TestGatherDeviceSeq(const size_t n)\n{\n  TestGatherDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceSeq);\n\ntemplate <typename T>\nvoid TestGatherDeviceDevice(const size_t n)\n{\n  TestGatherDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceDevice);\n#endif\n\nvoid TestGatherCudaStreams()\n{\n  thrust::device_vector<int> map = {6, 2, 1, 7, 2}; // gather indices\n  thrust::device_vector<int> src = {0, 1, 2, 3, 4, 5, 6, 7}; // source vector\n  thrust::device_vector<int> dst = {0, 0, 0, 0, 0}; // destination vector\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::gather(thrust::cuda::par.on(s), map.begin(), map.end(), src.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n\n  thrust::device_vector<int> ref = {6, 2, 1, 7, 2}; // destination vector\n\n  ASSERT_EQUAL(dst, ref);\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate>\n__global__ void gather_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 map_first,\n  Iterator1 map_last,\n  Iterator2 stencil_first,\n  Iterator3 elements_first,\n  Iterator4 result,\n  Predicate pred)\n{\n  thrust::gather_if(exec, map_first, map_last, stencil_first, elements_first, result, pred);\n}\n\ntemplate <typename T>\nstruct is_even_gather_if\n{\n  _CCCL_HOST_DEVICE bool operator()(const T i) const\n  {\n    return (i % 2) == 0;\n  }\n};\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGatherIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n\n  // source vectors to gather from\n  thrust::host_vector<T> h_source   = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n\n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % source_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  // gather stencil\n  thrust::host_vector<unsigned int> h_stencil = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_stencil[i] = h_stencil[i] % 2;\n  }\n\n  thrust::device_vector<unsigned int> d_stencil = h_stencil;\n\n  // gather destination\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::gather_if(\n    h_map.begin(),\n    h_map.end(),\n    h_stencil.begin(),\n    h_source.begin(),\n    h_output.begin(),\n    is_even_gather_if<unsigned int>());\n\n  gather_if_kernel<<<1, 1>>>(\n    exec,\n    d_map.begin(),\n    d_map.end(),\n    d_stencil.begin(),\n    d_source.begin(),\n    d_output.begin(),\n    is_even_gather_if<unsigned int>());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate <typename T>\nvoid TestGatherIfDeviceSeq(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceSeq);\n\ntemplate <typename T>\nvoid TestGatherIfDeviceDevice(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceDevice);\n#endif\n\nvoid TestGatherIfCudaStreams()\n{\n  thrust::device_vector<int> flg{0, 1, 0, 1, 0}; // predicate array\n  thrust::device_vector<int> map{6, 2, 1, 7, 2}; // gather indices\n  thrust::device_vector<int> src{0, 1, 2, 3, 4, 5, 6, 7}; // source vector\n  thrust::device_vector<int> dst(5, 0); // destination vector\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::gather_if(thrust::cuda::par.on(s), map.begin(), map.end(), flg.begin(), src.begin(), dst.begin());\n  cudaStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 2, 0, 7, 0}; // destination vector\n\n  ASSERT_EQUAL(dst, ref);\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherIfCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/gather.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void\ngather_kernel(ExecutionPolicy exec, Iterator1 map_first, Iterator1 map_last, Iterator2 elements_first, Iterator3 result)\n{\n  thrust::gather(exec, map_first, map_last, elements_first, result);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGatherDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n\n  // source vectors to gather from\n  thrust::host_vector<T> h_source   = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n\n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % source_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  // gather destination\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::gather(h_map.begin(), h_map.end(), h_source.begin(), h_output.begin());\n\n  gather_kernel<<<1, 1>>>(exec, d_map.begin(), d_map.end(), d_source.begin(), d_output.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate <typename T>\nvoid TestGatherDeviceSeq(const size_t n)\n{\n  TestGatherDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceSeq);\n\ntemplate <typename T>\nvoid TestGatherDeviceDevice(const size_t n)\n{\n  TestGatherDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherDeviceDevice);\n#endif\n\nvoid TestGatherCudaStreams()\n{\n  thrust::device_vector<int> map = {6, 2, 1, 7, 2}; // gather indices\n  thrust::device_vector<int> src = {0, 1, 2, 3, 4, 5, 6, 7}; // source vector\n  thrust::device_vector<int> dst = {0, 0, 0, 0, 0}; // destination vector\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::gather(thrust::cuda::par.on(s), map.begin(), map.end(), src.begin(), dst.begin());\n  hipStreamSynchronize(s);\n\n  thrust::device_vector<int> ref = {6, 2, 1, 7, 2}; // destination vector\n\n  ASSERT_EQUAL(dst, ref);\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate>\n__global__ void gather_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 map_first,\n  Iterator1 map_last,\n  Iterator2 stencil_first,\n  Iterator3 elements_first,\n  Iterator4 result,\n  Predicate pred)\n{\n  thrust::gather_if(exec, map_first, map_last, stencil_first, elements_first, result, pred);\n}\n\ntemplate <typename T>\nstruct is_even_gather_if\n{\n  _CCCL_HOST_DEVICE bool operator()(const T i) const\n  {\n    return (i % 2) == 0;\n  }\n};\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestGatherIfDevice(ExecutionPolicy exec, const size_t n)\n{\n  const size_t source_size = std::min((size_t) 10, 2 * n);\n\n  // source vectors to gather from\n  thrust::host_vector<T> h_source   = unittest::random_samples<T>(source_size);\n  thrust::device_vector<T> d_source = h_source;\n\n  // gather indices\n  thrust::host_vector<unsigned int> h_map = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_map[i] = h_map[i] % source_size;\n  }\n\n  thrust::device_vector<unsigned int> d_map = h_map;\n\n  // gather stencil\n  thrust::host_vector<unsigned int> h_stencil = unittest::random_integers<unsigned int>(n);\n\n  for (size_t i = 0; i < n; i++)\n  {\n    h_stencil[i] = h_stencil[i] % 2;\n  }\n\n  thrust::device_vector<unsigned int> d_stencil = h_stencil;\n\n  // gather destination\n  thrust::host_vector<T> h_output(n);\n  thrust::device_vector<T> d_output(n);\n\n  thrust::gather_if(\n    h_map.begin(),\n    h_map.end(),\n    h_stencil.begin(),\n    h_source.begin(),\n    h_output.begin(),\n    is_even_gather_if<unsigned int>());\n\n  gather_if_kernel<<<1, 1>>>(\n    exec,\n    d_map.begin(),\n    d_map.end(),\n    d_stencil.begin(),\n    d_source.begin(),\n    d_output.begin(),\n    is_even_gather_if<unsigned int>());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_output, d_output);\n}\n\ntemplate <typename T>\nvoid TestGatherIfDeviceSeq(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceSeq);\n\ntemplate <typename T>\nvoid TestGatherIfDeviceDevice(const size_t n)\n{\n  TestGatherIfDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestGatherIfDeviceDevice);\n#endif\n\nvoid TestGatherIfCudaStreams()\n{\n  thrust::device_vector<int> flg{0, 1, 0, 1, 0}; // predicate array\n  thrust::device_vector<int> map{6, 2, 1, 7, 2}; // gather indices\n  thrust::device_vector<int> src{0, 1, 2, 3, 4, 5, 6, 7}; // source vector\n  thrust::device_vector<int> dst(5, 0); // destination vector\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::gather_if(thrust::cuda::par.on(s), map.begin(), map.end(), flg.begin(), src.begin(), dst.begin());\n  hipStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 2, 0, 7, 0}; // destination vector\n\n  ASSERT_EQUAL(dst, ref);\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestGatherIfCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/gather.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/partition.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void\nis_partitioned_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::is_partitioned(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsPartitionedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  n = ::cuda::std::max<size_t>(n, 2);\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_partitioned_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::partition(v.begin(), v.end(), is_even<int>());\n\n  is_partitioned_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsPartitionedDeviceSeq()\n{\n  TestIsPartitionedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceSeq);\n\nvoid TestIsPartitionedDeviceDevice()\n{\n  TestIsPartitionedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceDevice);\n#endif\n\nvoid TestIsPartitionedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 1;\n  v[1] = 1;\n  v[2] = 1;\n  v[3] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  // empty partition\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin(), ::cuda::std::identity{}));\n\n  // one element true partition\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, ::cuda::std::identity{}));\n\n  // just true partition\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 2, ::cuda::std::identity{}));\n\n  // both true & false partitions\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}));\n\n  // one element false partition\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin() + 3, v.end(), ::cuda::std::identity{}));\n\n  v[0] = 1;\n  v[1] = 0;\n  v[2] = 1;\n  v[3] = 1;\n\n  // not partitioned\n  ASSERT_EQUAL_QUIET(false,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}));\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsPartitionedCudaStreams);\n\ntemplate <typename T>\nstruct is_even_non_const\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) // no const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\nvoid TestIsPartitionedWithNonConstPredicate()\n{\n  thrust::device_vector<int> partitioned   = {0, 2, 4, 1, 3, 5};\n  thrust::device_vector<int> unpartitioned = {0, 1, 2, 3};\n\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par, partitioned.begin(), partitioned.end(), is_even_non_const<int>{}));\n\n  ASSERT_EQUAL_QUIET(\n    false,\n    thrust::is_partitioned(thrust::cuda::par, unpartitioned.begin(), unpartitioned.end(), is_even_non_const<int>{}));\n}\nDECLARE_UNITTEST(TestIsPartitionedWithNonConstPredicate);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/partition.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Predicate, typename Iterator2>\n__global__ void\nis_partitioned_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::is_partitioned(exec, first, last, pred);\n}\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsPartitionedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  n = ::cuda::std::max<size_t>(n, 2);\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_partitioned_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::partition(v.begin(), v.end(), is_even<int>());\n\n  is_partitioned_kernel<<<1, 1>>>(exec, v.begin(), v.end(), is_even<int>(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsPartitionedDeviceSeq()\n{\n  TestIsPartitionedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceSeq);\n\nvoid TestIsPartitionedDeviceDevice()\n{\n  TestIsPartitionedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsPartitionedDeviceDevice);\n#endif\n\nvoid TestIsPartitionedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 1;\n  v[1] = 1;\n  v[2] = 1;\n  v[3] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  // empty partition\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin(), ::cuda::std::identity{}));\n\n  // one element true partition\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, ::cuda::std::identity{}));\n\n  // just true partition\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.begin() + 2, ::cuda::std::identity{}));\n\n  // both true & false partitions\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}));\n\n  // one element false partition\n  ASSERT_EQUAL_QUIET(true,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin() + 3, v.end(), ::cuda::std::identity{}));\n\n  v[0] = 1;\n  v[1] = 0;\n  v[2] = 1;\n  v[3] = 1;\n\n  // not partitioned\n  ASSERT_EQUAL_QUIET(false,\n                     thrust::is_partitioned(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}));\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsPartitionedCudaStreams);\n\ntemplate <typename T>\nstruct is_even_non_const\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) // no const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\nvoid TestIsPartitionedWithNonConstPredicate()\n{\n  thrust::device_vector<int> partitioned   = {0, 2, 4, 1, 3, 5};\n  thrust::device_vector<int> unpartitioned = {0, 1, 2, 3};\n\n  ASSERT_EQUAL_QUIET(\n    true, thrust::is_partitioned(thrust::cuda::par, partitioned.begin(), partitioned.end(), is_even_non_const<int>{}));\n\n  ASSERT_EQUAL_QUIET(\n    false,\n    thrust::is_partitioned(thrust::cuda::par, unpartitioned.begin(), unpartitioned.end(), is_even_non_const<int>{}));\n}\nDECLARE_UNITTEST(TestIsPartitionedWithNonConstPredicate);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/is_partitioned.cu"}
{"input": "#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void equal_kernel(\n  ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2, pred);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestEqualDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::device_vector<T> d_data1 = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data2 = unittest::random_samples<T>(n);\n  thrust::device_vector<bool> d_result(1, false);\n\n  // empty ranges\n  equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.begin(), d_data1.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n\n  // symmetric cases\n  equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.end(), d_data1.begin(), d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n\n  if (n > 0)\n  {\n    d_data1[0] = 0;\n    d_data2[0] = 1;\n\n    // different vectors\n    equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.end(), d_data2.begin(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n\n    // different predicates\n    equal_kernel<<<1, 1>>>(\n      exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), ::cuda::std::less<T>(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], true);\n\n    equal_kernel<<<1, 1>>>(\n      exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), ::cuda::std::greater<T>(), d_result.begin());\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n  }\n}\n\ntemplate <typename T>\nvoid TestEqualDeviceSeq(const size_t n)\n{\n  TestEqualDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceSeq);\n\ntemplate <typename T>\nvoid TestEqualDeviceDevice(const size_t n)\n{\n  TestEqualDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceDevice);\n#endif\n\nvoid TestEqualCudaStreams()\n{\n  thrust::device_vector<int> v1 = {5, 2, 0, 0, 0};\n  thrust::device_vector<int> v2 = {5, 2, 0, 6, 1};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin()), false);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v2.begin(), v2.end(), v2.begin()), true);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 0, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 1, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 3, v2.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 4, v2.begin()), false);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), ::cuda::std::less_equal<int>()),\n               true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), ::cuda::std::greater<int>()),\n               false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestEqualCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/equal.h>\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Iterator3>\n__global__ void equal_kernel(ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename BinaryPredicate, typename Iterator3>\n__global__ void equal_kernel(\n  ExecutionPolicy exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryPredicate pred, Iterator3 result)\n{\n  *result = thrust::equal(exec, first1, last1, first2, pred);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestEqualDevice(ExecutionPolicy exec, const size_t n)\n{\n  thrust::device_vector<T> d_data1 = unittest::random_samples<T>(n);\n  thrust::device_vector<T> d_data2 = unittest::random_samples<T>(n);\n  thrust::device_vector<bool> d_result(1, false);\n\n  // empty ranges\n  equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.begin(), d_data1.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n\n  // symmetric cases\n  equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.end(), d_data1.begin(), d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(d_result[0], true);\n\n  if (n > 0)\n  {\n    d_data1[0] = 0;\n    d_data2[0] = 1;\n\n    // different vectors\n    equal_kernel<<<1, 1>>>(exec, d_data1.begin(), d_data1.end(), d_data2.begin(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n\n    // different predicates\n    equal_kernel<<<1, 1>>>(\n      exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), ::cuda::std::less<T>(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], true);\n\n    equal_kernel<<<1, 1>>>(\n      exec, d_data1.begin(), d_data1.begin() + 1, d_data2.begin(), ::cuda::std::greater<T>(), d_result.begin());\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n\n    ASSERT_EQUAL(d_result[0], false);\n  }\n}\n\ntemplate <typename T>\nvoid TestEqualDeviceSeq(const size_t n)\n{\n  TestEqualDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceSeq);\n\ntemplate <typename T>\nvoid TestEqualDeviceDevice(const size_t n)\n{\n  TestEqualDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestEqualDeviceDevice);\n#endif\n\nvoid TestEqualCudaStreams()\n{\n  thrust::device_vector<int> v1 = {5, 2, 0, 0, 0};\n  thrust::device_vector<int> v2 = {5, 2, 0, 6, 1};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin()), false);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v2.begin(), v2.end(), v2.begin()), true);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 0, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 1, v1.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 3, v2.begin()), true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.begin() + 4, v2.begin()), false);\n\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), ::cuda::std::less_equal<int>()),\n               true);\n  ASSERT_EQUAL(thrust::equal(thrust::cuda::par.on(s), v1.begin(), v1.end(), v2.begin(), ::cuda::std::greater<int>()),\n               false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestEqualCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/equal.cu"}
{"input": "#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include \"thrust/iterator/transform_iterator.h\"\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (static_cast<unsigned int>(x) & 1) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct mod_3\n{\n  _CCCL_HOST_DEVICE unsigned int operator()(T x)\n  {\n    return static_cast<unsigned int>(x) % 3;\n  }\n};\n\ntemplate <typename T>\nstruct mod_n\n{\n  T mod;\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (x % mod == 0) ? true : false;\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  _CCCL_HOST_DEVICE T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<typename thrust::device_vector<int>::iterator> d_new_end_vec(1);\n\n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1, 1>>>(\n      exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n\n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestCopyIfDeviceSeq()\n{\n  TestCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceSeq);\n\nvoid TestCopyIfDeviceDevice()\n{\n  TestCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceDevice);\n\nvoid TestCopyIfDeviceNoSync()\n{\n  TestCopyIfDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector data{1, 2, 1, 3, 2};\n  Vector result(data.size());\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s), data.begin(), data.end(), result.begin(), is_even<int>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n  result.resize(end - result.begin());\n  Vector ref{2, 2};\n  ASSERT_EQUAL(result, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestCopyIfCudaStreamsSync()\n{\n  TestCopyIfCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsSync);\n\nvoid TestCopyIfCudaStreamsNoSync()\n{\n  TestCopyIfCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result1,\n  Predicate pred,\n  Iterator4 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, stencil_first, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data(n);\n  thrust::sequence(h_data.begin(), h_data.end());\n  thrust::device_vector<int> d_data(n);\n  thrust::sequence(d_data.begin(), d_data.end());\n\n  thrust::host_vector<int> h_stencil   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_stencil = unittest::random_integers<int>(n);\n\n  typename thrust::host_vector<int>::iterator h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<typename thrust::device_vector<int>::iterator> d_new_end_vec(1);\n\n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1, 1>>>(\n      exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n\n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestCopyIfStencilDeviceSeq()\n{\n  TestCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceSeq);\n\nvoid TestCopyIfStencilDeviceDevice()\n{\n  TestCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceDevice);\n\nvoid TestCopyIfStencilDeviceNoSync()\n{\n  TestCopyIfStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfStencilCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::copy_if(policy.on(s), data.begin(), data.end(), stencil.begin(), result.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n  result.resize(end - result.begin());\n\n  Vector ref{2, 2};\n  ASSERT_EQUAL(result, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestCopyIfStencilCudaStreamsSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsSync);\n\nvoid TestCopyIfStencilCudaStreamsNoSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsNoSync);\n\nvoid TestCopyIfWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth     = 1000000;\n  offset_t expected_num_copied = (num_items + match_every_nth - 1) / match_every_nth;\n  thrust::device_vector<offset_t> copied_out(expected_num_copied);\n  auto selected_out_end = thrust::copy_if(begin, end, copied_out.begin(), mod_n<offset_t>{match_every_nth});\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(::cuda::std::distance(copied_out.begin(), selected_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_copied);\n  copied_out.resize(expected_num_copied);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n  bool all_results_correct = thrust::equal(copied_out.begin(), copied_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestCopyIfWithLargeNumberOfItems()\n{\n  TestCopyIfWithMagnitude(30);\n  TestCopyIfWithMagnitude(31);\n  TestCopyIfWithMagnitude(32);\n  TestCopyIfWithMagnitude(33);\n}\nDECLARE_UNITTEST(TestCopyIfWithLargeNumberOfItems);\n\nvoid TestCopyIfStencilWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  thrust::counting_iterator<offset_t> stencil(offset_t{0});\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth     = 1000000;\n  offset_t expected_num_copied = (num_items + match_every_nth - 1) / match_every_nth;\n  thrust::device_vector<offset_t> copied_out(expected_num_copied);\n  auto selected_out_end = thrust::copy_if(begin, end, stencil, copied_out.begin(), mod_n<offset_t>{match_every_nth});\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(::cuda::std::distance(copied_out.begin(), selected_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_copied);\n  copied_out.resize(expected_num_copied);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n  bool all_results_correct = thrust::equal(copied_out.begin(), copied_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestCopyIfStencilWithLargeNumberOfItems()\n{\n  TestCopyIfStencilWithMagnitude(30);\n  TestCopyIfStencilWithMagnitude(31);\n  TestCopyIfStencilWithMagnitude(32);\n  TestCopyIfStencilWithMagnitude(33);\n}\nDECLARE_UNITTEST(TestCopyIfStencilWithLargeNumberOfItems);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/copy.h>\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include \"thrust/iterator/transform_iterator.h\"\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (static_cast<unsigned int>(x) & 1) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct mod_3\n{\n  _CCCL_HOST_DEVICE unsigned int operator()(T x)\n  {\n    return static_cast<unsigned int>(x) % 3;\n  }\n};\n\ntemplate <typename T>\nstruct mod_n\n{\n  T mod;\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (x % mod == 0) ? true : false;\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  _CCCL_HOST_DEVICE T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void copy_if_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 result1, Predicate pred, Iterator3 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfDevice(ExecutionPolicy exec)\n{\n  size_t n                          = 1000;\n  thrust::host_vector<int> h_data   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_data = h_data;\n\n  typename thrust::host_vector<int>::iterator h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<typename thrust::device_vector<int>::iterator> d_new_end_vec(1);\n\n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1, 1>>>(\n      exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n\n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestCopyIfDeviceSeq()\n{\n  TestCopyIfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceSeq);\n\nvoid TestCopyIfDeviceDevice()\n{\n  TestCopyIfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceDevice);\n\nvoid TestCopyIfDeviceNoSync()\n{\n  TestCopyIfDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector data{1, 2, 1, 3, 2};\n  Vector result(data.size());\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end = thrust::copy_if(policy.on(s), data.begin(), data.end(), result.begin(), is_even<int>());\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n  result.resize(end - result.begin());\n  Vector ref{2, 2};\n  ASSERT_EQUAL(result, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestCopyIfCudaStreamsSync()\n{\n  TestCopyIfCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsSync);\n\nvoid TestCopyIfCudaStreamsNoSync()\n{\n  TestCopyIfCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfCudaStreamsNoSync);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void copy_if_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 result1,\n  Predicate pred,\n  Iterator4 result2)\n{\n  *result2 = thrust::copy_if(exec, first, last, stencil_first, result1, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfStencilDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n  thrust::host_vector<int> h_data(n);\n  thrust::sequence(h_data.begin(), h_data.end());\n  thrust::device_vector<int> d_data(n);\n  thrust::sequence(d_data.begin(), d_data.end());\n\n  thrust::host_vector<int> h_stencil   = unittest::random_integers<int>(n);\n  thrust::device_vector<int> d_stencil = unittest::random_integers<int>(n);\n\n  typename thrust::host_vector<int>::iterator h_new_end;\n  typename thrust::device_vector<int>::iterator d_new_end;\n\n  thrust::device_vector<typename thrust::device_vector<int>::iterator> d_new_end_vec(1);\n\n  // test with Predicate that returns a bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), is_even<int>());\n\n    copy_if_kernel<<<1, 1>>>(\n      exec, d_data.begin(), d_data.end(), d_result.begin(), is_even<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n\n  // test with Predicate that returns a non-bool\n  {\n    thrust::host_vector<int> h_result(n);\n    thrust::device_vector<int> d_result(n);\n\n    h_new_end = thrust::copy_if(h_data.begin(), h_data.end(), h_result.begin(), mod_3<int>());\n\n    copy_if_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), d_result.begin(), mod_3<int>(), d_new_end_vec.begin());\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n\n    d_new_end = d_new_end_vec[0];\n\n    h_result.resize(h_new_end - h_result.begin());\n    d_result.resize(d_new_end - d_result.begin());\n\n    ASSERT_EQUAL(h_result, d_result);\n  }\n}\n\nvoid TestCopyIfStencilDeviceSeq()\n{\n  TestCopyIfStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceSeq);\n\nvoid TestCopyIfStencilDeviceDevice()\n{\n  TestCopyIfStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceDevice);\n\nvoid TestCopyIfStencilDeviceNoSync()\n{\n  TestCopyIfStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilDeviceNoSync);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestCopyIfStencilCudaStreams(ExecutionPolicy policy)\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector data{1, 2, 1, 3, 2};\n\n  Vector result(5);\n\n  Vector stencil{0, 1, 0, 0, 1};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  Vector::iterator end =\n    thrust::copy_if(policy.on(s), data.begin(), data.end(), stencil.begin(), result.begin(), ::cuda::std::identity{});\n\n  ASSERT_EQUAL(end - result.begin(), 2);\n  result.resize(end - result.begin());\n\n  Vector ref{2, 2};\n  ASSERT_EQUAL(result, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestCopyIfStencilCudaStreamsSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsSync);\n\nvoid TestCopyIfStencilCudaStreamsNoSync()\n{\n  TestCopyIfStencilCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestCopyIfStencilCudaStreamsNoSync);\n\nvoid TestCopyIfWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth     = 1000000;\n  offset_t expected_num_copied = (num_items + match_every_nth - 1) / match_every_nth;\n  thrust::device_vector<offset_t> copied_out(expected_num_copied);\n  auto selected_out_end = thrust::copy_if(begin, end, copied_out.begin(), mod_n<offset_t>{match_every_nth});\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(::cuda::std::distance(copied_out.begin(), selected_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_copied);\n  copied_out.resize(expected_num_copied);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n  bool all_results_correct = thrust::equal(copied_out.begin(), copied_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestCopyIfWithLargeNumberOfItems()\n{\n  TestCopyIfWithMagnitude(30);\n  TestCopyIfWithMagnitude(31);\n  TestCopyIfWithMagnitude(32);\n  TestCopyIfWithMagnitude(33);\n}\nDECLARE_UNITTEST(TestCopyIfWithLargeNumberOfItems);\n\nvoid TestCopyIfStencilWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  thrust::counting_iterator<offset_t> stencil(offset_t{0});\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth     = 1000000;\n  offset_t expected_num_copied = (num_items + match_every_nth - 1) / match_every_nth;\n  thrust::device_vector<offset_t> copied_out(expected_num_copied);\n  auto selected_out_end = thrust::copy_if(begin, end, stencil, copied_out.begin(), mod_n<offset_t>{match_every_nth});\n\n  // Ensure number of selected items are correct\n  offset_t num_selected_out = static_cast<offset_t>(::cuda::std::distance(copied_out.begin(), selected_out_end));\n  ASSERT_EQUAL(num_selected_out, expected_num_copied);\n  copied_out.resize(expected_num_copied);\n\n  // Ensure selected items are correct\n  auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n  bool all_results_correct = thrust::equal(copied_out.begin(), copied_out.end(), expected_out_it);\n  ASSERT_EQUAL(all_results_correct, true);\n}\n\nvoid TestCopyIfStencilWithLargeNumberOfItems()\n{\n  TestCopyIfStencilWithMagnitude(30);\n  TestCopyIfStencilWithMagnitude(31);\n  TestCopyIfStencilWithMagnitude(32);\n  TestCopyIfStencilWithMagnitude(33);\n}\nDECLARE_UNITTEST(TestCopyIfStencilWithLargeNumberOfItems);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/copy_if.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void is_sorted_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::is_sorted(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsSortedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsSortedDeviceSeq()\n{\n  TestIsSortedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceSeq);\n\nvoid TestIsSortedDeviceDevice()\n{\n  TestIsSortedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceDevice);\n#endif\n\nvoid TestIsSortedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 0;\n  v[1] = 5;\n  v[2] = 8;\n  v[3] = 0;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 0), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1), true);\n\n  // the following line crashes gcc 4.3\n#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)\n  // do nothing\n#else\n  // compile this line on other compilers\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 2), true);\n#endif // GCC\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4), false);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3, ::cuda::std::less<int>()), true);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, ::cuda::std::greater<int>()), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4, ::cuda::std::greater<int>()),\n               false);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.end()), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/sort.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Iterator2>\n__global__ void is_sorted_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Iterator2 result)\n{\n  *result = thrust::is_sorted(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestIsSortedDevice(ExecutionPolicy exec)\n{\n  size_t n = 1000;\n\n  thrust::device_vector<int> v = unittest::random_integers<int>(n);\n\n  thrust::device_vector<bool> result(1);\n\n  v[0] = 1;\n  v[1] = 0;\n\n  is_sorted_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  thrust::sort(v.begin(), v.end());\n\n  is_sorted_kernel<<<1, 1>>>(exec, v.begin(), v.end(), result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestIsSortedDeviceSeq()\n{\n  TestIsSortedDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceSeq);\n\nvoid TestIsSortedDeviceDevice()\n{\n  TestIsSortedDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestIsSortedDeviceDevice);\n#endif\n\nvoid TestIsSortedCudaStreams()\n{\n  thrust::device_vector<int> v(4);\n  v[0] = 0;\n  v[1] = 5;\n  v[2] = 8;\n  v[3] = 0;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 0), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1), true);\n\n  // the following line crashes gcc 4.3\n#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)\n  // do nothing\n#else\n  // compile this line on other compilers\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 2), true);\n#endif // GCC\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4), false);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 3, ::cuda::std::less<int>()), true);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 1, ::cuda::std::greater<int>()), true);\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.begin() + 4, ::cuda::std::greater<int>()),\n               false);\n\n  ASSERT_EQUAL(thrust::is_sorted(thrust::cuda::par.on(s), v.begin(), v.end()), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestIsSortedCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/is_sorted.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/logical.h>\n#include <thrust/memory.h>\n#include <thrust/system/cpp/memory.h>\n#include <thrust/system/cuda/memory.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T1, typename T2>\nbool are_same_type(const T1&, const T2&)\n{\n  return false;\n}\n\ntemplate <typename T>\nbool are_same_type(const T&, const T&)\n{\n  return true;\n}\n\nvoid TestSelectSystemCudaToCpp()\n{\n  using thrust::system::detail::generic::select_system;\n\n  thrust::cuda::tag cuda_tag;\n  thrust::cpp::tag cpp_tag;\n  thrust::cuda_cub::cross_system<thrust::cuda::tag, thrust::cpp::tag> cuda_to_cpp(cuda_tag, cpp_tag);\n\n  // select_system(cuda::tag, thrust::host_system_tag) should return cuda_to_cpp\n  bool is_cuda_to_cpp = are_same_type(cuda_to_cpp, select_system(cuda_tag, cpp_tag));\n  ASSERT_EQUAL(true, is_cuda_to_cpp);\n}\nDECLARE_UNITTEST(TestSelectSystemCudaToCpp);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename Iterator>\n__global__ void get_temporary_buffer_kernel(size_t n, Iterator result)\n{\n  *result = thrust::get_temporary_buffer<int>(thrust::seq, n);\n}\n\ntemplate <typename Pointer>\n__global__ void return_temporary_buffer_kernel(Pointer ptr, std::ptrdiff_t n)\n{\n  thrust::return_temporary_buffer(thrust::seq, ptr, n);\n}\n\nvoid TestGetTemporaryBufferDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  using pointer         = thrust::pointer<int, thrust::detail::seq_t>;\n  using ptr_and_sz_type = cuda::std::pair<pointer, std::ptrdiff_t>;\n  thrust::device_vector<ptr_and_sz_type> d_result(1);\n\n  get_temporary_buffer_kernel<<<1, 1>>>(n, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ptr_and_sz_type ptr_and_sz = d_result[0];\n\n  if (ptr_and_sz.second > 0)\n  {\n    ASSERT_EQUAL(ptr_and_sz.second, n);\n\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr_and_sz.first, n, ref_val);\n\n    ASSERT_EQUAL(\n      true,\n      thrust::all_of(thrust::device, ptr_and_sz.first, ptr_and_sz.first + n, thrust::placeholders::_1 == ref_val));\n\n    return_temporary_buffer_kernel<<<1, 1>>>(ptr_and_sz.first, ptr_and_sz.second);\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestGetTemporaryBufferDeviceSeq);\n\ntemplate <typename Iterator>\n__global__ void malloc_kernel(size_t n, Iterator result)\n{\n  *result = static_cast<int*>(thrust::malloc(thrust::seq, sizeof(int) * n).get());\n}\n\ntemplate <typename Pointer>\n__global__ void free_kernel(Pointer ptr)\n{\n  thrust::free(thrust::seq, ptr);\n}\n\nvoid TestMallocDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  using pointer = thrust::pointer<int, thrust::detail::seq_t>;\n  thrust::device_vector<pointer> d_result(1);\n\n  malloc_kernel<<<1, 1>>>(n, d_result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  pointer ptr = d_result[0];\n\n  if (ptr.get() != 0)\n  {\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr, ptr + n, thrust::placeholders::_1 == ref_val));\n\n    free_kernel<<<1, 1>>>(ptr);\n    {\n      cudaError_t const err = cudaDeviceSynchronize();\n      ASSERT_EQUAL(cudaSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestMallocDeviceSeq);\n#endif\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/logical.h>\n#include <thrust/memory.h>\n#include <thrust/system/cpp/memory.h>\n#include <thrust/system/cuda/memory.h>\n\n#include <unittest/unittest.h>\n\ntemplate <typename T1, typename T2>\nbool are_same_type(const T1&, const T2&)\n{\n  return false;\n}\n\ntemplate <typename T>\nbool are_same_type(const T&, const T&)\n{\n  return true;\n}\n\nvoid TestSelectSystemCudaToCpp()\n{\n  using thrust::system::detail::generic::select_system;\n\n  thrust::cuda::tag cuda_tag;\n  thrust::cpp::tag cpp_tag;\n  thrust::cuda_cub::cross_system<thrust::cuda::tag, thrust::cpp::tag> cuda_to_cpp(cuda_tag, cpp_tag);\n\n  // select_system(cuda::tag, thrust::host_system_tag) should return cuda_to_cpp\n  bool is_cuda_to_cpp = are_same_type(cuda_to_cpp, select_system(cuda_tag, cpp_tag));\n  ASSERT_EQUAL(true, is_cuda_to_cpp);\n}\nDECLARE_UNITTEST(TestSelectSystemCudaToCpp);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename Iterator>\n__global__ void get_temporary_buffer_kernel(size_t n, Iterator result)\n{\n  *result = thrust::get_temporary_buffer<int>(thrust::seq, n);\n}\n\ntemplate <typename Pointer>\n__global__ void return_temporary_buffer_kernel(Pointer ptr, std::ptrdiff_t n)\n{\n  thrust::return_temporary_buffer(thrust::seq, ptr, n);\n}\n\nvoid TestGetTemporaryBufferDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  using pointer         = thrust::pointer<int, thrust::detail::seq_t>;\n  using ptr_and_sz_type = cuda::std::pair<pointer, std::ptrdiff_t>;\n  thrust::device_vector<ptr_and_sz_type> d_result(1);\n\n  get_temporary_buffer_kernel<<<1, 1>>>(n, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ptr_and_sz_type ptr_and_sz = d_result[0];\n\n  if (ptr_and_sz.second > 0)\n  {\n    ASSERT_EQUAL(ptr_and_sz.second, n);\n\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr_and_sz.first, n, ref_val);\n\n    ASSERT_EQUAL(\n      true,\n      thrust::all_of(thrust::device, ptr_and_sz.first, ptr_and_sz.first + n, thrust::placeholders::_1 == ref_val));\n\n    return_temporary_buffer_kernel<<<1, 1>>>(ptr_and_sz.first, ptr_and_sz.second);\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestGetTemporaryBufferDeviceSeq);\n\ntemplate <typename Iterator>\n__global__ void malloc_kernel(size_t n, Iterator result)\n{\n  *result = static_cast<int*>(thrust::malloc(thrust::seq, sizeof(int) * n).get());\n}\n\ntemplate <typename Pointer>\n__global__ void free_kernel(Pointer ptr)\n{\n  thrust::free(thrust::seq, ptr);\n}\n\nvoid TestMallocDeviceSeq()\n{\n  const std::ptrdiff_t n = 9001;\n\n  using pointer = thrust::pointer<int, thrust::detail::seq_t>;\n  thrust::device_vector<pointer> d_result(1);\n\n  malloc_kernel<<<1, 1>>>(n, d_result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  pointer ptr = d_result[0];\n\n  if (ptr.get() != 0)\n  {\n    const int ref_val = 13;\n    thrust::device_vector<int> ref(n, ref_val);\n\n    thrust::fill_n(thrust::device, ptr, n, ref_val);\n\n    ASSERT_EQUAL(true, thrust::all_of(thrust::device, ptr, ptr + n, thrust::placeholders::_1 == ref_val));\n\n    free_kernel<<<1, 1>>>(ptr);\n    {\n      hipError_t const err = hipDeviceSynchronize();\n      ASSERT_EQUAL(hipSuccess, err);\n    }\n  }\n}\nDECLARE_UNITTEST(TestMallocDeviceSeq);\n#endif\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/memory.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::sequence(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init)\n{\n  thrust::sequence(exec, first, last, init);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, T step)\n{\n  thrust::sequence(exec, first, last, init, step);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSequenceDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> v(5);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  thrust::device_vector<int> ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end(), 10);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {10, 11, 12, 13, 14};\n  ASSERT_EQUAL(v, ref);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end(), 10, 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ref = {10, 12, 14, 16, 18};\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestSequenceDeviceSeq()\n{\n  TestSequenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSequenceDeviceSeq);\n\nvoid TestSequenceDeviceDevice()\n{\n  TestSequenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSequenceDeviceDevice);\n#endif\n\nvoid TestSequenceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v(5);\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end());\n  cudaStreamSynchronize(s);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10);\n  cudaStreamSynchronize(s);\n\n  ref = {10, 11, 12, 13, 14};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10, 2);\n  cudaStreamSynchronize(s);\n\n  ref = {10, 12, 14, 16, 18};\n  ASSERT_EQUAL(v, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSequenceCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last)\n{\n  thrust::sequence(exec, first, last);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init)\n{\n  thrust::sequence(exec, first, last, init);\n}\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void sequence_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T init, T step)\n{\n  thrust::sequence(exec, first, last, init, step);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestSequenceDevice(ExecutionPolicy exec)\n{\n  thrust::device_vector<int> v(5);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  thrust::device_vector<int> ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end(), 10);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {10, 11, 12, 13, 14};\n  ASSERT_EQUAL(v, ref);\n\n  sequence_kernel<<<1, 1>>>(exec, v.begin(), v.end(), 10, 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ref = {10, 12, 14, 16, 18};\n  ASSERT_EQUAL(v, ref);\n}\n\nvoid TestSequenceDeviceSeq()\n{\n  TestSequenceDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestSequenceDeviceSeq);\n\nvoid TestSequenceDeviceDevice()\n{\n  TestSequenceDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestSequenceDeviceDevice);\n#endif\n\nvoid TestSequenceCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n\n  Vector v(5);\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end());\n  hipStreamSynchronize(s);\n\n  Vector ref{0, 1, 2, 3, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10);\n  hipStreamSynchronize(s);\n\n  ref = {10, 11, 12, 13, 14};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::sequence(thrust::cuda::par.on(s), v.begin(), v.end(), 10, 2);\n  hipStreamSynchronize(s);\n\n  ref = {10, 12, 14, 16, 18};\n  ASSERT_EQUAL(v, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestSequenceCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/sequence.cu"}
{"input": "#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/partition.h>\n\n#include \"thrust/detail/raw_pointer_cast.h\"\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct mod_n\n{\n  T mod;\n  bool negate;\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (x % mod == 0) ? (!negate) : negate;\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  _CCCL_HOST_DEVICE T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestPartitionDeviceSeq()\n{\n  TestPartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionDeviceSeq);\n\nvoid TestPartitionDeviceDevice()\n{\n  TestPartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionDeviceDevice);\n\nvoid TestPartitionDeviceNoSync()\n{\n  TestPartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void partition_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::partition(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionStencilDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestPartitionStencilDeviceSeq()\n{\n  TestPartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceSeq);\n\nvoid TestPartitionStencilDeviceDevice()\n{\n  TestPartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceDevice);\n\nvoid TestPartitionStencilDeviceNoSync()\n{\n  TestPartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 true_result,\n  Iterator3 false_result,\n  Predicate pred,\n  Iterator4 result)\n{\n  *result = thrust::partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCopyDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] = 2;\n  true_ref[1] = 2;\n\n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] = 1;\n  false_ref[1] = 1;\n  false_ref[2] = 1;\n\n  pair_type ends = iterators[0];\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestPartitionCopyDeviceSeq()\n{\n  TestPartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceSeq);\n\nvoid TestPartitionCopyDeviceDevice()\n{\n  TestPartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceDevice);\n\nvoid TestPartitionCopyDeviceNoSync()\n{\n  TestPartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate,\n          typename Iterator5>\n__global__ void partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 true_result,\n  Iterator4 false_result,\n  Predicate pred,\n  Iterator5 result)\n{\n  *result = thrust::partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<int> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<int> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using iterator  = typename thrust::device_vector<int>::iterator;\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1, 1>>>(\n    exec,\n    data.begin(),\n    data.end(),\n    stencil.begin(),\n    true_results.begin(),\n    false_results.begin(),\n    is_even<T>(),\n    iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  pair_type ends = iterators[0];\n\n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] = 1;\n  true_ref[1] = 1;\n\n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] = 0;\n  false_ref[1] = 0;\n  false_ref[2] = 0;\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestPartitionCopyStencilDeviceSeq()\n{\n  TestPartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceSeq);\n\nvoid TestPartitionCopyStencilDeviceDevice()\n{\n  TestPartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceDevice);\n\nvoid TestPartitionCopyStencilDeviceNoSync()\n{\n  TestPartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void\nstable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::stable_partition(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestStablePartitionDeviceSeq()\n{\n  TestStablePartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceSeq);\n\nvoid TestStablePartitionDeviceDevice()\n{\n  TestStablePartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceDevice);\n\nvoid TestStablePartitionDeviceNoSync()\n{\n  TestStablePartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void stable_partition_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::stable_partition(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionStencilDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestStablePartitionStencilDeviceSeq()\n{\n  TestStablePartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceSeq);\n\nvoid TestStablePartitionStencilDeviceDevice()\n{\n  TestStablePartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceDevice);\n\nvoid TestStablePartitionStencilDeviceNoSync()\n{\n  TestStablePartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void stable_partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 true_result,\n  Iterator3 false_result,\n  Predicate pred,\n  Iterator4 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionCopyDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] = 2;\n  true_ref[1] = 2;\n\n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] = 1;\n  false_ref[1] = 1;\n  false_ref[2] = 1;\n\n  pair_type ends = iterators[0];\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestStablePartitionCopyDeviceSeq()\n{\n  TestStablePartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceSeq);\n\nvoid TestStablePartitionCopyDeviceDevice()\n{\n  TestStablePartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceDevice);\n\nvoid TestStablePartitionCopyDeviceNoSync()\n{\n  TestStablePartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate,\n          typename Iterator5>\n__global__ void stable_partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 true_result,\n  Iterator4 false_result,\n  Predicate pred,\n  Iterator5 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<int> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<int> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using iterator  = typename thrust::device_vector<int>::iterator;\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1, 1>>>(\n    exec,\n    data.begin(),\n    data.end(),\n    stencil.begin(),\n    true_results.begin(),\n    false_results.begin(),\n    is_even<T>(),\n    iterators.begin());\n  cudaError_t const err = cudaDeviceSynchronize();\n  ASSERT_EQUAL(cudaSuccess, err);\n\n  pair_type ends = iterators[0];\n\n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] = 1;\n  true_ref[1] = 1;\n\n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] = 0;\n  false_ref[1] = 0;\n  false_ref[2] = 0;\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestStablePartitionCopyStencilDeviceSeq()\n{\n  TestStablePartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceSeq);\n\nvoid TestStablePartitionCopyStencilDeviceDevice()\n{\n  TestStablePartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceDevice);\n\nvoid TestStablePartitionCopyStencilDeviceNoSync()\n{\n  TestStablePartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceNoSync);\n\nvoid TestPartitionIfWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  thrust::counting_iterator<offset_t> stencil(offset_t{0});\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth      = 1000000;\n  offset_t expected_num_written = (num_items + match_every_nth - 1) / match_every_nth;\n\n  // Tests input is correctly dereferenced for large offsets and selected items are correctly written\n  {\n    // Initialize input\n    thrust::device_vector<offset_t> partitioned_out(expected_num_written);\n\n    // Run test\n    constexpr bool negate_matches = false;\n    auto select_op                = mod_n<offset_t>{match_every_nth, negate_matches};\n    auto partitioned_out_ends =\n      thrust::stable_partition_copy(begin, end, partitioned_out.begin(), thrust::make_discard_iterator(), select_op);\n    const auto selected_out_end = partitioned_out_ends.first;\n\n    // Ensure number of selected items are correct\n    const offset_t num_selected_out =\n      static_cast<offset_t>(::cuda::std::distance(partitioned_out.begin(), selected_out_end));\n    ASSERT_EQUAL(num_selected_out, expected_num_written);\n    partitioned_out.resize(expected_num_written);\n\n    // Ensure selected items are correct\n    auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n    bool all_results_correct = thrust::equal(partitioned_out.begin(), partitioned_out.end(), expected_out_it);\n    ASSERT_EQUAL(all_results_correct, true);\n  }\n\n  // Tests input is correctly dereferenced for large offsets and rejected items are correctly written\n  {\n    // Initialize input\n    thrust::device_vector<offset_t> partitioned_out(expected_num_written);\n\n    // Run test\n    constexpr bool negate_matches = true;\n    auto select_op                = mod_n<offset_t>{match_every_nth, negate_matches};\n    const auto partitioned_out_ends =\n      thrust::stable_partition_copy(begin, end, thrust::make_discard_iterator(), partitioned_out.begin(), select_op);\n    const auto rejected_out_end = partitioned_out_ends.second;\n\n    // Ensure number of rejected items are correct\n    const offset_t num_rejected_out =\n      static_cast<offset_t>(::cuda::std::distance(partitioned_out.begin(), rejected_out_end));\n    ASSERT_EQUAL(num_rejected_out, expected_num_written);\n    partitioned_out.resize(expected_num_written);\n\n    // Ensure rejected items are correct\n    auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n    bool all_results_correct = thrust::equal(partitioned_out.begin(), partitioned_out.end(), expected_out_it);\n    ASSERT_EQUAL(all_results_correct, true);\n  }\n}\n\nvoid TestPartitionIfWithLargeNumberOfItems()\n{\n  TestPartitionIfWithMagnitude(30);\n  // These require 64-bit dispatches even when magnitude < 32.\n#  ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestPartitionIfWithMagnitude(31);\n  TestPartitionIfWithMagnitude(32);\n  TestPartitionIfWithMagnitude(33);\n#  endif\n}\nDECLARE_UNITTEST(TestPartitionIfWithLargeNumberOfItems);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator iter = thrust::partition(streampolicy, data.begin(), data.end(), is_even<T>());\n\n  Vector ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(iter - data.begin(), 2);\n  ASSERT_EQUAL(data, ref);\n\n  cudaStreamDestroy(s);\n}\n\nvoid TestPartitionCudaStreamsSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsSync);\n\nvoid TestPartitionCudaStreamsNoSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsNoSync);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/count.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/discard_iterator.h>\n#include <thrust/partition.h>\n\n#include \"thrust/detail/raw_pointer_cast.h\"\n#include <unittest/unittest.h>\n\ntemplate <typename T>\nstruct is_even\n{\n  _CCCL_HOST_DEVICE bool operator()(T x) const\n  {\n    return ((int) x % 2) == 0;\n  }\n};\n\ntemplate <typename T>\nstruct mod_n\n{\n  T mod;\n  bool negate;\n  _CCCL_HOST_DEVICE bool operator()(T x)\n  {\n    return (x % mod == 0) ? (!negate) : negate;\n  }\n};\n\ntemplate <typename T>\nstruct multiply_n\n{\n  T multiplier;\n  _CCCL_HOST_DEVICE T operator()(T x)\n  {\n    return x * multiplier;\n  }\n};\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::partition(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestPartitionDeviceSeq()\n{\n  TestPartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionDeviceSeq);\n\nvoid TestPartitionDeviceDevice()\n{\n  TestPartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionDeviceDevice);\n\nvoid TestPartitionDeviceNoSync()\n{\n  TestPartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void partition_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::partition(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionStencilDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestPartitionStencilDeviceSeq()\n{\n  TestPartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceSeq);\n\nvoid TestPartitionStencilDeviceDevice()\n{\n  TestPartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceDevice);\n\nvoid TestPartitionStencilDeviceNoSync()\n{\n  TestPartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 true_result,\n  Iterator3 false_result,\n  Predicate pred,\n  Iterator4 result)\n{\n  *result = thrust::partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCopyDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] = 2;\n  true_ref[1] = 2;\n\n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] = 1;\n  false_ref[1] = 1;\n  false_ref[2] = 1;\n\n  pair_type ends = iterators[0];\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestPartitionCopyDeviceSeq()\n{\n  TestPartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceSeq);\n\nvoid TestPartitionCopyDeviceDevice()\n{\n  TestPartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceDevice);\n\nvoid TestPartitionCopyDeviceNoSync()\n{\n  TestPartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate,\n          typename Iterator5>\n__global__ void partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 true_result,\n  Iterator4 false_result,\n  Predicate pred,\n  Iterator5 result)\n{\n  *result = thrust::partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<int> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<int> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using iterator  = typename thrust::device_vector<int>::iterator;\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  partition_copy_kernel<<<1, 1>>>(\n    exec,\n    data.begin(),\n    data.end(),\n    stencil.begin(),\n    true_results.begin(),\n    false_results.begin(),\n    is_even<T>(),\n    iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  pair_type ends = iterators[0];\n\n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] = 1;\n  true_ref[1] = 1;\n\n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] = 0;\n  false_ref[1] = 0;\n  false_ref[2] = 0;\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestPartitionCopyStencilDeviceSeq()\n{\n  TestPartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceSeq);\n\nvoid TestPartitionCopyStencilDeviceDevice()\n{\n  TestPartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceDevice);\n\nvoid TestPartitionCopyStencilDeviceNoSync()\n{\n  TestPartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCopyStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Predicate, typename Iterator2>\n__global__ void\nstable_partition_kernel(ExecutionPolicy exec, Iterator1 first, Iterator1 last, Predicate pred, Iterator2 result)\n{\n  *result = thrust::stable_partition(exec, first, last, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestStablePartitionDeviceSeq()\n{\n  TestStablePartitionDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceSeq);\n\nvoid TestStablePartitionDeviceDevice()\n{\n  TestStablePartitionDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceDevice);\n\nvoid TestStablePartitionDeviceNoSync()\n{\n  TestStablePartitionDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionDeviceNoSync);\n\ntemplate <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate, typename Iterator3>\n__global__ void stable_partition_kernel(\n  ExecutionPolicy exec, Iterator1 first, Iterator1 last, Iterator2 stencil_first, Predicate pred, Iterator3 result)\n{\n  *result = thrust::stable_partition(exec, first, last, stencil_first, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionStencilDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = typename thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<T> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<iterator> result(1);\n\n  stable_partition_kernel<<<1, 1>>>(exec, data.begin(), data.end(), stencil.begin(), is_even<T>(), result.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> ref(5);\n  ref[0] = 1;\n  ref[1] = 1;\n  ref[2] = 0;\n  ref[3] = 0;\n  ref[4] = 0;\n\n  ASSERT_EQUAL(2, (iterator) result[0] - data.begin());\n  ASSERT_EQUAL(ref, data);\n}\n\nvoid TestStablePartitionStencilDeviceSeq()\n{\n  TestStablePartitionStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceSeq);\n\nvoid TestStablePartitionStencilDeviceDevice()\n{\n  TestStablePartitionStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceDevice);\n\nvoid TestStablePartitionStencilDeviceNoSync()\n{\n  TestStablePartitionStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionStencilDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Predicate,\n          typename Iterator4>\n__global__ void stable_partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 true_result,\n  Iterator3 false_result,\n  Predicate pred,\n  Iterator4 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionCopyDevice(ExecutionPolicy exec)\n{\n  using T        = int;\n  using iterator = thrust::device_vector<T>::iterator;\n\n  thrust::device_vector<T> data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1, 1>>>(\n    exec, data.begin(), data.end(), true_results.begin(), false_results.begin(), is_even<T>(), iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  thrust::device_vector<T> true_ref(2);\n  true_ref[0] = 2;\n  true_ref[1] = 2;\n\n  thrust::device_vector<T> false_ref(3);\n  false_ref[0] = 1;\n  false_ref[1] = 1;\n  false_ref[2] = 1;\n\n  pair_type ends = iterators[0];\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestStablePartitionCopyDeviceSeq()\n{\n  TestStablePartitionCopyDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceSeq);\n\nvoid TestStablePartitionCopyDeviceDevice()\n{\n  TestStablePartitionCopyDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceDevice);\n\nvoid TestStablePartitionCopyDeviceNoSync()\n{\n  TestStablePartitionCopyDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyDeviceNoSync);\n\ntemplate <typename ExecutionPolicy,\n          typename Iterator1,\n          typename Iterator2,\n          typename Iterator3,\n          typename Iterator4,\n          typename Predicate,\n          typename Iterator5>\n__global__ void stable_partition_copy_kernel(\n  ExecutionPolicy exec,\n  Iterator1 first,\n  Iterator1 last,\n  Iterator2 stencil_first,\n  Iterator3 true_result,\n  Iterator4 false_result,\n  Predicate pred,\n  Iterator5 result)\n{\n  *result = thrust::stable_partition_copy(exec, first, last, stencil_first, true_result, false_result, pred);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestStablePartitionCopyStencilDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<int> data(5);\n  data[0] = 0;\n  data[1] = 1;\n  data[2] = 0;\n  data[3] = 0;\n  data[4] = 1;\n\n  thrust::device_vector<int> stencil(5);\n  stencil[0] = 1;\n  stencil[1] = 2;\n  stencil[2] = 1;\n  stencil[3] = 1;\n  stencil[4] = 2;\n\n  thrust::device_vector<int> true_results(2);\n  thrust::device_vector<int> false_results(3);\n\n  using iterator  = typename thrust::device_vector<int>::iterator;\n  using pair_type = cuda::std::pair<iterator, iterator>;\n  thrust::device_vector<pair_type> iterators(1);\n\n  stable_partition_copy_kernel<<<1, 1>>>(\n    exec,\n    data.begin(),\n    data.end(),\n    stencil.begin(),\n    true_results.begin(),\n    false_results.begin(),\n    is_even<T>(),\n    iterators.begin());\n  hipError_t const err = hipDeviceSynchronize();\n  ASSERT_EQUAL(hipSuccess, err);\n\n  pair_type ends = iterators[0];\n\n  thrust::device_vector<int> true_ref(2);\n  true_ref[0] = 1;\n  true_ref[1] = 1;\n\n  thrust::device_vector<int> false_ref(3);\n  false_ref[0] = 0;\n  false_ref[1] = 0;\n  false_ref[2] = 0;\n\n  ASSERT_EQUAL(2, ends.first - true_results.begin());\n  ASSERT_EQUAL(3, ends.second - false_results.begin());\n  ASSERT_EQUAL(true_ref, true_results);\n  ASSERT_EQUAL(false_ref, false_results);\n}\n\nvoid TestStablePartitionCopyStencilDeviceSeq()\n{\n  TestStablePartitionCopyStencilDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceSeq);\n\nvoid TestStablePartitionCopyStencilDeviceDevice()\n{\n  TestStablePartitionCopyStencilDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceDevice);\n\nvoid TestStablePartitionCopyStencilDeviceNoSync()\n{\n  TestStablePartitionCopyStencilDevice(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestStablePartitionCopyStencilDeviceNoSync);\n\nvoid TestPartitionIfWithMagnitude(int magnitude)\n{\n  using offset_t = std::size_t;\n\n  // Prepare input\n  offset_t num_items = offset_t{1ull} << magnitude;\n  thrust::counting_iterator<offset_t> begin(offset_t{0});\n  auto end = begin + num_items;\n  thrust::counting_iterator<offset_t> stencil(offset_t{0});\n  ASSERT_EQUAL(static_cast<offset_t>(::cuda::std::distance(begin, end)), num_items);\n\n  // Run algorithm on large number of items\n  offset_t match_every_nth      = 1000000;\n  offset_t expected_num_written = (num_items + match_every_nth - 1) / match_every_nth;\n\n  // Tests input is correctly dereferenced for large offsets and selected items are correctly written\n  {\n    // Initialize input\n    thrust::device_vector<offset_t> partitioned_out(expected_num_written);\n\n    // Run test\n    constexpr bool negate_matches = false;\n    auto select_op                = mod_n<offset_t>{match_every_nth, negate_matches};\n    auto partitioned_out_ends =\n      thrust::stable_partition_copy(begin, end, partitioned_out.begin(), thrust::make_discard_iterator(), select_op);\n    const auto selected_out_end = partitioned_out_ends.first;\n\n    // Ensure number of selected items are correct\n    const offset_t num_selected_out =\n      static_cast<offset_t>(::cuda::std::distance(partitioned_out.begin(), selected_out_end));\n    ASSERT_EQUAL(num_selected_out, expected_num_written);\n    partitioned_out.resize(expected_num_written);\n\n    // Ensure selected items are correct\n    auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n    bool all_results_correct = thrust::equal(partitioned_out.begin(), partitioned_out.end(), expected_out_it);\n    ASSERT_EQUAL(all_results_correct, true);\n  }\n\n  // Tests input is correctly dereferenced for large offsets and rejected items are correctly written\n  {\n    // Initialize input\n    thrust::device_vector<offset_t> partitioned_out(expected_num_written);\n\n    // Run test\n    constexpr bool negate_matches = true;\n    auto select_op                = mod_n<offset_t>{match_every_nth, negate_matches};\n    const auto partitioned_out_ends =\n      thrust::stable_partition_copy(begin, end, thrust::make_discard_iterator(), partitioned_out.begin(), select_op);\n    const auto rejected_out_end = partitioned_out_ends.second;\n\n    // Ensure number of rejected items are correct\n    const offset_t num_rejected_out =\n      static_cast<offset_t>(::cuda::std::distance(partitioned_out.begin(), rejected_out_end));\n    ASSERT_EQUAL(num_rejected_out, expected_num_written);\n    partitioned_out.resize(expected_num_written);\n\n    // Ensure rejected items are correct\n    auto expected_out_it     = thrust::make_transform_iterator(begin, multiply_n<offset_t>{match_every_nth});\n    bool all_results_correct = thrust::equal(partitioned_out.begin(), partitioned_out.end(), expected_out_it);\n    ASSERT_EQUAL(all_results_correct, true);\n  }\n}\n\nvoid TestPartitionIfWithLargeNumberOfItems()\n{\n  TestPartitionIfWithMagnitude(30);\n  // These require 64-bit dispatches even when magnitude < 32.\n#  ifndef THRUST_FORCE_32_BIT_OFFSET_TYPE\n  TestPartitionIfWithMagnitude(31);\n  TestPartitionIfWithMagnitude(32);\n  TestPartitionIfWithMagnitude(33);\n#  endif\n}\nDECLARE_UNITTEST(TestPartitionIfWithLargeNumberOfItems);\n#endif\n\ntemplate <typename ExecutionPolicy>\nvoid TestPartitionCudaStreams(ExecutionPolicy policy)\n{\n  using Vector   = thrust::device_vector<int>;\n  using T        = Vector::value_type;\n  using Iterator = Vector::iterator;\n\n  Vector data(5);\n  data[0] = 1;\n  data[1] = 2;\n  data[2] = 1;\n  data[3] = 1;\n  data[4] = 2;\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  auto streampolicy = policy.on(s);\n\n  Iterator iter = thrust::partition(streampolicy, data.begin(), data.end(), is_even<T>());\n\n  Vector ref(5);\n  ref[0] = 2;\n  ref[1] = 2;\n  ref[2] = 1;\n  ref[3] = 1;\n  ref[4] = 1;\n\n  ASSERT_EQUAL(iter - data.begin(), 2);\n  ASSERT_EQUAL(data, ref);\n\n  hipStreamDestroy(s);\n}\n\nvoid TestPartitionCudaStreamsSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsSync);\n\nvoid TestPartitionCudaStreamsNoSync()\n{\n  TestPartitionCudaStreams(thrust::cuda::par_nosync);\n}\nDECLARE_UNITTEST(TestPartitionCudaStreamsNoSync);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/partition.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/logical.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void all_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::all_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestAllOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  v[1] = 0;\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\nvoid TestAllOfDeviceSeq()\n{\n  TestAllOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAllOfDeviceSeq);\n\nvoid TestAllOfDeviceDevice()\n{\n  TestAllOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAllOfDeviceDevice);\n#endif\n\nvoid TestAllOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAllOfCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void any_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::any_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestAnyOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  v[1] = 0;\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\nvoid TestAnyOfDeviceSeq()\n{\n  TestAnyOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceSeq);\n\nvoid TestAnyOfDeviceDevice()\n{\n  TestAnyOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceDevice);\n#endif\n\nvoid TestAnyOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), false);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAnyOfCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void none_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::none_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestNoneOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  v[1] = 0;\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestNoneOfDeviceSeq()\n{\n  TestNoneOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceSeq);\n\nvoid TestNoneOfDeviceDevice()\n{\n  TestNoneOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceDevice);\n#endif\n\nvoid TestNoneOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), true);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestNoneOfCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/functional.h>\n#include <thrust/logical.h>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void all_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::all_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestAllOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  v[1] = 0;\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  all_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\nvoid TestAllOfDeviceSeq()\n{\n  TestAllOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAllOfDeviceSeq);\n\nvoid TestAllOfDeviceDevice()\n{\n  TestAllOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAllOfDeviceDevice);\n#endif\n\nvoid TestAllOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::all_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAllOfCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void any_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::any_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestAnyOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  v[1] = 0;\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  any_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n}\n\nvoid TestAnyOfDeviceSeq()\n{\n  TestAnyOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceSeq);\n\nvoid TestAnyOfDeviceDevice()\n{\n  TestAnyOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestAnyOfDeviceDevice);\n#endif\n\nvoid TestAnyOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), true);\n\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::any_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), false);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestAnyOfCudaStreams);\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename Function, typename Iterator2>\n__global__ void none_of_kernel(ExecutionPolicy exec, Iterator first, Iterator last, Function f, Iterator2 result)\n{\n  *result = thrust::none_of(exec, first, last, f);\n}\n\ntemplate <typename ExecutionPolicy>\nvoid TestNoneOfDevice(ExecutionPolicy exec)\n{\n  using T = int;\n\n  thrust::device_vector<T> v(3, 1);\n  thrust::device_vector<bool> result(1);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  v[1] = 0;\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin(), v.end(), ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(false, result[0]);\n\n  none_of_kernel<<<1, 1>>>(exec, v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}, result.begin());\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(true, result[0]);\n}\n\nvoid TestNoneOfDeviceSeq()\n{\n  TestNoneOfDevice(thrust::seq);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceSeq);\n\nvoid TestNoneOfDeviceDevice()\n{\n  TestNoneOfDevice(thrust::device);\n}\nDECLARE_UNITTEST(TestNoneOfDeviceDevice);\n#endif\n\nvoid TestNoneOfCudaStreams()\n{\n  using Vector = thrust::device_vector<int>;\n  using T      = Vector::value_type;\n\n  Vector v(3, T{1});\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  v[1] = 0;\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin(), v.end(), ::cuda::std::identity{}), false);\n\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 0, ::cuda::std::identity{}), true);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 1, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 2, ::cuda::std::identity{}), false);\n  ASSERT_EQUAL(thrust::none_of(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 2, ::cuda::std::identity{}), true);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestNoneOfCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/logical.cu"}
{"input": "#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value)\n{\n  thrust::fill(exec, first, last, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestFillDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::fill(h_data.begin() + std::min((size_t) 1, n), h_data.begin() + std::min((size_t) 3, n), (T) 0);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin() + std::min((size_t) 1, n), d_data.begin() + std::min((size_t) 3, n), (T) 0);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 117, n), h_data.begin() + std::min((size_t) 367, n), (T) 1);\n\n  fill_kernel<<<1, 1>>>(\n    exec, d_data.begin() + std::min((size_t) 117, n), d_data.begin() + std::min((size_t) 367, n), (T) 1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 8, n), h_data.begin() + std::min((size_t) 259, n), (T) 2);\n\n  fill_kernel<<<1, 1>>>(\n    exec, d_data.begin() + std::min((size_t) 8, n), d_data.begin() + std::min((size_t) 259, n), (T) 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 3, n), h_data.end(), (T) 3);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin() + std::min((size_t) 3, n), d_data.end(), (T) 3);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin(), h_data.end(), (T) 4);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), (T) 4);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestFillDeviceSeq(size_t n)\n{\n  TestFillDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceSeq);\n\ntemplate <typename T>\nvoid TestFillDeviceDevice(size_t n)\n{\n  TestFillDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename T>\n__global__ void fill_n_kernel(ExecutionPolicy exec, Iterator first, Size n, T value)\n{\n  thrust::fill_n(exec, first, n, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestFillNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  size_t begin_offset = std::min<size_t>(1, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 3, n) - begin_offset, (T) 0);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 3, n) - begin_offset, (T) 0);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(117, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 367, n) - begin_offset, (T) 1);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 367, n) - begin_offset, (T) 1);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(8, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 259, n) - begin_offset, (T) 2);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 259, n) - begin_offset, (T) 2);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(3, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, h_data.size() - begin_offset, (T) 3);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, d_data.size() - begin_offset, (T) 3);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill_n(h_data.begin(), h_data.size(), (T) 4);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.size(), (T) 4);\n  {\n    cudaError_t const err = cudaDeviceSynchronize();\n    ASSERT_EQUAL(cudaSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestFillNDeviceSeq(size_t n)\n{\n  TestFillNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceSeq);\n\ntemplate <typename T>\nvoid TestFillNDeviceDevice(size_t n)\n{\n  TestFillNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceDevice);\n#endif\n\nvoid TestFillCudaStreams()\n{\n  thrust::device_vector<int> v{0, 1, 2, 3, 4};\n\n  cudaStream_t s;\n  cudaStreamCreate(&s);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 4, 7);\n  cudaStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 7, 7, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 3, 8);\n  cudaStreamSynchronize(s);\n\n  ref = {8, 8, 8, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 2, v.end(), 9);\n  cudaStreamSynchronize(s);\n\n  ref = {8, 8, 9, 9, 9};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin(), v.end(), 1);\n  cudaStreamSynchronize(s);\n\n  ref = {1, 1, 1, 1, 1};\n  ASSERT_EQUAL(v, ref);\n\n  cudaStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFillCudaStreams);\n", "output": "#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n\n#include <algorithm>\n\n#include <unittest/unittest.h>\n\n#ifdef THRUST_TEST_DEVICE_SIDE\ntemplate <typename ExecutionPolicy, typename Iterator, typename T>\n__global__ void fill_kernel(ExecutionPolicy exec, Iterator first, Iterator last, T value)\n{\n  thrust::fill(exec, first, last, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestFillDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  thrust::fill(h_data.begin() + std::min((size_t) 1, n), h_data.begin() + std::min((size_t) 3, n), (T) 0);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin() + std::min((size_t) 1, n), d_data.begin() + std::min((size_t) 3, n), (T) 0);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 117, n), h_data.begin() + std::min((size_t) 367, n), (T) 1);\n\n  fill_kernel<<<1, 1>>>(\n    exec, d_data.begin() + std::min((size_t) 117, n), d_data.begin() + std::min((size_t) 367, n), (T) 1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 8, n), h_data.begin() + std::min((size_t) 259, n), (T) 2);\n\n  fill_kernel<<<1, 1>>>(\n    exec, d_data.begin() + std::min((size_t) 8, n), d_data.begin() + std::min((size_t) 259, n), (T) 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin() + std::min((size_t) 3, n), h_data.end(), (T) 3);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin() + std::min((size_t) 3, n), d_data.end(), (T) 3);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill(h_data.begin(), h_data.end(), (T) 4);\n\n  fill_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.end(), (T) 4);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestFillDeviceSeq(size_t n)\n{\n  TestFillDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceSeq);\n\ntemplate <typename T>\nvoid TestFillDeviceDevice(size_t n)\n{\n  TestFillDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillDeviceDevice);\n\ntemplate <typename ExecutionPolicy, typename Iterator, typename Size, typename T>\n__global__ void fill_n_kernel(ExecutionPolicy exec, Iterator first, Size n, T value)\n{\n  thrust::fill_n(exec, first, n, value);\n}\n\ntemplate <typename T, typename ExecutionPolicy>\nvoid TestFillNDevice(ExecutionPolicy exec, size_t n)\n{\n  thrust::host_vector<T> h_data   = unittest::random_integers<T>(n);\n  thrust::device_vector<T> d_data = h_data;\n\n  size_t begin_offset = std::min<size_t>(1, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 3, n) - begin_offset, (T) 0);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 3, n) - begin_offset, (T) 0);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(117, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 367, n) - begin_offset, (T) 1);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 367, n) - begin_offset, (T) 1);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(8, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, std::min((size_t) 259, n) - begin_offset, (T) 2);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, std::min((size_t) 259, n) - begin_offset, (T) 2);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  begin_offset = std::min<size_t>(3, n);\n\n  thrust::fill_n(h_data.begin() + begin_offset, h_data.size() - begin_offset, (T) 3);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin() + begin_offset, d_data.size() - begin_offset, (T) 3);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n\n  thrust::fill_n(h_data.begin(), h_data.size(), (T) 4);\n\n  fill_n_kernel<<<1, 1>>>(exec, d_data.begin(), d_data.size(), (T) 4);\n  {\n    hipError_t const err = hipDeviceSynchronize();\n    ASSERT_EQUAL(hipSuccess, err);\n  }\n\n  ASSERT_EQUAL(h_data, d_data);\n}\n\ntemplate <typename T>\nvoid TestFillNDeviceSeq(size_t n)\n{\n  TestFillNDevice<T>(thrust::seq, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceSeq);\n\ntemplate <typename T>\nvoid TestFillNDeviceDevice(size_t n)\n{\n  TestFillNDevice<T>(thrust::device, n);\n}\nDECLARE_VARIABLE_UNITTEST(TestFillNDeviceDevice);\n#endif\n\nvoid TestFillCudaStreams()\n{\n  thrust::device_vector<int> v{0, 1, 2, 3, 4};\n\n  hipStream_t s;\n  hipStreamCreate(&s);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 1, v.begin() + 4, 7);\n  hipStreamSynchronize(s);\n\n  thrust::device_vector<int> ref{0, 7, 7, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 0, v.begin() + 3, 8);\n  hipStreamSynchronize(s);\n\n  ref = {8, 8, 8, 7, 4};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin() + 2, v.end(), 9);\n  hipStreamSynchronize(s);\n\n  ref = {8, 8, 9, 9, 9};\n  ASSERT_EQUAL(v, ref);\n\n  thrust::fill(thrust::cuda::par.on(s), v.begin(), v.end(), 1);\n  hipStreamSynchronize(s);\n\n  ref = {1, 1, 1, 1, 1};\n  ASSERT_EQUAL(v, ref);\n\n  hipStreamDestroy(s);\n}\nDECLARE_UNITTEST(TestFillCudaStreams);\n", "source": "/kaggle/working/cccl/thrust/testing/cuda/fill.cu"}
{"input": "#include <thrust/system/cuda/memory.h>\n\n#include <iostream>\n#include <numeric>\n\n#include <cuda_runtime.h>\n\n#include <unittest/cuda/testframework.h>\n#include <unittest/testframework.h>\n\n__global__ void dummy_kernel() {}\n\nbool binary_exists_for_current_device()\n{\n  // check against the dummy_kernel\n  // if we're unable to get the attributes, then\n  // we didn't compile a binary compatible with the current device\n  cudaFuncAttributes attr;\n  cudaError_t error = cudaFuncGetAttributes(&attr, dummy_kernel);\n\n  // clear the CUDA global error state if we just set it, so that\n  // check_cuda_error doesn't complain\n  if (cudaSuccess != error)\n  {\n    (void) cudaGetLastError();\n  }\n\n  return cudaSuccess == error;\n}\n\nvoid list_devices()\n{\n  int deviceCount;\n  cudaGetDeviceCount(&deviceCount);\n  if (deviceCount == 0)\n  {\n    std::cout << \"There is no device supporting CUDA\" << '\\n';\n  }\n\n  int selected_device;\n  cudaGetDevice(&selected_device);\n\n  for (int dev = 0; dev < deviceCount; ++dev)\n  {\n    cudaDeviceProp deviceProp;\n    cudaGetDeviceProperties(&deviceProp, dev);\n\n    if (dev == 0)\n    {\n      if (deviceProp.major == 9999 && deviceProp.minor == 9999)\n      {\n        std::cout << \"There is no device supporting CUDA.\" << '\\n';\n      }\n      else if (deviceCount == 1)\n      {\n        std::cout << \"There is 1 device supporting CUDA\" << '\\n';\n      }\n      else\n      {\n        std::cout << \"There are \" << deviceCount << \" devices supporting CUDA\" << '\\n';\n      }\n    }\n\n    std::cout << \"\\nDevice \" << dev << \": \\\"\" << deviceProp.name << \"\\\"\";\n    if (dev == selected_device)\n    {\n      std::cout << \"  [SELECTED]\";\n    }\n    std::cout << '\\n';\n\n    std::cout << \"  Major revision number:                         \" << deviceProp.major << '\\n';\n    std::cout << \"  Minor revision number:                         \" << deviceProp.minor << '\\n';\n    std::cout << \"  Total amount of global memory:                 \" << deviceProp.totalGlobalMem << \" bytes\" << '\\n';\n  }\n  std::cout << '\\n';\n}\n\n// provide next, which c++03 doesn't have\ntemplate <typename Iterator>\nIterator my_next(Iterator iter)\n{\n  return ++iter;\n}\n\nstd::vector<int> CUDATestDriver::target_devices(const ArgumentMap& kwargs)\n{\n  std::vector<int> result;\n\n  // by default, test all devices in the system (device id -1)\n  int device_id = kwargs.count(\"device\") ? atoi(kwargs.find(\"device\")->second.c_str()) : -1;\n\n  if (device_id < 0)\n  {\n    // target all devices in the system\n    int count = 0;\n    cudaGetDeviceCount(&count);\n\n    result.resize(count);\n    std::iota(result.begin(), result.end(), 0);\n  }\n  else\n  {\n    // target the specified device\n    result = std::vector<int>(1, device_id);\n  }\n\n  return result;\n}\n\nbool CUDATestDriver::check_cuda_error(bool concise)\n{\n  cudaError_t const error = cudaGetLastError();\n  if (cudaSuccess != error)\n  {\n    if (!concise)\n    {\n      std::cout << \"[ERROR] CUDA error detected before running tests: [\" << std::string(cudaGetErrorName(error)) << \": \"\n                << std::string(cudaGetErrorString(error)) << \"]\" << '\\n';\n    }\n  }\n\n  return cudaSuccess != error;\n}\n\nbool CUDATestDriver::post_test_smoke_check(const UnitTest& test, bool concise)\n{\n  cudaError_t const error = cudaDeviceSynchronize();\n  if (cudaSuccess != error)\n  {\n    if (!concise)\n    {\n      std::cout\n        << \"\\t[ERROR] CUDA error detected after running \" << test.name << \": [\" << std::string(cudaGetErrorName(error))\n        << \": \" << std::string(cudaGetErrorString(error)) << \"]\" << '\\n';\n    }\n  }\n\n  return cudaSuccess == error;\n}\n\nbool CUDATestDriver::run_tests(const ArgumentSet& args, const ArgumentMap& kwargs)\n{\n  bool verbose = kwargs.count(\"verbose\");\n  bool concise = kwargs.count(\"concise\");\n\n  if (verbose && concise)\n  {\n    std::cout << \"--verbose and --concise cannot be used together\" << '\\n';\n    exit(EXIT_FAILURE);\n  }\n\n  // check error status before doing anything\n  if (check_cuda_error(concise))\n  {\n    return false;\n  }\n\n  bool result = true;\n\n  if (kwargs.count(\"verbose\"))\n  {\n    list_devices();\n  }\n\n  // figure out which devices to target\n  std::vector<int> devices = target_devices(kwargs);\n\n  // target each device\n  for (std::vector<int>::iterator device = devices.begin(); device != devices.end(); ++device)\n  {\n    cudaDeviceSynchronize();\n\n    // set the device\n    cudaSetDevice(*device);\n\n    // check if a binary exists for this device\n    // if none exists, skip the device silently unless this is the only one we're targeting\n    if (devices.size() > 1 && !binary_exists_for_current_device())\n    {\n      // note which device we're skipping\n      cudaDeviceProp deviceProp;\n      cudaGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Skipping Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << '\\n';\n\n      continue;\n    }\n\n    if (!concise)\n    {\n      // note which device we're testing\n      cudaDeviceProp deviceProp;\n      cudaGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Testing Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << '\\n';\n    }\n\n    // check error status before running any tests\n    if (check_cuda_error(concise))\n    {\n      return false;\n    }\n\n    // run tests\n    result &= UnitTestDriver::run_tests(args, kwargs);\n\n    if (!concise && my_next(device) != devices.end())\n    {\n      // provide some separation between the output of separate tests\n      std::cout << '\\n';\n    }\n  }\n\n  return result;\n}\n\nint CUDATestDriver::current_device_architecture() const\n{\n  int current = -1;\n  cudaGetDevice(&current);\n  cudaDeviceProp deviceProp;\n  cudaGetDeviceProperties(&deviceProp, current);\n\n  return 100 * deviceProp.major + 10 * deviceProp.minor;\n}\n\nUnitTestDriver& driver_instance(thrust::system::cuda::tag)\n{\n  static CUDATestDriver s_instance;\n  return s_instance;\n}\n", "output": "#include <thrust/system/cuda/memory.h>\n\n#include <iostream>\n#include <numeric>\n\n#include <hip/hip_runtime.h>\n\n#include <unittest/cuda/testframework.h>\n#include <unittest/testframework.h>\n\n__global__ void dummy_kernel() {}\n\nbool binary_exists_for_current_device()\n{\n  // check against the dummy_kernel\n  // if we're unable to get the attributes, then\n  // we didn't compile a binary compatible with the current device\n  hipFuncAttributes attr;\n  hipError_t error = hipFuncGetAttributes(&attr,reinterpret_cast<const void*>(dummy_kernel));\n\n  // clear the CUDA global error state if we just set it, so that\n  // check_cuda_error doesn't complain\n  if (hipSuccess != error)\n  {\n    (void) hipGetLastError();\n  }\n\n  return hipSuccess == error;\n}\n\nvoid list_devices()\n{\n  int deviceCount;\n  hipGetDeviceCount(&deviceCount);\n  if (deviceCount == 0)\n  {\n    std::cout << \"There is no device supporting CUDA\" << '\\n';\n  }\n\n  int selected_device;\n  hipGetDevice(&selected_device);\n\n  for (int dev = 0; dev < deviceCount; ++dev)\n  {\n    hipDeviceProp_t deviceProp;\n    hipGetDeviceProperties(&deviceProp, dev);\n\n    if (dev == 0)\n    {\n      if (deviceProp.major == 9999 && deviceProp.minor == 9999)\n      {\n        std::cout << \"There is no device supporting CUDA.\" << '\\n';\n      }\n      else if (deviceCount == 1)\n      {\n        std::cout << \"There is 1 device supporting CUDA\" << '\\n';\n      }\n      else\n      {\n        std::cout << \"There are \" << deviceCount << \" devices supporting CUDA\" << '\\n';\n      }\n    }\n\n    std::cout << \"\\nDevice \" << dev << \": \\\"\" << deviceProp.name << \"\\\"\";\n    if (dev == selected_device)\n    {\n      std::cout << \"  [SELECTED]\";\n    }\n    std::cout << '\\n';\n\n    std::cout << \"  Major revision number:                         \" << deviceProp.major << '\\n';\n    std::cout << \"  Minor revision number:                         \" << deviceProp.minor << '\\n';\n    std::cout << \"  Total amount of global memory:                 \" << deviceProp.totalGlobalMem << \" bytes\" << '\\n';\n  }\n  std::cout << '\\n';\n}\n\n// provide next, which c++03 doesn't have\ntemplate <typename Iterator>\nIterator my_next(Iterator iter)\n{\n  return ++iter;\n}\n\nstd::vector<int> CUDATestDriver::target_devices(const ArgumentMap& kwargs)\n{\n  std::vector<int> result;\n\n  // by default, test all devices in the system (device id -1)\n  int device_id = kwargs.count(\"device\") ? atoi(kwargs.find(\"device\")->second.c_str()) : -1;\n\n  if (device_id < 0)\n  {\n    // target all devices in the system\n    int count = 0;\n    hipGetDeviceCount(&count);\n\n    result.resize(count);\n    std::iota(result.begin(), result.end(), 0);\n  }\n  else\n  {\n    // target the specified device\n    result = std::vector<int>(1, device_id);\n  }\n\n  return result;\n}\n\nbool CUDATestDriver::check_cuda_error(bool concise)\n{\n  hipError_t const error = hipGetLastError();\n  if (hipSuccess != error)\n  {\n    if (!concise)\n    {\n      std::cout << \"[ERROR] CUDA error detected before running tests: [\" << std::string(hipGetErrorName(error)) << \": \"\n                << std::string(hipGetErrorString(error)) << \"]\" << '\\n';\n    }\n  }\n\n  return hipSuccess != error;\n}\n\nbool CUDATestDriver::post_test_smoke_check(const UnitTest& test, bool concise)\n{\n  hipError_t const error = hipDeviceSynchronize();\n  if (hipSuccess != error)\n  {\n    if (!concise)\n    {\n      std::cout\n        << \"\\t[ERROR] CUDA error detected after running \" << test.name << \": [\" << std::string(hipGetErrorName(error))\n        << \": \" << std::string(hipGetErrorString(error)) << \"]\" << '\\n';\n    }\n  }\n\n  return hipSuccess == error;\n}\n\nbool CUDATestDriver::run_tests(const ArgumentSet& args, const ArgumentMap& kwargs)\n{\n  bool verbose = kwargs.count(\"verbose\");\n  bool concise = kwargs.count(\"concise\");\n\n  if (verbose && concise)\n  {\n    std::cout << \"--verbose and --concise cannot be used together\" << '\\n';\n    exit(EXIT_FAILURE);\n  }\n\n  // check error status before doing anything\n  if (check_cuda_error(concise))\n  {\n    return false;\n  }\n\n  bool result = true;\n\n  if (kwargs.count(\"verbose\"))\n  {\n    list_devices();\n  }\n\n  // figure out which devices to target\n  std::vector<int> devices = target_devices(kwargs);\n\n  // target each device\n  for (std::vector<int>::iterator device = devices.begin(); device != devices.end(); ++device)\n  {\n    hipDeviceSynchronize();\n\n    // set the device\n    hipSetDevice(*device);\n\n    // check if a binary exists for this device\n    // if none exists, skip the device silently unless this is the only one we're targeting\n    if (devices.size() > 1 && !binary_exists_for_current_device())\n    {\n      // note which device we're skipping\n      hipDeviceProp_t deviceProp;\n      hipGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Skipping Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << '\\n';\n\n      continue;\n    }\n\n    if (!concise)\n    {\n      // note which device we're testing\n      hipDeviceProp_t deviceProp;\n      hipGetDeviceProperties(&deviceProp, *device);\n\n      std::cout << \"Testing Device \" << *device << \": \\\"\" << deviceProp.name << \"\\\"\" << '\\n';\n    }\n\n    // check error status before running any tests\n    if (check_cuda_error(concise))\n    {\n      return false;\n    }\n\n    // run tests\n    result &= UnitTestDriver::run_tests(args, kwargs);\n\n    if (!concise && my_next(device) != devices.end())\n    {\n      // provide some separation between the output of separate tests\n      std::cout << '\\n';\n    }\n  }\n\n  return result;\n}\n\nint CUDATestDriver::current_device_architecture() const\n{\n  int current = -1;\n  hipGetDevice(&current);\n  hipDeviceProp_t deviceProp;\n  hipGetDeviceProperties(&deviceProp, current);\n\n  return 100 * deviceProp.major + 10 * deviceProp.minor;\n}\n\nUnitTestDriver& driver_instance(thrust::system::cuda::tag)\n{\n  static CUDATestDriver s_instance;\n  return s_instance;\n}\n", "source": "/kaggle/working/cccl/thrust/testing/unittest/cuda/testframework.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// When compiling with nvrtcc, the __NVRTCC__ macro is defined.\n#ifndef __NVRTCC__\n#  error \"this file must be compiled with nvrtcc\"\n#endif\n\n// We need to guard includes when compiling device code with nvrtc.\n#ifndef __CUDACC_RTC__\n#  include <cassert>\n#  include <cstdio>\n#endif\n\n#define TO_STRING_HELPER(...) #__VA_ARGS__\n#define TO_STRING(...)        TO_STRING_HELPER(__VA_ARGS__)\n\n// Ordinary kernels work without any problems.\n__global__ void kernel()\n{\n  printf(\"[%d, %d]: Hello world from kernel!\\n\", blockIdx.x, threadIdx.x);\n}\n\n// For template kernels the situation is a bit more complicated. We use nvrtcAddNameExpression to make sure these\n// kernels are instantiated.\ntemplate <class T, int = 3>\n__global__ void template_kernel(int)\n{\n  printf(\"[%d, %d]: Hello world from template kernel!\\n\", blockIdx.x, threadIdx.x);\n\n  if (blockIdx.x == 0 && threadIdx.x == 0)\n  {\n    // __CUDA_ARCH_LIST__ behaves the same way as if we were compiling with nvcc.\n    printf(\"\\narch list: \" TO_STRING(__CUDA_ARCH_LIST__) \"\\n\");\n  }\n}\n\n// Host functions must not be visible to nvrtc.\n#ifndef __CUDACC_RTC__\nint main()\n{\n  // We can now call kernels compiled with nvrtc directly.\n  kernel<<<2, 2>>>();\n  if (cudaDeviceSynchronize() != cudaSuccess)\n  {\n    return 1;\n  }\n\n  printf(\"\\n\");\n\n  template_kernel<void><<<3, 1>>>(0);\n  if (cudaDeviceSynchronize() != cudaSuccess)\n  {\n    return 1;\n  }\n}\n#endif\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// When compiling with nvrtcc, the __NVRTCC__ macro is defined.\n#include \"hip/hip_runtime.h\"\n#ifndef __NVRTCC__\n#  error \"this file must be compiled with nvrtcc\"\n#endif\n\n// We need to guard includes when compiling device code with nvrtc.\n#ifndef __CUDACC_RTC__\n#  include <cassert>\n#  include <cstdio>\n#endif\n\n#define TO_STRING_HELPER(...) #__VA_ARGS__\n#define TO_STRING(...)        TO_STRING_HELPER(__VA_ARGS__)\n\n// Ordinary kernels work without any problems.\n__global__ void kernel()\n{\n  printf(\"[%d, %d]: Hello world from kernel!\\n\", blockIdx.x, threadIdx.x);\n}\n\n// For template kernels the situation is a bit more complicated. We use nvrtcAddNameExpression to make sure these\n// kernels are instantiated.\ntemplate <class T, int = 3>\n__global__ void template_kernel(int)\n{\n  printf(\"[%d, %d]: Hello world from template kernel!\\n\", blockIdx.x, threadIdx.x);\n\n  if (blockIdx.x == 0 && threadIdx.x == 0)\n  {\n    // __CUDA_ARCH_LIST__ behaves the same way as if we were compiling with nvcc.\n    printf(\"\\narch list: \" TO_STRING(__CUDA_ARCH_LIST__) \"\\n\");\n  }\n}\n\n// Host functions must not be visible to nvrtc.\n#ifndef __CUDACC_RTC__\nint main()\n{\n  // We can now call kernels compiled with nvrtc directly.\n  kernel<<<2, 2>>>();\n  if (hipDeviceSynchronize() != hipSuccess)\n  {\n    return 1;\n  }\n\n  printf(\"\\n\");\n\n  template_kernel<void><<<3, 1>>>(0);\n  if (hipDeviceSynchronize() != hipSuccess)\n  {\n    return 1;\n  }\n}\n#endif\n", "source": "/kaggle/working/cccl/nvrtcc/examples/demo.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"../common/check_predefined_macros.h\"\n\n#if !defined(__CUDACC_RTC__)\n#  include <cassert>\n#endif // !__CUDACC_RTC__\n\n__managed__ int proof_var;\n\n__global__ void proof_kernel()\n{\n#if defined(__CUDACC_RTC__)\n  proof_var = 1;\n#else // ^^^ __CUDACC_RTC__ ^^^ / vvv !__CUDACC_RTC__ vvv\n  proof_var = -1;\n#endif // ^^^ !__CUDACC_RTC__ ^^^\n}\n\n#if !defined(__CUDACC_RTC__)\nint main()\n{\n  proof_var = 0;\n\n  proof_kernel<<<1, 1>>>();\n  assert(cudaDeviceSynchronize() == cudaSuccess);\n\n#  if defined(__NVRTCC_USE_NVRTC__)\n  assert(proof_var == 1);\n#  else // ^^^ __NVRTCC_USE_NVRTC__ ^^^ / vvv !__NVRTCC_USE_NVRTC__ vvv\n  assert(proof_var == -1);\n#  endif // ^^^ !__NVRTCC_USE_NVRTC__ ^^^\n}\n#endif // ^^^ !__CUDACC_RTC__ ^^^\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/check_predefined_macros.h\"\n\n#if !defined(__CUDACC_RTC__)\n#  include <cassert>\n#endif // !__CUDACC_RTC__\n\n__managed__ int proof_var;\n\n__global__ void proof_kernel()\n{\n#if defined(__CUDACC_RTC__)\n  proof_var = 1;\n#else // ^^^ __CUDACC_RTC__ ^^^ / vvv !__CUDACC_RTC__ vvv\n  proof_var = -1;\n#endif // ^^^ !__CUDACC_RTC__ ^^^\n}\n\n#if !defined(__CUDACC_RTC__)\nint main()\n{\n  proof_var = 0;\n\n  proof_kernel<<<1, 1>>>();\n  assert(hipDeviceSynchronize() == hipSuccess);\n\n#  if defined(__NVRTCC_USE_NVRTC__)\n  assert(proof_var == 1);\n#  else // ^^^ __NVRTCC_USE_NVRTC__ ^^^ / vvv !__NVRTCC_USE_NVRTC__ vvv\n  assert(proof_var == -1);\n#  endif // ^^^ !__NVRTCC_USE_NVRTC__ ^^^\n}\n#endif // ^^^ !__CUDACC_RTC__ ^^^\n", "source": "/kaggle/working/cccl/nvrtcc/test/nvrtcc_options/use_nvrtc.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#if defined(__CUDACC_RTC__)\n#  define EXPECT_CUDACC_RTC_INT128\n#endif // __CUDACC_RTC__\n#include \"../common/check_predefined_macros.h\"\n\n#if defined(EXPECT_CUDACC_RTC_INT128)\n__global__ void kernel(__int128* value)\n{\n  *value = __int128{1};\n}\n#endif // EXPECT_CUDACC_RTC_INT128\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#if defined(__CUDACC_RTC__)\n#  define EXPECT_CUDACC_RTC_INT128\n#endif // __CUDACC_RTC__\n#include \"../common/check_predefined_macros.h\"\n\n#if defined(EXPECT_CUDACC_RTC_INT128)\n__global__ void kernel(__int128* value)\n{\n  *value = __int128{1};\n}\n#endif // EXPECT_CUDACC_RTC_INT128\n", "source": "/kaggle/working/cccl/nvrtcc/test/predefined_macros/__CUDACC_RTC_INT128__.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#if defined(__CUDACC_RTC__) && __CUDA_ARCH__ >= 1000\n#  define EXPECT_CUDACC_RTC_FLOAT128\n#endif // __CUDACC_RTC__ && __CUDA_ARCH__ >= 1000\n#include \"../common/check_predefined_macros.h\"\n\n#if defined(EXPECT_CUDACC_RTC_FLOAT128)\n__global__ void kernel(__float128* value)\n{\n  *value = __float128{1.0};\n}\n#endif // EXPECT_CUDACC_RTC_FLOAT128\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#if defined(__CUDACC_RTC__) && __CUDA_ARCH__ >= 1000\n#  define EXPECT_CUDACC_RTC_FLOAT128\n#endif // __CUDACC_RTC__ && __CUDA_ARCH__ >= 1000\n#include \"../common/check_predefined_macros.h\"\n\n#if defined(EXPECT_CUDACC_RTC_FLOAT128)\n__global__ void kernel(__float128* value)\n{\n  *value = __float128{1.0};\n}\n#endif // EXPECT_CUDACC_RTC_FLOAT128\n", "source": "/kaggle/working/cccl/nvrtcc/test/predefined_macros/__CUDACC_RTC_FLOAT128__.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"../common/check_predefined_macros.h\"\n\n// Triggers: \"warning #186-D: pointless comparison of unsigned integer with zero\".\n__global__ void kernel(bool* result, unsigned value)\n{\n  *result = value >= 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of nvrtcc in CUDA Core Compute Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/check_predefined_macros.h\"\n\n// Triggers: \"warning #186-D: pointless comparison of unsigned integer with zero\".\n__global__ void kernel(bool* result, unsigned value)\n{\n  *result = value >= 0;\n}\n", "source": "/kaggle/working/cccl/nvrtcc/test/nvcc_options/diag_suppress.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/devices>\n#include <cuda/std/cassert>\n\n#include <testing.cuh>\n\n#include \"test_macros.h\"\n\ntemplate <class T>\nTEST_DEVICE_FUNC T foo(const T& x)\n{\n  return x;\n}\n\ntemplate <cuda::arch_id Arch>\n__global__ void arch_specific_kernel_mock_do_not_launch()\n{\n  assert(Arch == cuda::device::current_arch_id());\n\n  // I will try to pack something like this into an API\n  if constexpr (cuda::arch_traits<Arch>().compute_capability != cuda::device::current_arch_traits().compute_capability)\n  {\n    return;\n  }\n\n  [[maybe_unused]] __shared__ int array[cuda::arch_traits<Arch>().max_shared_memory_per_block / sizeof(int)];\n\n  // constexpr is useless and I can't use intrinsics here :(\n  if (cuda::device::current_arch_traits().cluster_supported)\n  {\n    [[maybe_unused]] int dummy;\n    asm volatile(\"mov.u32 %0, %%cluster_ctarank;\" : \"=r\"(dummy));\n  }\n  if (cuda::device::current_arch_traits().redux_intrinisic)\n  {\n    [[maybe_unused]] int dummy1 = 0, dummy2 = 0;\n    asm volatile(\"redux.sync.add.s32 %0, %1, 0xffffffff;\" : \"=r\"(dummy1) : \"r\"(dummy2));\n  }\n  if (cuda::device::current_arch_traits().cp_async_supported)\n  {\n    asm volatile(\"cp.async.commit_group;\");\n  }\n\n  // Confirm trait value is defined device code and usable as a reference\n  foo(cuda::arch_traits<Arch>().compute_capability);\n  foo(cuda::device::current_arch_traits().compute_capability);\n}\n\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_70>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_75>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_80>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_86>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_87>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_88>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_89>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_90>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_100>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_103>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_110>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_120>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_121>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_90a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_100a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_103a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_110a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_120a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_121a>();\n\ntemplate <int ComputeCapability>\nvoid constexpr compare_static_and_dynamic()\n{\n  constexpr cuda::compute_capability cc{ComputeCapability};\n  constexpr cuda::arch_traits_t static_traits  = cuda::arch_traits<cuda::to_arch_id(cc)>();\n  constexpr cuda::arch_traits_t dynamic_traits = cuda::arch_traits_for(cuda::to_arch_id(cc));\n\n  static_assert(static_traits.arch_id == dynamic_traits.arch_id);\n  static_assert(static_traits.max_threads_per_block == dynamic_traits.max_threads_per_block);\n  static_assert(static_traits.max_block_dim_x == dynamic_traits.max_block_dim_x);\n  static_assert(static_traits.max_block_dim_y == dynamic_traits.max_block_dim_y);\n  static_assert(static_traits.max_block_dim_z == dynamic_traits.max_block_dim_z);\n  static_assert(static_traits.max_grid_dim_x == dynamic_traits.max_grid_dim_x);\n  static_assert(static_traits.max_grid_dim_y == dynamic_traits.max_grid_dim_y);\n  static_assert(static_traits.max_grid_dim_z == dynamic_traits.max_grid_dim_z);\n\n  static_assert(static_traits.warp_size == dynamic_traits.warp_size);\n  static_assert(static_traits.total_constant_memory == dynamic_traits.total_constant_memory);\n  static_assert(static_traits.max_resident_grids == dynamic_traits.max_resident_grids);\n  static_assert(static_traits.max_shared_memory_per_block == dynamic_traits.max_shared_memory_per_block);\n  static_assert(static_traits.gpu_overlap == dynamic_traits.gpu_overlap);\n  static_assert(static_traits.can_map_host_memory == dynamic_traits.can_map_host_memory);\n  static_assert(static_traits.concurrent_kernels == dynamic_traits.concurrent_kernels);\n  static_assert(static_traits.stream_priorities_supported == dynamic_traits.stream_priorities_supported);\n  static_assert(static_traits.global_l1_cache_supported == dynamic_traits.global_l1_cache_supported);\n  static_assert(static_traits.local_l1_cache_supported == dynamic_traits.local_l1_cache_supported);\n  static_assert(static_traits.max_registers_per_block == dynamic_traits.max_registers_per_block);\n  static_assert(static_traits.max_registers_per_multiprocessor == dynamic_traits.max_registers_per_multiprocessor);\n\n  static_assert(static_traits.compute_capability == dynamic_traits.compute_capability);\n  static_assert(static_traits.compute_capability_major == dynamic_traits.compute_capability_major);\n  static_assert(static_traits.compute_capability_minor == dynamic_traits.compute_capability_minor);\n  static_assert(static_traits.compute_capability == dynamic_traits.compute_capability);\n  static_assert(\n    static_traits.max_shared_memory_per_multiprocessor == dynamic_traits.max_shared_memory_per_multiprocessor);\n  static_assert(static_traits.max_blocks_per_multiprocessor == dynamic_traits.max_blocks_per_multiprocessor);\n  static_assert(static_traits.max_warps_per_multiprocessor == dynamic_traits.max_warps_per_multiprocessor);\n  static_assert(static_traits.max_threads_per_multiprocessor == dynamic_traits.max_threads_per_multiprocessor);\n  static_assert(static_traits.reserved_shared_memory_per_block == dynamic_traits.reserved_shared_memory_per_block);\n  static_assert(static_traits.max_shared_memory_per_block_optin == dynamic_traits.max_shared_memory_per_block_optin);\n  static_assert(static_traits.cluster_supported == dynamic_traits.cluster_supported);\n  static_assert(static_traits.redux_intrinisic == dynamic_traits.redux_intrinisic);\n  static_assert(static_traits.elect_intrinsic == dynamic_traits.elect_intrinsic);\n  static_assert(static_traits.cp_async_supported == dynamic_traits.cp_async_supported);\n  static_assert(static_traits.tma_supported == dynamic_traits.tma_supported);\n}\n\nC2H_CCCLRT_TEST(\"Traits\", \"[device]\")\n{\n  compare_static_and_dynamic<70>();\n  compare_static_and_dynamic<75>();\n  compare_static_and_dynamic<80>();\n  compare_static_and_dynamic<86>();\n  compare_static_and_dynamic<89>();\n  compare_static_and_dynamic<90>();\n  compare_static_and_dynamic<100>();\n  compare_static_and_dynamic<103>();\n  compare_static_and_dynamic<110>();\n  compare_static_and_dynamic<120>();\n\n  // Compare arch traits with attributes\n  for (const cuda::device_ref& dev : cuda::devices)\n  {\n    const auto cc = dev.attribute(cuda::device_attributes::compute_capability);\n\n    const auto traits = cuda::arch_traits_for(cc);\n\n    CCCLRT_REQUIRE(traits.max_threads_per_block == dev.attribute(cuda::device_attributes::max_threads_per_block));\n    CCCLRT_REQUIRE(traits.max_block_dim_x == dev.attribute(cuda::device_attributes::max_block_dim_x));\n    CCCLRT_REQUIRE(traits.max_block_dim_y == dev.attribute(cuda::device_attributes::max_block_dim_y));\n    CCCLRT_REQUIRE(traits.max_block_dim_z == dev.attribute(cuda::device_attributes::max_block_dim_z));\n    CCCLRT_REQUIRE(traits.max_grid_dim_x == dev.attribute(cuda::device_attributes::max_grid_dim_x));\n    CCCLRT_REQUIRE(traits.max_grid_dim_y == dev.attribute(cuda::device_attributes::max_grid_dim_y));\n    CCCLRT_REQUIRE(traits.max_grid_dim_z == dev.attribute(cuda::device_attributes::max_grid_dim_z));\n\n    CCCLRT_REQUIRE(traits.warp_size == dev.attribute(cuda::device_attributes::warp_size));\n    CCCLRT_REQUIRE(traits.total_constant_memory == dev.attribute(cuda::device_attributes::total_constant_memory));\n    CCCLRT_REQUIRE(\n      traits.max_shared_memory_per_block == dev.attribute(cuda::device_attributes::max_shared_memory_per_block));\n    CCCLRT_REQUIRE(traits.gpu_overlap == dev.attribute(cuda::device_attributes::gpu_overlap));\n    CCCLRT_REQUIRE(traits.can_map_host_memory == dev.attribute(cuda::device_attributes::can_map_host_memory));\n    CCCLRT_REQUIRE(traits.concurrent_kernels == dev.attribute(cuda::device_attributes::concurrent_kernels));\n    CCCLRT_REQUIRE(\n      traits.stream_priorities_supported == dev.attribute(cuda::device_attributes::stream_priorities_supported));\n    CCCLRT_REQUIRE(\n      traits.global_l1_cache_supported == dev.attribute(cuda::device_attributes::global_l1_cache_supported));\n    CCCLRT_REQUIRE(traits.local_l1_cache_supported == dev.attribute(cuda::device_attributes::local_l1_cache_supported));\n    CCCLRT_REQUIRE(traits.max_registers_per_block == dev.attribute(cuda::device_attributes::max_registers_per_block));\n    CCCLRT_REQUIRE(traits.max_registers_per_multiprocessor\n                   == dev.attribute(cuda::device_attributes::max_registers_per_multiprocessor));\n    CCCLRT_REQUIRE(traits.compute_capability_major == dev.attribute(cuda::device_attributes::compute_capability_major));\n    CCCLRT_REQUIRE(traits.compute_capability_minor == dev.attribute(cuda::device_attributes::compute_capability_minor));\n    CCCLRT_REQUIRE(traits.compute_capability == dev.attribute(cuda::device_attributes::compute_capability));\n    CCCLRT_REQUIRE(traits.max_shared_memory_per_multiprocessor\n                   == dev.attribute(cuda::device_attributes::max_shared_memory_per_multiprocessor));\n    CCCLRT_REQUIRE(\n      traits.max_blocks_per_multiprocessor == dev.attribute(cuda::device_attributes::max_blocks_per_multiprocessor));\n    CCCLRT_REQUIRE(\n      traits.max_threads_per_multiprocessor == dev.attribute(cuda::device_attributes::max_threads_per_multiprocessor));\n    CCCLRT_REQUIRE(traits.reserved_shared_memory_per_block\n                   == dev.attribute(cuda::device_attributes::reserved_shared_memory_per_block));\n    CCCLRT_REQUIRE(traits.max_shared_memory_per_block_optin\n                   == dev.attribute(cuda::device_attributes::max_shared_memory_per_block_optin));\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/devices>\n#include <cuda/std/cassert>\n\n#include <testing.cuh>\n\n#include \"test_macros.h\"\n\ntemplate <class T>\nTEST_DEVICE_FUNC T foo(const T& x)\n{\n  return x;\n}\n\ntemplate <cuda::arch_id Arch>\n__global__ void arch_specific_kernel_mock_do_not_launch()\n{\n  assert(Arch == cuda::device::current_arch_id());\n\n  // I will try to pack something like this into an API\n  if constexpr (cuda::arch_traits<Arch>().compute_capability != cuda::device::current_arch_traits().compute_capability)\n  {\n    return;\n  }\n\n  [[maybe_unused]] __shared__ int array[cuda::arch_traits<Arch>().max_shared_memory_per_block / sizeof(int)];\n\n  // constexpr is useless and I can't use intrinsics here :(\n  if (cuda::device::current_arch_traits().cluster_supported)\n  {\n    [[maybe_unused]] int dummy;\n    asm volatile(\"mov.u32 %0, %%cluster_ctarank;\" : \"=r\"(dummy));\n  }\n  if (cuda::device::current_arch_traits().redux_intrinisic)\n  {\n    [[maybe_unused]] int dummy1 = 0, dummy2 = 0;\n    asm volatile(\"redux.sync.add.s32 %0, %1, 0xffffffff;\" : \"=r\"(dummy1) : \"r\"(dummy2));\n  }\n  if (cuda::device::current_arch_traits().cp_async_supported)\n  {\n    asm volatile(\"cp.async.commit_group;\");\n  }\n\n  // Confirm trait value is defined device code and usable as a reference\n  foo(cuda::arch_traits<Arch>().compute_capability);\n  foo(cuda::device::current_arch_traits().compute_capability);\n}\n\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_70>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_75>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_80>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_86>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_87>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_88>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_89>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_90>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_100>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_103>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_110>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_120>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_121>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_90a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_100a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_103a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_110a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_120a>();\ntemplate __global__ void arch_specific_kernel_mock_do_not_launch<cuda::arch_id::sm_121a>();\n\ntemplate <int ComputeCapability>\nvoid constexpr compare_static_and_dynamic()\n{\n  constexpr cuda::compute_capability cc{ComputeCapability};\n  constexpr cuda::arch_traits_t static_traits  = cuda::arch_traits<cuda::to_arch_id(cc)>();\n  constexpr cuda::arch_traits_t dynamic_traits = cuda::arch_traits_for(cuda::to_arch_id(cc));\n\n  static_assert(static_traits.arch_id == dynamic_traits.arch_id);\n  static_assert(static_traits.max_threads_per_block == dynamic_traits.max_threads_per_block);\n  static_assert(static_traits.max_block_dim_x == dynamic_traits.max_block_dim_x);\n  static_assert(static_traits.max_block_dim_y == dynamic_traits.max_block_dim_y);\n  static_assert(static_traits.max_block_dim_z == dynamic_traits.max_block_dim_z);\n  static_assert(static_traits.max_grid_dim_x == dynamic_traits.max_grid_dim_x);\n  static_assert(static_traits.max_grid_dim_y == dynamic_traits.max_grid_dim_y);\n  static_assert(static_traits.max_grid_dim_z == dynamic_traits.max_grid_dim_z);\n\n  static_assert(static_traits.warp_size == dynamic_traits.warp_size);\n  static_assert(static_traits.total_constant_memory == dynamic_traits.total_constant_memory);\n  static_assert(static_traits.max_resident_grids == dynamic_traits.max_resident_grids);\n  static_assert(static_traits.max_shared_memory_per_block == dynamic_traits.max_shared_memory_per_block);\n  static_assert(static_traits.gpu_overlap == dynamic_traits.gpu_overlap);\n  static_assert(static_traits.can_map_host_memory == dynamic_traits.can_map_host_memory);\n  static_assert(static_traits.concurrent_kernels == dynamic_traits.concurrent_kernels);\n  static_assert(static_traits.stream_priorities_supported == dynamic_traits.stream_priorities_supported);\n  static_assert(static_traits.global_l1_cache_supported == dynamic_traits.global_l1_cache_supported);\n  static_assert(static_traits.local_l1_cache_supported == dynamic_traits.local_l1_cache_supported);\n  static_assert(static_traits.max_registers_per_block == dynamic_traits.max_registers_per_block);\n  static_assert(static_traits.max_registers_per_multiprocessor == dynamic_traits.max_registers_per_multiprocessor);\n\n  static_assert(static_traits.compute_capability == dynamic_traits.compute_capability);\n  static_assert(static_traits.compute_capability_major == dynamic_traits.compute_capability_major);\n  static_assert(static_traits.compute_capability_minor == dynamic_traits.compute_capability_minor);\n  static_assert(static_traits.compute_capability == dynamic_traits.compute_capability);\n  static_assert(\n    static_traits.max_shared_memory_per_multiprocessor == dynamic_traits.max_shared_memory_per_multiprocessor);\n  static_assert(static_traits.max_blocks_per_multiprocessor == dynamic_traits.max_blocks_per_multiprocessor);\n  static_assert(static_traits.max_warps_per_multiprocessor == dynamic_traits.max_warps_per_multiprocessor);\n  static_assert(static_traits.max_threads_per_multiprocessor == dynamic_traits.max_threads_per_multiprocessor);\n  static_assert(static_traits.reserved_shared_memory_per_block == dynamic_traits.reserved_shared_memory_per_block);\n  static_assert(static_traits.max_shared_memory_per_block_optin == dynamic_traits.max_shared_memory_per_block_optin);\n  static_assert(static_traits.cluster_supported == dynamic_traits.cluster_supported);\n  static_assert(static_traits.redux_intrinisic == dynamic_traits.redux_intrinisic);\n  static_assert(static_traits.elect_intrinsic == dynamic_traits.elect_intrinsic);\n  static_assert(static_traits.cp_async_supported == dynamic_traits.cp_async_supported);\n  static_assert(static_traits.tma_supported == dynamic_traits.tma_supported);\n}\n\nC2H_CCCLRT_TEST(\"Traits\", \"[device]\")\n{\n  compare_static_and_dynamic<70>();\n  compare_static_and_dynamic<75>();\n  compare_static_and_dynamic<80>();\n  compare_static_and_dynamic<86>();\n  compare_static_and_dynamic<89>();\n  compare_static_and_dynamic<90>();\n  compare_static_and_dynamic<100>();\n  compare_static_and_dynamic<103>();\n  compare_static_and_dynamic<110>();\n  compare_static_and_dynamic<120>();\n\n  // Compare arch traits with attributes\n  for (const cuda::device_ref& dev : cuda::devices)\n  {\n    const auto cc = dev.attribute(cuda::device_attributes::compute_capability);\n\n    const auto traits = cuda::arch_traits_for(cc);\n\n    CCCLRT_REQUIRE(traits.max_threads_per_block == dev.attribute(cuda::device_attributes::max_threads_per_block));\n    CCCLRT_REQUIRE(traits.max_block_dim_x == dev.attribute(cuda::device_attributes::max_block_dim_x));\n    CCCLRT_REQUIRE(traits.max_block_dim_y == dev.attribute(cuda::device_attributes::max_block_dim_y));\n    CCCLRT_REQUIRE(traits.max_block_dim_z == dev.attribute(cuda::device_attributes::max_block_dim_z));\n    CCCLRT_REQUIRE(traits.max_grid_dim_x == dev.attribute(cuda::device_attributes::max_grid_dim_x));\n    CCCLRT_REQUIRE(traits.max_grid_dim_y == dev.attribute(cuda::device_attributes::max_grid_dim_y));\n    CCCLRT_REQUIRE(traits.max_grid_dim_z == dev.attribute(cuda::device_attributes::max_grid_dim_z));\n\n    CCCLRT_REQUIRE(traits.warp_size == dev.attribute(cuda::device_attributes::warp_size));\n    CCCLRT_REQUIRE(traits.total_constant_memory == dev.attribute(cuda::device_attributes::total_constant_memory));\n    CCCLRT_REQUIRE(\n      traits.max_shared_memory_per_block == dev.attribute(cuda::device_attributes::max_shared_memory_per_block));\n    CCCLRT_REQUIRE(traits.gpu_overlap == dev.attribute(cuda::device_attributes::gpu_overlap));\n    CCCLRT_REQUIRE(traits.can_map_host_memory == dev.attribute(cuda::device_attributes::can_map_host_memory));\n    CCCLRT_REQUIRE(traits.concurrent_kernels == dev.attribute(cuda::device_attributes::concurrent_kernels));\n    CCCLRT_REQUIRE(\n      traits.stream_priorities_supported == dev.attribute(cuda::device_attributes::stream_priorities_supported));\n    CCCLRT_REQUIRE(\n      traits.global_l1_cache_supported == dev.attribute(cuda::device_attributes::global_l1_cache_supported));\n    CCCLRT_REQUIRE(traits.local_l1_cache_supported == dev.attribute(cuda::device_attributes::local_l1_cache_supported));\n    CCCLRT_REQUIRE(traits.max_registers_per_block == dev.attribute(cuda::device_attributes::max_registers_per_block));\n    CCCLRT_REQUIRE(traits.max_registers_per_multiprocessor\n                   == dev.attribute(cuda::device_attributes::max_registers_per_multiprocessor));\n    CCCLRT_REQUIRE(traits.compute_capability_major == dev.attribute(cuda::device_attributes::compute_capability_major));\n    CCCLRT_REQUIRE(traits.compute_capability_minor == dev.attribute(cuda::device_attributes::compute_capability_minor));\n    CCCLRT_REQUIRE(traits.compute_capability == dev.attribute(cuda::device_attributes::compute_capability));\n    CCCLRT_REQUIRE(traits.max_shared_memory_per_multiprocessor\n                   == dev.attribute(cuda::device_attributes::max_shared_memory_per_multiprocessor));\n    CCCLRT_REQUIRE(\n      traits.max_blocks_per_multiprocessor == dev.attribute(cuda::device_attributes::max_blocks_per_multiprocessor));\n    CCCLRT_REQUIRE(\n      traits.max_threads_per_multiprocessor == dev.attribute(cuda::device_attributes::max_threads_per_multiprocessor));\n    CCCLRT_REQUIRE(traits.reserved_shared_memory_per_block\n                   == dev.attribute(cuda::device_attributes::reserved_shared_memory_per_block));\n    CCCLRT_REQUIRE(traits.max_shared_memory_per_block_optin\n                   == dev.attribute(cuda::device_attributes::max_shared_memory_per_block_optin));\n  }\n}\n", "source": "/kaggle/working/cccl/libcudacxx/test/libcudacxx/cuda/ccclrt/device/arch_traits.c2h.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda.h>\n\nvoid test_launch_kernel_replacement(CUlaunchConfig& config, CUfunction kernel, void* args[]);\n\n// This is a replacement for the launch kernel function that is used to test\n// the configuration of the launch kernel. It checks if the configuration\n// matches the expected configuration and calls the original launch kernel\n// function if it does. If the configuration does not match, it will fail the\n// test.\n#define _CCCLRT_LAUNCH_CONFIG_TEST\n#include <cuda/launch>\n\n#include <host_device.cuh>\n\nstatic CUlaunchConfig expectedConfig;\nstatic bool replacementCalled = false;\n\nvoid test_launch_kernel_replacement(CUlaunchConfig& config, CUfunction kernel, void* args[])\n{\n  replacementCalled = true;\n  bool has_cluster  = false;\n\n  CCCLRT_CHECK(expectedConfig.gridDimX == config.gridDimX);\n  CCCLRT_CHECK(expectedConfig.gridDimY == config.gridDimY);\n  CCCLRT_CHECK(expectedConfig.gridDimZ == config.gridDimZ);\n  CCCLRT_CHECK(expectedConfig.blockDimX == config.blockDimX);\n  CCCLRT_CHECK(expectedConfig.blockDimY == config.blockDimY);\n  CCCLRT_CHECK(expectedConfig.blockDimZ == config.blockDimZ);\n  CCCLRT_CHECK(expectedConfig.sharedMemBytes == config.sharedMemBytes);\n  CCCLRT_CHECK(expectedConfig.hStream == config.hStream);\n  CCCLRT_CHECK(expectedConfig.numAttrs == config.numAttrs);\n\n  for (unsigned int i = 0; i < expectedConfig.numAttrs; ++i)\n  {\n    auto& expectedAttr = expectedConfig.attrs[i];\n    unsigned int j;\n    for (j = 0; j < expectedConfig.numAttrs; ++j)\n    {\n      auto& actualAttr = config.attrs[j];\n      if (expectedAttr.id == actualAttr.id)\n      {\n        switch (expectedAttr.id)\n        {\n          case CU_LAUNCH_ATTRIBUTE_CLUSTER_DIMENSION:\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.x == actualAttr.value.clusterDim.x);\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.y == actualAttr.value.clusterDim.y);\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.z == actualAttr.value.clusterDim.z);\n            has_cluster = true;\n            break;\n          case CU_LAUNCH_ATTRIBUTE_COOPERATIVE:\n            CCCLRT_CHECK(expectedAttr.value.cooperative == actualAttr.value.cooperative);\n            break;\n          case CU_LAUNCH_ATTRIBUTE_PRIORITY:\n            CCCLRT_CHECK(expectedAttr.value.priority == actualAttr.value.priority);\n            break;\n          default:\n            CCCLRT_CHECK(false);\n            break;\n        }\n        break;\n      }\n    }\n    INFO(\"Searched attribute is \" << expectedAttr.id);\n    CCCLRT_CHECK(j != expectedConfig.numAttrs);\n  }\n\n  if (!has_cluster || !skip_device_exec(arch_filter<std::less<int>, 90>))\n  {\n    return ::cuda::__driver::__launchKernel(config, kernel, args);\n  }\n}\n\n__global__ void empty_kernel(int i) {}\n\ntemplate <bool HasCluster>\nauto make_test_dims(const dim3& grid_dims, const dim3& block_dims, const dim3& cluster_dims = dim3())\n{\n  if constexpr (HasCluster)\n  {\n    return cuda::make_hierarchy(\n      cuda::grid_dims(grid_dims), cuda::cluster_dims(cluster_dims), cuda::block_dims(block_dims));\n  }\n  else\n  {\n    return cuda::make_hierarchy(cuda::grid_dims(grid_dims), cuda::block_dims(block_dims));\n  }\n}\n\nauto add_cluster(const dim3& cluster_dims, CUlaunchAttribute& attr)\n{\n  attr.id               = CU_LAUNCH_ATTRIBUTE_CLUSTER_DIMENSION;\n  attr.value.clusterDim = {cluster_dims.x, cluster_dims.y, cluster_dims.z};\n}\n\ntemplate <bool HasCluster, typename... Dims>\nauto configuration_test(\n  ::cuda::stream_ref stream, const dim3& grid_dims, const dim3& block_dims, const dim3& cluster_dims = dim3())\n{\n  auto dims              = make_test_dims<HasCluster>(grid_dims, block_dims, cluster_dims);\n  expectedConfig         = {};\n  expectedConfig.hStream = stream.get();\n  if constexpr (HasCluster)\n  {\n    expectedConfig.gridDimX = grid_dims.x * cluster_dims.x;\n    expectedConfig.gridDimY = grid_dims.y * cluster_dims.y;\n    expectedConfig.gridDimZ = grid_dims.z * cluster_dims.z;\n  }\n  else\n  {\n    expectedConfig.gridDimX = grid_dims.x;\n    expectedConfig.gridDimY = grid_dims.y;\n    expectedConfig.gridDimZ = grid_dims.z;\n  }\n  expectedConfig.blockDimX = block_dims.x;\n  expectedConfig.blockDimY = block_dims.y;\n  expectedConfig.blockDimZ = block_dims.z;\n\n  SECTION(\"Simple cooperative launch\")\n  {\n    CUlaunchAttribute attrs[2];\n    auto config                               = cuda::make_config(dims, cuda::cooperative_launch());\n    expectedConfig.numAttrs                   = 1 + HasCluster;\n    expectedConfig.attrs                      = &attrs[0];\n    expectedConfig.attrs[0].id                = CU_LAUNCH_ATTRIBUTE_COOPERATIVE;\n    expectedConfig.attrs[0].value.cooperative = 1;\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[1]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n\n  SECTION(\"Priority and dynamic smem\")\n  {\n    CUlaunchAttribute attrs[2];\n    constexpr int priority = 42;\n    constexpr int num_ints = 128;\n    auto config =\n      cuda::make_config(dims, cuda::launch_priority(priority), cuda::dynamic_shared_memory<int[num_ints]>());\n    expectedConfig.sharedMemBytes          = num_ints * sizeof(int);\n    expectedConfig.numAttrs                = 1 + HasCluster;\n    expectedConfig.attrs                   = &attrs[0];\n    expectedConfig.attrs[0].id             = CU_LAUNCH_ATTRIBUTE_PRIORITY;\n    expectedConfig.attrs[0].value.priority = priority;\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[1]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n\n  SECTION(\"Large dynamic smem\")\n  {\n    // Exceed the default 48kB of shared to check if its properly handled\n    // TODO move to launch option (available since CUDA 12.4)\n    struct S\n    {\n      int arr[13 * 1024];\n    };\n    CUlaunchAttribute attrs[1];\n    auto config                   = cuda::make_config(dims, cuda::dynamic_shared_memory<S>(cuda::non_portable));\n    expectedConfig.sharedMemBytes = sizeof(S);\n    expectedConfig.numAttrs       = HasCluster;\n    expectedConfig.attrs          = &attrs[0];\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[0]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n  stream.sync();\n}\n\nC2H_TEST(\"Launch configuration\", \"[launch]\")\n{\n  cudaStream_t stream;\n  CUDART(cudaStreamCreate(&stream));\n  SECTION(\"No cluster\")\n  {\n    configuration_test<false>(stream, 8, 64);\n  }\n  SECTION(\"With cluster\")\n  {\n    configuration_test<true>(stream, 8, 32, 2);\n  }\n\n  CUDART(cudaStreamDestroy(stream));\n  CCCLRT_CHECK(replacementCalled);\n}\n\nC2H_TEST(\"Hierarchy construction in config\", \"[launch]\")\n{\n  auto config = cuda::make_config(cuda::grid_dims<2>(), cuda::cooperative_launch());\n  static_assert(cuda::block.count(cuda::grid, config) == 2);\n\n  auto config_larger = cuda::make_config(cuda::grid_dims<2>(), cuda::block_dims(256), cuda::cooperative_launch());\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config_larger) == 512);\n\n  auto config_no_options = cuda::make_config(cuda::grid_dims(2), cuda::block_dims<128>());\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config_no_options) == 256);\n\n  [[maybe_unused]] auto config_no_dims = cuda::make_config(cuda::cooperative_launch());\n  static_assert(\n    cuda::std::is_same_v<::cuda::std::remove_cvref_t<decltype(config_no_dims.hierarchy())>, cuda::__empty_hierarchy>);\n}\n\nC2H_TEST(\"Configuration combine\", \"[launch]\")\n{\n  auto grid    = cuda::grid_dims<2>;\n  auto cluster = cuda::cluster_dims<2, 2>;\n  auto block   = cuda::block_dims(256);\n  SECTION(\"Combine with no overlap\")\n  {\n    auto config_part1                         = cuda::make_config(grid);\n    auto config_part2                         = cuda::make_config(block, cuda::launch_priority(2));\n    auto combined                             = config_part1.combine(config_part2);\n    [[maybe_unused]] auto combined_other_way  = config_part2.combine(config_part1);\n    [[maybe_unused]] auto combined_with_empty = combined.combine(cuda::make_config());\n    [[maybe_unused]] auto empty_with_combined = cuda::make_config().combine(combined);\n    static_assert(\n      cuda::std::is_same_v<decltype(combined), decltype(cuda::make_config(grid, block, cuda::launch_priority(2)))>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_other_way)>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_with_empty)>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(empty_with_combined)>);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined) == 512);\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    auto config_part1 = make_config(grid, cluster, cuda::launch_priority(2));\n    auto config_part2 = make_config(cuda::cluster_dims<256>(), block, cuda::launch_priority(42));\n    auto combined     = config_part1.combine(config_part2);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined) == 2048);\n    CCCLRT_REQUIRE(cuda::std::get<0>(combined.options()).priority == 2);\n\n    auto replaced_one_option = cuda::make_config(cuda::launch_priority(3)).combine(combined);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, replaced_one_option) == 2048);\n    CCCLRT_REQUIRE(cuda::std::get<0>(replaced_one_option.options()).priority == 3);\n\n    [[maybe_unused]] auto combined_with_extra_option = combined.combine(cuda::make_config(cuda::cooperative_launch()));\n    static_assert(\n      cuda::std::is_same_v<decltype(combined.hierarchy()), decltype(combined_with_extra_option.hierarchy())>);\n    static_assert(\n      cuda::std::tuple_size_v<::cuda::std::remove_cvref_t<decltype(combined_with_extra_option.options())>> == 2);\n  }\n}\n\n#if !_CCCL_CUDA_COMPILER(CLANG)\ntemplate <typename Config>\nTEST_FUNC void test_queries_on_config(const Config& config)\n{\n  CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, config) == dim3(1024));\n  {\n    auto dims = cuda::gpu_thread.dims_as<int>(cuda::grid, config);\n    CCCLRT_REQUIRE(dims.x == 1024);\n    CCCLRT_REQUIRE(dims.y == 1);\n    CCCLRT_REQUIRE(dims.z == 1);\n  }\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count_as<int>(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config) == 1024);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count_as<int>(cuda::grid, config) == 1024);\n  CCCLRT_REQUIRE(cuda::block.extents(cuda::grid, config).extent(0) == 4);\n  CCCLRT_REQUIRE(cuda::block.extents_as<int>(cuda::grid, config).extent(0) == 4);\n  NV_IF_TARGET(\n    NV_IS_DEVICE,\n    (CCCLRT_REQUIRE(cuda::block.rank(cuda::grid, config) == blockIdx.x);\n     CCCLRT_REQUIRE(cuda::block.rank_as<int>(cuda::grid, config) == blockIdx.x);\n     CCCLRT_REQUIRE(cuda::gpu_thread.index(cuda::block, config) == threadIdx);\n     {\n       auto idx = cuda::gpu_thread.index_as<int>(cuda::block, config);\n       CCCLRT_REQUIRE(idx.x == static_cast<int>(threadIdx.x));\n       CCCLRT_REQUIRE(idx.y == static_cast<int>(threadIdx.y));\n       CCCLRT_REQUIRE(idx.z == static_cast<int>(threadIdx.z));\n     }));\n}\n\ntemplate <typename Config>\n__global__ void test_kernel(Config config)\n{\n  test_queries_on_config(config);\n}\n\nC2H_TEST(\"Queries on config\", \"[launch]\")\n{\n  auto config = cuda::make_config(cuda::grid_dims(4), cuda::block_dims<256>(), cuda::cooperative_launch());\n  test_queries_on_config(config);\n  test_kernel<<<4, 256>>>(config);\n  CUDART(cudaGetLastError());\n  CUDART(cudaDeviceSynchronize());\n}\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <hip/hip_runtime.h>\n\nvoid test_launch_kernel_replacement(HIP_LAUNCH_CONFIG& config, hipFunction_t kernel, void* args[]);\n\n// This is a replacement for the launch kernel function that is used to test\n// the configuration of the launch kernel. It checks if the configuration\n// matches the expected configuration and calls the original launch kernel\n// function if it does. If the configuration does not match, it will fail the\n// test.\n#define _CCCLRT_LAUNCH_CONFIG_TEST\n#include <cuda/launch>\n\n#include <host_device.cuh>\n\nstatic HIP_LAUNCH_CONFIG expectedConfig;\nstatic bool replacementCalled = false;\n\nvoid test_launch_kernel_replacement(HIP_LAUNCH_CONFIG& config, hipFunction_t kernel, void* args[])\n{\n  replacementCalled = true;\n  bool has_cluster  = false;\n\n  CCCLRT_CHECK(expectedConfig.gridDimX == config.gridDimX);\n  CCCLRT_CHECK(expectedConfig.gridDimY == config.gridDimY);\n  CCCLRT_CHECK(expectedConfig.gridDimZ == config.gridDimZ);\n  CCCLRT_CHECK(expectedConfig.blockDimX == config.blockDimX);\n  CCCLRT_CHECK(expectedConfig.blockDimY == config.blockDimY);\n  CCCLRT_CHECK(expectedConfig.blockDimZ == config.blockDimZ);\n  CCCLRT_CHECK(expectedConfig.sharedMemBytes == config.sharedMemBytes);\n  CCCLRT_CHECK(expectedConfig.hStream == config.hStream);\n  CCCLRT_CHECK(expectedConfig.numAttrs == config.numAttrs);\n\n  for (unsigned int i = 0; i < expectedConfig.numAttrs; ++i)\n  {\n    auto& expectedAttr = expectedConfig.attrs[i];\n    unsigned int j;\n    for (j = 0; j < expectedConfig.numAttrs; ++j)\n    {\n      auto& actualAttr = config.attrs[j];\n      if (expectedAttr.id == actualAttr.id)\n      {\n        switch (expectedAttr.id)\n        {\n          case CU_LAUNCH_ATTRIBUTE_CLUSTER_DIMENSION:\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.x == actualAttr.value.clusterDim.x);\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.y == actualAttr.value.clusterDim.y);\n            CCCLRT_CHECK(expectedAttr.value.clusterDim.z == actualAttr.value.clusterDim.z);\n            has_cluster = true;\n            break;\n          case hipLaunchAttributeCooperative:\n            CCCLRT_CHECK(expectedAttr.value.cooperative == actualAttr.value.cooperative);\n            break;\n          case hipLaunchAttributePriority:\n            CCCLRT_CHECK(expectedAttr.value.priority == actualAttr.value.priority);\n            break;\n          default:\n            CCCLRT_CHECK(false);\n            break;\n        }\n        break;\n      }\n    }\n    INFO(\"Searched attribute is \" << expectedAttr.id);\n    CCCLRT_CHECK(j != expectedConfig.numAttrs);\n  }\n\n  if (!has_cluster || !skip_device_exec(arch_filter<std::less<int>, 90>))\n  {\n    return ::cuda::__driver::__launchKernel(config, kernel, args);\n  }\n}\n\n__global__ void empty_kernel(int i) {}\n\ntemplate <bool HasCluster>\nauto make_test_dims(const dim3& grid_dims, const dim3& block_dims, const dim3& cluster_dims = dim3())\n{\n  if constexpr (HasCluster)\n  {\n    return cuda::make_hierarchy(\n      cuda::grid_dims(grid_dims), cuda::cluster_dims(cluster_dims), cuda::block_dims(block_dims));\n  }\n  else\n  {\n    return cuda::make_hierarchy(cuda::grid_dims(grid_dims), cuda::block_dims(block_dims));\n  }\n}\n\nauto add_cluster(const dim3& cluster_dims, hipLaunchAttribute& attr)\n{\n  attr.id               = CU_LAUNCH_ATTRIBUTE_CLUSTER_DIMENSION;\n  attr.value.clusterDim = {cluster_dims.x, cluster_dims.y, cluster_dims.z};\n}\n\ntemplate <bool HasCluster, typename... Dims>\nauto configuration_test(\n  ::cuda::stream_ref stream, const dim3& grid_dims, const dim3& block_dims, const dim3& cluster_dims = dim3())\n{\n  auto dims              = make_test_dims<HasCluster>(grid_dims, block_dims, cluster_dims);\n  expectedConfig         = {};\n  expectedConfig.hStream = stream.get();\n  if constexpr (HasCluster)\n  {\n    expectedConfig.gridDimX = grid_dims.x * cluster_dims.x;\n    expectedConfig.gridDimY = grid_dims.y * cluster_dims.y;\n    expectedConfig.gridDimZ = grid_dims.z * cluster_dims.z;\n  }\n  else\n  {\n    expectedConfig.gridDimX = grid_dims.x;\n    expectedConfig.gridDimY = grid_dims.y;\n    expectedConfig.gridDimZ = grid_dims.z;\n  }\n  expectedConfig.blockDimX = block_dims.x;\n  expectedConfig.blockDimY = block_dims.y;\n  expectedConfig.blockDimZ = block_dims.z;\n\n  SECTION(\"Simple cooperative launch\")\n  {\n    hipLaunchAttribute attrs[2];\n    auto config                               = cuda::make_config(dims, cuda::cooperative_launch());\n    expectedConfig.numAttrs                   = 1 + HasCluster;\n    expectedConfig.attrs                      = &attrs[0];\n    expectedConfig.attrs[0].id                = hipLaunchAttributeCooperative;\n    expectedConfig.attrs[0].value.cooperative = 1;\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[1]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n\n  SECTION(\"Priority and dynamic smem\")\n  {\n    hipLaunchAttribute attrs[2];\n    constexpr int priority = 42;\n    constexpr int num_ints = 128;\n    auto config =\n      cuda::make_config(dims, cuda::launch_priority(priority), cuda::dynamic_shared_memory<int[num_ints]>());\n    expectedConfig.sharedMemBytes          = num_ints * sizeof(int);\n    expectedConfig.numAttrs                = 1 + HasCluster;\n    expectedConfig.attrs                   = &attrs[0];\n    expectedConfig.attrs[0].id             = hipLaunchAttributePriority;\n    expectedConfig.attrs[0].value.priority = priority;\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[1]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n\n  SECTION(\"Large dynamic smem\")\n  {\n    // Exceed the default 48kB of shared to check if its properly handled\n    // TODO move to launch option (available since CUDA 12.4)\n    struct S\n    {\n      int arr[13 * 1024];\n    };\n    hipLaunchAttribute attrs[1];\n    auto config                   = cuda::make_config(dims, cuda::dynamic_shared_memory<S>(cuda::non_portable));\n    expectedConfig.sharedMemBytes = sizeof(S);\n    expectedConfig.numAttrs       = HasCluster;\n    expectedConfig.attrs          = &attrs[0];\n    if constexpr (HasCluster)\n    {\n      add_cluster(cluster_dims, expectedConfig.attrs[0]);\n    }\n    cuda::launch(stream, config, empty_kernel, 0);\n  }\n  stream.sync();\n}\n\nC2H_TEST(\"Launch configuration\", \"[launch]\")\n{\n  hipStream_t stream;\n  CUDART(hipStreamCreate(&stream));\n  SECTION(\"No cluster\")\n  {\n    configuration_test<false>(stream, 8, 64);\n  }\n  SECTION(\"With cluster\")\n  {\n    configuration_test<true>(stream, 8, 32, 2);\n  }\n\n  CUDART(hipStreamDestroy(stream));\n  CCCLRT_CHECK(replacementCalled);\n}\n\nC2H_TEST(\"Hierarchy construction in config\", \"[launch]\")\n{\n  auto config = cuda::make_config(cuda::grid_dims<2>(), cuda::cooperative_launch());\n  static_assert(cuda::block.count(cuda::grid, config) == 2);\n\n  auto config_larger = cuda::make_config(cuda::grid_dims<2>(), cuda::block_dims(256), cuda::cooperative_launch());\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config_larger) == 512);\n\n  auto config_no_options = cuda::make_config(cuda::grid_dims(2), cuda::block_dims<128>());\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config_no_options) == 256);\n\n  [[maybe_unused]] auto config_no_dims = cuda::make_config(cuda::cooperative_launch());\n  static_assert(\n    cuda::std::is_same_v<::cuda::std::remove_cvref_t<decltype(config_no_dims.hierarchy())>, cuda::__empty_hierarchy>);\n}\n\nC2H_TEST(\"Configuration combine\", \"[launch]\")\n{\n  auto grid    = cuda::grid_dims<2>;\n  auto cluster = cuda::cluster_dims<2, 2>;\n  auto block   = cuda::block_dims(256);\n  SECTION(\"Combine with no overlap\")\n  {\n    auto config_part1                         = cuda::make_config(grid);\n    auto config_part2                         = cuda::make_config(block, cuda::launch_priority(2));\n    auto combined                             = config_part1.combine(config_part2);\n    [[maybe_unused]] auto combined_other_way  = config_part2.combine(config_part1);\n    [[maybe_unused]] auto combined_with_empty = combined.combine(cuda::make_config());\n    [[maybe_unused]] auto empty_with_combined = cuda::make_config().combine(combined);\n    static_assert(\n      cuda::std::is_same_v<decltype(combined), decltype(cuda::make_config(grid, block, cuda::launch_priority(2)))>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_other_way)>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_with_empty)>);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(empty_with_combined)>);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined) == 512);\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    auto config_part1 = make_config(grid, cluster, cuda::launch_priority(2));\n    auto config_part2 = make_config(cuda::cluster_dims<256>(), block, cuda::launch_priority(42));\n    auto combined     = config_part1.combine(config_part2);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined) == 2048);\n    CCCLRT_REQUIRE(cuda::std::get<0>(combined.options()).priority == 2);\n\n    auto replaced_one_option = cuda::make_config(cuda::launch_priority(3)).combine(combined);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, replaced_one_option) == 2048);\n    CCCLRT_REQUIRE(cuda::std::get<0>(replaced_one_option.options()).priority == 3);\n\n    [[maybe_unused]] auto combined_with_extra_option = combined.combine(cuda::make_config(cuda::cooperative_launch()));\n    static_assert(\n      cuda::std::is_same_v<decltype(combined.hierarchy()), decltype(combined_with_extra_option.hierarchy())>);\n    static_assert(\n      cuda::std::tuple_size_v<::cuda::std::remove_cvref_t<decltype(combined_with_extra_option.options())>> == 2);\n  }\n}\n\n#if !_CCCL_CUDA_COMPILER(CLANG)\ntemplate <typename Config>\nTEST_FUNC void test_queries_on_config(const Config& config)\n{\n  CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, config) == dim3(1024));\n  {\n    auto dims = cuda::gpu_thread.dims_as<int>(cuda::grid, config);\n    CCCLRT_REQUIRE(dims.x == 1024);\n    CCCLRT_REQUIRE(dims.y == 1);\n    CCCLRT_REQUIRE(dims.z == 1);\n  }\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count_as<int>(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, config) == 1024);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count_as<int>(cuda::grid, config) == 1024);\n  CCCLRT_REQUIRE(cuda::block.extents(cuda::grid, config).extent(0) == 4);\n  CCCLRT_REQUIRE(cuda::block.extents_as<int>(cuda::grid, config).extent(0) == 4);\n  NV_IF_TARGET(\n    NV_IS_DEVICE,\n    (CCCLRT_REQUIRE(cuda::block.rank(cuda::grid, config) == blockIdx.x);\n     CCCLRT_REQUIRE(cuda::block.rank_as<int>(cuda::grid, config) == blockIdx.x);\n     CCCLRT_REQUIRE(cuda::gpu_thread.index(cuda::block, config) == threadIdx);\n     {\n       auto idx = cuda::gpu_thread.index_as<int>(cuda::block, config);\n       CCCLRT_REQUIRE(idx.x == static_cast<int>(threadIdx.x));\n       CCCLRT_REQUIRE(idx.y == static_cast<int>(threadIdx.y));\n       CCCLRT_REQUIRE(idx.z == static_cast<int>(threadIdx.z));\n     }));\n}\n\ntemplate <typename Config>\n__global__ void test_kernel(Config config)\n{\n  test_queries_on_config(config);\n}\n\nC2H_TEST(\"Queries on config\", \"[launch]\")\n{\n  auto config = cuda::make_config(cuda::grid_dims(4), cuda::block_dims<256>(), cuda::cooperative_launch());\n  test_queries_on_config(config);\n  test_kernel<<<4, 256>>>(config);\n  CUDART(hipGetLastError());\n  CUDART(hipDeviceSynchronize());\n}\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n", "source": "/kaggle/working/cccl/libcudacxx/test/libcudacxx/cuda/ccclrt/launch/configuration.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n#include <cuda/atomic>\n#include <cuda/devices>\n#include <cuda/launch>\n#include <cuda/memory>\n#include <cuda/stream>\n\n#include <cooperative_groups.h>\n#include <testing.cuh>\n\n#include \"test_macros.h\"\n\n#if !_CCCL_CUDA_COMPILER(CLANG)\n\n__managed__ bool kernel_run_proof = false;\n\nvoid check_kernel_run(cudaStream_t stream)\n{\n  CUDART(cudaStreamSynchronize(stream));\n  CCCLRT_CHECK(kernel_run_proof);\n  kernel_run_proof = false;\n}\n\nstruct kernel_run_proof_check\n{\n  TEST_DEVICE_FUNC void operator()()\n  {\n    CCCLRT_CHECK_DEVICE(kernel_run_proof);\n    kernel_run_proof = false;\n  }\n};\n\nstruct functor_int_argument\n{\n  TEST_DEVICE_FUNC void operator()(int dummy)\n  {\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <unsigned int BlockSize>\nstruct functor_taking_config\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int grid_size)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == BlockSize);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.count(cuda::grid, config) == grid_size);\n    kernel_run_proof = true;\n  }\n};\n\n__global__ void kernel_no_arguments()\n{\n  kernel_run_proof = true;\n}\n\n__global__ void kernel_int_argument(int dummy)\n{\n  kernel_run_proof = true;\n}\n\ntemplate <typename Config, unsigned int BlockSize>\n__global__ void kernel_taking_config(Config config, int grid_size)\n{\n  functor_taking_config<BlockSize>()(config, grid_size);\n}\n\nstruct my_dynamic_smem_t\n{\n  int i;\n};\n\ntemplate <typename SmemType>\nstruct dynamic_smem_single\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config)\n  {\n    decltype(auto) dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem)>);\n    CCCLRT_REQUIRE_DEVICE(::cuda::device::is_object_from(dynamic_smem, ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <typename SmemType, size_t Extent>\nstruct dynamic_smem_span\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int size)\n  {\n    auto dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(decltype(dynamic_smem)::extent == Extent);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem[1])>);\n    CCCLRT_REQUIRE_DEVICE(dynamic_smem.size() == size);\n    CCCLRT_REQUIRE_DEVICE(::cuda::device::is_object_from(dynamic_smem[1], ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\nstruct launch_transform_to_int_convertible\n{\n  int value_;\n\n  struct int_convertible\n  {\n    cudaStream_t stream_;\n    int value_;\n\n    int_convertible(cudaStream_t stream, int value) noexcept\n        : stream_(stream)\n        , value_(value)\n    {\n      // Check that the constructor runs before the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDAX_CHECK_FALSE(kernel_run_proof);\n    }\n\n    // Immovable to ensure that launch_transform doesn't copy the returned\n    // object\n    int_convertible(int_convertible&&) noexcept = delete;\n\n    ~int_convertible() noexcept\n    {\n      // Check that the destructor runs after the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDART(cudaStreamSynchronize(stream_));\n      // CCCLRT_CHECK(kernel_run_proof);\n    }\n\n    // This is the value that will be passed to the kernel\n    int transformed_argument() const\n    {\n      return value_;\n    }\n  };\n\n  [[nodiscard]] friend int_convertible\n  transform_launch_argument(::cuda::stream_ref stream, launch_transform_to_int_convertible self) noexcept\n  {\n    return int_convertible(stream.get(), self.value_);\n  }\n};\n\n// Needs a separate function for Windows extended lambda\nvoid launch_smoke_test(cudaStream_t dst)\n{\n  cuda::__ensure_current_context guard(cuda::device_ref{0});\n  // Use raw stream to make sure it can be implicitly converted on call to\n  // launch\n  cudaStream_t stream;\n\n  CUDART(cudaStreamCreate(&stream));\n  // Spell out all overloads to make sure they compile, include a check for\n  // implicit conversions\n  {\n    const int grid_size      = 4;\n    constexpr int block_size = 256;\n    auto dimensions          = cuda::make_hierarchy(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n    auto config              = cuda::make_config(dimensions);\n\n    // Not taking dims\n    {\n      cuda::launch(dst, config, kernel_no_arguments);\n      check_kernel_run(dst);\n\n      const int dummy = 1;\n      cuda::launch(dst, config, kernel_int_argument, dummy);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, 1);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, 1U);\n      check_kernel_run(dst);\n\n      cuda::launch(dst, config, functor_int_argument(), dummy);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), 1);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), 1U);\n      check_kernel_run(dst);\n    }\n\n    // Config argument\n    {\n      auto functor_instance = functor_taking_config<block_size>();\n      auto kernel_instance  = kernel_taking_config<decltype(config), block_size>;\n\n      cuda::launch(dst, config, functor_instance, grid_size);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n      cuda::launch(dst, config, kernel_instance, grid_size);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n    }\n  }\n\n  // Dynamic shared memory option\n  {\n    auto config = cuda::block_dims<32>() & cuda::grid_dims<1>();\n\n    auto test = [&](const auto& input_config) {\n      // Single element\n      {\n        auto config = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t>());\n\n        cuda::launch(dst, config, dynamic_smem_single<my_dynamic_smem_t>());\n        check_kernel_run(dst);\n      }\n\n      // Dynamic span\n      {\n        const int size = 2;\n        auto config    = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[]>(size));\n        cuda::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, ::cuda::std::dynamic_extent>(), size);\n        check_kernel_run(dst);\n      }\n\n      // Static span\n      {\n        constexpr int size = 3;\n        auto config        = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[size]>());\n        cuda::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, size>(), size);\n        check_kernel_run(dst);\n      }\n    };\n\n    test(config);\n    test(config.add(cuda::cooperative_launch(), cuda::launch_priority(0)));\n  }\n}\n\nC2H_CCCLRT_TEST(\"Launch smoke stream\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to\n  // launch\n  cudaStream_t stream;\n\n  {\n    ::cuda::__ensure_current_context guard(cuda::device_ref{0});\n    CUDART(cudaStreamCreate(&stream));\n  }\n\n  launch_smoke_test(stream);\n\n  {\n    ::cuda::__ensure_current_context guard(cuda::device_ref{0});\n    CUDART(cudaStreamSynchronize(stream));\n    CUDART(cudaStreamDestroy(stream));\n  }\n}\n\ntemplate <typename DefaultConfig>\nstruct kernel_with_default_config\n{\n  DefaultConfig config;\n\n  kernel_with_default_config(DefaultConfig c)\n      : config(c)\n  {}\n\n  DefaultConfig default_config() const\n  {\n    return config;\n  }\n\n  template <typename Config, typename ConfigCheckFn>\n  TEST_DEVICE_FUNC void operator()(Config config, ConfigCheckFn check_fn)\n  {\n    check_fn(config);\n  }\n};\n\nstruct verify_callable\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == 256);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, config) == 4);\n    cooperative_groups::this_grid().sync();\n  }\n};\n\nC2H_CCCLRT_TEST(\"Launch with default config\", \"\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n  auto grid  = cuda::grid_dims(4);\n  auto block = cuda::block_dims<256>;\n\n  SECTION(\"Combine with empty\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block, grid, cuda::cooperative_launch())};\n    static_assert(cuda::__is_kernel_config<decltype(kernel.default_config())>);\n    static_assert(cuda::__kernel_has_default_config<decltype(kernel)>);\n\n    cuda::launch(stream, cuda::make_config(), kernel, verify_callable{});\n    stream.sync();\n  }\n  SECTION(\"Combine with no overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block)};\n    cuda::launch(stream, cuda::make_config(grid, cuda::cooperative_launch()), kernel, verify_callable{});\n    stream.sync();\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(cuda::block_dims<1>(), cuda::cooperative_launch())};\n    cuda::launch(stream, cuda::make_config(block, grid, cuda::cooperative_launch()), kernel, verify_callable{});\n    stream.sync();\n  }\n}\n\n// Regression test: cuda::launch must work when the calling function has\n// a __restrict__-qualified pointer parameter. On some nvcc + host compiler\n// combos, __restrict__ survives through the type transformation pipeline\n// and causes a function pointer conversion failure in __get_kernel_launcher.\nstruct restrict_assign_functor\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int* __restrict__ dst)\n  {\n    *dst = 42;\n  }\n};\n\n// The __restrict__ on the function parameter is the trigger: on affected compilers,\n// it leaks into the template args of __get_kernel_launcher via cuda::launch.\nvoid launch_with_restrict_param(cuda::stream_ref stream, int* __restrict__ dst)\n{\n  auto config = cuda::make_config(cuda::grid_dims(1), cuda::block_dims<1>());\n  cuda::launch(stream, config, restrict_assign_functor{}, dst);\n}\n\nC2H_CCCLRT_TEST(\"Launch functor with __restrict__ pointer arg\", \"[launch]\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n  test::pinned<int> val{0};\n\n  launch_with_restrict_param(stream, val.get());\n  stream.sync();\n\n  CCCLRT_CHECK(*val == 42);\n}\n\n__managed__ cuda::std::size_t launched_nthreads;\n__managed__ cuda::std::size_t launched_nblocks;\n__managed__ cuda::std::size_t launched_nclusters;\n\nstruct LaunchDimsFunctor\n{\n  template <class Config>\n  TEST_DEVICE_FUNC void operator()(const Config& config)\n  {\n    cuda::atomic_ref(launched_nthreads)++;\n\n    if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0)\n    {\n      cuda::atomic_ref(launched_nblocks)++;\n\n      NV_IF_ELSE_TARGET(NV_PROVIDES_SM_90,\n                        ({\n                          if (__clusterRelativeBlockRank() == 0)\n                          {\n                            cuda::atomic_ref(launched_nclusters)++;\n                          }\n                        }),\n                        ({ cuda::atomic_ref(launched_nclusters)++; }));\n    }\n  }\n};\n\ntemplate <class GridDesc, class BlockDesc>\nvoid test_launch_dims(cuda::stream_ref stream, GridDesc grid_desc, BlockDesc block_desc)\n{\n  launched_nthreads  = 0;\n  launched_nblocks   = 0;\n  launched_nclusters = 0;\n\n  const auto& grid_exts  = grid_desc.extents();\n  const auto& block_exts = block_desc.extents();\n\n  const auto config = cuda::make_config(grid_desc, block_desc);\n  cuda::launch(stream, config, LaunchDimsFunctor{});\n  stream.sync();\n\n  const auto exp_nclusters = cuda::std::size_t{grid_exts.extent(0)} * grid_exts.extent(1) * grid_exts.extent(2);\n  CCCLRT_CHECK(launched_nclusters == exp_nclusters);\n\n  const auto exp_nblocks = exp_nclusters;\n  CCCLRT_CHECK(launched_nblocks == exp_nblocks);\n\n  const auto exp_nthreads = exp_nblocks * block_exts.extent(0) * block_exts.extent(1) * block_exts.extent(2);\n  CCCLRT_CHECK(launched_nthreads == exp_nthreads);\n}\n\ntemplate <class GridDesc, class ClusterDesc, class BlockDesc>\nvoid test_launch_dims(cuda::stream_ref stream, GridDesc grid_desc, ClusterDesc cluster_desc, BlockDesc block_desc)\n{\n  launched_nthreads  = 0;\n  launched_nblocks   = 0;\n  launched_nclusters = 0;\n\n  const auto& grid_exts    = grid_desc.extents();\n  const auto& cluster_exts = cluster_desc.extents();\n  const auto& block_exts   = block_desc.extents();\n\n  const auto config = cuda::make_config(grid_desc, cluster_desc, block_desc);\n  cuda::launch(stream, config, LaunchDimsFunctor{});\n  stream.sync();\n\n  const auto exp_nclusters = cuda::std::size_t{grid_exts.extent(0)} * grid_exts.extent(1) * grid_exts.extent(2);\n  CCCLRT_CHECK(launched_nclusters == exp_nclusters);\n\n  const auto exp_nblocks = exp_nclusters * cluster_exts.extent(0) * cluster_exts.extent(1) * cluster_exts.extent(2);\n  CCCLRT_CHECK(launched_nblocks == exp_nblocks);\n\n  const auto exp_nthreads = exp_nblocks * block_exts.extent(0) * block_exts.extent(1) * block_exts.extent(2);\n  CCCLRT_CHECK(launched_nthreads == exp_nthreads);\n}\n\nC2H_TEST(\"Launch dims\", \"[launch]\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n\n  test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::block_dims(dim3{10}));\n  test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::block_dims(dim3{3, 7}));\n  test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::block_dims(dim3{2, 7, 9}));\n\n  test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::block_dims<10>());\n  test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::block_dims<3, 7>());\n  test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::block_dims<2, 7, 9>());\n\n  test_launch_dims(stream, cuda::grid_dims<2>(), cuda::block_dims(dim3{10}));\n  test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::block_dims(dim3{3, 7}));\n  test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::block_dims(dim3{2, 7, 9}));\n\n  test_launch_dims(stream, cuda::grid_dims<2>(), cuda::block_dims<10>());\n  test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::block_dims<3, 7>());\n  test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::block_dims<2, 7, 9>());\n\n  if (cuda::device_attributes::compute_capability_major(stream.device()) >= 9)\n  {\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims(dim3{3}), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims(dim3{3}), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims<3, 7>());\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims<3>(), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims<1, 5>(), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims<3>(), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims<1, 5>(), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims(dim3{3}), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims(dim3{3}), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims<3>(), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims<1, 5>(), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims<3>(), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims<1, 5>(), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims<2, 7, 9>());\n  }\n}\n\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n#include <cuda/devices>\n#include <cuda/launch>\n#include <cuda/memory>\n#include <cuda/stream>\n\n#include <hip/hip_cooperative_groups.h>\n#include <testing.cuh>\n\n#include \"test_macros.h\"\n\n#if !_CCCL_CUDA_COMPILER(CLANG)\n\n__managed__ bool kernel_run_proof = false;\n\nvoid check_kernel_run(hipStream_t stream)\n{\n  CUDART(hipStreamSynchronize(stream));\n  CCCLRT_CHECK(kernel_run_proof);\n  kernel_run_proof = false;\n}\n\nstruct kernel_run_proof_check\n{\n  TEST_DEVICE_FUNC void operator()()\n  {\n    CCCLRT_CHECK_DEVICE(kernel_run_proof);\n    kernel_run_proof = false;\n  }\n};\n\nstruct functor_int_argument\n{\n  TEST_DEVICE_FUNC void operator()(int dummy)\n  {\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <unsigned int BlockSize>\nstruct functor_taking_config\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int grid_size)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == BlockSize);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.count(cuda::grid, config) == grid_size);\n    kernel_run_proof = true;\n  }\n};\n\n__global__ void kernel_no_arguments()\n{\n  kernel_run_proof = true;\n}\n\n__global__ void kernel_int_argument(int dummy)\n{\n  kernel_run_proof = true;\n}\n\ntemplate <typename Config, unsigned int BlockSize>\n__global__ void kernel_taking_config(Config config, int grid_size)\n{\n  functor_taking_config<BlockSize>()(config, grid_size);\n}\n\nstruct my_dynamic_smem_t\n{\n  int i;\n};\n\ntemplate <typename SmemType>\nstruct dynamic_smem_single\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config)\n  {\n    decltype(auto) dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem)>);\n    CCCLRT_REQUIRE_DEVICE(::cuda::device::is_object_from(dynamic_smem, ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <typename SmemType, size_t Extent>\nstruct dynamic_smem_span\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int size)\n  {\n    auto dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(decltype(dynamic_smem)::extent == Extent);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem[1])>);\n    CCCLRT_REQUIRE_DEVICE(dynamic_smem.size() == size);\n    CCCLRT_REQUIRE_DEVICE(::cuda::device::is_object_from(dynamic_smem[1], ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\nstruct launch_transform_to_int_convertible\n{\n  int value_;\n\n  struct int_convertible\n  {\n    hipStream_t stream_;\n    int value_;\n\n    int_convertible(hipStream_t stream, int value) noexcept\n        : stream_(stream)\n        , value_(value)\n    {\n      // Check that the constructor runs before the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDAX_CHECK_FALSE(kernel_run_proof);\n    }\n\n    // Immovable to ensure that launch_transform doesn't copy the returned\n    // object\n    int_convertible(int_convertible&&) noexcept = delete;\n\n    ~int_convertible() noexcept\n    {\n      // Check that the destructor runs after the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDART(cudaStreamSynchronize(stream_));\n      // CCCLRT_CHECK(kernel_run_proof);\n    }\n\n    // This is the value that will be passed to the kernel\n    int transformed_argument() const\n    {\n      return value_;\n    }\n  };\n\n  [[nodiscard]] friend int_convertible\n  transform_launch_argument(::cuda::stream_ref stream, launch_transform_to_int_convertible self) noexcept\n  {\n    return int_convertible(stream.get(), self.value_);\n  }\n};\n\n// Needs a separate function for Windows extended lambda\nvoid launch_smoke_test(hipStream_t dst)\n{\n  cuda::__ensure_current_context guard(cuda::device_ref{0});\n  // Use raw stream to make sure it can be implicitly converted on call to\n  // launch\n  hipStream_t stream;\n\n  CUDART(hipStreamCreate(&stream));\n  // Spell out all overloads to make sure they compile, include a check for\n  // implicit conversions\n  {\n    const int grid_size      = 4;\n    constexpr int block_size = 256;\n    auto dimensions          = cuda::make_hierarchy(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n    auto config              = cuda::make_config(dimensions);\n\n    // Not taking dims\n    {\n      cuda::launch(dst, config, kernel_no_arguments);\n      check_kernel_run(dst);\n\n      const int dummy = 1;\n      cuda::launch(dst, config, kernel_int_argument, dummy);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, 1);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_int_argument, 1U);\n      check_kernel_run(dst);\n\n      cuda::launch(dst, config, functor_int_argument(), dummy);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), 1);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_int_argument(), 1U);\n      check_kernel_run(dst);\n    }\n\n    // Config argument\n    {\n      auto functor_instance = functor_taking_config<block_size>();\n      auto kernel_instance  = kernel_taking_config<decltype(config), block_size>;\n\n      cuda::launch(dst, config, functor_instance, grid_size);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, functor_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n      cuda::launch(dst, config, kernel_instance, grid_size);\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cuda::launch(dst, config, kernel_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n    }\n  }\n\n  // Dynamic shared memory option\n  {\n    auto config = cuda::block_dims<32>() & cuda::grid_dims<1>();\n\n    auto test = [&](const auto& input_config) {\n      // Single element\n      {\n        auto config = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t>());\n\n        cuda::launch(dst, config, dynamic_smem_single<my_dynamic_smem_t>());\n        check_kernel_run(dst);\n      }\n\n      // Dynamic span\n      {\n        const int size = 2;\n        auto config    = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[]>(size));\n        cuda::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, ::cuda::std::dynamic_extent>(), size);\n        check_kernel_run(dst);\n      }\n\n      // Static span\n      {\n        constexpr int size = 3;\n        auto config        = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[size]>());\n        cuda::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, size>(), size);\n        check_kernel_run(dst);\n      }\n    };\n\n    test(config);\n    test(config.add(cuda::cooperative_launch(), cuda::launch_priority(0)));\n  }\n}\n\nC2H_CCCLRT_TEST(\"Launch smoke stream\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to\n  // launch\n  hipStream_t stream;\n\n  {\n    ::cuda::__ensure_current_context guard(cuda::device_ref{0});\n    CUDART(hipStreamCreate(&stream));\n  }\n\n  launch_smoke_test(stream);\n\n  {\n    ::cuda::__ensure_current_context guard(cuda::device_ref{0});\n    CUDART(hipStreamSynchronize(stream));\n    CUDART(hipStreamDestroy(stream));\n  }\n}\n\ntemplate <typename DefaultConfig>\nstruct kernel_with_default_config\n{\n  DefaultConfig config;\n\n  kernel_with_default_config(DefaultConfig c)\n      : config(c)\n  {}\n\n  DefaultConfig default_config() const\n  {\n    return config;\n  }\n\n  template <typename Config, typename ConfigCheckFn>\n  TEST_DEVICE_FUNC void operator()(Config config, ConfigCheckFn check_fn)\n  {\n    check_fn(config);\n  }\n};\n\nstruct verify_callable\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == 256);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, config) == 4);\n    cooperative_groups::this_grid().sync();\n  }\n};\n\nC2H_CCCLRT_TEST(\"Launch with default config\", \"\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n  auto grid  = cuda::grid_dims(4);\n  auto block = cuda::block_dims<256>;\n\n  SECTION(\"Combine with empty\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block, grid, cuda::cooperative_launch())};\n    static_assert(cuda::__is_kernel_config<decltype(kernel.default_config())>);\n    static_assert(cuda::__kernel_has_default_config<decltype(kernel)>);\n\n    cuda::launch(stream, cuda::make_config(), kernel, verify_callable{});\n    stream.sync();\n  }\n  SECTION(\"Combine with no overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block)};\n    cuda::launch(stream, cuda::make_config(grid, cuda::cooperative_launch()), kernel, verify_callable{});\n    stream.sync();\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(cuda::block_dims<1>(), cuda::cooperative_launch())};\n    cuda::launch(stream, cuda::make_config(block, grid, cuda::cooperative_launch()), kernel, verify_callable{});\n    stream.sync();\n  }\n}\n\n// Regression test: cuda::launch must work when the calling function has\n// a __restrict__-qualified pointer parameter. On some nvcc + host compiler\n// combos, __restrict__ survives through the type transformation pipeline\n// and causes a function pointer conversion failure in __get_kernel_launcher.\nstruct restrict_assign_functor\n{\n  template <typename Config>\n  TEST_DEVICE_FUNC void operator()(Config config, int* __restrict__ dst)\n  {\n    *dst = 42;\n  }\n};\n\n// The __restrict__ on the function parameter is the trigger: on affected compilers,\n// it leaks into the template args of __get_kernel_launcher via cuda::launch.\nvoid launch_with_restrict_param(cuda::stream_ref stream, int* __restrict__ dst)\n{\n  auto config = cuda::make_config(cuda::grid_dims(1), cuda::block_dims<1>());\n  cuda::launch(stream, config, restrict_assign_functor{}, dst);\n}\n\nC2H_CCCLRT_TEST(\"Launch functor with __restrict__ pointer arg\", \"[launch]\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n  test::pinned<int> val{0};\n\n  launch_with_restrict_param(stream, val.get());\n  stream.sync();\n\n  CCCLRT_CHECK(*val == 42);\n}\n\n__managed__ cuda::std::size_t launched_nthreads;\n__managed__ cuda::std::size_t launched_nblocks;\n__managed__ cuda::std::size_t launched_nclusters;\n\nstruct LaunchDimsFunctor\n{\n  template <class Config>\n  TEST_DEVICE_FUNC void operator()(const Config& config)\n  {\n    cuda::atomic_ref(launched_nthreads)++;\n\n    if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0)\n    {\n      cuda::atomic_ref(launched_nblocks)++;\n\n      NV_IF_ELSE_TARGET(NV_PROVIDES_SM_90,\n                        ({\n                          if (__clusterRelativeBlockRank() == 0)\n                          {\n                            cuda::atomic_ref(launched_nclusters)++;\n                          }\n                        }),\n                        ({ cuda::atomic_ref(launched_nclusters)++; }));\n    }\n  }\n};\n\ntemplate <class GridDesc, class BlockDesc>\nvoid test_launch_dims(cuda::stream_ref stream, GridDesc grid_desc, BlockDesc block_desc)\n{\n  launched_nthreads  = 0;\n  launched_nblocks   = 0;\n  launched_nclusters = 0;\n\n  const auto& grid_exts  = grid_desc.extents();\n  const auto& block_exts = block_desc.extents();\n\n  const auto config = cuda::make_config(grid_desc, block_desc);\n  cuda::launch(stream, config, LaunchDimsFunctor{});\n  stream.sync();\n\n  const auto exp_nclusters = cuda::std::size_t{grid_exts.extent(0)} * grid_exts.extent(1) * grid_exts.extent(2);\n  CCCLRT_CHECK(launched_nclusters == exp_nclusters);\n\n  const auto exp_nblocks = exp_nclusters;\n  CCCLRT_CHECK(launched_nblocks == exp_nblocks);\n\n  const auto exp_nthreads = exp_nblocks * block_exts.extent(0) * block_exts.extent(1) * block_exts.extent(2);\n  CCCLRT_CHECK(launched_nthreads == exp_nthreads);\n}\n\ntemplate <class GridDesc, class ClusterDesc, class BlockDesc>\nvoid test_launch_dims(cuda::stream_ref stream, GridDesc grid_desc, ClusterDesc cluster_desc, BlockDesc block_desc)\n{\n  launched_nthreads  = 0;\n  launched_nblocks   = 0;\n  launched_nclusters = 0;\n\n  const auto& grid_exts    = grid_desc.extents();\n  const auto& cluster_exts = cluster_desc.extents();\n  const auto& block_exts   = block_desc.extents();\n\n  const auto config = cuda::make_config(grid_desc, cluster_desc, block_desc);\n  cuda::launch(stream, config, LaunchDimsFunctor{});\n  stream.sync();\n\n  const auto exp_nclusters = cuda::std::size_t{grid_exts.extent(0)} * grid_exts.extent(1) * grid_exts.extent(2);\n  CCCLRT_CHECK(launched_nclusters == exp_nclusters);\n\n  const auto exp_nblocks = exp_nclusters * cluster_exts.extent(0) * cluster_exts.extent(1) * cluster_exts.extent(2);\n  CCCLRT_CHECK(launched_nblocks == exp_nblocks);\n\n  const auto exp_nthreads = exp_nblocks * block_exts.extent(0) * block_exts.extent(1) * block_exts.extent(2);\n  CCCLRT_CHECK(launched_nthreads == exp_nthreads);\n}\n\nC2H_TEST(\"Launch dims\", \"[launch]\")\n{\n  cuda::stream stream{cuda::device_ref{0}};\n\n  test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::block_dims(dim3{10}));\n  test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::block_dims(dim3{3, 7}));\n  test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::block_dims(dim3{2, 7, 9}));\n\n  test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::block_dims<10>());\n  test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::block_dims<3, 7>());\n  test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::block_dims<2, 7, 9>());\n\n  test_launch_dims(stream, cuda::grid_dims<2>(), cuda::block_dims(dim3{10}));\n  test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::block_dims(dim3{3, 7}));\n  test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::block_dims(dim3{2, 7, 9}));\n\n  test_launch_dims(stream, cuda::grid_dims<2>(), cuda::block_dims<10>());\n  test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::block_dims<3, 7>());\n  test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::block_dims<2, 7, 9>());\n\n  if (cuda::device_attributes::compute_capability_major(stream.device()) >= 9)\n  {\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims(dim3{3}), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims(dim3{3}), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims<3, 7>());\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims<3>(), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims<1, 5>(), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims(dim3{2}), cuda::cluster_dims<3>(), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{2, 9}), cuda::cluster_dims<1, 5>(), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims(dim3{3, 4, 5}), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims(dim3{3}), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(\n      stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims(dim3{3}), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims(dim3{1, 5}), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims(dim3{3, 1, 2}), cuda::block_dims<2, 7, 9>());\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims<3>(), cuda::block_dims(dim3{10}));\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims<1, 5>(), cuda::block_dims(dim3{3, 7}));\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims(dim3{2, 7, 9}));\n\n    test_launch_dims(stream, cuda::grid_dims<2>(), cuda::cluster_dims<3>(), cuda::block_dims<10>());\n    test_launch_dims(stream, cuda::grid_dims<2, 9>(), cuda::cluster_dims<1, 5>(), cuda::block_dims<3, 7>());\n    test_launch_dims(stream, cuda::grid_dims<3, 4, 5>(), cuda::cluster_dims<3, 1, 2>(), cuda::block_dims<2, 7, 9>());\n  }\n}\n\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n", "source": "/kaggle/working/cccl/libcudacxx/test/libcudacxx/cuda/ccclrt/launch/launch_smoke.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/__type_traits/vector_type.h>\n#include <cuda/hierarchy>\n#include <cuda/launch>\n#include <cuda/std/cstddef>\n\n#include <iostream>\n\n#include <cooperative_groups.h>\n#include <host_device.cuh>\n\n#include \"testing.cuh\"\n\nnamespace cg = cooperative_groups;\n\nusing size_t3 = cuda::vector_type_t<cuda::std::size_t, 3>;\n\nstruct basic_test_single_dim\n{\n  static constexpr int block_size = 256;\n  static constexpr int grid_size  = 512;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims).x == grid_size * block_size);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == grid_size * block_size);\n\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::block, dims).x == block_size);\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims).x == grid_size);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, dims) == block_size);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == grid_size);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims = cuda::make_hierarchy(cuda::block_dims<block_size>(), cuda::grid_dims<grid_size>());\n    static_assert(cuda::gpu_thread.dims(cuda::grid, dims).x == grid_size * block_size);\n    static_assert(cuda::gpu_thread.count(cuda::grid, dims) == grid_size * block_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims)[0] == grid_size * block_size);\n\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims).x == block_size);\n    static_assert(cuda::block.dims(cuda::grid, dims).x == grid_size);\n    static_assert(cuda::gpu_thread.count(cuda::block, dims) == block_size);\n    static_assert(cuda::block.count(cuda::grid, dims) == grid_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims)[0] == block_size);\n\n    auto dims_dyn = cuda::make_hierarchy(cuda::block_dims(block_size), cuda::grid_dims(grid_size));\n\n    test_host_dev(dims_dyn, *this);\n\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_dyn)[0] == cuda::std::dynamic_extent);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims_dyn)[0] == cuda::std::dynamic_extent);\n\n    // Test that we can also drop the empty parens in the level constructors:\n    auto config = cuda::make_hierarchy(cuda::block_dims<block_size>, cuda::grid_dims<grid_size>);\n    CCCLRT_REQUIRE(dims == config);\n  }\n};\n\nstruct basic_test_multi_dim\n{\n  static constexpr int block_size = 256;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(32, 12, 4));\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(0) == 32);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(1) == 12);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(2) == 4);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 512 * 3);\n\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::block, dims) == dim3(2, 3, 4));\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(16, 4, 1));\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, dims) == 24);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 64);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims_multidim = cuda::make_hierarchy(cuda::block_dims<2, 3, 4>(), cuda::grid_dims<16, 4, 1>());\n\n    static_assert(cuda::gpu_thread.dims(cuda::grid, dims_multidim) == dim3(32, 12, 4));\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(0) == 32);\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(1) == 12);\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(2) == 4);\n    static_assert(cuda::gpu_thread.count(cuda::grid, dims_multidim) == 512 * 3);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims_multidim) == size_t3{32, 12, 4});\n\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims_multidim) == dim3(2, 3, 4));\n    static_assert(cuda::block.dims(cuda::grid, dims_multidim) == dim3(16, 4, 1));\n    static_assert(cuda::gpu_thread.count(cuda::block, dims_multidim) == 24);\n    static_assert(cuda::block.count(cuda::grid, dims_multidim) == 64);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_multidim) == size_t3{2, 3, 4});\n    static_assert(cuda::block.static_dims(cuda::grid, dims_multidim) == size_t3{16, 4, 1});\n\n    auto dims_multidim_dyn = cuda::make_hierarchy(cuda::block_dims(dim3(2, 3, 4)), cuda::grid_dims(dim3(16, 4, 1)));\n\n    test_host_dev(dims_multidim_dyn, *this);\n  }\n};\n\nstruct basic_test_mixed\n{\n  static constexpr int block_size = 256;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(2048, 4, 2));\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(0) == 2048);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(1) == 4);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(2) == 2);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 16 * 1024);\n\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(8, 4, 2));\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 64);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims_mixed = cuda::make_hierarchy(cuda::block_dims<block_size>(), cuda::grid_dims(dim3(8, 4, 2)));\n\n    test_host_dev(dims_mixed, *this);\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims_mixed).x == block_size);\n    static_assert(cuda::gpu_thread.count(cuda::block, dims_mixed) == block_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_mixed)[0] == block_size);\n\n    // TODO include mixed static and dynamic info on a single level\n    // Currently bugged in std::extents\n  }\n};\n\nC2H_TEST(\"Basic\", \"[hierarchy]\")\n{\n  basic_test_single_dim().run();\n  basic_test_multi_dim().run();\n  basic_test_mixed().run();\n}\n\nstruct basic_test_cluster\n{\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(512, 6, 9));\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 27 * 1024);\n\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(2, 6, 9));\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 108);\n    CCCLRT_REQUIRE(cuda::cluster.dims(cuda::grid, dims) == dim3(1, 3, 9));\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::cluster, dims) == dim3(512, 2, 1));\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    SECTION(\"Static cluster dims\")\n    {\n      auto dims = cuda::make_hierarchy(cuda::block_dims<256>(), cuda::cluster_dims<8>(), cuda::grid_dims<512>());\n\n      static_assert(cuda::gpu_thread.dims(cuda::grid, dims).x == 1024 * 1024);\n      static_assert(cuda::gpu_thread.count(cuda::grid, dims) == 1024 * 1024);\n      static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims)[0] == 1024 * 1024);\n\n      static_assert(cuda::gpu_thread.dims(cuda::block, dims).x == 256);\n      static_assert(cuda::block.dims(cuda::grid, dims).x == 4 * 1024);\n      static_assert(cuda::gpu_thread.count(cuda::cluster, dims) == 2 * 1024);\n      static_assert(cuda::cluster.count(cuda::grid, dims) == 512);\n      static_assert(cuda::gpu_thread.static_dims(cuda::block, dims)[0] == 256);\n      static_assert(cuda::block.static_dims(cuda::grid, dims)[0] == 4 * 1024);\n    }\n    SECTION(\"Mixed cluster dims\")\n    {\n      auto dims_mixed = cuda::make_hierarchy(\n        cuda::block_dims<256>(), cuda::cluster_dims(dim3(2, 2, 1)), cuda::grid_dims(dim3(1, 3, 9)));\n      test_host_dev(dims_mixed, *this, arch_filter<std::less<int>, 90>);\n      static_assert(cuda::gpu_thread.dims(cuda::block, dims_mixed).x == 256);\n      static_assert(cuda::gpu_thread.count(cuda::block, dims_mixed) == 256);\n      static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_mixed)[0] == 256);\n      static_assert(cuda::block.static_dims(cuda::cluster, dims_mixed)[0] == cuda::std::dynamic_extent);\n      static_assert(cuda::block.static_dims(cuda::grid, dims_mixed)[0] == cuda::std::dynamic_extent);\n    }\n  }\n};\n\nC2H_TEST(\"Cluster dims\", \"[hierarchy]\")\n{\n  basic_test_cluster().run();\n}\n\nC2H_TEST(\"Different constructions\", \"[hierarchy]\")\n{\n  /*\n  const auto block_size  = 512;\n  const auto cluster_cnt = 8;\n  const auto grid_size   = 256;\n\n  [[maybe_unused]] const auto config =\n    cuda::block_dims<block_size>() & cuda::cluster_dims<cluster_cnt>() &\n  cuda::grid_dims(grid_size);\n  [[maybe_unused]] const auto config2 =\n    cuda::grid_dims(grid_size) & cuda::cluster_dims<cluster_cnt>() &\n  cuda::block_dims<block_size>();\n\n  [[maybe_unused]] const auto config3 =\n    cuda::cluster_dims<cluster_cnt>() & cuda::grid_dims(grid_size) &\n  cuda::block_dims<block_size>();\n  [[maybe_unused]] const auto config4 =\n    cuda::cluster_dims<cluster_cnt>() & cuda::block_dims<block_size>() &\n  cuda::grid_dims(grid_size);\n\n  [[maybe_unused]] const auto config5 =\n    cuda::make_config(cuda::block_dims<block_size>(),\n  cuda::cluster_dims<cluster_cnt>(), cuda::grid_dims(grid_size));\n  [[maybe_unused]] const auto config6 =\n    cuda::make_config(cuda::grid_dims(grid_size),\n  cuda::cluster_dims<cluster_cnt>(), cuda::block_dims<block_size>());\n\n  static_assert(std::is_same_v<decltype(config), decltype(config2)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config3)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config4)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config5)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config6)>);\n\n  [[maybe_unused]] const auto conf_weird_order =\n    cuda::grid_dims(grid_size) & (cuda::cluster_dims<cluster_cnt>() &\n  cuda::block_dims<block_size>());\n  static_assert(std::is_same_v<decltype(config), decltype(conf_weird_order)>);\n\n  static_assert(config.hierarchy().count(cuda::gpu_thread, cuda::block) == block_size);\n  static_assert(config.hierarchy().count(cuda::gpu_thread, cuda::cluster) == cluster_cnt *\n  block_size); static_assert(config.hierarchy().count(cuda::block, cuda::cluster) ==\n  cluster_cnt); CCCLRT_REQUIRE(config.hierarchy().count() == grid_size * cluster_cnt *\n  block_size);\n\n  static_assert(config.hierarchy().has_level(cuda::block));\n  static_assert(config.hierarchy().has_level(cuda::cluster));\n  static_assert(config.hierarchy().has_level(cuda::grid));\n  static_assert(!config.hierarchy().has_level(cuda::thread));\n  */\n}\n\nC2H_TEST(\"Replace level\", \"[hierarchy]\")\n{\n// GCC 7 and 8 complains here that the hierarchy was not declared constexpr\n#if !_CCCL_COMPILER(GCC, <, 9)\n  const auto dimensions = cuda::make_hierarchy(cuda::block_dims<512>(), cuda::cluster_dims<8>(), cuda::grid_dims(256));\n  const auto fragment   = dimensions.fragment(cuda::block, cuda::grid);\n  static_assert(!fragment.has_level(cuda::block));\n  static_assert(!cuda::__has_bottom_unit_or_level_v<cuda::thread_level, decltype(fragment)>);\n  static_assert(fragment.has_level(cuda::cluster));\n  static_assert(fragment.has_level(cuda::grid));\n  static_assert(cuda::__has_bottom_unit_or_level_v<cuda::block_level, decltype(fragment)>);\n\n  const auto replaced = cuda::hierarchy_add_level(fragment, cuda::block_dims(256));\n  static_assert(replaced.has_level(cuda::block));\n  static_assert(cuda::__has_bottom_unit_or_level_v<cuda::thread_level, decltype(replaced)>);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, replaced) == 256);\n#endif // !_CCCL_COMPILER(GCC, <, 9)\n}\n\ntemplate <typename Hierarchy>\n__global__ void kernel(Hierarchy hierarchy)\n{\n  auto grid  = cg::this_grid();\n  auto block = cg::this_thread_block();\n\n  CCCLRT_REQUIRE_DEVICE(grid.thread_rank() == cuda::gpu_thread.rank(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.block_rank() == cuda::block.rank(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.block_index() == cuda::block.index(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.num_threads() == cuda::gpu_thread.count(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.num_blocks() == cuda::block.count(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.dim_blocks() == cuda::block.dims(cuda::grid));\n\n  CCCLRT_REQUIRE_DEVICE(block.thread_rank() == cuda::gpu_thread.rank(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.thread_index() == cuda::gpu_thread.index(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.num_threads() == cuda::gpu_thread.count(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.dim_threads() == cuda::gpu_thread.dims(cuda::block));\n\n  CCCLRT_REQUIRE_DEVICE(block.thread_index() == cuda::gpu_thread.index(cuda::block, hierarchy));\n\n  const auto grid_index = cuda::gpu_thread.index_as<unsigned long long>(cuda::grid, hierarchy);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.x\n    == static_cast<unsigned long long>(grid.block_index().x) * block.dim_threads().x + block.thread_index().x);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.y\n    == static_cast<unsigned long long>(grid.block_index().y) * block.dim_threads().y + block.thread_index().y);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.z\n    == static_cast<unsigned long long>(grid.block_index().z) * block.dim_threads().z + block.thread_index().z);\n\n  CCCLRT_REQUIRE_DEVICE(grid.block_rank() == cuda::block.rank(cuda::grid, hierarchy));\n  CCCLRT_REQUIRE_DEVICE(block.thread_rank() == cuda::gpu_thread.rank(cuda::block, hierarchy));\n  CCCLRT_REQUIRE_DEVICE(grid.thread_rank() == cuda::gpu_thread.rank(cuda::grid, hierarchy));\n}\n\nC2H_TEST(\"Dims queries indexing and ambient hierarchy\", \"[hierarchy]\")\n{\n  const auto hierarchies = cuda::std::make_tuple(\n    cuda::make_hierarchy(cuda::block_dims(dim3(64, 4, 2)), cuda::grid_dims(dim3(12, 6, 3))),\n    cuda::make_hierarchy(cuda::block_dims(dim3(2, 4, 64)), cuda::grid_dims(dim3(3, 6, 12))),\n    cuda::make_hierarchy(cuda::block_dims<256>(), cuda::grid_dims<4>()),\n    cuda::make_hierarchy(cuda::block_dims<16, 2, 4>(), cuda::grid_dims<2, 3, 4>()),\n    cuda::make_hierarchy(cuda::block_dims(dim3(8, 4, 2)), cuda::grid_dims<4, 5, 6>()),\n#if defined(NDEBUG)\n    cuda::make_hierarchy(cuda::block_dims<32>(), cuda::grid_dims<(1 << 30) - 2>()),\n#endif\n    cuda::make_hierarchy(cuda::block_dims<8, 2, 4>(), cuda::grid_dims(dim3(5, 4, 3))));\n\n  apply_each(\n    [](const auto& hierarchy) {\n      auto [grid, block] = cuda::get_launch_dimensions(hierarchy);\n\n      kernel<<<grid, block>>>(hierarchy);\n      CUDART(cudaDeviceSynchronize());\n    },\n    hierarchies);\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel_optimized(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cuda::gpu_thread.rank(cuda::block, hierarchy);\n  out[thread_id] = thread_id;\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cuda::gpu_thread.rank(cuda::block);\n  out[thread_id] = thread_id;\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel_cg(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cg::thread_block::thread_rank();\n  out[thread_id] = thread_id;\n}\n\n// Testcase mostly for generated code comparison\nC2H_TEST(\"On device rank calculation\", \"[hierarchy]\")\n{\n  unsigned int* ptr;\n  CUDART(cudaMalloc((void**) &ptr, 2 * 1024 * sizeof(unsigned int)));\n\n  const auto hierarchy_static = cuda::make_hierarchy(cuda::block_dims<256>(), cuda::grid_dims(dim3(2, 2, 2)));\n  rank_kernel<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(cudaDeviceSynchronize());\n  rank_kernel_cg<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(cudaDeviceSynchronize());\n  rank_kernel_optimized<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(cudaDeviceSynchronize());\n  CUDART(cudaFree(ptr));\n}\n\ntemplate <typename Hierarchy>\n__global__ void examples_kernel(Hierarchy hierarchy)\n{\n  {\n    auto thread_index_in_block = cuda::gpu_thread.index(cuda::block, hierarchy);\n    CCCLRT_REQUIRE_DEVICE(thread_index_in_block == threadIdx);\n    auto block_index_in_grid = cuda::block.index(cuda::grid, hierarchy);\n    CCCLRT_REQUIRE_DEVICE(block_index_in_grid == blockIdx);\n  }\n  {\n    int thread_rank_in_block = cuda::gpu_thread.rank(cuda::block, hierarchy);\n    int block_rank_in_grid   = cuda::block.rank(cuda::grid, hierarchy);\n  }\n  {\n    // Can be called with the instances of level types\n    int num_threads_in_block = cuda::gpu_thread.count(cuda::block);\n    int num_blocks_in_grid   = cuda::block.count(cuda::grid);\n\n    // Or using the level types as template arguments\n    int num_threads_in_grid = cuda::gpu_thread.count(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    int thread_rank_in_block = cuda::gpu_thread.rank(cuda::block);\n    int block_rank_in_grid   = cuda::block.rank(cuda::grid);\n\n    // Or using the level types as template arguments\n    int thread_rank_in_grid = cuda::gpu_thread.rank(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    CCCLRT_REQUIRE_DEVICE(cuda::gpu_thread.dims(cuda::block) == blockDim);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.dims(cuda::grid) == gridDim);\n\n    // Or using the level types as template arguments\n    auto grid_dims_in_threads = cuda::gpu_thread.dims(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    CCCLRT_REQUIRE_DEVICE(cuda::gpu_thread.index(cuda::block) == threadIdx);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.index(cuda::grid) == blockIdx);\n\n    // Or using the level types as template arguments\n    auto thread_index_in_grid = cuda::gpu_thread.index(cuda::grid);\n  }\n}\n\n// Test examples from the inline rst documentation\nC2H_TEST(\"Examples\", \"[hierarchy]\")\n{\n  // GCC 7 and 8 complains here that the hierarchy was not declared constexpr\n#if !_CCCL_COMPILER(GCC, <, 9)\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    auto fragment  = hierarchy.fragment(cuda::block, cuda::grid);\n    auto new_hierarchy = cuda::hierarchy_add_level(fragment, cuda::block_dims<128>());\n    static_assert(cuda::gpu_thread.count(cuda::block, new_hierarchy) == 128);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.count(cuda::cluster, hierarchy) == 4 * 8 * 8 * 8);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, hierarchy) == 256 * 4 * 8 * 8 * 8);\n    CCCLRT_REQUIRE(cuda::cluster.count(cuda::grid, hierarchy) == 256);\n  }\n  {\n    [[maybe_unused]] auto hierarchy =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.count(cuda::cluster, hierarchy) == 4 * 8 * 8 * 8);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(0) == 4 * 8);\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(1) == 8);\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(2) == 8);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, hierarchy).extent(0) == 256 * 4 * 8);\n    CCCLRT_REQUIRE(cuda::cluster.extents(cuda::grid, hierarchy).extent(0) == 256);\n  }\n#endif // !_CCCL_COMPILER(GCC, <, 9)\n  {\n    [[maybe_unused]] auto hierarchy =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(decltype(hierarchy.level(cuda::cluster).extents())::static_extent(0) == 4);\n  }\n  {\n    auto partial1 = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims(256), cuda::cluster_dims<4>());\n    [[maybe_unused]] auto hierarchy1 = cuda::hierarchy_add_level(partial1, cuda::block_dims<8, 8, 8>());\n    auto partial2 = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<8, 8, 8>(), cuda::cluster_dims<4>());\n    [[maybe_unused]] auto hierarchy2 = cuda::hierarchy_add_level(partial2, cuda::grid_dims(256));\n    static_assert(cuda::std::is_same_v<decltype(hierarchy1), decltype(hierarchy2)>);\n  }\n  {\n    [[maybe_unused]] auto hierarchy1 =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    [[maybe_unused]] auto hierarchy2 =\n      cuda::make_hierarchy(cuda::block_dims<8, 8, 8>(), cuda::cluster_dims<4>(), cuda::grid_dims(256));\n    static_assert(cuda::std::is_same_v<decltype(hierarchy1), decltype(hierarchy2)>);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    auto [grid_dimensions, cluster_dimensions, block_dimensions] = cuda::get_launch_dimensions(hierarchy);\n    CCCLRT_REQUIRE(grid_dimensions.x == 256 * 4);\n    CCCLRT_REQUIRE(cluster_dimensions.x == 4);\n    CCCLRT_REQUIRE(block_dimensions.x == 8);\n    CCCLRT_REQUIRE(block_dimensions.y == 8);\n    CCCLRT_REQUIRE(block_dimensions.z == 8);\n  }\n  {\n    auto hierarchy                           = cuda::make_hierarchy(cuda::grid_dims(16), cuda::block_dims<8, 8, 8>());\n    auto [grid_dimensions, block_dimensions] = cuda::get_launch_dimensions(hierarchy);\n    examples_kernel<<<grid_dimensions, block_dimensions>>>(hierarchy);\n    CUDART(cudaGetLastError());\n    CUDART(cudaDeviceSynchronize());\n  }\n}\n\nC2H_TEST(\"Trivially constructable\", \"[hierarchy]\")\n{\n  // static_assert(std::is_trivial_v<decltype(cuda::block_dims(256))>);\n  // static_assert(std::is_trivial_v<decltype(cuda::block_dims<256>())>);\n\n  // Hierarchy is not trivially copyable (yet), because tuple is not\n  // static_assert(std::is_trivially_copyable_v<decltype(cuda::block_dims<256>()\n  // & cuda::grid_dims<256>())>);\n  // static_assert(std::is_trivially_copyable_v<decltype(cuda::std::make_tuple(cuda::block_dims<256>(),\n  // cuda::grid_dims<256>()))>);\n}\n\nC2H_TEST(\"cuda::distribute\", \"[hierarchy]\")\n{\n  unsigned numElements          = 50000;\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::block.count(cuda::grid, config) == (numElements + threadsPerBlock - 1) / threadsPerBlock);\n}\n\nC2H_TEST(\"hierarchy merge\", \"[hierarchy]\")\n{\n  SECTION(\"Non overlapping\")\n  {\n    auto h1       = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<2>());\n    auto h2       = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<3>());\n    auto combined = h1.combine(h2);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined) == 6);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined) == 3);\n    static_assert(cuda::block.count(cuda::grid, combined) == 2);\n    auto combined_the_other_way = h2.combine(h1);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_the_other_way)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined_the_other_way) == 6);\n\n    auto dynamic_values   = cuda::make_hierarchy(cuda::cluster_dims(4), cuda::block_dims(5));\n    auto combined_dynamic = dynamic_values.combine(h1);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined_dynamic) == 40);\n  }\n  SECTION(\"Overlapping\")\n  {\n    auto h1       = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<2>(), cuda::cluster_dims<3>());\n    auto h2       = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<4>(), cuda::cluster_dims<5>());\n    auto combined = h1.combine(h2);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined) == 24);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined) == 4);\n    static_assert(cuda::block.count(cuda::grid, combined) == 6);\n\n    auto combined_the_other_way = h2.combine(h1);\n    static_assert(!cuda::std::is_same_v<decltype(combined), decltype(combined_the_other_way)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined_the_other_way) == 40);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined_the_other_way) == 4);\n    static_assert(cuda::block.count(cuda::grid, combined_the_other_way) == 10);\n\n    auto ultimate_combination = combined.combine(combined_the_other_way);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(ultimate_combination)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, ultimate_combination) == 24);\n\n    auto block_level_replacement = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<6>());\n    auto with_block_replaced     = block_level_replacement.combine(combined);\n    static_assert(cuda::gpu_thread.count(cuda::grid, with_block_replaced) == 36);\n    static_assert(cuda::gpu_thread.count(cuda::block, with_block_replaced) == 6);\n\n    auto grid_cluster_level_replacement =\n      cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<7>(), cuda::cluster_dims<8>());\n    auto with_grid_cluster_replaced = grid_cluster_level_replacement.combine(combined);\n    static_assert(cuda::gpu_thread.count(cuda::grid, with_grid_cluster_replaced) == 7 * 8 * 4);\n    static_assert(cuda::block.count(cuda::cluster, with_grid_cluster_replaced) == 8);\n    static_assert(cuda::cluster.count(cuda::grid, with_grid_cluster_replaced) == 7);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++, the C++ Standard Library for your entire system,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/__type_traits/vector_type.h>\n#include <cuda/hierarchy>\n#include <cuda/launch>\n#include <cuda/std/cstddef>\n\n#include <iostream>\n\n#include <hip/hip_cooperative_groups.h>\n#include <host_device.cuh>\n\n#include \"testing.cuh\"\n\nnamespace cg = cooperative_groups;\n\nusing size_t3 = cuda::vector_type_t<cuda::std::size_t, 3>;\n\nstruct basic_test_single_dim\n{\n  static constexpr int block_size = 256;\n  static constexpr int grid_size  = 512;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims).x == grid_size * block_size);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == grid_size * block_size);\n\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::block, dims).x == block_size);\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims).x == grid_size);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, dims) == block_size);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == grid_size);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims = cuda::make_hierarchy(cuda::block_dims<block_size>(), cuda::grid_dims<grid_size>());\n    static_assert(cuda::gpu_thread.dims(cuda::grid, dims).x == grid_size * block_size);\n    static_assert(cuda::gpu_thread.count(cuda::grid, dims) == grid_size * block_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims)[0] == grid_size * block_size);\n\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims).x == block_size);\n    static_assert(cuda::block.dims(cuda::grid, dims).x == grid_size);\n    static_assert(cuda::gpu_thread.count(cuda::block, dims) == block_size);\n    static_assert(cuda::block.count(cuda::grid, dims) == grid_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims)[0] == block_size);\n\n    auto dims_dyn = cuda::make_hierarchy(cuda::block_dims(block_size), cuda::grid_dims(grid_size));\n\n    test_host_dev(dims_dyn, *this);\n\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_dyn)[0] == cuda::std::dynamic_extent);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims_dyn)[0] == cuda::std::dynamic_extent);\n\n    // Test that we can also drop the empty parens in the level constructors:\n    auto config = cuda::make_hierarchy(cuda::block_dims<block_size>, cuda::grid_dims<grid_size>);\n    CCCLRT_REQUIRE(dims == config);\n  }\n};\n\nstruct basic_test_multi_dim\n{\n  static constexpr int block_size = 256;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(32, 12, 4));\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(0) == 32);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(1) == 12);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(2) == 4);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 512 * 3);\n\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::block, dims) == dim3(2, 3, 4));\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(16, 4, 1));\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, dims) == 24);\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 64);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims_multidim = cuda::make_hierarchy(cuda::block_dims<2, 3, 4>(), cuda::grid_dims<16, 4, 1>());\n\n    static_assert(cuda::gpu_thread.dims(cuda::grid, dims_multidim) == dim3(32, 12, 4));\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(0) == 32);\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(1) == 12);\n    static_assert(cuda::gpu_thread.extents(cuda::grid, dims_multidim).extent(2) == 4);\n    static_assert(cuda::gpu_thread.count(cuda::grid, dims_multidim) == 512 * 3);\n    static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims_multidim) == size_t3{32, 12, 4});\n\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims_multidim) == dim3(2, 3, 4));\n    static_assert(cuda::block.dims(cuda::grid, dims_multidim) == dim3(16, 4, 1));\n    static_assert(cuda::gpu_thread.count(cuda::block, dims_multidim) == 24);\n    static_assert(cuda::block.count(cuda::grid, dims_multidim) == 64);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_multidim) == size_t3{2, 3, 4});\n    static_assert(cuda::block.static_dims(cuda::grid, dims_multidim) == size_t3{16, 4, 1});\n\n    auto dims_multidim_dyn = cuda::make_hierarchy(cuda::block_dims(dim3(2, 3, 4)), cuda::grid_dims(dim3(16, 4, 1)));\n\n    test_host_dev(dims_multidim_dyn, *this);\n  }\n};\n\nstruct basic_test_mixed\n{\n  static constexpr int block_size = 256;\n\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(2048, 4, 2));\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(0) == 2048);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(1) == 4);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, dims).extent(2) == 2);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 16 * 1024);\n\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(8, 4, 2));\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 64);\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    auto dims_mixed = cuda::make_hierarchy(cuda::block_dims<block_size>(), cuda::grid_dims(dim3(8, 4, 2)));\n\n    test_host_dev(dims_mixed, *this);\n    static_assert(cuda::gpu_thread.dims(cuda::block, dims_mixed).x == block_size);\n    static_assert(cuda::gpu_thread.count(cuda::block, dims_mixed) == block_size);\n    static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_mixed)[0] == block_size);\n\n    // TODO include mixed static and dynamic info on a single level\n    // Currently bugged in std::extents\n  }\n};\n\nC2H_TEST(\"Basic\", \"[hierarchy]\")\n{\n  basic_test_single_dim().run();\n  basic_test_multi_dim().run();\n  basic_test_mixed().run();\n}\n\nstruct basic_test_cluster\n{\n  template <typename DynDims>\n  TEST_FUNC void operator()(const DynDims& dims) const\n  {\n    // todo: allow this after fixing CCCLRT_REQUIRE with clang-cuda\n#if !_CCCL_CUDA_COMPILER(CLANG)\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::grid, dims) == dim3(512, 6, 9));\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, dims) == 27 * 1024);\n\n    CCCLRT_REQUIRE(cuda::block.dims(cuda::grid, dims) == dim3(2, 6, 9));\n    CCCLRT_REQUIRE(cuda::block.count(cuda::grid, dims) == 108);\n    CCCLRT_REQUIRE(cuda::cluster.dims(cuda::grid, dims) == dim3(1, 3, 9));\n    CCCLRT_REQUIRE(cuda::gpu_thread.dims(cuda::cluster, dims) == dim3(512, 2, 1));\n#endif // !_CCCL_CUDA_COMPILER(CLANG)\n  }\n\n  void run()\n  {\n    SECTION(\"Static cluster dims\")\n    {\n      auto dims = cuda::make_hierarchy(cuda::block_dims<256>(), cuda::cluster_dims<8>(), cuda::grid_dims<512>());\n\n      static_assert(cuda::gpu_thread.dims(cuda::grid, dims).x == 1024 * 1024);\n      static_assert(cuda::gpu_thread.count(cuda::grid, dims) == 1024 * 1024);\n      static_assert(cuda::gpu_thread.static_dims(cuda::grid, dims)[0] == 1024 * 1024);\n\n      static_assert(cuda::gpu_thread.dims(cuda::block, dims).x == 256);\n      static_assert(cuda::block.dims(cuda::grid, dims).x == 4 * 1024);\n      static_assert(cuda::gpu_thread.count(cuda::cluster, dims) == 2 * 1024);\n      static_assert(cuda::cluster.count(cuda::grid, dims) == 512);\n      static_assert(cuda::gpu_thread.static_dims(cuda::block, dims)[0] == 256);\n      static_assert(cuda::block.static_dims(cuda::grid, dims)[0] == 4 * 1024);\n    }\n    SECTION(\"Mixed cluster dims\")\n    {\n      auto dims_mixed = cuda::make_hierarchy(\n        cuda::block_dims<256>(), cuda::cluster_dims(dim3(2, 2, 1)), cuda::grid_dims(dim3(1, 3, 9)));\n      test_host_dev(dims_mixed, *this, arch_filter<std::less<int>, 90>);\n      static_assert(cuda::gpu_thread.dims(cuda::block, dims_mixed).x == 256);\n      static_assert(cuda::gpu_thread.count(cuda::block, dims_mixed) == 256);\n      static_assert(cuda::gpu_thread.static_dims(cuda::block, dims_mixed)[0] == 256);\n      static_assert(cuda::block.static_dims(cuda::cluster, dims_mixed)[0] == cuda::std::dynamic_extent);\n      static_assert(cuda::block.static_dims(cuda::grid, dims_mixed)[0] == cuda::std::dynamic_extent);\n    }\n  }\n};\n\nC2H_TEST(\"Cluster dims\", \"[hierarchy]\")\n{\n  basic_test_cluster().run();\n}\n\nC2H_TEST(\"Different constructions\", \"[hierarchy]\")\n{\n  /*\n  const auto block_size  = 512;\n  const auto cluster_cnt = 8;\n  const auto grid_size   = 256;\n\n  [[maybe_unused]] const auto config =\n    cuda::block_dims<block_size>() & cuda::cluster_dims<cluster_cnt>() &\n  cuda::grid_dims(grid_size);\n  [[maybe_unused]] const auto config2 =\n    cuda::grid_dims(grid_size) & cuda::cluster_dims<cluster_cnt>() &\n  cuda::block_dims<block_size>();\n\n  [[maybe_unused]] const auto config3 =\n    cuda::cluster_dims<cluster_cnt>() & cuda::grid_dims(grid_size) &\n  cuda::block_dims<block_size>();\n  [[maybe_unused]] const auto config4 =\n    cuda::cluster_dims<cluster_cnt>() & cuda::block_dims<block_size>() &\n  cuda::grid_dims(grid_size);\n\n  [[maybe_unused]] const auto config5 =\n    cuda::make_config(cuda::block_dims<block_size>(),\n  cuda::cluster_dims<cluster_cnt>(), cuda::grid_dims(grid_size));\n  [[maybe_unused]] const auto config6 =\n    cuda::make_config(cuda::grid_dims(grid_size),\n  cuda::cluster_dims<cluster_cnt>(), cuda::block_dims<block_size>());\n\n  static_assert(std::is_same_v<decltype(config), decltype(config2)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config3)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config4)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config5)>);\n  static_assert(std::is_same_v<decltype(config), decltype(config6)>);\n\n  [[maybe_unused]] const auto conf_weird_order =\n    cuda::grid_dims(grid_size) & (cuda::cluster_dims<cluster_cnt>() &\n  cuda::block_dims<block_size>());\n  static_assert(std::is_same_v<decltype(config), decltype(conf_weird_order)>);\n\n  static_assert(config.hierarchy().count(cuda::gpu_thread, cuda::block) == block_size);\n  static_assert(config.hierarchy().count(cuda::gpu_thread, cuda::cluster) == cluster_cnt *\n  block_size); static_assert(config.hierarchy().count(cuda::block, cuda::cluster) ==\n  cluster_cnt); CCCLRT_REQUIRE(config.hierarchy().count() == grid_size * cluster_cnt *\n  block_size);\n\n  static_assert(config.hierarchy().has_level(cuda::block));\n  static_assert(config.hierarchy().has_level(cuda::cluster));\n  static_assert(config.hierarchy().has_level(cuda::grid));\n  static_assert(!config.hierarchy().has_level(cuda::thread));\n  */\n}\n\nC2H_TEST(\"Replace level\", \"[hierarchy]\")\n{\n// GCC 7 and 8 complains here that the hierarchy was not declared constexpr\n#if !_CCCL_COMPILER(GCC, <, 9)\n  const auto dimensions = cuda::make_hierarchy(cuda::block_dims<512>(), cuda::cluster_dims<8>(), cuda::grid_dims(256));\n  const auto fragment   = dimensions.fragment(cuda::block, cuda::grid);\n  static_assert(!fragment.has_level(cuda::block));\n  static_assert(!cuda::__has_bottom_unit_or_level_v<cuda::thread_level, decltype(fragment)>);\n  static_assert(fragment.has_level(cuda::cluster));\n  static_assert(fragment.has_level(cuda::grid));\n  static_assert(cuda::__has_bottom_unit_or_level_v<cuda::block_level, decltype(fragment)>);\n\n  const auto replaced = cuda::hierarchy_add_level(fragment, cuda::block_dims(256));\n  static_assert(replaced.has_level(cuda::block));\n  static_assert(cuda::__has_bottom_unit_or_level_v<cuda::thread_level, decltype(replaced)>);\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, replaced) == 256);\n#endif // !_CCCL_COMPILER(GCC, <, 9)\n}\n\ntemplate <typename Hierarchy>\n__global__ void kernel(Hierarchy hierarchy)\n{\n  auto grid  = cg::this_grid();\n  auto block = cg::this_thread_block();\n\n  CCCLRT_REQUIRE_DEVICE(grid.thread_rank() == cuda::gpu_thread.rank(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.block_rank() == cuda::block.rank(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.block_index() == cuda::block.index(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.num_threads() == cuda::gpu_thread.count(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.num_blocks() == cuda::block.count(cuda::grid));\n  CCCLRT_REQUIRE_DEVICE(grid.dim_blocks() == cuda::block.dims(cuda::grid));\n\n  CCCLRT_REQUIRE_DEVICE(block.thread_rank() == cuda::gpu_thread.rank(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.thread_index() == cuda::gpu_thread.index(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.num_threads() == cuda::gpu_thread.count(cuda::block));\n  CCCLRT_REQUIRE_DEVICE(block.dim_threads() == cuda::gpu_thread.dims(cuda::block));\n\n  CCCLRT_REQUIRE_DEVICE(block.thread_index() == cuda::gpu_thread.index(cuda::block, hierarchy));\n\n  const auto grid_index = cuda::gpu_thread.index_as<unsigned long long>(cuda::grid, hierarchy);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.x\n    == static_cast<unsigned long long>(grid.block_index().x) * block.dim_threads().x + block.thread_index().x);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.y\n    == static_cast<unsigned long long>(grid.block_index().y) * block.dim_threads().y + block.thread_index().y);\n  CCCLRT_REQUIRE_DEVICE(\n    grid_index.z\n    == static_cast<unsigned long long>(grid.block_index().z) * block.dim_threads().z + block.thread_index().z);\n\n  CCCLRT_REQUIRE_DEVICE(grid.block_rank() == cuda::block.rank(cuda::grid, hierarchy));\n  CCCLRT_REQUIRE_DEVICE(block.thread_rank() == cuda::gpu_thread.rank(cuda::block, hierarchy));\n  CCCLRT_REQUIRE_DEVICE(grid.thread_rank() == cuda::gpu_thread.rank(cuda::grid, hierarchy));\n}\n\nC2H_TEST(\"Dims queries indexing and ambient hierarchy\", \"[hierarchy]\")\n{\n  const auto hierarchies = cuda::std::make_tuple(\n    cuda::make_hierarchy(cuda::block_dims(dim3(64, 4, 2)), cuda::grid_dims(dim3(12, 6, 3))),\n    cuda::make_hierarchy(cuda::block_dims(dim3(2, 4, 64)), cuda::grid_dims(dim3(3, 6, 12))),\n    cuda::make_hierarchy(cuda::block_dims<256>(), cuda::grid_dims<4>()),\n    cuda::make_hierarchy(cuda::block_dims<16, 2, 4>(), cuda::grid_dims<2, 3, 4>()),\n    cuda::make_hierarchy(cuda::block_dims(dim3(8, 4, 2)), cuda::grid_dims<4, 5, 6>()),\n#if defined(NDEBUG)\n    cuda::make_hierarchy(cuda::block_dims<32>(), cuda::grid_dims<(1 << 30) - 2>()),\n#endif\n    cuda::make_hierarchy(cuda::block_dims<8, 2, 4>(), cuda::grid_dims(dim3(5, 4, 3))));\n\n  apply_each(\n    [](const auto& hierarchy) {\n      auto [grid, block] = cuda::get_launch_dimensions(hierarchy);\n\n      kernel<<<grid, block>>>(hierarchy);\n      CUDART(hipDeviceSynchronize());\n    },\n    hierarchies);\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel_optimized(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cuda::gpu_thread.rank(cuda::block, hierarchy);\n  out[thread_id] = thread_id;\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cuda::gpu_thread.rank(cuda::block);\n  out[thread_id] = thread_id;\n}\n\ntemplate <typename Hierarchy>\n__global__ void rank_kernel_cg(Hierarchy hierarchy, unsigned int* out)\n{\n  auto thread_id = cg::thread_block::thread_rank();\n  out[thread_id] = thread_id;\n}\n\n// Testcase mostly for generated code comparison\nC2H_TEST(\"On device rank calculation\", \"[hierarchy]\")\n{\n  unsigned int* ptr;\n  CUDART(hipMalloc((void**) &ptr, 2 * 1024 * sizeof(unsigned int)));\n\n  const auto hierarchy_static = cuda::make_hierarchy(cuda::block_dims<256>(), cuda::grid_dims(dim3(2, 2, 2)));\n  rank_kernel<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(hipDeviceSynchronize());\n  rank_kernel_cg<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(hipDeviceSynchronize());\n  rank_kernel_optimized<<<dim3(2, 2, 2), 256>>>(hierarchy_static, ptr);\n  CUDART(hipDeviceSynchronize());\n  CUDART(hipFree(ptr));\n}\n\ntemplate <typename Hierarchy>\n__global__ void examples_kernel(Hierarchy hierarchy)\n{\n  {\n    auto thread_index_in_block = cuda::gpu_thread.index(cuda::block, hierarchy);\n    CCCLRT_REQUIRE_DEVICE(thread_index_in_block == threadIdx);\n    auto block_index_in_grid = cuda::block.index(cuda::grid, hierarchy);\n    CCCLRT_REQUIRE_DEVICE(block_index_in_grid == blockIdx);\n  }\n  {\n    int thread_rank_in_block = cuda::gpu_thread.rank(cuda::block, hierarchy);\n    int block_rank_in_grid   = cuda::block.rank(cuda::grid, hierarchy);\n  }\n  {\n    // Can be called with the instances of level types\n    int num_threads_in_block = cuda::gpu_thread.count(cuda::block);\n    int num_blocks_in_grid   = cuda::block.count(cuda::grid);\n\n    // Or using the level types as template arguments\n    int num_threads_in_grid = cuda::gpu_thread.count(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    int thread_rank_in_block = cuda::gpu_thread.rank(cuda::block);\n    int block_rank_in_grid   = cuda::block.rank(cuda::grid);\n\n    // Or using the level types as template arguments\n    int thread_rank_in_grid = cuda::gpu_thread.rank(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    CCCLRT_REQUIRE_DEVICE(cuda::gpu_thread.dims(cuda::block) == blockDim);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.dims(cuda::grid) == gridDim);\n\n    // Or using the level types as template arguments\n    auto grid_dims_in_threads = cuda::gpu_thread.dims(cuda::grid);\n  }\n  {\n    // Can be called with the instances of level types\n    CCCLRT_REQUIRE_DEVICE(cuda::gpu_thread.index(cuda::block) == threadIdx);\n    CCCLRT_REQUIRE_DEVICE(cuda::block.index(cuda::grid) == blockIdx);\n\n    // Or using the level types as template arguments\n    auto thread_index_in_grid = cuda::gpu_thread.index(cuda::grid);\n  }\n}\n\n// Test examples from the inline rst documentation\nC2H_TEST(\"Examples\", \"[hierarchy]\")\n{\n  // GCC 7 and 8 complains here that the hierarchy was not declared constexpr\n#if !_CCCL_COMPILER(GCC, <, 9)\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    auto fragment  = hierarchy.fragment(cuda::block, cuda::grid);\n    auto new_hierarchy = cuda::hierarchy_add_level(fragment, cuda::block_dims<128>());\n    static_assert(cuda::gpu_thread.count(cuda::block, new_hierarchy) == 128);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.count(cuda::cluster, hierarchy) == 4 * 8 * 8 * 8);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, hierarchy) == 256 * 4 * 8 * 8 * 8);\n    CCCLRT_REQUIRE(cuda::cluster.count(cuda::grid, hierarchy) == 256);\n  }\n  {\n    [[maybe_unused]] auto hierarchy =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.count(cuda::cluster, hierarchy) == 4 * 8 * 8 * 8);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(0) == 4 * 8);\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(1) == 8);\n    static_assert(cuda::gpu_thread.extents(cuda::cluster, hierarchy).extent(2) == 8);\n    CCCLRT_REQUIRE(cuda::gpu_thread.extents(cuda::grid, hierarchy).extent(0) == 256 * 4 * 8);\n    CCCLRT_REQUIRE(cuda::cluster.extents(cuda::grid, hierarchy).extent(0) == 256);\n  }\n#endif // !_CCCL_COMPILER(GCC, <, 9)\n  {\n    [[maybe_unused]] auto hierarchy =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    static_assert(decltype(hierarchy.level(cuda::cluster).extents())::static_extent(0) == 4);\n  }\n  {\n    auto partial1 = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims(256), cuda::cluster_dims<4>());\n    [[maybe_unused]] auto hierarchy1 = cuda::hierarchy_add_level(partial1, cuda::block_dims<8, 8, 8>());\n    auto partial2 = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<8, 8, 8>(), cuda::cluster_dims<4>());\n    [[maybe_unused]] auto hierarchy2 = cuda::hierarchy_add_level(partial2, cuda::grid_dims(256));\n    static_assert(cuda::std::is_same_v<decltype(hierarchy1), decltype(hierarchy2)>);\n  }\n  {\n    [[maybe_unused]] auto hierarchy1 =\n      cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    [[maybe_unused]] auto hierarchy2 =\n      cuda::make_hierarchy(cuda::block_dims<8, 8, 8>(), cuda::cluster_dims<4>(), cuda::grid_dims(256));\n    static_assert(cuda::std::is_same_v<decltype(hierarchy1), decltype(hierarchy2)>);\n  }\n  {\n    auto hierarchy = cuda::make_hierarchy(cuda::grid_dims(256), cuda::cluster_dims<4>(), cuda::block_dims<8, 8, 8>());\n    auto [grid_dimensions, cluster_dimensions, block_dimensions] = cuda::get_launch_dimensions(hierarchy);\n    CCCLRT_REQUIRE(grid_dimensions.x == 256 * 4);\n    CCCLRT_REQUIRE(cluster_dimensions.x == 4);\n    CCCLRT_REQUIRE(block_dimensions.x == 8);\n    CCCLRT_REQUIRE(block_dimensions.y == 8);\n    CCCLRT_REQUIRE(block_dimensions.z == 8);\n  }\n  {\n    auto hierarchy                           = cuda::make_hierarchy(cuda::grid_dims(16), cuda::block_dims<8, 8, 8>());\n    auto [grid_dimensions, block_dimensions] = cuda::get_launch_dimensions(hierarchy);\n    examples_kernel<<<grid_dimensions, block_dimensions>>>(hierarchy);\n    CUDART(hipGetLastError());\n    CUDART(hipDeviceSynchronize());\n  }\n}\n\nC2H_TEST(\"Trivially constructable\", \"[hierarchy]\")\n{\n  // static_assert(std::is_trivial_v<decltype(cuda::block_dims(256))>);\n  // static_assert(std::is_trivial_v<decltype(cuda::block_dims<256>())>);\n\n  // Hierarchy is not trivially copyable (yet), because tuple is not\n  // static_assert(std::is_trivially_copyable_v<decltype(cuda::block_dims<256>()\n  // & cuda::grid_dims<256>())>);\n  // static_assert(std::is_trivially_copyable_v<decltype(cuda::std::make_tuple(cuda::block_dims<256>(),\n  // cuda::grid_dims<256>()))>);\n}\n\nC2H_TEST(\"cuda::distribute\", \"[hierarchy]\")\n{\n  unsigned numElements          = 50000;\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::block, config) == 256);\n  CCCLRT_REQUIRE(cuda::block.count(cuda::grid, config) == (numElements + threadsPerBlock - 1) / threadsPerBlock);\n}\n\nC2H_TEST(\"hierarchy merge\", \"[hierarchy]\")\n{\n  SECTION(\"Non overlapping\")\n  {\n    auto h1       = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<2>());\n    auto h2       = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<3>());\n    auto combined = h1.combine(h2);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined) == 6);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined) == 3);\n    static_assert(cuda::block.count(cuda::grid, combined) == 2);\n    auto combined_the_other_way = h2.combine(h1);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(combined_the_other_way)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined_the_other_way) == 6);\n\n    auto dynamic_values   = cuda::make_hierarchy(cuda::cluster_dims(4), cuda::block_dims(5));\n    auto combined_dynamic = dynamic_values.combine(h1);\n    CCCLRT_REQUIRE(cuda::gpu_thread.count(cuda::grid, combined_dynamic) == 40);\n  }\n  SECTION(\"Overlapping\")\n  {\n    auto h1       = cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<2>(), cuda::cluster_dims<3>());\n    auto h2       = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<4>(), cuda::cluster_dims<5>());\n    auto combined = h1.combine(h2);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined) == 24);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined) == 4);\n    static_assert(cuda::block.count(cuda::grid, combined) == 6);\n\n    auto combined_the_other_way = h2.combine(h1);\n    static_assert(!cuda::std::is_same_v<decltype(combined), decltype(combined_the_other_way)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, combined_the_other_way) == 40);\n    static_assert(cuda::gpu_thread.count(cuda::block, combined_the_other_way) == 4);\n    static_assert(cuda::block.count(cuda::grid, combined_the_other_way) == 10);\n\n    auto ultimate_combination = combined.combine(combined_the_other_way);\n    static_assert(cuda::std::is_same_v<decltype(combined), decltype(ultimate_combination)>);\n    static_assert(cuda::gpu_thread.count(cuda::grid, ultimate_combination) == 24);\n\n    auto block_level_replacement = cuda::make_hierarchy<cuda::thread_level>(cuda::block_dims<6>());\n    auto with_block_replaced     = block_level_replacement.combine(combined);\n    static_assert(cuda::gpu_thread.count(cuda::grid, with_block_replaced) == 36);\n    static_assert(cuda::gpu_thread.count(cuda::block, with_block_replaced) == 6);\n\n    auto grid_cluster_level_replacement =\n      cuda::make_hierarchy<cuda::block_level>(cuda::grid_dims<7>(), cuda::cluster_dims<8>());\n    auto with_grid_cluster_replaced = grid_cluster_level_replacement.combine(combined);\n    static_assert(cuda::gpu_thread.count(cuda::grid, with_grid_cluster_replaced) == 7 * 8 * 4);\n    static_assert(cuda::block.count(cuda::cluster, with_grid_cluster_replaced) == 8);\n    static_assert(cuda::cluster.count(cuda::grid, with_grid_cluster_replaced) == 7);\n  }\n}\n", "source": "/kaggle/working/cccl/libcudacxx/test/libcudacxx/cuda/ccclrt/hierarchy/hierarchy_smoke.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void add_relaxed_device_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.fetch_add(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*add_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#RESULT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#RESULT]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.add.relaxed.gpu.s32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void add_relaxed_device_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.fetch_add(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*add_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#RESULT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#RESULT]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.add.relaxed.gpu.s32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_add_non_volatile.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void exch_device_relaxed_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.exchange(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*exch_device_relaxed_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.exch.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void exch_device_relaxed_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.exchange(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*exch_device_relaxed_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.exch.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_exch_non_volatile.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void load_relaxed_device_non_volatile(int* data, int* out)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.load(cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*load_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-NEXT: {{/*[[:space:]] *}}ld.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void load_relaxed_device_non_volatile(int* data, int* out)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.load(cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*load_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-NEXT: {{/*[[:space:]] *}}ld.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_load_non_volatile.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void cas_device_relaxed_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  ref.compare_exchange_strong(*out, n, cuda::std::memory_order_relaxed);\n}\n\n// clang-format off\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*cas_device_relaxed_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-DAG:  ld.global.{{b|u}}32 %r[[#LOCALEXP:]], [%rd[[#INPUT]]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.cas.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#LOCALEXP]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void cas_device_relaxed_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  ref.compare_exchange_strong(*out, n, cuda::std::memory_order_relaxed);\n}\n\n// clang-format off\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*cas_device_relaxed_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#EXPECTED:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#EXPECTED]];\n; SM8X-DAG:  ld.global.{{b|u}}32 %r[[#LOCALEXP:]], [%rd[[#INPUT]]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.cas.relaxed.gpu.b32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#LOCALEXP]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_cas_non_volatile.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void store_relaxed_device_non_volatile(int* data, int in)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  ref.store(in, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*store_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-NEXT: {{/*[[:space:]] *}}st.relaxed.gpu.b32 [%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void store_relaxed_device_non_volatile(int* data, int in)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  ref.store(in, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*store_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-NEXT: {{/*[[:space:]] *}}st.relaxed.gpu.b32 [%rd[[#ATOM]]],%r[[#INPUT]];{{[[:space:]]/*}}\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_store_non_volatile.cu"}
{"input": "#include <cuda/atomic>\n\n__global__ void sub_relaxed_device_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.fetch_sub(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*sub_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#RESULT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#RESULT]];\n; SM8X-NEXT: neg.s32 %r[[#NEG:]], %r[[#INPUT]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.add.relaxed.gpu.s32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#NEG]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "output": "#include \"hip/hip_runtime.h\"\n#include <cuda/atomic>\n\n__global__ void sub_relaxed_device_non_volatile(int* data, int* out, int n)\n{\n  auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{*(data)};\n  *out     = ref.fetch_sub(n, cuda::std::memory_order_relaxed);\n}\n\n/*\n\n; SM8X-LABEL: .target sm_80\n; SM8X:      .visible .entry [[FUNCTION:_.*sub_relaxed_device_non_volatile.*]](\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#ATOM:]], {{.*}}[[FUNCTION]]_param_0{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}64 %rd[[#RESULT:]], {{.*}}[[FUNCTION]]_param_1{{.*}}\n; SM8X-DAG:  ld.param.{{b|u}}32 %r[[#INPUT:]], {{.*}}[[FUNCTION]]_param_2{{.*}}\n; SM8X-DAG:  cvta.to.global.u64 %rd[[#GOUT:]], %rd[[#RESULT]];\n; SM8X-NEXT: neg.s32 %r[[#NEG:]], %r[[#INPUT]];\n; SM8X-NEXT: {{/*[[:space:]] *}}atom.add.relaxed.gpu.s32 %r[[#DEST:]],[%rd[[#ATOM]]],%r[[#NEG]];{{[[:space:]]/*}}\n; SM8X-NEXT: st.global.{{b|u}}32 [%rd[[#GOUT]]], %r[[#DEST]];\n; SM8X-NEXT: ret;\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/atomic_codegen/atomic_sub_non_volatile.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_decrement_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  Vec_f32_4 vec(values);\n  --vec;\n\n  out[0] = vec[0];\n  out[1] = vec[1];\n  out[2] = vec[2];\n  out[3] = vec[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_decrement_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_decrement_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  Vec_f32_4 vec(values);\n  --vec;\n\n  out[0] = vec[0];\n  out[1] = vec[1];\n  out[2] = vec[2];\n  out[3] = vec[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_decrement_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/decrement_f32x2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <cuda_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_less_f16_4(const __half* lhs, const __half* rhs, bool* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const auto result = lhs_vec < rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_less_f16_4\n; SM80: {{.*HSETP2.*}}\n; SM80: {{.*HSETP2.*}}\n; SM90: {{.*HSETP2.*}}\n; SM90: {{.*HSETP2.*}}\n; SM100: {{.*HSETP2.*}}\n; SM100: {{.*HSETP2.*}}\n; SM120: {{.*HSETP2.*}}\n; SM120: {{.*HSETP2.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <hip/hip_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_less_f16_4(const __half* lhs, const __half* rhs, bool* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const auto result = lhs_vec < rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_less_f16_4\n; SM80: {{.*HSETP2.*}}\n; SM80: {{.*HSETP2.*}}\n; SM90: {{.*HSETP2.*}}\n; SM90: {{.*HSETP2.*}}\n; SM100: {{.*HSETP2.*}}\n; SM100: {{.*HSETP2.*}}\n; SM120: {{.*HSETP2.*}}\n; SM120: {{.*HSETP2.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/less_f16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <cuda_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_plus_f16_4(const __half* lhs, const __half* rhs, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_f16_4\n; SMXX: {{.*(HADD2|HFMA2).*}}\n; SMXX: {{.*(HADD2|HFMA2).*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <hip/hip_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_plus_f16_4(const __half* lhs, const __half* rhs, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_f16_4\n; SMXX: {{.*(HADD2|HFMA2).*}}\n; SMXX: {{.*(HADD2|HFMA2).*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/plus_f16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <cuda_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__nv_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_operator_multiplies_bf16_4(const __nv_bfloat16* lhs, const __nv_bfloat16* rhs, __nv_bfloat16* out)\n{\n  const cuda::std::array<__nv_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__nv_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 result = lhs_vec * rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_multiplies_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HMUL2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HMUL2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HMUL2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <hip/hip_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__hip_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_operator_multiplies_bf16_4(const __hip_bfloat16* lhs, const __hip_bfloat16* rhs, __hip_bfloat16* out)\n{\n  const cuda::std::array<__hip_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__hip_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 result = lhs_vec * rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_multiplies_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HMUL2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HMUL2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HMUL2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/multiplies_bf16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_minus_f32_4(const float* lhs, const float* rhs, float* out)\n{\n  const cuda::std::array<float, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<float, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f32_4 lhs_vec(lhs_values);\n  const Vec_f32_4 rhs_vec(rhs_values);\n  const Vec_f32_4 result = lhs_vec - rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_minus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_minus_f32_4(const float* lhs, const float* rhs, float* out)\n{\n  const cuda::std::array<float, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<float, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f32_4 lhs_vec(lhs_values);\n  const Vec_f32_4 rhs_vec(rhs_values);\n  const Vec_f32_4 result = lhs_vec - rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_minus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/minus_f32x2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_plus_f32_4(const float* lhs, const float* rhs, float* out)\n{\n  const cuda::std::array<float, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<float, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f32_4 lhs_vec(lhs_values);\n  const Vec_f32_4 rhs_vec(rhs_values);\n  const Vec_f32_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_plus_f32_4(const float* lhs, const float* rhs, float* out)\n{\n  const cuda::std::array<float, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<float, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f32_4 lhs_vec(lhs_values);\n  const Vec_f32_4 rhs_vec(rhs_values);\n  const Vec_f32_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/plus_f32x2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_increment_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  Vec_f32_4 vec(values);\n  ++vec;\n\n  out[0] = vec[0];\n  out[1] = vec[1];\n  out[2] = vec[2];\n  out[3] = vec[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_increment_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_increment_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  Vec_f32_4 vec(values);\n  ++vec;\n\n  out[0] = vec[0];\n  out[1] = vec[1];\n  out[2] = vec[2];\n  out[3] = vec[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_increment_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/increment_f32x2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <cuda_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_fma_f16_4(const __half* lhs, const __half* rhs, const __half* add, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n  const cuda::std::array<__half, 4> add_values{add[0], add[1], add[2], add[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 add_vec(add_values);\n  const Vec_f16_4 result = lhs_vec * rhs_vec + add_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_fma_f16_4\n; SM80: {{.*HFMA2.*}}\n; SM80: {{.*HFMA2.*}}\n; SM90: {{.*HFMA2.*}}\n; SM90: {{.*HFMA2.*}}\n; SM100: {{.*HFMA2.*}}\n; SM100: {{.*HFMA2.*}}\n; SM120: {{.*HFMA2.*}}\n; SM120: {{.*HFMA2.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <hip/hip_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_fma_f16_4(const __half* lhs, const __half* rhs, const __half* add, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n  const cuda::std::array<__half, 4> add_values{add[0], add[1], add[2], add[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 add_vec(add_values);\n  const Vec_f16_4 result = lhs_vec * rhs_vec + add_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_fma_f16_4\n; SM80: {{.*HFMA2.*}}\n; SM80: {{.*HFMA2.*}}\n; SM90: {{.*HFMA2.*}}\n; SM90: {{.*HFMA2.*}}\n; SM100: {{.*HFMA2.*}}\n; SM100: {{.*HFMA2.*}}\n; SM120: {{.*HFMA2.*}}\n; SM120: {{.*HFMA2.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/fma_f16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_unary_minus_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  const Vec_f32_4 vec(values);\n  const Vec_f32_4 result = -vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_unary_minus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f32_4 = simd::basic_vec<float, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_unary_minus_f32_4(const float* in, float* out)\n{\n  const cuda::std::array<float, 4> values{in[0], in[1], in[2], in[3]};\n\n  const Vec_f32_4 vec(values);\n  const Vec_f32_4 result = -vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_unary_minus_f32_4\n; SM100: {{.*FADD2.*}}\n; SM100: {{.*FADD2.*}}\n\n*/\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/unary_minus_f32x2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <cuda_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__nv_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_operator_plus_bf16_4(const __nv_bfloat16* lhs, const __nv_bfloat16* rhs, __nv_bfloat16* out)\n{\n  const cuda::std::array<__nv_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__nv_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HADD2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HADD2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HADD2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <hip/hip_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__hip_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_operator_plus_bf16_4(const __hip_bfloat16* lhs, const __hip_bfloat16* rhs, __hip_bfloat16* out)\n{\n  const cuda::std::array<__hip_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__hip_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 result = lhs_vec + rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_plus_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HADD2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HADD2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HADD2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/plus_bf16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <cuda_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__nv_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_less_bf16_4(const __nv_bfloat16* lhs, const __nv_bfloat16* rhs, bool* out)\n{\n  const cuda::std::array<__nv_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__nv_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const auto result = lhs_vec < rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_less_bf16_4\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM90: {{.*HSETP2.*BF16.*}}\n; SM90: {{.*HSETP2.*BF16.*}}\n; SM100: {{.*HSETP2.*BF16.*}}\n; SM100: {{.*HSETP2.*BF16.*}}\n; SM120: {{.*HSETP2.*BF16.*}}\n; SM120: {{.*HSETP2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <hip/hip_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__hip_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_less_bf16_4(const __hip_bfloat16* lhs, const __hip_bfloat16* rhs, bool* out)\n{\n  const cuda::std::array<__hip_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__hip_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const auto result = lhs_vec < rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_less_bf16_4\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM80: {{.*FSETP\\.LT.*}}\n; SM90: {{.*HSETP2.*BF16.*}}\n; SM90: {{.*HSETP2.*BF16.*}}\n; SM100: {{.*HSETP2.*BF16.*}}\n; SM100: {{.*HSETP2.*BF16.*}}\n; SM120: {{.*HSETP2.*BF16.*}}\n; SM120: {{.*HSETP2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/less_bf16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <cuda_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_multiplies_f16_4(const __half* lhs, const __half* rhs, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 result = lhs_vec * rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_multiplies_f16_4\n; SM80: {{.*(HMUL2|HFMA2).*}}\n; SM80: {{.*(HMUL2|HFMA2).*}}\n; SM90: {{.*(HMUL2|HFMA2).*}}\n; SM90: {{.*(HMUL2|HFMA2).*}}\n; SM100: {{.*(HMUL2|HFMA2).*}}\n; SM100: {{.*(HMUL2|HFMA2).*}}\n; SM120: {{.*(HMUL2|HFMA2).*}}\n; SM120: {{.*(HMUL2|HFMA2).*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVFP16()\n\n#  include <hip/hip_fp16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_f16_4 = simd::basic_vec<__half, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void test_operator_multiplies_f16_4(const __half* lhs, const __half* rhs, __half* out)\n{\n  const cuda::std::array<__half, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__half, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n\n  const Vec_f16_4 lhs_vec(lhs_values);\n  const Vec_f16_4 rhs_vec(rhs_values);\n  const Vec_f16_4 result = lhs_vec * rhs_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_operator_multiplies_f16_4\n; SM80: {{.*(HMUL2|HFMA2).*}}\n; SM80: {{.*(HMUL2|HFMA2).*}}\n; SM90: {{.*(HMUL2|HFMA2).*}}\n; SM90: {{.*(HMUL2|HFMA2).*}}\n; SM100: {{.*(HMUL2|HFMA2).*}}\n; SM100: {{.*(HMUL2|HFMA2).*}}\n; SM120: {{.*(HMUL2|HFMA2).*}}\n; SM120: {{.*(HMUL2|HFMA2).*}}\n\n*/\n\n#endif // _CCCL_HAS_NVFP16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/multiplies_f16.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <cuda_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__nv_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_fma_bf16_4(const __nv_bfloat16* lhs, const __nv_bfloat16* rhs, const __nv_bfloat16* add, __nv_bfloat16* out)\n{\n  const cuda::std::array<__nv_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__nv_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n  const cuda::std::array<__nv_bfloat16, 4> add_values{add[0], add[1], add[2], add[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 add_vec(add_values);\n  const Vec_bf16_4 result = lhs_vec * rhs_vec + add_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_fma_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of libcu++ in the CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/__simd_> // IWYU pragma: keep\n#include <cuda/std/array>\n\n#if _CCCL_HAS_NVBF16()\n\n#  include <hip/hip_bf16.h>\n\nnamespace simd = cuda::std::simd;\n\nusing Vec_bf16_4 = simd::basic_vec<__hip_bfloat16, simd::fixed_size<4>>;\n\nextern \"C\" __global__ void\ntest_fma_bf16_4(const __hip_bfloat16* lhs, const __hip_bfloat16* rhs, const __hip_bfloat16* add, __hip_bfloat16* out)\n{\n  const cuda::std::array<__hip_bfloat16, 4> lhs_values{lhs[0], lhs[1], lhs[2], lhs[3]};\n  const cuda::std::array<__hip_bfloat16, 4> rhs_values{rhs[0], rhs[1], rhs[2], rhs[3]};\n  const cuda::std::array<__hip_bfloat16, 4> add_values{add[0], add[1], add[2], add[3]};\n\n  const Vec_bf16_4 lhs_vec(lhs_values);\n  const Vec_bf16_4 rhs_vec(rhs_values);\n  const Vec_bf16_4 add_vec(add_values);\n  const Vec_bf16_4 result = lhs_vec * rhs_vec + add_vec;\n\n  out[0] = result[0];\n  out[1] = result[1];\n  out[2] = result[2];\n  out[3] = result[3];\n}\n\n/*\n\n; SMXX-LABEL: {{[[:space:]]*}}Function : test_fma_bf16_4\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM80: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM90: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM100: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n; SM120: {{.*HFMA2.*BF16.*}}\n\n*/\n\n#endif // _CCCL_HAS_NVBF16()\n", "source": "/kaggle/working/cccl/libcudacxx/test/simd_codegen/fma_bf16.cu"}
{"input": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <cstdio>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <cuda/std/span>\n\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include <cuda_runtime.h>\n\n#include \"vector.cuh\"\n\nnamespace cudax = cuda::experimental;\nusing cudax::in;\nusing cudax::out;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(cudax::span<const float> A, cudax::span<const float> B, cudax::span<float> C)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < A.size())\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\n/**\n * Host main routine\n */\nint main()\ntry\n{\n  // A CUDA stream on which to execute the vector addition kernel\n  cudax::stream stream(cuda::devices[0]);\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host vectors\n  cudax::vector<float> A(numElements); // input\n  cudax::vector<float> B(numElements); // input\n  cudax::vector<float> C(numElements); // output\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    A[i] = rand() / (float) RAND_MAX;\n    B[i] = rand() / (float) RAND_MAX;\n  }\n\n  // Define the kernel launch parameters\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  // Launch the vectorAdd kernel\n  printf(\"CUDA kernel launch with %zu blocks of %d threads\\n\", cuda::block.count(cuda::grid, config), threadsPerBlock);\n  cudax::launch(stream, config, vectorAdd, in(A), in(B), out(C));\n\n  printf(\"waiting for the stream to finish\\n\");\n  stream.sync();\n\n  printf(\"verifying the results\\n\");\n  // Verify that the result vector is correct\n  for (int i = 0; i < numElements; ++i)\n  {\n    if (fabs(A[i] + B[i] - C[i]) > 1e-5)\n    {\n      fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  printf(\"Test PASSED\\n\");\n\n  printf(\"Done\\n\");\n  return 0;\n}\ncatch (const std::exception& e)\n{\n  printf(\"caught an exception: \\\"%s\\\"\\n\", e.what());\n}\ncatch (...)\n{\n  printf(\"caught an unknown exception\\n\");\n}\n", "output": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <cstdio>\n\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include <cuda/std/span>\n\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include <hip/hip_runtime.h>\n\n#include \"vector.cuh\"\n\nnamespace cudax = cuda::experimental;\nusing cudax::in;\nusing cudax::out;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(cudax::span<const float> A, cudax::span<const float> B, cudax::span<float> C)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < A.size())\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\n/**\n * Host main routine\n */\nint main()\ntry\n{\n  // A CUDA stream on which to execute the vector addition kernel\n  cudax::stream stream(cuda::devices[0]);\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host vectors\n  cudax::vector<float> A(numElements); // input\n  cudax::vector<float> B(numElements); // input\n  cudax::vector<float> C(numElements); // output\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    A[i] = rand() / (float) RAND_MAX;\n    B[i] = rand() / (float) RAND_MAX;\n  }\n\n  // Define the kernel launch parameters\n  constexpr int threadsPerBlock = 256;\n  auto config                   = cuda::distribute<threadsPerBlock>(numElements);\n\n  // Launch the vectorAdd kernel\n  printf(\"CUDA kernel launch with %zu blocks of %d threads\\n\", cuda::block.count(cuda::grid, config), threadsPerBlock);\n  cudax::launch(stream, config, vectorAdd, in(A), in(B), out(C));\n\n  printf(\"waiting for the stream to finish\\n\");\n  stream.sync();\n\n  printf(\"verifying the results\\n\");\n  // Verify that the result vector is correct\n  for (int i = 0; i < numElements; ++i)\n  {\n    if (fabs(A[i] + B[i] - C[i]) > 1e-5)\n    {\n      fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  printf(\"Test PASSED\\n\");\n\n  printf(\"Done\\n\");\n  return 0;\n}\ncatch (const std::exception& e)\n{\n  printf(\"caught an exception: \\\"%s\\\"\\n\", e.what());\n}\ncatch (...)\n{\n  printf(\"caught an unknown exception\\n\");\n}\n", "source": "/kaggle/working/cccl/cudax/examples/vector_add.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An example of Fibonacci sequence illustrating how we can use\n *        dynamically created logical data\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint fibo_ref(int n)\n{\n  if (n < 2)\n  {\n    return n;\n  }\n  else\n  {\n    return fibo_ref(n - 1) + fibo_ref(n - 2);\n  }\n}\n\n__global__ void add(slice<int> out, const slice<const int> in1, const slice<const int> in2)\n{\n  out(0) = in1(0) + in2(0);\n}\n\n__global__ void set(slice<int> out, int val)\n{\n  out(0) = val;\n}\n\nlogical_data<slice<int>> compute_fibo(context& ctx, int n)\n{\n  auto out = ctx.logical_data(shape_of<slice<int>>(1));\n  if (n < 2)\n  {\n    ctx.task(out.write())->*[=](cudaStream_t s, auto sout) {\n      set<<<1, 1, 0, s>>>(sout, n);\n    };\n  }\n  else\n  {\n    auto fib1 = compute_fibo(ctx, n - 1);\n    auto fib2 = compute_fibo(ctx, n - 2);\n    ctx.task(fib1.read(), fib2.read(), out.write())->*[=](cudaStream_t s, auto s1, auto s2, auto sout) {\n      add<<<1, 1, 0, s>>>(sout, s1, s2);\n    };\n  }\n\n  return out;\n}\n\nint main(int argc, char** argv)\n{\n  int n = (argc > 1) ? atoi(argv[1]) : 4;\n\n  context ctx; // = graph_ctx();\n  auto result = compute_fibo(ctx, n);\n  ctx.host_launch(result.read())->*[&](auto res) {\n    EXPECT(res(0) == fibo_ref(n));\n  };\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An example of Fibonacci sequence illustrating how we can use\n *        dynamically created logical data\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint fibo_ref(int n)\n{\n  if (n < 2)\n  {\n    return n;\n  }\n  else\n  {\n    return fibo_ref(n - 1) + fibo_ref(n - 2);\n  }\n}\n\n__global__ void add(slice<int> out, const slice<const int> in1, const slice<const int> in2)\n{\n  out(0) = in1(0) + in2(0);\n}\n\n__global__ void set(slice<int> out, int val)\n{\n  out(0) = val;\n}\n\nlogical_data<slice<int>> compute_fibo(context& ctx, int n)\n{\n  auto out = ctx.logical_data(shape_of<slice<int>>(1));\n  if (n < 2)\n  {\n    ctx.task(out.write())->*[=](hipStream_t s, auto sout) {\n      set<<<1, 1, 0, s>>>(sout, n);\n    };\n  }\n  else\n  {\n    auto fib1 = compute_fibo(ctx, n - 1);\n    auto fib2 = compute_fibo(ctx, n - 2);\n    ctx.task(fib1.read(), fib2.read(), out.write())->*[=](hipStream_t s, auto s1, auto s2, auto sout) {\n      add<<<1, 1, 0, s>>>(sout, s1, s2);\n    };\n  }\n\n  return out;\n}\n\nint main(int argc, char** argv)\n{\n  int n = (argc > 1) ? atoi(argv[1]) : 4;\n\n  context ctx; // = graph_ctx();\n  auto result = compute_fibo(ctx, n);\n  ctx.host_launch(result.read())->*[&](auto res) {\n    EXPECT(res(0) == fibo_ref(n));\n  };\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/04-fibonacci.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with CUDA kernel in a task\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with CUDA kernel in a task\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/01-axpy.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Illustrate how to use the void data interface\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy_kernel() {}\n\nint main()\n{\n  context ctx;\n\n  auto token = ctx.token();\n  ctx.task(token.write())->*[](cudaStream_t) {\n\n  };\n\n  void_interface sync;\n  auto token2 = ctx.logical_data(sync);\n\n  auto token3 = ctx.token();\n  ctx.task(token2.write(), token.read())->*[](cudaStream_t) {\n\n  };\n\n  // Do not pass useless arguments by removing void_interface arguments\n  // Note that the rw() access is possible even if there was no prior write()\n  // or actual underlying data.\n  ctx.task(token3.rw(), token.read())->*[](cudaStream_t) {\n\n  };\n\n  ctx.cuda_kernel(token3.rw())->*[]() {\n    return cuda_kernel_desc{dummy_kernel, 16, 128, 0};\n  };\n\n  EXPECT(token.is_void_interface());\n  EXPECT(token2.is_void_interface());\n  EXPECT(token3.is_void_interface());\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Illustrate how to use the void data interface\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy_kernel() {}\n\nint main()\n{\n  context ctx;\n\n  auto token = ctx.token();\n  ctx.task(token.write())->*[](hipStream_t) {\n\n  };\n\n  void_interface sync;\n  auto token2 = ctx.logical_data(sync);\n\n  auto token3 = ctx.token();\n  ctx.task(token2.write(), token.read())->*[](hipStream_t) {\n\n  };\n\n  // Do not pass useless arguments by removing void_interface arguments\n  // Note that the rw() access is possible even if there was no prior write()\n  // or actual underlying data.\n  ctx.task(token3.rw(), token.read())->*[](hipStream_t) {\n\n  };\n\n  ctx.cuda_kernel(token3.rw())->*[]() {\n    return cuda_kernel_desc{dummy_kernel, 16, 128, 0};\n  };\n\n  EXPECT(token.is_void_interface());\n  EXPECT(token2.is_void_interface());\n  EXPECT(token3.is_void_interface());\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/void_data_interface.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Toy example to reproduce the asynchrony of a 1F1B pipeline\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void forward(slice<int>, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\n__global__ void backward(slice<int>, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  context ctx;\n  // Use a graph context if the second argument is set and not null\n  if (argc > 2 && atoi(argv[2]))\n  {\n    ctx = graph_ctx();\n  }\n\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  auto occ_f = reserved::compute_occupancy(forward);\n  auto occ_b = reserved::compute_occupancy(backward);\n\n  int factor = 1;\n  if (argc > 1)\n  {\n    factor = atoi(argv[1]);\n  }\n\n  size_t num_batches = 8 * factor;\n  int num_devs       = 8;\n  int real_devs;\n  cuda_safe_call(cudaGetDeviceCount(&real_devs));\n\n  std::vector<logical_data<slice<int>>> data;\n\n  for (size_t b = 0; b < num_batches; b++)\n  {\n    auto batch_data = ctx.logical_data(shape_of<slice<int>>(1024));\n    data.push_back(batch_data);\n\n    ctx.task(exec_place::device(0), data[b].write())->*[](cudaStream_t, auto) {\n      // Init ...\n    };\n  }\n\n  cuda_safe_call(cudaStreamSynchronize(ctx.fence()));\n\n  size_t niter = 10;\n\n  for (size_t iter = 0; iter < niter; iter++)\n  {\n    for (size_t b = 0; b < num_batches; b++)\n    {\n      for (int d = 0; d < num_devs; d++)\n      {\n        ctx.task(exec_place::device(d % real_devs), data[b].rw())->*[=](cudaStream_t s, auto bd) {\n          int ms                  = 10;\n          long long int clock_cnt = (long long int) (ms * clock_rate / factor);\n          forward<<<occ_f.min_grid_size, occ_f.block_size, 0, s>>>(bd, clock_cnt);\n        };\n      }\n      //        }\n      //\n      //        for (size_t b = 0; b < num_batches; b++) {\n      for (int d = num_devs; d-- > 0;)\n      {\n        ctx.task(exec_place::device(d % real_devs), data[b].rw())->*[=](cudaStream_t s, auto bd) {\n          int ms                  = 20;\n          long long int clock_cnt = (long long int) (ms * clock_rate / factor);\n          backward<<<occ_b.min_grid_size, occ_b.block_size, 0, s>>>(bd, clock_cnt);\n        };\n      }\n    }\n\n    /* We introduce a fence because the actual pipeline would introduce\n     * some all to all communication to update coefficients */\n    cuda_safe_call(cudaStreamSynchronize(ctx.fence()));\n  }\n\n  ctx.finalize();\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Toy example to reproduce the asynchrony of a 1F1B pipeline\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void forward(slice<int>, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\n__global__ void backward(slice<int>, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  context ctx;\n  // Use a graph context if the second argument is set and not null\n  if (argc > 2 && atoi(argv[2]))\n  {\n    ctx = graph_ctx();\n  }\n\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  auto occ_f = reserved::compute_occupancy(forward);\n  auto occ_b = reserved::compute_occupancy(backward);\n\n  int factor = 1;\n  if (argc > 1)\n  {\n    factor = atoi(argv[1]);\n  }\n\n  size_t num_batches = 8 * factor;\n  int num_devs       = 8;\n  int real_devs;\n  cuda_safe_call(hipGetDeviceCount(&real_devs));\n\n  std::vector<logical_data<slice<int>>> data;\n\n  for (size_t b = 0; b < num_batches; b++)\n  {\n    auto batch_data = ctx.logical_data(shape_of<slice<int>>(1024));\n    data.push_back(batch_data);\n\n    ctx.task(exec_place::device(0), data[b].write())->*[](hipStream_t, auto) {\n      // Init ...\n    };\n  }\n\n  cuda_safe_call(hipStreamSynchronize(ctx.fence()));\n\n  size_t niter = 10;\n\n  for (size_t iter = 0; iter < niter; iter++)\n  {\n    for (size_t b = 0; b < num_batches; b++)\n    {\n      for (int d = 0; d < num_devs; d++)\n      {\n        ctx.task(exec_place::device(d % real_devs), data[b].rw())->*[=](hipStream_t s, auto bd) {\n          int ms                  = 10;\n          long long int clock_cnt = (long long int) (ms * clock_rate / factor);\n          forward<<<occ_f.min_grid_size, occ_f.block_size, 0, s>>>(bd, clock_cnt);\n        };\n      }\n      //        }\n      //\n      //        for (size_t b = 0; b < num_batches; b++) {\n      for (int d = num_devs; d-- > 0;)\n      {\n        ctx.task(exec_place::device(d % real_devs), data[b].rw())->*[=](hipStream_t s, auto bd) {\n          int ms                  = 20;\n          long long int clock_cnt = (long long int) (ms * clock_rate / factor);\n          backward<<<occ_b.min_grid_size, occ_b.block_size, 0, s>>>(bd, clock_cnt);\n        };\n      }\n    }\n\n    /* We introduce a fence because the actual pipeline would introduce\n     * some all to all communication to update coefficients */\n    cuda_safe_call(hipStreamSynchronize(ctx.fence()));\n  }\n\n  ctx.finalize();\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/1f1b.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA graph backend and\n *        a host callback\n *\n * The host_launch mechanism is also illustrated\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  graph_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  /* Asynchronously check the result on the host */\n  ctx.host_launch(lX.read(), lY.read())->*[&](auto hX, auto hY) {\n    for (size_t ind = 0; ind < hX.extent(0); ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(hY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(hX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA graph backend and\n *        a host callback\n *\n * The host_launch mechanism is also illustrated\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  graph_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  /* Asynchronously check the result on the host */\n  ctx.host_launch(lX.read(), lY.read())->*[&](auto hX, auto hY) {\n    for (size_t ind = 0; ind < hX.extent(0); ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(hY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(hX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/02-axpy-host_launch.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how we can annotate tasks and logical data with debugging symbol\n *\n * CUDASTF_DOT_FILE=axpy.dot build/examples/axpy-annotated\n *\n * # Generate the visualization from this dot file in PDF or PNG format\n * dot -Tpdf axpy.dot -o axpy.pdf\n * dot -Tpng axpy.dot -o axpy.png\n *\n * # Generate visualization with events (for advanced users)\n * CUDASTF_DOT_IGNORE_PREREQS=0 CUDASTF_DOT_FILE=axpy-with-events.dot build/examples/axpy-annotated\n * dot -Tpng axpy-with-events.dot -o axpy-with-events.png\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(Y).set_symbol(\"Y\");\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw()).set_symbol(\"axpy\")->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how we can annotate tasks and logical data with debugging symbol\n *\n * CUDASTF_DOT_FILE=axpy.dot build/examples/axpy-annotated\n *\n * # Generate the visualization from this dot file in PDF or PNG format\n * dot -Tpdf axpy.dot -o axpy.pdf\n * dot -Tpng axpy.dot -o axpy.png\n *\n * # Generate visualization with events (for advanced users)\n * CUDASTF_DOT_IGNORE_PREREQS=0 CUDASTF_DOT_FILE=axpy-with-events.dot build/examples/axpy-annotated\n * dot -Tpng axpy-with-events.dot -o axpy-with-events.png\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(Y).set_symbol(\"Y\");\n\n  /* Compute Y = Y + alpha X */\n  ctx.task(lX.read(), lY.rw()).set_symbol(\"axpy\")->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/axpy-annotated.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel described using a cuda_kernel construct\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // axpy<<<16, 128, 0, ...>>>(alpha, dX, dY)\n    return cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY};\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel described using a cuda_kernel construct\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X */\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // axpy<<<16, 128, 0, ...>>>(alpha, dX, dY)\n    return cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY};\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/01-axpy-cuda_kernel.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An example of Fibonacci sequence illustrating how we can use\n *        dynamically created logical data and the run_once utility\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint fibo_ref(int n)\n{\n  if (n < 2)\n  {\n    return n;\n  }\n  else\n  {\n    return fibo_ref(n - 1) + fibo_ref(n - 2);\n  }\n}\n\n__global__ void add(slice<int> out, const slice<const int> in1, const slice<const int> in2)\n{\n  out(0) = in1(0) + in2(0);\n}\n\n__global__ void set(slice<int> out, int val)\n{\n  out(0) = val;\n}\n\nlogical_data<slice<int>> compute_fibo(context& ctx, int n)\n{\n  // The result for a given value n is memoized in a logical_data that will be reused every time we compute the same\n  // value\n  return run_once(n)->*[&](int n) {\n    auto result = ctx.logical_data(shape_of<slice<int>>(1)).set_symbol(std::to_string(n));\n    if (n < 2)\n    {\n      ctx.task(result.write()).set_symbol(\"fibo\" + std::to_string(n))->*[=](cudaStream_t s, auto sresult) {\n        set<<<1, 1, 0, s>>>(sresult, n);\n      };\n    }\n    else\n    {\n      auto fib2 = compute_fibo(ctx, n - 2);\n      auto fib1 = compute_fibo(ctx, n - 1);\n      ctx.task(fib1.read(), fib2.read(), result.write()).set_symbol(\"fibo\" + std::to_string(n))\n          ->*[=](cudaStream_t s, auto s1, auto s2, auto sresult) {\n                add<<<1, 1, 0, s>>>(sresult, s1, s2);\n              };\n    }\n    return result;\n  };\n}\n\nint main(int argc, char** argv)\n{\n  int n = (argc > 1) ? atoi(argv[1]) : 4;\n\n  context ctx;\n  auto result = compute_fibo(ctx, n);\n  ctx.host_launch(result.read())->*[&](auto res) {\n    EXPECT(res(0) == fibo_ref(n));\n  };\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An example of Fibonacci sequence illustrating how we can use\n *        dynamically created logical data and the run_once utility\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint fibo_ref(int n)\n{\n  if (n < 2)\n  {\n    return n;\n  }\n  else\n  {\n    return fibo_ref(n - 1) + fibo_ref(n - 2);\n  }\n}\n\n__global__ void add(slice<int> out, const slice<const int> in1, const slice<const int> in2)\n{\n  out(0) = in1(0) + in2(0);\n}\n\n__global__ void set(slice<int> out, int val)\n{\n  out(0) = val;\n}\n\nlogical_data<slice<int>> compute_fibo(context& ctx, int n)\n{\n  // The result for a given value n is memoized in a logical_data that will be reused every time we compute the same\n  // value\n  return run_once(n)->*[&](int n) {\n    auto result = ctx.logical_data(shape_of<slice<int>>(1)).set_symbol(std::to_string(n));\n    if (n < 2)\n    {\n      ctx.task(result.write()).set_symbol(\"fibo\" + std::to_string(n))->*[=](hipStream_t s, auto sresult) {\n        set<<<1, 1, 0, s>>>(sresult, n);\n      };\n    }\n    else\n    {\n      auto fib2 = compute_fibo(ctx, n - 2);\n      auto fib1 = compute_fibo(ctx, n - 1);\n      ctx.task(fib1.read(), fib2.read(), result.write()).set_symbol(\"fibo\" + std::to_string(n))\n          ->*[=](hipStream_t s, auto s1, auto s2, auto sresult) {\n                add<<<1, 1, 0, s>>>(sresult, s1, s2);\n              };\n    }\n    return result;\n  };\n}\n\nint main(int argc, char** argv)\n{\n  int n = (argc > 1) ? atoi(argv[1]) : 4;\n\n  context ctx;\n  auto result = compute_fibo(ctx, n);\n  ctx.host_launch(result.read())->*[&](auto res) {\n    EXPECT(res(0) == fibo_ref(n));\n  };\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/04-fibonacci-run_once.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Example of reduction implementing using CUB kernels\n */\n\n#include <thrust/device_vector.h>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <int BLOCK_THREADS, typename T>\n__global__ void reduce(slice<const T> values, slice<T> partials, size_t nelems)\n{\n  using namespace cub;\n  typedef BlockReduce<T, BLOCK_THREADS> BlockReduceT;\n\n  auto thread_id = BLOCK_THREADS * blockIdx.x + threadIdx.x;\n\n  // Local reduction\n  T local_sum = 0;\n  for (size_t ind = thread_id; ind < nelems; ind += blockDim.x * gridDim.x)\n  {\n    local_sum += values(ind);\n  }\n\n  __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T result = BlockReduceT(temp_storage).Sum(local_sum);\n\n  if (threadIdx.x == 0)\n  {\n    partials(blockIdx.x) = result;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const size_t N          = 1024 * 16;\n  const size_t BLOCK_SIZE = 128;\n  const size_t num_blocks = 32;\n\n  int *X, ref_tot;\n\n  X       = new int[N];\n  ref_tot = 0;\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = rand() % N;\n    ref_tot += X[ind];\n  }\n\n  auto values   = ctx.logical_data(X, {N});\n  auto partials = ctx.logical_data(shape_of<slice<int>>(num_blocks));\n  auto result   = ctx.logical_data(shape_of<slice<int>>(1));\n\n  ctx.task(values.read(), partials.write(), result.write())->*[&](auto stream, auto values, auto partials, auto result) {\n    // reduce values into partials\n    reduce<BLOCK_SIZE, int><<<num_blocks, BLOCK_SIZE, 0, stream>>>(values, partials, N);\n\n    // reduce partials on a single block into result\n    reduce<BLOCK_SIZE, int><<<1, BLOCK_SIZE, 0, stream>>>(partials, result, num_blocks);\n  };\n\n  ctx.host_launch(result.read())->*[&](auto p) {\n    if (p(0) != ref_tot)\n    {\n      fprintf(stderr, \"INCORRECT RESULT: p sum = %d, ref tot = %d\\n\", p(0), ref_tot);\n      abort();\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Example of reduction implementing using CUB kernels\n */\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <int BLOCK_THREADS, typename T>\n__global__ void reduce(slice<const T> values, slice<T> partials, size_t nelems)\n{\n  using namespace hipcub;\n  typedef BlockReduce<T, BLOCK_THREADS> BlockReduceT;\n\n  auto thread_id = BLOCK_THREADS * blockIdx.x + threadIdx.x;\n\n  // Local reduction\n  T local_sum = 0;\n  for (size_t ind = thread_id; ind < nelems; ind += blockDim.x * gridDim.x)\n  {\n    local_sum += values(ind);\n  }\n\n  __shared__ typename BlockReduceT::TempStorage temp_storage;\n\n  // Per-thread tile data\n  T result = BlockReduceT(temp_storage).Sum(local_sum);\n\n  if (threadIdx.x == 0)\n  {\n    partials(blockIdx.x) = result;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const size_t N          = 1024 * 16;\n  const size_t BLOCK_SIZE = 128;\n  const size_t num_blocks = 32;\n\n  int *X, ref_tot;\n\n  X       = new int[N];\n  ref_tot = 0;\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = rand() % N;\n    ref_tot += X[ind];\n  }\n\n  auto values   = ctx.logical_data(X, {N});\n  auto partials = ctx.logical_data(shape_of<slice<int>>(num_blocks));\n  auto result   = ctx.logical_data(shape_of<slice<int>>(1));\n\n  ctx.task(values.read(), partials.write(), result.write())->*[&](auto stream, auto values, auto partials, auto result) {\n    // reduce values into partials\n    reduce<BLOCK_SIZE, int><<<num_blocks, BLOCK_SIZE, 0, stream>>>(values, partials, N);\n\n    // reduce partials on a single block into result\n    reduce<BLOCK_SIZE, int><<<1, BLOCK_SIZE, 0, stream>>>(partials, result, num_blocks);\n  };\n\n  ctx.host_launch(result.read())->*[&](auto p) {\n    if (p(0) != ref_tot)\n    {\n      fprintf(stderr, \"INCORRECT RESULT: p sum = %d, ref tot = %d\\n\", p(0), ref_tot);\n      abort();\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/08-cub-reduce.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses host memory from the device\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(cudaGetDevice(&dev));\n  assert(dev >= 0);\n  cudaDeviceProp prop;\n  cuda_safe_call(cudaGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(lX.read(data_place::host()), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses host memory from the device\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(hipGetDevice(&dev));\n  assert(dev >= 0);\n  hipDeviceProp_t prop;\n  cuda_safe_call(hipGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(lX.read(data_place::host()), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/explicit_data_places.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how to create a custom data interface and use them in tasks\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/**\n * @brief A simple class describing a contiguous matrix of size (m, n)\n */\ntemplate <typename T>\nclass matrix\n{\npublic:\n  matrix(size_t m, size_t n, T* base)\n      : m(m)\n      , n(n)\n      , base(base)\n  {}\n\n  __host__ __device__ T& operator()(size_t i, size_t j)\n  {\n    return base[i + j * m];\n  }\n\n  __host__ __device__ const T& operator()(size_t i, size_t j) const\n  {\n    return base[i + j * m];\n  }\n\n  size_t m, n;\n  T* base;\n};\n\n/**\n * @brief defines the shape of a matrix\n *\n * Note that we specialize cuda::experimental::stf::shape_of to avoid ambiguous specialization\n *\n * @extends shape_of\n */\ntemplate <typename T>\nclass cuda::experimental::stf::shape_of<matrix<T>>\n{\npublic:\n  /**\n   * @brief The default constructor.\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of() = default;\n\n  explicit shape_of(size_t m, size_t n)\n      : m(m)\n      , n(n)\n  {}\n\n  /**\n   * @name Copies a shape.\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of(const shape_of&) = default;\n\n  /**\n   * @brief Extracts the shape from a matrix\n   *\n   * @param M matrix to get the shape from\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of(const matrix<T>& M)\n      : shape_of<matrix<T>>(M.m, M.n)\n  {}\n\n  /// Mandatory method : defined the total number of elements in the shape\n  size_t size() const\n  {\n    return m * n;\n  }\n\n  using coords_t = ::std::tuple<size_t, size_t>;\n\n  // This transforms a tuple of (shape, 1D index) into a coordinate\n  _CCCL_HOST_DEVICE coords_t index_to_coords(size_t index) const\n  {\n    return coords_t(index % m, index / m);\n  }\n\n  size_t m;\n  size_t n;\n};\n\n/**\n * @brief Data interface to manipulate a matrix in the CUDA stream backend\n */\ntemplate <typename T>\nclass matrix_stream_interface : public stream_data_interface_simple<matrix<T>>\n{\npublic:\n  using base = stream_data_interface_simple<matrix<T>>;\n  using typename base::shape_t;\n\n  /// Initialize from an existing matrix\n  matrix_stream_interface(matrix<T> m)\n      : base(std::move(m))\n  {}\n\n  /// Initialize from a shape of matrix\n  matrix_stream_interface(typename base::shape_t s)\n      : base(s)\n  {}\n\n  /// Copy the content of an instance to another instance\n  ///\n  /// This implementation assumes that we have registered memory if one of the data place is the host\n  void stream_data_copy(\n    const data_place& dst_memory_node,\n    instance_id_t dst_instance_id,\n    const data_place& src_memory_node,\n    instance_id_t src_instance_id,\n    cudaStream_t stream) override\n  {\n    assert(src_memory_node != dst_memory_node);\n\n    cudaMemcpyKind kind = cudaMemcpyDeviceToDevice;\n    if (src_memory_node.is_host())\n    {\n      kind = cudaMemcpyHostToDevice;\n    }\n\n    if (dst_memory_node.is_host())\n    {\n      kind = cudaMemcpyDeviceToHost;\n    }\n\n    const matrix<T>& src_instance = this->instance(src_instance_id);\n    const matrix<T>& dst_instance = this->instance(dst_instance_id);\n\n    size_t sz = src_instance.m * src_instance.n * sizeof(T);\n\n    cuda_safe_call(cudaMemcpyAsync((void*) dst_instance.base, (void*) src_instance.base, sz, kind, stream));\n  }\n\n  /// allocate an instance on a specific data place\n  ///\n  /// setting *s to a negative value informs CUDASTF that the allocation\n  /// failed, and that a memory reclaiming mechanism need to be performed.\n  void stream_data_allocate(\n    backend_ctx_untyped& /*unused*/,\n    const data_place& memory_node,\n    instance_id_t instance_id,\n    ::std::ptrdiff_t& s,\n    void** /*unused*/,\n    cudaStream_t stream) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    size_t sz           = instance.m * instance.n * sizeof(T);\n\n    T* base_ptr;\n\n    if (memory_node.is_host())\n    {\n      // Fallback to a synchronous method as there is no asynchronous host allocation API\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      cuda_safe_call(cudaHostAlloc(&base_ptr, sz, cudaHostAllocMapped));\n    }\n    else\n    {\n      cuda_safe_call(cudaMallocAsync(&base_ptr, sz, stream));\n    }\n\n    // By filling a positive number, we notify that the allocation was successful\n    s = sz;\n\n    instance.base = base_ptr;\n  }\n\n  /// deallocate an instance\n  void stream_data_deallocate(\n    backend_ctx_untyped& /*unused*/,\n    const data_place& memory_node,\n    instance_id_t instance_id,\n    void* /*unused*/,\n    cudaStream_t stream) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    if (memory_node.is_host())\n    {\n      // Fallback to a synchronous method as there is no asynchronous host deallocation API\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      cuda_safe_call(cudaFreeHost(instance.base));\n    }\n    else\n    {\n      cuda_safe_call(cudaFreeAsync(instance.base, stream));\n    }\n  }\n\n  /// Register the host memory associated to an instance of matrix\n  ///\n  /// Note that this pin_host_memory method is not mandatory, but then it is\n  /// the responsibility of the user to only passed memory that is already\n  /// registered, and the allocation method on the host must allocate\n  /// registered memory too. Otherwise, copy methods need to be synchronous.\n  bool pin_host_memory(instance_id_t instance_id) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    if (!instance.base)\n    {\n      return false;\n    }\n\n    cuda_safe_call(pin_memory(instance.base, instance.m * instance.n * sizeof(T)));\n\n    return true;\n  }\n\n  /// Unregister memory pinned by pin_host_memory\n  void unpin_host_memory(instance_id_t instance_id) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    unpin_memory(instance.base);\n  }\n};\n\n/**\n * @brief Define how the CUDA stream backend must manipulate a matrix\n *\n * Note that we specialize cuda::experimental::stf::shape_of to avoid ambiguous specialization\n *\n * @extends streamed_interface_of\n */\ntemplate <typename T>\nstruct cuda::experimental::stf::streamed_interface_of<matrix<T>>\n{\n  using type = matrix_stream_interface<T>;\n};\n\n/**\n * @brief A hash of the matrix\n */\ntemplate <typename T>\nstruct cuda::experimental::stf::hash<matrix<T>>\n{\n  std::size_t operator()(matrix<T> const& m) const noexcept\n  {\n    // Combine hashes from the base address and sizes\n    return cuda::experimental::stf::hash_all(m.m, m.n, m.base);\n  }\n};\n\ntemplate <typename T>\n__global__ void kernel(matrix<T> M)\n{\n  int tid_x      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads_x = gridDim.x * blockDim.x;\n\n  int tid_y      = blockIdx.y * blockDim.y + threadIdx.y;\n  int nthreads_y = gridDim.y * blockDim.y;\n\n  for (int x = tid_x; x < M.m; x += nthreads_x)\n  {\n    for (int y = tid_y; y < M.n; y += nthreads_y)\n    {\n      M(x, y) += -x + 7 * y;\n    }\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const size_t m = 8;\n  const size_t n = 10;\n  std::vector<int> v(m * n);\n\n  matrix<int> M(m, n, &v[0]);\n\n  // M(i,j) = 17 * i + 23 * j\n  for (size_t j = 0; j < n; j++)\n  {\n    for (size_t i = 0; i < m; i++)\n    {\n      M(i, j) = 17 * i + 23 * j;\n    }\n  }\n\n  auto lM = ctx.logical_data(M);\n\n  // M(i,j) +=  -i + 7*i\n  ctx.task(lM.rw())->*[](cudaStream_t s, auto dM) {\n    kernel<<<dim3(8, 8), dim3(8, 8), 0, s>>>(dM);\n  };\n\n  // M(i,j) +=  2*i + 6*j\n  ctx.parallel_for(lM.shape(), lM.rw())->*[] _CCCL_DEVICE(size_t i, size_t j, auto dM) {\n    dM(i, j) += 2 * i + 6 * j;\n  };\n\n  ctx.finalize();\n\n  for (size_t j = 0; j < n; j++)\n  {\n    for (size_t i = 0; i < m; i++)\n    {\n      assert(M(i, j) == (17 * i + 23 * j) + (-i + 7 * j) + (2 * i + 6 * j));\n    }\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how to create a custom data interface and use them in tasks\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/**\n * @brief A simple class describing a contiguous matrix of size (m, n)\n */\ntemplate <typename T>\nclass matrix\n{\npublic:\n  matrix(size_t m, size_t n, T* base)\n      : m(m)\n      , n(n)\n      , base(base)\n  {}\n\n  __host__ __device__ T& operator()(size_t i, size_t j)\n  {\n    return base[i + j * m];\n  }\n\n  __host__ __device__ const T& operator()(size_t i, size_t j) const\n  {\n    return base[i + j * m];\n  }\n\n  size_t m, n;\n  T* base;\n};\n\n/**\n * @brief defines the shape of a matrix\n *\n * Note that we specialize cuda::experimental::stf::shape_of to avoid ambiguous specialization\n *\n * @extends shape_of\n */\ntemplate <typename T>\nclass cuda::experimental::stf::shape_of<matrix<T>>\n{\npublic:\n  /**\n   * @brief The default constructor.\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of() = default;\n\n  explicit shape_of(size_t m, size_t n)\n      : m(m)\n      , n(n)\n  {}\n\n  /**\n   * @name Copies a shape.\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of(const shape_of&) = default;\n\n  /**\n   * @brief Extracts the shape from a matrix\n   *\n   * @param M matrix to get the shape from\n   *\n   * All `shape_of` specializations must define this constructor.\n   */\n  shape_of(const matrix<T>& M)\n      : shape_of<matrix<T>>(M.m, M.n)\n  {}\n\n  /// Mandatory method : defined the total number of elements in the shape\n  size_t size() const\n  {\n    return m * n;\n  }\n\n  using coords_t = ::std::tuple<size_t, size_t>;\n\n  // This transforms a tuple of (shape, 1D index) into a coordinate\n  _CCCL_HOST_DEVICE coords_t index_to_coords(size_t index) const\n  {\n    return coords_t(index % m, index / m);\n  }\n\n  size_t m;\n  size_t n;\n};\n\n/**\n * @brief Data interface to manipulate a matrix in the CUDA stream backend\n */\ntemplate <typename T>\nclass matrix_stream_interface : public stream_data_interface_simple<matrix<T>>\n{\npublic:\n  using base = stream_data_interface_simple<matrix<T>>;\n  using typename base::shape_t;\n\n  /// Initialize from an existing matrix\n  matrix_stream_interface(matrix<T> m)\n      : base(std::move(m))\n  {}\n\n  /// Initialize from a shape of matrix\n  matrix_stream_interface(typename base::shape_t s)\n      : base(s)\n  {}\n\n  /// Copy the content of an instance to another instance\n  ///\n  /// This implementation assumes that we have registered memory if one of the data place is the host\n  void stream_data_copy(\n    const data_place& dst_memory_node,\n    instance_id_t dst_instance_id,\n    const data_place& src_memory_node,\n    instance_id_t src_instance_id,\n    hipStream_t stream) override\n  {\n    assert(src_memory_node != dst_memory_node);\n\n    hipMemcpyKind kind = hipMemcpyDeviceToDevice;\n    if (src_memory_node.is_host())\n    {\n      kind = hipMemcpyHostToDevice;\n    }\n\n    if (dst_memory_node.is_host())\n    {\n      kind = hipMemcpyDeviceToHost;\n    }\n\n    const matrix<T>& src_instance = this->instance(src_instance_id);\n    const matrix<T>& dst_instance = this->instance(dst_instance_id);\n\n    size_t sz = src_instance.m * src_instance.n * sizeof(T);\n\n    cuda_safe_call(hipMemcpyAsync((void*) dst_instance.base, (void*) src_instance.base, sz, kind, stream));\n  }\n\n  /// allocate an instance on a specific data place\n  ///\n  /// setting *s to a negative value informs CUDASTF that the allocation\n  /// failed, and that a memory reclaiming mechanism need to be performed.\n  void stream_data_allocate(\n    backend_ctx_untyped& /*unused*/,\n    const data_place& memory_node,\n    instance_id_t instance_id,\n    ::std::ptrdiff_t& s,\n    void** /*unused*/,\n    hipStream_t stream) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    size_t sz           = instance.m * instance.n * sizeof(T);\n\n    T* base_ptr;\n\n    if (memory_node.is_host())\n    {\n      // Fallback to a synchronous method as there is no asynchronous host allocation API\n      cuda_safe_call(hipStreamSynchronize(stream));\n      cuda_safe_call(hipHostAlloc(&base_ptr, sz, hipHostMallocMapped));\n    }\n    else\n    {\n      cuda_safe_call(hipMallocAsync(&base_ptr, sz, stream));\n    }\n\n    // By filling a positive number, we notify that the allocation was successful\n    s = sz;\n\n    instance.base = base_ptr;\n  }\n\n  /// deallocate an instance\n  void stream_data_deallocate(\n    backend_ctx_untyped& /*unused*/,\n    const data_place& memory_node,\n    instance_id_t instance_id,\n    void* /*unused*/,\n    hipStream_t stream) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    if (memory_node.is_host())\n    {\n      // Fallback to a synchronous method as there is no asynchronous host deallocation API\n      cuda_safe_call(hipStreamSynchronize(stream));\n      cuda_safe_call(hipHostFree(instance.base));\n    }\n    else\n    {\n      cuda_safe_call(hipFreeAsync(instance.base, stream));\n    }\n  }\n\n  /// Register the host memory associated to an instance of matrix\n  ///\n  /// Note that this pin_host_memory method is not mandatory, but then it is\n  /// the responsibility of the user to only passed memory that is already\n  /// registered, and the allocation method on the host must allocate\n  /// registered memory too. Otherwise, copy methods need to be synchronous.\n  bool pin_host_memory(instance_id_t instance_id) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    if (!instance.base)\n    {\n      return false;\n    }\n\n    cuda_safe_call(pin_memory(instance.base, instance.m * instance.n * sizeof(T)));\n\n    return true;\n  }\n\n  /// Unregister memory pinned by pin_host_memory\n  void unpin_host_memory(instance_id_t instance_id) override\n  {\n    matrix<T>& instance = this->instance(instance_id);\n    unpin_memory(instance.base);\n  }\n};\n\n/**\n * @brief Define how the CUDA stream backend must manipulate a matrix\n *\n * Note that we specialize cuda::experimental::stf::shape_of to avoid ambiguous specialization\n *\n * @extends streamed_interface_of\n */\ntemplate <typename T>\nstruct cuda::experimental::stf::streamed_interface_of<matrix<T>>\n{\n  using type = matrix_stream_interface<T>;\n};\n\n/**\n * @brief A hash of the matrix\n */\ntemplate <typename T>\nstruct cuda::experimental::stf::hash<matrix<T>>\n{\n  std::size_t operator()(matrix<T> const& m) const noexcept\n  {\n    // Combine hashes from the base address and sizes\n    return cuda::experimental::stf::hash_all(m.m, m.n, m.base);\n  }\n};\n\ntemplate <typename T>\n__global__ void kernel(matrix<T> M)\n{\n  int tid_x      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads_x = gridDim.x * blockDim.x;\n\n  int tid_y      = blockIdx.y * blockDim.y + threadIdx.y;\n  int nthreads_y = gridDim.y * blockDim.y;\n\n  for (int x = tid_x; x < M.m; x += nthreads_x)\n  {\n    for (int y = tid_y; y < M.n; y += nthreads_y)\n    {\n      M(x, y) += -x + 7 * y;\n    }\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const size_t m = 8;\n  const size_t n = 10;\n  std::vector<int> v(m * n);\n\n  matrix<int> M(m, n, &v[0]);\n\n  // M(i,j) = 17 * i + 23 * j\n  for (size_t j = 0; j < n; j++)\n  {\n    for (size_t i = 0; i < m; i++)\n    {\n      M(i, j) = 17 * i + 23 * j;\n    }\n  }\n\n  auto lM = ctx.logical_data(M);\n\n  // M(i,j) +=  -i + 7*i\n  ctx.task(lM.rw())->*[](hipStream_t s, auto dM) {\n    kernel<<<dim3(8, 8), dim3(8, 8), 0, s>>>(dM);\n  };\n\n  // M(i,j) +=  2*i + 6*j\n  ctx.parallel_for(lM.shape(), lM.rw())->*[] _CCCL_DEVICE(size_t i, size_t j, auto dM) {\n    dM(i, j) += 2 * i + 6 * j;\n  };\n\n  ctx.finalize();\n\n  for (size_t j = 0; j < n; j++)\n  {\n    for (size_t i = 0; i < m; i++)\n    {\n      assert(M(i, j) == (17 * i + 23 * j) + (-i + 7 * j) + (2 * i + 6 * j));\n    }\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/custom_data_interface.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  ctx.cuda_kernel_chain(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // clang-format off\n        return std::vector<cuda_kernel_desc> {\n            { axpy, 16, 128, 0, alpha, dX, dY },\n            { axpy, 16, 128, 0, beta, dX, dY },\n            { axpy, 16, 128, 0, gamma, dX, dY }\n        };\n    // clang-format on\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  ctx.cuda_kernel_chain(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // clang-format off\n        return std::vector<cuda_kernel_desc> {\n            { axpy, 16, 128, 0, alpha, dX, dY },\n            { axpy, 16, 128, 0, beta, dX, dY },\n            { axpy, 16, 128, 0, gamma, dX, dY }\n        };\n    // clang-format on\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/01-axpy-cuda_kernel_chain.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Conjugate gradient for a tiled dense matrix\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic cublasHandle_t cublas_handle;\n\nstream_ctx ctx;\n\nclass matrix\n{\npublic:\n  matrix(size_t N)\n      : N(N)\n  {\n    h_addr.reset(new double[N * N]);\n    cuda_safe_call(cudaHostRegister(h_addr.get(), N * N * sizeof(double), cudaHostRegisterPortable));\n    handle = ::std::make_shared<logical_data<slice<double, 2>>>(\n      ctx.logical_data(make_slice(h_addr.get(), std::tuple{N, N}, N)));\n  }\n\n  void fill(const std::function<double(int, int)>& f)\n  {\n    ctx.task(exec_place::host(), handle->write())->*[&f](cudaStream_t stream, auto ds) {\n      cuda_safe_call(cudaStreamSynchronize(stream));\n\n      for (size_t col = 0; col < ds.extent(1); col++)\n      {\n        for (size_t row = 0; row < ds.extent(0); row++)\n        {\n          ds(row, col) = f(row, col);\n        }\n      }\n    };\n  }\n\n  size_t N;\n  std::unique_ptr<double[]> h_addr;\n  std::shared_ptr<logical_data<slice<double, 2>>> handle;\n};\n\nclass vector\n{\npublic:\n  vector(size_t N, size_t _block_size, bool is_tmp = false)\n      : N(N)\n      , block_size(_block_size)\n      , nblocks((N + block_size - 1) / block_size)\n  {\n    handles.resize(nblocks);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      for (size_t b = 0; b < nblocks; b++)\n      {\n        size_t bs  = std::min(N - block_size * b, block_size);\n        handles[b] = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(bs)));\n      }\n    }\n    else\n    {\n      h_addr.reset(new double[N]);\n      cuda_safe_call(cudaHostRegister(h_addr.get(), N * sizeof(double), cudaHostRegisterPortable));\n      for (size_t b = 0; b < nblocks; b++)\n      {\n        size_t bs = std::min(N - block_size * b, block_size);\n        handles[b] =\n          ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(make_slice(&h_addr[block_size * b], bs)));\n      }\n    }\n  }\n\n  // Copy constructor\n  vector(const vector& a)\n      : N(a.N)\n      , block_size(a.block_size)\n      , nblocks(a.nblocks)\n  {\n    handles.resize(nblocks);\n\n    for (size_t b = 0; b < nblocks; b++)\n    {\n      size_t bs  = std::min(N - block_size * b, block_size);\n      handles[b] = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(bs)));\n\n      ctx.task(handles[b]->write(), a.handles[b]->read())->*[bs](cudaStream_t stream, auto dthis, auto da) {\n        // There are likely much more efficient ways.\n        cuda_safe_call(cudaMemcpyAsync(\n          dthis.data_handle(), da.data_handle(), bs * sizeof(double), cudaMemcpyDeviceToDevice, stream));\n      };\n    }\n  }\n\n  void fill(const std::function<double(int)>& f)\n  {\n    size_t bs = block_size;\n    for (size_t b = 0; b < nblocks; b++)\n    {\n      ctx.task(exec_place::host(), handles[b]->write())->*[&f, b, bs](cudaStream_t stream, auto ds) {\n        cuda_safe_call(cudaStreamSynchronize(stream));\n\n        for (size_t local_row = 0; local_row < ds.extent(0); local_row++)\n        {\n          ds(local_row) = f(local_row + b * bs);\n        }\n      };\n    }\n  }\n\n  size_t N;\n  size_t block_size;\n  size_t nblocks;\n\n  mutable std::vector<std::shared_ptr<logical_data<slice<double>>>> handles;\n  std::unique_ptr<double[]> h_addr;\n};\n\n__global__ void scalar_div(const double* a, const double* b, double* c)\n{\n  *c = *a / *b;\n}\n\n// A += B\n__global__ void scalar_add(double* a, const double* b)\n{\n  *a = *a + *b;\n}\n\n__global__ void scalar_minus(const double* a, double* res)\n{\n  *res = -(*a);\n}\n\nclass scalar\n{\npublic:\n  scalar(bool is_tmp = false)\n  {\n    size_t s = sizeof(double);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(1)));\n    }\n    else\n    {\n      h_addr.reset(new double);\n      cuda_safe_call(cudaHostRegister(h_addr.get(), s, cudaHostRegisterPortable));\n      handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(make_slice(h_addr.get(), 1)));\n    }\n  }\n\n  scalar(scalar&&)            = default;\n  scalar& operator=(scalar&&) = default;\n\n  // Copy constructor\n  scalar(const scalar& a)\n  {\n    handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(1)));\n\n    ctx.task(handle->write(), a.handle->read())->*[](cudaStream_t stream, auto dthis, auto da) {\n      // There are likely much more efficient ways.\n      cuda_safe_call(\n        cudaMemcpyAsync(dthis.data_handle(), da.data_handle(), sizeof(double), cudaMemcpyDeviceToDevice, stream));\n    };\n  }\n\n  scalar operator/(scalar const& rhs) const\n  {\n    // Submit a task that computes this/rhs\n    scalar res(true);\n    ctx.task(handle->read(), rhs.handle->read(), res.handle->write())\n        ->*[](cudaStream_t stream, auto da, auto db, auto dres) {\n              scalar_div<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle(), dres.data_handle());\n            };\n\n    return res;\n  }\n\n  // this += rhs\n  scalar& operator+=(const scalar& rhs)\n  {\n    ctx.task(handle->rw(), rhs.handle->read())->*[](cudaStream_t stream, auto dthis, auto drhs) {\n      scalar_add<<<1, 1, 0, stream>>>(dthis.data_handle(), drhs.data_handle());\n    };\n\n    return *this;\n  }\n\n  scalar operator-() const\n  {\n    // Submit a task that computes -s\n    scalar res(true);\n    ctx.task(handle->read(), res.handle->write())->*[](cudaStream_t stream, auto dthis, auto dres) {\n      scalar_minus<<<1, 1, 0, stream>>>(dthis.data_handle(), dres.data_handle());\n    };\n\n    return res;\n  }\n\n  // Get value on the host\n  double get_value()\n  {\n    double val;\n    ctx.task(exec_place::host(), handle->read())->*[&val](cudaStream_t stream, auto ds) {\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      val = ds(0);\n    };\n\n    return val;\n  }\n\n  mutable std::shared_ptr<logical_data<slice<double>>> handle;\n  std::unique_ptr<double> h_addr;\n};\n\nclass scalar DOT(vector& a, class vector& b)\n{\n  assert(a.nblocks == b.nblocks);\n  scalar global_res(true);\n\n  // Loop over all blocks,\n  for (size_t bid = 0; bid < a.nblocks; bid++)\n  {\n    scalar res(true);\n\n    // Note that it works even if a.handle == b.handle because they have the same access mode\n    ctx.task(a.handles[bid]->read(), b.handles[bid]->read(), res.handle->write())\n        ->*[](cudaStream_t stream, auto da, auto db, auto dres) {\n              cuda_safe_call(cublasSetStream(cublas_handle, stream));\n              cuda_safe_call(cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_DEVICE));\n              cuda_safe_call(\n                cublasDdot(cublas_handle, da.extent(0), da.data_handle(), 1, db.data_handle(), 1, dres.data_handle()));\n            };\n\n    if (bid == 0)\n    {\n      // First access requires an assignment because it was not initialized\n      global_res = std::move(res);\n    }\n    else\n    {\n      global_res += res;\n    }\n  }\n\n  return global_res;\n};\n\n// Y = Y + alpha * X\nvoid AXPY(const class scalar& alpha, class vector& x, class vector& y)\n{\n  assert(x.N == y.N);\n  assert(x.nblocks == y.nblocks);\n\n  for (size_t b = 0; b < x.nblocks; b++)\n  {\n    ctx.task(alpha.handle->read(), x.handles[b]->read(), y.handles[b]->rw())\n        ->*\n      [](cudaStream_t stream, auto dalpha, auto dx, auto dy) {\n        auto nx = dx.extent(0);\n        cuda_safe_call(cublasSetStream(cublas_handle, stream));\n        cuda_safe_call(cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_DEVICE));\n        cuda_safe_call(cublasDaxpy(cublas_handle, nx, dalpha.data_handle(), dx.data_handle(), 1, dy.data_handle(), 1));\n      };\n  }\n};\n\n// Y = alpha*Y + X\nvoid SCALE_AXPY(const scalar& alpha, const class vector& x, class vector& y)\n{\n  assert(x.N == y.N);\n  assert(x.nblocks == y.nblocks);\n\n  for (size_t b = 0; b < x.nblocks; b++)\n  {\n    ctx.task(alpha.handle->read(), x.handles[b]->read(), y.handles[b]->rw())\n        ->*[](cudaStream_t stream, auto dalpha, auto dx, auto dy) {\n              cuda_safe_call(cublasSetStream(cublas_handle, stream));\n\n              auto nx = dx.extent(0);\n\n              // Y = alpha Y\n              cuda_safe_call(cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_DEVICE));\n              cuda_safe_call(cublasDscal(cublas_handle, nx, dalpha.data_handle(), dy.data_handle(), 1));\n\n              // Y = Y + X\n              const double one = 1.0;\n              cuda_safe_call(cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_HOST));\n              cuda_safe_call(cublasDaxpy(cublas_handle, nx, &one, dx.data_handle(), 1, dy.data_handle(), 1));\n            };\n  }\n};\n\n// y = alpha Ax + beta y\nvoid GEMV(double alpha, class matrix& a, class vector& x, double beta, class vector& y)\n{\n  assert(a.N == x.N);\n  assert(x.N == y.N);\n\n  size_t block_size = x.block_size;\n  assert(block_size == y.block_size);\n\n  for (size_t row_y = 0; row_y < y.nblocks; row_y++)\n  {\n    for (size_t row_x = 0; row_x < x.nblocks; row_x++)\n    {\n      double local_beta = (row_x == 0) ? beta : 1.0;\n\n      // If beta is null, then this is a write only mode\n      auto y_mode = local_beta == 0.0 ? access_mode::write : access_mode::rw;\n\n      ctx.task(a.handle->read(), x.handles[row_x]->read(), task_dep<slice<double>>(*(y.handles[row_y].get()), y_mode))\n          ->*[alpha, local_beta, row_x, row_y, block_size](cudaStream_t stream, auto da, auto dx, auto dy) {\n                auto nx              = dx.extent(0);\n                auto ny              = dy.extent(0);\n                auto ldA             = da.stride(1);\n                const double* Ablock = &da(row_y * block_size, row_x * block_size);\n\n                cuda_safe_call(cublasSetStream(cublas_handle, stream));\n                cuda_safe_call(cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_HOST));\n                cuda_safe_call(cublasDgemv(\n                  cublas_handle,\n                  CUBLAS_OP_N,\n                  ny,\n                  nx,\n                  &alpha,\n                  Ablock,\n                  ldA,\n                  dx.data_handle(),\n                  1,\n                  &local_beta,\n                  dy.data_handle(),\n                  1));\n              };\n    }\n  }\n}\n\nvoid cg(matrix& A, vector& X, vector& B)\n{\n  int N = A.N;\n\n  assert(N == X.N);\n  assert(N == B.N);\n\n  vector R = B;\n\n  // R = R - A*X\n  GEMV(-1.0, A, X, 1.0, R);\n\n  vector P = R;\n\n  // RSOLD = R'*R\n  scalar rsold = DOT(R, R);\n\n  int MAXITER = N;\n\n  if (getenv(\"MAXITER\"))\n  {\n    MAXITER = atoi(getenv(\"MAXITER\"));\n  }\n\n  for (int k = 0; k < MAXITER; k++)\n  {\n    vector Ap(N, P.block_size, true);\n\n    // Ap = A*P\n    GEMV(1.0, A, P, 0.0, Ap);\n\n    // alpha = rsold / (p' * Ap);\n    scalar alpha = rsold / DOT(P, Ap);\n\n    // x = x + alpha * p;\n    AXPY(alpha, P, X);\n\n    // r = r - alpha * Ap;\n    AXPY(-alpha, Ap, R);\n\n    // rsnew = r' * r;\n    scalar rsnew = DOT(R, R);\n\n    // Read the residual on the CPU, and halt the iterative process if we have converged\n    {\n      double err;\n      ctx.task(exec_place::host(), rsnew.handle->read())->*[&err](cudaStream_t stream, auto dres) {\n        cuda_safe_call(cudaStreamSynchronize(stream));\n        err = sqrt(dres(0));\n      };\n\n      if (err < 1e-10)\n      {\n        // We have converged\n        // fprintf(stderr, \"Successfully converged (err = %le)\\n\", err);\n        break;\n      }\n    }\n\n    // p = r + (rsnew / rsold) * p;\n    SCALE_AXPY(rsnew / rsold, R, P);\n\n    rsold = std::move(rsnew);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  size_t N = 1024;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n    fprintf(stderr, \"N = %zu\\n\", N);\n  }\n\n  size_t block_size = N / 4;\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n    fprintf(stderr, \"block_size = %zu\\n\", block_size);\n  }\n\n  // Do this lazily ?\n  cuda_safe_call(cublasCreate(&cublas_handle));\n\n  matrix A(N);\n  A.fill([&](int row, int col) {\n    return (1.0 / (row + col + 1) + (row == col ? 0.1 : 0.0));\n  });\n\n  vector B(N, block_size);\n  vector X(N, block_size);\n\n  B.fill([&](int /*unused*/) {\n    return 1.0;\n  });\n\n  X.fill([&](int /*unused*/) {\n    return 0.0;\n  });\n\n  cg(A, X, B);\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Conjugate gradient for a tiled dense matrix\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic hipblasHandle_t cublas_handle;\n\nstream_ctx ctx;\n\nclass matrix\n{\npublic:\n  matrix(size_t N)\n      : N(N)\n  {\n    h_addr.reset(new double[N * N]);\n    cuda_safe_call(hipHostRegister(h_addr.get(), N * N * sizeof(double), hipHostRegisterPortable));\n    handle = ::std::make_shared<logical_data<slice<double, 2>>>(\n      ctx.logical_data(make_slice(h_addr.get(), std::tuple{N, N}, N)));\n  }\n\n  void fill(const std::function<double(int, int)>& f)\n  {\n    ctx.task(exec_place::host(), handle->write())->*[&f](hipStream_t stream, auto ds) {\n      cuda_safe_call(hipStreamSynchronize(stream));\n\n      for (size_t col = 0; col < ds.extent(1); col++)\n      {\n        for (size_t row = 0; row < ds.extent(0); row++)\n        {\n          ds(row, col) = f(row, col);\n        }\n      }\n    };\n  }\n\n  size_t N;\n  std::unique_ptr<double[]> h_addr;\n  std::shared_ptr<logical_data<slice<double, 2>>> handle;\n};\n\nclass vector\n{\npublic:\n  vector(size_t N, size_t _block_size, bool is_tmp = false)\n      : N(N)\n      , block_size(_block_size)\n      , nblocks((N + block_size - 1) / block_size)\n  {\n    handles.resize(nblocks);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      for (size_t b = 0; b < nblocks; b++)\n      {\n        size_t bs  = std::min(N - block_size * b, block_size);\n        handles[b] = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(bs)));\n      }\n    }\n    else\n    {\n      h_addr.reset(new double[N]);\n      cuda_safe_call(hipHostRegister(h_addr.get(), N * sizeof(double), hipHostRegisterPortable));\n      for (size_t b = 0; b < nblocks; b++)\n      {\n        size_t bs = std::min(N - block_size * b, block_size);\n        handles[b] =\n          ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(make_slice(&h_addr[block_size * b], bs)));\n      }\n    }\n  }\n\n  // Copy constructor\n  vector(const vector& a)\n      : N(a.N)\n      , block_size(a.block_size)\n      , nblocks(a.nblocks)\n  {\n    handles.resize(nblocks);\n\n    for (size_t b = 0; b < nblocks; b++)\n    {\n      size_t bs  = std::min(N - block_size * b, block_size);\n      handles[b] = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(bs)));\n\n      ctx.task(handles[b]->write(), a.handles[b]->read())->*[bs](hipStream_t stream, auto dthis, auto da) {\n        // There are likely much more efficient ways.\n        cuda_safe_call(hipMemcpyAsync(\n          dthis.data_handle(), da.data_handle(), bs * sizeof(double), hipMemcpyDeviceToDevice, stream));\n      };\n    }\n  }\n\n  void fill(const std::function<double(int)>& f)\n  {\n    size_t bs = block_size;\n    for (size_t b = 0; b < nblocks; b++)\n    {\n      ctx.task(exec_place::host(), handles[b]->write())->*[&f, b, bs](hipStream_t stream, auto ds) {\n        cuda_safe_call(hipStreamSynchronize(stream));\n\n        for (size_t local_row = 0; local_row < ds.extent(0); local_row++)\n        {\n          ds(local_row) = f(local_row + b * bs);\n        }\n      };\n    }\n  }\n\n  size_t N;\n  size_t block_size;\n  size_t nblocks;\n\n  mutable std::vector<std::shared_ptr<logical_data<slice<double>>>> handles;\n  std::unique_ptr<double[]> h_addr;\n};\n\n__global__ void scalar_div(const double* a, const double* b, double* c)\n{\n  *c = *a / *b;\n}\n\n// A += B\n__global__ void scalar_add(double* a, const double* b)\n{\n  *a = *a + *b;\n}\n\n__global__ void scalar_minus(const double* a, double* res)\n{\n  *res = -(*a);\n}\n\nclass scalar\n{\npublic:\n  scalar(bool is_tmp = false)\n  {\n    size_t s = sizeof(double);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(1)));\n    }\n    else\n    {\n      h_addr.reset(new double);\n      cuda_safe_call(hipHostRegister(h_addr.get(), s, hipHostRegisterPortable));\n      handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(make_slice(h_addr.get(), 1)));\n    }\n  }\n\n  scalar(scalar&&)            = default;\n  scalar& operator=(scalar&&) = default;\n\n  // Copy constructor\n  scalar(const scalar& a)\n  {\n    handle = ::std::make_shared<logical_data<slice<double>>>(ctx.logical_data(shape_of<slice<double>>(1)));\n\n    ctx.task(handle->write(), a.handle->read())->*[](hipStream_t stream, auto dthis, auto da) {\n      // There are likely much more efficient ways.\n      cuda_safe_call(\n        hipMemcpyAsync(dthis.data_handle(), da.data_handle(), sizeof(double), hipMemcpyDeviceToDevice, stream));\n    };\n  }\n\n  scalar operator/(scalar const& rhs) const\n  {\n    // Submit a task that computes this/rhs\n    scalar res(true);\n    ctx.task(handle->read(), rhs.handle->read(), res.handle->write())\n        ->*[](hipStream_t stream, auto da, auto db, auto dres) {\n              scalar_div<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle(), dres.data_handle());\n            };\n\n    return res;\n  }\n\n  // this += rhs\n  scalar& operator+=(const scalar& rhs)\n  {\n    ctx.task(handle->rw(), rhs.handle->read())->*[](hipStream_t stream, auto dthis, auto drhs) {\n      scalar_add<<<1, 1, 0, stream>>>(dthis.data_handle(), drhs.data_handle());\n    };\n\n    return *this;\n  }\n\n  scalar operator-() const\n  {\n    // Submit a task that computes -s\n    scalar res(true);\n    ctx.task(handle->read(), res.handle->write())->*[](hipStream_t stream, auto dthis, auto dres) {\n      scalar_minus<<<1, 1, 0, stream>>>(dthis.data_handle(), dres.data_handle());\n    };\n\n    return res;\n  }\n\n  // Get value on the host\n  double get_value()\n  {\n    double val;\n    ctx.task(exec_place::host(), handle->read())->*[&val](hipStream_t stream, auto ds) {\n      cuda_safe_call(hipStreamSynchronize(stream));\n      val = ds(0);\n    };\n\n    return val;\n  }\n\n  mutable std::shared_ptr<logical_data<slice<double>>> handle;\n  std::unique_ptr<double> h_addr;\n};\n\nclass scalar DOT(vector& a, class vector& b)\n{\n  assert(a.nblocks == b.nblocks);\n  scalar global_res(true);\n\n  // Loop over all blocks,\n  for (size_t bid = 0; bid < a.nblocks; bid++)\n  {\n    scalar res(true);\n\n    // Note that it works even if a.handle == b.handle because they have the same access mode\n    ctx.task(a.handles[bid]->read(), b.handles[bid]->read(), res.handle->write())\n        ->*[](hipStream_t stream, auto da, auto db, auto dres) {\n              cuda_safe_call(hipblasSetStream(cublas_handle, stream));\n              cuda_safe_call(hipblasSetPointerMode(cublas_handle, HIPBLAS_POINTER_MODE_DEVICE));\n              cuda_safe_call(\n                hipblasDdot(cublas_handle, da.extent(0), da.data_handle(), 1, db.data_handle(), 1, dres.data_handle()));\n            };\n\n    if (bid == 0)\n    {\n      // First access requires an assignment because it was not initialized\n      global_res = std::move(res);\n    }\n    else\n    {\n      global_res += res;\n    }\n  }\n\n  return global_res;\n};\n\n// Y = Y + alpha * X\nvoid AXPY(const class scalar& alpha, class vector& x, class vector& y)\n{\n  assert(x.N == y.N);\n  assert(x.nblocks == y.nblocks);\n\n  for (size_t b = 0; b < x.nblocks; b++)\n  {\n    ctx.task(alpha.handle->read(), x.handles[b]->read(), y.handles[b]->rw())\n        ->*\n      [](hipStream_t stream, auto dalpha, auto dx, auto dy) {\n        auto nx = dx.extent(0);\n        cuda_safe_call(hipblasSetStream(cublas_handle, stream));\n        cuda_safe_call(hipblasSetPointerMode(cublas_handle, HIPBLAS_POINTER_MODE_DEVICE));\n        cuda_safe_call(hipblasDaxpy(cublas_handle, nx, dalpha.data_handle(), dx.data_handle(), 1, dy.data_handle(), 1));\n      };\n  }\n};\n\n// Y = alpha*Y + X\nvoid SCALE_AXPY(const scalar& alpha, const class vector& x, class vector& y)\n{\n  assert(x.N == y.N);\n  assert(x.nblocks == y.nblocks);\n\n  for (size_t b = 0; b < x.nblocks; b++)\n  {\n    ctx.task(alpha.handle->read(), x.handles[b]->read(), y.handles[b]->rw())\n        ->*[](hipStream_t stream, auto dalpha, auto dx, auto dy) {\n              cuda_safe_call(hipblasSetStream(cublas_handle, stream));\n\n              auto nx = dx.extent(0);\n\n              // Y = alpha Y\n              cuda_safe_call(hipblasSetPointerMode(cublas_handle, HIPBLAS_POINTER_MODE_DEVICE));\n              cuda_safe_call(hipblasDscal(cublas_handle, nx, dalpha.data_handle(), dy.data_handle(), 1));\n\n              // Y = Y + X\n              const double one = 1.0;\n              cuda_safe_call(hipblasSetPointerMode(cublas_handle, HIPBLAS_POINTER_MODE_HOST));\n              cuda_safe_call(hipblasDaxpy(cublas_handle, nx, &one, dx.data_handle(), 1, dy.data_handle(), 1));\n            };\n  }\n};\n\n// y = alpha Ax + beta y\nvoid GEMV(double alpha, class matrix& a, class vector& x, double beta, class vector& y)\n{\n  assert(a.N == x.N);\n  assert(x.N == y.N);\n\n  size_t block_size = x.block_size;\n  assert(block_size == y.block_size);\n\n  for (size_t row_y = 0; row_y < y.nblocks; row_y++)\n  {\n    for (size_t row_x = 0; row_x < x.nblocks; row_x++)\n    {\n      double local_beta = (row_x == 0) ? beta : 1.0;\n\n      // If beta is null, then this is a write only mode\n      auto y_mode = local_beta == 0.0 ? access_mode::write : access_mode::rw;\n\n      ctx.task(a.handle->read(), x.handles[row_x]->read(), task_dep<slice<double>>(*(y.handles[row_y].get()), y_mode))\n          ->*[alpha, local_beta, row_x, row_y, block_size](hipStream_t stream, auto da, auto dx, auto dy) {\n                auto nx              = dx.extent(0);\n                auto ny              = dy.extent(0);\n                auto ldA             = da.stride(1);\n                const double* Ablock = &da(row_y * block_size, row_x * block_size);\n\n                cuda_safe_call(hipblasSetStream(cublas_handle, stream));\n                cuda_safe_call(hipblasSetPointerMode(cublas_handle, HIPBLAS_POINTER_MODE_HOST));\n                cuda_safe_call(hipblasDgemv(\n                  cublas_handle,\n                  HIPBLAS_OP_N,\n                  ny,\n                  nx,\n                  &alpha,\n                  Ablock,\n                  ldA,\n                  dx.data_handle(),\n                  1,\n                  &local_beta,\n                  dy.data_handle(),\n                  1));\n              };\n    }\n  }\n}\n\nvoid cg(matrix& A, vector& X, vector& B)\n{\n  int N = A.N;\n\n  assert(N == X.N);\n  assert(N == B.N);\n\n  vector R = B;\n\n  // R = R - A*X\n  GEMV(-1.0, A, X, 1.0, R);\n\n  vector P = R;\n\n  // RSOLD = R'*R\n  scalar rsold = DOT(R, R);\n\n  int MAXITER = N;\n\n  if (getenv(\"MAXITER\"))\n  {\n    MAXITER = atoi(getenv(\"MAXITER\"));\n  }\n\n  for (int k = 0; k < MAXITER; k++)\n  {\n    vector Ap(N, P.block_size, true);\n\n    // Ap = A*P\n    GEMV(1.0, A, P, 0.0, Ap);\n\n    // alpha = rsold / (p' * Ap);\n    scalar alpha = rsold / DOT(P, Ap);\n\n    // x = x + alpha * p;\n    AXPY(alpha, P, X);\n\n    // r = r - alpha * Ap;\n    AXPY(-alpha, Ap, R);\n\n    // rsnew = r' * r;\n    scalar rsnew = DOT(R, R);\n\n    // Read the residual on the CPU, and halt the iterative process if we have converged\n    {\n      double err;\n      ctx.task(exec_place::host(), rsnew.handle->read())->*[&err](hipStream_t stream, auto dres) {\n        cuda_safe_call(hipStreamSynchronize(stream));\n        err = sqrt(dres(0));\n      };\n\n      if (err < 1e-10)\n      {\n        // We have converged\n        // fprintf(stderr, \"Successfully converged (err = %le)\\n\", err);\n        break;\n      }\n    }\n\n    // p = r + (rsnew / rsold) * p;\n    SCALE_AXPY(rsnew / rsold, R, P);\n\n    rsold = std::move(rsnew);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  size_t N = 1024;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n    fprintf(stderr, \"N = %zu\\n\", N);\n  }\n\n  size_t block_size = N / 4;\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n    fprintf(stderr, \"block_size = %zu\\n\", block_size);\n  }\n\n  // Do this lazily ?\n  cuda_safe_call(hipblasCreate(&cublas_handle));\n\n  matrix A(N);\n  A.fill([&](int row, int col) {\n    return (1.0 / (row + col + 1) + (row == col ? 0.1 : 0.0));\n  });\n\n  vector B(N, block_size);\n  vector X(N, block_size);\n\n  B.fill([&](int /*unused*/) {\n    return 1.0;\n  });\n\n  X.fill([&](int /*unused*/) {\n    return 0.0;\n  });\n\n  cg(A, X, B);\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/linear_algebra/cg_dense_2D.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Strassen matrix multiplication algorithm\n *\n * This demonstrates how CUDASTF helps combining many interdependent tasks and\n * deal with temporary data.\n */\n\n#include <cuda/experimental/stf.cuh>\n\nstatic const size_t BLOCKSIZE = 1024;\n\nusing namespace cuda::experimental::stf;\n\nusing logical_matrix = logical_data<slice<double, 2>>;\n\ninline size_t get_m(logical_matrix& s)\n{\n  return s.shape().extent(0);\n}\n\ninline size_t get_n(logical_matrix& s)\n{\n  return s.shape().extent(1);\n}\n\n// XXX global for the sake of simplicity, yet ...\nstatic std::vector<cublasHandle_t> cublas_handle;\n\ncublasHandle_t get_cublas_handle()\n{\n  int dev;\n  cuda_safe_call(cudaGetDevice(&dev));\n  return cublas_handle[dev];\n}\n\n// C = AB\nvoid MULT_CLASSIC(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  ctx.task(A.read(), B.read(), C.write()).set_symbol(\"MULT\")->*[](cudaStream_t s, auto a, auto b, auto c) {\n    cuda_safe_call(cublasSetStream(get_cublas_handle(), s));\n\n    size_t N = a.extent(0);\n\n    const double zero = 0.0;\n    const double one  = 1.0;\n    cuda_safe_call(cublasDgemm(\n      get_cublas_handle(),\n      CUBLAS_OP_N,\n      CUBLAS_OP_N,\n      N,\n      N,\n      N,\n      &one,\n      a.data_handle(),\n      a.stride(1),\n      b.data_handle(),\n      b.stride(1),\n      &zero,\n      c.data_handle(),\n      c.stride(1)));\n  };\n}\n\n// A = A + alpha B\ntemplate <typename T>\n__global__ void add_kernel(int m, int n, T* A, int ld_A, T alpha, const T* B, int ld_B)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < n; idx += blockDim.x * gridDim.x)\n  {\n    for (int idy = threadIdx.y + blockIdx.y * blockDim.y; idy < m; idy += blockDim.y * gridDim.y)\n    {\n      A[idy + idx * ld_A] += alpha * B[idy + idx * ld_B];\n    }\n  }\n}\n\n// Compute A = A + B\ntemplate <typename T>\nvoid ADD(context& ctx, logical_matrix& A, T alpha, logical_matrix& B)\n{\n  ctx.task(A.rw(), B.read()).set_symbol(\"ADD\")->*[&](cudaStream_t s, auto a, auto b) {\n    int m_A = a.extent(0);\n    int n_A = a.extent(1);\n\n    int ld_A = a.stride(1);\n    int ld_B = b.stride(1);\n\n    T* addr_A       = a.data_handle();\n    const T* addr_B = b.data_handle();\n\n    add_kernel<<<16, 16, 0, s>>>(m_A, n_A, addr_A, ld_A, alpha, addr_B, ld_B);\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(int m, int n, const T* src, int ld_src, T* dst, int ld_dst)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < n; idx += blockDim.x * gridDim.x)\n  {\n    for (int idy = threadIdx.y + blockIdx.y * blockDim.y; idy < m; idy += blockDim.y * gridDim.y)\n    {\n      dst[idy + idx * ld_dst] = src[idy + idx * ld_src];\n    }\n  }\n}\n\n// row and col = 0 or 1\ntemplate <typename T>\nvoid COPY_TO_SUBMATRIX(context& ctx, logical_data<slice<T, 2>>& A, logical_data<slice<T, 2>>& subA, int row, int col)\n{\n  // To copy to a subset, this is a write only access, so that we did not need a valid copy for subA before ...\n  ctx.task(A.read(), subA.write()).set_symbol(\"COPY_TO\")->*[&](cudaStream_t s, auto a, auto subA) {\n    int ld_A             = a.stride(1);\n    int ld_subA          = subA.stride(1);\n    int m_subA           = subA.extent(0);\n    int n_subA           = subA.extent(1);\n    T* addr_subA         = subA.data_handle();\n    const T* addr_A_base = a.data_handle();\n    const T* addr_A      = addr_A_base + row * m_subA + col * n_subA * ld_A;\n\n    // subA = A_row,col\n    copy_kernel<<<16, 16, 0, s>>>(m_subA, n_subA, addr_A, ld_A, addr_subA, ld_subA);\n  };\n}\n\ntemplate <typename T>\nvoid COPY_FROM_SUBMATRICES(context& ctx, logical_data<slice<T, 2>>& A, logical_data<slice<T, 2>> subA[2][2])\n{\n  // To copy to a subset, this is a write only access, so that we did not need a valid copy for subA before ...\n  // When copying from a subset to the whole matrix, we need a RW because we only modify a part of the matrix\n  ctx.task(A.write(), subA[0][0].read(), subA[0][1].read(), subA[1][0].read(), subA[1][1].read()).set_symbol(\"COPY_FROM\")\n      ->*[&](cudaStream_t s, auto a, auto a00, auto a01, auto a10, auto a11) {\n            int ld_A       = a.stride(1);\n            T* addr_A_base = a.data_handle();\n\n            for (int col = 0; col < 2; col++)\n            {\n              for (int row = 0; row < 2; row++)\n              {\n                auto& subA         = col == 0 ? (row == 0 ? a00 : a10) : (row == 0 ? a01 : a11);\n                int m_subA         = subA.extent(0);\n                int n_subA         = subA.extent(1);\n                int ld_subA        = subA.stride(1);\n                const T* addr_subA = subA.data_handle();\n                T* addr_A          = addr_A_base + row * m_subA + col * n_subA * ld_A;\n\n                // A_row,col= subA\n                copy_kernel<<<16, 16, 0, s>>>(m_subA, n_subA, addr_subA, ld_subA, addr_A, ld_A);\n              }\n            }\n          };\n}\n\ntemplate <typename T>\nvoid COPY_MATRIX(context& ctx, logical_data<slice<T, 2>>& dst, logical_data<slice<T, 2>>& src)\n{\n  // This is a write only access, so that we did not need a valid copy for subA before ...\n  ctx.task(dst.write(), src.read()).set_symbol(\"COPY\")->*[&](cudaStream_t s, auto d_dst, auto d_src) {\n    int ld_src = d_dst.stride(1);\n    int ld_dst = d_src.stride(1);\n\n    auto m = d_src.extent(0);\n    assert(m == d_dst.extent(0));\n\n    auto n = d_src.extent(1);\n    assert(n == d_dst.extent(1));\n\n    const T* addr_src = d_src.data_handle();\n    T* addr_dst       = d_dst.data_handle();\n\n    copy_kernel<<<16, 16, 0, s>>>(m, n, addr_src, ld_src, addr_dst, ld_dst);\n  };\n}\n\nvoid MULT(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C);\n\nvoid MULT_REC_NAIVE(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  logical_matrix subA[2][2], subB[2][2], subC[2][2];\n\n  size_t N = get_m(A);\n\n  assert(get_m(A) == get_n(A));\n  assert(get_m(B) == get_n(B));\n  assert(get_m(C) == get_n(C));\n\n  assert(N % 2 == 0);\n\n  size_t half_N = N / 2;\n\n  // These are TMP data which don't have a valid copy yet\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      subA[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subB[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subC[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n      COPY_TO_SUBMATRIX(ctx, A, subA[row][col], row, col);\n      COPY_TO_SUBMATRIX(ctx, B, subB[row][col], row, col);\n    }\n  }\n\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      for (int k = 0; k < 2; k++)\n      {\n        auto Ck = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n        MULT(ctx, subA[row][k], subB[k][col], Ck);\n\n        ADD(ctx, subC[row][col], 1.0, Ck);\n      }\n\n      // C_row,col = subC[row][col]\n      COPY_FROM_SUBMATRICES(ctx, C, subC);\n    }\n  }\n}\n\nvoid MULT_STRASSEN(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  /*\n   *  STRASSEN ALGORITHM\n   *\n   *   M1 = (A00 + A11)(B00 + B11)\n   *   M2 = (A10 + A11)B00\n   *   M3 = A00(B01 - B11)\n   *   M4 = A11(B10 - B00)\n   *   M5 = (A00 + A01)B11\n   *   M6 = (A10 - A00)(B00 + B01)\n   *   M7 = (A01 - A11)(B10 + B11)\n   *\n   *   C00 = M1 + M4 - M5 + M7\n   *   C01 = M3 + M5\n   *   C10 = M2 + M4\n   *   C11 = M1 - M2 + M3 + M6\n   *\n   */\n  size_t N = get_m(A);\n  assert(N % 2 == 0);\n  size_t half_N = N / 2;\n\n  logical_matrix subA[2][2], subB[2][2], subC[2][2];\n  auto M1 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M2 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M3 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M4 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M5 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M6 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M7 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n  assert(get_m(A) == get_n(A));\n  assert(get_m(B) == get_n(B));\n  assert(get_m(C) == get_n(C));\n\n  // These are TMP data which don't have a valid copy yet\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      subA[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subB[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subC[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n      COPY_TO_SUBMATRIX(ctx, A, subA[row][col], row, col);\n      COPY_TO_SUBMATRIX(ctx, B, subB[row][col], row, col);\n    }\n  }\n\n  // M1 = (A00 + A11)(B00 + B11)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N)),\n         right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][0]);\n    ADD(ctx, left, 1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[0][0]);\n    ADD(ctx, right, 1.0, subB[1][1]);\n\n    MULT(ctx, left, right, M1);\n  }\n\n  // M2 = (A10 + A11)B00\n  {\n    auto left = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[1][0]);\n    ADD(ctx, left, 1.0, subA[1][1]);\n\n    MULT(ctx, left, subB[0][0], M2);\n  }\n\n  // M3 = A00(B01 - B11)\n  {\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, right, subB[0][1]);\n    ADD(ctx, right, -1.0, subB[1][1]);\n\n    MULT(ctx, subA[0][0], right, M3);\n  }\n\n  // M4 = A11(B10 - B00)\n  {\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, right, subB[1][0]);\n    ADD(ctx, right, -1.0, subB[0][0]);\n\n    MULT(ctx, subA[1][1], right, M4);\n  }\n\n  // M5 = (A00 + A01)B11\n  {\n    auto left = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][0]);\n    ADD(ctx, left, 1.0, subA[0][1]);\n\n    MULT(ctx, left, subB[1][1], M5);\n  }\n\n  // M6 = (A10 - A00)(B00 + B01)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N)),\n         right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[1][0]);\n    ADD(ctx, left, -1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[0][0]);\n    ADD(ctx, right, 1.0, subB[0][1]);\n\n    MULT(ctx, left, right, M6);\n  }\n\n  // M7 = (A01 - A11)(B10 + B11)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][1]);\n    ADD(ctx, left, -1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[1][0]);\n    ADD(ctx, right, 1.0, subB[1][1]);\n\n    MULT(ctx, left, right, M7);\n  }\n\n  // C00 = M1 + M4 - M5 + M7\n  COPY_MATRIX(ctx, subC[0][0], M1);\n  ADD(ctx, subC[0][0], 1.0, M4);\n  ADD(ctx, subC[0][0], -1.0, M5);\n  ADD(ctx, subC[0][0], -1.0, M5);\n  ADD(ctx, subC[0][0], 1.0, M7);\n\n  // C01 = M3 + M5\n  COPY_MATRIX(ctx, subC[0][1], M3);\n  ADD(ctx, subC[0][1], 1.0, M5);\n\n  // C10 = M2 + M4\n  COPY_MATRIX(ctx, subC[1][0], M2);\n  ADD(ctx, subC[1][0], 1.0, M4);\n\n  // C11 = M1 - M2 + M3 + M6\n  COPY_MATRIX(ctx, subC[1][1], M1);\n  ADD(ctx, subC[1][1], -1.0, M2);\n  ADD(ctx, subC[1][1], 1.0, M3);\n  ADD(ctx, subC[1][1], 1.0, M6);\n\n  // Write back subsets of C to C\n  COPY_FROM_SUBMATRICES(ctx, C, subC);\n}\n\nvoid MULT(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  size_t N = get_m(A);\n\n  if (N <= BLOCKSIZE)\n  {\n    MULT_CLASSIC(ctx, A, B, C);\n  }\n  else\n  {\n    // MULT_REC_NAIVE(ctx, A, B, C);\n    MULT_STRASSEN(ctx, A, B, C);\n  }\n}\n\nvoid strassen_test(context& ctx, size_t N)\n{\n  double* A = new double[N * N];\n  double* B = new double[N * N];\n  double* C = new double[N * N];\n\n  int ldA = N;\n  int ldB = N;\n  int ldC = N;\n\n  cuda_safe_call(cudaHostRegister(A, N * N * sizeof(double), cudaHostRegisterPortable));\n  cuda_safe_call(cudaHostRegister(B, N * N * sizeof(double), cudaHostRegisterPortable));\n  cuda_safe_call(cudaHostRegister(C, N * N * sizeof(double), cudaHostRegisterPortable));\n\n  for (size_t col = 0; col < N; col++)\n  {\n    for (size_t row = 0; row < N; row++)\n    {\n      A[row + N * col] = 1.0;\n      B[row + N * col] = -1.0;\n      C[row + N * col] = 0.0;\n    }\n  }\n\n  auto descA = ctx.logical_data(make_slice(A, std::tuple{N, N}, ldA)),\n       descB = ctx.logical_data(make_slice(B, std::tuple{N, N}, ldB)),\n       descC = ctx.logical_data(make_slice(C, std::tuple{N, N}, ldC));\n  descA.set_symbol(\"A\");\n  descB.set_symbol(\"B\");\n  descC.set_symbol(\"C\");\n\n  std::chrono::steady_clock::time_point start, stop;\n\n  ctx.host_launch(descC.read())->*[&](auto /* ignored */) {\n    start = std::chrono::steady_clock::now();\n  };\n\n  MULT(ctx, descA, descB, descC);\n\n  ctx.host_launch(descC.read())->*[&](auto /* ignored */) {\n    stop = std::chrono::steady_clock::now();\n  };\n\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf ms\\n\", duration.count() * 1000.0);\n}\n\nint main(int argc, char** argv)\n{\n  long N = 2 * BLOCKSIZE;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n  }\n\n  bool use_graphs = false;\n  if (argc > 2)\n  {\n    use_graphs = (atoi(argv[2]) > 0);\n  }\n\n  // Set up CUBLAS\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n  cublas_handle.resize(ndevs);\n  for (int d = 0; d < ndevs; d++)\n  {\n    cuda_safe_call(cudaSetDevice(d));\n    cuda_safe_call(cublasCreate(&cublas_handle[d]));\n  }\n\n  cuda_safe_call(cudaSetDevice(0));\n\n  context ctx;\n  if (use_graphs)\n  {\n    ctx = graph_ctx();\n  }\n\n  strassen_test(ctx, N);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Strassen matrix multiplication algorithm\n *\n * This demonstrates how CUDASTF helps combining many interdependent tasks and\n * deal with temporary data.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nstatic const size_t BLOCKSIZE = 1024;\n\nusing namespace cuda::experimental::stf;\n\nusing logical_matrix = logical_data<slice<double, 2>>;\n\ninline size_t get_m(logical_matrix& s)\n{\n  return s.shape().extent(0);\n}\n\ninline size_t get_n(logical_matrix& s)\n{\n  return s.shape().extent(1);\n}\n\n// XXX global for the sake of simplicity, yet ...\nstatic std::vector<hipblasHandle_t> cublas_handle;\n\nhipblasHandle_t get_cublas_handle()\n{\n  int dev;\n  cuda_safe_call(hipGetDevice(&dev));\n  return cublas_handle[dev];\n}\n\n// C = AB\nvoid MULT_CLASSIC(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  ctx.task(A.read(), B.read(), C.write()).set_symbol(\"MULT\")->*[](hipStream_t s, auto a, auto b, auto c) {\n    cuda_safe_call(hipblasSetStream(get_cublas_handle(), s));\n\n    size_t N = a.extent(0);\n\n    const double zero = 0.0;\n    const double one  = 1.0;\n    cuda_safe_call(hipblasDgemm(\n      get_cublas_handle(),\n      HIPBLAS_OP_N,\n      HIPBLAS_OP_N,\n      N,\n      N,\n      N,\n      &one,\n      a.data_handle(),\n      a.stride(1),\n      b.data_handle(),\n      b.stride(1),\n      &zero,\n      c.data_handle(),\n      c.stride(1)));\n  };\n}\n\n// A = A + alpha B\ntemplate <typename T>\n__global__ void add_kernel(int m, int n, T* A, int ld_A, T alpha, const T* B, int ld_B)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < n; idx += blockDim.x * gridDim.x)\n  {\n    for (int idy = threadIdx.y + blockIdx.y * blockDim.y; idy < m; idy += blockDim.y * gridDim.y)\n    {\n      A[idy + idx * ld_A] += alpha * B[idy + idx * ld_B];\n    }\n  }\n}\n\n// Compute A = A + B\ntemplate <typename T>\nvoid ADD(context& ctx, logical_matrix& A, T alpha, logical_matrix& B)\n{\n  ctx.task(A.rw(), B.read()).set_symbol(\"ADD\")->*[&](hipStream_t s, auto a, auto b) {\n    int m_A = a.extent(0);\n    int n_A = a.extent(1);\n\n    int ld_A = a.stride(1);\n    int ld_B = b.stride(1);\n\n    T* addr_A       = a.data_handle();\n    const T* addr_B = b.data_handle();\n\n    add_kernel<<<16, 16, 0, s>>>(m_A, n_A, addr_A, ld_A, alpha, addr_B, ld_B);\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(int m, int n, const T* src, int ld_src, T* dst, int ld_dst)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < n; idx += blockDim.x * gridDim.x)\n  {\n    for (int idy = threadIdx.y + blockIdx.y * blockDim.y; idy < m; idy += blockDim.y * gridDim.y)\n    {\n      dst[idy + idx * ld_dst] = src[idy + idx * ld_src];\n    }\n  }\n}\n\n// row and col = 0 or 1\ntemplate <typename T>\nvoid COPY_TO_SUBMATRIX(context& ctx, logical_data<slice<T, 2>>& A, logical_data<slice<T, 2>>& subA, int row, int col)\n{\n  // To copy to a subset, this is a write only access, so that we did not need a valid copy for subA before ...\n  ctx.task(A.read(), subA.write()).set_symbol(\"COPY_TO\")->*[&](hipStream_t s, auto a, auto subA) {\n    int ld_A             = a.stride(1);\n    int ld_subA          = subA.stride(1);\n    int m_subA           = subA.extent(0);\n    int n_subA           = subA.extent(1);\n    T* addr_subA         = subA.data_handle();\n    const T* addr_A_base = a.data_handle();\n    const T* addr_A      = addr_A_base + row * m_subA + col * n_subA * ld_A;\n\n    // subA = A_row,col\n    copy_kernel<<<16, 16, 0, s>>>(m_subA, n_subA, addr_A, ld_A, addr_subA, ld_subA);\n  };\n}\n\ntemplate <typename T>\nvoid COPY_FROM_SUBMATRICES(context& ctx, logical_data<slice<T, 2>>& A, logical_data<slice<T, 2>> subA[2][2])\n{\n  // To copy to a subset, this is a write only access, so that we did not need a valid copy for subA before ...\n  // When copying from a subset to the whole matrix, we need a RW because we only modify a part of the matrix\n  ctx.task(A.write(), subA[0][0].read(), subA[0][1].read(), subA[1][0].read(), subA[1][1].read()).set_symbol(\"COPY_FROM\")\n      ->*[&](hipStream_t s, auto a, auto a00, auto a01, auto a10, auto a11) {\n            int ld_A       = a.stride(1);\n            T* addr_A_base = a.data_handle();\n\n            for (int col = 0; col < 2; col++)\n            {\n              for (int row = 0; row < 2; row++)\n              {\n                auto& subA         = col == 0 ? (row == 0 ? a00 : a10) : (row == 0 ? a01 : a11);\n                int m_subA         = subA.extent(0);\n                int n_subA         = subA.extent(1);\n                int ld_subA        = subA.stride(1);\n                const T* addr_subA = subA.data_handle();\n                T* addr_A          = addr_A_base + row * m_subA + col * n_subA * ld_A;\n\n                // A_row,col= subA\n                copy_kernel<<<16, 16, 0, s>>>(m_subA, n_subA, addr_subA, ld_subA, addr_A, ld_A);\n              }\n            }\n          };\n}\n\ntemplate <typename T>\nvoid COPY_MATRIX(context& ctx, logical_data<slice<T, 2>>& dst, logical_data<slice<T, 2>>& src)\n{\n  // This is a write only access, so that we did not need a valid copy for subA before ...\n  ctx.task(dst.write(), src.read()).set_symbol(\"COPY\")->*[&](hipStream_t s, auto d_dst, auto d_src) {\n    int ld_src = d_dst.stride(1);\n    int ld_dst = d_src.stride(1);\n\n    auto m = d_src.extent(0);\n    assert(m == d_dst.extent(0));\n\n    auto n = d_src.extent(1);\n    assert(n == d_dst.extent(1));\n\n    const T* addr_src = d_src.data_handle();\n    T* addr_dst       = d_dst.data_handle();\n\n    copy_kernel<<<16, 16, 0, s>>>(m, n, addr_src, ld_src, addr_dst, ld_dst);\n  };\n}\n\nvoid MULT(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C);\n\nvoid MULT_REC_NAIVE(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  logical_matrix subA[2][2], subB[2][2], subC[2][2];\n\n  size_t N = get_m(A);\n\n  assert(get_m(A) == get_n(A));\n  assert(get_m(B) == get_n(B));\n  assert(get_m(C) == get_n(C));\n\n  assert(N % 2 == 0);\n\n  size_t half_N = N / 2;\n\n  // These are TMP data which don't have a valid copy yet\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      subA[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subB[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subC[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n      COPY_TO_SUBMATRIX(ctx, A, subA[row][col], row, col);\n      COPY_TO_SUBMATRIX(ctx, B, subB[row][col], row, col);\n    }\n  }\n\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      for (int k = 0; k < 2; k++)\n      {\n        auto Ck = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n        MULT(ctx, subA[row][k], subB[k][col], Ck);\n\n        ADD(ctx, subC[row][col], 1.0, Ck);\n      }\n\n      // C_row,col = subC[row][col]\n      COPY_FROM_SUBMATRICES(ctx, C, subC);\n    }\n  }\n}\n\nvoid MULT_STRASSEN(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  /*\n   *  STRASSEN ALGORITHM\n   *\n   *   M1 = (A00 + A11)(B00 + B11)\n   *   M2 = (A10 + A11)B00\n   *   M3 = A00(B01 - B11)\n   *   M4 = A11(B10 - B00)\n   *   M5 = (A00 + A01)B11\n   *   M6 = (A10 - A00)(B00 + B01)\n   *   M7 = (A01 - A11)(B10 + B11)\n   *\n   *   C00 = M1 + M4 - M5 + M7\n   *   C01 = M3 + M5\n   *   C10 = M2 + M4\n   *   C11 = M1 - M2 + M3 + M6\n   *\n   */\n  size_t N = get_m(A);\n  assert(N % 2 == 0);\n  size_t half_N = N / 2;\n\n  logical_matrix subA[2][2], subB[2][2], subC[2][2];\n  auto M1 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M2 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M3 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M4 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M5 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M6 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n  auto M7 = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n  assert(get_m(A) == get_n(A));\n  assert(get_m(B) == get_n(B));\n  assert(get_m(C) == get_n(C));\n\n  // These are TMP data which don't have a valid copy yet\n  for (int col = 0; col < 2; col++)\n  {\n    for (int row = 0; row < 2; row++)\n    {\n      subA[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subB[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n      subC[row][col] = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n      COPY_TO_SUBMATRIX(ctx, A, subA[row][col], row, col);\n      COPY_TO_SUBMATRIX(ctx, B, subB[row][col], row, col);\n    }\n  }\n\n  // M1 = (A00 + A11)(B00 + B11)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N)),\n         right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][0]);\n    ADD(ctx, left, 1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[0][0]);\n    ADD(ctx, right, 1.0, subB[1][1]);\n\n    MULT(ctx, left, right, M1);\n  }\n\n  // M2 = (A10 + A11)B00\n  {\n    auto left = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[1][0]);\n    ADD(ctx, left, 1.0, subA[1][1]);\n\n    MULT(ctx, left, subB[0][0], M2);\n  }\n\n  // M3 = A00(B01 - B11)\n  {\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, right, subB[0][1]);\n    ADD(ctx, right, -1.0, subB[1][1]);\n\n    MULT(ctx, subA[0][0], right, M3);\n  }\n\n  // M4 = A11(B10 - B00)\n  {\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, right, subB[1][0]);\n    ADD(ctx, right, -1.0, subB[0][0]);\n\n    MULT(ctx, subA[1][1], right, M4);\n  }\n\n  // M5 = (A00 + A01)B11\n  {\n    auto left = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][0]);\n    ADD(ctx, left, 1.0, subA[0][1]);\n\n    MULT(ctx, left, subB[1][1], M5);\n  }\n\n  // M6 = (A10 - A00)(B00 + B01)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N)),\n         right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[1][0]);\n    ADD(ctx, left, -1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[0][0]);\n    ADD(ctx, right, 1.0, subB[0][1]);\n\n    MULT(ctx, left, right, M6);\n  }\n\n  // M7 = (A01 - A11)(B10 + B11)\n  {\n    auto left  = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n    auto right = ctx.logical_data(shape_of<slice<double, 2>>(half_N, half_N));\n\n    COPY_MATRIX(ctx, left, subA[0][1]);\n    ADD(ctx, left, -1.0, subA[1][1]);\n\n    COPY_MATRIX(ctx, right, subB[1][0]);\n    ADD(ctx, right, 1.0, subB[1][1]);\n\n    MULT(ctx, left, right, M7);\n  }\n\n  // C00 = M1 + M4 - M5 + M7\n  COPY_MATRIX(ctx, subC[0][0], M1);\n  ADD(ctx, subC[0][0], 1.0, M4);\n  ADD(ctx, subC[0][0], -1.0, M5);\n  ADD(ctx, subC[0][0], -1.0, M5);\n  ADD(ctx, subC[0][0], 1.0, M7);\n\n  // C01 = M3 + M5\n  COPY_MATRIX(ctx, subC[0][1], M3);\n  ADD(ctx, subC[0][1], 1.0, M5);\n\n  // C10 = M2 + M4\n  COPY_MATRIX(ctx, subC[1][0], M2);\n  ADD(ctx, subC[1][0], 1.0, M4);\n\n  // C11 = M1 - M2 + M3 + M6\n  COPY_MATRIX(ctx, subC[1][1], M1);\n  ADD(ctx, subC[1][1], -1.0, M2);\n  ADD(ctx, subC[1][1], 1.0, M3);\n  ADD(ctx, subC[1][1], 1.0, M6);\n\n  // Write back subsets of C to C\n  COPY_FROM_SUBMATRICES(ctx, C, subC);\n}\n\nvoid MULT(context& ctx, logical_matrix& A, logical_matrix& B, logical_matrix& C)\n{\n  size_t N = get_m(A);\n\n  if (N <= BLOCKSIZE)\n  {\n    MULT_CLASSIC(ctx, A, B, C);\n  }\n  else\n  {\n    // MULT_REC_NAIVE(ctx, A, B, C);\n    MULT_STRASSEN(ctx, A, B, C);\n  }\n}\n\nvoid strassen_test(context& ctx, size_t N)\n{\n  double* A = new double[N * N];\n  double* B = new double[N * N];\n  double* C = new double[N * N];\n\n  int ldA = N;\n  int ldB = N;\n  int ldC = N;\n\n  cuda_safe_call(hipHostRegister(A, N * N * sizeof(double), hipHostRegisterPortable));\n  cuda_safe_call(hipHostRegister(B, N * N * sizeof(double), hipHostRegisterPortable));\n  cuda_safe_call(hipHostRegister(C, N * N * sizeof(double), hipHostRegisterPortable));\n\n  for (size_t col = 0; col < N; col++)\n  {\n    for (size_t row = 0; row < N; row++)\n    {\n      A[row + N * col] = 1.0;\n      B[row + N * col] = -1.0;\n      C[row + N * col] = 0.0;\n    }\n  }\n\n  auto descA = ctx.logical_data(make_slice(A, std::tuple{N, N}, ldA)),\n       descB = ctx.logical_data(make_slice(B, std::tuple{N, N}, ldB)),\n       descC = ctx.logical_data(make_slice(C, std::tuple{N, N}, ldC));\n  descA.set_symbol(\"A\");\n  descB.set_symbol(\"B\");\n  descC.set_symbol(\"C\");\n\n  std::chrono::steady_clock::time_point start, stop;\n\n  ctx.host_launch(descC.read())->*[&](auto /* ignored */) {\n    start = std::chrono::steady_clock::now();\n  };\n\n  MULT(ctx, descA, descB, descC);\n\n  ctx.host_launch(descC.read())->*[&](auto /* ignored */) {\n    stop = std::chrono::steady_clock::now();\n  };\n\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf ms\\n\", duration.count() * 1000.0);\n}\n\nint main(int argc, char** argv)\n{\n  long N = 2 * BLOCKSIZE;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n  }\n\n  bool use_graphs = false;\n  if (argc > 2)\n  {\n    use_graphs = (atoi(argv[2]) > 0);\n  }\n\n  // Set up CUBLAS\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n  cublas_handle.resize(ndevs);\n  for (int d = 0; d < ndevs; d++)\n  {\n    cuda_safe_call(hipSetDevice(d));\n    cuda_safe_call(hipblasCreate(&cublas_handle[d]));\n  }\n\n  cuda_safe_call(hipSetDevice(0));\n\n  context ctx;\n  if (use_graphs)\n  {\n    ctx = graph_ctx();\n  }\n\n  strassen_test(ctx, N);\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/linear_algebra/strassen.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example implements the POTRI matrix inversion algorithm over multiple devices\n *\n *\n */\n#include <cuda/experimental/stf.cuh>\n\n#include <iostream>\n\n#include <cusolverDn.h>\n\n#include <cublas_v2.h>\n\n#define TILED\n\nusing namespace cuda::experimental::stf;\n\nstream_ctx ctx;\n\nstatic std::unordered_map<exec_place, cublasHandle_t, hash<exec_place>> cublas_handles;\nstatic std::unordered_map<exec_place, cusolverDnHandle_t, hash<exec_place>> cusolver_handles;\n\n/* Get a CUBLAS handle valid on the current execution place, or initialize it lazily */\ncublasHandle_t& get_cublas_handle(const exec_place& ep = exec_place::current_device())\n{\n  auto& result = cublas_handles[ep];\n  if (result == cublasHandle_t())\n  { // not found, default value inserted\n    // Lazy initialization, and save the handle for future use\n    cuda_try(cublasCreate(&result));\n  }\n  return result;\n}\n\n/* Get a CUSOLVER handle valid on the current execution place, or initialize it lazily */\ncusolverDnHandle_t& get_cusolver_handle(const exec_place& ep = exec_place::current_device())\n{\n  auto& result = cusolver_handles[ep];\n  if (result == cusolverDnHandle_t())\n  { // not found, default value inserted\n    // Lazy initialization, and save the handle for future use\n    cuda_try(cusolverDnCreate(&result));\n  }\n  return result;\n}\n\ntemplate <typename T>\nclass matrix\n{\npublic:\n  matrix(int NROWS, int NCOLS, int BLOCKSIZE_ROWS, int BLOCKSIZE_COLS, bool is_sym, const char* _symbol = \"matrix\")\n  {\n    symbol = _symbol;\n\n    sym_matrix = is_sym;\n\n    m  = NROWS;\n    mb = BLOCKSIZE_ROWS;\n\n    n  = NCOLS;\n    nb = BLOCKSIZE_COLS;\n\n    assert(m % mb == 0);\n    assert(n % nb == 0);\n\n    size_t s = m * n * sizeof(T);\n    // cuda_try(cudaMallocHost(&h_array, m*n*sizeof(T)));\n    // fprintf(stderr, \"Allocating %ld x %ld x %ld = %ld bytes (%f GB) on host for %s\\n\", m, n, sizeof(T), s,\n    //        s / (1024.0 * 1024.0 * 1024.0), _symbol);\n    h_array = (T*) malloc(s);\n    assert(h_array);\n    cuda_try(cudaHostRegister(h_array, s, cudaHostRegisterPortable));\n    // cuda_try(cudaMalloc(&d_array, m*n*sizeof(T)));\n\n    // Compute the number of blocks\n    mt = m / mb;\n    nt = n / nb;\n\n    handles.resize(mt * nt);\n\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      size_t low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        T* addr_h = get_block_h(rowb, colb);\n        auto& h   = get_handle(rowb, colb);\n\n#ifdef TILED\n        // tiles are stored contiguously\n        size_t ld = mb;\n#else\n        size_t ld = m;\n#endif\n        std::ignore = ld; // work around compiler bug\n        h           = ctx.logical_data(make_slice(addr_h, std::tuple{mb, nb}, ld));\n        h.set_symbol(std::string(symbol) + \"_\" + std::to_string(rowb) + \"_\" + std::to_string(colb));\n        h.set_write_back(false);\n      }\n    }\n\n    cuda_try(cudaGetDeviceCount(&ndevs));\n    for (int a = 1; a * a <= ndevs; a++)\n    {\n      if (ndevs % a == 0)\n      {\n        grid_p = a;\n        grid_q = ndevs / a;\n      }\n    }\n\n    assert(grid_p * grid_q == ndevs);\n\n    // std::cout << \"FOUND \" << ndevs << \" DEVICES \"\n    //          << \"p=\" << grid_p << \" q=\" << grid_q << '\\n';\n  }\n\n  int get_preferred_devid(int row, int col)\n  {\n    return (row % grid_p) + (col % grid_q) * grid_p;\n  }\n\n  auto& get_handle(int row, int col)\n  {\n    return handles[row + col * mt];\n  }\n\n  size_t get_index(size_t row, size_t col)\n  {\n#ifdef TILED\n    // Find which tile contains this element\n    int tile_row = row / mb;\n    int tile_col = col / nb;\n\n    size_t tile_size = mb * nb;\n\n    // Look for the index of the beginning of the tile\n    size_t tile_start = (tile_row + mt * tile_col) * tile_size;\n\n    // Offset within the tile\n    size_t offset = (row % mb) + (col % nb) * mb;\n\n    return tile_start + offset;\n#else\n    return row + col * m;\n#endif\n  }\n\n  T* get_block_h(int brow, int bcol)\n  {\n    size_t index = get_index(brow * mb, bcol * nb);\n    return &h_array[index];\n  }\n\n  // Fill with func(Matrix*,row, col)\n  template <typename Fun>\n  void fill(Fun&& fun)\n  {\n    nvtx_range r(\"fill\");\n\n    // Fill blocks by blocks\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      size_t low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        // Each task fills a block\n        auto& h   = get_handle(rowb, colb);\n        int devid = get_preferred_devid(rowb, colb);\n\n        ctx.parallel_for(exec_place::device(devid), h.shape(), h.write()).set_symbol(\"INIT\")->*\n          [=] _CCCL_DEVICE(size_t lrow, size_t lcol, auto sA) {\n            size_t row     = lrow + rowb * sA.extent(0);\n            size_t col     = lcol + colb * sA.extent(1);\n            sA(lrow, lcol) = fun(row, col);\n          };\n      }\n    }\n  }\n\n  // Print blocks\n  void print()\n  {\n    // print blocks by blocks\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      int low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        // Each task fills a block\n        ctx.host_launch(get_handle(rowb, colb).read())->*[=](auto sA) {\n          for (size_t lcol = 0; lcol < sA.extent(1); lcol++)\n          {\n            size_t col = lcol + colb * sA.extent(1);\n            for (size_t lrow = 0; lrow < sA.extent(0); lrow++)\n            {\n              size_t row = lrow + rowb * sA.extent(0);\n\n              fprintf(stderr, \"%d,%d : %le\\n\", row, col, sA(lrow, lcol));\n            }\n          }\n        };\n      }\n    }\n  }\n\n  T* h_array;\n  T* d_array;\n  size_t m; // nrows\n  size_t n; // ncols\n\n  // Is this a sym matrix ? (lower assumed)\n  bool sym_matrix;\n\n  size_t mb; // block size (rows)\n  size_t nb; // block size (cols)\n\n  size_t mt; // number of column blocks\n  size_t nt; // number of row blocks\n\n  // abstract data handles\n  std::vector<logical_data<slice<double, 2>>> handles;\n\n  const char* symbol;\n\n  // for the mapping\n  int ndevs;\n  int grid_p, grid_q;\n};\n\nvoid DPOTRF(cublasFillMode_t uplo, matrix<double>& A, int A_row, int A_col)\n{\n  auto& Akk    = A.get_handle(A_row, A_col);\n  size_t m_akk = Akk.shape().extent(0);\n  // Note that the handle may be different from the actual handle...\n  int Lwork_expected;\n  cuda_safe_call(cusolverDnDpotrf_bufferSize(get_cusolver_handle(), uplo, m_akk, nullptr, 0, &Lwork_expected));\n\n  auto potrf_buffer = ctx.logical_data(shape_of<slice<double>>(Lwork_expected));\n  potrf_buffer.set_allocator(ctx.get_default_allocator());\n\n  auto devInfo = ctx.logical_data(shape_of<slice<int>>(1));\n\n  auto t = ctx.task(Akk.rw(), potrf_buffer.write(), devInfo.write());\n  t.set_symbol(\"DPOTRF\");\n  t->*[&](auto s, auto sAkk, auto buffer, auto info) {\n    auto& h = get_cusolver_handle();\n    cuda_try(cusolverDnSetStream(h, s));\n\n    cuda_try(cusolverDnDpotrf(\n      h,\n      uplo,\n      sAkk.extent(0),\n      sAkk.data_handle(),\n      sAkk.stride(1),\n      buffer.data_handle(),\n      buffer.extent(0),\n      info.data_handle()));\n  };\n}\n\nvoid DTRTRI(cublasFillMode_t uplo, cublasDiagType_t diag, matrix<double>& A, int A_row, int A_col)\n{\n  // Preallocate a buffer used by CUSOLVER\n  size_t workspaceInBytesOnDevice, workspaceInBytesOnHost;\n  int64_t m_a00 = A.mb;\n  assert(A.mb == A.nb);\n\n  cuda_try(cusolverDnXtrtri_bufferSize(\n    get_cusolver_handle(),\n    uplo,\n    diag,\n    m_a00,\n    CUDA_R_64F /* DTRTRI */,\n    nullptr,\n    m_a00,\n    &workspaceInBytesOnDevice,\n    &workspaceInBytesOnHost));\n\n  // We don't support allocating buffers of 0 bytes ... XXX\n  if (workspaceInBytesOnHost == 0)\n  {\n    workspaceInBytesOnHost = 8;\n  }\n\n  auto d_buffer = ctx.logical_data(shape_of<slice<char>>(workspaceInBytesOnDevice));\n  auto h_buffer = ctx.logical_data(shape_of<slice<char>>(workspaceInBytesOnHost));\n  d_buffer.set_allocator(ctx.get_default_allocator());\n  h_buffer.set_allocator(ctx.get_default_allocator());\n\n  auto devInfo = ctx.logical_data(shape_of<slice<int>>(1));\n\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).rw(), d_buffer.write(), h_buffer.write(data_place::managed()), devInfo.write());\n  t.set_symbol(\"DTRTRI\");\n  t->*[&](auto s, auto sA, auto dbuffer, auto hbuffer, auto info) {\n    auto& h = get_cusolver_handle();\n    cuda_try(cusolverDnSetStream(h, s));\n\n    // DTRTRI(...)\n    cuda_try(cusolverDnXtrtri(\n      h,\n      uplo,\n      diag,\n      sA.extent(0),\n      CUDA_R_64F /* DTRTRI */,\n      sA.data_handle(),\n      sA.stride(1),\n      (double*) dbuffer.data_handle(),\n      workspaceInBytesOnDevice,\n      (double*) hbuffer.data_handle(),\n      workspaceInBytesOnHost,\n      info.data_handle()));\n  };\n}\n\n/*\n * Note: this code was taken from CUSOLVER\n *\n * SLACPY copies all or part of a two-dimensional matrix A to another matrix B.\n *\n *  up     up_and_lo\n *  1          0          upper triangle, including diagonal\n *  0          0          lower triangle, including diagonal\n *  ?          1          whole matrix\n *\n * configuration:\n *   dim3 grids( m/VEC, m/BY )\n *   dim3 threads(VEC,BY)\n */\ntemplate <typename T_ELEM_SRC, typename T_ELEM_DST, int VEC_LOG, int BY_LOG>\n__global__ void __launch_bounds__(1 << (VEC_LOG + BY_LOG))\n  lacpy_kernel(int m, int n, const T_ELEM_SRC* A, size_t lda, T_ELEM_DST* B, size_t ldb, int up, int up_and_lo)\n{\n  const int VEC = (1 << VEC_LOG);\n  const int BY  = (1 << BY_LOG);\n\n  const int inx = threadIdx.x;\n  const int iny = threadIdx.y;\n\n  const int ibx = blockIdx.x * VEC;\n  const int iby = blockIdx.y * BY;\n\n  const int i = ibx + inx;\n  const int j = iby + iny;\n\n  if (ibx >= m)\n  {\n    return;\n  }\n  if (iby >= n)\n  {\n    return;\n  }\n\n  T_ELEM_SRC Areg = T_ELEM_SRC(0);\n\n  if (up_and_lo)\n  {\n    /*\n     * copy whole matrix\n             DO 60 J = 1, N\n                DO 50 I = 1, M\n                   B( I, J ) = A( I, J )\n       50       CONTINUE\n       60    CONTINUE\n    */\n    if ((i < m) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n    return;\n  }\n\n  // only lower or upper triangle is copied.\n  if (up)\n  {\n    /*\n     * copy upper triangle, including diagonal\n             DO 20 J = 1, N\n                DO 10 I = 1, MIN( J, M )\n                   B( I, J ) = A( I, J )\n       10       CONTINUE\n       20    CONTINUE\n     */\n    if ((i <= min(j, m - 1)) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n  }\n  else\n  {\n    /*\n     * copy lower triangle, including diagonal\n             DO 40 J = 1, N\n                DO 30 I = J, M\n                   B( I, J ) = A( I, J )\n       30       CONTINUE\n       40    CONTINUE\n\n     */\n    if (((j <= i) && (i < m)) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n  }\n}\n\n/*\n * SLACPY copies all or part of a two-dimensional matrix A to another\n * matrix B.\n *\n * Input\n * -------\n *          UPLO is CHARACTER*1\n *          Specifies the part of the matrix A to be copied to B.\n *          = 'U':      Upper triangular part\n *          = 'L':      Lower triangular part\n *          Otherwise:  All of the matrix A\n *\n *          M is INTEGER\n *          The number of rows of the matrix A.\n *          M >= 0.\n *\n *          N is INTEGER\n *          The number of columns of the matrix A.\n *          N >= 0.\n *\n *          A is REAL array, dimension (LDA,N)\n *          The m by n matrix A.  If UPLO = 'U', only the upper triangle\n *          or trapezoid is accessed; if UPLO = 'L', only the lower\n *          triangle or trapezoid is accessed.\n *\n *          LDA is INTEGER\n *          The first dimension of the array A. LDA >= max(1,M).\n *\n *          B is REAL array, dimension (LDB,N)\n *          On exit, B = A in the locations specified by UPLO.\n *\n *          LDB is INTEGER\n *          The leading dimension of the array B.  LDB >= max(1,M).\n *\n */\ntemplate <typename T_ELEM_SRC, typename T_ELEM_DST>\ncusolverStatus_t cusolverDnXlacpy(\n  cublasFillMode_t uplo, // \"UPPER\", B = upper(A)\n                         // \"LOWER\", B = lower(A)\n                         // otherwise, B = A\n  int m,\n  int n,\n  const T_ELEM_SRC* A,\n  int lda,\n  T_ELEM_DST* B,\n  int ldb,\n  cudaStream_t stream)\n{\n  cusolverStatus_t status = CUSOLVER_STATUS_SUCCESS;\n  cudaError_t cudaStat1   = cudaSuccess;\n\n  int up        = 0;\n  int up_and_lo = 0;\n\n  //  Quick return if possible\n  if ((0 >= m) || (0 >= n))\n  {\n    return status;\n  }\n\n  /*\n   *  up     up_and_lo\n   *  1          0          upper triangle, including diagonal\n   *  0          0          lower triangle, including diagonal\n   *  ?          1          whole matrix\n   */\n  if (CUBLAS_FILL_MODE_LOWER == uplo)\n  {\n    // Lower triangular part\n    up = 0;\n  }\n  else if (CUBLAS_FILL_MODE_UPPER == uplo)\n  {\n    // upper triangular part\n    up = 1;\n  }\n  else\n  {\n    up_and_lo = 1; // Otherwise:  All of the matrix A\n  }\n\n  const int VEC_LOG = 5;\n  const int BY_LOG  = 3;\n  const int VEC     = (1 << VEC_LOG);\n  const int BY      = (1 << BY_LOG);\n  dim3 grids((m + VEC - 1) / VEC, (n + BY - 1) / BY);\n  dim3 threads(VEC, BY);\n\n  lacpy_kernel<T_ELEM_SRC, T_ELEM_DST, VEC_LOG, BY_LOG>\n    <<<grids, threads, 0, stream>>>(m, n, A, (size_t) lda, B, (size_t) ldb, up, up_and_lo);\n\n  cudaStat1 = cudaGetLastError(); /* launch error */\n  if (cudaSuccess != cudaStat1)\n  {\n    fprintf(stderr, \"Error (lacpy): %d\\n\", cudaStat1);\n    status = CUSOLVER_STATUS_EXECUTION_FAILED;\n  }\n\n  return status;\n}\n\ncusolverStatus_t cusolverDnDlacpy(\n  cublasFillMode_t uplo, // \"UPPER\", B = upper(A)\n                         // \"LOWER\", B = lower(A)\n                         // otherwise, B = A\n  int m,\n  int n,\n  const double* A,\n  int lda,\n  double* B,\n  int ldb,\n  cudaStream_t stream)\n{\n  return cusolverDnXlacpy<double, double>(uplo, m, n, A, lda, B, ldb, stream);\n}\n\n// Pretend there is a CUBLAS interface for DLAAUM\nvoid cublasDnDlaaum_bufferSize(cublasHandle_t /*unused*/, int m, int n, size_t* Workspace_size)\n{\n  assert(Workspace_size);\n  *Workspace_size = m * n * sizeof(double);\n}\n\n// Pretend there is a CUBLAS interface for DLAAUM\n// A triangular\n// Lower : A = A^T * A\n// Upper : A = A A^T\nvoid cublasDnDlaaum(\n  cublasHandle_t cublas_handle,\n  cublasFillMode_t uplo,\n  int m,\n  int n,\n  double* A,\n  int ldA,\n  double* Workspace_d,\n  size_t Workspace_size)\n{\n  cudaStream_t stream;\n  cuda_safe_call(cublasGetStream(cublas_handle, &stream));\n\n  // \"Hand coded\"\n  // We use a full copy of A !\n  // fprintf(stderr, \"GOT Workspace_size %ld ... expected %d\\n\", Workspace_size, m * n * sizeof(double));\n  std::ignore = Workspace_size;\n  assert(Workspace_size >= m * n * sizeof(double));\n\n  double* B = Workspace_d;\n  int ldB   = m;\n\n  // Blank the buffer\n  cuda_safe_call(cudaMemsetAsync(B, 0, m * n * sizeof(double), stream));\n\n  // Copy A (with upper or lower 0 untouched)\n  cusolverDnDlacpy(uplo, m, n, A, ldA, B, ldB, stream);\n\n  cublasDiagType_t diag = CUBLAS_DIAG_NON_UNIT;\n  const double one      = 1.0;\n\n  auto side = (uplo == CUBLAS_FILL_MODE_LOWER) ? CUBLAS_SIDE_LEFT : CUBLAS_SIDE_RIGHT;\n\n  // LOWER: TRMM(A,B) : B = op(A) * B = A^T * B with A triangular (B = C in CUBLAS), CUBLAS_OP_T, CUBLAS_SIDE_RIGHT\n  // UPPER: TRMM(A,B) : B = B * op(A) = B A^T with A triangular (B = C in CUBLAS), CUBLAS_OP_T, CUBLAS_SIDE_RIGHT\n  cuda_safe_call(cublasDtrmm(cublas_handle, side, uplo, CUBLAS_OP_T, diag, m, n, &one, A, ldA, B, ldB, B, ldB));\n\n  // Copy B=AA^T back into A (with upper or lower 0 untouched)\n  cusolverDnDlacpy(uplo, m, n, B, ldB, A, ldA, stream);\n}\n\nvoid DLAAUM(cublasFillMode_t uplo, matrix<double>& A, int A_row, int A_col)\n{\n  int NB = A.mb;\n  size_t Lwork;\n  cublasDnDlaaum_bufferSize(get_cublas_handle(), NB, NB, &Lwork);\n\n  auto d_buffer = ctx.logical_data(shape_of<slice<char>>(Lwork));\n\n  auto t = ctx.task(A.get_handle(A_row, A_col).rw(), d_buffer.write());\n  t.set_symbol(\"DLAAUM\");\n  t->*[&](auto s, auto sA, auto buffer) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    cublasDnDlaaum(\n      h, uplo, sA.extent(0), sA.extent(1), sA.data_handle(), sA.stride(1), (double*) buffer.data_handle(), Lwork);\n  };\n}\n\nvoid DGEMM(\n  cublasOperation_t transa,\n  cublasOperation_t transb,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DGEMM\");\n  t->*[&](auto s, auto sA, auto sB, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    int k = (transa == CUBLAS_OP_N) ? sA.extent(1) : sA.extent(0);\n    cuda_try(cublasDgemm(\n      h,\n      transa,\n      transb,\n      sC.extent(0),\n      sC.extent(1),\n      k,\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      &beta,\n      sC.data_handle(),\n      sC.stride(1)));\n  };\n}\n\nvoid DSYMM(\n  cublasSideMode_t side,\n  cublasFillMode_t uplo,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DSYMM\");\n  t->*[&](auto s, auto sA, auto sB, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    cuda_try(cublasDsymm(\n      h,\n      side,\n      uplo,\n      sC.extent(0),\n      sC.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      &beta,\n      sC.data_handle(),\n      sC.stride(1)));\n  };\n}\n\nvoid DSYRK(\n  cublasFillMode_t uplo,\n  cublasOperation_t trans,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DSYRK\");\n  t->*[&](auto s, auto sA, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    // number of rows of matrix op(A) and C\n    int n = sC.extent(0);\n\n    // number of columns of matrix op(A)\n    int k = (trans == CUBLAS_OP_N) ? sA.extent(1) : sA.extent(0);\n\n    cuda_try(\n      cublasDsyrk(h, uplo, trans, n, k, &alpha, sA.data_handle(), sA.stride(1), &beta, sC.data_handle(), sC.stride(1)));\n  };\n}\n\nvoid DTRSM(\n  cublasSideMode_t side,\n  cublasFillMode_t uplo,\n  cublasOperation_t transa,\n  cublasDiagType_t diag,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).rw());\n  t.set_symbol(\"DTRSM\");\n  t->*[&](auto s, auto sA, auto sB) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    cuda_try(cublasDtrsm(\n      h,\n      side,\n      uplo,\n      transa,\n      diag,\n      sB.extent(0),\n      sB.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1)));\n  };\n}\n\nvoid DTRMM(\n  cublasSideMode_t side,\n  cublasFillMode_t uplo,\n  cublasOperation_t transa,\n  cublasDiagType_t diag,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).rw());\n  t.set_symbol(\"DTRMM\");\n  t->*[&](auto s, auto sA, auto sB) {\n    auto& h = get_cublas_handle();\n    cuda_try(cublasSetStream(h, s));\n\n    // Note : CUBLAS DTRMM implementation is out of place but supports in place by using the same buffer B and C\n    cuda_try(cublasDtrmm(\n      get_cublas_handle(),\n      side,\n      uplo,\n      transa,\n      diag,\n      sB.extent(0),\n      sB.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      sB.data_handle(),\n      sB.stride(1) /* same as B*/));\n  };\n}\n\nvoid PDNRM2_HOST(matrix<double>* A, double* result)\n{\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"red\");\n#endif\n\n  for (size_t rowb = 0; rowb < A->mt; rowb++)\n  {\n    for (size_t colb = 0; colb < A->nt; colb++)\n    {\n      ctx.host_launch(A->get_handle(rowb, colb).read())->*[=](auto sA) {\n        double res2 = 0.0;\n        for (size_t col = 0; col < sA.extent(1); col++)\n        {\n          for (size_t row = 0; row < sA.extent(0); row++)\n          {\n            double v = sA(row, col);\n            res2 += v * v;\n          }\n        }\n        *result += res2;\n      };\n    }\n  }\n}\n\nvoid PDPOTRF(matrix<double>& A)\n{\n  nvtx_range r(\"PDPOTRF\");\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"yellow\");\n#endif\n\n  assert(A.m == A.n);\n  assert(A.mt == A.nt);\n\n  int NBLOCKS = A.mt;\n  assert(A.mb == A.nb);\n\n  for (int K = 0; K < NBLOCKS; K++)\n  {\n    cuda_try(cudaSetDevice(A.get_preferred_devid(K, K)));\n    DPOTRF(CUBLAS_FILL_MODE_LOWER, A, K, K);\n\n    for (int row = K + 1; row < NBLOCKS; row++)\n    {\n      cuda_try(cudaSetDevice(A.get_preferred_devid(row, K)));\n      DTRSM(CUBLAS_SIDE_RIGHT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_T, CUBLAS_DIAG_NON_UNIT, 1.0, A, K, K, A, row, K);\n\n      for (int col = K + 1; col < row; col++)\n      {\n        cuda_try(cudaSetDevice(A.get_preferred_devid(row, col)));\n        DGEMM(CUBLAS_OP_N, CUBLAS_OP_T, -1.0, A, row, K, A, col, K, 1.0, A, row, col);\n      }\n\n      cuda_try(cudaSetDevice(A.get_preferred_devid(row, row)));\n      DSYRK(CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_N, -1.0, A, row, K, 1.0, A, row, row);\n    }\n  }\n}\n\n// Algorithm from PLASMA\nvoid PDTRSM(cublasSideMode_t side,\n            cublasFillMode_t uplo,\n            cublasOperation_t trans,\n            cublasDiagType_t diag,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B)\n{\n  nvtx_range r(\"PDTRSM\");\n  //    std::cout << \"[PDTRSM] START B MT \" << B.mt << \" NT \" << B.nt << '\\n';\n\n  if (side == CUBLAS_SIDE_LEFT)\n  {\n    if (uplo == CUBLAS_FILL_MODE_UPPER)\n    {\n      // TODO\n      abort();\n    }\n    else\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //===========================================\n      if (trans == CUBLAS_OP_N)\n      {\n        for (size_t k = 0; k < B.mt; k++)\n        {\n          double lalpha = k == 0 ? alpha : 1.0;\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(cudaSetDevice(A.get_preferred_devid(k, k)));\n            DTRSM(side, uplo, trans, diag, lalpha, A, k, k, B, k, n);\n          }\n          for (size_t m = k + 1; m < B.mt; m++)\n          {\n            for (size_t n = 0; n < B.nt; n++)\n            {\n              cuda_try(cudaSetDevice(A.get_preferred_devid(m, k)));\n              DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, -1.0, A, m, k, B, k, n, lalpha, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_[C|T]\n      //================================================\n      else\n      {\n        for (size_t k = 0; k < B.mt; k++)\n        {\n          double lalpha = k == 0 ? alpha : 1.0;\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(cudaSetDevice(A.get_preferred_devid(B.mt - k - 1, B.mt - k - 1)));\n            DTRSM(side, uplo, trans, diag, lalpha, A, B.mt - k - 1, B.mt - k - 1, B, B.mt - k - 1, n);\n          }\n          for (size_t m = k + 1; m < B.mt; m++)\n          {\n            for (size_t n = 0; n < B.nt; n++)\n            {\n              cuda_try(cudaSetDevice(A.get_preferred_devid(B.mt - k - 1, B.mt - 1 - m)));\n              DGEMM(\n                trans, CUBLAS_OP_N, -1.0, A, B.mt - k - 1, B.mt - 1 - m, B, B.mt - k - 1, n, lalpha, B, B.mt - 1 - m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n  else\n  {\n    // TODO\n    abort();\n  }\n  //    std::cout << \"[PDTRSM] END\" << '\\n';\n}\n\nvoid PDPOTRS(matrix<double>& A, matrix<double>& B, cublasFillMode_t uplo)\n{\n  nvtx_range r(\"PDPOTRS\");\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"green\");\n#endif\n\n  //    std::cout << \"[PDPOTRS] START\" << '\\n';\n  // Call the parallel functions.\n  PDTRSM(\n    CUBLAS_SIDE_LEFT, uplo, uplo == CUBLAS_FILL_MODE_UPPER ? CUBLAS_OP_T : CUBLAS_OP_N, CUBLAS_DIAG_NON_UNIT, 1.0, A, B);\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"darkgreen\");\n#endif\n\n  PDTRSM(\n    CUBLAS_SIDE_LEFT, uplo, uplo == CUBLAS_FILL_MODE_UPPER ? CUBLAS_OP_N : CUBLAS_OP_T, CUBLAS_DIAG_NON_UNIT, 1.0, A, B);\n  //    std::cout << \"[PDPOTRS] END\" << '\\n';\n}\n\n/***************************************************************************/ /**\n                                                                               * Parallel tile matrix-matrix\n                                                                               *multiplication.\n                                                                               * @see plasma_omp_dgemm\n                                                                               ******************************************************************************/\nvoid PDGEMM(cublasOperation_t transa,\n            cublasOperation_t transb,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B,\n            double beta,\n            matrix<double>& C)\n{\n  nvtx_range r(\"PDGEMM\");\n\n#ifdef HAVE_DOT\n  reserved::dot::set_current_color(\"blue\");\n#endif\n\n  for (size_t m = 0; m < C.mt; m++)\n  {\n    for (size_t n = 0; n < C.nt; n++)\n    {\n      cuda_try(cudaSetDevice(C.get_preferred_devid(m, n)));\n\n      //=========================================\n      // alpha*A*B does not contribute; scale C\n      //=========================================\n      int inner_k = transa == CUBLAS_OP_N ? A.n : A.m;\n      if (alpha == 0.0 || inner_k == 0)\n      {\n        DGEMM(transa, transb, alpha, A, 0, 0, B, 0, 0, beta, C, m, n);\n      }\n      else if (transa == CUBLAS_OP_N)\n      {\n        //================================\n        // CUBLAS_OP_N / CUBLAS_OP_N\n        //================================\n        if (transb == CUBLAS_OP_N)\n        {\n          assert(A.nt == B.mt);\n          for (size_t k = 0; k < A.nt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n          }\n        }\n        //=====================================\n        // CUBLAS_OP_N / CUBLAS_OP_T\n        //=====================================\n        else\n        {\n          for (size_t k = 0; k < A.nt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, m, k, B, n, k, zbeta, C, m, n);\n          }\n        }\n      }\n      else\n      {\n        //=====================================\n        // CUBLAS_OP_T / CUBLAS_OP_N\n        //=====================================\n        if (transb == CUBLAS_OP_N)\n        {\n          for (size_t k = 0; k < A.mt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n          }\n        }\n        //==========================================\n        // CUBLAS_OP_T / CUBLAS_OP_T\n        //==========================================\n        else\n        {\n          for (size_t k = 0; k < A.mt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, k, m, B, n, k, zbeta, C, m, n);\n          }\n        }\n      }\n    }\n  }\n}\n\n/*\n * Algorithm taken from the PLASMA library\n */\n// We assume a lower triangular matrix (uplo == CUBLAS_FILL_MODE_LOWER)\nvoid PDTRTRI(matrix<double>& A, cublasFillMode_t uplo, cublasDiagType_t diag)\n{\n  nvtx_range r(\"PDTRTRI\");\n\n  assert(uplo == CUBLAS_FILL_MODE_LOWER);\n\n  for (size_t k = 0; k < A.nt; k++)\n  {\n    for (size_t m = k + 1; m < A.mt; m++)\n    {\n      cuda_try(cudaSetDevice(A.get_preferred_devid(m, k)));\n      DTRSM(CUBLAS_SIDE_RIGHT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_N, diag, -1.0, A, k, k, A, m, k);\n    }\n    for (size_t m = k + 1; m < A.mt; m++)\n    {\n      for (size_t n = 0; n < k; n++)\n      {\n        cuda_try(cudaSetDevice(A.get_preferred_devid(m, n)));\n        DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, 1.0, A, m, k, A, k, n, 1.0, A, m, n);\n      }\n    }\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(cudaSetDevice(A.get_preferred_devid(k, n)));\n      DTRSM(CUBLAS_SIDE_LEFT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_N, diag, 1.0, A, k, k, A, k, n);\n    }\n\n    // DTRTRI(...)\n    cuda_try(cudaSetDevice(A.get_preferred_devid(k, k)));\n    DTRTRI(uplo, diag, A, k, k);\n  }\n}\n\n/*\n * Algorithm taken from the PLASMA library\n */\n// We assume a lower triangular matrix (uplo == CUBLAS_FILL_MODE_LOWER)\nvoid PDLAUUM(matrix<double>& A, cublasFillMode_t uplo)\n{\n  nvtx_range r(\"PDLAUUM\");\n  assert(uplo == CUBLAS_FILL_MODE_LOWER);\n\n  for (size_t k = 0; k < A.mt; k++)\n  {\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(cudaSetDevice(A.get_preferred_devid(n, n)));\n      DSYRK(CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_T, 1.0, A, k, n, 1.0, A, n, n);\n\n      for (size_t m = n + 1; m < k; m++)\n      {\n        cuda_try(cudaSetDevice(A.get_preferred_devid(m, n)));\n        DGEMM(CUBLAS_OP_T, CUBLAS_OP_N, 1.0, A, k, m, A, k, n, 1.0, A, m, n);\n      }\n    }\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(cudaSetDevice(A.get_preferred_devid(k, n)));\n      DTRMM(CUBLAS_SIDE_LEFT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_T, CUBLAS_DIAG_NON_UNIT, 1.0, A, k, k, A, k, n);\n    }\n\n    // LAAUM (Akk RW) (compute Akk^T * Akk)\n    cuda_try(cudaSetDevice(A.get_preferred_devid(k, k)));\n    DLAAUM(uplo, A, k, k);\n  }\n}\n\nvoid PDSYMM(cublasSideMode_t side,\n            cublasFillMode_t uplo,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B,\n            double beta,\n            matrix<double>& C)\n{\n  nvtx_range r(\"PDSYMM\");\n\n  size_t k, m, n;\n  double zbeta;\n  double zone = (double) 1.0;\n\n  for (m = 0; m < C.mt; m++)\n  {\n    for (n = 0; n < C.nt; n++)\n    {\n      cuda_try(cudaSetDevice(C.get_preferred_devid(m, n)));\n      /*\n       *  CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER\n       */\n      if (side == CUBLAS_SIDE_LEFT)\n      {\n        if (uplo == CUBLAS_FILL_MODE_LOWER)\n        {\n          for (k = 0; k < C.mt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < m)\n            {\n              DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == m)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, k, n, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(CUBLAS_OP_T, CUBLAS_OP_N, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n        /*\n         *  CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER\n         */\n        else\n        {\n          for (k = 0; k < C.mt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < m)\n            {\n              DGEMM(CUBLAS_OP_T, CUBLAS_OP_N, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == m)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, k, n, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n      }\n      /*\n       *  CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER\n       */\n      else\n      {\n        if (uplo == CUBLAS_FILL_MODE_LOWER)\n        {\n          for (k = 0; k < C.nt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < n)\n            {\n              DGEMM(CUBLAS_OP_N, CUBLAS_OP_T, alpha, B, m, k, A, n, k, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == n)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, m, k, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, alpha, B, m, k, A, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n        /*\n         *  CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER\n         */\n        else\n        {\n          for (k = 0; k < C.nt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < n)\n            {\n              DGEMM(CUBLAS_OP_N, CUBLAS_OP_N, alpha, B, m, k, A, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == n)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, m, k, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(CUBLAS_OP_N, CUBLAS_OP_T, alpha, B, m, k, A, n, k, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid PDTRMM(cublasSideMode_t side,\n            cublasFillMode_t uplo,\n            cublasOperation_t trans,\n            cublasDiagType_t diag,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B)\n{\n  if (side == CUBLAS_SIDE_LEFT)\n  {\n    if (uplo == CUBLAS_FILL_MODE_UPPER)\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_N\n      //===========================================\n      if (trans == CUBLAS_OP_N)\n      {\n        for (size_t m = 0; m < B.mt; m++)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (size_t k = m + 1; k < A.mt; k++)\n            {\n              DGEMM(trans, CUBLAS_OP_N, alpha, A, m, k, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_T\n      //================================================\n      else\n      {\n        for (ssize_t m = B.mt - 1; m > -1; m--)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (ssize_t k = 0; k < m; k++)\n            {\n              DGEMM(trans, CUBLAS_OP_N, alpha, A, k, m, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n    else\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //===========================================\n      if (trans == CUBLAS_OP_N)\n      {\n        for (ssize_t m = B.mt - 1; m > -1; m--)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (ssize_t k = 0; k < m; k++)\n            {\n              DGEMM(trans, CUBLAS_OP_N, alpha, A, m, k, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_T\n      //================================================\n      else\n      {\n        for (size_t m = 0; m < B.mt; m++)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (size_t k = m + 1; k < A.mt; k++)\n            {\n              DGEMM(trans, CUBLAS_OP_N, alpha, A, k, m, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n  else\n  {\n    if (uplo == CUBLAS_FILL_MODE_UPPER)\n    {\n      //============================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_N\n      //============================================\n      if (trans == CUBLAS_OP_N)\n      {\n        for (ssize_t n = B.nt - 1; n > -1; n--)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (ssize_t k = 0; k < n; k++)\n            {\n              DGEMM(CUBLAS_OP_N, trans, alpha, B, m, k, A, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //=================================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER / Plasma[_Conj]Trans\n      //=================================================\n      else\n      {\n        for (size_t n = 0; n < B.nt; n++)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (size_t k = n + 1; k < A.mt; k++)\n            {\n              DGEMM(CUBLAS_OP_N, trans, alpha, B, m, k, A, n, k, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n    else\n    {\n      //============================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //============================================\n      if (trans == CUBLAS_OP_N)\n      {\n        for (size_t n = 0; n < B.nt; n++)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (size_t k = n + 1; k < A.mt; k++)\n            {\n              DGEMM(CUBLAS_OP_N, trans, alpha, B, m, k, A, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //=================================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER / Plasma[_Conj]Trans\n      //=================================================\n      else\n      {\n        for (ssize_t n = B.nt - 1; n > -1; n--)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(cudaSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (ssize_t k = 0; k < n; k++)\n            {\n              DGEMM(CUBLAS_OP_N, trans, alpha, B, m, k, A, n, k, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// Taken from Chameleon (INRIA)\n// All the formula are reported in the LAPACK Lawn 41:\n//     http://www.netlib.org/lapack/lawns/lawn41.ps\n#define FMULS_POTRI(__n) ((double) (__n) * ((2. / 3.) + (double) (__n) * ((1. / 3.) * (double) (__n) + 1.)))\n#define FADDS_POTRI(__n) ((double) (__n) * ((1. / 6.) + (double) (__n) * ((1. / 3.) * (double) (__n) - 0.5)))\ndouble flops_dpotri(double __n)\n{\n  double flops = (FMULS_POTRI((__n)) + FADDS_POTRI((__n)));\n  return flops;\n}\n\nvoid run(int N, int NB)\n{\n  // Use pools of preallocated blocks\n  auto fixed_alloc = block_allocator<fixed_size_allocator>(ctx, NB * NB * sizeof(double));\n  ctx.set_allocator(fixed_alloc);\n\n  // Set up CUBLAS and CUSOLVER\n  int ndevs;\n  cuda_try(cudaGetDeviceCount(&ndevs));\n\n  for (int d = 0; d < ndevs; d++)\n  {\n    auto ldummy = ctx.logical_data(shape_of<slice<char>>(1));\n    ctx.task(exec_place::device(d), ldummy.write())->*[](cudaStream_t, auto) {\n      get_cublas_handle();\n      get_cusolver_handle();\n    };\n\n    ctx.task(exec_place::host(), ldummy.write(data_place::managed()))->*[](cudaStream_t, auto) {};\n  }\n\n  cuda_try(cudaSetDevice(0));\n\n  cudaStream_t timing_stream;\n  cuda_try(cudaStreamCreate(&timing_stream));\n\n  matrix<double> A(N, N, NB, NB, true, \"A\");\n  matrix<double> Aref(N, N, NB, NB, false, \"Aref\");\n\n  // (Hilbert matrix + 2*N*Id) to have a diagonal dominant matrix\n  auto hilbert = [=] _CCCL_HOST_DEVICE(size_t row, size_t col) {\n    return 1.0 / (col + row + 1.0) + 2.0 * N * (col == row);\n  };\n\n  Aref.fill(hilbert);\n  A.fill(hilbert);\n\n  /* Right-hand side */\n  matrix<double> B_potrs(N, 1, NB, 1, false, \"B\");\n  matrix<double> Bref_potrs(N, 1, NB, 1, false, \"Bref\");\n\n  auto rhs_vals = [] _CCCL_HOST_DEVICE(size_t row, size_t /*unused*/) {\n    return 1.0 * (row + 1);\n  };\n\n  B_potrs.fill(rhs_vals);\n  Bref_potrs.fill(rhs_vals);\n\n  int check_result = 1;\n  if (getenv(\"CHECK_RESULT\"))\n  {\n    check_result = atoi(getenv(\"CHECK_RESULT\"));\n  }\n\n  int check_result_potrs = check_result;\n  if (getenv(\"CHECK_RESULT_POTRS\"))\n  {\n    check_result_potrs = atoi(getenv(\"CHECK_RESULT_POTRS\"));\n  }\n\n  //    // Compute ||Bref||\n  double Bref_nrm2 = 0.0;\n  double res_nrm2  = 0.0;\n\n  if (check_result_potrs)\n  {\n    PDNRM2_HOST(&Bref_potrs, &Bref_nrm2);\n  }\n\n  cudaEvent_t startEvent, stopEvent;\n\n  cuda_safe_call(cudaSetDevice(0));\n  cuda_safe_call(cudaStreamSynchronize(ctx.fence()));\n  cuda_safe_call(cudaEventCreate(&startEvent));\n  cuda_safe_call(cudaEventCreate(&stopEvent));\n  cuda_safe_call(cudaEventRecord(startEvent, ctx.fence()));\n\n  ctx.get_dot()->set_current_color(\"green\");\n  PDPOTRF(A);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  /*\n   *  POTRS\n   */\n\n  if (check_result_potrs)\n  {\n    // Solve AX = B and put the result in B\n    PDPOTRS(A, B_potrs, CUBLAS_FILL_MODE_LOWER);\n\n    // Compute (AX - B)\n    // Bref = (Aref*B - Bref)\n    PDGEMM(CUBLAS_OP_N, CUBLAS_OP_N, 1.0, Aref, B_potrs, -1.0, Bref_potrs);\n\n    // Compute ||AX - B|| = ||Bref||\n    PDNRM2_HOST(&Bref_potrs, &res_nrm2);\n  }\n\n  /*\n   *  POTRI\n   */\n  /* PDPOTRI = PDTRTRI + PDLAUUM */\n\n  // PDTRTRI : La^-1 (invert A)\n  //    fprintf(stderr, \"A=La before POTRI\\n\");\n  //    A.print();\n\n  ctx.get_dot()->set_current_color(\"yellow\");\n  PDTRTRI(A, CUBLAS_FILL_MODE_LOWER, CUBLAS_DIAG_NON_UNIT);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  //    fprintf(stderr, \"A=La^-1 after POTRI\\n\");\n  //    A.print();\n\n  // Computes the lower part of A^tA (La^-t La^-1)\n  ctx.get_dot()->set_current_color(\"blue\");\n  PDLAUUM(A, CUBLAS_FILL_MODE_LOWER);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  double b_nrm2_potri   = 0.0;\n  double res_nrm2_potri = 0.0;\n\n  if (check_result)\n  {\n    /* Right-hand side */\n    matrix<double> B_potri(N, 1, NB, 1, false, \"B_potri\");\n    matrix<double> Bref_potri(N, 1, NB, 1, false, \"Bref_potri\");\n\n    // auto rhs_vals = [](matrix<double>& mat, int row, int col) { return 1.0 * (row + 1); };\n    B_potri.fill(rhs_vals);\n    Bref_potri.fill(rhs_vals);\n\n    // AX = B, X = A^-1 B\n    // LLt X = B, X = (LLt)^-1 B = L^-t L^-1 B\n    // Compute Bref_potri = (A^-1 B - B)\n    PDNRM2_HOST(&Bref_potri, &b_nrm2_potri);\n\n    // B = (A^-1)*B (A triangular lower, B_potri full)\n    //    fprintf(stderr, \"B_potri before PDTRMM\\n\");\n    //    B_potri.print();\n    //\n    //    fprintf(stderr, \"A before PDTRMM\\n\");\n    //    A.print();\n\n    // B_tmp = 0 (to avoid NaN*0.0)\n    matrix<double> B_tmp(N, 1, NB, 1, false, \"B_tmp\");\n    auto zero_vals = [] _CCCL_HOST_DEVICE(size_t /* unused */, size_t /*unused*/) {\n      return 0.0;\n    };\n    B_tmp.fill(zero_vals);\n\n    // B_tmp = A * B_potri + 0*B_tmp\n    PDSYMM(CUBLAS_SIDE_LEFT, CUBLAS_FILL_MODE_LOWER, 1.0, A, B_potri, 0.0, B_tmp);\n\n    //    fprintf(stderr, \"B_potri after PDTRMM\\n\");\n    //    B_potri.print();\n\n    // res = A X - B\n    PDGEMM(CUBLAS_OP_N, CUBLAS_OP_N, 1.0, Aref, B_tmp, -1.0, Bref_potri);\n\n    //    fprintf(stderr, \"Bref_potri after PDGEMM\\n\");\n    //    Bref_potri.print();\n\n    // Compute residual\n    PDNRM2_HOST(&Bref_potri, &res_nrm2_potri);\n  }\n\n  cuda_safe_call(cudaSetDevice(0));\n  cuda_safe_call(cudaEventRecord(stopEvent, ctx.fence()));\n\n  ctx.finalize();\n\n  if (check_result_potrs)\n  {\n    double residual = sqrt(res_nrm2) / sqrt(Bref_nrm2);\n    // std::cout << \"[POTRS] ||AX - B|| : \" << sqrt(res_nrm2) << '\\n';\n    // std::cout << \"[POTRS] ||B|| : \" << sqrt(Bref_nrm2) << '\\n';\n    // std::cout << \"[POTRS] RESIDUAL (||AX - B||/||B||) : \" << residual << '\\n';\n    EXPECT(residual < 0.01);\n  }\n\n  if (check_result)\n  {\n    double residual_potri = sqrt(res_nrm2_potri) / sqrt(b_nrm2_potri);\n    // std::cout << \"[POTRI] RESIDUAL ||A * ((A^-1)B) - B|| : \" << sqrt(res_nrm2_potri) << '\\n';\n    // std::cout << \"[POTRI] RESIDUAL ||B|| : \" << sqrt(b_nrm2_potri) << '\\n';\n    // std::cout << \"[POTRI] RESIDUAL (||A * ((A^-1)B) - B||/||B||) : \" << residual_potri << '\\n';\n    EXPECT(residual_potri < 0.0001);\n  }\n\n  //    // Compute Aref * A^-1 in Aref (A^-1 is lower triangular)\n  //    PDTRMM(CUBLAS_SIDE_RIGHT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_N, CUBLAS_DIAG_NON_UNIT, 1.0, A, Aref);\n\n  //    // This should be almost identity\n  //    Aref.print();\n\n#if 0\n\n    std::cout << \"Print A^-1 after PDLAUUM : \" << '\\n';\n    A.print();\n\n    std::cout << \"RES after AX - B POTRI : \" << '\\n';\n    Bref_potri.print();\n\n    // This should be almost identity\n    Aref.print();\n#endif\n\n  float milliseconds;\n  cuda_safe_call(cudaEventElapsedTime(&milliseconds, startEvent, stopEvent));\n\n  double gflops = flops_dpotri((double) N) / (1000000000.0);\n  std::cout << \"[PDPOTRI] ELAPSED: \" << milliseconds << \" ms, GFLOPS: \" << gflops / (milliseconds / 1000.0) << '\\n';\n}\n\nint main(int argc, char** argv)\n{\n  int N  = 1024;\n  int NB = 128;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NB = atoi(argv[2]);\n  }\n\n  assert(N % NB == 0);\n\n  run(N, NB);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example implements the POTRI matrix inversion algorithm over multiple devices\n *\n *\n */\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <iostream>\n\n#include <hipsolver.h>\n\n#include <hipblas.h>\n\n#define TILED\n\nusing namespace cuda::experimental::stf;\n\nstream_ctx ctx;\n\nstatic std::unordered_map<exec_place, hipblasHandle_t, hash<exec_place>> cublas_handles;\nstatic std::unordered_map<exec_place, hipsolverHandle_t, hash<exec_place>> cusolver_handles;\n\n/* Get a CUBLAS handle valid on the current execution place, or initialize it lazily */\nhipblasHandle_t& get_cublas_handle(const exec_place& ep = exec_place::current_device())\n{\n  auto& result = cublas_handles[ep];\n  if (result == hipblasHandle_t())\n  { // not found, default value inserted\n    // Lazy initialization, and save the handle for future use\n    cuda_try(hipblasCreate(&result));\n  }\n  return result;\n}\n\n/* Get a CUSOLVER handle valid on the current execution place, or initialize it lazily */\nhipsolverHandle_t& get_cusolver_handle(const exec_place& ep = exec_place::current_device())\n{\n  auto& result = cusolver_handles[ep];\n  if (result == hipsolverHandle_t())\n  { // not found, default value inserted\n    // Lazy initialization, and save the handle for future use\n    cuda_try(hipsolverDnCreate(&result));\n  }\n  return result;\n}\n\ntemplate <typename T>\nclass matrix\n{\npublic:\n  matrix(int NROWS, int NCOLS, int BLOCKSIZE_ROWS, int BLOCKSIZE_COLS, bool is_sym, const char* _symbol = \"matrix\")\n  {\n    symbol = _symbol;\n\n    sym_matrix = is_sym;\n\n    m  = NROWS;\n    mb = BLOCKSIZE_ROWS;\n\n    n  = NCOLS;\n    nb = BLOCKSIZE_COLS;\n\n    assert(m % mb == 0);\n    assert(n % nb == 0);\n\n    size_t s = m * n * sizeof(T);\n    // cuda_try(cudaMallocHost(&h_array, m*n*sizeof(T)));\n    // fprintf(stderr, \"Allocating %ld x %ld x %ld = %ld bytes (%f GB) on host for %s\\n\", m, n, sizeof(T), s,\n    //        s / (1024.0 * 1024.0 * 1024.0), _symbol);\n    h_array = (T*) malloc(s);\n    assert(h_array);\n    cuda_try(hipHostRegister(h_array, s, hipHostRegisterPortable));\n    // cuda_try(cudaMalloc(&d_array, m*n*sizeof(T)));\n\n    // Compute the number of blocks\n    mt = m / mb;\n    nt = n / nb;\n\n    handles.resize(mt * nt);\n\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      size_t low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        T* addr_h = get_block_h(rowb, colb);\n        auto& h   = get_handle(rowb, colb);\n\n#ifdef TILED\n        // tiles are stored contiguously\n        size_t ld = mb;\n#else\n        size_t ld = m;\n#endif\n        std::ignore = ld; // work around compiler bug\n        h           = ctx.logical_data(make_slice(addr_h, std::tuple{mb, nb}, ld));\n        h.set_symbol(std::string(symbol) + \"_\" + std::to_string(rowb) + \"_\" + std::to_string(colb));\n        h.set_write_back(false);\n      }\n    }\n\n    cuda_try(hipGetDeviceCount(&ndevs));\n    for (int a = 1; a * a <= ndevs; a++)\n    {\n      if (ndevs % a == 0)\n      {\n        grid_p = a;\n        grid_q = ndevs / a;\n      }\n    }\n\n    assert(grid_p * grid_q == ndevs);\n\n    // std::cout << \"FOUND \" << ndevs << \" DEVICES \"\n    //          << \"p=\" << grid_p << \" q=\" << grid_q << '\\n';\n  }\n\n  int get_preferred_devid(int row, int col)\n  {\n    return (row % grid_p) + (col % grid_q) * grid_p;\n  }\n\n  auto& get_handle(int row, int col)\n  {\n    return handles[row + col * mt];\n  }\n\n  size_t get_index(size_t row, size_t col)\n  {\n#ifdef TILED\n    // Find which tile contains this element\n    int tile_row = row / mb;\n    int tile_col = col / nb;\n\n    size_t tile_size = mb * nb;\n\n    // Look for the index of the beginning of the tile\n    size_t tile_start = (tile_row + mt * tile_col) * tile_size;\n\n    // Offset within the tile\n    size_t offset = (row % mb) + (col % nb) * mb;\n\n    return tile_start + offset;\n#else\n    return row + col * m;\n#endif\n  }\n\n  T* get_block_h(int brow, int bcol)\n  {\n    size_t index = get_index(brow * mb, bcol * nb);\n    return &h_array[index];\n  }\n\n  // Fill with func(Matrix*,row, col)\n  template <typename Fun>\n  void fill(Fun&& fun)\n  {\n    nvtx_range r(\"fill\");\n\n    // Fill blocks by blocks\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      size_t low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        // Each task fills a block\n        auto& h   = get_handle(rowb, colb);\n        int devid = get_preferred_devid(rowb, colb);\n\n        ctx.parallel_for(exec_place::device(devid), h.shape(), h.write()).set_symbol(\"INIT\")->*\n          [=] _CCCL_DEVICE(size_t lrow, size_t lcol, auto sA) {\n            size_t row     = lrow + rowb * sA.extent(0);\n            size_t col     = lcol + colb * sA.extent(1);\n            sA(lrow, lcol) = fun(row, col);\n          };\n      }\n    }\n  }\n\n  // Print blocks\n  void print()\n  {\n    // print blocks by blocks\n    for (size_t colb = 0; colb < nt; colb++)\n    {\n      int low_rowb = sym_matrix ? colb : 0;\n      for (size_t rowb = low_rowb; rowb < mt; rowb++)\n      {\n        // Each task fills a block\n        ctx.host_launch(get_handle(rowb, colb).read())->*[=](auto sA) {\n          for (size_t lcol = 0; lcol < sA.extent(1); lcol++)\n          {\n            size_t col = lcol + colb * sA.extent(1);\n            for (size_t lrow = 0; lrow < sA.extent(0); lrow++)\n            {\n              size_t row = lrow + rowb * sA.extent(0);\n\n              fprintf(stderr, \"%d,%d : %le\\n\", row, col, sA(lrow, lcol));\n            }\n          }\n        };\n      }\n    }\n  }\n\n  T* h_array;\n  T* d_array;\n  size_t m; // nrows\n  size_t n; // ncols\n\n  // Is this a sym matrix ? (lower assumed)\n  bool sym_matrix;\n\n  size_t mb; // block size (rows)\n  size_t nb; // block size (cols)\n\n  size_t mt; // number of column blocks\n  size_t nt; // number of row blocks\n\n  // abstract data handles\n  std::vector<logical_data<slice<double, 2>>> handles;\n\n  const char* symbol;\n\n  // for the mapping\n  int ndevs;\n  int grid_p, grid_q;\n};\n\nvoid DPOTRF(hipblasFillMode_t uplo, matrix<double>& A, int A_row, int A_col)\n{\n  auto& Akk    = A.get_handle(A_row, A_col);\n  size_t m_akk = Akk.shape().extent(0);\n  // Note that the handle may be different from the actual handle...\n  int Lwork_expected;\n  cuda_safe_call(hipsolverDnDpotrf_bufferSize(get_cusolver_handle(), uplo, m_akk, nullptr, 0, &Lwork_expected));\n\n  auto potrf_buffer = ctx.logical_data(shape_of<slice<double>>(Lwork_expected));\n  potrf_buffer.set_allocator(ctx.get_default_allocator());\n\n  auto devInfo = ctx.logical_data(shape_of<slice<int>>(1));\n\n  auto t = ctx.task(Akk.rw(), potrf_buffer.write(), devInfo.write());\n  t.set_symbol(\"DPOTRF\");\n  t->*[&](auto s, auto sAkk, auto buffer, auto info) {\n    auto& h = get_cusolver_handle();\n    cuda_try(hipsolverSetStream(h, s));\n\n    cuda_try(hipsolverDnDpotrf(\n      h,\n      uplo,\n      sAkk.extent(0),\n      sAkk.data_handle(),\n      sAkk.stride(1),\n      buffer.data_handle(),\n      buffer.extent(0),\n      info.data_handle()));\n  };\n}\n\nvoid DTRTRI(hipblasFillMode_t uplo, hipblasDiagType_t diag, matrix<double>& A, int A_row, int A_col)\n{\n  // Preallocate a buffer used by CUSOLVER\n  size_t workspaceInBytesOnDevice, workspaceInBytesOnHost;\n  int64_t m_a00 = A.mb;\n  assert(A.mb == A.nb);\n\n  cuda_try(cusolverDnXtrtri_bufferSize(\n    get_cusolver_handle(),\n    uplo,\n    diag,\n    m_a00,\n    HIP_R_64F /* DTRTRI */,\n    nullptr,\n    m_a00,\n    &workspaceInBytesOnDevice,\n    &workspaceInBytesOnHost));\n\n  // We don't support allocating buffers of 0 bytes ... XXX\n  if (workspaceInBytesOnHost == 0)\n  {\n    workspaceInBytesOnHost = 8;\n  }\n\n  auto d_buffer = ctx.logical_data(shape_of<slice<char>>(workspaceInBytesOnDevice));\n  auto h_buffer = ctx.logical_data(shape_of<slice<char>>(workspaceInBytesOnHost));\n  d_buffer.set_allocator(ctx.get_default_allocator());\n  h_buffer.set_allocator(ctx.get_default_allocator());\n\n  auto devInfo = ctx.logical_data(shape_of<slice<int>>(1));\n\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).rw(), d_buffer.write(), h_buffer.write(data_place::managed()), devInfo.write());\n  t.set_symbol(\"DTRTRI\");\n  t->*[&](auto s, auto sA, auto dbuffer, auto hbuffer, auto info) {\n    auto& h = get_cusolver_handle();\n    cuda_try(hipsolverSetStream(h, s));\n\n    // DTRTRI(...)\n    cuda_try(cusolverDnXtrtri(\n      h,\n      uplo,\n      diag,\n      sA.extent(0),\n      HIP_R_64F /* DTRTRI */,\n      sA.data_handle(),\n      sA.stride(1),\n      (double*) dbuffer.data_handle(),\n      workspaceInBytesOnDevice,\n      (double*) hbuffer.data_handle(),\n      workspaceInBytesOnHost,\n      info.data_handle()));\n  };\n}\n\n/*\n * Note: this code was taken from CUSOLVER\n *\n * SLACPY copies all or part of a two-dimensional matrix A to another matrix B.\n *\n *  up     up_and_lo\n *  1          0          upper triangle, including diagonal\n *  0          0          lower triangle, including diagonal\n *  ?          1          whole matrix\n *\n * configuration:\n *   dim3 grids( m/VEC, m/BY )\n *   dim3 threads(VEC,BY)\n */\ntemplate <typename T_ELEM_SRC, typename T_ELEM_DST, int VEC_LOG, int BY_LOG>\n__global__ void __launch_bounds__(1 << (VEC_LOG + BY_LOG))\n  lacpy_kernel(int m, int n, const T_ELEM_SRC* A, size_t lda, T_ELEM_DST* B, size_t ldb, int up, int up_and_lo)\n{\n  const int VEC = (1 << VEC_LOG);\n  const int BY  = (1 << BY_LOG);\n\n  const int inx = threadIdx.x;\n  const int iny = threadIdx.y;\n\n  const int ibx = blockIdx.x * VEC;\n  const int iby = blockIdx.y * BY;\n\n  const int i = ibx + inx;\n  const int j = iby + iny;\n\n  if (ibx >= m)\n  {\n    return;\n  }\n  if (iby >= n)\n  {\n    return;\n  }\n\n  T_ELEM_SRC Areg = T_ELEM_SRC(0);\n\n  if (up_and_lo)\n  {\n    /*\n     * copy whole matrix\n             DO 60 J = 1, N\n                DO 50 I = 1, M\n                   B( I, J ) = A( I, J )\n       50       CONTINUE\n       60    CONTINUE\n    */\n    if ((i < m) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n    return;\n  }\n\n  // only lower or upper triangle is copied.\n  if (up)\n  {\n    /*\n     * copy upper triangle, including diagonal\n             DO 20 J = 1, N\n                DO 10 I = 1, MIN( J, M )\n                   B( I, J ) = A( I, J )\n       10       CONTINUE\n       20    CONTINUE\n     */\n    if ((i <= min(j, m - 1)) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n  }\n  else\n  {\n    /*\n     * copy lower triangle, including diagonal\n             DO 40 J = 1, N\n                DO 30 I = J, M\n                   B( I, J ) = A( I, J )\n       30       CONTINUE\n       40    CONTINUE\n\n     */\n    if (((j <= i) && (i < m)) && (j < n))\n    {\n      Areg           = A[i + j * lda];\n      B[i + j * ldb] = T_ELEM_DST(Areg);\n    }\n  }\n}\n\n/*\n * SLACPY copies all or part of a two-dimensional matrix A to another\n * matrix B.\n *\n * Input\n * -------\n *          UPLO is CHARACTER*1\n *          Specifies the part of the matrix A to be copied to B.\n *          = 'U':      Upper triangular part\n *          = 'L':      Lower triangular part\n *          Otherwise:  All of the matrix A\n *\n *          M is INTEGER\n *          The number of rows of the matrix A.\n *          M >= 0.\n *\n *          N is INTEGER\n *          The number of columns of the matrix A.\n *          N >= 0.\n *\n *          A is REAL array, dimension (LDA,N)\n *          The m by n matrix A.  If UPLO = 'U', only the upper triangle\n *          or trapezoid is accessed; if UPLO = 'L', only the lower\n *          triangle or trapezoid is accessed.\n *\n *          LDA is INTEGER\n *          The first dimension of the array A. LDA >= max(1,M).\n *\n *          B is REAL array, dimension (LDB,N)\n *          On exit, B = A in the locations specified by UPLO.\n *\n *          LDB is INTEGER\n *          The leading dimension of the array B.  LDB >= max(1,M).\n *\n */\ntemplate <typename T_ELEM_SRC, typename T_ELEM_DST>\nhipsolverStatus_t cusolverDnXlacpy(\n  hipblasFillMode_t uplo, // \"UPPER\", B = upper(A)\n                         // \"LOWER\", B = lower(A)\n                         // otherwise, B = A\n  int m,\n  int n,\n  const T_ELEM_SRC* A,\n  int lda,\n  T_ELEM_DST* B,\n  int ldb,\n  hipStream_t stream)\n{\n  hipsolverStatus_t status = HIPSOLVER_STATUS_SUCCESS;\n  hipError_t cudaStat1   = hipSuccess;\n\n  int up        = 0;\n  int up_and_lo = 0;\n\n  //  Quick return if possible\n  if ((0 >= m) || (0 >= n))\n  {\n    return status;\n  }\n\n  /*\n   *  up     up_and_lo\n   *  1          0          upper triangle, including diagonal\n   *  0          0          lower triangle, including diagonal\n   *  ?          1          whole matrix\n   */\n  if (HIPBLAS_FILL_MODE_LOWER == uplo)\n  {\n    // Lower triangular part\n    up = 0;\n  }\n  else if (HIPBLAS_FILL_MODE_UPPER == uplo)\n  {\n    // upper triangular part\n    up = 1;\n  }\n  else\n  {\n    up_and_lo = 1; // Otherwise:  All of the matrix A\n  }\n\n  const int VEC_LOG = 5;\n  const int BY_LOG  = 3;\n  const int VEC     = (1 << VEC_LOG);\n  const int BY      = (1 << BY_LOG);\n  dim3 grids((m + VEC - 1) / VEC, (n + BY - 1) / BY);\n  dim3 threads(VEC, BY);\n\n  lacpy_kernel<T_ELEM_SRC, T_ELEM_DST, VEC_LOG, BY_LOG>\n    <<<grids, threads, 0, stream>>>(m, n, A, (size_t) lda, B, (size_t) ldb, up, up_and_lo);\n\n  cudaStat1 = hipGetLastError(); /* launch error */\n  if (hipSuccess != cudaStat1)\n  {\n    fprintf(stderr, \"Error (lacpy): %d\\n\", cudaStat1);\n    status = HIPSOLVER_STATUS_EXECUTION_FAILED;\n  }\n\n  return status;\n}\n\nhipsolverStatus_t cusolverDnDlacpy(\n  hipblasFillMode_t uplo, // \"UPPER\", B = upper(A)\n                         // \"LOWER\", B = lower(A)\n                         // otherwise, B = A\n  int m,\n  int n,\n  const double* A,\n  int lda,\n  double* B,\n  int ldb,\n  hipStream_t stream)\n{\n  return cusolverDnXlacpy<double, double>(uplo, m, n, A, lda, B, ldb, stream);\n}\n\n// Pretend there is a CUBLAS interface for DLAAUM\nvoid cublasDnDlaaum_bufferSize(hipblasHandle_t /*unused*/, int m, int n, size_t* Workspace_size)\n{\n  assert(Workspace_size);\n  *Workspace_size = m * n * sizeof(double);\n}\n\n// Pretend there is a CUBLAS interface for DLAAUM\n// A triangular\n// Lower : A = A^T * A\n// Upper : A = A A^T\nvoid cublasDnDlaaum(\n  hipblasHandle_t cublas_handle,\n  hipblasFillMode_t uplo,\n  int m,\n  int n,\n  double* A,\n  int ldA,\n  double* Workspace_d,\n  size_t Workspace_size)\n{\n  hipStream_t stream;\n  cuda_safe_call(hipblasGetStream(cublas_handle, &stream));\n\n  // \"Hand coded\"\n  // We use a full copy of A !\n  // fprintf(stderr, \"GOT Workspace_size %ld ... expected %d\\n\", Workspace_size, m * n * sizeof(double));\n  std::ignore = Workspace_size;\n  assert(Workspace_size >= m * n * sizeof(double));\n\n  double* B = Workspace_d;\n  int ldB   = m;\n\n  // Blank the buffer\n  cuda_safe_call(hipMemsetAsync(B, 0, m * n * sizeof(double), stream));\n\n  // Copy A (with upper or lower 0 untouched)\n  cusolverDnDlacpy(uplo, m, n, A, ldA, B, ldB, stream);\n\n  hipblasDiagType_t diag = HIPBLAS_DIAG_NON_UNIT;\n  const double one      = 1.0;\n\n  auto side = (uplo == HIPBLAS_FILL_MODE_LOWER) ? HIPBLAS_SIDE_LEFT : HIPBLAS_SIDE_RIGHT;\n\n  // LOWER: TRMM(A,B) : B = op(A) * B = A^T * B with A triangular (B = C in CUBLAS), CUBLAS_OP_T, CUBLAS_SIDE_RIGHT\n  // UPPER: TRMM(A,B) : B = B * op(A) = B A^T with A triangular (B = C in CUBLAS), CUBLAS_OP_T, CUBLAS_SIDE_RIGHT\n  cuda_safe_call(hipblasDtrmm(cublas_handle, side, uplo, HIPBLAS_OP_T, diag, m, n, &one, A, ldA, B, ldB, B, ldB));\n\n  // Copy B=AA^T back into A (with upper or lower 0 untouched)\n  cusolverDnDlacpy(uplo, m, n, B, ldB, A, ldA, stream);\n}\n\nvoid DLAAUM(hipblasFillMode_t uplo, matrix<double>& A, int A_row, int A_col)\n{\n  int NB = A.mb;\n  size_t Lwork;\n  cublasDnDlaaum_bufferSize(get_cublas_handle(), NB, NB, &Lwork);\n\n  auto d_buffer = ctx.logical_data(shape_of<slice<char>>(Lwork));\n\n  auto t = ctx.task(A.get_handle(A_row, A_col).rw(), d_buffer.write());\n  t.set_symbol(\"DLAAUM\");\n  t->*[&](auto s, auto sA, auto buffer) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    cublasDnDlaaum(\n      h, uplo, sA.extent(0), sA.extent(1), sA.data_handle(), sA.stride(1), (double*) buffer.data_handle(), Lwork);\n  };\n}\n\nvoid DGEMM(\n  hipblasOperation_t transa,\n  hipblasOperation_t transb,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DGEMM\");\n  t->*[&](auto s, auto sA, auto sB, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    int k = (transa == HIPBLAS_OP_N) ? sA.extent(1) : sA.extent(0);\n    cuda_try(hipblasDgemm(\n      h,\n      transa,\n      transb,\n      sC.extent(0),\n      sC.extent(1),\n      k,\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      &beta,\n      sC.data_handle(),\n      sC.stride(1)));\n  };\n}\n\nvoid DSYMM(\n  hipblasSideMode_t side,\n  hipblasFillMode_t uplo,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t =\n    ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DSYMM\");\n  t->*[&](auto s, auto sA, auto sB, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    cuda_try(hipblasDsymm(\n      h,\n      side,\n      uplo,\n      sC.extent(0),\n      sC.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      &beta,\n      sC.data_handle(),\n      sC.stride(1)));\n  };\n}\n\nvoid DSYRK(\n  hipblasFillMode_t uplo,\n  hipblasOperation_t trans,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  double beta,\n  matrix<double>& C,\n  int C_row,\n  int C_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), C.get_handle(C_row, C_col).rw());\n  t.set_symbol(\"DSYRK\");\n  t->*[&](auto s, auto sA, auto sC) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    // number of rows of matrix op(A) and C\n    int n = sC.extent(0);\n\n    // number of columns of matrix op(A)\n    int k = (trans == HIPBLAS_OP_N) ? sA.extent(1) : sA.extent(0);\n\n    cuda_try(\n      hipblasDsyrk(h, uplo, trans, n, k, &alpha, sA.data_handle(), sA.stride(1), &beta, sC.data_handle(), sC.stride(1)));\n  };\n}\n\nvoid DTRSM(\n  hipblasSideMode_t side,\n  hipblasFillMode_t uplo,\n  hipblasOperation_t transa,\n  hipblasDiagType_t diag,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).rw());\n  t.set_symbol(\"DTRSM\");\n  t->*[&](auto s, auto sA, auto sB) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    cuda_try(hipblasDtrsm(\n      h,\n      side,\n      uplo,\n      transa,\n      diag,\n      sB.extent(0),\n      sB.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1)));\n  };\n}\n\nvoid DTRMM(\n  hipblasSideMode_t side,\n  hipblasFillMode_t uplo,\n  hipblasOperation_t transa,\n  hipblasDiagType_t diag,\n  double alpha,\n  matrix<double>& A,\n  int A_row,\n  int A_col,\n  matrix<double>& B,\n  int B_row,\n  int B_col)\n{\n  auto ignored = get_cublas_handle();\n  auto t       = ctx.task(A.get_handle(A_row, A_col).read(), B.get_handle(B_row, B_col).rw());\n  t.set_symbol(\"DTRMM\");\n  t->*[&](auto s, auto sA, auto sB) {\n    auto& h = get_cublas_handle();\n    cuda_try(hipblasSetStream(h, s));\n\n    // Note : CUBLAS DTRMM implementation is out of place but supports in place by using the same buffer B and C\n    cuda_try(hipblasDtrmm(\n      get_cublas_handle(),\n      side,\n      uplo,\n      transa,\n      diag,\n      sB.extent(0),\n      sB.extent(1),\n      &alpha,\n      sA.data_handle(),\n      sA.stride(1),\n      sB.data_handle(),\n      sB.stride(1),\n      sB.data_handle(),\n      sB.stride(1) /* same as B*/));\n  };\n}\n\nvoid PDNRM2_HOST(matrix<double>* A, double* result)\n{\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"red\");\n#endif\n\n  for (size_t rowb = 0; rowb < A->mt; rowb++)\n  {\n    for (size_t colb = 0; colb < A->nt; colb++)\n    {\n      ctx.host_launch(A->get_handle(rowb, colb).read())->*[=](auto sA) {\n        double res2 = 0.0;\n        for (size_t col = 0; col < sA.extent(1); col++)\n        {\n          for (size_t row = 0; row < sA.extent(0); row++)\n          {\n            double v = sA(row, col);\n            res2 += v * v;\n          }\n        }\n        *result += res2;\n      };\n    }\n  }\n}\n\nvoid PDPOTRF(matrix<double>& A)\n{\n  nvtx_range r(\"PDPOTRF\");\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"yellow\");\n#endif\n\n  assert(A.m == A.n);\n  assert(A.mt == A.nt);\n\n  int NBLOCKS = A.mt;\n  assert(A.mb == A.nb);\n\n  for (int K = 0; K < NBLOCKS; K++)\n  {\n    cuda_try(hipSetDevice(A.get_preferred_devid(K, K)));\n    DPOTRF(HIPBLAS_FILL_MODE_LOWER, A, K, K);\n\n    for (int row = K + 1; row < NBLOCKS; row++)\n    {\n      cuda_try(hipSetDevice(A.get_preferred_devid(row, K)));\n      DTRSM(HIPBLAS_SIDE_RIGHT, HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_T, HIPBLAS_DIAG_NON_UNIT, 1.0, A, K, K, A, row, K);\n\n      for (int col = K + 1; col < row; col++)\n      {\n        cuda_try(hipSetDevice(A.get_preferred_devid(row, col)));\n        DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_T, -1.0, A, row, K, A, col, K, 1.0, A, row, col);\n      }\n\n      cuda_try(hipSetDevice(A.get_preferred_devid(row, row)));\n      DSYRK(HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_N, -1.0, A, row, K, 1.0, A, row, row);\n    }\n  }\n}\n\n// Algorithm from PLASMA\nvoid PDTRSM(hipblasSideMode_t side,\n            hipblasFillMode_t uplo,\n            hipblasOperation_t trans,\n            hipblasDiagType_t diag,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B)\n{\n  nvtx_range r(\"PDTRSM\");\n  //    std::cout << \"[PDTRSM] START B MT \" << B.mt << \" NT \" << B.nt << '\\n';\n\n  if (side == HIPBLAS_SIDE_LEFT)\n  {\n    if (uplo == HIPBLAS_FILL_MODE_UPPER)\n    {\n      // TODO\n      abort();\n    }\n    else\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //===========================================\n      if (trans == HIPBLAS_OP_N)\n      {\n        for (size_t k = 0; k < B.mt; k++)\n        {\n          double lalpha = k == 0 ? alpha : 1.0;\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(hipSetDevice(A.get_preferred_devid(k, k)));\n            DTRSM(side, uplo, trans, diag, lalpha, A, k, k, B, k, n);\n          }\n          for (size_t m = k + 1; m < B.mt; m++)\n          {\n            for (size_t n = 0; n < B.nt; n++)\n            {\n              cuda_try(hipSetDevice(A.get_preferred_devid(m, k)));\n              DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, -1.0, A, m, k, B, k, n, lalpha, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_[C|T]\n      //================================================\n      else\n      {\n        for (size_t k = 0; k < B.mt; k++)\n        {\n          double lalpha = k == 0 ? alpha : 1.0;\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(hipSetDevice(A.get_preferred_devid(B.mt - k - 1, B.mt - k - 1)));\n            DTRSM(side, uplo, trans, diag, lalpha, A, B.mt - k - 1, B.mt - k - 1, B, B.mt - k - 1, n);\n          }\n          for (size_t m = k + 1; m < B.mt; m++)\n          {\n            for (size_t n = 0; n < B.nt; n++)\n            {\n              cuda_try(hipSetDevice(A.get_preferred_devid(B.mt - k - 1, B.mt - 1 - m)));\n              DGEMM(\n                trans, HIPBLAS_OP_N, -1.0, A, B.mt - k - 1, B.mt - 1 - m, B, B.mt - k - 1, n, lalpha, B, B.mt - 1 - m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n  else\n  {\n    // TODO\n    abort();\n  }\n  //    std::cout << \"[PDTRSM] END\" << '\\n';\n}\n\nvoid PDPOTRS(matrix<double>& A, matrix<double>& B, hipblasFillMode_t uplo)\n{\n  nvtx_range r(\"PDPOTRS\");\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"green\");\n#endif\n\n  //    std::cout << \"[PDPOTRS] START\" << '\\n';\n  // Call the parallel functions.\n  PDTRSM(\n    HIPBLAS_SIDE_LEFT, uplo, uplo == HIPBLAS_FILL_MODE_UPPER ? HIPBLAS_OP_T : HIPBLAS_OP_N, HIPBLAS_DIAG_NON_UNIT, 1.0, A, B);\n\n#ifdef HAVE_DOT\n  ctx.get_dot()->set_current_color(\"darkgreen\");\n#endif\n\n  PDTRSM(\n    HIPBLAS_SIDE_LEFT, uplo, uplo == HIPBLAS_FILL_MODE_UPPER ? HIPBLAS_OP_N : HIPBLAS_OP_T, HIPBLAS_DIAG_NON_UNIT, 1.0, A, B);\n  //    std::cout << \"[PDPOTRS] END\" << '\\n';\n}\n\n/***************************************************************************/ /**\n                                                                               * Parallel tile matrix-matrix\n                                                                               *multiplication.\n                                                                               * @see plasma_omp_dgemm\n                                                                               ******************************************************************************/\nvoid PDGEMM(hipblasOperation_t transa,\n            hipblasOperation_t transb,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B,\n            double beta,\n            matrix<double>& C)\n{\n  nvtx_range r(\"PDGEMM\");\n\n#ifdef HAVE_DOT\n  reserved::dot::set_current_color(\"blue\");\n#endif\n\n  for (size_t m = 0; m < C.mt; m++)\n  {\n    for (size_t n = 0; n < C.nt; n++)\n    {\n      cuda_try(hipSetDevice(C.get_preferred_devid(m, n)));\n\n      //=========================================\n      // alpha*A*B does not contribute; scale C\n      //=========================================\n      int inner_k = transa == HIPBLAS_OP_N ? A.n : A.m;\n      if (alpha == 0.0 || inner_k == 0)\n      {\n        DGEMM(transa, transb, alpha, A, 0, 0, B, 0, 0, beta, C, m, n);\n      }\n      else if (transa == HIPBLAS_OP_N)\n      {\n        //================================\n        // CUBLAS_OP_N / CUBLAS_OP_N\n        //================================\n        if (transb == HIPBLAS_OP_N)\n        {\n          assert(A.nt == B.mt);\n          for (size_t k = 0; k < A.nt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n          }\n        }\n        //=====================================\n        // CUBLAS_OP_N / CUBLAS_OP_T\n        //=====================================\n        else\n        {\n          for (size_t k = 0; k < A.nt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, m, k, B, n, k, zbeta, C, m, n);\n          }\n        }\n      }\n      else\n      {\n        //=====================================\n        // CUBLAS_OP_T / CUBLAS_OP_N\n        //=====================================\n        if (transb == HIPBLAS_OP_N)\n        {\n          for (size_t k = 0; k < A.mt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n          }\n        }\n        //==========================================\n        // CUBLAS_OP_T / CUBLAS_OP_T\n        //==========================================\n        else\n        {\n          for (size_t k = 0; k < A.mt; k++)\n          {\n            double zbeta = k == 0 ? beta : 1.0;\n            DGEMM(transa, transb, alpha, A, k, m, B, n, k, zbeta, C, m, n);\n          }\n        }\n      }\n    }\n  }\n}\n\n/*\n * Algorithm taken from the PLASMA library\n */\n// We assume a lower triangular matrix (uplo == CUBLAS_FILL_MODE_LOWER)\nvoid PDTRTRI(matrix<double>& A, hipblasFillMode_t uplo, hipblasDiagType_t diag)\n{\n  nvtx_range r(\"PDTRTRI\");\n\n  assert(uplo == HIPBLAS_FILL_MODE_LOWER);\n\n  for (size_t k = 0; k < A.nt; k++)\n  {\n    for (size_t m = k + 1; m < A.mt; m++)\n    {\n      cuda_try(hipSetDevice(A.get_preferred_devid(m, k)));\n      DTRSM(HIPBLAS_SIDE_RIGHT, HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_N, diag, -1.0, A, k, k, A, m, k);\n    }\n    for (size_t m = k + 1; m < A.mt; m++)\n    {\n      for (size_t n = 0; n < k; n++)\n      {\n        cuda_try(hipSetDevice(A.get_preferred_devid(m, n)));\n        DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, 1.0, A, m, k, A, k, n, 1.0, A, m, n);\n      }\n    }\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(hipSetDevice(A.get_preferred_devid(k, n)));\n      DTRSM(HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_N, diag, 1.0, A, k, k, A, k, n);\n    }\n\n    // DTRTRI(...)\n    cuda_try(hipSetDevice(A.get_preferred_devid(k, k)));\n    DTRTRI(uplo, diag, A, k, k);\n  }\n}\n\n/*\n * Algorithm taken from the PLASMA library\n */\n// We assume a lower triangular matrix (uplo == CUBLAS_FILL_MODE_LOWER)\nvoid PDLAUUM(matrix<double>& A, hipblasFillMode_t uplo)\n{\n  nvtx_range r(\"PDLAUUM\");\n  assert(uplo == HIPBLAS_FILL_MODE_LOWER);\n\n  for (size_t k = 0; k < A.mt; k++)\n  {\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(hipSetDevice(A.get_preferred_devid(n, n)));\n      DSYRK(HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_T, 1.0, A, k, n, 1.0, A, n, n);\n\n      for (size_t m = n + 1; m < k; m++)\n      {\n        cuda_try(hipSetDevice(A.get_preferred_devid(m, n)));\n        DGEMM(HIPBLAS_OP_T, HIPBLAS_OP_N, 1.0, A, k, m, A, k, n, 1.0, A, m, n);\n      }\n    }\n    for (size_t n = 0; n < k; n++)\n    {\n      cuda_try(hipSetDevice(A.get_preferred_devid(k, n)));\n      DTRMM(HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_LOWER, HIPBLAS_OP_T, HIPBLAS_DIAG_NON_UNIT, 1.0, A, k, k, A, k, n);\n    }\n\n    // LAAUM (Akk RW) (compute Akk^T * Akk)\n    cuda_try(hipSetDevice(A.get_preferred_devid(k, k)));\n    DLAAUM(uplo, A, k, k);\n  }\n}\n\nvoid PDSYMM(hipblasSideMode_t side,\n            hipblasFillMode_t uplo,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B,\n            double beta,\n            matrix<double>& C)\n{\n  nvtx_range r(\"PDSYMM\");\n\n  size_t k, m, n;\n  double zbeta;\n  double zone = (double) 1.0;\n\n  for (m = 0; m < C.mt; m++)\n  {\n    for (n = 0; n < C.nt; n++)\n    {\n      cuda_try(hipSetDevice(C.get_preferred_devid(m, n)));\n      /*\n       *  CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER\n       */\n      if (side == HIPBLAS_SIDE_LEFT)\n      {\n        if (uplo == HIPBLAS_FILL_MODE_LOWER)\n        {\n          for (k = 0; k < C.mt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < m)\n            {\n              DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == m)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, k, n, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(HIPBLAS_OP_T, HIPBLAS_OP_N, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n        /*\n         *  CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER\n         */\n        else\n        {\n          for (k = 0; k < C.mt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < m)\n            {\n              DGEMM(HIPBLAS_OP_T, HIPBLAS_OP_N, alpha, A, k, m, B, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == m)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, k, n, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, alpha, A, m, k, B, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n      }\n      /*\n       *  CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER\n       */\n      else\n      {\n        if (uplo == HIPBLAS_FILL_MODE_LOWER)\n        {\n          for (k = 0; k < C.nt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < n)\n            {\n              DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_T, alpha, B, m, k, A, n, k, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == n)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, m, k, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, alpha, B, m, k, A, k, n, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n        /*\n         *  CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER\n         */\n        else\n        {\n          for (k = 0; k < C.nt; k++)\n          {\n            zbeta = k == 0 ? beta : zone;\n            if (k < n)\n            {\n              DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, alpha, B, m, k, A, k, n, zbeta, C, m, n);\n            }\n            else\n            {\n              if (k == n)\n              {\n                DSYMM(side, uplo, alpha, A, k, k, B, m, k, zbeta, C, m, n);\n              }\n              else\n              {\n                DGEMM(HIPBLAS_OP_N, HIPBLAS_OP_T, alpha, B, m, k, A, n, k, zbeta, C, m, n);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid PDTRMM(hipblasSideMode_t side,\n            hipblasFillMode_t uplo,\n            hipblasOperation_t trans,\n            hipblasDiagType_t diag,\n            double alpha,\n            matrix<double>& A,\n            matrix<double>& B)\n{\n  if (side == HIPBLAS_SIDE_LEFT)\n  {\n    if (uplo == HIPBLAS_FILL_MODE_UPPER)\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_N\n      //===========================================\n      if (trans == HIPBLAS_OP_N)\n      {\n        for (size_t m = 0; m < B.mt; m++)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (size_t k = m + 1; k < A.mt; k++)\n            {\n              DGEMM(trans, HIPBLAS_OP_N, alpha, A, m, k, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_T\n      //================================================\n      else\n      {\n        for (ssize_t m = B.mt - 1; m > -1; m--)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (ssize_t k = 0; k < m; k++)\n            {\n              DGEMM(trans, HIPBLAS_OP_N, alpha, A, k, m, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n    else\n    {\n      //===========================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //===========================================\n      if (trans == HIPBLAS_OP_N)\n      {\n        for (ssize_t m = B.mt - 1; m > -1; m--)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (ssize_t k = 0; k < m; k++)\n            {\n              DGEMM(trans, HIPBLAS_OP_N, alpha, A, m, k, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //================================================\n      // CUBLAS_SIDE_LEFT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_T\n      //================================================\n      else\n      {\n        for (size_t m = 0; m < B.mt; m++)\n        {\n          for (size_t n = 0; n < B.nt; n++)\n          {\n            DTRMM(side, uplo, trans, diag, alpha, A, m, m, B, m, n);\n\n            for (size_t k = m + 1; k < A.mt; k++)\n            {\n              DGEMM(trans, HIPBLAS_OP_N, alpha, A, k, m, B, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n  else\n  {\n    if (uplo == HIPBLAS_FILL_MODE_UPPER)\n    {\n      //============================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER / CUBLAS_OP_N\n      //============================================\n      if (trans == HIPBLAS_OP_N)\n      {\n        for (ssize_t n = B.nt - 1; n > -1; n--)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (ssize_t k = 0; k < n; k++)\n            {\n              DGEMM(HIPBLAS_OP_N, trans, alpha, B, m, k, A, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //=================================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_UPPER / Plasma[_Conj]Trans\n      //=================================================\n      else\n      {\n        for (size_t n = 0; n < B.nt; n++)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (size_t k = n + 1; k < A.mt; k++)\n            {\n              DGEMM(HIPBLAS_OP_N, trans, alpha, B, m, k, A, n, k, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n    else\n    {\n      //============================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER / CUBLAS_OP_N\n      //============================================\n      if (trans == HIPBLAS_OP_N)\n      {\n        for (size_t n = 0; n < B.nt; n++)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (size_t k = n + 1; k < A.mt; k++)\n            {\n              DGEMM(HIPBLAS_OP_N, trans, alpha, B, m, k, A, k, n, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n      //=================================================\n      // CUBLAS_SIDE_RIGHT / CUBLAS_FILL_MODE_LOWER / Plasma[_Conj]Trans\n      //=================================================\n      else\n      {\n        for (ssize_t n = B.nt - 1; n > -1; n--)\n        {\n          for (size_t m = 0; m < B.mt; m++)\n          {\n            cuda_try(hipSetDevice(B.get_preferred_devid(m, n)));\n\n            DTRMM(side, uplo, trans, diag, alpha, A, n, n, B, m, n);\n\n            for (ssize_t k = 0; k < n; k++)\n            {\n              DGEMM(HIPBLAS_OP_N, trans, alpha, B, m, k, A, n, k, 1.0, B, m, n);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// Taken from Chameleon (INRIA)\n// All the formula are reported in the LAPACK Lawn 41:\n//     http://www.netlib.org/lapack/lawns/lawn41.ps\n#define FMULS_POTRI(__n) ((double) (__n) * ((2. / 3.) + (double) (__n) * ((1. / 3.) * (double) (__n) + 1.)))\n#define FADDS_POTRI(__n) ((double) (__n) * ((1. / 6.) + (double) (__n) * ((1. / 3.) * (double) (__n) - 0.5)))\ndouble flops_dpotri(double __n)\n{\n  double flops = (FMULS_POTRI((__n)) + FADDS_POTRI((__n)));\n  return flops;\n}\n\nvoid run(int N, int NB)\n{\n  // Use pools of preallocated blocks\n  auto fixed_alloc = block_allocator<fixed_size_allocator>(ctx, NB * NB * sizeof(double));\n  ctx.set_allocator(fixed_alloc);\n\n  // Set up CUBLAS and CUSOLVER\n  int ndevs;\n  cuda_try(hipGetDeviceCount(&ndevs));\n\n  for (int d = 0; d < ndevs; d++)\n  {\n    auto ldummy = ctx.logical_data(shape_of<slice<char>>(1));\n    ctx.task(exec_place::device(d), ldummy.write())->*[](hipStream_t, auto) {\n      get_cublas_handle();\n      get_cusolver_handle();\n    };\n\n    ctx.task(exec_place::host(), ldummy.write(data_place::managed()))->*[](hipStream_t, auto) {};\n  }\n\n  cuda_try(hipSetDevice(0));\n\n  hipStream_t timing_stream;\n  cuda_try(hipStreamCreate(&timing_stream));\n\n  matrix<double> A(N, N, NB, NB, true, \"A\");\n  matrix<double> Aref(N, N, NB, NB, false, \"Aref\");\n\n  // (Hilbert matrix + 2*N*Id) to have a diagonal dominant matrix\n  auto hilbert = [=] _CCCL_HOST_DEVICE(size_t row, size_t col) {\n    return 1.0 / (col + row + 1.0) + 2.0 * N * (col == row);\n  };\n\n  Aref.fill(hilbert);\n  A.fill(hilbert);\n\n  /* Right-hand side */\n  matrix<double> B_potrs(N, 1, NB, 1, false, \"B\");\n  matrix<double> Bref_potrs(N, 1, NB, 1, false, \"Bref\");\n\n  auto rhs_vals = [] _CCCL_HOST_DEVICE(size_t row, size_t /*unused*/) {\n    return 1.0 * (row + 1);\n  };\n\n  B_potrs.fill(rhs_vals);\n  Bref_potrs.fill(rhs_vals);\n\n  int check_result = 1;\n  if (getenv(\"CHECK_RESULT\"))\n  {\n    check_result = atoi(getenv(\"CHECK_RESULT\"));\n  }\n\n  int check_result_potrs = check_result;\n  if (getenv(\"CHECK_RESULT_POTRS\"))\n  {\n    check_result_potrs = atoi(getenv(\"CHECK_RESULT_POTRS\"));\n  }\n\n  //    // Compute ||Bref||\n  double Bref_nrm2 = 0.0;\n  double res_nrm2  = 0.0;\n\n  if (check_result_potrs)\n  {\n    PDNRM2_HOST(&Bref_potrs, &Bref_nrm2);\n  }\n\n  hipEvent_t startEvent, stopEvent;\n\n  cuda_safe_call(hipSetDevice(0));\n  cuda_safe_call(hipStreamSynchronize(ctx.fence()));\n  cuda_safe_call(hipEventCreate(&startEvent));\n  cuda_safe_call(hipEventCreate(&stopEvent));\n  cuda_safe_call(hipEventRecord(startEvent, ctx.fence()));\n\n  ctx.get_dot()->set_current_color(\"green\");\n  PDPOTRF(A);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  /*\n   *  POTRS\n   */\n\n  if (check_result_potrs)\n  {\n    // Solve AX = B and put the result in B\n    PDPOTRS(A, B_potrs, HIPBLAS_FILL_MODE_LOWER);\n\n    // Compute (AX - B)\n    // Bref = (Aref*B - Bref)\n    PDGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, 1.0, Aref, B_potrs, -1.0, Bref_potrs);\n\n    // Compute ||AX - B|| = ||Bref||\n    PDNRM2_HOST(&Bref_potrs, &res_nrm2);\n  }\n\n  /*\n   *  POTRI\n   */\n  /* PDPOTRI = PDTRTRI + PDLAUUM */\n\n  // PDTRTRI : La^-1 (invert A)\n  //    fprintf(stderr, \"A=La before POTRI\\n\");\n  //    A.print();\n\n  ctx.get_dot()->set_current_color(\"yellow\");\n  PDTRTRI(A, HIPBLAS_FILL_MODE_LOWER, HIPBLAS_DIAG_NON_UNIT);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  //    fprintf(stderr, \"A=La^-1 after POTRI\\n\");\n  //    A.print();\n\n  // Computes the lower part of A^tA (La^-t La^-1)\n  ctx.get_dot()->set_current_color(\"blue\");\n  PDLAUUM(A, HIPBLAS_FILL_MODE_LOWER);\n  ctx.get_dot()->set_current_color(\"white\");\n\n  double b_nrm2_potri   = 0.0;\n  double res_nrm2_potri = 0.0;\n\n  if (check_result)\n  {\n    /* Right-hand side */\n    matrix<double> B_potri(N, 1, NB, 1, false, \"B_potri\");\n    matrix<double> Bref_potri(N, 1, NB, 1, false, \"Bref_potri\");\n\n    // auto rhs_vals = [](matrix<double>& mat, int row, int col) { return 1.0 * (row + 1); };\n    B_potri.fill(rhs_vals);\n    Bref_potri.fill(rhs_vals);\n\n    // AX = B, X = A^-1 B\n    // LLt X = B, X = (LLt)^-1 B = L^-t L^-1 B\n    // Compute Bref_potri = (A^-1 B - B)\n    PDNRM2_HOST(&Bref_potri, &b_nrm2_potri);\n\n    // B = (A^-1)*B (A triangular lower, B_potri full)\n    //    fprintf(stderr, \"B_potri before PDTRMM\\n\");\n    //    B_potri.print();\n    //\n    //    fprintf(stderr, \"A before PDTRMM\\n\");\n    //    A.print();\n\n    // B_tmp = 0 (to avoid NaN*0.0)\n    matrix<double> B_tmp(N, 1, NB, 1, false, \"B_tmp\");\n    auto zero_vals = [] _CCCL_HOST_DEVICE(size_t /* unused */, size_t /*unused*/) {\n      return 0.0;\n    };\n    B_tmp.fill(zero_vals);\n\n    // B_tmp = A * B_potri + 0*B_tmp\n    PDSYMM(HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_LOWER, 1.0, A, B_potri, 0.0, B_tmp);\n\n    //    fprintf(stderr, \"B_potri after PDTRMM\\n\");\n    //    B_potri.print();\n\n    // res = A X - B\n    PDGEMM(HIPBLAS_OP_N, HIPBLAS_OP_N, 1.0, Aref, B_tmp, -1.0, Bref_potri);\n\n    //    fprintf(stderr, \"Bref_potri after PDGEMM\\n\");\n    //    Bref_potri.print();\n\n    // Compute residual\n    PDNRM2_HOST(&Bref_potri, &res_nrm2_potri);\n  }\n\n  cuda_safe_call(hipSetDevice(0));\n  cuda_safe_call(hipEventRecord(stopEvent, ctx.fence()));\n\n  ctx.finalize();\n\n  if (check_result_potrs)\n  {\n    double residual = sqrt(res_nrm2) / sqrt(Bref_nrm2);\n    // std::cout << \"[POTRS] ||AX - B|| : \" << sqrt(res_nrm2) << '\\n';\n    // std::cout << \"[POTRS] ||B|| : \" << sqrt(Bref_nrm2) << '\\n';\n    // std::cout << \"[POTRS] RESIDUAL (||AX - B||/||B||) : \" << residual << '\\n';\n    EXPECT(residual < 0.01);\n  }\n\n  if (check_result)\n  {\n    double residual_potri = sqrt(res_nrm2_potri) / sqrt(b_nrm2_potri);\n    // std::cout << \"[POTRI] RESIDUAL ||A * ((A^-1)B) - B|| : \" << sqrt(res_nrm2_potri) << '\\n';\n    // std::cout << \"[POTRI] RESIDUAL ||B|| : \" << sqrt(b_nrm2_potri) << '\\n';\n    // std::cout << \"[POTRI] RESIDUAL (||A * ((A^-1)B) - B||/||B||) : \" << residual_potri << '\\n';\n    EXPECT(residual_potri < 0.0001);\n  }\n\n  //    // Compute Aref * A^-1 in Aref (A^-1 is lower triangular)\n  //    PDTRMM(CUBLAS_SIDE_RIGHT, CUBLAS_FILL_MODE_LOWER, CUBLAS_OP_N, CUBLAS_DIAG_NON_UNIT, 1.0, A, Aref);\n\n  //    // This should be almost identity\n  //    Aref.print();\n\n#if 0\n\n    std::cout << \"Print A^-1 after PDLAUUM : \" << '\\n';\n    A.print();\n\n    std::cout << \"RES after AX - B POTRI : \" << '\\n';\n    Bref_potri.print();\n\n    // This should be almost identity\n    Aref.print();\n#endif\n\n  float milliseconds;\n  cuda_safe_call(hipEventElapsedTime(&milliseconds, startEvent, stopEvent));\n\n  double gflops = flops_dpotri((double) N) / (1000000000.0);\n  std::cout << \"[PDPOTRI] ELAPSED: \" << milliseconds << \" ms, GFLOPS: \" << gflops / (milliseconds / 1000.0) << '\\n';\n}\n\nint main(int argc, char** argv)\n{\n  int N  = 1024;\n  int NB = 128;\n\n  if (argc > 1)\n  {\n    N = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NB = atoi(argv[2]);\n  }\n\n  assert(N % NB == 0);\n\n  run(N, NB);\n}\n", "source": "/kaggle/working/cccl/cudax/examples/stf/linear_algebra/07-potri.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include <thrust/device_vector.h>\n\n#include <cuda/std/cstddef>\n#include <cuda/std/cstdint>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n\n#include <nvbench/nvbench.cuh>\n#include <nvbench/range.cuh>\n\nnamespace cudax = cuda::experimental;\n\n// repeat hash computation n times\nstatic constexpr auto n_repeats = 100;\n\ntemplate <cuda::std::int32_t Words>\nstruct large_key\n{\n  constexpr __host__ __device__ large_key(cuda::std::int32_t seed) noexcept\n  {\n    for (cuda::std::int32_t i = 0; i < Words; ++i)\n    {\n      data_[i] = seed;\n    }\n  }\n\nprivate:\n  cuda::std::int32_t data_[Words];\n};\n\ntemplate <cuda::std::int32_t BlockSize, typename Key, typename Hasher, typename OutputIt>\n__global__ void hash_bench_kernel(Hasher hash, size_t n, OutputIt out, bool materialize_result)\n{\n  size_t const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  size_t const loop_stride = gridDim.x * BlockSize;\n  size_t idx               = gid;\n  using result_t           = decltype(hash(0));\n\n  result_t agg{};\n\n  while (idx < n)\n  {\n    Key key(idx);\n    for (cuda::std::int32_t i = 0; i < n_repeats; ++i)\n    { // execute hash func n times\n      agg += hash(key);\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result)\n  {\n    out[gid] = agg;\n  }\n}\n\n// benchmark evaluating performance of various hash functions\ntemplate <typename HasherTag, typename Key>\nvoid hash_eval(nvbench::state& state, nvbench::type_list<HasherTag, Key>)\n{\n  using Hash = typename HasherTag::template fn<Key>;\n\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n  using result_t                = decltype(std::declval<Hash>()(std::declval<cuda::std::int32_t>()));\n\n  thrust::device_vector<result_t> hash_values((materialize_result) ? num_keys : 1);\n\n  state.add_element_count(num_keys);\n\n  state.exec([&](nvbench::launch& launch) {\n    hash_bench_kernel<block_size, Key>\n      <<<grid_size, block_size, 0, launch.get_stream()>>>(Hash{}, num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\nstruct xxhash_32_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::xxhash_32>;\n};\n\nstruct xxhash_64_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::xxhash_64>;\n};\n\nstruct murmurhash3_32_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_32>;\n};\n\n#if _CCCL_HAS_INT128()\n\nstruct murmurhash3_x86_128_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_x86_128>;\n};\n\nstruct murmurhash3_x64_128_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_x64_128>;\n};\n\n#endif // _CCCL_HAS_INT128()\n\nNVBENCH_BENCH_TYPES(\n  hash_eval,\n  NVBENCH_TYPE_AXES(\n    nvbench::type_list<xxhash_32_tag,\n                       xxhash_64_tag,\n                       murmurhash3_32_tag\n#if _CCCL_HAS_INT128()\n                       ,\n                       murmurhash3_x86_128_tag,\n                       murmurhash3_x64_128_tag\n#endif // _CCCL_HAS_INT128()\n                       >,\n    nvbench::type_list<cuda::std::int32_t, large_key<4>, large_key<8>, large_key<16>, large_key<32>>))\n  .set_name(\"hash_function_eval\")\n  .set_type_axes_names({\"Hash\", \"Key\"})\n  .add_int64_power_of_two_axis(\"NumInputs\", nvbench::range(18, 26, 4));\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.\n// SPDX-License-Identifier: SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n\n#include <cuda/std/cstddef>\n#include <cuda/std/cstdint>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n\n#include <nvbench/nvbench.cuh>\n#include <nvbench/range.cuh>\n\nnamespace cudax = cuda::experimental;\n\n// repeat hash computation n times\nstatic constexpr auto n_repeats = 100;\n\ntemplate <cuda::std::int32_t Words>\nstruct large_key\n{\n  constexpr __host__ __device__ large_key(cuda::std::int32_t seed) noexcept\n  {\n    for (cuda::std::int32_t i = 0; i < Words; ++i)\n    {\n      data_[i] = seed;\n    }\n  }\n\nprivate:\n  cuda::std::int32_t data_[Words];\n};\n\ntemplate <cuda::std::int32_t BlockSize, typename Key, typename Hasher, typename OutputIt>\n__global__ void hash_bench_kernel(Hasher hash, size_t n, OutputIt out, bool materialize_result)\n{\n  size_t const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  size_t const loop_stride = gridDim.x * BlockSize;\n  size_t idx               = gid;\n  using result_t           = decltype(hash(0));\n\n  result_t agg{};\n\n  while (idx < n)\n  {\n    Key key(idx);\n    for (cuda::std::int32_t i = 0; i < n_repeats; ++i)\n    { // execute hash func n times\n      agg += hash(key);\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result)\n  {\n    out[gid] = agg;\n  }\n}\n\n// benchmark evaluating performance of various hash functions\ntemplate <typename HasherTag, typename Key>\nvoid hash_eval(nvbench::state& state, nvbench::type_list<HasherTag, Key>)\n{\n  using Hash = typename HasherTag::template fn<Key>;\n\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n  using result_t                = decltype(std::declval<Hash>()(std::declval<cuda::std::int32_t>()));\n\n  thrust::device_vector<result_t> hash_values((materialize_result) ? num_keys : 1);\n\n  state.add_element_count(num_keys);\n\n  state.exec([&](nvbench::launch& launch) {\n    hash_bench_kernel<block_size, Key>\n      <<<grid_size, block_size, 0, launch.get_stream()>>>(Hash{}, num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\nstruct xxhash_32_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::xxhash_32>;\n};\n\nstruct xxhash_64_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::xxhash_64>;\n};\n\nstruct murmurhash3_32_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_32>;\n};\n\n#if _CCCL_HAS_INT128()\n\nstruct murmurhash3_x86_128_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_x86_128>;\n};\n\nstruct murmurhash3_x64_128_tag\n{\n  template <typename Key>\n  using fn = cudax::cuco::hash<Key, cudax::cuco::hash_algorithm::murmurhash3_x64_128>;\n};\n\n#endif // _CCCL_HAS_INT128()\n\nNVBENCH_BENCH_TYPES(\n  hash_eval,\n  NVBENCH_TYPE_AXES(\n    nvbench::type_list<xxhash_32_tag,\n                       xxhash_64_tag,\n                       murmurhash3_32_tag\n#if _CCCL_HAS_INT128()\n                       ,\n                       murmurhash3_x86_128_tag,\n                       murmurhash3_x64_128_tag\n#endif // _CCCL_HAS_INT128()\n                       >,\n    nvbench::type_list<cuda::std::int32_t, large_key<4>, large_key<8>, large_key<16>, large_key<32>>))\n  .set_name(\"hash_function_eval\")\n  .set_type_axes_names({\"Hash\", \"Key\"})\n  .add_int64_power_of_two_axis(\"NumInputs\", nvbench::range(18, 26, 4));\n", "source": "/kaggle/working/cccl/cudax/benchmarks/bench/cuco/hashers.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/__driver/driver_api.h>\n#include <cuda/__runtime/ensure_current_context.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/kernel.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __global__ void kernel_ptx1(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n//\n// extern \"C\" __global__ void kernel_ptx2(float* array, int n)\n// {\n//   __shared__ float shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + static_cast<float>(const_data);\n//   }\n// }\n\nconstexpr char kernel_ptx_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel_ptx1\n.const .align 4 .u32 const_data;\n// _ZZ11kernel_ptx1E6shared has been demoted\n// _ZZ11kernel_ptx2E6shared has been demoted\n\n.visible .entry kernel_ptx1(\n\t.param .u64 kernel_ptx1_param_0,\n\t.param .u32 kernel_ptx1_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ11kernel_ptx1E6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_ptx1_param_0];\n\tld.param.u32 \t%r3, [kernel_ptx1_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ11kernel_ptx1E6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n\t// .globl\tkernel_ptx2\n.visible .entry kernel_ptx2(\n\t.param .u64 kernel_ptx2_param_0,\n\t.param .u32 kernel_ptx2_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .f32 \t%f<5>;\n\t.reg .b32 \t%r<10>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ11kernel_ptx2E6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_ptx2_param_0];\n\tld.param.u32 \t%r3, [kernel_ptx2_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB1_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.f32 \t%f1, [%rd4];\n\tshl.b32 \t%r6, %r1, 2;\n\tmov.u32 \t%r7, _ZZ11kernel_ptx2E6shared;\n\tadd.s32 \t%r8, %r7, %r6;\n\tst.shared.f32 \t[%r8], %f1;\n\tbar.sync \t0;\n\tld.const.u32 \t%r9, [const_data];\n\tcvt.rn.f32.s32 \t%f2, %r9;\n\tld.shared.f32 \t%f3, [%r8+4];\n\tadd.f32 \t%f4, %f3, %f2;\n\tst.global.f32 \t[%rd4], %f4;\n\n$L__BB1_2:\n\tret;\n\n}\n)\";\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\nextern \"C\" __global__ void kernel_rt(int* data, int size)\n{\n  const auto idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < size)\n  {\n    data[idx] += 1;\n  }\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\ntemplate <const auto& Attr, ::CUfunction_attribute ExpectedAttr, class ExpectedResult, class Signature>\n[[maybe_unused]] auto test_kernel_attribute(\n  cudax::kernel_ref<Signature> kernel,\n  cuda::device_ref dev,\n  cuda::std::optional<ExpectedResult> expected_value = cuda::std::nullopt)\n{\n  STATIC_REQUIRE(Attr == ExpectedAttr);\n\n  auto result = kernel.attribute(Attr, dev);\n  STATIC_REQUIRE(::cuda::std::is_same_v<decltype(result), ExpectedResult>);\n  if (expected_value.has_value())\n  {\n    CUDAX_REQUIRE(result == expected_value.value());\n  }\n  CUDAX_REQUIRE(result == Attr(kernel, dev));\n  return result;\n}\n\nC2H_CCCLRT_TEST(\"Kernel reference\", \"[kernel_ref]\")\n{\n  CUlibrary lib = ::cuda::__driver::__libraryLoadData(kernel_ptx_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  CUkernel kernel_ptx1_handle = ::cuda::__driver::__libraryGetKernel(lib, \"kernel_ptx1\");\n  CUkernel kernel_ptx2_handle = ::cuda::__driver::__libraryGetKernel(lib, \"kernel_ptx2\");\n\n  cuda::device_ref device{0};\n  cuda::__ensure_current_context context_guard{device};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::kernel_ref<void()>::value_type, CUkernel>);\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::kernel_ref<void()>>);\n  }\n\n  // Constructor from kernel handle\n  {\n    STATIC_REQUIRE(!cuda::std::is_convertible_v<CUkernel, cudax::kernel_ref<void()>>);\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::kernel_ref<void()>, CUkernel>);\n\n    // We currently have no way to check if the kernel parameters match\n    {\n      cudax::kernel_ref<void()> kernel_ref{kernel_ptx1_handle};\n      CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n\n      cudax::kernel_ref<void()> kernel_ref2{kernel_ptx2_handle};\n      CUDAX_REQUIRE(kernel_ptx2_handle == kernel_ref2.get());\n\n      CUDAX_REQUIRE(kernel_ptx1_handle != kernel_ptx2_handle);\n      CUDAX_REQUIRE(kernel_ref.get() != kernel_ref2.get());\n    }\n    {\n      cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n      CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n\n      cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ptx2_handle};\n      CUDAX_REQUIRE(kernel_ptx2_handle == kernel_ref2.get());\n\n      CUDAX_REQUIRE(kernel_ptx1_handle != kernel_ptx2_handle);\n      CUDAX_REQUIRE(kernel_ref.get() != kernel_ref2.get());\n    }\n  }\n\n  // Constructor from kernel function\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  {\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::kernel_ref<void(int*, int)>, decltype(kernel_rt)>);\n    STATIC_REQUIRE(cuda::std::is_convertible_v<decltype(kernel_rt), cudax::kernel_ref<void(int*, int)>>);\n    STATIC_REQUIRE(!cuda::std::is_constructible_v<cudax::kernel_ref<void()>, decltype(kernel_rt)>);\n\n    CUkernel kernel_rt_handle{};\n    CUDAX_REQUIRE(cudaGetKernel(&kernel_rt_handle, kernel_rt) == cudaSuccess);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_rt};\n    CUDAX_REQUIRE(kernel_rt_handle == kernel_ref1.get());\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_trivially_copy_constructible_v<cudax::kernel_ref<void()>>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref1.get());\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ref1};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref2.get());\n    CUDAX_REQUIRE(kernel_ref1.get() == kernel_ref2.get());\n  }\n\n  // Name\n#if _CCCL_CTK_AT_LEAST(12, 3)\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::kernel_ref<void()>>().name()), cuda::std::string_view>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ref.name() == \"kernel_ptx1\");\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 3)\n\n  // Attributes\n  {\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n\n    const auto cc = cuda::device_attributes::compute_capability(device);\n\n    test_kernel_attribute<cudax::kernel_attributes::max_threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, int>(\n      kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::shared_memory_size,\n                          CU_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device, sizeof(int) * 32);\n    test_kernel_attribute<cudax::kernel_attributes::const_memory_size,\n                          CU_FUNC_ATTRIBUTE_CONST_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device, sizeof(int));\n    test_kernel_attribute<cudax::kernel_attributes::local_memory_size,\n                          CU_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::num_regs, CU_FUNC_ATTRIBUTE_NUM_REGS, int>(kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::virtual_arch, CU_FUNC_ATTRIBUTE_PTX_VERSION, cuda::arch_id>(\n      kernel_ref, device, cuda::arch_id::sm_75);\n    test_kernel_attribute<cudax::kernel_attributes::binary_arch, CU_FUNC_ATTRIBUTE_BINARY_VERSION, cuda::arch_id>(\n      kernel_ref, device, cuda::to_arch_id(cc));\n    test_kernel_attribute<cudax::kernel_attributes::cache_mode_ca, CU_FUNC_ATTRIBUTE_CACHE_MODE_CA, bool>(\n      kernel_ref, device, false);\n    test_kernel_attribute<cudax::kernel_attributes::requires_cluster_dims,\n                          CU_FUNC_ATTRIBUTE_CLUSTER_SIZE_MUST_BE_SET,\n                          bool>(kernel_ref, device, false);\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::kernel_ref<void()>>().get()), CUkernel>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_ptx1_handle};\n    cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ptx2_handle};\n\n    CUDAX_REQUIRE(kernel_ref1 == kernel_ref1);\n    CUDAX_REQUIRE(kernel_ref1 != kernel_ref2);\n  }\n\n  // Deduction guidelines\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  {\n    cudax::kernel_ref kernel_ref1{kernel_rt};\n    CUDAX_REQUIRE((cuda::std::is_same_v<decltype(kernel_ref1), cudax::kernel_ref<void(int*, int)>>) );\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib) == cudaSuccess);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/__driver/driver_api.h>\n#include <cuda/__runtime/ensure_current_context.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/kernel.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __global__ void kernel_ptx1(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n//\n// extern \"C\" __global__ void kernel_ptx2(float* array, int n)\n// {\n//   __shared__ float shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + static_cast<float>(const_data);\n//   }\n// }\n\nconstexpr char kernel_ptx_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel_ptx1\n.const .align 4 .u32 const_data;\n// _ZZ11kernel_ptx1E6shared has been demoted\n// _ZZ11kernel_ptx2E6shared has been demoted\n\n.visible .entry kernel_ptx1(\n\t.param .u64 kernel_ptx1_param_0,\n\t.param .u32 kernel_ptx1_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ11kernel_ptx1E6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_ptx1_param_0];\n\tld.param.u32 \t%r3, [kernel_ptx1_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ11kernel_ptx1E6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n\t// .globl\tkernel_ptx2\n.visible .entry kernel_ptx2(\n\t.param .u64 kernel_ptx2_param_0,\n\t.param .u32 kernel_ptx2_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .f32 \t%f<5>;\n\t.reg .b32 \t%r<10>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ11kernel_ptx2E6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_ptx2_param_0];\n\tld.param.u32 \t%r3, [kernel_ptx2_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB1_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.f32 \t%f1, [%rd4];\n\tshl.b32 \t%r6, %r1, 2;\n\tmov.u32 \t%r7, _ZZ11kernel_ptx2E6shared;\n\tadd.s32 \t%r8, %r7, %r6;\n\tst.shared.f32 \t[%r8], %f1;\n\tbar.sync \t0;\n\tld.const.u32 \t%r9, [const_data];\n\tcvt.rn.f32.s32 \t%f2, %r9;\n\tld.shared.f32 \t%f3, [%r8+4];\n\tadd.f32 \t%f4, %f3, %f2;\n\tst.global.f32 \t[%rd4], %f4;\n\n$L__BB1_2:\n\tret;\n\n}\n)\";\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\nextern \"C\" __global__ void kernel_rt(int* data, int size)\n{\n  const auto idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < size)\n  {\n    data[idx] += 1;\n  }\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\ntemplate <const auto& Attr, ::hipFunction_attribute ExpectedAttr, class ExpectedResult, class Signature>\n[[maybe_unused]] auto test_kernel_attribute(\n  cudax::kernel_ref<Signature> kernel,\n  cuda::device_ref dev,\n  cuda::std::optional<ExpectedResult> expected_value = cuda::std::nullopt)\n{\n  STATIC_REQUIRE(Attr == ExpectedAttr);\n\n  auto result = kernel.attribute(Attr, dev);\n  STATIC_REQUIRE(::cuda::std::is_same_v<decltype(result), ExpectedResult>);\n  if (expected_value.has_value())\n  {\n    CUDAX_REQUIRE(result == expected_value.value());\n  }\n  CUDAX_REQUIRE(result == Attr(kernel, dev));\n  return result;\n}\n\nC2H_CCCLRT_TEST(\"Kernel reference\", \"[kernel_ref]\")\n{\n  hipLibrary_t lib = ::cuda::__driver::__libraryLoadData(kernel_ptx_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  hipKernel_t kernel_ptx1_handle = ::cuda::__driver::__libraryGetKernel(lib, \"kernel_ptx1\");\n  hipKernel_t kernel_ptx2_handle = ::cuda::__driver::__libraryGetKernel(lib, \"kernel_ptx2\");\n\n  cuda::device_ref device{0};\n  cuda::__ensure_current_context context_guard{device};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::kernel_ref<void()>::value_type, hipKernel_t>);\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::kernel_ref<void()>>);\n  }\n\n  // Constructor from kernel handle\n  {\n    STATIC_REQUIRE(!cuda::std::is_convertible_v<hipKernel_t, cudax::kernel_ref<void()>>);\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::kernel_ref<void()>, hipKernel_t>);\n\n    // We currently have no way to check if the kernel parameters match\n    {\n      cudax::kernel_ref<void()> kernel_ref{kernel_ptx1_handle};\n      CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n\n      cudax::kernel_ref<void()> kernel_ref2{kernel_ptx2_handle};\n      CUDAX_REQUIRE(kernel_ptx2_handle == kernel_ref2.get());\n\n      CUDAX_REQUIRE(kernel_ptx1_handle != kernel_ptx2_handle);\n      CUDAX_REQUIRE(kernel_ref.get() != kernel_ref2.get());\n    }\n    {\n      cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n      CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n\n      cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ptx2_handle};\n      CUDAX_REQUIRE(kernel_ptx2_handle == kernel_ref2.get());\n\n      CUDAX_REQUIRE(kernel_ptx1_handle != kernel_ptx2_handle);\n      CUDAX_REQUIRE(kernel_ref.get() != kernel_ref2.get());\n    }\n  }\n\n  // Constructor from kernel function\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  {\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::kernel_ref<void(int*, int)>, decltype(kernel_rt)>);\n    STATIC_REQUIRE(cuda::std::is_convertible_v<decltype(kernel_rt), cudax::kernel_ref<void(int*, int)>>);\n    STATIC_REQUIRE(!cuda::std::is_constructible_v<cudax::kernel_ref<void()>, decltype(kernel_rt)>);\n\n    hipKernel_t kernel_rt_handle{};\n    CUDAX_REQUIRE(cudaGetKernel(&kernel_rt_handle, kernel_rt) == hipSuccess);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_rt};\n    CUDAX_REQUIRE(kernel_rt_handle == kernel_ref1.get());\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_trivially_copy_constructible_v<cudax::kernel_ref<void()>>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref1.get());\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ref1};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref2.get());\n    CUDAX_REQUIRE(kernel_ref1.get() == kernel_ref2.get());\n  }\n\n  // Name\n#if _CCCL_CTK_AT_LEAST(12, 3)\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::kernel_ref<void()>>().name()), cuda::std::string_view>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ref.name() == \"kernel_ptx1\");\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 3)\n\n  // Attributes\n  {\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n\n    const auto cc = cuda::device_attributes::compute_capability(device);\n\n    test_kernel_attribute<cudax::kernel_attributes::max_threads_per_block, HIP_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, int>(\n      kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::shared_memory_size,\n                          HIP_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device, sizeof(int) * 32);\n    test_kernel_attribute<cudax::kernel_attributes::const_memory_size,\n                          HIP_FUNC_ATTRIBUTE_CONST_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device, sizeof(int));\n    test_kernel_attribute<cudax::kernel_attributes::local_memory_size,\n                          HIP_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES,\n                          cuda::std::size_t>(kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::num_regs, HIP_FUNC_ATTRIBUTE_NUM_REGS, int>(kernel_ref, device);\n    test_kernel_attribute<cudax::kernel_attributes::virtual_arch, HIP_FUNC_ATTRIBUTE_PTX_VERSION, cuda::arch_id>(\n      kernel_ref, device, cuda::arch_id::sm_75);\n    test_kernel_attribute<cudax::kernel_attributes::binary_arch, HIP_FUNC_ATTRIBUTE_BINARY_VERSION, cuda::arch_id>(\n      kernel_ref, device, cuda::to_arch_id(cc));\n    test_kernel_attribute<cudax::kernel_attributes::cache_mode_ca, HIP_FUNC_ATTRIBUTE_CACHE_MODE_CA, bool>(\n      kernel_ref, device, false);\n    test_kernel_attribute<cudax::kernel_attributes::requires_cluster_dims,\n                          CU_FUNC_ATTRIBUTE_CLUSTER_SIZE_MUST_BE_SET,\n                          bool>(kernel_ref, device, false);\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::kernel_ref<void()>>().get()), hipKernel_t>);\n\n    cudax::kernel_ref<void(int*, int)> kernel_ref{kernel_ptx1_handle};\n    CUDAX_REQUIRE(kernel_ptx1_handle == kernel_ref.get());\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::kernel_ref<void(int*, int)> kernel_ref1{kernel_ptx1_handle};\n    cudax::kernel_ref<void(int*, int)> kernel_ref2{kernel_ptx2_handle};\n\n    CUDAX_REQUIRE(kernel_ref1 == kernel_ref1);\n    CUDAX_REQUIRE(kernel_ref1 != kernel_ref2);\n  }\n\n  // Deduction guidelines\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  {\n    cudax::kernel_ref kernel_ref1{kernel_rt};\n    CUDAX_REQUIRE((cuda::std::is_same_v<decltype(kernel_ref1), cudax::kernel_ref<void(int*, int)>>) );\n  }\n#endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib) == hipSuccess);\n}\n", "source": "/kaggle/working/cccl/cudax/test/kernel/kernel_ref.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <cuda/functional>\n#include <cuda/std/cstddef>\n#include <cuda/std/span>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n#include <cuda/experimental/__cuco/hyperloglog.cuh>\n#include <cuda/experimental/__cuco/hyperloglog_ref.cuh>\n\n#include <cooperative_groups.h>\n#include <testing.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\nnamespace cudax = cuda::experimental;\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void estimate_kernel(typename Ref::sketch_size_kb sketch_size_kb, InputIt in, size_t n, OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  const auto block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0)\n  {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads())\n    {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    const auto estimate = estimator.estimate(block);\n    if (block.thread_rank() == 0)\n    {\n      *out = estimate;\n    }\n  }\n}\n\nusing test_types = c2h::type_list<int32_t, int64_t>;\n\nC2H_TEST(\"HyperLogLog device ref\", \"[hyperloglog]\", test_types)\n{\n  using T              = c2h::get<0, TestType>;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  // Test parameters\n  const std::size_t num_items_pow2 = GENERATE(25, 26, 28);\n  const int hll_precision          = GENERATE(8, 10, 12, 13);\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n  const std::size_t num_items = 1ull << num_items_pow2;\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  // Initialize the estimator\n  estimator_type estimator{sketch_size_kb};\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  const auto host_estimate = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  estimate_kernel<typename estimator_type::template ref_type<cuda::thread_scope_block>>\n    <<<1, 512, estimator.sketch_bytes()>>>(sketch_size_kb, items.begin(), num_items, device_estimate.begin());\n\n  REQUIRE(cudaDeviceSynchronize() == cudaSuccess);\n\n  std::size_t device_estimate_value = device_estimate[0];\n  REQUIRE(device_estimate_value == host_estimate);\n}\n\nC2H_TEST(\"HyperLogLog unique sequence\", \"[hyperloglog]\", test_types)\n{\n  using T              = c2h::get<0, TestType>;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const std::size_t num_items_pow2 = GENERATE(25, 26, 28);\n  const int hll_precision          = GENERATE(8, 10, 12, 13, 18);\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n  const std::size_t num_items = 1ull << num_items_pow2;\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  // This factor determines the error threshold for passing the test\n  constexpr double tolerance_factor = 2.5;\n  // RSD for a given precision is given by the following formula\n  const double relative_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << hll_precision));\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  // Initialize the estimator\n  estimator_type estimator{sketch_size_kb};\n\n  REQUIRE(estimator.estimate() == 0);\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  const auto estimate = estimator.estimate();\n\n  // Adding the same items again should not affect the result\n  estimator.add(items.begin(), items.begin() + num_items / 2);\n  REQUIRE(estimator.estimate() == estimate);\n\n  // Adding the same items again (might use shared memory code path) should not affect the result\n  auto* ptr = thrust::raw_pointer_cast(items.data());\n  estimator.add(ptr, ptr + num_items / 2);\n  REQUIRE(estimator.estimate() == estimate);\n\n  // Clearing the estimator should reset the estimate\n  estimator.clear();\n  REQUIRE(estimator.estimate() == 0);\n\n  const double relative_error = std::abs((static_cast<double>(estimate) / static_cast<double>(num_items)) - 1.0);\n\n  // Check if the error is acceptable\n  REQUIRE(relative_error < tolerance_factor * relative_standard_deviation);\n}\n\n//! @brief The following unit tests mimic Spark's unit tests which can be found here:\n//! https://github.com/apache/spark/blob/d10dbaa31a44878df5c7e144f111e18261346531/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/aggregate/HyperLogLogPlusPlusSuite.scala\n//!\n\nC2H_TEST(\"HyperLogLog Spark parity deterministic\", \"[hyperloglog]\")\n{\n  using T              = int;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  constexpr std::size_t repeats = 10;\n  // This factor determines the error threshold for passing the test\n  constexpr double tolerance_factor = 3.0;\n  const auto num_items              = GENERATE(100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000);\n  const auto standard_deviation     = GENERATE(0.1, 0.05, 0.025, 0.01, 0.005, 0.0025);\n\n  const auto expected_hll_precision =\n    std::max(static_cast<int32_t>(4),\n             static_cast<int32_t>(std::ceil(2.0 * std::log(1.106 / standard_deviation) / std::log(2.0))));\n  const auto expected_sketch_bytes = 4 * (1ull << expected_hll_precision);\n\n  CAPTURE(num_items, standard_deviation, expected_hll_precision, expected_sketch_bytes);\n\n  const estimator_type::standard_deviation sd(standard_deviation);\n  const estimator_type::sketch_size_kb sb(expected_sketch_bytes / 1024.0);\n\n  // Validate sketch size calculation\n  REQUIRE(estimator_type::sketch_bytes(sd) >= 64);\n  REQUIRE(estimator_type::sketch_bytes(sd) == expected_sketch_bytes);\n  REQUIRE(estimator_type::sketch_bytes(sd) == estimator_type::sketch_bytes(sb));\n\n  auto items_begin = thrust::make_transform_iterator(\n    thrust::make_counting_iterator<std::size_t>(0), cuda::proclaim_return_type<T>([repeats] __device__(auto i) {\n      return static_cast<T>(i / repeats);\n    }));\n\n  estimator_type estimator{sd};\n\n  REQUIRE(estimator.estimate() == 0);\n\n  // Add all items to the estimator\n  estimator.add(items_begin, items_begin + num_items);\n\n  const auto estimate = estimator.estimate();\n\n  const double relative_error =\n    std::abs((static_cast<double>(estimate) / static_cast<double>(num_items / repeats)) - 1.0);\n  // RSD for a given precision is given by the following formula\n  const double expected_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << expected_hll_precision));\n\n  // Check if the error is acceptable\n  REQUIRE(relative_error < expected_standard_deviation * tolerance_factor);\n}\n\nC2H_TEST(\"HyperLogLog precision constructor\", \"[hyperloglog]\")\n{\n  using T              = int;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const auto precision_value = GENERATE(4, 6, 8, 12, 16, 18);\n\n  const estimator_type::precision precision(precision_value);\n  const auto expected_sketch_bytes = 4 * (1ull << precision_value);\n\n  CAPTURE(precision_value, expected_sketch_bytes);\n\n  REQUIRE(estimator_type::sketch_bytes(precision) == expected_sketch_bytes);\n\n  estimator_type estimator{precision};\n\n  REQUIRE(estimator.sketch_bytes() == expected_sketch_bytes);\n  REQUIRE(estimator.estimate() == 0);\n}\n\n#if _CCCL_CTK_AT_LEAST(12, 9) // Pinned memory resource is only supported with CTK 12.9 and later\nC2H_TEST(\"Hyperloglog estimate works with pinned memory pool\", \"[hyperloglog]\")\n{\n  using T              = int32_t;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const std::size_t num_items = 1 << 20;\n  const int hll_precision     = 12;\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  constexpr double tolerance_factor        = 2.5;\n  const double relative_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << hll_precision));\n\n  thrust::device_vector<T> items(num_items);\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  estimator_type estimator{sketch_size_kb};\n  estimator.add(items.begin(), items.end());\n\n  auto host_mr        = ::cuda::pinned_default_memory_pool();\n  const auto estimate = estimator.estimate(host_mr);\n\n  const double relative_error = std::abs((static_cast<double>(estimate) / static_cast<double>(num_items)) - 1.0);\n\n  REQUIRE(relative_error < tolerance_factor * relative_standard_deviation);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 9)\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <cuda/functional>\n#include <cuda/std/cstddef>\n#include <cuda/std/span>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n#include <cuda/experimental/__cuco/hyperloglog.cuh>\n#include <cuda/experimental/__cuco/hyperloglog_ref.cuh>\n\n#include <hip/hip_cooperative_groups.h>\n#include <testing.cuh>\n\n#include <c2h/catch2_test_helper.h>\n\nnamespace cudax = cuda::experimental;\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void estimate_kernel(typename Ref::sketch_size_kb sketch_size_kb, InputIt in, size_t n, OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  const auto block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0)\n  {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads())\n    {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    const auto estimate = estimator.estimate(block);\n    if (block.thread_rank() == 0)\n    {\n      *out = estimate;\n    }\n  }\n}\n\nusing test_types = c2h::type_list<int32_t, int64_t>;\n\nC2H_TEST(\"HyperLogLog device ref\", \"[hyperloglog]\", test_types)\n{\n  using T              = c2h::get<0, TestType>;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  // Test parameters\n  const std::size_t num_items_pow2 = GENERATE(25, 26, 28);\n  const int hll_precision          = GENERATE(8, 10, 12, 13);\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n  const std::size_t num_items = 1ull << num_items_pow2;\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  // Initialize the estimator\n  estimator_type estimator{sketch_size_kb};\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  const auto host_estimate = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  estimate_kernel<typename estimator_type::template ref_type<cuda::thread_scope_block>>\n    <<<1, 512, estimator.sketch_bytes()>>>(sketch_size_kb, items.begin(), num_items, device_estimate.begin());\n\n  REQUIRE(hipDeviceSynchronize() == hipSuccess);\n\n  std::size_t device_estimate_value = device_estimate[0];\n  REQUIRE(device_estimate_value == host_estimate);\n}\n\nC2H_TEST(\"HyperLogLog unique sequence\", \"[hyperloglog]\", test_types)\n{\n  using T              = c2h::get<0, TestType>;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const std::size_t num_items_pow2 = GENERATE(25, 26, 28);\n  const int hll_precision          = GENERATE(8, 10, 12, 13, 18);\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n  const std::size_t num_items = 1ull << num_items_pow2;\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  // This factor determines the error threshold for passing the test\n  constexpr double tolerance_factor = 2.5;\n  // RSD for a given precision is given by the following formula\n  const double relative_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << hll_precision));\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  // Initialize the estimator\n  estimator_type estimator{sketch_size_kb};\n\n  REQUIRE(estimator.estimate() == 0);\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  const auto estimate = estimator.estimate();\n\n  // Adding the same items again should not affect the result\n  estimator.add(items.begin(), items.begin() + num_items / 2);\n  REQUIRE(estimator.estimate() == estimate);\n\n  // Adding the same items again (might use shared memory code path) should not affect the result\n  auto* ptr = thrust::raw_pointer_cast(items.data());\n  estimator.add(ptr, ptr + num_items / 2);\n  REQUIRE(estimator.estimate() == estimate);\n\n  // Clearing the estimator should reset the estimate\n  estimator.clear();\n  REQUIRE(estimator.estimate() == 0);\n\n  const double relative_error = std::abs((static_cast<double>(estimate) / static_cast<double>(num_items)) - 1.0);\n\n  // Check if the error is acceptable\n  REQUIRE(relative_error < tolerance_factor * relative_standard_deviation);\n}\n\n//! @brief The following unit tests mimic Spark's unit tests which can be found here:\n//! https://github.com/apache/spark/blob/d10dbaa31a44878df5c7e144f111e18261346531/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/aggregate/HyperLogLogPlusPlusSuite.scala\n//!\n\nC2H_TEST(\"HyperLogLog Spark parity deterministic\", \"[hyperloglog]\")\n{\n  using T              = int;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  constexpr std::size_t repeats = 10;\n  // This factor determines the error threshold for passing the test\n  constexpr double tolerance_factor = 3.0;\n  const auto num_items              = GENERATE(100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000);\n  const auto standard_deviation     = GENERATE(0.1, 0.05, 0.025, 0.01, 0.005, 0.0025);\n\n  const auto expected_hll_precision =\n    std::max(static_cast<int32_t>(4),\n             static_cast<int32_t>(std::ceil(2.0 * std::log(1.106 / standard_deviation) / std::log(2.0))));\n  const auto expected_sketch_bytes = 4 * (1ull << expected_hll_precision);\n\n  CAPTURE(num_items, standard_deviation, expected_hll_precision, expected_sketch_bytes);\n\n  const estimator_type::standard_deviation sd(standard_deviation);\n  const estimator_type::sketch_size_kb sb(expected_sketch_bytes / 1024.0);\n\n  // Validate sketch size calculation\n  REQUIRE(estimator_type::sketch_bytes(sd) >= 64);\n  REQUIRE(estimator_type::sketch_bytes(sd) == expected_sketch_bytes);\n  REQUIRE(estimator_type::sketch_bytes(sd) == estimator_type::sketch_bytes(sb));\n\n  auto items_begin = thrust::make_transform_iterator(\n    thrust::make_counting_iterator<std::size_t>(0), cuda::proclaim_return_type<T>([repeats] __device__(auto i) {\n      return static_cast<T>(i / repeats);\n    }));\n\n  estimator_type estimator{sd};\n\n  REQUIRE(estimator.estimate() == 0);\n\n  // Add all items to the estimator\n  estimator.add(items_begin, items_begin + num_items);\n\n  const auto estimate = estimator.estimate();\n\n  const double relative_error =\n    std::abs((static_cast<double>(estimate) / static_cast<double>(num_items / repeats)) - 1.0);\n  // RSD for a given precision is given by the following formula\n  const double expected_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << expected_hll_precision));\n\n  // Check if the error is acceptable\n  REQUIRE(relative_error < expected_standard_deviation * tolerance_factor);\n}\n\nC2H_TEST(\"HyperLogLog precision constructor\", \"[hyperloglog]\")\n{\n  using T              = int;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const auto precision_value = GENERATE(4, 6, 8, 12, 16, 18);\n\n  const estimator_type::precision precision(precision_value);\n  const auto expected_sketch_bytes = 4 * (1ull << precision_value);\n\n  CAPTURE(precision_value, expected_sketch_bytes);\n\n  REQUIRE(estimator_type::sketch_bytes(precision) == expected_sketch_bytes);\n\n  estimator_type estimator{precision};\n\n  REQUIRE(estimator.sketch_bytes() == expected_sketch_bytes);\n  REQUIRE(estimator.estimate() == 0);\n}\n\n#if _CCCL_CTK_AT_LEAST(12, 9) // Pinned memory resource is only supported with CTK 12.9 and later\nC2H_TEST(\"Hyperloglog estimate works with pinned memory pool\", \"[hyperloglog]\")\n{\n  using T              = int32_t;\n  using estimator_type = cudax::cuco::hyperloglog<T>;\n\n  const std::size_t num_items = 1 << 20;\n  const int hll_precision     = 12;\n  const typename estimator_type::sketch_size_kb sketch_size_kb(4.0 * (1ull << hll_precision) / 1024.0);\n\n  CAPTURE(num_items, hll_precision, sketch_size_kb);\n\n  constexpr double tolerance_factor        = 2.5;\n  const double relative_standard_deviation = 1.04 / std::sqrt(static_cast<double>(1ull << hll_precision));\n\n  thrust::device_vector<T> items(num_items);\n  thrust::sequence(items.begin(), items.end(), T{0});\n\n  estimator_type estimator{sketch_size_kb};\n  estimator.add(items.begin(), items.end());\n\n  auto host_mr        = ::cuda::pinned_default_memory_pool();\n  const auto estimate = estimator.estimate(host_mr);\n\n  const double relative_error = std::abs((static_cast<double>(estimate) / static_cast<double>(num_items)) - 1.0);\n\n  REQUIRE(relative_error < tolerance_factor * relative_standard_deviation);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 9)\n", "source": "/kaggle/working/cccl/cudax/test/cuco/hyperloglog/test_hyperloglog.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <thrust/detail/raw_pointer_cast.h>\n\n#include <cuda/std/array>\n#include <cuda/std/cstddef>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/span>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n\n#include <testing.cuh>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <int32_t Words>\nstruct large_key\n{\n  constexpr _CCCL_HOST_DEVICE large_key(int32_t value) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i)\n    {\n      data_[i] = value;\n    }\n  }\n\nprivate:\n  int32_t data_[Words];\n};\n\ntemplate <cudax::cuco::hash_algorithm Algorithm>\nstruct hash_test\n{\n  template <typename Key, typename ResultT, typename... HashConstructorArgs>\n  _CCCL_HOST_DEVICE void\n  operator()(Key const& key, ResultT expected, HashConstructorArgs&&... hash_constructor_args) noexcept\n  {\n    cudax::cuco::hash<Key, Algorithm> hasher(::cuda::std::forward<HashConstructorArgs>(hash_constructor_args)...);\n\n    cuda::std::array<Key, 1> arr_keys = {key};\n\n    CUDAX_REQUIRE(hasher(key) == expected);\n    CUDAX_REQUIRE(hasher(cuda::std::span<Key>(thrust::raw_pointer_cast(arr_keys.data()), arr_keys.size())) == expected);\n  }\n};\n\nstruct test_xxhash32\n{\n  hash_test<cudax::cuco::hash_algorithm::xxhash_32> xxhash32_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    xxhash32_test(static_cast<char>(0), 3479547966u, 0);\n    xxhash32_test(static_cast<char>(42), 3774771295u, 0);\n    xxhash32_test(static_cast<char>(0), 2099223482u, 42);\n    xxhash32_test(static_cast<int32_t>(0), 148298089u, 0);\n    xxhash32_test(static_cast<int32_t>(0), 2132181312u, 42);\n    xxhash32_test(static_cast<int32_t>(42), 1161967057u, 0);\n    xxhash32_test(static_cast<int32_t>(123456789), 2987034094u, 0);\n    xxhash32_test(static_cast<int64_t>(0), 3736311059u, 0);\n    xxhash32_test(static_cast<int64_t>(0), 1076387279u, 42);\n    xxhash32_test(static_cast<int64_t>(42), 2332451213u, 0);\n    xxhash32_test(static_cast<int64_t>(123456789), 1561711919u, 0);\n#if _CCCL_HAS_INT128()\n    xxhash32_test(static_cast<__int128_t>(123456789), 1846633701u, 0);\n#endif\n    xxhash32_test(large_key<32>(123456789), 3715432378u, 0);\n  }\n};\n\nstruct test_xxhash64\n{\n  hash_test<cudax::cuco::hash_algorithm::xxhash_64> xxhash64_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    xxhash64_test(static_cast<char>(0), 16804241149081757544ull, 0);\n    xxhash64_test(static_cast<char>(42), 765293966243412708ull, 0);\n    xxhash64_test(static_cast<char>(0), 9486749600008296231ull, 42);\n    xxhash64_test(static_cast<int32_t>(0), 4246796580750024372ull, 0);\n    xxhash64_test(static_cast<int32_t>(0), 3614696996920510707ull, 42);\n    xxhash64_test(static_cast<int32_t>(42), 15516826743637085169ull, 0);\n    xxhash64_test(static_cast<int32_t>(123456789), 9462334144942111946ull, 0);\n    xxhash64_test(static_cast<int64_t>(0), 3803688792395291579ull, 0);\n    xxhash64_test(static_cast<int64_t>(0), 13194218611613725804ull, 42);\n    xxhash64_test(static_cast<int64_t>(42), 13066772586158965587ull, 0);\n    xxhash64_test(static_cast<int64_t>(123456789), 14662639848940634189ull, 0);\n#if _CCCL_HAS_INT128()\n    xxhash64_test(static_cast<__int128_t>(123456789), 7986913354431084250ull, 0);\n#endif\n    xxhash64_test(large_key<32>(123456789), 2031761887105658523ull, 0);\n  }\n};\n\nstruct test_murmurhash3_32\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_32> murmurhash3_32_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_32_test(static_cast<char>(0), 1364076727u, 0);\n    murmurhash3_32_test(static_cast<char>(42), 338914844u, 0);\n    murmurhash3_32_test(static_cast<char>(0), 3712240066u, 42);\n    murmurhash3_32_test(static_cast<int32_t>(0), 593689054u, 0);\n    murmurhash3_32_test(static_cast<int32_t>(0), 933211791u, 42);\n    murmurhash3_32_test(static_cast<int32_t>(42), 3160117731u, 0);\n    murmurhash3_32_test(static_cast<int32_t>(123456789), 3206620847u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(0), 1669671676u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(0), 2624043101u, 42);\n    murmurhash3_32_test(static_cast<int64_t>(42), 1871679806u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(123456789), 690028081u, 0);\n#if _CCCL_HAS_INT128()\n    murmurhash3_32_test(static_cast<__int128_t>(123456789), 2191144977u, 0);\n#endif\n    murmurhash3_32_test(large_key<32>(123456789), 2555553099u, 0);\n  }\n};\n\n#if _CCCL_HAS_INT128()\nstruct test_murmurhash3_x86_128\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_x86_128> murmurhash3_x86_128_test;\n\n  _CCCL_HOST_DEVICE __uint128_t conv(cuda::std::array<uint32_t, 4> const& arr) const\n  {\n    return cuda::std::bit_cast<__uint128_t>(arr);\n  }\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_x86_128_test(int32_t(0), conv({3422973727u, 2656139328u, 2656139328u, 2656139328u}), 0);\n    murmurhash3_x86_128_test(int32_t(9), conv({2808089785u, 314604614u, 314604614u, 314604614u}), 0);\n    murmurhash3_x86_128_test(int32_t(42), conv({3611919118u, 1962256489u, 1962256489u, 1962256489u}), 0);\n    murmurhash3_x86_128_test(int32_t(42), conv({3399017053u, 732469929u, 732469929u, 732469929u}), 42);\n\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 2>{2, 2}, conv({1234494082u, 1431451587u, 431049201u, 431049201u}), 0);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 3>{1, 4, 9}, conv({2516796247u, 2757675829u, 778406919u, 2453259553u}), 42);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 4>{42, 64, 108, 1024}, conv({2686265656u, 591236665u, 3797082165u, 2731908938u}), 63);\n    murmurhash3_x86_128_test(cuda::std::array<int32_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({3918256832u, 4205523739u, 1707810111u, 1625952473u}),\n                             1024);\n\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 2>{2, 2}, conv({3811075945u, 727160712u, 3510740342u, 235225510u}), 0);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 3>{1, 4, 9}, conv({2817194959u, 206796677u, 3391242768u, 248681098u}), 42);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 4>{42, 64, 108, 1024}, conv({2335912146u, 1566515912u, 760710030u, 452077451u}), 63);\n    murmurhash3_x86_128_test(cuda::std::array<int64_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({1101169764u, 1758958147u, 2406511780u, 2903571412u}),\n                             1024);\n  }\n};\n\nstruct test_murmurhash3_x64_128\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_x64_128> murmurhash3_x64_128_test;\n\n  _CCCL_HOST_DEVICE __uint128_t conv(cuda::std::array<uint64_t, 2> const& arr) const\n  {\n    return cuda::std::bit_cast<__uint128_t>(arr);\n  }\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_x64_128_test(int32_t(0), conv({14961230494313510588ull, 6383328099726337777ull}), 0);\n    murmurhash3_x64_128_test(int32_t(9), conv({1779292183511753683ull, 16298496441448380334ull}), 0);\n    murmurhash3_x64_128_test(int32_t(42), conv({2913627637088662735ull, 16344193523890567190ull}), 0);\n    murmurhash3_x64_128_test(int32_t(42), conv({2248879576374326886ull, 18006515275339376488ull}), 42);\n\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 2>{2, 2}, conv({12221386834995143465ull, 6690950894782946573ull}), 0);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 3>{1, 4, 9}, conv({299140022350411792ull, 9891903873182035274ull}), 42);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 4>{42, 64, 108, 1024}, conv({4333511168876981289ull, 4659486988434316416ull}), 63);\n    murmurhash3_x64_128_test(cuda::std::array<int32_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({3302412811061286680ull, 7070355726356610672ull}),\n                             1024);\n\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 2>{2, 2}, conv({8554944597931919519ull, 14938998000509429729ull}), 0);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 3>{1, 4, 9}, conv({13442629947720186435ull, 7061727494178573325ull}), 42);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 4>{42, 64, 108, 1024}, conv({8786399719555989948ull, 14954183901757012458ull}), 63);\n    murmurhash3_x64_128_test(cuda::std::array<int64_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({15409921801541329777ull, 10546487400963404004ull}),\n                             1024);\n  }\n};\n#endif // _CCCL_HAS_INT128()\n\ntemplate <typename TestFn>\n__global__ void test_hasher_kernel(TestFn test_fn)\n{\n  test_fn();\n}\n\ntemplate <typename TestFn>\nvoid test_hasher_on_device(TestFn test_fn)\n{\n  test_hasher_kernel<<<1, 1>>>(test_fn);\n  CUDAX_REQUIRE(cudaDeviceSynchronize() == cudaSuccess);\n}\n\nTEST_CASE(\"Test Hasher's on host and device\", \"\")\n{\n  SECTION(\"host-generated hash values match the reference implementation.\")\n  {\n    test_xxhash32{}();\n    test_xxhash64{}();\n    test_murmurhash3_32{}();\n#if _CCCL_HAS_INT128()\n    test_murmurhash3_x86_128{}();\n    test_murmurhash3_x64_128{}();\n#endif // _CCCL_HAS_INT128()\n  }\n\n  SECTION(\"device-generated hash values match the reference implementation.\")\n  {\n    test_hasher_on_device(test_xxhash32{});\n    test_hasher_on_device(test_xxhash64{});\n    test_hasher_on_device(test_murmurhash3_32{});\n#if _CCCL_HAS_INT128()\n    test_hasher_on_device(test_murmurhash3_x86_128{});\n    test_hasher_on_device(test_murmurhash3_x64_128{});\n#endif // _CCCL_HAS_INT128()\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/detail/raw_pointer_cast.h>\n\n#include <cuda/std/array>\n#include <cuda/std/cstddef>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <cuda/std/span>\n\n#include <cuda/experimental/__cuco/hash_functions.cuh>\n\n#include <testing.cuh>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <int32_t Words>\nstruct large_key\n{\n  constexpr _CCCL_HOST_DEVICE large_key(int32_t value) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i)\n    {\n      data_[i] = value;\n    }\n  }\n\nprivate:\n  int32_t data_[Words];\n};\n\ntemplate <cudax::cuco::hash_algorithm Algorithm>\nstruct hash_test\n{\n  template <typename Key, typename ResultT, typename... HashConstructorArgs>\n  _CCCL_HOST_DEVICE void\n  operator()(Key const& key, ResultT expected, HashConstructorArgs&&... hash_constructor_args) noexcept\n  {\n    cudax::cuco::hash<Key, Algorithm> hasher(::cuda::std::forward<HashConstructorArgs>(hash_constructor_args)...);\n\n    cuda::std::array<Key, 1> arr_keys = {key};\n\n    CUDAX_REQUIRE(hasher(key) == expected);\n    CUDAX_REQUIRE(hasher(cuda::std::span<Key>(thrust::raw_pointer_cast(arr_keys.data()), arr_keys.size())) == expected);\n  }\n};\n\nstruct test_xxhash32\n{\n  hash_test<cudax::cuco::hash_algorithm::xxhash_32> xxhash32_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    xxhash32_test(static_cast<char>(0), 3479547966u, 0);\n    xxhash32_test(static_cast<char>(42), 3774771295u, 0);\n    xxhash32_test(static_cast<char>(0), 2099223482u, 42);\n    xxhash32_test(static_cast<int32_t>(0), 148298089u, 0);\n    xxhash32_test(static_cast<int32_t>(0), 2132181312u, 42);\n    xxhash32_test(static_cast<int32_t>(42), 1161967057u, 0);\n    xxhash32_test(static_cast<int32_t>(123456789), 2987034094u, 0);\n    xxhash32_test(static_cast<int64_t>(0), 3736311059u, 0);\n    xxhash32_test(static_cast<int64_t>(0), 1076387279u, 42);\n    xxhash32_test(static_cast<int64_t>(42), 2332451213u, 0);\n    xxhash32_test(static_cast<int64_t>(123456789), 1561711919u, 0);\n#if _CCCL_HAS_INT128()\n    xxhash32_test(static_cast<__int128_t>(123456789), 1846633701u, 0);\n#endif\n    xxhash32_test(large_key<32>(123456789), 3715432378u, 0);\n  }\n};\n\nstruct test_xxhash64\n{\n  hash_test<cudax::cuco::hash_algorithm::xxhash_64> xxhash64_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    xxhash64_test(static_cast<char>(0), 16804241149081757544ull, 0);\n    xxhash64_test(static_cast<char>(42), 765293966243412708ull, 0);\n    xxhash64_test(static_cast<char>(0), 9486749600008296231ull, 42);\n    xxhash64_test(static_cast<int32_t>(0), 4246796580750024372ull, 0);\n    xxhash64_test(static_cast<int32_t>(0), 3614696996920510707ull, 42);\n    xxhash64_test(static_cast<int32_t>(42), 15516826743637085169ull, 0);\n    xxhash64_test(static_cast<int32_t>(123456789), 9462334144942111946ull, 0);\n    xxhash64_test(static_cast<int64_t>(0), 3803688792395291579ull, 0);\n    xxhash64_test(static_cast<int64_t>(0), 13194218611613725804ull, 42);\n    xxhash64_test(static_cast<int64_t>(42), 13066772586158965587ull, 0);\n    xxhash64_test(static_cast<int64_t>(123456789), 14662639848940634189ull, 0);\n#if _CCCL_HAS_INT128()\n    xxhash64_test(static_cast<__int128_t>(123456789), 7986913354431084250ull, 0);\n#endif\n    xxhash64_test(large_key<32>(123456789), 2031761887105658523ull, 0);\n  }\n};\n\nstruct test_murmurhash3_32\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_32> murmurhash3_32_test;\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_32_test(static_cast<char>(0), 1364076727u, 0);\n    murmurhash3_32_test(static_cast<char>(42), 338914844u, 0);\n    murmurhash3_32_test(static_cast<char>(0), 3712240066u, 42);\n    murmurhash3_32_test(static_cast<int32_t>(0), 593689054u, 0);\n    murmurhash3_32_test(static_cast<int32_t>(0), 933211791u, 42);\n    murmurhash3_32_test(static_cast<int32_t>(42), 3160117731u, 0);\n    murmurhash3_32_test(static_cast<int32_t>(123456789), 3206620847u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(0), 1669671676u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(0), 2624043101u, 42);\n    murmurhash3_32_test(static_cast<int64_t>(42), 1871679806u, 0);\n    murmurhash3_32_test(static_cast<int64_t>(123456789), 690028081u, 0);\n#if _CCCL_HAS_INT128()\n    murmurhash3_32_test(static_cast<__int128_t>(123456789), 2191144977u, 0);\n#endif\n    murmurhash3_32_test(large_key<32>(123456789), 2555553099u, 0);\n  }\n};\n\n#if _CCCL_HAS_INT128()\nstruct test_murmurhash3_x86_128\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_x86_128> murmurhash3_x86_128_test;\n\n  _CCCL_HOST_DEVICE __uint128_t conv(cuda::std::array<uint32_t, 4> const& arr) const\n  {\n    return cuda::std::bit_cast<__uint128_t>(arr);\n  }\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_x86_128_test(int32_t(0), conv({3422973727u, 2656139328u, 2656139328u, 2656139328u}), 0);\n    murmurhash3_x86_128_test(int32_t(9), conv({2808089785u, 314604614u, 314604614u, 314604614u}), 0);\n    murmurhash3_x86_128_test(int32_t(42), conv({3611919118u, 1962256489u, 1962256489u, 1962256489u}), 0);\n    murmurhash3_x86_128_test(int32_t(42), conv({3399017053u, 732469929u, 732469929u, 732469929u}), 42);\n\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 2>{2, 2}, conv({1234494082u, 1431451587u, 431049201u, 431049201u}), 0);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 3>{1, 4, 9}, conv({2516796247u, 2757675829u, 778406919u, 2453259553u}), 42);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int32_t, 4>{42, 64, 108, 1024}, conv({2686265656u, 591236665u, 3797082165u, 2731908938u}), 63);\n    murmurhash3_x86_128_test(cuda::std::array<int32_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({3918256832u, 4205523739u, 1707810111u, 1625952473u}),\n                             1024);\n\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 2>{2, 2}, conv({3811075945u, 727160712u, 3510740342u, 235225510u}), 0);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 3>{1, 4, 9}, conv({2817194959u, 206796677u, 3391242768u, 248681098u}), 42);\n    murmurhash3_x86_128_test(\n      cuda::std::array<int64_t, 4>{42, 64, 108, 1024}, conv({2335912146u, 1566515912u, 760710030u, 452077451u}), 63);\n    murmurhash3_x86_128_test(cuda::std::array<int64_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({1101169764u, 1758958147u, 2406511780u, 2903571412u}),\n                             1024);\n  }\n};\n\nstruct test_murmurhash3_x64_128\n{\n  hash_test<cudax::cuco::hash_algorithm::murmurhash3_x64_128> murmurhash3_x64_128_test;\n\n  _CCCL_HOST_DEVICE __uint128_t conv(cuda::std::array<uint64_t, 2> const& arr) const\n  {\n    return cuda::std::bit_cast<__uint128_t>(arr);\n  }\n\n  _CCCL_HOST_DEVICE void operator()()\n  {\n    murmurhash3_x64_128_test(int32_t(0), conv({14961230494313510588ull, 6383328099726337777ull}), 0);\n    murmurhash3_x64_128_test(int32_t(9), conv({1779292183511753683ull, 16298496441448380334ull}), 0);\n    murmurhash3_x64_128_test(int32_t(42), conv({2913627637088662735ull, 16344193523890567190ull}), 0);\n    murmurhash3_x64_128_test(int32_t(42), conv({2248879576374326886ull, 18006515275339376488ull}), 42);\n\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 2>{2, 2}, conv({12221386834995143465ull, 6690950894782946573ull}), 0);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 3>{1, 4, 9}, conv({299140022350411792ull, 9891903873182035274ull}), 42);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int32_t, 4>{42, 64, 108, 1024}, conv({4333511168876981289ull, 4659486988434316416ull}), 63);\n    murmurhash3_x64_128_test(cuda::std::array<int32_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({3302412811061286680ull, 7070355726356610672ull}),\n                             1024);\n\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 2>{2, 2}, conv({8554944597931919519ull, 14938998000509429729ull}), 0);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 3>{1, 4, 9}, conv({13442629947720186435ull, 7061727494178573325ull}), 42);\n    murmurhash3_x64_128_test(\n      cuda::std::array<int64_t, 4>{42, 64, 108, 1024}, conv({8786399719555989948ull, 14954183901757012458ull}), 63);\n    murmurhash3_x64_128_test(cuda::std::array<int64_t, 16>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n                             conv({15409921801541329777ull, 10546487400963404004ull}),\n                             1024);\n  }\n};\n#endif // _CCCL_HAS_INT128()\n\ntemplate <typename TestFn>\n__global__ void test_hasher_kernel(TestFn test_fn)\n{\n  test_fn();\n}\n\ntemplate <typename TestFn>\nvoid test_hasher_on_device(TestFn test_fn)\n{\n  test_hasher_kernel<<<1, 1>>>(test_fn);\n  CUDAX_REQUIRE(hipDeviceSynchronize() == hipSuccess);\n}\n\nTEST_CASE(\"Test Hasher's on host and device\", \"\")\n{\n  SECTION(\"host-generated hash values match the reference implementation.\")\n  {\n    test_xxhash32{}();\n    test_xxhash64{}();\n    test_murmurhash3_32{}();\n#if _CCCL_HAS_INT128()\n    test_murmurhash3_x86_128{}();\n    test_murmurhash3_x64_128{}();\n#endif // _CCCL_HAS_INT128()\n  }\n\n  SECTION(\"device-generated hash values match the reference implementation.\")\n  {\n    test_hasher_on_device(test_xxhash32{});\n    test_hasher_on_device(test_xxhash64{});\n    test_hasher_on_device(test_murmurhash3_32{});\n#if _CCCL_HAS_INT128()\n    test_hasher_on_device(test_murmurhash3_x86_128{});\n    test_hasher_on_device(test_murmurhash3_x64_128{});\n#endif // _CCCL_HAS_INT128()\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/cuco/utility/test_hashers.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic stream_ctx ctx;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(size_t beg, size_t end, size_t GHOST_SIZE)\n      : beg(beg)\n      , end(end)\n      , block_size(end - beg)\n      , ghost_size(GHOST_SIZE)\n      , array(std::vector<T>(block_size + 2 * ghost_size))\n      , left_interface(std::vector<T>(ghost_size))\n      , right_interface(std::vector<T>(ghost_size))\n      , handle(ctx.logical_data(&array[0], block_size + 2 * ghost_size))\n      , left_handle(ctx.logical_data(&left_interface[0], ghost_size))\n      , right_handle(ctx.logical_data(&right_interface[0], ghost_size))\n  {}\n\n  T check_sum()\n  {\n    T sum = 0.0;\n\n    ctx.task(exec_place::host(), handle.read())->*[&](cudaStream_t stream, auto sn) {\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      const T* h_center = sn.data_handle();\n      for (size_t offset = ghost_size; offset < ghost_size + block_size; offset++)\n      {\n        sum += h_center[offset];\n      }\n    };\n\n    return sum;\n  }\n\npublic:\n  size_t beg;\n  size_t end;\n  size_t block_size;\n  size_t ghost_size;\n  int preferred_device;\n\nprivate:\n  std::vector<T> array;\n  std::vector<T> left_interface;\n  std::vector<T> right_interface;\n\npublic:\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n  // A piece of data to store the left part of the block\n  logical_data<slice<T>> left_handle;\n  // A piece of data to store the right part of the block\n  logical_data<slice<T>> right_handle;\n};\n\n// array and array1 have a size of (cnt + 2*ghost_size)\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    size_t idx2 = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid stencil(data_block<T>& bn, data_block<T>& bn1)\n{\n  int dev = bn.preferred_device;\n\n  ctx.task(exec_place::device(dev), bn.handle.rw(), bn1.handle.read())->*[&](cudaStream_t stream, auto sN, auto sN1) {\n    stencil_kernel<T><<<32, 64, 0, stream>>>(bn.block_size, bn.ghost_size, sN.data_handle(), sN1.data_handle());\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\nvoid copy_task(\n  size_t cnt, logical_data<slice<T>>& dst, size_t offset_dst, logical_data<slice<T>>& src, size_t offset_src, int dev)\n{\n  ctx.task(exec_place::device(dev), dst.rw(), src.read())->*[&](cudaStream_t stream, auto dstS, auto srcS) {\n    int nblocks = (cnt > 64) ? 32 : 1;\n    copy_kernel<T><<<nblocks, 64, 0, stream>>>(cnt, dstS.data_handle() + offset_dst, srcS.data_handle() + offset_src);\n  };\n}\n\ntemplate <typename T>\nvoid update_inner_interfaces(data_block<T>& bn)\n{\n  // LEFT\n  copy_task<T>(bn.ghost_size, bn.left_handle, 0, bn.handle, bn.ghost_size, bn.preferred_device);\n\n  // RIGHT\n  copy_task<T>(bn.ghost_size, bn.right_handle, 0, bn.handle, bn.block_size, bn.preferred_device);\n}\n\n// Copy left/right handles from neighbours to the array\ntemplate <typename T>\nvoid update_outer_interfaces(data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  // update_outer_interface_left\n  copy_task<T>(bn.ghost_size, bn.handle, 0, left.right_handle, 0, bn.preferred_device);\n\n  // update_outer_interface_right\n  copy_task<T>(bn.ghost_size, bn.handle, bn.ghost_size + bn.block_size, right.left_handle, 0, bn.preferred_device);\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid copy_array(data_block<T>& bn, data_block<T>& bn1)\n{\n  assert(bn.preferred_device == bn1.preferred_device);\n  copy_task<T>(bn.block_size + 2 * bn.ghost_size, bn1.handle, 0, bn.handle, 0, bn.preferred_device);\n}\n\nint main(int argc, char** argv)\n{\n  int NITER         = 500;\n  size_t NBLOCKS    = 4;\n  size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t GHOST_SIZE = 1;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  // fprintf(stderr, \"GOT %d devices\\n\", ndevs);\n\n  double* U0 = new double[NBLOCKS * BLOCK_SIZE];\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    size_t end = (b + 1) * BLOCK_SIZE;\n\n    Un.emplace_back(beg, end, 1ull);\n    Un1.emplace_back(beg, end, 1ull);\n  }\n\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    Un[b].preferred_device  = b % ndevs;\n    Un1[b].preferred_device = b % ndevs;\n  }\n\n  // Fill blocks with initial values. For the sake of simplicity, we are\n  // using a synchronization primitive and host code, but this could have\n  // been written asynchronously using host callbacks.\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n\n    ctx.task(exec_place::host(), Un1[b].handle.rw())->*[&](cudaStream_t stream, auto sUn1) {\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      double* Un1_vals = sUn1.data_handle();\n\n      for (size_t local_idx = 0; local_idx < BLOCK_SIZE; local_idx++)\n      {\n        Un1_vals[local_idx + GHOST_SIZE] = U0[(beg + local_idx + TOTAL_SIZE) % TOTAL_SIZE];\n      }\n    };\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Update the internal copies of the left and right boundaries\n      update_inner_interfaces(Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Apply ghost cells from neighbours to put then in the \"center\" array\n      update_outer_interfaces(Un1[b], Un1[(b - 1 + NBLOCKS) % NBLOCKS], Un1[(b + 1) % NBLOCKS]);\n    }\n\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(Un[b], Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Save Un into Un1\n      copy_array(Un[b], Un1[b]);\n    }\n\n#if 0\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double check_sum = 0.0;\n      for (size_t b = 0; b < NBLOCKS; b++)\n      {\n        check_sum += Un[b].check_sum();\n      }\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, check_sum);\n    }\n#endif\n  }\n\n  // In this stencil, the sum of the elements is supposed to be a constant\n  double check_sum = 0.0;\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    check_sum += Un[b].check_sum();\n  }\n\n  double err = fabs(check_sum - 1.0);\n  EXPECT(err < 0.0001);\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic stream_ctx ctx;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(size_t beg, size_t end, size_t GHOST_SIZE)\n      : beg(beg)\n      , end(end)\n      , block_size(end - beg)\n      , ghost_size(GHOST_SIZE)\n      , array(std::vector<T>(block_size + 2 * ghost_size))\n      , left_interface(std::vector<T>(ghost_size))\n      , right_interface(std::vector<T>(ghost_size))\n      , handle(ctx.logical_data(&array[0], block_size + 2 * ghost_size))\n      , left_handle(ctx.logical_data(&left_interface[0], ghost_size))\n      , right_handle(ctx.logical_data(&right_interface[0], ghost_size))\n  {}\n\n  T check_sum()\n  {\n    T sum = 0.0;\n\n    ctx.task(exec_place::host(), handle.read())->*[&](hipStream_t stream, auto sn) {\n      cuda_safe_call(hipStreamSynchronize(stream));\n      const T* h_center = sn.data_handle();\n      for (size_t offset = ghost_size; offset < ghost_size + block_size; offset++)\n      {\n        sum += h_center[offset];\n      }\n    };\n\n    return sum;\n  }\n\npublic:\n  size_t beg;\n  size_t end;\n  size_t block_size;\n  size_t ghost_size;\n  int preferred_device;\n\nprivate:\n  std::vector<T> array;\n  std::vector<T> left_interface;\n  std::vector<T> right_interface;\n\npublic:\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n  // A piece of data to store the left part of the block\n  logical_data<slice<T>> left_handle;\n  // A piece of data to store the right part of the block\n  logical_data<slice<T>> right_handle;\n};\n\n// array and array1 have a size of (cnt + 2*ghost_size)\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    size_t idx2 = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid stencil(data_block<T>& bn, data_block<T>& bn1)\n{\n  int dev = bn.preferred_device;\n\n  ctx.task(exec_place::device(dev), bn.handle.rw(), bn1.handle.read())->*[&](hipStream_t stream, auto sN, auto sN1) {\n    stencil_kernel<T><<<32, 64, 0, stream>>>(bn.block_size, bn.ghost_size, sN.data_handle(), sN1.data_handle());\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\nvoid copy_task(\n  size_t cnt, logical_data<slice<T>>& dst, size_t offset_dst, logical_data<slice<T>>& src, size_t offset_src, int dev)\n{\n  ctx.task(exec_place::device(dev), dst.rw(), src.read())->*[&](hipStream_t stream, auto dstS, auto srcS) {\n    int nblocks = (cnt > 64) ? 32 : 1;\n    copy_kernel<T><<<nblocks, 64, 0, stream>>>(cnt, dstS.data_handle() + offset_dst, srcS.data_handle() + offset_src);\n  };\n}\n\ntemplate <typename T>\nvoid update_inner_interfaces(data_block<T>& bn)\n{\n  // LEFT\n  copy_task<T>(bn.ghost_size, bn.left_handle, 0, bn.handle, bn.ghost_size, bn.preferred_device);\n\n  // RIGHT\n  copy_task<T>(bn.ghost_size, bn.right_handle, 0, bn.handle, bn.block_size, bn.preferred_device);\n}\n\n// Copy left/right handles from neighbours to the array\ntemplate <typename T>\nvoid update_outer_interfaces(data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  // update_outer_interface_left\n  copy_task<T>(bn.ghost_size, bn.handle, 0, left.right_handle, 0, bn.preferred_device);\n\n  // update_outer_interface_right\n  copy_task<T>(bn.ghost_size, bn.handle, bn.ghost_size + bn.block_size, right.left_handle, 0, bn.preferred_device);\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid copy_array(data_block<T>& bn, data_block<T>& bn1)\n{\n  assert(bn.preferred_device == bn1.preferred_device);\n  copy_task<T>(bn.block_size + 2 * bn.ghost_size, bn1.handle, 0, bn.handle, 0, bn.preferred_device);\n}\n\nint main(int argc, char** argv)\n{\n  int NITER         = 500;\n  size_t NBLOCKS    = 4;\n  size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t GHOST_SIZE = 1;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  // fprintf(stderr, \"GOT %d devices\\n\", ndevs);\n\n  double* U0 = new double[NBLOCKS * BLOCK_SIZE];\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    size_t end = (b + 1) * BLOCK_SIZE;\n\n    Un.emplace_back(beg, end, 1ull);\n    Un1.emplace_back(beg, end, 1ull);\n  }\n\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    Un[b].preferred_device  = b % ndevs;\n    Un1[b].preferred_device = b % ndevs;\n  }\n\n  // Fill blocks with initial values. For the sake of simplicity, we are\n  // using a synchronization primitive and host code, but this could have\n  // been written asynchronously using host callbacks.\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n\n    ctx.task(exec_place::host(), Un1[b].handle.rw())->*[&](hipStream_t stream, auto sUn1) {\n      cuda_safe_call(hipStreamSynchronize(stream));\n      double* Un1_vals = sUn1.data_handle();\n\n      for (size_t local_idx = 0; local_idx < BLOCK_SIZE; local_idx++)\n      {\n        Un1_vals[local_idx + GHOST_SIZE] = U0[(beg + local_idx + TOTAL_SIZE) % TOTAL_SIZE];\n      }\n    };\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Update the internal copies of the left and right boundaries\n      update_inner_interfaces(Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Apply ghost cells from neighbours to put then in the \"center\" array\n      update_outer_interfaces(Un1[b], Un1[(b - 1 + NBLOCKS) % NBLOCKS], Un1[(b + 1) % NBLOCKS]);\n    }\n\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(Un[b], Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Save Un into Un1\n      copy_array(Un[b], Un1[b]);\n    }\n\n#if 0\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double check_sum = 0.0;\n      for (size_t b = 0; b < NBLOCKS; b++)\n      {\n        check_sum += Un[b].check_sum();\n      }\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, check_sum);\n    }\n#endif\n  }\n\n  // In this stencil, the sum of the elements is supposed to be a constant\n  double check_sum = 0.0;\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    check_sum += Un[b].check_sum();\n  }\n\n  double err = fabs(check_sum - 1.0);\n  EXPECT(err < 0.0001);\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/05-stencil.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n#include <cuda/experimental/__stf/utility/pretty_print.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void stencil2D_kernel(slice<T, 2> sUn, slice<const T, 2> sUn1)\n{\n  size_t N = sUn.extent(0);\n  for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += blockDim.x * gridDim.x)\n  {\n    for (size_t j = 0; j < N; j++)\n    {\n      sUn(j, i) = 0.8 * sUn1(j, i) + 0.05 * sUn1(j, (i + 1) % N) + 0.05 * sUn1(j, (i - 1 + N) % N)\n                + 0.05 * sUn1((j + 1) % N, i) + 0.05 * sUn1((j - 1 + N) % N, i);\n    }\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n\n  size_t NITER  = 500;\n  size_t N      = 1000;\n  bool vtk_dump = false;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    N = atoi(argv[2]);\n  }\n\n  if (argc > 3)\n  {\n    int val  = atoi(argv[3]);\n    vtk_dump = (val == 1);\n  }\n\n  size_t TOTAL_SIZE = N * N;\n\n  double* Un  = new double[TOTAL_SIZE];\n  double* Un1 = new double[TOTAL_SIZE];\n\n  for (size_t idx = 0; idx < TOTAL_SIZE; idx++)\n  {\n    Un[idx]  = (idx == 0) ? 1.0 : 0.0;\n    Un1[idx] = Un[idx];\n  }\n\n  auto lUn  = ctx.logical_data(make_slice(Un, std::tuple{N, N}, N));\n  auto lUn1 = ctx.logical_data(make_slice(Un1, std::tuple{N, N}, N));\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  // Partition over the vector of processor along the y-axis of the data domain\n  // TODO implement the proper tiled_partitioning along y !\n  data_place cdp = data_place::composite(tiled_partition<128>(), all_devs);\n\n  for (size_t iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    ctx.task(lUn.rw(cdp), lUn1.read(cdp))->*[&](auto stream, auto sUn, auto sUn1) {\n      stencil2D_kernel<double><<<32, 128, 0, stream>>>(sUn, sUn1);\n    };\n\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n\n      ctx.task(exec_place::host(), lUn.read())->*[&](auto stream, auto sUn) {\n        cuda_safe_call(cudaStreamSynchronize(stream));\n        for (size_t j = 0; j < N; j++)\n        {\n          for (size_t i = 0; i < N; i++)\n          {\n            sum += sUn(j, i);\n          }\n        }\n\n        if (vtk_dump)\n        {\n          char str[32];\n          snprintf(str, 32, \"Un_%05zu.vtk\", iter);\n          mdspan_to_vtk(sUn, std::string(str));\n        }\n      };\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n\n    std::swap(lUn, lUn1);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n#include <cuda/experimental/__stf/utility/pretty_print.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void stencil2D_kernel(slice<T, 2> sUn, slice<const T, 2> sUn1)\n{\n  size_t N = sUn.extent(0);\n  for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += blockDim.x * gridDim.x)\n  {\n    for (size_t j = 0; j < N; j++)\n    {\n      sUn(j, i) = 0.8 * sUn1(j, i) + 0.05 * sUn1(j, (i + 1) % N) + 0.05 * sUn1(j, (i - 1 + N) % N)\n                + 0.05 * sUn1((j + 1) % N, i) + 0.05 * sUn1((j - 1 + N) % N, i);\n    }\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n\n  size_t NITER  = 500;\n  size_t N      = 1000;\n  bool vtk_dump = false;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    N = atoi(argv[2]);\n  }\n\n  if (argc > 3)\n  {\n    int val  = atoi(argv[3]);\n    vtk_dump = (val == 1);\n  }\n\n  size_t TOTAL_SIZE = N * N;\n\n  double* Un  = new double[TOTAL_SIZE];\n  double* Un1 = new double[TOTAL_SIZE];\n\n  for (size_t idx = 0; idx < TOTAL_SIZE; idx++)\n  {\n    Un[idx]  = (idx == 0) ? 1.0 : 0.0;\n    Un1[idx] = Un[idx];\n  }\n\n  auto lUn  = ctx.logical_data(make_slice(Un, std::tuple{N, N}, N));\n  auto lUn1 = ctx.logical_data(make_slice(Un1, std::tuple{N, N}, N));\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  // Partition over the vector of processor along the y-axis of the data domain\n  // TODO implement the proper tiled_partitioning along y !\n  data_place cdp = data_place::composite(tiled_partition<128>(), all_devs);\n\n  for (size_t iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    ctx.task(lUn.rw(cdp), lUn1.read(cdp))->*[&](auto stream, auto sUn, auto sUn1) {\n      stencil2D_kernel<double><<<32, 128, 0, stream>>>(sUn, sUn1);\n    };\n\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n\n      ctx.task(exec_place::host(), lUn.read())->*[&](auto stream, auto sUn) {\n        cuda_safe_call(hipStreamSynchronize(stream));\n        for (size_t j = 0; j < N; j++)\n        {\n          for (size_t i = 0; i < N; i++)\n          {\n            sum += sUn(j, i);\n          }\n        }\n\n        if (vtk_dump)\n        {\n          char str[32];\n          snprintf(str, 32, \"Un_%05zu.vtk\", iter);\n          mdspan_to_vtk(sUn, std::string(str));\n        }\n      };\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n\n    std::swap(lUn, lUn1);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/05-stencil2d-places.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how to use the task construct with grids of\n *        places and composite data places\n */\n\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t start, size_t cnt, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < cnt; ind += nthreads)\n  {\n    y[ind + start] += a * x[ind + start];\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const int N = 1024 * 1024 * 32;\n  double *X, *Y;\n\n  X = new double[N];\n  Y = new double[N];\n  SCOPE(exit)\n  {\n    delete[] X;\n    delete[] Y;\n  };\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = X0(ind);\n    Y[ind] = Y0(ind);\n  }\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  // 512k doubles = 4MB (2 pages)\n  // A 1D blocking strategy over all devices with a block size of 32 and a round robin distribution of blocks across\n  // devices\n  //    data_place cdp = data_place(exec_place::all_devices().as_grid().get_grid(),\n  //            [](dim4 grid_dim, pos4 index_pos) { return pos4((index_pos.x / (512 * 1024ULL)) % grid_dim.x); });\n\n  data_place cdp = data_place::composite(tiled_partition<512 * 1024ULL>(), all_devs);\n\n  auto handle_X = ctx.logical_data(X, {N});\n  auto handle_Y = ctx.logical_data(Y, {N});\n\n  double alpha = 3.14;\n\n  /* Compute Y = Y + alpha X */\n  auto t = ctx.task(all_devs, handle_X.read(cdp), handle_Y.rw(cdp));\n  t->*[&](auto, auto sX, auto sY) {\n    size_t grid_size = t.grid_dims().size();\n\n    assert(N % grid_size == 0);\n\n    for (size_t i = 0; i < grid_size; i++)\n    {\n      auto active = t.activate_place(i);\n      axpy<<<16, 128, 0, t.get_stream(i)>>>(i * N / grid_size, N / grid_size, alpha, sX.data_handle(), sY.data_handle());\n    }\n  };\n\n  /* Check the result on the host */\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto sX, auto sY) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(sY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(sX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  // Disabled until composite data places are implemented with graphs\n  // run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief This example illustrates how to use the task construct with grids of\n *        places and composite data places\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t start, size_t cnt, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < cnt; ind += nthreads)\n  {\n    y[ind + start] += a * x[ind + start];\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const int N = 1024 * 1024 * 32;\n  double *X, *Y;\n\n  X = new double[N];\n  Y = new double[N];\n  SCOPE(exit)\n  {\n    delete[] X;\n    delete[] Y;\n  };\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = X0(ind);\n    Y[ind] = Y0(ind);\n  }\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  // 512k doubles = 4MB (2 pages)\n  // A 1D blocking strategy over all devices with a block size of 32 and a round robin distribution of blocks across\n  // devices\n  //    data_place cdp = data_place(exec_place::all_devices().as_grid().get_grid(),\n  //            [](dim4 grid_dim, pos4 index_pos) { return pos4((index_pos.x / (512 * 1024ULL)) % grid_dim.x); });\n\n  data_place cdp = data_place::composite(tiled_partition<512 * 1024ULL>(), all_devs);\n\n  auto handle_X = ctx.logical_data(X, {N});\n  auto handle_Y = ctx.logical_data(Y, {N});\n\n  double alpha = 3.14;\n\n  /* Compute Y = Y + alpha X */\n  auto t = ctx.task(all_devs, handle_X.read(cdp), handle_Y.rw(cdp));\n  t->*[&](auto, auto sX, auto sY) {\n    size_t grid_size = t.grid_dims().size();\n\n    assert(N % grid_size == 0);\n\n    for (size_t i = 0; i < grid_size; i++)\n    {\n      auto active = t.activate_place(i);\n      axpy<<<16, 128, 0, t.get_stream(i)>>>(i * N / grid_size, N / grid_size, alpha, sX.data_handle(), sY.data_handle());\n    }\n  };\n\n  /* Check the result on the host */\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto sX, auto sY) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(sY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(sX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  // Disabled until composite data places are implemented with graphs\n  // run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/01-axpy-places.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that the cuda_kernel construct works with global kernels, CUfunction and CUkernel entries.\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid test(bool is_graph)\n{\n  context ctx;\n  if (is_graph)\n  {\n    ctx = graph_ctx();\n  }\n\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  // Number of times we have applied the axpy kernel\n  int num_axpy = 0;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  // runtime global kernel\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // axpy<<<16, 128, 0, ...>>>(alpha, dX, dY)\n    return cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n\n  // CUfunction driver API\n  CUfunction axpy_fun;\n  cuda_safe_call(cudaGetFuncBySymbol(&axpy_fun, (void*) axpy));\n\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    return cuda_kernel_desc{axpy_fun, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  // CUkernel driver API\n  CUkernel axpy_kernel;\n  cuda_safe_call(cudaGetKernel(&axpy_kernel, (void*) axpy));\n\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    return cuda_kernel_desc{axpy_kernel, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n#endif\n\n  /* Some extra sanity checks, we put this in a dummy task to get access to dX and dY values */\n  ctx.task(lX.read(), lY.rw())->*[&](auto, auto dX, auto dY) {\n    int nregs = cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n\n    int nregs_fun = cuda_kernel_desc{axpy_fun, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n    _CCCL_ASSERT(nregs == nregs_fun, \"invalid value\");\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\n    int nregs_kernel = cuda_kernel_desc{axpy_kernel, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n    _CCCL_ASSERT(nregs == nregs_kernel, \"invalid value\");\n#endif\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    _CCCL_ASSERT(fabs(Y[i] - (Y0(i) + num_axpy * alpha * X0(i))) < 0.0001, \"Invalid result\");\n    _CCCL_ASSERT(fabs(X[i] - X0(i)) < 0.0001, \"Invalid result\");\n  }\n}\n\nint main()\n{\n  // stream context\n  test(false);\n  // graph context\n  test(true);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that the cuda_kernel construct works with global kernels, CUfunction and CUkernel entries.\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid test(bool is_graph)\n{\n  context ctx;\n  if (is_graph)\n  {\n    ctx = graph_ctx();\n  }\n\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  // Number of times we have applied the axpy kernel\n  int num_axpy = 0;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  // runtime global kernel\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    // axpy<<<16, 128, 0, ...>>>(alpha, dX, dY)\n    return cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n\n  // CUfunction driver API\n  hipFunction_t axpy_fun;\n  cuda_safe_call(hipGetFuncBySymbol(&axpy_fun, (void*) axpy));\n\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    return cuda_kernel_desc{axpy_fun, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\n  // CUkernel driver API\n  hipKernel_t axpy_kernel;\n  cuda_safe_call(cudaGetKernel(&axpy_kernel, (void*) axpy));\n\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto dX, auto dY) {\n    return cuda_kernel_desc{axpy_kernel, 16, 128, 0, alpha, dX, dY};\n  };\n  num_axpy++;\n#endif\n\n  /* Some extra sanity checks, we put this in a dummy task to get access to dX and dY values */\n  ctx.task(lX.read(), lY.rw())->*[&](auto, auto dX, auto dY) {\n    int nregs = cuda_kernel_desc{axpy, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n\n    int nregs_fun = cuda_kernel_desc{axpy_fun, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n    _CCCL_ASSERT(nregs == nregs_fun, \"invalid value\");\n\n#if _CCCL_CTK_AT_LEAST(12, 1)\n    int nregs_kernel = cuda_kernel_desc{axpy_kernel, 16, 128, 0, alpha, dX, dY}.get_num_registers();\n    _CCCL_ASSERT(nregs == nregs_kernel, \"invalid value\");\n#endif\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    _CCCL_ASSERT(fabs(Y[i] - (Y0(i) + num_axpy * alpha * X0(i))) < 0.0001, \"Invalid result\");\n    _CCCL_ASSERT(fabs(X[i] - X0(i)) < 0.0001, \"Invalid result\");\n  }\n}\n\nint main()\n{\n  // stream context\n  test(false);\n  // graph context\n  test(true);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/cuda_kernels_driver.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void stencil_kernel(slice<T> Un, slice<const T> Un1)\n{\n  size_t N = Un.extent(0);\n  for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += blockDim.x * gridDim.x)\n  {\n    Un(i) = 0.9 * Un1(i) + 0.05 * Un1((i + N - 1) % N) + 0.05 * Un1((i + 1) % N);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n\n  int NITER               = 500;\n  int NBLOCKS             = 20;\n  const size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  double* Un  = new double[TOTAL_SIZE];\n  double* Un1 = new double[TOTAL_SIZE];\n\n  for (size_t idx = 0; idx < TOTAL_SIZE; idx++)\n  {\n    Un[idx]  = (idx == 0) ? 1.0 : 0.0;\n    Un1[idx] = Un[idx];\n  }\n\n  auto lUn  = ctx.logical_data(make_slice(Un, TOTAL_SIZE));\n  auto lUn1 = ctx.logical_data(make_slice(Un1, TOTAL_SIZE));\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  data_place cdp = data_place::composite(tiled_partition<BLOCK_SIZE>(), all_devs);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    ctx.task(lUn.rw(cdp), lUn1.read(cdp))->*[&](auto stream, auto sUn, auto sUn1) {\n      stencil_kernel<double><<<32, 128, 0, stream>>>(sUn, sUn1);\n    };\n\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n\n      ctx.task(exec_place::host(), lUn.read())->*[&](auto stream, auto sUn) {\n        cuda_safe_call(cudaStreamSynchronize(stream));\n        for (size_t offset = 0; offset < TOTAL_SIZE; offset++)\n        {\n          sum += sUn(offset);\n        }\n      };\n\n      // TODO add an assertion to check whether sum is close enough to 1.0\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n\n    std::swap(lUn, lUn1);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void stencil_kernel(slice<T> Un, slice<const T> Un1)\n{\n  size_t N = Un.extent(0);\n  for (size_t i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += blockDim.x * gridDim.x)\n  {\n    Un(i) = 0.9 * Un1(i) + 0.05 * Un1((i + N - 1) % N) + 0.05 * Un1((i + 1) % N);\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n\n  int NITER               = 500;\n  int NBLOCKS             = 20;\n  const size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  double* Un  = new double[TOTAL_SIZE];\n  double* Un1 = new double[TOTAL_SIZE];\n\n  for (size_t idx = 0; idx < TOTAL_SIZE; idx++)\n  {\n    Un[idx]  = (idx == 0) ? 1.0 : 0.0;\n    Un1[idx] = Un[idx];\n  }\n\n  auto lUn  = ctx.logical_data(make_slice(Un, TOTAL_SIZE));\n  auto lUn1 = ctx.logical_data(make_slice(Un1, TOTAL_SIZE));\n\n  //    std::shared_ptr<execution_grid> all_devs = exec_place::all_devices();\n  // use grid [ 0 0 0 0 ] for debugging purpose\n  auto all_devs = exec_place::repeat(exec_place::device(0), 4);\n\n  data_place cdp = data_place::composite(tiled_partition<BLOCK_SIZE>(), all_devs);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    ctx.task(lUn.rw(cdp), lUn1.read(cdp))->*[&](auto stream, auto sUn, auto sUn1) {\n      stencil_kernel<double><<<32, 128, 0, stream>>>(sUn, sUn1);\n    };\n\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n\n      ctx.task(exec_place::host(), lUn.read())->*[&](auto stream, auto sUn) {\n        cuda_safe_call(hipStreamSynchronize(stream));\n        for (size_t offset = 0; offset < TOTAL_SIZE; offset++)\n        {\n          sum += sUn(offset);\n        }\n      };\n\n      // TODO add an assertion to check whether sum is close enough to 1.0\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n\n    std::swap(lUn, lUn1);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/05-stencil-places.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(stream_ctx& ctx, size_t beg, size_t end, size_t GHOST_SIZE)\n      : beg(beg)\n      , end(end)\n      , block_size(end - beg)\n      , ghost_size(GHOST_SIZE)\n      , array(std::vector<T>(block_size + 2 * ghost_size))\n      , handle(ctx.logical_data(&array[0], block_size + 2 * ghost_size))\n  {}\n\npublic:\n  size_t beg;\n  size_t end;\n  size_t block_size;\n  size_t ghost_size;\n  int dev_id;\n\nprivate:\n  std::vector<T> array;\n\npublic:\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n};\n\ntemplate <typename T>\nT check_sum(stream_ctx& ctx, data_block<T>& bn)\n{\n  T sum = 0.0;\n\n  auto t = ctx.task(exec_place::host(), bn.handle.read());\n  t->*[&](cudaStream_t stream, auto h_center) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    for (size_t offset = bn.ghost_size; offset < bn.ghost_size + bn.block_size; offset++)\n    {\n      sum += h_center.data_handle()[offset];\n    }\n  };\n\n  return sum;\n}\n\n// array and array1 have a size of (cnt + 2*ghost_size)\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    size_t idx2 = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\ntemplate <typename T>\nvoid stencil(stream_ctx& ctx, data_block<T>& bn, data_block<T>& bn1)\n{\n  int dev = bn.dev_id;\n\n  auto t = ctx.task(exec_place::device(dev), bn.handle.rw(), bn1.handle.read());\n  t->*[&](cudaStream_t stream, auto bn_array, auto bn1_array) {\n    stencil_kernel<T>\n      <<<32, 64, 0, stream>>>(bn.block_size, bn.ghost_size, bn_array.data_handle(), bn1_array.data_handle());\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\nvoid copy_task(\n  stream_ctx& ctx,\n  size_t cnt,\n  logical_data<slice<T>>& dst,\n  size_t offset_dst,\n  int dst_dev,\n  logical_data<slice<T>>& src,\n  size_t offset_src,\n  int src_dev)\n{\n  auto t = ctx.task(exec_place::device(dst_dev), dst.rw(), src.read(data_place::device(src_dev)));\n  t->*[&](cudaStream_t stream, auto dst_array, auto src_array) {\n    int nblocks = (cnt > 64) ? 32 : 1;\n    copy_kernel<T>\n      <<<nblocks, 64, 0, stream>>>(cnt, dst_array.data_handle() + offset_dst, src_array.data_handle() + offset_src);\n  };\n}\n\n// Copy left/right handles from neighbours to the array\ntemplate <typename T>\nvoid update_halo(stream_ctx& ctx, data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  size_t gs = bn.ghost_size;\n  size_t bs = bn.block_size;\n\n  // Copy the bn.ghost_size last computed items in \"left\" (outside the halo)\n  copy_task<T>(ctx, gs, bn.handle, 0, bn.dev_id, left.handle, bs, left.dev_id);\n\n  // Copy the bn.ghost_size first computed items (outside the halo)\n  copy_task<T>(ctx, gs, bn.handle, gs + bs, bn.dev_id, right.handle, gs, right.dev_id);\n}\n\n// Copy inner part of bn into bn1\ntemplate <typename T>\nvoid copy_inner(stream_ctx& ctx, data_block<T>& bn1, data_block<T>& bn)\n{\n  size_t gs = bn.ghost_size;\n  size_t bs = bn.block_size;\n\n  int dev_id = bn.dev_id;\n\n  // Copy the bn.ghost_size last computed items in \"left\" (outside the halo)\n  copy_task<T>(ctx, bs, bn1.handle, gs, dev_id, bn.handle, gs, dev_id);\n}\n\nint main(int argc, char** argv)\n{\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n\n  int NITER         = 500;\n  size_t NBLOCKS    = 4 * ndevs;\n  size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t GHOST_SIZE = 1;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  double* U0 = new double[NBLOCKS * BLOCK_SIZE];\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    size_t end = (b + 1) * BLOCK_SIZE;\n\n    Un.emplace_back(ctx, beg, end, 1ull);\n    Un1.emplace_back(ctx, beg, end, 1ull);\n  }\n\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    Un[b].dev_id  = b % ndevs;\n    Un1[b].dev_id = b % ndevs;\n  }\n\n  // Fill blocks with initial values. For the sake of simplicity, we are\n  // using a synchronization primitive and host code, but this could have\n  // been written asynchronously using host callbacks.\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n\n    auto t = ctx.task(exec_place::host(), Un[b].handle.rw(), Un1[b].handle.rw());\n    t->*[&](cudaStream_t stream, auto Un_vals, auto Un1_vals) {\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      for (size_t local_idx = 0; local_idx < BLOCK_SIZE; local_idx++)\n      {\n        double val                                     = U0[(beg + local_idx + TOTAL_SIZE) % TOTAL_SIZE];\n        Un1_vals.data_handle()[local_idx + GHOST_SIZE] = val;\n        Un_vals.data_handle()[local_idx + GHOST_SIZE]  = val;\n      }\n    };\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      update_halo(ctx, Un1[b], Un[(b - 1 + NBLOCKS) % NBLOCKS], Un[(b + 1) % NBLOCKS]);\n    }\n\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(ctx, Un[b], Un1[b]);\n    }\n\n#if 0\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n      for (size_t b = 0; b < NBLOCKS; b++)\n      {\n        sum += check_sum(ctx, Un[b]);\n      }\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n#endif\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Copy inner part of Un into Un1\n      copy_inner(ctx, Un[b], Un1[b]);\n    }\n  }\n\n  // In this stencil, the sum of the elements is supposed to be a constant\n  double sum = 0.0;\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    sum += check_sum(ctx, Un[b]);\n  }\n\n  double err = fabs(sum - 1.0);\n  EXPECT(err < 0.0001);\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(stream_ctx& ctx, size_t beg, size_t end, size_t GHOST_SIZE)\n      : beg(beg)\n      , end(end)\n      , block_size(end - beg)\n      , ghost_size(GHOST_SIZE)\n      , array(std::vector<T>(block_size + 2 * ghost_size))\n      , handle(ctx.logical_data(&array[0], block_size + 2 * ghost_size))\n  {}\n\npublic:\n  size_t beg;\n  size_t end;\n  size_t block_size;\n  size_t ghost_size;\n  int dev_id;\n\nprivate:\n  std::vector<T> array;\n\npublic:\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n};\n\ntemplate <typename T>\nT check_sum(stream_ctx& ctx, data_block<T>& bn)\n{\n  T sum = 0.0;\n\n  auto t = ctx.task(exec_place::host(), bn.handle.read());\n  t->*[&](hipStream_t stream, auto h_center) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    for (size_t offset = bn.ghost_size; offset < bn.ghost_size + bn.block_size; offset++)\n    {\n      sum += h_center.data_handle()[offset];\n    }\n  };\n\n  return sum;\n}\n\n// array and array1 have a size of (cnt + 2*ghost_size)\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    size_t idx2 = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\ntemplate <typename T>\nvoid stencil(stream_ctx& ctx, data_block<T>& bn, data_block<T>& bn1)\n{\n  int dev = bn.dev_id;\n\n  auto t = ctx.task(exec_place::device(dev), bn.handle.rw(), bn1.handle.read());\n  t->*[&](hipStream_t stream, auto bn_array, auto bn1_array) {\n    stencil_kernel<T>\n      <<<32, 64, 0, stream>>>(bn.block_size, bn.ghost_size, bn_array.data_handle(), bn1_array.data_handle());\n  };\n}\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (size_t idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\nvoid copy_task(\n  stream_ctx& ctx,\n  size_t cnt,\n  logical_data<slice<T>>& dst,\n  size_t offset_dst,\n  int dst_dev,\n  logical_data<slice<T>>& src,\n  size_t offset_src,\n  int src_dev)\n{\n  auto t = ctx.task(exec_place::device(dst_dev), dst.rw(), src.read(data_place::device(src_dev)));\n  t->*[&](hipStream_t stream, auto dst_array, auto src_array) {\n    int nblocks = (cnt > 64) ? 32 : 1;\n    copy_kernel<T>\n      <<<nblocks, 64, 0, stream>>>(cnt, dst_array.data_handle() + offset_dst, src_array.data_handle() + offset_src);\n  };\n}\n\n// Copy left/right handles from neighbours to the array\ntemplate <typename T>\nvoid update_halo(stream_ctx& ctx, data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  size_t gs = bn.ghost_size;\n  size_t bs = bn.block_size;\n\n  // Copy the bn.ghost_size last computed items in \"left\" (outside the halo)\n  copy_task<T>(ctx, gs, bn.handle, 0, bn.dev_id, left.handle, bs, left.dev_id);\n\n  // Copy the bn.ghost_size first computed items (outside the halo)\n  copy_task<T>(ctx, gs, bn.handle, gs + bs, bn.dev_id, right.handle, gs, right.dev_id);\n}\n\n// Copy inner part of bn into bn1\ntemplate <typename T>\nvoid copy_inner(stream_ctx& ctx, data_block<T>& bn1, data_block<T>& bn)\n{\n  size_t gs = bn.ghost_size;\n  size_t bs = bn.block_size;\n\n  int dev_id = bn.dev_id;\n\n  // Copy the bn.ghost_size last computed items in \"left\" (outside the halo)\n  copy_task<T>(ctx, bs, bn1.handle, gs, dev_id, bn.handle, gs, dev_id);\n}\n\nint main(int argc, char** argv)\n{\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n\n  int NITER         = 500;\n  size_t NBLOCKS    = 4 * ndevs;\n  size_t BLOCK_SIZE = 2048 * 1024;\n\n  if (argc > 1)\n  {\n    NITER = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    NBLOCKS = atoi(argv[2]);\n  }\n\n  const size_t GHOST_SIZE = 1;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  double* U0 = new double[NBLOCKS * BLOCK_SIZE];\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    size_t end = (b + 1) * BLOCK_SIZE;\n\n    Un.emplace_back(ctx, beg, end, 1ull);\n    Un1.emplace_back(ctx, beg, end, 1ull);\n  }\n\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    Un[b].dev_id  = b % ndevs;\n    Un1[b].dev_id = b % ndevs;\n  }\n\n  // Fill blocks with initial values. For the sake of simplicity, we are\n  // using a synchronization primitive and host code, but this could have\n  // been written asynchronously using host callbacks.\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n\n    auto t = ctx.task(exec_place::host(), Un[b].handle.rw(), Un1[b].handle.rw());\n    t->*[&](hipStream_t stream, auto Un_vals, auto Un1_vals) {\n      cuda_safe_call(hipStreamSynchronize(stream));\n      for (size_t local_idx = 0; local_idx < BLOCK_SIZE; local_idx++)\n      {\n        double val                                     = U0[(beg + local_idx + TOTAL_SIZE) % TOTAL_SIZE];\n        Un1_vals.data_handle()[local_idx + GHOST_SIZE] = val;\n        Un_vals.data_handle()[local_idx + GHOST_SIZE]  = val;\n      }\n    };\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      update_halo(ctx, Un1[b], Un[(b - 1 + NBLOCKS) % NBLOCKS], Un[(b + 1) % NBLOCKS]);\n    }\n\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(ctx, Un[b], Un1[b]);\n    }\n\n#if 0\n    // We make sure that the total sum of elements remains constant\n    if (iter % 250 == 0)\n    {\n      double sum = 0.0;\n      for (size_t b = 0; b < NBLOCKS; b++)\n      {\n        sum += check_sum(ctx, Un[b]);\n      }\n\n      // fprintf(stderr, \"iter %d : CHECK SUM = %e\\n\", iter, sum);\n    }\n#endif\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Copy inner part of Un into Un1\n      copy_inner(ctx, Un[b], Un1[b]);\n    }\n  }\n\n  // In this stencil, the sum of the elements is supposed to be a constant\n  double sum = 0.0;\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    sum += check_sum(ctx, Un[b]);\n  }\n\n  double err = fabs(sum - 1.0);\n  EXPECT(err < 0.0001);\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/examples/05-stencil-no-copy.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Insert the key/values in kvs into the hashtable\n__global__ void gpu_hashtable_insert_kernel(hashtable h, const reserved::KeyValue* kvs, unsigned int numkvs)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (threadid < numkvs)\n  {\n    h.insert(kvs[threadid]);\n  }\n}\n\nvoid gpu_hashtable_insert(hashtable d_h, const reserved::KeyValue* device_kvs, unsigned int num_kvs, cudaStream_t stream)\n{\n  // Have CUDA calculate the thread block size\n  const auto occ_res  = reserved::compute_occupancy(gpu_hashtable_insert_kernel);\n  int threadblocksize = occ_res.block_size;\n  int gridsize        = ((uint32_t) num_kvs + threadblocksize - 1) / threadblocksize;\n\n  // Insert all the keys into the hash table\n  gpu_hashtable_insert_kernel<<<gridsize, threadblocksize, 0, stream>>>(d_h, device_kvs, (uint32_t) num_kvs);\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  // This constructor automatically initializes an empty hashtable on the host\n  hashtable h;\n  auto lh = ctx.logical_data(h);\n\n  // Create an array of values on the host\n  reserved::KeyValue kvs_array[16];\n  for (uint32_t i = 0; i < 16; i++)\n  {\n    kvs_array[i].key   = i * 10;\n    kvs_array[i].value = 17 + i * 14;\n  }\n  auto h_kvs_array = ctx.logical_data(make_slice(&kvs_array[0], 16));\n\n  ctx.task(lh.rw(), h_kvs_array.read())->*[](auto stream, auto h, auto a) {\n    gpu_hashtable_insert(h, a.data_handle(), static_cast<uint32_t>(a.extent(0)), stream);\n  };\n\n  ctx.finalize();\n\n  // Thanks to the write-back mechanism on lh, h has been updated\n  for (size_t i = 0; i < 16; i++)\n  {\n    assert(h.get(i * 10) == 17 + i * 14);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Insert the key/values in kvs into the hashtable\n__global__ void gpu_hashtable_insert_kernel(hashtable h, const reserved::KeyValue* kvs, unsigned int numkvs)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (threadid < numkvs)\n  {\n    h.insert(kvs[threadid]);\n  }\n}\n\nvoid gpu_hashtable_insert(hashtable d_h, const reserved::KeyValue* device_kvs, unsigned int num_kvs, hipStream_t stream)\n{\n  // Have CUDA calculate the thread block size\n  const auto occ_res  = reserved::compute_occupancy(gpu_hashtable_insert_kernel);\n  int threadblocksize = occ_res.block_size;\n  int gridsize        = ((uint32_t) num_kvs + threadblocksize - 1) / threadblocksize;\n\n  // Insert all the keys into the hash table\n  gpu_hashtable_insert_kernel<<<gridsize, threadblocksize, 0, stream>>>(d_h, device_kvs, (uint32_t) num_kvs);\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  // This constructor automatically initializes an empty hashtable on the host\n  hashtable h;\n  auto lh = ctx.logical_data(h);\n\n  // Create an array of values on the host\n  reserved::KeyValue kvs_array[16];\n  for (uint32_t i = 0; i < 16; i++)\n  {\n    kvs_array[i].key   = i * 10;\n    kvs_array[i].value = 17 + i * 14;\n  }\n  auto h_kvs_array = ctx.logical_data(make_slice(&kvs_array[0], 16));\n\n  ctx.task(lh.rw(), h_kvs_array.read())->*[](auto stream, auto h, auto a) {\n    gpu_hashtable_insert(h, a.data_handle(), static_cast<uint32_t>(a.extent(0)), stream);\n  };\n\n  ctx.finalize();\n\n  // Thanks to the write-back mechanism on lh, h has been updated\n  for (size_t i = 0; i < 16; i++)\n  {\n    assert(h.get(i * 10) == 17 + i * 14);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/hashtable/test.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Iterate over every item in the hashtableA, and add them to B\n\n__global__ void gpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  while (threadid < reserved::kHashTableCapacity)\n  {\n    if (B.addr[threadid].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[threadid].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[threadid]);\n      }\n    }\n    threadid += blockDim.x * gridDim.x;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  hashtable A;\n  A.insert(reserved::KeyValue(107, 4));\n  A.insert(reserved::KeyValue(108, 6));\n\n  hashtable B;\n  B.insert(reserved::KeyValue(7, 14));\n  B.insert(reserved::KeyValue(8, 16));\n\n  auto lA = ctx.logical_data(A);\n  auto lB = ctx.logical_data(B);\n\n  ctx.task(lA.rw(), lB.read())->*[](auto stream, auto hA, auto hB) {\n    gpu_merge_hashtable<<<32, 128, 0, stream>>>(hA, hB);\n    cuda_safe_call(cudaGetLastError());\n  };\n\n  ctx.host_launch(lA.read())->*[](auto hA) {\n    EXPECT(hA.get(107) == 4);\n    EXPECT(hA.get(108) == 6);\n    EXPECT(hA.get(7) == 14);\n    EXPECT(hA.get(8) == 16);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Iterate over every item in the hashtableA, and add them to B\n\n__global__ void gpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  while (threadid < reserved::kHashTableCapacity)\n  {\n    if (B.addr[threadid].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[threadid].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[threadid]);\n      }\n    }\n    threadid += blockDim.x * gridDim.x;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  hashtable A;\n  A.insert(reserved::KeyValue(107, 4));\n  A.insert(reserved::KeyValue(108, 6));\n\n  hashtable B;\n  B.insert(reserved::KeyValue(7, 14));\n  B.insert(reserved::KeyValue(8, 16));\n\n  auto lA = ctx.logical_data(A);\n  auto lB = ctx.logical_data(B);\n\n  ctx.task(lA.rw(), lB.read())->*[](auto stream, auto hA, auto hB) {\n    gpu_merge_hashtable<<<32, 128, 0, stream>>>(hA, hB);\n    cuda_safe_call(hipGetLastError());\n  };\n\n  ctx.host_launch(lA.read())->*[](auto hA) {\n    EXPECT(hA.get(107) == 4);\n    EXPECT(hA.get(108) == 6);\n    EXPECT(hA.get(7) == 14);\n    EXPECT(hA.get(8) == 16);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/hashtable/fusion.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void gpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  while (threadid < B.get_capacity())\n  {\n    if (B.addr[threadid].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[threadid].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[threadid]);\n      }\n    }\n    threadid += blockDim.x * gridDim.x;\n  }\n}\n\nvoid cpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  for (unsigned int i = 0; i < B.get_capacity(); i++)\n  {\n    if (B.addr[i].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[i].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[i]);\n      }\n    }\n  }\n}\n\nclass hashtable_fusion_t : public stream_reduction_operator<hashtable>\n{\n  void op(const hashtable& in, hashtable& inout, const exec_place& e, cudaStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      cuda_safe_call(cudaStreamSynchronize(s)); // TODO use a callback\n      cpu_merge_hashtable(inout, in);\n    }\n    else\n    {\n      gpu_merge_hashtable<<<32, 32, 0, s>>>(inout, in);\n    }\n  }\n\n  void init_op(hashtable& /*unused*/, const exec_place& /*unused*/, cudaStream_t /*unused*/) override\n  {\n    // This init operator is a no-op because hashtables are already\n    // initialized as empty tables\n  }\n};\n\n// A kernel to fill the hashtable with some fictitious values\n__global__ void fill_table(size_t dev_id, size_t cnt, hashtable h)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  unsigned int nthreads = blockDim.x * gridDim.x;\n\n  for (unsigned int i = threadid; i < cnt; i += nthreads)\n  {\n    uint32_t key   = dev_id * 1000 + i;\n    uint32_t value = 2 * i;\n\n    reserved::KeyValue kvs(key, value);\n    h.insert(kvs);\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  // Explicit capacity of 2048 entries\n  hashtable refh(2048);\n  auto h_handle = ctx.logical_data(refh);\n\n  auto fusion_op = std::make_shared<hashtable_fusion_t>();\n\n  for (size_t dev_id = 0; dev_id < 4; dev_id++)\n  {\n    ctx.task(h_handle.relaxed(fusion_op))->*[&](auto stream, auto h) {\n      EXPECT(h.get_capacity() == 2048);\n      fill_table<<<32, 32, 0, stream>>>(dev_id, 10, h);\n    };\n  }\n\n  ctx.host_launch(h_handle.read())->*[&](auto h) {\n    // Check that the table contains all values\n    for (size_t dev_id = 0; dev_id < 4; dev_id++)\n    {\n      for (unsigned i = 0; i < 10; i++)\n      {\n        uint32_t key   = static_cast<uint32_t>(dev_id * 1000 + i);\n        uint32_t value = 2 * i;\n\n        EXPECT(h.get(key) == value);\n      }\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/hashtable_linearprobing.cuh>\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void gpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  while (threadid < B.get_capacity())\n  {\n    if (B.addr[threadid].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[threadid].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[threadid]);\n      }\n    }\n    threadid += blockDim.x * gridDim.x;\n  }\n}\n\nvoid cpu_merge_hashtable(hashtable A, const hashtable B)\n{\n  for (unsigned int i = 0; i < B.get_capacity(); i++)\n  {\n    if (B.addr[i].key != reserved::kEmpty)\n    {\n      uint32_t value = B.addr[i].value;\n      if (value != reserved::kEmpty)\n      {\n        //    printf(\"INSERTING key %d value %d\\n\", pHashTableB[threadid].key, value);\n        A.insert(B.addr[i]);\n      }\n    }\n  }\n}\n\nclass hashtable_fusion_t : public stream_reduction_operator<hashtable>\n{\n  void op(const hashtable& in, hashtable& inout, const exec_place& e, hipStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      cuda_safe_call(hipStreamSynchronize(s)); // TODO use a callback\n      cpu_merge_hashtable(inout, in);\n    }\n    else\n    {\n      gpu_merge_hashtable<<<32, 32, 0, s>>>(inout, in);\n    }\n  }\n\n  void init_op(hashtable& /*unused*/, const exec_place& /*unused*/, hipStream_t /*unused*/) override\n  {\n    // This init operator is a no-op because hashtables are already\n    // initialized as empty tables\n  }\n};\n\n// A kernel to fill the hashtable with some fictitious values\n__global__ void fill_table(size_t dev_id, size_t cnt, hashtable h)\n{\n  unsigned int threadid = blockIdx.x * blockDim.x + threadIdx.x;\n  unsigned int nthreads = blockDim.x * gridDim.x;\n\n  for (unsigned int i = threadid; i < cnt; i += nthreads)\n  {\n    uint32_t key   = dev_id * 1000 + i;\n    uint32_t value = 2 * i;\n\n    reserved::KeyValue kvs(key, value);\n    h.insert(kvs);\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  // Explicit capacity of 2048 entries\n  hashtable refh(2048);\n  auto h_handle = ctx.logical_data(refh);\n\n  auto fusion_op = std::make_shared<hashtable_fusion_t>();\n\n  for (size_t dev_id = 0; dev_id < 4; dev_id++)\n  {\n    ctx.task(h_handle.relaxed(fusion_op))->*[&](auto stream, auto h) {\n      EXPECT(h.get_capacity() == 2048);\n      fill_table<<<32, 32, 0, stream>>>(dev_id, 10, h);\n    };\n  }\n\n  ctx.host_launch(h_handle.read())->*[&](auto h) {\n    // Check that the table contains all values\n    for (size_t dev_id = 0; dev_id < 4; dev_id++)\n    {\n      for (unsigned i = 0; i < 10; i++)\n      {\n        uint32_t key   = static_cast<uint32_t>(dev_id * 1000 + i);\n        uint32_t value = 2 * i;\n\n        EXPECT(h.get(key) == value);\n      }\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/hashtable/fusion_reduction.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\ntemplate <typename Ctx_t>\nvoid run(int NTASKS, int ms)\n{\n  Ctx_t ctx;\n\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  cudaEvent_t start, stop;\n  cuda_safe_call(cudaEventCreate(&start));\n  cuda_safe_call(cudaEventCreate(&stop));\n\n  // warm-up\n  ctx.task(handle.rw())->*[ms](cudaStream_t stream, auto) {\n    cuda_sleep(ms, stream);\n  };\n\n  cuda_safe_call(cudaEventRecord(start, ctx.fence()));\n\n  for (int iter = 0; iter < NTASKS; iter++)\n  {\n    ctx.task(handle.rw())->*[ms](cudaStream_t stream, auto) {\n      cuda_sleep(ms, stream);\n    };\n  }\n\n  cuda_safe_call(cudaEventRecord(stop, ctx.fence()));\n\n  ctx.finalize();\n\n  [[maybe_unused]] float elapsed;\n  cuda_safe_call(cudaEventElapsedTime(&elapsed, start, stop));\n\n  [[maybe_unused]] float expected = 1.0f * NTASKS * ms;\n\n  /* We cannot really expect this measurement to be accurate because the\n   * thread(s) executing the code might be preempted on a system with a high load\n   * (as during unit tests). So the best we can expect is that the elapsed time\n   * is larger than the sleep time, but event the timer on the GPU is not\n   * perfectly accurate so we do not make any strict assumptions about the\n   * test, and just keep this test to demonstrate how to use the mechanisms,\n   * and ensure they are functional . */\n  // EXPECT(elapsed >= expected);\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 25;\n  int ms     = 200;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  run<context>(NTASKS, ms);\n  run<stream_ctx>(NTASKS, ms);\n  run<graph_ctx>(NTASKS, ms);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\ntemplate <typename Ctx_t>\nvoid run(int NTASKS, int ms)\n{\n  Ctx_t ctx;\n\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  hipEvent_t start, stop;\n  cuda_safe_call(hipEventCreate(&start));\n  cuda_safe_call(hipEventCreate(&stop));\n\n  // warm-up\n  ctx.task(handle.rw())->*[ms](hipStream_t stream, auto) {\n    cuda_sleep(ms, stream);\n  };\n\n  cuda_safe_call(hipEventRecord(start, ctx.fence()));\n\n  for (int iter = 0; iter < NTASKS; iter++)\n  {\n    ctx.task(handle.rw())->*[ms](hipStream_t stream, auto) {\n      cuda_sleep(ms, stream);\n    };\n  }\n\n  cuda_safe_call(hipEventRecord(stop, ctx.fence()));\n\n  ctx.finalize();\n\n  [[maybe_unused]] float elapsed;\n  cuda_safe_call(hipEventElapsedTime(&elapsed, start, stop));\n\n  [[maybe_unused]] float expected = 1.0f * NTASKS * ms;\n\n  /* We cannot really expect this measurement to be accurate because the\n   * thread(s) executing the code might be preempted on a system with a high load\n   * (as during unit tests). So the best we can expect is that the elapsed time\n   * is larger than the sleep time, but event the timer on the GPU is not\n   * perfectly accurate so we do not make any strict assumptions about the\n   * test, and just keep this test to demonstrate how to use the mechanisms,\n   * and ensure they are functional . */\n  // EXPECT(elapsed >= expected);\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 25;\n  int ms     = 200;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  run<context>(NTASKS, ms);\n  run<stream_ctx>(NTASKS, ms);\n  run<graph_ctx>(NTASKS, ms);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/utility/timing_with_fences.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(const float* A, const float* B, float* C, int numElements)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < numElements)\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  // Error code to check return values for CUDA calls\n  cudaError_t err = cudaSuccess;\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  size_t size     = numElements * sizeof(float);\n  // printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host input vector A\n  float* h_A = (float*) malloc(size);\n\n  // Allocate the host input vector B\n  float* h_B = (float*) malloc(size);\n\n  // Allocate the host output vector C\n  float* h_C = (float*) malloc(size);\n\n  // Verify that allocations succeeded\n  if (h_A == NULL || h_B == NULL || h_C == NULL)\n  {\n    fprintf(stderr, \"Failed to allocate host vectors!\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    h_A[i] = rand() / (float) RAND_MAX;\n    h_B[i] = rand() / (float) RAND_MAX;\n  }\n\n  auto A_handle = ctx.logical_data(h_A, numElements);\n  auto B_handle = ctx.logical_data(h_B, numElements);\n  auto C_handle = ctx.logical_data(h_C, numElements);\n\n  ctx.task(A_handle.read(), B_handle.read(), C_handle.write())->*[&](cudaStream_t stream, auto d_A, auto d_B, auto d_C) {\n    // Launch the Vector Add CUDA Kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid   = (numElements + threadsPerBlock - 1) / threadsPerBlock;\n    // printf(\"CUDA kernel launch with %d blocks of %d threads\\n\", blocksPerGrid, threadsPerBlock);\n    vectorAdd<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(\n      d_A.data_handle(), d_B.data_handle(), d_C.data_handle(), numElements);\n    err = cudaGetLastError();\n\n    if (err != cudaSuccess)\n    {\n      fprintf(stderr, \"Failed to launch vectorAdd kernel (error code %s)!\\n\", cudaGetErrorString(err));\n      exit(EXIT_FAILURE);\n    }\n  };\n\n  ctx.host_launch(A_handle.read(), B_handle.read(), C_handle.read())->*[](auto hA, auto hB, auto hC) {\n    // Verify that the result vector is correct\n    for (int i = 0; i < hC.extent(0); ++i)\n    {\n      if (::std::abs(hA[i] + hB[i] - hC[i]) > 1e-5)\n      {\n        fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n        exit(EXIT_FAILURE);\n      }\n    }\n  };\n\n  ctx.finalize();\n\n  // Free host memory\n  free(h_A);\n  free(h_B);\n  free(h_C);\n}\n\n/**\n * Host main routine\n */\nint main(void)\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(const float* A, const float* B, float* C, int numElements)\n{\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < numElements)\n  {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  // Error code to check return values for CUDA calls\n  hipError_t err = hipSuccess;\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  size_t size     = numElements * sizeof(float);\n  // printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host input vector A\n  float* h_A = (float*) malloc(size);\n\n  // Allocate the host input vector B\n  float* h_B = (float*) malloc(size);\n\n  // Allocate the host output vector C\n  float* h_C = (float*) malloc(size);\n\n  // Verify that allocations succeeded\n  if (h_A == NULL || h_B == NULL || h_C == NULL)\n  {\n    fprintf(stderr, \"Failed to allocate host vectors!\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i)\n  {\n    h_A[i] = rand() / (float) RAND_MAX;\n    h_B[i] = rand() / (float) RAND_MAX;\n  }\n\n  auto A_handle = ctx.logical_data(h_A, numElements);\n  auto B_handle = ctx.logical_data(h_B, numElements);\n  auto C_handle = ctx.logical_data(h_C, numElements);\n\n  ctx.task(A_handle.read(), B_handle.read(), C_handle.write())->*[&](hipStream_t stream, auto d_A, auto d_B, auto d_C) {\n    // Launch the Vector Add CUDA Kernel\n    int threadsPerBlock = 256;\n    int blocksPerGrid   = (numElements + threadsPerBlock - 1) / threadsPerBlock;\n    // printf(\"CUDA kernel launch with %d blocks of %d threads\\n\", blocksPerGrid, threadsPerBlock);\n    vectorAdd<<<blocksPerGrid, threadsPerBlock, 0, stream>>>(\n      d_A.data_handle(), d_B.data_handle(), d_C.data_handle(), numElements);\n    err = hipGetLastError();\n\n    if (err != hipSuccess)\n    {\n      fprintf(stderr, \"Failed to launch vectorAdd kernel (error code %s)!\\n\", hipGetErrorString(err));\n      exit(EXIT_FAILURE);\n    }\n  };\n\n  ctx.host_launch(A_handle.read(), B_handle.read(), C_handle.read())->*[](auto hA, auto hB, auto hC) {\n    // Verify that the result vector is correct\n    for (int i = 0; i < hC.extent(0); ++i)\n    {\n      if (::std::abs(hA[i] + hB[i] - hC[i]) > 1e-5)\n      {\n        fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n        exit(EXIT_FAILURE);\n      }\n    }\n  };\n\n  ctx.finalize();\n\n  // Free host memory\n  free(h_A);\n  free(h_B);\n  free(h_C);\n}\n\n/**\n * Host main routine\n */\nint main(void)\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cuda-samples/0_Introduction/vectorAdd/vectorAdd_cudastf.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types\n////////////////////////////////////////////////////////////////////////////////\n#include \"MonteCarlo_reduction.cuh\"\n\n// This will output the proper error string when calling cudaGetLastError\n#define getLastCudaError(msg) __getLastCudaError((msg), __FILE__, __LINE__)\n\nnamespace cg = cooperative_groups;\n\ninline void __getLastCudaError(const char* errorMessage, const char* file, const int line)\n{\n  cudaError_t err = cudaGetLastError();\n\n  if (cudaSuccess != err)\n  {\n    fprintf(stderr,\n            \"%s(%i) : getLastCudaError() CUDA error:\"\n            \" %s : (%d) %s.\\n\",\n            file,\n            line,\n            errorMessage,\n            static_cast<int>(err),\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Internal GPU-side data structures\n////////////////////////////////////////////////////////////////////////////////\n#define MAX_OPTIONS (1024 * 1024)\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut payoff functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)\n{\n  float callValue = S * __expf(MuByT + VBySqrtT * r) - X;\n  return (callValue > 0.0F) ? callValue : 0.0F;\n}\n\n__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)\n{\n  double callValue = S * exp(MuByT + VBySqrtT * r) - X;\n  return (callValue > 0.0) ? callValue : 0.0;\n}\n\n#define THREAD_N 256\n\n////////////////////////////////////////////////////////////////////////////////\n// This kernel computes the integral over all paths using a single thread block\n// per option. It is fastest when the number of thread blocks times the work per\n// block is high enough to keep the GPU busy.\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void MonteCarloOneBlockPerOption(\n  curandState* __restrict rngStates,\n  const __TOptionData* __restrict d_OptionData,\n  __TOptionValue* __restrict d_CallValue,\n  int pathN,\n  int optionN)\n{\n  // Handle to thread block group\n  cg::thread_block cta             = cg::this_thread_block();\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  const int SUM_N = THREAD_N;\n  __shared__ real s_SumCall[SUM_N];\n  __shared__ real s_Sum2Call[SUM_N];\n\n  // determine global thread id\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  // Copy random number state to local memory for efficiency\n  curandState localState = rngStates[tid];\n  for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x)\n  {\n    const real S        = d_OptionData[optionIndex].S;\n    const real X        = d_OptionData[optionIndex].X;\n    const real MuByT    = d_OptionData[optionIndex].MuByT;\n    const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;\n\n    // Cycle through the entire samples array:\n    // derive end stock price for each path\n    // accumulate partial integrals into intermediate shared memory buffer\n    for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x)\n    {\n      __TOptionValue sumCall = {0, 0};\n\n      _CCCL_PRAGMA_UNROLL(8)\n      for (int i = iSum; i < pathN; i += SUM_N)\n      {\n        real r         = curand_normal(&localState);\n        real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);\n        sumCall.Expected += callValue;\n        sumCall.Confidence += callValue * callValue;\n      }\n\n      s_SumCall[iSum]  = sumCall.Expected;\n      s_Sum2Call[iSum] = sumCall.Confidence;\n    }\n\n    // Reduce shared memory accumulators\n    // and write final result to global memory\n    cg::sync(cta);\n    sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);\n  }\n}\n\nstatic __global__ void rngSetupStates(curandState* rngState, int device_id)\n{\n  // determine global thread id\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  // Each threadblock gets different seed,\n  // Threads within a threadblock get different sequence numbers\n  curand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU Monte Carlo\n////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Ctx>\nvoid initMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  plan->h_OptionData = new __TOptionData[plan->optionCount];\n  plan->h_CallValue  = new __TOptionValue[plan->optionCount];\n  cuda_safe_call(\n    cudaHostRegister(plan->h_OptionData, plan->optionCount * sizeof(__TOptionData), cudaHostRegisterPortable));\n  cuda_safe_call(\n    cudaHostRegister(plan->h_CallValue, plan->optionCount * sizeof(__TOptionValue), cudaHostRegisterPortable));\n\n  // Register this vector\n  plan->preproc_optionData_handle =\n    ctx.logical_data((__TOptionData*) plan->h_OptionData, plan->optionCount).set_symbol(\"preproc_optionData\");\n  plan->callValue_handle =\n    ctx.logical_data((__TOptionValue*) plan->h_CallValue, plan->optionCount).set_symbol(\"callValue\");\n  plan->rngStates_handle =\n    ctx.logical_data(shape_of<slice<curandState>>(plan->gridSize * THREAD_N)).set_symbol(\"rngStates\");\n\n  cuda_safe_call(cudaSetDevice(plan->device));\n\n  // Allocate states for pseudo random number generators\n  ctx.task(plan->rngStates_handle.write()).set_symbol(\"rngSetupStates\")->*[&](cudaStream_t stream, auto rngStates) {\n    cuda_safe_call(\n      cudaMemsetAsync(rngStates.data_handle(), 0, plan->gridSize * THREAD_N * sizeof(curandState), stream));\n    getLastCudaError(\"cudaMemsetAsync failed.\\n\");\n\n    rngSetupStates<<<plan->gridSize, THREAD_N, 0, stream>>>(rngStates.data_handle(), plan->device);\n    getLastCudaError(\"rngSetupStates kernel failed.\\n\");\n  };\n}\n\n// Compute statistics and deallocate internal device memory\ntemplate <typename Ctx>\nvoid closeMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  ctx.host_launch(plan->callValue_handle.rw()).set_symbol(\"compute_stats\")->*[&](auto h_CallValue) {\n    for (int i = 0; i < plan->optionCount; i++)\n    {\n      const double RT               = plan->optionData[i].R * plan->optionData[i].T;\n      const double sum              = h_CallValue.data_handle()[i].Expected;\n      const double sum2             = h_CallValue.data_handle()[i].Confidence;\n      const double pathN            = plan->pathN;\n      plan->callValue[i].Expected   = (float) (exp(-RT) * sum / pathN);\n      double stdDev                 = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));\n      plan->callValue[i].Confidence = (float) (exp(-RT) * 1.96 * stdDev / sqrt(pathN));\n    }\n  };\n}\n\n// Main computations\ntemplate <typename Ctx>\nvoid MonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS)\n  {\n    printf(\"MonteCarloGPU(): bad option count.\\n\");\n    return;\n  }\n\n  // Preprocess computations on the host\n  ctx.host_launch(plan->preproc_optionData_handle.rw()).set_symbol(\"preprocess\")->*[&](auto h_preproc_OptionData) {\n    for (int i = 0; i < plan->optionCount; i++)\n    {\n      const double T                                 = plan->optionData[i].T;\n      const double R                                 = plan->optionData[i].R;\n      const double V                                 = plan->optionData[i].V;\n      const double MuByT                             = (R - 0.5 * V * V) * T;\n      const double VBySqrtT                          = V * sqrt(T);\n      h_preproc_OptionData.data_handle()[i].S        = (real) plan->optionData[i].S;\n      h_preproc_OptionData.data_handle()[i].X        = (real) plan->optionData[i].X;\n      h_preproc_OptionData.data_handle()[i].MuByT    = (real) MuByT;\n      h_preproc_OptionData.data_handle()[i].VBySqrtT = (real) VBySqrtT;\n    }\n  };\n\n  ctx.task(plan->preproc_optionData_handle.read(), plan->callValue_handle.write(), plan->rngStates_handle.rw())\n      .set_symbol(\"MonteCarloOneBlockPerOption\")\n      ->*[&](cudaStream_t stream, auto preproc_optionData, auto callValue_handle, auto rngStates) {\n            MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(\n              rngStates.data_handle(),\n              preproc_optionData.data_handle(),\n              callValue_handle.data_handle(),\n              plan->pathN,\n              plan->optionCount);\n            getLastCudaError(\"MonteCarloOneBlockPerOption() execution failed\\n\");\n          };\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n////////////////////////////////////////////////////////////////////////////////\n// Global types\n////////////////////////////////////////////////////////////////////////////////\n#include \"hip/hip_runtime.h\"\n#include \"MonteCarlo_reduction.cuh\"\n\n// This will output the proper error string when calling cudaGetLastError\n#define getLastCudaError(msg) __getLastCudaError((msg), __FILE__, __LINE__)\n\nnamespace cg = cooperative_groups;\n\ninline void __getLastCudaError(const char* errorMessage, const char* file, const int line)\n{\n  hipError_t err = hipGetLastError();\n\n  if (hipSuccess != err)\n  {\n    fprintf(stderr,\n            \"%s(%i) : getLastCudaError() CUDA error:\"\n            \" %s : (%d) %s.\\n\",\n            file,\n            line,\n            errorMessage,\n            static_cast<int>(err),\n            hipGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Internal GPU-side data structures\n////////////////////////////////////////////////////////////////////////////////\n#define MAX_OPTIONS (1024 * 1024)\n\n////////////////////////////////////////////////////////////////////////////////\n// Overloaded shortcut payoff functions for different precision modes\n////////////////////////////////////////////////////////////////////////////////\n__device__ inline float endCallValue(float S, float X, float r, float MuByT, float VBySqrtT)\n{\n  float callValue = S * __expf(MuByT + VBySqrtT * r) - X;\n  return (callValue > 0.0F) ? callValue : 0.0F;\n}\n\n__device__ inline double endCallValue(double S, double X, double r, double MuByT, double VBySqrtT)\n{\n  double callValue = S * exp(MuByT + VBySqrtT * r) - X;\n  return (callValue > 0.0) ? callValue : 0.0;\n}\n\n#define THREAD_N 256\n\n////////////////////////////////////////////////////////////////////////////////\n// This kernel computes the integral over all paths using a single thread block\n// per option. It is fastest when the number of thread blocks times the work per\n// block is high enough to keep the GPU busy.\n////////////////////////////////////////////////////////////////////////////////\nstatic __global__ void MonteCarloOneBlockPerOption(\n  hiprandState* __restrict rngStates,\n  const __TOptionData* __restrict d_OptionData,\n  __TOptionValue* __restrict d_CallValue,\n  int pathN,\n  int optionN)\n{\n  // Handle to thread block group\n  cg::thread_block cta             = cg::this_thread_block();\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  const int SUM_N = THREAD_N;\n  __shared__ real s_SumCall[SUM_N];\n  __shared__ real s_Sum2Call[SUM_N];\n\n  // determine global thread id\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  // Copy random number state to local memory for efficiency\n  hiprandState localState = rngStates[tid];\n  for (int optionIndex = blockIdx.x; optionIndex < optionN; optionIndex += gridDim.x)\n  {\n    const real S        = d_OptionData[optionIndex].S;\n    const real X        = d_OptionData[optionIndex].X;\n    const real MuByT    = d_OptionData[optionIndex].MuByT;\n    const real VBySqrtT = d_OptionData[optionIndex].VBySqrtT;\n\n    // Cycle through the entire samples array:\n    // derive end stock price for each path\n    // accumulate partial integrals into intermediate shared memory buffer\n    for (int iSum = threadIdx.x; iSum < SUM_N; iSum += blockDim.x)\n    {\n      __TOptionValue sumCall = {0, 0};\n\n      _CCCL_PRAGMA_UNROLL(8)\n      for (int i = iSum; i < pathN; i += SUM_N)\n      {\n        real r         = hiprand_normal(&localState);\n        real callValue = endCallValue(S, X, r, MuByT, VBySqrtT);\n        sumCall.Expected += callValue;\n        sumCall.Confidence += callValue * callValue;\n      }\n\n      s_SumCall[iSum]  = sumCall.Expected;\n      s_Sum2Call[iSum] = sumCall.Confidence;\n    }\n\n    // Reduce shared memory accumulators\n    // and write final result to global memory\n    cg::sync(cta);\n    sumReduce<real, SUM_N, THREAD_N>(s_SumCall, s_Sum2Call, cta, tile32, &d_CallValue[optionIndex]);\n  }\n}\n\nstatic __global__ void rngSetupStates(hiprandState* rngState, int device_id)\n{\n  // determine global thread id\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  // Each threadblock gets different seed,\n  // Threads within a threadblock get different sequence numbers\n  hiprand_init(blockIdx.x + gridDim.x * device_id, threadIdx.x, 0, &rngState[tid]);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Host-side interface to GPU Monte Carlo\n////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Ctx>\nvoid initMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  plan->h_OptionData = new __TOptionData[plan->optionCount];\n  plan->h_CallValue  = new __TOptionValue[plan->optionCount];\n  cuda_safe_call(\n    hipHostRegister(plan->h_OptionData, plan->optionCount * sizeof(__TOptionData), hipHostRegisterPortable));\n  cuda_safe_call(\n    hipHostRegister(plan->h_CallValue, plan->optionCount * sizeof(__TOptionValue), hipHostRegisterPortable));\n\n  // Register this vector\n  plan->preproc_optionData_handle =\n    ctx.logical_data((__TOptionData*) plan->h_OptionData, plan->optionCount).set_symbol(\"preproc_optionData\");\n  plan->callValue_handle =\n    ctx.logical_data((__TOptionValue*) plan->h_CallValue, plan->optionCount).set_symbol(\"callValue\");\n  plan->rngStates_handle =\n    ctx.logical_data(shape_of<slice<hiprandState>>(plan->gridSize * THREAD_N)).set_symbol(\"rngStates\");\n\n  cuda_safe_call(hipSetDevice(plan->device));\n\n  // Allocate states for pseudo random number generators\n  ctx.task(plan->rngStates_handle.write()).set_symbol(\"rngSetupStates\")->*[&](hipStream_t stream, auto rngStates) {\n    cuda_safe_call(\n      hipMemsetAsync(rngStates.data_handle(), 0, plan->gridSize * THREAD_N * sizeof(hiprandState), stream));\n    getLastCudaError(\"hipMemsetAsync failed.\\n\");\n\n    rngSetupStates<<<plan->gridSize, THREAD_N, 0, stream>>>(rngStates.data_handle(), plan->device);\n    getLastCudaError(\"rngSetupStates kernel failed.\\n\");\n  };\n}\n\n// Compute statistics and deallocate internal device memory\ntemplate <typename Ctx>\nvoid closeMonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  ctx.host_launch(plan->callValue_handle.rw()).set_symbol(\"compute_stats\")->*[&](auto h_CallValue) {\n    for (int i = 0; i < plan->optionCount; i++)\n    {\n      const double RT               = plan->optionData[i].R * plan->optionData[i].T;\n      const double sum              = h_CallValue.data_handle()[i].Expected;\n      const double sum2             = h_CallValue.data_handle()[i].Confidence;\n      const double pathN            = plan->pathN;\n      plan->callValue[i].Expected   = (float) (exp(-RT) * sum / pathN);\n      double stdDev                 = sqrt((pathN * sum2 - sum * sum) / (pathN * (pathN - 1)));\n      plan->callValue[i].Confidence = (float) (exp(-RT) * 1.96 * stdDev / sqrt(pathN));\n    }\n  };\n}\n\n// Main computations\ntemplate <typename Ctx>\nvoid MonteCarloGPU(Ctx& ctx, TOptionPlan* plan)\n{\n  if (plan->optionCount <= 0 || plan->optionCount > MAX_OPTIONS)\n  {\n    printf(\"MonteCarloGPU(): bad option count.\\n\");\n    return;\n  }\n\n  // Preprocess computations on the host\n  ctx.host_launch(plan->preproc_optionData_handle.rw()).set_symbol(\"preprocess\")->*[&](auto h_preproc_OptionData) {\n    for (int i = 0; i < plan->optionCount; i++)\n    {\n      const double T                                 = plan->optionData[i].T;\n      const double R                                 = plan->optionData[i].R;\n      const double V                                 = plan->optionData[i].V;\n      const double MuByT                             = (R - 0.5 * V * V) * T;\n      const double VBySqrtT                          = V * sqrt(T);\n      h_preproc_OptionData.data_handle()[i].S        = (real) plan->optionData[i].S;\n      h_preproc_OptionData.data_handle()[i].X        = (real) plan->optionData[i].X;\n      h_preproc_OptionData.data_handle()[i].MuByT    = (real) MuByT;\n      h_preproc_OptionData.data_handle()[i].VBySqrtT = (real) VBySqrtT;\n    }\n  };\n\n  ctx.task(plan->preproc_optionData_handle.read(), plan->callValue_handle.write(), plan->rngStates_handle.rw())\n      .set_symbol(\"MonteCarloOneBlockPerOption\")\n      ->*[&](hipStream_t stream, auto preproc_optionData, auto callValue_handle, auto rngStates) {\n            MonteCarloOneBlockPerOption<<<plan->gridSize, THREAD_N, 0, stream>>>(\n              rngStates.data_handle(),\n              preproc_optionData.data_handle(),\n              callValue_handle.data_handle(),\n              plan->pathN,\n              plan->optionCount);\n            getLastCudaError(\"MonteCarloOneBlockPerOption() execution failed\\n\");\n          };\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cuda-samples/5_Domain_Specific/MonteCarloMultiGPU_cudastf/MonteCarlo_kernel.cu"}
{"input": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This file demonstrates the usage of conditional graph nodes with\n * a series of *simple* example graphs.\n *\n * For more information on conditional nodes, see the programming guide:\n *\n *   https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#conditional-graph-nodes\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\n#include <cassert>\n#include <cstdio>\n\n#include <cuda_runtime.h>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n\n// This kernel will only be executed if the condition is true\n__global__ void doWhileEmptyKernel(void)\n{\n  printf(\"GPU: doWhileEmptyKernel()\\n\");\n  return;\n}\n\n__global__ void doWhileLoopKernel(char* dPtr, cudaGraphConditionalHandle handle)\n{\n  if (--(*dPtr) == 0)\n  {\n    cudaGraphSetConditional(handle, 0);\n  }\n  printf(\"GPU: counter = %d\\n\", *dPtr);\n}\n\nvoid simpleDoWhileGraph(void)\n{\n  cudaGraph_t graph;\n  cudaGraphExec_t graphExec;\n  cudaGraphNode_t conditionalNode;\n\n  // Allocate a byte of device memory to use as input\n  char* dPtr;\n  cuda_safe_call(cudaMalloc((void**) &dPtr, 1));\n\n  printf(\"simpleDoWhileGraph: Building graph...\\n\");\n  cuda_safe_call(cudaGraphCreate(&graph, 0));\n\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault));\n\n  cudaGraphNodeParams cParams{};\n  cParams.type               = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle = handle;\n  cParams.conditional.type   = cudaGraphCondTypeWhile;\n  cParams.conditional.size   = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, NULL, NULL, 0, &cParams));\n#  else\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, NULL, 0, &cParams));\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  cudaStream_t captureStream;\n  cuda_safe_call(cudaStreamCreate(&captureStream));\n\n  cuda_safe_call(\n    cudaStreamBeginCaptureToGraph(captureStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));\n  doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n  doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n  doWhileLoopKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n  cuda_safe_call(cudaStreamEndCapture(captureStream, nullptr));\n  cuda_safe_call(cudaStreamDestroy(captureStream));\n\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  // Initialize device memory and launch the graph\n  cuda_safe_call(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10\n  printf(\"Host: Launching graph with loop counter set to 10\\n\");\n  cuda_safe_call(cudaGraphLaunch(graphExec, 0));\n  cuda_safe_call(cudaDeviceSynchronize());\n\n  // Cleanup\n  cuda_safe_call(cudaGraphExecDestroy(graphExec));\n  cuda_safe_call(cudaGraphDestroy(graph));\n  cuda_safe_call(cudaFree(dPtr));\n\n  printf(\"simpleDoWhileGraph: Complete\\n\\n\");\n}\n\nvoid stf_dowhile()\n{\n  stackable_ctx ctx;\n\n  {\n    auto repeat_guard = ctx.repeat_graph_scope(10);\n\n    ctx.task()->*[](cudaStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  ctx.finalize();\n  printf(\"STF do while complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a conditional while loop using stream capture.\n * This demonstrates how to insert a conditional node into a stream which is\n * being captured. The graph consists of a kernel node, A, followed by a\n * conditional while node, B, followed by a kernel node, D. The conditional\n * body is populated by a single kernel node, C:\n *\n * A -> B [ C ] -> D\n *\n * The same kernel will be used for both nodes A and C. This kernel will test\n * a device memory location and set the condition when the location is non-zero.\n * We must run the kernel before the loop as well as inside the loop in order\n * to behave like a while loop as opposed to a do-while loop. We need to evaluate\n * the device memory location before the conditional node is evaluated in order\n * to set the condition variable properly. Because we're using a kernel upstream\n * of the conditional node, there is no need to use the handle default value to\n * initialize the conditional value.\n */\n\n__global__ void capturedWhileKernel(char* dPtr, cudaGraphConditionalHandle handle)\n{\n  printf(\"GPU: counter = %d\\n\", *dPtr);\n  if (*dPtr)\n  {\n    (*dPtr)--;\n  }\n  cudaGraphSetConditional(handle, *dPtr);\n}\n\n__global__ void capturedWhileEmptyKernel(void)\n{\n  printf(\"GPU: capturedWhileEmptyKernel()\\n\");\n  return;\n}\n\nvoid capturedWhileGraph(void)\n{\n  cudaGraph_t graph;\n  cudaGraphExec_t graphExec;\n\n  cudaStreamCaptureStatus status;\n  const cudaGraphNode_t* dependencies;\n  size_t numDependencies;\n\n  // Allocate a byte of device memory to use as input\n  char* dPtr;\n  cuda_safe_call(cudaMalloc((void**) &dPtr, 1));\n\n  printf(\"capturedWhileGraph: Building graph...\\n\");\n  cudaStream_t captureStream;\n  cuda_safe_call(cudaStreamCreate(&captureStream));\n\n  cuda_safe_call(cudaStreamBeginCapture(captureStream, cudaStreamCaptureModeGlobal));\n\n  // Obtain the handle of the graph\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n#  else\n  cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));\n#  endif\n\n  // Create the conditional handle\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph));\n\n  // Insert kernel node A\n  capturedWhileKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n\n  // Obtain the handle for node A\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n#  else\n  cuda_safe_call(cudaStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));\n#  endif\n\n  // Insert conditional node B\n  cudaGraphNode_t conditionalNode;\n  cudaGraphNodeParams cParams{};\n  cParams.type               = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle = handle;\n  cParams.conditional.type   = cudaGraphCondTypeWhile;\n  cParams.conditional.size   = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, dependencies, NULL, numDependencies, &cParams));\n#  else\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, dependencies, numDependencies, &cParams));\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // Update stream capture dependencies to account for the node we manually added\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(\n    cudaStreamUpdateCaptureDependencies(captureStream, &conditionalNode, NULL, 1, cudaStreamSetCaptureDependencies));\n#  else\n  cuda_safe_call(\n    cudaStreamUpdateCaptureDependencies(captureStream, &conditionalNode, 1, cudaStreamSetCaptureDependencies));\n#  endif\n\n  // Insert kernel node D\n  capturedWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n\n  cuda_safe_call(cudaStreamEndCapture(captureStream, &graph));\n  cuda_safe_call(cudaStreamDestroy(captureStream));\n\n  // Populate conditional body graph using stream capture\n  cudaStream_t bodyStream;\n  cuda_safe_call(cudaStreamCreate(&bodyStream));\n\n  cuda_safe_call(\n    cudaStreamBeginCaptureToGraph(bodyStream, bodyGraph, nullptr, nullptr, 0, cudaStreamCaptureModeGlobal));\n\n  // Insert kernel node C\n  capturedWhileKernel<<<1, 1, 0, bodyStream>>>(dPtr, handle);\n  cuda_safe_call(cudaStreamEndCapture(bodyStream, nullptr));\n  cuda_safe_call(cudaStreamDestroy(bodyStream));\n\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  // Initialize device memory and launch the graph\n  // Device memory is zero, so the conditional node will not execute\n  cuda_safe_call(cudaMemset(dPtr, 0, 1)); // Set dPtr to 0\n  printf(\"Host: Launching graph with loop counter set to 0\\n\");\n  cuda_safe_call(cudaGraphLaunch(graphExec, 0));\n  cuda_safe_call(cudaDeviceSynchronize());\n\n  // Initialize device memory and launch the graph\n  cuda_safe_call(cudaMemset(dPtr, 10, 1)); // Set dPtr to 10\n  printf(\"Host: Launching graph with loop counter set to 10\\n\");\n  cuda_safe_call(cudaGraphLaunch(graphExec, 0));\n  cuda_safe_call(cudaDeviceSynchronize());\n\n  // Cleanup\n  cuda_safe_call(cudaGraphExecDestroy(graphExec));\n  cuda_safe_call(cudaGraphDestroy(graph));\n  cuda_safe_call(cudaFree(dPtr));\n\n  printf(\"capturedWhileGraph: Complete\\n\\n\");\n}\n\nvoid stf_dowhile_2()\n{\n  stackable_ctx ctx;\n\n  {\n    // We force everything to be a CUDA graph\n    auto scope = ctx.graph_scope();\n\n    // We use a token to ensure that A, B(C) and D are serialized\n    auto t = ctx.token();\n\n    // A\n    ctx.task(t.rw())->*[](cudaStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n\n    // B\n    {\n      auto repeat_guard = ctx.repeat_graph_scope(10);\n\n      // C\n      ctx.task(t.rw())->*[](cudaStream_t stream) {\n        doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n      };\n    }\n\n    // D\n    ctx.task(t.rw())->*[](cudaStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  ctx.finalize();\n}\n\nvoid stf_dowhile_2_cuda_kernel()\n{\n  stackable_ctx ctx;\n\n  {\n    // We force everything to be a CUDA graph\n    auto scope = ctx.graph_scope();\n\n    // We use a token to ensure that A, B(C) and D are serialized\n    auto t = ctx.token();\n\n    // A\n    ctx.cuda_kernel(t.rw())->*[]() {\n      return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n    };\n\n    // B\n    {\n      auto repeat_guard = ctx.repeat_graph_scope(10);\n\n      // C\n      ctx.cuda_kernel(t.rw())->*[]() {\n        return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n      };\n    }\n\n    // D\n    ctx.cuda_kernel(t.rw())->*[]() {\n      return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n    };\n  }\n\n  ctx.finalize();\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main(int, char**)\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n  return 0;\n#else\n  simpleDoWhileGraph();\n  stf_dowhile();\n\n  capturedWhileGraph();\n  stf_dowhile_2();\n\n  // same as stf_dowhile_2 but uses cuda_kernel\n  stf_dowhile_2_cuda_kernel();\n\n  return 0;\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n}\n", "output": "/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*\n * This file demonstrates the usage of conditional graph nodes with\n * a series of *simple* example graphs.\n *\n * For more information on conditional nodes, see the programming guide:\n *\n *   https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#conditional-graph-nodes\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\n#include <cassert>\n#include <cstdio>\n\n#include <hip/hip_runtime.h>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n\n// This kernel will only be executed if the condition is true\n__global__ void doWhileEmptyKernel(void)\n{\n  printf(\"GPU: doWhileEmptyKernel()\\n\");\n  return;\n}\n\n__global__ void doWhileLoopKernel(char* dPtr, cudaGraphConditionalHandle handle)\n{\n  if (--(*dPtr) == 0)\n  {\n    cudaGraphSetConditional(handle, 0);\n  }\n  printf(\"GPU: counter = %d\\n\", *dPtr);\n}\n\nvoid simpleDoWhileGraph(void)\n{\n  hipGraph_t graph;\n  hipGraphExec_t graphExec;\n  hipGraphNode_t conditionalNode;\n\n  // Allocate a byte of device memory to use as input\n  char* dPtr;\n  cuda_safe_call(hipMalloc((void**) &dPtr, 1));\n\n  printf(\"simpleDoWhileGraph: Building graph...\\n\");\n  cuda_safe_call(hipGraphCreate(&graph, 0));\n\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault));\n\n  hipGraphNodeParams cParams{};\n  cParams.type               = hipGraphNodeTypeConditional;\n  cParams.conditional.handle = handle;\n  cParams.conditional.type   = cudaGraphCondTypeWhile;\n  cParams.conditional.size   = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, NULL, NULL, 0, &cParams));\n#  else\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, NULL, 0, &cParams));\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  hipStream_t captureStream;\n  cuda_safe_call(hipStreamCreate(&captureStream));\n\n  cuda_safe_call(\n    hipStreamBeginCaptureToGraph(captureStream, bodyGraph, nullptr, nullptr, 0, hipStreamCaptureModeGlobal));\n  doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n  doWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n  doWhileLoopKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n  cuda_safe_call(hipStreamEndCapture(captureStream, nullptr));\n  cuda_safe_call(hipStreamDestroy(captureStream));\n\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  // Initialize device memory and launch the graph\n  cuda_safe_call(hipMemset(dPtr, 10, 1)); // Set dPtr to 10\n  printf(\"Host: Launching graph with loop counter set to 10\\n\");\n  cuda_safe_call(hipGraphLaunch(graphExec, 0));\n  cuda_safe_call(hipDeviceSynchronize());\n\n  // Cleanup\n  cuda_safe_call(hipGraphExecDestroy(graphExec));\n  cuda_safe_call(hipGraphDestroy(graph));\n  cuda_safe_call(hipFree(dPtr));\n\n  printf(\"simpleDoWhileGraph: Complete\\n\\n\");\n}\n\nvoid stf_dowhile()\n{\n  stackable_ctx ctx;\n\n  {\n    auto repeat_guard = ctx.repeat_graph_scope(10);\n\n    ctx.task()->*[](hipStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  ctx.finalize();\n  printf(\"STF do while complete\\n\\n\");\n}\n\n/*\n * Create a graph containing a conditional while loop using stream capture.\n * This demonstrates how to insert a conditional node into a stream which is\n * being captured. The graph consists of a kernel node, A, followed by a\n * conditional while node, B, followed by a kernel node, D. The conditional\n * body is populated by a single kernel node, C:\n *\n * A -> B [ C ] -> D\n *\n * The same kernel will be used for both nodes A and C. This kernel will test\n * a device memory location and set the condition when the location is non-zero.\n * We must run the kernel before the loop as well as inside the loop in order\n * to behave like a while loop as opposed to a do-while loop. We need to evaluate\n * the device memory location before the conditional node is evaluated in order\n * to set the condition variable properly. Because we're using a kernel upstream\n * of the conditional node, there is no need to use the handle default value to\n * initialize the conditional value.\n */\n\n__global__ void capturedWhileKernel(char* dPtr, cudaGraphConditionalHandle handle)\n{\n  printf(\"GPU: counter = %d\\n\", *dPtr);\n  if (*dPtr)\n  {\n    (*dPtr)--;\n  }\n  cudaGraphSetConditional(handle, *dPtr);\n}\n\n__global__ void capturedWhileEmptyKernel(void)\n{\n  printf(\"GPU: capturedWhileEmptyKernel()\\n\");\n  return;\n}\n\nvoid capturedWhileGraph(void)\n{\n  hipGraph_t graph;\n  hipGraphExec_t graphExec;\n\n  hipStreamCaptureStatus status;\n  const hipGraphNode_t* dependencies;\n  size_t numDependencies;\n\n  // Allocate a byte of device memory to use as input\n  char* dPtr;\n  cuda_safe_call(hipMalloc((void**) &dPtr, 1));\n\n  printf(\"capturedWhileGraph: Building graph...\\n\");\n  hipStream_t captureStream;\n  cuda_safe_call(hipStreamCreate(&captureStream));\n\n  cuda_safe_call(hipStreamBeginCapture(captureStream, hipStreamCaptureModeGlobal));\n\n  // Obtain the handle of the graph\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n#  else\n  cuda_safe_call(hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));\n#  endif\n\n  // Create the conditional handle\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, graph));\n\n  // Insert kernel node A\n  capturedWhileKernel<<<1, 1, 0, captureStream>>>(dPtr, handle);\n\n  // Obtain the handle for node A\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, NULL, &numDependencies));\n#  else\n  cuda_safe_call(hipStreamGetCaptureInfo(captureStream, &status, NULL, &graph, &dependencies, &numDependencies));\n#  endif\n\n  // Insert conditional node B\n  hipGraphNode_t conditionalNode;\n  hipGraphNodeParams cParams{};\n  cParams.type               = hipGraphNodeTypeConditional;\n  cParams.conditional.handle = handle;\n  cParams.conditional.type   = cudaGraphCondTypeWhile;\n  cParams.conditional.size   = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, dependencies, NULL, numDependencies, &cParams));\n#  else\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, dependencies, numDependencies, &cParams));\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // Update stream capture dependencies to account for the node we manually added\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(\n    hipStreamUpdateCaptureDependencies(captureStream, &conditionalNode, NULL, 1, hipStreamSetCaptureDependencies));\n#  else\n  cuda_safe_call(\n    hipStreamUpdateCaptureDependencies(captureStream, &conditionalNode, 1, hipStreamSetCaptureDependencies));\n#  endif\n\n  // Insert kernel node D\n  capturedWhileEmptyKernel<<<1, 1, 0, captureStream>>>();\n\n  cuda_safe_call(hipStreamEndCapture(captureStream, &graph));\n  cuda_safe_call(hipStreamDestroy(captureStream));\n\n  // Populate conditional body graph using stream capture\n  hipStream_t bodyStream;\n  cuda_safe_call(hipStreamCreate(&bodyStream));\n\n  cuda_safe_call(\n    hipStreamBeginCaptureToGraph(bodyStream, bodyGraph, nullptr, nullptr, 0, hipStreamCaptureModeGlobal));\n\n  // Insert kernel node C\n  capturedWhileKernel<<<1, 1, 0, bodyStream>>>(dPtr, handle);\n  cuda_safe_call(hipStreamEndCapture(bodyStream, nullptr));\n  cuda_safe_call(hipStreamDestroy(bodyStream));\n\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  // Initialize device memory and launch the graph\n  // Device memory is zero, so the conditional node will not execute\n  cuda_safe_call(hipMemset(dPtr, 0, 1)); // Set dPtr to 0\n  printf(\"Host: Launching graph with loop counter set to 0\\n\");\n  cuda_safe_call(hipGraphLaunch(graphExec, 0));\n  cuda_safe_call(hipDeviceSynchronize());\n\n  // Initialize device memory and launch the graph\n  cuda_safe_call(hipMemset(dPtr, 10, 1)); // Set dPtr to 10\n  printf(\"Host: Launching graph with loop counter set to 10\\n\");\n  cuda_safe_call(hipGraphLaunch(graphExec, 0));\n  cuda_safe_call(hipDeviceSynchronize());\n\n  // Cleanup\n  cuda_safe_call(hipGraphExecDestroy(graphExec));\n  cuda_safe_call(hipGraphDestroy(graph));\n  cuda_safe_call(hipFree(dPtr));\n\n  printf(\"capturedWhileGraph: Complete\\n\\n\");\n}\n\nvoid stf_dowhile_2()\n{\n  stackable_ctx ctx;\n\n  {\n    // We force everything to be a CUDA graph\n    auto scope = ctx.graph_scope();\n\n    // We use a token to ensure that A, B(C) and D are serialized\n    auto t = ctx.token();\n\n    // A\n    ctx.task(t.rw())->*[](hipStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n\n    // B\n    {\n      auto repeat_guard = ctx.repeat_graph_scope(10);\n\n      // C\n      ctx.task(t.rw())->*[](hipStream_t stream) {\n        doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n      };\n    }\n\n    // D\n    ctx.task(t.rw())->*[](hipStream_t stream) {\n      doWhileEmptyKernel<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  ctx.finalize();\n}\n\nvoid stf_dowhile_2_cuda_kernel()\n{\n  stackable_ctx ctx;\n\n  {\n    // We force everything to be a CUDA graph\n    auto scope = ctx.graph_scope();\n\n    // We use a token to ensure that A, B(C) and D are serialized\n    auto t = ctx.token();\n\n    // A\n    ctx.cuda_kernel(t.rw())->*[]() {\n      return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n    };\n\n    // B\n    {\n      auto repeat_guard = ctx.repeat_graph_scope(10);\n\n      // C\n      ctx.cuda_kernel(t.rw())->*[]() {\n        return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n      };\n    }\n\n    // D\n    ctx.cuda_kernel(t.rw())->*[]() {\n      return cuda_kernel_desc{doWhileEmptyKernel, 1, 1, 0};\n    };\n  }\n\n  ctx.finalize();\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main(int, char**)\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n  return 0;\n#else\n  simpleDoWhileGraph();\n  stf_dowhile();\n\n  capturedWhileGraph();\n  stf_dowhile_2();\n\n  // same as stf_dowhile_2 but uses cuda_kernel\n  stf_dowhile_2_cuda_kernel();\n\n  return 0;\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cuda-samples/3_CUDA_Features/graphConditionalNodes/graphConditionalNodes.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample demonstrates Instantiated CUDA Graph Update\n// with Jacobi Iterative Method in 3 different methods:\n// 1 - JacobiMethodGpuCudaGraphExecKernelSetParams() - CUDA Graph with\n// cudaGraphExecKernelNodeSetParams() 2 - JacobiMethodGpuCudaGraphExecUpdate() -\n// CUDA Graph with cudaGraphExecUpdate() 3 - JacobiMethodGpu() - Non CUDA Graph\n// method\n\n// Jacobi method on a linear system A*x = b,\n// where A is diagonally dominant and the exact solution consists\n// of all ones.\n\n#include <cuda/experimental/__stf/utility/cuda_safe_call.cuh>\n\n#include <cooperative_groups.h>\n\nusing cuda::experimental::stf::cuda_safe_call;\n\n#define N_ROWS 512\n\nnamespace cg = cooperative_groups;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600\n#else\n__device__ double atomicAdd(double* address, double val)\n{\n  unsigned long long int* address_as_ull = (unsigned long long int*) address;\n  unsigned long long int old             = *address_as_ull, assumed;\n\n  do\n  {\n    assumed = old;\n    old     = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));\n\n    // Note: uses integer comparison to avoid hang in case of NaN (since NaN !=\n    // NaN)\n  } while (assumed != old);\n\n  return __longlong_as_double(old);\n}\n#endif\n\n// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1\n// elsewhere. The elements of the right hand side b all equal 2*n, hence the\n// exact solution x to A*x = b is a vector of ones.\nvoid createLinearSystem(float* A, double* b)\n{\n  int i, j;\n  for (i = 0; i < N_ROWS; i++)\n  {\n    b[i] = 2.0 * N_ROWS;\n    for (j = 0; j < N_ROWS; j++)\n    {\n      A[i * N_ROWS + j] = 1.0;\n    }\n    A[i * N_ROWS + i] = N_ROWS + 1.0;\n  }\n}\n\nstatic __global__ void\nJacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n  __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n  for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)\n  {\n    x_shared[i] = x[i];\n  }\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    int k = threadIdx.x;\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n    }\n  }\n\n  cg::sync(cta);\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)\n  {\n    double rowThreadSum = 0.0;\n    for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)\n    {\n      rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n    }\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n    }\n\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n    }\n  }\n\n  cg::sync(cta);\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);\n    double temp_sum                           = 0.0;\n\n    int k = threadIdx.x;\n\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n      dx /= A[i * N_ROWS + i];\n\n      x_new[i] = (x_shared[i] + dx);\n      temp_sum += fabs(dx);\n    }\n\n    for (int offset = tile8.size() / 2; offset > 0; offset /= 2)\n    {\n      temp_sum += tile8.shfl_down(temp_sum, offset);\n    }\n\n    if (tile8.thread_rank() == 0)\n    {\n      atomicAdd(sum, temp_sum);\n    }\n  }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(double* x, double* g_sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  extern __shared__ double warpSum[];\n  double sum = 0.0;\n\n  int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n  for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n  {\n    sum += tile32.shfl_down(sum, offset);\n  }\n\n  if (tile32.thread_rank() == 0)\n  {\n    warpSum[threadIdx.x / warpSize] = sum;\n  }\n\n  cg::sync(cta);\n\n  double blockSum = 0.0;\n  if (threadIdx.x < (blockDim.x / warpSize))\n  {\n    blockSum = warpSum[threadIdx.x];\n  }\n\n  if (threadIdx.x < 32)\n  {\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      blockSum += tile32.shfl_down(blockSum, offset);\n    }\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(g_sum, blockSum);\n    }\n  }\n}\n\n// Run the Jacobi method for A*x = b on GPU with CUDA Graph -\n// cudaGraphExecKernelNodeSetParams().\ndouble JacobiMethodGpuCudaGraphExecKernelSetParams(\n  const float* A,\n  const double* b,\n  float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  cudaStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n  cudaGraph_t graph;\n  cudaGraphExec_t graphExec = NULL;\n\n  double sum    = 0.0;\n  double* d_sum = NULL;\n  cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));\n\n  std::vector<cudaGraphNode_t> nodeDependencies;\n  cudaGraphNode_t memcpyNode, jacobiKernelNode, memsetNode;\n  cudaMemcpy3DParms memcpyParams;\n  cudaMemsetParams memsetParams;\n\n  memsetParams.dst   = (void*) d_sum;\n  memsetParams.value = 0;\n  memsetParams.pitch = 0;\n  // elementSize can be max 4 bytes, so we take sizeof(float) and width=2\n  memsetParams.elementSize = sizeof(float);\n  memsetParams.width       = 2;\n  memsetParams.height      = 1;\n\n  cuda_safe_call(cudaGraphCreate(&graph, 0));\n  cuda_safe_call(cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n  nodeDependencies.push_back(memsetNode);\n\n  cudaKernelNodeParams NodeParams0, NodeParams1;\n  NodeParams0.func           = (void*) JacobiMethod;\n  NodeParams0.gridDim        = nblocks;\n  NodeParams0.blockDim       = nthreads;\n  NodeParams0.sharedMemBytes = 0;\n  void* kernelArgs0[6]       = {\n    (void*) &A,\n    const_cast<double**>(&b),\n    const_cast<float*>(&conv_threshold),\n    (void*) &x,\n    (void*) &x_new,\n    (void*) &d_sum};\n  NodeParams0.kernelParams = kernelArgs0;\n  NodeParams0.extra        = NULL;\n\n  cuda_safe_call(\n    cudaGraphAddKernelNode(&jacobiKernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &NodeParams0));\n\n  nodeDependencies.clear();\n  nodeDependencies.push_back(jacobiKernelNode);\n\n  memcpyParams.srcArray = NULL;\n  memcpyParams.srcPos   = make_cudaPos(0, 0, 0);\n  memcpyParams.srcPtr   = make_cudaPitchedPtr(d_sum, sizeof(double), 1, 1);\n  memcpyParams.dstArray = NULL;\n  memcpyParams.dstPos   = make_cudaPos(0, 0, 0);\n  memcpyParams.dstPtr   = make_cudaPitchedPtr(&sum, sizeof(double), 1, 1);\n  memcpyParams.extent   = make_cudaExtent(sizeof(double), 1, 1);\n  memcpyParams.kind     = cudaMemcpyDeviceToHost;\n\n  cuda_safe_call(\n    cudaGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  NodeParams1.func           = (void*) JacobiMethod;\n  NodeParams1.gridDim        = nblocks;\n  NodeParams1.blockDim       = nthreads;\n  NodeParams1.sharedMemBytes = 0;\n  void* kernelArgs1[6]       = {\n    (void*) &A,\n    const_cast<double**>(&b),\n    const_cast<float*>(&conv_threshold),\n    (void*) &x_new,\n    (void*) &x,\n    (void*) &d_sum};\n  NodeParams1.kernelParams = kernelArgs1;\n  NodeParams1.extra        = NULL;\n\n  int k = 0;\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(\n      cudaGraphExecKernelNodeSetParams(graphExec, jacobiKernelNode, ((k & 1) == 0) ? &NodeParams0 : &NodeParams1));\n    cuda_safe_call(cudaGraphLaunch(graphExec, stream));\n    cuda_safe_call(cudaStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(cudaFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on GPU with Instantiated CUDA Graph Update\n// API - cudaGraphExecUpdate().\ndouble JacobiMethodGpuCudaGraphExecUpdate(\n  const float* A,\n  const double* b,\n  const float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  cudaStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n  cudaGraph_t graph;\n  cudaGraphExec_t graphExec = NULL;\n\n  double sum = 0.0;\n  double* d_sum;\n  cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));\n\n  int k = 0;\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal));\n    cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n    if ((k & 1) == 0)\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n    }\n    else\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n    }\n    cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n    cuda_safe_call(cudaStreamEndCapture(stream, &graph));\n\n    if (graphExec == NULL)\n    {\n      cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n    }\n    else\n    {\n      cudaGraphExecUpdateResult updateResult_out;\n      cuda_safe_call(cudaGraphExecUpdate(graphExec, graph, NULL, &updateResult_out));\n      if (updateResult_out != cudaGraphExecUpdateSuccess)\n      {\n        if (graphExec != NULL)\n        {\n          cuda_safe_call(cudaGraphExecDestroy(graphExec));\n        }\n        printf(\"k = %d graph update failed with error - %d\\n\", k, updateResult_out);\n        cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n      }\n    }\n    cuda_safe_call(cudaGraphLaunch(graphExec, stream));\n    cuda_safe_call(cudaStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(cudaFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on GPU without CUDA Graph.\ndouble JacobiMethodGpu(\n  const float* A,\n  const double* b,\n  const float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  cudaStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n  double sum = 0.0;\n  double* d_sum;\n  cuda_safe_call(cudaMalloc(&d_sum, sizeof(double)));\n  int k = 0;\n\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n    if ((k & 1) == 0)\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n    }\n    else\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n    }\n    cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n    cuda_safe_call(cudaStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(cudaMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(cudaMemcpyAsync(&sum, d_sum, sizeof(double), cudaMemcpyDeviceToHost, stream));\n      cuda_safe_call(cudaStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(cudaFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on CPU.\nvoid JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)\n{\n  double* x_new;\n  x_new = (double*) calloc(N_ROWS, sizeof(double));\n  int k;\n\n  for (k = 0; k < max_iter; k++)\n  {\n    double sum = 0.0;\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      double temp_dx = b[i];\n      for (int j = 0; j < N_ROWS; j++)\n      {\n        temp_dx -= A[i * N_ROWS + j] * x[j];\n      }\n      temp_dx /= A[i * N_ROWS + i];\n      x_new[i] += temp_dx;\n      sum += fabs(temp_dx);\n    }\n\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      x[i] = x_new[i];\n    }\n\n    if (sum <= conv_threshold)\n    {\n      break;\n    }\n  }\n  *num_iter = k + 1;\n  free(x_new);\n}\n\nint main()\n{\n  //  if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n  //    printf(\"Command line: jacobiCudaGraphs [-option]\\n\");\n  //    printf(\"Valid options:\\n\");\n  //    printf(\n  //        \"-gpumethod=<0,1 or 2>  : 0 - [Default] \"\n  //        \"JacobiMethodGpuCudaGraphExecKernelSetParams\\n\");\n  //    printf(\"                       : 1 - JacobiMethodGpuCudaGraphExecUpdate\\n\");\n  //    printf(\"                       : 2 - JacobiMethodGpu - Non CUDA Graph\\n\");\n  //    printf(\"-device=device_num     : cuda device id\");\n  //    printf(\"-help         : Output a help message\\n\");\n  //    exit(EXIT_SUCCESS);\n  //  }\n  //\n  int gpumethod = 0;\n  //  if (checkCmdLineFlag(argc, (const char **)argv, \"gpumethod\")) {\n  //    gpumethod = getCmdLineArgumentInt(argc, (const char **)argv, \"gpumethod\");\n  //\n  //    if (gpumethod < 0 || gpumethod > 2) {\n  //      printf(\"Error: gpumethod must be 0 or 1 or 2, gpumethod=%d is invalid\\n\",\n  //             gpumethod);\n  //      exit(EXIT_SUCCESS);\n  //    }\n  //  }\n\n  //  int dev = findCudaDevice(argc, (const char **)argv);\n  // int dev = 0;\n\n  double* b = NULL;\n  float* A  = NULL;\n  cuda_safe_call(cudaMallocHost(&b, N_ROWS * sizeof(double)));\n  memset(b, 0, N_ROWS * sizeof(double));\n  cuda_safe_call(cudaMallocHost(&A, N_ROWS * N_ROWS * sizeof(float)));\n  memset(A, 0, N_ROWS * N_ROWS * sizeof(float));\n\n  createLinearSystem(A, b);\n  double* x = NULL;\n  // start with array of all zeroes\n  x = (double*) calloc(N_ROWS, sizeof(double));\n\n  float conv_threshold = 1.0e-2;\n  int max_iter         = 4 * N_ROWS * N_ROWS;\n  int cnt              = 0;\n\n  //  // create timer\n  //  StopWatchInterface *timerCPU = NULL, *timerGpu = NULL;\n  //  sdkCreateTimer(&timerCPU);\n  //\n  //  sdkStartTimer(&timerCPU);\n  JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);\n\n  double sum = 0.0;\n  // Compute error\n  for (int i = 0; i < N_ROWS; i++)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n  //  sdkStopTimer(&timerCPU);\n  // printf(\"CPU iterations : %d\\n\", cnt);\n  // printf(\"CPU error: %.3e\\n\", sum);\n  //  printf(\"CPU Processing time: %f (ms)\\n\", sdkGetTimerValue(&timerCPU));\n\n  float* d_A;\n  double *d_b, *d_x, *d_x_new;\n  cudaStream_t stream1;\n  cuda_safe_call(cudaStreamCreateWithFlags(&stream1, cudaStreamNonBlocking));\n  cuda_safe_call(cudaMalloc(&d_b, sizeof(double) * N_ROWS));\n  cuda_safe_call(cudaMalloc(&d_A, sizeof(float) * N_ROWS * N_ROWS));\n  cuda_safe_call(cudaMalloc(&d_x, sizeof(double) * N_ROWS));\n  cuda_safe_call(cudaMalloc(&d_x_new, sizeof(double) * N_ROWS));\n\n  cuda_safe_call(cudaMemsetAsync(d_x, 0, sizeof(double) * N_ROWS, stream1));\n  cuda_safe_call(cudaMemsetAsync(d_x_new, 0, sizeof(double) * N_ROWS, stream1));\n  cuda_safe_call(cudaMemcpyAsync(d_A, A, sizeof(float) * N_ROWS * N_ROWS, cudaMemcpyHostToDevice, stream1));\n  cuda_safe_call(cudaMemcpyAsync(d_b, b, sizeof(double) * N_ROWS, cudaMemcpyHostToDevice, stream1));\n\n  //  sdkCreateTimer(&timerGpu);\n  //  sdkStartTimer(&timerGpu);\n\n  double sumGPU = 0.0;\n  if (gpumethod == 0)\n  {\n    sumGPU = JacobiMethodGpuCudaGraphExecKernelSetParams(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n  else if (gpumethod == 1)\n  {\n    sumGPU = JacobiMethodGpuCudaGraphExecUpdate(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n  else if (gpumethod == 2)\n  {\n    sumGPU = JacobiMethodGpu(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n\n  //  sdkStopTimer(&timerGpu);\n  //  printf(\"GPU Processing time: %f (ms)\\n\", sdkGetTimerValue(&timerGpu));\n\n  cuda_safe_call(cudaFree(d_b));\n  cuda_safe_call(cudaFree(d_A));\n  cuda_safe_call(cudaFree(d_x));\n  cuda_safe_call(cudaFree(d_x_new));\n\n  cuda_safe_call(cudaFreeHost(A));\n  cuda_safe_call(cudaFreeHost(b));\n\n  // printf(\"&&&& jacobiCudaGraphs %s\\n\", (fabs(sum - sumGPU) < conv_threshold) ? \"PASSED\" : \"FAILED\");\n\n  return (fabs(sum - sumGPU) < conv_threshold) ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// This sample demonstrates Instantiated CUDA Graph Update\n// with Jacobi Iterative Method in 3 different methods:\n// 1 - JacobiMethodGpuCudaGraphExecKernelSetParams() - CUDA Graph with\n// cudaGraphExecKernelNodeSetParams() 2 - JacobiMethodGpuCudaGraphExecUpdate() -\n// CUDA Graph with cudaGraphExecUpdate() 3 - JacobiMethodGpu() - Non CUDA Graph\n// method\n\n// Jacobi method on a linear system A*x = b,\n// where A is diagonally dominant and the exact solution consists\n// of all ones.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/utility/cuda_safe_call.cuh>\n\n#include <hip/hip_cooperative_groups.h>\n\nusing cuda::experimental::stf::cuda_safe_call;\n\n#define N_ROWS 512\n\nnamespace cg = cooperative_groups;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600\n#else\n__device__ double atomicAdd(double* address, double val)\n{\n  unsigned long long int* address_as_ull = (unsigned long long int*) address;\n  unsigned long long int old             = *address_as_ull, assumed;\n\n  do\n  {\n    assumed = old;\n    old     = atomicCAS(address_as_ull, assumed, __double_as_longlong(val + __longlong_as_double(assumed)));\n\n    // Note: uses integer comparison to avoid hang in case of NaN (since NaN !=\n    // NaN)\n  } while (assumed != old);\n\n  return __longlong_as_double(old);\n}\n#endif\n\n// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1\n// elsewhere. The elements of the right hand side b all equal 2*n, hence the\n// exact solution x to A*x = b is a vector of ones.\nvoid createLinearSystem(float* A, double* b)\n{\n  int i, j;\n  for (i = 0; i < N_ROWS; i++)\n  {\n    b[i] = 2.0 * N_ROWS;\n    for (j = 0; j < N_ROWS; j++)\n    {\n      A[i * N_ROWS + j] = 1.0;\n    }\n    A[i * N_ROWS + i] = N_ROWS + 1.0;\n  }\n}\n\nstatic __global__ void\nJacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n  __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n  for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)\n  {\n    x_shared[i] = x[i];\n  }\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    int k = threadIdx.x;\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n    }\n  }\n\n  cg::sync(cta);\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)\n  {\n    double rowThreadSum = 0.0;\n    for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)\n    {\n      rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n    }\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n    }\n\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n    }\n  }\n\n  cg::sync(cta);\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);\n    double temp_sum                           = 0.0;\n\n    int k = threadIdx.x;\n\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n      dx /= A[i * N_ROWS + i];\n\n      x_new[i] = (x_shared[i] + dx);\n      temp_sum += fabs(dx);\n    }\n\n    for (int offset = tile8.size() / 2; offset > 0; offset /= 2)\n    {\n      temp_sum += tile8.shfl_down(temp_sum, offset);\n    }\n\n    if (tile8.thread_rank() == 0)\n    {\n      atomicAdd(sum, temp_sum);\n    }\n  }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(double* x, double* g_sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  extern __shared__ double warpSum[];\n  double sum = 0.0;\n\n  int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n  for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n  {\n    sum += tile32.shfl_down(sum, offset);\n  }\n\n  if (tile32.thread_rank() == 0)\n  {\n    warpSum[threadIdx.x / warpSize] = sum;\n  }\n\n  cg::sync(cta);\n\n  double blockSum = 0.0;\n  if (threadIdx.x < (blockDim.x / warpSize))\n  {\n    blockSum = warpSum[threadIdx.x];\n  }\n\n  if (threadIdx.x < 32)\n  {\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      blockSum += tile32.shfl_down(blockSum, offset);\n    }\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(g_sum, blockSum);\n    }\n  }\n}\n\n// Run the Jacobi method for A*x = b on GPU with CUDA Graph -\n// cudaGraphExecKernelNodeSetParams().\ndouble JacobiMethodGpuCudaGraphExecKernelSetParams(\n  const float* A,\n  const double* b,\n  float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  hipStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n  hipGraph_t graph;\n  hipGraphExec_t graphExec = NULL;\n\n  double sum    = 0.0;\n  double* d_sum = NULL;\n  cuda_safe_call(hipMalloc(&d_sum, sizeof(double)));\n\n  std::vector<hipGraphNode_t> nodeDependencies;\n  hipGraphNode_t memcpyNode, jacobiKernelNode, memsetNode;\n  hipMemcpy3DParms memcpyParams;\n  hipMemsetParams memsetParams;\n\n  memsetParams.dst   = (void*) d_sum;\n  memsetParams.value = 0;\n  memsetParams.pitch = 0;\n  // elementSize can be max 4 bytes, so we take sizeof(float) and width=2\n  memsetParams.elementSize = sizeof(float);\n  memsetParams.width       = 2;\n  memsetParams.height      = 1;\n\n  cuda_safe_call(hipGraphCreate(&graph, 0));\n  cuda_safe_call(hipGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams));\n  nodeDependencies.push_back(memsetNode);\n\n  hipKernelNodeParams NodeParams0, NodeParams1;\n  NodeParams0.func           = (void*) JacobiMethod;\n  NodeParams0.gridDim        = nblocks;\n  NodeParams0.blockDim       = nthreads;\n  NodeParams0.sharedMemBytes = 0;\n  void* kernelArgs0[6]       = {\n    (void*) &A,\n    const_cast<double**>(&b),\n    const_cast<float*>(&conv_threshold),\n    (void*) &x,\n    (void*) &x_new,\n    (void*) &d_sum};\n  NodeParams0.kernelParams = kernelArgs0;\n  NodeParams0.extra        = NULL;\n\n  cuda_safe_call(\n    hipGraphAddKernelNode(&jacobiKernelNode, graph, nodeDependencies.data(), nodeDependencies.size(), &NodeParams0));\n\n  nodeDependencies.clear();\n  nodeDependencies.push_back(jacobiKernelNode);\n\n  memcpyParams.srcArray = NULL;\n  memcpyParams.srcPos   = make_hipPos(0, 0, 0);\n  memcpyParams.srcPtr   = make_hipPitchedPtr(d_sum, sizeof(double), 1, 1);\n  memcpyParams.dstArray = NULL;\n  memcpyParams.dstPos   = make_hipPos(0, 0, 0);\n  memcpyParams.dstPtr   = make_hipPitchedPtr(&sum, sizeof(double), 1, 1);\n  memcpyParams.extent   = make_hipExtent(sizeof(double), 1, 1);\n  memcpyParams.kind     = hipMemcpyDeviceToHost;\n\n  cuda_safe_call(\n    hipGraphAddMemcpyNode(&memcpyNode, graph, nodeDependencies.data(), nodeDependencies.size(), &memcpyParams));\n\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n\n  NodeParams1.func           = (void*) JacobiMethod;\n  NodeParams1.gridDim        = nblocks;\n  NodeParams1.blockDim       = nthreads;\n  NodeParams1.sharedMemBytes = 0;\n  void* kernelArgs1[6]       = {\n    (void*) &A,\n    const_cast<double**>(&b),\n    const_cast<float*>(&conv_threshold),\n    (void*) &x_new,\n    (void*) &x,\n    (void*) &d_sum};\n  NodeParams1.kernelParams = kernelArgs1;\n  NodeParams1.extra        = NULL;\n\n  int k = 0;\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(\n      hipGraphExecKernelNodeSetParams(graphExec, jacobiKernelNode, ((k & 1) == 0) ? &NodeParams0 : &NodeParams1));\n    cuda_safe_call(hipGraphLaunch(graphExec, stream));\n    cuda_safe_call(hipStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n      cuda_safe_call(hipStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(hipFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on GPU with Instantiated CUDA Graph Update\n// API - cudaGraphExecUpdate().\ndouble JacobiMethodGpuCudaGraphExecUpdate(\n  const float* A,\n  const double* b,\n  const float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  hipStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n  hipGraph_t graph;\n  hipGraphExec_t graphExec = NULL;\n\n  double sum = 0.0;\n  double* d_sum;\n  cuda_safe_call(hipMalloc(&d_sum, sizeof(double)));\n\n  int k = 0;\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal));\n    cuda_safe_call(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n    if ((k & 1) == 0)\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n    }\n    else\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n    }\n    cuda_safe_call(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n    cuda_safe_call(hipStreamEndCapture(stream, &graph));\n\n    if (graphExec == NULL)\n    {\n      cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n    }\n    else\n    {\n      hipGraphExecUpdateResult updateResult_out;\n      cuda_safe_call(hipGraphExecUpdate(graphExec, graph, NULL, &updateResult_out));\n      if (updateResult_out != hipGraphExecUpdateSuccess)\n      {\n        if (graphExec != NULL)\n        {\n          cuda_safe_call(hipGraphExecDestroy(graphExec));\n        }\n        printf(\"k = %d graph update failed with error - %d\\n\", k, updateResult_out);\n        cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n      }\n    }\n    cuda_safe_call(hipGraphLaunch(graphExec, stream));\n    cuda_safe_call(hipStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n      cuda_safe_call(hipStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(hipFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on GPU without CUDA Graph.\ndouble JacobiMethodGpu(\n  const float* A,\n  const double* b,\n  const float conv_threshold,\n  const int max_iter,\n  double* x,\n  double* x_new,\n  hipStream_t stream)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n  double sum = 0.0;\n  double* d_sum;\n  cuda_safe_call(hipMalloc(&d_sum, sizeof(double)));\n  int k = 0;\n\n  for (k = 0; k < max_iter; k++)\n  {\n    cuda_safe_call(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n    if ((k & 1) == 0)\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x, x_new, d_sum);\n    }\n    else\n    {\n      JacobiMethod<<<nblocks, nthreads, 0, stream>>>(A, b, conv_threshold, x_new, x, d_sum);\n    }\n    cuda_safe_call(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n    cuda_safe_call(hipStreamSynchronize(stream));\n\n    if (sum <= conv_threshold)\n    {\n      cuda_safe_call(hipMemsetAsync(d_sum, 0, sizeof(double), stream));\n      nblocks.x            = (N_ROWS / nthreads.x) + 1;\n      size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n      if ((k & 1) == 0)\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x_new, d_sum);\n      }\n      else\n      {\n        finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(x, d_sum);\n      }\n\n      cuda_safe_call(hipMemcpyAsync(&sum, d_sum, sizeof(double), hipMemcpyDeviceToHost, stream));\n      cuda_safe_call(hipStreamSynchronize(stream));\n      // printf(\"GPU iterations : %d\\n\", k + 1);\n      // printf(\"GPU error: %.3e\\n\", sum);\n      break;\n    }\n  }\n\n  cuda_safe_call(hipFree(d_sum));\n  return sum;\n}\n\n// Run the Jacobi method for A*x = b on CPU.\nvoid JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)\n{\n  double* x_new;\n  x_new = (double*) calloc(N_ROWS, sizeof(double));\n  int k;\n\n  for (k = 0; k < max_iter; k++)\n  {\n    double sum = 0.0;\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      double temp_dx = b[i];\n      for (int j = 0; j < N_ROWS; j++)\n      {\n        temp_dx -= A[i * N_ROWS + j] * x[j];\n      }\n      temp_dx /= A[i * N_ROWS + i];\n      x_new[i] += temp_dx;\n      sum += fabs(temp_dx);\n    }\n\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      x[i] = x_new[i];\n    }\n\n    if (sum <= conv_threshold)\n    {\n      break;\n    }\n  }\n  *num_iter = k + 1;\n  free(x_new);\n}\n\nint main()\n{\n  //  if (checkCmdLineFlag(argc, (const char **)argv, \"help\")) {\n  //    printf(\"Command line: jacobiCudaGraphs [-option]\\n\");\n  //    printf(\"Valid options:\\n\");\n  //    printf(\n  //        \"-gpumethod=<0,1 or 2>  : 0 - [Default] \"\n  //        \"JacobiMethodGpuCudaGraphExecKernelSetParams\\n\");\n  //    printf(\"                       : 1 - JacobiMethodGpuCudaGraphExecUpdate\\n\");\n  //    printf(\"                       : 2 - JacobiMethodGpu - Non CUDA Graph\\n\");\n  //    printf(\"-device=device_num     : cuda device id\");\n  //    printf(\"-help         : Output a help message\\n\");\n  //    exit(EXIT_SUCCESS);\n  //  }\n  //\n  int gpumethod = 0;\n  //  if (checkCmdLineFlag(argc, (const char **)argv, \"gpumethod\")) {\n  //    gpumethod = getCmdLineArgumentInt(argc, (const char **)argv, \"gpumethod\");\n  //\n  //    if (gpumethod < 0 || gpumethod > 2) {\n  //      printf(\"Error: gpumethod must be 0 or 1 or 2, gpumethod=%d is invalid\\n\",\n  //             gpumethod);\n  //      exit(EXIT_SUCCESS);\n  //    }\n  //  }\n\n  //  int dev = findCudaDevice(argc, (const char **)argv);\n  // int dev = 0;\n\n  double* b = NULL;\n  float* A  = NULL;\n  cuda_safe_call(hipHostMalloc(&b, N_ROWS * sizeof(double)));\n  memset(b, 0, N_ROWS * sizeof(double));\n  cuda_safe_call(hipHostMalloc(&A, N_ROWS * N_ROWS * sizeof(float)));\n  memset(A, 0, N_ROWS * N_ROWS * sizeof(float));\n\n  createLinearSystem(A, b);\n  double* x = NULL;\n  // start with array of all zeroes\n  x = (double*) calloc(N_ROWS, sizeof(double));\n\n  float conv_threshold = 1.0e-2;\n  int max_iter         = 4 * N_ROWS * N_ROWS;\n  int cnt              = 0;\n\n  //  // create timer\n  //  StopWatchInterface *timerCPU = NULL, *timerGpu = NULL;\n  //  sdkCreateTimer(&timerCPU);\n  //\n  //  sdkStartTimer(&timerCPU);\n  JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);\n\n  double sum = 0.0;\n  // Compute error\n  for (int i = 0; i < N_ROWS; i++)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n  //  sdkStopTimer(&timerCPU);\n  // printf(\"CPU iterations : %d\\n\", cnt);\n  // printf(\"CPU error: %.3e\\n\", sum);\n  //  printf(\"CPU Processing time: %f (ms)\\n\", sdkGetTimerValue(&timerCPU));\n\n  float* d_A;\n  double *d_b, *d_x, *d_x_new;\n  hipStream_t stream1;\n  cuda_safe_call(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking));\n  cuda_safe_call(hipMalloc(&d_b, sizeof(double) * N_ROWS));\n  cuda_safe_call(hipMalloc(&d_A, sizeof(float) * N_ROWS * N_ROWS));\n  cuda_safe_call(hipMalloc(&d_x, sizeof(double) * N_ROWS));\n  cuda_safe_call(hipMalloc(&d_x_new, sizeof(double) * N_ROWS));\n\n  cuda_safe_call(hipMemsetAsync(d_x, 0, sizeof(double) * N_ROWS, stream1));\n  cuda_safe_call(hipMemsetAsync(d_x_new, 0, sizeof(double) * N_ROWS, stream1));\n  cuda_safe_call(hipMemcpyAsync(d_A, A, sizeof(float) * N_ROWS * N_ROWS, hipMemcpyHostToDevice, stream1));\n  cuda_safe_call(hipMemcpyAsync(d_b, b, sizeof(double) * N_ROWS, hipMemcpyHostToDevice, stream1));\n\n  //  sdkCreateTimer(&timerGpu);\n  //  sdkStartTimer(&timerGpu);\n\n  double sumGPU = 0.0;\n  if (gpumethod == 0)\n  {\n    sumGPU = JacobiMethodGpuCudaGraphExecKernelSetParams(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n  else if (gpumethod == 1)\n  {\n    sumGPU = JacobiMethodGpuCudaGraphExecUpdate(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n  else if (gpumethod == 2)\n  {\n    sumGPU = JacobiMethodGpu(d_A, d_b, conv_threshold, max_iter, d_x, d_x_new, stream1);\n  }\n\n  //  sdkStopTimer(&timerGpu);\n  //  printf(\"GPU Processing time: %f (ms)\\n\", sdkGetTimerValue(&timerGpu));\n\n  cuda_safe_call(hipFree(d_b));\n  cuda_safe_call(hipFree(d_A));\n  cuda_safe_call(hipFree(d_x));\n  cuda_safe_call(hipFree(d_x_new));\n\n  cuda_safe_call(hipHostFree(A));\n  cuda_safe_call(hipHostFree(b));\n\n  // printf(\"&&&& jacobiCudaGraphs %s\\n\", (fabs(sum - sumGPU) < conv_threshold) ? \"PASSED\" : \"FAILED\");\n\n  return (fabs(sum - sumGPU) < conv_threshold) ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cuda-samples/3_CUDA_Features/jacobiCudaGraphs/jacobi.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Jacobi method on a linear system A*x = b,\n// where A is diagonally dominant and the exact solution consists\n// of all ones.\n\n#include <cuda/experimental/__stf/internal/scalar_interface.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#define N_ROWS 512\n\nnamespace cg = cooperative_groups;\n\nusing namespace cuda::experimental::stf;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1\n// elsewhere. The elements of the right hand side b all equal 2*n, hence the\n// exact solution x to A*x = b is a vector of ones.\nvoid createLinearSystem(float* A, double* b)\n{\n  int i, j;\n  for (i = 0; i < N_ROWS; i++)\n  {\n    b[i] = 2.0 * N_ROWS;\n    for (j = 0; j < N_ROWS; j++)\n    {\n      A[i * N_ROWS + j] = 1.0;\n    }\n    A[i * N_ROWS + i] = N_ROWS + 1.0;\n  }\n}\n\nstatic __global__ void\nJacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n  __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n  for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)\n  {\n    x_shared[i] = x[i];\n  }\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    int k = threadIdx.x;\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n    }\n  }\n\n  cg::sync(cta);\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)\n  {\n    double rowThreadSum = 0.0;\n    for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)\n    {\n      rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n    }\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n    }\n\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n    }\n  }\n\n  cg::sync(cta);\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);\n    double temp_sum                           = 0.0;\n\n    int k = threadIdx.x;\n\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n      dx /= A[i * N_ROWS + i];\n\n      x_new[i] = (x_shared[i] + dx);\n      temp_sum += fabs(dx);\n    }\n\n    for (int offset = tile8.size() / 2; offset > 0; offset /= 2)\n    {\n      temp_sum += tile8.shfl_down(temp_sum, offset);\n    }\n\n    if (tile8.thread_rank() == 0)\n    {\n      atomicAdd(sum, temp_sum);\n    }\n  }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(const double* x, double* g_sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  extern __shared__ double warpSum[];\n  double sum = 0.0;\n\n  int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n  for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n  {\n    sum += tile32.shfl_down(sum, offset);\n  }\n\n  if (tile32.thread_rank() == 0)\n  {\n    warpSum[threadIdx.x / warpSize] = sum;\n  }\n\n  cg::sync(cta);\n\n  double blockSum = 0.0;\n  if (threadIdx.x < (blockDim.x / warpSize))\n  {\n    blockSum = warpSum[threadIdx.x];\n  }\n\n  if (threadIdx.x < 32)\n  {\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      blockSum += tile32.shfl_down(blockSum, offset);\n    }\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(g_sum, blockSum);\n    }\n  }\n}\n\n// Run the Jacobi method for A*x = b on GPU without CUDA Graph.\n// double JacobiMethodGpu(const float *A, const double *b,\n//                       const float conv_threshold, const int max_iter,\n//                       double *x, double *x_new) {\ndouble JacobiMethodGpu(\n  stream_ctx& ctx,\n  logical_data<slice<float>>& A_handle,\n  logical_data<slice<double>>& b_handle,\n  const float conv_threshold,\n  const int max_iter,\n  logical_data<slice<double>>& x_handle,\n  logical_data<slice<double>>& x_new_handle)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n  auto sum_handle = ctx.logical_data(shape_of<scalar_view<double>>()).set_symbol(\"sum\");\n\n  int k;\n  for (k = 0; k < max_iter; k++)\n  {\n    auto x_mode     = (k & 1) == 0 ? access_mode::read : access_mode::rw;\n    auto x_new_mode = (k & 1) == 0 ? access_mode::rw : access_mode::read;\n\n    ctx.task(A_handle.read(),\n             b_handle.read(),\n             task_dep<slice<double>>(x_handle, x_mode),\n             task_dep<slice<double>>(x_new_handle, x_new_mode),\n             sum_handle.write())\n        .set_symbol(\"JacobiMethod\")\n        ->*[&](cudaStream_t stream, auto A, auto b, auto x, auto x_new, auto d_sum) {\n              cuda_try(cudaMemsetAsync(d_sum.addr, 0, sizeof(double), stream));\n\n              if ((k & 1) == 0)\n              {\n                JacobiMethod<<<nblocks, nthreads, 0, stream>>>(\n                  A.data_handle(), b.data_handle(), conv_threshold, x.data_handle(), x_new.data_handle(), d_sum.addr);\n              }\n              else\n              {\n                JacobiMethod<<<nblocks, nthreads, 0, stream>>>(\n                  A.data_handle(), b.data_handle(), conv_threshold, x_new.data_handle(), x.data_handle(), d_sum.addr);\n              }\n            };\n\n    if (ctx.wait(sum_handle) <= conv_threshold)\n    {\n      break;\n    }\n  }\n\n  auto final_x_handle = ((k & 1) == 0) ? &x_new_handle : &x_handle;\n  ctx.task(sum_handle.write(), final_x_handle->read()).set_symbol(\"finalError\")\n      ->*[&](cudaStream_t stream, auto d_sum, auto final_x) {\n            cuda_try(cudaMemsetAsync(d_sum.addr, 0, sizeof(double), stream));\n\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(final_x.data_handle(), d_sum.addr);\n          };\n\n  return ctx.wait(sum_handle);\n}\n\n// Run the Jacobi method for A*x = b on CPU.\nvoid JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)\n{\n  double* x_new = (double*) calloc(N_ROWS, sizeof(double));\n  SCOPE(exit)\n  {\n    free(x_new);\n  };\n\n  int k = 0;\n\n  for (; k < max_iter; k++)\n  {\n    double sum = 0.0;\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      double temp_dx = b[i];\n      for (int j = 0; j < N_ROWS; j++)\n      {\n        temp_dx -= A[i * N_ROWS + j] * x[j];\n      }\n      temp_dx /= A[i * N_ROWS + i];\n      x_new[i] += temp_dx;\n      sum += fabs(temp_dx);\n    }\n\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      x[i] = x_new[i];\n    }\n\n    if (sum <= conv_threshold)\n    {\n      break;\n    }\n  }\n\n  *num_iter = k + 1;\n}\n\ntemplate <typename Ctx>\nint run()\n{\n  Ctx ctx;\n\n  double* b = cuda_try<cudaMallocHost<double>>(N_ROWS * sizeof(double), 0);\n  SCOPE(exit)\n  {\n    cuda_try(cudaFreeHost(b));\n  };\n\n  float* A = cuda_try<cudaMallocHost<float>>(N_ROWS * N_ROWS * sizeof(float), 0);\n  SCOPE(exit)\n  {\n    cuda_try(cudaFreeHost(A));\n  };\n\n  memset(b, 0, N_ROWS * sizeof(double));\n\n  memset(A, 0, N_ROWS * N_ROWS * sizeof(float));\n\n  createLinearSystem(A, b);\n  // start with array of all zeroes\n  double* x = (double*) calloc(N_ROWS, sizeof(double));\n  SCOPE(exit)\n  {\n    free(x);\n  };\n\n  auto A_handle     = ctx.logical_data(A, N_ROWS * N_ROWS).set_symbol(\"A\");\n  auto b_handle     = ctx.logical_data(b, N_ROWS).set_symbol(\"b\");\n  auto x_handle     = ctx.logical_data(x, N_ROWS).set_symbol(\"x\");\n  auto x_new_handle = ctx.logical_data(shape_of<slice<double>>(N_ROWS)).set_symbol(\"x_new\");\n\n  float conv_threshold = 1.0e-2;\n  int max_iter         = 4 * N_ROWS * N_ROWS;\n  int cnt              = 0;\n\n  JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);\n\n  double sum = 0.0;\n  // Compute error\n  for (int i = 0; i < N_ROWS; i++)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  ctx.task(x_handle.write()).set_symbol(\"memset x\")->*[&](cudaStream_t stream, auto d_x) {\n    cuda_try(cudaMemsetAsync(d_x.data_handle(), 0, sizeof(double) * N_ROWS, stream));\n  };\n\n  ctx.task(x_new_handle.write()).set_symbol(\"memset x_new\")->*[](cudaStream_t stream, auto d_x_new) {\n    cuda_try(cudaMemsetAsync(d_x_new.data_handle(), 0, sizeof(double) * N_ROWS, stream));\n  };\n\n  double sumGPU = JacobiMethodGpu(ctx, A_handle, b_handle, conv_threshold, max_iter, x_handle, x_new_handle);\n\n  ctx.finalize();\n\n  if (fabs(sum - sumGPU) > conv_threshold)\n  {\n    printf(\"&&&& jacobiCudaGraphs FAILED\\n\");\n    return EXIT_FAILURE;\n  }\n  return 0;\n}\n\nint main()\n{\n  return run<stream_ctx>();\n  // run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// Jacobi method on a linear system A*x = b,\n// where A is diagonally dominant and the exact solution consists\n// of all ones.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/internal/scalar_interface.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#define N_ROWS 512\n\nnamespace cg = cooperative_groups;\n\nusing namespace cuda::experimental::stf;\n\n// 8 Rows of square-matrix A processed by each CTA.\n// This can be max 32 and only power of 2 (i.e., 2/4/8/16/32).\n#define ROWS_PER_CTA 8\n\n// creates N_ROWS x N_ROWS matrix A with N_ROWS+1 on the diagonal and 1\n// elsewhere. The elements of the right hand side b all equal 2*n, hence the\n// exact solution x to A*x = b is a vector of ones.\nvoid createLinearSystem(float* A, double* b)\n{\n  int i, j;\n  for (i = 0; i < N_ROWS; i++)\n  {\n    b[i] = 2.0 * N_ROWS;\n    for (j = 0; j < N_ROWS; j++)\n    {\n      A[i * N_ROWS + j] = 1.0;\n    }\n    A[i * N_ROWS + i] = N_ROWS + 1.0;\n  }\n}\n\nstatic __global__ void\nJacobiMethod(const float* A, const double* b, const float conv_threshold, double* x, double* x_new, double* sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  __shared__ double x_shared[N_ROWS]; // N_ROWS == n\n  __shared__ double b_shared[ROWS_PER_CTA + 1];\n\n  for (int i = threadIdx.x; i < N_ROWS; i += blockDim.x)\n  {\n    x_shared[i] = x[i];\n  }\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    int k = threadIdx.x;\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      b_shared[i % (ROWS_PER_CTA + 1)] = b[i];\n    }\n  }\n\n  cg::sync(cta);\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int k = 0, i = blockIdx.x * ROWS_PER_CTA; (k < ROWS_PER_CTA) && (i < N_ROWS); k++, i++)\n  {\n    double rowThreadSum = 0.0;\n    for (int j = threadIdx.x; j < N_ROWS; j += blockDim.x)\n    {\n      rowThreadSum += (A[i * N_ROWS + j] * x_shared[j]);\n    }\n\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      rowThreadSum += tile32.shfl_down(rowThreadSum, offset);\n    }\n\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(&b_shared[i % (ROWS_PER_CTA + 1)], -rowThreadSum);\n    }\n  }\n\n  cg::sync(cta);\n\n  if (threadIdx.x < ROWS_PER_CTA)\n  {\n    cg::thread_block_tile<ROWS_PER_CTA> tile8 = cg::tiled_partition<ROWS_PER_CTA>(cta);\n    double temp_sum                           = 0.0;\n\n    int k = threadIdx.x;\n\n    for (int i = k + (blockIdx.x * ROWS_PER_CTA); (k < ROWS_PER_CTA) && (i < N_ROWS);\n         k += ROWS_PER_CTA, i += ROWS_PER_CTA)\n    {\n      double dx = b_shared[i % (ROWS_PER_CTA + 1)];\n      dx /= A[i * N_ROWS + i];\n\n      x_new[i] = (x_shared[i] + dx);\n      temp_sum += fabs(dx);\n    }\n\n    for (int offset = tile8.size() / 2; offset > 0; offset /= 2)\n    {\n      temp_sum += tile8.shfl_down(temp_sum, offset);\n    }\n\n    if (tile8.thread_rank() == 0)\n    {\n      atomicAdd(sum, temp_sum);\n    }\n  }\n}\n\n// Thread block size for finalError kernel should be multiple of 32\nstatic __global__ void finalError(const double* x, double* g_sum)\n{\n  // Handle to thread block group\n  cg::thread_block cta = cg::this_thread_block();\n  extern __shared__ double warpSum[];\n  double sum = 0.0;\n\n  int globalThreadId = blockIdx.x * blockDim.x + threadIdx.x;\n\n  for (int i = globalThreadId; i < N_ROWS; i += blockDim.x * gridDim.x)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  cg::thread_block_tile<32> tile32 = cg::tiled_partition<32>(cta);\n\n  for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n  {\n    sum += tile32.shfl_down(sum, offset);\n  }\n\n  if (tile32.thread_rank() == 0)\n  {\n    warpSum[threadIdx.x / warpSize] = sum;\n  }\n\n  cg::sync(cta);\n\n  double blockSum = 0.0;\n  if (threadIdx.x < (blockDim.x / warpSize))\n  {\n    blockSum = warpSum[threadIdx.x];\n  }\n\n  if (threadIdx.x < 32)\n  {\n    for (int offset = tile32.size() / 2; offset > 0; offset /= 2)\n    {\n      blockSum += tile32.shfl_down(blockSum, offset);\n    }\n    if (tile32.thread_rank() == 0)\n    {\n      atomicAdd(g_sum, blockSum);\n    }\n  }\n}\n\n// Run the Jacobi method for A*x = b on GPU without CUDA Graph.\n// double JacobiMethodGpu(const float *A, const double *b,\n//                       const float conv_threshold, const int max_iter,\n//                       double *x, double *x_new) {\ndouble JacobiMethodGpu(\n  stream_ctx& ctx,\n  logical_data<slice<float>>& A_handle,\n  logical_data<slice<double>>& b_handle,\n  const float conv_threshold,\n  const int max_iter,\n  logical_data<slice<double>>& x_handle,\n  logical_data<slice<double>>& x_new_handle)\n{\n  // CTA size\n  dim3 nthreads(256, 1, 1);\n  // grid size\n  dim3 nblocks((N_ROWS / ROWS_PER_CTA) + 2, 1, 1);\n\n  auto sum_handle = ctx.logical_data(shape_of<scalar_view<double>>()).set_symbol(\"sum\");\n\n  int k;\n  for (k = 0; k < max_iter; k++)\n  {\n    auto x_mode     = (k & 1) == 0 ? access_mode::read : access_mode::rw;\n    auto x_new_mode = (k & 1) == 0 ? access_mode::rw : access_mode::read;\n\n    ctx.task(A_handle.read(),\n             b_handle.read(),\n             task_dep<slice<double>>(x_handle, x_mode),\n             task_dep<slice<double>>(x_new_handle, x_new_mode),\n             sum_handle.write())\n        .set_symbol(\"JacobiMethod\")\n        ->*[&](hipStream_t stream, auto A, auto b, auto x, auto x_new, auto d_sum) {\n              cuda_try(hipMemsetAsync(d_sum.addr, 0, sizeof(double), stream));\n\n              if ((k & 1) == 0)\n              {\n                JacobiMethod<<<nblocks, nthreads, 0, stream>>>(\n                  A.data_handle(), b.data_handle(), conv_threshold, x.data_handle(), x_new.data_handle(), d_sum.addr);\n              }\n              else\n              {\n                JacobiMethod<<<nblocks, nthreads, 0, stream>>>(\n                  A.data_handle(), b.data_handle(), conv_threshold, x_new.data_handle(), x.data_handle(), d_sum.addr);\n              }\n            };\n\n    if (ctx.wait(sum_handle) <= conv_threshold)\n    {\n      break;\n    }\n  }\n\n  auto final_x_handle = ((k & 1) == 0) ? &x_new_handle : &x_handle;\n  ctx.task(sum_handle.write(), final_x_handle->read()).set_symbol(\"finalError\")\n      ->*[&](hipStream_t stream, auto d_sum, auto final_x) {\n            cuda_try(hipMemsetAsync(d_sum.addr, 0, sizeof(double), stream));\n\n            nblocks.x            = (N_ROWS / nthreads.x) + 1;\n            size_t sharedMemSize = ((nthreads.x / 32) + 1) * sizeof(double);\n            finalError<<<nblocks, nthreads, sharedMemSize, stream>>>(final_x.data_handle(), d_sum.addr);\n          };\n\n  return ctx.wait(sum_handle);\n}\n\n// Run the Jacobi method for A*x = b on CPU.\nvoid JacobiMethodCPU(float* A, double* b, float conv_threshold, int max_iter, int* num_iter, double* x)\n{\n  double* x_new = (double*) calloc(N_ROWS, sizeof(double));\n  SCOPE(exit)\n  {\n    free(x_new);\n  };\n\n  int k = 0;\n\n  for (; k < max_iter; k++)\n  {\n    double sum = 0.0;\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      double temp_dx = b[i];\n      for (int j = 0; j < N_ROWS; j++)\n      {\n        temp_dx -= A[i * N_ROWS + j] * x[j];\n      }\n      temp_dx /= A[i * N_ROWS + i];\n      x_new[i] += temp_dx;\n      sum += fabs(temp_dx);\n    }\n\n    for (int i = 0; i < N_ROWS; i++)\n    {\n      x[i] = x_new[i];\n    }\n\n    if (sum <= conv_threshold)\n    {\n      break;\n    }\n  }\n\n  *num_iter = k + 1;\n}\n\ntemplate <typename Ctx>\nint run()\n{\n  Ctx ctx;\n\n  double* b = cuda_try<hipHostMalloc<double>>(N_ROWS * sizeof(double), 0);\n  SCOPE(exit)\n  {\n    cuda_try(hipHostFree(b));\n  };\n\n  float* A = cuda_try<hipHostMalloc<float>>(N_ROWS * N_ROWS * sizeof(float), 0);\n  SCOPE(exit)\n  {\n    cuda_try(hipHostFree(A));\n  };\n\n  memset(b, 0, N_ROWS * sizeof(double));\n\n  memset(A, 0, N_ROWS * N_ROWS * sizeof(float));\n\n  createLinearSystem(A, b);\n  // start with array of all zeroes\n  double* x = (double*) calloc(N_ROWS, sizeof(double));\n  SCOPE(exit)\n  {\n    free(x);\n  };\n\n  auto A_handle     = ctx.logical_data(A, N_ROWS * N_ROWS).set_symbol(\"A\");\n  auto b_handle     = ctx.logical_data(b, N_ROWS).set_symbol(\"b\");\n  auto x_handle     = ctx.logical_data(x, N_ROWS).set_symbol(\"x\");\n  auto x_new_handle = ctx.logical_data(shape_of<slice<double>>(N_ROWS)).set_symbol(\"x_new\");\n\n  float conv_threshold = 1.0e-2;\n  int max_iter         = 4 * N_ROWS * N_ROWS;\n  int cnt              = 0;\n\n  JacobiMethodCPU(A, b, conv_threshold, max_iter, &cnt, x);\n\n  double sum = 0.0;\n  // Compute error\n  for (int i = 0; i < N_ROWS; i++)\n  {\n    double d = x[i] - 1.0;\n    sum += fabs(d);\n  }\n\n  ctx.task(x_handle.write()).set_symbol(\"memset x\")->*[&](hipStream_t stream, auto d_x) {\n    cuda_try(hipMemsetAsync(d_x.data_handle(), 0, sizeof(double) * N_ROWS, stream));\n  };\n\n  ctx.task(x_new_handle.write()).set_symbol(\"memset x_new\")->*[](hipStream_t stream, auto d_x_new) {\n    cuda_try(hipMemsetAsync(d_x_new.data_handle(), 0, sizeof(double) * N_ROWS, stream));\n  };\n\n  double sumGPU = JacobiMethodGpu(ctx, A_handle, b_handle, conv_threshold, max_iter, x_handle, x_new_handle);\n\n  ctx.finalize();\n\n  if (fabs(sum - sumGPU) > conv_threshold)\n  {\n    printf(\"&&&& jacobiCudaGraphs FAILED\\n\");\n    return EXIT_FAILURE;\n  }\n  return 0;\n}\n\nint main()\n{\n  return run<stream_ctx>();\n  // run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cuda-samples/3_CUDA_Features/jacobiCudaGraphs/jacobi_cudastf.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t start, size_t cnt, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < cnt; ind += nthreads)\n  {\n    y[ind + start] += a * x[ind + start];\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 1024 * 1024 * 32;\n  double *X, *Y;\n\n  X = new double[N];\n  Y = new double[N];\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = X0(ind);\n    Y[ind] = Y0(ind);\n  }\n\n  auto handle_X = ctx.logical_data(X, {N});\n  auto handle_Y = ctx.logical_data(Y, {N});\n\n  auto all_devs = exec_place::all_devices();\n\n  double alpha = 3.14;\n\n  /* Compute Y = Y + alpha X */\n  ctx.parallel_for(tiled_partition<1024 * 1024>(), all_devs, handle_X.shape(), handle_X.read(), handle_Y.rw())\n      ->*[=] _CCCL_DEVICE(size_t i, auto sX, auto sY) {\n            sY(i) += alpha * sX(i);\n          };\n\n  /* Check the result on the host */\n  ctx.task(exec_place::host(), handle_X.read(), handle_Y.read())->*[&](cudaStream_t s, auto sX, auto sY) {\n    cuda_safe_call(cudaStreamSynchronize(s));\n\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(sY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(sX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/partitions/tiled_partition.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t start, size_t cnt, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < cnt; ind += nthreads)\n  {\n    y[ind + start] += a * x[ind + start];\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 1024 * 1024 * 32;\n  double *X, *Y;\n\n  X = new double[N];\n  Y = new double[N];\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = X0(ind);\n    Y[ind] = Y0(ind);\n  }\n\n  auto handle_X = ctx.logical_data(X, {N});\n  auto handle_Y = ctx.logical_data(Y, {N});\n\n  auto all_devs = exec_place::all_devices();\n\n  double alpha = 3.14;\n\n  /* Compute Y = Y + alpha X */\n  ctx.parallel_for(tiled_partition<1024 * 1024>(), all_devs, handle_X.shape(), handle_X.read(), handle_Y.rw())\n      ->*[=] _CCCL_DEVICE(size_t i, auto sX, auto sY) {\n            sY(i) += alpha * sX(i);\n          };\n\n  /* Check the result on the host */\n  ctx.task(exec_place::host(), handle_X.read(), handle_Y.read())->*[&](hipStream_t s, auto sX, auto sY) {\n    cuda_safe_call(hipStreamSynchronize(s));\n\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // Y should be Y0 + alpha X0\n      EXPECT(fabs(sY(ind) - (Y0(ind) + alpha * X0(ind))) < 0.0001);\n\n      // X should be X0\n      EXPECT(fabs(sX(ind) - X0(ind)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/parallel_for/parallel_for_all_devs.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\n/*\n * The goal of this test is to ensure that using read access modes actually\n * results in concurrent tasks\n */\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 256;\n  int ms     = 40;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cuda_safe_call(cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, 0));\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n\n  graph_ctx ctx;\n\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  ctx.task(handle.rw())->*[](cudaGraph_t graph, auto /*unused*/) {\n    cudaGraphNode_t n;\n    cuda_safe_call(cudaGraphAddEmptyNode(&n, graph, nullptr, 0));\n  };\n\n  for (int iter = 0; iter < 10; iter++)\n  {\n    for (int k = 0; k < NTASKS; k++)\n    {\n      ctx.task(handle.read())->*[&](cudaStream_t stream, auto /*unused*/) {\n        cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n      };\n    }\n\n    ctx.task(handle.rw())->*[&](cudaGraph_t graph, auto /*unused*/) {\n      cudaGraphNode_t n;\n      cuda_safe_call(cudaGraphAddEmptyNode(&n, graph, nullptr, 0));\n    };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[3]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\n/*\n * The goal of this test is to ensure that using read access modes actually\n * results in concurrent tasks\n */\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 256;\n  int ms     = 40;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cuda_safe_call(hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, 0));\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n\n  graph_ctx ctx;\n\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  ctx.task(handle.rw())->*[](hipGraph_t graph, auto /*unused*/) {\n    hipGraphNode_t n;\n    cuda_safe_call(hipGraphAddEmptyNode(&n, graph, nullptr, 0));\n  };\n\n  for (int iter = 0; iter < 10; iter++)\n  {\n    for (int k = 0; k < NTASKS; k++)\n    {\n      ctx.task(handle.read())->*[&](hipStream_t stream, auto /*unused*/) {\n        cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n      };\n    }\n\n    ctx.task(handle.rw())->*[&](hipGraph_t graph, auto /*unused*/) {\n      hipGraphNode_t n;\n      cuda_safe_call(hipGraphAddEmptyNode(&n, graph, nullptr, 0));\n    };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[3]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/concurrency_test.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided child graph from a while loop\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void dummy() {}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  cudaStream_t stream;\n\n  cuda_safe_call(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n  cudaGraph_t graph;\n  cudaGraphNode_t conditionalNode;\n\n  cudaGraphCreate(&graph, 0);\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  cudaGraphNodeParams cParams = {};\n  cParams.type                = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cudaGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams);\n#  else\n  cudaGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams);\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx ctx(bodyGraph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.cuda_kernel(lX.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lX.read(), lY.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lX.read(), lZ.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lY.rw(), lZ.rw())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  ctx.finalize_as_graph();\n\n  cudaGraphExec_t graphExec = NULL;\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(cudaGraphLaunch(graphExec, stream));\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  cuda_safe_call(cudaGraphDebugDotPrint(graph, \"test-while.dot\", cudaGraphDebugDotFlags(0)));\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided child graph from a while loop\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void dummy() {}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  hipStream_t stream;\n\n  cuda_safe_call(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n  hipGraph_t graph;\n  hipGraphNode_t conditionalNode;\n\n  hipGraphCreate(&graph, 0);\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  hipGraphNodeParams cParams = {};\n  cParams.type                = hipGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  hipGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams);\n#  else\n  hipGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams);\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx ctx(bodyGraph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.cuda_kernel(lX.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lX.read(), lY.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lX.read(), lZ.write())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel(lY.rw(), lZ.rw())->*[]() {\n    return cuda_kernel_desc{dummy, 1, 1, 0};\n  };\n\n  ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  ctx.finalize_as_graph();\n\n  hipGraphExec_t graphExec = NULL;\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(hipGraphLaunch(graphExec, stream));\n  cuda_safe_call(hipStreamSynchronize(stream));\n  cuda_safe_call(hipGraphDebugDotPrint(graph, \"test-while.dot\", hipGraphDebugDotFlags(0)));\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/explicit_graph_while-kernels.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure temporary data are destroyed\n *\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  //    stream_ctx ctx;\n  graph_ctx ctx;\n  const int N = 16;\n  double X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  for (int i = 0; i < 10; i++)\n  {\n    // fprintf(stderr, \"START loop %ld\\n\", i);\n    auto lY = ctx.logical_data(lX.shape());\n    lY.set_symbol(\"tmp\" + std::to_string(i));\n\n    // fprintf(stderr, \"START pfor %ld\\n\", i);\n    ctx.parallel_for(lX.shape(), lX.rw(), lY.write())->*[] _CCCL_DEVICE(size_t ind, auto dX, auto dY) {\n      dY(ind) = dX(ind);\n      dX(ind) = dY(ind) + 1.0;\n    };\n    // fprintf(stderr, \"End loop %ld\\n\", i);\n  }\n  // fprintf(stderr, \"OVER...\\n\");\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure temporary data are destroyed\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  //    stream_ctx ctx;\n  graph_ctx ctx;\n  const int N = 16;\n  double X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  for (int i = 0; i < 10; i++)\n  {\n    // fprintf(stderr, \"START loop %ld\\n\", i);\n    auto lY = ctx.logical_data(lX.shape());\n    lY.set_symbol(\"tmp\" + std::to_string(i));\n\n    // fprintf(stderr, \"START pfor %ld\\n\", i);\n    ctx.parallel_for(lX.shape(), lX.rw(), lY.write())->*[] _CCCL_DEVICE(size_t ind, auto dX, auto dY) {\n      dY(ind) = dX(ind);\n      dX(ind) = dY(ind) + 1.0;\n    };\n    // fprintf(stderr, \"End loop %ld\\n\", i);\n  }\n  // fprintf(stderr, \"OVER...\\n\");\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/graph_tmp_data.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure temporary data are destroyed\n *\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  auto stream = ctx.pick_stream();\n\n  graph_ctx gctx(stream);\n\n  auto frozen_X = fX.get(data_place::current_device(), stream);\n  auto lX_alias = gctx.logical_data(frozen_X, data_place::current_device());\n\n  auto lY = gctx.logical_data(lX.shape());\n\n  gctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n    y(i) = x(i);\n  };\n\n  gctx.parallel_for(lX.shape(), lX_alias.write(), lY.read())->*[] __device__(size_t i, auto x, auto y) {\n    x(i) = y(i) + 2;\n  };\n\n  gctx.finalize();\n\n  fX.unfreeze(stream);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure temporary data are destroyed\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  auto stream = ctx.pick_stream();\n\n  graph_ctx gctx(stream);\n\n  auto frozen_X = fX.get(data_place::current_device(), stream);\n  auto lX_alias = gctx.logical_data(frozen_X, data_place::current_device());\n\n  auto lY = gctx.logical_data(lX.shape());\n\n  gctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n    y(i) = x(i);\n  };\n\n  gctx.parallel_for(lX.shape(), lX_alias.write(), lY.read())->*[] __device__(size_t i, auto x, auto y) {\n    x(i) = y(i) + 2;\n  };\n\n  gctx.finalize();\n\n  fX.unfreeze(stream);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_for_graph.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure a graph context can be defined as a static variable\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n// Static graph ctx\ngraph_ctx ctx;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  double X[1024], Y[1024];\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  for (int k = 0; k < 10; k++)\n  {\n    ctx.task(handle_X.rw())->*[&](cudaStream_t s, auto /*unused*/) {\n      dummy<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](cudaStream_t s, auto /*unused*/, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure a graph context can be defined as a static variable\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n// Static graph ctx\ngraph_ctx ctx;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  double X[1024], Y[1024];\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  for (int k = 0; k < 10; k++)\n  {\n    ctx.task(handle_X.rw())->*[&](hipStream_t s, auto /*unused*/) {\n      dummy<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](hipStream_t s, auto /*unused*/, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/static_graph_ctx.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided graph\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n  cudaGraph_t graph;\n  cudaGraphExec_t graphExec = NULL;\n  cudaStream_t stream;\n\n  cuda_safe_call(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n  cuda_safe_call(cudaGraphCreate(&graph, 0));\n\n  graph_ctx ctx(graph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.finalize_as_graph();\n\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(cudaGraphLaunch(graphExec, stream));\n  cuda_safe_call(cudaStreamSynchronize(stream));\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided graph\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n  hipGraph_t graph;\n  hipGraphExec_t graphExec = NULL;\n  hipStream_t stream;\n\n  cuda_safe_call(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n  cuda_safe_call(hipGraphCreate(&graph, 0));\n\n  graph_ctx ctx(graph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.finalize_as_graph();\n\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(hipGraphLaunch(graphExec, stream));\n  cuda_safe_call(hipStreamSynchronize(stream));\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/explicit_graph.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node\n\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, ctx.get_graph(), 1, cudaGraphCondAssignDefault);\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  cudaGraph_t sub_graph;\n  cuda_safe_call(cudaGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph which is the body of the\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // We now create a conditional graph which depends on the same dependencies\n  // as the inner ctx. We then insert the body of the graph as a child graph of\n  // the conditional node because we cannot decide what graph is the body of\n  // the conditional node ourselves, and we cannot add input dependencies to\n  // the conditional node after it was added.\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<cudaGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  cudaGraphNodeParams cParams = {};\n  cParams.type                = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  cudaGraphNode_t conditionalNode;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cudaGraphAddNode(&conditionalNode, ctx.get_graph(), fX_ready_nodes.data(), nullptr, fX_ready_nodes.size(), &cParams);\n#  else\n  cudaGraphAddNode(&conditionalNode, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), &cParams);\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // A child graph contains the entire body\n  cudaGraphNode_t child_graph_node;\n  cuda_safe_call(cudaGraphAddChildGraphNode(&child_graph_node, bodyGraph, nullptr, 0, sub_ctx.get_graph()));\n\n  // Create an event that depends on the conditional node, so that we unfreeze\n  // after the completion of the while loop\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, conditionalNode, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, ctx.get_graph(), 1, cudaGraphCondAssignDefault);\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  hipGraph_t sub_graph;\n  cuda_safe_call(hipGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph which is the body of the\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // We now create a conditional graph which depends on the same dependencies\n  // as the inner ctx. We then insert the body of the graph as a child graph of\n  // the conditional node because we cannot decide what graph is the body of\n  // the conditional node ourselves, and we cannot add input dependencies to\n  // the conditional node after it was added.\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<hipGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  hipGraphNodeParams cParams = {};\n  cParams.type                = hipGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  hipGraphNode_t conditionalNode;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  hipGraphAddNode(&conditionalNode, ctx.get_graph(), fX_ready_nodes.data(), nullptr, fX_ready_nodes.size(), &cParams);\n#  else\n  hipGraphAddNode(&conditionalNode, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), &cParams);\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // A child graph contains the entire body\n  hipGraphNode_t child_graph_node;\n  cuda_safe_call(hipGraphAddChildGraphNode(&child_graph_node, bodyGraph, nullptr, 0, sub_ctx.get_graph()));\n\n  // Create an event that depends on the conditional node, so that we unfreeze\n  // after the completion of the while loop\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, conditionalNode, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_while_graphs.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in a child graph\n\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  cudaGraph_t sub_graph;\n  cuda_safe_call(cudaGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  // XXX we need an adapter to allocate data from the upper context\n  //  auto lY = sub_ctx.logical_data(lX.shape());\n  //  sub_ctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n  //    y(i) = x(i);\n  //  };\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<cudaGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  // Add the child graph as a node that depends on the frozen data being ready\n  cudaGraphNode_t child_graph_node;\n  cuda_safe_call(cudaGraphAddChildGraphNode(\n    &child_graph_node, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), sub_ctx.get_graph()));\n\n  // Create an event that signals when the child graph completes\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, child_graph_node, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n\n  // Unfreeze the data after the child graph completes\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in a child graph\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  hipGraph_t sub_graph;\n  cuda_safe_call(hipGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  // XXX we need an adapter to allocate data from the upper context\n  //  auto lY = sub_ctx.logical_data(lX.shape());\n  //  sub_ctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n  //    y(i) = x(i);\n  //  };\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<hipGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  // Add the child graph as a node that depends on the frozen data being ready\n  hipGraphNode_t child_graph_node;\n  cuda_safe_call(hipGraphAddChildGraphNode(\n    &child_graph_node, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), sub_ctx.get_graph()));\n\n  // Create an event that signals when the child graph completes\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, child_graph_node, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n\n  // Unfreeze the data after the child graph completes\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_nested_graphs_allocator.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node, the resulting looping graph\n//! will be executed within a stream context.\n\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n\n/**\n * @brief Insert an existing CUDA graph node into a graph context with appropriate dependencies\n *\n * This function is designed for graph contexts and adds the provided graph node\n * to the context's graph with dependencies from the input prerequisites.\n *\n * @tparam ctx_t Context type (must be a graph_ctx or context using graph_ctx under the hood)\n * @param ctx The execution context (must be a graph context)\n * @param node The existing CUDA graph node to insert\n * @param input_prereqs Input dependencies that must be satisfied (must be graph events)\n * @return event_list Events representing the completion of the graph node insertion\n */\ntemplate <typename ctx_t>\nevent_list insert_graph_node(ctx_t& ctx, cudaGraphNode_t node, event_list& input_prereqs)\n{\n  cudaGraph_t support_graph = ctx.graph();\n  size_t graph_stage        = ctx.stage();\n\n  ::std::vector<cudaGraphNode_t> ready_nodes = reserved::join_with_graph_nodes(ctx, input_prereqs, graph_stage);\n\n  // Add dependencies from the ready_nodes to the existing node\n  if (!ready_nodes.empty())\n  {\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n    cuda_safe_call(cudaGraphAddDependencies(support_graph, ready_nodes.data(), &node, nullptr, ready_nodes.size()));\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n    cuda_safe_call(cudaGraphAddDependencies(support_graph, ready_nodes.data(), &node, ready_nodes.size()));\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n  }\n\n  // Create an event that depends on the inserted graph node\n  auto node_event = reserved::graph_event(node, graph_stage, support_graph);\n  node_event->set_symbol(ctx, \"inserted_graph_node\");\n\n  // Return the event list from that single event\n  return event_list(mv(node_event));\n}\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, ctx.graph(), 1, cudaGraphCondAssignDefault));\n\n  cudaGraphNodeParams cParams = {};\n  cParams.type                = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  cudaGraphNode_t conditionalNode;\n  // There is no input dependencies yet, we will add them later\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, ctx.graph(), nullptr, nullptr, 0, &cParams));\n#  else\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, ctx.graph(), nullptr, 0, &cParams));\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx sub_ctx(bodyGraph);\n\n  auto fX                        = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  event_list cond_graph_launched = insert_graph_node(ctx, conditionalNode, fX_get_events);\n\n  fX.unfreeze(mv(cond_graph_launched));\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node, the resulting looping graph\n//! will be executed within a stream context.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n\n/**\n * @brief Insert an existing CUDA graph node into a graph context with appropriate dependencies\n *\n * This function is designed for graph contexts and adds the provided graph node\n * to the context's graph with dependencies from the input prerequisites.\n *\n * @tparam ctx_t Context type (must be a graph_ctx or context using graph_ctx under the hood)\n * @param ctx The execution context (must be a graph context)\n * @param node The existing CUDA graph node to insert\n * @param input_prereqs Input dependencies that must be satisfied (must be graph events)\n * @return event_list Events representing the completion of the graph node insertion\n */\ntemplate <typename ctx_t>\nevent_list insert_graph_node(ctx_t& ctx, hipGraphNode_t node, event_list& input_prereqs)\n{\n  hipGraph_t support_graph = ctx.graph();\n  size_t graph_stage        = ctx.stage();\n\n  ::std::vector<hipGraphNode_t> ready_nodes = reserved::join_with_graph_nodes(ctx, input_prereqs, graph_stage);\n\n  // Add dependencies from the ready_nodes to the existing node\n  if (!ready_nodes.empty())\n  {\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n    cuda_safe_call(hipGraphAddDependencies(support_graph, ready_nodes.data(), &node, nullptr, ready_nodes.size()));\n#  else // _CCCL_CTK_AT_LEAST(13, 0)\n    cuda_safe_call(hipGraphAddDependencies(support_graph, ready_nodes.data(), &node, ready_nodes.size()));\n#  endif // _CCCL_CTK_AT_LEAST(13, 0)\n  }\n\n  // Create an event that depends on the inserted graph node\n  auto node_event = reserved::graph_event(node, graph_stage, support_graph);\n  node_event->set_symbol(ctx, \"inserted_graph_node\");\n\n  // Return the event list from that single event\n  return event_list(mv(node_event));\n}\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  cudaGraphConditionalHandle handle;\n  cuda_safe_call(cudaGraphConditionalHandleCreate(&handle, ctx.graph(), 1, cudaGraphCondAssignDefault));\n\n  hipGraphNodeParams cParams = {};\n  cParams.type                = hipGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  hipGraphNode_t conditionalNode;\n  // There is no input dependencies yet, we will add them later\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, ctx.graph(), nullptr, nullptr, 0, &cParams));\n#  else\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, ctx.graph(), nullptr, 0, &cParams));\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx sub_ctx(bodyGraph);\n\n  auto fX                        = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  event_list cond_graph_launched = insert_graph_node(ctx, conditionalNode, fX_get_events);\n\n  fX.unfreeze(mv(cond_graph_launched));\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_while_graphs_3.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in a child graph\n\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  cudaGraph_t sub_graph;\n  cuda_safe_call(cudaGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  // XXX we need an adapter to allocate data from the upper context\n  //  auto lY = sub_ctx.logical_data(lX.shape());\n  //  sub_ctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n  //    y(i) = x(i);\n  //  };\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<cudaGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  // Add the child graph as a node that depends on the frozen data being ready\n  cudaGraphNode_t child_graph_node;\n  cuda_safe_call(cudaGraphAddChildGraphNode(\n    &child_graph_node, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), sub_ctx.get_graph()));\n\n  // Create an event that signals when the child graph completes\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, child_graph_node, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n\n  // Unfreeze the data after the child graph completes\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in a child graph\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void dummy() {}\n\nint main()\n{\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  graph_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  auto fX = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  hipGraph_t sub_graph;\n  cuda_safe_call(hipGraphCreate(&sub_graph, 0));\n\n  // Create a context based on this child graph\n  graph_ctx sub_ctx(sub_graph);\n\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  // XXX we need an adapter to allocate data from the upper context\n  //  auto lY = sub_ctx.logical_data(lX.shape());\n  //  sub_ctx.parallel_for(lX.shape(), lX_alias.read(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n  //    y(i) = x(i);\n  //  };\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The child graph depends on the events to get the frozen data\n  ::std::vector<hipGraphNode_t> fX_ready_nodes = reserved::join_with_graph_nodes(ctx, fX_get_events, ctx.stage());\n\n  // Add the child graph as a node that depends on the frozen data being ready\n  hipGraphNode_t child_graph_node;\n  cuda_safe_call(hipGraphAddChildGraphNode(\n    &child_graph_node, ctx.get_graph(), fX_ready_nodes.data(), fX_ready_nodes.size(), sub_ctx.get_graph()));\n\n  // Create an event that signals when the child graph completes\n  event_list child_graph_event;\n  reserved::fork_from_graph_node(\n    ctx, child_graph_node, ctx.get_graph(), ctx.stage(), child_graph_event, \"child graph done\");\n\n  // Unfreeze the data after the child graph completes\n  fX.unfreeze(child_graph_event);\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_nested_graphs.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node, the resulting looping graph\n//! will be executed within a stream context.\n\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Skipping test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  stream_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  /* We are going to create a local context which is a graph, and we will populate it using a graph_ctx */\n  cudaGraph_t graph;\n  cuda_safe_call(cudaGraphCreate(&graph, 0));\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  cudaGraph_t sub_graph;\n  cuda_safe_call(cudaGraphCreate(&sub_graph, 0));\n\n  cudaGraphNodeParams cParams = {};\n  cParams.type                = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  cudaGraphNode_t conditionalNode;\n  // There is no input dependency because they are implied by graph launch\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams));\n#  else\n  cuda_safe_call(cudaGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams));\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // Create a context based on this child graph which is the body of the\n  graph_ctx sub_ctx(bodyGraph);\n\n  auto fX                        = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The sub graph depends on the events to get the frozen data, so the\n  // launch of the graph will depend on them\n\n  cudaGraphExec_t graph_exec = NULL;\n  cuda_safe_call(cudaGraphInstantiate(&graph_exec, graph, NULL, NULL, 0));\n\n  auto support_dstream = ctx.pick_dstream();\n  /* auto before_launch = */ reserved::join_with_stream(ctx, support_dstream, fX_get_events, \"graph_launch\", false);\n\n  cuda_safe_call(cudaGraphLaunch(graph_exec, support_dstream.stream));\n\n  // We stop using the frozen logical data after then graph has been launched\n  auto graph_launched = reserved::record_event_in_stream(support_dstream);\n  fX.unfreeze(event_list(mv(graph_launched)));\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze a logical data in a graph to use it in the body of a \"while\" graph node, the resulting looping graph\n//! will be executed within a stream context.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <vector>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Skipping test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  stream_ctx ctx;\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.parallel_for(lX.shape(), lX.rw())->*[] __device__(size_t i, auto x) {\n    x(i) *= 3;\n  };\n\n  /* We are going to create a local context which is a graph, and we will populate it using a graph_ctx */\n  hipGraph_t graph;\n  cuda_safe_call(hipGraphCreate(&graph, 0));\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  // Create a graph that will later be inserted as a child graph once all input\n  // dependencies are known\n  hipGraph_t sub_graph;\n  cuda_safe_call(hipGraphCreate(&sub_graph, 0));\n\n  hipGraphNodeParams cParams = {};\n  cParams.type                = hipGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n\n  hipGraphNode_t conditionalNode;\n  // There is no input dependency because they are implied by graph launch\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams));\n#  else\n  cuda_safe_call(hipGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams));\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  // Create a context based on this child graph which is the body of the\n  graph_ctx sub_ctx(bodyGraph);\n\n  auto fX                        = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n  auto [frozen_X, fX_get_events] = fX.get(data_place::current_device());\n\n  auto lX_alias = sub_ctx.logical_data(frozen_X, data_place::current_device());\n\n  sub_ctx.parallel_for(lX.shape(), lX_alias.rw())->*[] __device__(size_t i, auto x) {\n    x(i) = x(i) + 2;\n  };\n\n  // We want to repeat this a fixed number of times\n  sub_ctx.cuda_kernel()->*[handle]() {\n    return cuda_kernel_desc{setHandle, 1, 1, 0, handle};\n  };\n\n  sub_ctx.finalize_as_graph();\n\n  // The sub graph depends on the events to get the frozen data, so the\n  // launch of the graph will depend on them\n\n  hipGraphExec_t graph_exec = NULL;\n  cuda_safe_call(hipGraphInstantiate(&graph_exec, graph, NULL, NULL, 0));\n\n  auto support_dstream = ctx.pick_dstream();\n  /* auto before_launch = */ reserved::join_with_stream(ctx, support_dstream, fX_get_events, \"graph_launch\", false);\n\n  cuda_safe_call(hipGraphLaunch(graph_exec, support_dstream.stream));\n\n  // We stop using the frozen logical data after then graph has been launched\n  auto graph_launched = reserved::record_event_in_stream(support_dstream);\n  fX.unfreeze(event_list(mv(graph_launched)));\n\n  ctx.host_launch(lX.read())->*[](auto x) {\n    for (int i = 0; i < static_cast<int>(x.size()); i++)\n    {\n      EXPECT(x(i) == 3 * X0(i) + 2 * 5);\n    }\n  };\n\n  ctx.finalize();\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/freeze_while_graphs_2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use multiple graph contexts simultaneously and launch them\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  graph_ctx ctx;\n  graph_ctx ctx_2;\n\n  double X[1024], Y[1024];\n  auto handle_X = ctx.logical_data(make_slice(X, 1024));\n  auto handle_Y = ctx.logical_data(make_slice(Y, 1024));\n\n  double Z[1024];\n  auto handle_Z = ctx_2.logical_data(make_slice(Z, 1024));\n\n  for (int k = 0; k < 10; k++)\n  {\n    ctx.task(handle_X.rw())->*[&](cudaStream_t s, auto /*unused*/) {\n      dummy<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](cudaStream_t s, auto /*unused*/, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx_2.task(handle_Z.rw())->*[&](cudaStream_t s, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  cudaStream_t stream;\n  cuda_safe_call(cudaStreamCreate(&stream));\n\n  ctx.submit(stream);\n  ctx_2.submit(stream);\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    std::cout << \"Generating DOT output in \" << argv[2] << '\\n';\n    ctx_2.print_to_dot(argv[2]);\n  }\n\n  ctx.finalize();\n  ctx_2.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use multiple graph contexts simultaneously and launch them\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  graph_ctx ctx;\n  graph_ctx ctx_2;\n\n  double X[1024], Y[1024];\n  auto handle_X = ctx.logical_data(make_slice(X, 1024));\n  auto handle_Y = ctx.logical_data(make_slice(Y, 1024));\n\n  double Z[1024];\n  auto handle_Z = ctx_2.logical_data(make_slice(Z, 1024));\n\n  for (int k = 0; k < 10; k++)\n  {\n    ctx.task(handle_X.rw())->*[&](hipStream_t s, auto /*unused*/) {\n      dummy<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](hipStream_t s, auto /*unused*/, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx_2.task(handle_Z.rw())->*[&](hipStream_t s, auto /*unused*/) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  hipStream_t stream;\n  cuda_safe_call(hipStreamCreate(&stream));\n\n  ctx.submit(stream);\n  ctx_2.submit(stream);\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    std::cout << \"Generating DOT output in \" << argv[2] << '\\n';\n    ctx_2.print_to_dot(argv[2]);\n  }\n\n  ctx.finalize();\n  ctx_2.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/multiple_graph_ctx.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided graph and launch on a user-provided stream.\n//!        Exercises graph_ctx(cudaGraph_t, cudaStream_t): finalize() submits the\n//!        graph on the given stream and does not block; the caller synchronizes.\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n  cudaGraph_t graph;\n  cudaStream_t stream;\n\n  cuda_safe_call(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n  cuda_safe_call(cudaGraphCreate(&graph, 0));\n\n  graph_ctx ctx(graph, stream);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  // Non-blocking: submits the graph on the user-provided stream\n  ctx.finalize();\n\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  cuda_safe_call(cudaStreamDestroy(stream));\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided graph and launch on a user-provided stream.\n//!        Exercises graph_ctx(cudaGraph_t, cudaStream_t): finalize() submits the\n//!        graph on the given stream and does not block; the caller synchronizes.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n  hipGraph_t graph;\n  hipStream_t stream;\n\n  cuda_safe_call(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n  cuda_safe_call(hipGraphCreate(&graph, 0));\n\n  graph_ctx ctx(graph, stream);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  // Non-blocking: submits the graph on the user-provided stream\n  ctx.finalize();\n\n  cuda_safe_call(hipStreamSynchronize(stream));\n  cuda_safe_call(hipStreamDestroy(stream));\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/explicit_graph_async.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided child graph from a while loop\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void dummy() {}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  cudaStream_t stream;\n\n  cuda_safe_call(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n  cudaGraph_t graph;\n  cudaGraphNode_t conditionalNode;\n\n  cudaGraphCreate(&graph, 0);\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  cudaGraphNodeParams cParams = {};\n  cParams.type                = cudaGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  cudaGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams);\n#  else\n  cudaGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams);\n#  endif\n\n  cudaGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx ctx(bodyGraph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](cudaStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task()->*[&handle](cudaStream_t s) {\n    setHandle<<<1, 1, 0, s>>>(handle);\n  };\n\n  ctx.finalize_as_graph();\n\n  cudaGraphExec_t graphExec = NULL;\n  cuda_safe_call(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(cudaGraphLaunch(graphExec, stream));\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  cuda_safe_call(cudaGraphDebugDotPrint(graph, \"test-while.dot\", cudaGraphDebugDotFlags(0)));\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! @file\n//! @brief Add tasks to a user-provided child graph from a while loop\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void dummy() {}\n\n__global__ void setHandle(cudaGraphConditionalHandle handle)\n{\n  static int count = 5;\n  cudaGraphSetConditional(handle, --count ? 1 : 0);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Waiving test: conditional nodes are only available since CUDA 12.4.\\n\");\n#else\n  hipStream_t stream;\n\n  cuda_safe_call(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n  hipGraph_t graph;\n  hipGraphNode_t conditionalNode;\n\n  hipGraphCreate(&graph, 0);\n\n  cudaGraphConditionalHandle handle;\n  cudaGraphConditionalHandleCreate(&handle, graph, 1, cudaGraphCondAssignDefault);\n\n  hipGraphNodeParams cParams = {};\n  cParams.type                = hipGraphNodeTypeConditional;\n  cParams.conditional.handle  = handle;\n  cParams.conditional.type    = cudaGraphCondTypeWhile;\n  cParams.conditional.size    = 1;\n#  if _CCCL_CTK_AT_LEAST(13, 0)\n  hipGraphAddNode(&conditionalNode, graph, nullptr, nullptr, 0, &cParams);\n#  else\n  hipGraphAddNode(&conditionalNode, graph, nullptr, 0, &cParams);\n#  endif\n\n  hipGraph_t bodyGraph = cParams.conditional.phGraph_out[0];\n\n  graph_ctx ctx(bodyGraph);\n\n  auto lX = ctx.token();\n  auto lY = ctx.token();\n  auto lZ = ctx.token();\n\n  ctx.task(lX.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lY.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lX.read(), lZ.write())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task(lY.rw(), lZ.rw())->*[](hipStream_t s) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n\n  ctx.task()->*[&handle](hipStream_t s) {\n    setHandle<<<1, 1, 0, s>>>(handle);\n  };\n\n  ctx.finalize_as_graph();\n\n  hipGraphExec_t graphExec = NULL;\n  cuda_safe_call(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0));\n  cuda_safe_call(hipGraphLaunch(graphExec, stream));\n  cuda_safe_call(hipStreamSynchronize(stream));\n  cuda_safe_call(hipGraphDebugDotPrint(graph, \"test-while.dot\", hipGraphDebugDotFlags(0)));\n#endif // !_CCCL_CTK_BELOW(12, 4)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/explicit_graph_while.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Verify that graph_ctx tasks whose captured child graphs contain\n *        memory allocation/free nodes (from cudaMallocAsync) work correctly.\n *\n * Before the move-ownership fix, cudaGraphAddChildGraphNode (clone semantics)\n * rejected such child graphs with CUDA_ERROR_NOT_SUPPORTED.  CTK 13+ exposes\n * cudaGraphChildGraphOwnershipMove via cudaGraphAddNode which transfers the\n * child graph to the parent instead of cloning it.\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void fill_kernel(int* ptr, int n, int val)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = val;\n  }\n}\n\nint main()\n{\n#if _CCCL_CTK_BELOW(13, 0)\n  fprintf(stderr, \"Waiving test: cudaGraphChildGraphOwnershipMove requires CTK 13+.\\n\");\n#else\n  constexpr int N = 256;\n  int host_data[N];\n  for (int i = 0; i < N; i++)\n  {\n    host_data[i] = 0;\n  }\n\n  graph_ctx ctx;\n  auto ldata = ctx.logical_data(host_data);\n\n  // The lambda receives cudaStream_t, so graph_ctx uses stream capture.\n  // cudaMallocAsync/cudaFreeAsync on that stream produce mem-alloc/free\n  // graph nodes inside the captured child graph.\n  ctx.task(ldata.rw())->*[](cudaStream_t s, auto sdata) {\n    int* tmp = nullptr;\n    cuda_safe_call(cudaMallocAsync(&tmp, N * sizeof(int), s));\n    fill_kernel<<<(N + 255) / 256, 256, 0, s>>>(tmp, N, 42);\n    cuda_safe_call(cudaMemcpyAsync(sdata.data_handle(), tmp, N * sizeof(int), cudaMemcpyDeviceToDevice, s));\n    cuda_safe_call(cudaFreeAsync(tmp, s));\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    assert(host_data[i] == 42);\n  }\n#endif // !_CCCL_CTK_BELOW(13, 0)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Verify that graph_ctx tasks whose captured child graphs contain\n *        memory allocation/free nodes (from cudaMallocAsync) work correctly.\n *\n * Before the move-ownership fix, cudaGraphAddChildGraphNode (clone semantics)\n * rejected such child graphs with CUDA_ERROR_NOT_SUPPORTED.  CTK 13+ exposes\n * cudaGraphChildGraphOwnershipMove via cudaGraphAddNode which transfers the\n * child graph to the parent instead of cloning it.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void fill_kernel(int* ptr, int n, int val)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = val;\n  }\n}\n\nint main()\n{\n#if _CCCL_CTK_BELOW(13, 0)\n  fprintf(stderr, \"Waiving test: cudaGraphChildGraphOwnershipMove requires CTK 13+.\\n\");\n#else\n  constexpr int N = 256;\n  int host_data[N];\n  for (int i = 0; i < N; i++)\n  {\n    host_data[i] = 0;\n  }\n\n  graph_ctx ctx;\n  auto ldata = ctx.logical_data(host_data);\n\n  // The lambda receives cudaStream_t, so graph_ctx uses stream capture.\n  // cudaMallocAsync/cudaFreeAsync on that stream produce mem-alloc/free\n  // graph nodes inside the captured child graph.\n  ctx.task(ldata.rw())->*[](hipStream_t s, auto sdata) {\n    int* tmp = nullptr;\n    cuda_safe_call(hipMallocAsync(&tmp, N * sizeof(int), s));\n    fill_kernel<<<(N + 255) / 256, 256, 0, s>>>(tmp, N, 42);\n    cuda_safe_call(hipMemcpyAsync(sdata.data_handle(), tmp, N * sizeof(int), hipMemcpyDeviceToDevice, s));\n    cuda_safe_call(hipFreeAsync(tmp, s));\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    assert(host_data[i] == 42);\n  }\n#endif // !_CCCL_CTK_BELOW(13, 0)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/graph/graph_child_move_ownership.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  const int dev_id = 0;\n  cuda_safe_call(cudaSetDevice(dev_id));\n\n  cudaDeviceProp prop;\n  cuda_safe_call(cudaGetDeviceProperties(&prop, dev_id));\n\n  const size_t total_mem_ref = prop.totalGlobalMem;\n\n  size_t free_mem, total_mem;\n  cuda_safe_call(cudaMemGetInfo(&free_mem, &total_mem));\n\n  std::cout << \"Device memory: \" << total_mem_ref / (1024 * 1024.) << \" MB\"\n            << \" FREE/TOTAL=\" << free_mem / (1024 * 1024.) << \"/\" << total_mem / (1024 * 1024.) << '\\n';\n\n  // Warning: this should represent 6% of device's available memory\n  size_t block_size = free_mem / 32;\n  int nblocks       = 2;\n\n  // We preallocate most available device memory to avoid being limited by host memory.\n  void* wasted_mem;\n  size_t wasted_size = block_size * 29;\n  cuda_safe_call(cudaMalloc(&wasted_mem, wasted_size));\n\n  std::cout << \"Wasted: \" << wasted_size / (1024 * 1024.) << \" MB\" << '\\n';\n\n  graph_ctx ctx;\n\n  std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < 2; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // There can only be a single handle on the device at the same time\n  for (int i = 0; i < nblocks; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[](cudaStream_t stream, auto /*unused*/) {\n      dummy<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  // Checking the amount of memory actually available now\n  cuda_safe_call(cudaMemGetInfo(&free_mem, &total_mem));\n  std::cout << \"Device memory: FREE/TOTAL=\" << free_mem / (1024 * 1024.) << \"/\" << total_mem / (1024 * 1024.) << '\\n';\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main(int argc, char** argv)\n{\n  const int dev_id = 0;\n  cuda_safe_call(hipSetDevice(dev_id));\n\n  hipDeviceProp_t prop;\n  cuda_safe_call(hipGetDeviceProperties(&prop, dev_id));\n\n  const size_t total_mem_ref = prop.totalGlobalMem;\n\n  size_t free_mem, total_mem;\n  cuda_safe_call(hipMemGetInfo(&free_mem, &total_mem));\n\n  std::cout << \"Device memory: \" << total_mem_ref / (1024 * 1024.) << \" MB\"\n            << \" FREE/TOTAL=\" << free_mem / (1024 * 1024.) << \"/\" << total_mem / (1024 * 1024.) << '\\n';\n\n  // Warning: this should represent 6% of device's available memory\n  size_t block_size = free_mem / 32;\n  int nblocks       = 2;\n\n  // We preallocate most available device memory to avoid being limited by host memory.\n  void* wasted_mem;\n  size_t wasted_size = block_size * 29;\n  cuda_safe_call(hipMalloc(&wasted_mem, wasted_size));\n\n  std::cout << \"Wasted: \" << wasted_size / (1024 * 1024.) << \" MB\" << '\\n';\n\n  graph_ctx ctx;\n\n  std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < 2; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // There can only be a single handle on the device at the same time\n  for (int i = 0; i < nblocks; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[](hipStream_t stream, auto /*unused*/) {\n      dummy<<<1, 1, 0, stream>>>();\n    };\n  }\n\n  // Checking the amount of memory actually available now\n  cuda_safe_call(hipMemGetInfo(&free_mem, &total_mem));\n  std::cout << \"Device memory: FREE/TOTAL=\" << free_mem / (1024 * 1024.) << \"/\" << total_mem / (1024 * 1024.) << '\\n';\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reclaiming/graph_real_oom.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel(int i, slice<char> buf)\n{\n  buf[0] = (char) i;\n}\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n// Dummy allocator which only allocates a single block on a single device\n//\n// The first allocation succeeds, next attempt will fail until buffer is\n// deallocated.\nclass one_block_allocator : public block_allocator_interface\n{\npublic:\n  one_block_allocator() = default;\n\npublic:\n  // Note that this allocates memory immediately, so we just do not modify the event list and ignore it\n  void* allocate(backend_ctx_untyped&, const data_place& memory_node, ::std::ptrdiff_t& s, event_list&) override\n  {\n    if (busy)\n    {\n      s = -s;\n      return nullptr;\n    }\n\n    EXPECT(memory_node.is_device());\n    if (!base)\n    {\n      cuda_safe_call(cudaMalloc(&base, s));\n    }\n\n    busy = true;\n\n    return base;\n  }\n\n  void deallocate(backend_ctx_untyped&, const data_place&, event_list&, void*, size_t) override\n  {\n    EXPECT(busy);\n    busy = false;\n  }\n\n  event_list deinit(backend_ctx_untyped&) override\n  {\n    return event_list();\n  }\n\n  std::string to_string() const override\n  {\n    return \"dummy\";\n  }\n\nprivate:\n  // We have a single block, so we keep its address, and a flag to indicate\n  // if it's busy\n  void* base = nullptr;\n  bool busy  = false;\n};\n\nint main(int argc, char** argv)\n{\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  stream_ctx ctx;\n\n  auto dummy_alloc = block_allocator<one_block_allocator>(ctx);\n  ctx.set_allocator(dummy_alloc);\n\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  EXPECT(nblocks > 0);\n  EXPECT(block_size > 0);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < nblocks; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[&](cudaStream_t s, auto buf) {\n      // Wait 100ms to have a more stressful asynchronous execution\n      cuda_sleep(100, s);\n      kernel<<<1, 1, 0, s>>>(i, buf);\n    };\n  }\n\n  ctx.finalize();\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    EXPECT(h_buffer[block_size * i] == i);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel(int i, slice<char> buf)\n{\n  buf[0] = (char) i;\n}\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n// Dummy allocator which only allocates a single block on a single device\n//\n// The first allocation succeeds, next attempt will fail until buffer is\n// deallocated.\nclass one_block_allocator : public block_allocator_interface\n{\npublic:\n  one_block_allocator() = default;\n\npublic:\n  // Note that this allocates memory immediately, so we just do not modify the event list and ignore it\n  void* allocate(backend_ctx_untyped&, const data_place& memory_node, ::std::ptrdiff_t& s, event_list&) override\n  {\n    if (busy)\n    {\n      s = -s;\n      return nullptr;\n    }\n\n    EXPECT(memory_node.is_device());\n    if (!base)\n    {\n      cuda_safe_call(hipMalloc(&base, s));\n    }\n\n    busy = true;\n\n    return base;\n  }\n\n  void deallocate(backend_ctx_untyped&, const data_place&, event_list&, void*, size_t) override\n  {\n    EXPECT(busy);\n    busy = false;\n  }\n\n  event_list deinit(backend_ctx_untyped&) override\n  {\n    return event_list();\n  }\n\n  std::string to_string() const override\n  {\n    return \"dummy\";\n  }\n\nprivate:\n  // We have a single block, so we keep its address, and a flag to indicate\n  // if it's busy\n  void* base = nullptr;\n  bool busy  = false;\n};\n\nint main(int argc, char** argv)\n{\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  stream_ctx ctx;\n\n  auto dummy_alloc = block_allocator<one_block_allocator>(ctx);\n  ctx.set_allocator(dummy_alloc);\n\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  EXPECT(nblocks > 0);\n  EXPECT(block_size > 0);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < nblocks; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[&](hipStream_t s, auto buf) {\n      // Wait 100ms to have a more stressful asynchronous execution\n      cuda_sleep(100, s);\n      kernel<<<1, 1, 0, s>>>(i, buf);\n    };\n  }\n\n  ctx.finalize();\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    EXPECT(h_buffer[block_size * i] == i);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reclaiming/stream.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\n#if !_CCCL_COMPILER(MSVC)\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel()\n{\n  // No-op\n}\n#endif // !_CCCL_COMPILER(MSVC)\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)\n{\n// TODO fix setenv\n#if !_CCCL_COMPILER(MSVC)\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  // At most 1 buffer is allocated at the same time\n  setenv(\"MAX_ALLOC_CNT\", \"1\", 0);\n\n  graph_ctx ctx;\n\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < 3; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[&](cudaStream_t s, auto /*unused*/) {\n      kernel<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n#endif // !_CCCL_COMPILER(MSVC)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\n#if !_CCCL_COMPILER(MSVC)\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel()\n{\n  // No-op\n}\n#endif // !_CCCL_COMPILER(MSVC)\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)\n{\n// TODO fix setenv\n#if !_CCCL_COMPILER(MSVC)\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  // At most 1 buffer is allocated at the same time\n  setenv(\"MAX_ALLOC_CNT\", \"1\", 0);\n\n  graph_ctx ctx;\n\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n\n  char* h_buffer = new char[nblocks * block_size];\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < 3; i++)\n  {\n    ctx.task(handles[i % nblocks].rw())->*[&](hipStream_t s, auto /*unused*/) {\n      kernel<<<1, 1, 0, s>>>();\n    };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n#endif // !_CCCL_COMPILER(MSVC)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reclaiming/graph.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel()\n{\n  // No-op\n}\n\nint main(int argc, char** argv)\n{\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  // At most 1 buffer is allocated at the same time\n  setenv(\"MAX_ALLOC_CNT\", \"2\", 1);\n\n  graph_ctx ctx;\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n  std::vector<char> h_buffer(nblocks * block_size);\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < 3; i++)\n  {\n    ctx.task(handles[i % nblocks].read(), handles[(i + 1) % nblocks].rw())\n        ->*[&](cudaStream_t s, auto /*unused*/, auto /*unused*/) {\n              kernel<<<1, 1, 0, s>>>();\n            };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[3]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel()\n{\n  // No-op\n}\n\nint main(int argc, char** argv)\n{\n  int nblocks       = 4;\n  size_t block_size = 1024 * 1024;\n\n  if (argc > 1)\n  {\n    nblocks = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    block_size = atoi(argv[2]);\n  }\n\n  // At most 1 buffer is allocated at the same time\n  setenv(\"MAX_ALLOC_CNT\", \"2\", 1);\n\n  graph_ctx ctx;\n  ::std::vector<logical_data<slice<char>>> handles(nblocks);\n  std::vector<char> h_buffer(nblocks * block_size);\n\n  for (int i = 0; i < nblocks; i++)\n  {\n    handles[i] = ctx.logical_data(make_slice(&h_buffer[i * block_size], block_size));\n    handles[i].set_symbol(\"D_\" + std::to_string(i));\n  }\n\n  // We only 2 buffers, we are forced to reuse the buffer from D0 for D2\n  for (int i = 0; i < 3; i++)\n  {\n    ctx.task(handles[i % nblocks].read(), handles[(i + 1) % nblocks].rw())\n        ->*[&](hipStream_t s, auto /*unused*/, auto /*unused*/) {\n              kernel<<<1, 1, 0, s>>>();\n            };\n  }\n\n  ctx.submit();\n\n  if (argc > 3)\n  {\n    std::cout << \"Generating DOT output in \" << argv[3] << '\\n';\n    ctx.print_to_dot(argv[3]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reclaiming/graph_2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test makes sure we can generate a dot file\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n// TODO (miscco): Make it work for windows\n#if !_CCCL_COMPILER(MSVC)\n  // Generate a random filename\n  int r = rand();\n\n  char filename[64];\n  snprintf(filename, 64, \"output_%d.dot\", r);\n  // fprintf(stderr, \"filename %s\\n\", filename);\n\n  graph_ctx ctx;\n\n  auto lA = ctx.logical_data(shape_of<slice<char>>(64));\n  ctx.task(lA.write())->*[](cudaStream_t s, auto) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n  ctx.task(lA.rw())->*[](cudaStream_t s, auto) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n  ctx.print_to_dot(filename, cudaGraphDebugDotFlagsVerbose);\n  ctx.finalize();\n\n  // Make sure the file exists, and erase it\n  EXPECT(access(filename, F_OK) != -1);\n\n  EXPECT(unlink(filename) == 0);\n#endif // !_CCCL_COMPILER(MSVC)\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test makes sure we can generate a dot file\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nint main()\n{\n// TODO (miscco): Make it work for windows\n#if !_CCCL_COMPILER(MSVC)\n  // Generate a random filename\n  int r = rand();\n\n  char filename[64];\n  snprintf(filename, 64, \"output_%d.dot\", r);\n  // fprintf(stderr, \"filename %s\\n\", filename);\n\n  graph_ctx ctx;\n\n  auto lA = ctx.logical_data(shape_of<slice<char>>(64));\n  ctx.task(lA.write())->*[](hipStream_t s, auto) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n  ctx.task(lA.rw())->*[](hipStream_t s, auto) {\n    dummy<<<1, 1, 0, s>>>();\n  };\n  ctx.print_to_dot(filename, hipGraphDebugDotFlagsVerbose);\n  ctx.finalize();\n\n  // Make sure the file exists, and erase it\n  EXPECT(access(filename, F_OK) != -1);\n\n  EXPECT(unlink(filename) == 0);\n#endif // !_CCCL_COMPILER(MSVC)\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/dot/graph_print_to_dot.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/stf.cuh>\n\n/*\n * The goal of this test is to ensure that using read access modes actually\n * results in concurrent tasks\n */\n\nusing namespace cuda::experimental::stf;\n\n/**\n * @brief Call `__nanosleep` (potentially repeatedly) to sleep `nanoseconds` nanoseconds. Supports sleep times longer\n * than 4 billion nanoseconds (i.e. 4 seconds).\n *\n * @param nanoseconds how many nanoseconds to sleep\n * @return void\n */\n__global__ void nano_sleep(unsigned long long nanoseconds)\n{\n#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n  static constexpr auto m = std::numeric_limits<unsigned int>::max();\n  for (;;)\n  {\n    if (nanoseconds > m)\n    {\n      __nanosleep(m);\n      nanoseconds -= m;\n    }\n    else\n    {\n      __nanosleep(static_cast<unsigned int>(nanoseconds));\n      break;\n    }\n  }\n#else\n  const clock_t end = clock() + nanoseconds / (1000000000ULL / CLOCKS_PER_SEC);\n  while (clock() < end)\n  {\n    // busy wait\n  }\n#endif\n}\n\nvoid run(context& ctx, int NTASKS, int ms)\n{\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  ctx.task().add_deps(handle.rw())->*[](cudaStream_t stream) {\n    nano_sleep<<<1, 1, 0, stream>>>(0);\n  };\n\n  for (int iter = 0; iter < 10; iter++)\n  {\n    for (int k = 0; k < NTASKS; k++)\n    {\n      ctx.task().add_deps(handle.read())->*[&](cudaStream_t stream) {\n        nano_sleep<<<1, 1, 0, stream>>>(ms * 1000ULL * 1000ULL);\n      };\n    }\n\n    ctx.task().add_deps(handle.rw())->*[&](cudaStream_t stream) {\n      nano_sleep<<<1, 1, 0, stream>>>(0);\n    };\n  }\n\n  ctx.finalize();\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 256;\n  int ms     = 40;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  context ctx;\n  run(ctx, NTASKS, ms);\n  ctx = graph_ctx();\n  run(ctx, NTASKS, ms);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n/*\n * The goal of this test is to ensure that using read access modes actually\n * results in concurrent tasks\n */\n\nusing namespace cuda::experimental::stf;\n\n/**\n * @brief Call `__nanosleep` (potentially repeatedly) to sleep `nanoseconds` nanoseconds. Supports sleep times longer\n * than 4 billion nanoseconds (i.e. 4 seconds).\n *\n * @param nanoseconds how many nanoseconds to sleep\n * @return void\n */\n__global__ void nano_sleep(unsigned long long nanoseconds)\n{\n#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n  static constexpr auto m = std::numeric_limits<unsigned int>::max();\n  for (;;)\n  {\n    if (nanoseconds > m)\n    {\n      __nanosleep(m);\n      nanoseconds -= m;\n    }\n    else\n    {\n      __nanosleep(static_cast<unsigned int>(nanoseconds));\n      break;\n    }\n  }\n#else\n  const clock_t end = clock() + nanoseconds / (1000000000ULL / CLOCKS_PER_SEC);\n  while (clock() < end)\n  {\n    // busy wait\n  }\n#endif\n}\n\nvoid run(context& ctx, int NTASKS, int ms)\n{\n  int dummy[1];\n  auto handle = ctx.logical_data(dummy);\n\n  ctx.task().add_deps(handle.rw())->*[](hipStream_t stream) {\n    nano_sleep<<<1, 1, 0, stream>>>(0);\n  };\n\n  for (int iter = 0; iter < 10; iter++)\n  {\n    for (int k = 0; k < NTASKS; k++)\n    {\n      ctx.task().add_deps(handle.read())->*[&](hipStream_t stream) {\n        nano_sleep<<<1, 1, 0, stream>>>(ms * 1000ULL * 1000ULL);\n      };\n    }\n\n    ctx.task().add_deps(handle.rw())->*[&](hipStream_t stream) {\n      nano_sleep<<<1, 1, 0, stream>>>(0);\n    };\n  }\n\n  ctx.finalize();\n}\n\nint main(int argc, char** argv)\n{\n  int NTASKS = 256;\n  int ms     = 40;\n\n  if (argc > 1)\n  {\n    NTASKS = atoi(argv[1]);\n  }\n\n  if (argc > 2)\n  {\n    ms = atoi(argv[2]);\n  }\n\n  context ctx;\n  run(ctx, NTASKS, ms);\n  ctx = graph_ctx();\n  run(ctx, NTASKS, ms);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cpp/concurrency_test.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * In this example, the user provides streams in which the STF model inserts the proper dependencies\n */\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\nint main()\n{\n  stream_ctx ctx;\n  double vA, vB, vC, vD;\n  auto A = ctx.logical_data(make_slice(&vA, 1));\n  auto B = ctx.logical_data(make_slice(&vB, 1));\n  auto C = ctx.logical_data(make_slice(&vC, 1));\n  auto D = ctx.logical_data(make_slice(&vD, 1));\n\n  // We are going to submit kernels with the following data accesses, where\n  // K2 and K3 can be executed concurrently, after K1 and been executed, and\n  // before K4 is executed.\n  // K1(Aw); K2(Ar,Bw); K3(Ar, Cw); K4(Br,Cr,Dw);\n\n  // User-provided streams\n  cudaStream_t K1_stream;\n  cudaStream_t K2_stream;\n  cudaStream_t K3_stream;\n  cudaStream_t K4_stream;\n  cudaStreamCreate(&K1_stream);\n  cudaStreamCreate(&K2_stream);\n  cudaStreamCreate(&K3_stream);\n  cudaStreamCreate(&K4_stream);\n\n  // Kernel 1 : A(write)\n  auto k1 = ctx.task(A.rw());\n  k1.set_stream(K1_stream);\n  k1.set_symbol(\"K1\");\n  k1.start();\n  cuda_sleep(500, K1_stream);\n  k1.end();\n\n  // Kernel 2 : A(read) B(write)\n  auto k2 = ctx.task(A.read(), B.write());\n  k2.set_stream(K2_stream);\n  k2.set_symbol(\"K2\");\n  k2.start();\n  cuda_sleep(500, K2_stream);\n  k2.end();\n\n  // Kernel 3 : A(read) C(write)\n  auto k3 = ctx.task(A.read(), C.write());\n  k3.set_stream(K3_stream);\n  k3.set_symbol(\"K3\");\n  k3.start();\n  cuda_sleep(500, K3_stream);\n  k3.end();\n\n  // Kernel 4 : B(read) C(read) D(write)\n  auto k4 = ctx.task(B.read(), C.read(), D.write());\n  k4.set_stream(K4_stream);\n  k4.set_symbol(\"K4\");\n  k4.start();\n  cuda_sleep(500, K4_stream);\n  k4.end();\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * In this example, the user provides streams in which the STF model inserts the proper dependencies\n */\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\nint main()\n{\n  stream_ctx ctx;\n  double vA, vB, vC, vD;\n  auto A = ctx.logical_data(make_slice(&vA, 1));\n  auto B = ctx.logical_data(make_slice(&vB, 1));\n  auto C = ctx.logical_data(make_slice(&vC, 1));\n  auto D = ctx.logical_data(make_slice(&vD, 1));\n\n  // We are going to submit kernels with the following data accesses, where\n  // K2 and K3 can be executed concurrently, after K1 and been executed, and\n  // before K4 is executed.\n  // K1(Aw); K2(Ar,Bw); K3(Ar, Cw); K4(Br,Cr,Dw);\n\n  // User-provided streams\n  hipStream_t K1_stream;\n  hipStream_t K2_stream;\n  hipStream_t K3_stream;\n  hipStream_t K4_stream;\n  hipStreamCreate(&K1_stream);\n  hipStreamCreate(&K2_stream);\n  hipStreamCreate(&K3_stream);\n  hipStreamCreate(&K4_stream);\n\n  // Kernel 1 : A(write)\n  auto k1 = ctx.task(A.rw());\n  k1.set_stream(K1_stream);\n  k1.set_symbol(\"K1\");\n  k1.start();\n  cuda_sleep(500, K1_stream);\n  k1.end();\n\n  // Kernel 2 : A(read) B(write)\n  auto k2 = ctx.task(A.read(), B.write());\n  k2.set_stream(K2_stream);\n  k2.set_symbol(\"K2\");\n  k2.start();\n  cuda_sleep(500, K2_stream);\n  k2.end();\n\n  // Kernel 3 : A(read) C(write)\n  auto k3 = ctx.task(A.read(), C.write());\n  k3.set_stream(K3_stream);\n  k3.set_symbol(\"K3\");\n  k3.start();\n  cuda_sleep(500, K3_stream);\n  k3.end();\n\n  // Kernel 4 : B(read) C(read) D(write)\n  auto k4 = ctx.task(B.read(), C.read(), D.write());\n  k4.set_stream(K4_stream);\n  k4.set_symbol(\"K4\");\n  k4.start();\n  cuda_sleep(500, K4_stream);\n  k4.end();\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cpp/user_streams.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple times in the same\n *        task even with different access modes (which should be combined)\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(T* a, const T* b)\n{\n  *a = *b + 1;\n}\n\ntemplate <class Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  int var         = 42;\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n\n  // da and db are for the same variable : we expect it to be equivalent to a RW access\n  ctx.task(var_handle.write(), var_handle.read())->*[](cudaStream_t stream, auto da, auto db) {\n    add<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle());\n  };\n\n  // Read that value on the host\n  ctx.host_launch(var_handle.read())->*[](auto da) {\n    [[maybe_unused]] int result = *da.data_handle();\n    assert(result == 43);\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple times in the same\n *        task even with different access modes (which should be combined)\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(T* a, const T* b)\n{\n  *a = *b + 1;\n}\n\ntemplate <class Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  int var         = 42;\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n\n  // da and db are for the same variable : we expect it to be equivalent to a RW access\n  ctx.task(var_handle.write(), var_handle.read())->*[](hipStream_t stream, auto da, auto db) {\n    add<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle());\n  };\n\n  // Read that value on the host\n  ctx.host_launch(var_handle.read())->*[](auto da) {\n    [[maybe_unused]] int result = *da.data_handle();\n    assert(result == 43);\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cpp/redundant_data_different_modes.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Test the behavior of the get_stream() method of the tasks in the different backends\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nvoid test_stream()\n{\n  // stream context\n  context ctx;\n\n  auto token = ctx.token();\n  EXPECT(token.is_void_interface());\n  auto t = ctx.task(token.write());\n  t.start();\n  cudaStream_t s = t.get_stream();\n  EXPECT(s != nullptr);\n  dummy<<<1, 1, 0, s>>>();\n  t.end();\n  ctx.finalize();\n}\n\nvoid test_graph()\n{\n  context ctx = graph_ctx();\n\n  auto token = ctx.token();\n  auto t     = ctx.task(token.write());\n  t.start();\n  cudaStream_t s = t.get_stream();\n  // We are not capturing so there is no stream associated\n  EXPECT(s == nullptr);\n  t.end();\n\n  auto t2 = ctx.task(token.rw());\n  t2.enable_capture();\n  t2.start();\n  cudaStream_t s2 = t2.get_stream();\n  // We are capturing so the stream used for capture is associated to the task\n  EXPECT(s2 != nullptr);\n  t2.end();\n\n  ctx.finalize();\n}\n\nint main()\n{\n  test_stream();\n  test_graph();\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Test the behavior of the get_stream() method of the tasks in the different backends\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\nvoid test_stream()\n{\n  // stream context\n  context ctx;\n\n  auto token = ctx.token();\n  EXPECT(token.is_void_interface());\n  auto t = ctx.task(token.write());\n  t.start();\n  hipStream_t s = t.get_stream();\n  EXPECT(s != nullptr);\n  dummy<<<1, 1, 0, s>>>();\n  t.end();\n  ctx.finalize();\n}\n\nvoid test_graph()\n{\n  context ctx = graph_ctx();\n\n  auto token = ctx.token();\n  auto t     = ctx.task(token.write());\n  t.start();\n  hipStream_t s = t.get_stream();\n  // We are not capturing so there is no stream associated\n  EXPECT(s == nullptr);\n  t.end();\n\n  auto t2 = ctx.task(token.rw());\n  t2.enable_capture();\n  t2.start();\n  hipStream_t s2 = t2.get_stream();\n  // We are capturing so the stream used for capture is associated to the task\n  EXPECT(s2 != nullptr);\n  t2.end();\n\n  ctx.finalize();\n}\n\nint main()\n{\n  test_stream();\n  test_graph();\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cpp/task_get_stream.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple time in a task\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void diff_cnt(int n, T* x, T* y, int* delta)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    if (y[ind] != x[ind])\n    {\n      atomicAdd(delta, 1);\n    }\n  }\n}\n\ntemplate <typename Ctx, typename T>\nvoid compare_two_vectors(Ctx& ctx, logical_data<T>& a, logical_data<T>& b, int& delta)\n{\n  auto delta_cnt = ctx.logical_data(make_slice(&delta, 1));\n  const auto n   = a.shape().extent(0);\n\n  // Count the number of differences\n  ctx.task(a.read(), b.read(), delta_cnt.rw())->*[=](cudaStream_t stream, auto da, auto db, auto ddelta) {\n    diff_cnt<<<16, 128, 0, stream>>>(static_cast<int>(n), da.data_handle(), db.data_handle(), ddelta.data_handle());\n  };\n\n  // Read that value on the host\n  ctx.host_launch(delta_cnt.read())->*[&](auto /*unused*/) {};\n}\n\nstatic const size_t N = 12;\n\ntemplate <class Ctx>\nvoid run(double (&X)[N], double (&Y)[N])\n{\n  Ctx ctx;\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  int ret1 = 0, ret2 = 0;\n\n  compare_two_vectors(ctx, handle_X, handle_Y, ret1);\n  compare_two_vectors(ctx, handle_X, handle_X, ret2);\n\n  ctx.finalize();\n\n  // After sync, we can inspect the returned values.\n  // First two vectors are different\n  assert(ret1 > 0);\n  // Other two vectors are equal\n  assert(ret2 == 0);\n}\n\nint main()\n{\n  double X[N], Y[N];\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  run<stream_ctx>(X, Y);\n  run<graph_ctx>(X, Y);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple time in a task\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void diff_cnt(int n, T* x, T* y, int* delta)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    if (y[ind] != x[ind])\n    {\n      atomicAdd(delta, 1);\n    }\n  }\n}\n\ntemplate <typename Ctx, typename T>\nvoid compare_two_vectors(Ctx& ctx, logical_data<T>& a, logical_data<T>& b, int& delta)\n{\n  auto delta_cnt = ctx.logical_data(make_slice(&delta, 1));\n  const auto n   = a.shape().extent(0);\n\n  // Count the number of differences\n  ctx.task(a.read(), b.read(), delta_cnt.rw())->*[=](hipStream_t stream, auto da, auto db, auto ddelta) {\n    diff_cnt<<<16, 128, 0, stream>>>(static_cast<int>(n), da.data_handle(), db.data_handle(), ddelta.data_handle());\n  };\n\n  // Read that value on the host\n  ctx.host_launch(delta_cnt.read())->*[&](auto /*unused*/) {};\n}\n\nstatic const size_t N = 12;\n\ntemplate <class Ctx>\nvoid run(double (&X)[N], double (&Y)[N])\n{\n  Ctx ctx;\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  int ret1 = 0, ret2 = 0;\n\n  compare_two_vectors(ctx, handle_X, handle_Y, ret1);\n  compare_two_vectors(ctx, handle_X, handle_X, ret2);\n\n  ctx.finalize();\n\n  // After sync, we can inspect the returned values.\n  // First two vectors are different\n  assert(ret1 > 0);\n  // Other two vectors are equal\n  assert(ret2 == 0);\n}\n\nint main()\n{\n  double X[N], Y[N];\n\n  for (size_t ind = 0; ind < N; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  run<stream_ctx>(X, Y);\n  run<graph_ctx>(X, Y);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/cpp/redundant_data.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n\nvoid debug_info(cudaStream_t stream, CUgreenCtx g_ctx)\n{\n  // Get the green context associated to that CUDA stream\n  CUgreenCtx stream_cugc;\n  cuda_safe_call(cuStreamGetGreenCtx(CUstream(stream), &stream_cugc));\n  assert(stream_cugc != nullptr);\n\n  CUcontext stream_green_primary;\n  CUcontext place_green_primary;\n\n  unsigned long long stream_ctxId;\n  unsigned long long place_ctxId;\n\n  // Convert green contexts to primary contexts and get their ID\n  cuda_safe_call(cuCtxFromGreenCtx(&stream_green_primary, stream_cugc));\n  cuda_safe_call(cuCtxGetId(stream_green_primary, &stream_ctxId));\n\n  cuda_safe_call(cuCtxFromGreenCtx(&place_green_primary, g_ctx));\n  cuda_safe_call(cuCtxGetId(place_green_primary, &place_ctxId));\n\n  // Make sure the stream belongs to the same green context as the execution place\n  EXPECT(stream_ctxId == place_ctxId);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 8;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n\n  int NITER   = 8;\n  const int n = 16 * 1024 * 1024;\n\n  std::vector<double> X(n);\n  std::vector<double> Y(n);\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  std::vector<exec_place> exec_places;\n\n  // The green_context_helper class automates the creation of green context views\n  std::vector<green_context_helper> gc(ndevs);\n  for (int devid = 0; devid < ndevs; devid++)\n  {\n    gc[devid] = green_context_helper(num_sms, devid);\n\n    auto& g_ctx = gc[devid];\n    auto cnt    = g_ctx.get_count();\n    for (size_t i = 0; i < cnt; i++)\n    {\n      exec_places.push_back(exec_place::green_ctx(g_ctx.get_view(i)));\n    }\n  }\n\n  auto where = make_grid(exec_places);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    ctx.parallel_for(blocked_partition(), where, handle_X.shape(), handle_X.rw(), handle_Y.read())\n        ->*[] __device__(size_t i, auto x, auto y) {\n              x(i) += y(i);\n            };\n  }\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n\nvoid debug_info(hipStream_t stream, CUgreenCtx g_ctx)\n{\n  // Get the green context associated to that CUDA stream\n  CUgreenCtx stream_cugc;\n  cuda_safe_call(cuStreamGetGreenCtx(hipStream_t(stream), &stream_cugc));\n  assert(stream_cugc != nullptr);\n\n  hipCtx_t stream_green_primary;\n  hipCtx_t place_green_primary;\n\n  unsigned long long stream_ctxId;\n  unsigned long long place_ctxId;\n\n  // Convert green contexts to primary contexts and get their ID\n  cuda_safe_call(cuCtxFromGreenCtx(&stream_green_primary, stream_cugc));\n  cuda_safe_call(cuCtxGetId(stream_green_primary, &stream_ctxId));\n\n  cuda_safe_call(cuCtxFromGreenCtx(&place_green_primary, g_ctx));\n  cuda_safe_call(cuCtxGetId(place_green_primary, &place_ctxId));\n\n  // Make sure the stream belongs to the same green context as the execution place\n  EXPECT(stream_ctxId == place_ctxId);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 8;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n\n  int NITER   = 8;\n  const int n = 16 * 1024 * 1024;\n\n  std::vector<double> X(n);\n  std::vector<double> Y(n);\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  std::vector<exec_place> exec_places;\n\n  // The green_context_helper class automates the creation of green context views\n  std::vector<green_context_helper> gc(ndevs);\n  for (int devid = 0; devid < ndevs; devid++)\n  {\n    gc[devid] = green_context_helper(num_sms, devid);\n\n    auto& g_ctx = gc[devid];\n    auto cnt    = g_ctx.get_count();\n    for (size_t i = 0; i < cnt; i++)\n    {\n      exec_places.push_back(exec_place::green_ctx(g_ctx.get_view(i)));\n    }\n  }\n\n  auto where = make_grid(exec_places);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    ctx.parallel_for(blocked_partition(), where, handle_X.shape(), handle_X.rw(), handle_Y.read())\n        ->*[] __device__(size_t i, auto x, auto y) {\n              x(i) += y(i);\n            };\n  }\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/green_context/gc_grid.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n\nvoid debug_info(cudaStream_t stream, CUgreenCtx g_ctx)\n{\n  // Get the green context associated to that CUDA stream\n  CUgreenCtx stream_cugc;\n  cuda_safe_call(cuStreamGetGreenCtx(CUstream(stream), &stream_cugc));\n  assert(stream_cugc != nullptr);\n\n  CUcontext stream_green_primary;\n  CUcontext place_green_primary;\n\n  unsigned long long stream_ctxId;\n  unsigned long long place_ctxId;\n\n  // Convert green contexts to primary contexts and get their ID\n  cuda_safe_call(cuCtxFromGreenCtx(&stream_green_primary, stream_cugc));\n  cuda_safe_call(cuCtxGetId(stream_green_primary, &stream_ctxId));\n\n  cuda_safe_call(cuCtxFromGreenCtx(&place_green_primary, g_ctx));\n  cuda_safe_call(cuCtxGetId(place_green_primary, &place_ctxId));\n\n  // Make sure the stream belongs to the same green context as the execution place\n  EXPECT(stream_ctxId == place_ctxId);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 8;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n  const double alpha = 2.0;\n\n  int NITER   = 30;\n  const int n = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  // The green_context_helper class automates the creation of green context views\n  std::vector<green_context_helper> gc(ndevs);\n  for (int devid = 0; devid < ndevs; devid++)\n  {\n    gc[devid] = green_context_helper(num_sms, devid);\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (int devid = 0; devid < ndevs; devid++)\n    {\n      auto& g_ctx = gc[devid];\n      auto cnt    = g_ctx.get_count();\n      ctx.task(exec_place::green_ctx(g_ctx.get_view(iter % cnt)), handle_X.read(), handle_Y.rw())\n          ->*[&](cudaStream_t stream, auto dX, auto dY) {\n                debug_info(stream, g_ctx.get_view(iter % cnt).g_ctx);\n                axpy<<<16, 16, 0, stream>>>(alpha, dX, dY);\n              };\n    }\n  }\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto hX, auto hY) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      EXPECT(fabs(hY(ind) - (2.0 * ind - 3.0) - NITER * ndevs * alpha * hX(ind)) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n\nvoid debug_info(hipStream_t stream, CUgreenCtx g_ctx)\n{\n  // Get the green context associated to that CUDA stream\n  CUgreenCtx stream_cugc;\n  cuda_safe_call(cuStreamGetGreenCtx(hipStream_t(stream), &stream_cugc));\n  assert(stream_cugc != nullptr);\n\n  hipCtx_t stream_green_primary;\n  hipCtx_t place_green_primary;\n\n  unsigned long long stream_ctxId;\n  unsigned long long place_ctxId;\n\n  // Convert green contexts to primary contexts and get their ID\n  cuda_safe_call(cuCtxFromGreenCtx(&stream_green_primary, stream_cugc));\n  cuda_safe_call(cuCtxGetId(stream_green_primary, &stream_ctxId));\n\n  cuda_safe_call(cuCtxFromGreenCtx(&place_green_primary, g_ctx));\n  cuda_safe_call(cuCtxGetId(place_green_primary, &place_ctxId));\n\n  // Make sure the stream belongs to the same green context as the execution place\n  EXPECT(stream_ctxId == place_ctxId);\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 8;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  stream_ctx ctx;\n  const double alpha = 2.0;\n\n  int NITER   = 30;\n  const int n = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  // The green_context_helper class automates the creation of green context views\n  std::vector<green_context_helper> gc(ndevs);\n  for (int devid = 0; devid < ndevs; devid++)\n  {\n    gc[devid] = green_context_helper(num_sms, devid);\n  }\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    for (int devid = 0; devid < ndevs; devid++)\n    {\n      auto& g_ctx = gc[devid];\n      auto cnt    = g_ctx.get_count();\n      ctx.task(exec_place::green_ctx(g_ctx.get_view(iter % cnt)), handle_X.read(), handle_Y.rw())\n          ->*[&](hipStream_t stream, auto dX, auto dY) {\n                debug_info(stream, g_ctx.get_view(iter % cnt).g_ctx);\n                axpy<<<16, 16, 0, stream>>>(alpha, dX, dY);\n              };\n    }\n  }\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto hX, auto hY) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      EXPECT(fabs(hY(ind) - (2.0 * ind - 3.0) - NITER * ndevs * alpha * hX(ind)) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/green_context/axpy_gc.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 16;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  graph_ctx ctx;\n  const double alpha = 2.0;\n\n  int NITER   = 30;\n  const int n = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  // The green_context_helper class automates the creation of green context views\n  green_context_helper gc(num_sms);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    auto cnt = gc.get_count();\n    ctx.task(exec_place::green_ctx(gc.get_view(iter % cnt)), handle_X.read(), handle_Y.rw())\n        ->*[&](cudaStream_t stream, auto dX, auto dY) {\n              axpy<<<16, 16, 0, stream>>>(alpha, dX, dY);\n            };\n  }\n\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto hX, auto hY) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      EXPECT(fabs(hY(ind) - (2.0 * ind - 3.0) - NITER * ndevs * alpha * hX(ind)) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/exec/green_context.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Green contexts are only supported since CUDA 12.4\n#if _CCCL_CTK_AT_LEAST(12, 4)\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  size_t n = x.extent(0);\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y(ind) += a * x(ind);\n  }\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 4)\n\nint main()\n{\n#if _CCCL_CTK_BELOW(12, 4)\n  fprintf(stderr, \"Green contexts are not supported by this version of CUDA: skipping test.\\n\");\n  return 0;\n#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv\n  int ndevs;\n  const int num_sms = 16;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  graph_ctx ctx;\n  const double alpha = 2.0;\n\n  int NITER   = 30;\n  const int n = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(make_slice(&X[0], n));\n  auto handle_Y = ctx.logical_data(make_slice(&Y[0], n));\n\n  // The green_context_helper class automates the creation of green context views\n  green_context_helper gc(num_sms);\n\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    auto cnt = gc.get_count();\n    ctx.task(exec_place::green_ctx(gc.get_view(iter % cnt)), handle_X.read(), handle_Y.rw())\n        ->*[&](hipStream_t stream, auto dX, auto dY) {\n              axpy<<<16, 16, 0, stream>>>(alpha, dX, dY);\n            };\n  }\n\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto hX, auto hY) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      EXPECT(fabs(hY(ind) - (2.0 * ind - 3.0) - NITER * ndevs * alpha * hX(ind)) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/green_context/cuda_graph.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This example illustrates how to introduce CUDASTF contexts within existing stream-synchronized library calls\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// B += alpha*A;\n__global__ void axpy(double alpha, const double* d_ptrA, double* d_ptrB, size_t N)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] += alpha * d_ptrA[i];\n  }\n}\n\nint main()\n{\n  double *d_ptrA, *d_ptrB;\n  const size_t N     = 128 * 1024;\n  const size_t NITER = 128;\n\n  // User allocated memory\n  cuda_safe_call(cudaMalloc(&d_ptrA, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc(&d_ptrB, N * sizeof(double)));\n\n  cudaStream_t stream;\n  cuda_safe_call(cudaStreamCreate(&stream));\n\n  async_resources_handle handle;\n  for (size_t i = 0; i < NITER; i++)\n  {\n    stream_ctx ctx(stream, handle);\n    auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n    auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n\n    ctx.parallel_for(lA.shape(), lA.write())->*[] __device__(size_t i, auto a) {\n      a(i) = sin(double(i));\n    };\n    ctx.parallel_for(lB.shape(), lB.write())->*[] __device__(size_t i, auto b) {\n      b(i) = cos(double(i));\n    };\n\n    ctx.task(lA.read(), lB.rw())->*[=](cudaStream_t s, auto a, auto b) {\n      axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n    };\n\n    // Note that this is non-blocking because we have creating the stream_ctx\n    // relative to a user-provided CUDA stream\n    ctx.finalize();\n\n    axpy<<<128, 32, 0, stream>>>(2.0, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This example illustrates how to introduce CUDASTF contexts within existing stream-synchronized library calls\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// B += alpha*A;\n__global__ void axpy(double alpha, const double* d_ptrA, double* d_ptrB, size_t N)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] += alpha * d_ptrA[i];\n  }\n}\n\nint main()\n{\n  double *d_ptrA, *d_ptrB;\n  const size_t N     = 128 * 1024;\n  const size_t NITER = 128;\n\n  // User allocated memory\n  cuda_safe_call(hipMalloc(&d_ptrA, N * sizeof(double)));\n  cuda_safe_call(hipMalloc(&d_ptrB, N * sizeof(double)));\n\n  hipStream_t stream;\n  cuda_safe_call(hipStreamCreate(&stream));\n\n  async_resources_handle handle;\n  for (size_t i = 0; i < NITER; i++)\n  {\n    stream_ctx ctx(stream, handle);\n    auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n    auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n\n    ctx.parallel_for(lA.shape(), lA.write())->*[] __device__(size_t i, auto a) {\n      a(i) = sin(double(i));\n    };\n    ctx.parallel_for(lB.shape(), lB.write())->*[] __device__(size_t i, auto b) {\n      b(i) = cos(double(i));\n    };\n\n    ctx.task(lA.read(), lB.rw())->*[=](hipStream_t s, auto a, auto b) {\n      axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n    };\n\n    // Note that this is non-blocking because we have creating the stream_ctx\n    // relative to a user-provided CUDA stream\n    ctx.finalize();\n\n    axpy<<<128, 32, 0, stream>>>(2.0, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/interop_cuda.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Experiment with local context and temporary logical data\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\nstatic __global__ void kernel_set(T* addr, T val)\n{\n  *addr = val;\n}\n\nint main()\n{\n  stackable_ctx ctx;\n  auto lA = ctx.logical_data(shape_of<slice<int>>(1024));\n\n  ctx.task(lA.write())->*[](cudaStream_t stream, auto a) {\n    kernel_set<<<1, 1, 0, stream>>>(a.data_handle(), 42);\n  };\n\n  {\n    auto scope = ctx.graph_scope();\n\n    // Create temporary data in nested context\n    auto temp = ctx.logical_data(shape_of<slice<int>>(1024));\n\n    ctx.parallel_for(lA.shape(), temp.write(), lA.read())->*[] __device__(size_t i, auto temp, auto a) {\n      // Copy data and modify\n      temp(i) = a(i) * 2;\n    };\n\n    ctx.parallel_for(lA.shape(), lA.write(), temp.read())->*[] __device__(size_t i, auto a, auto temp) {\n      // Copy back\n      a(i) = temp(i) + 1;\n    };\n\n    // temp automatically cleaned up when scope ends\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Experiment with local context and temporary logical data\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\nstatic __global__ void kernel_set(T* addr, T val)\n{\n  *addr = val;\n}\n\nint main()\n{\n  stackable_ctx ctx;\n  auto lA = ctx.logical_data(shape_of<slice<int>>(1024));\n\n  ctx.task(lA.write())->*[](hipStream_t stream, auto a) {\n    kernel_set<<<1, 1, 0, stream>>>(a.data_handle(), 42);\n  };\n\n  {\n    auto scope = ctx.graph_scope();\n\n    // Create temporary data in nested context\n    auto temp = ctx.logical_data(shape_of<slice<int>>(1024));\n\n    ctx.parallel_for(lA.shape(), temp.write(), lA.read())->*[] __device__(size_t i, auto temp, auto a) {\n      // Copy data and modify\n      temp(i) = a(i) * 2;\n    };\n\n    ctx.parallel_for(lA.shape(), lA.write(), temp.read())->*[] __device__(size_t i, auto a, auto temp) {\n      // Copy back\n      a(i) = temp(i) + 1;\n    };\n\n    // temp automatically cleaned up when scope ends\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/stackable_tmp.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//! \\brief Test that ctx_t::logical_data_t works correctly when ctx_t is a template parameter\n//!\n//! This test ensures that the logical_data_t type alias defined in context classes\n//! (stream_ctx, graph_ctx, stackable_ctx, etc.) works correctly in generic code where\n//! the context type is a template parameter. This is essential for writing context-agnostic\n//! library functions and algorithms.\n\n#include <cuda/std/type_traits>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Helper kernel for testing\ntemplate <typename T>\n__global__ void scale_kernel(size_t n, T factor, T* data)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    data[i] *= factor;\n  }\n}\n\n// Test 1: Compile-time type trait tests\n// Verify that ctx_t::logical_data_t<T> is a valid type for different context types\nvoid test_compile_time_logical_data_t_exists()\n{\n  // Test that logical_data_t is defined for stream_ctx\n  using stream_logical_data_int    = stream_ctx::logical_data_t<slice<int>>;\n  using stream_logical_data_double = stream_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<stream_logical_data_int, logical_data<slice<int>>>,\n                \"stream_ctx::logical_data_t<slice<int>> should be logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<stream_logical_data_double, logical_data<slice<double>>>,\n                \"stream_ctx::logical_data_t<slice<double>> should be logical_data<slice<double>>\");\n\n  // Test that logical_data_t is defined for graph_ctx\n  using graph_logical_data_int    = graph_ctx::logical_data_t<slice<int>>;\n  using graph_logical_data_double = graph_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<graph_logical_data_int, logical_data<slice<int>>>,\n                \"graph_ctx::logical_data_t<slice<int>> should be logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<graph_logical_data_double, logical_data<slice<double>>>,\n                \"graph_ctx::logical_data_t<slice<double>> should be logical_data<slice<double>>\");\n\n  // Test that logical_data_t is defined for stackable_ctx\n  using stackable_logical_data_int    = stackable_ctx::logical_data_t<slice<int>>;\n  using stackable_logical_data_double = stackable_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<stackable_logical_data_int, stackable_logical_data<slice<int>>>,\n                \"stackable_ctx::logical_data_t<slice<int>> should be stackable_logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<stackable_logical_data_double, stackable_logical_data<slice<double>>>,\n                \"stackable_ctx::logical_data_t<slice<double>> should be stackable_logical_data<slice<double>>\");\n}\n\n// Test 2: Generic function that uses ctx_t::logical_data_t as a template parameter\n// This demonstrates the main use case: writing context-agnostic code\ntemplate <typename Ctx>\ntypename Ctx::template logical_data_t<slice<int>> create_and_initialize_data(Ctx& ctx, size_t n, int init_value)\n{\n  // Create logical data using the context-specific logical_data_t type\n  auto data = ctx.logical_data(shape_of<slice<int>>(n));\n  data.set_symbol(\"initialized_data\");\n\n  // Initialize the data\n  ctx.parallel_for(data.shape(), data.write())->*[init_value] __device__(size_t i, auto d) {\n    d(i) = init_value + static_cast<int>(i);\n  };\n\n  return data;\n}\n\n// Test 3: Generic function that takes and returns ctx_t::logical_data_t\n// Note: Using auto&& for the data parameter to avoid template deduction issues with dependent types\ntemplate <typename Ctx, typename LogicalData, typename Factor>\nvoid scale_data(Ctx& ctx, LogicalData&& data, Factor factor)\n{\n  ctx.task(data.rw())->*[factor](cudaStream_t s, auto d) {\n    scale_kernel<<<16, 128, 0, s>>>(d.size(), factor, d.data_handle());\n  };\n}\n\n// Test 4: Generic class that stores ctx_t::logical_data_t\ntemplate <typename Ctx>\nclass GenericDataHolder\n{\npublic:\n  using int_data_t    = typename Ctx::template logical_data_t<slice<int>>;\n  using double_data_t = typename Ctx::template logical_data_t<slice<double>>;\n\n  GenericDataHolder(Ctx& ctx, size_t n)\n      : int_data_(ctx.logical_data(shape_of<slice<int>>(n)))\n      , double_data_(ctx.logical_data(shape_of<slice<double>>(n)))\n  {\n    int_data_.set_symbol(\"holder_int_data\");\n    double_data_.set_symbol(\"holder_double_data\");\n  }\n\n  int_data_t& get_int_data()\n  {\n    return int_data_;\n  }\n\n  double_data_t& get_double_data()\n  {\n    return double_data_;\n  }\n\nprivate:\n  int_data_t int_data_;\n  double_data_t double_data_;\n};\n\n// Test 5: Generic function using auto with ctx_t::logical_data_t\ntemplate <typename Ctx>\nvoid test_auto_deduction(Ctx& ctx)\n{\n  // Create data using the generic function\n  auto data = create_and_initialize_data(ctx, 100, 42);\n\n  // Verify the type is correct\n  using expected_type = typename Ctx::template logical_data_t<slice<int>>;\n  static_assert(::std::is_same_v<decltype(data), expected_type>,\n                \"Auto-deduced type should match ctx_t::logical_data_t<slice<int>>\");\n\n  // Use the data\n  ctx.host_launch(data.read())->*[](auto d) {\n    for (size_t i = 0; i < d.size(); i++)\n    {\n      EXPECT(d(i) == 42 + static_cast<int>(i), \"Expected \", 42 + i, \" but got \", d(i), \" at index \", i);\n    }\n  };\n}\n\n// Main test function template\ntemplate <typename Ctx>\nvoid run_tests()\n{\n  Ctx ctx;\n\n  // Test 1: Basic creation and usage with template logical_data_t\n  {\n    auto data = create_and_initialize_data(ctx, 256, 10);\n\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == 10 + static_cast<int>(i));\n      }\n    };\n  }\n\n  // Test 2: Using scale_data generic function\n  {\n    auto data = create_and_initialize_data(ctx, 128, 5);\n    scale_data(ctx, data, 3);\n\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == (5 + static_cast<int>(i)) * 3);\n      }\n    };\n  }\n\n  // Test 3: Using GenericDataHolder class\n  {\n    GenericDataHolder<Ctx> holder(ctx, 64);\n\n    // Initialize int data\n    ctx.parallel_for(holder.get_int_data().shape(), holder.get_int_data().write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i * 2);\n    };\n\n    // Initialize double data\n    ctx.parallel_for(holder.get_double_data().shape(), holder.get_double_data().write())\n        ->*[] __device__(size_t i, auto d) {\n              d(i) = static_cast<double>(i) * 1.5;\n            };\n\n    // Verify int data\n    ctx.host_launch(holder.get_int_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i * 2));\n      }\n    };\n\n    // Verify double data\n    ctx.host_launch(holder.get_double_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(fabs(d(i) - static_cast<double>(i) * 1.5) < 1e-9);\n      }\n    };\n  }\n\n  // Test 4: Auto deduction\n  test_auto_deduction(ctx);\n\n  // Test 5: Multiple operations with logical_data_t\n  {\n    typename Ctx::template logical_data_t<slice<int>> data1 = ctx.logical_data(shape_of<slice<int>>(32));\n    typename Ctx::template logical_data_t<slice<int>> data2 = ctx.logical_data(shape_of<slice<int>>(32));\n\n    data1.set_symbol(\"data1\");\n    data2.set_symbol(\"data2\");\n\n    // Initialize both\n    ctx.parallel_for(data1.shape(), data1.write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i);\n    };\n\n    ctx.parallel_for(data2.shape(), data2.write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i * 10);\n    };\n\n    // Combine them\n    ctx.parallel_for(data1.shape(), data1.rw(), data2.read())->*[] __device__(size_t i, auto d1, auto d2) {\n      d1(i) += d2(i);\n    };\n\n    // Verify\n    ctx.host_launch(data1.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i + i * 10));\n      }\n    };\n  }\n\n  ctx.finalize();\n}\n\n// Additional test for stackable_ctx specific features\nvoid run_stackable_tests()\n{\n  stackable_ctx ctx;\n\n  // Test nested contexts with logical_data_t\n  {\n    auto data = create_and_initialize_data(ctx, 128, 20);\n\n    // Enter nested context\n    {\n      stackable_ctx::graph_scope_guard scope{ctx};\n\n      // Scale in nested context\n      scale_data(ctx, data, 2);\n\n      // Create local data in nested context\n      typename stackable_ctx::logical_data_t<slice<int>> local_data = ctx.logical_data(shape_of<slice<int>>(64));\n      local_data.set_symbol(\"nested_local\");\n\n      ctx.parallel_for(local_data.shape(), local_data.write())->*[] __device__(size_t i, auto d) {\n        d(i) = static_cast<int>(i * 5);\n      };\n\n      ctx.host_launch(local_data.read())->*[](auto d) {\n        for (size_t i = 0; i < d.size(); i++)\n        {\n          EXPECT(d(i) == static_cast<int>(i * 5));\n        }\n      };\n    }\n\n    // Verify data after nested context\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == (20 + static_cast<int>(i)) * 2);\n      }\n    };\n  }\n\n  // Test with GenericDataHolder in stackable context\n  {\n    GenericDataHolder<stackable_ctx> holder(ctx, 48);\n\n    ctx.parallel_for(holder.get_int_data().shape(), holder.get_int_data().write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i + 100);\n    };\n\n    ctx.host_launch(holder.get_int_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i + 100));\n      }\n    };\n  }\n\n  ctx.finalize();\n}\n\nint main()\n{\n  // Run compile-time tests\n  test_compile_time_logical_data_t_exists();\n\n  // Run runtime tests with different context types\n  run_tests<stream_ctx>();\n  run_tests<graph_ctx>();\n  run_tests<stackable_ctx>();\n\n  // Run stackable-specific tests\n  run_stackable_tests();\n\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//! \\brief Test that ctx_t::logical_data_t works correctly when ctx_t is a template parameter\n//!\n//! This test ensures that the logical_data_t type alias defined in context classes\n//! (stream_ctx, graph_ctx, stackable_ctx, etc.) works correctly in generic code where\n//! the context type is a template parameter. This is essential for writing context-agnostic\n//! library functions and algorithms.\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/type_traits>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Helper kernel for testing\ntemplate <typename T>\n__global__ void scale_kernel(size_t n, T factor, T* data)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    data[i] *= factor;\n  }\n}\n\n// Test 1: Compile-time type trait tests\n// Verify that ctx_t::logical_data_t<T> is a valid type for different context types\nvoid test_compile_time_logical_data_t_exists()\n{\n  // Test that logical_data_t is defined for stream_ctx\n  using stream_logical_data_int    = stream_ctx::logical_data_t<slice<int>>;\n  using stream_logical_data_double = stream_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<stream_logical_data_int, logical_data<slice<int>>>,\n                \"stream_ctx::logical_data_t<slice<int>> should be logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<stream_logical_data_double, logical_data<slice<double>>>,\n                \"stream_ctx::logical_data_t<slice<double>> should be logical_data<slice<double>>\");\n\n  // Test that logical_data_t is defined for graph_ctx\n  using graph_logical_data_int    = graph_ctx::logical_data_t<slice<int>>;\n  using graph_logical_data_double = graph_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<graph_logical_data_int, logical_data<slice<int>>>,\n                \"graph_ctx::logical_data_t<slice<int>> should be logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<graph_logical_data_double, logical_data<slice<double>>>,\n                \"graph_ctx::logical_data_t<slice<double>> should be logical_data<slice<double>>\");\n\n  // Test that logical_data_t is defined for stackable_ctx\n  using stackable_logical_data_int    = stackable_ctx::logical_data_t<slice<int>>;\n  using stackable_logical_data_double = stackable_ctx::logical_data_t<slice<double>>;\n  static_assert(::std::is_same_v<stackable_logical_data_int, stackable_logical_data<slice<int>>>,\n                \"stackable_ctx::logical_data_t<slice<int>> should be stackable_logical_data<slice<int>>\");\n  static_assert(::std::is_same_v<stackable_logical_data_double, stackable_logical_data<slice<double>>>,\n                \"stackable_ctx::logical_data_t<slice<double>> should be stackable_logical_data<slice<double>>\");\n}\n\n// Test 2: Generic function that uses ctx_t::logical_data_t as a template parameter\n// This demonstrates the main use case: writing context-agnostic code\ntemplate <typename Ctx>\ntypename Ctx::template logical_data_t<slice<int>> create_and_initialize_data(Ctx& ctx, size_t n, int init_value)\n{\n  // Create logical data using the context-specific logical_data_t type\n  auto data = ctx.logical_data(shape_of<slice<int>>(n));\n  data.set_symbol(\"initialized_data\");\n\n  // Initialize the data\n  ctx.parallel_for(data.shape(), data.write())->*[init_value] __device__(size_t i, auto d) {\n    d(i) = init_value + static_cast<int>(i);\n  };\n\n  return data;\n}\n\n// Test 3: Generic function that takes and returns ctx_t::logical_data_t\n// Note: Using auto&& for the data parameter to avoid template deduction issues with dependent types\ntemplate <typename Ctx, typename LogicalData, typename Factor>\nvoid scale_data(Ctx& ctx, LogicalData&& data, Factor factor)\n{\n  ctx.task(data.rw())->*[factor](hipStream_t s, auto d) {\n    scale_kernel<<<16, 128, 0, s>>>(d.size(), factor, d.data_handle());\n  };\n}\n\n// Test 4: Generic class that stores ctx_t::logical_data_t\ntemplate <typename Ctx>\nclass GenericDataHolder\n{\npublic:\n  using int_data_t    = typename Ctx::template logical_data_t<slice<int>>;\n  using double_data_t = typename Ctx::template logical_data_t<slice<double>>;\n\n  GenericDataHolder(Ctx& ctx, size_t n)\n      : int_data_(ctx.logical_data(shape_of<slice<int>>(n)))\n      , double_data_(ctx.logical_data(shape_of<slice<double>>(n)))\n  {\n    int_data_.set_symbol(\"holder_int_data\");\n    double_data_.set_symbol(\"holder_double_data\");\n  }\n\n  int_data_t& get_int_data()\n  {\n    return int_data_;\n  }\n\n  double_data_t& get_double_data()\n  {\n    return double_data_;\n  }\n\nprivate:\n  int_data_t int_data_;\n  double_data_t double_data_;\n};\n\n// Test 5: Generic function using auto with ctx_t::logical_data_t\ntemplate <typename Ctx>\nvoid test_auto_deduction(Ctx& ctx)\n{\n  // Create data using the generic function\n  auto data = create_and_initialize_data(ctx, 100, 42);\n\n  // Verify the type is correct\n  using expected_type = typename Ctx::template logical_data_t<slice<int>>;\n  static_assert(::std::is_same_v<decltype(data), expected_type>,\n                \"Auto-deduced type should match ctx_t::logical_data_t<slice<int>>\");\n\n  // Use the data\n  ctx.host_launch(data.read())->*[](auto d) {\n    for (size_t i = 0; i < d.size(); i++)\n    {\n      EXPECT(d(i) == 42 + static_cast<int>(i), \"Expected \", 42 + i, \" but got \", d(i), \" at index \", i);\n    }\n  };\n}\n\n// Main test function template\ntemplate <typename Ctx>\nvoid run_tests()\n{\n  Ctx ctx;\n\n  // Test 1: Basic creation and usage with template logical_data_t\n  {\n    auto data = create_and_initialize_data(ctx, 256, 10);\n\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == 10 + static_cast<int>(i));\n      }\n    };\n  }\n\n  // Test 2: Using scale_data generic function\n  {\n    auto data = create_and_initialize_data(ctx, 128, 5);\n    scale_data(ctx, data, 3);\n\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == (5 + static_cast<int>(i)) * 3);\n      }\n    };\n  }\n\n  // Test 3: Using GenericDataHolder class\n  {\n    GenericDataHolder<Ctx> holder(ctx, 64);\n\n    // Initialize int data\n    ctx.parallel_for(holder.get_int_data().shape(), holder.get_int_data().write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i * 2);\n    };\n\n    // Initialize double data\n    ctx.parallel_for(holder.get_double_data().shape(), holder.get_double_data().write())\n        ->*[] __device__(size_t i, auto d) {\n              d(i) = static_cast<double>(i) * 1.5;\n            };\n\n    // Verify int data\n    ctx.host_launch(holder.get_int_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i * 2));\n      }\n    };\n\n    // Verify double data\n    ctx.host_launch(holder.get_double_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(fabs(d(i) - static_cast<double>(i) * 1.5) < 1e-9);\n      }\n    };\n  }\n\n  // Test 4: Auto deduction\n  test_auto_deduction(ctx);\n\n  // Test 5: Multiple operations with logical_data_t\n  {\n    typename Ctx::template logical_data_t<slice<int>> data1 = ctx.logical_data(shape_of<slice<int>>(32));\n    typename Ctx::template logical_data_t<slice<int>> data2 = ctx.logical_data(shape_of<slice<int>>(32));\n\n    data1.set_symbol(\"data1\");\n    data2.set_symbol(\"data2\");\n\n    // Initialize both\n    ctx.parallel_for(data1.shape(), data1.write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i);\n    };\n\n    ctx.parallel_for(data2.shape(), data2.write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i * 10);\n    };\n\n    // Combine them\n    ctx.parallel_for(data1.shape(), data1.rw(), data2.read())->*[] __device__(size_t i, auto d1, auto d2) {\n      d1(i) += d2(i);\n    };\n\n    // Verify\n    ctx.host_launch(data1.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i + i * 10));\n      }\n    };\n  }\n\n  ctx.finalize();\n}\n\n// Additional test for stackable_ctx specific features\nvoid run_stackable_tests()\n{\n  stackable_ctx ctx;\n\n  // Test nested contexts with logical_data_t\n  {\n    auto data = create_and_initialize_data(ctx, 128, 20);\n\n    // Enter nested context\n    {\n      stackable_ctx::graph_scope_guard scope{ctx};\n\n      // Scale in nested context\n      scale_data(ctx, data, 2);\n\n      // Create local data in nested context\n      typename stackable_ctx::logical_data_t<slice<int>> local_data = ctx.logical_data(shape_of<slice<int>>(64));\n      local_data.set_symbol(\"nested_local\");\n\n      ctx.parallel_for(local_data.shape(), local_data.write())->*[] __device__(size_t i, auto d) {\n        d(i) = static_cast<int>(i * 5);\n      };\n\n      ctx.host_launch(local_data.read())->*[](auto d) {\n        for (size_t i = 0; i < d.size(); i++)\n        {\n          EXPECT(d(i) == static_cast<int>(i * 5));\n        }\n      };\n    }\n\n    // Verify data after nested context\n    ctx.host_launch(data.read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == (20 + static_cast<int>(i)) * 2);\n      }\n    };\n  }\n\n  // Test with GenericDataHolder in stackable context\n  {\n    GenericDataHolder<stackable_ctx> holder(ctx, 48);\n\n    ctx.parallel_for(holder.get_int_data().shape(), holder.get_int_data().write())->*[] __device__(size_t i, auto d) {\n      d(i) = static_cast<int>(i + 100);\n    };\n\n    ctx.host_launch(holder.get_int_data().read())->*[](auto d) {\n      for (size_t i = 0; i < d.size(); i++)\n      {\n        EXPECT(d(i) == static_cast<int>(i + 100));\n      }\n    };\n  }\n\n  ctx.finalize();\n}\n\nint main()\n{\n  // Run compile-time tests\n  test_compile_time_logical_data_t_exists();\n\n  // Run runtime tests with different context types\n  run_tests<stream_ctx>();\n  run_tests<graph_ctx>();\n  run_tests<stackable_ctx>();\n\n  // Run stackable-specific tests\n  run_stackable_tests();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/logical_data_t_template.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Experiment to see if we can read data generated in a nested context\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel(slice<int> b, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n\n  size_t n   = b.size();\n  int i      = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  while (i < n)\n  {\n    b(i) = 17 - 3 * i;\n    i += stride;\n  }\n}\n\nint main()\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  double ms               = 500;\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n\n  stackable_ctx sctx;\n\n  auto lB = sctx.logical_data(shape_of<slice<int>>(1024));\n  sctx.push();\n  lB.push(access_mode::write);\n\n  lB.set_symbol(\"B\");\n\n  sctx.task(lB.write())->*[clock_cnt](cudaStream_t stream, auto b) {\n    kernel<<<32, 4, 0, stream>>>(b, clock_cnt);\n  };\n\n  sctx.pop();\n\n  /* Access B in a context below the context where it was created */\n  sctx.host_launch(lB.read())->*[](auto b) {\n    for (size_t i = 0; i < b.size(); i++)\n    {\n      EXPECT(b(i) == (17 - 3 * i));\n    }\n  };\n\n  sctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Experiment to see if we can read data generated in a nested context\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void kernel(slice<int> b, long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n\n  size_t n   = b.size();\n  int i      = blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = blockDim.x * gridDim.x;\n\n  while (i < n)\n  {\n    b(i) = 17 - 3 * i;\n    i += stride;\n  }\n}\n\nint main()\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  double ms               = 500;\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n\n  stackable_ctx sctx;\n\n  auto lB = sctx.logical_data(shape_of<slice<int>>(1024));\n  sctx.push();\n  lB.push(access_mode::write);\n\n  lB.set_symbol(\"B\");\n\n  sctx.task(lB.write())->*[clock_cnt](hipStream_t stream, auto b) {\n    kernel<<<32, 4, 0, stream>>>(b, clock_cnt);\n  };\n\n  sctx.pop();\n\n  /* Access B in a context below the context where it was created */\n  sctx.host_launch(lB.read())->*[](auto b) {\n    for (size_t i = 0; i < b.size(); i++)\n    {\n      EXPECT(b(i) == (17 - 3 * i));\n    }\n  };\n\n  sctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/stackable_export2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/utility/nvtx.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void initA(double* d_ptrA, size_t N)\n{\n  size_t tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  for (size_t i = tid; i < N; i += nthreads)\n  {\n    d_ptrA[i] = sin((double) i);\n  }\n}\n\n__global__ void initB(double* d_ptrB, size_t N)\n{\n  size_t tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  for (size_t i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] = cos((double) i);\n  }\n}\n\n// B += alpha*A;\n__global__ void axpy(double alpha, const double* d_ptrA, double* d_ptrB, size_t N)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] += alpha * d_ptrA[i];\n  }\n}\n\n__global__ void empty_kernel()\n{\n  // no-op\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid ref_lib_call(cudaStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  initA<<<128, 32, 0, stream>>>(d_ptrA, N);\n  initB<<<128, 32, 0, stream>>>(d_ptrB, N);\n  axpy<<<128, 32, 0, stream>>>(3.0, d_ptrA, d_ptrB, N);\n  empty_kernel<<<16, 8, 0, stream>>>();\n}\n\nvoid lib_call(cudaStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  stream_ctx ctx(stream);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](cudaStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](cudaStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](cudaStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](cudaStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  // Note that this is non-blocking because we have creating the stream_ctx\n  // relative to a user-provided CUDA stream\n  ctx.finalize();\n}\n\nvoid lib_call_with_handle(async_resources_handle& handle, cudaStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  stream_ctx ctx(stream, handle);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](cudaStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](cudaStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](cudaStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](cudaStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  // Note that this is non-blocking because we have creating the stream_ctx\n  // relative to a user-provided CUDA stream\n  ctx.finalize();\n}\n\ntemplate <typename Ctx_t>\nvoid lib_call_generic(async_resources_handle& handle, cudaStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  Ctx_t ctx(stream, handle);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](cudaStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](cudaStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](cudaStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](cudaStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  ctx.submit();\n}\n\ntemplate <typename Ctx_t>\nvoid lib_call_token(async_resources_handle& handle, cudaStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  Ctx_t ctx(stream, handle);\n  auto lA = ctx.token();\n  auto lB = ctx.token();\n  ctx.task(lA.write())->*[=](cudaStream_t s) {\n    initA<<<128, 32, 0, s>>>(d_ptrA, N);\n  };\n\n  ctx.task(lB.write())->*[=](cudaStream_t s) {\n    initB<<<128, 32, 0, s>>>(d_ptrB, N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](cudaStream_t s) {\n    axpy<<<128, 32, 0, s>>>(3.0, d_ptrA, d_ptrB, N);\n  };\n\n  ctx.task()->*[](cudaStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  ctx.submit();\n}\n\nint main()\n{\n  double *d_ptrA, *d_ptrB;\n  const size_t N     = 128 * 1024;\n  const size_t NITER = 128;\n\n  // User allocated memory\n  cuda_safe_call(cudaMalloc(&d_ptrA, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc(&d_ptrB, N * sizeof(double)));\n\n  cudaStream_t stream;\n  cuda_safe_call(cudaStreamCreate(&stream));\n\n  nvtx_range r_warmup(\"warmup\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    ref_lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_warmup.end();\n\n  nvtx_range r_ref(\"ref\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    ref_lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_ref.end();\n\n  nvtx_range r_local(\"local stf\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_local.end();\n\n  nvtx_range r_local_handle(\"local stf handle\");\n  async_resources_handle handle;\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_with_handle(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_local_handle.end();\n\n  nvtx_range r_generic_graph_handle(\"generic graph handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<graph_ctx>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_generic_graph_handle.end();\n\n  nvtx_range r_generic_stream_handle(\"generic stream handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<stream_ctx>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_generic_stream_handle.end();\n\n  nvtx_range r_generic_context_handle(\"generic context handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<context>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_generic_context_handle.end();\n\n  nvtx_range r_token(\"logical token\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_token<context>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(cudaStreamSynchronize(stream));\n  r_token.end();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/utility/nvtx.cuh>\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void initA(double* d_ptrA, size_t N)\n{\n  size_t tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  for (size_t i = tid; i < N; i += nthreads)\n  {\n    d_ptrA[i] = sin((double) i);\n  }\n}\n\n__global__ void initB(double* d_ptrB, size_t N)\n{\n  size_t tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  for (size_t i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] = cos((double) i);\n  }\n}\n\n// B += alpha*A;\n__global__ void axpy(double alpha, const double* d_ptrA, double* d_ptrB, size_t N)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < N; i += nthreads)\n  {\n    d_ptrB[i] += alpha * d_ptrA[i];\n  }\n}\n\n__global__ void empty_kernel()\n{\n  // no-op\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid ref_lib_call(hipStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  initA<<<128, 32, 0, stream>>>(d_ptrA, N);\n  initB<<<128, 32, 0, stream>>>(d_ptrB, N);\n  axpy<<<128, 32, 0, stream>>>(3.0, d_ptrA, d_ptrB, N);\n  empty_kernel<<<16, 8, 0, stream>>>();\n}\n\nvoid lib_call(hipStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  stream_ctx ctx(stream);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](hipStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](hipStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](hipStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](hipStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  // Note that this is non-blocking because we have creating the stream_ctx\n  // relative to a user-provided CUDA stream\n  ctx.finalize();\n}\n\nvoid lib_call_with_handle(async_resources_handle& handle, hipStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  stream_ctx ctx(stream, handle);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](hipStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](hipStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](hipStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](hipStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  // Note that this is non-blocking because we have creating the stream_ctx\n  // relative to a user-provided CUDA stream\n  ctx.finalize();\n}\n\ntemplate <typename Ctx_t>\nvoid lib_call_generic(async_resources_handle& handle, hipStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  Ctx_t ctx(stream, handle);\n  auto lA = ctx.logical_data(make_slice(d_ptrA, N), data_place::current_device());\n  auto lB = ctx.logical_data(make_slice(d_ptrB, N), data_place::current_device());\n  ctx.task(lA.write())->*[=](hipStream_t s, auto a) {\n    initA<<<128, 32, 0, s>>>(a.data_handle(), N);\n  };\n\n  ctx.task(lB.write())->*[=](hipStream_t s, auto b) {\n    initB<<<128, 32, 0, s>>>(b.data_handle(), N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](hipStream_t s, auto a, auto b) {\n    axpy<<<128, 32, 0, s>>>(3.0, a.data_handle(), b.data_handle(), N);\n  };\n\n  ctx.task()->*[](hipStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  ctx.submit();\n}\n\ntemplate <typename Ctx_t>\nvoid lib_call_token(async_resources_handle& handle, hipStream_t stream, double* d_ptrA, double* d_ptrB, size_t N)\n{\n  Ctx_t ctx(stream, handle);\n  auto lA = ctx.token();\n  auto lB = ctx.token();\n  ctx.task(lA.write())->*[=](hipStream_t s) {\n    initA<<<128, 32, 0, s>>>(d_ptrA, N);\n  };\n\n  ctx.task(lB.write())->*[=](hipStream_t s) {\n    initB<<<128, 32, 0, s>>>(d_ptrB, N);\n  };\n\n  ctx.task(lA.read(), lB.rw())->*[=](hipStream_t s) {\n    axpy<<<128, 32, 0, s>>>(3.0, d_ptrA, d_ptrB, N);\n  };\n\n  ctx.task()->*[](hipStream_t s) {\n    empty_kernel<<<16, 8, 0, s>>>();\n  };\n\n  ctx.submit();\n}\n\nint main()\n{\n  double *d_ptrA, *d_ptrB;\n  const size_t N     = 128 * 1024;\n  const size_t NITER = 128;\n\n  // User allocated memory\n  cuda_safe_call(hipMalloc(&d_ptrA, N * sizeof(double)));\n  cuda_safe_call(hipMalloc(&d_ptrB, N * sizeof(double)));\n\n  hipStream_t stream;\n  cuda_safe_call(hipStreamCreate(&stream));\n\n  nvtx_range r_warmup(\"warmup\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    ref_lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_warmup.end();\n\n  nvtx_range r_ref(\"ref\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    ref_lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_ref.end();\n\n  nvtx_range r_local(\"local stf\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call(stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_local.end();\n\n  nvtx_range r_local_handle(\"local stf handle\");\n  async_resources_handle handle;\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_with_handle(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_local_handle.end();\n\n  nvtx_range r_generic_graph_handle(\"generic graph handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<graph_ctx>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_generic_graph_handle.end();\n\n  nvtx_range r_generic_stream_handle(\"generic stream handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<stream_ctx>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_generic_stream_handle.end();\n\n  nvtx_range r_generic_context_handle(\"generic context handle\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_generic<context>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_generic_context_handle.end();\n\n  nvtx_range r_token(\"logical token\");\n  for (size_t i = 0; i < NITER; i++)\n  {\n    lib_call_token<context>(handle, stream, d_ptrA, d_ptrB, N);\n  }\n  cuda_safe_call(hipStreamSynchronize(stream));\n  r_token.end();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/legacy_to_stf.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test copy and move semantics for task dependency types\n *\n * This test verifies that task_dep_untyped, task_dep<T>, and stackable_task_dep<T>\n * are properly copyable and movable, which is essential for the STF framework to work\n * correctly with deferred operations and stackable contexts.\n */\n\n#include <cuda/std/type_traits>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Simple kernel for testing - defined outside to avoid device lambda nesting issues\n__global__ void double_values_kernel(int* out, const int* in, int n)\n{\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n)\n  {\n    out[idx] = in[idx] * 2;\n  }\n}\n\n// Helper template to test copy/move operations on any type\ntemplate <typename T>\nvoid test_copy_move_semantics(const T& original)\n{\n  // Test copy constructor\n  {\n    T copy_constructed(original);\n    (void) copy_constructed; // Suppress unused variable warning\n  }\n\n  // Test move constructor\n  {\n    T temp(original);\n    T move_constructed(::std::move(temp));\n    (void) move_constructed; // Suppress unused variable warning\n  }\n\n  // Test copy assignment\n  {\n    T temp(original); // Create a temporary to assign to\n    T copy_assigned(original); // Initialize with copy constructor first\n    copy_assigned = temp; // Then test copy assignment\n    (void) copy_assigned; // Suppress unused variable warning\n  }\n\n  // Test move assignment\n  {\n    T temp(original);\n    T move_assigned(original); // Initialize with copy constructor first\n    move_assigned = ::std::move(temp); // Then test move assignment\n    (void) move_assigned; // Suppress unused variable warning\n  }\n}\n\n// Compile-time type trait tests\nvoid test_compile_time_type_traits()\n{\n  // Test task_dep_untyped\n  static_assert(::std::is_copy_constructible_v<task_dep_untyped>, \"task_dep_untyped must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<task_dep_untyped>, \"task_dep_untyped must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<task_dep_untyped>, \"task_dep_untyped must be move constructible\");\n  static_assert(::std::is_move_assignable_v<task_dep_untyped>, \"task_dep_untyped must be move assignable\");\n\n  // Test task_dep<T> for common types\n  using int_task_dep = task_dep<slice<int>>;\n  static_assert(::std::is_copy_constructible_v<int_task_dep>, \"task_dep<slice<int>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<int_task_dep>, \"task_dep<slice<int>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<int_task_dep>, \"task_dep<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_task_dep>, \"task_dep<slice<int>> must be move assignable\");\n\n  using double_task_dep = task_dep<slice<double>>;\n  static_assert(::std::is_copy_constructible_v<double_task_dep>, \"task_dep<slice<double>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<double_task_dep>, \"task_dep<slice<double>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<double_task_dep>, \"task_dep<slice<double>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<double_task_dep>, \"task_dep<slice<double>> must be move assignable\");\n\n  // Test stackable_task_dep<T> with proper template parameters\n  using int_stackable_task_dep = stackable_task_dep<slice<int>, ::std::monostate, false>;\n  static_assert(::std::is_copy_constructible_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be move assignable\");\n\n  using double_stackable_task_dep = stackable_task_dep<slice<double>, ::std::monostate, false>;\n  static_assert(::std::is_copy_constructible_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be move assignable\");\n\n  // Test stackable_logical_data<T>\n  using int_stackable_logical_data = stackable_logical_data<slice<int>>;\n  static_assert(::std::is_move_constructible_v<int_stackable_logical_data>,\n                \"stackable_logical_data<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_stackable_logical_data>,\n                \"stackable_logical_data<slice<int>> must be move assignable\");\n}\n\nint main()\n{\n  // Run compile-time tests\n  test_compile_time_type_traits();\n\n  // Create stackable context for runtime tests\n  stackable_ctx ctx;\n\n  const size_t N = 64;\n\n  // Create test data\n  auto ldata_int    = ctx.logical_data(shape_of<slice<int>>(N));\n  auto ldata_double = ctx.logical_data(shape_of<slice<double>>(N));\n\n  // Test runtime copy/move semantics for stackable_task_dep\n  {\n    auto stackable_read_dep  = ldata_int.read();\n    auto stackable_write_dep = ldata_int.write();\n    auto stackable_rw_dep    = ldata_int.rw();\n\n    // Test copy/move operations on stackable task dependencies\n    test_copy_move_semantics(stackable_read_dep);\n    test_copy_move_semantics(stackable_write_dep);\n    test_copy_move_semantics(stackable_rw_dep);\n\n    // Test with different data types\n    auto stackable_double_dep = ldata_double.read();\n    test_copy_move_semantics(stackable_double_dep);\n  }\n\n  // Test that stackable logical data itself is movable\n  {\n    auto ldata_copy  = ldata_int; // Copy\n    auto ldata_moved = mv(ldata_int); // Move using STF's mv() utility\n\n    // Verify we can still use the moved-to object\n    auto dep_from_moved = ldata_moved.read();\n    test_copy_move_semantics(dep_from_moved);\n  }\n\n  // Test that we can store task dependencies in containers (requires copy/move)\n  {\n    auto ldata_container_test = ctx.logical_data(shape_of<slice<float>>(32));\n\n    ::std::vector<decltype(ldata_container_test.read())> read_deps;\n    read_deps.push_back(ldata_container_test.read());\n    read_deps.push_back(ldata_container_test.read());\n\n    ::std::vector<decltype(ldata_container_test.write())> write_deps;\n    write_deps.emplace_back(ldata_container_test.write());\n\n    // Verify container operations work (copy, move, etc.)\n    auto read_deps_copy   = read_deps; // Copy container\n    auto write_deps_moved = ::std::move(write_deps); // Move container\n\n    (void) read_deps_copy; // Suppress unused variable warnings\n    (void) write_deps_moved;\n  }\n\n  // Test functional usage with actual tasks to ensure copy/move semantics work in practice\n  {\n    auto ltest = ctx.logical_data(shape_of<slice<int>>(16));\n\n    // Initialize data\n    ctx.parallel_for(ltest.shape(), ltest.write())->*[] __device__(size_t i, auto data) {\n      data(i) = static_cast<int>(i);\n    };\n\n    ctx.push(); // Enter nested context\n\n    // Create dependencies that will be copied/moved through deferred operations\n    auto read_dep  = ltest.read();\n    auto write_dep = ltest.write();\n\n    // Use add_deps which relies on copy/move semantics\n    auto task = ctx.task();\n    task.add_deps(read_dep); // This should copy/move the dependency\n    task.add_deps(write_dep); // This should copy/move the dependency\n\n    task->*[&task](cudaStream_t stream) {\n      // Get dependencies by index - this is how add_deps works\n      auto input  = task.template get<slice<int>>(0); // First dependency (read)\n      auto output = task.template get<slice<int>>(1); // Second dependency (write)\n\n      // Simple kernel that doubles the values\n      int N = input.size();\n\n      int block_size = 256;\n      int grid_size  = (N + block_size - 1) / block_size;\n\n      // Use the global kernel function to avoid device lambda nesting\n      double_values_kernel<<<grid_size, block_size, 0, stream>>>(output.data_handle(), input.data_handle(), N);\n    };\n\n    ctx.pop(); // Exit nested context\n\n    // Verify result\n    ctx.host_launch(ltest.read())->*[](auto data) {\n      for (size_t i = 0; i < data.size(); i++)\n      {\n        EXPECT(data(i) == static_cast<int>(i * 2), \"Expected \", i * 2, \" but got \", data(i), \" at index \", i);\n      }\n    };\n  }\n\n  ctx.finalize();\n\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test copy and move semantics for task dependency types\n *\n * This test verifies that task_dep_untyped, task_dep<T>, and stackable_task_dep<T>\n * are properly copyable and movable, which is essential for the STF framework to work\n * correctly with deferred operations and stackable contexts.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/std/type_traits>\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// Simple kernel for testing - defined outside to avoid device lambda nesting issues\n__global__ void double_values_kernel(int* out, const int* in, int n)\n{\n  int idx = blockIdx.x * blockDim.x + threadIdx.x;\n  if (idx < n)\n  {\n    out[idx] = in[idx] * 2;\n  }\n}\n\n// Helper template to test copy/move operations on any type\ntemplate <typename T>\nvoid test_copy_move_semantics(const T& original)\n{\n  // Test copy constructor\n  {\n    T copy_constructed(original);\n    (void) copy_constructed; // Suppress unused variable warning\n  }\n\n  // Test move constructor\n  {\n    T temp(original);\n    T move_constructed(::std::move(temp));\n    (void) move_constructed; // Suppress unused variable warning\n  }\n\n  // Test copy assignment\n  {\n    T temp(original); // Create a temporary to assign to\n    T copy_assigned(original); // Initialize with copy constructor first\n    copy_assigned = temp; // Then test copy assignment\n    (void) copy_assigned; // Suppress unused variable warning\n  }\n\n  // Test move assignment\n  {\n    T temp(original);\n    T move_assigned(original); // Initialize with copy constructor first\n    move_assigned = ::std::move(temp); // Then test move assignment\n    (void) move_assigned; // Suppress unused variable warning\n  }\n}\n\n// Compile-time type trait tests\nvoid test_compile_time_type_traits()\n{\n  // Test task_dep_untyped\n  static_assert(::std::is_copy_constructible_v<task_dep_untyped>, \"task_dep_untyped must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<task_dep_untyped>, \"task_dep_untyped must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<task_dep_untyped>, \"task_dep_untyped must be move constructible\");\n  static_assert(::std::is_move_assignable_v<task_dep_untyped>, \"task_dep_untyped must be move assignable\");\n\n  // Test task_dep<T> for common types\n  using int_task_dep = task_dep<slice<int>>;\n  static_assert(::std::is_copy_constructible_v<int_task_dep>, \"task_dep<slice<int>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<int_task_dep>, \"task_dep<slice<int>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<int_task_dep>, \"task_dep<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_task_dep>, \"task_dep<slice<int>> must be move assignable\");\n\n  using double_task_dep = task_dep<slice<double>>;\n  static_assert(::std::is_copy_constructible_v<double_task_dep>, \"task_dep<slice<double>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<double_task_dep>, \"task_dep<slice<double>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<double_task_dep>, \"task_dep<slice<double>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<double_task_dep>, \"task_dep<slice<double>> must be move assignable\");\n\n  // Test stackable_task_dep<T> with proper template parameters\n  using int_stackable_task_dep = stackable_task_dep<slice<int>, ::std::monostate, false>;\n  static_assert(::std::is_copy_constructible_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_stackable_task_dep>,\n                \"stackable_task_dep<slice<int>> must be move assignable\");\n\n  using double_stackable_task_dep = stackable_task_dep<slice<double>, ::std::monostate, false>;\n  static_assert(::std::is_copy_constructible_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be copy constructible\");\n  static_assert(::std::is_copy_assignable_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be copy assignable\");\n  static_assert(::std::is_move_constructible_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<double_stackable_task_dep>,\n                \"stackable_task_dep<slice<double>> must be move assignable\");\n\n  // Test stackable_logical_data<T>\n  using int_stackable_logical_data = stackable_logical_data<slice<int>>;\n  static_assert(::std::is_move_constructible_v<int_stackable_logical_data>,\n                \"stackable_logical_data<slice<int>> must be move constructible\");\n  static_assert(::std::is_move_assignable_v<int_stackable_logical_data>,\n                \"stackable_logical_data<slice<int>> must be move assignable\");\n}\n\nint main()\n{\n  // Run compile-time tests\n  test_compile_time_type_traits();\n\n  // Create stackable context for runtime tests\n  stackable_ctx ctx;\n\n  const size_t N = 64;\n\n  // Create test data\n  auto ldata_int    = ctx.logical_data(shape_of<slice<int>>(N));\n  auto ldata_double = ctx.logical_data(shape_of<slice<double>>(N));\n\n  // Test runtime copy/move semantics for stackable_task_dep\n  {\n    auto stackable_read_dep  = ldata_int.read();\n    auto stackable_write_dep = ldata_int.write();\n    auto stackable_rw_dep    = ldata_int.rw();\n\n    // Test copy/move operations on stackable task dependencies\n    test_copy_move_semantics(stackable_read_dep);\n    test_copy_move_semantics(stackable_write_dep);\n    test_copy_move_semantics(stackable_rw_dep);\n\n    // Test with different data types\n    auto stackable_double_dep = ldata_double.read();\n    test_copy_move_semantics(stackable_double_dep);\n  }\n\n  // Test that stackable logical data itself is movable\n  {\n    auto ldata_copy  = ldata_int; // Copy\n    auto ldata_moved = mv(ldata_int); // Move using STF's mv() utility\n\n    // Verify we can still use the moved-to object\n    auto dep_from_moved = ldata_moved.read();\n    test_copy_move_semantics(dep_from_moved);\n  }\n\n  // Test that we can store task dependencies in containers (requires copy/move)\n  {\n    auto ldata_container_test = ctx.logical_data(shape_of<slice<float>>(32));\n\n    ::std::vector<decltype(ldata_container_test.read())> read_deps;\n    read_deps.push_back(ldata_container_test.read());\n    read_deps.push_back(ldata_container_test.read());\n\n    ::std::vector<decltype(ldata_container_test.write())> write_deps;\n    write_deps.emplace_back(ldata_container_test.write());\n\n    // Verify container operations work (copy, move, etc.)\n    auto read_deps_copy   = read_deps; // Copy container\n    auto write_deps_moved = ::std::move(write_deps); // Move container\n\n    (void) read_deps_copy; // Suppress unused variable warnings\n    (void) write_deps_moved;\n  }\n\n  // Test functional usage with actual tasks to ensure copy/move semantics work in practice\n  {\n    auto ltest = ctx.logical_data(shape_of<slice<int>>(16));\n\n    // Initialize data\n    ctx.parallel_for(ltest.shape(), ltest.write())->*[] __device__(size_t i, auto data) {\n      data(i) = static_cast<int>(i);\n    };\n\n    ctx.push(); // Enter nested context\n\n    // Create dependencies that will be copied/moved through deferred operations\n    auto read_dep  = ltest.read();\n    auto write_dep = ltest.write();\n\n    // Use add_deps which relies on copy/move semantics\n    auto task = ctx.task();\n    task.add_deps(read_dep); // This should copy/move the dependency\n    task.add_deps(write_dep); // This should copy/move the dependency\n\n    task->*[&task](hipStream_t stream) {\n      // Get dependencies by index - this is how add_deps works\n      auto input  = task.template get<slice<int>>(0); // First dependency (read)\n      auto output = task.template get<slice<int>>(1); // Second dependency (write)\n\n      // Simple kernel that doubles the values\n      int N = input.size();\n\n      int block_size = 256;\n      int grid_size  = (N + block_size - 1) / block_size;\n\n      // Use the global kernel function to avoid device lambda nesting\n      double_values_kernel<<<grid_size, block_size, 0, stream>>>(output.data_handle(), input.data_handle(), N);\n    };\n\n    ctx.pop(); // Exit nested context\n\n    // Verify result\n    ctx.host_launch(ltest.read())->*[](auto data) {\n      for (size_t i = 0; i < data.size(); i++)\n      {\n        EXPECT(data(i) == static_cast<int>(i * 2), \"Expected \", i * 2, \" but got \", data(i), \" at index \", i);\n      }\n    };\n  }\n\n  ctx.finalize();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/task_dep_copy_move_semantics.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the add_deps mechanism on tasks in stackable contexts.\n *        This test verifies that dependencies can be added one by one to tasks\n *        using add_deps() in stackable contexts, with proper automatic data\n *        pushing and validation.\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(slice<T> a, slice<T> b)\n{\n  a(0) += b(0);\n}\n\nint main()\n{\n  stackable_ctx ctx;\n\n  int var1 = 42;\n  int var2 = 64;\n\n  auto lvar1 = ctx.logical_data(make_slice(&var1, 1));\n  auto lvar2 = ctx.logical_data(make_slice(&var2, 1));\n\n  ctx.push();\n\n  auto t = ctx.task();\n  t.add_deps(lvar1.rw());\n  t.add_deps(lvar2.read());\n  t->*[&t](cudaStream_t stream) {\n    auto d1 = t.template get<slice<int>>(0);\n    auto d2 = t.template get<slice<int>>(1);\n    add<<<1, 1, 0, stream>>>(d1, d2);\n  };\n\n  ctx.pop();\n\n  ctx.host_launch(lvar1.read())->*[]([[maybe_unused]] auto d1) {\n    _CCCL_ASSERT(d1(0) == (42 + 64), \"invalid result\");\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the add_deps mechanism on tasks in stackable contexts.\n *        This test verifies that dependencies can be added one by one to tasks\n *        using add_deps() in stackable contexts, with proper automatic data\n *        pushing and validation.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(slice<T> a, slice<T> b)\n{\n  a(0) += b(0);\n}\n\nint main()\n{\n  stackable_ctx ctx;\n\n  int var1 = 42;\n  int var2 = 64;\n\n  auto lvar1 = ctx.logical_data(make_slice(&var1, 1));\n  auto lvar2 = ctx.logical_data(make_slice(&var2, 1));\n\n  ctx.push();\n\n  auto t = ctx.task();\n  t.add_deps(lvar1.rw());\n  t.add_deps(lvar2.read());\n  t->*[&t](hipStream_t stream) {\n    auto d1 = t.template get<slice<int>>(0);\n    auto d2 = t.template get<slice<int>>(1);\n    add<<<1, 1, 0, stream>>>(d1, d2);\n  };\n\n  ctx.pop();\n\n  ctx.host_launch(lvar1.read())->*[]([[maybe_unused]] auto d1) {\n    _CCCL_ASSERT(d1(0) == (42 + 64), \"invalid result\");\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/stackable_add_deps.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple times in the same\n *        task even with different access modes (which should be combined)\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(T* a, const T* b)\n{\n  *a = *b + 1;\n}\n\nint main()\n{\n  stackable_ctx ctx;\n\n  int var         = 42;\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n\n  ctx.push();\n\n  // da and db are for the same variable : we expect it to be equivalent to a RW access\n  ctx.task(var_handle.write(), var_handle.read())->*[](cudaStream_t stream, auto da, auto db) {\n    add<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle());\n  };\n\n  ctx.pop();\n\n  // Read that value on the host\n  ctx.host_launch(var_handle.read())->*[](auto da) {\n    int result = *da.data_handle();\n    assert(result == 43);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Ensure we can use the same logical data multiple times in the same\n *        task even with different access modes (which should be combined)\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n// a = b + 1;\ntemplate <typename T>\n__global__ void add(T* a, const T* b)\n{\n  *a = *b + 1;\n}\n\nint main()\n{\n  stackable_ctx ctx;\n\n  int var         = 42;\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n\n  ctx.push();\n\n  // da and db are for the same variable : we expect it to be equivalent to a RW access\n  ctx.task(var_handle.write(), var_handle.read())->*[](hipStream_t stream, auto da, auto db) {\n    add<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle());\n  };\n\n  ctx.pop();\n\n  // Read that value on the host\n  ctx.host_launch(var_handle.read())->*[](auto da) {\n    int result = *da.data_handle();\n    assert(result == 43);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/local_stf/stackable_redundant_deps.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Make sure we can automatically allocate and use data in managed memory based on their shape\n *\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\n__host__ __device__ double X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(cudaGetDevice(&dev));\n  assert(dev >= 0);\n  cudaDeviceProp prop;\n  cuda_safe_call(cudaGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(shape_of<slice<double>>(N));\n  auto lY = ctx.logical_data(Y);\n\n  // Make sure X is created automatically in managed memory\n  ctx.parallel_for(lX.shape(), lX.write(data_place::managed()))->*[] _CCCL_DEVICE(size_t i, auto X) {\n    X(i) = X0(i);\n  };\n\n  /* Compute Y = Y + alpha X, but leave X in managed memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.host_launch(lX.read(data_place::managed()), lY.read())->*[=](auto X, auto Y) {\n    for (size_t i = 0; i < N; i++)\n    {\n      EXPECT(fabs(Y(i) - (Y0(i) + alpha * X0(i))) < 0.0001);\n      EXPECT(fabs(X(i) - X0(i)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Make sure we can automatically allocate and use data in managed memory based on their shape\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\n__host__ __device__ double X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(hipGetDevice(&dev));\n  assert(dev >= 0);\n  hipDeviceProp_t prop;\n  cuda_safe_call(hipGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(shape_of<slice<double>>(N));\n  auto lY = ctx.logical_data(Y);\n\n  // Make sure X is created automatically in managed memory\n  ctx.parallel_for(lX.shape(), lX.write(data_place::managed()))->*[] _CCCL_DEVICE(size_t i, auto X) {\n    X(i) = X0(i);\n  };\n\n  /* Compute Y = Y + alpha X, but leave X in managed memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.host_launch(lX.read(data_place::managed()), lY.read())->*[=](auto X, auto Y) {\n    for (size_t i = 0; i < N; i++)\n    {\n      EXPECT(fabs(Y(i) - (Y0(i) + alpha * X0(i))) < 0.0001);\n      EXPECT(fabs(X(i) - X0(i)) < 0.0001);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/places/managed_from_shape.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int n, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\nint main()\n{\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  cuda_safe_call(cudaSetDevice(0));\n\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"Skipping test that needs at last 2 devices.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n\n  const double alpha = 2.0;\n  const int n        = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(exec_place::device(1), handle_X.read(), handle_Y.rw())->*[&](cudaStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(n, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.task(exec_place::host(), handle_X.read(), handle_Y.read())->*[&](cudaStream_t stream, auto X, auto Y) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n\n    for (int ind = 0; ind < n; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int n, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\nint main()\n{\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  cuda_safe_call(hipSetDevice(0));\n\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"Skipping test that needs at last 2 devices.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n\n  const double alpha = 2.0;\n  const int n        = 12;\n\n  double X[n], Y[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = 1.0 * ind;\n    Y[ind] = 2.0 * ind - 3.0;\n  }\n\n  auto handle_X = ctx.logical_data(X);\n  auto handle_Y = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(exec_place::device(1), handle_X.read(), handle_Y.rw())->*[&](hipStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(n, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.task(exec_place::host(), handle_X.read(), handle_Y.read())->*[&](hipStream_t stream, auto X, auto Y) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n\n    for (int ind = 0; ind < n; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/places/non_current_device.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses managed memory from the device\n *\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(cudaGetDevice(&dev));\n  assert(dev >= 0);\n  cudaDeviceProp prop;\n  cuda_safe_call(cudaGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses managed memory from the device\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(hipGetDevice(&dev));\n  assert(dev >= 0);\n  hipDeviceProp_t prop;\n  cuda_safe_call(hipGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X on the host and access it with mapped memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/places/managed.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses managed memory from the device. This tests\n * explicitly created managed memory, and passes it to a logical data.\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(cudaGetDevice(&dev));\n  assert(dev >= 0);\n  cudaDeviceProp prop;\n  cuda_safe_call(cudaGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double Y[N];\n\n  double* X;\n  cuda_safe_call(cudaMallocManaged(&X, N * sizeof(double)));\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(make_slice(X, N), data_place::managed());\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X in managed memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n * where the task accesses managed memory from the device. This tests\n * explicitly created managed memory, and passes it to a logical data.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(size_t i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(size_t i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  // Verify whether this device can access memory concurrently from CPU and GPU.\n  int dev;\n  cuda_safe_call(hipGetDevice(&dev));\n  assert(dev >= 0);\n  hipDeviceProp_t prop;\n  cuda_safe_call(hipGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    fprintf(stderr, \"Concurrent CPU/GPU access not supported, skipping test.\\n\");\n    return 0;\n  }\n\n  stream_ctx ctx;\n  const size_t N = 16;\n  double Y[N];\n\n  double* X;\n  cuda_safe_call(hipMallocManaged(&X, N * sizeof(double)));\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data(make_slice(X, N), data_place::managed());\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, but leave X in managed memory */\n  ctx.task(lX.read(data_place::managed()), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n    axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + alpha * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/places/managed_from_user.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void print(slice<int> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    printf(\"%d %d\\n\", i, s(i));\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  cudaStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X=2X\")->*[] __device__(size_t i, auto x) {\n    x(i) *= 2;\n  };\n\n  auto fx = ctx.freeze(lX);\n\n  auto dX = fx.get(data_place::current_device(), stream);\n\n  print<<<8, 4, 0, stream>>>(dX);\n\n  ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n    y(i) = x(i);\n  };\n\n  fx.unfreeze(stream);\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X+=1\")->*[] __device__(size_t i, auto x) {\n    x(i) += 1;\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    EXPECT(X[i] == 2 * X0(i) + 1);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void print(slice<int> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    printf(\"%d %d\\n\", i, s(i));\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  hipStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X=2X\")->*[] __device__(size_t i, auto x) {\n    x(i) *= 2;\n  };\n\n  auto fx = ctx.freeze(lX);\n\n  auto dX = fx.get(data_place::current_device(), stream);\n\n  print<<<8, 4, 0, stream>>>(dX);\n\n  ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n    y(i) = x(i);\n  };\n\n  fx.unfreeze(stream);\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X+=1\")->*[] __device__(size_t i, auto x) {\n    x(i) += 1;\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    EXPECT(X[i] == 2 * X0(i) + 1);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/freeze/freeze.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void mult(slice<int> s, int val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    s(i) *= val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  cudaStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  for (int k = 0; k < 4; k++)\n  {\n    auto fx = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n    _CCCL_ASSERT(fx.get_access_mode() == access_mode::rw, \"invalid access mode\");\n\n    auto dX = fx.get(data_place::current_device(), stream);\n    mult<<<8, 4, 0, stream>>>(dX, 4);\n    fx.unfreeze(stream);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    ctx.parallel_for(lX.shape(), lY.rw()).set_symbol(\"Y+=1\")->*[] __device__(size_t i, auto y) {\n      y(i) += 1;\n    };\n\n    //        ctx.host_launch(lX.read(), lY.read())->*[](auto x, auto y) {\n    //            for (int i = 0; i < x.size(); i++) {\n    //                EXPECT(x(i) == 2*X0(i) + 4);\n    //            }\n    //\n    //            for (int i = 0; i < y.size(); i++) {\n    //                EXPECT(y(i) == 2*X0(i) + 4);\n    //            }\n    //        };\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void mult(slice<int> s, int val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    s(i) *= val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  hipStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  for (int k = 0; k < 4; k++)\n  {\n    auto fx = ctx.freeze(lX, access_mode::rw, data_place::current_device());\n\n    _CCCL_ASSERT(fx.get_access_mode() == access_mode::rw, \"invalid access mode\");\n\n    auto dX = fx.get(data_place::current_device(), stream);\n    mult<<<8, 4, 0, stream>>>(dX, 4);\n    fx.unfreeze(stream);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    ctx.parallel_for(lX.shape(), lY.rw()).set_symbol(\"Y+=1\")->*[] __device__(size_t i, auto y) {\n      y(i) += 1;\n    };\n\n    //        ctx.host_launch(lX.read(), lY.read())->*[](auto x, auto y) {\n    //            for (int i = 0; i < x.size(); i++) {\n    //                EXPECT(x(i) == 2*X0(i) + 4);\n    //            }\n    //\n    //            for (int i = 0; i < y.size(); i++) {\n    //                EXPECT(y(i) == 2*X0(i) + 4);\n    //            }\n    //        };\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/freeze/freeze_rw.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void print(slice<int> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    printf(\"%d %d\\n\", i, s(i));\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X=2X\")->*[] __device__(size_t i, auto x) {\n    x(i) *= 2;\n  };\n\n  // test 1 : implicit sync of gets\n  {\n    auto fx      = ctx.freeze(lX);\n    auto [dX, _] = fx.get(data_place::current_device());\n\n    // the stream returned by fence should depend on the get operation\n    auto stream2 = ctx.fence();\n    print<<<8, 4, 0, stream2>>>(dX);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    fx.unfreeze(stream2);\n  }\n\n  // test 2 : unfreeze with no events due to user sync\n  {\n    auto fx      = ctx.freeze(lX);\n    auto [dX, _] = fx.get(data_place::current_device());\n\n    // the stream returned by fence should depend on the get operation\n    auto stream2 = ctx.fence();\n    print<<<8, 4, 0, stream2>>>(dX);\n\n    // We synchronize so there is nothing to depend on anymore\n    cudaStreamSynchronize(stream2);\n    fx.unfreeze(event_list());\n  }\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X+=1\")->*[] __device__(size_t i, auto x) {\n    x(i) += 1;\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    EXPECT(X[i] == 2 * X0(i) + 1);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Freeze data in read-only fashion\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void print(slice<int> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    printf(\"%d %d\\n\", i, s(i));\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X=2X\")->*[] __device__(size_t i, auto x) {\n    x(i) *= 2;\n  };\n\n  // test 1 : implicit sync of gets\n  {\n    auto fx      = ctx.freeze(lX);\n    auto [dX, _] = fx.get(data_place::current_device());\n\n    // the stream returned by fence should depend on the get operation\n    auto stream2 = ctx.fence();\n    print<<<8, 4, 0, stream2>>>(dX);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    fx.unfreeze(stream2);\n  }\n\n  // test 2 : unfreeze with no events due to user sync\n  {\n    auto fx      = ctx.freeze(lX);\n    auto [dX, _] = fx.get(data_place::current_device());\n\n    // the stream returned by fence should depend on the get operation\n    auto stream2 = ctx.fence();\n    print<<<8, 4, 0, stream2>>>(dX);\n\n    // We synchronize so there is nothing to depend on anymore\n    hipStreamSynchronize(stream2);\n    fx.unfreeze(event_list());\n  }\n\n  ctx.parallel_for(lX.shape(), lX.rw()).set_symbol(\"X+=1\")->*[] __device__(size_t i, auto x) {\n    x(i) += 1;\n  };\n\n  ctx.finalize();\n\n  for (int i = 0; i < N; i++)\n  {\n    EXPECT(X[i] == 2 * X0(i) + 1);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/freeze/task_fence.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze data and store it as a frozen_logical_data_untyped object\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void mult(slice<int> s, int val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    s(i) *= val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  cudaStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  for (int k = 0; k < 4; k++)\n  {\n    logical_data_untyped lX_untyped = lX;\n    auto fx                         = ctx.freeze(lX_untyped, access_mode::rw, data_place::current_device());\n\n    _CCCL_ASSERT(fx.get_access_mode() == access_mode::rw, \"invalid access mode\");\n\n    auto dX = fx.template get<slice<int>>(data_place::current_device(), stream);\n    mult<<<8, 4, 0, stream>>>(dX, 4);\n    fx.unfreeze(stream);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    ctx.parallel_for(lX.shape(), lY.rw()).set_symbol(\"Y+=1\")->*[] __device__(size_t i, auto y) {\n      y(i) += 1;\n    };\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n//! \\file\n//!\n//! \\brief Freeze data and store it as a frozen_logical_data_untyped object\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nint X0(int i)\n{\n  return 17 * i + 45;\n}\n\n__global__ void mult(slice<int> s, int val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < s.size(); i += nthreads)\n  {\n    s(i) *= val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  hipStream_t stream = ctx.pick_stream();\n\n  const int N = 16;\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n  }\n\n  auto lX = ctx.logical_data(X).set_symbol(\"X\");\n  auto lY = ctx.logical_data(lX.shape()).set_symbol(\"Y\");\n\n  for (int k = 0; k < 4; k++)\n  {\n    logical_data_untyped lX_untyped = lX;\n    auto fx                         = ctx.freeze(lX_untyped, access_mode::rw, data_place::current_device());\n\n    _CCCL_ASSERT(fx.get_access_mode() == access_mode::rw, \"invalid access mode\");\n\n    auto dX = fx.template get<slice<int>>(data_place::current_device(), stream);\n    mult<<<8, 4, 0, stream>>>(dX, 4);\n    fx.unfreeze(stream);\n\n    ctx.parallel_for(lX.shape(), lX.read(), lY.write()).set_symbol(\"Y=X\")->*[] __device__(size_t i, auto x, auto y) {\n      y(i) = x(i);\n    };\n\n    ctx.parallel_for(lX.shape(), lY.rw()).set_symbol(\"Y+=1\")->*[] __device__(size_t i, auto y) {\n      y(i) += 1;\n    };\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/freeze/freeze_untyped_rw.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename S>\n__global__ void inc_kernel(S sA)\n{\n  sA(threadIdx.x)++;\n}\n\ntemplate <typename S1, typename S2>\n__global__ void copy_kernel(S1 sA, S2 sB)\n{\n  sB(threadIdx.x) = sA(threadIdx.x);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  int A[10] = {0};\n  int B[10] = {0};\n  Ctx ctx;\n\n  auto lA = ctx.logical_data(A);\n  auto lB = ctx.logical_data(B);\n\n  for (size_t k = 0; k < 10; k++)\n  {\n    // fprintf(stderr, \"iter %zu - 0 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lA.rw())->*[](cudaStream_t stream, auto sA) {\n      inc_kernel<<<1, 10, 0, stream>>>(sA);\n    };\n\n    // fprintf(stderr, \"iter %zu - 1 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lA.read(), lB.rw())->*[](cudaStream_t stream, auto sA, auto sB) {\n      copy_kernel<<<1, 10, 0, stream>>>(sA, sB);\n    };\n\n    // fprintf(stderr, \"iter %zu - 2 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lB.rw())->*[](cudaStream_t stream, auto sB) {\n      inc_kernel<<<1, 10, 0, stream>>>(sB);\n    };\n\n    // fprintf(stderr, \"iter %zu - 3 : ctx.hash %zu\\n\", k, ctx.hash());\n  }\n\n  ctx.host_launch(lA.read(), lB.read())->*[&](auto /*unused*/, auto /*unused*/) {\n    // fprintf(stderr, \"HOST END : ctx.hash %zu\\n\", ctx.hash());\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename S>\n__global__ void inc_kernel(S sA)\n{\n  sA(threadIdx.x)++;\n}\n\ntemplate <typename S1, typename S2>\n__global__ void copy_kernel(S1 sA, S2 sB)\n{\n  sB(threadIdx.x) = sA(threadIdx.x);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  int A[10] = {0};\n  int B[10] = {0};\n  Ctx ctx;\n\n  auto lA = ctx.logical_data(A);\n  auto lB = ctx.logical_data(B);\n\n  for (size_t k = 0; k < 10; k++)\n  {\n    // fprintf(stderr, \"iter %zu - 0 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lA.rw())->*[](hipStream_t stream, auto sA) {\n      inc_kernel<<<1, 10, 0, stream>>>(sA);\n    };\n\n    // fprintf(stderr, \"iter %zu - 1 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lA.read(), lB.rw())->*[](hipStream_t stream, auto sA, auto sB) {\n      copy_kernel<<<1, 10, 0, stream>>>(sA, sB);\n    };\n\n    // fprintf(stderr, \"iter %zu - 2 : ctx.hash %zu\\n\", k, ctx.hash());\n    ctx.task(lB.rw())->*[](hipStream_t stream, auto sB) {\n      inc_kernel<<<1, 10, 0, stream>>>(sB);\n    };\n\n    // fprintf(stderr, \"iter %zu - 3 : ctx.hash %zu\\n\", k, ctx.hash());\n  }\n\n  ctx.host_launch(lA.read(), lB.read())->*[&](auto /*unused*/, auto /*unused*/) {\n    // fprintf(stderr, \"HOST END : ctx.hash %zu\\n\", ctx.hash());\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/hash/ctx_hash.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename S>\n__global__ void inc_kernel(S sA)\n{\n  sA(threadIdx.x)++;\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  int A[10] = {0};\n  stream_ctx ctx;\n\n  auto l = ctx.logical_data(A);\n\n  for (size_t k = 0; k < 10; k++)\n  {\n    // size_t h = l.hash();\n    // fprintf(stderr, \"iter %zu : logical data hash %zu ctx.hash %zu\\n\", k, h, ctx.hash());\n\n    ctx.task(l.rw())->*[](cudaStream_t stream, auto sA) {\n      inc_kernel<<<1, 10, 0, stream>>>(sA);\n    };\n  }\n\n  ctx.host_launch(l.read())->*[&](auto /*unused*/) {\n    // fprintf(stderr, \"HOST end : logical data hash %zu ctx.hash %zu\\n\", l.hash(), ctx.hash());\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename S>\n__global__ void inc_kernel(S sA)\n{\n  sA(threadIdx.x)++;\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  int A[10] = {0};\n  stream_ctx ctx;\n\n  auto l = ctx.logical_data(A);\n\n  for (size_t k = 0; k < 10; k++)\n  {\n    // size_t h = l.hash();\n    // fprintf(stderr, \"iter %zu : logical data hash %zu ctx.hash %zu\\n\", k, h, ctx.hash());\n\n    ctx.task(l.rw())->*[](hipStream_t stream, auto sA) {\n      inc_kernel<<<1, 10, 0, stream>>>(sA);\n    };\n  }\n\n  ctx.host_launch(l.read())->*[&](auto /*unused*/) {\n    // fprintf(stderr, \"HOST end : logical data hash %zu ctx.hash %zu\\n\", l.hash(), ctx.hash());\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/hash/logical_data.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test that we can use a local graph context within a task of the\n *        stream backend\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = T(ind);\n  }\n}\n\ntemplate <typename T>\n__global__ void add(slice<T> s, T val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) += val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  constexpr int N = 12;\n\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = i;\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.task(lX.rw())->*[](cudaStream_t stream, auto sX) {\n    graph_ctx gctx;\n    auto lX_alias = gctx.logical_data(sX, data_place::current_device());\n\n    // X(i) = (i + 17)\n    gctx.task(lX_alias.rw())->*[](cudaStream_t stream2, auto sX) {\n      add<<<16, 128, 0, stream2>>>(sX, 17);\n    };\n\n    // X(i) = 2*(i + 17) + 1\n    gctx.host_launch(lX_alias.rw())->*[&](auto sX) {\n      for (int ind = 0; ind < N; ind++)\n      {\n        sX(ind) = 2 * sX(ind) + 1;\n      }\n    };\n\n    gctx.submit(stream);\n    // no sync !\n  };\n\n  ctx.finalize();\n\n  for (int ind = 0; ind < N; ind++)\n  {\n    EXPECT(X[ind] == 2 * (ind + 17) + 1);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test that we can use a local graph context within a task of the\n *        stream backend\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = T(ind);\n  }\n}\n\ntemplate <typename T>\n__global__ void add(slice<T> s, T val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) += val;\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  constexpr int N = 12;\n\n  int X[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = i;\n  }\n\n  auto lX = ctx.logical_data(X);\n\n  ctx.task(lX.rw())->*[](hipStream_t stream, auto sX) {\n    graph_ctx gctx;\n    auto lX_alias = gctx.logical_data(sX, data_place::current_device());\n\n    // X(i) = (i + 17)\n    gctx.task(lX_alias.rw())->*[](hipStream_t stream2, auto sX) {\n      add<<<16, 128, 0, stream2>>>(sX, 17);\n    };\n\n    // X(i) = 2*(i + 17) + 1\n    gctx.host_launch(lX_alias.rw())->*[&](auto sX) {\n      for (int ind = 0; ind < N; ind++)\n      {\n        sX(ind) = 2 * sX(ind) + 1;\n      }\n    };\n\n    gctx.submit(stream);\n    // no sync !\n  };\n\n  ctx.finalize();\n\n  for (int ind = 0; ind < N; ind++)\n  {\n    EXPECT(X[ind] == 2 * (ind + 17) + 1);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/mix_stream_and_graph.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels with dynamic dependencies (add_deps)\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  auto t = ctx.cuda_kernel_chain();\n  t.add_deps(lX.read());\n  t.add_deps(lY.rw());\n  t->*[&]() {\n    auto dX = t.template get<slice<double>>(0);\n    auto dY = t.template get<slice<double>>(1);\n    // clang-format off\n    return std::vector<cuda_kernel_desc> {\n        { axpy, 16, 128, 0, alpha, dX, dY },\n        { axpy, 16, 128, 0, beta, dX, dY },\n        { axpy, 16, 128, 0, gamma, dX, dY }\n    };\n    // clang-format on\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels with dynamic dependencies (add_deps)\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  auto t = ctx.cuda_kernel_chain();\n  t.add_deps(lX.read());\n  t.add_deps(lY.rw());\n  t->*[&]() {\n    auto dX = t.template get<slice<double>>(0);\n    auto dY = t.template get<slice<double>>(1);\n    // clang-format off\n    return std::vector<cuda_kernel_desc> {\n        { axpy, 16, 128, 0, alpha, dX, dY },\n        { axpy, 16, 128, 0, beta, dX, dY },\n        { axpy, 16, 128, 0, gamma, dX, dY }\n    };\n    // clang-format on\n  };\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/cuda_kernel_chain-add_deps.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void scal(size_t n, T a, T* x)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < n; ind += nthreads)\n  {\n    x[ind] = a * x[ind];\n  }\n}\n\ndouble x_init(int i)\n{\n  return cos((double) i);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const int n = 4096;\n  double X[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = x_init(ind);\n  }\n\n  auto handle_X = ctx.logical_data(X);\n\n  double alpha = 2.0;\n  int niter    = 4;\n  for (int iter = 0; iter < niter; iter++)\n  {\n    ctx.task(handle_X.rw())->*[&](cudaStream_t s, auto sX) {\n      scal<<<16, 128, 0, s>>>(sX.size(), alpha, sX.data_handle());\n    };\n  }\n\n  // Ask to use Y on the host\n  ctx.host_launch(handle_X.read())->*[&](auto sX) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(sX(ind) - pow(alpha, niter) * (x_init(ind))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void scal(size_t n, T a, T* x)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < n; ind += nthreads)\n  {\n    x[ind] = a * x[ind];\n  }\n}\n\ndouble x_init(int i)\n{\n  return cos((double) i);\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const int n = 4096;\n  double X[n];\n\n  for (int ind = 0; ind < n; ind++)\n  {\n    X[ind] = x_init(ind);\n  }\n\n  auto handle_X = ctx.logical_data(X);\n\n  double alpha = 2.0;\n  int niter    = 4;\n  for (int iter = 0; iter < niter; iter++)\n  {\n    ctx.task(handle_X.rw())->*[&](hipStream_t s, auto sX) {\n      scal<<<16, 128, 0, s>>>(sX.size(), alpha, sX.data_handle());\n    };\n  }\n\n  // Ask to use Y on the host\n  ctx.host_launch(handle_X.read())->*[&](auto sX) {\n    for (int ind = 0; ind < n; ind++)\n    {\n      EXPECT(fabs(sX(ind) - pow(alpha, niter) * (x_init(ind))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/scal.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void scalar_div(const double* a, const double* b, double* c)\n{\n  *c = (*a) / (*b);\n}\n\nstatic __global__ void scalar_minus(const double* a, double* res)\n{\n  *res = -(*a);\n}\n\n/**\n * This class is an example of class to issue tasks when accessing a scalar\n * value.\n *\n * This is not meant to be the most efficient approach, but this is\n * supposedly convenient.\n *\n */\ntemplate <typename Ctx>\nclass scalar\n{\npublic:\n  scalar(Ctx* ctx, bool is_tmp = false)\n      : ctx(ctx)\n  {\n    size_t s = sizeof(double);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      h_addr = NULL;\n    }\n    else\n    {\n      h_addr = (double*) malloc(s);\n      cuda_safe_call(cudaHostRegister(h_addr, s, cudaHostRegisterPortable));\n    }\n\n    data_place d = is_tmp ? data_place::invalid() : data_place::host();\n    handle       = ctx->logical_data(make_slice(h_addr), d);\n  }\n\n  // Copy constructor\n  scalar(const scalar& a)\n      : ctx(a.ctx)\n  {\n    h_addr = NULL;\n    handle = ctx->logical_data(make_slice((double*) nullptr));\n\n    ctx->task(handle.write(), a.handle.read())->*[](cudaStream_t stream, auto dst, auto src) {\n      // There are likely much more efficient ways.\n      cuda_safe_call(\n        cudaMemcpyAsync(dst.data_handle(), src.data_handle(), sizeof(double), cudaMemcpyDeviceToDevice, stream));\n    };\n  }\n\n  scalar operator/(scalar const& rhs) const\n  {\n    // Submit a task that computes this/rhs\n    scalar res(ctx);\n\n    ctx->task(handle.read(), rhs.handle.read(), res.handle.write())\n        ->*[](cudaStream_t stream, auto x, auto y1, auto result) {\n              scalar_div<<<1, 1, 0, stream>>>(x.data_handle(), y1.data_handle(), result.data_handle());\n            };\n\n    return res;\n  }\n\n  scalar operator-() const\n  {\n    // Submit a task that computes -s\n    scalar res(ctx);\n    ctx->task(handle.read(), res.handle.write())->*[](cudaStream_t stream, auto x, auto result) {\n      scalar_minus<<<1, 1, 0, stream>>>(x.data_handle(), result.data_handle());\n    };\n\n    return res;\n  }\n\n  Ctx* ctx;\n  mutable logical_data<slice<double, 0>> handle;\n  double* h_addr;\n};\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  scalar a(&ctx);\n  scalar b(&ctx);\n\n  *a.h_addr = 42.0;\n  *b.h_addr = 12.3;\n\n  scalar c = (-a) / b;\n\n  ctx.host_launch(c.handle.read())->*[](auto x) {\n    EXPECT(fabs(*x.data_handle() - (-42.0) / 12.3) < 0.001);\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void scalar_div(const double* a, const double* b, double* c)\n{\n  *c = (*a) / (*b);\n}\n\nstatic __global__ void scalar_minus(const double* a, double* res)\n{\n  *res = -(*a);\n}\n\n/**\n * This class is an example of class to issue tasks when accessing a scalar\n * value.\n *\n * This is not meant to be the most efficient approach, but this is\n * supposedly convenient.\n *\n */\ntemplate <typename Ctx>\nclass scalar\n{\npublic:\n  scalar(Ctx* ctx, bool is_tmp = false)\n      : ctx(ctx)\n  {\n    size_t s = sizeof(double);\n\n    if (is_tmp)\n    {\n      // There is no physical backing for this temporary vector\n      h_addr = NULL;\n    }\n    else\n    {\n      h_addr = (double*) malloc(s);\n      cuda_safe_call(hipHostRegister(h_addr, s, hipHostRegisterPortable));\n    }\n\n    data_place d = is_tmp ? data_place::invalid() : data_place::host();\n    handle       = ctx->logical_data(make_slice(h_addr), d);\n  }\n\n  // Copy constructor\n  scalar(const scalar& a)\n      : ctx(a.ctx)\n  {\n    h_addr = NULL;\n    handle = ctx->logical_data(make_slice((double*) nullptr));\n\n    ctx->task(handle.write(), a.handle.read())->*[](hipStream_t stream, auto dst, auto src) {\n      // There are likely much more efficient ways.\n      cuda_safe_call(\n        hipMemcpyAsync(dst.data_handle(), src.data_handle(), sizeof(double), hipMemcpyDeviceToDevice, stream));\n    };\n  }\n\n  scalar operator/(scalar const& rhs) const\n  {\n    // Submit a task that computes this/rhs\n    scalar res(ctx);\n\n    ctx->task(handle.read(), rhs.handle.read(), res.handle.write())\n        ->*[](hipStream_t stream, auto x, auto y1, auto result) {\n              scalar_div<<<1, 1, 0, stream>>>(x.data_handle(), y1.data_handle(), result.data_handle());\n            };\n\n    return res;\n  }\n\n  scalar operator-() const\n  {\n    // Submit a task that computes -s\n    scalar res(ctx);\n    ctx->task(handle.read(), res.handle.write())->*[](hipStream_t stream, auto x, auto result) {\n      scalar_minus<<<1, 1, 0, stream>>>(x.data_handle(), result.data_handle());\n    };\n\n    return res;\n  }\n\n  Ctx* ctx;\n  mutable logical_data<slice<double, 0>> handle;\n  double* h_addr;\n};\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  scalar a(&ctx);\n  scalar b(&ctx);\n\n  *a.h_addr = 42.0;\n  *b.h_addr = 12.3;\n\n  scalar c = (-a) / b;\n\n  ctx.host_launch(c.handle.read())->*[](auto x) {\n    EXPECT(fabs(*x.data_handle() - (-42.0) / 12.3) < 0.001);\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/scalar_div.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t n, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(size_t n, T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < n; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const size_t n     = 12;\n  const double alpha = 2.0;\n\n  double *dX, *dY;\n  cuda_safe_call(cudaMalloc((void**) &dX, n * sizeof(double)));\n  cuda_safe_call(cudaMalloc((void**) &dY, n * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(n, dX, dY);\n  cuda_safe_call(cudaDeviceSynchronize());\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, n), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, n), data_place::device(0));\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](cudaStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(n, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto X, auto Y) {\n    for (size_t ind = 0; ind < n; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(size_t n, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(size_t n, T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < n; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const size_t n     = 12;\n  const double alpha = 2.0;\n\n  double *dX, *dY;\n  cuda_safe_call(hipMalloc((void**) &dX, n * sizeof(double)));\n  cuda_safe_call(hipMalloc((void**) &dY, n * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(n, dX, dY);\n  cuda_safe_call(hipDeviceSynchronize());\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, n), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, n), data_place::device(0));\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](hipStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(n, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.host_launch(handle_X.read(), handle_Y.read())->*[&](auto X, auto Y) {\n    for (size_t ind = 0; ind < n; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/data_from_device.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int N, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(int N, T* x, T* y, T* z)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n    z[ind] = 7.0 * ind + 6.0;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const double alpha = 2.0;\n  const int N        = 12;\n\n  double *dX, *dY, *dZ;\n  cuda_safe_call(cudaMalloc((void**) &dX, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc((void**) &dY, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc((void**) &dZ, N * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(N, dX, dY, dZ);\n  cuda_safe_call(cudaDeviceSynchronize());\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, N), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, N), data_place::device(0));\n  auto handle_Z = ctx.logical_data(make_slice(dZ, N), data_place::device(0));\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](cudaStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  ctx.task(handle_X.read(), handle_Z.rw())->*[&](cudaStream_t stream, auto X, auto Z) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, X.data_handle(), Z.data_handle());\n  };\n\n  ctx.task(handle_Y.read(), handle_Z.rw())->*[&](cudaStream_t stream, auto Y, auto Z) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, Y.data_handle(), Z.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.host_launch(handle_X.read(), handle_Y.read(), handle_Z.read())->*[&](auto X, auto Y, auto Z) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n      // Z = Z + alpha (X + alpha Y)\n      EXPECT(fabs(Z(ind) - ((6.0 - 3 * alpha) + ind * (7.0 + 3 * alpha + alpha * alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int N, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(int N, T* x, T* y, T* z)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n    z[ind] = 7.0 * ind + 6.0;\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n\n  const double alpha = 2.0;\n  const int N        = 12;\n\n  double *dX, *dY, *dZ;\n  cuda_safe_call(hipMalloc((void**) &dX, N * sizeof(double)));\n  cuda_safe_call(hipMalloc((void**) &dY, N * sizeof(double)));\n  cuda_safe_call(hipMalloc((void**) &dZ, N * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(N, dX, dY, dZ);\n  cuda_safe_call(hipDeviceSynchronize());\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, N), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, N), data_place::device(0));\n  auto handle_Z = ctx.logical_data(make_slice(dZ, N), data_place::device(0));\n\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](hipStream_t stream, auto X, auto Y) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, X.data_handle(), Y.data_handle());\n  };\n\n  ctx.task(handle_X.read(), handle_Z.rw())->*[&](hipStream_t stream, auto X, auto Z) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, X.data_handle(), Z.data_handle());\n  };\n\n  ctx.task(handle_Y.read(), handle_Z.rw())->*[&](hipStream_t stream, auto Y, auto Z) {\n    axpy<<<16, 128, 0, stream>>>(N, alpha, Y.data_handle(), Z.data_handle());\n  };\n\n  // Access Ask to use X, Y and Z on the host\n  ctx.host_launch(handle_X.read(), handle_Y.read(), handle_Z.read())->*[&](auto X, auto Y, auto Z) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(X(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(Y(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n      // Z = Z + alpha (X + alpha Y)\n      EXPECT(fabs(Z(ind) - ((6.0 - 3 * alpha) + ind * (7.0 + 3 * alpha + alpha * alpha))) < 0.00001);\n    }\n  };\n\n  ctx.finalize();\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/data_from_device_2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * This test makes sure write-back works even if the original data place was a device\n */\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = 1.0 * ind;\n  }\n}\n\ntemplate <typename T>\n__global__ void check(T* x, size_t n)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    assert(x[ind] == 2.0 * ind + 1.0);\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const size_t n = 12;\n\n  double* dX;\n  cuda_safe_call(cudaMalloc((void**) &dX, n * sizeof(double)));\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, n), data_place::device(0));\n\n  ctx.task(handle_X.write())->*[&](cudaStream_t stream, auto X) {\n    setup<<<16, 128, 0, stream>>>(X);\n  };\n\n  ctx.host_launch(handle_X.rw())->*[&](auto X) {\n    for (size_t ind = 0; ind < n; ind++)\n    {\n      X(ind) = 2.0 * X(ind) + 1.0;\n    }\n  };\n\n  ctx.finalize();\n\n  // Check if data was properly written-back with a blocking kernel\n  check<<<16, 128>>>(dX, n);\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n/*\n * This test makes sure write-back works even if the original data place was a device\n */\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = 1.0 * ind;\n  }\n}\n\ntemplate <typename T>\n__global__ void check(T* x, size_t n)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < n; ind += nthreads)\n  {\n    assert(x[ind] == 2.0 * ind + 1.0);\n  }\n}\n\ntemplate <typename Ctx>\nvoid run()\n{\n  Ctx ctx;\n  const size_t n = 12;\n\n  double* dX;\n  cuda_safe_call(hipMalloc((void**) &dX, n * sizeof(double)));\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, n), data_place::device(0));\n\n  ctx.task(handle_X.write())->*[&](hipStream_t stream, auto X) {\n    setup<<<16, 128, 0, stream>>>(X);\n  };\n\n  ctx.host_launch(handle_X.rw())->*[&](auto X) {\n    for (size_t ind = 0; ind < n; ind++)\n    {\n      X(ind) = 2.0 * X(ind) + 1.0;\n    }\n  };\n\n  ctx.finalize();\n\n  // Check if data was properly written-back with a blocking kernel\n  check<<<16, 128>>>(dX, n);\n}\n\nint main()\n{\n  run<stream_ctx>();\n  run<graph_ctx>();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/data_from_device_wb.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test that we can use a local graph context within a task of the\n *        stream backend to create a CUDA graph that we can launch multiple\n *        times.\n */\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = T(ind);\n  }\n}\n\ntemplate <typename T>\n__global__ void add(slice<T> s, T val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) += val;\n  }\n}\n\n__global__ void slice_add(slice<const int> s_from, slice<int> s_to)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s_from.size(); ind += nthreads)\n  {\n    s_to(ind) += s_from(ind);\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N    = 12;\n  const size_t K = 10;\n\n  int X[N];\n  int Y[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = i;\n    Y[i] = -i;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /*\n   * Create a CUDA graph from a single task, and launch it many times\n   */\n\n  ctx.task(lX.rw(), lY.rw())->*[&](cudaStream_t stream, auto sX, auto sY) {\n    graph_ctx gctx;\n    auto lX_alias = gctx.logical_data(sX, data_place::current_device());\n    auto lY_alias = gctx.logical_data(sY, data_place::current_device());\n\n    for (size_t ii = 0; ii < 10; ii++)\n    {\n      gctx.task(lX_alias.rw())->*[](cudaStream_t stream2, auto sX) {\n        add<<<16, 128, 0, stream2>>>(sX, 17);\n      };\n      gctx.task(lY_alias.rw())->*[](cudaStream_t stream2, auto sY) {\n        add<<<16, 128, 0, stream2>>>(sY, 17);\n      };\n      gctx.task(lX_alias.read(), lY_alias.rw())->*[](cudaStream_t stream2, auto sX, auto sY) {\n        slice_add<<<16, 128, 0, stream2>>>(sX, sY);\n      };\n      gctx.task(lX_alias.rw())->*[](cudaStream_t stream2, auto sX) {\n        add<<<16, 128, 0, stream2>>>(sX, 17);\n      };\n      gctx.task(lY_alias.rw())->*[](cudaStream_t stream2, auto sY) {\n        add<<<16, 128, 0, stream2>>>(sY, 17);\n      };\n    }\n\n    //        gctx.host_launch(lX_alias.rw())->*[&](auto sX) {\n    //            for (size_t ind = 0; ind < N; ind++) {\n    //                sX(ind) = 2 * sX(ind) + 1;\n    //            }\n    //        };\n\n    // gctx.print_to_dot(\"gctx\" + std::to_string(iter));\n    auto exec_graph = gctx.instantiate();\n    for (size_t iter = 0; iter < K; iter++)\n    {\n      cudaGraphLaunch(*exec_graph, stream);\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Test that we can use a local graph context within a task of the\n *        stream backend to create a CUDA graph that we can launch multiple\n *        times.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void setup(slice<T> s)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) = T(ind);\n  }\n}\n\ntemplate <typename T>\n__global__ void add(slice<T> s, T val)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s.size(); ind += nthreads)\n  {\n    s(ind) += val;\n  }\n}\n\n__global__ void slice_add(slice<const int> s_from, slice<int> s_to)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (size_t ind = tid; ind < s_from.size(); ind += nthreads)\n  {\n    s_to(ind) += s_from(ind);\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N    = 12;\n  const size_t K = 10;\n\n  int X[N];\n  int Y[N];\n\n  for (int i = 0; i < N; i++)\n  {\n    X[i] = i;\n    Y[i] = -i;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /*\n   * Create a CUDA graph from a single task, and launch it many times\n   */\n\n  ctx.task(lX.rw(), lY.rw())->*[&](hipStream_t stream, auto sX, auto sY) {\n    graph_ctx gctx;\n    auto lX_alias = gctx.logical_data(sX, data_place::current_device());\n    auto lY_alias = gctx.logical_data(sY, data_place::current_device());\n\n    for (size_t ii = 0; ii < 10; ii++)\n    {\n      gctx.task(lX_alias.rw())->*[](hipStream_t stream2, auto sX) {\n        add<<<16, 128, 0, stream2>>>(sX, 17);\n      };\n      gctx.task(lY_alias.rw())->*[](hipStream_t stream2, auto sY) {\n        add<<<16, 128, 0, stream2>>>(sY, 17);\n      };\n      gctx.task(lX_alias.read(), lY_alias.rw())->*[](hipStream_t stream2, auto sX, auto sY) {\n        slice_add<<<16, 128, 0, stream2>>>(sX, sY);\n      };\n      gctx.task(lX_alias.rw())->*[](hipStream_t stream2, auto sX) {\n        add<<<16, 128, 0, stream2>>>(sX, 17);\n      };\n      gctx.task(lY_alias.rw())->*[](hipStream_t stream2, auto sY) {\n        add<<<16, 128, 0, stream2>>>(sY, 17);\n      };\n    }\n\n    //        gctx.host_launch(lX_alias.rw())->*[&](auto sX) {\n    //            for (size_t ind = 0; ind < N; ind++) {\n    //                sX(ind) = 2 * sX(ind) + 1;\n    //            }\n    //        };\n\n    // gctx.print_to_dot(\"gctx\" + std::to_string(iter));\n    auto exec_graph = gctx.instantiate();\n    for (size_t iter = 0; iter < K; iter++)\n    {\n      hipGraphLaunch(*exec_graph, stream);\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/mix_stream_and_graph_2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Make sure we can compile with no arguments in CUDA kernel\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  // Ensure this works without arguments in the kernel\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto, auto) {\n    return cuda_kernel_desc{dummy, 16, 128, 0};\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Make sure we can compile with no arguments in CUDA kernel\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void dummy() {}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  // Ensure this works without arguments in the kernel\n  ctx.cuda_kernel(lX.read(), lY.rw())->*[&](auto, auto) {\n    return cuda_kernel_desc{dummy, 16, 128, 0};\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/cuda_kernel_empty_args.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n// #include <cuda/experimental/__stf/graph/interfaces/slice.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int N, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(int N, T* x, T* y, T* z)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n    z[ind] = 7.0 * ind + 6.0;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  graph_ctx ctx;\n  const int N        = 12;\n  const double alpha = 2.0;\n\n  double *dX, *dY, *dZ;\n\n  cuda_safe_call(cudaMalloc((void**) &dX, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc((void**) &dY, N * sizeof(double)));\n  cuda_safe_call(cudaMalloc((void**) &dZ, N * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(N, dX, dY, dZ);\n  cuda_safe_call(cudaDeviceSynchronize());\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, N), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, N), data_place::device(0));\n  auto handle_Z = ctx.logical_data(make_slice(dZ, N), data_place::device(0));\n\n  // Y = Y + alpha X\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](cudaStream_t stream, auto dX, auto dY) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dX.data_handle(), dY.data_handle());\n  };\n\n  // Z = Z + alpha X\n  ctx.task(handle_X.read(), handle_Z.rw())->*[&](cudaStream_t stream, auto dX, auto dZ) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dX.data_handle(), dZ.data_handle());\n  };\n\n  // Z = Z + alpha Y\n  ctx.task(handle_Y.read(), handle_Z.rw())->*[&](cudaStream_t stream, auto dY, auto dZ) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dY.data_handle(), dZ.data_handle());\n  };\n\n  ctx.host_launch(handle_X.read(), handle_Y.read(), handle_Z.read())->*[&](auto hX, auto hY, auto hZ) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(hY(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n      // Z = Z + alpha (X + alpha Y)\n      EXPECT(fabs(hZ(ind) - ((6.0 - 3 * alpha) + ind * (7.0 + 3 * alpha + alpha * alpha))) < 0.00001);\n    }\n  };\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n// #include <cuda/experimental/__stf/graph/interfaces/slice.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void axpy(int N, T a, const T* x, T* y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    y[ind] += a * x[ind];\n  }\n}\n\ntemplate <typename T>\n__global__ void setup_vectors(int N, T* x, T* y, T* z)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int ind = tid; ind < N; ind += nthreads)\n  {\n    x[ind] = 1.0 * ind;\n    y[ind] = 2.0 * ind - 3.0;\n    z[ind] = 7.0 * ind + 6.0;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  graph_ctx ctx;\n  const int N        = 12;\n  const double alpha = 2.0;\n\n  double *dX, *dY, *dZ;\n\n  cuda_safe_call(hipMalloc((void**) &dX, N * sizeof(double)));\n  cuda_safe_call(hipMalloc((void**) &dY, N * sizeof(double)));\n  cuda_safe_call(hipMalloc((void**) &dZ, N * sizeof(double)));\n\n  // Use a kernel to setup values\n  setup_vectors<<<16, 16>>>(N, dX, dY, dZ);\n  cuda_safe_call(hipDeviceSynchronize());\n\n  // We here provide device addresses and memory node 1 (which is assumed to\n  // be device 0)\n  auto handle_X = ctx.logical_data(make_slice(dX, N), data_place::device(0));\n  auto handle_Y = ctx.logical_data(make_slice(dY, N), data_place::device(0));\n  auto handle_Z = ctx.logical_data(make_slice(dZ, N), data_place::device(0));\n\n  // Y = Y + alpha X\n  ctx.task(handle_X.read(), handle_Y.rw())->*[&](hipStream_t stream, auto dX, auto dY) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dX.data_handle(), dY.data_handle());\n  };\n\n  // Z = Z + alpha X\n  ctx.task(handle_X.read(), handle_Z.rw())->*[&](hipStream_t stream, auto dX, auto dZ) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dX.data_handle(), dZ.data_handle());\n  };\n\n  // Z = Z + alpha Y\n  ctx.task(handle_Y.read(), handle_Z.rw())->*[&](hipStream_t stream, auto dY, auto dZ) {\n    axpy<<<16, 16, 0, stream>>>(N, alpha, dY.data_handle(), dZ.data_handle());\n  };\n\n  ctx.host_launch(handle_X.read(), handle_Y.read(), handle_Z.read())->*[&](auto hX, auto hY, auto hZ) {\n    for (size_t ind = 0; ind < N; ind++)\n    {\n      // X unchanged\n      EXPECT(fabs(hX(ind) - 1.0 * ind) < 0.00001);\n      // Y = Y + alpha X\n      EXPECT(fabs(hY(ind) - (-3.0 + ind * (2.0 + alpha))) < 0.00001);\n      // Z = Z + alpha (X + alpha Y)\n      EXPECT(fabs(hZ(ind) - ((6.0 - 3 * alpha) + ind * (7.0 + 3 * alpha + alpha * alpha))) < 0.00001);\n    }\n  };\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/graph_use_device_data.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels with dynamic dependencies (add_deps)\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  auto t = ctx.cuda_kernel_chain();\n  t.add_deps(lX.read());\n  t.add_deps(lY.rw());\n  t.start();\n  auto dX = t.template get<slice<double>>(0);\n  auto dY = t.template get<slice<double>>(1);\n  ::std::vector<cuda_kernel_desc> descs;\n  descs.resize(3);\n  // Configure with types\n  descs[0].configure(axpy, 16, 128, 0, alpha, dX, dY);\n  descs[1].configure(axpy, 16, 128, 0, beta, dX, dY);\n\n  // Configure with low level API\n  const void* args[3] = {&gamma, &dX, &dY};\n  descs[2].configure_raw(axpy, 16, 128, 0, 3, args);\n\n  t.add_kernel_desc(descs);\n  t.end();\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Example of task implementing a chain of CUDA kernels with dynamic dependencies (add_deps)\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nint main()\n{\n  context ctx    = graph_ctx();\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = X0(i);\n    Y[i] = Y0(i);\n  }\n\n  double alpha = 3.14;\n  double beta  = 4.5;\n  double gamma = -4.1;\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n  /* Compute Y = Y + alpha X, Y = Y + beta X and then  Y = Y + gamma X */\n  auto t = ctx.cuda_kernel_chain();\n  t.add_deps(lX.read());\n  t.add_deps(lY.rw());\n  t.start();\n  auto dX = t.template get<slice<double>>(0);\n  auto dY = t.template get<slice<double>>(1);\n  ::std::vector<cuda_kernel_desc> descs;\n  descs.resize(3);\n  // Configure with types\n  descs[0].configure(axpy, 16, 128, 0, alpha, dX, dY);\n  descs[1].configure(axpy, 16, 128, 0, beta, dX, dY);\n\n  // Configure with low level API\n  const void* args[3] = {&gamma, &dX, &dY};\n  descs[2].configure_raw(axpy, 16, 128, 0, 3, args);\n\n  t.add_kernel_desc(descs);\n  t.end();\n\n  ctx.finalize();\n\n  for (size_t i = 0; i < N; i++)\n  {\n    assert(fabs(Y[i] - (Y0(i) + (alpha + beta + gamma) * X0(i))) < 0.0001);\n    assert(fabs(X[i] - X0(i)) < 0.0001);\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/interface/cuda_kernel_chain-add_deps_low_level.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr)\n{\n  *ptr = *ptr + 1;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  int a = 17;\n\n  auto handle = ctx.logical_data(make_slice(&a, 1));\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  int K = 1024;\n  for (int i = 0; i < K; i++)\n  {\n    // Increment the variable by 1\n    ctx.task(exec_place::device(i % ndevs), handle.relaxed(redux_op))->*[](auto stream, auto s) {\n      add<<<1, 1, 0, stream>>>(s.data_handle());\n    };\n  }\n\n  // Total value should be initial value + K\n  ctx.task(exec_place::host(), handle.read())->*[&](auto stream, auto s) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    EXPECT(s(0) == 17 + K);\n    // printf(\"VALUE %d expected %d\\n\", s(0), 17 + K);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr)\n{\n  *ptr = *ptr + 1;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  int a = 17;\n\n  auto handle = ctx.logical_data(make_slice(&a, 1));\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  int K = 1024;\n  for (int i = 0; i < K; i++)\n  {\n    // Increment the variable by 1\n    ctx.task(exec_place::device(i % ndevs), handle.relaxed(redux_op))->*[](auto stream, auto s) {\n      add<<<1, 1, 0, stream>>>(s.data_handle());\n    };\n  }\n\n  // Total value should be initial value + K\n  ctx.task(exec_place::host(), handle.read())->*[&](auto stream, auto s) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    EXPECT(s(0) == 17 + K);\n    // printf(\"VALUE %d expected %d\\n\", s(0), 17 + K);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/many_inc.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test ensures that data are properly reconstructed after a\n *        reduction phase during the write-back mechanism\n */\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void add_val(slice<T> inout, T val)\n{\n  inout(0) += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  int var = 42;\n\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[=](cudaStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var, i);\n      };\n    }\n\n    // host\n    ctx.host_launch(var_handle.relaxed(redux_op))->*[=](auto var) {\n      var(0) += i;\n    };\n  }\n\n  ctx.finalize();\n\n  int expected = 42 + (N * (N - 1)) / 2 * (ndevs + 1);\n  EXPECT(var == expected);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test ensures that data are properly reconstructed after a\n *        reduction phase during the write-back mechanism\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void add_val(slice<T> inout, T val)\n{\n  inout(0) += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  int var = 42;\n\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[=](hipStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var, i);\n      };\n    }\n\n    // host\n    ctx.host_launch(var_handle.relaxed(redux_op))->*[=](auto var) {\n      var(0) += i;\n    };\n  }\n\n  ctx.finalize();\n\n  int expected = 42 + (N * (N - 1)) / 2 * (ndevs + 1);\n  EXPECT(var == expected);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/write_back_after_redux.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test ensures that we can apply a reduction on a logical data\n *        described using a shape\n */\n\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice<int>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\n// instance id are put for debugging purpose ...\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  *inout_addr += *in_addr;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\nclass scalar_sum_t : public stream_reduction_operator<scalar_t>\n{\npublic:\n  void op(const scalar_t& in, scalar_t& inout, const exec_place& e, cudaStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      // TODO make a callback when the situation gets better\n      cuda_safe_call(cudaStreamSynchronize(s));\n      *inout.data_handle() += *in.data_handle();\n    }\n    else\n    {\n      // this is not the host, so this has to be a device ... (XXX)\n      add<int><<<1, 1, 0, s>>>(in.data_handle(), inout.data_handle());\n    }\n  }\n\n  void init_op(scalar_t& out, const exec_place& e, cudaStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      // TODO make a callback when the situation gets better\n      cuda_safe_call(cudaStreamSynchronize(s));\n      *out.data_handle() = 0;\n    }\n    else\n    {\n      // this is not the host, so this has to be a device ... (XXX)\n      set_value<int><<<1, 1, 0, s>>>(out.data_handle(), 0);\n    }\n  }\n};\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[&](cudaStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var.data_handle(), i);\n      };\n    }\n\n    // host\n    ctx.task(exec_place::host(), var_handle.relaxed(redux_op))->*[&](cudaStream_t s, auto var) {\n      cuda_safe_call(cudaStreamSynchronize(s));\n      *var.data_handle() += i;\n    };\n  }\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[&](cudaStream_t s, auto var) {\n    cuda_safe_call(cudaStreamSynchronize(s));\n    int value    = *var.data_handle();\n    int expected = (N * (N - 1)) / 2 * (ndevs + 1);\n    EXPECT(value == expected);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This test ensures that we can apply a reduction on a logical data\n *        described using a shape\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice<int>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\n// instance id are put for debugging purpose ...\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  *inout_addr += *in_addr;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\nclass scalar_sum_t : public stream_reduction_operator<scalar_t>\n{\npublic:\n  void op(const scalar_t& in, scalar_t& inout, const exec_place& e, hipStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      // TODO make a callback when the situation gets better\n      cuda_safe_call(hipStreamSynchronize(s));\n      *inout.data_handle() += *in.data_handle();\n    }\n    else\n    {\n      // this is not the host, so this has to be a device ... (XXX)\n      add<int><<<1, 1, 0, s>>>(in.data_handle(), inout.data_handle());\n    }\n  }\n\n  void init_op(scalar_t& out, const exec_place& e, hipStream_t s) override\n  {\n    if (e.affine_data_place().is_host())\n    {\n      // TODO make a callback when the situation gets better\n      cuda_safe_call(hipStreamSynchronize(s));\n      *out.data_handle() = 0;\n    }\n    else\n    {\n      // this is not the host, so this has to be a device ... (XXX)\n      set_value<int><<<1, 1, 0, s>>>(out.data_handle(), 0);\n    }\n  }\n};\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[&](hipStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var.data_handle(), i);\n      };\n    }\n\n    // host\n    ctx.task(exec_place::host(), var_handle.relaxed(redux_op))->*[&](hipStream_t s, auto var) {\n      cuda_safe_call(hipStreamSynchronize(s));\n      *var.data_handle() += i;\n    };\n  }\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[&](hipStream_t s, auto var) {\n    cuda_safe_call(hipStreamSynchronize(s));\n    int value    = *var.data_handle();\n    int expected = (N * (N - 1)) / 2 * (ndevs + 1);\n    EXPECT(value == expected);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/sum_multiple_places_no_refvalue.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr, int value)\n{\n  *ptr = *ptr + value;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int a         = 17;\n  auto handle   = ctx.logical_data(make_slice(&a, 1));\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n  ctx.task(handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 42);\n  };\n\n  ctx.task(handle.rw())->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 1);\n  };\n\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    EXPECT(s(0) == 17 + 42 + 1);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr, int value)\n{\n  *ptr = *ptr + value;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int a         = 17;\n  auto handle   = ctx.logical_data(make_slice(&a, 1));\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n  ctx.task(handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 42);\n  };\n\n  ctx.task(handle.rw())->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 1);\n  };\n\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    EXPECT(s(0) == 17 + 42 + 1);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/redux_test.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void check_value_and_reset(T* addr, T val, T reset_val)\n{\n  assert(*addr == val);\n  *addr = reset_val;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N = 4;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  const int niters = 4;\n  for (int iter = 0; iter < niters; iter++)\n  {\n    // We add i (total = N(N-1)/2 + initial_value)\n    for (int i = 0; i < N; i++)\n    {\n      ctx.task(var_handle.relaxed(redux_op))->*[=](cudaStream_t stream, auto d_var) {\n        add_val<<<1, 1, 0, stream>>>(d_var.data_handle(), i);\n        cuda_safe_call(cudaGetLastError());\n      };\n    }\n\n    // Check that we have the expected value, and reset it so that we can perform another reduction\n    ctx.task(var_handle.rw())->*[=](cudaStream_t stream, auto d_var) {\n      int expected = (N * (N - 1)) / 2;\n      check_value_and_reset<<<1, 1, 0, stream>>>(d_var.data_handle(), expected, 0);\n    };\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void check_value_and_reset(T* addr, T val, T reset_val)\n{\n  assert(*addr == val);\n  *addr = reset_val;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const int N = 4;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  const int niters = 4;\n  for (int iter = 0; iter < niters; iter++)\n  {\n    // We add i (total = N(N-1)/2 + initial_value)\n    for (int i = 0; i < N; i++)\n    {\n      ctx.task(var_handle.relaxed(redux_op))->*[=](hipStream_t stream, auto d_var) {\n        add_val<<<1, 1, 0, stream>>>(d_var.data_handle(), i);\n        cuda_safe_call(hipGetLastError());\n      };\n    }\n\n    // Check that we have the expected value, and reset it so that we can perform another reduction\n    ctx.task(var_handle.rw())->*[=](hipStream_t stream, auto d_var) {\n      int expected = (N * (N - 1)) / 2;\n      check_value_and_reset<<<1, 1, 0, stream>>>(d_var.data_handle(), expected, 0);\n    };\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/successive_reductions.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Use the reduction access mode to add variables concurrently on different places\n */\n\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  // printf(\"ADD: %d += %d : RES %d\\n\", *inout_addr, *in_addr, *inout_addr + *in_addr);\n\n  *inout_addr += *in_addr;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\n/*\n * Define a SUM reduction operator over a scalar\n */\nclass scalar_sum_t : public stream_reduction_operator_untyped\n{\npublic:\n  scalar_sum_t()\n      : stream_reduction_operator_untyped() {};\n\n  void stream_redux_op(\n    logical_data_untyped& d,\n    const data_place& /*unused*/,\n    instance_id_t inout_instance_id,\n    const data_place& /*unused*/,\n    instance_id_t in_instance_id,\n    const exec_place& /*unused*/,\n    cudaStream_t s) override\n  {\n    auto& in_instance    = d.instance<typename scalar_t::element_type>(in_instance_id);\n    auto& inout_instance = d.instance<typename scalar_t::element_type>(inout_instance_id);\n    // fprintf(stderr, \"REDUX OP d %p inout (node %d id %d addr %p) in (node %d id %d addr %p)\\n\", d,\n    //        inout_memory_node, inout_instance_id, *inout_instance, in_memory_node, in_instance_id, *in_instance);\n    add<<<1, 1, 0, s>>>(in_instance.data_handle(), inout_instance.data_handle());\n  }\n\n  void stream_init_op(logical_data_untyped& d,\n                      const data_place& /*unused*/,\n                      instance_id_t out_instance_id,\n                      const exec_place& /*unused*/,\n                      cudaStream_t s) override\n  {\n    auto& out_instance = d.instance<typename scalar_t::element_type>(out_instance_id);\n    // fprintf(stderr, \"REDUX INIT d %p memory node %d instance id %d => addr %p\\n\", d, out_memory_node,\n    //        out_instance_id, *out_instance);\n    set_value<<<1, 1, 0, s>>>(out_instance.data_handle(), 0);\n  }\n};\n\nint main()\n{\n  const int N = 4;\n\n  stream_ctx ctx;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  // We add i (total = N(N-1)/2 + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    ctx.task(var_handle.relaxed(redux_op))->*[&](cudaStream_t stream, auto d_var) {\n      add_val<<<1, 1, 0, stream>>>(d_var.data_handle(), i);\n    };\n  }\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[&](cudaStream_t stream, auto h_var) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    int expected = (N * (N - 1)) / 2;\n    EXPECT(h_var(0) == expected);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Use the reduction access mode to add variables concurrently on different places\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  // printf(\"ADD: %d += %d : RES %d\\n\", *inout_addr, *in_addr, *inout_addr + *in_addr);\n\n  *inout_addr += *in_addr;\n}\n\ntemplate <typename T>\n__global__ void add_val(T* inout_addr, T val)\n{\n  *inout_addr += val;\n}\n\n/*\n * Define a SUM reduction operator over a scalar\n */\nclass scalar_sum_t : public stream_reduction_operator_untyped\n{\npublic:\n  scalar_sum_t()\n      : stream_reduction_operator_untyped() {};\n\n  void stream_redux_op(\n    logical_data_untyped& d,\n    const data_place& /*unused*/,\n    instance_id_t inout_instance_id,\n    const data_place& /*unused*/,\n    instance_id_t in_instance_id,\n    const exec_place& /*unused*/,\n    hipStream_t s) override\n  {\n    auto& in_instance    = d.instance<typename scalar_t::element_type>(in_instance_id);\n    auto& inout_instance = d.instance<typename scalar_t::element_type>(inout_instance_id);\n    // fprintf(stderr, \"REDUX OP d %p inout (node %d id %d addr %p) in (node %d id %d addr %p)\\n\", d,\n    //        inout_memory_node, inout_instance_id, *inout_instance, in_memory_node, in_instance_id, *in_instance);\n    add<<<1, 1, 0, s>>>(in_instance.data_handle(), inout_instance.data_handle());\n  }\n\n  void stream_init_op(logical_data_untyped& d,\n                      const data_place& /*unused*/,\n                      instance_id_t out_instance_id,\n                      const exec_place& /*unused*/,\n                      hipStream_t s) override\n  {\n    auto& out_instance = d.instance<typename scalar_t::element_type>(out_instance_id);\n    // fprintf(stderr, \"REDUX INIT d %p memory node %d instance id %d => addr %p\\n\", d, out_memory_node,\n    //        out_instance_id, *out_instance);\n    set_value<<<1, 1, 0, s>>>(out_instance.data_handle(), 0);\n  }\n};\n\nint main()\n{\n  const int N = 4;\n\n  stream_ctx ctx;\n\n  auto var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  // We add i (total = N(N-1)/2 + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    ctx.task(var_handle.relaxed(redux_op))->*[&](hipStream_t stream, auto d_var) {\n      add_val<<<1, 1, 0, stream>>>(d_var.data_handle(), i);\n    };\n  }\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[&](hipStream_t stream, auto h_var) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    int expected = (N * (N - 1)) / 2;\n    EXPECT(h_var(0) == expected);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/sum.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr, int value)\n{\n  *ptr = *ptr + value;\n}\n\n__global__ void check_val(const int* ptr, int expected)\n{\n  assert(*ptr == expected);\n}\n\n/*\n * This test ensures that we can reconstruct a piece of data where there are\n * multiple \"shared\" instances, and \"redux\" instances\n */\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"Skipping test: need at least 2 devices.\\n\");\n    return 0;\n  }\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  int a = 17;\n\n  // init op (17)\n  auto handle = ctx.logical_data(make_slice(&a, 1));\n\n  // RW dev0 (18)\n  ctx.task(exec_place::device(0), handle.rw())->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 1);\n  };\n\n  // READ dev1 (18)\n  ctx.task(exec_place::device(1), handle.read())->*[](auto stream, auto s) {\n    check_val<<<1, 1, 0, stream>>>(s.data_handle(), 18);\n  };\n\n  // REDUX dev1 (18 + 42)\n  ctx.task(exec_place::device(1), handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 42);\n  };\n\n  // READ dev0 (18 + 42)\n  ctx.task(exec_place::device(0), handle.read())->*[](auto stream, auto s) {\n    check_val<<<1, 1, 0, stream>>>(s.data_handle(), 18 + 42);\n  };\n\n  // READ\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    EXPECT(s(0) == 18 + 42);\n    // printf(\"VALUE %d expected %d\\n\", s(0), 18 + 42);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(int* ptr, int value)\n{\n  *ptr = *ptr + value;\n}\n\n__global__ void check_val(const int* ptr, int expected)\n{\n  assert(*ptr == expected);\n}\n\n/*\n * This test ensures that we can reconstruct a piece of data where there are\n * multiple \"shared\" instances, and \"redux\" instances\n */\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"Skipping test: need at least 2 devices.\\n\");\n    return 0;\n  }\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  int a = 17;\n\n  // init op (17)\n  auto handle = ctx.logical_data(make_slice(&a, 1));\n\n  // RW dev0 (18)\n  ctx.task(exec_place::device(0), handle.rw())->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 1);\n  };\n\n  // READ dev1 (18)\n  ctx.task(exec_place::device(1), handle.read())->*[](auto stream, auto s) {\n    check_val<<<1, 1, 0, stream>>>(s.data_handle(), 18);\n  };\n\n  // REDUX dev1 (18 + 42)\n  ctx.task(exec_place::device(1), handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<1, 1, 0, stream>>>(s.data_handle(), 42);\n  };\n\n  // READ dev0 (18 + 42)\n  ctx.task(exec_place::device(0), handle.read())->*[](auto stream, auto s) {\n    check_val<<<1, 1, 0, stream>>>(s.data_handle(), 18 + 42);\n  };\n\n  // READ\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    EXPECT(s(0) == 18 + 42);\n    // printf(\"VALUE %d expected %d\\n\", s(0), 18 + 42);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/redux_test2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void add_val(slice<T> inout, T val)\n{\n  inout(0) += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(cudaGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  int var = 42;\n\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[=](cudaStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var, i);\n      };\n    }\n\n    // host\n    ctx.host_launch(var_handle.relaxed(redux_op))->*[=](auto var) {\n      var(0) += i;\n    };\n  }\n\n  // Check result\n  ctx.host_launch(var_handle.read())->*[&](auto var) {\n    int expected = 42 + (N * (N - 1)) / 2 * (ndevs + 1);\n    EXPECT(var(0) == expected);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\ntemplate <typename T>\n__global__ void add_val(slice<T> inout, T val)\n{\n  inout(0) += val;\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int ndevs;\n  cuda_safe_call(hipGetDeviceCount(&ndevs));\n\n  const int N = 4;\n\n  int var = 42;\n\n  auto var_handle = ctx.logical_data(make_slice(&var, 1));\n  var_handle.set_symbol(\"var\");\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int>>();\n\n  // We add i twice (total = N(N-1) + initial_value)\n  for (int i = 0; i < N; i++)\n  {\n    // device\n    for (int d = 0; d < ndevs; d++)\n    {\n      ctx.task(exec_place::device(d), var_handle.relaxed(redux_op))->*[=](hipStream_t s, auto var) {\n        add_val<int><<<1, 1, 0, s>>>(var, i);\n      };\n    }\n\n    // host\n    ctx.host_launch(var_handle.relaxed(redux_op))->*[=](auto var) {\n      var(0) += i;\n    };\n  }\n\n  // Check result\n  ctx.host_launch(var_handle.read())->*[&](auto var) {\n    int expected = 42 + (N * (N - 1)) / 2 * (ndevs + 1);\n    EXPECT(var(0) == expected);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/sum_multiple_places.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  *inout_addr += *in_addr;\n}\n\n/*\n * Define a SUM reduction operator over a scalar\n */\nclass scalar_sum_t : public stream_reduction_operator_untyped\n{\npublic:\n  scalar_sum_t()\n      : stream_reduction_operator_untyped() {};\n\n  void stream_redux_op(\n    logical_data_untyped& d,\n    const data_place& /*unused*/,\n    instance_id_t inout_instance_id,\n    const data_place& /*unused*/,\n    instance_id_t in_instance_id,\n    const exec_place& /*unused*/,\n    cudaStream_t s) override\n  {\n    auto& in_instance    = d.instance<typename scalar_t::element_type>(in_instance_id);\n    auto& inout_instance = d.instance<typename scalar_t::element_type>(inout_instance_id);\n    add<<<1, 1, 0, s>>>(in_instance.data_handle(), inout_instance.data_handle());\n  }\n\n  void stream_init_op(logical_data_untyped& d,\n                      const data_place& /*unused*/,\n                      instance_id_t out_instance_id,\n                      const exec_place& /*unused*/,\n                      cudaStream_t s) override\n  {\n    auto& out_instance = d.instance<typename scalar_t::element_type>(out_instance_id);\n    // fprintf(stderr, \"REDUX INIT d %p memory node %d instance id %d => addr %p\\n\", d, out_memory_node,\n    //        out_instance_id, *out_instance);\n    set_value<<<1, 1, 0, s>>>(out_instance.data_handle(), 0);\n  }\n};\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 128;\n\n  // We have an array, and a handle for each entry of the array\n  int array[N];\n  logical_data<slice<int>> array_handles[N];\n\n  /*\n   * We are going to compute the sum of this array\n   */\n  for (int i = 0; i < N; i++)\n  {\n    array[i]         = i;\n    array_handles[i] = ctx.logical_data(&array[i], {1});\n    array_handles[i].set_symbol(std::string(\"array[\") + std::to_string(i) + std::string(\"]\"));\n  }\n\n  logical_data<slice<int>> var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  int check_sum = 0;\n  for (int i = 0; i < N; i++)\n  {\n    check_sum += array[i];\n  }\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  for (int i = 0; i < N; i++)\n  {\n    ctx.task(var_handle.relaxed(redux_op), array_handles[i].read())\n        ->*[](cudaStream_t stream, auto d_var, auto d_array_i) {\n              add<<<1, 1, 0, stream>>>(d_array_i.data_handle(), d_var.data_handle());\n            };\n  }\n\n  // Force the reconstruction of data on the device, so that no transfers are\n  // necessary while reconstructing the result.\n  // This will of course not be necessary in the future ...\n  ctx.task(var_handle.read())->*[](cudaStream_t /*unused*/, auto /*unused*/) {};\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[=](cudaStream_t stream, auto h_var) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n    int value = h_var(0);\n    EXPECT(value == check_sum);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/reduction.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\nusing scalar_t = slice_stream_interface<int, 1>;\n\ntemplate <typename T>\n__global__ void set_value(T* addr, T val)\n{\n  *addr = val;\n}\n\ntemplate <typename T>\n__global__ void add(const T* in_addr, T* inout_addr)\n{\n  *inout_addr += *in_addr;\n}\n\n/*\n * Define a SUM reduction operator over a scalar\n */\nclass scalar_sum_t : public stream_reduction_operator_untyped\n{\npublic:\n  scalar_sum_t()\n      : stream_reduction_operator_untyped() {};\n\n  void stream_redux_op(\n    logical_data_untyped& d,\n    const data_place& /*unused*/,\n    instance_id_t inout_instance_id,\n    const data_place& /*unused*/,\n    instance_id_t in_instance_id,\n    const exec_place& /*unused*/,\n    hipStream_t s) override\n  {\n    auto& in_instance    = d.instance<typename scalar_t::element_type>(in_instance_id);\n    auto& inout_instance = d.instance<typename scalar_t::element_type>(inout_instance_id);\n    add<<<1, 1, 0, s>>>(in_instance.data_handle(), inout_instance.data_handle());\n  }\n\n  void stream_init_op(logical_data_untyped& d,\n                      const data_place& /*unused*/,\n                      instance_id_t out_instance_id,\n                      const exec_place& /*unused*/,\n                      hipStream_t s) override\n  {\n    auto& out_instance = d.instance<typename scalar_t::element_type>(out_instance_id);\n    // fprintf(stderr, \"REDUX INIT d %p memory node %d instance id %d => addr %p\\n\", d, out_memory_node,\n    //        out_instance_id, *out_instance);\n    set_value<<<1, 1, 0, s>>>(out_instance.data_handle(), 0);\n  }\n};\n\nint main()\n{\n  stream_ctx ctx;\n\n  const int N = 128;\n\n  // We have an array, and a handle for each entry of the array\n  int array[N];\n  logical_data<slice<int>> array_handles[N];\n\n  /*\n   * We are going to compute the sum of this array\n   */\n  for (int i = 0; i < N; i++)\n  {\n    array[i]         = i;\n    array_handles[i] = ctx.logical_data(&array[i], {1});\n    array_handles[i].set_symbol(std::string(\"array[\") + std::to_string(i) + std::string(\"]\"));\n  }\n\n  logical_data<slice<int>> var_handle = ctx.logical_data(shape_of<slice<int>>(1));\n  var_handle.set_symbol(\"var\");\n\n  int check_sum = 0;\n  for (int i = 0; i < N; i++)\n  {\n    check_sum += array[i];\n  }\n\n  auto redux_op = std::make_shared<scalar_sum_t>();\n\n  for (int i = 0; i < N; i++)\n  {\n    ctx.task(var_handle.relaxed(redux_op), array_handles[i].read())\n        ->*[](hipStream_t stream, auto d_var, auto d_array_i) {\n              add<<<1, 1, 0, stream>>>(d_array_i.data_handle(), d_var.data_handle());\n            };\n  }\n\n  // Force the reconstruction of data on the device, so that no transfers are\n  // necessary while reconstructing the result.\n  // This will of course not be necessary in the future ...\n  ctx.task(var_handle.read())->*[](hipStream_t /*unused*/, auto /*unused*/) {};\n\n  // Check result\n  ctx.task(exec_place::host(), var_handle.read())->*[=](hipStream_t stream, auto h_var) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n    int value = h_var(0);\n    EXPECT(value == check_sum);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/sum_array.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(slice<int, 2> s, int val)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n\n  for (size_t j = 0; j < s.extent(1); j++)\n  {\n    for (size_t i = tid; i < s.extent(0); i += nthreads)\n    {\n      s(i, j) += val;\n    }\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int array[6] = {0, 1, 2, 3, 4, 5};\n\n  // auto handle = ctx.logical_data(slice<int, 2>(&array[0], std::tuple{ 2, 3 }, 2));\n  auto handle = ctx.logical_data(make_slice(&array[0], std::tuple{2, 3}, 2));\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int, 2>>();\n\n  ctx.task(handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<32, 32, 0, stream>>>(s, 42);\n  };\n\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(cudaStreamSynchronize(stream));\n\n    for (size_t j = 0; j < s.extent(1); j++)\n    {\n      for (size_t i = 0; i < s.extent(0); i++)\n      {\n        // fprintf(stderr, \"%d\\t\", s(i, j));\n      }\n      // fprintf(stderr, \"\\n\");\n    }\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/interfaces/slice_reduction_ops.cuh>\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void add(slice<int, 2> s, int val)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n\n  for (size_t j = 0; j < s.extent(1); j++)\n  {\n    for (size_t i = tid; i < s.extent(0); i += nthreads)\n    {\n      s(i, j) += val;\n    }\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  int array[6] = {0, 1, 2, 3, 4, 5};\n\n  // auto handle = ctx.logical_data(slice<int, 2>(&array[0], std::tuple{ 2, 3 }, 2));\n  auto handle = ctx.logical_data(make_slice(&array[0], std::tuple{2, 3}, 2));\n\n  auto redux_op = std::make_shared<slice_reduction_op_sum<int, 2>>();\n\n  ctx.task(handle.relaxed(redux_op))->*[](auto stream, auto s) {\n    add<<<32, 32, 0, stream>>>(s, 42);\n  };\n\n  ctx.task(exec_place::host(), handle.read())->*[](auto stream, auto s) {\n    cuda_safe_call(hipStreamSynchronize(stream));\n\n    for (size_t j = 0; j < s.extent(1); j++)\n    {\n      for (size_t i = 0; i < s.extent(0); i++)\n      {\n        // fprintf(stderr, \"%d\\t\", s(i, j));\n      }\n      // fprintf(stderr, \"\\n\");\n    }\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/reductions/slice2d_reduction.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void swap(slice<double> dst, const slice<double> src)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  size_t n        = dst.size();\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    double tmp = dst(i);\n    dst(i)     = src(i);\n    src(i)     = tmp;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = 1.0;\n    Y[i] = 2.0;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n#ifdef NDEBUG\n  size_t iter_cnt = 10000000;\n#else\n  size_t iter_cnt = 10000;\n  fprintf(stderr, \"Warning: Running with small problem size in debug mode, should use DEBUG=0.\\n\");\n#endif\n\n  if (argc > 1)\n  {\n    iter_cnt = atol(argv[1]);\n  }\n\n  std::chrono::steady_clock::time_point start, stop;\n  start = std::chrono::steady_clock::now();\n  for (size_t iter = 0; iter < iter_cnt; iter++)\n  {\n    ctx.task(lY.rw(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n      ::swap<<<4, 16, 0, s>>>(dY, dX);\n      ::swap<<<4, 16, 0, s>>>(dX, dY);\n    };\n  }\n  stop = std::chrono::steady_clock::now();\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf us per task\\n\", duration.count() * 1000000.0 / (iter_cnt));\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void swap(slice<double> dst, const slice<double> src)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  size_t n        = dst.size();\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    double tmp = dst(i);\n    dst(i)     = src(i);\n    src(i)     = tmp;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = 1.0;\n    Y[i] = 2.0;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n#ifdef NDEBUG\n  size_t iter_cnt = 10000000;\n#else\n  size_t iter_cnt = 10000;\n  fprintf(stderr, \"Warning: Running with small problem size in debug mode, should use DEBUG=0.\\n\");\n#endif\n\n  if (argc > 1)\n  {\n    iter_cnt = atol(argv[1]);\n  }\n\n  std::chrono::steady_clock::time_point start, stop;\n  start = std::chrono::steady_clock::now();\n  for (size_t iter = 0; iter < iter_cnt; iter++)\n  {\n    ctx.task(lY.rw(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n      ::swap<<<4, 16, 0, s>>>(dY, dX);\n      ::swap<<<4, 16, 0, s>>>(dX, dY);\n    };\n  }\n  stop = std::chrono::steady_clock::now();\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf us per task\\n\", duration.count() * 1000000.0 / (iter_cnt));\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/stress/kernel_chain_fused.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void swap_kernel(slice<double> dst, slice<double> src)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  size_t n        = dst.size();\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    double tmp = dst(i);\n    dst(i)     = src(i);\n    src(i)     = tmp;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = 1.0;\n    Y[i] = 2.0;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n#ifdef NDEBUG\n  size_t iter_cnt = 10000000;\n#else\n  size_t iter_cnt = 10000;\n  fprintf(stderr, \"Warning: Running with small problem size in debug mode, should use DEBUG=0.\\n\");\n#endif\n\n  if (argc > 1)\n  {\n    iter_cnt = atol(argv[1]);\n  }\n\n  std::chrono::steady_clock::time_point start, stop;\n  start = std::chrono::steady_clock::now();\n  for (size_t iter = 0; iter < iter_cnt; iter++)\n  {\n    ctx.task(lX.rw(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n      swap_kernel<<<4, 16, 0, s>>>(dY, dX);\n    };\n    ctx.task(lY.rw(), lX.rw())->*[&](cudaStream_t s, auto dY, auto dX) {\n      swap_kernel<<<4, 16, 0, s>>>(dX, dY);\n    };\n  }\n  stop = std::chrono::steady_clock::now();\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf us per task pair\\n\", duration.count() * 1000000.0 / (iter_cnt));\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void swap_kernel(slice<double> dst, slice<double> src)\n{\n  size_t tid      = threadIdx.x + blockIdx.x * blockDim.x;\n  size_t nthreads = blockDim.x * gridDim.x;\n  size_t n        = dst.size();\n\n  for (size_t i = tid; i < n; i += nthreads)\n  {\n    double tmp = dst(i);\n    dst(i)     = src(i);\n    src(i)     = tmp;\n  }\n}\n\nint main(int argc, char** argv)\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  double X[N], Y[N];\n\n  for (size_t i = 0; i < N; i++)\n  {\n    X[i] = 1.0;\n    Y[i] = 2.0;\n  }\n\n  auto lX = ctx.logical_data(X);\n  auto lY = ctx.logical_data(Y);\n\n#ifdef NDEBUG\n  size_t iter_cnt = 10000000;\n#else\n  size_t iter_cnt = 10000;\n  fprintf(stderr, \"Warning: Running with small problem size in debug mode, should use DEBUG=0.\\n\");\n#endif\n\n  if (argc > 1)\n  {\n    iter_cnt = atol(argv[1]);\n  }\n\n  std::chrono::steady_clock::time_point start, stop;\n  start = std::chrono::steady_clock::now();\n  for (size_t iter = 0; iter < iter_cnt; iter++)\n  {\n    ctx.task(lX.rw(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n      swap_kernel<<<4, 16, 0, s>>>(dY, dX);\n    };\n    ctx.task(lY.rw(), lX.rw())->*[&](hipStream_t s, auto dY, auto dX) {\n      swap_kernel<<<4, 16, 0, s>>>(dX, dY);\n    };\n  }\n  stop = std::chrono::steady_clock::now();\n  ctx.finalize();\n\n  std::chrono::duration<double> duration = stop - start;\n  fprintf(stderr, \"Elapsed: %.2lf us per task pair\\n\", duration.count() * 1000000.0 / (iter_cnt));\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/stress/kernel_chain.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data<double>(N);\n  auto lY = ctx.logical_data<double>(N);\n\n  ctx.task(lX.write())->*[](cudaStream_t, auto) {};\n\n  ctx.task(lY.write())->*[](cudaStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data<double>(N);\n  auto lY = ctx.logical_data<double>(N);\n\n  ctx.task(lX.write())->*[](hipStream_t, auto) {};\n\n  ctx.task(lY.write())->*[](hipStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/threads/axpy-threads.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/, logical_data<slice<double>> lX)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lY = ctx.logical_data<double>(N);\n  ctx.task(lY.write())->*[](cudaStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  auto lX        = ctx.logical_data<double>(N);\n  ctx.task(lX.write())->*[](cudaStream_t, auto) {};\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i, lX);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.task(lX.rw())->*[&](cudaStream_t s, auto) {\n    cuda_sleep(100.0, s);\n  };\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/, logical_data<slice<double>> lX)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lY = ctx.logical_data<double>(N);\n  ctx.task(lY.write())->*[](hipStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n  const size_t N = 16;\n  auto lX        = ctx.logical_data<double>(N);\n  ctx.task(lX.write())->*[](hipStream_t, auto) {};\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i, lX);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.task(lX.rw())->*[&](hipStream_t s, auto) {\n    cuda_sleep(100.0, s);\n  };\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/threads/axpy-threads-2.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data<double>(N);\n  auto lY = ctx.logical_data<double>(N);\n\n  ctx.parallel_for(lX.shape(), lX.write())->*[] __device__(size_t i, auto x) {\n    x(i) = 1.0;\n  };\n\n  ctx.task(lY.write())->*[](cudaStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](cudaStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief An AXPY kernel implemented with a task of the CUDA stream backend\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/stream/stream_ctx.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\n__global__ void axpy(double a, slice<const double> x, slice<double> y)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\ndouble X0(int i)\n{\n  return sin((double) i);\n}\n\ndouble Y0(int i)\n{\n  return cos((double) i);\n}\n\nvoid mytask(stream_ctx ctx, int /*id*/)\n{\n  // std::cout << \"Thread \" << id << \" is executing.\\n\";\n\n  const size_t N = 16;\n\n  double alpha = 3.14;\n\n  auto lX = ctx.logical_data<double>(N);\n  auto lY = ctx.logical_data<double>(N);\n\n  ctx.parallel_for(lX.shape(), lX.write())->*[] __device__(size_t i, auto x) {\n    x(i) = 1.0;\n  };\n\n  ctx.task(lY.write())->*[](hipStream_t, auto) {};\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 10; i++)\n  {\n    ctx.task(lX.read(), lY.rw())->*[&](hipStream_t s, auto dX, auto dY) {\n      axpy<<<16, 128, 0, s>>>(alpha, dX, dY);\n      cuda_sleep(100.0, s);\n    };\n  }\n}\n\nint main()\n{\n  stream_ctx ctx;\n\n  std::vector<std::thread> threads;\n  // Launch 8 threads.\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/threads/axpy-threads-pfor.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure a graph_ctx can be used concurrently\n *\n */\n\n#include <cuda/experimental/stf.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(slice<int> y, slice<const int> x, int a)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\nvoid mytask(graph_ctx ctx, int /*id*/)\n{\n  const size_t N = 16;\n\n  int alpha = 3;\n\n  auto lX = ctx.logical_data<int>(N);\n  auto lY = ctx.logical_data<int>(N);\n\n  ctx.parallel_for(lX.shape(), lX.write(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n    x(i) = (1 + i);\n    y(i) = (2 + i * i);\n  };\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 200; i++)\n  {\n    ctx.task(lY.rw(), lX.read())->*[alpha](cudaStream_t stream, auto dY, auto dX) {\n      axpy<<<128, 64, 0, stream>>>(dY, dX, alpha);\n    };\n  }\n\n  ctx.host_launch(lX.read(), lY.read())->*[alpha](auto x, auto y) {\n    for (size_t i = 0; i < N; i++)\n    {\n      EXPECT(x(i) == 1 + i);\n      EXPECT(y(i) == 2 + i * i + 200 * alpha * x(i));\n    }\n  };\n}\n\nint main()\n{\n  graph_ctx ctx;\n\n  ::std::vector<::std::thread> threads;\n  // Launch threads\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Ensure a graph_ctx can be used concurrently\n *\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <mutex>\n#include <thread>\n\nusing namespace cuda::experimental::stf;\n\n__global__ void axpy(slice<int> y, slice<const int> x, int a)\n{\n  int tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  int nthreads = gridDim.x * blockDim.x;\n\n  for (int i = tid; i < x.size(); i += nthreads)\n  {\n    y(i) += a * x(i);\n  }\n}\n\nvoid mytask(graph_ctx ctx, int /*id*/)\n{\n  const size_t N = 16;\n\n  int alpha = 3;\n\n  auto lX = ctx.logical_data<int>(N);\n  auto lY = ctx.logical_data<int>(N);\n\n  ctx.parallel_for(lX.shape(), lX.write(), lY.write())->*[] __device__(size_t i, auto x, auto y) {\n    x(i) = (1 + i);\n    y(i) = (2 + i * i);\n  };\n\n  /* Compute Y = Y + alpha X */\n  for (size_t i = 0; i < 200; i++)\n  {\n    ctx.task(lY.rw(), lX.read())->*[alpha](hipStream_t stream, auto dY, auto dX) {\n      axpy<<<128, 64, 0, stream>>>(dY, dX, alpha);\n    };\n  }\n\n  ctx.host_launch(lX.read(), lY.read())->*[alpha](auto x, auto y) {\n    for (size_t i = 0; i < N; i++)\n    {\n      EXPECT(x(i) == 1 + i);\n      EXPECT(y(i) == 2 + i * i + 200 * alpha * x(i));\n    }\n  };\n}\n\nint main()\n{\n  graph_ctx ctx;\n\n  ::std::vector<::std::thread> threads;\n  // Launch threads\n  for (int i = 0; i < 10; ++i)\n  {\n    threads.emplace_back(mytask, ctx, i);\n  }\n\n  // Wait for all threads to complete.\n  for (auto& th : threads)\n  {\n    th.join();\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/threads/axpy-threads-graph-capture.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This shows how we can asynchronously compose a sequence of operations\n *        described with an IR\n */\n\n#include <cuda/experimental/stf.cuh>\n\n#include <fstream>\n#include <iostream>\n#include <map>\n\nusing namespace cuda::experimental::stf;\n\nusing logical_slice = logical_data<slice<double>>;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, cudaStream_t stream)\n{\n  int device;\n  cudaGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\nconst double sleep_time = 1.0;\n\n// z = LITERAL(length, value)\ntemplate <typename Ctx>\nlogical_slice LITERAL(Ctx& ctx, size_t n, int, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(n));\n  z.set_symbol(out_symbol);\n\n  ctx.task(z.write()).set_symbol(\"LITERAL\")->*[](cudaStream_t stream, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// z = OR(x,y)\ntemplate <typename Ctx>\nlogical_slice OR(Ctx& ctx, logical_slice x, logical_slice y, std::string out_symbol = \"undefined\")\n{\n  assert(x.shape().size() == y.shape().size());\n\n  auto z = ctx.logical_data(x.shape());\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.read(), z.write()).set_symbol(\"OR\")->*\n    [](cudaStream_t stream, auto /*unused*/, auto /*unused*/, auto /*unused*/) {\n      cuda_sleep(sleep_time, stream);\n    };\n\n  return z;\n}\n\n// z = AND(x,y)\ntemplate <typename Ctx>\nlogical_slice AND(Ctx& ctx, logical_slice x, logical_slice y, std::string out_symbol = \"undefined\")\n{\n  assert(x.shape().size() == y.shape().size());\n\n  auto z = ctx.logical_data(x.shape());\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.read(), z.write()).set_symbol(\"AND\")->*\n    [](cudaStream_t stream, auto /*unused*/, auto /*unused*/, auto /*unused*/) {\n      cuda_sleep(sleep_time, stream);\n    };\n\n  return z;\n}\n\ntemplate <typename Ctx>\nlogical_slice\nARRAY_INDEX(Ctx& ctx, logical_slice x, logical_slice /*unused*/, size_t sz, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(sz));\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), z.write()).set_symbol(\"ARRAY INDEX\")->*[](cudaStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// z = BIT_SLICE(x, position, size)\ntemplate <typename Ctx>\nlogical_slice BIT_SLICE(Ctx& ctx, logical_slice x, size_t /*unused*/, size_t sz, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(sz));\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), z.write()).set_symbol(\"BIT SLICE\")->*[](cudaStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// y = NOT(x)\ntemplate <typename Ctx>\nlogical_slice NOT(Ctx& ctx, logical_slice x, std::string out_symbol = \"undefined\")\n{\n  auto y = ctx.logical_data(x.shape());\n  y.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.write()).set_symbol(\"NOT\")->*[](cudaStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return y;\n}\n\n// y = CONCAT(sz, vector<> inputs)\ntemplate <typename Ctx>\nlogical_slice CONCAT(Ctx& ctx, size_t sz, std::vector<logical_slice> inputs, std::string out_symbol = \"undefined\")\n{\n  auto y = ctx.logical_data(shape_of<slice<double>>(sz));\n  y.set_symbol(out_symbol);\n\n  auto t = ctx.task();\n  t.add_deps(y.write());\n  t.set_symbol(\"CONCAT\");\n  for (auto& input : inputs)\n  {\n    t.add_deps(input.read());\n  }\n\n  t->*[](cudaStream_t stream) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return y;\n}\n\ntemplate <typename Ctx>\nvoid run(const char* inputfile)\n{\n  // Find the handle from its symbol\n  std::map<std::string, logical_slice> logical_slices;\n  std::string output_data_symbol;\n  std::ifstream read(inputfile);\n  Ctx ctx;\n\n  // Indicates if we are parsing the body of the circuit\n  bool in_body = false;\n\n  for (std::string line; std::getline(read, line);)\n  {\n    std::stringstream ss(line);\n\n    //        std::cout << \"LINE : \" << line << '\\n';\n    if (!in_body)\n    {\n      std::string token;\n      ss >> token;\n      //            std::cout << \"TOKEN : \" << token << '\\n';\n\n      if (token == \"fn\")\n      {\n        // We are parsing the declaration of the function, this starts the body\n        // std::cout << \"GOT DECLARATION \" << line << '\\n';\n        in_body = true;\n\n        // Look for parameters\n        size_t begin_params, end_params;\n        begin_params       = line.find('(');\n        end_params         = line.find(')');\n        std::string params = line.substr(begin_params + 1, end_params - begin_params - 1);\n\n        // std::cout << \"PARAMS = \" << params << '\\n';\n\n        // Parse parameters which are separated by a comma, format = \"symbol: type\"\n        while (true)\n        {\n          // Find symbol\n          size_t pos;\n          pos                = params.find(\":\");\n          std::string symbol = params.substr(0, pos);\n          // std::cout << symbol << '\\n';\n\n          // We create a dummy allocation so that the data handles refers to actually allocated host memory\n          double* dummy     = new double[1];\n          auto param_handle = ctx.logical_data(make_slice(dummy, 1));\n\n          param_handle.set_symbol(symbol);\n          logical_slices[symbol] = param_handle;\n\n          pos = params.find(\", \");\n          if (pos == std::string::npos)\n          {\n            break;\n          }\n\n          params.erase(0, pos + 2);\n        }\n      }\n    }\n    else\n    {\n      std::string token;\n      ss >> token;\n      // std::cout << \"TOKEN : \" << token << '\\n';\n\n      if (token == \"}\")\n      {\n        // This closes the body\n        in_body = false;\n        continue;\n      }\n\n      // We expect lines of the format : \"  symbol: type = gate_name(..., id=VALUE)\"\n      size_t end_symbol = line.find(\":\");\n\n      // We look for the first \"= \" to find the gate name\n      size_t gate_symbol_pos = line.find(\"= \");\n      std::string gate       = line.substr(gate_symbol_pos + 2);\n\n      size_t gate_name_end           = gate.find(\"(\");\n      std::string gate_symbol        = gate.substr(0, gate_name_end);\n      std::string gate_args          = gate.substr(gate_name_end + 1, gate.size() - gate_name_end - 2);\n      std::string gate_outvar_symbol = line.substr(2, end_symbol - 2);\n\n      // Possibly remove the \"ret\" out of the gate_outvar_symbol\n      size_t ret_pos = gate_outvar_symbol.find(\"ret \");\n      if (ret_pos != std::string::npos)\n      {\n        // This is our result !\n        gate_outvar_symbol.erase(4);\n\n        output_data_symbol = gate_outvar_symbol;\n      }\n\n      // std::cout << \"GATE OUT SYMBOL \" << gate_outvar_symbol << '\\n';\n      // std::cout << \"GATE DESCRIPTION : \" << gate << '\\n';\n      // std::cout << \"GATE SYMBOL \" << gate_symbol << '\\n';\n      // std::cout << \"GATE ARGS \" << gate_args << '\\n';\n\n      // We now dispatch between the different gates\n      if (gate_symbol == \"literal\")\n      {\n        //  literal.916: bits[1] = literal(value=1, id=916)\n        int value = 42; // TODO parse\n        size_t sz = 1;\n\n        logical_slices[gate_outvar_symbol] = LITERAL(ctx, sz, value, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"or\")\n      {\n        //  or.1268: bits[1] = or(or.1267, and.1236, id=1268)\n        size_t pos;\n        pos                     = gate_args.find(\", \");\n        std::string symbol_left = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                      = gate_args.find(\", \");\n        std::string symbol_right = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        // std::cout << \"OR GATE on symbols\" << symbol_left << \" AND \" << symbol_right << '\\n';\n\n        auto data_left                     = logical_slices[symbol_left];\n        auto data_right                    = logical_slices[symbol_right];\n        logical_slices[gate_outvar_symbol] = OR(ctx, data_left, data_right, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"and\")\n      {\n        size_t pos;\n        pos                     = gate_args.find(\", \");\n        std::string symbol_left = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                      = gate_args.find(\", \");\n        std::string symbol_right = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        auto data_left                     = logical_slices[symbol_left];\n        auto data_right                    = logical_slices[symbol_right];\n        logical_slices[gate_outvar_symbol] = AND(ctx, data_left, data_right, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"bit_slice\")\n      {\n        // bit_slice.936: bits[1] = bit_slice(y, start=15, width=1, id=936)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        // hardcoded ...\n        size_t sz = 1;\n\n        auto data_in                       = logical_slices[symbol_in];\n        logical_slices[gate_outvar_symbol] = BIT_SLICE(ctx, data_in, 42, sz, gate_outvar_symbol);\n\n        // std::cout << \"PRODUCED DATA FOR \" << gate_outvar_symbol << '\\n';\n\n        continue;\n      }\n\n      if (gate_symbol == \"array_index\")\n      {\n        // array_index.2804: bits[8] = array_index(window, indices=[literal.2803], id=2804)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                        = gate_args.find(\", \");\n        std::string symbol_indices = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        size_t pos_beg          = symbol_indices.find(\"[\");\n        size_t pos_end          = symbol_indices.find(\"]\");\n        std::string symbol_in_2 = symbol_indices.substr(pos_beg + 1, pos_end - pos_beg - 1);\n        // std::cout << \"ARRAY INDEX ... INDEX = \" << symbol_in_2 << '\\n';\n\n        // hardcoded ...\n        size_t sz = 1;\n\n        auto data_in                       = logical_slices[symbol_in];\n        auto data_in_2                     = logical_slices[symbol_in_2];\n        logical_slices[gate_outvar_symbol] = ARRAY_INDEX(ctx, data_in, data_in_2, sz, gate_outvar_symbol);\n\n        // std::cout << \"PRODUCED DATA FOR \" << gate_outvar_symbol << '\\n';\n        continue;\n      }\n\n      if (gate_symbol == \"not\")\n      {\n        // not.953: bits[1] = not(bit_slice.936, id=953)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        auto data_in                       = logical_slices[symbol_in];\n        logical_slices[gate_outvar_symbol] = NOT(ctx, data_in, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"concat\")\n      {\n        //   ret concat.1269: bits[16] = concat(or.1238, or.1240, or.1242, or.1244, or.1246, or.1248, or.1250,\n        //   or.1252, or.1254, or.1256, or.1258, or.1260, or.1262, or.1264, or.1266, or.1268, id=1269)\n        // Remove the end \", id =..\"\n        size_t id_pos = gate_args.find(\", id=\");\n        gate_args     = gate_args.substr(0, id_pos);\n\n        std::vector<logical_slice> inputs;\n        size_t pos;\n\n        while (true)\n        {\n          pos = gate_args.find(\", \");\n          if (pos == std::string::npos)\n          {\n            break;\n          }\n\n          std::string symbol = gate_args.substr(0, pos);\n          inputs.push_back(logical_slices[symbol]);\n          gate_args.erase(0, pos + 2);\n\n          // std::cout << \"CONCAT ARG = \" << symbol << '\\n';\n        }\n\n        size_t sz                          = 1;\n        logical_slices[gate_outvar_symbol] = CONCAT(ctx, sz, inputs, gate_outvar_symbol);\n        continue;\n      }\n\n      std::cout << \"UNRECOGNIZED GATE !\" << '\\n';\n      abort();\n    }\n  }\n\n  auto output_data = logical_slices[output_data_symbol];\n\n  ctx.finalize();\n}\n\nint main(int argc, char** argv)\n{\n  /* Until we find a simple and \"safe\" way to pass a file to the test, we\n   * consider this is not an error ...\n   * One possible approach would be to convert a default .ir file to a large\n   * static data processed in the test suite ?\n   */\n  if (argc < 2)\n  {\n    fprintf(stderr, \"This test needs an input file, skipping.\\n\");\n    return 0;\n  }\n  run<stream_ctx>(argv[1]);\n  run<graph_ctx>(argv[1]);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief This shows how we can asynchronously compose a sequence of operations\n *        described with an IR\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/stf.cuh>\n\n#include <fstream>\n#include <iostream>\n#include <map>\n\nusing namespace cuda::experimental::stf;\n\nusing logical_slice = logical_data<slice<double>>;\n\nstatic __global__ void cuda_sleep_kernel(long long int clock_cnt)\n{\n  long long int start_clock  = clock64();\n  long long int clock_offset = 0;\n  while (clock_offset < clock_cnt)\n  {\n    clock_offset = clock64() - start_clock;\n  }\n}\n\nvoid cuda_sleep(double ms, hipStream_t stream)\n{\n  int device;\n  hipGetDevice(&device);\n\n  // cudaDevAttrClockRate: Peak clock frequency in kilohertz;\n  int clock_rate;\n  hipDeviceGetAttribute(&clock_rate, hipDeviceAttributeClockRate, device);\n\n  long long int clock_cnt = (long long int) (ms * clock_rate);\n  cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);\n}\n\nconst double sleep_time = 1.0;\n\n// z = LITERAL(length, value)\ntemplate <typename Ctx>\nlogical_slice LITERAL(Ctx& ctx, size_t n, int, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(n));\n  z.set_symbol(out_symbol);\n\n  ctx.task(z.write()).set_symbol(\"LITERAL\")->*[](hipStream_t stream, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// z = OR(x,y)\ntemplate <typename Ctx>\nlogical_slice OR(Ctx& ctx, logical_slice x, logical_slice y, std::string out_symbol = \"undefined\")\n{\n  assert(x.shape().size() == y.shape().size());\n\n  auto z = ctx.logical_data(x.shape());\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.read(), z.write()).set_symbol(\"OR\")->*\n    [](hipStream_t stream, auto /*unused*/, auto /*unused*/, auto /*unused*/) {\n      cuda_sleep(sleep_time, stream);\n    };\n\n  return z;\n}\n\n// z = AND(x,y)\ntemplate <typename Ctx>\nlogical_slice AND(Ctx& ctx, logical_slice x, logical_slice y, std::string out_symbol = \"undefined\")\n{\n  assert(x.shape().size() == y.shape().size());\n\n  auto z = ctx.logical_data(x.shape());\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.read(), z.write()).set_symbol(\"AND\")->*\n    [](hipStream_t stream, auto /*unused*/, auto /*unused*/, auto /*unused*/) {\n      cuda_sleep(sleep_time, stream);\n    };\n\n  return z;\n}\n\ntemplate <typename Ctx>\nlogical_slice\nARRAY_INDEX(Ctx& ctx, logical_slice x, logical_slice /*unused*/, size_t sz, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(sz));\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), z.write()).set_symbol(\"ARRAY INDEX\")->*[](hipStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// z = BIT_SLICE(x, position, size)\ntemplate <typename Ctx>\nlogical_slice BIT_SLICE(Ctx& ctx, logical_slice x, size_t /*unused*/, size_t sz, std::string out_symbol = \"undefined\")\n{\n  auto z = ctx.logical_data(shape_of<slice<double>>(sz));\n  z.set_symbol(out_symbol);\n\n  ctx.task(x.read(), z.write()).set_symbol(\"BIT SLICE\")->*[](hipStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return z;\n}\n\n// y = NOT(x)\ntemplate <typename Ctx>\nlogical_slice NOT(Ctx& ctx, logical_slice x, std::string out_symbol = \"undefined\")\n{\n  auto y = ctx.logical_data(x.shape());\n  y.set_symbol(out_symbol);\n\n  ctx.task(x.read(), y.write()).set_symbol(\"NOT\")->*[](hipStream_t stream, auto /*unused*/, auto /*unused*/) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return y;\n}\n\n// y = CONCAT(sz, vector<> inputs)\ntemplate <typename Ctx>\nlogical_slice CONCAT(Ctx& ctx, size_t sz, std::vector<logical_slice> inputs, std::string out_symbol = \"undefined\")\n{\n  auto y = ctx.logical_data(shape_of<slice<double>>(sz));\n  y.set_symbol(out_symbol);\n\n  auto t = ctx.task();\n  t.add_deps(y.write());\n  t.set_symbol(\"CONCAT\");\n  for (auto& input : inputs)\n  {\n    t.add_deps(input.read());\n  }\n\n  t->*[](hipStream_t stream) {\n    cuda_sleep(sleep_time, stream);\n  };\n\n  return y;\n}\n\ntemplate <typename Ctx>\nvoid run(const char* inputfile)\n{\n  // Find the handle from its symbol\n  std::map<std::string, logical_slice> logical_slices;\n  std::string output_data_symbol;\n  std::ifstream read(inputfile);\n  Ctx ctx;\n\n  // Indicates if we are parsing the body of the circuit\n  bool in_body = false;\n\n  for (std::string line; std::getline(read, line);)\n  {\n    std::stringstream ss(line);\n\n    //        std::cout << \"LINE : \" << line << '\\n';\n    if (!in_body)\n    {\n      std::string token;\n      ss >> token;\n      //            std::cout << \"TOKEN : \" << token << '\\n';\n\n      if (token == \"fn\")\n      {\n        // We are parsing the declaration of the function, this starts the body\n        // std::cout << \"GOT DECLARATION \" << line << '\\n';\n        in_body = true;\n\n        // Look for parameters\n        size_t begin_params, end_params;\n        begin_params       = line.find('(');\n        end_params         = line.find(')');\n        std::string params = line.substr(begin_params + 1, end_params - begin_params - 1);\n\n        // std::cout << \"PARAMS = \" << params << '\\n';\n\n        // Parse parameters which are separated by a comma, format = \"symbol: type\"\n        while (true)\n        {\n          // Find symbol\n          size_t pos;\n          pos                = params.find(\":\");\n          std::string symbol = params.substr(0, pos);\n          // std::cout << symbol << '\\n';\n\n          // We create a dummy allocation so that the data handles refers to actually allocated host memory\n          double* dummy     = new double[1];\n          auto param_handle = ctx.logical_data(make_slice(dummy, 1));\n\n          param_handle.set_symbol(symbol);\n          logical_slices[symbol] = param_handle;\n\n          pos = params.find(\", \");\n          if (pos == std::string::npos)\n          {\n            break;\n          }\n\n          params.erase(0, pos + 2);\n        }\n      }\n    }\n    else\n    {\n      std::string token;\n      ss >> token;\n      // std::cout << \"TOKEN : \" << token << '\\n';\n\n      if (token == \"}\")\n      {\n        // This closes the body\n        in_body = false;\n        continue;\n      }\n\n      // We expect lines of the format : \"  symbol: type = gate_name(..., id=VALUE)\"\n      size_t end_symbol = line.find(\":\");\n\n      // We look for the first \"= \" to find the gate name\n      size_t gate_symbol_pos = line.find(\"= \");\n      std::string gate       = line.substr(gate_symbol_pos + 2);\n\n      size_t gate_name_end           = gate.find(\"(\");\n      std::string gate_symbol        = gate.substr(0, gate_name_end);\n      std::string gate_args          = gate.substr(gate_name_end + 1, gate.size() - gate_name_end - 2);\n      std::string gate_outvar_symbol = line.substr(2, end_symbol - 2);\n\n      // Possibly remove the \"ret\" out of the gate_outvar_symbol\n      size_t ret_pos = gate_outvar_symbol.find(\"ret \");\n      if (ret_pos != std::string::npos)\n      {\n        // This is our result !\n        gate_outvar_symbol.erase(4);\n\n        output_data_symbol = gate_outvar_symbol;\n      }\n\n      // std::cout << \"GATE OUT SYMBOL \" << gate_outvar_symbol << '\\n';\n      // std::cout << \"GATE DESCRIPTION : \" << gate << '\\n';\n      // std::cout << \"GATE SYMBOL \" << gate_symbol << '\\n';\n      // std::cout << \"GATE ARGS \" << gate_args << '\\n';\n\n      // We now dispatch between the different gates\n      if (gate_symbol == \"literal\")\n      {\n        //  literal.916: bits[1] = literal(value=1, id=916)\n        int value = 42; // TODO parse\n        size_t sz = 1;\n\n        logical_slices[gate_outvar_symbol] = LITERAL(ctx, sz, value, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"or\")\n      {\n        //  or.1268: bits[1] = or(or.1267, and.1236, id=1268)\n        size_t pos;\n        pos                     = gate_args.find(\", \");\n        std::string symbol_left = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                      = gate_args.find(\", \");\n        std::string symbol_right = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        // std::cout << \"OR GATE on symbols\" << symbol_left << \" AND \" << symbol_right << '\\n';\n\n        auto data_left                     = logical_slices[symbol_left];\n        auto data_right                    = logical_slices[symbol_right];\n        logical_slices[gate_outvar_symbol] = OR(ctx, data_left, data_right, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"and\")\n      {\n        size_t pos;\n        pos                     = gate_args.find(\", \");\n        std::string symbol_left = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                      = gate_args.find(\", \");\n        std::string symbol_right = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        auto data_left                     = logical_slices[symbol_left];\n        auto data_right                    = logical_slices[symbol_right];\n        logical_slices[gate_outvar_symbol] = AND(ctx, data_left, data_right, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"bit_slice\")\n      {\n        // bit_slice.936: bits[1] = bit_slice(y, start=15, width=1, id=936)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        // hardcoded ...\n        size_t sz = 1;\n\n        auto data_in                       = logical_slices[symbol_in];\n        logical_slices[gate_outvar_symbol] = BIT_SLICE(ctx, data_in, 42, sz, gate_outvar_symbol);\n\n        // std::cout << \"PRODUCED DATA FOR \" << gate_outvar_symbol << '\\n';\n\n        continue;\n      }\n\n      if (gate_symbol == \"array_index\")\n      {\n        // array_index.2804: bits[8] = array_index(window, indices=[literal.2803], id=2804)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        pos                        = gate_args.find(\", \");\n        std::string symbol_indices = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        size_t pos_beg          = symbol_indices.find(\"[\");\n        size_t pos_end          = symbol_indices.find(\"]\");\n        std::string symbol_in_2 = symbol_indices.substr(pos_beg + 1, pos_end - pos_beg - 1);\n        // std::cout << \"ARRAY INDEX ... INDEX = \" << symbol_in_2 << '\\n';\n\n        // hardcoded ...\n        size_t sz = 1;\n\n        auto data_in                       = logical_slices[symbol_in];\n        auto data_in_2                     = logical_slices[symbol_in_2];\n        logical_slices[gate_outvar_symbol] = ARRAY_INDEX(ctx, data_in, data_in_2, sz, gate_outvar_symbol);\n\n        // std::cout << \"PRODUCED DATA FOR \" << gate_outvar_symbol << '\\n';\n        continue;\n      }\n\n      if (gate_symbol == \"not\")\n      {\n        // not.953: bits[1] = not(bit_slice.936, id=953)\n        size_t pos;\n        pos                   = gate_args.find(\", \");\n        std::string symbol_in = gate_args.substr(0, pos);\n        gate_args.erase(0, pos + 2);\n\n        auto data_in                       = logical_slices[symbol_in];\n        logical_slices[gate_outvar_symbol] = NOT(ctx, data_in, gate_outvar_symbol);\n\n        continue;\n      }\n\n      if (gate_symbol == \"concat\")\n      {\n        //   ret concat.1269: bits[16] = concat(or.1238, or.1240, or.1242, or.1244, or.1246, or.1248, or.1250,\n        //   or.1252, or.1254, or.1256, or.1258, or.1260, or.1262, or.1264, or.1266, or.1268, id=1269)\n        // Remove the end \", id =..\"\n        size_t id_pos = gate_args.find(\", id=\");\n        gate_args     = gate_args.substr(0, id_pos);\n\n        std::vector<logical_slice> inputs;\n        size_t pos;\n\n        while (true)\n        {\n          pos = gate_args.find(\", \");\n          if (pos == std::string::npos)\n          {\n            break;\n          }\n\n          std::string symbol = gate_args.substr(0, pos);\n          inputs.push_back(logical_slices[symbol]);\n          gate_args.erase(0, pos + 2);\n\n          // std::cout << \"CONCAT ARG = \" << symbol << '\\n';\n        }\n\n        size_t sz                          = 1;\n        logical_slices[gate_outvar_symbol] = CONCAT(ctx, sz, inputs, gate_outvar_symbol);\n        continue;\n      }\n\n      std::cout << \"UNRECOGNIZED GATE !\" << '\\n';\n      abort();\n    }\n  }\n\n  auto output_data = logical_slices[output_data_symbol];\n\n  ctx.finalize();\n}\n\nint main(int argc, char** argv)\n{\n  /* Until we find a simple and \"safe\" way to pass a file to the test, we\n   * consider this is not an error ...\n   * One possible approach would be to convert a default .ir file to a large\n   * static data processed in the test suite ?\n   */\n  if (argc < 2)\n  {\n    fprintf(stderr, \"This test needs an input file, skipping.\\n\");\n    return 0;\n  }\n  run<stream_ctx>(argv[1]);\n  run<graph_ctx>(argv[1]);\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/fhe/parse_arctyrex.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\nstatic graph_ctx ctx;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(size_t beg, size_t end, size_t ghost_size)\n      : ghost_size(ghost_size)\n      , block_size(end - beg + 1)\n      , array(new T[block_size + 2 * ghost_size])\n      , left_interface(new T[ghost_size])\n      , right_interface(new T[ghost_size])\n      , handle(ctx.logical_data(array.get(), block_size + 2 * ghost_size))\n      , left_handle(ctx.logical_data(left_interface.get(), ghost_size))\n      , right_handle(ctx.logical_data(right_interface.get(), ghost_size))\n  {}\n\n  T* get_array_in_task()\n  {\n    return handle.instance().data_handle();\n  }\n\n  T* get_array()\n  {\n    return array.get();\n  }\n\npublic:\n  size_t ghost_size;\n  size_t block_size;\n  std::unique_ptr<T[]> array;\n  std::unique_ptr<T[]> left_interface;\n  std::unique_ptr<T[]> right_interface;\n\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n\n  // A piece of data to store the left part of the block\n  logical_data<slice<T>> left_handle;\n\n  // A piece of data to store the right part of the block\n  logical_data<slice<T>> right_handle;\n};\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    int idx2    = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid stencil(data_block<T>& bn, data_block<T>& bn1)\n{\n  ctx.task(bn.handle.rw(), bn1.handle.read())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n            stencil_kernel<<<256, 64, 0, stream>>>(bs, gs, s1.data_handle(), s2.data_handle());\n          };\n}\n\ntemplate <typename T>\nvoid update_inner_interfaces(data_block<T>& bn)\n{\n  // LEFT\n  ctx.task(bn.handle.read(), bn.left_handle.rw())->*[gs = bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n    copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s2.data_handle(), s1.data_handle() + gs);\n  };\n\n  // RIGHT\n  ctx.task(bn.handle.read(), bn.right_handle.rw())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s2.data_handle(), s1.data_handle() + bs);\n          };\n}\n\ntemplate <typename T>\nvoid update_outer_interfaces(data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  ctx.task(bn.handle.rw(), left.right_handle.read())->*[gs = bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n    copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s1.data_handle(), s2.data_handle());\n  };\n  ctx.task(bn.handle.rw(), right.left_handle.read())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s1.data_handle() + gs + bs, s2.data_handle());\n          };\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid copy_array(data_block<T>& bn, data_block<T>& bn1)\n{\n  ctx.task(bn1.handle.rw(), bn.handle.read())\n      ->*[sz = bn.block_size + 2 * bn.ghost_size](cudaStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<256, 64, 0, stream>>>(sz, s1.data_handle(), s2.data_handle());\n          };\n}\n\nint main(int argc, char** argv)\n{\n  size_t NBLOCKS    = 2;\n  size_t BLOCK_SIZE = 1024 * 64;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  std::vector<double> U0(NBLOCKS * BLOCK_SIZE);\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    int beg = b * BLOCK_SIZE;\n    int end = (b + 1) * BLOCK_SIZE;\n\n    Un.push_back(data_block<double>(beg, end, 1));\n    Un1.push_back(data_block<double>(beg, end, 1));\n  }\n\n  // Fill blocks with initial values\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    // int end = (b+1)*BLOCK_SIZE;\n\n    double* Un_vals  = Un[b].get_array();\n    double* Un1_vals = Un1[b].get_array();\n    // Attention, unusual loop: index goes all through BLOCK_SIZE inclusive.\n    for (size_t local_idx = 0; local_idx <= BLOCK_SIZE; local_idx++)\n    {\n      Un_vals[local_idx]  = U0[(beg + local_idx - 1) % TOTAL_SIZE];\n      Un1_vals[local_idx] = U0[(beg + local_idx - 1) % TOTAL_SIZE];\n    }\n  }\n\n  // Create the graph - it starts out empty\n\n  int NITER = 400;\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(Un[b], Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Update the internal copies of the left and right boundaries\n      update_inner_interfaces(Un[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      update_outer_interfaces(Un[b], Un[(b - 1 + NBLOCKS) % NBLOCKS], Un[(b + 1) % NBLOCKS]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      copy_array(Un[b], Un1[b]);\n    }\n  }\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__stf/graph/graph_ctx.cuh>\n\n#include <iostream>\n\nusing namespace cuda::experimental::stf;\n\nstatic graph_ctx ctx;\n\n/*\n * DATA BLOCKS\n *   | GHOSTS |\u00a0DATA |\u00a0GHOSTS |\n */\ntemplate <typename T>\nclass data_block\n{\npublic:\n  data_block(size_t beg, size_t end, size_t ghost_size)\n      : ghost_size(ghost_size)\n      , block_size(end - beg + 1)\n      , array(new T[block_size + 2 * ghost_size])\n      , left_interface(new T[ghost_size])\n      , right_interface(new T[ghost_size])\n      , handle(ctx.logical_data(array.get(), block_size + 2 * ghost_size))\n      , left_handle(ctx.logical_data(left_interface.get(), ghost_size))\n      , right_handle(ctx.logical_data(right_interface.get(), ghost_size))\n  {}\n\n  T* get_array_in_task()\n  {\n    return handle.instance().data_handle();\n  }\n\n  T* get_array()\n  {\n    return array.get();\n  }\n\npublic:\n  size_t ghost_size;\n  size_t block_size;\n  std::unique_ptr<T[]> array;\n  std::unique_ptr<T[]> left_interface;\n  std::unique_ptr<T[]> right_interface;\n\n  // HANDLE = whole data + boundaries\n  logical_data<slice<T>> handle;\n\n  // A piece of data to store the left part of the block\n  logical_data<slice<T>> left_handle;\n\n  // A piece of data to store the right part of the block\n  logical_data<slice<T>> right_handle;\n};\n\ntemplate <typename T>\n__global__ void copy_kernel(size_t cnt, T* dst, const T* src)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    dst[idx] = src[idx];\n  }\n}\n\ntemplate <typename T>\n__global__ void stencil_kernel(size_t cnt, size_t ghost_size, T* array, const T* array1)\n{\n  for (int idx = threadIdx.x + blockIdx.x * blockDim.x; idx < cnt; idx += blockDim.x * gridDim.x)\n  {\n    int idx2    = idx + ghost_size;\n    array[idx2] = 0.9 * array1[idx2] + 0.05 * array1[idx2 - 1] + 0.05 * array1[idx2 + 1];\n  }\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid stencil(data_block<T>& bn, data_block<T>& bn1)\n{\n  ctx.task(bn.handle.rw(), bn1.handle.read())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n            stencil_kernel<<<256, 64, 0, stream>>>(bs, gs, s1.data_handle(), s2.data_handle());\n          };\n}\n\ntemplate <typename T>\nvoid update_inner_interfaces(data_block<T>& bn)\n{\n  // LEFT\n  ctx.task(bn.handle.read(), bn.left_handle.rw())->*[gs = bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n    copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s2.data_handle(), s1.data_handle() + gs);\n  };\n\n  // RIGHT\n  ctx.task(bn.handle.read(), bn.right_handle.rw())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s2.data_handle(), s1.data_handle() + bs);\n          };\n}\n\ntemplate <typename T>\nvoid update_outer_interfaces(data_block<T>& bn, data_block<T>& left, data_block<T>& right)\n{\n  ctx.task(bn.handle.rw(), left.right_handle.read())->*[gs = bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n    copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s1.data_handle(), s2.data_handle());\n  };\n  ctx.task(bn.handle.rw(), right.left_handle.read())\n      ->*[bs = bn.block_size, gs = bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<(gs > 64 ? 256 : 1), 64, 0, stream>>>(gs, s1.data_handle() + gs + bs, s2.data_handle());\n          };\n}\n\n// bn1.array = bn.array\ntemplate <typename T>\nvoid copy_array(data_block<T>& bn, data_block<T>& bn1)\n{\n  ctx.task(bn1.handle.rw(), bn.handle.read())\n      ->*[sz = bn.block_size + 2 * bn.ghost_size](hipStream_t stream, auto s1, auto s2) {\n            copy_kernel<<<256, 64, 0, stream>>>(sz, s1.data_handle(), s2.data_handle());\n          };\n}\n\nint main(int argc, char** argv)\n{\n  size_t NBLOCKS    = 2;\n  size_t BLOCK_SIZE = 1024 * 64;\n\n  size_t TOTAL_SIZE = NBLOCKS * BLOCK_SIZE;\n\n  std::vector<double> U0(NBLOCKS * BLOCK_SIZE);\n  for (size_t idx = 0; idx < NBLOCKS * BLOCK_SIZE; idx++)\n  {\n    U0[idx] = (idx == 0) ? 1.0 : 0.0;\n  }\n\n  std::vector<data_block<double>> Un;\n  std::vector<data_block<double>> Un1;\n\n  // Create blocks and allocates host data\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    int beg = b * BLOCK_SIZE;\n    int end = (b + 1) * BLOCK_SIZE;\n\n    Un.push_back(data_block<double>(beg, end, 1));\n    Un1.push_back(data_block<double>(beg, end, 1));\n  }\n\n  // Fill blocks with initial values\n  for (size_t b = 0; b < NBLOCKS; b++)\n  {\n    size_t beg = b * BLOCK_SIZE;\n    // int end = (b+1)*BLOCK_SIZE;\n\n    double* Un_vals  = Un[b].get_array();\n    double* Un1_vals = Un1[b].get_array();\n    // Attention, unusual loop: index goes all through BLOCK_SIZE inclusive.\n    for (size_t local_idx = 0; local_idx <= BLOCK_SIZE; local_idx++)\n    {\n      Un_vals[local_idx]  = U0[(beg + local_idx - 1) % TOTAL_SIZE];\n      Un1_vals[local_idx] = U0[(beg + local_idx - 1) % TOTAL_SIZE];\n    }\n  }\n\n  // Create the graph - it starts out empty\n\n  int NITER = 400;\n  for (int iter = 0; iter < NITER; iter++)\n  {\n    // UPDATE Un from Un1\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      stencil(Un[b], Un1[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      // Update the internal copies of the left and right boundaries\n      update_inner_interfaces(Un[b]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      update_outer_interfaces(Un[b], Un[(b - 1 + NBLOCKS) % NBLOCKS], Un[(b + 1) % NBLOCKS]);\n    }\n\n    for (size_t b = 0; b < NBLOCKS; b++)\n    {\n      copy_array(Un[b], Un1[b]);\n    }\n  }\n\n  ctx.submit();\n\n  if (argc > 1)\n  {\n    std::cout << \"Generating DOT output in \" << argv[1] << '\\n';\n    ctx.print_to_dot(argv[1]);\n  }\n\n  ctx.finalize();\n}\n", "source": "/kaggle/working/cccl/cudax/test/stf/stencil/stencil-1D.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// clang does not support __managed__ declarations, so clang-tidy produces spurious\n// errors. https://github.com/llvm/llvm-project/pull/149716 seemingly adds support for\n// __managed__ variables, but that PR seems to have stagnated.\n#ifndef _CCCL_CLANG_TIDY_INVOKED\n\n#  include <cuda/atomic>\n#  include <cuda/memory>\n\n#  include <cuda/experimental/graph.cuh>\n#  include <cuda/experimental/kernel.cuh>\n#  include <cuda/experimental/launch.cuh>\n#  include <cuda/experimental/stream.cuh>\n\n#  include <cooperative_groups.h>\n#  include <testing.cuh>\n\n__managed__ bool kernel_run_proof = false;\n\nvoid check_kernel_run(cudaStream_t stream)\n{\n  CUDART(cudaStreamSynchronize(stream));\n  CUDAX_CHECK(kernel_run_proof);\n  kernel_run_proof = false;\n}\n\nstruct kernel_run_proof_check\n{\n  __device__ void operator()()\n  {\n    CUDAX_CHECK(kernel_run_proof);\n    kernel_run_proof = false;\n  }\n};\n\nvoid check_kernel_run(cudax::path_builder& pb)\n{\n  cudax::launch(pb, cuda::make_config(cuda::block_dims<1>, cuda::grid_dims<1>), kernel_run_proof_check{});\n}\n\nstruct functor_int_argument\n{\n  __device__ void operator()(int dummy)\n  {\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <unsigned int BlockSize>\nstruct functor_taking_config\n{\n  template <typename Config>\n  __device__ void operator()(Config config, int grid_size)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == BlockSize);\n    CUDAX_REQUIRE(cuda::block.count(cuda::grid, config) == grid_size);\n    kernel_run_proof = true;\n  }\n};\n\n__global__ void kernel_no_arguments()\n{\n  kernel_run_proof = true;\n}\n\n__global__ void kernel_int_argument(int dummy)\n{\n  kernel_run_proof = true;\n}\n\ntemplate <typename Config, unsigned int BlockSize>\n__global__ void kernel_taking_config(Config config, int grid_size)\n{\n  functor_taking_config<BlockSize>()(config, grid_size);\n}\n\nstruct my_dynamic_smem_t\n{\n  int i;\n};\n\ntemplate <typename SmemType>\nstruct dynamic_smem_single\n{\n  template <typename Config>\n  __device__ void operator()(Config config)\n  {\n    decltype(auto) dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem)>);\n    CUDAX_REQUIRE(::cuda::device::is_object_from(dynamic_smem, ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <typename SmemType, size_t Extent>\nstruct dynamic_smem_span\n{\n  template <typename Config>\n  __device__ void operator()(Config config, int size)\n  {\n    auto dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(decltype(dynamic_smem)::extent == Extent);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem[1])>);\n    CUDAX_REQUIRE(dynamic_smem.size() == size);\n    CUDAX_REQUIRE(::cuda::device::is_object_from(dynamic_smem[1], ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\nstruct launch_transform_to_int_convertible\n{\n  int value_;\n\n  struct int_convertible\n  {\n    cudaStream_t stream_;\n    int value_;\n\n    int_convertible(cudaStream_t stream, int value) noexcept\n        : stream_(stream)\n        , value_(value)\n    {\n      // Check that the constructor runs before the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDAX_CHECK_FALSE(kernel_run_proof);\n    }\n\n    // Immovable to ensure that launch_transform doesn't copy the returned\n    // object\n    int_convertible(int_convertible&&) noexcept = delete;\n\n    ~int_convertible() noexcept\n    {\n      // Check that the destructor runs after the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDART(cudaStreamSynchronize(stream_));\n      // CUDAX_CHECK(kernel_run_proof);\n    }\n\n    // This is the value that will be passed to the kernel\n    int transformed_argument() const\n    {\n      return value_;\n    }\n  };\n\n  [[nodiscard]] friend int_convertible\n  transform_launch_argument(::cuda::stream_ref stream, launch_transform_to_int_convertible self) noexcept\n  {\n    return int_convertible(stream.get(), self.value_);\n  }\n};\n\n// Needs a separate function for Windows extended lambda\ntemplate <typename StreamOrPathBuilder>\nvoid launch_smoke_test(StreamOrPathBuilder& dst)\n{\n  cudax::__ensure_current_device guard(cuda::device_ref{0});\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  cudaStream_t stream;\n\n  CUDART(cudaStreamCreate(&stream));\n  // Spell out all overloads to make sure they compile, include a check for implicit conversions\n  {\n    const int grid_size      = 4;\n    constexpr int block_size = 256;\n    auto dimensions          = cuda::make_hierarchy(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n    auto config              = cuda::make_config(dimensions);\n\n    // Not taking dims\n    {\n      cudax::launch(dst, config, kernel_no_arguments);\n      check_kernel_run(dst);\n\n      const int dummy = 1;\n      cudax::launch(dst, config, kernel_int_argument, dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, 1U);\n      check_kernel_run(dst);\n\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, 1U);\n      check_kernel_run(dst);\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n      cudax::launch(dst, config, functor_int_argument(), dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), 1U);\n      check_kernel_run(dst);\n    }\n\n    // Config argument\n    {\n      auto functor_instance = functor_taking_config<block_size>();\n      auto kernel_instance  = kernel_taking_config<decltype(config), block_size>;\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::kernel_ref kernel_ref_instance = kernel_instance;\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n      cudax::launch(dst, config, functor_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n      cudax::launch(dst, config, kernel_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::launch(dst, config, kernel_ref_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n    }\n  }\n\n  // Lambda\n  {\n    cudax::launch(dst, cuda::block_dims<256>() & cuda::grid_dims(1), [] __device__() {\n      if (cuda::gpu_thread.rank(cuda::block) == 0)\n      {\n        printf(\"Hello from the GPU\\n\");\n        kernel_run_proof = true;\n      }\n    });\n    check_kernel_run(dst);\n  }\n\n  // Dynamic shared memory option\n  {\n    auto config = cuda::block_dims<32>() & cuda::grid_dims<1>();\n\n    auto test = [&](const auto& input_config) {\n      // Single element\n      {\n        auto config = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t>());\n\n        cudax::launch(dst, config, dynamic_smem_single<my_dynamic_smem_t>());\n        check_kernel_run(dst);\n      }\n\n      // Dynamic span\n      {\n        const int size = 2;\n        auto config    = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[]>(size));\n        cudax::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, ::cuda::std::dynamic_extent>(), size);\n        check_kernel_run(dst);\n      }\n\n      // Static span\n      {\n        constexpr int size = 3;\n        auto config        = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[size]>());\n        cudax::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, size>(), size);\n        check_kernel_run(dst);\n      }\n    };\n\n    test(config);\n    test(config.add(cuda::cooperative_launch(), cuda::launch_priority(0)));\n  }\n}\n\nC2H_TEST(\"Launch smoke stream\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  cudaStream_t stream;\n\n  CUDART(cudaStreamCreate(&stream));\n\n  launch_smoke_test(stream);\n\n  CUDART(cudaStreamSynchronize(stream));\n  CUDART(cudaStreamDestroy(stream));\n}\n\nC2H_TEST(\"Launch smoke path builder\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  cudax::graph_builder g;\n  cudax::path_builder pb = cudax::start_path(g);\n\n  launch_smoke_test(pb);\n\n  // In CUDA 12.0 we don't test kernel_ref launches, so the node count is lower\n#  if _CCCL_CTK_BELOW(12, 1)\n  CUDAX_REQUIRE(g.node_count() == 48);\n#  else // ^^^ _CCCL_CTK_BELOW(12, 1) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 1) vvv\n  CUDAX_REQUIRE(g.node_count() == 64);\n#  endif // _CCCL_CTK_BELOW(12, 1)\n\n  auto exec = g.instantiate();\n  cudax::stream s{cuda::device_ref{0}};\n  exec.launch(s);\n  s.sync();\n}\n\ntemplate <typename DefaultConfig>\nstruct kernel_with_default_config\n{\n  DefaultConfig config;\n\n  kernel_with_default_config(DefaultConfig c)\n      : config(c)\n  {}\n\n  DefaultConfig default_config() const\n  {\n    return config;\n  }\n\n  template <typename Config, typename ConfigCheckFn>\n  __device__ void operator()(Config config, ConfigCheckFn check_fn)\n  {\n    check_fn(config);\n  }\n};\n\nvoid test_default_config()\n{\n  cudax::stream stream{cuda::device_ref{0}};\n  auto grid  = cuda::grid_dims(4);\n  auto block = cuda::block_dims<256>;\n\n  auto verify_lambda = [] __device__(auto config) {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == 256);\n    CUDAX_REQUIRE(cuda::block.count(cuda::grid, config) == 4);\n    cooperative_groups::this_grid().sync();\n  };\n\n  SECTION(\"Combine with empty\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block, grid, cuda::cooperative_launch())};\n    static_assert(cuda::__is_kernel_config<decltype(kernel.default_config())>);\n    static_assert(cuda::__kernel_has_default_config<decltype(kernel)>);\n\n    cudax::launch(stream, cuda::make_config(), kernel, verify_lambda);\n    stream.sync();\n  }\n  SECTION(\"Combine with no overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block)};\n    cudax::launch(stream, cuda::make_config(grid, cuda::cooperative_launch()), kernel, verify_lambda);\n    stream.sync();\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(cuda::block_dims<1>, cuda::cooperative_launch())};\n    cudax::launch(stream, cuda::make_config(block, grid, cuda::cooperative_launch()), kernel, verify_lambda);\n    stream.sync();\n  }\n}\n\nC2H_TEST(\"Launch with default config\", \"\")\n{\n  test_default_config();\n}\n\n#endif // _CCCL_CLANG_TIDY_INVOKED\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n// clang does not support __managed__ declarations, so clang-tidy produces spurious\n// errors. https://github.com/llvm/llvm-project/pull/149716 seemingly adds support for\n// __managed__ variables, but that PR seems to have stagnated.\n#include \"hip/hip_runtime.h\"\n#ifndef _CCCL_CLANG_TIDY_INVOKED\n\n#  include <cuda/atomic>\n#  include <cuda/memory>\n\n#  include <cuda/experimental/graph.cuh>\n#  include <cuda/experimental/kernel.cuh>\n#  include <cuda/experimental/launch.cuh>\n#  include <cuda/experimental/stream.cuh>\n\n#  include <hip/hip_cooperative_groups.h>\n#  include <testing.cuh>\n\n__managed__ bool kernel_run_proof = false;\n\nvoid check_kernel_run(hipStream_t stream)\n{\n  CUDART(hipStreamSynchronize(stream));\n  CUDAX_CHECK(kernel_run_proof);\n  kernel_run_proof = false;\n}\n\nstruct kernel_run_proof_check\n{\n  __device__ void operator()()\n  {\n    CUDAX_CHECK(kernel_run_proof);\n    kernel_run_proof = false;\n  }\n};\n\nvoid check_kernel_run(cudax::path_builder& pb)\n{\n  cudax::launch(pb, cuda::make_config(cuda::block_dims<1>, cuda::grid_dims<1>), kernel_run_proof_check{});\n}\n\nstruct functor_int_argument\n{\n  __device__ void operator()(int dummy)\n  {\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <unsigned int BlockSize>\nstruct functor_taking_config\n{\n  template <typename Config>\n  __device__ void operator()(Config config, int grid_size)\n  {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == BlockSize);\n    CUDAX_REQUIRE(cuda::block.count(cuda::grid, config) == grid_size);\n    kernel_run_proof = true;\n  }\n};\n\n__global__ void kernel_no_arguments()\n{\n  kernel_run_proof = true;\n}\n\n__global__ void kernel_int_argument(int dummy)\n{\n  kernel_run_proof = true;\n}\n\ntemplate <typename Config, unsigned int BlockSize>\n__global__ void kernel_taking_config(Config config, int grid_size)\n{\n  functor_taking_config<BlockSize>()(config, grid_size);\n}\n\nstruct my_dynamic_smem_t\n{\n  int i;\n};\n\ntemplate <typename SmemType>\nstruct dynamic_smem_single\n{\n  template <typename Config>\n  __device__ void operator()(Config config)\n  {\n    decltype(auto) dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem)>);\n    CUDAX_REQUIRE(::cuda::device::is_object_from(dynamic_smem, ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\ntemplate <typename SmemType, size_t Extent>\nstruct dynamic_smem_span\n{\n  template <typename Config>\n  __device__ void operator()(Config config, int size)\n  {\n    auto dynamic_smem = cuda::dynamic_shared_memory(config);\n    static_assert(decltype(dynamic_smem)::extent == Extent);\n    static_assert(::cuda::std::is_same_v<SmemType&, decltype(dynamic_smem[1])>);\n    CUDAX_REQUIRE(dynamic_smem.size() == size);\n    CUDAX_REQUIRE(::cuda::device::is_object_from(dynamic_smem[1], ::cuda::device::address_space::shared));\n    kernel_run_proof = true;\n  }\n};\n\nstruct launch_transform_to_int_convertible\n{\n  int value_;\n\n  struct int_convertible\n  {\n    hipStream_t stream_;\n    int value_;\n\n    int_convertible(hipStream_t stream, int value) noexcept\n        : stream_(stream)\n        , value_(value)\n    {\n      // Check that the constructor runs before the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDAX_CHECK_FALSE(kernel_run_proof);\n    }\n\n    // Immovable to ensure that launch_transform doesn't copy the returned\n    // object\n    int_convertible(int_convertible&&) noexcept = delete;\n\n    ~int_convertible() noexcept\n    {\n      // Check that the destructor runs after the kernel is launched\n      // Disabled for now because we don't handle it with graphs\n      // CUDART(cudaStreamSynchronize(stream_));\n      // CUDAX_CHECK(kernel_run_proof);\n    }\n\n    // This is the value that will be passed to the kernel\n    int transformed_argument() const\n    {\n      return value_;\n    }\n  };\n\n  [[nodiscard]] friend int_convertible\n  transform_launch_argument(::cuda::stream_ref stream, launch_transform_to_int_convertible self) noexcept\n  {\n    return int_convertible(stream.get(), self.value_);\n  }\n};\n\n// Needs a separate function for Windows extended lambda\ntemplate <typename StreamOrPathBuilder>\nvoid launch_smoke_test(StreamOrPathBuilder& dst)\n{\n  cudax::__ensure_current_device guard(cuda::device_ref{0});\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  hipStream_t stream;\n\n  CUDART(hipStreamCreate(&stream));\n  // Spell out all overloads to make sure they compile, include a check for implicit conversions\n  {\n    const int grid_size      = 4;\n    constexpr int block_size = 256;\n    auto dimensions          = cuda::make_hierarchy(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n    auto config              = cuda::make_config(dimensions);\n\n    // Not taking dims\n    {\n      cudax::launch(dst, config, kernel_no_arguments);\n      check_kernel_run(dst);\n\n      const int dummy = 1;\n      cudax::launch(dst, config, kernel_int_argument, dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_int_argument, 1U);\n      check_kernel_run(dst);\n\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, cudax::kernel_ref{kernel_int_argument}, 1U);\n      check_kernel_run(dst);\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n      cudax::launch(dst, config, functor_int_argument(), dummy);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), 1);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), launch_transform_to_int_convertible{1});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_int_argument(), 1U);\n      check_kernel_run(dst);\n    }\n\n    // Config argument\n    {\n      auto functor_instance = functor_taking_config<block_size>();\n      auto kernel_instance  = kernel_taking_config<decltype(config), block_size>;\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::kernel_ref kernel_ref_instance = kernel_instance;\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n\n      cudax::launch(dst, config, functor_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, functor_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n      cudax::launch(dst, config, kernel_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n\n#  if _CCCL_CTK_AT_LEAST(12, 1)\n      cudax::launch(dst, config, kernel_ref_instance, grid_size);\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, ::cuda::std::move(grid_size));\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, launch_transform_to_int_convertible{grid_size});\n      check_kernel_run(dst);\n      cudax::launch(dst, config, kernel_ref_instance, static_cast<unsigned int>(grid_size));\n      check_kernel_run(dst);\n#  endif // _CCCL_CTK_AT_LEAST(12, 1)\n    }\n  }\n\n  // Lambda\n  {\n    cudax::launch(dst, cuda::block_dims<256>() & cuda::grid_dims(1), [] __device__() {\n      if (cuda::gpu_thread.rank(cuda::block) == 0)\n      {\n        printf(\"Hello from the GPU\\n\");\n        kernel_run_proof = true;\n      }\n    });\n    check_kernel_run(dst);\n  }\n\n  // Dynamic shared memory option\n  {\n    auto config = cuda::block_dims<32>() & cuda::grid_dims<1>();\n\n    auto test = [&](const auto& input_config) {\n      // Single element\n      {\n        auto config = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t>());\n\n        cudax::launch(dst, config, dynamic_smem_single<my_dynamic_smem_t>());\n        check_kernel_run(dst);\n      }\n\n      // Dynamic span\n      {\n        const int size = 2;\n        auto config    = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[]>(size));\n        cudax::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, ::cuda::std::dynamic_extent>(), size);\n        check_kernel_run(dst);\n      }\n\n      // Static span\n      {\n        constexpr int size = 3;\n        auto config        = input_config.add(cuda::dynamic_shared_memory<my_dynamic_smem_t[size]>());\n        cudax::launch(dst, config, dynamic_smem_span<my_dynamic_smem_t, size>(), size);\n        check_kernel_run(dst);\n      }\n    };\n\n    test(config);\n    test(config.add(cuda::cooperative_launch(), cuda::launch_priority(0)));\n  }\n}\n\nC2H_TEST(\"Launch smoke stream\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  hipStream_t stream;\n\n  CUDART(hipStreamCreate(&stream));\n\n  launch_smoke_test(stream);\n\n  CUDART(hipStreamSynchronize(stream));\n  CUDART(hipStreamDestroy(stream));\n}\n\nC2H_TEST(\"Launch smoke path builder\", \"[launch]\")\n{\n  // Use raw stream to make sure it can be implicitly converted on call to launch\n  cudax::graph_builder g;\n  cudax::path_builder pb = cudax::start_path(g);\n\n  launch_smoke_test(pb);\n\n  // In CUDA 12.0 we don't test kernel_ref launches, so the node count is lower\n#  if _CCCL_CTK_BELOW(12, 1)\n  CUDAX_REQUIRE(g.node_count() == 48);\n#  else // ^^^ _CCCL_CTK_BELOW(12, 1) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 1) vvv\n  CUDAX_REQUIRE(g.node_count() == 64);\n#  endif // _CCCL_CTK_BELOW(12, 1)\n\n  auto exec = g.instantiate();\n  cudax::stream s{cuda::device_ref{0}};\n  exec.launch(s);\n  s.sync();\n}\n\ntemplate <typename DefaultConfig>\nstruct kernel_with_default_config\n{\n  DefaultConfig config;\n\n  kernel_with_default_config(DefaultConfig c)\n      : config(c)\n  {}\n\n  DefaultConfig default_config() const\n  {\n    return config;\n  }\n\n  template <typename Config, typename ConfigCheckFn>\n  __device__ void operator()(Config config, ConfigCheckFn check_fn)\n  {\n    check_fn(config);\n  }\n};\n\nvoid test_default_config()\n{\n  cudax::stream stream{cuda::device_ref{0}};\n  auto grid  = cuda::grid_dims(4);\n  auto block = cuda::block_dims<256>;\n\n  auto verify_lambda = [] __device__(auto config) {\n    static_assert(cuda::gpu_thread.count(cuda::block, config) == 256);\n    CUDAX_REQUIRE(cuda::block.count(cuda::grid, config) == 4);\n    cooperative_groups::this_grid().sync();\n  };\n\n  SECTION(\"Combine with empty\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block, grid, cuda::cooperative_launch())};\n    static_assert(cuda::__is_kernel_config<decltype(kernel.default_config())>);\n    static_assert(cuda::__kernel_has_default_config<decltype(kernel)>);\n\n    cudax::launch(stream, cuda::make_config(), kernel, verify_lambda);\n    stream.sync();\n  }\n  SECTION(\"Combine with no overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(block)};\n    cudax::launch(stream, cuda::make_config(grid, cuda::cooperative_launch()), kernel, verify_lambda);\n    stream.sync();\n  }\n  SECTION(\"Combine with overlap\")\n  {\n    kernel_with_default_config kernel{cuda::make_config(cuda::block_dims<1>, cuda::cooperative_launch())};\n    cudax::launch(stream, cuda::make_config(block, grid, cuda::cooperative_launch()), kernel, verify_lambda);\n    stream.sync();\n  }\n}\n\nC2H_TEST(\"Launch with default config\", \"\")\n{\n  test_default_config();\n}\n\n#endif // _CCCL_CLANG_TIDY_INVOKED\n", "source": "/kaggle/working/cccl/cudax/test/launch/launch_smoke.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that data_place::mem_create() can be used to create VMM-based\n *        physical memory allocations.\n *\n * This tests the low-level VMM allocation interface used by localized arrays\n * (composite_slice) for creating physical memory segments that are mapped\n * into a contiguous virtual address space.\n */\n\n#include <cuda/experimental/__places/places.cuh>\n\n#include <cstdio>\n\nusing namespace cuda::experimental::places;\n\n__global__ void init_kernel(int* ptr, int n, int value)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = value + tid;\n  }\n}\n\n__global__ void check_kernel(int* ptr, int n, int value, int* result)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    if (ptr[tid] != value + tid)\n    {\n      atomicExch(result, 1); // Set error flag\n    }\n  }\n}\n\n// Check if VMM is supported on the current device\nbool vmm_supported(int dev_id = 0)\n{\n  CUdevice dev;\n  cuda_try(cuDeviceGet(&dev, dev_id));\n  int supportsVMM;\n  cuda_try(cuDeviceGetAttribute(&supportsVMM, CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED, dev));\n  return supportsVMM == 1;\n}\n\n// Get allocation granularity for VMM\nsize_t get_granularity(int dev_id)\n{\n  CUmemAllocationProp prop = {};\n  prop.type                = CU_MEM_ALLOCATION_TYPE_PINNED;\n  prop.location.type       = CU_MEM_LOCATION_TYPE_DEVICE;\n  prop.location.id         = dev_id;\n\n  size_t granularity;\n  cuda_try(cuMemGetAllocationGranularity(&granularity, &prop, CU_MEM_ALLOC_GRANULARITY_MINIMUM));\n  return granularity;\n}\n\nvoid test_device_vmm_allocation()\n{\n  printf(\"Testing device VMM allocation (mem_create)...\\n\");\n\n  int dev_id = 0;\n  cuda_try(cudaSetDevice(dev_id));\n\n  // Get allocation granularity - VMM allocations must be aligned to this\n  size_t granularity = get_granularity(dev_id);\n  printf(\"  Allocation granularity: %zu bytes\\n\", granularity);\n\n  // Allocate at least one granularity unit\n  const size_t alloc_size = granularity;\n  const size_t n          = alloc_size / sizeof(int);\n  const int test_value    = 42;\n\n  // Create physical memory using data_place::mem_create\n  auto place = data_place::device(dev_id);\n  CUmemGenericAllocationHandle handle;\n  CUresult result = place.mem_create(&handle, alloc_size);\n  EXPECT(result == CUDA_SUCCESS);\n\n  // Reserve virtual address space\n  CUdeviceptr va_ptr;\n  cuda_try(cuMemAddressReserve(&va_ptr, alloc_size, 0, 0, 0));\n\n  // Map the physical allocation to the virtual address\n  cuda_try(cuMemMap(va_ptr, alloc_size, 0, handle, 0));\n\n  // Set access permissions for the current device\n  CUmemAccessDesc accessDesc = {};\n  accessDesc.location.type   = CU_MEM_LOCATION_TYPE_DEVICE;\n  accessDesc.location.id     = dev_id;\n  accessDesc.flags           = CU_MEM_ACCESS_FLAGS_PROT_READWRITE;\n  cuda_try(cuMemSetAccess(va_ptr, alloc_size, &accessDesc, 1));\n\n  // Now we can use the memory!\n  int* d_ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  // Create a stream for operations\n  cudaStream_t stream;\n  cuda_try(cudaStreamCreate(&stream));\n\n  // Initialize on device\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Allocate result flag for checking\n  int* d_result;\n  cuda_try(cudaMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(cudaMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  // Check on device\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  // Copy result back\n  int h_result = 0;\n  cuda_try(cudaMemcpyAsync(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(cudaFreeAsync(d_result, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n  cuda_try(cudaStreamDestroy(stream));\n\n  // Unmap and release VMM resources\n  cuda_try(cuMemUnmap(va_ptr, alloc_size));\n  cuda_try(cuMemRelease(handle));\n  cuda_try(cuMemAddressFree(va_ptr, alloc_size));\n\n  printf(\"  Device VMM allocation test PASSED\\n\");\n}\n\n// Host VMM requires CU_MEM_LOCATION_TYPE_HOST which is only available in CUDA 12.2+\n#if _CCCL_CTK_AT_LEAST(12, 2)\nvoid test_host_vmm_allocation()\n{\n  printf(\"Testing host VMM allocation (mem_create)...\\n\");\n\n  // Host VMM allocations use CU_MEM_LOCATION_TYPE_HOST\n  // First check if host VMM is supported (requires appropriate driver/hardware)\n\n  // Get host allocation granularity\n  CUmemAllocationProp prop = {};\n  prop.type                = CU_MEM_ALLOCATION_TYPE_PINNED;\n  prop.location.type       = CU_MEM_LOCATION_TYPE_HOST;\n  prop.location.id         = 0;\n\n  size_t granularity;\n  CUresult gran_result = cuMemGetAllocationGranularity(&granularity, &prop, CU_MEM_ALLOC_GRANULARITY_MINIMUM);\n  if (gran_result != CUDA_SUCCESS)\n  {\n    printf(\"  Host VMM not supported on this system, skipping.\\n\");\n    return;\n  }\n\n  printf(\"  Host allocation granularity: %zu bytes\\n\", granularity);\n\n  const size_t alloc_size = granularity;\n  const size_t n          = alloc_size / sizeof(int);\n\n  // Create physical memory using data_place::mem_create with host place\n  auto place = data_place::host();\n  CUmemGenericAllocationHandle handle;\n  CUresult result = place.mem_create(&handle, alloc_size);\n  if (result != CUDA_SUCCESS)\n  {\n    printf(\"  Host mem_create not supported (error %d), skipping.\\n\", result);\n    return;\n  }\n\n  // Reserve virtual address space\n  CUdeviceptr va_ptr;\n  cuda_try(cuMemAddressReserve(&va_ptr, alloc_size, 0, 0, 0));\n\n  // Map the physical allocation to the virtual address\n  cuda_try(cuMemMap(va_ptr, alloc_size, 0, handle, 0));\n\n  // Set access permissions for the host\n  CUmemAccessDesc accessDesc = {};\n  accessDesc.location.type   = CU_MEM_LOCATION_TYPE_HOST;\n  accessDesc.location.id     = 0;\n  accessDesc.flags           = CU_MEM_ACCESS_FLAGS_PROT_READWRITE;\n  cuda_try(cuMemSetAccess(va_ptr, alloc_size, &accessDesc, 1));\n\n  // Use the memory from the host\n  int* ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  // Initialize on host\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = static_cast<int>(i * 2);\n  }\n\n  // Verify on host\n  for (size_t i = 0; i < n; i++)\n  {\n    EXPECT(ptr[i] == static_cast<int>(i * 2));\n  }\n\n  // Cleanup VMM resources\n  cuda_try(cuMemUnmap(va_ptr, alloc_size));\n  cuda_try(cuMemRelease(handle));\n  cuda_try(cuMemAddressFree(va_ptr, alloc_size));\n\n  printf(\"  Host VMM allocation test PASSED\\n\");\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n\nvoid test_multi_segment_vmm()\n{\n  printf(\"Testing multi-segment VMM allocation...\\n\");\n\n  int dev_id = 0;\n  cuda_try(cudaSetDevice(dev_id));\n\n  size_t granularity = get_granularity(dev_id);\n\n  // Create two segments and map them contiguously\n  const size_t segment_size = granularity;\n  const size_t total_size   = 2 * segment_size;\n  const size_t n            = total_size / sizeof(int);\n  const int test_value      = 100;\n\n  auto place = data_place::device(dev_id);\n\n  // Create two physical allocations\n  CUmemGenericAllocationHandle handle1, handle2;\n  cuda_try(place.mem_create(&handle1, segment_size));\n  cuda_try(place.mem_create(&handle2, segment_size));\n\n  // Reserve contiguous virtual address space for both\n  CUdeviceptr va_ptr;\n  cuda_try(cuMemAddressReserve(&va_ptr, total_size, 0, 0, 0));\n\n  // Map both segments contiguously\n  cuda_try(cuMemMap(va_ptr, segment_size, 0, handle1, 0));\n  cuda_try(cuMemMap(va_ptr + segment_size, segment_size, 0, handle2, 0));\n\n  // Set access for the entire range\n  CUmemAccessDesc accessDesc = {};\n  accessDesc.location.type   = CU_MEM_LOCATION_TYPE_DEVICE;\n  accessDesc.location.id     = dev_id;\n  accessDesc.flags           = CU_MEM_ACCESS_FLAGS_PROT_READWRITE;\n  cuda_try(cuMemSetAccess(va_ptr, total_size, &accessDesc, 1));\n\n  int* d_ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  cudaStream_t stream;\n  cuda_try(cudaStreamCreate(&stream));\n\n  // Initialize the entire contiguous range\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Check the entire range\n  int* d_result;\n  cuda_try(cudaMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(cudaMemsetAsync(d_result, 0, sizeof(int), stream));\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  int h_result = 0;\n  cuda_try(cudaMemcpyAsync(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  EXPECT(h_result == 0);\n\n  // Cleanup\n  cuda_try(cudaFreeAsync(d_result, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n  cuda_try(cudaStreamDestroy(stream));\n\n  cuda_try(cuMemUnmap(va_ptr, segment_size));\n  cuda_try(cuMemUnmap(va_ptr + segment_size, segment_size));\n  cuda_try(cuMemRelease(handle1));\n  cuda_try(cuMemRelease(handle2));\n  cuda_try(cuMemAddressFree(va_ptr, total_size));\n\n  printf(\"  Multi-segment VMM allocation test PASSED\\n\");\n}\n\nint main()\n{\n  printf(\"=== Testing data_place VMM allocation (mem_create) ===\\n\\n\");\n\n  // Initialize CUDA driver API\n  cuda_try(cuInit(0));\n\n  // Check VMM support\n  if (!vmm_supported())\n  {\n    printf(\"VMM not supported on this device, skipping tests.\\n\");\n    return 0;\n  }\n\n  test_device_vmm_allocation();\n#if _CCCL_CTK_AT_LEAST(12, 2)\n  test_host_vmm_allocation();\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n  test_multi_segment_vmm();\n\n  printf(\"\\n=== All VMM tests PASSED ===\\n\");\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that data_place::mem_create() can be used to create VMM-based\n *        physical memory allocations.\n *\n * This tests the low-level VMM allocation interface used by localized arrays\n * (composite_slice) for creating physical memory segments that are mapped\n * into a contiguous virtual address space.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/places.cuh>\n\n#include <cstdio>\n\nusing namespace cuda::experimental::places;\n\n__global__ void init_kernel(int* ptr, int n, int value)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = value + tid;\n  }\n}\n\n__global__ void check_kernel(int* ptr, int n, int value, int* result)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    if (ptr[tid] != value + tid)\n    {\n      atomicExch(result, 1); // Set error flag\n    }\n  }\n}\n\n// Check if VMM is supported on the current device\nbool vmm_supported(int dev_id = 0)\n{\n  hipDevice_t dev;\n  cuda_try(hipDeviceGet(&dev, dev_id));\n  int supportsVMM;\n  cuda_try(hipDeviceGetAttribute(&supportsVMM, CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED, dev));\n  return supportsVMM == 1;\n}\n\n// Get allocation granularity for VMM\nsize_t get_granularity(int dev_id)\n{\n  hipMemAllocationProp prop = {};\n  prop.type                = hipMemAllocationTypePinned;\n  prop.location.type       = hipMemLocationTypeDevice;\n  prop.location.id         = dev_id;\n\n  size_t granularity;\n  cuda_try(hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum));\n  return granularity;\n}\n\nvoid test_device_vmm_allocation()\n{\n  printf(\"Testing device VMM allocation (mem_create)...\\n\");\n\n  int dev_id = 0;\n  cuda_try(hipSetDevice(dev_id));\n\n  // Get allocation granularity - VMM allocations must be aligned to this\n  size_t granularity = get_granularity(dev_id);\n  printf(\"  Allocation granularity: %zu bytes\\n\", granularity);\n\n  // Allocate at least one granularity unit\n  const size_t alloc_size = granularity;\n  const size_t n          = alloc_size / sizeof(int);\n  const int test_value    = 42;\n\n  // Create physical memory using data_place::mem_create\n  auto place = data_place::device(dev_id);\n  hipMemGenericAllocationHandle_t handle;\n  hipError_t result = place.mem_create(&handle, alloc_size);\n  EXPECT(result == hipSuccess);\n\n  // Reserve virtual address space\n  hipDeviceptr_t va_ptr;\n  cuda_try(hipMemAddressReserve(&va_ptr, alloc_size, 0, 0, 0));\n\n  // Map the physical allocation to the virtual address\n  cuda_try(hipMemMap(va_ptr, alloc_size, 0, handle, 0));\n\n  // Set access permissions for the current device\n  hipMemAccessDesc accessDesc = {};\n  accessDesc.location.type   = hipMemLocationTypeDevice;\n  accessDesc.location.id     = dev_id;\n  accessDesc.flags           = hipMemAccessFlagsProtReadWrite;\n  cuda_try(hipMemSetAccess(va_ptr, alloc_size, &accessDesc, 1));\n\n  // Now we can use the memory!\n  int* d_ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  // Create a stream for operations\n  hipStream_t stream;\n  cuda_try(hipStreamCreate(&stream));\n\n  // Initialize on device\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Allocate result flag for checking\n  int* d_result;\n  cuda_try(hipMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(hipMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  // Check on device\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  // Copy result back\n  int h_result = 0;\n  cuda_try(hipMemcpyAsync(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(hipFreeAsync(d_result, stream));\n  cuda_try(hipStreamSynchronize(stream));\n  cuda_try(hipStreamDestroy(stream));\n\n  // Unmap and release VMM resources\n  cuda_try(hipMemUnmap(va_ptr, alloc_size));\n  cuda_try(hipMemRelease(handle));\n  cuda_try(hipMemAddressFree(va_ptr, alloc_size));\n\n  printf(\"  Device VMM allocation test PASSED\\n\");\n}\n\n// Host VMM requires CU_MEM_LOCATION_TYPE_HOST which is only available in CUDA 12.2+\n#if _CCCL_CTK_AT_LEAST(12, 2)\nvoid test_host_vmm_allocation()\n{\n  printf(\"Testing host VMM allocation (mem_create)...\\n\");\n\n  // Host VMM allocations use CU_MEM_LOCATION_TYPE_HOST\n  // First check if host VMM is supported (requires appropriate driver/hardware)\n\n  // Get host allocation granularity\n  hipMemAllocationProp prop = {};\n  prop.type                = hipMemAllocationTypePinned;\n  prop.location.type       = hipMemLocationTypeHost;\n  prop.location.id         = 0;\n\n  size_t granularity;\n  hipError_t gran_result = hipMemGetAllocationGranularity(&granularity, &prop, hipMemAllocationGranularityMinimum);\n  if (gran_result != hipSuccess)\n  {\n    printf(\"  Host VMM not supported on this system, skipping.\\n\");\n    return;\n  }\n\n  printf(\"  Host allocation granularity: %zu bytes\\n\", granularity);\n\n  const size_t alloc_size = granularity;\n  const size_t n          = alloc_size / sizeof(int);\n\n  // Create physical memory using data_place::mem_create with host place\n  auto place = data_place::host();\n  hipMemGenericAllocationHandle_t handle;\n  hipError_t result = place.mem_create(&handle, alloc_size);\n  if (result != hipSuccess)\n  {\n    printf(\"  Host mem_create not supported (error %d), skipping.\\n\", result);\n    return;\n  }\n\n  // Reserve virtual address space\n  hipDeviceptr_t va_ptr;\n  cuda_try(hipMemAddressReserve(&va_ptr, alloc_size, 0, 0, 0));\n\n  // Map the physical allocation to the virtual address\n  cuda_try(hipMemMap(va_ptr, alloc_size, 0, handle, 0));\n\n  // Set access permissions for the host\n  hipMemAccessDesc accessDesc = {};\n  accessDesc.location.type   = hipMemLocationTypeHost;\n  accessDesc.location.id     = 0;\n  accessDesc.flags           = hipMemAccessFlagsProtReadWrite;\n  cuda_try(hipMemSetAccess(va_ptr, alloc_size, &accessDesc, 1));\n\n  // Use the memory from the host\n  int* ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  // Initialize on host\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = static_cast<int>(i * 2);\n  }\n\n  // Verify on host\n  for (size_t i = 0; i < n; i++)\n  {\n    EXPECT(ptr[i] == static_cast<int>(i * 2));\n  }\n\n  // Cleanup VMM resources\n  cuda_try(hipMemUnmap(va_ptr, alloc_size));\n  cuda_try(hipMemRelease(handle));\n  cuda_try(hipMemAddressFree(va_ptr, alloc_size));\n\n  printf(\"  Host VMM allocation test PASSED\\n\");\n}\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n\nvoid test_multi_segment_vmm()\n{\n  printf(\"Testing multi-segment VMM allocation...\\n\");\n\n  int dev_id = 0;\n  cuda_try(hipSetDevice(dev_id));\n\n  size_t granularity = get_granularity(dev_id);\n\n  // Create two segments and map them contiguously\n  const size_t segment_size = granularity;\n  const size_t total_size   = 2 * segment_size;\n  const size_t n            = total_size / sizeof(int);\n  const int test_value      = 100;\n\n  auto place = data_place::device(dev_id);\n\n  // Create two physical allocations\n  hipMemGenericAllocationHandle_t handle1, handle2;\n  cuda_try(place.mem_create(&handle1, segment_size));\n  cuda_try(place.mem_create(&handle2, segment_size));\n\n  // Reserve contiguous virtual address space for both\n  hipDeviceptr_t va_ptr;\n  cuda_try(hipMemAddressReserve(&va_ptr, total_size, 0, 0, 0));\n\n  // Map both segments contiguously\n  cuda_try(hipMemMap(va_ptr, segment_size, 0, handle1, 0));\n  cuda_try(hipMemMap(va_ptr + segment_size, segment_size, 0, handle2, 0));\n\n  // Set access for the entire range\n  hipMemAccessDesc accessDesc = {};\n  accessDesc.location.type   = hipMemLocationTypeDevice;\n  accessDesc.location.id     = dev_id;\n  accessDesc.flags           = hipMemAccessFlagsProtReadWrite;\n  cuda_try(hipMemSetAccess(va_ptr, total_size, &accessDesc, 1));\n\n  int* d_ptr = reinterpret_cast<int*>(va_ptr); // NOLINT(performance-no-int-to-ptr)\n\n  hipStream_t stream;\n  cuda_try(hipStreamCreate(&stream));\n\n  // Initialize the entire contiguous range\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Check the entire range\n  int* d_result;\n  cuda_try(hipMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(hipMemsetAsync(d_result, 0, sizeof(int), stream));\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  int h_result = 0;\n  cuda_try(hipMemcpyAsync(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  EXPECT(h_result == 0);\n\n  // Cleanup\n  cuda_try(hipFreeAsync(d_result, stream));\n  cuda_try(hipStreamSynchronize(stream));\n  cuda_try(hipStreamDestroy(stream));\n\n  cuda_try(hipMemUnmap(va_ptr, segment_size));\n  cuda_try(hipMemUnmap(va_ptr + segment_size, segment_size));\n  cuda_try(hipMemRelease(handle1));\n  cuda_try(hipMemRelease(handle2));\n  cuda_try(hipMemAddressFree(va_ptr, total_size));\n\n  printf(\"  Multi-segment VMM allocation test PASSED\\n\");\n}\n\nint main()\n{\n  printf(\"=== Testing data_place VMM allocation (mem_create) ===\\n\\n\");\n\n  // Initialize CUDA driver API\n  cuda_try(hipInit(0));\n\n  // Check VMM support\n  if (!vmm_supported())\n  {\n    printf(\"VMM not supported on this device, skipping tests.\\n\");\n    return 0;\n  }\n\n  test_device_vmm_allocation();\n#if _CCCL_CTK_AT_LEAST(12, 2)\n  test_host_vmm_allocation();\n#endif // _CCCL_CTK_AT_LEAST(12, 2)\n  test_multi_segment_vmm();\n\n  printf(\"\\n=== All VMM tests PASSED ===\\n\");\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/places/data_place_vmm.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Tests for the standalone stream pool functionality in exec_place.\n *\n * Verifies that exec_place::pick_stream() works without a CUDASTF context\n * or async_resources_handle, returning valid CUDA streams from the\n * per-place stream pool.\n */\n\n#include <cuda/experimental/__places/places.cuh>\n\nusing namespace cuda::experimental::places;\n\n__global__ void increment_kernel(int* data, int n)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    data[tid] += 1;\n  }\n}\n\n// Streams returned by pick_stream() are owned by the exec_place's internal\n// pool (round-robin, lazily created). Callers must NOT destroy them.\nvoid test_basic_pick_stream()\n{\n  exec_place place = exec_place::current_device();\n\n  cudaStream_t stream = place.pick_stream();\n  _CCCL_ASSERT(stream != nullptr, \"pick_stream must return a valid stream\");\n\n  int current_device;\n  cuda_try(cudaGetDevice(&current_device));\n  _CCCL_ASSERT(get_device_from_stream(stream) == current_device, \"stream must belong to the current device\");\n\n  fprintf(stderr, \"test_basic_pick_stream: PASSED\\n\");\n}\n\nvoid test_pick_stream_computation_hint()\n{\n  exec_place place = exec_place::current_device();\n\n  cudaStream_t compute_stream  = place.pick_stream(true);\n  cudaStream_t transfer_stream = place.pick_stream(false);\n\n  _CCCL_ASSERT(compute_stream != nullptr, \"compute stream must be valid\");\n  _CCCL_ASSERT(transfer_stream != nullptr, \"transfer stream must be valid\");\n\n  fprintf(stderr, \"test_pick_stream_computation_hint: PASSED\\n\");\n}\n\nvoid test_pick_stream_specific_device(int ndevs)\n{\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"test_pick_stream_specific_device: skipped (need >= 2 devices)\\n\");\n    return;\n  }\n\n  for (int d = 0; d < ndevs && d < 2; d++)\n  {\n    exec_place dev      = exec_place::device(d);\n    cudaStream_t stream = dev.pick_stream();\n    _CCCL_ASSERT(stream != nullptr, \"stream must be valid\");\n    _CCCL_ASSERT(get_device_from_stream(stream) == d, \"stream must belong to the requested device\");\n  }\n\n  fprintf(stderr, \"test_pick_stream_specific_device: PASSED\\n\");\n}\n\nvoid test_launch_kernel_on_picked_stream()\n{\n  exec_place place    = exec_place::current_device();\n  cudaStream_t stream = place.pick_stream();\n\n  constexpr int N = 256;\n  int* d_data;\n  cuda_try(cudaMallocAsync(&d_data, N * sizeof(int), stream));\n  cuda_try(cudaMemsetAsync(d_data, 0, N * sizeof(int), stream));\n\n  increment_kernel<<<1, N, 0, stream>>>(d_data, N);\n\n  int h_data[N];\n  cuda_try(cudaMemcpyAsync(h_data, d_data, N * sizeof(int), cudaMemcpyDeviceToHost, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  for (const auto& v : h_data)\n  {\n    _CCCL_ASSERT(v == 1, \"kernel result mismatch\");\n  }\n\n  cuda_try(cudaFreeAsync(d_data, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  fprintf(stderr, \"test_launch_kernel_on_picked_stream: PASSED\\n\");\n}\n\nvoid test_round_robin_streams()\n{\n  exec_place place = exec_place::current_device();\n\n  cudaStream_t first  = place.pick_stream();\n  cudaStream_t second = place.pick_stream();\n\n  _CCCL_ASSERT(first != nullptr, \"first stream must be valid\");\n  _CCCL_ASSERT(second != nullptr, \"second stream must be valid\");\n\n  fprintf(stderr, \"test_round_robin_streams: PASSED\\n\");\n}\n\nint main()\n{\n  int ndevs;\n  cuda_try(cudaGetDeviceCount(&ndevs));\n\n  test_basic_pick_stream();\n  test_pick_stream_computation_hint();\n  test_pick_stream_specific_device(ndevs);\n  test_launch_kernel_on_picked_stream();\n  test_round_robin_streams();\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n * @brief Tests for the standalone stream pool functionality in exec_place.\n *\n * Verifies that exec_place::pick_stream() works without a CUDASTF context\n * or async_resources_handle, returning valid CUDA streams from the\n * per-place stream pool.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/places.cuh>\n\nusing namespace cuda::experimental::places;\n\n__global__ void increment_kernel(int* data, int n)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    data[tid] += 1;\n  }\n}\n\n// Streams returned by pick_stream() are owned by the exec_place's internal\n// pool (round-robin, lazily created). Callers must NOT destroy them.\nvoid test_basic_pick_stream()\n{\n  exec_place place = exec_place::current_device();\n\n  hipStream_t stream = place.pick_stream();\n  _CCCL_ASSERT(stream != nullptr, \"pick_stream must return a valid stream\");\n\n  int current_device;\n  cuda_try(hipGetDevice(&current_device));\n  _CCCL_ASSERT(get_device_from_stream(stream) == current_device, \"stream must belong to the current device\");\n\n  fprintf(stderr, \"test_basic_pick_stream: PASSED\\n\");\n}\n\nvoid test_pick_stream_computation_hint()\n{\n  exec_place place = exec_place::current_device();\n\n  hipStream_t compute_stream  = place.pick_stream(true);\n  hipStream_t transfer_stream = place.pick_stream(false);\n\n  _CCCL_ASSERT(compute_stream != nullptr, \"compute stream must be valid\");\n  _CCCL_ASSERT(transfer_stream != nullptr, \"transfer stream must be valid\");\n\n  fprintf(stderr, \"test_pick_stream_computation_hint: PASSED\\n\");\n}\n\nvoid test_pick_stream_specific_device(int ndevs)\n{\n  if (ndevs < 2)\n  {\n    fprintf(stderr, \"test_pick_stream_specific_device: skipped (need >= 2 devices)\\n\");\n    return;\n  }\n\n  for (int d = 0; d < ndevs && d < 2; d++)\n  {\n    exec_place dev      = exec_place::device(d);\n    hipStream_t stream = dev.pick_stream();\n    _CCCL_ASSERT(stream != nullptr, \"stream must be valid\");\n    _CCCL_ASSERT(get_device_from_stream(stream) == d, \"stream must belong to the requested device\");\n  }\n\n  fprintf(stderr, \"test_pick_stream_specific_device: PASSED\\n\");\n}\n\nvoid test_launch_kernel_on_picked_stream()\n{\n  exec_place place    = exec_place::current_device();\n  hipStream_t stream = place.pick_stream();\n\n  constexpr int N = 256;\n  int* d_data;\n  cuda_try(hipMallocAsync(&d_data, N * sizeof(int), stream));\n  cuda_try(hipMemsetAsync(d_data, 0, N * sizeof(int), stream));\n\n  increment_kernel<<<1, N, 0, stream>>>(d_data, N);\n\n  int h_data[N];\n  cuda_try(hipMemcpyAsync(h_data, d_data, N * sizeof(int), hipMemcpyDeviceToHost, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  for (const auto& v : h_data)\n  {\n    _CCCL_ASSERT(v == 1, \"kernel result mismatch\");\n  }\n\n  cuda_try(hipFreeAsync(d_data, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  fprintf(stderr, \"test_launch_kernel_on_picked_stream: PASSED\\n\");\n}\n\nvoid test_round_robin_streams()\n{\n  exec_place place = exec_place::current_device();\n\n  hipStream_t first  = place.pick_stream();\n  hipStream_t second = place.pick_stream();\n\n  _CCCL_ASSERT(first != nullptr, \"first stream must be valid\");\n  _CCCL_ASSERT(second != nullptr, \"second stream must be valid\");\n\n  fprintf(stderr, \"test_round_robin_streams: PASSED\\n\");\n}\n\nint main()\n{\n  int ndevs;\n  cuda_try(hipGetDeviceCount(&ndevs));\n\n  test_basic_pick_stream();\n  test_pick_stream_computation_hint();\n  test_pick_stream_specific_device(ndevs);\n  test_launch_kernel_on_picked_stream();\n  test_round_robin_streams();\n}\n", "source": "/kaggle/working/cccl/cudax/test/places/stream_pool.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that data_place can be used to allocate/deallocate memory\n *        directly without a CUDASTF context.\n *\n * This demonstrates how places can be used for raw memory allocation\n * outside of the task-based programming model.\n */\n\n#include <cuda/experimental/__places/places.cuh>\n\n#include <cstdio>\n\nusing namespace cuda::experimental::places;\n\n__global__ void init_kernel(int* ptr, int n, int value)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = value + tid;\n  }\n}\n\n__global__ void check_kernel(int* ptr, int n, int value, int* result)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    if (ptr[tid] != value + tid)\n    {\n      atomicExch(result, 1); // Set error flag\n    }\n  }\n}\n\nvoid test_host_allocation()\n{\n  printf(\"Testing host allocation...\\n\");\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n\n  // Allocate using data_place::host() - stream parameter is ignored for host allocations\n  auto place = data_place::host();\n  EXPECT(!place.allocation_is_stream_ordered()); // Host allocations are blocking\n\n  int* ptr = static_cast<int*>(place.allocate(byte_size));\n  EXPECT(ptr != nullptr);\n\n  // Initialize on host\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = static_cast<int>(i * 2);\n  }\n\n  // Verify\n  for (size_t i = 0; i < n; i++)\n  {\n    EXPECT(ptr[i] == static_cast<int>(i * 2));\n  }\n\n  // Deallocate\n  place.deallocate(ptr, byte_size, nullptr);\n\n  printf(\"  Host allocation test PASSED\\n\");\n}\n\nvoid test_device_allocation()\n{\n  printf(\"Testing device allocation...\\n\");\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n  const int test_value   = 42;\n\n  // Create a stream for the allocation\n  cudaStream_t stream;\n  cuda_try(cudaStreamCreate(&stream));\n\n  // Allocate using data_place::device(0)\n  auto place = data_place::device(0);\n  EXPECT(place.allocation_is_stream_ordered()); // Device allocations are stream-ordered\n\n  int* d_ptr = static_cast<int*>(place.allocate(byte_size, stream));\n  EXPECT(d_ptr != nullptr);\n\n  // Initialize on device\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Allocate result flag on host for checking\n  int* d_result;\n  cuda_try(cudaMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(cudaMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  // Check on device\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  // Copy result back\n  int h_result = 0;\n  cuda_try(cudaMemcpyAsync(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(cudaFreeAsync(d_result, stream));\n  place.deallocate(d_ptr, byte_size, stream);\n\n  cuda_try(cudaStreamSynchronize(stream));\n  cuda_try(cudaStreamDestroy(stream));\n\n  printf(\"  Device allocation test PASSED\\n\");\n}\n\nvoid test_managed_allocation()\n{\n  printf(\"Testing managed allocation...\\n\");\n\n  // Check if concurrent managed access is supported\n  int dev;\n  cuda_try(cudaGetDevice(&dev));\n  cudaDeviceProp prop;\n  cuda_try(cudaGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    printf(\"  Concurrent CPU/GPU access not supported, skipping managed test.\\n\");\n    return;\n  }\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n  const int test_value   = 100;\n\n  cudaStream_t stream;\n  cuda_try(cudaStreamCreate(&stream));\n\n  // Allocate using data_place::managed()\n  auto place = data_place::managed();\n  EXPECT(!place.allocation_is_stream_ordered()); // Managed allocations are immediate (stream ignored)\n\n  int* ptr = static_cast<int*>(place.allocate(byte_size));\n  EXPECT(ptr != nullptr);\n\n  // Initialize on host (managed memory is accessible from both CPU and GPU)\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = test_value + static_cast<int>(i);\n  }\n\n  // Read back on device and verify\n  int* d_result;\n  cuda_try(cudaMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(cudaMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(ptr, n, test_value, d_result);\n\n  int h_result = 0;\n  cuda_try(cudaMemcpyAsync(&h_result, d_result, sizeof(int), cudaMemcpyDeviceToHost, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(cudaFreeAsync(d_result, stream));\n  cuda_try(cudaStreamSynchronize(stream));\n  place.deallocate(ptr, byte_size);\n\n  cuda_try(cudaStreamDestroy(stream));\n\n  printf(\"  Managed allocation test PASSED\\n\");\n}\n\nint main()\n{\n  printf(\"=== Testing data_place direct allocation (no context) ===\\n\\n\");\n\n  test_host_allocation();\n  test_device_allocation();\n  test_managed_allocation();\n\n  printf(\"\\n=== All tests PASSED ===\\n\");\n  return 0;\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDASTF in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n/**\n * @file\n *\n * @brief Test that data_place can be used to allocate/deallocate memory\n *        directly without a CUDASTF context.\n *\n * This demonstrates how places can be used for raw memory allocation\n * outside of the task-based programming model.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/experimental/__places/places.cuh>\n\n#include <cstdio>\n\nusing namespace cuda::experimental::places;\n\n__global__ void init_kernel(int* ptr, int n, int value)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    ptr[tid] = value + tid;\n  }\n}\n\n__global__ void check_kernel(int* ptr, int n, int value, int* result)\n{\n  int tid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (tid < n)\n  {\n    if (ptr[tid] != value + tid)\n    {\n      atomicExch(result, 1); // Set error flag\n    }\n  }\n}\n\nvoid test_host_allocation()\n{\n  printf(\"Testing host allocation...\\n\");\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n\n  // Allocate using data_place::host() - stream parameter is ignored for host allocations\n  auto place = data_place::host();\n  EXPECT(!place.allocation_is_stream_ordered()); // Host allocations are blocking\n\n  int* ptr = static_cast<int*>(place.allocate(byte_size));\n  EXPECT(ptr != nullptr);\n\n  // Initialize on host\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = static_cast<int>(i * 2);\n  }\n\n  // Verify\n  for (size_t i = 0; i < n; i++)\n  {\n    EXPECT(ptr[i] == static_cast<int>(i * 2));\n  }\n\n  // Deallocate\n  place.deallocate(ptr, byte_size, nullptr);\n\n  printf(\"  Host allocation test PASSED\\n\");\n}\n\nvoid test_device_allocation()\n{\n  printf(\"Testing device allocation...\\n\");\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n  const int test_value   = 42;\n\n  // Create a stream for the allocation\n  hipStream_t stream;\n  cuda_try(hipStreamCreate(&stream));\n\n  // Allocate using data_place::device(0)\n  auto place = data_place::device(0);\n  EXPECT(place.allocation_is_stream_ordered()); // Device allocations are stream-ordered\n\n  int* d_ptr = static_cast<int*>(place.allocate(byte_size, stream));\n  EXPECT(d_ptr != nullptr);\n\n  // Initialize on device\n  init_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value);\n\n  // Allocate result flag on host for checking\n  int* d_result;\n  cuda_try(hipMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(hipMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  // Check on device\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(d_ptr, n, test_value, d_result);\n\n  // Copy result back\n  int h_result = 0;\n  cuda_try(hipMemcpyAsync(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(hipFreeAsync(d_result, stream));\n  place.deallocate(d_ptr, byte_size, stream);\n\n  cuda_try(hipStreamSynchronize(stream));\n  cuda_try(hipStreamDestroy(stream));\n\n  printf(\"  Device allocation test PASSED\\n\");\n}\n\nvoid test_managed_allocation()\n{\n  printf(\"Testing managed allocation...\\n\");\n\n  // Check if concurrent managed access is supported\n  int dev;\n  cuda_try(hipGetDevice(&dev));\n  hipDeviceProp_t prop;\n  cuda_try(hipGetDeviceProperties(&prop, dev));\n  if (!prop.concurrentManagedAccess)\n  {\n    printf(\"  Concurrent CPU/GPU access not supported, skipping managed test.\\n\");\n    return;\n  }\n\n  const size_t n         = 1024;\n  const size_t byte_size = n * sizeof(int);\n  const int test_value   = 100;\n\n  hipStream_t stream;\n  cuda_try(hipStreamCreate(&stream));\n\n  // Allocate using data_place::managed()\n  auto place = data_place::managed();\n  EXPECT(!place.allocation_is_stream_ordered()); // Managed allocations are immediate (stream ignored)\n\n  int* ptr = static_cast<int*>(place.allocate(byte_size));\n  EXPECT(ptr != nullptr);\n\n  // Initialize on host (managed memory is accessible from both CPU and GPU)\n  for (size_t i = 0; i < n; i++)\n  {\n    ptr[i] = test_value + static_cast<int>(i);\n  }\n\n  // Read back on device and verify\n  int* d_result;\n  cuda_try(hipMallocAsync(&d_result, sizeof(int), stream));\n  cuda_try(hipMemsetAsync(d_result, 0, sizeof(int), stream));\n\n  check_kernel<<<(n + 255) / 256, 256, 0, stream>>>(ptr, n, test_value, d_result);\n\n  int h_result = 0;\n  cuda_try(hipMemcpyAsync(&h_result, d_result, sizeof(int), hipMemcpyDeviceToHost, stream));\n  cuda_try(hipStreamSynchronize(stream));\n\n  EXPECT(h_result == 0); // No errors\n\n  // Cleanup\n  cuda_try(hipFreeAsync(d_result, stream));\n  cuda_try(hipStreamSynchronize(stream));\n  place.deallocate(ptr, byte_size);\n\n  cuda_try(hipStreamDestroy(stream));\n\n  printf(\"  Managed allocation test PASSED\\n\");\n}\n\nint main()\n{\n  printf(\"=== Testing data_place direct allocation (no context) ===\\n\\n\");\n\n  test_host_allocation();\n  test_device_allocation();\n  test_managed_allocation();\n\n  printf(\"\\n=== All tests PASSED ===\\n\");\n  return 0;\n}\n", "source": "/kaggle/working/cccl/cudax/test/places/data_place_alloc.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/__driver/driver_api.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/__utility/ensure_current_device.cuh>\n#include <cuda/experimental/kernel.cuh>\n#include <cuda/experimental/library.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __device__ int global_data;\n//\n// extern \"C\" __managed__ int managed_data;\n//\n// extern \"C\" __global__ void kernel(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n\nconstexpr char library_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel\n.const .align 4 .u32 const_data;\n.global .align 4 .u32 global_data;\n.global .attribute(.managed) .align 4 .u32 managed_data;\n// _ZZ6kernelE6shared has been demoted\n\n.visible .entry kernel(\n\t.param .u64 kernel_param_0,\n\t.param .u32 kernel_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ6kernelE6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_param_0];\n\tld.param.u32 \t%r3, [kernel_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ6kernelE6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n)\";\n\nC2H_CCCLRT_TEST(\"Library\", \"[library]\")\n{\n  constexpr char kernel_name[]         = \"kernel\";\n  constexpr char global_symbol_name[]  = \"global_data\";\n  constexpr char const_symbol_name[]   = \"const_data\";\n  constexpr char managed_symbol_name[] = \"managed_data\";\n\n  CUlibrary lib1_native = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n  CUlibrary lib2_native = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  const cuda::device_ref device{0};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::library::value_type, CUlibrary>);\n  }\n\n  // Construction from native handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cudax::library::from_native_handle(CUlibrary{})), cudax::library>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1_native == lib.get());\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::library>);\n  }\n\n  // Constructor into moved-from state\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_constructible_v<cudax::library, cudax::no_init_t>);\n    cudax::library lib{cudax::no_init};\n    CUDAX_REQUIRE(lib.get() == CUlibrary{});\n\n    // lib is in a moved-from state\n  }\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_copy_constructible_v<cudax::library>);\n  }\n\n  // Move constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_move_constructible_v<cudax::library>);\n\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1.get() == lib1_native);\n\n    cudax::library lib2{cuda::std::move(lib1)};\n    CUDAX_REQUIRE(lib1.get() == CUlibrary{});\n    CUDAX_REQUIRE(lib2.get() == lib1_native);\n\n    // lib1 is in a moved-from state\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Copy assignment\n  {\n    STATIC_REQUIRE(!cuda::std::is_copy_assignable_v<cudax::library>);\n  }\n\n  // Move assignment\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_move_assignable_v<cudax::library>);\n\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1.get() == lib1_native);\n\n    cudax::library lib2{cudax::no_init};\n    CUDAX_REQUIRE(lib2.get() == CUlibrary{});\n\n    lib2 = cuda::std::move(lib1);\n    CUDAX_REQUIRE(lib1.get() == CUlibrary{});\n    CUDAX_REQUIRE(lib2.get() == lib1_native);\n\n    // lib1 is in a moved-from state\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Has kernel\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_kernel(kernel_name)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_kernel(kernel_name));\n    CUDAX_REQUIRE(!lib.has_kernel(\"non_existent_kernel\"));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().kernel<void(int*, int)>(kernel_name)),\n                           cudax::kernel_ref<void(int*, int)>>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    auto kernel        = lib.kernel<void(int*, int)>(kernel_name);\n\n    CUkernel kernel_handle;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetKernelNoThrow(kernel_handle, lib1_native, kernel_name) == cudaSuccess);\n    CUDAX_REQUIRE(kernel.get() == kernel_handle);\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Has global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_global(global_symbol_name, device)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_global(global_symbol_name, device));\n    CUDAX_REQUIRE(lib.has_global(const_symbol_name, device));\n    CUDAX_REQUIRE(!lib.has_global(\"non_existent_global\", device));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().global(global_symbol_name, device)),\n                           cudax::library_symbol_info>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n\n    // Test global_symbol_name\n    {\n      auto global_sym = lib.global(global_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      CUdeviceptr global_symbol_ptr;\n      cuda::std::size_t global_symbol_size;\n      CUDAX_REQUIRE(::cuda::__driver::__libraryGetGlobalNoThrow(\n                      global_symbol_ptr, global_symbol_size, lib1_native, global_symbol_name)\n                    == cudaSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(global_sym.ptr) == global_symbol_ptr);\n      CUDAX_REQUIRE(global_sym.size == global_symbol_size);\n      CUDAX_REQUIRE(global_sym.size == sizeof(int));\n    }\n\n    // Test const_symbol_name\n    {\n      auto const_sym = lib.global(const_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      CUdeviceptr const_symbol_ptr;\n      cuda::std::size_t const_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(const_symbol_ptr, const_symbol_size, lib1_native, const_symbol_name)\n        == cudaSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(const_sym.ptr) == const_symbol_ptr);\n      CUDAX_REQUIRE(const_sym.size == const_symbol_size);\n      CUDAX_REQUIRE(const_sym.size == sizeof(int));\n    }\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Has managed symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_managed(managed_symbol_name)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_managed(managed_symbol_name));\n    CUDAX_REQUIRE(!lib.has_managed(\"non_existent_managed\"));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get managed symbol\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().managed(managed_symbol_name)),\n                                        cudax::library_symbol_info>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    auto managed_sym   = lib.managed(managed_symbol_name);\n\n    CUdeviceptr managed_symbol_ptr;\n    cuda::std::size_t managed_symbol_size;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetManagedNoThrow(\n                    managed_symbol_ptr, managed_symbol_size, lib1_native, managed_symbol_name)\n                  == cudaSuccess);\n\n    CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(managed_sym.ptr) == managed_symbol_ptr);\n    CUDAX_REQUIRE(managed_sym.size == managed_symbol_size);\n    CUDAX_REQUIRE(managed_sym.size == sizeof(int));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().get()), CUlibrary>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.get() == lib1_native);\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Release handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().release()), CUlibrary>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.get() == lib1_native);\n\n    CUlibrary released_handle = lib.release();\n    CUDAX_REQUIRE(released_handle == lib1_native);\n    CUDAX_REQUIRE(lib.get() == CUlibrary{});\n\n    // lib is in a moved-from state\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    cudax::library lib2 = cudax::library::from_native_handle(lib2_native);\n\n    CUDAX_REQUIRE(lib1 == lib1);\n    CUDAX_REQUIRE(lib1 != lib2);\n\n    (void) lib1.release(); // prevent library unload in destructor\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Destructor\n  {\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    cudax::library lib2 = cudax::library::from_native_handle(lib2_native);\n\n    // lib1 and lib2 will be destroyed here, which will unload the libraries\n  }\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/__driver/driver_api.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/__utility/ensure_current_device.cuh>\n#include <cuda/experimental/kernel.cuh>\n#include <cuda/experimental/library.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __device__ int global_data;\n//\n// extern \"C\" __managed__ int managed_data;\n//\n// extern \"C\" __global__ void kernel(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n\nconstexpr char library_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel\n.const .align 4 .u32 const_data;\n.global .align 4 .u32 global_data;\n.global .attribute(.managed) .align 4 .u32 managed_data;\n// _ZZ6kernelE6shared has been demoted\n\n.visible .entry kernel(\n\t.param .u64 kernel_param_0,\n\t.param .u32 kernel_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ6kernelE6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_param_0];\n\tld.param.u32 \t%r3, [kernel_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ6kernelE6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n)\";\n\nC2H_CCCLRT_TEST(\"Library\", \"[library]\")\n{\n  constexpr char kernel_name[]         = \"kernel\";\n  constexpr char global_symbol_name[]  = \"global_data\";\n  constexpr char const_symbol_name[]   = \"const_data\";\n  constexpr char managed_symbol_name[] = \"managed_data\";\n\n  hipLibrary_t lib1_native = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n  hipLibrary_t lib2_native = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  const cuda::device_ref device{0};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::library::value_type, hipLibrary_t>);\n  }\n\n  // Construction from native handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cudax::library::from_native_handle(hipLibrary_t{})), cudax::library>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1_native == lib.get());\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::library>);\n  }\n\n  // Constructor into moved-from state\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_constructible_v<cudax::library, cudax::no_init_t>);\n    cudax::library lib{cudax::no_init};\n    CUDAX_REQUIRE(lib.get() == hipLibrary_t{});\n\n    // lib is in a moved-from state\n  }\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_copy_constructible_v<cudax::library>);\n  }\n\n  // Move constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_move_constructible_v<cudax::library>);\n\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1.get() == lib1_native);\n\n    cudax::library lib2{cuda::std::move(lib1)};\n    CUDAX_REQUIRE(lib1.get() == hipLibrary_t{});\n    CUDAX_REQUIRE(lib2.get() == lib1_native);\n\n    // lib1 is in a moved-from state\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Copy assignment\n  {\n    STATIC_REQUIRE(!cuda::std::is_copy_assignable_v<cudax::library>);\n  }\n\n  // Move assignment\n  {\n    STATIC_REQUIRE(cuda::std::is_nothrow_move_assignable_v<cudax::library>);\n\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib1.get() == lib1_native);\n\n    cudax::library lib2{cudax::no_init};\n    CUDAX_REQUIRE(lib2.get() == hipLibrary_t{});\n\n    lib2 = cuda::std::move(lib1);\n    CUDAX_REQUIRE(lib1.get() == hipLibrary_t{});\n    CUDAX_REQUIRE(lib2.get() == lib1_native);\n\n    // lib1 is in a moved-from state\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Has kernel\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_kernel(kernel_name)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_kernel(kernel_name));\n    CUDAX_REQUIRE(!lib.has_kernel(\"non_existent_kernel\"));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().kernel<void(int*, int)>(kernel_name)),\n                           cudax::kernel_ref<void(int*, int)>>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    auto kernel        = lib.kernel<void(int*, int)>(kernel_name);\n\n    hipKernel_t kernel_handle;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetKernelNoThrow(kernel_handle, lib1_native, kernel_name) == hipSuccess);\n    CUDAX_REQUIRE(kernel.get() == kernel_handle);\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Has global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_global(global_symbol_name, device)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_global(global_symbol_name, device));\n    CUDAX_REQUIRE(lib.has_global(const_symbol_name, device));\n    CUDAX_REQUIRE(!lib.has_global(\"non_existent_global\", device));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().global(global_symbol_name, device)),\n                           cudax::library_symbol_info>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n\n    // Test global_symbol_name\n    {\n      auto global_sym = lib.global(global_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      hipDeviceptr_t global_symbol_ptr;\n      cuda::std::size_t global_symbol_size;\n      CUDAX_REQUIRE(::cuda::__driver::__libraryGetGlobalNoThrow(\n                      global_symbol_ptr, global_symbol_size, lib1_native, global_symbol_name)\n                    == hipSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(global_sym.ptr) == global_symbol_ptr);\n      CUDAX_REQUIRE(global_sym.size == global_symbol_size);\n      CUDAX_REQUIRE(global_sym.size == sizeof(int));\n    }\n\n    // Test const_symbol_name\n    {\n      auto const_sym = lib.global(const_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      hipDeviceptr_t const_symbol_ptr;\n      cuda::std::size_t const_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(const_symbol_ptr, const_symbol_size, lib1_native, const_symbol_name)\n        == hipSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(const_sym.ptr) == const_symbol_ptr);\n      CUDAX_REQUIRE(const_sym.size == const_symbol_size);\n      CUDAX_REQUIRE(const_sym.size == sizeof(int));\n    }\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Has managed symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().has_managed(managed_symbol_name)), bool>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.has_managed(managed_symbol_name));\n    CUDAX_REQUIRE(!lib.has_managed(\"non_existent_managed\"));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get managed symbol\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().managed(managed_symbol_name)),\n                                        cudax::library_symbol_info>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    auto managed_sym   = lib.managed(managed_symbol_name);\n\n    hipDeviceptr_t managed_symbol_ptr;\n    cuda::std::size_t managed_symbol_size;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetManagedNoThrow(\n                    managed_symbol_ptr, managed_symbol_size, lib1_native, managed_symbol_name)\n                  == hipSuccess);\n\n    CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(managed_sym.ptr) == managed_symbol_ptr);\n    CUDAX_REQUIRE(managed_sym.size == managed_symbol_size);\n    CUDAX_REQUIRE(managed_sym.size == sizeof(int));\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().get()), hipLibrary_t>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.get() == lib1_native);\n\n    (void) lib.release(); // prevent library unload in destructor\n  }\n\n  // Release handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library>().release()), hipLibrary_t>);\n\n    cudax::library lib = cudax::library::from_native_handle(lib1_native);\n    CUDAX_REQUIRE(lib.get() == lib1_native);\n\n    hipLibrary_t released_handle = lib.release();\n    CUDAX_REQUIRE(released_handle == lib1_native);\n    CUDAX_REQUIRE(lib.get() == hipLibrary_t{});\n\n    // lib is in a moved-from state\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    cudax::library lib2 = cudax::library::from_native_handle(lib2_native);\n\n    CUDAX_REQUIRE(lib1 == lib1);\n    CUDAX_REQUIRE(lib1 != lib2);\n\n    (void) lib1.release(); // prevent library unload in destructor\n    (void) lib2.release(); // prevent library unload in destructor\n  }\n\n  // Destructor\n  {\n    cudax::library lib1 = cudax::library::from_native_handle(lib1_native);\n    cudax::library lib2 = cudax::library::from_native_handle(lib2_native);\n\n    // lib1 and lib2 will be destroyed here, which will unload the libraries\n  }\n}\n", "source": "/kaggle/working/cccl/cudax/test/library/library.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <cuda/__driver/driver_api.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/__utility/ensure_current_device.cuh>\n#include <cuda/experimental/kernel.cuh>\n#include <cuda/experimental/library.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __device__ int global_data;\n//\n// extern \"C\" __managed__ int managed_data;\n//\n// extern \"C\" __global__ void kernel(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n\nconstexpr char library_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel\n.const .align 4 .u32 const_data;\n.global .align 4 .u32 global_data;\n.global .attribute(.managed) .align 4 .u32 managed_data;\n// _ZZ6kernelE6shared has been demoted\n\n.visible .entry kernel(\n\t.param .u64 kernel_param_0,\n\t.param .u32 kernel_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ6kernelE6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_param_0];\n\tld.param.u32 \t%r3, [kernel_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ6kernelE6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n)\";\n\nC2H_CCCLRT_TEST(\"Library reference\", \"[library_ref]\")\n{\n  constexpr char kernel_name[]         = \"kernel\";\n  constexpr char global_symbol_name[]  = \"global_data\";\n  constexpr char const_symbol_name[]   = \"const_data\";\n  constexpr char managed_symbol_name[] = \"managed_data\";\n\n  CUlibrary lib1 = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n  CUlibrary lib2 = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  const cuda::device_ref device{0};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::library_ref::value_type, CUlibrary>);\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::library_ref>);\n  }\n\n  // Constructor from library handle\n  {\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::library_ref, CUlibrary>);\n    STATIC_REQUIRE(cuda::std::is_convertible_v<CUlibrary, cudax::library_ref>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref.get());\n  }\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_trivially_copy_constructible_v<cudax::library_ref>);\n\n    cudax::library_ref lib_ref1{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref1.get());\n\n    cudax::library_ref lib_ref2{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref2.get());\n    CUDAX_REQUIRE(lib_ref1.get() == lib_ref2.get());\n  }\n\n  // Has kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_kernel(kernel_name)), bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_kernel(kernel_name));\n    CUDAX_REQUIRE(!lib_ref.has_kernel(\"non_existent_kernel\"));\n  }\n\n  // Get kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().kernel<void(int*, int)>(kernel_name)),\n                           cudax::kernel_ref<void(int*, int)>>);\n\n    cudax::library_ref lib_ref{lib1};\n    auto kernel = lib_ref.kernel<void(int*, int)>(kernel_name);\n\n    CUkernel kernel_handle;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetKernelNoThrow(kernel_handle, lib1, kernel_name) == cudaSuccess);\n    CUDAX_REQUIRE(kernel.get() == kernel_handle);\n  }\n\n  // Has global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_global(global_symbol_name, device)),\n                           bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_global(global_symbol_name, device));\n    CUDAX_REQUIRE(lib_ref.has_global(const_symbol_name, device));\n    CUDAX_REQUIRE(!lib_ref.has_global(\"non_existent_global\", device));\n  }\n\n  // Get global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().global(global_symbol_name, device)),\n                           cudax::library_symbol_info>);\n\n    cudax::library_ref lib_ref{lib1};\n\n    // Test global_symbol_name\n    {\n      auto global_sym = lib_ref.global(global_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      CUdeviceptr global_symbol_ptr;\n      cuda::std::size_t global_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(global_symbol_ptr, global_symbol_size, lib1, global_symbol_name)\n        == cudaSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(global_sym.ptr) == global_symbol_ptr);\n      CUDAX_REQUIRE(global_sym.size == global_symbol_size);\n      CUDAX_REQUIRE(global_sym.size == sizeof(int));\n    }\n\n    // Test const_symbol_name\n    {\n      auto const_sym = lib_ref.global(const_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      CUdeviceptr const_symbol_ptr;\n      cuda::std::size_t const_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(const_symbol_ptr, const_symbol_size, lib1, const_symbol_name)\n        == cudaSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(const_sym.ptr) == const_symbol_ptr);\n      CUDAX_REQUIRE(const_sym.size == const_symbol_size);\n      CUDAX_REQUIRE(const_sym.size == sizeof(int));\n    }\n  }\n\n  // Has managed symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_managed(managed_symbol_name)), bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_managed(managed_symbol_name));\n    CUDAX_REQUIRE(!lib_ref.has_managed(\"non_existent_managed\"));\n  }\n\n  // Get managed symbol\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().managed(managed_symbol_name)),\n                                        cudax::library_symbol_info>);\n\n    cudax::library_ref lib_ref{lib1};\n    auto managed_sym = lib_ref.managed(managed_symbol_name);\n\n    CUdeviceptr managed_symbol_ptr;\n    cuda::std::size_t managed_symbol_size;\n    CUDAX_REQUIRE(\n      ::cuda::__driver::__libraryGetManagedNoThrow(managed_symbol_ptr, managed_symbol_size, lib1, managed_symbol_name)\n      == cudaSuccess);\n\n    CUDAX_REQUIRE(reinterpret_cast<CUdeviceptr>(managed_sym.ptr) == managed_symbol_ptr);\n    CUDAX_REQUIRE(managed_sym.size == managed_symbol_size);\n    CUDAX_REQUIRE(managed_sym.size == sizeof(int));\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().get()), CUlibrary>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref.get());\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::library_ref lib_ref1{lib1};\n    cudax::library_ref lib_ref2{lib2};\n\n    CUDAX_REQUIRE(lib_ref1 == lib_ref1);\n    CUDAX_REQUIRE(lib_ref1 != lib_ref2);\n  }\n\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib1) == cudaSuccess);\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib2) == cudaSuccess);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <cuda/__driver/driver_api.h>\n#include <cuda/devices>\n#include <cuda/std/cstddef>\n#include <cuda/std/optional>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/__utility/ensure_current_device.cuh>\n#include <cuda/experimental/kernel.cuh>\n#include <cuda/experimental/library.cuh>\n\n#include <testing.cuh>\n\n// extern \"C\" __constant__ int const_data;\n//\n// extern \"C\" __device__ int global_data;\n//\n// extern \"C\" __managed__ int managed_data;\n//\n// extern \"C\" __global__ void kernel(int* array, int n)\n// {\n//   __shared__ int shared[32];\n//   int tid = blockDim.x * blockIdx.x + threadIdx.x;\n//   if (tid < n)\n//   {\n//     shared[threadIdx.x] = array[tid];\n//     __syncthreads();\n//     array[tid] = shared[threadIdx.x + 1 % 32] + const_data;\n//   }\n// }\n\nconstexpr char library_src[] = R\"(\n//\n// Generated by NVIDIA NVVM Compiler\n//\n// Compiler Build ID: CL-32267302\n// Cuda compilation tools, release 12.0, V12.0.140\n// Based on NVVM 7.0.1\n//\n\n.version 8.0\n.target sm_75\n.address_size 64\n\n\t// .globl\tkernel\n.const .align 4 .u32 const_data;\n.global .align 4 .u32 global_data;\n.global .attribute(.managed) .align 4 .u32 managed_data;\n// _ZZ6kernelE6shared has been demoted\n\n.visible .entry kernel(\n\t.param .u64 kernel_param_0,\n\t.param .u32 kernel_param_1\n)\n{\n\t.reg .pred \t%p<2>;\n\t.reg .b32 \t%r<13>;\n\t.reg .b64 \t%rd<5>;\n\t// demoted variable\n\t.shared .align 4 .b8 _ZZ6kernelE6shared[128];\n\n\tld.param.u64 \t%rd1, [kernel_param_0];\n\tld.param.u32 \t%r3, [kernel_param_1];\n\tmov.u32 \t%r4, %ntid.x;\n\tmov.u32 \t%r5, %ctaid.x;\n\tmov.u32 \t%r1, %tid.x;\n\tmad.lo.s32 \t%r2, %r4, %r5, %r1;\n\tsetp.ge.s32 \t%p1, %r2, %r3;\n\t@%p1 bra \t$L__BB0_2;\n\n\tcvta.to.global.u64 \t%rd2, %rd1;\n\tmul.wide.s32 \t%rd3, %r2, 4;\n\tadd.s64 \t%rd4, %rd2, %rd3;\n\tld.global.u32 \t%r6, [%rd4];\n\tshl.b32 \t%r7, %r1, 2;\n\tmov.u32 \t%r8, _ZZ6kernelE6shared;\n\tadd.s32 \t%r9, %r8, %r7;\n\tst.shared.u32 \t[%r9], %r6;\n\tbar.sync \t0;\n\tld.const.u32 \t%r10, [const_data];\n\tld.shared.u32 \t%r11, [%r9+4];\n\tadd.s32 \t%r12, %r10, %r11;\n\tst.global.u32 \t[%rd4], %r12;\n\n$L__BB0_2:\n\tret;\n\n}\n)\";\n\nC2H_CCCLRT_TEST(\"Library reference\", \"[library_ref]\")\n{\n  constexpr char kernel_name[]         = \"kernel\";\n  constexpr char global_symbol_name[]  = \"global_data\";\n  constexpr char const_symbol_name[]   = \"const_data\";\n  constexpr char managed_symbol_name[] = \"managed_data\";\n\n  hipLibrary_t lib1 = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n  hipLibrary_t lib2 = ::cuda::__driver::__libraryLoadData(library_src, nullptr, nullptr, 0, nullptr, nullptr, 0);\n\n  const cuda::device_ref device{0};\n\n  // Types\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<typename cudax::library_ref::value_type, hipLibrary_t>);\n  }\n\n  // Default constructor\n  {\n    STATIC_REQUIRE(!cuda::std::is_default_constructible_v<cudax::library_ref>);\n  }\n\n  // Constructor from library handle\n  {\n    STATIC_REQUIRE(cuda::std::is_constructible_v<cudax::library_ref, hipLibrary_t>);\n    STATIC_REQUIRE(cuda::std::is_convertible_v<hipLibrary_t, cudax::library_ref>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref.get());\n  }\n\n  // Copy constructor\n  {\n    STATIC_REQUIRE(cuda::std::is_trivially_copy_constructible_v<cudax::library_ref>);\n\n    cudax::library_ref lib_ref1{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref1.get());\n\n    cudax::library_ref lib_ref2{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref2.get());\n    CUDAX_REQUIRE(lib_ref1.get() == lib_ref2.get());\n  }\n\n  // Has kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_kernel(kernel_name)), bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_kernel(kernel_name));\n    CUDAX_REQUIRE(!lib_ref.has_kernel(\"non_existent_kernel\"));\n  }\n\n  // Get kernel\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().kernel<void(int*, int)>(kernel_name)),\n                           cudax::kernel_ref<void(int*, int)>>);\n\n    cudax::library_ref lib_ref{lib1};\n    auto kernel = lib_ref.kernel<void(int*, int)>(kernel_name);\n\n    hipKernel_t kernel_handle;\n    CUDAX_REQUIRE(::cuda::__driver::__libraryGetKernelNoThrow(kernel_handle, lib1, kernel_name) == hipSuccess);\n    CUDAX_REQUIRE(kernel.get() == kernel_handle);\n  }\n\n  // Has global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_global(global_symbol_name, device)),\n                           bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_global(global_symbol_name, device));\n    CUDAX_REQUIRE(lib_ref.has_global(const_symbol_name, device));\n    CUDAX_REQUIRE(!lib_ref.has_global(\"non_existent_global\", device));\n  }\n\n  // Get global symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().global(global_symbol_name, device)),\n                           cudax::library_symbol_info>);\n\n    cudax::library_ref lib_ref{lib1};\n\n    // Test global_symbol_name\n    {\n      auto global_sym = lib_ref.global(global_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      hipDeviceptr_t global_symbol_ptr;\n      cuda::std::size_t global_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(global_symbol_ptr, global_symbol_size, lib1, global_symbol_name)\n        == hipSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(global_sym.ptr) == global_symbol_ptr);\n      CUDAX_REQUIRE(global_sym.size == global_symbol_size);\n      CUDAX_REQUIRE(global_sym.size == sizeof(int));\n    }\n\n    // Test const_symbol_name\n    {\n      auto const_sym = lib_ref.global(const_symbol_name, device);\n\n      cuda::__ensure_current_context context_guard{device};\n\n      hipDeviceptr_t const_symbol_ptr;\n      cuda::std::size_t const_symbol_size;\n      CUDAX_REQUIRE(\n        ::cuda::__driver::__libraryGetGlobalNoThrow(const_symbol_ptr, const_symbol_size, lib1, const_symbol_name)\n        == hipSuccess);\n\n      CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(const_sym.ptr) == const_symbol_ptr);\n      CUDAX_REQUIRE(const_sym.size == const_symbol_size);\n      CUDAX_REQUIRE(const_sym.size == sizeof(int));\n    }\n  }\n\n  // Has managed symbol\n  {\n    STATIC_REQUIRE(\n      cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().has_managed(managed_symbol_name)), bool>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib_ref.has_managed(managed_symbol_name));\n    CUDAX_REQUIRE(!lib_ref.has_managed(\"non_existent_managed\"));\n  }\n\n  // Get managed symbol\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().managed(managed_symbol_name)),\n                                        cudax::library_symbol_info>);\n\n    cudax::library_ref lib_ref{lib1};\n    auto managed_sym = lib_ref.managed(managed_symbol_name);\n\n    hipDeviceptr_t managed_symbol_ptr;\n    cuda::std::size_t managed_symbol_size;\n    CUDAX_REQUIRE(\n      ::cuda::__driver::__libraryGetManagedNoThrow(managed_symbol_ptr, managed_symbol_size, lib1, managed_symbol_name)\n      == hipSuccess);\n\n    CUDAX_REQUIRE(reinterpret_cast<hipDeviceptr_t>(managed_sym.ptr) == managed_symbol_ptr);\n    CUDAX_REQUIRE(managed_sym.size == managed_symbol_size);\n    CUDAX_REQUIRE(managed_sym.size == sizeof(int));\n  }\n\n  // Get handle\n  {\n    STATIC_REQUIRE(cuda::std::is_same_v<decltype(cuda::std::declval<cudax::library_ref>().get()), hipLibrary_t>);\n\n    cudax::library_ref lib_ref{lib1};\n    CUDAX_REQUIRE(lib1 == lib_ref.get());\n  }\n\n  // Equality/Inequality comparison\n  {\n    cudax::library_ref lib_ref1{lib1};\n    cudax::library_ref lib_ref2{lib2};\n\n    CUDAX_REQUIRE(lib_ref1 == lib_ref1);\n    CUDAX_REQUIRE(lib_ref1 != lib_ref2);\n  }\n\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib1) == hipSuccess);\n  CUDAX_REQUIRE(::cuda::__driver::__libraryUnloadNoThrow(lib2) == hipSuccess);\n}\n", "source": "/kaggle/working/cccl/cudax/test/library/library_ref.cu"}
{"input": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n#include <thrust/reduce.h>\n\n#include <cuda/memory_pool>\n#include <cuda/std/cstdint>\n#include <cuda/std/functional>\n#include <cuda/std/span>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/container.cuh>\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/memory_resource.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include \"testing.cuh\"\n\n#if _CCCL_COMPILER(GCC, >=, 13)\n_CCCL_DIAG_SUPPRESS_GCC(\"-Wself-move\")\n#endif // _CCCL_COMPILER(GCC, >=, 13)\n_CCCL_DIAG_SUPPRESS_CLANG(\"-Wself-move\")\n\nstruct do_not_construct\n{\n  do_not_construct()\n  {\n    CUDAX_CHECK(false);\n  }\n};\n\nstruct non_trivial\n{\n  int val_      = 0;\n  non_trivial() = default;\n  __host__ __device__ constexpr non_trivial(const int val) noexcept\n      : val_(val)\n  {}\n\n  __host__ __device__ constexpr friend bool operator==(const non_trivial& lhs, const non_trivial& rhs)\n  {\n    return lhs.val_ == rhs.val_;\n  }\n};\n\nstruct my_property\n{\n  using value_type = int;\n};\nconstexpr int get_property(\n  const cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible, my_property>&, my_property)\n{\n  return 42;\n}\nconstexpr int get_property(const cuda::device_memory_pool_ref&, my_property)\n{\n  return 42;\n}\n\n__global__ void kernel(::cuda::std::span<int> data)\n{\n  // Touch the memory to be sure it's accessible\n  CUDAX_CHECK(data.size() == 1024);\n  data[0] = 42;\n}\n\n__global__ void const_kernel(::cuda::std::span<const int> data)\n{\n  // Touch the memory to be sure it's accessible\n  CUDAX_CHECK(data.size() == 1024);\n}\n\nC2H_TEST_LIST(\"uninitialized_buffer\", \"[container]\", char, short, int, long, long long, float, double, do_not_construct)\n{\n  using uninitialized_buffer = cuda::experimental::uninitialized_buffer<TestType, cuda::mr::device_accessible>;\n  static_assert(!cuda::std::is_default_constructible<uninitialized_buffer>::value);\n  static_assert(!cuda::std::is_copy_constructible<uninitialized_buffer>::value);\n  static_assert(!cuda::std::is_copy_assignable<uninitialized_buffer>::value);\n\n  cuda::device_memory_pool_ref resource = cuda::device_default_memory_pool(cuda::device_ref{0});\n\n  SECTION(\"construction\")\n  {\n    static_assert(!cuda::std::is_copy_constructible<uninitialized_buffer>::value);\n    {\n      uninitialized_buffer from_count{resource, 42};\n      CUDAX_CHECK(from_count.data() != nullptr);\n      CUDAX_CHECK(from_count.size() == 42);\n    }\n    {\n      uninitialized_buffer input{resource, 42};\n      const TestType* ptr = input.data();\n\n      uninitialized_buffer from_rvalue{cuda::std::move(input)};\n      CUDAX_CHECK(from_rvalue.data() == ptr);\n      CUDAX_CHECK(from_rvalue.size() == 42);\n\n      // Ensure that we properly reset the input buffer\n      CUDAX_CHECK(input.data() == nullptr);\n      CUDAX_CHECK(input.size() == 0);\n    }\n  }\n\n  SECTION(\"conversion\")\n  {\n    cuda::experimental::uninitialized_buffer<TestType, cuda::mr::device_accessible, my_property> input{resource, 42};\n    const TestType* ptr = input.data();\n\n    uninitialized_buffer from_rvalue{cuda::std::move(input)};\n    CUDAX_CHECK(from_rvalue.data() == ptr);\n    CUDAX_CHECK(from_rvalue.size() == 42);\n\n    // Ensure that we properly reset the input buffer\n    CUDAX_CHECK(input.data() == nullptr);\n    CUDAX_CHECK(input.size() == 0);\n  }\n\n  SECTION(\"assignment\")\n  {\n    static_assert(!cuda::std::is_copy_assignable<uninitialized_buffer>::value);\n    {\n      cuda::mr::legacy_pinned_memory_resource other_resource{};\n      uninitialized_buffer input{other_resource, 42};\n      uninitialized_buffer buf{resource, 1337};\n      const auto* old_ptr       = buf.data();\n      const auto* old_input_ptr = input.data();\n\n      buf = cuda::std::move(input);\n      CUDAX_CHECK(buf.data() != old_ptr);\n      CUDAX_CHECK(buf.data() == old_input_ptr);\n      CUDAX_CHECK(buf.size() == 42);\n      CUDAX_CHECK(buf.size_bytes() == 42 * sizeof(TestType));\n      CUDAX_CHECK(buf.memory_resource() == other_resource);\n\n      CUDAX_CHECK(input.data() == nullptr);\n      CUDAX_CHECK(input.size() == 0);\n      CUDAX_CHECK(input.size_bytes() == 0);\n    }\n\n    { // Ensure self move assignment does not do anything\n      uninitialized_buffer buf{resource, 1337};\n      const auto* old_ptr = buf.data();\n\n      buf = cuda::std::move(buf);\n      CUDAX_CHECK(buf.data() == old_ptr);\n      CUDAX_CHECK(buf.size() == 1337);\n      CUDAX_CHECK(buf.size_bytes() == 1337 * sizeof(TestType));\n    }\n  }\n\n  SECTION(\"access\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    static_assert(cuda::std::is_same<decltype(buf.begin()), TestType*>::value);\n    static_assert(cuda::std::is_same<decltype(buf.end()), TestType*>::value);\n    static_assert(cuda::std::is_same<decltype(buf.data()), TestType*>::value);\n    CUDAX_CHECK(buf.data() != nullptr);\n    CUDAX_CHECK(buf.size() == 42);\n    CUDAX_CHECK(buf.size_bytes() == 42 * sizeof(TestType));\n    CUDAX_CHECK(buf.begin() == buf.data());\n    CUDAX_CHECK(buf.end() == buf.begin() + buf.size());\n    CUDAX_CHECK(buf.memory_resource() == resource);\n\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).begin()), TestType const*>::value);\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).end()), TestType const*>::value);\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).data()), TestType const*>::value);\n    CUDAX_CHECK(cuda::std::as_const(buf).data() != nullptr);\n    CUDAX_CHECK(cuda::std::as_const(buf).size() == 42);\n    CUDAX_CHECK(cuda::std::as_const(buf).begin() == buf.data());\n    CUDAX_CHECK(cuda::std::as_const(buf).end() == buf.begin() + buf.size());\n    CUDAX_CHECK(cuda::std::as_const(buf).memory_resource() == resource);\n  }\n\n  SECTION(\"properties\")\n  {\n    static_assert(cuda::has_property<cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible>,\n                                     cuda::mr::device_accessible>);\n    static_assert(\n      cuda::has_property<cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible, my_property>,\n                         my_property>);\n  }\n\n  SECTION(\"conversion to span\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    const cuda::std::span<TestType> as_span{buf};\n    CUDAX_CHECK(as_span.data() == buf.data());\n    CUDAX_CHECK(as_span.size() == 42);\n  }\n\n  SECTION(\"Actually use memory\")\n  {\n    if constexpr (!cuda::std::is_same_v<TestType, do_not_construct>)\n    {\n      uninitialized_buffer buf{resource, 42};\n      thrust::fill(thrust::device, buf.begin(), buf.end(), TestType{2});\n      const auto res = thrust::reduce(thrust::device, buf.begin(), buf.end(), TestType{0}, cuda::std::plus<int>());\n      CUDAX_CHECK(res == TestType{84});\n    }\n  }\n\n  SECTION(\"Replace allocation of current buffer\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    const TestType* old_ptr = buf.data();\n    const size_t old_size   = buf.size();\n\n    {\n      const uninitialized_buffer old_buf = buf.__replace_allocation(1337);\n      CUDAX_CHECK(buf.data() != old_ptr);\n      CUDAX_CHECK(buf.size() == 1337);\n\n      CUDAX_CHECK(old_buf.data() == old_ptr);\n      CUDAX_CHECK(old_buf.size() == old_size);\n    }\n  }\n\n  SECTION(\"destroy\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    buf.destroy();\n    CUDAX_CHECK(buf.data() == nullptr);\n    CUDAX_CHECK(buf.size() == 0);\n\n    buf = uninitialized_buffer{resource, 42};\n    CUDAX_CHECK(buf.data() != nullptr);\n    CUDAX_CHECK(buf.size() == 42);\n  }\n}\n\nC2H_TEST(\"uninitialized_buffer is usable with cudax::launch\", \"[container]\")\n{\n  SECTION(\"non-const\")\n  {\n    const int grid_size = 4;\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n      cuda::device_default_memory_pool(cuda::device_ref{0}), 1024};\n    auto configuration = cuda::make_config(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n\n    cudax::stream stream{cuda::device_ref{0}};\n\n    cudax::launch(stream, configuration, kernel, buffer);\n  }\n\n  SECTION(\"const\")\n  {\n    const int grid_size = 4;\n    const cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n      cuda::device_default_memory_pool(cuda::device_ref{0}), 1024};\n    auto configuration = cuda::make_config(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n\n    cudax::stream stream{cuda::device_ref{0}};\n\n    cudax::launch(stream, configuration, const_kernel, buffer);\n  }\n}\n\n// A test resource that keeps track of the number of resources are\n// currently alive.\nstruct test_device_memory_pool_ref : cuda::device_memory_pool_ref\n{\n  static int count;\n\n  test_device_memory_pool_ref()\n      : cuda::device_memory_pool_ref(cuda::device_default_memory_pool(cuda::device_ref{0}))\n  {\n    ++count;\n  }\n\n  test_device_memory_pool_ref(const test_device_memory_pool_ref& other)\n      : cuda::device_memory_pool_ref{other}\n  {\n    ++count;\n  }\n\n  ~test_device_memory_pool_ref()\n  {\n    --count;\n  }\n};\n\nint test_device_memory_pool_ref::count = 0;\n\nC2H_TEST(\"uninitialized_buffer's memory resource does not dangle\", \"[container]\")\n{\n  cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n    cuda::device_default_memory_pool(cuda::device_ref{0}), 0};\n\n  {\n    CHECK(test_device_memory_pool_ref::count == 0);\n\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> src_buffer{test_device_memory_pool_ref{}, 1024};\n\n    CHECK(test_device_memory_pool_ref::count == 1);\n\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> dst_buffer{src_buffer.memory_resource(), 1024};\n\n    CHECK(test_device_memory_pool_ref::count == 2);\n\n    buffer = ::cuda::std::move(dst_buffer);\n  }\n\n  CHECK(test_device_memory_pool_ref::count == 1);\n}\n", "output": "//===----------------------------------------------------------------------===//\n//\n// Part of CUDA Experimental in CUDA C++ Core Libraries,\n// under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/execution_policy.h>\n#include <thrust/fill.h>\n#include <thrust/reduce.h>\n\n#include <cuda/memory_pool>\n#include <cuda/std/cstdint>\n#include <cuda/std/functional>\n#include <cuda/std/span>\n#include <cuda/std/type_traits>\n#include <cuda/std/utility>\n\n#include <cuda/experimental/container.cuh>\n#include <cuda/experimental/launch.cuh>\n#include <cuda/experimental/memory_resource.cuh>\n#include <cuda/experimental/stream.cuh>\n\n#include \"testing.cuh\"\n\n#if _CCCL_COMPILER(GCC, >=, 13)\n_CCCL_DIAG_SUPPRESS_GCC(\"-Wself-move\")\n#endif // _CCCL_COMPILER(GCC, >=, 13)\n_CCCL_DIAG_SUPPRESS_CLANG(\"-Wself-move\")\n\nstruct do_not_construct\n{\n  do_not_construct()\n  {\n    CUDAX_CHECK(false);\n  }\n};\n\nstruct non_trivial\n{\n  int val_      = 0;\n  non_trivial() = default;\n  __host__ __device__ constexpr non_trivial(const int val) noexcept\n      : val_(val)\n  {}\n\n  __host__ __device__ constexpr friend bool operator==(const non_trivial& lhs, const non_trivial& rhs)\n  {\n    return lhs.val_ == rhs.val_;\n  }\n};\n\nstruct my_property\n{\n  using value_type = int;\n};\nconstexpr int get_property(\n  const cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible, my_property>&, my_property)\n{\n  return 42;\n}\nconstexpr int get_property(const cuda::device_memory_pool_ref&, my_property)\n{\n  return 42;\n}\n\n__global__ void kernel(::cuda::std::span<int> data)\n{\n  // Touch the memory to be sure it's accessible\n  CUDAX_CHECK(data.size() == 1024);\n  data[0] = 42;\n}\n\n__global__ void const_kernel(::cuda::std::span<const int> data)\n{\n  // Touch the memory to be sure it's accessible\n  CUDAX_CHECK(data.size() == 1024);\n}\n\nC2H_TEST_LIST(\"uninitialized_buffer\", \"[container]\", char, short, int, long, long long, float, double, do_not_construct)\n{\n  using uninitialized_buffer = cuda::experimental::uninitialized_buffer<TestType, cuda::mr::device_accessible>;\n  static_assert(!cuda::std::is_default_constructible<uninitialized_buffer>::value);\n  static_assert(!cuda::std::is_copy_constructible<uninitialized_buffer>::value);\n  static_assert(!cuda::std::is_copy_assignable<uninitialized_buffer>::value);\n\n  cuda::device_memory_pool_ref resource = cuda::device_default_memory_pool(cuda::device_ref{0});\n\n  SECTION(\"construction\")\n  {\n    static_assert(!cuda::std::is_copy_constructible<uninitialized_buffer>::value);\n    {\n      uninitialized_buffer from_count{resource, 42};\n      CUDAX_CHECK(from_count.data() != nullptr);\n      CUDAX_CHECK(from_count.size() == 42);\n    }\n    {\n      uninitialized_buffer input{resource, 42};\n      const TestType* ptr = input.data();\n\n      uninitialized_buffer from_rvalue{cuda::std::move(input)};\n      CUDAX_CHECK(from_rvalue.data() == ptr);\n      CUDAX_CHECK(from_rvalue.size() == 42);\n\n      // Ensure that we properly reset the input buffer\n      CUDAX_CHECK(input.data() == nullptr);\n      CUDAX_CHECK(input.size() == 0);\n    }\n  }\n\n  SECTION(\"conversion\")\n  {\n    cuda::experimental::uninitialized_buffer<TestType, cuda::mr::device_accessible, my_property> input{resource, 42};\n    const TestType* ptr = input.data();\n\n    uninitialized_buffer from_rvalue{cuda::std::move(input)};\n    CUDAX_CHECK(from_rvalue.data() == ptr);\n    CUDAX_CHECK(from_rvalue.size() == 42);\n\n    // Ensure that we properly reset the input buffer\n    CUDAX_CHECK(input.data() == nullptr);\n    CUDAX_CHECK(input.size() == 0);\n  }\n\n  SECTION(\"assignment\")\n  {\n    static_assert(!cuda::std::is_copy_assignable<uninitialized_buffer>::value);\n    {\n      cuda::mr::legacy_pinned_memory_resource other_resource{};\n      uninitialized_buffer input{other_resource, 42};\n      uninitialized_buffer buf{resource, 1337};\n      const auto* old_ptr       = buf.data();\n      const auto* old_input_ptr = input.data();\n\n      buf = cuda::std::move(input);\n      CUDAX_CHECK(buf.data() != old_ptr);\n      CUDAX_CHECK(buf.data() == old_input_ptr);\n      CUDAX_CHECK(buf.size() == 42);\n      CUDAX_CHECK(buf.size_bytes() == 42 * sizeof(TestType));\n      CUDAX_CHECK(buf.memory_resource() == other_resource);\n\n      CUDAX_CHECK(input.data() == nullptr);\n      CUDAX_CHECK(input.size() == 0);\n      CUDAX_CHECK(input.size_bytes() == 0);\n    }\n\n    { // Ensure self move assignment does not do anything\n      uninitialized_buffer buf{resource, 1337};\n      const auto* old_ptr = buf.data();\n\n      buf = cuda::std::move(buf);\n      CUDAX_CHECK(buf.data() == old_ptr);\n      CUDAX_CHECK(buf.size() == 1337);\n      CUDAX_CHECK(buf.size_bytes() == 1337 * sizeof(TestType));\n    }\n  }\n\n  SECTION(\"access\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    static_assert(cuda::std::is_same<decltype(buf.begin()), TestType*>::value);\n    static_assert(cuda::std::is_same<decltype(buf.end()), TestType*>::value);\n    static_assert(cuda::std::is_same<decltype(buf.data()), TestType*>::value);\n    CUDAX_CHECK(buf.data() != nullptr);\n    CUDAX_CHECK(buf.size() == 42);\n    CUDAX_CHECK(buf.size_bytes() == 42 * sizeof(TestType));\n    CUDAX_CHECK(buf.begin() == buf.data());\n    CUDAX_CHECK(buf.end() == buf.begin() + buf.size());\n    CUDAX_CHECK(buf.memory_resource() == resource);\n\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).begin()), TestType const*>::value);\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).end()), TestType const*>::value);\n    static_assert(cuda::std::is_same<decltype(cuda::std::as_const(buf).data()), TestType const*>::value);\n    CUDAX_CHECK(cuda::std::as_const(buf).data() != nullptr);\n    CUDAX_CHECK(cuda::std::as_const(buf).size() == 42);\n    CUDAX_CHECK(cuda::std::as_const(buf).begin() == buf.data());\n    CUDAX_CHECK(cuda::std::as_const(buf).end() == buf.begin() + buf.size());\n    CUDAX_CHECK(cuda::std::as_const(buf).memory_resource() == resource);\n  }\n\n  SECTION(\"properties\")\n  {\n    static_assert(cuda::has_property<cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible>,\n                                     cuda::mr::device_accessible>);\n    static_assert(\n      cuda::has_property<cuda::experimental::uninitialized_buffer<int, cuda::mr::device_accessible, my_property>,\n                         my_property>);\n  }\n\n  SECTION(\"conversion to span\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    const cuda::std::span<TestType> as_span{buf};\n    CUDAX_CHECK(as_span.data() == buf.data());\n    CUDAX_CHECK(as_span.size() == 42);\n  }\n\n  SECTION(\"Actually use memory\")\n  {\n    if constexpr (!cuda::std::is_same_v<TestType, do_not_construct>)\n    {\n      uninitialized_buffer buf{resource, 42};\n      thrust::fill(thrust::device, buf.begin(), buf.end(), TestType{2});\n      const auto res = thrust::reduce(thrust::device, buf.begin(), buf.end(), TestType{0}, cuda::std::plus<int>());\n      CUDAX_CHECK(res == TestType{84});\n    }\n  }\n\n  SECTION(\"Replace allocation of current buffer\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    const TestType* old_ptr = buf.data();\n    const size_t old_size   = buf.size();\n\n    {\n      const uninitialized_buffer old_buf = buf.__replace_allocation(1337);\n      CUDAX_CHECK(buf.data() != old_ptr);\n      CUDAX_CHECK(buf.size() == 1337);\n\n      CUDAX_CHECK(old_buf.data() == old_ptr);\n      CUDAX_CHECK(old_buf.size() == old_size);\n    }\n  }\n\n  SECTION(\"destroy\")\n  {\n    uninitialized_buffer buf{resource, 42};\n    buf.destroy();\n    CUDAX_CHECK(buf.data() == nullptr);\n    CUDAX_CHECK(buf.size() == 0);\n\n    buf = uninitialized_buffer{resource, 42};\n    CUDAX_CHECK(buf.data() != nullptr);\n    CUDAX_CHECK(buf.size() == 42);\n  }\n}\n\nC2H_TEST(\"uninitialized_buffer is usable with cudax::launch\", \"[container]\")\n{\n  SECTION(\"non-const\")\n  {\n    const int grid_size = 4;\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n      cuda::device_default_memory_pool(cuda::device_ref{0}), 1024};\n    auto configuration = cuda::make_config(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n\n    cudax::stream stream{cuda::device_ref{0}};\n\n    cudax::launch(stream, configuration, kernel, buffer);\n  }\n\n  SECTION(\"const\")\n  {\n    const int grid_size = 4;\n    const cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n      cuda::device_default_memory_pool(cuda::device_ref{0}), 1024};\n    auto configuration = cuda::make_config(cuda::grid_dims(grid_size), cuda::block_dims<256>());\n\n    cudax::stream stream{cuda::device_ref{0}};\n\n    cudax::launch(stream, configuration, const_kernel, buffer);\n  }\n}\n\n// A test resource that keeps track of the number of resources are\n// currently alive.\nstruct test_device_memory_pool_ref : cuda::device_memory_pool_ref\n{\n  static int count;\n\n  test_device_memory_pool_ref()\n      : cuda::device_memory_pool_ref(cuda::device_default_memory_pool(cuda::device_ref{0}))\n  {\n    ++count;\n  }\n\n  test_device_memory_pool_ref(const test_device_memory_pool_ref& other)\n      : cuda::device_memory_pool_ref{other}\n  {\n    ++count;\n  }\n\n  ~test_device_memory_pool_ref()\n  {\n    --count;\n  }\n};\n\nint test_device_memory_pool_ref::count = 0;\n\nC2H_TEST(\"uninitialized_buffer's memory resource does not dangle\", \"[container]\")\n{\n  cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> buffer{\n    cuda::device_default_memory_pool(cuda::device_ref{0}), 0};\n\n  {\n    CHECK(test_device_memory_pool_ref::count == 0);\n\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> src_buffer{test_device_memory_pool_ref{}, 1024};\n\n    CHECK(test_device_memory_pool_ref::count == 1);\n\n    cudax::uninitialized_buffer<int, ::cuda::mr::device_accessible> dst_buffer{src_buffer.memory_resource(), 1024};\n\n    CHECK(test_device_memory_pool_ref::count == 2);\n\n    buffer = ::cuda::std::move(dst_buffer);\n  }\n\n  CHECK(test_device_memory_pool_ref::count == 1);\n}\n", "source": "/kaggle/working/cccl/cudax/test/containers/uninitialized_buffer.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"gpuinflate.hpp\"\n#include \"io/utilities/block_utils.cuh\"\n\n#include <cudf/detail/utilities/cuda.cuh>\n#include <cudf/detail/utilities/grid_1d.cuh>\n#include <cudf/detail/utilities/integer_utils.hpp>\n\n#include <rmm/cuda_stream_view.hpp>\n\n#include <cub/cub.cuh>\n\nnamespace cudf::io::detail {\nconstexpr int32_t batch_size    = (1 << 5);\nconstexpr int32_t batch_count   = (1 << 2);\nconstexpr int32_t prefetch_size = (1 << 9);  // 512B, in 32B chunks\n\nvoid __device__ busy_wait(size_t cycles)\n{\n  clock_t start = clock();\n  for (;;) {\n    clock_t const now     = clock();\n    clock_t const elapsed = now > start ? now - start : now + (0xffff'ffff - start);\n    if (elapsed >= cycles) return;\n  }\n}\n\n/**\n * @brief Describes a single LZ77 symbol (single entry in batch)\n */\nstruct unsnap_batch_s {\n  int32_t len;  // 1..64 = Number of bytes\n  uint32_t\n    offset;  // copy distance if greater than zero or negative of literal offset in byte stream\n};\n\n/**\n * @brief Queue structure used to exchange data between warps\n */\nstruct unsnap_queue_s {\n  unsnap_queue_s() = default;  // required to compile on ctk-12.2 + aarch64\n\n  uint32_t prefetch_wrpos;         ///< Prefetcher write position\n  uint32_t prefetch_rdpos;         ///< Prefetch consumer read position\n  int32_t prefetch_end;            ///< Prefetch enable flag (nonzero stops prefetcher)\n  int32_t batch_len[batch_count];  ///< Length of each batch - <0:end, 0:not ready, >0:symbol count\n  unsnap_batch_s batch[batch_count * batch_size];  ///< LZ77 batch data\n  uint8_t buf[prefetch_size];                      ///< Prefetch buffer\n};\n\n/**\n * @brief snappy decompression state\n */\nstruct unsnap_state_s {\n  CUDF_HOST_DEVICE constexpr unsnap_state_s() noexcept {\n  }  // required to compile on ctk-12.2 + aarch64\n\n  uint8_t const* base{};           ///< base ptr of compressed stream\n  uint8_t const* end{};            ///< end of compressed stream\n  uint32_t uncompressed_size{};    ///< uncompressed stream size\n  uint32_t bytes_left{};           ///< remaining bytes to decompress\n  int32_t error{};                 ///< current error status\n  uint32_t tstart{};               ///< start time for perf logging\n  volatile unsnap_queue_s q{};     ///< queue for cross-warp communication\n  device_span<uint8_t const> src;  ///< input for current block\n  device_span<uint8_t> dst;        ///< output for current block\n};\n\ninline __device__ volatile uint8_t& byte_access(unsnap_state_s* s, uint32_t pos)\n{\n  return s->q.buf[pos & (prefetch_size - 1)];\n}\n\n/**\n * @brief prefetches data for the symbol decoding stage\n *\n * @param s decompression state\n * @param t warp lane id\n */\n__device__ void snappy_prefetch_bytestream(unsnap_state_s* s, int t)\n{\n  uint8_t const* base = s->base;\n  auto end            = (uint32_t)(s->end - base);\n  auto align_bytes    = (uint32_t)(0x20 - (0x1f & reinterpret_cast<uintptr_t>(base)));\n  int32_t pos         = min(align_bytes, end);\n  int32_t blen;\n  // Start by prefetching up to the next a 32B-aligned location\n  if (t < pos) { s->q.buf[t] = base[t]; }\n  blen = 0;\n  do {\n    __syncwarp();\n    if (!t) {\n      uint32_t minrdpos;\n      s->q.prefetch_wrpos = pos;\n      minrdpos            = pos - min(pos, prefetch_size - 32u);\n      blen                = (int)min(32u, end - pos);\n      for (;;) {\n        uint32_t rdpos = s->q.prefetch_rdpos;\n        if (rdpos >= minrdpos) break;\n        if (s->q.prefetch_end) {\n          blen = 0;\n          break;\n        }\n        busy_wait(20);\n      }\n    }\n    blen = shuffle(blen);\n    if (t < blen) { byte_access(s, pos + t) = base[pos + t]; }\n    pos += blen;\n  } while (blen > 0);\n}\n\n/**\n * @brief Lookup table for get_len3_mask()\n *\n * Indexed by a 10-bit pattern, contains the corresponding 4-bit mask of\n * 3-byte code lengths in the lower 4 bits, along with the total number of\n * bytes used for coding the four lengths in the upper 4 bits.\n * The upper 4-bit value could also be obtained by 8+__popc(mask4)\n *\n *   for (uint32_t k = 0; k < 1024; k++)\n *   {\n *       for (uint32_t i = 0, v = 0, b = k, n = 0; i < 4; i++)\n *       {\n *           v |= (b & 1) << i;\n *           n += (b & 1) + 2;\n *           b >>= (b & 1) + 2;\n *       }\n *       k_len3lut[k] = v | (n << 4);\n *   }\n */\nstatic const uint8_t __device__ __constant__ k_len3lut[1 << 10] = {\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xa3, 0x80, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xa3, 0x94, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xa3, 0x94, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xb7, 0x98, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xb7, 0x94, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xb7, 0x98, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xb7, 0x94, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xa3, 0x80, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xa3, 0xac, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xa3, 0xac, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xb7, 0x98, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xb7, 0xac, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xb7, 0x98, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xb7, 0xac, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xbb, 0x80, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xbb, 0x94, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xbb, 0x94, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xb7, 0x98, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xb7, 0x94, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xb7, 0x98, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xb7, 0x94, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xbb, 0x80, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xbb, 0xac, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xbb, 0xac, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xb7, 0x98, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xb7, 0xac, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xb7, 0x98, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xb7, 0xac, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xa3, 0x80, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xa3, 0x94, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xa3, 0x94, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xcf, 0x98, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xcf, 0x94, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xcf, 0x98, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xcf, 0x94, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xa3, 0x80, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xa3, 0xac, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xa3, 0xac, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xcf, 0x98, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xcf, 0xac, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xcf, 0x98, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xcf, 0xac, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xbb, 0x80, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xbb, 0x94, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xbb, 0x94, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xcf, 0x98, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xcf, 0x94, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xcf, 0x98, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xcf, 0x94, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xbb, 0x80, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xbb, 0xac, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xbb, 0xac, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xcf, 0x98, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xcf, 0xac, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xcf, 0x98, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xcf, 0xac, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf};\n\n/**\n * @brief Returns a 32-bit mask where 1 means 3-byte code length and 0 means 2-byte\n * code length, given an input mask of up to 96 bits.\n *\n * Implemented by doing 8 consecutive lookups, building the result 4-bit at a time\n */\ninline __device__ uint32_t get_len3_mask(uint32_t v0, uint32_t v1, uint32_t v2)\n{\n  uint32_t m, v, m4, n;\n  v  = v0;\n  m4 = k_len3lut[v & 0x3ff];\n  m  = m4 & 0xf;\n  n  = m4 >> 4;  // 8..12\n  v  = v0 >> n;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 4;\n  n += m4 >> 4;  // 16..24\n  v  = __funnelshift_r(v0, v1, n);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 8;\n  n += m4 >> 4;  // 24..36\n  v >>= (m4 >> 4);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 12;\n  n  = (n + (m4 >> 4)) & 0x1f;  // (32..48) % 32 = 0..16\n  v1 = __funnelshift_r(v1, v2, n);\n  v2 >>= n;\n  v  = v1;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 16;\n  n  = m4 >> 4;  // 8..12\n  v  = v1 >> n;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 20;\n  n += m4 >> 4;  // 16..24\n  v  = __funnelshift_r(v1, v2, n);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 24;\n  n += m4 >> 4;  // 24..36\n  v >>= (m4 >> 4);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 28;\n  return m;\n}\n\n/**\n * @brief Returns a 32-bit mask where each 2-bit pair contains the symbol length\n * minus 2, given two input masks each containing bit0 or bit1 of the corresponding\n * code length minus 2 for up to 32 bytes\n */\ninline __device__ uint32_t get_len5_mask(uint32_t v0, uint32_t v1)\n{\n  uint32_t m;\n  m = (v1 & 1) * 2 + (v0 & 1);\n  v0 >>= (m + 2);\n  v1 >>= (m + 1);\n  for (uint32_t i = 1; i < 16; i++) {\n    uint32_t m2 = (v1 & 2) | (v0 & 1);\n    uint32_t n  = m2 + 2;\n    m |= m2 << (i * 2);\n    v0 >>= n;\n    v1 >>= n;\n  }\n  return m;\n}\n\n/**\n * @brief decode symbols and output LZ77 batches (single-warp)\n *\n * @param s decompression state\n * @param t warp lane id\n */\n__device__ void snappy_decode_symbols(unsnap_state_s* s, uint32_t t)\n{\n  uint32_t cur        = 0;\n  auto end            = static_cast<uint32_t>(s->end - s->base);\n  uint32_t bytes_left = s->uncompressed_size;\n  uint32_t dst_pos    = 0;\n  int32_t batch       = 0;\n\n  for (;;) {\n    int32_t batch_len;\n    volatile unsnap_batch_s* b;\n\n    // Wait for prefetcher\n    if (t == 0) {\n      s->q.prefetch_rdpos = cur;\n#pragma unroll(1)  // We don't want unrolling here\n      while (s->q.prefetch_wrpos < min(cur + 5 * batch_size, end)) {\n        busy_wait(10);\n      }\n      b = &s->q.batch[batch * batch_size];\n    }\n    // Process small symbols in parallel: for data that does not get good compression,\n    // the stream will consist of a large number of short literals (1-byte or 2-byte)\n    // followed by short repeat runs. This results in many 2-byte or 3-byte symbols\n    // that can all be decoded in parallel once we know the symbol length.\n    {\n      uint32_t v0, v1, v2, len3_mask, cur_t, is_long_sym, short_sym_mask;\n      uint32_t b0;\n      cur            = shuffle(cur);\n      cur_t          = cur + t;\n      b0             = byte_access(s, cur_t);\n      v0             = ballot((b0 == 4) || (b0 & 2));\n      b0             = byte_access(s, cur_t + 32);\n      v1             = ballot((b0 == 4) || (b0 & 2));\n      b0             = byte_access(s, cur_t + 64);\n      v2             = ballot((b0 == 4) || (b0 & 2));\n      len3_mask      = shuffle((t == 0) ? get_len3_mask(v0, v1, v2) : 0);\n      cur_t          = cur + 2 * t + __popc(len3_mask & ((1 << t) - 1));\n      b0             = byte_access(s, cur_t);\n      is_long_sym    = ((b0 & ~4) != 0) && (((b0 + 1) & 2) == 0);\n      short_sym_mask = ballot(is_long_sym);\n      batch_len      = 0;\n      b = reinterpret_cast<volatile unsnap_batch_s*>(shuffle(reinterpret_cast<uintptr_t>(b)));\n      if (!(short_sym_mask & 1)) {\n        batch_len = shuffle((t == 0) ? (short_sym_mask) ? __ffs(short_sym_mask) - 1 : 32 : 0);\n        if (batch_len != 0) {\n          uint32_t blen = 0;\n          int32_t ofs   = 0;\n          // Check for bad copy element (offset == 0).\n          bool is_copy_offset_bad = false;\n          if (t < batch_len) {\n            blen               = (b0 & 1) ? ((b0 >> 2) & 7) + 4 : ((b0 >> 2) + 1);\n            ofs                = (b0 & 1) ? ((b0 & 0xe0) << 3) | byte_access(s, cur_t + 1)\n                                 : (b0 & 2) ? byte_access(s, cur_t + 1) | (byte_access(s, cur_t + 2) << 8)\n                                            : -(int32_t)(cur_t + 1);\n            is_copy_offset_bad = (b0 & 3) != 0 && ofs == 0;\n            b[t].len           = blen;\n            b[t].offset        = ofs;\n            ofs += blen;  // for correct out-of-range detection below\n          }\n          blen       = warp_reduce_pos<cudf::detail::warp_size>(blen, t);\n          bytes_left = shuffle(bytes_left);\n          dst_pos    = shuffle(dst_pos);\n          // If `is_copy_offset_bad`, truncate the batch at the first lane with a malformed copy\n          // element (offset == 0) so warp 2 never runs it; the scalar slow path below then re-reads\n          // it, breaks, and the kernel reports FAILURE.\n          short_sym_mask = __ffs(\n            ballot(blen > bytes_left || ofs > (int32_t)(dst_pos + blen) || is_copy_offset_bad));\n          if (short_sym_mask != 0) { batch_len = min(batch_len, short_sym_mask - 1); }\n          if (batch_len != 0) {\n            blen = shuffle(blen, batch_len - 1);\n            cur  = shuffle(cur_t, batch_len - 1) + 2 + ((len3_mask >> (batch_len - 1)) & 1);\n            if (t == 0) {\n              dst_pos += blen;\n              bytes_left -= blen;\n            }\n          }\n        }\n      }\n      // Check if the batch was stopped by a 3-byte or 4-byte literal\n      if (batch_len < batch_size - 2 && shuffle(b0 & ~4, batch_len) == 8) {\n        // If so, run a slower version of the above that can also handle 3/4-byte literal sequences\n        uint32_t batch_add;\n        do {\n          uint32_t clen, mask_t;\n          cur_t     = cur + t;\n          b0        = byte_access(s, cur_t);\n          clen      = (b0 & 3) ? (b0 & 2) ? 1 : 0 : (b0 >> 2);  // symbol length minus 2\n          v0        = ballot(clen & 1);\n          v1        = ballot((clen >> 1) & 1);\n          len3_mask = shuffle((t == 0) ? get_len5_mask(v0, v1) : 0);\n          mask_t    = (1 << (2 * t)) - 1;\n          cur_t     = cur + 2 * t + 2 * __popc((len3_mask & 0xaaaa'aaaa) & mask_t) +\n                  __popc((len3_mask & 0x5555'5555) & mask_t);\n          b0          = byte_access(s, cur_t);\n          is_long_sym = ((b0 & 3) ? ((b0 & 3) == 3) : (b0 > 3 * 4)) || (cur_t >= cur + 32) ||\n                        (batch_len + t >= batch_size);\n          batch_add = __ffs(ballot(is_long_sym)) - 1;\n          if (batch_add != 0) {\n            uint32_t blen           = 0;\n            int32_t ofs             = 0;\n            bool is_copy_offset_bad = false;\n            if (t < batch_add) {\n              blen                    = (b0 & 1) ? ((b0 >> 2) & 7) + 4 : ((b0 >> 2) + 1);\n              ofs                     = (b0 & 1) ? ((b0 & 0xe0) << 3) | byte_access(s, cur_t + 1)\n                                        : (b0 & 2) ? byte_access(s, cur_t + 1) | (byte_access(s, cur_t + 2) << 8)\n                                                   : -(int32_t)(cur_t + 1);\n              is_copy_offset_bad      = (b0 & 3) != 0 && ofs == 0;\n              b[batch_len + t].len    = blen;\n              b[batch_len + t].offset = ofs;\n              ofs += blen;  // for correct out-of-range detection below\n            }\n            blen       = warp_reduce_pos<cudf::detail::warp_size>(blen, t);\n            bytes_left = shuffle(bytes_left);\n            dst_pos    = shuffle(dst_pos);\n            // See comment in the fast-path block above for `is_copy_offset_bad`.\n            short_sym_mask = __ffs(\n              ballot(blen > bytes_left || ofs > (int32_t)(dst_pos + blen) || is_copy_offset_bad));\n            if (short_sym_mask != 0) { batch_add = min(batch_add, short_sym_mask - 1); }\n            if (batch_add != 0) {\n              blen = shuffle(blen, batch_add - 1);\n              cur  = shuffle(cur_t, batch_add - 1) + 2 + ((len3_mask >> ((batch_add - 1) * 2)) & 3);\n              if (t == 0) {\n                dst_pos += blen;\n                bytes_left -= blen;\n              }\n              batch_len += batch_add;\n            }\n          }\n        } while (batch_add >= 6 && batch_len < batch_size - 2);\n      }\n    }\n    if (t == 0) {\n      while (bytes_left > 0 && batch_len < batch_size) {\n        uint32_t blen, offset;\n        uint8_t b0 = byte_access(s, cur);\n        if (b0 & 3) {\n          uint8_t b1 = byte_access(s, cur + 1);\n          if (!(b0 & 2)) {\n            // xxxxxx01.oooooooo: copy with 3-bit length, 11-bit offset\n            offset = ((b0 & 0xe0) << 3) | b1;\n            blen   = ((b0 >> 2) & 7) + 4;\n            cur += 2;\n          } else {\n            // xxxxxx1x: copy with 6-bit length, 2-byte or 4-byte offset\n            offset = b1 | (byte_access(s, cur + 2) << 8);\n            if (b0 & 1)  // 4-byte offset\n            {\n              offset |= (byte_access(s, cur + 3) << 16) | (byte_access(s, cur + 4) << 24);\n              cur += 5;\n            } else {\n              cur += 3;\n            }\n            blen = (b0 >> 2) + 1;\n          }\n          dst_pos += blen;\n          if (offset - 1u >= dst_pos || bytes_left < blen) break;\n          bytes_left -= blen;\n        } else if (b0 < 4 * 4) {\n          // 0000xx00: short literal\n          blen   = (b0 >> 2) + 1;\n          offset = -(int32_t)(cur + 1);\n          cur += 1 + blen;\n          dst_pos += blen;\n          if (bytes_left < blen) break;\n          bytes_left -= blen;\n        } else {\n          // xxxxxx00: literal\n          blen = b0 >> 2;\n          if (blen >= 60) {\n            uint32_t num_bytes = blen - 59;\n            blen               = byte_access(s, cur + 1);\n            if (num_bytes > 1) {\n              blen |= byte_access(s, cur + 2) << 8;\n              if (num_bytes > 2) {\n                blen |= byte_access(s, cur + 3) << 16;\n                if (num_bytes > 3) { blen |= byte_access(s, cur + 4) << 24; }\n              }\n            }\n            cur += num_bytes;\n          }\n          cur += 1;\n          blen += 1;\n          offset = -(int32_t)cur;\n          cur += blen;\n          // Wait for prefetcher\n          s->q.prefetch_rdpos = cur;\n#pragma unroll(1)  // We don't want unrolling here\n          while (s->q.prefetch_wrpos < min(cur + 5 * batch_size, end)) {\n            busy_wait(10);\n          }\n          dst_pos += blen;\n          if (bytes_left < blen) break;\n          bytes_left -= blen;\n        }\n        b[batch_len].len    = blen;\n        b[batch_len].offset = offset;\n        batch_len++;\n      }\n      if (batch_len != 0) {\n        s->q.batch_len[batch] = batch_len;\n        batch                 = (batch + 1) & (batch_count - 1);\n      }\n    }\n    batch_len = shuffle(batch_len);\n    if (t == 0) {\n      while (s->q.batch_len[batch] != 0) {\n        busy_wait(20);\n      }\n    }\n    if (batch_len != batch_size) { break; }\n  }\n  if (!t) {\n    s->q.prefetch_end     = 1;\n    s->q.batch_len[batch] = -1;\n    s->bytes_left         = bytes_left;\n    if (bytes_left != 0) { s->error = -2; }\n  }\n}\n\n/**\n * @brief process LZ77 symbols and output uncompressed stream\n *\n * @param s decompression state\n * @param t thread id within participating group (lane id)\n * @param temp_storage temporary storage used by the algorithm\n *\n * NOTE: No error checks at this stage (WARP0 responsible for not sending offsets and lengths that\n *would result in out-of-bounds accesses)\n */\ntemplate <typename Storage>\n__device__ void snappy_process_symbols(unsnap_state_s* s, int t, Storage& temp_storage)\n{\n  auto const literal_base = s->base;\n  auto out                = s->dst.data();\n  int batch               = 0;\n\n  do {\n    volatile unsnap_batch_s* b = &s->q.batch[batch * batch_size];\n    int32_t batch_len, blen_t, dist_t;\n\n    if (t == 0) {\n      while ((batch_len = s->q.batch_len[batch]) == 0) {\n        busy_wait(20);\n      }\n    } else {\n      batch_len = 0;\n    }\n    batch_len = shuffle(batch_len);\n    if (batch_len <= 0) { break; }\n    if (t < batch_len) {\n      blen_t = b[t].len;\n      dist_t = b[t].offset;\n    } else {\n      blen_t = dist_t = 0;\n    }\n    // Try to combine as many small entries as possible, but try to avoid doing that\n    // if we see a small repeat distance 8 bytes or less\n    if (shuffle(min((uint32_t)dist_t, (uint32_t)shuffle_xor(dist_t, 1))) > 8) {\n      uint32_t n;\n      do {\n        uint32_t bofs      = warp_reduce_pos<cudf::detail::warp_size>(blen_t, t);\n        uint32_t stop_mask = ballot((uint32_t)dist_t < bofs);\n        uint32_t start_mask =\n          cub::WarpReduce<uint32_t>(temp_storage).Sum((bofs < 32 && t < batch_len) ? 1 << bofs : 0);\n        start_mask = shuffle(start_mask);\n        n          = min(min((uint32_t)__popc(start_mask), (uint32_t)(__ffs(stop_mask) - 1u)),\n                (uint32_t)batch_len);\n        if (n != 0) {\n          uint32_t it  = __popc(start_mask & ((2 << t) - 1));\n          uint32_t tr  = t - shuffle(bofs - blen_t, it);\n          int32_t dist = shuffle(dist_t, it);\n          if (it < n) {\n            uint8_t const* src = (dist > 0) ? (out + t - dist) : (literal_base + tr - dist);\n            out[t]             = *src;\n          }\n          out += shuffle(bofs, n - 1);\n          blen_t = shuffle(blen_t, (n + t) & 0x1f);\n          dist_t = shuffle(dist_t, (n + t) & 0x1f);\n          batch_len -= n;\n        }\n      } while (n >= 4);\n    }\n    for (int i = 0; i < batch_len; i++) {\n      int32_t blen  = shuffle(blen_t, i);\n      int32_t dist  = shuffle(dist_t, i);\n      int32_t blen2 = (i + 1 < batch_len) ? shuffle(blen_t, i + 1) : 32;\n      // Try to combine consecutive small entries if they are independent\n      if ((uint32_t)dist >= (uint32_t)blen && blen + blen2 <= 32) {\n        int32_t dist2 = shuffle(dist_t, i + 1);\n        if ((uint32_t)dist2 >= (uint32_t)(blen + blen2)) {\n          int32_t d;\n          if (t < blen) {\n            d = dist;\n          } else {\n            dist = dist2;\n            d    = (dist2 <= 0) ? dist2 + blen : dist2;\n          }\n          blen += blen2;\n          if (t < blen) {\n            uint8_t const* src = (dist > 0) ? (out - d) : (literal_base - d);\n            out[t]             = src[t];\n          }\n          out += blen;\n          i++;\n          continue;\n        }\n      }\n      if (dist > 0) {\n        // Copy\n        uint8_t b0, b1;\n        if (t < blen) {\n          uint32_t pos       = t;\n          uint8_t const* src = out + ((pos >= dist) ? (pos % dist) : pos) - dist;\n          b0                 = *src;\n        }\n        if (32 + t < blen) {\n          uint32_t pos       = 32 + t;\n          uint8_t const* src = out + ((pos >= dist) ? (pos % dist) : pos) - dist;\n          b1                 = *src;\n        }\n        if (t < blen) { out[t] = b0; }\n        if (32 + t < blen) { out[32 + t] = b1; }\n      } else {\n        // Literal\n        uint8_t b0, b1;\n        dist = -dist;\n        while (blen >= 64) {\n          b0          = literal_base[dist + t];\n          b1          = literal_base[dist + 32 + t];\n          out[t]      = b0;\n          out[32 + t] = b1;\n          dist += 64;\n          out += 64;\n          blen -= 64;\n        }\n        if (t < blen) { b0 = literal_base[dist + t]; }\n        if (32 + t < blen) { b1 = literal_base[dist + 32 + t]; }\n        if (t < blen) { out[t] = b0; }\n        if (32 + t < blen) { out[32 + t] = b1; }\n      }\n      out += blen;\n    }\n    __syncwarp();\n    if (t == 0) { s->q.batch_len[batch] = 0; }\n    batch = (batch + 1) & (batch_count - 1);\n  } while (true);\n}\n\n/**\n * @brief Snappy decompression kernel\n * See http://github.com/google/snappy/blob/master/format_description.txt\n *\n * blockDim {128,1,1}\n *\n * @param[in] inputs Source & destination information per block\n * @param[out] outputs Decompression status per block\n */\ntemplate <int block_size>\nCUDF_KERNEL void __launch_bounds__(block_size)\n  unsnap_kernel_no_racecheck(device_span<device_span<uint8_t const> const> inputs,\n                             device_span<device_span<uint8_t> const> outputs,\n                             device_span<codec_exec_result> results)\n{\n  __shared__ __align__(16) unsnap_state_s state_g;\n  __shared__ cub::WarpReduce<uint32_t>::TempStorage temp_storage;\n  int t             = threadIdx.x;\n  unsnap_state_s* s = &state_g;\n  int strm_id       = blockIdx.x;\n\n  if (t < batch_count) { s->q.batch_len[t] = 0; }\n  __syncthreads();\n  if (!t) {\n    s->src         = inputs[strm_id];\n    s->dst         = outputs[strm_id];\n    auto cur       = s->src.data();\n    auto const end = s->src.data() + s->src.size();\n    s->error       = 0;\n    if (cur < end) {\n      // Read uncompressed size (varint), limited to 32-bit\n      uint32_t uncompressed_size = *cur++;\n      if (uncompressed_size > 0x7f) {\n        uint32_t c        = (cur < end) ? *cur++ : 0;\n        uncompressed_size = (uncompressed_size & 0x7f) | (c << 7);\n        if (uncompressed_size >= (0x80 << 7)) {\n          c                 = (cur < end) ? *cur++ : 0;\n          uncompressed_size = (uncompressed_size & ((0x7f << 7) | 0x7f)) | (c << 14);\n          if (uncompressed_size >= (0x80 << 14)) {\n            c = (cur < end) ? *cur++ : 0;\n            uncompressed_size =\n              (uncompressed_size & ((0x7f << 14) | (0x7f << 7) | 0x7f)) | (c << 21);\n            if (uncompressed_size >= (0x80 << 21)) {\n              c = (cur < end) ? *cur++ : 0;\n              if (c < 0x8)\n                uncompressed_size =\n                  (uncompressed_size & ((0x7f << 21) | (0x7f << 14) | (0x7f << 7) | 0x7f)) |\n                  (c << 28);\n              else\n                s->error = -1;\n            }\n          }\n        }\n      }\n      s->uncompressed_size = uncompressed_size;\n      s->bytes_left        = uncompressed_size;\n      s->base              = cur;\n      s->end               = end;\n      if ((cur >= end && uncompressed_size != 0) || (uncompressed_size > s->dst.size())) {\n        s->error = -1;\n      }\n    } else {\n      s->error = -1;\n    }\n    s->q.prefetch_end   = 0;\n    s->q.prefetch_wrpos = 0;\n    s->q.prefetch_rdpos = 0;\n  }\n  __syncthreads();\n  if (!s->error) {\n    if (t < 32) {\n      // WARP0: decode lengths and offsets\n      snappy_decode_symbols(s, t);\n    } else if (t < 64) {\n      // WARP1: prefetch byte stream for WARP0\n      snappy_prefetch_bytestream(s, t & 0x1f);\n    } else if (t < 96) {\n      // WARP2: LZ77\n      snappy_process_symbols(s, t & 0x1f, temp_storage);\n    }\n    __syncthreads();\n  }\n  if (!t) {\n    results[strm_id].bytes_written = s->uncompressed_size - s->bytes_left;\n    results[strm_id].status = (s->error == 0) ? codec_status::SUCCESS : codec_status::FAILURE;\n  }\n}\n\nvoid gpu_unsnap(device_span<device_span<uint8_t const> const> inputs,\n                device_span<device_span<uint8_t> const> outputs,\n                device_span<codec_exec_result> results,\n                rmm::cuda_stream_view stream)\n{\n  dim3 dim_block(128, 1);           // 4 warps per stream, 1 stream per block\n  dim3 dim_grid(inputs.size(), 1);  // TODO: Check max grid dimensions vs max expected count\n\n  unsnap_kernel_no_racecheck<128>\n    <<<dim_grid, dim_block, 0, stream.value()>>>(inputs, outputs, results);\n}\n\n__global__ void get_snappy_uncompressed_size_kernel(\n  device_span<device_span<uint8_t const> const> inputs, device_span<size_t> uncompressed_sizes)\n{\n  auto const idx = cudf::detail::grid_1d::global_thread_id();\n  if (idx >= inputs.size()) return;\n\n  auto cur       = inputs[idx].data();\n  auto const end = inputs[idx].data() + inputs[idx].size();\n\n  constexpr int payload_bits_per_byte = 7;\n  constexpr size_t payload_mask       = (1 << payload_bits_per_byte) - 1;\n  size_t uncompressed_size            = 0;\n  int shift                           = 0;\n  while (cur < end && shift + payload_bits_per_byte <= 8 * sizeof(size_t)) {\n    size_t const byte = *cur++;\n    uncompressed_size |= (byte & payload_mask) << shift;\n    if ((byte & (1 << payload_bits_per_byte)) == 0) {\n      uncompressed_sizes[idx] = uncompressed_size;\n      return;\n    }\n    shift += payload_bits_per_byte;\n  }\n  // Invalid varint\n  uncompressed_sizes[idx] = 0;\n}\n\nvoid get_snappy_uncompressed_size(device_span<device_span<uint8_t const> const> inputs,\n                                  device_span<size_t> uncompressed_sizes,\n                                  rmm::cuda_stream_view stream)\n{\n  int threads_per_block = 128;\n  auto const num_blocks =\n    cudf::util::div_rounding_up_safe<size_t>(inputs.size(), threads_per_block);\n\n  get_snappy_uncompressed_size_kernel<<<num_blocks, threads_per_block, 0, stream.value()>>>(\n    inputs, uncompressed_sizes);\n}\n\n}  // namespace cudf::io::detail\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2018-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"hip/hip_runtime.h\"\n#include \"gpuinflate.hpp\"\n#include \"io/utilities/block_utils.cuh\"\n\n#include <cudf/detail/utilities/cuda.cuh>\n#include <cudf/detail/utilities/grid_1d.cuh>\n#include <cudf/detail/utilities/integer_utils.hpp>\n\n#include <rmm/cuda_stream_view.hpp>\n\n#include <hipcub/hipcub.hpp>\n\nnamespace cudf::io::detail {\nconstexpr int32_t batch_size    = (1 << 5);\nconstexpr int32_t batch_count   = (1 << 2);\nconstexpr int32_t prefetch_size = (1 << 9);  // 512B, in 32B chunks\n\nvoid __device__ busy_wait(size_t cycles)\n{\n  clock_t start = clock();\n  for (;;) {\n    clock_t const now     = clock();\n    clock_t const elapsed = now > start ? now - start : now + (0xffff'ffff - start);\n    if (elapsed >= cycles) return;\n  }\n}\n\n/**\n * @brief Describes a single LZ77 symbol (single entry in batch)\n */\nstruct unsnap_batch_s {\n  int32_t len;  // 1..64 = Number of bytes\n  uint32_t\n    offset;  // copy distance if greater than zero or negative of literal offset in byte stream\n};\n\n/**\n * @brief Queue structure used to exchange data between warps\n */\nstruct unsnap_queue_s {\n  unsnap_queue_s() = default;  // required to compile on ctk-12.2 + aarch64\n\n  uint32_t prefetch_wrpos;         ///< Prefetcher write position\n  uint32_t prefetch_rdpos;         ///< Prefetch consumer read position\n  int32_t prefetch_end;            ///< Prefetch enable flag (nonzero stops prefetcher)\n  int32_t batch_len[batch_count];  ///< Length of each batch - <0:end, 0:not ready, >0:symbol count\n  unsnap_batch_s batch[batch_count * batch_size];  ///< LZ77 batch data\n  uint8_t buf[prefetch_size];                      ///< Prefetch buffer\n};\n\n/**\n * @brief snappy decompression state\n */\nstruct unsnap_state_s {\n  CUDF_HOST_DEVICE constexpr unsnap_state_s() noexcept {\n  }  // required to compile on ctk-12.2 + aarch64\n\n  uint8_t const* base{};           ///< base ptr of compressed stream\n  uint8_t const* end{};            ///< end of compressed stream\n  uint32_t uncompressed_size{};    ///< uncompressed stream size\n  uint32_t bytes_left{};           ///< remaining bytes to decompress\n  int32_t error{};                 ///< current error status\n  uint32_t tstart{};               ///< start time for perf logging\n  volatile unsnap_queue_s q{};     ///< queue for cross-warp communication\n  device_span<uint8_t const> src;  ///< input for current block\n  device_span<uint8_t> dst;        ///< output for current block\n};\n\ninline __device__ volatile uint8_t& byte_access(unsnap_state_s* s, uint32_t pos)\n{\n  return s->q.buf[pos & (prefetch_size - 1)];\n}\n\n/**\n * @brief prefetches data for the symbol decoding stage\n *\n * @param s decompression state\n * @param t warp lane id\n */\n__device__ void snappy_prefetch_bytestream(unsnap_state_s* s, int t)\n{\n  uint8_t const* base = s->base;\n  auto end            = (uint32_t)(s->end - base);\n  auto align_bytes    = (uint32_t)(0x20 - (0x1f & reinterpret_cast<uintptr_t>(base)));\n  int32_t pos         = min(align_bytes, end);\n  int32_t blen;\n  // Start by prefetching up to the next a 32B-aligned location\n  if (t < pos) { s->q.buf[t] = base[t]; }\n  blen = 0;\n  do {\n    __syncwarp();\n    if (!t) {\n      uint32_t minrdpos;\n      s->q.prefetch_wrpos = pos;\n      minrdpos            = pos - min(pos, prefetch_size - 32u);\n      blen                = (int)min(32u, end - pos);\n      for (;;) {\n        uint32_t rdpos = s->q.prefetch_rdpos;\n        if (rdpos >= minrdpos) break;\n        if (s->q.prefetch_end) {\n          blen = 0;\n          break;\n        }\n        busy_wait(20);\n      }\n    }\n    blen = shuffle(blen);\n    if (t < blen) { byte_access(s, pos + t) = base[pos + t]; }\n    pos += blen;\n  } while (blen > 0);\n}\n\n/**\n * @brief Lookup table for get_len3_mask()\n *\n * Indexed by a 10-bit pattern, contains the corresponding 4-bit mask of\n * 3-byte code lengths in the lower 4 bits, along with the total number of\n * bytes used for coding the four lengths in the upper 4 bits.\n * The upper 4-bit value could also be obtained by 8+__popc(mask4)\n *\n *   for (uint32_t k = 0; k < 1024; k++)\n *   {\n *       for (uint32_t i = 0, v = 0, b = k, n = 0; i < 4; i++)\n *       {\n *           v |= (b & 1) << i;\n *           n += (b & 1) + 2;\n *           b >>= (b & 1) + 2;\n *       }\n *       k_len3lut[k] = v | (n << 4);\n *   }\n */\nstatic const uint8_t __device__ __constant__ k_len3lut[1 << 10] = {\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xa3, 0x80, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xa3, 0x94, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xa3, 0x94, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xb7, 0x98, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xb7, 0x94, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xb7, 0x98, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xb7, 0x94, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xa3, 0x80, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xa3, 0xac, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xa3, 0xac, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xb7, 0x98, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xb7, 0xac, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xb7, 0x98, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xb7, 0xac, 0xb7, 0xa6, 0xb7, 0xa6, 0xb7,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xbb, 0x80, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xbb, 0x94, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xbb, 0x94, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xb7, 0x98, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xb7, 0x94, 0xb7, 0x92, 0xb7, 0x92, 0xb7,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xb7, 0x98, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xb7, 0x94, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xbb, 0x80, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xbb, 0xac, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xbb, 0xac, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xb7, 0x98, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xb7, 0xac, 0xb7, 0xaa, 0xb7, 0xaa, 0xb7,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xb7, 0x98, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xb7, 0xac, 0xb7, 0xbe, 0xb7, 0xbe, 0xb7,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xa3, 0x80, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xa3, 0x94, 0xa3, 0x92, 0xa3, 0x92, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xa3, 0x94, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xcf, 0x98, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xcf, 0x94, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xcf, 0x98, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x94, 0xa5, 0x94, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x94, 0xcf, 0x94, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xa3, 0x80, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xa3, 0xac, 0xa3, 0xaa, 0xa3, 0xaa, 0xa3,\n  0x80, 0xa5, 0x80, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x80, 0xa3, 0x80, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xa3, 0xac, 0xa3, 0xa6, 0xa3, 0xa6, 0xa3,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xcf, 0x98, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xcf, 0xac, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0x98, 0xa5, 0x98, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0x98, 0xcf, 0x98, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0xac, 0xa5, 0xac, 0xa5, 0xa6, 0xa5, 0xa6, 0xa5, 0xac, 0xcf, 0xac, 0xcf, 0xa6, 0xcf, 0xa6, 0xcf,\n  0x80, 0x91, 0x80, 0x91, 0x92, 0x91, 0x92, 0x91, 0x80, 0xbb, 0x80, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xbb, 0x94, 0xbb, 0x92, 0xbb, 0x92, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xbb, 0x94, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0x91, 0x98, 0x91, 0x92, 0x91, 0x92, 0x91, 0x98, 0xcf, 0x98, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x94, 0x91, 0x94, 0x91, 0x92, 0x91, 0x92, 0x91, 0x94, 0xcf, 0x94, 0xcf, 0x92, 0xcf, 0x92, 0xcf,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xcf, 0x98, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0x94, 0xbd, 0x94, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x94, 0xcf, 0x94, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0x80, 0xa9, 0x80, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x80, 0xbb, 0x80, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xbb, 0xac, 0xbb, 0xaa, 0xbb, 0xaa, 0xbb,\n  0x80, 0xbd, 0x80, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x80, 0xbb, 0x80, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xbb, 0xac, 0xbb, 0xbe, 0xbb, 0xbe, 0xbb,\n  0x98, 0xa9, 0x98, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0x98, 0xcf, 0x98, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0xac, 0xa9, 0xac, 0xa9, 0xaa, 0xa9, 0xaa, 0xa9, 0xac, 0xcf, 0xac, 0xcf, 0xaa, 0xcf, 0xaa, 0xcf,\n  0x98, 0xbd, 0x98, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0x98, 0xcf, 0x98, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf,\n  0xac, 0xbd, 0xac, 0xbd, 0xbe, 0xbd, 0xbe, 0xbd, 0xac, 0xcf, 0xac, 0xcf, 0xbe, 0xcf, 0xbe, 0xcf};\n\n/**\n * @brief Returns a 32-bit mask where 1 means 3-byte code length and 0 means 2-byte\n * code length, given an input mask of up to 96 bits.\n *\n * Implemented by doing 8 consecutive lookups, building the result 4-bit at a time\n */\ninline __device__ uint32_t get_len3_mask(uint32_t v0, uint32_t v1, uint32_t v2)\n{\n  uint32_t m, v, m4, n;\n  v  = v0;\n  m4 = k_len3lut[v & 0x3ff];\n  m  = m4 & 0xf;\n  n  = m4 >> 4;  // 8..12\n  v  = v0 >> n;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 4;\n  n += m4 >> 4;  // 16..24\n  v  = __funnelshift_r(v0, v1, n);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 8;\n  n += m4 >> 4;  // 24..36\n  v >>= (m4 >> 4);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 12;\n  n  = (n + (m4 >> 4)) & 0x1f;  // (32..48) % 32 = 0..16\n  v1 = __funnelshift_r(v1, v2, n);\n  v2 >>= n;\n  v  = v1;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 16;\n  n  = m4 >> 4;  // 8..12\n  v  = v1 >> n;\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 20;\n  n += m4 >> 4;  // 16..24\n  v  = __funnelshift_r(v1, v2, n);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 24;\n  n += m4 >> 4;  // 24..36\n  v >>= (m4 >> 4);\n  m4 = k_len3lut[v & 0x3ff];\n  m |= (m4 & 0xf) << 28;\n  return m;\n}\n\n/**\n * @brief Returns a 32-bit mask where each 2-bit pair contains the symbol length\n * minus 2, given two input masks each containing bit0 or bit1 of the corresponding\n * code length minus 2 for up to 32 bytes\n */\ninline __device__ uint32_t get_len5_mask(uint32_t v0, uint32_t v1)\n{\n  uint32_t m;\n  m = (v1 & 1) * 2 + (v0 & 1);\n  v0 >>= (m + 2);\n  v1 >>= (m + 1);\n  for (uint32_t i = 1; i < 16; i++) {\n    uint32_t m2 = (v1 & 2) | (v0 & 1);\n    uint32_t n  = m2 + 2;\n    m |= m2 << (i * 2);\n    v0 >>= n;\n    v1 >>= n;\n  }\n  return m;\n}\n\n/**\n * @brief decode symbols and output LZ77 batches (single-warp)\n *\n * @param s decompression state\n * @param t warp lane id\n */\n__device__ void snappy_decode_symbols(unsnap_state_s* s, uint32_t t)\n{\n  uint32_t cur        = 0;\n  auto end            = static_cast<uint32_t>(s->end - s->base);\n  uint32_t bytes_left = s->uncompressed_size;\n  uint32_t dst_pos    = 0;\n  int32_t batch       = 0;\n\n  for (;;) {\n    int32_t batch_len;\n    volatile unsnap_batch_s* b;\n\n    // Wait for prefetcher\n    if (t == 0) {\n      s->q.prefetch_rdpos = cur;\n#pragma unroll(1)  // We don't want unrolling here\n      while (s->q.prefetch_wrpos < min(cur + 5 * batch_size, end)) {\n        busy_wait(10);\n      }\n      b = &s->q.batch[batch * batch_size];\n    }\n    // Process small symbols in parallel: for data that does not get good compression,\n    // the stream will consist of a large number of short literals (1-byte or 2-byte)\n    // followed by short repeat runs. This results in many 2-byte or 3-byte symbols\n    // that can all be decoded in parallel once we know the symbol length.\n    {\n      uint32_t v0, v1, v2, len3_mask, cur_t, is_long_sym, short_sym_mask;\n      uint32_t b0;\n      cur            = shuffle(cur);\n      cur_t          = cur + t;\n      b0             = byte_access(s, cur_t);\n      v0             = ballot((b0 == 4) || (b0 & 2));\n      b0             = byte_access(s, cur_t + 32);\n      v1             = ballot((b0 == 4) || (b0 & 2));\n      b0             = byte_access(s, cur_t + 64);\n      v2             = ballot((b0 == 4) || (b0 & 2));\n      len3_mask      = shuffle((t == 0) ? get_len3_mask(v0, v1, v2) : 0);\n      cur_t          = cur + 2 * t + __popc(len3_mask & ((1 << t) - 1));\n      b0             = byte_access(s, cur_t);\n      is_long_sym    = ((b0 & ~4) != 0) && (((b0 + 1) & 2) == 0);\n      short_sym_mask = ballot(is_long_sym);\n      batch_len      = 0;\n      b = reinterpret_cast<volatile unsnap_batch_s*>(shuffle(reinterpret_cast<uintptr_t>(b)));\n      if (!(short_sym_mask & 1)) {\n        batch_len = shuffle((t == 0) ? (short_sym_mask) ? __ffs(short_sym_mask) - 1 : 32 : 0);\n        if (batch_len != 0) {\n          uint32_t blen = 0;\n          int32_t ofs   = 0;\n          // Check for bad copy element (offset == 0).\n          bool is_copy_offset_bad = false;\n          if (t < batch_len) {\n            blen               = (b0 & 1) ? ((b0 >> 2) & 7) + 4 : ((b0 >> 2) + 1);\n            ofs                = (b0 & 1) ? ((b0 & 0xe0) << 3) | byte_access(s, cur_t + 1)\n                                 : (b0 & 2) ? byte_access(s, cur_t + 1) | (byte_access(s, cur_t + 2) << 8)\n                                            : -(int32_t)(cur_t + 1);\n            is_copy_offset_bad = (b0 & 3) != 0 && ofs == 0;\n            b[t].len           = blen;\n            b[t].offset        = ofs;\n            ofs += blen;  // for correct out-of-range detection below\n          }\n          blen       = warp_reduce_pos<cudf::detail::warp_size>(blen, t);\n          bytes_left = shuffle(bytes_left);\n          dst_pos    = shuffle(dst_pos);\n          // If `is_copy_offset_bad`, truncate the batch at the first lane with a malformed copy\n          // element (offset == 0) so warp 2 never runs it; the scalar slow path below then re-reads\n          // it, breaks, and the kernel reports FAILURE.\n          short_sym_mask = __ffs(\n            ballot(blen > bytes_left || ofs > (int32_t)(dst_pos + blen) || is_copy_offset_bad));\n          if (short_sym_mask != 0) { batch_len = min(batch_len, short_sym_mask - 1); }\n          if (batch_len != 0) {\n            blen = shuffle(blen, batch_len - 1);\n            cur  = shuffle(cur_t, batch_len - 1) + 2 + ((len3_mask >> (batch_len - 1)) & 1);\n            if (t == 0) {\n              dst_pos += blen;\n              bytes_left -= blen;\n            }\n          }\n        }\n      }\n      // Check if the batch was stopped by a 3-byte or 4-byte literal\n      if (batch_len < batch_size - 2 && shuffle(b0 & ~4, batch_len) == 8) {\n        // If so, run a slower version of the above that can also handle 3/4-byte literal sequences\n        uint32_t batch_add;\n        do {\n          uint32_t clen, mask_t;\n          cur_t     = cur + t;\n          b0        = byte_access(s, cur_t);\n          clen      = (b0 & 3) ? (b0 & 2) ? 1 : 0 : (b0 >> 2);  // symbol length minus 2\n          v0        = ballot(clen & 1);\n          v1        = ballot((clen >> 1) & 1);\n          len3_mask = shuffle((t == 0) ? get_len5_mask(v0, v1) : 0);\n          mask_t    = (1 << (2 * t)) - 1;\n          cur_t     = cur + 2 * t + 2 * __popc((len3_mask & 0xaaaa'aaaa) & mask_t) +\n                  __popc((len3_mask & 0x5555'5555) & mask_t);\n          b0          = byte_access(s, cur_t);\n          is_long_sym = ((b0 & 3) ? ((b0 & 3) == 3) : (b0 > 3 * 4)) || (cur_t >= cur + 32) ||\n                        (batch_len + t >= batch_size);\n          batch_add = __ffs(ballot(is_long_sym)) - 1;\n          if (batch_add != 0) {\n            uint32_t blen           = 0;\n            int32_t ofs             = 0;\n            bool is_copy_offset_bad = false;\n            if (t < batch_add) {\n              blen                    = (b0 & 1) ? ((b0 >> 2) & 7) + 4 : ((b0 >> 2) + 1);\n              ofs                     = (b0 & 1) ? ((b0 & 0xe0) << 3) | byte_access(s, cur_t + 1)\n                                        : (b0 & 2) ? byte_access(s, cur_t + 1) | (byte_access(s, cur_t + 2) << 8)\n                                                   : -(int32_t)(cur_t + 1);\n              is_copy_offset_bad      = (b0 & 3) != 0 && ofs == 0;\n              b[batch_len + t].len    = blen;\n              b[batch_len + t].offset = ofs;\n              ofs += blen;  // for correct out-of-range detection below\n            }\n            blen       = warp_reduce_pos<cudf::detail::warp_size>(blen, t);\n            bytes_left = shuffle(bytes_left);\n            dst_pos    = shuffle(dst_pos);\n            // See comment in the fast-path block above for `is_copy_offset_bad`.\n            short_sym_mask = __ffs(\n              ballot(blen > bytes_left || ofs > (int32_t)(dst_pos + blen) || is_copy_offset_bad));\n            if (short_sym_mask != 0) { batch_add = min(batch_add, short_sym_mask - 1); }\n            if (batch_add != 0) {\n              blen = shuffle(blen, batch_add - 1);\n              cur  = shuffle(cur_t, batch_add - 1) + 2 + ((len3_mask >> ((batch_add - 1) * 2)) & 3);\n              if (t == 0) {\n                dst_pos += blen;\n                bytes_left -= blen;\n              }\n              batch_len += batch_add;\n            }\n          }\n        } while (batch_add >= 6 && batch_len < batch_size - 2);\n      }\n    }\n    if (t == 0) {\n      while (bytes_left > 0 && batch_len < batch_size) {\n        uint32_t blen, offset;\n        uint8_t b0 = byte_access(s, cur);\n        if (b0 & 3) {\n          uint8_t b1 = byte_access(s, cur + 1);\n          if (!(b0 & 2)) {\n            // xxxxxx01.oooooooo: copy with 3-bit length, 11-bit offset\n            offset = ((b0 & 0xe0) << 3) | b1;\n            blen   = ((b0 >> 2) & 7) + 4;\n            cur += 2;\n          } else {\n            // xxxxxx1x: copy with 6-bit length, 2-byte or 4-byte offset\n            offset = b1 | (byte_access(s, cur + 2) << 8);\n            if (b0 & 1)  // 4-byte offset\n            {\n              offset |= (byte_access(s, cur + 3) << 16) | (byte_access(s, cur + 4) << 24);\n              cur += 5;\n            } else {\n              cur += 3;\n            }\n            blen = (b0 >> 2) + 1;\n          }\n          dst_pos += blen;\n          if (offset - 1u >= dst_pos || bytes_left < blen) break;\n          bytes_left -= blen;\n        } else if (b0 < 4 * 4) {\n          // 0000xx00: short literal\n          blen   = (b0 >> 2) + 1;\n          offset = -(int32_t)(cur + 1);\n          cur += 1 + blen;\n          dst_pos += blen;\n          if (bytes_left < blen) break;\n          bytes_left -= blen;\n        } else {\n          // xxxxxx00: literal\n          blen = b0 >> 2;\n          if (blen >= 60) {\n            uint32_t num_bytes = blen - 59;\n            blen               = byte_access(s, cur + 1);\n            if (num_bytes > 1) {\n              blen |= byte_access(s, cur + 2) << 8;\n              if (num_bytes > 2) {\n                blen |= byte_access(s, cur + 3) << 16;\n                if (num_bytes > 3) { blen |= byte_access(s, cur + 4) << 24; }\n              }\n            }\n            cur += num_bytes;\n          }\n          cur += 1;\n          blen += 1;\n          offset = -(int32_t)cur;\n          cur += blen;\n          // Wait for prefetcher\n          s->q.prefetch_rdpos = cur;\n#pragma unroll(1)  // We don't want unrolling here\n          while (s->q.prefetch_wrpos < min(cur + 5 * batch_size, end)) {\n            busy_wait(10);\n          }\n          dst_pos += blen;\n          if (bytes_left < blen) break;\n          bytes_left -= blen;\n        }\n        b[batch_len].len    = blen;\n        b[batch_len].offset = offset;\n        batch_len++;\n      }\n      if (batch_len != 0) {\n        s->q.batch_len[batch] = batch_len;\n        batch                 = (batch + 1) & (batch_count - 1);\n      }\n    }\n    batch_len = shuffle(batch_len);\n    if (t == 0) {\n      while (s->q.batch_len[batch] != 0) {\n        busy_wait(20);\n      }\n    }\n    if (batch_len != batch_size) { break; }\n  }\n  if (!t) {\n    s->q.prefetch_end     = 1;\n    s->q.batch_len[batch] = -1;\n    s->bytes_left         = bytes_left;\n    if (bytes_left != 0) { s->error = -2; }\n  }\n}\n\n/**\n * @brief process LZ77 symbols and output uncompressed stream\n *\n * @param s decompression state\n * @param t thread id within participating group (lane id)\n * @param temp_storage temporary storage used by the algorithm\n *\n * NOTE: No error checks at this stage (WARP0 responsible for not sending offsets and lengths that\n *would result in out-of-bounds accesses)\n */\ntemplate <typename Storage>\n__device__ void snappy_process_symbols(unsnap_state_s* s, int t, Storage& temp_storage)\n{\n  auto const literal_base = s->base;\n  auto out                = s->dst.data();\n  int batch               = 0;\n\n  do {\n    volatile unsnap_batch_s* b = &s->q.batch[batch * batch_size];\n    int32_t batch_len, blen_t, dist_t;\n\n    if (t == 0) {\n      while ((batch_len = s->q.batch_len[batch]) == 0) {\n        busy_wait(20);\n      }\n    } else {\n      batch_len = 0;\n    }\n    batch_len = shuffle(batch_len);\n    if (batch_len <= 0) { break; }\n    if (t < batch_len) {\n      blen_t = b[t].len;\n      dist_t = b[t].offset;\n    } else {\n      blen_t = dist_t = 0;\n    }\n    // Try to combine as many small entries as possible, but try to avoid doing that\n    // if we see a small repeat distance 8 bytes or less\n    if (shuffle(min((uint32_t)dist_t, (uint32_t)shuffle_xor(dist_t, 1))) > 8) {\n      uint32_t n;\n      do {\n        uint32_t bofs      = warp_reduce_pos<cudf::detail::warp_size>(blen_t, t);\n        uint32_t stop_mask = ballot((uint32_t)dist_t < bofs);\n        uint32_t start_mask =\n          hipcub::WarpReduce<uint32_t>(temp_storage).Sum((bofs < 32 && t < batch_len) ? 1 << bofs : 0);\n        start_mask = shuffle(start_mask);\n        n          = min(min((uint32_t)__popc(start_mask), (uint32_t)(__ffs(stop_mask) - 1u)),\n                (uint32_t)batch_len);\n        if (n != 0) {\n          uint32_t it  = __popc(start_mask & ((2 << t) - 1));\n          uint32_t tr  = t - shuffle(bofs - blen_t, it);\n          int32_t dist = shuffle(dist_t, it);\n          if (it < n) {\n            uint8_t const* src = (dist > 0) ? (out + t - dist) : (literal_base + tr - dist);\n            out[t]             = *src;\n          }\n          out += shuffle(bofs, n - 1);\n          blen_t = shuffle(blen_t, (n + t) & 0x1f);\n          dist_t = shuffle(dist_t, (n + t) & 0x1f);\n          batch_len -= n;\n        }\n      } while (n >= 4);\n    }\n    for (int i = 0; i < batch_len; i++) {\n      int32_t blen  = shuffle(blen_t, i);\n      int32_t dist  = shuffle(dist_t, i);\n      int32_t blen2 = (i + 1 < batch_len) ? shuffle(blen_t, i + 1) : 32;\n      // Try to combine consecutive small entries if they are independent\n      if ((uint32_t)dist >= (uint32_t)blen && blen + blen2 <= 32) {\n        int32_t dist2 = shuffle(dist_t, i + 1);\n        if ((uint32_t)dist2 >= (uint32_t)(blen + blen2)) {\n          int32_t d;\n          if (t < blen) {\n            d = dist;\n          } else {\n            dist = dist2;\n            d    = (dist2 <= 0) ? dist2 + blen : dist2;\n          }\n          blen += blen2;\n          if (t < blen) {\n            uint8_t const* src = (dist > 0) ? (out - d) : (literal_base - d);\n            out[t]             = src[t];\n          }\n          out += blen;\n          i++;\n          continue;\n        }\n      }\n      if (dist > 0) {\n        // Copy\n        uint8_t b0, b1;\n        if (t < blen) {\n          uint32_t pos       = t;\n          uint8_t const* src = out + ((pos >= dist) ? (pos % dist) : pos) - dist;\n          b0                 = *src;\n        }\n        if (32 + t < blen) {\n          uint32_t pos       = 32 + t;\n          uint8_t const* src = out + ((pos >= dist) ? (pos % dist) : pos) - dist;\n          b1                 = *src;\n        }\n        if (t < blen) { out[t] = b0; }\n        if (32 + t < blen) { out[32 + t] = b1; }\n      } else {\n        // Literal\n        uint8_t b0, b1;\n        dist = -dist;\n        while (blen >= 64) {\n          b0          = literal_base[dist + t];\n          b1          = literal_base[dist + 32 + t];\n          out[t]      = b0;\n          out[32 + t] = b1;\n          dist += 64;\n          out += 64;\n          blen -= 64;\n        }\n        if (t < blen) { b0 = literal_base[dist + t]; }\n        if (32 + t < blen) { b1 = literal_base[dist + 32 + t]; }\n        if (t < blen) { out[t] = b0; }\n        if (32 + t < blen) { out[32 + t] = b1; }\n      }\n      out += blen;\n    }\n    __syncwarp();\n    if (t == 0) { s->q.batch_len[batch] = 0; }\n    batch = (batch + 1) & (batch_count - 1);\n  } while (true);\n}\n\n/**\n * @brief Snappy decompression kernel\n * See http://github.com/google/snappy/blob/master/format_description.txt\n *\n * blockDim {128,1,1}\n *\n * @param[in] inputs Source & destination information per block\n * @param[out] outputs Decompression status per block\n */\ntemplate <int block_size>\nCUDF_KERNEL void __launch_bounds__(block_size)\n  unsnap_kernel_no_racecheck(device_span<device_span<uint8_t const> const> inputs,\n                             device_span<device_span<uint8_t> const> outputs,\n                             device_span<codec_exec_result> results)\n{\n  __shared__ __align__(16) unsnap_state_s state_g;\n  __shared__ hipcub::WarpReduce<uint32_t>::TempStorage temp_storage;\n  int t             = threadIdx.x;\n  unsnap_state_s* s = &state_g;\n  int strm_id       = blockIdx.x;\n\n  if (t < batch_count) { s->q.batch_len[t] = 0; }\n  __syncthreads();\n  if (!t) {\n    s->src         = inputs[strm_id];\n    s->dst         = outputs[strm_id];\n    auto cur       = s->src.data();\n    auto const end = s->src.data() + s->src.size();\n    s->error       = 0;\n    if (cur < end) {\n      // Read uncompressed size (varint), limited to 32-bit\n      uint32_t uncompressed_size = *cur++;\n      if (uncompressed_size > 0x7f) {\n        uint32_t c        = (cur < end) ? *cur++ : 0;\n        uncompressed_size = (uncompressed_size & 0x7f) | (c << 7);\n        if (uncompressed_size >= (0x80 << 7)) {\n          c                 = (cur < end) ? *cur++ : 0;\n          uncompressed_size = (uncompressed_size & ((0x7f << 7) | 0x7f)) | (c << 14);\n          if (uncompressed_size >= (0x80 << 14)) {\n            c = (cur < end) ? *cur++ : 0;\n            uncompressed_size =\n              (uncompressed_size & ((0x7f << 14) | (0x7f << 7) | 0x7f)) | (c << 21);\n            if (uncompressed_size >= (0x80 << 21)) {\n              c = (cur < end) ? *cur++ : 0;\n              if (c < 0x8)\n                uncompressed_size =\n                  (uncompressed_size & ((0x7f << 21) | (0x7f << 14) | (0x7f << 7) | 0x7f)) |\n                  (c << 28);\n              else\n                s->error = -1;\n            }\n          }\n        }\n      }\n      s->uncompressed_size = uncompressed_size;\n      s->bytes_left        = uncompressed_size;\n      s->base              = cur;\n      s->end               = end;\n      if ((cur >= end && uncompressed_size != 0) || (uncompressed_size > s->dst.size())) {\n        s->error = -1;\n      }\n    } else {\n      s->error = -1;\n    }\n    s->q.prefetch_end   = 0;\n    s->q.prefetch_wrpos = 0;\n    s->q.prefetch_rdpos = 0;\n  }\n  __syncthreads();\n  if (!s->error) {\n    if (t < 32) {\n      // WARP0: decode lengths and offsets\n      snappy_decode_symbols(s, t);\n    } else if (t < 64) {\n      // WARP1: prefetch byte stream for WARP0\n      snappy_prefetch_bytestream(s, t & 0x1f);\n    } else if (t < 96) {\n      // WARP2: LZ77\n      snappy_process_symbols(s, t & 0x1f, temp_storage);\n    }\n    __syncthreads();\n  }\n  if (!t) {\n    results[strm_id].bytes_written = s->uncompressed_size - s->bytes_left;\n    results[strm_id].status = (s->error == 0) ? codec_status::SUCCESS : codec_status::FAILURE;\n  }\n}\n\nvoid gpu_unsnap(device_span<device_span<uint8_t const> const> inputs,\n                device_span<device_span<uint8_t> const> outputs,\n                device_span<codec_exec_result> results,\n                rmm::cuda_stream_view stream)\n{\n  dim3 dim_block(128, 1);           // 4 warps per stream, 1 stream per block\n  dim3 dim_grid(inputs.size(), 1);  // TODO: Check max grid dimensions vs max expected count\n\n  unsnap_kernel_no_racecheck<128>\n    <<<dim_grid, dim_block, 0, stream.value()>>>(inputs, outputs, results);\n}\n\n__global__ void get_snappy_uncompressed_size_kernel(\n  device_span<device_span<uint8_t const> const> inputs, device_span<size_t> uncompressed_sizes)\n{\n  auto const idx = cudf::detail::grid_1d::global_thread_id();\n  if (idx >= inputs.size()) return;\n\n  auto cur       = inputs[idx].data();\n  auto const end = inputs[idx].data() + inputs[idx].size();\n\n  constexpr int payload_bits_per_byte = 7;\n  constexpr size_t payload_mask       = (1 << payload_bits_per_byte) - 1;\n  size_t uncompressed_size            = 0;\n  int shift                           = 0;\n  while (cur < end && shift + payload_bits_per_byte <= 8 * sizeof(size_t)) {\n    size_t const byte = *cur++;\n    uncompressed_size |= (byte & payload_mask) << shift;\n    if ((byte & (1 << payload_bits_per_byte)) == 0) {\n      uncompressed_sizes[idx] = uncompressed_size;\n      return;\n    }\n    shift += payload_bits_per_byte;\n  }\n  // Invalid varint\n  uncompressed_sizes[idx] = 0;\n}\n\nvoid get_snappy_uncompressed_size(device_span<device_span<uint8_t const> const> inputs,\n                                  device_span<size_t> uncompressed_sizes,\n                                  rmm::cuda_stream_view stream)\n{\n  int threads_per_block = 128;\n  auto const num_blocks =\n    cudf::util::div_rounding_up_safe<size_t>(inputs.size(), threads_per_block);\n\n  get_snappy_uncompressed_size_kernel<<<num_blocks, threads_per_block, 0, stream.value()>>>(\n    inputs, uncompressed_sizes);\n}\n\n}  // namespace cudf::io::detail\n", "source": "/kaggle/working/cudf/cpp/src/io/comp/unsnap.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n#include <cudf/scalar/scalar.hpp>\n\n#include <rmm/device_uvector.hpp>\n\n#include <cuda_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param redaction Redacted string replacement\n * @param working_memory Output memory for all rows\n * @param d_offsets Byte offset in `d_chars` for each row\n * @param d_output Output array of string_view objects\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::string_view redaction,\n                                     char* working_memory,\n                                     cudf::size_type const* d_offsets,\n                                     cudf::string_view* d_output)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible = cudf::string_view(\"public\", 6);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    char* output_ptr = working_memory + d_offsets[index];\n    d_output[index]  = cudf::string_view{output_ptr, output_size};\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    d_output[index] = cudf::string_view{redaction.data(), redaction.size_bytes()};\n  }\n}\n\n/**\n * @brief Redacts each name per the corresponding visibility entry\n *\n * This implementation builds the individual strings into a fixed memory buffer\n * and then calls a factory function to gather them into a strings column.\n *\n * @param names Column of names\n * @param visibilities Column of visibilities\n * @return Redacted column of names\n */\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n  auto const d_redaction    = cudf::string_scalar(std::string(\"X X\"), true, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  auto const blocks        = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  auto const scv     = cudf::strings_column_view(names);\n  auto const offsets = scv.offsets().begin<cudf::size_type>();\n\n  // create working memory to hold the output of each string\n  auto working_memory = rmm::device_uvector<char>(scv.chars_size(stream), stream);\n  // create a vector for the output strings' pointers\n  auto str_ptrs = rmm::device_uvector<cudf::string_view>(names.size(), stream);\n\n  // build the output strings\n  redact_kernel<<<blocks, block_size, 0, stream.value()>>>(*d_names,\n                                                           *d_visibilities,\n                                                           d_redaction.value(),\n                                                           working_memory.data(),\n                                                           offsets,\n                                                           str_ptrs.data());\n\n  // create strings column from the string_pairs;\n  // this copies all the individual strings into a single output column\n  auto result = cudf::make_strings_column(str_ptrs, cudf::string_view{nullptr, 0}, stream);\n  // temporary memory cleanup cost here for str_ptrs and working_memory\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n#include <cudf/scalar/scalar.hpp>\n\n#include <rmm/device_uvector.hpp>\n\n#include <hip/hip_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param redaction Redacted string replacement\n * @param working_memory Output memory for all rows\n * @param d_offsets Byte offset in `d_chars` for each row\n * @param d_output Output array of string_view objects\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::string_view redaction,\n                                     char* working_memory,\n                                     cudf::size_type const* d_offsets,\n                                     cudf::string_view* d_output)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible = cudf::string_view(\"public\", 6);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    char* output_ptr = working_memory + d_offsets[index];\n    d_output[index]  = cudf::string_view{output_ptr, output_size};\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    d_output[index] = cudf::string_view{redaction.data(), redaction.size_bytes()};\n  }\n}\n\n/**\n * @brief Redacts each name per the corresponding visibility entry\n *\n * This implementation builds the individual strings into a fixed memory buffer\n * and then calls a factory function to gather them into a strings column.\n *\n * @param names Column of names\n * @param visibilities Column of visibilities\n * @return Redacted column of names\n */\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n  auto const d_redaction    = cudf::string_scalar(std::string(\"X X\"), true, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  auto const blocks        = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  auto const scv     = cudf::strings_column_view(names);\n  auto const offsets = scv.offsets().begin<cudf::size_type>();\n\n  // create working memory to hold the output of each string\n  auto working_memory = rmm::device_uvector<char>(scv.chars_size(stream), stream);\n  // create a vector for the output strings' pointers\n  auto str_ptrs = rmm::device_uvector<cudf::string_view>(names.size(), stream);\n\n  // build the output strings\n  redact_kernel<<<blocks, block_size, 0, stream.value()>>>(*d_names,\n                                                           *d_visibilities,\n                                                           d_redaction.value(),\n                                                           working_memory.data(),\n                                                           offsets,\n                                                           str_ptrs.data());\n\n  // create strings column from the string_pairs;\n  // this copies all the individual strings into a single output column\n  auto result = cudf::make_strings_column(str_ptrs, cudf::string_view{nullptr, 0}, stream);\n  // temporary memory cleanup cost here for str_ptrs and working_memory\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "source": "/kaggle/working/cudf/cpp/examples/strings/custom_prealloc.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n\n#include <rmm/cuda_stream_view.hpp>\n#include <rmm/device_uvector.hpp>\n#include <rmm/exec_policy.hpp>\n\n#include <cuda_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n#include <thrust/scan.h>\n\n/**\n * @brief Computes the size of each output row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param d_sizes Output sizes for each row\n */\n__global__ static void sizes_kernel(cudf::column_device_view const d_names,\n                                    cudf::column_device_view const d_visibilities,\n                                    cudf::size_type* d_sizes)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible   = cudf::string_view(\"public\", 6);\n  auto const redaction = cudf::string_view(\"X X\", 3);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n\n  cudf::size_type result = redaction.size_bytes();  // init to redaction size\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n\n    result = first.size_bytes() + last_initial.size_bytes() + 1;\n  }\n\n  d_sizes[index] = result;\n}\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param d_offsets Byte offset in `d_chars` for each row\n * @param d_chars Output memory for all rows\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::size_type const* d_offsets,\n                                     char* d_chars)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible   = cudf::string_view(\"public\", 6);\n  auto const redaction = cudf::string_view(\"X X\", 3);\n\n  // resolve output_ptr using the offsets vector\n  char* output_ptr = d_chars + d_offsets[index];\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    memcpy(output_ptr, redaction.data(), redaction.size_bytes());\n  }\n}\n\n/**\n * @brief Redacts each name per the corresponding visibility entry\n *\n * This implementation builds the strings column children (offsets and chars)\n * directly into device memory for libcudf.\n *\n * @param names Column of names\n * @param visibilities Column of visibilities\n * @return Redacted column of names\n */\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  int const blocks         = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  // create offsets vector\n  auto offsets = rmm::device_uvector<cudf::size_type>(names.size() + 1, stream);\n\n  // compute output sizes\n  sizes_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    *d_names, *d_visibilities, offsets.data());\n\n  // convert sizes to offsets (in place)\n  thrust::exclusive_scan(\n    rmm::exec_policy_nosync(stream), offsets.begin(), offsets.end(), offsets.begin());\n\n  // last element is the total output size\n  // (device-to-host copy of 1 integer -- includes syncing the stream)\n  cudf::size_type output_size = offsets.back_element(stream);\n\n  //  create chars vector\n  auto chars = rmm::device_uvector<char>(output_size, stream);\n\n  // build chars output\n  redact_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    *d_names, *d_visibilities, offsets.data(), chars.data());\n\n  // create column from offsets vector (move only)\n  auto offsets_column = std::make_unique<cudf::column>(std::move(offsets), rmm::device_buffer{}, 0);\n\n  // create column for chars vector (no copy is performed)\n  auto result = cudf::make_strings_column(\n    names.size(), std::move(offsets_column), chars.release(), 0, rmm::device_buffer{});\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n\n#include <rmm/cuda_stream_view.hpp>\n#include <rmm/device_uvector.hpp>\n#include <rmm/exec_policy.hpp>\n\n#include <hip/hip_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n#include <thrust/scan.h>\n\n/**\n * @brief Computes the size of each output row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param d_sizes Output sizes for each row\n */\n__global__ static void sizes_kernel(cudf::column_device_view const d_names,\n                                    cudf::column_device_view const d_visibilities,\n                                    cudf::size_type* d_sizes)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible   = cudf::string_view(\"public\", 6);\n  auto const redaction = cudf::string_view(\"X X\", 3);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n\n  cudf::size_type result = redaction.size_bytes();  // init to redaction size\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n\n    result = first.size_bytes() + last_initial.size_bytes() + 1;\n  }\n\n  d_sizes[index] = result;\n}\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param d_offsets Byte offset in `d_chars` for each row\n * @param d_chars Output memory for all rows\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::size_type const* d_offsets,\n                                     char* d_chars)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible   = cudf::string_view(\"public\", 6);\n  auto const redaction = cudf::string_view(\"X X\", 3);\n\n  // resolve output_ptr using the offsets vector\n  char* output_ptr = d_chars + d_offsets[index];\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    memcpy(output_ptr, redaction.data(), redaction.size_bytes());\n  }\n}\n\n/**\n * @brief Redacts each name per the corresponding visibility entry\n *\n * This implementation builds the strings column children (offsets and chars)\n * directly into device memory for libcudf.\n *\n * @param names Column of names\n * @param visibilities Column of visibilities\n * @return Redacted column of names\n */\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  int const blocks         = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  // create offsets vector\n  auto offsets = rmm::device_uvector<cudf::size_type>(names.size() + 1, stream);\n\n  // compute output sizes\n  sizes_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    *d_names, *d_visibilities, offsets.data());\n\n  // convert sizes to offsets (in place)\n  thrust::exclusive_scan(\n    rmm::exec_policy_nosync(stream), offsets.begin(), offsets.end(), offsets.begin());\n\n  // last element is the total output size\n  // (device-to-host copy of 1 integer -- includes syncing the stream)\n  cudf::size_type output_size = offsets.back_element(stream);\n\n  //  create chars vector\n  auto chars = rmm::device_uvector<char>(output_size, stream);\n\n  // build chars output\n  redact_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    *d_names, *d_visibilities, offsets.data(), chars.data());\n\n  // create column from offsets vector (move only)\n  auto offsets_column = std::make_unique<cudf::column>(std::move(offsets), rmm::device_buffer{}, 0);\n\n  // create column for chars vector (no copy is performed)\n  auto result = cudf::make_strings_column(\n    names.size(), std::move(offsets_column), chars.release(), 0, rmm::device_buffer{});\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "source": "/kaggle/working/cudf/cpp/examples/strings/custom_optimized.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n#include <cudf/scalar/scalar.hpp>\n\n#include <rmm/device_uvector.hpp>\n\n#include <cuda_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n\n/**\n * @brief Reserve CUDA malloc heap size\n *\n * Call this function to change the CUDA malloc heap size limit.\n * This value depends on the total size of all the malloc()\n * calls needed for redact_kernel.\n *\n * @param heap_size Number of bytes to reserve\n *                  Default is 1GB\n */\nvoid set_malloc_heap_size(size_t heap_size = 1073741824)  // 1GB\n{\n  size_t max_malloc_heap_size = 0;\n  cudaDeviceGetLimit(&max_malloc_heap_size, cudaLimitMallocHeapSize);\n  if (max_malloc_heap_size < heap_size) {\n    max_malloc_heap_size = heap_size;\n    if (cudaDeviceSetLimit(cudaLimitMallocHeapSize, max_malloc_heap_size) != cudaSuccess) {\n      fprintf(stderr, \"could not set malloc heap size to %ldMB\\n\", (heap_size / (1024 * 1024)));\n      throw std::runtime_error(\"\");\n    }\n  }\n}\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * Note: This uses malloc() in a device kernel which works great\n * but is not very efficient. This can be useful for prototyping\n * on functions where performance is not yet important.\n * All calls to malloc() must have a corresponding free() call.\n * The separate free_kernel is launched for this purpose.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param redaction Redacted string replacement\n * @param d_output Output array of string_view objects\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::string_view redaction,\n                                     cudf::string_view* d_output)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible = cudf::string_view(\"public\", 6);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    char* output_ptr = static_cast<char*>(malloc(output_size));\n    d_output[index]  = cudf::string_view{output_ptr, output_size};\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    d_output[index] = cudf::string_view{redaction.data(), redaction.size_bytes()};\n  }\n}\n\n/**\n * @brief Frees the temporary individual string objects created in the\n * redact_kernel\n *\n * Like malloc(), free() is not very efficient but must be called for\n * each malloc() to return the memory to the CUDA malloc heap.\n *\n * @param redaction Redacted string replacement (not to be freed)\n * @param d_output Output array of string_view objects to free\n */\n__global__ static void free_kernel(cudf::string_view redaction,\n                                   cudf::string_view* d_output,\n                                   int count)\n{\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  if (index >= count) return;\n\n  auto ptr = const_cast<char*>(d_output[index].data());\n  if (ptr != redaction.data()) { free(ptr); }\n}\n\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  set_malloc_heap_size();  // to illustrate adjusting the malloc heap\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n  auto const d_redaction    = cudf::string_scalar(std::string(\"X X\"), true, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  auto const blocks        = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  // create a vector for the output strings' pointers\n  auto str_ptrs = new rmm::device_uvector<cudf::string_view>(names.size(), stream);\n\n  auto result = [&] {\n    // build the output strings\n    redact_kernel<<<blocks, block_size, 0, stream.value()>>>(\n      *d_names, *d_visibilities, d_redaction.value(), str_ptrs->data());\n    // create strings column from the string_view vector\n    // this copies all the individual strings into a single output column\n    return cudf::make_strings_column(*str_ptrs, cudf::string_view{nullptr, 0}, stream);\n  }();\n\n  // free the individual temporary memory pointers\n  free_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    d_redaction.value(), str_ptrs->data(), names.size());\n  delete str_ptrs;\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"common.hpp\"\n\n#include <cudf/column/column_device_view.cuh>\n#include <cudf/column/column_factories.hpp>\n#include <cudf/scalar/scalar.hpp>\n\n#include <rmm/device_uvector.hpp>\n\n#include <hip/hip_runtime.h>\n#include <nvtx3/nvToolsExt.h>\n\n/**\n * @brief Reserve CUDA malloc heap size\n *\n * Call this function to change the CUDA malloc heap size limit.\n * This value depends on the total size of all the malloc()\n * calls needed for redact_kernel.\n *\n * @param heap_size Number of bytes to reserve\n *                  Default is 1GB\n */\nvoid set_malloc_heap_size(size_t heap_size = 1073741824)  // 1GB\n{\n  size_t max_malloc_heap_size = 0;\n  hipDeviceGetLimit(&max_malloc_heap_size, hipLimitMallocHeapSize);\n  if (max_malloc_heap_size < heap_size) {\n    max_malloc_heap_size = heap_size;\n    if (hipDeviceSetLimit(hipLimitMallocHeapSize, max_malloc_heap_size) != hipSuccess) {\n      fprintf(stderr, \"could not set malloc heap size to %ldMB\\n\", (heap_size / (1024 * 1024)));\n      throw std::runtime_error(\"\");\n    }\n  }\n}\n\n/**\n * @brief Builds the output for each row\n *\n * This thread is called once per row in d_names.\n *\n * Note: This uses malloc() in a device kernel which works great\n * but is not very efficient. This can be useful for prototyping\n * on functions where performance is not yet important.\n * All calls to malloc() must have a corresponding free() call.\n * The separate free_kernel is launched for this purpose.\n *\n * @param d_names Column of names\n * @param d_visibilities Column of visibilities\n * @param redaction Redacted string replacement\n * @param d_output Output array of string_view objects\n */\n__global__ static void redact_kernel(cudf::column_device_view const d_names,\n                                     cudf::column_device_view const d_visibilities,\n                                     cudf::string_view redaction,\n                                     cudf::string_view* d_output)\n{\n  // The row index is resolved from the CUDA thread/block objects\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  // There may be more threads than actual rows\n  if (index >= d_names.size()) return;\n\n  auto const visible = cudf::string_view(\"public\", 6);\n\n  auto const name = d_names.element<cudf::string_view>(index);\n  auto const vis  = d_visibilities.element<cudf::string_view>(index);\n  if (vis == visible) {\n    auto const space_idx    = name.find(' ');\n    auto const first        = name.substr(0, space_idx);\n    auto const last_initial = name.substr(space_idx + 1, 1);\n    auto const output_size  = first.size_bytes() + last_initial.size_bytes() + 1;\n\n    char* output_ptr = static_cast<char*>(malloc(output_size));\n    d_output[index]  = cudf::string_view{output_ptr, output_size};\n\n    // build output string\n    memcpy(output_ptr, last_initial.data(), last_initial.size_bytes());\n    output_ptr += last_initial.size_bytes();\n    *output_ptr++ = ' ';\n    memcpy(output_ptr, first.data(), first.size_bytes());\n  } else {\n    d_output[index] = cudf::string_view{redaction.data(), redaction.size_bytes()};\n  }\n}\n\n/**\n * @brief Frees the temporary individual string objects created in the\n * redact_kernel\n *\n * Like malloc(), free() is not very efficient but must be called for\n * each malloc() to return the memory to the CUDA malloc heap.\n *\n * @param redaction Redacted string replacement (not to be freed)\n * @param d_output Output array of string_view objects to free\n */\n__global__ static void free_kernel(cudf::string_view redaction,\n                                   cudf::string_view* d_output,\n                                   int count)\n{\n  auto index = threadIdx.x + blockIdx.x * blockDim.x;\n  if (index >= count) return;\n\n  auto ptr = const_cast<char*>(d_output[index].data());\n  if (ptr != redaction.data()) { free(ptr); }\n}\n\nstd::unique_ptr<cudf::column> redact_strings(cudf::column_view const& names,\n                                             cudf::column_view const& visibilities)\n{\n  // all device memory operations and kernel functions will run on this stream\n  auto stream = rmm::cuda_stream_default;\n\n  set_malloc_heap_size();  // to illustrate adjusting the malloc heap\n\n  auto const d_names        = cudf::column_device_view::create(names, stream);\n  auto const d_visibilities = cudf::column_device_view::create(visibilities, stream);\n  auto const d_redaction    = cudf::string_scalar(std::string(\"X X\"), true, stream);\n\n  constexpr int block_size = 128;  // this arbitrary size should be a power of 2\n  auto const blocks        = (names.size() + block_size - 1) / block_size;\n\n  nvtxRangePushA(\"redact_strings\");\n\n  // create a vector for the output strings' pointers\n  auto str_ptrs = new rmm::device_uvector<cudf::string_view>(names.size(), stream);\n\n  auto result = [&] {\n    // build the output strings\n    redact_kernel<<<blocks, block_size, 0, stream.value()>>>(\n      *d_names, *d_visibilities, d_redaction.value(), str_ptrs->data());\n    // create strings column from the string_view vector\n    // this copies all the individual strings into a single output column\n    return cudf::make_strings_column(*str_ptrs, cudf::string_view{nullptr, 0}, stream);\n  }();\n\n  // free the individual temporary memory pointers\n  free_kernel<<<blocks, block_size, 0, stream.value()>>>(\n    d_redaction.value(), str_ptrs->data(), names.size());\n  delete str_ptrs;\n\n  // wait for all of the above to finish\n  stream.synchronize();\n\n  nvtxRangePop();\n  return result;\n}\n", "source": "/kaggle/working/cudf/cpp/examples/strings/custom_with_malloc.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <cudf_test/base_fixture.hpp>\n#include <cudf_test/random.hpp>\n#include <cudf_test/testing_main.hpp>\n#include <cudf_test/timestamp_utilities.cuh>\n#include <cudf_test/type_lists.hpp>\n\n#include <cudf/detail/utilities/device_atomics.cuh>\n#include <cudf/detail/utilities/vector_factories.hpp>\n#include <cudf/utilities/default_stream.hpp>\n#include <cudf/utilities/error.hpp>\n#include <cudf/utilities/memory_resource.hpp>\n#include <cudf/utilities/traits.hpp>\n#include <cudf/wrappers/timestamps.hpp>\n\n#include <rmm/device_scalar.hpp>\n\n#include <cuda/std/limits>\n\n#include <algorithm>\n\nnamespace {\ntemplate <typename T>\nCUDF_KERNEL void gpu_atomic_test(T* result, T* data, size_t size)\n{\n  size_t id   = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t step = blockDim.x * gridDim.x;\n\n  for (; id < size; id += step) {\n    cudf::detail::atomic_add(&result[0], data[id]);\n    cudf::detail::atomic_min(&result[1], data[id]);\n    cudf::detail::atomic_max(&result[2], data[id]);\n    cudf::detail::genericAtomicOperation(&result[3], data[id], cudf::DeviceSum{});\n    cudf::detail::genericAtomicOperation(&result[4], data[id], cudf::DeviceMin{});\n    cudf::detail::genericAtomicOperation(&result[5], data[id], cudf::DeviceMax{});\n  }\n}\n\ntemplate <typename T, typename BinaryOp>\nconstexpr inline bool is_timestamp_sum()\n{\n  return cudf::is_timestamp<T>() && std::is_same_v<BinaryOp, cudf::DeviceSum>;\n}\n// Disable SUM of TIMESTAMP types\ntemplate <typename T,\n          typename BinaryOp,\n          std::enable_if_t<is_timestamp_sum<T, BinaryOp>()>* = nullptr>\n__device__ T atomic_op(T* addr, T const& value, BinaryOp op)\n{\n  return {};\n}\n\ntemplate <typename T,\n          typename BinaryOp,\n          std::enable_if_t<!is_timestamp_sum<T, BinaryOp>()>* = nullptr>\n__device__ T atomic_op(T* addr, T const& value, BinaryOp op)\n{\n  T old_value = *addr;\n  T assumed;\n\n  do {\n    assumed     = old_value;\n    T new_value = op(old_value, value);\n\n    old_value = cudf::detail::atomic_cas(addr, assumed, new_value);\n  } while (assumed != old_value);\n\n  return old_value;\n}\n\ntemplate <typename T>\nCUDF_KERNEL void gpu_atomicCAS_test(T* result, T* data, size_t size)\n{\n  size_t id   = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t step = blockDim.x * gridDim.x;\n\n  for (; id < size; id += step) {\n    atomic_op(&result[0], data[id], cudf::DeviceSum{});\n    atomic_op(&result[1], data[id], cudf::DeviceMin{});\n    atomic_op(&result[2], data[id], cudf::DeviceMax{});\n    atomic_op(&result[3], data[id], cudf::DeviceSum{});\n    atomic_op(&result[4], data[id], cudf::DeviceMin{});\n    atomic_op(&result[5], data[id], cudf::DeviceMax{});\n  }\n}\n\ntemplate <typename T>\nstd::enable_if_t<!cudf::is_timestamp<T>(), T> accumulate(cudf::host_span<T const> xs)\n{\n  return std::accumulate(xs.begin(), xs.end(), T{0});\n}\n\ntemplate <typename T>\nstd::enable_if_t<cudf::is_timestamp<T>(), T> accumulate(cudf::host_span<T const> xs)\n{\n  auto ys = std::vector<typename T::rep>(xs.size());\n  std::transform(\n    xs.begin(), xs.end(), ys.begin(), [](T const& ts) { return ts.time_since_epoch().count(); });\n  return T{typename T::duration{std::accumulate(ys.begin(), ys.end(), 0)}};\n}\n}  // namespace\n\ntemplate <typename T>\nstruct AtomicsTest : public cudf::test::BaseFixture {\n  void atomic_test(std::vector<int> const& v_input,\n                   bool is_cas_test,\n                   int block_size = 0,\n                   int grid_size  = 1)\n  {\n    size_t vec_size = v_input.size();\n\n    // use transform from thrust::host_vector<int> instead.\n    thrust::host_vector<T> v(vec_size);\n    std::transform(v_input.begin(), v_input.end(), v.begin(), [](int x) {\n      T t = cudf::test::make_type_param_scalar<T>(x);\n      return t;\n    });\n\n    T exact[3];\n    exact[0] = accumulate<T>(v);\n    exact[1] = *(std::min_element(v.begin(), v.end()));\n    exact[2] = *(std::max_element(v.begin(), v.end()));\n\n    thrust::host_vector<T> result_init(9);  // +3 padding for int8 tests\n    result_init[0] = cudf::test::make_type_param_scalar<T>(0);\n    if constexpr (cudf::is_chrono<T>()) {\n      result_init[1] = T::max();\n      result_init[2] = T::min();\n    } else {\n      result_init[1] = std::numeric_limits<T>::max();\n      result_init[2] = std::numeric_limits<T>::min();\n    }\n    result_init[3] = result_init[0];\n    result_init[4] = result_init[1];\n    result_init[5] = result_init[2];\n\n    auto dev_data = cudf::detail::make_device_uvector(\n      v, cudf::get_default_stream(), cudf::get_current_device_resource_ref());\n    auto dev_result = cudf::detail::make_device_uvector(\n      result_init, cudf::get_default_stream(), cudf::get_current_device_resource_ref());\n\n    if (block_size == 0) { block_size = vec_size; }\n\n    if (is_cas_test) {\n      gpu_atomicCAS_test<<<grid_size, block_size, 0, cudf::get_default_stream().value()>>>(\n        dev_result.data(), dev_data.data(), vec_size);\n    } else {\n      gpu_atomic_test<<<grid_size, block_size, 0, cudf::get_default_stream().value()>>>(\n        dev_result.data(), dev_data.data(), vec_size);\n    }\n\n    auto host_result = cudf::detail::make_host_vector(dev_result, cudf::get_default_stream());\n\n    CUDF_CHECK_CUDA(cudf::get_default_stream().value());\n\n    if (!is_timestamp_sum<T, cudf::DeviceSum>()) {\n      EXPECT_EQ(host_result[0], exact[0]) << \"atomicAdd test failed\";\n    }\n    EXPECT_EQ(host_result[1], exact[1]) << \"atomicMin test failed\";\n    EXPECT_EQ(host_result[2], exact[2]) << \"atomicMax test failed\";\n    if (!is_timestamp_sum<T, cudf::DeviceSum>()) {\n      EXPECT_EQ(host_result[3], exact[0]) << \"atomicAdd test(2) failed\";\n    }\n    EXPECT_EQ(host_result[4], exact[1]) << \"atomicMin test(2) failed\";\n    EXPECT_EQ(host_result[5], exact[2]) << \"atomicMax test(2) failed\";\n  }\n};\n\nTYPED_TEST_SUITE(AtomicsTest, cudf::test::FixedWidthTypesWithoutFixedPoint);\n\n// tests for atomicAdd/Min/Max\nTYPED_TEST(AtomicsTest, atomicOps)\n{\n  bool is_cas_test = false;\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test);\n}\n\n// tests for atomicCAS\nTYPED_TEST(AtomicsTest, atomicCAS)\n{\n  bool is_cas_test = true;\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test);\n}\n\n// tests for atomicAdd/Min/Max\nTYPED_TEST(AtomicsTest, atomicOpsGrid)\n{\n  bool is_cas_test = false;\n  int block_size   = 3;\n  int grid_size    = 4;\n\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test, block_size, grid_size);\n}\n\n// tests for atomicCAS\nTYPED_TEST(AtomicsTest, atomicCASGrid)\n{\n  bool is_cas_test = true;\n  int block_size   = 3;\n  int grid_size    = 4;\n\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test, block_size, grid_size);\n}\n\n// tests for large array\nTYPED_TEST(AtomicsTest, atomicOpsRandom)\n{\n  bool is_cas_test = false;\n  int block_size   = 256;\n  int grid_size    = 64;\n\n  std::vector<int> input_array(grid_size * block_size);\n\n  std::default_random_engine engine;\n  std::uniform_int_distribution<> dist(-10, 10);\n  std::generate(input_array.begin(), input_array.end(), [&]() { return dist(engine); });\n\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n}\n\nTYPED_TEST(AtomicsTest, atomicCASRandom)\n{\n  bool is_cas_test = true;\n  int block_size   = 256;\n  int grid_size    = 64;\n\n  std::vector<int> input_array(grid_size * block_size);\n\n  std::default_random_engine engine;\n  std::uniform_int_distribution<> dist(-10, 10);\n  std::generate(input_array.begin(), input_array.end(), [&]() { return dist(engine); });\n\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n}\n\n__global__ void test_single_atomic_add_kernel(__int128_t* target, __int128_t value)\n{\n  cudf::detail::atomic_add(target, value);\n}\n\nclass Atomic128Test : public cudf::test::BaseFixture {\n public:\n  void run_atomic_add_test(__int128_t initial_value,\n                           __int128_t add_value,\n                           __int128_t expected_result)\n  {\n    rmm::device_scalar<__int128_t> d_target(initial_value, cudf::get_default_stream());\n    test_single_atomic_add_kernel<<<32, 256, 0, cudf::get_default_stream().value()>>>(\n      d_target.data(), add_value);\n    CUDF_CHECK_CUDA(cudf::get_default_stream().value());\n    __int128_t result = d_target.value(cudf::get_default_stream());\n    EXPECT_EQ(result, expected_result);\n  }\n};\n\nTEST_F(Atomic128Test, BasicAddition) { run_atomic_add_test(0, 1, 32 * 256 * 1); }\n\nTEST_F(Atomic128Test, CarryPropagation)\n{\n  constexpr int total_threads = 32 * 256;\n  __int128_t initial_value    = cuda::std::numeric_limits<int64_t>::max() - total_threads + 1;\n  __int128_t expected = static_cast<__int128_t>(cuda::std::numeric_limits<int64_t>::max()) + 1;\n  run_atomic_add_test(initial_value, 1, expected);\n}\n\nTEST_F(Atomic128Test, NegativeNumbers)\n{\n  constexpr int total_threads = 32 * 256;\n  run_atomic_add_test(0, -50, -total_threads * 50);\n}\n\nTEST_F(Atomic128Test, NegativeCarryPropagation)\n{\n  constexpr int total_threads = 32 * 256;\n  __int128_t initial_value    = cuda::std::numeric_limits<int64_t>::min() + total_threads - 1;\n  __int128_t expected = static_cast<__int128_t>(cuda::std::numeric_limits<int64_t>::min()) - 1;\n  run_atomic_add_test(initial_value, -1, expected);\n}\n\nCUDF_TEST_PROGRAM_MAIN()\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cudf_test/base_fixture.hpp>\n#include <cudf_test/random.hpp>\n#include <cudf_test/testing_main.hpp>\n#include <cudf_test/timestamp_utilities.cuh>\n#include <cudf_test/type_lists.hpp>\n\n#include <cudf/detail/utilities/device_atomics.cuh>\n#include <cudf/detail/utilities/vector_factories.hpp>\n#include <cudf/utilities/default_stream.hpp>\n#include <cudf/utilities/error.hpp>\n#include <cudf/utilities/memory_resource.hpp>\n#include <cudf/utilities/traits.hpp>\n#include <cudf/wrappers/timestamps.hpp>\n\n#include <rmm/device_scalar.hpp>\n\n#include <cuda/std/limits>\n\n#include <algorithm>\n\nnamespace {\ntemplate <typename T>\nCUDF_KERNEL void gpu_atomic_test(T* result, T* data, size_t size)\n{\n  size_t id   = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t step = blockDim.x * gridDim.x;\n\n  for (; id < size; id += step) {\n    cudf::detail::atomic_add(&result[0], data[id]);\n    cudf::detail::atomic_min(&result[1], data[id]);\n    cudf::detail::atomic_max(&result[2], data[id]);\n    cudf::detail::genericAtomicOperation(&result[3], data[id], cudf::DeviceSum{});\n    cudf::detail::genericAtomicOperation(&result[4], data[id], cudf::DeviceMin{});\n    cudf::detail::genericAtomicOperation(&result[5], data[id], cudf::DeviceMax{});\n  }\n}\n\ntemplate <typename T, typename BinaryOp>\nconstexpr inline bool is_timestamp_sum()\n{\n  return cudf::is_timestamp<T>() && std::is_same_v<BinaryOp, cudf::DeviceSum>;\n}\n// Disable SUM of TIMESTAMP types\ntemplate <typename T,\n          typename BinaryOp,\n          std::enable_if_t<is_timestamp_sum<T, BinaryOp>()>* = nullptr>\n__device__ T atomic_op(T* addr, T const& value, BinaryOp op)\n{\n  return {};\n}\n\ntemplate <typename T,\n          typename BinaryOp,\n          std::enable_if_t<!is_timestamp_sum<T, BinaryOp>()>* = nullptr>\n__device__ T atomic_op(T* addr, T const& value, BinaryOp op)\n{\n  T old_value = *addr;\n  T assumed;\n\n  do {\n    assumed     = old_value;\n    T new_value = op(old_value, value);\n\n    old_value = cudf::detail::atomic_cas(addr, assumed, new_value);\n  } while (assumed != old_value);\n\n  return old_value;\n}\n\ntemplate <typename T>\nCUDF_KERNEL void gpu_atomicCAS_test(T* result, T* data, size_t size)\n{\n  size_t id   = blockIdx.x * blockDim.x + threadIdx.x;\n  size_t step = blockDim.x * gridDim.x;\n\n  for (; id < size; id += step) {\n    atomic_op(&result[0], data[id], cudf::DeviceSum{});\n    atomic_op(&result[1], data[id], cudf::DeviceMin{});\n    atomic_op(&result[2], data[id], cudf::DeviceMax{});\n    atomic_op(&result[3], data[id], cudf::DeviceSum{});\n    atomic_op(&result[4], data[id], cudf::DeviceMin{});\n    atomic_op(&result[5], data[id], cudf::DeviceMax{});\n  }\n}\n\ntemplate <typename T>\nstd::enable_if_t<!cudf::is_timestamp<T>(), T> accumulate(cudf::host_span<T const> xs)\n{\n  return std::accumulate(xs.begin(), xs.end(), T{0});\n}\n\ntemplate <typename T>\nstd::enable_if_t<cudf::is_timestamp<T>(), T> accumulate(cudf::host_span<T const> xs)\n{\n  auto ys = std::vector<typename T::rep>(xs.size());\n  std::transform(\n    xs.begin(), xs.end(), ys.begin(), [](T const& ts) { return ts.time_since_epoch().count(); });\n  return T{typename T::duration{std::accumulate(ys.begin(), ys.end(), 0)}};\n}\n}  // namespace\n\ntemplate <typename T>\nstruct AtomicsTest : public cudf::test::BaseFixture {\n  void atomic_test(std::vector<int> const& v_input,\n                   bool is_cas_test,\n                   int block_size = 0,\n                   int grid_size  = 1)\n  {\n    size_t vec_size = v_input.size();\n\n    // use transform from thrust::host_vector<int> instead.\n    thrust::host_vector<T> v(vec_size);\n    std::transform(v_input.begin(), v_input.end(), v.begin(), [](int x) {\n      T t = cudf::test::make_type_param_scalar<T>(x);\n      return t;\n    });\n\n    T exact[3];\n    exact[0] = accumulate<T>(v);\n    exact[1] = *(std::min_element(v.begin(), v.end()));\n    exact[2] = *(std::max_element(v.begin(), v.end()));\n\n    thrust::host_vector<T> result_init(9);  // +3 padding for int8 tests\n    result_init[0] = cudf::test::make_type_param_scalar<T>(0);\n    if constexpr (cudf::is_chrono<T>()) {\n      result_init[1] = T::max();\n      result_init[2] = T::min();\n    } else {\n      result_init[1] = std::numeric_limits<T>::max();\n      result_init[2] = std::numeric_limits<T>::min();\n    }\n    result_init[3] = result_init[0];\n    result_init[4] = result_init[1];\n    result_init[5] = result_init[2];\n\n    auto dev_data = cudf::detail::make_device_uvector(\n      v, cudf::get_default_stream(), cudf::get_current_device_resource_ref());\n    auto dev_result = cudf::detail::make_device_uvector(\n      result_init, cudf::get_default_stream(), cudf::get_current_device_resource_ref());\n\n    if (block_size == 0) { block_size = vec_size; }\n\n    if (is_cas_test) {\n      gpu_atomicCAS_test<<<grid_size, block_size, 0, cudf::get_default_stream().value()>>>(\n        dev_result.data(), dev_data.data(), vec_size);\n    } else {\n      gpu_atomic_test<<<grid_size, block_size, 0, cudf::get_default_stream().value()>>>(\n        dev_result.data(), dev_data.data(), vec_size);\n    }\n\n    auto host_result = cudf::detail::make_host_vector(dev_result, cudf::get_default_stream());\n\n    CUDF_CHECK_CUDA(cudf::get_default_stream().value());\n\n    if (!is_timestamp_sum<T, cudf::DeviceSum>()) {\n      EXPECT_EQ(host_result[0], exact[0]) << \"atomicAdd test failed\";\n    }\n    EXPECT_EQ(host_result[1], exact[1]) << \"atomicMin test failed\";\n    EXPECT_EQ(host_result[2], exact[2]) << \"atomicMax test failed\";\n    if (!is_timestamp_sum<T, cudf::DeviceSum>()) {\n      EXPECT_EQ(host_result[3], exact[0]) << \"atomicAdd test(2) failed\";\n    }\n    EXPECT_EQ(host_result[4], exact[1]) << \"atomicMin test(2) failed\";\n    EXPECT_EQ(host_result[5], exact[2]) << \"atomicMax test(2) failed\";\n  }\n};\n\nTYPED_TEST_SUITE(AtomicsTest, cudf::test::FixedWidthTypesWithoutFixedPoint);\n\n// tests for atomicAdd/Min/Max\nTYPED_TEST(AtomicsTest, atomicOps)\n{\n  bool is_cas_test = false;\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test);\n}\n\n// tests for atomicCAS\nTYPED_TEST(AtomicsTest, atomicCAS)\n{\n  bool is_cas_test = true;\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test);\n}\n\n// tests for atomicAdd/Min/Max\nTYPED_TEST(AtomicsTest, atomicOpsGrid)\n{\n  bool is_cas_test = false;\n  int block_size   = 3;\n  int grid_size    = 4;\n\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test, block_size, grid_size);\n}\n\n// tests for atomicCAS\nTYPED_TEST(AtomicsTest, atomicCASGrid)\n{\n  bool is_cas_test = true;\n  int block_size   = 3;\n  int grid_size    = 4;\n\n  std::vector<int> input_array({0, 6, 0, -14, 13, 64, -13, -20, 45});\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n\n  std::vector<int> input_array2({6, -6, 13, 62, -11, -20, 33});\n  this->atomic_test(input_array2, is_cas_test, block_size, grid_size);\n}\n\n// tests for large array\nTYPED_TEST(AtomicsTest, atomicOpsRandom)\n{\n  bool is_cas_test = false;\n  int block_size   = 256;\n  int grid_size    = 64;\n\n  std::vector<int> input_array(grid_size * block_size);\n\n  std::default_random_engine engine;\n  std::uniform_int_distribution<> dist(-10, 10);\n  std::generate(input_array.begin(), input_array.end(), [&]() { return dist(engine); });\n\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n}\n\nTYPED_TEST(AtomicsTest, atomicCASRandom)\n{\n  bool is_cas_test = true;\n  int block_size   = 256;\n  int grid_size    = 64;\n\n  std::vector<int> input_array(grid_size * block_size);\n\n  std::default_random_engine engine;\n  std::uniform_int_distribution<> dist(-10, 10);\n  std::generate(input_array.begin(), input_array.end(), [&]() { return dist(engine); });\n\n  this->atomic_test(input_array, is_cas_test, block_size, grid_size);\n}\n\n__global__ void test_single_atomic_add_kernel(__int128_t* target, __int128_t value)\n{\n  cudf::detail::atomic_add(target, value);\n}\n\nclass Atomic128Test : public cudf::test::BaseFixture {\n public:\n  void run_atomic_add_test(__int128_t initial_value,\n                           __int128_t add_value,\n                           __int128_t expected_result)\n  {\n    rmm::device_scalar<__int128_t> d_target(initial_value, cudf::get_default_stream());\n    test_single_atomic_add_kernel<<<32, 256, 0, cudf::get_default_stream().value()>>>(\n      d_target.data(), add_value);\n    CUDF_CHECK_CUDA(cudf::get_default_stream().value());\n    __int128_t result = d_target.value(cudf::get_default_stream());\n    EXPECT_EQ(result, expected_result);\n  }\n};\n\nTEST_F(Atomic128Test, BasicAddition) { run_atomic_add_test(0, 1, 32 * 256 * 1); }\n\nTEST_F(Atomic128Test, CarryPropagation)\n{\n  constexpr int total_threads = 32 * 256;\n  __int128_t initial_value    = cuda::std::numeric_limits<int64_t>::max() - total_threads + 1;\n  __int128_t expected = static_cast<__int128_t>(cuda::std::numeric_limits<int64_t>::max()) + 1;\n  run_atomic_add_test(initial_value, 1, expected);\n}\n\nTEST_F(Atomic128Test, NegativeNumbers)\n{\n  constexpr int total_threads = 32 * 256;\n  run_atomic_add_test(0, -50, -total_threads * 50);\n}\n\nTEST_F(Atomic128Test, NegativeCarryPropagation)\n{\n  constexpr int total_threads = 32 * 256;\n  __int128_t initial_value    = cuda::std::numeric_limits<int64_t>::min() + total_threads - 1;\n  __int128_t expected = static_cast<__int128_t>(cuda::std::numeric_limits<int64_t>::min()) - 1;\n  run_atomic_add_test(initial_value, -1, expected);\n}\n\nCUDF_TEST_PROGRAM_MAIN()\n", "source": "/kaggle/working/cudf/cpp/tests/device_atomics/device_atomics_test.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include <stdexcept>\n\n__global__ static void kernel() { printf(\"The kernel ran!\\n\"); }\n\nvoid test_cudaLaunchKernel()\n{\n  cudaStream_t stream;\n  cudaStreamCreate(&stream);\n  kernel<<<1, 1, 0, stream>>>();\n  cudaError_t err{cudaDeviceSynchronize()};\n  if (err != cudaSuccess) { throw std::runtime_error(\"Kernel failed on non-default stream!\"); }\n  err = cudaGetLastError();\n  if (err != cudaSuccess) { throw std::runtime_error(\"Kernel failed on non-default stream!\"); }\n\n  try {\n    kernel<<<1, 1>>>();\n  } catch (std::runtime_error&) {\n    return;\n  }\n  if (getenv(\"GTEST_CUDF_STREAM_MODE\") == nullptr) { return; }\n  throw std::runtime_error(\"No exception raised for kernel on default stream!\");\n}\n\nint main()\n{\n  test_cudaLaunchKernel();\n  return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdexcept>\n\n__global__ static void kernel() { printf(\"The kernel ran!\\n\"); }\n\nvoid test_cudaLaunchKernel()\n{\n  hipStream_t stream;\n  hipStreamCreate(&stream);\n  kernel<<<1, 1, 0, stream>>>();\n  hipError_t err{hipDeviceSynchronize()};\n  if (err != hipSuccess) { throw std::runtime_error(\"Kernel failed on non-default stream!\"); }\n  err = hipGetLastError();\n  if (err != hipSuccess) { throw std::runtime_error(\"Kernel failed on non-default stream!\"); }\n\n  try {\n    kernel<<<1, 1>>>();\n  } catch (std::runtime_error&) {\n    return;\n  }\n  if (getenv(\"GTEST_CUDF_STREAM_MODE\") == nullptr) { return; }\n  throw std::runtime_error(\"No exception raised for kernel on default stream!\");\n}\n\nint main()\n{\n  test_cudaLaunchKernel();\n  return 0;\n}\n", "source": "/kaggle/working/cudf/cpp/tests/identify_stream_usage/test_default_stream_identification.cu"}
{"input": "/******************************************************************************\n * Copyright (c) 2024, Jay Shah, Ganesh Bikshandi, Ying Zhang, Vijay Thakkar, Pradeep Ramani, Tri Dao.\n ******************************************************************************/\n\n#include <cub/cub.cuh>\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/barrier.h\"\n#include \"cutlass/arch/barrier.h\"\n\n#include \"cutlass/arch/grid_dependency_control.h\"\n\n#include \"flash.h\"\n\n#include \"static_switch.h\"\n\nnamespace flash {\n\n// Sort in descending order\ntemplate <typename T>\nstruct PrepareSortOp\n{\n    __device__ __forceinline__ bool operator()(T const & lhs, T const & rhs)\n    {\n        return lhs > rhs;\n    }\n};\n\ntemplate <>\nstruct PrepareSortOp<int2> {\n    __device__ __forceinline__ bool operator()(int2 const & lhs, int2 const & rhs) const {\n        return lhs.x > rhs.x;\n    }\n};\n\ntemplate <>\nstruct PrepareSortOp<int4> {\n    __device__ __forceinline__ bool operator()(int4 const & lhs, int4 const & rhs) const {\n        return lhs.x > rhs.x;\n    }\n};\n\ntemplate <int NumWarps, bool Sort>\n__global__ void prepare_varlen_num_blocks_kernel(\n        int seqlen_q_static, int seqlen_k_static, int seqlen_k_new_static,\n        int const* const cu_seqlens_q, int const* const cu_seqlens_k, int const* const cu_seqlens_k_new,\n        int const* const seqused_q, int const* const seqused_k, int const* const leftpad_k_ptr,\n        int num_batch, int num_head, int qhead_per_khead, int num_sm, int num_splits_static,\n        cutlass::FastDivmod blockm_divmod, cutlass::FastDivmod blockn_divmod,\n        int* const tile_count_semaphore,\n        int* const num_m_blocks_ptr,\n        int* const num_splits_dynamic_ptr,\n        int* const varlen_batch_idx_ptr,\n        // int* const num_n_blocks_ptr,\n        int* const num_nheads_in_l2_ptr,\n        bool enable_pdl,\n        bool is_causal,\n        bool packgqa,\n        int max_kvblocks_in_l2) {\n\n    static constexpr int kNumBatchPerWarp = cutlass::NumThreadsPerWarp - 1;\n    static constexpr int kSmemSize = 1;\n    static constexpr int BLOCK_DIM_X = NumWarps * 32;\n    static constexpr int ITEMS_PER_THREAD = 1;\n    static_assert(BLOCK_DIM_X * ITEMS_PER_THREAD == NumWarps * 32);\n    using BlockMergeSort = cub::BlockMergeSort<int4, BLOCK_DIM_X, ITEMS_PER_THREAD>;\n\n    __shared__ int total_blocks_smem[kSmemSize];\n\n    // Allocate shared memory for BlockMergeSort operations\n    __shared__ typename BlockMergeSort::TempStorage temp_storage;\n\n    if (enable_pdl) { cutlass::arch::launch_dependent_grids(); }\n\n    if (threadIdx.x < kSmemSize) { total_blocks_smem[threadIdx.x] = 0; }\n    __syncthreads();\n\n    if (threadIdx.x == 0 && tile_count_semaphore) { *tile_count_semaphore = 0; }\n\n    int lane = threadIdx.x % cutlass::NumThreadsPerWarp;\n\n    auto get_num_m_blocks = [&](int batch_idx) {\n        int seqlen;\n        if (seqused_q) {\n            seqlen = batch_idx < num_batch ? seqused_q[batch_idx] : 0;\n        } else if (cu_seqlens_q) {\n            int cur_cu_seqlen = batch_idx <= num_batch ? cu_seqlens_q[batch_idx] : 0;\n            int next_cu_seqlen = __shfl_down_sync(0xffffffff, cur_cu_seqlen, 1);\n            seqlen = next_cu_seqlen - cur_cu_seqlen;\n        } else {\n            seqlen = seqlen_q_static;\n        }\n        if(packgqa) { seqlen *= qhead_per_khead; }\n        return batch_idx < num_batch && lane < kNumBatchPerWarp\n            ? blockm_divmod.div(seqlen + blockm_divmod.divisor - 1) : 0;\n    };\n\n    auto get_num_n_blocks = [&](int batch_idx) {\n        int leftpad_k = batch_idx < num_batch && leftpad_k_ptr != nullptr ? leftpad_k_ptr[batch_idx] : 0;\n        int seqlen;\n        if (seqused_k) {\n            seqlen = batch_idx < num_batch ? seqused_k[batch_idx] : 0;\n        } else if (cu_seqlens_k) {\n            int cur_cu_seqlen = batch_idx <= num_batch ? cu_seqlens_k[batch_idx] : 0;\n            int next_cu_seqlen = __shfl_down_sync(0xffffffff, cur_cu_seqlen, 1);\n            seqlen = next_cu_seqlen - cur_cu_seqlen;\n        } else {\n            seqlen = seqlen_k_static;\n        }\n        int seqlen_new;\n        if (cu_seqlens_k_new) {\n            int cur_cu_seqlen_new = batch_idx <= num_batch ? cu_seqlens_k_new[batch_idx] : 0;\n            int next_cu_seqlen_new = __shfl_down_sync(0xffffffff, cur_cu_seqlen_new, 1);\n            seqlen_new = next_cu_seqlen_new - cur_cu_seqlen_new;\n        } else {\n            seqlen_new = seqlen_k_new_static;\n        }\n        // if (threadIdx.x == 0) { printf(\"seqlen = %d, seqlen_new = %d, leftpad_k = %d\\n\", seqlen, seqlen_new, leftpad_k); }\n        seqlen = seqlen - leftpad_k + seqlen_new;\n        return batch_idx < num_batch && lane < kNumBatchPerWarp\n            ? blockn_divmod.div(seqlen + blockn_divmod.divisor - 1) : 0;\n    };\n\n    int warp_idx = threadIdx.x / cutlass::NumThreadsPerWarp;\n    int batch_cta_idx_offset = int(blockIdx.x) * 992;\n    int bidb_start = batch_cta_idx_offset + kNumBatchPerWarp * warp_idx;\n    int batch_idx = lane + bidb_start;\n    int num_m_blocks = get_num_m_blocks(batch_idx);\n    int num_n_blocks = get_num_n_blocks(batch_idx);\n\n    auto get_nheads_in_l2 = [&](int n_blocks) {\n        int nheads_in_l2 = n_blocks * 16 <= max_kvblocks_in_l2 ? 16\n            : n_blocks * 8 <= max_kvblocks_in_l2 ? 8\n            : n_blocks * 4 <= max_kvblocks_in_l2 ? 4\n            : n_blocks * 2 <= max_kvblocks_in_l2 ? 2\n            : 1;\n        if(!packgqa) { nheads_in_l2 *= qhead_per_khead; }\n        return min(nheads_in_l2, num_head);\n    };\n    \n    int num_splits_dynamic;\n    if (int(gridDim.x) > 1 || num_splits_static == 1) {\n        // set num splits for all batches to 1 (note that user expects num_splits_static to mean upper bound on splits)\n        // for batch size > 992, we expect GPU occupancy to not be an issue except in degenerate cases (e.g., most are zero-length)\n        num_splits_dynamic = 1;\n    } else {\n        int total_blocks = num_m_blocks * num_n_blocks;\n        // Warp sum\n        #pragma unroll\n        for (int i = cutlass::NumThreadsPerWarp / 2; i >= 1; i /= 2) {\n            total_blocks += __shfl_down_sync(0xffffffff, total_blocks, i);\n        }\n        if (lane == 0) { atomicAdd(total_blocks_smem, total_blocks); }\n        __syncthreads();\n        total_blocks = total_blocks_smem[0];\n        // 10% margin\n        int blocks_per_sm = static_cast<int>(ceilf(float(total_blocks) * 1.1f * float(num_head) / float(num_sm)));\n        // blocks_per_sm = std::max(1, blocks_per_sm);  // 1 is the minimum number of blocks per SM\n        num_splits_dynamic = std::max(std::min((num_n_blocks + blocks_per_sm - 1) / blocks_per_sm, num_splits_static), 1);\n        // num_n_blocks per work tile for the batch\n        num_n_blocks = cutlass::ceil_div(num_n_blocks, num_splits_dynamic); \n    }\n\n    if constexpr (Sort) {\n        if(lane == kNumBatchPerWarp || batch_idx >= num_batch) {\n            num_n_blocks = INT_MIN; // sort last\n        } else if (is_causal) {\n            // sort by shortest member to process\n            num_n_blocks = num_n_blocks * blockn_divmod.divisor - num_m_blocks * blockm_divmod.divisor;\n        }\n        int4 batch_coords[ITEMS_PER_THREAD]; // 1 item per thread\n        batch_coords[0] = make_int4(num_n_blocks, num_m_blocks, num_splits_dynamic, batch_idx);\n\n        // if (threadIdx.x == 0) {\n        //     printf(\"Unsorted: num_n_blocks - num_m_blocks = %d, num_m_blocks = %d, num_splits = %d, batch_idx = %d.\\n\", \n        //         batch_coords[0].x, batch_coords[0].y, batch_coords[0].z, batch_coords[0].w);\n        // } __syncthreads();\n\n        // Sort batches by num_n_blocks in descending order\n        BlockMergeSort(temp_storage).Sort(batch_coords, PrepareSortOp<int4>());\n\n        // if (threadIdx.x == 0) {\n        //     printf(\"Sorted: num_n_blocks - num_m_blocks = %d, num_m_blocks = %d, num_splits = %d, batch_idx = %d.\\n\", \n        //         batch_coords[0].x, batch_coords[0].y, batch_coords[0].z, batch_coords[0].w);\n        // } __syncthreads();\n\n        if (is_causal) {\n            // reset value to num_n_blocks\n            batch_coords[0].x = blockn_divmod.div(batch_coords[0].x + batch_coords[0].y * blockm_divmod.divisor);\n        }\n\n        // When sorting, we re-index some metadata by 'virtual batch index'\n        // and also store the vbidx -> bidx mapping.\n        // 1. num_nheads_in_l2_ptr: virtual_batch_idx -> num_nheads_in_l2[batch_idx]\n        // 2. num_splits_dynamic_ptr: virtual_batch_idx -> num_splits[batch_idx]\n        // 3. num_m_blocks_ptr: virtual_batch_idx -> num_m_blocks[batch_idx]\n        // 4. varlen_batch_idx_ptr: virtual_batch_idx -> batch_idx      \n        batch_idx = batch_cta_idx_offset + threadIdx.x;\n        if (batch_idx < num_batch && threadIdx.x < 992) {\n            // num_n_blocks_ptr[threadIdx.x] = max(batch_coords[0].x, 1);\n            if(num_nheads_in_l2_ptr) { num_nheads_in_l2_ptr[batch_idx] = get_nheads_in_l2(max(batch_coords[0].x, 1)); }\n            num_m_blocks_ptr[batch_idx] = batch_coords[0].y;\n            num_splits_dynamic_ptr[batch_idx] = batch_coords[0].z;\n            varlen_batch_idx_ptr[batch_idx] = batch_coords[0].w;\n        }  \n    } else {\n        if (batch_idx < num_batch && lane < kNumBatchPerWarp) {\n            // num_n_blocks_ptr[batch_idx] = max(num_n_blocks, 1);\n            if(num_nheads_in_l2_ptr) { num_nheads_in_l2_ptr[batch_idx] = get_nheads_in_l2(max(num_n_blocks, 1)); }\n            num_splits_dynamic_ptr[batch_idx] = num_splits_dynamic;\n            num_m_blocks_ptr[batch_idx] = num_m_blocks;\n            // printf(\"idx = %d, num_m_blocks = %d, num_n_blocks = %d, num_split_static = %d, num_splits_dynamic = %d\\n\", bidb_start + lane, num_m_blocks_ptr[bidb_start + lane], num_n_blocks, num_splits_static, num_splits_dynamic);\n        }\n    }\n    \n}\n\n} // flash\n\nvoid prepare_varlen_num_blocks(Flash_fwd_params &params, cudaStream_t stream, bool packgqa,\n                               int blockM, int blockN, bool enable_pdl) {\n    int qhead_per_khead = cutlass::ceil_div(params.h, params.h_k);\n    int num_warps = cutlass::ceil_div(params.b, 31); // warp switch will cap this at 32\n    int num_ctas = cutlass::ceil_div(params.b, 31 * 32);\n    // int const size_l2 = 50 * 1024 * 1024; // 50 MB\n    int const size_l2 = 8 * 1024 * 1024; // underestimate seems better in practice\n    int const element_size = params.is_e4m3 ? 1 : 2;\n    int const size_one_kvblock = blockN * (params.d + params.dv) * element_size;\n    // printf(\"block size = %d, element size = %d, headdim = %d, headdim_v = %d, size 1 kblock = %d.\\n\", blockN, element_size, params.d, params.dv, size_one_kvblock);\n    int const max_kvblocks_in_l2 = size_l2 / size_one_kvblock;\n    BOOL_SWITCH(params.varlen_sort_batches, Sort, [&] {\n        NUM_WARP_SWITCH(num_warps, NumWarps, [&] {\n            flash::prepare_varlen_num_blocks_kernel<NumWarps, Sort><<<num_ctas /*grid*/, 32 * NumWarps /*block*/, 0, stream>>>(\n                params.seqlen_q, params.seqlen_k, params.seqlen_knew,\n                params.cu_seqlens_q, params.cu_seqlens_k, params.cu_seqlens_knew,\n                params.seqused_q, params.seqused_k, params.leftpad_k,\n                params.b, !packgqa ? params.h : params.h_k, qhead_per_khead, params.num_sm, params.num_splits,\n                cutlass::FastDivmod(blockM), cutlass::FastDivmod(blockN),\n                params.tile_count_semaphore,\n                params.num_m_blocks_ptr,\n                params.num_splits_dynamic_ptr,\n                params.varlen_batch_idx_ptr,\n                // params.num_n_blocks_ptr,\n                params.num_nheads_in_l2_ptr,\n                enable_pdl,\n                params.is_causal,\n                packgqa,\n                max_kvblocks_in_l2);\n        });\n    });\n}\n", "output": "/******************************************************************************\n * Copyright (c) 2024, Jay Shah, Ganesh Bikshandi, Ying Zhang, Vijay Thakkar, Pradeep Ramani, Tri Dao.\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <hipcub/hipcub.hpp>\n#include \"cutlass/fast_math.h\"\n#include \"cutlass/barrier.h\"\n#include \"cutlass/arch/barrier.h\"\n\n#include \"cutlass/arch/grid_dependency_control.h\"\n\n#include \"flash.h\"\n\n#include \"static_switch.h\"\n\nnamespace flash {\n\n// Sort in descending order\ntemplate <typename T>\nstruct PrepareSortOp\n{\n    __device__ __forceinline__ bool operator()(T const & lhs, T const & rhs)\n    {\n        return lhs > rhs;\n    }\n};\n\ntemplate <>\nstruct PrepareSortOp<int2> {\n    __device__ __forceinline__ bool operator()(int2 const & lhs, int2 const & rhs) const {\n        return lhs.x > rhs.x;\n    }\n};\n\ntemplate <>\nstruct PrepareSortOp<int4> {\n    __device__ __forceinline__ bool operator()(int4 const & lhs, int4 const & rhs) const {\n        return lhs.x > rhs.x;\n    }\n};\n\ntemplate <int NumWarps, bool Sort>\n__global__ void prepare_varlen_num_blocks_kernel(\n        int seqlen_q_static, int seqlen_k_static, int seqlen_k_new_static,\n        int const* const cu_seqlens_q, int const* const cu_seqlens_k, int const* const cu_seqlens_k_new,\n        int const* const seqused_q, int const* const seqused_k, int const* const leftpad_k_ptr,\n        int num_batch, int num_head, int qhead_per_khead, int num_sm, int num_splits_static,\n        cutlass::FastDivmod blockm_divmod, cutlass::FastDivmod blockn_divmod,\n        int* const tile_count_semaphore,\n        int* const num_m_blocks_ptr,\n        int* const num_splits_dynamic_ptr,\n        int* const varlen_batch_idx_ptr,\n        // int* const num_n_blocks_ptr,\n        int* const num_nheads_in_l2_ptr,\n        bool enable_pdl,\n        bool is_causal,\n        bool packgqa,\n        int max_kvblocks_in_l2) {\n\n    static constexpr int kNumBatchPerWarp = cutlass::NumThreadsPerWarp - 1;\n    static constexpr int kSmemSize = 1;\n    static constexpr int BLOCK_DIM_X = NumWarps * 32;\n    static constexpr int ITEMS_PER_THREAD = 1;\n    static_assert(BLOCK_DIM_X * ITEMS_PER_THREAD == NumWarps * 32);\n    using BlockMergeSort = hipcub::BlockMergeSort<int4, BLOCK_DIM_X, ITEMS_PER_THREAD>;\n\n    __shared__ int total_blocks_smem[kSmemSize];\n\n    // Allocate shared memory for BlockMergeSort operations\n    __shared__ typename BlockMergeSort::TempStorage temp_storage;\n\n    if (enable_pdl) { cutlass::arch::launch_dependent_grids(); }\n\n    if (threadIdx.x < kSmemSize) { total_blocks_smem[threadIdx.x] = 0; }\n    __syncthreads();\n\n    if (threadIdx.x == 0 && tile_count_semaphore) { *tile_count_semaphore = 0; }\n\n    int lane = threadIdx.x % cutlass::NumThreadsPerWarp;\n\n    auto get_num_m_blocks = [&](int batch_idx) {\n        int seqlen;\n        if (seqused_q) {\n            seqlen = batch_idx < num_batch ? seqused_q[batch_idx] : 0;\n        } else if (cu_seqlens_q) {\n            int cur_cu_seqlen = batch_idx <= num_batch ? cu_seqlens_q[batch_idx] : 0;\n            int next_cu_seqlen = __shfl_down_sync(0xffffffff, cur_cu_seqlen, 1);\n            seqlen = next_cu_seqlen - cur_cu_seqlen;\n        } else {\n            seqlen = seqlen_q_static;\n        }\n        if(packgqa) { seqlen *= qhead_per_khead; }\n        return batch_idx < num_batch && lane < kNumBatchPerWarp\n            ? blockm_divmod.div(seqlen + blockm_divmod.divisor - 1) : 0;\n    };\n\n    auto get_num_n_blocks = [&](int batch_idx) {\n        int leftpad_k = batch_idx < num_batch && leftpad_k_ptr != nullptr ? leftpad_k_ptr[batch_idx] : 0;\n        int seqlen;\n        if (seqused_k) {\n            seqlen = batch_idx < num_batch ? seqused_k[batch_idx] : 0;\n        } else if (cu_seqlens_k) {\n            int cur_cu_seqlen = batch_idx <= num_batch ? cu_seqlens_k[batch_idx] : 0;\n            int next_cu_seqlen = __shfl_down_sync(0xffffffff, cur_cu_seqlen, 1);\n            seqlen = next_cu_seqlen - cur_cu_seqlen;\n        } else {\n            seqlen = seqlen_k_static;\n        }\n        int seqlen_new;\n        if (cu_seqlens_k_new) {\n            int cur_cu_seqlen_new = batch_idx <= num_batch ? cu_seqlens_k_new[batch_idx] : 0;\n            int next_cu_seqlen_new = __shfl_down_sync(0xffffffff, cur_cu_seqlen_new, 1);\n            seqlen_new = next_cu_seqlen_new - cur_cu_seqlen_new;\n        } else {\n            seqlen_new = seqlen_k_new_static;\n        }\n        // if (threadIdx.x == 0) { printf(\"seqlen = %d, seqlen_new = %d, leftpad_k = %d\\n\", seqlen, seqlen_new, leftpad_k); }\n        seqlen = seqlen - leftpad_k + seqlen_new;\n        return batch_idx < num_batch && lane < kNumBatchPerWarp\n            ? blockn_divmod.div(seqlen + blockn_divmod.divisor - 1) : 0;\n    };\n\n    int warp_idx = threadIdx.x / cutlass::NumThreadsPerWarp;\n    int batch_cta_idx_offset = int(blockIdx.x) * 992;\n    int bidb_start = batch_cta_idx_offset + kNumBatchPerWarp * warp_idx;\n    int batch_idx = lane + bidb_start;\n    int num_m_blocks = get_num_m_blocks(batch_idx);\n    int num_n_blocks = get_num_n_blocks(batch_idx);\n\n    auto get_nheads_in_l2 = [&](int n_blocks) {\n        int nheads_in_l2 = n_blocks * 16 <= max_kvblocks_in_l2 ? 16\n            : n_blocks * 8 <= max_kvblocks_in_l2 ? 8\n            : n_blocks * 4 <= max_kvblocks_in_l2 ? 4\n            : n_blocks * 2 <= max_kvblocks_in_l2 ? 2\n            : 1;\n        if(!packgqa) { nheads_in_l2 *= qhead_per_khead; }\n        return min(nheads_in_l2, num_head);\n    };\n    \n    int num_splits_dynamic;\n    if (int(gridDim.x) > 1 || num_splits_static == 1) {\n        // set num splits for all batches to 1 (note that user expects num_splits_static to mean upper bound on splits)\n        // for batch size > 992, we expect GPU occupancy to not be an issue except in degenerate cases (e.g., most are zero-length)\n        num_splits_dynamic = 1;\n    } else {\n        int total_blocks = num_m_blocks * num_n_blocks;\n        // Warp sum\n        #pragma unroll\n        for (int i = cutlass::NumThreadsPerWarp / 2; i >= 1; i /= 2) {\n            total_blocks += __shfl_down_sync(0xffffffff, total_blocks, i);\n        }\n        if (lane == 0) { atomicAdd(total_blocks_smem, total_blocks); }\n        __syncthreads();\n        total_blocks = total_blocks_smem[0];\n        // 10% margin\n        int blocks_per_sm = static_cast<int>(ceilf(float(total_blocks) * 1.1f * float(num_head) / float(num_sm)));\n        // blocks_per_sm = std::max(1, blocks_per_sm);  // 1 is the minimum number of blocks per SM\n        num_splits_dynamic = std::max(std::min((num_n_blocks + blocks_per_sm - 1) / blocks_per_sm, num_splits_static), 1);\n        // num_n_blocks per work tile for the batch\n        num_n_blocks = cutlass::ceil_div(num_n_blocks, num_splits_dynamic); \n    }\n\n    if constexpr (Sort) {\n        if(lane == kNumBatchPerWarp || batch_idx >= num_batch) {\n            num_n_blocks = INT_MIN; // sort last\n        } else if (is_causal) {\n            // sort by shortest member to process\n            num_n_blocks = num_n_blocks * blockn_divmod.divisor - num_m_blocks * blockm_divmod.divisor;\n        }\n        int4 batch_coords[ITEMS_PER_THREAD]; // 1 item per thread\n        batch_coords[0] = make_int4(num_n_blocks, num_m_blocks, num_splits_dynamic, batch_idx);\n\n        // if (threadIdx.x == 0) {\n        //     printf(\"Unsorted: num_n_blocks - num_m_blocks = %d, num_m_blocks = %d, num_splits = %d, batch_idx = %d.\\n\", \n        //         batch_coords[0].x, batch_coords[0].y, batch_coords[0].z, batch_coords[0].w);\n        // } __syncthreads();\n\n        // Sort batches by num_n_blocks in descending order\n        BlockMergeSort(temp_storage).Sort(batch_coords, PrepareSortOp<int4>());\n\n        // if (threadIdx.x == 0) {\n        //     printf(\"Sorted: num_n_blocks - num_m_blocks = %d, num_m_blocks = %d, num_splits = %d, batch_idx = %d.\\n\", \n        //         batch_coords[0].x, batch_coords[0].y, batch_coords[0].z, batch_coords[0].w);\n        // } __syncthreads();\n\n        if (is_causal) {\n            // reset value to num_n_blocks\n            batch_coords[0].x = blockn_divmod.div(batch_coords[0].x + batch_coords[0].y * blockm_divmod.divisor);\n        }\n\n        // When sorting, we re-index some metadata by 'virtual batch index'\n        // and also store the vbidx -> bidx mapping.\n        // 1. num_nheads_in_l2_ptr: virtual_batch_idx -> num_nheads_in_l2[batch_idx]\n        // 2. num_splits_dynamic_ptr: virtual_batch_idx -> num_splits[batch_idx]\n        // 3. num_m_blocks_ptr: virtual_batch_idx -> num_m_blocks[batch_idx]\n        // 4. varlen_batch_idx_ptr: virtual_batch_idx -> batch_idx      \n        batch_idx = batch_cta_idx_offset + threadIdx.x;\n        if (batch_idx < num_batch && threadIdx.x < 992) {\n            // num_n_blocks_ptr[threadIdx.x] = max(batch_coords[0].x, 1);\n            if(num_nheads_in_l2_ptr) { num_nheads_in_l2_ptr[batch_idx] = get_nheads_in_l2(max(batch_coords[0].x, 1)); }\n            num_m_blocks_ptr[batch_idx] = batch_coords[0].y;\n            num_splits_dynamic_ptr[batch_idx] = batch_coords[0].z;\n            varlen_batch_idx_ptr[batch_idx] = batch_coords[0].w;\n        }  \n    } else {\n        if (batch_idx < num_batch && lane < kNumBatchPerWarp) {\n            // num_n_blocks_ptr[batch_idx] = max(num_n_blocks, 1);\n            if(num_nheads_in_l2_ptr) { num_nheads_in_l2_ptr[batch_idx] = get_nheads_in_l2(max(num_n_blocks, 1)); }\n            num_splits_dynamic_ptr[batch_idx] = num_splits_dynamic;\n            num_m_blocks_ptr[batch_idx] = num_m_blocks;\n            // printf(\"idx = %d, num_m_blocks = %d, num_n_blocks = %d, num_split_static = %d, num_splits_dynamic = %d\\n\", bidb_start + lane, num_m_blocks_ptr[bidb_start + lane], num_n_blocks, num_splits_static, num_splits_dynamic);\n        }\n    }\n    \n}\n\n} // flash\n\nvoid prepare_varlen_num_blocks(Flash_fwd_params &params, hipStream_t stream, bool packgqa,\n                               int blockM, int blockN, bool enable_pdl) {\n    int qhead_per_khead = cutlass::ceil_div(params.h, params.h_k);\n    int num_warps = cutlass::ceil_div(params.b, 31); // warp switch will cap this at 32\n    int num_ctas = cutlass::ceil_div(params.b, 31 * 32);\n    // int const size_l2 = 50 * 1024 * 1024; // 50 MB\n    int const size_l2 = 8 * 1024 * 1024; // underestimate seems better in practice\n    int const element_size = params.is_e4m3 ? 1 : 2;\n    int const size_one_kvblock = blockN * (params.d + params.dv) * element_size;\n    // printf(\"block size = %d, element size = %d, headdim = %d, headdim_v = %d, size 1 kblock = %d.\\n\", blockN, element_size, params.d, params.dv, size_one_kvblock);\n    int const max_kvblocks_in_l2 = size_l2 / size_one_kvblock;\n    BOOL_SWITCH(params.varlen_sort_batches, Sort, [&] {\n        NUM_WARP_SWITCH(num_warps, NumWarps, [&] {\n            flash::prepare_varlen_num_blocks_kernel<NumWarps, Sort><<<num_ctas /*grid*/, 32 * NumWarps /*block*/, 0, stream>>>(\n                params.seqlen_q, params.seqlen_k, params.seqlen_knew,\n                params.cu_seqlens_q, params.cu_seqlens_k, params.cu_seqlens_knew,\n                params.seqused_q, params.seqused_k, params.leftpad_k,\n                params.b, !packgqa ? params.h : params.h_k, qhead_per_khead, params.num_sm, params.num_splits,\n                cutlass::FastDivmod(blockM), cutlass::FastDivmod(blockN),\n                params.tile_count_semaphore,\n                params.num_m_blocks_ptr,\n                params.num_splits_dynamic_ptr,\n                params.varlen_batch_idx_ptr,\n                // params.num_n_blocks_ptr,\n                params.num_nheads_in_l2_ptr,\n                enable_pdl,\n                params.is_causal,\n                packgqa,\n                max_kvblocks_in_l2);\n        });\n    });\n}\n", "source": "/kaggle/working/flash-attention/hopper/flash_prepare_scheduler.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * APIC Wave Simulation Example\n *\n * This program demonstrates how to:\n * 1. Load a pre-captured CUDA graph (.wrp file) using Warp's APIC API\n * 2. Execute the graph with dynamic input parameters\n * 3. Visualize the results using GLFW/OpenGL\n *\n * The wave simulation graph is captured by the Python script (capture_wave.py)\n * and contains multiple kernel launches for a full simulation frame.\n *\n * Key APIC concepts demonstrated:\n * - Graph contains 17 kernel launches (1 displacement + 16 wave solve)\n * - C++ issues ONE cudaGraphLaunch() per frame\n * - Mouse position is passed as a dynamic parameter\n * - No graph rebuilding needed - structure is fixed, only data changes\n *\n * See README.md for build instructions.\n */\n\n// clang-format off\n// Include order matters: GLAD must come before other GL headers,\n// and aot.h (CUDA) must come after GLAD to avoid type conflicts\n#include <glad/gl.h>\n#include <GLFW/glfw3.h>\n\n#include \"aot.h\"  // Warp AOT utilities (includes CUDA)\n#include \"warp.h\" // Warp C API\n#include \"apic.h\" // APIC graph loading and execution\n\n#include <cmath>\n#include <cstdio>\n#include <vector>\n// clang-format on\n\n// Simulation constants\nconstexpr int GRID_WIDTH = 128;\nconstexpr int GRID_HEIGHT = 128;\nconstexpr float GRID_SCALE = 0.1f;  // World-space size per grid cell\n\n// Window and mouse state\nstatic int g_window_width = 800;\nstatic int g_window_height = 600;\nstatic double g_mouse_x = 0;\nstatic double g_mouse_y = 0;\nstatic bool g_mouse_down = false;\nstatic float g_camera_distance = 20.0f;\nstatic float g_camera_angle_x = 45.0f;\nstatic float g_camera_angle_y = 45.0f;\nstatic double g_last_mouse_x = 0;\nstatic double g_last_mouse_y = 0;\nstatic bool g_right_mouse_down = false;\n\n// GLFW callbacks\nvoid mouse_button_callback(GLFWwindow* window, int button, int action, int mods)\n{\n    if (button == GLFW_MOUSE_BUTTON_LEFT) {\n        g_mouse_down = (action == GLFW_PRESS);\n    }\n    if (button == GLFW_MOUSE_BUTTON_RIGHT) {\n        g_right_mouse_down = (action == GLFW_PRESS);\n        if (action == GLFW_PRESS) {\n            glfwGetCursorPos(window, &g_last_mouse_x, &g_last_mouse_y);\n        }\n    }\n}\n\nvoid cursor_pos_callback(GLFWwindow* window, double x, double y)\n{\n    g_mouse_x = x;\n    g_mouse_y = y;\n\n    if (g_right_mouse_down) {\n        double dx = x - g_last_mouse_x;\n        double dy = y - g_last_mouse_y;\n        g_camera_angle_y += (float)dx * 0.5f;\n        g_camera_angle_x += (float)dy * 0.5f;\n        g_camera_angle_x = fmaxf(-89.0f, fminf(89.0f, g_camera_angle_x));\n        g_last_mouse_x = x;\n        g_last_mouse_y = y;\n    }\n}\n\nvoid scroll_callback(GLFWwindow* window, double xoffset, double yoffset)\n{\n    g_camera_distance -= (float)yoffset * 2.0f;\n    g_camera_distance = fmaxf(5.0f, fminf(50.0f, g_camera_distance));\n}\n\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height)\n{\n    g_window_width = width;\n    g_window_height = height;\n    glViewport(0, 0, width, height);\n}\n\n// Simple matrix utilities\nvoid mat4_identity(float* m)\n{\n    for (int i = 0; i < 16; i++)\n        m[i] = 0.0f;\n    m[0] = m[5] = m[10] = m[15] = 1.0f;\n}\n\nvoid mat4_perspective(float* m, float fov, float aspect, float z_near, float z_far)\n{\n    float f = 1.0f / tanf(fov * 0.5f);\n    mat4_identity(m);\n    m[0] = f / aspect;\n    m[5] = f;\n    m[10] = (z_far + z_near) / (z_near - z_far);\n    m[11] = -1.0f;\n    m[14] = (2.0f * z_far * z_near) / (z_near - z_far);\n    m[15] = 0.0f;\n}\n\nvoid mat4_look_at(float* m, float ex, float ey, float ez, float cx, float cy, float cz, float ux, float uy, float uz)\n{\n    float fx = cx - ex, fy = cy - ey, fz = cz - ez;\n    float len = sqrtf(fx * fx + fy * fy + fz * fz);\n    fx /= len;\n    fy /= len;\n    fz /= len;\n\n    float sx = fy * uz - fz * uy;\n    float sy = fz * ux - fx * uz;\n    float sz = fx * uy - fy * ux;\n    len = sqrtf(sx * sx + sy * sy + sz * sz);\n    sx /= len;\n    sy /= len;\n    sz /= len;\n\n    float ux2 = sy * fz - sz * fy;\n    float uy2 = sz * fx - sx * fz;\n    float uz2 = sx * fy - sy * fx;\n\n    mat4_identity(m);\n    m[0] = sx;\n    m[4] = sy;\n    m[8] = sz;\n    m[1] = ux2;\n    m[5] = uy2;\n    m[9] = uz2;\n    m[2] = -fx;\n    m[6] = -fy;\n    m[10] = -fz;\n    m[12] = -(sx * ex + sy * ey + sz * ez);\n    m[13] = -(ux2 * ex + uy2 * ey + uz2 * ez);\n    m[14] = (fx * ex + fy * ey + fz * ez);\n}\n\nvoid mat4_multiply(float* result, const float* a, const float* b)\n{\n    float tmp[16];\n    for (int i = 0; i < 4; i++) {\n        for (int j = 0; j < 4; j++) {\n            tmp[j * 4 + i]\n                = a[i] * b[j * 4] + a[4 + i] * b[j * 4 + 1] + a[8 + i] * b[j * 4 + 2] + a[12 + i] * b[j * 4 + 3];\n        }\n    }\n    for (int i = 0; i < 16; i++)\n        result[i] = tmp[i];\n}\n\n// Shaders\nconst char* vertex_shader_src = R\"(\n#version 330 core\nlayout(location = 0) in vec3 position;\nuniform mat4 mvp;\nout vec3 world_pos;\nvoid main() {\n    world_pos = position;\n    gl_Position = mvp * vec4(position, 1.0);\n}\n)\";\n\nconst char* fragment_shader_src = R\"(\n#version 330 core\nin vec3 world_pos;\nuniform vec3 light_dir;\nout vec4 frag_color;\nvoid main() {\n    // Compute normal from derivatives\n    vec3 dx = dFdx(world_pos);\n    vec3 dy = dFdy(world_pos);\n    vec3 normal = normalize(cross(dx, dy));\n\n    // Simple diffuse + ambient lighting\n    float diffuse = max(dot(normal, light_dir), 0.0);\n    vec3 color = vec3(0.2, 0.4, 0.8);  // Blue water color\n    vec3 ambient = color * 0.3;\n    vec3 lit = color * diffuse * 0.7 + ambient;\n    frag_color = vec4(lit, 1.0);\n}\n)\";\n\nGLuint compile_shader(GLenum type, const char* src)\n{\n    GLuint shader = glCreateShader(type);\n    glShaderSource(shader, 1, &src, nullptr);\n    glCompileShader(shader);\n\n    GLint success;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);\n    if (!success) {\n        char log[512];\n        glGetShaderInfoLog(shader, 512, nullptr, log);\n        fprintf(stderr, \"Shader compilation failed: %s\\n\", log);\n    }\n    return shader;\n}\n\nGLuint create_shader_program()\n{\n    GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader_src);\n    GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src);\n\n    GLuint program = glCreateProgram();\n    glAttachShader(program, vs);\n    glAttachShader(program, fs);\n    glLinkProgram(program);\n\n    GLint success;\n    glGetProgramiv(program, GL_LINK_STATUS, &success);\n    if (!success) {\n        char log[512];\n        glGetProgramInfoLog(program, 512, nullptr, log);\n        fprintf(stderr, \"Program linking failed: %s\\n\", log);\n    }\n\n    glDeleteShader(vs);\n    glDeleteShader(fs);\n\n    return program;\n}\n\n// CUDA kernel to update mesh vertices from height field\n__global__ void update_vertices(float* vertices, const float* heights, int width, int height, float scale)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (tid >= width * height)\n        return;\n\n    int x = tid % width;\n    int y = tid / width;\n\n    // Update Y coordinate (height), keep X and Z\n    vertices[tid * 3 + 0] = x * scale;\n    vertices[tid * 3 + 1] = heights[tid];\n    vertices[tid * 3 + 2] = y * scale;\n}\n\nint main(int argc, char** argv)\n{\n    printf(\"=== APIC Wave Simulation Example ===\\n\\n\");\n\n    // Parse command line\n    const char* graph_path = \"generated/wave_sim\";\n    bool smoke = false;\n    for (int i = 1; i < argc; i++) {\n        if (strcmp(argv[i], \"--graph\") == 0 && i + 1 < argc) {\n            graph_path = argv[++i];\n        } else if (strcmp(argv[i], \"--smoke\") == 0) {\n            smoke = true;\n        }\n    }\n\n    // Initialize CUDA\n    printf(\"Initializing CUDA...\\n\");\n    CHECK_CU(cuInit(0));\n\n    CUdevice device;\n    CHECK_CU(cuDeviceGet(&device, 0));\n\n    char device_name[256];\n    CHECK_CU(cuDeviceGetName(device_name, sizeof(device_name), device));\n    printf(\"Using device: %s\\n\", device_name);\n\n    CUcontext context;\n    CHECK_CU(cuDevicePrimaryCtxRetain(&context, device));\n    CHECK_CU(cuCtxSetCurrent(context));\n\n    // Initialize Warp runtime (wp_init returns 0 on success)\n    printf(\"Initializing Warp runtime...\\n\");\n    wp_init(nullptr);\n\n    // Load APIC graph\n    printf(\"\\nLoading APIC graph from: %s\\n\", graph_path);\n    APICGraph graph = wp_apic_load_graph(context, graph_path, 0);  // 0 = CUDA device\n    if (!graph) {\n        fprintf(stderr, \"Failed to load graph: %s\\n\", wp_get_error_string());\n        return 1;\n    }\n\n    // Query and print graph parameters\n    int n_params = wp_apic_get_num_params(graph);\n    printf(\"Graph parameters (%d):\\n\", n_params);\n    for (int i = 0; i < n_params; i++) {\n        const char* name = wp_apic_get_param_name(graph, i);\n        size_t size = wp_apic_get_param_size(graph, name);\n        printf(\"  [%d] %s: %zu bytes\\n\", i, name, size);\n    }\n\n    if (smoke) {\n        cudaGraphExec_t exec = (cudaGraphExec_t)wp_apic_get_cuda_graph_exec(graph);\n        if (!exec) {\n            fprintf(stderr, \"Failed to get graph executable: %s\\n\", wp_get_error_string());\n            wp_apic_destroy_graph(graph);\n            return 1;\n        }\n        cudaStream_t stream;\n        CHECK_CUDA(cudaStreamCreate(&stream));\n        const int kSmokeIterations = 10;\n        for (int i = 0; i < kSmokeIterations; i++) {\n            CHECK_CUDA(cudaGraphLaunch(exec, stream));\n        }\n        CHECK_CUDA(cudaStreamSynchronize(stream));\n        CHECK_CUDA(cudaStreamDestroy(stream));\n        printf(\"smoke OK (%d graph launches)\\n\", kSmokeIterations);\n        wp_apic_destroy_graph(graph);\n        return 0;\n    }\n\n    // Initialize GLFW\n    printf(\"\\nInitializing GLFW/OpenGL...\\n\");\n    if (!glfwInit()) {\n        fprintf(stderr, \"Failed to initialize GLFW\\n\");\n        return 1;\n    }\n\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n#ifdef __APPLE__\n    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);\n#endif\n\n    GLFWwindow* window = glfwCreateWindow(g_window_width, g_window_height, \"APIC Wave Simulation\", nullptr, nullptr);\n    if (!window) {\n        fprintf(stderr, \"Failed to create window\\n\");\n        glfwTerminate();\n        return 1;\n    }\n\n    glfwMakeContextCurrent(window);\n    glfwSetMouseButtonCallback(window, mouse_button_callback);\n    glfwSetCursorPosCallback(window, cursor_pos_callback);\n    glfwSetScrollCallback(window, scroll_callback);\n    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n\n    // Load OpenGL functions using GLAD\n    if (!gladLoadGL(glfwGetProcAddress)) {\n        fprintf(stderr, \"Failed to initialize GLAD\\n\");\n        glfwTerminate();\n        return 1;\n    }\n\n    // Create shader program\n    GLuint program = create_shader_program();\n    GLint mvp_loc = glGetUniformLocation(program, \"mvp\");\n    GLint light_loc = glGetUniformLocation(program, \"light_dir\");\n\n    // Create mesh (grid of vertices)\n    printf(\"Creating mesh (%dx%d)...\\n\", GRID_WIDTH, GRID_HEIGHT);\n    std::vector<float> vertices(GRID_WIDTH * GRID_HEIGHT * 3);\n    std::vector<unsigned int> indices;\n\n    for (int y = 0; y < GRID_HEIGHT; y++) {\n        for (int x = 0; x < GRID_WIDTH; x++) {\n            int i = y * GRID_WIDTH + x;\n            vertices[i * 3 + 0] = x * GRID_SCALE;\n            vertices[i * 3 + 1] = 0.0f;\n            vertices[i * 3 + 2] = y * GRID_SCALE;\n\n            if (x > 0 && y > 0) {\n                int i00 = (y - 1) * GRID_WIDTH + (x - 1);\n                int i10 = (y - 1) * GRID_WIDTH + x;\n                int i01 = y * GRID_WIDTH + (x - 1);\n                int i11 = y * GRID_WIDTH + x;\n\n                indices.push_back(i00);\n                indices.push_back(i10);\n                indices.push_back(i11);\n\n                indices.push_back(i00);\n                indices.push_back(i11);\n                indices.push_back(i01);\n            }\n        }\n    }\n\n    // Create OpenGL buffers\n    GLuint vao, vbo, ebo;\n    glGenVertexArrays(1, &vao);\n    glGenBuffers(1, &vbo);\n    glGenBuffers(1, &ebo);\n\n    glBindVertexArray(vao);\n\n    glBindBuffer(GL_ARRAY_BUFFER, vbo);\n    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW);\n\n    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);\n    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);\n\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0);\n\n    // Allocate CUDA arrays (two height buffers for wave equation)\n    size_t heights_size = GRID_WIDTH * GRID_HEIGHT * sizeof(float);\n    float* d_heights[2];  // Double buffer: [0]=current, [1]=previous\n    float* d_vertices;\n    float* d_mouse_pos;\n    int current_buffer = 0;\n\n    CHECK_CUDA(cudaMalloc(&d_heights[0], heights_size));\n    CHECK_CUDA(cudaMalloc(&d_heights[1], heights_size));\n    CHECK_CUDA(cudaMalloc(&d_vertices, vertices.size() * sizeof(float)));\n    CHECK_CUDA(cudaMalloc(&d_mouse_pos, 2 * sizeof(float)));\n\n    CHECK_CUDA(cudaMemset(d_heights[0], 0, heights_size));\n    CHECK_CUDA(cudaMemset(d_heights[1], 0, heights_size));\n\n    // Create CUDA stream\n    cudaStream_t stream;\n    CHECK_CUDA(cudaStreamCreate(&stream));\n\n    // Get graph executable (builds on first call)\n    printf(\"\\nBuilding CUDA graph executable...\\n\");\n    cudaGraphExec_t exec = (cudaGraphExec_t)wp_apic_get_cuda_graph_exec(graph);\n    if (!exec) {\n        fprintf(stderr, \"Failed to get graph executable: %s\\n\", wp_get_error_string());\n        return 1;\n    }\n\n    printf(\"\\nStarting simulation loop...\\n\");\n    printf(\"  Left-click to create waves\\n\");\n    printf(\"  Right-drag to rotate camera\\n\");\n    printf(\"  Scroll to zoom\\n\\n\");\n\n    glEnable(GL_DEPTH_TEST);\n\n    int frame = 0;\n    double last_time = glfwGetTime();\n    int fps_counter = 0;\n\n    // Main loop\n    while (!glfwWindowShouldClose(window)) {\n        glfwPollEvents();\n\n        // Compute mouse position in grid coordinates\n        float mouse_grid[2] = { -1000.0f, -1000.0f };  // Off-grid by default\n        if (g_mouse_down) {\n            // Simple mapping: screen coords to grid coords\n            // (approximate - proper implementation would use ray casting)\n            mouse_grid[0] = (float)(g_mouse_x / g_window_width) * GRID_WIDTH;\n            mouse_grid[1] = (float)(g_mouse_y / g_window_height) * GRID_HEIGHT;\n        }\n\n        // Upload mouse position\n        CHECK_CUDA(cudaMemcpyAsync(d_mouse_pos, mouse_grid, 2 * sizeof(float), cudaMemcpyHostToDevice, stream));\n\n        // Set graph input parameters (wave eq needs current and previous heights)\n        wp_apic_set_param(graph, \"heights\", d_heights[current_buffer], heights_size);\n        wp_apic_set_param(graph, \"heights_prev\", d_heights[1 - current_buffer], heights_size);\n        wp_apic_set_param(graph, \"mouse_pos\", d_mouse_pos, 2 * sizeof(float));\n\n        // Execute the graph (runs all 17 kernels in one launch!)\n        CHECK_CUDA(cudaGraphLaunch(exec, stream));\n        CHECK_CUDA(cudaStreamSynchronize(stream));  // Must sync before reading results\n\n        // Get output heights (swap buffers for next frame)\n        wp_apic_get_param(graph, \"heights_out\", d_heights[1 - current_buffer], heights_size);\n        wp_apic_get_param(graph, \"heights_prev_out\", d_heights[current_buffer], heights_size);\n        current_buffer = 1 - current_buffer;\n\n        // Update vertex positions from heights (current_buffer now points to heights_out)\n        int block_size = 256;\n        int num_blocks = (GRID_WIDTH * GRID_HEIGHT + block_size - 1) / block_size;\n        update_vertices<<<num_blocks, block_size, 0, stream>>>(\n            d_vertices, d_heights[current_buffer], GRID_WIDTH, GRID_HEIGHT, GRID_SCALE\n        );\n\n        // Copy vertices to host for OpenGL\n        CHECK_CUDA(cudaMemcpyAsync(\n            vertices.data(), d_vertices, vertices.size() * sizeof(float), cudaMemcpyDeviceToHost, stream\n        ));\n        CHECK_CUDA(cudaStreamSynchronize(stream));\n\n        // Update VBO\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(float), vertices.data());\n\n        // Compute camera position\n        float cx = GRID_WIDTH * GRID_SCALE * 0.5f;\n        float cy = 0.0f;\n        float cz = GRID_HEIGHT * GRID_SCALE * 0.5f;\n\n        float angle_x_rad = g_camera_angle_x * 3.14159f / 180.0f;\n        float angle_y_rad = g_camera_angle_y * 3.14159f / 180.0f;\n\n        float eye_x = cx + g_camera_distance * cosf(angle_x_rad) * sinf(angle_y_rad);\n        float eye_y = cy + g_camera_distance * sinf(angle_x_rad);\n        float eye_z = cz + g_camera_distance * cosf(angle_x_rad) * cosf(angle_y_rad);\n\n        // Compute MVP matrix\n        float proj[16], view[16], mvp[16];\n        mat4_perspective(proj, 60.0f * 3.14159f / 180.0f, (float)g_window_width / g_window_height, 0.1f, 100.0f);\n        mat4_look_at(view, eye_x, eye_y, eye_z, cx, cy, cz, 0, 1, 0);\n        mat4_multiply(mvp, proj, view);\n\n        // Render\n        glClearColor(0.1f, 0.1f, 0.15f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n        glUseProgram(program);\n        glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, mvp);\n\n        // Light direction (normalized)\n        float lx = 0.5f, ly = 0.8f, lz = 0.3f;\n        float ll = sqrtf(lx * lx + ly * ly + lz * lz);\n        glUniform3f(light_loc, lx / ll, ly / ll, lz / ll);\n\n        glBindVertexArray(vao);\n        glDrawElements(GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, 0);\n\n        glfwSwapBuffers(window);\n\n        // FPS counter\n        fps_counter++;\n        double current_time = glfwGetTime();\n        if (current_time - last_time >= 1.0) {\n            char title[256];\n            snprintf(title, sizeof(title), \"APIC Wave Simulation - %d FPS\", fps_counter);\n            glfwSetWindowTitle(window, title);\n            fps_counter = 0;\n            last_time = current_time;\n        }\n\n        frame++;\n    }\n\n    // Cleanup\n    printf(\"\\nCleaning up...\\n\");\n\n    CHECK_CUDA(cudaStreamDestroy(stream));\n    CHECK_CUDA(cudaFree(d_heights[0]));\n    CHECK_CUDA(cudaFree(d_heights[1]));\n    CHECK_CUDA(cudaFree(d_vertices));\n    CHECK_CUDA(cudaFree(d_mouse_pos));\n\n    glDeleteVertexArrays(1, &vao);\n    glDeleteBuffers(1, &vbo);\n    glDeleteBuffers(1, &ebo);\n    glDeleteProgram(program);\n\n    wp_apic_destroy_graph(graph);\n\n    glfwDestroyWindow(window);\n    glfwTerminate();\n\n    CHECK_CU(cuDevicePrimaryCtxRelease(device));\n\n    printf(\"Done.\\n\");\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * APIC Wave Simulation Example\n *\n * This program demonstrates how to:\n * 1. Load a pre-captured CUDA graph (.wrp file) using Warp's APIC API\n * 2. Execute the graph with dynamic input parameters\n * 3. Visualize the results using GLFW/OpenGL\n *\n * The wave simulation graph is captured by the Python script (capture_wave.py)\n * and contains multiple kernel launches for a full simulation frame.\n *\n * Key APIC concepts demonstrated:\n * - Graph contains 17 kernel launches (1 displacement + 16 wave solve)\n * - C++ issues ONE cudaGraphLaunch() per frame\n * - Mouse position is passed as a dynamic parameter\n * - No graph rebuilding needed - structure is fixed, only data changes\n *\n * See README.md for build instructions.\n */\n\n// clang-format off\n// Include order matters: GLAD must come before other GL headers,\n// and aot.h (CUDA) must come after GLAD to avoid type conflicts\n#include \"hip/hip_runtime.h\"\n#include <glad/gl.h>\n#include <GLFW/glfw3.h>\n\n#include \"aot.h\"  // Warp AOT utilities (includes CUDA)\n#include \"warp.h\" // Warp C API\n#include \"apic.h\" // APIC graph loading and execution\n\n#include <cmath>\n#include <cstdio>\n#include <vector>\n// clang-format on\n\n// Simulation constants\nconstexpr int GRID_WIDTH = 128;\nconstexpr int GRID_HEIGHT = 128;\nconstexpr float GRID_SCALE = 0.1f;  // World-space size per grid cell\n\n// Window and mouse state\nstatic int g_window_width = 800;\nstatic int g_window_height = 600;\nstatic double g_mouse_x = 0;\nstatic double g_mouse_y = 0;\nstatic bool g_mouse_down = false;\nstatic float g_camera_distance = 20.0f;\nstatic float g_camera_angle_x = 45.0f;\nstatic float g_camera_angle_y = 45.0f;\nstatic double g_last_mouse_x = 0;\nstatic double g_last_mouse_y = 0;\nstatic bool g_right_mouse_down = false;\n\n// GLFW callbacks\nvoid mouse_button_callback(GLFWwindow* window, int button, int action, int mods)\n{\n    if (button == GLFW_MOUSE_BUTTON_LEFT) {\n        g_mouse_down = (action == GLFW_PRESS);\n    }\n    if (button == GLFW_MOUSE_BUTTON_RIGHT) {\n        g_right_mouse_down = (action == GLFW_PRESS);\n        if (action == GLFW_PRESS) {\n            glfwGetCursorPos(window, &g_last_mouse_x, &g_last_mouse_y);\n        }\n    }\n}\n\nvoid cursor_pos_callback(GLFWwindow* window, double x, double y)\n{\n    g_mouse_x = x;\n    g_mouse_y = y;\n\n    if (g_right_mouse_down) {\n        double dx = x - g_last_mouse_x;\n        double dy = y - g_last_mouse_y;\n        g_camera_angle_y += (float)dx * 0.5f;\n        g_camera_angle_x += (float)dy * 0.5f;\n        g_camera_angle_x = fmaxf(-89.0f, fminf(89.0f, g_camera_angle_x));\n        g_last_mouse_x = x;\n        g_last_mouse_y = y;\n    }\n}\n\nvoid scroll_callback(GLFWwindow* window, double xoffset, double yoffset)\n{\n    g_camera_distance -= (float)yoffset * 2.0f;\n    g_camera_distance = fmaxf(5.0f, fminf(50.0f, g_camera_distance));\n}\n\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height)\n{\n    g_window_width = width;\n    g_window_height = height;\n    glViewport(0, 0, width, height);\n}\n\n// Simple matrix utilities\nvoid mat4_identity(float* m)\n{\n    for (int i = 0; i < 16; i++)\n        m[i] = 0.0f;\n    m[0] = m[5] = m[10] = m[15] = 1.0f;\n}\n\nvoid mat4_perspective(float* m, float fov, float aspect, float z_near, float z_far)\n{\n    float f = 1.0f / tanf(fov * 0.5f);\n    mat4_identity(m);\n    m[0] = f / aspect;\n    m[5] = f;\n    m[10] = (z_far + z_near) / (z_near - z_far);\n    m[11] = -1.0f;\n    m[14] = (2.0f * z_far * z_near) / (z_near - z_far);\n    m[15] = 0.0f;\n}\n\nvoid mat4_look_at(float* m, float ex, float ey, float ez, float cx, float cy, float cz, float ux, float uy, float uz)\n{\n    float fx = cx - ex, fy = cy - ey, fz = cz - ez;\n    float len = sqrtf(fx * fx + fy * fy + fz * fz);\n    fx /= len;\n    fy /= len;\n    fz /= len;\n\n    float sx = fy * uz - fz * uy;\n    float sy = fz * ux - fx * uz;\n    float sz = fx * uy - fy * ux;\n    len = sqrtf(sx * sx + sy * sy + sz * sz);\n    sx /= len;\n    sy /= len;\n    sz /= len;\n\n    float ux2 = sy * fz - sz * fy;\n    float uy2 = sz * fx - sx * fz;\n    float uz2 = sx * fy - sy * fx;\n\n    mat4_identity(m);\n    m[0] = sx;\n    m[4] = sy;\n    m[8] = sz;\n    m[1] = ux2;\n    m[5] = uy2;\n    m[9] = uz2;\n    m[2] = -fx;\n    m[6] = -fy;\n    m[10] = -fz;\n    m[12] = -(sx * ex + sy * ey + sz * ez);\n    m[13] = -(ux2 * ex + uy2 * ey + uz2 * ez);\n    m[14] = (fx * ex + fy * ey + fz * ez);\n}\n\nvoid mat4_multiply(float* result, const float* a, const float* b)\n{\n    float tmp[16];\n    for (int i = 0; i < 4; i++) {\n        for (int j = 0; j < 4; j++) {\n            tmp[j * 4 + i]\n                = a[i] * b[j * 4] + a[4 + i] * b[j * 4 + 1] + a[8 + i] * b[j * 4 + 2] + a[12 + i] * b[j * 4 + 3];\n        }\n    }\n    for (int i = 0; i < 16; i++)\n        result[i] = tmp[i];\n}\n\n// Shaders\nconst char* vertex_shader_src = R\"(\n#version 330 core\nlayout(location = 0) in vec3 position;\nuniform mat4 mvp;\nout vec3 world_pos;\nvoid main() {\n    world_pos = position;\n    gl_Position = mvp * vec4(position, 1.0);\n}\n)\";\n\nconst char* fragment_shader_src = R\"(\n#version 330 core\nin vec3 world_pos;\nuniform vec3 light_dir;\nout vec4 frag_color;\nvoid main() {\n    // Compute normal from derivatives\n    vec3 dx = dFdx(world_pos);\n    vec3 dy = dFdy(world_pos);\n    vec3 normal = normalize(cross(dx, dy));\n\n    // Simple diffuse + ambient lighting\n    float diffuse = max(dot(normal, light_dir), 0.0);\n    vec3 color = vec3(0.2, 0.4, 0.8);  // Blue water color\n    vec3 ambient = color * 0.3;\n    vec3 lit = color * diffuse * 0.7 + ambient;\n    frag_color = vec4(lit, 1.0);\n}\n)\";\n\nGLuint compile_shader(GLenum type, const char* src)\n{\n    GLuint shader = glCreateShader(type);\n    glShaderSource(shader, 1, &src, nullptr);\n    glCompileShader(shader);\n\n    GLint success;\n    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);\n    if (!success) {\n        char log[512];\n        glGetShaderInfoLog(shader, 512, nullptr, log);\n        fprintf(stderr, \"Shader compilation failed: %s\\n\", log);\n    }\n    return shader;\n}\n\nGLuint create_shader_program()\n{\n    GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader_src);\n    GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src);\n\n    GLuint program = glCreateProgram();\n    glAttachShader(program, vs);\n    glAttachShader(program, fs);\n    glLinkProgram(program);\n\n    GLint success;\n    glGetProgramiv(program, GL_LINK_STATUS, &success);\n    if (!success) {\n        char log[512];\n        glGetProgramInfoLog(program, 512, nullptr, log);\n        fprintf(stderr, \"Program linking failed: %s\\n\", log);\n    }\n\n    glDeleteShader(vs);\n    glDeleteShader(fs);\n\n    return program;\n}\n\n// CUDA kernel to update mesh vertices from height field\n__global__ void update_vertices(float* vertices, const float* heights, int width, int height, float scale)\n{\n    int tid = blockIdx.x * blockDim.x + threadIdx.x;\n    if (tid >= width * height)\n        return;\n\n    int x = tid % width;\n    int y = tid / width;\n\n    // Update Y coordinate (height), keep X and Z\n    vertices[tid * 3 + 0] = x * scale;\n    vertices[tid * 3 + 1] = heights[tid];\n    vertices[tid * 3 + 2] = y * scale;\n}\n\nint main(int argc, char** argv)\n{\n    printf(\"=== APIC Wave Simulation Example ===\\n\\n\");\n\n    // Parse command line\n    const char* graph_path = \"generated/wave_sim\";\n    bool smoke = false;\n    for (int i = 1; i < argc; i++) {\n        if (strcmp(argv[i], \"--graph\") == 0 && i + 1 < argc) {\n            graph_path = argv[++i];\n        } else if (strcmp(argv[i], \"--smoke\") == 0) {\n            smoke = true;\n        }\n    }\n\n    // Initialize CUDA\n    printf(\"Initializing CUDA...\\n\");\n    CHECK_CU(hipInit(0));\n\n    hipDevice_t device;\n    CHECK_CU(hipDeviceGet(&device, 0));\n\n    char device_name[256];\n    CHECK_CU(hipDeviceGetName(device_name, sizeof(device_name), device));\n    printf(\"Using device: %s\\n\", device_name);\n\n    hipCtx_t context;\n    CHECK_CU(hipDevicePrimaryCtxRetain(&context, device));\n    CHECK_CU(hipCtxSetCurrent(context));\n\n    // Initialize Warp runtime (wp_init returns 0 on success)\n    printf(\"Initializing Warp runtime...\\n\");\n    wp_init(nullptr);\n\n    // Load APIC graph\n    printf(\"\\nLoading APIC graph from: %s\\n\", graph_path);\n    APICGraph graph = wp_apic_load_graph(context, graph_path, 0);  // 0 = CUDA device\n    if (!graph) {\n        fprintf(stderr, \"Failed to load graph: %s\\n\", wp_get_error_string());\n        return 1;\n    }\n\n    // Query and print graph parameters\n    int n_params = wp_apic_get_num_params(graph);\n    printf(\"Graph parameters (%d):\\n\", n_params);\n    for (int i = 0; i < n_params; i++) {\n        const char* name = wp_apic_get_param_name(graph, i);\n        size_t size = wp_apic_get_param_size(graph, name);\n        printf(\"  [%d] %s: %zu bytes\\n\", i, name, size);\n    }\n\n    if (smoke) {\n        hipGraphExec_t exec = (hipGraphExec_t)wp_apic_get_cuda_graph_exec(graph);\n        if (!exec) {\n            fprintf(stderr, \"Failed to get graph executable: %s\\n\", wp_get_error_string());\n            wp_apic_destroy_graph(graph);\n            return 1;\n        }\n        hipStream_t stream;\n        CHECK_CUDA(hipStreamCreate(&stream));\n        const int kSmokeIterations = 10;\n        for (int i = 0; i < kSmokeIterations; i++) {\n            CHECK_CUDA(hipGraphLaunch(exec, stream));\n        }\n        CHECK_CUDA(hipStreamSynchronize(stream));\n        CHECK_CUDA(hipStreamDestroy(stream));\n        printf(\"smoke OK (%d graph launches)\\n\", kSmokeIterations);\n        wp_apic_destroy_graph(graph);\n        return 0;\n    }\n\n    // Initialize GLFW\n    printf(\"\\nInitializing GLFW/OpenGL...\\n\");\n    if (!glfwInit()) {\n        fprintf(stderr, \"Failed to initialize GLFW\\n\");\n        return 1;\n    }\n\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n#ifdef __APPLE__\n    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);\n#endif\n\n    GLFWwindow* window = glfwCreateWindow(g_window_width, g_window_height, \"APIC Wave Simulation\", nullptr, nullptr);\n    if (!window) {\n        fprintf(stderr, \"Failed to create window\\n\");\n        glfwTerminate();\n        return 1;\n    }\n\n    glfwMakeContextCurrent(window);\n    glfwSetMouseButtonCallback(window, mouse_button_callback);\n    glfwSetCursorPosCallback(window, cursor_pos_callback);\n    glfwSetScrollCallback(window, scroll_callback);\n    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n\n    // Load OpenGL functions using GLAD\n    if (!gladLoadGL(glfwGetProcAddress)) {\n        fprintf(stderr, \"Failed to initialize GLAD\\n\");\n        glfwTerminate();\n        return 1;\n    }\n\n    // Create shader program\n    GLuint program = create_shader_program();\n    GLint mvp_loc = glGetUniformLocation(program, \"mvp\");\n    GLint light_loc = glGetUniformLocation(program, \"light_dir\");\n\n    // Create mesh (grid of vertices)\n    printf(\"Creating mesh (%dx%d)...\\n\", GRID_WIDTH, GRID_HEIGHT);\n    std::vector<float> vertices(GRID_WIDTH * GRID_HEIGHT * 3);\n    std::vector<unsigned int> indices;\n\n    for (int y = 0; y < GRID_HEIGHT; y++) {\n        for (int x = 0; x < GRID_WIDTH; x++) {\n            int i = y * GRID_WIDTH + x;\n            vertices[i * 3 + 0] = x * GRID_SCALE;\n            vertices[i * 3 + 1] = 0.0f;\n            vertices[i * 3 + 2] = y * GRID_SCALE;\n\n            if (x > 0 && y > 0) {\n                int i00 = (y - 1) * GRID_WIDTH + (x - 1);\n                int i10 = (y - 1) * GRID_WIDTH + x;\n                int i01 = y * GRID_WIDTH + (x - 1);\n                int i11 = y * GRID_WIDTH + x;\n\n                indices.push_back(i00);\n                indices.push_back(i10);\n                indices.push_back(i11);\n\n                indices.push_back(i00);\n                indices.push_back(i11);\n                indices.push_back(i01);\n            }\n        }\n    }\n\n    // Create OpenGL buffers\n    GLuint vao, vbo, ebo;\n    glGenVertexArrays(1, &vao);\n    glGenBuffers(1, &vbo);\n    glGenBuffers(1, &ebo);\n\n    glBindVertexArray(vao);\n\n    glBindBuffer(GL_ARRAY_BUFFER, vbo);\n    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW);\n\n    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);\n    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);\n\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0);\n\n    // Allocate CUDA arrays (two height buffers for wave equation)\n    size_t heights_size = GRID_WIDTH * GRID_HEIGHT * sizeof(float);\n    float* d_heights[2];  // Double buffer: [0]=current, [1]=previous\n    float* d_vertices;\n    float* d_mouse_pos;\n    int current_buffer = 0;\n\n    CHECK_CUDA(hipMalloc(&d_heights[0], heights_size));\n    CHECK_CUDA(hipMalloc(&d_heights[1], heights_size));\n    CHECK_CUDA(hipMalloc(&d_vertices, vertices.size() * sizeof(float)));\n    CHECK_CUDA(hipMalloc(&d_mouse_pos, 2 * sizeof(float)));\n\n    CHECK_CUDA(hipMemset(d_heights[0], 0, heights_size));\n    CHECK_CUDA(hipMemset(d_heights[1], 0, heights_size));\n\n    // Create CUDA stream\n    hipStream_t stream;\n    CHECK_CUDA(hipStreamCreate(&stream));\n\n    // Get graph executable (builds on first call)\n    printf(\"\\nBuilding CUDA graph executable...\\n\");\n    hipGraphExec_t exec = (hipGraphExec_t)wp_apic_get_cuda_graph_exec(graph);\n    if (!exec) {\n        fprintf(stderr, \"Failed to get graph executable: %s\\n\", wp_get_error_string());\n        return 1;\n    }\n\n    printf(\"\\nStarting simulation loop...\\n\");\n    printf(\"  Left-click to create waves\\n\");\n    printf(\"  Right-drag to rotate camera\\n\");\n    printf(\"  Scroll to zoom\\n\\n\");\n\n    glEnable(GL_DEPTH_TEST);\n\n    int frame = 0;\n    double last_time = glfwGetTime();\n    int fps_counter = 0;\n\n    // Main loop\n    while (!glfwWindowShouldClose(window)) {\n        glfwPollEvents();\n\n        // Compute mouse position in grid coordinates\n        float mouse_grid[2] = { -1000.0f, -1000.0f };  // Off-grid by default\n        if (g_mouse_down) {\n            // Simple mapping: screen coords to grid coords\n            // (approximate - proper implementation would use ray casting)\n            mouse_grid[0] = (float)(g_mouse_x / g_window_width) * GRID_WIDTH;\n            mouse_grid[1] = (float)(g_mouse_y / g_window_height) * GRID_HEIGHT;\n        }\n\n        // Upload mouse position\n        CHECK_CUDA(hipMemcpyAsync(d_mouse_pos, mouse_grid, 2 * sizeof(float), hipMemcpyHostToDevice, stream));\n\n        // Set graph input parameters (wave eq needs current and previous heights)\n        wp_apic_set_param(graph, \"heights\", d_heights[current_buffer], heights_size);\n        wp_apic_set_param(graph, \"heights_prev\", d_heights[1 - current_buffer], heights_size);\n        wp_apic_set_param(graph, \"mouse_pos\", d_mouse_pos, 2 * sizeof(float));\n\n        // Execute the graph (runs all 17 kernels in one launch!)\n        CHECK_CUDA(hipGraphLaunch(exec, stream));\n        CHECK_CUDA(hipStreamSynchronize(stream));  // Must sync before reading results\n\n        // Get output heights (swap buffers for next frame)\n        wp_apic_get_param(graph, \"heights_out\", d_heights[1 - current_buffer], heights_size);\n        wp_apic_get_param(graph, \"heights_prev_out\", d_heights[current_buffer], heights_size);\n        current_buffer = 1 - current_buffer;\n\n        // Update vertex positions from heights (current_buffer now points to heights_out)\n        int block_size = 256;\n        int num_blocks = (GRID_WIDTH * GRID_HEIGHT + block_size - 1) / block_size;\n        update_vertices<<<num_blocks, block_size, 0, stream>>>(\n            d_vertices, d_heights[current_buffer], GRID_WIDTH, GRID_HEIGHT, GRID_SCALE\n        );\n\n        // Copy vertices to host for OpenGL\n        CHECK_CUDA(hipMemcpyAsync(\n            vertices.data(), d_vertices, vertices.size() * sizeof(float), hipMemcpyDeviceToHost, stream\n        ));\n        CHECK_CUDA(hipStreamSynchronize(stream));\n\n        // Update VBO\n        glBindBuffer(GL_ARRAY_BUFFER, vbo);\n        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(float), vertices.data());\n\n        // Compute camera position\n        float cx = GRID_WIDTH * GRID_SCALE * 0.5f;\n        float cy = 0.0f;\n        float cz = GRID_HEIGHT * GRID_SCALE * 0.5f;\n\n        float angle_x_rad = g_camera_angle_x * 3.14159f / 180.0f;\n        float angle_y_rad = g_camera_angle_y * 3.14159f / 180.0f;\n\n        float eye_x = cx + g_camera_distance * cosf(angle_x_rad) * sinf(angle_y_rad);\n        float eye_y = cy + g_camera_distance * sinf(angle_x_rad);\n        float eye_z = cz + g_camera_distance * cosf(angle_x_rad) * cosf(angle_y_rad);\n\n        // Compute MVP matrix\n        float proj[16], view[16], mvp[16];\n        mat4_perspective(proj, 60.0f * 3.14159f / 180.0f, (float)g_window_width / g_window_height, 0.1f, 100.0f);\n        mat4_look_at(view, eye_x, eye_y, eye_z, cx, cy, cz, 0, 1, 0);\n        mat4_multiply(mvp, proj, view);\n\n        // Render\n        glClearColor(0.1f, 0.1f, 0.15f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\n        glUseProgram(program);\n        glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, mvp);\n\n        // Light direction (normalized)\n        float lx = 0.5f, ly = 0.8f, lz = 0.3f;\n        float ll = sqrtf(lx * lx + ly * ly + lz * lz);\n        glUniform3f(light_loc, lx / ll, ly / ll, lz / ll);\n\n        glBindVertexArray(vao);\n        glDrawElements(GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, 0);\n\n        glfwSwapBuffers(window);\n\n        // FPS counter\n        fps_counter++;\n        double current_time = glfwGetTime();\n        if (current_time - last_time >= 1.0) {\n            char title[256];\n            snprintf(title, sizeof(title), \"APIC Wave Simulation - %d FPS\", fps_counter);\n            glfwSetWindowTitle(window, title);\n            fps_counter = 0;\n            last_time = current_time;\n        }\n\n        frame++;\n    }\n\n    // Cleanup\n    printf(\"\\nCleaning up...\\n\");\n\n    CHECK_CUDA(hipStreamDestroy(stream));\n    CHECK_CUDA(hipFree(d_heights[0]));\n    CHECK_CUDA(hipFree(d_heights[1]));\n    CHECK_CUDA(hipFree(d_vertices));\n    CHECK_CUDA(hipFree(d_mouse_pos));\n\n    glDeleteVertexArrays(1, &vao);\n    glDeleteBuffers(1, &vbo);\n    glDeleteBuffers(1, &ebo);\n    glDeleteProgram(program);\n\n    wp_apic_destroy_graph(graph);\n\n    glfwDestroyWindow(window);\n    glfwTerminate();\n\n    CHECK_CU(hipDevicePrimaryCtxRelease(device));\n\n    printf(\"Done.\\n\");\n    return 0;\n}\n", "source": "/kaggle/working/warp/warp/examples/cpp/02_apic_visualization/main.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"alloc_tracker.h\"\n#include \"apic.h\"\n#include \"cuda_util.h\"\n#include \"error.h\"\n#include \"scan.h\"\n#include \"sort.h\"\n\n#include <cstdlib>\n#include <fstream>\n\n#include <nvPTXCompiler.h>\n#include <nvrtc.h>\n#if WP_ENABLE_MATHDX\n#include <libcublasdx.h>\n#include <libcufftdx.h>\n#include <libcusolverdx.h>\n#include <libmathdx.h>\n#include <nvJitLink.h>\n#endif\n\n#include <algorithm>\n#include <array>\n#include <iterator>\n#include <list>\n#include <map>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#define check_any(result) (check_generic(result, __FILE__, __LINE__))\n#define check_nvrtc(code) (check_nvrtc_result(code, __FILE__, __LINE__))\n#define check_nvptx(code) (check_nvptx_result(code, __FILE__, __LINE__))\n#define check_nvjitlink(handle, code) (check_nvjitlink_result(handle, code, __FILE__, __LINE__))\n#define check_cufftdx(code) (check_cufftdx_result(code, __FILE__, __LINE__))\n#define check_cublasdx(code) (check_cublasdx_result(code, __FILE__, __LINE__))\n#define check_cusolver(code) (check_cusolver_result(code, __FILE__, __LINE__))\n#define CHECK_ANY(code) \\\n{ \\\n    do { \\\n        bool out = (check_any(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUFFTDX(code) \\\n{ \\\n    do { \\\n        bool out = (check_cufftdx(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUBLASDX(code) \\\n{ \\\n    do { \\\n        bool out = (check_cufftdx(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUSOLVER(code) \\\n{ \\\n    do { \\\n        bool out = (check_cusolver(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n\nbool check_nvrtc_result(nvrtcResult result, const char* file, int line)\n{\n    if (result == NVRTC_SUCCESS)\n        return true;\n\n    const char* error_string = nvrtcGetErrorString(result);\n    fprintf(stderr, \"Warp NVRTC compilation error %u: %s (%s:%d)\\n\", unsigned(result), error_string, file, line);\n    return false;\n}\n\nbool check_nvptx_result(nvPTXCompileResult result, const char* file, int line)\n{\n    if (result == NVPTXCOMPILE_SUCCESS)\n        return true;\n\n    const char* error_string;\n    switch (result) {\n    case NVPTXCOMPILE_ERROR_INVALID_COMPILER_HANDLE:\n        error_string = \"Invalid compiler handle\";\n        break;\n    case NVPTXCOMPILE_ERROR_INVALID_INPUT:\n        error_string = \"Invalid input\";\n        break;\n    case NVPTXCOMPILE_ERROR_COMPILATION_FAILURE:\n        error_string = \"Compilation failure\";\n        break;\n    case NVPTXCOMPILE_ERROR_INTERNAL:\n        error_string = \"Internal error\";\n        break;\n    case NVPTXCOMPILE_ERROR_OUT_OF_MEMORY:\n        error_string = \"Out of memory\";\n        break;\n    case NVPTXCOMPILE_ERROR_COMPILER_INVOCATION_INCOMPLETE:\n        error_string = \"Incomplete compiler invocation\";\n        break;\n    case NVPTXCOMPILE_ERROR_UNSUPPORTED_PTX_VERSION:\n        error_string = \"Unsupported PTX version\";\n        break;\n    default:\n        error_string = \"Unknown error\";\n        break;\n    }\n\n    fprintf(stderr, \"Warp PTX compilation error %u: %s (%s:%d)\\n\", unsigned(result), error_string, file, line);\n    return false;\n}\n\nbool check_generic(int result, const char* file, int line)\n{\n    if (!result) {\n        fprintf(stderr, \"Error %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nstruct DeviceInfo {\n    static constexpr int kNameLen = 128;\n\n    CUdevice device = -1;\n    CUuuid uuid = { 0 };\n    int ordinal = -1;\n    int pci_domain_id = -1;\n    int pci_bus_id = -1;\n    int pci_device_id = -1;\n    char name[kNameLen] = \"\";\n    int arch = 0;\n    int is_uva = 0;\n    int is_mempool_supported = 0;\n    int sm_count = 0;\n    int is_ipc_supported = -1;\n    int max_smem_bytes = 0;\n    CUcontext primary_context = NULL;\n};\n\nstruct ContextInfo {\n    DeviceInfo* device_info = NULL;\n\n    // the current stream, managed from Python (see wp_cuda_context_set_stream() and wp_cuda_context_get_stream())\n    CUstream stream = NULL;\n\n    // conditional graph node support, loaded on demand if the driver supports it (CUDA 12.4+)\n    CUmodule conditional_module = NULL;\n};\n\n// Information used for freeing allocations.\nstruct FreeInfo {\n    void* context = NULL;\n    void* ptr = NULL;\n    bool is_async = false;\n};\n\nstruct CaptureInfo {\n    CUstream stream = NULL;  // the main stream where capture begins and ends\n    uint64_t id = 0;  // unique capture id from CUDA\n    bool external = false;  // whether this is an external capture\n    cudaStreamCaptureMode mode = cudaStreamCaptureModeThreadLocal;  // mode used to open the capture (for pause/resume)\n    std::vector<FreeInfo> tmp_allocs;  // temporary allocations owned by the graph (e.g., staged array fill values)\n};\n\nstruct StreamInfo {\n    CUevent cached_event = NULL;  // event used for stream synchronization (cached to avoid creating temporary events)\n    CaptureInfo* capture = NULL;  // capture info (only if started on this stream)\n};\n\n// Extra resources tied to a graph, freed after the graph is released by CUDA.\n// Used with the on_graph_destroy() callback.\nstruct GraphDestroyCallbackInfo {\n    void* context = NULL;  // graph CUDA context\n    std::vector<void*> unfreed_allocs;  // graph allocations not freed by the graph\n    std::vector<FreeInfo> tmp_allocs;  // temporary allocations owned by the graph (e.g., staged array fill values)\n};\n\n// Information for graph allocations that are not freed by the graph.\n// These allocations have a shared ownership:\n// - The graph instance allocates/maps the memory on each launch, even if the user reference is released.\n// - The user reference must remain valid even if the graph is destroyed.\n// The memory will be freed once the user reference is released and the graph is destroyed.\nstruct GraphAllocInfo {\n    uint64_t capture_id = 0;\n    void* context = NULL;\n    bool ref_exists = false;  // whether user reference still exists\n    bool graph_destroyed = false;  // whether graph instance was destroyed\n};\n\n// Information used when deferring module unloading.\nstruct ModuleInfo {\n    void* context = NULL;\n    void* module = NULL;\n};\n\n// Information used when deferring graph destruction.\nstruct GraphDestroyInfo {\n    void* context = NULL;\n    void* graph = NULL;\n    void* graph_exec = NULL;\n};\n\nstatic std::unordered_map<CUfunction, std::string> g_kernel_names;\n\n// cached info for all devices, indexed by ordinal\nstatic std::vector<DeviceInfo> g_devices;\n\n// maps CUdevice to DeviceInfo\nstatic std::map<CUdevice, DeviceInfo*> g_device_map;\n\n// cached info for all known contexts\nstatic std::map<CUcontext, ContextInfo> g_contexts;\n\n// cached info for all known streams (including registered external streams)\nstatic std::unordered_map<CUstream, StreamInfo> g_streams;\n\n// Ongoing graph captures registered using wp.capture_begin().\n// This maps the capture id to the stream where capture was started.\n// See wp_cuda_graph_begin_capture(), wp_cuda_graph_end_capture(), and wp_free_device_async().\nstatic std::unordered_map<uint64_t, CaptureInfo*> g_captures;\n\n// Memory allocated during graph capture requires special handling.\n// See wp_alloc_device_async() and wp_free_device_async().\nstatic std::unordered_map<void*, GraphAllocInfo> g_graph_allocs;\n\n// Memory that cannot be freed immediately gets queued here.\n// Call free_deferred_allocs() to release.\nstatic std::vector<FreeInfo> g_deferred_free_list;\n\n// Modules that cannot be unloaded immediately get queued here.\n// Call unload_deferred_modules() to release.\nstatic std::vector<ModuleInfo> g_deferred_module_list;\n\n// Graphs that cannot be destroyed immediately get queued here.\n// Call destroy_deferred_graphs() to release.\nstatic std::vector<GraphDestroyInfo> g_deferred_graph_list;\n\n// Data from on_graph_destroy() callbacks that run on a different thread.\nstatic std::vector<GraphDestroyCallbackInfo*> g_deferred_graph_destroy_list;\nstatic std::mutex g_graph_destroy_mutex;\n\n\nvoid wp_cuda_set_context_restore_policy(bool always_restore) { ContextGuard::always_restore = always_restore; }\n\nint wp_cuda_get_context_restore_policy() { return int(ContextGuard::always_restore); }\n\nint cuda_init()\n{\n    if (!init_cuda_driver())\n        return -1;\n\n    int device_count = 0;\n    if (check_cu(cuDeviceGetCount_f(&device_count))) {\n        g_devices.resize(device_count);\n\n        for (int i = 0; i < device_count; i++) {\n            CUdevice device;\n            if (check_cu(cuDeviceGet_f(&device, i))) {\n                // query device info\n                g_devices[i].device = device;\n                g_devices[i].ordinal = i;\n                check_cu(cuDeviceGetName_f(g_devices[i].name, DeviceInfo::kNameLen, device));\n                check_cu(cuDeviceGetUuid_f(&g_devices[i].uuid, device));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].pci_domain_id, CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, device)\n                );\n                check_cu(cuDeviceGetAttribute_f(&g_devices[i].pci_bus_id, CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, device));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].pci_device_id, CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID, device)\n                );\n                check_cu(cuDeviceGetAttribute_f(&g_devices[i].is_uva, CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING, device));\n                check_cu(cuDeviceGetAttribute_f(\n                    &g_devices[i].is_mempool_supported, CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED, device\n                ));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].sm_count, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, device)\n                );\n#ifdef CUDA_VERSION\n#if CUDA_VERSION >= 12000\n                int device_attribute_integrated = 0;\n                check_cu(cuDeviceGetAttribute_f(&device_attribute_integrated, CU_DEVICE_ATTRIBUTE_INTEGRATED, device));\n                if (device_attribute_integrated == 0) {\n                    check_cu(cuDeviceGetAttribute_f(\n                        &g_devices[i].is_ipc_supported, CU_DEVICE_ATTRIBUTE_IPC_EVENT_SUPPORTED, device\n                    ));\n                } else {\n                    // integrated devices do not support CUDA IPC\n                    g_devices[i].is_ipc_supported = 0;\n                }\n#endif\n#endif\n                check_cu(cuDeviceGetAttribute_f(\n                    &g_devices[i].max_smem_bytes, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK_OPTIN, device\n                ));\n                int major = 0;\n                int minor = 0;\n                check_cu(cuDeviceGetAttribute_f(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device));\n                check_cu(cuDeviceGetAttribute_f(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device));\n                g_devices[i].arch = 10 * major + minor;\n#ifdef CUDA_VERSION\n#if CUDA_VERSION < 13000\n                if (g_devices[i].arch == 110) {\n                    g_devices[i].arch = 101;  // Thor SM change\n                }\n#endif\n#endif\n                g_device_map[device] = &g_devices[i];\n            } else {\n                return -1;\n            }\n        }\n    } else {\n        return -1;\n    }\n\n    // initialize default timing state\n    static CudaTimingState default_timing_state(0, NULL);\n    g_cuda_timing_state = &default_timing_state;\n\n    return 0;\n}\n\n\nCUcontext get_current_context()\n{\n    CUcontext ctx;\n    if (check_cu(cuCtxGetCurrent_f(&ctx)))\n        return ctx;\n    else\n        return NULL;\n}\n\nstatic inline CUstream get_current_stream(void* context = NULL)\n{\n    return static_cast<CUstream>(wp_cuda_context_get_stream(context));\n}\n\nstatic ContextInfo* get_context_info(CUcontext ctx)\n{\n    if (!ctx) {\n        ctx = get_current_context();\n        if (!ctx)\n            return NULL;\n    }\n\n    auto it = g_contexts.find(ctx);\n    if (it != g_contexts.end()) {\n        return &it->second;\n    } else {\n        // previously unseen context, add the info\n        ContextGuard guard(ctx, true);\n\n        CUdevice device;\n        if (check_cu(cuCtxGetDevice_f(&device))) {\n            DeviceInfo* device_info = g_device_map[device];\n\n            // workaround for https://nvbugspro.nvidia.com/bug/4456003\n            if (device_info->is_mempool_supported) {\n                void* dummy = NULL;\n                check_cuda(cudaMallocAsync(&dummy, 1, NULL));\n                check_cuda(cudaFreeAsync(dummy, NULL));\n            }\n\n            ContextInfo context_info;\n            context_info.device_info = device_info;\n            auto result = g_contexts.insert(std::make_pair(ctx, context_info));\n            return &result.first->second;\n        }\n    }\n\n    return NULL;\n}\n\nstatic inline ContextInfo* get_context_info(void* context) { return get_context_info(static_cast<CUcontext>(context)); }\n\nstatic inline StreamInfo* get_stream_info(CUstream stream)\n{\n    auto it = g_streams.find(stream);\n    if (it != g_streams.end())\n        return &it->second;\n    else\n        return NULL;\n}\n\nstatic inline CaptureInfo* get_capture_info(CUstream stream)\n{\n    if (!g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        uint64_t capture_id = get_capture_id(stream);\n        auto capture_iter = g_captures.find(capture_id);\n        if (capture_iter != g_captures.end())\n            return capture_iter->second;\n    }\n    return NULL;\n}\n\n// helper function to copy a value to device memory in a graph-friendly way\nstatic bool capturable_tmp_alloc(void* context, const void* data, size_t size, void** devptr_ret, bool* free_devptr_ret)\n{\n    ContextGuard guard(context);\n\n    CUstream stream = get_current_stream();\n    CaptureInfo* capture_info = get_capture_info(stream);\n    int device_ordinal = wp_cuda_context_get_device_ordinal(context);\n    void* devptr = NULL;\n    bool free_devptr = true;\n\n    if (capture_info) {\n        // ongoing graph capture - need to stage the fill value so that it persists with the graph\n        if (CUDA_VERSION >= 12040 && wp_cuda_driver_version() >= 12040) {\n            // pause the capture so that the alloc/memcpy won't be captured\n            void* graph = NULL;\n            if (!wp_cuda_graph_pause_capture(WP_CURRENT_CONTEXT, stream, &graph))\n                return false;\n\n            // copy value to device memory\n            devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n            if (!devptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                    (unsigned long long)size, device_ordinal, __FUNCTION__\n                );\n                return false;\n            }\n            if (!check_cuda(cudaMemcpyAsync(devptr, data, size, cudaMemcpyHostToDevice, stream)))\n                return false;\n\n            // graph takes ownership of the value storage\n            FreeInfo free_info;\n            free_info.context = context ? context : get_current_context();\n            free_info.ptr = devptr;\n            free_info.is_async = wp_cuda_device_is_mempool_supported(device_ordinal);\n\n            // allocation will be freed when graph is destroyed\n            capture_info->tmp_allocs.push_back(free_info);\n\n            // resume the capture\n            if (!wp_cuda_graph_resume_capture(WP_CURRENT_CONTEXT, stream, graph))\n                return false;\n\n            free_devptr = false;  // memory is owned by the graph, doesn't need to be freed\n        } else {\n            // older CUDA can't pause/resume the capture, so stage in CPU memory\n            void* hostptr = wp_alloc_host(size);\n            if (!hostptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cpu' (in function %s)\\n\",\n                    (unsigned long long)size, __FUNCTION__\n                );\n                return false;\n            }\n            memcpy(hostptr, data, size);\n\n            // the device allocation and h2d copy will be captured in the graph\n            devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n            if (!devptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                    (unsigned long long)size, device_ordinal, __FUNCTION__\n                );\n                return false;\n            }\n            if (!check_cuda(cudaMemcpyAsync(devptr, hostptr, size, cudaMemcpyHostToDevice, stream)))\n                return false;\n\n            // graph takes ownership of the value storage\n            FreeInfo free_info;\n            free_info.context = NULL;\n            free_info.ptr = hostptr;\n            free_info.is_async = false;\n\n            // allocation will be freed when graph is destroyed\n            capture_info->tmp_allocs.push_back(free_info);\n        }\n    } else {\n        // not capturing, copy the value to device memory\n        devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n        if (!devptr) {\n            fprintf(\n                stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                (unsigned long long)size, device_ordinal, __FUNCTION__\n            );\n            return false;\n        }\n        if (!check_cuda(cudaMemcpyAsync(devptr, data, size, cudaMemcpyHostToDevice, stream)))\n            return false;\n    }\n\n    *devptr_ret = devptr;\n    *free_devptr_ret = free_devptr;\n\n    return true;\n}\n\nstatic void deferred_free(void* ptr, void* context, bool is_async)\n{\n    FreeInfo free_info;\n    free_info.ptr = ptr;\n    free_info.context = context ? context : get_current_context();\n    free_info.is_async = is_async;\n    g_deferred_free_list.push_back(free_info);\n}\n\nstatic int free_deferred_allocs(void* context = NULL)\n{\n    if (g_deferred_free_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_freed_allocs = 0;\n    for (auto it = g_deferred_free_list.begin(); it != g_deferred_free_list.end(); /*noop*/) {\n        const FreeInfo& free_info = *it;\n\n        // free the pointer if it matches the given context or if the context is unspecified\n        if (free_info.context == context || !context) {\n            ContextGuard guard(free_info.context);\n\n            if (free_info.is_async) {\n                // this could be a regular stream-ordered allocation or a graph allocation\n                cudaError_t res = cudaFreeAsync(free_info.ptr, NULL);\n                if (res != cudaSuccess) {\n                    if (res == cudaErrorInvalidValue) {\n                        // This can happen if we try to release the pointer but the graph was\n                        // never launched, so the memory isn't mapped.\n                        // This is fine, so clear the error.\n                        cudaGetLastError();\n                    } else {\n                        // something else went wrong, report error\n                        check_cuda(res);\n                    }\n                }\n            } else {\n                check_cuda(cudaFree(free_info.ptr));\n            }\n\n            ++num_freed_allocs;\n\n            it = g_deferred_free_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_freed_allocs;\n}\n\nstatic int unload_deferred_modules(void* context = NULL)\n{\n    if (g_deferred_module_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_unloaded_modules = 0;\n    for (auto it = g_deferred_module_list.begin(); it != g_deferred_module_list.end(); /*noop*/) {\n        // free the module if it matches the given context or if the context is unspecified\n        const ModuleInfo& module_info = *it;\n        if (module_info.context == context || !context) {\n            wp_cuda_unload_module(module_info.context, module_info.module);\n            ++num_unloaded_modules;\n            it = g_deferred_module_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_unloaded_modules;\n}\n\nstatic int destroy_deferred_graphs(void* context = NULL)\n{\n    if (g_deferred_graph_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_destroyed_graphs = 0;\n    for (auto it = g_deferred_graph_list.begin(); it != g_deferred_graph_list.end(); /*noop*/) {\n        // destroy the graph if it matches the given context or if the context is unspecified\n        const GraphDestroyInfo& graph_info = *it;\n        if (graph_info.context == context || !context) {\n            if (graph_info.graph) {\n                check_cuda(cudaGraphDestroy((cudaGraph_t)graph_info.graph));\n            }\n            if (graph_info.graph_exec) {\n                check_cuda(cudaGraphExecDestroy((cudaGraphExec_t)graph_info.graph_exec));\n            }\n            ++num_destroyed_graphs;\n            it = g_deferred_graph_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_destroyed_graphs;\n}\n\nstatic int process_deferred_graph_destroy_callbacks(void* context = NULL)\n{\n    int num_freed = 0;\n\n    std::lock_guard<std::mutex> lock(g_graph_destroy_mutex);\n\n    for (auto it = g_deferred_graph_destroy_list.begin(); it != g_deferred_graph_destroy_list.end(); /*noop*/) {\n        GraphDestroyCallbackInfo* graph_info = *it;\n        if (graph_info->context == context || !context) {\n            // handle unfreed graph allocations (may have outstanding user references)\n            for (void* ptr : graph_info->unfreed_allocs) {\n                auto alloc_iter = g_graph_allocs.find(ptr);\n                if (alloc_iter != g_graph_allocs.end()) {\n                    GraphAllocInfo& alloc_info = alloc_iter->second;\n                    if (alloc_info.ref_exists) {\n                        // unreference from graph so the pointer will be deallocated when the user reference goes away\n                        alloc_info.graph_destroyed = true;\n                    } else {\n                        // the pointer can be freed, no references remain\n                        wp_free_device_async(alloc_info.context, ptr);\n                        g_graph_allocs.erase(alloc_iter);\n                    }\n                }\n            }\n\n            // handle temporary allocations owned by the graph (no user references)\n            for (const FreeInfo& tmp_info : graph_info->tmp_allocs) {\n                if (tmp_info.context) {\n                    // GPU alloc\n                    if (tmp_info.is_async) {\n                        wp_free_device_async(tmp_info.context, tmp_info.ptr);\n                    } else {\n                        wp_free_device_default(tmp_info.context, tmp_info.ptr);\n                    }\n                } else {\n                    // CPU alloc\n                    wp_free_host(tmp_info.ptr);\n                }\n            }\n\n            ++num_freed;\n            delete graph_info;\n            it = g_deferred_graph_destroy_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_freed;\n}\n\nstatic int run_deferred_actions(void* context = NULL)\n{\n    int num_actions = 0;\n    num_actions += free_deferred_allocs(context);\n    num_actions += unload_deferred_modules(context);\n    num_actions += destroy_deferred_graphs(context);\n    num_actions += process_deferred_graph_destroy_callbacks(context);\n    return num_actions;\n}\n\n// Callback used when a graph is destroyed.\n// NOTE: this runs on an internal CUDA thread and requires synchronization.\nstatic void CUDART_CB on_graph_destroy(void* user_data)\n{\n    if (user_data) {\n        std::lock_guard<std::mutex> lock(g_graph_destroy_mutex);\n        g_deferred_graph_destroy_list.push_back(static_cast<GraphDestroyCallbackInfo*>(user_data));\n    }\n}\n\nstatic inline const char* get_cuda_kernel_name(void* kernel)\n{\n    CUfunction cuda_func = static_cast<CUfunction>(kernel);\n    auto name_iter = g_kernel_names.find((CUfunction)cuda_func);\n    if (name_iter != g_kernel_names.end())\n        return name_iter->second.c_str();\n    else\n        return \"unknown_kernel\";\n}\n\n\nvoid* wp_alloc_pinned(size_t s, const char* tag)\n{\n    void* ptr = NULL;\n    check_cuda(cudaMallocHost(&ptr, s));\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_PINNED, -1, tag);\n    return ptr;\n}\n\nvoid wp_free_pinned(void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n    cudaFreeHost(ptr);\n}\n\nvoid* wp_alloc_device(void* context, size_t s, const char* tag)\n{\n    int ordinal = wp_cuda_context_get_device_ordinal(context);\n\n    if (wp_cuda_device_is_mempool_supported(ordinal))\n        return wp_alloc_device_async(context, s, tag);\n    else\n        return wp_alloc_device_default(context, s, tag);\n}\n\nvoid wp_free_device(void* context, void* ptr)\n{\n    int ordinal = wp_cuda_context_get_device_ordinal(context);\n\n    // use stream-ordered allocator if available\n    if (wp_cuda_device_is_mempool_supported(ordinal))\n        wp_free_device_async(context, ptr);\n    else\n        wp_free_device_default(context, ptr);\n}\n\nvoid* wp_alloc_device_default(void* context, size_t s, const char* tag)\n{\n    ContextGuard guard(context);\n\n    void* ptr = NULL;\n    check_cuda(cudaMalloc(&ptr, s));\n\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_DEVICE, wp_cuda_context_get_device_ordinal(context), tag);\n    return ptr;\n}\n\nvoid wp_free_device_default(void* context, void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n\n    ContextGuard guard(context);\n\n    // check if a capture is in progress\n    if (g_captures.empty()) {\n        check_cuda(cudaFree(ptr));\n    } else {\n        // we must defer the operation until graph captures complete\n        deferred_free(ptr, context, false);\n    }\n}\n\nvoid* wp_alloc_device_async(void* context, size_t s, const char* tag)\n{\n    // stream-ordered allocations don't rely on the current context,\n    // but we set the context here for consistent behaviour\n    ContextGuard guard(context);\n\n    ContextInfo* context_info = get_context_info(context);\n    if (!context_info)\n        return NULL;\n\n    CUstream stream = context_info->stream;\n\n    void* ptr = NULL;\n    check_cuda(cudaMallocAsync(&ptr, s, stream));\n\n    if (ptr) {\n        // if the stream is capturing, the allocation requires special handling\n        if (wp_cuda_stream_is_capturing(stream)) {\n            // check if this is a known capture\n            uint64_t capture_id = get_capture_id(stream);\n            auto capture_iter = g_captures.find(capture_id);\n            if (capture_iter != g_captures.end()) {\n                // remember graph allocation details\n                GraphAllocInfo alloc_info;\n                alloc_info.capture_id = capture_id;\n                alloc_info.context = context ? context : get_current_context();\n                alloc_info.ref_exists = true;  // user reference created and returned here\n                alloc_info.graph_destroyed = false;  // graph not destroyed yet\n                g_graph_allocs[ptr] = alloc_info;\n            }\n        }\n    }\n\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_DEVICE, wp_cuda_context_get_device_ordinal(context), tag);\n    return ptr;\n}\n\nvoid wp_free_device_async(void* context, void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n\n    // stream-ordered allocators generally don't rely on the current context,\n    // but we set the context here for consistent behaviour\n    ContextGuard guard(context);\n\n    // NB: Stream-ordered deallocations are tricky, because the memory could still be used on another stream\n    // or even multiple streams.  To avoid use-after-free errors, we need to ensure that all preceding work\n    // completes before releasing the memory.  The strategy is different for regular stream-ordered allocations\n    // and allocations made during graph capture.  See below for details.\n\n    // check if this allocation was made during graph capture\n    auto alloc_iter = g_graph_allocs.find(ptr);\n    if (alloc_iter == g_graph_allocs.end()) {\n        // Not a graph allocation.\n        // Check if graph capture is ongoing.\n        if (g_captures.empty()) {\n            // cudaFreeAsync on the null stream does not block or trigger synchronization, but it postpones\n            // the deallocation until a synchronization point is reached, so preceding work on this pointer\n            // should safely complete.\n            check_cuda(cudaFreeAsync(ptr, NULL));\n        } else {\n            // We must defer the free operation until graph capture completes.\n            deferred_free(ptr, context, true);\n        }\n    } else {\n        // get the graph allocation details\n        GraphAllocInfo& alloc_info = alloc_iter->second;\n\n        uint64_t capture_id = alloc_info.capture_id;\n\n        // check if the capture is still active\n        auto capture_iter = g_captures.find(capture_id);\n        if (capture_iter != g_captures.end()) {\n            // Add a mem free node.  Use all current leaf nodes as dependencies to ensure that all prior\n            // work completes before deallocating.  This works with both Warp-initiated and external captures\n            // and avoids the need to explicitly track all streams used during the capture.\n            CaptureInfo* capture = capture_iter->second;\n            cudaGraph_t graph = get_capture_graph(capture->stream);\n            std::vector<cudaGraphNode_t> leaf_nodes;\n            if (graph && get_graph_leaf_nodes(graph, leaf_nodes)) {\n                cudaGraphNode_t free_node;\n                if (check_cuda(cudaGraphAddMemFreeNode(&free_node, graph, leaf_nodes.data(), leaf_nodes.size(), ptr))) {\n                    check_cu(cuStreamUpdateCaptureDependencies_f(\n                        capture->stream, &free_node, 1, cudaStreamSetCaptureDependencies\n                    ));\n                }\n            }\n\n            // we're done with this allocation, it's owned by the graph\n            g_graph_allocs.erase(alloc_iter);\n        } else {\n            // the capture has ended\n            // if the owning graph was already destroyed, we can free the pointer now\n            if (alloc_info.graph_destroyed) {\n                if (g_captures.empty()) {\n                    // try to free the pointer now\n                    cudaError_t res = cudaFreeAsync(ptr, NULL);\n                    if (res == cudaErrorInvalidValue) {\n                        // This can happen if we try to release the pointer but the graph was\n                        // never launched, so the memory isn't mapped.\n                        // This is fine, so clear the error.\n                        cudaGetLastError();\n                    } else {\n                        // check for other errors\n                        check_cuda(res);\n                    }\n                } else {\n                    // We must defer the operation until graph capture completes.\n                    deferred_free(ptr, context, true);\n                }\n\n                // we're done with this allocation\n                g_graph_allocs.erase(alloc_iter);\n            } else {\n                // graph still exists\n                // unreference the pointer so it will be deallocated once the graph instance is destroyed\n                alloc_info.ref_exists = false;\n            }\n        }\n    }\n}\n\nbool wp_memcpy_h2d(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy HtoD\");\n\n    bool result = check_cuda(cudaMemcpyAsync(dest, src, n, cudaMemcpyHostToDevice, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\nbool wp_memcpy_d2h(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy DtoH\");\n\n    bool result = check_cuda(cudaMemcpyAsync(dest, src, n, cudaMemcpyDeviceToHost, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\nbool wp_memcpy_d2d(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy DtoD\");\n\n    bool result = check_cuda(cudaMemcpyAsync(dest, src, n, cudaMemcpyDeviceToDevice, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    // APIC recording.\n    // TODO: When execution becomes fully deferred (like the CPU path), move\n    // CUDA-result checking to replay time (capture_launch / load+launch) and\n    // make recording unconditional at capture time. For now we still execute\n    // the CUDA op live under stream capture, but the record itself is API-\n    // intent only and doesn't depend on the live call's result.\n    APICState apic_state = wp_apic_get_recording_state();\n    if (apic_state) {\n        int32_t dst_region, src_region;\n        uint64_t dst_offset, src_offset;\n        bool dst_ok = apic_resolve_ptr(apic_state, (uint64_t)dest, &dst_region, &dst_offset);\n        bool src_ok = apic_resolve_ptr(apic_state, (uint64_t)src, &src_region, &src_offset);\n        if (!dst_ok)\n            fprintf(stderr, \"APIC: Error - memcpy dst pointer not in any registered region\\n\");\n        if (!src_ok)\n            fprintf(stderr, \"APIC: Error - memcpy src pointer not in any registered region\\n\");\n        if (dst_ok && src_ok)\n            apic_record_memcpy_d2d(apic_state, dst_region, dst_offset, src_region, src_offset, n);\n    }\n\n    return result;\n}\n\nbool wp_memcpy_p2p(void* dst_context, void* dst, void* src_context, void* src, size_t n, void* stream)\n{\n    // ContextGuard guard(context);\n\n    CUstream cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream(dst_context);\n\n    // Notes:\n    // - cuMemcpyPeerAsync() works fine with both regular and pooled allocations (cudaMalloc() and cudaMallocAsync(),\n    // respectively)\n    //   when not capturing a graph.\n    // - cuMemcpyPeerAsync() is not supported during graph capture, so we must use cudaMemcpyAsync() with\n    // kind=cudaMemcpyDefault.\n    // - cudaMemcpyAsync() works fine with regular allocations, but doesn't work with pooled allocations\n    //   unless mempool access has been enabled.\n    // - There is no reliable way to check if mempool access is enabled during graph capture,\n    //   because cudaMemPoolGetAccess() cannot be called during graph capture.\n    // - CUDA will report error 1 (invalid argument) if cudaMemcpyAsync() is called but mempool access is not enabled.\n\n    if (!wp_cuda_stream_is_capturing(stream)) {\n        begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, get_stream_context(stream), \"memcpy PtoP\");\n\n        bool result = check_cu(cuMemcpyPeerAsync_f(\n            (CUdeviceptr)dst, (CUcontext)dst_context, (CUdeviceptr)src, (CUcontext)src_context, n, cuda_stream\n        ));\n\n        end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n        return result;\n    } else {\n        cudaError_t result = cudaSuccess;\n\n        // cudaMemcpyAsync() is sensitive to the bound context to resolve pointer locations.\n        // If fails with cudaErrorInvalidValue if it cannot resolve an argument.\n        // We first try the copy in the destination context, then if it fails we retry in the source context.\n        // The cudaErrorInvalidValue error doesn't cause graph capture to fail, so it's ok to retry.\n        // Since this trial-and-error shenanigans only happens during capture, there\n        // is no perf impact when the graph is launched.\n        // For bonus points, this approach simplifies memory pool access requirements.\n        // Access only needs to be enabled one way, either from the source device to the destination device\n        // or vice versa.  Sometimes, when it's really quiet, you can actually hear my genius.\n        {\n            // try doing the copy in the destination context\n            ContextGuard guard(dst_context);\n            result = cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, cuda_stream);\n\n            if (result != cudaSuccess) {\n                // clear error in destination context\n                cudaGetLastError();\n\n                // try doing the copy in the source context\n                ContextGuard guard(src_context);\n                result = cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, cuda_stream);\n\n                // clear error in source context\n                cudaGetLastError();\n            }\n        }\n\n        // If the copy failed, try to detect if mempool allocations are involved to generate a helpful error message.\n        if (!check_cuda(result)) {\n            if (result == cudaErrorInvalidValue && src != NULL && dst != NULL) {\n                // check if either of the pointers was allocated from a mempool\n                void* src_mempool = NULL;\n                void* dst_mempool = NULL;\n                cuPointerGetAttribute_f(&src_mempool, CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE, (CUdeviceptr)src);\n                cuPointerGetAttribute_f(&dst_mempool, CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE, (CUdeviceptr)dst);\n                cudaGetLastError();  // clear any errors\n                // check if either of the pointers was allocated during graph capture\n                auto src_alloc = g_graph_allocs.find(src);\n                auto dst_alloc = g_graph_allocs.find(dst);\n                if (src_mempool != NULL || src_alloc != g_graph_allocs.end() || dst_mempool != NULL\n                    || dst_alloc != g_graph_allocs.end()) {\n                    wp::append_error_string(\n                        \"*** CUDA mempool allocations were used in a peer-to-peer copy during graph capture.\"\n                    );\n                    wp::append_error_string(\n                        \"*** This operation fails if mempool access is not enabled between the peer devices.\"\n                    );\n                    wp::append_error_string(\n                        \"*** Either enable mempool access between the devices or use the default CUDA allocator\"\n                    );\n                    wp::append_error_string(\"*** to pre-allocate the arrays before graph capture begins.\");\n                }\n            }\n\n            return false;\n        }\n\n        return true;\n    }\n}\n\nbool wp_memcpy_batch(void* context, void** dsts, void** srcs, size_t* sizes, size_t count, void* stream)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy batch\");\n\n    bool result = true;\n\n#if CUDA_VERSION >= 12080\n    if (wp_cuda_driver_version() >= 12080) {\n        CUmemcpyAttributes attr = {};\n        attr.srcAccessOrder = CU_MEMCPY_SRC_ACCESS_ORDER_STREAM;\n        // attr.flags = CU_MEMCPY_FLAG_PREFER_OVERLAP_WITH_COMPUTE;\n        size_t attr_idx = 0;\n        size_t fail_idx = 0;\n        result = check_cuda(cuMemcpyBatchAsync_f(\n            (CUdeviceptr*)dsts, (CUdeviceptr*)srcs, sizes, count, &attr, &attr_idx, 1, &fail_idx, cuda_stream\n        ));\n    } else {\n        for (size_t i = 0; i < count; i++)\n            result = result && check_cuda(cudaMemcpyAsync(dsts[i], srcs[i], sizes[i], cudaMemcpyDefault, cuda_stream));\n    }\n#else\n    for (size_t i = 0; i < count; i++)\n        result = result && check_cuda(cudaMemcpyAsync(dsts[i], srcs[i], sizes[i], cudaMemcpyDefault, cuda_stream));\n#endif\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\n\n__global__ void memset_kernel(int* dest, int value, size_t n)\n{\n    const size_t tid\n        = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n\n    if (tid < n) {\n        dest[tid] = value;\n    }\n}\n\nbool wp_memset_device(void* context, void* dest, int value, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    cudaStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<CUstream>(stream);\n    else\n        cuda_stream = get_current_stream();\n\n    begin_cuda_range(WP_TIMING_MEMSET, cuda_stream, context, \"memset\");\n\n    bool result = check_cuda(cudaMemsetAsync(dest, value, n, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMSET, cuda_stream);\n\n    // APIC recording.\n    // TODO: When execution becomes fully deferred (like the CPU path), move\n    // CUDA-result checking to replay time (capture_launch / load+launch) and\n    // make recording unconditional at capture time. For now we still execute\n    // the CUDA op live under stream capture, but the record itself is API-\n    // intent only and doesn't depend on the live call's result.\n    APICState apic_state = wp_apic_get_recording_state();\n    if (apic_state) {\n        int32_t region_id;\n        uint64_t offset;\n        if (apic_resolve_ptr(apic_state, (uint64_t)dest, &region_id, &offset))\n            apic_record_memset(apic_state, region_id, offset, n, value);\n        else\n            fprintf(stderr, \"APIC: Error - memset dst pointer not in any registered region\\n\");\n    }\n    return result;\n}\n\n// fill memory buffer with a value: generic memtile kernel using memcpy for each element\n__global__ void memtile_kernel(void* dst, const void* src, size_t srcsize, size_t n)\n{\n    size_t tid = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n    if (tid < n) {\n        memcpy((int8_t*)dst + srcsize * tid, src, srcsize);\n    }\n}\n\n// this should be faster than memtile_kernel, but requires proper alignment of dst\ntemplate <typename T> __global__ void memtile_value_kernel(T* dst, T value, size_t n)\n{\n    size_t tid = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n    if (tid < n) {\n        dst[tid] = value;\n    }\n}\n\nvoid wp_memtile_device(void* context, void* dst, const void* src, size_t srcsize, size_t n)\n{\n    ContextGuard guard(context);\n\n    size_t dst_addr = reinterpret_cast<size_t>(dst);\n    size_t src_addr = reinterpret_cast<size_t>(src);\n\n    // try memtile_value first because it should be faster, but we need to ensure proper alignment\n    if (srcsize == 8 && (dst_addr & 7) == 0 && (src_addr & 7) == 0) {\n        int64_t* p = reinterpret_cast<int64_t*>(dst);\n        int64_t value = *reinterpret_cast<const int64_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 4 && (dst_addr & 3) == 0 && (src_addr & 3) == 0) {\n        int32_t* p = reinterpret_cast<int32_t*>(dst);\n        int32_t value = *reinterpret_cast<const int32_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 2 && (dst_addr & 1) == 0 && (src_addr & 1) == 0) {\n        int16_t* p = reinterpret_cast<int16_t*>(dst);\n        int16_t value = *reinterpret_cast<const int16_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 1) {\n        check_cuda(cudaMemset(dst, *reinterpret_cast<const int8_t*>(src), n));\n    } else {\n        // generic version\n        void* value_devptr = NULL;  // fill value in device memory\n        bool free_devptr = true;  // whether we need to free the memory\n\n        // prepare the fill value in a graph-friendly way\n        if (!capturable_tmp_alloc(WP_CURRENT_CONTEXT, src, srcsize, &value_devptr, &free_devptr)) {\n            fprintf(stderr, \"Warp fill error: failed to copy value to device memory\\n\");\n            return;\n        }\n\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_kernel, n, (dst, value_devptr, srcsize, n));\n\n        if (free_devptr) {\n            wp_free_device(WP_CURRENT_CONTEXT, value_devptr);\n        }\n    }\n}\n\n\nstatic __global__ void array_copy_1d_kernel(\n    void* dst,\n    const void* src,\n    size_t dst_stride,\n    size_t src_stride,\n    const int* dst_indices,\n    const int* src_indices,\n    size_t n,\n    size_t elem_size\n)\n{\n    size_t i = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (i < n) {\n        size_t src_idx = src_indices ? src_indices[i] : i;\n        size_t dst_idx = dst_indices ? dst_indices[i] : i;\n        const char* p = (const char*)src + src_idx * src_stride;\n        char* q = (char*)dst + dst_idx * dst_stride;\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_2d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<2, size_t> dst_strides,\n    wp::vec_t<2, size_t> src_strides,\n    wp::vec_t<2, const int*> dst_indices,\n    wp::vec_t<2, const int*> src_indices,\n    wp::vec_t<2, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t i = tid / n;\n    size_t j = tid % n;\n    if (i < shape[0] /*&& j < shape[1]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        const char* p = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1];\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_3d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<3, size_t> dst_strides,\n    wp::vec_t<3, size_t> src_strides,\n    wp::vec_t<3, const int*> dst_indices,\n    wp::vec_t<3, const int*> src_indices,\n    wp::vec_t<3, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t i = tid / (n * o);\n    size_t j = tid % (n * o) / o;\n    size_t k = tid % o;\n    if (i < shape[0] && j < shape[1] /*&& k < shape[2]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        size_t src_idx2 = src_indices[2] ? src_indices[2][k] : k;\n        size_t dst_idx2 = dst_indices[2] ? dst_indices[2][k] : k;\n        const char* p\n            = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1] + src_idx2 * src_strides[2];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1] + dst_idx2 * dst_strides[2];\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_4d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<4, size_t> dst_strides,\n    wp::vec_t<4, size_t> src_strides,\n    wp::vec_t<4, const int*> dst_indices,\n    wp::vec_t<4, const int*> src_indices,\n    wp::vec_t<4, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t p = shape[3];\n    size_t i = tid / (n * o * p);\n    size_t j = tid % (n * o * p) / (o * p);\n    size_t k = tid % (o * p) / p;\n    size_t l = tid % p;\n    if (i < shape[0] && j < shape[1] && k < shape[2] /*&& l < shape[3]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        size_t src_idx2 = src_indices[2] ? src_indices[2][k] : k;\n        size_t dst_idx2 = dst_indices[2] ? dst_indices[2][k] : k;\n        size_t src_idx3 = src_indices[3] ? src_indices[3][l] : l;\n        size_t dst_idx3 = dst_indices[3] ? dst_indices[3][l] : l;\n        const char* p = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1]\n            + src_idx2 * src_strides[2] + src_idx3 * src_strides[3];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1] + dst_idx2 * dst_strides[2]\n            + dst_idx3 * dst_strides[3];\n        memcpy(q, p, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_from_fabric_kernel(\n    wp::fabricarray_t<void> src, void* dst_data, size_t dst_stride, const int* dst_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < src.size) {\n        size_t dst_idx = dst_indices ? dst_indices[tid] : tid;\n        void* dst_ptr = (char*)dst_data + dst_idx * dst_stride;\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_from_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> src, void* dst_data, size_t dst_stride, const int* dst_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < src.size) {\n        size_t src_index = src.indices[tid];\n        size_t dst_idx = dst_indices ? dst_indices[tid] : tid;\n        void* dst_ptr = (char*)dst_data + dst_idx * dst_stride;\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_to_fabric_kernel(\n    wp::fabricarray_t<void> dst, const void* src_data, size_t src_stride, const int* src_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_idx = src_indices ? src_indices[tid] : tid;\n        const void* src_ptr = (const char*)src_data + src_idx * src_stride;\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst,\n    const void* src_data,\n    size_t src_stride,\n    const int* src_indices,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_idx = src_indices ? src_indices[tid] : tid;\n        const void* src_ptr = (const char*)src_data + src_idx * src_stride;\n        size_t dst_idx = dst.indices[tid];\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_idx, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void\narray_copy_fabric_to_fabric_kernel(wp::fabricarray_t<void> dst, wp::fabricarray_t<void> src, size_t elem_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst, wp::fabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        size_t dst_index = dst.indices[tid];\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_indexed_to_fabric_kernel(\n    wp::fabricarray_t<void> dst, wp::indexedfabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_index = src.indices[tid];\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_indexed_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst, wp::indexedfabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_index = src.indices[tid];\n        size_t dst_index = dst.indices[tid];\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nWP_API bool wp_array_copy_device(void* context, void* dst, void* src, int dst_type, int src_type, int elem_size)\n{\n    if (!src || !dst)\n        return false;\n\n    const void* src_data = NULL;\n    void* dst_data = NULL;\n    int src_ndim = 0;\n    int dst_ndim = 0;\n    const int* src_shape = NULL;\n    const int* dst_shape = NULL;\n    const int* src_strides = NULL;\n    const int* dst_strides = NULL;\n    const int* const* src_indices = NULL;\n    const int* const* dst_indices = NULL;\n\n    const wp::fabricarray_t<void>* src_fabricarray = NULL;\n    wp::fabricarray_t<void>* dst_fabricarray = NULL;\n\n    const wp::indexedfabricarray_t<void>* src_indexedfabricarray = NULL;\n    wp::indexedfabricarray_t<void>* dst_indexedfabricarray = NULL;\n\n    const int* null_indices[wp::ARRAY_MAX_DIMS] = { NULL };\n\n    if (src_type == wp::ARRAY_TYPE_REGULAR) {\n        const wp::array_t<void>& src_arr = *static_cast<const wp::array_t<void>*>(src);\n        src_data = src_arr.data;\n        src_ndim = src_arr.ndim;\n        src_shape = src_arr.shape.dims;\n        src_strides = src_arr.strides;\n        src_indices = null_indices;\n    } else if (src_type == wp::ARRAY_TYPE_INDEXED) {\n        const wp::indexedarray_t<void>& src_arr = *static_cast<const wp::indexedarray_t<void>*>(src);\n        src_data = src_arr.arr.data;\n        src_ndim = src_arr.arr.ndim;\n        src_shape = src_arr.shape.dims;\n        src_strides = src_arr.arr.strides;\n        src_indices = src_arr.indices;\n    } else if (src_type == wp::ARRAY_TYPE_FABRIC) {\n        src_fabricarray = static_cast<const wp::fabricarray_t<void>*>(src);\n        src_ndim = 1;\n    } else if (src_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        src_indexedfabricarray = static_cast<const wp::indexedfabricarray_t<void>*>(src);\n        src_ndim = 1;\n    } else {\n        fprintf(stderr, \"Warp copy error: Invalid array type (%d)\\n\", src_type);\n        return false;\n    }\n\n    if (dst_type == wp::ARRAY_TYPE_REGULAR) {\n        const wp::array_t<void>& dst_arr = *static_cast<const wp::array_t<void>*>(dst);\n        dst_data = dst_arr.data;\n        dst_ndim = dst_arr.ndim;\n        dst_shape = dst_arr.shape.dims;\n        dst_strides = dst_arr.strides;\n        dst_indices = null_indices;\n    } else if (dst_type == wp::ARRAY_TYPE_INDEXED) {\n        const wp::indexedarray_t<void>& dst_arr = *static_cast<const wp::indexedarray_t<void>*>(dst);\n        dst_data = dst_arr.arr.data;\n        dst_ndim = dst_arr.arr.ndim;\n        dst_shape = dst_arr.shape.dims;\n        dst_strides = dst_arr.arr.strides;\n        dst_indices = dst_arr.indices;\n    } else if (dst_type == wp::ARRAY_TYPE_FABRIC) {\n        dst_fabricarray = static_cast<wp::fabricarray_t<void>*>(dst);\n        dst_ndim = 1;\n    } else if (dst_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        dst_indexedfabricarray = static_cast<wp::indexedfabricarray_t<void>*>(dst);\n        dst_ndim = 1;\n    } else {\n        fprintf(stderr, \"Warp copy error: Invalid array type (%d)\\n\", dst_type);\n        return false;\n    }\n\n    if (src_ndim != dst_ndim) {\n        fprintf(stderr, \"Warp copy error: Incompatible array dimensionalities (%d and %d)\\n\", src_ndim, dst_ndim);\n        return false;\n    }\n\n    ContextGuard guard(context);\n\n    // handle fabric arrays\n    if (dst_fabricarray) {\n        size_t n = dst_fabricarray->size;\n        if (src_fabricarray) {\n            // copy from fabric to fabric\n            if (src_fabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_to_fabric_kernel, n,\n                (*dst_fabricarray, *src_fabricarray, elem_size)\n            );\n            return true;\n        } else if (src_indexedfabricarray) {\n            // copy from fabric indexed to fabric\n            if (src_indexedfabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_indexed_to_fabric_kernel, n,\n                (*dst_fabricarray, *src_indexedfabricarray, elem_size)\n            );\n            return true;\n        } else {\n            // copy to fabric\n            if (size_t(src_shape[0]) != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_to_fabric_kernel, n,\n                (*dst_fabricarray, src_data, src_strides[0], src_indices[0], elem_size)\n            );\n            return true;\n        }\n    }\n    if (dst_indexedfabricarray) {\n        size_t n = dst_indexedfabricarray->size;\n        if (src_fabricarray) {\n            // copy from fabric to fabric indexed\n            if (src_fabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, *src_fabricarray, elem_size)\n            );\n            return true;\n        } else if (src_indexedfabricarray) {\n            // copy from fabric indexed to fabric indexed\n            if (src_indexedfabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_indexed_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, *src_indexedfabricarray, elem_size)\n            );\n            return true;\n        } else {\n            // copy to fabric indexed\n            if (size_t(src_shape[0]) != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, src_data, src_strides[0], src_indices[0], elem_size)\n            );\n            return true;\n        }\n    } else if (src_fabricarray) {\n        // copy from fabric\n        size_t n = src_fabricarray->size;\n        if (size_t(dst_shape[0]) != n) {\n            fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n            return false;\n        }\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_from_fabric_kernel, n,\n            (*src_fabricarray, dst_data, dst_strides[0], dst_indices[0], elem_size)\n        );\n        return true;\n    } else if (src_indexedfabricarray) {\n        // copy from fabric indexed\n        size_t n = src_indexedfabricarray->size;\n        if (size_t(dst_shape[0]) != n) {\n            fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n            return false;\n        }\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_from_fabric_indexed_kernel, n,\n            (*src_indexedfabricarray, dst_data, dst_strides[0], dst_indices[0], elem_size)\n        );\n        return true;\n    }\n\n    size_t n = 1;\n    for (int i = 0; i < src_ndim; i++) {\n        if (src_shape[i] != dst_shape[i]) {\n            fprintf(stderr, \"Warp copy error: Incompatible array shapes\\n\");\n            return false;\n        }\n        n *= src_shape[i];\n    }\n\n    switch (src_ndim) {\n    case 1: {\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_1d_kernel, n,\n            (dst_data, src_data, dst_strides[0], src_strides[0], dst_indices[0], src_indices[0], src_shape[0],\n             elem_size)\n        );\n        break;\n    }\n    case 2: {\n        wp::vec_t<2, size_t> shape_v(src_shape[0], src_shape[1]);\n        wp::vec_t<2, size_t> src_strides_v(src_strides[0], src_strides[1]);\n        wp::vec_t<2, size_t> dst_strides_v(dst_strides[0], dst_strides[1]);\n        wp::vec_t<2, const int*> src_indices_v(src_indices[0], src_indices[1]);\n        wp::vec_t<2, const int*> dst_indices_v(dst_indices[0], dst_indices[1]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_2d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    case 3: {\n        wp::vec_t<3, size_t> shape_v(src_shape[0], src_shape[1], src_shape[2]);\n        wp::vec_t<3, size_t> src_strides_v(src_strides[0], src_strides[1], src_strides[2]);\n        wp::vec_t<3, size_t> dst_strides_v(dst_strides[0], dst_strides[1], dst_strides[2]);\n        wp::vec_t<3, const int*> src_indices_v(src_indices[0], src_indices[1], src_indices[2]);\n        wp::vec_t<3, const int*> dst_indices_v(dst_indices[0], dst_indices[1], dst_indices[2]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_3d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    case 4: {\n        wp::vec_t<4, size_t> shape_v(src_shape[0], src_shape[1], src_shape[2], src_shape[3]);\n        wp::vec_t<4, size_t> src_strides_v(src_strides[0], src_strides[1], src_strides[2], src_strides[3]);\n        wp::vec_t<4, size_t> dst_strides_v(dst_strides[0], dst_strides[1], dst_strides[2], dst_strides[3]);\n        wp::vec_t<4, const int*> src_indices_v(src_indices[0], src_indices[1], src_indices[2], src_indices[3]);\n        wp::vec_t<4, const int*> dst_indices_v(dst_indices[0], dst_indices[1], dst_indices[2], dst_indices[3]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_4d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    default:\n        fprintf(stderr, \"Warp copy error: invalid array dimensionality (%d)\\n\", src_ndim);\n        return false;\n    }\n\n    return check_cuda(cudaGetLastError());\n}\n\n\nstatic __global__ void\narray_fill_1d_kernel(void* data, size_t n, size_t stride, const int* indices, const void* value, size_t value_size)\n{\n    size_t i = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (i < n) {\n        size_t idx = indices ? indices[i] : i;\n        char* p = (char*)data + idx * stride;\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_2d_kernel(\n    void* data,\n    wp::vec_t<2, size_t> shape,\n    wp::vec_t<2, size_t> strides,\n    wp::vec_t<2, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t i = tid / n;\n    size_t j = tid % n;\n    if (i < shape[0] /*&& j < shape[1]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1];\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_3d_kernel(\n    void* data,\n    wp::vec_t<3, size_t> shape,\n    wp::vec_t<3, size_t> strides,\n    wp::vec_t<3, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t i = tid / (n * o);\n    size_t j = tid % (n * o) / o;\n    size_t k = tid % o;\n    if (i < shape[0] && j < shape[1] /*&& k < shape[2]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        size_t idx2 = indices[2] ? indices[2][k] : k;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1] + idx2 * strides[2];\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_4d_kernel(\n    void* data,\n    wp::vec_t<4, size_t> shape,\n    wp::vec_t<4, size_t> strides,\n    wp::vec_t<4, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t p = shape[3];\n    size_t i = tid / (n * o * p);\n    size_t j = tid % (n * o * p) / (o * p);\n    size_t k = tid % (o * p) / p;\n    size_t l = tid % p;\n    if (i < shape[0] && j < shape[1] && k < shape[2] /*&& l < shape[3]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        size_t idx2 = indices[2] ? indices[2][k] : k;\n        size_t idx3 = indices[3] ? indices[3][l] : l;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1] + idx2 * strides[2] + idx3 * strides[3];\n        memcpy(p, value, value_size);\n    }\n}\n\n\nstatic __global__ void array_fill_fabric_kernel(wp::fabricarray_t<void> fa, const void* value, size_t value_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (tid < fa.size) {\n        void* dst_ptr = fabricarray_element_ptr(fa, tid, value_size);\n        memcpy(dst_ptr, value, value_size);\n    }\n}\n\n\nstatic __global__ void\narray_fill_fabric_indexed_kernel(wp::indexedfabricarray_t<void> ifa, const void* value, size_t value_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (tid < ifa.size) {\n        size_t idx = size_t(ifa.indices[tid]);\n        if (idx < ifa.fa.size) {\n            void* dst_ptr = fabricarray_element_ptr(ifa.fa, idx, value_size);\n            memcpy(dst_ptr, value, value_size);\n        }\n    }\n}\n\n\nWP_API void wp_array_fill_device(void* context, void* arr_ptr, int arr_type, const void* value_ptr, int value_size)\n{\n    if (!arr_ptr || !value_ptr)\n        return;\n\n    void* data = NULL;\n    int ndim = 0;\n    const int* shape = NULL;\n    const int* strides = NULL;\n    const int* const* indices = NULL;\n\n    wp::fabricarray_t<void>* fa = NULL;\n    wp::indexedfabricarray_t<void>* ifa = NULL;\n\n    const int* null_indices[wp::ARRAY_MAX_DIMS] = { NULL };\n\n    if (arr_type == wp::ARRAY_TYPE_REGULAR) {\n        wp::array_t<void>& arr = *static_cast<wp::array_t<void>*>(arr_ptr);\n        data = arr.data;\n        ndim = arr.ndim;\n        shape = arr.shape.dims;\n        strides = arr.strides;\n        indices = null_indices;\n    } else if (arr_type == wp::ARRAY_TYPE_INDEXED) {\n        wp::indexedarray_t<void>& ia = *static_cast<wp::indexedarray_t<void>*>(arr_ptr);\n        data = ia.arr.data;\n        ndim = ia.arr.ndim;\n        shape = ia.shape.dims;\n        strides = ia.arr.strides;\n        indices = ia.indices;\n    } else if (arr_type == wp::ARRAY_TYPE_FABRIC) {\n        fa = static_cast<wp::fabricarray_t<void>*>(arr_ptr);\n    } else if (arr_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        ifa = static_cast<wp::indexedfabricarray_t<void>*>(arr_ptr);\n    } else {\n        fprintf(stderr, \"Warp fill error: Invalid array type id %d\\n\", arr_type);\n        return;\n    }\n\n    size_t n = 1;\n    for (int i = 0; i < ndim; i++)\n        n *= shape[i];\n\n    ContextGuard guard(context);\n\n    void* value_devptr = NULL;  // fill value in device memory\n    bool free_devptr = true;  // whether we need to free the memory\n\n    // prepare the fill value in a graph-friendly way\n    if (!capturable_tmp_alloc(WP_CURRENT_CONTEXT, value_ptr, value_size, &value_devptr, &free_devptr)) {\n        fprintf(stderr, \"Warp fill error: failed to copy value to device memory\\n\");\n        return;\n    }\n\n    if (fa) {\n        // handle fabric arrays\n        wp_launch_device(WP_CURRENT_CONTEXT, array_fill_fabric_kernel, n, (*fa, value_devptr, value_size));\n    } else if (ifa) {\n        // handle indexed fabric arrays\n        wp_launch_device(WP_CURRENT_CONTEXT, array_fill_fabric_indexed_kernel, n, (*ifa, value_devptr, value_size));\n    } else {\n        // handle regular or indexed arrays\n        switch (ndim) {\n        case 1: {\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_1d_kernel, n,\n                (data, shape[0], strides[0], indices[0], value_devptr, value_size)\n            );\n            break;\n        }\n        case 2: {\n            wp::vec_t<2, size_t> shape_v(shape[0], shape[1]);\n            wp::vec_t<2, size_t> strides_v(strides[0], strides[1]);\n            wp::vec_t<2, const int*> indices_v(indices[0], indices[1]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_2d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        case 3: {\n            wp::vec_t<3, size_t> shape_v(shape[0], shape[1], shape[2]);\n            wp::vec_t<3, size_t> strides_v(strides[0], strides[1], strides[2]);\n            wp::vec_t<3, const int*> indices_v(indices[0], indices[1], indices[2]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_3d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        case 4: {\n            wp::vec_t<4, size_t> shape_v(shape[0], shape[1], shape[2], shape[3]);\n            wp::vec_t<4, size_t> strides_v(strides[0], strides[1], strides[2], strides[3]);\n            wp::vec_t<4, const int*> indices_v(indices[0], indices[1], indices[2], indices[3]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_4d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        default:\n            fprintf(stderr, \"Warp fill error: invalid array dimensionality (%d)\\n\", ndim);\n            break;\n        }\n    }\n\n    if (free_devptr) {\n        wp_free_device(WP_CURRENT_CONTEXT, value_devptr);\n    }\n}\n\nvoid wp_array_scan_int_device(uint64_t in, uint64_t out, int len, bool inclusive)\n{\n    scan_device((const int*)in, (int*)out, len, inclusive);\n}\n\nvoid wp_array_scan_float_device(uint64_t in, uint64_t out, int len, bool inclusive)\n{\n    scan_device((const float*)in, (float*)out, len, inclusive);\n}\n\nint wp_cuda_driver_version()\n{\n    int version;\n    if (check_cu(cuDriverGetVersion_f(&version)))\n        return version;\n    else\n        return 0;\n}\n\nint wp_cuda_toolkit_version() { return CUDA_VERSION; }\n\nint wp_nvrtc_version()\n{\n    int major = 0, minor = 0;\n    nvrtcVersion(&major, &minor);\n    return major * 1000 + minor * 10;\n}\n\nconst char* wp_libmathdx_version()\n{\n#if WP_ENABLE_MATHDX\n    static char version[64];\n    snprintf(version, sizeof(version), \"%d.%d.%d\", LIBMATHDX_VER_MAJOR, LIBMATHDX_VER_MINOR, LIBMATHDX_VER_PATCH);\n    return version;\n#else\n    return \"\";\n#endif\n}\n\nbool wp_cuda_driver_is_initialized() { return is_cuda_driver_initialized(); }\n\nint wp_nvrtc_supported_arch_count()\n{\n    int count;\n    if (check_nvrtc(nvrtcGetNumSupportedArchs(&count)))\n        return count;\n    else\n        return 0;\n}\n\nvoid wp_nvrtc_supported_archs(int* archs)\n{\n    if (archs) {\n        check_nvrtc(nvrtcGetSupportedArchs(archs));\n    }\n}\n\nint wp_cuda_device_get_count()\n{\n    int count = 0;\n    check_cu(cuDeviceGetCount_f(&count));\n    return count;\n}\n\nvoid* wp_cuda_device_get_primary_context(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size())) {\n        DeviceInfo& device_info = g_devices[ordinal];\n\n        // acquire the primary context if we haven't already\n        if (!device_info.primary_context)\n            check_cu(cuDevicePrimaryCtxRetain_f(&device_info.primary_context, device_info.device));\n\n        return device_info.primary_context;\n    }\n\n    return NULL;\n}\n\nconst char* wp_cuda_device_get_name(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].name;\n    return NULL;\n}\n\nint wp_cuda_device_get_arch(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].arch;\n    return 0;\n}\n\nint wp_cuda_device_get_sm_count(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].sm_count;\n    return 0;\n}\n\nint wp_cuda_device_get_max_shared_memory(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].max_smem_bytes;\n    return 0;\n}\n\nvoid wp_cuda_device_get_uuid(int ordinal, char uuid[16])\n{\n    memcpy(uuid, g_devices[ordinal].uuid.bytes, sizeof(char) * 16);\n}\n\nint wp_cuda_device_get_pci_domain_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_domain_id;\n    return -1;\n}\n\nint wp_cuda_device_get_pci_bus_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_bus_id;\n    return -1;\n}\n\nint wp_cuda_device_get_pci_device_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_device_id;\n    return -1;\n}\n\nint wp_cuda_device_is_uva(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_uva;\n    return 0;\n}\n\nint wp_cuda_device_is_mempool_supported(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_mempool_supported;\n    return 0;\n}\n\nint wp_cuda_device_is_ipc_supported(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_ipc_supported;\n    return 0;\n}\n\nint wp_cuda_device_set_mempool_release_threshold(int ordinal, uint64_t threshold)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!check_cuda(cudaMemPoolSetAttribute(pool, cudaMemPoolAttrReleaseThreshold, &threshold))) {\n        fprintf(stderr, \"Warp error: Failed to set memory pool attribute on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    return 1;  // success\n}\n\nuint64_t wp_cuda_device_get_mempool_release_threshold(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t threshold = 0;\n    if (!check_cuda(cudaMemPoolGetAttribute(pool, cudaMemPoolAttrReleaseThreshold, &threshold))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool release threshold on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    return threshold;\n}\n\nuint64_t wp_cuda_device_get_mempool_used_mem_current(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t mem_used = 0;\n    if (!check_cuda(cudaMemPoolGetAttribute(pool, cudaMemPoolAttrUsedMemCurrent, &mem_used))) {\n        fprintf(\n            stderr, \"Warp error: Failed to get amount of currently used memory from the memory pool on device %d\\n\",\n            ordinal\n        );\n        return 0;\n    }\n\n    return mem_used;\n}\n\nuint64_t wp_cuda_device_get_mempool_used_mem_high(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t mem_high_water_mark = 0;\n    if (!check_cuda(cudaMemPoolGetAttribute(pool, cudaMemPoolAttrUsedMemHigh, &mem_high_water_mark))) {\n        fprintf(\n            stderr, \"Warp error: Failed to get memory usage high water mark from the memory pool on device %d\\n\",\n            ordinal\n        );\n        return 0;\n    }\n\n    return mem_high_water_mark;\n}\n\nvoid wp_cuda_device_get_memory_info(int ordinal, size_t* free_mem, size_t* total_mem)\n{\n    // use temporary storage if user didn't specify pointers\n    size_t tmp_free_mem, tmp_total_mem;\n\n    if (free_mem)\n        *free_mem = 0;\n    else\n        free_mem = &tmp_free_mem;\n\n    if (total_mem)\n        *total_mem = 0;\n    else\n        total_mem = &tmp_total_mem;\n\n    if (ordinal >= 0 && ordinal < int(g_devices.size())) {\n        if (g_devices[ordinal].primary_context) {\n            ContextGuard guard(g_devices[ordinal].primary_context, true);\n            check_cu(cuMemGetInfo_f(free_mem, total_mem));\n        } else {\n            // if we haven't acquired the primary context yet, acquire it temporarily\n            CUcontext primary_context = NULL;\n            check_cu(cuDevicePrimaryCtxRetain_f(&primary_context, g_devices[ordinal].device));\n            {\n                ContextGuard guard(primary_context, true);\n                check_cu(cuMemGetInfo_f(free_mem, total_mem));\n            }\n            check_cu(cuDevicePrimaryCtxRelease_f(g_devices[ordinal].device));\n        }\n    }\n}\n\n\nvoid* wp_cuda_context_get_current() { return get_current_context(); }\n\nvoid wp_cuda_context_set_current(void* context)\n{\n    CUcontext ctx = static_cast<CUcontext>(context);\n    CUcontext prev_ctx = NULL;\n    check_cu(cuCtxGetCurrent_f(&prev_ctx));\n    if (ctx != prev_ctx) {\n        check_cu(cuCtxSetCurrent_f(ctx));\n    }\n}\n\nvoid wp_cuda_context_push_current(void* context) { check_cu(cuCtxPushCurrent_f(static_cast<CUcontext>(context))); }\n\nvoid wp_cuda_context_pop_current()\n{\n    CUcontext context;\n    check_cu(cuCtxPopCurrent_f(&context));\n}\n\nvoid* wp_cuda_context_create(int device_ordinal)\n{\n    CUcontext ctx = NULL;\n    CUdevice device;\n    if (check_cu(cuDeviceGet_f(&device, device_ordinal)))\n        check_cu(cuCtxCreate_f(&ctx, 0, device));\n    return ctx;\n}\n\nvoid wp_cuda_context_destroy(void* context)\n{\n    if (context) {\n        CUcontext ctx = static_cast<CUcontext>(context);\n\n        // ensure this is not the current context\n        if (ctx == wp_cuda_context_get_current())\n            wp_cuda_context_set_current(NULL);\n\n        // release the cached info about this context\n        ContextInfo* info = get_context_info(ctx);\n        if (info) {\n            if (info->stream)\n                check_cu(cuStreamDestroy_f(info->stream));\n\n            if (info->conditional_module)\n                check_cu(cuModuleUnload_f(info->conditional_module));\n\n            g_contexts.erase(ctx);\n        }\n\n        check_cu(cuCtxDestroy_f(ctx));\n    }\n}\n\nvoid wp_cuda_context_synchronize(void* context)\n{\n    ContextGuard guard(context);\n\n    check_cu(cuCtxSynchronize_f());\n\n    if (!context)\n        context = get_current_context();\n\n    if (run_deferred_actions(context) > 0) {\n        // ensure deferred asynchronous operations complete\n        check_cu(cuCtxSynchronize_f());\n    }\n\n    // check_cuda(cudaDeviceGraphMemTrim(wp_cuda_context_get_device_ordinal(context)));\n}\n\nuint64_t wp_cuda_context_check(void* context)\n{\n    ContextGuard guard(context);\n\n    // check errors before syncing\n    cudaError_t e = cudaGetLastError();\n    check_cuda(e);\n\n    cudaStreamCaptureStatus status = cudaStreamCaptureStatusNone;\n    check_cuda(cudaStreamIsCapturing(get_current_stream(), &status));\n\n    // synchronize if the stream is not capturing\n    if (status == cudaStreamCaptureStatusNone) {\n        check_cuda(cudaDeviceSynchronize());\n        e = cudaGetLastError();\n    }\n\n    return static_cast<uint64_t>(e);\n}\n\n\nint wp_cuda_context_get_device_ordinal(void* context)\n{\n    ContextInfo* info = get_context_info(static_cast<CUcontext>(context));\n    return info && info->device_info ? info->device_info->ordinal : -1;\n}\n\nint wp_cuda_context_is_primary(void* context)\n{\n    CUcontext ctx = static_cast<CUcontext>(context);\n    ContextInfo* context_info = get_context_info(ctx);\n    if (!context_info) {\n        fprintf(stderr, \"Warp error: Failed to get context info\\n\");\n        return 0;\n    }\n\n    // if the device primary context is known, check if it matches the given context\n    DeviceInfo* device_info = context_info->device_info;\n    if (device_info->primary_context)\n        return int(ctx == device_info->primary_context);\n\n    // there is no CUDA API to check if a context is primary, but we can temporarily\n    // acquire the device's primary context to check the pointer\n    CUcontext primary_ctx;\n    if (check_cu(cuDevicePrimaryCtxRetain_f(&primary_ctx, device_info->device))) {\n        check_cu(cuDevicePrimaryCtxRelease_f(device_info->device));\n        return int(ctx == primary_ctx);\n    }\n\n    return 0;\n}\n\nvoid* wp_cuda_context_get_stream(void* context)\n{\n    ContextInfo* info = get_context_info(static_cast<CUcontext>(context));\n    if (info) {\n        return info->stream;\n    }\n    return NULL;\n}\n\nvoid wp_cuda_context_set_stream(void* context, void* stream, int sync)\n{\n    ContextInfo* context_info = get_context_info(static_cast<CUcontext>(context));\n    if (context_info) {\n        CUstream new_stream = static_cast<CUstream>(stream);\n\n        // check whether we should sync with the previous stream on this device\n        if (sync) {\n            CUstream old_stream = context_info->stream;\n            StreamInfo* old_stream_info = get_stream_info(old_stream);\n            if (old_stream_info) {\n                CUevent cached_event = old_stream_info->cached_event;\n                check_cu(cuEventRecord_f(cached_event, old_stream));\n                check_cu(cuStreamWaitEvent_f(new_stream, cached_event, CU_EVENT_WAIT_DEFAULT));\n            }\n        }\n\n        context_info->stream = new_stream;\n    }\n}\n\nint wp_cuda_is_peer_access_supported(int target_ordinal, int peer_ordinal)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid target device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;\n\n    int can_access = 0;\n    check_cuda(cudaDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n\n    return can_access;\n}\n\nint wp_cuda_is_peer_access_enabled(void* target_context, void* peer_context)\n{\n    if (!target_context || !peer_context) {\n        fprintf(stderr, \"Warp error: invalid CUDA context\\n\");\n        return 0;\n    }\n\n    if (target_context == peer_context)\n        return 1;\n\n    int target_ordinal = wp_cuda_context_get_device_ordinal(target_context);\n    int peer_ordinal = wp_cuda_context_get_device_ordinal(peer_context);\n\n    // check if peer access is supported\n    int can_access = 0;\n    check_cuda(cudaDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n    if (!can_access)\n        return 0;\n\n    // There is no CUDA API to query if peer access is enabled, but we can try to enable it and check the result.\n\n    ContextGuard guard(peer_context, true);\n\n    CUcontext target_ctx = static_cast<CUcontext>(target_context);\n\n    CUresult result = cuCtxEnablePeerAccess_f(target_ctx, 0);\n    if (result == CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED) {\n        return 1;\n    } else if (result == CUDA_SUCCESS) {\n        // undo enablement\n        check_cu(cuCtxDisablePeerAccess_f(target_ctx));\n        return 0;\n    } else {\n        // report error\n        check_cu(result);\n        return 0;\n    }\n}\n\nint wp_cuda_set_peer_access_enabled(void* target_context, void* peer_context, int enable)\n{\n    if (!target_context || !peer_context) {\n        fprintf(stderr, \"Warp error: invalid CUDA context\\n\");\n        return 0;\n    }\n\n    if (target_context == peer_context)\n        return 1;  // no-op\n\n    int target_ordinal = wp_cuda_context_get_device_ordinal(target_context);\n    int peer_ordinal = wp_cuda_context_get_device_ordinal(peer_context);\n\n    // check if peer access is supported\n    int can_access = 0;\n    check_cuda(cudaDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n    if (!can_access) {\n        // failure if enabling, success if disabling\n        if (enable) {\n            fprintf(stderr, \"Warp error: device %d cannot access device %d\\n\", peer_ordinal, target_ordinal);\n            return 0;\n        } else\n            return 1;\n    }\n\n    ContextGuard guard(peer_context, true);\n\n    CUcontext target_ctx = static_cast<CUcontext>(target_context);\n\n    if (enable) {\n        CUresult status = cuCtxEnablePeerAccess_f(target_ctx, 0);\n        if (status != CUDA_SUCCESS && status != CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED) {\n            check_cu(status);\n            fprintf(\n                stderr, \"Warp error: failed to enable peer access from device %d to device %d\\n\", peer_ordinal,\n                target_ordinal\n            );\n            return 0;\n        }\n    } else {\n        CUresult status = cuCtxDisablePeerAccess_f(target_ctx);\n        if (status != CUDA_SUCCESS && status != CUDA_ERROR_PEER_ACCESS_NOT_ENABLED) {\n            check_cu(status);\n            fprintf(\n                stderr, \"Warp error: failed to disable peer access from device %d to device %d\\n\", peer_ordinal,\n                target_ordinal\n            );\n            return 0;\n        }\n    }\n\n    return 1;  // success\n}\n\nint wp_cuda_is_mempool_access_enabled(int target_ordinal, int peer_ordinal)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;\n\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, target_ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool of device %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    cudaMemAccessFlags flags = cudaMemAccessFlagsProtNone;\n    cudaMemLocation location;\n    location.id = peer_ordinal;\n    location.type = cudaMemLocationTypeDevice;\n    if (check_cuda(cudaMemPoolGetAccess(&flags, pool, &location)))\n        return int(flags != cudaMemAccessFlagsProtNone);\n\n    return 0;\n}\n\nint wp_cuda_set_mempool_access_enabled(int target_ordinal, int peer_ordinal, int enable)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;  // no-op\n\n    // get the memory pool\n    cudaMemPool_t pool;\n    if (!check_cuda(cudaDeviceGetDefaultMemPool(&pool, target_ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool of device %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    cudaMemAccessDesc desc;\n    desc.location.type = cudaMemLocationTypeDevice;\n    desc.location.id = peer_ordinal;\n\n    // only cudaMemAccessFlagsProtReadWrite and cudaMemAccessFlagsProtNone are supported\n    if (enable)\n        desc.flags = cudaMemAccessFlagsProtReadWrite;\n    else\n        desc.flags = cudaMemAccessFlagsProtNone;\n\n    if (!check_cuda(cudaMemPoolSetAccess(pool, &desc, 1))) {\n        fprintf(\n            stderr, \"Warp error: Failed to set mempool access from device %d to device %d\\n\", peer_ordinal,\n            target_ordinal\n        );\n        return 0;\n    }\n\n    return 1;  // success\n}\n\nvoid wp_cuda_ipc_get_mem_handle(void* ptr, char* out_buffer)\n{\n    CUipcMemHandle memHandle;\n    check_cu(cuIpcGetMemHandle_f(&memHandle, (CUdeviceptr)ptr));\n    memcpy(out_buffer, memHandle.reserved, CU_IPC_HANDLE_SIZE);\n}\n\nvoid* wp_cuda_ipc_open_mem_handle(void* context, char* handle)\n{\n    ContextGuard guard(context);\n\n    CUipcMemHandle memHandle;\n    memcpy(memHandle.reserved, handle, CU_IPC_HANDLE_SIZE);\n\n    CUdeviceptr device_ptr;\n\n    // Strangely, the CU_IPC_MEM_LAZY_ENABLE_PEER_ACCESS flag is required\n    if check_cu (cuIpcOpenMemHandle_f(&device_ptr, memHandle, CU_IPC_MEM_LAZY_ENABLE_PEER_ACCESS))\n        return (void*)device_ptr;\n    else\n        return NULL;\n}\n\nvoid wp_cuda_ipc_close_mem_handle(void* ptr) { check_cu(cuIpcCloseMemHandle_f((CUdeviceptr)ptr)); }\n\nvoid wp_cuda_ipc_get_event_handle(void* context, void* event, char* out_buffer)\n{\n    ContextGuard guard(context);\n\n    CUipcEventHandle eventHandle;\n    check_cu(cuIpcGetEventHandle_f(&eventHandle, static_cast<CUevent>(event)));\n    memcpy(out_buffer, eventHandle.reserved, CU_IPC_HANDLE_SIZE);\n}\n\nvoid* wp_cuda_ipc_open_event_handle(void* context, char* handle)\n{\n    ContextGuard guard(context);\n\n    CUipcEventHandle eventHandle;\n    memcpy(eventHandle.reserved, handle, CU_IPC_HANDLE_SIZE);\n\n    CUevent event;\n\n    if (check_cu(cuIpcOpenEventHandle_f(&event, eventHandle)))\n        return event;\n    else\n        return NULL;\n}\n\nvoid* wp_cuda_stream_create(void* context, int priority)\n{\n    ContextGuard guard(context, true);\n\n    CUstream stream;\n    if (check_cu(cuStreamCreateWithPriority_f(&stream, CU_STREAM_DEFAULT, priority))) {\n        wp_cuda_stream_register(WP_CURRENT_CONTEXT, stream);\n        return stream;\n    } else\n        return NULL;\n}\n\nvoid wp_cuda_stream_destroy(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    wp_cuda_stream_unregister(context, stream);\n\n    // release temporary radix sort buffer associated with this stream\n    radix_sort_release(context, stream);\n\n    check_cu(cuStreamDestroy_f(static_cast<CUstream>(stream)));\n}\n\nint wp_cuda_stream_query(void* stream)\n{\n    CUresult res = cuStreamQuery_f(static_cast<CUstream>(stream));\n\n    if ((res != CUDA_SUCCESS) && (res != CUDA_ERROR_NOT_READY)) {\n        // Abnormal, print out error\n        check_cu(res);\n    }\n\n    return res;\n}\n\nvoid wp_cuda_stream_register(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    ContextGuard guard(context);\n\n    // populate stream info\n    StreamInfo& stream_info = g_streams[static_cast<CUstream>(stream)];\n    check_cu(cuEventCreate_f(&stream_info.cached_event, CU_EVENT_DISABLE_TIMING));\n}\n\nvoid wp_cuda_stream_unregister(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (stream_info) {\n        // release stream info\n        check_cu(cuEventDestroy_f(stream_info->cached_event));\n        g_streams.erase(cuda_stream);\n    }\n\n    // make sure we don't leave dangling references to this stream\n    ContextInfo* context_info = get_context_info(context);\n    if (context_info) {\n        if (cuda_stream == context_info->stream)\n            context_info->stream = NULL;\n    }\n}\n\nvoid* wp_cuda_stream_get_current() { return get_current_stream(); }\n\nvoid wp_cuda_stream_synchronize(void* stream) { check_cu(cuStreamSynchronize_f(static_cast<CUstream>(stream))); }\n\nvoid wp_cuda_stream_wait_event(void* stream, void* event, bool external)\n{\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        // wait for an external event during graph capture\n        check_cu(\n            cuStreamWaitEvent_f(static_cast<CUstream>(stream), static_cast<CUevent>(event), CU_EVENT_WAIT_EXTERNAL)\n        );\n    } else {\n        check_cu(\n            cuStreamWaitEvent_f(static_cast<CUstream>(stream), static_cast<CUevent>(event), CU_EVENT_WAIT_DEFAULT)\n        );\n    }\n}\n\nvoid wp_cuda_stream_wait_stream(void* stream, void* other_stream, void* event, bool external)\n{\n    unsigned record_flags = CU_EVENT_RECORD_DEFAULT;\n    unsigned wait_flags = CU_EVENT_WAIT_DEFAULT;\n\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty()) {\n        if (wp_cuda_stream_is_capturing(other_stream))\n            record_flags = CU_EVENT_RECORD_EXTERNAL;\n        if (wp_cuda_stream_is_capturing(stream))\n            wait_flags = CU_EVENT_WAIT_EXTERNAL;\n    }\n\n    check_cu(cuEventRecordWithFlags_f(static_cast<CUevent>(event), static_cast<CUstream>(other_stream), record_flags));\n    check_cu(cuStreamWaitEvent_f(static_cast<CUstream>(stream), static_cast<CUevent>(event), wait_flags));\n}\n\nint wp_cuda_stream_is_capturing(void* stream)\n{\n    cudaStreamCaptureStatus status = cudaStreamCaptureStatusNone;\n    check_cuda(cudaStreamIsCapturing(static_cast<cudaStream_t>(stream), &status));\n\n    return int(status != cudaStreamCaptureStatusNone);\n}\n\nuint64_t wp_cuda_stream_get_capture_id(void* stream) { return get_capture_id(static_cast<CUstream>(stream)); }\n\nint wp_cuda_stream_get_priority(void* stream)\n{\n    int priority = 0;\n    check_cuda(cuStreamGetPriority_f(static_cast<CUstream>(stream), &priority));\n\n    return priority;\n}\n\nvoid* wp_cuda_event_create(void* context, unsigned flags)\n{\n    ContextGuard guard(context, true);\n\n    CUevent event;\n    if (check_cu(cuEventCreate_f(&event, flags)))\n        return event;\n    else\n        return NULL;\n}\n\nvoid wp_cuda_event_destroy(void* event) { check_cu(cuEventDestroy_f(static_cast<CUevent>(event))); }\n\nint wp_cuda_event_query(void* event)\n{\n    CUresult res = cuEventQuery_f(static_cast<CUevent>(event));\n\n    if ((res != CUDA_SUCCESS) && (res != CUDA_ERROR_NOT_READY)) {\n        // Abnormal, print out error\n        check_cu(res);\n    }\n\n    return res;\n}\n\nvoid wp_cuda_event_record(void* event, void* stream, bool external)\n{\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        // record external event during graph capture (e.g., for timing or when explicitly specified by the user)\n        check_cu(cuEventRecordWithFlags_f(\n            static_cast<CUevent>(event), static_cast<CUstream>(stream), CU_EVENT_RECORD_EXTERNAL\n        ));\n    } else {\n        check_cu(cuEventRecord_f(static_cast<CUevent>(event), static_cast<CUstream>(stream)));\n    }\n}\n\nvoid wp_cuda_event_synchronize(void* event) { check_cu(cuEventSynchronize_f(static_cast<CUevent>(event))); }\n\nfloat wp_cuda_event_elapsed_time(void* start_event, void* end_event)\n{\n    float elapsed = 0.0f;\n    cudaEvent_t start = static_cast<cudaEvent_t>(start_event);\n    cudaEvent_t end = static_cast<cudaEvent_t>(end_event);\n    check_cuda(cudaEventElapsedTime(&elapsed, start, end));\n    return elapsed;\n}\n\nbool wp_cuda_graph_begin_capture(void* context, void* stream, int external, int mode)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: unknown stream\");\n        return false;\n    }\n\n    cudaStreamCaptureMode capture_mode;\n    switch (mode) {\n    case WP_CUDA_GRAPH_CAPTURE_MODE_GLOBAL:\n        capture_mode = cudaStreamCaptureModeGlobal;\n        break;\n    case WP_CUDA_GRAPH_CAPTURE_MODE_THREAD_LOCAL:\n        capture_mode = cudaStreamCaptureModeThreadLocal;\n        break;\n    case WP_CUDA_GRAPH_CAPTURE_MODE_RELAXED:\n        capture_mode = cudaStreamCaptureModeRelaxed;\n        break;\n    default:\n        wp::set_error_string(\"Warp error: invalid capture mode\");\n        return false;\n    }\n\n    if (external) {\n        // if it's an external capture, make sure it's already active so we can get the capture id\n        cudaStreamCaptureStatus status = cudaStreamCaptureStatusNone;\n        if (!check_cuda(cudaStreamIsCapturing(cuda_stream, &status)))\n            return false;\n        if (status != cudaStreamCaptureStatusActive) {\n            wp::set_error_string(\"Warp error: stream is not capturing\");\n            return false;\n        }\n    } else {\n        if (!check_cuda(cudaStreamBeginCapture(cuda_stream, capture_mode)))\n            return false;\n    }\n\n    uint64_t capture_id = get_capture_id(cuda_stream);\n\n    CaptureInfo* capture = new CaptureInfo();\n    capture->stream = cuda_stream;\n    capture->id = capture_id;\n    capture->external = bool(external);\n    capture->mode = capture_mode;\n\n    // update stream info\n    stream_info->capture = capture;\n\n    // add to known captures\n    g_captures[capture_id] = capture;\n\n    return true;\n}\n\nbool wp_cuda_graph_end_capture(void* context, void* stream, void** graph_ret)\n{\n    ContextGuard guard(context);\n\n    // check if this is a known stream\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: unknown capture stream\");\n        return false;\n    }\n\n    // check if this stream was used to start a capture\n    CaptureInfo* capture = stream_info->capture;\n    if (!capture) {\n        wp::set_error_string(\"Warp error: stream has no capture started\");\n        return false;\n    }\n\n    // get capture info\n    bool external = capture->external;\n    uint64_t capture_id = capture->id;\n    std::vector<FreeInfo> tmp_allocs = capture->tmp_allocs;\n\n    // clear capture info\n    stream_info->capture = NULL;\n    g_captures.erase(capture_id);\n    delete capture;\n\n    // a lambda to clean up on exit in case of error\n    auto clean_up = [cuda_stream, capture_id, external]() {\n        // unreference outstanding graph allocs so that they will be released with the user reference\n        for (auto it = g_graph_allocs.begin(); it != g_graph_allocs.end(); ++it) {\n            GraphAllocInfo& alloc_info = it->second;\n            if (alloc_info.capture_id == capture_id)\n                alloc_info.graph_destroyed = true;\n        }\n\n        // make sure we terminate the capture\n        if (!external) {\n            cudaGraph_t graph = NULL;\n            cudaStreamEndCapture(cuda_stream, &graph);\n            cudaGetLastError();\n        }\n    };\n\n    // get captured graph without ending the capture in case it is external\n    cudaGraph_t graph = get_capture_graph(cuda_stream);\n    if (!graph) {\n        clean_up();\n        return false;\n    }\n\n    // ensure that all forked streams are joined to the main capture stream by manually\n    // adding outstanding capture dependencies gathered from the graph leaf nodes\n    std::vector<cudaGraphNode_t> stream_dependencies;\n    std::vector<cudaGraphNode_t> leaf_nodes;\n    if (get_capture_dependencies(cuda_stream, stream_dependencies) && get_graph_leaf_nodes(graph, leaf_nodes)) {\n        // compute set difference to get unjoined dependencies\n        std::vector<cudaGraphNode_t> unjoined_dependencies;\n        std::sort(stream_dependencies.begin(), stream_dependencies.end());\n        std::sort(leaf_nodes.begin(), leaf_nodes.end());\n        std::set_difference(\n            leaf_nodes.begin(), leaf_nodes.end(), stream_dependencies.begin(), stream_dependencies.end(),\n            std::back_inserter(unjoined_dependencies)\n        );\n        if (!unjoined_dependencies.empty()) {\n            check_cu(cuStreamUpdateCaptureDependencies_f(\n                cuda_stream, unjoined_dependencies.data(), unjoined_dependencies.size(),\n                CU_STREAM_ADD_CAPTURE_DEPENDENCIES\n            ));\n            // ensure graph is still valid\n            if (get_capture_graph(cuda_stream) != graph) {\n                clean_up();\n                return false;\n            }\n        }\n    }\n\n    // check if this graph has unfreed allocations, which require special handling\n    std::vector<void*> unfreed_allocs;\n    for (auto it = g_graph_allocs.begin(); it != g_graph_allocs.end(); ++it) {\n        GraphAllocInfo& alloc_info = it->second;\n        if (alloc_info.capture_id == capture_id)\n            unfreed_allocs.push_back(it->first);\n    }\n\n    if (!unfreed_allocs.empty() || !tmp_allocs.empty()) {\n        // Create a user object that will notify us when the instantiated graph is destroyed.\n        // This works for external captures also, since we wouldn't otherwise know when\n        // the externally-created graph instance gets deleted.\n        // This callback is guaranteed to arrive after the graph has finished executing on the device,\n        // not necessarily when cudaGraphExecDestroy() is called.\n        GraphDestroyCallbackInfo* graph_info = new GraphDestroyCallbackInfo;\n        graph_info->context = context ? context : get_current_context();\n        graph_info->unfreed_allocs = unfreed_allocs;\n        graph_info->tmp_allocs = tmp_allocs;\n        cudaUserObject_t user_object;\n        check_cuda(cudaUserObjectCreate(&user_object, graph_info, on_graph_destroy, 1, cudaUserObjectNoDestructorSync));\n        check_cuda(cudaGraphRetainUserObject(graph, user_object, 1, cudaGraphUserObjectMove));\n\n        // ensure graph is still valid\n        if (get_capture_graph(cuda_stream) != graph) {\n            clean_up();\n            return false;\n        }\n    }\n\n    // for external captures, we don't instantiate the graph ourselves, so we're done\n    if (external)\n        return true;\n\n    // end the capture\n    if (!check_cuda(cudaStreamEndCapture(cuda_stream, &graph)))\n        return false;\n\n    // process deferred free list if no more captures are ongoing\n    if (g_captures.empty()) {\n        run_deferred_actions();\n    }\n\n    if (graph_ret)\n        *graph_ret = graph;\n\n    return true;\n}\n\nbool wp_capture_debug_dot_print(void* graph, const char* path, uint32_t flags)\n{\n    if (!check_cuda(cudaGraphDebugDotPrint((cudaGraph_t)graph, path, flags)))\n        return false;\n    return true;\n}\n\nbool wp_cuda_graph_create_exec(void* context, void* stream, void* graph, void** graph_exec_ret)\n{\n    ContextGuard guard(context);\n\n    cudaGraphExec_t graph_exec = NULL;\n    if (!check_cuda(\n            cudaGraphInstantiateWithFlags(&graph_exec, (cudaGraph_t)graph, cudaGraphInstantiateFlagAutoFreeOnLaunch)\n        ))\n        return false;\n\n    // Usually uploading the graph explicitly is optional, but when updating graph nodes (e.g., indirect dispatch)\n    // then the upload is required because otherwise the graph nodes that get updated might not yet be uploaded, which\n    // results in undefined behavior.\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    if (!check_cuda(cudaGraphUpload(graph_exec, cuda_stream)))\n        return false;\n\n    if (graph_exec_ret)\n        *graph_exec_ret = graph_exec;\n\n    return true;\n}\n\nvoid* wp_cuda_graph_insert_memcpy(void* context, void* stream, void* dst, void* src, size_t size, int kind)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    cudaMemcpyKind memcpy_kind = static_cast<cudaMemcpyKind>(kind);\n\n    // Get the current stream capturing graph\n    CUstreamCaptureStatus capture_status = CU_STREAM_CAPTURE_STATUS_NONE;\n    cudaGraph_t graph = NULL;\n    const cudaGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)))\n        return NULL;\n\n    // abort if not capturing\n    if (!graph || capture_status != CU_STREAM_CAPTURE_STATUS_ACTIVE) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return NULL;\n    }\n\n    cudaGraphNode_t node = NULL;\n    if (!check_cuda(cudaGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dst, src, size, memcpy_kind)))\n        return NULL;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &node, 1, cudaStreamSetCaptureDependencies)))\n        return NULL;\n\n    return node;\n}\n\nbool wp_cuda_graph_insert_memcpy_batch(\n    void* context, void* stream, void** dsts, void** srcs, size_t* sizes, int* kinds, int count, void** nodes_ret\n)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    // Get the current stream capturing graph\n    CUstreamCaptureStatus capture_status = CU_STREAM_CAPTURE_STATUS_NONE;\n    cudaGraph_t graph = NULL;\n    const cudaGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)))\n        return false;\n\n    // abort if not capturing\n    if (!graph || capture_status != CU_STREAM_CAPTURE_STATUS_ACTIVE) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n#if 1\n    // sequential version (copies executed on the same stream)\n    //\n    // TODO:\n    // - figure out why sequential is faster\n    // - overhead due to graph launch on multiple streams?\n    //\n\n    for (int i = 0; i < count; i++) {\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        cudaMemcpyKind memcpy_kind = static_cast<cudaMemcpyKind>(kinds[i]);\n        cudaGraphNode_t node = NULL;\n        if (!check_cuda(\n                cudaGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n        nodes_ret[i] = node;\n\n        if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &node, 1, cudaStreamSetCaptureDependencies)))\n            return false;\n    }\n#else\n    // parallel version (copies can execute on multiple streams)\n    for (int i = 0; i < count; i++) {\n        cudaMemcpyKind memcpy_kind = static_cast<cudaMemcpyKind>(kinds[i]);\n        cudaGraphNode_t node = NULL;\n        if (!check_cuda(\n                cudaGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n        nodes_ret[i] = node;\n    }\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(\n            cuda_stream, (cudaGraphNode_t*)nodes_ret, count, cudaStreamSetCaptureDependencies\n        )))\n        return false;\n#endif\n\n    return true;\n}\n\nbool wp_cuda_graph_update_memcpy(void* graph_exec, void* node, void* dst, void* src, size_t size, int kind)\n{\n    cudaGraphExec_t cuda_graph_exec = static_cast<cudaGraphExec_t>(graph_exec);\n    cudaGraphNode_t cuda_node = static_cast<cudaGraphNode_t>(node);\n    cudaMemcpyKind memcpy_kind = static_cast<cudaMemcpyKind>(kind);\n\n    if (!check_cuda(cudaGraphExecMemcpyNodeSetParams1D(cuda_graph_exec, cuda_node, dst, src, size, memcpy_kind)))\n        return false;\n\n    return true;\n}\n\nbool wp_cuda_graph_update_memcpy_batch(\n    void* graph_exec, void** nodes, void** dsts, void** srcs, size_t* sizes, int* kinds, int count\n)\n{\n    cudaGraphExec_t cuda_graph_exec = static_cast<cudaGraphExec_t>(graph_exec);\n\n    for (int i = 0; i < count; i++) {\n        cudaGraphNode_t cuda_node = static_cast<cudaGraphNode_t>(nodes[i]);\n        cudaMemcpyKind memcpy_kind = static_cast<cudaMemcpyKind>(kinds[i]);\n\n        if (!check_cuda(\n                cudaGraphExecMemcpyNodeSetParams1D(cuda_graph_exec, cuda_node, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n    }\n\n    return true;\n}\n\n// Support for conditional graph nodes available with CUDA 12.4+.\n#if CUDA_VERSION >= 12040\n\n// CUBIN or PTX data for compiled conditional modules, loaded on demand, keyed on device architecture\nusing ModuleKey = std::pair<int, bool>;  // <arch, use_ptx>\nstatic std::map<ModuleKey, void*> g_conditional_modules;\n\n// Compile module with conditional helper kernels\nstatic void* compile_conditional_module(int arch, bool use_ptx)\n{\n    static const char* kernel_source = R\"(\n        typedef __device_builtin__ unsigned long long cudaGraphConditionalHandle;\n        extern \"C\" __device__ __cudart_builtin__ void cudaGraphSetConditional(cudaGraphConditionalHandle handle, unsigned int value);\n\n        extern \"C\" __global__ void set_conditional_if_handle_kernel(cudaGraphConditionalHandle handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n                cudaGraphSetConditional(handle, *value);\n        }\n\n        extern \"C\" __global__ void set_conditional_else_handle_kernel(cudaGraphConditionalHandle handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n                cudaGraphSetConditional(handle, !*value);\n        }\n\n        extern \"C\" __global__ void set_conditional_if_else_handles_kernel(cudaGraphConditionalHandle if_handle, cudaGraphConditionalHandle else_handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n            {\n                cudaGraphSetConditional(if_handle, *value);\n                cudaGraphSetConditional(else_handle, !*value);\n            }\n        }\n    )\";\n\n    // avoid recompilation\n    ModuleKey key = { arch, use_ptx };\n    auto it = g_conditional_modules.find(key);\n    if (it != g_conditional_modules.end())\n        return it->second;\n\n    nvrtcProgram prog;\n    if (!check_nvrtc(nvrtcCreateProgram(&prog, kernel_source, \"conditional_kernels\", 0, NULL, NULL)))\n        return NULL;\n\n    char arch_opt[128];\n    if (use_ptx)\n        snprintf(arch_opt, sizeof(arch_opt), \"--gpu-architecture=compute_%d\", arch);\n    else\n        snprintf(arch_opt, sizeof(arch_opt), \"--gpu-architecture=sm_%d\", arch);\n\n    std::vector<const char*> opts;\n    opts.push_back(arch_opt);\n\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n    if (print_debug) {\n        printf(\"NVRTC options (conditional module, arch=%d, use_ptx=%s):\\n\", arch, use_ptx ? \"true\" : \"false\");\n        for (auto o : opts) {\n            printf(\"%s\\n\", o);\n        }\n    }\n\n    if (!check_nvrtc(nvrtcCompileProgram(prog, int(opts.size()), opts.data()))) {\n        size_t log_size;\n        if (check_nvrtc(nvrtcGetProgramLogSize(prog, &log_size))) {\n            std::vector<char> log(log_size);\n            if (check_nvrtc(nvrtcGetProgramLog(prog, log.data())))\n                fprintf(stderr, \"%s\", log.data());\n        }\n        nvrtcDestroyProgram(&prog);\n        return NULL;\n    }\n\n    // get output\n    char* output = NULL;\n    size_t output_size = 0;\n\n    if (use_ptx) {\n        check_nvrtc(nvrtcGetPTXSize(prog, &output_size));\n        if (output_size > 0) {\n            output = new char[output_size];\n            if (check_nvrtc(nvrtcGetPTX(prog, output)))\n                g_conditional_modules[key] = output;\n        }\n    } else {\n        check_nvrtc(nvrtcGetCUBINSize(prog, &output_size));\n        if (output_size > 0) {\n            output = new char[output_size];\n            if (check_nvrtc(nvrtcGetCUBIN(prog, output)))\n                g_conditional_modules[key] = output;\n        }\n    }\n\n    nvrtcDestroyProgram(&prog);\n\n    // return CUBIN or PTX data\n    return output;\n}\n\n\n// Load module with conditional helper kernels\nstatic CUmodule load_conditional_module(void* context, int arch, bool use_ptx)\n{\n    ContextInfo* context_info = get_context_info(context);\n    if (!context_info)\n        return NULL;\n\n    // check if already loaded\n    if (context_info->conditional_module)\n        return context_info->conditional_module;\n\n    // compile if needed\n    void* compiled_module = compile_conditional_module(arch, use_ptx);\n    if (!compiled_module) {\n        fprintf(stderr, \"Warp error: Failed to compile conditional kernels\\n\");\n        return NULL;\n    }\n\n    // load module (handles both PTX and CUBIN data automatically)\n    CUmodule module = NULL;\n    if (!check_cu(cuModuleLoadDataEx_f(&module, compiled_module, 0, NULL, NULL))) {\n        fprintf(stderr, \"Warp error: Failed to load conditional kernels module\\n\");\n        return NULL;\n    }\n\n    context_info->conditional_module = module;\n\n    return module;\n}\n\nstatic CUfunction get_conditional_kernel(void* context, int arch, bool use_ptx, const char* name)\n{\n    // load module if needed\n    CUmodule module = load_conditional_module(context, arch, use_ptx);\n    if (!module)\n        return NULL;\n\n    CUfunction kernel;\n    if (!check_cu(cuModuleGetFunction_f(&kernel, module, name))) {\n        fprintf(stderr, \"Warp error: Failed to get kernel %s\\n\", name);\n        return NULL;\n    }\n\n    return kernel;\n}\n\nbool wp_cuda_graph_pause_capture(void* context, void* stream, void** graph_ret)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    if (!check_cuda(cudaStreamEndCapture(cuda_stream, (cudaGraph_t*)graph_ret)))\n        return false;\n    return true;\n}\n\nbool wp_cuda_graph_resume_capture(void* context, void* stream, void* graph)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n    cudaGraph_t cuda_graph = static_cast<cudaGraph_t>(graph);\n\n    std::vector<cudaGraphNode_t> leaf_nodes;\n    if (!get_graph_leaf_nodes(cuda_graph, leaf_nodes))\n        return false;\n\n    // Resume with the same capture mode the user picked at begin time so a\n    // pause/resume cycle (driven by conditional/while graph nodes) does not\n    // silently downgrade Global/Relaxed captures to ThreadLocal. The stream\n    // must already be known to Warp with an active CaptureInfo at this\n    // point because the resume path is only reached after a matching pause\n    // on a Warp-managed capture; if either is missing something is badly\n    // out of sync, fail fast rather than guess a mode.\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: resume_capture called on unknown stream\");\n        return false;\n    }\n    CaptureInfo* capture = stream_info->capture;\n    if (!capture) {\n        wp::set_error_string(\"Warp error: resume_capture called on stream with no active capture\");\n        return false;\n    }\n    cudaStreamCaptureMode resume_mode = capture->mode;\n\n    if (!check_cuda(cudaStreamBeginCaptureToGraph(\n            cuda_stream, cuda_graph, leaf_nodes.data(), nullptr, leaf_nodes.size(), resume_mode\n        )))\n        return false;\n\n    return true;\n}\n\n// https://developer.nvidia.com/blog/constructing-cuda-graphs-with-dynamic-parameters/#combined_approach\n// https://developer.nvidia.com/blog/dynamic-control-flow-in-cuda-graphs-with-conditional-nodes/\n// condition is a gpu pointer\n// if_graph_ret and else_graph_ret should be NULL if not needed\nbool wp_cuda_graph_insert_if_else(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** if_graph_ret, void** else_graph_ret\n)\n{\n    bool has_if = if_graph_ret != NULL;\n    bool has_else = else_graph_ret != NULL;\n    int num_branches = int(has_if) + int(has_else);\n\n    // if neither the IF nor ELSE branches are required, it's a no-op\n    if (num_branches == 0)\n        return true;\n\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    // Get the current stream capturing graph\n    CUstreamCaptureStatus capture_status = CU_STREAM_CAPTURE_STATUS_NONE;\n    cudaGraph_t cuda_graph = NULL;\n    const cudaGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // abort if not capturing\n    if (!cuda_graph || capture_status != CU_STREAM_CAPTURE_STATUS_ACTIVE) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n    // int driver_version = wp_cuda_driver_version();\n\n    // IF-ELSE nodes are only supported with CUDA 12.8+\n    // Somehow child graphs produce wrong results when an else branch is used\n    // Seems to be a bug in the CUDA driver: https://nvbugs/5241330\n    if (num_branches == 1 /*|| driver_version >= 12080*/) {\n        cudaGraphConditionalHandle handle;\n        check_cuda(cudaGraphConditionalHandleCreate(&handle, cuda_graph));\n\n        // run a kernel to set the condition handle from the condition pointer\n        // (need to negate the condition if only the else branch is used)\n        CUfunction kernel;\n        if (has_if)\n            kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n        else\n            kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_else_handle_kernel\");\n\n        if (!kernel) {\n            wp::set_error_string(\"Failed to get built-in conditional kernel\");\n            return false;\n        }\n\n        void* kernel_args[2];\n        kernel_args[0] = &handle;\n        kernel_args[1] = &condition;\n\n        if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n            return false;\n\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        // create conditional node\n        CUgraphNode condition_node;\n        CUgraphNodeParams condition_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        condition_params.conditional.handle = handle;\n        condition_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        condition_params.conditional.size = num_branches;\n        condition_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&condition_node, cuda_graph, capture_deps, NULL, dep_count, &condition_params)))\n            return false;\n\n        if (!check_cu(\n                cuStreamUpdateCaptureDependencies_f(cuda_stream, &condition_node, 1, cudaStreamSetCaptureDependencies)\n            ))\n            return false;\n\n        if (num_branches == 1) {\n            if (has_if)\n                *if_graph_ret = condition_params.conditional.phGraph_out[0];\n            else\n                *else_graph_ret = condition_params.conditional.phGraph_out[0];\n        } else {\n            *if_graph_ret = condition_params.conditional.phGraph_out[0];\n            *else_graph_ret = condition_params.conditional.phGraph_out[1];\n        }\n    } else {\n        // Create IF node followed by an additional IF node with negated condition\n        cudaGraphConditionalHandle if_handle, else_handle;\n        check_cuda(cudaGraphConditionalHandleCreate(&if_handle, cuda_graph));\n        check_cuda(cudaGraphConditionalHandleCreate(&else_handle, cuda_graph));\n\n        CUfunction kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_else_handles_kernel\");\n        if (!kernel) {\n            wp::set_error_string(\"Failed to get built-in conditional kernel\");\n            return false;\n        }\n\n        void* kernel_args[3];\n        kernel_args[0] = &if_handle;\n        kernel_args[1] = &else_handle;\n        kernel_args[2] = &condition;\n\n        if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n            return false;\n\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        CUgraphNode if_node;\n        CUgraphNodeParams if_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        if_params.conditional.handle = if_handle;\n        if_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        if_params.conditional.size = 1;\n        if_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&if_node, cuda_graph, capture_deps, NULL, dep_count, &if_params)))\n            return false;\n\n        CUgraphNode else_node;\n        CUgraphNodeParams else_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        else_params.conditional.handle = else_handle;\n        else_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        else_params.conditional.size = 1;\n        else_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&else_node, cuda_graph, &if_node, NULL, 1, &else_params)))\n            return false;\n\n        if (!check_cu(\n                cuStreamUpdateCaptureDependencies_f(cuda_stream, &else_node, 1, cudaStreamSetCaptureDependencies)\n            ))\n            return false;\n\n        *if_graph_ret = if_params.conditional.phGraph_out[0];\n        *else_graph_ret = else_params.conditional.phGraph_out[0];\n    }\n\n    return true;\n}\n\n// graph node type names for intelligible error reporting\nstatic const char* get_graph_node_type_name(CUgraphNodeType type)\n{\n    static const std::unordered_map<CUgraphNodeType, const char*> names {\n        { CU_GRAPH_NODE_TYPE_KERNEL, \"kernel launch\" },\n        { CU_GRAPH_NODE_TYPE_MEMCPY, \"memcpy\" },\n        { CU_GRAPH_NODE_TYPE_MEMSET, \"memset\" },\n        { CU_GRAPH_NODE_TYPE_HOST, \"host execution\" },\n        { CU_GRAPH_NODE_TYPE_GRAPH, \"graph launch\" },\n        { CU_GRAPH_NODE_TYPE_EMPTY, \"empty node\" },\n        { CU_GRAPH_NODE_TYPE_WAIT_EVENT, \"event wait\" },\n        { CU_GRAPH_NODE_TYPE_EVENT_RECORD, \"event record\" },\n        { CU_GRAPH_NODE_TYPE_EXT_SEMAS_SIGNAL, \"semaphore signal\" },\n        { CU_GRAPH_NODE_TYPE_EXT_SEMAS_WAIT, \"semaphore wait\" },\n        { CU_GRAPH_NODE_TYPE_MEM_ALLOC, \"memory allocation\" },\n        { CU_GRAPH_NODE_TYPE_MEM_FREE, \"memory deallocation\" },\n        { CU_GRAPH_NODE_TYPE_BATCH_MEM_OP, \"batched mem op\" },\n        { CU_GRAPH_NODE_TYPE_CONDITIONAL, \"conditional node\" },\n    };\n\n    auto it = names.find(type);\n    if (it != names.end())\n        return it->second;\n    else\n        return \"unknown node\";\n}\n\n// check if a graph can be launched as a child graph\nstatic bool is_valid_child_graph(void* child_graph)\n{\n    // disallowed child graph nodes according to the documentation of cuGraphAddChildGraphNode()\n    static const std::unordered_set<CUgraphNodeType> disallowed_nodes {\n        CU_GRAPH_NODE_TYPE_MEM_ALLOC,\n        CU_GRAPH_NODE_TYPE_MEM_FREE,\n        CU_GRAPH_NODE_TYPE_CONDITIONAL,\n    };\n\n    if (!child_graph) {\n        wp::set_error_string(\"Child graph is null\");\n        return false;\n    }\n\n    size_t num_nodes = 0;\n    if (!check_cuda(cudaGraphGetNodes((cudaGraph_t)child_graph, NULL, &num_nodes)))\n        return false;\n    std::vector<cudaGraphNode_t> nodes(num_nodes);\n    if (!check_cuda(cudaGraphGetNodes((cudaGraph_t)child_graph, nodes.data(), &num_nodes)))\n        return false;\n\n    for (size_t i = 0; i < num_nodes; i++) {\n        // note: we use the driver API to get the node type, otherwise some nodes are not recognized correctly\n        CUgraphNodeType node_type;\n        check_cu(cuGraphNodeGetType_f(nodes[i], &node_type));\n        auto it = disallowed_nodes.find(node_type);\n        if (it != disallowed_nodes.end()) {\n            wp::set_error_string(\n                \"Child graph contains an unsupported operation (%s)\", get_graph_node_type_name(node_type)\n            );\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// check if a graph can be used as a conditional body graph\n// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#condtional-node-body-graph-requirements\nbool wp_cuda_graph_check_conditional_body(void* body_graph)\n{\n    static const std::unordered_set<CUgraphNodeType> allowed_nodes {\n        CU_GRAPH_NODE_TYPE_MEMCPY, CU_GRAPH_NODE_TYPE_MEMSET, CU_GRAPH_NODE_TYPE_KERNEL,\n        CU_GRAPH_NODE_TYPE_GRAPH,  CU_GRAPH_NODE_TYPE_EMPTY,  CU_GRAPH_NODE_TYPE_CONDITIONAL,\n    };\n\n    if (!body_graph) {\n        wp::set_error_string(\"Conditional body graph is null\");\n        return false;\n    }\n\n    size_t num_nodes = 0;\n    if (!check_cuda(cudaGraphGetNodes((cudaGraph_t)body_graph, NULL, &num_nodes)))\n        return false;\n    std::vector<cudaGraphNode_t> nodes(num_nodes);\n    if (!check_cuda(cudaGraphGetNodes((cudaGraph_t)body_graph, nodes.data(), &num_nodes)))\n        return false;\n\n    for (size_t i = 0; i < num_nodes; i++) {\n        // note: we use the driver API to get the node type, otherwise some nodes are not recognized correctly\n        CUgraphNodeType node_type;\n        check_cu(cuGraphNodeGetType_f(nodes[i], &node_type));\n        if (allowed_nodes.find(node_type) == allowed_nodes.end()) {\n            wp::set_error_string(\n                \"Conditional body graph contains an unsupported operation (%s)\", get_graph_node_type_name(node_type)\n            );\n            return false;\n        } else if (node_type == CU_GRAPH_NODE_TYPE_GRAPH) {\n            // check nested child graphs recursively\n            cudaGraph_t child_graph = NULL;\n            if (!check_cuda(cudaGraphChildGraphNodeGetGraph(nodes[i], &child_graph)))\n                return false;\n            if (!wp_cuda_graph_check_conditional_body(child_graph))\n                return false;\n        }\n    }\n\n    return true;\n}\n\nbool wp_cuda_graph_insert_child_graph(void* context, void* stream, void* child_graph)\n{\n    if (!is_valid_child_graph(child_graph))\n        return false;\n\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    // Get the current stream capturing graph\n    CUstreamCaptureStatus capture_status = CU_STREAM_CAPTURE_STATUS_NONE;\n    void* cuda_graph = NULL;\n    const CUgraphNode* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(\n            cuda_stream, &capture_status, nullptr, (cudaGraph_t*)&cuda_graph, &capture_deps, &dep_count\n        )))\n        return false;\n\n    if (!wp_cuda_graph_pause_capture(context, cuda_stream, &cuda_graph))\n        return false;\n\n    cudaGraphNode_t body_node;\n    if (!check_cuda(cudaGraphAddChildGraphNode(\n            &body_node, static_cast<cudaGraph_t>(cuda_graph), capture_deps, dep_count,\n            static_cast<cudaGraph_t>(child_graph)\n        )))\n        return false;\n\n    if (!wp_cuda_graph_resume_capture(context, cuda_stream, cuda_graph))\n        return false;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &body_node, 1, cudaStreamSetCaptureDependencies)))\n        return false;\n\n    return true;\n}\n\nbool wp_cuda_graph_insert_while(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** body_graph_ret, uint64_t* handle_ret\n)\n{\n    // if there's no body, it's a no-op\n    if (!body_graph_ret)\n        return true;\n\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    // Get the current stream capturing graph\n    CUstreamCaptureStatus capture_status = CU_STREAM_CAPTURE_STATUS_NONE;\n    cudaGraph_t cuda_graph = NULL;\n    const cudaGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // abort if not capturing\n    if (!cuda_graph || capture_status != CU_STREAM_CAPTURE_STATUS_ACTIVE) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n    cudaGraphConditionalHandle handle;\n    if (!check_cuda(cudaGraphConditionalHandleCreate(&handle, cuda_graph)))\n        return false;\n\n    // launch a kernel to set the condition handle from condition pointer\n    CUfunction kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n    if (!kernel) {\n        wp::set_error_string(\"Failed to get built-in conditional kernel\");\n        return false;\n    }\n\n    void* kernel_args[2];\n    kernel_args[0] = &handle;\n    kernel_args[1] = &condition;\n\n    if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n        return false;\n\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // insert conditional graph node\n    CUgraphNode while_node;\n    CUgraphNodeParams while_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n    while_params.conditional.handle = handle;\n    while_params.conditional.type = CU_GRAPH_COND_TYPE_WHILE;\n    while_params.conditional.size = 1;\n    while_params.conditional.ctx = get_current_context();\n    if (!check_cu(cuGraphAddNode_f(&while_node, cuda_graph, capture_deps, NULL, dep_count, &while_params)))\n        return false;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &while_node, 1, cudaStreamSetCaptureDependencies)))\n        return false;\n\n    *body_graph_ret = while_params.conditional.phGraph_out[0];\n    *handle_ret = handle;\n\n    return true;\n}\n\nbool wp_cuda_graph_set_condition(void* context, void* stream, int arch, bool use_ptx, int* condition, uint64_t handle)\n{\n    ContextGuard guard(context);\n\n    CUstream cuda_stream = static_cast<CUstream>(stream);\n\n    // launch a kernel to set the condition handle from condition pointer\n    CUfunction kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n    if (!kernel) {\n        wp::set_error_string(\"Failed to get built-in conditional kernel\");\n        return false;\n    }\n\n    void* kernel_args[2];\n    kernel_args[0] = &handle;\n    kernel_args[1] = &condition;\n\n    if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n        return false;\n\n    return true;\n}\n\n#else\n// stubs for conditional graph node API if CUDA toolkit is too old.\n\nbool wp_cuda_graph_pause_capture(void* context, void* stream, void** graph_ret)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_resume_capture(void* context, void* stream, void* graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_if_else(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** if_graph_ret, void** else_graph_ret\n)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_while(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** body_graph_ret, uint64_t* handle_ret\n)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_set_condition(void* context, void* stream, int arch, bool use_ptx, int* condition, uint64_t handle)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_child_graph(void* context, void* stream, void* child_graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_check_conditional_body(void* body_graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\n#endif  // support for conditional graph nodes\n\n\nbool wp_cuda_graph_launch(void* graph_exec, void* stream)\n{\n    // TODO: allow naming graphs?\n    begin_cuda_range(WP_TIMING_GRAPH, stream, get_stream_context(stream), \"graph\");\n\n    bool result = check_cuda(cudaGraphLaunch((cudaGraphExec_t)graph_exec, (cudaStream_t)stream));\n\n    end_cuda_range(WP_TIMING_GRAPH, stream);\n\n    return result;\n}\n\nbool wp_cuda_graph_destroy(void* context, void* graph)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        return check_cuda(cudaGraphDestroy((cudaGraph_t)graph));\n    } else {\n        GraphDestroyInfo info;\n        info.context = context ? context : get_current_context();\n        info.graph = graph;\n        g_deferred_graph_list.push_back(info);\n        return true;\n    }\n}\n\nbool wp_cuda_graph_exec_destroy(void* context, void* graph_exec)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        return check_cuda(cudaGraphExecDestroy((cudaGraphExec_t)graph_exec));\n    } else {\n        GraphDestroyInfo info;\n        info.context = context ? context : get_current_context();\n        info.graph_exec = graph_exec;\n        g_deferred_graph_list.push_back(info);\n        return true;\n    }\n}\n\nbool write_file(const char* data, size_t size, std::string filename, const char* mode)\n{\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n    if (print_debug) {\n        printf(\"Writing %zu B to %s (%s)\\n\", size, filename.c_str(), mode);\n    }\n    FILE* file = fopen(filename.c_str(), mode);\n    if (file) {\n        if (fwrite(data, 1, size, file) != size) {\n            fprintf(stderr, \"Warp error: Failed to write to output file '%s'\\n\", filename.c_str());\n            return false;\n        }\n        fclose(file);\n        return true;\n    } else {\n        fprintf(stderr, \"Warp error: Failed to open file '%s'\\n\", filename.c_str());\n        return false;\n    }\n}\n\n#if WP_ENABLE_MATHDX\nbool check_nvjitlink_result(nvJitLinkHandle handle, nvJitLinkResult result, const char* file, int line)\n{\n    if (result != NVJITLINK_SUCCESS) {\n        fprintf(stderr, \"nvJitLink error: %d on %s:%d\\n\", (int)result, file, line);\n        size_t lsize;\n        result = nvJitLinkGetErrorLogSize(handle, &lsize);\n        if (result == NVJITLINK_SUCCESS && lsize > 0) {\n            std::vector<char> log(lsize);\n            result = nvJitLinkGetErrorLog(handle, log.data());\n            if (result == NVJITLINK_SUCCESS) {\n                fprintf(stderr, \"%s\\n\", log.data());\n            }\n        }\n        return false;\n    } else {\n        return true;\n    }\n}\n#endif\n\nsize_t wp_cuda_compile_program(\n    const char* cuda_src,\n    const char* program_name,\n    int arch,\n    const char* arch_suffix,\n    const char* include_dir,\n    int num_cuda_include_dirs,\n    const char** cuda_include_dirs,\n    bool debug,\n    int optimization_level,\n    bool verbose,\n    bool verify_fp,\n    bool fast_math,\n    bool fuse_fp,\n    bool lineinfo,\n    bool compile_time_trace,\n    bool precompiled_headers,\n    const char* output_path,\n    const char* pch_dir,\n    size_t num_ltoirs,\n    char** ltoirs,\n    size_t* ltoir_sizes,\n    int* ltoir_input_types\n)\n{\n    // use file extension to determine whether to output PTX or CUBIN\n    const char* output_ext = strrchr(output_path, '.');\n    bool use_ptx = output_ext && strcmp(output_ext + 1, \"ptx\") == 0;\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n\n    // check include dir path len (path + option)\n    const int max_path = 4096 + 16;\n    if (strlen(include_dir) > max_path) {\n        fprintf(stderr, \"Warp error: Include path too long\\n\");\n        return size_t(-1);\n    }\n\n    if (print_debug) {\n        // Not available in all nvJitLink versions\n        // unsigned major = 0;\n        // unsigned minor = 0;\n        // nvJitLinkVersion(&major, &minor);\n        // printf(\"nvJitLink version %d.%d\\n\", major, minor);\n        int major = 0;\n        int minor = 0;\n        nvrtcVersion(&major, &minor);\n        printf(\"NVRTC version %d.%d\\n\", major, minor);\n    }\n\n    char include_opt[max_path];\n    strcpy(include_opt, \"--include-path=\");\n    strcat(include_opt, include_dir);\n\n    const int max_arch = 128;\n    char arch_opt[max_arch];\n    char arch_opt_lto[max_arch];\n\n    // arch_suffix is \"\" (no suffix), \"a\" (arch-specific), or \"f\" (family-specific)\n    const char* suffix = (arch_suffix != nullptr) ? arch_suffix : \"\";\n\n    if (use_ptx) {\n        snprintf(arch_opt, max_arch, \"--gpu-architecture=compute_%d%s\", arch, suffix);\n        snprintf(arch_opt_lto, max_arch, \"-arch=compute_%d%s\", arch, suffix);\n    } else {\n        snprintf(arch_opt, max_arch, \"--gpu-architecture=sm_%d%s\", arch, suffix);\n        snprintf(arch_opt_lto, max_arch, \"-arch=sm_%d%s\", arch, suffix);\n    }\n\n    std::vector<const char*> opts;\n    opts.push_back(arch_opt);\n    opts.push_back(include_opt);\n    opts.push_back(\"--std=c++17\");\n\n    // CUDA 12.9+ supports --Ofast-compile\n#if CUDA_VERSION >= 12090\n    // --Ofast-compile works inversely to normal -O optimization levels\n    switch (optimization_level) {\n    case 0:\n        opts.push_back(\"--Ofast-compile=max\");\n        break;\n    case 1:\n        opts.push_back(\"--Ofast-compile=mid\");\n        break;\n    case 2:\n        opts.push_back(\"--Ofast-compile=min\");\n        break;\n    default:\n        opts.push_back(\"--Ofast-compile=0\");\n        break;  // 3 and up\n    }\n#endif\n\n    // Vector to store dynamically created option strings\n    std::vector<std::string> stored_options;\n\n    if (precompiled_headers) {\n        // CUDA 12.8+ supports precompiled headers\n#if CUDA_VERSION >= 12080\n        opts.push_back(\"-pch\");\n#if CUDA_VERSION < 13000\n        // CUDA 12.x series puts .pch files in the current working directory unless explicitly set\n        if (pch_dir != nullptr) {\n            if (print_debug) {\n                printf(\"PCH directory: %s\\n\", pch_dir);\n            }\n            std::string pch_dir_opt = std::string(\"--pch-dir=\") + pch_dir;\n            stored_options.push_back(pch_dir_opt);\n            opts.push_back(stored_options.back().c_str());\n        }\n#endif\n#endif\n    }\n\n    if (debug) {\n        opts.push_back(\"--define-macro=_DEBUG\");\n        opts.push_back(\"--generate-line-info\");\n#ifndef _WIN32\n        opts.push_back(\"--device-debug\");  // -G\n#endif\n    } else {\n        opts.push_back(\"--define-macro=NDEBUG\");\n\n        if (lineinfo)\n            opts.push_back(\"--generate-line-info\");\n    }\n\n    if (verify_fp)\n        opts.push_back(\"--define-macro=WP_VERIFY_FP\");\n    else\n        opts.push_back(\"--undefine-macro=WP_VERIFY_FP\");\n\n#if WP_ENABLE_MATHDX\n    opts.push_back(\"--define-macro=WP_ENABLE_MATHDX=1\");\n#else\n    opts.push_back(\"--define-macro=WP_ENABLE_MATHDX=0\");\n#endif\n\n    if (fast_math)\n        opts.push_back(\"--use_fast_math\");\n\n    if (fuse_fp)\n        opts.push_back(\"--fmad=true\");\n    else\n        opts.push_back(\"--fmad=false\");\n\n    for (int i = 0; i < num_cuda_include_dirs; i++) {\n        stored_options.push_back(std::string(\"--include-path=\") + cuda_include_dirs[i]);\n        opts.push_back(stored_options.back().c_str());\n    }\n\n    opts.push_back(\"--device-as-default-execution-space\");\n    opts.push_back(\"--extra-device-vectorization\");\n    opts.push_back(\"--restrict\");\n    opts.push_back(\"--diag-suppress=177,550\");  // \"was declared but never referenced\", \"was set but never used\"\n\n    if (num_ltoirs > 0) {\n        opts.push_back(\"-dlto\");\n        opts.push_back(\"--relocatable-device-code=true\");\n    }\n\n    if (compile_time_trace) {\n#if CUDA_VERSION >= 12080\n        stored_options.push_back(\n            std::string(\"--fdevice-time-trace=\") + std::string(output_path).append(\"_compile-time-trace.json\")\n        );\n        opts.push_back(stored_options.back().c_str());\n#else\n        fprintf(stderr, \"Warp warning: CUDA version is less than 12.8, compile_time_trace is not supported\\n\");\n#endif\n    }\n\n    nvrtcProgram prog;\n    nvrtcResult res;\n\n    res = nvrtcCreateProgram(\n        &prog,  // prog\n        cuda_src,  // buffer\n        program_name,  // name\n        0,  // numHeaders\n        NULL,  // headers\n        NULL\n    );  // includeNames\n\n    if (!check_nvrtc(res))\n        return size_t(res);\n\n    if (print_debug) {\n        printf(\"NVRTC options:\\n\");\n        for (auto o : opts) {\n            printf(\"%s\\n\", o);\n        }\n    }\n    res = nvrtcCompileProgram(prog, int(opts.size()), opts.data());\n\n    if (!check_nvrtc(res) || verbose) {\n        // get program log\n        size_t log_size;\n        if (check_nvrtc(nvrtcGetProgramLogSize(prog, &log_size))) {\n            std::vector<char> log(log_size);\n            if (check_nvrtc(nvrtcGetProgramLog(prog, log.data()))) {\n                // todo: figure out better way to return this to python\n                if (res != NVRTC_SUCCESS)\n                    fprintf(stderr, \"%s\", log.data());\n                else\n                    fprintf(stdout, \"%s\", log.data());\n            }\n        }\n\n        if (res != NVRTC_SUCCESS) {\n            nvrtcDestroyProgram(&prog);\n            return size_t(res);\n        }\n    }\n\n    nvrtcResult (*get_output_size)(nvrtcProgram, size_t*);\n    nvrtcResult (*get_output_data)(nvrtcProgram, char*);\n    const char* output_mode;\n    if (num_ltoirs > 0) {\n#if WP_ENABLE_MATHDX\n        get_output_size = nvrtcGetLTOIRSize;\n        get_output_data = nvrtcGetLTOIR;\n        output_mode = \"wb\";\n#else\n        fprintf(stderr, \"Warp error: num_ltoirs > 0 but Warp was not built with MathDx support\\n\");\n        return size_t(-1);\n#endif\n    } else if (use_ptx) {\n        get_output_size = nvrtcGetPTXSize;\n        get_output_data = nvrtcGetPTX;\n        output_mode = \"wt\";\n    } else {\n        get_output_size = nvrtcGetCUBINSize;\n        get_output_data = nvrtcGetCUBIN;\n        output_mode = \"wb\";\n    }\n\n    // save output\n    size_t output_size;\n    res = get_output_size(prog, &output_size);\n    if (check_nvrtc(res)) {\n        std::vector<char> output(output_size);\n        res = get_output_data(prog, output.data());\n        if (check_nvrtc(res)) {\n\n            // LTOIR case - need an extra step\n            if (num_ltoirs > 0) {\n#if WP_ENABLE_MATHDX\n                if (ltoir_input_types == nullptr || ltoirs == nullptr || ltoir_sizes == nullptr) {\n                    fprintf(\n                        stderr, \"Warp error: num_ltoirs > 0 but ltoir_input_types, ltoirs or ltoir_sizes are NULL\\n\"\n                    );\n                    return size_t(-1);\n                }\n                nvJitLinkHandle handle = nullptr;\n                std::vector<const char*> lopts = { \"-dlto\", arch_opt_lto };\n                if (use_ptx) {\n                    lopts.push_back(\"-ptx\");\n                }\n                if (print_debug) {\n                    printf(\"nvJitLink options:\\n\");\n                    for (auto o : lopts) {\n                        printf(\"%s\\n\", o);\n                    }\n                }\n                if (!check_nvjitlink(handle, nvJitLinkCreate(&handle, lopts.size(), lopts.data()))) {\n                    res = nvrtcResult(-1);\n                }\n                // Links\n                if (std::getenv(\"WARP_DUMP_LTOIR\")) {\n                    write_file(output.data(), output.size(), \"nvrtc_output.ltoir\", \"wb\");\n                }\n                if (!check_nvjitlink(\n                        handle,\n                        nvJitLinkAddData(handle, NVJITLINK_INPUT_LTOIR, output.data(), output.size(), \"nvrtc_output\")\n                    ))  // NVRTC business\n                {\n                    res = nvrtcResult(-1);\n                }\n                for (size_t ltoidx = 0; ltoidx < num_ltoirs; ltoidx++) {\n                    nvJitLinkInputType input_type = static_cast<nvJitLinkInputType>(ltoir_input_types[ltoidx]);\n                    const char* ext = \".unknown\";\n                    switch (input_type) {\n                    case NVJITLINK_INPUT_CUBIN:\n                        ext = \".cubin\";\n                        break;\n                    case NVJITLINK_INPUT_LTOIR:\n                        ext = \".ltoir\";\n                        break;\n                    case NVJITLINK_INPUT_FATBIN:\n                        ext = \".fatbin\";\n                        break;\n                    default:\n                        break;\n                    }\n                    if (std::getenv(\"WARP_DUMP_LTOIR\")) {\n                        write_file(\n                            ltoirs[ltoidx], ltoir_sizes[ltoidx],\n                            std::string(\"lto_online_\") + std::to_string(ltoidx) + ext, \"wb\"\n                        );\n                    }\n                    if (!check_nvjitlink(\n                            handle,\n                            nvJitLinkAddData(handle, input_type, ltoirs[ltoidx], ltoir_sizes[ltoidx], \"lto_online\")\n                        ))  // External LTOIR\n                    {\n                        res = nvrtcResult(-1);\n                    }\n                }\n                if (!check_nvjitlink(handle, nvJitLinkComplete(handle))) {\n                    res = nvrtcResult(-1);\n                } else {\n                    if (use_ptx) {\n                        size_t ptx_size = 0;\n                        check_nvjitlink(handle, nvJitLinkGetLinkedPtxSize(handle, &ptx_size));\n                        std::vector<char> ptx(ptx_size);\n                        check_nvjitlink(handle, nvJitLinkGetLinkedPtx(handle, ptx.data()));\n                        output = ptx;\n                    } else {\n                        size_t cubin_size = 0;\n                        check_nvjitlink(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n                        std::vector<char> cubin(cubin_size);\n                        check_nvjitlink(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n                        output = cubin;\n                    }\n                }\n                check_nvjitlink(handle, nvJitLinkDestroy(&handle));\n#else\n                fprintf(stderr, \"Warp error: num_ltoirs > 0 but Warp was not built with MathDx support\\n\");\n                return size_t(-1);\n#endif\n            }\n\n            if (!write_file(output.data(), output.size(), output_path, output_mode)) {\n                res = nvrtcResult(-1);\n            }\n        }\n    }\n\n    check_nvrtc(nvrtcDestroyProgram(&prog));\n\n    return res;\n}\n\n#if WP_ENABLE_MATHDX\nbool check_cufftdx_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuFFTDx error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool check_cublasdx_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuBLASDx error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool check_cusolver_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuSOLVER error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool wp_cuda_compile_fft(\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int size,\n    int elements_per_thread,\n    int direction,\n    int precision,\n    int* shared_memory_size\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    CHECK_ANY(shared_memory_size != nullptr);\n    // Includes currently unused\n    CHECK_ANY(include_dirs == nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n\n    bool res = true;\n    cufftdxDescriptor h;\n    CHECK_CUFFTDX(cufftdxCreateDescriptor(&h));\n\n    // CUFFTDX_API_LMEM means each thread starts with a subset of the data\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_API, cufftdxApi::CUFFTDX_API_LMEM));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(\n        h, cufftdxOperatorType::CUFFTDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_SIZE, (long long)size));\n    CHECK_CUFFTDX(\n        cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_DIRECTION, (cufftdxDirection)direction)\n    );\n    CHECK_CUFFTDX(\n        cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_PRECISION, (commondxPrecision)precision)\n    );\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_SM, (long long)(arch * 10)));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(\n        h, cufftdxOperatorType::CUFFTDX_OPERATOR_ELEMENTS_PER_THREAD, (long long)(elements_per_thread)\n    ));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_FFTS_PER_BLOCK, 1));\n\n    CHECK_CUFFTDX(cufftdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUFFTDX(cufftdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUFFTDX(cufftdxGetLTOIR(h, lto.size(), lto.data()));\n\n    long long int smem = 0;\n    CHECK_CUFFTDX(cufftdxGetTraitInt64(h, cufftdxTraitType::CUFFTDX_TRAIT_SHARED_MEMORY_SIZE, &smem));\n    *shared_memory_size = (int)smem;\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUFFTDX(cufftdxDestroyDescriptor(h));\n\n    return res;\n}\n\nbool wp_cuda_compile_dot(\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int M,\n    int N,\n    int K,\n    int precision_A,\n    int precision_B,\n    int precision_C,\n    int type,\n    int arrangement_A,\n    int arrangement_B,\n    int arrangement_C,\n    int num_threads\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    // Includes currently unused\n    CHECK_ANY(include_dirs == nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n\n    bool res = true;\n    cublasdxDescriptor h;\n    CHECK_CUBLASDX(cublasdxCreateDescriptor(&h));\n\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_FUNCTION, cublasdxFunction::CUBLASDX_FUNCTION_MM\n    ));\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_API, cublasdxApi::CUBLASDX_API_SMEM)\n    );\n    std::array<long long int, 3> precisions = { precision_A, precision_B, precision_C };\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64s(h, cublasdxOperatorType::CUBLASDX_OPERATOR_PRECISION, 3, precisions.data())\n    );\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_SM, (long long)(arch * 10)));\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_TYPE, (cublasdxType)type));\n    std::array<long long int, 3> block_dim = { num_threads, 1, 1 };\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64s(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_BLOCK_DIM, block_dim.size(), block_dim.data()\n    ));\n    std::array<long long int, 3> size = { M, N, K };\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64s(h, cublasdxOperatorType::CUBLASDX_OPERATOR_SIZE, size.size(), size.data())\n    );\n    std::array<long long int, 3> arrangement = { arrangement_A, arrangement_B, arrangement_C };\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64s(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_ARRANGEMENT, arrangement.size(), arrangement.data()\n    ));\n\n    CHECK_CUBLASDX(cublasdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUBLASDX(cublasdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUBLASDX(cublasdxGetLTOIR(h, lto.size(), lto.data()));\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUBLASDX(cublasdxDestroyDescriptor(h));\n\n    return res;\n}\n\nbool wp_cuda_compile_solver(\n    const char* fatbin_output_path,\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int M,\n    int N,\n    int K,\n    int function,\n    int side,\n    int diag,\n    int precision,\n    int arrangement_A,\n    int arrangement_B,\n    int fill_mode,\n    int num_threads\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n    CHECK_ANY(include_dirs == nullptr);\n\n    bool res = true;\n\n    cusolverdxDescriptor h { 0 };\n    CHECK_CUSOLVER(cusolverdxCreateDescriptor(&h));\n    std::array<long long int, 3> size = { M, N, K };\n    CHECK_CUSOLVER(\n        cusolverdxSetOperatorInt64s(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SIZE, size.size(), size.data())\n    );\n    std::array<long long int, 3> block_dim = { num_threads, 1, 1 };\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64s(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_BLOCK_DIM, block_dim.size(), block_dim.data()\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_TYPE, cusolverdxType::CUSOLVERDX_TYPE_REAL\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_API, cusolverdxApi::CUSOLVERDX_API_SMEM\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_FUNCTION, (cusolverdxFunction)function\n    ));\n    if (side >= 0) {\n        CHECK_CUSOLVER(\n            cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SIDE, (cusolverdxSide)side)\n        );\n    }\n    if (diag >= 0) {\n        CHECK_CUSOLVER(\n            cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_DIAG, (cusolverdxDiag)diag)\n        );\n    }\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_PRECISION, (commondxPrecision)precision\n    ));\n    std::array<long long int, 2> arrangement = { arrangement_A, arrangement_B };\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64s(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_ARRANGEMENT, arrangement.size(), arrangement.data()\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_FILL_MODE, (cusolverdxFillMode)fill_mode\n    ));\n    CHECK_CUSOLVER(\n        cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SM, (long long)(arch * 10))\n    );\n\n    CHECK_CUSOLVER(cusolverdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUSOLVER(cusolverdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUSOLVER(cusolverdxGetLTOIR(h, lto.size(), lto.data()));\n\n    // This fatbin is universal, ie it is the same for any instantiations of a cusolver device function\n    size_t fatbin_size = 0;\n    CHECK_CUSOLVER(cusolverdxGetUniversalFATBINSize(h, &fatbin_size));\n\n    std::vector<char> fatbin(fatbin_size);\n    CHECK_CUSOLVER(cusolverdxGetUniversalFATBIN(h, fatbin.size(), fatbin.data()));\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    if (!write_file(fatbin.data(), fatbin.size(), fatbin_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUSOLVER(cusolverdxDestroyDescriptor(h));\n\n    return res;\n}\n\n#endif\n\nvoid* wp_cuda_load_module(void* context, const char* path)\n{\n    ContextGuard guard(context);\n\n    // use file extension to determine whether to load PTX or CUBIN\n    const char* input_ext = strrchr(path, '.');\n    bool load_ptx = input_ext && strcmp(input_ext + 1, \"ptx\") == 0;\n\n    std::vector<char> input;\n\n    FILE* file = fopen(path, \"rb\");\n    if (file) {\n        fseek(file, 0, SEEK_END);\n        size_t length = ftell(file);\n        fseek(file, 0, SEEK_SET);\n\n        input.resize(length + 1);\n        if (fread(input.data(), 1, length, file) != length) {\n            fprintf(stderr, \"Warp error: Failed to read input file '%s'\\n\", path);\n            fclose(file);\n            return NULL;\n        }\n        fclose(file);\n\n        input[length] = '\\0';\n    } else {\n        fprintf(stderr, \"Warp error: Failed to open input file '%s'\\n\", path);\n        return NULL;\n    }\n\n    int driver_cuda_version = 0;\n    CUmodule module = NULL;\n\n    if (load_ptx) {\n        if (check_cu(cuDriverGetVersion_f(&driver_cuda_version)) && driver_cuda_version >= CUDA_VERSION) {\n            // let the driver compile the PTX\n\n            CUjit_option options[2];\n            void* option_vals[2];\n            char error_log[8192] = \"\";\n            unsigned int log_size = 8192;\n            // Set up loader options\n            // Pass a buffer for error message\n            options[0] = CU_JIT_ERROR_LOG_BUFFER;\n            option_vals[0] = (void*)error_log;\n            // Pass the size of the error buffer\n            options[1] = CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES;\n            option_vals[1] = (void*)(size_t)log_size;\n\n            if (!check_cu(cuModuleLoadDataEx_f(&module, input.data(), 2, options, option_vals))) {\n                fprintf(stderr, \"Warp error: Loading PTX module failed\\n\");\n                // print error log if not empty\n                if (*error_log)\n                    fprintf(stderr, \"PTX loader error:\\n%s\\n\", error_log);\n                return NULL;\n            }\n        } else {\n            // manually compile the PTX and load as CUBIN\n\n            ContextInfo* context_info = get_context_info(static_cast<CUcontext>(context));\n            if (!context_info || !context_info->device_info) {\n                fprintf(stderr, \"Warp error: Failed to determine target architecture\\n\");\n                return NULL;\n            }\n\n            int arch = context_info->device_info->arch;\n\n            char arch_opt[128];\n            sprintf(arch_opt, \"--gpu-name=sm_%d\", arch);\n\n            const char* compiler_options[] = { arch_opt };\n\n            nvPTXCompilerHandle compiler = NULL;\n            if (!check_nvptx(nvPTXCompilerCreate(&compiler, input.size(), input.data())))\n                return NULL;\n\n            if (!check_nvptx(nvPTXCompilerCompile(\n                    compiler, sizeof(compiler_options) / sizeof(*compiler_options), compiler_options\n                )))\n                return NULL;\n\n            size_t cubin_size = 0;\n            if (!check_nvptx(nvPTXCompilerGetCompiledProgramSize(compiler, &cubin_size)))\n                return NULL;\n\n            std::vector<char> cubin(cubin_size);\n            if (!check_nvptx(nvPTXCompilerGetCompiledProgram(compiler, cubin.data())))\n                return NULL;\n\n            check_nvptx(nvPTXCompilerDestroy(&compiler));\n\n            if (!check_cu(cuModuleLoadDataEx_f(&module, cubin.data(), 0, NULL, NULL))) {\n                fprintf(stderr, \"Warp CUDA error: Loading module failed\\n\");\n                return NULL;\n            }\n        }\n    } else {\n        // load CUBIN\n        if (!check_cu(cuModuleLoadDataEx_f(&module, input.data(), 0, NULL, NULL))) {\n            fprintf(stderr, \"Warp CUDA error: Loading module failed\\n\");\n            return NULL;\n        }\n    }\n\n    return module;\n}\n\nvoid wp_cuda_unload_module(void* context, void* module)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        check_cu(cuModuleUnload_f((CUmodule)module));\n    } else {\n        // defer until graph capture completes\n        ModuleInfo module_info;\n        module_info.context = context ? context : get_current_context();\n        module_info.module = module;\n        g_deferred_module_list.push_back(module_info);\n    }\n}\n\n\nint wp_cuda_get_max_shared_memory(void* context)\n{\n    ContextInfo* info = get_context_info(context);\n    if (!info)\n        return -1;\n\n    int max_smem_bytes = info->device_info->max_smem_bytes;\n    return max_smem_bytes;\n}\n\nbool wp_cuda_configure_kernel_shared_memory(void* kernel, int size)\n{\n    int requested_smem_bytes = size;\n\n    // configure shared memory\n    CUresult res = cuFuncSetAttribute_f(\n        (CUfunction)kernel, CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, requested_smem_bytes\n    );\n    if (res != CUDA_SUCCESS)\n        return false;\n\n    return true;\n}\n\nvoid* wp_cuda_get_kernel(void* context, void* module, const char* name)\n{\n    ContextGuard guard(context);\n\n    CUfunction kernel = NULL;\n    if (!check_cu(cuModuleGetFunction_f(&kernel, (CUmodule)module, name))) {\n        fprintf(stderr, \"Warp CUDA error: Failed to lookup kernel function %s in module\\n\", name);\n        return NULL;\n    }\n\n    g_kernel_names[kernel] = name;\n    return kernel;\n}\n\nsize_t wp_cuda_launch_kernel(\n    void* context,\n    void* kernel,\n    size_t dim,\n    int max_blocks,\n    int block_dim,\n    int shared_memory_bytes,\n    void** args,\n    void* stream,\n    const APICLaunchInfo* apic_info\n)\n{\n    ContextGuard guard(context);\n\n    if (block_dim <= 0) {\n#if defined(_DEBUG)\n        fprintf(stderr, \"Warp warning: Launch got block_dim %d. Setting to 256.\\n\", block_dim);\n#endif\n        block_dim = 256;\n    }\n\n    // CUDA specs up to compute capability 9.0 says the max x-dim grid is 2**31-1, so\n    // grid_dim is fine as an int for the near future\n    int grid_dim = (dim + block_dim - 1) / block_dim;\n\n    if (max_blocks <= 0) {\n        max_blocks = 2147483647;\n    }\n\n    if (grid_dim < 0) {\n#if defined(_DEBUG)\n        fprintf(\n            stderr,\n            \"Warp warning: Overflow in grid dimensions detected for %zu total elements and 256 threads \"\n            \"per block.\\n    Setting block count to %d.\\n\",\n            dim, max_blocks\n        );\n#endif\n        grid_dim = max_blocks;\n    } else {\n        if (grid_dim > max_blocks) {\n            grid_dim = max_blocks;\n        }\n    }\n\n    begin_cuda_range(WP_TIMING_KERNEL, stream, context, get_cuda_kernel_name(kernel));\n\n    CUresult res = cuLaunchKernel_f(\n        (CUfunction)kernel, grid_dim, 1, 1, block_dim, 1, 1, shared_memory_bytes, static_cast<CUstream>(stream), args, 0\n    );\n\n    check_cu(res);\n\n    end_cuda_range(WP_TIMING_KERNEL, stream);\n\n    // APIC recording: record kernel launch to byte stream if capturing\n    if (apic_info) {\n        APICState state = wp_apic_get_recording_state();\n        if (state) {\n            // Read shape/ndim/size from the launch_bounds_t* in args[0] (see builtin.h).\n            int shape[APIC_LAUNCH_MAX_DIMS] = {};\n            int ndim = 0;\n            uint64_t launch_size = dim;\n            if (args && args[0]) {\n                const auto* lb = static_cast<const wp::launch_bounds_t*>(args[0]);\n                ndim = lb->ndim;\n                if (ndim < 1)\n                    ndim = 1;\n                if (ndim > APIC_LAUNCH_MAX_DIMS)\n                    ndim = APIC_LAUNCH_MAX_DIMS;\n                for (int d = 0; d < ndim; d++)\n                    shape[d] = lb->shape[d];\n                launch_size = lb->size;\n            } else {\n                ndim = 1;\n                shape[0] = (int)dim;\n            }\n\n            apic_record_kernel_launch(\n                state, apic_info->kernel_key, apic_info->module_hash, apic_info->is_forward, shape, ndim, launch_size,\n                max_blocks, block_dim, shared_memory_bytes, apic_info->params, apic_info->num_params\n            );\n        }\n    }\n\n    return res;\n}\n\nbool wp_cuda_get_suggested_block_size(\n    void* context, void* kernel, int shared_memory_bytes, int* block_size_out, int* min_grid_size_out\n)\n{\n    ContextGuard guard(context);\n\n    int min_grid_size = 0;\n    int block_size = 0;\n    CUresult res = cuOccupancyMaxPotentialBlockSize_f(\n        &min_grid_size, &block_size, (CUfunction)kernel, NULL, shared_memory_bytes, 0\n    );\n\n    if (!check_cu(res))\n        return false;\n\n    *block_size_out = block_size;\n    *min_grid_size_out = min_grid_size;\n    return true;\n}\n\nbool wp_cuda_graphics_map(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    return check_cu(cuGraphicsMapResources_f(1, (CUgraphicsResource*)resource, get_current_stream()));\n}\n\nvoid wp_cuda_graphics_unmap(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    check_cu(cuGraphicsUnmapResources_f(1, (CUgraphicsResource*)resource, get_current_stream()));\n}\n\nvoid wp_cuda_graphics_device_ptr_and_size(void* context, void* resource, uint64_t* ptr, size_t* size)\n{\n    ContextGuard guard(context);\n\n    CUdeviceptr device_ptr;\n    size_t bytes;\n    check_cu(cuGraphicsResourceGetMappedPointer_f(&device_ptr, &bytes, *(CUgraphicsResource*)resource));\n\n    *ptr = device_ptr;\n    *size = bytes;\n}\n\nvoid* wp_cuda_graphics_register_gl_buffer(void* context, uint32_t gl_buffer, unsigned int flags)\n{\n    ContextGuard guard(context);\n\n    CUgraphicsResource* resource = new CUgraphicsResource;\n    bool success = check_cu(cuGraphicsGLRegisterBuffer_f(resource, gl_buffer, flags));\n    if (!success) {\n        delete resource;\n        return NULL;\n    }\n\n    return resource;\n}\n\nvoid* wp_cuda_graphics_register_gl_image(void* context, uint32_t image, uint32_t target, unsigned int flags)\n{\n    ContextGuard guard(context);\n\n    CUgraphicsResource* resource = new CUgraphicsResource;\n    bool success = check_cu(cuGraphicsGLRegisterImage_f(resource, image, target, flags));\n    if (!success) {\n        delete resource;\n        return NULL;\n    }\n\n    return resource;\n}\n\nuint64_t wp_cuda_graphics_sub_resource_get_mapped_array(\n    void* context, void* resource, unsigned int array_index, unsigned int mip_level\n)\n{\n    ContextGuard guard(context);\n\n    CUarray cuda_array = NULL;\n    check_cu(\n        cuGraphicsSubResourceGetMappedArray_f(&cuda_array, *(CUgraphicsResource*)resource, array_index, mip_level)\n    );\n    return reinterpret_cast<uint64_t>(cuda_array);\n}\n\nvoid wp_cuda_graphics_unregister_resource(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    CUgraphicsResource* res = (CUgraphicsResource*)resource;\n    check_cu(cuGraphicsUnregisterResource_f(*res));\n    delete res;\n}\n\nvoid wp_cuda_timing_begin(int flags) { g_cuda_timing_state = new CudaTimingState(flags, g_cuda_timing_state); }\n\nint wp_cuda_timing_get_result_count()\n{\n    if (g_cuda_timing_state)\n        return int(g_cuda_timing_state->ranges.size());\n    return 0;\n}\n\nvoid wp_cuda_timing_end(timing_result_t* results, int size)\n{\n    if (!g_cuda_timing_state)\n        return;\n\n    // number of results to write to the user buffer\n    int count = std::min(wp_cuda_timing_get_result_count(), size);\n\n    // compute timings and write results\n    for (int i = 0; i < count; i++) {\n        const CudaTimingRange& range = g_cuda_timing_state->ranges[i];\n        timing_result_t& result = results[i];\n        result.context = range.context;\n        result.name = range.name;\n        result.flag = range.flag;\n        check_cuda(cudaEventElapsedTime(&result.elapsed, range.start, range.end));\n    }\n\n    // release events\n    for (CudaTimingRange& range : g_cuda_timing_state->ranges) {\n        check_cu(cuEventDestroy_f(range.start));\n        check_cu(cuEventDestroy_f(range.end));\n    }\n\n    // restore previous state\n    CudaTimingState* parent_state = g_cuda_timing_state->parent;\n    delete g_cuda_timing_state;\n    g_cuda_timing_state = parent_state;\n}\n\n// #include \"spline.inl\"\n// #include \"volume.inl\"\n\n// APIC (API Capture) implementation\n#include \"apic.cu\"\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"alloc_tracker.h\"\n#include \"apic.h\"\n#include \"cuda_util.h\"\n#include \"error.h\"\n#include \"scan.h\"\n#include \"sort.h\"\n\n#include <cstdlib>\n#include <fstream>\n\n#include <nvPTXCompiler.h>\n#include <hip/hiprtc.h>\n#if WP_ENABLE_MATHDX\n#include <libcublasdx.h>\n#include <libcufftdx.h>\n#include <libcusolverdx.h>\n#include <libmathdx.h>\n#include <nvJitLink.h>\n#endif\n\n#include <algorithm>\n#include <array>\n#include <iterator>\n#include <list>\n#include <map>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#define check_any(result) (check_generic(result, __FILE__, __LINE__))\n#define check_nvrtc(code) (check_nvrtc_result(code, __FILE__, __LINE__))\n#define check_nvptx(code) (check_nvptx_result(code, __FILE__, __LINE__))\n#define check_nvjitlink(handle, code) (check_nvjitlink_result(handle, code, __FILE__, __LINE__))\n#define check_cufftdx(code) (check_cufftdx_result(code, __FILE__, __LINE__))\n#define check_cublasdx(code) (check_cublasdx_result(code, __FILE__, __LINE__))\n#define check_cusolver(code) (check_cusolver_result(code, __FILE__, __LINE__))\n#define CHECK_ANY(code) \\\n{ \\\n    do { \\\n        bool out = (check_any(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUFFTDX(code) \\\n{ \\\n    do { \\\n        bool out = (check_cufftdx(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUBLASDX(code) \\\n{ \\\n    do { \\\n        bool out = (check_cufftdx(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n#define CHECK_CUSOLVER(code) \\\n{ \\\n    do { \\\n        bool out = (check_cusolver(code)); \\\n        if(!out) { \\\n            return out; \\\n        } \\\n    } while(0); \\\n}\n\nbool check_nvrtc_result(hiprtcResult result, const char* file, int line)\n{\n    if (result == HIPRTC_SUCCESS)\n        return true;\n\n    const char* error_string = hiprtcGetErrorString(result);\n    fprintf(stderr, \"Warp NVRTC compilation error %u: %s (%s:%d)\\n\", unsigned(result), error_string, file, line);\n    return false;\n}\n\nbool check_nvptx_result(nvPTXCompileResult result, const char* file, int line)\n{\n    if (result == NVPTXCOMPILE_SUCCESS)\n        return true;\n\n    const char* error_string;\n    switch (result) {\n    case NVPTXCOMPILE_ERROR_INVALID_COMPILER_HANDLE:\n        error_string = \"Invalid compiler handle\";\n        break;\n    case NVPTXCOMPILE_ERROR_INVALID_INPUT:\n        error_string = \"Invalid input\";\n        break;\n    case NVPTXCOMPILE_ERROR_COMPILATION_FAILURE:\n        error_string = \"Compilation failure\";\n        break;\n    case NVPTXCOMPILE_ERROR_INTERNAL:\n        error_string = \"Internal error\";\n        break;\n    case NVPTXCOMPILE_ERROR_OUT_OF_MEMORY:\n        error_string = \"Out of memory\";\n        break;\n    case NVPTXCOMPILE_ERROR_COMPILER_INVOCATION_INCOMPLETE:\n        error_string = \"Incomplete compiler invocation\";\n        break;\n    case NVPTXCOMPILE_ERROR_UNSUPPORTED_PTX_VERSION:\n        error_string = \"Unsupported PTX version\";\n        break;\n    default:\n        error_string = \"Unknown error\";\n        break;\n    }\n\n    fprintf(stderr, \"Warp PTX compilation error %u: %s (%s:%d)\\n\", unsigned(result), error_string, file, line);\n    return false;\n}\n\nbool check_generic(int result, const char* file, int line)\n{\n    if (!result) {\n        fprintf(stderr, \"Error %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nstruct DeviceInfo {\n    static constexpr int kNameLen = 128;\n\n    hipDevice_t device = -1;\n    hipUUID uuid = { 0 };\n    int ordinal = -1;\n    int pci_domain_id = -1;\n    int pci_bus_id = -1;\n    int pci_device_id = -1;\n    char name[kNameLen] = \"\";\n    int arch = 0;\n    int is_uva = 0;\n    int is_mempool_supported = 0;\n    int sm_count = 0;\n    int is_ipc_supported = -1;\n    int max_smem_bytes = 0;\n    hipCtx_t primary_context = NULL;\n};\n\nstruct ContextInfo {\n    DeviceInfo* device_info = NULL;\n\n    // the current stream, managed from Python (see wp_cuda_context_set_stream() and wp_cuda_context_get_stream())\n    hipStream_t stream = NULL;\n\n    // conditional graph node support, loaded on demand if the driver supports it (CUDA 12.4+)\n    hipModule_t conditional_module = NULL;\n};\n\n// Information used for freeing allocations.\nstruct FreeInfo {\n    void* context = NULL;\n    void* ptr = NULL;\n    bool is_async = false;\n};\n\nstruct CaptureInfo {\n    hipStream_t stream = NULL;  // the main stream where capture begins and ends\n    uint64_t id = 0;  // unique capture id from CUDA\n    bool external = false;  // whether this is an external capture\n    hipStreamCaptureMode mode = hipStreamCaptureModeThreadLocal;  // mode used to open the capture (for pause/resume)\n    std::vector<FreeInfo> tmp_allocs;  // temporary allocations owned by the graph (e.g., staged array fill values)\n};\n\nstruct StreamInfo {\n    hipEvent_t cached_event = NULL;  // event used for stream synchronization (cached to avoid creating temporary events)\n    CaptureInfo* capture = NULL;  // capture info (only if started on this stream)\n};\n\n// Extra resources tied to a graph, freed after the graph is released by CUDA.\n// Used with the on_graph_destroy() callback.\nstruct GraphDestroyCallbackInfo {\n    void* context = NULL;  // graph CUDA context\n    std::vector<void*> unfreed_allocs;  // graph allocations not freed by the graph\n    std::vector<FreeInfo> tmp_allocs;  // temporary allocations owned by the graph (e.g., staged array fill values)\n};\n\n// Information for graph allocations that are not freed by the graph.\n// These allocations have a shared ownership:\n// - The graph instance allocates/maps the memory on each launch, even if the user reference is released.\n// - The user reference must remain valid even if the graph is destroyed.\n// The memory will be freed once the user reference is released and the graph is destroyed.\nstruct GraphAllocInfo {\n    uint64_t capture_id = 0;\n    void* context = NULL;\n    bool ref_exists = false;  // whether user reference still exists\n    bool graph_destroyed = false;  // whether graph instance was destroyed\n};\n\n// Information used when deferring module unloading.\nstruct ModuleInfo {\n    void* context = NULL;\n    void* module = NULL;\n};\n\n// Information used when deferring graph destruction.\nstruct GraphDestroyInfo {\n    void* context = NULL;\n    void* graph = NULL;\n    void* graph_exec = NULL;\n};\n\nstatic std::unordered_map<hipFunction_t, std::string> g_kernel_names;\n\n// cached info for all devices, indexed by ordinal\nstatic std::vector<DeviceInfo> g_devices;\n\n// maps CUdevice to DeviceInfo\nstatic std::map<hipDevice_t, DeviceInfo*> g_device_map;\n\n// cached info for all known contexts\nstatic std::map<hipCtx_t, ContextInfo> g_contexts;\n\n// cached info for all known streams (including registered external streams)\nstatic std::unordered_map<hipStream_t, StreamInfo> g_streams;\n\n// Ongoing graph captures registered using wp.capture_begin().\n// This maps the capture id to the stream where capture was started.\n// See wp_cuda_graph_begin_capture(), wp_cuda_graph_end_capture(), and wp_free_device_async().\nstatic std::unordered_map<uint64_t, CaptureInfo*> g_captures;\n\n// Memory allocated during graph capture requires special handling.\n// See wp_alloc_device_async() and wp_free_device_async().\nstatic std::unordered_map<void*, GraphAllocInfo> g_graph_allocs;\n\n// Memory that cannot be freed immediately gets queued here.\n// Call free_deferred_allocs() to release.\nstatic std::vector<FreeInfo> g_deferred_free_list;\n\n// Modules that cannot be unloaded immediately get queued here.\n// Call unload_deferred_modules() to release.\nstatic std::vector<ModuleInfo> g_deferred_module_list;\n\n// Graphs that cannot be destroyed immediately get queued here.\n// Call destroy_deferred_graphs() to release.\nstatic std::vector<GraphDestroyInfo> g_deferred_graph_list;\n\n// Data from on_graph_destroy() callbacks that run on a different thread.\nstatic std::vector<GraphDestroyCallbackInfo*> g_deferred_graph_destroy_list;\nstatic std::mutex g_graph_destroy_mutex;\n\n\nvoid wp_cuda_set_context_restore_policy(bool always_restore) { ContextGuard::always_restore = always_restore; }\n\nint wp_cuda_get_context_restore_policy() { return int(ContextGuard::always_restore); }\n\nint cuda_init()\n{\n    if (!init_cuda_driver())\n        return -1;\n\n    int device_count = 0;\n    if (check_cu(cuDeviceGetCount_f(&device_count))) {\n        g_devices.resize(device_count);\n\n        for (int i = 0; i < device_count; i++) {\n            hipDevice_t device;\n            if (check_cu(cuDeviceGet_f(&device, i))) {\n                // query device info\n                g_devices[i].device = device;\n                g_devices[i].ordinal = i;\n                check_cu(cuDeviceGetName_f(g_devices[i].name, DeviceInfo::kNameLen, device));\n                check_cu(cuDeviceGetUuid_f(&g_devices[i].uuid, device));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].pci_domain_id, hipDeviceAttributePciDomainId, device)\n                );\n                check_cu(cuDeviceGetAttribute_f(&g_devices[i].pci_bus_id, hipDeviceAttributePciBusId, device));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].pci_device_id, hipDeviceAttributePciDeviceId, device)\n                );\n                check_cu(cuDeviceGetAttribute_f(&g_devices[i].is_uva, hipDeviceAttributeUnifiedAddressing, device));\n                check_cu(cuDeviceGetAttribute_f(\n                    &g_devices[i].is_mempool_supported, hipDeviceAttributeMemoryPoolsSupported, device\n                ));\n                check_cu(\n                    cuDeviceGetAttribute_f(&g_devices[i].sm_count, hipDeviceAttributeMultiprocessorCount, device)\n                );\n#ifdef CUDA_VERSION\n#if CUDA_VERSION >= 12000\n                int device_attribute_integrated = 0;\n                check_cu(cuDeviceGetAttribute_f(&device_attribute_integrated, hipDeviceAttributeIntegrated, device));\n                if (device_attribute_integrated == 0) {\n                    check_cu(cuDeviceGetAttribute_f(\n                        &g_devices[i].is_ipc_supported, CU_DEVICE_ATTRIBUTE_IPC_EVENT_SUPPORTED, device\n                    ));\n                } else {\n                    // integrated devices do not support CUDA IPC\n                    g_devices[i].is_ipc_supported = 0;\n                }\n#endif\n#endif\n                check_cu(cuDeviceGetAttribute_f(\n                    &g_devices[i].max_smem_bytes, hipDeviceAttributeSharedMemPerBlockOptin, device\n                ));\n                int major = 0;\n                int minor = 0;\n                check_cu(cuDeviceGetAttribute_f(&major, hipDeviceAttributeComputeCapabilityMajor, device));\n                check_cu(cuDeviceGetAttribute_f(&minor, hipDeviceAttributeComputeCapabilityMinor, device));\n                g_devices[i].arch = 10 * major + minor;\n#ifdef CUDA_VERSION\n#if CUDA_VERSION < 13000\n                if (g_devices[i].arch == 110) {\n                    g_devices[i].arch = 101;  // Thor SM change\n                }\n#endif\n#endif\n                g_device_map[device] = &g_devices[i];\n            } else {\n                return -1;\n            }\n        }\n    } else {\n        return -1;\n    }\n\n    // initialize default timing state\n    static CudaTimingState default_timing_state(0, NULL);\n    g_cuda_timing_state = &default_timing_state;\n\n    return 0;\n}\n\n\nhipCtx_t get_current_context()\n{\n    hipCtx_t ctx;\n    if (check_cu(cuCtxGetCurrent_f(&ctx)))\n        return ctx;\n    else\n        return NULL;\n}\n\nstatic inline hipStream_t get_current_stream(void* context = NULL)\n{\n    return static_cast<hipStream_t>(wp_cuda_context_get_stream(context));\n}\n\nstatic ContextInfo* get_context_info(hipCtx_t ctx)\n{\n    if (!ctx) {\n        ctx = get_current_context();\n        if (!ctx)\n            return NULL;\n    }\n\n    auto it = g_contexts.find(ctx);\n    if (it != g_contexts.end()) {\n        return &it->second;\n    } else {\n        // previously unseen context, add the info\n        ContextGuard guard(ctx, true);\n\n        hipDevice_t device;\n        if (check_cu(cuCtxGetDevice_f(&device))) {\n            DeviceInfo* device_info = g_device_map[device];\n\n            // workaround for https://nvbugspro.nvidia.com/bug/4456003\n            if (device_info->is_mempool_supported) {\n                void* dummy = NULL;\n                check_cuda(hipMallocAsync(&dummy, 1, NULL));\n                check_cuda(hipFreeAsync(dummy, NULL));\n            }\n\n            ContextInfo context_info;\n            context_info.device_info = device_info;\n            auto result = g_contexts.insert(std::make_pair(ctx, context_info));\n            return &result.first->second;\n        }\n    }\n\n    return NULL;\n}\n\nstatic inline ContextInfo* get_context_info(void* context) { return get_context_info(static_cast<hipCtx_t>(context)); }\n\nstatic inline StreamInfo* get_stream_info(hipStream_t stream)\n{\n    auto it = g_streams.find(stream);\n    if (it != g_streams.end())\n        return &it->second;\n    else\n        return NULL;\n}\n\nstatic inline CaptureInfo* get_capture_info(hipStream_t stream)\n{\n    if (!g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        uint64_t capture_id = get_capture_id(stream);\n        auto capture_iter = g_captures.find(capture_id);\n        if (capture_iter != g_captures.end())\n            return capture_iter->second;\n    }\n    return NULL;\n}\n\n// helper function to copy a value to device memory in a graph-friendly way\nstatic bool capturable_tmp_alloc(void* context, const void* data, size_t size, void** devptr_ret, bool* free_devptr_ret)\n{\n    ContextGuard guard(context);\n\n    hipStream_t stream = get_current_stream();\n    CaptureInfo* capture_info = get_capture_info(stream);\n    int device_ordinal = wp_cuda_context_get_device_ordinal(context);\n    void* devptr = NULL;\n    bool free_devptr = true;\n\n    if (capture_info) {\n        // ongoing graph capture - need to stage the fill value so that it persists with the graph\n        if (CUDA_VERSION >= 12040 && wp_cuda_driver_version() >= 12040) {\n            // pause the capture so that the alloc/memcpy won't be captured\n            void* graph = NULL;\n            if (!wp_cuda_graph_pause_capture(WP_CURRENT_CONTEXT, stream, &graph))\n                return false;\n\n            // copy value to device memory\n            devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n            if (!devptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                    (unsigned long long)size, device_ordinal, __FUNCTION__\n                );\n                return false;\n            }\n            if (!check_cuda(hipMemcpyAsync(devptr, data, size, hipMemcpyHostToDevice, stream)))\n                return false;\n\n            // graph takes ownership of the value storage\n            FreeInfo free_info;\n            free_info.context = context ? context : get_current_context();\n            free_info.ptr = devptr;\n            free_info.is_async = wp_cuda_device_is_mempool_supported(device_ordinal);\n\n            // allocation will be freed when graph is destroyed\n            capture_info->tmp_allocs.push_back(free_info);\n\n            // resume the capture\n            if (!wp_cuda_graph_resume_capture(WP_CURRENT_CONTEXT, stream, graph))\n                return false;\n\n            free_devptr = false;  // memory is owned by the graph, doesn't need to be freed\n        } else {\n            // older CUDA can't pause/resume the capture, so stage in CPU memory\n            void* hostptr = wp_alloc_host(size);\n            if (!hostptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cpu' (in function %s)\\n\",\n                    (unsigned long long)size, __FUNCTION__\n                );\n                return false;\n            }\n            memcpy(hostptr, data, size);\n\n            // the device allocation and h2d copy will be captured in the graph\n            devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n            if (!devptr) {\n                fprintf(\n                    stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                    (unsigned long long)size, device_ordinal, __FUNCTION__\n                );\n                return false;\n            }\n            if (!check_cuda(hipMemcpyAsync(devptr, hostptr, size, hipMemcpyHostToDevice, stream)))\n                return false;\n\n            // graph takes ownership of the value storage\n            FreeInfo free_info;\n            free_info.context = NULL;\n            free_info.ptr = hostptr;\n            free_info.is_async = false;\n\n            // allocation will be freed when graph is destroyed\n            capture_info->tmp_allocs.push_back(free_info);\n        }\n    } else {\n        // not capturing, copy the value to device memory\n        devptr = wp_alloc_device(WP_CURRENT_CONTEXT, size);\n        if (!devptr) {\n            fprintf(\n                stderr, \"Warp error: Failed to allocate %llu bytes on device 'cuda:%d' (in function %s)\\n\",\n                (unsigned long long)size, device_ordinal, __FUNCTION__\n            );\n            return false;\n        }\n        if (!check_cuda(hipMemcpyAsync(devptr, data, size, hipMemcpyHostToDevice, stream)))\n            return false;\n    }\n\n    *devptr_ret = devptr;\n    *free_devptr_ret = free_devptr;\n\n    return true;\n}\n\nstatic void deferred_free(void* ptr, void* context, bool is_async)\n{\n    FreeInfo free_info;\n    free_info.ptr = ptr;\n    free_info.context = context ? context : get_current_context();\n    free_info.is_async = is_async;\n    g_deferred_free_list.push_back(free_info);\n}\n\nstatic int free_deferred_allocs(void* context = NULL)\n{\n    if (g_deferred_free_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_freed_allocs = 0;\n    for (auto it = g_deferred_free_list.begin(); it != g_deferred_free_list.end(); /*noop*/) {\n        const FreeInfo& free_info = *it;\n\n        // free the pointer if it matches the given context or if the context is unspecified\n        if (free_info.context == context || !context) {\n            ContextGuard guard(free_info.context);\n\n            if (free_info.is_async) {\n                // this could be a regular stream-ordered allocation or a graph allocation\n                hipError_t res = hipFreeAsync(free_info.ptr, NULL);\n                if (res != hipSuccess) {\n                    if (res == hipErrorInvalidValue) {\n                        // This can happen if we try to release the pointer but the graph was\n                        // never launched, so the memory isn't mapped.\n                        // This is fine, so clear the error.\n                        hipGetLastError();\n                    } else {\n                        // something else went wrong, report error\n                        check_cuda(res);\n                    }\n                }\n            } else {\n                check_cuda(hipFree(free_info.ptr));\n            }\n\n            ++num_freed_allocs;\n\n            it = g_deferred_free_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_freed_allocs;\n}\n\nstatic int unload_deferred_modules(void* context = NULL)\n{\n    if (g_deferred_module_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_unloaded_modules = 0;\n    for (auto it = g_deferred_module_list.begin(); it != g_deferred_module_list.end(); /*noop*/) {\n        // free the module if it matches the given context or if the context is unspecified\n        const ModuleInfo& module_info = *it;\n        if (module_info.context == context || !context) {\n            wp_cuda_unload_module(module_info.context, module_info.module);\n            ++num_unloaded_modules;\n            it = g_deferred_module_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_unloaded_modules;\n}\n\nstatic int destroy_deferred_graphs(void* context = NULL)\n{\n    if (g_deferred_graph_list.empty() || !g_captures.empty())\n        return 0;\n\n    int num_destroyed_graphs = 0;\n    for (auto it = g_deferred_graph_list.begin(); it != g_deferred_graph_list.end(); /*noop*/) {\n        // destroy the graph if it matches the given context or if the context is unspecified\n        const GraphDestroyInfo& graph_info = *it;\n        if (graph_info.context == context || !context) {\n            if (graph_info.graph) {\n                check_cuda(hipGraphDestroy((hipGraph_t)graph_info.graph));\n            }\n            if (graph_info.graph_exec) {\n                check_cuda(hipGraphExecDestroy((hipGraphExec_t)graph_info.graph_exec));\n            }\n            ++num_destroyed_graphs;\n            it = g_deferred_graph_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_destroyed_graphs;\n}\n\nstatic int process_deferred_graph_destroy_callbacks(void* context = NULL)\n{\n    int num_freed = 0;\n\n    std::lock_guard<std::mutex> lock(g_graph_destroy_mutex);\n\n    for (auto it = g_deferred_graph_destroy_list.begin(); it != g_deferred_graph_destroy_list.end(); /*noop*/) {\n        GraphDestroyCallbackInfo* graph_info = *it;\n        if (graph_info->context == context || !context) {\n            // handle unfreed graph allocations (may have outstanding user references)\n            for (void* ptr : graph_info->unfreed_allocs) {\n                auto alloc_iter = g_graph_allocs.find(ptr);\n                if (alloc_iter != g_graph_allocs.end()) {\n                    GraphAllocInfo& alloc_info = alloc_iter->second;\n                    if (alloc_info.ref_exists) {\n                        // unreference from graph so the pointer will be deallocated when the user reference goes away\n                        alloc_info.graph_destroyed = true;\n                    } else {\n                        // the pointer can be freed, no references remain\n                        wp_free_device_async(alloc_info.context, ptr);\n                        g_graph_allocs.erase(alloc_iter);\n                    }\n                }\n            }\n\n            // handle temporary allocations owned by the graph (no user references)\n            for (const FreeInfo& tmp_info : graph_info->tmp_allocs) {\n                if (tmp_info.context) {\n                    // GPU alloc\n                    if (tmp_info.is_async) {\n                        wp_free_device_async(tmp_info.context, tmp_info.ptr);\n                    } else {\n                        wp_free_device_default(tmp_info.context, tmp_info.ptr);\n                    }\n                } else {\n                    // CPU alloc\n                    wp_free_host(tmp_info.ptr);\n                }\n            }\n\n            ++num_freed;\n            delete graph_info;\n            it = g_deferred_graph_destroy_list.erase(it);\n        } else {\n            ++it;\n        }\n    }\n\n    return num_freed;\n}\n\nstatic int run_deferred_actions(void* context = NULL)\n{\n    int num_actions = 0;\n    num_actions += free_deferred_allocs(context);\n    num_actions += unload_deferred_modules(context);\n    num_actions += destroy_deferred_graphs(context);\n    num_actions += process_deferred_graph_destroy_callbacks(context);\n    return num_actions;\n}\n\n// Callback used when a graph is destroyed.\n// NOTE: this runs on an internal CUDA thread and requires synchronization.\nstatic void CUDART_CB on_graph_destroy(void* user_data)\n{\n    if (user_data) {\n        std::lock_guard<std::mutex> lock(g_graph_destroy_mutex);\n        g_deferred_graph_destroy_list.push_back(static_cast<GraphDestroyCallbackInfo*>(user_data));\n    }\n}\n\nstatic inline const char* get_cuda_kernel_name(void* kernel)\n{\n    hipFunction_t cuda_func = static_cast<hipFunction_t>(kernel);\n    auto name_iter = g_kernel_names.find((hipFunction_t)cuda_func);\n    if (name_iter != g_kernel_names.end())\n        return name_iter->second.c_str();\n    else\n        return \"unknown_kernel\";\n}\n\n\nvoid* wp_alloc_pinned(size_t s, const char* tag)\n{\n    void* ptr = NULL;\n    check_cuda(hipHostMalloc(&ptr, s));\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_PINNED, -1, tag);\n    return ptr;\n}\n\nvoid wp_free_pinned(void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n    hipHostFree(ptr);\n}\n\nvoid* wp_alloc_device(void* context, size_t s, const char* tag)\n{\n    int ordinal = wp_cuda_context_get_device_ordinal(context);\n\n    if (wp_cuda_device_is_mempool_supported(ordinal))\n        return wp_alloc_device_async(context, s, tag);\n    else\n        return wp_alloc_device_default(context, s, tag);\n}\n\nvoid wp_free_device(void* context, void* ptr)\n{\n    int ordinal = wp_cuda_context_get_device_ordinal(context);\n\n    // use stream-ordered allocator if available\n    if (wp_cuda_device_is_mempool_supported(ordinal))\n        wp_free_device_async(context, ptr);\n    else\n        wp_free_device_default(context, ptr);\n}\n\nvoid* wp_alloc_device_default(void* context, size_t s, const char* tag)\n{\n    ContextGuard guard(context);\n\n    void* ptr = NULL;\n    check_cuda(hipMalloc(&ptr, s));\n\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_DEVICE, wp_cuda_context_get_device_ordinal(context), tag);\n    return ptr;\n}\n\nvoid wp_free_device_default(void* context, void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n\n    ContextGuard guard(context);\n\n    // check if a capture is in progress\n    if (g_captures.empty()) {\n        check_cuda(hipFree(ptr));\n    } else {\n        // we must defer the operation until graph captures complete\n        deferred_free(ptr, context, false);\n    }\n}\n\nvoid* wp_alloc_device_async(void* context, size_t s, const char* tag)\n{\n    // stream-ordered allocations don't rely on the current context,\n    // but we set the context here for consistent behaviour\n    ContextGuard guard(context);\n\n    ContextInfo* context_info = get_context_info(context);\n    if (!context_info)\n        return NULL;\n\n    hipStream_t stream = context_info->stream;\n\n    void* ptr = NULL;\n    check_cuda(hipMallocAsync(&ptr, s, stream));\n\n    if (ptr) {\n        // if the stream is capturing, the allocation requires special handling\n        if (wp_cuda_stream_is_capturing(stream)) {\n            // check if this is a known capture\n            uint64_t capture_id = get_capture_id(stream);\n            auto capture_iter = g_captures.find(capture_id);\n            if (capture_iter != g_captures.end()) {\n                // remember graph allocation details\n                GraphAllocInfo alloc_info;\n                alloc_info.capture_id = capture_id;\n                alloc_info.context = context ? context : get_current_context();\n                alloc_info.ref_exists = true;  // user reference created and returned here\n                alloc_info.graph_destroyed = false;  // graph not destroyed yet\n                g_graph_allocs[ptr] = alloc_info;\n            }\n        }\n    }\n\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_alloc(ptr, s, ALLOC_KIND_DEVICE, wp_cuda_context_get_device_ordinal(context), tag);\n    return ptr;\n}\n\nvoid wp_free_device_async(void* context, void* ptr)\n{\n    if (g_alloc_tracker.enabled && ptr)\n        g_alloc_tracker.record_free(ptr);\n\n    // stream-ordered allocators generally don't rely on the current context,\n    // but we set the context here for consistent behaviour\n    ContextGuard guard(context);\n\n    // NB: Stream-ordered deallocations are tricky, because the memory could still be used on another stream\n    // or even multiple streams.  To avoid use-after-free errors, we need to ensure that all preceding work\n    // completes before releasing the memory.  The strategy is different for regular stream-ordered allocations\n    // and allocations made during graph capture.  See below for details.\n\n    // check if this allocation was made during graph capture\n    auto alloc_iter = g_graph_allocs.find(ptr);\n    if (alloc_iter == g_graph_allocs.end()) {\n        // Not a graph allocation.\n        // Check if graph capture is ongoing.\n        if (g_captures.empty()) {\n            // cudaFreeAsync on the null stream does not block or trigger synchronization, but it postpones\n            // the deallocation until a synchronization point is reached, so preceding work on this pointer\n            // should safely complete.\n            check_cuda(hipFreeAsync(ptr, NULL));\n        } else {\n            // We must defer the free operation until graph capture completes.\n            deferred_free(ptr, context, true);\n        }\n    } else {\n        // get the graph allocation details\n        GraphAllocInfo& alloc_info = alloc_iter->second;\n\n        uint64_t capture_id = alloc_info.capture_id;\n\n        // check if the capture is still active\n        auto capture_iter = g_captures.find(capture_id);\n        if (capture_iter != g_captures.end()) {\n            // Add a mem free node.  Use all current leaf nodes as dependencies to ensure that all prior\n            // work completes before deallocating.  This works with both Warp-initiated and external captures\n            // and avoids the need to explicitly track all streams used during the capture.\n            CaptureInfo* capture = capture_iter->second;\n            hipGraph_t graph = get_capture_graph(capture->stream);\n            std::vector<hipGraphNode_t> leaf_nodes;\n            if (graph && get_graph_leaf_nodes(graph, leaf_nodes)) {\n                hipGraphNode_t free_node;\n                if (check_cuda(hipGraphAddMemFreeNode(&free_node, graph, leaf_nodes.data(), leaf_nodes.size(), ptr))) {\n                    check_cu(cuStreamUpdateCaptureDependencies_f(\n                        capture->stream, &free_node, 1, hipStreamSetCaptureDependencies\n                    ));\n                }\n            }\n\n            // we're done with this allocation, it's owned by the graph\n            g_graph_allocs.erase(alloc_iter);\n        } else {\n            // the capture has ended\n            // if the owning graph was already destroyed, we can free the pointer now\n            if (alloc_info.graph_destroyed) {\n                if (g_captures.empty()) {\n                    // try to free the pointer now\n                    hipError_t res = hipFreeAsync(ptr, NULL);\n                    if (res == hipErrorInvalidValue) {\n                        // This can happen if we try to release the pointer but the graph was\n                        // never launched, so the memory isn't mapped.\n                        // This is fine, so clear the error.\n                        hipGetLastError();\n                    } else {\n                        // check for other errors\n                        check_cuda(res);\n                    }\n                } else {\n                    // We must defer the operation until graph capture completes.\n                    deferred_free(ptr, context, true);\n                }\n\n                // we're done with this allocation\n                g_graph_allocs.erase(alloc_iter);\n            } else {\n                // graph still exists\n                // unreference the pointer so it will be deallocated once the graph instance is destroyed\n                alloc_info.ref_exists = false;\n            }\n        }\n    }\n}\n\nbool wp_memcpy_h2d(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy HtoD\");\n\n    bool result = check_cuda(hipMemcpyAsync(dest, src, n, hipMemcpyHostToDevice, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\nbool wp_memcpy_d2h(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy DtoH\");\n\n    bool result = check_cuda(hipMemcpyAsync(dest, src, n, hipMemcpyDeviceToHost, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\nbool wp_memcpy_d2d(void* context, void* dest, void* src, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy DtoD\");\n\n    bool result = check_cuda(hipMemcpyAsync(dest, src, n, hipMemcpyDeviceToDevice, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    // APIC recording.\n    // TODO: When execution becomes fully deferred (like the CPU path), move\n    // CUDA-result checking to replay time (capture_launch / load+launch) and\n    // make recording unconditional at capture time. For now we still execute\n    // the CUDA op live under stream capture, but the record itself is API-\n    // intent only and doesn't depend on the live call's result.\n    APICState apic_state = wp_apic_get_recording_state();\n    if (apic_state) {\n        int32_t dst_region, src_region;\n        uint64_t dst_offset, src_offset;\n        bool dst_ok = apic_resolve_ptr(apic_state, (uint64_t)dest, &dst_region, &dst_offset);\n        bool src_ok = apic_resolve_ptr(apic_state, (uint64_t)src, &src_region, &src_offset);\n        if (!dst_ok)\n            fprintf(stderr, \"APIC: Error - memcpy dst pointer not in any registered region\\n\");\n        if (!src_ok)\n            fprintf(stderr, \"APIC: Error - memcpy src pointer not in any registered region\\n\");\n        if (dst_ok && src_ok)\n            apic_record_memcpy_d2d(apic_state, dst_region, dst_offset, src_region, src_offset, n);\n    }\n\n    return result;\n}\n\nbool wp_memcpy_p2p(void* dst_context, void* dst, void* src_context, void* src, size_t n, void* stream)\n{\n    // ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream(dst_context);\n\n    // Notes:\n    // - cuMemcpyPeerAsync() works fine with both regular and pooled allocations (cudaMalloc() and cudaMallocAsync(),\n    // respectively)\n    //   when not capturing a graph.\n    // - cuMemcpyPeerAsync() is not supported during graph capture, so we must use cudaMemcpyAsync() with\n    // kind=cudaMemcpyDefault.\n    // - cudaMemcpyAsync() works fine with regular allocations, but doesn't work with pooled allocations\n    //   unless mempool access has been enabled.\n    // - There is no reliable way to check if mempool access is enabled during graph capture,\n    //   because cudaMemPoolGetAccess() cannot be called during graph capture.\n    // - CUDA will report error 1 (invalid argument) if cudaMemcpyAsync() is called but mempool access is not enabled.\n\n    if (!wp_cuda_stream_is_capturing(stream)) {\n        begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, get_stream_context(stream), \"memcpy PtoP\");\n\n        bool result = check_cu(cuMemcpyPeerAsync_f(\n            (hipDeviceptr_t)dst, (hipCtx_t)dst_context, (hipDeviceptr_t)src, (hipCtx_t)src_context, n, cuda_stream\n        ));\n\n        end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n        return result;\n    } else {\n        hipError_t result = hipSuccess;\n\n        // cudaMemcpyAsync() is sensitive to the bound context to resolve pointer locations.\n        // If fails with cudaErrorInvalidValue if it cannot resolve an argument.\n        // We first try the copy in the destination context, then if it fails we retry in the source context.\n        // The cudaErrorInvalidValue error doesn't cause graph capture to fail, so it's ok to retry.\n        // Since this trial-and-error shenanigans only happens during capture, there\n        // is no perf impact when the graph is launched.\n        // For bonus points, this approach simplifies memory pool access requirements.\n        // Access only needs to be enabled one way, either from the source device to the destination device\n        // or vice versa.  Sometimes, when it's really quiet, you can actually hear my genius.\n        {\n            // try doing the copy in the destination context\n            ContextGuard guard(dst_context);\n            result = hipMemcpyAsync(dst, src, n, hipMemcpyDefault, cuda_stream);\n\n            if (result != hipSuccess) {\n                // clear error in destination context\n                hipGetLastError();\n\n                // try doing the copy in the source context\n                ContextGuard guard(src_context);\n                result = hipMemcpyAsync(dst, src, n, hipMemcpyDefault, cuda_stream);\n\n                // clear error in source context\n                hipGetLastError();\n            }\n        }\n\n        // If the copy failed, try to detect if mempool allocations are involved to generate a helpful error message.\n        if (!check_cuda(result)) {\n            if (result == hipErrorInvalidValue && src != NULL && dst != NULL) {\n                // check if either of the pointers was allocated from a mempool\n                void* src_mempool = NULL;\n                void* dst_mempool = NULL;\n                cuPointerGetAttribute_f(&src_mempool, HIP_POINTER_ATTRIBUTE_MEMPOOL_HANDLE, (hipDeviceptr_t)src);\n                cuPointerGetAttribute_f(&dst_mempool, HIP_POINTER_ATTRIBUTE_MEMPOOL_HANDLE, (hipDeviceptr_t)dst);\n                hipGetLastError();  // clear any errors\n                // check if either of the pointers was allocated during graph capture\n                auto src_alloc = g_graph_allocs.find(src);\n                auto dst_alloc = g_graph_allocs.find(dst);\n                if (src_mempool != NULL || src_alloc != g_graph_allocs.end() || dst_mempool != NULL\n                    || dst_alloc != g_graph_allocs.end()) {\n                    wp::append_error_string(\n                        \"*** CUDA mempool allocations were used in a peer-to-peer copy during graph capture.\"\n                    );\n                    wp::append_error_string(\n                        \"*** This operation fails if mempool access is not enabled between the peer devices.\"\n                    );\n                    wp::append_error_string(\n                        \"*** Either enable mempool access between the devices or use the default CUDA allocator\"\n                    );\n                    wp::append_error_string(\"*** to pre-allocate the arrays before graph capture begins.\");\n                }\n            }\n\n            return false;\n        }\n\n        return true;\n    }\n}\n\nbool wp_memcpy_batch(void* context, void** dsts, void** srcs, size_t* sizes, size_t count, void* stream)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream(context);\n\n    begin_cuda_range(WP_TIMING_MEMCPY, cuda_stream, context, \"memcpy batch\");\n\n    bool result = true;\n\n#if CUDA_VERSION >= 12080\n    if (wp_cuda_driver_version() >= 12080) {\n        hipMemcpyAttributes attr = {};\n        attr.srcAccessOrder = hipMemcpySrcAccessOrderStream;\n        // attr.flags = CU_MEMCPY_FLAG_PREFER_OVERLAP_WITH_COMPUTE;\n        size_t attr_idx = 0;\n        size_t fail_idx = 0;\n        result = check_cuda(cuMemcpyBatchAsync_f(\n            (hipDeviceptr_t*)dsts, (hipDeviceptr_t*)srcs, sizes, count, &attr, &attr_idx, 1, &fail_idx, cuda_stream\n        ));\n    } else {\n        for (size_t i = 0; i < count; i++)\n            result = result && check_cuda(hipMemcpyAsync(dsts[i], srcs[i], sizes[i], hipMemcpyDefault, cuda_stream));\n    }\n#else\n    for (size_t i = 0; i < count; i++)\n        result = result && check_cuda(hipMemcpyAsync(dsts[i], srcs[i], sizes[i], hipMemcpyDefault, cuda_stream));\n#endif\n\n    end_cuda_range(WP_TIMING_MEMCPY, cuda_stream);\n\n    return result;\n}\n\n\n__global__ void memset_kernel(int* dest, int value, size_t n)\n{\n    const size_t tid\n        = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n\n    if (tid < n) {\n        dest[tid] = value;\n    }\n}\n\nbool wp_memset_device(void* context, void* dest, int value, size_t n, void* stream)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream;\n    if (stream != WP_CURRENT_STREAM)\n        cuda_stream = static_cast<hipStream_t>(stream);\n    else\n        cuda_stream = get_current_stream();\n\n    begin_cuda_range(WP_TIMING_MEMSET, cuda_stream, context, \"memset\");\n\n    bool result = check_cuda(hipMemsetAsync(dest, value, n, cuda_stream));\n\n    end_cuda_range(WP_TIMING_MEMSET, cuda_stream);\n\n    // APIC recording.\n    // TODO: When execution becomes fully deferred (like the CPU path), move\n    // CUDA-result checking to replay time (capture_launch / load+launch) and\n    // make recording unconditional at capture time. For now we still execute\n    // the CUDA op live under stream capture, but the record itself is API-\n    // intent only and doesn't depend on the live call's result.\n    APICState apic_state = wp_apic_get_recording_state();\n    if (apic_state) {\n        int32_t region_id;\n        uint64_t offset;\n        if (apic_resolve_ptr(apic_state, (uint64_t)dest, &region_id, &offset))\n            apic_record_memset(apic_state, region_id, offset, n, value);\n        else\n            fprintf(stderr, \"APIC: Error - memset dst pointer not in any registered region\\n\");\n    }\n    return result;\n}\n\n// fill memory buffer with a value: generic memtile kernel using memcpy for each element\n__global__ void memtile_kernel(void* dst, const void* src, size_t srcsize, size_t n)\n{\n    size_t tid = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n    if (tid < n) {\n        memcpy((int8_t*)dst + srcsize * tid, src, srcsize);\n    }\n}\n\n// this should be faster than memtile_kernel, but requires proper alignment of dst\ntemplate <typename T> __global__ void memtile_value_kernel(T* dst, T value, size_t n)\n{\n    size_t tid = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n    if (tid < n) {\n        dst[tid] = value;\n    }\n}\n\nvoid wp_memtile_device(void* context, void* dst, const void* src, size_t srcsize, size_t n)\n{\n    ContextGuard guard(context);\n\n    size_t dst_addr = reinterpret_cast<size_t>(dst);\n    size_t src_addr = reinterpret_cast<size_t>(src);\n\n    // try memtile_value first because it should be faster, but we need to ensure proper alignment\n    if (srcsize == 8 && (dst_addr & 7) == 0 && (src_addr & 7) == 0) {\n        int64_t* p = reinterpret_cast<int64_t*>(dst);\n        int64_t value = *reinterpret_cast<const int64_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 4 && (dst_addr & 3) == 0 && (src_addr & 3) == 0) {\n        int32_t* p = reinterpret_cast<int32_t*>(dst);\n        int32_t value = *reinterpret_cast<const int32_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 2 && (dst_addr & 1) == 0 && (src_addr & 1) == 0) {\n        int16_t* p = reinterpret_cast<int16_t*>(dst);\n        int16_t value = *reinterpret_cast<const int16_t*>(src);\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_value_kernel, n, (p, value, n));\n    } else if (srcsize == 1) {\n        check_cuda(hipMemset(dst, *reinterpret_cast<const int8_t*>(src), n));\n    } else {\n        // generic version\n        void* value_devptr = NULL;  // fill value in device memory\n        bool free_devptr = true;  // whether we need to free the memory\n\n        // prepare the fill value in a graph-friendly way\n        if (!capturable_tmp_alloc(WP_CURRENT_CONTEXT, src, srcsize, &value_devptr, &free_devptr)) {\n            fprintf(stderr, \"Warp fill error: failed to copy value to device memory\\n\");\n            return;\n        }\n\n        wp_launch_device(WP_CURRENT_CONTEXT, memtile_kernel, n, (dst, value_devptr, srcsize, n));\n\n        if (free_devptr) {\n            wp_free_device(WP_CURRENT_CONTEXT, value_devptr);\n        }\n    }\n}\n\n\nstatic __global__ void array_copy_1d_kernel(\n    void* dst,\n    const void* src,\n    size_t dst_stride,\n    size_t src_stride,\n    const int* dst_indices,\n    const int* src_indices,\n    size_t n,\n    size_t elem_size\n)\n{\n    size_t i = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (i < n) {\n        size_t src_idx = src_indices ? src_indices[i] : i;\n        size_t dst_idx = dst_indices ? dst_indices[i] : i;\n        const char* p = (const char*)src + src_idx * src_stride;\n        char* q = (char*)dst + dst_idx * dst_stride;\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_2d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<2, size_t> dst_strides,\n    wp::vec_t<2, size_t> src_strides,\n    wp::vec_t<2, const int*> dst_indices,\n    wp::vec_t<2, const int*> src_indices,\n    wp::vec_t<2, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t i = tid / n;\n    size_t j = tid % n;\n    if (i < shape[0] /*&& j < shape[1]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        const char* p = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1];\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_3d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<3, size_t> dst_strides,\n    wp::vec_t<3, size_t> src_strides,\n    wp::vec_t<3, const int*> dst_indices,\n    wp::vec_t<3, const int*> src_indices,\n    wp::vec_t<3, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t i = tid / (n * o);\n    size_t j = tid % (n * o) / o;\n    size_t k = tid % o;\n    if (i < shape[0] && j < shape[1] /*&& k < shape[2]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        size_t src_idx2 = src_indices[2] ? src_indices[2][k] : k;\n        size_t dst_idx2 = dst_indices[2] ? dst_indices[2][k] : k;\n        const char* p\n            = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1] + src_idx2 * src_strides[2];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1] + dst_idx2 * dst_strides[2];\n        memcpy(q, p, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_4d_kernel(\n    void* dst,\n    const void* src,\n    wp::vec_t<4, size_t> dst_strides,\n    wp::vec_t<4, size_t> src_strides,\n    wp::vec_t<4, const int*> dst_indices,\n    wp::vec_t<4, const int*> src_indices,\n    wp::vec_t<4, size_t> shape,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t p = shape[3];\n    size_t i = tid / (n * o * p);\n    size_t j = tid % (n * o * p) / (o * p);\n    size_t k = tid % (o * p) / p;\n    size_t l = tid % p;\n    if (i < shape[0] && j < shape[1] && k < shape[2] /*&& l < shape[3]*/) {\n        size_t src_idx0 = src_indices[0] ? src_indices[0][i] : i;\n        size_t dst_idx0 = dst_indices[0] ? dst_indices[0][i] : i;\n        size_t src_idx1 = src_indices[1] ? src_indices[1][j] : j;\n        size_t dst_idx1 = dst_indices[1] ? dst_indices[1][j] : j;\n        size_t src_idx2 = src_indices[2] ? src_indices[2][k] : k;\n        size_t dst_idx2 = dst_indices[2] ? dst_indices[2][k] : k;\n        size_t src_idx3 = src_indices[3] ? src_indices[3][l] : l;\n        size_t dst_idx3 = dst_indices[3] ? dst_indices[3][l] : l;\n        const char* p = (const char*)src + src_idx0 * src_strides[0] + src_idx1 * src_strides[1]\n            + src_idx2 * src_strides[2] + src_idx3 * src_strides[3];\n        char* q = (char*)dst + dst_idx0 * dst_strides[0] + dst_idx1 * dst_strides[1] + dst_idx2 * dst_strides[2]\n            + dst_idx3 * dst_strides[3];\n        memcpy(q, p, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_from_fabric_kernel(\n    wp::fabricarray_t<void> src, void* dst_data, size_t dst_stride, const int* dst_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < src.size) {\n        size_t dst_idx = dst_indices ? dst_indices[tid] : tid;\n        void* dst_ptr = (char*)dst_data + dst_idx * dst_stride;\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_from_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> src, void* dst_data, size_t dst_stride, const int* dst_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < src.size) {\n        size_t src_index = src.indices[tid];\n        size_t dst_idx = dst_indices ? dst_indices[tid] : tid;\n        void* dst_ptr = (char*)dst_data + dst_idx * dst_stride;\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_to_fabric_kernel(\n    wp::fabricarray_t<void> dst, const void* src_data, size_t src_stride, const int* src_indices, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_idx = src_indices ? src_indices[tid] : tid;\n        const void* src_ptr = (const char*)src_data + src_idx * src_stride;\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\nstatic __global__ void array_copy_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst,\n    const void* src_data,\n    size_t src_stride,\n    const int* src_indices,\n    size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_idx = src_indices ? src_indices[tid] : tid;\n        const void* src_ptr = (const char*)src_data + src_idx * src_stride;\n        size_t dst_idx = dst.indices[tid];\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_idx, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void\narray_copy_fabric_to_fabric_kernel(wp::fabricarray_t<void> dst, wp::fabricarray_t<void> src, size_t elem_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst, wp::fabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        const void* src_ptr = fabricarray_element_ptr(src, tid, elem_size);\n        size_t dst_index = dst.indices[tid];\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_indexed_to_fabric_kernel(\n    wp::fabricarray_t<void> dst, wp::indexedfabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_index = src.indices[tid];\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst, tid, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nstatic __global__ void array_copy_fabric_indexed_to_fabric_indexed_kernel(\n    wp::indexedfabricarray_t<void> dst, wp::indexedfabricarray_t<void> src, size_t elem_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n\n    if (tid < dst.size) {\n        size_t src_index = src.indices[tid];\n        size_t dst_index = dst.indices[tid];\n        const void* src_ptr = fabricarray_element_ptr(src.fa, src_index, elem_size);\n        void* dst_ptr = fabricarray_element_ptr(dst.fa, dst_index, elem_size);\n        memcpy(dst_ptr, src_ptr, elem_size);\n    }\n}\n\n\nWP_API bool wp_array_copy_device(void* context, void* dst, void* src, int dst_type, int src_type, int elem_size)\n{\n    if (!src || !dst)\n        return false;\n\n    const void* src_data = NULL;\n    void* dst_data = NULL;\n    int src_ndim = 0;\n    int dst_ndim = 0;\n    const int* src_shape = NULL;\n    const int* dst_shape = NULL;\n    const int* src_strides = NULL;\n    const int* dst_strides = NULL;\n    const int* const* src_indices = NULL;\n    const int* const* dst_indices = NULL;\n\n    const wp::fabricarray_t<void>* src_fabricarray = NULL;\n    wp::fabricarray_t<void>* dst_fabricarray = NULL;\n\n    const wp::indexedfabricarray_t<void>* src_indexedfabricarray = NULL;\n    wp::indexedfabricarray_t<void>* dst_indexedfabricarray = NULL;\n\n    const int* null_indices[wp::ARRAY_MAX_DIMS] = { NULL };\n\n    if (src_type == wp::ARRAY_TYPE_REGULAR) {\n        const wp::array_t<void>& src_arr = *static_cast<const wp::array_t<void>*>(src);\n        src_data = src_arr.data;\n        src_ndim = src_arr.ndim;\n        src_shape = src_arr.shape.dims;\n        src_strides = src_arr.strides;\n        src_indices = null_indices;\n    } else if (src_type == wp::ARRAY_TYPE_INDEXED) {\n        const wp::indexedarray_t<void>& src_arr = *static_cast<const wp::indexedarray_t<void>*>(src);\n        src_data = src_arr.arr.data;\n        src_ndim = src_arr.arr.ndim;\n        src_shape = src_arr.shape.dims;\n        src_strides = src_arr.arr.strides;\n        src_indices = src_arr.indices;\n    } else if (src_type == wp::ARRAY_TYPE_FABRIC) {\n        src_fabricarray = static_cast<const wp::fabricarray_t<void>*>(src);\n        src_ndim = 1;\n    } else if (src_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        src_indexedfabricarray = static_cast<const wp::indexedfabricarray_t<void>*>(src);\n        src_ndim = 1;\n    } else {\n        fprintf(stderr, \"Warp copy error: Invalid array type (%d)\\n\", src_type);\n        return false;\n    }\n\n    if (dst_type == wp::ARRAY_TYPE_REGULAR) {\n        const wp::array_t<void>& dst_arr = *static_cast<const wp::array_t<void>*>(dst);\n        dst_data = dst_arr.data;\n        dst_ndim = dst_arr.ndim;\n        dst_shape = dst_arr.shape.dims;\n        dst_strides = dst_arr.strides;\n        dst_indices = null_indices;\n    } else if (dst_type == wp::ARRAY_TYPE_INDEXED) {\n        const wp::indexedarray_t<void>& dst_arr = *static_cast<const wp::indexedarray_t<void>*>(dst);\n        dst_data = dst_arr.arr.data;\n        dst_ndim = dst_arr.arr.ndim;\n        dst_shape = dst_arr.shape.dims;\n        dst_strides = dst_arr.arr.strides;\n        dst_indices = dst_arr.indices;\n    } else if (dst_type == wp::ARRAY_TYPE_FABRIC) {\n        dst_fabricarray = static_cast<wp::fabricarray_t<void>*>(dst);\n        dst_ndim = 1;\n    } else if (dst_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        dst_indexedfabricarray = static_cast<wp::indexedfabricarray_t<void>*>(dst);\n        dst_ndim = 1;\n    } else {\n        fprintf(stderr, \"Warp copy error: Invalid array type (%d)\\n\", dst_type);\n        return false;\n    }\n\n    if (src_ndim != dst_ndim) {\n        fprintf(stderr, \"Warp copy error: Incompatible array dimensionalities (%d and %d)\\n\", src_ndim, dst_ndim);\n        return false;\n    }\n\n    ContextGuard guard(context);\n\n    // handle fabric arrays\n    if (dst_fabricarray) {\n        size_t n = dst_fabricarray->size;\n        if (src_fabricarray) {\n            // copy from fabric to fabric\n            if (src_fabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_to_fabric_kernel, n,\n                (*dst_fabricarray, *src_fabricarray, elem_size)\n            );\n            return true;\n        } else if (src_indexedfabricarray) {\n            // copy from fabric indexed to fabric\n            if (src_indexedfabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_indexed_to_fabric_kernel, n,\n                (*dst_fabricarray, *src_indexedfabricarray, elem_size)\n            );\n            return true;\n        } else {\n            // copy to fabric\n            if (size_t(src_shape[0]) != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_to_fabric_kernel, n,\n                (*dst_fabricarray, src_data, src_strides[0], src_indices[0], elem_size)\n            );\n            return true;\n        }\n    }\n    if (dst_indexedfabricarray) {\n        size_t n = dst_indexedfabricarray->size;\n        if (src_fabricarray) {\n            // copy from fabric to fabric indexed\n            if (src_fabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, *src_fabricarray, elem_size)\n            );\n            return true;\n        } else if (src_indexedfabricarray) {\n            // copy from fabric indexed to fabric indexed\n            if (src_indexedfabricarray->size != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_fabric_indexed_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, *src_indexedfabricarray, elem_size)\n            );\n            return true;\n        } else {\n            // copy to fabric indexed\n            if (size_t(src_shape[0]) != n) {\n                fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n                return false;\n            }\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_copy_to_fabric_indexed_kernel, n,\n                (*dst_indexedfabricarray, src_data, src_strides[0], src_indices[0], elem_size)\n            );\n            return true;\n        }\n    } else if (src_fabricarray) {\n        // copy from fabric\n        size_t n = src_fabricarray->size;\n        if (size_t(dst_shape[0]) != n) {\n            fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n            return false;\n        }\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_from_fabric_kernel, n,\n            (*src_fabricarray, dst_data, dst_strides[0], dst_indices[0], elem_size)\n        );\n        return true;\n    } else if (src_indexedfabricarray) {\n        // copy from fabric indexed\n        size_t n = src_indexedfabricarray->size;\n        if (size_t(dst_shape[0]) != n) {\n            fprintf(stderr, \"Warp copy error: Incompatible array sizes\\n\");\n            return false;\n        }\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_from_fabric_indexed_kernel, n,\n            (*src_indexedfabricarray, dst_data, dst_strides[0], dst_indices[0], elem_size)\n        );\n        return true;\n    }\n\n    size_t n = 1;\n    for (int i = 0; i < src_ndim; i++) {\n        if (src_shape[i] != dst_shape[i]) {\n            fprintf(stderr, \"Warp copy error: Incompatible array shapes\\n\");\n            return false;\n        }\n        n *= src_shape[i];\n    }\n\n    switch (src_ndim) {\n    case 1: {\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_1d_kernel, n,\n            (dst_data, src_data, dst_strides[0], src_strides[0], dst_indices[0], src_indices[0], src_shape[0],\n             elem_size)\n        );\n        break;\n    }\n    case 2: {\n        wp::vec_t<2, size_t> shape_v(src_shape[0], src_shape[1]);\n        wp::vec_t<2, size_t> src_strides_v(src_strides[0], src_strides[1]);\n        wp::vec_t<2, size_t> dst_strides_v(dst_strides[0], dst_strides[1]);\n        wp::vec_t<2, const int*> src_indices_v(src_indices[0], src_indices[1]);\n        wp::vec_t<2, const int*> dst_indices_v(dst_indices[0], dst_indices[1]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_2d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    case 3: {\n        wp::vec_t<3, size_t> shape_v(src_shape[0], src_shape[1], src_shape[2]);\n        wp::vec_t<3, size_t> src_strides_v(src_strides[0], src_strides[1], src_strides[2]);\n        wp::vec_t<3, size_t> dst_strides_v(dst_strides[0], dst_strides[1], dst_strides[2]);\n        wp::vec_t<3, const int*> src_indices_v(src_indices[0], src_indices[1], src_indices[2]);\n        wp::vec_t<3, const int*> dst_indices_v(dst_indices[0], dst_indices[1], dst_indices[2]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_3d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    case 4: {\n        wp::vec_t<4, size_t> shape_v(src_shape[0], src_shape[1], src_shape[2], src_shape[3]);\n        wp::vec_t<4, size_t> src_strides_v(src_strides[0], src_strides[1], src_strides[2], src_strides[3]);\n        wp::vec_t<4, size_t> dst_strides_v(dst_strides[0], dst_strides[1], dst_strides[2], dst_strides[3]);\n        wp::vec_t<4, const int*> src_indices_v(src_indices[0], src_indices[1], src_indices[2], src_indices[3]);\n        wp::vec_t<4, const int*> dst_indices_v(dst_indices[0], dst_indices[1], dst_indices[2], dst_indices[3]);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, array_copy_4d_kernel, n,\n            (dst_data, src_data, dst_strides_v, src_strides_v, dst_indices_v, src_indices_v, shape_v, elem_size)\n        );\n        break;\n    }\n    default:\n        fprintf(stderr, \"Warp copy error: invalid array dimensionality (%d)\\n\", src_ndim);\n        return false;\n    }\n\n    return check_cuda(hipGetLastError());\n}\n\n\nstatic __global__ void\narray_fill_1d_kernel(void* data, size_t n, size_t stride, const int* indices, const void* value, size_t value_size)\n{\n    size_t i = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (i < n) {\n        size_t idx = indices ? indices[i] : i;\n        char* p = (char*)data + idx * stride;\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_2d_kernel(\n    void* data,\n    wp::vec_t<2, size_t> shape,\n    wp::vec_t<2, size_t> strides,\n    wp::vec_t<2, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t i = tid / n;\n    size_t j = tid % n;\n    if (i < shape[0] /*&& j < shape[1]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1];\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_3d_kernel(\n    void* data,\n    wp::vec_t<3, size_t> shape,\n    wp::vec_t<3, size_t> strides,\n    wp::vec_t<3, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t i = tid / (n * o);\n    size_t j = tid % (n * o) / o;\n    size_t k = tid % o;\n    if (i < shape[0] && j < shape[1] /*&& k < shape[2]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        size_t idx2 = indices[2] ? indices[2][k] : k;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1] + idx2 * strides[2];\n        memcpy(p, value, value_size);\n    }\n}\n\nstatic __global__ void array_fill_4d_kernel(\n    void* data,\n    wp::vec_t<4, size_t> shape,\n    wp::vec_t<4, size_t> strides,\n    wp::vec_t<4, const int*> indices,\n    const void* value,\n    size_t value_size\n)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    size_t n = shape[1];\n    size_t o = shape[2];\n    size_t p = shape[3];\n    size_t i = tid / (n * o * p);\n    size_t j = tid % (n * o * p) / (o * p);\n    size_t k = tid % (o * p) / p;\n    size_t l = tid % p;\n    if (i < shape[0] && j < shape[1] && k < shape[2] /*&& l < shape[3]*/) {\n        size_t idx0 = indices[0] ? indices[0][i] : i;\n        size_t idx1 = indices[1] ? indices[1][j] : j;\n        size_t idx2 = indices[2] ? indices[2][k] : k;\n        size_t idx3 = indices[3] ? indices[3][l] : l;\n        char* p = (char*)data + idx0 * strides[0] + idx1 * strides[1] + idx2 * strides[2] + idx3 * strides[3];\n        memcpy(p, value, value_size);\n    }\n}\n\n\nstatic __global__ void array_fill_fabric_kernel(wp::fabricarray_t<void> fa, const void* value, size_t value_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (tid < fa.size) {\n        void* dst_ptr = fabricarray_element_ptr(fa, tid, value_size);\n        memcpy(dst_ptr, value, value_size);\n    }\n}\n\n\nstatic __global__ void\narray_fill_fabric_indexed_kernel(wp::indexedfabricarray_t<void> ifa, const void* value, size_t value_size)\n{\n    size_t tid = size_t(blockIdx.x) * size_t(blockDim.x) + size_t(threadIdx.x);\n    if (tid < ifa.size) {\n        size_t idx = size_t(ifa.indices[tid]);\n        if (idx < ifa.fa.size) {\n            void* dst_ptr = fabricarray_element_ptr(ifa.fa, idx, value_size);\n            memcpy(dst_ptr, value, value_size);\n        }\n    }\n}\n\n\nWP_API void wp_array_fill_device(void* context, void* arr_ptr, int arr_type, const void* value_ptr, int value_size)\n{\n    if (!arr_ptr || !value_ptr)\n        return;\n\n    void* data = NULL;\n    int ndim = 0;\n    const int* shape = NULL;\n    const int* strides = NULL;\n    const int* const* indices = NULL;\n\n    wp::fabricarray_t<void>* fa = NULL;\n    wp::indexedfabricarray_t<void>* ifa = NULL;\n\n    const int* null_indices[wp::ARRAY_MAX_DIMS] = { NULL };\n\n    if (arr_type == wp::ARRAY_TYPE_REGULAR) {\n        wp::array_t<void>& arr = *static_cast<wp::array_t<void>*>(arr_ptr);\n        data = arr.data;\n        ndim = arr.ndim;\n        shape = arr.shape.dims;\n        strides = arr.strides;\n        indices = null_indices;\n    } else if (arr_type == wp::ARRAY_TYPE_INDEXED) {\n        wp::indexedarray_t<void>& ia = *static_cast<wp::indexedarray_t<void>*>(arr_ptr);\n        data = ia.arr.data;\n        ndim = ia.arr.ndim;\n        shape = ia.shape.dims;\n        strides = ia.arr.strides;\n        indices = ia.indices;\n    } else if (arr_type == wp::ARRAY_TYPE_FABRIC) {\n        fa = static_cast<wp::fabricarray_t<void>*>(arr_ptr);\n    } else if (arr_type == wp::ARRAY_TYPE_FABRIC_INDEXED) {\n        ifa = static_cast<wp::indexedfabricarray_t<void>*>(arr_ptr);\n    } else {\n        fprintf(stderr, \"Warp fill error: Invalid array type id %d\\n\", arr_type);\n        return;\n    }\n\n    size_t n = 1;\n    for (int i = 0; i < ndim; i++)\n        n *= shape[i];\n\n    ContextGuard guard(context);\n\n    void* value_devptr = NULL;  // fill value in device memory\n    bool free_devptr = true;  // whether we need to free the memory\n\n    // prepare the fill value in a graph-friendly way\n    if (!capturable_tmp_alloc(WP_CURRENT_CONTEXT, value_ptr, value_size, &value_devptr, &free_devptr)) {\n        fprintf(stderr, \"Warp fill error: failed to copy value to device memory\\n\");\n        return;\n    }\n\n    if (fa) {\n        // handle fabric arrays\n        wp_launch_device(WP_CURRENT_CONTEXT, array_fill_fabric_kernel, n, (*fa, value_devptr, value_size));\n    } else if (ifa) {\n        // handle indexed fabric arrays\n        wp_launch_device(WP_CURRENT_CONTEXT, array_fill_fabric_indexed_kernel, n, (*ifa, value_devptr, value_size));\n    } else {\n        // handle regular or indexed arrays\n        switch (ndim) {\n        case 1: {\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_1d_kernel, n,\n                (data, shape[0], strides[0], indices[0], value_devptr, value_size)\n            );\n            break;\n        }\n        case 2: {\n            wp::vec_t<2, size_t> shape_v(shape[0], shape[1]);\n            wp::vec_t<2, size_t> strides_v(strides[0], strides[1]);\n            wp::vec_t<2, const int*> indices_v(indices[0], indices[1]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_2d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        case 3: {\n            wp::vec_t<3, size_t> shape_v(shape[0], shape[1], shape[2]);\n            wp::vec_t<3, size_t> strides_v(strides[0], strides[1], strides[2]);\n            wp::vec_t<3, const int*> indices_v(indices[0], indices[1], indices[2]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_3d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        case 4: {\n            wp::vec_t<4, size_t> shape_v(shape[0], shape[1], shape[2], shape[3]);\n            wp::vec_t<4, size_t> strides_v(strides[0], strides[1], strides[2], strides[3]);\n            wp::vec_t<4, const int*> indices_v(indices[0], indices[1], indices[2], indices[3]);\n            wp_launch_device(\n                WP_CURRENT_CONTEXT, array_fill_4d_kernel, n,\n                (data, shape_v, strides_v, indices_v, value_devptr, value_size)\n            );\n            break;\n        }\n        default:\n            fprintf(stderr, \"Warp fill error: invalid array dimensionality (%d)\\n\", ndim);\n            break;\n        }\n    }\n\n    if (free_devptr) {\n        wp_free_device(WP_CURRENT_CONTEXT, value_devptr);\n    }\n}\n\nvoid wp_array_scan_int_device(uint64_t in, uint64_t out, int len, bool inclusive)\n{\n    scan_device((const int*)in, (int*)out, len, inclusive);\n}\n\nvoid wp_array_scan_float_device(uint64_t in, uint64_t out, int len, bool inclusive)\n{\n    scan_device((const float*)in, (float*)out, len, inclusive);\n}\n\nint wp_cuda_driver_version()\n{\n    int version;\n    if (check_cu(cuDriverGetVersion_f(&version)))\n        return version;\n    else\n        return 0;\n}\n\nint wp_cuda_toolkit_version() { return CUDA_VERSION; }\n\nint wp_nvrtc_version()\n{\n    int major = 0, minor = 0;\n    hiprtcVersion(&major, &minor);\n    return major * 1000 + minor * 10;\n}\n\nconst char* wp_libmathdx_version()\n{\n#if WP_ENABLE_MATHDX\n    static char version[64];\n    snprintf(version, sizeof(version), \"%d.%d.%d\", LIBMATHDX_VER_MAJOR, LIBMATHDX_VER_MINOR, LIBMATHDX_VER_PATCH);\n    return version;\n#else\n    return \"\";\n#endif\n}\n\nbool wp_cuda_driver_is_initialized() { return is_cuda_driver_initialized(); }\n\nint wp_nvrtc_supported_arch_count()\n{\n    int count;\n    if (check_nvrtc(nvrtcGetNumSupportedArchs(&count)))\n        return count;\n    else\n        return 0;\n}\n\nvoid wp_nvrtc_supported_archs(int* archs)\n{\n    if (archs) {\n        check_nvrtc(nvrtcGetSupportedArchs(archs));\n    }\n}\n\nint wp_cuda_device_get_count()\n{\n    int count = 0;\n    check_cu(cuDeviceGetCount_f(&count));\n    return count;\n}\n\nvoid* wp_cuda_device_get_primary_context(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size())) {\n        DeviceInfo& device_info = g_devices[ordinal];\n\n        // acquire the primary context if we haven't already\n        if (!device_info.primary_context)\n            check_cu(cuDevicePrimaryCtxRetain_f(&device_info.primary_context, device_info.device));\n\n        return device_info.primary_context;\n    }\n\n    return NULL;\n}\n\nconst char* wp_cuda_device_get_name(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].name;\n    return NULL;\n}\n\nint wp_cuda_device_get_arch(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].arch;\n    return 0;\n}\n\nint wp_cuda_device_get_sm_count(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].sm_count;\n    return 0;\n}\n\nint wp_cuda_device_get_max_shared_memory(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].max_smem_bytes;\n    return 0;\n}\n\nvoid wp_cuda_device_get_uuid(int ordinal, char uuid[16])\n{\n    memcpy(uuid, g_devices[ordinal].uuid.bytes, sizeof(char) * 16);\n}\n\nint wp_cuda_device_get_pci_domain_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_domain_id;\n    return -1;\n}\n\nint wp_cuda_device_get_pci_bus_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_bus_id;\n    return -1;\n}\n\nint wp_cuda_device_get_pci_device_id(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].pci_device_id;\n    return -1;\n}\n\nint wp_cuda_device_is_uva(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_uva;\n    return 0;\n}\n\nint wp_cuda_device_is_mempool_supported(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_mempool_supported;\n    return 0;\n}\n\nint wp_cuda_device_is_ipc_supported(int ordinal)\n{\n    if (ordinal >= 0 && ordinal < int(g_devices.size()))\n        return g_devices[ordinal].is_ipc_supported;\n    return 0;\n}\n\nint wp_cuda_device_set_mempool_release_threshold(int ordinal, uint64_t threshold)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!check_cuda(hipMemPoolSetAttribute(pool, hipMemPoolAttrReleaseThreshold, &threshold))) {\n        fprintf(stderr, \"Warp error: Failed to set memory pool attribute on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    return 1;  // success\n}\n\nuint64_t wp_cuda_device_get_mempool_release_threshold(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t threshold = 0;\n    if (!check_cuda(hipMemPoolGetAttribute(pool, hipMemPoolAttrReleaseThreshold, &threshold))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool release threshold on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    return threshold;\n}\n\nuint64_t wp_cuda_device_get_mempool_used_mem_current(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t mem_used = 0;\n    if (!check_cuda(hipMemPoolGetAttribute(pool, hipMemPoolAttrUsedMemCurrent, &mem_used))) {\n        fprintf(\n            stderr, \"Warp error: Failed to get amount of currently used memory from the memory pool on device %d\\n\",\n            ordinal\n        );\n        return 0;\n    }\n\n    return mem_used;\n}\n\nuint64_t wp_cuda_device_get_mempool_used_mem_high(int ordinal)\n{\n    if (ordinal < 0 || ordinal > int(g_devices.size())) {\n        fprintf(stderr, \"Invalid device ordinal %d\\n\", ordinal);\n        return 0;\n    }\n\n    if (!g_devices[ordinal].is_mempool_supported)\n        return 0;\n\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool on device %d\\n\", ordinal);\n        return 0;\n    }\n\n    uint64_t mem_high_water_mark = 0;\n    if (!check_cuda(hipMemPoolGetAttribute(pool, hipMemPoolAttrUsedMemHigh, &mem_high_water_mark))) {\n        fprintf(\n            stderr, \"Warp error: Failed to get memory usage high water mark from the memory pool on device %d\\n\",\n            ordinal\n        );\n        return 0;\n    }\n\n    return mem_high_water_mark;\n}\n\nvoid wp_cuda_device_get_memory_info(int ordinal, size_t* free_mem, size_t* total_mem)\n{\n    // use temporary storage if user didn't specify pointers\n    size_t tmp_free_mem, tmp_total_mem;\n\n    if (free_mem)\n        *free_mem = 0;\n    else\n        free_mem = &tmp_free_mem;\n\n    if (total_mem)\n        *total_mem = 0;\n    else\n        total_mem = &tmp_total_mem;\n\n    if (ordinal >= 0 && ordinal < int(g_devices.size())) {\n        if (g_devices[ordinal].primary_context) {\n            ContextGuard guard(g_devices[ordinal].primary_context, true);\n            check_cu(cuMemGetInfo_f(free_mem, total_mem));\n        } else {\n            // if we haven't acquired the primary context yet, acquire it temporarily\n            hipCtx_t primary_context = NULL;\n            check_cu(cuDevicePrimaryCtxRetain_f(&primary_context, g_devices[ordinal].device));\n            {\n                ContextGuard guard(primary_context, true);\n                check_cu(cuMemGetInfo_f(free_mem, total_mem));\n            }\n            check_cu(cuDevicePrimaryCtxRelease_f(g_devices[ordinal].device));\n        }\n    }\n}\n\n\nvoid* wp_cuda_context_get_current() { return get_current_context(); }\n\nvoid wp_cuda_context_set_current(void* context)\n{\n    hipCtx_t ctx = static_cast<hipCtx_t>(context);\n    hipCtx_t prev_ctx = NULL;\n    check_cu(cuCtxGetCurrent_f(&prev_ctx));\n    if (ctx != prev_ctx) {\n        check_cu(cuCtxSetCurrent_f(ctx));\n    }\n}\n\nvoid wp_cuda_context_push_current(void* context) { check_cu(cuCtxPushCurrent_f(static_cast<hipCtx_t>(context))); }\n\nvoid wp_cuda_context_pop_current()\n{\n    hipCtx_t context;\n    check_cu(cuCtxPopCurrent_f(&context));\n}\n\nvoid* wp_cuda_context_create(int device_ordinal)\n{\n    hipCtx_t ctx = NULL;\n    hipDevice_t device;\n    if (check_cu(cuDeviceGet_f(&device, device_ordinal)))\n        check_cu(cuCtxCreate_f(&ctx, 0, device));\n    return ctx;\n}\n\nvoid wp_cuda_context_destroy(void* context)\n{\n    if (context) {\n        hipCtx_t ctx = static_cast<hipCtx_t>(context);\n\n        // ensure this is not the current context\n        if (ctx == wp_cuda_context_get_current())\n            wp_cuda_context_set_current(NULL);\n\n        // release the cached info about this context\n        ContextInfo* info = get_context_info(ctx);\n        if (info) {\n            if (info->stream)\n                check_cu(cuStreamDestroy_f(info->stream));\n\n            if (info->conditional_module)\n                check_cu(cuModuleUnload_f(info->conditional_module));\n\n            g_contexts.erase(ctx);\n        }\n\n        check_cu(cuCtxDestroy_f(ctx));\n    }\n}\n\nvoid wp_cuda_context_synchronize(void* context)\n{\n    ContextGuard guard(context);\n\n    check_cu(cuCtxSynchronize_f());\n\n    if (!context)\n        context = get_current_context();\n\n    if (run_deferred_actions(context) > 0) {\n        // ensure deferred asynchronous operations complete\n        check_cu(cuCtxSynchronize_f());\n    }\n\n    // check_cuda(cudaDeviceGraphMemTrim(wp_cuda_context_get_device_ordinal(context)));\n}\n\nuint64_t wp_cuda_context_check(void* context)\n{\n    ContextGuard guard(context);\n\n    // check errors before syncing\n    hipError_t e = hipGetLastError();\n    check_cuda(e);\n\n    hipStreamCaptureStatus status = hipStreamCaptureStatusNone;\n    check_cuda(hipStreamIsCapturing(get_current_stream(), &status));\n\n    // synchronize if the stream is not capturing\n    if (status == hipStreamCaptureStatusNone) {\n        check_cuda(hipDeviceSynchronize());\n        e = hipGetLastError();\n    }\n\n    return static_cast<uint64_t>(e);\n}\n\n\nint wp_cuda_context_get_device_ordinal(void* context)\n{\n    ContextInfo* info = get_context_info(static_cast<hipCtx_t>(context));\n    return info && info->device_info ? info->device_info->ordinal : -1;\n}\n\nint wp_cuda_context_is_primary(void* context)\n{\n    hipCtx_t ctx = static_cast<hipCtx_t>(context);\n    ContextInfo* context_info = get_context_info(ctx);\n    if (!context_info) {\n        fprintf(stderr, \"Warp error: Failed to get context info\\n\");\n        return 0;\n    }\n\n    // if the device primary context is known, check if it matches the given context\n    DeviceInfo* device_info = context_info->device_info;\n    if (device_info->primary_context)\n        return int(ctx == device_info->primary_context);\n\n    // there is no CUDA API to check if a context is primary, but we can temporarily\n    // acquire the device's primary context to check the pointer\n    hipCtx_t primary_ctx;\n    if (check_cu(cuDevicePrimaryCtxRetain_f(&primary_ctx, device_info->device))) {\n        check_cu(cuDevicePrimaryCtxRelease_f(device_info->device));\n        return int(ctx == primary_ctx);\n    }\n\n    return 0;\n}\n\nvoid* wp_cuda_context_get_stream(void* context)\n{\n    ContextInfo* info = get_context_info(static_cast<hipCtx_t>(context));\n    if (info) {\n        return info->stream;\n    }\n    return NULL;\n}\n\nvoid wp_cuda_context_set_stream(void* context, void* stream, int sync)\n{\n    ContextInfo* context_info = get_context_info(static_cast<hipCtx_t>(context));\n    if (context_info) {\n        hipStream_t new_stream = static_cast<hipStream_t>(stream);\n\n        // check whether we should sync with the previous stream on this device\n        if (sync) {\n            hipStream_t old_stream = context_info->stream;\n            StreamInfo* old_stream_info = get_stream_info(old_stream);\n            if (old_stream_info) {\n                hipEvent_t cached_event = old_stream_info->cached_event;\n                check_cu(cuEventRecord_f(cached_event, old_stream));\n                check_cu(cuStreamWaitEvent_f(new_stream, cached_event, CU_EVENT_WAIT_DEFAULT));\n            }\n        }\n\n        context_info->stream = new_stream;\n    }\n}\n\nint wp_cuda_is_peer_access_supported(int target_ordinal, int peer_ordinal)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid target device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;\n\n    int can_access = 0;\n    check_cuda(hipDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n\n    return can_access;\n}\n\nint wp_cuda_is_peer_access_enabled(void* target_context, void* peer_context)\n{\n    if (!target_context || !peer_context) {\n        fprintf(stderr, \"Warp error: invalid CUDA context\\n\");\n        return 0;\n    }\n\n    if (target_context == peer_context)\n        return 1;\n\n    int target_ordinal = wp_cuda_context_get_device_ordinal(target_context);\n    int peer_ordinal = wp_cuda_context_get_device_ordinal(peer_context);\n\n    // check if peer access is supported\n    int can_access = 0;\n    check_cuda(hipDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n    if (!can_access)\n        return 0;\n\n    // There is no CUDA API to query if peer access is enabled, but we can try to enable it and check the result.\n\n    ContextGuard guard(peer_context, true);\n\n    hipCtx_t target_ctx = static_cast<hipCtx_t>(target_context);\n\n    hipError_t result = cuCtxEnablePeerAccess_f(target_ctx, 0);\n    if (result == hipErrorPeerAccessAlreadyEnabled) {\n        return 1;\n    } else if (result == hipSuccess) {\n        // undo enablement\n        check_cu(cuCtxDisablePeerAccess_f(target_ctx));\n        return 0;\n    } else {\n        // report error\n        check_cu(result);\n        return 0;\n    }\n}\n\nint wp_cuda_set_peer_access_enabled(void* target_context, void* peer_context, int enable)\n{\n    if (!target_context || !peer_context) {\n        fprintf(stderr, \"Warp error: invalid CUDA context\\n\");\n        return 0;\n    }\n\n    if (target_context == peer_context)\n        return 1;  // no-op\n\n    int target_ordinal = wp_cuda_context_get_device_ordinal(target_context);\n    int peer_ordinal = wp_cuda_context_get_device_ordinal(peer_context);\n\n    // check if peer access is supported\n    int can_access = 0;\n    check_cuda(hipDeviceCanAccessPeer(&can_access, peer_ordinal, target_ordinal));\n    if (!can_access) {\n        // failure if enabling, success if disabling\n        if (enable) {\n            fprintf(stderr, \"Warp error: device %d cannot access device %d\\n\", peer_ordinal, target_ordinal);\n            return 0;\n        } else\n            return 1;\n    }\n\n    ContextGuard guard(peer_context, true);\n\n    hipCtx_t target_ctx = static_cast<hipCtx_t>(target_context);\n\n    if (enable) {\n        hipError_t status = cuCtxEnablePeerAccess_f(target_ctx, 0);\n        if (status != hipSuccess && status != hipErrorPeerAccessAlreadyEnabled) {\n            check_cu(status);\n            fprintf(\n                stderr, \"Warp error: failed to enable peer access from device %d to device %d\\n\", peer_ordinal,\n                target_ordinal\n            );\n            return 0;\n        }\n    } else {\n        hipError_t status = cuCtxDisablePeerAccess_f(target_ctx);\n        if (status != hipSuccess && status != hipErrorPeerAccessNotEnabled) {\n            check_cu(status);\n            fprintf(\n                stderr, \"Warp error: failed to disable peer access from device %d to device %d\\n\", peer_ordinal,\n                target_ordinal\n            );\n            return 0;\n        }\n    }\n\n    return 1;  // success\n}\n\nint wp_cuda_is_mempool_access_enabled(int target_ordinal, int peer_ordinal)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;\n\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, target_ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool of device %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    hipMemAccessFlags flags = hipMemAccessFlagsProtNone;\n    hipMemLocation location;\n    location.id = peer_ordinal;\n    location.type = hipMemLocationTypeDevice;\n    if (check_cuda(hipMemPoolGetAccess(&flags, pool, &location)))\n        return int(flags != hipMemAccessFlagsProtNone);\n\n    return 0;\n}\n\nint wp_cuda_set_mempool_access_enabled(int target_ordinal, int peer_ordinal, int enable)\n{\n    int num_devices = int(g_devices.size());\n\n    if (target_ordinal < 0 || target_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid device ordinal %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    if (peer_ordinal < 0 || peer_ordinal > num_devices) {\n        fprintf(stderr, \"Warp error: Invalid peer device ordinal %d\\n\", peer_ordinal);\n        return 0;\n    }\n\n    if (target_ordinal == peer_ordinal)\n        return 1;  // no-op\n\n    // get the memory pool\n    hipMemPool_t pool;\n    if (!check_cuda(hipDeviceGetDefaultMemPool(&pool, target_ordinal))) {\n        fprintf(stderr, \"Warp error: Failed to get memory pool of device %d\\n\", target_ordinal);\n        return 0;\n    }\n\n    hipMemAccessDesc desc;\n    desc.location.type = hipMemLocationTypeDevice;\n    desc.location.id = peer_ordinal;\n\n    // only cudaMemAccessFlagsProtReadWrite and cudaMemAccessFlagsProtNone are supported\n    if (enable)\n        desc.flags = hipMemAccessFlagsProtReadWrite;\n    else\n        desc.flags = hipMemAccessFlagsProtNone;\n\n    if (!check_cuda(hipMemPoolSetAccess(pool, &desc, 1))) {\n        fprintf(\n            stderr, \"Warp error: Failed to set mempool access from device %d to device %d\\n\", peer_ordinal,\n            target_ordinal\n        );\n        return 0;\n    }\n\n    return 1;  // success\n}\n\nvoid wp_cuda_ipc_get_mem_handle(void* ptr, char* out_buffer)\n{\n    hipIpcMemHandle_t memHandle;\n    check_cu(cuIpcGetMemHandle_f(&memHandle, (hipDeviceptr_t)ptr));\n    memcpy(out_buffer, memHandle.reserved, HIP_IPC_HANDLE_SIZE);\n}\n\nvoid* wp_cuda_ipc_open_mem_handle(void* context, char* handle)\n{\n    ContextGuard guard(context);\n\n    hipIpcMemHandle_t memHandle;\n    memcpy(memHandle.reserved, handle, HIP_IPC_HANDLE_SIZE);\n\n    hipDeviceptr_t device_ptr;\n\n    // Strangely, the CU_IPC_MEM_LAZY_ENABLE_PEER_ACCESS flag is required\n    if check_cu (cuIpcOpenMemHandle_f(&device_ptr, memHandle, hipIpcMemLazyEnablePeerAccess))\n        return (void*)device_ptr;\n    else\n        return NULL;\n}\n\nvoid wp_cuda_ipc_close_mem_handle(void* ptr) { check_cu(cuIpcCloseMemHandle_f((hipDeviceptr_t)ptr)); }\n\nvoid wp_cuda_ipc_get_event_handle(void* context, void* event, char* out_buffer)\n{\n    ContextGuard guard(context);\n\n    hipIpcEventHandle_t eventHandle;\n    check_cu(cuIpcGetEventHandle_f(&eventHandle, static_cast<hipEvent_t>(event)));\n    memcpy(out_buffer, eventHandle.reserved, HIP_IPC_HANDLE_SIZE);\n}\n\nvoid* wp_cuda_ipc_open_event_handle(void* context, char* handle)\n{\n    ContextGuard guard(context);\n\n    hipIpcEventHandle_t eventHandle;\n    memcpy(eventHandle.reserved, handle, HIP_IPC_HANDLE_SIZE);\n\n    hipEvent_t event;\n\n    if (check_cu(cuIpcOpenEventHandle_f(&event, eventHandle)))\n        return event;\n    else\n        return NULL;\n}\n\nvoid* wp_cuda_stream_create(void* context, int priority)\n{\n    ContextGuard guard(context, true);\n\n    hipStream_t stream;\n    if (check_cu(cuStreamCreateWithPriority_f(&stream, hipStreamDefault, priority))) {\n        wp_cuda_stream_register(WP_CURRENT_CONTEXT, stream);\n        return stream;\n    } else\n        return NULL;\n}\n\nvoid wp_cuda_stream_destroy(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    wp_cuda_stream_unregister(context, stream);\n\n    // release temporary radix sort buffer associated with this stream\n    radix_sort_release(context, stream);\n\n    check_cu(cuStreamDestroy_f(static_cast<hipStream_t>(stream)));\n}\n\nint wp_cuda_stream_query(void* stream)\n{\n    hipError_t res = cuStreamQuery_f(static_cast<hipStream_t>(stream));\n\n    if ((res != hipSuccess) && (res != hipErrorNotReady)) {\n        // Abnormal, print out error\n        check_cu(res);\n    }\n\n    return res;\n}\n\nvoid wp_cuda_stream_register(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    ContextGuard guard(context);\n\n    // populate stream info\n    StreamInfo& stream_info = g_streams[static_cast<hipStream_t>(stream)];\n    check_cu(cuEventCreate_f(&stream_info.cached_event, hipEventDisableTiming));\n}\n\nvoid wp_cuda_stream_unregister(void* context, void* stream)\n{\n    if (!stream)\n        return;\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (stream_info) {\n        // release stream info\n        check_cu(cuEventDestroy_f(stream_info->cached_event));\n        g_streams.erase(cuda_stream);\n    }\n\n    // make sure we don't leave dangling references to this stream\n    ContextInfo* context_info = get_context_info(context);\n    if (context_info) {\n        if (cuda_stream == context_info->stream)\n            context_info->stream = NULL;\n    }\n}\n\nvoid* wp_cuda_stream_get_current() { return get_current_stream(); }\n\nvoid wp_cuda_stream_synchronize(void* stream) { check_cu(cuStreamSynchronize_f(static_cast<hipStream_t>(stream))); }\n\nvoid wp_cuda_stream_wait_event(void* stream, void* event, bool external)\n{\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        // wait for an external event during graph capture\n        check_cu(\n            cuStreamWaitEvent_f(static_cast<hipStream_t>(stream), static_cast<hipEvent_t>(event), CU_EVENT_WAIT_EXTERNAL)\n        );\n    } else {\n        check_cu(\n            cuStreamWaitEvent_f(static_cast<hipStream_t>(stream), static_cast<hipEvent_t>(event), CU_EVENT_WAIT_DEFAULT)\n        );\n    }\n}\n\nvoid wp_cuda_stream_wait_stream(void* stream, void* other_stream, void* event, bool external)\n{\n    unsigned record_flags = hipEventRecordDefault;\n    unsigned wait_flags = CU_EVENT_WAIT_DEFAULT;\n\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty()) {\n        if (wp_cuda_stream_is_capturing(other_stream))\n            record_flags = hipEventRecordExternal;\n        if (wp_cuda_stream_is_capturing(stream))\n            wait_flags = CU_EVENT_WAIT_EXTERNAL;\n    }\n\n    check_cu(cuEventRecordWithFlags_f(static_cast<hipEvent_t>(event), static_cast<hipStream_t>(other_stream), record_flags));\n    check_cu(cuStreamWaitEvent_f(static_cast<hipStream_t>(stream), static_cast<hipEvent_t>(event), wait_flags));\n}\n\nint wp_cuda_stream_is_capturing(void* stream)\n{\n    hipStreamCaptureStatus status = hipStreamCaptureStatusNone;\n    check_cuda(hipStreamIsCapturing(static_cast<hipStream_t>(stream), &status));\n\n    return int(status != hipStreamCaptureStatusNone);\n}\n\nuint64_t wp_cuda_stream_get_capture_id(void* stream) { return get_capture_id(static_cast<hipStream_t>(stream)); }\n\nint wp_cuda_stream_get_priority(void* stream)\n{\n    int priority = 0;\n    check_cuda(cuStreamGetPriority_f(static_cast<hipStream_t>(stream), &priority));\n\n    return priority;\n}\n\nvoid* wp_cuda_event_create(void* context, unsigned flags)\n{\n    ContextGuard guard(context, true);\n\n    hipEvent_t event;\n    if (check_cu(cuEventCreate_f(&event, flags)))\n        return event;\n    else\n        return NULL;\n}\n\nvoid wp_cuda_event_destroy(void* event) { check_cu(cuEventDestroy_f(static_cast<hipEvent_t>(event))); }\n\nint wp_cuda_event_query(void* event)\n{\n    hipError_t res = cuEventQuery_f(static_cast<hipEvent_t>(event));\n\n    if ((res != hipSuccess) && (res != hipErrorNotReady)) {\n        // Abnormal, print out error\n        check_cu(res);\n    }\n\n    return res;\n}\n\nvoid wp_cuda_event_record(void* event, void* stream, bool external)\n{\n    // the external flag can only be used during graph capture\n    if (external && !g_captures.empty() && wp_cuda_stream_is_capturing(stream)) {\n        // record external event during graph capture (e.g., for timing or when explicitly specified by the user)\n        check_cu(cuEventRecordWithFlags_f(\n            static_cast<hipEvent_t>(event), static_cast<hipStream_t>(stream), hipEventRecordExternal\n        ));\n    } else {\n        check_cu(cuEventRecord_f(static_cast<hipEvent_t>(event), static_cast<hipStream_t>(stream)));\n    }\n}\n\nvoid wp_cuda_event_synchronize(void* event) { check_cu(cuEventSynchronize_f(static_cast<hipEvent_t>(event))); }\n\nfloat wp_cuda_event_elapsed_time(void* start_event, void* end_event)\n{\n    float elapsed = 0.0f;\n    hipEvent_t start = static_cast<hipEvent_t>(start_event);\n    hipEvent_t end = static_cast<hipEvent_t>(end_event);\n    check_cuda(hipEventElapsedTime(&elapsed, start, end));\n    return elapsed;\n}\n\nbool wp_cuda_graph_begin_capture(void* context, void* stream, int external, int mode)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: unknown stream\");\n        return false;\n    }\n\n    hipStreamCaptureMode capture_mode;\n    switch (mode) {\n    case WP_CUDA_GRAPH_CAPTURE_MODE_GLOBAL:\n        capture_mode = hipStreamCaptureModeGlobal;\n        break;\n    case WP_CUDA_GRAPH_CAPTURE_MODE_THREAD_LOCAL:\n        capture_mode = hipStreamCaptureModeThreadLocal;\n        break;\n    case WP_CUDA_GRAPH_CAPTURE_MODE_RELAXED:\n        capture_mode = hipStreamCaptureModeRelaxed;\n        break;\n    default:\n        wp::set_error_string(\"Warp error: invalid capture mode\");\n        return false;\n    }\n\n    if (external) {\n        // if it's an external capture, make sure it's already active so we can get the capture id\n        hipStreamCaptureStatus status = hipStreamCaptureStatusNone;\n        if (!check_cuda(hipStreamIsCapturing(cuda_stream, &status)))\n            return false;\n        if (status != hipStreamCaptureStatusActive) {\n            wp::set_error_string(\"Warp error: stream is not capturing\");\n            return false;\n        }\n    } else {\n        if (!check_cuda(hipStreamBeginCapture(cuda_stream, capture_mode)))\n            return false;\n    }\n\n    uint64_t capture_id = get_capture_id(cuda_stream);\n\n    CaptureInfo* capture = new CaptureInfo();\n    capture->stream = cuda_stream;\n    capture->id = capture_id;\n    capture->external = bool(external);\n    capture->mode = capture_mode;\n\n    // update stream info\n    stream_info->capture = capture;\n\n    // add to known captures\n    g_captures[capture_id] = capture;\n\n    return true;\n}\n\nbool wp_cuda_graph_end_capture(void* context, void* stream, void** graph_ret)\n{\n    ContextGuard guard(context);\n\n    // check if this is a known stream\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: unknown capture stream\");\n        return false;\n    }\n\n    // check if this stream was used to start a capture\n    CaptureInfo* capture = stream_info->capture;\n    if (!capture) {\n        wp::set_error_string(\"Warp error: stream has no capture started\");\n        return false;\n    }\n\n    // get capture info\n    bool external = capture->external;\n    uint64_t capture_id = capture->id;\n    std::vector<FreeInfo> tmp_allocs = capture->tmp_allocs;\n\n    // clear capture info\n    stream_info->capture = NULL;\n    g_captures.erase(capture_id);\n    delete capture;\n\n    // a lambda to clean up on exit in case of error\n    auto clean_up = [cuda_stream, capture_id, external]() {\n        // unreference outstanding graph allocs so that they will be released with the user reference\n        for (auto it = g_graph_allocs.begin(); it != g_graph_allocs.end(); ++it) {\n            GraphAllocInfo& alloc_info = it->second;\n            if (alloc_info.capture_id == capture_id)\n                alloc_info.graph_destroyed = true;\n        }\n\n        // make sure we terminate the capture\n        if (!external) {\n            hipGraph_t graph = NULL;\n            hipStreamEndCapture(cuda_stream, &graph);\n            hipGetLastError();\n        }\n    };\n\n    // get captured graph without ending the capture in case it is external\n    hipGraph_t graph = get_capture_graph(cuda_stream);\n    if (!graph) {\n        clean_up();\n        return false;\n    }\n\n    // ensure that all forked streams are joined to the main capture stream by manually\n    // adding outstanding capture dependencies gathered from the graph leaf nodes\n    std::vector<hipGraphNode_t> stream_dependencies;\n    std::vector<hipGraphNode_t> leaf_nodes;\n    if (get_capture_dependencies(cuda_stream, stream_dependencies) && get_graph_leaf_nodes(graph, leaf_nodes)) {\n        // compute set difference to get unjoined dependencies\n        std::vector<hipGraphNode_t> unjoined_dependencies;\n        std::sort(stream_dependencies.begin(), stream_dependencies.end());\n        std::sort(leaf_nodes.begin(), leaf_nodes.end());\n        std::set_difference(\n            leaf_nodes.begin(), leaf_nodes.end(), stream_dependencies.begin(), stream_dependencies.end(),\n            std::back_inserter(unjoined_dependencies)\n        );\n        if (!unjoined_dependencies.empty()) {\n            check_cu(cuStreamUpdateCaptureDependencies_f(\n                cuda_stream, unjoined_dependencies.data(), unjoined_dependencies.size(),\n                hipStreamAddCaptureDependencies\n            ));\n            // ensure graph is still valid\n            if (get_capture_graph(cuda_stream) != graph) {\n                clean_up();\n                return false;\n            }\n        }\n    }\n\n    // check if this graph has unfreed allocations, which require special handling\n    std::vector<void*> unfreed_allocs;\n    for (auto it = g_graph_allocs.begin(); it != g_graph_allocs.end(); ++it) {\n        GraphAllocInfo& alloc_info = it->second;\n        if (alloc_info.capture_id == capture_id)\n            unfreed_allocs.push_back(it->first);\n    }\n\n    if (!unfreed_allocs.empty() || !tmp_allocs.empty()) {\n        // Create a user object that will notify us when the instantiated graph is destroyed.\n        // This works for external captures also, since we wouldn't otherwise know when\n        // the externally-created graph instance gets deleted.\n        // This callback is guaranteed to arrive after the graph has finished executing on the device,\n        // not necessarily when cudaGraphExecDestroy() is called.\n        GraphDestroyCallbackInfo* graph_info = new GraphDestroyCallbackInfo;\n        graph_info->context = context ? context : get_current_context();\n        graph_info->unfreed_allocs = unfreed_allocs;\n        graph_info->tmp_allocs = tmp_allocs;\n        hipUserObject_t user_object;\n        check_cuda(hipUserObjectCreate(&user_object, graph_info, on_graph_destroy, 1, hipUserObjectNoDestructorSync));\n        check_cuda(hipGraphRetainUserObject(graph, user_object, 1, hipGraphUserObjectMove));\n\n        // ensure graph is still valid\n        if (get_capture_graph(cuda_stream) != graph) {\n            clean_up();\n            return false;\n        }\n    }\n\n    // for external captures, we don't instantiate the graph ourselves, so we're done\n    if (external)\n        return true;\n\n    // end the capture\n    if (!check_cuda(hipStreamEndCapture(cuda_stream, &graph)))\n        return false;\n\n    // process deferred free list if no more captures are ongoing\n    if (g_captures.empty()) {\n        run_deferred_actions();\n    }\n\n    if (graph_ret)\n        *graph_ret = graph;\n\n    return true;\n}\n\nbool wp_capture_debug_dot_print(void* graph, const char* path, uint32_t flags)\n{\n    if (!check_cuda(hipGraphDebugDotPrint((hipGraph_t)graph, path, flags)))\n        return false;\n    return true;\n}\n\nbool wp_cuda_graph_create_exec(void* context, void* stream, void* graph, void** graph_exec_ret)\n{\n    ContextGuard guard(context);\n\n    hipGraphExec_t graph_exec = NULL;\n    if (!check_cuda(\n            hipGraphInstantiateWithFlags(&graph_exec, (hipGraph_t)graph, hipGraphInstantiateFlagAutoFreeOnLaunch)\n        ))\n        return false;\n\n    // Usually uploading the graph explicitly is optional, but when updating graph nodes (e.g., indirect dispatch)\n    // then the upload is required because otherwise the graph nodes that get updated might not yet be uploaded, which\n    // results in undefined behavior.\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    if (!check_cuda(hipGraphUpload(graph_exec, cuda_stream)))\n        return false;\n\n    if (graph_exec_ret)\n        *graph_exec_ret = graph_exec;\n\n    return true;\n}\n\nvoid* wp_cuda_graph_insert_memcpy(void* context, void* stream, void* dst, void* src, size_t size, int kind)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    hipMemcpyKind memcpy_kind = static_cast<hipMemcpyKind>(kind);\n\n    // Get the current stream capturing graph\n    hipStreamCaptureStatus capture_status = hipStreamCaptureStatusNone;\n    hipGraph_t graph = NULL;\n    const hipGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)))\n        return NULL;\n\n    // abort if not capturing\n    if (!graph || capture_status != hipStreamCaptureStatusActive) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return NULL;\n    }\n\n    hipGraphNode_t node = NULL;\n    if (!check_cuda(hipGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dst, src, size, memcpy_kind)))\n        return NULL;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &node, 1, hipStreamSetCaptureDependencies)))\n        return NULL;\n\n    return node;\n}\n\nbool wp_cuda_graph_insert_memcpy_batch(\n    void* context, void* stream, void** dsts, void** srcs, size_t* sizes, int* kinds, int count, void** nodes_ret\n)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    // Get the current stream capturing graph\n    hipStreamCaptureStatus capture_status = hipStreamCaptureStatusNone;\n    hipGraph_t graph = NULL;\n    const hipGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)))\n        return false;\n\n    // abort if not capturing\n    if (!graph || capture_status != hipStreamCaptureStatusActive) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n#if 1\n    // sequential version (copies executed on the same stream)\n    //\n    // TODO:\n    // - figure out why sequential is faster\n    // - overhead due to graph launch on multiple streams?\n    //\n\n    for (int i = 0; i < count; i++) {\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        hipMemcpyKind memcpy_kind = static_cast<hipMemcpyKind>(kinds[i]);\n        hipGraphNode_t node = NULL;\n        if (!check_cuda(\n                hipGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n        nodes_ret[i] = node;\n\n        if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &node, 1, hipStreamSetCaptureDependencies)))\n            return false;\n    }\n#else\n    // parallel version (copies can execute on multiple streams)\n    for (int i = 0; i < count; i++) {\n        hipMemcpyKind memcpy_kind = static_cast<hipMemcpyKind>(kinds[i]);\n        hipGraphNode_t node = NULL;\n        if (!check_cuda(\n                hipGraphAddMemcpyNode1D(&node, graph, capture_deps, dep_count, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n        nodes_ret[i] = node;\n    }\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(\n            cuda_stream, (hipGraphNode_t*)nodes_ret, count, hipStreamSetCaptureDependencies\n        )))\n        return false;\n#endif\n\n    return true;\n}\n\nbool wp_cuda_graph_update_memcpy(void* graph_exec, void* node, void* dst, void* src, size_t size, int kind)\n{\n    hipGraphExec_t cuda_graph_exec = static_cast<hipGraphExec_t>(graph_exec);\n    hipGraphNode_t cuda_node = static_cast<hipGraphNode_t>(node);\n    hipMemcpyKind memcpy_kind = static_cast<hipMemcpyKind>(kind);\n\n    if (!check_cuda(hipGraphExecMemcpyNodeSetParams1D(cuda_graph_exec, cuda_node, dst, src, size, memcpy_kind)))\n        return false;\n\n    return true;\n}\n\nbool wp_cuda_graph_update_memcpy_batch(\n    void* graph_exec, void** nodes, void** dsts, void** srcs, size_t* sizes, int* kinds, int count\n)\n{\n    hipGraphExec_t cuda_graph_exec = static_cast<hipGraphExec_t>(graph_exec);\n\n    for (int i = 0; i < count; i++) {\n        hipGraphNode_t cuda_node = static_cast<hipGraphNode_t>(nodes[i]);\n        hipMemcpyKind memcpy_kind = static_cast<hipMemcpyKind>(kinds[i]);\n\n        if (!check_cuda(\n                hipGraphExecMemcpyNodeSetParams1D(cuda_graph_exec, cuda_node, dsts[i], srcs[i], sizes[i], memcpy_kind)\n            ))\n            return false;\n    }\n\n    return true;\n}\n\n// Support for conditional graph nodes available with CUDA 12.4+.\n#if CUDA_VERSION >= 12040\n\n// CUBIN or PTX data for compiled conditional modules, loaded on demand, keyed on device architecture\nusing ModuleKey = std::pair<int, bool>;  // <arch, use_ptx>\nstatic std::map<ModuleKey, void*> g_conditional_modules;\n\n// Compile module with conditional helper kernels\nstatic void* compile_conditional_module(int arch, bool use_ptx)\n{\n    static const char* kernel_source = R\"(\n        typedef __device_builtin__ unsigned long long cudaGraphConditionalHandle;\n        extern \"C\" __device__ __cudart_builtin__ void cudaGraphSetConditional(cudaGraphConditionalHandle handle, unsigned int value);\n\n        extern \"C\" __global__ void set_conditional_if_handle_kernel(cudaGraphConditionalHandle handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n                cudaGraphSetConditional(handle, *value);\n        }\n\n        extern \"C\" __global__ void set_conditional_else_handle_kernel(cudaGraphConditionalHandle handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n                cudaGraphSetConditional(handle, !*value);\n        }\n\n        extern \"C\" __global__ void set_conditional_if_else_handles_kernel(cudaGraphConditionalHandle if_handle, cudaGraphConditionalHandle else_handle, int* value)\n        {\n            if (threadIdx.x + blockIdx.x * blockDim.x == 0)\n            {\n                cudaGraphSetConditional(if_handle, *value);\n                cudaGraphSetConditional(else_handle, !*value);\n            }\n        }\n    )\";\n\n    // avoid recompilation\n    ModuleKey key = { arch, use_ptx };\n    auto it = g_conditional_modules.find(key);\n    if (it != g_conditional_modules.end())\n        return it->second;\n\n    hiprtcProgram prog;\n    if (!check_nvrtc(hiprtcCreateProgram(&prog, kernel_source, \"conditional_kernels\", 0, NULL, NULL)))\n        return NULL;\n\n    char arch_opt[128];\n    if (use_ptx)\n        snprintf(arch_opt, sizeof(arch_opt), \"--gpu-architecture=compute_%d\", arch);\n    else\n        snprintf(arch_opt, sizeof(arch_opt), \"--gpu-architecture=sm_%d\", arch);\n\n    std::vector<const char*> opts;\n    opts.push_back(arch_opt);\n\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n    if (print_debug) {\n        printf(\"NVRTC options (conditional module, arch=%d, use_ptx=%s):\\n\", arch, use_ptx ? \"true\" : \"false\");\n        for (auto o : opts) {\n            printf(\"%s\\n\", o);\n        }\n    }\n\n    if (!check_nvrtc(hiprtcCompileProgram(prog, int(opts.size()), opts.data()))) {\n        size_t log_size;\n        if (check_nvrtc(hiprtcGetProgramLogSize(prog, &log_size))) {\n            std::vector<char> log(log_size);\n            if (check_nvrtc(hiprtcGetProgramLog(prog, log.data())))\n                fprintf(stderr, \"%s\", log.data());\n        }\n        hiprtcDestroyProgram(&prog);\n        return NULL;\n    }\n\n    // get output\n    char* output = NULL;\n    size_t output_size = 0;\n\n    if (use_ptx) {\n        check_nvrtc(hiprtcGetCodeSize(prog, &output_size));\n        if (output_size > 0) {\n            output = new char[output_size];\n            if (check_nvrtc(hiprtcGetCode(prog, output)))\n                g_conditional_modules[key] = output;\n        }\n    } else {\n        check_nvrtc(hiprtcGetBitcodeSize(prog, &output_size));\n        if (output_size > 0) {\n            output = new char[output_size];\n            if (check_nvrtc(hiprtcGetBitcode(prog, output)))\n                g_conditional_modules[key] = output;\n        }\n    }\n\n    hiprtcDestroyProgram(&prog);\n\n    // return CUBIN or PTX data\n    return output;\n}\n\n\n// Load module with conditional helper kernels\nstatic hipModule_t load_conditional_module(void* context, int arch, bool use_ptx)\n{\n    ContextInfo* context_info = get_context_info(context);\n    if (!context_info)\n        return NULL;\n\n    // check if already loaded\n    if (context_info->conditional_module)\n        return context_info->conditional_module;\n\n    // compile if needed\n    void* compiled_module = compile_conditional_module(arch, use_ptx);\n    if (!compiled_module) {\n        fprintf(stderr, \"Warp error: Failed to compile conditional kernels\\n\");\n        return NULL;\n    }\n\n    // load module (handles both PTX and CUBIN data automatically)\n    hipModule_t module = NULL;\n    if (!check_cu(cuModuleLoadDataEx_f(&module, compiled_module, 0, NULL, NULL))) {\n        fprintf(stderr, \"Warp error: Failed to load conditional kernels module\\n\");\n        return NULL;\n    }\n\n    context_info->conditional_module = module;\n\n    return module;\n}\n\nstatic hipFunction_t get_conditional_kernel(void* context, int arch, bool use_ptx, const char* name)\n{\n    // load module if needed\n    hipModule_t module = load_conditional_module(context, arch, use_ptx);\n    if (!module)\n        return NULL;\n\n    hipFunction_t kernel;\n    if (!check_cu(cuModuleGetFunction_f(&kernel, module, name))) {\n        fprintf(stderr, \"Warp error: Failed to get kernel %s\\n\", name);\n        return NULL;\n    }\n\n    return kernel;\n}\n\nbool wp_cuda_graph_pause_capture(void* context, void* stream, void** graph_ret)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    if (!check_cuda(hipStreamEndCapture(cuda_stream, (hipGraph_t*)graph_ret)))\n        return false;\n    return true;\n}\n\nbool wp_cuda_graph_resume_capture(void* context, void* stream, void* graph)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n    hipGraph_t cuda_graph = static_cast<hipGraph_t>(graph);\n\n    std::vector<hipGraphNode_t> leaf_nodes;\n    if (!get_graph_leaf_nodes(cuda_graph, leaf_nodes))\n        return false;\n\n    // Resume with the same capture mode the user picked at begin time so a\n    // pause/resume cycle (driven by conditional/while graph nodes) does not\n    // silently downgrade Global/Relaxed captures to ThreadLocal. The stream\n    // must already be known to Warp with an active CaptureInfo at this\n    // point because the resume path is only reached after a matching pause\n    // on a Warp-managed capture; if either is missing something is badly\n    // out of sync, fail fast rather than guess a mode.\n    StreamInfo* stream_info = get_stream_info(cuda_stream);\n    if (!stream_info) {\n        wp::set_error_string(\"Warp error: resume_capture called on unknown stream\");\n        return false;\n    }\n    CaptureInfo* capture = stream_info->capture;\n    if (!capture) {\n        wp::set_error_string(\"Warp error: resume_capture called on stream with no active capture\");\n        return false;\n    }\n    hipStreamCaptureMode resume_mode = capture->mode;\n\n    if (!check_cuda(hipStreamBeginCaptureToGraph(\n            cuda_stream, cuda_graph, leaf_nodes.data(), nullptr, leaf_nodes.size(), resume_mode\n        )))\n        return false;\n\n    return true;\n}\n\n// https://developer.nvidia.com/blog/constructing-cuda-graphs-with-dynamic-parameters/#combined_approach\n// https://developer.nvidia.com/blog/dynamic-control-flow-in-cuda-graphs-with-conditional-nodes/\n// condition is a gpu pointer\n// if_graph_ret and else_graph_ret should be NULL if not needed\nbool wp_cuda_graph_insert_if_else(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** if_graph_ret, void** else_graph_ret\n)\n{\n    bool has_if = if_graph_ret != NULL;\n    bool has_else = else_graph_ret != NULL;\n    int num_branches = int(has_if) + int(has_else);\n\n    // if neither the IF nor ELSE branches are required, it's a no-op\n    if (num_branches == 0)\n        return true;\n\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    // Get the current stream capturing graph\n    hipStreamCaptureStatus capture_status = hipStreamCaptureStatusNone;\n    hipGraph_t cuda_graph = NULL;\n    const hipGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // abort if not capturing\n    if (!cuda_graph || capture_status != hipStreamCaptureStatusActive) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n    // int driver_version = wp_cuda_driver_version();\n\n    // IF-ELSE nodes are only supported with CUDA 12.8+\n    // Somehow child graphs produce wrong results when an else branch is used\n    // Seems to be a bug in the CUDA driver: https://nvbugs/5241330\n    if (num_branches == 1 /*|| driver_version >= 12080*/) {\n        cudaGraphConditionalHandle handle;\n        check_cuda(cudaGraphConditionalHandleCreate(&handle, cuda_graph));\n\n        // run a kernel to set the condition handle from the condition pointer\n        // (need to negate the condition if only the else branch is used)\n        hipFunction_t kernel;\n        if (has_if)\n            kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n        else\n            kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_else_handle_kernel\");\n\n        if (!kernel) {\n            wp::set_error_string(\"Failed to get built-in conditional kernel\");\n            return false;\n        }\n\n        void* kernel_args[2];\n        kernel_args[0] = &handle;\n        kernel_args[1] = &condition;\n\n        if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n            return false;\n\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        // create conditional node\n        hipGraphNode_t condition_node;\n        hipGraphNodeParams condition_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        condition_params.conditional.handle = handle;\n        condition_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        condition_params.conditional.size = num_branches;\n        condition_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&condition_node, cuda_graph, capture_deps, NULL, dep_count, &condition_params)))\n            return false;\n\n        if (!check_cu(\n                cuStreamUpdateCaptureDependencies_f(cuda_stream, &condition_node, 1, hipStreamSetCaptureDependencies)\n            ))\n            return false;\n\n        if (num_branches == 1) {\n            if (has_if)\n                *if_graph_ret = condition_params.conditional.phGraph_out[0];\n            else\n                *else_graph_ret = condition_params.conditional.phGraph_out[0];\n        } else {\n            *if_graph_ret = condition_params.conditional.phGraph_out[0];\n            *else_graph_ret = condition_params.conditional.phGraph_out[1];\n        }\n    } else {\n        // Create IF node followed by an additional IF node with negated condition\n        cudaGraphConditionalHandle if_handle, else_handle;\n        check_cuda(cudaGraphConditionalHandleCreate(&if_handle, cuda_graph));\n        check_cuda(cudaGraphConditionalHandleCreate(&else_handle, cuda_graph));\n\n        hipFunction_t kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_else_handles_kernel\");\n        if (!kernel) {\n            wp::set_error_string(\"Failed to get built-in conditional kernel\");\n            return false;\n        }\n\n        void* kernel_args[3];\n        kernel_args[0] = &if_handle;\n        kernel_args[1] = &else_handle;\n        kernel_args[2] = &condition;\n\n        if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n            return false;\n\n        if (!check_cu(\n                cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n            ))\n            return false;\n\n        hipGraphNode_t if_node;\n        hipGraphNodeParams if_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        if_params.conditional.handle = if_handle;\n        if_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        if_params.conditional.size = 1;\n        if_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&if_node, cuda_graph, capture_deps, NULL, dep_count, &if_params)))\n            return false;\n\n        hipGraphNode_t else_node;\n        hipGraphNodeParams else_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n        else_params.conditional.handle = else_handle;\n        else_params.conditional.type = CU_GRAPH_COND_TYPE_IF;\n        else_params.conditional.size = 1;\n        else_params.conditional.ctx = get_current_context();\n        if (!check_cu(cuGraphAddNode_f(&else_node, cuda_graph, &if_node, NULL, 1, &else_params)))\n            return false;\n\n        if (!check_cu(\n                cuStreamUpdateCaptureDependencies_f(cuda_stream, &else_node, 1, hipStreamSetCaptureDependencies)\n            ))\n            return false;\n\n        *if_graph_ret = if_params.conditional.phGraph_out[0];\n        *else_graph_ret = else_params.conditional.phGraph_out[0];\n    }\n\n    return true;\n}\n\n// graph node type names for intelligible error reporting\nstatic const char* get_graph_node_type_name(hipGraphNodeType type)\n{\n    static const std::unordered_map<hipGraphNodeType, const char*> names {\n        { hipGraphNodeTypeKernel, \"kernel launch\" },\n        { hipGraphNodeTypeMemcpy, \"memcpy\" },\n        { hipGraphNodeTypeMemset, \"memset\" },\n        { hipGraphNodeTypeHost, \"host execution\" },\n        { hipGraphNodeTypeGraph, \"graph launch\" },\n        { hipGraphNodeTypeEmpty, \"empty node\" },\n        { hipGraphNodeTypeWaitEvent, \"event wait\" },\n        { hipGraphNodeTypeEventRecord, \"event record\" },\n        { hipGraphNodeTypeExtSemaphoreSignal, \"semaphore signal\" },\n        { hipGraphNodeTypeExtSemaphoreWait, \"semaphore wait\" },\n        { hipGraphNodeTypeMemAlloc, \"memory allocation\" },\n        { hipGraphNodeTypeMemFree, \"memory deallocation\" },\n        { hipGraphNodeTypeBatchMemOp, \"batched mem op\" },\n        { CU_GRAPH_NODE_TYPE_CONDITIONAL, \"conditional node\" },\n    };\n\n    auto it = names.find(type);\n    if (it != names.end())\n        return it->second;\n    else\n        return \"unknown node\";\n}\n\n// check if a graph can be launched as a child graph\nstatic bool is_valid_child_graph(void* child_graph)\n{\n    // disallowed child graph nodes according to the documentation of cuGraphAddChildGraphNode()\n    static const std::unordered_set<hipGraphNodeType> disallowed_nodes {\n        hipGraphNodeTypeMemAlloc,\n        hipGraphNodeTypeMemFree,\n        CU_GRAPH_NODE_TYPE_CONDITIONAL,\n    };\n\n    if (!child_graph) {\n        wp::set_error_string(\"Child graph is null\");\n        return false;\n    }\n\n    size_t num_nodes = 0;\n    if (!check_cuda(hipGraphGetNodes((hipGraph_t)child_graph, NULL, &num_nodes)))\n        return false;\n    std::vector<hipGraphNode_t> nodes(num_nodes);\n    if (!check_cuda(hipGraphGetNodes((hipGraph_t)child_graph, nodes.data(), &num_nodes)))\n        return false;\n\n    for (size_t i = 0; i < num_nodes; i++) {\n        // note: we use the driver API to get the node type, otherwise some nodes are not recognized correctly\n        hipGraphNodeType node_type;\n        check_cu(cuGraphNodeGetType_f(nodes[i], &node_type));\n        auto it = disallowed_nodes.find(node_type);\n        if (it != disallowed_nodes.end()) {\n            wp::set_error_string(\n                \"Child graph contains an unsupported operation (%s)\", get_graph_node_type_name(node_type)\n            );\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// check if a graph can be used as a conditional body graph\n// https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#condtional-node-body-graph-requirements\nbool wp_cuda_graph_check_conditional_body(void* body_graph)\n{\n    static const std::unordered_set<hipGraphNodeType> allowed_nodes {\n        hipGraphNodeTypeMemcpy, hipGraphNodeTypeMemset, hipGraphNodeTypeKernel,\n        hipGraphNodeTypeGraph,  hipGraphNodeTypeEmpty,  CU_GRAPH_NODE_TYPE_CONDITIONAL,\n    };\n\n    if (!body_graph) {\n        wp::set_error_string(\"Conditional body graph is null\");\n        return false;\n    }\n\n    size_t num_nodes = 0;\n    if (!check_cuda(hipGraphGetNodes((hipGraph_t)body_graph, NULL, &num_nodes)))\n        return false;\n    std::vector<hipGraphNode_t> nodes(num_nodes);\n    if (!check_cuda(hipGraphGetNodes((hipGraph_t)body_graph, nodes.data(), &num_nodes)))\n        return false;\n\n    for (size_t i = 0; i < num_nodes; i++) {\n        // note: we use the driver API to get the node type, otherwise some nodes are not recognized correctly\n        hipGraphNodeType node_type;\n        check_cu(cuGraphNodeGetType_f(nodes[i], &node_type));\n        if (allowed_nodes.find(node_type) == allowed_nodes.end()) {\n            wp::set_error_string(\n                \"Conditional body graph contains an unsupported operation (%s)\", get_graph_node_type_name(node_type)\n            );\n            return false;\n        } else if (node_type == hipGraphNodeTypeGraph) {\n            // check nested child graphs recursively\n            hipGraph_t child_graph = NULL;\n            if (!check_cuda(hipGraphChildGraphNodeGetGraph(nodes[i], &child_graph)))\n                return false;\n            if (!wp_cuda_graph_check_conditional_body(child_graph))\n                return false;\n        }\n    }\n\n    return true;\n}\n\nbool wp_cuda_graph_insert_child_graph(void* context, void* stream, void* child_graph)\n{\n    if (!is_valid_child_graph(child_graph))\n        return false;\n\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    // Get the current stream capturing graph\n    hipStreamCaptureStatus capture_status = hipStreamCaptureStatusNone;\n    void* cuda_graph = NULL;\n    const hipGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(cuStreamGetCaptureInfo_f(\n            cuda_stream, &capture_status, nullptr, (hipGraph_t*)&cuda_graph, &capture_deps, &dep_count\n        )))\n        return false;\n\n    if (!wp_cuda_graph_pause_capture(context, cuda_stream, &cuda_graph))\n        return false;\n\n    hipGraphNode_t body_node;\n    if (!check_cuda(hipGraphAddChildGraphNode(\n            &body_node, static_cast<hipGraph_t>(cuda_graph), capture_deps, dep_count,\n            static_cast<hipGraph_t>(child_graph)\n        )))\n        return false;\n\n    if (!wp_cuda_graph_resume_capture(context, cuda_stream, cuda_graph))\n        return false;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &body_node, 1, hipStreamSetCaptureDependencies)))\n        return false;\n\n    return true;\n}\n\nbool wp_cuda_graph_insert_while(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** body_graph_ret, uint64_t* handle_ret\n)\n{\n    // if there's no body, it's a no-op\n    if (!body_graph_ret)\n        return true;\n\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    // Get the current stream capturing graph\n    hipStreamCaptureStatus capture_status = hipStreamCaptureStatusNone;\n    hipGraph_t cuda_graph = NULL;\n    const hipGraphNode_t* capture_deps = NULL;\n    size_t dep_count = 0;\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // abort if not capturing\n    if (!cuda_graph || capture_status != hipStreamCaptureStatusActive) {\n        wp::set_error_string(\"Stream is not capturing\");\n        return false;\n    }\n\n    cudaGraphConditionalHandle handle;\n    if (!check_cuda(cudaGraphConditionalHandleCreate(&handle, cuda_graph)))\n        return false;\n\n    // launch a kernel to set the condition handle from condition pointer\n    hipFunction_t kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n    if (!kernel) {\n        wp::set_error_string(\"Failed to get built-in conditional kernel\");\n        return false;\n    }\n\n    void* kernel_args[2];\n    kernel_args[0] = &handle;\n    kernel_args[1] = &condition;\n\n    if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n        return false;\n\n    if (!check_cu(\n            cuStreamGetCaptureInfo_f(cuda_stream, &capture_status, nullptr, &cuda_graph, &capture_deps, &dep_count)\n        ))\n        return false;\n\n    // insert conditional graph node\n    hipGraphNode_t while_node;\n    hipGraphNodeParams while_params = { CU_GRAPH_NODE_TYPE_CONDITIONAL };\n    while_params.conditional.handle = handle;\n    while_params.conditional.type = CU_GRAPH_COND_TYPE_WHILE;\n    while_params.conditional.size = 1;\n    while_params.conditional.ctx = get_current_context();\n    if (!check_cu(cuGraphAddNode_f(&while_node, cuda_graph, capture_deps, NULL, dep_count, &while_params)))\n        return false;\n\n    if (!check_cu(cuStreamUpdateCaptureDependencies_f(cuda_stream, &while_node, 1, hipStreamSetCaptureDependencies)))\n        return false;\n\n    *body_graph_ret = while_params.conditional.phGraph_out[0];\n    *handle_ret = handle;\n\n    return true;\n}\n\nbool wp_cuda_graph_set_condition(void* context, void* stream, int arch, bool use_ptx, int* condition, uint64_t handle)\n{\n    ContextGuard guard(context);\n\n    hipStream_t cuda_stream = static_cast<hipStream_t>(stream);\n\n    // launch a kernel to set the condition handle from condition pointer\n    hipFunction_t kernel = get_conditional_kernel(context, arch, use_ptx, \"set_conditional_if_handle_kernel\");\n    if (!kernel) {\n        wp::set_error_string(\"Failed to get built-in conditional kernel\");\n        return false;\n    }\n\n    void* kernel_args[2];\n    kernel_args[0] = &handle;\n    kernel_args[1] = &condition;\n\n    if (!check_cu(cuLaunchKernel_f(kernel, 1, 1, 1, 1, 1, 1, 0, cuda_stream, kernel_args, NULL)))\n        return false;\n\n    return true;\n}\n\n#else\n// stubs for conditional graph node API if CUDA toolkit is too old.\n\nbool wp_cuda_graph_pause_capture(void* context, void* stream, void** graph_ret)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_resume_capture(void* context, void* stream, void* graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_if_else(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** if_graph_ret, void** else_graph_ret\n)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_while(\n    void* context, void* stream, int arch, bool use_ptx, int* condition, void** body_graph_ret, uint64_t* handle_ret\n)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_set_condition(void* context, void* stream, int arch, bool use_ptx, int* condition, uint64_t handle)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_insert_child_graph(void* context, void* stream, void* child_graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\nbool wp_cuda_graph_check_conditional_body(void* body_graph)\n{\n    wp::set_error_string(\"Warp error: Warp must be built with CUDA Toolkit 12.4+ to enable conditional graph nodes\");\n    return false;\n}\n\n#endif  // support for conditional graph nodes\n\n\nbool wp_cuda_graph_launch(void* graph_exec, void* stream)\n{\n    // TODO: allow naming graphs?\n    begin_cuda_range(WP_TIMING_GRAPH, stream, get_stream_context(stream), \"graph\");\n\n    bool result = check_cuda(hipGraphLaunch((hipGraphExec_t)graph_exec, (hipStream_t)stream));\n\n    end_cuda_range(WP_TIMING_GRAPH, stream);\n\n    return result;\n}\n\nbool wp_cuda_graph_destroy(void* context, void* graph)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        return check_cuda(hipGraphDestroy((hipGraph_t)graph));\n    } else {\n        GraphDestroyInfo info;\n        info.context = context ? context : get_current_context();\n        info.graph = graph;\n        g_deferred_graph_list.push_back(info);\n        return true;\n    }\n}\n\nbool wp_cuda_graph_exec_destroy(void* context, void* graph_exec)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        return check_cuda(hipGraphExecDestroy((hipGraphExec_t)graph_exec));\n    } else {\n        GraphDestroyInfo info;\n        info.context = context ? context : get_current_context();\n        info.graph_exec = graph_exec;\n        g_deferred_graph_list.push_back(info);\n        return true;\n    }\n}\n\nbool write_file(const char* data, size_t size, std::string filename, const char* mode)\n{\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n    if (print_debug) {\n        printf(\"Writing %zu B to %s (%s)\\n\", size, filename.c_str(), mode);\n    }\n    FILE* file = fopen(filename.c_str(), mode);\n    if (file) {\n        if (fwrite(data, 1, size, file) != size) {\n            fprintf(stderr, \"Warp error: Failed to write to output file '%s'\\n\", filename.c_str());\n            return false;\n        }\n        fclose(file);\n        return true;\n    } else {\n        fprintf(stderr, \"Warp error: Failed to open file '%s'\\n\", filename.c_str());\n        return false;\n    }\n}\n\n#if WP_ENABLE_MATHDX\nbool check_nvjitlink_result(nvJitLinkHandle handle, nvJitLinkResult result, const char* file, int line)\n{\n    if (result != NVJITLINK_SUCCESS) {\n        fprintf(stderr, \"nvJitLink error: %d on %s:%d\\n\", (int)result, file, line);\n        size_t lsize;\n        result = nvJitLinkGetErrorLogSize(handle, &lsize);\n        if (result == NVJITLINK_SUCCESS && lsize > 0) {\n            std::vector<char> log(lsize);\n            result = nvJitLinkGetErrorLog(handle, log.data());\n            if (result == NVJITLINK_SUCCESS) {\n                fprintf(stderr, \"%s\\n\", log.data());\n            }\n        }\n        return false;\n    } else {\n        return true;\n    }\n}\n#endif\n\nsize_t wp_cuda_compile_program(\n    const char* cuda_src,\n    const char* program_name,\n    int arch,\n    const char* arch_suffix,\n    const char* include_dir,\n    int num_cuda_include_dirs,\n    const char** cuda_include_dirs,\n    bool debug,\n    int optimization_level,\n    bool verbose,\n    bool verify_fp,\n    bool fast_math,\n    bool fuse_fp,\n    bool lineinfo,\n    bool compile_time_trace,\n    bool precompiled_headers,\n    const char* output_path,\n    const char* pch_dir,\n    size_t num_ltoirs,\n    char** ltoirs,\n    size_t* ltoir_sizes,\n    int* ltoir_input_types\n)\n{\n    // use file extension to determine whether to output PTX or CUBIN\n    const char* output_ext = strrchr(output_path, '.');\n    bool use_ptx = output_ext && strcmp(output_ext + 1, \"ptx\") == 0;\n    const bool print_debug = (std::getenv(\"WARP_DEBUG\") != nullptr);\n\n    // check include dir path len (path + option)\n    const int max_path = 4096 + 16;\n    if (strlen(include_dir) > max_path) {\n        fprintf(stderr, \"Warp error: Include path too long\\n\");\n        return size_t(-1);\n    }\n\n    if (print_debug) {\n        // Not available in all nvJitLink versions\n        // unsigned major = 0;\n        // unsigned minor = 0;\n        // nvJitLinkVersion(&major, &minor);\n        // printf(\"nvJitLink version %d.%d\\n\", major, minor);\n        int major = 0;\n        int minor = 0;\n        hiprtcVersion(&major, &minor);\n        printf(\"NVRTC version %d.%d\\n\", major, minor);\n    }\n\n    char include_opt[max_path];\n    strcpy(include_opt, \"--include-path=\");\n    strcat(include_opt, include_dir);\n\n    const int max_arch = 128;\n    char arch_opt[max_arch];\n    char arch_opt_lto[max_arch];\n\n    // arch_suffix is \"\" (no suffix), \"a\" (arch-specific), or \"f\" (family-specific)\n    const char* suffix = (arch_suffix != nullptr) ? arch_suffix : \"\";\n\n    if (use_ptx) {\n        snprintf(arch_opt, max_arch, \"--gpu-architecture=compute_%d%s\", arch, suffix);\n        snprintf(arch_opt_lto, max_arch, \"-arch=compute_%d%s\", arch, suffix);\n    } else {\n        snprintf(arch_opt, max_arch, \"--gpu-architecture=sm_%d%s\", arch, suffix);\n        snprintf(arch_opt_lto, max_arch, \"-arch=sm_%d%s\", arch, suffix);\n    }\n\n    std::vector<const char*> opts;\n    opts.push_back(arch_opt);\n    opts.push_back(include_opt);\n    opts.push_back(\"--std=c++17\");\n\n    // CUDA 12.9+ supports --Ofast-compile\n#if CUDA_VERSION >= 12090\n    // --Ofast-compile works inversely to normal -O optimization levels\n    switch (optimization_level) {\n    case 0:\n        opts.push_back(\"--Ofast-compile=max\");\n        break;\n    case 1:\n        opts.push_back(\"--Ofast-compile=mid\");\n        break;\n    case 2:\n        opts.push_back(\"--Ofast-compile=min\");\n        break;\n    default:\n        opts.push_back(\"--Ofast-compile=0\");\n        break;  // 3 and up\n    }\n#endif\n\n    // Vector to store dynamically created option strings\n    std::vector<std::string> stored_options;\n\n    if (precompiled_headers) {\n        // CUDA 12.8+ supports precompiled headers\n#if CUDA_VERSION >= 12080\n        opts.push_back(\"-pch\");\n#if CUDA_VERSION < 13000\n        // CUDA 12.x series puts .pch files in the current working directory unless explicitly set\n        if (pch_dir != nullptr) {\n            if (print_debug) {\n                printf(\"PCH directory: %s\\n\", pch_dir);\n            }\n            std::string pch_dir_opt = std::string(\"--pch-dir=\") + pch_dir;\n            stored_options.push_back(pch_dir_opt);\n            opts.push_back(stored_options.back().c_str());\n        }\n#endif\n#endif\n    }\n\n    if (debug) {\n        opts.push_back(\"--define-macro=_DEBUG\");\n        opts.push_back(\"--generate-line-info\");\n#ifndef _WIN32\n        opts.push_back(\"--device-debug\");  // -G\n#endif\n    } else {\n        opts.push_back(\"--define-macro=NDEBUG\");\n\n        if (lineinfo)\n            opts.push_back(\"--generate-line-info\");\n    }\n\n    if (verify_fp)\n        opts.push_back(\"--define-macro=WP_VERIFY_FP\");\n    else\n        opts.push_back(\"--undefine-macro=WP_VERIFY_FP\");\n\n#if WP_ENABLE_MATHDX\n    opts.push_back(\"--define-macro=WP_ENABLE_MATHDX=1\");\n#else\n    opts.push_back(\"--define-macro=WP_ENABLE_MATHDX=0\");\n#endif\n\n    if (fast_math)\n        opts.push_back(\"--use_fast_math\");\n\n    if (fuse_fp)\n        opts.push_back(\"--fmad=true\");\n    else\n        opts.push_back(\"--fmad=false\");\n\n    for (int i = 0; i < num_cuda_include_dirs; i++) {\n        stored_options.push_back(std::string(\"--include-path=\") + cuda_include_dirs[i]);\n        opts.push_back(stored_options.back().c_str());\n    }\n\n    opts.push_back(\"--device-as-default-execution-space\");\n    opts.push_back(\"--extra-device-vectorization\");\n    opts.push_back(\"--restrict\");\n    opts.push_back(\"--diag-suppress=177,550\");  // \"was declared but never referenced\", \"was set but never used\"\n\n    if (num_ltoirs > 0) {\n        opts.push_back(\"-dlto\");\n        opts.push_back(\"--relocatable-device-code=true\");\n    }\n\n    if (compile_time_trace) {\n#if CUDA_VERSION >= 12080\n        stored_options.push_back(\n            std::string(\"--fdevice-time-trace=\") + std::string(output_path).append(\"_compile-time-trace.json\")\n        );\n        opts.push_back(stored_options.back().c_str());\n#else\n        fprintf(stderr, \"Warp warning: CUDA version is less than 12.8, compile_time_trace is not supported\\n\");\n#endif\n    }\n\n    hiprtcProgram prog;\n    hiprtcResult res;\n\n    res = hiprtcCreateProgram(\n        &prog,  // prog\n        cuda_src,  // buffer\n        program_name,  // name\n        0,  // numHeaders\n        NULL,  // headers\n        NULL\n    );  // includeNames\n\n    if (!check_nvrtc(res))\n        return size_t(res);\n\n    if (print_debug) {\n        printf(\"NVRTC options:\\n\");\n        for (auto o : opts) {\n            printf(\"%s\\n\", o);\n        }\n    }\n    res = hiprtcCompileProgram(prog, int(opts.size()), opts.data());\n\n    if (!check_nvrtc(res) || verbose) {\n        // get program log\n        size_t log_size;\n        if (check_nvrtc(hiprtcGetProgramLogSize(prog, &log_size))) {\n            std::vector<char> log(log_size);\n            if (check_nvrtc(hiprtcGetProgramLog(prog, log.data()))) {\n                // todo: figure out better way to return this to python\n                if (res != HIPRTC_SUCCESS)\n                    fprintf(stderr, \"%s\", log.data());\n                else\n                    fprintf(stdout, \"%s\", log.data());\n            }\n        }\n\n        if (res != HIPRTC_SUCCESS) {\n            hiprtcDestroyProgram(&prog);\n            return size_t(res);\n        }\n    }\n\n    hiprtcResult (*get_output_size)(hiprtcProgram, size_t*);\n    hiprtcResult (*get_output_data)(hiprtcProgram, char*);\n    const char* output_mode;\n    if (num_ltoirs > 0) {\n#if WP_ENABLE_MATHDX\n        get_output_size = nvrtcGetLTOIRSize;\n        get_output_data = nvrtcGetLTOIR;\n        output_mode = \"wb\";\n#else\n        fprintf(stderr, \"Warp error: num_ltoirs > 0 but Warp was not built with MathDx support\\n\");\n        return size_t(-1);\n#endif\n    } else if (use_ptx) {\n        get_output_size = hiprtcGetCodeSize;\n        get_output_data = hiprtcGetCode;\n        output_mode = \"wt\";\n    } else {\n        get_output_size = hiprtcGetBitcodeSize;\n        get_output_data = hiprtcGetBitcode;\n        output_mode = \"wb\";\n    }\n\n    // save output\n    size_t output_size;\n    res = get_output_size(prog, &output_size);\n    if (check_nvrtc(res)) {\n        std::vector<char> output(output_size);\n        res = get_output_data(prog, output.data());\n        if (check_nvrtc(res)) {\n\n            // LTOIR case - need an extra step\n            if (num_ltoirs > 0) {\n#if WP_ENABLE_MATHDX\n                if (ltoir_input_types == nullptr || ltoirs == nullptr || ltoir_sizes == nullptr) {\n                    fprintf(\n                        stderr, \"Warp error: num_ltoirs > 0 but ltoir_input_types, ltoirs or ltoir_sizes are NULL\\n\"\n                    );\n                    return size_t(-1);\n                }\n                nvJitLinkHandle handle = nullptr;\n                std::vector<const char*> lopts = { \"-dlto\", arch_opt_lto };\n                if (use_ptx) {\n                    lopts.push_back(\"-ptx\");\n                }\n                if (print_debug) {\n                    printf(\"nvJitLink options:\\n\");\n                    for (auto o : lopts) {\n                        printf(\"%s\\n\", o);\n                    }\n                }\n                if (!check_nvjitlink(handle, nvJitLinkCreate(&handle, lopts.size(), lopts.data()))) {\n                    res = hiprtcResult(-1);\n                }\n                // Links\n                if (std::getenv(\"WARP_DUMP_LTOIR\")) {\n                    write_file(output.data(), output.size(), \"nvrtc_output.ltoir\", \"wb\");\n                }\n                if (!check_nvjitlink(\n                        handle,\n                        nvJitLinkAddData(handle, NVJITLINK_INPUT_LTOIR, output.data(), output.size(), \"nvrtc_output\")\n                    ))  // NVRTC business\n                {\n                    res = hiprtcResult(-1);\n                }\n                for (size_t ltoidx = 0; ltoidx < num_ltoirs; ltoidx++) {\n                    nvJitLinkInputType input_type = static_cast<nvJitLinkInputType>(ltoir_input_types[ltoidx]);\n                    const char* ext = \".unknown\";\n                    switch (input_type) {\n                    case NVJITLINK_INPUT_CUBIN:\n                        ext = \".cubin\";\n                        break;\n                    case NVJITLINK_INPUT_LTOIR:\n                        ext = \".ltoir\";\n                        break;\n                    case NVJITLINK_INPUT_FATBIN:\n                        ext = \".fatbin\";\n                        break;\n                    default:\n                        break;\n                    }\n                    if (std::getenv(\"WARP_DUMP_LTOIR\")) {\n                        write_file(\n                            ltoirs[ltoidx], ltoir_sizes[ltoidx],\n                            std::string(\"lto_online_\") + std::to_string(ltoidx) + ext, \"wb\"\n                        );\n                    }\n                    if (!check_nvjitlink(\n                            handle,\n                            nvJitLinkAddData(handle, input_type, ltoirs[ltoidx], ltoir_sizes[ltoidx], \"lto_online\")\n                        ))  // External LTOIR\n                    {\n                        res = hiprtcResult(-1);\n                    }\n                }\n                if (!check_nvjitlink(handle, nvJitLinkComplete(handle))) {\n                    res = hiprtcResult(-1);\n                } else {\n                    if (use_ptx) {\n                        size_t ptx_size = 0;\n                        check_nvjitlink(handle, nvJitLinkGetLinkedPtxSize(handle, &ptx_size));\n                        std::vector<char> ptx(ptx_size);\n                        check_nvjitlink(handle, nvJitLinkGetLinkedPtx(handle, ptx.data()));\n                        output = ptx;\n                    } else {\n                        size_t cubin_size = 0;\n                        check_nvjitlink(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n                        std::vector<char> cubin(cubin_size);\n                        check_nvjitlink(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n                        output = cubin;\n                    }\n                }\n                check_nvjitlink(handle, nvJitLinkDestroy(&handle));\n#else\n                fprintf(stderr, \"Warp error: num_ltoirs > 0 but Warp was not built with MathDx support\\n\");\n                return size_t(-1);\n#endif\n            }\n\n            if (!write_file(output.data(), output.size(), output_path, output_mode)) {\n                res = hiprtcResult(-1);\n            }\n        }\n    }\n\n    check_nvrtc(hiprtcDestroyProgram(&prog));\n\n    return res;\n}\n\n#if WP_ENABLE_MATHDX\nbool check_cufftdx_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuFFTDx error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool check_cublasdx_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuBLASDx error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool check_cusolver_result(commondxStatusType result, const char* file, int line)\n{\n    if (result != commondxStatusType::COMMONDX_SUCCESS) {\n        fprintf(stderr, \"libmathdx cuSOLVER error: %d on %s:%d\\n\", (int)result, file, line);\n        return false;\n    } else {\n        return true;\n    }\n}\n\nbool wp_cuda_compile_fft(\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int size,\n    int elements_per_thread,\n    int direction,\n    int precision,\n    int* shared_memory_size\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    CHECK_ANY(shared_memory_size != nullptr);\n    // Includes currently unused\n    CHECK_ANY(include_dirs == nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n\n    bool res = true;\n    cufftdxDescriptor h;\n    CHECK_CUFFTDX(cufftdxCreateDescriptor(&h));\n\n    // CUFFTDX_API_LMEM means each thread starts with a subset of the data\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_API, cufftdxApi::CUFFTDX_API_LMEM));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(\n        h, cufftdxOperatorType::CUFFTDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_SIZE, (long long)size));\n    CHECK_CUFFTDX(\n        cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_DIRECTION, (cufftdxDirection)direction)\n    );\n    CHECK_CUFFTDX(\n        cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_PRECISION, (commondxPrecision)precision)\n    );\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_SM, (long long)(arch * 10)));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(\n        h, cufftdxOperatorType::CUFFTDX_OPERATOR_ELEMENTS_PER_THREAD, (long long)(elements_per_thread)\n    ));\n    CHECK_CUFFTDX(cufftdxSetOperatorInt64(h, cufftdxOperatorType::CUFFTDX_OPERATOR_FFTS_PER_BLOCK, 1));\n\n    CHECK_CUFFTDX(cufftdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUFFTDX(cufftdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUFFTDX(cufftdxGetLTOIR(h, lto.size(), lto.data()));\n\n    long long int smem = 0;\n    CHECK_CUFFTDX(cufftdxGetTraitInt64(h, cufftdxTraitType::CUFFTDX_TRAIT_SHARED_MEMORY_SIZE, &smem));\n    *shared_memory_size = (int)smem;\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUFFTDX(cufftdxDestroyDescriptor(h));\n\n    return res;\n}\n\nbool wp_cuda_compile_dot(\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int M,\n    int N,\n    int K,\n    int precision_A,\n    int precision_B,\n    int precision_C,\n    int type,\n    int arrangement_A,\n    int arrangement_B,\n    int arrangement_C,\n    int num_threads\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    // Includes currently unused\n    CHECK_ANY(include_dirs == nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n\n    bool res = true;\n    cublasdxDescriptor h;\n    CHECK_CUBLASDX(cublasdxCreateDescriptor(&h));\n\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_FUNCTION, cublasdxFunction::CUBLASDX_FUNCTION_MM\n    ));\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_API, cublasdxApi::CUBLASDX_API_SMEM)\n    );\n    std::array<long long int, 3> precisions = { precision_A, precision_B, precision_C };\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64s(h, cublasdxOperatorType::CUBLASDX_OPERATOR_PRECISION, 3, precisions.data())\n    );\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_SM, (long long)(arch * 10)));\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64(h, cublasdxOperatorType::CUBLASDX_OPERATOR_TYPE, (cublasdxType)type));\n    std::array<long long int, 3> block_dim = { num_threads, 1, 1 };\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64s(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_BLOCK_DIM, block_dim.size(), block_dim.data()\n    ));\n    std::array<long long int, 3> size = { M, N, K };\n    CHECK_CUBLASDX(\n        cublasdxSetOperatorInt64s(h, cublasdxOperatorType::CUBLASDX_OPERATOR_SIZE, size.size(), size.data())\n    );\n    std::array<long long int, 3> arrangement = { arrangement_A, arrangement_B, arrangement_C };\n    CHECK_CUBLASDX(cublasdxSetOperatorInt64s(\n        h, cublasdxOperatorType::CUBLASDX_OPERATOR_ARRANGEMENT, arrangement.size(), arrangement.data()\n    ));\n\n    CHECK_CUBLASDX(cublasdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUBLASDX(cublasdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUBLASDX(cublasdxGetLTOIR(h, lto.size(), lto.data()));\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUBLASDX(cublasdxDestroyDescriptor(h));\n\n    return res;\n}\n\nbool wp_cuda_compile_solver(\n    const char* fatbin_output_path,\n    const char* ltoir_output_path,\n    const char* symbol_name,\n    int num_include_dirs,\n    const char** include_dirs,\n    const char* mathdx_include_dir,\n    int arch,\n    int M,\n    int N,\n    int K,\n    int function,\n    int side,\n    int diag,\n    int precision,\n    int arrangement_A,\n    int arrangement_B,\n    int fill_mode,\n    int num_threads\n)\n{\n\n    CHECK_ANY(ltoir_output_path != nullptr);\n    CHECK_ANY(symbol_name != nullptr);\n    CHECK_ANY(mathdx_include_dir == nullptr);\n    CHECK_ANY(num_include_dirs == 0);\n    CHECK_ANY(include_dirs == nullptr);\n\n    bool res = true;\n\n    cusolverdxDescriptor h { 0 };\n    CHECK_CUSOLVER(cusolverdxCreateDescriptor(&h));\n    std::array<long long int, 3> size = { M, N, K };\n    CHECK_CUSOLVER(\n        cusolverdxSetOperatorInt64s(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SIZE, size.size(), size.data())\n    );\n    std::array<long long int, 3> block_dim = { num_threads, 1, 1 };\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64s(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_BLOCK_DIM, block_dim.size(), block_dim.data()\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_TYPE, cusolverdxType::CUSOLVERDX_TYPE_REAL\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_API, cusolverdxApi::CUSOLVERDX_API_SMEM\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_FUNCTION, (cusolverdxFunction)function\n    ));\n    if (side >= 0) {\n        CHECK_CUSOLVER(\n            cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SIDE, (cusolverdxSide)side)\n        );\n    }\n    if (diag >= 0) {\n        CHECK_CUSOLVER(\n            cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_DIAG, (cusolverdxDiag)diag)\n        );\n    }\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_EXECUTION, commondxExecution::COMMONDX_EXECUTION_BLOCK\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_PRECISION, (commondxPrecision)precision\n    ));\n    std::array<long long int, 2> arrangement = { arrangement_A, arrangement_B };\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64s(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_ARRANGEMENT, arrangement.size(), arrangement.data()\n    ));\n    CHECK_CUSOLVER(cusolverdxSetOperatorInt64(\n        h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_FILL_MODE, (cusolverdxFillMode)fill_mode\n    ));\n    CHECK_CUSOLVER(\n        cusolverdxSetOperatorInt64(h, cusolverdxOperatorType::CUSOLVERDX_OPERATOR_SM, (long long)(arch * 10))\n    );\n\n    CHECK_CUSOLVER(cusolverdxSetOptionStr(h, commondxOption::COMMONDX_OPTION_SYMBOL_NAME, symbol_name));\n\n    size_t lto_size = 0;\n    CHECK_CUSOLVER(cusolverdxGetLTOIRSize(h, &lto_size));\n\n    std::vector<char> lto(lto_size);\n    CHECK_CUSOLVER(cusolverdxGetLTOIR(h, lto.size(), lto.data()));\n\n    // This fatbin is universal, ie it is the same for any instantiations of a cusolver device function\n    size_t fatbin_size = 0;\n    CHECK_CUSOLVER(cusolverdxGetUniversalFATBINSize(h, &fatbin_size));\n\n    std::vector<char> fatbin(fatbin_size);\n    CHECK_CUSOLVER(cusolverdxGetUniversalFATBIN(h, fatbin.size(), fatbin.data()));\n\n    if (!write_file(lto.data(), lto.size(), ltoir_output_path, \"wb\")) {\n        res = false;\n    }\n\n    if (!write_file(fatbin.data(), fatbin.size(), fatbin_output_path, \"wb\")) {\n        res = false;\n    }\n\n    CHECK_CUSOLVER(cusolverdxDestroyDescriptor(h));\n\n    return res;\n}\n\n#endif\n\nvoid* wp_cuda_load_module(void* context, const char* path)\n{\n    ContextGuard guard(context);\n\n    // use file extension to determine whether to load PTX or CUBIN\n    const char* input_ext = strrchr(path, '.');\n    bool load_ptx = input_ext && strcmp(input_ext + 1, \"ptx\") == 0;\n\n    std::vector<char> input;\n\n    FILE* file = fopen(path, \"rb\");\n    if (file) {\n        fseek(file, 0, SEEK_END);\n        size_t length = ftell(file);\n        fseek(file, 0, SEEK_SET);\n\n        input.resize(length + 1);\n        if (fread(input.data(), 1, length, file) != length) {\n            fprintf(stderr, \"Warp error: Failed to read input file '%s'\\n\", path);\n            fclose(file);\n            return NULL;\n        }\n        fclose(file);\n\n        input[length] = '\\0';\n    } else {\n        fprintf(stderr, \"Warp error: Failed to open input file '%s'\\n\", path);\n        return NULL;\n    }\n\n    int driver_cuda_version = 0;\n    hipModule_t module = NULL;\n\n    if (load_ptx) {\n        if (check_cu(cuDriverGetVersion_f(&driver_cuda_version)) && driver_cuda_version >= CUDA_VERSION) {\n            // let the driver compile the PTX\n\n            hipJitOption options[2];\n            void* option_vals[2];\n            char error_log[8192] = \"\";\n            unsigned int log_size = 8192;\n            // Set up loader options\n            // Pass a buffer for error message\n            options[0] = hipJitOptionErrorLogBuffer;\n            option_vals[0] = (void*)error_log;\n            // Pass the size of the error buffer\n            options[1] = hipJitOptionErrorLogBufferSizeBytes;\n            option_vals[1] = (void*)(size_t)log_size;\n\n            if (!check_cu(cuModuleLoadDataEx_f(&module, input.data(), 2, options, option_vals))) {\n                fprintf(stderr, \"Warp error: Loading PTX module failed\\n\");\n                // print error log if not empty\n                if (*error_log)\n                    fprintf(stderr, \"PTX loader error:\\n%s\\n\", error_log);\n                return NULL;\n            }\n        } else {\n            // manually compile the PTX and load as CUBIN\n\n            ContextInfo* context_info = get_context_info(static_cast<hipCtx_t>(context));\n            if (!context_info || !context_info->device_info) {\n                fprintf(stderr, \"Warp error: Failed to determine target architecture\\n\");\n                return NULL;\n            }\n\n            int arch = context_info->device_info->arch;\n\n            char arch_opt[128];\n            sprintf(arch_opt, \"--gpu-name=sm_%d\", arch);\n\n            const char* compiler_options[] = { arch_opt };\n\n            nvPTXCompilerHandle compiler = NULL;\n            if (!check_nvptx(nvPTXCompilerCreate(&compiler, input.size(), input.data())))\n                return NULL;\n\n            if (!check_nvptx(nvPTXCompilerCompile(\n                    compiler, sizeof(compiler_options) / sizeof(*compiler_options), compiler_options\n                )))\n                return NULL;\n\n            size_t cubin_size = 0;\n            if (!check_nvptx(nvPTXCompilerGetCompiledProgramSize(compiler, &cubin_size)))\n                return NULL;\n\n            std::vector<char> cubin(cubin_size);\n            if (!check_nvptx(nvPTXCompilerGetCompiledProgram(compiler, cubin.data())))\n                return NULL;\n\n            check_nvptx(nvPTXCompilerDestroy(&compiler));\n\n            if (!check_cu(cuModuleLoadDataEx_f(&module, cubin.data(), 0, NULL, NULL))) {\n                fprintf(stderr, \"Warp CUDA error: Loading module failed\\n\");\n                return NULL;\n            }\n        }\n    } else {\n        // load CUBIN\n        if (!check_cu(cuModuleLoadDataEx_f(&module, input.data(), 0, NULL, NULL))) {\n            fprintf(stderr, \"Warp CUDA error: Loading module failed\\n\");\n            return NULL;\n        }\n    }\n\n    return module;\n}\n\nvoid wp_cuda_unload_module(void* context, void* module)\n{\n    // ensure there are no graph captures in progress\n    if (g_captures.empty()) {\n        ContextGuard guard(context);\n        check_cu(cuModuleUnload_f((hipModule_t)module));\n    } else {\n        // defer until graph capture completes\n        ModuleInfo module_info;\n        module_info.context = context ? context : get_current_context();\n        module_info.module = module;\n        g_deferred_module_list.push_back(module_info);\n    }\n}\n\n\nint wp_cuda_get_max_shared_memory(void* context)\n{\n    ContextInfo* info = get_context_info(context);\n    if (!info)\n        return -1;\n\n    int max_smem_bytes = info->device_info->max_smem_bytes;\n    return max_smem_bytes;\n}\n\nbool wp_cuda_configure_kernel_shared_memory(void* kernel, int size)\n{\n    int requested_smem_bytes = size;\n\n    // configure shared memory\n    hipError_t res = cuFuncSetAttribute_f(\n        (hipFunction_t)kernel, HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, requested_smem_bytes\n    );\n    if (res != hipSuccess)\n        return false;\n\n    return true;\n}\n\nvoid* wp_cuda_get_kernel(void* context, void* module, const char* name)\n{\n    ContextGuard guard(context);\n\n    hipFunction_t kernel = NULL;\n    if (!check_cu(cuModuleGetFunction_f(&kernel, (hipModule_t)module, name))) {\n        fprintf(stderr, \"Warp CUDA error: Failed to lookup kernel function %s in module\\n\", name);\n        return NULL;\n    }\n\n    g_kernel_names[kernel] = name;\n    return kernel;\n}\n\nsize_t wp_cuda_launch_kernel(\n    void* context,\n    void* kernel,\n    size_t dim,\n    int max_blocks,\n    int block_dim,\n    int shared_memory_bytes,\n    void** args,\n    void* stream,\n    const APICLaunchInfo* apic_info\n)\n{\n    ContextGuard guard(context);\n\n    if (block_dim <= 0) {\n#if defined(_DEBUG)\n        fprintf(stderr, \"Warp warning: Launch got block_dim %d. Setting to 256.\\n\", block_dim);\n#endif\n        block_dim = 256;\n    }\n\n    // CUDA specs up to compute capability 9.0 says the max x-dim grid is 2**31-1, so\n    // grid_dim is fine as an int for the near future\n    int grid_dim = (dim + block_dim - 1) / block_dim;\n\n    if (max_blocks <= 0) {\n        max_blocks = 2147483647;\n    }\n\n    if (grid_dim < 0) {\n#if defined(_DEBUG)\n        fprintf(\n            stderr,\n            \"Warp warning: Overflow in grid dimensions detected for %zu total elements and 256 threads \"\n            \"per block.\\n    Setting block count to %d.\\n\",\n            dim, max_blocks\n        );\n#endif\n        grid_dim = max_blocks;\n    } else {\n        if (grid_dim > max_blocks) {\n            grid_dim = max_blocks;\n        }\n    }\n\n    begin_cuda_range(WP_TIMING_KERNEL, stream, context, get_cuda_kernel_name(kernel));\n\n    hipError_t res = cuLaunchKernel_f(\n        (hipFunction_t)kernel, grid_dim, 1, 1, block_dim, 1, 1, shared_memory_bytes, static_cast<hipStream_t>(stream), args, 0\n    );\n\n    check_cu(res);\n\n    end_cuda_range(WP_TIMING_KERNEL, stream);\n\n    // APIC recording: record kernel launch to byte stream if capturing\n    if (apic_info) {\n        APICState state = wp_apic_get_recording_state();\n        if (state) {\n            // Read shape/ndim/size from the launch_bounds_t* in args[0] (see builtin.h).\n            int shape[APIC_LAUNCH_MAX_DIMS] = {};\n            int ndim = 0;\n            uint64_t launch_size = dim;\n            if (args && args[0]) {\n                const auto* lb = static_cast<const wp::launch_bounds_t*>(args[0]);\n                ndim = lb->ndim;\n                if (ndim < 1)\n                    ndim = 1;\n                if (ndim > APIC_LAUNCH_MAX_DIMS)\n                    ndim = APIC_LAUNCH_MAX_DIMS;\n                for (int d = 0; d < ndim; d++)\n                    shape[d] = lb->shape[d];\n                launch_size = lb->size;\n            } else {\n                ndim = 1;\n                shape[0] = (int)dim;\n            }\n\n            apic_record_kernel_launch(\n                state, apic_info->kernel_key, apic_info->module_hash, apic_info->is_forward, shape, ndim, launch_size,\n                max_blocks, block_dim, shared_memory_bytes, apic_info->params, apic_info->num_params\n            );\n        }\n    }\n\n    return res;\n}\n\nbool wp_cuda_get_suggested_block_size(\n    void* context, void* kernel, int shared_memory_bytes, int* block_size_out, int* min_grid_size_out\n)\n{\n    ContextGuard guard(context);\n\n    int min_grid_size = 0;\n    int block_size = 0;\n    hipError_t res = cuOccupancyMaxPotentialBlockSize_f(\n        &min_grid_size, &block_size, (hipFunction_t)kernel, NULL, shared_memory_bytes, 0\n    );\n\n    if (!check_cu(res))\n        return false;\n\n    *block_size_out = block_size;\n    *min_grid_size_out = min_grid_size;\n    return true;\n}\n\nbool wp_cuda_graphics_map(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    return check_cu(cuGraphicsMapResources_f(1, (hipGraphicsResource_t*)resource, get_current_stream()));\n}\n\nvoid wp_cuda_graphics_unmap(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    check_cu(cuGraphicsUnmapResources_f(1, (hipGraphicsResource_t*)resource, get_current_stream()));\n}\n\nvoid wp_cuda_graphics_device_ptr_and_size(void* context, void* resource, uint64_t* ptr, size_t* size)\n{\n    ContextGuard guard(context);\n\n    hipDeviceptr_t device_ptr;\n    size_t bytes;\n    check_cu(cuGraphicsResourceGetMappedPointer_f(&device_ptr, &bytes, *(hipGraphicsResource_t*)resource));\n\n    *ptr = device_ptr;\n    *size = bytes;\n}\n\nvoid* wp_cuda_graphics_register_gl_buffer(void* context, uint32_t gl_buffer, unsigned int flags)\n{\n    ContextGuard guard(context);\n\n    hipGraphicsResource_t* resource = new hipGraphicsResource_t;\n    bool success = check_cu(cuGraphicsGLRegisterBuffer_f(resource, gl_buffer, flags));\n    if (!success) {\n        delete resource;\n        return NULL;\n    }\n\n    return resource;\n}\n\nvoid* wp_cuda_graphics_register_gl_image(void* context, uint32_t image, uint32_t target, unsigned int flags)\n{\n    ContextGuard guard(context);\n\n    hipGraphicsResource_t* resource = new hipGraphicsResource_t;\n    bool success = check_cu(cuGraphicsGLRegisterImage_f(resource, image, target, flags));\n    if (!success) {\n        delete resource;\n        return NULL;\n    }\n\n    return resource;\n}\n\nuint64_t wp_cuda_graphics_sub_resource_get_mapped_array(\n    void* context, void* resource, unsigned int array_index, unsigned int mip_level\n)\n{\n    ContextGuard guard(context);\n\n    hipArray_t cuda_array = NULL;\n    check_cu(\n        cuGraphicsSubResourceGetMappedArray_f(&cuda_array, *(hipGraphicsResource_t*)resource, array_index, mip_level)\n    );\n    return reinterpret_cast<uint64_t>(cuda_array);\n}\n\nvoid wp_cuda_graphics_unregister_resource(void* context, void* resource)\n{\n    ContextGuard guard(context);\n\n    hipGraphicsResource_t* res = (hipGraphicsResource_t*)resource;\n    check_cu(cuGraphicsUnregisterResource_f(*res));\n    delete res;\n}\n\nvoid wp_cuda_timing_begin(int flags) { g_cuda_timing_state = new CudaTimingState(flags, g_cuda_timing_state); }\n\nint wp_cuda_timing_get_result_count()\n{\n    if (g_cuda_timing_state)\n        return int(g_cuda_timing_state->ranges.size());\n    return 0;\n}\n\nvoid wp_cuda_timing_end(timing_result_t* results, int size)\n{\n    if (!g_cuda_timing_state)\n        return;\n\n    // number of results to write to the user buffer\n    int count = std::min(wp_cuda_timing_get_result_count(), size);\n\n    // compute timings and write results\n    for (int i = 0; i < count; i++) {\n        const CudaTimingRange& range = g_cuda_timing_state->ranges[i];\n        timing_result_t& result = results[i];\n        result.context = range.context;\n        result.name = range.name;\n        result.flag = range.flag;\n        check_cuda(hipEventElapsedTime(&result.elapsed, range.start, range.end));\n    }\n\n    // release events\n    for (CudaTimingRange& range : g_cuda_timing_state->ranges) {\n        check_cu(cuEventDestroy_f(range.start));\n        check_cu(cuEventDestroy_f(range.end));\n    }\n\n    // restore previous state\n    CudaTimingState* parent_state = g_cuda_timing_state->parent;\n    delete g_cuda_timing_state;\n    g_cuda_timing_state = parent_state;\n}\n\n// #include \"spline.inl\"\n// #include \"volume.inl\"\n\n// APIC (API Capture) implementation\n#include \"apic.cu\"\n", "source": "/kaggle/working/warp/warp/native/warp.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"hashgrid.h\"\n#include \"sort.h\"\n\nextern CUcontext get_current_context();\n\nnamespace wp {\n\ntemplate <typename Type> __global__ void compute_cell_indices(HashGrid_t<Type> grid, wp::array_t<vec_t<3, Type>> points)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < points.shape[0]) {\n        const vec_t<3, Type>& point = wp::index(points, tid);\n        grid.point_cells[tid] = hash_grid_index(grid, point);\n        grid.point_ids[tid] = tid;\n    }\n}\n\n__global__ void compute_cell_offsets(int* cell_starts, int* cell_ends, const int* point_cells, int num_points)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // compute cell start / end\n    if (tid < num_points) {\n        // scan the particle-cell array to find the start and end\n        const int c = point_cells[tid];\n\n        if (tid == 0)\n            cell_starts[c] = 0;\n        else {\n            const int p = point_cells[tid - 1];\n\n            if (c != p) {\n                cell_starts[c] = tid;\n                cell_ends[p] = tid;\n            }\n        }\n\n        if (tid == num_points - 1) {\n            cell_ends[c] = tid + 1;\n        }\n    }\n}\n\ntemplate <typename Type>\nvoid hash_grid_rebuild_device(const wp::HashGrid_t<Type>& grid, const wp::array_t<vec_t<3, Type>>& points)\n{\n    ContextGuard guard(grid.context);\n\n    int num_points = points.shape[0];\n\n    wp_launch_device(WP_CURRENT_CONTEXT, (wp::compute_cell_indices<Type>), num_points, (grid, points));\n\n    radix_sort_pairs_device(WP_CURRENT_CONTEXT, grid.point_cells, grid.point_ids, num_points);\n\n    const int num_cells = grid.dim_x * grid.dim_y * grid.dim_z;\n\n    wp_memset_device(WP_CURRENT_CONTEXT, grid.cell_starts, 0, sizeof(int) * num_cells);\n    wp_memset_device(WP_CURRENT_CONTEXT, grid.cell_ends, 0, sizeof(int) * num_cells);\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_cell_offsets, num_points,\n        (grid.cell_starts, grid.cell_ends, grid.point_cells, num_points)\n    );\n}\n\n// Explicit template instantiations\ntemplate void hash_grid_rebuild_device<half>(const HashGrid_t<half>&, const array_t<vec_t<3, half>>&);\ntemplate void hash_grid_rebuild_device<float>(const HashGrid_t<float>&, const array_t<vec_t<3, float>>&);\ntemplate void hash_grid_rebuild_device<double>(const HashGrid_t<double>&, const array_t<vec_t<3, double>>&);\n\n\n}  // namespace wp\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"hashgrid.h\"\n#include \"sort.h\"\n\nextern hipCtx_t get_current_context();\n\nnamespace wp {\n\ntemplate <typename Type> __global__ void compute_cell_indices(HashGrid_t<Type> grid, wp::array_t<vec_t<3, Type>> points)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < points.shape[0]) {\n        const vec_t<3, Type>& point = wp::index(points, tid);\n        grid.point_cells[tid] = hash_grid_index(grid, point);\n        grid.point_ids[tid] = tid;\n    }\n}\n\n__global__ void compute_cell_offsets(int* cell_starts, int* cell_ends, const int* point_cells, int num_points)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    // compute cell start / end\n    if (tid < num_points) {\n        // scan the particle-cell array to find the start and end\n        const int c = point_cells[tid];\n\n        if (tid == 0)\n            cell_starts[c] = 0;\n        else {\n            const int p = point_cells[tid - 1];\n\n            if (c != p) {\n                cell_starts[c] = tid;\n                cell_ends[p] = tid;\n            }\n        }\n\n        if (tid == num_points - 1) {\n            cell_ends[c] = tid + 1;\n        }\n    }\n}\n\ntemplate <typename Type>\nvoid hash_grid_rebuild_device(const wp::HashGrid_t<Type>& grid, const wp::array_t<vec_t<3, Type>>& points)\n{\n    ContextGuard guard(grid.context);\n\n    int num_points = points.shape[0];\n\n    wp_launch_device(WP_CURRENT_CONTEXT, (wp::compute_cell_indices<Type>), num_points, (grid, points));\n\n    radix_sort_pairs_device(WP_CURRENT_CONTEXT, grid.point_cells, grid.point_ids, num_points);\n\n    const int num_cells = grid.dim_x * grid.dim_y * grid.dim_z;\n\n    wp_memset_device(WP_CURRENT_CONTEXT, grid.cell_starts, 0, sizeof(int) * num_cells);\n    wp_memset_device(WP_CURRENT_CONTEXT, grid.cell_ends, 0, sizeof(int) * num_cells);\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_cell_offsets, num_points,\n        (grid.cell_starts, grid.cell_ends, grid.point_cells, num_points)\n    );\n}\n\n// Explicit template instantiations\ntemplate void hash_grid_rebuild_device<half>(const HashGrid_t<half>&, const array_t<vec_t<3, half>>&);\ntemplate void hash_grid_rebuild_device<float>(const HashGrid_t<float>&, const array_t<vec_t<3, float>>&);\ntemplate void hash_grid_rebuild_device<double>(const HashGrid_t<double>&, const array_t<vec_t<3, double>>&);\n\n\n}  // namespace wp\n", "source": "/kaggle/working/warp/warp/native/hashgrid.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"volume_builder.h\"\n\n#include <cub/cub.cuh>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n#include <nanovdb/tools/cuda/PointsToGrid.cuh>\n\n#if defined(__NVCC_DIAG_PRAGMA_SUPPORT__)\n// dynamic initialization is not supported for a function-scope static __shared__ variable within a\n// __device__/__global__ function\n#pragma nv_diag_suppress 20054\n#elif defined(__NVCC__)\n#pragma diag_suppress 20054\n#endif\nnamespace {\n/// Resource class following interface of nanovdb::DeviceResource as expected by nanovdb::PointsToGrid\nclass Resource {\npublic:\n    // cudaMalloc aligns memory to 256 bytes by default\n    static constexpr size_t DEFAULT_ALIGNMENT = 256;\n\n    static void* allocateAsync(size_t bytes, size_t, cudaStream_t stream)\n    {\n        void* d_ptr = wp_alloc_device(WP_CURRENT_CONTEXT, bytes, \"(native:volume_builder)\");\n        cudaCheckError();\n        return d_ptr;\n    }\n\n    static void deallocateAsync(void* d_ptr, size_t, size_t, cudaStream_t stream)\n    {\n        wp_free_device(WP_CURRENT_CONTEXT, d_ptr);\n    }\n};\n\n/// @brief  Implementation of NanoVDB's DeviceBuffer that uses warp allocators\nclass DeviceBuffer {\n    uint64_t mSize;  // total number of bytes managed by this buffer (assumed to be identical for host and device)\n    void *mCpuData, *mGpuData;  // raw pointers to the host and device buffers\n    bool mManaged;\n\npublic:\n    /// @brief Static factory method that return an instance of this buffer\n    /// @param size byte size of buffer to be initialized\n    /// @param dummy this argument is currently ignored but required to match the API of the HostBuffer\n    /// @param device id of the device on which to initialize the buffer\n    /// @param stream optional stream argument (defaults to stream NULL)\n    /// @return An instance of this class using move semantics\n    static DeviceBuffer create(\n        uint64_t size, const DeviceBuffer* dummy = nullptr, int device = cudaCpuDeviceId, cudaStream_t stream = nullptr\n    )\n    {\n        return DeviceBuffer(size, device, stream);\n    }\n\n    /// @brief Static factory method that return an instance of this buffer that wraps externally managed memory\n    /// @param size byte size of buffer specified by external memory\n    /// @param cpuData pointer to externally managed host memory\n    /// @param gpuData pointer to externally managed device memory\n    /// @return An instance of this class using move semantics\n#ifdef __NVCC__\n#pragma nv_diag_suppress 177\n#endif\n    static DeviceBuffer create(uint64_t size, void* cpuData, void* gpuData)\n    {\n        return DeviceBuffer(size, cpuData, gpuData);\n    }\n#ifdef __NVCC__\n#pragma nv_diag_default 177\n#endif\n\n    /// @brief Constructor\n    /// @param size byte size of buffer to be initialized\n    /// @param host If true buffer is initialized only on the host/CPU, else on the device/GPU\n    /// @param stream optional stream argument (defaults to stream NULL)\n    DeviceBuffer(uint64_t size = 0, int device = cudaCpuDeviceId, cudaStream_t stream = nullptr)\n        : mSize(0)\n        , mCpuData(nullptr)\n        , mGpuData(nullptr)\n        , mManaged(false)\n    {\n        if (size > 0)\n            this->init(size, device, stream);\n    }\n\n    DeviceBuffer(uint64_t size, void* cpuData, void* gpuData)\n        : mSize(size)\n        , mCpuData(cpuData)\n        , mGpuData(gpuData)\n        , mManaged(false)\n    {\n    }\n\n    /// @brief Disallow copy-construction\n    DeviceBuffer(const DeviceBuffer&) = delete;\n\n    /// @brief Move copy-constructor\n    DeviceBuffer(DeviceBuffer&& other) noexcept\n        : mSize(other.mSize)\n        , mCpuData(other.mCpuData)\n        , mGpuData(other.mGpuData)\n        , mManaged(other.mManaged)\n    {\n        other.mSize = 0;\n        other.mCpuData = nullptr;\n        other.mGpuData = nullptr;\n        other.mManaged = false;\n    }\n\n    /// @brief Disallow copy assignment operation\n    DeviceBuffer& operator=(const DeviceBuffer&) = delete;\n\n    /// @brief Move copy assignment operation\n    DeviceBuffer& operator=(DeviceBuffer&& other) noexcept\n    {\n        this->clear();\n        mSize = other.mSize;\n        mCpuData = other.mCpuData;\n        mGpuData = other.mGpuData;\n        mManaged = other.mManaged;\n        other.mSize = 0;\n        other.mCpuData = nullptr;\n        other.mGpuData = nullptr;\n        other.mManaged = false;\n        return *this;\n    }\n\n    /// @brief Destructor frees memory on both the host and device\n    ~DeviceBuffer() { this->clear(); };\n\n    /// @brief Initialize buffer\n    /// @param size byte size of buffer to be initialized\n    /// @param device id of the device on which to initialize the buffer\n    /// @note All existing buffers are first cleared\n    /// @warning size is expected to be non-zero. Use clear() clear buffer!\n    void init(uint64_t size, int device = cudaCpuDeviceId, void* stream = nullptr)\n    {\n        if (mSize > 0)\n            this->clear(stream);\n        NANOVDB_ASSERT(size > 0);\n        if (device == cudaCpuDeviceId) {\n            mCpuData = wp_alloc_pinned(size, \"(native:volume_builder)\");\n        } else {\n            mGpuData = wp_alloc_device(WP_CURRENT_CONTEXT, size, \"(native:volume_builder)\");\n        }\n        cudaCheckError();\n        mSize = size;\n        mManaged = true;\n    }\n\n    /// @brief Returns a raw pointer to the host/CPU buffer managed by this allocator.\n    /// @warning Note that the pointer can be NULL!\n    void* data() const { return mCpuData; }\n\n    /// @brief Returns a raw pointer to the device/GPU buffer managed by this allocator.\n    /// @warning Note that the pointer can be NULL!\n    void* deviceData() const { return mGpuData; }\n\n    /// @brief Returns the size in bytes of the raw memory buffer managed by this allocator.\n#ifdef __NVCC__\n#pragma nv_diag_suppress 177\n#endif\n    uint64_t size() const { return mSize; }\n\n    //@{\n    /// @brief Returns true if this allocator is empty, i.e. has no allocated memory\n    bool empty() const { return mSize == 0; }\n    bool isEmpty() const { return mSize == 0; }\n#ifdef __NVCC__\n#pragma nv_diag_default 177\n#endif\n    //@}\n\n    /// @brief Detach device data so it is not dealloced when this buffer is destroyed\n    void detachDeviceData()\n    {\n        mGpuData = nullptr;\n        if (!mCpuData) {\n            mSize = 0;\n        }\n    }\n\n    /// @brief De-allocate all memory managed by this allocator and set all pointers to NULL\n    void clear(void* stream = nullptr)\n    {\n        if (mManaged && mGpuData)\n            wp_free_device(WP_CURRENT_CONTEXT, mGpuData);\n        if (mManaged && mCpuData)\n            wp_free_pinned(mCpuData);\n        mCpuData = mGpuData = nullptr;\n        mSize = 0;\n        mManaged = false;\n    }\n\n};  // DeviceBuffer class\n\ntemplate <typename Tree> __global__ void activateAllLeafVoxels(Tree* tree)\n{\n    const unsigned leaf_count = tree->mNodeCount[0];\n\n    const unsigned tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < leaf_count) {\n        // activate all leaf voxels\n        typename Tree::LeafNodeType* const leaf_nodes = tree->getFirstLeaf();\n        typename Tree::LeafNodeType& leaf = leaf_nodes[tid];\n        leaf.mValueMask.setOn();\n        leaf.updateBBox();\n    }\n\n    if (tid == 0) {\n        tree->mVoxelCount = Tree::LeafNodeType::SIZE * leaf_count;  // full leaves\n    }\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<!nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, unsigned tile_id, const typename Node::BuildType background_value)\n{\n    node.setValue(tile_id, background_value);\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, unsigned tile_id, const typename Node::BuildType background_value)\n{\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<!nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, const typename Node::BuildType background_value)\n{\n    node.mBackground = background_value;\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, const typename Node::BuildType background_value)\n{\n}\n\ntemplate <typename T> struct alignas(alignof(T)) AlignedProxy {\n    char data[sizeof(T)];\n};\n\ntemplate <typename Tree, typename NodeT>\n__global__ void setInternalBBoxAndBackgroundValue(Tree* tree, const typename Tree::BuildType background_value)\n{\n    using BBox = nanovdb::math::BBox<typename NodeT::CoordT>;\n    using BBoxProxy = AlignedProxy<BBox>;\n\n    __shared__ BBoxProxy bbox_mem;\n\n    BBox& bbox = reinterpret_cast<BBox&>(bbox_mem);\n\n    const unsigned node_count = tree->mNodeCount[NodeT::LEVEL];\n    const unsigned node_id = blockIdx.x;\n\n    if (node_id < node_count) {\n\n        if (threadIdx.x == 0) {\n            new (&bbox) BBox();\n        }\n\n        __syncthreads();\n\n        NodeT& node = tree->template getFirstNode<NodeT>()[node_id];\n        for (unsigned child_id = threadIdx.x; child_id < NodeT::SIZE; child_id += blockDim.x) {\n            if (node.isChild(child_id)) {\n                bbox.expandAtomic(node.getChild(child_id)->bbox());\n            } else {\n                setBackgroundValue(node, child_id, background_value);\n            }\n        }\n\n        __syncthreads();\n\n        if (threadIdx.x == 0) {\n            node.mBBox = bbox;\n        }\n    }\n}\n\ntemplate <typename Tree>\n__global__ void\nsetRootBBoxAndBackgroundValue(nanovdb::Grid<Tree>* grid, const typename Tree::BuildType background_value)\n{\n    using BBox = typename Tree::RootNodeType::BBoxType;\n    using BBoxProxy = AlignedProxy<BBox>;\n    __shared__ BBoxProxy bbox_mem;\n\n    BBox& bbox = reinterpret_cast<BBox&>(bbox_mem);\n\n    Tree& tree = grid->tree();\n    const unsigned upper_count = tree.mNodeCount[2];\n\n    if (threadIdx.x == 0) {\n        new (&bbox) BBox();\n    }\n\n    __syncthreads();\n\n    for (unsigned upper_id = threadIdx.x; upper_id < upper_count; upper_id += blockDim.x) {\n        typename Tree::UpperNodeType& upper = tree.getFirstUpper()[upper_id];\n        bbox.expandAtomic(upper.bbox());\n    }\n\n    __syncthreads();\n\n    if (threadIdx.x == 0) {\n        typename Tree::RootNodeType& root = tree.root();\n        setBackgroundValue(root, background_value);\n        root.mBBox = bbox;\n\n        grid->mWorldBBox = root.mBBox.transform(grid->map());\n    }\n}\n\ntemplate <typename BuildT>\nvoid finalize_grid(nanovdb::Grid<nanovdb::NanoTree<BuildT>>& out_grid, const BuildGridParams<BuildT>& params)\n{\n    // set background value, activate all voxels for allocated tiles and update bbox\n\n    using Tree = nanovdb::NanoTree<BuildT>;\n    Tree* tree = &out_grid.tree();\n\n    int node_counts[3];\n    wp_memcpy_d2h(WP_CURRENT_CONTEXT, node_counts, tree->mNodeCount, sizeof(node_counts));\n    // synchronization below is unnecessary as node_counts is in pageable memory.\n    // keep it for clarity\n    cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n    wp_cuda_stream_synchronize(stream);\n\n    const unsigned int leaf_count = node_counts[0];\n    const unsigned int lower_count = node_counts[1];\n    const unsigned int upper_count = node_counts[2];\n\n    constexpr unsigned NUM_THREADS = 256;\n    const unsigned leaf_blocks = (leaf_count + NUM_THREADS - 1) / NUM_THREADS;\n    activateAllLeafVoxels<Tree><<<leaf_blocks, NUM_THREADS, 0, stream>>>(tree);\n\n    setInternalBBoxAndBackgroundValue<Tree, typename Tree::LowerNodeType>\n        <<<lower_count, NUM_THREADS, 0, stream>>>(tree, params.background_value);\n    setInternalBBoxAndBackgroundValue<Tree, typename Tree::UpperNodeType>\n        <<<upper_count, NUM_THREADS, 0, stream>>>(tree, params.background_value);\n    setRootBBoxAndBackgroundValue<Tree><<<1, NUM_THREADS, 0, stream>>>(&out_grid, params.background_value);\n\n    check_cuda(wp_cuda_context_check(WP_CURRENT_CONTEXT));\n}\n\ntemplate <>\nvoid finalize_grid(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueOnIndex>>& out_grid,\n    const BuildGridParams<nanovdb::ValueOnIndex>& params\n)\n{\n    // nothing to do for OnIndex grids\n}\n\n/// \"fancy-pointer\" that transforms from world to index coordinates\nstruct WorldSpacePointsPtr {\n    const nanovdb::Vec3f* points;\n    const nanovdb::Map map;\n\n    __device__ nanovdb::Vec3f operator[](int idx) const { return map.applyInverseMapF(points[idx]); }\n\n    __device__ nanovdb::Vec3f operator*() const { return (*this)[0]; }\n};\n\n}  // namespace\n\nnamespace nanovdb {\ntemplate <> struct BufferTraits<DeviceBuffer> {\n    static constexpr bool hasDeviceDual = true;\n};\n\n}  // namespace nanovdb\n\ntemplate <typename BuildT>\nvoid build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<BuildT>>*& out_grid,\n    size_t& out_grid_size,\n    const void* points,\n    size_t num_points,\n    bool points_in_world_space,\n    const BuildGridParams<BuildT>& params\n)\n{\n\n    out_grid = nullptr;\n    out_grid_size = 0;\n\n    try {\n\n        cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n        nanovdb::tools::cuda::PointsToGrid<BuildT, Resource> p2g(params.map, stream);\n\n        // p2g.setVerbose(2);\n        p2g.setGridName(params.name);\n        p2g.setChecksum(nanovdb::CheckMode::Disable);\n\n        // Only compute bbox for OnIndex grids. Otherwise bbox will be computed after activating all leaf voxels\n        p2g.includeBBox(nanovdb::BuildTraits<BuildT>::is_onindex);\n\n        nanovdb::GridHandle<DeviceBuffer> grid_handle;\n\n        if (points_in_world_space) {\n            grid_handle = p2g.getHandle(\n                WorldSpacePointsPtr { static_cast<const nanovdb::Vec3f*>(points), params.map }, num_points,\n                DeviceBuffer()\n            );\n        } else {\n            grid_handle = p2g.getHandle(static_cast<const nanovdb::Coord*>(points), num_points, DeviceBuffer());\n        }\n\n        out_grid = grid_handle.deviceGrid<BuildT>();\n        out_grid_size = grid_handle.gridSize();\n\n        finalize_grid(*out_grid, params);\n\n        // So that buffer is not destroyed when handles goes out of scope\n        grid_handle.buffer().detachDeviceData();\n    } catch (const std::runtime_error& exc) {\n        out_grid = nullptr;\n        out_grid_size = 0;\n    }\n}\n\n\n#define EXPAND_BUILDER_TYPE(type) \\\ntemplate void build_grid_from_points(nanovdb::Grid<nanovdb::NanoTree<type>> *&, size_t &, const void *, size_t, bool, \\\n                                     const BuildGridParams<type> &);\n\nWP_VOLUME_BUILDER_INSTANTIATE_TYPES\n#undef EXPAND_BUILDER_TYPE\n\ntemplate void build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueIndex>>*&,\n    size_t&,\n    const void*,\n    size_t,\n    bool,\n    const BuildGridParams<nanovdb::ValueIndex>&\n);\ntemplate void build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueOnIndex>>*&,\n    size_t&,\n    const void*,\n    size_t,\n    bool,\n    const BuildGridParams<nanovdb::ValueOnIndex>&\n);\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"volume_builder.h\"\n\n#include <hipcub/hipcub.hpp>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <nanovdb/tools/cuda/PointsToGrid.cuh>\n\n#if defined(__NVCC_DIAG_PRAGMA_SUPPORT__)\n// dynamic initialization is not supported for a function-scope static __shared__ variable within a\n// __device__/__global__ function\n#pragma nv_diag_suppress 20054\n#elif defined(__NVCC__)\n#pragma diag_suppress 20054\n#endif\nnamespace {\n/// Resource class following interface of nanovdb::DeviceResource as expected by nanovdb::PointsToGrid\nclass Resource {\npublic:\n    // cudaMalloc aligns memory to 256 bytes by default\n    static constexpr size_t DEFAULT_ALIGNMENT = 256;\n\n    static void* allocateAsync(size_t bytes, size_t, hipStream_t stream)\n    {\n        void* d_ptr = wp_alloc_device(WP_CURRENT_CONTEXT, bytes, \"(native:volume_builder)\");\n        cudaCheckError();\n        return d_ptr;\n    }\n\n    static void deallocateAsync(void* d_ptr, size_t, size_t, hipStream_t stream)\n    {\n        wp_free_device(WP_CURRENT_CONTEXT, d_ptr);\n    }\n};\n\n/// @brief  Implementation of NanoVDB's DeviceBuffer that uses warp allocators\nclass DeviceBuffer {\n    uint64_t mSize;  // total number of bytes managed by this buffer (assumed to be identical for host and device)\n    void *mCpuData, *mGpuData;  // raw pointers to the host and device buffers\n    bool mManaged;\n\npublic:\n    /// @brief Static factory method that return an instance of this buffer\n    /// @param size byte size of buffer to be initialized\n    /// @param dummy this argument is currently ignored but required to match the API of the HostBuffer\n    /// @param device id of the device on which to initialize the buffer\n    /// @param stream optional stream argument (defaults to stream NULL)\n    /// @return An instance of this class using move semantics\n    static DeviceBuffer create(\n        uint64_t size, const DeviceBuffer* dummy = nullptr, int device = hipCpuDeviceId, hipStream_t stream = nullptr\n    )\n    {\n        return DeviceBuffer(size, device, stream);\n    }\n\n    /// @brief Static factory method that return an instance of this buffer that wraps externally managed memory\n    /// @param size byte size of buffer specified by external memory\n    /// @param cpuData pointer to externally managed host memory\n    /// @param gpuData pointer to externally managed device memory\n    /// @return An instance of this class using move semantics\n#ifdef __NVCC__\n#pragma nv_diag_suppress 177\n#endif\n    static DeviceBuffer create(uint64_t size, void* cpuData, void* gpuData)\n    {\n        return DeviceBuffer(size, cpuData, gpuData);\n    }\n#ifdef __NVCC__\n#pragma nv_diag_default 177\n#endif\n\n    /// @brief Constructor\n    /// @param size byte size of buffer to be initialized\n    /// @param host If true buffer is initialized only on the host/CPU, else on the device/GPU\n    /// @param stream optional stream argument (defaults to stream NULL)\n    DeviceBuffer(uint64_t size = 0, int device = hipCpuDeviceId, hipStream_t stream = nullptr)\n        : mSize(0)\n        , mCpuData(nullptr)\n        , mGpuData(nullptr)\n        , mManaged(false)\n    {\n        if (size > 0)\n            this->init(size, device, stream);\n    }\n\n    DeviceBuffer(uint64_t size, void* cpuData, void* gpuData)\n        : mSize(size)\n        , mCpuData(cpuData)\n        , mGpuData(gpuData)\n        , mManaged(false)\n    {\n    }\n\n    /// @brief Disallow copy-construction\n    DeviceBuffer(const DeviceBuffer&) = delete;\n\n    /// @brief Move copy-constructor\n    DeviceBuffer(DeviceBuffer&& other) noexcept\n        : mSize(other.mSize)\n        , mCpuData(other.mCpuData)\n        , mGpuData(other.mGpuData)\n        , mManaged(other.mManaged)\n    {\n        other.mSize = 0;\n        other.mCpuData = nullptr;\n        other.mGpuData = nullptr;\n        other.mManaged = false;\n    }\n\n    /// @brief Disallow copy assignment operation\n    DeviceBuffer& operator=(const DeviceBuffer&) = delete;\n\n    /// @brief Move copy assignment operation\n    DeviceBuffer& operator=(DeviceBuffer&& other) noexcept\n    {\n        this->clear();\n        mSize = other.mSize;\n        mCpuData = other.mCpuData;\n        mGpuData = other.mGpuData;\n        mManaged = other.mManaged;\n        other.mSize = 0;\n        other.mCpuData = nullptr;\n        other.mGpuData = nullptr;\n        other.mManaged = false;\n        return *this;\n    }\n\n    /// @brief Destructor frees memory on both the host and device\n    ~DeviceBuffer() { this->clear(); };\n\n    /// @brief Initialize buffer\n    /// @param size byte size of buffer to be initialized\n    /// @param device id of the device on which to initialize the buffer\n    /// @note All existing buffers are first cleared\n    /// @warning size is expected to be non-zero. Use clear() clear buffer!\n    void init(uint64_t size, int device = hipCpuDeviceId, void* stream = nullptr)\n    {\n        if (mSize > 0)\n            this->clear(stream);\n        NANOVDB_ASSERT(size > 0);\n        if (device == hipCpuDeviceId) {\n            mCpuData = wp_alloc_pinned(size, \"(native:volume_builder)\");\n        } else {\n            mGpuData = wp_alloc_device(WP_CURRENT_CONTEXT, size, \"(native:volume_builder)\");\n        }\n        cudaCheckError();\n        mSize = size;\n        mManaged = true;\n    }\n\n    /// @brief Returns a raw pointer to the host/CPU buffer managed by this allocator.\n    /// @warning Note that the pointer can be NULL!\n    void* data() const { return mCpuData; }\n\n    /// @brief Returns a raw pointer to the device/GPU buffer managed by this allocator.\n    /// @warning Note that the pointer can be NULL!\n    void* deviceData() const { return mGpuData; }\n\n    /// @brief Returns the size in bytes of the raw memory buffer managed by this allocator.\n#ifdef __NVCC__\n#pragma nv_diag_suppress 177\n#endif\n    uint64_t size() const { return mSize; }\n\n    //@{\n    /// @brief Returns true if this allocator is empty, i.e. has no allocated memory\n    bool empty() const { return mSize == 0; }\n    bool isEmpty() const { return mSize == 0; }\n#ifdef __NVCC__\n#pragma nv_diag_default 177\n#endif\n    //@}\n\n    /// @brief Detach device data so it is not dealloced when this buffer is destroyed\n    void detachDeviceData()\n    {\n        mGpuData = nullptr;\n        if (!mCpuData) {\n            mSize = 0;\n        }\n    }\n\n    /// @brief De-allocate all memory managed by this allocator and set all pointers to NULL\n    void clear(void* stream = nullptr)\n    {\n        if (mManaged && mGpuData)\n            wp_free_device(WP_CURRENT_CONTEXT, mGpuData);\n        if (mManaged && mCpuData)\n            wp_free_pinned(mCpuData);\n        mCpuData = mGpuData = nullptr;\n        mSize = 0;\n        mManaged = false;\n    }\n\n};  // DeviceBuffer class\n\ntemplate <typename Tree> __global__ void activateAllLeafVoxels(Tree* tree)\n{\n    const unsigned leaf_count = tree->mNodeCount[0];\n\n    const unsigned tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < leaf_count) {\n        // activate all leaf voxels\n        typename Tree::LeafNodeType* const leaf_nodes = tree->getFirstLeaf();\n        typename Tree::LeafNodeType& leaf = leaf_nodes[tid];\n        leaf.mValueMask.setOn();\n        leaf.updateBBox();\n    }\n\n    if (tid == 0) {\n        tree->mVoxelCount = Tree::LeafNodeType::SIZE * leaf_count;  // full leaves\n    }\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<!nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, unsigned tile_id, const typename Node::BuildType background_value)\n{\n    node.setValue(tile_id, background_value);\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, unsigned tile_id, const typename Node::BuildType background_value)\n{\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<!nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, const typename Node::BuildType background_value)\n{\n    node.mBackground = background_value;\n}\n\ntemplate <typename Node>\n__device__ std::enable_if_t<nanovdb::BuildTraits<typename Node::BuildType>::is_index>\nsetBackgroundValue(Node& node, const typename Node::BuildType background_value)\n{\n}\n\ntemplate <typename T> struct alignas(alignof(T)) AlignedProxy {\n    char data[sizeof(T)];\n};\n\ntemplate <typename Tree, typename NodeT>\n__global__ void setInternalBBoxAndBackgroundValue(Tree* tree, const typename Tree::BuildType background_value)\n{\n    using BBox = nanovdb::math::BBox<typename NodeT::CoordT>;\n    using BBoxProxy = AlignedProxy<BBox>;\n\n    __shared__ BBoxProxy bbox_mem;\n\n    BBox& bbox = reinterpret_cast<BBox&>(bbox_mem);\n\n    const unsigned node_count = tree->mNodeCount[NodeT::LEVEL];\n    const unsigned node_id = blockIdx.x;\n\n    if (node_id < node_count) {\n\n        if (threadIdx.x == 0) {\n            new (&bbox) BBox();\n        }\n\n        __syncthreads();\n\n        NodeT& node = tree->template getFirstNode<NodeT>()[node_id];\n        for (unsigned child_id = threadIdx.x; child_id < NodeT::SIZE; child_id += blockDim.x) {\n            if (node.isChild(child_id)) {\n                bbox.expandAtomic(node.getChild(child_id)->bbox());\n            } else {\n                setBackgroundValue(node, child_id, background_value);\n            }\n        }\n\n        __syncthreads();\n\n        if (threadIdx.x == 0) {\n            node.mBBox = bbox;\n        }\n    }\n}\n\ntemplate <typename Tree>\n__global__ void\nsetRootBBoxAndBackgroundValue(nanovdb::Grid<Tree>* grid, const typename Tree::BuildType background_value)\n{\n    using BBox = typename Tree::RootNodeType::BBoxType;\n    using BBoxProxy = AlignedProxy<BBox>;\n    __shared__ BBoxProxy bbox_mem;\n\n    BBox& bbox = reinterpret_cast<BBox&>(bbox_mem);\n\n    Tree& tree = grid->tree();\n    const unsigned upper_count = tree.mNodeCount[2];\n\n    if (threadIdx.x == 0) {\n        new (&bbox) BBox();\n    }\n\n    __syncthreads();\n\n    for (unsigned upper_id = threadIdx.x; upper_id < upper_count; upper_id += blockDim.x) {\n        typename Tree::UpperNodeType& upper = tree.getFirstUpper()[upper_id];\n        bbox.expandAtomic(upper.bbox());\n    }\n\n    __syncthreads();\n\n    if (threadIdx.x == 0) {\n        typename Tree::RootNodeType& root = tree.root();\n        setBackgroundValue(root, background_value);\n        root.mBBox = bbox;\n\n        grid->mWorldBBox = root.mBBox.transform(grid->map());\n    }\n}\n\ntemplate <typename BuildT>\nvoid finalize_grid(nanovdb::Grid<nanovdb::NanoTree<BuildT>>& out_grid, const BuildGridParams<BuildT>& params)\n{\n    // set background value, activate all voxels for allocated tiles and update bbox\n\n    using Tree = nanovdb::NanoTree<BuildT>;\n    Tree* tree = &out_grid.tree();\n\n    int node_counts[3];\n    wp_memcpy_d2h(WP_CURRENT_CONTEXT, node_counts, tree->mNodeCount, sizeof(node_counts));\n    // synchronization below is unnecessary as node_counts is in pageable memory.\n    // keep it for clarity\n    hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n    wp_cuda_stream_synchronize(stream);\n\n    const unsigned int leaf_count = node_counts[0];\n    const unsigned int lower_count = node_counts[1];\n    const unsigned int upper_count = node_counts[2];\n\n    constexpr unsigned NUM_THREADS = 256;\n    const unsigned leaf_blocks = (leaf_count + NUM_THREADS - 1) / NUM_THREADS;\n    activateAllLeafVoxels<Tree><<<leaf_blocks, NUM_THREADS, 0, stream>>>(tree);\n\n    setInternalBBoxAndBackgroundValue<Tree, typename Tree::LowerNodeType>\n        <<<lower_count, NUM_THREADS, 0, stream>>>(tree, params.background_value);\n    setInternalBBoxAndBackgroundValue<Tree, typename Tree::UpperNodeType>\n        <<<upper_count, NUM_THREADS, 0, stream>>>(tree, params.background_value);\n    setRootBBoxAndBackgroundValue<Tree><<<1, NUM_THREADS, 0, stream>>>(&out_grid, params.background_value);\n\n    check_cuda(wp_cuda_context_check(WP_CURRENT_CONTEXT));\n}\n\ntemplate <>\nvoid finalize_grid(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueOnIndex>>& out_grid,\n    const BuildGridParams<nanovdb::ValueOnIndex>& params\n)\n{\n    // nothing to do for OnIndex grids\n}\n\n/// \"fancy-pointer\" that transforms from world to index coordinates\nstruct WorldSpacePointsPtr {\n    const nanovdb::Vec3f* points;\n    const nanovdb::Map map;\n\n    __device__ nanovdb::Vec3f operator[](int idx) const { return map.applyInverseMapF(points[idx]); }\n\n    __device__ nanovdb::Vec3f operator*() const { return (*this)[0]; }\n};\n\n}  // namespace\n\nnamespace nanovdb {\ntemplate <> struct BufferTraits<DeviceBuffer> {\n    static constexpr bool hasDeviceDual = true;\n};\n\n}  // namespace nanovdb\n\ntemplate <typename BuildT>\nvoid build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<BuildT>>*& out_grid,\n    size_t& out_grid_size,\n    const void* points,\n    size_t num_points,\n    bool points_in_world_space,\n    const BuildGridParams<BuildT>& params\n)\n{\n\n    out_grid = nullptr;\n    out_grid_size = 0;\n\n    try {\n\n        hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n        nanovdb::tools::cuda::PointsToGrid<BuildT, Resource> p2g(params.map, stream);\n\n        // p2g.setVerbose(2);\n        p2g.setGridName(params.name);\n        p2g.setChecksum(nanovdb::CheckMode::Disable);\n\n        // Only compute bbox for OnIndex grids. Otherwise bbox will be computed after activating all leaf voxels\n        p2g.includeBBox(nanovdb::BuildTraits<BuildT>::is_onindex);\n\n        nanovdb::GridHandle<DeviceBuffer> grid_handle;\n\n        if (points_in_world_space) {\n            grid_handle = p2g.getHandle(\n                WorldSpacePointsPtr { static_cast<const nanovdb::Vec3f*>(points), params.map }, num_points,\n                DeviceBuffer()\n            );\n        } else {\n            grid_handle = p2g.getHandle(static_cast<const nanovdb::Coord*>(points), num_points, DeviceBuffer());\n        }\n\n        out_grid = grid_handle.deviceGrid<BuildT>();\n        out_grid_size = grid_handle.gridSize();\n\n        finalize_grid(*out_grid, params);\n\n        // So that buffer is not destroyed when handles goes out of scope\n        grid_handle.buffer().detachDeviceData();\n    } catch (const std::runtime_error& exc) {\n        out_grid = nullptr;\n        out_grid_size = 0;\n    }\n}\n\n\n#define EXPAND_BUILDER_TYPE(type) \\\ntemplate void build_grid_from_points(nanovdb::Grid<nanovdb::NanoTree<type>> *&, size_t &, const void *, size_t, bool, \\\n                                     const BuildGridParams<type> &);\n\nWP_VOLUME_BUILDER_INSTANTIATE_TYPES\n#undef EXPAND_BUILDER_TYPE\n\ntemplate void build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueIndex>>*&,\n    size_t&,\n    const void*,\n    size_t,\n    bool,\n    const BuildGridParams<nanovdb::ValueIndex>&\n);\ntemplate void build_grid_from_points(\n    nanovdb::Grid<nanovdb::NanoTree<nanovdb::ValueOnIndex>>*&,\n    size_t&,\n    const void*,\n    size_t,\n    bool,\n    const BuildGridParams<nanovdb::ValueOnIndex>&\n);\n", "source": "/kaggle/working/warp/warp/native/volume_builder.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"temp_buffer.h\"\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n#include <cub/device/device_reduce.cuh>\n\nnamespace {\n\ntemplate <typename T>\n__global__ void cwise_mult_kernel(int len, int stride_a, int stride_b, const T* a, const T* b, T* out)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i >= len)\n        return;\n    out[i] = a[i * stride_a] * b[i * stride_b];\n}\n\n/// Custom iterator for allowing strided access with CUB\ntemplate <typename T> struct cub_strided_iterator {\n    typedef cub_strided_iterator<T> self_type;\n    typedef std::ptrdiff_t difference_type;\n    typedef T value_type;\n    typedef T* pointer;\n    typedef T& reference;\n\n    typedef std::random_access_iterator_tag iterator_category;  ///< The iterator category\n\n    T* ptr = nullptr;\n    int stride = 1;\n\n    CUDA_CALLABLE self_type operator++(int) { return ++(self_type(*this)); }\n\n    CUDA_CALLABLE self_type& operator++()\n    {\n        ptr += stride;\n        return *this;\n    }\n\n    __host__ __device__ __forceinline__ reference operator*() const { return *ptr; }\n\n    CUDA_CALLABLE self_type operator+(difference_type n) const { return self_type(*this) += n; }\n\n    CUDA_CALLABLE self_type& operator+=(difference_type n)\n    {\n        ptr += n * stride;\n        return *this;\n    }\n\n    CUDA_CALLABLE self_type operator-(difference_type n) const { return self_type(*this) -= n; }\n\n    CUDA_CALLABLE self_type& operator-=(difference_type n)\n    {\n        ptr -= n * stride;\n        return *this;\n    }\n\n    CUDA_CALLABLE difference_type operator-(const self_type& other) const { return (ptr - other.ptr) / stride; }\n\n    CUDA_CALLABLE reference operator[](difference_type n) const { return *(ptr + n * stride); }\n\n    CUDA_CALLABLE pointer operator->() const { return ptr; }\n\n    CUDA_CALLABLE bool operator==(const self_type& rhs) const { return (ptr == rhs.ptr); }\n\n    CUDA_CALLABLE bool operator!=(const self_type& rhs) const { return (ptr != rhs.ptr); }\n};\n\ntemplate <typename T> void array_sum_device(const T* ptr_a, T* ptr_out, int count, int byte_stride, int type_length)\n{\n    assert((byte_stride % sizeof(T)) == 0);\n    const int stride = byte_stride / sizeof(T);\n\n    ContextGuard guard(wp_cuda_context_get_current());\n    cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n\n    cub_strided_iterator<const T> ptr_strided { ptr_a, stride };\n\n    size_t buff_size = 0;\n    check_cuda(cub::DeviceReduce::Sum(nullptr, buff_size, ptr_strided, ptr_out, count, stream));\n    void* temp_buffer = wp_alloc_device(WP_CURRENT_CONTEXT, buff_size, \"(native:reduce)\");\n\n    for (int k = 0; k < type_length; ++k) {\n        cub_strided_iterator<const T> ptr_strided { ptr_a + k, stride };\n        check_cuda(cub::DeviceReduce::Sum(temp_buffer, buff_size, ptr_strided, ptr_out + k, count, stream));\n    }\n\n    wp_free_device(WP_CURRENT_CONTEXT, temp_buffer);\n}\n\ntemplate <typename T>\nvoid array_sum_device_dispatch(const T* ptr_a, T* ptr_out, int count, int byte_stride, int type_length)\n{\n    using vec2 = wp::vec_t<2, T>;\n    using vec3 = wp::vec_t<3, T>;\n    using vec4 = wp::vec_t<4, T>;\n\n    // specialized calls for common vector types\n\n    if ((type_length % 4) == 0 && (byte_stride % sizeof(vec4)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec4*>(ptr_a), reinterpret_cast<vec4*>(ptr_out), count, byte_stride, type_length / 4\n        );\n    }\n\n    if ((type_length % 3) == 0 && (byte_stride % sizeof(vec3)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec3*>(ptr_a), reinterpret_cast<vec3*>(ptr_out), count, byte_stride, type_length / 3\n        );\n    }\n\n    if ((type_length % 2) == 0 && (byte_stride % sizeof(vec2)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec2*>(ptr_a), reinterpret_cast<vec2*>(ptr_out), count, byte_stride, type_length / 2\n        );\n    }\n\n    return array_sum_device(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n\ntemplate <typename T> CUDA_CALLABLE T element_inner_product(const T& a, const T& b) { return a * b; }\n\ntemplate <unsigned Length, typename T>\nCUDA_CALLABLE T element_inner_product(const wp::vec_t<Length, T>& a, const wp::vec_t<Length, T>& b)\n{\n    return wp::dot(a, b);\n}\n\n/// Custom iterator for allowing strided access with CUB\ntemplate <typename ElemT, typename ScalarT> struct cub_inner_product_iterator {\n    typedef cub_inner_product_iterator<ElemT, ScalarT> self_type;\n    typedef std::ptrdiff_t difference_type;\n    typedef ScalarT value_type;\n    typedef ScalarT* pointer;\n    typedef ScalarT reference;\n\n    typedef std::random_access_iterator_tag iterator_category;  ///< The iterator category\n\n    const ElemT* ptr_a = nullptr;\n    const ElemT* ptr_b = nullptr;\n\n    int stride_a = 1;\n    int stride_b = 1;\n    int type_length = 1;\n\n    CUDA_CALLABLE self_type operator++(int) { return ++(self_type(*this)); }\n\n    CUDA_CALLABLE self_type& operator++()\n    {\n        ptr_a += stride_a;\n        ptr_b += stride_b;\n        return *this;\n    }\n\n    __host__ __device__ __forceinline__ reference operator*() const { return compute_value(0); }\n\n    CUDA_CALLABLE self_type operator+(difference_type n) const { return self_type(*this) += n; }\n\n    CUDA_CALLABLE self_type& operator+=(difference_type n)\n    {\n        ptr_a += n * stride_a;\n        ptr_b += n * stride_b;\n        return *this;\n    }\n\n    CUDA_CALLABLE self_type operator-(difference_type n) const { return self_type(*this) -= n; }\n\n    CUDA_CALLABLE self_type& operator-=(difference_type n)\n    {\n        ptr_a -= n * stride_a;\n        ptr_b -= n * stride_b;\n        return *this;\n    }\n\n    CUDA_CALLABLE difference_type operator-(const self_type& other) const { return (ptr_a - other.ptr_a) / stride_a; }\n\n    CUDA_CALLABLE reference operator[](difference_type n) const { return compute_value(n); }\n\n    CUDA_CALLABLE bool operator==(const self_type& rhs) const { return (ptr_a == rhs.ptr_a); }\n\n    CUDA_CALLABLE bool operator!=(const self_type& rhs) const { return (ptr_a != rhs.ptr_a); }\n\nprivate:\n    CUDA_CALLABLE ScalarT compute_value(difference_type n) const\n    {\n        ScalarT val(0);\n        const ElemT* a = ptr_a + n * stride_a;\n        const ElemT* b = ptr_b + n * stride_b;\n        for (int k = 0; k < type_length; ++k) {\n            val += element_inner_product(a[k], b[k]);\n        }\n        return val;\n    }\n};\n\ntemplate <typename ElemT, typename ScalarT>\nvoid array_inner_device(\n    const ElemT* ptr_a,\n    const ElemT* ptr_b,\n    ScalarT* ptr_out,\n    int count,\n    int byte_stride_a,\n    int byte_stride_b,\n    int type_length\n)\n{\n    assert((byte_stride_a % sizeof(ElemT)) == 0);\n    assert((byte_stride_b % sizeof(ElemT)) == 0);\n    const int stride_a = byte_stride_a / sizeof(ElemT);\n    const int stride_b = byte_stride_b / sizeof(ElemT);\n\n    ContextGuard guard(wp_cuda_context_get_current());\n    cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n\n    cub_inner_product_iterator<ElemT, ScalarT> inner_iterator { ptr_a, ptr_b, stride_a, stride_b, type_length };\n\n    size_t buff_size = 0;\n    check_cuda(cub::DeviceReduce::Sum(nullptr, buff_size, inner_iterator, ptr_out, count, stream));\n    void* temp_buffer = wp_alloc_device(WP_CURRENT_CONTEXT, buff_size, \"(native:reduce)\");\n\n    check_cuda(cub::DeviceReduce::Sum(temp_buffer, buff_size, inner_iterator, ptr_out, count, stream));\n\n    wp_free_device(WP_CURRENT_CONTEXT, temp_buffer);\n}\n\ntemplate <typename T>\nvoid array_inner_device_dispatch(\n    const T* ptr_a, const T* ptr_b, T* ptr_out, int count, int byte_stride_a, int byte_stride_b, int type_length\n)\n{\n    using vec2 = wp::vec_t<2, T>;\n    using vec3 = wp::vec_t<3, T>;\n    using vec4 = wp::vec_t<4, T>;\n\n    // specialized calls for common vector types\n\n    if ((type_length % 4) == 0 && (byte_stride_a % sizeof(vec4)) == 0 && (byte_stride_b % sizeof(vec4)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec4*>(ptr_a), reinterpret_cast<const vec4*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 4\n        );\n    }\n\n    if ((type_length % 3) == 0 && (byte_stride_a % sizeof(vec3)) == 0 && (byte_stride_b % sizeof(vec3)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec3*>(ptr_a), reinterpret_cast<const vec3*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 3\n        );\n    }\n\n    if ((type_length % 2) == 0 && (byte_stride_a % sizeof(vec2)) == 0 && (byte_stride_b % sizeof(vec2)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec2*>(ptr_a), reinterpret_cast<const vec2*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 2\n        );\n    }\n\n    return array_inner_device(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_length);\n}\n\n}  // anonymous namespace\n\nvoid wp_array_inner_float_device(\n    uint64_t a, uint64_t b, uint64_t out, int count, int byte_stride_a, int byte_stride_b, int type_len\n)\n{\n    void* context = wp_cuda_context_get_current();\n\n    const float* ptr_a = (const float*)(a);\n    const float* ptr_b = (const float*)(b);\n    float* ptr_out = (float*)(out);\n\n    array_inner_device_dispatch(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_len);\n}\n\nvoid wp_array_inner_double_device(\n    uint64_t a, uint64_t b, uint64_t out, int count, int byte_stride_a, int byte_stride_b, int type_len\n)\n{\n    const double* ptr_a = (const double*)(a);\n    const double* ptr_b = (const double*)(b);\n    double* ptr_out = (double*)(out);\n\n    array_inner_device_dispatch(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_len);\n}\n\nvoid wp_array_sum_float_device(uint64_t a, uint64_t out, int count, int byte_stride, int type_length)\n{\n    const float* ptr_a = (const float*)(a);\n    float* ptr_out = (float*)(out);\n    array_sum_device_dispatch(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n\nvoid wp_array_sum_double_device(uint64_t a, uint64_t out, int count, int byte_stride, int type_length)\n{\n    const double* ptr_a = (const double*)(a);\n    double* ptr_out = (double*)(out);\n    array_sum_device_dispatch(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"temp_buffer.h\"\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n#include <cub/device/device_reduce.cuh>\n\nnamespace {\n\ntemplate <typename T>\n__global__ void cwise_mult_kernel(int len, int stride_a, int stride_b, const T* a, const T* b, T* out)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i >= len)\n        return;\n    out[i] = a[i * stride_a] * b[i * stride_b];\n}\n\n/// Custom iterator for allowing strided access with CUB\ntemplate <typename T> struct cub_strided_iterator {\n    typedef cub_strided_iterator<T> self_type;\n    typedef std::ptrdiff_t difference_type;\n    typedef T value_type;\n    typedef T* pointer;\n    typedef T& reference;\n\n    typedef std::random_access_iterator_tag iterator_category;  ///< The iterator category\n\n    T* ptr = nullptr;\n    int stride = 1;\n\n    CUDA_CALLABLE self_type operator++(int) { return ++(self_type(*this)); }\n\n    CUDA_CALLABLE self_type& operator++()\n    {\n        ptr += stride;\n        return *this;\n    }\n\n    __host__ __device__ __forceinline__ reference operator*() const { return *ptr; }\n\n    CUDA_CALLABLE self_type operator+(difference_type n) const { return self_type(*this) += n; }\n\n    CUDA_CALLABLE self_type& operator+=(difference_type n)\n    {\n        ptr += n * stride;\n        return *this;\n    }\n\n    CUDA_CALLABLE self_type operator-(difference_type n) const { return self_type(*this) -= n; }\n\n    CUDA_CALLABLE self_type& operator-=(difference_type n)\n    {\n        ptr -= n * stride;\n        return *this;\n    }\n\n    CUDA_CALLABLE difference_type operator-(const self_type& other) const { return (ptr - other.ptr) / stride; }\n\n    CUDA_CALLABLE reference operator[](difference_type n) const { return *(ptr + n * stride); }\n\n    CUDA_CALLABLE pointer operator->() const { return ptr; }\n\n    CUDA_CALLABLE bool operator==(const self_type& rhs) const { return (ptr == rhs.ptr); }\n\n    CUDA_CALLABLE bool operator!=(const self_type& rhs) const { return (ptr != rhs.ptr); }\n};\n\ntemplate <typename T> void array_sum_device(const T* ptr_a, T* ptr_out, int count, int byte_stride, int type_length)\n{\n    assert((byte_stride % sizeof(T)) == 0);\n    const int stride = byte_stride / sizeof(T);\n\n    ContextGuard guard(wp_cuda_context_get_current());\n    hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n\n    cub_strided_iterator<const T> ptr_strided { ptr_a, stride };\n\n    size_t buff_size = 0;\n    check_cuda(hipcub::DeviceReduce::Sum(nullptr, buff_size, ptr_strided, ptr_out, count, stream));\n    void* temp_buffer = wp_alloc_device(WP_CURRENT_CONTEXT, buff_size, \"(native:reduce)\");\n\n    for (int k = 0; k < type_length; ++k) {\n        cub_strided_iterator<const T> ptr_strided { ptr_a + k, stride };\n        check_cuda(hipcub::DeviceReduce::Sum(temp_buffer, buff_size, ptr_strided, ptr_out + k, count, stream));\n    }\n\n    wp_free_device(WP_CURRENT_CONTEXT, temp_buffer);\n}\n\ntemplate <typename T>\nvoid array_sum_device_dispatch(const T* ptr_a, T* ptr_out, int count, int byte_stride, int type_length)\n{\n    using vec2 = wp::vec_t<2, T>;\n    using vec3 = wp::vec_t<3, T>;\n    using vec4 = wp::vec_t<4, T>;\n\n    // specialized calls for common vector types\n\n    if ((type_length % 4) == 0 && (byte_stride % sizeof(vec4)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec4*>(ptr_a), reinterpret_cast<vec4*>(ptr_out), count, byte_stride, type_length / 4\n        );\n    }\n\n    if ((type_length % 3) == 0 && (byte_stride % sizeof(vec3)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec3*>(ptr_a), reinterpret_cast<vec3*>(ptr_out), count, byte_stride, type_length / 3\n        );\n    }\n\n    if ((type_length % 2) == 0 && (byte_stride % sizeof(vec2)) == 0) {\n        return array_sum_device(\n            reinterpret_cast<const vec2*>(ptr_a), reinterpret_cast<vec2*>(ptr_out), count, byte_stride, type_length / 2\n        );\n    }\n\n    return array_sum_device(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n\ntemplate <typename T> CUDA_CALLABLE T element_inner_product(const T& a, const T& b) { return a * b; }\n\ntemplate <unsigned Length, typename T>\nCUDA_CALLABLE T element_inner_product(const wp::vec_t<Length, T>& a, const wp::vec_t<Length, T>& b)\n{\n    return wp::dot(a, b);\n}\n\n/// Custom iterator for allowing strided access with CUB\ntemplate <typename ElemT, typename ScalarT> struct cub_inner_product_iterator {\n    typedef cub_inner_product_iterator<ElemT, ScalarT> self_type;\n    typedef std::ptrdiff_t difference_type;\n    typedef ScalarT value_type;\n    typedef ScalarT* pointer;\n    typedef ScalarT reference;\n\n    typedef std::random_access_iterator_tag iterator_category;  ///< The iterator category\n\n    const ElemT* ptr_a = nullptr;\n    const ElemT* ptr_b = nullptr;\n\n    int stride_a = 1;\n    int stride_b = 1;\n    int type_length = 1;\n\n    CUDA_CALLABLE self_type operator++(int) { return ++(self_type(*this)); }\n\n    CUDA_CALLABLE self_type& operator++()\n    {\n        ptr_a += stride_a;\n        ptr_b += stride_b;\n        return *this;\n    }\n\n    __host__ __device__ __forceinline__ reference operator*() const { return compute_value(0); }\n\n    CUDA_CALLABLE self_type operator+(difference_type n) const { return self_type(*this) += n; }\n\n    CUDA_CALLABLE self_type& operator+=(difference_type n)\n    {\n        ptr_a += n * stride_a;\n        ptr_b += n * stride_b;\n        return *this;\n    }\n\n    CUDA_CALLABLE self_type operator-(difference_type n) const { return self_type(*this) -= n; }\n\n    CUDA_CALLABLE self_type& operator-=(difference_type n)\n    {\n        ptr_a -= n * stride_a;\n        ptr_b -= n * stride_b;\n        return *this;\n    }\n\n    CUDA_CALLABLE difference_type operator-(const self_type& other) const { return (ptr_a - other.ptr_a) / stride_a; }\n\n    CUDA_CALLABLE reference operator[](difference_type n) const { return compute_value(n); }\n\n    CUDA_CALLABLE bool operator==(const self_type& rhs) const { return (ptr_a == rhs.ptr_a); }\n\n    CUDA_CALLABLE bool operator!=(const self_type& rhs) const { return (ptr_a != rhs.ptr_a); }\n\nprivate:\n    CUDA_CALLABLE ScalarT compute_value(difference_type n) const\n    {\n        ScalarT val(0);\n        const ElemT* a = ptr_a + n * stride_a;\n        const ElemT* b = ptr_b + n * stride_b;\n        for (int k = 0; k < type_length; ++k) {\n            val += element_inner_product(a[k], b[k]);\n        }\n        return val;\n    }\n};\n\ntemplate <typename ElemT, typename ScalarT>\nvoid array_inner_device(\n    const ElemT* ptr_a,\n    const ElemT* ptr_b,\n    ScalarT* ptr_out,\n    int count,\n    int byte_stride_a,\n    int byte_stride_b,\n    int type_length\n)\n{\n    assert((byte_stride_a % sizeof(ElemT)) == 0);\n    assert((byte_stride_b % sizeof(ElemT)) == 0);\n    const int stride_a = byte_stride_a / sizeof(ElemT);\n    const int stride_b = byte_stride_b / sizeof(ElemT);\n\n    ContextGuard guard(wp_cuda_context_get_current());\n    hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n\n    cub_inner_product_iterator<ElemT, ScalarT> inner_iterator { ptr_a, ptr_b, stride_a, stride_b, type_length };\n\n    size_t buff_size = 0;\n    check_cuda(hipcub::DeviceReduce::Sum(nullptr, buff_size, inner_iterator, ptr_out, count, stream));\n    void* temp_buffer = wp_alloc_device(WP_CURRENT_CONTEXT, buff_size, \"(native:reduce)\");\n\n    check_cuda(hipcub::DeviceReduce::Sum(temp_buffer, buff_size, inner_iterator, ptr_out, count, stream));\n\n    wp_free_device(WP_CURRENT_CONTEXT, temp_buffer);\n}\n\ntemplate <typename T>\nvoid array_inner_device_dispatch(\n    const T* ptr_a, const T* ptr_b, T* ptr_out, int count, int byte_stride_a, int byte_stride_b, int type_length\n)\n{\n    using vec2 = wp::vec_t<2, T>;\n    using vec3 = wp::vec_t<3, T>;\n    using vec4 = wp::vec_t<4, T>;\n\n    // specialized calls for common vector types\n\n    if ((type_length % 4) == 0 && (byte_stride_a % sizeof(vec4)) == 0 && (byte_stride_b % sizeof(vec4)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec4*>(ptr_a), reinterpret_cast<const vec4*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 4\n        );\n    }\n\n    if ((type_length % 3) == 0 && (byte_stride_a % sizeof(vec3)) == 0 && (byte_stride_b % sizeof(vec3)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec3*>(ptr_a), reinterpret_cast<const vec3*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 3\n        );\n    }\n\n    if ((type_length % 2) == 0 && (byte_stride_a % sizeof(vec2)) == 0 && (byte_stride_b % sizeof(vec2)) == 0) {\n        return array_inner_device(\n            reinterpret_cast<const vec2*>(ptr_a), reinterpret_cast<const vec2*>(ptr_b), ptr_out, count, byte_stride_a,\n            byte_stride_b, type_length / 2\n        );\n    }\n\n    return array_inner_device(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_length);\n}\n\n}  // anonymous namespace\n\nvoid wp_array_inner_float_device(\n    uint64_t a, uint64_t b, uint64_t out, int count, int byte_stride_a, int byte_stride_b, int type_len\n)\n{\n    void* context = wp_cuda_context_get_current();\n\n    const float* ptr_a = (const float*)(a);\n    const float* ptr_b = (const float*)(b);\n    float* ptr_out = (float*)(out);\n\n    array_inner_device_dispatch(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_len);\n}\n\nvoid wp_array_inner_double_device(\n    uint64_t a, uint64_t b, uint64_t out, int count, int byte_stride_a, int byte_stride_b, int type_len\n)\n{\n    const double* ptr_a = (const double*)(a);\n    const double* ptr_b = (const double*)(b);\n    double* ptr_out = (double*)(out);\n\n    array_inner_device_dispatch(ptr_a, ptr_b, ptr_out, count, byte_stride_a, byte_stride_b, type_len);\n}\n\nvoid wp_array_sum_float_device(uint64_t a, uint64_t out, int count, int byte_stride, int type_length)\n{\n    const float* ptr_a = (const float*)(a);\n    float* ptr_out = (float*)(out);\n    array_sum_device_dispatch(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n\nvoid wp_array_sum_double_device(uint64_t a, uint64_t out, int count, int byte_stride, int type_length)\n{\n    const double* ptr_a = (const double*)(a);\n    double* ptr_out = (double*)(out);\n    array_sum_device_dispatch(ptr_a, ptr_out, count, byte_stride, type_length);\n}\n", "source": "/kaggle/working/warp/warp/native/reduce.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"temp_buffer.h\"\n\n#include <cstdint>\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/device/device_scan.cuh>\n\nextern CUcontext get_current_context();\n\nnamespace {\n\n// Combined row+column value that can be radix-sorted with CUB\nusing BsrRowCol = uint64_t;\n\nstatic constexpr BsrRowCol PRUNED_ROWCOL = ~BsrRowCol(0);\n\nCUDA_CALLABLE BsrRowCol bsr_combine_row_col(uint32_t row, uint32_t col)\n{\n    return (static_cast<uint64_t>(row) << 32) | col;\n}\n\nCUDA_CALLABLE uint32_t bsr_get_row(const BsrRowCol& row_col) { return row_col >> 32; }\n\nCUDA_CALLABLE uint32_t bsr_get_col(const BsrRowCol& row_col) { return row_col & INT_MAX; }\n\ntemplate <typename T> struct BsrBlockIsNotZero {\n    int block_size;\n    const T* values;\n    T zero_mask;\n\n    BsrBlockIsNotZero(int block_size, const void* values, const uint64_t zero_mask)\n        : block_size(block_size)\n        , values(static_cast<const T*>(values))\n        , zero_mask(static_cast<T>(zero_mask))\n    {\n    }\n\n    CUDA_CALLABLE_DEVICE bool operator()(int block) const\n    {\n        if (!values)\n            return true;\n\n        const T* val = values + block * block_size;\n        for (int i = 0; i < block_size; ++i, ++val) {\n            if ((*val & zero_mask) != 0)\n                return true;\n        }\n        return false;\n    }\n};\n\ntemplate <> struct BsrBlockIsNotZero<void> {\n    BsrBlockIsNotZero(int block_size, const void* values, const uint64_t zero_mask) { }\n\n    CUDA_CALLABLE_DEVICE bool operator()(int block) const { return true; }\n};\n\nstruct BsrBlockInMask {\n    const int nrow;\n    const int ncol;\n    const int* bsr_offsets;\n    const int* bsr_columns;\n    const int* device_nnz;\n\n    CUDA_CALLABLE_DEVICE bool operator()(int index, int row, int col) const\n    {\n        if (device_nnz != nullptr && index >= *device_nnz)\n            return false;\n\n        if (row < 0 || row >= nrow || col < 0 || col >= ncol) {\n            return false;\n        }\n\n        if (bsr_offsets == nullptr)\n            return true;\n\n        int lower = bsr_offsets[row];\n        int upper = bsr_offsets[row + 1] - 1;\n\n        while (lower < upper) {\n            const int mid = lower + (upper - lower) / 2;\n\n            if (bsr_columns[mid] < col) {\n                lower = mid + 1;\n            } else {\n                upper = mid;\n            }\n        }\n\n        return lower == upper && (bsr_columns[lower] == col);\n    }\n};\n\ntemplate <typename T>\n__global__ void bsr_fill_triplet_key_values(\n    const int nnz,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const BsrBlockIsNotZero<T> nonZero,\n    const BsrBlockInMask mask,\n    int* block_indices,\n    BsrRowCol* tpl_row_col\n)\n{\n    int block = blockIdx.x * blockDim.x + threadIdx.x;\n    if (block >= nnz)\n        return;\n\n    const int row = tpl_rows[block];\n    const int col = tpl_columns[block];\n\n    const BsrRowCol row_col = mask(block, row, col) && nonZero(block) ? bsr_combine_row_col(row, col) : PRUNED_ROWCOL;\n\n    tpl_row_col[block] = row_col;\n    block_indices[block] = block;\n}\n\ntemplate <typename T>\n__global__ void\nbsr_find_row_offsets(uint32_t row_count, const T* d_nnz, const BsrRowCol* unique_row_col, int* row_offsets)\n{\n    const uint32_t row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row > row_count)\n        return;\n\n    const uint32_t nnz = *d_nnz;\n    if (row == 0 || nnz == 0) {\n        row_offsets[row] = 0;\n        return;\n    }\n\n    if (bsr_get_row(unique_row_col[nnz - 1]) < row) {\n        row_offsets[row] = nnz;\n        return;\n    }\n\n    // binary search for row start\n    uint32_t lower = 0;\n    uint32_t upper = nnz - 1;\n    while (lower < upper) {\n        uint32_t mid = lower + (upper - lower) / 2;\n\n        if (bsr_get_row(unique_row_col[mid]) < row) {\n            lower = mid + 1;\n        } else {\n            upper = mid;\n        }\n    }\n\n    row_offsets[row] = lower;\n}\n\n__global__ void bsr_set_column(const int* d_nnz, const BsrRowCol* unique_row_cols, int* bsr_cols)\n{\n    const uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i >= *d_nnz)\n        return;\n    const BsrRowCol row_col = unique_row_cols[i];\n    bsr_cols[i] = bsr_get_col(row_col);\n}\n\ntemplate <typename T>\nvoid launch_bsr_fill_triplet_key_values(\n    const int block_size,\n    const int nnz,\n    const BsrBlockInMask& mask,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const void* tpl_values,\n    const uint64_t scalar_zero_mask,\n    int* block_indices,\n    BsrRowCol* row_col\n)\n{\n    BsrBlockIsNotZero<T> isNotZero { block_size, tpl_values, scalar_zero_mask };\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_fill_triplet_key_values, nnz,\n        (nnz, tpl_rows, tpl_columns, isNotZero, mask, block_indices, row_col)\n    );\n}\n\n\n__global__ void bsr_transpose_fill_row_col(\n    const int nnz_upper_bound,\n    const int row_count,\n    const int* bsr_offsets,\n    const int* bsr_columns,\n    int* block_indices,\n    BsrRowCol* transposed_row_col\n)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i >= nnz_upper_bound) {\n        // Outside of allocated bounds, do nothing\n        return;\n    }\n\n    block_indices[i] = i;\n\n    if (i >= bsr_offsets[row_count]) {\n        // Below upper bound but above actual nnz count, mark as invalid\n        transposed_row_col[i] = PRUNED_ROWCOL;\n        return;\n    }\n\n    // Binary search for row\n    int lower = 0;\n    int upper = row_count - 1;\n\n    while (lower < upper) {\n        int mid = lower + (upper - lower) / 2;\n\n        if (bsr_offsets[mid + 1] <= i) {\n            lower = mid + 1;\n        } else {\n            upper = mid;\n        }\n    }\n\n    const int row = lower;\n    const int col = bsr_columns[i];\n    BsrRowCol row_col = bsr_combine_row_col(col, row);\n    transposed_row_col[i] = row_col;\n}\n\n}  // namespace\n\n\nWP_API void wp_bsr_matrix_from_triplets_device(\n    const int block_size,\n    int scalar_size,\n    const int row_count,\n    const int col_count,\n    const int nnz,\n    const int* tpl_nnz,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const void* tpl_values,\n    const uint64_t scalar_zero_mask,\n    const bool masked_topology,\n    int* tpl_block_offsets,\n    int* tpl_block_indices,\n    int* bsr_offsets,\n    int* bsr_columns,\n    int* bsr_nnz,\n    void* bsr_nnz_event\n)\n{\n    void* context = wp_cuda_context_get_current();\n    ContextGuard guard(context);\n\n    cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n\n    ScopedTemporary<BsrRowCol> combined_row_col(context, 2 * size_t(nnz));\n    ScopedTemporary<int> unique_triplet_count(context, 1);\n\n    bool return_summed_blocks = tpl_block_offsets != nullptr && tpl_block_indices != nullptr;\n    if (!return_summed_blocks) {\n        // if not provided, allocate temporary offset and indices buffers\n        tpl_block_offsets = static_cast<int*>(wp_alloc_device(context, size_t(nnz) * sizeof(int), \"(native:sparse)\"));\n        tpl_block_indices = static_cast<int*>(wp_alloc_device(context, size_t(nnz) * sizeof(int), \"(native:sparse)\"));\n    }\n\n\n    cub::DoubleBuffer<int> d_keys(tpl_block_indices, tpl_block_offsets);\n    cub::DoubleBuffer<BsrRowCol> d_values(combined_row_col.buffer(), combined_row_col.buffer() + nnz);\n\n    // Combine rows and columns so we can sort on them both,\n    // ensuring that blocks that should be pruned are moved to the end\n    BsrBlockInMask mask { row_count, col_count, masked_topology ? bsr_offsets : nullptr, bsr_columns, tpl_nnz };\n    if (scalar_zero_mask == 0 || tpl_values == nullptr)\n        scalar_size = 0;\n    switch (scalar_size) {\n    case sizeof(uint8_t):\n        launch_bsr_fill_triplet_key_values<uint8_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint16_t):\n        launch_bsr_fill_triplet_key_values<uint16_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint32_t):\n        launch_bsr_fill_triplet_key_values<uint32_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint64_t):\n        launch_bsr_fill_triplet_key_values<uint64_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    default:\n        // no scalar-level pruning\n        launch_bsr_fill_triplet_key_values<void>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    }\n\n\n    // Sort\n    {\n        size_t buff_size = 0;\n        check_cuda(cub::DeviceRadixSort::SortPairs(nullptr, buff_size, d_values, d_keys, nnz, 0, 64, stream));\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(cub::DeviceRadixSort::SortPairs(temp.buffer(), buff_size, d_values, d_keys, nnz, 0, 64, stream));\n\n        // Depending on data size and GPU architecture buffers may have been swapped or not\n        // Ensures the sorted keys are available in summed_block_indices if needed\n        if (return_summed_blocks && d_keys.Current() != tpl_block_indices) {\n            check_cuda(cudaMemcpyAsync(\n                tpl_block_indices, d_keys.Current(), nnz * sizeof(int), cudaMemcpyDeviceToDevice, stream\n            ));\n        }\n    }\n\n    // Runlength encode row-col sequences\n    {\n        size_t buff_size = 0;\n        check_cuda(\n            cub::DeviceRunLengthEncode::Encode(\n                nullptr, buff_size, d_values.Current(), d_values.Alternate(), tpl_block_offsets,\n                unique_triplet_count.buffer(), nnz, stream\n            )\n        );\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(\n            cub::DeviceRunLengthEncode::Encode(\n                temp.buffer(), buff_size, d_values.Current(), d_values.Alternate(), tpl_block_offsets,\n                unique_triplet_count.buffer(), nnz, stream\n            )\n        );\n    }\n\n    // Compute row offsets from sorted unique blocks\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_find_row_offsets, row_count + 1,\n        (row_count, unique_triplet_count.buffer(), d_values.Alternate(), bsr_offsets)\n    );\n\n    if (bsr_nnz) {\n        // Copy nnz to host, and record an event for the completed transfer if desired\n\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, bsr_nnz, bsr_offsets + row_count, sizeof(int), stream);\n\n        if (bsr_nnz_event) {\n            const bool external = true;\n            wp_cuda_event_record(bsr_nnz_event, stream, external);\n        }\n    }\n\n    // Set column indices\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_set_column, nnz, (bsr_offsets + row_count, d_values.Alternate(), bsr_columns)\n    );\n\n    // Scan repeated block counts\n    if (return_summed_blocks) {\n        size_t buff_size = 0;\n        check_cuda(\n            cub::DeviceScan::InclusiveSum(nullptr, buff_size, tpl_block_offsets, tpl_block_offsets, nnz, stream)\n        );\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(\n            cub::DeviceScan::InclusiveSum(temp.buffer(), buff_size, tpl_block_offsets, tpl_block_offsets, nnz, stream)\n        );\n    } else {\n        // free our temporary buffers\n        wp_free_device(context, tpl_block_offsets);\n        wp_free_device(context, tpl_block_indices);\n    }\n}\n\n\nWP_API void wp_bsr_transpose_device(\n    int row_count,\n    int col_count,\n    int nnz,\n    const int* bsr_offsets,\n    const int* bsr_columns,\n    int* transposed_bsr_offsets,\n    int* transposed_bsr_columns,\n    int* src_block_indices\n)\n{\n    void* context = wp_cuda_context_get_current();\n    ContextGuard guard(context);\n\n    cudaStream_t stream = static_cast<cudaStream_t>(wp_cuda_stream_get_current());\n\n    ScopedTemporary<BsrRowCol> combined_row_col(context, 2 * nnz);\n\n    cub::DoubleBuffer<int> d_keys(src_block_indices + nnz, src_block_indices);\n    cub::DoubleBuffer<BsrRowCol> d_values(combined_row_col.buffer(), combined_row_col.buffer() + nnz);\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_transpose_fill_row_col, nnz,\n        (nnz, row_count, bsr_offsets, bsr_columns, d_keys.Current(), d_values.Current())\n    );\n\n    // Sort blocks\n    {\n        size_t buff_size = 0;\n        check_cuda(cub::DeviceRadixSort::SortPairs(nullptr, buff_size, d_values, d_keys, nnz, 0, 64, stream));\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(cub::DeviceRadixSort::SortPairs(temp.buffer(), buff_size, d_values, d_keys, nnz, 0, 64, stream));\n\n        // Depending on data size and GPU architecture buffers may have been swapped or not\n        // Ensures the sorted keys are available in summed_block_indices if needed\n        if (d_keys.Current() != src_block_indices) {\n            check_cuda(cudaMemcpyAsync(\n                src_block_indices, src_block_indices + nnz, size_t(nnz) * sizeof(int), cudaMemcpyDeviceToDevice, stream\n            ));\n        }\n    }\n\n    // Compute row offsets from sorted unique blocks\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_find_row_offsets, col_count + 1,\n        (col_count, bsr_offsets + row_count, d_values.Current(), transposed_bsr_offsets)\n    );\n\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_set_column, nnz, (bsr_offsets + row_count, d_values.Current(), transposed_bsr_columns)\n    );\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"temp_buffer.h\"\n\n#include <cstdint>\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n\n#include <cub/device/device_radix_sort.cuh>\n#include <cub/device/device_run_length_encode.cuh>\n#include <cub/device/device_scan.cuh>\n\nextern hipCtx_t get_current_context();\n\nnamespace {\n\n// Combined row+column value that can be radix-sorted with CUB\nusing BsrRowCol = uint64_t;\n\nstatic constexpr BsrRowCol PRUNED_ROWCOL = ~BsrRowCol(0);\n\nCUDA_CALLABLE BsrRowCol bsr_combine_row_col(uint32_t row, uint32_t col)\n{\n    return (static_cast<uint64_t>(row) << 32) | col;\n}\n\nCUDA_CALLABLE uint32_t bsr_get_row(const BsrRowCol& row_col) { return row_col >> 32; }\n\nCUDA_CALLABLE uint32_t bsr_get_col(const BsrRowCol& row_col) { return row_col & INT_MAX; }\n\ntemplate <typename T> struct BsrBlockIsNotZero {\n    int block_size;\n    const T* values;\n    T zero_mask;\n\n    BsrBlockIsNotZero(int block_size, const void* values, const uint64_t zero_mask)\n        : block_size(block_size)\n        , values(static_cast<const T*>(values))\n        , zero_mask(static_cast<T>(zero_mask))\n    {\n    }\n\n    CUDA_CALLABLE_DEVICE bool operator()(int block) const\n    {\n        if (!values)\n            return true;\n\n        const T* val = values + block * block_size;\n        for (int i = 0; i < block_size; ++i, ++val) {\n            if ((*val & zero_mask) != 0)\n                return true;\n        }\n        return false;\n    }\n};\n\ntemplate <> struct BsrBlockIsNotZero<void> {\n    BsrBlockIsNotZero(int block_size, const void* values, const uint64_t zero_mask) { }\n\n    CUDA_CALLABLE_DEVICE bool operator()(int block) const { return true; }\n};\n\nstruct BsrBlockInMask {\n    const int nrow;\n    const int ncol;\n    const int* bsr_offsets;\n    const int* bsr_columns;\n    const int* device_nnz;\n\n    CUDA_CALLABLE_DEVICE bool operator()(int index, int row, int col) const\n    {\n        if (device_nnz != nullptr && index >= *device_nnz)\n            return false;\n\n        if (row < 0 || row >= nrow || col < 0 || col >= ncol) {\n            return false;\n        }\n\n        if (bsr_offsets == nullptr)\n            return true;\n\n        int lower = bsr_offsets[row];\n        int upper = bsr_offsets[row + 1] - 1;\n\n        while (lower < upper) {\n            const int mid = lower + (upper - lower) / 2;\n\n            if (bsr_columns[mid] < col) {\n                lower = mid + 1;\n            } else {\n                upper = mid;\n            }\n        }\n\n        return lower == upper && (bsr_columns[lower] == col);\n    }\n};\n\ntemplate <typename T>\n__global__ void bsr_fill_triplet_key_values(\n    const int nnz,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const BsrBlockIsNotZero<T> nonZero,\n    const BsrBlockInMask mask,\n    int* block_indices,\n    BsrRowCol* tpl_row_col\n)\n{\n    int block = blockIdx.x * blockDim.x + threadIdx.x;\n    if (block >= nnz)\n        return;\n\n    const int row = tpl_rows[block];\n    const int col = tpl_columns[block];\n\n    const BsrRowCol row_col = mask(block, row, col) && nonZero(block) ? bsr_combine_row_col(row, col) : PRUNED_ROWCOL;\n\n    tpl_row_col[block] = row_col;\n    block_indices[block] = block;\n}\n\ntemplate <typename T>\n__global__ void\nbsr_find_row_offsets(uint32_t row_count, const T* d_nnz, const BsrRowCol* unique_row_col, int* row_offsets)\n{\n    const uint32_t row = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (row > row_count)\n        return;\n\n    const uint32_t nnz = *d_nnz;\n    if (row == 0 || nnz == 0) {\n        row_offsets[row] = 0;\n        return;\n    }\n\n    if (bsr_get_row(unique_row_col[nnz - 1]) < row) {\n        row_offsets[row] = nnz;\n        return;\n    }\n\n    // binary search for row start\n    uint32_t lower = 0;\n    uint32_t upper = nnz - 1;\n    while (lower < upper) {\n        uint32_t mid = lower + (upper - lower) / 2;\n\n        if (bsr_get_row(unique_row_col[mid]) < row) {\n            lower = mid + 1;\n        } else {\n            upper = mid;\n        }\n    }\n\n    row_offsets[row] = lower;\n}\n\n__global__ void bsr_set_column(const int* d_nnz, const BsrRowCol* unique_row_cols, int* bsr_cols)\n{\n    const uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;\n    if (i >= *d_nnz)\n        return;\n    const BsrRowCol row_col = unique_row_cols[i];\n    bsr_cols[i] = bsr_get_col(row_col);\n}\n\ntemplate <typename T>\nvoid launch_bsr_fill_triplet_key_values(\n    const int block_size,\n    const int nnz,\n    const BsrBlockInMask& mask,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const void* tpl_values,\n    const uint64_t scalar_zero_mask,\n    int* block_indices,\n    BsrRowCol* row_col\n)\n{\n    BsrBlockIsNotZero<T> isNotZero { block_size, tpl_values, scalar_zero_mask };\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_fill_triplet_key_values, nnz,\n        (nnz, tpl_rows, tpl_columns, isNotZero, mask, block_indices, row_col)\n    );\n}\n\n\n__global__ void bsr_transpose_fill_row_col(\n    const int nnz_upper_bound,\n    const int row_count,\n    const int* bsr_offsets,\n    const int* bsr_columns,\n    int* block_indices,\n    BsrRowCol* transposed_row_col\n)\n{\n    int i = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (i >= nnz_upper_bound) {\n        // Outside of allocated bounds, do nothing\n        return;\n    }\n\n    block_indices[i] = i;\n\n    if (i >= bsr_offsets[row_count]) {\n        // Below upper bound but above actual nnz count, mark as invalid\n        transposed_row_col[i] = PRUNED_ROWCOL;\n        return;\n    }\n\n    // Binary search for row\n    int lower = 0;\n    int upper = row_count - 1;\n\n    while (lower < upper) {\n        int mid = lower + (upper - lower) / 2;\n\n        if (bsr_offsets[mid + 1] <= i) {\n            lower = mid + 1;\n        } else {\n            upper = mid;\n        }\n    }\n\n    const int row = lower;\n    const int col = bsr_columns[i];\n    BsrRowCol row_col = bsr_combine_row_col(col, row);\n    transposed_row_col[i] = row_col;\n}\n\n}  // namespace\n\n\nWP_API void wp_bsr_matrix_from_triplets_device(\n    const int block_size,\n    int scalar_size,\n    const int row_count,\n    const int col_count,\n    const int nnz,\n    const int* tpl_nnz,\n    const int* tpl_rows,\n    const int* tpl_columns,\n    const void* tpl_values,\n    const uint64_t scalar_zero_mask,\n    const bool masked_topology,\n    int* tpl_block_offsets,\n    int* tpl_block_indices,\n    int* bsr_offsets,\n    int* bsr_columns,\n    int* bsr_nnz,\n    void* bsr_nnz_event\n)\n{\n    void* context = wp_cuda_context_get_current();\n    ContextGuard guard(context);\n\n    hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n\n    ScopedTemporary<BsrRowCol> combined_row_col(context, 2 * size_t(nnz));\n    ScopedTemporary<int> unique_triplet_count(context, 1);\n\n    bool return_summed_blocks = tpl_block_offsets != nullptr && tpl_block_indices != nullptr;\n    if (!return_summed_blocks) {\n        // if not provided, allocate temporary offset and indices buffers\n        tpl_block_offsets = static_cast<int*>(wp_alloc_device(context, size_t(nnz) * sizeof(int), \"(native:sparse)\"));\n        tpl_block_indices = static_cast<int*>(wp_alloc_device(context, size_t(nnz) * sizeof(int), \"(native:sparse)\"));\n    }\n\n\n    hipcub::DoubleBuffer<int> d_keys(tpl_block_indices, tpl_block_offsets);\n    hipcub::DoubleBuffer<BsrRowCol> d_values(combined_row_col.buffer(), combined_row_col.buffer() + nnz);\n\n    // Combine rows and columns so we can sort on them both,\n    // ensuring that blocks that should be pruned are moved to the end\n    BsrBlockInMask mask { row_count, col_count, masked_topology ? bsr_offsets : nullptr, bsr_columns, tpl_nnz };\n    if (scalar_zero_mask == 0 || tpl_values == nullptr)\n        scalar_size = 0;\n    switch (scalar_size) {\n    case sizeof(uint8_t):\n        launch_bsr_fill_triplet_key_values<uint8_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint16_t):\n        launch_bsr_fill_triplet_key_values<uint16_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint32_t):\n        launch_bsr_fill_triplet_key_values<uint32_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    case sizeof(uint64_t):\n        launch_bsr_fill_triplet_key_values<uint64_t>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    default:\n        // no scalar-level pruning\n        launch_bsr_fill_triplet_key_values<void>(\n            block_size, nnz, mask, tpl_rows, tpl_columns, tpl_values, scalar_zero_mask, d_keys.Current(),\n            d_values.Current()\n        );\n        break;\n    }\n\n\n    // Sort\n    {\n        size_t buff_size = 0;\n        check_cuda(hipcub::DeviceRadixSort::SortPairs(nullptr, buff_size, d_values, d_keys, nnz, 0, 64, stream));\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(hipcub::DeviceRadixSort::SortPairs(temp.buffer(), buff_size, d_values, d_keys, nnz, 0, 64, stream));\n\n        // Depending on data size and GPU architecture buffers may have been swapped or not\n        // Ensures the sorted keys are available in summed_block_indices if needed\n        if (return_summed_blocks && d_keys.Current() != tpl_block_indices) {\n            check_cuda(hipMemcpyAsync(\n                tpl_block_indices, d_keys.Current(), nnz * sizeof(int), hipMemcpyDeviceToDevice, stream\n            ));\n        }\n    }\n\n    // Runlength encode row-col sequences\n    {\n        size_t buff_size = 0;\n        check_cuda(\n            hipcub::DeviceRunLengthEncode::Encode(\n                nullptr, buff_size, d_values.Current(), d_values.Alternate(), tpl_block_offsets,\n                unique_triplet_count.buffer(), nnz, stream\n            )\n        );\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(\n            hipcub::DeviceRunLengthEncode::Encode(\n                temp.buffer(), buff_size, d_values.Current(), d_values.Alternate(), tpl_block_offsets,\n                unique_triplet_count.buffer(), nnz, stream\n            )\n        );\n    }\n\n    // Compute row offsets from sorted unique blocks\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_find_row_offsets, row_count + 1,\n        (row_count, unique_triplet_count.buffer(), d_values.Alternate(), bsr_offsets)\n    );\n\n    if (bsr_nnz) {\n        // Copy nnz to host, and record an event for the completed transfer if desired\n\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, bsr_nnz, bsr_offsets + row_count, sizeof(int), stream);\n\n        if (bsr_nnz_event) {\n            const bool external = true;\n            wp_cuda_event_record(bsr_nnz_event, stream, external);\n        }\n    }\n\n    // Set column indices\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_set_column, nnz, (bsr_offsets + row_count, d_values.Alternate(), bsr_columns)\n    );\n\n    // Scan repeated block counts\n    if (return_summed_blocks) {\n        size_t buff_size = 0;\n        check_cuda(\n            hipcub::DeviceScan::InclusiveSum(nullptr, buff_size, tpl_block_offsets, tpl_block_offsets, nnz, stream)\n        );\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(\n            hipcub::DeviceScan::InclusiveSum(temp.buffer(), buff_size, tpl_block_offsets, tpl_block_offsets, nnz, stream)\n        );\n    } else {\n        // free our temporary buffers\n        wp_free_device(context, tpl_block_offsets);\n        wp_free_device(context, tpl_block_indices);\n    }\n}\n\n\nWP_API void wp_bsr_transpose_device(\n    int row_count,\n    int col_count,\n    int nnz,\n    const int* bsr_offsets,\n    const int* bsr_columns,\n    int* transposed_bsr_offsets,\n    int* transposed_bsr_columns,\n    int* src_block_indices\n)\n{\n    void* context = wp_cuda_context_get_current();\n    ContextGuard guard(context);\n\n    hipStream_t stream = static_cast<hipStream_t>(wp_cuda_stream_get_current());\n\n    ScopedTemporary<BsrRowCol> combined_row_col(context, 2 * nnz);\n\n    hipcub::DoubleBuffer<int> d_keys(src_block_indices + nnz, src_block_indices);\n    hipcub::DoubleBuffer<BsrRowCol> d_values(combined_row_col.buffer(), combined_row_col.buffer() + nnz);\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_transpose_fill_row_col, nnz,\n        (nnz, row_count, bsr_offsets, bsr_columns, d_keys.Current(), d_values.Current())\n    );\n\n    // Sort blocks\n    {\n        size_t buff_size = 0;\n        check_cuda(hipcub::DeviceRadixSort::SortPairs(nullptr, buff_size, d_values, d_keys, nnz, 0, 64, stream));\n        ScopedTemporary<> temp(context, buff_size);\n        check_cuda(hipcub::DeviceRadixSort::SortPairs(temp.buffer(), buff_size, d_values, d_keys, nnz, 0, 64, stream));\n\n        // Depending on data size and GPU architecture buffers may have been swapped or not\n        // Ensures the sorted keys are available in summed_block_indices if needed\n        if (d_keys.Current() != src_block_indices) {\n            check_cuda(hipMemcpyAsync(\n                src_block_indices, src_block_indices + nnz, size_t(nnz) * sizeof(int), hipMemcpyDeviceToDevice, stream\n            ));\n        }\n    }\n\n    // Compute row offsets from sorted unique blocks\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_find_row_offsets, col_count + 1,\n        (col_count, bsr_offsets + row_count, d_values.Current(), transposed_bsr_offsets)\n    );\n\n\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bsr_set_column, nnz, (bsr_offsets + row_count, d_values.Current(), transposed_bsr_columns)\n    );\n}\n", "source": "/kaggle/working/warp/warp/native/sparse.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#define CUBQL_GPU_BUILDER_IMPLEMENTATION 1\n\n#include \"warp.h\"\n\n#include \"bvh.h\"\n#include \"cuda_util.h\"\n#include \"sort.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n#define REORDER_HOST_TREE\n\n// CUB must be included before cuBQL. cuBQL's math/common.h includes <stdexcept>,\n// which causes CCCL's _CCCL_HAS_EXCEPTIONS() to be true when typeid.h is later\n// pulled in by CUB. This makes __throw_out_of_range non-constexpr, breaking a\n// static_assert in typeid.h on GCC < 12 (which lacks P2448R2 relaxed constexpr).\n#include <cub/cub.cuh>\n\n#ifndef WP_DISABLE_CUBQL\n#include \"cuBQL/bvh.h\"\n#endif\n\nextern CUcontext get_current_context();\n\nnamespace wp {\nvoid bvh_create_host(vec3* lowers, vec3* uppers, int num_items, int constructor_type, BVH& bvh, int leaf_size);\nvoid bvh_destroy_host(BVH& bvh);\n\n__global__ void memset_kernel(int* dest, int value, size_t n)\n{\n    const size_t tid\n        = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n\n    if (tid < n) {\n        dest[tid] = value;\n    }\n}\n\n// for LBVH: this will start with some muted leaf nodes, but that is okay, we can still trace up because there parents\n// information is still valid the only thing worth mentioning is that when the parent leaf node is also a leaf node, we\n// need to recompute its bounds, since their child information are lost for a compact tree such as those from SAH or\n// Median constructor, there is no muted leaf nodes\n__global__ void bvh_refit_kernel(\n    int n,\n    const int* __restrict__ parents,\n    int* __restrict__ child_count,\n    const int* __restrict__ primitive_indices,\n    BVHPackedNodeHalf* __restrict__ node_lowers,\n    BVHPackedNodeHalf* __restrict__ node_uppers,\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        bool leaf = node_lowers[index].b;\n        int parent = parents[index];\n\n        if (leaf) {\n            BVHPackedNodeHalf& lower = node_lowers[index];\n            BVHPackedNodeHalf& upper = node_uppers[index];\n            // update the leaf node\n\n            // only need to compute bound when this is a valid leaf node\n            if (!node_lowers[parent].b) {\n                const int start = lower.i;\n                const int end = upper.i;\n\n                bounds3 bound;\n                for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                    const int primitive = primitive_indices[primitive_counter];\n                    bound.add_bounds(item_lowers[primitive], item_uppers[primitive]);\n                }\n                (vec3&)lower = bound.lower;\n                (vec3&)upper = bound.upper;\n            }\n        } else {\n            // only keep leaf threads\n            return;\n        }\n\n        // update hierarchy\n        for (;;) {\n            parent = parents[index];\n            // reached root\n            if (parent == -1)\n                return;\n\n            // ensure all writes are visible\n            __threadfence();\n\n            int finished = atomicAdd(&child_count[parent], 1);\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (finished == 1) {\n                BVHPackedNodeHalf& parent_lower = node_lowers[parent];\n                BVHPackedNodeHalf& parent_upper = node_uppers[parent];\n                if (parent_lower.b)\n                // a packed leaf node can still be a parent in LBVH, we need to recompute its bounds\n                // since we've lost its left and right child node index in the muting process\n                {\n                    // update the leaf node\n                    int parent_parent = parents[parent];\n                    ;\n\n                    // only need to compute bound when this is a valid leaf node\n                    if (!node_lowers[parent_parent].b) {\n                        const int start = parent_lower.i;\n                        const int end = parent_upper.i;\n                        bounds3 bound;\n                        for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                            const int primitive = primitive_indices[primitive_counter];\n                            bound.add_bounds(item_lowers[primitive], item_uppers[primitive]);\n                        }\n\n                        (vec3&)parent_lower = bound.lower;\n                        (vec3&)parent_upper = bound.upper;\n                    }\n                } else {\n                    const int left_child = parent_lower.i;\n                    const int right_child = parent_upper.i;\n\n                    vec3 left_lower = (vec3&)(node_lowers[left_child]);\n                    vec3 left_upper = (vec3&)(node_uppers[left_child]);\n                    vec3 right_lower = (vec3&)(node_lowers[right_child]);\n                    vec3 right_upper = (vec3&)(node_uppers[right_child]);\n\n                    // union of child bounds\n                    vec3 lower = min(left_lower, right_lower);\n                    vec3 upper = max(left_upper, right_upper);\n\n                    // write new BVH nodes\n                    (vec3&)parent_lower = lower;\n                    (vec3&)parent_upper = upper;\n                }\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////\n\n// Create a linear BVH as described in Fast and Simple Agglomerative LBVH construction\n// this is a bottom-up clustering method that outputs one node per-leaf\n//\nclass LinearBVHBuilderGPU {\npublic:\n    LinearBVHBuilderGPU();\n    ~LinearBVHBuilderGPU();\n\n    // takes a bvh (host ref), and pointers to the GPU lower and upper bounds for each triangle\n    void build(\n        BVH& bvh,\n        const vec3* item_lowers,\n        const vec3* item_uppers,\n        int num_items,\n        bounds3* total_bounds,\n        int* item_groups\n    );\n\nprivate:\n    // temporary data used during building\n    int* indices;\n    uint64_t* keys;\n    int* deltas;\n    int* range_lefts;\n    int* range_rights;\n    int* num_children;\n\n    // bounds data when total item bounds built on GPU\n    vec3* total_lower;\n    vec3* total_upper;\n    vec3* total_inv_edges;\n};\n\n////////////////////////////////////////////////////////\n\n\n__global__ void compute_morton_codes(\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers,\n    int n,\n    const vec3* grid_lower,\n    const vec3* grid_inv_edges,\n    int* __restrict__ indices,\n    uint64_t* __restrict__ keys,\n    const int* __restrict__ item_groups\n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        vec3 lower = item_lowers[index];\n        vec3 upper = item_uppers[index];\n\n        vec3 center = 0.5f * (lower + upper);\n\n        vec3 local = cw_mul((center - grid_lower[0]), grid_inv_edges[0]);\n\n        // 10-bit Morton codes stored in lower 30bits (1024^3 effective resolution)\n        // Group stored in upper 32 bits\n        uint64_t morton_code = static_cast<uint64_t>(morton3<1024>(local[0], local[1], local[2]));\n        const uint64_t group = item_groups ? static_cast<uint32_t>(item_groups[index]) : 0u;\n        const uint64_t key = (group << 32) | morton_code;\n\n        indices[index] = index;\n        keys[index] = key;\n    }\n}\n\n// compute a distance metric between adjacent keys; larger across groups\n// Using raw XOR magnitude preserves the original builder's assumptions\n__global__ void compute_key_deltas(const uint64_t* __restrict__ keys, int* __restrict__ deltas, int n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const uint64_t diff = keys[index] ^ keys[index + 1];\n        deltas[index] = (diff == 0) ? 64 : __clzll(diff);\n    }\n}\n\n__global__ void build_leaves(\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers,\n    int n,\n    const int* __restrict__ indices,\n    int* __restrict__ range_lefts,\n    int* __restrict__ range_rights,\n    BVHPackedNodeHalf* __restrict__ lowers,\n    BVHPackedNodeHalf* __restrict__ uppers\n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const int item = indices[index];\n\n        vec3 lower = item_lowers[item];\n        vec3 upper = item_uppers[item];\n\n        // write leaf nodes using position indices and [start, end) range\n        lowers[index] = make_node(lower, index, true);\n        uppers[index] = make_node(upper, index, false);\n\n        // write leaf key ranges\n        range_lefts[index] = index;\n        range_rights[index] = index;\n    }\n}\n\n// this bottom-up process assigns left and right children and combines bounds to form internal nodes\n// there is one thread launched per-leaf node, each thread calculates it's parent node and assigns\n// itself to either the left or right parent slot, the last child to complete the parent and moves\n// up the hierarchy\n__global__ void build_hierarchy(\n    int n,\n    int* root,\n    const int* __restrict__ deltas,\n    const uint64_t* __restrict__ keys,\n    int* __restrict__ num_children,\n    const int* __restrict__ primitive_indices,\n    volatile int* __restrict__ range_lefts,\n    volatile int* __restrict__ range_rights,\n    volatile int* __restrict__ parents,\n    volatile BVHPackedNodeHalf* __restrict__ lowers,\n    volatile BVHPackedNodeHalf* __restrict__ uppers\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const int internal_offset = n;\n\n        for (;;) {\n            int left = range_lefts[index];\n            int right = range_rights[index];\n\n            // check if we are the root node, if so then store out our index and terminate\n            if (left == 0 && right == n - 1) {\n                *root = index;\n                parents[index] = -1;\n\n                break;\n            }\n\n            int childCount = 0;\n\n            int parent;\n\n            // group away selection of parent. We need to make sure that:\n            // 1) If node spans a single group and exactly one neighbor keeps us in-group, choose that side\n            // 2) Otherwise, use the original delta rule with parity tie-break to avoid ladders\n            // This is important to make sure that our tree does not mix groups until a single group root is formed.\n            bool parent_right = false;\n            if (left == 0) {\n                parent_right = true;\n            } else {\n                bool decided = false;\n                const uint32_t group_left = (uint32_t)(keys[left] >> 32);\n                const uint32_t group_right = (uint32_t)(keys[right] >> 32);\n\n                // Check if either of the neighbors keep us in the same group\n                if (group_left == group_right) {\n                    const uint32_t group = group_left;\n                    const bool can_go_right_same = (right < n - 1) && ((uint32_t)(keys[right + 1] >> 32) == group);\n                    const bool can_go_left_same = ((uint32_t)(keys[left - 1] >> 32) == group);\n\n                    // If exactly one neighbor keeps us in the same group, choose that side\n                    if (can_go_right_same ^ can_go_left_same) {\n                        parent_right = can_go_right_same;\n                        decided = true;\n                    }\n\n                    // If both or neither keep us in the same group, fall through to delta rule\n                }\n\n                if (!decided) {\n                    // prefer side with greater common prefix\n                    if (right != n - 1 && deltas[right] >= deltas[left - 1]) {\n                        if (deltas[right] == deltas[left - 1])\n                            parent_right = (primitive_indices[left - 1] % 2) ^ (primitive_indices[right] % 2);\n                        else\n                            parent_right = true;\n                    } else {\n                        parent_right = false;\n                    }\n                }\n            }\n\n            if (parent_right) {\n                parent = right + internal_offset;\n\n                // set parent left child\n                parents[index] = parent;\n                lowers[parent].i = index;\n                range_lefts[parent] = left;\n\n                // ensure above writes are visible to all threads\n                __threadfence();\n\n                childCount = atomicAdd(&num_children[parent], 1);\n            } else {\n                parent = left + internal_offset - 1;\n\n                // set parent right child\n                parents[index] = parent;\n                uppers[parent].i = index;\n                range_rights[parent] = right;\n\n                // ensure above writes are visible to all threads\n                __threadfence();\n\n                childCount = atomicAdd(&num_children[parent], 1);\n            }\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (childCount == 1) {\n                const int left_child = lowers[parent].i;\n                const int right_child = uppers[parent].i;\n\n                vec3 left_lower = vec3(lowers[left_child].x, lowers[left_child].y, lowers[left_child].z);\n\n                vec3 left_upper = vec3(uppers[left_child].x, uppers[left_child].y, uppers[left_child].z);\n\n                vec3 right_lower = vec3(lowers[right_child].x, lowers[right_child].y, lowers[right_child].z);\n\n\n                vec3 right_upper = vec3(uppers[right_child].x, uppers[right_child].y, uppers[right_child].z);\n\n                // bounds_union of child bounds; ensure min on lowers and max on uppers\n                vec3 lower = min(left_lower, right_lower);\n                vec3 upper = max(left_upper, right_upper);\n\n                // write new BVH nodes\n                make_node(lowers + parent, lower, left_child, false);\n                make_node(uppers + parent, upper, right_child, false);\n\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n/*\n * LBVH uses a bottom-up constructor which makes variable-sized leaf nodes more challenging to achieve.\n * Simply splitting the ordered primitives into uniform groups of size leaf_size will result in poor\n * quality. Instead, after the hierarchy is built, we convert any intermediate node whose size is\n * <= leaf_size into a new leaf node. This process is done using the new kernel function called\n * mark_packed_leaf_nodes .\n */\n__global__ void mark_packed_leaf_nodes(\n    int n,\n    const int* __restrict__ range_lefts,\n    const int* __restrict__ range_rights,\n    const int* __restrict__ parents,\n    const uint64_t* __restrict__ keys,\n    BVHPackedNodeHalf* __restrict__ lowers,\n    BVHPackedNodeHalf* __restrict__ uppers,\n    const int leaf_size\n)\n{\n    int node_index = blockDim.x * blockIdx.x + threadIdx.x;\n    if (node_index < n) {\n        // mark the node as leaf if its range is less than leaf_size or it is deeper than BVH_QUERY_STACK_SIZE\n        // this will forever mute its child nodes so that they will never be accessed\n\n        // calculate depth\n        int depth = 1;\n        int parent = parents[node_index];\n        while (parent != -1) {\n            parent = parents[parent];\n            depth++;\n        }\n\n        int left = range_lefts[node_index];\n        // the LBVH constructor's range is defined as left <= i <= right\n        // we need to convert it to our convention: left <= i < right\n        int right = range_rights[node_index] + 1;\n\n        // avoid creating packed leaves that straddle group boundaries\n        bool single_group = true;\n        const uint64_t group_left = keys[left] >> 32;\n        const uint64_t group_right = keys[right - 1] >> 32;\n        single_group = (group_left == group_right);\n\n        if (single_group && (right - left <= leaf_size || depth >= BVH_QUERY_STACK_SIZE)) {\n            lowers[node_index].b = 1;\n            lowers[node_index].i = left;\n            uppers[node_index].i = right;\n        }\n    }\n}\n\n\nCUDA_CALLABLE inline vec3 Vec3Max(const vec3& a, const vec3& b) { return wp::max(a, b); }\nCUDA_CALLABLE inline vec3 Vec3Min(const vec3& a, const vec3& b) { return wp::min(a, b); }\n\n__global__ void compute_total_bounds(\n    const vec3* item_lowers, const vec3* item_uppers, vec3* total_lower, vec3* total_upper, int num_items\n)\n{\n    typedef cub::BlockReduce<vec3, 256> BlockReduce;\n\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n\n    const int blockStart = blockDim.x * blockIdx.x;\n    const int numValid = ::min(num_items - blockStart, blockDim.x);\n\n    const int tid = blockStart + threadIdx.x;\n\n    if (tid < num_items) {\n        vec3 lower = item_lowers[tid];\n        vec3 upper = item_uppers[tid];\n\n        vec3 block_upper = BlockReduce(temp_storage).Reduce(upper, Vec3Max, numValid);\n\n        // sync threads because second reduce uses same temp storage as first\n        __syncthreads();\n\n        vec3 block_lower = BlockReduce(temp_storage).Reduce(lower, Vec3Min, numValid);\n\n        if (threadIdx.x == 0) {\n            // write out block results, expanded by the radius\n            atomic_max(total_upper, block_upper);\n            atomic_min(total_lower, block_lower);\n        }\n    }\n}\n\n// compute inverse edge length, this is just done on the GPU to avoid a CPU->GPU sync point\n__global__ void compute_total_inv_edges(const vec3* total_lower, const vec3* total_upper, vec3* total_inv_edges)\n{\n    vec3 edges = (total_upper[0] - total_lower[0]);\n    edges += vec3(0.0001f);\n\n    total_inv_edges[0] = vec3(1.0f / edges[0], 1.0f / edges[1], 1.0f / edges[2]);\n}\n\n\nLinearBVHBuilderGPU::LinearBVHBuilderGPU()\n    : indices(NULL)\n    , keys(NULL)\n    , deltas(NULL)\n    , range_lefts(NULL)\n    , range_rights(NULL)\n    , num_children(NULL)\n    , total_lower(NULL)\n    , total_upper(NULL)\n    , total_inv_edges(NULL)\n{\n    total_lower = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n    total_upper = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n    total_inv_edges = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n}\n\nLinearBVHBuilderGPU::~LinearBVHBuilderGPU()\n{\n    wp_free_device(WP_CURRENT_CONTEXT, total_lower);\n    wp_free_device(WP_CURRENT_CONTEXT, total_upper);\n    wp_free_device(WP_CURRENT_CONTEXT, total_inv_edges);\n}\n\n\nvoid LinearBVHBuilderGPU::build(\n    BVH& bvh, const vec3* item_lowers, const vec3* item_uppers, int num_items, bounds3* total_bounds, int* item_groups\n)\n{\n    // allocate temporary memory used during  building\n    indices\n        = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * num_items * 2, \"(native:bvh)\");  // *2 for radix sort\n    keys = (uint64_t*)wp_alloc_device(\n        WP_CURRENT_CONTEXT, sizeof(uint64_t) * num_items * 2, \"(native:bvh)\"\n    );  // *2 for radix sort\n    deltas = (int*)wp_alloc_device(\n        WP_CURRENT_CONTEXT, sizeof(int) * num_items, \"(native:bvh)\"\n    );  // highest differentiating bit between keys for item i and i+1\n    range_lefts = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n    range_rights = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n    num_children = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n\n    // if total bounds supplied by the host then we just\n    // compute our edge length and upload it to the GPU directly\n    if (total_bounds) {\n        // calculate Morton codes\n        vec3 edges = (*total_bounds).edges();\n        edges += vec3(0.0001f);\n\n        vec3 inv_edges = vec3(1.0f / edges[0], 1.0f / edges[1], 1.0f / edges[2]);\n\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_lower, &total_bounds->lower[0], sizeof(vec3));\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_upper, &total_bounds->upper[0], sizeof(vec3));\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_inv_edges, &inv_edges[0], sizeof(vec3));\n    } else {\n        // IEEE-754 bit patterns for \u00b1 FLT_MAX\n        constexpr int FLT_MAX_BITS = 0x7f7fffff;\n        constexpr int NEG_FLT_MAX_BITS = 0xff7fffff;\n\n        // total_lower := ( +FLT_MAX, +FLT_MAX, +FLT_MAX )\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, memset_kernel, sizeof(vec3) / 4, ((int*)total_lower, FLT_MAX_BITS, sizeof(vec3) / 4)\n        );\n\n        // total_upper := ( -FLT_MAX, -FLT_MAX, -FLT_MAX )\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, memset_kernel, sizeof(vec3) / 4, ((int*)total_upper, NEG_FLT_MAX_BITS, sizeof(vec3) / 4)\n        );\n\n        // compute the total bounds on the GPU\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, compute_total_bounds, num_items,\n            (item_lowers, item_uppers, total_lower, total_upper, num_items)\n        );\n\n        // compute the total edge length\n        wp_launch_device(WP_CURRENT_CONTEXT, compute_total_inv_edges, 1, (total_lower, total_upper, total_inv_edges));\n    }\n\n    // assign 30-bit Morton code based on the centroid of each triangle and bounds for each leaf\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, compute_morton_codes, num_items,\n        (item_lowers, item_uppers, num_items, total_lower, total_inv_edges, indices, keys, item_groups)\n    );\n\n    // sort items based on Morton key (note the 64-bit sort key includes group in upper 32 bits and morton code in lower\n    // 32 bits)\n    radix_sort_pairs_device(WP_CURRENT_CONTEXT, keys, indices, num_items);\n    wp_memcpy_d2d(WP_CURRENT_CONTEXT, bvh.primitive_indices, indices, sizeof(int) * num_items);\n\n    // calculate deltas between adjacent keys\n    wp_launch_device(WP_CURRENT_CONTEXT, compute_key_deltas, num_items, (keys, deltas, num_items - 1));\n\n    // initialize leaf nodes\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, build_leaves, num_items,\n        (item_lowers, item_uppers, num_items, indices, range_lefts, range_rights, bvh.node_lowers, bvh.node_uppers)\n    );\n\n    // reset children count, this is our atomic counter so we know when an internal node is complete, only used during\n    // building\n    wp_memset_device(WP_CURRENT_CONTEXT, num_children, 0, sizeof(int) * bvh.max_nodes);\n\n    // build the tree and internal node bounds\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, build_hierarchy, num_items,\n        (num_items, bvh.root, deltas, keys, num_children, bvh.primitive_indices, range_lefts, range_rights,\n         bvh.node_parents, bvh.node_lowers, bvh.node_uppers)\n    );\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, mark_packed_leaf_nodes, bvh.max_nodes,\n        (bvh.max_nodes, range_lefts, range_rights, bvh.node_parents, keys, bvh.node_lowers, bvh.node_uppers,\n         bvh.leaf_size)\n    );\n\n    // free temporary memory\n    wp_free_device(WP_CURRENT_CONTEXT, indices);\n    wp_free_device(WP_CURRENT_CONTEXT, keys);\n    wp_free_device(WP_CURRENT_CONTEXT, deltas);\n\n    wp_free_device(WP_CURRENT_CONTEXT, range_lefts);\n    wp_free_device(WP_CURRENT_CONTEXT, range_rights);\n    wp_free_device(WP_CURRENT_CONTEXT, num_children);\n}\n\n// buffer_size is the number of T, not the number of bytes\ntemplate <typename T> T* make_device_buffer_of(void* context, T* host_buffer, size_t buffer_size)\n{\n    T* device_buffer = (T*)wp_alloc_device(context, sizeof(T) * buffer_size, \"(native:bvh)\");\n    ;\n    wp_memcpy_h2d(context, device_buffer, host_buffer, sizeof(T) * buffer_size);\n\n    return device_buffer;\n}\n\nvoid copy_host_tree_to_device(void* context, BVH& bvh_host, BVH& bvh_device_on_host)\n{\n    if (!bvh_host.item_groups)\n    // if it's grouped bvh it's already reordered\n    {\n        reorder_top_down_bvh(bvh_host);\n    }\n\n    bvh_device_on_host.num_nodes = bvh_host.num_nodes;\n    bvh_device_on_host.num_leaf_nodes = bvh_host.num_leaf_nodes;\n    bvh_device_on_host.max_nodes = bvh_host.max_nodes;\n    bvh_device_on_host.num_items = bvh_host.num_items;\n    bvh_device_on_host.max_depth = bvh_host.max_depth;\n    bvh_device_on_host.leaf_size = bvh_host.leaf_size;\n\n    bvh_device_on_host.root = (int*)wp_alloc_device(context, sizeof(int), \"(native:bvh)\");\n    wp_memcpy_h2d(context, bvh_device_on_host.root, bvh_host.root, sizeof(int));\n    bvh_device_on_host.context = context;\n\n    bvh_device_on_host.node_lowers = make_device_buffer_of(context, bvh_host.node_lowers, bvh_host.max_nodes);\n    bvh_device_on_host.node_uppers = make_device_buffer_of(context, bvh_host.node_uppers, bvh_host.max_nodes);\n    bvh_device_on_host.node_parents = make_device_buffer_of(context, bvh_host.node_parents, bvh_host.max_nodes);\n    bvh_device_on_host.primitive_indices\n        = make_device_buffer_of(context, bvh_host.primitive_indices, bvh_host.num_items);\n}\n\n// create in-place given existing descriptor\nvoid bvh_create_device(\n    void* context,\n    vec3* lowers,\n    vec3* uppers,\n    int num_items,\n    int constructor_type,\n    int* groups,\n    int leaf_size,\n    BVH& bvh_device_on_host\n)\n{\n    ContextGuard guard(context);\n    if (constructor_type == BVH_CONSTRUCTOR_SAH || constructor_type == BVH_CONSTRUCTOR_MEDIAN)\n    // CPU based constructors\n    {\n        // copy bounds back to CPU\n        std::vector<vec3> lowers_host(num_items);\n        std::vector<vec3> uppers_host(num_items);\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, lowers_host.data(), lowers, sizeof(vec3) * num_items);\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, uppers_host.data(), uppers, sizeof(vec3) * num_items);\n\n        // copy groups back to CPU (if exists)\n        std::vector<int> groups_host(num_items);\n        int* groups_host_ptr = nullptr;\n        if (groups) {\n            wp_memcpy_d2h(WP_CURRENT_CONTEXT, groups_host.data(), groups, sizeof(int) * num_items);\n            groups_host_ptr = groups_host.data();\n        }\n\n        // run CPU based constructor\n        wp::BVH bvh_host;\n        wp::bvh_create_host(\n            lowers_host.data(), uppers_host.data(), num_items, constructor_type, groups_host_ptr, leaf_size, bvh_host\n        );\n\n        // copy host tree to device\n        wp::copy_host_tree_to_device(WP_CURRENT_CONTEXT, bvh_host, bvh_device_on_host);\n        // replace host bounds with device bounds\n        bvh_device_on_host.item_lowers = lowers;\n        bvh_device_on_host.item_uppers = uppers;\n        bvh_device_on_host.item_groups = groups;\n        // node_counts is not allocated for host tree\n        bvh_device_on_host.node_counts\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        wp::bvh_destroy_host(bvh_host);\n    } else if (constructor_type == BVH_CONSTRUCTOR_LBVH) {\n        bvh_device_on_host.leaf_size = leaf_size;\n        bvh_device_on_host.num_items = num_items;\n        bvh_device_on_host.max_nodes = 2 * num_items - 1;\n        bvh_device_on_host.num_leaf_nodes = num_items;\n        bvh_device_on_host.node_lowers = (BVHPackedNodeHalf*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes, \"(native:bvh)\"\n        );\n        wp_memset_device(\n            WP_CURRENT_CONTEXT, bvh_device_on_host.node_lowers, 0,\n            sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes\n        );\n        bvh_device_on_host.node_uppers = (BVHPackedNodeHalf*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes, \"(native:bvh)\"\n        );\n        wp_memset_device(\n            WP_CURRENT_CONTEXT, bvh_device_on_host.node_uppers, 0,\n            sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes\n        );\n        bvh_device_on_host.node_parents\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        bvh_device_on_host.node_counts\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        bvh_device_on_host.root = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int), \"(native:bvh)\");\n        bvh_device_on_host.primitive_indices\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * num_items, \"(native:bvh)\");\n        bvh_device_on_host.item_lowers = lowers;\n        bvh_device_on_host.item_uppers = uppers;\n        bvh_device_on_host.item_groups = groups;\n        bvh_device_on_host.context = context ? context : wp_cuda_context_get_current();\n\n        LinearBVHBuilderGPU builder;\n        builder.build(bvh_device_on_host, lowers, uppers, num_items, NULL, groups);\n    } else {\n        printf(\n            \"Unrecognized Constructor type: %d! For GPU constructor it should be SAH (0), Median (1), or LBVH (2)!\\n\",\n            constructor_type\n        );\n    }\n}\n\nvoid bvh_destroy_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_lowers);\n    bvh.node_lowers = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_uppers);\n    bvh.node_uppers = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_parents);\n    bvh.node_parents = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_counts);\n    bvh.node_counts = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.primitive_indices);\n    bvh.primitive_indices = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.root);\n    bvh.root = NULL;\n}\n\nvoid bvh_refit_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    // clear child counters\n    wp_memset_device(WP_CURRENT_CONTEXT, bvh.node_counts, 0, sizeof(int) * bvh.max_nodes);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bvh_refit_kernel, bvh.num_leaf_nodes,\n        (bvh.num_leaf_nodes, bvh.node_parents, bvh.node_counts, bvh.primitive_indices, bvh.node_lowers, bvh.node_uppers,\n         bvh.item_lowers, bvh.item_uppers)\n    );\n}\n\nvoid bvh_rebuild_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    LinearBVHBuilderGPU builder;\n    builder.build(bvh, bvh.item_lowers, bvh.item_uppers, bvh.num_items, NULL, bvh.item_groups);\n}\n\n#ifndef WP_DISABLE_CUBQL\n\n__global__ void cubql_make_boxes(const vec3* lowers, const vec3* uppers, cuBQL::box3f* boxes, int n)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    if (tid < n) {\n        const vec3 lower = lowers[tid];\n        const vec3 upper = uppers[tid];\n        boxes[tid]\n            = cuBQL::box3f(cuBQL::vec3f(lower[0], lower[1], lower[2]), cuBQL::vec3f(upper[0], upper[1], upper[2]));\n    }\n}\n\nstatic inline cuBQL::bvh3f cubql_native_view(const CuBQLBVH& bvh)\n{\n    cuBQL::bvh3f native;\n    native.nodes = reinterpret_cast<cuBQL::bvh3f::node_t*>(bvh.nodes);\n    native.numNodes = uint32_t(bvh.num_nodes);\n    native.primIDs = reinterpret_cast<uint32_t*>(bvh.primitive_indices);\n    native.numPrims = uint32_t(bvh.num_prims);\n    return native;\n}\n\nstatic inline void cubql_assign(CuBQLBVH& bvh, const cuBQL::bvh3f& native)\n{\n    bvh.nodes = reinterpret_cast<CuBQLNode*>(native.nodes);\n    bvh.num_nodes = int(native.numNodes);\n    bvh.primitive_indices = native.primIDs;\n    bvh.num_prims = int(native.numPrims);\n    if (bvh.root) {\n        int root_index = native.numNodes > 0 ? 0 : -1;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh.root, &root_index, sizeof(int));\n    }\n}\n\nstatic inline void cubql_update_device_boxes(CuBQLBVH& bvh)\n{\n    if (!bvh.boxes || bvh.num_items <= 0) {\n        return;\n    }\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, cubql_make_boxes, bvh.num_items,\n        (bvh.item_lowers, bvh.item_uppers, reinterpret_cast<cuBQL::box3f*>(bvh.boxes), bvh.num_items)\n    );\n}\n\nvoid cubql_bvh_create_device(\n    void* context, vec3* lowers, vec3* uppers, int num_items, int leaf_size, CuBQLBVH& bvh_device_on_host\n)\n{\n    ContextGuard guard(context);\n    memset(&bvh_device_on_host, 0, sizeof(CuBQLBVH));\n\n    bvh_device_on_host.context = context ? context : wp_cuda_context_get_current();\n    bvh_device_on_host.item_lowers = lowers;\n    bvh_device_on_host.item_uppers = uppers;\n    bvh_device_on_host.num_items = num_items;\n    bvh_device_on_host.leaf_size = leaf_size;\n    bvh_device_on_host.root = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int));\n\n    int root_index = -1;\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_on_host.root, &root_index, sizeof(int));\n\n    if (num_items <= 0) {\n        return;\n    }\n\n    bvh_device_on_host.boxes = wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(cuBQL::box3f) * num_items);\n    cubql_update_device_boxes(bvh_device_on_host);\n\n    cuBQL::bvh3f native;\n    cuBQL::BuildConfig build_config;\n    build_config.enableSAH();\n    build_config.makeLeafThreshold = leaf_size;\n    cuBQL::gpuBuilder(\n        native, reinterpret_cast<cuBQL::box3f*>(bvh_device_on_host.boxes), uint32_t(num_items), build_config\n    );\n    cubql_assign(bvh_device_on_host, native);\n}\n\nvoid cubql_bvh_destroy_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    if (bvh.nodes || bvh.primitive_indices) {\n        cuBQL::bvh3f native = cubql_native_view(bvh);\n        cuBQL::cuda::free(native);\n    }\n\n    if (bvh.boxes) {\n        wp_free_device(WP_CURRENT_CONTEXT, bvh.boxes);\n        bvh.boxes = nullptr;\n    }\n    if (bvh.root) {\n        wp_free_device(WP_CURRENT_CONTEXT, bvh.root);\n        bvh.root = nullptr;\n    }\n    bvh.nodes = nullptr;\n    bvh.num_nodes = 0;\n    bvh.primitive_indices = nullptr;\n    bvh.num_prims = 0;\n    bvh.leaf_size = 0;\n}\n\nvoid cubql_bvh_refit_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n    if (!bvh.nodes || !bvh.boxes || bvh.num_items <= 0) {\n        return;\n    }\n\n    cubql_update_device_boxes(bvh);\n    cuBQL::bvh3f native = cubql_native_view(bvh);\n    cuBQL::cuda::refit(native, reinterpret_cast<cuBQL::box3f*>(bvh.boxes));\n}\n\nvoid cubql_bvh_rebuild_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    if (bvh.nodes || bvh.primitive_indices) {\n        cuBQL::bvh3f old_native = cubql_native_view(bvh);\n        cuBQL::cuda::free(old_native);\n    }\n\n    if (bvh.num_items <= 0) {\n        bvh.nodes = nullptr;\n        bvh.primitive_indices = nullptr;\n        bvh.num_nodes = 0;\n        bvh.num_prims = 0;\n        if (bvh.root) {\n            int root_index = -1;\n            wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh.root, &root_index, sizeof(int));\n        }\n        return;\n    }\n\n    if (!bvh.boxes) {\n        bvh.boxes = wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(cuBQL::box3f) * bvh.num_items);\n    }\n    cubql_update_device_boxes(bvh);\n\n    cuBQL::bvh3f native;\n    cuBQL::BuildConfig build_config;\n    build_config.enableSAH();\n    build_config.makeLeafThreshold = bvh.leaf_size;\n    cuBQL::gpuBuilder(native, reinterpret_cast<cuBQL::box3f*>(bvh.boxes), uint32_t(bvh.num_items), build_config);\n    cubql_assign(bvh, native);\n}\n\n#else  // WP_DISABLE_CUBQL\n\nvoid cubql_bvh_create_device(\n    void* context, vec3* lowers, vec3* uppers, int num_items, int leaf_size, CuBQLBVH& bvh_device_on_host\n)\n{\n    fprintf(stderr, \"Warp error: cuBQL support disabled (WP_DISABLE_CUBQL)\\n\");\n    memset(&bvh_device_on_host, 0, sizeof(CuBQLBVH));\n}\n\nvoid cubql_bvh_destroy_device(CuBQLBVH&) { }\nvoid cubql_bvh_refit_device(CuBQLBVH&) { }\nvoid cubql_bvh_rebuild_device(CuBQLBVH&) { }\n\n#endif  // WP_DISABLE_CUBQL\n\n\n}  // namespace wp\n\n\nvoid wp_bvh_refit_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n\n        wp::bvh_refit_device(bvh);\n    }\n}\n\nvoid wp_bvh_rebuild_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n\n        wp::bvh_rebuild_device(bvh);\n    }\n}\n\n/*\n * Since we don't even know the number of true leaf nodes, never mention where they are, we will launch\n * the num_items threads, which are identical to the number of leaf nodes in the original tree. The\n * refitting threads will start from the nodes corresponding to the original leaf nodes, which might be\n * muted. However, the muted leaf nodes will still have the pointer to their parents, thus the up-tracing\n * can still work. We will only compute the bounding box of a leaf node if its parent is not a leaf node.\n */\nuint64_t wp_bvh_create_device(\n    void* context, wp::vec3* lowers, wp::vec3* uppers, int num_items, int constructor_type, int* groups, int leaf_size\n)\n{\n    ContextGuard guard(context);\n    wp::BVH bvh_device_on_host;\n    wp::BVH* bvh_device_ptr = nullptr;\n\n    wp::bvh_create_device(\n        WP_CURRENT_CONTEXT, lowers, uppers, num_items, constructor_type, groups, leaf_size, bvh_device_on_host\n    );\n\n    // create device-side BVH descriptor\n    bvh_device_ptr = (wp::BVH*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::BVH), \"(native:bvh)\");\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_ptr, &bvh_device_on_host, sizeof(wp::BVH));\n\n    uint64_t bvh_id = (uint64_t)bvh_device_ptr;\n    wp::bvh_add_descriptor(bvh_id, bvh_device_on_host);\n    return bvh_id;\n}\n\nuint64_t wp_cubql_bvh_create_device(void* context, wp::vec3* lowers, wp::vec3* uppers, int num_items, int leaf_size)\n{\n    ContextGuard guard(context);\n    wp::CuBQLBVH bvh_device_on_host;\n    memset(&bvh_device_on_host, 0, sizeof(wp::CuBQLBVH));\n\n    wp::cubql_bvh_create_device(WP_CURRENT_CONTEXT, lowers, uppers, num_items, leaf_size, bvh_device_on_host);\n\n    if (!bvh_device_on_host.nodes && num_items > 0) {\n        return 0;\n    }\n\n    wp::CuBQLBVH* bvh_device_ptr = (wp::CuBQLBVH*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::CuBQLBVH));\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_ptr, &bvh_device_on_host, sizeof(wp::CuBQLBVH));\n\n    uint64_t bvh_id = (uint64_t)bvh_device_ptr;\n    wp::cubql_bvh_add_descriptor(bvh_id, bvh_device_on_host);\n    return bvh_id;\n}\n\n\nvoid wp_bvh_destroy_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (wp::bvh_get_descriptor(id, bvh)) {\n        wp::bvh_destroy_device(bvh);\n        wp::bvh_rem_descriptor(id);\n\n        // free descriptor\n        wp_free_device(WP_CURRENT_CONTEXT, (void*)id);\n    }\n}\n\nvoid wp_cubql_bvh_refit_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n        wp::cubql_bvh_refit_device(bvh);\n        wp::cubql_bvh_add_descriptor(id, bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, (void*)id, &bvh, sizeof(wp::CuBQLBVH));\n    }\n}\n\nvoid wp_cubql_bvh_rebuild_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n        wp::cubql_bvh_rebuild_device(bvh);\n        wp::cubql_bvh_add_descriptor(id, bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, (void*)id, &bvh, sizeof(wp::CuBQLBVH));\n    }\n}\n\nvoid wp_cubql_bvh_destroy_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        wp::cubql_bvh_destroy_device(bvh);\n        wp::cubql_bvh_rem_descriptor(id);\n        wp_free_device(WP_CURRENT_CONTEXT, (void*)id);\n    }\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#define CUBQL_GPU_BUILDER_IMPLEMENTATION 1\n\n#include \"warp.h\"\n\n#include \"bvh.h\"\n#include \"cuda_util.h\"\n#include \"sort.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\n#define THRUST_IGNORE_CUB_VERSION_CHECK\n#define REORDER_HOST_TREE\n\n// CUB must be included before cuBQL. cuBQL's math/common.h includes <stdexcept>,\n// which causes CCCL's _CCCL_HAS_EXCEPTIONS() to be true when typeid.h is later\n// pulled in by CUB. This makes __throw_out_of_range non-constexpr, breaking a\n// static_assert in typeid.h on GCC < 12 (which lacks P2448R2 relaxed constexpr).\n#include <hipcub/hipcub.hpp>\n\n#ifndef WP_DISABLE_CUBQL\n#include \"cuBQL/bvh.h\"\n#endif\n\nextern hipCtx_t get_current_context();\n\nnamespace wp {\nvoid bvh_create_host(vec3* lowers, vec3* uppers, int num_items, int constructor_type, BVH& bvh, int leaf_size);\nvoid bvh_destroy_host(BVH& bvh);\n\n__global__ void memset_kernel(int* dest, int value, size_t n)\n{\n    const size_t tid\n        = static_cast<size_t>(blockDim.x) * static_cast<size_t>(blockIdx.x) + static_cast<size_t>(threadIdx.x);\n\n    if (tid < n) {\n        dest[tid] = value;\n    }\n}\n\n// for LBVH: this will start with some muted leaf nodes, but that is okay, we can still trace up because there parents\n// information is still valid the only thing worth mentioning is that when the parent leaf node is also a leaf node, we\n// need to recompute its bounds, since their child information are lost for a compact tree such as those from SAH or\n// Median constructor, there is no muted leaf nodes\n__global__ void bvh_refit_kernel(\n    int n,\n    const int* __restrict__ parents,\n    int* __restrict__ child_count,\n    const int* __restrict__ primitive_indices,\n    BVHPackedNodeHalf* __restrict__ node_lowers,\n    BVHPackedNodeHalf* __restrict__ node_uppers,\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        bool leaf = node_lowers[index].b;\n        int parent = parents[index];\n\n        if (leaf) {\n            BVHPackedNodeHalf& lower = node_lowers[index];\n            BVHPackedNodeHalf& upper = node_uppers[index];\n            // update the leaf node\n\n            // only need to compute bound when this is a valid leaf node\n            if (!node_lowers[parent].b) {\n                const int start = lower.i;\n                const int end = upper.i;\n\n                bounds3 bound;\n                for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                    const int primitive = primitive_indices[primitive_counter];\n                    bound.add_bounds(item_lowers[primitive], item_uppers[primitive]);\n                }\n                (vec3&)lower = bound.lower;\n                (vec3&)upper = bound.upper;\n            }\n        } else {\n            // only keep leaf threads\n            return;\n        }\n\n        // update hierarchy\n        for (;;) {\n            parent = parents[index];\n            // reached root\n            if (parent == -1)\n                return;\n\n            // ensure all writes are visible\n            __threadfence();\n\n            int finished = atomicAdd(&child_count[parent], 1);\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (finished == 1) {\n                BVHPackedNodeHalf& parent_lower = node_lowers[parent];\n                BVHPackedNodeHalf& parent_upper = node_uppers[parent];\n                if (parent_lower.b)\n                // a packed leaf node can still be a parent in LBVH, we need to recompute its bounds\n                // since we've lost its left and right child node index in the muting process\n                {\n                    // update the leaf node\n                    int parent_parent = parents[parent];\n                    ;\n\n                    // only need to compute bound when this is a valid leaf node\n                    if (!node_lowers[parent_parent].b) {\n                        const int start = parent_lower.i;\n                        const int end = parent_upper.i;\n                        bounds3 bound;\n                        for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                            const int primitive = primitive_indices[primitive_counter];\n                            bound.add_bounds(item_lowers[primitive], item_uppers[primitive]);\n                        }\n\n                        (vec3&)parent_lower = bound.lower;\n                        (vec3&)parent_upper = bound.upper;\n                    }\n                } else {\n                    const int left_child = parent_lower.i;\n                    const int right_child = parent_upper.i;\n\n                    vec3 left_lower = (vec3&)(node_lowers[left_child]);\n                    vec3 left_upper = (vec3&)(node_uppers[left_child]);\n                    vec3 right_lower = (vec3&)(node_lowers[right_child]);\n                    vec3 right_upper = (vec3&)(node_uppers[right_child]);\n\n                    // union of child bounds\n                    vec3 lower = min(left_lower, right_lower);\n                    vec3 upper = max(left_upper, right_upper);\n\n                    // write new BVH nodes\n                    (vec3&)parent_lower = lower;\n                    (vec3&)parent_upper = upper;\n                }\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////\n\n// Create a linear BVH as described in Fast and Simple Agglomerative LBVH construction\n// this is a bottom-up clustering method that outputs one node per-leaf\n//\nclass LinearBVHBuilderGPU {\npublic:\n    LinearBVHBuilderGPU();\n    ~LinearBVHBuilderGPU();\n\n    // takes a bvh (host ref), and pointers to the GPU lower and upper bounds for each triangle\n    void build(\n        BVH& bvh,\n        const vec3* item_lowers,\n        const vec3* item_uppers,\n        int num_items,\n        bounds3* total_bounds,\n        int* item_groups\n    );\n\nprivate:\n    // temporary data used during building\n    int* indices;\n    uint64_t* keys;\n    int* deltas;\n    int* range_lefts;\n    int* range_rights;\n    int* num_children;\n\n    // bounds data when total item bounds built on GPU\n    vec3* total_lower;\n    vec3* total_upper;\n    vec3* total_inv_edges;\n};\n\n////////////////////////////////////////////////////////\n\n\n__global__ void compute_morton_codes(\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers,\n    int n,\n    const vec3* grid_lower,\n    const vec3* grid_inv_edges,\n    int* __restrict__ indices,\n    uint64_t* __restrict__ keys,\n    const int* __restrict__ item_groups\n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        vec3 lower = item_lowers[index];\n        vec3 upper = item_uppers[index];\n\n        vec3 center = 0.5f * (lower + upper);\n\n        vec3 local = cw_mul((center - grid_lower[0]), grid_inv_edges[0]);\n\n        // 10-bit Morton codes stored in lower 30bits (1024^3 effective resolution)\n        // Group stored in upper 32 bits\n        uint64_t morton_code = static_cast<uint64_t>(morton3<1024>(local[0], local[1], local[2]));\n        const uint64_t group = item_groups ? static_cast<uint32_t>(item_groups[index]) : 0u;\n        const uint64_t key = (group << 32) | morton_code;\n\n        indices[index] = index;\n        keys[index] = key;\n    }\n}\n\n// compute a distance metric between adjacent keys; larger across groups\n// Using raw XOR magnitude preserves the original builder's assumptions\n__global__ void compute_key_deltas(const uint64_t* __restrict__ keys, int* __restrict__ deltas, int n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const uint64_t diff = keys[index] ^ keys[index + 1];\n        deltas[index] = (diff == 0) ? 64 : __clzll(diff);\n    }\n}\n\n__global__ void build_leaves(\n    const vec3* __restrict__ item_lowers,\n    const vec3* __restrict__ item_uppers,\n    int n,\n    const int* __restrict__ indices,\n    int* __restrict__ range_lefts,\n    int* __restrict__ range_rights,\n    BVHPackedNodeHalf* __restrict__ lowers,\n    BVHPackedNodeHalf* __restrict__ uppers\n)\n{\n    const int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const int item = indices[index];\n\n        vec3 lower = item_lowers[item];\n        vec3 upper = item_uppers[item];\n\n        // write leaf nodes using position indices and [start, end) range\n        lowers[index] = make_node(lower, index, true);\n        uppers[index] = make_node(upper, index, false);\n\n        // write leaf key ranges\n        range_lefts[index] = index;\n        range_rights[index] = index;\n    }\n}\n\n// this bottom-up process assigns left and right children and combines bounds to form internal nodes\n// there is one thread launched per-leaf node, each thread calculates it's parent node and assigns\n// itself to either the left or right parent slot, the last child to complete the parent and moves\n// up the hierarchy\n__global__ void build_hierarchy(\n    int n,\n    int* root,\n    const int* __restrict__ deltas,\n    const uint64_t* __restrict__ keys,\n    int* __restrict__ num_children,\n    const int* __restrict__ primitive_indices,\n    volatile int* __restrict__ range_lefts,\n    volatile int* __restrict__ range_rights,\n    volatile int* __restrict__ parents,\n    volatile BVHPackedNodeHalf* __restrict__ lowers,\n    volatile BVHPackedNodeHalf* __restrict__ uppers\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        const int internal_offset = n;\n\n        for (;;) {\n            int left = range_lefts[index];\n            int right = range_rights[index];\n\n            // check if we are the root node, if so then store out our index and terminate\n            if (left == 0 && right == n - 1) {\n                *root = index;\n                parents[index] = -1;\n\n                break;\n            }\n\n            int childCount = 0;\n\n            int parent;\n\n            // group away selection of parent. We need to make sure that:\n            // 1) If node spans a single group and exactly one neighbor keeps us in-group, choose that side\n            // 2) Otherwise, use the original delta rule with parity tie-break to avoid ladders\n            // This is important to make sure that our tree does not mix groups until a single group root is formed.\n            bool parent_right = false;\n            if (left == 0) {\n                parent_right = true;\n            } else {\n                bool decided = false;\n                const uint32_t group_left = (uint32_t)(keys[left] >> 32);\n                const uint32_t group_right = (uint32_t)(keys[right] >> 32);\n\n                // Check if either of the neighbors keep us in the same group\n                if (group_left == group_right) {\n                    const uint32_t group = group_left;\n                    const bool can_go_right_same = (right < n - 1) && ((uint32_t)(keys[right + 1] >> 32) == group);\n                    const bool can_go_left_same = ((uint32_t)(keys[left - 1] >> 32) == group);\n\n                    // If exactly one neighbor keeps us in the same group, choose that side\n                    if (can_go_right_same ^ can_go_left_same) {\n                        parent_right = can_go_right_same;\n                        decided = true;\n                    }\n\n                    // If both or neither keep us in the same group, fall through to delta rule\n                }\n\n                if (!decided) {\n                    // prefer side with greater common prefix\n                    if (right != n - 1 && deltas[right] >= deltas[left - 1]) {\n                        if (deltas[right] == deltas[left - 1])\n                            parent_right = (primitive_indices[left - 1] % 2) ^ (primitive_indices[right] % 2);\n                        else\n                            parent_right = true;\n                    } else {\n                        parent_right = false;\n                    }\n                }\n            }\n\n            if (parent_right) {\n                parent = right + internal_offset;\n\n                // set parent left child\n                parents[index] = parent;\n                lowers[parent].i = index;\n                range_lefts[parent] = left;\n\n                // ensure above writes are visible to all threads\n                __threadfence();\n\n                childCount = atomicAdd(&num_children[parent], 1);\n            } else {\n                parent = left + internal_offset - 1;\n\n                // set parent right child\n                parents[index] = parent;\n                uppers[parent].i = index;\n                range_rights[parent] = right;\n\n                // ensure above writes are visible to all threads\n                __threadfence();\n\n                childCount = atomicAdd(&num_children[parent], 1);\n            }\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (childCount == 1) {\n                const int left_child = lowers[parent].i;\n                const int right_child = uppers[parent].i;\n\n                vec3 left_lower = vec3(lowers[left_child].x, lowers[left_child].y, lowers[left_child].z);\n\n                vec3 left_upper = vec3(uppers[left_child].x, uppers[left_child].y, uppers[left_child].z);\n\n                vec3 right_lower = vec3(lowers[right_child].x, lowers[right_child].y, lowers[right_child].z);\n\n\n                vec3 right_upper = vec3(uppers[right_child].x, uppers[right_child].y, uppers[right_child].z);\n\n                // bounds_union of child bounds; ensure min on lowers and max on uppers\n                vec3 lower = min(left_lower, right_lower);\n                vec3 upper = max(left_upper, right_upper);\n\n                // write new BVH nodes\n                make_node(lowers + parent, lower, left_child, false);\n                make_node(uppers + parent, upper, right_child, false);\n\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n/*\n * LBVH uses a bottom-up constructor which makes variable-sized leaf nodes more challenging to achieve.\n * Simply splitting the ordered primitives into uniform groups of size leaf_size will result in poor\n * quality. Instead, after the hierarchy is built, we convert any intermediate node whose size is\n * <= leaf_size into a new leaf node. This process is done using the new kernel function called\n * mark_packed_leaf_nodes .\n */\n__global__ void mark_packed_leaf_nodes(\n    int n,\n    const int* __restrict__ range_lefts,\n    const int* __restrict__ range_rights,\n    const int* __restrict__ parents,\n    const uint64_t* __restrict__ keys,\n    BVHPackedNodeHalf* __restrict__ lowers,\n    BVHPackedNodeHalf* __restrict__ uppers,\n    const int leaf_size\n)\n{\n    int node_index = blockDim.x * blockIdx.x + threadIdx.x;\n    if (node_index < n) {\n        // mark the node as leaf if its range is less than leaf_size or it is deeper than BVH_QUERY_STACK_SIZE\n        // this will forever mute its child nodes so that they will never be accessed\n\n        // calculate depth\n        int depth = 1;\n        int parent = parents[node_index];\n        while (parent != -1) {\n            parent = parents[parent];\n            depth++;\n        }\n\n        int left = range_lefts[node_index];\n        // the LBVH constructor's range is defined as left <= i <= right\n        // we need to convert it to our convention: left <= i < right\n        int right = range_rights[node_index] + 1;\n\n        // avoid creating packed leaves that straddle group boundaries\n        bool single_group = true;\n        const uint64_t group_left = keys[left] >> 32;\n        const uint64_t group_right = keys[right - 1] >> 32;\n        single_group = (group_left == group_right);\n\n        if (single_group && (right - left <= leaf_size || depth >= BVH_QUERY_STACK_SIZE)) {\n            lowers[node_index].b = 1;\n            lowers[node_index].i = left;\n            uppers[node_index].i = right;\n        }\n    }\n}\n\n\nCUDA_CALLABLE inline vec3 Vec3Max(const vec3& a, const vec3& b) { return wp::max(a, b); }\nCUDA_CALLABLE inline vec3 Vec3Min(const vec3& a, const vec3& b) { return wp::min(a, b); }\n\n__global__ void compute_total_bounds(\n    const vec3* item_lowers, const vec3* item_uppers, vec3* total_lower, vec3* total_upper, int num_items\n)\n{\n    typedef hipcub::BlockReduce<vec3, 256> BlockReduce;\n\n    __shared__ typename BlockReduce::TempStorage temp_storage;\n\n    const int blockStart = blockDim.x * blockIdx.x;\n    const int numValid = ::min(num_items - blockStart, blockDim.x);\n\n    const int tid = blockStart + threadIdx.x;\n\n    if (tid < num_items) {\n        vec3 lower = item_lowers[tid];\n        vec3 upper = item_uppers[tid];\n\n        vec3 block_upper = BlockReduce(temp_storage).Reduce(upper, Vec3Max, numValid);\n\n        // sync threads because second reduce uses same temp storage as first\n        __syncthreads();\n\n        vec3 block_lower = BlockReduce(temp_storage).Reduce(lower, Vec3Min, numValid);\n\n        if (threadIdx.x == 0) {\n            // write out block results, expanded by the radius\n            atomic_max(total_upper, block_upper);\n            atomic_min(total_lower, block_lower);\n        }\n    }\n}\n\n// compute inverse edge length, this is just done on the GPU to avoid a CPU->GPU sync point\n__global__ void compute_total_inv_edges(const vec3* total_lower, const vec3* total_upper, vec3* total_inv_edges)\n{\n    vec3 edges = (total_upper[0] - total_lower[0]);\n    edges += vec3(0.0001f);\n\n    total_inv_edges[0] = vec3(1.0f / edges[0], 1.0f / edges[1], 1.0f / edges[2]);\n}\n\n\nLinearBVHBuilderGPU::LinearBVHBuilderGPU()\n    : indices(NULL)\n    , keys(NULL)\n    , deltas(NULL)\n    , range_lefts(NULL)\n    , range_rights(NULL)\n    , num_children(NULL)\n    , total_lower(NULL)\n    , total_upper(NULL)\n    , total_inv_edges(NULL)\n{\n    total_lower = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n    total_upper = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n    total_inv_edges = (vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(vec3), \"(native:bvh)\");\n}\n\nLinearBVHBuilderGPU::~LinearBVHBuilderGPU()\n{\n    wp_free_device(WP_CURRENT_CONTEXT, total_lower);\n    wp_free_device(WP_CURRENT_CONTEXT, total_upper);\n    wp_free_device(WP_CURRENT_CONTEXT, total_inv_edges);\n}\n\n\nvoid LinearBVHBuilderGPU::build(\n    BVH& bvh, const vec3* item_lowers, const vec3* item_uppers, int num_items, bounds3* total_bounds, int* item_groups\n)\n{\n    // allocate temporary memory used during  building\n    indices\n        = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * num_items * 2, \"(native:bvh)\");  // *2 for radix sort\n    keys = (uint64_t*)wp_alloc_device(\n        WP_CURRENT_CONTEXT, sizeof(uint64_t) * num_items * 2, \"(native:bvh)\"\n    );  // *2 for radix sort\n    deltas = (int*)wp_alloc_device(\n        WP_CURRENT_CONTEXT, sizeof(int) * num_items, \"(native:bvh)\"\n    );  // highest differentiating bit between keys for item i and i+1\n    range_lefts = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n    range_rights = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n    num_children = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh.max_nodes, \"(native:bvh)\");\n\n    // if total bounds supplied by the host then we just\n    // compute our edge length and upload it to the GPU directly\n    if (total_bounds) {\n        // calculate Morton codes\n        vec3 edges = (*total_bounds).edges();\n        edges += vec3(0.0001f);\n\n        vec3 inv_edges = vec3(1.0f / edges[0], 1.0f / edges[1], 1.0f / edges[2]);\n\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_lower, &total_bounds->lower[0], sizeof(vec3));\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_upper, &total_bounds->upper[0], sizeof(vec3));\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, total_inv_edges, &inv_edges[0], sizeof(vec3));\n    } else {\n        // IEEE-754 bit patterns for \u00b1 FLT_MAX\n        constexpr int FLT_MAX_BITS = 0x7f7fffff;\n        constexpr int NEG_FLT_MAX_BITS = 0xff7fffff;\n\n        // total_lower := ( +FLT_MAX, +FLT_MAX, +FLT_MAX )\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, memset_kernel, sizeof(vec3) / 4, ((int*)total_lower, FLT_MAX_BITS, sizeof(vec3) / 4)\n        );\n\n        // total_upper := ( -FLT_MAX, -FLT_MAX, -FLT_MAX )\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, memset_kernel, sizeof(vec3) / 4, ((int*)total_upper, NEG_FLT_MAX_BITS, sizeof(vec3) / 4)\n        );\n\n        // compute the total bounds on the GPU\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, compute_total_bounds, num_items,\n            (item_lowers, item_uppers, total_lower, total_upper, num_items)\n        );\n\n        // compute the total edge length\n        wp_launch_device(WP_CURRENT_CONTEXT, compute_total_inv_edges, 1, (total_lower, total_upper, total_inv_edges));\n    }\n\n    // assign 30-bit Morton code based on the centroid of each triangle and bounds for each leaf\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, compute_morton_codes, num_items,\n        (item_lowers, item_uppers, num_items, total_lower, total_inv_edges, indices, keys, item_groups)\n    );\n\n    // sort items based on Morton key (note the 64-bit sort key includes group in upper 32 bits and morton code in lower\n    // 32 bits)\n    radix_sort_pairs_device(WP_CURRENT_CONTEXT, keys, indices, num_items);\n    wp_memcpy_d2d(WP_CURRENT_CONTEXT, bvh.primitive_indices, indices, sizeof(int) * num_items);\n\n    // calculate deltas between adjacent keys\n    wp_launch_device(WP_CURRENT_CONTEXT, compute_key_deltas, num_items, (keys, deltas, num_items - 1));\n\n    // initialize leaf nodes\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, build_leaves, num_items,\n        (item_lowers, item_uppers, num_items, indices, range_lefts, range_rights, bvh.node_lowers, bvh.node_uppers)\n    );\n\n    // reset children count, this is our atomic counter so we know when an internal node is complete, only used during\n    // building\n    wp_memset_device(WP_CURRENT_CONTEXT, num_children, 0, sizeof(int) * bvh.max_nodes);\n\n    // build the tree and internal node bounds\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, build_hierarchy, num_items,\n        (num_items, bvh.root, deltas, keys, num_children, bvh.primitive_indices, range_lefts, range_rights,\n         bvh.node_parents, bvh.node_lowers, bvh.node_uppers)\n    );\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, mark_packed_leaf_nodes, bvh.max_nodes,\n        (bvh.max_nodes, range_lefts, range_rights, bvh.node_parents, keys, bvh.node_lowers, bvh.node_uppers,\n         bvh.leaf_size)\n    );\n\n    // free temporary memory\n    wp_free_device(WP_CURRENT_CONTEXT, indices);\n    wp_free_device(WP_CURRENT_CONTEXT, keys);\n    wp_free_device(WP_CURRENT_CONTEXT, deltas);\n\n    wp_free_device(WP_CURRENT_CONTEXT, range_lefts);\n    wp_free_device(WP_CURRENT_CONTEXT, range_rights);\n    wp_free_device(WP_CURRENT_CONTEXT, num_children);\n}\n\n// buffer_size is the number of T, not the number of bytes\ntemplate <typename T> T* make_device_buffer_of(void* context, T* host_buffer, size_t buffer_size)\n{\n    T* device_buffer = (T*)wp_alloc_device(context, sizeof(T) * buffer_size, \"(native:bvh)\");\n    ;\n    wp_memcpy_h2d(context, device_buffer, host_buffer, sizeof(T) * buffer_size);\n\n    return device_buffer;\n}\n\nvoid copy_host_tree_to_device(void* context, BVH& bvh_host, BVH& bvh_device_on_host)\n{\n    if (!bvh_host.item_groups)\n    // if it's grouped bvh it's already reordered\n    {\n        reorder_top_down_bvh(bvh_host);\n    }\n\n    bvh_device_on_host.num_nodes = bvh_host.num_nodes;\n    bvh_device_on_host.num_leaf_nodes = bvh_host.num_leaf_nodes;\n    bvh_device_on_host.max_nodes = bvh_host.max_nodes;\n    bvh_device_on_host.num_items = bvh_host.num_items;\n    bvh_device_on_host.max_depth = bvh_host.max_depth;\n    bvh_device_on_host.leaf_size = bvh_host.leaf_size;\n\n    bvh_device_on_host.root = (int*)wp_alloc_device(context, sizeof(int), \"(native:bvh)\");\n    wp_memcpy_h2d(context, bvh_device_on_host.root, bvh_host.root, sizeof(int));\n    bvh_device_on_host.context = context;\n\n    bvh_device_on_host.node_lowers = make_device_buffer_of(context, bvh_host.node_lowers, bvh_host.max_nodes);\n    bvh_device_on_host.node_uppers = make_device_buffer_of(context, bvh_host.node_uppers, bvh_host.max_nodes);\n    bvh_device_on_host.node_parents = make_device_buffer_of(context, bvh_host.node_parents, bvh_host.max_nodes);\n    bvh_device_on_host.primitive_indices\n        = make_device_buffer_of(context, bvh_host.primitive_indices, bvh_host.num_items);\n}\n\n// create in-place given existing descriptor\nvoid bvh_create_device(\n    void* context,\n    vec3* lowers,\n    vec3* uppers,\n    int num_items,\n    int constructor_type,\n    int* groups,\n    int leaf_size,\n    BVH& bvh_device_on_host\n)\n{\n    ContextGuard guard(context);\n    if (constructor_type == BVH_CONSTRUCTOR_SAH || constructor_type == BVH_CONSTRUCTOR_MEDIAN)\n    // CPU based constructors\n    {\n        // copy bounds back to CPU\n        std::vector<vec3> lowers_host(num_items);\n        std::vector<vec3> uppers_host(num_items);\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, lowers_host.data(), lowers, sizeof(vec3) * num_items);\n        wp_memcpy_d2h(WP_CURRENT_CONTEXT, uppers_host.data(), uppers, sizeof(vec3) * num_items);\n\n        // copy groups back to CPU (if exists)\n        std::vector<int> groups_host(num_items);\n        int* groups_host_ptr = nullptr;\n        if (groups) {\n            wp_memcpy_d2h(WP_CURRENT_CONTEXT, groups_host.data(), groups, sizeof(int) * num_items);\n            groups_host_ptr = groups_host.data();\n        }\n\n        // run CPU based constructor\n        wp::BVH bvh_host;\n        wp::bvh_create_host(\n            lowers_host.data(), uppers_host.data(), num_items, constructor_type, groups_host_ptr, leaf_size, bvh_host\n        );\n\n        // copy host tree to device\n        wp::copy_host_tree_to_device(WP_CURRENT_CONTEXT, bvh_host, bvh_device_on_host);\n        // replace host bounds with device bounds\n        bvh_device_on_host.item_lowers = lowers;\n        bvh_device_on_host.item_uppers = uppers;\n        bvh_device_on_host.item_groups = groups;\n        // node_counts is not allocated for host tree\n        bvh_device_on_host.node_counts\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        wp::bvh_destroy_host(bvh_host);\n    } else if (constructor_type == BVH_CONSTRUCTOR_LBVH) {\n        bvh_device_on_host.leaf_size = leaf_size;\n        bvh_device_on_host.num_items = num_items;\n        bvh_device_on_host.max_nodes = 2 * num_items - 1;\n        bvh_device_on_host.num_leaf_nodes = num_items;\n        bvh_device_on_host.node_lowers = (BVHPackedNodeHalf*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes, \"(native:bvh)\"\n        );\n        wp_memset_device(\n            WP_CURRENT_CONTEXT, bvh_device_on_host.node_lowers, 0,\n            sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes\n        );\n        bvh_device_on_host.node_uppers = (BVHPackedNodeHalf*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes, \"(native:bvh)\"\n        );\n        wp_memset_device(\n            WP_CURRENT_CONTEXT, bvh_device_on_host.node_uppers, 0,\n            sizeof(BVHPackedNodeHalf) * bvh_device_on_host.max_nodes\n        );\n        bvh_device_on_host.node_parents\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        bvh_device_on_host.node_counts\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * bvh_device_on_host.max_nodes, \"(native:bvh)\");\n        bvh_device_on_host.root = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int), \"(native:bvh)\");\n        bvh_device_on_host.primitive_indices\n            = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int) * num_items, \"(native:bvh)\");\n        bvh_device_on_host.item_lowers = lowers;\n        bvh_device_on_host.item_uppers = uppers;\n        bvh_device_on_host.item_groups = groups;\n        bvh_device_on_host.context = context ? context : wp_cuda_context_get_current();\n\n        LinearBVHBuilderGPU builder;\n        builder.build(bvh_device_on_host, lowers, uppers, num_items, NULL, groups);\n    } else {\n        printf(\n            \"Unrecognized Constructor type: %d! For GPU constructor it should be SAH (0), Median (1), or LBVH (2)!\\n\",\n            constructor_type\n        );\n    }\n}\n\nvoid bvh_destroy_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_lowers);\n    bvh.node_lowers = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_uppers);\n    bvh.node_uppers = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_parents);\n    bvh.node_parents = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.node_counts);\n    bvh.node_counts = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.primitive_indices);\n    bvh.primitive_indices = NULL;\n    wp_free_device(WP_CURRENT_CONTEXT, bvh.root);\n    bvh.root = NULL;\n}\n\nvoid bvh_refit_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    // clear child counters\n    wp_memset_device(WP_CURRENT_CONTEXT, bvh.node_counts, 0, sizeof(int) * bvh.max_nodes);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bvh_refit_kernel, bvh.num_leaf_nodes,\n        (bvh.num_leaf_nodes, bvh.node_parents, bvh.node_counts, bvh.primitive_indices, bvh.node_lowers, bvh.node_uppers,\n         bvh.item_lowers, bvh.item_uppers)\n    );\n}\n\nvoid bvh_rebuild_device(BVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    LinearBVHBuilderGPU builder;\n    builder.build(bvh, bvh.item_lowers, bvh.item_uppers, bvh.num_items, NULL, bvh.item_groups);\n}\n\n#ifndef WP_DISABLE_CUBQL\n\n__global__ void cubql_make_boxes(const vec3* lowers, const vec3* uppers, cuBQL::box3f* boxes, int n)\n{\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    if (tid < n) {\n        const vec3 lower = lowers[tid];\n        const vec3 upper = uppers[tid];\n        boxes[tid]\n            = cuBQL::box3f(cuBQL::vec3f(lower[0], lower[1], lower[2]), cuBQL::vec3f(upper[0], upper[1], upper[2]));\n    }\n}\n\nstatic inline cuBQL::bvh3f cubql_native_view(const CuBQLBVH& bvh)\n{\n    cuBQL::bvh3f native;\n    native.nodes = reinterpret_cast<cuBQL::bvh3f::node_t*>(bvh.nodes);\n    native.numNodes = uint32_t(bvh.num_nodes);\n    native.primIDs = reinterpret_cast<uint32_t*>(bvh.primitive_indices);\n    native.numPrims = uint32_t(bvh.num_prims);\n    return native;\n}\n\nstatic inline void cubql_assign(CuBQLBVH& bvh, const cuBQL::bvh3f& native)\n{\n    bvh.nodes = reinterpret_cast<CuBQLNode*>(native.nodes);\n    bvh.num_nodes = int(native.numNodes);\n    bvh.primitive_indices = native.primIDs;\n    bvh.num_prims = int(native.numPrims);\n    if (bvh.root) {\n        int root_index = native.numNodes > 0 ? 0 : -1;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh.root, &root_index, sizeof(int));\n    }\n}\n\nstatic inline void cubql_update_device_boxes(CuBQLBVH& bvh)\n{\n    if (!bvh.boxes || bvh.num_items <= 0) {\n        return;\n    }\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, cubql_make_boxes, bvh.num_items,\n        (bvh.item_lowers, bvh.item_uppers, reinterpret_cast<cuBQL::box3f*>(bvh.boxes), bvh.num_items)\n    );\n}\n\nvoid cubql_bvh_create_device(\n    void* context, vec3* lowers, vec3* uppers, int num_items, int leaf_size, CuBQLBVH& bvh_device_on_host\n)\n{\n    ContextGuard guard(context);\n    memset(&bvh_device_on_host, 0, sizeof(CuBQLBVH));\n\n    bvh_device_on_host.context = context ? context : wp_cuda_context_get_current();\n    bvh_device_on_host.item_lowers = lowers;\n    bvh_device_on_host.item_uppers = uppers;\n    bvh_device_on_host.num_items = num_items;\n    bvh_device_on_host.leaf_size = leaf_size;\n    bvh_device_on_host.root = (int*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(int));\n\n    int root_index = -1;\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_on_host.root, &root_index, sizeof(int));\n\n    if (num_items <= 0) {\n        return;\n    }\n\n    bvh_device_on_host.boxes = wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(cuBQL::box3f) * num_items);\n    cubql_update_device_boxes(bvh_device_on_host);\n\n    cuBQL::bvh3f native;\n    cuBQL::BuildConfig build_config;\n    build_config.enableSAH();\n    build_config.makeLeafThreshold = leaf_size;\n    cuBQL::gpuBuilder(\n        native, reinterpret_cast<cuBQL::box3f*>(bvh_device_on_host.boxes), uint32_t(num_items), build_config\n    );\n    cubql_assign(bvh_device_on_host, native);\n}\n\nvoid cubql_bvh_destroy_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    if (bvh.nodes || bvh.primitive_indices) {\n        cuBQL::bvh3f native = cubql_native_view(bvh);\n        cuBQL::cuda::free(native);\n    }\n\n    if (bvh.boxes) {\n        wp_free_device(WP_CURRENT_CONTEXT, bvh.boxes);\n        bvh.boxes = nullptr;\n    }\n    if (bvh.root) {\n        wp_free_device(WP_CURRENT_CONTEXT, bvh.root);\n        bvh.root = nullptr;\n    }\n    bvh.nodes = nullptr;\n    bvh.num_nodes = 0;\n    bvh.primitive_indices = nullptr;\n    bvh.num_prims = 0;\n    bvh.leaf_size = 0;\n}\n\nvoid cubql_bvh_refit_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n    if (!bvh.nodes || !bvh.boxes || bvh.num_items <= 0) {\n        return;\n    }\n\n    cubql_update_device_boxes(bvh);\n    cuBQL::bvh3f native = cubql_native_view(bvh);\n    cuBQL::cuda::refit(native, reinterpret_cast<cuBQL::box3f*>(bvh.boxes));\n}\n\nvoid cubql_bvh_rebuild_device(CuBQLBVH& bvh)\n{\n    ContextGuard guard(bvh.context);\n\n    if (bvh.nodes || bvh.primitive_indices) {\n        cuBQL::bvh3f old_native = cubql_native_view(bvh);\n        cuBQL::cuda::free(old_native);\n    }\n\n    if (bvh.num_items <= 0) {\n        bvh.nodes = nullptr;\n        bvh.primitive_indices = nullptr;\n        bvh.num_nodes = 0;\n        bvh.num_prims = 0;\n        if (bvh.root) {\n            int root_index = -1;\n            wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh.root, &root_index, sizeof(int));\n        }\n        return;\n    }\n\n    if (!bvh.boxes) {\n        bvh.boxes = wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(cuBQL::box3f) * bvh.num_items);\n    }\n    cubql_update_device_boxes(bvh);\n\n    cuBQL::bvh3f native;\n    cuBQL::BuildConfig build_config;\n    build_config.enableSAH();\n    build_config.makeLeafThreshold = bvh.leaf_size;\n    cuBQL::gpuBuilder(native, reinterpret_cast<cuBQL::box3f*>(bvh.boxes), uint32_t(bvh.num_items), build_config);\n    cubql_assign(bvh, native);\n}\n\n#else  // WP_DISABLE_CUBQL\n\nvoid cubql_bvh_create_device(\n    void* context, vec3* lowers, vec3* uppers, int num_items, int leaf_size, CuBQLBVH& bvh_device_on_host\n)\n{\n    fprintf(stderr, \"Warp error: cuBQL support disabled (WP_DISABLE_CUBQL)\\n\");\n    memset(&bvh_device_on_host, 0, sizeof(CuBQLBVH));\n}\n\nvoid cubql_bvh_destroy_device(CuBQLBVH&) { }\nvoid cubql_bvh_refit_device(CuBQLBVH&) { }\nvoid cubql_bvh_rebuild_device(CuBQLBVH&) { }\n\n#endif  // WP_DISABLE_CUBQL\n\n\n}  // namespace wp\n\n\nvoid wp_bvh_refit_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n\n        wp::bvh_refit_device(bvh);\n    }\n}\n\nvoid wp_bvh_rebuild_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n\n        wp::bvh_rebuild_device(bvh);\n    }\n}\n\n/*\n * Since we don't even know the number of true leaf nodes, never mention where they are, we will launch\n * the num_items threads, which are identical to the number of leaf nodes in the original tree. The\n * refitting threads will start from the nodes corresponding to the original leaf nodes, which might be\n * muted. However, the muted leaf nodes will still have the pointer to their parents, thus the up-tracing\n * can still work. We will only compute the bounding box of a leaf node if its parent is not a leaf node.\n */\nuint64_t wp_bvh_create_device(\n    void* context, wp::vec3* lowers, wp::vec3* uppers, int num_items, int constructor_type, int* groups, int leaf_size\n)\n{\n    ContextGuard guard(context);\n    wp::BVH bvh_device_on_host;\n    wp::BVH* bvh_device_ptr = nullptr;\n\n    wp::bvh_create_device(\n        WP_CURRENT_CONTEXT, lowers, uppers, num_items, constructor_type, groups, leaf_size, bvh_device_on_host\n    );\n\n    // create device-side BVH descriptor\n    bvh_device_ptr = (wp::BVH*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::BVH), \"(native:bvh)\");\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_ptr, &bvh_device_on_host, sizeof(wp::BVH));\n\n    uint64_t bvh_id = (uint64_t)bvh_device_ptr;\n    wp::bvh_add_descriptor(bvh_id, bvh_device_on_host);\n    return bvh_id;\n}\n\nuint64_t wp_cubql_bvh_create_device(void* context, wp::vec3* lowers, wp::vec3* uppers, int num_items, int leaf_size)\n{\n    ContextGuard guard(context);\n    wp::CuBQLBVH bvh_device_on_host;\n    memset(&bvh_device_on_host, 0, sizeof(wp::CuBQLBVH));\n\n    wp::cubql_bvh_create_device(WP_CURRENT_CONTEXT, lowers, uppers, num_items, leaf_size, bvh_device_on_host);\n\n    if (!bvh_device_on_host.nodes && num_items > 0) {\n        return 0;\n    }\n\n    wp::CuBQLBVH* bvh_device_ptr = (wp::CuBQLBVH*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::CuBQLBVH));\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, bvh_device_ptr, &bvh_device_on_host, sizeof(wp::CuBQLBVH));\n\n    uint64_t bvh_id = (uint64_t)bvh_device_ptr;\n    wp::cubql_bvh_add_descriptor(bvh_id, bvh_device_on_host);\n    return bvh_id;\n}\n\n\nvoid wp_bvh_destroy_device(uint64_t id)\n{\n    wp::BVH bvh;\n    if (wp::bvh_get_descriptor(id, bvh)) {\n        wp::bvh_destroy_device(bvh);\n        wp::bvh_rem_descriptor(id);\n\n        // free descriptor\n        wp_free_device(WP_CURRENT_CONTEXT, (void*)id);\n    }\n}\n\nvoid wp_cubql_bvh_refit_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n        wp::cubql_bvh_refit_device(bvh);\n        wp::cubql_bvh_add_descriptor(id, bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, (void*)id, &bvh, sizeof(wp::CuBQLBVH));\n    }\n}\n\nvoid wp_cubql_bvh_rebuild_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        ContextGuard guard(bvh.context);\n        wp::cubql_bvh_rebuild_device(bvh);\n        wp::cubql_bvh_add_descriptor(id, bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, (void*)id, &bvh, sizeof(wp::CuBQLBVH));\n    }\n}\n\nvoid wp_cubql_bvh_destroy_device(uint64_t id)\n{\n    wp::CuBQLBVH bvh;\n    if (wp::cubql_bvh_get_descriptor(id, bvh)) {\n        wp::cubql_bvh_destroy_device(bvh);\n        wp::cubql_bvh_rem_descriptor(id);\n        wp_free_device(WP_CURRENT_CONTEXT, (void*)id);\n    }\n}\n", "source": "/kaggle/working/warp/warp/native/bvh.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"volume_impl.h\"\n\nextern CUcontext get_current_context();\n\n__global__ void\nvolume_get_leaf_coords(const uint32_t leaf_count, pnanovdb_coord_t* leaf_coords, const pnanovdb_buf_t buf)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < leaf_count) {\n        pnanovdb_leaf_handle_t leaf = wp::volume::get_leaf(buf, tid);\n        leaf_coords[tid] = wp::volume::leaf_origin(buf, leaf);\n    }\n}\n\n__global__ void\nvolume_get_voxel_coords(const uint32_t voxel_count, pnanovdb_coord_t* voxel_coords, const pnanovdb_buf_t buf)\n{\n    const uint32_t leaf_index = blockIdx.x;\n    pnanovdb_leaf_handle_t leaf = wp::volume::get_leaf(buf, leaf_index);\n    pnanovdb_coord_t leaf_coords = wp::volume::leaf_origin(buf, leaf);\n\n    pnanovdb_coord_t ijk = {\n        int32_t(threadIdx.x) + leaf_coords.x,\n        int32_t(threadIdx.y) + leaf_coords.y,\n        int32_t(threadIdx.z) + leaf_coords.z,\n    };\n\n    const uint64_t index = wp::volume::leaf_voxel_index(buf, leaf_index, ijk);\n    if (index < voxel_count) {\n        voxel_coords[index] = ijk;\n    }\n}\n\nvoid launch_get_leaf_coords(void* context, const uint32_t leaf_count, pnanovdb_coord_t* leaf_coords, pnanovdb_buf_t buf)\n{\n    ContextGuard guard(context);\n    wp_launch_device(WP_CURRENT_CONTEXT, volume_get_leaf_coords, leaf_count, (leaf_count, leaf_coords, buf));\n}\n\nvoid launch_get_voxel_coords(\n    void* context,\n    const uint32_t leaf_count,\n    const uint32_t voxel_count,\n    pnanovdb_coord_t* voxel_coords,\n    pnanovdb_buf_t buf\n)\n{\n    ContextGuard guard(context);\n    cudaStream_t stream = (cudaStream_t)wp_cuda_stream_get_current();\n    volume_get_voxel_coords<<<leaf_count, dim3(8, 8, 8), 0, stream>>>(voxel_count, voxel_coords, buf);\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"cuda_util.h\"\n#include \"volume_impl.h\"\n\nextern hipCtx_t get_current_context();\n\n__global__ void\nvolume_get_leaf_coords(const uint32_t leaf_count, pnanovdb_coord_t* leaf_coords, const pnanovdb_buf_t buf)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < leaf_count) {\n        pnanovdb_leaf_handle_t leaf = wp::volume::get_leaf(buf, tid);\n        leaf_coords[tid] = wp::volume::leaf_origin(buf, leaf);\n    }\n}\n\n__global__ void\nvolume_get_voxel_coords(const uint32_t voxel_count, pnanovdb_coord_t* voxel_coords, const pnanovdb_buf_t buf)\n{\n    const uint32_t leaf_index = blockIdx.x;\n    pnanovdb_leaf_handle_t leaf = wp::volume::get_leaf(buf, leaf_index);\n    pnanovdb_coord_t leaf_coords = wp::volume::leaf_origin(buf, leaf);\n\n    pnanovdb_coord_t ijk = {\n        int32_t(threadIdx.x) + leaf_coords.x,\n        int32_t(threadIdx.y) + leaf_coords.y,\n        int32_t(threadIdx.z) + leaf_coords.z,\n    };\n\n    const uint64_t index = wp::volume::leaf_voxel_index(buf, leaf_index, ijk);\n    if (index < voxel_count) {\n        voxel_coords[index] = ijk;\n    }\n}\n\nvoid launch_get_leaf_coords(void* context, const uint32_t leaf_count, pnanovdb_coord_t* leaf_coords, pnanovdb_buf_t buf)\n{\n    ContextGuard guard(context);\n    wp_launch_device(WP_CURRENT_CONTEXT, volume_get_leaf_coords, leaf_count, (leaf_count, leaf_coords, buf));\n}\n\nvoid launch_get_voxel_coords(\n    void* context,\n    const uint32_t leaf_count,\n    const uint32_t voxel_count,\n    pnanovdb_coord_t* voxel_coords,\n    pnanovdb_buf_t buf\n)\n{\n    ContextGuard guard(context);\n    hipStream_t stream = (hipStream_t)wp_cuda_stream_get_current();\n    volume_get_voxel_coords<<<leaf_count, dim3(8, 8, 8), 0, stream>>>(voxel_count, voxel_coords, buf);\n}\n", "source": "/kaggle/working/warp/warp/native/volume.cu"}
{"input": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"warp.h\"\n\n#include \"bvh.h\"\n#include \"cuda_util.h\"\n#include \"mesh.h\"\n#include \"scan.h\"\n\nextern CUcontext get_current_context();\n\nnamespace wp {\n\n__global__ void compute_triangle_bounds(int n, const vec3* points, const int* indices, vec3* lowers, vec3* uppers)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < n) {\n        // if leaf then update bounds\n        int i = indices[tid * 3 + 0];\n        int j = indices[tid * 3 + 1];\n        int k = indices[tid * 3 + 2];\n\n        vec3 p = points[i];\n        vec3 q = points[j];\n        vec3 r = points[k];\n\n        vec3 lower = min(min(p, q), r);\n        vec3 upper = max(max(p, q), r);\n\n        lowers[tid] = lower;\n        uppers[tid] = upper;\n    }\n}\n\n__global__ void compute_mesh_edge_lengths(int n, const vec3* points, const int* indices, float* edge_lengths)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < n) {\n        // if leaf then update bounds\n        int i = indices[tid * 3 + 0];\n        int j = indices[tid * 3 + 1];\n        int k = indices[tid * 3 + 2];\n\n        vec3 p = points[i];\n        vec3 q = points[j];\n        vec3 r = points[k];\n\n\n        edge_lengths[tid] = length(p - q) + length(p - r) + length(q - r);\n    }\n}\n\n__global__ void compute_average_mesh_edge_length(int n, float* sum_edge_lengths, Mesh* m)\n{\n    m->average_edge_length = sum_edge_lengths[n - 1] / (3 * n);\n}\n\n__global__ void bvh_refit_with_solid_angle_kernel(\n    int n,\n    const int* __restrict__ parents,\n    int* __restrict__ child_count,\n    BVHPackedNodeHalf* __restrict__ node_lowers,\n    BVHPackedNodeHalf* __restrict__ node_uppers,\n    const vec3* points,\n    const int* indices,\n    const int* primitive_indices,\n    SolidAngleProps* solid_angle_props\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        bool leaf = node_lowers[index].b;\n        int parent = parents[index];\n\n        if (leaf) {\n            BVHPackedNodeHalf& lower = node_lowers[index];\n            BVHPackedNodeHalf& upper = node_uppers[index];\n\n            // update the leaf node\n            bool true_leaf = true;\n\n            if (parent != -1) {\n                true_leaf = !node_lowers[parent].b;\n            }\n\n            if (true_leaf) {\n                SolidAngleProps node_solid_angle_props;\n\n                const int start = lower.i;\n                const int end = upper.i;\n\n                // loops through primitives in the leaf\n                for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                    int primitive_index = primitive_indices[primitive_counter];\n                    if (primitive_counter == start) {\n                        precompute_triangle_solid_angle_props(\n                            points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                            points[indices[primitive_index * 3 + 2]], node_solid_angle_props\n                        );\n                    } else {\n                        SolidAngleProps triangle_solid_angle_props;\n                        precompute_triangle_solid_angle_props(\n                            points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                            points[indices[primitive_index * 3 + 2]], triangle_solid_angle_props\n                        );\n                        node_solid_angle_props = combine_precomputed_solid_angle_props(\n                            &node_solid_angle_props, &triangle_solid_angle_props\n                        );\n                    }\n                }\n\n                (vec3&)lower = node_solid_angle_props.box.lower;\n                (vec3&)upper = node_solid_angle_props.box.upper;\n                solid_angle_props[index] = node_solid_angle_props;\n            }\n        }\n\n        else {\n            // only keep leaf threads\n            return;\n        }\n\n        // update hierarchy\n        for (;;) {\n            parent = parents[index];\n\n            // reached root\n            if (parent == -1)\n                return;\n\n            // ensure all writes are visible\n            __threadfence();\n\n            int finished = atomicAdd(&child_count[parent], 1);\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (finished == 1) {\n                BVHPackedNodeHalf& parent_lower = node_lowers[parent];\n                BVHPackedNodeHalf& parent_upper = node_uppers[parent];\n                if (parent_lower.b)\n                // a packed leaf node can still be a parent in LBVH, we need to recompute its bounds\n                // since we've lost its left and right child node index in the muting process\n                {\n                    int parent_parent = parents[parent];\n                    ;\n                    // only need to compute bound when this is a valid leaf node\n                    bool true_leaf = true;\n\n                    if (parent_parent != -1) {\n                        true_leaf = !node_lowers[parent_parent].b;\n                    }\n\n                    if (true_leaf) {\n                        SolidAngleProps node_solid_angle_props;\n                        const int start = parent_lower.i;\n                        const int end = parent_upper.i;\n                        // loops through primitives in the leaf\n                        for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                            int primitive_index = primitive_indices[primitive_counter];\n                            if (primitive_counter == start) {\n                                precompute_triangle_solid_angle_props(\n                                    points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                                    points[indices[primitive_index * 3 + 2]], node_solid_angle_props\n                                );\n                            } else {\n                                SolidAngleProps triangle_solid_angle_props;\n                                precompute_triangle_solid_angle_props(\n                                    points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                                    points[indices[primitive_index * 3 + 2]], triangle_solid_angle_props\n                                );\n                                node_solid_angle_props = combine_precomputed_solid_angle_props(\n                                    &node_solid_angle_props, &triangle_solid_angle_props\n                                );\n                            }\n                        }\n\n                        (vec3&)parent_lower = node_solid_angle_props.box.lower;\n                        (vec3&)parent_upper = node_solid_angle_props.box.upper;\n                        solid_angle_props[parent] = node_solid_angle_props;\n                    }\n                } else {\n                    // printf(\"Compute non-leaf at %d\\n\", index);\n                    const int left_child = node_lowers[parent].i;\n                    const int right_child = node_uppers[parent].i;\n\n                    vec3 left_lower = (vec3&)(node_lowers[left_child]);\n                    vec3 left_upper = (vec3&)(node_uppers[left_child]);\n                    vec3 right_lower = (vec3&)(node_lowers[right_child]);\n                    vec3 right_upper = (vec3&)(node_uppers[right_child]);\n\n                    // union of child bounds\n                    vec3 lower = min(left_lower, right_lower);\n                    vec3 upper = max(left_upper, right_upper);\n\n                    // write new BVH nodes\n                    (vec3&)parent_lower = lower;\n                    (vec3&)parent_upper = upper;\n\n                    // combine\n                    SolidAngleProps* left_child_data = &solid_angle_props[left_child];\n                    SolidAngleProps* right_child_data\n                        = (left_child != right_child) ? &solid_angle_props[right_child] : NULL;\n\n                    combine_precomputed_solid_angle_props(solid_angle_props[parent], left_child_data, right_child_data);\n                }\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n\nvoid bvh_refit_with_solid_angle_device(BVH& bvh, Mesh& mesh)\n{\n    ContextGuard guard(bvh.context);\n\n    // clear child counters\n    wp_memset_device(WP_CURRENT_CONTEXT, bvh.node_counts, 0, sizeof(int) * bvh.max_nodes);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bvh_refit_with_solid_angle_kernel, bvh.num_leaf_nodes,\n        (bvh.num_leaf_nodes, bvh.node_parents, bvh.node_counts, bvh.node_lowers, bvh.node_uppers, mesh.points,\n         mesh.indices, bvh.primitive_indices, mesh.solid_angle_props)\n    );\n}\n\n}  // namespace wp\n\n\nuint64_t wp_mesh_create_device(\n    void* context,\n    wp::array_t<wp::vec3> points,\n    wp::array_t<wp::vec3> velocities,\n    wp::array_t<int> indices,\n    int num_points,\n    int num_tris,\n    int support_winding_number,\n    int constructor_type,\n    int* groups,\n    int bvh_leaf_size\n)\n{\n    ContextGuard guard(context);\n\n    wp::Mesh mesh(points, velocities, indices, num_points, num_tris);\n    const bool use_cubql = (constructor_type == CUBQL_MESH_CONSTRUCTOR_TYPE);\n    mesh.bvh_backend = use_cubql ? wp::MESH_BVH_BACKEND_CUBQL : wp::MESH_BVH_BACKEND_WARP;\n\n    mesh.context = context ? context : wp_cuda_context_get_current();\n\n    // create lower upper arrays expected by GPU BVH builder\n    mesh.lowers = (wp::vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::vec3) * num_tris, \"(native:mesh)\");\n    mesh.uppers = (wp::vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::vec3) * num_tris, \"(native:mesh)\");\n\n    if (support_winding_number && !use_cubql) {\n        int num_bvh_nodes = 2 * num_tris;\n        mesh.solid_angle_props = (wp::SolidAngleProps*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(wp::SolidAngleProps) * num_bvh_nodes, \"(native:mesh)\"\n        );\n    }\n\n    wp::Mesh* mesh_device = (wp::Mesh*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::Mesh), \"(native:mesh)\");\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &mesh, sizeof(wp::Mesh));\n\n    // save descriptor\n    uint64_t mesh_id = (uint64_t)mesh_device;\n\n    // we compute mesh the average edge length\n    // for use in mesh_query_point_sign_normal()\n    // since it relies on an epsilon for welding\n    // reuse bounds memory temporarily for computing edge lengths\n    float* length_tmp_ptr = (float*)mesh.lowers;\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_mesh_edge_lengths, mesh.num_tris,\n        (mesh.num_tris, mesh.points, mesh.indices, length_tmp_ptr)\n    );\n    scan_device(length_tmp_ptr, length_tmp_ptr, mesh.num_tris, true);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_average_mesh_edge_length, 1, (mesh.num_tris, length_tmp_ptr, mesh_device)\n    );\n\n    // compute triangle bound and construct BVH\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_triangle_bounds, mesh.num_tris,\n        (mesh.num_tris, mesh.points, mesh.indices, mesh.lowers, mesh.uppers)\n    );\n#ifndef WP_DISABLE_CUBQL\n    if (use_cubql) {\n        wp::cubql_bvh_create_device(mesh.context, mesh.lowers, mesh.uppers, num_tris, bvh_leaf_size, mesh.cubql_bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, &(mesh_device->cubql_bvh), &mesh.cubql_bvh, sizeof(wp::CuBQLBVH));\n    } else\n#else\n    if (use_cubql) {\n        fprintf(stderr, \"Warp error: cuBQL support disabled (WP_DISABLE_CUBQL)\\n\");\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.lowers);\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.uppers);\n        if (mesh.solid_angle_props) {\n            wp_free_device(WP_CURRENT_CONTEXT, mesh.solid_angle_props);\n        }\n        wp_free_device(WP_CURRENT_CONTEXT, mesh_device);\n        return 0;\n    }\n#endif\n    {\n        wp::bvh_create_device(\n            mesh.context, mesh.lowers, mesh.uppers, num_tris, constructor_type, groups, bvh_leaf_size, mesh.bvh\n        );\n        // we need to overwrite mesh.bvh because it is not initialized when we construct it on device\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, &(mesh_device->bvh), &mesh.bvh, sizeof(wp::BVH));\n    }\n\n    mesh_add_descriptor(mesh_id, mesh);\n\n    if (support_winding_number && !use_cubql)\n        wp_mesh_refit_device(mesh_id);\n\n    return mesh_id;\n}\n\nvoid wp_mesh_destroy_device(uint64_t id)\n{\n    wp::Mesh mesh;\n    if (wp::mesh_get_descriptor(id, mesh)) {\n        ContextGuard guard(mesh.context);\n\n#ifndef WP_DISABLE_CUBQL\n        if (mesh.bvh_backend == wp::MESH_BVH_BACKEND_CUBQL) {\n            wp::cubql_bvh_destroy_device(mesh.cubql_bvh);\n        } else\n#endif\n        {\n            wp::bvh_destroy_device(mesh.bvh);\n        }\n\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.lowers);\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.uppers);\n        wp_free_device(WP_CURRENT_CONTEXT, (wp::Mesh*)id);\n\n        if (mesh.solid_angle_props) {\n            wp_free_device(WP_CURRENT_CONTEXT, mesh.solid_angle_props);\n        }\n        wp::mesh_rem_descriptor(id);\n    }\n}\n\nvoid mesh_update_stats(uint64_t id) { }\n\nvoid wp_mesh_refit_device(uint64_t id)\n{\n    // recompute triangle bounds\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        ContextGuard guard(m.context);\n\n        // we compute mesh the average edge length\n        // for use in mesh_query_point_sign_normal()\n        // since it relies on an epsilon for welding\n\n        // reuse bounds memory temporarily for computing edge lengths\n        float* length_tmp_ptr = (float*)m.lowers;\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_mesh_edge_lengths, m.num_tris,\n            (m.num_tris, m.points, m.indices, length_tmp_ptr)\n        );\n\n        scan_device(length_tmp_ptr, length_tmp_ptr, m.num_tris, true);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_average_mesh_edge_length, 1, (m.num_tris, length_tmp_ptr, (wp::Mesh*)id)\n        );\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_triangle_bounds, m.num_tris,\n            (m.num_tris, m.points, m.indices, m.lowers, m.uppers)\n        );\n\n#ifndef WP_DISABLE_CUBQL\n        if (m.bvh_backend == wp::MESH_BVH_BACKEND_CUBQL) {\n            wp::cubql_bvh_refit_device(m.cubql_bvh);\n        } else\n#endif\n            if (m.solid_angle_props) {\n            // update solid angle data\n            bvh_refit_with_solid_angle_device(m.bvh, m);\n        } else {\n            wp::bvh_refit_device(m.bvh);\n        }\n    }\n}\n\nvoid wp_mesh_set_points_device(uint64_t id, wp::array_t<wp::vec3> points)\n{\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        if (points.ndim != 1 || points.shape[0] != m.points.shape[0]) {\n            fprintf(\n                stderr,\n                \"The new points input for wp_mesh_set_points_device does not match the shape of the original points!\\n\"\n            );\n            return;\n        }\n\n        m.points = points;\n\n        wp::Mesh* mesh_device = (wp::Mesh*)id;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &m, sizeof(wp::Mesh));\n\n        // update the cpu copy as well\n        mesh_set_descriptor(id, m);\n\n        wp_mesh_refit_device(id);\n    } else {\n        fprintf(stderr, \"The mesh id provided to wp_mesh_set_points_device is not valid!\\n\");\n        return;\n    }\n}\n\nvoid wp_mesh_set_velocities_device(uint64_t id, wp::array_t<wp::vec3> velocities)\n{\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        if (velocities.ndim != 1 || velocities.shape[0] != m.velocities.shape[0]) {\n            fprintf(\n                stderr,\n                \"The new velocities input for wp_mesh_set_velocities_device does not match the shape of the original \"\n                \"velocities\\n\"\n            );\n            return;\n        }\n\n        m.velocities = velocities;\n\n        wp::Mesh* mesh_device = (wp::Mesh*)id;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &m, sizeof(wp::Mesh));\n        mesh_set_descriptor(id, m);\n    } else {\n        fprintf(stderr, \"The mesh id provided to wp_mesh_set_velocities_device is not valid!\\n\");\n        return;\n    }\n}\n", "output": "// SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\n#include \"hip/hip_runtime.h\"\n#include \"warp.h\"\n\n#include \"bvh.h\"\n#include \"cuda_util.h\"\n#include \"mesh.h\"\n#include \"scan.h\"\n\nextern hipCtx_t get_current_context();\n\nnamespace wp {\n\n__global__ void compute_triangle_bounds(int n, const vec3* points, const int* indices, vec3* lowers, vec3* uppers)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < n) {\n        // if leaf then update bounds\n        int i = indices[tid * 3 + 0];\n        int j = indices[tid * 3 + 1];\n        int k = indices[tid * 3 + 2];\n\n        vec3 p = points[i];\n        vec3 q = points[j];\n        vec3 r = points[k];\n\n        vec3 lower = min(min(p, q), r);\n        vec3 upper = max(max(p, q), r);\n\n        lowers[tid] = lower;\n        uppers[tid] = upper;\n    }\n}\n\n__global__ void compute_mesh_edge_lengths(int n, const vec3* points, const int* indices, float* edge_lengths)\n{\n    const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n\n    if (tid < n) {\n        // if leaf then update bounds\n        int i = indices[tid * 3 + 0];\n        int j = indices[tid * 3 + 1];\n        int k = indices[tid * 3 + 2];\n\n        vec3 p = points[i];\n        vec3 q = points[j];\n        vec3 r = points[k];\n\n\n        edge_lengths[tid] = length(p - q) + length(p - r) + length(q - r);\n    }\n}\n\n__global__ void compute_average_mesh_edge_length(int n, float* sum_edge_lengths, Mesh* m)\n{\n    m->average_edge_length = sum_edge_lengths[n - 1] / (3 * n);\n}\n\n__global__ void bvh_refit_with_solid_angle_kernel(\n    int n,\n    const int* __restrict__ parents,\n    int* __restrict__ child_count,\n    BVHPackedNodeHalf* __restrict__ node_lowers,\n    BVHPackedNodeHalf* __restrict__ node_uppers,\n    const vec3* points,\n    const int* indices,\n    const int* primitive_indices,\n    SolidAngleProps* solid_angle_props\n)\n{\n    int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    if (index < n) {\n        bool leaf = node_lowers[index].b;\n        int parent = parents[index];\n\n        if (leaf) {\n            BVHPackedNodeHalf& lower = node_lowers[index];\n            BVHPackedNodeHalf& upper = node_uppers[index];\n\n            // update the leaf node\n            bool true_leaf = true;\n\n            if (parent != -1) {\n                true_leaf = !node_lowers[parent].b;\n            }\n\n            if (true_leaf) {\n                SolidAngleProps node_solid_angle_props;\n\n                const int start = lower.i;\n                const int end = upper.i;\n\n                // loops through primitives in the leaf\n                for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                    int primitive_index = primitive_indices[primitive_counter];\n                    if (primitive_counter == start) {\n                        precompute_triangle_solid_angle_props(\n                            points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                            points[indices[primitive_index * 3 + 2]], node_solid_angle_props\n                        );\n                    } else {\n                        SolidAngleProps triangle_solid_angle_props;\n                        precompute_triangle_solid_angle_props(\n                            points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                            points[indices[primitive_index * 3 + 2]], triangle_solid_angle_props\n                        );\n                        node_solid_angle_props = combine_precomputed_solid_angle_props(\n                            &node_solid_angle_props, &triangle_solid_angle_props\n                        );\n                    }\n                }\n\n                (vec3&)lower = node_solid_angle_props.box.lower;\n                (vec3&)upper = node_solid_angle_props.box.upper;\n                solid_angle_props[index] = node_solid_angle_props;\n            }\n        }\n\n        else {\n            // only keep leaf threads\n            return;\n        }\n\n        // update hierarchy\n        for (;;) {\n            parent = parents[index];\n\n            // reached root\n            if (parent == -1)\n                return;\n\n            // ensure all writes are visible\n            __threadfence();\n\n            int finished = atomicAdd(&child_count[parent], 1);\n\n            // if we have are the last thread (such that the parent node is now complete)\n            // then update its bounds and move onto the next parent in the hierarchy\n            if (finished == 1) {\n                BVHPackedNodeHalf& parent_lower = node_lowers[parent];\n                BVHPackedNodeHalf& parent_upper = node_uppers[parent];\n                if (parent_lower.b)\n                // a packed leaf node can still be a parent in LBVH, we need to recompute its bounds\n                // since we've lost its left and right child node index in the muting process\n                {\n                    int parent_parent = parents[parent];\n                    ;\n                    // only need to compute bound when this is a valid leaf node\n                    bool true_leaf = true;\n\n                    if (parent_parent != -1) {\n                        true_leaf = !node_lowers[parent_parent].b;\n                    }\n\n                    if (true_leaf) {\n                        SolidAngleProps node_solid_angle_props;\n                        const int start = parent_lower.i;\n                        const int end = parent_upper.i;\n                        // loops through primitives in the leaf\n                        for (int primitive_counter = start; primitive_counter < end; primitive_counter++) {\n                            int primitive_index = primitive_indices[primitive_counter];\n                            if (primitive_counter == start) {\n                                precompute_triangle_solid_angle_props(\n                                    points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                                    points[indices[primitive_index * 3 + 2]], node_solid_angle_props\n                                );\n                            } else {\n                                SolidAngleProps triangle_solid_angle_props;\n                                precompute_triangle_solid_angle_props(\n                                    points[indices[primitive_index * 3 + 0]], points[indices[primitive_index * 3 + 1]],\n                                    points[indices[primitive_index * 3 + 2]], triangle_solid_angle_props\n                                );\n                                node_solid_angle_props = combine_precomputed_solid_angle_props(\n                                    &node_solid_angle_props, &triangle_solid_angle_props\n                                );\n                            }\n                        }\n\n                        (vec3&)parent_lower = node_solid_angle_props.box.lower;\n                        (vec3&)parent_upper = node_solid_angle_props.box.upper;\n                        solid_angle_props[parent] = node_solid_angle_props;\n                    }\n                } else {\n                    // printf(\"Compute non-leaf at %d\\n\", index);\n                    const int left_child = node_lowers[parent].i;\n                    const int right_child = node_uppers[parent].i;\n\n                    vec3 left_lower = (vec3&)(node_lowers[left_child]);\n                    vec3 left_upper = (vec3&)(node_uppers[left_child]);\n                    vec3 right_lower = (vec3&)(node_lowers[right_child]);\n                    vec3 right_upper = (vec3&)(node_uppers[right_child]);\n\n                    // union of child bounds\n                    vec3 lower = min(left_lower, right_lower);\n                    vec3 upper = max(left_upper, right_upper);\n\n                    // write new BVH nodes\n                    (vec3&)parent_lower = lower;\n                    (vec3&)parent_upper = upper;\n\n                    // combine\n                    SolidAngleProps* left_child_data = &solid_angle_props[left_child];\n                    SolidAngleProps* right_child_data\n                        = (left_child != right_child) ? &solid_angle_props[right_child] : NULL;\n\n                    combine_precomputed_solid_angle_props(solid_angle_props[parent], left_child_data, right_child_data);\n                }\n                // move onto processing the parent\n                index = parent;\n            } else {\n                // parent not ready (we are the first child), terminate thread\n                break;\n            }\n        }\n    }\n}\n\n\nvoid bvh_refit_with_solid_angle_device(BVH& bvh, Mesh& mesh)\n{\n    ContextGuard guard(bvh.context);\n\n    // clear child counters\n    wp_memset_device(WP_CURRENT_CONTEXT, bvh.node_counts, 0, sizeof(int) * bvh.max_nodes);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, bvh_refit_with_solid_angle_kernel, bvh.num_leaf_nodes,\n        (bvh.num_leaf_nodes, bvh.node_parents, bvh.node_counts, bvh.node_lowers, bvh.node_uppers, mesh.points,\n         mesh.indices, bvh.primitive_indices, mesh.solid_angle_props)\n    );\n}\n\n}  // namespace wp\n\n\nuint64_t wp_mesh_create_device(\n    void* context,\n    wp::array_t<wp::vec3> points,\n    wp::array_t<wp::vec3> velocities,\n    wp::array_t<int> indices,\n    int num_points,\n    int num_tris,\n    int support_winding_number,\n    int constructor_type,\n    int* groups,\n    int bvh_leaf_size\n)\n{\n    ContextGuard guard(context);\n\n    wp::Mesh mesh(points, velocities, indices, num_points, num_tris);\n    const bool use_cubql = (constructor_type == CUBQL_MESH_CONSTRUCTOR_TYPE);\n    mesh.bvh_backend = use_cubql ? wp::MESH_BVH_BACKEND_CUBQL : wp::MESH_BVH_BACKEND_WARP;\n\n    mesh.context = context ? context : wp_cuda_context_get_current();\n\n    // create lower upper arrays expected by GPU BVH builder\n    mesh.lowers = (wp::vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::vec3) * num_tris, \"(native:mesh)\");\n    mesh.uppers = (wp::vec3*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::vec3) * num_tris, \"(native:mesh)\");\n\n    if (support_winding_number && !use_cubql) {\n        int num_bvh_nodes = 2 * num_tris;\n        mesh.solid_angle_props = (wp::SolidAngleProps*)wp_alloc_device(\n            WP_CURRENT_CONTEXT, sizeof(wp::SolidAngleProps) * num_bvh_nodes, \"(native:mesh)\"\n        );\n    }\n\n    wp::Mesh* mesh_device = (wp::Mesh*)wp_alloc_device(WP_CURRENT_CONTEXT, sizeof(wp::Mesh), \"(native:mesh)\");\n    wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &mesh, sizeof(wp::Mesh));\n\n    // save descriptor\n    uint64_t mesh_id = (uint64_t)mesh_device;\n\n    // we compute mesh the average edge length\n    // for use in mesh_query_point_sign_normal()\n    // since it relies on an epsilon for welding\n    // reuse bounds memory temporarily for computing edge lengths\n    float* length_tmp_ptr = (float*)mesh.lowers;\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_mesh_edge_lengths, mesh.num_tris,\n        (mesh.num_tris, mesh.points, mesh.indices, length_tmp_ptr)\n    );\n    scan_device(length_tmp_ptr, length_tmp_ptr, mesh.num_tris, true);\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_average_mesh_edge_length, 1, (mesh.num_tris, length_tmp_ptr, mesh_device)\n    );\n\n    // compute triangle bound and construct BVH\n    wp_launch_device(\n        WP_CURRENT_CONTEXT, wp::compute_triangle_bounds, mesh.num_tris,\n        (mesh.num_tris, mesh.points, mesh.indices, mesh.lowers, mesh.uppers)\n    );\n#ifndef WP_DISABLE_CUBQL\n    if (use_cubql) {\n        wp::cubql_bvh_create_device(mesh.context, mesh.lowers, mesh.uppers, num_tris, bvh_leaf_size, mesh.cubql_bvh);\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, &(mesh_device->cubql_bvh), &mesh.cubql_bvh, sizeof(wp::CuBQLBVH));\n    } else\n#else\n    if (use_cubql) {\n        fprintf(stderr, \"Warp error: cuBQL support disabled (WP_DISABLE_CUBQL)\\n\");\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.lowers);\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.uppers);\n        if (mesh.solid_angle_props) {\n            wp_free_device(WP_CURRENT_CONTEXT, mesh.solid_angle_props);\n        }\n        wp_free_device(WP_CURRENT_CONTEXT, mesh_device);\n        return 0;\n    }\n#endif\n    {\n        wp::bvh_create_device(\n            mesh.context, mesh.lowers, mesh.uppers, num_tris, constructor_type, groups, bvh_leaf_size, mesh.bvh\n        );\n        // we need to overwrite mesh.bvh because it is not initialized when we construct it on device\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, &(mesh_device->bvh), &mesh.bvh, sizeof(wp::BVH));\n    }\n\n    mesh_add_descriptor(mesh_id, mesh);\n\n    if (support_winding_number && !use_cubql)\n        wp_mesh_refit_device(mesh_id);\n\n    return mesh_id;\n}\n\nvoid wp_mesh_destroy_device(uint64_t id)\n{\n    wp::Mesh mesh;\n    if (wp::mesh_get_descriptor(id, mesh)) {\n        ContextGuard guard(mesh.context);\n\n#ifndef WP_DISABLE_CUBQL\n        if (mesh.bvh_backend == wp::MESH_BVH_BACKEND_CUBQL) {\n            wp::cubql_bvh_destroy_device(mesh.cubql_bvh);\n        } else\n#endif\n        {\n            wp::bvh_destroy_device(mesh.bvh);\n        }\n\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.lowers);\n        wp_free_device(WP_CURRENT_CONTEXT, mesh.uppers);\n        wp_free_device(WP_CURRENT_CONTEXT, (wp::Mesh*)id);\n\n        if (mesh.solid_angle_props) {\n            wp_free_device(WP_CURRENT_CONTEXT, mesh.solid_angle_props);\n        }\n        wp::mesh_rem_descriptor(id);\n    }\n}\n\nvoid mesh_update_stats(uint64_t id) { }\n\nvoid wp_mesh_refit_device(uint64_t id)\n{\n    // recompute triangle bounds\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        ContextGuard guard(m.context);\n\n        // we compute mesh the average edge length\n        // for use in mesh_query_point_sign_normal()\n        // since it relies on an epsilon for welding\n\n        // reuse bounds memory temporarily for computing edge lengths\n        float* length_tmp_ptr = (float*)m.lowers;\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_mesh_edge_lengths, m.num_tris,\n            (m.num_tris, m.points, m.indices, length_tmp_ptr)\n        );\n\n        scan_device(length_tmp_ptr, length_tmp_ptr, m.num_tris, true);\n\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_average_mesh_edge_length, 1, (m.num_tris, length_tmp_ptr, (wp::Mesh*)id)\n        );\n        wp_launch_device(\n            WP_CURRENT_CONTEXT, wp::compute_triangle_bounds, m.num_tris,\n            (m.num_tris, m.points, m.indices, m.lowers, m.uppers)\n        );\n\n#ifndef WP_DISABLE_CUBQL\n        if (m.bvh_backend == wp::MESH_BVH_BACKEND_CUBQL) {\n            wp::cubql_bvh_refit_device(m.cubql_bvh);\n        } else\n#endif\n            if (m.solid_angle_props) {\n            // update solid angle data\n            bvh_refit_with_solid_angle_device(m.bvh, m);\n        } else {\n            wp::bvh_refit_device(m.bvh);\n        }\n    }\n}\n\nvoid wp_mesh_set_points_device(uint64_t id, wp::array_t<wp::vec3> points)\n{\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        if (points.ndim != 1 || points.shape[0] != m.points.shape[0]) {\n            fprintf(\n                stderr,\n                \"The new points input for wp_mesh_set_points_device does not match the shape of the original points!\\n\"\n            );\n            return;\n        }\n\n        m.points = points;\n\n        wp::Mesh* mesh_device = (wp::Mesh*)id;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &m, sizeof(wp::Mesh));\n\n        // update the cpu copy as well\n        mesh_set_descriptor(id, m);\n\n        wp_mesh_refit_device(id);\n    } else {\n        fprintf(stderr, \"The mesh id provided to wp_mesh_set_points_device is not valid!\\n\");\n        return;\n    }\n}\n\nvoid wp_mesh_set_velocities_device(uint64_t id, wp::array_t<wp::vec3> velocities)\n{\n    wp::Mesh m;\n    if (mesh_get_descriptor(id, m)) {\n        if (velocities.ndim != 1 || velocities.shape[0] != m.velocities.shape[0]) {\n            fprintf(\n                stderr,\n                \"The new velocities input for wp_mesh_set_velocities_device does not match the shape of the original \"\n                \"velocities\\n\"\n            );\n            return;\n        }\n\n        m.velocities = velocities;\n\n        wp::Mesh* mesh_device = (wp::Mesh*)id;\n        wp_memcpy_h2d(WP_CURRENT_CONTEXT, mesh_device, &m, sizeof(wp::Mesh));\n        mesh_set_descriptor(id, m);\n    } else {\n        fprintf(stderr, \"The mesh id provided to wp_mesh_set_velocities_device is not valid!\\n\");\n        return;\n    }\n}\n", "source": "/kaggle/working/warp/warp/native/mesh.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example requires NVIDIA Ampere GPU or later.\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS Includes\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/functional.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/gemm/warp/default_mma_tensor_op.h\"\n#include \"cutlass/epilogue/warp/fragment_iterator_tensor_op.h\"\n#include \"cutlass/epilogue/warp/tile_iterator_tensor_op.h\"\n\n// CUTLASS Utility Includes\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/gemm_complex.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define the overall warp-level problem shape\nint const kM = 27;\nint const kN = 31;\nint const kK = 17;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define a warp-level GEMM operator.\n//\n// This template could be part of the CUTLASS Template Library or implemented internally. This\n// wraps the matrix multiply operation and epilogue with a GEMM-like interface that can be\n// instantiated in device code.\n\nnamespace cutlass {\nnamespace gemm {\nnamespace warp {\n\ntemplate <\n  typename Shape,\n  typename InstructionShape,\n  typename ElementA,\n  typename LayoutA,\n  typename ElementB,\n  typename LayoutB,\n  typename ElementC,\n  typename LayoutC,\n  typename ElementScalar\n>\nclass GemmTensorOp {\npublic:\n\n  using WarpShape = GemmShape<\n    ((Shape::kM + InstructionShape::kM - 1) / InstructionShape::kM) * InstructionShape::kM,\n    ((Shape::kN + InstructionShape::kN - 1) / InstructionShape::kN) * InstructionShape::kN,\n    InstructionShape::kK\n  >;\n\n  using MmaWarp = typename cutlass::gemm::warp::DefaultMmaTensorOp<\n    WarpShape,\n    InstructionShape,\n    double,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    double,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    double,                             // Data type of C elements\n    cutlass::layout::RowMajor           // Layout of C matrix\n  >::Type;\n \n  // Number of 'K groups' \n  int const kKgroups = (Shape::kK + InstructionShape::kK - 1) / InstructionShape::kK;\n\n  // Define a 'FragmentIterator' to iterate over slices of accumulators\n  using FragmentIterator = cutlass::epilogue::warp::FragmentIteratorTensorOp<\n    typename MmaWarp::Shape,\n    InstructionShape,\n    double,\n    typename MmaWarp::Policy::Operator::FragmentC,\n    cutlass::layout::RowMajor\n  >;\n\n  // Define an epilogue 'Tile Iteterator' to iterate over slices of elements in Shared Memory\n  using AccumulatorTileIterator = cutlass::epilogue::warp::TileIteratorTensorOpCanonical<\n    typename MmaWarp::Shape,\n    InstructionShape,\n    double,\n    cutlass::layout::RowMajor\n  >;\n\n  using TensorRefA = typename MmaWarp::IteratorA::TensorRef;\n  using TensorRefB = typename MmaWarp::IteratorB::TensorRef;\n  using TensorRefC = typename AccumulatorTileIterator::TensorRef;\n\npublic:\n  CUTLASS_HOST_DEVICE\n  GemmTensorOp() { }\n\n  CUTLASS_DEVICE\n  void operator()(\n    ElementScalar alpha, \n    TensorRefA ref_A, \n    TensorRefB ref_B, \n    ElementScalar beta,\n    TensorRefC ref_C,\n    TensorRefC ref_D,\n    int lane_id) const {\n  \n    // Instantiate iterators pointing to slices of the A and B matrices in shared memory\n    typename MmaWarp::IteratorA iter_A(ref_A, {Shape::kM, Shape::kK}, lane_id);\n    typename MmaWarp::IteratorB iter_B(ref_B, {Shape::kK, Shape::kN}, lane_id);\n\n    // Instantiate and clear accumulator tile holding the C matrix\n    typename MmaWarp::FragmentC accum;\n    accum.clear();\n  \n    // Instantiate the warp-level matrix multiply operator\n    MmaWarp mma_op;\n\n    // Instantiate fragments holding the slice of the matrix held by each warp\n    typename MmaWarp::FragmentA frag_A[2];\n    typename MmaWarp::FragmentB frag_B[2];\n      \n    // Load fragments from shared memory\n    iter_A.load(frag_A[0]);\n    iter_B.load(frag_B[0]);\n\n    ++iter_A;\n    ++iter_B;\n\n    // Load fragments from shared memory\n    CUTLASS_PRAGMA_UNROLL\n    for (int k = 0; k < kKgroups; ++k) {\n\n      // Load fragments from shared memory\n      iter_A.load(frag_A[(k + 1) % 2]);\n      iter_B.load(frag_B[(k + 1) % 2]);\n\n      ++iter_A;\n      ++iter_B;\n\n      // Compute the matrix multiply\n      mma_op(accum, frag_A[k % 2], frag_B[k % 2], accum);\n    }\n  \n    // Instantiate iterators\n    FragmentIterator accum_frag_it(accum);\n    AccumulatorTileIterator source_tile_it(ref_C, {Shape::kM, Shape::kN}, lane_id);\n    AccumulatorTileIterator dest_tile_it(ref_D, {Shape::kM, Shape::kN}, lane_id);\n\n    // Define function objects for linear scaling operation\n    cutlass::multiplies<typename FragmentIterator::Fragment> mul_source;\n    cutlass::multiply_add<typename FragmentIterator::Fragment> mul_add_accumulator;\n\n    // Iterate over the epilogue components\n    CUTLASS_PRAGMA_UNROLL\n    for (int idx = 0; idx < FragmentIterator::kIterations; ++idx) {\n\n      // Define storage for slices of the accumulators\n      typename FragmentIterator::Fragment accum_fragment;\n      typename FragmentIterator::Fragment source_fragment;\n\n      // Select a slice of accumulators from the accumulator tile\n      accum_frag_it.load(accum_fragment);\n      ++accum_frag_it;\n\n      // Load a corresponding slice from Shared memory\n      source_tile_it.load(source_fragment);\n      ++source_tile_it;\n\n      // Compute linear scaling - alpha * AB + beta * C\n      source_fragment = mul_source(beta, source_fragment);\n      accum_fragment = mul_add_accumulator(alpha, accum_fragment, source_fragment);\n\n      // Store the result to shared memory\n      dest_tile_it.store(accum_fragment);\n      ++dest_tile_it;\n    }\n  }\n};\n\n} // namespace warp\n} // namespace gemm\n} // namespace cutlass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Sample kernel demonstrating a collective GEMM operation by a warp on arbitrary matrices held\n// in Shared Memory.\n__global__ void kernel(\n  double *D_gmem, \n  double alpha, \n  double const *A_gmem, \n  double const *B_gmem, \n  double beta,\n  double const *C_gmem) {\n\n  // Define several matrices in shared memory\n  __shared__ double A[kM][kK];\n  __shared__ double B[kN][kK];\n  __shared__ double C[kM][kN];\n\n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      for (int k = 0; k < kK; ++k) {\n        A[m][k] = A_gmem[m * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int n = 0; n < kN; ++n) {\n      for (int k = 0; k < kK; ++k) {\n        B[n][k] = B_gmem[n * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        C[m][n] = C_gmem[m * kN + n];\n      }\n    }\n  }\n\n  __syncthreads();\n  \n  //\n  // Instantiate a warp-level matrix multiply operator given the fundamental instruction shape (8x8x4),\n  // overall shape, data type of each operand, and layout of each operand.\n  //\n\n  using GemmTensorOp = cutlass::gemm::warp::GemmTensorOp<\n    cutlass::gemm::GemmShape<kM, kN, kK>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    double,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    double,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    double,                             // Data type of C elements\n    cutlass::layout::RowMajor,          // Layout of C matrix\n    double                              // Scalar type of alpha and beta\n  >;\n\n  // Instantiate the GEMM operator\n  GemmTensorOp gemm;\n\n  // Execute the warp-level GEMM operation\n  gemm(\n    alpha, \n    {&A[0][0], kK},\n    {&B[0][0], kK},\n    beta,\n    {&C[0][0], kN},\n    {&C[0][0], kN},\n    threadIdx.x);\n\n  __syncthreads();\n  \n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        D_gmem[m * kN + n] = C[m][n];\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to canonical warp-level GEMM operation\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  cudaDeviceProp props;\n\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    // Return 0 so tests are considered passing if run on unsupported platforms.\n    return 0;\n  }\n\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> A({kM, kK});\n  cutlass::HostTensor<double, cutlass::layout::ColumnMajor> B({kK, kN});\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> C({kM, kN});\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> D({kM, kN});\n\n  uint64_t seed = 2020;\n  double max = 8;\n  double min = -8;\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    A.host_view(),\n    seed,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    B.host_view(),\n    seed + 17,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    C.host_view(),\n    seed + 31,\n    max,\n    min,\n    0\n  );\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  dim3 grid(1,1);\n  dim3 block(32, 1, 1);\n\n  double alpha = 2.25;\n  double beta = 1.24;\n\n  kernel<<< grid, block >>>(\n    D.device_data(),\n    alpha,\n    A.device_data(),\n    B.device_data(),\n    beta,\n    C.device_data()\n  );\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to synchronize device after kernel launch.\" << std::endl;\n    return -1;\n  }\n\n  D.sync_host();\n  \n  // Compute reference on host\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> D_ref({kM, kN}, false);\n\n  cutlass::reference::host::GemmComplex(\n    {kM, kN, kK},\n    alpha,\n    A.host_ref(),\n    cutlass::ComplexTransform::kNone,\n    B.host_ref(),\n    cutlass::ComplexTransform::kNone,\n    beta,\n    C.host_ref(),\n    D_ref.host_ref(),\n    double()\n  );\n\n  // Verify reference matches computed\n  if (!cutlass::reference::host::TensorEquals(\n    D.host_view(),\n    D_ref.host_view())) {\n\n    std::cerr \n      << \"A =\\n\" << A.host_view() \n      << \"\\n\\nB = \\n\" << B.host_view() \n      << \"\\n\\nC = \" << C.host_view() \n      << \"\\n\\nRef =\\n\" << D_ref.host_view()\n      << \"\\n\\nD =\\n\" << D.host_view() << \"\\n\\n\";\n\n    std::cerr << \"Error - device results mismatch host reference.\" << std::endl;\n\n    return -1;\n  }\n\n  std::cout << \"Passed\" << std::endl;\n\n  return 0;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example requires NVIDIA Ampere GPU or later.\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS Includes\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/functional.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/gemm/warp/default_mma_tensor_op.h\"\n#include \"cutlass/epilogue/warp/fragment_iterator_tensor_op.h\"\n#include \"cutlass/epilogue/warp/tile_iterator_tensor_op.h\"\n\n// CUTLASS Utility Includes\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/gemm_complex.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define the overall warp-level problem shape\nint const kM = 27;\nint const kN = 31;\nint const kK = 17;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define a warp-level GEMM operator.\n//\n// This template could be part of the CUTLASS Template Library or implemented internally. This\n// wraps the matrix multiply operation and epilogue with a GEMM-like interface that can be\n// instantiated in device code.\n\nnamespace cutlass {\nnamespace gemm {\nnamespace warp {\n\ntemplate <\n  typename Shape,\n  typename InstructionShape,\n  typename ElementA,\n  typename LayoutA,\n  typename ElementB,\n  typename LayoutB,\n  typename ElementC,\n  typename LayoutC,\n  typename ElementScalar\n>\nclass GemmTensorOp {\npublic:\n\n  using WarpShape = GemmShape<\n    ((Shape::kM + InstructionShape::kM - 1) / InstructionShape::kM) * InstructionShape::kM,\n    ((Shape::kN + InstructionShape::kN - 1) / InstructionShape::kN) * InstructionShape::kN,\n    InstructionShape::kK\n  >;\n\n  using MmaWarp = typename cutlass::gemm::warp::DefaultMmaTensorOp<\n    WarpShape,\n    InstructionShape,\n    double,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    double,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    double,                             // Data type of C elements\n    cutlass::layout::RowMajor           // Layout of C matrix\n  >::Type;\n \n  // Number of 'K groups' \n  int const kKgroups = (Shape::kK + InstructionShape::kK - 1) / InstructionShape::kK;\n\n  // Define a 'FragmentIterator' to iterate over slices of accumulators\n  using FragmentIterator = cutlass::epilogue::warp::FragmentIteratorTensorOp<\n    typename MmaWarp::Shape,\n    InstructionShape,\n    double,\n    typename MmaWarp::Policy::Operator::FragmentC,\n    cutlass::layout::RowMajor\n  >;\n\n  // Define an epilogue 'Tile Iteterator' to iterate over slices of elements in Shared Memory\n  using AccumulatorTileIterator = cutlass::epilogue::warp::TileIteratorTensorOpCanonical<\n    typename MmaWarp::Shape,\n    InstructionShape,\n    double,\n    cutlass::layout::RowMajor\n  >;\n\n  using TensorRefA = typename MmaWarp::IteratorA::TensorRef;\n  using TensorRefB = typename MmaWarp::IteratorB::TensorRef;\n  using TensorRefC = typename AccumulatorTileIterator::TensorRef;\n\npublic:\n  CUTLASS_HOST_DEVICE\n  GemmTensorOp() { }\n\n  CUTLASS_DEVICE\n  void operator()(\n    ElementScalar alpha, \n    TensorRefA ref_A, \n    TensorRefB ref_B, \n    ElementScalar beta,\n    TensorRefC ref_C,\n    TensorRefC ref_D,\n    int lane_id) const {\n  \n    // Instantiate iterators pointing to slices of the A and B matrices in shared memory\n    typename MmaWarp::IteratorA iter_A(ref_A, {Shape::kM, Shape::kK}, lane_id);\n    typename MmaWarp::IteratorB iter_B(ref_B, {Shape::kK, Shape::kN}, lane_id);\n\n    // Instantiate and clear accumulator tile holding the C matrix\n    typename MmaWarp::FragmentC accum;\n    accum.clear();\n  \n    // Instantiate the warp-level matrix multiply operator\n    MmaWarp mma_op;\n\n    // Instantiate fragments holding the slice of the matrix held by each warp\n    typename MmaWarp::FragmentA frag_A[2];\n    typename MmaWarp::FragmentB frag_B[2];\n      \n    // Load fragments from shared memory\n    iter_A.load(frag_A[0]);\n    iter_B.load(frag_B[0]);\n\n    ++iter_A;\n    ++iter_B;\n\n    // Load fragments from shared memory\n    CUTLASS_PRAGMA_UNROLL\n    for (int k = 0; k < kKgroups; ++k) {\n\n      // Load fragments from shared memory\n      iter_A.load(frag_A[(k + 1) % 2]);\n      iter_B.load(frag_B[(k + 1) % 2]);\n\n      ++iter_A;\n      ++iter_B;\n\n      // Compute the matrix multiply\n      mma_op(accum, frag_A[k % 2], frag_B[k % 2], accum);\n    }\n  \n    // Instantiate iterators\n    FragmentIterator accum_frag_it(accum);\n    AccumulatorTileIterator source_tile_it(ref_C, {Shape::kM, Shape::kN}, lane_id);\n    AccumulatorTileIterator dest_tile_it(ref_D, {Shape::kM, Shape::kN}, lane_id);\n\n    // Define function objects for linear scaling operation\n    cutlass::multiplies<typename FragmentIterator::Fragment> mul_source;\n    cutlass::multiply_add<typename FragmentIterator::Fragment> mul_add_accumulator;\n\n    // Iterate over the epilogue components\n    CUTLASS_PRAGMA_UNROLL\n    for (int idx = 0; idx < FragmentIterator::kIterations; ++idx) {\n\n      // Define storage for slices of the accumulators\n      typename FragmentIterator::Fragment accum_fragment;\n      typename FragmentIterator::Fragment source_fragment;\n\n      // Select a slice of accumulators from the accumulator tile\n      accum_frag_it.load(accum_fragment);\n      ++accum_frag_it;\n\n      // Load a corresponding slice from Shared memory\n      source_tile_it.load(source_fragment);\n      ++source_tile_it;\n\n      // Compute linear scaling - alpha * AB + beta * C\n      source_fragment = mul_source(beta, source_fragment);\n      accum_fragment = mul_add_accumulator(alpha, accum_fragment, source_fragment);\n\n      // Store the result to shared memory\n      dest_tile_it.store(accum_fragment);\n      ++dest_tile_it;\n    }\n  }\n};\n\n} // namespace warp\n} // namespace gemm\n} // namespace cutlass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Sample kernel demonstrating a collective GEMM operation by a warp on arbitrary matrices held\n// in Shared Memory.\n__global__ void kernel(\n  double *D_gmem, \n  double alpha, \n  double const *A_gmem, \n  double const *B_gmem, \n  double beta,\n  double const *C_gmem) {\n\n  // Define several matrices in shared memory\n  __shared__ double A[kM][kK];\n  __shared__ double B[kN][kK];\n  __shared__ double C[kM][kN];\n\n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      for (int k = 0; k < kK; ++k) {\n        A[m][k] = A_gmem[m * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int n = 0; n < kN; ++n) {\n      for (int k = 0; k < kK; ++k) {\n        B[n][k] = B_gmem[n * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        C[m][n] = C_gmem[m * kN + n];\n      }\n    }\n  }\n\n  __syncthreads();\n  \n  //\n  // Instantiate a warp-level matrix multiply operator given the fundamental instruction shape (8x8x4),\n  // overall shape, data type of each operand, and layout of each operand.\n  //\n\n  using GemmTensorOp = cutlass::gemm::warp::GemmTensorOp<\n    cutlass::gemm::GemmShape<kM, kN, kK>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    double,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    double,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    double,                             // Data type of C elements\n    cutlass::layout::RowMajor,          // Layout of C matrix\n    double                              // Scalar type of alpha and beta\n  >;\n\n  // Instantiate the GEMM operator\n  GemmTensorOp gemm;\n\n  // Execute the warp-level GEMM operation\n  gemm(\n    alpha, \n    {&A[0][0], kK},\n    {&B[0][0], kK},\n    beta,\n    {&C[0][0], kN},\n    {&C[0][0], kN},\n    threadIdx.x);\n\n  __syncthreads();\n  \n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        D_gmem[m * kN + n] = C[m][n];\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to canonical warp-level GEMM operation\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  hipDeviceProp_t props;\n\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    // Return 0 so tests are considered passing if run on unsupported platforms.\n    return 0;\n  }\n\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> A({kM, kK});\n  cutlass::HostTensor<double, cutlass::layout::ColumnMajor> B({kK, kN});\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> C({kM, kN});\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> D({kM, kN});\n\n  uint64_t seed = 2020;\n  double max = 8;\n  double min = -8;\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    A.host_view(),\n    seed,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    B.host_view(),\n    seed + 17,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    C.host_view(),\n    seed + 31,\n    max,\n    min,\n    0\n  );\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  dim3 grid(1,1);\n  dim3 block(32, 1, 1);\n\n  double alpha = 2.25;\n  double beta = 1.24;\n\n  kernel<<< grid, block >>>(\n    D.device_data(),\n    alpha,\n    A.device_data(),\n    B.device_data(),\n    beta,\n    C.device_data()\n  );\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to synchronize device after kernel launch.\" << std::endl;\n    return -1;\n  }\n\n  D.sync_host();\n  \n  // Compute reference on host\n  cutlass::HostTensor<double, cutlass::layout::RowMajor> D_ref({kM, kN}, false);\n\n  cutlass::reference::host::GemmComplex(\n    {kM, kN, kK},\n    alpha,\n    A.host_ref(),\n    cutlass::ComplexTransform::kNone,\n    B.host_ref(),\n    cutlass::ComplexTransform::kNone,\n    beta,\n    C.host_ref(),\n    D_ref.host_ref(),\n    double()\n  );\n\n  // Verify reference matches computed\n  if (!cutlass::reference::host::TensorEquals(\n    D.host_view(),\n    D_ref.host_view())) {\n\n    std::cerr \n      << \"A =\\n\" << A.host_view() \n      << \"\\n\\nB = \\n\" << B.host_view() \n      << \"\\n\\nC = \" << C.host_view() \n      << \"\\n\\nRef =\\n\" << D_ref.host_view()\n      << \"\\n\\nD =\\n\" << D.host_view() << \"\\n\\n\";\n\n    std::cerr << \"Error - device results mismatch host reference.\" << std::endl;\n\n    return -1;\n  }\n\n  std::cout << \"Passed\" << std::endl;\n\n  return 0;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/19_tensorop_canonical/tensorop_canonical.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n  \\brief Example demonstrating CuTe and CUTLASS 3.x based Ampere convolution forward propagation kernel\n      capable of operating on both affine and gather/scatter tensors.\n\n  This example demonstartes a few super cool features of CUTLASS and CuTe. It shows off\n  1. A dense conv 3D fprop kernel written as a single file ...\n  2. ... that leverages off the shelf CUTLASS collectives to show how custom kernels can use collectives ...\n  3. ... and uses the exact same templated kernel to also stamp out a gather/scatter 3D fprop conv ...\n  4. ... while getting near peak performance of the Ampere class tensor core on Ampere and Ada GPUs ...\n  5. ... by using static cute shapes and strides in case problem shapes are known at compile time.\n\n  Full documentation for this example can be found within the README.md file in this directory.\n\n  Example executions:\n  ./59_ampere_gather_scatter_conv\n  ./59_ampere_gather_scatter_conv --n=108\n  ./59_ampere_gather_scatter_conv --n=4096 --i=1\n  ./59_ampere_gather_scatter_conv --n=1080 --i=1000\n  ./59_ampere_gather_scatter_conv --n=131072 --i=1000 --no-check\n*/\n\n#include <thrust/sequence.h>\n#include <thrust/universal_vector.h>\n\n#include \"ampere_conv_kernel.h\"\n#include \"gather_tensor.hpp\"\n\n#include \"cutlass/util/command_line.h\"\n\nbool check_cuda_result(cudaError_t code, const char* file, int line) {\n  if (code == cudaSuccess) {\n    return true;\n  }\n\n  std::cerr << \"CUDA error at  (\" << file << \",\" << line << \")\\n\\t\" << unsigned(code) << \" -- \" << cudaGetErrorString(code) << \"\\n\";\n  return false;\n}\n\n#define CHECK_CUDA(code) (check_cuda_result(code, __FILE__, __LINE__))\n\nusing namespace cute;\nusing example::IndexedGather;\nusing example::CustomStride;\n\ntemplate<class Operator, class FilterTensor, class ActivationTensor, class OutputTensor>\n__global__\n__launch_bounds__(Operator::MaxThreadsPerBlock, Operator::MinBlocksPerMultiprocessor)\nvoid kernel_entrypoint(FilterTensor mFlt, ActivationTensor mAct, OutputTensor mOut) {\n  extern __shared__ char smem_buf[];\n  Operator op;\n  op(mFlt, mAct, mOut, smem_buf);\n}\n\nint ampere_dense_conv_fprop(\n    int num_images,\n    float* activations,\n    float* filter,\n    float* output,\n    float* output_ref,\n    int num_iterations = 1,\n    bool do_ref_check = true) {\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n  auto S = typename AmpereUnpredicatedFprop::S{};\n  auto R = typename AmpereUnpredicatedFprop::R{};\n  auto T = typename AmpereUnpredicatedFprop::T{};\n\n  int N = num_images; // dynamic\n  if (num_images % int(typename AmpereUnpredicatedFprop::Tiler_N{}) != 0) {\n    printf(\"ERROR: Input image count must be evenly divisible by CTA tiler N.\\n\");\n    return 1;\n  }\n\n  // Tensor Activation: (n,d,h,w,c)::(?,6,4,4,64):(6144,1536,384,64,1)\n  auto activation_layout = make_layout(\n    make_shape (make_shape (      N,     D,   H, W), make_shape ( C,   _1{},_1{},_1{})),\n    make_stride(make_stride(D*H*W*C, H*W*C, W*C, C), make_stride(_1{}, _0{},_0{},_0{})));\n\n  auto xformed_act_layout = make_layout(\n    make_shape (make_shape(N, Z, P, Q),       make_shape ( C,       T,   R, S)),\n    make_stride(stride<0>(activation_layout), make_stride(_1{}, H*W*C, W*C, C)));\n\n  // Tensor Filter    : (k,c,s,r,t)::(128,3,3,3,64):(1728,576,192,64,1)\n  auto filter_layout = AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  // Tensor Output    : (n,z,p,q,k)::(?,4,2,2,128):(2048,1024,512,128,1)\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape( N,   Z,   P,   Q)),\n    make_tuple(_0{}, make_tuple(_4{},_3{},_2{},_1{})));\n\n  Tensor mActivation = make_tensor(make_gmem_ptr(activations), activation_layout);\n  Tensor mXformedAct = make_tensor(make_gmem_ptr(activations), xformed_act_layout);\n  Tensor mFilter     = make_tensor(make_gmem_ptr(filter), filter_layout);\n  Tensor mOutput     = make_tensor(make_gmem_ptr(output), output_layout); // (K, (N,Z,P,Q))\n  Tensor mOutputRef  = make_tensor(make_gmem_ptr(output_ref), output_layout);\n\n  print(\"xformed act layout ((N,Z,P,Q), (C,T,R,S)) = \"); print(xformed_act_layout); print(\"\\n\");\n\n  cudaEvent_t start, stop;\n  CHECK_CUDA(cudaEventCreate(&start));\n  CHECK_CUDA(cudaEventCreate(&stop));\n\n  constexpr size_t smem_size = sizeof(typename AmpereUnpredicatedFprop::SharedStorage);\n  Tensor gOutput_mn = zipped_divide(mOutput, typename AmpereUnpredicatedFprop::TilerOut{}); // ((BLK_M, BLK_N), (m', n'))\n  dim3 lauch_grid {static_cast<uint32_t>(size<1,1>(gOutput_mn)), static_cast<uint32_t>(size<1,0>(gOutput_mn)), 1};\n\n  CHECK_CUDA(cudaFuncSetAttribute(\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedAct), decltype(mOutput)>,\n    cudaFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  CHECK_CUDA(cudaEventRecord(start));\n  for (int i = 0; i < num_iterations; ++i) {\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedAct), decltype(mOutput)>\n      <<<lauch_grid, AmpereUnpredicatedFprop::MaxThreadsPerBlock, smem_size>>>(\n        mFilter, mXformedAct, mOutput);\n  }\n  CHECK_CUDA(cudaEventRecord(stop));\n  CHECK_CUDA(cudaEventSynchronize(stop));\n\n  float milliseconds = 0;\n  cudaEventElapsedTime(&milliseconds, start, stop);\n  milliseconds /= float(num_iterations);\n\n  double tflop_count = (2 * double(size<0>(xformed_act_layout)) * double(size(filter_layout))) / double(1e12);\n  double tflops = tflop_count / (double(milliseconds) / double(1e3));\n\n  printf(\"Conv TFLOP count = %f\\n\", tflop_count);\n  printf(\"Conv dense perf: %fms | TFLOP/s = %f\\n\", milliseconds, tflops);\n\n  if (do_ref_check) {\n    printf(\"Running host reference check ...\\n\");\n    return fprop_reference(mFilter, mXformedAct, mOutput, mOutputRef);\n  }\n  else {\n    return 0;\n  }\n}\n\nint ampere_gather_scatter_conv_fprop(\n    int num_images,\n    float* activations,\n    uint32_t *gather_idx_buf,\n    float* filter,\n    float* output,\n    uint32_t *scatter_idx_buf,\n    int num_iterations = 1) {\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n  auto S = typename AmpereUnpredicatedFprop::S{};\n  auto R = typename AmpereUnpredicatedFprop::R{};\n  auto T = typename AmpereUnpredicatedFprop::T{};\n\n  int N = num_images; // dynamic\n  if (N % int(typename AmpereUnpredicatedFprop::Tiler_N{}) != 0) {\n    printf(\"ERROR: Input image count must be evenly divisible by CTA tiler N. Got num_images = %d\\n\", N);\n    return 1;\n  }\n\n  // Tensor Filter    : (k,c,s,r,t)::(128,3,3,3,64):(1728,576,192,64,1)\n  auto filter_layout = AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  // Tensor Output    : (n,z,p,q,k)::(?,4,2,2,128):(2048,1024,512,128,1)\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape( N,   Z,   P,   Q)),\n    make_tuple(_0{}, make_tuple(_4{},_3{},_2{},_1{})));\n\n  // Input gather layout\n  // inner_layout(make_coord((nzpq), (csrt))) => (idx_buffer_idx, dense_c_idx)\n  auto EG = E<0>{};  // Gather basis     (1,0) (idx_buffer_idx) \n  auto EC = E<1>{};  // Contiguous basis (0,1) (dense_offset)    \n  auto xformed_act_logical_inner = make_layout(\n    make_shape (make_shape (       N,      Z,    P,  Q), make_shape ( C,      T,    R,  S)),\n    make_stride(make_stride(D*H*W*EG, H*W*EG, W*EG, EG), make_stride(EC, H*W*EG, W*EG, EG)));\n\n  // outer_layout(make_coord(idx_buffer_idx, dense_c_idx)) => idx\n  // IndexedGather obtains idx by applying (gmem_base_ptr + gather_idx_buf[idx_buffer_idx] + dense_offset)\n  auto xformed_act_gather_outer = make_layout(\n    make_shape(_1{},_1{}),\n    make_stride(CustomStride{IndexedGather{gather_idx_buf}, C}, _1{}));\n\n  // Compose the inner and outer layouts\n  // gather_composed(make_coord((nzpq), (csrt))) => idx\n  auto xformed_act_composed_layout = composition(\n    xformed_act_gather_outer,\n    make_arithmetic_tuple(_0{}, _0{}),\n    xformed_act_logical_inner);\n\n  // Output scatter layout\n  auto out_basis_stride = make_stride(\n    E<1>{},\n    make_stride(Z*P*Q*E<0>{}, P*Q*E<0>{}, Q*E<0>{}, _1{}*E<0>{})); // -> (crd0, crd1)\n  auto out_basis_layout = make_layout(shape(output_layout), out_basis_stride);\n  auto out_scatter_layout = make_layout(\n    make_shape(_1{},_1{}),\n    make_stride(CustomStride{IndexedGather{scatter_idx_buf}, K}, _1{}));\n  auto out_composed_layout = composition(\n    out_scatter_layout,\n    make_arithmetic_tuple(_0{},_0{}),\n    out_basis_layout);\n\n  Tensor mXformedActGather = make_tensor(make_gmem_ptr(activations), xformed_act_composed_layout);\n  Tensor mFilter = make_tensor(make_gmem_ptr(filter), filter_layout);\n  Tensor mOutputScatter = make_tensor(make_gmem_ptr(output), out_composed_layout);  // (K, (N,Z,P,Q))\n\n  Tensor gOutput_mn = zipped_divide(mOutputScatter, typename AmpereUnpredicatedFprop::TilerOut{}); // ((BLK_M, BLK_N), (m', n'))\n  dim3 lauch_grid {static_cast<uint32_t>(size<1,1>(gOutput_mn)), static_cast<uint32_t>(size<1,0>(gOutput_mn)), 1};\n  constexpr size_t smem_size = sizeof(typename AmpereUnpredicatedFprop::SharedStorage);\n\n  print(\"xforemed gather layout ((N,Z,P,Q), (C,T,R,S)) = \"); print(xformed_act_composed_layout); print(\"\\n\");\n  print(\"Output  scatter layout ( K,        (N,Z,P,Q)) = \"); print(out_composed_layout);         print(\"\\n\");\n  print(\"Filter layout          ( K,        (C,T,R,S)) = \"); print(filter_layout);               print(\"\\n\");\n\n  CHECK_CUDA(cudaFuncSetAttribute(\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedActGather), decltype(mOutputScatter)>,\n    cudaFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  cudaEvent_t start, stop;\n  CHECK_CUDA(cudaEventCreate(&start));\n  CHECK_CUDA(cudaEventCreate(&stop));\n  CHECK_CUDA(cudaEventRecord(start));\n  for (int i = 0; i < num_iterations; ++i) {\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedActGather), decltype(mOutputScatter)>\n      <<<lauch_grid, AmpereUnpredicatedFprop::MaxThreadsPerBlock, smem_size>>>(\n          mFilter, mXformedActGather, mOutputScatter);\n  }\n  CHECK_CUDA(cudaEventRecord(stop));\n  CHECK_CUDA(cudaEventSynchronize(stop));\n  float milliseconds = 0;\n  cudaEventElapsedTime(&milliseconds, start, stop);\n  milliseconds /= float(num_iterations);\n\n  double tflop_count = (2 * double(size<0>(xformed_act_logical_inner)) * double(size(filter_layout))) / double(1e12);\n  double tflops = tflop_count / (double(milliseconds) / double(1e3));\n  printf(\"Conv TFLOP count = %f\\n\", tflop_count);\n  printf(\"Conv gather/scatter perf: %fms | TFLOP/s = %f\\n\", milliseconds, tflops);\n\n  return 0;\n}\n\nint\nmain(int argc, char const** argv) {\n  cutlass::CommandLine cmd(argc, argv);\n  std::cout << \"Ampere convolution forward propagation kernel supporting both affine and gather/scatter tensors.\\n\\n\";\n  if (cmd.check_cmd_line_flag(\"help\")) {\n    std::cout\n      << \"Options:\\n\"\n         \"\\t--n=<int>    Sets the number of images for the input activation tensor (dataset size). Default = 131072.\\n\"\n         \"\\t--i=<int>    Sets the benchmarking repetitions. Default = 128.\\n\"\n         \"\\t--nocheck    If specified, skips the reference check for dense kernel.\\n\"\n         \"\\t--help       Displays this help message and exits.\\n\";\n    return 0;\n  }\n\n\n  cudaDeviceProp props;\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n  if (props.major < 8) {\n    std::cerr << \"This example requires an Ampere GPU or newer.\\n\";\n    return 0;\n  }\n\n  int num_images = 4320;\n  cmd.get_cmd_line_argument(\"n\", num_images, 4320);\n  int num_iterations = 128;\n  cmd.get_cmd_line_argument(\"i\", num_iterations, 128);\n  bool do_host_ref_check = not cmd.check_cmd_line_flag(\"no-check\");\n\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n\n  auto activation_layout = make_layout(\n    make_shape (make_shape (num_images,     D,   H, W), make_shape ( C,   _1{},_1{},_1{})),\n    make_stride(make_stride(   D*H*W*C, H*W*C, W*C, C), make_stride(_1{}, _0{},_0{},_0{})));\n\n  auto filter_layout = typename AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape(num_images,   Z,   P,   Q)),\n    make_step (_0{}, make_step (      _4{},_3{},_2{},_1{})));\n\n  print(\"Filter layout     ( K,        (C,T,R,S)) = \"); print(filter_layout);     print(\"\\n\");\n  print(\"Activation layout ((N,D,H,W), (C,1,1,1)) = \"); print(activation_layout); print(\"\\n\");\n  print(\"Output layout     ( K,        (N,Z,P,Q)) = \"); print(output_layout);     print(\"\\n\");\n\n  // allocate tensors\n  std::cout << \"Allocating tensors ... \";\n  thrust::universal_vector<float> activation_data(size_t(cute::size(activation_layout)), float(0));\n  thrust::universal_vector<float> filter_data(size_t(cute::size(filter_layout)), float(0));\n  thrust::universal_vector<float> output_data(size_t(cute::size(output_layout)), float(0));\n  thrust::universal_vector<float> output_data_ref(size_t(cute::size(output_layout)), float(0));\n  std::cout << \"done.\\n\";\n\n  // init tensors\n  std::cout << \"Initializing data ... \" << std::flush;\n  std::random_device rd;\n  std::mt19937 gen(rd());\n  std::uniform_real_distribution<float> uniform_dist(-1.0, 1.0);\n  for (std::size_t i = 0; i < size_t(cute::size(activation_layout)); ++i) {\n    activation_data[i] = uniform_dist(gen);\n  }\n\n  for (std::size_t i = 0; i < size_t(cute::size(filter_layout)); ++i) {\n    filter_data[i] = uniform_dist(gen);\n  }\n  std::cout << \"done.\\n\";\n\n  // set up index buffers for gather/scatter, fill with indireciton indices in reversed order\n  std::cout << \"Initializing gather/scatter index buffers ... \";\n  thrust::universal_vector<uint32_t> gather_idx_buf(size_t(size<0>(activation_layout)));\n  thrust::universal_vector<uint32_t> scatter_idx_buf(size_t(size<1>(output_layout)));\n  thrust::sequence(gather_idx_buf.rbegin(), gather_idx_buf.rend());\n  thrust::sequence(scatter_idx_buf.rbegin(), scatter_idx_buf.rend());\n  std::cout << \"done.\\n\";\n\n  // launch dense\n  std::cout << \"\\nRunning dense fprop kernel\\n\";\n  int passed = ampere_dense_conv_fprop(\n    num_images,\n    activation_data.data().get(),\n    filter_data.data().get(),\n    output_data.data().get(),\n    output_data_ref.data().get(),\n    num_iterations,\n    do_host_ref_check);\n\n  // launch gather/scatter\n  std::cout << \"\\nRunning gather/scatter fprop kernel\\n\";\n  ampere_gather_scatter_conv_fprop(\n    num_images,\n    activation_data.data().get(),\n    gather_idx_buf.data().get(),\n    filter_data.data().get(),\n    output_data.data().get(),\n    scatter_idx_buf.data().get(),\n    num_iterations);\n\n  return passed;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n  \\brief Example demonstrating CuTe and CUTLASS 3.x based Ampere convolution forward propagation kernel\n      capable of operating on both affine and gather/scatter tensors.\n\n  This example demonstartes a few super cool features of CUTLASS and CuTe. It shows off\n  1. A dense conv 3D fprop kernel written as a single file ...\n  2. ... that leverages off the shelf CUTLASS collectives to show how custom kernels can use collectives ...\n  3. ... and uses the exact same templated kernel to also stamp out a gather/scatter 3D fprop conv ...\n  4. ... while getting near peak performance of the Ampere class tensor core on Ampere and Ada GPUs ...\n  5. ... by using static cute shapes and strides in case problem shapes are known at compile time.\n\n  Full documentation for this example can be found within the README.md file in this directory.\n\n  Example executions:\n  ./59_ampere_gather_scatter_conv\n  ./59_ampere_gather_scatter_conv --n=108\n  ./59_ampere_gather_scatter_conv --n=4096 --i=1\n  ./59_ampere_gather_scatter_conv --n=1080 --i=1000\n  ./59_ampere_gather_scatter_conv --n=131072 --i=1000 --no-check\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/sequence.h>\n#include <thrust/universal_vector.h>\n\n#include \"ampere_conv_kernel.h\"\n#include \"gather_tensor.hpp\"\n\n#include \"cutlass/util/command_line.h\"\n\nbool check_cuda_result(hipError_t code, const char* file, int line) {\n  if (code == hipSuccess) {\n    return true;\n  }\n\n  std::cerr << \"CUDA error at  (\" << file << \",\" << line << \")\\n\\t\" << unsigned(code) << \" -- \" << hipGetErrorString(code) << \"\\n\";\n  return false;\n}\n\n#define CHECK_CUDA(code) (check_cuda_result(code, __FILE__, __LINE__))\n\nusing namespace cute;\nusing example::IndexedGather;\nusing example::CustomStride;\n\ntemplate<class Operator, class FilterTensor, class ActivationTensor, class OutputTensor>\n__global__\n__launch_bounds__(Operator::MaxThreadsPerBlock, Operator::MinBlocksPerMultiprocessor)\nvoid kernel_entrypoint(FilterTensor mFlt, ActivationTensor mAct, OutputTensor mOut) {\n  extern __shared__ char smem_buf[];\n  Operator op;\n  op(mFlt, mAct, mOut, smem_buf);\n}\n\nint ampere_dense_conv_fprop(\n    int num_images,\n    float* activations,\n    float* filter,\n    float* output,\n    float* output_ref,\n    int num_iterations = 1,\n    bool do_ref_check = true) {\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n  auto S = typename AmpereUnpredicatedFprop::S{};\n  auto R = typename AmpereUnpredicatedFprop::R{};\n  auto T = typename AmpereUnpredicatedFprop::T{};\n\n  int N = num_images; // dynamic\n  if (num_images % int(typename AmpereUnpredicatedFprop::Tiler_N{}) != 0) {\n    printf(\"ERROR: Input image count must be evenly divisible by CTA tiler N.\\n\");\n    return 1;\n  }\n\n  // Tensor Activation: (n,d,h,w,c)::(?,6,4,4,64):(6144,1536,384,64,1)\n  auto activation_layout = make_layout(\n    make_shape (make_shape (      N,     D,   H, W), make_shape ( C,   _1{},_1{},_1{})),\n    make_stride(make_stride(D*H*W*C, H*W*C, W*C, C), make_stride(_1{}, _0{},_0{},_0{})));\n\n  auto xformed_act_layout = make_layout(\n    make_shape (make_shape(N, Z, P, Q),       make_shape ( C,       T,   R, S)),\n    make_stride(stride<0>(activation_layout), make_stride(_1{}, H*W*C, W*C, C)));\n\n  // Tensor Filter    : (k,c,s,r,t)::(128,3,3,3,64):(1728,576,192,64,1)\n  auto filter_layout = AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  // Tensor Output    : (n,z,p,q,k)::(?,4,2,2,128):(2048,1024,512,128,1)\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape( N,   Z,   P,   Q)),\n    make_tuple(_0{}, make_tuple(_4{},_3{},_2{},_1{})));\n\n  Tensor mActivation = make_tensor(make_gmem_ptr(activations), activation_layout);\n  Tensor mXformedAct = make_tensor(make_gmem_ptr(activations), xformed_act_layout);\n  Tensor mFilter     = make_tensor(make_gmem_ptr(filter), filter_layout);\n  Tensor mOutput     = make_tensor(make_gmem_ptr(output), output_layout); // (K, (N,Z,P,Q))\n  Tensor mOutputRef  = make_tensor(make_gmem_ptr(output_ref), output_layout);\n\n  print(\"xformed act layout ((N,Z,P,Q), (C,T,R,S)) = \"); print(xformed_act_layout); print(\"\\n\");\n\n  hipEvent_t start, stop;\n  CHECK_CUDA(hipEventCreate(&start));\n  CHECK_CUDA(hipEventCreate(&stop));\n\n  constexpr size_t smem_size = sizeof(typename AmpereUnpredicatedFprop::SharedStorage);\n  Tensor gOutput_mn = zipped_divide(mOutput, typename AmpereUnpredicatedFprop::TilerOut{}); // ((BLK_M, BLK_N), (m', n'))\n  dim3 lauch_grid {static_cast<uint32_t>(size<1,1>(gOutput_mn)), static_cast<uint32_t>(size<1,0>(gOutput_mn)), 1};\n\n  CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedAct), decltype(mOutput)>),\n    hipFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  CHECK_CUDA(hipEventRecord(start));\n  for (int i = 0; i < num_iterations; ++i) {\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedAct), decltype(mOutput)>\n      <<<lauch_grid, AmpereUnpredicatedFprop::MaxThreadsPerBlock, smem_size>>>(\n        mFilter, mXformedAct, mOutput);\n  }\n  CHECK_CUDA(hipEventRecord(stop));\n  CHECK_CUDA(hipEventSynchronize(stop));\n\n  float milliseconds = 0;\n  hipEventElapsedTime(&milliseconds, start, stop);\n  milliseconds /= float(num_iterations);\n\n  double tflop_count = (2 * double(size<0>(xformed_act_layout)) * double(size(filter_layout))) / double(1e12);\n  double tflops = tflop_count / (double(milliseconds) / double(1e3));\n\n  printf(\"Conv TFLOP count = %f\\n\", tflop_count);\n  printf(\"Conv dense perf: %fms | TFLOP/s = %f\\n\", milliseconds, tflops);\n\n  if (do_ref_check) {\n    printf(\"Running host reference check ...\\n\");\n    return fprop_reference(mFilter, mXformedAct, mOutput, mOutputRef);\n  }\n  else {\n    return 0;\n  }\n}\n\nint ampere_gather_scatter_conv_fprop(\n    int num_images,\n    float* activations,\n    uint32_t *gather_idx_buf,\n    float* filter,\n    float* output,\n    uint32_t *scatter_idx_buf,\n    int num_iterations = 1) {\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n  auto S = typename AmpereUnpredicatedFprop::S{};\n  auto R = typename AmpereUnpredicatedFprop::R{};\n  auto T = typename AmpereUnpredicatedFprop::T{};\n\n  int N = num_images; // dynamic\n  if (N % int(typename AmpereUnpredicatedFprop::Tiler_N{}) != 0) {\n    printf(\"ERROR: Input image count must be evenly divisible by CTA tiler N. Got num_images = %d\\n\", N);\n    return 1;\n  }\n\n  // Tensor Filter    : (k,c,s,r,t)::(128,3,3,3,64):(1728,576,192,64,1)\n  auto filter_layout = AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  // Tensor Output    : (n,z,p,q,k)::(?,4,2,2,128):(2048,1024,512,128,1)\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape( N,   Z,   P,   Q)),\n    make_tuple(_0{}, make_tuple(_4{},_3{},_2{},_1{})));\n\n  // Input gather layout\n  // inner_layout(make_coord((nzpq), (csrt))) => (idx_buffer_idx, dense_c_idx)\n  auto EG = E<0>{};  // Gather basis     (1,0) (idx_buffer_idx) \n  auto EC = E<1>{};  // Contiguous basis (0,1) (dense_offset)    \n  auto xformed_act_logical_inner = make_layout(\n    make_shape (make_shape (       N,      Z,    P,  Q), make_shape ( C,      T,    R,  S)),\n    make_stride(make_stride(D*H*W*EG, H*W*EG, W*EG, EG), make_stride(EC, H*W*EG, W*EG, EG)));\n\n  // outer_layout(make_coord(idx_buffer_idx, dense_c_idx)) => idx\n  // IndexedGather obtains idx by applying (gmem_base_ptr + gather_idx_buf[idx_buffer_idx] + dense_offset)\n  auto xformed_act_gather_outer = make_layout(\n    make_shape(_1{},_1{}),\n    make_stride(CustomStride{IndexedGather{gather_idx_buf}, C}, _1{}));\n\n  // Compose the inner and outer layouts\n  // gather_composed(make_coord((nzpq), (csrt))) => idx\n  auto xformed_act_composed_layout = composition(\n    xformed_act_gather_outer,\n    make_arithmetic_tuple(_0{}, _0{}),\n    xformed_act_logical_inner);\n\n  // Output scatter layout\n  auto out_basis_stride = make_stride(\n    E<1>{},\n    make_stride(Z*P*Q*E<0>{}, P*Q*E<0>{}, Q*E<0>{}, _1{}*E<0>{})); // -> (crd0, crd1)\n  auto out_basis_layout = make_layout(shape(output_layout), out_basis_stride);\n  auto out_scatter_layout = make_layout(\n    make_shape(_1{},_1{}),\n    make_stride(CustomStride{IndexedGather{scatter_idx_buf}, K}, _1{}));\n  auto out_composed_layout = composition(\n    out_scatter_layout,\n    make_arithmetic_tuple(_0{},_0{}),\n    out_basis_layout);\n\n  Tensor mXformedActGather = make_tensor(make_gmem_ptr(activations), xformed_act_composed_layout);\n  Tensor mFilter = make_tensor(make_gmem_ptr(filter), filter_layout);\n  Tensor mOutputScatter = make_tensor(make_gmem_ptr(output), out_composed_layout);  // (K, (N,Z,P,Q))\n\n  Tensor gOutput_mn = zipped_divide(mOutputScatter, typename AmpereUnpredicatedFprop::TilerOut{}); // ((BLK_M, BLK_N), (m', n'))\n  dim3 lauch_grid {static_cast<uint32_t>(size<1,1>(gOutput_mn)), static_cast<uint32_t>(size<1,0>(gOutput_mn)), 1};\n  constexpr size_t smem_size = sizeof(typename AmpereUnpredicatedFprop::SharedStorage);\n\n  print(\"xforemed gather layout ((N,Z,P,Q), (C,T,R,S)) = \"); print(xformed_act_composed_layout); print(\"\\n\");\n  print(\"Output  scatter layout ( K,        (N,Z,P,Q)) = \"); print(out_composed_layout);         print(\"\\n\");\n  print(\"Filter layout          ( K,        (C,T,R,S)) = \"); print(filter_layout);               print(\"\\n\");\n\n  CHECK_CUDA(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedActGather), decltype(mOutputScatter)>),\n    hipFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  hipEvent_t start, stop;\n  CHECK_CUDA(hipEventCreate(&start));\n  CHECK_CUDA(hipEventCreate(&stop));\n  CHECK_CUDA(hipEventRecord(start));\n  for (int i = 0; i < num_iterations; ++i) {\n    kernel_entrypoint<AmpereUnpredicatedFprop, decltype(mFilter), decltype(mXformedActGather), decltype(mOutputScatter)>\n      <<<lauch_grid, AmpereUnpredicatedFprop::MaxThreadsPerBlock, smem_size>>>(\n          mFilter, mXformedActGather, mOutputScatter);\n  }\n  CHECK_CUDA(hipEventRecord(stop));\n  CHECK_CUDA(hipEventSynchronize(stop));\n  float milliseconds = 0;\n  hipEventElapsedTime(&milliseconds, start, stop);\n  milliseconds /= float(num_iterations);\n\n  double tflop_count = (2 * double(size<0>(xformed_act_logical_inner)) * double(size(filter_layout))) / double(1e12);\n  double tflops = tflop_count / (double(milliseconds) / double(1e3));\n  printf(\"Conv TFLOP count = %f\\n\", tflop_count);\n  printf(\"Conv gather/scatter perf: %fms | TFLOP/s = %f\\n\", milliseconds, tflops);\n\n  return 0;\n}\n\nint\nmain(int argc, char const** argv) {\n  cutlass::CommandLine cmd(argc, argv);\n  std::cout << \"Ampere convolution forward propagation kernel supporting both affine and gather/scatter tensors.\\n\\n\";\n  if (cmd.check_cmd_line_flag(\"help\")) {\n    std::cout\n      << \"Options:\\n\"\n         \"\\t--n=<int>    Sets the number of images for the input activation tensor (dataset size). Default = 131072.\\n\"\n         \"\\t--i=<int>    Sets the benchmarking repetitions. Default = 128.\\n\"\n         \"\\t--nocheck    If specified, skips the reference check for dense kernel.\\n\"\n         \"\\t--help       Displays this help message and exits.\\n\";\n    return 0;\n  }\n\n\n  hipDeviceProp_t props;\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n  if (props.major < 8) {\n    std::cerr << \"This example requires an Ampere GPU or newer.\\n\";\n    return 0;\n  }\n\n  int num_images = 4320;\n  cmd.get_cmd_line_argument(\"n\", num_images, 4320);\n  int num_iterations = 128;\n  cmd.get_cmd_line_argument(\"i\", num_iterations, 128);\n  bool do_host_ref_check = not cmd.check_cmd_line_flag(\"no-check\");\n\n  auto D = typename AmpereUnpredicatedFprop::D{};\n  auto H = typename AmpereUnpredicatedFprop::H{};\n  auto W = typename AmpereUnpredicatedFprop::W{};\n  auto Z = typename AmpereUnpredicatedFprop::Z{};\n  auto P = typename AmpereUnpredicatedFprop::P{};\n  auto Q = typename AmpereUnpredicatedFprop::Q{};\n  auto C = typename AmpereUnpredicatedFprop::C{};\n  auto K = typename AmpereUnpredicatedFprop::K{};\n\n  auto activation_layout = make_layout(\n    make_shape (make_shape (num_images,     D,   H, W), make_shape ( C,   _1{},_1{},_1{})),\n    make_stride(make_stride(   D*H*W*C, H*W*C, W*C, C), make_stride(_1{}, _0{},_0{},_0{})));\n\n  auto filter_layout = typename AmpereUnpredicatedFprop::GmemLayoutFlt{};\n\n  auto output_layout = make_ordered_layout(\n    make_shape( K,   make_shape(num_images,   Z,   P,   Q)),\n    make_step (_0{}, make_step (      _4{},_3{},_2{},_1{})));\n\n  print(\"Filter layout     ( K,        (C,T,R,S)) = \"); print(filter_layout);     print(\"\\n\");\n  print(\"Activation layout ((N,D,H,W), (C,1,1,1)) = \"); print(activation_layout); print(\"\\n\");\n  print(\"Output layout     ( K,        (N,Z,P,Q)) = \"); print(output_layout);     print(\"\\n\");\n\n  // allocate tensors\n  std::cout << \"Allocating tensors ... \";\n  thrust::universal_vector<float> activation_data(size_t(cute::size(activation_layout)), float(0));\n  thrust::universal_vector<float> filter_data(size_t(cute::size(filter_layout)), float(0));\n  thrust::universal_vector<float> output_data(size_t(cute::size(output_layout)), float(0));\n  thrust::universal_vector<float> output_data_ref(size_t(cute::size(output_layout)), float(0));\n  std::cout << \"done.\\n\";\n\n  // init tensors\n  std::cout << \"Initializing data ... \" << std::flush;\n  std::random_device rd;\n  std::mt19937 gen(rd());\n  std::uniform_real_distribution<float> uniform_dist(-1.0, 1.0);\n  for (std::size_t i = 0; i < size_t(cute::size(activation_layout)); ++i) {\n    activation_data[i] = uniform_dist(gen);\n  }\n\n  for (std::size_t i = 0; i < size_t(cute::size(filter_layout)); ++i) {\n    filter_data[i] = uniform_dist(gen);\n  }\n  std::cout << \"done.\\n\";\n\n  // set up index buffers for gather/scatter, fill with indireciton indices in reversed order\n  std::cout << \"Initializing gather/scatter index buffers ... \";\n  thrust::universal_vector<uint32_t> gather_idx_buf(size_t(size<0>(activation_layout)));\n  thrust::universal_vector<uint32_t> scatter_idx_buf(size_t(size<1>(output_layout)));\n  thrust::sequence(gather_idx_buf.rbegin(), gather_idx_buf.rend());\n  thrust::sequence(scatter_idx_buf.rbegin(), scatter_idx_buf.rend());\n  std::cout << \"done.\\n\";\n\n  // launch dense\n  std::cout << \"\\nRunning dense fprop kernel\\n\";\n  int passed = ampere_dense_conv_fprop(\n    num_images,\n    activation_data.data().get(),\n    filter_data.data().get(),\n    output_data.data().get(),\n    output_data_ref.data().get(),\n    num_iterations,\n    do_host_ref_check);\n\n  // launch gather/scatter\n  std::cout << \"\\nRunning gather/scatter fprop kernel\\n\";\n  ampere_gather_scatter_conv_fprop(\n    num_images,\n    activation_data.data().get(),\n    gather_idx_buf.data().get(),\n    filter_data.data().get(),\n    output_data.data().get(),\n    scatter_idx_buf.data().get(),\n    num_iterations);\n\n  return passed;\n}\n", "source": "/kaggle/working/cutlass/examples/59_ampere_gather_scatter_conv/ampere_gather_scatter_conv.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example requires NVIDIA Maxwell GPU or beyond.\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS Includes\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/functional.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/gemm/warp/mma_simt.h\"\n#include \"cutlass/epilogue/warp/fragment_iterator_simt.h\"\n#include \"cutlass/epilogue/warp/tile_iterator_simt.h\"\n\n// CUTLASS Utility Includes\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/gemm_complex.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define the overall warp-level problem shape\nint const kM = 14;\nint const kN = 27;\nint const kK = 17;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define a warp-level GEMM operator.\n//\n// This template could be part of the CUTLASS Template Library or implemented internally. This\n// wraps the matrix multiply operation and epilogue with a GEMM-like interface that can be\n// instantiated in device code.\n\nnamespace cutlass {\nnamespace gemm {\nnamespace warp {\n\ntemplate <\n  typename Shape,\n  typename ElementA,\n  typename LayoutA,\n  typename ElementB,\n  typename LayoutB,\n  typename ElementC,\n  typename LayoutC,\n  typename ElementScalar\n>\nclass GemmSimt {\npublic:\n\n\n  using Policy = cutlass::gemm::warp::MmaSimtPolicy<\n    cutlass::MatrixShape<4, 8>,\n    cutlass::layout::RowMajorInterleaved<2>,\n    cutlass::gemm::GemmShape<4, 4, 1>\n  >;\n\n  using MmaWarp = cutlass::gemm::warp::MmaSimt<\n    cutlass::gemm::GemmShape<16, 32, 8>,\n    float,\n    cutlass::layout::RowMajor,\n    float,\n    cutlass::layout::ColumnMajor,\n    float,\n    cutlass::layout::RowMajor,\n    Policy\n  >;\n\n  // Number of 'K groups'\n  int const kKgroups = Shape::kK;\n\n  using FragmentIterator = cutlass::epilogue::warp::FragmentIteratorSimt<\n    typename MmaWarp::Shape,\n    typename MmaWarp::ThreadMma,\n    layout::RowMajor,                // SMEM layout\n    typename MmaWarp::Policy\n  >;\n\n  using AccumulatorTileIterator = cutlass::epilogue::warp::TileIteratorSimtCanonical<\n    typename MmaWarp::Shape,\n    typename MmaWarp::ThreadMma,\n    float,                             // ElementAccumulator\n    layout::RowMajor,                  // SMEM layout\n    typename MmaWarp::Policy\n  >;\n\n  using TensorRefA = typename MmaWarp::IteratorA::TensorRef;\n  using TensorRefB = typename MmaWarp::IteratorB::TensorRef;\n  using TensorRefC = typename AccumulatorTileIterator::TensorRef;\n\npublic:\n  CUTLASS_HOST_DEVICE\n  GemmSimt() { }\n\n  CUTLASS_DEVICE\n  void operator()(\n    ElementScalar alpha, \n    TensorRefA ref_A, \n    TensorRefB ref_B, \n    ElementScalar beta,\n    TensorRefC ref_C,\n    TensorRefC ref_D,\n    int lane_id) const {\n\n    // Instantiate iterators pointing to slices of the A and B matrices in shared memory\n    typename MmaWarp::IteratorA iter_A(ref_A, {Shape::kM, Shape::kK}, lane_id);\n    typename MmaWarp::IteratorB iter_B(ref_B, {Shape::kK, Shape::kN}, lane_id);\n\n    // Instantiate and clear accumulator tile holding the C matrix\n    typename MmaWarp::FragmentC accum;\n    accum.clear();\n  \n    // Instantiate the warp-level matrix multiply operator\n    MmaWarp mma_op;\n\n    // Instantiate fragments holding the slice of the matrix held by each warp\n    typename MmaWarp::FragmentA frag_A[2];\n    typename MmaWarp::FragmentB frag_B[2];\n      \n    // Load fragments from shared memory\n    iter_A.load(frag_A[0]);\n    iter_B.load(frag_B[0]);\n\n    ++iter_A;\n    ++iter_B;\n\n    // Load fragments from shared memory\n    CUTLASS_PRAGMA_UNROLL\n    for (int k = 0; k < kKgroups; ++k) {\n\n      // Load fragments from shared memory\n      iter_A.load(frag_A[(k + 1) % 2]);\n      iter_B.load(frag_B[(k + 1) % 2]);\n\n      ++iter_A;\n      ++iter_B;\n\n      // Compute the matrix multiply\n      mma_op(accum, frag_A[k % 2], frag_B[k % 2], accum);\n    }\n  \n    // Instantiate iterators\n    FragmentIterator accum_frag_it(accum);\n    AccumulatorTileIterator source_tile_it(ref_C, {Shape::kM, Shape::kN}, lane_id);\n    AccumulatorTileIterator dest_tile_it(ref_D, {Shape::kM, Shape::kN}, lane_id);\n\n    // Define function objects for linear scaling operation\n    cutlass::multiplies<typename FragmentIterator::Fragment> mul_source;\n    cutlass::multiply_add<typename FragmentIterator::Fragment> mul_add_accumulator;\n\n    // Iterate over the epilogue components\n    CUTLASS_PRAGMA_UNROLL\n    for (int idx = 0; idx < FragmentIterator::kIterations; ++idx) {\n\n      // Define storage for slices of the accumulators\n      typename FragmentIterator::Fragment accum_fragment;\n      typename FragmentIterator::Fragment source_fragment;\n\n      // Select a slice of accumulators from the accumulator tile\n      accum_frag_it.load(accum_fragment);\n      ++accum_frag_it;\n\n      // Load a corresponding slice from Shared memory\n      source_tile_it.load(source_fragment);\n      ++source_tile_it;\n\n      // Compute linear scaling - alpha * AB + beta * C\n      source_fragment = mul_source(beta, source_fragment);\n      accum_fragment = mul_add_accumulator(alpha, accum_fragment, source_fragment);\n\n      // Store the result to shared memory\n      dest_tile_it.store(accum_fragment);\n      ++dest_tile_it;\n    }\n\n  }\n\n};\n\n} // namespace warp\n} // namespace gemm\n} // namespace cutlass\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Sample kernel demonstrating a collective GEMM operation by a warp on arbitrary matrices held\n// in Shared Memory.\n__global__ void kernel(\n  float *D_gmem, \n  float alpha, \n  float const *A_gmem, \n  float const *B_gmem, \n  float beta,\n  float const *C_gmem) {\n\n  // Define several matrices in shared memory\n  __shared__ float A[kM][kK];\n  __shared__ float B[kN][kK];\n  __shared__ float C[kM][kN];\n\n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      for (int k = 0; k < kK; ++k) {\n        A[m][k] = A_gmem[m * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int n = 0; n < kN; ++n) {\n      for (int k = 0; k < kK; ++k) {\n        B[n][k] = B_gmem[n * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        C[m][n] = C_gmem[m * kN + n];\n      }\n    }\n  }\n\n  __syncthreads();\n  \n  //\n  // Instantiate a warp-level matrix multiply operator given the fundamental instruction shape (8x8x4),\n  // overall shape, data type of each operand, and layout of each operand.\n  //\n\n  using GemmSimt = cutlass::gemm::warp::GemmSimt<\n    cutlass::gemm::GemmShape<kM, kN, kK>,\n    float,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    float,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    float,                             // Data type of C elements\n    cutlass::layout::RowMajor,          // Layout of C matrix\n    float                              // Scalar type of alpha and beta\n  >;\n\n  // Instantiate the GEMM operator\n  GemmSimt gemm;\n\n  // Execute the warp-level GEMM operation\n  gemm(\n    alpha, \n    {&A[0][0], kK},\n    {&B[0][0], kK},\n    beta,\n    {&C[0][0], kN},\n    {&C[0][0], kN},\n    threadIdx.x);\n\n  __syncthreads();\n  \n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        D_gmem[m * kN + n] = C[m][n];\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint main(int argc, const char *arg[]) { \n\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> A({kM, kK});\n  cutlass::HostTensor<float, cutlass::layout::ColumnMajor> B({kK, kN});\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> C({kM, kN});\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> D({kM, kN});\n\n  uint64_t seed = 2020;\n  float max = 8;\n  float min = -8;\n\n  std::cout << \"Simt canonical GEMM problem size = (\" << cutlass::gemm::GemmShape<kM, kN, kK>() <<\")\" << std::endl;\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    A.host_view(),\n    seed,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    B.host_view(),\n    seed + 17,\n    max,\n    min,\n    0\n  );\n\n#if 0 // Debug: fill A sequentially and B as Identity matrix for debugging\n  cutlass::reference::host::BlockFillSequential(\n        A.host_view().data(), A.host_view().capacity());\n\n  cutlass::reference::host::TensorFillIdentity(B.host_view());\n#endif\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    C.host_view(),\n    seed + 31,\n    max,\n    min,\n    0\n  );\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  dim3 grid(1, 1);\n  dim3 block(32, 1, 1);\n\n  float alpha = 1.0f;\n  float beta = 0.0f;\n\n  kernel<<< grid, block >>>(\n    D.device_data(),\n    alpha,\n    A.device_data(),\n    B.device_data(),\n    beta,\n    C.device_data()\n  );\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to synchronize device after kernel launch.\" << std::endl;\n    return -1;\n  }\n\n  D.sync_host();\n\n  // Compute reference on host\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> D_ref({kM, kN}, false);\n  cutlass::reference::host::TensorCopy(D_ref.host_view(), C.host_view());\n\n  cutlass::reference::host::Gemm<\n  float, cutlass::layout::RowMajor, \n  float, cutlass::layout::ColumnMajor,\n  float, cutlass::layout::RowMajor, \n  float, float> reference_gemm;\n\n  reference_gemm(\n    {kM, kN, kK},\n    alpha,\n    A.host_ref(),\n    B.host_ref(),\n    beta,\n    D_ref.host_ref(),\n    float()\n  );\n\n  // Verify reference matches computed\n  if (!cutlass::reference::host::TensorEquals(\n    D.host_view(),\n    D_ref.host_view())) {\n\n    std::cerr \n      << \"A =\\n\" << A.host_view() \n      << \"\\n\\nB = \\n\" << B.host_view() \n      << \"\\n\\nC = \" << C.host_view() \n      << \"\\n\\nRef =\\n\" << D_ref.host_view()\n      << \"\\n\\nD =\\n\" << D.host_view() << \"\\n\\n\";\n\n    std::cerr << \"Error - device results mismatch host reference.\" << std::endl;\n\n    return -1;\n  }\n\n  std::cout << \"Passed\" << std::endl;\n\n  return 0; \n\n}\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example requires NVIDIA Maxwell GPU or beyond.\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS Includes\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/functional.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/gemm/warp/mma_simt.h\"\n#include \"cutlass/epilogue/warp/fragment_iterator_simt.h\"\n#include \"cutlass/epilogue/warp/tile_iterator_simt.h\"\n\n// CUTLASS Utility Includes\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/gemm_complex.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define the overall warp-level problem shape\nint const kM = 14;\nint const kN = 27;\nint const kK = 17;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Define a warp-level GEMM operator.\n//\n// This template could be part of the CUTLASS Template Library or implemented internally. This\n// wraps the matrix multiply operation and epilogue with a GEMM-like interface that can be\n// instantiated in device code.\n\nnamespace cutlass {\nnamespace gemm {\nnamespace warp {\n\ntemplate <\n  typename Shape,\n  typename ElementA,\n  typename LayoutA,\n  typename ElementB,\n  typename LayoutB,\n  typename ElementC,\n  typename LayoutC,\n  typename ElementScalar\n>\nclass GemmSimt {\npublic:\n\n\n  using Policy = cutlass::gemm::warp::MmaSimtPolicy<\n    cutlass::MatrixShape<4, 8>,\n    cutlass::layout::RowMajorInterleaved<2>,\n    cutlass::gemm::GemmShape<4, 4, 1>\n  >;\n\n  using MmaWarp = cutlass::gemm::warp::MmaSimt<\n    cutlass::gemm::GemmShape<16, 32, 8>,\n    float,\n    cutlass::layout::RowMajor,\n    float,\n    cutlass::layout::ColumnMajor,\n    float,\n    cutlass::layout::RowMajor,\n    Policy\n  >;\n\n  // Number of 'K groups'\n  int const kKgroups = Shape::kK;\n\n  using FragmentIterator = cutlass::epilogue::warp::FragmentIteratorSimt<\n    typename MmaWarp::Shape,\n    typename MmaWarp::ThreadMma,\n    layout::RowMajor,                // SMEM layout\n    typename MmaWarp::Policy\n  >;\n\n  using AccumulatorTileIterator = cutlass::epilogue::warp::TileIteratorSimtCanonical<\n    typename MmaWarp::Shape,\n    typename MmaWarp::ThreadMma,\n    float,                             // ElementAccumulator\n    layout::RowMajor,                  // SMEM layout\n    typename MmaWarp::Policy\n  >;\n\n  using TensorRefA = typename MmaWarp::IteratorA::TensorRef;\n  using TensorRefB = typename MmaWarp::IteratorB::TensorRef;\n  using TensorRefC = typename AccumulatorTileIterator::TensorRef;\n\npublic:\n  CUTLASS_HOST_DEVICE\n  GemmSimt() { }\n\n  CUTLASS_DEVICE\n  void operator()(\n    ElementScalar alpha, \n    TensorRefA ref_A, \n    TensorRefB ref_B, \n    ElementScalar beta,\n    TensorRefC ref_C,\n    TensorRefC ref_D,\n    int lane_id) const {\n\n    // Instantiate iterators pointing to slices of the A and B matrices in shared memory\n    typename MmaWarp::IteratorA iter_A(ref_A, {Shape::kM, Shape::kK}, lane_id);\n    typename MmaWarp::IteratorB iter_B(ref_B, {Shape::kK, Shape::kN}, lane_id);\n\n    // Instantiate and clear accumulator tile holding the C matrix\n    typename MmaWarp::FragmentC accum;\n    accum.clear();\n  \n    // Instantiate the warp-level matrix multiply operator\n    MmaWarp mma_op;\n\n    // Instantiate fragments holding the slice of the matrix held by each warp\n    typename MmaWarp::FragmentA frag_A[2];\n    typename MmaWarp::FragmentB frag_B[2];\n      \n    // Load fragments from shared memory\n    iter_A.load(frag_A[0]);\n    iter_B.load(frag_B[0]);\n\n    ++iter_A;\n    ++iter_B;\n\n    // Load fragments from shared memory\n    CUTLASS_PRAGMA_UNROLL\n    for (int k = 0; k < kKgroups; ++k) {\n\n      // Load fragments from shared memory\n      iter_A.load(frag_A[(k + 1) % 2]);\n      iter_B.load(frag_B[(k + 1) % 2]);\n\n      ++iter_A;\n      ++iter_B;\n\n      // Compute the matrix multiply\n      mma_op(accum, frag_A[k % 2], frag_B[k % 2], accum);\n    }\n  \n    // Instantiate iterators\n    FragmentIterator accum_frag_it(accum);\n    AccumulatorTileIterator source_tile_it(ref_C, {Shape::kM, Shape::kN}, lane_id);\n    AccumulatorTileIterator dest_tile_it(ref_D, {Shape::kM, Shape::kN}, lane_id);\n\n    // Define function objects for linear scaling operation\n    cutlass::multiplies<typename FragmentIterator::Fragment> mul_source;\n    cutlass::multiply_add<typename FragmentIterator::Fragment> mul_add_accumulator;\n\n    // Iterate over the epilogue components\n    CUTLASS_PRAGMA_UNROLL\n    for (int idx = 0; idx < FragmentIterator::kIterations; ++idx) {\n\n      // Define storage for slices of the accumulators\n      typename FragmentIterator::Fragment accum_fragment;\n      typename FragmentIterator::Fragment source_fragment;\n\n      // Select a slice of accumulators from the accumulator tile\n      accum_frag_it.load(accum_fragment);\n      ++accum_frag_it;\n\n      // Load a corresponding slice from Shared memory\n      source_tile_it.load(source_fragment);\n      ++source_tile_it;\n\n      // Compute linear scaling - alpha * AB + beta * C\n      source_fragment = mul_source(beta, source_fragment);\n      accum_fragment = mul_add_accumulator(alpha, accum_fragment, source_fragment);\n\n      // Store the result to shared memory\n      dest_tile_it.store(accum_fragment);\n      ++dest_tile_it;\n    }\n\n  }\n\n};\n\n} // namespace warp\n} // namespace gemm\n} // namespace cutlass\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Sample kernel demonstrating a collective GEMM operation by a warp on arbitrary matrices held\n// in Shared Memory.\n__global__ void kernel(\n  float *D_gmem, \n  float alpha, \n  float const *A_gmem, \n  float const *B_gmem, \n  float beta,\n  float const *C_gmem) {\n\n  // Define several matrices in shared memory\n  __shared__ float A[kM][kK];\n  __shared__ float B[kN][kK];\n  __shared__ float C[kM][kN];\n\n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      for (int k = 0; k < kK; ++k) {\n        A[m][k] = A_gmem[m * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int n = 0; n < kN; ++n) {\n      for (int k = 0; k < kK; ++k) {\n        B[n][k] = B_gmem[n * kK + k];\n      }\n    }\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        C[m][n] = C_gmem[m * kN + n];\n      }\n    }\n  }\n\n  __syncthreads();\n  \n  //\n  // Instantiate a warp-level matrix multiply operator given the fundamental instruction shape (8x8x4),\n  // overall shape, data type of each operand, and layout of each operand.\n  //\n\n  using GemmSimt = cutlass::gemm::warp::GemmSimt<\n    cutlass::gemm::GemmShape<kM, kN, kK>,\n    float,                             // Data type of A elements\n    cutlass::layout::RowMajor,          // Layout of A matrix\n    float,                             // Data type of B elements\n    cutlass::layout::ColumnMajor,       // Layout of B matrix\n    float,                             // Data type of C elements\n    cutlass::layout::RowMajor,          // Layout of C matrix\n    float                              // Scalar type of alpha and beta\n  >;\n\n  // Instantiate the GEMM operator\n  GemmSimt gemm;\n\n  // Execute the warp-level GEMM operation\n  gemm(\n    alpha, \n    {&A[0][0], kK},\n    {&B[0][0], kK},\n    beta,\n    {&C[0][0], kN},\n    {&C[0][0], kN},\n    threadIdx.x);\n\n  __syncthreads();\n  \n  // Copy data into SMEM\n  if (threadIdx.x == 0) {\n    CUTLASS_PRAGMA_NO_UNROLL\n    for (int m = 0; m < kM; ++m) {\n      CUTLASS_PRAGMA_NO_UNROLL\n      for (int n = 0; n < kN; ++n) {\n        D_gmem[m * kN + n] = C[m][n];\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nint main(int argc, const char *arg[]) { \n\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> A({kM, kK});\n  cutlass::HostTensor<float, cutlass::layout::ColumnMajor> B({kK, kN});\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> C({kM, kN});\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> D({kM, kN});\n\n  uint64_t seed = 2020;\n  float max = 8;\n  float min = -8;\n\n  std::cout << \"Simt canonical GEMM problem size = (\" << cutlass::gemm::GemmShape<kM, kN, kK>() <<\")\" << std::endl;\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    A.host_view(),\n    seed,\n    max,\n    min,\n    0\n  );\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    B.host_view(),\n    seed + 17,\n    max,\n    min,\n    0\n  );\n\n#if 0 // Debug: fill A sequentially and B as Identity matrix for debugging\n  cutlass::reference::host::BlockFillSequential(\n        A.host_view().data(), A.host_view().capacity());\n\n  cutlass::reference::host::TensorFillIdentity(B.host_view());\n#endif\n\n  cutlass::reference::host::TensorFillRandomUniform(\n    C.host_view(),\n    seed + 31,\n    max,\n    min,\n    0\n  );\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  dim3 grid(1, 1);\n  dim3 block(32, 1, 1);\n\n  float alpha = 1.0f;\n  float beta = 0.0f;\n\n  kernel<<< grid, block >>>(\n    D.device_data(),\n    alpha,\n    A.device_data(),\n    B.device_data(),\n    beta,\n    C.device_data()\n  );\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to synchronize device after kernel launch.\" << std::endl;\n    return -1;\n  }\n\n  D.sync_host();\n\n  // Compute reference on host\n  cutlass::HostTensor<float, cutlass::layout::RowMajor> D_ref({kM, kN}, false);\n  cutlass::reference::host::TensorCopy(D_ref.host_view(), C.host_view());\n\n  cutlass::reference::host::Gemm<\n  float, cutlass::layout::RowMajor, \n  float, cutlass::layout::ColumnMajor,\n  float, cutlass::layout::RowMajor, \n  float, float> reference_gemm;\n\n  reference_gemm(\n    {kM, kN, kK},\n    alpha,\n    A.host_ref(),\n    B.host_ref(),\n    beta,\n    D_ref.host_ref(),\n    float()\n  );\n\n  // Verify reference matches computed\n  if (!cutlass::reference::host::TensorEquals(\n    D.host_view(),\n    D_ref.host_view())) {\n\n    std::cerr \n      << \"A =\\n\" << A.host_view() \n      << \"\\n\\nB = \\n\" << B.host_view() \n      << \"\\n\\nC = \" << C.host_view() \n      << \"\\n\\nRef =\\n\" << D_ref.host_view()\n      << \"\\n\\nD =\\n\" << D.host_view() << \"\\n\\n\";\n\n    std::cerr << \"Error - device results mismatch host reference.\" << std::endl;\n\n    return -1;\n  }\n\n  std::cout << \"Passed\" << std::endl;\n\n  return 0; \n\n}\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/20_simt_canonical/simt_canonical.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS TRMM kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Trmm template is instantiated in the function CutlassStrmmNN. This is kernel computes\n  the triangular matrix product (TRMM) using double-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 64x64x16 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the STRMM kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for double-precision TRMM kernel\n//\n\n// Defines cutlass::gemm::device::Trmm, the generic Trmm computation template class.\n#include \"cutlass/gemm/device/trmm.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS TRMM kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS TRMM template and launch a TRMM kernel.\ncudaError_t CutlassStrmmNN(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  // Define type definition for double-precision CUTLASS TRMM with column-major\n  // input matrices and 64x64x16 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for double-precision TRMM. Typical values are used as\n  // default template arguments.\n  //\n  // To view the full trmm device API interface, see `cutlass/gemm/device/trmm.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassTrmm = cutlass::gemm::device::Trmm<\n    double,\n    ColumnMajor,\n    cutlass::SideMode::kLeft,\n    cutlass::FillMode::kLower,\n    cutlass::DiagType::kNonUnit,\n    double,\n    ColumnMajor,\n    double,\n    ColumnMajor,\n    double,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n      double,\n      1,\n      double,\n      double,\n      cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling \n    >,\n    cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,\n    5,\n    1,\n    1,\n    false,\n    cutlass::arch::OpMultiplyAdd\n  >;\n\n  // Define a CUTLASS TRMM type\n  CutlassTrmm trmm_operator;\n\n  // Construct the CUTLASS TRMM arguments object.\n  //\n  // One of CUTLASS's design patterns is to define trmm argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Trmm and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassTrmm::Arguments args(cutlass::gemm::GemmUniversalMode::kGemm,\n                              {M, N, M}, // Trmm Problem dimensions in Left-Side Mode\n                              1, // batch_count,\n                              {alpha}, // Scalars used in the Epilogue\n                              reinterpret_cast<void const *>(A),\n                              reinterpret_cast<void const *>(B),\n                              reinterpret_cast<void *>(C), // destination matrix D (may be different memory than source C matrix)\n                              (int64_t)M*M, // Batch strides\n                              (int64_t)M*N,\n                              (int64_t)M*N,\n                              lda,\n                              ldb,\n                              ldc);\n\n  //\n  // Launch the CUTLASS TRMM kernel.\n  //\n  \n  cutlass::Status status = trmm_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS TRMM operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return cudaErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  double *matrix,\n  int ldm,\n  int rows,\n  int columns,\n  int seed = 0,\n  cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    if (fill_mode == cutlass::FillMode::kLower && i < j) return;\n    else if (fill_mode == cutlass::FillMode::kUpper && i > j) return;\n    int offset = i + j * ldm;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    double value = double(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\ncudaError_t InitializeMatrix(double *matrix, int ldm, int rows, int columns, int seed = 0,\n                             cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, ldm, rows, columns, seed, fill_mode);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\ncudaError_t AllocateMatrix(double **matrix, int ldm, int rows, int columns, int seed = 0,\n                           cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n  cudaError_t result;\n\n  size_t sizeof_matrix = sizeof(double) * ldm * columns;\n\n  // Allocate device memory.\n  result = cudaMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = cudaMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, ldm, rows, columns, seed, fill_mode);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference TRMM computation.\n__global__ void ReferenceTrmm_kernel(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < M && j < N) {\n    double accumulator = 0;\n\n    for (int k = 0; k < M; ++k) {\n      accumulator += A[i + k * lda] * B[k + j * ldb]; // Since A is in Left-Side Mode\n    }\n\n    C[i + j * ldc] = alpha * accumulator;\n  }\n}\n\n/// Reference TRMM computation.\ncudaError_t ReferenceTrmm(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (M + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceTrmm_kernel<<< grid, block >>>(M, N, alpha, A, lda, B, ldb, C, ldc);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a double-precision\n/// CUTLASS TRMM kernel.\ncudaError_t TestCutlassTrmm(int M, int N, double alpha) {\n  cudaError_t result;\n\n  //\n  // Define several matrices to be used as operands to TRMM kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = M;\n  int ldb = M;\n  int ldc = M;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(double) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  double *A;\n  double *B;\n  double *C_cutlass;\n  double *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, lda, M, M, 0, cutlass::FillMode::kLower);\n\n  if (result !=  cudaSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&B, ldb, M, N, 17);\n\n  if (result !=  cudaSuccess) {\n    cudaFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, ldc, M, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    cudaFree(B);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, ldc, M, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    cudaFree(B);\n    cudaFree(C_cutlass);\n    return result;\n  }\n\n  result = cudaMemcpy(C_reference, C_cutlass, sizeof_C, cudaMemcpyDeviceToDevice);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS TRMM.\n  //\n\n  result = CutlassStrmmNN(M, N, alpha, A, lda, B, ldb, C_cutlass, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"CUTLASS TRMM kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference TRMM\n  result = ReferenceTrmm(M, N, alpha, A, lda, B, ldb, C_reference, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Reference TRMM kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<double> host_cutlass(ldc * N, 0);\n  std::vector<double> host_reference(ldc * N, 0);\n\n  result = cudaMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy CUTLASS TRMM results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  result = cudaMemcpy(host_reference.data(), C_reference, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy Reference TRMM results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  cudaFree(C_reference);\n  cudaFree(C_cutlass);\n  cudaFree(B);\n  cudaFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return cudaErrorUnknown;\n  }\n\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_trmm example.\n//\n// usage:\n//\n//   00_basic_trmm <M> <N> <alpha> \n//\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA  Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  cudaDeviceProp props;\n\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    return 0;\n  }\n\n  //\n  // Parse the command line to obtain TRMM dimensions and scalar values.\n  //\n\n  // TRMM problem dimensions.\n  int problem[2] = { 128, 128 };\n\n  for (int i = 1; i < argc && i < 3; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  double scalars[1] = { 1 };\n\n  for (int i = 3; i < argc && i < 4; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 3];\n  }\n\n  //\n  // Run the CUTLASS TRMM test.\n  //\n\n  cudaError_t result = TestCutlassTrmm(\n    problem[0],     // TRMM M dimension\n    problem[1],     // TRMM N dimension\n    scalars[0]     // alpha\n  );\n\n  if (result == cudaSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == cudaSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS TRMM kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Trmm template is instantiated in the function CutlassStrmmNN. This is kernel computes\n  the triangular matrix product (TRMM) using double-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 64x64x16 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the STRMM kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for double-precision TRMM kernel\n//\n\n// Defines cutlass::gemm::device::Trmm, the generic Trmm computation template class.\n#include \"cutlass/gemm/device/trmm.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS TRMM kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS TRMM template and launch a TRMM kernel.\nhipError_t CutlassStrmmNN(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  // Define type definition for double-precision CUTLASS TRMM with column-major\n  // input matrices and 64x64x16 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for double-precision TRMM. Typical values are used as\n  // default template arguments.\n  //\n  // To view the full trmm device API interface, see `cutlass/gemm/device/trmm.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassTrmm = cutlass::gemm::device::Trmm<\n    double,\n    ColumnMajor,\n    cutlass::SideMode::kLeft,\n    cutlass::FillMode::kLower,\n    cutlass::DiagType::kNonUnit,\n    double,\n    ColumnMajor,\n    double,\n    ColumnMajor,\n    double,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n      double,\n      1,\n      double,\n      double,\n      cutlass::epilogue::thread::ScaleType::OnlyAlphaScaling \n    >,\n    cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,\n    5,\n    1,\n    1,\n    false,\n    cutlass::arch::OpMultiplyAdd\n  >;\n\n  // Define a CUTLASS TRMM type\n  CutlassTrmm trmm_operator;\n\n  // Construct the CUTLASS TRMM arguments object.\n  //\n  // One of CUTLASS's design patterns is to define trmm argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Trmm and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassTrmm::Arguments args(cutlass::gemm::GemmUniversalMode::kGemm,\n                              {M, N, M}, // Trmm Problem dimensions in Left-Side Mode\n                              1, // batch_count,\n                              {alpha}, // Scalars used in the Epilogue\n                              reinterpret_cast<void const *>(A),\n                              reinterpret_cast<void const *>(B),\n                              reinterpret_cast<void *>(C), // destination matrix D (may be different memory than source C matrix)\n                              (int64_t)M*M, // Batch strides\n                              (int64_t)M*N,\n                              (int64_t)M*N,\n                              lda,\n                              ldb,\n                              ldc);\n\n  //\n  // Launch the CUTLASS TRMM kernel.\n  //\n  \n  cutlass::Status status = trmm_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS TRMM operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return hipErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  double *matrix,\n  int ldm,\n  int rows,\n  int columns,\n  int seed = 0,\n  cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    if (fill_mode == cutlass::FillMode::kLower && i < j) return;\n    else if (fill_mode == cutlass::FillMode::kUpper && i > j) return;\n    int offset = i + j * ldm;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    double value = double(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\nhipError_t InitializeMatrix(double *matrix, int ldm, int rows, int columns, int seed = 0,\n                             cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, ldm, rows, columns, seed, fill_mode);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\nhipError_t AllocateMatrix(double **matrix, int ldm, int rows, int columns, int seed = 0,\n                           cutlass::FillMode fill_mode = cutlass::FillMode::kInvalid) {\n  hipError_t result;\n\n  size_t sizeof_matrix = sizeof(double) * ldm * columns;\n\n  // Allocate device memory.\n  result = hipMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = hipMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, ldm, rows, columns, seed, fill_mode);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference TRMM computation.\n__global__ void ReferenceTrmm_kernel(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < M && j < N) {\n    double accumulator = 0;\n\n    for (int k = 0; k < M; ++k) {\n      accumulator += A[i + k * lda] * B[k + j * ldb]; // Since A is in Left-Side Mode\n    }\n\n    C[i + j * ldc] = alpha * accumulator;\n  }\n}\n\n/// Reference TRMM computation.\nhipError_t ReferenceTrmm(\n  int M,\n  int N,\n  double alpha,\n  double const *A,\n  int lda,\n  double const *B,\n  int ldb,\n  double *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (M + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceTrmm_kernel<<< grid, block >>>(M, N, alpha, A, lda, B, ldb, C, ldc);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a double-precision\n/// CUTLASS TRMM kernel.\nhipError_t TestCutlassTrmm(int M, int N, double alpha) {\n  hipError_t result;\n\n  //\n  // Define several matrices to be used as operands to TRMM kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = M;\n  int ldb = M;\n  int ldc = M;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(double) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  double *A;\n  double *B;\n  double *C_cutlass;\n  double *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, lda, M, M, 0, cutlass::FillMode::kLower);\n\n  if (result !=  hipSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&B, ldb, M, N, 17);\n\n  if (result !=  hipSuccess) {\n    hipFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, ldc, M, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    hipFree(B);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, ldc, M, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    hipFree(B);\n    hipFree(C_cutlass);\n    return result;\n  }\n\n  result = hipMemcpy(C_reference, C_cutlass, sizeof_C, hipMemcpyDeviceToDevice);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS TRMM.\n  //\n\n  result = CutlassStrmmNN(M, N, alpha, A, lda, B, ldb, C_cutlass, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"CUTLASS TRMM kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference TRMM\n  result = ReferenceTrmm(M, N, alpha, A, lda, B, ldb, C_reference, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Reference TRMM kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<double> host_cutlass(ldc * N, 0);\n  std::vector<double> host_reference(ldc * N, 0);\n\n  result = hipMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy CUTLASS TRMM results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  result = hipMemcpy(host_reference.data(), C_reference, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy Reference TRMM results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  hipFree(C_reference);\n  hipFree(C_cutlass);\n  hipFree(B);\n  hipFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return hipErrorUnknown;\n  }\n\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_trmm example.\n//\n// usage:\n//\n//   00_basic_trmm <M> <N> <alpha> \n//\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA  Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  hipDeviceProp_t props;\n\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    return 0;\n  }\n\n  //\n  // Parse the command line to obtain TRMM dimensions and scalar values.\n  //\n\n  // TRMM problem dimensions.\n  int problem[2] = { 128, 128 };\n\n  for (int i = 1; i < argc && i < 3; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  double scalars[1] = { 1 };\n\n  for (int i = 3; i < argc && i < 4; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 3];\n  }\n\n  //\n  // Run the CUTLASS TRMM test.\n  //\n\n  hipError_t result = TestCutlassTrmm(\n    problem[0],     // TRMM M dimension\n    problem[1],     // TRMM N dimension\n    scalars[0]     // alpha\n  );\n\n  if (result == hipSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == hipSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/32_basic_trmm/basic_trmm.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to use the PredicatedTileIterator in CUTLASS to load data from\n  addressable memory, and then store it back into addressable memory.\n\n  TileIterator is a core concept in CUTLASS that enables efficient loading and storing of data to\n  and from addressable memory. The PredicatedTileIterator accepts a ThreadMap type, which defines\n  the mapping of threads to a \"tile\" in memory. This separation of concerns enables user-defined\n  thread mappings to be specified. \n\n  In this example, a PredicatedTileIterator is used to load elements from a tile in global memory,\n  stored in column-major layout, into a fragment and then back into global memory in the same\n  layout.\n\n  This example uses CUTLASS utilities to ease the matrix operations.\n\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS includes\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/layout/pitch_linear.h\"\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n\n//\n//  CUTLASS utility includes\n//\n\n// Defines operator<<() to write TensorView objects to std::ostream\n#include \"cutlass/util/tensor_view_io.h\"\n\n// Defines cutlass::HostTensor<>\n#include \"cutlass/util/host_tensor.h\"\n\n// Defines cutlass::reference::host::TensorFill() and\n// cutlass::reference::host::TensorFillBlockSequential()\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n#pragma warning( disable : 4503)\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define PredicatedTileIterators to load and store a M-by-K tile, in column major layout.\n\ntemplate <typename Iterator>\n__global__ void copy(\n    typename Iterator::Params dst_params,\n    typename Iterator::Element *dst_pointer,\n    typename Iterator::Params src_params,\n    typename Iterator::Element *src_pointer,\n    cutlass::Coord<2> extent) {\n\n\n    Iterator dst_iterator(dst_params, dst_pointer, extent, threadIdx.x);\n    Iterator src_iterator(src_params, src_pointer, extent, threadIdx.x);\n\n    // PredicatedTileIterator uses PitchLinear layout and therefore takes in a PitchLinearShape.\n    // The contiguous dimension can be accessed via Iterator::Shape::kContiguous and the strided\n    // dimension can be accessed via Iterator::Shape::kStrided\n    int iterations = (extent[1] + Iterator::Shape::kStrided - 1) / Iterator::Shape::kStrided;\n\n    typename Iterator::Fragment fragment;\n\n    for(size_t i = 0; i < fragment.size(); ++i) {\n      fragment[i] = 0;\n    }\n\n    src_iterator.load(fragment);\n    dst_iterator.store(fragment);\n\n\n    ++src_iterator;\n    ++dst_iterator;\n\n    for(; iterations > 1; --iterations) {\n\n      src_iterator.load(fragment);\n      dst_iterator.store(fragment);\n\n      ++src_iterator;\n      ++dst_iterator;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Initializes the source tile with sequentially increasing values and performs the copy into\n// the destination tile using two PredicatedTileIterators, one to load the data from addressable\n// memory into a fragment (regiser-backed array of elements owned by each thread) and another to \n// store the data from the fragment back into the addressable memory of the destination tile.\n\ncudaError_t TestTileIterator(int M, int K) {\n\n    // For this example, we chose a <64, 4> tile shape. The PredicatedTileIterator expects\n    // PitchLinearShape and PitchLinear layout.\n    using Shape = cutlass::layout::PitchLinearShape<64, 4>;\n    using Layout = cutlass::layout::PitchLinear;\n    using Element = int;\n    int const kThreads = 32;\n\n    // ThreadMaps define how threads are mapped to a given tile. The PitchLinearStripminedThreadMap\n    // stripmines a pitch-linear tile among a given number of threads, first along the contiguous\n    // dimension then along the strided dimension.\n    using ThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<Shape, kThreads>;\n\n    // Define the PredicatedTileIterator, using TileShape, Element, Layout, and ThreadMap types\n    using Iterator = cutlass::transform::threadblock::PredicatedTileIterator<\n        Shape, Element, Layout, 1, ThreadMap>;\n\n\n    cutlass::Coord<2> copy_extent = cutlass::make_Coord(M, K);\n    cutlass::Coord<2> alloc_extent = cutlass::make_Coord(M, K);\n\n    // Allocate source and destination tensors\n    cutlass::HostTensor<Element, Layout> src_tensor(alloc_extent);\n    cutlass::HostTensor<Element, Layout> dst_tensor(alloc_extent);\n\n    Element oob_value = Element(-1);\n\n    // Initialize destination tensor with all -1s\n    cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n    // Initialize source tensor with sequentially increasing values\n    cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n    dst_tensor.sync_device();\n    src_tensor.sync_device();\n\n    typename Iterator::Params dst_params(dst_tensor.layout());\n    typename Iterator::Params src_params(src_tensor.layout());\n\n    dim3 block(kThreads, 1);\n    dim3 grid(1, 1);\n\n    // Launch copy kernel to perform the copy\n    copy<Iterator><<< grid, block >>>(\n            dst_params,\n            dst_tensor.device_data(),\n            src_params,\n            src_tensor.device_data(),\n            copy_extent\n    );\n\n    cudaError_t result = cudaGetLastError();\n    if(result != cudaSuccess) {\n      std::cerr << \"Error - kernel failed.\" << std::endl;\n      return result;\n    }\n\n    dst_tensor.sync_host();\n\n    // Verify results\n    for(int s = 0; s < alloc_extent[1]; ++s) {\n      for(int c = 0; c < alloc_extent[0]; ++c) {\n\n          Element expected = Element(0);\n\n          if(c < copy_extent[0] && s < copy_extent[1]) {\n            expected = src_tensor.at({c, s});\n          }\n          else {\n            expected = oob_value;\n          }\n\n          Element got = dst_tensor.at({c, s});\n          bool equal = (expected == got);\n\n          if(!equal) {\n              std::cerr << \"Error - source tile differs from destination tile.\" << std::endl;\n            return cudaErrorUnknown;\n          }\n      }\n    }\n\n    return cudaSuccess;\n}\n\nint main(int argc, const char *arg[]) {\n\n    cudaError_t result = TestTileIterator(57, 35);\n\n    if(result == cudaSuccess) {\n      std::cout << \"Passed.\" << std::endl;  \n    }\n\n    // Exit\n    return result == cudaSuccess ? 0 : -1;\n}\n\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to use the PredicatedTileIterator in CUTLASS to load data from\n  addressable memory, and then store it back into addressable memory.\n\n  TileIterator is a core concept in CUTLASS that enables efficient loading and storing of data to\n  and from addressable memory. The PredicatedTileIterator accepts a ThreadMap type, which defines\n  the mapping of threads to a \"tile\" in memory. This separation of concerns enables user-defined\n  thread mappings to be specified. \n\n  In this example, a PredicatedTileIterator is used to load elements from a tile in global memory,\n  stored in column-major layout, into a fragment and then back into global memory in the same\n  layout.\n\n  This example uses CUTLASS utilities to ease the matrix operations.\n\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// CUTLASS includes\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/layout/pitch_linear.h\"\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n\n//\n//  CUTLASS utility includes\n//\n\n// Defines operator<<() to write TensorView objects to std::ostream\n#include \"cutlass/util/tensor_view_io.h\"\n\n// Defines cutlass::HostTensor<>\n#include \"cutlass/util/host_tensor.h\"\n\n// Defines cutlass::reference::host::TensorFill() and\n// cutlass::reference::host::TensorFillBlockSequential()\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n#pragma warning( disable : 4503)\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define PredicatedTileIterators to load and store a M-by-K tile, in column major layout.\n\ntemplate <typename Iterator>\n__global__ void copy(\n    typename Iterator::Params dst_params,\n    typename Iterator::Element *dst_pointer,\n    typename Iterator::Params src_params,\n    typename Iterator::Element *src_pointer,\n    cutlass::Coord<2> extent) {\n\n\n    Iterator dst_iterator(dst_params, dst_pointer, extent, threadIdx.x);\n    Iterator src_iterator(src_params, src_pointer, extent, threadIdx.x);\n\n    // PredicatedTileIterator uses PitchLinear layout and therefore takes in a PitchLinearShape.\n    // The contiguous dimension can be accessed via Iterator::Shape::kContiguous and the strided\n    // dimension can be accessed via Iterator::Shape::kStrided\n    int iterations = (extent[1] + Iterator::Shape::kStrided - 1) / Iterator::Shape::kStrided;\n\n    typename Iterator::Fragment fragment;\n\n    for(size_t i = 0; i < fragment.size(); ++i) {\n      fragment[i] = 0;\n    }\n\n    src_iterator.load(fragment);\n    dst_iterator.store(fragment);\n\n\n    ++src_iterator;\n    ++dst_iterator;\n\n    for(; iterations > 1; --iterations) {\n\n      src_iterator.load(fragment);\n      dst_iterator.store(fragment);\n\n      ++src_iterator;\n      ++dst_iterator;\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Initializes the source tile with sequentially increasing values and performs the copy into\n// the destination tile using two PredicatedTileIterators, one to load the data from addressable\n// memory into a fragment (regiser-backed array of elements owned by each thread) and another to \n// store the data from the fragment back into the addressable memory of the destination tile.\n\nhipError_t TestTileIterator(int M, int K) {\n\n    // For this example, we chose a <64, 4> tile shape. The PredicatedTileIterator expects\n    // PitchLinearShape and PitchLinear layout.\n    using Shape = cutlass::layout::PitchLinearShape<64, 4>;\n    using Layout = cutlass::layout::PitchLinear;\n    using Element = int;\n    int const kThreads = 32;\n\n    // ThreadMaps define how threads are mapped to a given tile. The PitchLinearStripminedThreadMap\n    // stripmines a pitch-linear tile among a given number of threads, first along the contiguous\n    // dimension then along the strided dimension.\n    using ThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<Shape, kThreads>;\n\n    // Define the PredicatedTileIterator, using TileShape, Element, Layout, and ThreadMap types\n    using Iterator = cutlass::transform::threadblock::PredicatedTileIterator<\n        Shape, Element, Layout, 1, ThreadMap>;\n\n\n    cutlass::Coord<2> copy_extent = cutlass::make_Coord(M, K);\n    cutlass::Coord<2> alloc_extent = cutlass::make_Coord(M, K);\n\n    // Allocate source and destination tensors\n    cutlass::HostTensor<Element, Layout> src_tensor(alloc_extent);\n    cutlass::HostTensor<Element, Layout> dst_tensor(alloc_extent);\n\n    Element oob_value = Element(-1);\n\n    // Initialize destination tensor with all -1s\n    cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n    // Initialize source tensor with sequentially increasing values\n    cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n    dst_tensor.sync_device();\n    src_tensor.sync_device();\n\n    typename Iterator::Params dst_params(dst_tensor.layout());\n    typename Iterator::Params src_params(src_tensor.layout());\n\n    dim3 block(kThreads, 1);\n    dim3 grid(1, 1);\n\n    // Launch copy kernel to perform the copy\n    copy<Iterator><<< grid, block >>>(\n            dst_params,\n            dst_tensor.device_data(),\n            src_params,\n            src_tensor.device_data(),\n            copy_extent\n    );\n\n    hipError_t result = hipGetLastError();\n    if(result != hipSuccess) {\n      std::cerr << \"Error - kernel failed.\" << std::endl;\n      return result;\n    }\n\n    dst_tensor.sync_host();\n\n    // Verify results\n    for(int s = 0; s < alloc_extent[1]; ++s) {\n      for(int c = 0; c < alloc_extent[0]; ++c) {\n\n          Element expected = Element(0);\n\n          if(c < copy_extent[0] && s < copy_extent[1]) {\n            expected = src_tensor.at({c, s});\n          }\n          else {\n            expected = oob_value;\n          }\n\n          Element got = dst_tensor.at({c, s});\n          bool equal = (expected == got);\n\n          if(!equal) {\n              std::cerr << \"Error - source tile differs from destination tile.\" << std::endl;\n            return hipErrorUnknown;\n          }\n      }\n    }\n\n    return hipSuccess;\n}\n\nint main(int argc, const char *arg[]) {\n\n    hipError_t result = TestTileIterator(57, 35);\n\n    if(result == hipSuccess) {\n      std::cout << \"Passed.\" << std::endl;  \n    }\n\n    // Exit\n    return result == hipSuccess ? 0 : -1;\n}\n\n", "source": "/kaggle/working/cutlass/examples/04_tile_iterator/tile_iterator.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledCopy\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K)\n  Tensor tArA = make_fragment_like(tAsA);                              // (CPY,CPY_M,CPY_K)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K)\n  Tensor tBrB = make_fragment_like(tBsB);                              // (CPY,CPY_N,CPY_K)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tArA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tArA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBrB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBrB));                // CPY_K\n\n  // Copy gmem to rmem for k_tile=0\n  copy(copy_a, tAgA(_,_,_,0), tArA);\n  copy(copy_b, tBgB(_,_,_,0), tBrB);\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledMMA\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V(  shape(tCrA) ==   shape(tCsA));                // (MMA,MMA_M,MMA_K)\n  CUTE_STATIC_ASSERT_V(  shape(tCrB) ==   shape(tCsB));                // (MMA,MMA_N,MMA_K)\n  CUTE_STATIC_ASSERT_V(  shape(tCrC) ==   shape(tCgC));                // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V(size<1>(tCgC) == size<1>(tCsA));                // MMA_M\n  CUTE_STATIC_ASSERT_V(size<2>(tCgC) == size<1>(tCsB));                // MMA_N\n  CUTE_STATIC_ASSERT_V(size<2>(tCsA) == size<2>(tCsB));                // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n    print(\"tArA : \"); print(tArA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n    print(\"tBrB : \"); print(tBrB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Copy rmem to smem\n  copy(tArA, tAsA);\n  copy(tBrB, tBsB);\n  __syncthreads();\n\n  //\n  // PIPELINED MAIN LOOP\n  // TUTORIAL: Example of a gemm loop that pipelines shared memory AND register memory\n  //   Data is read from global to registers, then to shared via the tA|tB partitions\n  //   Data is then copied from shared to registers in multiple waves via the tC partitions\n  //     and gemm(.) operates on the current register wave\n  //\n\n  // Load A, B shmem->regs for k_block=0\n  copy(tCsA(_,_,0), tCrA(_,_,0));\n  copy(tCsB(_,_,0), tCrB(_,_,0));\n  auto K_TILE_MAX  = size<3>(tAgA);\n  auto K_BLOCK_MAX = size<2>(tCrA);\n\n  CUTE_NO_UNROLL\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Pipeline the k-mode of the block registers\n    CUTE_UNROLL\n    for (int k_block = 0; k_block < K_BLOCK_MAX; ++k_block)\n    {\n      if (k_block == K_BLOCK_MAX - 1)\n      {\n        // Copy rmem to smem\n        __syncthreads();\n        copy(tArA, tAsA);\n        copy(tBrB, tBsB);\n        __syncthreads();\n      }\n\n      // Copy smem to rmem for k_block+1\n      int k_block_next = (k_block + 1) % K_BLOCK_MAX;\n      copy(tCsA(_,_,k_block_next), tCrA(_,_,k_block_next));\n      copy(tCsB(_,_,k_block_next), tCrB(_,_,k_block_next));\n      if (k_block == 0)\n      {\n        // Copy gmem to rmem for k_tile+1\n        int k_tile_next = (k_tile + 1 < K_TILE_MAX) ? k_tile + 1 : k_tile;\n        copy(copy_a, tAgA(_,_,_,k_tile_next), tArA);\n        copy(copy_b, tBgB(_,_,_,k_tile_next), tBrB);\n      }\n      // Thread-level register gemm for k_block\n      gemm(mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCrC);\n    } // k_block\n  } // k_tile\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 n-major\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape (      bM,          bK),\n                        make_stride(Int<1>{}, bM+Int<1>{}));        // (m,k) -> smem_idx; padded m-major\n  auto sB = make_layout(make_shape (      bN,          bK),\n                        make_stride(Int<1>{}, bN+Int<1>{}));        // (n,k) -> smem_idx; padded n-major\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major < 7) {\n    std::cout << \"This example requires an Volta GPU or newer (CC >= 70)\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledCopy\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K)\n  Tensor tArA = make_fragment_like(tAsA);                              // (CPY,CPY_M,CPY_K)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K)\n  Tensor tBrB = make_fragment_like(tBsB);                              // (CPY,CPY_N,CPY_K)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tArA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tArA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBrB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBrB));                // CPY_K\n\n  // Copy gmem to rmem for k_tile=0\n  copy(copy_a, tAgA(_,_,_,0), tArA);\n  copy(copy_b, tBgB(_,_,_,0), tBrB);\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledMMA\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V(  shape(tCrA) ==   shape(tCsA));                // (MMA,MMA_M,MMA_K)\n  CUTE_STATIC_ASSERT_V(  shape(tCrB) ==   shape(tCsB));                // (MMA,MMA_N,MMA_K)\n  CUTE_STATIC_ASSERT_V(  shape(tCrC) ==   shape(tCgC));                // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V(size<1>(tCgC) == size<1>(tCsA));                // MMA_M\n  CUTE_STATIC_ASSERT_V(size<2>(tCgC) == size<1>(tCsB));                // MMA_N\n  CUTE_STATIC_ASSERT_V(size<2>(tCsA) == size<2>(tCsB));                // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n    print(\"tArA : \"); print(tArA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n    print(\"tBrB : \"); print(tBrB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Copy rmem to smem\n  copy(tArA, tAsA);\n  copy(tBrB, tBsB);\n  __syncthreads();\n\n  //\n  // PIPELINED MAIN LOOP\n  // TUTORIAL: Example of a gemm loop that pipelines shared memory AND register memory\n  //   Data is read from global to registers, then to shared via the tA|tB partitions\n  //   Data is then copied from shared to registers in multiple waves via the tC partitions\n  //     and gemm(.) operates on the current register wave\n  //\n\n  // Load A, B shmem->regs for k_block=0\n  copy(tCsA(_,_,0), tCrA(_,_,0));\n  copy(tCsB(_,_,0), tCrB(_,_,0));\n  auto K_TILE_MAX  = size<3>(tAgA);\n  auto K_BLOCK_MAX = size<2>(tCrA);\n\n  CUTE_NO_UNROLL\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Pipeline the k-mode of the block registers\n    CUTE_UNROLL\n    for (int k_block = 0; k_block < K_BLOCK_MAX; ++k_block)\n    {\n      if (k_block == K_BLOCK_MAX - 1)\n      {\n        // Copy rmem to smem\n        __syncthreads();\n        copy(tArA, tAsA);\n        copy(tBrB, tBsB);\n        __syncthreads();\n      }\n\n      // Copy smem to rmem for k_block+1\n      int k_block_next = (k_block + 1) % K_BLOCK_MAX;\n      copy(tCsA(_,_,k_block_next), tCrA(_,_,k_block_next));\n      copy(tCsB(_,_,k_block_next), tCrB(_,_,k_block_next));\n      if (k_block == 0)\n      {\n        // Copy gmem to rmem for k_tile+1\n        int k_tile_next = (k_tile + 1 < K_TILE_MAX) ? k_tile + 1 : k_tile;\n        copy(copy_a, tAgA(_,_,_,k_tile_next), tArA);\n        copy(copy_b, tBgB(_,_,_,k_tile_next), tBrB);\n      }\n      // Thread-level register gemm for k_block\n      gemm(mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCrC);\n    } // k_block\n  } // k_tile\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 n-major\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape (      bM,          bK),\n                        make_stride(Int<1>{}, bM+Int<1>{}));        // (m,k) -> smem_idx; padded m-major\n  auto sB = make_layout(make_shape (      bN,          bK),\n                        make_stride(Int<1>{}, bN+Int<1>{}));        // (n,k) -> smem_idx; padded n-major\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major < 7) {\n    std::cout << \"This example requires an Volta GPU or newer (CC >= 70)\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/sgemm_sm70.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledCopy\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K)\n  // Allocate registers same shape/layout as partitioned data\n  Tensor tArA = make_fragment_like(tAsA);                              // (CPY,CPY_M,CPY_K)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K)\n  // Allocate registers same shape/layout as partitioned data\n  Tensor tBrB = make_fragment_like(tBsB);                              // (CPY,CPY_N,CPY_K)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tArA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tArA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBrB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBrB));                // CPY_K\n\n  // Copy gmem to rmem for k_tile=0\n  copy(copy_a, tAgA(_,_,_,0), tArA);\n  copy(copy_b, tBgB(_,_,_,0), tBrB);\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledMMA\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V(  shape(tCrC) ==   shape(tCgC));                // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V(size<1>(tCgC) == size<1>(tCsA));                // MMA_M\n  CUTE_STATIC_ASSERT_V(size<2>(tCgC) == size<1>(tCsB));                // MMA_N\n  CUTE_STATIC_ASSERT_V(size<2>(tCsA) == size<2>(tCsB));                // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n    print(\"tArA : \"); print(tArA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n    print(\"tBrB : \"); print(tBrB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // TUTORIAL: Example of an inner loop that pipelines compute with reads\n  //           from global memory by staging through register and shared memory.\n  //   Data is read from global to registers, then to shared via the TiledCopy partitions\n  //   gemm(.) operates on the shared memory directly via the TiledMMA partitions\n\n  auto K_TILE_MAX = size<3>(tAgA);\n\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Copy rmem to smem with tA|tB thread-partitioned tensors\n    __syncthreads();         // Wait for all threads to consume smem\n    copy(tArA, tAsA);\n    copy(tBrB, tBsB);\n    __syncthreads();         // Wait for all threads to consume smem\n\n    // Copy gmem to rmem for k_tile+1 with tA|tB thread-partitioned tensors\n    int k_tile_next = (k_tile + 1 < K_TILE_MAX) ? k_tile + 1 : k_tile;\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tArA);\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBrB);\n    // TUTORIAL: The above call to copy(copy_a, tAgA(_,_,_,k_tile_next), tArA) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       copy_a.call(tAgA(_,m,k), tArA(_,m,k);\n    //     }\n    //   }\n\n    // Compute gemm on mma-partitioned smem\n    gemm(mma, tCsA, tCsB, tCrC);\n    // TUTORIAL: The above call to gemm(tCsA, tCsB, tCrC) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       CUTE_UNROLL\n    //       for (int n = 0; n < size<1>(tCrC); ++n) {\n    //         mma.call(tCsA(_,m,k), tCsB(_,n,k), tCrC(_,m,n);\n    //       }\n    //     }\n    //   }\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n\n  // TUTORIAL: Construct TiledCopy with a particular Copy_Atom to use and\n  //           define the partitioning pattern to apply.\n  // Each thread will (try to) copy 4x1 elements of type TA using 128-bit copy.\n  // Use 32x8 of these threads.\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 n-major\n\n  // TUTORIAL: Construct TiledMMA with a particular MMA_Atom to use and\n  //           define the partitioning pattern to apply.\n  // Use a 1x1x1 FMA on the types TC += TA * TB. Each atom requires a single thread.\n  // Reproduce that atom 16x16x1 times (m-major) across threads so that we use 256 threads.\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 UniversalFMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape (      bM,          bK),\n                        make_stride(Int<1>{}, bM+Int<1>{}));        // (m,k) -> smem_idx; padded m-major\n  auto sB = make_layout(make_shape (      bN,          bK),\n                        make_stride(Int<1>{}, bN+Int<1>{}));        // (n,k) -> smem_idx; padded n-major\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // TUTORIAL: Construct TiledCopy to define the Copy_Atom to use and the\n  //           partitioning pattern to apply.\n  // Each thread will copy 1x1 elements of type TA.\n  // Use 32x8 of these threads arranged in k-major.\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  // TUTORIAL: Construct TiledMMA to define the MMA_Atom to use and the\n  //           partitioning pattern to apply.\n  // Use a 1x1x1 FMA on the types TC += TA * TB. Each atom requires a single thread.\n  // Reproduce that atom 16x16x1 times (m-major) across threads so that we use 256 threads.\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  cute::device_init(0);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledCopy\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K)\n  // Allocate registers same shape/layout as partitioned data\n  Tensor tArA = make_fragment_like(tAsA);                              // (CPY,CPY_M,CPY_K)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K)\n  // Allocate registers same shape/layout as partitioned data\n  Tensor tBrB = make_fragment_like(tBsB);                              // (CPY,CPY_N,CPY_K)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tArA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tArA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBrB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBrB));                // CPY_K\n\n  // Copy gmem to rmem for k_tile=0\n  copy(copy_a, tAgA(_,_,_,0), tArA);\n  copy(copy_b, tBgB(_,_,_,0), tBrB);\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via a TiledMMA\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V(  shape(tCrC) ==   shape(tCgC));                // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V(size<1>(tCgC) == size<1>(tCsA));                // MMA_M\n  CUTE_STATIC_ASSERT_V(size<2>(tCgC) == size<1>(tCsB));                // MMA_N\n  CUTE_STATIC_ASSERT_V(size<2>(tCsA) == size<2>(tCsB));                // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n    print(\"tArA : \"); print(tArA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n    print(\"tBrB : \"); print(tBrB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // TUTORIAL: Example of an inner loop that pipelines compute with reads\n  //           from global memory by staging through register and shared memory.\n  //   Data is read from global to registers, then to shared via the TiledCopy partitions\n  //   gemm(.) operates on the shared memory directly via the TiledMMA partitions\n\n  auto K_TILE_MAX = size<3>(tAgA);\n\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Copy rmem to smem with tA|tB thread-partitioned tensors\n    __syncthreads();         // Wait for all threads to consume smem\n    copy(tArA, tAsA);\n    copy(tBrB, tBsB);\n    __syncthreads();         // Wait for all threads to consume smem\n\n    // Copy gmem to rmem for k_tile+1 with tA|tB thread-partitioned tensors\n    int k_tile_next = (k_tile + 1 < K_TILE_MAX) ? k_tile + 1 : k_tile;\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tArA);\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBrB);\n    // TUTORIAL: The above call to copy(copy_a, tAgA(_,_,_,k_tile_next), tArA) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       copy_a.call(tAgA(_,m,k), tArA(_,m,k);\n    //     }\n    //   }\n\n    // Compute gemm on mma-partitioned smem\n    gemm(mma, tCsA, tCsB, tCrC);\n    // TUTORIAL: The above call to gemm(tCsA, tCsB, tCrC) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       CUTE_UNROLL\n    //       for (int n = 0; n < size<1>(tCrC); ++n) {\n    //         mma.call(tCsA(_,m,k), tCsB(_,n,k), tCrC(_,m,n);\n    //       }\n    //     }\n    //   }\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n\n  // TUTORIAL: Construct TiledCopy with a particular Copy_Atom to use and\n  //           define the partitioning pattern to apply.\n  // Each thread will (try to) copy 4x1 elements of type TA using 128-bit copy.\n  // Use 32x8 of these threads.\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{},  // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{}); // Val layout  4x1 n-major\n\n  // TUTORIAL: Construct TiledMMA with a particular MMA_Atom to use and\n  //           define the partitioning pattern to apply.\n  // Use a 1x1x1 FMA on the types TC += TA * TB. Each atom requires a single thread.\n  // Reproduce that atom 16x16x1 times (m-major) across threads so that we use 256 threads.\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 UniversalFMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape (      bM,          bK),\n                        make_stride(Int<1>{}, bM+Int<1>{}));        // (m,k) -> smem_idx; padded m-major\n  auto sB = make_layout(make_shape (      bN,          bK),\n                        make_stride(Int<1>{}, bN+Int<1>{}));        // (n,k) -> smem_idx; padded n-major\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // TUTORIAL: Construct TiledCopy to define the Copy_Atom to use and the\n  //           partitioning pattern to apply.\n  // Each thread will copy 1x1 elements of type TA.\n  // Use 32x8 of these threads arranged in k-major.\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<UniversalCopy<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<UniversalCopy<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  // TUTORIAL: Construct TiledMMA to define the MMA_Atom to use and the\n  //           partitioning pattern to apply.\n  // Use a 1x1x1 FMA on the types TC += TA * TB. Each atom requires a single thread.\n  // Reproduce that atom 16x16x1 times (m-major) across threads so that we use 256 threads.\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA,\n       B, dB, sB, copyB,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  cute::device_init(0);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/sgemm_2.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class AThreadLayout,\n          class TB, class BStride, class BSmemLayout, class BThreadLayout,\n          class TC, class CStride, class CSmemLayout, class CThreadLayout,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(CThreadLayout{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, AThreadLayout tA,\n            TB const* B, BStride dB, BSmemLayout sB_layout, BThreadLayout tB,\n            TC      * C, CStride dC, CSmemLayout          , CThreadLayout tC,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  static_assert(is_static<AThreadLayout>::value);\n  static_assert(is_static<BThreadLayout>::value);\n  static_assert(is_static<CThreadLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size(tA) == size(tB));                          // NumThreads\n  CUTE_STATIC_ASSERT_V(size(tC) == size(tA));                          // NumThreads\n\n  CUTE_STATIC_ASSERT_V(size<0>(cta_tiler) % size<0>(tA) == Int<0>{});  // BLK_M / THR_M\n  CUTE_STATIC_ASSERT_V(size<2>(cta_tiler) % size<1>(tA) == Int<0>{});  // BLK_K / THR_K\n  CUTE_STATIC_ASSERT_V(size<1>(cta_tiler) % size<0>(tB) == Int<0>{});  // BLK_N / THR_N\n  CUTE_STATIC_ASSERT_V(size<2>(cta_tiler) % size<1>(tB) == Int<0>{});  // BLK_K / THR_K\n  CUTE_STATIC_ASSERT_V(size<0>(cta_tiler) % size<0>(tC) == Int<0>{});  // BLK_M / THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(cta_tiler) % size<1>(tC) == Int<0>{});  // BLK_N / THR_N\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of simple raked partitioning of ThreadLayouts tA|tB over data A|B tiles\n\n  Tensor tAgA = local_partition(gA, tA, threadIdx.x);                  // (THR_M,THR_K,k)\n  Tensor tAsA = local_partition(sA, tA, threadIdx.x);                  // (THR_M,THR_K)\n\n  Tensor tBgB = local_partition(gB, tB, threadIdx.x);                  // (THR_N,THR_K,k)\n  Tensor tBsB = local_partition(sB, tB, threadIdx.x);                  // (THR_N,THR_K)\n\n  CUTE_STATIC_ASSERT_V(size<0>(tAgA) == size<0>(tAsA));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // THR_K\n  CUTE_STATIC_ASSERT_V(size<0>(tBgB) == size<0>(tBsB));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // THR_K\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via projections of a ThreadLayout tC\n\n  // Partition sA (BLK_M, BLK_K) by the rows of tC\n  Tensor tCsA = local_partition(sA, tC, threadIdx.x, Step<_1, X>{});   // (THR_M,BLK_K)\n  // Partition sB (BLK_N, BLK_K) by the cols of tC\n  Tensor tCsB = local_partition(sB, tC, threadIdx.x, Step< X,_1>{});   // (THR_N,BLK_K)\n  // Partition gC (M,N) by the tile of tC\n  Tensor tCgC = local_partition(gC, tC, threadIdx.x, Step<_1,_1>{});   // (THR_M,THR_N)\n\n  // Allocate the accumulators -- same shape/layout as the partitioned data\n  Tensor tCrC = make_tensor_like(tCgC);                                // (THR_M,THR_N)\n\n  CUTE_STATIC_ASSERT_V(size<0>(tCrC) == size<0>(tCgC));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<0>(tCrC) == size<0>(tCsA));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(tCrC) == size<1>(tCgC));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tCrC) == size<0>(tCsB));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tCsA) == size<1>(tCsB));                // BLK_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // TUTORIAL: Example of a simple mainloop that read tiles of data into shared memory,\n  //           and then computes on those tiles.\n  //   copy(.) operates on the global and shared memory via the tA|tB partitioning\n  //   gemm(.) operates on the shared and register memory via the tC partitioning\n\n  auto K_TILE_MAX = size<2>(tAgA);\n\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Copy gmem to smem with tA|tB thread-partitioned tensors\n    copy(tAgA(_,_,k_tile), tAsA);      // A   (THR_M,THR_K) -> (THR_M,THR_K)\n    copy(tBgB(_,_,k_tile), tBsB);      // B   (THR_N,THR_K) -> (THR_N,THR_K)\n\n    // TUTORIAL: The above call to copy(tAgA(_,_,k_tile), tAsA) is equivalent to\n    //   Tensor tAgAk = tAgA(_,_,k_tile);\n    //   CUTE_UNROLL\n    //   for (int i = 0; i < size(tAsA); ++i) {\n    //     tAsA(i) = tAgAk(i);\n    //   }\n\n    cp_async_fence();        // Label the end of (potential) cp.async instructions\n    cp_async_wait<0>();      // Sync on all (potential) cp.async instructions\n    __syncthreads();         // Wait for all threads to write to smem\n\n    // Compute gemm on tC thread-partitioned smem\n    gemm(tCsA, tCsB, tCrC);            // (THR_M,THR_N) += (THR_M,BLK_K) * (THR_N,BLK_K)\n\n    // TUTORIAL: The above call to gemm(tCsA, tCsB, tCrC) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       CUTE_UNROLL\n    //       for (int n = 0; n < size<1>(tCrC); ++n) {\n    //         tCrC(m,n) += tCsA(m,k) * tCsB(n,k);\n    //       }\n    //     }\n    //   }\n\n    __syncthreads();         // Wait for all threads to read from smem\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n\n  // TUTORIAL: The above call to axpby(alpha, tCrC, beta, tCgC) is equivalent to\n  //   CUTE_UNROLL\n  //   for (int i = 0; i < size(tCrC); ++i) {\n  //     tCgC(i) = alpha * tCrC(i) + beta * tCgC(i);\n  //   }\n}\n\n// Setup params for an NT GEMM\n// Use m-major smem sA, n-major smem sB, and mn-major threads tA|tB\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  auto tA = make_layout(make_shape(Int<32>{}, Int< 8>{}));   // (m,k) -> thr_idx\n  auto tB = make_layout(make_shape(Int<32>{}, Int< 8>{}));   // (n,k) -> thr_idx\n  auto tC = make_layout(make_shape(Int<16>{}, Int<16>{}));   // (m,n) -> thr_idx\n\n  dim3 dimBlock(size(tC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, tA,\n       B, dB, sB, tB,\n       C, dC, sC, tC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\n// Use padded m-major smem sA, padded n-major smem sB, and k-major threads tA|tB\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM,bK), LayoutRight{});   // (m,k) -> smem_idx; k-major\n  auto sB = make_layout(make_shape(bN,bK), LayoutRight{});   // (n,k) -> smem_idx; k-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  auto tA = make_layout(make_shape(Int<32>{}, Int< 8>{}), LayoutRight{});  // (m,k) -> thr_idx; k-major\n  auto tB = make_layout(make_shape(Int<32>{}, Int< 8>{}), LayoutRight{});  // (n,k) -> thr_idx; k-major\n  auto tC = make_layout(make_shape(Int<16>{}, Int<16>{}));                 // (m,n) -> thr_idx; m-major\n\n  dim3 dimBlock(size(tC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, tA,\n       B, dB, sB, tB,\n       C, dC, sC, tC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  cute::device_init(0);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class AThreadLayout,\n          class TB, class BStride, class BSmemLayout, class BThreadLayout,\n          class TC, class CStride, class CSmemLayout, class CThreadLayout,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(CThreadLayout{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, AThreadLayout tA,\n            TB const* B, BStride dB, BSmemLayout sB_layout, BThreadLayout tB,\n            TC      * C, CStride dC, CSmemLayout          , CThreadLayout tC,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  static_assert(is_static<AThreadLayout>::value);\n  static_assert(is_static<BThreadLayout>::value);\n  static_assert(is_static<CThreadLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size(tA) == size(tB));                          // NumThreads\n  CUTE_STATIC_ASSERT_V(size(tC) == size(tA));                          // NumThreads\n\n  CUTE_STATIC_ASSERT_V(size<0>(cta_tiler) % size<0>(tA) == Int<0>{});  // BLK_M / THR_M\n  CUTE_STATIC_ASSERT_V(size<2>(cta_tiler) % size<1>(tA) == Int<0>{});  // BLK_K / THR_K\n  CUTE_STATIC_ASSERT_V(size<1>(cta_tiler) % size<0>(tB) == Int<0>{});  // BLK_N / THR_N\n  CUTE_STATIC_ASSERT_V(size<2>(cta_tiler) % size<1>(tB) == Int<0>{});  // BLK_K / THR_K\n  CUTE_STATIC_ASSERT_V(size<0>(cta_tiler) % size<0>(tC) == Int<0>{});  // BLK_M / THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(cta_tiler) % size<1>(tC) == Int<0>{});  // BLK_N / THR_N\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  __shared__ TA smemA[cosize_v<ASmemLayout>];\n  __shared__ TB smemB[cosize_v<BSmemLayout>];\n  Tensor sA = make_tensor(make_smem_ptr(smemA), sA_layout);            // (BLK_M,BLK_K)\n  Tensor sB = make_tensor(make_smem_ptr(smemB), sB_layout);            // (BLK_N,BLK_K)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  // TUTORIAL: Example of simple raked partitioning of ThreadLayouts tA|tB over data A|B tiles\n\n  Tensor tAgA = local_partition(gA, tA, threadIdx.x);                  // (THR_M,THR_K,k)\n  Tensor tAsA = local_partition(sA, tA, threadIdx.x);                  // (THR_M,THR_K)\n\n  Tensor tBgB = local_partition(gB, tB, threadIdx.x);                  // (THR_N,THR_K,k)\n  Tensor tBsB = local_partition(sB, tB, threadIdx.x);                  // (THR_N,THR_K)\n\n  CUTE_STATIC_ASSERT_V(size<0>(tAgA) == size<0>(tAsA));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // THR_K\n  CUTE_STATIC_ASSERT_V(size<0>(tBgB) == size<0>(tBsB));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // THR_K\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  // TUTORIAL: Example of partitioning via projections of a ThreadLayout tC\n\n  // Partition sA (BLK_M, BLK_K) by the rows of tC\n  Tensor tCsA = local_partition(sA, tC, threadIdx.x, Step<_1, X>{});   // (THR_M,BLK_K)\n  // Partition sB (BLK_N, BLK_K) by the cols of tC\n  Tensor tCsB = local_partition(sB, tC, threadIdx.x, Step< X,_1>{});   // (THR_N,BLK_K)\n  // Partition gC (M,N) by the tile of tC\n  Tensor tCgC = local_partition(gC, tC, threadIdx.x, Step<_1,_1>{});   // (THR_M,THR_N)\n\n  // Allocate the accumulators -- same shape/layout as the partitioned data\n  Tensor tCrC = make_tensor_like(tCgC);                                // (THR_M,THR_N)\n\n  CUTE_STATIC_ASSERT_V(size<0>(tCrC) == size<0>(tCgC));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<0>(tCrC) == size<0>(tCsA));                // THR_M\n  CUTE_STATIC_ASSERT_V(size<1>(tCrC) == size<1>(tCgC));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tCrC) == size<0>(tCsB));                // THR_N\n  CUTE_STATIC_ASSERT_V(size<1>(tCsA) == size<1>(tCsB));                // BLK_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // TUTORIAL: Example of a simple mainloop that read tiles of data into shared memory,\n  //           and then computes on those tiles.\n  //   copy(.) operates on the global and shared memory via the tA|tB partitioning\n  //   gemm(.) operates on the shared and register memory via the tC partitioning\n\n  auto K_TILE_MAX = size<2>(tAgA);\n\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    // Copy gmem to smem with tA|tB thread-partitioned tensors\n    copy(tAgA(_,_,k_tile), tAsA);      // A   (THR_M,THR_K) -> (THR_M,THR_K)\n    copy(tBgB(_,_,k_tile), tBsB);      // B   (THR_N,THR_K) -> (THR_N,THR_K)\n\n    // TUTORIAL: The above call to copy(tAgA(_,_,k_tile), tAsA) is equivalent to\n    //   Tensor tAgAk = tAgA(_,_,k_tile);\n    //   CUTE_UNROLL\n    //   for (int i = 0; i < size(tAsA); ++i) {\n    //     tAsA(i) = tAgAk(i);\n    //   }\n\n    cp_async_fence();        // Label the end of (potential) cp.async instructions\n    cp_async_wait<0>();      // Sync on all (potential) cp.async instructions\n    __syncthreads();         // Wait for all threads to write to smem\n\n    // Compute gemm on tC thread-partitioned smem\n    gemm(tCsA, tCsB, tCrC);            // (THR_M,THR_N) += (THR_M,BLK_K) * (THR_N,BLK_K)\n\n    // TUTORIAL: The above call to gemm(tCsA, tCsB, tCrC) is equivalent to\n    //   CUTE_UNROLL\n    //   for (int k = 0; k < size<1>(tCsA); ++k) {\n    //     CUTE_UNROLL\n    //     for (int m = 0; m < size<0>(tCrC); ++m) {\n    //       CUTE_UNROLL\n    //       for (int n = 0; n < size<1>(tCrC); ++n) {\n    //         tCrC(m,n) += tCsA(m,k) * tCsB(n,k);\n    //       }\n    //     }\n    //   }\n\n    __syncthreads();         // Wait for all threads to read from smem\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n\n  // TUTORIAL: The above call to axpby(alpha, tCrC, beta, tCgC) is equivalent to\n  //   CUTE_UNROLL\n  //   for (int i = 0; i < size(tCrC); ++i) {\n  //     tCgC(i) = alpha * tCrC(i) + beta * tCgC(i);\n  //   }\n}\n\n// Setup params for an NT GEMM\n// Use m-major smem sA, n-major smem sB, and mn-major threads tA|tB\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK));                 // (m,k) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK));                 // (n,k) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  auto tA = make_layout(make_shape(Int<32>{}, Int< 8>{}));   // (m,k) -> thr_idx\n  auto tB = make_layout(make_shape(Int<32>{}, Int< 8>{}));   // (n,k) -> thr_idx\n  auto tC = make_layout(make_shape(Int<16>{}, Int<16>{}));   // (m,n) -> thr_idx\n\n  dim3 dimBlock(size(tC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, tA,\n       B, dB, sB, tB,\n       C, dC, sC, tC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\n// Use padded m-major smem sA, padded n-major smem sB, and k-major threads tA|tB\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM,bK), LayoutRight{});   // (m,k) -> smem_idx; k-major\n  auto sB = make_layout(make_shape(bN,bK), LayoutRight{});   // (n,k) -> smem_idx; k-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n  auto tA = make_layout(make_shape(Int<32>{}, Int< 8>{}), LayoutRight{});  // (m,k) -> thr_idx; k-major\n  auto tB = make_layout(make_shape(Int<32>{}, Int< 8>{}), LayoutRight{});  // (n,k) -> thr_idx; k-major\n  auto tC = make_layout(make_shape(Int<16>{}, Int<16>{}));                 // (m,n) -> thr_idx; m-major\n\n  dim3 dimBlock(size(tC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, 0, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, tA,\n       B, dB, sB, tB,\n       C, dC, sC, tC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = float;\n  using TB = float;\n  using TC = float;\n  using TI = float;\n\n  TI alpha = 1.0;\n  TI beta  = 0.0;\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  cute::device_init(0);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/sgemm_1.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,\n          class SmemLayoutB>\nstruct SharedStorage\n{\n  cute::ArrayEngine<ElementA, cute::cosize_v<SmemLayoutA>> A;\n  cute::ArrayEngine<ElementB, cute::cosize_v<SmemLayoutB>> B;\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA, class S2RAtomA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB, class S2RAtomB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a, S2RAtomA s2r_atom_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b, S2RAtomB s2r_atom_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), sA_layout);   // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), sB_layout);   // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K,PIPE)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K,PIPE)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n\n  //\n  // PREFETCH\n  //\n\n  auto K_PIPE_MAX = size<3>(tAsA);\n\n  // Total count of tiles\n  int k_tile_count = size<3>(tAgA);\n  // Current tile index in gmem to read from\n  int k_tile_next = 0;\n\n  // Start async loads for all pipes but the last\n  CUTE_UNROLL\n  for (int k_pipe = 0; k_pipe < K_PIPE_MAX-1; ++k_pipe) {\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe));\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe));\n    cp_async_fence();\n    --k_tile_count;\n    if (k_tile_count > 0) { ++k_tile_next; }\n  }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.partition_fragment_A(sA(_,_,0));               // (MMA,MMA_M,MMA_K)\n  Tensor tCrB = thr_mma.partition_fragment_B(sB(_,_,0));               // (MMA,MMA_N,MMA_K)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V((  shape(tCrC) == take<0,3>(shape(tCgC))));     // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V((size<1>(tCgC) == size<1>(tCrA)));              // MMA_M\n  CUTE_STATIC_ASSERT_V((size<2>(tCgC) == size<1>(tCrB)));              // MMA_N\n\n  // Clear the accumulators\n  clear(tCrC);\n\n  //\n  // Copy Atom retiling\n  //\n\n  TiledCopy s2r_copy_a = make_tiled_copy_A(s2r_atom_a, mma);\n  ThrCopy   s2r_thr_copy_a = s2r_copy_a.get_slice(threadIdx.x);\n  Tensor tXsA = s2r_thr_copy_a.partition_S(sA);                        // (CPY,MMA_M,MMA_K,PIPE)\n  Tensor tXrA = s2r_thr_copy_a.retile_D(tCrA);                         // (CPY,MMA_M,MMA_K)\n\n  TiledCopy s2r_copy_b = make_tiled_copy_B(s2r_atom_b, mma);\n  ThrCopy   s2r_thr_copy_b = s2r_copy_b.get_slice(threadIdx.x);\n  Tensor tXsB = s2r_thr_copy_b.partition_S(sB);                        // (CPY,MMA_N,MMA_K,PIPE)\n  Tensor tXrB = s2r_thr_copy_b.retile_D(tCrB);                         // (CPY,MMA_N,MMA_K)\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrA : \"); print(tCrA); print(\"\\n\");\n    print(\"tCrB : \"); print(tCrB); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n\n    print(\"tXsA : \"); print(tXsA); print(\"\\n\");\n    print(\"tXrA : \"); print(tXrA); print(\"\\n\");\n    print(\"tXsB : \"); print(tXsB); print(\"\\n\");\n    print(\"tXrB : \"); print(tXrB); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Current pipe index in smem to read from\n  int smem_pipe_read  = 0;\n  // Current pipe index in smem to write to\n  int smem_pipe_write = K_PIPE_MAX-1;\n\n  // Pipe slice\n  Tensor tXsA_p = tXsA(_,_,_,smem_pipe_read);\n  Tensor tXsB_p = tXsB(_,_,_,smem_pipe_read);\n\n  // Size of the register pipeline\n  auto K_BLOCK_MAX = size<2>(tCrA);\n  CUTE_STATIC_ASSERT_V(K_BLOCK_MAX == size<2>(tXrA));\n\n  // PREFETCH register pipeline\n  if (K_BLOCK_MAX > 1) {\n    // Wait until our first prefetched tile is loaded in\n    cp_async_wait<K_PIPE_MAX-2>();\n    __syncthreads();\n\n    // Prefetch the first rmem from the first k-tile\n    copy(s2r_atom_a, tXsA_p(_,_,Int<0>{}), tXrA(_,_,Int<0>{}));\n    copy(s2r_atom_b, tXsB_p(_,_,Int<0>{}), tXrB(_,_,Int<0>{}));\n  }\n\n  //\n  // PIPELINED MAIN LOOP\n  // TUTORIAL: Example of a gemm loop that pipelines shared memory using SM80's cp.async instructions\n  //           and explicit pipelines in shared memory.\n  //   Data is read from global(k_tile_next) to shared(smem_pipe_write).\n  //   Data is read from shared(smem_pipe_read) to registers(k_block_next).\n  //   Data is computed on registers(b_block).\n  //\n  //   This allows all copies and compute to overlap:\n  //     Copy from gmem->smem can overlap with copies from smem->rmem and compute on rmem.\n  //     Copy from smem->rmem can overlap with compute on rmem.\n  //\n\n  CUTE_NO_UNROLL\n  while (k_tile_count > -(K_PIPE_MAX-1))\n  {\n    CUTE_UNROLL\n    for (int k_block = 0; k_block < K_BLOCK_MAX; ++k_block)\n    {\n      if (k_block == K_BLOCK_MAX - 1)\n      {\n        // Slice the smem_pipe_read smem\n        tXsA_p = tXsA(_,_,_,smem_pipe_read);\n        tXsB_p = tXsB(_,_,_,smem_pipe_read);\n\n        // Commit the smem for smem_pipe_read\n        cp_async_wait<K_PIPE_MAX-2>();\n        __syncthreads();\n      }\n\n      // Load A, B shmem->regs for k_block+1\n      auto k_block_next = (k_block + Int<1>{}) % K_BLOCK_MAX;      // static\n      copy(s2r_atom_a, tXsA_p(_,_,k_block_next), tXrA(_,_,k_block_next));\n      copy(s2r_atom_b, tXsB_p(_,_,k_block_next), tXrB(_,_,k_block_next));\n      // Copy gmem to smem before computing gemm on each k-pipe\n      if (k_block == 0)\n      {\n        copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,smem_pipe_write));\n        copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,smem_pipe_write));\n        cp_async_fence();\n\n        // Advance the gmem tile\n        --k_tile_count;\n        if (k_tile_count > 0) { ++k_tile_next; }\n\n        // Advance the smem pipe\n        smem_pipe_write = smem_pipe_read;\n        smem_pipe_read = (smem_pipe_read == K_PIPE_MAX-1) ? 0 : smem_pipe_read+1;\n      }\n      // Thread-level register gemm for k_block\n      gemm(mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCrC);\n    }\n\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\ntemplate <class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        cute::half_t const* A, int ldA,\n        cute::half_t const* B, int ldB,\n        Beta beta,\n        cute::half_t      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  assert(false && \"Not implemented\");\n}\n\n// Setup params for a TN HGEMM\ntemplate <class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        cute::half_t const* A, int ldA,\n        cute::half_t const* B, int ldB,\n        Beta beta,\n        cute::half_t      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  // Swizzles for LDSM and 128b k-major loads\n  auto swizzle_atom = composition(Swizzle<3,3,3>{},\n                                  Layout<Shape <_8,Shape <_8, _8>>,\n                                         Stride<_8,Stride<_1,_64>>>{});\n\n  auto sA = tile_to_shape(swizzle_atom, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(swizzle_atom, make_shape(bN,bK,bP));\n  auto sC = make_layout(make_shape(bM, bN));\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, cute::half_t>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{},  // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});               // Val layout  1x8 k-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, cute::half_t>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{},  // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});               // Val layout  1x8 n-major\n\n  TiledMMA mmaC = make_tiled_mma(SM80_16x8x16_F16F16F16F16_TN{},\n                                 Layout<Shape<_2,_2>>{},    // 2x2x1 MMA Atoms\n                                 Tile<_32,_32,_16>{});      // 32x32x16 Tiled MMA for LDSM\n\n  //Copy_Atom<DefaultCopy, half_t> s2r_atom_A;\n  //Copy_Atom<UniversalCopy<half_t>, half_t> s2r_atom_A;\n  //Copy_Atom<SM75_U32x1_LDSM_N, half_t> s2r_atom_A;\n  //Copy_Atom<SM75_U32x2_LDSM_N, half_t> s2r_atom_A;\n  Copy_Atom<SM75_U32x4_LDSM_N, half_t> s2r_atom_A;\n\n  //Copy_Atom<DefaultCopy, half_t> s2r_atom_B;\n  //Copy_Atom<UniversalCopy<half_t>, half_t> s2r_atom_B;\n  //Copy_Atom<SM75_U32x1_LDSM_N, half_t> s2r_atom_B;\n  //Copy_Atom<SM75_U32x2_LDSM_N, half_t> s2r_atom_B;\n  Copy_Atom<SM75_U32x4_LDSM_N, half_t> s2r_atom_B;\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<cute::half_t, cute::half_t, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n\n  auto kernel_fptr = gemm_device<\n    decltype(prob_shape), decltype(cta_tiler),\n    cute::half_t, decltype(dA), decltype(sA), decltype(copyA), decltype(s2r_atom_A),\n    cute::half_t, decltype(dB), decltype(sB), decltype(copyB), decltype(s2r_atom_B),\n    cute::half_t, decltype(dC), decltype(sC), decltype(mmaC),\n    decltype(alpha), decltype(beta)>;\n\n  // Set L1 to be SMEM only\n  cudaFuncSetAttribute(\n    kernel_fptr,\n    cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size);\n\n  cudaFuncSetAttribute(\n    kernel_fptr,\n    cudaFuncAttributePreferredSharedMemoryCarveout, 100);\n\n  kernel_fptr<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, s2r_atom_A,\n       B, dB, sB, copyB, s2r_atom_B,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK, bP));             // (m,k,p) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK, bP));             // (n,k,p) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{}, // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{});// Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{}, // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{});// Val layout  4x1 n-major\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, Copy_Atom<AutoVectorizingCopy, TA>{},\n       B, dB, sB, copyB, Copy_Atom<AutoVectorizingCopy, TB>{},\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA_atom                  = make_layout(make_shape (      bM,          bK),\n                                              make_stride(Int<1>{}, bM+Int<1>{})); // (m,k) -> smem_idx; padded m-major\n  [[maybe_unused]] auto sB_atom = make_layout(make_shape (      bN,          bK),\n                                              make_stride(Int<1>{}, bN+Int<1>{})); // (n,k) -> smem_idx; padded n-major\n  auto sA = tile_to_shape(sA_atom, make_shape(bM, bK, bP));\n  auto sB = tile_to_shape(sA_atom, make_shape(bN, bK, bP));\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, Copy_Atom<AutoVectorizingCopy, TA>{},\n       B, dB, sB, copyB, Copy_Atom<AutoVectorizingCopy, TB>{},\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major < 8) {\n    std::cout << \"This example requires an Ampere GPU or newer (CC >= 80)\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n  std::cout << \"Using device 0: \" << props.name\n            << \" (SM\" << props.major * 10 + props.minor\n            << \", \" << props.multiProcessorCount\n            << \")\" << std::endl;\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = static_cast<TI>(1.0f);\n  TI beta  = static_cast<TI>(0.0f);\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,\n          class SmemLayoutB>\nstruct SharedStorage\n{\n  cute::ArrayEngine<ElementA, cute::cosize_v<SmemLayoutA>> A;\n  cute::ArrayEngine<ElementB, cute::cosize_v<SmemLayoutB>> B;\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA, class S2RAtomA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB, class S2RAtomB,\n          class TC, class CStride, class CSmemLayout, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a, S2RAtomA s2r_atom_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b, S2RAtomB s2r_atom_b,\n            TC      * C, CStride dC, CSmemLayout          , TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  using namespace cute;\n\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n  static_assert(is_static<CSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(CSmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(CSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory buffers\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), sA_layout);   // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), sB_layout);   // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor tAsA = thr_copy_a.partition_D(sA);                            // (CPY,CPY_M,CPY_K,PIPE)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor tBsB = thr_copy_b.partition_D(sB);                            // (CPY,CPY_N,CPY_K,PIPE)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n\n  //\n  // PREFETCH\n  //\n\n  auto K_PIPE_MAX = size<3>(tAsA);\n\n  // Total count of tiles\n  int k_tile_count = size<3>(tAgA);\n  // Current tile index in gmem to read from\n  int k_tile_next = 0;\n\n  // Start async loads for all pipes but the last\n  CUTE_UNROLL\n  for (int k_pipe = 0; k_pipe < K_PIPE_MAX-1; ++k_pipe) {\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe));\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe));\n    cp_async_fence();\n    --k_tile_count;\n    if (k_tile_count > 0) { ++k_tile_next; }\n  }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.partition_fragment_A(sA(_,_,0));               // (MMA,MMA_M,MMA_K)\n  Tensor tCrB = thr_mma.partition_fragment_B(sB(_,_,0));               // (MMA,MMA_N,MMA_K)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V((  shape(tCrC) == take<0,3>(shape(tCgC))));     // (MMA,MMA_M,MMA_N)\n  CUTE_STATIC_ASSERT_V((size<1>(tCgC) == size<1>(tCrA)));              // MMA_M\n  CUTE_STATIC_ASSERT_V((size<2>(tCgC) == size<1>(tCrB)));              // MMA_N\n\n  // Clear the accumulators\n  clear(tCrC);\n\n  //\n  // Copy Atom retiling\n  //\n\n  TiledCopy s2r_copy_a = make_tiled_copy_A(s2r_atom_a, mma);\n  ThrCopy   s2r_thr_copy_a = s2r_copy_a.get_slice(threadIdx.x);\n  Tensor tXsA = s2r_thr_copy_a.partition_S(sA);                        // (CPY,MMA_M,MMA_K,PIPE)\n  Tensor tXrA = s2r_thr_copy_a.retile_D(tCrA);                         // (CPY,MMA_M,MMA_K)\n\n  TiledCopy s2r_copy_b = make_tiled_copy_B(s2r_atom_b, mma);\n  ThrCopy   s2r_thr_copy_b = s2r_copy_b.get_slice(threadIdx.x);\n  Tensor tXsB = s2r_thr_copy_b.partition_S(sB);                        // (CPY,MMA_N,MMA_K,PIPE)\n  Tensor tXrB = s2r_thr_copy_b.retile_D(tCrB);                         // (CPY,MMA_N,MMA_K)\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrA : \"); print(tCrA); print(\"\\n\");\n    print(\"tCrB : \"); print(tCrB); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n\n    print(\"tXsA : \"); print(tXsA); print(\"\\n\");\n    print(\"tXrA : \"); print(tXrA); print(\"\\n\");\n    print(\"tXsB : \"); print(tXsB); print(\"\\n\");\n    print(\"tXrB : \"); print(tXrB); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Current pipe index in smem to read from\n  int smem_pipe_read  = 0;\n  // Current pipe index in smem to write to\n  int smem_pipe_write = K_PIPE_MAX-1;\n\n  // Pipe slice\n  Tensor tXsA_p = tXsA(_,_,_,smem_pipe_read);\n  Tensor tXsB_p = tXsB(_,_,_,smem_pipe_read);\n\n  // Size of the register pipeline\n  auto K_BLOCK_MAX = size<2>(tCrA);\n  CUTE_STATIC_ASSERT_V(K_BLOCK_MAX == size<2>(tXrA));\n\n  // PREFETCH register pipeline\n  if (K_BLOCK_MAX > 1) {\n    // Wait until our first prefetched tile is loaded in\n    cp_async_wait<K_PIPE_MAX-2>();\n    __syncthreads();\n\n    // Prefetch the first rmem from the first k-tile\n    copy(s2r_atom_a, tXsA_p(_,_,Int<0>{}), tXrA(_,_,Int<0>{}));\n    copy(s2r_atom_b, tXsB_p(_,_,Int<0>{}), tXrB(_,_,Int<0>{}));\n  }\n\n  //\n  // PIPELINED MAIN LOOP\n  // TUTORIAL: Example of a gemm loop that pipelines shared memory using SM80's cp.async instructions\n  //           and explicit pipelines in shared memory.\n  //   Data is read from global(k_tile_next) to shared(smem_pipe_write).\n  //   Data is read from shared(smem_pipe_read) to registers(k_block_next).\n  //   Data is computed on registers(b_block).\n  //\n  //   This allows all copies and compute to overlap:\n  //     Copy from gmem->smem can overlap with copies from smem->rmem and compute on rmem.\n  //     Copy from smem->rmem can overlap with compute on rmem.\n  //\n\n  CUTE_NO_UNROLL\n  while (k_tile_count > -(K_PIPE_MAX-1))\n  {\n    CUTE_UNROLL\n    for (int k_block = 0; k_block < K_BLOCK_MAX; ++k_block)\n    {\n      if (k_block == K_BLOCK_MAX - 1)\n      {\n        // Slice the smem_pipe_read smem\n        tXsA_p = tXsA(_,_,_,smem_pipe_read);\n        tXsB_p = tXsB(_,_,_,smem_pipe_read);\n\n        // Commit the smem for smem_pipe_read\n        cp_async_wait<K_PIPE_MAX-2>();\n        __syncthreads();\n      }\n\n      // Load A, B shmem->regs for k_block+1\n      auto k_block_next = (k_block + Int<1>{}) % K_BLOCK_MAX;      // static\n      copy(s2r_atom_a, tXsA_p(_,_,k_block_next), tXrA(_,_,k_block_next));\n      copy(s2r_atom_b, tXsB_p(_,_,k_block_next), tXrB(_,_,k_block_next));\n      // Copy gmem to smem before computing gemm on each k-pipe\n      if (k_block == 0)\n      {\n        copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,smem_pipe_write));\n        copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,smem_pipe_write));\n        cp_async_fence();\n\n        // Advance the gmem tile\n        --k_tile_count;\n        if (k_tile_count > 0) { ++k_tile_next; }\n\n        // Advance the smem pipe\n        smem_pipe_write = smem_pipe_read;\n        smem_pipe_read = (smem_pipe_read == K_PIPE_MAX-1) ? 0 : smem_pipe_read+1;\n      }\n      // Thread-level register gemm for k_block\n      gemm(mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCrC);\n    }\n\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\ntemplate <class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        cute::half_t const* A, int ldA,\n        cute::half_t const* B, int ldB,\n        Beta beta,\n        cute::half_t      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  assert(false && \"Not implemented\");\n}\n\n// Setup params for a TN HGEMM\ntemplate <class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        cute::half_t const* A, int ldA,\n        cute::half_t const* B, int ldB,\n        Beta beta,\n        cute::half_t      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  // Swizzles for LDSM and 128b k-major loads\n  auto swizzle_atom = composition(Swizzle<3,3,3>{},\n                                  Layout<Shape <_8,Shape <_8, _8>>,\n                                         Stride<_8,Stride<_1,_64>>>{});\n\n  auto sA = tile_to_shape(swizzle_atom, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(swizzle_atom, make_shape(bN,bK,bP));\n  auto sC = make_layout(make_shape(bM, bN));\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, cute::half_t>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{},  // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});               // Val layout  1x8 k-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, cute::half_t>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{},  // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});               // Val layout  1x8 n-major\n\n  TiledMMA mmaC = make_tiled_mma(SM80_16x8x16_F16F16F16F16_TN{},\n                                 Layout<Shape<_2,_2>>{},    // 2x2x1 MMA Atoms\n                                 Tile<_32,_32,_16>{});      // 32x32x16 Tiled MMA for LDSM\n\n  //Copy_Atom<DefaultCopy, half_t> s2r_atom_A;\n  //Copy_Atom<UniversalCopy<half_t>, half_t> s2r_atom_A;\n  //Copy_Atom<SM75_U32x1_LDSM_N, half_t> s2r_atom_A;\n  //Copy_Atom<SM75_U32x2_LDSM_N, half_t> s2r_atom_A;\n  Copy_Atom<SM75_U32x4_LDSM_N, half_t> s2r_atom_A;\n\n  //Copy_Atom<DefaultCopy, half_t> s2r_atom_B;\n  //Copy_Atom<UniversalCopy<half_t>, half_t> s2r_atom_B;\n  //Copy_Atom<SM75_U32x1_LDSM_N, half_t> s2r_atom_B;\n  //Copy_Atom<SM75_U32x2_LDSM_N, half_t> s2r_atom_B;\n  Copy_Atom<SM75_U32x4_LDSM_N, half_t> s2r_atom_B;\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<cute::half_t, cute::half_t, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n\n  auto kernel_fptr = gemm_device<\n    decltype(prob_shape), decltype(cta_tiler),\n    cute::half_t, decltype(dA), decltype(sA), decltype(copyA), decltype(s2r_atom_A),\n    cute::half_t, decltype(dB), decltype(sB), decltype(copyB), decltype(s2r_atom_B),\n    cute::half_t, decltype(dC), decltype(sC), decltype(mmaC),\n    decltype(alpha), decltype(beta)>;\n\n  // Set L1 to be SMEM only\n  hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_fptr),\n    hipFuncAttributeMaxDynamicSharedMemorySize, smem_size);\n\n  hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_fptr),\n    hipFuncAttributePreferredSharedMemoryCarveout, 100);\n\n  kernel_fptr<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, s2r_atom_A,\n       B, dB, sB, copyB, s2r_atom_B,\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = make_layout(make_shape(bM, bK, bP));             // (m,k,p) -> smem_idx; m-major\n  auto sB = make_layout(make_shape(bN, bK, bP));             // (n,k,p) -> smem_idx; n-major\n  auto sC = make_layout(make_shape(bM, bN));                 // (m,n) -> smem_idx; m-major\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_32,_8>>{}, // Thr layout 32x8 m-major\n                                    Layout<Shape< _4,_1>>{});// Val layout  4x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_32,_8>>{}, // Thr layout 32x8 n-major\n                                    Layout<Shape< _4,_1>>{});// Val layout  4x1 n-major\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, Copy_Atom<AutoVectorizingCopy, TA>{},\n       B, dB, sB, copyB, Copy_Atom<AutoVectorizingCopy, TB>{},\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  using namespace cute;\n\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int<  8>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA_atom                  = make_layout(make_shape (      bM,          bK),\n                                              make_stride(Int<1>{}, bM+Int<1>{})); // (m,k) -> smem_idx; padded m-major\n  [[maybe_unused]] auto sB_atom = make_layout(make_shape (      bN,          bK),\n                                              make_stride(Int<1>{}, bN+Int<1>{})); // (n,k) -> smem_idx; padded n-major\n  auto sA = tile_to_shape(sA_atom, make_shape(bM, bK, bP));\n  auto sB = tile_to_shape(sA_atom, make_shape(bN, bK, bP));\n  auto sC = make_layout(make_shape(bM, bN));                        // (m,n) -> smem_idx\n\n  // Define the thread layouts (static)\n\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<TA>, TA>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<TB>, TB>{},\n                                    Layout<Shape<_32,_8>,Stride<_8,_1>>{}, // Thr layout 32x8 k-major\n                                    Layout<Shape< _1,_1>>{});              // Val layout  1x1\n\n  TiledMMA mmaC = make_tiled_mma(UniversalFMA<TC,TA,TB>{},\n                                 Layout<Shape<_16,_16,_1>>{});  // 16x16x1 TiledMMA\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(mmaC));\n  dim3 dimGrid(size(ceil_div(M, bM)),\n               size(ceil_div(N, bN)));\n  gemm_device<<<dimGrid, dimBlock, smem_size, stream>>>\n      (prob_shape, cta_tiler,\n       A, dA, sA, copyA, Copy_Atom<AutoVectorizingCopy, TA>{},\n       B, dB, sB, copyB, Copy_Atom<AutoVectorizingCopy, TB>{},\n       C, dC, sC, mmaC,\n       alpha, beta);\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major < 8) {\n    std::cout << \"This example requires an Ampere GPU or newer (CC >= 80)\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n  std::cout << \"Using device 0: \" << props.name\n            << \" (SM\" << props.major * 10 + props.minor\n            << \", \" << props.multiProcessorCount\n            << \")\" << std::endl;\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = static_cast<TI>(1.0f);\n  TI beta  = static_cast<TI>(0.0f);\n\n  std::cout << \"M = \" << m << std::endl;\n  std::cout << \"N = \" << n << std::endl;\n  std::cout << \"K = \" << k << std::endl;\n  std::cout << \"C = A^\" << transA << \" B^\" << transB << std::endl;\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  for (int j = 0; j < m*k; ++j) h_A[j] = static_cast<TA>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < n*k; ++j) h_B[j] = static_cast<TB>( 2*(rand() / double(RAND_MAX)) - 1 );\n  for (int j = 0; j < m*n; ++j) h_C[j] = static_cast<TC>(-1);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/sgemm_sm80.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\n// This is a simple tutorial showing several ways to partition a tensor into tiles then\n// perform efficient, coalesced copies. This example also shows how to vectorize accesses\n// which may be a useful optimization or required for certain workloads.\n//\n// `copy_kernel()` and `copy_kernel_vectorized()` each assume a pair of tensors with\n// dimensions (m, n) have been partitioned via `tiled_divide()`.\n//\n// The result are a part of compatible tensors with dimensions ((M, N), m', n'), where\n// (M, N) denotes a statically sized tile, and m' and n' denote the number of such tiles\n// within the tensor.\n//\n// Each statically sized tile is mapped to a CUDA threadblock which performs efficient\n// loads and stores to Global Memory.\n//\n// `copy_kernel()` uses `cute::local_partition()` to partition the tensor and map\n// the result to threads using a striped indexing scheme. Threads themselve are arranged\n// in a (ThreadShape_M, ThreadShape_N) arrangement which is replicated over the tile.\n//\n// `copy_kernel_vectorized()` uses `cute::make_tiled_copy()` to perform a similar\n// partitioning using `cute::Copy_Atom` to perform vectorization. The actual vector\n// size is defined by `ThreadShape`.\n//\n// This example assumes the overall tensor shape is divisible by the tile size and\n// does not perform predication.\n\n\n/// Simple copy kernel.\n//\n// Uses local_partition() to partition a tile among threads arranged as (THR_M, THR_N).\ntemplate <class TensorS, class TensorD, class ThreadLayout>\n__global__ void copy_kernel(TensorS S, TensorD D, ThreadLayout)\n{\n  using namespace cute;\n\n  // Slice the tiled tensors\n  Tensor tile_S = S(make_coord(_,_), blockIdx.x, blockIdx.y);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = D(make_coord(_,_), blockIdx.x, blockIdx.y);   // (BlockShape_M, BlockShape_N)\n\n  // Construct a partitioning of the tile among threads with the given thread arrangement.\n\n  // Concept:                         Tensor  ThrLayout       ThrIndex\n  Tensor thr_tile_S = local_partition(tile_S, ThreadLayout{}, threadIdx.x);  // (ThrValM, ThrValN)\n  Tensor thr_tile_D = local_partition(tile_D, ThreadLayout{}, threadIdx.x);  // (ThrValM, ThrValN)\n\n  // Construct a register-backed Tensor with the same shape as each thread's partition\n  // Use make_tensor to try to match the layout of thr_tile_S\n  Tensor fragment = make_tensor_like(thr_tile_S);               // (ThrValM, ThrValN)\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy(thr_tile_S, fragment);\n  copy(fragment, thr_tile_D);\n}\n\n/// Vectorized copy kernel.\n///\n/// Uses `make_tiled_copy()` to perform a copy using vector instructions. This operation\n/// has the precondition that pointers are aligned to the vector size.\n///\ntemplate <class TensorS, class TensorD, class Tiled_Copy>\n__global__ void copy_kernel_vectorized(TensorS S, TensorD D, Tiled_Copy tiled_copy)\n{\n  using namespace cute;\n\n  // Slice the tensors to obtain a view into each tile.\n  Tensor tile_S = S(make_coord(_, _), blockIdx.x, blockIdx.y);  // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = D(make_coord(_, _), blockIdx.x, blockIdx.y);  // (BlockShape_M, BlockShape_N)\n\n  // Construct a Tensor corresponding to each thread's slice.\n  ThrCopy thr_copy = tiled_copy.get_thread_slice(threadIdx.x);\n\n  Tensor thr_tile_S = thr_copy.partition_S(tile_S);             // (CopyOp, CopyM, CopyN)\n  Tensor thr_tile_D = thr_copy.partition_D(tile_D);             // (CopyOp, CopyM, CopyN)\n\n  // Construct a register-backed Tensor with the same shape as each thread's partition\n  // Use make_fragment because the first mode is the instruction-local mode\n  Tensor fragment = make_fragment_like(thr_tile_D);             // (CopyOp, CopyM, CopyN)\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy(tiled_copy, thr_tile_S, fragment);\n  copy(tiled_copy, fragment, thr_tile_D);\n}\n\n/// Main function\nint main(int argc, char** argv)\n{\n  //\n  // Given a 2D shape, perform an efficient copy\n  //\n\n  using namespace cute;\n  using Element = float;\n\n  // Define a tensor shape with dynamic extents (m, n)\n  auto tensor_shape = make_shape(256, 512);\n\n  //\n  // Allocate and initialize\n  //\n\n  thrust::host_vector<Element> h_S(size(tensor_shape));\n  thrust::host_vector<Element> h_D(size(tensor_shape));\n\n  for (size_t i = 0; i < h_S.size(); ++i) {\n    h_S[i] = static_cast<Element>(i);\n    h_D[i] = Element{};\n  }\n\n  thrust::device_vector<Element> d_S = h_S;\n  thrust::device_vector<Element> d_D = h_D;\n\n  //\n  // Make tensors\n  //\n\n  Tensor tensor_S = make_tensor(make_gmem_ptr(thrust::raw_pointer_cast(d_S.data())), make_layout(tensor_shape));\n  Tensor tensor_D = make_tensor(make_gmem_ptr(thrust::raw_pointer_cast(d_D.data())), make_layout(tensor_shape));\n\n  //\n  // Tile tensors\n  //\n\n  // Define a statically sized block (M, N).\n  // Note, by convention, capital letters are used to represent static modes.\n  auto block_shape = make_shape(Int<128>{}, Int<64>{});\n\n  if ((size<0>(tensor_shape) % size<0>(block_shape)) || (size<1>(tensor_shape) % size<1>(block_shape))) {\n    std::cerr << \"The tensor shape must be divisible by the block shape.\" << std::endl;\n    return -1;\n  }\n  // Equivalent check to the above\n  if (not evenly_divides(tensor_shape, block_shape)) {\n    std::cerr << \"Expected the block_shape to evenly divide the tensor shape.\" << std::endl;\n    return -1;\n  }\n\n  // Tile the tensor (m, n) ==> ((M, N), m', n') where (M, N) is the static tile\n  // shape, and modes (m', n') correspond to the number of tiles.\n  //\n  // These will be used to determine the CUDA kernel grid dimensions.\n  Tensor tiled_tensor_S = tiled_divide(tensor_S, block_shape);      // ((M, N), m', n')\n  Tensor tiled_tensor_D = tiled_divide(tensor_D, block_shape);      // ((M, N), m', n')\n\n  // Construct a TiledCopy with a specific access pattern.\n  //   This version uses a\n  //   (1) Layout-of-Threads to describe the number and arrangement of threads (e.g. row-major, col-major, etc),\n  //   (2) Layout-of-Values that each thread will access.\n\n  // Thread arrangement\n  Layout thr_layout = make_layout(make_shape(Int<32>{}, Int<8>{}));  // (32,8) -> thr_idx\n\n  // Value arrangement per thread\n  Layout val_layout = make_layout(make_shape(Int<4>{}, Int<1>{}));   // (4,1) -> val_idx\n\n  // Define `AccessType` which controls the size of the actual memory access instruction.\n  using CopyOp = UniversalCopy<uint_byte_t<sizeof(Element) * size(val_layout)>>;     // A very specific access width copy instruction\n  //using CopyOp = UniversalCopy<cutlass::AlignedArray<Element, size(val_layout)>>;  // A more generic type that supports many copy strategies\n  //using CopyOp = AutoVectorizingCopy;                                              // An adaptable-width instruction that assumes maximal alignment of inputs\n\n  // A Copy_Atom corresponds to one CopyOperation applied to Tensors of type Element.\n  using Atom = Copy_Atom<CopyOp, Element>;\n\n  // Construct tiled copy, a tiling of copy atoms.\n  //\n  // Note, this assumes the vector and thread layouts are aligned with contiguous data\n  // in GMEM. Alternative thread layouts are possible but may result in uncoalesced\n  // reads. Alternative value layouts are also possible, though incompatible layouts\n  // will result in compile time errors.\n  TiledCopy tiled_copy = make_tiled_copy(Atom{},             // Access strategy\n                                         thr_layout,         // thread layout (e.g. 32x4 Col-Major)\n                                         val_layout);        // value layout (e.g. 4x1)\n\n  //\n  // Determine grid and block dimensions\n  //\n\n  dim3 gridDim (size<1>(tiled_tensor_D), size<2>(tiled_tensor_D));   // Grid shape corresponds to modes m' and n'\n  dim3 blockDim(size(thr_layout));\n\n  //\n  // Launch the kernel\n  //\n  copy_kernel_vectorized<<< gridDim, blockDim >>>(\n    tiled_tensor_S,\n    tiled_tensor_D,\n    tiled_copy);\n\n  cudaError result = cudaDeviceSynchronize();\n  if (result != cudaSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << cudaGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  //\n  // Verify\n  //\n\n  h_D = d_D;\n\n  int32_t errors = 0;\n  int32_t const kErrorLimit = 10;\n\n  for (size_t i = 0; i < h_D.size(); ++i) {\n    if (h_S[i] != h_D[i]) {\n      std::cerr << \"Error. S[\" << i << \"]: \" << h_S[i] << \",   D[\" << i << \"]: \" << h_D[i] << std::endl;\n\n      if (++errors >= kErrorLimit) {\n        std::cerr << \"Aborting on \" << kErrorLimit << \"nth error.\" << std::endl;\n        return -1;\n      }\n    }\n  }\n\n  std::cout << \"Success.\" << std::endl;\n\n  return 0;\n}\n\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\n// This is a simple tutorial showing several ways to partition a tensor into tiles then\n// perform efficient, coalesced copies. This example also shows how to vectorize accesses\n// which may be a useful optimization or required for certain workloads.\n//\n// `copy_kernel()` and `copy_kernel_vectorized()` each assume a pair of tensors with\n// dimensions (m, n) have been partitioned via `tiled_divide()`.\n//\n// The result are a part of compatible tensors with dimensions ((M, N), m', n'), where\n// (M, N) denotes a statically sized tile, and m' and n' denote the number of such tiles\n// within the tensor.\n//\n// Each statically sized tile is mapped to a CUDA threadblock which performs efficient\n// loads and stores to Global Memory.\n//\n// `copy_kernel()` uses `cute::local_partition()` to partition the tensor and map\n// the result to threads using a striped indexing scheme. Threads themselve are arranged\n// in a (ThreadShape_M, ThreadShape_N) arrangement which is replicated over the tile.\n//\n// `copy_kernel_vectorized()` uses `cute::make_tiled_copy()` to perform a similar\n// partitioning using `cute::Copy_Atom` to perform vectorization. The actual vector\n// size is defined by `ThreadShape`.\n//\n// This example assumes the overall tensor shape is divisible by the tile size and\n// does not perform predication.\n\n\n/// Simple copy kernel.\n//\n// Uses local_partition() to partition a tile among threads arranged as (THR_M, THR_N).\ntemplate <class TensorS, class TensorD, class ThreadLayout>\n__global__ void copy_kernel(TensorS S, TensorD D, ThreadLayout)\n{\n  using namespace cute;\n\n  // Slice the tiled tensors\n  Tensor tile_S = S(make_coord(_,_), blockIdx.x, blockIdx.y);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = D(make_coord(_,_), blockIdx.x, blockIdx.y);   // (BlockShape_M, BlockShape_N)\n\n  // Construct a partitioning of the tile among threads with the given thread arrangement.\n\n  // Concept:                         Tensor  ThrLayout       ThrIndex\n  Tensor thr_tile_S = local_partition(tile_S, ThreadLayout{}, threadIdx.x);  // (ThrValM, ThrValN)\n  Tensor thr_tile_D = local_partition(tile_D, ThreadLayout{}, threadIdx.x);  // (ThrValM, ThrValN)\n\n  // Construct a register-backed Tensor with the same shape as each thread's partition\n  // Use make_tensor to try to match the layout of thr_tile_S\n  Tensor fragment = make_tensor_like(thr_tile_S);               // (ThrValM, ThrValN)\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy(thr_tile_S, fragment);\n  copy(fragment, thr_tile_D);\n}\n\n/// Vectorized copy kernel.\n///\n/// Uses `make_tiled_copy()` to perform a copy using vector instructions. This operation\n/// has the precondition that pointers are aligned to the vector size.\n///\ntemplate <class TensorS, class TensorD, class Tiled_Copy>\n__global__ void copy_kernel_vectorized(TensorS S, TensorD D, Tiled_Copy tiled_copy)\n{\n  using namespace cute;\n\n  // Slice the tensors to obtain a view into each tile.\n  Tensor tile_S = S(make_coord(_, _), blockIdx.x, blockIdx.y);  // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = D(make_coord(_, _), blockIdx.x, blockIdx.y);  // (BlockShape_M, BlockShape_N)\n\n  // Construct a Tensor corresponding to each thread's slice.\n  ThrCopy thr_copy = tiled_copy.get_thread_slice(threadIdx.x);\n\n  Tensor thr_tile_S = thr_copy.partition_S(tile_S);             // (CopyOp, CopyM, CopyN)\n  Tensor thr_tile_D = thr_copy.partition_D(tile_D);             // (CopyOp, CopyM, CopyN)\n\n  // Construct a register-backed Tensor with the same shape as each thread's partition\n  // Use make_fragment because the first mode is the instruction-local mode\n  Tensor fragment = make_fragment_like(thr_tile_D);             // (CopyOp, CopyM, CopyN)\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy(tiled_copy, thr_tile_S, fragment);\n  copy(tiled_copy, fragment, thr_tile_D);\n}\n\n/// Main function\nint main(int argc, char** argv)\n{\n  //\n  // Given a 2D shape, perform an efficient copy\n  //\n\n  using namespace cute;\n  using Element = float;\n\n  // Define a tensor shape with dynamic extents (m, n)\n  auto tensor_shape = make_shape(256, 512);\n\n  //\n  // Allocate and initialize\n  //\n\n  thrust::host_vector<Element> h_S(size(tensor_shape));\n  thrust::host_vector<Element> h_D(size(tensor_shape));\n\n  for (size_t i = 0; i < h_S.size(); ++i) {\n    h_S[i] = static_cast<Element>(i);\n    h_D[i] = Element{};\n  }\n\n  thrust::device_vector<Element> d_S = h_S;\n  thrust::device_vector<Element> d_D = h_D;\n\n  //\n  // Make tensors\n  //\n\n  Tensor tensor_S = make_tensor(make_gmem_ptr(thrust::raw_pointer_cast(d_S.data())), make_layout(tensor_shape));\n  Tensor tensor_D = make_tensor(make_gmem_ptr(thrust::raw_pointer_cast(d_D.data())), make_layout(tensor_shape));\n\n  //\n  // Tile tensors\n  //\n\n  // Define a statically sized block (M, N).\n  // Note, by convention, capital letters are used to represent static modes.\n  auto block_shape = make_shape(Int<128>{}, Int<64>{});\n\n  if ((size<0>(tensor_shape) % size<0>(block_shape)) || (size<1>(tensor_shape) % size<1>(block_shape))) {\n    std::cerr << \"The tensor shape must be divisible by the block shape.\" << std::endl;\n    return -1;\n  }\n  // Equivalent check to the above\n  if (not evenly_divides(tensor_shape, block_shape)) {\n    std::cerr << \"Expected the block_shape to evenly divide the tensor shape.\" << std::endl;\n    return -1;\n  }\n\n  // Tile the tensor (m, n) ==> ((M, N), m', n') where (M, N) is the static tile\n  // shape, and modes (m', n') correspond to the number of tiles.\n  //\n  // These will be used to determine the CUDA kernel grid dimensions.\n  Tensor tiled_tensor_S = tiled_divide(tensor_S, block_shape);      // ((M, N), m', n')\n  Tensor tiled_tensor_D = tiled_divide(tensor_D, block_shape);      // ((M, N), m', n')\n\n  // Construct a TiledCopy with a specific access pattern.\n  //   This version uses a\n  //   (1) Layout-of-Threads to describe the number and arrangement of threads (e.g. row-major, col-major, etc),\n  //   (2) Layout-of-Values that each thread will access.\n\n  // Thread arrangement\n  Layout thr_layout = make_layout(make_shape(Int<32>{}, Int<8>{}));  // (32,8) -> thr_idx\n\n  // Value arrangement per thread\n  Layout val_layout = make_layout(make_shape(Int<4>{}, Int<1>{}));   // (4,1) -> val_idx\n\n  // Define `AccessType` which controls the size of the actual memory access instruction.\n  using CopyOp = UniversalCopy<uint_byte_t<sizeof(Element) * size(val_layout)>>;     // A very specific access width copy instruction\n  //using CopyOp = UniversalCopy<cutlass::AlignedArray<Element, size(val_layout)>>;  // A more generic type that supports many copy strategies\n  //using CopyOp = AutoVectorizingCopy;                                              // An adaptable-width instruction that assumes maximal alignment of inputs\n\n  // A Copy_Atom corresponds to one CopyOperation applied to Tensors of type Element.\n  using Atom = Copy_Atom<CopyOp, Element>;\n\n  // Construct tiled copy, a tiling of copy atoms.\n  //\n  // Note, this assumes the vector and thread layouts are aligned with contiguous data\n  // in GMEM. Alternative thread layouts are possible but may result in uncoalesced\n  // reads. Alternative value layouts are also possible, though incompatible layouts\n  // will result in compile time errors.\n  TiledCopy tiled_copy = make_tiled_copy(Atom{},             // Access strategy\n                                         thr_layout,         // thread layout (e.g. 32x4 Col-Major)\n                                         val_layout);        // value layout (e.g. 4x1)\n\n  //\n  // Determine grid and block dimensions\n  //\n\n  dim3 gridDim (size<1>(tiled_tensor_D), size<2>(tiled_tensor_D));   // Grid shape corresponds to modes m' and n'\n  dim3 blockDim(size(thr_layout));\n\n  //\n  // Launch the kernel\n  //\n  copy_kernel_vectorized<<< gridDim, blockDim >>>(\n    tiled_tensor_S,\n    tiled_tensor_D,\n    tiled_copy);\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result != hipSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << hipGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  //\n  // Verify\n  //\n\n  h_D = d_D;\n\n  int32_t errors = 0;\n  int32_t const kErrorLimit = 10;\n\n  for (size_t i = 0; i < h_D.size(); ++i) {\n    if (h_S[i] != h_D[i]) {\n      std::cerr << \"Error. S[\" << i << \"]: \" << h_S[i] << \",   D[\" << i << \"]: \" << h_D[i] << std::endl;\n\n      if (++errors >= kErrorLimit) {\n        std::cerr << \"Aborting on \" << kErrorLimit << \"nth error.\" << std::endl;\n        return -1;\n      }\n    }\n  }\n\n  std::cout << \"Success.\" << std::endl;\n\n  return 0;\n}\n\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/tiled_copy.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\n// This example extends `tiled_copy` using predicate tensors to guard memory accesses performed\n// by `cute::copy_if()`. This enables tensors to have shapes that are not integer multiples of\n// block sizes.\n//\n// This is accomplished by instantiating a tensor of coordinates which correspond to tensor elements\n// to be accessed and then computing a predicate tensor which masks accesses. The example demonstrates\n// how constructing of an identity tensor containing coordinates and a predicate tensor containing\n// mask bits can be implemented using the same CuTe operations used to tile the tensors in\n// Global Memory.\n//\n// This example implements two variants:\n//  - copy_if_kernel() uses `cute::local_partition()` to construct each thread's slice\n//  - copy_if_kernel_vectorized() uses `make_tiled_copy() to implement vectorized memory accesses.\n//\n// The tensor shapes and strides must be divisible by the shape of the vector access.\n//\n\n/// Simple copy kernel.\n//\n// Uses local_partition() to partition a tile among threads arranged as (THR_M, THR_N).\ntemplate <class TensorS, class TensorD, class BlockShape, class ThreadLayout>\n__global__ void copy_if_kernel(TensorS S, TensorD D, BlockShape block_shape, ThreadLayout)\n{\n  using namespace cute;\n\n  // Construct a coordinate tensor whose elements are the coordinates used to access tensors S and D.\n  auto shape_S = shape(S);\n  Tensor C = make_identity_tensor(shape_S);\n  // Construct a predicate tensor which compares the coordinates with the original shape\n  Tensor P = cute::lazy::transform(C, [&](auto c) { return elem_less(c, shape_S); });\n\n  // Tile the input tensor into blocks\n  auto block_coord = make_coord(blockIdx.x, blockIdx.y);\n  Tensor tile_S = local_tile(S, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = local_tile(D, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_P = local_tile(P, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n\n  // Construct a partitioning of the tile among threads with the given thread arrangement.\n\n  // Concept:                         Tensor  ThrLayout       ThrIndex\n  Tensor thr_tile_S = local_partition(tile_S, ThreadLayout{}, threadIdx.x);\n  Tensor thr_tile_D = local_partition(tile_D, ThreadLayout{}, threadIdx.x);\n  Tensor thr_tile_P = local_partition(tile_P, ThreadLayout{}, threadIdx.x);\n\n  // Copy from GMEM to GMEM using `thr_tile_P` to guard accesses.\n  copy_if(thr_tile_P, thr_tile_S, thr_tile_D);\n}\n\n/// Vectorized copy kernel.\n///\n/// Uses `make_tiled_copy()` to perform a copy using vector instructions. This operation\n/// has the precondition that pointers are aligned to the vector size.\n///\ntemplate <class TensorS, class TensorD, class BlockShape, class Tiled_Copy>\n__global__ void copy_if_kernel_vectorized(TensorS S, TensorD D, BlockShape block_shape, Tiled_Copy tiled_copy)\n{\n  using namespace cute;\n\n  // Construct a coordinate tensor whose elements are the coordinates used to access tensors S and D.\n  auto shape_S = shape(S);\n  Tensor C = make_identity_tensor(shape_S);\n  // Construct a predicate tensor which compares the coordinates with the original shape\n  Tensor P = cute::lazy::transform(C, [&](auto c) { return elem_less(c, shape_S); });\n\n  // Tile the input tensor into blocks\n  auto block_coord = make_coord(blockIdx.x, blockIdx.y);\n  Tensor tile_S = local_tile(S, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = local_tile(D, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n  Tensor tile_P = local_tile(P, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n\n  //\n  // Construct a Tensor corresponding to each thread's slice.\n  //\n  ThrCopy thr_copy = tiled_copy.get_thread_slice(threadIdx.x);\n  Tensor thr_tile_S = thr_copy.partition_S(tile_S);              // (CPY, CPY_M, CPY_N)\n  Tensor thr_tile_D = thr_copy.partition_D(tile_D);              // (CPY, CPY_M, CPY_N)\n  Tensor thr_tile_P = thr_copy.partition_S(tile_P);              // (CPY, CPY_M, CPY_N)\n\n#if 0\n  // Copy from GMEM to GMEM\n  copy_if(tiled_copy, thr_tile_P, thr_tile_S, thr_tile_D);\n#else\n  // make_fragment_like() constructs a tensor in RMEM with the same shape as thr_tile_S.\n  Tensor frag = make_fragment_like(thr_tile_S);\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy_if(tiled_copy, thr_tile_P, thr_tile_S, frag);\n  copy_if(tiled_copy, thr_tile_P, frag,       thr_tile_D);\n#endif\n}\n\n/// Main function\nint main(int argc, char** argv)\n{\n  //\n  // Given a 2D shape, perform an efficient copy\n  //\n\n  using namespace cute;\n  using Element = float;\n\n  // Define a tensor shape with dynamic extents (m, n)\n  auto tensor_shape = make_shape(528, 300);\n\n  thrust::host_vector<Element> h_S(size(tensor_shape));\n  thrust::host_vector<Element> h_D(size(tensor_shape));\n\n  //\n  // Initialize\n  //\n\n  for (size_t i = 0; i < h_S.size(); ++i) {\n    h_S[i] = static_cast<Element>(i);\n    h_D[i] = Element{};\n  }\n\n  thrust::device_vector<Element> d_S = h_S;\n  thrust::device_vector<Element> d_D = h_D;\n  thrust::device_vector<Element> d_Zero = h_D;\n\n  //\n  // Make tensors\n  //\n\n  Tensor tensor_S = make_tensor(make_gmem_ptr(d_S.data().get()), make_layout(tensor_shape));\n  Tensor tensor_D = make_tensor(make_gmem_ptr(d_D.data().get()), make_layout(tensor_shape));\n\n  //\n  // Partition\n  //\n\n  // Define a statically sized block (M, N).\n  //\n  // Note, by convention, capital letters are used to represent static modes.\n  auto block_shape = make_shape(Int<128>{}, Int<64>{});\n\n  // Tile the tensor (m, n) ==> ((M, N), m', n') where (M, N) is the static tile\n  // shape, and modes (m', n') correspond to the number of tiles.\n  //\n  // These will be used to determine the CUDA kernel grid dimensinos.\n  Tensor tiled_tensor_D = tiled_divide(tensor_D, block_shape);        // ((M, N), m', n')\n\n  // Describes the layout of threads which is then replicated to tile 'block_shape.'\n  Layout thr_layout = make_layout(make_shape(Int<32>{}, Int< 8>{}));  // (ThrM, ThrN)\n\n  //\n  // Determine grid and block dimensions\n  //\n\n  dim3 gridDim (size<1>(tiled_tensor_D), size<2>(tiled_tensor_D));   // Grid shape corresponds to modes m' and n'\n  dim3 blockDim(size(thr_layout));\n\n  //\n  // Launch the kernel\n  //\n\n  // copy_if()\n  copy_if_kernel<<< gridDim, blockDim >>>(\n    tensor_S,\n    tensor_D,\n    block_shape,\n    thr_layout);\n\n  cudaError result = cudaDeviceSynchronize();\n\n  if (result != cudaSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << cudaGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  h_D = d_D;\n\n  //\n  // Verification\n  //\n\n  auto verify = [](thrust::host_vector<Element> const &S, thrust::host_vector<Element> const &D){\n\n    int32_t errors = 0;\n    int32_t const kErrorLimit = 10;\n\n    if (S.size() != D.size()) {\n      return 1;\n    }\n\n    for (size_t i = 0; i < D.size(); ++i) {\n      if (S[i] != D[i]) {\n        std::cerr << \"Error. S[\" << i << \"]: \" << S[i] << \",   D[\" << i << \"]: \" << D[i] << std::endl;\n\n        if (++errors >= kErrorLimit) {\n          std::cerr << \"Aborting on \" << kErrorLimit << \"nth error.\" << std::endl;\n          return errors;\n        }\n      }\n    }\n\n    return errors;\n  };\n\n  if (verify(h_D, h_S)) {\n    return -1;\n  } else {\n    std::cout << \"Success.\" << std::endl;\n  }\n\n  thrust::copy(d_Zero.begin(), d_Zero.end(), d_D.begin());\n\n  // Construct a TiledCopy with a specific access pattern.\n  //   This version uses a\n  //   (1) Layout-of-Threads to describe the number and arrangement of threads (e.g. row-major, col-major, etc),\n  //   (2) Layout-of-Values that each thread will access.\n\n  // Value arrangement per thread\n  Layout val_layout = make_layout(make_shape(Int<4>{}, Int<1>{}));   // (4,1) -> val_idx\n\n  // Define `AccessType` which controls the size of the actual memory access instruction.\n  using CopyOp = UniversalCopy<uint_byte_t<sizeof(Element) * size(val_layout)>>;     // A very specific access width copy instruction\n  //using CopyOp = UniversalCopy<cutlass::AlignedArray<Element, size(val_layout)>>;  // A more generic type that supports many copy strategies\n  //using CopyOp = AutoVectorizingCopy;                                              // An adaptable-width instruction that assumes maximal alignment of inputs\n\n  // A Copy_Atom corresponds to one CopyOperation applied to Tensors of type Element.\n  using Atom = Copy_Atom<CopyOp, Element>;\n\n  // Construct tiled copy, a tiling of copy atoms.\n  //\n  // Note, this assumes the vector and thread layouts are aligned with contigous data\n  // in GMEM. Alternative thread layouts are possible but may result in uncoalesced\n  // reads. Alternative value layouts are also possible, though incompatible layouts\n  // will result in compile time errors.\n  TiledCopy tiled_copy = make_tiled_copy(Atom{},             // Access strategy\n                                         thr_layout,         // thread layout (e.g. 32x4 Col-Major)\n                                         val_layout);        // value layout (e.g. 4x1)\n\n  // copy_if() with vectorization\n  copy_if_kernel_vectorized<<< gridDim, blockDim >>>(\n    tensor_S,\n    tensor_D,\n    block_shape,\n    tiled_copy);\n\n  result = cudaDeviceSynchronize();\n\n  if (result != cudaSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << cudaGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  h_D = d_D;\n\n  if (verify(h_D, h_S)) {\n    return -1;\n  } else {\n    std::cout << \"Success.\" << std::endl;\n  }\n  return 0;\n}\n\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\n// This example extends `tiled_copy` using predicate tensors to guard memory accesses performed\n// by `cute::copy_if()`. This enables tensors to have shapes that are not integer multiples of\n// block sizes.\n//\n// This is accomplished by instantiating a tensor of coordinates which correspond to tensor elements\n// to be accessed and then computing a predicate tensor which masks accesses. The example demonstrates\n// how constructing of an identity tensor containing coordinates and a predicate tensor containing\n// mask bits can be implemented using the same CuTe operations used to tile the tensors in\n// Global Memory.\n//\n// This example implements two variants:\n//  - copy_if_kernel() uses `cute::local_partition()` to construct each thread's slice\n//  - copy_if_kernel_vectorized() uses `make_tiled_copy() to implement vectorized memory accesses.\n//\n// The tensor shapes and strides must be divisible by the shape of the vector access.\n//\n\n/// Simple copy kernel.\n//\n// Uses local_partition() to partition a tile among threads arranged as (THR_M, THR_N).\ntemplate <class TensorS, class TensorD, class BlockShape, class ThreadLayout>\n__global__ void copy_if_kernel(TensorS S, TensorD D, BlockShape block_shape, ThreadLayout)\n{\n  using namespace cute;\n\n  // Construct a coordinate tensor whose elements are the coordinates used to access tensors S and D.\n  auto shape_S = shape(S);\n  Tensor C = make_identity_tensor(shape_S);\n  // Construct a predicate tensor which compares the coordinates with the original shape\n  Tensor P = cute::lazy::transform(C, [&](auto c) { return elem_less(c, shape_S); });\n\n  // Tile the input tensor into blocks\n  auto block_coord = make_coord(blockIdx.x, blockIdx.y);\n  Tensor tile_S = local_tile(S, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = local_tile(D, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n  Tensor tile_P = local_tile(P, block_shape, block_coord);   // (BlockShape_M, BlockShape_N)\n\n  // Construct a partitioning of the tile among threads with the given thread arrangement.\n\n  // Concept:                         Tensor  ThrLayout       ThrIndex\n  Tensor thr_tile_S = local_partition(tile_S, ThreadLayout{}, threadIdx.x);\n  Tensor thr_tile_D = local_partition(tile_D, ThreadLayout{}, threadIdx.x);\n  Tensor thr_tile_P = local_partition(tile_P, ThreadLayout{}, threadIdx.x);\n\n  // Copy from GMEM to GMEM using `thr_tile_P` to guard accesses.\n  copy_if(thr_tile_P, thr_tile_S, thr_tile_D);\n}\n\n/// Vectorized copy kernel.\n///\n/// Uses `make_tiled_copy()` to perform a copy using vector instructions. This operation\n/// has the precondition that pointers are aligned to the vector size.\n///\ntemplate <class TensorS, class TensorD, class BlockShape, class Tiled_Copy>\n__global__ void copy_if_kernel_vectorized(TensorS S, TensorD D, BlockShape block_shape, Tiled_Copy tiled_copy)\n{\n  using namespace cute;\n\n  // Construct a coordinate tensor whose elements are the coordinates used to access tensors S and D.\n  auto shape_S = shape(S);\n  Tensor C = make_identity_tensor(shape_S);\n  // Construct a predicate tensor which compares the coordinates with the original shape\n  Tensor P = cute::lazy::transform(C, [&](auto c) { return elem_less(c, shape_S); });\n\n  // Tile the input tensor into blocks\n  auto block_coord = make_coord(blockIdx.x, blockIdx.y);\n  Tensor tile_S = local_tile(S, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n  Tensor tile_D = local_tile(D, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n  Tensor tile_P = local_tile(P, block_shape, block_coord);       // (BlockShape_M, BlockShape_N)\n\n  //\n  // Construct a Tensor corresponding to each thread's slice.\n  //\n  ThrCopy thr_copy = tiled_copy.get_thread_slice(threadIdx.x);\n  Tensor thr_tile_S = thr_copy.partition_S(tile_S);              // (CPY, CPY_M, CPY_N)\n  Tensor thr_tile_D = thr_copy.partition_D(tile_D);              // (CPY, CPY_M, CPY_N)\n  Tensor thr_tile_P = thr_copy.partition_S(tile_P);              // (CPY, CPY_M, CPY_N)\n\n#if 0\n  // Copy from GMEM to GMEM\n  copy_if(tiled_copy, thr_tile_P, thr_tile_S, thr_tile_D);\n#else\n  // make_fragment_like() constructs a tensor in RMEM with the same shape as thr_tile_S.\n  Tensor frag = make_fragment_like(thr_tile_S);\n\n  // Copy from GMEM to RMEM and from RMEM to GMEM\n  copy_if(tiled_copy, thr_tile_P, thr_tile_S, frag);\n  copy_if(tiled_copy, thr_tile_P, frag,       thr_tile_D);\n#endif\n}\n\n/// Main function\nint main(int argc, char** argv)\n{\n  //\n  // Given a 2D shape, perform an efficient copy\n  //\n\n  using namespace cute;\n  using Element = float;\n\n  // Define a tensor shape with dynamic extents (m, n)\n  auto tensor_shape = make_shape(528, 300);\n\n  thrust::host_vector<Element> h_S(size(tensor_shape));\n  thrust::host_vector<Element> h_D(size(tensor_shape));\n\n  //\n  // Initialize\n  //\n\n  for (size_t i = 0; i < h_S.size(); ++i) {\n    h_S[i] = static_cast<Element>(i);\n    h_D[i] = Element{};\n  }\n\n  thrust::device_vector<Element> d_S = h_S;\n  thrust::device_vector<Element> d_D = h_D;\n  thrust::device_vector<Element> d_Zero = h_D;\n\n  //\n  // Make tensors\n  //\n\n  Tensor tensor_S = make_tensor(make_gmem_ptr(d_S.data().get()), make_layout(tensor_shape));\n  Tensor tensor_D = make_tensor(make_gmem_ptr(d_D.data().get()), make_layout(tensor_shape));\n\n  //\n  // Partition\n  //\n\n  // Define a statically sized block (M, N).\n  //\n  // Note, by convention, capital letters are used to represent static modes.\n  auto block_shape = make_shape(Int<128>{}, Int<64>{});\n\n  // Tile the tensor (m, n) ==> ((M, N), m', n') where (M, N) is the static tile\n  // shape, and modes (m', n') correspond to the number of tiles.\n  //\n  // These will be used to determine the CUDA kernel grid dimensinos.\n  Tensor tiled_tensor_D = tiled_divide(tensor_D, block_shape);        // ((M, N), m', n')\n\n  // Describes the layout of threads which is then replicated to tile 'block_shape.'\n  Layout thr_layout = make_layout(make_shape(Int<32>{}, Int< 8>{}));  // (ThrM, ThrN)\n\n  //\n  // Determine grid and block dimensions\n  //\n\n  dim3 gridDim (size<1>(tiled_tensor_D), size<2>(tiled_tensor_D));   // Grid shape corresponds to modes m' and n'\n  dim3 blockDim(size(thr_layout));\n\n  //\n  // Launch the kernel\n  //\n\n  // copy_if()\n  copy_if_kernel<<< gridDim, blockDim >>>(\n    tensor_S,\n    tensor_D,\n    block_shape,\n    thr_layout);\n\n  hipError_t result = hipDeviceSynchronize();\n\n  if (result != hipSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << hipGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  h_D = d_D;\n\n  //\n  // Verification\n  //\n\n  auto verify = [](thrust::host_vector<Element> const &S, thrust::host_vector<Element> const &D){\n\n    int32_t errors = 0;\n    int32_t const kErrorLimit = 10;\n\n    if (S.size() != D.size()) {\n      return 1;\n    }\n\n    for (size_t i = 0; i < D.size(); ++i) {\n      if (S[i] != D[i]) {\n        std::cerr << \"Error. S[\" << i << \"]: \" << S[i] << \",   D[\" << i << \"]: \" << D[i] << std::endl;\n\n        if (++errors >= kErrorLimit) {\n          std::cerr << \"Aborting on \" << kErrorLimit << \"nth error.\" << std::endl;\n          return errors;\n        }\n      }\n    }\n\n    return errors;\n  };\n\n  if (verify(h_D, h_S)) {\n    return -1;\n  } else {\n    std::cout << \"Success.\" << std::endl;\n  }\n\n  thrust::copy(d_Zero.begin(), d_Zero.end(), d_D.begin());\n\n  // Construct a TiledCopy with a specific access pattern.\n  //   This version uses a\n  //   (1) Layout-of-Threads to describe the number and arrangement of threads (e.g. row-major, col-major, etc),\n  //   (2) Layout-of-Values that each thread will access.\n\n  // Value arrangement per thread\n  Layout val_layout = make_layout(make_shape(Int<4>{}, Int<1>{}));   // (4,1) -> val_idx\n\n  // Define `AccessType` which controls the size of the actual memory access instruction.\n  using CopyOp = UniversalCopy<uint_byte_t<sizeof(Element) * size(val_layout)>>;     // A very specific access width copy instruction\n  //using CopyOp = UniversalCopy<cutlass::AlignedArray<Element, size(val_layout)>>;  // A more generic type that supports many copy strategies\n  //using CopyOp = AutoVectorizingCopy;                                              // An adaptable-width instruction that assumes maximal alignment of inputs\n\n  // A Copy_Atom corresponds to one CopyOperation applied to Tensors of type Element.\n  using Atom = Copy_Atom<CopyOp, Element>;\n\n  // Construct tiled copy, a tiling of copy atoms.\n  //\n  // Note, this assumes the vector and thread layouts are aligned with contigous data\n  // in GMEM. Alternative thread layouts are possible but may result in uncoalesced\n  // reads. Alternative value layouts are also possible, though incompatible layouts\n  // will result in compile time errors.\n  TiledCopy tiled_copy = make_tiled_copy(Atom{},             // Access strategy\n                                         thr_layout,         // thread layout (e.g. 32x4 Col-Major)\n                                         val_layout);        // value layout (e.g. 4x1)\n\n  // copy_if() with vectorization\n  copy_if_kernel_vectorized<<< gridDim, blockDim >>>(\n    tensor_S,\n    tensor_D,\n    block_shape,\n    tiled_copy);\n\n  result = hipDeviceSynchronize();\n\n  if (result != hipSuccess) {\n    std::cerr << \"CUDA Runtime error: \" << hipGetErrorString(result) << std::endl;\n    return -1;\n  }\n\n  h_D = d_D;\n\n  if (verify(h_D, h_S)) {\n    return -1;\n  } else {\n    std::cout << \"Success.\" << std::endl;\n  }\n  return 0;\n}\n\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/tiled_copy_if.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/cluster_launch.hpp\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\nusing namespace cute;\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,  // (M,K,P)\n          class SmemLayoutB>  // (N,K,P)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<ElementA, cosize_v<SmemLayoutA>> A;\n  alignas(128) cute::ArrayEngine<ElementB, cosize_v<SmemLayoutB>> B;\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory tensors\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), ASmemLayout{}); // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), BSmemLayout{}); // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor sA_ = as_position_independent_swizzle_tensor(sA);\n  Tensor tAsA = thr_copy_a.partition_D(sA_);                           // (CPY,CPY_M,CPY_K,PIPE)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor sB_ = as_position_independent_swizzle_tensor(sB);\n  Tensor tBsB = thr_copy_b.partition_D(sB_);                           // (CPY,CPY_N,CPY_K,PIPE)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n\n  //\n  // PREFETCH\n  //\n\n  // auto K_PIPE_MAX = size<3>(tAsA);\n\n  // // Total count of tiles\n  // int k_tile_count = size<3>(tAgA);\n  // // Current tile index in gmem to read from\n  // int k_tile_next = 0;\n\n  // // Start async loads for all pipes but the last\n  // CUTE_UNROLL\n  // for (int k_pipe = 0; k_pipe < K_PIPE_MAX-1; ++k_pipe) {\n  //   copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe));\n  //   copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe));\n  //   cp_async_fence();\n  //   --k_tile_count;\n  //   if (k_tile_count > 0) { ++k_tile_next; }\n  // }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K,PIPE)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K,PIPE)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V((size<1>(tCgC) == size<1>(tCsA)));              // MMA_M\n  CUTE_STATIC_ASSERT_V((size<2>(tCgC) == size<1>(tCsB)));              // MMA_N\n  CUTE_STATIC_ASSERT_V((size<2>(tCsA) == size<2>(tCsB)));              // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrA : \"); print(tCrA); print(\"\\n\");\n    print(\"tCrB : \"); print(tCrB); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Total number of k-tiles\n  auto K_TILE_MAX  = size<3>(tAgA);\n  // Number of pipelined k-tiles in smem\n  auto K_PIPE_MAX  = size<3>(tAsA);\n\n  //\n  // PREFETCH\n  //\n\n  // Prefetch all but the last\n  CUTE_UNROLL\n  for (int k = 0; k < K_PIPE_MAX-1; ++k)\n  {\n    copy(copy_a, tAgA(_,_,_,k), tAsA(_,_,_,k));\n    copy(copy_b, tBgB(_,_,_,k), tBsB(_,_,_,k));\n    cp_async_fence();\n  }\n\n  // Clear the accumulators\n  clear(tCrC);\n\n  __syncthreads();\n\n  //\n  // PIPELINED MAIN LOOP\n  //\n\n  // Current pipe to read from\n  int k_pipe_read  = 0;\n  // Current pipe to write to\n  int k_pipe_write = K_PIPE_MAX-1;\n\n  CUTE_NO_UNROLL\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    int k_tile_next = k_tile + (K_PIPE_MAX-1);\n    k_tile_next = (k_tile_next >= K_TILE_MAX) ? K_TILE_MAX-1 : k_tile_next;\n\n    //\n    // Copy gmem to smem for k_tile_write\n    //\n\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe_write));\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe_write));\n    cp_async_fence();\n\n    // Advance k_pipe_write\n    ++k_pipe_write;\n    k_pipe_write = (k_pipe_write == K_PIPE_MAX) ? 0 : k_pipe_write;\n\n    //\n    // Compute on k_tile\n    //\n\n    // Wait on all cp.async -- optimize by pipelining to overlap GMEM reads\n    cp_async_wait<0>();\n\n    warpgroup_fence_operand(tCrC);\n    warpgroup_arrive();\n    // (V,M,K) x (V,N,K) => (V,M,N)\n    cute::gemm(mma, tCrA(_,_,_,k_pipe_read), tCrB(_,_,_,k_pipe_read), tCrC);\n    warpgroup_commit_batch();\n    /// Wait on the GMMA barrier for K_PIPE_MMAS (or fewer) outstanding to ensure smem_pipe_write is consumed\n    warpgroup_wait<0>();\n    warpgroup_fence_operand(tCrC);\n\n    // Advance k_pipe_read\n    ++k_pipe_read;\n    k_pipe_read = (k_pipe_read == K_PIPE_MAX) ? 0 : k_pipe_read;\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_16,_8>>{}, // Thr layout 32x4 m-major\n                                    Layout<Shape< _8,_1>>{});// Val layout  8x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_16,_8>>{}, // Thr layout 32x4 n-major\n                                    Layout<Shape< _8,_1>>{});// Val layout  8x1 n-major\n\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::MN,GMMA::Major::MN>{});\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(1, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  int  smemBytes = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n\n  auto* kernel_ptr = &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                  TA, decltype(dA), decltype(sA), decltype(copyA),\n                                  TB, decltype(dB), decltype(sB), decltype(copyB),\n                                  TC, decltype(dC), decltype(tiled_mma),\n                                  decltype(alpha), decltype(beta)>;\n\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  // Kernel Launch\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, dA, sA, copyA,\n                                                             B, dB, sB, copyB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_K_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_K_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{}, // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});              // Val layout  1x8\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{}, // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});              // Val layout  1x8\n\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::K,GMMA::Major::K>{});\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(1, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n                              &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                           TA, decltype(dA), decltype(sA), decltype(copyA),\n                                           TB, decltype(dB), decltype(sB), decltype(copyB),\n                                           TC, decltype(dC), decltype(tiled_mma),\n                                           decltype(alpha), decltype(beta)>);\n\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(\n    kernel_ptr,\n    cudaFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  // Kernel Launch\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, dA, sA, copyA,\n                                                             B, dB, sB, copyB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major != 9) {\n    std::cout << \"This example requires NVIDIA's Hopper Architecture GPU with compute capability 90a\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM90_SUPPORTED)\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = TI(1.0f);\n  TI beta  = TI(0.0f);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  // Initialize the tensors\n  for (int j = 0; j < m*k; ++j) h_A[j] = TA(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < n*k; ++j) h_B[j] = TB(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < m*n; ++j) h_C[j] = TC(0);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM90_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/cluster_launch.hpp\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n\nusing namespace cute;\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,  // (M,K,P)\n          class SmemLayoutB>  // (N,K,P)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<ElementA, cosize_v<SmemLayoutA>> A;\n  alignas(128) cute::ArrayEngine<ElementB, cosize_v<SmemLayoutB>> B;\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class AStride, class ASmemLayout, class TiledCopyA,\n          class TB, class BStride, class BSmemLayout, class TiledCopyB,\n          class TC, class CStride, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, AStride dA, ASmemLayout sA_layout, TiledCopyA copy_a,\n            TB const* B, BStride dB, BSmemLayout sB_layout, TiledCopyB copy_b,\n            TC      * C, CStride dC, TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  CUTE_STATIC_ASSERT_V(size(copy_a) == size(mma));                     // NumThreads\n  CUTE_STATIC_ASSERT_V(size(copy_b) == size(mma));                     // NumThreads\n\n  static_assert(is_static<ASmemLayout>::value);\n  static_assert(is_static<BSmemLayout>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(ASmemLayout{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(BSmemLayout{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(ASmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(BSmemLayout{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,2>(shape_MNK), dA));         // dA strides for shape MK\n  CUTE_STATIC_ASSERT_V(congruent(select<1,2>(shape_MNK), dB));         // dB strides for shape NK\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  Tensor mA = make_tensor(make_gmem_ptr(A), select<0,2>(shape_MNK), dA); // (M,K)\n  Tensor mB = make_tensor(make_gmem_ptr(B), select<1,2>(shape_MNK), dB); // (N,K)\n  Tensor mC = make_tensor(make_gmem_ptr(C), select<0,1>(shape_MNK), dC); // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory tensors\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, ASmemLayout, BSmemLayout>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), ASmemLayout{}); // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), BSmemLayout{}); // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles across the threads\n  //\n\n  ThrCopy thr_copy_a = copy_a.get_slice(threadIdx.x);\n  Tensor tAgA = thr_copy_a.partition_S(gA);                            // (CPY,CPY_M,CPY_K,k)\n  Tensor sA_ = as_position_independent_swizzle_tensor(sA);\n  Tensor tAsA = thr_copy_a.partition_D(sA_);                           // (CPY,CPY_M,CPY_K,PIPE)\n\n  ThrCopy thr_copy_b = copy_b.get_slice(threadIdx.x);\n  Tensor tBgB = thr_copy_b.partition_S(gB);                            // (CPY,CPY_N,CPY_K,k)\n  Tensor sB_ = as_position_independent_swizzle_tensor(sB);\n  Tensor tBsB = thr_copy_b.partition_D(sB_);                           // (CPY,CPY_N,CPY_K,PIPE)\n\n  CUTE_STATIC_ASSERT_V(size<1>(tAgA) == size<1>(tAsA));                // CPY_M\n  CUTE_STATIC_ASSERT_V(size<2>(tAgA) == size<2>(tAsA));                // CPY_K\n  CUTE_STATIC_ASSERT_V(size<1>(tBgB) == size<1>(tBsB));                // CPY_N\n  CUTE_STATIC_ASSERT_V(size<2>(tBgB) == size<2>(tBsB));                // CPY_K\n\n  //\n  // PREFETCH\n  //\n\n  // auto K_PIPE_MAX = size<3>(tAsA);\n\n  // // Total count of tiles\n  // int k_tile_count = size<3>(tAgA);\n  // // Current tile index in gmem to read from\n  // int k_tile_next = 0;\n\n  // // Start async loads for all pipes but the last\n  // CUTE_UNROLL\n  // for (int k_pipe = 0; k_pipe < K_PIPE_MAX-1; ++k_pipe) {\n  //   copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe));\n  //   copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe));\n  //   cp_async_fence();\n  //   --k_tile_count;\n  //   if (k_tile_count > 0) { ++k_tile_next; }\n  // }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n\n  ThrMMA thr_mma = mma.get_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K,PIPE)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate registers for pipelining\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K,PIPE)\n  // Allocate the accumulators -- same size as the projected data\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n\n  CUTE_STATIC_ASSERT_V((size<1>(tCgC) == size<1>(tCsA)));              // MMA_M\n  CUTE_STATIC_ASSERT_V((size<2>(tCgC) == size<1>(tCsB)));              // MMA_N\n  CUTE_STATIC_ASSERT_V((size<2>(tCsA) == size<2>(tCsB)));              // MMA_K\n\n  // Clear the accumulators\n  clear(tCrC);\n\n#if 0\n  if(thread0()) {\n    print(\"  mA : \"); print(  mA); print(\"\\n\");\n    print(\"  gA : \"); print(  gA); print(\"\\n\");\n    print(\"  sA : \"); print(  sA); print(\"\\n\");\n    print(\"tAgA : \"); print(tAgA); print(\"\\n\");\n    print(\"tAsA : \"); print(tAsA); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mB : \"); print(  mB); print(\"\\n\");\n    print(\"  gB : \"); print(  gB); print(\"\\n\");\n    print(\"  sB : \"); print(  sB); print(\"\\n\");\n    print(\"tBgB : \"); print(tBgB); print(\"\\n\");\n    print(\"tBsB : \"); print(tBsB); print(\"\\n\");\n  }\n#endif\n\n#if 0\n  if(thread0()) {\n    print(\"  mC : \"); print(  mC); print(\"\\n\");\n    print(\"  gC : \"); print(  gC); print(\"\\n\");\n    print(\"tCsA : \"); print(tCsA); print(\"\\n\");\n    print(\"tCsB : \"); print(tCsB); print(\"\\n\");\n    print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n    print(\"tCrA : \"); print(tCrA); print(\"\\n\");\n    print(\"tCrB : \"); print(tCrB); print(\"\\n\");\n    print(\"tCrC : \"); print(tCrC); print(\"\\n\");\n  }\n#endif\n\n#if 1\n\n  // Total number of k-tiles\n  auto K_TILE_MAX  = size<3>(tAgA);\n  // Number of pipelined k-tiles in smem\n  auto K_PIPE_MAX  = size<3>(tAsA);\n\n  //\n  // PREFETCH\n  //\n\n  // Prefetch all but the last\n  CUTE_UNROLL\n  for (int k = 0; k < K_PIPE_MAX-1; ++k)\n  {\n    copy(copy_a, tAgA(_,_,_,k), tAsA(_,_,_,k));\n    copy(copy_b, tBgB(_,_,_,k), tBsB(_,_,_,k));\n    cp_async_fence();\n  }\n\n  // Clear the accumulators\n  clear(tCrC);\n\n  __syncthreads();\n\n  //\n  // PIPELINED MAIN LOOP\n  //\n\n  // Current pipe to read from\n  int k_pipe_read  = 0;\n  // Current pipe to write to\n  int k_pipe_write = K_PIPE_MAX-1;\n\n  CUTE_NO_UNROLL\n  for (int k_tile = 0; k_tile < K_TILE_MAX; ++k_tile)\n  {\n    int k_tile_next = k_tile + (K_PIPE_MAX-1);\n    k_tile_next = (k_tile_next >= K_TILE_MAX) ? K_TILE_MAX-1 : k_tile_next;\n\n    //\n    // Copy gmem to smem for k_tile_write\n    //\n\n    copy(copy_a, tAgA(_,_,_,k_tile_next), tAsA(_,_,_,k_pipe_write));\n    copy(copy_b, tBgB(_,_,_,k_tile_next), tBsB(_,_,_,k_pipe_write));\n    cp_async_fence();\n\n    // Advance k_pipe_write\n    ++k_pipe_write;\n    k_pipe_write = (k_pipe_write == K_PIPE_MAX) ? 0 : k_pipe_write;\n\n    //\n    // Compute on k_tile\n    //\n\n    // Wait on all cp.async -- optimize by pipelining to overlap GMEM reads\n    cp_async_wait<0>();\n\n    warpgroup_fence_operand(tCrC);\n    warpgroup_arrive();\n    // (V,M,K) x (V,N,K) => (V,M,N)\n    cute::gemm(mma, tCrA(_,_,_,k_pipe_read), tCrB(_,_,_,k_pipe_read), tCrC);\n    warpgroup_commit_batch();\n    /// Wait on the GMMA barrier for K_PIPE_MMAS (or fewer) outstanding to ensure smem_pipe_write is consumed\n    warpgroup_wait<0>();\n    warpgroup_fence_operand(tCrC);\n\n    // Advance k_pipe_read\n    ++k_pipe_read;\n    k_pipe_read = (k_pipe_read == K_PIPE_MAX) ? 0 : k_pipe_read;\n  }\n\n#endif\n\n  //\n  // Epilogue\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for a NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define NT strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_16,_8>>{}, // Thr layout 32x4 m-major\n                                    Layout<Shape< _8,_1>>{});// Val layout  8x1 m-major\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_16,_8>>{}, // Thr layout 32x4 n-major\n                                    Layout<Shape< _8,_1>>{});// Val layout  8x1 n-major\n\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::MN,GMMA::Major::MN>{});\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(1, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  int  smemBytes = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n\n  auto* kernel_ptr = &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                  TA, decltype(dA), decltype(sA), decltype(copyA),\n                                  TB, decltype(dB), decltype(sB), decltype(copyB),\n                                  TC, decltype(dC), decltype(tiled_mma),\n                                  decltype(alpha), decltype(beta)>;\n\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  // Kernel Launch\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, dA, sA, copyA,\n                                                             B, dB, sB, copyB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_K_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_K_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the thread layouts (static)\n  TiledCopy copyA = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TA>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{}, // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});              // Val layout  1x8\n  TiledCopy copyB = make_tiled_copy(Copy_Atom<SM80_CP_ASYNC_CACHEALWAYS<uint128_t>, TB>{},\n                                    Layout<Shape<_16,_8>,Stride<_8,_1>>{}, // Thr layout 16x8 k-major\n                                    Layout<Shape< _1,_8>>{});              // Val layout  1x8\n\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::K,GMMA::Major::K>{});\n\n#if 0\n  print(copyA);\n  print(copyB);\n  print(mmaC);\n#endif\n\n#if 0\n  print_latex(copyA);\n  print_latex(copyB);\n  print_latex(mmaC);\n#endif\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(1, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n                              &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                           TA, decltype(dA), decltype(sA), decltype(copyA),\n                                           TB, decltype(dB), decltype(sB), decltype(copyB),\n                                           TC, decltype(dC), decltype(tiled_mma),\n                                           decltype(alpha), decltype(beta)>);\n\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n    hipFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  // Kernel Launch\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, dA, sA, copyA,\n                                                             B, dB, sB, copyB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major != 9) {\n    std::cout << \"This example requires NVIDIA's Hopper Architecture GPU with compute capability 90a\" << std::endl;\n    // Return 0 so tests pass if run on unsupported architectures or CUDA Toolkits.\n    return 0;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM90_SUPPORTED)\n\n  int m = 5120;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 5120;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 4096;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = TI(1.0f);\n  TI beta  = TI(0.0f);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  // Initialize the tensors\n  for (int j = 0; j < m*k; ++j) h_A[j] = TA(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < n*k; ++j) h_B[j] = TB(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < m*n; ++j) h_C[j] = TC(0);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM90_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/hopper/wgmma_sm90.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/pipeline/sm90_pipeline.hpp\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n#include \"cutlass/arch/mma_sm90.h\"\n#include \"cutlass/device_kernel.h\"\n\nusing namespace cute;\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,  // (M,K,P)\n          class SmemLayoutB>  // (N,K,P)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<ElementA, cosize_v<SmemLayoutA>> A;\n  alignas(128) cute::ArrayEngine<ElementB, cosize_v<SmemLayoutB>> B;\n\n  uint64_t tma_barrier[size<2>(SmemLayoutA{})];\n  uint64_t mma_barrier[size<2>(SmemLayoutA{})];\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class SmemLayoutA, class TmaA,\n          class TB, class SmemLayoutB, class TmaB,\n          class TC, class CStride, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, CUTLASS_GRID_CONSTANT TmaA const tma_a,\n            TB const* B, CUTLASS_GRID_CONSTANT TmaB const tma_b,\n            TC      * C, CStride dC, TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  static_assert(is_static<SmemLayoutA>::value);\n  static_assert(is_static<SmemLayoutB>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(SmemLayoutA{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(SmemLayoutB{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(SmemLayoutA{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(SmemLayoutB{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  auto [M, N, K] = shape_MNK;\n  Tensor mA = tma_a.get_tma_tensor(make_shape(M,K));                   // (M,K) TMA Tensor\n  Tensor mB = tma_b.get_tma_tensor(make_shape(N,K));                   // (N,K) TMA Tensor\n  Tensor mC = make_tensor(make_gmem_ptr(C), make_shape(M,N), dC);      // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory tensors\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, SmemLayoutA, SmemLayoutB>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), SmemLayoutA{}); // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), SmemLayoutB{}); // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles\n  //\n  // TUTORIAL:\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   The Int<0>, Layout<_1> indicates that the TMAs are not multicasted.\n  //     Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   The group_modes<0,2> transforms the (X,Y,Z)-shaped tensors into ((X,Y),Z)-shaped tensors\n  //     with the understanding that the TMA is responsible for everything in mode-0.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n  //\n\n  auto [tAgA, tAsA] = tma_partition(tma_a, Int<0>{}, Layout<_1>{},\n                                    group_modes<0,2>(sA), group_modes<0,2>(gA));  // (TMA,k) and (TMA,PIPE)\n\n  auto [tBgB, tBsB] = tma_partition(tma_b, Int<0>{}, Layout<_1>{},\n                                    group_modes<0,2>(sB), group_modes<0,2>(gB));  // (TMA,k) and (TMA,PIPE)\n\n  // The TMA is responsible for copying everything in mode-0 of tAsA and tBsB\n  constexpr int tma_transaction_bytes = sizeof(make_tensor_like(tensor<0>(tAsA)))\n                                      + sizeof(make_tensor_like(tensor<0>(tBsB)));\n\n  //\n  // PREFETCH\n  //\n\n  auto K_PIPE_MAX = size<1>(tAsA);\n\n  // Total count of tiles\n  int k_tile_count = size<1>(tAgA);\n  // Current tile index in gmem to read from\n  int k_tile = 0;\n\n  // Initialize Barriers\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n  int lane_predicate = cute::elect_one_sync();\n  uint64_t* producer_mbar = smem.tma_barrier;\n  uint64_t* consumer_mbar = smem.mma_barrier;\n\n  using ProducerBarType = cutlass::arch::ClusterTransactionBarrier;  // TMA\n  using ConsumerBarType = cutlass::arch::ClusterBarrier;             // MMA\n  CUTE_UNROLL\n  for (int pipe = 0; pipe < K_PIPE_MAX; ++pipe) {\n    if ((warp_idx == 0) && lane_predicate) {\n      ProducerBarType::init(&producer_mbar[pipe],   1);\n      ConsumerBarType::init(&consumer_mbar[pipe], 128);\n    }\n  }\n  // Ensure barrier init is complete on all CTAs\n  cluster_sync();\n\n  // Start async loads for all pipes\n  CUTE_UNROLL\n  for (int pipe = 0; pipe < K_PIPE_MAX; ++pipe)\n  {\n    if ((warp_idx == 0) && lane_predicate)\n    {\n      // Set expected Tx Bytes after each reset / init\n      ProducerBarType::arrive_and_expect_tx(&producer_mbar[pipe], tma_transaction_bytes);\n      copy(tma_a.with(producer_mbar[pipe]), tAgA(_,k_tile), tAsA(_,pipe));\n      copy(tma_b.with(producer_mbar[pipe]), tBgB(_,k_tile), tBsB(_,pipe));\n    }\n    --k_tile_count;\n    ++k_tile;\n  }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n  // TUTORIAL:\n  //   The tCrA and tCrB are actually Tensors of MMA Descriptors constructed as views of SMEM.\n  //   The MMA Descriptor generation is automatic via inspection and validation of the SMEM Layouts.\n  //   Because the MMA reads directly from SMEM and the fragments are descriptors rather than registers,\n  //     there is no need for copy(tCsA, tCrA) in the mainloop.\n  //\n\n  ThrMMA thr_mma = mma.get_thread_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K,PIPE)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate accumulators and clear them\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n  clear(tCrC);\n\n  // Allocate \"fragments\"\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K,PIPE)\n\n  //\n  // PIPELINED MAIN LOOP\n  //\n  // TUTORIAL:\n  //   Rather than interleaving the stages and instructions like in SM70 and SM80,\n  //     the SM90 mainloops rely on explicit producer-consumer synchronization\n  //     on the purely async instructions TMA and MMA.\n  //   More advanced pipeline and warp-specialization strategies are available in CUTLASS mainloops.\n  //\n\n  // A PipelineState is a circular pipe index [.index()] and a pipe phase [.phase()]\n  //   that flips each cycle through K_PIPE_MAX.\n  auto write_state = cutlass::PipelineState<K_PIPE_MAX>();             // TMA writes\n  auto read_state  = cutlass::PipelineState<K_PIPE_MAX>();             // MMA  reads\n\n  CUTE_NO_UNROLL\n  while (k_tile_count > -K_PIPE_MAX)\n  {\n    // Wait for Producer to complete\n    int read_pipe = read_state.index();\n    ProducerBarType::wait(&producer_mbar[read_pipe], read_state.phase());\n\n    // MMAs to cover 1 K_TILE\n    warpgroup_arrive();\n    gemm(mma, tCrA(_,_,_,read_pipe), tCrB(_,_,_,read_pipe), tCrC);     // (V,M) x (V,N) => (V,M,N)\n    warpgroup_commit_batch();\n\n    // Wait for all MMAs in a K_TILE to complete\n    warpgroup_wait<0>();\n\n    // Notify that consumption is done\n    ConsumerBarType::arrive(&consumer_mbar[read_pipe]);\n    ++read_state;\n\n    // Only issue new TMA copies if there are more tiles to fetch\n    if ((warp_idx == 0) && lane_predicate && (k_tile_count > 0))\n    {\n      int pipe = write_state.index();\n      // Wait for Consumer to complete consumption\n      ConsumerBarType::wait(&consumer_mbar[pipe], write_state.phase());\n      // Set expected Tx Bytes after each reset / init\n      ProducerBarType::arrive_and_expect_tx(&producer_mbar[pipe], tma_transaction_bytes);\n      copy(tma_a.with(producer_mbar[pipe]), tAgA(_,k_tile), tAsA(_,pipe));\n      copy(tma_b.with(producer_mbar[pipe]), tBgB(_,k_tile), tBsB(_,pipe));\n      ++write_state;\n    }\n    --k_tile_count;\n    ++k_tile;\n  }\n\n  //\n  // Epilogue (unpredicated)\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for an NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<  3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the MMA\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::MN,GMMA::Major::MN>{});\n\n  // Define the TMAs\n  // Create Global memory tensors for TMA inspection\n  Tensor mA = make_tensor(A, make_shape(M,K), dA);\n  Tensor mB = make_tensor(B, make_shape(N,K), dB);\n\n  // Create TMA Atoms with the desired copy operation on the source and destination\n  Copy_Atom tmaA = make_tma_atom(SM90_TMA_LOAD{}, mA, sA(_,_,0), make_shape(bM,bK));\n  Copy_Atom tmaB = make_tma_atom(SM90_TMA_LOAD{}, mB, sB(_,_,0), make_shape(bN,bK));\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(2, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n                              &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                           TA, decltype(sA), decltype(tmaA),\n                                           TB, decltype(sB), decltype(tmaB),\n                                           TC, decltype(dC), decltype(tiled_mma),\n                                           decltype(alpha), decltype(beta)>);\n\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(\n    kernel_ptr,\n    cudaFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  // Kernel Launch\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, tmaA,\n                                                             B, tmaB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        cudaStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_K_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_K_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the MMA\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::K,GMMA::Major::K>{});\n\n  // Define the TMAs\n  // Create Global memory tensors for TMA inspection\n  Tensor mA = make_tensor(A, make_shape(M,K), dA);\n  Tensor mB = make_tensor(B, make_shape(N,K), dB);\n\n  // Create TMA Atoms with the desired copy operation on the source and destination\n  Copy_Atom tmaA = make_tma_atom(SM90_TMA_LOAD{}, mA, sA(_,_,0), make_shape(bM,bK));\n  Copy_Atom tmaB = make_tma_atom(SM90_TMA_LOAD{}, mB, sB(_,_,0), make_shape(bN,bK));\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(2, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  int  smemBytes = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n\n  auto* kernel_ptr = &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                  TA, decltype(sA), decltype(tmaA),\n                                  TB, decltype(sB), decltype(tmaB),\n                                  TC, decltype(dC), decltype(tiled_mma),\n                                  decltype(alpha), decltype(beta)>;\n\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  // Kernel Launch\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, tmaA,\n                                                             B, tmaB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     cudaStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major != 9) {\n    std::cout << \"This example requires NVIDIA's Hopper Architecture GPU with compute capability 90a\\n\" << std::endl;\n    return 0;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM90_SUPPORTED)\n\n  int m = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 256;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 1024;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = TI(1.0f);\n  TI beta  = TI(0.0f);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  // Initialize the tensors\n  for (int j = 0; j < m*k; ++j) h_A[j] = TA(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < n*k; ++j) h_B[j] = TB(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < m*n; ++j) h_C[j] = TC(0);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM90_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n#include \"hip/hip_runtime.h\"\n#include <cstdlib>\n#include <cstdio>\n#include <cassert>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/pipeline/sm90_pipeline.hpp\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n#include \"cutlass/util/helper_cuda.hpp\"\n#include \"cutlass/arch/mma_sm90.h\"\n#include \"cutlass/device_kernel.h\"\n\nusing namespace cute;\n\ntemplate <class ElementA,\n          class ElementB,\n          class SmemLayoutA,  // (M,K,P)\n          class SmemLayoutB>  // (N,K,P)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<ElementA, cosize_v<SmemLayoutA>> A;\n  alignas(128) cute::ArrayEngine<ElementB, cosize_v<SmemLayoutB>> B;\n\n  uint64_t tma_barrier[size<2>(SmemLayoutA{})];\n  uint64_t mma_barrier[size<2>(SmemLayoutA{})];\n};\n\ntemplate <class ProblemShape, class CtaTiler,\n          class TA, class SmemLayoutA, class TmaA,\n          class TB, class SmemLayoutB, class TmaB,\n          class TC, class CStride, class TiledMma,\n          class Alpha, class Beta>\n__global__ static\n__launch_bounds__(decltype(size(TiledMma{}))::value)\nvoid\ngemm_device(ProblemShape shape_MNK, CtaTiler cta_tiler,\n            TA const* A, CUTLASS_GRID_CONSTANT TmaA const tma_a,\n            TB const* B, CUTLASS_GRID_CONSTANT TmaB const tma_b,\n            TC      * C, CStride dC, TiledMma mma,\n            Alpha alpha, Beta beta)\n{\n  // Preconditions\n  CUTE_STATIC_ASSERT_V(rank(shape_MNK) == Int<3>{});                   // (M, N, K)\n  CUTE_STATIC_ASSERT_V(rank(cta_tiler) == Int<3>{});                   // (BLK_M, BLK_N, BLK_K)\n\n  static_assert(is_static<SmemLayoutA>::value);\n  static_assert(is_static<SmemLayoutB>::value);\n\n  CUTE_STATIC_ASSERT_V(size<0>(SmemLayoutA{}) == size<0>(cta_tiler));  // BLK_M\n  CUTE_STATIC_ASSERT_V(size<0>(SmemLayoutB{}) == size<1>(cta_tiler));  // BLK_N\n  CUTE_STATIC_ASSERT_V(size<1>(SmemLayoutA{}) == size<2>(cta_tiler));  // BLK_K\n  CUTE_STATIC_ASSERT_V(size<1>(SmemLayoutB{}) == size<2>(cta_tiler));  // BLK_K\n\n  CUTE_STATIC_ASSERT_V(congruent(select<0,1>(shape_MNK), dC));         // dC strides for shape MN\n\n  //\n  // Full and Tiled Tensors\n  //\n\n  // Represent the full tensors\n  auto [M, N, K] = shape_MNK;\n  Tensor mA = tma_a.get_tma_tensor(make_shape(M,K));                   // (M,K) TMA Tensor\n  Tensor mB = tma_b.get_tma_tensor(make_shape(N,K));                   // (N,K) TMA Tensor\n  Tensor mC = make_tensor(make_gmem_ptr(C), make_shape(M,N), dC);      // (M,N)\n\n  // Get the appropriate blocks for this thread block\n  auto cta_coord = make_coord(blockIdx.x, blockIdx.y, _);              // (m,n,k)\n  Tensor gA = local_tile(mA, cta_tiler, cta_coord, Step<_1, X,_1>{});  // (BLK_M,BLK_K,k)\n  Tensor gB = local_tile(mB, cta_tiler, cta_coord, Step< X,_1,_1>{});  // (BLK_N,BLK_K,k)\n  Tensor gC = local_tile(mC, cta_tiler, cta_coord, Step<_1,_1, X>{});  // (BLK_M,BLK_N)\n\n  // Shared memory tensors\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<TA, TB, SmemLayoutA, SmemLayoutB>;\n  SharedStorage& smem = *reinterpret_cast<SharedStorage*>(shared_memory);\n  Tensor sA = make_tensor(make_smem_ptr(smem.A.begin()), SmemLayoutA{}); // (BLK_M,BLK_K,PIPE)\n  Tensor sB = make_tensor(make_smem_ptr(smem.B.begin()), SmemLayoutB{}); // (BLK_N,BLK_K,PIPE)\n\n  //\n  // Partition the copying of A and B tiles\n  //\n  // TUTORIAL:\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   The Int<0>, Layout<_1> indicates that the TMAs are not multicasted.\n  //     Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   The group_modes<0,2> transforms the (X,Y,Z)-shaped tensors into ((X,Y),Z)-shaped tensors\n  //     with the understanding that the TMA is responsible for everything in mode-0.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n  //\n\n  auto [tAgA, tAsA] = tma_partition(tma_a, Int<0>{}, Layout<_1>{},\n                                    group_modes<0,2>(sA), group_modes<0,2>(gA));  // (TMA,k) and (TMA,PIPE)\n\n  auto [tBgB, tBsB] = tma_partition(tma_b, Int<0>{}, Layout<_1>{},\n                                    group_modes<0,2>(sB), group_modes<0,2>(gB));  // (TMA,k) and (TMA,PIPE)\n\n  // The TMA is responsible for copying everything in mode-0 of tAsA and tBsB\n  constexpr int tma_transaction_bytes = sizeof(make_tensor_like(tensor<0>(tAsA)))\n                                      + sizeof(make_tensor_like(tensor<0>(tBsB)));\n\n  //\n  // PREFETCH\n  //\n\n  auto K_PIPE_MAX = size<1>(tAsA);\n\n  // Total count of tiles\n  int k_tile_count = size<1>(tAgA);\n  // Current tile index in gmem to read from\n  int k_tile = 0;\n\n  // Initialize Barriers\n  int warp_idx = cutlass::canonical_warp_idx_sync();\n  int lane_predicate = cute::elect_one_sync();\n  uint64_t* producer_mbar = smem.tma_barrier;\n  uint64_t* consumer_mbar = smem.mma_barrier;\n\n  using ProducerBarType = cutlass::arch::ClusterTransactionBarrier;  // TMA\n  using ConsumerBarType = cutlass::arch::ClusterBarrier;             // MMA\n  CUTE_UNROLL\n  for (int pipe = 0; pipe < K_PIPE_MAX; ++pipe) {\n    if ((warp_idx == 0) && lane_predicate) {\n      ProducerBarType::init(&producer_mbar[pipe],   1);\n      ConsumerBarType::init(&consumer_mbar[pipe], 128);\n    }\n  }\n  // Ensure barrier init is complete on all CTAs\n  cluster_sync();\n\n  // Start async loads for all pipes\n  CUTE_UNROLL\n  for (int pipe = 0; pipe < K_PIPE_MAX; ++pipe)\n  {\n    if ((warp_idx == 0) && lane_predicate)\n    {\n      // Set expected Tx Bytes after each reset / init\n      ProducerBarType::arrive_and_expect_tx(&producer_mbar[pipe], tma_transaction_bytes);\n      copy(tma_a.with(producer_mbar[pipe]), tAgA(_,k_tile), tAsA(_,pipe));\n      copy(tma_b.with(producer_mbar[pipe]), tBgB(_,k_tile), tBsB(_,pipe));\n    }\n    --k_tile_count;\n    ++k_tile;\n  }\n\n  //\n  // Define A/B partitioning and C accumulators\n  //\n  // TUTORIAL:\n  //   The tCrA and tCrB are actually Tensors of MMA Descriptors constructed as views of SMEM.\n  //   The MMA Descriptor generation is automatic via inspection and validation of the SMEM Layouts.\n  //   Because the MMA reads directly from SMEM and the fragments are descriptors rather than registers,\n  //     there is no need for copy(tCsA, tCrA) in the mainloop.\n  //\n\n  ThrMMA thr_mma = mma.get_thread_slice(threadIdx.x);\n  Tensor tCsA = thr_mma.partition_A(sA);                               // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCsB = thr_mma.partition_B(sB);                               // (MMA,MMA_N,MMA_K,PIPE)\n  Tensor tCgC = thr_mma.partition_C(gC);                               // (MMA,MMA_M,MMA_N)\n\n  // Allocate accumulators and clear them\n  Tensor tCrC = thr_mma.make_fragment_C(tCgC);                         // (MMA,MMA_M,MMA_N)\n  clear(tCrC);\n\n  // Allocate \"fragments\"\n  Tensor tCrA = thr_mma.make_fragment_A(tCsA);                         // (MMA,MMA_M,MMA_K,PIPE)\n  Tensor tCrB = thr_mma.make_fragment_B(tCsB);                         // (MMA,MMA_N,MMA_K,PIPE)\n\n  //\n  // PIPELINED MAIN LOOP\n  //\n  // TUTORIAL:\n  //   Rather than interleaving the stages and instructions like in SM70 and SM80,\n  //     the SM90 mainloops rely on explicit producer-consumer synchronization\n  //     on the purely async instructions TMA and MMA.\n  //   More advanced pipeline and warp-specialization strategies are available in CUTLASS mainloops.\n  //\n\n  // A PipelineState is a circular pipe index [.index()] and a pipe phase [.phase()]\n  //   that flips each cycle through K_PIPE_MAX.\n  auto write_state = cutlass::PipelineState<K_PIPE_MAX>();             // TMA writes\n  auto read_state  = cutlass::PipelineState<K_PIPE_MAX>();             // MMA  reads\n\n  CUTE_NO_UNROLL\n  while (k_tile_count > -K_PIPE_MAX)\n  {\n    // Wait for Producer to complete\n    int read_pipe = read_state.index();\n    ProducerBarType::wait(&producer_mbar[read_pipe], read_state.phase());\n\n    // MMAs to cover 1 K_TILE\n    warpgroup_arrive();\n    gemm(mma, tCrA(_,_,_,read_pipe), tCrB(_,_,_,read_pipe), tCrC);     // (V,M) x (V,N) => (V,M,N)\n    warpgroup_commit_batch();\n\n    // Wait for all MMAs in a K_TILE to complete\n    warpgroup_wait<0>();\n\n    // Notify that consumption is done\n    ConsumerBarType::arrive(&consumer_mbar[read_pipe]);\n    ++read_state;\n\n    // Only issue new TMA copies if there are more tiles to fetch\n    if ((warp_idx == 0) && lane_predicate && (k_tile_count > 0))\n    {\n      int pipe = write_state.index();\n      // Wait for Consumer to complete consumption\n      ConsumerBarType::wait(&consumer_mbar[pipe], write_state.phase());\n      // Set expected Tx Bytes after each reset / init\n      ProducerBarType::arrive_and_expect_tx(&producer_mbar[pipe], tma_transaction_bytes);\n      copy(tma_a.with(producer_mbar[pipe]), tAgA(_,k_tile), tAsA(_,pipe));\n      copy(tma_b.with(producer_mbar[pipe]), tBgB(_,k_tile), tBsB(_,pipe));\n      ++write_state;\n    }\n    --k_tile_count;\n    ++k_tile;\n  }\n\n  //\n  // Epilogue (unpredicated)\n  //\n\n  axpby(alpha, tCrC, beta, tCgC);\n}\n\n// Setup params for an NT GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_nt(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(Int<1>{}, ldA);                      // (dM, dK)\n  auto dB = make_stride(Int<1>{}, ldB);                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<  3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_MN_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the MMA\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::MN,GMMA::Major::MN>{});\n\n  // Define the TMAs\n  // Create Global memory tensors for TMA inspection\n  Tensor mA = make_tensor(A, make_shape(M,K), dA);\n  Tensor mB = make_tensor(B, make_shape(N,K), dB);\n\n  // Create TMA Atoms with the desired copy operation on the source and destination\n  Copy_Atom tmaA = make_tma_atom(SM90_TMA_LOAD{}, mA, sA(_,_,0), make_shape(bM,bK));\n  Copy_Atom tmaB = make_tma_atom(SM90_TMA_LOAD{}, mB, sB(_,_,0), make_shape(bN,bK));\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  int smem_size = int(sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>));\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(2, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smem_size};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n                              &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                           TA, decltype(sA), decltype(tmaA),\n                                           TB, decltype(sB), decltype(tmaB),\n                                           TC, decltype(dC), decltype(tiled_mma),\n                                           decltype(alpha), decltype(beta)>);\n\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n    hipFuncAttributeMaxDynamicSharedMemorySize,\n    smem_size));\n\n  // Kernel Launch\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, tmaA,\n                                                             B, tmaB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n// Setup params for a TN GEMM\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm_tn(int m, int n, int k,\n        Alpha alpha,\n        TA const* A, int ldA,\n        TB const* B, int ldB,\n        Beta beta,\n        TC      * C, int ldC,\n        hipStream_t stream = 0)\n{\n  // Define shapes (dynamic)\n  auto M = int(m);\n  auto N = int(n);\n  auto K = int(k);\n  auto prob_shape = make_shape(M, N, K);                     // (M, N, K)\n\n  // Define TN strides (mixed)\n  auto dA = make_stride(ldA, Int<1>{});                      // (dM, dK)\n  auto dB = make_stride(ldB, Int<1>{});                      // (dN, dK)\n  auto dC = make_stride(Int<1>{}, ldC);                      // (dM, dN)\n\n  // Define CTA tile sizes (static)\n  auto bM = Int<128>{};\n  auto bN = Int<128>{};\n  auto bK = Int< 64>{};\n  auto cta_tiler = make_shape(bM, bN, bK);                   // (BLK_M, BLK_N, BLK_K)\n  auto bP = Int<3>{};  // Pipeline\n\n  // Define the smem layouts (static)\n  auto sA = tile_to_shape(GMMA::Layout_K_SW128_Atom<TA>{}, make_shape(bM,bK,bP));\n  auto sB = tile_to_shape(GMMA::Layout_K_SW128_Atom<TB>{}, make_shape(bN,bK,bP));\n\n  // Define the MMA\n  TiledMMA tiled_mma = make_tiled_mma(SM90_64x64x16_F16F16F16_SS<GMMA::Major::K,GMMA::Major::K>{});\n\n  // Define the TMAs\n  // Create Global memory tensors for TMA inspection\n  Tensor mA = make_tensor(A, make_shape(M,K), dA);\n  Tensor mB = make_tensor(B, make_shape(N,K), dB);\n\n  // Create TMA Atoms with the desired copy operation on the source and destination\n  Copy_Atom tmaA = make_tma_atom(SM90_TMA_LOAD{}, mA, sA(_,_,0), make_shape(bM,bK));\n  Copy_Atom tmaB = make_tma_atom(SM90_TMA_LOAD{}, mB, sB(_,_,0), make_shape(bN,bK));\n\n  //\n  // Setup and Launch\n  //\n\n  // Launch parameter setup\n  dim3 dimBlock(size(tiled_mma));\n  dim3 dimCluster(2, 1, 1);\n  dim3 dimGrid(round_up(size(ceil_div(m, bM)), dimCluster.x),\n               round_up(size(ceil_div(n, bN)), dimCluster.y));\n  int  smemBytes = sizeof(SharedStorage<TA, TB, decltype(sA), decltype(sB)>);\n\n  auto* kernel_ptr = &gemm_device<decltype(prob_shape), decltype(cta_tiler),\n                                  TA, decltype(sA), decltype(tmaA),\n                                  TB, decltype(sB), decltype(tmaB),\n                                  TC, decltype(dC), decltype(tiled_mma),\n                                  decltype(alpha), decltype(beta)>;\n\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  // Kernel Launch\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             prob_shape, cta_tiler,\n                                                             A, tmaA,\n                                                             B, tmaB,\n                                                             C, dC, tiled_mma,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\ntemplate <class TA, class TB, class TC,\n          class Alpha, class Beta>\nvoid\ngemm(char transA, char transB, int m, int n, int k,\n     Alpha alpha,\n     TA const* A, int ldA,\n     TB const* B, int ldB,\n     Beta beta,\n     TC      * C, int ldC,\n     hipStream_t stream = 0)\n{\n  if (transA == 'N' && transB == 'T') {\n    return gemm_nt(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  } else\n  if (transA == 'T' && transB == 'N') {\n    return gemm_tn(m, n, k, alpha, A, ldA, B, ldB, beta, C, ldC, stream);\n  }\n  assert(false && \"Not implemented\");\n}\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if (props.major != 9) {\n    std::cout << \"This example requires NVIDIA's Hopper Architecture GPU with compute capability 90a\\n\" << std::endl;\n    return 0;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM90_SUPPORTED)\n\n  int m = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &m);\n\n  int n = 256;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &n);\n\n  int k = 1024;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &k);\n\n  char transA = 'N';\n  if (argc >= 5)\n    sscanf(argv[4], \"%c\", &transA);\n\n  char transB = 'T';\n  if (argc >= 6)\n    sscanf(argv[5], \"%c\", &transB);\n\n  using TA = cute::half_t;\n  using TB = cute::half_t;\n  using TC = cute::half_t;\n  using TI = cute::half_t;\n\n  TI alpha = TI(1.0f);\n  TI beta  = TI(0.0f);\n\n  thrust::host_vector<TA> h_A(m*k);\n  thrust::host_vector<TB> h_B(n*k);\n  thrust::host_vector<TC> h_C(m*n);\n\n  // Initialize the tensors\n  for (int j = 0; j < m*k; ++j) h_A[j] = TA(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < n*k; ++j) h_B[j] = TB(int((rand() % 2) ? 1 : -1));\n  for (int j = 0; j < m*n; ++j) h_C[j] = TC(0);\n\n  thrust::device_vector<TA> d_A = h_A;\n  thrust::device_vector<TB> d_B = h_B;\n  thrust::device_vector<TC> d_C = h_C;\n\n  double gflops = (2.0*m*n*k) * 1e-9;\n\n  const int timing_iterations = 100;\n  GPU_Clock timer;\n\n  int ldA = 0, ldB = 0, ldC = m;\n\n  if (transA == 'N') {\n    ldA = m;\n  } else if (transA == 'T') {\n    ldA = k;\n  } else {\n    assert(false);\n  }\n\n  if (transB == 'N') {\n    ldB = k;\n  } else if (transB == 'T') {\n    ldB = n;\n  } else {\n    assert(false);\n  }\n\n  // Run once\n  d_C = h_C;\n  gemm(transA, transB, m, n, k,\n       alpha,\n       d_A.data().get(), ldA,\n       d_B.data().get(), ldB,\n       beta,\n       d_C.data().get(), ldC);\n  CUTE_CHECK_LAST();\n  thrust::host_vector<TC> cute_result = d_C;\n\n  // Timing iterations\n  timer.start();\n  for (int i = 0; i < timing_iterations; ++i) {\n    gemm(transA, transB, m, n, k,\n         alpha,\n         d_A.data().get(), ldA,\n         d_B.data().get(), ldB,\n         beta,\n         d_C.data().get(), ldC);\n  }\n  double cute_time = timer.seconds() / timing_iterations;\n  CUTE_CHECK_LAST();\n  printf(\"CUTE_GEMM:     [%6.1f]GFlop/s  (%6.4f)ms\\n\", gflops / cute_time, cute_time*1000);\n\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM90_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/hopper/wgmma_tma_sm90.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 03: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions from tutorial 02_mma_tma_sm100.cu:\n// 1. Introduce ClusterShape for coordinated execution across thread blocks\n// 2. Introduce TMA multicast\n// 3. Enhanced TMA <-> MMA synchronization for cluster-wide operations\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_MNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_MNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Multicast behavior for CTA 1,2 in the cluster\n  //   A multicast            B multicast\n  //    0  1  2  3             0  1  2  3\n  // 0  -  -  -  -          0  -  -  X  -\n  // 1  X  X  X  X          1  -  -  X  -\n  // 2  -  -  -  -          2  -  -  X  -\n  // 3  -  -  -  -          3  -  -  X  -\n  // tma_multicast_mask_A = 0x2222\n  // tma_multicast_mask_B = 0x0F00\n  // mma_multicast_mask_C = 0x2F22\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion\n  int tma_transaction_bytes = sizeof(make_tensor_like(tAsA))\n                            + sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all threads across all CTAs in Cluster observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Set transaction bytes and execute with barrier\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for TMA loads to SMEM to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive_multicast(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  Copy_Atom tma_atom_A = make_tma_atom(\n      SM90_TMA_LOAD_MULTICAST{},       // TMA load operation with multicast\n      mA,                              // Source GMEM tensor\n      sA_layout,                       // Destination SMEM layout\n      select<0,2>(mma_tiler),          // MK Tiler for TMA operation\n      size<2>(cluster_layout_vmnk)     // The number of CTAs in the N-mode for multicasting\n    );\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_8192):(_0,_1)\n  //  ValLayoutDst: (_1,_8192):(_0,_1)\n  //  ValLayoutRef: (_1,_8192):(_0,_1)\n  //  ValueType:    16b\n\n  Copy_Atom tma_atom_B = make_tma_atom(\n      SM90_TMA_LOAD_MULTICAST{},      // TMA load operation with multicast\n      mB,                             // Source GMEM tensor\n      sB_layout,                      // Destination SMEM layout\n      select<1,2>(mma_tiler),         // NK Tiler for TMA operation\n      size<1>(cluster_layout_vmnk)    // The number of CTAs in the M-mode for multicasting\n    );\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_16384):(_0,_1)\n  //  ValLayoutDst: (_1,_16384):(_0,_1)\n  //  ValLayoutRef: (_1,_16384):(_0,_1)\n  //  ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 03: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions from tutorial 02_mma_tma_sm100.cu:\n// 1. Introduce ClusterShape for coordinated execution across thread blocks\n// 2. Introduce TMA multicast\n// 3. Enhanced TMA <-> MMA synchronization for cluster-wide operations\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_MNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_MNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Multicast behavior for CTA 1,2 in the cluster\n  //   A multicast            B multicast\n  //    0  1  2  3             0  1  2  3\n  // 0  -  -  -  -          0  -  -  X  -\n  // 1  X  X  X  X          1  -  -  X  -\n  // 2  -  -  -  -          2  -  -  X  -\n  // 3  -  -  -  -          3  -  -  X  -\n  // tma_multicast_mask_A = 0x2222\n  // tma_multicast_mask_B = 0x0F00\n  // mma_multicast_mask_C = 0x2F22\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion\n  int tma_transaction_bytes = sizeof(make_tensor_like(tAsA))\n                            + sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all threads across all CTAs in Cluster observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Set transaction bytes and execute with barrier\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for TMA loads to SMEM to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive_multicast(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  Copy_Atom tma_atom_A = make_tma_atom(\n      SM90_TMA_LOAD_MULTICAST{},       // TMA load operation with multicast\n      mA,                              // Source GMEM tensor\n      sA_layout,                       // Destination SMEM layout\n      select<0,2>(mma_tiler),          // MK Tiler for TMA operation\n      size<2>(cluster_layout_vmnk)     // The number of CTAs in the N-mode for multicasting\n    );\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_8192):(_0,_1)\n  //  ValLayoutDst: (_1,_8192):(_0,_1)\n  //  ValLayoutRef: (_1,_8192):(_0,_1)\n  //  ValueType:    16b\n\n  Copy_Atom tma_atom_B = make_tma_atom(\n      SM90_TMA_LOAD_MULTICAST{},      // TMA load operation with multicast\n      mB,                             // Source GMEM tensor\n      sB_layout,                      // Destination SMEM layout\n      select<1,2>(mma_tiler),         // NK Tiler for TMA operation\n      size<1>(cluster_layout_vmnk)    // The number of CTAs in the M-mode for multicasting\n    );\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_16384):(_0,_1)\n  //  ValLayoutDst: (_1,_16384):(_0,_1)\n  //  ValLayoutRef: (_1,_16384):(_0,_1)\n  //  ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/blackwell/03_mma_tma_multicast_sm100.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 01: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// The goal of this tutorial is to show the CuTe interface for tcgen05.mma and tcgen05.ld operations.\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// This GEMM kernel performs the following steps:\n// 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) for one MmaTile\n//    using auto-vectorizing copy operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;   // Barrier to track MMA computation on SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   gmem_ptr[16b](GMEM_ADDR_A) o (512,256):(256,_1)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   gmem_ptr[16b](GMEM_ADDR_B) o (1024,256):(256,_1)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   gmem_ptr[16b](GMEM_ADDR_A + offset_for_mma_tile) o (_128,_64,4):(256,_1,_64)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   gmem_ptr[16b](GMEM_ADDR_B + offset_for_mma_tile) o (_256,_64,4):(256,_1,_64)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(1024,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(1024,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n  // Note: Partitioned tensors use tXgY naming convention:\n  //  tXgY -> The partitioning pattern tX applied to tensor gY\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   gmem_ptr[16b](GMEM_ADDR_A + offset_for_mma_tile + offset_for_mma) o ((_128,_16),_1,_4,4):((256,_1),_0,_16,_64)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   gmem_ptr[16b](GMEM_ADDR_B + offset_for_mma_tile + offset_for_mma) o ((_256,_16),_1,_4,4):((256,_1),_0,_16,_64)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((1024,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((1024,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  __syncthreads();                // Make sure all threads observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // Using auto-vectorized copy operation:\n    // - Utilizes 128 threads for parallel data transfer\n    // - Copy operations are distributed efficiently across all threads\n    // - CuTe can automatically determine optimal vector width\n    cooperative_copy<128>(threadIdx.x, tCgA(_,_,_,k_tile), tCsA); // Load MmaTile_M x MmaTile_K A tile\n    cooperative_copy<128>(threadIdx.x, tCgB(_,_,_,k_tile), tCsB); // Load MmaTile_N x MmaTile_K B tile\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for loads to SMEM to complete with __syncthreads()\n    __syncthreads();\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive(&shared_storage.mma_barrier);\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<1>{}, Int<1>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA), decltype(mB), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA, mB, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 01: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n// The goal of this tutorial is to show the CuTe interface for tcgen05.mma and tcgen05.ld operations.\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// This GEMM kernel performs the following steps:\n// 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) for one MmaTile\n//    using auto-vectorizing copy operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;   // Barrier to track MMA computation on SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   gmem_ptr[16b](GMEM_ADDR_A) o (512,256):(256,_1)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   gmem_ptr[16b](GMEM_ADDR_B) o (1024,256):(256,_1)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   gmem_ptr[16b](GMEM_ADDR_A + offset_for_mma_tile) o (_128,_64,4):(256,_1,_64)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   gmem_ptr[16b](GMEM_ADDR_B + offset_for_mma_tile) o (_256,_64,4):(256,_1,_64)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(1024,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(1024,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n  // Note: Partitioned tensors use tXgY naming convention:\n  //  tXgY -> The partitioning pattern tX applied to tensor gY\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   gmem_ptr[16b](GMEM_ADDR_A + offset_for_mma_tile + offset_for_mma) o ((_128,_16),_1,_4,4):((256,_1),_0,_16,_64)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   gmem_ptr[16b](GMEM_ADDR_B + offset_for_mma_tile + offset_for_mma) o ((_256,_16),_1,_4,4):((256,_1),_0,_16,_64)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((1024,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((1024,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  __syncthreads();                // Make sure all threads observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // Using auto-vectorized copy operation:\n    // - Utilizes 128 threads for parallel data transfer\n    // - Copy operations are distributed efficiently across all threads\n    // - CuTe can automatically determine optimal vector width\n    cooperative_copy<128>(threadIdx.x, tCgA(_,_,_,k_tile), tCsA); // Load MmaTile_M x MmaTile_K A tile\n    cooperative_copy<128>(threadIdx.x, tCgB(_,_,_,k_tile), tCsB); // Load MmaTile_N x MmaTile_K B tile\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for loads to SMEM to complete with __syncthreads()\n    __syncthreads();\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive(&shared_storage.mma_barrier);\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<1>{}, Int<1>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA), decltype(mB), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA, mB, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/blackwell/01_mma_sm100.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 05: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions to tutorial 04_mma_tma_2sm_sm100.cu:\n// 1. Demonstrate using TMA instructions in the epilogue\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA.2SM load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using 2SM tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to shared memory (SMEM) with TMA.\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from shared memory (SMEM) to global memory (GMEM) with TMA.\n//\n// SM100 2SM tcgen05.mma instructions operate as follows:\n// - Mma is launched by only one SM\n//    With 2SM MMA instructions, only 1 of the 2 CTAs collaborating on MMA executes the instruction.\n//    We call the collaborating CTAs, peer CTAs. And the CTA executing the MMA instruction is called leader CTA.\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 256x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A, B, C, and D matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class TypeC,           // Tensor C data type\n          class TypeD,           // Tensor D data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout,     // (MmaB, NumMma_N, NumMma_K, ...)\n          class CSmemLayout,     // EpiTile_M, EpiTile_N\n          class DSmemLayout>     // EpiTile_M, EpiTile_N\nstruct SharedStorage\n{\n  alignas(128) union {\n    alignas(128) struct {\n      alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n      alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n    } mainloop;\n    alignas(128) cute::ArrayEngine<TypeC, cute::cosize_v<CSmemLayout>> C;\n    alignas(128) cute::ArrayEngine<TypeD, cute::cosize_v<DSmemLayout>> D;\n  } tensors;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(tensors.mainloop.A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(tensors.mainloop.B.begin()), BSmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sC() { return make_tensor(make_smem_ptr(tensors.C.begin()), CSmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sD() { return make_tensor(make_smem_ptr(tensors.D.begin()), DSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class EpiTiler_MN, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB, class TmaAtomC, class TmaAtomD,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            EpiTiler_MN epi_tiler_mn,        // <EpiTile_M, EpiTile_N>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            CUTE_GRID_CONSTANT TmaAtomC const tma_atom_C,\n            CUTE_GRID_CONSTANT TmaAtomD const tma_atom_D,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator2Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_VMNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_VMNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Computation of the multicast masks must take into account the Peer CTA for TMA.2SM\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n  auto elect_one_cta  = get<0>(cta_in_cluster_coord_vmnk) == Int<0>{};\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion, accounting for TMA.2SM\n  int tma_transaction_bytes = size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tAsA))\n                            + size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM should be initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all CTAs in Cluster observe barrier init and TMEM alloc.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Both peer and leader CTAs initiate TMA loads\n    // - Set expected transaction bytes. For 2SM TMA instructions, the transaction bytes counts both CTAs.\n    // - Although TMAs are initiated by both peer and leader CTAs, the barrier is only set and waited by the leader CTA.\n    // - Initiate asynchronous transfers with a multicast mask that includes all CTAs that participate in multicast.\n    if (elect_one_warp && elect_one_thr) { // TMA loads are executed by one thread\n      if (elect_one_cta) { // Only the leader CTA waits for TMA transactions\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes); // Set the expected transaction bytes for the TMA loads\n      }\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    if (elect_one_cta) {\n      // Wait for TMA loads to complete on leader CTAs\n      cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n      tma_barrier_phase_bit ^= 1;\n\n      // tcgen05.mma instructions require single-thread execution:\n      // - Only one warp performs the MMA-related loop operations\n      // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n      // - No explicit elect_one_sync region is needed from the user\n      if (elect_one_warp) {\n        // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n        for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n            gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n            tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n        }\n        // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n        cutlass::arch::umma_arrive_multicast_2x1SM(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n      }\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Apply rank-2 epilogue tiler to rank-2 MMA_V mode\n  auto epi_tiler_v = make_tile(epi_tiler_mn);               // (EpiTile)\n  Tensor tAcc_epi = zipped_divide(tCtAcc, epi_tiler_v);     // (EpiTile,NumTiles)\n  Tensor gC_epi   = zipped_divide(tCgC,   epi_tiler_v);     // (EpiTile,NumTiles)\n  Tensor gD_epi   = zipped_divide(tCgD,   epi_tiler_v);     // (EpiTile,NumTiles)\n\n  // Construct corresponding SMEM tensors\n  Tensor sC_epi = shared_storage.tensor_sC();               // (EpiTile)\n  Tensor sD_epi = shared_storage.tensor_sD();               // (EpiTile)\n\n  // Partition for TMA\n  auto [tGS_gC, tGS_sC] = tma_partition(tma_atom_C, sC_epi, gC_epi); // (GMEM -> SMEM)\n  auto [tSG_gD, tSG_sD] = tma_partition(tma_atom_D, sD_epi, gD_epi); // (SMEM -> GMEM)\n\n  // Reset transaction bytes for C load\n  tma_transaction_bytes = sizeof(make_tensor_like(tGS_sC));\n\n  // Partition for TMEM accumulators load (TMEM -> RMEM)\n  TiledCopy t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tAcc_epi(_,_0{}));\n  ThrCopy   thr_t2r  = t2r_copy.get_slice(threadIdx.x);\n  Tensor tTR_tAcc = thr_t2r.partition_S(tAcc_epi);          // (TmemCpy,NumTmemCpy,NumTiles)\n  Tensor tTR_sC   = thr_t2r.partition_D(sC_epi);            // (TmemCpy,NumTmemCpy)\n  Tensor tTR_sD   = thr_t2r.partition_D(sD_epi);            // (TmemCpy,NumTmemCpy)\n  // Allocate register tensors\n  Tensor tTR_rC = make_tensor_like(tTR_sC);                 // (TmemCpy,NumTmemCpy)\n  Tensor tTR_rD = make_fragment_like(tTR_sD);               // (TmemCpy,NumTmemCpy)\n\n  // Loop over the epilogue tiles\n  CUTE_UNROLL\n  for (int epi_tile_idx = 0; epi_tile_idx < size<2>(tTR_tAcc); ++epi_tile_idx) {\n    // TMA Load C:  GMEM -> SMEM\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_C.with(shared_storage.tma_barrier, 0 /*no multicast*/), tGS_gC(_,epi_tile_idx), tGS_sC);\n    }\n    // All threads wait for C TMA load to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // Load C:  SMEM -> RMEM\n    copy_aligned(tTR_sC, tTR_rC);\n\n    // Load Acc:  TMEM -> RMEM\n    copy(t2r_copy, tTR_tAcc(_,_,epi_tile_idx), tTR_rD);\n\n    // Compute D = beta * C + alpha * (A*B)\n    axpby(beta, tTR_rC, alpha, tTR_rD);\n\n    // Store D:  RMEM -> SMEM\n    __syncthreads(); // Ensure C loads are finished before reusing smem (unnecessary if smem layouts match)\n    copy_aligned(tTR_rD, tTR_sD);\n\n    // TMA Store D:  SMEM -> GMEM\n    tma_store_fence(); // Ensure D smem stores are visible to TMA\n    __syncthreads(); // Ensure all threads have issued fence\n    if (elect_one_warp && elect_one_thr) {\n      copy(tma_atom_D, tSG_sD, tSG_gD(_,epi_tile_idx));\n      tma_store_arrive(); // issuing thread commits D TMA store\n      tma_store_wait<0>(); // issuing thread waits for D TMA store to complete\n    }\n    __syncthreads(); // All threads sync with issuing thread\n  }\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_2x1SM_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                                 256, 256,                            // Mma M and N dimensions\n                                                                 UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_2,_1,_1,_1):(_1,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _2:_1\n  //   Shape_MNK:  (_256,_256,_16)                      // MmaM, MmaN, MmaK (MmaK is constant for each instr.)\n  //   LayoutA_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_2,(_128,_256)):(_128,(_1,_256))    // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  //\n  // Epilogue parameters\n  //\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_N) to post-partitioned ((MmaM,MmaN), NumMma_M, NumMma_N)\n  auto mma_shape_C = partition_shape_C(tiled_mma, make_shape(size<0>(mma_tiler), size<1>(mma_tiler)));\n\n  // For TMA epilogue performance it may be beneficial to iterate over the output in smaller tiles than the MMA tile\n  auto epi_tiler = make_tile(size<0,0>(mma_shape_C), size<0,1>(mma_shape_C) / Int<4>{});  // 4 TMA copies per CTA per MMA tile\n\n  // SMEM layouts for C and D should match the epilogue tile\n  auto sC_layout_mn = tile_to_shape(UMMA::Layout_K_SW128_Atom<TypeC>{}, // MMA K-major is equivalent to epilogue N-major\n                                    make_shape(size<0>(epi_tiler), size<1>(epi_tiler)));\n  auto sC_layout = group<0,2>(sC_layout_mn); // Group modes for tma_partition\n\n  auto sD_layout_mn = tile_to_shape(UMMA::Layout_K_SW128_Atom<TypeD>{}, // MMA K-major is equivalent to epilogue N-major\n                                    make_shape(size<0>(epi_tiler), size<1>(epi_tiler)));\n  auto sD_layout = group<0,2>(sD_layout_mn); // Group modes for tma_partition\n\n  print(\"sC_layout:\\t\"); print(sC_layout); print(\"\\n\");      // sC_layout:   Sw<3,4,3> o smem_ptr[32b](unset) o ((_8,_16),(_32,_2)):((_32,_256),(_1,_4096))\n  print(\"sD_layout:\\t\"); print(sD_layout); print(\"\\n\");      // sD_layout:   Sw<3,4,3> o smem_ptr[32b](unset) o ((_8,_16),(_32,_2)):((_32,_256),(_1,_4096))\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, TypeC, TypeD,\n                                    decltype(sA_layout), decltype(sB_layout),\n                                    decltype(sC_layout), decltype(sD_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_A = make_tma_atom_A_sm100(\n      SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n      mA,                             // Source GMEM tensor\n      sA_layout,                      // Destination SMEM layout\n      mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n      tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n      cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                      //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _2:_1\n  //  ValLayoutSrc: (_2,_8192):(_8192,_1)\n  //  ValLayoutDst: (_2,_8192):(_8192,_1)\n  //  ValLayoutRef: (_2,_8192):(_8192,_1)\n  //  ValueType:    16b\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_B = make_tma_atom_B_sm100(\n    SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n    mB,                             // Source GMEM tensor\n    sB_layout,                      // Destination SMEM layout\n    mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n    tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n    cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                    //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  // ThrID:        _2:_1\n  // ValLayoutSrc: (_2,_8192):(_8192,_1)\n  // ValLayoutDst: (_2,_8192):(_8192,_1)\n  // ValLayoutRef: (_2,_8192):(_8192,_1)\n  // ValueType:    16b\n\n  Copy_Atom tma_atom_C = make_tma_atom(\n        SM90_TMA_LOAD{},            // TMA load operation\n        mC,                         // Source GMEM tensor\n        sC_layout,                  // Destination SMEM layout\n        epi_tiler);                 // MN Tiler for epilogue\n  Tensor mC_tma = tma_atom_C.get_tma_tensor(shape(mC));   // (Gemm_M, Gemm_N)\n\n  print(\"tma_atom_C:\\t\"); print(tma_atom_C); print(\"\\n\");\n  // tma_atom_C:     Copy_Atom\n  //   ThrID:        _1:_0\n  //   ValLayoutSrc: (_1,_4096):(_0,_1)\n  //   ValLayoutDst: (_1,_4096):(_0,_1)\n  //   ValLayoutRef: (_1,_4096):(_0,_1)\n  //   ValueType:    32b\n\n  Copy_Atom tma_atom_D = make_tma_atom(\n        SM90_TMA_STORE{},           // TMA store operation\n        mD,                         // Destination GMEM tensor\n        sD_layout,                  // Source SMEM layout\n        epi_tiler);                 // MN Tiler for epilogue\n  Tensor mD_tma = tma_atom_D.get_tma_tensor(shape(mD));   // (Gemm_M, Gemm_N)\n\n  print(\"tma_atom_D:\\t\"); print(tma_atom_D); print(\"\\n\");\n  // tma_atom_D:     Copy_Atom\n  //   ThrID:        _1:_0\n  //   ValLayoutSrc: (_1,_4096):(_0,_1)\n  //   ValLayoutDst: (_1,_4096):(_0,_1)\n  //   ValLayoutRef: (_1,_4096):(_0,_1)\n  //   ValueType:    32b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC_tma), decltype(mD_tma),\n                                  decltype(mma_tiler), decltype(epi_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), decltype(tma_atom_C), decltype(tma_atom_D), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC_tma, mD_tma,\n                                                             mma_tiler, epi_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B, tma_atom_C, tma_atom_D,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 05: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions to tutorial 04_mma_tma_2sm_sm100.cu:\n// 1. Demonstrate using TMA instructions in the epilogue\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA.2SM load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using 2SM tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to shared memory (SMEM) with TMA.\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from shared memory (SMEM) to global memory (GMEM) with TMA.\n//\n// SM100 2SM tcgen05.mma instructions operate as follows:\n// - Mma is launched by only one SM\n//    With 2SM MMA instructions, only 1 of the 2 CTAs collaborating on MMA executes the instruction.\n//    We call the collaborating CTAs, peer CTAs. And the CTA executing the MMA instruction is called leader CTA.\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 256x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A, B, C, and D matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class TypeC,           // Tensor C data type\n          class TypeD,           // Tensor D data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout,     // (MmaB, NumMma_N, NumMma_K, ...)\n          class CSmemLayout,     // EpiTile_M, EpiTile_N\n          class DSmemLayout>     // EpiTile_M, EpiTile_N\nstruct SharedStorage\n{\n  alignas(128) union {\n    alignas(128) struct {\n      alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n      alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n    } mainloop;\n    alignas(128) cute::ArrayEngine<TypeC, cute::cosize_v<CSmemLayout>> C;\n    alignas(128) cute::ArrayEngine<TypeD, cute::cosize_v<DSmemLayout>> D;\n  } tensors;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(tensors.mainloop.A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(tensors.mainloop.B.begin()), BSmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sC() { return make_tensor(make_smem_ptr(tensors.C.begin()), CSmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sD() { return make_tensor(make_smem_ptr(tensors.D.begin()), DSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class EpiTiler_MN, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB, class TmaAtomC, class TmaAtomD,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            EpiTiler_MN epi_tiler_mn,        // <EpiTile_M, EpiTile_N>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            CUTE_GRID_CONSTANT TmaAtomC const tma_atom_C,\n            CUTE_GRID_CONSTANT TmaAtomD const tma_atom_D,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator2Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_VMNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_VMNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Computation of the multicast masks must take into account the Peer CTA for TMA.2SM\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n  auto elect_one_cta  = get<0>(cta_in_cluster_coord_vmnk) == Int<0>{};\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion, accounting for TMA.2SM\n  int tma_transaction_bytes = size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tAsA))\n                            + size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM should be initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all CTAs in Cluster observe barrier init and TMEM alloc.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Both peer and leader CTAs initiate TMA loads\n    // - Set expected transaction bytes. For 2SM TMA instructions, the transaction bytes counts both CTAs.\n    // - Although TMAs are initiated by both peer and leader CTAs, the barrier is only set and waited by the leader CTA.\n    // - Initiate asynchronous transfers with a multicast mask that includes all CTAs that participate in multicast.\n    if (elect_one_warp && elect_one_thr) { // TMA loads are executed by one thread\n      if (elect_one_cta) { // Only the leader CTA waits for TMA transactions\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes); // Set the expected transaction bytes for the TMA loads\n      }\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    if (elect_one_cta) {\n      // Wait for TMA loads to complete on leader CTAs\n      cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n      tma_barrier_phase_bit ^= 1;\n\n      // tcgen05.mma instructions require single-thread execution:\n      // - Only one warp performs the MMA-related loop operations\n      // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n      // - No explicit elect_one_sync region is needed from the user\n      if (elect_one_warp) {\n        // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n        for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n            gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n            tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n        }\n        // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n        cutlass::arch::umma_arrive_multicast_2x1SM(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n      }\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Apply rank-2 epilogue tiler to rank-2 MMA_V mode\n  auto epi_tiler_v = make_tile(epi_tiler_mn);               // (EpiTile)\n  Tensor tAcc_epi = zipped_divide(tCtAcc, epi_tiler_v);     // (EpiTile,NumTiles)\n  Tensor gC_epi   = zipped_divide(tCgC,   epi_tiler_v);     // (EpiTile,NumTiles)\n  Tensor gD_epi   = zipped_divide(tCgD,   epi_tiler_v);     // (EpiTile,NumTiles)\n\n  // Construct corresponding SMEM tensors\n  Tensor sC_epi = shared_storage.tensor_sC();               // (EpiTile)\n  Tensor sD_epi = shared_storage.tensor_sD();               // (EpiTile)\n\n  // Partition for TMA\n  auto [tGS_gC, tGS_sC] = tma_partition(tma_atom_C, sC_epi, gC_epi); // (GMEM -> SMEM)\n  auto [tSG_gD, tSG_sD] = tma_partition(tma_atom_D, sD_epi, gD_epi); // (SMEM -> GMEM)\n\n  // Reset transaction bytes for C load\n  tma_transaction_bytes = sizeof(make_tensor_like(tGS_sC));\n\n  // Partition for TMEM accumulators load (TMEM -> RMEM)\n  TiledCopy t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tAcc_epi(_,_0{}));\n  ThrCopy   thr_t2r  = t2r_copy.get_slice(threadIdx.x);\n  Tensor tTR_tAcc = thr_t2r.partition_S(tAcc_epi);          // (TmemCpy,NumTmemCpy,NumTiles)\n  Tensor tTR_sC   = thr_t2r.partition_D(sC_epi);            // (TmemCpy,NumTmemCpy)\n  Tensor tTR_sD   = thr_t2r.partition_D(sD_epi);            // (TmemCpy,NumTmemCpy)\n  // Allocate register tensors\n  Tensor tTR_rC = make_tensor_like(tTR_sC);                 // (TmemCpy,NumTmemCpy)\n  Tensor tTR_rD = make_fragment_like(tTR_sD);               // (TmemCpy,NumTmemCpy)\n\n  // Loop over the epilogue tiles\n  CUTE_UNROLL\n  for (int epi_tile_idx = 0; epi_tile_idx < size<2>(tTR_tAcc); ++epi_tile_idx) {\n    // TMA Load C:  GMEM -> SMEM\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_C.with(shared_storage.tma_barrier, 0 /*no multicast*/), tGS_gC(_,epi_tile_idx), tGS_sC);\n    }\n    // All threads wait for C TMA load to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // Load C:  SMEM -> RMEM\n    copy_aligned(tTR_sC, tTR_rC);\n\n    // Load Acc:  TMEM -> RMEM\n    copy(t2r_copy, tTR_tAcc(_,_,epi_tile_idx), tTR_rD);\n\n    // Compute D = beta * C + alpha * (A*B)\n    axpby(beta, tTR_rC, alpha, tTR_rD);\n\n    // Store D:  RMEM -> SMEM\n    __syncthreads(); // Ensure C loads are finished before reusing smem (unnecessary if smem layouts match)\n    copy_aligned(tTR_rD, tTR_sD);\n\n    // TMA Store D:  SMEM -> GMEM\n    tma_store_fence(); // Ensure D smem stores are visible to TMA\n    __syncthreads(); // Ensure all threads have issued fence\n    if (elect_one_warp && elect_one_thr) {\n      copy(tma_atom_D, tSG_sD, tSG_gD(_,epi_tile_idx));\n      tma_store_arrive(); // issuing thread commits D TMA store\n      tma_store_wait<0>(); // issuing thread waits for D TMA store to complete\n    }\n    __syncthreads(); // All threads sync with issuing thread\n  }\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_2x1SM_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                                 256, 256,                            // Mma M and N dimensions\n                                                                 UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_2,_1,_1,_1):(_1,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _2:_1\n  //   Shape_MNK:  (_256,_256,_16)                      // MmaM, MmaN, MmaK (MmaK is constant for each instr.)\n  //   LayoutA_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_2,(_128,_256)):(_128,(_1,_256))    // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  //\n  // Epilogue parameters\n  //\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_N) to post-partitioned ((MmaM,MmaN), NumMma_M, NumMma_N)\n  auto mma_shape_C = partition_shape_C(tiled_mma, make_shape(size<0>(mma_tiler), size<1>(mma_tiler)));\n\n  // For TMA epilogue performance it may be beneficial to iterate over the output in smaller tiles than the MMA tile\n  auto epi_tiler = make_tile(size<0,0>(mma_shape_C), size<0,1>(mma_shape_C) / Int<4>{});  // 4 TMA copies per CTA per MMA tile\n\n  // SMEM layouts for C and D should match the epilogue tile\n  auto sC_layout_mn = tile_to_shape(UMMA::Layout_K_SW128_Atom<TypeC>{}, // MMA K-major is equivalent to epilogue N-major\n                                    make_shape(size<0>(epi_tiler), size<1>(epi_tiler)));\n  auto sC_layout = group<0,2>(sC_layout_mn); // Group modes for tma_partition\n\n  auto sD_layout_mn = tile_to_shape(UMMA::Layout_K_SW128_Atom<TypeD>{}, // MMA K-major is equivalent to epilogue N-major\n                                    make_shape(size<0>(epi_tiler), size<1>(epi_tiler)));\n  auto sD_layout = group<0,2>(sD_layout_mn); // Group modes for tma_partition\n\n  print(\"sC_layout:\\t\"); print(sC_layout); print(\"\\n\");      // sC_layout:   Sw<3,4,3> o smem_ptr[32b](unset) o ((_8,_16),(_32,_2)):((_32,_256),(_1,_4096))\n  print(\"sD_layout:\\t\"); print(sD_layout); print(\"\\n\");      // sD_layout:   Sw<3,4,3> o smem_ptr[32b](unset) o ((_8,_16),(_32,_2)):((_32,_256),(_1,_4096))\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, TypeC, TypeD,\n                                    decltype(sA_layout), decltype(sB_layout),\n                                    decltype(sC_layout), decltype(sD_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_A = make_tma_atom_A_sm100(\n      SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n      mA,                             // Source GMEM tensor\n      sA_layout,                      // Destination SMEM layout\n      mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n      tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n      cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                      //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _2:_1\n  //  ValLayoutSrc: (_2,_8192):(_8192,_1)\n  //  ValLayoutDst: (_2,_8192):(_8192,_1)\n  //  ValLayoutRef: (_2,_8192):(_8192,_1)\n  //  ValueType:    16b\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_B = make_tma_atom_B_sm100(\n    SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n    mB,                             // Source GMEM tensor\n    sB_layout,                      // Destination SMEM layout\n    mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n    tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n    cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                    //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  // ThrID:        _2:_1\n  // ValLayoutSrc: (_2,_8192):(_8192,_1)\n  // ValLayoutDst: (_2,_8192):(_8192,_1)\n  // ValLayoutRef: (_2,_8192):(_8192,_1)\n  // ValueType:    16b\n\n  Copy_Atom tma_atom_C = make_tma_atom(\n        SM90_TMA_LOAD{},            // TMA load operation\n        mC,                         // Source GMEM tensor\n        sC_layout,                  // Destination SMEM layout\n        epi_tiler);                 // MN Tiler for epilogue\n  Tensor mC_tma = tma_atom_C.get_tma_tensor(shape(mC));   // (Gemm_M, Gemm_N)\n\n  print(\"tma_atom_C:\\t\"); print(tma_atom_C); print(\"\\n\");\n  // tma_atom_C:     Copy_Atom\n  //   ThrID:        _1:_0\n  //   ValLayoutSrc: (_1,_4096):(_0,_1)\n  //   ValLayoutDst: (_1,_4096):(_0,_1)\n  //   ValLayoutRef: (_1,_4096):(_0,_1)\n  //   ValueType:    32b\n\n  Copy_Atom tma_atom_D = make_tma_atom(\n        SM90_TMA_STORE{},           // TMA store operation\n        mD,                         // Destination GMEM tensor\n        sD_layout,                  // Source SMEM layout\n        epi_tiler);                 // MN Tiler for epilogue\n  Tensor mD_tma = tma_atom_D.get_tma_tensor(shape(mD));   // (Gemm_M, Gemm_N)\n\n  print(\"tma_atom_D:\\t\"); print(tma_atom_D); print(\"\\n\");\n  // tma_atom_D:     Copy_Atom\n  //   ThrID:        _1:_0\n  //   ValLayoutSrc: (_1,_4096):(_0,_1)\n  //   ValLayoutDst: (_1,_4096):(_0,_1)\n  //   ValLayoutRef: (_1,_4096):(_0,_1)\n  //   ValueType:    32b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC_tma), decltype(mD_tma),\n                                  decltype(mma_tiler), decltype(epi_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), decltype(tma_atom_C), decltype(tma_atom_D), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC_tma, mD_tma,\n                                                             mma_tiler, epi_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B, tma_atom_C, tma_atom_D,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/blackwell/05_mma_tma_epi_sm100.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 04: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions to tutorial 03_mma_tma_multicast_sm100.cu:\n// 1. Introduce 2SM tcgen05.mma instructions\n// 2. Introduce 2SM TMA instructions\n// 3. Demonstrate TMA multicast pattern specialized for 2SM instructions for loading A and B matrices\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA.2SM load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using 2SM tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 2SM tcgen05.mma instructions operate as follows:\n// - Mma is launched by only one SM\n//    With 2SM MMA instructions, only 1 of the 2 CTAs collaborating on MMA executes the instruction.\n//    We call the collaborating CTAs, peer CTAs. And the CTA executing the MMA instruction is called leader CTA.\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 256x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator2Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_VMNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_VMNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Computation of the multicast masks must take into account the Peer CTA for TMA.2SM\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n  auto elect_one_cta  = get<0>(cta_in_cluster_coord_vmnk) == Int<0>{};\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion, accounting for TMA.2SM\n  int tma_transaction_bytes = size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tAsA))\n                            + size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM should be initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all CTAs in Cluster observe barrier init and TMEM alloc.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Both peer and leader CTAs initiate TMA loads\n    // - Set expected transaction bytes. For 2SM TMA instructions, the transaction bytes counts both CTAs.\n    // - Although TMAs are initiated by both peer and leader CTAs, the barrier is only set and waited by the leader CTA.\n    // - Initiate asynchronous transfers with a multicast mask that includes all CTAs that participate in multicast.\n    if (elect_one_warp && elect_one_thr) { // TMA loads are executed by one thread\n      if (elect_one_cta) { // Only the leader CTA waits for TMA transactions\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes); // Set the expected transaction bytes for the TMA loads\n      }\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    if (elect_one_cta) {\n      // Wait for TMA loads to complete on leader CTAs\n      cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n      tma_barrier_phase_bit ^= 1;\n\n      // tcgen05.mma instructions require single-thread execution:\n      // - Only one warp performs the MMA-related loop operations\n      // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n      // - No explicit elect_one_sync region is needed from the user\n      if (elect_one_warp) {\n        // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n        for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n            gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n            tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n        }\n        // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n        cutlass::arch::umma_arrive_multicast_2x1SM(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n      }\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_2x1SM_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                                 256, 256,                            // Mma M and N dimensions\n                                                                 UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_2,_1,_1,_1):(_1,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _2:_1\n  //   Shape_MNK:  (_256,_256,_16)                      // MmaM, MmaN, MmaK (MmaK is constant for each instr.)\n  //   LayoutA_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_2,(_128,_256)):(_128,(_1,_256))    // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_A = make_tma_atom_A_sm100(\n      SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n      mA,                             // Source GMEM tensor\n      sA_layout,                      // Destination SMEM layout\n      mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n      tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n      cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                      //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _2:_1\n  //  ValLayoutSrc: (_2,_8192):(_8192,_1)\n  //  ValLayoutDst: (_2,_8192):(_8192,_1)\n  //  ValLayoutRef: (_2,_8192):(_8192,_1)\n  //  ValueType:    16b\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_B = make_tma_atom_B_sm100(\n    SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n    mB,                             // Source GMEM tensor\n    sB_layout,                      // Destination SMEM layout\n    mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n    tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n    cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                    //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  // ThrID:        _2:_1\n  // ValLayoutSrc: (_2,_8192):(_8192,_1)\n  // ValLayoutDst: (_2,_8192):(_8192,_1)\n  // ValLayoutRef: (_2,_8192):(_8192,_1)\n  // ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 04: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// Key extensions to tutorial 03_mma_tma_multicast_sm100.cu:\n// 1. Introduce 2SM tcgen05.mma instructions\n// 2. Introduce 2SM TMA instructions\n// 3. Demonstrate TMA multicast pattern specialized for 2SM instructions for loading A and B matrices\n//\n// This GEMM kernel will perform the following steps:\n// 1. Load A and B matrices from GMEM to SMEM using Multicasted TMA.2SM load operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using 2SM tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 2SM tcgen05.mma instructions operate as follows:\n// - Mma is launched by only one SM\n//    With 2SM MMA instructions, only 1 of the 2 CTAs collaborating on MMA executes the instruction.\n//    We call the collaborating CTAs, peer CTAs. And the CTA executing the MMA instruction is called leader CTA.\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 256x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator2Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   In this example, the TMA multicasts the loads across multiple CTAs.\n  //   Loads of A are multicasted along the N dimension of the cluster_shape_VMNK and\n  //   Loads of B are multicasted along the M dimension of the cluster_shape_VMNK.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  // Each CTA with the same m-coord will load a portion of A\n  // Each CTA with the same n-coord will load a portion of B\n  // Computation of the multicast masks must take into account the Peer CTA for TMA.2SM\n\n  // Construct the CTA-in-Cluster coordinate for multicasting\n  auto cta_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(int(cute::block_rank_in_cluster()));\n  auto elect_one_cta  = get<0>(cta_in_cluster_coord_vmnk) == Int<0>{};\n\n  // Project the cluster_layout for tma_A along the N-modes\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    get<2>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along N mode of the cluster\n                                    make_layout(size<2>(cluster_layout_vmnk)),  // The CTA layout along N mode of the cluster\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  // Project the cluster_layout for tma_B along the M-modes\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    get<1>(cta_in_cluster_coord_vmnk),          // The CTA coordinate along M mode of the cluster\n                                    make_layout(size<1>(cluster_layout_vmnk)),  // The CTA layout along M mode of the cluster\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Project the cluster_layout and cta_coord along the N-mode to determine the multicast mask for A\n  uint16_t tma_mcast_mask_a = create_tma_multicast_mask<2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the M-mode to determine the multicast mask for B\n  uint16_t tma_mcast_mask_b = create_tma_multicast_mask<1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n  // Project the cluster_layout and cta_coord along the VM + VN-modes to determine the multicast mask for C\n  uint16_t mma_mcast_mask_c = create_tma_multicast_mask<0,1>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk) |\n                              create_tma_multicast_mask<0,2>(cluster_layout_vmnk, cta_in_cluster_coord_vmnk);\n\n  // Calculate total bytes that TMA will transfer each tile to track completion, accounting for TMA.2SM\n  int tma_transaction_bytes = size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tAsA))\n                            + size<0>(cluster_layout_vmnk) * sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"tma_transaction_bytes: %d\\n\", tma_transaction_bytes);\n    printf(\"tma_mcast_mask_a: %x\\n\", tma_mcast_mask_a);\n    printf(\"tma_mcast_mask_b: %x\\n\", tma_mcast_mask_b);\n    printf(\"mma_mcast_mask_c: %x\\n\", mma_mcast_mask_c);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM should be initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    // The number of CTAs that participates in multicast operation with this CTA (for both A and B matrices)\n    int num_mcast_participants = size<1>(cluster_layout_vmnk) + size<2>(cluster_layout_vmnk) - 1;\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ num_mcast_participants);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  cute::cluster_sync();           // Make sure all CTAs in Cluster observe barrier init and TMEM alloc.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Both peer and leader CTAs initiate TMA loads\n    // - Set expected transaction bytes. For 2SM TMA instructions, the transaction bytes counts both CTAs.\n    // - Although TMAs are initiated by both peer and leader CTAs, the barrier is only set and waited by the leader CTA.\n    // - Initiate asynchronous transfers with a multicast mask that includes all CTAs that participate in multicast.\n    if (elect_one_warp && elect_one_thr) { // TMA loads are executed by one thread\n      if (elect_one_cta) { // Only the leader CTA waits for TMA transactions\n        cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes); // Set the expected transaction bytes for the TMA loads\n      }\n      copy(tma_atom_A.with(shared_storage.tma_barrier,tma_mcast_mask_a), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier,tma_mcast_mask_b), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    if (elect_one_cta) {\n      // Wait for TMA loads to complete on leader CTAs\n      cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n      tma_barrier_phase_bit ^= 1;\n\n      // tcgen05.mma instructions require single-thread execution:\n      // - Only one warp performs the MMA-related loop operations\n      // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n      // - No explicit elect_one_sync region is needed from the user\n      if (elect_one_warp) {\n        // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n        for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n            gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n            tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n        }\n        // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n        cutlass::arch::umma_arrive_multicast_2x1SM(&shared_storage.mma_barrier, mma_mcast_mask_c); // All multicasting CTAs encoded in mask.\n      }\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_2x1SM_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                                 256, 256,                            // Mma M and N dimensions\n                                                                 UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_2,_1,_1,_1):(_1,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _2:_1\n  //   Shape_MNK:  (_256,_256,_16)                      // MmaM, MmaN, MmaK (MmaK is constant for each instr.)\n  //   LayoutA_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_2,(_128,_16)):(_128,(_1,_256))     // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_2,(_128,_256)):(_128,(_1,_256))    // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<4>{}, Int<4>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_A = make_tma_atom_A_sm100(\n      SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n      mA,                             // Source GMEM tensor\n      sA_layout,                      // Destination SMEM layout\n      mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n      tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n      cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                      //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _2:_1\n  //  ValLayoutSrc: (_2,_8192):(_8192,_1)\n  //  ValLayoutDst: (_2,_8192):(_8192,_1)\n  //  ValLayoutRef: (_2,_8192):(_8192,_1)\n  //  ValueType:    16b\n\n  // SM100 interface for creating TMA loads.\n  Copy_Atom tma_atom_B = make_tma_atom_B_sm100(\n    SM100_TMA_2SM_LOAD_MULTICAST{}, // TMA load operation -- Multicasting 2SM instruction.\n    mB,                             // Source GMEM tensor\n    sB_layout,                      // Destination SMEM layout\n    mma_tiler,                      // MmaTiler_MNK. Unlike Sm90 interface where the tiler only included M and K modes.\n    tiled_mma,                      // Sm100 also requires the TiledMma to perform CTA-level partitioning.\n    cluster_layout_vmnk);           // ClusterLayout_VMNK. Unlike Sm90 interface where only the multicasting mode is passed.\n                                    //   We have make_tma_atom_[A|B]_sm100 and which determines the multicast mode.\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  // ThrID:        _2:_1\n  // ValLayoutSrc: (_2,_8192):(_8192,_1)\n  // ValLayoutDst: (_2,_8192):(_8192,_1)\n  // ValLayoutRef: (_2,_8192):(_8192,_1)\n  // ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/blackwell/04_mma_tma_2sm_sm100.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 02: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// This GEMM kernel extends 01_mma_sm100.cu by adding Tensor Memory Access (TMA) and performs the following steps:\n// 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n  // Note: Partitioned tensors use tXgY naming convention:\n  //  tXgY -> The partitioning pattern tX applied to tensor gY\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   The Int<0>, Layout<_1> indicates that the TMAs are not multicasted.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    Int<0>{}, Layout<_1>{},\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    Int<0>{}, Layout<_1>{},\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Calculate total bytes that TMA will transfer each tile to track completion\n  int tma_transaction_bytes = sizeof(make_tensor_like(tAsA))\n                            + sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"TmaBytes: %d\\n\", tma_transaction_bytes);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ 1);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  __syncthreads();                // Make sure all threads observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Set transaction bytes and execute with barrier\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_A.with(shared_storage.tma_barrier), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for TMA loads to SMEM to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive(&shared_storage.mma_barrier);\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<1>{}, Int<1>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // Create TMA descriptors for A and B matrices\n  Copy_Atom tma_atom_A = make_tma_atom(\n    SM90_TMA_LOAD{},        // TMA Load Op\n    mA,                     // Source GMEM tensor\n    sA_layout,              // Destination SMEM layout\n    select<0,2>(mma_tiler)  // MK Tiler for TMA operation\n  );\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_8192):(_0,_1)\n  //  ValLayoutDst: (_1,_8192):(_0,_1)\n  //  ValLayoutRef: (_1,_8192):(_0,_1)\n  //  ValueType:    16b\n\n  Copy_Atom tma_atom_B = make_tma_atom(\n      SM90_TMA_LOAD{},        // TMA Load Op\n      mB,                     // Source GMEM tensor\n      sB_layout,              // Destination SMEM layout\n      select<1,2>(mma_tiler)  // NK Tiler for TMA operation\n    );\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_16384):(_0,_1)\n  //  ValLayoutDst: (_1,_16384):(_0,_1)\n  //  ValLayoutRef: (_1,_16384):(_0,_1)\n  //  ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(cudaFuncSetAttribute(kernel_ptr,\n                                        cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  cudaDeviceProp props;\n  int current_device_id;\n  cudaGetDevice(&current_device_id);\n  cudaGetDeviceProperties(&props, current_device_id);\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2024 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n//                             CuTe Tutorial for SM100 Programming\n// This tutorial series demonstrates CuTe Blackwell capabilities that are frequently used\n// throughout CUTLASS. The goal is to familiarize developers with CuTe SM100 interfaces.\n//\n// The tutorial series is split into five stages:\n// * 01_mma_sm100.cu: Simple Blackwell SM100 GEMM using a tcgen05.mma instruction.\n// * 02_mma_tma_sm100.cu: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n// * 03_mma_tma_multicast_sm100.cu: Blackwell SM100 GEMM using tcgen05.mma and Multicast TMA.\n// * 04_mma_tma_2sm_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma and 2SM Multicast TMA.\n// * 05_mma_tma_epi_sm100.cu: Blackwell SM100 GEMM with 2SM tcgen05.mma, 2SM TMA mainloop, and TMA epilogue.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <cstdio>\n\n// Use Thrust to handle host/device allocations\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n// Cutlass includes\n#include <cutlass/half.h>                       // F16 data type\n#include <cutlass/util/print_error.hpp>\n#include <cutlass/arch/barrier.h>\n#include <cutlass/cluster_launch.hpp>\n\n// CuTe includes\n#include <cute/tensor.hpp>                      // CuTe tensor implementation\n#include <cute/arch/cluster_sm90.hpp>           // CuTe functions for querying the details of cluster launched\n#include <cute/numeric/integral_constant.hpp>   // Compile time in constants such as _1, _256 etc.\n#include <cute/algorithm/cooperative_copy.hpp>  // Auto vectorized copy operation\n#include <cute/arch/tmem_allocator_sm100.hpp>   // TMEM allocator for SM100\n\n// Tutorial helpers\n#include \"example_utils.hpp\"\n\nusing namespace cute;\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Tutorial 02: Simple Blackwell SM100 GEMM using tcgen05.mma and TMA instructions.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// We will implement a GEMM operation: D (f32) = beta * C (F32) + alpha * A (F16) * B (F16) where:\n// - Matrix A is MxK, K-major (BLAS transpose T, row-major)\n// - Matrix B is NxK, K-major (BLAS transpose N, column-major)\n// - Matrices C and D are MxN, N-major (BLAS row-major)\n//\n// This GEMM kernel extends 01_mma_sm100.cu by adding Tensor Memory Access (TMA) and performs the following steps:\n// 1. Load A and B matrices from global memory (GMEM) to shared memory (SMEM) using TMA operations.\n// 2. Perform matrix multiply-accumulate (MMA) operations using tcgen05.mma instruction.\n// 3. Load completed accumulator from tensor memory (TMEM) to registers (RMEM) using tcgen05.ld.\n// 4. Read C matrix from global memory (GMEM) to register (RMEM).\n// 5. Apply alpha and beta scaling to the MMA accumulator and C matrix.\n// 6. Store D matrix from registers (RMEM) to global memory (GMEM).\n//\n// SM100 tcgen05.mma instructions operate as follows:\n// - Read matrix A from SMEM or TMEM\n// - Read matrix B from SMEM\n// - Write accumulator to TMEM\n// The accumulator in TMEM must then be loaded to registers before writing back to GMEM.\n//\n// The tcgen05.mma instruction requires an Instruction Descriptor that encodes A, B, and Accumulator types\n//   and the MMA's M and N dimensions.\n// The A and B matrices that are read from SMEM need to be provided to MMA instructions as SMEM Descriptors.\n//   These are the A and B fragments of the tcgen05.mma in CuTe terminology.\n// CuTe provides these descriptors transparently in the instruction and fragments, shown in this tutorial.\n//\n// The MMA details:\n// We use the tcgen05.mma.f16 instruction (F16xF16 = F32) that performs a 128x256x16 MMA\n// operation. F32 accumulator type is chosen since both C and D matrices use F32.\n// This example uses F16xF16 = F32 MMA where:\n// TypeA = cutlass::half_t;  // MMA A Data Type\n// TypeB = cutlass::half_t;  // MMA B Data Type\n// TypeC = float;            // MMA C Data Type\n// TypeD = float;            // MMA D Data Type\n// TypeAccumulator = float;  // Both TypeC and TypeD are float, so we use float accumulator type\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n// The shared memory buffers for A and B matrices.\ntemplate <class TypeA,           // Tensor A data type\n          class TypeB,           // Tensor B data type\n          class ASmemLayout,     // (MmaA, NumMma_M, NumMma_K, ...)\n          class BSmemLayout>     // (MmaB, NumMma_N, NumMma_K, ...)\nstruct SharedStorage\n{\n  alignas(128) cute::ArrayEngine<TypeA, cute::cosize_v<ASmemLayout>> A;\n  alignas(128) cute::ArrayEngine<TypeB, cute::cosize_v<BSmemLayout>> B;\n\n  alignas(16) cute::uint64_t mma_barrier;  // Barrier to track MMA computation on SMEM\n  alignas(16) cute::uint64_t tma_barrier;  // Barrier to track TMA data transfers to SMEM\n\n  alignas(16) cute::uint32_t tmem_base_ptr; // Base pointer for TMEM allocation\n\n  CUTE_DEVICE constexpr auto tensor_sA() { return make_tensor(make_smem_ptr(A.begin()), ASmemLayout{}); }\n  CUTE_DEVICE constexpr auto tensor_sB() { return make_tensor(make_smem_ptr(B.begin()), BSmemLayout{}); }\n};\n\n// The device kernel\ntemplate <class SharedStorage,\n          class ATensor, class BTensor, class CTensor, class DTensor,\n          class MmaTiler_MNK, class TiledMMA, class ClusterShape_MNK,\n          class TmaAtomA, class TmaAtomB,\n          class Alpha, class Beta>\n__global__ static\nvoid\ngemm_device(ATensor mA,                      // (Gemm_M, Gemm_K)\n            BTensor mB,                      // (Gemm_N, Gemm_K)\n            CTensor mC,                      // (Gemm_M, Gemm_N)\n            DTensor mD,                      // (Gemm_M, Gemm_N)\n            MmaTiler_MNK mma_tiler,          // <MmaTile_M, MmaTile_N, MmaTile_K>\n            TiledMMA tiled_mma,              // <    Mma_M,     Mma_N,     Mma_K>\n            ClusterShape_MNK cluster_shape,  // (ClusterM, ClusterN, ClusterK)\n            CUTE_GRID_CONSTANT TmaAtomA const tma_atom_A,\n            CUTE_GRID_CONSTANT TmaAtomB const tma_atom_B,\n            Alpha alpha, Beta beta)\n{\n  // Step 1: The Prologue.\n\n  // The CTA layout within the Cluster: (V,M,N,K) -> CTA idx\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename TiledMMA::AtomThrID{}));\n\n  // Construct the MMA grid coordinate from the CTA grid coordinate\n  auto mma_coord_vmnk = make_coord(blockIdx.x % size<0>(cluster_layout_vmnk), // Peer CTA coordinate\n                                   blockIdx.x / size<0>(cluster_layout_vmnk), //    MMA-M coordinate\n                                   blockIdx.y,                                //    MMA-N coordinate\n                                   _);                                        //    MMA-K coordinate\n\n  // Partition the GMEM tensors with the mma_tiler and mma_coord to get the slices processed\n  //   by this mma tile.\n  // CuTe provides local_tile partitioning function. local_tile accepts 4 parameters:\n  //   * Tensor to partition\n  //   * Tiler to use for partitioning\n  //   * Coordinate to use for slicing the partitioned tensor\n  //   * Projection to ignore unwanted modes of the Tiler and Coordinate\n  auto mma_coord = select<1,2,3>(mma_coord_vmnk);\n  Tensor gA = local_tile(mA, mma_tiler, mma_coord, Step<_1, X,_1>{});  // (MmaTile_M, MmaTile_K, Tiles_K)\n  Tensor gB = local_tile(mB, mma_tiler, mma_coord, Step< X,_1,_1>{});  // (MmaTile_N, MmaTile_K, Tiles_K)\n  Tensor gC = local_tile(mC, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n  Tensor gD = local_tile(mD, mma_tiler, mma_coord, Step<_1,_1, X>{});  // (MmaTile_M, MmaTile_N)\n\n  if (thread0()) {\n    print(\"mA:\\t\"); print(mA); print(\"\\n\");   // mA:   ArithTuple(_0,_0) o (512,256):(_1@1,_1@0)\n    print(\"mB:\\t\"); print(mB); print(\"\\n\");   // mB:   ArithTuple(_0,_0) o (1024,256):(_1@1,_1@0)\n    print(\"mC:\\t\"); print(mC); print(\"\\n\");   // mC:   gmem_ptr[32b](GMEM_ADDR_C) o (512,1024):(1024,_1)\n    print(\"mD:\\t\"); print(mD); print(\"\\n\");   // mD:   gmem_ptr[32b](GMEM_ADDR_D) o (512,1024):(1024,_1)\n\n    print(\"gA:\\t\"); print(gA); print(\"\\n\");   // gA:   ArithTuple(_0,0) o (_128,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gB:\\t\"); print(gB); print(\"\\n\");   // gB:   ArithTuple(_0,0) o (_256,_64,4):(_1@1,_1@0,_64@0)\n    print(\"gC:\\t\"); print(gC); print(\"\\n\");   // gC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile) o (_128,_256):(256,_1)\n    print(\"gD:\\t\"); print(gD); print(\"\\n\");   // gD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile) o (_128,_256):(256,_1)\n  } __syncthreads();\n\n  // The SMEM tensors\n\n  // Allocate SMEM\n  extern __shared__ char shared_memory[];\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Represent the SMEM buffers for A and B\n  Tensor tCsA = shared_storage.tensor_sA();         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCsB = shared_storage.tensor_sB();         // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  //\n  // Mma partitioning for A and B\n  //\n  // Note: Partitioned tensors use tXgY naming convention:\n  //  tXgY -> The partitioning pattern tX applied to tensor gY\n\n  auto mma_v = get<0>(mma_coord_vmnk);\n  ThrMMA cta_mma = tiled_mma.get_slice(mma_v);   // Use Peer CTA coordinate\n  Tensor tCgA = cta_mma.partition_A(gA);         // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCgB = cta_mma.partition_B(gB);         // (MmaB, NumMma_N, NumMma_K, Tiles_K)\n  Tensor tCgC = cta_mma.partition_C(gC);         // (MmaC, NumMma_M, NumMma_N)\n  Tensor tCgD = cta_mma.partition_C(gD);         // (MmaC, NumMma_M, NumMma_N)\n\n  if (thread0()) {\n    print(\"tCgA:\\t\"); print(tCgA); print(\"\\n\");  // tCgA:   ArithTuple(_0,0) o ((_128,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgB:\\t\"); print(tCgB); print(\"\\n\");  // tCgB:   ArithTuple(_0,0) o ((_256,_16),_1,_4,4):((_1@1,_1@0),_0,_16@0,_64@0)\n    print(\"tCgC:\\t\"); print(tCgC); print(\"\\n\");  // tCgC:   gmem_ptr[32b](GMEM_ADDR_C + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n    print(\"tCgD:\\t\"); print(tCgD); print(\"\\n\");  // tCgD:   gmem_ptr[32b](GMEM_ADDR_D + offset_for_mma_tile + offset_for_mma) o ((_128,_256),_1,_1):((256,_1),_0,_0)\n  } __syncthreads();\n\n  // MMA Fragment Allocation\n  // We allocate \"fragments\" which are SMEM descriptors that serve as inputs to cute::gemm operations.\n  // For tcgen05.mma operations:\n  // - Matrices A and B are sourced from SMEM\n  // - tCrA and tCrB provide descriptor views of tCsA and tCsB respectively\n  // - The first mode of each descriptor represents the SMEM for a single MMA operation\n  Tensor tCrA = cta_mma.make_fragment_A(tCsA);      // (MmaA, NumMma_M, NumMma_K, Tiles_K)\n  Tensor tCrB = cta_mma.make_fragment_B(tCsB);      // (MmaB, NumMma_M, NumMma_K, Tiles_K)\n\n  // TMEM Allocation\n  // On SM100 architecture, accumulators are stored exclusively in tensor memory (TMEM).\n  // ThrMma's make_fragment_C() creates a TMEM tensor with the appropriate layout for the accumulator.\n  Tensor tCtAcc = cta_mma.make_fragment_C(tCgC);    // (MmaC, NumMma_M, NumMma_N)\n\n  uint32_t elect_one_thr  = cute::elect_one_sync();\n  uint32_t elect_one_warp = (threadIdx.x / 32 == 0);\n\n  using TmemAllocator = cute::TMEM::Allocator1Sm;\n  TmemAllocator tmem_allocator{};\n\n  if (elect_one_warp) {\n    tmem_allocator.allocate(TmemAllocator::Sm100TmemCapacityColumns, &shared_storage.tmem_base_ptr);\n  }\n  __syncthreads(); // Wait for all threads until warp0 allocates TMEM\n  tCtAcc.data() = shared_storage.tmem_base_ptr;\n\n  if (thread0()) {\n    print(\"tCsA:\\t\"); print(tCsA); print(\"\\n\");     // tCsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCsB:\\t\"); print(tCsB); print(\"\\n\");     // tCsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n    print(\"tCrA:\\t\"); print(tCrA); print(\"\\n\");     // tCrA:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCrB:\\t\"); print(tCrB); print(\"\\n\");     // tCrB:   UMMA::DescriptorIterator o (_1,_1,_4):(_0,_0,_2)\n    print(\"tCtAcc:\\t\"); print(tCtAcc); print(\"\\n\"); // tCtAcc: tmem_[32b](TMEM_ADDR) o ((_128,_256),_1,_1):((_65536,_1),_0,_0)\n  } __syncthreads();\n\n  // TMA Setup\n  //\n  //   These are TMA partitionings, which have a dedicated custom partitioner.\n  //   The Int<0>, Layout<_1> indicates that the TMAs are not multicasted.\n  //      Any multicasting must be in conformance with tma_x constructed with make_tma_atom on host.\n  //   For A tensor: The group_modes<0,3> transforms the (MmaA, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaA, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile MK.\n  //   For B tensor: The group_modes<0,3> transforms the (MmaB, NumMma_M, NumMma_K, Tiles_K)-shaped tensor\n  //      into ((MmaB, NumMma_M, NumMma_K), Tiles_K). The partitioning only pays attention to mode-0, the MMA Tile NK.\n  //   Simply put, the TMA will be responsible for everything in mode-0 with a single call to cute::copy.\n  //   The tma_partition reorders and offsets mode-0 according to the tma_x atom and the multicast info.\n\n  auto [tAgA, tAsA] = tma_partition(tma_atom_A,\n                                    Int<0>{}, Layout<_1>{},\n                                    group_modes<0,3>(tCsA), group_modes<0,3>(tCgA));\n\n  auto [tBgB, tBsB] = tma_partition(tma_atom_B,\n                                    Int<0>{}, Layout<_1>{},\n                                    group_modes<0,3>(tCsB), group_modes<0,3>(tCgB));\n\n  // Calculate total bytes that TMA will transfer each tile to track completion\n  int tma_transaction_bytes = sizeof(make_tensor_like(tAsA))\n                            + sizeof(make_tensor_like(tBsB));\n\n  if (thread0()) {\n    print(\"tAgA:\\t\"); print(tAgA); print(\"\\n\");  // tAgA:   ArithTuple(_0,0) o (((_64,_128),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tAsA:\\t\"); print(tAsA); print(\"\\n\");  // tAsA:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_A) o ((_8192,_1)):((_1,_0))\n    print(\"tBgB:\\t\"); print(tBgB); print(\"\\n\");  // tBgB:   ArithTuple(_0,0) o (((_64,_256),_1),4):(((_1@0,_1@1),_0),_64@0)\n    print(\"tBsB:\\t\"); print(tBsB); print(\"\\n\");  // tBsB:   Sw<3,4,3>_smem_ptr[16b](SMEM_ADDR_B) o ((_16384,_1)):((_1,_0))\n    printf(\"TmaBytes: %d\\n\", tma_transaction_bytes);\n  } __syncthreads();\n\n  // Barrier Initialization\n  // Barriers in SMEM initialized by a single thread.\n  if (elect_one_warp && elect_one_thr) {\n    cute::initialize_barrier(shared_storage.mma_barrier, /* num_ctas */ 1);\n    cute::initialize_barrier(shared_storage.tma_barrier, /* num_threads */ 1);\n  }\n  int mma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  int tma_barrier_phase_bit = 0;  // Each barrier has an associated phase_bit.\n  __syncthreads();                // Make sure all threads observe barrier initialization.\n\n  // Step 2: The Mainloop.\n\n  // Set mma accumulate option to zero so that the first MMA instruction will clear the TMEM accumulator.\n  tiled_mma.accumulate_ = UMMA::ScaleOut::Zero;\n\n  // Execute a MmaTile_M x MmaTile_N x GEMM_K GEMM\n  for (int k_tile = 0; k_tile < size<3>(tCgA); ++k_tile)\n  {\n    // Step 2a: Load A and B tiles\n\n    // TMA Load Operations:\n    // - Execute asynchronous TMA loads with single thread\n    // - Set transaction bytes and execute with barrier\n    if (elect_one_warp && elect_one_thr) {\n      cute::set_barrier_transaction_bytes(shared_storage.tma_barrier, tma_transaction_bytes);\n      copy(tma_atom_A.with(shared_storage.tma_barrier), tAgA(_,k_tile), tAsA); // Load MmaTile_M x MmaTile_K A tile\n      copy(tma_atom_B.with(shared_storage.tma_barrier), tBgB(_,k_tile), tBsB); // Load MmaTile_N x MmaTile_K B tile\n    }\n\n    // Step 2b: Execute the MMAs for this tile\n\n    // Wait for TMA loads to SMEM to complete\n    cute::wait_barrier(shared_storage.tma_barrier, tma_barrier_phase_bit);\n    tma_barrier_phase_bit ^= 1;\n\n    // tcgen05.mma instructions require single-thread execution:\n    // - Only one warp performs the MMA-related loop operations\n    // - CuTe operations internally manage the single-thread execution of tcgen05.mma and tcgen05.cp\n    // - No explicit elect_one_sync region is needed from the user\n    if (elect_one_warp) {\n      // Execute a MmaTile_M x MmaTile_N x MmaTile_K GEMM\n      for (int k_block = 0; k_block < size<2>(tCrA); ++k_block) {\n        gemm(tiled_mma, tCrA(_,_,k_block), tCrB(_,_,k_block), tCtAcc);\n        tiled_mma.accumulate_ = UMMA::ScaleOut::One;\n      }\n      // Ensure MMAs are completed, only then we can reuse the A and B SMEM.\n      cutlass::arch::umma_arrive(&shared_storage.mma_barrier);\n    }\n    // Wait MMAs to complete to avoid overwriting the A and B SMEM.\n    cute::wait_barrier(shared_storage.mma_barrier, mma_barrier_phase_bit);\n    mma_barrier_phase_bit ^= 1;\n  }\n\n  // Step 3: The Epilogue.\n\n  // Create the tiled copy operation for the accumulator (TMEM -> RMEM)\n  TiledCopy tiled_t2r_copy = make_tmem_copy(SM100_TMEM_LOAD_32dp32b1x{}, tCtAcc);\n  ThrCopy   thr_t2r_copy   = tiled_t2r_copy.get_slice(threadIdx.x);\n\n  Tensor tDgC = thr_t2r_copy.partition_D(tCgC);                   // (CpyD, NumCpy_M, NumCpy_N)\n  Tensor tDrC = make_fragment_like(tDgC);                         // (CpyD, NumCpy_M, NumCpy_N)\n  // Load C tensor GMEM -> RMEM\n  copy(tDgC, tDrC);\n\n  Tensor tDtAcc = thr_t2r_copy.partition_S(tCtAcc);               // (CpyS, NumCpy_M, NumCpy_N)\n  Tensor tDgD   = thr_t2r_copy.partition_D(tCgD);                 // (CpyD, NumCpy_M, NumCpy_N)\n  using AccType = typename decltype(tCtAcc)::value_type;\n  Tensor tDrAcc = make_tensor<AccType>(shape(tDgD));              // (CpyD, NumCpy_M, NumCpy_N)\n  // Load TMEM -> RMEM\n  copy(tiled_t2r_copy, tDtAcc, tDrAcc);\n\n  // AXPBY RMEM -> RMEM: tDrC = alpha * tDrAcc + beta * tDrC\n  axpby(alpha, tDrAcc, beta, tDrC);\n  // Store RMEM -> GMEM\n  copy(tDrC, tDgD);\n\n  __syncthreads();\n\n  // Release the right to allocate before deallocations so that the next CTA can rasterize\n  // Then deallocate TMEM\n  if (elect_one_warp) {\n    tmem_allocator.release_allocation_lock();\n    tmem_allocator.free(shared_storage.tmem_base_ptr, TmemAllocator::Sm100TmemCapacityColumns);\n  }\n}\n\ntemplate <class TypeA, class LayoutA,\n          class TypeB, class LayoutB,\n          class TypeC, class LayoutC,\n          class TypeD, class LayoutD,\n          class Alpha, class Beta>\nvoid gemm_host_f16xf16_f32_f32_tnt(TypeA const* device_ptr_A, LayoutA layout_A,\n                                   TypeB const* device_ptr_B, LayoutB layout_B,\n                                   TypeC const* device_ptr_C, LayoutC layout_C,\n                                   TypeD      * device_ptr_D, LayoutD layout_D,\n                                   Alpha const alpha, Beta const beta)\n{\n  assert(shape<0>(layout_A) == shape<0>(layout_C));  // Gemm_M\n  assert(shape<0>(layout_A) == shape<0>(layout_D));  // Gemm_M\n  assert(shape<0>(layout_B) == shape<1>(layout_C));  // Gemm_N\n  assert(shape<0>(layout_B) == shape<1>(layout_D));  // Gemm_N\n  assert(shape<1>(layout_A) == shape<1>(layout_B));  // Gemm_K\n\n  // Represent the full tensors in global memory\n  Tensor mA = make_tensor(make_gmem_ptr(device_ptr_A), layout_A);      // (Gemm_M, Gemm_K)\n  Tensor mB = make_tensor(make_gmem_ptr(device_ptr_B), layout_B);      // (Gemm_N, Gemm_K)\n  Tensor mC = make_tensor(make_gmem_ptr(device_ptr_C), layout_C);      // (Gemm_M, Gemm_N)\n  Tensor mD = make_tensor(make_gmem_ptr(device_ptr_D), layout_D);      // (Gemm_M, Gemm_N)\n\n  // Get M, N, K dimensions of the GEMM we are running\n  auto Gemm_M = shape<0>(layout_A);\n  auto Gemm_N = shape<0>(layout_B);\n  auto Gemm_K = shape<1>(layout_A);\n  std::cout << \"Running for problem shape (MxNxK): \" << Gemm_M << \"x\" << Gemm_N << \"x\" << Gemm_K << std::endl;\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Initialize the GEMM kernel parameters\n  //\n  ////////////////////////////////////////////////////////////\n\n  // Create TiledMma. make_tiled_mma takes the target instructions and an (optional) instruction layout as parameters to create a\n  // larger TiledMma from the given mma instruction.\n  // See cute/arch/mma_sm100_umma.hpp for all tcgen05.mma instructions\n  TiledMMA tiled_mma = make_tiled_mma(SM100_MMA_F16BF16_SS<TypeA, TypeB, TypeC,                 // Mma's A, B, and Accumulator types\n                                                           128, 256,                            // Mma M and N dimensions\n                                                           UMMA::Major::K, UMMA::Major::K>{});  // A and B layouts\n\n  // We can also print and inspect the tiled_mma\n  print(tiled_mma);\n  // TiledMMA\n  //   ThrLayoutVMNK:  (_1,_1,_1,_1):(_0,_0,_0,_0)\n  //   PermutationMNK: (_,_,_)\n  // MMA_Atom\n  //   ThrID:      _1:_0\n  //   Shape_MNK:  (_128,_256,_16)                      // MmaM, MmaN, MmaK instruction size\n  //   LayoutA_TV: (_1,(_128,_16)):(_0,(_1,_128))       // TV -> MmaCoordinate mapping for A matrix\n  //   LayoutB_TV: (_1,(_256,_16)):(_0,(_1,_256))       // TV -> MmaCoordinate mapping for B matrix\n  //   LayoutC_TV: (_1,(_128,_256)):(_0,(_1,_128))      // TV -> MmaCoordinate mapping for C matrix\n\n  // Define MMA tiler sizes (static)\n  auto bM = tile_size<0>(tiled_mma);             // MMA Tile M. We'll use 1 MMAs per MMA Tile M.\n  auto bN = tile_size<1>(tiled_mma);             // MMA Tile N. We'll use 1 MMAs per MMA Tile M.\n  auto bK = tile_size<2>(tiled_mma) * Int<4>{};  // MMA Tile K. We'll use 4 MMAs per MMA Tile K. For 16b types, tcgen05.mma has K16.\n  auto mma_tiler = make_shape(bM, bN, bK);       // (MMA_M, MMA_N, MMA_K)\n\n  // In SM90,  the MMAs are CTA-local and perform thread-level partitioning.\n  // In SM100, the MMAs are Cluster-local and perform CTA-level partitioning.\n  // Thus, SM90 uses a cta_tiler to extract portions of the Problem for the CTA\n  //  and SM100 uses a mma_tiler to extract portions of the Problem for the MMA.\n  //  The MMA's partitioning then yields the CTA-local work.\n\n  if (not evenly_divides(shape(mma_tiler), tile_shape(tiled_mma))) {\n    std::cerr << \"The MMA Shape should evenly divide the MMA Tiler.\" << std::endl;\n    return;\n  }\n\n  if (not evenly_divides(make_shape(Gemm_M, Gemm_N, Gemm_K), mma_tiler)) {\n    std::cerr << \"OOB accesses are not supported. MmaTiler_MNK should evenly divide ProblemShape_MNK.\" << std::endl;\n    return;\n  }\n\n  //\n  // Determine the SMEM layouts:\n  //\n\n  //  * SMEM layouts for A and B must match the post-partitioned (CTA-local) shapes expected by the MMA instructions.\n  //  * CuTe provides partition_shape_[A|B] functions to determine the post-partitioned shape.\n  //    These functions take the TiledMma, and the MMA Tile Shape as inputs and returns a shape that is at least rank-3\n  //    where the first mode has the same shape as the MMA instruction, 2nd and 3rd mode expresses the number of time\n  //    MMA instr is repeated in M/N mode and K mode of MMA tile, respectively.\n  //  * Note that SMEM layouts are needed to determine SMEM allocation for kernel launch.\n\n  // Pre-partitioned Tile Shape (MmaTile_M, MmaTile_K) to post-partitioned (MmaA, NumMma_M, NumMma_K)\n  auto mma_shape_A = partition_shape_A(tiled_mma, make_shape(size<0>(mma_tiler), size<2>(mma_tiler)));\n  // Pre-partitioned Tile Shape (MmaTile_N, MmaTile_K) to post-partitioned (MmaB, NumMma_N, NumMma_K)\n  auto mma_shape_B = partition_shape_B(tiled_mma, make_shape(size<1>(mma_tiler), size<2>(mma_tiler)));\n\n  // Print and inspect mma_shape_A, and mma_shape_B for this example.\n  print(\"mma_shape_A:\\t\"); print(mma_shape_A); print(\"\\n\");  // mma_shape_A:  ((_128,_16),_1,_4)\n  print(\"mma_shape_B:\\t\"); print(mma_shape_B); print(\"\\n\");  // mma_shape_B:  ((_256,_16),_1,_4)\n\n  // A and B tensors are swizzled in SMEM to improve MMA performance.\n  //  * However, expressing swizzled layouts is very hard.\n  //  * CuTe provides tile_to_mma_shape functions for SM100 to create swizzled layouts for post-partitioned Mma Shapes\n  auto sA_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeA>{}, mma_shape_A);\n  auto sB_layout = UMMA::tile_to_mma_shape(UMMA::Layout_K_SW128_Atom<TypeB>{}, mma_shape_B);\n\n  // Print and inspect sA_layout and sB_layout for this example.\n  print(\"sA_layout:\\t\"); print(sA_layout); print(\"\\n\");      // sA_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_128,_16),_1,_4):((_64,_1),_0,_16)\n  print(\"sB_layout:\\t\"); print(sB_layout); print(\"\\n\");      // sB_layout:   Sw<3,4,3> o smem_ptr[16b](unset) o ((_256,_16),_1,_4):((_64,_1),_0,_16)\n\n  // Now we can find the SMEM allocation size\n  using SMEMStorage = SharedStorage<TypeA, TypeB, decltype(sA_layout), decltype(sB_layout)>;\n\n  //\n  // TMA Descriptor Creation (Host Side)\n  //\n\n  // The cluster shape and layout\n  auto cluster_shape = make_shape(Int<1>{}, Int<1>{}, Int<1>{});\n  Layout cluster_layout_vmnk = tiled_divide(make_layout(cluster_shape),\n                                            make_tile(typename decltype(tiled_mma)::AtomThrID{}));\n\n  // Create TMA descriptors for A and B matrices\n  Copy_Atom tma_atom_A = make_tma_atom(\n    SM90_TMA_LOAD{},        // TMA Load Op\n    mA,                     // Source GMEM tensor\n    sA_layout,              // Destination SMEM layout\n    select<0,2>(mma_tiler)  // MK Tiler for TMA operation\n  );\n  Tensor mA_tma = tma_atom_A.get_tma_tensor(shape(mA));   // (Gemm_M, Gemm_K)\n\n  print(\"tma_atom_A:\\t\"); print(tma_atom_A); print(\"\\n\");\n  // tma_atom_A:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_8192):(_0,_1)\n  //  ValLayoutDst: (_1,_8192):(_0,_1)\n  //  ValLayoutRef: (_1,_8192):(_0,_1)\n  //  ValueType:    16b\n\n  Copy_Atom tma_atom_B = make_tma_atom(\n      SM90_TMA_LOAD{},        // TMA Load Op\n      mB,                     // Source GMEM tensor\n      sB_layout,              // Destination SMEM layout\n      select<1,2>(mma_tiler)  // NK Tiler for TMA operation\n    );\n  Tensor mB_tma = tma_atom_B.get_tma_tensor(shape(mB));   // (Gemm_N, Gemm_K)\n\n  print(\"tma_atom_B:\\t\"); print(tma_atom_B); print(\"\\n\");\n  // tma_atom_B:     Copy_Atom\n  //  ThrID:        _1:_0\n  //  ValLayoutSrc: (_1,_16384):(_0,_1)\n  //  ValLayoutDst: (_1,_16384):(_0,_1)\n  //  ValLayoutRef: (_1,_16384):(_0,_1)\n  //  ValueType:    16b\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Launch GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  dim3 dimBlock(128);\n  dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));\n  dim3 dimGrid(size(ceil_div(Gemm_M, bM * size<1>(cluster_layout_vmnk))) * dimCluster.x,\n               size(ceil_div(Gemm_N, bN * size<2>(cluster_layout_vmnk))) * dimCluster.y);\n  int  smemBytes = sizeof(SMEMStorage);\n\n  auto* kernel_ptr = &gemm_device<SMEMStorage,\n                                  decltype(mA_tma), decltype(mB_tma), decltype(mC), decltype(mD),\n                                  decltype(mma_tiler), decltype(tiled_mma), decltype(cluster_shape),\n                                  decltype(tma_atom_A), decltype(tma_atom_B), // Includes the TMA descriptor.\n                                  Alpha, Beta>;\n\n  // Set kernel attributes (set SMEM)\n  CUTE_CHECK_ERROR(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel_ptr),\n                                        hipFuncAttributeMaxDynamicSharedMemorySize,\n                                        smemBytes));\n\n  printf(\"Grid launched: %d, %d, %d\\n\", dimGrid.x, dimGrid.y, dimGrid.z);\n  printf(\"Cluster launched: %d, %d, %d\\n\", dimCluster.x, dimCluster.y, dimCluster.z);\n\n  cutlass::ClusterLaunchParams params = {dimGrid, dimBlock, dimCluster, smemBytes};\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params, (void const*) kernel_ptr,\n                                                             mA_tma, mB_tma, mC, mD,\n                                                             mma_tiler, tiled_mma, cluster_shape,\n                                                             tma_atom_A, tma_atom_B,\n                                                             alpha, beta);\n  CUTE_CHECK_LAST();\n\n  if (status != cutlass::Status::kSuccess) {\n    std::cerr << \"Error: Failed at kernel Launch\" << std::endl;\n  }\n}\n\n#endif // defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\nint main(int argc, char** argv)\n{\n  hipDeviceProp_t props;\n  int current_device_id;\n  hipGetDevice(&current_device_id);\n  hipGetDeviceProperties(&props, current_device_id);\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n    return -1;\n  }\n\n  if ((props.major != 10) || (props.major == 10 && props.minor > 1)) {\n    std::cerr << \"This example requires NVIDIA's Blackwell Architecture GPU with compute capability 100a.\" << std::endl;\n    std::cerr << \"  Found \" << props.major << \".\" << props.minor << std::endl;\n    return -1;\n  }\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n\n  int Gemm_M = 512;\n  if (argc >= 2)\n    sscanf(argv[1], \"%d\", &Gemm_M);\n\n  int Gemm_N = 1024;\n  if (argc >= 3)\n    sscanf(argv[2], \"%d\", &Gemm_N);\n\n  int Gemm_K = 256;\n  if (argc >= 4)\n    sscanf(argv[3], \"%d\", &Gemm_K);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Create A, B, C, and D tensors\n  //\n  ////////////////////////////////////////////////////////////\n  // Define the data types. A and B types are same for MMA instruction.\n  using TypeA = cutlass::half_t; // MMA A Data Type\n  auto type_str_a = \"half_t\";\n  using TypeB = cutlass::half_t; // MMA B Data Type\n  auto type_str_b = \"half_t\";\n  using TypeC = float;           // MMA C Data Type\n  [[maybe_unused]] auto type_str_c = \"float\";\n  using TypeD = float;           // MMA D Data Type\n  auto type_str_d = \"float\";\n  using TypeAccumulator = float; // Both TypeC and TypeD are float, use float accumulator type.\n\n  // A tensor MxK K-major (Layout T = Row-Major)\n  Layout layout_A = make_layout(make_shape (Gemm_M,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_M,Gemm_K):(Gemm_K,_1)\n  // B tensor NxK K-major (Layout N = Column-Major)\n  Layout layout_B = make_layout(make_shape (Gemm_N,   Gemm_K),\n                                make_stride(Gemm_K, Int<1>{}));   // (Gemm_N,Gemm_K):(Gemm_K,_1)\n  // C tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_C = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n  // D tensor MxN N-major (Layout T = Row-Major)\n  Layout layout_D = make_layout(make_shape (Gemm_M,   Gemm_N),\n                                make_stride(Gemm_N, Int<1>{}));   // (Gemm_M,Gemm_N):(Gemm_N,_1)\n\n  // Host allocations and host CuTe tensors for A, B, and C tensors.\n  thrust::host_vector<TypeA>   host_A(Gemm_M * Gemm_K);\n  Tensor host_tensor_A = make_tensor(host_A.data(), layout_A);\n  print(\"host_tensor_A:\\t\"); print(host_tensor_A); print(\"\\n\"); // host_tensor_A:\tptr[16b](ADDR_A) o (512,256):(256,_1)\n\n  thrust::host_vector<TypeB>   host_B(Gemm_N * Gemm_K);\n  Tensor host_tensor_B = make_tensor(host_B.data(), layout_B);\n  print(\"host_tensor_B:\\t\"); print(host_tensor_B); print(\"\\n\"); // host_tensor_B:\tptr[16b](ADDR_B) o (1024,256):(256,_1)\n\n  thrust::host_vector<TypeC>   host_C(Gemm_M * Gemm_N);\n  Tensor host_tensor_C = make_tensor(host_C.data(), layout_C);\n  print(\"host_tensor_C:\\t\"); print(host_tensor_C); print(\"\\n\"); // host_tensor_C:\tptr[32b](ADDR_C) o (512,1024):(1024,_1)\n\n  // Note that we don't need a host_tensor for D yet.\n  thrust::device_vector<TypeD> device_D(Gemm_M * Gemm_N);\n\n  // Initialize A, B, and C tensors with random values.\n  initialize_tensor(host_tensor_A);\n  initialize_tensor(host_tensor_B);\n  initialize_tensor(host_tensor_C);\n\n  // Copy A, B, and C tensors from host memory to device memory\n  thrust::device_vector<TypeA> device_A = host_A;\n  thrust::device_vector<TypeB> device_B = host_B;\n  thrust::device_vector<TypeC> device_C = host_C;\n\n  using Alpha = float;\n  using Beta = float;\n  Alpha alpha = 1.0f;\n  Beta beta = 0.0f;\n  // Setup input and output tensors, and the kernel parameters; and execute the kernel on device\n  gemm_host_f16xf16_f32_f32_tnt(device_A.data().get(), layout_A,\n                                device_B.data().get(), layout_B,\n                                device_C.data().get(), layout_C,\n                                device_D.data().get(), layout_D,\n                                alpha, beta);\n  // Host allocation for D tensor and transfer D tensor from device to host\n  thrust::host_vector<TypeD> host_D = device_D;\n  // Create a non-owning CuTe tensor for D tensor\n  Tensor host_tensor_D = make_tensor(host_D.data(), layout_D);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Execute reference GEMM kernel\n  //\n  ////////////////////////////////////////////////////////////\n\n  thrust::host_vector<TypeD> host_reference_D(Gemm_M*Gemm_N);\n  auto host_reference_tensor_D = make_tensor(host_reference_D.data(), layout_D);\n  reference_gemm<TypeAccumulator>(host_tensor_A, host_tensor_B, host_tensor_C, host_reference_tensor_D, alpha, beta);\n\n  ////////////////////////////////////////////////////////////\n  //\n  // Compare results\n  //\n  ////////////////////////////////////////////////////////////\n\n  auto relative_error = print_matrix_multiply_mollified_relative_error(type_str_a, host_tensor_A,\n                                                                       type_str_b, host_tensor_B,\n                                                                       type_str_d, host_tensor_D, host_reference_tensor_D);\n  bool success = relative_error <= 0.0;\n  std::cout << \"Execution is \" << ((success) ? \"successful.\" : \"failed.\") << std::endl;\n#else\n  std::cout << \"CUTLASS_ARCH_MMA_SM100_SUPPORTED must be enabled, but it is not. Test is waived \\n\" << std::endl;\n#endif\n\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/examples/cute/tutorial/blackwell/02_mma_tma_sm100.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Demonstrate CUTLASS debugging tool for dumping fragments and shared\n  memory\n */\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Standard Library includes\n\n#include <iostream>\n\n//\n// CUTLASS includes\n//\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/matrix_shape.h\"\n#include \"cutlass/numeric_types.h\"\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/transform/threadblock/regular_tile_iterator_tensor_op.h\"\n\n#include \"cutlass/util/debug.h\"\n#include \"cutlass/util/device_dump.h\"\n\n#define EXAMPLE_MATRIX_ROW 64\n#define EXAMPLE_MATRIX_COL 32\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Element, typename GmemIterator, typename SmemIterator>\n__global__ void kernel_dump(typename GmemIterator::Params params,\n                            typename GmemIterator::TensorRef ref) {\n  extern __shared__ Element shared_storage[];\n\n  // Construct the global iterator and load the data to the fragments.\n  int tb_thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  GmemIterator gmem_iterator(params, ref.data(),\n                             {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL},\n                             tb_thread_id);\n\n  typename GmemIterator::Fragment frag;\n\n  frag.clear();\n  gmem_iterator.load(frag);\n\n  // Call dump_fragment() with different parameters.\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nAll threads dump all the elements:\\n\");\n  cutlass::debug::dump_fragment(frag);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps all the elements:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps first 16 elements:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1, /*M = */ 16);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps first 16 elements with a stride of 8:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1, /*M = */ 16, /*S = */ 8);\n\n  // Construct the shared iterator and store the data to the shared memory.\n  SmemIterator smem_iterator(\n      typename SmemIterator::TensorRef(\n          {shared_storage, SmemIterator::Layout::packed(\n                               {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL})}),\n      tb_thread_id);\n\n  smem_iterator.store(frag);\n\n  // Call dump_shmem() with different parameters.\n  if (threadIdx.x == 0 && blockIdx.x == 0) printf(\"\\nDump all the elements:\\n\");\n  cutlass::debug::dump_shmem(shared_storage,\n                             EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nDump all the elements with a stride of 8:\\n\");\n  cutlass::debug::dump_shmem(\n      shared_storage, EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL, /*S = */ 8);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point for dump_reg_shmem example.\n//\n// usage:\n//\n//   02_dump_reg_shmem\n//\nint main() {\n  // Initialize a 64x32 column major matrix with sequential data (1,2,3...).\n  using Element = cutlass::half_t;\n  using Layout = cutlass::layout::ColumnMajor;\n\n  cutlass::HostTensor<Element, Layout> matrix(\n      {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL});\n  cutlass::reference::host::BlockFillSequential(matrix.host_data(),\n                                                matrix.capacity());\n\n  // Dump the matrix.\n  std::cout << \"Matrix:\\n\" << matrix.host_view() << \"\\n\";\n\n  // Copy the matrix to the device.\n  matrix.sync_device();\n\n  // Define a global iterator, a shared iterator and their thread map.\n  using ThreadMap = cutlass::transform::PitchLinearWarpRakedThreadMap<\n      cutlass::layout::PitchLinearShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>,\n      32, cutlass::layout::PitchLinearShape<8, 4>, 8>;\n\n  using GmemIterator =\n      cutlass::transform::threadblock::PredicatedTileIterator<\n          cutlass::MatrixShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>, Element,\n          Layout, 1, ThreadMap>;\n\n  typename GmemIterator::Params params(matrix.layout());\n\n  using SmemIterator = cutlass::transform::threadblock::RegularTileIterator<\n      cutlass::MatrixShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>, Element,\n      cutlass::layout::ColumnMajorTensorOpMultiplicandCongruous<16, 64>, 1,\n      ThreadMap>;\n\n  dim3 grid(1, 1);\n  dim3 block(32, 1, 1);\n\n  int smem_size =\n      int(sizeof(Element) * EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL);\n\n  kernel_dump<Element, GmemIterator, SmemIterator>\n      <<<grid, block, smem_size, 0>>>(params, matrix.device_ref());\n\n  cudaError_t result = cudaDeviceSynchronize();\n\n  if (result != cudaSuccess) {\n    std::cout << \"Failed\" << std::endl;\n  }\n\n  return (result == cudaSuccess ? 0 : -1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Demonstrate CUTLASS debugging tool for dumping fragments and shared\n  memory\n */\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n// Standard Library includes\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n\n//\n// CUTLASS includes\n//\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/matrix_shape.h\"\n#include \"cutlass/numeric_types.h\"\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/transform/threadblock/regular_tile_iterator_tensor_op.h\"\n\n#include \"cutlass/util/debug.h\"\n#include \"cutlass/util/device_dump.h\"\n\n#define EXAMPLE_MATRIX_ROW 64\n#define EXAMPLE_MATRIX_COL 32\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Element, typename GmemIterator, typename SmemIterator>\n__global__ void kernel_dump(typename GmemIterator::Params params,\n                            typename GmemIterator::TensorRef ref) {\n  extern __shared__ Element shared_storage[];\n\n  // Construct the global iterator and load the data to the fragments.\n  int tb_thread_id = threadIdx.y * blockDim.x + threadIdx.x;\n\n  GmemIterator gmem_iterator(params, ref.data(),\n                             {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL},\n                             tb_thread_id);\n\n  typename GmemIterator::Fragment frag;\n\n  frag.clear();\n  gmem_iterator.load(frag);\n\n  // Call dump_fragment() with different parameters.\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nAll threads dump all the elements:\\n\");\n  cutlass::debug::dump_fragment(frag);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps all the elements:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps first 16 elements:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1, /*M = */ 16);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nFirst thread dumps first 16 elements with a stride of 8:\\n\");\n  cutlass::debug::dump_fragment(frag, /*N = */ 1, /*M = */ 16, /*S = */ 8);\n\n  // Construct the shared iterator and store the data to the shared memory.\n  SmemIterator smem_iterator(\n      typename SmemIterator::TensorRef(\n          {shared_storage, SmemIterator::Layout::packed(\n                               {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL})}),\n      tb_thread_id);\n\n  smem_iterator.store(frag);\n\n  // Call dump_shmem() with different parameters.\n  if (threadIdx.x == 0 && blockIdx.x == 0) printf(\"\\nDump all the elements:\\n\");\n  cutlass::debug::dump_shmem(shared_storage,\n                             EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL);\n\n  if (threadIdx.x == 0 && blockIdx.x == 0)\n    printf(\"\\nDump all the elements with a stride of 8:\\n\");\n  cutlass::debug::dump_shmem(\n      shared_storage, EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL, /*S = */ 8);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point for dump_reg_shmem example.\n//\n// usage:\n//\n//   02_dump_reg_shmem\n//\nint main() {\n  // Initialize a 64x32 column major matrix with sequential data (1,2,3...).\n  using Element = cutlass::half_t;\n  using Layout = cutlass::layout::ColumnMajor;\n\n  cutlass::HostTensor<Element, Layout> matrix(\n      {EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL});\n  cutlass::reference::host::BlockFillSequential(matrix.host_data(),\n                                                matrix.capacity());\n\n  // Dump the matrix.\n  std::cout << \"Matrix:\\n\" << matrix.host_view() << \"\\n\";\n\n  // Copy the matrix to the device.\n  matrix.sync_device();\n\n  // Define a global iterator, a shared iterator and their thread map.\n  using ThreadMap = cutlass::transform::PitchLinearWarpRakedThreadMap<\n      cutlass::layout::PitchLinearShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>,\n      32, cutlass::layout::PitchLinearShape<8, 4>, 8>;\n\n  using GmemIterator =\n      cutlass::transform::threadblock::PredicatedTileIterator<\n          cutlass::MatrixShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>, Element,\n          Layout, 1, ThreadMap>;\n\n  typename GmemIterator::Params params(matrix.layout());\n\n  using SmemIterator = cutlass::transform::threadblock::RegularTileIterator<\n      cutlass::MatrixShape<EXAMPLE_MATRIX_ROW, EXAMPLE_MATRIX_COL>, Element,\n      cutlass::layout::ColumnMajorTensorOpMultiplicandCongruous<16, 64>, 1,\n      ThreadMap>;\n\n  dim3 grid(1, 1);\n  dim3 block(32, 1, 1);\n\n  int smem_size =\n      int(sizeof(Element) * EXAMPLE_MATRIX_ROW * EXAMPLE_MATRIX_COL);\n\n  kernel_dump<Element, GmemIterator, SmemIterator>\n      <<<grid, block, smem_size, 0>>>(params, matrix.device_ref());\n\n  hipError_t result = hipDeviceSynchronize();\n\n  if (result != hipSuccess) {\n    std::cout << \"Failed\" << std::endl;\n  }\n\n  return (result == hipSuccess ? 0 : -1);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/02_dump_reg_shmem/dump_reg_shmem.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS SYRK kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Syrk template is instantiated in the function CutlassSsyrkNN. This is kernel computes\n  the symmetric rank-k update (SYRK) using double-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 16x32x16 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the SSYRK kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for double-precision SYRK kernel\n//\n\n// Defines cutlass::gemm::device::Syrk, the generic Syrk computation template class.\n#include \"cutlass/gemm/device/rank_k.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS SYRK kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS SYRK template and launch a SYRK kernel.\ncudaError_t CutlassSsyrkNN(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  // Define type definition for double-precision CUTLASS SYRK with column-major\n  // input matrices and 16x32x16 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for double-precision SYRK. Typical values are used as\n  // default template arguments.\n  //\n  // To view the full syrk device API interface, see `cutlass/gemm/device/syrk.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassSyrk = cutlass::gemm::device::RankK<\n    double,\n    ColumnMajor,\n    double,\n    ColumnMajor,\n    cutlass::FillMode::kLower,\n    double,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<16, 32, 16>,\n    cutlass::gemm::GemmShape<16, 16, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n      double,\n      1,\n      double,\n      double\n    >,\n    cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<8>,\n    5,     // Stages\n    1,     // AlignmentA\n    false, // SplitKSerail\n    cutlass::arch::OpMultiplyAdd,\n    cutlass::ComplexTransform::kNone,\n    cutlass::BlasMode::kSymmetric\n  >;\n\n  // Define a CUTLASS SYRK type\n  CutlassSyrk syrk_operator;\n\n  // Construct the CUTLASS SYRK arguments object.\n  //\n  // One of CUTLASS's design patterns is to define syrk argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Syrk and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassSyrk::Arguments args(cutlass::gemm::GemmUniversalMode::kGemm,\n                              {N, N, K}, // Syrk Problem dimensions\n                              1, // batch_count,\n                              {alpha, beta}, // Scalars used in the Epilogue\n                              reinterpret_cast<void const *>(A),\n                              const_cast<void *>(reinterpret_cast<void *>(C)),\n                              reinterpret_cast<void *>(C), // destination matrix D (may be different memory than source C matrix)\n                              (int64_t)N*K, // Batch strides\n                              (int64_t)N*N,\n                              (int64_t)N*N,\n                              lda,\n                              ldc,\n                              ldc);\n\n  //\n  // Launch the CUTLASS SYRK kernel.\n  //\n\n  cutlass::Status status = syrk_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS SYRK operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return cudaErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  double *matrix,\n  int ldm,\n  int rows,\n  int columns,\n  int seed = 0) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    int offset = i + j * ldm;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    double value = double(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\ncudaError_t InitializeMatrix(double *matrix, int ldm, int rows, int columns, int seed = 0) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, ldm, rows, columns, seed);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\ncudaError_t AllocateMatrix(double **matrix, int ldm, int rows, int columns, int seed = 0) {\n  cudaError_t result;\n\n  size_t sizeof_matrix = sizeof(double) * ldm * columns;\n\n  // Allocate device memory.\n  result = cudaMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = cudaMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, ldm, rows, columns, seed);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference SYRK computation.\n__global__ void ReferenceSyrk_kernel(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < N && j < N && i >= j ) { // Since C is in Lower Fill Mode\n    double accumulator = 0;\n\n    for (int k = 0; k < K; ++k) {\n      accumulator += A[i + k * lda] * A[j + k * lda];\n    }\n\n    C[i + j * ldc] = alpha * accumulator + beta * C[i + j * ldc];\n  }\n}\n\n/// Reference SYRK computation.\ncudaError_t ReferenceSyrk(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (N + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceSyrk_kernel<<< grid, block >>>(N, K, alpha, A, lda, beta, C, ldc);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a double-precision\n/// CUTLASS SYRK kernel.\ncudaError_t TestCutlassSyrk(int N, int K, double alpha, double beta) {\n  cudaError_t result;\n\n  //\n  // Define several matrices to be used as operands to SYRK kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = N;\n  int ldc = N;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(double) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  double *A;\n  double *C_cutlass;\n  double *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, lda, N, K, 0);\n\n  if (result !=  cudaSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, ldc, N, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, ldc, N, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    cudaFree(C_cutlass);\n    return result;\n  }\n\n  result = cudaMemcpy(C_reference, C_cutlass, sizeof_C, cudaMemcpyDeviceToDevice);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS SYRK.\n  //\n\n  result = CutlassSsyrkNN(N, K, alpha, A, lda, beta, C_cutlass, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"CUTLASS SYRK kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference SYRK\n  result = ReferenceSyrk(N, K, alpha, A, lda, beta, C_reference, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Reference SYRK kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<double> host_cutlass(ldc * N, 0);\n  std::vector<double> host_reference(ldc * N, 0);\n\n  result = cudaMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy CUTLASS SYRK results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(A);\n\n    return result;\n  }\n\n  result = cudaMemcpy(host_reference.data(), C_reference, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy Reference SYRK results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  cudaFree(C_reference);\n  cudaFree(C_cutlass);\n  cudaFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return cudaErrorUnknown;\n  }\n\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_syrk example.\n//\n// usage:\n//\n//   00_basic_syrk <N> <K> <alpha> <beta>\n//\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA  Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  cudaDeviceProp props;\n\n  cudaError_t error = cudaGetDeviceProperties(&props, 0);\n  if (error != cudaSuccess) {\n    std::cerr << \"cudaGetDeviceProperties() returned an error: \" << cudaGetErrorString(error) << std::endl;\n\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    return 0;\n  }\n\n  //\n  // Parse the command line to obtain SYRK dimensions and scalar values.\n  //\n\n  // SYRK problem dimensions.\n  int problem[2] = { 128, 128 };\n\n  for (int i = 1; i < argc && i < 3; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  double scalars[2] = { 1, 0 };\n\n  for (int i = 3; i < argc && i < 5; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 3];\n  }\n\n  //\n  // Run the CUTLASS SYRK test.\n  //\n\n  cudaError_t result = TestCutlassSyrk(\n    problem[0],     // SYRK N dimension\n    problem[1],     // SYRK K dimension\n    scalars[0],     // alpha\n    scalars[1]      // beta\n  );\n\n  if (result == cudaSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == cudaSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS SYRK kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Syrk template is instantiated in the function CutlassSsyrkNN. This is kernel computes\n  the symmetric rank-k update (SYRK) using double-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 16x32x16 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the SSYRK kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for double-precision SYRK kernel\n//\n\n// Defines cutlass::gemm::device::Syrk, the generic Syrk computation template class.\n#include \"cutlass/gemm/device/rank_k.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS SYRK kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS SYRK template and launch a SYRK kernel.\nhipError_t CutlassSsyrkNN(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  // Define type definition for double-precision CUTLASS SYRK with column-major\n  // input matrices and 16x32x16 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for double-precision SYRK. Typical values are used as\n  // default template arguments.\n  //\n  // To view the full syrk device API interface, see `cutlass/gemm/device/syrk.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassSyrk = cutlass::gemm::device::RankK<\n    double,\n    ColumnMajor,\n    double,\n    ColumnMajor,\n    cutlass::FillMode::kLower,\n    double,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<16, 32, 16>,\n    cutlass::gemm::GemmShape<16, 16, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n      double,\n      1,\n      double,\n      double\n    >,\n    cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<8>,\n    5,     // Stages\n    1,     // AlignmentA\n    false, // SplitKSerail\n    cutlass::arch::OpMultiplyAdd,\n    cutlass::ComplexTransform::kNone,\n    cutlass::BlasMode::kSymmetric\n  >;\n\n  // Define a CUTLASS SYRK type\n  CutlassSyrk syrk_operator;\n\n  // Construct the CUTLASS SYRK arguments object.\n  //\n  // One of CUTLASS's design patterns is to define syrk argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Syrk and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassSyrk::Arguments args(cutlass::gemm::GemmUniversalMode::kGemm,\n                              {N, N, K}, // Syrk Problem dimensions\n                              1, // batch_count,\n                              {alpha, beta}, // Scalars used in the Epilogue\n                              reinterpret_cast<void const *>(A),\n                              const_cast<void *>(reinterpret_cast<void *>(C)),\n                              reinterpret_cast<void *>(C), // destination matrix D (may be different memory than source C matrix)\n                              (int64_t)N*K, // Batch strides\n                              (int64_t)N*N,\n                              (int64_t)N*N,\n                              lda,\n                              ldc,\n                              ldc);\n\n  //\n  // Launch the CUTLASS SYRK kernel.\n  //\n\n  cutlass::Status status = syrk_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS SYRK operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return hipErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  double *matrix,\n  int ldm,\n  int rows,\n  int columns,\n  int seed = 0) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    int offset = i + j * ldm;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    double value = double(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\nhipError_t InitializeMatrix(double *matrix, int ldm, int rows, int columns, int seed = 0) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, ldm, rows, columns, seed);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\nhipError_t AllocateMatrix(double **matrix, int ldm, int rows, int columns, int seed = 0) {\n  hipError_t result;\n\n  size_t sizeof_matrix = sizeof(double) * ldm * columns;\n\n  // Allocate device memory.\n  result = hipMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = hipMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, ldm, rows, columns, seed);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference SYRK computation.\n__global__ void ReferenceSyrk_kernel(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < N && j < N && i >= j ) { // Since C is in Lower Fill Mode\n    double accumulator = 0;\n\n    for (int k = 0; k < K; ++k) {\n      accumulator += A[i + k * lda] * A[j + k * lda];\n    }\n\n    C[i + j * ldc] = alpha * accumulator + beta * C[i + j * ldc];\n  }\n}\n\n/// Reference SYRK computation.\nhipError_t ReferenceSyrk(\n  int N,\n  int K,\n  double alpha,\n  double const *A,\n  int lda,\n  double beta,\n  double *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (N + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceSyrk_kernel<<< grid, block >>>(N, K, alpha, A, lda, beta, C, ldc);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a double-precision\n/// CUTLASS SYRK kernel.\nhipError_t TestCutlassSyrk(int N, int K, double alpha, double beta) {\n  hipError_t result;\n\n  //\n  // Define several matrices to be used as operands to SYRK kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = N;\n  int ldc = N;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(double) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  double *A;\n  double *C_cutlass;\n  double *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, lda, N, K, 0);\n\n  if (result !=  hipSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, ldc, N, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, ldc, N, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    hipFree(C_cutlass);\n    return result;\n  }\n\n  result = hipMemcpy(C_reference, C_cutlass, sizeof_C, hipMemcpyDeviceToDevice);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS SYRK.\n  //\n\n  result = CutlassSsyrkNN(N, K, alpha, A, lda, beta, C_cutlass, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"CUTLASS SYRK kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference SYRK\n  result = ReferenceSyrk(N, K, alpha, A, lda, beta, C_reference, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Reference SYRK kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<double> host_cutlass(ldc * N, 0);\n  std::vector<double> host_reference(ldc * N, 0);\n\n  result = hipMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy CUTLASS SYRK results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(A);\n\n    return result;\n  }\n\n  result = hipMemcpy(host_reference.data(), C_reference, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy Reference SYRK results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  hipFree(C_reference);\n  hipFree(C_cutlass);\n  hipFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return hipErrorUnknown;\n  }\n\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_syrk example.\n//\n// usage:\n//\n//   00_basic_syrk <N> <K> <alpha> <beta>\n//\nint main(int argc, const char *arg[]) {\n\n  bool notSupported = false;\n\n  // CUTLASS must be compiled with CUDA 11 Toolkit to run these examples.\n  if (!(__CUDACC_VER_MAJOR__ >= 11)) {\n    std::cerr << \"NVIDIA  Ampere Tensor Core operations must be compiled with CUDA 11.0 Toolkit or later.\" << std::endl;\n    notSupported = true;\n  }\n\n  hipDeviceProp_t props;\n\n  hipError_t error = hipGetDeviceProperties(&props, 0);\n  if (error != hipSuccess) {\n    std::cerr << \"hipGetDeviceProperties() returned an error: \" << hipGetErrorString(error) << std::endl;\n\n    return -1;\n  }\n\n  if (!((props.major * 10 + props.minor) >= 80)) {\n\n    std::cerr << \"This example requires compute capability at least 80.\"\n              << std::endl;\n    notSupported = true;\n  }\n\n  if (notSupported) {\n    return 0;\n  }\n\n  //\n  // Parse the command line to obtain SYRK dimensions and scalar values.\n  //\n\n  // SYRK problem dimensions.\n  int problem[2] = { 128, 128 };\n\n  for (int i = 1; i < argc && i < 3; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  double scalars[2] = { 1, 0 };\n\n  for (int i = 3; i < argc && i < 5; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 3];\n  }\n\n  //\n  // Run the CUTLASS SYRK test.\n  //\n\n  hipError_t result = TestCutlassSyrk(\n    problem[0],     // SYRK N dimension\n    problem[1],     // SYRK K dimension\n    scalars[0],     // alpha\n    scalars[1]      // beta\n  );\n\n  if (result == hipSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == hipSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/31_basic_syrk/basic_syrk.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS GEMM kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Gemm template is instantiated in the function CutlassSgemmNN. This is kernel computes\n  the general matrix product (GEMM) using single-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 128x128x8 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the SGEMM kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n  This example has delibrately been kept similar to the basic_gemm example from cutlass-1.3 to\n  highlight the minimum amount of differences needed to transition to cutlass-2.0.\n\n  Cutlass-1.3 sgemm: https://github.com/NVIDIA/cutlass/blob/master/examples/00_basic_gemm/basic_gemm.cu\n*/\n\n// Standard Library includes\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for single-precision GEMM kernel\n//\n\n// Defines cutlass::gemm::device::Gemm, the generic Gemm computation template class.\n#include \"cutlass/gemm/device/gemm.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS GEMM kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS GEMM template and launch a GEMM kernel.\ncudaError_t CutlassSgemmNN(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  // Define type definition for single-precision CUTLASS GEMM with column-major\n  // input matrices and 128x128x8 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for single-precision GEMM. Typical values are used as\n  // default template arguments. See `cutlass/gemm/device/default_gemm_configuration.h` for more details.\n  //\n  // To view the full gemm device API interface, see `cutlass/gemm/device/gemm.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassGemm = cutlass::gemm::device::Gemm<float,        // Data-type of A matrix\n                                                  ColumnMajor,  // Layout of A matrix\n                                                  float,        // Data-type of B matrix\n                                                  ColumnMajor,  // Layout of B matrix\n                                                  float,        // Data-type of C matrix\n                                                  ColumnMajor>; // Layout of C matrix\n\n  // Define a CUTLASS GEMM type\n  CutlassGemm gemm_operator;\n\n  // Construct the CUTLASS GEMM arguments object.\n  //\n  // One of CUTLASS's design patterns is to define gemm argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Gemm and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassGemm::Arguments args({M , N, K},  // Gemm Problem dimensions\n                              {A, lda},    // Tensor-ref for source matrix A\n                              {B, ldb},    // Tensor-ref for source matrix B\n                              {C, ldc},    // Tensor-ref for source matrix C\n                              {C, ldc},    // Tensor-ref for destination matrix D (may be different memory than source C matrix)\n                              {alpha, beta}); // Scalars used in the Epilogue\n\n  //\n  // Launch the CUTLASS GEMM kernel.\n  //\n  \n  cutlass::Status status = gemm_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS GEMM operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return cudaErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  float *matrix,\n  int rows,\n  int columns,\n  int seed = 0) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    int offset = i + j * rows;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    float value = float(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\ncudaError_t InitializeMatrix(float *matrix, int rows, int columns, int seed = 0) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, rows, columns, seed);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\ncudaError_t AllocateMatrix(float **matrix, int rows, int columns, int seed = 0) {\n  cudaError_t result;\n\n  size_t sizeof_matrix = sizeof(float) * rows * columns;\n\n  // Allocate device memory.\n  result = cudaMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = cudaMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, rows, columns, seed);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << cudaGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference GEMM computation.\n__global__ void ReferenceGemm_kernel(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < M && j < N) {\n    float accumulator = 0;\n\n    for (int k = 0; k < K; ++k) {\n      accumulator += A[i + k * lda] * B[k + j * ldb];\n    }\n\n    C[i + j * ldc] = alpha * accumulator + beta * C[i + j * ldc];\n  }\n}\n\n/// Reference GEMM computation.\ncudaError_t ReferenceGemm(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (M + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceGemm_kernel<<< grid, block >>>(M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);\n\n  return cudaGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a single-precision\n/// CUTLASS GEMM kernel.\ncudaError_t TestCutlassGemm(int M, int N, int K, float alpha, float beta) {\n  cudaError_t result;\n\n  //\n  // Define several matrices to be used as operands to GEMM kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = M;\n  int ldb = K;\n  int ldc = M;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(float) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  float *A;\n  float *B;\n  float *C_cutlass;\n  float *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, M, K, 0);\n\n  if (result !=  cudaSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&B, K, N, 17);\n\n  if (result !=  cudaSuccess) {\n    cudaFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, M, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    cudaFree(B);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, M, N, 101);\n\n  if (result != cudaSuccess) {\n    cudaFree(A);\n    cudaFree(B);\n    cudaFree(C_cutlass);\n    return result;\n  }\n\n  result = cudaMemcpy(C_reference, C_cutlass, sizeof_C, cudaMemcpyDeviceToDevice);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS GEMM.\n  //\n\n  result = CutlassSgemmNN(M, N, K, alpha, A, lda, B, ldb, beta, C_cutlass, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"CUTLASS GEMM kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference GEMM\n  result = ReferenceGemm(M, N, K, alpha, A, lda, B, ldb, beta, C_reference, ldc);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Reference GEMM kernel failed: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<float> host_cutlass(ldc * N, 0);\n  std::vector<float> host_reference(ldc * N, 0);\n\n  result = cudaMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy CUTLASS GEMM results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  result = cudaMemcpy(host_reference.data(), C_reference, sizeof_C, cudaMemcpyDeviceToHost);\n\n  if (result != cudaSuccess) {\n    std::cerr << \"Failed to copy Reference GEMM results: \"\n      << cudaGetErrorString(result) << std::endl;\n\n    cudaFree(C_reference);\n    cudaFree(C_cutlass);\n    cudaFree(B);\n    cudaFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  cudaFree(C_reference);\n  cudaFree(C_cutlass);\n  cudaFree(B);\n  cudaFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return cudaErrorUnknown;\n  }\n\n  return cudaSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_gemm example.\n//\n// usage:\n//\n//   00_basic_gemm <M> <N> <K> <alpha> <beta>\n//\nint main(int argc, const char *arg[]) {\n\n  //\n  // Parse the command line to obtain GEMM dimensions and scalar values.\n  //\n\n  // GEMM problem dimensions.\n  int problem[3] = { 128, 128, 128 };\n\n  for (int i = 1; i < argc && i < 4; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  float scalars[2] = { 1, 0 };\n\n  for (int i = 4; i < argc && i < 6; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 4];\n  }\n\n  //\n  // Run the CUTLASS GEMM test.\n  //\n\n  cudaError_t result = TestCutlassGemm(\n    problem[0],     // GEMM M dimension\n    problem[1],     // GEMM N dimension\n    problem[2],     // GEMM K dimension\n    scalars[0],     // alpha\n    scalars[1]      // beta\n  );\n\n  if (result == cudaSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == cudaSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*\n  This example demonstrates how to call a CUTLASS GEMM kernel and provides a naive reference\n  matrix multiply kernel to verify its correctness.\n\n  The CUTLASS Gemm template is instantiated in the function CutlassSgemmNN. This is kernel computes\n  the general matrix product (GEMM) using single-precision floating-point arithmetic and assumes\n  all matrices have column-major layout.\n\n  The threadblock tile size is chosen as 128x128x8 which offers good performance for large matrices.\n  See the CUTLASS Parallel for All blog post for more exposition on the tunable parameters available\n  in CUTLASS.\n\n  https://devblogs.nvidia.com/cutlass-linear-algebra-cuda/\n\n  Aside from defining and launching the SGEMM kernel, this example does not use any other components\n  or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are\n  prevalent in the CUTLASS unit tests.\n\n  This example has delibrately been kept similar to the basic_gemm example from cutlass-1.3 to\n  highlight the minimum amount of differences needed to transition to cutlass-2.0.\n\n  Cutlass-1.3 sgemm: https://github.com/NVIDIA/cutlass/blob/master/examples/00_basic_gemm/basic_gemm.cu\n*/\n\n// Standard Library includes\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <sstream>\n#include <vector>\n\n// Helper methods to check for errors\n#include \"helper.h\"\n\n//\n// CUTLASS includes needed for single-precision GEMM kernel\n//\n\n// Defines cutlass::gemm::device::Gemm, the generic Gemm computation template class.\n#include \"cutlass/gemm/device/gemm.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// This function defines a CUTLASS GEMM kernel instantiation, constructs its parameters object,\n// and launches it on the CUDA device.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Define a CUTLASS GEMM template and launch a GEMM kernel.\nhipError_t CutlassSgemmNN(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  // Define type definition for single-precision CUTLASS GEMM with column-major\n  // input matrices and 128x128x8 threadblock tile size (chosen by default).\n  //\n  // To keep the interface manageable, several helpers are defined for plausible compositions\n  // including the following example for single-precision GEMM. Typical values are used as\n  // default template arguments. See `cutlass/gemm/device/default_gemm_configuration.h` for more details.\n  //\n  // To view the full gemm device API interface, see `cutlass/gemm/device/gemm.h`\n\n  using ColumnMajor = cutlass::layout::ColumnMajor;\n\n  using CutlassGemm = cutlass::gemm::device::Gemm<float,        // Data-type of A matrix\n                                                  ColumnMajor,  // Layout of A matrix\n                                                  float,        // Data-type of B matrix\n                                                  ColumnMajor,  // Layout of B matrix\n                                                  float,        // Data-type of C matrix\n                                                  ColumnMajor>; // Layout of C matrix\n\n  // Define a CUTLASS GEMM type\n  CutlassGemm gemm_operator;\n\n  // Construct the CUTLASS GEMM arguments object.\n  //\n  // One of CUTLASS's design patterns is to define gemm argument objects that are constructible\n  // in host code and passed to kernels by value. These may include pointers, strides, scalars,\n  // and other arguments needed by Gemm and its components.\n  //\n  // The benefits of this pattern are (1.) a structured, composable strategy for passing host-constructible\n  // arguments to kernels and (2.) minimized initialization overhead on kernel entry.\n  //\n  CutlassGemm::Arguments args({M , N, K},  // Gemm Problem dimensions\n                              {A, lda},    // Tensor-ref for source matrix A\n                              {B, ldb},    // Tensor-ref for source matrix B\n                              {C, ldc},    // Tensor-ref for source matrix C\n                              {C, ldc},    // Tensor-ref for destination matrix D (may be different memory than source C matrix)\n                              {alpha, beta}); // Scalars used in the Epilogue\n\n  //\n  // Launch the CUTLASS GEMM kernel.\n  //\n  \n  cutlass::Status status = gemm_operator(args);\n\n  //\n  // Return a cudaError_t if the CUTLASS GEMM operator returned an error code.\n  //\n\n  if (status != cutlass::Status::kSuccess) {\n    return hipErrorUnknown;\n  }\n\n  // Return success, if no errors were encountered.\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// The source code after this point in the file is generic CUDA using the CUDA Runtime API\n// and simple CUDA kernels to initialize matrices and compute the general matrix product.\n//\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel to initialize a matrix with small integers.\n__global__ void InitializeMatrix_kernel(\n  float *matrix,\n  int rows,\n  int columns,\n  int seed = 0) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < rows && j < columns) {\n    int offset = i + j * rows;\n\n    // Generate arbitrary elements.\n    int const k = 16807;\n    int const m = 16;\n    float value = float(((offset + seed) * k % m) - m / 2);\n\n    matrix[offset] = value;\n  }\n}\n\n/// Simple function to initialize a matrix to arbitrary small integers.\nhipError_t InitializeMatrix(float *matrix, int rows, int columns, int seed = 0) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (rows + block.x - 1) / block.x,\n    (columns + block.y - 1) / block.y\n  );\n\n  InitializeMatrix_kernel<<< grid, block >>>(matrix, rows, columns, seed);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocates device memory for a matrix then fills with arbitrary small integers.\nhipError_t AllocateMatrix(float **matrix, int rows, int columns, int seed = 0) {\n  hipError_t result;\n\n  size_t sizeof_matrix = sizeof(float) * rows * columns;\n\n  // Allocate device memory.\n  result = hipMalloc(reinterpret_cast<void **>(matrix), sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to allocate matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Clear the allocation.\n  result = hipMemset(*matrix, 0, sizeof_matrix);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to clear matrix device memory: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  // Initialize matrix elements to arbitrary small integers.\n  result = InitializeMatrix(*matrix, rows, columns, seed);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to initialize matrix: \"\n      << hipGetErrorString(result) << std::endl;\n    return result;\n  }\n\n  return result;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Naive reference GEMM computation.\n__global__ void ReferenceGemm_kernel(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  int i = threadIdx.x + blockIdx.x * blockDim.x;\n  int j = threadIdx.y + blockIdx.y * blockDim.y;\n\n  if (i < M && j < N) {\n    float accumulator = 0;\n\n    for (int k = 0; k < K; ++k) {\n      accumulator += A[i + k * lda] * B[k + j * ldb];\n    }\n\n    C[i + j * ldc] = alpha * accumulator + beta * C[i + j * ldc];\n  }\n}\n\n/// Reference GEMM computation.\nhipError_t ReferenceGemm(\n  int M,\n  int N,\n  int K,\n  float alpha,\n  float const *A,\n  int lda,\n  float const *B,\n  int ldb,\n  float beta,\n  float *C,\n  int ldc) {\n\n  dim3 block(16, 16);\n  dim3 grid(\n    (M + block.x - 1) / block.x,\n    (N + block.y - 1) / block.y\n  );\n\n  ReferenceGemm_kernel<<< grid, block >>>(M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);\n\n  return hipGetLastError();\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Allocate several matrices in GPU device memory and call a single-precision\n/// CUTLASS GEMM kernel.\nhipError_t TestCutlassGemm(int M, int N, int K, float alpha, float beta) {\n  hipError_t result;\n\n  //\n  // Define several matrices to be used as operands to GEMM kernels.\n  //\n\n  // Compute leading dimensions for each matrix.\n  int lda = M;\n  int ldb = K;\n  int ldc = M;\n\n  // Compute size in bytes of the C matrix.\n  size_t sizeof_C = sizeof(float) * ldc * N;\n\n  // Define pointers to matrices in GPU device memory.\n  float *A;\n  float *B;\n  float *C_cutlass;\n  float *C_reference;\n\n  //\n  // Allocate matrices in GPU device memory with arbitrary seeds.\n  //\n\n  result = AllocateMatrix(&A, M, K, 0);\n\n  if (result !=  hipSuccess) {\n    return result;\n  }\n\n  result = AllocateMatrix(&B, K, N, 17);\n\n  if (result !=  hipSuccess) {\n    hipFree(A);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_cutlass, M, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    hipFree(B);\n    return result;\n  }\n\n  result = AllocateMatrix(&C_reference, M, N, 101);\n\n  if (result != hipSuccess) {\n    hipFree(A);\n    hipFree(B);\n    hipFree(C_cutlass);\n    return result;\n  }\n\n  result = hipMemcpy(C_reference, C_cutlass, sizeof_C, hipMemcpyDeviceToDevice);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy C_cutlass matrix to C_reference: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Launch CUTLASS GEMM.\n  //\n\n  result = CutlassSgemmNN(M, N, K, alpha, A, lda, B, ldb, beta, C_cutlass, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"CUTLASS GEMM kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Verify.\n  //\n\n  // Launch reference GEMM\n  result = ReferenceGemm(M, N, K, alpha, A, lda, B, ldb, beta, C_reference, ldc);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Reference GEMM kernel failed: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  // Copy to host and verify equivalence.\n  std::vector<float> host_cutlass(ldc * N, 0);\n  std::vector<float> host_reference(ldc * N, 0);\n\n  result = hipMemcpy(host_cutlass.data(), C_cutlass, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy CUTLASS GEMM results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  result = hipMemcpy(host_reference.data(), C_reference, sizeof_C, hipMemcpyDeviceToHost);\n\n  if (result != hipSuccess) {\n    std::cerr << \"Failed to copy Reference GEMM results: \"\n      << hipGetErrorString(result) << std::endl;\n\n    hipFree(C_reference);\n    hipFree(C_cutlass);\n    hipFree(B);\n    hipFree(A);\n\n    return result;\n  }\n\n  //\n  // Free device memory allocations.\n  //\n\n  hipFree(C_reference);\n  hipFree(C_cutlass);\n  hipFree(B);\n  hipFree(A);\n\n  //\n  // Test for bit equivalence of results.\n  //\n\n  if (host_cutlass != host_reference) {\n    std::cerr << \"CUTLASS results incorrect.\" << std::endl;\n\n    return hipErrorUnknown;\n  }\n\n  return hipSuccess;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Entry point to basic_gemm example.\n//\n// usage:\n//\n//   00_basic_gemm <M> <N> <K> <alpha> <beta>\n//\nint main(int argc, const char *arg[]) {\n\n  //\n  // Parse the command line to obtain GEMM dimensions and scalar values.\n  //\n\n  // GEMM problem dimensions.\n  int problem[3] = { 128, 128, 128 };\n\n  for (int i = 1; i < argc && i < 4; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> problem[i - 1];\n  }\n\n  // Scalars used for linear scaling the result of the matrix product.\n  float scalars[2] = { 1, 0 };\n\n  for (int i = 4; i < argc && i < 6; ++i) {\n    std::stringstream ss(arg[i]);\n    ss >> scalars[i - 4];\n  }\n\n  //\n  // Run the CUTLASS GEMM test.\n  //\n\n  hipError_t result = TestCutlassGemm(\n    problem[0],     // GEMM M dimension\n    problem[1],     // GEMM N dimension\n    problem[2],     // GEMM K dimension\n    scalars[0],     // alpha\n    scalars[1]      // beta\n  );\n\n  if (result == hipSuccess) {\n    std::cout << \"Passed.\" << std::endl;\n  }\n\n  // Exit.\n  return result == hipSuccess ? 0 : -1;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/examples/00_basic_gemm/basic_gemm.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/* \\file\n   \\brief Defines a math function\n*/\n\n#include <algorithm>\n#include <stdexcept>\n#include <iomanip>\n#include <cstring>\n#include <fstream>\n#include <sstream>\n\n#ifdef __unix__\n#include <unistd.h>\n#elif defined(_WIN32) || defined(WIN32)\n#include <windows.h>\n#else\n// sleep not supported\n#endif\n\n#include <cuda/atomic>\n\n#include \"cutlass/profiler/options.h\"\n#include \"cutlass/profiler/operation_profiler.h\"\n#include \"cutlass/profiler/gpu_timer.h\"\n\n#include \"cutlass/trace.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace cutlass {\nnamespace profiler {\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nOperationProfiler::OperationProfiler(): kind_(library::OperationKind::kInvalid) { }\n\n/// Ctor\nOperationProfiler::OperationProfiler(\n  Options const &options,\n  library::OperationKind kind,\n  ArgumentDescriptionVector const &arguments,\n  ProviderVector const & verification_providers\n):\n  kind_(kind), arguments_(arguments) {\n\n  ArgumentDescriptionVector tile_description_arguments{\n    {ArgumentTypeID::kEnumerated, {\"op_class\", \"opcode-class\"}, \"Class of math instruction (simt, tensorop, wmmatensorop, wmma)\"},\n    {ArgumentTypeID::kEnumerated, {\"accum\", \"accumulator-type\"}, \"Math instruction accumulator data type\"},\n    {ArgumentTypeID::kInteger, {\"cta_m\", \"threadblock-shape::m\"}, \"Threadblock shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cta_n\", \"threadblock-shape::n\"}, \"Threadblock shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cta_k\", \"threadblock-shape::k\"}, \"Threadblock shape in the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_m\", \"cluster-shape::m\"}, \"Cluster shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_n\", \"cluster-shape::n\"}, \"Cluster shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_k\", \"cluster-shape::k\"}, \"Cluster shape in the K dimension\"},\n    \n    {ArgumentTypeID::kInteger, {\"cluster_m_fallback\", \"cluster-shape-fallback::m\"}, \"Fallback Cluster shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_n_fallback\", \"cluster-shape-fallback::n\"}, \"Fallback Cluster shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_k_fallback\", \"cluster-shape-fallback::k\"}, \"Fallback Cluster shape in the K dimension\"},\n    \n    {ArgumentTypeID::kInteger, {\"stages\", \"threadblock-stages\"}, \"Number of stages of threadblock-scoped matrix multiply\"},\n    {ArgumentTypeID::kInteger, {\"warps_m\", \"warp-count::m\"}, \"Number of warps within threadblock along the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"warps_n\", \"warp-count::n\"}, \"Number of warps within threadblock along the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"warps_k\", \"warp-count::k\"}, \"Number of warps within threadblock along the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_m\", \"instruction-shape::m\"}, \"Math instruction shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_n\", \"instruction-shape::n\"}, \"Math instruction shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_k\", \"instruction-shape::k\"}, \"Math instruction shape in the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"min_cc\", \"minimum-compute-capability\"}, \"Minimum device compute capability\"},\n    {ArgumentTypeID::kInteger, {\"max_cc\", \"maximum-compute-capability\"}, \"Maximum device compute capability\"}\n  };\n\n  arguments_.insert(arguments_.end(), tile_description_arguments.begin(), tile_description_arguments.end());\n\n  for (auto provider : verification_providers) {\n    if (std::find(\n      options.verification.providers.begin(),\n      options.verification.providers.end(),\n      provider) != options.verification.providers.end()) {\n\n      verification_providers_.push_back(provider);\n    }\n  }\n\n}\n\n/// Destructor\nOperationProfiler::~OperationProfiler() {}\n\n/// Gets the schema description\nstd::string const & OperationProfiler::description() const {\n  return description_;\n}\n\n/// Prints usage statement for the math function\nvoid OperationProfiler::print_usage(std::ostream &out) const {\n  for (auto const & desc : arguments_) {\n\n    size_t const kAliasStart = 10;\n\n    size_t columns = 0;\n\n    std::string type_str = to_string(desc.type);\n    columns += type_str.size();\n\n    out << \"  [\" << type_str << \"]\";\n\n    if (columns < kAliasStart) {\n      out << std::string(kAliasStart - columns, ' ');\n    }\n\n    columns = 0;\n\n    int j = 0;\n    for (auto const & alias : desc.aliases) {\n      columns += alias.size() + (j ? 1 : 0) + 2;\n\n      out << (j++ ? \",\" : \"\") << \"--\" << alias;\n    }\n\n    size_t const kTotalColumns = 50;\n\n    if (columns < kTotalColumns) {\n      out << std::string(kTotalColumns - columns, ' ');\n    }\n\n    out << desc.description << \"\\n\";\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Returns true if the current operation description satisfies the problem space\nbool OperationProfiler::satisfies(\n  library::OperationDescription const &op_desc,\n  ProblemSpace const &problem_space,\n  ProblemSpace::Problem const &problem) {\n\n  library::OpcodeClassID opcode_class;\n  if (arg_as_OpcodeClassID(opcode_class, \"op_class\", problem_space, problem)) {\n    if (opcode_class != op_desc.tile_description.math_instruction.opcode_class) {\n      return false;\n    }\n  }\n  \n  bool dynamic_cluster = int64_t(op_desc.tile_description.cluster_shape.m()) == 0 ||\n                         int64_t(op_desc.tile_description.cluster_shape.n()) == 0 ||\n                         int64_t(op_desc.tile_description.cluster_shape.k()) == 0;\n  \n  int64_t int_value;\n\n  if (arg_as_int(int_value, \"inst_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"inst_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"inst_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  if (!dynamic_cluster) { \n  if (arg_as_int(int_value, \"cluster_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cluster_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cluster_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  } \n  if (arg_as_int(int_value, \"stages\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_stages) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.k()) != int_value) {\n      return false;\n    }\n  }\n\n  library::NumericTypeID numeric_type;\n  if (arg_as_NumericTypeID(numeric_type, \"accum\", problem_space, problem)) {\n    if (numeric_type != op_desc.tile_description.math_instruction.element_accumulator) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n\nstd::ostream& operator<<(std::ostream& out, library::Provider provider) {\n  if (provider == library::Provider::kNone) {\n    out << \"kNone\";\n  }\n  else if (provider == library::Provider::kCUTLASS) {\n    out << \"kCUTLASS\";\n  }\n  else if (provider == library::Provider::kReferenceHost) {\n    out << \"kReferenceHost\";\n  }\n  else if (provider == library::Provider::kReferenceDevice) {\n    out << \"kReferenceDevice\";\n  }\n  else if (provider == library::Provider::kCUBLAS) {\n    out << \"kCUBLAS\";\n  }\n  else if (provider == library::Provider::kCUDNN) {\n    out << \"kCUDNN\";\n  }\n  else {\n    out << \"kInvalid\";\n  }\n\n  return out;\n}\n\nstd::ostream& operator<<(std::ostream& out, library::OperationKind op_kind) {\n  if (op_kind == library::OperationKind::kGemm) {\n    out << \"kGemm\";\n  }\n  else if (op_kind == library::OperationKind::kBlockScaledGemm) {\n    out << \"kBlockScaledGemm\";\n  }\n  else if (op_kind == library::OperationKind::kBlockwiseGemm) {\n    out << \"kBlockwiseGemm\";\n  }\n  else if (op_kind == library::OperationKind::kRankK) {\n    out << \"kRankK\";\n  }\n  else if (op_kind == library::OperationKind::kRank2K) {\n    out << \"kRank2K\";\n  }\n  else if (op_kind == library::OperationKind::kTrmm) {\n    out << \"kTrmm\";\n  }\n  else if (op_kind == library::OperationKind::kSymm) {\n    out << \"kSymm\";\n  }\n  else if (op_kind == library::OperationKind::kConv2d) {\n    out << \"kConv2d\";\n  }\n  else if (op_kind == library::OperationKind::kConv3d) {\n    out << \"kConv3d\";\n  }\n  else if (op_kind == library::OperationKind::kEqGemm) {\n    out << \"kEqGemm\";\n  }\n  else if (op_kind == library::OperationKind::kSparseGemm) {\n    out << \"kSparseGemm\";\n  }\n  else if (op_kind == library::OperationKind::kReduction) {\n    out << \"kReduction\";\n  }\n  else if (op_kind == library::OperationKind::kGroupedGemm) {\n    out << \"kGroupedGemm\";\n  }\n  else {\n    out << \"kInvalid\";\n  }\n\n  return out;\n}\n\n#endif // defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n\n/// Entry point to profile all operations in the manifest\nint OperationProfiler::profile_all(\n  Options const &options,\n  library::Manifest const &manifest,\n  DeviceContext &device_context) {\n  ProblemSpace cmdline_problem_space(arguments_, options.cmdline);\n\n  bool do_testlist_run = !options.operation_problems.empty();\n\n  std::vector<std::pair<std::string, std::unique_ptr<ProblemSpace>>> all_operations_and_problems;\n  if (do_testlist_run) {\n    for (const auto& [operation_name, cmd_vec] : options.operation_problems)  {\n      for (auto& cmd_line : cmd_vec) {\n        all_operations_and_problems.push_back({operation_name, std::make_unique<ProblemSpace>(arguments_, cmd_line)});\n      }\n    }\n  }\n\n  // 1. Construct performance report\n  PerformanceReport report(options, cmdline_problem_space.argument_names(), kind_);\n\n  //\n  int retval = 0;\n\n  size_t bound = (all_operations_and_problems.empty() ? 1 : all_operations_and_problems.size());\n  for (size_t i = 0; i < bound; i++) {\n\n    // New problem space for each operation if we are running a testlist\n    ProblemSpace& problem_space = do_testlist_run ? *all_operations_and_problems[i].second : cmdline_problem_space;\n\n    // 2. For each problem in problem space\n    ProblemSpace::Iterator problem_it = problem_space.begin();\n    ProblemSpace::Iterator problem_end = problem_space.end();\n\n    bool continue_profiling = true;\n\n    // For each problem in problem space\n    for (; continue_profiling && problem_it != problem_end; ++problem_it) {\n      ProblemSpace::Problem problem = problem_it.at();\n      report.next_problem();\n\n      // For each operation in manifest\n      int matched_operation_count = 0;\n      int profiled_operation_count = 0;\n      for (auto const& operation_ptr : manifest) {\n\n        library::Operation const *operation = operation_ptr.get();\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        std::cerr << \"  Operation: \" << typeid(*operation).name() << \"\\n\"\n                  << \"    name: \" << operation->description().name << \"\\n\"\n                  << \"    kind: \" << operation->description().kind << \"\\n\"\n                  << \"    provider: \" << operation->description().provider << \"\\n\";\n#endif // CUTLASS_DEBUG_TRACE_LEVEL\n\n        auto min_cc = operation->description().tile_description.minimum_compute_capability;\n        auto max_cc = operation->description().tile_description.maximum_compute_capability;\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        std::cerr << \"    min_cc: \" << min_cc << \"\\n\";\n        std::cerr << \"    max_cc: \" << min_cc << \"\\n\";\n#endif\n\n        // Clear named allocations\n        device_context.free();\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        if (operation->description().kind != kind_) {\n          std::cerr << \"    @ kind \" << operation->description().kind\n                    << \" != kind_ \" << kind_ << \"\\n\";\n        }\n        if (operation->description().provider != library::Provider::kCUTLASS) {\n          std::cerr << \"    @ provider \" << operation->description().provider\n                    << \" != library::Provider::kCUTLASS\\n\";\n        }\n        if (options.device.compute_capability(0) < min_cc) {\n          std::cerr << \"    @ compute_capability \"\n                    << options.device.compute_capability(0)\n                    << \" < min_cc \" << min_cc << \"\\n\";\n        }\n        if (options.device.compute_capability(0) > max_cc) {\n          std::cerr << \"    @ compute_capability \"\n                    << options.device.compute_capability(0)\n                    << \" > max_cc \" << max_cc << \"\\n\";\n        }\n#endif\n\n        // Execute compatible cutlass operations if they satisfy the current device's compute capability\n        if (operation->description().kind == kind_ &&\n            operation->description().provider == library::Provider::kCUTLASS &&\n            options.device.compute_capability(0) >= min_cc &&\n            options.device.compute_capability(0) <= max_cc) {\n\n          std::string operation_name(operation->description().name);\n          // Filter kernels by name\n          bool filtered_by_name = options.operation_names.empty();\n          if (!filtered_by_name) {\n\n            for (auto const & op_name : options.operation_names) {\n              if (find_string_matches_(op_name, operation_name)) {\n                filtered_by_name = true;\n                break;\n              }\n            }\n          }\n\n          for (auto const & op_name : options.excluded_operation_names) {\n            if (find_string_matches_(op_name, operation_name)) {\n              filtered_by_name = false;\n              break;\n            }\n          }\n\n          // Problems list uses exact match on operation names\n          if (do_testlist_run && !(all_operations_and_problems[i].first == operation_name)) {\n            filtered_by_name = false;\n          }\n\n          if (!filtered_by_name || !satisfies(operation->description(), problem_space, problem)) {\n            continue;\n          }\n\n          // we have found a kernel match, so increment the counter for match kernels\n          ++matched_operation_count;\n\n          // A. Initialize configuration\n          Status status = this->initialize_configuration(\n            options,\n            report,\n            device_context,\n            operation,\n            problem_space,\n            problem);\n\n          if (status == Status::kErrorInternal) {\n\n            // If there was an internal error, consume the CUDA error and move to the next operation.\n            (void)cudaGetLastError();\n\n            report.append_result(model_result_);\n            continue;\n          }\n          else if (status != Status::kSuccess) {\n            // If the workspace could not be initialized for any other reason, continue to\n            // the next operation.\n            continue;\n          }\n\n          if (continue_profiling) {\n\n            if (options.report.print_kernel_before_running) {\n              std::cout << \"Profiling kernel for JUnit test \" << options.report.junit_output_path << \": \"\n                        << operation_name << std::endl;\n            }\n\n            status = this->initialize_workspace(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            if (status == Status::kErrorInternal) {\n\n              // If there was an internal error, consume the CUDA error and move to the next operation.\n              (void)cudaGetLastError();\n\n              report.append_results(results_);\n              continue;\n            }\n            else if (status != Status::kSuccess) {\n              // If the workspace could not be initialized for any other reason, continue to\n              // the next operation.\n              continue;\n            }\n          }\n\n          //\n          // Profile CUTLASS if it is enabled\n          //\n\n          // B. Verify CUTLASS\n          if (continue_profiling && options.profiling.provider_enabled(library::Provider::kCUTLASS)) {\n\n            continue_profiling = this->verify_cutlass(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            retval |= (not continue_profiling);\n          }\n\n          if (options.execution_mode == ExecutionMode::kDryRun) {\n            report.append_results(results_);\n            results_.clear();\n            continue;\n          }\n\n          //\n          // C. Optionally save workspace\n          //\n\n          if (options.verification.save_workspace == SaveWorkspace::kAlways) {\n            save_workspace(\n              device_context,\n              options,\n              operation->description(),\n              library::Provider::kCUTLASS);\n          }\n\n          //\n          // D. Profile\n          //\n\n          if (continue_profiling && options.profiling.enabled) {\n\n            continue_profiling = this->profile(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            // Count op as profiled, even it failed to profile\n            profiled_operation_count++;\n          }\n\n          report.append_results(results_);\n          results_.clear();\n        } // if op satisfied compute capacity\n\n        if (!continue_profiling) {\n          // break out of `for op in manifest` loop and move to next problem\n          // `for each problem in problem space` conditional check on not continue profiling\n          break;\n        }\n      } // for op in manifest\n\n      // If we did not find any kernels that match our filters and error_on_no_match was set, report an error\n      if (options.profiling.error_on_no_match && matched_operation_count <= 0) {\n        #if !NDEBUG\n        std::cerr << \"Error: No matching kernels found with kernel selection filters [--error_on_no_match]\" << std::endl;\n        #endif\n        retval |= 1;\n        // Stop profiling on error no match\n        continue_profiling = false;\n      }\n\n      if (options.profiling.error_if_nothing_is_profiled && options.profiling.enabled && profiled_operation_count <= 0) {\n        #if !NDEBUG\n        std::cerr << \"Error: No kernels profiled found with kernel selection filters [--error_if_nothing_is_profiled]\" << std::endl;\n        #endif\n        retval |= 1;\n        // Stop profiling on error no match\n        continue_profiling = false;\n      }\n\n    } // for each problem in problem space\n  }\n\n  return retval;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Sleep for a given duration in ms\nvoid OperationProfiler::sleep(int sleep_duration) {\n  if (sleep_duration) {\n    #ifdef __unix__\n    usleep(sleep_duration * 1000);\n    #elif defined(_WIN32) || defined(WIN32)\n    SleepEx(sleep_duration, false);\n    #else\n    // sleep not supported\n    #endif\n  }\n}\n\n\n/// Compares tensors for equality\nDisposition OperationProfiler::compare_tensors(\n  Options const &options,\n  DeviceAllocation &experimental,\n  DeviceAllocation &reference,\n  int64_t count) {\n\n  if (experimental.type() != reference.type()) {\n    return Disposition::kIncorrect;\n  }\n\n  bool passed = false;\n\n  if (count == 0) {\n    count = reference.capacity();\n  }\n\n  if (options.verification.epsilon == 0) {\n\n    // bit-level equality\n    passed = DeviceAllocation::block_compare_equal(\n      experimental.type(),\n      experimental.data(),\n      reference.data(),\n      count);\n  }\n  else {\n\n    // relative error function\n    passed = DeviceAllocation::block_compare_relatively_equal(\n      experimental.type(),\n      experimental.data(),\n      reference.data(),\n      count,\n      options.verification.epsilon,\n      options.verification.nonzero_floor);\n  }\n\n  return passed ? Disposition::kPassed : Disposition::kIncorrect;\n}\n\n/// Saves the workspace\nvoid OperationProfiler::save_workspace(\n  DeviceContext &device_context,\n  Options const &options,\n  library::OperationDescription const &desc,\n  library::Provider provider,\n  library::Provider verification_provider) {\n\n  for (auto const & named_allocation : device_context) {\n\n    DeviceAllocation *allocation = named_allocation.second;\n\n    if (allocation->layout() == library::LayoutTypeID::kUnknown) {\n      continue; // write_tensor not set up to handle DeviceAllocations initialized using\n                // allocate_block()\n    }\n\n    std::stringstream filename;\n\n    filename << desc.name << \"_\" << library::to_string(provider) << \"_\";\n\n    if (verification_provider != library::Provider::kInvalid) {\n      filename << \"verified_by_\" << library::to_string(verification_provider) << \"_\";\n    }\n\n    filename << named_allocation.first + \".mat\";\n\n    std::ofstream out(filename.str());\n\n    allocation->write_tensor_csv(out);\n    out << \"\\n\";\n\n    if (options.report.verbose) {\n      std::cout << \"wrote '\" << filename.str() << \"'\" << std::endl;\n    }\n  }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace {\nextern \"C\" {\n__global__ void delay(cuda::atomic<bool> const *release) {\n  while (release->load(cuda::memory_order_acquire) != true) {\n#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100);\n#endif\n  }\n}\n}\n\nStatus predict_iters(\n  int &iterations,\n  Options const &options,\n  const std::function<Status(cudaStream_t, int)> &func,\n  cudaStream_t stream) {\n  // always use profiling-iterations if requested\n  if (options.profiling.iterations != 0) {\n    iterations = options.profiling.iterations;\n    return Status::kSuccess;\n  }\n\n  // otherwise run for as many iterations as necessary to\n  // meet profiling-duration\n  constexpr int CALIBRATION_ITERS = 5;\n  GpuTimer timer;\n  timer.start(stream);\n  for (int i = 0; i < CALIBRATION_ITERS; i++) {\n    Status status = func(stream, i);\n    if (status != Status::kSuccess) {\n      return status;\n    }\n  }\n  timer.stop_and_wait(stream);\n\n  double est_iters             = options.profiling.duration / std::max(timer.duration(CALIBRATION_ITERS), 1e-6);\n  constexpr uint64_t MAX_ITERS = 1'000'000;\n  iterations = std::min(static_cast<uint64_t>(std::ceil(est_iters)), static_cast<uint64_t>(MAX_ITERS));\n  iterations = std::max(options.profiling.min_iterations, iterations);\n  return Status::kSuccess;\n};\n\n} // namespace\n\n/// This profiling method is designed to run a kernel on several GPUs to\n/// measure interference (e.g. due to power throttling).\n/// To encourage the kernels to start at the same time and minimize jitter,\n/// a spinloop kernel blocks each stream while work is being enqueued, which is\n/// later triggered from the host.\n/// CUDA graphs allows you to record the launch of large numbers of kernels without\n/// blocking and therefore avoids a deadlock which happens if you try to enqueue too\n/// many kernels behind the spinloop kernel.\nStatus OperationProfiler::profile_kernel_w_cuda_graphs_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(int, cudaStream_t, int)> const& func,\n  std::vector<cudaStream_t> const& streams) {\n\n  auto dev_count = streams.size();\n\n  cuda::atomic<bool> *release;\n\n  if (dev_count > 1) {\n    CUDA_CHECK(cudaHostAlloc(&release, sizeof(*release), cudaHostAllocPortable));\n    release->store(false, cuda::memory_order_release);\n  }\n\n  std::vector<GpuTimer> timer;\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    timer.emplace_back();\n  }\n\n  std::vector<cudaGraph_t> graphs;\n  graphs.resize(dev_count);\n  std::vector<cudaGraphExec_t> graphExecs;\n  graphExecs.resize(dev_count);\n\n  sleep(options.profiling.sleep_duration);\n\n  // predict time by running on device 0\n  int iterations;\n  CUDA_CHECK(cudaSetDevice(0));\n  Status status = predict_iters(\n    iterations,\n    options,\n    [&](cudaStream_t stream, int iter) { return func(0, stream, iter); },\n    streams[0]);\n  if (status != Status::kSuccess) {\n    return status;\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(cudaStreamBeginCapture(streams[i], cudaStreamCaptureModeGlobal));\n    if (dev_count > 1) {\n      // Halt execution until all GPUs are ready to precede.\n      // It allows the CPU to trigger the GPUs all start at the same time.\n      delay<<<1, 1, 0, streams[i]>>>(release);\n    }\n    for (int iteration = 0; iteration < options.profiling.warmup_iterations; ++iteration) {\n      Status status = func(i, streams[i], iteration);\n      if (status != Status::kSuccess) {\n        return status;\n      }\n    }\n\n    timer[i].start(streams[i], cudaEventRecordExternal);\n\n    int iteration = 0;\n    for (; iteration < iterations; ++iteration) {\n      Status status = func(i, streams[i], iteration + options.profiling.warmup_iterations);\n      if (status != Status::kSuccess) {\n        return status;\n      }\n    }\n    timer[i].stop(streams[i], cudaEventRecordExternal);\n    CUDA_CHECK(cudaStreamEndCapture(streams[i], &graphs[i]));\n    CUDA_CHECK(cudaGraphInstantiate(&graphExecs[i], graphs[i], nullptr, nullptr, 0));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(cudaGraphLaunch(graphExecs[i], streams[i]));\n  }\n\n  if (dev_count > 1) {\n    // release the enqueued kernels\n    release->store(true, cuda::memory_order_release);\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(cudaStreamSynchronize(streams[i]));\n  }\n\n  result.runtime = 0;\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    result.runtime_vector[i] = timer[i].duration(iterations);\n    result.runtime += result.runtime_vector[i];\n  }\n  result.runtime /= static_cast<double>(dev_count);\n\n  if (dev_count > 1) {\n    CUDA_CHECK(cudaFreeHost(release));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(cudaGraphExecDestroy(graphExecs[i]));\n    CUDA_CHECK(cudaGraphDestroy(graphs[i]));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(cudaSetDevice(options.device.device_id(dev_count - i - 1)));\n    timer.pop_back();\n  }\n\n  return Status::kSuccess;\n}\n\nStatus OperationProfiler::profile_kernel_(\n  PerformanceResult &result,\n  Options const &options,\n  const std::function<Status(int, cudaStream_t, int)> &func,\n  const std::vector<cudaStream_t> &streams) {\n\n  if (options.profiling.use_cuda_graphs) {\n    return profile_kernel_w_cuda_graphs_(result, options, func, streams);\n  }\n  else if (streams.size() == 1) {\n    auto single_device_func = [&](cudaStream_t stream, int iteration) {\n      return func(0, stream, iteration);\n    };\n    return profile_kernel_no_cuda_graphs_(result, options, single_device_func, streams[0]);\n  }\n  return Status::kErrorNotSupported;\n}\n\n/// Method to profile GPU execution time of a kernel launched in func\nStatus OperationProfiler::profile_kernel_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(cudaStream_t, int)> const& func,\n  cudaStream_t stream) {\n\n  if (options.profiling.use_cuda_graphs) {\n    auto graph_func = [&](int dev_id, cudaStream_t stream, int iteration) {\n      return func(stream, iteration);\n    };\n    return profile_kernel_w_cuda_graphs_(result, options, graph_func, {stream});\n  } else {\n    return profile_kernel_no_cuda_graphs_(result, options, func, stream);\n  }\n  return Status::kSuccess;\n}\n\n/// Method to profile GPU execution time of a kernel launched in func\nStatus OperationProfiler::profile_kernel_no_cuda_graphs_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(cudaStream_t, int)> const& func,\n  cudaStream_t stream) {\n\n  GpuTimer timer;\n  // Optional sleep to limit power consumption and thermals\n  sleep(options.profiling.sleep_duration);\n\n  Status status = Status::kSuccess;\n\n  int iterations;\n  status = predict_iters(iterations, options, func, stream);\n  if (status != Status::kSuccess) {\n    return status;\n  }\n\n  for (int iteration = 0; iteration < options.profiling.warmup_iterations; ++iteration) {\n    status = func(stream, iteration);\n    if (status != Status::kSuccess) {\n      return status;\n    }\n  }\n\n  timer.start(stream);\n\n  int iteration = 0;\n  for (; iteration < iterations; ++iteration) {\n    status = func(stream, iteration + options.profiling.warmup_iterations);\n\n    if (status != Status::kSuccess) {\n      result.status = status;\n      return status;\n    }\n  }\n\n  timer.stop_and_wait(stream);\n\n  result.runtime = timer.duration(iteration);\n  result.status  = status;\n\n  return status;\n}\n\n/// Method to profile a CUTLASS Operation\nStatus OperationProfiler::profile_cutlass_(\n  PerformanceResult &result,\n  Options const &options,\n  library::Operation const *operation,\n  void *arguments,\n  void *host_workspace,\n  void *device_workspace) {\n\n  auto op = [=](cudaStream_t, int) { return operation->run(arguments, host_workspace, device_workspace); };\n  return profile_kernel_(result, options, op);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Sets operation description\nvoid OperationProfiler::initialize_result_(\n  PerformanceResult &result,\n  library::OperationDescription const &operation_desc,\n  ProblemSpace const &problem_space) {\n\n  set_argument(result, \"op_class\", problem_space,\n    library::to_string(operation_desc.tile_description.math_instruction.opcode_class));\n\n  set_argument(result, \"accum\", problem_space,\n    library::to_string(operation_desc.tile_description.math_instruction.element_accumulator));\n\n  set_argument(result, \"cta_m\", problem_space, operation_desc.tile_description.threadblock_shape.m());\n  set_argument(result, \"cta_n\", problem_space, operation_desc.tile_description.threadblock_shape.n());\n  set_argument(result, \"cta_k\", problem_space, operation_desc.tile_description.threadblock_shape.k());\n  set_argument(result, \"stages\", problem_space, operation_desc.tile_description.threadblock_stages);\n  set_argument(result, \"warps_m\", problem_space, operation_desc.tile_description.warp_count.m());\n  set_argument(result, \"warps_n\", problem_space, operation_desc.tile_description.warp_count.n());\n  set_argument(result, \"warps_k\", problem_space, operation_desc.tile_description.warp_count.k());\n  set_argument(result, \"inst_m\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.m());\n  set_argument(result, \"inst_n\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.n());\n  set_argument(result, \"inst_k\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.k());\n  set_argument(result, \"min_cc\", problem_space, operation_desc.tile_description.minimum_compute_capability);\n  set_argument(result, \"max_cc\", problem_space, operation_desc.tile_description.maximum_compute_capability);\n}\n\n/// Helper\nvoid OperationProfiler::set_argument(\n  PerformanceResult &result,\n  char const *name,\n  ProblemSpace const &problem_space,\n  std::string const &value) {\n\n  result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), value);\n}\n\nvoid OperationProfiler::set_argument(\n  PerformanceResult &result,\n  char const *name,\n  ProblemSpace const &problem_space,\n  int64_t value) {\n\n  result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), library::lexical_cast(value));\n}\n\n\n/// finds string matches filter_string in operation_name\nbool OperationProfiler::find_string_matches_(\n  std::string const &filter_string,\n  std::string const &operation_name) {\n  // Returns true if all substrings appear in the operation_name in order\n\n  // Split filter_string of the format \"gemm*f32*nt\" to tokens [\"gemm\", \"f32\", \"nt\"]\n  std::string item;\n  std::istringstream iss(filter_string);\n  std::vector<std::string> filter_tokens;\n  while (std::getline(iss, item, '*')) {\n    filter_tokens.push_back(item);\n  }\n\n  // Search filter_tokens in operation_name in order\n  size_t start = 0, idx = 0;\n  for (auto & token : filter_tokens) {\n    // Check if characters left to be parsed in operation_name\n    if (start < operation_name.length()) {\n      // Find token in operation_name[start:]\n      idx = operation_name.substr(start).find(token);\n      if (idx == std::string::npos) {\n        return false;\n      }\n    }\n    start += (idx + token.length());\n  }\n\n  // All tokens in filter_string found in operation_name\n  return true;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace profiler\n} // namespace cutlass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/* \\file\n   \\brief Defines a math function\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <algorithm>\n#include <stdexcept>\n#include <iomanip>\n#include <cstring>\n#include <fstream>\n#include <sstream>\n\n#ifdef __unix__\n#include <unistd.h>\n#elif defined(_WIN32) || defined(WIN32)\n#include <windows.h>\n#else\n// sleep not supported\n#endif\n\n#include <cuda/atomic>\n\n#include \"cutlass/profiler/options.h\"\n#include \"cutlass/profiler/operation_profiler.h\"\n#include \"cutlass/profiler/gpu_timer.h\"\n\n#include \"cutlass/trace.h\"\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace cutlass {\nnamespace profiler {\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nOperationProfiler::OperationProfiler(): kind_(library::OperationKind::kInvalid) { }\n\n/// Ctor\nOperationProfiler::OperationProfiler(\n  Options const &options,\n  library::OperationKind kind,\n  ArgumentDescriptionVector const &arguments,\n  ProviderVector const & verification_providers\n):\n  kind_(kind), arguments_(arguments) {\n\n  ArgumentDescriptionVector tile_description_arguments{\n    {ArgumentTypeID::kEnumerated, {\"op_class\", \"opcode-class\"}, \"Class of math instruction (simt, tensorop, wmmatensorop, wmma)\"},\n    {ArgumentTypeID::kEnumerated, {\"accum\", \"accumulator-type\"}, \"Math instruction accumulator data type\"},\n    {ArgumentTypeID::kInteger, {\"cta_m\", \"threadblock-shape::m\"}, \"Threadblock shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cta_n\", \"threadblock-shape::n\"}, \"Threadblock shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cta_k\", \"threadblock-shape::k\"}, \"Threadblock shape in the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_m\", \"cluster-shape::m\"}, \"Cluster shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_n\", \"cluster-shape::n\"}, \"Cluster shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_k\", \"cluster-shape::k\"}, \"Cluster shape in the K dimension\"},\n    \n    {ArgumentTypeID::kInteger, {\"cluster_m_fallback\", \"cluster-shape-fallback::m\"}, \"Fallback Cluster shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_n_fallback\", \"cluster-shape-fallback::n\"}, \"Fallback Cluster shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"cluster_k_fallback\", \"cluster-shape-fallback::k\"}, \"Fallback Cluster shape in the K dimension\"},\n    \n    {ArgumentTypeID::kInteger, {\"stages\", \"threadblock-stages\"}, \"Number of stages of threadblock-scoped matrix multiply\"},\n    {ArgumentTypeID::kInteger, {\"warps_m\", \"warp-count::m\"}, \"Number of warps within threadblock along the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"warps_n\", \"warp-count::n\"}, \"Number of warps within threadblock along the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"warps_k\", \"warp-count::k\"}, \"Number of warps within threadblock along the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_m\", \"instruction-shape::m\"}, \"Math instruction shape in the M dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_n\", \"instruction-shape::n\"}, \"Math instruction shape in the N dimension\"},\n    {ArgumentTypeID::kInteger, {\"inst_k\", \"instruction-shape::k\"}, \"Math instruction shape in the K dimension\"},\n    {ArgumentTypeID::kInteger, {\"min_cc\", \"minimum-compute-capability\"}, \"Minimum device compute capability\"},\n    {ArgumentTypeID::kInteger, {\"max_cc\", \"maximum-compute-capability\"}, \"Maximum device compute capability\"}\n  };\n\n  arguments_.insert(arguments_.end(), tile_description_arguments.begin(), tile_description_arguments.end());\n\n  for (auto provider : verification_providers) {\n    if (std::find(\n      options.verification.providers.begin(),\n      options.verification.providers.end(),\n      provider) != options.verification.providers.end()) {\n\n      verification_providers_.push_back(provider);\n    }\n  }\n\n}\n\n/// Destructor\nOperationProfiler::~OperationProfiler() {}\n\n/// Gets the schema description\nstd::string const & OperationProfiler::description() const {\n  return description_;\n}\n\n/// Prints usage statement for the math function\nvoid OperationProfiler::print_usage(std::ostream &out) const {\n  for (auto const & desc : arguments_) {\n\n    size_t const kAliasStart = 10;\n\n    size_t columns = 0;\n\n    std::string type_str = to_string(desc.type);\n    columns += type_str.size();\n\n    out << \"  [\" << type_str << \"]\";\n\n    if (columns < kAliasStart) {\n      out << std::string(kAliasStart - columns, ' ');\n    }\n\n    columns = 0;\n\n    int j = 0;\n    for (auto const & alias : desc.aliases) {\n      columns += alias.size() + (j ? 1 : 0) + 2;\n\n      out << (j++ ? \",\" : \"\") << \"--\" << alias;\n    }\n\n    size_t const kTotalColumns = 50;\n\n    if (columns < kTotalColumns) {\n      out << std::string(kTotalColumns - columns, ' ');\n    }\n\n    out << desc.description << \"\\n\";\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Returns true if the current operation description satisfies the problem space\nbool OperationProfiler::satisfies(\n  library::OperationDescription const &op_desc,\n  ProblemSpace const &problem_space,\n  ProblemSpace::Problem const &problem) {\n\n  library::OpcodeClassID opcode_class;\n  if (arg_as_OpcodeClassID(opcode_class, \"op_class\", problem_space, problem)) {\n    if (opcode_class != op_desc.tile_description.math_instruction.opcode_class) {\n      return false;\n    }\n  }\n  \n  bool dynamic_cluster = int64_t(op_desc.tile_description.cluster_shape.m()) == 0 ||\n                         int64_t(op_desc.tile_description.cluster_shape.n()) == 0 ||\n                         int64_t(op_desc.tile_description.cluster_shape.k()) == 0;\n  \n  int64_t int_value;\n\n  if (arg_as_int(int_value, \"inst_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"inst_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"inst_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.math_instruction.instruction_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cta_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  if (!dynamic_cluster) { \n  if (arg_as_int(int_value, \"cluster_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cluster_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"cluster_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.cluster_shape.k()) != int_value) {\n      return false;\n    }\n  }\n\n  } \n  if (arg_as_int(int_value, \"stages\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.threadblock_stages) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_m\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.m()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_n\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.n()) != int_value) {\n      return false;\n    }\n  }\n\n  if (arg_as_int(int_value, \"warps_k\", problem_space, problem)) {\n    if (int64_t(op_desc.tile_description.warp_count.k()) != int_value) {\n      return false;\n    }\n  }\n\n  library::NumericTypeID numeric_type;\n  if (arg_as_NumericTypeID(numeric_type, \"accum\", problem_space, problem)) {\n    if (numeric_type != op_desc.tile_description.math_instruction.element_accumulator) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n\nstd::ostream& operator<<(std::ostream& out, library::Provider provider) {\n  if (provider == library::Provider::kNone) {\n    out << \"kNone\";\n  }\n  else if (provider == library::Provider::kCUTLASS) {\n    out << \"kCUTLASS\";\n  }\n  else if (provider == library::Provider::kReferenceHost) {\n    out << \"kReferenceHost\";\n  }\n  else if (provider == library::Provider::kReferenceDevice) {\n    out << \"kReferenceDevice\";\n  }\n  else if (provider == library::Provider::kCUBLAS) {\n    out << \"kCUBLAS\";\n  }\n  else if (provider == library::Provider::kCUDNN) {\n    out << \"kCUDNN\";\n  }\n  else {\n    out << \"kInvalid\";\n  }\n\n  return out;\n}\n\nstd::ostream& operator<<(std::ostream& out, library::OperationKind op_kind) {\n  if (op_kind == library::OperationKind::kGemm) {\n    out << \"kGemm\";\n  }\n  else if (op_kind == library::OperationKind::kBlockScaledGemm) {\n    out << \"kBlockScaledGemm\";\n  }\n  else if (op_kind == library::OperationKind::kBlockwiseGemm) {\n    out << \"kBlockwiseGemm\";\n  }\n  else if (op_kind == library::OperationKind::kRankK) {\n    out << \"kRankK\";\n  }\n  else if (op_kind == library::OperationKind::kRank2K) {\n    out << \"kRank2K\";\n  }\n  else if (op_kind == library::OperationKind::kTrmm) {\n    out << \"kTrmm\";\n  }\n  else if (op_kind == library::OperationKind::kSymm) {\n    out << \"kSymm\";\n  }\n  else if (op_kind == library::OperationKind::kConv2d) {\n    out << \"kConv2d\";\n  }\n  else if (op_kind == library::OperationKind::kConv3d) {\n    out << \"kConv3d\";\n  }\n  else if (op_kind == library::OperationKind::kEqGemm) {\n    out << \"kEqGemm\";\n  }\n  else if (op_kind == library::OperationKind::kSparseGemm) {\n    out << \"kSparseGemm\";\n  }\n  else if (op_kind == library::OperationKind::kReduction) {\n    out << \"kReduction\";\n  }\n  else if (op_kind == library::OperationKind::kGroupedGemm) {\n    out << \"kGroupedGemm\";\n  }\n  else {\n    out << \"kInvalid\";\n  }\n\n  return out;\n}\n\n#endif // defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n\n/// Entry point to profile all operations in the manifest\nint OperationProfiler::profile_all(\n  Options const &options,\n  library::Manifest const &manifest,\n  DeviceContext &device_context) {\n  ProblemSpace cmdline_problem_space(arguments_, options.cmdline);\n\n  bool do_testlist_run = !options.operation_problems.empty();\n\n  std::vector<std::pair<std::string, std::unique_ptr<ProblemSpace>>> all_operations_and_problems;\n  if (do_testlist_run) {\n    for (const auto& [operation_name, cmd_vec] : options.operation_problems)  {\n      for (auto& cmd_line : cmd_vec) {\n        all_operations_and_problems.push_back({operation_name, std::make_unique<ProblemSpace>(arguments_, cmd_line)});\n      }\n    }\n  }\n\n  // 1. Construct performance report\n  PerformanceReport report(options, cmdline_problem_space.argument_names(), kind_);\n\n  //\n  int retval = 0;\n\n  size_t bound = (all_operations_and_problems.empty() ? 1 : all_operations_and_problems.size());\n  for (size_t i = 0; i < bound; i++) {\n\n    // New problem space for each operation if we are running a testlist\n    ProblemSpace& problem_space = do_testlist_run ? *all_operations_and_problems[i].second : cmdline_problem_space;\n\n    // 2. For each problem in problem space\n    ProblemSpace::Iterator problem_it = problem_space.begin();\n    ProblemSpace::Iterator problem_end = problem_space.end();\n\n    bool continue_profiling = true;\n\n    // For each problem in problem space\n    for (; continue_profiling && problem_it != problem_end; ++problem_it) {\n      ProblemSpace::Problem problem = problem_it.at();\n      report.next_problem();\n\n      // For each operation in manifest\n      int matched_operation_count = 0;\n      int profiled_operation_count = 0;\n      for (auto const& operation_ptr : manifest) {\n\n        library::Operation const *operation = operation_ptr.get();\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        std::cerr << \"  Operation: \" << typeid(*operation).name() << \"\\n\"\n                  << \"    name: \" << operation->description().name << \"\\n\"\n                  << \"    kind: \" << operation->description().kind << \"\\n\"\n                  << \"    provider: \" << operation->description().provider << \"\\n\";\n#endif // CUTLASS_DEBUG_TRACE_LEVEL\n\n        auto min_cc = operation->description().tile_description.minimum_compute_capability;\n        auto max_cc = operation->description().tile_description.maximum_compute_capability;\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        std::cerr << \"    min_cc: \" << min_cc << \"\\n\";\n        std::cerr << \"    max_cc: \" << min_cc << \"\\n\";\n#endif\n\n        // Clear named allocations\n        device_context.free();\n\n#if defined(CUTLASS_DEBUG_TRACE_LEVEL) && (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n        if (operation->description().kind != kind_) {\n          std::cerr << \"    @ kind \" << operation->description().kind\n                    << \" != kind_ \" << kind_ << \"\\n\";\n        }\n        if (operation->description().provider != library::Provider::kCUTLASS) {\n          std::cerr << \"    @ provider \" << operation->description().provider\n                    << \" != library::Provider::kCUTLASS\\n\";\n        }\n        if (options.device.compute_capability(0) < min_cc) {\n          std::cerr << \"    @ compute_capability \"\n                    << options.device.compute_capability(0)\n                    << \" < min_cc \" << min_cc << \"\\n\";\n        }\n        if (options.device.compute_capability(0) > max_cc) {\n          std::cerr << \"    @ compute_capability \"\n                    << options.device.compute_capability(0)\n                    << \" > max_cc \" << max_cc << \"\\n\";\n        }\n#endif\n\n        // Execute compatible cutlass operations if they satisfy the current device's compute capability\n        if (operation->description().kind == kind_ &&\n            operation->description().provider == library::Provider::kCUTLASS &&\n            options.device.compute_capability(0) >= min_cc &&\n            options.device.compute_capability(0) <= max_cc) {\n\n          std::string operation_name(operation->description().name);\n          // Filter kernels by name\n          bool filtered_by_name = options.operation_names.empty();\n          if (!filtered_by_name) {\n\n            for (auto const & op_name : options.operation_names) {\n              if (find_string_matches_(op_name, operation_name)) {\n                filtered_by_name = true;\n                break;\n              }\n            }\n          }\n\n          for (auto const & op_name : options.excluded_operation_names) {\n            if (find_string_matches_(op_name, operation_name)) {\n              filtered_by_name = false;\n              break;\n            }\n          }\n\n          // Problems list uses exact match on operation names\n          if (do_testlist_run && !(all_operations_and_problems[i].first == operation_name)) {\n            filtered_by_name = false;\n          }\n\n          if (!filtered_by_name || !satisfies(operation->description(), problem_space, problem)) {\n            continue;\n          }\n\n          // we have found a kernel match, so increment the counter for match kernels\n          ++matched_operation_count;\n\n          // A. Initialize configuration\n          Status status = this->initialize_configuration(\n            options,\n            report,\n            device_context,\n            operation,\n            problem_space,\n            problem);\n\n          if (status == Status::kErrorInternal) {\n\n            // If there was an internal error, consume the CUDA error and move to the next operation.\n            (void)hipGetLastError();\n\n            report.append_result(model_result_);\n            continue;\n          }\n          else if (status != Status::kSuccess) {\n            // If the workspace could not be initialized for any other reason, continue to\n            // the next operation.\n            continue;\n          }\n\n          if (continue_profiling) {\n\n            if (options.report.print_kernel_before_running) {\n              std::cout << \"Profiling kernel for JUnit test \" << options.report.junit_output_path << \": \"\n                        << operation_name << std::endl;\n            }\n\n            status = this->initialize_workspace(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            if (status == Status::kErrorInternal) {\n\n              // If there was an internal error, consume the CUDA error and move to the next operation.\n              (void)hipGetLastError();\n\n              report.append_results(results_);\n              continue;\n            }\n            else if (status != Status::kSuccess) {\n              // If the workspace could not be initialized for any other reason, continue to\n              // the next operation.\n              continue;\n            }\n          }\n\n          //\n          // Profile CUTLASS if it is enabled\n          //\n\n          // B. Verify CUTLASS\n          if (continue_profiling && options.profiling.provider_enabled(library::Provider::kCUTLASS)) {\n\n            continue_profiling = this->verify_cutlass(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            retval |= (not continue_profiling);\n          }\n\n          if (options.execution_mode == ExecutionMode::kDryRun) {\n            report.append_results(results_);\n            results_.clear();\n            continue;\n          }\n\n          //\n          // C. Optionally save workspace\n          //\n\n          if (options.verification.save_workspace == SaveWorkspace::kAlways) {\n            save_workspace(\n              device_context,\n              options,\n              operation->description(),\n              library::Provider::kCUTLASS);\n          }\n\n          //\n          // D. Profile\n          //\n\n          if (continue_profiling && options.profiling.enabled) {\n\n            continue_profiling = this->profile(\n              options,\n              report,\n              device_context,\n              operation,\n              problem_space,\n              problem);\n\n            // Count op as profiled, even it failed to profile\n            profiled_operation_count++;\n          }\n\n          report.append_results(results_);\n          results_.clear();\n        } // if op satisfied compute capacity\n\n        if (!continue_profiling) {\n          // break out of `for op in manifest` loop and move to next problem\n          // `for each problem in problem space` conditional check on not continue profiling\n          break;\n        }\n      } // for op in manifest\n\n      // If we did not find any kernels that match our filters and error_on_no_match was set, report an error\n      if (options.profiling.error_on_no_match && matched_operation_count <= 0) {\n        #if !NDEBUG\n        std::cerr << \"Error: No matching kernels found with kernel selection filters [--error_on_no_match]\" << std::endl;\n        #endif\n        retval |= 1;\n        // Stop profiling on error no match\n        continue_profiling = false;\n      }\n\n      if (options.profiling.error_if_nothing_is_profiled && options.profiling.enabled && profiled_operation_count <= 0) {\n        #if !NDEBUG\n        std::cerr << \"Error: No kernels profiled found with kernel selection filters [--error_if_nothing_is_profiled]\" << std::endl;\n        #endif\n        retval |= 1;\n        // Stop profiling on error no match\n        continue_profiling = false;\n      }\n\n    } // for each problem in problem space\n  }\n\n  return retval;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Sleep for a given duration in ms\nvoid OperationProfiler::sleep(int sleep_duration) {\n  if (sleep_duration) {\n    #ifdef __unix__\n    usleep(sleep_duration * 1000);\n    #elif defined(_WIN32) || defined(WIN32)\n    SleepEx(sleep_duration, false);\n    #else\n    // sleep not supported\n    #endif\n  }\n}\n\n\n/// Compares tensors for equality\nDisposition OperationProfiler::compare_tensors(\n  Options const &options,\n  DeviceAllocation &experimental,\n  DeviceAllocation &reference,\n  int64_t count) {\n\n  if (experimental.type() != reference.type()) {\n    return Disposition::kIncorrect;\n  }\n\n  bool passed = false;\n\n  if (count == 0) {\n    count = reference.capacity();\n  }\n\n  if (options.verification.epsilon == 0) {\n\n    // bit-level equality\n    passed = DeviceAllocation::block_compare_equal(\n      experimental.type(),\n      experimental.data(),\n      reference.data(),\n      count);\n  }\n  else {\n\n    // relative error function\n    passed = DeviceAllocation::block_compare_relatively_equal(\n      experimental.type(),\n      experimental.data(),\n      reference.data(),\n      count,\n      options.verification.epsilon,\n      options.verification.nonzero_floor);\n  }\n\n  return passed ? Disposition::kPassed : Disposition::kIncorrect;\n}\n\n/// Saves the workspace\nvoid OperationProfiler::save_workspace(\n  DeviceContext &device_context,\n  Options const &options,\n  library::OperationDescription const &desc,\n  library::Provider provider,\n  library::Provider verification_provider) {\n\n  for (auto const & named_allocation : device_context) {\n\n    DeviceAllocation *allocation = named_allocation.second;\n\n    if (allocation->layout() == library::LayoutTypeID::kUnknown) {\n      continue; // write_tensor not set up to handle DeviceAllocations initialized using\n                // allocate_block()\n    }\n\n    std::stringstream filename;\n\n    filename << desc.name << \"_\" << library::to_string(provider) << \"_\";\n\n    if (verification_provider != library::Provider::kInvalid) {\n      filename << \"verified_by_\" << library::to_string(verification_provider) << \"_\";\n    }\n\n    filename << named_allocation.first + \".mat\";\n\n    std::ofstream out(filename.str());\n\n    allocation->write_tensor_csv(out);\n    out << \"\\n\";\n\n    if (options.report.verbose) {\n      std::cout << \"wrote '\" << filename.str() << \"'\" << std::endl;\n    }\n  }\n}\n\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace {\nextern \"C\" {\n__global__ void delay(cuda::atomic<bool> const *release) {\n  while (release->load(cuda::memory_order_acquire) != true) {\n#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100);\n#endif\n  }\n}\n}\n\nStatus predict_iters(\n  int &iterations,\n  Options const &options,\n  const std::function<Status(hipStream_t, int)> &func,\n  hipStream_t stream) {\n  // always use profiling-iterations if requested\n  if (options.profiling.iterations != 0) {\n    iterations = options.profiling.iterations;\n    return Status::kSuccess;\n  }\n\n  // otherwise run for as many iterations as necessary to\n  // meet profiling-duration\n  constexpr int CALIBRATION_ITERS = 5;\n  GpuTimer timer;\n  timer.start(stream);\n  for (int i = 0; i < CALIBRATION_ITERS; i++) {\n    Status status = func(stream, i);\n    if (status != Status::kSuccess) {\n      return status;\n    }\n  }\n  timer.stop_and_wait(stream);\n\n  double est_iters             = options.profiling.duration / std::max(timer.duration(CALIBRATION_ITERS), 1e-6);\n  constexpr uint64_t MAX_ITERS = 1'000'000;\n  iterations = std::min(static_cast<uint64_t>(std::ceil(est_iters)), static_cast<uint64_t>(MAX_ITERS));\n  iterations = std::max(options.profiling.min_iterations, iterations);\n  return Status::kSuccess;\n};\n\n} // namespace\n\n/// This profiling method is designed to run a kernel on several GPUs to\n/// measure interference (e.g. due to power throttling).\n/// To encourage the kernels to start at the same time and minimize jitter,\n/// a spinloop kernel blocks each stream while work is being enqueued, which is\n/// later triggered from the host.\n/// CUDA graphs allows you to record the launch of large numbers of kernels without\n/// blocking and therefore avoids a deadlock which happens if you try to enqueue too\n/// many kernels behind the spinloop kernel.\nStatus OperationProfiler::profile_kernel_w_cuda_graphs_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(int, hipStream_t, int)> const& func,\n  std::vector<hipStream_t> const& streams) {\n\n  auto dev_count = streams.size();\n\n  cuda::atomic<bool> *release;\n\n  if (dev_count > 1) {\n    CUDA_CHECK(hipHostAlloc(&release, sizeof(*release), hipHostMallocPortable));\n    release->store(false, cuda::memory_order_release);\n  }\n\n  std::vector<GpuTimer> timer;\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    timer.emplace_back();\n  }\n\n  std::vector<hipGraph_t> graphs;\n  graphs.resize(dev_count);\n  std::vector<hipGraphExec_t> graphExecs;\n  graphExecs.resize(dev_count);\n\n  sleep(options.profiling.sleep_duration);\n\n  // predict time by running on device 0\n  int iterations;\n  CUDA_CHECK(hipSetDevice(0));\n  Status status = predict_iters(\n    iterations,\n    options,\n    [&](hipStream_t stream, int iter) { return func(0, stream, iter); },\n    streams[0]);\n  if (status != Status::kSuccess) {\n    return status;\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(hipStreamBeginCapture(streams[i], hipStreamCaptureModeGlobal));\n    if (dev_count > 1) {\n      // Halt execution until all GPUs are ready to precede.\n      // It allows the CPU to trigger the GPUs all start at the same time.\n      delay<<<1, 1, 0, streams[i]>>>(release);\n    }\n    for (int iteration = 0; iteration < options.profiling.warmup_iterations; ++iteration) {\n      Status status = func(i, streams[i], iteration);\n      if (status != Status::kSuccess) {\n        return status;\n      }\n    }\n\n    timer[i].start(streams[i], hipEventRecordExternal);\n\n    int iteration = 0;\n    for (; iteration < iterations; ++iteration) {\n      Status status = func(i, streams[i], iteration + options.profiling.warmup_iterations);\n      if (status != Status::kSuccess) {\n        return status;\n      }\n    }\n    timer[i].stop(streams[i], hipEventRecordExternal);\n    CUDA_CHECK(hipStreamEndCapture(streams[i], &graphs[i]));\n    CUDA_CHECK(hipGraphInstantiate(&graphExecs[i], graphs[i], nullptr, nullptr, 0));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(hipGraphLaunch(graphExecs[i], streams[i]));\n  }\n\n  if (dev_count > 1) {\n    // release the enqueued kernels\n    release->store(true, cuda::memory_order_release);\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(hipStreamSynchronize(streams[i]));\n  }\n\n  result.runtime = 0;\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    result.runtime_vector[i] = timer[i].duration(iterations);\n    result.runtime += result.runtime_vector[i];\n  }\n  result.runtime /= static_cast<double>(dev_count);\n\n  if (dev_count > 1) {\n    CUDA_CHECK(hipHostFree(release));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(i)));\n    CUDA_CHECK(hipGraphExecDestroy(graphExecs[i]));\n    CUDA_CHECK(hipGraphDestroy(graphs[i]));\n  }\n\n  for (size_t i = 0; i < dev_count; ++i) {\n    CUDA_CHECK(hipSetDevice(options.device.device_id(dev_count - i - 1)));\n    timer.pop_back();\n  }\n\n  return Status::kSuccess;\n}\n\nStatus OperationProfiler::profile_kernel_(\n  PerformanceResult &result,\n  Options const &options,\n  const std::function<Status(int, hipStream_t, int)> &func,\n  const std::vector<hipStream_t> &streams) {\n\n  if (options.profiling.use_cuda_graphs) {\n    return profile_kernel_w_cuda_graphs_(result, options, func, streams);\n  }\n  else if (streams.size() == 1) {\n    auto single_device_func = [&](hipStream_t stream, int iteration) {\n      return func(0, stream, iteration);\n    };\n    return profile_kernel_no_cuda_graphs_(result, options, single_device_func, streams[0]);\n  }\n  return Status::kErrorNotSupported;\n}\n\n/// Method to profile GPU execution time of a kernel launched in func\nStatus OperationProfiler::profile_kernel_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(hipStream_t, int)> const& func,\n  hipStream_t stream) {\n\n  if (options.profiling.use_cuda_graphs) {\n    auto graph_func = [&](int dev_id, hipStream_t stream, int iteration) {\n      return func(stream, iteration);\n    };\n    return profile_kernel_w_cuda_graphs_(result, options, graph_func, {stream});\n  } else {\n    return profile_kernel_no_cuda_graphs_(result, options, func, stream);\n  }\n  return Status::kSuccess;\n}\n\n/// Method to profile GPU execution time of a kernel launched in func\nStatus OperationProfiler::profile_kernel_no_cuda_graphs_(\n  PerformanceResult& result,\n  Options const& options,\n  std::function<Status(hipStream_t, int)> const& func,\n  hipStream_t stream) {\n\n  GpuTimer timer;\n  // Optional sleep to limit power consumption and thermals\n  sleep(options.profiling.sleep_duration);\n\n  Status status = Status::kSuccess;\n\n  int iterations;\n  status = predict_iters(iterations, options, func, stream);\n  if (status != Status::kSuccess) {\n    return status;\n  }\n\n  for (int iteration = 0; iteration < options.profiling.warmup_iterations; ++iteration) {\n    status = func(stream, iteration);\n    if (status != Status::kSuccess) {\n      return status;\n    }\n  }\n\n  timer.start(stream);\n\n  int iteration = 0;\n  for (; iteration < iterations; ++iteration) {\n    status = func(stream, iteration + options.profiling.warmup_iterations);\n\n    if (status != Status::kSuccess) {\n      result.status = status;\n      return status;\n    }\n  }\n\n  timer.stop_and_wait(stream);\n\n  result.runtime = timer.duration(iteration);\n  result.status  = status;\n\n  return status;\n}\n\n/// Method to profile a CUTLASS Operation\nStatus OperationProfiler::profile_cutlass_(\n  PerformanceResult &result,\n  Options const &options,\n  library::Operation const *operation,\n  void *arguments,\n  void *host_workspace,\n  void *device_workspace) {\n\n  auto op = [=](hipStream_t, int) { return operation->run(arguments, host_workspace, device_workspace); };\n  return profile_kernel_(result, options, op);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Sets operation description\nvoid OperationProfiler::initialize_result_(\n  PerformanceResult &result,\n  library::OperationDescription const &operation_desc,\n  ProblemSpace const &problem_space) {\n\n  set_argument(result, \"op_class\", problem_space,\n    library::to_string(operation_desc.tile_description.math_instruction.opcode_class));\n\n  set_argument(result, \"accum\", problem_space,\n    library::to_string(operation_desc.tile_description.math_instruction.element_accumulator));\n\n  set_argument(result, \"cta_m\", problem_space, operation_desc.tile_description.threadblock_shape.m());\n  set_argument(result, \"cta_n\", problem_space, operation_desc.tile_description.threadblock_shape.n());\n  set_argument(result, \"cta_k\", problem_space, operation_desc.tile_description.threadblock_shape.k());\n  set_argument(result, \"stages\", problem_space, operation_desc.tile_description.threadblock_stages);\n  set_argument(result, \"warps_m\", problem_space, operation_desc.tile_description.warp_count.m());\n  set_argument(result, \"warps_n\", problem_space, operation_desc.tile_description.warp_count.n());\n  set_argument(result, \"warps_k\", problem_space, operation_desc.tile_description.warp_count.k());\n  set_argument(result, \"inst_m\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.m());\n  set_argument(result, \"inst_n\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.n());\n  set_argument(result, \"inst_k\", problem_space, operation_desc.tile_description.math_instruction.instruction_shape.k());\n  set_argument(result, \"min_cc\", problem_space, operation_desc.tile_description.minimum_compute_capability);\n  set_argument(result, \"max_cc\", problem_space, operation_desc.tile_description.maximum_compute_capability);\n}\n\n/// Helper\nvoid OperationProfiler::set_argument(\n  PerformanceResult &result,\n  char const *name,\n  ProblemSpace const &problem_space,\n  std::string const &value) {\n\n  result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), value);\n}\n\nvoid OperationProfiler::set_argument(\n  PerformanceResult &result,\n  char const *name,\n  ProblemSpace const &problem_space,\n  int64_t value) {\n\n  result.arguments.at(problem_space.argument_index(name)) = make_pair(std::string(name), library::lexical_cast(value));\n}\n\n\n/// finds string matches filter_string in operation_name\nbool OperationProfiler::find_string_matches_(\n  std::string const &filter_string,\n  std::string const &operation_name) {\n  // Returns true if all substrings appear in the operation_name in order\n\n  // Split filter_string of the format \"gemm*f32*nt\" to tokens [\"gemm\", \"f32\", \"nt\"]\n  std::string item;\n  std::istringstream iss(filter_string);\n  std::vector<std::string> filter_tokens;\n  while (std::getline(iss, item, '*')) {\n    filter_tokens.push_back(item);\n  }\n\n  // Search filter_tokens in operation_name in order\n  size_t start = 0, idx = 0;\n  for (auto & token : filter_tokens) {\n    // Check if characters left to be parsed in operation_name\n    if (start < operation_name.length()) {\n      // Find token in operation_name[start:]\n      idx = operation_name.substr(start).find(token);\n      if (idx == std::string::npos) {\n        return false;\n      }\n    }\n    start += (idx + token.length());\n  }\n\n  // All tokens in filter_string found in operation_name\n  return true;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace profiler\n} // namespace cutlass\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/tools/profiler/src/operation_profiler.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Basic CUDA file for testing compiler flags.\n*/\n\n__device__ int inner()\n{\n  return -1;\n}\n\n__global__ void test()\n{\n  inner();\n}\n\nint main()\n{\n  test<<<1,1>>>();\n  return 0;\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Basic CUDA file for testing compiler flags.\n*/\n\n#include \"hip/hip_runtime.h\"\n__device__ int inner()\n{\n  return -1;\n}\n\n__global__ void test()\n{\n  inner();\n}\n\nint main()\n{\n  test<<<1,1>>>();\n  return 0;\n}\n", "source": "/kaggle/working/cutlass/cmake/nop.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief \n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/layout/pitch_linear.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/regular_tile_iterator_tensor_op.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/// \ntemplate <typename Iterator>\n__global__ void kernel_gemm_threadblock_tensor_op_multiplicand_store(\n  typename Iterator::TensorRef ref_output,\n  typename Iterator::Element *input) {\n\n  // Construct fragment\n  typename Iterator::Fragment frag;\n\n  frag.clear();\n\n  // each thread loads a fragment\n  using AccessType = cutlass::Array<typename Iterator::Element, Iterator::ThreadMap::kElementsPerAccess>;\n\n  int const kElementsPerAccess = Iterator::ThreadMap::kElementsPerAccess;\n  int stride = Iterator::Shape::kContiguous;\n\n  int warp_id = (threadIdx.x / 32);\n  int lane_id = (threadIdx.x % 32);\n\n  input += (lane_id % 8) * kElementsPerAccess + (lane_id / 8) * stride;\n\n  input += (warp_id * Iterator::Shape::kStrided / Iterator::ThreadMap::Detail::kWarpCount) * stride;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int s = 0; s < Iterator::ThreadMap::Iterations::kStrided; ++s) {\n    CUTLASS_PRAGMA_UNROLL\n    for (int c = 0; c < Iterator::ThreadMap::Iterations::kContiguous; ++c) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int v = 0; v < Iterator::ThreadMap::kElementsPerAccess; ++v) {\n        frag[v + Iterator::ThreadMap::kElementsPerAccess * (c + s * Iterator::ThreadMap::Iterations::kContiguous)] = \n          input[v + c * 64 + s * Iterator::ThreadMap::Delta::kStrided * stride];\n      }\n    }\n  }\n\n  // Use iterator to store results\n  Iterator iter(ref_output, threadIdx.x);\n  iter.store(frag);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Simple test environment\ntemplate <\n  typename Shape_,\n  int WarpCount\n>\nclass MultiplicandTileIteratorTestbed {\npublic:\n\n  //\n  // Define iterator\n  //\n\n  using Shape = Shape_;\n  using Element = cutlass::half_t;\n  using Layout = cutlass::layout::TensorOpMultiplicandCongruous<\n      cutlass::sizeof_bits<Element>::value, 64>;\n  static int const kAdvanceRank = 1;\n  static int const kThreads = 32 * WarpCount;\n\n  using ThreadMap = cutlass::transform::PitchLinearWarpRakedThreadMap<\n    Shape,\n    kThreads,\n    cutlass::layout::PitchLinearShape<8, 4>,\n    128 / cutlass::sizeof_bits<Element>::value\n  >;\n\n  using Iterator = cutlass::transform::threadblock::RegularTileIterator<\n    Shape, Element, Layout, kAdvanceRank, ThreadMap\n  >;\n\npublic:\n\n  //\n  // Members\n  //\n\n  cutlass::HostTensor<Element, Layout> destination_tensor;\n  cutlass::HostTensor<Element, cutlass::layout::PitchLinear> source_tensor;\n  \n\npublic:\n\n  MultiplicandTileIteratorTestbed(): \n    destination_tensor({Shape::kContiguous, Shape::kStrided}),\n    source_tensor({Shape::kContiguous, Shape::kStrided}) {\n\n  }\n\n  bool run() {\n\n    cutlass::reference::host::BlockFillSequential(\n      source_tensor.host_data(),\n      source_tensor.capacity()\n    );\n\n    cutlass::reference::host::BlockFillSequential(\n      destination_tensor.host_data(),\n      destination_tensor.capacity(),\n      Element(0),\n      Element(0)\n    );\n\n    //\n    // Launch kernel\n    //\n\n    dim3 grid(1,1);\n    dim3 block(kThreads, 1);\n\n    destination_tensor.sync_device();\n    source_tensor.sync_device();\n\n    test::gemm::threadblock::kernel_gemm_threadblock_tensor_op_multiplicand_store<Iterator><<<\n      grid, block\n    >>>(\n      destination_tensor.device_ref(),\n      source_tensor.device_data()\n    );\n\n    cudaError_t result = cudaDeviceSynchronize();\n    EXPECT_EQ(result, cudaSuccess) << \" - CUDA ERROR: \" << cudaGetErrorString(result);\n\n    destination_tensor.sync_host();\n\n    //\n    // Verify\n    //\n\n    // Verify that its contents match the destination\n    int errors = 0;\n    for (int s = 0; s < Shape::kStrided; ++s) {\n      for (int c = 0; c < Shape::kContiguous; ++c) {\n\n        if (errors >= 10) {\n          break;\n        }\n\n        Element expected = source_tensor.at({c, s});\n        Element got = destination_tensor.at({c, s});\n\n        bool passed = (expected == got);\n        if (!passed) {\n          ++errors;\n        }\n      }\n    }\n\n    EXPECT_EQ(errors, 0)\n      << source_tensor.host_view() << \"\\n\\n\" << destination_tensor.host_view() << std::endl;\n\n    return !errors;\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x8_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 8>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x16_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 16>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x16_w2) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 16>, 2>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x8_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 8>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x32_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 32>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 256x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<256, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 256x32_w8) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<256, 32>, 8>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief \n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/layout/pitch_linear.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/regular_tile_iterator_tensor_op.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/// \ntemplate <typename Iterator>\n__global__ void kernel_gemm_threadblock_tensor_op_multiplicand_store(\n  typename Iterator::TensorRef ref_output,\n  typename Iterator::Element *input) {\n\n  // Construct fragment\n  typename Iterator::Fragment frag;\n\n  frag.clear();\n\n  // each thread loads a fragment\n  using AccessType = cutlass::Array<typename Iterator::Element, Iterator::ThreadMap::kElementsPerAccess>;\n\n  int const kElementsPerAccess = Iterator::ThreadMap::kElementsPerAccess;\n  int stride = Iterator::Shape::kContiguous;\n\n  int warp_id = (threadIdx.x / 32);\n  int lane_id = (threadIdx.x % 32);\n\n  input += (lane_id % 8) * kElementsPerAccess + (lane_id / 8) * stride;\n\n  input += (warp_id * Iterator::Shape::kStrided / Iterator::ThreadMap::Detail::kWarpCount) * stride;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int s = 0; s < Iterator::ThreadMap::Iterations::kStrided; ++s) {\n    CUTLASS_PRAGMA_UNROLL\n    for (int c = 0; c < Iterator::ThreadMap::Iterations::kContiguous; ++c) {\n      CUTLASS_PRAGMA_UNROLL\n      for (int v = 0; v < Iterator::ThreadMap::kElementsPerAccess; ++v) {\n        frag[v + Iterator::ThreadMap::kElementsPerAccess * (c + s * Iterator::ThreadMap::Iterations::kContiguous)] = \n          input[v + c * 64 + s * Iterator::ThreadMap::Delta::kStrided * stride];\n      }\n    }\n  }\n\n  // Use iterator to store results\n  Iterator iter(ref_output, threadIdx.x);\n  iter.store(frag);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Simple test environment\ntemplate <\n  typename Shape_,\n  int WarpCount\n>\nclass MultiplicandTileIteratorTestbed {\npublic:\n\n  //\n  // Define iterator\n  //\n\n  using Shape = Shape_;\n  using Element = cutlass::half_t;\n  using Layout = cutlass::layout::TensorOpMultiplicandCongruous<\n      cutlass::sizeof_bits<Element>::value, 64>;\n  static int const kAdvanceRank = 1;\n  static int const kThreads = 32 * WarpCount;\n\n  using ThreadMap = cutlass::transform::PitchLinearWarpRakedThreadMap<\n    Shape,\n    kThreads,\n    cutlass::layout::PitchLinearShape<8, 4>,\n    128 / cutlass::sizeof_bits<Element>::value\n  >;\n\n  using Iterator = cutlass::transform::threadblock::RegularTileIterator<\n    Shape, Element, Layout, kAdvanceRank, ThreadMap\n  >;\n\npublic:\n\n  //\n  // Members\n  //\n\n  cutlass::HostTensor<Element, Layout> destination_tensor;\n  cutlass::HostTensor<Element, cutlass::layout::PitchLinear> source_tensor;\n  \n\npublic:\n\n  MultiplicandTileIteratorTestbed(): \n    destination_tensor({Shape::kContiguous, Shape::kStrided}),\n    source_tensor({Shape::kContiguous, Shape::kStrided}) {\n\n  }\n\n  bool run() {\n\n    cutlass::reference::host::BlockFillSequential(\n      source_tensor.host_data(),\n      source_tensor.capacity()\n    );\n\n    cutlass::reference::host::BlockFillSequential(\n      destination_tensor.host_data(),\n      destination_tensor.capacity(),\n      Element(0),\n      Element(0)\n    );\n\n    //\n    // Launch kernel\n    //\n\n    dim3 grid(1,1);\n    dim3 block(kThreads, 1);\n\n    destination_tensor.sync_device();\n    source_tensor.sync_device();\n\n    test::gemm::threadblock::kernel_gemm_threadblock_tensor_op_multiplicand_store<Iterator><<<\n      grid, block\n    >>>(\n      destination_tensor.device_ref(),\n      source_tensor.device_data()\n    );\n\n    hipError_t result = hipDeviceSynchronize();\n    EXPECT_EQ(result, hipSuccess) << \" - CUDA ERROR: \" << hipGetErrorString(result);\n\n    destination_tensor.sync_host();\n\n    //\n    // Verify\n    //\n\n    // Verify that its contents match the destination\n    int errors = 0;\n    for (int s = 0; s < Shape::kStrided; ++s) {\n      for (int c = 0; c < Shape::kContiguous; ++c) {\n\n        if (errors >= 10) {\n          break;\n        }\n\n        Element expected = source_tensor.at({c, s});\n        Element got = destination_tensor.at({c, s});\n\n        bool passed = (expected == got);\n        if (!passed) {\n          ++errors;\n        }\n      }\n    }\n\n    EXPECT_EQ(errors, 0)\n      << source_tensor.host_view() << \"\\n\\n\" << destination_tensor.host_view() << std::endl;\n\n    return !errors;\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x8_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 8>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x16_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 16>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x16_w2) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 16>, 2>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x8_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 8>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 64x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<64, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x32_w1) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 32>, 1>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 128x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<128, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 256x32_w4) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<256, 32>, 4>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_tensor_op_multplicand_iterator_congruous_16b, 256x32_w8) {\n\n  test::gemm::threadblock::MultiplicandTileIteratorTestbed<\n    cutlass::layout::PitchLinearShape<256, 32>, 8>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/transform/threadblock/regular_tile_iterator_tensor_op.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests cutlass::transform::threadblock::PredicatedTileIterator \n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator_2dthreadtile.h\"\n\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace transform {\nnamespace threadblock {\nnamespace kernel {\n\n/// Copy with an iterator\ntemplate <typename Iterator>\n__global__ void copy(\n  typename Iterator::Params dst_params, \n  typename Iterator::Element *dst_pointer,\n  typename Iterator::Params src_params,\n  typename Iterator::Element *src_pointer,\n  cutlass::Coord<2> extent) {\n\n  Iterator dst_iterator(dst_params, dst_pointer, extent, threadIdx.x);\n  Iterator src_iterator(src_params, src_pointer, extent, threadIdx.x);\n\n  int iterations = (extent[1] + Iterator::Shape::kStrided - 1) / Iterator::Shape::kStrided;\n\n  typename Iterator::Fragment frag;\n\n  for(size_t i = 0; i < frag.size(); i++)\n    frag[i] = 0;\n\n  src_iterator.load(frag);\n  dst_iterator.store(frag);\n\n  ++dst_iterator;\n  ++src_iterator;\n\n  for (; iterations > 1; --iterations) {\n    \n    src_iterator.load(frag);\n    dst_iterator.store(frag);\n\n    ++dst_iterator;\n    ++src_iterator;\n  }\n}\n\n} // namespace kernel\n} // namespace threadblock\n} // namespace transform\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<Shape, kThreads>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(57, 35);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 35);\n  \n  cutlass::HostTensor<int, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_128x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 4>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, false\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(128, 4);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(128, 4);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_128x64) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 64>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(128, 64);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(128, 64);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x64) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 64>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 64);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 64);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x8) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(32, 8);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 8);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x32_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 32);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 32);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x29_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 29);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 29);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_120x4_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 4>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(120, 4);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(120, 4);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_48x29_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(48, 29);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(48, 29);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  cudaError_t result = cudaGetLastError();\n  EXPECT_EQ(result, cudaSuccess) << \" - CUDA error: \" << cudaGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests cutlass::transform::threadblock::PredicatedTileIterator \n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n\n#include \"cutlass/transform/pitch_linear_thread_map.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/transform/threadblock/predicated_tile_iterator_2dthreadtile.h\"\n\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace transform {\nnamespace threadblock {\nnamespace kernel {\n\n/// Copy with an iterator\ntemplate <typename Iterator>\n__global__ void copy(\n  typename Iterator::Params dst_params, \n  typename Iterator::Element *dst_pointer,\n  typename Iterator::Params src_params,\n  typename Iterator::Element *src_pointer,\n  cutlass::Coord<2> extent) {\n\n  Iterator dst_iterator(dst_params, dst_pointer, extent, threadIdx.x);\n  Iterator src_iterator(src_params, src_pointer, extent, threadIdx.x);\n\n  int iterations = (extent[1] + Iterator::Shape::kStrided - 1) / Iterator::Shape::kStrided;\n\n  typename Iterator::Fragment frag;\n\n  for(size_t i = 0; i < frag.size(); i++)\n    frag[i] = 0;\n\n  src_iterator.load(frag);\n  dst_iterator.store(frag);\n\n  ++dst_iterator;\n  ++src_iterator;\n\n  for (; iterations > 1; --iterations) {\n    \n    src_iterator.load(frag);\n    dst_iterator.store(frag);\n\n    ++dst_iterator;\n    ++src_iterator;\n  }\n}\n\n} // namespace kernel\n} // namespace threadblock\n} // namespace transform\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<Shape, kThreads>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(57, 35);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 35);\n  \n  cutlass::HostTensor<int, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_128x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 4>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, false\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(128, 4);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(128, 4);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_128x64) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 64>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(128, 64);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(128, 64);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x64) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 64>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 64);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 64);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x8) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(32, 8);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 8);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::BlockFillSequential(src_tensor.host_data(), src_tensor.capacity());\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]; ++s) {\n    for (int c = 0; c < alloc_extent[0]; ++c) {\n\n      Element expected = Element(0);\n\n      if (c < copy_extent[0] && s < copy_extent[1]) {\n        expected = src_tensor.at({c, s});\n      }\n      else {\n        expected = oob_value;\n      }\n\n      Element got = dst_tensor.at({c, s});\n      bool equal = (expected == got);\n\n      EXPECT_EQ(expected, got)\n        << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n        << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n      if (!equal) {\n        return;\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x32_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 32);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 32);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_64x29_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(64, 29);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(64, 29);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_120x4_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<128, 4>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(120, 4);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(120, 4);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Transform_threadblock_PredicatedTileIterator, PitchLinear_Stripmined_2dtile_48x29_transpose4x4) {\n\n  using Shape = cutlass::layout::PitchLinearShape<64, 8>;\n  using ThreadTileShape = cutlass::layout::PitchLinearShape<4, 4>;\n  using Layout = cutlass::layout::PitchLinear;\n  using Element = int8_t;\n  static int const kThreads = 32;\n  \n  using ThreadMap = cutlass::transform::PitchLinear2DThreadTileStripminedThreadMap<Shape, kThreads, ThreadTileShape>;\n\n  using Iterator = cutlass::transform::threadblock::PredicatedTileIterator2dThreadTile<\n    Shape, Element, Layout, 1, ThreadMap, true\n  >;\n\n  cutlass::Coord<2> copy_extent = cutlass::make_Coord(48, 29);\n  cutlass::Coord<2> alloc_extent = cutlass::make_Coord(48, 29);\n  \n  cutlass::HostTensor<int8_t, Layout> src_tensor(alloc_extent);\n  cutlass::HostTensor<int8_t, Layout> dst_tensor(alloc_extent);\n\n  Element oob_value = Element(-1);\n  uint64_t seed = 7;\n  cutlass::reference::host::TensorFill(dst_tensor.host_view(), oob_value);\n  cutlass::reference::host::TensorFillRandomUniform(src_tensor.host_view(), seed, 8, -8, 0);\n\n  dst_tensor.sync_device();\n  src_tensor.sync_device();\n\n  typename Iterator::Params dst_params(dst_tensor.layout());\n  typename Iterator::Params src_params(src_tensor.layout());\n\n  dim3 block(kThreads, 1);\n  dim3 grid(1, 1);\n\n  test::transform::threadblock::kernel::copy<Iterator><<< grid, block >>>(\n    dst_params,\n    dst_tensor.device_data(),\n    src_params,\n    src_tensor.device_data(),\n    copy_extent\n  );\n\n  hipError_t result = hipGetLastError();\n  EXPECT_EQ(result, hipSuccess) << \" - CUDA error: \" << hipGetErrorString(result);\n  \n  dst_tensor.sync_host();\n\n  for (int s = 0; s < alloc_extent[1]/4; ++s) {\n    for (int c = 0; c < alloc_extent[0]/4; ++c) {\n      for (int s1 = 0; s1 < 4; s1++){\n        for(int c1 = 0; c1 < 4; c1++){\n          Element expected = Element(0);\n\n          int l_c = c * 4 + c1;\n          int l_s = s * 4 + s1;\n\n          int l_tc = c * 4 + s1;\n          int l_ts = s * 4 + c1;\n\n          if (l_c < copy_extent[0] && l_s < copy_extent[1]) {\n            expected = src_tensor.at({l_c, l_s});\n          }\n          else {\n            expected = oob_value;\n          }    \n\n          Element got = dst_tensor.at({l_tc, l_ts});\n          bool equal = (expected == got);\n\n          EXPECT_EQ(expected, got)\n            << \"Source:\\n\" << src_tensor.host_view() << \"\\n\\n\"\n            << \"Destination:\\n\" << dst_tensor.host_view() << \"\\n\";\n          if (!equal) {\n            return;\n          }\n        }\n      }\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/transform/threadblock/predicated_tile_iterator.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for device-wide GEMM interface\n*/\n\n#include <iostream>\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/epilogue/thread/linear_combination.h\"\n#include \"cutlass/reduction/kernel/reduce_split_k.h\"\n#include \"cutlass/reduction/thread/reduction_operators.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_norm.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace reduction {\n\ntemplate <typename ReductionKernel>\n__global__ void kernel_reduce_splitk(typename ReductionKernel::Params params) {\n\n  __shared__ typename ReductionKernel::SharedStorage shared_storage;\n\n  ReductionKernel reduction_op;\n\n  reduction_op(params, shared_storage);\n}\n\ntemplate <typename ReductionKernel>\nclass ReduceSplitKTestbed {\npublic:\n\n  using ElementAccumulator = typename ReductionKernel::ElementAccumulator;\n  using ElementWorkspace = typename ReductionKernel::ElementWorkspace;\n  using ElementOutput = typename ReductionKernel::ElementOutput;\n  using Layout = cutlass::layout::RowMajor;\n\npublic:\n\n  cutlass::Distribution::Kind distribution_workspace;\n  cutlass::Distribution::Kind distribution_source;\n  uint64_t seed;\n\npublic:\n\n  /// Ctor\n  ReduceSplitKTestbed(\n    cutlass::Distribution::Kind distribution_workspace = cutlass::Distribution::Uniform,\n    cutlass::Distribution::Kind distribution_source = cutlass::Distribution::Uniform,\n    uint64_t seed = 2019\n  ):\n    distribution_workspace(distribution_workspace),\n    distribution_source(distribution_source),\n    seed(seed) {\n\n  }\n\n  /// Helper to initialize a tensor view\n  template <typename Element, typename Layout>\n  bool initialize_tensor(cutlass::TensorView<Element, Layout> view,\n                         cutlass::Distribution::Kind dist_kind, \n                         uint64_t seed) {\n\n    if (dist_kind == cutlass::Distribution::Uniform) {\n      cutlass::reference::host::TensorFillRandomUniform(view, seed, 8, -8, 0);\n    }\n    else if (dist_kind == cutlass::Distribution::Gaussian) {\n      cutlass::reference::host::TensorFillRandomGaussian(view, seed, 0, 0.5, -1);\n    } else if (dist_kind == cutlass::Distribution::Identity) {\n      cutlass::reference::host::TensorFillIdentity(view);\n    } else if (dist_kind == cutlass::Distribution::Sequential) {\n      cutlass::reference::host::BlockFillSequential(view.data(),\n                                                    view.capacity());\n    } else {\n      EXPECT_TRUE(false) << \"Not implemented\";\n      return false;\n    }\n\n    return true;\n  }\n\n  /// Runs a single problem size\n  bool run(\n    cutlass::MatrixCoord problem_size, \n    int partitions, \n    ElementAccumulator alpha = 1, \n    ElementAccumulator beta = 0) {\n\n    cutlass::HostTensor<ElementWorkspace, Layout> workspace({\n      problem_size.row() * partitions, \n      problem_size.column()\n    });\n\n    cutlass::HostTensor<ElementOutput, Layout> source(problem_size);\n    cutlass::HostTensor<ElementOutput, Layout> destination(problem_size);\n    cutlass::HostTensor<ElementOutput, Layout> destination_reference(problem_size, false);\n\n    //\n    // Initialize\n    //\n    initialize_tensor(workspace.host_view(), distribution_workspace, seed);\n    initialize_tensor(source.host_view(), distribution_source, seed + 23);\n\n    cutlass::reference::host::TensorFill(destination.host_view());\n\n    workspace.sync_device();\n    source.sync_device();\n    destination.sync_device();\n\n    //\n    // Launch reduction kernel\n    //\n\n    dim3 block = ReductionKernel::block_shape();\n    dim3 grid = ReductionKernel::grid_shape(problem_size);\n\n    typename ReductionKernel::Params params(\n      problem_size,\n      partitions,\n      problem_size.row() * problem_size.column(),\n      workspace.device_ref(),\n      destination.device_ref(),\n      source.device_ref(),\n      {alpha, beta}\n    );\n\n    test::reduction::kernel_reduce_splitk<ReductionKernel><<< grid, block >>>(params);\n\n    cudaError_t result = cudaDeviceSynchronize();\n\n    EXPECT_EQ(result, cudaSuccess)\n      << \"CUDA error: \" << cudaGetErrorString(result);\n\n    destination.sync_host();\n\n    //\n    // Compute reference\n    //\n\n    for (int m = 0; m < problem_size.row(); ++m) {\n      for (int n = 0; n < problem_size.column(); ++n) {\n\n        ElementAccumulator accum = 0;\n\n        for (int k = 0; k < partitions; ++k) {\n          accum += ElementAccumulator(workspace.at({m + k * problem_size.row(), n}));\n        }\n\n        ElementAccumulator c = ElementAccumulator(source.at({m, n}));\n\n        destination_reference.at({m, n}) = ElementOutput(accum * alpha + beta * c);\n      }\n    }\n\n    //\n    // Compare\n    //\n\n    EXPECT_GT(cutlass::reference::host::TensorNorm(destination.host_view()), 0);\n    EXPECT_GT(cutlass::reference::host::TensorNorm(destination_reference.host_view()), 0);\n\n    bool passed = cutlass::reference::host::TensorEquals(\n        destination.host_view(), destination_reference.host_view());\n\n    EXPECT_TRUE(passed)\n      << \"Workspace =\\n\" << workspace.host_view() << \"\\n\\n\"\n      << \"\\n\"\n      << \"Reference =\\n\" << destination_reference.host_view() << \"\\n\\n\"\n      << \"Computed =\\n\" << destination.host_view() << \"\\n\";\n\n    return passed;\n  }\n\n  /// Runs through a variety of test cases\n  bool run_all() {\n\n    cutlass::MatrixCoord problem_sizes[] = {\n      {8, 8},\n      {136, 72},\n      {248, 232},\n    };\n\n    int partition_counts[] = {\n      1,3,4,5,11\n    };\n\n    bool passed = false;\n\n    for (cutlass::MatrixCoord problem : problem_sizes) {\n      for (int partitions : partition_counts) {\n        passed = run(problem, partitions);\n        if (!passed) {\n          return false;\n        }\n      }\n    }\n\n    return passed;\n  }\n};\n\n} // namespace reduction\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Strictly F32 data\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f32_1_1x32) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = float;\n  int const kN = 1;\n  using Shape = cutlass::MatrixShape<1, 32>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f32_2_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = float;\n  int const kN = 2;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f16_2_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = cutlass::half_t;\n  int const kN = 2;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f16_8_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = cutlass::half_t;\n  int const kN = 8;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for device-wide GEMM interface\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/cutlass.h\"\n#include \"cutlass/epilogue/thread/linear_combination.h\"\n#include \"cutlass/reduction/kernel/reduce_split_k.h\"\n#include \"cutlass/reduction/thread/reduction_operators.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_norm.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace reduction {\n\ntemplate <typename ReductionKernel>\n__global__ void kernel_reduce_splitk(typename ReductionKernel::Params params) {\n\n  __shared__ typename ReductionKernel::SharedStorage shared_storage;\n\n  ReductionKernel reduction_op;\n\n  reduction_op(params, shared_storage);\n}\n\ntemplate <typename ReductionKernel>\nclass ReduceSplitKTestbed {\npublic:\n\n  using ElementAccumulator = typename ReductionKernel::ElementAccumulator;\n  using ElementWorkspace = typename ReductionKernel::ElementWorkspace;\n  using ElementOutput = typename ReductionKernel::ElementOutput;\n  using Layout = cutlass::layout::RowMajor;\n\npublic:\n\n  cutlass::Distribution::Kind distribution_workspace;\n  cutlass::Distribution::Kind distribution_source;\n  uint64_t seed;\n\npublic:\n\n  /// Ctor\n  ReduceSplitKTestbed(\n    cutlass::Distribution::Kind distribution_workspace = cutlass::Distribution::Uniform,\n    cutlass::Distribution::Kind distribution_source = cutlass::Distribution::Uniform,\n    uint64_t seed = 2019\n  ):\n    distribution_workspace(distribution_workspace),\n    distribution_source(distribution_source),\n    seed(seed) {\n\n  }\n\n  /// Helper to initialize a tensor view\n  template <typename Element, typename Layout>\n  bool initialize_tensor(cutlass::TensorView<Element, Layout> view,\n                         cutlass::Distribution::Kind dist_kind, \n                         uint64_t seed) {\n\n    if (dist_kind == cutlass::Distribution::Uniform) {\n      cutlass::reference::host::TensorFillRandomUniform(view, seed, 8, -8, 0);\n    }\n    else if (dist_kind == cutlass::Distribution::Gaussian) {\n      cutlass::reference::host::TensorFillRandomGaussian(view, seed, 0, 0.5, -1);\n    } else if (dist_kind == cutlass::Distribution::Identity) {\n      cutlass::reference::host::TensorFillIdentity(view);\n    } else if (dist_kind == cutlass::Distribution::Sequential) {\n      cutlass::reference::host::BlockFillSequential(view.data(),\n                                                    view.capacity());\n    } else {\n      EXPECT_TRUE(false) << \"Not implemented\";\n      return false;\n    }\n\n    return true;\n  }\n\n  /// Runs a single problem size\n  bool run(\n    cutlass::MatrixCoord problem_size, \n    int partitions, \n    ElementAccumulator alpha = 1, \n    ElementAccumulator beta = 0) {\n\n    cutlass::HostTensor<ElementWorkspace, Layout> workspace({\n      problem_size.row() * partitions, \n      problem_size.column()\n    });\n\n    cutlass::HostTensor<ElementOutput, Layout> source(problem_size);\n    cutlass::HostTensor<ElementOutput, Layout> destination(problem_size);\n    cutlass::HostTensor<ElementOutput, Layout> destination_reference(problem_size, false);\n\n    //\n    // Initialize\n    //\n    initialize_tensor(workspace.host_view(), distribution_workspace, seed);\n    initialize_tensor(source.host_view(), distribution_source, seed + 23);\n\n    cutlass::reference::host::TensorFill(destination.host_view());\n\n    workspace.sync_device();\n    source.sync_device();\n    destination.sync_device();\n\n    //\n    // Launch reduction kernel\n    //\n\n    dim3 block = ReductionKernel::block_shape();\n    dim3 grid = ReductionKernel::grid_shape(problem_size);\n\n    typename ReductionKernel::Params params(\n      problem_size,\n      partitions,\n      problem_size.row() * problem_size.column(),\n      workspace.device_ref(),\n      destination.device_ref(),\n      source.device_ref(),\n      {alpha, beta}\n    );\n\n    test::reduction::kernel_reduce_splitk<ReductionKernel><<< grid, block >>>(params);\n\n    hipError_t result = hipDeviceSynchronize();\n\n    EXPECT_EQ(result, hipSuccess)\n      << \"CUDA error: \" << hipGetErrorString(result);\n\n    destination.sync_host();\n\n    //\n    // Compute reference\n    //\n\n    for (int m = 0; m < problem_size.row(); ++m) {\n      for (int n = 0; n < problem_size.column(); ++n) {\n\n        ElementAccumulator accum = 0;\n\n        for (int k = 0; k < partitions; ++k) {\n          accum += ElementAccumulator(workspace.at({m + k * problem_size.row(), n}));\n        }\n\n        ElementAccumulator c = ElementAccumulator(source.at({m, n}));\n\n        destination_reference.at({m, n}) = ElementOutput(accum * alpha + beta * c);\n      }\n    }\n\n    //\n    // Compare\n    //\n\n    EXPECT_GT(cutlass::reference::host::TensorNorm(destination.host_view()), 0);\n    EXPECT_GT(cutlass::reference::host::TensorNorm(destination_reference.host_view()), 0);\n\n    bool passed = cutlass::reference::host::TensorEquals(\n        destination.host_view(), destination_reference.host_view());\n\n    EXPECT_TRUE(passed)\n      << \"Workspace =\\n\" << workspace.host_view() << \"\\n\\n\"\n      << \"\\n\"\n      << \"Reference =\\n\" << destination_reference.host_view() << \"\\n\\n\"\n      << \"Computed =\\n\" << destination.host_view() << \"\\n\";\n\n    return passed;\n  }\n\n  /// Runs through a variety of test cases\n  bool run_all() {\n\n    cutlass::MatrixCoord problem_sizes[] = {\n      {8, 8},\n      {136, 72},\n      {248, 232},\n    };\n\n    int partition_counts[] = {\n      1,3,4,5,11\n    };\n\n    bool passed = false;\n\n    for (cutlass::MatrixCoord problem : problem_sizes) {\n      for (int partitions : partition_counts) {\n        passed = run(problem, partitions);\n        if (!passed) {\n          return false;\n        }\n      }\n    }\n\n    return passed;\n  }\n};\n\n} // namespace reduction\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Strictly F32 data\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f32_1_1x32) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = float;\n  int const kN = 1;\n  using Shape = cutlass::MatrixShape<1, 32>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f32_2_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = float;\n  int const kN = 2;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f16_2_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = cutlass::half_t;\n  int const kN = 2;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Vectorized access\n//\nTEST(Reduction_ReduceSplitK, f32_f32_f16_8_4x64) {\n\n  using ElementWorkspace = float;\n  using ElementAccumulator = float;\n  using ElementOutput = cutlass::half_t;\n  int const kN = 8;\n  using Shape = cutlass::MatrixShape<4, 64>;\n\n  using OutputOp = cutlass::epilogue::thread::LinearCombination<\n    ElementOutput,\n    kN,\n    ElementAccumulator,\n    ElementAccumulator\n  >;\n\n  using ReductionOp = cutlass::reduction::thread::ReduceAdd<\n    ElementAccumulator, \n    ElementWorkspace,\n    kN\n  >;\n\n  using ReductionKernel = cutlass::reduction::kernel::ReduceSplitK<\n    Shape,\n    OutputOp,\n    ReductionOp\n  >;\n\n  test::reduction::ReduceSplitKTestbed<ReductionKernel> testbed;\n\n  EXPECT_TRUE(testbed.run_all());\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n", "source": "/kaggle/working/cutlass/test/unit/reduction/kernel/reduce_splitk.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/epilogue/epilogue_workspace.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel computes accumulator data and stores it out\ntemplate <typename Epilogue>\n__global__ void kernel_epilogue_workspace(typename Epilogue::Params params) {\n\n  __shared__ typename Epilogue::SharedStorage shared_storage;\n\n  int warp_id = threadIdx.y;\n  int lane_id = threadIdx.x;\n\n  Epilogue epilogue(params, shared_storage, warp_id, lane_id);\n\n  //\n  // Initialize accumulator tile\n  //\n  typename Epilogue::FragmentC accum;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < Epilogue::FragmentC::kElements; ++i) {\n    accum[i] = Element(warp_id * blockDim.x + lane_id);\n  }\n\n  //\n  // Efficient epilogue\n  //\n\n  cutlass::GemmCoord tb_tile_coord{blockIdx.x, blockIdx.y, 0};\n  \n  cutlass::GemmCoord problem_size = \n    tb_tile_coord * \n    cutlass::GemmCoord{Epilogue::Shape::kM, Epilogue::Shape::kN, 1};\n\n  // Store accumulators\n  epilogue(\n    problem_size, \n    tb_tile_coord, \n    accum);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_epilogue_workspace, tensor_op_128x128_64x64) {\n\n  //\n  // Define an instance of the epilogue and see if it works\n  //\n  static int const kWarpCount = 4;\n  static int const kWarpSize = 32;\n\n  using Shape = cutlass::MatrixShape<128, 128>;\n  using FragmentC = cutlass::Array<int, Shape::kCount / (kWarpCount * kWarpSize)>;\n\n  using Epilogue = cutlass::gemm::threadblock::EpilogueWorkspace<\n    Shape,\n    kWarpCount,\n    FragmentC\n  >;\n\n  typename Epilogue::Params params(\n    \n  );\n\n  // Launch the kernel\n  dim3 grid(1,1);\n  dim3 block(kWarpSize, kWarpCount);\n\n  test::gemm::threadblock::kernel_epilogue_workspace<Epilogue><<< grid, block >>>(\n    params\n  );\n\n  cudaError_t result = cudaDeviceSynchronize();\n  EXPECT_EQ(result, cudaSuccess) << \"Kernel launch error - \" << cudaGetErrorString(result);\n\n  //\n  // \n  //\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/epilogue/epilogue_workspace.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel computes accumulator data and stores it out\ntemplate <typename Epilogue>\n__global__ void kernel_epilogue_workspace(typename Epilogue::Params params) {\n\n  __shared__ typename Epilogue::SharedStorage shared_storage;\n\n  int warp_id = threadIdx.y;\n  int lane_id = threadIdx.x;\n\n  Epilogue epilogue(params, shared_storage, warp_id, lane_id);\n\n  //\n  // Initialize accumulator tile\n  //\n  typename Epilogue::FragmentC accum;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < Epilogue::FragmentC::kElements; ++i) {\n    accum[i] = Element(warp_id * blockDim.x + lane_id);\n  }\n\n  //\n  // Efficient epilogue\n  //\n\n  cutlass::GemmCoord tb_tile_coord{blockIdx.x, blockIdx.y, 0};\n  \n  cutlass::GemmCoord problem_size = \n    tb_tile_coord * \n    cutlass::GemmCoord{Epilogue::Shape::kM, Epilogue::Shape::kN, 1};\n\n  // Store accumulators\n  epilogue(\n    problem_size, \n    tb_tile_coord, \n    accum);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM75_gemm_threadblock_epilogue_workspace, tensor_op_128x128_64x64) {\n\n  //\n  // Define an instance of the epilogue and see if it works\n  //\n  static int const kWarpCount = 4;\n  static int const kWarpSize = 32;\n\n  using Shape = cutlass::MatrixShape<128, 128>;\n  using FragmentC = cutlass::Array<int, Shape::kCount / (kWarpCount * kWarpSize)>;\n\n  using Epilogue = cutlass::gemm::threadblock::EpilogueWorkspace<\n    Shape,\n    kWarpCount,\n    FragmentC\n  >;\n\n  typename Epilogue::Params params(\n    \n  );\n\n  // Launch the kernel\n  dim3 grid(1,1);\n  dim3 block(kWarpSize, kWarpCount);\n\n  test::gemm::threadblock::kernel_epilogue_workspace<Epilogue><<< grid, block >>>(\n    params\n  );\n\n  hipError_t result = hipDeviceSynchronize();\n  EXPECT_EQ(result, hipSuccess) << \"Kernel launch error - \" << hipGetErrorString(result);\n\n  //\n  // \n  //\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/gemm/threadblock/epilogue_workspace.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for threadblock level GEMV\n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/tensor_ref.h\"\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n\n#include \"cutlass/gemm/threadblock/gemv.h\"\n#include \"cutlass/gemm/threadblock/default_gemv_core.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Gemv, typename LongIndex, typename RefA, typename RefB, typename RefC>\n__global__ void batched_gemv_threadblock_test_kernel(\n  cutlass::gemm::GemmCoord problem_size,\n  LongIndex stride_a,\n  LongIndex stride_b,\n  LongIndex stride_c,\n  RefA ref_A,\n  RefB ref_B,\n  RefC ref_C\n  ) {\n\n  typename Gemv::IteratorA::TensorCoord threadblock_offset_A(0, 0);\n  typename Gemv::IteratorB::TensorCoord threadblock_offset_B(0, 0);\n  typename Gemv::IteratorB::TensorCoord threadblock_offset_C(0, 0);\n\n  // Move to the right batches for these threads\n  ref_A.add_pointer_offset(threadIdx.y * stride_a);\n  ref_B.add_pointer_offset(threadIdx.y * stride_b);\n  ref_C.add_pointer_offset(threadIdx.y * stride_c);\n\n  // Construct iterators to A and B operands\n  typename Gemv::IteratorA::Params params_A(ref_A.layout());\n  typename Gemv::IteratorA iterator_A(params_A, ref_A.data(), { problem_size.m(), problem_size.k() }, 0, threadblock_offset_A);\n  typename Gemv::IteratorB::Params params_B(ref_B.layout());\n  typename Gemv::IteratorB iterator_B(params_B, ref_B.data(), { problem_size.k(), problem_size.n() }, threadIdx.x, threadblock_offset_B);\n\n  Gemv gemv;\n\n  typename Gemv::FragmentC accum;\n  accum.clear();\n\n  // Compute threadblock-scoped matrix multiply-add\n  gemv(problem_size, accum, iterator_A, iterator_B, accum);\n\n  // IteratorC is PitchLinear<> assumes n() contiguous\n  typename Gemv::IteratorC::Params params_C(ref_C.layout());\n  typename Gemv::IteratorC iterator_C(params_C, ref_C.data(), { problem_size.m(), problem_size.n() }, threadIdx.x, threadblock_offset_C);\n  iterator_C.store(accum);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Shape_,\n         typename ElementAB_,\n         typename ElementC_,\n         typename LayoutA_,\n         typename LayoutB_,\n         typename LayoutC_,\n         int THREAD_N,\n         int THREAD_K,\n         int MAX_THREADS_PER_BLOCK=512,\n         bool DEBUG=false>\nvoid batched_gemv_threadblock_test(cutlass::gemm::GemmCoord problem_size, int num_batch)\n{\n  using Shape = Shape_;\n  using ElementA = ElementAB_;\n  using LayoutA = LayoutA_;\n  using ElementB = ElementAB_;\n  using LayoutB = LayoutB_;\n  using ElementC = ElementC_;\n  using LayoutC = LayoutC_;\n  using ThreadShape = cutlass::gemm::GemmShape<1, THREAD_N, THREAD_K>;\n\n  using Core = typename cutlass::gemm::threadblock::DefaultGemvCore<\n    Shape,\n    ThreadShape,\n    ElementA,\n    LayoutA,\n    ElementB,\n    LayoutB,\n    ElementC,\n    LayoutC\n  >;\n\n  if (DEBUG)\n  { \n      num_batch = 1;\n  }\n\n  using Mma = cutlass::gemm::threadblock::Gemv<Core>;\n\n  // Create host tensors that will be the backing store for the batches\n  // Note that no device memory is initially allocated\n  cutlass::HostTensor<ElementA, LayoutA> matrix_A({problem_size.m(), problem_size.k()}, false); \n  cutlass::HostTensor<ElementB, LayoutB> matrix_B({problem_size.k(), problem_size.n()}, false); \n  cutlass::HostTensor<ElementC, LayoutC> matrix_C_computed({problem_size.m(), problem_size.n()}, false); \n  cutlass::HostTensor<ElementC, LayoutC> matrix_C_reference({problem_size.m(), problem_size.n()}, false);\n\n  // Reserve memory for the batch of tensors\n  matrix_A.reserve(problem_size.m()*problem_size.k()*num_batch);\n  matrix_B.reserve(problem_size.n()*problem_size.k()*num_batch);\n  matrix_C_computed.reserve(problem_size.m()*problem_size.n()*num_batch);\n  matrix_C_reference.reserve(problem_size.m()*problem_size.n()*num_batch, false);\n\n  // Fill eatch tensor batch\n  const int seed = 6834;\n  for (int b = 0; b < num_batch; b++)\n  {\n    if(DEBUG)\n    {\n      cutlass::reference::host::BlockFillSequential(\n        matrix_A.host_data_ptr_offset(b*matrix_A.capacity()), matrix_A.capacity());\n      cutlass::reference::host::BlockFillSequential(\n        matrix_B.host_data_ptr_offset(b*matrix_B.capacity()), matrix_B.capacity());\n    }\n    else\n    {\n      cutlass::reference::host::TensorFillRandomUniform(\n        matrix_A.host_view(b*matrix_A.capacity()),\n        seed + 1660,\n        8,\n        -8,\n        0\n      );\n\n      cutlass::reference::host::TensorFillRandomUniform(\n        matrix_B.host_view(b*matrix_B.capacity()),\n        seed + 1880,\n        8,\n        -8,\n        0\n      );\n    }\n\n    cutlass::reference::host::TensorFill(matrix_C_computed.host_view(b*matrix_C_computed.capacity()));\n    cutlass::reference::host::TensorFill(matrix_C_reference.host_view(b*matrix_C_reference.capacity()));\n  }\n\n  matrix_A.sync_device();\n  matrix_B.sync_device();\n  matrix_C_computed.sync_device();\n\n  dim3 grid(1, 1);      // only 1 CTA is used\n  dim3 block(Shape::kN / THREAD_N, num_batch, 1);\n\n  #if 0\n  printf(\"block dim = %d x %d\\n\", block.x, block.y);\n  #endif\n\n  // Some sanity checks\n  EXPECT_TRUE( problem_size.n() % THREAD_N == 0 );\n  EXPECT_TRUE( block.x*block.y <= MAX_THREADS_PER_BLOCK );\n\n  test::gemm::threadblock::batched_gemv_threadblock_test_kernel<Mma><<< grid, block >>>(\n    problem_size,\n    matrix_A.capacity(),\n    matrix_B.capacity(),\n    matrix_C_computed.capacity(),\n    matrix_A.device_ref(),\n    matrix_B.device_ref(),\n    matrix_C_computed.device_ref()\n  );\n\n  cudaError_t result = cudaDeviceSynchronize();\n  EXPECT_EQ(result, cudaSuccess) << \" kernel error: \" << cudaGetErrorString(result);\n\n  matrix_C_computed.sync_host();\n\n  // Compute the batched gemms\n  for (int b = 0; b < num_batch; b++)\n  {\n\n    cutlass::reference::host::Gemm<ElementA, LayoutA, ElementB, LayoutB,\n                                   ElementC, LayoutC, ElementC, ElementC> reference_gemm;\n\n    reference_gemm(\n      problem_size.mnk(),\n      ElementC(1),\n      matrix_A.host_ref(b*matrix_A.capacity()),\n      matrix_B.host_ref(b*matrix_B.capacity()),\n      ElementC(0),\n      matrix_C_reference.host_ref(b*matrix_C_computed.capacity())\n    );\n\n    bool passed = cutlass::reference::host::TensorEquals(\n                    matrix_C_computed.host_view(b*matrix_C_computed.capacity()), \n                    matrix_C_reference.host_view(b*matrix_C_reference.capacity()));\n\n    EXPECT_TRUE(passed)\n    //<< \"A:\\n\" << matrix_A.host_view() << \"\\n\"\n    //<< \"B:\\n\" << matrix_B.host_view() << \"\\n\"\n      << \"Batch: \" << b << \"\\n\"\n      << \"Reference:\\n\" << matrix_C_reference.host_view(b*matrix_C_reference.capacity()) << \"\\n\"\n      << \"Computed:\\n\" << matrix_C_computed.host_view(b*matrix_C_computed.capacity()) << \"\\n\";\n  }\n}\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n// A: ColumnMajor\n// B: RowMajor\n// C: ColumnMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_crc_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\n// A: RowMajor\n// B: ColumnMajor\n// C: RowMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_rcr_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\n// A: RowMajor\n// B: ColumnMajor\n// C: ColumnMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_rcc_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for threadblock level GEMV\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/tensor_ref.h\"\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n\n#include \"cutlass/gemm/threadblock/gemv.h\"\n#include \"cutlass/gemm/threadblock/default_gemv_core.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace gemm {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Gemv, typename LongIndex, typename RefA, typename RefB, typename RefC>\n__global__ void batched_gemv_threadblock_test_kernel(\n  cutlass::gemm::GemmCoord problem_size,\n  LongIndex stride_a,\n  LongIndex stride_b,\n  LongIndex stride_c,\n  RefA ref_A,\n  RefB ref_B,\n  RefC ref_C\n  ) {\n\n  typename Gemv::IteratorA::TensorCoord threadblock_offset_A(0, 0);\n  typename Gemv::IteratorB::TensorCoord threadblock_offset_B(0, 0);\n  typename Gemv::IteratorB::TensorCoord threadblock_offset_C(0, 0);\n\n  // Move to the right batches for these threads\n  ref_A.add_pointer_offset(threadIdx.y * stride_a);\n  ref_B.add_pointer_offset(threadIdx.y * stride_b);\n  ref_C.add_pointer_offset(threadIdx.y * stride_c);\n\n  // Construct iterators to A and B operands\n  typename Gemv::IteratorA::Params params_A(ref_A.layout());\n  typename Gemv::IteratorA iterator_A(params_A, ref_A.data(), { problem_size.m(), problem_size.k() }, 0, threadblock_offset_A);\n  typename Gemv::IteratorB::Params params_B(ref_B.layout());\n  typename Gemv::IteratorB iterator_B(params_B, ref_B.data(), { problem_size.k(), problem_size.n() }, threadIdx.x, threadblock_offset_B);\n\n  Gemv gemv;\n\n  typename Gemv::FragmentC accum;\n  accum.clear();\n\n  // Compute threadblock-scoped matrix multiply-add\n  gemv(problem_size, accum, iterator_A, iterator_B, accum);\n\n  // IteratorC is PitchLinear<> assumes n() contiguous\n  typename Gemv::IteratorC::Params params_C(ref_C.layout());\n  typename Gemv::IteratorC iterator_C(params_C, ref_C.data(), { problem_size.m(), problem_size.n() }, threadIdx.x, threadblock_offset_C);\n  iterator_C.store(accum);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename Shape_,\n         typename ElementAB_,\n         typename ElementC_,\n         typename LayoutA_,\n         typename LayoutB_,\n         typename LayoutC_,\n         int THREAD_N,\n         int THREAD_K,\n         int MAX_THREADS_PER_BLOCK=512,\n         bool DEBUG=false>\nvoid batched_gemv_threadblock_test(cutlass::gemm::GemmCoord problem_size, int num_batch)\n{\n  using Shape = Shape_;\n  using ElementA = ElementAB_;\n  using LayoutA = LayoutA_;\n  using ElementB = ElementAB_;\n  using LayoutB = LayoutB_;\n  using ElementC = ElementC_;\n  using LayoutC = LayoutC_;\n  using ThreadShape = cutlass::gemm::GemmShape<1, THREAD_N, THREAD_K>;\n\n  using Core = typename cutlass::gemm::threadblock::DefaultGemvCore<\n    Shape,\n    ThreadShape,\n    ElementA,\n    LayoutA,\n    ElementB,\n    LayoutB,\n    ElementC,\n    LayoutC\n  >;\n\n  if (DEBUG)\n  { \n      num_batch = 1;\n  }\n\n  using Mma = cutlass::gemm::threadblock::Gemv<Core>;\n\n  // Create host tensors that will be the backing store for the batches\n  // Note that no device memory is initially allocated\n  cutlass::HostTensor<ElementA, LayoutA> matrix_A({problem_size.m(), problem_size.k()}, false); \n  cutlass::HostTensor<ElementB, LayoutB> matrix_B({problem_size.k(), problem_size.n()}, false); \n  cutlass::HostTensor<ElementC, LayoutC> matrix_C_computed({problem_size.m(), problem_size.n()}, false); \n  cutlass::HostTensor<ElementC, LayoutC> matrix_C_reference({problem_size.m(), problem_size.n()}, false);\n\n  // Reserve memory for the batch of tensors\n  matrix_A.reserve(problem_size.m()*problem_size.k()*num_batch);\n  matrix_B.reserve(problem_size.n()*problem_size.k()*num_batch);\n  matrix_C_computed.reserve(problem_size.m()*problem_size.n()*num_batch);\n  matrix_C_reference.reserve(problem_size.m()*problem_size.n()*num_batch, false);\n\n  // Fill eatch tensor batch\n  const int seed = 6834;\n  for (int b = 0; b < num_batch; b++)\n  {\n    if(DEBUG)\n    {\n      cutlass::reference::host::BlockFillSequential(\n        matrix_A.host_data_ptr_offset(b*matrix_A.capacity()), matrix_A.capacity());\n      cutlass::reference::host::BlockFillSequential(\n        matrix_B.host_data_ptr_offset(b*matrix_B.capacity()), matrix_B.capacity());\n    }\n    else\n    {\n      cutlass::reference::host::TensorFillRandomUniform(\n        matrix_A.host_view(b*matrix_A.capacity()),\n        seed + 1660,\n        8,\n        -8,\n        0\n      );\n\n      cutlass::reference::host::TensorFillRandomUniform(\n        matrix_B.host_view(b*matrix_B.capacity()),\n        seed + 1880,\n        8,\n        -8,\n        0\n      );\n    }\n\n    cutlass::reference::host::TensorFill(matrix_C_computed.host_view(b*matrix_C_computed.capacity()));\n    cutlass::reference::host::TensorFill(matrix_C_reference.host_view(b*matrix_C_reference.capacity()));\n  }\n\n  matrix_A.sync_device();\n  matrix_B.sync_device();\n  matrix_C_computed.sync_device();\n\n  dim3 grid(1, 1);      // only 1 CTA is used\n  dim3 block(Shape::kN / THREAD_N, num_batch, 1);\n\n  #if 0\n  printf(\"block dim = %d x %d\\n\", block.x, block.y);\n  #endif\n\n  // Some sanity checks\n  EXPECT_TRUE( problem_size.n() % THREAD_N == 0 );\n  EXPECT_TRUE( block.x*block.y <= MAX_THREADS_PER_BLOCK );\n\n  test::gemm::threadblock::batched_gemv_threadblock_test_kernel<Mma><<< grid, block >>>(\n    problem_size,\n    matrix_A.capacity(),\n    matrix_B.capacity(),\n    matrix_C_computed.capacity(),\n    matrix_A.device_ref(),\n    matrix_B.device_ref(),\n    matrix_C_computed.device_ref()\n  );\n\n  hipError_t result = hipDeviceSynchronize();\n  EXPECT_EQ(result, hipSuccess) << \" kernel error: \" << hipGetErrorString(result);\n\n  matrix_C_computed.sync_host();\n\n  // Compute the batched gemms\n  for (int b = 0; b < num_batch; b++)\n  {\n\n    cutlass::reference::host::Gemm<ElementA, LayoutA, ElementB, LayoutB,\n                                   ElementC, LayoutC, ElementC, ElementC> reference_gemm;\n\n    reference_gemm(\n      problem_size.mnk(),\n      ElementC(1),\n      matrix_A.host_ref(b*matrix_A.capacity()),\n      matrix_B.host_ref(b*matrix_B.capacity()),\n      ElementC(0),\n      matrix_C_reference.host_ref(b*matrix_C_computed.capacity())\n    );\n\n    bool passed = cutlass::reference::host::TensorEquals(\n                    matrix_C_computed.host_view(b*matrix_C_computed.capacity()), \n                    matrix_C_reference.host_view(b*matrix_C_reference.capacity()));\n\n    EXPECT_TRUE(passed)\n    //<< \"A:\\n\" << matrix_A.host_view() << \"\\n\"\n    //<< \"B:\\n\" << matrix_B.host_view() << \"\\n\"\n      << \"Batch: \" << b << \"\\n\"\n      << \"Reference:\\n\" << matrix_C_reference.host_view(b*matrix_C_reference.capacity()) << \"\\n\"\n      << \"Computed:\\n\" << matrix_C_computed.host_view(b*matrix_C_computed.capacity()) << \"\\n\";\n  }\n}\n\n} // namespace threadblock\n} // namespace gemm\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n// A: ColumnMajor\n// B: RowMajor\n// C: ColumnMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_crc_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_crc_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_crc_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\n// A: RowMajor\n// B: ColumnMajor\n// C: RowMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_rcr_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcr_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcr_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::RowMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\n// A: RowMajor\n// B: ColumnMajor\n// C: ColumnMajor\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp32_fp32_2N_2K) {\n  \n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 5x1x128x128_rcc_fp32_fp32_4N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 128, 128);\n  const int num_batch = 5;\n  const int THREAD_N = 4;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape, float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_fp32_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                float, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp16_fp32_2N_2K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 2;\n  \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_fp16_fp32_2N_8K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 8;\n \n  using Shape = cutlass::gemm::GemmShape<1, 64, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_fp16_fp32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                cutlass::half_t, float, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 4x1x64x64_rcc_i8_i32_2N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 64, 64);\n  const int num_batch = 4;\n  const int THREAD_N = 2;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 128, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n\nTEST(SM50_batched_gemv_threadblock, 16x1x17x64_rcc_i8_i32_1N_4K) {\n  using namespace test::gemm::threadblock;\n  cutlass::gemm::GemmCoord problem_size(1, 17, 64);\n  const int num_batch = 16;\n  const int THREAD_N = 1;\n  const int THREAD_K = 4;\n\n  using Shape = cutlass::gemm::GemmShape<1, 32, THREAD_K>;\n  batched_gemv_threadblock_test<Shape,\n                                int8_t, int32_t, \n                                cutlass::layout::RowMajor,\n                                cutlass::layout::ColumnMajor,\n                                cutlass::layout::ColumnMajor,\n                                THREAD_N, THREAD_K>(problem_size, num_batch);\n}\n", "source": "/kaggle/working/cutlass/test/unit/gemm/threadblock/batched_gemv.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests that the stream-K scheduler covers the entire problem space.\n*/\n\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/kernel_hardware_info.hpp\"\n#include \"cutlass/gemm/kernel/sm90_tile_scheduler_stream_k.hpp\"\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/reference/device/tensor_fill.h\"\n\n#include \"../../common/cutlass_unit_test.h\"\n\n// Grids are launched with clusters enabled in these tests,\n// so the CTK version must support cluster launching.\n#if defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\nusing namespace cute;\nusing ProblemShape_MNKL = Shape<int, int, int, int>;\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel for getting each piece of work for a given block from the scheduler and logging\n/// the K iterations visited by the block.\ntemplate <\n  class Scheduler,\n  class TileShape,\n  class ClusterShape\n>\n__global__\nvoid\nrun_scheduler(int* visit_counters, typename Scheduler::Params params, TileShape tile_shape, ClusterShape cluster_shape, ProblemShape_MNKL problem_shape_mnkl) {\n  Scheduler scheduler{params};\n  auto work_tile_info = scheduler.get_current_work();\n\n  while (work_tile_info.is_valid()) {\n    // Increment counters to indicate coverage\n    auto tile_idx = Scheduler::output_tile_index(params, work_tile_info);\n    auto offset = tile_idx * params.divmod_tiles_per_output_tile_.divisor + work_tile_info.K_idx;\n    for (auto i = 0; i < work_tile_info.k_tile_count; ++i) {\n      // Use atomicAdd because the visit counters are shared by multiple thread blocks.\n      // While having more than one block increment the same counter indicates failure,\n      // we need to ensure that this behavior is captured (by having both increments reflected).\n      atomicAdd(visit_counters + offset + i, 1);\n    }\n\n    bool continue_current = scheduler.continue_current_work(work_tile_info);\n    if (!continue_current) {\n      scheduler.advance_to_next_work();\n      work_tile_info = scheduler.get_current_work();\n    }\n  }\n}\n\n/// Host-side wrapper for launching the kernel to test the scheduler.\ntemplate <\n  class TileShape,\n  class ClusterShape,\n  uint32_t NumMmaWarpGroups = 2\n>\nbool\ntest_scheduler(\n  ProblemShape_MNKL problem_shape_mnkl,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count,\n  int splits=1,\n  bool expect_data_parallel=false) {\n\n  using Scheduler = cutlass::gemm::kernel::detail::PersistentTileSchedulerSm90StreamK<TileShape, ClusterShape>;\n\n  cutlass::KernelHardwareInfo hw_info{0, sm_count};\n  auto params = Scheduler::to_underlying_arguments(problem_shape_mnkl, tile_shape, cluster_shape, hw_info, {splits}, nullptr);\n\n  typename Scheduler::Arguments args{};\n\n  // Set up the grid for the problem\n  dim3 grid = Scheduler::get_grid_shape(params, problem_shape_mnkl, tile_shape, cluster_shape, hw_info, args);\n\n  auto print_info = [&]() {\n    std::cout << \"Failed with problem size \"\n      << size<0>(problem_shape_mnkl) << \"x\"\n      << size<1>(problem_shape_mnkl) << \"x\"\n      << size<2>(problem_shape_mnkl) << \"x\"\n      << size<3>(problem_shape_mnkl)\n      << \" and grid size \" << grid.x << \"x\"\n      << grid.y << \"x\" << grid.z\n      << \" splits=\" << params.divmod_splits_.divisor\n      << \" k_iter=\" << params.divmod_tiles_per_output_tile_.divisor\n      << \" big_units_=\" << params.big_units_\n      << \" big_groups_=\" << params.big_groups_\n      << \" sk_tiles=\" << params.sk_tiles_\n      << \" sk_units=\" << params.sk_units_\n      << \" k_tiles_per_sk_unit=\" << params.divmod_k_tiles_per_sk_unit_.divisor\n      << \" k_tiles_per_sk_big_unit=\" << params.divmod_k_tiles_per_sk_big_unit_.divisor\n      << \" units_per_problem=\" << params.units_per_problem_\n      << \" groups=\" << params.divmod_sk_groups_.divisor << std::endl;\n  };\n\n  // If we expect the schedule to be data-parallel only, ensure that no stream-K tiles are launched.\n  if (expect_data_parallel && params.sk_tiles_ != 0) {\n    print_info();\n    std::cout << \"Expected stream-K to select a data-parallel decomposition.\" << std::endl;\n    return false;\n  }\n\n  // Allocate counters indicating the number of times each k iteration of each output tile has been visited\n  auto [blk_m, blk_n, blk_l] = Scheduler::get_tiled_cta_shape_mnl(problem_shape_mnkl, tile_shape, cluster_shape);\n  auto total_counters = blk_m * blk_n * blk_l * params.divmod_tiles_per_output_tile_.divisor;\n  cutlass::DeviceAllocation<int> visit_counters(total_counters);\n\n  // Initialize counters to zero\n  cudaError_t err = cudaMemset((void*)visit_counters.get(), 0, sizeof(int) * total_counters);\n  if (err != cudaSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__ << \" cudaMemset failed with error: \" << cudaGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  // Set up cluster and cluster launch. This is needed even for this simple kernel because\n  // the SM90 scheduler needs to be able to query the CTA id within a cluster, which requires\n  // explicitly launching with clusters.\n  dim3 cluster{\n    static_cast<uint32_t>(cute::get<0>(ClusterShape{})),\n    static_cast<uint32_t>(cute::get<1>(ClusterShape{})),\n    static_cast<uint32_t>(cute::get<2>(ClusterShape{}))\n  };\n\n  cudaLaunchConfig_t launch_config;\n  launch_config.gridDim = grid;\n  launch_config.blockDim = {1, 1, 1};\n  launch_config.dynamicSmemBytes = 0;\n  launch_config.stream = NULL;\n\n  cudaLaunchAttribute launch_attribute[1];\n  launch_attribute[0].id = cudaLaunchAttributeClusterDimension;\n  launch_attribute[0].val.clusterDim.x = cluster.x;\n  launch_attribute[0].val.clusterDim.y = cluster.y;\n  launch_attribute[0].val.clusterDim.z = cluster.z;\n\n  launch_config.attrs = launch_attribute;\n  launch_config.numAttrs = 1;\n\n  void const* kernel = (void const*) run_scheduler<Scheduler, TileShape, ClusterShape>;\n  int* counters_ptr = visit_counters.get();\n  void* kernel_params[] = {\n    &counters_ptr,\n    &params,\n    &tile_shape,\n    &cluster_shape,\n    &problem_shape_mnkl\n  };\n\n  // Run the scheduler to completion and log visits to each k iteration\n  err = cudaLaunchKernelExC(&launch_config, kernel, kernel_params);\n\n  if (err != cudaSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__\n              << \" cudaLaunchKernelExC failed with error: \"\n              << cudaGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  err = cudaDeviceSynchronize();\n  if (err != cudaSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__\n              << \" scheduler kernel failed with error: \"\n              << cudaGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  // Copy visit counts back to host and ensure that all entries are ones\n  std::vector<int> host_visit_counts(total_counters);\n  visit_counters.copy_to_host(host_visit_counts.data());\n\n  for (size_t i = 0; i < host_visit_counts.size(); ++i) {\n    if (host_visit_counts[i] != 1) {\n      print_info();\n      std::cout << \"Error at idx: \" << i << \". Got count \" << host_visit_counts[i] << std::endl;\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/// Executes tests of the scheduler with a sweep across problem size K\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool sweep_k(\n  ProblemShape_MNKL problem_shape_mnkl,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count,\n  int splits=1,\n  bool expect_data_parallel=false,\n  int k_start=128,\n  int k_stop=16384,\n  int k_step=0) {\n\n  if (k_step == 0) {\n    k_step = 4 * cute::size<2>(tile_shape);\n  }\n\n  for (int k = k_start; k <= k_stop; k += k_step) {\n    ProblemShape_MNKL problem{get<0>(problem_shape_mnkl), get<1>(problem_shape_mnkl), k, get<3>(problem_shape_mnkl)};\n    bool passed = test_scheduler(problem, tile_shape, cluster_shape, sm_count, splits, expect_data_parallel);\n    if (!passed) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/// Executes tests of the scheduler that are expected to result in a data-parallel schedule.\n/// This function assumes that the problem, tile, and cluster shape, alongside the SM count,\n/// are such that the problem executes only full waves on the device.\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool test_data_parallel(\n  int blocks_m,\n  int blocks_n,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count) {\n\n  // Since the configuration passed in executes only full waves, increasing\n  // the batch dimension simply results in running more full waves.\n  for (int l = 1; l < 4; ++l) {\n    ProblemShape_MNKL problem_shape{\n      size<0>(tile_shape) * blocks_m, size<1>(tile_shape) * blocks_n, 1, l};\n    bool passed = sweep_k(problem_shape, tile_shape, cluster_shape, sm_count, /*splits=*/1, /*expect_data_parallel=*/true);\n\n    if (!passed) {\n      return false;\n    }\n  }\n  return true;\n}\n\n/// Executes tests of the scheduler on the generic stream-K decomposition.\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool test_stream_k(\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count) {\n\n  int tile_m = size<0>(tile_shape);\n  int tile_n = size<1>(tile_shape);\n\n  for (int m_blocks = 1; m_blocks <= 24; ++m_blocks) {\n    for (int n_blocks = 1; n_blocks <= 24; ++n_blocks) {\n      for (int l = 1; l < 4; ++l) {\n        ProblemShape_MNKL problem{m_blocks * tile_m, n_blocks * tile_n, 1, l};\n        if (!sweep_k(problem, tile_shape, cluster_shape, sm_count)) {\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM90_Device_Gemm_stream_k_scheduler, 256x128x64_2x1x1) {\n  using TileShape_MNK = Shape<_256,_128,_64>;\n  using ClusterShape_MNK = Shape<_2,_1,_1>;\n\n  TileShape_MNK tile_shape;\n  ClusterShape_MNK cluster_shape;\n\n  // Test various data-parallel cases\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/ 4, /*blocks_n=*/ 4, tile_shape, cluster_shape, /*sm_count=*/ 16));\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/16, /*blocks_n=*/ 4, tile_shape, cluster_shape, /*sm_count=*/ 64));\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/ 8, /*blocks_n=*/27, tile_shape, cluster_shape, /*sm_count=*/108));\n\n  // Test various stream-K cases\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/ 16));\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/ 64));\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/108));\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM90_Device_Gemm_stream_k_scheduler, 128x128x64_2x1x1) {\n  using TileShape_MNK = Shape<_128,_128,_64>;\n  using ClusterShape_MNK = Shape<_2,_1,_1>;\n\n  TileShape_MNK tile_shape;\n  ClusterShape_MNK cluster_shape;\n\n  EXPECT_TRUE(test_scheduler({128, 512, 2048, 1}, tile_shape, cluster_shape, 114));\n}\n\n#endif // defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests that the stream-K scheduler covers the entire problem space.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cutlass/kernel_hardware_info.hpp\"\n#include \"cutlass/gemm/kernel/sm90_tile_scheduler_stream_k.hpp\"\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/reference/device/tensor_fill.h\"\n\n#include \"../../common/cutlass_unit_test.h\"\n\n// Grids are launched with clusters enabled in these tests,\n// so the CTK version must support cluster launching.\n#if defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\nusing namespace cute;\nusing ProblemShape_MNKL = Shape<int, int, int, int>;\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Kernel for getting each piece of work for a given block from the scheduler and logging\n/// the K iterations visited by the block.\ntemplate <\n  class Scheduler,\n  class TileShape,\n  class ClusterShape\n>\n__global__\nvoid\nrun_scheduler(int* visit_counters, typename Scheduler::Params params, TileShape tile_shape, ClusterShape cluster_shape, ProblemShape_MNKL problem_shape_mnkl) {\n  Scheduler scheduler{params};\n  auto work_tile_info = scheduler.get_current_work();\n\n  while (work_tile_info.is_valid()) {\n    // Increment counters to indicate coverage\n    auto tile_idx = Scheduler::output_tile_index(params, work_tile_info);\n    auto offset = tile_idx * params.divmod_tiles_per_output_tile_.divisor + work_tile_info.K_idx;\n    for (auto i = 0; i < work_tile_info.k_tile_count; ++i) {\n      // Use atomicAdd because the visit counters are shared by multiple thread blocks.\n      // While having more than one block increment the same counter indicates failure,\n      // we need to ensure that this behavior is captured (by having both increments reflected).\n      atomicAdd(visit_counters + offset + i, 1);\n    }\n\n    bool continue_current = scheduler.continue_current_work(work_tile_info);\n    if (!continue_current) {\n      scheduler.advance_to_next_work();\n      work_tile_info = scheduler.get_current_work();\n    }\n  }\n}\n\n/// Host-side wrapper for launching the kernel to test the scheduler.\ntemplate <\n  class TileShape,\n  class ClusterShape,\n  uint32_t NumMmaWarpGroups = 2\n>\nbool\ntest_scheduler(\n  ProblemShape_MNKL problem_shape_mnkl,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count,\n  int splits=1,\n  bool expect_data_parallel=false) {\n\n  using Scheduler = cutlass::gemm::kernel::detail::PersistentTileSchedulerSm90StreamK<TileShape, ClusterShape>;\n\n  cutlass::KernelHardwareInfo hw_info{0, sm_count};\n  auto params = Scheduler::to_underlying_arguments(problem_shape_mnkl, tile_shape, cluster_shape, hw_info, {splits}, nullptr);\n\n  typename Scheduler::Arguments args{};\n\n  // Set up the grid for the problem\n  dim3 grid = Scheduler::get_grid_shape(params, problem_shape_mnkl, tile_shape, cluster_shape, hw_info, args);\n\n  auto print_info = [&]() {\n    std::cout << \"Failed with problem size \"\n      << size<0>(problem_shape_mnkl) << \"x\"\n      << size<1>(problem_shape_mnkl) << \"x\"\n      << size<2>(problem_shape_mnkl) << \"x\"\n      << size<3>(problem_shape_mnkl)\n      << \" and grid size \" << grid.x << \"x\"\n      << grid.y << \"x\" << grid.z\n      << \" splits=\" << params.divmod_splits_.divisor\n      << \" k_iter=\" << params.divmod_tiles_per_output_tile_.divisor\n      << \" big_units_=\" << params.big_units_\n      << \" big_groups_=\" << params.big_groups_\n      << \" sk_tiles=\" << params.sk_tiles_\n      << \" sk_units=\" << params.sk_units_\n      << \" k_tiles_per_sk_unit=\" << params.divmod_k_tiles_per_sk_unit_.divisor\n      << \" k_tiles_per_sk_big_unit=\" << params.divmod_k_tiles_per_sk_big_unit_.divisor\n      << \" units_per_problem=\" << params.units_per_problem_\n      << \" groups=\" << params.divmod_sk_groups_.divisor << std::endl;\n  };\n\n  // If we expect the schedule to be data-parallel only, ensure that no stream-K tiles are launched.\n  if (expect_data_parallel && params.sk_tiles_ != 0) {\n    print_info();\n    std::cout << \"Expected stream-K to select a data-parallel decomposition.\" << std::endl;\n    return false;\n  }\n\n  // Allocate counters indicating the number of times each k iteration of each output tile has been visited\n  auto [blk_m, blk_n, blk_l] = Scheduler::get_tiled_cta_shape_mnl(problem_shape_mnkl, tile_shape, cluster_shape);\n  auto total_counters = blk_m * blk_n * blk_l * params.divmod_tiles_per_output_tile_.divisor;\n  cutlass::DeviceAllocation<int> visit_counters(total_counters);\n\n  // Initialize counters to zero\n  hipError_t err = hipMemset((void*)visit_counters.get(), 0, sizeof(int) * total_counters);\n  if (err != hipSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__ << \" hipMemset failed with error: \" << hipGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  // Set up cluster and cluster launch. This is needed even for this simple kernel because\n  // the SM90 scheduler needs to be able to query the CTA id within a cluster, which requires\n  // explicitly launching with clusters.\n  dim3 cluster{\n    static_cast<uint32_t>(cute::get<0>(ClusterShape{})),\n    static_cast<uint32_t>(cute::get<1>(ClusterShape{})),\n    static_cast<uint32_t>(cute::get<2>(ClusterShape{}))\n  };\n\n  hipLaunchConfig_t launch_config;\n  launch_config.gridDim = grid;\n  launch_config.blockDim = {1, 1, 1};\n  launch_config.dynamicSmemBytes = 0;\n  launch_config.stream = NULL;\n\n  hipLaunchAttribute launch_attribute[1];\n  launch_attribute[0].id = cudaLaunchAttributeClusterDimension;\n  launch_attribute[0].val.clusterDim.x = cluster.x;\n  launch_attribute[0].val.clusterDim.y = cluster.y;\n  launch_attribute[0].val.clusterDim.z = cluster.z;\n\n  launch_config.attrs = launch_attribute;\n  launch_config.numAttrs = 1;\n\n  void const* kernel = (void const*) run_scheduler<Scheduler, TileShape, ClusterShape>;\n  int* counters_ptr = visit_counters.get();\n  void* kernel_params[] = {\n    &counters_ptr,\n    &params,\n    &tile_shape,\n    &cluster_shape,\n    &problem_shape_mnkl\n  };\n\n  // Run the scheduler to completion and log visits to each k iteration\n  err = hipLaunchKernelExC(&launch_config, kernel, kernel_params);\n\n  if (err != hipSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__\n              << \" hipLaunchKernelExC failed with error: \"\n              << hipGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  err = hipDeviceSynchronize();\n  if (err != hipSuccess) {\n    print_info();\n    std::cout << __FILE__ << \":\" << __LINE__\n              << \" scheduler kernel failed with error: \"\n              << hipGetErrorString(err) << std::endl;\n    return false;\n  }\n\n  // Copy visit counts back to host and ensure that all entries are ones\n  std::vector<int> host_visit_counts(total_counters);\n  visit_counters.copy_to_host(host_visit_counts.data());\n\n  for (size_t i = 0; i < host_visit_counts.size(); ++i) {\n    if (host_visit_counts[i] != 1) {\n      print_info();\n      std::cout << \"Error at idx: \" << i << \". Got count \" << host_visit_counts[i] << std::endl;\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/// Executes tests of the scheduler with a sweep across problem size K\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool sweep_k(\n  ProblemShape_MNKL problem_shape_mnkl,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count,\n  int splits=1,\n  bool expect_data_parallel=false,\n  int k_start=128,\n  int k_stop=16384,\n  int k_step=0) {\n\n  if (k_step == 0) {\n    k_step = 4 * cute::size<2>(tile_shape);\n  }\n\n  for (int k = k_start; k <= k_stop; k += k_step) {\n    ProblemShape_MNKL problem{get<0>(problem_shape_mnkl), get<1>(problem_shape_mnkl), k, get<3>(problem_shape_mnkl)};\n    bool passed = test_scheduler(problem, tile_shape, cluster_shape, sm_count, splits, expect_data_parallel);\n    if (!passed) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/// Executes tests of the scheduler that are expected to result in a data-parallel schedule.\n/// This function assumes that the problem, tile, and cluster shape, alongside the SM count,\n/// are such that the problem executes only full waves on the device.\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool test_data_parallel(\n  int blocks_m,\n  int blocks_n,\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count) {\n\n  // Since the configuration passed in executes only full waves, increasing\n  // the batch dimension simply results in running more full waves.\n  for (int l = 1; l < 4; ++l) {\n    ProblemShape_MNKL problem_shape{\n      size<0>(tile_shape) * blocks_m, size<1>(tile_shape) * blocks_n, 1, l};\n    bool passed = sweep_k(problem_shape, tile_shape, cluster_shape, sm_count, /*splits=*/1, /*expect_data_parallel=*/true);\n\n    if (!passed) {\n      return false;\n    }\n  }\n  return true;\n}\n\n/// Executes tests of the scheduler on the generic stream-K decomposition.\ntemplate <\n  class TileShape,\n  class ClusterShape\n>\nbool test_stream_k(\n  TileShape tile_shape,\n  ClusterShape cluster_shape,\n  int sm_count) {\n\n  int tile_m = size<0>(tile_shape);\n  int tile_n = size<1>(tile_shape);\n\n  for (int m_blocks = 1; m_blocks <= 24; ++m_blocks) {\n    for (int n_blocks = 1; n_blocks <= 24; ++n_blocks) {\n      for (int l = 1; l < 4; ++l) {\n        ProblemShape_MNKL problem{m_blocks * tile_m, n_blocks * tile_n, 1, l};\n        if (!sweep_k(problem, tile_shape, cluster_shape, sm_count)) {\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM90_Device_Gemm_stream_k_scheduler, 256x128x64_2x1x1) {\n  using TileShape_MNK = Shape<_256,_128,_64>;\n  using ClusterShape_MNK = Shape<_2,_1,_1>;\n\n  TileShape_MNK tile_shape;\n  ClusterShape_MNK cluster_shape;\n\n  // Test various data-parallel cases\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/ 4, /*blocks_n=*/ 4, tile_shape, cluster_shape, /*sm_count=*/ 16));\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/16, /*blocks_n=*/ 4, tile_shape, cluster_shape, /*sm_count=*/ 64));\n  EXPECT_TRUE(test_data_parallel(/*blocks_m=*/ 8, /*blocks_n=*/27, tile_shape, cluster_shape, /*sm_count=*/108));\n\n  // Test various stream-K cases\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/ 16));\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/ 64));\n  EXPECT_TRUE(test_stream_k(tile_shape, cluster_shape, /*sm_count=*/108));\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM90_Device_Gemm_stream_k_scheduler, 128x128x64_2x1x1) {\n  using TileShape_MNK = Shape<_128,_128,_64>;\n  using ClusterShape_MNK = Shape<_2,_1,_1>;\n\n  TileShape_MNK tile_shape;\n  ClusterShape_MNK cluster_shape;\n\n  EXPECT_TRUE(test_scheduler({128, 512, 2048, 1}, tile_shape, cluster_shape, 114));\n}\n\n#endif // defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/gemm/device/sm90_gemm_stream_k_scheduler.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for device-wide GEMM interface\n\n*/\n\n#include <iostream>\n\n#include \"../../common/cutlass_unit_test.h\"\n#include \"cutlass/cutlass.h\"\n\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/gemm/kernel/gemm_grouped.h\"\n#include \"cutlass/gemm/kernel/default_gemm_grouped.h\"\n#include \"cutlass/gemm/device/gemm_grouped.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"testbed_grouped.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n#if defined(CUTLASS_ARCH_MMA_SM80_SUPPORTED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Visitor class to abstract away the algorithm for iterating over tiles.\n//\n// This is the prototype. We will delete this when the efficient kernel is\n// available.\nstruct GemmGroupedProblemVisitor {\n\n  struct Params {\n    cutlass::gemm::GemmCoord const *problem_sizes;\n    int32_t                         problem_count;\n    int64_t const                  *tile_count;\n  };\n\n  struct SharedStorage {\n    //\n    // Nothing for now. As an optimization step, we could consider parallel\n    // argmin or prefix sums across the block.\n    //\n  };\n\n  //\n  // Data members\n  //\n\n  SharedStorage &shared_storage;\n  Params const &params;\n  cutlass::MatrixCoord threadblock_shape;\n\n  int64_t tile_idx;\n  int64_t tile_count_sum;\n  int64_t problem_tile_start;\n  int32_t problem_idx;\n\n  //\n  // Methods\n  //\n  CUTLASS_DEVICE\n  GemmGroupedProblemVisitor(\n    SharedStorage &shared_storage_,\n    Params const &params_,\n    cutlass::MatrixCoord threadblock_shape_,\n    int32_t block_idx\n  ):\n    shared_storage(shared_storage_),\n    params(params_),\n    threadblock_shape(threadblock_shape_),\n    tile_idx(block_idx),\n    tile_count_sum(0),\n    problem_idx(0)\n  {\n\n    cutlass::gemm::GemmCoord problem = params.problem_sizes[problem_idx];\n\n    cutlass::gemm::GemmCoord  grid = grid_shape(problem);\n\n    problem_tile_start = 0;\n    tile_count_sum = grid.m() * grid.n();\n  }\n\n  /// Get the grid shape\n  CUTLASS_HOST_DEVICE\n  static cutlass::gemm::GemmCoord grid_shape(\n    cutlass::gemm::GemmCoord const &problem,\n    cutlass::MatrixCoord const & block_shape) {\n\n    return cutlass::gemm::GemmCoord(\n      ((problem.m() - 1 + block_shape.row()) / block_shape.row()),\n      ((problem.n() - 1 + block_shape.column()) / block_shape.column()),\n      1);\n  }\n\n  /// Get the grid shape\n  CUTLASS_DEVICE\n  cutlass::gemm::GemmCoord grid_shape(cutlass::gemm::GemmCoord const &problem) const {\n    return grid_shape(problem, threadblock_shape);\n  }\n\n  /// Returns true if there is a tile to compute\n  CUTLASS_DEVICE\n  bool next_tile() {\n\n    if (tile_idx < tile_count_sum) {\n      return true;\n    }\n\n    do {\n      ++problem_idx;\n\n      if (problem_idx >= params.problem_count) {\n        return false;\n      }\n\n      cutlass::gemm::GemmCoord problem = params.problem_sizes[problem_idx];\n      cutlass::gemm::GemmCoord  grid = grid_shape(problem);\n\n      int64_t tile_count = grid.m() * grid.n();\n\n      problem_tile_start = tile_count_sum;\n      tile_count_sum += tile_count;\n\n    } while (tile_count_sum <= tile_idx);\n\n    return true;\n  }\n\n  /// Gets the global tile index\n  CUTLASS_HOST_DEVICE\n  int64_t tile_index() const {\n    return tile_idx;\n  }\n\n  /// Gets the index of the problem\n  CUTLASS_HOST_DEVICE\n  int32_t problem_index() const {\n    return problem_idx;\n  }\n\n  /// Returns the problem size for the current problem\n  CUTLASS_HOST_DEVICE\n  cutlass::gemm::GemmCoord problem_size() const {\n    return params.problem_sizes[problem_idx];\n  }\n\n  CUTLASS_HOST_DEVICE\n  int64_t threadblock_idx() const {\n    return tile_idx - problem_tile_start;\n  }\n\n  CUTLASS_DEVICE\n  void advance(int32_t grid_size) {\n    tile_idx += grid_size;\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int ThreadblockShapeM, int ThreadblockShapeN>\n__global__ void GroupedBatchedKernel(GemmGroupedProblemVisitor::Params params) {\n\n  __shared__ GemmGroupedProblemVisitor::SharedStorage shared_storage;\n\n  GemmGroupedProblemVisitor problem_visitor(\n    shared_storage,\n    params,\n    {ThreadblockShapeM, ThreadblockShapeN},\n    blockIdx.x);\n\n  while (problem_visitor.next_tile()) {\n\n    cutlass::gemm::GemmCoord problem_size = problem_visitor.problem_size();\n    int64_t threadblock_idx                       = problem_visitor.threadblock_idx();\n\n    cutlass::gemm::GemmCoord grid_shape = problem_visitor.grid_shape(problem_size);\n\n    int threadblock_tile_m_idx = int(threadblock_idx / grid_shape.n());\n    int threadblock_tile_n_idx = int(threadblock_idx % grid_shape.n());\n\n    //\n    // Do the MMA\n    //\n\n    if (threadIdx.x == 0) {\n      #if 0\n      printf(\"Block %d - tile: %lld, problem %d, threadblock_idx: %lld, threadblock(m: %d, n: %d)\\n\",\n        blockIdx.x,\n        static_cast<long long>(problem_visitor.tile_index()),\n        problem_visitor.problem_index(),\n        threadblock_idx,\n        threadblock_tile_m_idx,\n        threadblock_tile_n_idx);\n      #endif\n    }\n\n    // Next tile\n    problem_visitor.advance(gridDim.x);\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_scheduler, 64x64x32_32x32x32) {\n\n  int32_t problem_count = 16;\n\n  int const kThreadblockShapeM = 64;\n  int const kThreadblockShapeN = 64;\n\n  std::vector<cutlass::gemm::GemmCoord> problem_sizes(problem_count);\n  std::vector<int64_t> tile_counts(problem_count);\n\n  // construct a few problems of random sizes\n  srand(1921);\n  for (int32_t i = 0; i < problem_count; ++i) {\n    problem_sizes.at(i) = cutlass::gemm::GemmCoord(\n      8 * (rand() % 48) + 64,\n      8 * (rand() % 48) + 64,\n      8 * (rand() % 48) + 64);\n  }\n\n  // compute prefix sum\n  int64_t tile_count = 0;\n\n  for (int32_t i = 0; i < problem_count; ++i) {\n\n    cutlass::gemm::GemmCoord grid_shape = GemmGroupedProblemVisitor::grid_shape(\n      problem_sizes.at(i), {kThreadblockShapeM, kThreadblockShapeN});\n\n    int32_t problem_tile_count = (grid_shape.m() * grid_shape.n());\n\n    int64_t tile_start = tile_count;\n\n    tile_count += problem_tile_count;\n    tile_counts.at(i) = tile_count;\n\n    if (false) {\n      std::cout << \"Problem \" << i << \" size(\"\n        << problem_sizes.at(i).m() << \"-by-\" << problem_sizes.at(i).n()\n        << \") - tiles: \" << problem_tile_count << \",  grid(\" << grid_shape.m() << \", \" << grid_shape.n()\n        << \"), tiles[\" << tile_start << \", \" << tile_count << \")\" << std::endl;\n    }\n  }\n\n  // Copy to device memory\n  cutlass::DeviceAllocation<cutlass::gemm::GemmCoord> problem_sizes_device(problem_count);\n  cutlass::DeviceAllocation<int64_t>                  tile_counts_device(problem_count);\n\n  problem_sizes_device.copy_from_host(problem_sizes.data());\n  tile_counts_device.copy_from_host(tile_counts.data());\n\n  GemmGroupedProblemVisitor::Params params;\n  params.problem_sizes = problem_sizes_device.get();\n  params.problem_count = problem_count;\n  params.tile_count = tile_counts_device.get();\n\n  // Launch the kernel\n  dim3 grid(108, 1, 1);\n  dim3 block(128, 1, 1);\n\n  GroupedBatchedKernel<kThreadblockShapeM, kThreadblockShapeN><<< grid, block >>>(params);\n\n  // wait\n  cudaDeviceSynchronize();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16n_f16t_f32n_tensor_op_f32, 128x128x32_64x64x32) {\n\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 32>,\n    cutlass::gemm::GemmShape<64, 64, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(24);\n  EXPECT_TRUE(passed);\n\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16n_f16t_f32t_tensor_op_f32, 128x128x32_64x64x32) {\n\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::RowMajor,    // row major\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 32>,\n    cutlass::gemm::GemmShape<64, 64, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(24);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16t_f16n_f32n_tensor_op_f32, 128x64x32_64x32x32) {\n\n  using ElementOutput = cutlass::half_t;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 32>,\n    cutlass::gemm::GemmShape<64, 32, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16t_f16n_f32t_tensor_op_f32, 128x64x32_64x32x32) {\n\n  using ElementOutput = cutlass::half_t;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 32>,\n    cutlass::gemm::GemmShape<64, 32, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f64t_f64t_f64n_tensor_op_f64, 64x64x16_32x32x16) {\n\n  using ElementInput = double;\n  using ElementOutput = double;\n  using ElementAccumulator = double;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32n_simt_f32, 128x128x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32t_simt_f32, 128x128x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32n_simt_f32, 128x64x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32t_simt_f32, 128x64x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32n_cf32n_cf32n_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32c_cf32t_cf32n_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32c_cf32t_cf32t_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32t_cf32h_cf32n_tensorop_f32, 64x64x16_16x16x16) {\n\n  using ElementInput = cutlass::complex<double>;\n  using ElementOutput = cutlass::complex<double>;\n  using ElementAccumulator = cutlass::complex<double>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 16, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n#endif // #if defined(CUTLASS_ARCH_MMA_SM80_SUPPORTED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for device-wide GEMM interface\n\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n\n#include \"../../common/cutlass_unit_test.h\"\n#include \"cutlass/cutlass.h\"\n\n#include \"cutlass/gemm/gemm.h\"\n#include \"cutlass/gemm/kernel/gemm_grouped.h\"\n#include \"cutlass/gemm/kernel/default_gemm_grouped.h\"\n#include \"cutlass/gemm/device/gemm_grouped.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/gemm.h\"\n#include \"cutlass/util/reference/host/tensor_compare.h\"\n#include \"cutlass/util/reference/host/tensor_copy.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n#include \"cutlass/util/tensor_view_io.h\"\n\n#include \"testbed_grouped.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n#if defined(CUTLASS_ARCH_MMA_SM80_SUPPORTED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Visitor class to abstract away the algorithm for iterating over tiles.\n//\n// This is the prototype. We will delete this when the efficient kernel is\n// available.\nstruct GemmGroupedProblemVisitor {\n\n  struct Params {\n    cutlass::gemm::GemmCoord const *problem_sizes;\n    int32_t                         problem_count;\n    int64_t const                  *tile_count;\n  };\n\n  struct SharedStorage {\n    //\n    // Nothing for now. As an optimization step, we could consider parallel\n    // argmin or prefix sums across the block.\n    //\n  };\n\n  //\n  // Data members\n  //\n\n  SharedStorage &shared_storage;\n  Params const &params;\n  cutlass::MatrixCoord threadblock_shape;\n\n  int64_t tile_idx;\n  int64_t tile_count_sum;\n  int64_t problem_tile_start;\n  int32_t problem_idx;\n\n  //\n  // Methods\n  //\n  CUTLASS_DEVICE\n  GemmGroupedProblemVisitor(\n    SharedStorage &shared_storage_,\n    Params const &params_,\n    cutlass::MatrixCoord threadblock_shape_,\n    int32_t block_idx\n  ):\n    shared_storage(shared_storage_),\n    params(params_),\n    threadblock_shape(threadblock_shape_),\n    tile_idx(block_idx),\n    tile_count_sum(0),\n    problem_idx(0)\n  {\n\n    cutlass::gemm::GemmCoord problem = params.problem_sizes[problem_idx];\n\n    cutlass::gemm::GemmCoord  grid = grid_shape(problem);\n\n    problem_tile_start = 0;\n    tile_count_sum = grid.m() * grid.n();\n  }\n\n  /// Get the grid shape\n  CUTLASS_HOST_DEVICE\n  static cutlass::gemm::GemmCoord grid_shape(\n    cutlass::gemm::GemmCoord const &problem,\n    cutlass::MatrixCoord const & block_shape) {\n\n    return cutlass::gemm::GemmCoord(\n      ((problem.m() - 1 + block_shape.row()) / block_shape.row()),\n      ((problem.n() - 1 + block_shape.column()) / block_shape.column()),\n      1);\n  }\n\n  /// Get the grid shape\n  CUTLASS_DEVICE\n  cutlass::gemm::GemmCoord grid_shape(cutlass::gemm::GemmCoord const &problem) const {\n    return grid_shape(problem, threadblock_shape);\n  }\n\n  /// Returns true if there is a tile to compute\n  CUTLASS_DEVICE\n  bool next_tile() {\n\n    if (tile_idx < tile_count_sum) {\n      return true;\n    }\n\n    do {\n      ++problem_idx;\n\n      if (problem_idx >= params.problem_count) {\n        return false;\n      }\n\n      cutlass::gemm::GemmCoord problem = params.problem_sizes[problem_idx];\n      cutlass::gemm::GemmCoord  grid = grid_shape(problem);\n\n      int64_t tile_count = grid.m() * grid.n();\n\n      problem_tile_start = tile_count_sum;\n      tile_count_sum += tile_count;\n\n    } while (tile_count_sum <= tile_idx);\n\n    return true;\n  }\n\n  /// Gets the global tile index\n  CUTLASS_HOST_DEVICE\n  int64_t tile_index() const {\n    return tile_idx;\n  }\n\n  /// Gets the index of the problem\n  CUTLASS_HOST_DEVICE\n  int32_t problem_index() const {\n    return problem_idx;\n  }\n\n  /// Returns the problem size for the current problem\n  CUTLASS_HOST_DEVICE\n  cutlass::gemm::GemmCoord problem_size() const {\n    return params.problem_sizes[problem_idx];\n  }\n\n  CUTLASS_HOST_DEVICE\n  int64_t threadblock_idx() const {\n    return tile_idx - problem_tile_start;\n  }\n\n  CUTLASS_DEVICE\n  void advance(int32_t grid_size) {\n    tile_idx += grid_size;\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int ThreadblockShapeM, int ThreadblockShapeN>\n__global__ void GroupedBatchedKernel(GemmGroupedProblemVisitor::Params params) {\n\n  __shared__ GemmGroupedProblemVisitor::SharedStorage shared_storage;\n\n  GemmGroupedProblemVisitor problem_visitor(\n    shared_storage,\n    params,\n    {ThreadblockShapeM, ThreadblockShapeN},\n    blockIdx.x);\n\n  while (problem_visitor.next_tile()) {\n\n    cutlass::gemm::GemmCoord problem_size = problem_visitor.problem_size();\n    int64_t threadblock_idx                       = problem_visitor.threadblock_idx();\n\n    cutlass::gemm::GemmCoord grid_shape = problem_visitor.grid_shape(problem_size);\n\n    int threadblock_tile_m_idx = int(threadblock_idx / grid_shape.n());\n    int threadblock_tile_n_idx = int(threadblock_idx % grid_shape.n());\n\n    //\n    // Do the MMA\n    //\n\n    if (threadIdx.x == 0) {\n      #if 0\n      printf(\"Block %d - tile: %lld, problem %d, threadblock_idx: %lld, threadblock(m: %d, n: %d)\\n\",\n        blockIdx.x,\n        static_cast<long long>(problem_visitor.tile_index()),\n        problem_visitor.problem_index(),\n        threadblock_idx,\n        threadblock_tile_m_idx,\n        threadblock_tile_n_idx);\n      #endif\n    }\n\n    // Next tile\n    problem_visitor.advance(gridDim.x);\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_scheduler, 64x64x32_32x32x32) {\n\n  int32_t problem_count = 16;\n\n  int const kThreadblockShapeM = 64;\n  int const kThreadblockShapeN = 64;\n\n  std::vector<cutlass::gemm::GemmCoord> problem_sizes(problem_count);\n  std::vector<int64_t> tile_counts(problem_count);\n\n  // construct a few problems of random sizes\n  srand(1921);\n  for (int32_t i = 0; i < problem_count; ++i) {\n    problem_sizes.at(i) = cutlass::gemm::GemmCoord(\n      8 * (rand() % 48) + 64,\n      8 * (rand() % 48) + 64,\n      8 * (rand() % 48) + 64);\n  }\n\n  // compute prefix sum\n  int64_t tile_count = 0;\n\n  for (int32_t i = 0; i < problem_count; ++i) {\n\n    cutlass::gemm::GemmCoord grid_shape = GemmGroupedProblemVisitor::grid_shape(\n      problem_sizes.at(i), {kThreadblockShapeM, kThreadblockShapeN});\n\n    int32_t problem_tile_count = (grid_shape.m() * grid_shape.n());\n\n    int64_t tile_start = tile_count;\n\n    tile_count += problem_tile_count;\n    tile_counts.at(i) = tile_count;\n\n    if (false) {\n      std::cout << \"Problem \" << i << \" size(\"\n        << problem_sizes.at(i).m() << \"-by-\" << problem_sizes.at(i).n()\n        << \") - tiles: \" << problem_tile_count << \",  grid(\" << grid_shape.m() << \", \" << grid_shape.n()\n        << \"), tiles[\" << tile_start << \", \" << tile_count << \")\" << std::endl;\n    }\n  }\n\n  // Copy to device memory\n  cutlass::DeviceAllocation<cutlass::gemm::GemmCoord> problem_sizes_device(problem_count);\n  cutlass::DeviceAllocation<int64_t>                  tile_counts_device(problem_count);\n\n  problem_sizes_device.copy_from_host(problem_sizes.data());\n  tile_counts_device.copy_from_host(tile_counts.data());\n\n  GemmGroupedProblemVisitor::Params params;\n  params.problem_sizes = problem_sizes_device.get();\n  params.problem_count = problem_count;\n  params.tile_count = tile_counts_device.get();\n\n  // Launch the kernel\n  dim3 grid(108, 1, 1);\n  dim3 block(128, 1, 1);\n\n  GroupedBatchedKernel<kThreadblockShapeM, kThreadblockShapeN><<< grid, block >>>(params);\n\n  // wait\n  hipDeviceSynchronize();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16n_f16t_f32n_tensor_op_f32, 128x128x32_64x64x32) {\n\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 32>,\n    cutlass::gemm::GemmShape<64, 64, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(24);\n  EXPECT_TRUE(passed);\n\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16n_f16t_f32t_tensor_op_f32, 128x128x32_64x64x32) {\n\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::RowMajor,    // row major\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 32>,\n    cutlass::gemm::GemmShape<64, 64, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(24);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16t_f16n_f32n_tensor_op_f32, 128x64x32_64x32x32) {\n\n  using ElementOutput = cutlass::half_t;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 32>,\n    cutlass::gemm::GemmShape<64, 32, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f16t_f16n_f32t_tensor_op_f32, 128x64x32_64x32x32) {\n\n  using ElementOutput = cutlass::half_t;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    cutlass::half_t,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    cutlass::half_t,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    8,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 32>,\n    cutlass::gemm::GemmShape<64, 32, 32>,\n    cutlass::gemm::GemmShape<16, 8, 16>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 128 / cutlass::sizeof_bits<ElementOutput>::value,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f64t_f64t_f64n_tensor_op_f64, 64x64x16_32x32x16) {\n\n  using ElementInput = double;\n  using ElementOutput = double;\n  using ElementAccumulator = double;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    4>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32n_simt_f32, 128x128x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32t_simt_f32, 128x128x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 128, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32n_simt_f32, 128x64x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_f32t_f32t_f32t_simt_f32, 128x64x8_64x32x1) {\n\n  using ElementInput = float;\n  using ElementOutput = float;\n  using ElementAccumulator = float;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassSimt,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<128, 64, 8>,\n    cutlass::gemm::GemmShape<64, 32, 8>,\n    cutlass::gemm::GemmShape<1, 1, 1>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32n_cf32n_cf32n_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32c_cf32t_cf32n_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32c_cf32t_cf32t_tensorop_f32, 64x64x16_32x32x16) {\n\n  using ElementInput = cutlass::complex<float>;\n  using ElementOutput = cutlass::complex<float>;\n  using ElementAccumulator = cutlass::complex<float>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementInput,\n    cutlass::layout::ColumnMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::RowMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<64, 64, 16>,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 8, 8>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(SM80_Device_GemmGrouped_cf32t_cf32h_cf32n_tensorop_f32, 64x64x16_16x16x16) {\n\n  using ElementInput = cutlass::complex<double>;\n  using ElementOutput = cutlass::complex<double>;\n  using ElementAccumulator = cutlass::complex<double>;\n\n  using GemmKernel = typename cutlass::gemm::kernel::DefaultGemmGrouped<\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kNone,\n    1,\n    ElementInput,\n    cutlass::layout::RowMajor,\n    cutlass::ComplexTransform::kConjugate,\n    1,\n    ElementOutput, cutlass::layout::ColumnMajor,\n    ElementAccumulator,\n    cutlass::arch::OpClassTensorOp,\n    cutlass::arch::Sm80,\n    cutlass::gemm::GemmShape<32, 32, 16>,\n    cutlass::gemm::GemmShape<16, 16, 16>,\n    cutlass::gemm::GemmShape<8, 8, 4>,\n    cutlass::epilogue::thread::LinearCombination<\n        ElementOutput, 1,\n        ElementAccumulator, ElementAccumulator>,\n    cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle,\n    3,\n    cutlass::gemm::kernel::GroupScheduleMode::kDeviceOnly,\n    cutlass::arch::OpMultiplyAddComplex>::GemmKernel;\n\n  using Gemm = cutlass::gemm::device::GemmGrouped<GemmKernel>;\n\n  //\n  // Test\n  //\n\n  test::gemm::device::TestbedGrouped<Gemm> testbed;\n\n  bool passed = testbed.run(27);\n  EXPECT_TRUE(passed);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n#endif // #if defined(CUTLASS_ARCH_MMA_SM80_SUPPORTED)\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/gemm/device/gemm_grouped_sm80.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineAsync class\n*/\n\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineAsync<Stages>::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\n// Simple 1 producer warp, one consumer warp scenario\ntemplate <class ClusterShape, uint32_t NumStages>\n__global__ static \nvoid pipeline_async_basic_device(uint32_t const num_iterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineAsync<NumStages>;\n  using PipelineState = typename cutlass::PipelineState<NumStages>;\n\n  using SharedStorage = SharedStorage<NumStages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  int warp_idx = __shfl_sync(0xffffffff, threadIdx.x / 32, 0);\n  int lane_predicate = cute::elect_one_sync();\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n  \n  // This example showcases 2 producer 1 consumer example \n  typename MainloopPipeline::Params params;\n  params.producer_arv_count = 2;\n  params.consumer_arv_count = 1;\n  MainloopPipeline pipeline(shared_storage.storage, params);\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n  __syncthreads();\n\n\n  if (lane_predicate) {\n    // Producer Warps\n    if (warp_idx==0 || warp_idx==1) {\n\n      PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();\n      int prologue_iterations = min(NumStages, num_iterations);\n      for ( int i = 0; i < prologue_iterations; ++i) {\n        // Can also specify stage to commit directly\n        pipeline.producer_commit(smem_pipe_write);\n        ++smem_pipe_write;\n      }\n\n      int mainloop_iterations = num_iterations - prologue_iterations;\n\n      for ( ; mainloop_iterations > 0; --mainloop_iterations) {\n        pipeline.producer_acquire(smem_pipe_write);\n        pipeline.producer_commit(smem_pipe_write);\n        ++smem_pipe_write;\n      }\n    }\n    else {\n      PipelineState smem_pipe_read;\n      for (int iter=0 ; iter < num_iterations; ++iter) {\n        pipeline.consumer_wait(smem_pipe_read);\n        pipeline.consumer_release(smem_pipe_read);\n        ++smem_pipe_read;\n      }\n    }\n  }\n\n  // To make sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive();  \n  cute::cluster_wait();  \n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 96;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest() = default;\n\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(uint32_t const kNumIters,\n                  cudaStream_t stream = nullptr) {\n\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 2;\n    cudaError_t result;\n\n    for (int iter = 0; iter < iterations; ++iter) {\n\n      // Define the tiled MMA layout (static, 4warps)\n      using MainloopPipeline = typename cutlass::PipelineAsync<Stages>;\n\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = cudaFuncSetAttribute(\n        pipeline_async_basic_device<decltype(cluster_shape), Stages>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_async_basic_device<decltype(cluster_shape), Stages>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    } // profiling loop ends\n\n    result = cudaDeviceSynchronize();\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    return cudaSuccess;\n  }\n\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage3) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 3;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage4) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage6) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 6;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage8) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 8;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage9) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 9;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage11) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 11;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineAsync class\n*/\n\n#include \"hip/hip_runtime.h\"\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineAsync<Stages>::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\n// Simple 1 producer warp, one consumer warp scenario\ntemplate <class ClusterShape, uint32_t NumStages>\n__global__ static \nvoid pipeline_async_basic_device(uint32_t const num_iterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineAsync<NumStages>;\n  using PipelineState = typename cutlass::PipelineState<NumStages>;\n\n  using SharedStorage = SharedStorage<NumStages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  int warp_idx = __shfl_sync(0xffffffff, threadIdx.x / 32, 0);\n  int lane_predicate = cute::elect_one_sync();\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n  \n  // This example showcases 2 producer 1 consumer example \n  typename MainloopPipeline::Params params;\n  params.producer_arv_count = 2;\n  params.consumer_arv_count = 1;\n  MainloopPipeline pipeline(shared_storage.storage, params);\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n  __syncthreads();\n\n\n  if (lane_predicate) {\n    // Producer Warps\n    if (warp_idx==0 || warp_idx==1) {\n\n      PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();\n      int prologue_iterations = min(NumStages, num_iterations);\n      for ( int i = 0; i < prologue_iterations; ++i) {\n        // Can also specify stage to commit directly\n        pipeline.producer_commit(smem_pipe_write);\n        ++smem_pipe_write;\n      }\n\n      int mainloop_iterations = num_iterations - prologue_iterations;\n\n      for ( ; mainloop_iterations > 0; --mainloop_iterations) {\n        pipeline.producer_acquire(smem_pipe_write);\n        pipeline.producer_commit(smem_pipe_write);\n        ++smem_pipe_write;\n      }\n    }\n    else {\n      PipelineState smem_pipe_read;\n      for (int iter=0 ; iter < num_iterations; ++iter) {\n        pipeline.consumer_wait(smem_pipe_read);\n        pipeline.consumer_release(smem_pipe_read);\n        ++smem_pipe_read;\n      }\n    }\n  }\n\n  // To make sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive();  \n  cute::cluster_wait();  \n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 96;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest() = default;\n\n\n  // Run CuTe GEMM kernel\n  hipError_t run(uint32_t const kNumIters,\n                  hipStream_t stream = nullptr) {\n\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 2;\n    hipError_t result;\n\n    for (int iter = 0; iter < iterations; ++iter) {\n\n      // Define the tiled MMA layout (static, 4warps)\n      using MainloopPipeline = typename cutlass::PipelineAsync<Stages>;\n\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = hipFuncSetAttribute(reinterpret_cast<const void*>(pipeline_async_basic_device<decltype(cluster_shape), Stages>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_async_basic_device<decltype(cluster_shape), Stages>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    } // profiling loop ends\n\n    result = hipDeviceSynchronize();\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    return hipSuccess;\n  }\n\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage3) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 3;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage4) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage6) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 6;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage8) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 8;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage9) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 9;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineAsync, Cluster4x4_Stage11) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 11;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/pipeline_async.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class\n*/\n\n\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <class ClusterShape, uint32_t NumStages>\n__global__ static\nvoid pipeline_device(uint32_t const NumIterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = cutlass::PipelineTmaAsync<NumStages>;\n  using PipelineState = cutlass::PipelineState<NumStages>;\n\n  using SharedStorage = SharedStorage<NumStages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{}; // (m,n) -> cta_id\n  int warp_idx = __shfl_sync(0xffffffff, threadIdx.x / 32, 0);\n  int warp_group_thread_idx = threadIdx.x % 128;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1\n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = sizeof(uint32_t) * NumProducers;\n  uint32_t const per_cta_bytes = sizeof(uint32_t);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params params;\n  params.transaction_bytes = TmaTransactionBytes;\n  params.role = MainloopPipeline::ThreadCategory::ProducerConsumer;\n  params.is_leader = warp_group_thread_idx == 0;\n  params.num_consumers = 128;\n\n  MainloopPipeline pipeline(shared_storage.storage, params, cluster_shape);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();\n  cute::cluster_wait();\n\n  // Total number of gemm_k_iterations\n  auto mma_k_iterations  = NumIterations;\n  auto tma_k_iterations  = NumIterations;\n\n  PipelineState smem_pipe_read;\n  // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n  // i.e., we know that the buffer is indeed empty\n  PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();\n  PipelineState smem_pipe_release;\n  int K_TILE_MMAS = 1;\n\n  int lane_predicate = cute::elect_one_sync();\n  int k_pipe_tma_prologue = min(NumStages, tma_k_iterations);\n\n  // DMA Prologue (Loads)\n  CUTLASS_PRAGMA_UNROLL\n  for(int i = 0; i < k_pipe_tma_prologue; ++i) {\n    pipeline.producer_acquire(smem_pipe_write);\n    // cp.async.bulk.tensor would typically happen here\n    pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n    ++smem_pipe_write;\n  }\n  tma_k_iterations -= k_pipe_tma_prologue;\n\n  // MMA Prologue (Compute) - modeling inflight MMAs\n  for (int iter = 0; iter < K_TILE_MMAS; ++iter)\n  {\n    pipeline.consumer_wait(smem_pipe_read);\n    warpgroup_arrive();\n    // GMMA would typically happen here\n\n    ++smem_pipe_read;\n  }\n\n  mma_k_iterations -= K_TILE_MMAS;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int iter = 0; iter < mma_k_iterations; ++iter)\n  {\n    pipeline.consumer_wait(smem_pipe_read);\n\n    warpgroup_arrive();\n    // GMMA would typically happen here\n\n    pipeline.consumer_release(smem_pipe_release);\n\n    if (lane_predicate && (warp_idx == 0) && (tma_k_iterations > 0)) {\n      pipeline.producer_acquire(smem_pipe_write);\n      // cp.async.bulk.tensor would typically happen here\n      pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n      ++smem_pipe_write;\n      --tma_k_iterations;\n    }\n\n    // next read stage\n    ++smem_pipe_read;\n    ++smem_pipe_release;\n  }\n\n  // To make sure remote SMEM doesn't get destoryed\n  cute::cluster_arrive();\n  cute::cluster_wait();\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest(){};\n\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(uint32_t const kNumIters,\n                  cudaStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    [[maybe_unused]] auto num_stages = Int<Stages>{};\n\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    cudaEvent_t events[2];\n    cudaError_t result;\n\n    for (cudaEvent_t & event : events) {\n      result = cudaEventCreate(&event);\n      if (result != cudaSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = cudaEventRecord(events[0]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = cudaFuncSetAttribute(\n        pipeline_device<decltype(cluster_shape), Stages>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    } // profiling loop ends\n\n    result = cudaEventRecord(events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = cudaDeviceSynchronize();\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = cudaEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (cudaEvent_t & event : events) {\n      (void)cudaEventDestroy(event);\n    }\n\n    return cudaSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x4_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class\n*/\n\n\n#include \"hip/hip_runtime.h\"\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <class ClusterShape, uint32_t NumStages>\n__global__ static\nvoid pipeline_device(uint32_t const NumIterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = cutlass::PipelineTmaAsync<NumStages>;\n  using PipelineState = cutlass::PipelineState<NumStages>;\n\n  using SharedStorage = SharedStorage<NumStages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{}; // (m,n) -> cta_id\n  int warp_idx = __shfl_sync(0xffffffff, threadIdx.x / 32, 0);\n  int warp_group_thread_idx = threadIdx.x % 128;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1\n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = sizeof(uint32_t) * NumProducers;\n  uint32_t const per_cta_bytes = sizeof(uint32_t);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params params;\n  params.transaction_bytes = TmaTransactionBytes;\n  params.role = MainloopPipeline::ThreadCategory::ProducerConsumer;\n  params.is_leader = warp_group_thread_idx == 0;\n  params.num_consumers = 128;\n\n  MainloopPipeline pipeline(shared_storage.storage, params, cluster_shape);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();\n  cute::cluster_wait();\n\n  // Total number of gemm_k_iterations\n  auto mma_k_iterations  = NumIterations;\n  auto tma_k_iterations  = NumIterations;\n\n  PipelineState smem_pipe_read;\n  // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n  // i.e., we know that the buffer is indeed empty\n  PipelineState smem_pipe_write = cutlass::make_producer_start_state<MainloopPipeline>();\n  PipelineState smem_pipe_release;\n  int K_TILE_MMAS = 1;\n\n  int lane_predicate = cute::elect_one_sync();\n  int k_pipe_tma_prologue = min(NumStages, tma_k_iterations);\n\n  // DMA Prologue (Loads)\n  CUTLASS_PRAGMA_UNROLL\n  for(int i = 0; i < k_pipe_tma_prologue; ++i) {\n    pipeline.producer_acquire(smem_pipe_write);\n    // cp.async.bulk.tensor would typically happen here\n    pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n    ++smem_pipe_write;\n  }\n  tma_k_iterations -= k_pipe_tma_prologue;\n\n  // MMA Prologue (Compute) - modeling inflight MMAs\n  for (int iter = 0; iter < K_TILE_MMAS; ++iter)\n  {\n    pipeline.consumer_wait(smem_pipe_read);\n    warpgroup_arrive();\n    // GMMA would typically happen here\n\n    ++smem_pipe_read;\n  }\n\n  mma_k_iterations -= K_TILE_MMAS;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int iter = 0; iter < mma_k_iterations; ++iter)\n  {\n    pipeline.consumer_wait(smem_pipe_read);\n\n    warpgroup_arrive();\n    // GMMA would typically happen here\n\n    pipeline.consumer_release(smem_pipe_release);\n\n    if (lane_predicate && (warp_idx == 0) && (tma_k_iterations > 0)) {\n      pipeline.producer_acquire(smem_pipe_write);\n      // cp.async.bulk.tensor would typically happen here\n      pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n      ++smem_pipe_write;\n      --tma_k_iterations;\n    }\n\n    // next read stage\n    ++smem_pipe_read;\n    ++smem_pipe_release;\n  }\n\n  // To make sure remote SMEM doesn't get destoryed\n  cute::cluster_arrive();\n  cute::cluster_wait();\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest(){};\n\n\n  // Run CuTe GEMM kernel\n  hipError_t run(uint32_t const kNumIters,\n                  hipStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    [[maybe_unused]] auto num_stages = Int<Stages>{};\n\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    hipEvent_t events[2];\n    hipError_t result;\n\n    for (hipEvent_t & event : events) {\n      result = hipEventCreate(&event);\n      if (result != hipSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = hipEventRecord(events[0]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = hipFuncSetAttribute(reinterpret_cast<const void*>(pipeline_device<decltype(cluster_shape), Stages>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    } // profiling loop ends\n\n    result = hipEventRecord(events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = hipDeviceSynchronize();\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = hipEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (hipEvent_t & event : events) {\n      (void)hipEventDestroy(event);\n    }\n\n    return hipSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x4_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x2_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/pipeline_tma_async.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineCLCFetchAsync class\n*/\n\n#define KERNEL_DBG_TRACE false\n\n#include <cuda/atomic>\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed_cluster_launch_control.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/gemm/kernel/sm100_tile_scheduler.hpp\"\n\n\nusing namespace cute;\nusing namespace cutlass;\nusing namespace cutlass::gemm::kernel::detail;\n\n//////////////////// Shared Memory  /////////////////////////\n\ntemplate <uint32_t Stages, typename ClusterShape>\nstruct SharedStorage\n{\n  alignas(16) typename PersistentTileSchedulerSm100<ClusterShape, Stages>::CLCResponse clc_response[Stages];\n  alignas(16) typename PipelineCLCFetchAsync<Stages, ClusterShape>::SharedStorage storage;\n};\n\n//////////////////// Kernel /////////////////////////\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(256, 1)\n__global__ static\nvoid pipeline_device(int *d_workerCount)\n{\n  extern __shared__ char shared_memory[];\n\n  // single producer, multiple consumers\n  // producer: WG0\n  // consumer: WG1\n\n  using SharedStorage = SharedStorage<Stages, ClusterShape>;\n  using Scheduler = PersistentTileSchedulerSm100<ClusterShape, Stages>;\n  using TileSchedulingPipeline = PipelineCLCFetchAsync<Stages, ClusterShape>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Logistics\n  int warp_idx = canonical_warp_idx();\n  auto cluster_shape = ClusterShape{};\n\n  typename TileSchedulingPipeline::Params params;\n  params.transaction_bytes = 16;\n\n  constexpr int NUM_PRODUCER = 32;\n  constexpr int NUM_CONSUMERS_PER_CTA = 32;\n  params.consumer_arv_count = NUM_PRODUCER + NUM_CONSUMERS_PER_CTA * cute::size<0>(cluster_shape) * cute::size<1>(cluster_shape);\n  params.producer_arv_count = 1;\n  // Only the first CTA in the Cluster is producing.\n  params.producer_blockid = 0;\n\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n  // mbarrier.init\n  TileSchedulingPipeline scheduler_pipeline(shared_storage.storage, params );\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();\n  cute::cluster_wait();\n\n  uint32_t is_first_block_in_cluster = block_id_in_cluster.x == 0 && block_id_in_cluster.y == 0;\n  int lane_predicate = cute::elect_one_sync();\n\n  uint32_t is_producer = (is_first_block_in_cluster && warp_idx == 0);\n  uint32_t is_consumer = (warp_idx == 4);\n\n  PipelineState<Stages> scheduler_pipe_state;\n  PipelineState<Stages> scheduler_pipe_state_write = cutlass::make_producer_start_state<TileSchedulingPipeline>();\n  typename Scheduler::WorkTileInfo work_tile_info = {\n    static_cast<int32_t>(blockIdx.x),\n    static_cast<int32_t>(blockIdx.y),\n    static_cast<int32_t>(blockIdx.z),\n    false\n  };\n\n  // Persistent loop\n  do {\n    // Producer\n    if (is_producer) {\n      // Only 1 thread of the entire cluster issues the query.\n      uint32_t mbarrier_addr = scheduler_pipeline.producer_get_barrier(scheduler_pipe_state_write);\n\n      // Wait for clcID buffer to become empty with a flipped phase\n      scheduler_pipeline.producer_acquire(scheduler_pipe_state_write);\n\n      if (cute::elect_one_sync()) {\n        Scheduler::issue_clc_query(scheduler_pipe_state_write, mbarrier_addr, shared_storage.clc_response);\n      }\n\n      ++scheduler_pipe_state_write;\n    }\n\n    // Consumers\n    if (is_consumer) {\n      int linearCLC = work_tile_info.N_idx * gridDim.x + work_tile_info.M_idx;\n      // Atomically increment the worker count for the linearCLC by 1.\n      if (lane_predicate) {\n        atomicAdd(&d_workerCount[linearCLC], 1);\n      }\n    }\n\n    // Union of all consumers. Note that the producer here is its own consumer.\n    if (is_producer || is_consumer) {\n      scheduler_pipeline.consumer_wait(scheduler_pipe_state);\n      uint32_t smem_addr = cute::cast_smem_ptr_to_uint(&shared_storage.clc_response[scheduler_pipe_state.index()]);\n      work_tile_info = Scheduler::work_tile_info_from_clc_response(smem_addr);\n      scheduler_pipeline.consumer_release(scheduler_pipe_state);\n      ++scheduler_pipe_state;\n\n      // Add block offset since the scheduler works at cluster level. \n      dim3 block_id_in_cluster = cute::block_id_in_cluster();\n      work_tile_info.M_idx += block_id_in_cluster.x;\n      work_tile_info.N_idx += block_id_in_cluster.y;\n      work_tile_info.L_idx += block_id_in_cluster.z;\n\n    }\n  } while (work_tile_info.is_valid_tile);\n\n  // End of kernel\n  cute::cluster_sync();\n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t BlockSize = 128 * 2;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  bool check_results(int *h_workerCount, int size ) {\n    for (int i = 0 ; i< size; i++ ){\n      if ( h_workerCount[i] != 1 )\n      {\n        std::cout << \"linearCLC \" << i << \" has worker count \" << h_workerCount[i] << \"\\n\";\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(bool &success, dim3 grid_dim,\n                  cudaStream_t stream = 0 ) {\n\n    //\n    // Configure and launch\n    //\n    cudaError_t result;\n\n    int smem_size = 192 * 1024;  // 192kB to force 1CTA/SM\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n    // Launch a single Cluster, with BlockSize threads per CTA\n    dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n    dim3 dimGrid = grid_dim;\n    dim3 dimBlock(BlockSize,1,1);\n\n    result = cudaFuncSetAttribute(\n                  pipeline_device<\n                    decltype(cluster_shape),\n                    Stages>,\n                    cudaFuncAttributeMaxDynamicSharedMemorySize,\n                    smem_size\n                  );\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to set Shared Memory size.\" << std::endl;\n      return result;\n    }\n\n    int array_size = dimGrid.x * dimGrid.y;\n    int *d_workerCount, *h_workerCount;\n\n    /* Allocate memory. workerCount[i] counts the number of worker(s) which work\n       on linear t i.  The expectation is that workerCount[i] == 1 for all i.\n    */\n    h_workerCount = (int*)malloc(array_size * sizeof(int));\n\n    result = cudaMalloc(&d_workerCount, array_size * sizeof(int));\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to do cudaMalloc.\" << result << \"\\n\";\n      return result;\n    }\n\n    for(int i = 0 ; i < array_size; i++)\n    {\n      h_workerCount[i] = 0;  // Initialize workerCount[i] to 0 for all i.\n    }\n\n    result = cudaMemcpy(d_workerCount, h_workerCount, array_size * sizeof(int), cudaMemcpyHostToDevice);\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to do cudaMemcpy.\" << result << \"\\n\";\n      return result;\n    }\n\n    //  Extended launch API\n    const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n    void* kernel_params[] = {&d_workerCount};\n    cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    result = cudaDeviceSynchronize();\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = cudaMemcpy(h_workerCount, d_workerCount, array_size * sizeof(int), cudaMemcpyDeviceToHost);\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to do cudaMemcpy.\" << result << \"\\n\";\n      return result;\n    }\n\n    success = check_results(h_workerCount, array_size);\n\n    free(h_workerCount);\n\n    result = cudaFree(d_workerCount);\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to do cudaFree.\" << result << \"\\n\";\n      return result;\n    }\n\n    return cudaSuccess;\n  }\n};\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n//Cluster1x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x1 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x1_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster1x1 Stage3\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x1_Stage3) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 3;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster1x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x1 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x1_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineCLCFetchAsync class\n*/\n\n#include \"hip/hip_runtime.h\"\n#define KERNEL_DBG_TRACE false\n\n#include <cuda/atomic>\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp>\n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed_cluster_launch_control.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n#include \"cutlass/gemm/kernel/sm100_tile_scheduler.hpp\"\n\n\nusing namespace cute;\nusing namespace cutlass;\nusing namespace cutlass::gemm::kernel::detail;\n\n//////////////////// Shared Memory  /////////////////////////\n\ntemplate <uint32_t Stages, typename ClusterShape>\nstruct SharedStorage\n{\n  alignas(16) typename PersistentTileSchedulerSm100<ClusterShape, Stages>::CLCResponse clc_response[Stages];\n  alignas(16) typename PipelineCLCFetchAsync<Stages, ClusterShape>::SharedStorage storage;\n};\n\n//////////////////// Kernel /////////////////////////\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(256, 1)\n__global__ static\nvoid pipeline_device(int *d_workerCount)\n{\n  extern __shared__ char shared_memory[];\n\n  // single producer, multiple consumers\n  // producer: WG0\n  // consumer: WG1\n\n  using SharedStorage = SharedStorage<Stages, ClusterShape>;\n  using Scheduler = PersistentTileSchedulerSm100<ClusterShape, Stages>;\n  using TileSchedulingPipeline = PipelineCLCFetchAsync<Stages, ClusterShape>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Logistics\n  int warp_idx = canonical_warp_idx();\n  auto cluster_shape = ClusterShape{};\n\n  typename TileSchedulingPipeline::Params params;\n  params.transaction_bytes = 16;\n\n  constexpr int NUM_PRODUCER = 32;\n  constexpr int NUM_CONSUMERS_PER_CTA = 32;\n  params.consumer_arv_count = NUM_PRODUCER + NUM_CONSUMERS_PER_CTA * cute::size<0>(cluster_shape) * cute::size<1>(cluster_shape);\n  params.producer_arv_count = 1;\n  // Only the first CTA in the Cluster is producing.\n  params.producer_blockid = 0;\n\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n  // mbarrier.init\n  TileSchedulingPipeline scheduler_pipeline(shared_storage.storage, params );\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();\n  cute::cluster_wait();\n\n  uint32_t is_first_block_in_cluster = block_id_in_cluster.x == 0 && block_id_in_cluster.y == 0;\n  int lane_predicate = cute::elect_one_sync();\n\n  uint32_t is_producer = (is_first_block_in_cluster && warp_idx == 0);\n  uint32_t is_consumer = (warp_idx == 4);\n\n  PipelineState<Stages> scheduler_pipe_state;\n  PipelineState<Stages> scheduler_pipe_state_write = cutlass::make_producer_start_state<TileSchedulingPipeline>();\n  typename Scheduler::WorkTileInfo work_tile_info = {\n    static_cast<int32_t>(blockIdx.x),\n    static_cast<int32_t>(blockIdx.y),\n    static_cast<int32_t>(blockIdx.z),\n    false\n  };\n\n  // Persistent loop\n  do {\n    // Producer\n    if (is_producer) {\n      // Only 1 thread of the entire cluster issues the query.\n      uint32_t mbarrier_addr = scheduler_pipeline.producer_get_barrier(scheduler_pipe_state_write);\n\n      // Wait for clcID buffer to become empty with a flipped phase\n      scheduler_pipeline.producer_acquire(scheduler_pipe_state_write);\n\n      if (cute::elect_one_sync()) {\n        Scheduler::issue_clc_query(scheduler_pipe_state_write, mbarrier_addr, shared_storage.clc_response);\n      }\n\n      ++scheduler_pipe_state_write;\n    }\n\n    // Consumers\n    if (is_consumer) {\n      int linearCLC = work_tile_info.N_idx * gridDim.x + work_tile_info.M_idx;\n      // Atomically increment the worker count for the linearCLC by 1.\n      if (lane_predicate) {\n        atomicAdd(&d_workerCount[linearCLC], 1);\n      }\n    }\n\n    // Union of all consumers. Note that the producer here is its own consumer.\n    if (is_producer || is_consumer) {\n      scheduler_pipeline.consumer_wait(scheduler_pipe_state);\n      uint32_t smem_addr = cute::cast_smem_ptr_to_uint(&shared_storage.clc_response[scheduler_pipe_state.index()]);\n      work_tile_info = Scheduler::work_tile_info_from_clc_response(smem_addr);\n      scheduler_pipeline.consumer_release(scheduler_pipe_state);\n      ++scheduler_pipe_state;\n\n      // Add block offset since the scheduler works at cluster level. \n      dim3 block_id_in_cluster = cute::block_id_in_cluster();\n      work_tile_info.M_idx += block_id_in_cluster.x;\n      work_tile_info.N_idx += block_id_in_cluster.y;\n      work_tile_info.L_idx += block_id_in_cluster.z;\n\n    }\n  } while (work_tile_info.is_valid_tile);\n\n  // End of kernel\n  cute::cluster_sync();\n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t BlockSize = 128 * 2;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  bool check_results(int *h_workerCount, int size ) {\n    for (int i = 0 ; i< size; i++ ){\n      if ( h_workerCount[i] != 1 )\n      {\n        std::cout << \"linearCLC \" << i << \" has worker count \" << h_workerCount[i] << \"\\n\";\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // Run CuTe GEMM kernel\n  hipError_t run(bool &success, dim3 grid_dim,\n                  hipStream_t stream = 0 ) {\n\n    //\n    // Configure and launch\n    //\n    hipError_t result;\n\n    int smem_size = 192 * 1024;  // 192kB to force 1CTA/SM\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n    // Launch a single Cluster, with BlockSize threads per CTA\n    dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);\n    dim3 dimGrid = grid_dim;\n    dim3 dimBlock(BlockSize,1,1);\n\n    result = hipFuncSetAttribute(reinterpret_cast<const void*>(pipeline_device<\n                    decltype(cluster_shape),\n                    Stages>),\n                    hipFuncAttributeMaxDynamicSharedMemorySize,\n                    smem_size\n                  );\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to set Shared Memory size.\" << std::endl;\n      return result;\n    }\n\n    int array_size = dimGrid.x * dimGrid.y;\n    int *d_workerCount, *h_workerCount;\n\n    /* Allocate memory. workerCount[i] counts the number of worker(s) which work\n       on linear t i.  The expectation is that workerCount[i] == 1 for all i.\n    */\n    h_workerCount = (int*)malloc(array_size * sizeof(int));\n\n    result = hipMalloc(&d_workerCount, array_size * sizeof(int));\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to do hipMalloc.\" << result << \"\\n\";\n      return result;\n    }\n\n    for(int i = 0 ; i < array_size; i++)\n    {\n      h_workerCount[i] = 0;  // Initialize workerCount[i] to 0 for all i.\n    }\n\n    result = hipMemcpy(d_workerCount, h_workerCount, array_size * sizeof(int), hipMemcpyHostToDevice);\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to do hipMemcpy.\" << result << \"\\n\";\n      return result;\n    }\n\n    //  Extended launch API\n    const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n    void* kernel_params[] = {&d_workerCount};\n    cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    result = hipDeviceSynchronize();\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = hipMemcpy(h_workerCount, d_workerCount, array_size * sizeof(int), hipMemcpyDeviceToHost);\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to do hipMemcpy.\" << result << \"\\n\";\n      return result;\n    }\n\n    success = check_results(h_workerCount, array_size);\n\n    free(h_workerCount);\n\n    result = hipFree(d_workerCount);\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to do hipFree.\" << result << \"\\n\";\n      return result;\n    }\n\n    return hipSuccess;\n  }\n};\n\n#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED)\n//Cluster1x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x1 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x1_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster1x1 Stage3\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x1_Stage3) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 3;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster1x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster1x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x1 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x1_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster2x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster2x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x2 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x2_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\n//Cluster4x4 Stage4\nTEST(SM100_Verify_PipelineClusterLaunchControlAsync_WS, Cluster4x4_Stage4) {\n  OptionsClusterLaunch options;\n  options.grid_dim = {32,32,1};\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 4;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  TestbedClusterLaunch<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/pipeline_cluster_launch_control_async_warp_specialized_blackwell.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class as it would be used in a Warp specialized loop\n*/\n\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n\n\nusing namespace cute;\nusing namespace cutlass;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage storage ;\n};\n\nstruct KernelParams\n{\n  uint32_t num_iterations;\n  int* data_ptr;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(384, 1)\n__global__ static\nvoid pipeline_device(KernelParams const kernel_params)\n{\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;  \n  using PipelineState = typename cutlass::PipelineState<Stages>;  \n\n  using SharedStorage = SharedStorage<Stages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{};            // (m,n) -> cta_id\n  int warp_group_idx = __shfl_sync(0xffffffff, threadIdx.x / 128, 0);\n  int warp_idx_in_warpgroup = __shfl_sync(0xffffffff, (threadIdx.x / 32) % 4, 0);\n  int warp_group_thread_idx = threadIdx.x % 128;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1 \n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = static_cast<uint32_t>(sizeof(uint32_t) * NumProducers);\n  uint32_t const per_cta_bytes = sizeof(uint32_t);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params params;\n  params.transaction_bytes = TmaTransactionBytes;\n  if (warp_group_idx == 0) {\n    params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  else {\n    params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  params.is_leader = warp_group_thread_idx == 0;\n  params.num_consumers = 128;\n\n  MainloopPipeline pipeline(shared_storage.storage, params, cluster_shape);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n\n  // Producer WarpGroup\n  if (warp_group_idx == 0) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n\n    int lane_predicate = cute::elect_one_sync();\n    if (warp_idx_in_warpgroup == 0 && lane_predicate) {\n\n      int tma_k_prologue = min(Stages, kernel_params.num_iterations);\n\n      // Simulating Prologue TMA Loads\n      // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n      // i.e., we know that the buffer is indeed empty\n      PipelineState smem_pipe_write = make_producer_start_state<MainloopPipeline>();\n      CUTLASS_PRAGMA_UNROLL\n      for(int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(smem_pipe_write);\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n        ++smem_pipe_write;\n      }\n      int tma_k_iter = kernel_params.num_iterations - tma_k_prologue;\n\n      // Simulating Mainloop TMA Loads\n      CUTE_NO_UNROLL\n      for ( ; tma_k_iter > 0; --tma_k_iter) {\n\n        pipeline.producer_acquire(smem_pipe_write);\n\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n\n        // Advance write stage\n        ++smem_pipe_write;\n      }\n\n      // Tail Loop\n      // Handles the case where we never enter the mainloop\n      PipelineState tail = tma_k_prologue == Stages ? smem_pipe_write : PipelineState{};\n      for ( int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(tail);\n        ++tail;\n      }\n    }\n  // Consumer WarpGroup\n  } else if(warp_group_idx == 1) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n\n    PipelineState smem_pipe_read;\n    PipelineState smem_pipe_release;\n\n    // simulates accumulators + extra reg. pressure\n    int arr[168];\n\n    // Init Shared Memory read stages & PhaseBit\n    static constexpr uint32_t K_PIPE_MMAS = 1;\n    static_assert( K_PIPE_MMAS < Stages, \"ERROR : Too many MMAs in flight\");\n\n    // Total number of gemm iterations\n    auto gemm_k_iterations  = kernel_params.num_iterations;\n\n    // Simulating Prologue MMAs\n    int mma_k_prologue = min(K_PIPE_MMAS, gemm_k_iterations);\n    CUTLASS_PRAGMA_UNROLL\n    for (int iter = 0; iter < mma_k_prologue; ++iter) {\n      pipeline.consumer_wait(smem_pipe_read);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      ++smem_pipe_read;\n    }\n    gemm_k_iterations -= mma_k_prologue;\n\n    // Simulating Mainloop MMAs\n    CUTLASS_PRAGMA_NO_UNROLL\n    for ( ; gemm_k_iterations > 0; --gemm_k_iterations) {\n\n      /// Wait on the smem_pipe_read stage / phase\n      pipeline.consumer_wait(smem_pipe_read);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      // Dummy op - which will never happen\n      // But simulates high register usage.\n      CUTE_UNROLL\n      for(int i = 0; i < 168; ++i){\n        if (threadIdx.x > 256){\n          arr[i] += kernel_params.data_ptr[i];\n        }\n      }\n\n      pipeline.consumer_release(smem_pipe_release);\n\n      // Advance stages\n      ++smem_pipe_read;\n      ++smem_pipe_release;\n    }\n\n    // Dummy op - which will never happen\n    CUTE_UNROLL\n    for(int i = 0; i < 168; ++i){\n      if (threadIdx.x > 256){\n        kernel_params.data_ptr[i] = arr[i];\n      }\n    }\n\n    // Tail Loop\n    for (int i = 0; i < K_PIPE_MMAS; ++i){\n      pipeline.consumer_release(smem_pipe_release);\n      ++smem_pipe_release;\n    }\n\n  // Warp-Group #2\n  } else {\n    cutlass::arch::warpgroup_reg_dealloc<40>();\n  }\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128 * 3;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest(){};\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(uint32_t const kNumIters,\n                  cudaStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    [[maybe_unused]] auto num_stages = Int<Stages>{};\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    cudaEvent_t events[2];\n    cudaError_t result;\n\n    for (cudaEvent_t & event : events) {\n      result = cudaEventCreate(&event);\n      if (result != cudaSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = cudaEventRecord(events[0]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n    \n      using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = cudaFuncSetAttribute(\n        pipeline_device<decltype(cluster_shape), Stages>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with kBlockSize threads per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      KernelParams params{kNumIters, nullptr};\n      void* kernel_params[] = {reinterpret_cast<void*>(&params)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    }\n\n    result = cudaEventRecord(events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = cudaDeviceSynchronize();\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = cudaEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (cudaEvent_t & event : events) {\n      (void)cudaEventDestroy(event);\n    }\n\n    return cudaSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class as it would be used in a Warp specialized loop\n*/\n\n#include \"hip/hip_runtime.h\"\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n\n\nusing namespace cute;\nusing namespace cutlass;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage storage ;\n};\n\nstruct KernelParams\n{\n  uint32_t num_iterations;\n  int* data_ptr;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(384, 1)\n__global__ static\nvoid pipeline_device(KernelParams const kernel_params)\n{\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;  \n  using PipelineState = typename cutlass::PipelineState<Stages>;  \n\n  using SharedStorage = SharedStorage<Stages>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{};            // (m,n) -> cta_id\n  int warp_group_idx = __shfl_sync(0xffffffff, threadIdx.x / 128, 0);\n  int warp_idx_in_warpgroup = __shfl_sync(0xffffffff, (threadIdx.x / 32) % 4, 0);\n  int warp_group_thread_idx = threadIdx.x % 128;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1 \n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = static_cast<uint32_t>(sizeof(uint32_t) * NumProducers);\n  uint32_t const per_cta_bytes = sizeof(uint32_t);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params params;\n  params.transaction_bytes = TmaTransactionBytes;\n  if (warp_group_idx == 0) {\n    params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  else {\n    params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  params.is_leader = warp_group_thread_idx == 0;\n  params.num_consumers = 128;\n\n  MainloopPipeline pipeline(shared_storage.storage, params, cluster_shape);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n\n  // Producer WarpGroup\n  if (warp_group_idx == 0) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n\n    int lane_predicate = cute::elect_one_sync();\n    if (warp_idx_in_warpgroup == 0 && lane_predicate) {\n\n      int tma_k_prologue = min(Stages, kernel_params.num_iterations);\n\n      // Simulating Prologue TMA Loads\n      // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n      // i.e., we know that the buffer is indeed empty\n      PipelineState smem_pipe_write = make_producer_start_state<MainloopPipeline>();\n      CUTLASS_PRAGMA_UNROLL\n      for(int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(smem_pipe_write);\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n        ++smem_pipe_write;\n      }\n      int tma_k_iter = kernel_params.num_iterations - tma_k_prologue;\n\n      // Simulating Mainloop TMA Loads\n      CUTE_NO_UNROLL\n      for ( ; tma_k_iter > 0; --tma_k_iter) {\n\n        pipeline.producer_acquire(smem_pipe_write);\n\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(smem_pipe_write, per_cta_bytes);\n\n        // Advance write stage\n        ++smem_pipe_write;\n      }\n\n      // Tail Loop\n      // Handles the case where we never enter the mainloop\n      PipelineState tail = tma_k_prologue == Stages ? smem_pipe_write : PipelineState{};\n      for ( int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(tail);\n        ++tail;\n      }\n    }\n  // Consumer WarpGroup\n  } else if(warp_group_idx == 1) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n\n    PipelineState smem_pipe_read;\n    PipelineState smem_pipe_release;\n\n    // simulates accumulators + extra reg. pressure\n    int arr[168];\n\n    // Init Shared Memory read stages & PhaseBit\n    static constexpr uint32_t K_PIPE_MMAS = 1;\n    static_assert( K_PIPE_MMAS < Stages, \"ERROR : Too many MMAs in flight\");\n\n    // Total number of gemm iterations\n    auto gemm_k_iterations  = kernel_params.num_iterations;\n\n    // Simulating Prologue MMAs\n    int mma_k_prologue = min(K_PIPE_MMAS, gemm_k_iterations);\n    CUTLASS_PRAGMA_UNROLL\n    for (int iter = 0; iter < mma_k_prologue; ++iter) {\n      pipeline.consumer_wait(smem_pipe_read);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      ++smem_pipe_read;\n    }\n    gemm_k_iterations -= mma_k_prologue;\n\n    // Simulating Mainloop MMAs\n    CUTLASS_PRAGMA_NO_UNROLL\n    for ( ; gemm_k_iterations > 0; --gemm_k_iterations) {\n\n      /// Wait on the smem_pipe_read stage / phase\n      pipeline.consumer_wait(smem_pipe_read);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      // Dummy op - which will never happen\n      // But simulates high register usage.\n      CUTE_UNROLL\n      for(int i = 0; i < 168; ++i){\n        if (threadIdx.x > 256){\n          arr[i] += kernel_params.data_ptr[i];\n        }\n      }\n\n      pipeline.consumer_release(smem_pipe_release);\n\n      // Advance stages\n      ++smem_pipe_read;\n      ++smem_pipe_release;\n    }\n\n    // Dummy op - which will never happen\n    CUTE_UNROLL\n    for(int i = 0; i < 168; ++i){\n      if (threadIdx.x > 256){\n        kernel_params.data_ptr[i] = arr[i];\n      }\n    }\n\n    // Tail Loop\n    for (int i = 0; i < K_PIPE_MMAS; ++i){\n      pipeline.consumer_release(smem_pipe_release);\n      ++smem_pipe_release;\n    }\n\n  // Warp-Group #2\n  } else {\n    cutlass::arch::warpgroup_reg_dealloc<40>();\n  }\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128 * 3;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Ctor\n  PipelineTest(){};\n\n  // Run CuTe GEMM kernel\n  hipError_t run(uint32_t const kNumIters,\n                  hipStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    [[maybe_unused]] auto num_stages = Int<Stages>{};\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    hipEvent_t events[2];\n    hipError_t result;\n\n    for (hipEvent_t & event : events) {\n      result = hipEventCreate(&event);\n      if (result != hipSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = hipEventRecord(events[0]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n    \n      using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n\n      int smem_size = int(sizeof(SharedStorage<Stages>));\n\n      result = hipFuncSetAttribute(reinterpret_cast<const void*>(pipeline_device<decltype(cluster_shape), Stages>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with kBlockSize threads per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      KernelParams params{kNumIters, nullptr};\n      void* kernel_params[] = {reinterpret_cast<void*>(&params)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    }\n\n    result = hipEventRecord(events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = hipDeviceSynchronize();\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = hipEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (hipEvent_t & event : events) {\n      (void)hipEventDestroy(event);\n    }\n\n    return hipSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/pipeline_tma_async_warp_specialized.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class used in a WarpSpecialized Persistent loop\n*/\n\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n\n\nusing namespace cute;\nusing namespace cutlass;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages, typename PingPongBarrier>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage pipeline_storage;\n  typename PingPongBarrier::SharedStorage pingpong_storage;\n};\n\ntemplate <typename ClusterShape, uint32_t Stages>\nstruct CollectiveSimulation {\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n  using PipelineState = typename cutlass::PipelineState<Stages>;\n\n  CUTLASS_DEVICE\n  static void\n  dma_wg_simulation(MainloopPipeline pipeline, PipelineState tile_start_state_pipe,\n                    uint32_t const num_iterations) {\n    uint32_t const per_cta_bytes = sizeof(uint32_t);\n    int warp_idx_in_warpgroup = __shfl_sync(0xffffffff, (threadIdx.x / 32) % 4, 0);\n    int lane_predicate = cute::elect_one_sync();\n    if (warp_idx_in_warpgroup==0 && lane_predicate) {\n\n      int tma_k_prologue = min(Stages, num_iterations);\n\n      // Simulating Prologue TMA Loads\n      CUTLASS_PRAGMA_UNROLL\n      for(int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(tile_start_state_pipe);\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(tile_start_state_pipe, per_cta_bytes);\n        ++tile_start_state_pipe;\n      }\n      int tma_k_iter = num_iterations - tma_k_prologue;\n\n      PipelineState wr_pipe = tile_start_state_pipe;\n      // Simulating Mainloop TMA Loads\n      CUTE_NO_UNROLL\n      for ( ; tma_k_iter > 0; --tma_k_iter){\n\n        pipeline.producer_acquire(wr_pipe);\n\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(wr_pipe, per_cta_bytes);\n\n        // Advance write stage\n        ++wr_pipe;\n      }\n    }\n  }\n\n  CUTLASS_DEVICE\n  static void\n  math_wg_simulation(MainloopPipeline pipeline, PipelineState tile_start_state_pipe,\n                    uint32_t const num_iterations, int* data_ptr) {\n    PipelineState rd_pipe = tile_start_state_pipe;\n    PipelineState release_pipe = rd_pipe;\n\n    // simulates accumulators + extra reg. pressure\n    int arr[168];\n\n    // Init Shared Memory read stages & PhaseBit\n    static constexpr uint32_t K_PIPE_MMAS = 1;\n    static_assert( K_PIPE_MMAS < Stages, \"ERROR : Too many MMAs in flight\");\n\n    // Total number of gemm iterations\n    auto gemm_k_iterations  = num_iterations;\n\n    // Simulating Prologue MMAs\n    int mma_k_prologue = min(K_PIPE_MMAS, gemm_k_iterations);\n    CUTLASS_PRAGMA_UNROLL\n    for (int iter = 0; iter < mma_k_prologue; ++iter) {\n      pipeline.consumer_wait(rd_pipe);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      ++rd_pipe;\n    }\n    gemm_k_iterations -= mma_k_prologue;\n\n    // Simulating Mainloop MMAs\n    CUTLASS_PRAGMA_NO_UNROLL\n    for ( ; gemm_k_iterations > 0; --gemm_k_iterations) {\n\n      /// Wait on the rd_pipe stage / phase\n      pipeline.consumer_wait(rd_pipe);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      // Dummy op - which will never happen\n      // But simulates high register usage.\n      CUTE_UNROLL\n      for(int i = 0; i < 168; ++i){\n        if (threadIdx.x > 384){\n          arr[i] += data_ptr[i];\n        }\n      }\n\n      pipeline.consumer_release(release_pipe);\n\n      // Advance stages\n      ++rd_pipe;\n      ++release_pipe;\n    }\n\n    // Dummy op - which will never happen\n    CUTE_UNROLL\n    for(int i = 0; i < 168; ++i){\n      if (threadIdx.x > 384){\n        data_ptr[i] = arr[i];\n      }\n    }\n\n    // Tail Loop\n    for (int i = 0; i < K_PIPE_MMAS; ++i){\n      pipeline.consumer_release(release_pipe);\n      ++release_pipe;\n    }\n\n  }\n};\n\nstruct KernelParams\n{\n  uint32_t num_iterations;\n  int tiles_per_cluster;\n  int* data_ptr;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(384, 1)\n__global__ static\nvoid pipeline_device(KernelParams params)\n{\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n  using PipelineState = typename cutlass::PipelineState<Stages>;\n\n  /* One for Mainloop and one for Epilogue */\n  constexpr int StagesPerMathWarpGroup = 2;\n  constexpr int MathWarpGroupCountPersistent = 2;\n  using PingPongBarrier = typename cutlass::OrderedSequenceBarrier<StagesPerMathWarpGroup, MathWarpGroupCountPersistent>;\n\n  using SharedStorage = SharedStorage<Stages, PingPongBarrier>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{};            // (m,n) -> cta_id\n  int warp_group_idx = __shfl_sync(0xffffffff, threadIdx.x / NumThreadsPerWarpGroup, 0);\n  int warp_group_thread_idx = threadIdx.x % NumThreadsPerWarpGroup;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1 \n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = static_cast<uint32_t>(sizeof(uint32_t) * NumProducers);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params pipeline_params;\n  pipeline_params.transaction_bytes = TmaTransactionBytes;\n  if (warp_group_idx == 0) {\n    pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  else {\n    pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  pipeline_params.is_leader = warp_group_thread_idx == 0;\n  pipeline_params.num_consumers = NumThreadsPerWarpGroup;\n\n  MainloopPipeline pipeline(shared_storage.pipeline_storage, pipeline_params, cluster_shape);\n  PipelineState tile_start_state_pipe;\n\n  int tiles_per_cluster = params.tiles_per_cluster;\n\n  /* Offset pipeline start state for Math WG 2 */\n  if (warp_group_idx == 2) {\n    // Update pipeline state for next persistent tile\n    tile_start_state_pipe.advance(params.num_iterations);\n    tiles_per_cluster--;\n  }\n\n  typename PingPongBarrier::Params pingpong_params;\n  pingpong_params.group_id = warp_group_idx - 1; // Since DMA Warp Group Idx 0 will not participate\n  pingpong_params.group_size = NumThreadsPerWarpGroup; // Number of threads / participants in a group\n  PingPongBarrier math_wg_barrier(shared_storage.pingpong_storage, pingpong_params);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n  // Producer/DMA WarpGroup\n  if (warp_group_idx == 0) {\n    cutlass::arch::warpgroup_reg_dealloc<40>();\n    // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n    // i.e., we know that the buffer is indeed empty\n    PipelineState tile_prologue_state_pipe = make_producer_start_state<MainloopPipeline>();\n    while (tiles_per_cluster > 0) {\n      CollectiveSimulation<ClusterShape,Stages>::dma_wg_simulation(pipeline, tile_prologue_state_pipe, params.num_iterations);\n      // Update pipeline state for next persistent tile\n      tile_prologue_state_pipe.advance(params.num_iterations);\n      tiles_per_cluster--;\n    }\n  }\n  // Math WarpGropups\n  if(warp_group_idx == 1 || warp_group_idx == 2) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n    while (tiles_per_cluster > 0) {\n      // MMA\n      math_wg_barrier.wait();\n      CollectiveSimulation<ClusterShape,Stages>::math_wg_simulation(pipeline, tile_start_state_pipe, params.num_iterations, params.data_ptr);\n      math_wg_barrier.arrive();\n      // Epilogue\n      math_wg_barrier.wait();\n      // Simulates long running stage\n      #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n      __nanosleep(100000);\n      #endif\n      math_wg_barrier.arrive();\n      // Update pipeline state for next persistent tile\n      tile_start_state_pipe.advance(params.num_iterations * 2);\n      tiles_per_cluster -= 2;\n    }\n  }\n\n  // Makes sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128 * 3;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(uint32_t const kNumIters,\n                  cudaStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    cudaEvent_t events[2];\n    cudaError_t result;\n\n    for (cudaEvent_t & event : events) {\n      result = cudaEventCreate(&event);\n      if (result != cudaSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = cudaEventRecord(events[0]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n      constexpr int StagesPerMathWarpGroup = 2;\n      constexpr int MathWarpGroupCountPersistent = 2;\n      int smem_size = int(sizeof(SharedStorage<Stages,\n                          typename cutlass::OrderedSequenceBarrier<StagesPerMathWarpGroup, MathWarpGroupCountPersistent>>));\n\n      result = cudaFuncSetAttribute(\n        pipeline_device<decltype(cluster_shape), Stages>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with kBlockSize threads per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      int tiles_per_cluster = (kNumIters % 10) + 1;\n      printf(\"Persistent version: Tiles per Cluster = %d\\n\", tiles_per_cluster);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      KernelParams params{kNumIters, tiles_per_cluster, nullptr};\n      void *kernel_params[] = {&params};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    }\n\n    result = cudaEventRecord(events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = cudaDeviceSynchronize();\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = cudaEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (cudaEvent_t & event : events) {\n      (void)cudaEventDestroy(event);\n    }\n\n    return cudaSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the PipelineTmaAsync class used in a WarpSpecialized Persistent loop\n*/\n\n#include \"hip/hip_runtime.h\"\n#define KERNEL_DBG_TRACE false\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cutlass/arch/reg_reconfig.h\"\n\n\nusing namespace cute;\nusing namespace cutlass;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate <uint32_t Stages, typename PingPongBarrier>\nstruct SharedStorage\n{\n  typename cutlass::PipelineTmaAsync<Stages>::SharedStorage pipeline_storage;\n  typename PingPongBarrier::SharedStorage pingpong_storage;\n};\n\ntemplate <typename ClusterShape, uint32_t Stages>\nstruct CollectiveSimulation {\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n  using PipelineState = typename cutlass::PipelineState<Stages>;\n\n  CUTLASS_DEVICE\n  static void\n  dma_wg_simulation(MainloopPipeline pipeline, PipelineState tile_start_state_pipe,\n                    uint32_t const num_iterations) {\n    uint32_t const per_cta_bytes = sizeof(uint32_t);\n    int warp_idx_in_warpgroup = __shfl_sync(0xffffffff, (threadIdx.x / 32) % 4, 0);\n    int lane_predicate = cute::elect_one_sync();\n    if (warp_idx_in_warpgroup==0 && lane_predicate) {\n\n      int tma_k_prologue = min(Stages, num_iterations);\n\n      // Simulating Prologue TMA Loads\n      CUTLASS_PRAGMA_UNROLL\n      for(int i = 0; i < tma_k_prologue; ++i) {\n        pipeline.producer_acquire(tile_start_state_pipe);\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(tile_start_state_pipe, per_cta_bytes);\n        ++tile_start_state_pipe;\n      }\n      int tma_k_iter = num_iterations - tma_k_prologue;\n\n      PipelineState wr_pipe = tile_start_state_pipe;\n      // Simulating Mainloop TMA Loads\n      CUTE_NO_UNROLL\n      for ( ; tma_k_iter > 0; --tma_k_iter){\n\n        pipeline.producer_acquire(wr_pipe);\n\n        // Simulating cp.async.bulk.tensor behavior\n        pipeline.producer_commit(wr_pipe, per_cta_bytes);\n\n        // Advance write stage\n        ++wr_pipe;\n      }\n    }\n  }\n\n  CUTLASS_DEVICE\n  static void\n  math_wg_simulation(MainloopPipeline pipeline, PipelineState tile_start_state_pipe,\n                    uint32_t const num_iterations, int* data_ptr) {\n    PipelineState rd_pipe = tile_start_state_pipe;\n    PipelineState release_pipe = rd_pipe;\n\n    // simulates accumulators + extra reg. pressure\n    int arr[168];\n\n    // Init Shared Memory read stages & PhaseBit\n    static constexpr uint32_t K_PIPE_MMAS = 1;\n    static_assert( K_PIPE_MMAS < Stages, \"ERROR : Too many MMAs in flight\");\n\n    // Total number of gemm iterations\n    auto gemm_k_iterations  = num_iterations;\n\n    // Simulating Prologue MMAs\n    int mma_k_prologue = min(K_PIPE_MMAS, gemm_k_iterations);\n    CUTLASS_PRAGMA_UNROLL\n    for (int iter = 0; iter < mma_k_prologue; ++iter) {\n      pipeline.consumer_wait(rd_pipe);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      ++rd_pipe;\n    }\n    gemm_k_iterations -= mma_k_prologue;\n\n    // Simulating Mainloop MMAs\n    CUTLASS_PRAGMA_NO_UNROLL\n    for ( ; gemm_k_iterations > 0; --gemm_k_iterations) {\n\n      /// Wait on the rd_pipe stage / phase\n      pipeline.consumer_wait(rd_pipe);\n\n      warpgroup_arrive();\n      // GMMA would typically happen here\n\n      // Dummy op - which will never happen\n      // But simulates high register usage.\n      CUTE_UNROLL\n      for(int i = 0; i < 168; ++i){\n        if (threadIdx.x > 384){\n          arr[i] += data_ptr[i];\n        }\n      }\n\n      pipeline.consumer_release(release_pipe);\n\n      // Advance stages\n      ++rd_pipe;\n      ++release_pipe;\n    }\n\n    // Dummy op - which will never happen\n    CUTE_UNROLL\n    for(int i = 0; i < 168; ++i){\n      if (threadIdx.x > 384){\n        data_ptr[i] = arr[i];\n      }\n    }\n\n    // Tail Loop\n    for (int i = 0; i < K_PIPE_MMAS; ++i){\n      pipeline.consumer_release(release_pipe);\n      ++release_pipe;\n    }\n\n  }\n};\n\nstruct KernelParams\n{\n  uint32_t num_iterations;\n  int tiles_per_cluster;\n  int* data_ptr;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate <typename ClusterShape, uint32_t Stages>\n__launch_bounds__(384, 1)\n__global__ static\nvoid pipeline_device(KernelParams params)\n{\n  extern __shared__ char shared_memory[];\n  using MainloopPipeline = typename cutlass::PipelineTmaAsync<Stages>;\n  using PipelineState = typename cutlass::PipelineState<Stages>;\n\n  /* One for Mainloop and one for Epilogue */\n  constexpr int StagesPerMathWarpGroup = 2;\n  constexpr int MathWarpGroupCountPersistent = 2;\n  using PingPongBarrier = typename cutlass::OrderedSequenceBarrier<StagesPerMathWarpGroup, MathWarpGroupCountPersistent>;\n\n  using SharedStorage = SharedStorage<Stages, PingPongBarrier>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  [[maybe_unused]] auto cta_layout = Layout<ClusterShape>{};            // (m,n) -> cta_id\n  int warp_group_idx = __shfl_sync(0xffffffff, threadIdx.x / NumThreadsPerWarpGroup, 0);\n  int warp_group_thread_idx = threadIdx.x % NumThreadsPerWarpGroup;\n  dim3 block_id_in_cluster = cute::block_id_in_cluster();\n\n  auto cluster_shape = ClusterShape{};\n\n  // #Producers = #RowsInCluster + #ColsInCluster - 1 \n  uint32_t const NumProducers = cute::size<0>(cluster_shape) + cute::size<1>(cluster_shape) - 1;\n  uint32_t const TmaTransactionBytes = static_cast<uint32_t>(sizeof(uint32_t) * NumProducers);\n\n  // mbarrier.init\n  typename MainloopPipeline::Params pipeline_params;\n  pipeline_params.transaction_bytes = TmaTransactionBytes;\n  if (warp_group_idx == 0) {\n    pipeline_params.role = MainloopPipeline::ThreadCategory::Producer;\n  }\n  else {\n    pipeline_params.role = MainloopPipeline::ThreadCategory::Consumer;\n  }\n  pipeline_params.is_leader = warp_group_thread_idx == 0;\n  pipeline_params.num_consumers = NumThreadsPerWarpGroup;\n\n  MainloopPipeline pipeline(shared_storage.pipeline_storage, pipeline_params, cluster_shape);\n  PipelineState tile_start_state_pipe;\n\n  int tiles_per_cluster = params.tiles_per_cluster;\n\n  /* Offset pipeline start state for Math WG 2 */\n  if (warp_group_idx == 2) {\n    // Update pipeline state for next persistent tile\n    tile_start_state_pipe.advance(params.num_iterations);\n    tiles_per_cluster--;\n  }\n\n  typename PingPongBarrier::Params pingpong_params;\n  pingpong_params.group_id = warp_group_idx - 1; // Since DMA Warp Group Idx 0 will not participate\n  pingpong_params.group_size = NumThreadsPerWarpGroup; // Number of threads / participants in a group\n  PingPongBarrier math_wg_barrier(shared_storage.pingpong_storage, pingpong_params);\n\n  __syncthreads();\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n  // Producer/DMA WarpGroup\n  if (warp_group_idx == 0) {\n    cutlass::arch::warpgroup_reg_dealloc<40>();\n    // For the DMA (prologue) - we start with an opposite phase - since we skip all waits\n    // i.e., we know that the buffer is indeed empty\n    PipelineState tile_prologue_state_pipe = make_producer_start_state<MainloopPipeline>();\n    while (tiles_per_cluster > 0) {\n      CollectiveSimulation<ClusterShape,Stages>::dma_wg_simulation(pipeline, tile_prologue_state_pipe, params.num_iterations);\n      // Update pipeline state for next persistent tile\n      tile_prologue_state_pipe.advance(params.num_iterations);\n      tiles_per_cluster--;\n    }\n  }\n  // Math WarpGropups\n  if(warp_group_idx == 1 || warp_group_idx == 2) {\n    cutlass::arch::warpgroup_reg_alloc<232>();\n    while (tiles_per_cluster > 0) {\n      // MMA\n      math_wg_barrier.wait();\n      CollectiveSimulation<ClusterShape,Stages>::math_wg_simulation(pipeline, tile_start_state_pipe, params.num_iterations, params.data_ptr);\n      math_wg_barrier.arrive();\n      // Epilogue\n      math_wg_barrier.wait();\n      // Simulates long running stage\n      #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n      __nanosleep(100000);\n      #endif\n      math_wg_barrier.arrive();\n      // Update pipeline state for next persistent tile\n      tile_start_state_pipe.advance(params.num_iterations * 2);\n      tiles_per_cluster -= 2;\n    }\n  }\n\n  // Makes sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n}\n/////////////////////////////////////////////////////\n\n/// Device NT GMMA + TMA specialized\ntemplate<uint32_t Stages_, typename ClusterShape_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t kBlockSize = 128 * 3;\n  using ClusterShape = ClusterShape_;\n\n  //\n  // Methods\n  //\n\n  // Run CuTe GEMM kernel\n  hipError_t run(uint32_t const kNumIters,\n                  hipStream_t stream = 0) {\n\n    float elapsed_ms = 0.0f;\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<Int<ClusterShape::kM>, Int<ClusterShape::kN>, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    hipEvent_t events[2];\n    hipError_t result;\n\n    for (hipEvent_t & event : events) {\n      result = hipEventCreate(&event);\n      if (result != hipSuccess) {\n        std::cerr << \"Error: Failed to create event.\";\n        return result;\n      }\n    }\n\n    result = hipEventRecord(events[0]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record start event.\";\n      return result;\n    }\n\n    for (int iter = 0; iter < iterations; ++iter) {\n      constexpr int StagesPerMathWarpGroup = 2;\n      constexpr int MathWarpGroupCountPersistent = 2;\n      int smem_size = int(sizeof(SharedStorage<Stages,\n                          typename cutlass::OrderedSequenceBarrier<StagesPerMathWarpGroup, MathWarpGroupCountPersistent>>));\n\n      result = hipFuncSetAttribute(reinterpret_cast<const void*>(pipeline_device<decltype(cluster_shape), Stages>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with kBlockSize threads per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(kBlockSize,1,1);\n\n      int tiles_per_cluster = (kNumIters % 10) + 1;\n      printf(\"Persistent version: Tiles per Cluster = %d\\n\", tiles_per_cluster);\n\n      const void* kernel = (const void*)pipeline_device<decltype(cluster_shape), Stages>;\n      KernelParams params{kNumIters, tiles_per_cluster, nullptr};\n      void *kernel_params[] = {&params};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n\n    }\n\n    result = hipEventRecord(events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: Failed to record stop event.\";\n      return result;\n    }\n\n    result = hipDeviceSynchronize();\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    result = hipEventElapsedTime(&elapsed_ms, events[0], events[1]);\n\n    if (result != hipSuccess) {\n      std::cerr << \"Failed to create event.\";\n      return result;\n    }\n\n    for (hipEvent_t & event : events) {\n      (void)hipEventDestroy(event);\n    }\n\n    return hipSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x1_Stage10) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 1, 1>;\n  static constexpr uint32_t Stages = 10;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage5) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 5;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x1_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x1_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 1, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster1x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<1, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x4_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster2x4_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<2, 4, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x2_Stage2) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_PipelineTmaAsync_WS_Persistent, Cluster4x2_Stage7) {\n  Options options;\n  using ClusterShape = cutlass::gemm::GemmShape<4, 2, 1>;\n  static constexpr uint32_t Stages = 7;\n  using Test = PipelineTest<Stages, ClusterShape>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/pipeline_tma_async_warp_specialized_persistent.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the OrderedSequenceBarrier class\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate<typename OrderedSequencer>\nstruct SharedStorage\n{\n  typename OrderedSequencer::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate<int Stages, int GroupCount, int ThreadsPerGroup>\n__global__ static\nvoid ordered_sequence_device(uint32_t const num_iterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using SequenceBarrier = typename cutlass::OrderedSequenceBarrier<Stages, GroupCount>;\n  using SmemStorage = SharedStorage<SequenceBarrier>;\n\n  SmemStorage& shared_storage = *reinterpret_cast<SmemStorage*>(shared_memory);\n\n  int group_idx = threadIdx.x / ThreadsPerGroup;\n\n  typename SequenceBarrier::Params params;\n  params.group_id = group_idx;              // sequence ID\n  params.group_size = ThreadsPerGroup;      // Number of threads / participants in a group\n\n  SequenceBarrier barrier(shared_storage.storage, params);\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  __syncthreads();\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < num_iterations; ++i){\n\n    barrier.wait();\n    // STAGE 1 CODE...\n    #ifndef NDEBUG\n    int thread_idx_in_group = threadIdx.x % ThreadsPerGroup;\n    if (thread_idx_in_group == 0) {\n      printf(\"STAGE 0 : Group_IDX : %d, id = %d, iter = %d, tidx = %d\\n\", group_idx, params.group_id, i, threadIdx.x);\n    }\n    #endif\n    // Simulates long running stage\n    #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100000);\n    #endif\n    barrier.arrive();\n\n    barrier.wait();\n    // STAGE 2 CODE...\n    #ifndef NDEBUG\n    if (thread_idx_in_group == 0) {\n      printf(\"STAGE 1 : Group_IDX : %d, id = %d, iter = %d, tidx = %d\\n\", group_idx, params.group_id, i, threadIdx.x);\n    }\n    #endif\n    // Simulates long running stage\n    #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100000);\n    #endif\n    barrier.arrive();\n  }\n\n  // To make sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive();  \n  cute::cluster_wait();  \n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, uint32_t GroupCount_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t ThreadsPerGroup = 128;\n  static constexpr uint32_t BlockSize = GroupCount_ * ThreadsPerGroup;\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t GroupCount = GroupCount_;\n  using SequenceBarrier = typename cutlass::OrderedSequenceBarrier<Stages, GroupCount>;\n  using SmemStorage = SharedStorage<SequenceBarrier>;\n\n  //\n  // Methods\n  //\n\n  // Run CuTe GEMM kernel\n  cudaError_t run(uint32_t const kNumIters,\n                  cudaStream_t stream = nullptr) {\n\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<_1, _1, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    cudaError_t result;\n\n    for (int iter = 0; iter < iterations; ++iter) {\n\n      int smem_size = int(sizeof(SmemStorage));\n\n      result = cudaFuncSetAttribute(\n        ordered_sequence_device<Stages, GroupCount, ThreadsPerGroup>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(BlockSize,1,1);\n\n      const void* kernel = (const void*)ordered_sequence_device<Stages, GroupCount, ThreadsPerGroup>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    } // profiling loop ends\n\n    result = cudaDeviceSynchronize();\n\n    if (result != cudaSuccess) {\n      std::cerr << \"Error: cudaDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    return cudaSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_2) {\n  Options options;\n  static constexpr uint32_t GroupCount = 2;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_3) {\n  Options options;\n  static constexpr uint32_t GroupCount = 3;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_4) {\n  Options options;\n  static constexpr uint32_t GroupCount = 4;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_5) {\n  Options options;\n  static constexpr uint32_t GroupCount = 5;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the OrderedSequenceBarrier class\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/cluster_sm90.hpp> \n\n#include <cutlass/util/reference/host/gemm.h>\n#include <cutlass/cluster_launch.hpp>\n\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/util/print_error.hpp\"\n#include \"cutlass/util/GPU_Clock.hpp\"\n\n#include \"testbed.h\"\n#include \"cutlass/pipeline/pipeline.hpp\"\n#include \"cutlass/arch/barrier.h\"\n#include \"cute/arch/cluster_sm90.hpp\"\n\nusing namespace cute;\n\n//////////////////// KERNEL /////////////////////////\n\ntemplate<typename OrderedSequencer>\nstruct SharedStorage\n{\n  typename OrderedSequencer::SharedStorage storage;\n};\n\n// Goal of this kernel is to complete deadlock-free\ntemplate<int Stages, int GroupCount, int ThreadsPerGroup>\n__global__ static\nvoid ordered_sequence_device(uint32_t const num_iterations)\n{\n\n  extern __shared__ char shared_memory[];\n  using SequenceBarrier = typename cutlass::OrderedSequenceBarrier<Stages, GroupCount>;\n  using SmemStorage = SharedStorage<SequenceBarrier>;\n\n  SmemStorage& shared_storage = *reinterpret_cast<SmemStorage*>(shared_memory);\n\n  int group_idx = threadIdx.x / ThreadsPerGroup;\n\n  typename SequenceBarrier::Params params;\n  params.group_id = group_idx;              // sequence ID\n  params.group_size = ThreadsPerGroup;      // Number of threads / participants in a group\n\n  SequenceBarrier barrier(shared_storage.storage, params);\n\n  // Ensure All CTAs in Cluster have completed init before issuing commits\n  __syncthreads();\n  cute::cluster_arrive_relaxed();  \n  cute::cluster_wait();\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < num_iterations; ++i){\n\n    barrier.wait();\n    // STAGE 1 CODE...\n    #ifndef NDEBUG\n    int thread_idx_in_group = threadIdx.x % ThreadsPerGroup;\n    if (thread_idx_in_group == 0) {\n      printf(\"STAGE 0 : Group_IDX : %d, id = %d, iter = %d, tidx = %d\\n\", group_idx, params.group_id, i, threadIdx.x);\n    }\n    #endif\n    // Simulates long running stage\n    #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100000);\n    #endif\n    barrier.arrive();\n\n    barrier.wait();\n    // STAGE 2 CODE...\n    #ifndef NDEBUG\n    if (thread_idx_in_group == 0) {\n      printf(\"STAGE 1 : Group_IDX : %d, id = %d, iter = %d, tidx = %d\\n\", group_idx, params.group_id, i, threadIdx.x);\n    }\n    #endif\n    // Simulates long running stage\n    #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)\n    __nanosleep(100000);\n    #endif\n    barrier.arrive();\n  }\n\n  // To make sure remote SMEM doesn't get destroyed\n  cute::cluster_arrive();  \n  cute::cluster_wait();  \n}\n/////////////////////////////////////////////////////\n\ntemplate<uint32_t Stages_, uint32_t GroupCount_>\nstruct PipelineTest {\n\n  //\n  // Data members\n  //\n  static constexpr uint32_t ThreadsPerGroup = 128;\n  static constexpr uint32_t BlockSize = GroupCount_ * ThreadsPerGroup;\n  static constexpr uint32_t Stages = Stages_;\n  static constexpr uint32_t GroupCount = GroupCount_;\n  using SequenceBarrier = typename cutlass::OrderedSequenceBarrier<Stages, GroupCount>;\n  using SmemStorage = SharedStorage<SequenceBarrier>;\n\n  //\n  // Methods\n  //\n\n  // Run CuTe GEMM kernel\n  hipError_t run(uint32_t const kNumIters,\n                  hipStream_t stream = nullptr) {\n\n    // Pipeline (multistage pipeline)\n    auto cluster_shape = Shape<_1, _1, _1>{};\n\n    //\n    // Configure and launch\n    //\n    int iterations = 1;\n    hipError_t result;\n\n    for (int iter = 0; iter < iterations; ++iter) {\n\n      int smem_size = int(sizeof(SmemStorage));\n\n      result = hipFuncSetAttribute(reinterpret_cast<const void*>(ordered_sequence_device<Stages, GroupCount, ThreadsPerGroup>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        smem_size);\n\n      // Launch a single Cluster, with 128 thread per CTA\n      dim3 dimCluster(size<0>(cluster_shape), size<1>(cluster_shape), size<2>(cluster_shape));    \n      dim3 dimGrid(size<0>(cluster_shape), size<1>(cluster_shape), 1);    \n      dim3 dimBlock(BlockSize,1,1);\n\n      const void* kernel = (const void*)ordered_sequence_device<Stages, GroupCount, ThreadsPerGroup>;\n      int iters = kNumIters;\n      void* kernel_params[] = {reinterpret_cast<void*>(&iters)};\n      cutlass::ClusterLauncher::launch(dimGrid, dimCluster, dimBlock, smem_size, stream, kernel, kernel_params);\n  \n    } // profiling loop ends\n\n    result = hipDeviceSynchronize();\n\n    if (result != hipSuccess) {\n      std::cerr << \"Error: hipDeviceSynchronize() failed\" << std::endl;\n      return result;\n    }\n\n    return hipSuccess;\n  }\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_2) {\n  Options options;\n  static constexpr uint32_t GroupCount = 2;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_3) {\n  Options options;\n  static constexpr uint32_t GroupCount = 3;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_4) {\n  Options options;\n  static constexpr uint32_t GroupCount = 4;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n\nTEST(SM90_Verify_OrderedSequence, Depth_2_Length_5) {\n  Options options;\n  static constexpr uint32_t GroupCount = 5;\n  static constexpr uint32_t Stages = 2;\n  using Test = PipelineTest<Stages, GroupCount>;\n  Testbed<Test> testbed(options);\n  EXPECT_TRUE(testbed.verification());\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/pipeline/sequence_barrier.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/util/device_memory.h\"\n#pragma warning( disable : 4800)\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\n\n/// Each thread clears its array and writes to global memory. No PRMT instructions should\n/// be generated if Array<T, N> is a multiple of 32 bits.\ntemplate <typename T, int N>\n__global__ void test_array_clear(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  storage.clear();\n\n  ptr[threadIdx.x] = storage;\n}\n\n/// Each thread writes its thread index into the elements of its array and then writes the result\n/// to global memory.\ntemplate <typename T, int N>\n__global__ void test_array_threadid(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < N; ++i) {\n    storage.at(i) = T(int(threadIdx.x));\n  }\n\n  ptr[threadIdx.x] = storage;\n}\n\n/// Each thread writes its thread index into the elements of its array and then writes the result\n/// to global memory.\ntemplate <typename T, int N>\n__global__ void test_array_sequence(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < N; ++i) {\n    storage.at(i) = T(i);\n  }\n\n  ptr[threadIdx.x] = storage;\n}\n\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int N>\nclass TestArray {\npublic:\n\n  //\n  // Data members\n  //\n\n  /// Number of threads\n  int const kThreads = 32;\n\n  typedef cutlass::Array<T, N> ArrayTy;\n\n\n  //\n  // Methods\n  //\n\n  /// Ctor\n  TestArray() {\n\n  }\n\n  /// Runs the test\n  void run() {\n\n    /// Device memory containing output\n    cutlass::device_memory::allocation< ArrayTy > output(static_cast<size_t>(kThreads));\n    std::vector< ArrayTy > output_host(static_cast<size_t>(kThreads));\n\n    dim3 grid(1,1);\n    dim3 block(kThreads, 1, 1);\n\n    test::core::test_array_clear<<< grid, block >>>(output.get());\n\n    cudaError_t result = cudaDeviceSynchronize();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    //\n    // Verify contains all zeros\n    //\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = cudaGetLastError();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    char const *ptr_host = reinterpret_cast<char const *>(output_host.data());\n    for (size_t i = 0; i < sizeof(ArrayTy) * kThreads; ++i) {\n      EXPECT_FALSE(ptr_host[i]);\n    }\n\n    //\n    // Verify each element contains the low bits of the thread Id\n    //\n\n    test::core::test_array_threadid<<< grid, block >>>(output.get());\n\n    result = cudaDeviceSynchronize();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = cudaGetLastError();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    for (int i = 0; i < kThreads; ++i) {\n      T tid = T(i);\n\n      ArrayTy thread = output_host.at(i);\n\n      // Element-wise access\n      for (int j = 0; j < N; ++j) {\n        EXPECT_TRUE(tid == thread[j]);\n      }\n\n      // Iterator access\n      for (auto it = thread.begin(); it != thread.end(); ++it) {\n        EXPECT_TRUE(tid == *it);\n      }\n\n      // Range-based for\n      for (auto const & x : thread) {\n        EXPECT_TRUE(tid == x);\n      }\n    }\n\n    //\n    // Verify each element\n    //\n\n    test::core::test_array_sequence<<< grid, block >>>(output.get());\n\n    result = cudaDeviceSynchronize();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = cudaGetLastError();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    for (int i = 0; i < kThreads; ++i) {\n\n      ArrayTy thread = output_host.at(i);\n\n      // Element-wise access\n      for (int j = 0; j < N; ++j) {\n        T got = T(j);\n        EXPECT_TRUE(got == thread[j]);\n      }\n\n      // Iterator access\n      int j = 0;\n      for (auto it = thread.begin(); it != thread.end(); ++it, ++j) {\n        T got = T(j);\n        EXPECT_TRUE(got == *it);\n      }\n\n      // Range-based for\n      j = 0;\n      for (auto const & x : thread) {\n        T got = T(j);\n        EXPECT_TRUE(got == x);\n        ++j;\n      }\n    }\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(Array, Int8x16) {\n  TestArray<int8_t, 16>().run();\n}\n\nTEST(Array, Int32x4) {\n  TestArray<int, 4>().run();\n}\n\n#if __CUDA_ARCH__ >= 520\nTEST(Array, Float16x8) {\n  TestArray<cutlass::half_t, 8>().run();\n}\n#endif\n\nTEST(Array, FloatBF16x8) {\n  TestArray<cutlass::bfloat16_t, 8>().run();\n}\n\nTEST(Array, FloatTF32x4) {\n  TestArray<cutlass::tfloat32_t, 4>().run();\n}\n\nTEST(Array, Float32x4) {\n  TestArray<float, 4>().run();\n}\n\nTEST(Array, Int4x32) {\n  TestArray<cutlass::int4b_t, 32>().run();\n}\n\nTEST(Array, Uint4x32) {\n  TestArray<cutlass::uint4b_t, 32>().run();\n}\n\nTEST(Array, Bin1x128) {\n  TestArray<cutlass::bin1_t, 128>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/util/device_memory.h\"\n#pragma warning( disable : 4800)\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\n\n/// Each thread clears its array and writes to global memory. No PRMT instructions should\n/// be generated if Array<T, N> is a multiple of 32 bits.\ntemplate <typename T, int N>\n__global__ void test_array_clear(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  storage.clear();\n\n  ptr[threadIdx.x] = storage;\n}\n\n/// Each thread writes its thread index into the elements of its array and then writes the result\n/// to global memory.\ntemplate <typename T, int N>\n__global__ void test_array_threadid(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < N; ++i) {\n    storage.at(i) = T(int(threadIdx.x));\n  }\n\n  ptr[threadIdx.x] = storage;\n}\n\n/// Each thread writes its thread index into the elements of its array and then writes the result\n/// to global memory.\ntemplate <typename T, int N>\n__global__ void test_array_sequence(cutlass::Array<T, N> *ptr) {\n\n  cutlass::Array<T, N> storage;\n\n  CUTLASS_PRAGMA_UNROLL\n  for (int i = 0; i < N; ++i) {\n    storage.at(i) = T(i);\n  }\n\n  ptr[threadIdx.x] = storage;\n}\n\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int N>\nclass TestArray {\npublic:\n\n  //\n  // Data members\n  //\n\n  /// Number of threads\n  int const kThreads = 32;\n\n  typedef cutlass::Array<T, N> ArrayTy;\n\n\n  //\n  // Methods\n  //\n\n  /// Ctor\n  TestArray() {\n\n  }\n\n  /// Runs the test\n  void run() {\n\n    /// Device memory containing output\n    cutlass::device_memory::allocation< ArrayTy > output(static_cast<size_t>(kThreads));\n    std::vector< ArrayTy > output_host(static_cast<size_t>(kThreads));\n\n    dim3 grid(1,1);\n    dim3 block(kThreads, 1, 1);\n\n    test::core::test_array_clear<<< grid, block >>>(output.get());\n\n    hipError_t result = hipDeviceSynchronize();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    //\n    // Verify contains all zeros\n    //\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = hipGetLastError();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    char const *ptr_host = reinterpret_cast<char const *>(output_host.data());\n    for (size_t i = 0; i < sizeof(ArrayTy) * kThreads; ++i) {\n      EXPECT_FALSE(ptr_host[i]);\n    }\n\n    //\n    // Verify each element contains the low bits of the thread Id\n    //\n\n    test::core::test_array_threadid<<< grid, block >>>(output.get());\n\n    result = hipDeviceSynchronize();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = hipGetLastError();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    for (int i = 0; i < kThreads; ++i) {\n      T tid = T(i);\n\n      ArrayTy thread = output_host.at(i);\n\n      // Element-wise access\n      for (int j = 0; j < N; ++j) {\n        EXPECT_TRUE(tid == thread[j]);\n      }\n\n      // Iterator access\n      for (auto it = thread.begin(); it != thread.end(); ++it) {\n        EXPECT_TRUE(tid == *it);\n      }\n\n      // Range-based for\n      for (auto const & x : thread) {\n        EXPECT_TRUE(tid == x);\n      }\n    }\n\n    //\n    // Verify each element\n    //\n\n    test::core::test_array_sequence<<< grid, block >>>(output.get());\n\n    result = hipDeviceSynchronize();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    cutlass::device_memory::copy_to_host(output_host.data(), output.get(), kThreads);\n\n    result = hipGetLastError();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    for (int i = 0; i < kThreads; ++i) {\n\n      ArrayTy thread = output_host.at(i);\n\n      // Element-wise access\n      for (int j = 0; j < N; ++j) {\n        T got = T(j);\n        EXPECT_TRUE(got == thread[j]);\n      }\n\n      // Iterator access\n      int j = 0;\n      for (auto it = thread.begin(); it != thread.end(); ++it, ++j) {\n        T got = T(j);\n        EXPECT_TRUE(got == *it);\n      }\n\n      // Range-based for\n      j = 0;\n      for (auto const & x : thread) {\n        T got = T(j);\n        EXPECT_TRUE(got == x);\n        ++j;\n      }\n    }\n  }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(Array, Int8x16) {\n  TestArray<int8_t, 16>().run();\n}\n\nTEST(Array, Int32x4) {\n  TestArray<int, 4>().run();\n}\n\n#if __CUDA_ARCH__ >= 520\nTEST(Array, Float16x8) {\n  TestArray<cutlass::half_t, 8>().run();\n}\n#endif\n\nTEST(Array, FloatBF16x8) {\n  TestArray<cutlass::bfloat16_t, 8>().run();\n}\n\nTEST(Array, FloatTF32x4) {\n  TestArray<cutlass::tfloat32_t, 4>().run();\n}\n\nTEST(Array, Float32x4) {\n  TestArray<float, 4>().run();\n}\n\nTEST(Array, Int4x32) {\n  TestArray<cutlass::int4b_t, 32>().run();\n}\n\nTEST(Array, Uint4x32) {\n  TestArray<cutlass::uint4b_t, 32>().run();\n}\n\nTEST(Array, Bin1x128) {\n  TestArray<cutlass::bin1_t, 128>().run();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/core/array.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for conversion operators.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/numeric_conversion.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/// Simple conversion function\ntemplate <typename Destination, typename Source, int Count>\n__global__ void convert(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source) {\n\n  cutlass::FastNumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test_integer_range_limited() {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    source.host_view().at({0, i}) = Source(i % 4);\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    EXPECT_TRUE(float(destination.host_view().at({0, i})) == float(source.host_view().at({0, i})));\n  }\n}\n\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test_integer_range_all() {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n\n  int const kIntSourceMin = cutlass::platform::numeric_limits<Source>::lowest();\n  int const kIntSourceMax = cutlass::platform::numeric_limits<Source>::max();\n  int const kIntRange = kIntSourceMax - kIntSourceMin + 1;\n\n  for (int i = 0; i < kN; ++i) {\n    source.host_view().at({0, i}) = Source(kIntSourceMin + (i % kIntRange));\n\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  // Verify conversion\n  bool passed = true;\n\n  for (int i = 0; i < kN; ++i) {\n    if(!(float(destination.host_view().at({0, i})) == float(source.host_view().at({0, i})))) {\n      passed = false;\n      break;\n    }\n  }\n\n  EXPECT_TRUE(passed) << \" FastNumericArrayConverter failed\";\n\n   // Print out results for the failed conversion.\n   if (!passed) {\n    for (int i = 0; i < kN; ++i) {\n        std::cout << \"source(\" << float(source.host_view().at({0, i})) << \") -> \"\n                  << \"destination (\"<< float(destination.host_view().at({0, i})) << \")\" << std::endl;\n    }\n   }\n   std::flush(std::cout);\n}\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(FastNumericConversion, s32_to_f32) {\n  int const kN = 4;\n  using Source = int;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_limited<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_f32_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_f32_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_f16_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = cutlass::half_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_f16_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = cutlass::half_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_bf16_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = cutlass::bfloat16_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_bf16_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = cutlass::bfloat16_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s4_to_s8_array) {\n  int const kN = 16;\n  using Source = cutlass::int4b_t;\n  using Destination = int8_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for conversion operators.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/numeric_conversion.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/// Simple conversion function\ntemplate <typename Destination, typename Source, int Count>\n__global__ void convert(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source) {\n\n  cutlass::FastNumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test_integer_range_limited() {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    source.host_view().at({0, i}) = Source(i % 4);\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    EXPECT_TRUE(float(destination.host_view().at({0, i})) == float(source.host_view().at({0, i})));\n  }\n}\n\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test_integer_range_all() {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n\n  int const kIntSourceMin = cutlass::platform::numeric_limits<Source>::lowest();\n  int const kIntSourceMax = cutlass::platform::numeric_limits<Source>::max();\n  int const kIntRange = kIntSourceMax - kIntSourceMin + 1;\n\n  for (int i = 0; i < kN; ++i) {\n    source.host_view().at({0, i}) = Source(kIntSourceMin + (i % kIntRange));\n\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  // Verify conversion\n  bool passed = true;\n\n  for (int i = 0; i < kN; ++i) {\n    if(!(float(destination.host_view().at({0, i})) == float(source.host_view().at({0, i})))) {\n      passed = false;\n      break;\n    }\n  }\n\n  EXPECT_TRUE(passed) << \" FastNumericArrayConverter failed\";\n\n   // Print out results for the failed conversion.\n   if (!passed) {\n    for (int i = 0; i < kN; ++i) {\n        std::cout << \"source(\" << float(source.host_view().at({0, i})) << \") -> \"\n                  << \"destination (\"<< float(destination.host_view().at({0, i})) << \")\" << std::endl;\n    }\n   }\n   std::flush(std::cout);\n}\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nTEST(FastNumericConversion, s32_to_f32) {\n  int const kN = 4;\n  using Source = int;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_limited<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_f32_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_f32_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = float;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_f16_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = cutlass::half_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_f16_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = cutlass::half_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, u8_to_bf16_array) {\n  int const kN = 256;\n  using Source = uint8_t;\n  using Destination = cutlass::bfloat16_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s8_to_bf16_array) {\n  int const kN = 256;\n  using Source = int8_t;\n  using Destination = cutlass::bfloat16_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n\nTEST(FastNumericConversion, s4_to_s8_array) {\n  int const kN = 16;\n  using Source = cutlass::int4b_t;\n  using Destination = int8_t;\n  test::core::kernel::run_test_integer_range_all<Destination, Source, kN>();\n}\n", "source": "/kaggle/working/cutlass/test/unit/core/fast_numeric_conversion.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/layout/matrix.h\"\n\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__ void convert_bf16_f32(cutlass::bfloat16_t *output, float const *input, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid < N) {\n    output[tid] = static_cast<cutlass::bfloat16_t>(input[tid]);\n  }\n}\n\n__global__ void convert_and_pack_bf16(cutlass::bfloat16_t *output, float const *input, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid * 2 < N) {\n\n    cutlass::NumericArrayConverter<cutlass::bfloat16_t, float, 2> convert;\n\n    cutlass::Array<cutlass::bfloat16_t, 2> *dst_ptr = \n      reinterpret_cast<cutlass::Array<cutlass::bfloat16_t, 2> *>(output + tid * 2);\n\n    cutlass::Array<float, 2> const *src_ptr = \n      reinterpret_cast<cutlass::Array<float, 2> const *>(input + tid * 2);\n\n    *dst_ptr = convert(*src_ptr);\n  } \n}\n\nTEST(bfloat16_t, device_conversion) {\n  using T = cutlass::bfloat16_t;\n  using S = float;\n\n  int const N = 256;\n\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> destination({N, 1});\n  cutlass::HostTensor<S, cutlass::layout::RowMajor> source({N, 1});\n\n  for (int i = 0; i < N; ++i) {\n    source.at({i, 0}) = float(i - 128);\n    destination.at({i, 0}) = T(0);\n  }\n\n  source.sync_device();\n  destination.sync_device();\n\n  convert_bf16_f32<<< dim3(1,1), dim3(N, 1) >>>(destination.device_data(), source.device_data(), N);\n\n  ASSERT_EQ(cudaGetLastError(), cudaSuccess) << \"Kernel launch error.\";\n\n  destination.sync_host();\n\n  int errors = 0;\n  for (int i = 0; i < N; ++i) {\n    T got = destination.at({i, 0});\n    S expected = source.at({i, 0});\n\n    if (S(got) != expected) {\n      ++errors;\n      if (errors < 10) {\n        std::cerr << \"Basic conversion error - [\" << i << \"] - got \" << got << \", expected \" << expected << \"\\n\";\n      }\n    }\n\n    destination.at({i, 0}) = T(0);\n  }\n\n  destination.sync_device();\n  \n  convert_and_pack_bf16<<< dim3(1,1), dim3(N, 1) >>>(destination.device_data(), source.device_data(), N);\n  \n  ASSERT_EQ(cudaGetLastError(), cudaSuccess) << \"Kernel launch error.\";\n  \n  destination.sync_host();\n\n  for (int i = 0; i < N; ++i) {\n    T got = destination.at({i, 0});\n    S expected = source.at({i, 0});\n\n    if (S(got) != expected) {\n      ++errors;\n      if (errors < 10) {\n        std::cerr << \"Convert and pack error - [\" << i << \"] - got \" << got << \", expected \" << expected << \"\\n\";\n      }\n    }\n  }\n\n  EXPECT_EQ(errors, 0);\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(bfloat16_t, host_conversion) {\n  for (int i = -128; i < 128; ++i) {\n    float f = static_cast<float>(i);\n\n    cutlass::bfloat16_t x = static_cast<cutlass::bfloat16_t>(i);\n    cutlass::bfloat16_t y = static_cast<cutlass::bfloat16_t>(f);\n\n    EXPECT_TRUE(static_cast<int>(x) == i);\n    EXPECT_TRUE(static_cast<float>(y) == f);\n  }\n\n  // Try out default-ctor (zero initialization of primitive proxy type)\n  EXPECT_TRUE(cutlass::bfloat16_t() == 0.0_bf16);\n\n  // Try out user-defined literals\n  EXPECT_TRUE(cutlass::bfloat16_t(7) == 7_bf16);\n  EXPECT_TRUE(7 == static_cast<int>(7_bf16));\n}\n\nTEST(bfloat16_t, host_arithmetic) {\n\n  for (int i = -100; i < 100; ++i) {\n    for (int j = -100; j < 100; ++j) {\n\n      cutlass::bfloat16_t x = static_cast<cutlass::bfloat16_t>(i);\n      cutlass::bfloat16_t y = static_cast<cutlass::bfloat16_t>(j);\n\n      EXPECT_TRUE(static_cast<int>(x + y) == (i + j));\n    }\n  }\n}\n\nTEST(bfloat16_t, host_round) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint16_t expected;\n  } tests[] = {\n    {0x40040000, 0x4004},  // M=0, R=0, S=0 => rtz\n    {0x40048000, 0x4004},  // M=0, R=1, S=0 => rtz\n    {0x40040001, 0x4004},  // M=0, R=1, S=1 => +inf\n    {0x4004c000, 0x4005},  // M=0, R=1, S=1 => +inf\n    {0x4004a000, 0x4005},  // M=0, R=1, S=1 => +inf\n    {0x40050000, 0x4005},  // M=1, R=0, S=0 => rtz\n    {0x40054000, 0x4005},  // M=1, R=0, S=1 => rtz\n    {0x40058000, 0x4006},  // M=1, R=1, S=0 => +inf\n    {0x40058001, 0x4006},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f80},  // +inf\n    {0xff800000, 0xff80},  // -inf\n    {0x7fffffff, 0x7fff},  // canonical NaN\n    {0x7ff00001, 0x7fff},  // NaN -> canonical NaN\n    {0xfff00010, 0x7fff},  // Nan -> canonical NaN\n    {0, 0}\n  };\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::bfloat16_t bf16 = cutlass::bfloat16_t(f32);\n\n    bool passed = (tests[i].expected == bf16.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << bf16.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/core_io.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/layout/matrix.h\"\n\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__ void convert_bf16_f32(cutlass::bfloat16_t *output, float const *input, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid < N) {\n    output[tid] = static_cast<cutlass::bfloat16_t>(input[tid]);\n  }\n}\n\n__global__ void convert_and_pack_bf16(cutlass::bfloat16_t *output, float const *input, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid * 2 < N) {\n\n    cutlass::NumericArrayConverter<cutlass::bfloat16_t, float, 2> convert;\n\n    cutlass::Array<cutlass::bfloat16_t, 2> *dst_ptr = \n      reinterpret_cast<cutlass::Array<cutlass::bfloat16_t, 2> *>(output + tid * 2);\n\n    cutlass::Array<float, 2> const *src_ptr = \n      reinterpret_cast<cutlass::Array<float, 2> const *>(input + tid * 2);\n\n    *dst_ptr = convert(*src_ptr);\n  } \n}\n\nTEST(bfloat16_t, device_conversion) {\n  using T = cutlass::bfloat16_t;\n  using S = float;\n\n  int const N = 256;\n\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> destination({N, 1});\n  cutlass::HostTensor<S, cutlass::layout::RowMajor> source({N, 1});\n\n  for (int i = 0; i < N; ++i) {\n    source.at({i, 0}) = float(i - 128);\n    destination.at({i, 0}) = T(0);\n  }\n\n  source.sync_device();\n  destination.sync_device();\n\n  convert_bf16_f32<<< dim3(1,1), dim3(N, 1) >>>(destination.device_data(), source.device_data(), N);\n\n  ASSERT_EQ(hipGetLastError(), hipSuccess) << \"Kernel launch error.\";\n\n  destination.sync_host();\n\n  int errors = 0;\n  for (int i = 0; i < N; ++i) {\n    T got = destination.at({i, 0});\n    S expected = source.at({i, 0});\n\n    if (S(got) != expected) {\n      ++errors;\n      if (errors < 10) {\n        std::cerr << \"Basic conversion error - [\" << i << \"] - got \" << got << \", expected \" << expected << \"\\n\";\n      }\n    }\n\n    destination.at({i, 0}) = T(0);\n  }\n\n  destination.sync_device();\n  \n  convert_and_pack_bf16<<< dim3(1,1), dim3(N, 1) >>>(destination.device_data(), source.device_data(), N);\n  \n  ASSERT_EQ(hipGetLastError(), hipSuccess) << \"Kernel launch error.\";\n  \n  destination.sync_host();\n\n  for (int i = 0; i < N; ++i) {\n    T got = destination.at({i, 0});\n    S expected = source.at({i, 0});\n\n    if (S(got) != expected) {\n      ++errors;\n      if (errors < 10) {\n        std::cerr << \"Convert and pack error - [\" << i << \"] - got \" << got << \", expected \" << expected << \"\\n\";\n      }\n    }\n  }\n\n  EXPECT_EQ(errors, 0);\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(bfloat16_t, host_conversion) {\n  for (int i = -128; i < 128; ++i) {\n    float f = static_cast<float>(i);\n\n    cutlass::bfloat16_t x = static_cast<cutlass::bfloat16_t>(i);\n    cutlass::bfloat16_t y = static_cast<cutlass::bfloat16_t>(f);\n\n    EXPECT_TRUE(static_cast<int>(x) == i);\n    EXPECT_TRUE(static_cast<float>(y) == f);\n  }\n\n  // Try out default-ctor (zero initialization of primitive proxy type)\n  EXPECT_TRUE(cutlass::bfloat16_t() == 0.0_bf16);\n\n  // Try out user-defined literals\n  EXPECT_TRUE(cutlass::bfloat16_t(7) == 7_bf16);\n  EXPECT_TRUE(7 == static_cast<int>(7_bf16));\n}\n\nTEST(bfloat16_t, host_arithmetic) {\n\n  for (int i = -100; i < 100; ++i) {\n    for (int j = -100; j < 100; ++j) {\n\n      cutlass::bfloat16_t x = static_cast<cutlass::bfloat16_t>(i);\n      cutlass::bfloat16_t y = static_cast<cutlass::bfloat16_t>(j);\n\n      EXPECT_TRUE(static_cast<int>(x + y) == (i + j));\n    }\n  }\n}\n\nTEST(bfloat16_t, host_round) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint16_t expected;\n  } tests[] = {\n    {0x40040000, 0x4004},  // M=0, R=0, S=0 => rtz\n    {0x40048000, 0x4004},  // M=0, R=1, S=0 => rtz\n    {0x40040001, 0x4004},  // M=0, R=1, S=1 => +inf\n    {0x4004c000, 0x4005},  // M=0, R=1, S=1 => +inf\n    {0x4004a000, 0x4005},  // M=0, R=1, S=1 => +inf\n    {0x40050000, 0x4005},  // M=1, R=0, S=0 => rtz\n    {0x40054000, 0x4005},  // M=1, R=0, S=1 => rtz\n    {0x40058000, 0x4006},  // M=1, R=1, S=0 => +inf\n    {0x40058001, 0x4006},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f80},  // +inf\n    {0xff800000, 0xff80},  // -inf\n    {0x7fffffff, 0x7fff},  // canonical NaN\n    {0x7ff00001, 0x7fff},  // NaN -> canonical NaN\n    {0xfff00010, 0x7fff},  // Nan -> canonical NaN\n    {0, 0}\n  };\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::bfloat16_t bf16 = cutlass::bfloat16_t(f32);\n\n    bool passed = (tests[i].expected == bf16.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << bf16.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/core/bfloat16.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/util/device_memory.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(tfloat32_t, host_conversion) {\n  for (int i = -1024; i < 1024; ++i) {\n    float f = static_cast<float>(i);\n\n    cutlass::tfloat32_t x = static_cast<cutlass::tfloat32_t>(i);\n    cutlass::tfloat32_t y = static_cast<cutlass::tfloat32_t>(f);\n\n    EXPECT_TRUE(static_cast<int>(x) == i);\n    EXPECT_TRUE(static_cast<float>(y) == f);\n  }\n\n  // Try out default-ctor (zero initialization of primitive proxy type)\n  EXPECT_TRUE(cutlass::tfloat32_t() == 0.0_tf32);\n\n  // Try out user-defined literals\n  EXPECT_TRUE(cutlass::tfloat32_t(7) == 7_tf32);\n  EXPECT_TRUE(7 == static_cast<int>(7_tf32));\n}\n\nTEST(tfloat32_t, host_arithmetic) {\n\n  for (int i = -100; i < 100; ++i) {\n    for (int j = -100; j < 100; ++j) {\n\n      cutlass::tfloat32_t x = static_cast<cutlass::tfloat32_t>(i);\n      cutlass::tfloat32_t y = static_cast<cutlass::tfloat32_t>(j);\n\n      EXPECT_TRUE(static_cast<int>(x + y) == (i + j));\n    }\n  }\n}\n\nTEST(tfloat32_t, host_round_nearest) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint32_t expected;\n  } tests[] = {\n    {0x40000000, 0x40000000},  // M=0, R=0, S=0 => rtz\n    {0x40001000, 0x40000000},  // M=0, R=1, S=0 => rtz\n    {0x40000001, 0x40000000},  // M=0, R=0, S=1 => rtz\n    {0x40001001, 0x40002000},  // M=0, R=1, S=1 => +inf\n    {0x40002000, 0x40002000},  // M=1, R=0, S=0 => rtz\n    {0x40002001, 0x40002000},  // M=1, R=0, S=1 => rtz\n    {0x40003000, 0x40004000},  // M=1, R=1, S=0 => +inf\n    {0x40003001, 0x40004000},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f800000},  // +inf\n    {0xff800000, 0xff800000},  // -inf\n    {0x7fffffff, 0x7fffffff},  // canonical NaN to canonical NaN\n    {0x7f800001, 0x7fffffff},  // NaN to canonical NaN\n    {0xff800001, 0x7fffffff},  // NaN to canonical NaN\n    {0, 0}\n  };\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::NumericConverter<\n      cutlass::tfloat32_t, \n      float, \n      cutlass::FloatRoundStyle::round_to_nearest> converter;\n\n    cutlass::tfloat32_t tf32 = converter(f32);\n    \n    // note, we must explicitly truncate the low-order bits since they are not defined in TF32.\n    if (cutlass::isfinite(tf32)) {\n      tf32.storage &= 0xffffe000;\n    }\n\n    bool passed = (tests[i].expected == tf32.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << tf32.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\nnamespace test {\nnamespace core {\n\n__global__ void convert_tf32_half_ulp(cutlass::tfloat32_t *out, float const *in) {\n\n  cutlass::NumericConverter<\n    cutlass::tfloat32_t, \n    float,\n    cutlass::FloatRoundStyle::round_half_ulp_truncate> convert;\n\n  *out = convert(*in);\n}\n\n}\n}\n\n\nTEST(tfloat32_t, host_round_half_ulp) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint32_t expected;\n  } tests[] = {\n    {0x40001fff, 0x40002000},\n    {0x40000000, 0x40000000},  // M=0, R=0, S=0 => rtz\n    {0x40001000, 0x40002000},  // M=0, R=1, S=0 => rtz  - this difers from RNE\n    {0x40000001, 0x40000000},  // M=0, R=0, S=1 => rtz\n    {0x40001001, 0x40002000},  // M=0, R=1, S=1 => +inf\n    {0x40002000, 0x40002000},  // M=1, R=0, S=0 => rtz\n    {0x40002001, 0x40002000},  // M=1, R=0, S=1 => rtz\n    {0x40003000, 0x40004000},  // M=1, R=1, S=0 => +inf\n    {0x40003001, 0x40004000},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f800000},  // +inf\n    {0xff800000, 0xff800000},  // -inf\n    {0x7fffffff, 0x7fffffff},  // canonical NaN to canonical NaN\n    {0x7f800001, 0x7f800001},  // NaN to NaN\n    {0xff800001, 0xff800001},  // NaN to NaN\n    {0, 0}\n  };\n\n  cutlass::NumericConverter<\n    cutlass::tfloat32_t, \n    float,\n    cutlass::FloatRoundStyle::round_half_ulp_truncate> convert;\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::tfloat32_t tf32 = convert(f32);\n\n    // note, for this test, we must explicitly truncate the low-order bits since they are not \n    // defined in TF32.\n    if (cutlass::isfinite(tf32)) {\n      tf32.storage &= 0xffffe000;\n    }\n\n    bool passed = (tests[i].expected == tf32.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << tf32.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Statically sized array of elements that accommodates all CUTLASS-supported numeric types\n           and is safe to use in a union.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/util/device_memory.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(tfloat32_t, host_conversion) {\n  for (int i = -1024; i < 1024; ++i) {\n    float f = static_cast<float>(i);\n\n    cutlass::tfloat32_t x = static_cast<cutlass::tfloat32_t>(i);\n    cutlass::tfloat32_t y = static_cast<cutlass::tfloat32_t>(f);\n\n    EXPECT_TRUE(static_cast<int>(x) == i);\n    EXPECT_TRUE(static_cast<float>(y) == f);\n  }\n\n  // Try out default-ctor (zero initialization of primitive proxy type)\n  EXPECT_TRUE(cutlass::tfloat32_t() == 0.0_tf32);\n\n  // Try out user-defined literals\n  EXPECT_TRUE(cutlass::tfloat32_t(7) == 7_tf32);\n  EXPECT_TRUE(7 == static_cast<int>(7_tf32));\n}\n\nTEST(tfloat32_t, host_arithmetic) {\n\n  for (int i = -100; i < 100; ++i) {\n    for (int j = -100; j < 100; ++j) {\n\n      cutlass::tfloat32_t x = static_cast<cutlass::tfloat32_t>(i);\n      cutlass::tfloat32_t y = static_cast<cutlass::tfloat32_t>(j);\n\n      EXPECT_TRUE(static_cast<int>(x + y) == (i + j));\n    }\n  }\n}\n\nTEST(tfloat32_t, host_round_nearest) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint32_t expected;\n  } tests[] = {\n    {0x40000000, 0x40000000},  // M=0, R=0, S=0 => rtz\n    {0x40001000, 0x40000000},  // M=0, R=1, S=0 => rtz\n    {0x40000001, 0x40000000},  // M=0, R=0, S=1 => rtz\n    {0x40001001, 0x40002000},  // M=0, R=1, S=1 => +inf\n    {0x40002000, 0x40002000},  // M=1, R=0, S=0 => rtz\n    {0x40002001, 0x40002000},  // M=1, R=0, S=1 => rtz\n    {0x40003000, 0x40004000},  // M=1, R=1, S=0 => +inf\n    {0x40003001, 0x40004000},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f800000},  // +inf\n    {0xff800000, 0xff800000},  // -inf\n    {0x7fffffff, 0x7fffffff},  // canonical NaN to canonical NaN\n    {0x7f800001, 0x7fffffff},  // NaN to canonical NaN\n    {0xff800001, 0x7fffffff},  // NaN to canonical NaN\n    {0, 0}\n  };\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::NumericConverter<\n      cutlass::tfloat32_t, \n      float, \n      cutlass::FloatRoundStyle::round_to_nearest> converter;\n\n    cutlass::tfloat32_t tf32 = converter(f32);\n    \n    // note, we must explicitly truncate the low-order bits since they are not defined in TF32.\n    if (cutlass::isfinite(tf32)) {\n      tf32.storage &= 0xffffe000;\n    }\n\n    bool passed = (tests[i].expected == tf32.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << tf32.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\nnamespace test {\nnamespace core {\n\n__global__ void convert_tf32_half_ulp(cutlass::tfloat32_t *out, float const *in) {\n\n  cutlass::NumericConverter<\n    cutlass::tfloat32_t, \n    float,\n    cutlass::FloatRoundStyle::round_half_ulp_truncate> convert;\n\n  *out = convert(*in);\n}\n\n}\n}\n\n\nTEST(tfloat32_t, host_round_half_ulp) {\n  \n  struct {\n    uint32_t f32_bits;\n    uint32_t expected;\n  } tests[] = {\n    {0x40001fff, 0x40002000},\n    {0x40000000, 0x40000000},  // M=0, R=0, S=0 => rtz\n    {0x40001000, 0x40002000},  // M=0, R=1, S=0 => rtz  - this difers from RNE\n    {0x40000001, 0x40000000},  // M=0, R=0, S=1 => rtz\n    {0x40001001, 0x40002000},  // M=0, R=1, S=1 => +inf\n    {0x40002000, 0x40002000},  // M=1, R=0, S=0 => rtz\n    {0x40002001, 0x40002000},  // M=1, R=0, S=1 => rtz\n    {0x40003000, 0x40004000},  // M=1, R=1, S=0 => +inf\n    {0x40003001, 0x40004000},  // M=1, R=1, S=1 => +inf\n    {0x7f800000, 0x7f800000},  // +inf\n    {0xff800000, 0xff800000},  // -inf\n    {0x7fffffff, 0x7fffffff},  // canonical NaN to canonical NaN\n    {0x7f800001, 0x7f800001},  // NaN to NaN\n    {0xff800001, 0xff800001},  // NaN to NaN\n    {0, 0}\n  };\n\n  cutlass::NumericConverter<\n    cutlass::tfloat32_t, \n    float,\n    cutlass::FloatRoundStyle::round_half_ulp_truncate> convert;\n  \n  bool running = true;\n  for (int i = 0; running; ++i) {\n\n    float f32 = reinterpret_cast<float const &>(tests[i].f32_bits);\n\n    cutlass::tfloat32_t tf32 = convert(f32);\n\n    // note, for this test, we must explicitly truncate the low-order bits since they are not \n    // defined in TF32.\n    if (cutlass::isfinite(tf32)) {\n      tf32.storage &= 0xffffe000;\n    }\n\n    bool passed = (tests[i].expected == tf32.raw());\n    \n    EXPECT_TRUE(passed)\n      << \"Error - convert(f32: 0x\" << std::hex << tests[i].f32_bits \n      << \") -> 0x\" << std::hex << tests[i].expected << \"\\ngot: 0x\" << std::hex << tf32.raw();\n\n    if (!tests[i].f32_bits) {\n      running = false;\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/core/tfloat32.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for basic uint128 functionality\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(uint128_t, host_arithmetic) {\n  using T = cutlass::uint128_t;\n\n  // only low 64bit\n  for (uint64_t i = 0; i < 1024; ++i) {\n    for (uint64_t j = 0; j < 1024; ++j) {\n      T x = i;\n      T y = j;\n\n      EXPECT_TRUE(static_cast<uint64_t>(x + y) == (i + j));\n      EXPECT_TRUE(static_cast<uint64_t>(x * static_cast<uint64_t>(y)) == (i * j));\n\n      if (j != 0) {\n        EXPECT_TRUE(static_cast<uint64_t>(x / static_cast<uint64_t>(y)) == (i / j));\n      }\n    }\n  }\n\n  // carry overflow for low uint64_t \n  {\n    for (uint64_t i = 0; i < 1024; ++i) {\n      T x = static_cast<uint64_t>(0xFFFFFFFFFFFFFFFF);\n      T y = i + 1;\n\n      T z = x + y;\n\n      EXPECT_EQ(z.hilo_.hi, static_cast<uint64_t>(0x1));\n      EXPECT_EQ(z.hilo_.lo, i);\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__ void uint128_add_operator(cutlass::uint128_t *output, cutlass::uint128_t const *input, cutlass::uint128_t base, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid < N) {\n    output[tid] = input[tid] + base;\n  }\n}\n\nTEST(uint128_t, device_arithmetic) {\n  using T = cutlass::uint128_t;\n\n  int const N = 1024;\n\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> input({N, 1});\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> sum({N, 1});\n\n  for (int i = 0; i < N; ++i) {\n    input.at({i, 0}) = static_cast<uint64_t>(i + 1);\n  }\n\n  T b = static_cast<uint64_t>(0xFFFFFFFFFFFFFFFF);\n\n  input.sync_device();\n\n  uint128_add_operator<<< dim3(1,1), dim3(N, 1) >>>(sum.device_data(), input.device_data(), b, N);\n\n  ASSERT_EQ(cudaGetLastError(), cudaSuccess) << \"Kernel launch error.\";\n\n  sum.sync_host();\n\n  for (int i = 0; i < N; ++i) {\n    T got = sum.at({i, 0});\n    uint64_t expected_hi = static_cast<uint64_t>(0x1);\n    uint64_t expected_lo = static_cast<uint64_t>(i);\n\n    EXPECT_EQ(got.hilo_.hi, expected_hi);\n    EXPECT_EQ(got.hilo_.lo, expected_lo);\n  }\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Tests for basic uint128 functionality\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/array.h\"\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/numeric_types.h\"\n#include \"cutlass/numeric_conversion.h\"\n#include \"cutlass/util/device_memory.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Host\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(uint128_t, host_arithmetic) {\n  using T = cutlass::uint128_t;\n\n  // only low 64bit\n  for (uint64_t i = 0; i < 1024; ++i) {\n    for (uint64_t j = 0; j < 1024; ++j) {\n      T x = i;\n      T y = j;\n\n      EXPECT_TRUE(static_cast<uint64_t>(x + y) == (i + j));\n      EXPECT_TRUE(static_cast<uint64_t>(x * static_cast<uint64_t>(y)) == (i * j));\n\n      if (j != 0) {\n        EXPECT_TRUE(static_cast<uint64_t>(x / static_cast<uint64_t>(y)) == (i / j));\n      }\n    }\n  }\n\n  // carry overflow for low uint64_t \n  {\n    for (uint64_t i = 0; i < 1024; ++i) {\n      T x = static_cast<uint64_t>(0xFFFFFFFFFFFFFFFF);\n      T y = i + 1;\n\n      T z = x + y;\n\n      EXPECT_EQ(z.hilo_.hi, static_cast<uint64_t>(0x1));\n      EXPECT_EQ(z.hilo_.lo, i);\n    }\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//\n// Device\n//\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n__global__ void uint128_add_operator(cutlass::uint128_t *output, cutlass::uint128_t const *input, cutlass::uint128_t base, int N) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  if (tid < N) {\n    output[tid] = input[tid] + base;\n  }\n}\n\nTEST(uint128_t, device_arithmetic) {\n  using T = cutlass::uint128_t;\n\n  int const N = 1024;\n\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> input({N, 1});\n  cutlass::HostTensor<T, cutlass::layout::RowMajor> sum({N, 1});\n\n  for (int i = 0; i < N; ++i) {\n    input.at({i, 0}) = static_cast<uint64_t>(i + 1);\n  }\n\n  T b = static_cast<uint64_t>(0xFFFFFFFFFFFFFFFF);\n\n  input.sync_device();\n\n  uint128_add_operator<<< dim3(1,1), dim3(N, 1) >>>(sum.device_data(), input.device_data(), b, N);\n\n  ASSERT_EQ(hipGetLastError(), hipSuccess) << \"Kernel launch error.\";\n\n  sum.sync_host();\n\n  for (int i = 0; i < N; ++i) {\n    T got = sum.at({i, 0});\n    uint64_t expected_hi = static_cast<uint64_t>(0x1);\n    uint64_t expected_lo = static_cast<uint64_t>(i);\n\n    EXPECT_EQ(got.hilo_.hi, expected_hi);\n    EXPECT_EQ(got.hilo_.lo, expected_lo);\n  }\n}\n", "source": "/kaggle/working/cutlass/test/unit/core/uint128.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for functional operators.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/functional.h\"\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void unary_operator(Element *d, Element const *a) {\n\n  Operator op;\n\n  *d = op(*a);\n}\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void binary_operator(Element *d, Element const *a, Element const *b, int Iterations = 1) {\n\n  Operator op;\n\n  Element a_x = *a;\n  Element b_x = *b;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < Iterations; ++i) {\n    b_x = op(a_x, b_x);\n  }\n  \n  *d = b_x;\n}\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void trinary_operator(\n  Element *d, \n  Element const *a, \n  Element const *b, \n  Element const *c, \n  int Iterations = 1) {\n\n  Operator op;\n\n  Element a_x = a[blockIdx.x];\n  Element b_x = b[blockIdx.x];\n  Element c_x = c[blockIdx.x];\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < Iterations; ++i) {\n    c_x = op(a_x, b_x, c_x);\n  }\n  \n  d[blockIdx.x] = c_x;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_plus_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::plus<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a + b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, plus_f16x16) {\n  Functional_plus_f16xN<16>();\n}\n\nTEST(Functional, plus_f16x17) {\n  Functional_plus_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_minus_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::minus<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a - b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, minus_f16x16) {\n  Functional_minus_f16xN<16>();\n}\n\nTEST(Functional, minus_f16x17) {\n  Functional_minus_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_multiplies_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::multiplies<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a * b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, multiplies_f16x16) {\n\n  Functional_multiplies_f16xN<16>();\n}\n\nTEST(Functional, multiplies_f16x17) {\n\n  Functional_multiplies_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_divides_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::divides<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    float expected = a / b;\n\n    float const kThreshold = 0.0005f;\n\n    if (std::isnan(expected)) {\n      EXPECT_TRUE(std::isnan(d));\n    }\n    else if (std::isinf(expected)) {\n      EXPECT_TRUE(std::isinf(d));\n    }\n    else {\n      EXPECT_TRUE(std::abs(d - expected) < kThreshold)\n        << \"Got: \" << d << \" = \" << a << \" / \" << b << \", expected: \" << (a / b); \n    }\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, divides_f16x16) {\n\n  Functional_divides_f16xN<16>();\n}\n\nTEST(Functional, divides_f16x17) {\n\n  Functional_divides_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int kN>\nvoid Functional_multiply_add_TxN() {\n\n  using Element = cutlass::Array<T, kN>;\n  using Operator = cutlass::multiply_add<Element>;\n\n  using Tensor = cutlass::HostTensor<T, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = T((i * 2 + 1) % 5);\n    B.host_data()[i] = T((i * 4 + 8) % 7);\n    C.host_data()[i] = T((i * 3 + 11) % 11);\n    D.host_data()[i] = T(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n\n  test::core::kernel::trinary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data()),\n    reinterpret_cast<Element const *>(C.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float c = float(C.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a * b + c));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Functional, multiply_add_f16x16) {\n  Functional_multiply_add_TxN<cutlass::half_t, 16>();\n}\n\nTEST(Functional, multiply_add_f16x17) {\n  Functional_multiply_add_TxN<cutlass::half_t, 17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Functional, multiply_add_bf16x16) {\n  Functional_multiply_add_TxN<cutlass::bfloat16_t, 16>();\n}\n\nTEST(Functional, multiply_add_bf16x17) {\n  Functional_multiply_add_TxN<cutlass::bfloat16_t, 17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T>\ncutlass::Quaternion<T> random_quaternion(int range) {\n  return cutlass::Quaternion<T>{\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range)\n  };\n}\n\ntemplate <typename T>\nvoid Functional_multiply_add_QuaternionT() {\n\n  using Element = cutlass::Quaternion<T>;\n  using Operator = cutlass::multiply_add<Element, Element, Element>;\n  using HostTensor = cutlass::HostTensor<Element, cutlass::layout::RowMajor>;\n\n  int const kM = 128;\n  int const kRange = 8;\n\n  HostTensor A({kM, 1});\n  HostTensor B({kM, 1});\n  HostTensor C({kM, 1});\n  HostTensor D({kM, 1});\n\n  srand(2021);\n\n  for (int m = 0; m < kM; ++m) {\n    A.at({m, 0}) = random_quaternion<T>(kRange);\n    B.at({m, 0}) = random_quaternion<T>(kRange);\n    C.at({m, 0}) = random_quaternion<T>(kRange);\n  }\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  test::core::kernel::trinary_operator<Element, Operator><<< dim3(kM,1), dim3(1,1) >>>(\n    D.device_data(),\n    A.device_data(),\n    B.device_data(),\n    C.device_data()\n  );\n\n  D.sync_host();\n  \n  for (int m = 0; m < kM; ++m) {\n\n    Element a = A.at({m, 0});\n    Element b = B.at({m, 0});\n    Element c = C.at({m, 0});\n    Element got = D.at({m, 0});\n    Element expected = a * b + c;\n\n    EXPECT_TRUE(got == expected);\n  }\n}\n\nTEST(Functional, multiply_add_quaternion_f32) {\n  Functional_multiply_add_QuaternionT<float>();\n}\n\nnamespace cutlass_test {\n\n__global__ void\ntest_cutlass_maximum(cutlass::half_t const* in1, cutlass::half_t const* in2, cutlass::half_t* out)\n{\n  {\n  constexpr bool propagate_NaN = true;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0\n    && blockIdx.x == 0 && blockIdx.y == 0 && blockIdx.z == 0) {\n    *out = op(*in1, *in2);\n  }\n  }\n  constexpr bool propagate_NaN = false;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0\n    && blockIdx.x == 0 && blockIdx.y == 0 && blockIdx.z == 0) {\n    *out = op(*in1, *in2);\n  }\n}\n\n} // cutlass_test\n\n// Test compilation on both host and device.\nTEST(Functional, maximum_half_host_propagate_NaN) {\n  constexpr bool propagate_NaN = true;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  cutlass::half_t x(1.0f);\n  cutlass::half_t y(2.0f);\n\n  auto result = op(x, y);\n  static_assert(std::is_same_v<decltype(result), cutlass::half_t>);\n  EXPECT_EQ(result, y);\n  result = op(y, x);\n  EXPECT_EQ(result, y);\n}\n\nTEST(Functional, maximum_half_host_dont_propagate_NaN) {\n  constexpr bool propagate_NaN = false;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  cutlass::half_t x(1.0f);\n  cutlass::half_t y(2.0f);\n\n  auto result = op(x, y);\n  static_assert(std::is_same_v<decltype(result), cutlass::half_t>);\n  EXPECT_EQ(result, y);\n  result = op(y, x);\n  EXPECT_EQ(result, y);\n}\n\nTEST(FUnction, maximum_half_device) {\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor in1({1, 1});\n  Tensor in2({1, 1});\n  Tensor out({1, 1});\n  in1.host_data()[0] = cutlass::half_t(1.0f);\n  in2.host_data()[0] = cutlass::half_t(2.0f);\n  out.host_data()[0] = cutlass::half_t(0.0f);\n\n  in1.sync_device();\n  in2.sync_device();\n  out.sync_device();\n\n  cutlass_test::test_cutlass_maximum<<< 1, 1 >>>(\n    in1.device_data(),\n    in2.device_data(),\n    out.device_data()\n  );\n  out.sync_host();\n\n  EXPECT_EQ(out.host_data()[0], 2.0f);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for functional operators.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/functional.h\"\n#include \"cutlass/core_io.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void unary_operator(Element *d, Element const *a) {\n\n  Operator op;\n\n  *d = op(*a);\n}\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void binary_operator(Element *d, Element const *a, Element const *b, int Iterations = 1) {\n\n  Operator op;\n\n  Element a_x = *a;\n  Element b_x = *b;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < Iterations; ++i) {\n    b_x = op(a_x, b_x);\n  }\n  \n  *d = b_x;\n}\n\n/// Conversion template\ntemplate <typename Element, typename Operator>\n__global__ void trinary_operator(\n  Element *d, \n  Element const *a, \n  Element const *b, \n  Element const *c, \n  int Iterations = 1) {\n\n  Operator op;\n\n  Element a_x = a[blockIdx.x];\n  Element b_x = b[blockIdx.x];\n  Element c_x = c[blockIdx.x];\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int i = 0; i < Iterations; ++i) {\n    c_x = op(a_x, b_x, c_x);\n  }\n  \n  d[blockIdx.x] = c_x;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_plus_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::plus<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a + b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, plus_f16x16) {\n  Functional_plus_f16xN<16>();\n}\n\nTEST(Functional, plus_f16x17) {\n  Functional_plus_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_minus_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::minus<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a - b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, minus_f16x16) {\n  Functional_minus_f16xN<16>();\n}\n\nTEST(Functional, minus_f16x17) {\n  Functional_minus_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_multiplies_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::multiplies<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a * b));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, multiplies_f16x16) {\n\n  Functional_multiplies_f16xN<16>();\n}\n\nTEST(Functional, multiplies_f16x17) {\n\n  Functional_multiplies_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <int kN>\nvoid Functional_divides_f16xN() {\n\n  using Element = cutlass::Array<cutlass::half_t, kN>;\n  using Operator = cutlass::divides<Element>;\n\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = cutlass::half_t((i * 2 + 1) % 5);\n    B.host_data()[i] = cutlass::half_t((i * 4 + 8) % 7);\n    D.host_data()[i] = cutlass::half_t(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n\n  test::core::kernel::binary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    float expected = a / b;\n\n    float const kThreshold = 0.0005f;\n\n    if (std::isnan(expected)) {\n      EXPECT_TRUE(std::isnan(d));\n    }\n    else if (std::isinf(expected)) {\n      EXPECT_TRUE(std::isinf(d));\n    }\n    else {\n      EXPECT_TRUE(std::abs(d - expected) < kThreshold)\n        << \"Got: \" << d << \" = \" << a << \" / \" << b << \", expected: \" << (a / b); \n    }\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\nTEST(Functional, divides_f16x16) {\n\n  Functional_divides_f16xN<16>();\n}\n\nTEST(Functional, divides_f16x17) {\n\n  Functional_divides_f16xN<17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int kN>\nvoid Functional_multiply_add_TxN() {\n\n  using Element = cutlass::Array<T, kN>;\n  using Operator = cutlass::multiply_add<Element>;\n\n  using Tensor = cutlass::HostTensor<T, cutlass::layout::RowMajor>;\n\n  Tensor D({1, kN});\n  Tensor A({1, kN});\n  Tensor B({1, kN});\n  Tensor C({1, kN});\n\n  for (int i = 0; i < kN; ++i) {\n    A.host_data()[i] = T((i * 2 + 1) % 5);\n    B.host_data()[i] = T((i * 4 + 8) % 7);\n    C.host_data()[i] = T((i * 3 + 11) % 11);\n    D.host_data()[i] = T(0);\n  }\n\n  D.sync_device();\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n\n  test::core::kernel::trinary_operator<Element, Operator><<< dim3(1,1), dim3(1,1) >>>(\n    reinterpret_cast<Element *>(D.device_data()),\n    reinterpret_cast<Element const *>(A.device_data()),\n    reinterpret_cast<Element const *>(B.device_data()),\n    reinterpret_cast<Element const *>(C.device_data())\n  );\n\n  D.sync_host();\n\n  bool some_d_nonzero = false;\n\n  for (int i = 0; i < kN; ++i) {\n    float a = float(A.host_data()[i]);\n    float b = float(B.host_data()[i]);\n    float c = float(C.host_data()[i]);\n    float d = float(D.host_data()[i]);\n\n    EXPECT_TRUE(d == (a * b + c));\n\n    if (d != 0) {\n      some_d_nonzero = true;\n    }\n  }\n\n  EXPECT_TRUE(some_d_nonzero);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Functional, multiply_add_f16x16) {\n  Functional_multiply_add_TxN<cutlass::half_t, 16>();\n}\n\nTEST(Functional, multiply_add_f16x17) {\n  Functional_multiply_add_TxN<cutlass::half_t, 17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Functional, multiply_add_bf16x16) {\n  Functional_multiply_add_TxN<cutlass::bfloat16_t, 16>();\n}\n\nTEST(Functional, multiply_add_bf16x17) {\n  Functional_multiply_add_TxN<cutlass::bfloat16_t, 17>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T>\ncutlass::Quaternion<T> random_quaternion(int range) {\n  return cutlass::Quaternion<T>{\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range),\n    T((rand() % range * 2) - range)\n  };\n}\n\ntemplate <typename T>\nvoid Functional_multiply_add_QuaternionT() {\n\n  using Element = cutlass::Quaternion<T>;\n  using Operator = cutlass::multiply_add<Element, Element, Element>;\n  using HostTensor = cutlass::HostTensor<Element, cutlass::layout::RowMajor>;\n\n  int const kM = 128;\n  int const kRange = 8;\n\n  HostTensor A({kM, 1});\n  HostTensor B({kM, 1});\n  HostTensor C({kM, 1});\n  HostTensor D({kM, 1});\n\n  srand(2021);\n\n  for (int m = 0; m < kM; ++m) {\n    A.at({m, 0}) = random_quaternion<T>(kRange);\n    B.at({m, 0}) = random_quaternion<T>(kRange);\n    C.at({m, 0}) = random_quaternion<T>(kRange);\n  }\n\n  A.sync_device();\n  B.sync_device();\n  C.sync_device();\n  D.sync_device();\n\n  test::core::kernel::trinary_operator<Element, Operator><<< dim3(kM,1), dim3(1,1) >>>(\n    D.device_data(),\n    A.device_data(),\n    B.device_data(),\n    C.device_data()\n  );\n\n  D.sync_host();\n  \n  for (int m = 0; m < kM; ++m) {\n\n    Element a = A.at({m, 0});\n    Element b = B.at({m, 0});\n    Element c = C.at({m, 0});\n    Element got = D.at({m, 0});\n    Element expected = a * b + c;\n\n    EXPECT_TRUE(got == expected);\n  }\n}\n\nTEST(Functional, multiply_add_quaternion_f32) {\n  Functional_multiply_add_QuaternionT<float>();\n}\n\nnamespace cutlass_test {\n\n__global__ void\ntest_cutlass_maximum(cutlass::half_t const* in1, cutlass::half_t const* in2, cutlass::half_t* out)\n{\n  {\n  constexpr bool propagate_NaN = true;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0\n    && blockIdx.x == 0 && blockIdx.y == 0 && blockIdx.z == 0) {\n    *out = op(*in1, *in2);\n  }\n  }\n  constexpr bool propagate_NaN = false;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0\n    && blockIdx.x == 0 && blockIdx.y == 0 && blockIdx.z == 0) {\n    *out = op(*in1, *in2);\n  }\n}\n\n} // cutlass_test\n\n// Test compilation on both host and device.\nTEST(Functional, maximum_half_host_propagate_NaN) {\n  constexpr bool propagate_NaN = true;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  cutlass::half_t x(1.0f);\n  cutlass::half_t y(2.0f);\n\n  auto result = op(x, y);\n  static_assert(std::is_same_v<decltype(result), cutlass::half_t>);\n  EXPECT_EQ(result, y);\n  result = op(y, x);\n  EXPECT_EQ(result, y);\n}\n\nTEST(Functional, maximum_half_host_dont_propagate_NaN) {\n  constexpr bool propagate_NaN = false;\n  cutlass::maximum<cutlass::half_t, propagate_NaN> op;\n  cutlass::half_t x(1.0f);\n  cutlass::half_t y(2.0f);\n\n  auto result = op(x, y);\n  static_assert(std::is_same_v<decltype(result), cutlass::half_t>);\n  EXPECT_EQ(result, y);\n  result = op(y, x);\n  EXPECT_EQ(result, y);\n}\n\nTEST(FUnction, maximum_half_device) {\n  using Tensor = cutlass::HostTensor<cutlass::half_t, cutlass::layout::RowMajor>;\n\n  Tensor in1({1, 1});\n  Tensor in2({1, 1});\n  Tensor out({1, 1});\n  in1.host_data()[0] = cutlass::half_t(1.0f);\n  in2.host_data()[0] = cutlass::half_t(2.0f);\n  out.host_data()[0] = cutlass::half_t(0.0f);\n\n  in1.sync_device();\n  in2.sync_device();\n  out.sync_device();\n\n  cutlass_test::test_cutlass_maximum<<< 1, 1 >>>(\n    in1.device_data(),\n    in2.device_data(),\n    out.device_data()\n  );\n  out.sync_host();\n\n  EXPECT_EQ(out.host_data()[0], 2.0f);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/core/functional.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for conversion operators.\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/numeric_conversion.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Simple conversion function\ntemplate <typename Destination, typename Source, int Count>\n__global__ void convert(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source) {\n\n  cutlass::NumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test(const char dest_name[], const char source_name[], const int range = 4, const int offset = 0) {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n  auto source_ref = source.host_ref();\n  auto destination_ref = destination.host_ref();\n\n  for (int i = 0; i < kN; ++i) {\n    source_ref.at({0, i}) = Source(i % range + offset);\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    EXPECT_TRUE(float(destination_ref.at({0, i})) == float(source_ref.at({0, i})))\n      << \"Destination type: \" << dest_name << \" \"<< float(destination_ref.at({0, i}))\n      << \", Source type: \" << source_name << \" \" << float(source_ref.at({0, i}))\n      << \", Count: \" << Count;\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, typename ScaleFactor, int Count>\n__global__ void convert_with_scale_factor(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source,\n  cutlass::Array<ScaleFactor, Count> const *scale_factor) {\n\n  cutlass::NumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source, *scale_factor);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, typename ScaleFactor,  int Count>\nvoid run_test_with_scalefactor(const char dest_name[], const char source_name[], const char scale_factor_name[], const int range = 4, const int offset = 0) {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n  cutlass::HostTensor<ScaleFactor, cutlass::layout::RowMajor> scale_factor({1, kN});\n  auto source_ref = source.host_ref();\n  auto destination_ref = destination.host_ref();\n  auto scale_factor_ref = scale_factor.host_ref();\n\n\n  for (int i = 0; i < kN; ++i) {\n    source_ref.at({0, i}) = Source(i % range + offset);\n  }\n\n  for (int i = 0; i < kN; ++i) {\n    scale_factor_ref.at({0, i}) = ScaleFactor(1 + i % 8);\n  }\n\n  source.sync_device();\n  scale_factor.sync_device();\n\n  convert_with_scale_factor<Destination, Source, ScaleFactor, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data()),\n    reinterpret_cast<cutlass::Array<ScaleFactor, kN> const *>(scale_factor.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    float ref = float(source_ref.at({0, i})) / float(scale_factor_ref.at({0, i}));\n    bool pass = float(destination_ref.at({0, i})) == ref;\n    EXPECT_TRUE(pass) \n      << \"Destination type: \" << dest_name << \" \"<< float(destination_ref.at({0, i})) << std::endl\n      << \", Source type: \" << source_name << \" \" << float(source_ref.at({0, i})) << std::endl\n      << \", Scalefactor type: \" << source_name << \" \" << float(scale_factor_ref.at({0, i})) << std::endl\n      << \", idx: \" << i << std::endl;\n  }\n}\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32_to_f16_rn) {\n  constexpr int kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32x2_to_f16x2_rn) {\n  constexpr int kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32x8_to_f16x8_rn) {\n  constexpr int kN = 8;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f16_to_f32_rn) {  \n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16x8_to_f32x8_rn) {\n  int const kN = 8;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe4m3_rn_2_elements) {\n  int const kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn_2_elements) {\n  int const kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, fe4m3_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_fe5m2_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_fe5m2_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32x8_to_s8x8_rn) {\n\n  int const kN = 8;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = int8_t;\n  const char dest_name[] = \"int8_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f32_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f32_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n// These are included as regression tests for a special case when N = 4.\nTEST(NumericConversion, int4b_t_to_fe5m2_t_array_4) {\n  int const kN = 4;\n  using Source = cutlass::int4b_t;\n  const char source_name[] = \"int4b_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int_to_fe4m3_t_array_4) {\n  int const kN = 4;\n  using Source = int;\n  const char source_name[] = \"int\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int2b_t_to_fe4m3_t_array_4) {\n  int const kN = 4;\n  using Source = cutlass::int2b_t;\n  const char source_name[] = \"int2b_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_t_to_double_array_4) {\n  int const kN = 4;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = double;\n  const char dest_name[] = \"double\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int_to_fe4m3_t_array_32) {\n  int const kN = 32;\n  using Source = int;\n  const char source_name[] = \"int\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T>\nstruct GetName {\n  static constexpr char name[] = \"UNSUPPORTED\";\n};\n\ntemplate <>\nstruct GetName<cutlass::int2b_t> {\n  static constexpr char name[] = \"int2b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::uint2b_t> {\n  static constexpr char name[] = \"uint2b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::int4b_t> {\n  static constexpr char name[] = \"int4b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::uint4b_t> {\n  static constexpr char name[] = \"uint4b_t\";\n};\n\ntemplate <>\nstruct GetName<uint8_t> {\n  static constexpr char name[] = \"uint8_t\";\n};\n\ntemplate <>\nstruct GetName<int8_t> {\n  static constexpr char name[] = \"int8_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::float_e4m3_t> {\n  static constexpr char name[] = \"float_e4m3_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::float_e5m2_t> {\n  static constexpr char name[] = \"float_e5m2_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::half_t> {\n  static constexpr char name[] = \"half_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::bfloat16_t> {\n  static constexpr char name[] = \"bfloat16_t\";\n};\n\ntemplate <>\nstruct GetName<float> {\n  static constexpr char name[] = \"float\";\n};\n\ntemplate <typename Result_, typename Source_>\nstruct ResultSourcePair {\n  using Result = Result_;\n  using Source = Source_;\n};\n\ntemplate <typename ResultSourcePair>\nclass VectorArrayConverterTest : public testing::Test {\n public:\n  using Result = typename ResultSourcePair::Result;\n  using Source = typename ResultSourcePair::Source;\n  \n  template <int N>\n  static void emit_test() { \n    const int range = 1 << cutlass::sizeof_bits<Source>::value;\n    const int offset = cutlass::platform::numeric_limits<Source>::lowest();\n    test::core::kernel::run_test<Result, Source, N>(GetName<Result>::name, GetName<Source>::name, range, offset);\n  }\n};\n\nusing VectorConvertTypes = ::testing::Types<\n  ResultSourcePair<float, int8_t>,\n  ResultSourcePair<float, uint8_t>,\n\n  ResultSourcePair<cutlass::half_t, int8_t>,\n  ResultSourcePair<cutlass::half_t, uint8_t>,\n\n  ResultSourcePair<cutlass::bfloat16_t, uint8_t>,\n  ResultSourcePair<cutlass::bfloat16_t, int8_t>,\n\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::uint2b_t>,\n\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::uint4b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::uint4b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::uint4b_t>,\n  ResultSourcePair<float, cutlass::int4b_t>\n>;\n\nTYPED_TEST_SUITE(VectorArrayConverterTest, VectorConvertTypes);\n\nTYPED_TEST(VectorArrayConverterTest, array_1) {\n  TestFixture::template emit_test<1>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_2) {\n  TestFixture::template emit_test<2>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_3) {\n  TestFixture::template emit_test<3>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_4) {\n  TestFixture::template emit_test<4>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_5) {\n  TestFixture::template emit_test<5>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_8) {\n  TestFixture::template emit_test<8>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_10) {\n  // N > 8 and N is not a multiple of 4\n  TestFixture::template emit_test<10>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_12) {\n  // N > 8 and N is a multiple of 4\n  TestFixture::template emit_test<12>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_16) {\n  // N > 8 and N is a multiple of 8\n  TestFixture::template emit_test<16>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_17) {\n  // N > 8 and N is not a multiple of 8\n  TestFixture::template emit_test<17>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_27) {\n  // Test entire conversion range with residue (for int4)\n  TestFixture::template emit_test<27>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_31) {\n  // Force use of converters for 16, 8, 4, 2 and scalar \n  // if max width is 16\n  TestFixture::template emit_test<31>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_63) {\n  // Force use of converters for 32, 16, 8, 4, 2 and scalar \n  // if max width is 32\n  TestFixture::template emit_test<63>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_256) {\n  // Test entire conversion range (for int8)\n  TestFixture::template emit_test<256>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_259) {\n  // Force use of 4, 2 and scalar converter (if max width is 4)\n  TestFixture::template emit_test<259>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_263) {\n  // Force use of 8, 4, 2 and scalar converter (if max width is 8)\n  TestFixture::template emit_test<263>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for conversion operators.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/numeric_conversion.h\"\n\n#include \"cutlass/layout/matrix.h\"\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace core {\nnamespace kernel {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n/// Simple conversion function\ntemplate <typename Destination, typename Source, int Count>\n__global__ void convert(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source) {\n\n  cutlass::NumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, int Count>\nvoid run_test(const char dest_name[], const char source_name[], const int range = 4, const int offset = 0) {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n  auto source_ref = source.host_ref();\n  auto destination_ref = destination.host_ref();\n\n  for (int i = 0; i < kN; ++i) {\n    source_ref.at({0, i}) = Source(i % range + offset);\n  }\n\n  source.sync_device();\n\n  convert<Destination, Source, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    EXPECT_TRUE(float(destination_ref.at({0, i})) == float(source_ref.at({0, i})))\n      << \"Destination type: \" << dest_name << \" \"<< float(destination_ref.at({0, i}))\n      << \", Source type: \" << source_name << \" \" << float(source_ref.at({0, i}))\n      << \", Count: \" << Count;\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, typename ScaleFactor, int Count>\n__global__ void convert_with_scale_factor(\n  cutlass::Array<Destination, Count> *destination,\n  cutlass::Array<Source, Count> const *source,\n  cutlass::Array<ScaleFactor, Count> const *scale_factor) {\n\n  cutlass::NumericArrayConverter<Destination, Source, Count> convert;\n\n  *destination = convert(*source, *scale_factor);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename Destination, typename Source, typename ScaleFactor,  int Count>\nvoid run_test_with_scalefactor(const char dest_name[], const char source_name[], const char scale_factor_name[], const int range = 4, const int offset = 0) {\n  const int kN = Count;\n\n  dim3 grid(1, 1);\n  dim3 block(1, 1);\n\n  cutlass::HostTensor<Destination, cutlass::layout::RowMajor> destination({1, kN});\n  cutlass::HostTensor<Source, cutlass::layout::RowMajor> source({1, kN});\n  cutlass::HostTensor<ScaleFactor, cutlass::layout::RowMajor> scale_factor({1, kN});\n  auto source_ref = source.host_ref();\n  auto destination_ref = destination.host_ref();\n  auto scale_factor_ref = scale_factor.host_ref();\n\n\n  for (int i = 0; i < kN; ++i) {\n    source_ref.at({0, i}) = Source(i % range + offset);\n  }\n\n  for (int i = 0; i < kN; ++i) {\n    scale_factor_ref.at({0, i}) = ScaleFactor(1 + i % 8);\n  }\n\n  source.sync_device();\n  scale_factor.sync_device();\n\n  convert_with_scale_factor<Destination, Source, ScaleFactor, kN><<< grid, block >>>(\n    reinterpret_cast<cutlass::Array<Destination, kN> *>(destination.device_data()),\n    reinterpret_cast<cutlass::Array<Source, kN> const *>(source.device_data()),\n    reinterpret_cast<cutlass::Array<ScaleFactor, kN> const *>(scale_factor.device_data())\n  );\n\n  destination.sync_host();\n\n  for (int i = 0; i < kN; ++i) {\n    float ref = float(source_ref.at({0, i})) / float(scale_factor_ref.at({0, i}));\n    bool pass = float(destination_ref.at({0, i})) == ref;\n    EXPECT_TRUE(pass) \n      << \"Destination type: \" << dest_name << \" \"<< float(destination_ref.at({0, i})) << std::endl\n      << \", Source type: \" << source_name << \" \" << float(source_ref.at({0, i})) << std::endl\n      << \", Scalefactor type: \" << source_name << \" \" << float(scale_factor_ref.at({0, i})) << std::endl\n      << \", idx: \" << i << std::endl;\n  }\n}\n\n} // namespace kernel\n} // namespace core\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32_to_f16_rn) {\n  constexpr int kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32x2_to_f16x2_rn) {\n  constexpr int kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32x8_to_f16x8_rn) {\n  constexpr int kN = 8;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f16_to_f32_rn) {  \n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16x8_to_f32x8_rn) {\n  int const kN = 8;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe4m3_rn_2_elements) {\n  int const kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn_2_elements) {\n  int const kN = 2;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f32_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, f16_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::half_t;\n  const char source_name[] = \"half_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe4m3_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, bf16_to_fe5m2_rn_array) {\n  int const kN = 27;\n  using Source = cutlass::bfloat16_t;\n  const char source_name[] = \"bfloat16_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, fe4m3_to_fe5m2_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_fe5m2_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_fe5m2_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_2_elements) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_fe4m3_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(NumericConversion, f32x8_to_s8x8_rn) {\n\n  int const kN = 8;\n  using Source = float;\n  const char source_name[] = \"float\";\n  using Destination = int8_t;\n  const char dest_name[] = \"int8_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f32_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f32_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f32_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = float;\n  const char dest_name[] = \"float\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_f16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_f16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::half_t;\n  const char dest_name[] = \"half_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe4m3_to_bf16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e4m3_t;\n  const char source_name[] = \"float_e4m3_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_rn) {\n  int const kN = 1;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_2_elements) {\n  int const kN = 2;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_to_bf16_array) {\n  int const kN = 27;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = cutlass::bfloat16_t;\n  const char dest_name[] = \"bfloat16_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n// These are included as regression tests for a special case when N = 4.\nTEST(NumericConversion, int4b_t_to_fe5m2_t_array_4) {\n  int const kN = 4;\n  using Source = cutlass::int4b_t;\n  const char source_name[] = \"int4b_t\";\n  using Destination = cutlass::float_e5m2_t;\n  const char dest_name[] = \"float_e5m2_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int_to_fe4m3_t_array_4) {\n  int const kN = 4;\n  using Source = int;\n  const char source_name[] = \"int\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int2b_t_to_fe4m3_t_array_4) {\n  int const kN = 4;\n  using Source = cutlass::int2b_t;\n  const char source_name[] = \"int2b_t\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, fe5m2_t_to_double_array_4) {\n  int const kN = 4;\n  using Source = cutlass::float_e5m2_t;\n  const char source_name[] = \"float_e5m2_t\";\n  using Destination = double;\n  const char dest_name[] = \"double\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\nTEST(NumericConversion, int_to_fe4m3_t_array_32) {\n  int const kN = 32;\n  using Source = int;\n  const char source_name[] = \"int\";\n  using Destination = cutlass::float_e4m3_t;\n  const char dest_name[] = \"float_e4m3_t\";\n  test::core::kernel::run_test<Destination, Source, kN>(dest_name, source_name);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T>\nstruct GetName {\n  static constexpr char name[] = \"UNSUPPORTED\";\n};\n\ntemplate <>\nstruct GetName<cutlass::int2b_t> {\n  static constexpr char name[] = \"int2b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::uint2b_t> {\n  static constexpr char name[] = \"uint2b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::int4b_t> {\n  static constexpr char name[] = \"int4b_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::uint4b_t> {\n  static constexpr char name[] = \"uint4b_t\";\n};\n\ntemplate <>\nstruct GetName<uint8_t> {\n  static constexpr char name[] = \"uint8_t\";\n};\n\ntemplate <>\nstruct GetName<int8_t> {\n  static constexpr char name[] = \"int8_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::float_e4m3_t> {\n  static constexpr char name[] = \"float_e4m3_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::float_e5m2_t> {\n  static constexpr char name[] = \"float_e5m2_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::half_t> {\n  static constexpr char name[] = \"half_t\";\n};\n\ntemplate <>\nstruct GetName<cutlass::bfloat16_t> {\n  static constexpr char name[] = \"bfloat16_t\";\n};\n\ntemplate <>\nstruct GetName<float> {\n  static constexpr char name[] = \"float\";\n};\n\ntemplate <typename Result_, typename Source_>\nstruct ResultSourcePair {\n  using Result = Result_;\n  using Source = Source_;\n};\n\ntemplate <typename ResultSourcePair>\nclass VectorArrayConverterTest : public testing::Test {\n public:\n  using Result = typename ResultSourcePair::Result;\n  using Source = typename ResultSourcePair::Source;\n  \n  template <int N>\n  static void emit_test() { \n    const int range = 1 << cutlass::sizeof_bits<Source>::value;\n    const int offset = cutlass::platform::numeric_limits<Source>::lowest();\n    test::core::kernel::run_test<Result, Source, N>(GetName<Result>::name, GetName<Source>::name, range, offset);\n  }\n};\n\nusing VectorConvertTypes = ::testing::Types<\n  ResultSourcePair<float, int8_t>,\n  ResultSourcePair<float, uint8_t>,\n\n  ResultSourcePair<cutlass::half_t, int8_t>,\n  ResultSourcePair<cutlass::half_t, uint8_t>,\n\n  ResultSourcePair<cutlass::bfloat16_t, uint8_t>,\n  ResultSourcePair<cutlass::bfloat16_t, int8_t>,\n\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::int2b_t>,\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::uint2b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::uint2b_t>,\n\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::float_e5m2_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::int4b_t>,\n  ResultSourcePair<cutlass::float_e4m3_t, cutlass::uint4b_t>,\n  ResultSourcePair<cutlass::half_t, cutlass::uint4b_t>,\n  ResultSourcePair<cutlass::bfloat16_t, cutlass::uint4b_t>,\n  ResultSourcePair<float, cutlass::int4b_t>\n>;\n\nTYPED_TEST_SUITE(VectorArrayConverterTest, VectorConvertTypes);\n\nTYPED_TEST(VectorArrayConverterTest, array_1) {\n  TestFixture::template emit_test<1>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_2) {\n  TestFixture::template emit_test<2>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_3) {\n  TestFixture::template emit_test<3>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_4) {\n  TestFixture::template emit_test<4>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_5) {\n  TestFixture::template emit_test<5>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_8) {\n  TestFixture::template emit_test<8>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_10) {\n  // N > 8 and N is not a multiple of 4\n  TestFixture::template emit_test<10>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_12) {\n  // N > 8 and N is a multiple of 4\n  TestFixture::template emit_test<12>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_16) {\n  // N > 8 and N is a multiple of 8\n  TestFixture::template emit_test<16>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_17) {\n  // N > 8 and N is not a multiple of 8\n  TestFixture::template emit_test<17>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_27) {\n  // Test entire conversion range with residue (for int4)\n  TestFixture::template emit_test<27>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_31) {\n  // Force use of converters for 16, 8, 4, 2 and scalar \n  // if max width is 16\n  TestFixture::template emit_test<31>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_63) {\n  // Force use of converters for 32, 16, 8, 4, 2 and scalar \n  // if max width is 32\n  TestFixture::template emit_test<63>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_256) {\n  // Test entire conversion range (for int8)\n  TestFixture::template emit_test<256>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_259) {\n  // Force use of 4, 2 and scalar converter (if max width is 4)\n  TestFixture::template emit_test<259>();\n}\n\nTYPED_TEST(VectorArrayConverterTest, array_263) {\n  // Force use of 8, 4, 2 and scalar converter (if max width is 8)\n  TestFixture::template emit_test<263>();\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/core/numeric_conversion.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include <cstring>\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/predicate_vector.h\"\n#include \"cutlass/util/host_tensor.h\"\n\nnamespace test {\n\ntemplate <typename PredicateVector>\n__global__ void load_predicates(unsigned *output, unsigned const *input) {\n\n  PredicateVector predicates;\n\n  int const word_count = (PredicateVector::kPredicates + 31) / 32;\n\n  int i = 0;\n  for (int word_idx = 0; word_idx < word_count; ++word_idx) {\n    unsigned word = input[word_idx];\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int bit = 0; bit < sizeof(unsigned) * 8; ++bit) {\n      bool pred = ((word >> bit) & 1);\n      predicates.set(i, pred);\n\n      if (predicates.at(i) != pred) {\n        printf(\"ERROR - cannot read back predicate\\n\");\n      }\n      ++i;\n    }\n  }\n\n\n  __syncthreads();\n\n  i = 0;\n  for (int word_idx = 0; word_idx < word_count; ++word_idx) {\n\n    unsigned result = 0;\n    for (int bit = 0; bit < sizeof(unsigned) * 8; ++bit) {\n      bool pred = predicates.at(i ++);\n      result |= (unsigned(pred) << bit);\n    }\n    output[word_idx] = result;\n  }\n}\n}\n\nTEST(PredicateVector, Basic) {\n\n  static int const Bits = 32;\n  static int const Words = (Bits + 31) / 32;\n\n  typedef cutlass::PredicateVector<Bits> PredicateVector;\n\n  cutlass::HostTensor<unsigned, cutlass::IdentityTensorLayout<1> > output;\n  cutlass::HostTensor<unsigned, cutlass::IdentityTensorLayout<1>> input;\n\n  output.reserve(Words);\n  input.reserve(Words);\n\n  // some arbitrary test bits\n  unsigned values[] = {\n    0xdeadbeef,\n    0xa0070032,\n    0x9076d001,\n    0x00000000,\n    0xabdfc0ad\n  };\n\n  for (int test = 0; test < 5; ++test) {\n\n    input.host_data(0) = values[test];\n    output.host_data(0) = 0;\n\n    input.sync_device();\n    output.sync_device();\n\n    test::load_predicates<PredicateVector><<<\n      dim3(1,1,1), dim3(1,1,1)\n    >>>(\n      output.device_data(),\n      input.device_data()\n    );\n\n    output.sync_host();\n\n    for (int word = 0; word < Words; ++word) {\n      EXPECT_EQ(input.host_data(word), output.host_data(word))\n        << \"Expected: 0x\" << std::hex << input.host_data(word)\n        << \", got: 0x\" << output.host_data(word)\n        << std::dec;\n    }\n  }\n}\n\nTEST(PredicateVector, Count) {\n\n  {\n    typedef cutlass::PredicateVector<4, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<8, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<16, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<16, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<16, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<16, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<32, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<32, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<32, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 8)\n        << \"PredicateVector<32, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<64, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<64, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 8)\n        << \"PredicateVector<64, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 16)\n        << \"PredicateVector<64, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include <cstring>\n\n#include \"../common/cutlass_unit_test.h\"\n\n#include \"cutlass/predicate_vector.h\"\n#include \"cutlass/util/host_tensor.h\"\n\nnamespace test {\n\ntemplate <typename PredicateVector>\n__global__ void load_predicates(unsigned *output, unsigned const *input) {\n\n  PredicateVector predicates;\n\n  int const word_count = (PredicateVector::kPredicates + 31) / 32;\n\n  int i = 0;\n  for (int word_idx = 0; word_idx < word_count; ++word_idx) {\n    unsigned word = input[word_idx];\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int bit = 0; bit < sizeof(unsigned) * 8; ++bit) {\n      bool pred = ((word >> bit) & 1);\n      predicates.set(i, pred);\n\n      if (predicates.at(i) != pred) {\n        printf(\"ERROR - cannot read back predicate\\n\");\n      }\n      ++i;\n    }\n  }\n\n\n  __syncthreads();\n\n  i = 0;\n  for (int word_idx = 0; word_idx < word_count; ++word_idx) {\n\n    unsigned result = 0;\n    for (int bit = 0; bit < sizeof(unsigned) * 8; ++bit) {\n      bool pred = predicates.at(i ++);\n      result |= (unsigned(pred) << bit);\n    }\n    output[word_idx] = result;\n  }\n}\n}\n\nTEST(PredicateVector, Basic) {\n\n  static int const Bits = 32;\n  static int const Words = (Bits + 31) / 32;\n\n  typedef cutlass::PredicateVector<Bits> PredicateVector;\n\n  cutlass::HostTensor<unsigned, cutlass::IdentityTensorLayout<1> > output;\n  cutlass::HostTensor<unsigned, cutlass::IdentityTensorLayout<1>> input;\n\n  output.reserve(Words);\n  input.reserve(Words);\n\n  // some arbitrary test bits\n  unsigned values[] = {\n    0xdeadbeef,\n    0xa0070032,\n    0x9076d001,\n    0x00000000,\n    0xabdfc0ad\n  };\n\n  for (int test = 0; test < 5; ++test) {\n\n    input.host_data(0) = values[test];\n    output.host_data(0) = 0;\n\n    input.sync_device();\n    output.sync_device();\n\n    test::load_predicates<PredicateVector><<<\n      dim3(1,1,1), dim3(1,1,1)\n    >>>(\n      output.device_data(),\n      input.device_data()\n    );\n\n    output.sync_host();\n\n    for (int word = 0; word < Words; ++word) {\n      EXPECT_EQ(input.host_data(word), output.host_data(word))\n        << \"Expected: 0x\" << std::hex << input.host_data(word)\n        << \", got: 0x\" << output.host_data(word)\n        << std::dec;\n    }\n  }\n}\n\nTEST(PredicateVector, Count) {\n\n  {\n    typedef cutlass::PredicateVector<4, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<4, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<4, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<8, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<8, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<8, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<16, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<16, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<16, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<16, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<16, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 1)\n        << \"PredicateVector<32, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<32, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<32, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<32, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 8)\n        << \"PredicateVector<32, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 8> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 2)\n        << \"PredicateVector<64, 8> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 4> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 4)\n        << \"PredicateVector<64, 4> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 2> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 8)\n        << \"PredicateVector<64, 2> word count: \" << int(PredicateVector::kWordCount);\n  }\n\n  {\n    typedef cutlass::PredicateVector<64, 1> PredicateVector;\n    EXPECT_EQ(int(PredicateVector::kWordCount), 16)\n        << \"PredicateVector<64, 1> word count: \" << int(PredicateVector::kWordCount);\n  }\n}\n", "source": "/kaggle/working/cutlass/test/unit/core/predicate_vector.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Basic tests for BULK_COPY usage with various layouts.\n*/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class ElementType, class SmemLayout>\nstruct SharedStorage {\n  cute::array_aligned<ElementType, cute::cosize_v<SmemLayout>> smem;\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\ntemplate <class T, class GmemLayout, class SmemLayout>\n__global__ void\nbulk_copy_test_device_cute(T const* g_in,\n                           T      * g_out,\n                           GmemLayout gmem_layout,\n                           SmemLayout smem_layout)\n{\n  // Use Shared Storage structure to allocate and distribute aligned SMEM addresses\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<T, SmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Construct SMEM tensor\n  Tensor sA = make_tensor(make_smem_ptr(shared_storage.smem.data()), smem_layout);\n  // Construct the GMEM tensor\n  Tensor gA = make_tensor(make_gmem_ptr(g_in), gmem_layout);\n\n  //\n  // Read in trivially\n  //\n\n  // Input gmem -> smem\n  for (int i = threadIdx.x; i < size(sA); i += blockDim.x) {\n    sA(i) = gA(i);\n  }\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  //\n  // Perform the BULK_COPY store\n  //\n\n#if 0\n  if (thread0()) {\n    print(\"sA: \"); print(sA.data()); print(\" o \"); print(sA.layout()); print(\"\\n\");\n    print(\"gA: \"); print(gA.data()); print(\" o \"); print(gA.layout()); print(\"\\n\");\n  }\n#endif\n\n  Tensor gA_out = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n\n  auto blkcp = Copy_Traits<SM90_BULK_COPY_AUTO>{};\n\n  copy(blkcp, sA, gA_out);\n  // Bulk Copy store requires the same sync as TMA store.\n  tma_store_arrive();\n  tma_store_wait<0>();\n}\n\ntemplate <class T, class GLayout, class SLayout>\nvoid run_and_validate(GLayout gmem_layout,\n                      SLayout smem_layout)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout));\n  for (size_t i = 0; i < h_in.size(); ++i) {\n    h_in[i] = static_cast<T>(int(i));\n  }\n\n  thrust::device_vector<T> d_in = h_in;\n  thrust::device_vector<T> d_out(d_in.size(), T(-1));\n\n  int32_t smem_size = static_cast<int32_t>(sizeof(SharedStorage<T, decltype(smem_layout)>));\n  bulk_copy_test_device_cute<<<1, 128, smem_size>>>(thrust::raw_pointer_cast(d_in.data()),\n                                                    thrust::raw_pointer_cast(d_out.data()),\n                                                    gmem_layout,\n                                                    smem_layout);\n  // Transfering results back to host\n  thrust::host_vector<T> h_out = d_out;\n\n  // Validate the results\n  for (int i = 0; i < cute::size(gmem_layout); ++i) {\n    int k = gmem_layout(i);\n    EXPECT_EQ(int(h_in[k]), int(h_out[k]));\n  }\n}\n\n// }  // namespace\n\nTEST(SM90_CuTe_BLKCP, ColMajor)\n{\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenColMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, RowMajor)\n{\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenRowMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, NonCompact)\n{\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = make_layout(Shape<Shape<_16,_2>, Shape<_4,_8>>{}, Stride<Stride<_1,_64>,Stride<_16,_128>>{});\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_64,_1>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n}\n#endif // #if CUDA_12_0_SM90_FEATURES_SUPPORTED\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Basic tests for BULK_COPY usage with various layouts.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class ElementType, class SmemLayout>\nstruct SharedStorage {\n  cute::array_aligned<ElementType, cute::cosize_v<SmemLayout>> smem;\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\ntemplate <class T, class GmemLayout, class SmemLayout>\n__global__ void\nbulk_copy_test_device_cute(T const* g_in,\n                           T      * g_out,\n                           GmemLayout gmem_layout,\n                           SmemLayout smem_layout)\n{\n  // Use Shared Storage structure to allocate and distribute aligned SMEM addresses\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<T, SmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Construct SMEM tensor\n  Tensor sA = make_tensor(make_smem_ptr(shared_storage.smem.data()), smem_layout);\n  // Construct the GMEM tensor\n  Tensor gA = make_tensor(make_gmem_ptr(g_in), gmem_layout);\n\n  //\n  // Read in trivially\n  //\n\n  // Input gmem -> smem\n  for (int i = threadIdx.x; i < size(sA); i += blockDim.x) {\n    sA(i) = gA(i);\n  }\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  //\n  // Perform the BULK_COPY store\n  //\n\n#if 0\n  if (thread0()) {\n    print(\"sA: \"); print(sA.data()); print(\" o \"); print(sA.layout()); print(\"\\n\");\n    print(\"gA: \"); print(gA.data()); print(\" o \"); print(gA.layout()); print(\"\\n\");\n  }\n#endif\n\n  Tensor gA_out = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n\n  auto blkcp = Copy_Traits<SM90_BULK_COPY_AUTO>{};\n\n  copy(blkcp, sA, gA_out);\n  // Bulk Copy store requires the same sync as TMA store.\n  tma_store_arrive();\n  tma_store_wait<0>();\n}\n\ntemplate <class T, class GLayout, class SLayout>\nvoid run_and_validate(GLayout gmem_layout,\n                      SLayout smem_layout)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout));\n  for (size_t i = 0; i < h_in.size(); ++i) {\n    h_in[i] = static_cast<T>(int(i));\n  }\n\n  thrust::device_vector<T> d_in = h_in;\n  thrust::device_vector<T> d_out(d_in.size(), T(-1));\n\n  int32_t smem_size = static_cast<int32_t>(sizeof(SharedStorage<T, decltype(smem_layout)>));\n  bulk_copy_test_device_cute<<<1, 128, smem_size>>>(thrust::raw_pointer_cast(d_in.data()),\n                                                    thrust::raw_pointer_cast(d_out.data()),\n                                                    gmem_layout,\n                                                    smem_layout);\n  // Transfering results back to host\n  thrust::host_vector<T> h_out = d_out;\n\n  // Validate the results\n  for (int i = 0; i < cute::size(gmem_layout); ++i) {\n    int k = gmem_layout(i);\n    EXPECT_EQ(int(h_in[k]), int(h_out[k]));\n  }\n}\n\n// }  // namespace\n\nTEST(SM90_CuTe_BLKCP, ColMajor)\n{\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenColMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, RowMajor)\n{\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenRowMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, NonCompact)\n{\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = make_layout(Shape<Shape<_16,_2>, Shape<_4,_8>>{}, Stride<Stride<_1,_64>,Stride<_16,_128>>{});\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_64,_1>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n}\n#endif // #if CUDA_12_0_SM90_FEATURES_SUPPORTED\n", "source": "/kaggle/working/cutlass/test/unit/cute/hopper/bulk_store.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Basic tests for BULK_COPY usage with various layouts.\n*/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class ElementType, class SmemLayout>\nstruct SharedStorage {\n  cute::array_aligned<ElementType, cute::cosize_v<SmemLayout>> smem;\n  cute::uint64_t bulk_copy_mbar[1];\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\ntemplate <class T, class GmemLayout, class SmemLayout>\n__global__ void\nbulk_copy_test_device_cute(T const* g_in,\n                           T      * g_out,\n                           GmemLayout gmem_layout,\n                           SmemLayout smem_layout)\n{\n  // Use Shared Storage structure to allocate and distribute aligned SMEM addresses\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<T, SmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Construct SMEM tensor\n  Tensor sA = make_tensor(make_smem_ptr(shared_storage.smem.data()), smem_layout);\n  // Construct the GMEM tensor\n  Tensor gA = make_tensor(make_gmem_ptr(g_in), gmem_layout);\n\n  // Shared memory barriers use 64bits in SMEM for synchronization\n  uint64_t* bulk_copy_mbar = shared_storage.bulk_copy_mbar;\n\n  //\n  // Perform the BULK_COPY load\n  //\n\n  auto blkcp = Copy_Traits<SM90_BULK_COPY_AUTO>{};\n\n#if 0\n  if (thread0()) {\n    print(\"sA: \"); print(sA.data()); print(\" o \"); print(sA.layout()); print(\"\\n\");\n    print(\"gA: \"); print(gA.data()); print(\" o \"); print(gA.layout()); print(\"\\n\");\n  }\n#endif\n\n  // Set the bytes transferred in this transaction (may involve multiple issues)\n  constexpr int transaction_bytes = size(sA) * sizeof(T);\n\n  if (threadIdx.x == 0) {\n    /// Initialize shared memory barrier\n    bulk_copy_mbar[0] = 0;\n    initialize_barrier(bulk_copy_mbar[0], 1 /*numThreads*/);\n    set_barrier_transaction_bytes(bulk_copy_mbar[0], transaction_bytes);\n\n    copy(blkcp.with(bulk_copy_mbar[0]), gA, sA);\n  }\n  __syncthreads();\n\n  /// Wait on the shared memory barrier until the phase bit flips from kPhaseBit value\n  constexpr int kPhaseBit = 0;\n  wait_barrier(bulk_copy_mbar[0], kPhaseBit);\n\n#if 0\n  if (thread0()) {\n    print(sA);\n  }\n#endif\n\n  //\n  // Write out trivially\n  //\n\n  Tensor gA_out = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n\n  // Output smem -> gmem\n  for (int i = threadIdx.x; i < size(sA); i += blockDim.x) {\n    gA_out(i) = sA(i);\n  }\n}\n\ntemplate <class T, class GLayout, class SLayout>\nvoid run_and_validate(GLayout gmem_layout,\n                      SLayout smem_layout)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout));\n  for (size_t i = 0; i < h_in.size(); ++i) {\n    h_in[i] = static_cast<T>(int(i));\n  }\n\n  thrust::device_vector<T> d_in = h_in;\n  thrust::device_vector<T> d_out(d_in.size(), T(-1));\n\n  int32_t smem_size = static_cast<int32_t>(sizeof(SharedStorage<T, decltype(smem_layout)>));\n  bulk_copy_test_device_cute<<<1, 128, smem_size>>>(thrust::raw_pointer_cast(d_in.data()),\n                                                    thrust::raw_pointer_cast(d_out.data()),\n                                                    gmem_layout,\n                                                    smem_layout);\n  // Transfering results back to host\n  thrust::host_vector<T> h_out = d_out;\n\n  // Validate the results\n  for (int i = 0; i < cute::size(gmem_layout); ++i) {\n    int k = gmem_layout(i);\n    EXPECT_EQ(int(h_in[k]), int(h_out[k]));\n  }\n}\n\n// }  // namespace\n\nTEST(SM90_CuTe_BLKCP, ColMajor)\n{\n\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenColMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, RowMajor)\n{\n\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenRowMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, NonCompact)\n{\n\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = make_layout(Shape<Shape<_16,_2>, Shape<_4,_8>>{}, Stride<Stride<_1,_64>,Stride<_16,_128>>{});\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_64,_1>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n}\n#endif // #if CUDA_12_0_SM90_FEATURES_SUPPORTED\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n  \\brief Basic tests for BULK_COPY usage with various layouts.\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class ElementType, class SmemLayout>\nstruct SharedStorage {\n  cute::array_aligned<ElementType, cute::cosize_v<SmemLayout>> smem;\n  cute::uint64_t bulk_copy_mbar[1];\n};\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\ntemplate <class T, class GmemLayout, class SmemLayout>\n__global__ void\nbulk_copy_test_device_cute(T const* g_in,\n                           T      * g_out,\n                           GmemLayout gmem_layout,\n                           SmemLayout smem_layout)\n{\n  // Use Shared Storage structure to allocate and distribute aligned SMEM addresses\n  extern __shared__ char shared_memory[];\n  using SharedStorage = SharedStorage<T, SmemLayout>;\n  SharedStorage& shared_storage = *reinterpret_cast<SharedStorage*>(shared_memory);\n\n  // Construct SMEM tensor\n  Tensor sA = make_tensor(make_smem_ptr(shared_storage.smem.data()), smem_layout);\n  // Construct the GMEM tensor\n  Tensor gA = make_tensor(make_gmem_ptr(g_in), gmem_layout);\n\n  // Shared memory barriers use 64bits in SMEM for synchronization\n  uint64_t* bulk_copy_mbar = shared_storage.bulk_copy_mbar;\n\n  //\n  // Perform the BULK_COPY load\n  //\n\n  auto blkcp = Copy_Traits<SM90_BULK_COPY_AUTO>{};\n\n#if 0\n  if (thread0()) {\n    print(\"sA: \"); print(sA.data()); print(\" o \"); print(sA.layout()); print(\"\\n\");\n    print(\"gA: \"); print(gA.data()); print(\" o \"); print(gA.layout()); print(\"\\n\");\n  }\n#endif\n\n  // Set the bytes transferred in this transaction (may involve multiple issues)\n  constexpr int transaction_bytes = size(sA) * sizeof(T);\n\n  if (threadIdx.x == 0) {\n    /// Initialize shared memory barrier\n    bulk_copy_mbar[0] = 0;\n    initialize_barrier(bulk_copy_mbar[0], 1 /*numThreads*/);\n    set_barrier_transaction_bytes(bulk_copy_mbar[0], transaction_bytes);\n\n    copy(blkcp.with(bulk_copy_mbar[0]), gA, sA);\n  }\n  __syncthreads();\n\n  /// Wait on the shared memory barrier until the phase bit flips from kPhaseBit value\n  constexpr int kPhaseBit = 0;\n  wait_barrier(bulk_copy_mbar[0], kPhaseBit);\n\n#if 0\n  if (thread0()) {\n    print(sA);\n  }\n#endif\n\n  //\n  // Write out trivially\n  //\n\n  Tensor gA_out = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n\n  // Output smem -> gmem\n  for (int i = threadIdx.x; i < size(sA); i += blockDim.x) {\n    gA_out(i) = sA(i);\n  }\n}\n\ntemplate <class T, class GLayout, class SLayout>\nvoid run_and_validate(GLayout gmem_layout,\n                      SLayout smem_layout)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout));\n  for (size_t i = 0; i < h_in.size(); ++i) {\n    h_in[i] = static_cast<T>(int(i));\n  }\n\n  thrust::device_vector<T> d_in = h_in;\n  thrust::device_vector<T> d_out(d_in.size(), T(-1));\n\n  int32_t smem_size = static_cast<int32_t>(sizeof(SharedStorage<T, decltype(smem_layout)>));\n  bulk_copy_test_device_cute<<<1, 128, smem_size>>>(thrust::raw_pointer_cast(d_in.data()),\n                                                    thrust::raw_pointer_cast(d_out.data()),\n                                                    gmem_layout,\n                                                    smem_layout);\n  // Transfering results back to host\n  thrust::host_vector<T> h_out = d_out;\n\n  // Validate the results\n  for (int i = 0; i < cute::size(gmem_layout); ++i) {\n    int k = gmem_layout(i);\n    EXPECT_EQ(int(h_in[k]), int(h_out[k]));\n  }\n}\n\n// }  // namespace\n\nTEST(SM90_CuTe_BLKCP, ColMajor)\n{\n\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenColMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, RowMajor)\n{\n\n  auto smem_layout = make_layout(Shape<_32,_32>{}, GenRowMajor{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n}\n\nTEST(SM90_CuTe_BLKCP, NonCompact)\n{\n\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_1,Int<48>>{});\n  auto gmem_layout = make_layout(Shape<Shape<_16,_2>, Shape<_4,_8>>{}, Stride<Stride<_1,_64>,Stride<_16,_128>>{});\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n  {\n  auto smem_layout = make_layout(Shape<_32,_32>{}, Stride<_64,_1>{});\n  auto gmem_layout = smem_layout;\n  run_and_validate<    int8_t>(gmem_layout, smem_layout);\n  run_and_validate<    half_t>(gmem_layout, smem_layout);\n  run_and_validate<tfloat32_t>(gmem_layout, smem_layout);\n  }\n}\n#endif // #if CUDA_12_0_SM90_FEATURES_SUPPORTED\n", "source": "/kaggle/working/cutlass/test/unit/cute/hopper/bulk_load.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/copy_sm90.hpp>\n\nusing namespace cute;\n\ntemplate<class T>\n__global__ void\nstsm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  constexpr int count = sizeof(T) / 4;\n  int tid = threadIdx.x;\n  int stride = blockDim.x;\n\n  // load input gmem -> rmem\n  uint32_t reg[count];\n  for (int i = 0; i < (sizeof(T) / 4); i++) {\n    reg[i] = reinterpret_cast<uint32_t*>(g_in)[tid + (stride * i)];\n  }\n\n  __shared__ uint32_t smem[32 * count];\n\n  // load rmem -> smem using STSM\n  uint128_t* smem_ptr = reinterpret_cast<uint128_t*>(smem) + tid;\n  T*         rmem_ptr = reinterpret_cast<T*>(reg);\n  cute::copy_stsm(rmem_ptr, smem_ptr);\n\n  __syncthreads();\n\n  // store output smem -> gmem\n  for (int i = 0; i < (sizeof(T) / 4); i++) {\n    reinterpret_cast<uint32_t*>(g_out)[tid + (stride * i)] = smem[tid + (stride * i)];\n  }\n}\n\ntemplate <class TiledCopy, class SmemLayout>\n__global__ void\nstsm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, SmemLayout smem_layout)\n{\n  using namespace cute;\n\n  __shared__ uint16_t smem[size(smem_layout)];\n\n  Tensor t_g_in  = make_tensor(make_gmem_ptr(g_in),  smem_layout);\n  Tensor t_g_out = make_tensor(make_gmem_ptr(g_out), smem_layout);\n  Tensor t_smem  = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  int tid = threadIdx.x;\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  Tensor tXgX = thr_copy.partition_S(t_g_in);   // (V,M,N)\n  Tensor tXsX = thr_copy.partition_D(t_smem);   // (V,M,N)\n\n  Tensor tXrX = make_tensor<uint16_t>(shape(tXgX)); // (V,M,N)\n  clear(tXrX);    // Just to make sure\n\n/*\n  if (thread0()) {\n    print(\"tXsX: \" ); print(tXsX.layout()); print(\"\\n\");\n    print(\"tXgX: \" ); print(tXgX.layout()); print(\"\\n\");\n    print(\"tXrX: \" ); print(tXrX.layout()); print(\"\\n\");\n  }\n*/\n\n  // Load input gmem -> rmem\n  copy(tXgX, tXrX);\n\n  // Copy rmem -> smem via tiled_copy (STSM, STS)\n  copy(tiled_copy, tXrX, tXsX);\n\n  // Output smem -> gmem\n  for (int i = tid; i < size(t_smem); i += size(tiled_copy)) {\n    t_g_out(i) = t_smem(i);\n  }\n}\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_CuTe_Hopper, Stsm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  }\n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // STSM 1x (32b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint32_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 32; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 1x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // STSM 2x (64b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint64_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 64; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 2x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // STSM 4x (128b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint128_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 128; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 4x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe STSM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x1_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x1_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x2_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x2_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x4_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x4_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved STSM.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x1_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x1_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x2_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x2_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x4_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x4_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 STSM.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x2_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_2,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x2_STSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x4_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_4,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x4_STSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x8_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_8,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x8_STSM_T SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n#endif\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/arch/copy_sm90.hpp>\n\nusing namespace cute;\n\ntemplate<class T>\n__global__ void\nstsm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  constexpr int count = sizeof(T) / 4;\n  int tid = threadIdx.x;\n  int stride = blockDim.x;\n\n  // load input gmem -> rmem\n  uint32_t reg[count];\n  for (int i = 0; i < (sizeof(T) / 4); i++) {\n    reg[i] = reinterpret_cast<uint32_t*>(g_in)[tid + (stride * i)];\n  }\n\n  __shared__ uint32_t smem[32 * count];\n\n  // load rmem -> smem using STSM\n  uint128_t* smem_ptr = reinterpret_cast<uint128_t*>(smem) + tid;\n  T*         rmem_ptr = reinterpret_cast<T*>(reg);\n  cute::copy_stsm(rmem_ptr, smem_ptr);\n\n  __syncthreads();\n\n  // store output smem -> gmem\n  for (int i = 0; i < (sizeof(T) / 4); i++) {\n    reinterpret_cast<uint32_t*>(g_out)[tid + (stride * i)] = smem[tid + (stride * i)];\n  }\n}\n\ntemplate <class TiledCopy, class SmemLayout>\n__global__ void\nstsm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, SmemLayout smem_layout)\n{\n  using namespace cute;\n\n  __shared__ uint16_t smem[size(smem_layout)];\n\n  Tensor t_g_in  = make_tensor(make_gmem_ptr(g_in),  smem_layout);\n  Tensor t_g_out = make_tensor(make_gmem_ptr(g_out), smem_layout);\n  Tensor t_smem  = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  int tid = threadIdx.x;\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  Tensor tXgX = thr_copy.partition_S(t_g_in);   // (V,M,N)\n  Tensor tXsX = thr_copy.partition_D(t_smem);   // (V,M,N)\n\n  Tensor tXrX = make_tensor<uint16_t>(shape(tXgX)); // (V,M,N)\n  clear(tXrX);    // Just to make sure\n\n/*\n  if (thread0()) {\n    print(\"tXsX: \" ); print(tXsX.layout()); print(\"\\n\");\n    print(\"tXgX: \" ); print(tXgX.layout()); print(\"\\n\");\n    print(\"tXrX: \" ); print(tXrX.layout()); print(\"\\n\");\n  }\n*/\n\n  // Load input gmem -> rmem\n  copy(tXgX, tXrX);\n\n  // Copy rmem -> smem via tiled_copy (STSM, STS)\n  copy(tiled_copy, tXrX, tXsX);\n\n  // Output smem -> gmem\n  for (int i = tid; i < size(t_smem); i += size(tiled_copy)) {\n    t_g_out(i) = t_smem(i);\n  }\n}\n\n#if CUDA_12_0_SM90_FEATURES_SUPPORTED\nTEST(SM90_CuTe_Hopper, Stsm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  }\n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // STSM 1x (32b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint32_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 32; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 1x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // STSM 2x (64b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint64_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 64; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 2x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // STSM 4x (128b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  stsm_test_device<uint128_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 128; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"STSM 4x stsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe STSM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x1_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x1_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x2_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x2_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x4_STSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x4_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved STSM.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x1_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x1_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x2_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x2_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U32x4_STSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x4_STSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 STSM.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x2_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_2,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x2_STSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x4_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_4,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x4_STSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM90_U16x8_STSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_8,_1>>{});\n\n  stsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x8_STSM_T SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n#endif\n", "source": "/kaggle/working/cutlass/test/unit/cute/hopper/stsm.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\n__global__ void\ntest(double const* g_in, double* g_out)\n{\n  extern __shared__ double smem[];\n\n  smem[threadIdx.x] = g_in[threadIdx.x];\n\n  __syncthreads();\n\n  g_out[threadIdx.x] = 2 * smem[threadIdx.x];\n}\n\n__global__ void\ntest2(double const* g_in, double* g_out)\n{\n  using namespace cute;\n\n  extern __shared__ double smem[];\n\n  auto s_tensor = make_tensor(make_smem_ptr(smem + threadIdx.x), Int<1>{});\n  auto g_tensor = make_tensor(make_gmem_ptr(g_in + threadIdx.x), Int<1>{});\n\n  copy(g_tensor, s_tensor);\n\n  __syncthreads();\n\n  g_out[threadIdx.x] = 2 * smem[threadIdx.x];\n}\n\nTEST(SM80_CuTe_Ampere, CpSync)\n{\n  constexpr int count = 32;\n  thrust::host_vector<double> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = double(i);\n  }\n\n  thrust::device_vector<double> d_in(h_in);\n\n  thrust::device_vector<double> d_out(count, -1);\n  test<<<1, count, sizeof(double) * count>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<double> h_result = d_out;\n\n  thrust::device_vector<double> d_out_cp_async(count, -2);\n  test2<<<1, count, sizeof(double) * count>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out_cp_async.data()));\n  thrust::host_vector<double> h_result_cp_async = d_out_cp_async;\n\n  for (int i = 0; i < count; ++i) {\n    EXPECT_EQ(h_result[i], h_result_cp_async[i]);\n  }\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\n__global__ void\ntest(double const* g_in, double* g_out)\n{\n  extern __shared__ double smem[];\n\n  smem[threadIdx.x] = g_in[threadIdx.x];\n\n  __syncthreads();\n\n  g_out[threadIdx.x] = 2 * smem[threadIdx.x];\n}\n\n__global__ void\ntest2(double const* g_in, double* g_out)\n{\n  using namespace cute;\n\n  extern __shared__ double smem[];\n\n  auto s_tensor = make_tensor(make_smem_ptr(smem + threadIdx.x), Int<1>{});\n  auto g_tensor = make_tensor(make_gmem_ptr(g_in + threadIdx.x), Int<1>{});\n\n  copy(g_tensor, s_tensor);\n\n  __syncthreads();\n\n  g_out[threadIdx.x] = 2 * smem[threadIdx.x];\n}\n\nTEST(SM80_CuTe_Ampere, CpSync)\n{\n  constexpr int count = 32;\n  thrust::host_vector<double> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = double(i);\n  }\n\n  thrust::device_vector<double> d_in(h_in);\n\n  thrust::device_vector<double> d_out(count, -1);\n  test<<<1, count, sizeof(double) * count>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<double> h_result = d_out;\n\n  thrust::device_vector<double> d_out_cp_async(count, -2);\n  test2<<<1, count, sizeof(double) * count>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out_cp_async.data()));\n  thrust::host_vector<double> h_result_cp_async = d_out_cp_async;\n\n  for (int i = 0; i < count; ++i) {\n    EXPECT_EQ(h_result[i], h_result_cp_async[i]);\n  }\n}\n", "source": "/kaggle/working/cutlass/test/unit/cute/ampere/cp_sync.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n#include <tuple>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/swizzle.hpp> // cute::Swizzle\n#include <cute/swizzle_layout.hpp> // cute::compose(cute::Swizzle)\n#include <cute/numeric/numeric_types.hpp>\n#include <cute/atom/copy_traits_sm80.hpp>\n\nusing namespace cute;\n\nnamespace cooperative_copy_mode {\n  struct global_shared {};\n  struct global_global {};\n  struct shared_shared {};\n}\n\n// gs --> global to/from shared\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class GMemLayout, class SMemLayout>\n__device__ void\ncooperative_copy_default_gs(T const* g_in, T* g_out, GMemLayout const& gmem_layout, SMemLayout const& smem_layout)\n{\n  using namespace cute;\n  extern __shared__ uint128_t smem_buf[];\n  // Cast smem_buf to smem_uint8_ptr and move it by MaxVecBits bits\n  // This is to make sure tests pass on pointer aligned to MaxVecBits bits\n  uint8_t* smem_uint8_ptr = reinterpret_cast<uint8_t*>(smem_buf) + (MaxVecBits/8);\n  T* smem = reinterpret_cast<T*>(smem_uint8_ptr);\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  gmem_layout);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n  Tensor s_tensor     = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, g_in_tensor, s_tensor, AutoCopyAsync{});\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  if(thread0()) {\n    for(int i = 0; i < size(s_tensor); ++i) {\n      s_tensor(i) += T(i);\n    }\n  }\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, s_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\n// ss --> shared to shared\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__device__ void\ncooperative_copy_default_ss(T const* g_in, T* g_out, Layout1 const& layout1, Layout2 const& layout2)\n{\n  using namespace cute;\n  extern __shared__ uint128_t smem_buf[];\n  // Cast smem_buf to smem_uint8_ptr and move it by MaxVecBits bits\n  // This is to make sure tests pass on pointer aligned to MaxVecBits bits\n  T* smem1 = reinterpret_cast<T*>(smem_buf);\n  uint8_t* smem2_uint8_ptr = reinterpret_cast<uint8_t*>(smem_buf) + (MaxVecBits/8);\n  T* smem2 = reinterpret_cast<T*>(smem2_uint8_ptr) + cute::cosize(layout2);\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  layout1);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), layout2);\n\n  Tensor s1_tensor    = make_tensor(make_smem_ptr(smem1), layout2);\n  Tensor s2_tensor    = make_tensor(make_smem_ptr(smem2), layout1);\n\n  cooperative_copy<ThreadBlockSize,  cute::sizeof_bits_v<T>>(threadIdx.x, g_in_tensor, s1_tensor, AutoCopyAsync{});\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  if(thread0()) {\n    for(int i = 0; i < size(s1_tensor); ++i) {\n      s1_tensor(i) += T(i);\n    }\n  }\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, s1_tensor, s2_tensor, AutoCopyAsync{});\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize,  cute::sizeof_bits_v<T>>(threadIdx.x, s2_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\n// gg --> global to global\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__device__ void\ncooperative_copy_default_gg(T const* g_in, T* g_out, Layout1 const& layout1, Layout2 const& layout2)\n{\n  using namespace cute;\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  layout1);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), layout2);\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, g_in_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\ntemplate <class Mode, int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__global__ void\ncooperative_copy_default_kernel(T const* g_in, T* g_out, Layout1 const layout1, Layout2 const layout2)\n{\n  if constexpr(std::is_same_v<Mode, cooperative_copy_mode::global_shared>) {\n    cooperative_copy_default_gs<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  } else if constexpr (std::is_same_v<Mode, cooperative_copy_mode::global_global>) {\n    cooperative_copy_default_gg<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  } else if constexpr (std::is_same_v<Mode, cooperative_copy_mode::shared_shared>) {\n    cooperative_copy_default_ss<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  }\n}\n\n// Mode - defines memory types of src and dst in cooperative_copy operation\n// MaxVecBits - defines max vectorization in cooperative_copy operation, and enforces that\n//              alignment on used pointers to ensure correct testing\ntemplate <class Mode, int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\nvoid test_cooperative_copy_default(Layout1 const& layout1, Layout2 const& layout2)\n{\n  using value_type = T;\n  CUTE_STATIC_ASSERT_V(cute::size(layout1) == cute::size(layout2));\n\n  auto gmem_layout_in  = layout1;\n  auto gmem_layout_out = cute::conditional_return<std::is_same_v<Mode, cooperative_copy_mode::global_shared>>(layout1, layout2);\n\n#if 0\n  print(\"   \"); print(\"layout1:     \"); print(layout1); print(\"\\n\");\n  print(\"   \"); print(\"layout2:     \"); print(layout2); print(\"\\n\");\n  print(\"   \"); print(\"threads:     \"); print(ThreadBlockSize); print(\"\\n\");\n  print(\"   \"); print(\"maxvecbits:  \"); print(MaxVecBits); print(\"\\n\");\n#endif\n\n  if constexpr (MaxVecBits < cute::sizeof_bits_v<value_type>) {\n    GTEST_SKIP() << \"Skipping test since MaxVecBits (=\" << MaxVecBits\n                 << \") < cute::sizeof_bits_v<value_type> (=\" << cute::sizeof_bits_v<value_type> << \")\";\n  } else {\n    constexpr auto max_vec_bytes = MaxVecBits / 8;\n    static_assert((max_vec_bytes % sizeof(T)) == 0);\n\n    uint32_t count = cute::cosize(gmem_layout_in);\n    // Extra elements to force MaxVecBits alignment in global memory\n    uint32_t extra_elements = max_vec_bytes / sizeof(value_type);\n\n    // Allocate\n    thrust::host_vector<value_type> h_in (count + extra_elements);\n    thrust::host_vector<value_type> h_out(count + extra_elements);\n\n    // Initialize\n    Tensor h_in_tensor  = make_tensor(h_in.data()  + extra_elements, gmem_layout_in);\n    Tensor h_out_tensor = make_tensor(h_out.data() + extra_elements, gmem_layout_out);\n    for (int i = 0; i < cute::size(h_in_tensor); ++i) {\n      h_in_tensor(i)  = value_type(float(i));\n      // For global-to-global copy need to compare against the same value\n      h_out_tensor(i) = std::is_same_v<Mode, cooperative_copy_mode::global_global> ? value_type(float(i)) : value_type(float(2 * i));\n    }\n\n    // To GPU\n    thrust::device_vector<value_type> d_in = h_in;\n    thrust::device_vector<value_type> d_out(d_in.size(), value_type(float(-2)));\n\n    // Adds (MaxVecBits/8) bytes to shared memory as we'll move pointer by that many bytes inside the kernel to enforce\n    // alignment to (MaxVecBits/8) bytes\n    size_t shared_memory_bytes = (sizeof(value_type) * count) + max_vec_bytes;\n    shared_memory_bytes += std::is_same_v<Mode, cooperative_copy_mode::shared_shared> * (sizeof(value_type) * count);\n\n    // Launch\n    auto coop_copy = cooperative_copy_default_kernel<Mode, MaxVecBits, ThreadBlockSize, value_type, Layout1, Layout2>;\n    ASSERT_EQ(cudaFuncSetAttribute(coop_copy, cudaFuncAttributeMaxDynamicSharedMemorySize, static_cast<int>(shared_memory_bytes)), cudaSuccess);\n\n    auto d_in_ptr  = thrust::raw_pointer_cast(d_in.data()  + extra_elements);\n    auto d_out_ptr = thrust::raw_pointer_cast(d_out.data() + extra_elements);\n    coop_copy<<<1, ThreadBlockSize, shared_memory_bytes>>>(d_in_ptr, d_out_ptr, layout1, layout2);\n\n    cudaError_t result = cudaDeviceSynchronize();\n    if (result != cudaSuccess) {\n      cudaError_t error = cudaGetLastError();\n      FAIL() << \"Error at kernel sync: \" << cudaGetErrorString(error) << \"\\n\";\n    }\n\n    // Validate\n    thrust::host_vector<value_type> h_result        = d_out;\n    Tensor                          h_result_tensor = make_tensor(h_result.data() + extra_elements, gmem_layout_out);\n    for (int i = 0; i < cute::size(h_in_tensor); ++i) {\n      ASSERT_EQ(h_result_tensor(i), h_out_tensor(i))\n          << i << \" - result:\" << h_result_tensor(i) << \" expected:\" << h_out_tensor(i);\n    }\n  }\n}\n\ntemplate<class T>\nclass SM80_CuTe_Ampere;\n\ntemplate<class Mode, class MaxVecBits>\nclass SM80_CuTe_Ampere<std::tuple<Mode, MaxVecBits>>: public testing::Test\n{\npublic:\n  using mode = Mode;\n  static constexpr int max_vec_bits = MaxVecBits::value;\n};\n\ntypedef testing::Types<\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<16>>,\n\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<16>>,\n\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<16>>\n> CooperativeCopyModeMaxVecBitsList;\n\nTYPED_TEST_SUITE(SM80_CuTe_Ampere, CooperativeCopyModeMaxVecBitsList);\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault1D)\n{\n  using value_type = float;\n  constexpr uint32_t count = 512;\n  auto gmem_layout = make_layout(make_shape(Int<count>{}));\n  auto smem_layout = make_layout(make_shape(Int<count>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault1DFallback)\n{\n  using value_type = float;\n  constexpr uint32_t count = 99;\n  auto gmem_layout = make_layout(make_shape(Int<count>{}));\n  auto smem_layout = make_layout(make_shape(Int<count>{}));\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2D)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n#if 0\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DDynamicStrides)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DMixedStrides)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n#endif\n\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DFallback)\n{\n  using value_type = float;\n  constexpr uint32_t x = 37;\n  constexpr uint32_t y = 37;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DCustomStride)\n{\n  using value_type = float;\n  constexpr uint32_t x = 16;\n  constexpr uint32_t y = 16;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(Int<y>{}, Int<1>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(Int<1>{}, Int<x>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault3D)\n{\n  using value_type = cute::half_t;\n  constexpr uint32_t x = 8;\n  constexpr uint32_t y = 8;\n  constexpr uint32_t z = 16;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}, Int<z>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}, Int<z>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2Dto3D)\n{\n  using value_type = double;\n  constexpr uint32_t x = 16;\n  constexpr uint32_t y = 16;\n  constexpr uint32_t z = 4;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y*z>{}));\n  auto smem_layout = make_layout(make_shape(Int<z>{}, Int<y>{}, Int<x>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCustom1)\n{\n  using value_type = double;\n  auto gmem_layout = make_layout(\n    make_shape(Int<8>{}, make_shape(Int<2>{}, Int<2>{})),\n    make_stride(Int<2>{}, make_shape(Int<1>{}, Int<16>{}))\n  );\n  auto smem_layout = make_layout(\n    make_shape(Int<8>{}, Int<4>{}),\n    make_stride(Int<4>{}, Int<1>{})\n  );\n  constexpr uint32_t thread_block_size = 8;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCustom2)\n{\n  using value_type = float;\n  auto gmem_layout = make_layout(\n    make_shape(make_shape(Int<4>{}, Int<2>{}), make_shape(Int<2>{}, Int<2>{})),\n    make_stride(make_shape(Int<4>{}, Int<1>{}), make_shape(Int<16>{}, Int<2>{}))\n  );\n  auto smem_layout = make_layout(\n    make_shape(make_shape(Int<2>{}, Int<2>{}, Int<2>{}), make_shape(Int<2>{}, Int<2>{})),\n    make_stride(make_shape(Int<16>{}, Int<4>{}, Int<1>{}), make_shape(Int<8>{}, Int<2>{}))\n  );\n  constexpr uint32_t thread_block_size = 16;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle1)\n{\n  using value_type = float;\n  auto gmem_layout = Layout<Shape<_8, _64>, Stride<_64, _1>>{};\n  auto smem_layout = composition(Swizzle<3, 3, 3>{}, Layout<Shape<_8, _64>, Stride<_64, _1>>{});\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle2)\n{\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto smem_atom_layout = composition(Swizzle<3, 2, 3>{}, Layout<Shape<_8, _32>, Stride<_32, _1>>{});\n  auto smem_layout = tile_to_shape(\n      smem_atom_layout,\n      make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle3)\n{\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto smem_atom_layout = composition(Swizzle<2, 4, 3>{}, Layout<Shape<_16, _64>, Stride<_64, _1>>{});\n  auto smem_layout = tile_to_shape(\n      smem_atom_layout,\n      make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle4)\n{\n  using value_type = cute::half_t;\n  auto gmem_atom_layout = composition(Swizzle<3, 2, 3>{}, Layout<Shape<_8, _32>, Stride<_32, _1>>{});\n  auto smem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto gmem_layout = tile_to_shape(\n      gmem_atom_layout,\n      make_shape(shape<0>(smem_layout), shape<1>(smem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Needs coalescing to work on fast path\n// OK if we enforce slow path\n// Problem: Wrong condition when we select between slow and fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCoalesceToCompose)\n{\n  constexpr int m = 96;\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<m>{}), GenColMajor{});\n  auto smem_layout = make_layout(make_shape(Int<m>{}, Int<m>{}), GenColMajor{});\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n // Fast path (default): OK\n // Slow path (enforced): OK\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle5)\n {\n   constexpr int m = 64;\n   constexpr int n = 128;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   // auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{}));\n   auto smem_atom_layout =\n     composition(Swizzle<3,3,3>{},\n                 Layout<Shape < _8,_64>,\n                        Stride<_64, _1>>{});\n   auto smem_layout = tile_to_shape(\n     smem_atom_layout,\n     make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n   );\n\n   constexpr uint32_t thread_block_size = 128;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // If condition not strict enought will go to fast path\n // This test needs checking if CuTe can compose layouts\n // Fast path (default): fail\n // Slow path (enforced): Should go to vectorized naive path\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzleNaiveVectorizable)\n {\n   constexpr int m = 192;\n   constexpr int n = 64;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   // auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   auto smem_atom_layout =\n       composition(Swizzle<3,3,3>{},\n                   Layout<Shape <_64, _8>,\n                          Stride< _1,_64>>{});\n   auto smem_layout = tile_to_shape(\n     smem_atom_layout,\n     shape(gmem_layout)\n   );\n\n   constexpr uint32_t thread_block_size = 128;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: ok (chosen)\n // slow path: ok\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultRowMajorSmall)\n {\n   constexpr int m = 24;\n   constexpr int n = 8;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 64;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: doesn't apply\n // slow path: ok\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSlowPath)\n {\n   constexpr int m = 67;\n   constexpr int n = 67;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 64;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: doesn't apply\n // slow path: should vectorize\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzleSlowPathVectorize)\n {\n   constexpr int m = 68;\n   constexpr int n = 68;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 32;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopy48x48Swizzle)\n {\n   constexpr int m = 48;\n   constexpr int n = 48;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = composition(Swizzle<2,2,3>{},\n                                              Layout<Shape <Shape <_16,       _3, Int<48>>>,\n                                                     Stride<Stride< _1, Int<768>,     _16>>>{});\n\n   constexpr uint32_t thread_block_size = 8 * 32;\n   test_cooperative_copy_default<cooperative_copy_mode::shared_shared,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n#include <tuple>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n#include <cute/swizzle.hpp> // cute::Swizzle\n#include <cute/swizzle_layout.hpp> // cute::compose(cute::Swizzle)\n#include <cute/numeric/numeric_types.hpp>\n#include <cute/atom/copy_traits_sm80.hpp>\n\nusing namespace cute;\n\nnamespace cooperative_copy_mode {\n  struct global_shared {};\n  struct global_global {};\n  struct shared_shared {};\n}\n\n// gs --> global to/from shared\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class GMemLayout, class SMemLayout>\n__device__ void\ncooperative_copy_default_gs(T const* g_in, T* g_out, GMemLayout const& gmem_layout, SMemLayout const& smem_layout)\n{\n  using namespace cute;\n  extern __shared__ uint128_t smem_buf[];\n  // Cast smem_buf to smem_uint8_ptr and move it by MaxVecBits bits\n  // This is to make sure tests pass on pointer aligned to MaxVecBits bits\n  uint8_t* smem_uint8_ptr = reinterpret_cast<uint8_t*>(smem_buf) + (MaxVecBits/8);\n  T* smem = reinterpret_cast<T*>(smem_uint8_ptr);\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  gmem_layout);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), gmem_layout);\n  Tensor s_tensor     = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, g_in_tensor, s_tensor, AutoCopyAsync{});\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  if(thread0()) {\n    for(int i = 0; i < size(s_tensor); ++i) {\n      s_tensor(i) += T(i);\n    }\n  }\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, s_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\n// ss --> shared to shared\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__device__ void\ncooperative_copy_default_ss(T const* g_in, T* g_out, Layout1 const& layout1, Layout2 const& layout2)\n{\n  using namespace cute;\n  extern __shared__ uint128_t smem_buf[];\n  // Cast smem_buf to smem_uint8_ptr and move it by MaxVecBits bits\n  // This is to make sure tests pass on pointer aligned to MaxVecBits bits\n  T* smem1 = reinterpret_cast<T*>(smem_buf);\n  uint8_t* smem2_uint8_ptr = reinterpret_cast<uint8_t*>(smem_buf) + (MaxVecBits/8);\n  T* smem2 = reinterpret_cast<T*>(smem2_uint8_ptr) + cute::cosize(layout2);\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  layout1);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), layout2);\n\n  Tensor s1_tensor    = make_tensor(make_smem_ptr(smem1), layout2);\n  Tensor s2_tensor    = make_tensor(make_smem_ptr(smem2), layout1);\n\n  cooperative_copy<ThreadBlockSize,  cute::sizeof_bits_v<T>>(threadIdx.x, g_in_tensor, s1_tensor, AutoCopyAsync{});\n\n  cp_async_fence();\n  cp_async_wait<0>();\n  __syncthreads();\n\n  if(thread0()) {\n    for(int i = 0; i < size(s1_tensor); ++i) {\n      s1_tensor(i) += T(i);\n    }\n  }\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, s1_tensor, s2_tensor, AutoCopyAsync{});\n  __syncthreads();\n\n  cooperative_copy<ThreadBlockSize,  cute::sizeof_bits_v<T>>(threadIdx.x, s2_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\n// gg --> global to global\ntemplate <int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__device__ void\ncooperative_copy_default_gg(T const* g_in, T* g_out, Layout1 const& layout1, Layout2 const& layout2)\n{\n  using namespace cute;\n\n  Tensor g_in_tensor  = make_tensor(make_gmem_ptr(g_in),  layout1);\n  Tensor g_out_tensor = make_tensor(make_gmem_ptr(g_out), layout2);\n\n  cooperative_copy<ThreadBlockSize, MaxVecBits>(threadIdx.x, g_in_tensor, g_out_tensor, AutoCopyAsync{});\n}\n\ntemplate <class Mode, int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\n__global__ void\ncooperative_copy_default_kernel(T const* g_in, T* g_out, Layout1 const layout1, Layout2 const layout2)\n{\n  if constexpr(std::is_same_v<Mode, cooperative_copy_mode::global_shared>) {\n    cooperative_copy_default_gs<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  } else if constexpr (std::is_same_v<Mode, cooperative_copy_mode::global_global>) {\n    cooperative_copy_default_gg<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  } else if constexpr (std::is_same_v<Mode, cooperative_copy_mode::shared_shared>) {\n    cooperative_copy_default_ss<MaxVecBits, ThreadBlockSize>(g_in, g_out, layout1, layout2);\n  }\n}\n\n// Mode - defines memory types of src and dst in cooperative_copy operation\n// MaxVecBits - defines max vectorization in cooperative_copy operation, and enforces that\n//              alignment on used pointers to ensure correct testing\ntemplate <class Mode, int MaxVecBits, uint32_t ThreadBlockSize, class T, class Layout1, class Layout2>\nvoid test_cooperative_copy_default(Layout1 const& layout1, Layout2 const& layout2)\n{\n  using value_type = T;\n  CUTE_STATIC_ASSERT_V(cute::size(layout1) == cute::size(layout2));\n\n  auto gmem_layout_in  = layout1;\n  auto gmem_layout_out = cute::conditional_return<std::is_same_v<Mode, cooperative_copy_mode::global_shared>>(layout1, layout2);\n\n#if 0\n  print(\"   \"); print(\"layout1:     \"); print(layout1); print(\"\\n\");\n  print(\"   \"); print(\"layout2:     \"); print(layout2); print(\"\\n\");\n  print(\"   \"); print(\"threads:     \"); print(ThreadBlockSize); print(\"\\n\");\n  print(\"   \"); print(\"maxvecbits:  \"); print(MaxVecBits); print(\"\\n\");\n#endif\n\n  if constexpr (MaxVecBits < cute::sizeof_bits_v<value_type>) {\n    GTEST_SKIP() << \"Skipping test since MaxVecBits (=\" << MaxVecBits\n                 << \") < cute::sizeof_bits_v<value_type> (=\" << cute::sizeof_bits_v<value_type> << \")\";\n  } else {\n    constexpr auto max_vec_bytes = MaxVecBits / 8;\n    static_assert((max_vec_bytes % sizeof(T)) == 0);\n\n    uint32_t count = cute::cosize(gmem_layout_in);\n    // Extra elements to force MaxVecBits alignment in global memory\n    uint32_t extra_elements = max_vec_bytes / sizeof(value_type);\n\n    // Allocate\n    thrust::host_vector<value_type> h_in (count + extra_elements);\n    thrust::host_vector<value_type> h_out(count + extra_elements);\n\n    // Initialize\n    Tensor h_in_tensor  = make_tensor(h_in.data()  + extra_elements, gmem_layout_in);\n    Tensor h_out_tensor = make_tensor(h_out.data() + extra_elements, gmem_layout_out);\n    for (int i = 0; i < cute::size(h_in_tensor); ++i) {\n      h_in_tensor(i)  = value_type(float(i));\n      // For global-to-global copy need to compare against the same value\n      h_out_tensor(i) = std::is_same_v<Mode, cooperative_copy_mode::global_global> ? value_type(float(i)) : value_type(float(2 * i));\n    }\n\n    // To GPU\n    thrust::device_vector<value_type> d_in = h_in;\n    thrust::device_vector<value_type> d_out(d_in.size(), value_type(float(-2)));\n\n    // Adds (MaxVecBits/8) bytes to shared memory as we'll move pointer by that many bytes inside the kernel to enforce\n    // alignment to (MaxVecBits/8) bytes\n    size_t shared_memory_bytes = (sizeof(value_type) * count) + max_vec_bytes;\n    shared_memory_bytes += std::is_same_v<Mode, cooperative_copy_mode::shared_shared> * (sizeof(value_type) * count);\n\n    // Launch\n    auto coop_copy = cooperative_copy_default_kernel<Mode, MaxVecBits, ThreadBlockSize, value_type, Layout1, Layout2>;\n    ASSERT_EQ(hipFuncSetAttribute(reinterpret_cast<const void*>(coop_copy), hipFuncAttributeMaxDynamicSharedMemorySize, static_cast<int>(shared_memory_bytes)), hipSuccess);\n\n    auto d_in_ptr  = thrust::raw_pointer_cast(d_in.data()  + extra_elements);\n    auto d_out_ptr = thrust::raw_pointer_cast(d_out.data() + extra_elements);\n    coop_copy<<<1, ThreadBlockSize, shared_memory_bytes>>>(d_in_ptr, d_out_ptr, layout1, layout2);\n\n    hipError_t result = hipDeviceSynchronize();\n    if (result != hipSuccess) {\n      hipError_t error = hipGetLastError();\n      FAIL() << \"Error at kernel sync: \" << hipGetErrorString(error) << \"\\n\";\n    }\n\n    // Validate\n    thrust::host_vector<value_type> h_result        = d_out;\n    Tensor                          h_result_tensor = make_tensor(h_result.data() + extra_elements, gmem_layout_out);\n    for (int i = 0; i < cute::size(h_in_tensor); ++i) {\n      ASSERT_EQ(h_result_tensor(i), h_out_tensor(i))\n          << i << \" - result:\" << h_result_tensor(i) << \" expected:\" << h_out_tensor(i);\n    }\n  }\n}\n\ntemplate<class T>\nclass SM80_CuTe_Ampere;\n\ntemplate<class Mode, class MaxVecBits>\nclass SM80_CuTe_Ampere<std::tuple<Mode, MaxVecBits>>: public testing::Test\n{\npublic:\n  using mode = Mode;\n  static constexpr int max_vec_bits = MaxVecBits::value;\n};\n\ntypedef testing::Types<\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::global_shared, cute::Int<16>>,\n\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::global_global, cute::Int<16>>,\n\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<128>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<64>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<32>>,\n  std::tuple<cooperative_copy_mode::shared_shared, cute::Int<16>>\n> CooperativeCopyModeMaxVecBitsList;\n\nTYPED_TEST_SUITE(SM80_CuTe_Ampere, CooperativeCopyModeMaxVecBitsList);\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault1D)\n{\n  using value_type = float;\n  constexpr uint32_t count = 512;\n  auto gmem_layout = make_layout(make_shape(Int<count>{}));\n  auto smem_layout = make_layout(make_shape(Int<count>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault1DFallback)\n{\n  using value_type = float;\n  constexpr uint32_t count = 99;\n  auto gmem_layout = make_layout(make_shape(Int<count>{}));\n  auto smem_layout = make_layout(make_shape(Int<count>{}));\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2D)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n#if 0\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DDynamicStrides)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DMixedStrides)\n{\n  using value_type = float;\n  constexpr uint32_t x = 32;\n  constexpr uint32_t y = 32;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(1, x));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n#endif\n\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DFallback)\n{\n  using value_type = float;\n  constexpr uint32_t x = 37;\n  constexpr uint32_t y = 37;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2DCustomStride)\n{\n  using value_type = float;\n  constexpr uint32_t x = 16;\n  constexpr uint32_t y = 16;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(Int<y>{}, Int<1>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}), make_stride(Int<1>{}, Int<x>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault3D)\n{\n  using value_type = cute::half_t;\n  constexpr uint32_t x = 8;\n  constexpr uint32_t y = 8;\n  constexpr uint32_t z = 16;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}, Int<z>{}));\n  auto smem_layout = make_layout(make_shape(Int<x>{}, Int<y>{}, Int<z>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefault2Dto3D)\n{\n  using value_type = double;\n  constexpr uint32_t x = 16;\n  constexpr uint32_t y = 16;\n  constexpr uint32_t z = 4;\n  auto gmem_layout = make_layout(make_shape(Int<x>{}, Int<y*z>{}));\n  auto smem_layout = make_layout(make_shape(Int<z>{}, Int<y>{}, Int<x>{}));\n  constexpr uint32_t thread_block_size = 64;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCustom1)\n{\n  using value_type = double;\n  auto gmem_layout = make_layout(\n    make_shape(Int<8>{}, make_shape(Int<2>{}, Int<2>{})),\n    make_stride(Int<2>{}, make_shape(Int<1>{}, Int<16>{}))\n  );\n  auto smem_layout = make_layout(\n    make_shape(Int<8>{}, Int<4>{}),\n    make_stride(Int<4>{}, Int<1>{})\n  );\n  constexpr uint32_t thread_block_size = 8;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCustom2)\n{\n  using value_type = float;\n  auto gmem_layout = make_layout(\n    make_shape(make_shape(Int<4>{}, Int<2>{}), make_shape(Int<2>{}, Int<2>{})),\n    make_stride(make_shape(Int<4>{}, Int<1>{}), make_shape(Int<16>{}, Int<2>{}))\n  );\n  auto smem_layout = make_layout(\n    make_shape(make_shape(Int<2>{}, Int<2>{}, Int<2>{}), make_shape(Int<2>{}, Int<2>{})),\n    make_stride(make_shape(Int<16>{}, Int<4>{}, Int<1>{}), make_shape(Int<8>{}, Int<2>{}))\n  );\n  constexpr uint32_t thread_block_size = 16;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle1)\n{\n  using value_type = float;\n  auto gmem_layout = Layout<Shape<_8, _64>, Stride<_64, _1>>{};\n  auto smem_layout = composition(Swizzle<3, 3, 3>{}, Layout<Shape<_8, _64>, Stride<_64, _1>>{});\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle2)\n{\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto smem_atom_layout = composition(Swizzle<3, 2, 3>{}, Layout<Shape<_8, _32>, Stride<_32, _1>>{});\n  auto smem_layout = tile_to_shape(\n      smem_atom_layout,\n      make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast Path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle3)\n{\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto smem_atom_layout = composition(Swizzle<2, 4, 3>{}, Layout<Shape<_16, _64>, Stride<_64, _1>>{});\n  auto smem_layout = tile_to_shape(\n      smem_atom_layout,\n      make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle4)\n{\n  using value_type = cute::half_t;\n  auto gmem_atom_layout = composition(Swizzle<3, 2, 3>{}, Layout<Shape<_8, _32>, Stride<_32, _1>>{});\n  auto smem_layout = make_layout(make_shape(Int<64>{}, Int<64>{}));\n  auto gmem_layout = tile_to_shape(\n      gmem_atom_layout,\n      make_shape(shape<0>(smem_layout), shape<1>(smem_layout))\n  );\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n// Needs coalescing to work on fast path\n// OK if we enforce slow path\n// Problem: Wrong condition when we select between slow and fast path\nTYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultCoalesceToCompose)\n{\n  constexpr int m = 96;\n  using value_type = cute::half_t;\n  auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<m>{}), GenColMajor{});\n  auto smem_layout = make_layout(make_shape(Int<m>{}, Int<m>{}), GenColMajor{});\n  constexpr uint32_t thread_block_size = 128;\n  test_cooperative_copy_default<typename TestFixture::mode,\n                                TestFixture::max_vec_bits,\n                                thread_block_size,\n                                value_type>(gmem_layout, smem_layout);\n}\n\n // Fast path (default): OK\n // Slow path (enforced): OK\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzle5)\n {\n   constexpr int m = 64;\n   constexpr int n = 128;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   // auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{}));\n   auto smem_atom_layout =\n     composition(Swizzle<3,3,3>{},\n                 Layout<Shape < _8,_64>,\n                        Stride<_64, _1>>{});\n   auto smem_layout = tile_to_shape(\n     smem_atom_layout,\n     make_shape(shape<0>(gmem_layout), shape<1>(gmem_layout))\n   );\n\n   constexpr uint32_t thread_block_size = 128;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // If condition not strict enought will go to fast path\n // This test needs checking if CuTe can compose layouts\n // Fast path (default): fail\n // Slow path (enforced): Should go to vectorized naive path\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzleNaiveVectorizable)\n {\n   constexpr int m = 192;\n   constexpr int n = 64;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   // auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenColMajor{});\n   auto smem_atom_layout =\n       composition(Swizzle<3,3,3>{},\n                   Layout<Shape <_64, _8>,\n                          Stride< _1,_64>>{});\n   auto smem_layout = tile_to_shape(\n     smem_atom_layout,\n     shape(gmem_layout)\n   );\n\n   constexpr uint32_t thread_block_size = 128;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: ok (chosen)\n // slow path: ok\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultRowMajorSmall)\n {\n   constexpr int m = 24;\n   constexpr int n = 8;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 64;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: doesn't apply\n // slow path: ok\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSlowPath)\n {\n   constexpr int m = 67;\n   constexpr int n = 67;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 64;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n // fast path: doesn't apply\n // slow path: should vectorize\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopyDefaultSwizzleSlowPathVectorize)\n {\n   constexpr int m = 68;\n   constexpr int n = 68;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n\n   constexpr uint32_t thread_block_size = 32;\n   test_cooperative_copy_default<typename TestFixture::mode,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n\n TYPED_TEST(SM80_CuTe_Ampere, CooperativeCopy48x48Swizzle)\n {\n   constexpr int m = 48;\n   constexpr int n = 48;\n   using value_type = cute::half_t;\n   auto gmem_layout = make_layout(make_shape(Int<m>{}, Int<n>{}), GenRowMajor{});\n   auto smem_layout = composition(Swizzle<2,2,3>{},\n                                              Layout<Shape <Shape <_16,       _3, Int<48>>>,\n                                                     Stride<Stride< _1, Int<768>,     _16>>>{});\n\n   constexpr uint32_t thread_block_size = 8 * 32;\n   test_cooperative_copy_default<cooperative_copy_mode::shared_shared,\n                                 TestFixture::max_vec_bits,\n                                 thread_block_size,\n                                 value_type>(gmem_layout, smem_layout);\n }\n", "source": "/kaggle/working/cutlass/test/unit/cute/ampere/cooperative_copy.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include <cute/atom/copy_traits_sm75.hpp>\n\n\nusing namespace cute;\n\ntemplate <class T>\n__global__ void\nldsm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  constexpr int count = sizeof(T) / 4;\n  int tid = threadIdx.x;\n  int stride = blockDim.x;\n\n  // load input gmem -> smem\n  __shared__ uint32_t smem[32 * count];\n  for (int i = 0; i < count; ++i) {\n    smem[tid + (stride * i)] = reinterpret_cast<uint32_t*>(g_in)[tid + (stride * i)];\n  }\n\n  __syncthreads();\n\n  uint32_t reg[count];\n  for (int i = 0; i < count; ++i) {\n    reg[i] = 0;\n  }\n\n  // load smem -> rmem using LDSM\n  uint128_t* smem_ptr = reinterpret_cast<uint128_t*>(smem) + tid;\n  T*         rmem_ptr = reinterpret_cast<T*>(reg);\n  cute::copy_ldsm(smem_ptr, rmem_ptr);\n\n  // store output rmem -> gmem\n  for (int i = 0; i < count; ++i) {\n    reinterpret_cast<uint32_t*>(g_out)[tid + (stride * i)] = reg[i];\n  }\n}\n\ntemplate <class TiledCopy, class SmemLayout>\n__global__ void\nldsm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, SmemLayout smem_layout)\n{\n  using namespace cute;\n\n  __shared__ uint16_t smem[size(smem_layout)];\n\n  auto t_g_in  = make_tensor(make_gmem_ptr(g_in),  smem_layout);\n  auto t_g_out = make_tensor(make_gmem_ptr(g_out), smem_layout);\n  auto t_smem  = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  int tid = threadIdx.x;\n\n  // Load input gmem -> smem\n  for (int i = tid; i < size(t_smem); i += size(tiled_copy)) {\n    t_smem(i) = t_g_in(i);\n  }\n\n  __syncthreads();\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  auto tXsX = thr_copy.partition_S(t_smem);   // (V,M,N)\n  auto tXgX = thr_copy.partition_D(t_g_out);  // (V,M,N)\n\n  auto tXrX = make_tensor<uint16_t>(shape(tXgX)); // (V,M,N)\n  clear(tXrX);  // Just to make sure\n\n/*\n  if (thread0()) {\n    print(\"tXsX: \" ); print(tXsX.layout()); print(\"\\n\");\n    print(\"tXgX: \" ); print(tXgX.layout()); print(\"\\n\");\n    print(\"tXrX: \" ); print(tXrX.layout()); print(\"\\n\");\n  }\n*/\n\n  // Copy smem -> rmem via tiled_copy (LDSM, LDS)\n  copy(tiled_copy, tXsX, tXrX);\n\n  // Output rmem -> gmem\n  copy(tXrX, tXgX);\n}\n\n\nTEST(SM80_CuTe_Ampere, Ldsm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  }\n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // LDSM 1x (32b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint32_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 32; ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 1x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // LDSM 2x (64b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint64_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 64; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 2x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // LDSM 4x (128b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint128_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 128; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 4x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe LDSM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x1_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x2_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x2_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x4_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x4_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i] , h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved LDS.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x1_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x2_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x2_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x4_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x4_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 LDS.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x2_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_2,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i],  h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x2_LDSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x4_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_4,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i],  h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x4_LDSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x8_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_8,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x8_LDSM_T SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include <cute/atom/copy_traits_sm75.hpp>\n\n\nusing namespace cute;\n\ntemplate <class T>\n__global__ void\nldsm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  constexpr int count = sizeof(T) / 4;\n  int tid = threadIdx.x;\n  int stride = blockDim.x;\n\n  // load input gmem -> smem\n  __shared__ uint32_t smem[32 * count];\n  for (int i = 0; i < count; ++i) {\n    smem[tid + (stride * i)] = reinterpret_cast<uint32_t*>(g_in)[tid + (stride * i)];\n  }\n\n  __syncthreads();\n\n  uint32_t reg[count];\n  for (int i = 0; i < count; ++i) {\n    reg[i] = 0;\n  }\n\n  // load smem -> rmem using LDSM\n  uint128_t* smem_ptr = reinterpret_cast<uint128_t*>(smem) + tid;\n  T*         rmem_ptr = reinterpret_cast<T*>(reg);\n  cute::copy_ldsm(smem_ptr, rmem_ptr);\n\n  // store output rmem -> gmem\n  for (int i = 0; i < count; ++i) {\n    reinterpret_cast<uint32_t*>(g_out)[tid + (stride * i)] = reg[i];\n  }\n}\n\ntemplate <class TiledCopy, class SmemLayout>\n__global__ void\nldsm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, SmemLayout smem_layout)\n{\n  using namespace cute;\n\n  __shared__ uint16_t smem[size(smem_layout)];\n\n  auto t_g_in  = make_tensor(make_gmem_ptr(g_in),  smem_layout);\n  auto t_g_out = make_tensor(make_gmem_ptr(g_out), smem_layout);\n  auto t_smem  = make_tensor(make_smem_ptr(smem),  smem_layout);\n\n  int tid = threadIdx.x;\n\n  // Load input gmem -> smem\n  for (int i = tid; i < size(t_smem); i += size(tiled_copy)) {\n    t_smem(i) = t_g_in(i);\n  }\n\n  __syncthreads();\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  auto tXsX = thr_copy.partition_S(t_smem);   // (V,M,N)\n  auto tXgX = thr_copy.partition_D(t_g_out);  // (V,M,N)\n\n  auto tXrX = make_tensor<uint16_t>(shape(tXgX)); // (V,M,N)\n  clear(tXrX);  // Just to make sure\n\n/*\n  if (thread0()) {\n    print(\"tXsX: \" ); print(tXsX.layout()); print(\"\\n\");\n    print(\"tXgX: \" ); print(tXgX.layout()); print(\"\\n\");\n    print(\"tXrX: \" ); print(tXrX.layout()); print(\"\\n\");\n  }\n*/\n\n  // Copy smem -> rmem via tiled_copy (LDSM, LDS)\n  copy(tiled_copy, tXsX, tXrX);\n\n  // Output rmem -> gmem\n  copy(tXrX, tXgX);\n}\n\n\nTEST(SM80_CuTe_Ampere, Ldsm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  }\n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // LDSM 1x (32b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint32_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 32; ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 1x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // LDSM 2x (64b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint64_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 64; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 2x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // LDSM 4x (128b)\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  ldsm_test_device<uint128_t><<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < 128; ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"LDSM 4x ldsm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe LDSM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x1_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x2_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x2_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x4_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved U32x4_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,Shape <_2, _4>>,\n                            Stride< _2,Stride<_1,_64>>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_32,_1>>{},\n                                    Layout<Shape< _1,_8>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i] , h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x8 interleaved LDS.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x1_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x2_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x2_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x4_LDSM_N, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U32x4_LDSM_N SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<UniversalCopy<uint16_t>, uint16_t>{},\n                                    Layout<Shape<_16,_2>>{},\n                                    Layout<Shape< _2,_4>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 LDS.U16 SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x2_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_2,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i],  h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x2_LDSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x4_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_4,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i],  h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x4_LDSM_T SUCCESS\\n\");\n  }\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto smem_layout = Layout<Shape <_32,_32>,\n                            Stride<_32, _1>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U16x8_LDSM_T, uint16_t>{},\n                                    Layout<Shape<_4,_8>>{},\n                                    Layout<Shape<_8,_1>>{});\n\n  ldsm_test_device_cute<<<1, int(size(tiled_copy))>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    smem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < size(smem_layout); ++i) {\n    //printf(\"%d  %d\\n\", int(h_in[i]), int(h_out[i]));\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe 32x32 U16x8_LDSM_T SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n", "source": "/kaggle/working/cutlass/test/unit/cute/ampere/ldsm.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include <cute/atom/copy_traits_sm75.hpp>\n\nusing namespace cute;\n\n__global__ void\nmovm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  int tid = threadIdx.x;\n\n  // load input gmem -> register\n  uint32_t reg = reinterpret_cast<uint32_t*>(g_in)[tid];\n\n  // do two movmatrix calls (transpose twice => identity)\n  uint32_t tmp = 0;\n  uint32_t dst = 0;\n  SM75_U32x1_MOVM_T::copy(reg, tmp);\n  SM75_U32x1_MOVM_T::copy(tmp, dst);\n\n  // store result\n  reinterpret_cast<uint32_t*>(g_out)[tid] = dst;\n}\n\ntemplate <class TiledCopy, class GmemLayout>\n__global__ void\nmovm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, GmemLayout gmem_layout)\n{\n  using namespace cute;\n\n  auto t_g_in  = make_tensor(make_gmem_ptr(reinterpret_cast<uint32_t*>(g_in)),  gmem_layout);\n  auto t_g_out = make_tensor(make_gmem_ptr(reinterpret_cast<uint32_t*>(g_out)), gmem_layout);\n\n  int tid = threadIdx.x;\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  auto tXgS = thr_copy.partition_S(t_g_in);\n  auto tXgD = thr_copy.partition_D(t_g_out);\n\n  // Register tensors for intermediate and output data\n  auto tXrS = make_tensor<uint32_t>(shape(tXgS)); // src\n  auto tXrT = make_tensor<uint32_t>(shape(tXgS)); // tmp\n  auto tXrD = make_tensor<uint32_t>(shape(tXgD)); // dst\n  clear(tXrS);\n  clear(tXrT);\n  clear(tXrD);\n\n  // Load gmem -> registers\n  for (int i = 0; i < size(tXrS); ++i) {\n    tXrS(i) = tXgS(i);\n  }\n\n  // do two movmatrix calls for identity\n  copy(tiled_copy, tXrS, tXrT);\n  copy(tiled_copy, tXrT, tXrD);\n\n  // Store registers -> gmem\n  for (int i = 0; i < size(tXrD); ++i) {\n    tXgD(i) = tXrD(i);\n  }\n}\n\nTEST(SM75_CuTe_Turing, Movm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  } \n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // Direct MOVM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  movm_test_device<<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  // applied movmatrix twice so result should equal input\n  for (int i = 0; i < 64; ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"MOVM movm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe MOVM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto gmem_layout = Layout<Shape <_32, _1>, \n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_MOVM_T, uint32_t>{},\n                                    Layout<Shape<_32, _1>>{}, \n                                    Layout<Shape< _1, _1>>{});\n\n  movm_test_device_cute<<<1, int(size(tiled_copy))>>>(                              \n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    gmem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < (size(gmem_layout)*2); ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe MOVM SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n", "output": "/***************************************************************************************************\n * Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\n#include <cute/atom/copy_traits_sm75.hpp>\n\nusing namespace cute;\n\n__global__ void\nmovm_test_device(uint16_t* g_in, uint16_t* g_out)\n{\n  int tid = threadIdx.x;\n\n  // load input gmem -> register\n  uint32_t reg = reinterpret_cast<uint32_t*>(g_in)[tid];\n\n  // do two movmatrix calls (transpose twice => identity)\n  uint32_t tmp = 0;\n  uint32_t dst = 0;\n  SM75_U32x1_MOVM_T::copy(reg, tmp);\n  SM75_U32x1_MOVM_T::copy(tmp, dst);\n\n  // store result\n  reinterpret_cast<uint32_t*>(g_out)[tid] = dst;\n}\n\ntemplate <class TiledCopy, class GmemLayout>\n__global__ void\nmovm_test_device_cute(uint16_t* g_in, uint16_t* g_out,\n                      TiledCopy tiled_copy, GmemLayout gmem_layout)\n{\n  using namespace cute;\n\n  auto t_g_in  = make_tensor(make_gmem_ptr(reinterpret_cast<uint32_t*>(g_in)),  gmem_layout);\n  auto t_g_out = make_tensor(make_gmem_ptr(reinterpret_cast<uint32_t*>(g_out)), gmem_layout);\n\n  int tid = threadIdx.x;\n\n  auto thr_copy = tiled_copy.get_thread_slice(tid);\n\n  auto tXgS = thr_copy.partition_S(t_g_in);\n  auto tXgD = thr_copy.partition_D(t_g_out);\n\n  // Register tensors for intermediate and output data\n  auto tXrS = make_tensor<uint32_t>(shape(tXgS)); // src\n  auto tXrT = make_tensor<uint32_t>(shape(tXgS)); // tmp\n  auto tXrD = make_tensor<uint32_t>(shape(tXgD)); // dst\n  clear(tXrS);\n  clear(tXrT);\n  clear(tXrD);\n\n  // Load gmem -> registers\n  for (int i = 0; i < size(tXrS); ++i) {\n    tXrS(i) = tXgS(i);\n  }\n\n  // do two movmatrix calls for identity\n  copy(tiled_copy, tXrS, tXrT);\n  copy(tiled_copy, tXrT, tXrD);\n\n  // Store registers -> gmem\n  for (int i = 0; i < size(tXrD); ++i) {\n    tXgD(i) = tXrD(i);\n  }\n}\n\nTEST(SM75_CuTe_Turing, Movm)\n{\n  constexpr int count = 1024;\n\n  thrust::host_vector<uint16_t> h_in(count);\n  for (int i = 0; i < count; ++i) {\n    h_in[i] = uint16_t(i);\n  } \n  thrust::device_vector<uint16_t> d_in = h_in;\n\n  //\n  // Direct MOVM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n  movm_test_device<<<1, 32>>>(\n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()));\n  thrust::host_vector<uint16_t> h_out = d_out;\n  // applied movmatrix twice so result should equal input\n  for (int i = 0; i < 64; ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"MOVM movm_test_device SUCCESS\\n\");\n  }\n\n  //\n  // CuTe MOVM\n  //\n\n  {\n  thrust::device_vector<uint16_t> d_out(count);\n\n  auto gmem_layout = Layout<Shape <_32, _1>, \n                            Stride< _1,_32>>{};\n  auto tiled_copy = make_tiled_copy(Copy_Atom<SM75_U32x1_MOVM_T, uint32_t>{},\n                                    Layout<Shape<_32, _1>>{}, \n                                    Layout<Shape< _1, _1>>{});\n\n  movm_test_device_cute<<<1, int(size(tiled_copy))>>>(                              \n    thrust::raw_pointer_cast(d_in.data()),\n    thrust::raw_pointer_cast(d_out.data()),\n    tiled_copy,\n    gmem_layout);\n  thrust::host_vector<uint16_t> h_out = d_out;\n  for (int i = 0; i < (size(gmem_layout)*2); ++i) {\n    EXPECT_EQ(h_out[i], h_in[i]);\n  }\n  CUTLASS_TRACE_HOST(\"CuTe MOVM SUCCESS\\n\");\n  }\n\n  CUTLASS_TRACE_HOST(\"PASS\");\n}\n", "source": "/kaggle/working/cutlass/test/unit/cute/turing/movm.cu"}
{"input": "\n/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class GmemTensor, class RmemTiler, class CopyPolicy>\n__global__\nvoid\nkernel(GmemTensor gC, RmemTiler tiler, CopyPolicy policy)\n{\n  Tensor tCgC = local_tile(gC, tiler, 0);\n\n  Tensor rC = make_tensor_like(tCgC);\n  using T = typename GmemTensor::value_type;\n  for (int i = 0; i < size(rC); ++i) {\n    rC(i) = T(i % 13);\n  }\n\n#if 0\n  print(\"  gC : \"); print(  gC); print(\"\\n\");\n  print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n  print(\"  rC : \"); print(  rC); print(\"\\n\");\n#endif\n\n  // NOTE: only 1 thread, this thread produce a block of 8x8 output. The fringe will not be touched.\n  //copy(rC, tCgC);           // Enable auto-vectorization if static\n  copy(policy, rC, tCgC);     // Use a policy to establish vectorization assumptions\n}\n\ntemplate <class T, class CopyPolicy, class GmemLayout, class RmemTiler>\nvoid\ntest_copy_vectorization(CopyPolicy policy, GmemLayout gmem_layout, RmemTiler rmem_tiler)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout), T(0));\n\n  thrust::device_vector<T> d_in = h_in;\n  Tensor m_in = make_tensor(make_gmem_ptr(raw_pointer_cast(d_in.data())), gmem_layout);\n\n  kernel<<<1,1>>>(m_in, rmem_tiler, policy);\n\n  thrust::host_vector<T> h_out = d_in;\n  Tensor result = make_tensor(h_out.data(), gmem_layout);\n\n  thrust::host_vector<T> h_true = h_in;\n  Tensor ref = make_tensor(h_true.data(), gmem_layout);\n\n  // Set the values directly in the reference tensor, no copy\n  Tensor ref_tile = local_tile(ref, rmem_tiler, 0);\n  for (int i = 0; i < size(ref_tile); ++i) {\n    ref_tile(i) = T(i % 13);\n  }\n\n  // Compare the reference and the result. Print only the first 3 errors.\n  // print_tensor(result);\n  int count = 3;\n  for (int i = 0; i < size(ref) && count > 0; ++i) {\n    EXPECT_EQ(result(i), ref(i));\n    if (result(i) != ref(i)) {\n      --count;\n    }\n  }\n}\n\ntemplate <class T, class GmemLayout, class RmemTiler>\nvoid\ntest_copy_vectorization(GmemLayout gmem_layout, RmemTiler rmem_tiler)\n{\n  test_copy_vectorization<T>(DefaultCopy{}, gmem_layout, rmem_tiler);\n}\n\nTEST(SM70_CuTe_Volta, SimpleVec)\n{\n  // Fully static layouts are assumed to be aligned -- these will be vectorized\n  test_copy_vectorization<float>(make_layout(make_shape(Int<8>{}, Int<8>{})), Shape<_8,_8>{});\n  test_copy_vectorization<float>(make_layout(make_shape(Int<12>{}, Int<12>{})), Shape<_8,_8>{});\n  // Fails in vectorization recast due to misalignment and static assertions\n  //test_copy_vectorization<float>(make_layout(make_shape(Int<9>{}, Int<9>{})), Shape<_8,_8>{});\n\n  // Dynamic layouts are not assumed to be aligned -- these will not be vectorized\n  test_copy_vectorization<float>(make_layout(make_shape(12,12)), Shape<_8,_8>{});\n  test_copy_vectorization<float>(make_layout(make_shape( 9, 9)), Shape<_8,_8>{});\n\n  // Dynamic layouts that are assumed to be aligned -- these will be vectorized\n  test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape( 8, 8)), Shape<_8,_8>{});\n  test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape(12,12)), Shape<_8,_8>{});\n  // Fails -- bad alignment assumption\n  //test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape( 9, 9)), Shape<_8,_8>{});\n}\n", "output": "\n/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#include \"cutlass_unit_test.h\"\n\n#include <iostream>\n#include <iomanip>\n#include <utility>\n#include <type_traits>\n#include <vector>\n#include <numeric>\n\n#include <thrust/host_vector.h>\n#include <thrust/device_vector.h>\n\n#include <cute/tensor.hpp>\n\nusing namespace cute;\n\ntemplate <class GmemTensor, class RmemTiler, class CopyPolicy>\n__global__\nvoid\nkernel(GmemTensor gC, RmemTiler tiler, CopyPolicy policy)\n{\n  Tensor tCgC = local_tile(gC, tiler, 0);\n\n  Tensor rC = make_tensor_like(tCgC);\n  using T = typename GmemTensor::value_type;\n  for (int i = 0; i < size(rC); ++i) {\n    rC(i) = T(i % 13);\n  }\n\n#if 0\n  print(\"  gC : \"); print(  gC); print(\"\\n\");\n  print(\"tCgC : \"); print(tCgC); print(\"\\n\");\n  print(\"  rC : \"); print(  rC); print(\"\\n\");\n#endif\n\n  // NOTE: only 1 thread, this thread produce a block of 8x8 output. The fringe will not be touched.\n  //copy(rC, tCgC);           // Enable auto-vectorization if static\n  copy(policy, rC, tCgC);     // Use a policy to establish vectorization assumptions\n}\n\ntemplate <class T, class CopyPolicy, class GmemLayout, class RmemTiler>\nvoid\ntest_copy_vectorization(CopyPolicy policy, GmemLayout gmem_layout, RmemTiler rmem_tiler)\n{\n  thrust::host_vector<T> h_in(cosize(gmem_layout), T(0));\n\n  thrust::device_vector<T> d_in = h_in;\n  Tensor m_in = make_tensor(make_gmem_ptr(raw_pointer_cast(d_in.data())), gmem_layout);\n\n  kernel<<<1,1>>>(m_in, rmem_tiler, policy);\n\n  thrust::host_vector<T> h_out = d_in;\n  Tensor result = make_tensor(h_out.data(), gmem_layout);\n\n  thrust::host_vector<T> h_true = h_in;\n  Tensor ref = make_tensor(h_true.data(), gmem_layout);\n\n  // Set the values directly in the reference tensor, no copy\n  Tensor ref_tile = local_tile(ref, rmem_tiler, 0);\n  for (int i = 0; i < size(ref_tile); ++i) {\n    ref_tile(i) = T(i % 13);\n  }\n\n  // Compare the reference and the result. Print only the first 3 errors.\n  // print_tensor(result);\n  int count = 3;\n  for (int i = 0; i < size(ref) && count > 0; ++i) {\n    EXPECT_EQ(result(i), ref(i));\n    if (result(i) != ref(i)) {\n      --count;\n    }\n  }\n}\n\ntemplate <class T, class GmemLayout, class RmemTiler>\nvoid\ntest_copy_vectorization(GmemLayout gmem_layout, RmemTiler rmem_tiler)\n{\n  test_copy_vectorization<T>(DefaultCopy{}, gmem_layout, rmem_tiler);\n}\n\nTEST(SM70_CuTe_Volta, SimpleVec)\n{\n  // Fully static layouts are assumed to be aligned -- these will be vectorized\n  test_copy_vectorization<float>(make_layout(make_shape(Int<8>{}, Int<8>{})), Shape<_8,_8>{});\n  test_copy_vectorization<float>(make_layout(make_shape(Int<12>{}, Int<12>{})), Shape<_8,_8>{});\n  // Fails in vectorization recast due to misalignment and static assertions\n  //test_copy_vectorization<float>(make_layout(make_shape(Int<9>{}, Int<9>{})), Shape<_8,_8>{});\n\n  // Dynamic layouts are not assumed to be aligned -- these will not be vectorized\n  test_copy_vectorization<float>(make_layout(make_shape(12,12)), Shape<_8,_8>{});\n  test_copy_vectorization<float>(make_layout(make_shape( 9, 9)), Shape<_8,_8>{});\n\n  // Dynamic layouts that are assumed to be aligned -- these will be vectorized\n  test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape( 8, 8)), Shape<_8,_8>{});\n  test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape(12,12)), Shape<_8,_8>{});\n  // Fails -- bad alignment assumption\n  //test_copy_vectorization<float>(AutoVectorizingCopyWithAssumedAlignment<128>{}, make_layout(make_shape( 9, 9)), Shape<_8,_8>{});\n}\n", "source": "/kaggle/working/cutlass/test/unit/cute/volta/vectorization_auto.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*\n**************************************************************************************************/\n/*! \\file\n\\brief unit tests for NHWC tensor layout\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n#include \"cutlass/layout/tensor.h\"\n#include \"cutlass/util/device_memory.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nnamespace test {\nnamespace layout {\n\n  void test_nhwc_layout(int n_size, int h_size, int w_size, int c_size) {\n    int ldc = c_size + 1;\n    int ldw = ldc * (w_size + 2);\n    int ldh = ldw * (h_size + 3);\n\n    typedef cutlass::layout::TensorNHWC Tensor;\n\n    Tensor::Stride tensor_stride({ ldc, ldw, ldh });\n    Tensor tensor_nhw_packed_c(tensor_stride);\n\n    // test pointer offset\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n          for (int c_idx = 0; c_idx < c_size; c_idx++) {\n            cutlass::Tensor4DCoord tensor_coord(n_idx, p_idx, q_idx, c_idx);\n            auto ptr_offset = tensor_nhw_packed_c(tensor_coord);\n            decltype(ptr_offset) reference_offset = c_idx +\n              q_idx * ldc +\n              p_idx * ldw +\n              n_idx * ldh;\n            EXPECT_EQ(ptr_offset, reference_offset);\n          }\n        }\n      }\n    }\n\n    // test stride\n    auto stride = tensor_nhw_packed_c.stride();\n    EXPECT_EQ(stride, tensor_stride);\n\n    // test capacity\n    auto capacity = tensor_nhw_packed_c.capacity(\n            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));\n    decltype(capacity) reference_capacity = ldh * n_size;\n    EXPECT_EQ(capacity, reference_capacity);\n\n  }\n\n  __global__ void test_nhwc_inverse(\n          int *output, int n_size, int h_size, int w_size, int c_size) {\n    int ldc = c_size;\n    int ldw = ldc * w_size;\n    int ldh = ldw * h_size;\n\n    typedef cutlass::layout::TensorNHWC Tensor;\n\n    Tensor::Stride tensor_stride({ ldc, ldw, ldh });\n    Tensor tensor_nhw_packed_c(tensor_stride);\n\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n            cutlass::Tensor4DCoord tensor_coord(n_idx, p_idx, q_idx, threadIdx.x);\n            int ptr_offset = tensor_nhw_packed_c(tensor_coord);\n            cutlass::Tensor4DCoord inv_coord = tensor_nhw_packed_c.inverse(ptr_offset);\n            output[ptr_offset] = tensor_nhw_packed_c(inv_coord);\n        }\n      }\n    }\n  }\n\n  class TestTensorNHWC {\n    public:\n\n  //\n  // Data members\n  //\n\n  //\n  // Methods\n  //\n\n  /// Ctor\n  TestTensorNHWC() {\n\n  }\n\n  /// Runs the test\n  void run(int n_size, int h_size, int w_size, int c_size) {\n\n    size_t size = n_size * h_size * w_size * c_size;\n\n    /// Device memory containing output\n    cutlass::device_memory::allocation< int > output(size);\n    int *output_host = (int *)malloc(sizeof(int) * size);\n\n    dim3 grid(1,1);\n    dim3 block(c_size, 1, 1);\n\n    test::layout::test_nhwc_inverse<<< grid, block >>>(output.get(),\n            n_size, h_size, w_size, c_size);\n\n    cudaError_t result = cudaDeviceSynchronize();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    //\n    // Verify output\n    //\n\n    cutlass::device_memory::copy_to_host(output_host, output.get(), size);\n\n    result = cudaGetLastError();\n    ASSERT_EQ(result, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(result);\n\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n          for (int c_idx = 0; c_idx < c_size; c_idx++) {\n            int reference_offset = c_idx +\n              q_idx * c_size +\n              p_idx * (c_size * w_size) +\n              n_idx * (c_size * w_size * h_size);\n              EXPECT_EQ(output_host[reference_offset], reference_offset);\n          }\n        }\n      }\n    }\n  }\n};\n\n\n} // namespace layout\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Layout_TensorNHWC, NHWC_1_16_8_32) {\n  int n_size = 1;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 32;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\nTEST(Layout_TensorNHWC, NHWC_2_16_8_32) {\n  int n_size = 2;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 32;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n}\n\nTEST(Layout_TensorNHWC, NHWC_2_16_8_128) {\n  int n_size = 2;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 128;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\nTEST(Layout_TensorNHWC, NHWC_4_8_16_128) {\n  int n_size = 4;\n  int h_size = 8;\n  int w_size = 16;\n  int c_size = 128;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*\n**************************************************************************************************/\n/*! \\file\n\\brief unit tests for NHWC tensor layout\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n#include \"cutlass/layout/tensor.h\"\n#include \"cutlass/util/device_memory.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\nnamespace test {\nnamespace layout {\n\n  void test_nhwc_layout(int n_size, int h_size, int w_size, int c_size) {\n    int ldc = c_size + 1;\n    int ldw = ldc * (w_size + 2);\n    int ldh = ldw * (h_size + 3);\n\n    typedef cutlass::layout::TensorNHWC Tensor;\n\n    Tensor::Stride tensor_stride({ ldc, ldw, ldh });\n    Tensor tensor_nhw_packed_c(tensor_stride);\n\n    // test pointer offset\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n          for (int c_idx = 0; c_idx < c_size; c_idx++) {\n            cutlass::Tensor4DCoord tensor_coord(n_idx, p_idx, q_idx, c_idx);\n            auto ptr_offset = tensor_nhw_packed_c(tensor_coord);\n            decltype(ptr_offset) reference_offset = c_idx +\n              q_idx * ldc +\n              p_idx * ldw +\n              n_idx * ldh;\n            EXPECT_EQ(ptr_offset, reference_offset);\n          }\n        }\n      }\n    }\n\n    // test stride\n    auto stride = tensor_nhw_packed_c.stride();\n    EXPECT_EQ(stride, tensor_stride);\n\n    // test capacity\n    auto capacity = tensor_nhw_packed_c.capacity(\n            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));\n    decltype(capacity) reference_capacity = ldh * n_size;\n    EXPECT_EQ(capacity, reference_capacity);\n\n  }\n\n  __global__ void test_nhwc_inverse(\n          int *output, int n_size, int h_size, int w_size, int c_size) {\n    int ldc = c_size;\n    int ldw = ldc * w_size;\n    int ldh = ldw * h_size;\n\n    typedef cutlass::layout::TensorNHWC Tensor;\n\n    Tensor::Stride tensor_stride({ ldc, ldw, ldh });\n    Tensor tensor_nhw_packed_c(tensor_stride);\n\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n            cutlass::Tensor4DCoord tensor_coord(n_idx, p_idx, q_idx, threadIdx.x);\n            int ptr_offset = tensor_nhw_packed_c(tensor_coord);\n            cutlass::Tensor4DCoord inv_coord = tensor_nhw_packed_c.inverse(ptr_offset);\n            output[ptr_offset] = tensor_nhw_packed_c(inv_coord);\n        }\n      }\n    }\n  }\n\n  class TestTensorNHWC {\n    public:\n\n  //\n  // Data members\n  //\n\n  //\n  // Methods\n  //\n\n  /// Ctor\n  TestTensorNHWC() {\n\n  }\n\n  /// Runs the test\n  void run(int n_size, int h_size, int w_size, int c_size) {\n\n    size_t size = n_size * h_size * w_size * c_size;\n\n    /// Device memory containing output\n    cutlass::device_memory::allocation< int > output(size);\n    int *output_host = (int *)malloc(sizeof(int) * size);\n\n    dim3 grid(1,1);\n    dim3 block(c_size, 1, 1);\n\n    test::layout::test_nhwc_inverse<<< grid, block >>>(output.get(),\n            n_size, h_size, w_size, c_size);\n\n    hipError_t result = hipDeviceSynchronize();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    //\n    // Verify output\n    //\n\n    cutlass::device_memory::copy_to_host(output_host, output.get(), size);\n\n    result = hipGetLastError();\n    ASSERT_EQ(result, hipSuccess) << \"CUDA error: \" << hipGetErrorString(result);\n\n    for (int n_idx = 0; n_idx < n_size; n_idx++) {\n      for (int p_idx = 0; p_idx < h_size; p_idx++) {\n        for (int q_idx = 0; q_idx < w_size; q_idx++) {\n          for (int c_idx = 0; c_idx < c_size; c_idx++) {\n            int reference_offset = c_idx +\n              q_idx * c_size +\n              p_idx * (c_size * w_size) +\n              n_idx * (c_size * w_size * h_size);\n              EXPECT_EQ(output_host[reference_offset], reference_offset);\n          }\n        }\n      }\n    }\n  }\n};\n\n\n} // namespace layout\n} // namespace test\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Layout_TensorNHWC, NHWC_1_16_8_32) {\n  int n_size = 1;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 32;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\nTEST(Layout_TensorNHWC, NHWC_2_16_8_32) {\n  int n_size = 2;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 32;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n}\n\nTEST(Layout_TensorNHWC, NHWC_2_16_8_128) {\n  int n_size = 2;\n  int h_size = 16;\n  int w_size = 8;\n  int c_size = 128;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\nTEST(Layout_TensorNHWC, NHWC_4_8_16_128) {\n  int n_size = 4;\n  int h_size = 8;\n  int w_size = 16;\n  int c_size = 128;\n  test::layout::test_nhwc_layout(n_size, h_size, w_size, c_size);\n  test::layout::TestTensorNHWC test_nhwc;\n  test_nhwc.run(n_size, h_size, w_size, c_size);\n\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n", "source": "/kaggle/working/cutlass/test/unit/layout/tensor_nhwc.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the launch_on_cluster function\n*/\n\n#include \"../common/cutlass_unit_test.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include <cassert>\n#include <memory>\n#include <type_traits>\n\n#if defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\nnamespace { // (anonymous)\n\n// Using a struct instead of a lambda makes it possible\n// to name the deleter type without std::function\n// (which type-erases).\nstruct scalar_deleter {\n  void operator() (float* p) {\n    if (p != nullptr) {\n      cudaFree(p);\n    }\n  }\n};\n\nusing scalar_device_pointer = std::unique_ptr<float, scalar_deleter>;\n\n// Each test needs to initialize this anew,\n// from a scalar instance that is in scope during the test.\n__device__ float* scalar_ptr_gpu;\n\n// A single scalar value on device.\n// The constructor allocates space on device for one value,\n// copies the value to device, and sets the global pointer\n// `scalar_ptr_gpu` (see above) to point to it.\n// sync_to_host() copies that value back to host.\n//\n// This class exists only for the tests in this file.\n// In order to know whether a kernel that launch_on_cluster\n// claimed to launch actually got launched, each kernel\n// performs a side effect: it modifies the scalar value\n// through the scalar_ptr_gpu value.\n// It performs a side effect through a global,\n// rather than through an argument,\n// so that we can test kernel launch\n// with kernels that take zero parameters.\nclass scalar {\nprivate:\n  static constexpr std::size_t num_bytes = sizeof(float);\n\npublic:\n  scalar(float value) : value_host_(value)\n  {\n    float* ptr_gpu_raw = nullptr;\n    auto err = cudaMalloc(&ptr_gpu_raw, num_bytes);\n    assert(err == cudaSuccess);\n\n    scalar_device_pointer ptr_gpu{ptr_gpu_raw, scalar_deleter{}};\n    err = cudaMemcpy(ptr_gpu.get(), &value_host_,\n                     num_bytes, cudaMemcpyHostToDevice);\n    assert(err == cudaSuccess);\n    ptr_gpu_ = std::move(ptr_gpu);\n    upload_device_pointer();\n  }\n\n  float sync_to_host()\n  {\n    auto err = cudaMemcpy(&value_host_, ptr_gpu_.get(),\n                          num_bytes, cudaMemcpyDeviceToHost);\n    assert(err == cudaSuccess);\n    return value_host_;\n  }\n\nprivate:\n  void upload_device_pointer()\n  {\n    float* ptr_raw = ptr_gpu_.get();\n    auto err = cudaMemcpyToSymbol(scalar_ptr_gpu, &ptr_raw, sizeof(float*));\n    assert(err == cudaSuccess);\n  }\n\n  float value_host_ = 0.0;\n  scalar_device_pointer ptr_gpu_;\n};\n\ntemplate<int cluster_x, int cluster_y, int cluster_z>\nCUTE_DEVICE void check_cluster_shape() {\n  [[maybe_unused]] const dim3 cluster_shape = cute::cluster_shape();\n  assert(cluster_shape.x == cluster_x);\n  assert(cluster_shape.y == cluster_y);\n  assert(cluster_shape.z == cluster_z);\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z>\n__global__ void kernel_0()\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 0.1f;\n  }\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0>\n__global__ void kernel_1(int p0)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 1.2f;\n  }\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0,\n         int expected_p2>\n__global__ void kernel_2(int p0, void* p1, int p2)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n  assert(p1 == nullptr);\n  assert(p2 == expected_p2);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 2.3f;\n  }\n}\n\nstruct OverloadedOperatorAmpersand {\n  struct tag_t {};\n\n  // Test that kernel launch uses the actual address,\n  // instead of any overloaded operator& that might exist.\n  CUTE_HOST_DEVICE tag_t operator& () const {\n    return {};\n  }\n\n  int x = 0;\n  int y = 0;\n  int z = 0;\n  int w = 0;\n};\n\nstatic_assert(sizeof(OverloadedOperatorAmpersand) == 4 * sizeof(int));\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0,\n         int expected_p1_x,\n         int expected_p1_y,\n         int expected_p1_z,\n         int expected_p1_w,\n         std::uint64_t expected_p2>\n__global__ void kernel_3(int p0, OverloadedOperatorAmpersand p1, std::uint64_t p2)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n  assert(p1.x == expected_p1_x);\n  assert(p1.y == expected_p1_y);\n  assert(p1.z == expected_p1_z);\n  assert(p1.w == expected_p1_w);\n  assert(p2 == expected_p2);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 3.4f;\n  }\n}\n\n} // namespace (anonymous)\n\nTEST(SM90_ClusterLaunch, Kernel_0)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(&kernel_0<2, 1, 1>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result == cudaSuccess) {\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    cudaError_t error = cudaGetLastError();\n    EXPECT_EQ(result, cudaSuccess) << \"Error at kernel sync: \"\n      << cudaGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 0.1f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_1)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  void const* kernel_ptr = reinterpret_cast<void const*>(&kernel_1<2, 1, 1, expected_p0>);\n  const int p0 = expected_p0;\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result == cudaSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    cudaError_t error = cudaGetLastError();\n    EXPECT_EQ(result, cudaSuccess) << \"Error at kernel sync: \"\n      << cudaGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 1.2f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_2)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  constexpr int expected_p2 = 43;\n\n  int p0 = expected_p0;\n  int* p1 = nullptr;\n  int p2 = expected_p2;\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n    &kernel_2<2, 1, 1, expected_p0, expected_p2>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0, p1, p2);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result == cudaSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    cudaError_t error = cudaGetLastError();\n    EXPECT_EQ(result, cudaSuccess) << \"Error at kernel sync: \"\n      << cudaGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 2.3f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_3)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  constexpr int expected_p1_x = 1;\n  constexpr int expected_p1_y = 2;\n  constexpr int expected_p1_z = 3;\n  constexpr int expected_p1_w = 4;\n  constexpr std::uint64_t expected_p2 = 1'000'000'000'000uLL;\n\n  int p0 = expected_p0;\n  OverloadedOperatorAmpersand p1{expected_p1_x,\n    expected_p1_y, expected_p1_z, expected_p1_w};\n  // Verify that operator& is overloaded for this type.\n  static_assert(! std::is_same_v<decltype(&p1),\n                    OverloadedOperatorAmpersand*>);\n  std::uint64_t p2 = expected_p2;\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n    &kernel_3<2, 1, 1, expected_p0, expected_p1_x,\n      expected_p1_y, expected_p1_z, expected_p1_w,\n      expected_p2>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0, p1, p2);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  if (result == cudaSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    cudaError_t error = cudaGetLastError();\n    EXPECT_EQ(result, cudaSuccess) << \"Error at kernel sync: \"\n      << cudaGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 3.4f);\n}\n\n#endif // CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n\n/*! \\file\n    \\brief Unit test for the launch_on_cluster function\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/cutlass_unit_test.h\"\n#include \"cutlass/cluster_launch.hpp\"\n#include \"cute/arch/cluster_sm90.hpp\"\n#include <cassert>\n#include <memory>\n#include <type_traits>\n\n#if defined(CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED)\n\nnamespace { // (anonymous)\n\n// Using a struct instead of a lambda makes it possible\n// to name the deleter type without std::function\n// (which type-erases).\nstruct scalar_deleter {\n  void operator() (float* p) {\n    if (p != nullptr) {\n      hipFree(p);\n    }\n  }\n};\n\nusing scalar_device_pointer = std::unique_ptr<float, scalar_deleter>;\n\n// Each test needs to initialize this anew,\n// from a scalar instance that is in scope during the test.\n__device__ float* scalar_ptr_gpu;\n\n// A single scalar value on device.\n// The constructor allocates space on device for one value,\n// copies the value to device, and sets the global pointer\n// `scalar_ptr_gpu` (see above) to point to it.\n// sync_to_host() copies that value back to host.\n//\n// This class exists only for the tests in this file.\n// In order to know whether a kernel that launch_on_cluster\n// claimed to launch actually got launched, each kernel\n// performs a side effect: it modifies the scalar value\n// through the scalar_ptr_gpu value.\n// It performs a side effect through a global,\n// rather than through an argument,\n// so that we can test kernel launch\n// with kernels that take zero parameters.\nclass scalar {\nprivate:\n  static constexpr std::size_t num_bytes = sizeof(float);\n\npublic:\n  scalar(float value) : value_host_(value)\n  {\n    float* ptr_gpu_raw = nullptr;\n    auto err = hipMalloc(&ptr_gpu_raw, num_bytes);\n    assert(err == hipSuccess);\n\n    scalar_device_pointer ptr_gpu{ptr_gpu_raw, scalar_deleter{}};\n    err = hipMemcpy(ptr_gpu.get(), &value_host_,\n                     num_bytes, hipMemcpyHostToDevice);\n    assert(err == hipSuccess);\n    ptr_gpu_ = std::move(ptr_gpu);\n    upload_device_pointer();\n  }\n\n  float sync_to_host()\n  {\n    auto err = hipMemcpy(&value_host_, ptr_gpu_.get(),\n                          num_bytes, hipMemcpyDeviceToHost);\n    assert(err == hipSuccess);\n    return value_host_;\n  }\n\nprivate:\n  void upload_device_pointer()\n  {\n    float* ptr_raw = ptr_gpu_.get();\n    auto err = hipMemcpyToSymbol(HIP_SYMBOL(scalar_ptr_gpu), &ptr_raw, sizeof(float*));\n    assert(err == hipSuccess);\n  }\n\n  float value_host_ = 0.0;\n  scalar_device_pointer ptr_gpu_;\n};\n\ntemplate<int cluster_x, int cluster_y, int cluster_z>\nCUTE_DEVICE void check_cluster_shape() {\n  [[maybe_unused]] const dim3 cluster_shape = cute::cluster_shape();\n  assert(cluster_shape.x == cluster_x);\n  assert(cluster_shape.y == cluster_y);\n  assert(cluster_shape.z == cluster_z);\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z>\n__global__ void kernel_0()\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 0.1f;\n  }\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0>\n__global__ void kernel_1(int p0)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 1.2f;\n  }\n}\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0,\n         int expected_p2>\n__global__ void kernel_2(int p0, void* p1, int p2)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n  assert(p1 == nullptr);\n  assert(p2 == expected_p2);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 2.3f;\n  }\n}\n\nstruct OverloadedOperatorAmpersand {\n  struct tag_t {};\n\n  // Test that kernel launch uses the actual address,\n  // instead of any overloaded operator& that might exist.\n  CUTE_HOST_DEVICE tag_t operator& () const {\n    return {};\n  }\n\n  int x = 0;\n  int y = 0;\n  int z = 0;\n  int w = 0;\n};\n\nstatic_assert(sizeof(OverloadedOperatorAmpersand) == 4 * sizeof(int));\n\ntemplate<int cluster_x, int cluster_y, int cluster_z,\n         int expected_p0,\n         int expected_p1_x,\n         int expected_p1_y,\n         int expected_p1_z,\n         int expected_p1_w,\n         std::uint64_t expected_p2>\n__global__ void kernel_3(int p0, OverloadedOperatorAmpersand p1, std::uint64_t p2)\n{\n  check_cluster_shape<cluster_x, cluster_y, cluster_z>();\n  assert(p0 == expected_p0);\n  assert(p1.x == expected_p1_x);\n  assert(p1.y == expected_p1_y);\n  assert(p1.z == expected_p1_z);\n  assert(p1.w == expected_p1_w);\n  assert(p2 == expected_p2);\n\n  // Write to global memory, so that we know\n  // whether the kernel actually ran.\n  const dim3 block_id = cute::block_id_in_cluster();\n  if (threadIdx.x == 0 && block_id.x == 0 && block_id.y == 0 && block_id.z == 0) {\n    *scalar_ptr_gpu = 3.4f;\n  }\n}\n\n} // namespace (anonymous)\n\nTEST(SM90_ClusterLaunch, Kernel_0)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(&kernel_0<2, 1, 1>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result == hipSuccess) {\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    hipError_t error = hipGetLastError();\n    EXPECT_EQ(result, hipSuccess) << \"Error at kernel sync: \"\n      << hipGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 0.1f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_1)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  void const* kernel_ptr = reinterpret_cast<void const*>(&kernel_1<2, 1, 1, expected_p0>);\n  const int p0 = expected_p0;\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result == hipSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    hipError_t error = hipGetLastError();\n    EXPECT_EQ(result, hipSuccess) << \"Error at kernel sync: \"\n      << hipGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 1.2f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_2)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  constexpr int expected_p2 = 43;\n\n  int p0 = expected_p0;\n  int* p1 = nullptr;\n  int p2 = expected_p2;\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n    &kernel_2<2, 1, 1, expected_p0, expected_p2>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0, p1, p2);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result == hipSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    hipError_t error = hipGetLastError();\n    EXPECT_EQ(result, hipSuccess) << \"Error at kernel sync: \"\n      << hipGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 2.3f);\n}\n\nTEST(SM90_ClusterLaunch, Kernel_3)\n{\n  scalar global_value(-1.0f);\n\n  const dim3 grid_dims{2, 1, 1};\n  const dim3 block_dims{1, 1, 1};\n  const dim3 cluster_dims{grid_dims.x * block_dims.x, 1, 1};\n  const int smem_size_in_bytes = 0;\n  cutlass::ClusterLaunchParams params{\n    grid_dims, block_dims, cluster_dims, smem_size_in_bytes};\n\n  constexpr int expected_p0 = 42;\n  constexpr int expected_p1_x = 1;\n  constexpr int expected_p1_y = 2;\n  constexpr int expected_p1_z = 3;\n  constexpr int expected_p1_w = 4;\n  constexpr std::uint64_t expected_p2 = 1'000'000'000'000uLL;\n\n  int p0 = expected_p0;\n  OverloadedOperatorAmpersand p1{expected_p1_x,\n    expected_p1_y, expected_p1_z, expected_p1_w};\n  // Verify that operator& is overloaded for this type.\n  static_assert(! std::is_same_v<decltype(&p1),\n                    OverloadedOperatorAmpersand*>);\n  std::uint64_t p2 = expected_p2;\n\n  void const* kernel_ptr = reinterpret_cast<void const*>(\n    &kernel_3<2, 1, 1, expected_p0, expected_p1_x,\n      expected_p1_y, expected_p1_z, expected_p1_w,\n      expected_p2>);\n  cutlass::Status status = cutlass::launch_kernel_on_cluster(params,\n    kernel_ptr, p0, p1, p2);\n  ASSERT_EQ(status, cutlass::Status::kSuccess);\n\n  hipError_t result = hipDeviceSynchronize();\n  if (result == hipSuccess) {\n#if (CUTLASS_DEBUG_TRACE_LEVEL > 1)\n    CUTLASS_TRACE_HOST(\"Kernel launch succeeded\\n\");\n#endif\n  }\n  else {\n    CUTLASS_TRACE_HOST(\"Kernel launch FAILED\\n\");\n    hipError_t error = hipGetLastError();\n    EXPECT_EQ(result, hipSuccess) << \"Error at kernel sync: \"\n      << hipGetErrorString(error) << \"\\n\";\n  }\n\n  ASSERT_EQ(global_value.sync_to_host(), 3.4f);\n}\n\n#endif // CUTLASS_SM90_CLUSTER_LAUNCH_ENABLED\n", "source": "/kaggle/working/cutlass/test/unit/cluster_launch/cluster_launch.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/epilogue/thread/linear_combination_planar_complex.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF32F32 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  float, \n  4,\n  float, \n  float>;\n\n__global__ void epilogue_thread_functor_planar_complex_f32_f32(\n  float *output_ptr, \n  float const *accum_ptr, \n  float const *source_ptr,\n  typename FunctorPlanarComplexF32F32::Params params) {\n\n  FunctorPlanarComplexF32F32 linear_combination_op(params);\n\n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<float , 4> const *>(accum_ptr);\n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<float, 4> const *>(source_ptr);\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<float, 4>*>(output_ptr) = linear_combination_op(accum, source);\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f32) {\n\n  using Element = float;\n  using ElementOutput = float;\n  int const kCount = 4;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination = linear_combination_op(accum, source);\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF16F32 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  cutlass::half_t, \n  4,\n  float, \n  float>;\n\n__global__ void epilogue_thread_functor_planar_complex_f16_f32(\n  cutlass::half_t *output_ptr, \n  float const *accum_ptr, \n  cutlass::half_t const *source_ptr,\n  typename FunctorPlanarComplexF16F32::Params params,\n  int N) {\n\n  FunctorPlanarComplexF16F32 linear_combination_op(params);\n\n  \n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<float , 4> const *>(accum_ptr);   \n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(source_ptr);\n\n  #pragma unroll 1\n  for (int n = 0; n < N; ++n) {\n    source = linear_combination_op(accum, source);\n  }\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4>*>(output_ptr) = source;\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f16_f32) {\n\n  using Element = float;\n  using ElementOutput = cutlass::half_t;\n  int const kCount = 4;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination{ linear_combination_op(accum, source) };\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF16F16 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  cutlass::half_t, \n  4,\n  cutlass::half_t, \n  cutlass::half_t>;\n\n__global__ void epilogue_thread_functor_planar_complex_f16_f16(\n  cutlass::half_t *output_ptr, \n  cutlass::half_t const *accum_ptr, \n  cutlass::half_t const *source_ptr,\n  typename FunctorPlanarComplexF16F16::Params params,\n  int N) {\n\n  FunctorPlanarComplexF16F16 linear_combination_op(params);\n\n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(accum_ptr);\n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(source_ptr);\n\n  #pragma unroll 1\n  for (int n = 0; n < N; ++n) {\n    source = linear_combination_op(accum, source);\n  }\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4>*>(output_ptr) = source;\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f16_f16) {\n\n  using Element = cutlass::half_t;\n  using ElementOutput = cutlass::half_t;\n  int const kCount = 8;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination = linear_combination_op(accum, source);\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/epilogue/thread/linear_combination_planar_complex.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF32F32 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  float, \n  4,\n  float, \n  float>;\n\n__global__ void epilogue_thread_functor_planar_complex_f32_f32(\n  float *output_ptr, \n  float const *accum_ptr, \n  float const *source_ptr,\n  typename FunctorPlanarComplexF32F32::Params params) {\n\n  FunctorPlanarComplexF32F32 linear_combination_op(params);\n\n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<float , 4> const *>(accum_ptr);\n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<float, 4> const *>(source_ptr);\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<float, 4>*>(output_ptr) = linear_combination_op(accum, source);\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f32) {\n\n  using Element = float;\n  using ElementOutput = float;\n  int const kCount = 4;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination = linear_combination_op(accum, source);\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF16F32 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  cutlass::half_t, \n  4,\n  float, \n  float>;\n\n__global__ void epilogue_thread_functor_planar_complex_f16_f32(\n  cutlass::half_t *output_ptr, \n  float const *accum_ptr, \n  cutlass::half_t const *source_ptr,\n  typename FunctorPlanarComplexF16F32::Params params,\n  int N) {\n\n  FunctorPlanarComplexF16F32 linear_combination_op(params);\n\n  \n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<float , 4> const *>(accum_ptr);   \n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(source_ptr);\n\n  #pragma unroll 1\n  for (int n = 0; n < N; ++n) {\n    source = linear_combination_op(accum, source);\n  }\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4>*>(output_ptr) = source;\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f16_f32) {\n\n  using Element = float;\n  using ElementOutput = cutlass::half_t;\n  int const kCount = 4;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination{ linear_combination_op(accum, source) };\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace thread {\n\nusing FunctorPlanarComplexF16F16 = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n  cutlass::half_t, \n  4,\n  cutlass::half_t, \n  cutlass::half_t>;\n\n__global__ void epilogue_thread_functor_planar_complex_f16_f16(\n  cutlass::half_t *output_ptr, \n  cutlass::half_t const *accum_ptr, \n  cutlass::half_t const *source_ptr,\n  typename FunctorPlanarComplexF16F16::Params params,\n  int N) {\n\n  FunctorPlanarComplexF16F16 linear_combination_op(params);\n\n  auto accum = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(accum_ptr);\n  auto source = *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4> const *>(source_ptr);\n\n  #pragma unroll 1\n  for (int n = 0; n < N; ++n) {\n    source = linear_combination_op(accum, source);\n  }\n\n  *reinterpret_cast<cutlass::ArrayPlanarComplex<cutlass::half_t , 4>*>(output_ptr) = source;\n}\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_linear_combination_planar_complex, f16_f16) {\n\n  using Element = cutlass::half_t;\n  using ElementOutput = cutlass::half_t;\n  int const kCount = 8;\n\n  using Functor = cutlass::epilogue::thread::LinearCombinationPlanarComplex<\n    ElementOutput, \n    kCount,\n    Element, \n    Element>;\n\n  cutlass::complex<Element> alpha(Element(2), Element(1));\n  cutlass::complex<Element> beta(Element(1), Element(-1));\n\n  typename Functor::Params params(alpha, beta);\n\n  Functor linear_combination_op(params);\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> source;\n  cutlass::ArrayPlanarComplex<Element, kCount> accum;\n\n  // Define arbitrary inputs\n  for (int i = 0; i < kCount; ++i) {\n    accum.real[i] = Element(i * 2);\n    accum.imag[i] = Element((i * 3 % 6) - 3);\n    source.real[i] = ElementOutput((i * 7 % 9) - 4);\n    source.imag[i] = ElementOutput(((i * 5 + 2) % 9) - 4);\n  }\n\n  cutlass::ArrayPlanarComplex<ElementOutput, kCount> destination = linear_combination_op(accum, source);\n\n  // Verify each result\n  for (int i = 0; i < kCount; ++i) {\n    \n    cutlass::complex<Element> expected = alpha * cutlass::complex<Element>(accum.real[i], accum.imag[i]) + \n      beta * cutlass::complex<Element>(Element(source.real[i]), Element(source.imag[i]));\n\n    cutlass::complex<ElementOutput> got(destination.real[i], destination.imag[i]);\n    \n    EXPECT_TRUE(ElementOutput(expected.real()) == got.real());\n    EXPECT_TRUE(ElementOutput(expected.imag()) == got.imag());\n    EXPECT_TRUE(expected.real() != Element(0) || expected.imag() != Element(0));\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/epilogue/thread/linear_combination_planar_complex.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/layout/layout.h\"\n#include \"cutlass/epilogue/thread/activation.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int N, typename Func>\n__global__ void test_Epilogue_thread_activation(T *out, T *in) {\n\n  cutlass::Array<T, N> *vec_out = reinterpret_cast<cutlass::Array<T, N> *>(out);\n  cutlass::Array<T, N> *vec_in = reinterpret_cast<cutlass::Array<T, N> *>(in);\n\n  Func func;\n  vec_out[threadIdx.x] = func(vec_in[threadIdx.x]);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n//\n// Reference\n//\n\nstatic double GELU_golden_input[] = {\n    1.587425827980,  1.157652974129,  0.750432848930, -0.965980410576,\n    -0.388184845448,  0.014422321692,  0.353164494038,  1.354383468628,\n     0.167588576674,  0.272798538208, -0.377032428980,  1.923444747925,\n     0.308164477348, -0.341318070889,  0.278338819742, -0.292668998241,\n    -1.051743745804, -0.814175724983,  0.112737402320,  1.262938618660,\n    -1.582363605499,  0.722016870975,  1.053453564644, -0.659764587879,\n     0.734917521477,  0.091274201870,  0.604461073875, -0.219043627381,\n    -0.136795744300,  0.960650205612, -1.805408835411,  0.091029644012,\n    -1.023343324661,  0.147713735700, -0.499895423651,  1.351878166199,\n    -1.631091356277, -0.336171895266, -1.612408638000,  0.090832948685,\n    -0.658132910728, -0.326727777719, -1.986387014389,  0.787685871124,\n    -1.015677452087, -0.225094825029,  0.876752018929,  0.744826257229,\n     0.870290279388, -0.757595360279,  1.510331749916,  0.750012576580,\n     0.906444966793, -0.915759027004,  1.260277032852, -0.158465340734,\n    -0.109191477299, -0.817102134228,  0.391305118799, -0.524910449982,\n     0.351349592209,  0.801979541779,  0.446691334248, -0.741077482700,\n     1.205966711044, -0.910210072994,  0.945986449718,  0.784096539021,\n     1.670521497726,  0.344931513071, -0.301411420107,  0.309870749712,\n    -0.879704594612, -1.951189517975, -0.805817663670, -0.661812782288,\n    -0.505914270878, -1.836273789406, -0.381845980883, -0.554707705975,\n    -0.375447630882, -0.516645610332,  0.509586095810,  1.087131023407,\n     2.664817094803, -1.558295488358, -0.076461032033, -0.504621028900,\n     1.327111959457, -1.819981694221,  1.350415468216, -2.074112653732,\n     1.501431345940, -1.339013576508,  0.162817999721, -1.473457217216,\n     0.357770472765,  0.188413277268,  1.601302266121, -0.653882205486,\n     0.856162548065,  0.763102591038, -0.526283502579,  0.581961452961,\n     0.089969776571,  1.968745589256,  0.545802056789, -1.168786048889,\n     1.206663012505, -0.109096683562, -1.223938226700,  0.744599223137,\n    -1.779406785965,  0.766436159611, -0.579044401646, -1.002057313919,\n    -0.715845823288, -0.562508940697,  0.886768460274,  2.327786445618,\n    -0.148763969541, -0.918884515762, -0.367678701878, -1.105021238327,\n    -0.461237311363,  0.158228352666, -0.254040330648,  1.427477598190,\n     0.277530491352,  0.046293262392, -0.535557329655, -1.486695051193,\n    -0.953706681728, -1.040495038033, -0.314667612314,  0.348172843456,\n     0.522773325443,  0.025960063562, -0.482472360134,  1.993084549904,\n    -0.253064930439, -0.012146313675, -2.166327714920,  0.398040622473,\n    -0.022238900885, -0.443580865860, -0.898376941681, -0.571689844131,\n     1.666979670525, -0.831176340580, -0.671057403088,  0.481970995665,\n    -1.096243023872, -1.493894338608,  0.596651911736, -0.229505166411,\n     1.165976166725,  0.905094027519,  0.049716457725, -1.362933635712,\n    -0.366948783398,  1.461613893509, -0.718411505222,  0.895385026932,\n    -0.763122260571,  1.329716682434,  1.366570711136, -0.086544901133,\n     0.059739742428,  0.940766513348, -0.272854357958, -1.738811373711,\n    -0.361239165068,  0.696977972984,  1.288442254066,  1.264815807343,\n    -0.573566436768, -1.141678214073,  0.081865988672, -0.886228799820,\n    -0.236933603883,  1.050115466118, -0.538952171803,  0.651773929596,\n    -0.220034509897, -1.198960781097,  1.247478365898, -0.053529661149,\n     0.639809548855,  1.672434806824,  0.511088073254, -1.179364681244,\n    -0.730427742004,  0.157630980015,  0.389369845390, -0.925578773022,\n    -0.093250080943, -0.391062080860,  0.852983593941,  1.868778109550,\n    -1.198786258698,  0.604997038841, -1.482687234879, -2.469333171844,\n     0.718807697296, -0.559609353542,  2.187228441238, -2.927527904510,\n     0.148535788059, -0.097280368209,  0.674131810665, -1.137645959854,\n     0.792729616165, -1.166317462921, -0.498791724443,  1.675866723061,\n    -0.137909621000, -0.653263568878, -2.281216144562,  0.296096831560,\n     2.002410173416,  1.083609819412,  0.933580815792, -1.504760265350,\n     2.185185909271,  0.286121010780, -1.035485863686, -0.216372340918,\n    -0.274334043264, -0.849510788918, -1.397169828415, -0.407644748688,\n     0.159476816654, -0.170650705695,  0.335193097591, -0.156852483749,\n     0.036168430001,  0.858105242252, -1.086121797562,  0.404813349247,\n    -0.481496721506, -0.389882832766,  0.020690204576, -0.772020936012,\n    -0.758921504021,  0.323482036591,  0.115715265274, -0.811228036880,\n    -0.882436633110,  0.176811277866,  1.678015947342,  0.379081040621,\n    -0.842976212502,  0.346952259541, -0.545828759670,  1.632800459862\n};\n\nstatic double GELU_golden_output[] = {\n    1.498199582100,  1.014679551125,  0.580462038517, -0.161344811320,\n    -0.135453075171,  0.007294139825,  0.225325092673,  1.235459089279,\n     0.094946734607,  0.165724009275, -0.133120641112,  1.871103763580,\n     0.191376730800, -0.125069886446,  0.169681981206, -0.112644664943,\n    -0.154036879539, -0.169163048267,  0.061428427696,  1.132469892502,\n    -0.089851818979,  0.552240371704,  0.899579226971, -0.168043658137,\n     0.565008401871,  0.048956073821,  0.439583092928, -0.090532489121,\n    -0.060955654830,  0.798911273479, -0.064101703465,  0.048816055059,\n    -0.156645998359,  0.082529976964, -0.154254898429,  1.232632875443,\n    -0.083896033466, -0.123835846782, -0.086161509156,  0.048703473061,\n    -0.167972877622, -0.121522113681, -0.046670529991,  0.617986679077,\n    -0.157319813967, -0.092503339052,  0.709896743298,  0.574865520000,\n     0.703132867813, -0.169963955879,  1.411436080933,  0.580042064190,\n     0.741154611111, -0.164741978049,  1.129479527473, -0.069256491959,\n    -0.049848672003, -0.169087052345,  0.255214750767, -0.157380074263,\n     0.223928079009,  0.632535398006,  0.300378054380, -0.169946283102,\n     1.068588852882, -0.165071934462,  0.783203184605,  0.614346146584,\n     1.591325283051,  0.219006344676, -0.115003645420,  0.192637458444,\n    -0.166712537408, -0.049788996577, -0.169361919165, -0.168130636215,\n    -0.155041679740, -0.060888241976, -0.134137839079, -0.160614117980,\n    -0.132782235742, -0.156389534473,  0.354075312614,  0.936574816704,\n     2.654553413391, -0.092845752835, -0.035900454968, -0.154874503613,\n     1.204704761505, -0.062572605908,  1.230982899666, -0.039479542524,\n     1.401402950287, -0.120890334249,  0.091938301921, -0.103604510427,\n     0.228880971670,  0.108285568655,  1.513783097267, -0.167782157660,\n     0.688394129276,  0.593158841133, -0.157540664077,  0.418839782476,\n     0.048209801316,  1.920528769493,  0.386099845171, -0.141709372401,\n     1.069367766380, -0.049809500575, -0.135230198503,  0.574639260769,\n    -0.066881760955,  0.596510827541, -0.162873372436, -0.158483341336,\n    -0.169686436653, -0.161375194788,  0.720409095287,  2.304597616196,\n    -0.065585561097, -0.164551988244, -0.131098195910, -0.148708447814,\n    -0.148663327098,  0.089060656726, -0.101548098028,  1.317959904671,\n     0.169103100896,  0.024001283571, -0.158595800400, -0.101909510791,\n    -0.162240833044, -0.155090972781, -0.118474565446,  0.221488356590,\n     0.365645468235,  0.013248858973, -0.151851043105,  1.946992278099,\n    -0.101253561676, -0.006014300976, -0.032804865390,  0.260597169399,\n    -0.010922161862, -0.145792976022, -0.165743649006, -0.162226170301,\n     1.587365984917, -0.168676435947, -0.168497130275,  0.330191940069,\n    -0.149622067809, -0.100989677012,  0.432351946831, -0.093922272325,\n     1.023946166039,  0.739726305008,  0.025843897834, -0.117827951908,\n    -0.130937814713,  1.356489539146, -0.169726014137,  0.729478538036,\n    -0.169943705201,  1.207641005516,  1.249209761620, -0.040288090706,\n     0.031292784959,  0.777626037598, -0.107090584934, -0.071350336075,\n    -0.129670530558,  0.527676224709,  1.161149263382,  1.134579420090,\n    -0.162394225597, -0.144757837057,  0.043603736907, -0.166386902332,\n    -0.096278958023,  0.895924389362, -0.158969298005,  0.484089732170,\n    -0.090857118368, -0.138206124306,  1.115107178688, -0.025622237474,\n     0.472724437714,  1.593463659286,  0.355387806892, -0.140493586659,\n    -0.169871479273,  0.088687323034,  0.253673940897, -0.164135158062,\n    -0.043161027133, -0.136040985584,  0.685087263584,  1.811169505119,\n    -0.138226687908,  0.440080583096, -0.102422207594, -0.016713079065,\n     0.549075841904, -0.161096408963,  2.155813455582, -0.005001218989,\n     0.083037458360, -0.044870752841,  0.505522191525, -0.145202502608,\n     0.623111069202, -0.141991063952, -0.154108211398,  1.597298502922,\n    -0.061391282827, -0.167753636837, -0.025704355910,  0.182520583272,\n     1.957115054131,  0.932696640491,  0.769961357117, -0.099604383111,\n     2.153636932373,  0.175279796124, -0.155551761389, -0.089653611183,\n    -0.107515335083, -0.168032020330, -0.113423995674, -0.139319628477,\n     0.089841812849, -0.073763631284,  0.211594089866, -0.068651281297,\n     0.018605981022,  0.690416753292, -0.150658726692,  0.266040354967,\n    -0.151710823178, -0.135800719261,  0.010515870526, -0.169883996248,\n    -0.169960290194,  0.202769815922,  0.063187584281, -0.169236257672,\n    -0.166577890515,  0.100812792778,  1.599699616432,  0.245525524020,\n    -0.168275654316,  0.220552831888, -0.159705042839,  1.549110531807\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_gelu_taylor, device_f32) {\n\n    int const kN = 256;\n    int const kV = 4;\n\n    using Element = float;\n    using Func = cutlass::epilogue::thread::GELU_taylor<cutlass::Array<Element, kV>>;\n\n    double tolerance = 0.005;\n    \n    //\n    // Construct workspace\n    //\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Destination({1, kN});\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Source({1, kN});\n\n    for (int i = 0; i < kN; ++i) {\n        tensor_Source.host_data(i) = Element(GELU_golden_input[i]);\n    }\n\n    tensor_Destination.sync_device();\n    tensor_Source.sync_device();\n\n    //\n    // Launch the kernel\n    //\n    dim3 grid(1,1,1);\n    dim3 block(kN / kV, 1, 1);\n\n    test_Epilogue_thread_activation<Element, kV, Func><<< grid, block >>>(\n        tensor_Destination.device_data(),\n        tensor_Source.device_data());\n\n    tensor_Destination.sync_host();\n\n    //\n    // Verify\n    //\n\n    for (int i = 0; i < kN; ++i) {\n        Element input = Element(GELU_golden_input[i]);\n        Element got = tensor_Destination.host_data(i);\n        Element expected = Element(GELU_golden_output[i]);\n\n        double rel_error = (double(got) - double(expected)) / double(expected);\n\n        double tolerance_override = tolerance;\n\n        switch (i) {\n            case 142: tolerance_override = 0.008; break;\n            case 203: tolerance_override = 0.03; break;\n            case 207: tolerance_override = 0.09; break;\n            case 218: tolerance_override = 0.013; break;\n        }\n\n        EXPECT_LT(std::abs(rel_error), tolerance_override) \n            << \"Input[\" << i << \"]: \" << input << \", Got: \" << got << \", expected: \" << expected;\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_gelu_taylor, device_f16) {\n\n    int const kN = 256;\n    int const kV = 8;\n\n    using Element = cutlass::half_t;\n    using Func = cutlass::epilogue::thread::GELU_taylor<cutlass::Array<Element, kV>>;\n\n    double tolerance = 0.005;\n\n    //\n    // Construct workspace\n    //\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Destination({1, kN});\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Source({1, kN});\n\n    for (int i = 0; i < kN; ++i) {\n        tensor_Source.host_data(i) = Element(GELU_golden_input[i]);\n    }\n\n    tensor_Destination.sync_device();\n    tensor_Source.sync_device();\n\n    //\n    // Launch the kernel\n    //\n    dim3 grid(1,1,1);\n    dim3 block(kN / kV, 1, 1);\n\n    test_Epilogue_thread_activation<Element, kV, Func><<< grid, block >>>(\n        tensor_Destination.device_data(),\n        tensor_Source.device_data());\n\n    tensor_Destination.sync_host();\n\n    //\n    // Verify\n    //\n\n    for (int i = 0; i < kN; ++i) {\n        Element input = Element(GELU_golden_input[i]);\n        Element got = tensor_Destination.host_data(i);\n        Element expected = Element(GELU_golden_output[i]);\n\n        double rel_error = (double(got) - double(expected)) / double(expected);\n        \n        double tolerance_override = tolerance;\n\n        switch (i) {\n            case 36: tolerance_override = 0.006; break;\n            case 77: tolerance_override = 0.009; break;\n            case 95: tolerance_override = 0.008; break;\n            case 112: tolerance_override = 0.007; break;\n            case 171: tolerance_override = 0.006; break;\n            case 203: tolerance_override = 0.03; break;\n            case 207: tolerance_override = 0.15; break;\n        }\n\n        EXPECT_LT(std::abs(rel_error), tolerance_override) \n            << \"Input[\" << i << \"]: \" << input << \", Got: \" << got << \", expected: \" << expected;\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"hip/hip_runtime.h\"\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/layout/layout.h\"\n#include \"cutlass/epilogue/thread/activation.h\"\n\n#include \"cutlass/util/host_tensor.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, int N, typename Func>\n__global__ void test_Epilogue_thread_activation(T *out, T *in) {\n\n  cutlass::Array<T, N> *vec_out = reinterpret_cast<cutlass::Array<T, N> *>(out);\n  cutlass::Array<T, N> *vec_in = reinterpret_cast<cutlass::Array<T, N> *>(in);\n\n  Func func;\n  vec_out[threadIdx.x] = func(vec_in[threadIdx.x]);\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n//\n// Reference\n//\n\nstatic double GELU_golden_input[] = {\n    1.587425827980,  1.157652974129,  0.750432848930, -0.965980410576,\n    -0.388184845448,  0.014422321692,  0.353164494038,  1.354383468628,\n     0.167588576674,  0.272798538208, -0.377032428980,  1.923444747925,\n     0.308164477348, -0.341318070889,  0.278338819742, -0.292668998241,\n    -1.051743745804, -0.814175724983,  0.112737402320,  1.262938618660,\n    -1.582363605499,  0.722016870975,  1.053453564644, -0.659764587879,\n     0.734917521477,  0.091274201870,  0.604461073875, -0.219043627381,\n    -0.136795744300,  0.960650205612, -1.805408835411,  0.091029644012,\n    -1.023343324661,  0.147713735700, -0.499895423651,  1.351878166199,\n    -1.631091356277, -0.336171895266, -1.612408638000,  0.090832948685,\n    -0.658132910728, -0.326727777719, -1.986387014389,  0.787685871124,\n    -1.015677452087, -0.225094825029,  0.876752018929,  0.744826257229,\n     0.870290279388, -0.757595360279,  1.510331749916,  0.750012576580,\n     0.906444966793, -0.915759027004,  1.260277032852, -0.158465340734,\n    -0.109191477299, -0.817102134228,  0.391305118799, -0.524910449982,\n     0.351349592209,  0.801979541779,  0.446691334248, -0.741077482700,\n     1.205966711044, -0.910210072994,  0.945986449718,  0.784096539021,\n     1.670521497726,  0.344931513071, -0.301411420107,  0.309870749712,\n    -0.879704594612, -1.951189517975, -0.805817663670, -0.661812782288,\n    -0.505914270878, -1.836273789406, -0.381845980883, -0.554707705975,\n    -0.375447630882, -0.516645610332,  0.509586095810,  1.087131023407,\n     2.664817094803, -1.558295488358, -0.076461032033, -0.504621028900,\n     1.327111959457, -1.819981694221,  1.350415468216, -2.074112653732,\n     1.501431345940, -1.339013576508,  0.162817999721, -1.473457217216,\n     0.357770472765,  0.188413277268,  1.601302266121, -0.653882205486,\n     0.856162548065,  0.763102591038, -0.526283502579,  0.581961452961,\n     0.089969776571,  1.968745589256,  0.545802056789, -1.168786048889,\n     1.206663012505, -0.109096683562, -1.223938226700,  0.744599223137,\n    -1.779406785965,  0.766436159611, -0.579044401646, -1.002057313919,\n    -0.715845823288, -0.562508940697,  0.886768460274,  2.327786445618,\n    -0.148763969541, -0.918884515762, -0.367678701878, -1.105021238327,\n    -0.461237311363,  0.158228352666, -0.254040330648,  1.427477598190,\n     0.277530491352,  0.046293262392, -0.535557329655, -1.486695051193,\n    -0.953706681728, -1.040495038033, -0.314667612314,  0.348172843456,\n     0.522773325443,  0.025960063562, -0.482472360134,  1.993084549904,\n    -0.253064930439, -0.012146313675, -2.166327714920,  0.398040622473,\n    -0.022238900885, -0.443580865860, -0.898376941681, -0.571689844131,\n     1.666979670525, -0.831176340580, -0.671057403088,  0.481970995665,\n    -1.096243023872, -1.493894338608,  0.596651911736, -0.229505166411,\n     1.165976166725,  0.905094027519,  0.049716457725, -1.362933635712,\n    -0.366948783398,  1.461613893509, -0.718411505222,  0.895385026932,\n    -0.763122260571,  1.329716682434,  1.366570711136, -0.086544901133,\n     0.059739742428,  0.940766513348, -0.272854357958, -1.738811373711,\n    -0.361239165068,  0.696977972984,  1.288442254066,  1.264815807343,\n    -0.573566436768, -1.141678214073,  0.081865988672, -0.886228799820,\n    -0.236933603883,  1.050115466118, -0.538952171803,  0.651773929596,\n    -0.220034509897, -1.198960781097,  1.247478365898, -0.053529661149,\n     0.639809548855,  1.672434806824,  0.511088073254, -1.179364681244,\n    -0.730427742004,  0.157630980015,  0.389369845390, -0.925578773022,\n    -0.093250080943, -0.391062080860,  0.852983593941,  1.868778109550,\n    -1.198786258698,  0.604997038841, -1.482687234879, -2.469333171844,\n     0.718807697296, -0.559609353542,  2.187228441238, -2.927527904510,\n     0.148535788059, -0.097280368209,  0.674131810665, -1.137645959854,\n     0.792729616165, -1.166317462921, -0.498791724443,  1.675866723061,\n    -0.137909621000, -0.653263568878, -2.281216144562,  0.296096831560,\n     2.002410173416,  1.083609819412,  0.933580815792, -1.504760265350,\n     2.185185909271,  0.286121010780, -1.035485863686, -0.216372340918,\n    -0.274334043264, -0.849510788918, -1.397169828415, -0.407644748688,\n     0.159476816654, -0.170650705695,  0.335193097591, -0.156852483749,\n     0.036168430001,  0.858105242252, -1.086121797562,  0.404813349247,\n    -0.481496721506, -0.389882832766,  0.020690204576, -0.772020936012,\n    -0.758921504021,  0.323482036591,  0.115715265274, -0.811228036880,\n    -0.882436633110,  0.176811277866,  1.678015947342,  0.379081040621,\n    -0.842976212502,  0.346952259541, -0.545828759670,  1.632800459862\n};\n\nstatic double GELU_golden_output[] = {\n    1.498199582100,  1.014679551125,  0.580462038517, -0.161344811320,\n    -0.135453075171,  0.007294139825,  0.225325092673,  1.235459089279,\n     0.094946734607,  0.165724009275, -0.133120641112,  1.871103763580,\n     0.191376730800, -0.125069886446,  0.169681981206, -0.112644664943,\n    -0.154036879539, -0.169163048267,  0.061428427696,  1.132469892502,\n    -0.089851818979,  0.552240371704,  0.899579226971, -0.168043658137,\n     0.565008401871,  0.048956073821,  0.439583092928, -0.090532489121,\n    -0.060955654830,  0.798911273479, -0.064101703465,  0.048816055059,\n    -0.156645998359,  0.082529976964, -0.154254898429,  1.232632875443,\n    -0.083896033466, -0.123835846782, -0.086161509156,  0.048703473061,\n    -0.167972877622, -0.121522113681, -0.046670529991,  0.617986679077,\n    -0.157319813967, -0.092503339052,  0.709896743298,  0.574865520000,\n     0.703132867813, -0.169963955879,  1.411436080933,  0.580042064190,\n     0.741154611111, -0.164741978049,  1.129479527473, -0.069256491959,\n    -0.049848672003, -0.169087052345,  0.255214750767, -0.157380074263,\n     0.223928079009,  0.632535398006,  0.300378054380, -0.169946283102,\n     1.068588852882, -0.165071934462,  0.783203184605,  0.614346146584,\n     1.591325283051,  0.219006344676, -0.115003645420,  0.192637458444,\n    -0.166712537408, -0.049788996577, -0.169361919165, -0.168130636215,\n    -0.155041679740, -0.060888241976, -0.134137839079, -0.160614117980,\n    -0.132782235742, -0.156389534473,  0.354075312614,  0.936574816704,\n     2.654553413391, -0.092845752835, -0.035900454968, -0.154874503613,\n     1.204704761505, -0.062572605908,  1.230982899666, -0.039479542524,\n     1.401402950287, -0.120890334249,  0.091938301921, -0.103604510427,\n     0.228880971670,  0.108285568655,  1.513783097267, -0.167782157660,\n     0.688394129276,  0.593158841133, -0.157540664077,  0.418839782476,\n     0.048209801316,  1.920528769493,  0.386099845171, -0.141709372401,\n     1.069367766380, -0.049809500575, -0.135230198503,  0.574639260769,\n    -0.066881760955,  0.596510827541, -0.162873372436, -0.158483341336,\n    -0.169686436653, -0.161375194788,  0.720409095287,  2.304597616196,\n    -0.065585561097, -0.164551988244, -0.131098195910, -0.148708447814,\n    -0.148663327098,  0.089060656726, -0.101548098028,  1.317959904671,\n     0.169103100896,  0.024001283571, -0.158595800400, -0.101909510791,\n    -0.162240833044, -0.155090972781, -0.118474565446,  0.221488356590,\n     0.365645468235,  0.013248858973, -0.151851043105,  1.946992278099,\n    -0.101253561676, -0.006014300976, -0.032804865390,  0.260597169399,\n    -0.010922161862, -0.145792976022, -0.165743649006, -0.162226170301,\n     1.587365984917, -0.168676435947, -0.168497130275,  0.330191940069,\n    -0.149622067809, -0.100989677012,  0.432351946831, -0.093922272325,\n     1.023946166039,  0.739726305008,  0.025843897834, -0.117827951908,\n    -0.130937814713,  1.356489539146, -0.169726014137,  0.729478538036,\n    -0.169943705201,  1.207641005516,  1.249209761620, -0.040288090706,\n     0.031292784959,  0.777626037598, -0.107090584934, -0.071350336075,\n    -0.129670530558,  0.527676224709,  1.161149263382,  1.134579420090,\n    -0.162394225597, -0.144757837057,  0.043603736907, -0.166386902332,\n    -0.096278958023,  0.895924389362, -0.158969298005,  0.484089732170,\n    -0.090857118368, -0.138206124306,  1.115107178688, -0.025622237474,\n     0.472724437714,  1.593463659286,  0.355387806892, -0.140493586659,\n    -0.169871479273,  0.088687323034,  0.253673940897, -0.164135158062,\n    -0.043161027133, -0.136040985584,  0.685087263584,  1.811169505119,\n    -0.138226687908,  0.440080583096, -0.102422207594, -0.016713079065,\n     0.549075841904, -0.161096408963,  2.155813455582, -0.005001218989,\n     0.083037458360, -0.044870752841,  0.505522191525, -0.145202502608,\n     0.623111069202, -0.141991063952, -0.154108211398,  1.597298502922,\n    -0.061391282827, -0.167753636837, -0.025704355910,  0.182520583272,\n     1.957115054131,  0.932696640491,  0.769961357117, -0.099604383111,\n     2.153636932373,  0.175279796124, -0.155551761389, -0.089653611183,\n    -0.107515335083, -0.168032020330, -0.113423995674, -0.139319628477,\n     0.089841812849, -0.073763631284,  0.211594089866, -0.068651281297,\n     0.018605981022,  0.690416753292, -0.150658726692,  0.266040354967,\n    -0.151710823178, -0.135800719261,  0.010515870526, -0.169883996248,\n    -0.169960290194,  0.202769815922,  0.063187584281, -0.169236257672,\n    -0.166577890515,  0.100812792778,  1.599699616432,  0.245525524020,\n    -0.168275654316,  0.220552831888, -0.159705042839,  1.549110531807\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_gelu_taylor, device_f32) {\n\n    int const kN = 256;\n    int const kV = 4;\n\n    using Element = float;\n    using Func = cutlass::epilogue::thread::GELU_taylor<cutlass::Array<Element, kV>>;\n\n    double tolerance = 0.005;\n    \n    //\n    // Construct workspace\n    //\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Destination({1, kN});\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Source({1, kN});\n\n    for (int i = 0; i < kN; ++i) {\n        tensor_Source.host_data(i) = Element(GELU_golden_input[i]);\n    }\n\n    tensor_Destination.sync_device();\n    tensor_Source.sync_device();\n\n    //\n    // Launch the kernel\n    //\n    dim3 grid(1,1,1);\n    dim3 block(kN / kV, 1, 1);\n\n    test_Epilogue_thread_activation<Element, kV, Func><<< grid, block >>>(\n        tensor_Destination.device_data(),\n        tensor_Source.device_data());\n\n    tensor_Destination.sync_host();\n\n    //\n    // Verify\n    //\n\n    for (int i = 0; i < kN; ++i) {\n        Element input = Element(GELU_golden_input[i]);\n        Element got = tensor_Destination.host_data(i);\n        Element expected = Element(GELU_golden_output[i]);\n\n        double rel_error = (double(got) - double(expected)) / double(expected);\n\n        double tolerance_override = tolerance;\n\n        switch (i) {\n            case 142: tolerance_override = 0.008; break;\n            case 203: tolerance_override = 0.03; break;\n            case 207: tolerance_override = 0.09; break;\n            case 218: tolerance_override = 0.013; break;\n        }\n\n        EXPECT_LT(std::abs(rel_error), tolerance_override) \n            << \"Input[\" << i << \"]: \" << input << \", Got: \" << got << \", expected: \" << expected;\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(Epilogue_thread_gelu_taylor, device_f16) {\n\n    int const kN = 256;\n    int const kV = 8;\n\n    using Element = cutlass::half_t;\n    using Func = cutlass::epilogue::thread::GELU_taylor<cutlass::Array<Element, kV>>;\n\n    double tolerance = 0.005;\n\n    //\n    // Construct workspace\n    //\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Destination({1, kN});\n    cutlass::HostTensor<Element, cutlass::layout::RowMajor> tensor_Source({1, kN});\n\n    for (int i = 0; i < kN; ++i) {\n        tensor_Source.host_data(i) = Element(GELU_golden_input[i]);\n    }\n\n    tensor_Destination.sync_device();\n    tensor_Source.sync_device();\n\n    //\n    // Launch the kernel\n    //\n    dim3 grid(1,1,1);\n    dim3 block(kN / kV, 1, 1);\n\n    test_Epilogue_thread_activation<Element, kV, Func><<< grid, block >>>(\n        tensor_Destination.device_data(),\n        tensor_Source.device_data());\n\n    tensor_Destination.sync_host();\n\n    //\n    // Verify\n    //\n\n    for (int i = 0; i < kN; ++i) {\n        Element input = Element(GELU_golden_input[i]);\n        Element got = tensor_Destination.host_data(i);\n        Element expected = Element(GELU_golden_output[i]);\n\n        double rel_error = (double(got) - double(expected)) / double(expected);\n        \n        double tolerance_override = tolerance;\n\n        switch (i) {\n            case 36: tolerance_override = 0.006; break;\n            case 77: tolerance_override = 0.009; break;\n            case 95: tolerance_override = 0.008; break;\n            case 112: tolerance_override = 0.007; break;\n            case 171: tolerance_override = 0.006; break;\n            case 203: tolerance_override = 0.03; break;\n            case 207: tolerance_override = 0.15; break;\n        }\n\n        EXPECT_LT(std::abs(rel_error), tolerance_override) \n            << \"Input[\" << i << \"]: \" << input << \", Got: \" << got << \", expected: \" << expected;\n    }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/epilogue/thread/activation.cu"}
{"input": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include <fstream>\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/half.h\"\n\n#include \"cutlass/epilogue/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/epilogue/threadblock/default_thread_map_tensor_op.h\"\n\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename TileIterator>\n__global__ void kernel_store_iterator(\n  typename TileIterator::Params params,\n  typename TileIterator::TensorRef ref, \n  cutlass::MatrixCoord extent) {\n\n  TileIterator iterator(params, ref.data(), extent, threadIdx.x, {0, 0});\n\n  typename TileIterator::Fragment fragment;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int iter = 0; iter < TileIterator::ThreadMap::Count::kTile; ++iter) {\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < TileIterator::Fragment::kElements; ++i) {\n      typename TileIterator::Element tidx(iter + 1);\n      fragment[i] = tidx;\n    }\n\n    iterator.store(fragment);\n    \n    ++iterator;  \n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, typename Layout>\nstatic bool verify_footprint(cutlass::TensorView<T, Layout> view, cutlass::MatrixCoord extent) {\n\n  for (int r = 0; r < view.extent().row(); ++r) {\n    for (int c = 0; c < view.extent().column(); ++c) {\n\n      cutlass::MatrixCoord coord{r, c};\n      bool within = coord < extent;\n      if (within) {\n        if (view.at(coord) == T(0)) {\n          return false;\n        }\n      }\n      else {\n        if (view.at(coord) != T(0)) {\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_64x64x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n  //\n  // The following tests were used to develop the OutputTileOptimalThreadMap\n  // metaprogram. The definitions in the disabled blocks of code in this and\n  // the following tests are hand-written quantities. They are expected to\n  // match what is defined in the ThreadMap.\n  //\n\n  #if 1\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 8, 1, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 1, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 64>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 64};\n  cutlass::MatrixCoord output_extent{62, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_64x64x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_128x64x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 64;\n\n  #if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 8, 2, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 128>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    8,    // row\n    2,    // group\n    1,    // cluster\n    8     // tile\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    2,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    64,    // group\n    1,    // cluster\n    1     // tile\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 64};\n  cutlass::MatrixCoord output_extent{125, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_128x64x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_128x256x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n  #if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<256, 8, 2, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<256, 128>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    256,   // column\n    8,    // row\n    2,    // group\n    1,    // cluster\n    8     // tile\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    2,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    64,    // group\n    1,    // cluster\n    1     // tile\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 256};\n  cutlass::MatrixCoord output_extent{123, 252};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_128x256x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_64x64x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 2, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 8>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    2,    // row\n    4,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    8,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 64};\n  cutlass::MatrixCoord output_extent{62, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"volta_tensor_op_64x64x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_64x128x32_32x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 128;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 2, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<128, 8>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    128,   // column\n    2,    // row\n    2,    // group\n    2,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    1,    // group\n    2,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    8,    // group\n    32,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    4,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 128};\n  cutlass::MatrixCoord output_extent{57, 124};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"volta_tensor_op_64x128x32_32x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_128x256x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<256, 2, 4, 2, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<256, 16>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    256,   // column\n    2,    // row\n    4,    // group\n    2,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    2,    // group\n    2,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    16,    // group\n    64,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 256};\n  cutlass::MatrixCoord output_extent{128, 256};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed || true) {\n    std::ofstream output(\"volta_tensor_op_128x256x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n\nTEST(PredicatedTileIterator, volta_tensor_op_256x128x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 2, 4, 4, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{ 256, 128 };\n  cutlass::MatrixCoord output_extent{ 256, 128 };\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1, 1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator> <<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed || true) {\n    std::ofstream output(\"volta_tensor_op_256x128x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, simt_32x64x8_32x64x1) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 32 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 1, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 4>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    2,    // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    32,    // column\n    1,    // row\n    4,    // group\n    16,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{32, 64};\n  cutlass::MatrixCoord output_extent{27, 63};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"simt_32x64x8_32x64x1.csv\");\n    output << host_tensor.host_view(); \n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, simt_128x128x8_32x64x1) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 32 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 1, 4, 4, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<128, 16>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    128,   // column\n    1,    // row\n    4,    // group\n    4,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    2,    // column\n    1,    // row\n    2,    // group\n    4,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    32,    // column\n    1,    // row\n    8,    // group\n    32,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 128};\n  cutlass::MatrixCoord output_extent{123, 121};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  cudaError_t result = cudaDeviceSynchronize();\n  ASSERT_EQ(result, cudaSuccess) << cudaGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"simt_128x128x8_32x64x1.csv\");\n    output << host_tensor.host_view(); \n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "output": "/***************************************************************************************************\n * Copyright (c) 2017 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n **************************************************************************************************/\n/*! \\file\n    \\brief Unit tests for thread-level GEMM\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <fstream>\n\n#include \"../../common/cutlass_unit_test.h\"\n\n#include \"cutlass/aligned_buffer.h\"\n#include \"cutlass/half.h\"\n\n#include \"cutlass/epilogue/threadblock/predicated_tile_iterator.h\"\n#include \"cutlass/epilogue/threadblock/default_thread_map_tensor_op.h\"\n\n#include \"cutlass/util/tensor_view_io.h\"\n#include \"cutlass/util/host_tensor.h\"\n#include \"cutlass/util/reference/host/tensor_fill.h\"\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace test {\nnamespace epilogue {\nnamespace threadblock {\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename TileIterator>\n__global__ void kernel_store_iterator(\n  typename TileIterator::Params params,\n  typename TileIterator::TensorRef ref, \n  cutlass::MatrixCoord extent) {\n\n  TileIterator iterator(params, ref.data(), extent, threadIdx.x, {0, 0});\n\n  typename TileIterator::Fragment fragment;\n\n  CUTLASS_PRAGMA_NO_UNROLL\n  for (int iter = 0; iter < TileIterator::ThreadMap::Count::kTile; ++iter) {\n\n    CUTLASS_PRAGMA_UNROLL\n    for (int i = 0; i < TileIterator::Fragment::kElements; ++i) {\n      typename TileIterator::Element tidx(iter + 1);\n      fragment[i] = tidx;\n    }\n\n    iterator.store(fragment);\n    \n    ++iterator;  \n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n}\n}\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename T, typename Layout>\nstatic bool verify_footprint(cutlass::TensorView<T, Layout> view, cutlass::MatrixCoord extent) {\n\n  for (int r = 0; r < view.extent().row(); ++r) {\n    for (int c = 0; c < view.extent().column(); ++c) {\n\n      cutlass::MatrixCoord coord{r, c};\n      bool within = coord < extent;\n      if (within) {\n        if (view.at(coord) == T(0)) {\n          return false;\n        }\n      }\n      else {\n        if (view.at(coord) != T(0)) {\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_64x64x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n  //\n  // The following tests were used to develop the OutputTileOptimalThreadMap\n  // metaprogram. The definitions in the disabled blocks of code in this and\n  // the following tests are hand-written quantities. They are expected to\n  // match what is defined in the ThreadMap.\n  //\n\n  #if 1\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 8, 1, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 1, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 64>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    1,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 64};\n  cutlass::MatrixCoord output_extent{62, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_64x64x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_128x64x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 64;\n\n  #if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 8, 2, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 128>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    8,    // row\n    2,    // group\n    1,    // cluster\n    8     // tile\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    2,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    64,    // group\n    1,    // cluster\n    1     // tile\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 64};\n  cutlass::MatrixCoord output_extent{125, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_128x64x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, tensor_op_128x256x32_64x64x8) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n  #if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<256, 8, 2, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 8, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  #else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<256, 128>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    256,   // column\n    8,    // row\n    2,    // group\n    1,    // cluster\n    8     // tile\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    2,    // row\n    2,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    64,    // group\n    1,    // cluster\n    1     // tile\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    8,    // row\n    1,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n  #endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 256};\n  cutlass::MatrixCoord output_extent{123, 252};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"tensor_op_128x256x32_64x64x8.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_64x64x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 2, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 8>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    2,    // row\n    4,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    8,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 64};\n  cutlass::MatrixCoord output_extent{62, 56};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n  host_tensor.sync_host();\n  \n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"volta_tensor_op_64x64x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_64x128x32_32x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 128;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 2, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<128, 8>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    128,   // column\n    2,    // row\n    2,    // group\n    2,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    1,    // group\n    2,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    8,    // group\n    32,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    4,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{64, 128};\n  cutlass::MatrixCoord output_extent{57, 124};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"volta_tensor_op_64x128x32_32x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, volta_tensor_op_128x256x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<256, 2, 4, 2, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<256, 16>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    256,   // column\n    2,    // row\n    4,    // group\n    2,    // cluster\n    8     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    2,    // group\n    2,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    1,    // row\n    16,    // group\n    64,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 256};\n  cutlass::MatrixCoord output_extent{128, 256};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed || true) {\n    std::ofstream output(\"volta_tensor_op_128x256x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n\nTEST(PredicatedTileIterator, volta_tensor_op_256x128x32_64x64x4) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 128 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 2, 4, 4, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{ 256, 128 };\n  cutlass::MatrixCoord output_extent{ 256, 128 };\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1, 1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator> <<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed || true) {\n    std::ofstream output(\"volta_tensor_op_256x128x32_64x64x4.csv\");\n    output << host_tensor.host_view();\n  }\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, simt_32x64x8_32x64x1) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 32 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 32;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<64, 1, 4, 1, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<64, 4>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    64,   // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    2,    // column\n    1,    // row\n    4,    // group\n    1,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    32,    // column\n    1,    // row\n    4,    // group\n    16,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{32, 64};\n  cutlass::MatrixCoord output_extent{27, 63};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"simt_32x64x8_32x64x1.csv\");\n    output << host_tensor.host_view(); \n  }\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\nTEST(PredicatedTileIterator, simt_128x128x8_32x64x1) {\n\n  using Layout = cutlass::layout::RowMajor;\n  using Element = int;\n\n  static int const kElementsPerAccess = 32 / cutlass::sizeof_bits<Element>::value;\n  static int const kThreads = 256;\n\n#if 1\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileOptimalThreadMap <\n    cutlass::epilogue::threadblock::OutputTileShape<128, 1, 4, 4, 1>,\n    cutlass::epilogue::threadblock::OutputTileShape<1, 4, 2, 1, 8>,\n    kThreads,\n    kElementsPerAccess,\n    cutlass::sizeof_bits<Element>::value\n  >;\n\n#else\n  using InternalThreadMap = cutlass::transform::PitchLinearStripminedThreadMap<\n    cutlass::layout::PitchLinearShape<128, 16>,\n    kThreads,\n    kElementsPerAccess\n  >;\n\n  using Shape = cutlass::epilogue::threadblock::OutputTileShape<\n    128,   // column\n    1,    // row\n    4,    // group\n    4,    // cluster\n    1     // iterations\n  >;\n\n  using Iterations = cutlass::epilogue::threadblock::OutputTileShape<\n    2,    // column\n    1,    // row\n    2,    // group\n    4,    // cluster\n    1     // iterations\n  >;\n\n  using Delta = cutlass::epilogue::threadblock::OutputTileShape<\n    32,    // column\n    1,    // row\n    8,    // group\n    32,    // cluster\n    1     // iterations\n  >;\n\n  using Count = cutlass::epilogue::threadblock::OutputTileShape<\n    1,    // column\n    4,    // row\n    2,    // group\n    1,    // cluster\n    8     // iterations\n  >;\n\n  using ThreadMap = cutlass::epilogue::threadblock::OutputTileThreadMap<\n    InternalThreadMap,\n    Shape,\n    Iterations,\n    Delta,\n    Count\n  >;\n#endif\n\n  using PredicatedTileIterator = cutlass::epilogue::threadblock::PredicatedTileIterator<\n    ThreadMap,\n    Element\n  >;\n\n  //\n  // Initialize workspace\n  //\n  cutlass::MatrixCoord tensor_extent{128, 128};\n  cutlass::MatrixCoord output_extent{123, 121};\n\n  //\n  // Configure parameters\n  //\n\n  cutlass::HostTensor<Element, Layout> host_tensor(tensor_extent);\n\n  typename PredicatedTileIterator::Params iterator_params(host_tensor.layout());\n\n  host_tensor.sync_device();\n\n  //\n  // Launch kernel\n  //\n\n  dim3 grid(1,1);\n  dim3 block(kThreads, 1);\n\n  test::epilogue::threadblock::kernel_store_iterator<PredicatedTileIterator><<< grid, block >>>(\n    iterator_params, host_tensor.device_ref(), output_extent);\n\n  hipError_t result = hipDeviceSynchronize();\n  ASSERT_EQ(result, hipSuccess) << hipGetErrorString(result);\n\n  //\n  // Verify results\n  //\n\n  host_tensor.sync_host();\n\n  bool passed = verify_footprint(host_tensor.host_view(), output_extent);\n  EXPECT_TRUE(passed);\n\n  if (!passed) {\n    std::ofstream output(\"simt_128x128x8_32x64x1.csv\");\n    output << host_tensor.host_view(); \n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////////////\n", "source": "/kaggle/working/cutlass/test/unit/epilogue/threadblock/predicated_tile_iterator.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Simple example of how to use GDS with nvcomp.\n// GDS (GPU Direct Storage) allows to read and write from/to NVMe drives\n// directly from the GPU, bypassing the CPU.\n//\n// For best performance, the I/O buffer should be registered but it is not\n// mandatory. Registration can be expensive but done only once, and allows the\n// I/O to be performed directly from the registered buffer. Otherwise, GDS will\n// use it's own intermediate buffer, at the expense of extra memory copies.\n// Similarly, I/Os with a base address or size which is not aligned on 4KB will\n// go through GDS's internal buffer and will be less efficient.\n//\n// For more details on GDS, included the supported GPUs, please see the\n// documentation. https://docs.nvidia.com/gpudirect-storage/\n//\n// To compile this GDS example, GDS must be installed, and the following\n// option must be passed when configuring cmake:\n// cmake -DBUILD_GDS_EXAMPLE=on <...>\n\n#include <fcntl.h>\n#include <iostream>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <cufile.h>\n#include <nvtx3/nvToolsExt.h>\n\n#include \"nvcomp.hpp\"\n#include \"nvcomp/lz4.hpp\"\n\n#include \"util.h\"\n\nusing namespace nvcomp;\n\n// Kernel to initialize the input data with sequential bytes\n__global__ void initialize(uint8_t* data, size_t n)\n{\n  size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n    data[i] = i & 0xff;\n}\n\n// Kernel to compare 2 buffers. Invalid flag must be set to zero before.\n__global__ void\ncompare(const uint8_t* ref, const uint8_t* val, int* invalid, size_t n)\n{\n  size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n  while (i < n) {\n    if (ref[i] != val[i])\n      *invalid = 1;\n    i += stride;\n  }\n}\n\nvoid usage(const char* str)\n{\n  printf(\"Argument: %s <filename>\\n\", str);\n  exit(-1);\n}\n\nint main(int argc, char** argv)\n{\n  if (argc != 2)\n    usage(argv[0]);\n\n  // Open the file. Note: GDS requires O_DIRECT.\n  const char* filename = argv[1];\n  int fd = open(filename, O_RDWR | O_TRUNC | O_CREAT | O_DIRECT, 0666);\n  if (fd == -1) {\n    printf(\"Error, cannot create the file: %s\\n\", filename);\n    return -1;\n  }\n  cudaDeviceProp deviceProp;\n  CUDA_CHECK(cudaGetDeviceProperties(&deviceProp, 0));\n  printf(\"Using device: %s\\n\", deviceProp.name);\n  int smcount = deviceProp.multiProcessorCount;\n\n  // Uncompressed data = 100 MB\n  const size_t n = 100000000;\n\n  // Device pointers for the data to be compressed / decompressed\n  uint8_t *d_input, *d_output;\n  cudaStream_t stream;\n  CUDA_CHECK(cudaMallocSafe(&d_input, n));\n  CUDA_CHECK(cudaMallocSafe(&d_output, n));\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Initialize the input data (sequential bytes)\n  initialize<<<(n - 1) / 512 + 1, 512, 0, stream>>>(d_input, n);\n\n  // Using NVTX to highlight the different phases of the program in the Nsight\n  // Systems profiler\n  nvtxRangePushA(\"Compressor setup\");\n\n  // Create an LZ4 compressor, get the max output size, and temp storage size\n  constexpr size_t chunk_size = 1 << 16;\n  static_assert(chunk_size <= nvcompLZ4CompressionMaxAllowedChunkSize, \"Chunk size must be less than the constant specified in the nvCOMP library\");\n\n  LZ4Manager compressor(chunk_size, nvcompBatchedLZ4CompressDefaultOpts, nvcompBatchedLZ4DecompressDefaultOpts, stream);\n  const CompressionConfig comp_config = compressor.configure_compression(n);\n  size_t lcompbuf = comp_config.max_compressed_buffer_size;\n\n  // The compressed output buffer is padded to the next multiple of 4KB\n  // for best I/O performance. Unaligned I/Os go through an extra\n  // memory copy (GDS's internal aligned registered buffer)\n  lcompbuf = ((lcompbuf - 1) / 4096 + 1) * 4096;\n  uint8_t* d_compressed;\n  CUDA_CHECK(cudaMallocSafe(&d_compressed, lcompbuf));\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS setup\");\n\n  // Initialize the cufile driver\n  CUfileError_t status = cuFileDriverOpen();\n  if (status.err != CU_FILE_SUCCESS) {\n    printf(\"Error: cuFileDriverOpen failed (%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Register the file with GDS\n  CUfileDescr_t cf_descr;\n  memset(&cf_descr, 0, sizeof(CUfileDescr_t));\n  cf_descr.handle.fd = fd;\n  cf_descr.type = CU_FILE_HANDLE_TYPE_OPAQUE_FD;\n  CUfileHandle_t cf_handle;\n  status = cuFileHandleRegister(&cf_handle, &cf_descr);\n  if (status.err != CU_FILE_SUCCESS) {\n    printf(\"Error: cuFileHandleRegister failed (%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Buffer registration is not mandatory but recommended for best performance.\n  // I/Os from/to unregistered buffers will go through GDS's internal registered\n  // buffer (extra copy).\n  // Let's ignore if it fails (e.g. not enough BAR memory on this GPU)\n  bool registered = true;\n  status = cuFileBufRegister(d_compressed, lcompbuf, 0);\n  if (status.err != CU_FILE_SUCCESS) {\n    printf(\"Warning: GDS buffer registration failed\\n\");\n    registered = false;\n  }\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Compression\");\n\n  // The compressed size must be device-accessible, using pinned memory.\n\n  // Compress the data (asynchronous)\n  compressor.compress(d_input, d_compressed, comp_config);\n  const size_t compressed_size = compressor.get_compressed_output_size(d_compressed);\n\n  // Align the compressed size to the next multiple of 4KB.\n  size_t aligned_compressed_size = ((compressed_size - 1) / 4096 + 1) * 4096;\n  printf(\n      \"Data compressed from %lu Bytes to %lu Bytes, aligned to %lu Bytes\\n\",\n      n,\n      compressed_size,\n      aligned_compressed_size);\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS Write\");\n\n  // Write the data (padded to next 4KB), directly from the device, with GDS\n  ssize_t nb;\n  if ((nb = cuFileWrite(cf_handle, d_compressed, aligned_compressed_size, 0, 0)) != aligned_compressed_size) {\n    printf(\"Error, write returned %ld instead of %lu \\n\", nb, aligned_compressed_size);\n    return -1;\n  } else\n    printf(\"Wrote %ld bytes to file %s using GDS\\n\", nb, filename);\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Cleaning up compressor\");\n\n  // Erase the content of the compressed buffer\n  CUDA_CHECK(cudaMemsetAsync(d_compressed, 0xff, compressed_size, stream));\n  CUDA_CHECK(cudaStreamSynchronize(stream));\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS Read\");\n\n  // Read the compressed data from the GDS file into the device buffer\n  if ((nb = cuFileRead(cf_handle, d_compressed, aligned_compressed_size, 0, 0)) != aligned_compressed_size) {\n    nvtxRangePop();\n    printf(\"Error, GDS read returned %ld instead of %lu \\n\", nb, aligned_compressed_size);\n    return -1;\n  } else {\n    nvtxRangePop();\n    printf(\"Read %ld bytes from file %s using GDS\\n\", nb, filename);\n  }\n\n  nvtxRangePushA(\"Decompressor setup\");\n\n  CUDA_CHECK(cudaStreamSynchronize(stream));\n  // Decompressor, configured with the compressed data\n  const DecompressionConfig decomp_config\n      = compressor.configure_decompression(comp_config);\n  size_t ldecomp = decomp_config.decomp_data_size;\n  if (ldecomp != n) {\n    printf(\"Error: Uncompressed size does not match the original size\\n\");\n    return -1;\n  }\n\n  // Device-accessible flag to compare the data\n  int* dh_invalid;\n  CUDA_CHECK(cudaMallocHost(&dh_invalid, sizeof(int)));\n  *dh_invalid = 0;\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Decompression and comparison\");\n  printf(\"Decompressing\\n\");\n\n  // Decompress the data (asynchronous)\n  compressor.decompress(d_output, d_compressed, decomp_config);\n  // Compare the uncompressed data with the original, in the same stream\n  compare<<<2 * smcount, 1024, 0, stream>>>(d_input, d_output, dh_invalid, n);\n\n  // Sync the stream before we check the result\n  CUDA_CHECK(cudaStreamSynchronize(stream));\n  if (*dh_invalid)\n    printf(\"FAILED: Uncompressed data does not match the original\\n\");\n  else\n    printf(\"PASSED: Uncompressed data is identical to the input\\n\");\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Final cleanup\");\n\n  // Cleanup\n  if (registered) {\n    status = cuFileBufDeregister(d_compressed);\n    if (status.err != CU_FILE_SUCCESS) {\n      printf(\"Error: cuFileBufDeregister failed(%d)\\n\", status.err);\n      return -1;\n    }\n  }\n  close(fd);\n  status = cuFileDriverClose();\n  if (status.err != CU_FILE_SUCCESS) {\n    printf(\"Error: cuFileDriverClose failed(%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Deallocate internal memory structures of LZ4Manager\n  compressor.deallocate_gpu_mem();\n\n  CUDA_CHECK(cudaFreeHost(dh_invalid));\n  CUDA_CHECK(cudaFree(d_input));\n  CUDA_CHECK(cudaFree(d_output));\n  CUDA_CHECK(cudaFree(d_compressed));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n\n  printf(\"All done, exiting...\\n\");\n  nvtxRangePop();\n\n  return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Simple example of how to use GDS with nvcomp.\n// GDS (GPU Direct Storage) allows to read and write from/to NVMe drives\n// directly from the GPU, bypassing the CPU.\n//\n// For best performance, the I/O buffer should be registered but it is not\n// mandatory. Registration can be expensive but done only once, and allows the\n// I/O to be performed directly from the registered buffer. Otherwise, GDS will\n// use it's own intermediate buffer, at the expense of extra memory copies.\n// Similarly, I/Os with a base address or size which is not aligned on 4KB will\n// go through GDS's internal buffer and will be less efficient.\n//\n// For more details on GDS, included the supported GPUs, please see the\n// documentation. https://docs.nvidia.com/gpudirect-storage/\n//\n// To compile this GDS example, GDS must be installed, and the following\n// option must be passed when configuring cmake:\n// cmake -DBUILD_GDS_EXAMPLE=on <...>\n\n#include \"hip/hip_runtime.h\"\n#include <fcntl.h>\n#include <iostream>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <hipfile.h>\n#include <nvtx3/nvToolsExt.h>\n\n#include \"nvcomp.hpp\"\n#include \"nvcomp/lz4.hpp\"\n\n#include \"util.h\"\n\nusing namespace nvcomp;\n\n// Kernel to initialize the input data with sequential bytes\n__global__ void initialize(uint8_t* data, size_t n)\n{\n  size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n    data[i] = i & 0xff;\n}\n\n// Kernel to compare 2 buffers. Invalid flag must be set to zero before.\n__global__ void\ncompare(const uint8_t* ref, const uint8_t* val, int* invalid, size_t n)\n{\n  size_t i = (size_t)blockIdx.x * blockDim.x + threadIdx.x;\n  int stride = gridDim.x * blockDim.x;\n  while (i < n) {\n    if (ref[i] != val[i])\n      *invalid = 1;\n    i += stride;\n  }\n}\n\nvoid usage(const char* str)\n{\n  printf(\"Argument: %s <filename>\\n\", str);\n  exit(-1);\n}\n\nint main(int argc, char** argv)\n{\n  if (argc != 2)\n    usage(argv[0]);\n\n  // Open the file. Note: GDS requires O_DIRECT.\n  const char* filename = argv[1];\n  int fd = open(filename, O_RDWR | O_TRUNC | O_CREAT | O_DIRECT, 0666);\n  if (fd == -1) {\n    printf(\"Error, cannot create the file: %s\\n\", filename);\n    return -1;\n  }\n  hipDeviceProp_t deviceProp;\n  CUDA_CHECK(hipGetDeviceProperties(&deviceProp, 0));\n  printf(\"Using device: %s\\n\", deviceProp.name);\n  int smcount = deviceProp.multiProcessorCount;\n\n  // Uncompressed data = 100 MB\n  const size_t n = 100000000;\n\n  // Device pointers for the data to be compressed / decompressed\n  uint8_t *d_input, *d_output;\n  hipStream_t stream;\n  CUDA_CHECK(cudaMallocSafe(&d_input, n));\n  CUDA_CHECK(cudaMallocSafe(&d_output, n));\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Initialize the input data (sequential bytes)\n  initialize<<<(n - 1) / 512 + 1, 512, 0, stream>>>(d_input, n);\n\n  // Using NVTX to highlight the different phases of the program in the Nsight\n  // Systems profiler\n  nvtxRangePushA(\"Compressor setup\");\n\n  // Create an LZ4 compressor, get the max output size, and temp storage size\n  constexpr size_t chunk_size = 1 << 16;\n  static_assert(chunk_size <= nvcompLZ4CompressionMaxAllowedChunkSize, \"Chunk size must be less than the constant specified in the nvCOMP library\");\n\n  LZ4Manager compressor(chunk_size, nvcompBatchedLZ4CompressDefaultOpts, nvcompBatchedLZ4DecompressDefaultOpts, stream);\n  const CompressionConfig comp_config = compressor.configure_compression(n);\n  size_t lcompbuf = comp_config.max_compressed_buffer_size;\n\n  // The compressed output buffer is padded to the next multiple of 4KB\n  // for best I/O performance. Unaligned I/Os go through an extra\n  // memory copy (GDS's internal aligned registered buffer)\n  lcompbuf = ((lcompbuf - 1) / 4096 + 1) * 4096;\n  uint8_t* d_compressed;\n  CUDA_CHECK(cudaMallocSafe(&d_compressed, lcompbuf));\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS setup\");\n\n  // Initialize the cufile driver\n  hipFileError_t status = hipFileDriverOpen();\n  if (status.err != hipFileSuccess) {\n    printf(\"Error: hipFileDriverOpen failed (%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Register the file with GDS\n  hipFileDescr_t cf_descr;\n  memset(&cf_descr, 0, sizeof(hipFileDescr_t));\n  cf_descr.handle.fd = fd;\n  cf_descr.type = hipFileHandleTypeOpaqueFD;\n  hipFileHandle_t cf_handle;\n  status = hipFileHandleRegister(&cf_handle, &cf_descr);\n  if (status.err != hipFileSuccess) {\n    printf(\"Error: hipFileHandleRegister failed (%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Buffer registration is not mandatory but recommended for best performance.\n  // I/Os from/to unregistered buffers will go through GDS's internal registered\n  // buffer (extra copy).\n  // Let's ignore if it fails (e.g. not enough BAR memory on this GPU)\n  bool registered = true;\n  status = hipFileBufRegister(d_compressed, lcompbuf, 0);\n  if (status.err != hipFileSuccess) {\n    printf(\"Warning: GDS buffer registration failed\\n\");\n    registered = false;\n  }\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Compression\");\n\n  // The compressed size must be device-accessible, using pinned memory.\n\n  // Compress the data (asynchronous)\n  compressor.compress(d_input, d_compressed, comp_config);\n  const size_t compressed_size = compressor.get_compressed_output_size(d_compressed);\n\n  // Align the compressed size to the next multiple of 4KB.\n  size_t aligned_compressed_size = ((compressed_size - 1) / 4096 + 1) * 4096;\n  printf(\n      \"Data compressed from %lu Bytes to %lu Bytes, aligned to %lu Bytes\\n\",\n      n,\n      compressed_size,\n      aligned_compressed_size);\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS Write\");\n\n  // Write the data (padded to next 4KB), directly from the device, with GDS\n  ssize_t nb;\n  if ((nb = hipFileWrite(cf_handle, d_compressed, aligned_compressed_size, 0, 0)) != aligned_compressed_size) {\n    printf(\"Error, write returned %ld instead of %lu \\n\", nb, aligned_compressed_size);\n    return -1;\n  } else\n    printf(\"Wrote %ld bytes to file %s using GDS\\n\", nb, filename);\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Cleaning up compressor\");\n\n  // Erase the content of the compressed buffer\n  CUDA_CHECK(hipMemsetAsync(d_compressed, 0xff, compressed_size, stream));\n  CUDA_CHECK(hipStreamSynchronize(stream));\n\n  nvtxRangePop();\n  nvtxRangePushA(\"GDS Read\");\n\n  // Read the compressed data from the GDS file into the device buffer\n  if ((nb = hipFileRead(cf_handle, d_compressed, aligned_compressed_size, 0, 0)) != aligned_compressed_size) {\n    nvtxRangePop();\n    printf(\"Error, GDS read returned %ld instead of %lu \\n\", nb, aligned_compressed_size);\n    return -1;\n  } else {\n    nvtxRangePop();\n    printf(\"Read %ld bytes from file %s using GDS\\n\", nb, filename);\n  }\n\n  nvtxRangePushA(\"Decompressor setup\");\n\n  CUDA_CHECK(hipStreamSynchronize(stream));\n  // Decompressor, configured with the compressed data\n  const DecompressionConfig decomp_config\n      = compressor.configure_decompression(comp_config);\n  size_t ldecomp = decomp_config.decomp_data_size;\n  if (ldecomp != n) {\n    printf(\"Error: Uncompressed size does not match the original size\\n\");\n    return -1;\n  }\n\n  // Device-accessible flag to compare the data\n  int* dh_invalid;\n  CUDA_CHECK(hipHostMalloc(&dh_invalid, sizeof(int)));\n  *dh_invalid = 0;\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Decompression and comparison\");\n  printf(\"Decompressing\\n\");\n\n  // Decompress the data (asynchronous)\n  compressor.decompress(d_output, d_compressed, decomp_config);\n  // Compare the uncompressed data with the original, in the same stream\n  compare<<<2 * smcount, 1024, 0, stream>>>(d_input, d_output, dh_invalid, n);\n\n  // Sync the stream before we check the result\n  CUDA_CHECK(hipStreamSynchronize(stream));\n  if (*dh_invalid)\n    printf(\"FAILED: Uncompressed data does not match the original\\n\");\n  else\n    printf(\"PASSED: Uncompressed data is identical to the input\\n\");\n\n  nvtxRangePop();\n  nvtxRangePushA(\"Final cleanup\");\n\n  // Cleanup\n  if (registered) {\n    status = hipFileBufDeregister(d_compressed);\n    if (status.err != hipFileSuccess) {\n      printf(\"Error: hipFileBufDeregister failed(%d)\\n\", status.err);\n      return -1;\n    }\n  }\n  close(fd);\n  status = hipFileDriverClose();\n  if (status.err != hipFileSuccess) {\n    printf(\"Error: hipFileDriverClose failed(%d)\\n\", status.err);\n    return -1;\n  }\n\n  // Deallocate internal memory structures of LZ4Manager\n  compressor.deallocate_gpu_mem();\n\n  CUDA_CHECK(hipHostFree(dh_invalid));\n  CUDA_CHECK(hipFree(d_input));\n  CUDA_CHECK(hipFree(d_output));\n  CUDA_CHECK(hipFree(d_compressed));\n  CUDA_CHECK(hipStreamDestroy(stream));\n\n  printf(\"All done, exiting...\\n\");\n  nvtxRangePop();\n\n  return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/nvCOMP/examples/nvcomp_gds.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iomanip>\n#include <cuhash.hpp>\n#include <stdio.h>\n\nusing namespace cupqc;\n\nusing SHA2_256_THREAD = decltype(SHA2_256() + Thread());\n\n__global__ void hash_sha2_kernel(uint8_t* digest, const uint8_t* msg, size_t inbuf_len)\n{\n    SHA2_256_THREAD hash {};\n    if (threadIdx.x == 0) {\n        hash.reset();\n        hash.update(msg, inbuf_len);\n        hash.finalize();\n        hash.digest(digest, SHA2_256_THREAD::digest_size);\n    }\n}\n\n\nvoid hash_sha2(std::vector<uint8_t>& digest, std::vector<uint8_t>& msg)\n{\n    uint8_t* d_msg;\n    uint8_t* d_digest;\n    cudaMalloc(reinterpret_cast<void**>(&d_msg), msg.size());\n    cudaMalloc(reinterpret_cast<void**>(&d_digest), digest.size());\n\n    cudaMemcpy(d_msg, msg.data(), msg.size(), cudaMemcpyHostToDevice);\n\n    hash_sha2_kernel<<<1, 32>>>(d_digest, d_msg, msg.size());\n\n    cudaMemcpy(digest.data(), d_digest, digest.size(), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_msg);\n    cudaFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    const char * msg_str = \"The quick brown fox jumps over the lazy dog\";\n    std::vector<uint8_t> msg(reinterpret_cast<const uint8_t*>(msg_str), reinterpret_cast<const uint8_t*>(msg_str) + strlen(msg_str));\n    std::vector<uint8_t> digest(SHA2_256::digest_size, 0);\n    hash_sha2(digest, msg);\n    printf(\"SHA2-256: \");\n    for (uint8_t num : digest) {\n        printf(\"%02x\", num);\n    }\n    printf(\"\\n\");\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <iomanip>\n#include <cuhash.hpp>\n#include <stdio.h>\n\nusing namespace cupqc;\n\nusing SHA2_256_THREAD = decltype(SHA2_256() + Thread());\n\n__global__ void hash_sha2_kernel(uint8_t* digest, const uint8_t* msg, size_t inbuf_len)\n{\n    SHA2_256_THREAD hash {};\n    if (threadIdx.x == 0) {\n        hash.reset();\n        hash.update(msg, inbuf_len);\n        hash.finalize();\n        hash.digest(digest, SHA2_256_THREAD::digest_size);\n    }\n}\n\n\nvoid hash_sha2(std::vector<uint8_t>& digest, std::vector<uint8_t>& msg)\n{\n    uint8_t* d_msg;\n    uint8_t* d_digest;\n    hipMalloc(reinterpret_cast<void**>(&d_msg), msg.size());\n    hipMalloc(reinterpret_cast<void**>(&d_digest), digest.size());\n\n    hipMemcpy(d_msg, msg.data(), msg.size(), hipMemcpyHostToDevice);\n\n    hash_sha2_kernel<<<1, 32>>>(d_digest, d_msg, msg.size());\n\n    hipMemcpy(digest.data(), d_digest, digest.size(), hipMemcpyDeviceToHost);\n\n    hipFree(d_msg);\n    hipFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    const char * msg_str = \"The quick brown fox jumps over the lazy dog\";\n    std::vector<uint8_t> msg(reinterpret_cast<const uint8_t*>(msg_str), reinterpret_cast<const uint8_t*>(msg_str) + strlen(msg_str));\n    std::vector<uint8_t> digest(SHA2_256::digest_size, 0);\n    hash_sha2(digest, msg);\n    printf(\"SHA2-256: \");\n    for (uint8_t num : digest) {\n        printf(\"%02x\", num);\n    }\n    printf(\"\\n\");\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_sha2.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <cupqc.hpp>\nusing namespace cupqc;\n\nusing MLDSA44Key = decltype(ML_DSA_44()\n                            + Function<function::Keygen>()\n                            + Block()\n                            + BlockDim<128>());  //Optional operator with default config\n\nusing MLDSA44Sign = decltype(ML_DSA_44()\n                             + Function<function::Sign>()\n                             + Block()\n                             + BlockDim<128>());  //Optional operator with default config\n\nusing MLDSA44Verify = decltype(ML_DSA_44()\n                               + Function<function::Verify>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\n__global__ void keygen_kernel(uint8_t* public_keys, uint8_t* secret_keys, uint8_t* randombytes, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Key::shared_memory_size];\n    int block = blockIdx.x;\n    auto public_key = public_keys + block * MLDSA44Key::public_key_size;\n    auto secret_key = secret_keys + block * MLDSA44Key::secret_key_size;\n    auto entropy    = randombytes + block * MLDSA44Key::entropy_size;\n    auto work       = workspace   + block * MLDSA44Key::workspace_size;\n\n    MLDSA44Key().execute(public_key, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void sign_kernel(uint8_t* signatures, const uint8_t* messages, const size_t message_size, const uint8_t* secret_keys, uint8_t *randombytes, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Sign::shared_memory_size];\n    int block = blockIdx.x;\n    auto signature  = signatures  + block * ((MLDSA44Sign::signature_size + 15) / 16 * 16);\n    auto message    = messages    + block * message_size;\n    auto secret_key = secret_keys + block * MLDSA44Sign::secret_key_size;\n    auto entropy    = randombytes + block * MLDSA44Sign::entropy_size;\n    auto work       = workspace   + block * MLDSA44Sign::workspace_size;\n\n    MLDSA44Sign().execute(signature, message, message_size, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void verify_kernel(bool* valids, const uint8_t* signatures, const uint8_t* messages, const size_t message_size, const uint8_t* public_keys, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Verify::shared_memory_size];\n    int block = blockIdx.x;\n    auto signature   = signatures + block * ((MLDSA44Sign::signature_size + 15) / 16 * 16);\n    auto message     = messages    + block * message_size;\n    auto public_key  = public_keys + block * MLDSA44Verify::public_key_size;\n    auto work        = workspace   + block * MLDSA44Verify::workspace_size;\n\n    valids[block] = MLDSA44Verify().execute(message, message_size, signature, public_key, work, smem_ptr);\n}\n\nvoid ml_dsa_keygen(std::vector<uint8_t>& public_keys, std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Keygen.\n     * Allocates device workspace for computation\n     */\n    auto length_public_key = MLDSA44Key::public_key_size;\n    auto length_secret_key = MLDSA44Key::secret_key_size;\n\n    auto workspace         = make_workspace<MLDSA44Key>(batch);\n    auto randombytes       = get_entropy<MLDSA44Key>(batch);\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_public_key = nullptr;\n    uint8_t* d_secret_key = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n\n    keygen_kernel<<<batch, MLDSA44Key::BlockDim>>>(d_public_key, d_secret_key, randombytes, workspace);\n\n    /*\n     * Transfer generated keys to the host for communication or storage\n     */\n    cudaMemcpy(public_keys.data(), d_public_key, length_public_key * batch, cudaMemcpyDeviceToHost);\n    cudaMemcpy(secret_keys.data(), d_secret_key, length_secret_key * batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_public_key);\n    cudaFree(d_secret_key);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_dsa_sign(std::vector<uint8_t>& signatures, std::vector<uint8_t>& messages,  size_t message_size,\n                      const std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Sign.\n     * Allocates device workspace for computing\n     */\n    auto length_secret_key = MLDSA44Sign::secret_key_size;\n    auto length_signature  = MLDSA44Sign::signature_size;\n    auto length_signature_aligned = ((length_signature + 15) / 16) * 16;\n\n    auto workspace         = make_workspace<MLDSA44Sign>(batch);\n    auto randombytes       = get_entropy<MLDSA44Sign>(batch);\n\n    /*\n     * Allocate device memory for messages, secret_keys, messages\n     */\n    uint8_t* d_signatures  = nullptr;\n    uint8_t* d_secret_keys = nullptr;\n    uint8_t* d_messages    = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_signatures), length_signature_aligned * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_secret_keys), length_secret_key * batch);\n    cudaMalloc(reinterpret_cast<void**>(&d_messages), message_size * batch);\n\n    /*\n     * Transfer messages and secret_keys from host memory\n     */\n    cudaMemcpy(d_secret_keys, secret_keys.data(), length_secret_key * batch, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_messages, messages.data(), message_size * batch, cudaMemcpyHostToDevice);\n\n    sign_kernel<<<batch, MLDSA44Sign::BlockDim>>>(d_signatures, d_messages, message_size, d_secret_keys, randombytes, workspace);\n\n    /*\n     * Transfer signatures from device memory\n     */\n    cudaMemcpy(signatures.data(), d_signatures, length_signature_aligned * batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_secret_keys);\n    cudaFree(d_signatures);\n    cudaFree(d_messages);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_dsa_verify(bool* is_valids, const std::vector<uint8_t>& signatures, const std::vector<uint8_t>& messages, size_t message_size,\n                          const std::vector<uint8_t>& public_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Verify.\n     * Allocates device workspace for computing\n     */\n    auto workspace         = make_workspace<MLDSA44Verify>(batch);\n    auto length_signature  = MLDSA44Verify::signature_size;\n    auto length_signature_aligned = ((length_signature + 15) / 16) * 16;\n    auto length_public_key = MLDSA44Verify::public_key_size;\n\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_signatures  = nullptr;\n    uint8_t* d_messages    = nullptr;\n    uint8_t* d_public_keys = nullptr;\n    bool*    d_valids      = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_signatures), length_signature_aligned * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_public_keys), length_public_key * batch);\n    cudaMalloc(reinterpret_cast<void**>(&d_messages), message_size * batch);\n    cudaMalloc(reinterpret_cast<void**>(&d_valids), batch);\n\n    /*\n     * Transfer signatures, messages, and public_keys from host.\n     */\n    cudaMemcpy(d_public_keys, public_keys.data(), length_public_key * batch, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_signatures, signatures.data(), length_signature_aligned * batch, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_messages, messages.data(), message_size * batch, cudaMemcpyHostToDevice);\n\n    verify_kernel<<<batch, MLDSA44Verify::BlockDim>>>(d_valids, d_signatures, d_messages, message_size, d_public_keys, workspace);\n\n    /*\n     * Transfer valid messages from device to host mem\n     */\n    cudaMemcpy(is_valids, d_valids, batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_public_keys);\n    cudaFree(d_signatures);\n    cudaFree(d_messages);\n    cudaFree(d_valids);\n    destroy_workspace(workspace);\n}\n\n\n/*\n * Normally different actors will be performing the various functions associated with ML-DSA.\n * In this example, we perform all functions, Keygen, Sign, and Verify. \n * You could optimize this example by reusing the device memory, and not transfering to host,\n * however, this is not the normal scenario. So, to better illustrate the use of the cuPQCDx API we wrote\n * host functions ml_dsa_keygen, ml_dsa_sign, ml_dsa_verify that don't assume they will be called in\n * conjunction with the others. \n * \n * In this example we produce 10 keys, signatures and verified messages, using batching.\n * \n * In a typical scenerio:\n * Actor 1 (keygen + Sign)\n * Actor 2 (Verify)\n * \n * or \n * \n * Actor 1 as a keygeneration service (keygen)\n * Actor 2 Sign using key from 1.\n * Actor 3 Verify using key from 1 and signature from 2.\n */\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    constexpr unsigned int batch = 10;\n    constexpr size_t message_size = 1024;\n\n    /*\n     * Generate Public and Secret Keys!\n     */\n    std::vector<uint8_t> public_keys(MLDSA44Key::public_key_size * batch);\n    std::vector<uint8_t> secret_keys(MLDSA44Key::secret_key_size * batch);\n\n    ml_dsa_keygen(public_keys, secret_keys, batch);\n\n    /*\n     * Create signature from message and secret key!\n     */\n    std::vector<uint8_t> signatures(((MLDSA44Sign::signature_size + 15) / 16 * 16) * batch);\n    std::vector<uint8_t> messages(message_size * batch);\n    ml_dsa_sign(signatures, messages, message_size, secret_keys, batch);\n\n    bool is_valids[batch];\n    /*\n     * Verify that the signature is valid using public key!\n     */\n    ml_dsa_verify(is_valids, signatures, messages, message_size, public_keys, batch);\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <cupqc.hpp>\nusing namespace cupqc;\n\nusing MLDSA44Key = decltype(ML_DSA_44()\n                            + Function<function::Keygen>()\n                            + Block()\n                            + BlockDim<128>());  //Optional operator with default config\n\nusing MLDSA44Sign = decltype(ML_DSA_44()\n                             + Function<function::Sign>()\n                             + Block()\n                             + BlockDim<128>());  //Optional operator with default config\n\nusing MLDSA44Verify = decltype(ML_DSA_44()\n                               + Function<function::Verify>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\n__global__ void keygen_kernel(uint8_t* public_keys, uint8_t* secret_keys, uint8_t* randombytes, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Key::shared_memory_size];\n    int block = blockIdx.x;\n    auto public_key = public_keys + block * MLDSA44Key::public_key_size;\n    auto secret_key = secret_keys + block * MLDSA44Key::secret_key_size;\n    auto entropy    = randombytes + block * MLDSA44Key::entropy_size;\n    auto work       = workspace   + block * MLDSA44Key::workspace_size;\n\n    MLDSA44Key().execute(public_key, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void sign_kernel(uint8_t* signatures, const uint8_t* messages, const size_t message_size, const uint8_t* secret_keys, uint8_t *randombytes, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Sign::shared_memory_size];\n    int block = blockIdx.x;\n    auto signature  = signatures  + block * ((MLDSA44Sign::signature_size + 15) / 16 * 16);\n    auto message    = messages    + block * message_size;\n    auto secret_key = secret_keys + block * MLDSA44Sign::secret_key_size;\n    auto entropy    = randombytes + block * MLDSA44Sign::entropy_size;\n    auto work       = workspace   + block * MLDSA44Sign::workspace_size;\n\n    MLDSA44Sign().execute(signature, message, message_size, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void verify_kernel(bool* valids, const uint8_t* signatures, const uint8_t* messages, const size_t message_size, const uint8_t* public_keys, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLDSA44Verify::shared_memory_size];\n    int block = blockIdx.x;\n    auto signature   = signatures + block * ((MLDSA44Sign::signature_size + 15) / 16 * 16);\n    auto message     = messages    + block * message_size;\n    auto public_key  = public_keys + block * MLDSA44Verify::public_key_size;\n    auto work        = workspace   + block * MLDSA44Verify::workspace_size;\n\n    valids[block] = MLDSA44Verify().execute(message, message_size, signature, public_key, work, smem_ptr);\n}\n\nvoid ml_dsa_keygen(std::vector<uint8_t>& public_keys, std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Keygen.\n     * Allocates device workspace for computation\n     */\n    auto length_public_key = MLDSA44Key::public_key_size;\n    auto length_secret_key = MLDSA44Key::secret_key_size;\n\n    auto workspace         = make_workspace<MLDSA44Key>(batch);\n    auto randombytes       = get_entropy<MLDSA44Key>(batch);\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_public_key = nullptr;\n    uint8_t* d_secret_key = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n\n    keygen_kernel<<<batch, MLDSA44Key::BlockDim>>>(d_public_key, d_secret_key, randombytes, workspace);\n\n    /*\n     * Transfer generated keys to the host for communication or storage\n     */\n    hipMemcpy(public_keys.data(), d_public_key, length_public_key * batch, hipMemcpyDeviceToHost);\n    hipMemcpy(secret_keys.data(), d_secret_key, length_secret_key * batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_public_key);\n    hipFree(d_secret_key);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_dsa_sign(std::vector<uint8_t>& signatures, std::vector<uint8_t>& messages,  size_t message_size,\n                      const std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Sign.\n     * Allocates device workspace for computing\n     */\n    auto length_secret_key = MLDSA44Sign::secret_key_size;\n    auto length_signature  = MLDSA44Sign::signature_size;\n    auto length_signature_aligned = ((length_signature + 15) / 16) * 16;\n\n    auto workspace         = make_workspace<MLDSA44Sign>(batch);\n    auto randombytes       = get_entropy<MLDSA44Sign>(batch);\n\n    /*\n     * Allocate device memory for messages, secret_keys, messages\n     */\n    uint8_t* d_signatures  = nullptr;\n    uint8_t* d_secret_keys = nullptr;\n    uint8_t* d_messages    = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_signatures), length_signature_aligned * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_secret_keys), length_secret_key * batch);\n    hipMalloc(reinterpret_cast<void**>(&d_messages), message_size * batch);\n\n    /*\n     * Transfer messages and secret_keys from host memory\n     */\n    hipMemcpy(d_secret_keys, secret_keys.data(), length_secret_key * batch, hipMemcpyHostToDevice);\n    hipMemcpy(d_messages, messages.data(), message_size * batch, hipMemcpyHostToDevice);\n\n    sign_kernel<<<batch, MLDSA44Sign::BlockDim>>>(d_signatures, d_messages, message_size, d_secret_keys, randombytes, workspace);\n\n    /*\n     * Transfer signatures from device memory\n     */\n    hipMemcpy(signatures.data(), d_signatures, length_signature_aligned * batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_secret_keys);\n    hipFree(d_signatures);\n    hipFree(d_messages);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_dsa_verify(bool* is_valids, const std::vector<uint8_t>& signatures, const std::vector<uint8_t>& messages, size_t message_size,\n                          const std::vector<uint8_t>& public_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-DSA Verify.\n     * Allocates device workspace for computing\n     */\n    auto workspace         = make_workspace<MLDSA44Verify>(batch);\n    auto length_signature  = MLDSA44Verify::signature_size;\n    auto length_signature_aligned = ((length_signature + 15) / 16) * 16;\n    auto length_public_key = MLDSA44Verify::public_key_size;\n\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_signatures  = nullptr;\n    uint8_t* d_messages    = nullptr;\n    uint8_t* d_public_keys = nullptr;\n    bool*    d_valids      = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_signatures), length_signature_aligned * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_public_keys), length_public_key * batch);\n    hipMalloc(reinterpret_cast<void**>(&d_messages), message_size * batch);\n    hipMalloc(reinterpret_cast<void**>(&d_valids), batch);\n\n    /*\n     * Transfer signatures, messages, and public_keys from host.\n     */\n    hipMemcpy(d_public_keys, public_keys.data(), length_public_key * batch, hipMemcpyHostToDevice);\n    hipMemcpy(d_signatures, signatures.data(), length_signature_aligned * batch, hipMemcpyHostToDevice);\n    hipMemcpy(d_messages, messages.data(), message_size * batch, hipMemcpyHostToDevice);\n\n    verify_kernel<<<batch, MLDSA44Verify::BlockDim>>>(d_valids, d_signatures, d_messages, message_size, d_public_keys, workspace);\n\n    /*\n     * Transfer valid messages from device to host mem\n     */\n    hipMemcpy(is_valids, d_valids, batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_public_keys);\n    hipFree(d_signatures);\n    hipFree(d_messages);\n    hipFree(d_valids);\n    destroy_workspace(workspace);\n}\n\n\n/*\n * Normally different actors will be performing the various functions associated with ML-DSA.\n * In this example, we perform all functions, Keygen, Sign, and Verify. \n * You could optimize this example by reusing the device memory, and not transfering to host,\n * however, this is not the normal scenario. So, to better illustrate the use of the cuPQCDx API we wrote\n * host functions ml_dsa_keygen, ml_dsa_sign, ml_dsa_verify that don't assume they will be called in\n * conjunction with the others. \n * \n * In this example we produce 10 keys, signatures and verified messages, using batching.\n * \n * In a typical scenerio:\n * Actor 1 (keygen + Sign)\n * Actor 2 (Verify)\n * \n * or \n * \n * Actor 1 as a keygeneration service (keygen)\n * Actor 2 Sign using key from 1.\n * Actor 3 Verify using key from 1 and signature from 2.\n */\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    constexpr unsigned int batch = 10;\n    constexpr size_t message_size = 1024;\n\n    /*\n     * Generate Public and Secret Keys!\n     */\n    std::vector<uint8_t> public_keys(MLDSA44Key::public_key_size * batch);\n    std::vector<uint8_t> secret_keys(MLDSA44Key::secret_key_size * batch);\n\n    ml_dsa_keygen(public_keys, secret_keys, batch);\n\n    /*\n     * Create signature from message and secret key!\n     */\n    std::vector<uint8_t> signatures(((MLDSA44Sign::signature_size + 15) / 16 * 16) * batch);\n    std::vector<uint8_t> messages(message_size * batch);\n    ml_dsa_sign(signatures, messages, message_size, secret_keys, batch);\n\n    bool is_valids[batch];\n    /*\n     * Verify that the signature is valid using public key!\n     */\n    ml_dsa_verify(is_valids, signatures, messages, message_size, public_keys, batch);\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_ml_dsa.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <cupqc.hpp>\nusing namespace cupqc;\n\nusing MLKEM512Key = decltype(ML_KEM_512()\n                            + Function<function::Keygen>()\n                            + Block()\n                            + BlockDim<128>());  //Optional operator with default config\n\nusing MLKEM512Encaps = decltype(ML_KEM_512()\n                               + Function<function::Encaps>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\nusing MLKEM512Decaps = decltype(ML_KEM_512()\n                               + Function<function::Decaps>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\n__global__ void keygen_kernel(uint8_t* public_keys, uint8_t* secret_keys, uint8_t* workspace, uint8_t* randombytes)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Key::shared_memory_size];\n    int block = blockIdx.x;\n    auto public_key = public_keys + block * MLKEM512Key::public_key_size;\n    auto secret_key = secret_keys + block * MLKEM512Key::secret_key_size;\n    auto entropy    = randombytes + block * MLKEM512Key::entropy_size;\n    auto work       = workspace   + block * MLKEM512Key::workspace_size;\n\n    MLKEM512Key().execute(public_key, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void encaps_kernel(uint8_t* ciphertexts, uint8_t* shared_secrets, const uint8_t* public_keys, uint8_t* workspace, uint8_t *randombytes)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Encaps::shared_memory_size];\n    int block = blockIdx.x;\n    auto shared_secret = shared_secrets + block * MLKEM512Encaps::shared_secret_size;\n    auto ciphertext    = ciphertexts + block * MLKEM512Encaps::ciphertext_size;\n    auto public_key    = public_keys + block * MLKEM512Encaps::public_key_size;\n    auto entropy    = randombytes + block * MLKEM512Encaps::entropy_size;\n    auto work       = workspace   + block * MLKEM512Encaps::workspace_size;\n\n    MLKEM512Encaps().execute(ciphertext, shared_secret, public_key, entropy, work, smem_ptr);\n}\n\n__global__ void decaps_kernel(uint8_t* shared_secrets, const uint8_t* ciphertexts,  const uint8_t* secret_keys, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Decaps::shared_memory_size];\n    int block = blockIdx.x;\n    auto shared_secret = shared_secrets + block * MLKEM512Decaps::shared_secret_size;\n    auto ciphertext    = ciphertexts + block * MLKEM512Decaps::ciphertext_size;\n    auto secret_key    = secret_keys + block * MLKEM512Decaps::secret_key_size;\n    auto work          = workspace   + block * MLKEM512Decaps::workspace_size;\n\n    MLKEM512Decaps().execute(shared_secret, ciphertext, secret_key, work, smem_ptr);\n}\n\nvoid ml_kem_keygen(std::vector<uint8_t>& public_keys, std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Keygen.\n     * Allocates device workspace for computing\n     */\n    auto length_public_key = MLKEM512Key::public_key_size;\n    auto length_secret_key = MLKEM512Key::secret_key_size;\n\n    auto workspace         = make_workspace<MLKEM512Key>(batch);\n    auto randombytes       = get_entropy<MLKEM512Key>(batch);\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_public_key = nullptr;\n    uint8_t* d_secret_key = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n\n    keygen_kernel<<<batch, MLKEM512Key::BlockDim>>>(d_public_key, d_secret_key, workspace, randombytes);\n\n    /*\n     * Transfer generated keys to the host for communication or storage\n     */\n    cudaMemcpy(public_keys.data(), d_public_key, length_public_key * batch, cudaMemcpyDeviceToHost);\n    cudaMemcpy(secret_keys.data(), d_secret_key, length_secret_key * batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_public_key);\n    cudaFree(d_secret_key);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_kem_encaps(std::vector<uint8_t> &ciphertexts, std::vector<uint8_t> &sharedsecrets,\n                      const std::vector<uint8_t> &public_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Encaps.\n     * Allocates device workspace for computing\n     */\n    auto length_ciphertext   = MLKEM512Encaps::ciphertext_size;\n    auto length_public_key   = MLKEM512Encaps::public_key_size;\n    auto length_sharedsecret = MLKEM512Encaps::shared_secret_size;\n\n    auto workspace         = make_workspace<MLKEM512Encaps>(batch);\n    auto randombytes       = get_entropy<MLKEM512Encaps>(batch);\n\n    /*\n     * Allocate device memory for public keys, ciphertexts and shared secrets\n     */\n    uint8_t* d_ciphertext   = nullptr;\n    uint8_t* d_public_key   = nullptr;\n    uint8_t* d_sharedsecret = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_ciphertext), length_ciphertext * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch);\n    cudaMalloc(reinterpret_cast<void**>(&d_sharedsecret), length_sharedsecret * batch);\n\n    /*\n     * Transfer public_key from host memory\n     */\n    cudaMemcpy(d_public_key, public_keys.data(), length_public_key * batch, cudaMemcpyHostToDevice);\n\n    encaps_kernel<<<batch, MLKEM512Encaps::BlockDim>>>(d_ciphertext, d_sharedsecret, d_public_key, workspace, randombytes);\n\n    /*\n     * Transfer ciphertext and shared secret from device memory\n     */\n    cudaMemcpy(ciphertexts.data(), d_ciphertext, length_ciphertext * batch, cudaMemcpyDeviceToHost);\n    cudaMemcpy(sharedsecrets.data(), d_sharedsecret, length_sharedsecret * batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_public_key);\n    cudaFree(d_ciphertext);\n    cudaFree(d_sharedsecret);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_kem_decaps(std::vector<uint8_t> &sharedsecrets, const std::vector<uint8_t> &ciphertexts, \n                      const std::vector<uint8_t> &secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Decaps.\n     * Allocates device workspace for computing\n     */\n    auto length_ciphertext   = MLKEM512Encaps::ciphertext_size;\n    auto length_secret_key   = MLKEM512Encaps::secret_key_size;\n    auto length_sharedsecret = MLKEM512Encaps::shared_secret_size;\n\n    auto workspace         = make_workspace<MLKEM512Decaps>(batch);\n\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_sharedsecret = nullptr;\n    uint8_t* d_ciphertext   = nullptr;\n    uint8_t* d_secret_key   = nullptr;\n\n    cudaMalloc(reinterpret_cast<void**>(&d_ciphertext), length_ciphertext * batch); //These are uint8_t so length and batch are in bytes\n    cudaMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n    cudaMalloc(reinterpret_cast<void**>(&d_sharedsecret), length_sharedsecret * batch);\n\n    /*\n     * Transfer secret_key and ciphertext from host memory\n     */\n    cudaMemcpy(d_secret_key, secret_keys.data(), length_secret_key * batch, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_ciphertext, ciphertexts.data(), length_ciphertext * batch, cudaMemcpyHostToDevice);\n\n    decaps_kernel<<<batch, MLKEM512Decaps::BlockDim>>>(d_sharedsecret, d_ciphertext, d_secret_key, workspace);\n\n    /*\n     * Transfer sharedsecret from device to host mem\n     */\n    cudaMemcpy(sharedsecrets.data(), d_sharedsecret, length_sharedsecret * batch, cudaMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    cudaFree(d_ciphertext);\n    cudaFree(d_sharedsecret);\n    cudaFree(d_secret_key);\n    destroy_workspace(workspace);\n}\n\n\n/*\n * Normally different actors will be performing the various functions associated with ML-KEM.\n * In this example, we perform all functions, Keygen, Encaps, and Decaps. \n * You could optimize this example by reusing the device memory, and not transfering to host,\n * however, this is not the normal scenario. So, to better illustrate the use of the cuPQCDx API we wrote\n * host functions ml_kem_kygen, ml_kem_encaps, ml_kem_decaps that don't assume they will be called in \n * conjunction with the others. \n * \n * In this example we produce 10 keys, ciphertexts and shared secrets, using batching.\n * \n * In a typical scenerio:\n * Actor 1 (keygen + decaps)\n * Actor 2 (decaps)\n * \n * or \n * \n * Actor 1 as a keygeneration service (keygen)\n * Actor 2 encaps using key from 1.\n * Actor 3 decaps using key from 1 and ciphertext from 2.\n */\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    unsigned int batch = 10;\n\n    /*\n     * Generate Public and Secret Keys!\n     */\n    std::vector<uint8_t> public_keys(MLKEM512Key::public_key_size * batch);\n    std::vector<uint8_t> secret_keys(MLKEM512Key::secret_key_size * batch);\n\n    ml_kem_keygen(public_keys, secret_keys, batch);\n\n    /*\n     * Create shared secret and encapsulate it into ciphertext!\n     */\n    std::vector<uint8_t> ciphertexts(MLKEM512Encaps::ciphertext_size * batch);\n    std::vector<uint8_t> sharedsecrets(MLKEM512Encaps::shared_secret_size * batch);\n    ml_kem_encaps(ciphertexts, sharedsecrets, public_keys, batch);\n\n    /*\n     * Decapsulate ciphertext and store into shared secret!\n     */\n    ml_kem_decaps(sharedsecrets, ciphertexts, secret_keys, batch);\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <cupqc.hpp>\nusing namespace cupqc;\n\nusing MLKEM512Key = decltype(ML_KEM_512()\n                            + Function<function::Keygen>()\n                            + Block()\n                            + BlockDim<128>());  //Optional operator with default config\n\nusing MLKEM512Encaps = decltype(ML_KEM_512()\n                               + Function<function::Encaps>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\nusing MLKEM512Decaps = decltype(ML_KEM_512()\n                               + Function<function::Decaps>()\n                               + Block()\n                               + BlockDim<128>());  //Optional operator with default config\n\n__global__ void keygen_kernel(uint8_t* public_keys, uint8_t* secret_keys, uint8_t* workspace, uint8_t* randombytes)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Key::shared_memory_size];\n    int block = blockIdx.x;\n    auto public_key = public_keys + block * MLKEM512Key::public_key_size;\n    auto secret_key = secret_keys + block * MLKEM512Key::secret_key_size;\n    auto entropy    = randombytes + block * MLKEM512Key::entropy_size;\n    auto work       = workspace   + block * MLKEM512Key::workspace_size;\n\n    MLKEM512Key().execute(public_key, secret_key, entropy, work, smem_ptr);\n}\n\n__global__ void encaps_kernel(uint8_t* ciphertexts, uint8_t* shared_secrets, const uint8_t* public_keys, uint8_t* workspace, uint8_t *randombytes)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Encaps::shared_memory_size];\n    int block = blockIdx.x;\n    auto shared_secret = shared_secrets + block * MLKEM512Encaps::shared_secret_size;\n    auto ciphertext    = ciphertexts + block * MLKEM512Encaps::ciphertext_size;\n    auto public_key    = public_keys + block * MLKEM512Encaps::public_key_size;\n    auto entropy    = randombytes + block * MLKEM512Encaps::entropy_size;\n    auto work       = workspace   + block * MLKEM512Encaps::workspace_size;\n\n    MLKEM512Encaps().execute(ciphertext, shared_secret, public_key, entropy, work, smem_ptr);\n}\n\n__global__ void decaps_kernel(uint8_t* shared_secrets, const uint8_t* ciphertexts,  const uint8_t* secret_keys, uint8_t* workspace)\n{\n    __shared__ uint8_t smem_ptr[MLKEM512Decaps::shared_memory_size];\n    int block = blockIdx.x;\n    auto shared_secret = shared_secrets + block * MLKEM512Decaps::shared_secret_size;\n    auto ciphertext    = ciphertexts + block * MLKEM512Decaps::ciphertext_size;\n    auto secret_key    = secret_keys + block * MLKEM512Decaps::secret_key_size;\n    auto work          = workspace   + block * MLKEM512Decaps::workspace_size;\n\n    MLKEM512Decaps().execute(shared_secret, ciphertext, secret_key, work, smem_ptr);\n}\n\nvoid ml_kem_keygen(std::vector<uint8_t>& public_keys, std::vector<uint8_t>& secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Keygen.\n     * Allocates device workspace for computing\n     */\n    auto length_public_key = MLKEM512Key::public_key_size;\n    auto length_secret_key = MLKEM512Key::secret_key_size;\n\n    auto workspace         = make_workspace<MLKEM512Key>(batch);\n    auto randombytes       = get_entropy<MLKEM512Key>(batch);\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_public_key = nullptr;\n    uint8_t* d_secret_key = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n\n    keygen_kernel<<<batch, MLKEM512Key::BlockDim>>>(d_public_key, d_secret_key, workspace, randombytes);\n\n    /*\n     * Transfer generated keys to the host for communication or storage\n     */\n    hipMemcpy(public_keys.data(), d_public_key, length_public_key * batch, hipMemcpyDeviceToHost);\n    hipMemcpy(secret_keys.data(), d_secret_key, length_secret_key * batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_public_key);\n    hipFree(d_secret_key);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_kem_encaps(std::vector<uint8_t> &ciphertexts, std::vector<uint8_t> &sharedsecrets,\n                      const std::vector<uint8_t> &public_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Encaps.\n     * Allocates device workspace for computing\n     */\n    auto length_ciphertext   = MLKEM512Encaps::ciphertext_size;\n    auto length_public_key   = MLKEM512Encaps::public_key_size;\n    auto length_sharedsecret = MLKEM512Encaps::shared_secret_size;\n\n    auto workspace         = make_workspace<MLKEM512Encaps>(batch);\n    auto randombytes       = get_entropy<MLKEM512Encaps>(batch);\n\n    /*\n     * Allocate device memory for public keys, ciphertexts and shared secrets\n     */\n    uint8_t* d_ciphertext   = nullptr;\n    uint8_t* d_public_key   = nullptr;\n    uint8_t* d_sharedsecret = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_ciphertext), length_ciphertext * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_public_key), length_public_key * batch);\n    hipMalloc(reinterpret_cast<void**>(&d_sharedsecret), length_sharedsecret * batch);\n\n    /*\n     * Transfer public_key from host memory\n     */\n    hipMemcpy(d_public_key, public_keys.data(), length_public_key * batch, hipMemcpyHostToDevice);\n\n    encaps_kernel<<<batch, MLKEM512Encaps::BlockDim>>>(d_ciphertext, d_sharedsecret, d_public_key, workspace, randombytes);\n\n    /*\n     * Transfer ciphertext and shared secret from device memory\n     */\n    hipMemcpy(ciphertexts.data(), d_ciphertext, length_ciphertext * batch, hipMemcpyDeviceToHost);\n    hipMemcpy(sharedsecrets.data(), d_sharedsecret, length_sharedsecret * batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_public_key);\n    hipFree(d_ciphertext);\n    hipFree(d_sharedsecret);\n    destroy_workspace(workspace);\n    release_entropy(randombytes);\n}\n\nvoid ml_kem_decaps(std::vector<uint8_t> &sharedsecrets, const std::vector<uint8_t> &ciphertexts, \n                      const std::vector<uint8_t> &secret_keys, const unsigned int batch)\n{\n    /*\n     * Set up for utilizing cuPQCDx ML-KEM Decaps.\n     * Allocates device workspace for computing\n     */\n    auto length_ciphertext   = MLKEM512Encaps::ciphertext_size;\n    auto length_secret_key   = MLKEM512Encaps::secret_key_size;\n    auto length_sharedsecret = MLKEM512Encaps::shared_secret_size;\n\n    auto workspace         = make_workspace<MLKEM512Decaps>(batch);\n\n    /*\n     * Allocate device memory for public and secret keys\n     */\n    uint8_t* d_sharedsecret = nullptr;\n    uint8_t* d_ciphertext   = nullptr;\n    uint8_t* d_secret_key   = nullptr;\n\n    hipMalloc(reinterpret_cast<void**>(&d_ciphertext), length_ciphertext * batch); //These are uint8_t so length and batch are in bytes\n    hipMalloc(reinterpret_cast<void**>(&d_secret_key), length_secret_key * batch);\n    hipMalloc(reinterpret_cast<void**>(&d_sharedsecret), length_sharedsecret * batch);\n\n    /*\n     * Transfer secret_key and ciphertext from host memory\n     */\n    hipMemcpy(d_secret_key, secret_keys.data(), length_secret_key * batch, hipMemcpyHostToDevice);\n    hipMemcpy(d_ciphertext, ciphertexts.data(), length_ciphertext * batch, hipMemcpyHostToDevice);\n\n    decaps_kernel<<<batch, MLKEM512Decaps::BlockDim>>>(d_sharedsecret, d_ciphertext, d_secret_key, workspace);\n\n    /*\n     * Transfer sharedsecret from device to host mem\n     */\n    hipMemcpy(sharedsecrets.data(), d_sharedsecret, length_sharedsecret * batch, hipMemcpyDeviceToHost);\n\n    /*\n     * Delete device memory associated with the cryptograpic process.\n     */\n    hipFree(d_ciphertext);\n    hipFree(d_sharedsecret);\n    hipFree(d_secret_key);\n    destroy_workspace(workspace);\n}\n\n\n/*\n * Normally different actors will be performing the various functions associated with ML-KEM.\n * In this example, we perform all functions, Keygen, Encaps, and Decaps. \n * You could optimize this example by reusing the device memory, and not transfering to host,\n * however, this is not the normal scenario. So, to better illustrate the use of the cuPQCDx API we wrote\n * host functions ml_kem_kygen, ml_kem_encaps, ml_kem_decaps that don't assume they will be called in \n * conjunction with the others. \n * \n * In this example we produce 10 keys, ciphertexts and shared secrets, using batching.\n * \n * In a typical scenerio:\n * Actor 1 (keygen + decaps)\n * Actor 2 (decaps)\n * \n * or \n * \n * Actor 1 as a keygeneration service (keygen)\n * Actor 2 encaps using key from 1.\n * Actor 3 decaps using key from 1 and ciphertext from 2.\n */\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    unsigned int batch = 10;\n\n    /*\n     * Generate Public and Secret Keys!\n     */\n    std::vector<uint8_t> public_keys(MLKEM512Key::public_key_size * batch);\n    std::vector<uint8_t> secret_keys(MLKEM512Key::secret_key_size * batch);\n\n    ml_kem_keygen(public_keys, secret_keys, batch);\n\n    /*\n     * Create shared secret and encapsulate it into ciphertext!\n     */\n    std::vector<uint8_t> ciphertexts(MLKEM512Encaps::ciphertext_size * batch);\n    std::vector<uint8_t> sharedsecrets(MLKEM512Encaps::shared_secret_size * batch);\n    ml_kem_encaps(ciphertexts, sharedsecrets, public_keys, batch);\n\n    /*\n     * Decapsulate ciphertext and store into shared secret!\n     */\n    ml_kem_decaps(sharedsecrets, ciphertexts, secret_keys, batch);\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_ml_kem.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iomanip>\n#include <stdio.h>\n#include <iostream>\n\n#include <cuhash.hpp>\n\nusing namespace cupqc;\n\nusing HASH   = decltype(POSEIDON2_8_16() + Thread());\n\n//Will use MERKLE_FIELD_2048 in this example, we require FIELD when using the Poseidon2 hash function, and we use 256 threads per block\nusing MERKLE = decltype(MERKLE_FIELD_2048() + BlockDim<256>());\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void generate_merkle_tree_kernel(tree<Merkle::MerkleSize, Hash, Precision> merkle, const Precision* msg, size_t inbuf_len)\n{\n    Hash hash{};\n    for(uint32_t i = threadIdx.x; i < Merkle::MerkleSize; i += blockDim.x) {\n        Merkle().create_leaf(merkle.nodes + i * merkle.digest_size, msg + i * inbuf_len, hash, inbuf_len);\n    }\n\n    Merkle().generate_tree(merkle.nodes, hash, merkle);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void single_proof_kernel(proof<Merkle::MerkleSize, Hash, Precision> this_proof, const Precision* leaf_to_prove, const uint32_t leaf_index, const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    Merkle().generate_proof(this_proof, leaf_to_prove, leaf_index, merkle);\n}\n\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void generate_proof_kernel(proof<Merkle::MerkleSize, Hash, Precision>* proofs, const Precision* leaves_to_prove, const uint32_t* leaf_indices, const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    constexpr size_t digest_size = proof<Merkle::MerkleSize, Hash, Precision>::digest_size;\n    uint32_t thread_index = blockIdx.x * blockDim.x + threadIdx.x;\n    const Precision* leaf_to_prove = leaves_to_prove + thread_index * digest_size;\n    const uint32_t leaf_index = leaf_indices[thread_index];\n    Merkle().generate_proof(proofs[thread_index], leaf_to_prove, leaf_index, merkle); // Each thread generates a proof for the given leaf and corresponding index\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void single_verify_kernel(const proof<Merkle::MerkleSize, Hash, Precision> this_proof, const Precision* verify_leaf, const uint32_t verify_index, const Precision* root, bool* verified) \n{\n    Hash hash{};\n    *verified = Merkle().verify_proof(this_proof, verify_leaf, verify_index, root, hash);\n}\n\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void verify_proof_kernel(const proof<Merkle::MerkleSize, Hash, Precision>* proofs, const Precision* verify_leaves, const uint32_t* verify_indices, const Precision* root, bool* verified) \n{\n    Hash hash{};\n    constexpr size_t digest_size = proof<Merkle::MerkleSize, Hash, Precision>::digest_size;\n    uint32_t thread_index = blockIdx.x * blockDim.x + threadIdx.x;\n    const uint32_t verify_index = verify_indices[thread_index]; // This is the leaf's index in the tree\n    const Precision* verify_leaf = verify_leaves + thread_index * digest_size;\n    verified[thread_index] = Merkle().verify_proof(proofs[thread_index], verify_leaf, verify_index, root, hash);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid generate_tree(const std::vector<Precision>& msg, size_t inbuf_len, tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    Precision* d_msg;\n    cudaMalloc(reinterpret_cast<void**>(&d_msg), msg.size() * sizeof(Precision)); //Here msg.size has the number of messages and the length of each message is inbuf_len\n    cudaMemcpy(d_msg, msg.data(), msg.size() * sizeof(Precision), cudaMemcpyHostToDevice);\n    generate_merkle_tree_kernel<Merkle, Hash, Precision><<<1, Merkle::BlockDim>>>(merkle, d_msg, inbuf_len);\n    cudaFree(d_msg); //Device side message is no longer needed\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid generate_proof(const Precision* leaves_to_prove, const uint32_t* indices_to_prove, const size_t num_proofs, proof<Merkle::MerkleSize, Hash, Precision>* proofs, \n                    const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    dim3 gridDim((num_proofs + 255) / 256);\n    dim3 blockDim(256);\n    if(num_proofs < 256) {\n        gridDim.x = 1;\n        blockDim.x = num_proofs;\n    }\n    generate_proof_kernel<Merkle, Hash, Precision><<<gridDim, blockDim>>>(proofs, leaves_to_prove, indices_to_prove, merkle);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid verify_proof(const Precision* verify_leaves, const uint32_t* verify_indices, const size_t num_proofs, const proof<Merkle::MerkleSize, Hash, Precision>* proofs, \n                  const Precision* root, bool* verified)\n{\n    dim3 gridDim((num_proofs + 255) / 256);\n    dim3 blockDim(256);\n    if(num_proofs < 256) {\n        gridDim.x = 1;\n        blockDim.x = num_proofs;\n    }\n    verify_proof_kernel<Merkle, Hash, Precision><<<gridDim, blockDim>>>(proofs, verify_leaves, verify_indices, root, verified);\n}\n\n//This is simply a helper kernel to set the indices for this example.\n__global__ void index_set(uint32_t* indices, size_t num_indices) {\n    for (size_t i = threadIdx.x; i < num_indices; i += blockDim.x) {\n        indices[i] = i;\n    }\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n\n    using tree_type = tree<MERKLE::MerkleSize, HASH, uint32_t>;\n    using proof_type = proof<MERKLE::MerkleSize, HASH, uint32_t>;\n    constexpr auto N = MERKLE::MerkleSize;\n\n    constexpr size_t in_len = 64;\n    std::vector<uint32_t> msg(in_len * N, 0); // Here we have 2048 messages, each of length in_len\n\n    // Note that in_len is strictly less than BabyBearPrime, so we don't actually need to mod by BabyBearPrime.\n    // This is just for illustration, as our Poseidon2 hash is built on the BabyBear field.\n    for (size_t i = 0; i < N; i++) {\n        for (size_t j = 0; j < in_len; j++) {\n            msg[i * in_len + j] = (i * j) % cupqc_common::BabyBearPrime; // Ensure that the message is within the field\n        }\n    }\n\n    // To begin this sample, we will generate a Merkle tree from the messages.\n    // The host function will handle device memory for messages, and generate the tree using a single kernel.\n    tree_type merkle;\n    merkle.allocate_tree(); // This will allocate the tree on the device, as well as the root.\n    generate_tree<MERKLE, HASH, uint32_t>(msg, in_len, merkle);\n    //Our tree is now generated. We can now generate proofs for selected leaves. \n\n    //As a first illustration of proof generation and verification, we will generate and validate a proof for a single leaf.\n\n    proof_type first_proof;\n    first_proof.allocate_proof(); // This will allocate the proof on the device.\n    uint32_t* leaf_to_prove; \n\n    cudaMalloc(reinterpret_cast<void**>(&leaf_to_prove), proof_type::digest_size * sizeof(uint32_t));\n    cudaMemcpy(leaf_to_prove, merkle.nodes, proof_type::digest_size * sizeof(uint32_t), cudaMemcpyDeviceToDevice);\n\n    single_proof_kernel<MERKLE, HASH, uint32_t><<<1, 1>>>(first_proof, leaf_to_prove, 0, merkle);\n    //Our proof is now generated. We can now verify this leaf.\n\n    bool verified = false;\n    bool* d_verified;\n    cudaMalloc(reinterpret_cast<void**>(&d_verified), sizeof(bool));\n    cudaMemcpy(d_verified, &verified, sizeof(bool), cudaMemcpyHostToDevice);\n    single_verify_kernel<MERKLE, HASH, uint32_t><<<1, 1>>>(first_proof, leaf_to_prove, 0, merkle.root, d_verified);\n    cudaMemcpy(&verified, d_verified, sizeof(bool), cudaMemcpyDeviceToHost);\n    std::cout << \"Leaf \" << 0 << \" is \" << (verified ? \"verified\" : \"not verified\") << std::endl;\n\n    cudaFree(leaf_to_prove);\n    cudaFree(d_verified);\n    first_proof.free_proof();\n\n    // We can now generate proofs for 256 leaves, and verify them all.\n    uint32_t* d_leaves_to_prove;\n    uint32_t* d_indices_to_prove;\n    cudaMalloc(reinterpret_cast<void**>(&d_leaves_to_prove), 256 * proof_type::digest_size * sizeof(uint32_t));\n    cudaMalloc(reinterpret_cast<void**>(&d_indices_to_prove), 256 * sizeof(uint32_t));\n    cudaMemcpy(d_leaves_to_prove, merkle.nodes, 256 * proof_type::digest_size * sizeof(uint32_t), cudaMemcpyDeviceToDevice);\n    \n    index_set<<<1, 256>>>(d_indices_to_prove, 256);\n\n\n    //Here we will use cudaMallocManaged to allocate the proofs. We will need to directly allocate the nodes and indicies here. \n    proof_type* proofs;\n    cudaMallocManaged(reinterpret_cast<void**>(&proofs), 256 * sizeof(proof_type));\n    for (size_t i = 0; i < 256; i++) {\n        cudaMallocManaged(reinterpret_cast<void**>(&proofs[i].nodes), N * proof_type::digest_size * sizeof(uint32_t));\n        cudaMallocManaged(reinterpret_cast<void**>(&proofs[i].indices), N * sizeof(uint32_t));\n    }\n    cudaDeviceSynchronize();\n\n    std::cout << \" ================================================= \" << std::endl;\n    std::cout << \"Generating proofs for 256 leaves\" << std::endl;\n    generate_proof<MERKLE, HASH, uint32_t>(d_leaves_to_prove, d_indices_to_prove, 256, proofs, merkle);\n\n    std::cout << \" ================================================= \" << std::endl;\n    std::cout << \"Verifying proofs for 256 leaves\" << std::endl;\n    //We can now verify all of the proofs.\n    cudaMalloc(reinterpret_cast<void**>(&d_verified), 256 * sizeof(bool));\n    cudaMemset(d_verified, 0, 256 * sizeof(bool));\n    verify_proof<MERKLE, HASH, uint32_t>(d_leaves_to_prove, d_indices_to_prove, 256, proofs, merkle.root, d_verified);\n    for (size_t i = 0; i < 256; i++) {\n        cudaFree(proofs[i].nodes);\n        cudaFree(proofs[i].indices);\n    }\n    cudaFree(proofs);\n    cudaFree(d_leaves_to_prove);\n    cudaFree(d_indices_to_prove);\n    \n    bool verify[256];\n    cudaMemcpy(verify, d_verified, 256 * sizeof(bool), cudaMemcpyDeviceToHost);\n    for (size_t i = 0; i < 256; i++) {\n        std::cout << \"Leaf \" << i << \" is \" << (verify[i] ? \"verified\" : \"not verified\") << std::endl;\n    }\n    cudaFree(d_verified);\n\n    //We can now free the tree.\n    merkle.free_tree();\n\n    return 0;\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <iomanip>\n#include <stdio.h>\n#include <iostream>\n\n#include <cuhash.hpp>\n\nusing namespace cupqc;\n\nusing HASH   = decltype(POSEIDON2_8_16() + Thread());\n\n//Will use MERKLE_FIELD_2048 in this example, we require FIELD when using the Poseidon2 hash function, and we use 256 threads per block\nusing MERKLE = decltype(MERKLE_FIELD_2048() + BlockDim<256>());\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void generate_merkle_tree_kernel(tree<Merkle::MerkleSize, Hash, Precision> merkle, const Precision* msg, size_t inbuf_len)\n{\n    Hash hash{};\n    for(uint32_t i = threadIdx.x; i < Merkle::MerkleSize; i += blockDim.x) {\n        Merkle().create_leaf(merkle.nodes + i * merkle.digest_size, msg + i * inbuf_len, hash, inbuf_len);\n    }\n\n    Merkle().generate_tree(merkle.nodes, hash, merkle);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void single_proof_kernel(proof<Merkle::MerkleSize, Hash, Precision> this_proof, const Precision* leaf_to_prove, const uint32_t leaf_index, const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    Merkle().generate_proof(this_proof, leaf_to_prove, leaf_index, merkle);\n}\n\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void generate_proof_kernel(proof<Merkle::MerkleSize, Hash, Precision>* proofs, const Precision* leaves_to_prove, const uint32_t* leaf_indices, const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    constexpr size_t digest_size = proof<Merkle::MerkleSize, Hash, Precision>::digest_size;\n    uint32_t thread_index = blockIdx.x * blockDim.x + threadIdx.x;\n    const Precision* leaf_to_prove = leaves_to_prove + thread_index * digest_size;\n    const uint32_t leaf_index = leaf_indices[thread_index];\n    Merkle().generate_proof(proofs[thread_index], leaf_to_prove, leaf_index, merkle); // Each thread generates a proof for the given leaf and corresponding index\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void single_verify_kernel(const proof<Merkle::MerkleSize, Hash, Precision> this_proof, const Precision* verify_leaf, const uint32_t verify_index, const Precision* root, bool* verified) \n{\n    Hash hash{};\n    *verified = Merkle().verify_proof(this_proof, verify_leaf, verify_index, root, hash);\n}\n\n\ntemplate<class Merkle, class Hash, typename Precision>\n__global__ void verify_proof_kernel(const proof<Merkle::MerkleSize, Hash, Precision>* proofs, const Precision* verify_leaves, const uint32_t* verify_indices, const Precision* root, bool* verified) \n{\n    Hash hash{};\n    constexpr size_t digest_size = proof<Merkle::MerkleSize, Hash, Precision>::digest_size;\n    uint32_t thread_index = blockIdx.x * blockDim.x + threadIdx.x;\n    const uint32_t verify_index = verify_indices[thread_index]; // This is the leaf's index in the tree\n    const Precision* verify_leaf = verify_leaves + thread_index * digest_size;\n    verified[thread_index] = Merkle().verify_proof(proofs[thread_index], verify_leaf, verify_index, root, hash);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid generate_tree(const std::vector<Precision>& msg, size_t inbuf_len, tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    Precision* d_msg;\n    hipMalloc(reinterpret_cast<void**>(&d_msg), msg.size() * sizeof(Precision)); //Here msg.size has the number of messages and the length of each message is inbuf_len\n    hipMemcpy(d_msg, msg.data(), msg.size() * sizeof(Precision), hipMemcpyHostToDevice);\n    generate_merkle_tree_kernel<Merkle, Hash, Precision><<<1, Merkle::BlockDim>>>(merkle, d_msg, inbuf_len);\n    hipFree(d_msg); //Device side message is no longer needed\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid generate_proof(const Precision* leaves_to_prove, const uint32_t* indices_to_prove, const size_t num_proofs, proof<Merkle::MerkleSize, Hash, Precision>* proofs, \n                    const tree<Merkle::MerkleSize, Hash, Precision> merkle)\n{\n    dim3 gridDim((num_proofs + 255) / 256);\n    dim3 blockDim(256);\n    if(num_proofs < 256) {\n        gridDim.x = 1;\n        blockDim.x = num_proofs;\n    }\n    generate_proof_kernel<Merkle, Hash, Precision><<<gridDim, blockDim>>>(proofs, leaves_to_prove, indices_to_prove, merkle);\n}\n\ntemplate<class Merkle, class Hash, typename Precision>\nvoid verify_proof(const Precision* verify_leaves, const uint32_t* verify_indices, const size_t num_proofs, const proof<Merkle::MerkleSize, Hash, Precision>* proofs, \n                  const Precision* root, bool* verified)\n{\n    dim3 gridDim((num_proofs + 255) / 256);\n    dim3 blockDim(256);\n    if(num_proofs < 256) {\n        gridDim.x = 1;\n        blockDim.x = num_proofs;\n    }\n    verify_proof_kernel<Merkle, Hash, Precision><<<gridDim, blockDim>>>(proofs, verify_leaves, verify_indices, root, verified);\n}\n\n//This is simply a helper kernel to set the indices for this example.\n__global__ void index_set(uint32_t* indices, size_t num_indices) {\n    for (size_t i = threadIdx.x; i < num_indices; i += blockDim.x) {\n        indices[i] = i;\n    }\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n\n    using tree_type = tree<MERKLE::MerkleSize, HASH, uint32_t>;\n    using proof_type = proof<MERKLE::MerkleSize, HASH, uint32_t>;\n    constexpr auto N = MERKLE::MerkleSize;\n\n    constexpr size_t in_len = 64;\n    std::vector<uint32_t> msg(in_len * N, 0); // Here we have 2048 messages, each of length in_len\n\n    // Note that in_len is strictly less than BabyBearPrime, so we don't actually need to mod by BabyBearPrime.\n    // This is just for illustration, as our Poseidon2 hash is built on the BabyBear field.\n    for (size_t i = 0; i < N; i++) {\n        for (size_t j = 0; j < in_len; j++) {\n            msg[i * in_len + j] = (i * j) % cupqc_common::BabyBearPrime; // Ensure that the message is within the field\n        }\n    }\n\n    // To begin this sample, we will generate a Merkle tree from the messages.\n    // The host function will handle device memory for messages, and generate the tree using a single kernel.\n    tree_type merkle;\n    merkle.allocate_tree(); // This will allocate the tree on the device, as well as the root.\n    generate_tree<MERKLE, HASH, uint32_t>(msg, in_len, merkle);\n    //Our tree is now generated. We can now generate proofs for selected leaves. \n\n    //As a first illustration of proof generation and verification, we will generate and validate a proof for a single leaf.\n\n    proof_type first_proof;\n    first_proof.allocate_proof(); // This will allocate the proof on the device.\n    uint32_t* leaf_to_prove; \n\n    hipMalloc(reinterpret_cast<void**>(&leaf_to_prove), proof_type::digest_size * sizeof(uint32_t));\n    hipMemcpy(leaf_to_prove, merkle.nodes, proof_type::digest_size * sizeof(uint32_t), hipMemcpyDeviceToDevice);\n\n    single_proof_kernel<MERKLE, HASH, uint32_t><<<1, 1>>>(first_proof, leaf_to_prove, 0, merkle);\n    //Our proof is now generated. We can now verify this leaf.\n\n    bool verified = false;\n    bool* d_verified;\n    hipMalloc(reinterpret_cast<void**>(&d_verified), sizeof(bool));\n    hipMemcpy(d_verified, &verified, sizeof(bool), hipMemcpyHostToDevice);\n    single_verify_kernel<MERKLE, HASH, uint32_t><<<1, 1>>>(first_proof, leaf_to_prove, 0, merkle.root, d_verified);\n    hipMemcpy(&verified, d_verified, sizeof(bool), hipMemcpyDeviceToHost);\n    std::cout << \"Leaf \" << 0 << \" is \" << (verified ? \"verified\" : \"not verified\") << std::endl;\n\n    hipFree(leaf_to_prove);\n    hipFree(d_verified);\n    first_proof.free_proof();\n\n    // We can now generate proofs for 256 leaves, and verify them all.\n    uint32_t* d_leaves_to_prove;\n    uint32_t* d_indices_to_prove;\n    hipMalloc(reinterpret_cast<void**>(&d_leaves_to_prove), 256 * proof_type::digest_size * sizeof(uint32_t));\n    hipMalloc(reinterpret_cast<void**>(&d_indices_to_prove), 256 * sizeof(uint32_t));\n    hipMemcpy(d_leaves_to_prove, merkle.nodes, 256 * proof_type::digest_size * sizeof(uint32_t), hipMemcpyDeviceToDevice);\n    \n    index_set<<<1, 256>>>(d_indices_to_prove, 256);\n\n\n    //Here we will use cudaMallocManaged to allocate the proofs. We will need to directly allocate the nodes and indicies here. \n    proof_type* proofs;\n    hipMallocManaged(reinterpret_cast<void**>(&proofs), 256 * sizeof(proof_type));\n    for (size_t i = 0; i < 256; i++) {\n        hipMallocManaged(reinterpret_cast<void**>(&proofs[i].nodes), N * proof_type::digest_size * sizeof(uint32_t));\n        hipMallocManaged(reinterpret_cast<void**>(&proofs[i].indices), N * sizeof(uint32_t));\n    }\n    hipDeviceSynchronize();\n\n    std::cout << \" ================================================= \" << std::endl;\n    std::cout << \"Generating proofs for 256 leaves\" << std::endl;\n    generate_proof<MERKLE, HASH, uint32_t>(d_leaves_to_prove, d_indices_to_prove, 256, proofs, merkle);\n\n    std::cout << \" ================================================= \" << std::endl;\n    std::cout << \"Verifying proofs for 256 leaves\" << std::endl;\n    //We can now verify all of the proofs.\n    hipMalloc(reinterpret_cast<void**>(&d_verified), 256 * sizeof(bool));\n    hipMemset(d_verified, 0, 256 * sizeof(bool));\n    verify_proof<MERKLE, HASH, uint32_t>(d_leaves_to_prove, d_indices_to_prove, 256, proofs, merkle.root, d_verified);\n    for (size_t i = 0; i < 256; i++) {\n        hipFree(proofs[i].nodes);\n        hipFree(proofs[i].indices);\n    }\n    hipFree(proofs);\n    hipFree(d_leaves_to_prove);\n    hipFree(d_indices_to_prove);\n    \n    bool verify[256];\n    hipMemcpy(verify, d_verified, 256 * sizeof(bool), hipMemcpyDeviceToHost);\n    for (size_t i = 0; i < 256; i++) {\n        std::cout << \"Leaf \" << i << \" is \" << (verify[i] ? \"verified\" : \"not verified\") << std::endl;\n    }\n    hipFree(d_verified);\n\n    //We can now free the tree.\n    merkle.free_tree();\n\n    return 0;\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_merkle.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iomanip>\n#include <cuhash.hpp>\n#include <stdio.h>\n\nusing namespace cupqc;\n\nusing SHA3_256_WARP = decltype(SHA3_256() + Warp());\n\n__global__ void hash_sha3_kernel(uint8_t* digest, const uint8_t* msg, size_t inbuf_len)\n{\n    SHA3_256_WARP hash {};\n    hash.reset();\n    hash.update(msg, inbuf_len);\n    hash.finalize();\n    hash.digest(digest, SHA3_256_WARP::digest_size);\n}\n\nvoid hash_sha3(std::vector<uint8_t>& digest, std::vector<uint8_t>& msg)\n{\n    uint8_t* d_msg;\n    uint8_t* d_digest;\n    cudaMalloc(reinterpret_cast<void**>(&d_msg), msg.size());\n    cudaMalloc(reinterpret_cast<void**>(&d_digest), digest.size());\n\n    cudaMemcpy(d_msg, msg.data(), msg.size(), cudaMemcpyHostToDevice);\n\n    hash_sha3_kernel<<<1, 32>>>(d_digest, d_msg, msg.size());\n\n    cudaMemcpy(digest.data(), d_digest, digest.size(), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_msg);\n    cudaFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    const char * msg_str = \"The quick brown fox jumps over the lazy dog\";\n    std::vector<uint8_t> msg(reinterpret_cast<const uint8_t*>(msg_str), reinterpret_cast<const uint8_t*>(msg_str) + strlen(msg_str));\n    std::vector<uint8_t> digest(SHA3_256::digest_size, 0);\n    hash_sha3(digest, msg);\n    printf(\"SHA3-256: \");\n    for (uint8_t num : digest) {\n        printf(\"%02x\", num);\n    }\n    printf(\"\\n\");\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <iomanip>\n#include <cuhash.hpp>\n#include <stdio.h>\n\nusing namespace cupqc;\n\nusing SHA3_256_WARP = decltype(SHA3_256() + Warp());\n\n__global__ void hash_sha3_kernel(uint8_t* digest, const uint8_t* msg, size_t inbuf_len)\n{\n    SHA3_256_WARP hash {};\n    hash.reset();\n    hash.update(msg, inbuf_len);\n    hash.finalize();\n    hash.digest(digest, SHA3_256_WARP::digest_size);\n}\n\nvoid hash_sha3(std::vector<uint8_t>& digest, std::vector<uint8_t>& msg)\n{\n    uint8_t* d_msg;\n    uint8_t* d_digest;\n    hipMalloc(reinterpret_cast<void**>(&d_msg), msg.size());\n    hipMalloc(reinterpret_cast<void**>(&d_digest), digest.size());\n\n    hipMemcpy(d_msg, msg.data(), msg.size(), hipMemcpyHostToDevice);\n\n    hash_sha3_kernel<<<1, 32>>>(d_digest, d_msg, msg.size());\n\n    hipMemcpy(digest.data(), d_digest, digest.size(), hipMemcpyDeviceToHost);\n\n    hipFree(d_msg);\n    hipFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    const char * msg_str = \"The quick brown fox jumps over the lazy dog\";\n    std::vector<uint8_t> msg(reinterpret_cast<const uint8_t*>(msg_str), reinterpret_cast<const uint8_t*>(msg_str) + strlen(msg_str));\n    std::vector<uint8_t> digest(SHA3_256::digest_size, 0);\n    hash_sha3(digest, msg);\n    printf(\"SHA3-256: \");\n    for (uint8_t num : digest) {\n        printf(\"%02x\", num);\n    }\n    printf(\"\\n\");\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_sha3.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iomanip>\n#include <stdio.h>\n#include <iostream>\n\n#include <cuhash.hpp>\n\nusing namespace cupqc;\nusing HASH = decltype(POSEIDON2_8_16() + Thread());\n\n__global__ void hash_poseidon2_kernel(uint32_t* digest, const uint32_t* msg, size_t inbuf_len, size_t out_len)\n{\n    // Poseidon2 with Capacity 8 and Width 16 with BabyBear field\n    HASH hash {};\n    hash.reset();\n    hash.update(msg, inbuf_len);\n    hash.finalize();\n    hash.digest(digest, out_len);\n}\n\nvoid hash_poseidon2(std::vector<uint32_t>& digest, std::vector<uint32_t>& msg, size_t out_len)\n{\n    uint32_t* d_msg;\n    uint32_t* d_digest;\n    cudaMalloc(reinterpret_cast<void**>(&d_msg), msg.size() * sizeof(uint32_t));\n    cudaMalloc(reinterpret_cast<void**>(&d_digest), digest.size() * sizeof(uint32_t));\n\n    cudaMemcpy(d_msg, msg.data(), msg.size() * sizeof(uint32_t), cudaMemcpyHostToDevice);\n\n    //Note that the poseidon2 hash function is a thread configuration, so we use a single thread to hash a single message.\n    hash_poseidon2_kernel<<<1, 1>>>(d_digest, d_msg, msg.size(), out_len);\n\n    cudaMemcpy(digest.data(), d_digest, digest.size() * sizeof(uint32_t), cudaMemcpyDeviceToHost);\n\n    cudaFree(d_msg);\n    cudaFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    constexpr size_t in_len = 64;\n    constexpr size_t out_len = 16;\n    std::vector<uint32_t> msg(in_len, 0);\n\n    // Note that in_len is strictly less than BabyBearPrime, so we don't actually need to mod by BabyBearPrime.\n    // This is just for illustration, as our Poseidon2 hash is built on the BabyBear field.\n    for (size_t i = 0; i < in_len; i++) {\n        msg[i] = i % cupqc_common::BabyBearPrime;\n    }\n\n\n    std::vector<uint32_t> digest(out_len, 0);\n    hash_poseidon2(digest, msg, out_len);\n    std::cout << \"Poseidon2 Hashed output: \" << std::endl;\n    for (uint32_t num : digest) {\n        std::cout << num << '\\t';\n    }\n    std::cout << std::endl;\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <vector>\n#include <iomanip>\n#include <stdio.h>\n#include <iostream>\n\n#include <cuhash.hpp>\n\nusing namespace cupqc;\nusing HASH = decltype(POSEIDON2_8_16() + Thread());\n\n__global__ void hash_poseidon2_kernel(uint32_t* digest, const uint32_t* msg, size_t inbuf_len, size_t out_len)\n{\n    // Poseidon2 with Capacity 8 and Width 16 with BabyBear field\n    HASH hash {};\n    hash.reset();\n    hash.update(msg, inbuf_len);\n    hash.finalize();\n    hash.digest(digest, out_len);\n}\n\nvoid hash_poseidon2(std::vector<uint32_t>& digest, std::vector<uint32_t>& msg, size_t out_len)\n{\n    uint32_t* d_msg;\n    uint32_t* d_digest;\n    hipMalloc(reinterpret_cast<void**>(&d_msg), msg.size() * sizeof(uint32_t));\n    hipMalloc(reinterpret_cast<void**>(&d_digest), digest.size() * sizeof(uint32_t));\n\n    hipMemcpy(d_msg, msg.data(), msg.size() * sizeof(uint32_t), hipMemcpyHostToDevice);\n\n    //Note that the poseidon2 hash function is a thread configuration, so we use a single thread to hash a single message.\n    hash_poseidon2_kernel<<<1, 1>>>(d_digest, d_msg, msg.size(), out_len);\n\n    hipMemcpy(digest.data(), d_digest, digest.size() * sizeof(uint32_t), hipMemcpyDeviceToHost);\n\n    hipFree(d_msg);\n    hipFree(d_digest);\n}\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {\n    constexpr size_t in_len = 64;\n    constexpr size_t out_len = 16;\n    std::vector<uint32_t> msg(in_len, 0);\n\n    // Note that in_len is strictly less than BabyBearPrime, so we don't actually need to mod by BabyBearPrime.\n    // This is just for illustration, as our Poseidon2 hash is built on the BabyBear field.\n    for (size_t i = 0; i < in_len; i++) {\n        msg[i] = i % cupqc_common::BabyBearPrime;\n    }\n\n\n    std::vector<uint32_t> digest(out_len, 0);\n    hash_poseidon2(digest, msg, out_len);\n    std::cout << \"Poseidon2 Hashed output: \" << std::endl;\n    for (uint32_t num : digest) {\n        std::cout << num << '\\t';\n    }\n    std::cout << std::endl;\n}", "source": "/kaggle/working/CUDALibrarySamples/cuPQC/example_poseidon2.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Batched GEMM kernel using the pipelining API with a grid-stride loop\n// over the batch dimension.\n//\n// Each CUDA block processes one (row_tile, col_tile) spatial position and\n// iterates over batches: blockIdx.z, blockIdx.z + gridDim.z, ...\n//\n//   blockIdx.x = row tile index  (0 .. M/tile_m - 1)\n//   blockIdx.y = col tile index  (0 .. N/tile_n - 1)\n//   blockIdx.z = initial batch index\n//\n// A and B are rank-3 tensors (M, K, batch_count) and (K, N, batch_count).\n// The pipeline's get_tile() accepts rank-2 coordinates per operand:\n//   coord_a = (row_tile, batch)  -- selects the A tile for this block\n//   coord_b = (col_tile, batch)  -- selects the B tile for this block\n//\n// After the first batch, reset_tile() re-points the pipeline at the next\n// batch without reallocating barriers or shared memory.\n//\n// C is passed as a flat pointer. Inside the kernel, cublasdx::make_gmem_tensor\n// reconstructs a 2D view for the current batch (offset = batch * m * n),\n// then cublasdx::get_tile selects the per-block output tile.\n// ArrC is a template parameter so make_gmem_tensor<ArrC> can be called in device code.\ntemplate<class BLAS, cublasdx::arrangement ArrC, class Alpha, class Beta, class CValueType, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void batched_gemm_kernel(Alpha const                            alpha,\n                             Beta const                             beta,\n                             CValueType*                            c_ptr,\n                             unsigned                               m,\n                             unsigned                               n,\n                             unsigned                               ldc,\n                             unsigned                               batch_count,\n                             __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        // First batch: create the tile pipeline\n        auto tile_pipeline =\n            device_pipeline.get_tile(smem,\n                                     cublasdx::make_coord(blockIdx.x, blockIdx.z),  // A: (row_tile, batch)\n                                     cublasdx::make_coord(blockIdx.y, blockIdx.z)); // B: (col_tile, batch)\n\n        // Grid-stride loop over the batch dimension\n        for (unsigned batch = blockIdx.z; batch < batch_count;) {\n            // Build a 2D C view for the current batch\n            auto tile_gmem_c = cublasdx::get_tile(cublasdx::make_gmem_tensor<ArrC>(c_ptr + batch * m * n, m, n, ldc),\n                                                  BLAS::c_shape,\n                                                  blockIdx.x,\n                                                  blockIdx.y);\n\n            // Epilogue: D = alpha * A * B + beta * C\n            auto epilogue_functor = [&](auto& accumulator) {\n                auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n                cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n                accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n            };\n\n            tile_pipeline.execute(epilogue_functor);\n\n            // Re-point the pipeline at the next batch for the next iteration\n            batch += gridDim.z;\n            device_pipeline.reset_tile(tile_pipeline,\n                                       cublasdx::make_coord(blockIdx.x, batch),  // A: (row_tile, batch)\n                                       cublasdx::make_coord(blockIdx.y, batch)); // B: (col_tile, batch)\n        }\n    }\n#endif\n}\n\n// This is an example of batched GEMM using cuBLASDx pipelining API,\n// where batching is represented as the third dimension of global memory tensors.\n//\n//              C[b] = alpha * A[b] * B[b] + beta * C[b]   for b = 0 .. batch_count - 1\n//\n// A[b], B[b], C[b] are M x K, K x N, M x N matrices stored contiguously per batch:\n//   A: (M, K, batch_count)  -- row-major\n//   B: (K, N, batch_count)  -- col-major\n//   C: (M, N, batch_count)  -- row-major\n//\n// The pipelining API handles the batch dimension transparently through the rank-3\n// tensor layout. Internally, suggest_device_pipeline() detects rank-3 tensors and\n// adjusts the tiler to include a unit batch dimension (tile_m, tile_k, 1) / (tile_k, tile_n, 1).\n//\n// A grid-stride loop over the batch dimension lets each spatial (row_tile, col_tile)\n// block process multiple batches.  After the first batch, reset_tile() re-points the\n// pipeline at the next batch without reinitializing barriers or shared memory.\n//\n// Grid: (M/tile_m, N/tile_n, min(batch_count, num_sms / spatial_tiles))\n//   blockIdx.z is the initial batch index; the grid-stride loop handles the rest.\n//\n// M, N, K must be divisible by tile_m, tile_n, tile_k respectively.\n// batch_count can be any positive integer.\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint batched_gemm_pipeline() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Per-batch matrix dimensions\n    const unsigned m = 1024; // A: M x K, C: M x N\n    const unsigned n = 1024; // B: K x N, C: M x N\n    const unsigned k = 512;  // Contraction dimension\n\n    // Number of batches -- can be any positive integer\n    const unsigned batch_count = 4;\n\n    // Global data arrangement\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading dimensions (tight packing, no extra padding)\n    const unsigned global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const unsigned global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const unsigned global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    constexpr auto type = cublasdx::type::real;\n\n    using a_value_type =\n        std::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_value_type =\n        std::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_value_type =\n        std::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    c_value_type alpha = example::make_value<c_value_type>(1.1);\n    c_value_type beta  = example::make_value<c_value_type>(1.2);\n\n    constexpr unsigned tile_m       = 128;\n    constexpr unsigned tile_n       = 128;\n    constexpr unsigned tile_k       = 32;\n    constexpr int      tile_threads = 128;\n\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    constexpr unsigned int cublasdx_alignment = 16;\n\n    // If manual_pipeline_depth is 0, the maximum depth for the target architecture is used.\n    constexpr unsigned manual_pipeline_depth   = 0;\n    constexpr bool     override_pipeline_depth = (manual_pipeline_depth != 0);\n\n    // ================================\n    // Validate configuration\n    // ================================\n\n    constexpr unsigned stage_shared_req = tile_m * tile_k * sizeof(a_value_type) +\n                                          tile_k * tile_n * sizeof(b_value_type) +\n                                          sizeof(cublasdx::pipeline_stage_scratch_t);\n\n    constexpr unsigned available_shared_memory = commondx::device_info<Arch>::shared_memory();\n    constexpr unsigned maximal_pipeline_depth  = std::min(k / tile_k, std::min(16u, (available_shared_memory - 32u) / stage_shared_req));\n    constexpr unsigned pipeline_depth = override_pipeline_depth ? manual_pipeline_depth : maximal_pipeline_depth;\n\n    static_assert(pipeline_depth <= maximal_pipeline_depth,\n                  \"Chosen pipeline depth requires more shared memory than available on target architecture\");\n\n    if (m % tile_m != 0 || n % tile_n != 0 || k % tile_k != 0) {\n        std::cerr << \"M, N, K must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    if (k / tile_k < pipeline_depth) {\n        std::cerr << \"K stages (\" << k / tile_k << \") must be >= pipeline_depth (\" << pipeline_depth << \")\"\n                  << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Build BLAS descriptor\n    // ================================\n\n    // Same descriptor as example 11 -- batching is purely a grid/tensor concern,\n    // not part of the per-block BLAS descriptor.\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::EnableInputStreaming() + cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // ================================\n    // Allocate and fill device memory\n    // ================================\n\n    const size_t a_total = static_cast<size_t>(m) * k * batch_count;\n    const size_t b_total = static_cast<size_t>(k) * n * batch_count;\n    const size_t c_total = static_cast<size_t>(m) * n * batch_count;\n\n    a_value_type* d_a = nullptr;\n    b_value_type* d_b = nullptr;\n    c_value_type* d_c = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_a, a_total * sizeof(a_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_b, b_total * sizeof(b_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_c, c_total * sizeof(c_value_type)));\n\n    auto host_a = example::get_random_data<a_value_type>(a_total, 1);\n    auto host_b = example::get_random_data<b_value_type>(b_total, 2);\n    auto host_c = example::get_random_data<c_value_type>(c_total, 3);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_a, host_a.data(), a_total * sizeof(a_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_b, host_b.data(), b_total * sizeof(b_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_c, host_c.data(), c_total * sizeof(c_value_type), cudaMemcpyHostToDevice));\n\n    // ================================================================\n    // Build rank-3 global tensors -- batching hidden as 3rd dimension\n    //   A: (M, K, batch_count)   row-major, batch stride = M * K\n    //   B: (K, N, batch_count)   col-major, batch stride = K * N\n    // C is passed as a flat pointer; the kernel reconstructs a per-batch\n    // 2D view via cublasdx::make_gmem_tensor (batch stride = M * N).\n    // ================================================================\n\n    auto global_a = cublasdx::make_gmem_tensor_batched<global_arrangement_a>(d_a, m, k, batch_count);\n    auto global_b = cublasdx::make_gmem_tensor_batched<global_arrangement_b>(d_b, k, n, batch_count);\n\n    // ================================================================\n    // Build device pipeline from rank-3 A/B tensors.\n    // suggest_device_pipeline detects rank-3 tensors via pipeline_helper::is_3d\n    // and adjusts internal tilers to (tile_m, tile_k, 1) / (tile_k, tile_n, 1).\n    // ================================================================\n\n    auto opt_device_pipeline = cublasdx::suggest_device_pipeline<pipeline_depth, BLAS>(global_a, global_b);\n\n    if (not opt_device_pipeline) {\n        std::cerr << \"Pipeline configuration invalid: ensure M/N/K are divisible by tile sizes and K >= pipeline depth\"\n                  << std::endl;\n        return 1;\n    }\n\n    auto device_pipeline = opt_device_pipeline.value();\n\n    auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                  .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                  .get();\n\n    // ================================================================\n    // Launch kernel with grid-stride loop over the batch dimension\n    //\n    // Grid: (M/tile_m, N/tile_n, min(batch_count, num_sms / spatial_tiles))\n    //   blockIdx.x = row tile index\n    //   blockIdx.y = col tile index\n    //   blockIdx.z = initial batch index (grid-stride loop handles the rest)\n    // ================================================================\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(cudaDeviceGetAttribute(&num_sms, cudaDevAttrMultiProcessorCount, 0));\n    const unsigned spatial_tiles = (m / tile_m) * (n / tile_n);\n    const unsigned batch_grid    = std::min(batch_count, std::max(1u, static_cast<unsigned>(num_sms) / spatial_tiles));\n\n    dim3 grid_dim = {m / tile_m, n / tile_n, batch_grid};\n\n    auto kernel = batched_gemm_kernel<BLAS,\n                                      global_arrangement_c,\n                                      c_value_type,\n                                      c_value_type,\n                                      c_value_type,\n                                      decltype(device_pipeline)>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size>>>(\n        alpha, beta, d_c, m, n, global_ldc, batch_count, device_pipeline);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // ================================\n    // Copy results back and verify\n    // ================================\n\n    std::vector<c_value_type> host_c_result(c_total);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(host_c_result.data(), d_c, c_total * sizeof(c_value_type), cudaMemcpyDeviceToHost));\n\n    const auto gemm_shape = std::make_tuple(m, n, k);\n    const auto gemm_ld    = std::make_tuple(global_lda, global_ldb, global_ldc);\n\n    bool all_correct = true;\n    for (unsigned b = 0; b < batch_count; ++b) {\n        std::vector<a_value_type> batch_a(host_a.begin() + b * m * k, host_a.begin() + (b + 1) * m * k);\n        std::vector<b_value_type> batch_b(host_b.begin() + b * k * n, host_b.begin() + (b + 1) * k * n);\n        std::vector<c_value_type> batch_c(host_c.begin() + b * m * n, host_c.begin() + (b + 1) * m * n);\n\n        auto reference = example::reference_gemm<BLAS>(gemm_shape, gemm_ld, alpha, batch_a, batch_b, beta, batch_c);\n\n        std::vector<c_value_type> batch_result(host_c_result.begin() + b * m * n,\n                                               host_c_result.begin() + (b + 1) * m * n);\n\n        if (not example::check_error<BLAS>(batch_result, reference)) {\n            std::cerr << \"Batch \" << b << \": FAILED\" << std::endl;\n            all_correct = false;\n        }\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_a));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_b));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_c));\n\n    if (all_correct) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    return 1;\n}\n\nstruct batched_gemm_pipeline_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return batched_gemm_pipeline<Arch, Modifier>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(batched_gemm_pipeline_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Batched GEMM kernel using the pipelining API with a grid-stride loop\n// over the batch dimension.\n//\n// Each CUDA block processes one (row_tile, col_tile) spatial position and\n// iterates over batches: blockIdx.z, blockIdx.z + gridDim.z, ...\n//\n//   blockIdx.x = row tile index  (0 .. M/tile_m - 1)\n//   blockIdx.y = col tile index  (0 .. N/tile_n - 1)\n//   blockIdx.z = initial batch index\n//\n// A and B are rank-3 tensors (M, K, batch_count) and (K, N, batch_count).\n// The pipeline's get_tile() accepts rank-2 coordinates per operand:\n//   coord_a = (row_tile, batch)  -- selects the A tile for this block\n//   coord_b = (col_tile, batch)  -- selects the B tile for this block\n//\n// After the first batch, reset_tile() re-points the pipeline at the next\n// batch without reallocating barriers or shared memory.\n//\n// C is passed as a flat pointer. Inside the kernel, cublasdx::make_gmem_tensor\n// reconstructs a 2D view for the current batch (offset = batch * m * n),\n// then cublasdx::get_tile selects the per-block output tile.\n// ArrC is a template parameter so make_gmem_tensor<ArrC> can be called in device code.\ntemplate<class BLAS, cublasdx::arrangement ArrC, class Alpha, class Beta, class CValueType, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void batched_gemm_kernel(Alpha const                            alpha,\n                             Beta const                             beta,\n                             CValueType*                            c_ptr,\n                             unsigned                               m,\n                             unsigned                               n,\n                             unsigned                               ldc,\n                             unsigned                               batch_count,\n                             __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        // First batch: create the tile pipeline\n        auto tile_pipeline =\n            device_pipeline.get_tile(smem,\n                                     cublasdx::make_coord(blockIdx.x, blockIdx.z),  // A: (row_tile, batch)\n                                     cublasdx::make_coord(blockIdx.y, blockIdx.z)); // B: (col_tile, batch)\n\n        // Grid-stride loop over the batch dimension\n        for (unsigned batch = blockIdx.z; batch < batch_count;) {\n            // Build a 2D C view for the current batch\n            auto tile_gmem_c = cublasdx::get_tile(cublasdx::make_gmem_tensor<ArrC>(c_ptr + batch * m * n, m, n, ldc),\n                                                  BLAS::c_shape,\n                                                  blockIdx.x,\n                                                  blockIdx.y);\n\n            // Epilogue: D = alpha * A * B + beta * C\n            auto epilogue_functor = [&](auto& accumulator) {\n                auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n                cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n                accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n            };\n\n            tile_pipeline.execute(epilogue_functor);\n\n            // Re-point the pipeline at the next batch for the next iteration\n            batch += gridDim.z;\n            device_pipeline.reset_tile(tile_pipeline,\n                                       cublasdx::make_coord(blockIdx.x, batch),  // A: (row_tile, batch)\n                                       cublasdx::make_coord(blockIdx.y, batch)); // B: (col_tile, batch)\n        }\n    }\n#endif\n}\n\n// This is an example of batched GEMM using cuBLASDx pipelining API,\n// where batching is represented as the third dimension of global memory tensors.\n//\n//              C[b] = alpha * A[b] * B[b] + beta * C[b]   for b = 0 .. batch_count - 1\n//\n// A[b], B[b], C[b] are M x K, K x N, M x N matrices stored contiguously per batch:\n//   A: (M, K, batch_count)  -- row-major\n//   B: (K, N, batch_count)  -- col-major\n//   C: (M, N, batch_count)  -- row-major\n//\n// The pipelining API handles the batch dimension transparently through the rank-3\n// tensor layout. Internally, suggest_device_pipeline() detects rank-3 tensors and\n// adjusts the tiler to include a unit batch dimension (tile_m, tile_k, 1) / (tile_k, tile_n, 1).\n//\n// A grid-stride loop over the batch dimension lets each spatial (row_tile, col_tile)\n// block process multiple batches.  After the first batch, reset_tile() re-points the\n// pipeline at the next batch without reinitializing barriers or shared memory.\n//\n// Grid: (M/tile_m, N/tile_n, min(batch_count, num_sms / spatial_tiles))\n//   blockIdx.z is the initial batch index; the grid-stride loop handles the rest.\n//\n// M, N, K must be divisible by tile_m, tile_n, tile_k respectively.\n// batch_count can be any positive integer.\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint batched_gemm_pipeline() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Per-batch matrix dimensions\n    const unsigned m = 1024; // A: M x K, C: M x N\n    const unsigned n = 1024; // B: K x N, C: M x N\n    const unsigned k = 512;  // Contraction dimension\n\n    // Number of batches -- can be any positive integer\n    const unsigned batch_count = 4;\n\n    // Global data arrangement\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading dimensions (tight packing, no extra padding)\n    const unsigned global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const unsigned global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const unsigned global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    constexpr auto type = cublasdx::type::real;\n\n    using a_value_type =\n        std::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_value_type =\n        std::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_value_type =\n        std::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    c_value_type alpha = example::make_value<c_value_type>(1.1);\n    c_value_type beta  = example::make_value<c_value_type>(1.2);\n\n    constexpr unsigned tile_m       = 128;\n    constexpr unsigned tile_n       = 128;\n    constexpr unsigned tile_k       = 32;\n    constexpr int      tile_threads = 128;\n\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    constexpr unsigned int cublasdx_alignment = 16;\n\n    // If manual_pipeline_depth is 0, the maximum depth for the target architecture is used.\n    constexpr unsigned manual_pipeline_depth   = 0;\n    constexpr bool     override_pipeline_depth = (manual_pipeline_depth != 0);\n\n    // ================================\n    // Validate configuration\n    // ================================\n\n    constexpr unsigned stage_shared_req = tile_m * tile_k * sizeof(a_value_type) +\n                                          tile_k * tile_n * sizeof(b_value_type) +\n                                          sizeof(cublasdx::pipeline_stage_scratch_t);\n\n    constexpr unsigned available_shared_memory = commondx::device_info<Arch>::shared_memory();\n    constexpr unsigned maximal_pipeline_depth  = std::min(k / tile_k, std::min(16u, (available_shared_memory - 32u) / stage_shared_req));\n    constexpr unsigned pipeline_depth = override_pipeline_depth ? manual_pipeline_depth : maximal_pipeline_depth;\n\n    static_assert(pipeline_depth <= maximal_pipeline_depth,\n                  \"Chosen pipeline depth requires more shared memory than available on target architecture\");\n\n    if (m % tile_m != 0 || n % tile_n != 0 || k % tile_k != 0) {\n        std::cerr << \"M, N, K must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    if (k / tile_k < pipeline_depth) {\n        std::cerr << \"K stages (\" << k / tile_k << \") must be >= pipeline_depth (\" << pipeline_depth << \")\"\n                  << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Build BLAS descriptor\n    // ================================\n\n    // Same descriptor as example 11 -- batching is purely a grid/tensor concern,\n    // not part of the per-block BLAS descriptor.\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::EnableInputStreaming() + cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // ================================\n    // Allocate and fill device memory\n    // ================================\n\n    const size_t a_total = static_cast<size_t>(m) * k * batch_count;\n    const size_t b_total = static_cast<size_t>(k) * n * batch_count;\n    const size_t c_total = static_cast<size_t>(m) * n * batch_count;\n\n    a_value_type* d_a = nullptr;\n    b_value_type* d_b = nullptr;\n    c_value_type* d_c = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_a, a_total * sizeof(a_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_b, b_total * sizeof(b_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_c, c_total * sizeof(c_value_type)));\n\n    auto host_a = example::get_random_data<a_value_type>(a_total, 1);\n    auto host_b = example::get_random_data<b_value_type>(b_total, 2);\n    auto host_c = example::get_random_data<c_value_type>(c_total, 3);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_a, host_a.data(), a_total * sizeof(a_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_b, host_b.data(), b_total * sizeof(b_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_c, host_c.data(), c_total * sizeof(c_value_type), hipMemcpyHostToDevice));\n\n    // ================================================================\n    // Build rank-3 global tensors -- batching hidden as 3rd dimension\n    //   A: (M, K, batch_count)   row-major, batch stride = M * K\n    //   B: (K, N, batch_count)   col-major, batch stride = K * N\n    // C is passed as a flat pointer; the kernel reconstructs a per-batch\n    // 2D view via cublasdx::make_gmem_tensor (batch stride = M * N).\n    // ================================================================\n\n    auto global_a = cublasdx::make_gmem_tensor_batched<global_arrangement_a>(d_a, m, k, batch_count);\n    auto global_b = cublasdx::make_gmem_tensor_batched<global_arrangement_b>(d_b, k, n, batch_count);\n\n    // ================================================================\n    // Build device pipeline from rank-3 A/B tensors.\n    // suggest_device_pipeline detects rank-3 tensors via pipeline_helper::is_3d\n    // and adjusts internal tilers to (tile_m, tile_k, 1) / (tile_k, tile_n, 1).\n    // ================================================================\n\n    auto opt_device_pipeline = cublasdx::suggest_device_pipeline<pipeline_depth, BLAS>(global_a, global_b);\n\n    if (not opt_device_pipeline) {\n        std::cerr << \"Pipeline configuration invalid: ensure M/N/K are divisible by tile sizes and K >= pipeline depth\"\n                  << std::endl;\n        return 1;\n    }\n\n    auto device_pipeline = opt_device_pipeline.value();\n\n    auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                  .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                  .get();\n\n    // ================================================================\n    // Launch kernel with grid-stride loop over the batch dimension\n    //\n    // Grid: (M/tile_m, N/tile_n, min(batch_count, num_sms / spatial_tiles))\n    //   blockIdx.x = row tile index\n    //   blockIdx.y = col tile index\n    //   blockIdx.z = initial batch index (grid-stride loop handles the rest)\n    // ================================================================\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(hipDeviceGetAttribute(&num_sms, hipDeviceAttributeMultiprocessorCount, 0));\n    const unsigned spatial_tiles = (m / tile_m) * (n / tile_n);\n    const unsigned batch_grid    = std::min(batch_count, std::max(1u, static_cast<unsigned>(num_sms) / spatial_tiles));\n\n    dim3 grid_dim = {m / tile_m, n / tile_n, batch_grid};\n\n    auto kernel = batched_gemm_kernel<BLAS,\n                                      global_arrangement_c,\n                                      c_value_type,\n                                      c_value_type,\n                                      c_value_type,\n                                      decltype(device_pipeline)>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size>>>(\n        alpha, beta, d_c, m, n, global_ldc, batch_count, device_pipeline);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // ================================\n    // Copy results back and verify\n    // ================================\n\n    std::vector<c_value_type> host_c_result(c_total);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(host_c_result.data(), d_c, c_total * sizeof(c_value_type), hipMemcpyDeviceToHost));\n\n    const auto gemm_shape = std::make_tuple(m, n, k);\n    const auto gemm_ld    = std::make_tuple(global_lda, global_ldb, global_ldc);\n\n    bool all_correct = true;\n    for (unsigned b = 0; b < batch_count; ++b) {\n        std::vector<a_value_type> batch_a(host_a.begin() + b * m * k, host_a.begin() + (b + 1) * m * k);\n        std::vector<b_value_type> batch_b(host_b.begin() + b * k * n, host_b.begin() + (b + 1) * k * n);\n        std::vector<c_value_type> batch_c(host_c.begin() + b * m * n, host_c.begin() + (b + 1) * m * n);\n\n        auto reference = example::reference_gemm<BLAS>(gemm_shape, gemm_ld, alpha, batch_a, batch_b, beta, batch_c);\n\n        std::vector<c_value_type> batch_result(host_c_result.begin() + b * m * n,\n                                               host_c_result.begin() + (b + 1) * m * n);\n\n        if (not example::check_error<BLAS>(batch_result, reference)) {\n            std::cerr << \"Batch \" << b << \": FAILED\" << std::endl;\n            all_correct = false;\n        }\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_a));\n    CUDA_CHECK_AND_EXIT(hipFree(d_b));\n    CUDA_CHECK_AND_EXIT(hipFree(d_c));\n\n    if (all_correct) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    return 1;\n}\n\nstruct batched_gemm_pipeline_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return batched_gemm_pipeline<Arch, Modifier>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(batched_gemm_pipeline_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/19_gemm_batched/batched_gemm_pipeline.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates batched GEMM using the regular cuBLASDx API\n// (no pipelining, no K-dimension tiling).\n//\n//   C[b] = alpha * A[b] * B[b] + beta * C[b]   for b = 0 .. batch_count - 1\n//\n// Each per-batch matrix multiply has exactly the dimensions of the cuBLASDx\n// BLAS descriptor (M x N x K), so a single block computes one full GEMM for\n// one batch.  A grid-stride loop lets each block process multiple batches,\n// allowing the grid size to be smaller than batch_count for better SM occupancy.\n//\n// Global memory is represented as rank-3 tensors via\n// cublasdx::make_gmem_tensor_batched.  Per-batch 2D slices are extracted\n// with cublasdx::get_batch, which produces tensors with static layouts\n// compatible with cublasdx::copy.\n//\n// Compare with batched_gemm_pipeline.cu which uses the pipelining API and\n// tiles larger matrices across multiple blocks.\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// -------------------------------------------------------------------------\n// batched_gemm_kernel\n//\n// Blocks are mapped to batches via a grid-stride loop: each block processes\n// batches blockIdx.x, blockIdx.x + gridDim.x, ... until all batches are done.\n// This allows launching fewer blocks than batches for better occupancy control.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB, class GlobalTensorC>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void batched_gemm_kernel(typename BLAS::c_value_type const alpha,\n                             typename BLAS::c_value_type const beta,\n                             GlobalTensorA                     global_a,\n                             GlobalTensorB                     global_b,\n                             GlobalTensorC                     global_c,\n                             unsigned                          batch_count) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Slice shared memory for A and B only (C lives in registers via accumulator)\n    auto [a_shared, b_shared] =\n        cublasdx::shared_memory::slice<typename BLAS::a_value_type, typename BLAS::b_value_type>(\n            smem, alignment::a, BLAS::suggest_layout_smem_a(), alignment::b, BLAS::suggest_layout_smem_b());\n\n    // Grid-stride loop: each block processes multiple batches\n    for (unsigned batch = blockIdx.x; batch < batch_count; batch += gridDim.x) {\n        // Extract per-batch 2D slices with static layouts from rank-3 tensors\n        auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), batch);\n        auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), batch);\n        auto batch_c = cublasdx::get_batch(global_c, BLAS::get_layout_gmem_c(), batch);\n\n        // Copy A, B from global to shared memory\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::a>(batch_a, a_shared);\n        cublasdx::copy<BLAS, alignment::b>(batch_b, b_shared);\n        cublasdx::copy_wait();\n\n        // Execute GEMM: accumulator = A * B\n        auto accumulator = BLAS().execute(a_shared, b_shared);\n\n        // Epilogue: D = alpha * accumulator + beta * C (C loaded directly from gmem)\n        accumulator.axpby(alpha, beta, batch_c);\n    }\n}\n\n// -------------------------------------------------------------------------\n// batched_gemm  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint batched_gemm() {\n\n    // Per-batch matrix dimensions (must match the BLAS descriptor exactly)\n    constexpr unsigned m = 64;\n    constexpr unsigned n = 64;\n    constexpr unsigned k = 64;\n\n    // Number of independent GEMMs (batches)\n    const unsigned batch_count = 512;\n\n    // GEMM descriptor: one block computes the full M x N x K multiply\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    value_type alpha = value_type(1.1);\n    value_type beta  = value_type(1.2);\n\n    // ================================\n    // Allocate and fill device memory\n    // ================================\n\n    // Per-batch element counts must match the batch stride used by\n    // make_gmem_tensor_batched (= rows * cols, tight packing).\n    // Do NOT use global_memory_size_of<BLAS> here -- it may include smem padding.\n    constexpr auto a_per_batch = m * k;\n    constexpr auto b_per_batch = k * n;\n    constexpr auto c_per_batch = m * n;\n\n    const size_t a_total = a_per_batch * batch_count;\n    const size_t b_total = b_per_batch * batch_count;\n    const size_t c_total = c_per_batch * batch_count;\n\n    value_type* d_a = nullptr;\n    value_type* d_b = nullptr;\n    value_type* d_c = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_a, a_total * sizeof(value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_b, b_total * sizeof(value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&d_c, c_total * sizeof(value_type)));\n\n    auto host_a = example::get_random_data<value_type>(a_total, 1);\n    auto host_b = example::get_random_data<value_type>(b_total, 2);\n    auto host_c = example::get_random_data<value_type>(c_total, 3);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_a, host_a.data(), a_total * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_b, host_b.data(), b_total * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d_c, host_c.data(), c_total * sizeof(value_type), cudaMemcpyHostToDevice));\n\n    // ================================================================\n    // Build rank-3 global tensors using make_gmem_tensor_batched\n    //   A: (M, K, batch_count)  -- batch stride = M * K\n    //   B: (K, N, batch_count)  -- batch stride = K * N\n    //   C: (M, N, batch_count)  -- batch stride = M * N\n    // ================================================================\n\n    // Arrangements must match the BLAS descriptor defaults: A=row_major, B=col_major, C=col_major\n    auto global_a = cublasdx::make_gmem_tensor_batched<cublasdx::row_major>(d_a, m, k, batch_count);\n    auto global_b = cublasdx::make_gmem_tensor_batched<cublasdx::col_major>(d_b, k, n, batch_count);\n    auto global_c = cublasdx::make_gmem_tensor_batched<cublasdx::col_major>(d_c, m, n, batch_count);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n    using global_tensor_c_t = decltype(global_c);\n\n    // ================================\n    // Launch kernel with grid-stride loop\n    //\n    // Grid size is capped at the number of SMs so each SM gets one block.\n    // The grid-stride loop inside the kernel handles the remaining batches.\n    // ================================\n\n    auto shared_memory_size = cublasdx::get_shared_storage_size_ab<BLAS>();\n\n    auto kernel = batched_gemm_kernel<BLAS, global_tensor_a_t, global_tensor_b_t, global_tensor_c_t>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(cudaDeviceGetAttribute(&num_sms, cudaDevAttrMultiProcessorCount, 0));\n    const unsigned grid_size = std::min(batch_count, static_cast<unsigned>(num_sms));\n\n    kernel<<<grid_size, BLAS::block_dim, shared_memory_size>>>(alpha, beta, global_a, global_b, global_c, batch_count);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // ================================\n    // Copy results back and verify\n    // ================================\n\n    std::vector<value_type> host_c_result(c_total);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(host_c_result.data(), d_c, c_total * sizeof(value_type), cudaMemcpyDeviceToHost));\n\n    bool all_correct = true;\n    for (unsigned b = 0; b < batch_count; ++b) {\n        std::vector<value_type> batch_a(host_a.begin() + b * a_per_batch, host_a.begin() + (b + 1) * a_per_batch);\n        std::vector<value_type> batch_b(host_b.begin() + b * b_per_batch, host_b.begin() + (b + 1) * b_per_batch);\n        std::vector<value_type> batch_c(host_c.begin() + b * c_per_batch, host_c.begin() + (b + 1) * c_per_batch);\n\n        auto reference = example::reference_gemm<BLAS>(alpha, batch_a, batch_b, beta, batch_c);\n\n        std::vector<value_type> batch_result(host_c_result.begin() + b * c_per_batch,\n                                             host_c_result.begin() + (b + 1) * c_per_batch);\n\n        if (not example::check_error<BLAS>(batch_result, reference)) {\n            std::cerr << \"Batch \" << b << \": FAILED\" << std::endl;\n            all_correct = false;\n        }\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_a));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_b));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_c));\n\n    if (all_correct) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    return 1;\n}\n\nstruct batched_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return batched_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(batched_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates batched GEMM using the regular cuBLASDx API\n// (no pipelining, no K-dimension tiling).\n//\n//   C[b] = alpha * A[b] * B[b] + beta * C[b]   for b = 0 .. batch_count - 1\n//\n// Each per-batch matrix multiply has exactly the dimensions of the cuBLASDx\n// BLAS descriptor (M x N x K), so a single block computes one full GEMM for\n// one batch.  A grid-stride loop lets each block process multiple batches,\n// allowing the grid size to be smaller than batch_count for better SM occupancy.\n//\n// Global memory is represented as rank-3 tensors via\n// cublasdx::make_gmem_tensor_batched.  Per-batch 2D slices are extracted\n// with cublasdx::get_batch, which produces tensors with static layouts\n// compatible with cublasdx::copy.\n//\n// Compare with batched_gemm_pipeline.cu which uses the pipelining API and\n// tiles larger matrices across multiple blocks.\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// -------------------------------------------------------------------------\n// batched_gemm_kernel\n//\n// Blocks are mapped to batches via a grid-stride loop: each block processes\n// batches blockIdx.x, blockIdx.x + gridDim.x, ... until all batches are done.\n// This allows launching fewer blocks than batches for better occupancy control.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB, class GlobalTensorC>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void batched_gemm_kernel(typename BLAS::c_value_type const alpha,\n                             typename BLAS::c_value_type const beta,\n                             GlobalTensorA                     global_a,\n                             GlobalTensorB                     global_b,\n                             GlobalTensorC                     global_c,\n                             unsigned                          batch_count) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Slice shared memory for A and B only (C lives in registers via accumulator)\n    auto [a_shared, b_shared] =\n        cublasdx::shared_memory::slice<typename BLAS::a_value_type, typename BLAS::b_value_type>(\n            smem, alignment::a, BLAS::suggest_layout_smem_a(), alignment::b, BLAS::suggest_layout_smem_b());\n\n    // Grid-stride loop: each block processes multiple batches\n    for (unsigned batch = blockIdx.x; batch < batch_count; batch += gridDim.x) {\n        // Extract per-batch 2D slices with static layouts from rank-3 tensors\n        auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), batch);\n        auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), batch);\n        auto batch_c = cublasdx::get_batch(global_c, BLAS::get_layout_gmem_c(), batch);\n\n        // Copy A, B from global to shared memory\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::a>(batch_a, a_shared);\n        cublasdx::copy<BLAS, alignment::b>(batch_b, b_shared);\n        cublasdx::copy_wait();\n\n        // Execute GEMM: accumulator = A * B\n        auto accumulator = BLAS().execute(a_shared, b_shared);\n\n        // Epilogue: D = alpha * accumulator + beta * C (C loaded directly from gmem)\n        accumulator.axpby(alpha, beta, batch_c);\n    }\n}\n\n// -------------------------------------------------------------------------\n// batched_gemm  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint batched_gemm() {\n\n    // Per-batch matrix dimensions (must match the BLAS descriptor exactly)\n    constexpr unsigned m = 64;\n    constexpr unsigned n = 64;\n    constexpr unsigned k = 64;\n\n    // Number of independent GEMMs (batches)\n    const unsigned batch_count = 512;\n\n    // GEMM descriptor: one block computes the full M x N x K multiply\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    value_type alpha = value_type(1.1);\n    value_type beta  = value_type(1.2);\n\n    // ================================\n    // Allocate and fill device memory\n    // ================================\n\n    // Per-batch element counts must match the batch stride used by\n    // make_gmem_tensor_batched (= rows * cols, tight packing).\n    // Do NOT use global_memory_size_of<BLAS> here -- it may include smem padding.\n    constexpr auto a_per_batch = m * k;\n    constexpr auto b_per_batch = k * n;\n    constexpr auto c_per_batch = m * n;\n\n    const size_t a_total = a_per_batch * batch_count;\n    const size_t b_total = b_per_batch * batch_count;\n    const size_t c_total = c_per_batch * batch_count;\n\n    value_type* d_a = nullptr;\n    value_type* d_b = nullptr;\n    value_type* d_c = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_a, a_total * sizeof(value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_b, b_total * sizeof(value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&d_c, c_total * sizeof(value_type)));\n\n    auto host_a = example::get_random_data<value_type>(a_total, 1);\n    auto host_b = example::get_random_data<value_type>(b_total, 2);\n    auto host_c = example::get_random_data<value_type>(c_total, 3);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_a, host_a.data(), a_total * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_b, host_b.data(), b_total * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d_c, host_c.data(), c_total * sizeof(value_type), hipMemcpyHostToDevice));\n\n    // ================================================================\n    // Build rank-3 global tensors using make_gmem_tensor_batched\n    //   A: (M, K, batch_count)  -- batch stride = M * K\n    //   B: (K, N, batch_count)  -- batch stride = K * N\n    //   C: (M, N, batch_count)  -- batch stride = M * N\n    // ================================================================\n\n    // Arrangements must match the BLAS descriptor defaults: A=row_major, B=col_major, C=col_major\n    auto global_a = cublasdx::make_gmem_tensor_batched<cublasdx::row_major>(d_a, m, k, batch_count);\n    auto global_b = cublasdx::make_gmem_tensor_batched<cublasdx::col_major>(d_b, k, n, batch_count);\n    auto global_c = cublasdx::make_gmem_tensor_batched<cublasdx::col_major>(d_c, m, n, batch_count);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n    using global_tensor_c_t = decltype(global_c);\n\n    // ================================\n    // Launch kernel with grid-stride loop\n    //\n    // Grid size is capped at the number of SMs so each SM gets one block.\n    // The grid-stride loop inside the kernel handles the remaining batches.\n    // ================================\n\n    auto shared_memory_size = cublasdx::get_shared_storage_size_ab<BLAS>();\n\n    auto kernel = batched_gemm_kernel<BLAS, global_tensor_a_t, global_tensor_b_t, global_tensor_c_t>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(hipDeviceGetAttribute(&num_sms, hipDeviceAttributeMultiprocessorCount, 0));\n    const unsigned grid_size = std::min(batch_count, static_cast<unsigned>(num_sms));\n\n    kernel<<<grid_size, BLAS::block_dim, shared_memory_size>>>(alpha, beta, global_a, global_b, global_c, batch_count);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // ================================\n    // Copy results back and verify\n    // ================================\n\n    std::vector<value_type> host_c_result(c_total);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(host_c_result.data(), d_c, c_total * sizeof(value_type), hipMemcpyDeviceToHost));\n\n    bool all_correct = true;\n    for (unsigned b = 0; b < batch_count; ++b) {\n        std::vector<value_type> batch_a(host_a.begin() + b * a_per_batch, host_a.begin() + (b + 1) * a_per_batch);\n        std::vector<value_type> batch_b(host_b.begin() + b * b_per_batch, host_b.begin() + (b + 1) * b_per_batch);\n        std::vector<value_type> batch_c(host_c.begin() + b * c_per_batch, host_c.begin() + (b + 1) * c_per_batch);\n\n        auto reference = example::reference_gemm<BLAS>(alpha, batch_a, batch_b, beta, batch_c);\n\n        std::vector<value_type> batch_result(host_c_result.begin() + b * c_per_batch,\n                                             host_c_result.begin() + (b + 1) * c_per_batch);\n\n        if (not example::check_error<BLAS>(batch_result, reference)) {\n            std::cerr << \"Batch \" << b << \": FAILED\" << std::endl;\n            all_correct = false;\n        }\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_a));\n    CUDA_CHECK_AND_EXIT(hipFree(d_b));\n    CUDA_CHECK_AND_EXIT(hipFree(d_c));\n\n    if (all_correct) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    return 1;\n}\n\nstruct batched_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return batched_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(batched_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/19_gemm_batched/batched_gemm.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates solving a TRSM problem where the triangular matrix A\n// is used in its conjugate-transpose form via cublasdx::conj_transpose_view:\n//\n//   A^H * X = B\n//\n// where A is a lower-triangular, non-unit matrix of complex single-precision values\n// of size (M x M), and B / X are (M x N) complex matrices.  All matrices use\n// column-major layout.\n//\n// The TRSM descriptor is defined for the un-transposed storage of A (lower-triangular,\n// col-major).  At execute time, cublasdx::conj_transpose_view(smem_a) presents A^H to\n// the solver without any data copy or transposition in memory.\n//\n// Correctness is verified against a cuBLAS reference using CUBLAS_OP_C.\n\n#include <iomanip>\n#include <iostream>\n#include <random>\n#include <vector>\n\n#include <cublas_v2.h>\n#include <cublasdx/database/trsm/trsm_db.hpp>\n\n#include \"../common/trsm_common.hpp\"\n\n// -------------------------------------------------------------------------\n// trsm_conj_transpose_kernel\n//\n// Each block processes one TRSM instance.\n// Applies conj_transpose_view to smem_a so the solver operates on A^H.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void trsm_conj_transpose_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using T         = typename BLAS::a_value_type;\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    const unsigned total_blocks = cute::size<2>(global_a) / BLAS::batches_per_block;\n    if (blockIdx.x >= total_blocks) {\n        return;\n    }\n\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), blockIdx.x);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), blockIdx.x);\n\n    auto [smem_tensor_a, smem_tensor_b] = cublasdx::shared_memory::slice<T, T>(\n        smem, alignment::a, BLAS::get_layout_smem_a(), alignment::b, BLAS::get_layout_smem_b());\n\n    cublasdx::copy<BLAS, alignment::a>(batch_a, smem_tensor_a);\n    cublasdx::copy<BLAS, alignment::b>(batch_b, smem_tensor_b);\n    cublasdx::copy_wait();\n\n    // Solve A^H * X = B by passing the conjugate-transpose view of A.\n    BLAS {}.execute(cublasdx::conj_transpose_view(smem_tensor_a), smem_tensor_b);\n    __syncthreads();\n\n    cublasdx::copy<BLAS, alignment::b>(smem_tensor_b, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// trsm_conj_transpose  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint trsm_conj_transpose() {\n    constexpr unsigned M = 32;\n    constexpr unsigned N = 8;\n\n    using T                  = cublasdx::complex<float>;\n    constexpr auto Side      = cublasdx::side::left;\n    constexpr auto StoreFill = cublasdx::fill_mode::lower; // A's storage fill mode\n    constexpr auto ViewFill  = cublasdx::fill_mode::upper; // A^H is upper-triangular\n    constexpr auto Diag      = cublasdx::diag::non_unit;\n    constexpr auto ArrA      = cublasdx::col_major;\n    constexpr auto ArrB      = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 512;\n    constexpr unsigned dim_a       = M; // left-side: A is M x M\n\n    constexpr unsigned BPB         = 1;\n    constexpr unsigned num_threads = 64;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor.\n    // The descriptor's FillMode describes the viewed matrix A^H (upper-triangular).\n    // The conjugate transpose is applied at execute time via conj_transpose_view.\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<float>() +\n        cublasdx::Type<cublasdx::type::complex>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Block() +\n        cublasdx::BlockDim<num_threads>() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<ViewFill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>() +\n        cublasdx::BatchesPerBlock<BPB>());\n\n    const unsigned padded_batches = cute::round_up(num_batches, BPB);\n\n    // -----------------------------------------------------------------------\n    // Host data: random complex matrices.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto gen_complex = [](size_t n, float lo, float hi) {\n        std::vector<T>                        v(n);\n        std::mt19937                          rng(42);\n        std::uniform_real_distribution<float> dist(lo, hi);\n        for (auto& x : v) {\n            x = T {dist(rng), dist(rng)};\n        }\n        return v;\n    };\n\n    auto h_A = gen_complex(padded_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = gen_complex(padded_batches * b_per_batch, -1.f, 1.f);\n\n    // Make A diagonally dominant so the solve is well-conditioned.\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, padded_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers and global tensors\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, padded_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, padded_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Kernel configuration\n    // -----------------------------------------------------------------------\n    const unsigned smem_bytes = cublasdx::make_shared_storage_calculator()\n                                    .add(cublasdx::alignment_of<BLAS>::a, sizeof(T), BLAS::get_layout_smem_a())\n                                    .add(cublasdx::alignment_of<BLAS>::b, sizeof(T), BLAS::get_layout_smem_b())\n                                    .get();\n\n    auto kernel = trsm_conj_transpose_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n\n    const unsigned num_blocks = padded_batches / BPB;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // -----------------------------------------------------------------------\n    // Correctness run\n    // -----------------------------------------------------------------------\n    kernel<<<num_blocks, num_threads, smem_bytes, stream>>>(global_a, global_b);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, cudaMemcpyDeviceToHost));\n\n    // cuBLAS reference: same storage, but op = CUBLAS_OP_C (conjugate transpose).\n    constexpr bool is_col_a = (ArrA == cublasdx::col_major);\n    constexpr bool is_col_b = (ArrB == cublasdx::col_major);\n    constexpr bool is_left  = (Side == cublasdx::side::left);\n    constexpr bool is_lower = (StoreFill == cublasdx::fill_mode::lower);\n    constexpr bool is_unit  = (Diag == cublasdx::diag::unit);\n\n    auto [h_B_ref, _] = example::reference_trsm<T>(\n        h_A, h_B_orig, M, N, num_batches, is_left, is_lower, is_unit, is_col_a, is_col_b, CUBLAS_OP_C, 0, 0, stream);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    std::cout << \"cuBLASDx TRSM Conjugate-Transpose Example\" << std::endl;\n    std::cout << \"  Solving A^H * X = B\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Precision=complex<float>\"\n              << \"  StoreFill=lower  ViewFill=upper  Diag=non_unit\" << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << std::fixed << std::setprecision(6) << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    return (l2_err < 0.001) ? 0 : 1;\n}\n\nstruct trsm_conj_transpose_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return trsm_conj_transpose<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(trsm_conj_transpose_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates solving a TRSM problem where the triangular matrix A\n// is used in its conjugate-transpose form via cublasdx::conj_transpose_view:\n//\n//   A^H * X = B\n//\n// where A is a lower-triangular, non-unit matrix of complex single-precision values\n// of size (M x M), and B / X are (M x N) complex matrices.  All matrices use\n// column-major layout.\n//\n// The TRSM descriptor is defined for the un-transposed storage of A (lower-triangular,\n// col-major).  At execute time, cublasdx::conj_transpose_view(smem_a) presents A^H to\n// the solver without any data copy or transposition in memory.\n//\n// Correctness is verified against a cuBLAS reference using CUBLAS_OP_C.\n\n#include \"hip/hip_runtime.h\"\n#include <iomanip>\n#include <iostream>\n#include <random>\n#include <vector>\n\n#include <hipblas.h>\n#include <cublasdx/database/trsm/trsm_db.hpp>\n\n#include \"../common/trsm_common.hpp\"\n\n// -------------------------------------------------------------------------\n// trsm_conj_transpose_kernel\n//\n// Each block processes one TRSM instance.\n// Applies conj_transpose_view to smem_a so the solver operates on A^H.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void trsm_conj_transpose_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using T         = typename BLAS::a_value_type;\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    const unsigned total_blocks = cute::size<2>(global_a) / BLAS::batches_per_block;\n    if (blockIdx.x >= total_blocks) {\n        return;\n    }\n\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), blockIdx.x);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), blockIdx.x);\n\n    auto [smem_tensor_a, smem_tensor_b] = cublasdx::shared_memory::slice<T, T>(\n        smem, alignment::a, BLAS::get_layout_smem_a(), alignment::b, BLAS::get_layout_smem_b());\n\n    cublasdx::copy<BLAS, alignment::a>(batch_a, smem_tensor_a);\n    cublasdx::copy<BLAS, alignment::b>(batch_b, smem_tensor_b);\n    cublasdx::copy_wait();\n\n    // Solve A^H * X = B by passing the conjugate-transpose view of A.\n    BLAS {}.execute(cublasdx::conj_transpose_view(smem_tensor_a), smem_tensor_b);\n    __syncthreads();\n\n    cublasdx::copy<BLAS, alignment::b>(smem_tensor_b, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// trsm_conj_transpose  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint trsm_conj_transpose() {\n    constexpr unsigned M = 32;\n    constexpr unsigned N = 8;\n\n    using T                  = cublasdx::complex<float>;\n    constexpr auto Side      = cublasdx::side::left;\n    constexpr auto StoreFill = cublasdx::fill_mode::lower; // A's storage fill mode\n    constexpr auto ViewFill  = cublasdx::fill_mode::upper; // A^H is upper-triangular\n    constexpr auto Diag      = cublasdx::diag::non_unit;\n    constexpr auto ArrA      = cublasdx::col_major;\n    constexpr auto ArrB      = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 512;\n    constexpr unsigned dim_a       = M; // left-side: A is M x M\n\n    constexpr unsigned BPB         = 1;\n    constexpr unsigned num_threads = 64;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor.\n    // The descriptor's FillMode describes the viewed matrix A^H (upper-triangular).\n    // The conjugate transpose is applied at execute time via conj_transpose_view.\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<float>() +\n        cublasdx::Type<cublasdx::type::complex>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Block() +\n        cublasdx::BlockDim<num_threads>() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<ViewFill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>() +\n        cublasdx::BatchesPerBlock<BPB>());\n\n    const unsigned padded_batches = cute::round_up(num_batches, BPB);\n\n    // -----------------------------------------------------------------------\n    // Host data: random complex matrices.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto gen_complex = [](size_t n, float lo, float hi) {\n        std::vector<T>                        v(n);\n        std::mt19937                          rng(42);\n        std::uniform_real_distribution<float> dist(lo, hi);\n        for (auto& x : v) {\n            x = T {dist(rng), dist(rng)};\n        }\n        return v;\n    };\n\n    auto h_A = gen_complex(padded_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = gen_complex(padded_batches * b_per_batch, -1.f, 1.f);\n\n    // Make A diagonally dominant so the solve is well-conditioned.\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, padded_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers and global tensors\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, padded_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, padded_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Kernel configuration\n    // -----------------------------------------------------------------------\n    const unsigned smem_bytes = cublasdx::make_shared_storage_calculator()\n                                    .add(cublasdx::alignment_of<BLAS>::a, sizeof(T), BLAS::get_layout_smem_a())\n                                    .add(cublasdx::alignment_of<BLAS>::b, sizeof(T), BLAS::get_layout_smem_b())\n                                    .get();\n\n    auto kernel = trsm_conj_transpose_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n\n    const unsigned num_blocks = padded_batches / BPB;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // -----------------------------------------------------------------------\n    // Correctness run\n    // -----------------------------------------------------------------------\n    kernel<<<num_blocks, num_threads, smem_bytes, stream>>>(global_a, global_b);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, hipMemcpyDeviceToHost));\n\n    // cuBLAS reference: same storage, but op = CUBLAS_OP_C (conjugate transpose).\n    constexpr bool is_col_a = (ArrA == cublasdx::col_major);\n    constexpr bool is_col_b = (ArrB == cublasdx::col_major);\n    constexpr bool is_left  = (Side == cublasdx::side::left);\n    constexpr bool is_lower = (StoreFill == cublasdx::fill_mode::lower);\n    constexpr bool is_unit  = (Diag == cublasdx::diag::unit);\n\n    auto [h_B_ref, _] = example::reference_trsm<T>(\n        h_A, h_B_orig, M, N, num_batches, is_left, is_lower, is_unit, is_col_a, is_col_b, HIPBLAS_OP_C, 0, 0, stream);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    std::cout << \"cuBLASDx TRSM Conjugate-Transpose Example\" << std::endl;\n    std::cout << \"  Solving A^H * X = B\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Precision=complex<float>\"\n              << \"  StoreFill=lower  ViewFill=upper  Diag=non_unit\" << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << std::fixed << std::setprecision(6) << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    return (l2_err < 0.001) ? 0 : 1;\n}\n\nstruct trsm_conj_transpose_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return trsm_conj_transpose<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(trsm_conj_transpose_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/18_tensor_transform/trsm_conj_transpose.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates a complex GEMM where matrix A is used in its\n// conjugate-transpose form:\n//\n//   C = alpha * A^H * B + beta * C\n//\n// where A is an (M x M) complex single-precision matrix, B is (M x N), and\n// C is (M x N).  All matrices use column-major layout.\n//\n// The conjugate-transpose is achieved in two steps:\n//   1. cublasdx::transpose_view(smem_a) swaps the first two tensor modes,\n//      presenting A^T to the executor without any data movement.\n//   2. cublasdx::conjugate{} is passed as an ALoadOp, applying element-wise\n//      conjugation during the GEMM's internal copy from shared memory to\n//      registers.\n//\n// Together these give: conj(A^T) = A^H.\n//\n// Note: M must equal K so that A is square.  transpose_view swaps the first\n// two modes of the tensor, so using it on a non-square matrix would change the\n// logical dimensions and break the GEMM size contract.\n//\n// Correctness is verified against a cuBLAS reference that receives the\n// host-side conjugate-transposed A matrix.\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// -------------------------------------------------------------------------\n// gemm_conj_transpose_kernel\n//\n// Each block performs a single GEMM: C = alpha * A^H * B + beta * C.\n// transpose_view is applied to A in shared memory and conjugate{} is used\n// as a load-time transform so the GEMM operates on A^H.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_conj_transpose_kernel(const ValueType* a,\n                                                                                          const ValueType* b,\n                                                                                          const ValueType* c,\n                                                                                          const ValueType  alpha,\n                                                                                          const ValueType  beta,\n                                                                                          ValueType*       output) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Compute C = alpha * A^H * B + beta * C:\n    //   transpose_view(smem_a) presents A^T (layout change only, no data copy),\n    //   cublasdx::conjugate{} as ALoadOp applies element-wise conjugation.\n    BLAS().execute(alpha,\n                   cublasdx::conj_transpose_view(a_shared_tensor),\n                   b_shared_tensor,\n                   beta,\n                   cublasdx::transpose_view(c_shared_tensor));\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// -------------------------------------------------------------------------\n// gemm_conj_transpose  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint gemm_conj_transpose() {\n    // M == K so that A is square and transpose_view preserves tensor shape.\n    constexpr unsigned int square_size = 32;\n\n    // All sizes are the same to support transpose_view and conj_transpose_view\n    constexpr unsigned int m = square_size;\n    constexpr unsigned int n = square_size;\n    constexpr unsigned int k = square_size;\n\n    // GEMM definition: C = alpha * A * B + beta * C\n    // At execute time we pass transpose_view(A) with a conjugate load op,\n    // effectively computing C = alpha * A^H * B + beta * C.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() +\n                          cublasdx::Precision<float>() +\n                          cublasdx::Type<cublasdx::type::complex>() +\n                          cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() +\n                          cublasdx::BlockDim<256>() +\n                          cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for A, B, C, and output\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    value_type* inputs;\n    value_type* output;\n    auto        inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto        inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + global_a_size;\n    value_type* c     = b + global_b_size;\n    value_type  alpha = value_type(1.0, 0.5);\n    value_type  beta  = value_type(2.0, 0.0);\n\n    // Fill A, B, C with random complex values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Set shared memory and launch kernel\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(gemm_conj_transpose_kernel<BLAS>,\n                                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    gemm_conj_transpose_kernel<BLAS>\n        <<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // -----------------------------------------------------------------------\n    // Reference: compute A^H on the host and pass it to reference_gemm.\n    //\n    // A is col_major (m x k): element (i,j) is at index [i + j*m].\n    // A^H(i,j) = conj(A(j,i)) = conj(host_a[j + i*m]).\n    // Since m == k, A^H is also (m x k) col_major.\n    // -----------------------------------------------------------------------\n    std::vector<value_type> host_a_ct(global_a_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < k; ++j) {\n            auto val             = host_a[j + i * m];\n            host_a_ct[i + j * m] = value_type(val.real(), -val.imag());\n        }\n    }\n \n    // Transpose C\n    std::vector<value_type> host_c_ct(global_c_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < n; ++j) {\n            host_c_ct[i + j * m] = host_c[j + i * m];\n        }\n    }\n\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a_ct, host_b, beta, host_c_ct);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Transpose results\n    std::vector<value_type> reference_host_output_ct(global_c_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < n; ++j) {\n            reference_host_output_ct[i + j * m] = reference_host_output[j + i * m];\n        }\n    }\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output_ct)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct gemm_conj_transpose_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_conj_transpose<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_conj_transpose_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This example demonstrates a complex GEMM where matrix A is used in its\n// conjugate-transpose form:\n//\n//   C = alpha * A^H * B + beta * C\n//\n// where A is an (M x M) complex single-precision matrix, B is (M x N), and\n// C is (M x N).  All matrices use column-major layout.\n//\n// The conjugate-transpose is achieved in two steps:\n//   1. cublasdx::transpose_view(smem_a) swaps the first two tensor modes,\n//      presenting A^T to the executor without any data movement.\n//   2. cublasdx::conjugate{} is passed as an ALoadOp, applying element-wise\n//      conjugation during the GEMM's internal copy from shared memory to\n//      registers.\n//\n// Together these give: conj(A^T) = A^H.\n//\n// Note: M must equal K so that A is square.  transpose_view swaps the first\n// two modes of the tensor, so using it on a non-square matrix would change the\n// logical dimensions and break the GEMM size contract.\n//\n// Correctness is verified against a cuBLAS reference that receives the\n// host-side conjugate-transposed A matrix.\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// -------------------------------------------------------------------------\n// gemm_conj_transpose_kernel\n//\n// Each block performs a single GEMM: C = alpha * A^H * B + beta * C.\n// transpose_view is applied to A in shared memory and conjugate{} is used\n// as a load-time transform so the GEMM operates on A^H.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_conj_transpose_kernel(const ValueType* a,\n                                                                                          const ValueType* b,\n                                                                                          const ValueType* c,\n                                                                                          const ValueType  alpha,\n                                                                                          const ValueType  beta,\n                                                                                          ValueType*       output) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Compute C = alpha * A^H * B + beta * C:\n    //   transpose_view(smem_a) presents A^T (layout change only, no data copy),\n    //   cublasdx::conjugate{} as ALoadOp applies element-wise conjugation.\n    BLAS().execute(alpha,\n                   cublasdx::conj_transpose_view(a_shared_tensor),\n                   b_shared_tensor,\n                   beta,\n                   cublasdx::transpose_view(c_shared_tensor));\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// -------------------------------------------------------------------------\n// gemm_conj_transpose  - main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint gemm_conj_transpose() {\n    // M == K so that A is square and transpose_view preserves tensor shape.\n    constexpr unsigned int square_size = 32;\n\n    // All sizes are the same to support transpose_view and conj_transpose_view\n    constexpr unsigned int m = square_size;\n    constexpr unsigned int n = square_size;\n    constexpr unsigned int k = square_size;\n\n    // GEMM definition: C = alpha * A * B + beta * C\n    // At execute time we pass transpose_view(A) with a conjugate load op,\n    // effectively computing C = alpha * A^H * B + beta * C.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() +\n                          cublasdx::Precision<float>() +\n                          cublasdx::Type<cublasdx::type::complex>() +\n                          cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() +\n                          cublasdx::BlockDim<256>() +\n                          cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for A, B, C, and output\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    value_type* inputs;\n    value_type* output;\n    auto        inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto        inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + global_a_size;\n    value_type* c     = b + global_b_size;\n    value_type  alpha = value_type(1.0, 0.5);\n    value_type  beta  = value_type(2.0, 0.0);\n\n    // Fill A, B, C with random complex values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Set shared memory and launch kernel\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_conj_transpose_kernel<BLAS>),\n                                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    gemm_conj_transpose_kernel<BLAS>\n        <<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // -----------------------------------------------------------------------\n    // Reference: compute A^H on the host and pass it to reference_gemm.\n    //\n    // A is col_major (m x k): element (i,j) is at index [i + j*m].\n    // A^H(i,j) = conj(A(j,i)) = conj(host_a[j + i*m]).\n    // Since m == k, A^H is also (m x k) col_major.\n    // -----------------------------------------------------------------------\n    std::vector<value_type> host_a_ct(global_a_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < k; ++j) {\n            auto val             = host_a[j + i * m];\n            host_a_ct[i + j * m] = value_type(val.real(), -val.imag());\n        }\n    }\n \n    // Transpose C\n    std::vector<value_type> host_c_ct(global_c_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < n; ++j) {\n            host_c_ct[i + j * m] = host_c[j + i * m];\n        }\n    }\n\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a_ct, host_b, beta, host_c_ct);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Transpose results\n    std::vector<value_type> reference_host_output_ct(global_c_size);\n    for (unsigned int i = 0; i < m; ++i) {\n        for (unsigned int j = 0; j < n; ++j) {\n            reference_host_output_ct[i + j * m] = reference_host_output[j + i * m];\n        }\n    }\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output_ct)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct gemm_conj_transpose_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_conj_transpose<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_conj_transpose_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/18_tensor_transform/gemm_conj_transpose.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n#include <type_traits>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS1, class BLAS2, class ValueType = example::uniform_value_type_t<BLAS1>>\n__launch_bounds__(BLAS1::max_threads_per_block) //\n    __global__                                  //\n    void gemm_kernel(const ValueType  alpha1,\n                     const ValueType* a,\n                     const ValueType* b,\n                     const ValueType  beta1,\n                     const ValueType* c,\n                     const ValueType  alpha2,\n                     const ValueType* d,\n                     const ValueType  beta2,\n                     const ValueType* f,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    static_assert(std::is_same_v<value_type, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    // Matrix C is the first in shared memory, because it's reused in the 2nd GEMM. Moreover,\n    // matrices A and B might have different sizes than F and D.\n    value_type* smem_c = reinterpret_cast<value_type*>(smem);\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + BLAS1::c_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS1::c_size + BLAS1::a_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS1::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS1::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS1::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS1::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS1::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS1::get_layout_smem_c());\n\n    using alignment1 = cublasdx::alignment_of<BLAS1>;\n    cublasdx::copy<BLAS1, alignment1::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS1().execute(alpha1, a_shared_tensor, b_shared_tensor, beta1, c_shared_tensor);\n    __syncthreads();\n\n    static_assert((BLAS1::c_size == BLAS2::a_size), \"The sizes of C matrix are different in BLAS1 and BLAS2\");\n    value_type* smem_d = smem_c + BLAS2::a_size;\n    value_type* smem_f = smem_c + BLAS2::a_size + BLAS2::b_size;\n\n    auto d_global_tensor = cublasdx::make_tensor(d, BLAS2::get_layout_gmem_b());\n    auto f_global_tensor = cublasdx::make_tensor(f, BLAS2::get_layout_gmem_c());\n\n    auto d_shared_tensor = cublasdx::make_tensor(smem_d, BLAS2::get_layout_smem_b());\n    auto f_shared_tensor = cublasdx::make_tensor(smem_f, BLAS2::get_layout_smem_c());\n\n    using alignment2 = cublasdx::alignment_of<BLAS2>;\n    cublasdx::copy<BLAS2, alignment2::b>(d_global_tensor, d_shared_tensor);\n    cublasdx::copy<BLAS2, alignment2::c>(f_global_tensor, f_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS2().execute(alpha2, c_shared_tensor, d_shared_tensor, beta2, f_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS2::get_layout_gmem_c());\n    cublasdx::copy<BLAS2, alignment2::c>(f_shared_tensor, out_global_tensor);\n}\n\n// This is an example of two fp16 general matrix-matrix multiplications (GEMM) fused together\n// and performed in one kernel in a single CUDA block:\n//\n//             1) C = alpha1 * (A * B) + beta1 * C\n//             2) F = alpha2 * (C * D) + beta2 * F\n//\n// * A, B, C, D and F are matrices containing real half precision floating-point values.\n// * (alpha1, beta1) and (alpha2, beta2) and real half precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix F (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// Notes:\n// * Both GEMM operations use the same number of threads, however, it's not a requirement.\n// * It's important that the dimensions of the first and the 2nd GEMM are set in such a way that\n//   the C matrix has the same dimensions in both operations.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m1, n1, k1 define the dimensions of matrices A, B, and C\n    constexpr unsigned int m1 = 64;\n    constexpr unsigned int n1 = 64;\n    constexpr unsigned int k1 = 64;\n\n    // Parameters m2, n2, k2 define the dimensions of matrices C, D and F\n    // Note: (m1, n1) and (m2, k2) must be equal as describe the same matrix (matrix C)\n    constexpr unsigned int m2 = m1;\n    constexpr unsigned int n2 = 128;\n    constexpr unsigned int k2 = n1;\n\n    // The logical dimensions of matrix A are: [m1, k1] (m rows, k columns)\n    // The logical dimensions of matrix B are: [k1, n1]\n    // The logical dimensions of matrix C are: [m1, n1]\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::col_major;\n    constexpr auto c_arrangement = cublasdx::col_major;\n\n    // The logical dimensions of matrix C are: [m2, k2] == [m1, n1]\n    // The logical dimensions of matrix D are: [k2, n2]\n    // The logical dimensions of matrix F are: [m2, n2]\n    constexpr auto d_arrangement = cublasdx::col_major;\n    constexpr auto f_arrangement = cublasdx::col_major;\n\n    // Use the same block size for both GEMM operations, so BLAS1::block_dim == BLAS2::block_dim which\n    // simplifies the example.\n    constexpr unsigned int block_size = 128;\n\n    using BLAS1 = decltype(cublasdx::Size<m1, n1, k1>() + cublasdx::Precision<__half>() +\n                           cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                           cublasdx::Arrangement<a_arrangement, b_arrangement, c_arrangement>() + cublasdx::Block() +\n                           cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n    using BLAS2 = decltype(cublasdx::Size<m2, n2, k2>() + cublasdx::Precision<__half>() +\n                           cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                           cublasdx::Arrangement<c_arrangement, d_arrangement, f_arrangement>() + cublasdx::Block() +\n                           cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS1>;\n\n    // alpha and beta for the first GEMM\n    value_type alpha1 = 1.0;\n    value_type beta1  = 0.0;\n\n    // alpha and beta for the 2nd GEMM\n    value_type alpha2 = 1.0;\n    value_type beta2  = 1.0;\n\n    // Allocate managed memory for a, b, c, d, f and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a1_size = example::global_memory_size_of<BLAS1>::a_size;\n    constexpr auto global_b1_size = example::global_memory_size_of<BLAS1>::b_size;\n    constexpr auto global_c1_size = example::global_memory_size_of<BLAS1>::c_size;\n    constexpr auto global_b2_size = example::global_memory_size_of<BLAS2>::b_size;\n    constexpr auto global_c2_size = example::global_memory_size_of<BLAS2>::c_size;\n\n    auto inputs_size       = global_a1_size + global_b1_size + global_c1_size + global_b2_size + global_c2_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, global_c2_size * sizeof(value_type)));\n\n    value_type* a = inputs;\n    value_type* b = a + (global_a1_size);\n    value_type* c = b + (global_b1_size); // C matrix for BLAS1, A matrix for BLAS2\n    value_type* d = c + (global_c1_size); // D is B matrix for BLAS2\n    value_type* f = d + (global_b2_size); // F is C matrix for BLAS2\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a1_size);\n    auto host_b = example::get_random_data<value_type>(global_b1_size);\n    auto host_c = example::get_random_data<value_type>(global_c1_size);\n    auto host_d = example::get_random_data<value_type>(global_b2_size);\n    auto host_f = example::get_random_data<value_type>(global_c2_size);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d, host_d.data(), global_b2_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(f, host_f.data(), global_c2_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    const auto shared_memory =\n        std::max<size_t>(cublasdx::get_shared_storage_size<BLAS1>(), cublasdx::get_shared_storage_size<BLAS2>());\n    CUDA_CHECK_AND_EXIT(\n        cudaFuncSetAttribute(gemm_kernel<BLAS1, BLAS2>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory));\n\n    // Execute kernel\n    gemm_kernel<BLAS1, BLAS2>\n        <<<1, BLAS1::block_dim, shared_memory>>>(alpha1, a, b, beta1, c, alpha2, d, beta2, f, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c2_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c2_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    // 1st GEMM\n    auto blas1_reference_host_output = example::reference_gemm<BLAS1>(alpha1, host_a, host_b, beta1, host_c);\n    // 2nd GEMM\n    std::vector<value_type> blas2_host_c(blas1_reference_host_output.size());\n    blas2_host_c.assign(blas1_reference_host_output.begin(), blas1_reference_host_output.end());\n    auto reference_host_output = example::reference_gemm<BLAS2>(alpha2, blas2_host_c, host_d, beta2, host_f);\n\n    // Check against reference\n    if (example::check_error<BLAS2>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <type_traits>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS1, class BLAS2, class ValueType = example::uniform_value_type_t<BLAS1>>\n__launch_bounds__(BLAS1::max_threads_per_block) //\n    __global__                                  //\n    void gemm_kernel(const ValueType  alpha1,\n                     const ValueType* a,\n                     const ValueType* b,\n                     const ValueType  beta1,\n                     const ValueType* c,\n                     const ValueType  alpha2,\n                     const ValueType* d,\n                     const ValueType  beta2,\n                     const ValueType* f,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    static_assert(std::is_same_v<value_type, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    // Matrix C is the first in shared memory, because it's reused in the 2nd GEMM. Moreover,\n    // matrices A and B might have different sizes than F and D.\n    value_type* smem_c = reinterpret_cast<value_type*>(smem);\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + BLAS1::c_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS1::c_size + BLAS1::a_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS1::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS1::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS1::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS1::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS1::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS1::get_layout_smem_c());\n\n    using alignment1 = cublasdx::alignment_of<BLAS1>;\n    cublasdx::copy<BLAS1, alignment1::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS1().execute(alpha1, a_shared_tensor, b_shared_tensor, beta1, c_shared_tensor);\n    __syncthreads();\n\n    static_assert((BLAS1::c_size == BLAS2::a_size), \"The sizes of C matrix are different in BLAS1 and BLAS2\");\n    value_type* smem_d = smem_c + BLAS2::a_size;\n    value_type* smem_f = smem_c + BLAS2::a_size + BLAS2::b_size;\n\n    auto d_global_tensor = cublasdx::make_tensor(d, BLAS2::get_layout_gmem_b());\n    auto f_global_tensor = cublasdx::make_tensor(f, BLAS2::get_layout_gmem_c());\n\n    auto d_shared_tensor = cublasdx::make_tensor(smem_d, BLAS2::get_layout_smem_b());\n    auto f_shared_tensor = cublasdx::make_tensor(smem_f, BLAS2::get_layout_smem_c());\n\n    using alignment2 = cublasdx::alignment_of<BLAS2>;\n    cublasdx::copy<BLAS2, alignment2::b>(d_global_tensor, d_shared_tensor);\n    cublasdx::copy<BLAS2, alignment2::c>(f_global_tensor, f_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS2().execute(alpha2, c_shared_tensor, d_shared_tensor, beta2, f_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS2::get_layout_gmem_c());\n    cublasdx::copy<BLAS2, alignment2::c>(f_shared_tensor, out_global_tensor);\n}\n\n// This is an example of two fp16 general matrix-matrix multiplications (GEMM) fused together\n// and performed in one kernel in a single CUDA block:\n//\n//             1) C = alpha1 * (A * B) + beta1 * C\n//             2) F = alpha2 * (C * D) + beta2 * F\n//\n// * A, B, C, D and F are matrices containing real half precision floating-point values.\n// * (alpha1, beta1) and (alpha2, beta2) and real half precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix F (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// Notes:\n// * Both GEMM operations use the same number of threads, however, it's not a requirement.\n// * It's important that the dimensions of the first and the 2nd GEMM are set in such a way that\n//   the C matrix has the same dimensions in both operations.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m1, n1, k1 define the dimensions of matrices A, B, and C\n    constexpr unsigned int m1 = 64;\n    constexpr unsigned int n1 = 64;\n    constexpr unsigned int k1 = 64;\n\n    // Parameters m2, n2, k2 define the dimensions of matrices C, D and F\n    // Note: (m1, n1) and (m2, k2) must be equal as describe the same matrix (matrix C)\n    constexpr unsigned int m2 = m1;\n    constexpr unsigned int n2 = 128;\n    constexpr unsigned int k2 = n1;\n\n    // The logical dimensions of matrix A are: [m1, k1] (m rows, k columns)\n    // The logical dimensions of matrix B are: [k1, n1]\n    // The logical dimensions of matrix C are: [m1, n1]\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::col_major;\n    constexpr auto c_arrangement = cublasdx::col_major;\n\n    // The logical dimensions of matrix C are: [m2, k2] == [m1, n1]\n    // The logical dimensions of matrix D are: [k2, n2]\n    // The logical dimensions of matrix F are: [m2, n2]\n    constexpr auto d_arrangement = cublasdx::col_major;\n    constexpr auto f_arrangement = cublasdx::col_major;\n\n    // Use the same block size for both GEMM operations, so BLAS1::block_dim == BLAS2::block_dim which\n    // simplifies the example.\n    constexpr unsigned int block_size = 128;\n\n    using BLAS1 = decltype(cublasdx::Size<m1, n1, k1>() + cublasdx::Precision<__half>() +\n                           cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                           cublasdx::Arrangement<a_arrangement, b_arrangement, c_arrangement>() + cublasdx::Block() +\n                           cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n    using BLAS2 = decltype(cublasdx::Size<m2, n2, k2>() + cublasdx::Precision<__half>() +\n                           cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                           cublasdx::Arrangement<c_arrangement, d_arrangement, f_arrangement>() + cublasdx::Block() +\n                           cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS1>;\n\n    // alpha and beta for the first GEMM\n    value_type alpha1 = 1.0;\n    value_type beta1  = 0.0;\n\n    // alpha and beta for the 2nd GEMM\n    value_type alpha2 = 1.0;\n    value_type beta2  = 1.0;\n\n    // Allocate managed memory for a, b, c, d, f and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a1_size = example::global_memory_size_of<BLAS1>::a_size;\n    constexpr auto global_b1_size = example::global_memory_size_of<BLAS1>::b_size;\n    constexpr auto global_c1_size = example::global_memory_size_of<BLAS1>::c_size;\n    constexpr auto global_b2_size = example::global_memory_size_of<BLAS2>::b_size;\n    constexpr auto global_c2_size = example::global_memory_size_of<BLAS2>::c_size;\n\n    auto inputs_size       = global_a1_size + global_b1_size + global_c1_size + global_b2_size + global_c2_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, global_c2_size * sizeof(value_type)));\n\n    value_type* a = inputs;\n    value_type* b = a + (global_a1_size);\n    value_type* c = b + (global_b1_size); // C matrix for BLAS1, A matrix for BLAS2\n    value_type* d = c + (global_c1_size); // D is B matrix for BLAS2\n    value_type* f = d + (global_b2_size); // F is C matrix for BLAS2\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a1_size);\n    auto host_b = example::get_random_data<value_type>(global_b1_size);\n    auto host_c = example::get_random_data<value_type>(global_c1_size);\n    auto host_d = example::get_random_data<value_type>(global_b2_size);\n    auto host_f = example::get_random_data<value_type>(global_c2_size);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d, host_d.data(), global_b2_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(f, host_f.data(), global_c2_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    const auto shared_memory =\n        std::max<size_t>(cublasdx::get_shared_storage_size<BLAS1>(), cublasdx::get_shared_storage_size<BLAS2>());\n    CUDA_CHECK_AND_EXIT(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS1, BLAS2>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory));\n\n    // Execute kernel\n    gemm_kernel<BLAS1, BLAS2>\n        <<<1, BLAS1::block_dim, shared_memory>>>(alpha1, a, b, beta1, c, alpha2, d, beta2, f, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c2_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c2_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    // 1st GEMM\n    auto blas1_reference_host_output = example::reference_gemm<BLAS1>(alpha1, host_a, host_b, beta1, host_c);\n    // 2nd GEMM\n    std::vector<value_type> blas2_host_c(blas1_reference_host_output.size());\n    blas2_host_c.assign(blas1_reference_host_output.begin(), blas1_reference_host_output.end());\n    auto reference_host_output = example::reference_gemm<BLAS2>(alpha2, blas2_host_c, host_d, beta2, host_f);\n\n    // Check against reference\n    if (example::check_error<BLAS2>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/14_gemm_fused/gemm_fusion.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n#include <type_traits>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\n__launch_bounds__(BLAS1::max_threads_per_block) __global__ void fused_gemm_kernel(const ValueType  alpha1,\n                                                                                  const ValueType* a,\n                                                                                  const ValueType* b,\n                                                                                  const ValueType  beta1,\n                                                                                  const ValueType* c,\n                                                                                  const ValueType  alpha2,\n                                                                                  const ValueType* d,\n                                                                                  const ValueType  beta2,\n                                                                                  const ValueType* f,\n                                                                                  ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n    constexpr unsigned int block_size = BLAS1::block_dim.x * BLAS1::block_dim.y * BLAS1::block_dim.z;\n\n    static_assert(std::is_same_v<value_type, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    // Matrix C is the first in shared memory, because it's reused in the 2nd GEMM. Moreover,\n    // matrices A and B might have different sizes than F and D.\n    value_type* smem_c = reinterpret_cast<value_type*>(smem);\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + BLAS1::c_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS1::c_size + BLAS1::a_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS1::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS1::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS1::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS1::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS1::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS1::get_layout_smem_c());\n\n    using alignment1 = cublasdx::alignment_of<BLAS1>;\n    cublasdx::copy<BLAS1, alignment1::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS1().execute(alpha1, a_shared_tensor, b_shared_tensor, beta1, c_shared_tensor);\n    __syncthreads();\n\n    static_assert((BLAS1::c_size == BLAS2::a_size), \"The sizes of C matrix are different in BLAS1 and BLAS2\");\n    value_type* smem_d = smem_c + BLAS2::a_size;\n    value_type* smem_f = smem_c + BLAS2::a_size + BLAS2::b_size;\n\n    auto d_global_tensor = cublasdx::make_tensor(d, BLAS2::get_layout_gmem_b());\n    auto f_global_tensor = cublasdx::make_tensor(f, BLAS2::get_layout_gmem_c());\n\n    auto d_shared_tensor = cublasdx::make_tensor(smem_d, BLAS2::get_layout_smem_b());\n    auto f_shared_tensor = cublasdx::make_tensor(smem_f, BLAS2::get_layout_smem_c());\n\n    using alignment2 = cublasdx::alignment_of<BLAS2>;\n    cublasdx::copy<BLAS2, alignment2::b>(d_global_tensor, d_shared_tensor);\n    cublasdx::copy<BLAS2, alignment2::c>(f_global_tensor, f_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS2().execute(alpha2, c_shared_tensor, d_shared_tensor, beta2, f_shared_tensor);\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS2::get_layout_gmem_c());\n    cublasdx::copy<BLAS2, alignment2::c>(f_shared_tensor, out_global_tensor);\n}\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\ndouble measure_cublasdx(unsigned int     kernel_warm_up_repeats,\n                        unsigned int     kernel_repeats,\n                        const ValueType  alpha1,\n                        const ValueType* a,\n                        const ValueType* b,\n                        const ValueType  beta1,\n                        const ValueType* c,\n                        const ValueType  alpha2,\n                        const ValueType* d,\n                        const ValueType  beta2,\n                        const ValueType* f,\n                        ValueType*       output,\n                        cudaStream_t     stream) {\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto shared_memory =\n        std::max<size_t>(cublasdx::get_shared_storage_size<BLAS1>(), cublasdx::get_shared_storage_size<BLAS2>());\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        fused_gemm_kernel<BLAS1, BLAS2>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory));\n\n    // Execute kernel.\n    double time = example::measure::execution(\n        [&](cudaStream_t stream) {\n            fused_gemm_kernel<BLAS1, BLAS2>\n                <<<1, BLAS1::block_dim, shared_memory, stream>>>(alpha1, a, b, beta1, c, alpha2, d, beta2, f, output);\n        },\n        kernel_warm_up_repeats,\n        kernel_repeats,\n        stream);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    return time;\n}\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\ndouble measure_cublas(unsigned int     kernel_warm_up_repeats,\n                      unsigned int     kernel_repeats,\n                      ValueType        alpha1,\n                      const ValueType* a,\n                      const ValueType* b,\n                      ValueType        beta1,\n                      ValueType*       c,\n                      const ValueType  alpha2,\n                      const ValueType* d,\n                      const ValueType  beta2,\n                      ValueType*       f,\n                      cudaStream_t     stream) {\n\n    static_assert(std::is_same_v<ValueType, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    const unsigned int m1 = cublasdx::size_of<BLAS1>::m;\n    const unsigned int n1 = cublasdx::size_of<BLAS1>::n;\n    const unsigned int k1 = cublasdx::size_of<BLAS1>::k;\n\n    const unsigned int lda1 = cublasdx::leading_dimension_of<BLAS1>::a;\n    const unsigned int ldb1 = cublasdx::leading_dimension_of<BLAS1>::b;\n    const unsigned int ldc1 = cublasdx::leading_dimension_of<BLAS1>::c;\n\n    const unsigned int m2 = cublasdx::size_of<BLAS2>::m;\n    const unsigned int n2 = cublasdx::size_of<BLAS2>::n;\n    const unsigned int k2 = cublasdx::size_of<BLAS2>::k;\n\n    const unsigned int lda2 = cublasdx::leading_dimension_of<BLAS2>::a;\n    const unsigned int ldb2 = cublasdx::leading_dimension_of<BLAS2>::b;\n    const unsigned int ldc2 = cublasdx::leading_dimension_of<BLAS2>::c;\n\n    static_assert(\n        example::is_complex<ValueType>() && std::is_same_v<float, typename example::get_precision<ValueType>::type>,\n        \"Type or precision is currently not supported for cuBLAS measurement.\");\n\n    //\n    // cuBLAS\n    //\n    cublasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(cublasCreate(&handle));\n\n    const auto a_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS1>::a);\n    const auto b_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS1>::b);\n    static_assert(cublasdx::arrangement_of<BLAS1>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    const auto c_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS2>::a);\n    const auto d_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS2>::b);\n    static_assert(cublasdx::arrangement_of<BLAS2>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    cublasSetStream(handle, stream);\n\n    double time_cublas = example::measure::execution(\n        [&](cudaStream_t) {\n            // C = alpha1 * A * B + beta1 * C\n            CUBLAS_CHECK_AND_EXIT(cublasCgemm(handle,\n                                              a_transpose,\n                                              b_transpose,\n                                              m1,\n                                              n1,\n                                              k1,\n                                              reinterpret_cast<const cuComplex*>(&alpha1),\n                                              reinterpret_cast<const cuComplex*>(a),\n                                              lda1,\n                                              reinterpret_cast<const cuComplex*>(b),\n                                              ldb1,\n                                              reinterpret_cast<const cuComplex*>(&beta1),\n                                              reinterpret_cast<cuComplex*>(c),\n                                              ldc1));\n            // F = alpha2 * C * D + beta2 * F\n            CUBLAS_CHECK_AND_EXIT(cublasCgemm(handle,\n                                              c_transpose,\n                                              d_transpose,\n                                              m2,\n                                              n2,\n                                              k2,\n                                              reinterpret_cast<const cuComplex*>(&alpha2),\n                                              reinterpret_cast<const cuComplex*>(c),\n                                              lda2,\n                                              reinterpret_cast<const cuComplex*>(d),\n                                              ldb2,\n                                              reinterpret_cast<const cuComplex*>(&beta2),\n                                              reinterpret_cast<cuComplex*>(f),\n                                              ldc2));\n        },\n        kernel_warm_up_repeats,\n        kernel_repeats,\n        stream);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    CUBLAS_CHECK_AND_EXIT(cublasDestroy(handle));\n\n    return time_cublas;\n}\n\n// This example compares the performance of cuBLAS and cuBLASDx for the example described in\n// \"gemm_fusion.cu\". Using cuBLASDx we fuse the chained matrix multiplication into one kernel,\n// whereas we compose the final result using cuBLAS using two matrix multiplications.\n//\n//             1) C = alpha1 * (A * B) + beta1 * C\n//             2) F = alpha2 * (C * D) + beta2 * F\n//\n// * A, B, C, D and F are matrices containing complex single precision floating-point values.\n// * (alpha1, beta1) and (alpha2, beta2) and complex single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Then we compare the performance of the fused kernel in cuBLASDx with the\n// performance of the two matrix multiplications needed to compute the result using cuBLAS.\n//\n// For cuBLASDx the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// General Notes:\n// * The matrix sizes should be small enough so that a single block is the optimal choice, since\n//   cuBLASDx is limited to one using one block whereas cuBLAS can use a larger number of blocks.\n//\n// cuBLASDx Notes:\n// * Both GEMM operations use the same number of threads, however, it's not a requirement.\n// * It's important that the dimensions of the first and the 2nd GEMM are set in such a way that\n//   the C matrix has the same dimensions in both operations.\ntemplate<unsigned int Arch>\nint fused_gemm_performance() {\n    // Parameters m1, n1, k1 define the dimensions of matrices A, B, and C.\n    constexpr unsigned int m1 = 32;\n    constexpr unsigned int n1 = 32;\n    constexpr unsigned int k1 = 32;\n\n    // Parameters m2, n2, k2 define the dimensions of matrices C, D and F.\n    // Note: (m1, n1) and (m2, k2) must be equal as describe the same matrix (matrix C).\n    constexpr unsigned int m2 = m1;\n    constexpr unsigned int n2 = 32;\n    constexpr unsigned int k2 = n1;\n\n    // The logical dimensions of matrix A are: [m1, k1] (m rows, k columns).\n    // The logical dimensions of matrix B are: [k1, n1].\n    // The logical dimensions of matrix C are: [m1, n1].\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::col_major;\n\n    // The logical dimensions of matrix C are: [m2, k2] == [m1, n1].\n    // The logical dimensions of matrix D are: [k2, n2].\n    // The logical dimensions of matrix F are: [m2, n2].\n    constexpr auto c_arrangement = cublasdx::col_major;\n    constexpr auto d_arrangement = cublasdx::col_major;\n\n    // Use the same block size for both GEMM operations, so BLAS1::block_dim == BLAS2::block_dim which\n    // simplifies the example.\n    constexpr unsigned int block_size = 512;\n\n    // Choose the precision and data type. In this example, we limit ourselves single precision complex\n    // data to keep the cuBLAS measurement code simple.\n    using precision     = float;\n    constexpr auto type = cublasdx::type::complex;\n\n    using BLAS1 =\n        decltype(cublasdx::Size<m1, n1, k1>() + cublasdx::Precision<precision>() + cublasdx::Type<type>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<a_arrangement, b_arrangement>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n    using BLAS2 =\n        decltype(cublasdx::Size<m2, n2, k2>() + cublasdx::Precision<precision>() + cublasdx::Type<type>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<c_arrangement, d_arrangement>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS1>;\n\n    // Set the beta values (beta1 and beta2) for the two GEMMs to 0. since cuBLAS accumulates into the result and\n    // we perform multiple repeats.\n    // alpha and beta for the first GEMM.\n    value_type alpha1 = example::make_value<value_type>(1., 2.);\n    value_type beta1  = example::make_value<value_type>(0.);\n\n    // alpha and beta for the second GEMM.\n    value_type alpha2 = example::make_value<value_type>(3., 4.);\n    value_type beta2  = example::make_value<value_type>(0.);\n\n    // Allocate device memory for a, b, c, d, f and output.\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a1_size = example::global_memory_size_of<BLAS1>::a_size;\n    constexpr auto global_b1_size = example::global_memory_size_of<BLAS1>::b_size;\n    constexpr auto global_c1_size = example::global_memory_size_of<BLAS1>::c_size;\n    constexpr auto global_b2_size = example::global_memory_size_of<BLAS2>::b_size;\n    constexpr auto global_c2_size = example::global_memory_size_of<BLAS2>::c_size;\n\n    auto inputs_size       = global_a1_size + global_b1_size + global_c1_size + global_b2_size + global_c2_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, global_c2_size * sizeof(value_type)));\n\n    value_type* a = inputs;\n    value_type* b = a + (global_a1_size);\n    value_type* c = b + (global_b1_size); // C matrix for BLAS1, A matrix for BLAS2\n    value_type* d = c + (global_c1_size); // D is B matrix for BLAS2\n    value_type* f = d + (global_b2_size); // F is C matrix for BLAS2\n\n    // Fill the A, B, C matrices with random values.\n    auto host_a = example::get_random_data<value_type>(global_a1_size);\n    auto host_b = example::get_random_data<value_type>(global_b1_size);\n    auto host_c = example::get_random_data<value_type>(global_c1_size);\n    auto host_d = example::get_random_data<value_type>(global_b2_size);\n    auto host_f = example::get_random_data<value_type>(global_c2_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c1_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(d, host_d.data(), global_b2_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(f, host_f.data(), global_c2_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    const unsigned int kernel_repeats         = 100;\n    const unsigned int kernel_warm_up_repeats = 1;\n    cudaStream_t       stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream))\n\n    // Measure cuBLASDx performance.\n    double time_cublasdx = measure_cublasdx<BLAS1, BLAS2>(\n        kernel_warm_up_repeats, kernel_repeats, alpha1, a, b, beta1, c, alpha2, d, beta2, f, output, stream);\n\n    // Measure cuBLAS performance.\n    double time_cublas = measure_cublas<BLAS1, BLAS2>(\n        kernel_warm_up_repeats, kernel_repeats, alpha1, a, b, beta1, c, alpha2, d, beta2, f, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m1, n1, k1: \" << size_of<BLAS1>::m << \", \" << size_of<BLAS1>::n << \", \" << size_of<BLAS1>::k\n              << std::endl;\n    std::cout << \"m2, n2, k2: \" << size_of<BLAS2>::m << \", \" << size_of<BLAS2>::n << \", \" << size_of<BLAS2>::k\n              << std::endl;\n    std::cout << \"Type: \" << example::type_string<value_type>() << std::endl;\n    std::cout << \"Precision: \" << example::precision_string<value_type>() << std::endl;\n\n    std::cout << \"\\ncuBLASDx (fused kernel)\\n\";\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"Avg time [ms]  = \" << time_cublasdx / kernel_repeats << \"\\n\";\n\n    std::cout << \"\\ncuBLAS\\n\";\n    std::cout << \"Avg time [ms]  = \" << time_cublas / kernel_repeats << \"\\n\";\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    return 0;\n}\n\nstruct fused_gemm_performance_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return fused_gemm_performance<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(fused_gemm_performance_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <type_traits>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\n__launch_bounds__(BLAS1::max_threads_per_block) __global__ void fused_gemm_kernel(const ValueType  alpha1,\n                                                                                  const ValueType* a,\n                                                                                  const ValueType* b,\n                                                                                  const ValueType  beta1,\n                                                                                  const ValueType* c,\n                                                                                  const ValueType  alpha2,\n                                                                                  const ValueType* d,\n                                                                                  const ValueType  beta2,\n                                                                                  const ValueType* f,\n                                                                                  ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n    constexpr unsigned int block_size = BLAS1::block_dim.x * BLAS1::block_dim.y * BLAS1::block_dim.z;\n\n    static_assert(std::is_same_v<value_type, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    // Matrix C is the first in shared memory, because it's reused in the 2nd GEMM. Moreover,\n    // matrices A and B might have different sizes than F and D.\n    value_type* smem_c = reinterpret_cast<value_type*>(smem);\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + BLAS1::c_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS1::c_size + BLAS1::a_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS1::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS1::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS1::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS1::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS1::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS1::get_layout_smem_c());\n\n    using alignment1 = cublasdx::alignment_of<BLAS1>;\n    cublasdx::copy<BLAS1, alignment1::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS1, alignment1::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS1().execute(alpha1, a_shared_tensor, b_shared_tensor, beta1, c_shared_tensor);\n    __syncthreads();\n\n    static_assert((BLAS1::c_size == BLAS2::a_size), \"The sizes of C matrix are different in BLAS1 and BLAS2\");\n    value_type* smem_d = smem_c + BLAS2::a_size;\n    value_type* smem_f = smem_c + BLAS2::a_size + BLAS2::b_size;\n\n    auto d_global_tensor = cublasdx::make_tensor(d, BLAS2::get_layout_gmem_b());\n    auto f_global_tensor = cublasdx::make_tensor(f, BLAS2::get_layout_gmem_c());\n\n    auto d_shared_tensor = cublasdx::make_tensor(smem_d, BLAS2::get_layout_smem_b());\n    auto f_shared_tensor = cublasdx::make_tensor(smem_f, BLAS2::get_layout_smem_c());\n\n    using alignment2 = cublasdx::alignment_of<BLAS2>;\n    cublasdx::copy<BLAS2, alignment2::b>(d_global_tensor, d_shared_tensor);\n    cublasdx::copy<BLAS2, alignment2::c>(f_global_tensor, f_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS2().execute(alpha2, c_shared_tensor, d_shared_tensor, beta2, f_shared_tensor);\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS2::get_layout_gmem_c());\n    cublasdx::copy<BLAS2, alignment2::c>(f_shared_tensor, out_global_tensor);\n}\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\ndouble measure_cublasdx(unsigned int     kernel_warm_up_repeats,\n                        unsigned int     kernel_repeats,\n                        const ValueType  alpha1,\n                        const ValueType* a,\n                        const ValueType* b,\n                        const ValueType  beta1,\n                        const ValueType* c,\n                        const ValueType  alpha2,\n                        const ValueType* d,\n                        const ValueType  beta2,\n                        const ValueType* f,\n                        ValueType*       output,\n                        hipStream_t     stream) {\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto shared_memory =\n        std::max<size_t>(cublasdx::get_shared_storage_size<BLAS1>(), cublasdx::get_shared_storage_size<BLAS2>());\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(fused_gemm_kernel<BLAS1, BLAS2>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory));\n\n    // Execute kernel.\n    double time = example::measure::execution(\n        [&](hipStream_t stream) {\n            fused_gemm_kernel<BLAS1, BLAS2>\n                <<<1, BLAS1::block_dim, shared_memory, stream>>>(alpha1, a, b, beta1, c, alpha2, d, beta2, f, output);\n        },\n        kernel_warm_up_repeats,\n        kernel_repeats,\n        stream);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    return time;\n}\n\ntemplate<class BLAS1, class BLAS2, class ValueType = typename example::uniform_value_type_t<BLAS1>>\ndouble measure_cublas(unsigned int     kernel_warm_up_repeats,\n                      unsigned int     kernel_repeats,\n                      ValueType        alpha1,\n                      const ValueType* a,\n                      const ValueType* b,\n                      ValueType        beta1,\n                      ValueType*       c,\n                      const ValueType  alpha2,\n                      const ValueType* d,\n                      const ValueType  beta2,\n                      ValueType*       f,\n                      hipStream_t     stream) {\n\n    static_assert(std::is_same_v<ValueType, example::uniform_value_type_t<BLAS2>>,\n                  \"BLAS1 and BLAS2 must have the same type and precision\");\n    static_assert((BLAS1::c_shape == BLAS2::a_shape), \"The shape of C matrix is different in BLAS1 and BLAS2\");\n\n    const unsigned int m1 = cublasdx::size_of<BLAS1>::m;\n    const unsigned int n1 = cublasdx::size_of<BLAS1>::n;\n    const unsigned int k1 = cublasdx::size_of<BLAS1>::k;\n\n    const unsigned int lda1 = cublasdx::leading_dimension_of<BLAS1>::a;\n    const unsigned int ldb1 = cublasdx::leading_dimension_of<BLAS1>::b;\n    const unsigned int ldc1 = cublasdx::leading_dimension_of<BLAS1>::c;\n\n    const unsigned int m2 = cublasdx::size_of<BLAS2>::m;\n    const unsigned int n2 = cublasdx::size_of<BLAS2>::n;\n    const unsigned int k2 = cublasdx::size_of<BLAS2>::k;\n\n    const unsigned int lda2 = cublasdx::leading_dimension_of<BLAS2>::a;\n    const unsigned int ldb2 = cublasdx::leading_dimension_of<BLAS2>::b;\n    const unsigned int ldc2 = cublasdx::leading_dimension_of<BLAS2>::c;\n\n    static_assert(\n        example::is_complex<ValueType>() && std::is_same_v<float, typename example::get_precision<ValueType>::type>,\n        \"Type or precision is currently not supported for cuBLAS measurement.\");\n\n    //\n    // cuBLAS\n    //\n    hipblasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(hipblasCreate(&handle));\n\n    const auto a_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS1>::a);\n    const auto b_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS1>::b);\n    static_assert(cublasdx::arrangement_of<BLAS1>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    const auto c_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS2>::a);\n    const auto d_transpose = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS2>::b);\n    static_assert(cublasdx::arrangement_of<BLAS2>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    hipblasSetStream(handle, stream);\n\n    double time_cublas = example::measure::execution(\n        [&](hipStream_t) {\n            // C = alpha1 * A * B + beta1 * C\n            CUBLAS_CHECK_AND_EXIT(hipblasCgemm(handle,\n                                              a_transpose,\n                                              b_transpose,\n                                              m1,\n                                              n1,\n                                              k1,\n                                              reinterpret_cast<const hipComplex*>(&alpha1),\n                                              reinterpret_cast<const hipComplex*>(a),\n                                              lda1,\n                                              reinterpret_cast<const hipComplex*>(b),\n                                              ldb1,\n                                              reinterpret_cast<const hipComplex*>(&beta1),\n                                              reinterpret_cast<hipComplex*>(c),\n                                              ldc1));\n            // F = alpha2 * C * D + beta2 * F\n            CUBLAS_CHECK_AND_EXIT(hipblasCgemm(handle,\n                                              c_transpose,\n                                              d_transpose,\n                                              m2,\n                                              n2,\n                                              k2,\n                                              reinterpret_cast<const hipComplex*>(&alpha2),\n                                              reinterpret_cast<const hipComplex*>(c),\n                                              lda2,\n                                              reinterpret_cast<const hipComplex*>(d),\n                                              ldb2,\n                                              reinterpret_cast<const hipComplex*>(&beta2),\n                                              reinterpret_cast<hipComplex*>(f),\n                                              ldc2));\n        },\n        kernel_warm_up_repeats,\n        kernel_repeats,\n        stream);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    CUBLAS_CHECK_AND_EXIT(hipblasDestroy(handle));\n\n    return time_cublas;\n}\n\n// This example compares the performance of cuBLAS and cuBLASDx for the example described in\n// \"gemm_fusion.cu\". Using cuBLASDx we fuse the chained matrix multiplication into one kernel,\n// whereas we compose the final result using cuBLAS using two matrix multiplications.\n//\n//             1) C = alpha1 * (A * B) + beta1 * C\n//             2) F = alpha2 * (C * D) + beta2 * F\n//\n// * A, B, C, D and F are matrices containing complex single precision floating-point values.\n// * (alpha1, beta1) and (alpha2, beta2) and complex single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Then we compare the performance of the fused kernel in cuBLASDx with the\n// performance of the two matrix multiplications needed to compute the result using cuBLAS.\n//\n// For cuBLASDx the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// General Notes:\n// * The matrix sizes should be small enough so that a single block is the optimal choice, since\n//   cuBLASDx is limited to one using one block whereas cuBLAS can use a larger number of blocks.\n//\n// cuBLASDx Notes:\n// * Both GEMM operations use the same number of threads, however, it's not a requirement.\n// * It's important that the dimensions of the first and the 2nd GEMM are set in such a way that\n//   the C matrix has the same dimensions in both operations.\ntemplate<unsigned int Arch>\nint fused_gemm_performance() {\n    // Parameters m1, n1, k1 define the dimensions of matrices A, B, and C.\n    constexpr unsigned int m1 = 32;\n    constexpr unsigned int n1 = 32;\n    constexpr unsigned int k1 = 32;\n\n    // Parameters m2, n2, k2 define the dimensions of matrices C, D and F.\n    // Note: (m1, n1) and (m2, k2) must be equal as describe the same matrix (matrix C).\n    constexpr unsigned int m2 = m1;\n    constexpr unsigned int n2 = 32;\n    constexpr unsigned int k2 = n1;\n\n    // The logical dimensions of matrix A are: [m1, k1] (m rows, k columns).\n    // The logical dimensions of matrix B are: [k1, n1].\n    // The logical dimensions of matrix C are: [m1, n1].\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::col_major;\n\n    // The logical dimensions of matrix C are: [m2, k2] == [m1, n1].\n    // The logical dimensions of matrix D are: [k2, n2].\n    // The logical dimensions of matrix F are: [m2, n2].\n    constexpr auto c_arrangement = cublasdx::col_major;\n    constexpr auto d_arrangement = cublasdx::col_major;\n\n    // Use the same block size for both GEMM operations, so BLAS1::block_dim == BLAS2::block_dim which\n    // simplifies the example.\n    constexpr unsigned int block_size = 512;\n\n    // Choose the precision and data type. In this example, we limit ourselves single precision complex\n    // data to keep the cuBLAS measurement code simple.\n    using precision     = float;\n    constexpr auto type = cublasdx::type::complex;\n\n    using BLAS1 =\n        decltype(cublasdx::Size<m1, n1, k1>() + cublasdx::Precision<precision>() + cublasdx::Type<type>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<a_arrangement, b_arrangement>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n    using BLAS2 =\n        decltype(cublasdx::Size<m2, n2, k2>() + cublasdx::Precision<precision>() + cublasdx::Type<type>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<c_arrangement, d_arrangement>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS1>;\n\n    // Set the beta values (beta1 and beta2) for the two GEMMs to 0. since cuBLAS accumulates into the result and\n    // we perform multiple repeats.\n    // alpha and beta for the first GEMM.\n    value_type alpha1 = example::make_value<value_type>(1., 2.);\n    value_type beta1  = example::make_value<value_type>(0.);\n\n    // alpha and beta for the second GEMM.\n    value_type alpha2 = example::make_value<value_type>(3., 4.);\n    value_type beta2  = example::make_value<value_type>(0.);\n\n    // Allocate device memory for a, b, c, d, f and output.\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a1_size = example::global_memory_size_of<BLAS1>::a_size;\n    constexpr auto global_b1_size = example::global_memory_size_of<BLAS1>::b_size;\n    constexpr auto global_c1_size = example::global_memory_size_of<BLAS1>::c_size;\n    constexpr auto global_b2_size = example::global_memory_size_of<BLAS2>::b_size;\n    constexpr auto global_c2_size = example::global_memory_size_of<BLAS2>::c_size;\n\n    auto inputs_size       = global_a1_size + global_b1_size + global_c1_size + global_b2_size + global_c2_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, global_c2_size * sizeof(value_type)));\n\n    value_type* a = inputs;\n    value_type* b = a + (global_a1_size);\n    value_type* c = b + (global_b1_size); // C matrix for BLAS1, A matrix for BLAS2\n    value_type* d = c + (global_c1_size); // D is B matrix for BLAS2\n    value_type* f = d + (global_b2_size); // F is C matrix for BLAS2\n\n    // Fill the A, B, C matrices with random values.\n    auto host_a = example::get_random_data<value_type>(global_a1_size);\n    auto host_b = example::get_random_data<value_type>(global_b1_size);\n    auto host_c = example::get_random_data<value_type>(global_c1_size);\n    auto host_d = example::get_random_data<value_type>(global_b2_size);\n    auto host_f = example::get_random_data<value_type>(global_c2_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c1_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(d, host_d.data(), global_b2_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(f, host_f.data(), global_c2_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    const unsigned int kernel_repeats         = 100;\n    const unsigned int kernel_warm_up_repeats = 1;\n    hipStream_t       stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream))\n\n    // Measure cuBLASDx performance.\n    double time_cublasdx = measure_cublasdx<BLAS1, BLAS2>(\n        kernel_warm_up_repeats, kernel_repeats, alpha1, a, b, beta1, c, alpha2, d, beta2, f, output, stream);\n\n    // Measure cuBLAS performance.\n    double time_cublas = measure_cublas<BLAS1, BLAS2>(\n        kernel_warm_up_repeats, kernel_repeats, alpha1, a, b, beta1, c, alpha2, d, beta2, f, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m1, n1, k1: \" << size_of<BLAS1>::m << \", \" << size_of<BLAS1>::n << \", \" << size_of<BLAS1>::k\n              << std::endl;\n    std::cout << \"m2, n2, k2: \" << size_of<BLAS2>::m << \", \" << size_of<BLAS2>::n << \", \" << size_of<BLAS2>::k\n              << std::endl;\n    std::cout << \"Type: \" << example::type_string<value_type>() << std::endl;\n    std::cout << \"Precision: \" << example::precision_string<value_type>() << std::endl;\n\n    std::cout << \"\\ncuBLASDx (fused kernel)\\n\";\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"Avg time [ms]  = \" << time_cublasdx / kernel_repeats << \"\\n\";\n\n    std::cout << \"\\ncuBLAS\\n\";\n    std::cout << \"Avg time [ms]  = \" << time_cublas / kernel_repeats << \"\\n\";\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    return 0;\n}\n\nstruct fused_gemm_performance_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return fused_gemm_performance<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(fused_gemm_performance_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/14_gemm_fused/fused_gemm_performance.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         class AValueType = typename BLAS::a_value_type,\n         class BValueType = typename BLAS::b_value_type,\n         class CValueType = typename BLAS::c_value_type>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const AValueType* a,\n                     const BValueType* b,\n                     const CValueType* c,\n                     const CValueType  alpha,\n                     const CValueType  beta,\n                     CValueType*       output) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    using a_engine =\n        typename decltype(cublasdx::make_tensor(std::declval<decltype(a)>(), BLAS::get_layout_gmem_a()))::engine_type;\n    static_assert(std::is_same_v<a_engine, typename decltype(a_global_tensor)::engine_type>, \"\");\n\n\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor);\n    auto result_fragment = accumulator.get_results();\n\n    auto d_frag = accumulator.make_partition_and_copy(c_global_tensor);\n    cublasdx::axpby(alpha, result_fragment, beta, d_frag);\n    accumulator.partition_and_copy(d_frag, out_global_tensor);\n}\n\n// This is an example of int8 / int8 / int32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block with use of Tensor Cores:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing:\n//    A --> int8_t\n//    B --> int8_t\n//    C --> int32_t\n// * alpha and beta are real int32_t values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<int8_t, int8_t, int32_t>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using a_value_type = example::a_value_type_t<BLAS>;\n    using b_value_type = example::b_value_type_t<BLAS>;\n    using c_value_type = example::c_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    a_value_type* input_a;\n    b_value_type* input_b;\n    c_value_type* input_c;\n    c_value_type* output_c;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_a, global_a_size * sizeof(a_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_b, global_b_size * sizeof(b_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_c, global_c_size * sizeof(c_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_c, global_c_size * sizeof(c_value_type)));\n\n    c_value_type alpha = c_value_type(1.0);\n    c_value_type beta  = c_value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<a_value_type>(global_a_size);\n    auto host_b = example::get_random_data<b_value_type>(global_b_size);\n    auto host_c = example::get_random_data<c_value_type>(global_c_size);\n\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(input_a, host_a.data(), global_a_size * sizeof(a_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(input_b, host_b.data(), global_b_size * sizeof(b_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(input_c, host_c.data(), global_c_size * sizeof(c_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(\n        input_a, input_b, input_c, alpha, beta, output_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<c_value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output_c, global_c_size * sizeof(c_value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(input_a));\n    CUDA_CHECK_AND_EXIT(cudaFree(input_b));\n    CUDA_CHECK_AND_EXIT(cudaFree(input_c));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_c));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         class AValueType = typename BLAS::a_value_type,\n         class BValueType = typename BLAS::b_value_type,\n         class CValueType = typename BLAS::c_value_type>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const AValueType* a,\n                     const BValueType* b,\n                     const CValueType* c,\n                     const CValueType  alpha,\n                     const CValueType  beta,\n                     CValueType*       output) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    using a_engine =\n        typename decltype(cublasdx::make_tensor(std::declval<decltype(a)>(), BLAS::get_layout_gmem_a()))::engine_type;\n    static_assert(std::is_same_v<a_engine, typename decltype(a_global_tensor)::engine_type>, \"\");\n\n\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor);\n    auto result_fragment = accumulator.get_results();\n\n    auto d_frag = accumulator.make_partition_and_copy(c_global_tensor);\n    cublasdx::axpby(alpha, result_fragment, beta, d_frag);\n    accumulator.partition_and_copy(d_frag, out_global_tensor);\n}\n\n// This is an example of int8 / int8 / int32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block with use of Tensor Cores:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing:\n//    A --> int8_t\n//    B --> int8_t\n//    C --> int32_t\n// * alpha and beta are real int32_t values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<int8_t, int8_t, int32_t>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using a_value_type = example::a_value_type_t<BLAS>;\n    using b_value_type = example::b_value_type_t<BLAS>;\n    using c_value_type = example::c_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    a_value_type* input_a;\n    b_value_type* input_b;\n    c_value_type* input_c;\n    c_value_type* output_c;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_a, global_a_size * sizeof(a_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_b, global_b_size * sizeof(b_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_c, global_c_size * sizeof(c_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_c, global_c_size * sizeof(c_value_type)));\n\n    c_value_type alpha = c_value_type(1.0);\n    c_value_type beta  = c_value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<a_value_type>(global_a_size);\n    auto host_b = example::get_random_data<b_value_type>(global_b_size);\n    auto host_c = example::get_random_data<c_value_type>(global_c_size);\n\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(input_a, host_a.data(), global_a_size * sizeof(a_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(input_b, host_b.data(), global_b_size * sizeof(b_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(input_c, host_c.data(), global_c_size * sizeof(c_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(\n        input_a, input_b, input_c, alpha, beta, output_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<c_value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output_c, global_c_size * sizeof(c_value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(input_a));\n    CUDA_CHECK_AND_EXIT(hipFree(input_b));\n    CUDA_CHECK_AND_EXIT(hipFree(input_c));\n    CUDA_CHECK_AND_EXIT(hipFree(output_c));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/02_gemm_precisions/simple_gemm_int8_int8_int32.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class TA, class TB, class TC>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void gemm_kernel(const TA* a, const TB* b, const TC* c, const TC alpha, const TC beta, TC* output) {\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of mixed-precision general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed percisions are supported.\n// Note that alpha and beta are expected to have the same precision and type (real or complex value)\n// as the matrix C elements.\n//\n// Input data A, B, and C is generated on host using random number generators, and later copied to\n// the device global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n\ntemplate<unsigned int Arch>\nint simple_gemm_mixed_precision() {\n\n    using PA = cublasdx::tfloat32_t;\n    using PB = cublasdx::tfloat32_t;\n    using PC = float;\n\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 24;\n    constexpr unsigned int k = 32;\n\n    constexpr unsigned int block_size = 256;\n\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<PA, PB, PC>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::Alignment<16, 16, 16>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using TA = typename BLAS::a_value_type;\n    using TB = typename BLAS::b_value_type;\n    using TC = typename BLAS::c_value_type;\n\n    std::cout << \"Precisions: A is \" << example::precision_string<TA>() << \", B is \" << example::precision_string<TB>()\n              << \" and C is \" << example::precision_string<TC>() << \" \\nType: A/B/C is \" << example::type_string<TA>()\n              << \"\\n\";\n\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::a_type, PA>::value, \"TA and PA do not match\");\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::b_type, PB>::value, \"TB and PB do not match\");\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::c_type, PC>::value, \"TC and PC do not match\");\n\n    // Allocate managed memory for a, b, c, and output\n    TA* a      = nullptr;\n    TB* b      = nullptr;\n    TC* c      = nullptr;\n    TC* output = nullptr;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&a, global_a_size * sizeof(TA)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&b, global_b_size * sizeof(TB)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&c, global_c_size * sizeof(TC)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_a_size * sizeof(TC)));\n\n    TC alpha = TC(1.0, 0.0);\n    TC beta  = TC(2.0, 0.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<TA>(global_a_size);\n    auto host_b = example::get_random_data<TB>(global_b_size);\n    auto host_c = example::get_random_data<TC>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(TA), cudaMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(TB), cudaMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(TC), cudaMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Instantiate kernel\n    auto kernel = gemm_kernel<BLAS, TA, TB, TC>;\n\n    // Execute kernel\n    kernel<<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>(), 0>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<TC> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(host_output.data(), output, global_c_size * sizeof(TC), cudaMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(a));\n    CUDA_CHECK_AND_EXIT(cudaFree(b));\n    CUDA_CHECK_AND_EXIT(cudaFree(c));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    std::vector<TC> host_a_ref = example::convert<TA, TC>(host_a);\n    std::vector<TC> host_b_ref = example::convert<TB, TC>(host_b);\n    std::vector<TC> host_c_ref = host_c;\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a_ref, host_b_ref, beta, host_c_ref);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm_mixed_precision<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class TA, class TB, class TC>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void gemm_kernel(const TA* a, const TB* b, const TC* c, const TC alpha, const TC beta, TC* output) {\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of mixed-precision general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed percisions are supported.\n// Note that alpha and beta are expected to have the same precision and type (real or complex value)\n// as the matrix C elements.\n//\n// Input data A, B, and C is generated on host using random number generators, and later copied to\n// the device global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n\ntemplate<unsigned int Arch>\nint simple_gemm_mixed_precision() {\n\n    using PA = cublasdx::tfloat32_t;\n    using PB = cublasdx::tfloat32_t;\n    using PC = float;\n\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 24;\n    constexpr unsigned int k = 32;\n\n    constexpr unsigned int block_size = 256;\n\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<PA, PB, PC>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::Alignment<16, 16, 16>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using TA = typename BLAS::a_value_type;\n    using TB = typename BLAS::b_value_type;\n    using TC = typename BLAS::c_value_type;\n\n    std::cout << \"Precisions: A is \" << example::precision_string<TA>() << \", B is \" << example::precision_string<TB>()\n              << \" and C is \" << example::precision_string<TC>() << \" \\nType: A/B/C is \" << example::type_string<TA>()\n              << \"\\n\";\n\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::a_type, PA>::value, \"TA and PA do not match\");\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::b_type, PB>::value, \"TB and PB do not match\");\n    static_assert(std::is_same<typename cublasdx::precision_of<BLAS>::c_type, PC>::value, \"TC and PC do not match\");\n\n    // Allocate managed memory for a, b, c, and output\n    TA* a      = nullptr;\n    TB* b      = nullptr;\n    TC* c      = nullptr;\n    TC* output = nullptr;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&a, global_a_size * sizeof(TA)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&b, global_b_size * sizeof(TB)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&c, global_c_size * sizeof(TC)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_a_size * sizeof(TC)));\n\n    TC alpha = TC(1.0, 0.0);\n    TC beta  = TC(2.0, 0.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<TA>(global_a_size);\n    auto host_b = example::get_random_data<TB>(global_b_size);\n    auto host_c = example::get_random_data<TC>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(TA), hipMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(TB), hipMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(TC), hipMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Instantiate kernel\n    auto kernel = gemm_kernel<BLAS, TA, TB, TC>;\n\n    // Execute kernel\n    kernel<<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>(), 0>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<TC> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(host_output.data(), output, global_c_size * sizeof(TC), hipMemcpyDefault));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(a));\n    CUDA_CHECK_AND_EXIT(hipFree(b));\n    CUDA_CHECK_AND_EXIT(hipFree(c));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    std::vector<TC> host_a_ref = example::convert<TA, TC>(host_a);\n    std::vector<TC> host_b_ref = example::convert<TB, TC>(host_b);\n    std::vector<TC> host_c_ref = host_c;\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a_ref, host_b_ref, beta, host_c_ref);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm_mixed_precision<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/02_gemm_precisions/simple_gemm_mixed_precision.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_kernel(const typename BLAS::a_value_type* a,\n                                                                           const typename BLAS::b_value_type* b,\n                                                                           typename BLAS::c_value_type*       c,\n                                                                           const typename BLAS::c_value_type  alpha,\n                                                                           const typename BLAS::c_value_type  beta) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, c_global_tensor);\n}\n\n// This is an example of general matrix-matrix multiplication (GEMM) with 8-bit floating point format\n// performanced in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A is the input matrix containing complex FP8 e4m3 data format, B is another input matrix containing complex FP8 e5m2 data format,\n// * C is the output matrix containing complex FP32 data format, and alpha and beta are complex FP32 values.\n// If (A, B, C) are any of the following data formats:\n//    (e4m3, e4m3, FP32)\n//    (e4m3, e5m2, FP32)\n//    (e5m2, e4m3, FP32)\n//    (e5m2, e5m2, FP32)\n// and GEMM is executed on SM89 or higher GPUs, then matrix multiply-and-accumulation (MMA) instructions are used.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 32;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value using alias MaxAlignment.\n    //    - Optional\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__nv_fp8_e4m3, __nv_fp8_e5m2, float>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() +\n                          cublasdx::Alignment<2, 2, 8>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c\n\n    using TA = typename BLAS::a_value_type;\n    using TB = typename BLAS::b_value_type;\n    using TC = typename BLAS::c_value_type;\n\n    TA* a;\n    TB* b;\n    TC* c;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&a, global_a_size * sizeof(TA)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&b, global_b_size * sizeof(TB)));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&c, global_c_size * sizeof(TC)));\n\n    auto alpha = TC(1.0, 1.0);\n    auto beta  = TC(2.0, 1.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<TA>(global_a_size);\n    auto host_b = example::get_random_data<TB>(global_b_size);\n    auto host_c = example::get_random_data<TC>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(TA), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(TB), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(TC), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<TC> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(host_output.data(), c, global_c_size * sizeof(TC), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(a));\n    CUDA_CHECK_AND_EXIT(cudaFree(b));\n    CUDA_CHECK_AND_EXIT(cudaFree(c));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_kernel(const typename BLAS::a_value_type* a,\n                                                                           const typename BLAS::b_value_type* b,\n                                                                           typename BLAS::c_value_type*       c,\n                                                                           const typename BLAS::c_value_type  alpha,\n                                                                           const typename BLAS::c_value_type  beta) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, c_global_tensor);\n}\n\n// This is an example of general matrix-matrix multiplication (GEMM) with 8-bit floating point format\n// performanced in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A is the input matrix containing complex FP8 e4m3 data format, B is another input matrix containing complex FP8 e5m2 data format,\n// * C is the output matrix containing complex FP32 data format, and alpha and beta are complex FP32 values.\n// If (A, B, C) are any of the following data formats:\n//    (e4m3, e4m3, FP32)\n//    (e4m3, e5m2, FP32)\n//    (e5m2, e4m3, FP32)\n//    (e5m2, e5m2, FP32)\n// and GEMM is executed on SM89 or higher GPUs, then matrix multiply-and-accumulation (MMA) instructions are used.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 32;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value using alias MaxAlignment.\n    //    - Optional\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__hip_fp8_e4m3_fnuz, __hip_fp8_e5m2_fnuz, float>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() +\n                          cublasdx::Alignment<2, 2, 8>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c\n\n    using TA = typename BLAS::a_value_type;\n    using TB = typename BLAS::b_value_type;\n    using TC = typename BLAS::c_value_type;\n\n    TA* a;\n    TB* b;\n    TC* c;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&a, global_a_size * sizeof(TA)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&b, global_b_size * sizeof(TB)));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&c, global_c_size * sizeof(TC)));\n\n    auto alpha = TC(1.0, 1.0);\n    auto beta  = TC(2.0, 1.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<TA>(global_a_size);\n    auto host_b = example::get_random_data<TB>(global_b_size);\n    auto host_c = example::get_random_data<TC>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(TA), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(TB), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(TC), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<TC> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(host_output.data(), c, global_c_size * sizeof(TC), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(a));\n    CUDA_CHECK_AND_EXIT(hipFree(b));\n    CUDA_CHECK_AND_EXIT(hipFree(c));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/02_gemm_precisions/simple_gemm_fp8.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    using a_engine =\n        typename decltype(cublasdx::make_tensor(std::declval<decltype(a)>(), BLAS::get_layout_gmem_a()))::engine_type;\n    static_assert(std::is_same_v<a_engine, typename decltype(a_global_tensor)::engine_type>, \"\");\n\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    using a_engine =\n        typename decltype(cublasdx::make_tensor(std::declval<decltype(a)>(), BLAS::get_layout_gmem_a()))::engine_type;\n    static_assert(std::is_same_v<a_engine, typename decltype(a_global_tensor)::engine_type>, \"\");\n\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/02_gemm_precisions/simple_gemm_fp32.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\nvoid reference(const ValueType* a,\n               const ValueType* b,\n               ValueType*       c,\n               const ValueType  alpha,\n               const ValueType  beta,\n               ValueType*       output,\n               cudaStream_t     stream) {\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Prepare cuBLAS\n    cublasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(cublasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(cublasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<GEMM>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<GEMM>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::b);\n    static_assert(cublasdx::arrangement_of<GEMM>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    // Prepare cuFFT\n    const unsigned int fft_size   = cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n;\n    const unsigned int batch_size = 1;\n\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Run cuBLAS\n    CUBLAS_CHECK_AND_EXIT(cublasCgemm(handle,\n                                      a_transpose,\n                                      b_transpose,\n                                      m,\n                                      n,\n                                      k,\n                                      reinterpret_cast<const cuFloatComplex*>(&alpha),\n                                      reinterpret_cast<const cuFloatComplex*>(a),\n                                      is_a_transposed ? k : m,\n                                      reinterpret_cast<const cuFloatComplex*>(b),\n                                      is_b_transposed ? n : k,\n                                      reinterpret_cast<const cuFloatComplex*>(&beta),\n                                      reinterpret_cast<cuFloatComplex*>(c),\n                                      m));\n\n    // Run cuFFT\n    CUFFT_CHECK_AND_EXIT(\n        cufftExecC2C(plan, reinterpret_cast<cufftComplex*>(c), reinterpret_cast<cufftComplex*>(output), CUFFT_FORWARD));\n\n    CUBLAS_CHECK_AND_EXIT(cublasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n}\n\ntemplate<class FFT>\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\ntemplate<class FFT, class GEMM, class ValueType = typename example::uniform_value_type_t<GEMM>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_kernel(const ValueType* a,\n                                                                              const ValueType* b,\n                                                                              const ValueType* c,\n                                                                              const ValueType  alpha,\n                                                                              const ValueType  beta,\n                                                                              ValueType*       output) {\n    using fft_complex_type  = typename FFT::value_type;\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n\n    static_assert(std::is_same_v<fft_complex_type, blas_complex_type>, \"BLAS and FFT complex type should match\");\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = GEMM::block_dim.x * GEMM::block_dim.y * GEMM::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    // cuBLASDx\n\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Load a, b, c from global to shared memory\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(reinterpret_cast<char*>(smem));\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // cuFFTDx\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from shared memory to registers\n    const unsigned int     offset = batch_offset<FFT>(local_fft_id, FFT::ffts_per_block);\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = smem_c[index];\n            index += stride;\n        }\n    }\n    __syncthreads();\n\n    // Execute FFT on registers\n    FFT().execute(thread_data, smem);\n\n    // Save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            output[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline. The kernel\n// computes the following operations:\n//     1) C = alpha * A * B + beta * C\n//     2) 1D FFT(C)\n// First the data of matrices A, B, and C is loaded into shared memory. After that the GEMM is executed, and 1D FFT\n// is applied to the results (matrix C). The results are stored back to global memory.\n//\n// Important notes:\n// * Results are verified against cuFFT and cuBLAS.\n// * FFT and GEMM must be compatible in terms of the requirements on block dimensions. cuFFTDx requirements are more firm, and\n//   therefore FFT::block_dim is enforced for GEMM.\n// * Shared memory required by the kernel is the max of the amount required by FFT and GEMM.\n// * For cuBLASDx GEMM description and cuFFTDx FFT description GEMM::value_type and FFT::value_type are the same types if:\n//     * GEMM has complex type set, and\n//     * Precision for GEMM and FFT is float or double.\ntemplate<unsigned int Arch>\nint gemm_fft() {\n    using precision_type = float;\n    static_assert(!std::is_same_v<precision_type, __half>, \"half precision is not supported in this example\");\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<64>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<precision_type>() +\n                         cufftdx::ElementsPerThread<2>() + cufftdx::FFTsPerBlock<1>() + cufftdx::SM<Arch>());\n\n    using GEMM =\n        decltype(cublasdx::Size<8, 8, 8>() + cublasdx::Precision<precision_type>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::col_major, cublasdx::col_major>() + cublasdx::Block() +\n                 cublasdx::BlockDim<FFT::block_dim.x, FFT::block_dim.y, FFT::block_dim.z>() + cublasdx::SM<Arch>());\n\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    static_assert(std::is_same_v<blas_complex_type, fft_complex_type>, \"BLAS and FFT complex types should be the same\");\n    using complex_type = blas_complex_type;\n\n    // Checking that FFT matches GEMM output\n    static_assert(cufftdx::size_of<FFT>::value == (cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n),\n                  \"FFT must have the same size as C matrix (MxN)\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == GEMM::block_dim.x) && (FFT::block_dim.y == GEMM::block_dim.y) &&\n                      (FFT::block_dim.z == GEMM::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n    static_assert(sizeof(fft_complex_type) == sizeof(blas_complex_type), \"FFT::value_type matches as GEMM::value_type\");\n\n    // Allocate managed memory\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n    complex_type* reference_output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    auto size       = (global_a_size + // a\n                 global_b_size + // b\n                 global_c_size + // c\n                 global_c_size + // output\n                 global_c_size   // reference_output\n    );\n    auto size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&buffer, size_bytes));\n    a                = buffer;\n    b                = a + global_a_size;\n    c                = b + global_b_size;\n    output           = c + global_c_size;\n    reference_output = output + global_c_size;\n\n    complex_type alpha = {float(1), float(1)};\n    complex_type beta  = {float(1), float(1)};\n\n    // Fill the a, b, c matrices\n    {\n        float base = cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n * cublasdx::size_of<GEMM>::k;\n        for (size_t i = 0; i < global_a_size; i++) {\n            a[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_b_size; i++) {\n            b[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_c_size; i++) {\n            c[i] = complex_type {float(1) / base, float(1) / base};\n        }\n    }\n\n    // Create stream\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(cudaGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemPrefetchAsync(buffer, size_bytes, cudaMemLocation {cudaMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    }\n\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<GEMM>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_fft_kernel<FFT, GEMM>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Invokes cuBLASDx+cuFFTDx kernel with FFT::block_dim threads in CUDA block\n    gemm_fft_kernel<FFT, GEMM><<<1, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // cuBLAS+cuFFT\n    reference<FFT, GEMM>(a, b, c, alpha, beta, reference_output, stream);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"[cuBLASDx + cuFFTDx]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << output[index].x << \", \" << output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << reference_output[index].x << \", \" << reference_output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(buffer));\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\nvoid reference(const ValueType* a,\n               const ValueType* b,\n               ValueType*       c,\n               const ValueType  alpha,\n               const ValueType  beta,\n               ValueType*       output,\n               hipStream_t     stream) {\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Prepare cuBLAS\n    hipblasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(hipblasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(hipblasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<GEMM>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<GEMM>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::b);\n    static_assert(cublasdx::arrangement_of<GEMM>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    // Prepare cuFFT\n    const unsigned int fft_size   = cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n;\n    const unsigned int batch_size = 1;\n\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Run cuBLAS\n    CUBLAS_CHECK_AND_EXIT(hipblasCgemm(handle,\n                                      a_transpose,\n                                      b_transpose,\n                                      m,\n                                      n,\n                                      k,\n                                      reinterpret_cast<const hipFloatComplex*>(&alpha),\n                                      reinterpret_cast<const hipFloatComplex*>(a),\n                                      is_a_transposed ? k : m,\n                                      reinterpret_cast<const hipFloatComplex*>(b),\n                                      is_b_transposed ? n : k,\n                                      reinterpret_cast<const hipFloatComplex*>(&beta),\n                                      reinterpret_cast<hipFloatComplex*>(c),\n                                      m));\n\n    // Run cuFFT\n    CUFFT_CHECK_AND_EXIT(\n        hipfftExecC2C(plan, reinterpret_cast<hipfftComplex*>(c), reinterpret_cast<hipfftComplex*>(output), HIPFFT_FORWARD));\n\n    CUBLAS_CHECK_AND_EXIT(hipblasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n}\n\ntemplate<class FFT>\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\ntemplate<class FFT, class GEMM, class ValueType = typename example::uniform_value_type_t<GEMM>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_kernel(const ValueType* a,\n                                                                              const ValueType* b,\n                                                                              const ValueType* c,\n                                                                              const ValueType  alpha,\n                                                                              const ValueType  beta,\n                                                                              ValueType*       output) {\n    using fft_complex_type  = typename FFT::value_type;\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n\n    static_assert(std::is_same_v<fft_complex_type, blas_complex_type>, \"BLAS and FFT complex type should match\");\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = GEMM::block_dim.x * GEMM::block_dim.y * GEMM::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    // cuBLASDx\n\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Load a, b, c from global to shared memory\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(reinterpret_cast<char*>(smem));\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // cuFFTDx\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from shared memory to registers\n    const unsigned int     offset = batch_offset<FFT>(local_fft_id, FFT::ffts_per_block);\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = smem_c[index];\n            index += stride;\n        }\n    }\n    __syncthreads();\n\n    // Execute FFT on registers\n    FFT().execute(thread_data, smem);\n\n    // Save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            output[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline. The kernel\n// computes the following operations:\n//     1) C = alpha * A * B + beta * C\n//     2) 1D FFT(C)\n// First the data of matrices A, B, and C is loaded into shared memory. After that the GEMM is executed, and 1D FFT\n// is applied to the results (matrix C). The results are stored back to global memory.\n//\n// Important notes:\n// * Results are verified against cuFFT and cuBLAS.\n// * FFT and GEMM must be compatible in terms of the requirements on block dimensions. cuFFTDx requirements are more firm, and\n//   therefore FFT::block_dim is enforced for GEMM.\n// * Shared memory required by the kernel is the max of the amount required by FFT and GEMM.\n// * For cuBLASDx GEMM description and cuFFTDx FFT description GEMM::value_type and FFT::value_type are the same types if:\n//     * GEMM has complex type set, and\n//     * Precision for GEMM and FFT is float or double.\ntemplate<unsigned int Arch>\nint gemm_fft() {\n    using precision_type = float;\n    static_assert(!std::is_same_v<precision_type, __half>, \"half precision is not supported in this example\");\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<64>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<precision_type>() +\n                         cufftdx::ElementsPerThread<2>() + cufftdx::FFTsPerBlock<1>() + cufftdx::SM<Arch>());\n\n    using GEMM =\n        decltype(cublasdx::Size<8, 8, 8>() + cublasdx::Precision<precision_type>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::col_major, cublasdx::col_major>() + cublasdx::Block() +\n                 cublasdx::BlockDim<FFT::block_dim.x, FFT::block_dim.y, FFT::block_dim.z>() + cublasdx::SM<Arch>());\n\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    static_assert(std::is_same_v<blas_complex_type, fft_complex_type>, \"BLAS and FFT complex types should be the same\");\n    using complex_type = blas_complex_type;\n\n    // Checking that FFT matches GEMM output\n    static_assert(cufftdx::size_of<FFT>::value == (cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n),\n                  \"FFT must have the same size as C matrix (MxN)\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == GEMM::block_dim.x) && (FFT::block_dim.y == GEMM::block_dim.y) &&\n                      (FFT::block_dim.z == GEMM::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n    static_assert(sizeof(fft_complex_type) == sizeof(blas_complex_type), \"FFT::value_type matches as GEMM::value_type\");\n\n    // Allocate managed memory\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n    complex_type* reference_output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    auto size       = (global_a_size + // a\n                 global_b_size + // b\n                 global_c_size + // c\n                 global_c_size + // output\n                 global_c_size   // reference_output\n    );\n    auto size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&buffer, size_bytes));\n    a                = buffer;\n    b                = a + global_a_size;\n    c                = b + global_b_size;\n    output           = c + global_c_size;\n    reference_output = output + global_c_size;\n\n    complex_type alpha = {float(1), float(1)};\n    complex_type beta  = {float(1), float(1)};\n\n    // Fill the a, b, c matrices\n    {\n        float base = cublasdx::size_of<GEMM>::m * cublasdx::size_of<GEMM>::n * cublasdx::size_of<GEMM>::k;\n        for (size_t i = 0; i < global_a_size; i++) {\n            a[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_b_size; i++) {\n            b[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_c_size; i++) {\n            c[i] = complex_type {float(1) / base, float(1) / base};\n        }\n    }\n\n    // Create stream\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(hipGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            hipMemPrefetchAsync(buffer, size_bytes, hipMemLocation {hipMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    }\n\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<GEMM>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_fft_kernel<FFT, GEMM>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Invokes cuBLASDx+cuFFTDx kernel with FFT::block_dim threads in CUDA block\n    gemm_fft_kernel<FFT, GEMM><<<1, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // cuBLAS+cuFFT\n    reference<FFT, GEMM>(a, b, c, alpha, beta, reference_output, stream);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"[cuBLASDx + cuFFTDx]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << output[index].x << \", \" << output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << reference_output[index].x << \", \" << reference_output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(buffer));\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/13_gemm_fft/gemm_fft.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Uncomment to enable correctness mode and verify correctness of the results\n// #define CORRECTNESS 1\n\n#ifdef CORRECTNESS\nconstexpr unsigned int repeats         = 1;\nconstexpr unsigned int warm_up_repeats = 0;\n#else\nconstexpr unsigned int repeats         = 100;\nconstexpr unsigned int warm_up_repeats = 5;\n#endif\n\ntemplate<typename T>\nstruct custom_unary_op {\n    __host__ __device__ T operator()(T x) const { return x * x; }\n};\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\ndouble measure_cublas_cufft(ValueType*         a,\n                            ValueType*         b,\n                            ValueType*         c,\n                            const ValueType    alpha,\n                            const ValueType    beta,\n                            ValueType*         output,\n                            const unsigned int batch_size,\n                            cudaStream_t       stream) {\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Prepare cuBLAS\n    cublasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(cublasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(cublasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<GEMM>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<GEMM>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::b);\n    static_assert(cublasdx::arrangement_of<GEMM>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Prepare cuFFT\n    const unsigned int fft_size = global_c_size;\n\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Create unary operation for transform\n    custom_unary_op<ValueType> unary_op;\n\n#if (THRUST_VERSION >= 101600)\n    auto execution_policy = thrust::cuda::par_nosync.on(stream);\n#else\n    auto execution_policy = thrust::cuda::par.on(stream)\n#endif\n\n    double time = example::measure::execution(\n        [&](cudaStream_t) {\n            // Transform output\n            thrust::transform(execution_policy, a, a + (global_a_size * batch_size), a, unary_op);\n            // Run cuBLAS\n            CUBLAS_CHECK_AND_EXIT(cublasCgemmStridedBatched(handle,\n                                                            a_transpose,\n                                                            b_transpose,\n                                                            m,\n                                                            n,\n                                                            k,\n                                                            reinterpret_cast<const cuFloatComplex*>(&alpha),\n                                                            reinterpret_cast<const cuFloatComplex*>(a),\n                                                            is_a_transposed ? k : m,\n                                                            global_a_size,\n                                                            reinterpret_cast<const cuFloatComplex*>(b),\n                                                            is_b_transposed ? n : k,\n                                                            global_b_size,\n                                                            reinterpret_cast<const cuFloatComplex*>(&beta),\n                                                            reinterpret_cast<cuFloatComplex*>(c),\n                                                            m,\n                                                            global_c_size,\n                                                            batch_size));\n            // Run cuFFT\n            CUFFT_CHECK_AND_EXIT(cufftExecC2C(\n                plan, reinterpret_cast<cufftComplex*>(c), reinterpret_cast<cufftComplex*>(output), CUFFT_FORWARD));\n            // Transform output\n            thrust::transform(execution_policy, output, output + (global_c_size * batch_size), output, unary_op);\n        },\n        warm_up_repeats,\n        repeats,\n        stream);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUBLAS_CHECK_AND_EXIT(cublasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    return time;\n}\n\n// Batch offset in a CUDA block (not global offset)\ntemplate<class FFT>\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    return ffts_per_block == 1 ? 0 : (cufftdx::size_of<FFT>::value * local_fft_id);\n}\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_kernel(const ValueType* a,\n                                                                              const ValueType* b,\n                                                                              const ValueType* c,\n                                                                              const ValueType  alpha,\n                                                                              const ValueType  beta,\n                                                                              ValueType*       output) {\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n    static_assert(std::is_same_v<fft_complex_type, blas_complex_type>, \"BLAS and FFT complex type should match\");\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = GEMM::block_dim.x * GEMM::block_dim.y * GEMM::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // cuBLASDx\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Select batch for this CUDA block\n    const value_type* batch_a      = a + (blockIdx.x * global_a_size);\n    const value_type* batch_b      = b + (blockIdx.x * global_b_size);\n    const value_type* batch_c      = c + (blockIdx.x * global_c_size);\n    value_type*       batch_output = output + (blockIdx.x * global_c_size);\n\n    auto a_global_tensor = cublasdx::make_tensor(batch_a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(batch_b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(batch_c, GEMM::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(reinterpret_cast<char*>(smem));\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    // Load a, b, c from global to shared memory\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Transform A\n    custom_unary_op<complex_type> unary_op;\n    auto                          transformer = [unary_op](int i, complex_type v) {\n        int r = i % cublasdx::leading_dimension_of<GEMM>::a;\n        return r < cublasdx::size_of<GEMM>::m ? unary_op(v) : v;\n    };\n    example::transform(smem_a, GEMM::a_size, transformer);\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // cuFFTDx\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from shared memory to registers\n    const unsigned int     offset = batch_offset<FFT>(local_fft_id, FFT::ffts_per_block);\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = smem_c[index];\n            index += stride;\n        }\n    }\n    __syncthreads();\n\n    // Execute FFT on registers\n    FFT().execute(thread_data, smem);\n\n    // Transform and save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            batch_output[index] = unary_op(thread_data[i]);\n            index += stride;\n        }\n    }\n}\n\ntemplate<class FFT, class GEMM, class ValueType = typename GEMM::value_type>\ndouble measure_cublasdx_cufftdx(const ValueType*   a,\n                                const ValueType*   b,\n                                ValueType*         c,\n                                const ValueType    alpha,\n                                const ValueType    beta,\n                                ValueType*         output,\n                                const unsigned int batch_size,\n                                cudaStream_t       stream) {\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<GEMM>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_fft_kernel<FFT, GEMM>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    double time = example::measure::execution(\n        [&](cudaStream_t stream) {\n            gemm_fft_kernel<FFT, GEMM>\n                <<<batch_size, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n        },\n        warm_up_repeats,\n        repeats,\n        stream);\n    return time;\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline. Additionally, to showcase\n// how fusing kernels can improve performance transforms operations are performed before and after GEMM and FFT. The kernel runs\n// the following operations:\n//     1) Transform(A)\n//     2) C = alpha * A * B + beta * C\n//     3) 1D FFT(C)\n//     4) Transform(C)\n//\n// First the data of matrices A, B, and C is loaded into shared memory. Matrix A is transformed. After that the GEMM is\n// executed, and 1D FFT is applied to the results (matrix C). The results are transformed and stored back to global memory.\n//\n// Also see example gemm_fft.cu.\ntemplate<unsigned int Arch>\nint gemm_fft() {\n#ifdef CORRECTNESS\n    constexpr unsigned int m          = 8;\n    constexpr unsigned int n          = 8;\n    constexpr unsigned int k          = 8;\n    constexpr unsigned int block_size = 32;\n#else\n    constexpr unsigned int m          = 16;\n    constexpr unsigned int n          = 16;\n    constexpr unsigned int k          = 16;\n    constexpr unsigned int block_size = 128;\n#endif\n    constexpr unsigned int fft_size = m * n;\n    static_assert((fft_size / block_size) >= 2, \"Block size is too big\");\n    constexpr unsigned int fft_ept = fft_size / block_size;\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<fft_size>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<float>() +\n                         cufftdx::ElementsPerThread<fft_ept>() + cufftdx::FFTsPerBlock<1>() + cufftdx::SM<Arch>());\n\n    using GEMM =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::complex>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::TransposeMode<cublasdx::transpose_mode::non_transposed,\n                                         cublasdx::transpose_mode::non_transposed>() +\n                 cublasdx::Block() + cublasdx::BlockDim<FFT::block_dim.x, FFT::block_dim.y, FFT::block_dim.z>() +\n                 cublasdx::SM<Arch>());\n\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    static_assert(std::is_same_v<blas_complex_type, fft_complex_type>, \"BLAS and FFT complex types should be the same\");\n    using complex_type = blas_complex_type;\n\n    // Checking that FFT matches GEMM output\n    static_assert(cufftdx::size_of<FFT>::value == (GEMM::c_size), \"FFT must have the same size as C matrix (MxN)\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == GEMM::block_dim.x) && (FFT::block_dim.y == GEMM::block_dim.y) &&\n                      (FFT::block_dim.z == GEMM::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n    static_assert(sizeof(fft_complex_type) == sizeof(blas_complex_type), \"FFT::value_type matches as GEMM::value_type\");\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Get single batch size\n    auto single_batch_size       = (global_a_size + // a\n                              global_b_size + // b\n                              global_c_size + // c\n                              global_c_size + // output\n                              global_c_size   // reference_output\n    );\n    auto single_batch_size_bytes = single_batch_size * sizeof(complex_type);\n\n#ifdef CORRECTNESS\n    const unsigned int batches = 2;\n#else\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      gemm_fft_kernel<FFT, GEMM>,\n                                                      FFT::block_dim.x * FFT::block_dim.y * FFT::block_dim.y,\n                                                      0));\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least 1GB of data\n    const unsigned int            minimum_batches =\n        (minimum_input_size_bytes - single_batch_size_bytes + 1) / single_batch_size_bytes;\n    const unsigned int blocks_per_device = blocks_per_multiprocessor * example::get_multiprocessor_count();\n    unsigned int       batches           = std::max({minimum_batches, blocks_per_device});\n    batches = blocks_per_device * ((batches + (blocks_per_device)-1) / (blocks_per_device));\n// batches = 1; // single batch test\n// batches = example::get_multiprocessor_count(); // few batches test\n#endif\n\n    // Allocate memory for a, b, c\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n    complex_type* reference_output;\n\n    auto size_bytes = batches * single_batch_size_bytes;\n#ifdef CORRECTNESS\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&buffer, size_bytes));\n#else\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&buffer, size_bytes));\n#endif\n    a                = buffer;\n    b                = a + (batches * global_a_size);\n    c                = b + (batches * global_b_size);\n    output           = c + (batches * global_c_size);\n    reference_output = output + (batches * global_a_size);\n\n    // Se alpha and beta for GEMM\n    complex_type alpha = example::make_value<complex_type>(1., 1.);\n    complex_type beta  = example::make_value<complex_type>(1., 1.);\n\n#ifdef CORRECTNESS\n    // Fill the a, b, c matrices\n    {\n        float base = global_c_size * cublasdx::size_of<GEMM>::k;\n        for (size_t i = 0; i < batches * global_a_size; i++) {\n            a[i] = example::make_value<complex_type>(float(i) / base, float(i) / base);\n        }\n        for (size_t i = 0; i < batches * global_b_size; i++) {\n            b[i] = example::make_value<complex_type>(float(i) / base, float(i) / base);\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            c[i] = example::make_value<complex_type>(float(1) / base, float(1) / base);\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            output[i] = example::make_value<complex_type>(float(-1), float(-1));\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            reference_output[i] = example::make_value<complex_type>(float(-1), float(-1));\n        }\n    }\n#endif\n\n    // Create stream\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n#ifdef CORRECTNESS\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(cudaGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemPrefetchAsync(a, size_bytes, cudaMemLocation {cudaMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    }\n#endif\n\n    double time_cublasdx_cufftdx = measure_cublasdx_cufftdx<FFT, GEMM>(a, b, c, alpha, beta, output, batches, stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    double time_cublas_cufft = measure_cublas_cufft<FFT, GEMM>(a, b, c, alpha, beta, reference_output, batches, stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n#ifdef CORRECTNESS\n    std::cout << \"[cuBLASDx + cuFFTDx]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << output[index].x << \", \" << output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << reference_output[index].x << \", \" << reference_output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n#endif\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / repeats << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n    };\n\n    std::cout << \"GEMM: \";\n    std::cout << cublasdx::size_of<GEMM>::m << \" x \" << cublasdx::size_of<GEMM>::n << \" x \"\n              << cublasdx::size_of<GEMM>::k << \"\\n\";\n    std::cout << \"FFT: \" << cufftdx::size_of<FFT>::value << \"\\n\";\n    std::cout << \"Batches: \" << batches << \"\\n\";\n    report_time_and_performance(\"cuBLASDx+cuFFTDx\", time_cublasdx_cufftdx);\n    report_time_and_performance(\"cuBLAS+cuFFT\", time_cublas_cufft);\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Uncomment to enable correctness mode and verify correctness of the results\n// #define CORRECTNESS 1\n\n#ifdef CORRECTNESS\nconstexpr unsigned int repeats         = 1;\nconstexpr unsigned int warm_up_repeats = 0;\n#else\nconstexpr unsigned int repeats         = 100;\nconstexpr unsigned int warm_up_repeats = 5;\n#endif\n\ntemplate<typename T>\nstruct custom_unary_op {\n    __host__ __device__ T operator()(T x) const { return x * x; }\n};\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\ndouble measure_cublas_cufft(ValueType*         a,\n                            ValueType*         b,\n                            ValueType*         c,\n                            const ValueType    alpha,\n                            const ValueType    beta,\n                            ValueType*         output,\n                            const unsigned int batch_size,\n                            hipStream_t       stream) {\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Prepare cuBLAS\n    hipblasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(hipblasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(hipblasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<GEMM>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<GEMM>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<GEMM>::b);\n    static_assert(cublasdx::arrangement_of<GEMM>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Prepare cuFFT\n    const unsigned int fft_size = global_c_size;\n\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Create unary operation for transform\n    custom_unary_op<ValueType> unary_op;\n\n#if (THRUST_VERSION >= 101600)\n    auto execution_policy = thrust::cuda::par_nosync.on(stream);\n#else\n    auto execution_policy = thrust::cuda::par.on(stream)\n#endif\n\n    double time = example::measure::execution(\n        [&](hipStream_t) {\n            // Transform output\n            thrust::transform(execution_policy, a, a + (global_a_size * batch_size), a, unary_op);\n            // Run cuBLAS\n            CUBLAS_CHECK_AND_EXIT(hipblasCgemmStridedBatched(handle,\n                                                            a_transpose,\n                                                            b_transpose,\n                                                            m,\n                                                            n,\n                                                            k,\n                                                            reinterpret_cast<const hipFloatComplex*>(&alpha),\n                                                            reinterpret_cast<const hipFloatComplex*>(a),\n                                                            is_a_transposed ? k : m,\n                                                            global_a_size,\n                                                            reinterpret_cast<const hipFloatComplex*>(b),\n                                                            is_b_transposed ? n : k,\n                                                            global_b_size,\n                                                            reinterpret_cast<const hipFloatComplex*>(&beta),\n                                                            reinterpret_cast<hipFloatComplex*>(c),\n                                                            m,\n                                                            global_c_size,\n                                                            batch_size));\n            // Run cuFFT\n            CUFFT_CHECK_AND_EXIT(hipfftExecC2C(\n                plan, reinterpret_cast<hipfftComplex*>(c), reinterpret_cast<hipfftComplex*>(output), HIPFFT_FORWARD));\n            // Transform output\n            thrust::transform(execution_policy, output, output + (global_c_size * batch_size), output, unary_op);\n        },\n        warm_up_repeats,\n        repeats,\n        stream);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUBLAS_CHECK_AND_EXIT(hipblasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    return time;\n}\n\n// Batch offset in a CUDA block (not global offset)\ntemplate<class FFT>\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    return ffts_per_block == 1 ? 0 : (cufftdx::size_of<FFT>::value * local_fft_id);\n}\n\ntemplate<class FFT, class GEMM, class ValueType = example::uniform_value_type_t<GEMM>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_kernel(const ValueType* a,\n                                                                              const ValueType* b,\n                                                                              const ValueType* c,\n                                                                              const ValueType  alpha,\n                                                                              const ValueType  beta,\n                                                                              ValueType*       output) {\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n    static_assert(std::is_same_v<fft_complex_type, blas_complex_type>, \"BLAS and FFT complex type should match\");\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = GEMM::block_dim.x * GEMM::block_dim.y * GEMM::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // cuBLASDx\n    constexpr auto m = cublasdx::size_of<GEMM>::m;\n    constexpr auto n = cublasdx::size_of<GEMM>::n;\n    constexpr auto k = cublasdx::size_of<GEMM>::k;\n\n    // Select batch for this CUDA block\n    const value_type* batch_a      = a + (blockIdx.x * global_a_size);\n    const value_type* batch_b      = b + (blockIdx.x * global_b_size);\n    const value_type* batch_c      = c + (blockIdx.x * global_c_size);\n    value_type*       batch_output = output + (blockIdx.x * global_c_size);\n\n    auto a_global_tensor = cublasdx::make_tensor(batch_a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(batch_b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(batch_c, GEMM::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(reinterpret_cast<char*>(smem));\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    // Load a, b, c from global to shared memory\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Transform A\n    custom_unary_op<complex_type> unary_op;\n    auto                          transformer = [unary_op](int i, complex_type v) {\n        int r = i % cublasdx::leading_dimension_of<GEMM>::a;\n        return r < cublasdx::size_of<GEMM>::m ? unary_op(v) : v;\n    };\n    example::transform(smem_a, GEMM::a_size, transformer);\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // cuFFTDx\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from shared memory to registers\n    const unsigned int     offset = batch_offset<FFT>(local_fft_id, FFT::ffts_per_block);\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = smem_c[index];\n            index += stride;\n        }\n    }\n    __syncthreads();\n\n    // Execute FFT on registers\n    FFT().execute(thread_data, smem);\n\n    // Transform and save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            batch_output[index] = unary_op(thread_data[i]);\n            index += stride;\n        }\n    }\n}\n\ntemplate<class FFT, class GEMM, class ValueType = typename GEMM::value_type>\ndouble measure_cublasdx_cufftdx(const ValueType*   a,\n                                const ValueType*   b,\n                                ValueType*         c,\n                                const ValueType    alpha,\n                                const ValueType    beta,\n                                ValueType*         output,\n                                const unsigned int batch_size,\n                                hipStream_t       stream) {\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<GEMM>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_fft_kernel<FFT, GEMM>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    double time = example::measure::execution(\n        [&](hipStream_t stream) {\n            gemm_fft_kernel<FFT, GEMM>\n                <<<batch_size, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n        },\n        warm_up_repeats,\n        repeats,\n        stream);\n    return time;\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline. Additionally, to showcase\n// how fusing kernels can improve performance transforms operations are performed before and after GEMM and FFT. The kernel runs\n// the following operations:\n//     1) Transform(A)\n//     2) C = alpha * A * B + beta * C\n//     3) 1D FFT(C)\n//     4) Transform(C)\n//\n// First the data of matrices A, B, and C is loaded into shared memory. Matrix A is transformed. After that the GEMM is\n// executed, and 1D FFT is applied to the results (matrix C). The results are transformed and stored back to global memory.\n//\n// Also see example gemm_fft.cu.\ntemplate<unsigned int Arch>\nint gemm_fft() {\n#ifdef CORRECTNESS\n    constexpr unsigned int m          = 8;\n    constexpr unsigned int n          = 8;\n    constexpr unsigned int k          = 8;\n    constexpr unsigned int block_size = 32;\n#else\n    constexpr unsigned int m          = 16;\n    constexpr unsigned int n          = 16;\n    constexpr unsigned int k          = 16;\n    constexpr unsigned int block_size = 128;\n#endif\n    constexpr unsigned int fft_size = m * n;\n    static_assert((fft_size / block_size) >= 2, \"Block size is too big\");\n    constexpr unsigned int fft_ept = fft_size / block_size;\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<fft_size>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<float>() +\n                         cufftdx::ElementsPerThread<fft_ept>() + cufftdx::FFTsPerBlock<1>() + cufftdx::SM<Arch>());\n\n    using GEMM =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::complex>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::TransposeMode<cublasdx::transpose_mode::non_transposed,\n                                         cublasdx::transpose_mode::non_transposed>() +\n                 cublasdx::Block() + cublasdx::BlockDim<FFT::block_dim.x, FFT::block_dim.y, FFT::block_dim.z>() +\n                 cublasdx::SM<Arch>());\n\n    using blas_complex_type = example::uniform_value_type_t<GEMM>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    static_assert(std::is_same_v<blas_complex_type, fft_complex_type>, \"BLAS and FFT complex types should be the same\");\n    using complex_type = blas_complex_type;\n\n    // Checking that FFT matches GEMM output\n    static_assert(cufftdx::size_of<FFT>::value == (GEMM::c_size), \"FFT must have the same size as C matrix (MxN)\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == GEMM::block_dim.x) && (FFT::block_dim.y == GEMM::block_dim.y) &&\n                      (FFT::block_dim.z == GEMM::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n    static_assert(sizeof(fft_complex_type) == sizeof(blas_complex_type), \"FFT::value_type matches as GEMM::value_type\");\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Get single batch size\n    auto single_batch_size       = (global_a_size + // a\n                              global_b_size + // b\n                              global_c_size + // c\n                              global_c_size + // output\n                              global_c_size   // reference_output\n    );\n    auto single_batch_size_bytes = single_batch_size * sizeof(complex_type);\n\n#ifdef CORRECTNESS\n    const unsigned int batches = 2;\n#else\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      gemm_fft_kernel<FFT, GEMM>,\n                                                      FFT::block_dim.x * FFT::block_dim.y * FFT::block_dim.y,\n                                                      0));\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least 1GB of data\n    const unsigned int            minimum_batches =\n        (minimum_input_size_bytes - single_batch_size_bytes + 1) / single_batch_size_bytes;\n    const unsigned int blocks_per_device = blocks_per_multiprocessor * example::get_multiprocessor_count();\n    unsigned int       batches           = std::max({minimum_batches, blocks_per_device});\n    batches = blocks_per_device * ((batches + (blocks_per_device)-1) / (blocks_per_device));\n// batches = 1; // single batch test\n// batches = example::get_multiprocessor_count(); // few batches test\n#endif\n\n    // Allocate memory for a, b, c\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n    complex_type* reference_output;\n\n    auto size_bytes = batches * single_batch_size_bytes;\n#ifdef CORRECTNESS\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&buffer, size_bytes));\n#else\n    CUDA_CHECK_AND_EXIT(hipMalloc(&buffer, size_bytes));\n#endif\n    a                = buffer;\n    b                = a + (batches * global_a_size);\n    c                = b + (batches * global_b_size);\n    output           = c + (batches * global_c_size);\n    reference_output = output + (batches * global_a_size);\n\n    // Se alpha and beta for GEMM\n    complex_type alpha = example::make_value<complex_type>(1., 1.);\n    complex_type beta  = example::make_value<complex_type>(1., 1.);\n\n#ifdef CORRECTNESS\n    // Fill the a, b, c matrices\n    {\n        float base = global_c_size * cublasdx::size_of<GEMM>::k;\n        for (size_t i = 0; i < batches * global_a_size; i++) {\n            a[i] = example::make_value<complex_type>(float(i) / base, float(i) / base);\n        }\n        for (size_t i = 0; i < batches * global_b_size; i++) {\n            b[i] = example::make_value<complex_type>(float(i) / base, float(i) / base);\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            c[i] = example::make_value<complex_type>(float(1) / base, float(1) / base);\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            output[i] = example::make_value<complex_type>(float(-1), float(-1));\n        }\n        for (size_t i = 0; i < batches * global_c_size; i++) {\n            reference_output[i] = example::make_value<complex_type>(float(-1), float(-1));\n        }\n    }\n#endif\n\n    // Create stream\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n#ifdef CORRECTNESS\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(hipGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            hipMemPrefetchAsync(a, size_bytes, hipMemLocation {hipMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    }\n#endif\n\n    double time_cublasdx_cufftdx = measure_cublasdx_cufftdx<FFT, GEMM>(a, b, c, alpha, beta, output, batches, stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    double time_cublas_cufft = measure_cublas_cufft<FFT, GEMM>(a, b, c, alpha, beta, reference_output, batches, stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n#ifdef CORRECTNESS\n    std::cout << \"[cuBLASDx + cuFFTDx]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << output[index].x << \", \" << output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT]:\\n\";\n    for (size_t i = 0; i < cublasdx::size_of<GEMM>::m; i++) {     // rows\n        for (size_t j = 0; j < cublasdx::size_of<GEMM>::n; j++) { // cols\n            auto index = i * cublasdx::size_of<GEMM>::n + j;\n            std::cout << \"[\" << reference_output[index].x << \", \" << reference_output[index].y << \"]\\t\";\n        }\n        std::cout << \"\\n\";\n    }\n#endif\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / repeats << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n    };\n\n    std::cout << \"GEMM: \";\n    std::cout << cublasdx::size_of<GEMM>::m << \" x \" << cublasdx::size_of<GEMM>::n << \" x \"\n              << cublasdx::size_of<GEMM>::k << \"\\n\";\n    std::cout << \"FFT: \" << cufftdx::size_of<FFT>::value << \"\\n\";\n    std::cout << \"Batches: \" << batches << \"\\n\";\n    report_time_and_performance(\"cuBLASDx+cuFFTDx\", time_cublasdx_cufftdx);\n    report_time_and_performance(\"cuBLAS+cuFFT\", time_cublas_cufft);\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/13_gemm_fft/gemm_fft_performance.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iomanip>\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Batch size (number of signals to process)\nconstexpr unsigned int batch_size = 2;\n\n// Load two batches of complex<__half> data of size \"Size\" from shared or global memory to registers as complex<__half2>.\n// The source data is laid out in natural order, first for batch 1 and then for batch 2. The register memory layout follows\n// the interleaved format \"(real1, real2), (imag1, imag2), ...\", where 1 and 2 represent the first and second batch\n// respectively. This function is useful for performing half-precision FFTs using cuFFTDx.\ntemplate<unsigned int EPT, unsigned int Size, unsigned int Stride, template<class> class T, template<class> class U>\ninline __device__ void fft_load(const T<__half>* A, U<__half2>* thread_data) {\n    const unsigned int tid   = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * (blockDim.x * blockDim.y);\n    unsigned int       index = tid;\n    for (unsigned int i = 0; i < EPT; i++) {\n        if (index < Size) {\n            auto v1        = A[index].xy;\n            auto v2        = A[index + Size].xy;\n            thread_data[i] = {{v1.x, v2.x}, {v1.y, v2.y}};\n        }\n        index += Stride;\n    }\n}\n\n\n// Store batched complex<__half2> data from registers to shared or global memory into two batches of complex<__half> data of\n// size \"Size\". The register memory layout follows the interleaved format \"(real1, real2), (imag1, imag2), ...\", where\n// 1 and 2 represent the first and second batch respectively. The destination data is laid out in natural order, first for\n// batch 1 and then for batch 2. This function is useful for performing half-precision FFTs using cuFFTDx.\ntemplate<unsigned int EPT, unsigned int Size, unsigned int Stride, template<class> class U, template<class> class T>\ninline __device__ void fft_store(U<__half2>* thread_data, T<__half>* A) {\n    const unsigned int tid   = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * (blockDim.x * blockDim.y);\n    unsigned int       index = tid;\n    for (unsigned int i = 0; i < EPT; i++) {\n        if (index < Size) {\n            A[index]        = __lows2half2(thread_data[i].x, thread_data[i].y);\n            A[index + Size] = __highs2half2(thread_data[i].x, thread_data[i].y);\n        }\n        index += Stride;\n    }\n}\n\ntemplate<class FFT, class BLAS, class ValueType = cublasdx::complex<float>>\nvoid reference(const ValueType* a,\n               ValueType*       b,\n               ValueType*       c,\n               const ValueType  alpha,\n               const ValueType  beta,\n               ValueType*       output,\n               cudaStream_t     stream) {\n    constexpr auto m = cublasdx::size_of<BLAS>::m;\n    constexpr auto n = cublasdx::size_of<BLAS>::n;\n    constexpr auto k = cublasdx::size_of<BLAS>::k;\n\n    // Prepare cuFFT\n    const unsigned int fft_size = cublasdx::size_of<BLAS>::k;\n\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Run cuFFT\n    CUFFT_CHECK_AND_EXIT(\n        cufftExecC2C(plan, reinterpret_cast<cufftComplex*>(b), reinterpret_cast<cufftComplex*>(b), CUFFT_FORWARD));\n\n    // Prepare cuBLAS\n    cublasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(cublasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(cublasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<BLAS>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<BLAS>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS>::b);\n    static_assert(cublasdx::arrangement_of<BLAS>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    // Run cuBLAS\n    thrust::copy(c, c + m * n, output);\n\n    CUBLAS_CHECK_AND_EXIT(cublasCgemm(handle,\n                                      a_transpose,\n                                      b_transpose,\n                                      m,\n                                      n,\n                                      k,\n                                      reinterpret_cast<const cuFloatComplex*>(&alpha),\n                                      reinterpret_cast<const cuFloatComplex*>(a),\n                                      is_a_transposed ? k : m,\n                                      reinterpret_cast<const cuFloatComplex*>(b),\n                                      is_b_transposed ? n : k,\n                                      reinterpret_cast<const cuFloatComplex*>(&beta),\n                                      reinterpret_cast<cuFloatComplex*>(output),\n                                      m));\n\n    CUBLAS_CHECK_AND_EXIT(cublasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n}\n\ntemplate<class FFT, class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_fp16_kernel(const ValueType* a,\n                                                                                   const ValueType* b,\n                                                                                   const ValueType* c,\n                                                                                   const ValueType  alpha,\n                                                                                   const ValueType  beta,\n                                                                                   ValueType*       output) {\n    using blas_complex_type = example::uniform_value_type_t<BLAS>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = BLAS::block_dim.x * BLAS::block_dim.y * BLAS::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(reinterpret_cast<char*>(smem));\n\n    // Compute FFT(B, axis=0).\n    fft_complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory into registers for FFT, converting to RRII form from RIRI.\n    fft_load<FFT::elements_per_thread, cufftdx::size_of<FFT>::value, FFT::stride>(b, thread_data);\n    __syncthreads();\n\n    // Execute batched FFT on registers.\n    FFT().execute(thread_data, reinterpret_cast<fft_complex_type*>(smem));\n    __syncthreads();\n\n    // Store register data into smem_b, converting back to RIRI form from RRII.\n    fft_store<FFT::elements_per_thread, cufftdx::size_of<FFT>::value, FFT::stride>(thread_data, smem_b);\n\n    // Compute C := alpha * A @ FFT(B, axis=0) + beta * C.\n\n    // Load A and C from global to shared memory, B is already in shared memory after the FFT\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM: C = alpha * A @ FFT(B, axis=0) + beta * C.\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // Store the results.\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline for complex half-precision\n// data. A key goal is to illustrate the differences in how cuFFTDx and cuBLASDx handle complex half-precision.\n//\n// The kernel computes the following operations:\n//     1) FFT(B, axis=0)\n//     2) C = alpha * A @ B + beta * C\n//\n// This sequence of operations can represent reweighting of signals in the frequency domain, for example.\n//\n// We first load B from global memory into registers, interleaving data from the two batches in RRII format. The FFT operation is\n// implicitly batched in this case, with the two batches being processed in one execution. The results are then stored into shared\n// memory in preparation for the GEMM, with the data being converted back to RIRI format. Next the matrices A and C are loaded\n// into shared memory, and the GEMM (which is in fact a batched GEMV) is executed. The results are stored back to global memory.\n//\n// Important notes:\n// * Results are verified against cuFFT and cuBLAS.\n// * This example is written only for a batch size of 2 and for complex half-precision type.\n// * The type used by cuFFTDx is complex<__half2> while the type used by cuBLASDx is complex<half>.\n// * Shared memory required by the kernel is the max of the amount required by FFT and GEMM.\ntemplate<unsigned int Arch>\nint gemm_fft_fp16() {\n    using precision_type     = __half;\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = batch_size;\n    constexpr unsigned int k = m;\n\n    static_assert(batch_size == 2, \"This example only supports a batch size of 2.\");\n    static_assert(std::is_same_v<precision_type, __half>, \"This example only supports half-precision.\");\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<k>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<precision_type>() +\n                         cufftdx::ElementsPerThread<2>() + cufftdx::FFTsPerBlock<batch_size>() + cufftdx::SM<Arch>());\n\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision_type>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::col_major, cublasdx::col_major>() + cublasdx::Block() +\n                          cublasdx::BlockDim<FFT::block_dim.x>() + cublasdx::SM<Arch>());\n\n    using fft_complex_type  = typename FFT::value_type;\n    using blas_complex_type = example::uniform_value_type_t<BLAS>;\n    using complex_type      = blas_complex_type;\n\n    // Check that FFT matches GEMM dimensions.\n    static_assert(cufftdx::size_of<FFT>::value == cublasdx::size_of<BLAS>::k,\n                  \"FFT must have the same size as the GEMM k dimension\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == BLAS::block_dim.x) && (FFT::block_dim.y == BLAS::block_dim.y) &&\n                      (FFT::block_dim.z == BLAS::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n\n    // Allocate managed memory.\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto size       = (global_a_size + // a\n                 global_b_size + // b\n                 global_c_size + // c\n                 global_c_size   // output\n    );\n    auto size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&buffer, size_bytes));\n    a      = buffer;\n    b      = a + global_a_size;\n    c      = b + global_b_size;\n    output = c + global_c_size;\n\n    complex_type alpha = {float(1), float(0)};\n    complex_type beta  = {float(0), float(0)};\n\n    // Fill the a, b, c matrices.\n    {\n        float base = cublasdx::size_of<BLAS>::m * cublasdx::size_of<BLAS>::k;\n        for (size_t i = 0; i < global_a_size; i++) {\n            a[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_b_size; i++) {\n            b[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_c_size; i++) {\n            c[i] = complex_type {float(1) / base, float(1) / base};\n        }\n    }\n\n    // Create stream\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(cudaGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemPrefetchAsync(buffer, size_bytes, cudaMemLocation {cudaMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    }\n\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<BLAS>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_fft_fp16_kernel<FFT, BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Invokes cuBLASDx+cuFFTDx kernel with FFT::block_dim threads in CUDA block\n    gemm_fft_fp16_kernel<FFT, BLAS><<<1, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Compute reference results using cuBLAS and cuFFT (with float32 precision).\n    using reference_complex_type = cublasdx::complex<float>;\n    auto reference_size_bytes    = size * sizeof(reference_complex_type);\n\n    reference_complex_type* reference_buffer;\n    reference_complex_type* reference_a;\n    reference_complex_type* reference_b;\n    reference_complex_type* reference_c;\n    reference_complex_type* reference_output;\n\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&reference_buffer, reference_size_bytes));\n    reference_a      = reference_buffer;\n    reference_b      = reference_a + global_a_size;\n    reference_c      = reference_b + global_b_size;\n    reference_output = reference_c + global_c_size;\n\n    reference_complex_type reference_alpha {alpha.real(), alpha.imag()};\n    reference_complex_type reference_beta {beta.real(), beta.imag()};\n\n    // Copy a, b, and c to the corresponding reference data buffers.\n    thrust::copy(a, a + global_a_size, reference_a);\n    thrust::copy(b, b + global_b_size, reference_b);\n    thrust::copy(c, c + global_c_size, reference_c);\n\n    // cuBLAS+cuFFT\n    reference<FFT, BLAS>(\n        reference_a, reference_b, reference_c, reference_alpha, reference_beta, reference_output, stream);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Print results.\n    std::cout << std::fixed << std::showpos << std::setprecision(4);\n    std::cout << \"[cuBLASDx + cuFFTDx] (float16):\\n\";\n    std::cout << \"     Batch 1             Batch 2   \\n\";\n    for (size_t i = 0; i < m; i++) {\n        for (size_t j = 0; j < n; j++) {\n            auto index = i + j * m;\n            std::cout << \"[\" << float(output[index].real()) << \", \" << float(output[index].imag()) << \"]  \";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT] (float32):\\n\";\n    std::cout << \"     Batch 1             Batch 2   \\n\";\n    for (size_t i = 0; i < m; i++) {\n        for (size_t j = 0; j < n; j++) {\n            auto index = i + j * m;\n            std::cout << \"[\" << float(reference_output[index].x) << \", \" << float(reference_output[index].y) << \"]  \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(buffer));\n    CUDA_CHECK_AND_EXIT(cudaFree(reference_buffer));\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_fp16_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft_fp16<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_fp16_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iomanip>\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n\n#include <cublasdx.hpp>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Batch size (number of signals to process)\nconstexpr unsigned int batch_size = 2;\n\n// Load two batches of complex<__half> data of size \"Size\" from shared or global memory to registers as complex<__half2>.\n// The source data is laid out in natural order, first for batch 1 and then for batch 2. The register memory layout follows\n// the interleaved format \"(real1, real2), (imag1, imag2), ...\", where 1 and 2 represent the first and second batch\n// respectively. This function is useful for performing half-precision FFTs using cuFFTDx.\ntemplate<unsigned int EPT, unsigned int Size, unsigned int Stride, template<class> class T, template<class> class U>\ninline __device__ void fft_load(const T<__half>* A, U<__half2>* thread_data) {\n    const unsigned int tid   = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * (blockDim.x * blockDim.y);\n    unsigned int       index = tid;\n    for (unsigned int i = 0; i < EPT; i++) {\n        if (index < Size) {\n            auto v1        = A[index].xy;\n            auto v2        = A[index + Size].xy;\n            thread_data[i] = {{v1.x, v2.x}, {v1.y, v2.y}};\n        }\n        index += Stride;\n    }\n}\n\n\n// Store batched complex<__half2> data from registers to shared or global memory into two batches of complex<__half> data of\n// size \"Size\". The register memory layout follows the interleaved format \"(real1, real2), (imag1, imag2), ...\", where\n// 1 and 2 represent the first and second batch respectively. The destination data is laid out in natural order, first for\n// batch 1 and then for batch 2. This function is useful for performing half-precision FFTs using cuFFTDx.\ntemplate<unsigned int EPT, unsigned int Size, unsigned int Stride, template<class> class U, template<class> class T>\ninline __device__ void fft_store(U<__half2>* thread_data, T<__half>* A) {\n    const unsigned int tid   = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * (blockDim.x * blockDim.y);\n    unsigned int       index = tid;\n    for (unsigned int i = 0; i < EPT; i++) {\n        if (index < Size) {\n            A[index]        = __lows2half2(thread_data[i].x, thread_data[i].y);\n            A[index + Size] = __highs2half2(thread_data[i].x, thread_data[i].y);\n        }\n        index += Stride;\n    }\n}\n\ntemplate<class FFT, class BLAS, class ValueType = cublasdx::complex<float>>\nvoid reference(const ValueType* a,\n               ValueType*       b,\n               ValueType*       c,\n               const ValueType  alpha,\n               const ValueType  beta,\n               ValueType*       output,\n               hipStream_t     stream) {\n    constexpr auto m = cublasdx::size_of<BLAS>::m;\n    constexpr auto n = cublasdx::size_of<BLAS>::n;\n    constexpr auto k = cublasdx::size_of<BLAS>::k;\n\n    // Prepare cuFFT\n    const unsigned int fft_size = cublasdx::size_of<BLAS>::k;\n\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Run cuFFT\n    CUFFT_CHECK_AND_EXIT(\n        hipfftExecC2C(plan, reinterpret_cast<hipfftComplex*>(b), reinterpret_cast<hipfftComplex*>(b), HIPFFT_FORWARD));\n\n    // Prepare cuBLAS\n    hipblasHandle_t handle;\n    CUBLAS_CHECK_AND_EXIT(hipblasCreate(&handle));\n    CUBLAS_CHECK_AND_EXIT(hipblasSetStream(handle, stream));\n    constexpr bool is_a_transposed = (cublasdx::arrangement_of<BLAS>::a == cublasdx::row_major);\n    constexpr bool is_b_transposed = (cublasdx::arrangement_of<BLAS>::b == cublasdx::row_major);\n    const auto     a_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS>::a);\n    const auto     b_transpose     = example::get_cublas_transpose_mode(cublasdx::arrangement_of<BLAS>::b);\n    static_assert(cublasdx::arrangement_of<BLAS>::c == cublasdx::arrangement::col_major,\n                  \"Only column-major C matrix supported\");\n\n    // Run cuBLAS\n    thrust::copy(c, c + m * n, output);\n\n    CUBLAS_CHECK_AND_EXIT(hipblasCgemm(handle,\n                                      a_transpose,\n                                      b_transpose,\n                                      m,\n                                      n,\n                                      k,\n                                      reinterpret_cast<const hipFloatComplex*>(&alpha),\n                                      reinterpret_cast<const hipFloatComplex*>(a),\n                                      is_a_transposed ? k : m,\n                                      reinterpret_cast<const hipFloatComplex*>(b),\n                                      is_b_transposed ? n : k,\n                                      reinterpret_cast<const hipFloatComplex*>(&beta),\n                                      reinterpret_cast<hipFloatComplex*>(output),\n                                      m));\n\n    CUBLAS_CHECK_AND_EXIT(hipblasDestroy(handle));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n}\n\ntemplate<class FFT, class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void gemm_fft_fp16_kernel(const ValueType* a,\n                                                                                   const ValueType* b,\n                                                                                   const ValueType* c,\n                                                                                   const ValueType  alpha,\n                                                                                   const ValueType  beta,\n                                                                                   ValueType*       output) {\n    using blas_complex_type = example::uniform_value_type_t<BLAS>;\n    using fft_complex_type  = typename FFT::value_type;\n\n    using complex_type                = blas_complex_type;\n    using value_type                  = ValueType;\n    constexpr unsigned int block_size = BLAS::block_dim.x * BLAS::block_dim.y * BLAS::block_dim.z;\n\n    extern __shared__ __align__(alignof(complex_type)) complex_type smem[];\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(reinterpret_cast<char*>(smem));\n\n    // Compute FFT(B, axis=0).\n    fft_complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory into registers for FFT, converting to RRII form from RIRI.\n    fft_load<FFT::elements_per_thread, cufftdx::size_of<FFT>::value, FFT::stride>(b, thread_data);\n    __syncthreads();\n\n    // Execute batched FFT on registers.\n    FFT().execute(thread_data, reinterpret_cast<fft_complex_type*>(smem));\n    __syncthreads();\n\n    // Store register data into smem_b, converting back to RIRI form from RRII.\n    fft_store<FFT::elements_per_thread, cufftdx::size_of<FFT>::value, FFT::stride>(thread_data, smem_b);\n\n    // Compute C := alpha * A @ FFT(B, axis=0) + beta * C.\n\n    // Load A and C from global to shared memory, B is already in shared memory after the FFT\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM: C = alpha * A @ FFT(B, axis=0) + beta * C.\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // Store the results.\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// In this example cuBLASDx and cuFFTDx libraries are combined to perform GEMM and FFT in one pipeline for complex half-precision\n// data. A key goal is to illustrate the differences in how cuFFTDx and cuBLASDx handle complex half-precision.\n//\n// The kernel computes the following operations:\n//     1) FFT(B, axis=0)\n//     2) C = alpha * A @ B + beta * C\n//\n// This sequence of operations can represent reweighting of signals in the frequency domain, for example.\n//\n// We first load B from global memory into registers, interleaving data from the two batches in RRII format. The FFT operation is\n// implicitly batched in this case, with the two batches being processed in one execution. The results are then stored into shared\n// memory in preparation for the GEMM, with the data being converted back to RIRI format. Next the matrices A and C are loaded\n// into shared memory, and the GEMM (which is in fact a batched GEMV) is executed. The results are stored back to global memory.\n//\n// Important notes:\n// * Results are verified against cuFFT and cuBLAS.\n// * This example is written only for a batch size of 2 and for complex half-precision type.\n// * The type used by cuFFTDx is complex<__half2> while the type used by cuBLASDx is complex<half>.\n// * Shared memory required by the kernel is the max of the amount required by FFT and GEMM.\ntemplate<unsigned int Arch>\nint gemm_fft_fp16() {\n    using precision_type     = __half;\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = batch_size;\n    constexpr unsigned int k = m;\n\n    static_assert(batch_size == 2, \"This example only supports a batch size of 2.\");\n    static_assert(std::is_same_v<precision_type, __half>, \"This example only supports half-precision.\");\n\n    using FFT = decltype(cufftdx::Block() + cufftdx::Size<k>() + cufftdx::Type<cufftdx::fft_type::c2c>() +\n                         cufftdx::Direction<cufftdx::fft_direction::forward>() + cufftdx::Precision<precision_type>() +\n                         cufftdx::ElementsPerThread<2>() + cufftdx::FFTsPerBlock<batch_size>() + cufftdx::SM<Arch>());\n\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision_type>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::col_major, cublasdx::col_major>() + cublasdx::Block() +\n                          cublasdx::BlockDim<FFT::block_dim.x>() + cublasdx::SM<Arch>());\n\n    using fft_complex_type  = typename FFT::value_type;\n    using blas_complex_type = example::uniform_value_type_t<BLAS>;\n    using complex_type      = blas_complex_type;\n\n    // Check that FFT matches GEMM dimensions.\n    static_assert(cufftdx::size_of<FFT>::value == cublasdx::size_of<BLAS>::k,\n                  \"FFT must have the same size as the GEMM k dimension\");\n    // Checking that block dims match\n    static_assert((FFT::block_dim.x == BLAS::block_dim.x) && (FFT::block_dim.y == BLAS::block_dim.y) &&\n                      (FFT::block_dim.z == BLAS::block_dim.z),\n                  \"FFT must require the same CUDA block dimenions as GEMM\");\n\n    // Allocate managed memory.\n    complex_type* buffer;\n    complex_type* a;\n    complex_type* b;\n    complex_type* c;\n    complex_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto size       = (global_a_size + // a\n                 global_b_size + // b\n                 global_c_size + // c\n                 global_c_size   // output\n    );\n    auto size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&buffer, size_bytes));\n    a      = buffer;\n    b      = a + global_a_size;\n    c      = b + global_b_size;\n    output = c + global_c_size;\n\n    complex_type alpha = {float(1), float(0)};\n    complex_type beta  = {float(0), float(0)};\n\n    // Fill the a, b, c matrices.\n    {\n        float base = cublasdx::size_of<BLAS>::m * cublasdx::size_of<BLAS>::k;\n        for (size_t i = 0; i < global_a_size; i++) {\n            a[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_b_size; i++) {\n            b[i] = complex_type {float(i) / base, float(i) / base};\n        }\n        for (size_t i = 0; i < global_c_size; i++) {\n            c[i] = complex_type {float(1) / base, float(1) / base};\n        }\n    }\n\n    // Create stream\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Prefetch memory to device\n    {\n        int device;\n        CUDA_CHECK_AND_EXIT(hipGetDevice(&device));\n        CUDA_CHECK_AND_EXIT(\n            hipMemPrefetchAsync(buffer, size_bytes, hipMemLocation {hipMemLocationTypeDevice, device}, 0, stream));\n        CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n        CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    }\n\n    // Get max shared memory required by FFT and GEMM\n    constexpr auto shared_memory_size = std::max({FFT::shared_memory_size, cublasdx::get_shared_storage_size<BLAS>()});\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_fft_fp16_kernel<FFT, BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Invokes cuBLASDx+cuFFTDx kernel with FFT::block_dim threads in CUDA block\n    gemm_fft_fp16_kernel<FFT, BLAS><<<1, FFT::block_dim, shared_memory_size, stream>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Compute reference results using cuBLAS and cuFFT (with float32 precision).\n    using reference_complex_type = cublasdx::complex<float>;\n    auto reference_size_bytes    = size * sizeof(reference_complex_type);\n\n    reference_complex_type* reference_buffer;\n    reference_complex_type* reference_a;\n    reference_complex_type* reference_b;\n    reference_complex_type* reference_c;\n    reference_complex_type* reference_output;\n\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&reference_buffer, reference_size_bytes));\n    reference_a      = reference_buffer;\n    reference_b      = reference_a + global_a_size;\n    reference_c      = reference_b + global_b_size;\n    reference_output = reference_c + global_c_size;\n\n    reference_complex_type reference_alpha {alpha.real(), alpha.imag()};\n    reference_complex_type reference_beta {beta.real(), beta.imag()};\n\n    // Copy a, b, and c to the corresponding reference data buffers.\n    thrust::copy(a, a + global_a_size, reference_a);\n    thrust::copy(b, b + global_b_size, reference_b);\n    thrust::copy(c, c + global_c_size, reference_c);\n\n    // cuBLAS+cuFFT\n    reference<FFT, BLAS>(\n        reference_a, reference_b, reference_c, reference_alpha, reference_beta, reference_output, stream);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Print results.\n    std::cout << std::fixed << std::showpos << std::setprecision(4);\n    std::cout << \"[cuBLASDx + cuFFTDx] (float16):\\n\";\n    std::cout << \"     Batch 1             Batch 2   \\n\";\n    for (size_t i = 0; i < m; i++) {\n        for (size_t j = 0; j < n; j++) {\n            auto index = i + j * m;\n            std::cout << \"[\" << float(output[index].real()) << \", \" << float(output[index].imag()) << \"]  \";\n        }\n        std::cout << \"\\n\";\n    }\n    std::cout << \"[cuBLAS + cuFFT] (float32):\\n\";\n    std::cout << \"     Batch 1             Batch 2   \\n\";\n    for (size_t i = 0; i < m; i++) {\n        for (size_t j = 0; j < n; j++) {\n            auto index = i + j * m;\n            std::cout << \"[\" << float(reference_output[index].x) << \", \" << float(reference_output[index].y) << \"]  \";\n        }\n        std::cout << \"\\n\";\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(buffer));\n    CUDA_CHECK_AND_EXIT(hipFree(reference_buffer));\n\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct gemm_fft_fp16_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_fft_fp16<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(gemm_fft_fp16_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/13_gemm_fft/gemm_fft_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of complex fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing complex half precision floating-point values.\n// * alpha and beta are complex half precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is automatically\n// selected by cuBLASDx. Setting operator BlockDim in the GEMM definition can be used to impose the\n// number of threads that the GEMM will be performed with. Block dimensions are provided via\n// BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 64;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value using alias MaxAlignment.\n    //    - Optional\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::MaxAlignment() +\n                          cublasdx::Block() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0, 1.0);\n    value_type  beta  = value_type(2.0, 2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of complex fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing complex half precision floating-point values.\n// * alpha and beta are complex half precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is automatically\n// selected by cuBLASDx. Setting operator BlockDim in the GEMM definition can be used to impose the\n// number of threads that the GEMM will be performed with. Block dimensions are provided via\n// BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 64;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value using alias MaxAlignment.\n    //    - Optional\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() +\n                          cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::MaxAlignment() +\n                          cublasdx::Block() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0, 1.0);\n    value_type  beta  = value_type(2.0, 2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/03_gemm_complex/simple_gemm_cfp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda/std/complex>\n#if _LIBCUDACXX_CUDA_API_VERSION < 001007000\nint main(int, char**) {\n    std::cout << \"Example disabled, cuBLASDx requires cuda::std::complex from libcu++ 1.7.0 (CTK 11.6) or newer\"\n              << std::endl;\n    return 0;\n}\n#else\n\n#    include <cuda_runtime_api.h>\n#    include <cublasdx.hpp>\n\n#    include \"../common/common.hpp\"\n#    include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_kernel(const ValueType* a,\n                                                                           const ValueType* b,\n                                                                           const ValueType* c,\n                                                                           const ValueType  alpha,\n                                                                           const ValueType  beta,\n                                                                           ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Note: here we can not use slice_shared_memory since\n    // BLAS::a_value_type = cublasdx::complex<precision>, not cuda::std::complex<precision>;\n    value_type* smem_a = reinterpret_cast<value_type*>(smem);\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS::a_size;\n    value_type* smem_c = reinterpret_cast<value_type*>(smem) + BLAS::a_size + BLAS::b_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of a complex general matrix-matrix multiplication (GEMM) performed in a single\n// CUDA block using cuda::std::complex instead of cublasdx::complex.\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing complex floating-point values of the specified precision.\n// * alpha and beta are complex floating-point values also of the specified precision.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is automatically\n// selected by cuBLASDx. Setting operator BlockDim in the GEMM definition can be used to impose the\n// number of threads that the GEMM will be performed with. Block dimensions are provided via\n// BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 32;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 32;\n\n    // Specify the precision for the complex type.\n    using precision = float;\n\n    // Use cuda::std::complex as the complex type.\n    using value_type = cuda::std::complex<precision>;\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto arrangement_a = cublasdx::col_major;\n    constexpr auto arrangement_b = cublasdx::row_major;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<arrangement_a, arrangement_b>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0, 1.0);\n    value_type  beta  = value_type(2.0, 2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(gemm_kernel<BLAS, value_type>,\n                                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS, value_type>\n        <<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n#endif\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <cuda/std/complex>\n#if _LIBCUDACXX_CUDA_API_VERSION < 001007000\nint main(int, char**) {\n    std::cout << \"Example disabled, cuBLASDx requires cuda::std::complex from libcu++ 1.7.0 (CTK 11.6) or newer\"\n              << std::endl;\n    return 0;\n}\n#else\n\n#    include <hip/hip_runtime_api.h>\n#    include <cublasdx.hpp>\n\n#    include \"../common/common.hpp\"\n#    include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) __global__ void gemm_kernel(const ValueType* a,\n                                                                           const ValueType* b,\n                                                                           const ValueType* c,\n                                                                           const ValueType  alpha,\n                                                                           const ValueType  beta,\n                                                                           ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Note: here we can not use slice_shared_memory since\n    // BLAS::a_value_type = cublasdx::complex<precision>, not cuda::std::complex<precision>;\n    value_type* smem_a = reinterpret_cast<value_type*>(smem);\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + BLAS::a_size;\n    value_type* smem_c = reinterpret_cast<value_type*>(smem) + BLAS::a_size + BLAS::b_size;\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of a complex general matrix-matrix multiplication (GEMM) performed in a single\n// CUDA block using cuda::std::complex instead of cublasdx::complex.\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing complex floating-point values of the specified precision.\n// * alpha and beta are complex floating-point values also of the specified precision.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is automatically\n// selected by cuBLASDx. Setting operator BlockDim in the GEMM definition can be used to impose the\n// number of threads that the GEMM will be performed with. Block dimensions are provided via\n// BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 32;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 32;\n\n    // Specify the precision for the complex type.\n    using precision = float;\n\n    // Use cuda::std::complex as the complex type.\n    using value_type = cuda::std::complex<precision>;\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto arrangement_a = cublasdx::col_major;\n    constexpr auto arrangement_b = cublasdx::row_major;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision>() +\n                 cublasdx::Type<cublasdx::type::complex>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<arrangement_a, arrangement_b>() + cublasdx::Block() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0, 1.0);\n    value_type  beta  = value_type(2.0, 2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS, value_type>),\n                                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<BLAS, value_type>\n        <<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n#endif\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/03_gemm_complex/simple_gemm_std_complex_fp32.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n\n#include <cuda_runtime_api.h>\n\n#define CUBLASDX_IGNORE_NVBUG_5218000_ASSERT\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n\n// Requirements of the transform ops functors:\n// * The input type must be the same as the value type of the corresponding matrix.\n// * The return type must be convertible to the value type of the corresponding matrix.\nstruct negate {\n    template<class T>\n    __device__ __host__ constexpr T operator()(const T arg) const {\n        return -arg;\n    }\n};\n\ntemplate<unsigned N>\nstruct multiply_by {\n    template<class T>\n    __device__ __host__ constexpr T operator()(const T arg) const {\n        return static_cast<T>(N) * arg;\n    }\n};\n\ntemplate<class BLAS,\n         class ValueType = typename example::uniform_value_type_t<BLAS>,\n         class ALoadOp   = cublasdx::identity,\n         class BLoadOp   = cublasdx::identity,\n         class CLoadOp   = cublasdx::identity,\n         class CStoreOp  = cublasdx::identity>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output,\n                     const ALoadOp&   a_load_op  = {},\n                     const BLoadOp&   b_load_op  = {},\n                     const CLoadOp&   c_load_op  = {},\n                     const CStoreOp&  c_store_op = {}) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor, a_load_op, b_load_op);\n    auto result_fragment = accumulator.get_results();\n\n    // Make fragment, partition global tensor and copy\n    auto d_frag = accumulator.make_partition_and_copy(c_global_tensor);\n\n    cublasdx::transform_fragment(d_frag, c_load_op);\n    cublasdx::axpby(alpha, result_fragment, beta, d_frag);\n    cublasdx::transform_fragment(d_frag, c_store_op);\n    accumulator.partition_and_copy(d_frag, out_global_tensor);\n}\n\n// This is an example of fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = op_c_store(alpha * op_a(A) * op_b(B) + beta * op_c_load(C))\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n// * op_a, op_b, op_c_load, op_c_store are transform operations (functors) applying element-wisely\n//   to the input matrix. cuBLASDx provides two element-wise functors, cublasdx::identity and\n//   cublasdx::conjugate, where the later returns the complex conjugate of the input value\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 4;\n    constexpr unsigned int k = 2;\n\n    // Leading dimensions defined for matrices A, B, C\n    constexpr unsigned int lda = 16;\n    constexpr unsigned int ldb = 16;\n    constexpr unsigned int ldc = 16;\n\n    // Transform ops\n    auto a_load_op  = negate {};\n    auto b_load_op  = multiply_by<2> {};\n    auto c_load_op  = multiply_by<2> {};\n    auto c_store_op = negate {};\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::row_major>() +\n                 cublasdx::LeadingDimension<lda, ldb, ldc>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    value_type alpha = value_type(1.0);\n    value_type beta  = value_type(1.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n\n    example::device_vector<value_type> a = host_a;\n    example::device_vector<value_type> b = host_b;\n    example::device_vector<value_type> c = host_c;\n\n    example::device_vector<value_type> output(c.size());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    auto shared_size = cublasdx::get_shared_storage_size<BLAS>();\n    CUDA_CHECK_AND_EXIT(\n        cudaFuncSetAttribute(gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_size>>>(\n        a.data(), b.data(), c.data(), alpha, beta, output.data(), a_load_op, b_load_op, c_load_op, c_store_op);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output = output;\n\n    // Calculate reference\n    cudaStream_t str                   = 0;\n    auto         reference_host_output = example::reference_gemm<BLAS>(\n        alpha, host_a, host_b, beta, host_c, str, a_load_op, b_load_op, c_load_op, c_store_op);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n\n#include <hip/hip_runtime_api.h>\n\n#define CUBLASDX_IGNORE_NVBUG_5218000_ASSERT\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n\n// Requirements of the transform ops functors:\n// * The input type must be the same as the value type of the corresponding matrix.\n// * The return type must be convertible to the value type of the corresponding matrix.\nstruct negate {\n    template<class T>\n    __device__ __host__ constexpr T operator()(const T arg) const {\n        return -arg;\n    }\n};\n\ntemplate<unsigned N>\nstruct multiply_by {\n    template<class T>\n    __device__ __host__ constexpr T operator()(const T arg) const {\n        return static_cast<T>(N) * arg;\n    }\n};\n\ntemplate<class BLAS,\n         class ValueType = typename example::uniform_value_type_t<BLAS>,\n         class ALoadOp   = cublasdx::identity,\n         class BLoadOp   = cublasdx::identity,\n         class CLoadOp   = cublasdx::identity,\n         class CStoreOp  = cublasdx::identity>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output,\n                     const ALoadOp&   a_load_op  = {},\n                     const BLoadOp&   b_load_op  = {},\n                     const CLoadOp&   c_load_op  = {},\n                     const CStoreOp&  c_store_op = {}) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor, a_load_op, b_load_op);\n    auto result_fragment = accumulator.get_results();\n\n    // Make fragment, partition global tensor and copy\n    auto d_frag = accumulator.make_partition_and_copy(c_global_tensor);\n\n    cublasdx::transform_fragment(d_frag, c_load_op);\n    cublasdx::axpby(alpha, result_fragment, beta, d_frag);\n    cublasdx::transform_fragment(d_frag, c_store_op);\n    accumulator.partition_and_copy(d_frag, out_global_tensor);\n}\n\n// This is an example of fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = op_c_store(alpha * op_a(A) * op_b(B) + beta * op_c_load(C))\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n// * op_a, op_b, op_c_load, op_c_store are transform operations (functors) applying element-wisely\n//   to the input matrix. cuBLASDx provides two element-wise functors, cublasdx::identity and\n//   cublasdx::conjugate, where the later returns the complex conjugate of the input value\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 4;\n    constexpr unsigned int k = 2;\n\n    // Leading dimensions defined for matrices A, B, C\n    constexpr unsigned int lda = 16;\n    constexpr unsigned int ldb = 16;\n    constexpr unsigned int ldc = 16;\n\n    // Transform ops\n    auto a_load_op  = negate {};\n    auto b_load_op  = multiply_by<2> {};\n    auto c_load_op  = multiply_by<2> {};\n    auto c_store_op = negate {};\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::row_major>() +\n                 cublasdx::LeadingDimension<lda, ldb, ldc>() + cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    value_type alpha = value_type(1.0);\n    value_type beta  = value_type(1.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n\n    example::device_vector<value_type> a = host_a;\n    example::device_vector<value_type> b = host_b;\n    example::device_vector<value_type> c = host_c;\n\n    example::device_vector<value_type> output(c.size());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    auto shared_size = cublasdx::get_shared_storage_size<BLAS>();\n    CUDA_CHECK_AND_EXIT(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_size>>>(\n        a.data(), b.data(), c.data(), alpha, beta, output.data(), a_load_op, b_load_op, c_load_op, c_store_op);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output = output;\n\n    // Calculate reference\n    hipStream_t str                   = 0;\n    auto         reference_host_output = example::reference_gemm<BLAS>(\n        alpha, host_a, host_b, beta, host_c, str, a_load_op, b_load_op, c_load_op, c_store_op);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/07_gemm_transform/simple_gemm_transform.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <iostream>\n#include <system_error>\n#include <iomanip>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class Alpha, class Beta, class CTensor, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void gemm_kernel(Alpha const                            alpha,\n                     Beta const                             beta,\n                     CTensor                                global_c,\n                     __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        auto tile_pipeline = device_pipeline.get_tile(smem, blockIdx.x, blockIdx.y);\n        auto tile_gmem_c   = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n\n        auto epilogue_functor = [&](auto& accumulator) {\n            auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n            cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n            accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n        };\n\n        tile_pipeline.execute(epilogue_functor);\n    }\n#endif\n}\n\ntemplate<class BLAS,\n         int PipelineDepth,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\nauto measure_cublasdx(GEMMShape         gemm_shape,\n                      GEMMArr           gemm_arr,\n                      GEMMLD            gemm_ld,\n                      const Alpha       alpha,\n                      const AValueType* a,\n                      const BValueType* b,\n                      const Beta        beta,\n                      CValueType*       c,\n                      unsigned          kernel_warm_up_repeats,\n                      unsigned          kernel_repeats,\n                      cudaStream_t      stream) {\n    // Grid size configuration\n    const auto [m, n, k]       = gemm_shape;\n    const auto [lda, ldb, ldc] = gemm_ld;\n    constexpr auto tile_m      = cublasdx::size_of_v_m<BLAS>;\n    constexpr auto tile_n      = cublasdx::size_of_v_n<BLAS>;\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    auto global_a = cublasdx::make_gmem_tensor<cute::get<0>(gemm_arr)>(a, m, k, lda);\n    auto global_b = cublasdx::make_gmem_tensor<cute::get<1>(gemm_arr)>(b, k, n, ldb);\n    auto global_c = cublasdx::make_gmem_tensor<cute::get<2>(gemm_arr)>(c, m, n, ldc);\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(cudaDeviceGetAttribute(&num_sms, cudaDevAttrMultiProcessorCount, 0));\n\n    // Note that if there is enough smem and register pressure is low then it might be possible to execute\n    // multiple persistent CTAs per SM\n    dim3 grid_dim = dim3 {m / tile_m, n / tile_n, 1};\n\n    auto run_cublasdx_gemm = [&](cudaStream_t str) {\n        auto opt_device_pipeline = cublasdx::suggest_device_pipeline<PipelineDepth, BLAS>(global_a, global_b);\n\n        if (not opt_device_pipeline) {\n            std::cout << \"Incorrect pipeline configuration, please ensure global tensors are divisible by tile\"\n                      << std::endl;\n            exit(1);\n        }\n\n        auto device_pipeline = opt_device_pipeline.value();\n\n        // Increase max dynamic shared memory for the kernel if needed.\n        auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                      .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                      .get();\n\n        auto kernel = gemm_kernel<BLAS, Alpha, Beta, decltype(global_c), decltype(device_pipeline)>;\n        CUDA_CHECK_AND_EXIT(\n            cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n        kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size, str>>>(\n            alpha, beta, global_c, device_pipeline);\n    };\n\n    // First run for correctness check\n    run_cublasdx_gemm(stream);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy back data\n    std::vector<CValueType> results(m * n);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(results.data(), c, results.size() * sizeof(CValueType), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Execute kernel.\n    double time = example::measure::execution(run_cublasdx_gemm, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    return std::make_tuple(time, results);\n}\n\n// This is an example of testing performance of cuBLASDx as tile provider, executing a general matrix multiply (GEMM)\n// on the entire GPU and distributing work across all SMs, each of them running a small GEMM tile with cuBLASDx.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported, decoupled precisions are supported.\n// cuBLASLt is the comparison point for this example.\n//\n// This example provides a CLI interface for changing the dynamic size of the GEMM\n// make device_gemm_performance <m> <n> <k>\n//\n// Please refer to the documentation for more details.\n\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier, class GlobalShape>\nint device_gemm_performance(GlobalShape global_shape) {\n\n    // ==========================================================\n    // Tile chosen for big SGEMMs on B200 (fp32), for best\n    // results for other precisions please see our recommended\n    // configurations below, or attempt a thorough\n    // parameter scan over: (tile_m / tile_n / tile_k / threads)\n    // and staging C loads / stores through shared memory\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // This size can be set dynamically from command line\n    const auto m = cute::get<0>(global_shape); // Global M GEMM Size\n    const auto n = cute::get<1>(global_shape); // Global N GEMM Size\n    const auto k = cute::get<2>(global_shape); // Global K GEMM Size\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned tile_m = 128;\n    constexpr unsigned tile_n = 128;\n    constexpr unsigned tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr int tile_threads = 128;\n\n    // If manual_pipeline_depth is left at 0, an automatically generated depth\n    // will be used, equal to the maximal possible value based on shared memory\n    // size of chosen device architecture\n    constexpr unsigned manual_pipeline_depth   = 0;\n    constexpr bool     override_pipeline_depth = (manual_pipeline_depth != 0);\n\n    // GeForce tiles to try\n    // fp8  | TN | (4096 to 8192)  | --> V:64,64,64   T:128\n    // fp8  | TN | (over 8192)     | --> V:128,128,64 T:128\n    // int8 | TN | (4096 to 8192)  | --> V:64,64,64   T:128\n    // int8 | TN | (over 8192)     | --> V:128,128,64 T:128\n    // fp16 | TN | (4096 to 8192)  | --> V:64,64,32   T:128\n    // fp16 | TN | (over 8192)     | --> V:128,128,32 T:128\n    // tf32 | TN | (4096 to ~6000) | --> V:64,64,16   T:128\n    // tf32 | TN | (~6000 to 8192) | --> V:64,64,32   T:128\n    // tf32 | TN | (over 8192)     | --> V:128,128,16 T:128\n    // fp32 | TN | (col-major)     | --> V:256,128,32 T:256\n    // fp32 | TT | (row-major)     | --> V:64,128,16  T:128\n    // fp64 | TN | (all sizes)     | --> V:64,64,32   T:128\n\n    // Datacenter tiles to try\n    // fp32 | (big)   |            | --> V:128,128,32, T:128\n    // fp32 | (small) |            | --> V:128,64,16,  T:128\n\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Input used to be converted to the final compute precision,\n    // this can be used to simulate either in-flight quantization\n    // or flexible upcasting of data to save on bandwidth\n    // NOTE: either these types must be implicitly convertible to\n    // compute types, or converters should be provided in appropriate places.\n    // please refer to simple_gemm_fp32_decoupled.cu example for more details.\n    using a_io_value_type = a_compute_value_type;\n    using b_io_value_type = b_compute_value_type;\n    using c_io_value_type = c_compute_value_type;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 16;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    constexpr unsigned stage_shared_req = tile_m * tile_k * sizeof(a_compute_value_type) +\n                                          tile_k * tile_n * sizeof(b_compute_value_type) +\n                                          sizeof(cublasdx::pipeline_stage_scratch_t);\n\n    constexpr unsigned available_shared_memory = commondx::device_info<Arch>::shared_memory();\n    constexpr unsigned maximal_pipeline_depth  = cute::min(16, (available_shared_memory - 32) / stage_shared_req);\n    constexpr unsigned pipeline_depth = override_pipeline_depth ? manual_pipeline_depth : maximal_pipeline_depth;\n    static_assert(\n        pipeline_depth <= maximal_pipeline_depth,\n        \"The chosen pipeline depth requires more shared memory than is available for the target architecture\");\n\n    auto k_stages = k / tile_k;\n    if (k_stages < pipeline_depth) {\n        std::cerr << \"PipelineDepth must be less or equal to GEMM k stages, please adjust manual_pipeline_depth\"\n                  << std::endl;\n        return 1;\n    }\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    // Use tuples to avoid passing 20 arguments to a function\n    constexpr auto global_arrangement = cute::make_tuple(\n        // These must be passed as integral constants to properly dispatch static striding\n        std::integral_constant<cublasdx::arrangement, global_arrangement_a> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_b> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_c> {});\n\n    const auto global_ld = cute::make_tuple(global_lda, global_ldb, global_ldc);\n\n    // Performance comparison parameters\n    const unsigned int kernel_repeats         = 5;\n    const unsigned int kernel_warm_up_repeats = 15;\n\n    constexpr bool compare_to_fp64_cublas = false;\n    using a_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, a_compute_value_type>;\n    using b_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, b_compute_value_type>;\n    using c_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, c_compute_value_type>;\n\n    // Test implementation\n    a_io_value_type* a_cublasdx = nullptr;\n    b_io_value_type* b_cublasdx = nullptr;\n    c_io_value_type* c_cublasdx = nullptr;\n\n    a_cublas_value_type* a_cublas = nullptr;\n    b_cublas_value_type* b_cublas = nullptr;\n    c_cublas_value_type* c_cublas = nullptr;\n\n    // Fill the A, B, C matrices with random values.\n    {\n        // Use nullptr tensors to make it easier to calculate memory requirements\n        auto dummy_global_a =\n            cublasdx::make_gmem_tensor<cute::get<0>(global_arrangement)>(a_cublasdx, m, k, global_lda);\n        auto dummy_global_b =\n            cublasdx::make_gmem_tensor<cute::get<1>(global_arrangement)>(b_cublasdx, k, n, global_ldb);\n        auto dummy_global_c =\n            cublasdx::make_gmem_tensor<cute::get<2>(global_arrangement)>(c_cublasdx, m, n, global_ldc);\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&a_cublasdx, cublasdx::cosize(dummy_global_a.layout()) * sizeof(a_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&a_cublas, cublasdx::cosize(dummy_global_a.layout()) * sizeof(a_cublas_value_type)));\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&b_cublasdx, cublasdx::cosize(dummy_global_b.layout()) * sizeof(b_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&b_cublas, cublasdx::cosize(dummy_global_b.layout()) * sizeof(b_cublas_value_type)));\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&c_cublasdx, cublasdx::cosize(dummy_global_c.layout()) * sizeof(c_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            cudaMalloc(&c_cublas, cublasdx::cosize(dummy_global_c.layout()) * sizeof(c_cublas_value_type)));\n\n        auto host_a_io = example::get_random_data<a_io_value_type>(m * k, 1);\n        auto host_b_io = example::get_random_data<b_io_value_type>(k * n, 2);\n        auto host_c_io = example::get_random_data<c_io_value_type>(m * n, 3);\n\n        // Create A cuBLASLt input\n        using a_converter_t      = example::converter<a_cublas_value_type>;\n        using a_functor_output_t = cublasdx::detail::res_t<a_converter_t, a_io_value_type>;\n        static_assert(std::is_convertible_v<a_functor_output_t, a_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_a_cublas = std::vector<a_cublas_value_type>(host_a_io.size());\n        std::transform(host_a_io.cbegin(), host_a_io.cend(), host_a_cublas.begin(), a_converter_t {});\n\n        // Create B cuBLASLt input\n        using b_converter_t      = example::converter<b_cublas_value_type>;\n        using b_functor_output_t = cublasdx::detail::res_t<b_converter_t, b_io_value_type>;\n        static_assert(std::is_convertible_v<b_functor_output_t, b_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_b_cublas = std::vector<b_cublas_value_type>(host_b_io.size());\n        std::transform(host_b_io.cbegin(), host_b_io.cend(), host_b_cublas.begin(), b_converter_t {});\n\n        // Create C cuBLASLt input\n        using c_converter_t      = example::converter<c_cublas_value_type>;\n        using c_functor_output_t = cublasdx::detail::res_t<c_converter_t, c_io_value_type>;\n        static_assert(std::is_convertible_v<c_functor_output_t, c_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_c_cublas = std::vector<c_cublas_value_type>(host_c_io.size());\n        std::transform(host_c_io.cbegin(), host_c_io.cend(), host_c_cublas.begin(), c_converter_t {});\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(a_cublasdx, host_a_io.data(), m * k * sizeof(a_io_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(b_cublasdx, host_b_io.data(), k * n * sizeof(b_io_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(c_cublasdx, host_c_io.data(), m * n * sizeof(c_io_value_type), cudaMemcpyHostToDevice));\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(a_cublas, host_a_cublas.data(), m * k * sizeof(a_cublas_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(b_cublas, host_b_cublas.data(), k * n * sizeof(b_cublas_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(c_cublas, host_c_cublas.data(), m * n * sizeof(c_cublas_value_type), cudaMemcpyHostToDevice));\n        // destroy host vectors\n    }\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::EnableInputStreaming() + cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Execute cuBLASDx and cuBLASLt\n    // =============================\n\n    auto [time_cublasdx, host_dx_results] = measure_cublasdx<BLAS, pipeline_depth>(global_shape,\n                                                                                   global_arrangement,\n                                                                                   global_ld,\n                                                                                   alpha,\n                                                                                   a_cublasdx,\n                                                                                   b_cublasdx,\n                                                                                   beta,\n                                                                                   c_cublasdx,\n                                                                                   kernel_warm_up_repeats,\n                                                                                   kernel_repeats,\n                                                                                   stream);\n\n    // Measure cuBLAS performance.\n    auto [time_cublas, host_blas_results] =\n        example::cublaslt_runner<a_cublas_value_type, b_cublas_value_type, c_cublas_value_type>(\n            global_shape, global_arrangement, global_ld)\n            .execute_with_time_and_results(\n                alpha, a_cublas, b_cublas, beta, c_cublas, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m, n, k: \" << m << \", \" << n << \", \" << k << std::endl;\n    std::cout << \"Compute Type A: \" << example::type_string<a_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type B: \" << example::type_string<b_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type C: \" << example::type_string<c_compute_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision A: \" << example::precision_string<a_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision B: \" << example::precision_string<b_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision C: \" << example::precision_string<c_io_value_type>() << std::endl;\n\n    const double avg_time_dx = time_cublasdx / kernel_repeats;\n    const double dx_gflops =\n        example::gemm_flops<a_compute_value_type, b_compute_value_type, c_compute_value_type>(m, n, k) /\n        (avg_time_dx * 1e6);\n\n    std::cout << \"\\ncuBLASDx\\n\";\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"Avg time [ms]  = \" << avg_time_dx << \"\\n\";\n    std::cout << \"Avg GFLOP/s  = \" << dx_gflops << \"\\n\";\n\n    const double avg_time_cublas = time_cublas / kernel_repeats;\n    double cublas_gflops = example::gemm_flops<a_cublas_value_type, b_cublas_value_type, c_cublas_value_type>(m, n, k) /\n                           (avg_time_cublas * 1e6);\n\n    std::cout << \"\\ncuBLASLt (not including heuristic)\\n\";\n    std::cout << \"Avg time [ms]  = \" << avg_time_cublas << \"\\n\";\n    std::cout << \"Avg GFLOP/s  = \" << cublas_gflops << \"\\n\";\n\n    constexpr bool verbose_knob = false;\n    constexpr bool print_knob   = true;\n\n    auto error = example::calculate_error(host_dx_results, host_blas_results, verbose_knob, print_knob);\n    std::cout << std::fixed << std::setprecision(10) << \"Error = \" << error << \"\\n\";\n\n    std::cout << std::fixed << std::setprecision(2) << \"cuBLAS / cuBLASDx timings = \" << time_cublas / time_cublasdx\n              << \"\\n\";\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(a_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(b_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(c_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(a_cublas));\n    CUDA_CHECK_AND_EXIT(cudaFree(b_cublas));\n    CUDA_CHECK_AND_EXIT(cudaFree(c_cublas));\n\n    return 0;\n}\n\nstruct device_gemm_performance_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier, class GlobalShape>\n    int operator()(std::integral_constant<int, Arch>,\n                   std::integral_constant<cublasdx::sm_modifier, Modifier>,\n                   GlobalShape global_shape) {\n        return device_gemm_performance<Arch, Modifier>(global_shape);\n    }\n};\n\nint main(int argc, char** argv) {\n    std::array<unsigned int, 3> mnk = {8192, 8192, 8192};\n    auto usage = []() { std::cerr << \"Incorrect usage: ./device_gemm_performance [m n k]\" << std::endl; };\n\n    if (argc == 4) {\n        std::cout << \"Tile optimized for big SGEMMs on B200 (fp32), for best \"\n                     \"results for other precisions please see our recommended \"\n                     \"configurations in device_gemm_performance.cu, or attempt a thorough \"\n                     \"parameter scan over: (tile_m / tile_n / tile_k / threads) \"\n                     \"and staging C loads / stores through shared memory\"\n                  << std::endl;\n\n        try {\n            std::transform(argv + 1, argv + argc, mnk.begin(), [&](char* dim_input) { return std::stoul(dim_input); });\n        } catch (...) {\n            usage();\n            return 1;\n        }\n    } else if (argc != 1) {\n        usage();\n        return 1;\n    }\n\n    return example::sm_runner(device_gemm_performance_functor {}, cute::make_shape(mnk[0], mnk[1], mnk[2]));\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <array>\n#include <iostream>\n#include <system_error>\n#include <iomanip>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class Alpha, class Beta, class CTensor, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void gemm_kernel(Alpha const                            alpha,\n                     Beta const                             beta,\n                     CTensor                                global_c,\n                     __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        auto tile_pipeline = device_pipeline.get_tile(smem, blockIdx.x, blockIdx.y);\n        auto tile_gmem_c   = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n\n        auto epilogue_functor = [&](auto& accumulator) {\n            auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n            cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n            accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n        };\n\n        tile_pipeline.execute(epilogue_functor);\n    }\n#endif\n}\n\ntemplate<class BLAS,\n         int PipelineDepth,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\nauto measure_cublasdx(GEMMShape         gemm_shape,\n                      GEMMArr           gemm_arr,\n                      GEMMLD            gemm_ld,\n                      const Alpha       alpha,\n                      const AValueType* a,\n                      const BValueType* b,\n                      const Beta        beta,\n                      CValueType*       c,\n                      unsigned          kernel_warm_up_repeats,\n                      unsigned          kernel_repeats,\n                      hipStream_t      stream) {\n    // Grid size configuration\n    const auto [m, n, k]       = gemm_shape;\n    const auto [lda, ldb, ldc] = gemm_ld;\n    constexpr auto tile_m      = cublasdx::size_of_v_m<BLAS>;\n    constexpr auto tile_n      = cublasdx::size_of_v_n<BLAS>;\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    auto global_a = cublasdx::make_gmem_tensor<cute::get<0>(gemm_arr)>(a, m, k, lda);\n    auto global_b = cublasdx::make_gmem_tensor<cute::get<1>(gemm_arr)>(b, k, n, ldb);\n    auto global_c = cublasdx::make_gmem_tensor<cute::get<2>(gemm_arr)>(c, m, n, ldc);\n\n    int num_sms = 0;\n    CUDA_CHECK_AND_EXIT(hipDeviceGetAttribute(&num_sms, hipDeviceAttributeMultiprocessorCount, 0));\n\n    // Note that if there is enough smem and register pressure is low then it might be possible to execute\n    // multiple persistent CTAs per SM\n    dim3 grid_dim = dim3 {m / tile_m, n / tile_n, 1};\n\n    auto run_cublasdx_gemm = [&](hipStream_t str) {\n        auto opt_device_pipeline = cublasdx::suggest_device_pipeline<PipelineDepth, BLAS>(global_a, global_b);\n\n        if (not opt_device_pipeline) {\n            std::cout << \"Incorrect pipeline configuration, please ensure global tensors are divisible by tile\"\n                      << std::endl;\n            exit(1);\n        }\n\n        auto device_pipeline = opt_device_pipeline.value();\n\n        // Increase max dynamic shared memory for the kernel if needed.\n        auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                      .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                      .get();\n\n        auto kernel = gemm_kernel<BLAS, Alpha, Beta, decltype(global_c), decltype(device_pipeline)>;\n        CUDA_CHECK_AND_EXIT(\n            hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n        kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size, str>>>(\n            alpha, beta, global_c, device_pipeline);\n    };\n\n    // First run for correctness check\n    run_cublasdx_gemm(stream);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy back data\n    std::vector<CValueType> results(m * n);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(results.data(), c, results.size() * sizeof(CValueType), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Execute kernel.\n    double time = example::measure::execution(run_cublasdx_gemm, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    return std::make_tuple(time, results);\n}\n\n// This is an example of testing performance of cuBLASDx as tile provider, executing a general matrix multiply (GEMM)\n// on the entire GPU and distributing work across all SMs, each of them running a small GEMM tile with cuBLASDx.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported, decoupled precisions are supported.\n// cuBLASLt is the comparison point for this example.\n//\n// This example provides a CLI interface for changing the dynamic size of the GEMM\n// make device_gemm_performance <m> <n> <k>\n//\n// Please refer to the documentation for more details.\n\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier, class GlobalShape>\nint device_gemm_performance(GlobalShape global_shape) {\n\n    // ==========================================================\n    // Tile chosen for big SGEMMs on B200 (fp32), for best\n    // results for other precisions please see our recommended\n    // configurations below, or attempt a thorough\n    // parameter scan over: (tile_m / tile_n / tile_k / threads)\n    // and staging C loads / stores through shared memory\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // This size can be set dynamically from command line\n    const auto m = cute::get<0>(global_shape); // Global M GEMM Size\n    const auto n = cute::get<1>(global_shape); // Global N GEMM Size\n    const auto k = cute::get<2>(global_shape); // Global K GEMM Size\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned tile_m = 128;\n    constexpr unsigned tile_n = 128;\n    constexpr unsigned tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr int tile_threads = 128;\n\n    // If manual_pipeline_depth is left at 0, an automatically generated depth\n    // will be used, equal to the maximal possible value based on shared memory\n    // size of chosen device architecture\n    constexpr unsigned manual_pipeline_depth   = 0;\n    constexpr bool     override_pipeline_depth = (manual_pipeline_depth != 0);\n\n    // GeForce tiles to try\n    // fp8  | TN | (4096 to 8192)  | --> V:64,64,64   T:128\n    // fp8  | TN | (over 8192)     | --> V:128,128,64 T:128\n    // int8 | TN | (4096 to 8192)  | --> V:64,64,64   T:128\n    // int8 | TN | (over 8192)     | --> V:128,128,64 T:128\n    // fp16 | TN | (4096 to 8192)  | --> V:64,64,32   T:128\n    // fp16 | TN | (over 8192)     | --> V:128,128,32 T:128\n    // tf32 | TN | (4096 to ~6000) | --> V:64,64,16   T:128\n    // tf32 | TN | (~6000 to 8192) | --> V:64,64,32   T:128\n    // tf32 | TN | (over 8192)     | --> V:128,128,16 T:128\n    // fp32 | TN | (col-major)     | --> V:256,128,32 T:256\n    // fp32 | TT | (row-major)     | --> V:64,128,16  T:128\n    // fp64 | TN | (all sizes)     | --> V:64,64,32   T:128\n\n    // Datacenter tiles to try\n    // fp32 | (big)   |            | --> V:128,128,32, T:128\n    // fp32 | (small) |            | --> V:128,64,16,  T:128\n\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Input used to be converted to the final compute precision,\n    // this can be used to simulate either in-flight quantization\n    // or flexible upcasting of data to save on bandwidth\n    // NOTE: either these types must be implicitly convertible to\n    // compute types, or converters should be provided in appropriate places.\n    // please refer to simple_gemm_fp32_decoupled.cu example for more details.\n    using a_io_value_type = a_compute_value_type;\n    using b_io_value_type = b_compute_value_type;\n    using c_io_value_type = c_compute_value_type;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 16;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    constexpr unsigned stage_shared_req = tile_m * tile_k * sizeof(a_compute_value_type) +\n                                          tile_k * tile_n * sizeof(b_compute_value_type) +\n                                          sizeof(cublasdx::pipeline_stage_scratch_t);\n\n    constexpr unsigned available_shared_memory = commondx::device_info<Arch>::shared_memory();\n    constexpr unsigned maximal_pipeline_depth  = cute::min(16, (available_shared_memory - 32) / stage_shared_req);\n    constexpr unsigned pipeline_depth = override_pipeline_depth ? manual_pipeline_depth : maximal_pipeline_depth;\n    static_assert(\n        pipeline_depth <= maximal_pipeline_depth,\n        \"The chosen pipeline depth requires more shared memory than is available for the target architecture\");\n\n    auto k_stages = k / tile_k;\n    if (k_stages < pipeline_depth) {\n        std::cerr << \"PipelineDepth must be less or equal to GEMM k stages, please adjust manual_pipeline_depth\"\n                  << std::endl;\n        return 1;\n    }\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    // Use tuples to avoid passing 20 arguments to a function\n    constexpr auto global_arrangement = cute::make_tuple(\n        // These must be passed as integral constants to properly dispatch static striding\n        std::integral_constant<cublasdx::arrangement, global_arrangement_a> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_b> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_c> {});\n\n    const auto global_ld = cute::make_tuple(global_lda, global_ldb, global_ldc);\n\n    // Performance comparison parameters\n    const unsigned int kernel_repeats         = 5;\n    const unsigned int kernel_warm_up_repeats = 15;\n\n    constexpr bool compare_to_fp64_cublas = false;\n    using a_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, a_compute_value_type>;\n    using b_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, b_compute_value_type>;\n    using c_cublas_value_type             = cute::conditional_t<compare_to_fp64_cublas, double, c_compute_value_type>;\n\n    // Test implementation\n    a_io_value_type* a_cublasdx = nullptr;\n    b_io_value_type* b_cublasdx = nullptr;\n    c_io_value_type* c_cublasdx = nullptr;\n\n    a_cublas_value_type* a_cublas = nullptr;\n    b_cublas_value_type* b_cublas = nullptr;\n    c_cublas_value_type* c_cublas = nullptr;\n\n    // Fill the A, B, C matrices with random values.\n    {\n        // Use nullptr tensors to make it easier to calculate memory requirements\n        auto dummy_global_a =\n            cublasdx::make_gmem_tensor<cute::get<0>(global_arrangement)>(a_cublasdx, m, k, global_lda);\n        auto dummy_global_b =\n            cublasdx::make_gmem_tensor<cute::get<1>(global_arrangement)>(b_cublasdx, k, n, global_ldb);\n        auto dummy_global_c =\n            cublasdx::make_gmem_tensor<cute::get<2>(global_arrangement)>(c_cublasdx, m, n, global_ldc);\n\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&a_cublasdx, cublasdx::cosize(dummy_global_a.layout()) * sizeof(a_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&a_cublas, cublasdx::cosize(dummy_global_a.layout()) * sizeof(a_cublas_value_type)));\n\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&b_cublasdx, cublasdx::cosize(dummy_global_b.layout()) * sizeof(b_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&b_cublas, cublasdx::cosize(dummy_global_b.layout()) * sizeof(b_cublas_value_type)));\n\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&c_cublasdx, cublasdx::cosize(dummy_global_c.layout()) * sizeof(c_io_value_type)));\n        CUDA_CHECK_AND_EXIT(\n            hipMalloc(&c_cublas, cublasdx::cosize(dummy_global_c.layout()) * sizeof(c_cublas_value_type)));\n\n        auto host_a_io = example::get_random_data<a_io_value_type>(m * k, 1);\n        auto host_b_io = example::get_random_data<b_io_value_type>(k * n, 2);\n        auto host_c_io = example::get_random_data<c_io_value_type>(m * n, 3);\n\n        // Create A cuBLASLt input\n        using a_converter_t      = example::converter<a_cublas_value_type>;\n        using a_functor_output_t = cublasdx::detail::res_t<a_converter_t, a_io_value_type>;\n        static_assert(std::is_convertible_v<a_functor_output_t, a_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_a_cublas = std::vector<a_cublas_value_type>(host_a_io.size());\n        std::transform(host_a_io.cbegin(), host_a_io.cend(), host_a_cublas.begin(), a_converter_t {});\n\n        // Create B cuBLASLt input\n        using b_converter_t      = example::converter<b_cublas_value_type>;\n        using b_functor_output_t = cublasdx::detail::res_t<b_converter_t, b_io_value_type>;\n        static_assert(std::is_convertible_v<b_functor_output_t, b_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_b_cublas = std::vector<b_cublas_value_type>(host_b_io.size());\n        std::transform(host_b_io.cbegin(), host_b_io.cend(), host_b_cublas.begin(), b_converter_t {});\n\n        // Create C cuBLASLt input\n        using c_converter_t      = example::converter<c_cublas_value_type>;\n        using c_functor_output_t = cublasdx::detail::res_t<c_converter_t, c_io_value_type>;\n        static_assert(std::is_convertible_v<c_functor_output_t, c_cublas_value_type>,\n                      \"Input type must be convertible to compute type\");\n\n        auto host_c_cublas = std::vector<c_cublas_value_type>(host_c_io.size());\n        std::transform(host_c_io.cbegin(), host_c_io.cend(), host_c_cublas.begin(), c_converter_t {});\n\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(a_cublasdx, host_a_io.data(), m * k * sizeof(a_io_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(b_cublasdx, host_b_io.data(), k * n * sizeof(b_io_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(c_cublasdx, host_c_io.data(), m * n * sizeof(c_io_value_type), hipMemcpyHostToDevice));\n\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(a_cublas, host_a_cublas.data(), m * k * sizeof(a_cublas_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(b_cublas, host_b_cublas.data(), k * n * sizeof(b_cublas_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(c_cublas, host_c_cublas.data(), m * n * sizeof(c_cublas_value_type), hipMemcpyHostToDevice));\n        // destroy host vectors\n    }\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::EnableInputStreaming() + cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Execute cuBLASDx and cuBLASLt\n    // =============================\n\n    auto [time_cublasdx, host_dx_results] = measure_cublasdx<BLAS, pipeline_depth>(global_shape,\n                                                                                   global_arrangement,\n                                                                                   global_ld,\n                                                                                   alpha,\n                                                                                   a_cublasdx,\n                                                                                   b_cublasdx,\n                                                                                   beta,\n                                                                                   c_cublasdx,\n                                                                                   kernel_warm_up_repeats,\n                                                                                   kernel_repeats,\n                                                                                   stream);\n\n    // Measure cuBLAS performance.\n    auto [time_cublas, host_blas_results] =\n        example::cublaslt_runner<a_cublas_value_type, b_cublas_value_type, c_cublas_value_type>(\n            global_shape, global_arrangement, global_ld)\n            .execute_with_time_and_results(\n                alpha, a_cublas, b_cublas, beta, c_cublas, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m, n, k: \" << m << \", \" << n << \", \" << k << std::endl;\n    std::cout << \"Compute Type A: \" << example::type_string<a_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type B: \" << example::type_string<b_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type C: \" << example::type_string<c_compute_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision A: \" << example::precision_string<a_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision B: \" << example::precision_string<b_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision C: \" << example::precision_string<c_io_value_type>() << std::endl;\n\n    const double avg_time_dx = time_cublasdx / kernel_repeats;\n    const double dx_gflops =\n        example::gemm_flops<a_compute_value_type, b_compute_value_type, c_compute_value_type>(m, n, k) /\n        (avg_time_dx * 1e6);\n\n    std::cout << \"\\ncuBLASDx\\n\";\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"Avg time [ms]  = \" << avg_time_dx << \"\\n\";\n    std::cout << \"Avg GFLOP/s  = \" << dx_gflops << \"\\n\";\n\n    const double avg_time_cublas = time_cublas / kernel_repeats;\n    double cublas_gflops = example::gemm_flops<a_cublas_value_type, b_cublas_value_type, c_cublas_value_type>(m, n, k) /\n                           (avg_time_cublas * 1e6);\n\n    std::cout << \"\\ncuBLASLt (not including heuristic)\\n\";\n    std::cout << \"Avg time [ms]  = \" << avg_time_cublas << \"\\n\";\n    std::cout << \"Avg GFLOP/s  = \" << cublas_gflops << \"\\n\";\n\n    constexpr bool verbose_knob = false;\n    constexpr bool print_knob   = true;\n\n    auto error = example::calculate_error(host_dx_results, host_blas_results, verbose_knob, print_knob);\n    std::cout << std::fixed << std::setprecision(10) << \"Error = \" << error << \"\\n\";\n\n    std::cout << std::fixed << std::setprecision(2) << \"cuBLAS / cuBLASDx timings = \" << time_cublas / time_cublasdx\n              << \"\\n\";\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(a_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(b_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(c_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(a_cublas));\n    CUDA_CHECK_AND_EXIT(hipFree(b_cublas));\n    CUDA_CHECK_AND_EXIT(hipFree(c_cublas));\n\n    return 0;\n}\n\nstruct device_gemm_performance_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier, class GlobalShape>\n    int operator()(std::integral_constant<int, Arch>,\n                   std::integral_constant<cublasdx::sm_modifier, Modifier>,\n                   GlobalShape global_shape) {\n        return device_gemm_performance<Arch, Modifier>(global_shape);\n    }\n};\n\nint main(int argc, char** argv) {\n    std::array<unsigned int, 3> mnk = {8192, 8192, 8192};\n    auto usage = []() { std::cerr << \"Incorrect usage: ./device_gemm_performance [m n k]\" << std::endl; };\n\n    if (argc == 4) {\n        std::cout << \"Tile optimized for big SGEMMs on B200 (fp32), for best \"\n                     \"results for other precisions please see our recommended \"\n                     \"configurations in device_gemm_performance.cu, or attempt a thorough \"\n                     \"parameter scan over: (tile_m / tile_n / tile_k / threads) \"\n                     \"and staging C loads / stores through shared memory\"\n                  << std::endl;\n\n        try {\n            std::transform(argv + 1, argv + argc, mnk.begin(), [&](char* dim_input) { return std::stoul(dim_input); });\n        } catch (...) {\n            usage();\n            return 1;\n        }\n    } else if (argc != 1) {\n        usage();\n        return 1;\n    }\n\n    return example::sm_runner(device_gemm_performance_functor {}, cute::make_shape(mnk[0], mnk[1], mnk[2]));\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/11_gemm_device_performance/device_gemm_performance.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a, ValueType* output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    ValueType* smem_a = reinterpret_cast<ValueType*>(smem);\n    /// A, column major arrangement\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    // Same data, but because arrangement for 2nd argument (B) was set row major it will be read as A^T\n    auto at_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy_wait();\n\n    // C = A * A^T\n    auto accumulator = BLAS().execute(a_shared_tensor, at_shared_tensor);\n\n    accumulator.partition_and_store(out_global_tensor);\n}\n\n// This example demonstrates a special case of matrix multiplication where an input matrix is multiplied\n// by itself transposed:\n//\n//              C = A * A^T\n//\n// Using the same matrix for the 2nd argument of the multiplication increases the maximum possible dimensions\n// of the input matrix by decreasing the shared memory requirements. However, it is important to know this\n// can have performance impact.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of input matrices and the result matrix\n    constexpr unsigned int m = 32;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM precision\n    using precision = float;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Arrangement for A is column-major, so to reuse the same memory for A^T (2nd argument; B) is row-major.\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 6. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::col_major, cublasdx::row_major, cublasdx::col_major>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory\n    value_type* a_matrix; // A\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto input_size       = global_a_size;\n    auto input_size_bytes = input_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&a_matrix, input_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    // Fill the input matrix\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    for (size_t i = 0; i < host_a.size(); i++) {\n        host_a[i] = i + 1;\n    }\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(a_matrix, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    const auto shared_memory_size =\n        cublasdx::make_shared_storage_calculator()\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(value_type), BLAS::get_layout_smem_a())\n            .get();\n    CUDA_CHECK_AND_EXIT(\n        cudaFuncSetAttribute(gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_memory_size>>>(a_matrix, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(a_matrix));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    const value_type alpha {1.0};\n    const value_type beta {0.0};\n    auto             host_c                = std::vector<value_type>(host_a.size(), beta);\n    auto             reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_a, beta, host_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a, ValueType* output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    ValueType* smem_a = reinterpret_cast<ValueType*>(smem);\n    /// A, column major arrangement\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    // Same data, but because arrangement for 2nd argument (B) was set row major it will be read as A^T\n    auto at_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy_wait();\n\n    // C = A * A^T\n    auto accumulator = BLAS().execute(a_shared_tensor, at_shared_tensor);\n\n    accumulator.partition_and_store(out_global_tensor);\n}\n\n// This example demonstrates a special case of matrix multiplication where an input matrix is multiplied\n// by itself transposed:\n//\n//              C = A * A^T\n//\n// Using the same matrix for the 2nd argument of the multiplication increases the maximum possible dimensions\n// of the input matrix by decreasing the shared memory requirements. However, it is important to know this\n// can have performance impact.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of input matrices and the result matrix\n    constexpr unsigned int m = 32;\n    constexpr unsigned int n = 32;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM precision\n    using precision = float;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Arrangement for A is column-major, so to reuse the same memory for A^T (2nd argument; B) is row-major.\n    // 4. Block operator informs that GEMM should be performed on CUDA block level.\n    // 5. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 6. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<precision>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<cublasdx::col_major, cublasdx::row_major, cublasdx::col_major>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory\n    value_type* a_matrix; // A\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto input_size       = global_a_size;\n    auto input_size_bytes = input_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&a_matrix, input_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    // Fill the input matrix\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    for (size_t i = 0; i < host_a.size(); i++) {\n        host_a[i] = i + 1;\n    }\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(a_matrix, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    const auto shared_memory_size =\n        cublasdx::make_shared_storage_calculator()\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(value_type), BLAS::get_layout_smem_a())\n            .get();\n    CUDA_CHECK_AND_EXIT(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_memory_size>>>(a_matrix, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(a_matrix));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    const value_type alpha {1.0};\n    const value_type beta {0.0};\n    auto             host_c                = std::vector<value_type>(host_a.size(), beta);\n    auto             reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_a, beta, host_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/09_gemm_custom_layout/simple_gemm_aat.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         class ALayout,\n         class BLayout,\n         class CLayout,\n         class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output,\n                     ALayout          a_layout, // Static shape with dynamic strides\n                     BLayout          b_layout, // Static shape with dynamic strides\n                     CLayout          c_layout)          // Static shape with dynamic strides\n{\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    // Pass custom layouts to shared memory slicing for appropriate pointer offsets\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem, a_layout, b_layout, c_layout);\n\n    // Define shared memory tensors with custom layouts\n    auto a_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_a), a_layout);\n    auto b_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_b), b_layout);\n    auto c_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_c), c_layout);\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block using custom CuTe layouts (cute::Layout concept) for A, B and C matrices:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Custom shared memory layout\n    auto a_layout = cute::make_layout(cute::Shape<cute::Int<m>, cute::Int<k>> {}, cute::make_stride(2u, 2u * m + 3));\n    auto b_layout = cute::make_layout(cute::Shape<cute::Int<k>, cute::Int<n>> {}, cute::make_stride(2u, 2u * k + 4));\n    auto c_layout = cute::make_layout(cute::Shape<cute::Int<m>, cute::Int<n>> {}, cute::make_stride(2u, 2u * m + 5));\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, the type (real or complex) and the alignements are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Alignment<16, 16, 16>() + cublasdx::Block() +\n                 cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    unsigned int shared_memory_size = cublasdx::get_shared_storage_size<BLAS>(a_layout, b_layout, c_layout);\n    CUDA_CHECK_AND_EXIT(\n        cudaFuncSetAttribute(gemm_kernel<BLAS, decltype(a_layout), decltype(b_layout), decltype(c_layout)>,\n                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                             shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS, decltype(a_layout), decltype(b_layout), decltype(c_layout)>\n        <<<1, BLAS::block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output, a_layout, b_layout, c_layout);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         class ALayout,\n         class BLayout,\n         class CLayout,\n         class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType* a,\n                     const ValueType* b,\n                     const ValueType* c,\n                     const ValueType  alpha,\n                     const ValueType  beta,\n                     ValueType*       output,\n                     ALayout          a_layout, // Static shape with dynamic strides\n                     BLayout          b_layout, // Static shape with dynamic strides\n                     CLayout          c_layout)          // Static shape with dynamic strides\n{\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    // Pass custom layouts to shared memory slicing for appropriate pointer offsets\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem, a_layout, b_layout, c_layout);\n\n    // Define shared memory tensors with custom layouts\n    auto a_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_a), a_layout);\n    auto b_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_b), b_layout);\n    auto c_shared_tensor = cute::make_tensor(cute::make_smem_ptr(smem_c), c_layout);\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block using custom CuTe layouts (cute::Layout concept) for A, B and C matrices:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Custom shared memory layout\n    auto a_layout = cute::make_layout(cute::Shape<cute::Int<m>, cute::Int<k>> {}, cute::make_stride(2u, 2u * m + 3));\n    auto b_layout = cute::make_layout(cute::Shape<cute::Int<k>, cute::Int<n>> {}, cute::make_stride(2u, 2u * k + 4));\n    auto c_layout = cute::make_layout(cute::Shape<cute::Int<m>, cute::Int<n>> {}, cute::make_stride(2u, 2u * m + 5));\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, the type (real or complex) and the alignements are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<float>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Alignment<16, 16, 16>() + cublasdx::Block() +\n                 cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    unsigned int shared_memory_size = cublasdx::get_shared_storage_size<BLAS>(a_layout, b_layout, c_layout);\n    CUDA_CHECK_AND_EXIT(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS, decltype(a_layout), decltype(b_layout), decltype(c_layout)>),\n                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                             shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS, decltype(a_layout), decltype(b_layout), decltype(c_layout)>\n        <<<1, BLAS::block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output, a_layout, b_layout, c_layout);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/09_gemm_custom_layout/simple_gemm_custom_layout.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define CUBLASDX_EXAMPLE_NO_THRUST\n#include \"naive_reference.hpp\"\n\nnamespace example {\n\n    template<class AccumType, class AEngine, class ALayout, class BEngine, class BLayout>\n    CUTE_HOST_DEVICE void dot(AccumType&                            accum,\n                              cute::Tensor<AEngine, ALayout> const& tensor_a,\n                              cute::Tensor<BEngine, BLayout> const& tensor_b) {\n        static_assert(cute::rank(decltype(tensor_a.layout()) {}) == cute::rank(decltype(tensor_b.layout()) {}) == 1);\n        for (unsigned int k_iter = 0; k_iter < cute::size<0>(tensor_a); ++k_iter) {\n            // Cast needed to emulate --> higher += lower * lower\n            accum += convert<AccumType>(tensor_a(k_iter)) * convert<AccumType>(tensor_b(k_iter));\n        }\n    }\n\n    template<class AEngine,\n             class ALayout,\n             class BEngine,\n             class BLayout,\n             class CEngine,\n             class CLayout,\n             class Alpha,\n             class Beta>\n    __global__ void reference_gemm_naive_kernel(Alpha                                alpha,\n                                                cute::Tensor<AEngine, ALayout> const tensor_a,\n                                                cute::Tensor<BEngine, BLayout> const tensor_b,\n                                                Beta                                 beta,\n                                                cute::Tensor<CEngine, CLayout>       tensor_c) {\n\n        using value_type_c = typename CEngine::value_type;\n\n        auto idx_m = blockIdx.x * blockDim.x + threadIdx.x;\n        auto idx_n = blockIdx.y * blockDim.y + threadIdx.y;\n\n        if (idx_m < cute::size<0>(tensor_c) and idx_n < cute::size<1>(tensor_c)) {\n            value_type_c acc = convert<value_type_c>(0.f);\n            dot(acc, tensor_a(idx_m, cute::_), tensor_b(cute::_, idx_n));\n            tensor_c(idx_m, idx_n) = alpha * acc + beta * tensor_c(idx_m, idx_n);\n        }\n    }\n\n\n    template<class AEngine,\n             class ALayout,\n             class BEngine,\n             class BLayout,\n             class CEngine,\n             class CLayout,\n             class Alpha,\n             class Beta>\n    void reference_gemm_naive_host(Alpha                                 alpha,\n                                   cute::Tensor<AEngine, ALayout> const& tensor_a,\n                                   cute::Tensor<BEngine, BLayout> const& tensor_b,\n                                   Beta                                  beta,\n                                   cute::Tensor<CEngine, CLayout>&       tensor_c) {\n        assert(cute::size<1>(tensor_a) == cute::size<0>(tensor_b));\n        using value_type_c = typename CEngine::value_type;\n\n        for (unsigned int idx_m = 0; idx_m < cute::size<0>(tensor_c); ++idx_m) {\n            for (unsigned int idx_n = 0; idx_n < cute::size<1>(tensor_c); ++idx_n) {\n                value_type_c acc = convert<value_type_c>(0.f);\n                dot(acc, tensor_a(idx_m, cute::_), tensor_b(cute::_, idx_n));\n                tensor_c(idx_m, idx_n) = alpha * acc + beta * tensor_c(idx_m, idx_n);\n            }\n        }\n    }\n\n    template<typename ValueType>\n    void reference_gemm_naive_device(unsigned_tuple const&           gemm_shape,\n                                     arr_tuple const&                gemm_arr,\n                                     unsigned_tuple const&           gemm_ld,\n                                     ValueType const&                alpha,\n                                     device_vector<ValueType> const& A,\n                                     device_vector<ValueType> const& B,\n                                     ValueType const&                beta,\n                                     device_vector<ValueType>&       C) {\n        // Unpack arguments\n        auto [m, n, k]             = gemm_shape;\n        auto [lda, ldb, ldc]       = gemm_ld;\n        auto [arr_a, arr_b, arr_c] = gemm_arr;\n\n        auto make_tensor = [](auto ptr, auto sx, auto sy, auto ld, bool col_major) {\n            return cute::make_tensor(\n                ptr,\n                cute::make_layout(cute::make_shape(sx, sy), cute::make_stride(col_major ? 1 : ld, col_major ? ld : 1)));\n        };\n\n        cute::Tensor tensor_a = make_tensor(A.data(), m, k, lda, arr_a == cublasdx::col_major);\n        cute::Tensor tensor_b = make_tensor(B.data(), k, n, ldb, arr_b == cublasdx::col_major);\n        cute::Tensor tensor_c = make_tensor(C.data(), m, n, ldc, arr_c == cublasdx::col_major);\n\n        // Decide if device or host execution\n        const dim3 block_dim = {16, 16, 1};\n        const dim3 grid_dim  = {cute::ceil_div(m, block_dim.x), cute::ceil_div(n, block_dim.y), 1};\n        reference_gemm_naive_kernel<<<grid_dim, block_dim>>>(alpha, tensor_a, tensor_b, beta, tensor_c);\n        CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n        CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    }\n\n#define REFERENCE_FOR_TYPE(Prec)                                                           \\\n    template void reference_gemm_naive_device<Prec>(unsigned_tuple const&      gemm_shape, \\\n                                                    arr_tuple const&           gemm_arr,   \\\n                                                    unsigned_tuple const&      gemm_ld,    \\\n                                                    Prec const&                alpha,      \\\n                                                    device_vector<Prec> const& A,          \\\n                                                    device_vector<Prec> const& B,          \\\n                                                    Prec const&                beta,       \\\n                                                    device_vector<Prec>&       C);               \\\n                                                                                           \\\n    template void reference_gemm_naive_device<cublasdx::complex<Prec>>(                    \\\n        unsigned_tuple const&                         gemm_shape,                          \\\n        arr_tuple const&                              gemm_arr,                            \\\n        unsigned_tuple const&                         gemm_ld,                             \\\n        cublasdx::complex<Prec> const&                alpha,                               \\\n        device_vector<cublasdx::complex<Prec>> const& A,                                   \\\n        device_vector<cublasdx::complex<Prec>> const& B,                                   \\\n        cublasdx::complex<Prec> const&                beta,                                \\\n        device_vector<cublasdx::complex<Prec>>&       C);\n\n    REFERENCE_FOR_TYPE(double)\n    REFERENCE_FOR_TYPE(int64_t)\n    REFERENCE_FOR_TYPE(uint64_t)\n\n} // namespace example\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#define CUBLASDX_EXAMPLE_NO_THRUST\n#include \"naive_reference.hpp\"\n\nnamespace example {\n\n    template<class AccumType, class AEngine, class ALayout, class BEngine, class BLayout>\n    CUTE_HOST_DEVICE void dot(AccumType&                            accum,\n                              cute::Tensor<AEngine, ALayout> const& tensor_a,\n                              cute::Tensor<BEngine, BLayout> const& tensor_b) {\n        static_assert(cute::rank(decltype(tensor_a.layout()) {}) == cute::rank(decltype(tensor_b.layout()) {}) == 1);\n        for (unsigned int k_iter = 0; k_iter < cute::size<0>(tensor_a); ++k_iter) {\n            // Cast needed to emulate --> higher += lower * lower\n            accum += convert<AccumType>(tensor_a(k_iter)) * convert<AccumType>(tensor_b(k_iter));\n        }\n    }\n\n    template<class AEngine,\n             class ALayout,\n             class BEngine,\n             class BLayout,\n             class CEngine,\n             class CLayout,\n             class Alpha,\n             class Beta>\n    __global__ void reference_gemm_naive_kernel(Alpha                                alpha,\n                                                cute::Tensor<AEngine, ALayout> const tensor_a,\n                                                cute::Tensor<BEngine, BLayout> const tensor_b,\n                                                Beta                                 beta,\n                                                cute::Tensor<CEngine, CLayout>       tensor_c) {\n\n        using value_type_c = typename CEngine::value_type;\n\n        auto idx_m = blockIdx.x * blockDim.x + threadIdx.x;\n        auto idx_n = blockIdx.y * blockDim.y + threadIdx.y;\n\n        if (idx_m < cute::size<0>(tensor_c) and idx_n < cute::size<1>(tensor_c)) {\n            value_type_c acc = convert<value_type_c>(0.f);\n            dot(acc, tensor_a(idx_m, cute::_), tensor_b(cute::_, idx_n));\n            tensor_c(idx_m, idx_n) = alpha * acc + beta * tensor_c(idx_m, idx_n);\n        }\n    }\n\n\n    template<class AEngine,\n             class ALayout,\n             class BEngine,\n             class BLayout,\n             class CEngine,\n             class CLayout,\n             class Alpha,\n             class Beta>\n    void reference_gemm_naive_host(Alpha                                 alpha,\n                                   cute::Tensor<AEngine, ALayout> const& tensor_a,\n                                   cute::Tensor<BEngine, BLayout> const& tensor_b,\n                                   Beta                                  beta,\n                                   cute::Tensor<CEngine, CLayout>&       tensor_c) {\n        assert(cute::size<1>(tensor_a) == cute::size<0>(tensor_b));\n        using value_type_c = typename CEngine::value_type;\n\n        for (unsigned int idx_m = 0; idx_m < cute::size<0>(tensor_c); ++idx_m) {\n            for (unsigned int idx_n = 0; idx_n < cute::size<1>(tensor_c); ++idx_n) {\n                value_type_c acc = convert<value_type_c>(0.f);\n                dot(acc, tensor_a(idx_m, cute::_), tensor_b(cute::_, idx_n));\n                tensor_c(idx_m, idx_n) = alpha * acc + beta * tensor_c(idx_m, idx_n);\n            }\n        }\n    }\n\n    template<typename ValueType>\n    void reference_gemm_naive_device(unsigned_tuple const&           gemm_shape,\n                                     arr_tuple const&                gemm_arr,\n                                     unsigned_tuple const&           gemm_ld,\n                                     ValueType const&                alpha,\n                                     device_vector<ValueType> const& A,\n                                     device_vector<ValueType> const& B,\n                                     ValueType const&                beta,\n                                     device_vector<ValueType>&       C) {\n        // Unpack arguments\n        auto [m, n, k]             = gemm_shape;\n        auto [lda, ldb, ldc]       = gemm_ld;\n        auto [arr_a, arr_b, arr_c] = gemm_arr;\n\n        auto make_tensor = [](auto ptr, auto sx, auto sy, auto ld, bool col_major) {\n            return cute::make_tensor(\n                ptr,\n                cute::make_layout(cute::make_shape(sx, sy), cute::make_stride(col_major ? 1 : ld, col_major ? ld : 1)));\n        };\n\n        cute::Tensor tensor_a = make_tensor(A.data(), m, k, lda, arr_a == cublasdx::col_major);\n        cute::Tensor tensor_b = make_tensor(B.data(), k, n, ldb, arr_b == cublasdx::col_major);\n        cute::Tensor tensor_c = make_tensor(C.data(), m, n, ldc, arr_c == cublasdx::col_major);\n\n        // Decide if device or host execution\n        const dim3 block_dim = {16, 16, 1};\n        const dim3 grid_dim  = {cute::ceil_div(m, block_dim.x), cute::ceil_div(n, block_dim.y), 1};\n        reference_gemm_naive_kernel<<<grid_dim, block_dim>>>(alpha, tensor_a, tensor_b, beta, tensor_c);\n        CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n        CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    }\n\n#define REFERENCE_FOR_TYPE(Prec)                                                           \\\n    template void reference_gemm_naive_device<Prec>(unsigned_tuple const&      gemm_shape, \\\n                                                    arr_tuple const&           gemm_arr,   \\\n                                                    unsigned_tuple const&      gemm_ld,    \\\n                                                    Prec const&                alpha,      \\\n                                                    device_vector<Prec> const& A,          \\\n                                                    device_vector<Prec> const& B,          \\\n                                                    Prec const&                beta,       \\\n                                                    device_vector<Prec>&       C);               \\\n                                                                                           \\\n    template void reference_gemm_naive_device<cublasdx::complex<Prec>>(                    \\\n        unsigned_tuple const&                         gemm_shape,                          \\\n        arr_tuple const&                              gemm_arr,                            \\\n        unsigned_tuple const&                         gemm_ld,                             \\\n        cublasdx::complex<Prec> const&                alpha,                               \\\n        device_vector<cublasdx::complex<Prec>> const& A,                                   \\\n        device_vector<cublasdx::complex<Prec>> const& B,                                   \\\n        cublasdx::complex<Prec> const&                beta,                                \\\n        device_vector<cublasdx::complex<Prec>>&       C);\n\n    REFERENCE_FOR_TYPE(double)\n    REFERENCE_FOR_TYPE(int64_t)\n    REFERENCE_FOR_TYPE(uint64_t)\n\n} // namespace example\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/reference/naive_reference.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<unsigned int Scenario, class BLAS, class ValueType = example::uniform_value_type_t<BLAS>>\n__global__ void gemm_kernel(const ValueType* a,\n                            const ValueType* b,\n                            const ValueType* c,\n                            const ValueType  alpha,\n                            const ValueType  beta,\n                            ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    // Kernel launched with block dimensions equal to BLAS::block_dim\n    if constexpr (Scenario == 0) {\n        cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n        cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n        cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        cublasdx::copy_wait();\n        BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n    }\n    // Kernel launched with block dimensions of the same rank as BLAS::block_dim and\n    // at least the same number of threads\n    else if constexpr (Scenario == 1) {\n        cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n        cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n        cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        cublasdx::copy_wait();\n        BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n    }\n    // Kernel launched with 2D block dimensions blockDim=dim3(X, Y), BLAS::block_dim is 1D,\n    // blockDim must have at least as many threads in X dimension (1st dim) as BLAS::block_dim.\n    else if constexpr (Scenario == 2) {\n        if (threadIdx.y == 0) {\n            cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n            cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n            cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        }\n        cublasdx::copy_wait();\n        if (threadIdx.y == 0) {\n            BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        }\n        __syncthreads();\n        if (threadIdx.y == 0) {\n            cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n        }\n    }\n    // Kernel launched with 3D block dimensions blockDim=dim3(X, Y, Z), BLAS::block_dim is 2D,\n    // blockDim must have at least as many threads in the first 2 dimensions (X, Y) as BLAS::block_dim.\n    else if constexpr (Scenario == 3) {\n        if (threadIdx.z == 0) {\n            cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n            cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n            cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        }\n        cublasdx::copy_wait();\n        if (threadIdx.z == 0) {\n            BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        }\n        __syncthreads();\n        if (threadIdx.z == 0) {\n            cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n        }\n    }\n}\n\nconstexpr dim3 get_blas_block_dim(unsigned int scenario) {\n    if (scenario == 0) {\n        return dim3(128);\n    } else if (scenario == 1) {\n        return dim3(128);\n    } else if (scenario == 2) {\n        return dim3(128);\n    } else if (scenario == 3) {\n        return dim3(32, 4);\n    }\n    return dim3(128);\n}\n\nconstexpr dim3 get_kernel_block_dim(unsigned int scenario) {\n    if (scenario == 0) {\n        return dim3(128);\n    } else if (scenario == 1) {\n        return dim3(255);\n    } else if (scenario == 2) {\n        return dim3(196, 2);\n    } else if (scenario == 3) {\n        return dim3(32, 4, 2);\n    }\n    return dim3(128);\n}\n\n// This is an example of fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM.\n//\n// There are 4 scenarios (0, 1, 2, 3) listed below. Each scenario represents a different combination of block\n// dimensions set via BlockDim in BLAS description and block dimensions the kernel is launched with. The examples\n// demonstrates how to handle launching kernel with different layout and number of thread than the block dimensions\n// configured in BLAS description.\n//\n// Possible Scenarios:\n//\n// 0 - Kernel launched with block dimensions equal to BLAS::block_dim.\n// 1 - Kernel launched with block dimensions of the same rank as BLAS::block_dim and\n//     at least the same number of threads.\n// 2 - Kernel launched with 2D block dimensions blockDim=dim3(X, Y), BLAS::block_dim is 1D,\n//     blockDim must have at least as many threads in X dimension (1st dim) as BLAS::block_dim.\n// 3 - Kernel launched with 3D block dimensions blockDim=dim3(X, Y, Z), BLAS::block_dim is 2D,\n//     blockDim must have at least as many threads in the first 2 dimensions (X, Y) as BLAS::block_dim.\n//\n// In every scenario only one GEMM is performed. To see manual batching in a single CUDA block see\n// example batched_gemm_fp64.\ntemplate<unsigned int Scenario, unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr auto blas_block_dim   = get_blas_block_dim(Scenario);\n    constexpr auto kernel_block_dim = get_kernel_block_dim(Scenario);\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto arrangement_a = cublasdx::col_major;\n    constexpr auto arrangement_b = cublasdx::row_major;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets layout and number of threads.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<arrangement_a, arrangement_b>() +\n                 cublasdx::Block() + cublasdx::BlockDim<blas_block_dim.x, blas_block_dim.y, blas_block_dim.z>() +\n                 cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type*    inputs;\n    value_type*    output;\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n    auto           inputs_size   = global_a_size + global_b_size + global_c_size;\n\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + global_a_size;\n    value_type* c     = b + global_b_size;\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(gemm_kernel<Scenario, BLAS>,\n                                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<Scenario, BLAS>\n        <<<1, kernel_block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << Scenario << \": Success\" << std::endl;\n        return 0;\n    }\n    std::cout << Scenario << \": Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        int status = 0;\n        status     = simple_gemm<0, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<1, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<2, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<3, Arch>();\n        return status;\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<unsigned int Scenario, class BLAS, class ValueType = example::uniform_value_type_t<BLAS>>\n__global__ void gemm_kernel(const ValueType* a,\n                            const ValueType* b,\n                            const ValueType* c,\n                            const ValueType  alpha,\n                            const ValueType  beta,\n                            ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    // Kernel launched with block dimensions equal to BLAS::block_dim\n    if constexpr (Scenario == 0) {\n        cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n        cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n        cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        cublasdx::copy_wait();\n        BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n    }\n    // Kernel launched with block dimensions of the same rank as BLAS::block_dim and\n    // at least the same number of threads\n    else if constexpr (Scenario == 1) {\n        cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n        cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n        cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        cublasdx::copy_wait();\n        BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        __syncthreads();\n        cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n    }\n    // Kernel launched with 2D block dimensions blockDim=dim3(X, Y), BLAS::block_dim is 1D,\n    // blockDim must have at least as many threads in X dimension (1st dim) as BLAS::block_dim.\n    else if constexpr (Scenario == 2) {\n        if (threadIdx.y == 0) {\n            cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n            cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n            cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        }\n        cublasdx::copy_wait();\n        if (threadIdx.y == 0) {\n            BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        }\n        __syncthreads();\n        if (threadIdx.y == 0) {\n            cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n        }\n    }\n    // Kernel launched with 3D block dimensions blockDim=dim3(X, Y, Z), BLAS::block_dim is 2D,\n    // blockDim must have at least as many threads in the first 2 dimensions (X, Y) as BLAS::block_dim.\n    else if constexpr (Scenario == 3) {\n        if (threadIdx.z == 0) {\n            cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n            cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n            cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n        }\n        cublasdx::copy_wait();\n        if (threadIdx.z == 0) {\n            BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n        }\n        __syncthreads();\n        if (threadIdx.z == 0) {\n            cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n        }\n    }\n}\n\nconstexpr dim3 get_blas_block_dim(unsigned int scenario) {\n    if (scenario == 0) {\n        return dim3(128);\n    } else if (scenario == 1) {\n        return dim3(128);\n    } else if (scenario == 2) {\n        return dim3(128);\n    } else if (scenario == 3) {\n        return dim3(32, 4);\n    }\n    return dim3(128);\n}\n\nconstexpr dim3 get_kernel_block_dim(unsigned int scenario) {\n    if (scenario == 0) {\n        return dim3(128);\n    } else if (scenario == 1) {\n        return dim3(255);\n    } else if (scenario == 2) {\n        return dim3(196, 2);\n    } else if (scenario == 3) {\n        return dim3(32, 4, 2);\n    }\n    return dim3(128);\n}\n\n// This is an example of fp16 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM.\n//\n// There are 4 scenarios (0, 1, 2, 3) listed below. Each scenario represents a different combination of block\n// dimensions set via BlockDim in BLAS description and block dimensions the kernel is launched with. The examples\n// demonstrates how to handle launching kernel with different layout and number of thread than the block dimensions\n// configured in BLAS description.\n//\n// Possible Scenarios:\n//\n// 0 - Kernel launched with block dimensions equal to BLAS::block_dim.\n// 1 - Kernel launched with block dimensions of the same rank as BLAS::block_dim and\n//     at least the same number of threads.\n// 2 - Kernel launched with 2D block dimensions blockDim=dim3(X, Y), BLAS::block_dim is 1D,\n//     blockDim must have at least as many threads in X dimension (1st dim) as BLAS::block_dim.\n// 3 - Kernel launched with 3D block dimensions blockDim=dim3(X, Y, Z), BLAS::block_dim is 2D,\n//     blockDim must have at least as many threads in the first 2 dimensions (X, Y) as BLAS::block_dim.\n//\n// In every scenario only one GEMM is performed. To see manual batching in a single CUDA block see\n// example batched_gemm_fp64.\ntemplate<unsigned int Scenario, unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 64;\n    constexpr unsigned int n = 64;\n    constexpr unsigned int k = 64;\n\n    // Selected CUDA block size (1D)\n    constexpr auto blas_block_dim   = get_blas_block_dim(Scenario);\n    constexpr auto kernel_block_dim = get_kernel_block_dim(Scenario);\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto arrangement_a = cublasdx::col_major;\n    constexpr auto arrangement_b = cublasdx::row_major;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets layout and number of threads.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<__half>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::Arrangement<arrangement_a, arrangement_b>() +\n                 cublasdx::Block() + cublasdx::BlockDim<blas_block_dim.x, blas_block_dim.y, blas_block_dim.z>() +\n                 cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type*    inputs;\n    value_type*    output;\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n    auto           inputs_size   = global_a_size + global_b_size + global_c_size;\n\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + global_a_size;\n    value_type* c     = b + global_b_size;\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<Scenario, BLAS>),\n                                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel\n    gemm_kernel<Scenario, BLAS>\n        <<<1, kernel_block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output)) {\n        std::cout << Scenario << \": Success\" << std::endl;\n        return 0;\n    }\n    std::cout << Scenario << \": Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        int status = 0;\n        status     = simple_gemm<0, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<1, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<2, Arch>();\n        if (status)\n            return status;\n        status = simple_gemm<3, Arch>();\n        return status;\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/04_gemm_blockdim/blockdim_gemm_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Number of batches\nconstexpr unsigned int batches = 2;\n\ntemplate<class BLAS, class ValueType = example::uniform_value_type_t<BLAS>>\n__global__ void gemm_kernel(const ValueType* a,\n                            const ValueType* b,\n                            const ValueType* c,\n                            const ValueType  alpha,\n                            const ValueType  beta,\n                            ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // threads (X, 0) calculates the first batch\n    // threads (X, 1) calculates the 2nd batch etc.\n    // We need to move pointers to corresponding batches\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    a += threadIdx.y * global_a_size;\n    b += threadIdx.y * global_b_size;\n    c += threadIdx.y * global_c_size;\n    output += threadIdx.y * global_c_size;\n\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + threadIdx.y * BLAS::a_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + (batches * BLAS::a_size) + threadIdx.y * BLAS::b_size;\n    value_type* smem_c = reinterpret_cast<value_type*>(smem) + (batches * BLAS::a_size) + (batches * BLAS::b_size) +\n                         threadIdx.y * BLAS::c_size;\n\n    // Load all batches\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of two fp32 general matrix-matrix multiplications (GEMM) performed in a single CUDA block:\n//\n//              C(X) = alpha * A(X) * B(X) + beta * C(X)\n//\n// * X - batch id\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example BlockDim operator added to the GEMM definition defines the number and the layout of threads\n// participating in the calculations.\n//\n// In order to achieve batching on a block level 1D BlockDim operator (BlockDim<64>) is added to the description,\n// and launching the kernel with 2D block - dim3(64,2). Threads with the same 2nd dimension (threadIdx.y) participate\n// in the same calculations.\n//\n// Note: Examples demonstrates how to set block dimensions to enable manual batching. The performance of included\n// kernel was not checked and it is not optimized.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 16;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 64;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value.\n    //    - Optional\n    // 5. Block operator informs that GEMM should be performed on CUDA block level.\n    // 6. BlockDim operator sets layout and number of threads.\n    //    - Optional\n    // 7. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::Alignment<16, 16, 16>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n    auto           inputs_size   = batches * (global_a_size + global_b_size + global_c_size);\n\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, batches * global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (batches * global_a_size);\n    value_type* c     = b + (batches * global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(batches * global_a_size);\n    auto host_b = example::get_random_data<value_type>(batches * global_b_size);\n    auto host_c = example::get_random_data<value_type>(batches * global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(a, host_a.data(), batches * global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(b, host_b.data(), batches * global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(c, host_c.data(), batches * global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    const dim3 block_dim          = dim3(block_size, batches);\n    const auto shared_memory_size = batches * cublasdx::get_shared_storage_size<BLAS>();\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(\n        cudaFuncSetAttribute(gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results of the 1st batch back to host\n    std::vector<value_type> host_output1(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output1.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    // Copy results of the 2nd batch back to host\n    std::vector<value_type> host_output2(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(\n        host_output2.data(), output + global_c_size, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference for the 1st batch\n    decltype(host_a) host_a1(host_a.begin(), host_a.begin() + global_a_size);\n    decltype(host_b) host_b1(host_b.begin(), host_b.begin() + global_b_size);\n    decltype(host_c) host_c1(host_c.begin(), host_c.begin() + global_c_size);\n    auto             reference_host_output1 = example::reference_gemm<BLAS>(alpha, host_a1, host_b1, beta, host_c1);\n    // Calculate reference for the 2nd batch\n    decltype(host_a) host_a2(host_a.begin() + global_a_size, host_a.begin() + 2 * global_a_size);\n    decltype(host_b) host_b2(host_b.begin() + global_b_size, host_b.begin() + 2 * global_b_size);\n    decltype(host_c) host_c2(host_c.begin() + global_c_size, host_c.begin() + 2 * global_c_size);\n    auto             reference_host_output2 = example::reference_gemm<BLAS>(alpha, host_a2, host_b2, beta, host_c2);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output1, reference_host_output1) &&\n        example::check_error<BLAS>(host_output2, reference_host_output2)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\n// Number of batches\nconstexpr unsigned int batches = 2;\n\ntemplate<class BLAS, class ValueType = example::uniform_value_type_t<BLAS>>\n__global__ void gemm_kernel(const ValueType* a,\n                            const ValueType* b,\n                            const ValueType* c,\n                            const ValueType  alpha,\n                            const ValueType  beta,\n                            ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // threads (X, 0) calculates the first batch\n    // threads (X, 1) calculates the 2nd batch etc.\n    // We need to move pointers to corresponding batches\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    a += threadIdx.y * global_a_size;\n    b += threadIdx.y * global_b_size;\n    c += threadIdx.y * global_c_size;\n    output += threadIdx.y * global_c_size;\n\n    value_type* smem_a = reinterpret_cast<value_type*>(smem) + threadIdx.y * BLAS::a_size;\n    value_type* smem_b = reinterpret_cast<value_type*>(smem) + (batches * BLAS::a_size) + threadIdx.y * BLAS::b_size;\n    value_type* smem_c = reinterpret_cast<value_type*>(smem) + (batches * BLAS::a_size) + (batches * BLAS::b_size) +\n                         threadIdx.y * BLAS::c_size;\n\n    // Load all batches\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto a_shared_tensor = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\n// This is an example of two fp32 general matrix-matrix multiplications (GEMM) performed in a single CUDA block:\n//\n//              C(X) = alpha * A(X) * B(X) + beta * C(X)\n//\n// * X - batch id\n// * A, B, and C are matrices containing real single precision floating-point values.\n// * alpha and beta are real single precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example BlockDim operator added to the GEMM definition defines the number and the layout of threads\n// participating in the calculations.\n//\n// In order to achieve batching on a block level 1D BlockDim operator (BlockDim<64>) is added to the description,\n// and launching the kernel with 2D block - dim3(64,2). Threads with the same 2nd dimension (threadIdx.y) participate\n// in the same calculations.\n//\n// Note: Examples demonstrates how to set block dimensions to enable manual batching. The performance of included\n// kernel was not checked and it is not optimized.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 16;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 16;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 64;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value.\n    //    - Optional\n    // 5. Block operator informs that GEMM should be performed on CUDA block level.\n    // 6. BlockDim operator sets layout and number of threads.\n    //    - Optional\n    // 7. Targeted CUDA compute capability is selected with SM operator.\n    using BLAS =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() + cublasdx::Alignment<16, 16, 16>() +\n                 cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    using value_type = example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n    auto           inputs_size   = batches * (global_a_size + global_b_size + global_c_size);\n\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, batches * global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (batches * global_a_size);\n    value_type* c     = b + (batches * global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(batches * global_a_size);\n    auto host_b = example::get_random_data<value_type>(batches * global_b_size);\n    auto host_c = example::get_random_data<value_type>(batches * global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(a, host_a.data(), batches * global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(b, host_b.data(), batches * global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(c, host_c.data(), batches * global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    const dim3 block_dim          = dim3(block_size, batches);\n    const auto shared_memory_size = batches * cublasdx::get_shared_storage_size<BLAS>();\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(\n        hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results of the 1st batch back to host\n    std::vector<value_type> host_output1(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output1.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    // Copy results of the 2nd batch back to host\n    std::vector<value_type> host_output2(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(\n        host_output2.data(), output + global_c_size, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference for the 1st batch\n    decltype(host_a) host_a1(host_a.begin(), host_a.begin() + global_a_size);\n    decltype(host_b) host_b1(host_b.begin(), host_b.begin() + global_b_size);\n    decltype(host_c) host_c1(host_c.begin(), host_c.begin() + global_c_size);\n    auto             reference_host_output1 = example::reference_gemm<BLAS>(alpha, host_a1, host_b1, beta, host_c1);\n    // Calculate reference for the 2nd batch\n    decltype(host_a) host_a2(host_a.begin() + global_a_size, host_a.begin() + 2 * global_a_size);\n    decltype(host_b) host_b2(host_b.begin() + global_b_size, host_b.begin() + 2 * global_b_size);\n    decltype(host_c) host_c2(host_c.begin() + global_c_size, host_c.begin() + 2 * global_c_size);\n    auto             reference_host_output2 = example::reference_gemm<BLAS>(alpha, host_a2, host_b2, beta, host_c2);\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output1, reference_host_output1) &&\n        example::check_error<BLAS>(host_output2, reference_host_output2)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/05_gemm_batched/batched_gemm_fp64.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <iostream>\n#include <system_error>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         int AccumulationIterations,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\n__launch_bounds__(BLAS::max_threads_per_block, 1) __global__ void gemm_kernel(GEMMShape const   gemm_shape,\n                                                                              GEMMArr const     gemm_arr,\n                                                                              GEMMLD const      gemm_ld,\n                                                                              Alpha const       alpha,\n                                                                              AValueType const* a,\n                                                                              BValueType const* b,\n                                                                              Beta const        beta,\n                                                                              CValueType*       c) {\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // ================================\n    // 1. PREPARE GLOBAL MEMORY TENSORS\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    const auto [m, n, k]       = gemm_shape;\n    const auto [lda, ldb, ldc] = gemm_ld;\n    auto global_a              = cublasdx::make_gmem_tensor<cute::get<0>(gemm_arr)>(a, m, k, lda);\n    auto global_b              = cublasdx::make_gmem_tensor<cute::get<1>(gemm_arr)>(b, k, n, ldb);\n    auto global_c              = cublasdx::make_gmem_tensor<cute::get<2>(gemm_arr)>(c, m, n, ldc);\n\n    // Get tile row of A, tile col of B and tile of C\n    const auto tile_slice_a_gmem = cublasdx::get_tile_row(global_a, BLAS::a_shape, blockIdx.x);\n    const auto tile_slice_b_gmem = cublasdx::get_tile_col(global_b, BLAS::b_shape, blockIdx.y);\n    auto       tile_c_gmem       = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n\n    // ================================\n    // 2. PREPARE SHARED MEMORY TENSORS\n\n    // Slice shared memory into tensors for proper alignment in 2-stage pipelining\n    auto [s_a, s_b, s_a_n, s_b_n] =\n        cublasdx::shared_memory::slice<AValueType, BValueType, AValueType, BValueType>(smem,\n                                                                                       cublasdx::alignment_of_v_a<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_a(),\n                                                                                       cublasdx::alignment_of_v_b<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_b(),\n                                                                                       cublasdx::alignment_of_v_a<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_a(),\n                                                                                       cublasdx::alignment_of_v_b<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_b());\n\n    // ==================================\n    // 3. PREPARE 2-STAGE MEMORY PIPELINE\n\n    // Since both slices have an iteration dimension equal to number of necessary\n    // GEMM stages, we can just use it for iteration\n    const auto k_stages = cute::get<2>(cute::shape(tile_slice_a_gmem.layout()));\n\n    auto get_tile_from_slice = [](auto& slice, auto index) { return slice(cublasdx::slice, cublasdx::slice, index); };\n\n    // Schedule first stage into memory pipeline queue\n    // cute::Int<X> is a static integer similar to std::integral_constant\n    constexpr auto static_first_stage_index = cute::Int<0> {};\n    cublasdx::copy<BLAS, alignment::a>(get_tile_from_slice(tile_slice_a_gmem, static_first_stage_index), s_a);\n    cublasdx::copy<BLAS, alignment::b>(get_tile_from_slice(tile_slice_b_gmem, static_first_stage_index), s_b);\n\n    // ==============================================\n    // 4. EXECUTE GEMM WITH ACCUMULATION IN REGISTERS\n\n    auto accumulator = BLAS().suggest_accumulator();\n\n    // Prepare higher precision accumulator\n    auto c_frag_accumulator = cublasdx::make_fragment_like<CValueType>(accumulator.make_empty_fragment());\n    cublasdx::clear(c_frag_accumulator);\n\n#pragma unroll 1\n    for (int stage = 0; stage < k_stages; stage += AccumulationIterations) {\n// Inner loop for accumulating partial sums (fast accumulator is cleared every AccumulationIterations)\n#pragma unroll 1\n        for (int iter = 0; iter < AccumulationIterations; ++iter) {\n            const auto next_stage = stage + iter + 1;\n\n            // Wait for previous stage\n            cublasdx::copy_wait();\n\n            // Load next stage of A and B\n            if (next_stage < k_stages) {\n                cublasdx::copy<BLAS, alignment::a>(get_tile_from_slice(tile_slice_a_gmem, next_stage), s_a_n);\n                cublasdx::copy<BLAS, alignment::b>(get_tile_from_slice(tile_slice_b_gmem, next_stage), s_b_n);\n            }\n\n            // Accumulate results from this stage\n            BLAS().execute(s_a, s_b, accumulator);\n\n            // Swap for next iteration\n            example::swap(s_a_n, s_a);\n            example::swap(s_b_n, s_b);\n        }\n\n        // Accumulate partial sums in higher precision\n        cublasdx::transform_fragment(\n            accumulator.get_results(), c_frag_accumulator, c_frag_accumulator, [](auto a, auto b) { return a + b; });\n\n        // Clear fast accumulator for next iteration\n        accumulator.clear();\n    }\n\n    // ===========\n    // 5. EPILOGUE\n    auto d_frag = accumulator.make_partition_and_copy(tile_c_gmem);\n    cublasdx::axpby(static_cast<CValueType>(alpha), c_frag_accumulator, static_cast<CValueType>(beta), d_frag);\n    accumulator.partition_and_copy(d_frag, tile_c_gmem);\n}\n\ntemplate<class BLAS,\n         int AccumulationIterations,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\nauto measure_cublasdx(GEMMShape         gemm_shape,\n                      GEMMArr           gemm_arr,\n                      GEMMLD            gemm_ld,\n                      const Alpha       alpha,\n                      const AValueType* a,\n                      const BValueType* b,\n                      const Beta        beta,\n                      CValueType*       c,\n                      cudaStream_t      stream) {\n    // Grid size configuration\n    const auto m = cute::get<0>(gemm_shape);\n    const auto n = cute::get<1>(gemm_shape);\n\n    constexpr auto tile_m = cublasdx::size_of<BLAS>::m;\n    constexpr auto tile_n = cublasdx::size_of<BLAS>::n;\n    constexpr auto tile_k = cublasdx::size_of<BLAS>::k;\n\n    std::vector<CValueType> results(m * n);\n\n    std::cout << \"tile_m, tile_n, tile_k: \" << tile_m << \", \" << tile_n << \", \" << tile_k << std::endl;\n    std::cout << \"Alignment: \" << cublasdx::alignment_of<BLAS>::a << \", \" << cublasdx::alignment_of<BLAS>::b << \", \"\n              << cublasdx::alignment_of<BLAS>::c << std::endl;\n    std::cout << \"block_size: \" << BLAS::max_threads_per_block << std::endl;\n\n    constexpr bool reverse_block_coord =\n        (cute::get<0>(gemm_arr) == cublasdx::row_major) and (cute::get<1>(gemm_arr) == cublasdx::row_major);\n\n    dim3 grid_dim = cute::conditional_return<reverse_block_coord>(dim3 {n / tile_n, m / tile_m, 1},\n                                                                  dim3 {(m / tile_m), (n / tile_n), 1});\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    auto shared_memory_size =\n        cublasdx::make_shared_storage_calculator()\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(AValueType), BLAS::suggest_layout_smem_a())\n            .add(cublasdx::alignment_of_v_b<BLAS>, sizeof(BValueType), BLAS::suggest_layout_smem_b())\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(AValueType), BLAS::suggest_layout_smem_a())\n            .add(cublasdx::alignment_of_v_b<BLAS>, sizeof(BValueType), BLAS::suggest_layout_smem_b())\n            .get();\n\n    auto kernel = gemm_kernel<BLAS,\n                              AccumulationIterations,\n                              GEMMShape,\n                              GEMMArr,\n                              GEMMLD,\n                              Alpha,\n                              AValueType,\n                              BValueType,\n                              Beta,\n                              CValueType>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    auto run_cublasdx_gemm = [&](cudaStream_t str) {\n        kernel<<<grid_dim, BLAS::block_dim, shared_memory_size, str>>>(\n            gemm_shape, gemm_arr, gemm_ld, alpha, a, b, beta, c);\n    };\n\n    // Perform computations with cuBLASDx BLAS operator.\n    run_cublasdx_gemm(stream);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(results.data(), c, results.size() * sizeof(CValueType), cudaMemcpyDeviceToHost));\n\n    return results;\n}\n\n// This is an example of using cuBLASDx for computing partial sums of a GEMM and summing them in a\n// higher precision storage for enhanced accuracy of the final result.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported.\n// Note that alpha and beta are expected to have the same precision and type (real or complex value)\n// as the matrix C elements.\n//\n// The partial accumulation is performed every N iterations of the K dimension of the GEMM, which can\n// be configured via the partial_sum_iterations template parameter.\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint gemm_device_partial_sums() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // A matrix which is small but deep, a lot of roundings will be needed\n    const auto m            = 768;  // Global M GEMM Size\n    const auto n            = 768;  // Global N GEMM Size\n    const auto k            = 8192; // Global K GEMM Size\n    auto       global_shape = cute::make_tuple(m, n, k);\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Higher precision to perform partial sums in\n    // NOTE: either this type must be implicitly convertible to\n    // compute type, or converter should be provided in appropriate places.\n    // please refer to simple_gemm_fp32_decoupled.cu example for more details.\n    using c_accumulation_precision = double;\n    // What should be the interval for accumulating in higher precision\n    constexpr int partial_sum_iterations = 64;\n\n    if (k % partial_sum_iterations != 0) {\n        std::cerr << \"K must be divisible by partial_sum_iterations\" << std::endl;\n        return 1;\n    }\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    using c_accumulation_value_type = cute::conditional_t<type == cublasdx::type::real,\n                                                          c_accumulation_precision,\n                                                          cublasdx::complex<c_accumulation_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned int tile_m = 128;\n    constexpr unsigned int tile_n = 128;\n    constexpr unsigned int tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr unsigned int tile_threads = 128;\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Input used to be converted to the final compute precision,\n    // this can be used to simulate either in-flight quantization\n    // or flexible upcasting of data to save on bandwidth\n    using a_io_value_type = a_compute_value_type;\n    using b_io_value_type = b_compute_value_type;\n\n    // This is the accumulation precision\n    using c_io_value_type = c_accumulation_value_type;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 64;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    // Use tuples to avoid passing 20 arguments to a function\n    constexpr auto global_arrangement = cute::make_tuple(\n        // These must be passed as integral constants to properly dispatch static striding\n        std::integral_constant<cublasdx::arrangement, global_arrangement_a> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_b> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_c> {});\n\n    const auto global_ld = cute::make_tuple(global_lda, global_ldb, global_ldc);\n\n    // Test implementation\n    a_io_value_type* a_cublasdx = nullptr;\n    b_io_value_type* b_cublasdx = nullptr;\n    c_io_value_type* c_cublasdx = nullptr;\n\n    a_compute_value_type* a_cublas = nullptr;\n    b_compute_value_type* b_cublas = nullptr;\n    c_compute_value_type* c_cublas = nullptr;\n\n    // Use nullptr tensors to make it easier to calculate memory requirements\n    auto global_a = cublasdx::make_gmem_tensor<cute::get<0>(global_arrangement)>(a_cublas, m, k, global_lda);\n    auto global_b = cublasdx::make_gmem_tensor<cute::get<1>(global_arrangement)>(b_cublas, k, n, global_ldb);\n    auto global_c = cublasdx::make_gmem_tensor<cute::get<2>(global_arrangement)>(c_cublas, m, n, global_ldc);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&a_cublasdx, cublasdx::cosize(global_a.layout()) * sizeof(a_io_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&a_cublas, cublasdx::cosize(global_a.layout()) * sizeof(a_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&b_cublasdx, cublasdx::cosize(global_b.layout()) * sizeof(b_io_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&b_cublas, cublasdx::cosize(global_b.layout()) * sizeof(b_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&c_cublasdx, cublasdx::cosize(global_c.layout()) * sizeof(c_io_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&c_cublas, cublasdx::cosize(global_c.layout()) * sizeof(c_compute_value_type)));\n\n    // Fill the A, B, C matrices with random values.\n    {\n        auto host_a_io = example::get_random_data<a_io_value_type>(m * k);\n        auto host_b_io = example::get_random_data<b_io_value_type>(k * n);\n        auto host_c_io = example::get_random_data<c_io_value_type>(m * n);\n\n        static_assert(std::is_convertible_v<a_io_value_type, a_compute_value_type>);\n        auto host_a_compute = std::vector<a_compute_value_type>(host_a_io.begin(), host_a_io.end());\n\n        static_assert(std::is_convertible_v<b_io_value_type, b_compute_value_type>);\n        auto host_b_compute = std::vector<b_compute_value_type>(host_b_io.begin(), host_b_io.end());\n\n        static_assert(std::is_convertible_v<c_io_value_type, c_compute_value_type>);\n        auto host_c_compute = std::vector<c_compute_value_type>(host_c_io.begin(), host_c_io.end());\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(a_cublasdx, host_a_io.data(), m * k * sizeof(a_io_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(b_cublasdx, host_b_io.data(), k * n * sizeof(b_io_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(c_cublasdx, host_c_io.data(), m * n * sizeof(c_io_value_type), cudaMemcpyHostToDevice));\n\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(a_cublas, host_a_compute.data(), m * k * sizeof(a_compute_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(b_cublas, host_b_compute.data(), k * n * sizeof(b_compute_value_type), cudaMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            cudaMemcpy(c_cublas, host_c_compute.data(), m * n * sizeof(c_compute_value_type), cudaMemcpyHostToDevice));\n        // destroy host vectors\n    }\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() +\n                          cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() +\n                          cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Execute cuBLASDx and cuBLASLt\n    // =============================\n\n    auto host_dx_results = measure_cublasdx<BLAS, partial_sum_iterations>(\n        global_shape, global_arrangement, global_ld, alpha, a_cublasdx, b_cublasdx, beta, c_cublasdx, stream);\n\n    // Measure cuBLAS performance.\n    auto host_blas_results = example::cublaslt_runner<a_compute_value_type, b_compute_value_type, c_compute_value_type>(\n                                 global_shape, global_arrangement, global_ld)\n                                 .execute_with_results(alpha, a_cublas, b_cublas, beta, c_cublas, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m, n, k: \" << m << \", \" << n << \", \" << k << std::endl;\n    std::cout << \"Compute Type A: \" << example::type_string<a_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type B: \" << example::type_string<b_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type C: \" << example::type_string<c_compute_value_type>() << std::endl;\n    std::cout << \"Accumulation Type: \" << example::type_string<c_accumulation_value_type>() << std::endl;\n    std::cout << \"Accumulation Every N Iterations: \" << partial_sum_iterations << std::endl;\n\n    std::cout << \"Dx Input Precision A: \" << example::precision_string<a_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision B: \" << example::precision_string<b_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision C: \" << example::precision_string<c_io_value_type>() << std::endl;\n\n    std::cout << \"cuBLAS Input Precision A: \" << example::precision_string<a_compute_value_type>() << std::endl;\n    std::cout << \"cuBLAS Input Precision B: \" << example::precision_string<b_compute_value_type>() << std::endl;\n    std::cout << \"cuBLAS Input Precision C: \" << example::precision_string<c_compute_value_type>() << std::endl;\n\n    auto error = example::calculate_error(host_dx_results, host_blas_results);\n    std::cout << \"Error = \" << error << \"\\n\";\n\n    if (example::is_error_acceptable<a_compute_value_type, b_compute_value_type, c_compute_value_type>(error)) {\n        std::cout << \"Success!\\n\";\n    } else {\n        std::cout << \"Failure!\\n\";\n    }\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(a_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(b_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(c_cublasdx));\n    CUDA_CHECK_AND_EXIT(cudaFree(a_cublas));\n    CUDA_CHECK_AND_EXIT(cudaFree(b_cublas));\n    CUDA_CHECK_AND_EXIT(cudaFree(c_cublas));\n\n    return 0;\n}\n\nstruct gemm_device_partial_sums_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_device_partial_sums<Arch, Modifier>();\n    }\n};\n\nint main() {\n    return example::sm_runner(gemm_device_partial_sums_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <array>\n#include <iostream>\n#include <system_error>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS,\n         int AccumulationIterations,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\n__launch_bounds__(BLAS::max_threads_per_block, 1) __global__ void gemm_kernel(GEMMShape const   gemm_shape,\n                                                                              GEMMArr const     gemm_arr,\n                                                                              GEMMLD const      gemm_ld,\n                                                                              Alpha const       alpha,\n                                                                              AValueType const* a,\n                                                                              BValueType const* b,\n                                                                              Beta const        beta,\n                                                                              CValueType*       c) {\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // ================================\n    // 1. PREPARE GLOBAL MEMORY TENSORS\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    const auto [m, n, k]       = gemm_shape;\n    const auto [lda, ldb, ldc] = gemm_ld;\n    auto global_a              = cublasdx::make_gmem_tensor<cute::get<0>(gemm_arr)>(a, m, k, lda);\n    auto global_b              = cublasdx::make_gmem_tensor<cute::get<1>(gemm_arr)>(b, k, n, ldb);\n    auto global_c              = cublasdx::make_gmem_tensor<cute::get<2>(gemm_arr)>(c, m, n, ldc);\n\n    // Get tile row of A, tile col of B and tile of C\n    const auto tile_slice_a_gmem = cublasdx::get_tile_row(global_a, BLAS::a_shape, blockIdx.x);\n    const auto tile_slice_b_gmem = cublasdx::get_tile_col(global_b, BLAS::b_shape, blockIdx.y);\n    auto       tile_c_gmem       = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n\n    // ================================\n    // 2. PREPARE SHARED MEMORY TENSORS\n\n    // Slice shared memory into tensors for proper alignment in 2-stage pipelining\n    auto [s_a, s_b, s_a_n, s_b_n] =\n        cublasdx::shared_memory::slice<AValueType, BValueType, AValueType, BValueType>(smem,\n                                                                                       cublasdx::alignment_of_v_a<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_a(),\n                                                                                       cublasdx::alignment_of_v_b<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_b(),\n                                                                                       cublasdx::alignment_of_v_a<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_a(),\n                                                                                       cublasdx::alignment_of_v_b<BLAS>,\n                                                                                       BLAS::suggest_layout_smem_b());\n\n    // ==================================\n    // 3. PREPARE 2-STAGE MEMORY PIPELINE\n\n    // Since both slices have an iteration dimension equal to number of necessary\n    // GEMM stages, we can just use it for iteration\n    const auto k_stages = cute::get<2>(cute::shape(tile_slice_a_gmem.layout()));\n\n    auto get_tile_from_slice = [](auto& slice, auto index) { return slice(cublasdx::slice, cublasdx::slice, index); };\n\n    // Schedule first stage into memory pipeline queue\n    // cute::Int<X> is a static integer similar to std::integral_constant\n    constexpr auto static_first_stage_index = cute::Int<0> {};\n    cublasdx::copy<BLAS, alignment::a>(get_tile_from_slice(tile_slice_a_gmem, static_first_stage_index), s_a);\n    cublasdx::copy<BLAS, alignment::b>(get_tile_from_slice(tile_slice_b_gmem, static_first_stage_index), s_b);\n\n    // ==============================================\n    // 4. EXECUTE GEMM WITH ACCUMULATION IN REGISTERS\n\n    auto accumulator = BLAS().suggest_accumulator();\n\n    // Prepare higher precision accumulator\n    auto c_frag_accumulator = cublasdx::make_fragment_like<CValueType>(accumulator.make_empty_fragment());\n    cublasdx::clear(c_frag_accumulator);\n\n#pragma unroll 1\n    for (int stage = 0; stage < k_stages; stage += AccumulationIterations) {\n// Inner loop for accumulating partial sums (fast accumulator is cleared every AccumulationIterations)\n#pragma unroll 1\n        for (int iter = 0; iter < AccumulationIterations; ++iter) {\n            const auto next_stage = stage + iter + 1;\n\n            // Wait for previous stage\n            cublasdx::copy_wait();\n\n            // Load next stage of A and B\n            if (next_stage < k_stages) {\n                cublasdx::copy<BLAS, alignment::a>(get_tile_from_slice(tile_slice_a_gmem, next_stage), s_a_n);\n                cublasdx::copy<BLAS, alignment::b>(get_tile_from_slice(tile_slice_b_gmem, next_stage), s_b_n);\n            }\n\n            // Accumulate results from this stage\n            BLAS().execute(s_a, s_b, accumulator);\n\n            // Swap for next iteration\n            example::swap(s_a_n, s_a);\n            example::swap(s_b_n, s_b);\n        }\n\n        // Accumulate partial sums in higher precision\n        cublasdx::transform_fragment(\n            accumulator.get_results(), c_frag_accumulator, c_frag_accumulator, [](auto a, auto b) { return a + b; });\n\n        // Clear fast accumulator for next iteration\n        accumulator.clear();\n    }\n\n    // ===========\n    // 5. EPILOGUE\n    auto d_frag = accumulator.make_partition_and_copy(tile_c_gmem);\n    cublasdx::axpby(static_cast<CValueType>(alpha), c_frag_accumulator, static_cast<CValueType>(beta), d_frag);\n    accumulator.partition_and_copy(d_frag, tile_c_gmem);\n}\n\ntemplate<class BLAS,\n         int AccumulationIterations,\n         class GEMMShape,\n         class GEMMArr,\n         class GEMMLD,\n         class Alpha,\n         class AValueType,\n         class BValueType,\n         class Beta,\n         class CValueType>\nauto measure_cublasdx(GEMMShape         gemm_shape,\n                      GEMMArr           gemm_arr,\n                      GEMMLD            gemm_ld,\n                      const Alpha       alpha,\n                      const AValueType* a,\n                      const BValueType* b,\n                      const Beta        beta,\n                      CValueType*       c,\n                      hipStream_t      stream) {\n    // Grid size configuration\n    const auto m = cute::get<0>(gemm_shape);\n    const auto n = cute::get<1>(gemm_shape);\n\n    constexpr auto tile_m = cublasdx::size_of<BLAS>::m;\n    constexpr auto tile_n = cublasdx::size_of<BLAS>::n;\n    constexpr auto tile_k = cublasdx::size_of<BLAS>::k;\n\n    std::vector<CValueType> results(m * n);\n\n    std::cout << \"tile_m, tile_n, tile_k: \" << tile_m << \", \" << tile_n << \", \" << tile_k << std::endl;\n    std::cout << \"Alignment: \" << cublasdx::alignment_of<BLAS>::a << \", \" << cublasdx::alignment_of<BLAS>::b << \", \"\n              << cublasdx::alignment_of<BLAS>::c << std::endl;\n    std::cout << \"block_size: \" << BLAS::max_threads_per_block << std::endl;\n\n    constexpr bool reverse_block_coord =\n        (cute::get<0>(gemm_arr) == cublasdx::row_major) and (cute::get<1>(gemm_arr) == cublasdx::row_major);\n\n    dim3 grid_dim = cute::conditional_return<reverse_block_coord>(dim3 {n / tile_n, m / tile_m, 1},\n                                                                  dim3 {(m / tile_m), (n / tile_n), 1});\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    auto shared_memory_size =\n        cublasdx::make_shared_storage_calculator()\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(AValueType), BLAS::suggest_layout_smem_a())\n            .add(cublasdx::alignment_of_v_b<BLAS>, sizeof(BValueType), BLAS::suggest_layout_smem_b())\n            .add(cublasdx::alignment_of_v_a<BLAS>, sizeof(AValueType), BLAS::suggest_layout_smem_a())\n            .add(cublasdx::alignment_of_v_b<BLAS>, sizeof(BValueType), BLAS::suggest_layout_smem_b())\n            .get();\n\n    auto kernel = gemm_kernel<BLAS,\n                              AccumulationIterations,\n                              GEMMShape,\n                              GEMMArr,\n                              GEMMLD,\n                              Alpha,\n                              AValueType,\n                              BValueType,\n                              Beta,\n                              CValueType>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    auto run_cublasdx_gemm = [&](hipStream_t str) {\n        kernel<<<grid_dim, BLAS::block_dim, shared_memory_size, str>>>(\n            gemm_shape, gemm_arr, gemm_ld, alpha, a, b, beta, c);\n    };\n\n    // Perform computations with cuBLASDx BLAS operator.\n    run_cublasdx_gemm(stream);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(results.data(), c, results.size() * sizeof(CValueType), hipMemcpyDeviceToHost));\n\n    return results;\n}\n\n// This is an example of using cuBLASDx for computing partial sums of a GEMM and summing them in a\n// higher precision storage for enhanced accuracy of the final result.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported.\n// Note that alpha and beta are expected to have the same precision and type (real or complex value)\n// as the matrix C elements.\n//\n// The partial accumulation is performed every N iterations of the K dimension of the GEMM, which can\n// be configured via the partial_sum_iterations template parameter.\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint gemm_device_partial_sums() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // A matrix which is small but deep, a lot of roundings will be needed\n    const auto m            = 768;  // Global M GEMM Size\n    const auto n            = 768;  // Global N GEMM Size\n    const auto k            = 8192; // Global K GEMM Size\n    auto       global_shape = cute::make_tuple(m, n, k);\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Higher precision to perform partial sums in\n    // NOTE: either this type must be implicitly convertible to\n    // compute type, or converter should be provided in appropriate places.\n    // please refer to simple_gemm_fp32_decoupled.cu example for more details.\n    using c_accumulation_precision = double;\n    // What should be the interval for accumulating in higher precision\n    constexpr int partial_sum_iterations = 64;\n\n    if (k % partial_sum_iterations != 0) {\n        std::cerr << \"K must be divisible by partial_sum_iterations\" << std::endl;\n        return 1;\n    }\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    using c_accumulation_value_type = cute::conditional_t<type == cublasdx::type::real,\n                                                          c_accumulation_precision,\n                                                          cublasdx::complex<c_accumulation_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned int tile_m = 128;\n    constexpr unsigned int tile_n = 128;\n    constexpr unsigned int tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr unsigned int tile_threads = 128;\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Input used to be converted to the final compute precision,\n    // this can be used to simulate either in-flight quantization\n    // or flexible upcasting of data to save on bandwidth\n    using a_io_value_type = a_compute_value_type;\n    using b_io_value_type = b_compute_value_type;\n\n    // This is the accumulation precision\n    using c_io_value_type = c_accumulation_value_type;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 64;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    // Use tuples to avoid passing 20 arguments to a function\n    constexpr auto global_arrangement = cute::make_tuple(\n        // These must be passed as integral constants to properly dispatch static striding\n        std::integral_constant<cublasdx::arrangement, global_arrangement_a> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_b> {},\n        std::integral_constant<cublasdx::arrangement, global_arrangement_c> {});\n\n    const auto global_ld = cute::make_tuple(global_lda, global_ldb, global_ldc);\n\n    // Test implementation\n    a_io_value_type* a_cublasdx = nullptr;\n    b_io_value_type* b_cublasdx = nullptr;\n    c_io_value_type* c_cublasdx = nullptr;\n\n    a_compute_value_type* a_cublas = nullptr;\n    b_compute_value_type* b_cublas = nullptr;\n    c_compute_value_type* c_cublas = nullptr;\n\n    // Use nullptr tensors to make it easier to calculate memory requirements\n    auto global_a = cublasdx::make_gmem_tensor<cute::get<0>(global_arrangement)>(a_cublas, m, k, global_lda);\n    auto global_b = cublasdx::make_gmem_tensor<cute::get<1>(global_arrangement)>(b_cublas, k, n, global_ldb);\n    auto global_c = cublasdx::make_gmem_tensor<cute::get<2>(global_arrangement)>(c_cublas, m, n, global_ldc);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&a_cublasdx, cublasdx::cosize(global_a.layout()) * sizeof(a_io_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&a_cublas, cublasdx::cosize(global_a.layout()) * sizeof(a_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&b_cublasdx, cublasdx::cosize(global_b.layout()) * sizeof(b_io_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&b_cublas, cublasdx::cosize(global_b.layout()) * sizeof(b_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&c_cublasdx, cublasdx::cosize(global_c.layout()) * sizeof(c_io_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&c_cublas, cublasdx::cosize(global_c.layout()) * sizeof(c_compute_value_type)));\n\n    // Fill the A, B, C matrices with random values.\n    {\n        auto host_a_io = example::get_random_data<a_io_value_type>(m * k);\n        auto host_b_io = example::get_random_data<b_io_value_type>(k * n);\n        auto host_c_io = example::get_random_data<c_io_value_type>(m * n);\n\n        static_assert(std::is_convertible_v<a_io_value_type, a_compute_value_type>);\n        auto host_a_compute = std::vector<a_compute_value_type>(host_a_io.begin(), host_a_io.end());\n\n        static_assert(std::is_convertible_v<b_io_value_type, b_compute_value_type>);\n        auto host_b_compute = std::vector<b_compute_value_type>(host_b_io.begin(), host_b_io.end());\n\n        static_assert(std::is_convertible_v<c_io_value_type, c_compute_value_type>);\n        auto host_c_compute = std::vector<c_compute_value_type>(host_c_io.begin(), host_c_io.end());\n\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(a_cublasdx, host_a_io.data(), m * k * sizeof(a_io_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(b_cublasdx, host_b_io.data(), k * n * sizeof(b_io_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(c_cublasdx, host_c_io.data(), m * n * sizeof(c_io_value_type), hipMemcpyHostToDevice));\n\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(a_cublas, host_a_compute.data(), m * k * sizeof(a_compute_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(b_cublas, host_b_compute.data(), k * n * sizeof(b_compute_value_type), hipMemcpyHostToDevice));\n        CUDA_CHECK_AND_EXIT(\n            hipMemcpy(c_cublas, host_c_compute.data(), m * n * sizeof(c_compute_value_type), hipMemcpyHostToDevice));\n        // destroy host vectors\n    }\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() +\n                          cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() +\n                          cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() + cublasdx::StaticBlockDim() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Execute cuBLASDx and cuBLASLt\n    // =============================\n\n    auto host_dx_results = measure_cublasdx<BLAS, partial_sum_iterations>(\n        global_shape, global_arrangement, global_ld, alpha, a_cublasdx, b_cublasdx, beta, c_cublasdx, stream);\n\n    // Measure cuBLAS performance.\n    auto host_blas_results = example::cublaslt_runner<a_compute_value_type, b_compute_value_type, c_compute_value_type>(\n                                 global_shape, global_arrangement, global_ld)\n                                 .execute_with_results(alpha, a_cublas, b_cublas, beta, c_cublas, stream);\n\n    // Write performance data.\n    using cublasdx::size_of;\n    std::cout << \"m, n, k: \" << m << \", \" << n << \", \" << k << std::endl;\n    std::cout << \"Compute Type A: \" << example::type_string<a_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type B: \" << example::type_string<b_compute_value_type>() << std::endl;\n    std::cout << \"Compute Type C: \" << example::type_string<c_compute_value_type>() << std::endl;\n    std::cout << \"Accumulation Type: \" << example::type_string<c_accumulation_value_type>() << std::endl;\n    std::cout << \"Accumulation Every N Iterations: \" << partial_sum_iterations << std::endl;\n\n    std::cout << \"Dx Input Precision A: \" << example::precision_string<a_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision B: \" << example::precision_string<b_io_value_type>() << std::endl;\n    std::cout << \"Dx Input Precision C: \" << example::precision_string<c_io_value_type>() << std::endl;\n\n    std::cout << \"cuBLAS Input Precision A: \" << example::precision_string<a_compute_value_type>() << std::endl;\n    std::cout << \"cuBLAS Input Precision B: \" << example::precision_string<b_compute_value_type>() << std::endl;\n    std::cout << \"cuBLAS Input Precision C: \" << example::precision_string<c_compute_value_type>() << std::endl;\n\n    auto error = example::calculate_error(host_dx_results, host_blas_results);\n    std::cout << \"Error = \" << error << \"\\n\";\n\n    if (example::is_error_acceptable<a_compute_value_type, b_compute_value_type, c_compute_value_type>(error)) {\n        std::cout << \"Success!\\n\";\n    } else {\n        std::cout << \"Failure!\\n\";\n    }\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(a_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(b_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(c_cublasdx));\n    CUDA_CHECK_AND_EXIT(hipFree(a_cublas));\n    CUDA_CHECK_AND_EXIT(hipFree(b_cublas));\n    CUDA_CHECK_AND_EXIT(hipFree(c_cublas));\n\n    return 0;\n}\n\nstruct gemm_device_partial_sums_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return gemm_device_partial_sums<Arch, Modifier>();\n    }\n};\n\nint main() {\n    return example::sm_runner(gemm_device_partial_sums_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/12_gemm_device_partial_sums/gemm_device_partial_sums.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_shared(const typename GEMM::c_value_type  alpha,\n                                   const typename GEMM::a_value_type* a,\n                                   const typename GEMM::b_value_type* b,\n                                   const typename GEMM::c_value_type  beta,\n                                   typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // Store data from shared memory tensor to global memory tensor\n    cublasdx::copy<GEMM, alignment::c>(c_shared_tensor, c_global_tensor);\n}\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_registers_accumulation(const typename GEMM::a_value_type* a,\n                                                   const typename GEMM::b_value_type* b,\n                                                   typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<GEMM>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Get default accumulator\n    auto accumulator = GEMM::get_accumulator();\n\n    // Partition Global C for GEMM and load appropriate elements into accumulator\n    auto c_frag = accumulator.make_partition_and_copy(c_global_tensor);\n\n    // Execute GEMM with accumulation\n    GEMM().execute(a_shared_tensor, b_shared_tensor, accumulator);\n\n    auto results = accumulator.get_results();\n    cublasdx::axpby(1.0, results, 1.0, c_frag);\n\n    // Partition Global C for GEMM and store appropriate elements to global memory\n    accumulator.partition_and_copy(c_frag, c_global_tensor);\n}\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_registers(const typename GEMM::a_value_type* a,\n                                      const typename GEMM::b_value_type* b,\n                                      typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<GEMM>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM and get accumulator in return\n    auto accumulator = GEMM().execute(a_shared_tensor, b_shared_tensor);\n\n    // Partition Global C for GEMM and store appropriate elements to global memory\n    accumulator.partition_and_store(c_global_tensor);\n}\n\ntemplate<unsigned int Arch>\nint introduction_example() {\n    using GEMM =\n        decltype(cublasdx::Size<32, 32, 32>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::SM<Arch>() + cublasdx::Block() +\n                 cublasdx::BlockDim<256>());\n\n    using value_type = typename example::uniform_value_type_t<GEMM>;\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Allocate managed memory for A, B, C matrices in one go\n    value_type* abc;\n    auto        size       = global_a_size + global_b_size + global_c_size;\n    auto        size_bytes = size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&abc, size_bytes));\n    // Generate data\n    for (size_t i = 0; i < size; i++) {\n        abc[i] = double(i / size);\n    }\n\n    value_type* a = abc;\n    value_type* b = abc + global_a_size;\n    value_type* c = abc + global_a_size + global_b_size;\n\n\n    // Shared Memory API: C = alpha * A * B + beta * C\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_shared<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size<GEMM>()>>>(1.0, a, b, 1.0, c);\n    gemm_kernel_shared<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size<GEMM>()>>>(1.0, a, b, 1.0, c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    // Register Fragment Accumulation API: C = A * B + C\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_registers_accumulation<GEMM>\n        <<<1, GEMM::block_dim, cublasdx::get_shared_storage_size_ab<GEMM>()>>>(a, b, c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    // Register Fragment API: C = A * B\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_registers<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size_ab<GEMM>()>>>(a, b, c);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaFree(abc));\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct introduction_example_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return introduction_example<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(introduction_example_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_shared(const typename GEMM::c_value_type  alpha,\n                                   const typename GEMM::a_value_type* a,\n                                   const typename GEMM::b_value_type* b,\n                                   const typename GEMM::c_value_type  beta,\n                                   typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<GEMM>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, GEMM::get_layout_smem_c());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<GEMM, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM\n    GEMM().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n    __syncthreads();\n\n    // Store data from shared memory tensor to global memory tensor\n    cublasdx::copy<GEMM, alignment::c>(c_shared_tensor, c_global_tensor);\n}\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_registers_accumulation(const typename GEMM::a_value_type* a,\n                                                   const typename GEMM::b_value_type* b,\n                                                   typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<GEMM>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Get default accumulator\n    auto accumulator = GEMM::get_accumulator();\n\n    // Partition Global C for GEMM and load appropriate elements into accumulator\n    auto c_frag = accumulator.make_partition_and_copy(c_global_tensor);\n\n    // Execute GEMM with accumulation\n    GEMM().execute(a_shared_tensor, b_shared_tensor, accumulator);\n\n    auto results = accumulator.get_results();\n    cublasdx::axpby(1.0, results, 1.0, c_frag);\n\n    // Partition Global C for GEMM and store appropriate elements to global memory\n    accumulator.partition_and_copy(c_frag, c_global_tensor);\n}\n\ntemplate<class GEMM>\n__global__ void gemm_kernel_registers(const typename GEMM::a_value_type* a,\n                                      const typename GEMM::b_value_type* b,\n                                      typename GEMM::c_value_type*       c) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Make global memory tensor\n    auto a_global_tensor = cublasdx::make_tensor(a, GEMM::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, GEMM::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, GEMM::get_layout_gmem_c());\n\n    // Make shared memory tensor\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<GEMM>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, GEMM::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, GEMM::get_layout_smem_b());\n\n    // Load data from global memory tensor to shared memory tensor\n    using alignment = cublasdx::alignment_of<GEMM>;\n    cublasdx::copy<GEMM, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<GEMM, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM and get accumulator in return\n    auto accumulator = GEMM().execute(a_shared_tensor, b_shared_tensor);\n\n    // Partition Global C for GEMM and store appropriate elements to global memory\n    accumulator.partition_and_store(c_global_tensor);\n}\n\ntemplate<unsigned int Arch>\nint introduction_example() {\n    using GEMM =\n        decltype(cublasdx::Size<32, 32, 32>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major>() +\n                 cublasdx::Function<cublasdx::function::MM>() + cublasdx::SM<Arch>() + cublasdx::Block() +\n                 cublasdx::BlockDim<256>());\n\n    using value_type = typename example::uniform_value_type_t<GEMM>;\n\n    constexpr auto global_a_size = example::global_memory_size_of<GEMM>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<GEMM>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<GEMM>::c_size;\n\n    // Allocate managed memory for A, B, C matrices in one go\n    value_type* abc;\n    auto        size       = global_a_size + global_b_size + global_c_size;\n    auto        size_bytes = size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&abc, size_bytes));\n    // Generate data\n    for (size_t i = 0; i < size; i++) {\n        abc[i] = double(i / size);\n    }\n\n    value_type* a = abc;\n    value_type* b = abc + global_a_size;\n    value_type* c = abc + global_a_size + global_b_size;\n\n\n    // Shared Memory API: C = alpha * A * B + beta * C\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_shared<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size<GEMM>()>>>(1.0, a, b, 1.0, c);\n    gemm_kernel_shared<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size<GEMM>()>>>(1.0, a, b, 1.0, c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    // Register Fragment Accumulation API: C = A * B + C\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_registers_accumulation<GEMM>\n        <<<1, GEMM::block_dim, cublasdx::get_shared_storage_size_ab<GEMM>()>>>(a, b, c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    // Register Fragment API: C = A * B\n    // Invokes kernel with GEMM::block_dim threads in CUDA block\n    gemm_kernel_registers<GEMM><<<1, GEMM::block_dim, cublasdx::get_shared_storage_size_ab<GEMM>()>>>(a, b, c);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipFree(abc));\n    std::cout << \"Success\" << std::endl;\n    return 0;\n}\n\nstruct introduction_example_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return introduction_example<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(introduction_example_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/01_gemm_introduction/introduction_example.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <iostream>\n#include <system_error>\n#include <iomanip>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class BLAS, class Alpha, class Beta, class CTensor, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void gemm_kernel(Alpha const alpha,\n                     Beta const  beta,\n                     CTensor     global_c,\n                     // IMPORTANT: Notice __grid_constant__ is used for device_pipeline argument\n                     __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        // Use device_pipeline traits to properly align dynamic shared memory\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        // Instantiate tile_pipeline for trivial tile dictated by block coordinates\n        auto tile_pipeline = device_pipeline.get_tile(smem, blockIdx.x, blockIdx.y);\n\n        // Define epilogue to be opaquely executed by BLAS::block_dim threads\n        auto epilogue_functor = [&](auto& accumulator) {\n            // Partition logical C tile of input/output data\n            auto tile_gmem_c = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n            // Create fragment and copy appropriate per-thread partition of data into it\n            auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n            // D = alpha * A * B + beta * C\n            cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n            // Store results back to global memory\n            accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n        };\n\n        // Execute the pipeline, accumulating over entire K dimension the chosen per-CTA tile\n        tile_pipeline.execute(epilogue_functor);\n    }\n#endif\n}\n\n// This is an example of testing performance of cuBLASDx as tile provider, executing a general matrix multiply (GEMM)\n// on the entire GPU and distributing work across all SMs, each of them running a small GEMM tile with cuBLASDx.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported, decoupled precisions are supported.\n//\n\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint introduction_pipeline() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // This size can be set dynamically from command line\n    const auto m = 8192; // Global M GEMM Size\n    const auto n = 8192; // Global N GEMM Size\n    const auto k = 8192; // Global K GEMM Size\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned tile_m = 128;\n    constexpr unsigned tile_n = 128;\n    constexpr unsigned tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr int tile_threads = 128;\n\n    // If pipeline_depth is left at 0, an automatically generated depth\n    // will be used, equal to the maximal possible value based on shared memory\n    // size of chosen device architecture\n    constexpr unsigned pipeline_depth = 2;\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 16;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    auto k_stages = k / tile_k;\n    if (k_stages < pipeline_depth) {\n        std::cerr << \"PipelineDepth must be less or equal to GEMM k stages, please adjust pipeline_depth\" << std::endl;\n        return 1;\n    }\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    auto host_a_io = example::get_random_data<a_compute_value_type>(m * k, 1);\n    auto host_b_io = example::get_random_data<b_compute_value_type>(k * n, 2);\n    auto host_c_io = example::get_random_data<c_compute_value_type>(m * n, 3);\n\n    a_compute_value_type* a_data = nullptr;\n    b_compute_value_type* b_data = nullptr;\n    c_compute_value_type* c_data = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&a_data, m * k * sizeof(a_compute_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&b_data, k * n * sizeof(b_compute_value_type)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&c_data, m * n * sizeof(c_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(a_data, host_a_io.data(), m * k * sizeof(a_compute_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(b_data, host_b_io.data(), k * n * sizeof(b_compute_value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(c_data, host_c_io.data(), m * n * sizeof(c_compute_value_type), cudaMemcpyHostToDevice));\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    auto global_a = cublasdx::make_gmem_tensor<global_arrangement_a>(a_data, m, k, global_lda);\n    auto global_b = cublasdx::make_gmem_tensor<global_arrangement_b>(b_data, k, n, global_ldb);\n    auto global_c = cublasdx::make_gmem_tensor<global_arrangement_c>(c_data, m, n, global_ldc);\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Setup cuBLASDx pipeline\n    // =============================\n\n    // Note that if there is enough smem and register pressure is low then it might be possible to execute\n    // multiple persistent CTAs per SM\n    dim3 grid_dim = dim3 {m / tile_m, n / tile_n, 1};\n\n    // Attempt to create cuBLASDx pipeline object\n    auto opt_device_pipeline = cublasdx::suggest_device_pipeline<pipeline_depth, BLAS>(global_a, global_b);\n\n    // Check if object is valid\n    if (not opt_device_pipeline) {\n        std::cout << \"Incorrect pipeline configuration, please ensure global tensors are divisible by tile\"\n                  << std::endl;\n        exit(1);\n    }\n\n    // Get device_pipeline, used to instantiate tile_pipelines later\n    auto device_pipeline = opt_device_pipeline.value();\n\n    // Use device_pipeline traits to get necessary shared memory allocation size\n    auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                  .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                  .get();\n\n    using alpha_t = c_compute_value_type;\n    using beta_t  = c_compute_value_type;\n    auto kernel   = gemm_kernel<BLAS, alpha_t, beta_t, decltype(global_c), decltype(device_pipeline)>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Pass device_pipeline to kernel as regular by value argument\n    kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size, stream>>>(\n        alpha, beta, global_c, device_pipeline);\n\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy back data\n    std::vector<c_compute_value_type> results(m * n);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(results.data(), c_data, results.size() * sizeof(c_compute_value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(a_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(b_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(c_data));\n\n    printf(\"Success! Please refer to example_11 (device_gemm_performance) for an advanced measurement\\n\");\n\n    return 0;\n}\n\nstruct introduction_pipeline_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return introduction_pipeline<Arch, Modifier>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(introduction_pipeline_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <array>\n#include <iostream>\n#include <system_error>\n#include <iomanip>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class BLAS, class Alpha, class Beta, class CTensor, class DevicePipeline>\n__launch_bounds__(DevicePipeline::max_threads_per_block, 1) __global__\n    void gemm_kernel(Alpha const alpha,\n                     Beta const  beta,\n                     CTensor     global_c,\n                     // IMPORTANT: Notice __grid_constant__ is used for device_pipeline argument\n                     __grid_constant__ DevicePipeline const device_pipeline) {\n#ifdef __CUDA_ARCH__\n    if constexpr (cublasdx::sm_of_v<BLAS> == __CUDA_ARCH__) {\n\n        // Use device_pipeline traits to properly align dynamic shared memory\n        extern __shared__ __align__(device_pipeline.buffer_alignment()) cublasdx::byte smem[];\n\n        // Instantiate tile_pipeline for trivial tile dictated by block coordinates\n        auto tile_pipeline = device_pipeline.get_tile(smem, blockIdx.x, blockIdx.y);\n\n        // Define epilogue to be opaquely executed by BLAS::block_dim threads\n        auto epilogue_functor = [&](auto& accumulator) {\n            // Partition logical C tile of input/output data\n            auto tile_gmem_c = cublasdx::get_tile(global_c, BLAS::c_shape, blockIdx.x, blockIdx.y);\n            // Create fragment and copy appropriate per-thread partition of data into it\n            auto d_fragment = accumulator.make_partition_and_copy(tile_gmem_c);\n            // D = alpha * A * B + beta * C\n            cublasdx::axpby(alpha, accumulator.get_results(), beta, d_fragment);\n            // Store results back to global memory\n            accumulator.partition_and_copy(d_fragment, tile_gmem_c);\n        };\n\n        // Execute the pipeline, accumulating over entire K dimension the chosen per-CTA tile\n        tile_pipeline.execute(epilogue_functor);\n    }\n#endif\n}\n\n// This is an example of testing performance of cuBLASDx as tile provider, executing a general matrix multiply (GEMM)\n// on the entire GPU and distributing work across all SMs, each of them running a small GEMM tile with cuBLASDx.\n//\n//              C = alpha * A * B + beta * C\n//\n// A, B, and C are matrices. Mixed precisions are supported, decoupled precisions are supported.\n//\n\ntemplate<unsigned int Arch, cublasdx::sm_modifier Modifier>\nint introduction_pipeline() {\n\n    // ===================================\n    // Configurable Global GEMM properties\n    // ===================================\n\n    // Global GEMM Size --> MNK, where:\n    // - A matrix is M x K\n    // - B matrix is K x N\n    // - C matrix is M x N\n\n    // This size can be set dynamically from command line\n    const auto m = 8192; // Global M GEMM Size\n    const auto n = 8192; // Global N GEMM Size\n    const auto k = 8192; // Global K GEMM Size\n\n    // Global GEMM Arrangement:\n    // - cubladsx::row_major, row major data arrangement\n    // - cubladsx::col_major, col major data arrangement\n    // Note: these values need to be constexpr\n    constexpr auto global_arrangement_a = cublasdx::row_major;\n    constexpr auto global_arrangement_b = cublasdx::col_major;\n    constexpr auto global_arrangement_c = cublasdx::row_major;\n\n    // Leading Dimensions to be used for global data\n    // Note: for matrix of size X x Y, the LD must be:\n    // - greater or equal than X if matrix is col-major\n    // - greater or equal than Y if matrix is row-major\n    // Note: these values can be dynamic\n    const auto global_lda = (global_arrangement_a == cublasdx::col_major) ? m : k;\n    const auto global_ldb = (global_arrangement_b == cublasdx::col_major) ? k : n;\n    const auto global_ldc = (global_arrangement_c == cublasdx::col_major) ? m : n;\n\n    // Compute precision (use Tensor Cores of this precision)\n    // and cuBLAS input precision\n    using a_compute_precision = __half;\n    using b_compute_precision = __half;\n    using c_compute_precision = float;\n\n    // Number type, either real or complex\n    constexpr auto type = cublasdx::type::real;\n\n    // Create data type, based on:\n    // - precision\n    // - type (real / complex)\n    // this will be either precision or cublasdx::complex<precision>\n    using a_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, a_compute_precision, cublasdx::complex<a_compute_precision>>;\n    using b_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, b_compute_precision, cublasdx::complex<b_compute_precision>>;\n    using c_compute_value_type =\n        cute::conditional_t<type == cublasdx::type::real, c_compute_precision, cublasdx::complex<c_compute_precision>>;\n\n    // Scalar multipliers\n    // C = alpha * A * B + beta * C\n    c_compute_value_type alpha = example::make_value<c_compute_value_type>(1.1);\n    c_compute_value_type beta  = example::make_value<c_compute_value_type>(1.2);\n\n    // ======================================\n    // Configurable cuBLASDx tile properties\n    // ======================================\n\n    // tile size, this describes smaller GEMM,\n    // which will be computed by each threadblock\n    // using cuBLASDx\n    constexpr unsigned tile_m = 128;\n    constexpr unsigned tile_n = 128;\n    constexpr unsigned tile_k = 32;\n\n    // Number of threads to compute the tile described above\n    constexpr int tile_threads = 128;\n\n    // If pipeline_depth is left at 0, an automatically generated depth\n    // will be used, equal to the maximal possible value based on shared memory\n    // size of chosen device architecture\n    constexpr unsigned pipeline_depth = 2;\n\n    // Arrangement of data in a per-threadblock tile of data\n    constexpr auto tile_arr_a = global_arrangement_a;\n    constexpr auto tile_arr_b = global_arrangement_b;\n    constexpr auto tile_arr_c = global_arrangement_c;\n\n    // Maximal alignment to be used for shared memory data.\n    // Effectively this limits maximal vectorization level\n    // for loads and stores.\n    constexpr unsigned int maximal_alignment  = 16;\n    constexpr unsigned int cublasdx_alignment = maximal_alignment;\n\n    // ================================\n    // Verify configuration correctness\n    // ================================\n\n    auto k_stages = k / tile_k;\n    if (k_stages < pipeline_depth) {\n        std::cerr << \"PipelineDepth must be less or equal to GEMM k stages, please adjust pipeline_depth\" << std::endl;\n        return 1;\n    }\n\n    const bool divisible = (m % tile_m == 0 and n % tile_n == 0 and k % tile_k == 0);\n    if (not divisible) {\n        std::cerr << \"M, N, K dimensions must be divisible by tile_m, tile_n, tile_k\" << std::endl;\n        return 1;\n    }\n\n    // ================================\n    // Prepare inputs\n    // ================================\n\n    auto host_a_io = example::get_random_data<a_compute_value_type>(m * k, 1);\n    auto host_b_io = example::get_random_data<b_compute_value_type>(k * n, 2);\n    auto host_c_io = example::get_random_data<c_compute_value_type>(m * n, 3);\n\n    a_compute_value_type* a_data = nullptr;\n    b_compute_value_type* b_data = nullptr;\n    c_compute_value_type* c_data = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&a_data, m * k * sizeof(a_compute_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&b_data, k * n * sizeof(b_compute_value_type)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&c_data, m * n * sizeof(c_compute_value_type)));\n\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(a_data, host_a_io.data(), m * k * sizeof(a_compute_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(b_data, host_b_io.data(), k * n * sizeof(b_compute_value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(c_data, host_c_io.data(), m * n * sizeof(c_compute_value_type), hipMemcpyHostToDevice));\n\n    // Create tensors for global A / B / C corresponding to set MNK, arrangement and LDs\n    auto global_a = cublasdx::make_gmem_tensor<global_arrangement_a>(a_data, m, k, global_lda);\n    auto global_b = cublasdx::make_gmem_tensor<global_arrangement_b>(b_data, k, n, global_ldb);\n    auto global_c = cublasdx::make_gmem_tensor<global_arrangement_c>(c_data, m, n, global_ldc);\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream))\n\n    // cuBLASDx type creation\n    using BLAS = decltype(cublasdx::Size<tile_m, tile_n, tile_k>() +\n                          cublasdx::Precision<a_compute_precision, b_compute_precision, c_compute_precision>() +\n                          cublasdx::Type<type>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Arrangement<tile_arr_a, tile_arr_b, tile_arr_c>() + cublasdx::Block() +\n                          cublasdx::BlockDim<tile_threads>() +\n                          cublasdx::Alignment<cublasdx_alignment, cublasdx_alignment, cublasdx_alignment>() +\n                          cublasdx::WithPipeline() + cublasdx::SM<Arch, Modifier>());\n\n    // =============================\n    // Setup cuBLASDx pipeline\n    // =============================\n\n    // Note that if there is enough smem and register pressure is low then it might be possible to execute\n    // multiple persistent CTAs per SM\n    dim3 grid_dim = dim3 {m / tile_m, n / tile_n, 1};\n\n    // Attempt to create cuBLASDx pipeline object\n    auto opt_device_pipeline = cublasdx::suggest_device_pipeline<pipeline_depth, BLAS>(global_a, global_b);\n\n    // Check if object is valid\n    if (not opt_device_pipeline) {\n        std::cout << \"Incorrect pipeline configuration, please ensure global tensors are divisible by tile\"\n                  << std::endl;\n        exit(1);\n    }\n\n    // Get device_pipeline, used to instantiate tile_pipelines later\n    auto device_pipeline = opt_device_pipeline.value();\n\n    // Use device_pipeline traits to get necessary shared memory allocation size\n    auto shared_memory_size = cublasdx::make_shared_storage_calculator()\n                                  .add(device_pipeline.buffer_alignment(), device_pipeline.buffer_size())\n                                  .get();\n\n    using alpha_t = c_compute_value_type;\n    using beta_t  = c_compute_value_type;\n    auto kernel   = gemm_kernel<BLAS, alpha_t, beta_t, decltype(global_c), decltype(device_pipeline)>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    // Pass device_pipeline to kernel as regular by value argument\n    kernel<<<grid_dim, device_pipeline.get_block_dim(), shared_memory_size, stream>>>(\n        alpha, beta, global_c, device_pipeline);\n\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy back data\n    std::vector<c_compute_value_type> results(m * n);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(results.data(), c_data, results.size() * sizeof(c_compute_value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free resources.\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(a_data));\n    CUDA_CHECK_AND_EXIT(hipFree(b_data));\n    CUDA_CHECK_AND_EXIT(hipFree(c_data));\n\n    printf(\"Success! Please refer to example_11 (device_gemm_performance) for an advanced measurement\\n\");\n\n    return 0;\n}\n\nstruct introduction_pipeline_functor {\n\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return introduction_pipeline<Arch, Modifier>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(introduction_pipeline_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/01_gemm_introduction/introduction_pipeline.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class AValueType, class BValueType, class CValueType>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const AValueType* a,\n                     const BValueType* b,\n                     const CValueType* c,\n                     const CValueType  alpha,\n                     const CValueType  beta,\n                     CValueType*       output) {\n\n    using a_value_type   = AValueType;\n    using b_value_type   = BValueType;\n    using c_value_type   = CValueType;\n    using c_compute_type = typename BLAS::c_value_type;\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Create global tensors with input types\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    // Create shared memory tensors with input types\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS, AValueType, BValueType>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Copy input precision data from global to shared memory\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM on shared memory tensors\n    // 1. Load input precision data from shared to registers\n    // 2. Convert in registers to compute precision\n    // 3. Perform MMA and accumulate in registers in compute precision\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor);\n    auto result_fragment = accumulator.get_results();\n    // Create accumulator fragment with compute precision\n    auto d_frag_compute = cublasdx::make_fragment_like(result_fragment);\n    // Create output fragment with input precision\n    // Copy input precision data from global to output fragment\n    auto d_frag_io = accumulator.make_partition_and_copy(c_global_tensor);\n    // Convert in registers to compute precision\n    cublasdx::transform_fragment(d_frag_io, d_frag_compute, example::converter<c_compute_type> {});\n    // Perform AXPBY and accumulate in registers in compute precision\n    auto compute_alpha = static_cast<c_compute_type>(alpha);\n    auto compute_beta  = static_cast<c_compute_type>(beta);\n    cublasdx::axpby(compute_alpha, result_fragment, compute_beta, d_frag_compute);\n    // Convert in registers to output precision\n    cublasdx::transform_fragment(d_frag_compute, d_frag_io, example::converter<c_value_type> {});\n    // Copy output precision data from registers to global memory\n    accumulator.partition_and_copy(d_frag_io, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) with fp16 inputs\n// performed in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real half precision floating-point values.\n// * alpha and beta are values convertible to single precison floating-point values.\n// all operations happen in fp32 precision, with conversion happening in registers,\n// which allows to reduce memory transfers by half.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // Type of input and output values in global and shared memory\n    using data_type = __half;\n    // Type in which all mathematical operations and accumulation will happen\n    using compute_type = float;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    //\n    // NOTE: The alignment is adjusted to data_type not computation types from cublasdx::Precision\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<compute_type>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Alignment<sizeof(data_type), sizeof(data_type), sizeof(data_type)>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c, and output\n    data_type* inputs;\n    data_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(data_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output, global_c_size * sizeof(data_type)));\n\n    data_type* a     = inputs;\n    data_type* b     = a + (global_a_size);\n    data_type* c     = b + (global_b_size);\n    data_type  alpha = data_type(1.0);\n    data_type  beta  = data_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<data_type>(global_a_size);\n    auto host_b = example::get_random_data<data_type>(global_b_size);\n    auto host_c = example::get_random_data<data_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(data_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(data_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(data_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    auto shared_memory_size = cublasdx::get_shared_storage_size_ab<BLAS, data_type, data_type>();\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(gemm_kernel<BLAS, data_type, data_type, data_type>,\n                                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                             shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<data_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(data_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output, true, true)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class AValueType, class BValueType, class CValueType>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const AValueType* a,\n                     const BValueType* b,\n                     const CValueType* c,\n                     const CValueType  alpha,\n                     const CValueType  beta,\n                     CValueType*       output) {\n\n    using a_value_type   = AValueType;\n    using b_value_type   = BValueType;\n    using c_value_type   = CValueType;\n    using c_compute_type = typename BLAS::c_value_type;\n\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    // Create global tensors with input types\n    auto a_global_tensor   = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor   = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor   = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n\n    // Create shared memory tensors with input types\n    auto [smem_a, smem_b] = cublasdx::slice_shared_memory_ab<BLAS, AValueType, BValueType>(smem);\n    auto a_shared_tensor  = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor  = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Copy input precision data from global to shared memory\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy_wait();\n\n    // Execute GEMM on shared memory tensors\n    // 1. Load input precision data from shared to registers\n    // 2. Convert in registers to compute precision\n    // 3. Perform MMA and accumulate in registers in compute precision\n    auto accumulator     = BLAS().execute(a_shared_tensor, b_shared_tensor);\n    auto result_fragment = accumulator.get_results();\n    // Create accumulator fragment with compute precision\n    auto d_frag_compute = cublasdx::make_fragment_like(result_fragment);\n    // Create output fragment with input precision\n    // Copy input precision data from global to output fragment\n    auto d_frag_io = accumulator.make_partition_and_copy(c_global_tensor);\n    // Convert in registers to compute precision\n    cublasdx::transform_fragment(d_frag_io, d_frag_compute, example::converter<c_compute_type> {});\n    // Perform AXPBY and accumulate in registers in compute precision\n    auto compute_alpha = static_cast<c_compute_type>(alpha);\n    auto compute_beta  = static_cast<c_compute_type>(beta);\n    cublasdx::axpby(compute_alpha, result_fragment, compute_beta, d_frag_compute);\n    // Convert in registers to output precision\n    cublasdx::transform_fragment(d_frag_compute, d_frag_io, example::converter<c_value_type> {});\n    // Copy output precision data from registers to global memory\n    accumulator.partition_and_copy(d_frag_io, out_global_tensor);\n}\n\n// This is an example of fp32 general matrix-matrix multiplication (GEMM) with fp16 inputs\n// performed in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real half precision floating-point values.\n// * alpha and beta are values convertible to single precison floating-point values.\n// all operations happen in fp32 precision, with conversion happening in registers,\n// which allows to reduce memory transfers by half.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\ntemplate<unsigned int Arch>\nint simple_gemm() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 8;\n    constexpr unsigned int n = 16;\n    constexpr unsigned int k = 32;\n\n    // Selected CUDA block size (1D)\n    constexpr unsigned int block_size = 256;\n\n    // Type of input and output values in global and shared memory\n    using data_type = __half;\n    // Type in which all mathematical operations and accumulation will happen\n    using compute_type = float;\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. Block operator informs that GEMM should be performed on CUDA block level.\n    // 4. BlockDim operator sets CUDA block dimensions that the kernel will be executed with.\n    // 5. Targeted CUDA compute capability is selected with SM operator.\n    //\n    // NOTE: The alignment is adjusted to data_type not computation types from cublasdx::Precision\n    using BLAS = decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<compute_type>() +\n                          cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Alignment<sizeof(data_type), sizeof(data_type), sizeof(data_type)>() +\n                          cublasdx::Block() + cublasdx::BlockDim<block_size>() + cublasdx::SM<Arch>());\n\n    // Allocate managed memory for a, b, c, and output\n    data_type* inputs;\n    data_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(data_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output, global_c_size * sizeof(data_type)));\n\n    data_type* a     = inputs;\n    data_type* b     = a + (global_a_size);\n    data_type* c     = b + (global_b_size);\n    data_type  alpha = data_type(1.0);\n    data_type  beta  = data_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<data_type>(global_a_size);\n    auto host_b = example::get_random_data<data_type>(global_b_size);\n    auto host_c = example::get_random_data<data_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(data_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(data_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(data_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    auto shared_memory_size = cublasdx::get_shared_storage_size_ab<BLAS, data_type, data_type>();\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS, data_type, data_type, data_type>),\n                                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                                             shared_memory_size));\n\n    // Execute kernel\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, shared_memory_size>>>(a, b, c, alpha, beta, output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<data_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(data_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Check against reference\n    if (example::check_error<BLAS>(host_output, reference_host_output, true, true)) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/08_gemm_decoupled_io_and_compute/simple_gemm_fp32_decoupled.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType  alpha,\n                     const ValueType* a,\n                     const ValueType* b,\n                     const ValueType  beta,\n                     const ValueType* c,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\ntemplate<class BLASWithoutLD, class ValueType = typename example::uniform_value_type_t<BLASWithoutLD>>\n__launch_bounds__(BLASWithoutLD::max_threads_per_block) //\n    __global__                                          //\n    void gemm_kernel_dynamic_ld(const ValueType    alpha,\n                                const ValueType*   a,\n                                const unsigned int lda,\n                                const ValueType*   b,\n                                const unsigned int ldb,\n                                const ValueType    beta,\n                                const ValueType*   c,\n                                const unsigned int ldc,\n                                ValueType*         output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLASWithoutLD::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLASWithoutLD::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLASWithoutLD::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLASWithoutLD>(smem, lda, ldb, ldc);\n    auto ta                       = cublasdx::make_tensor(smem_a, BLASWithoutLD::get_layout_smem_a(lda));\n    auto tb                       = cublasdx::make_tensor(smem_b, BLASWithoutLD::get_layout_smem_b(ldb));\n    auto tc                       = cublasdx::make_tensor(smem_c, BLASWithoutLD::get_layout_smem_c(ldc));\n\n    using alignment = cublasdx::alignment_of<BLASWithoutLD>;\n    cublasdx::copy<BLASWithoutLD, alignment::a>(a_global_tensor, ta);\n    cublasdx::copy<BLASWithoutLD, alignment::b>(b_global_tensor, tb);\n    cublasdx::copy<BLASWithoutLD, alignment::c>(c_global_tensor, tc);\n    cublasdx::copy_wait();\n\n    BLASWithoutLD().execute(alpha, ta, tb, beta, tc);\n\n    __syncthreads();\n    auto tgout = cublasdx::make_tensor(output, BLASWithoutLD::get_layout_gmem_c(ldc));\n    cublasdx::copy<BLASWithoutLD, alignment::c>(tc, tgout);\n}\n\n// This is an example of fp64 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real double precision floating-point values.\n// * alpha and beta are real double precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// Additionally, this example sets leading dimensions for the A, B, C  matrices participating in the GEMM.\n// For BLAS type they are set statically via LeadingDimension operator. For comparision, there's  also\n// BLASWithoutLD type without that operator, and it is run with dynamic leading dimensions that are passed\n// as arguments to the execute() method.\ntemplate<unsigned int Arch>\nint simple_gemm_with_leading_dimensions() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 30;\n    constexpr unsigned int n = 31;\n    constexpr unsigned int k = 33;\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::row_major;\n    constexpr auto c_arrangement = cublasdx::row_major;\n\n    // Leading dimensions defined for matrices A, B, C\n\n    // If A is column-major, A is a matrix of real dimensions [lda, k] with lda >= m\n    // If A is row-major, A is a matrix of real dimensions [lda, m] with lda >= k\n    constexpr unsigned int lda = 32;\n    // If B is column-major, B is a matrix of real dimensions [ldb, n] with ldb >= k\n    // If B is row-major, B is a matrix of real dimensions [ldb, k] with ldb >= n\n    constexpr unsigned int ldb = 33;\n    // C is a matrix of real dimensions [ldc, n] with ldx >= m\n    constexpr unsigned int ldc = 31;\n\n    // Selected CUDA block size (2D)\n    constexpr dim3 block_dim(16, 16, 1);\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value.\n    //    - Optional\n    // 5. Block operator informs that GEMM should be performed on CUDA block level.\n    // 6. BlockDim operator sets CUDA block dimensions (in this cases 2D) that the kernel will be executed with.\n    // 7. Targeted CUDA compute capability is selected with SM operator.\n    using BLASWithoutLD =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<a_arrangement, b_arrangement, c_arrangement>() + cublasdx::Block() +\n                 cublasdx::BlockDim<block_dim.x, block_dim.y, block_dim.z>() + cublasdx::SM<Arch>());\n    // 6. Leading dimensions for matrices A, B, C\n    using BLAS       = decltype(BLASWithoutLD() + cublasdx::LeadingDimension<lda, ldb, ldc>());\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        gemm_kernel<BLAS>, cudaFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel with GEMM with defined leading dimensions (known at compile time)\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(alpha, a, b, beta, c, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Perform same GEMM but without defined leading dimensions, instead use dynamic leading dimensions.\n    // Dynamic leading dimensions are the same are for BLAS which will enable us to reuse the same device buffers.\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(gemm_kernel_dynamic_ld<BLASWithoutLD>,\n                                             cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLASWithoutLD>(lda, ldb, ldc)));\n\n    // Execute kernel\n    gemm_kernel_dynamic_ld<BLASWithoutLD>\n        <<<1, BLASWithoutLD::block_dim, cublasdx::get_shared_storage_size<BLASWithoutLD>(lda, ldb, ldc)>>>(\n            alpha, a, lda, b, ldb, beta, c, ldc, output);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output_dynamic_ld(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(host_output_dynamic_ld.data(), output, global_c_size * sizeof(value_type), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(cudaFree(inputs));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check both results against reference\n    auto correct_static_ld  = example::check_error<BLAS>(host_output, reference_host_output);\n    auto correct_dynamic_ld = example::check_error<BLAS>(host_output_dynamic_ld, reference_host_output);\n    if (correct_static_ld && correct_dynamic_ld) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_with_leading_dimensions_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm_with_leading_dimensions<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_with_leading_dimensions_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cublasdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../reference/reference.hpp\"\n\ntemplate<class BLAS, class ValueType = typename example::uniform_value_type_t<BLAS>>\n__launch_bounds__(BLAS::max_threads_per_block) //\n    __global__                                 //\n    void gemm_kernel(const ValueType  alpha,\n                     const ValueType* a,\n                     const ValueType* b,\n                     const ValueType  beta,\n                     const ValueType* c,\n                     ValueType*       output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLAS::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLAS::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLAS::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLAS>(smem);\n    auto a_shared_tensor          = cublasdx::make_tensor(smem_a, BLAS::get_layout_smem_a());\n    auto b_shared_tensor          = cublasdx::make_tensor(smem_b, BLAS::get_layout_smem_b());\n    auto c_shared_tensor          = cublasdx::make_tensor(smem_c, BLAS::get_layout_smem_c());\n\n    using alignment = cublasdx::alignment_of<BLAS>;\n    cublasdx::copy<BLAS, alignment::a>(a_global_tensor, a_shared_tensor);\n    cublasdx::copy<BLAS, alignment::b>(b_global_tensor, b_shared_tensor);\n    cublasdx::copy<BLAS, alignment::c>(c_global_tensor, c_shared_tensor);\n    cublasdx::copy_wait();\n\n    BLAS().execute(alpha, a_shared_tensor, b_shared_tensor, beta, c_shared_tensor);\n\n    __syncthreads();\n\n    auto out_global_tensor = cublasdx::make_tensor(output, BLAS::get_layout_gmem_c());\n    cublasdx::copy<BLAS, alignment::c>(c_shared_tensor, out_global_tensor);\n}\n\ntemplate<class BLASWithoutLD, class ValueType = typename example::uniform_value_type_t<BLASWithoutLD>>\n__launch_bounds__(BLASWithoutLD::max_threads_per_block) //\n    __global__                                          //\n    void gemm_kernel_dynamic_ld(const ValueType    alpha,\n                                const ValueType*   a,\n                                const unsigned int lda,\n                                const ValueType*   b,\n                                const unsigned int ldb,\n                                const ValueType    beta,\n                                const ValueType*   c,\n                                const unsigned int ldc,\n                                ValueType*         output) {\n    using value_type = ValueType;\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    auto a_global_tensor = cublasdx::make_tensor(a, BLASWithoutLD::get_layout_gmem_a());\n    auto b_global_tensor = cublasdx::make_tensor(b, BLASWithoutLD::get_layout_gmem_b());\n    auto c_global_tensor = cublasdx::make_tensor(c, BLASWithoutLD::get_layout_gmem_c());\n\n    auto [smem_a, smem_b, smem_c] = cublasdx::slice_shared_memory<BLASWithoutLD>(smem, lda, ldb, ldc);\n    auto ta                       = cublasdx::make_tensor(smem_a, BLASWithoutLD::get_layout_smem_a(lda));\n    auto tb                       = cublasdx::make_tensor(smem_b, BLASWithoutLD::get_layout_smem_b(ldb));\n    auto tc                       = cublasdx::make_tensor(smem_c, BLASWithoutLD::get_layout_smem_c(ldc));\n\n    using alignment = cublasdx::alignment_of<BLASWithoutLD>;\n    cublasdx::copy<BLASWithoutLD, alignment::a>(a_global_tensor, ta);\n    cublasdx::copy<BLASWithoutLD, alignment::b>(b_global_tensor, tb);\n    cublasdx::copy<BLASWithoutLD, alignment::c>(c_global_tensor, tc);\n    cublasdx::copy_wait();\n\n    BLASWithoutLD().execute(alpha, ta, tb, beta, tc);\n\n    __syncthreads();\n    auto tgout = cublasdx::make_tensor(output, BLASWithoutLD::get_layout_gmem_c(ldc));\n    cublasdx::copy<BLASWithoutLD, alignment::c>(tc, tgout);\n}\n\n// This is an example of fp64 general matrix-matrix multiplication (GEMM) performed\n// in a single CUDA block:\n//\n//              C = alpha * A * B + beta * C\n//\n// * A, B, and C are matrices containing real double precision floating-point values.\n// * alpha and beta are real double precision floating-point values.\n//\n// Input data is generated on host using random number generators, and later copied to\n// the global memory. Next, kernel with GEMM is executed, and then the matrix C (the result)\n// is copied back to host memory. The results are verified against cuBLAS.\n//\n// In this example the number of threads participating in the GEMM operation is imposed by providing\n// BlockDim operator in definition of the GEMM. If BlockDim operator is not used, cuBLASDx automatically\n// selects number of threads. Block dimensions are provided via BLAS::block_dim trait.\n//\n// Additionally, this example sets leading dimensions for the A, B, C  matrices participating in the GEMM.\n// For BLAS type they are set statically via LeadingDimension operator. For comparision, there's  also\n// BLASWithoutLD type without that operator, and it is run with dynamic leading dimensions that are passed\n// as arguments to the execute() method.\ntemplate<unsigned int Arch>\nint simple_gemm_with_leading_dimensions() {\n    // Parameters m, n, k define the dimensions of matrices A, B, and C\n    constexpr unsigned int m = 30;\n    constexpr unsigned int n = 31;\n    constexpr unsigned int k = 33;\n\n    // If matrix A is column-major (or not transposed in BLAS nomenclature) its logical dimensions are: [m, k] (m rows, k columns)\n    // If matrix B is column-major its logical dimensions are: [k, n]\n    // If matrix A is row-major (or transposed in BLAS nomenclature) its logical dimensions are: [k, m]\n    // If matrix B is row-major its logical dimensions are: [n, k]\n    // The dimensions of matrix C are: [m, n]\n    constexpr auto a_arrangement = cublasdx::col_major;\n    constexpr auto b_arrangement = cublasdx::row_major;\n    constexpr auto c_arrangement = cublasdx::row_major;\n\n    // Leading dimensions defined for matrices A, B, C\n\n    // If A is column-major, A is a matrix of real dimensions [lda, k] with lda >= m\n    // If A is row-major, A is a matrix of real dimensions [lda, m] with lda >= k\n    constexpr unsigned int lda = 32;\n    // If B is column-major, B is a matrix of real dimensions [ldb, n] with ldb >= k\n    // If B is row-major, B is a matrix of real dimensions [ldb, k] with ldb >= n\n    constexpr unsigned int ldb = 33;\n    // C is a matrix of real dimensions [ldc, n] with ldx >= m\n    constexpr unsigned int ldc = 31;\n\n    // Selected CUDA block size (2D)\n    constexpr dim3 block_dim(16, 16, 1);\n\n    // GEMM definition using cuBLASDx operators:\n    // 1. The size, the precision, and the type (real or complex) are set.\n    // 2. The BLAS function is selected: MM (matrix multiplication).\n    // 3. The data arrangements of A, B matrices are set (C is defaulted to column major).\n    //    - Optional\n    // 4. The data alignment of A, B and C matrices is set to the max accepted value.\n    //    - Optional\n    // 5. Block operator informs that GEMM should be performed on CUDA block level.\n    // 6. BlockDim operator sets CUDA block dimensions (in this cases 2D) that the kernel will be executed with.\n    // 7. Targeted CUDA compute capability is selected with SM operator.\n    using BLASWithoutLD =\n        decltype(cublasdx::Size<m, n, k>() + cublasdx::Precision<double>() + cublasdx::Type<cublasdx::type::real>() +\n                 cublasdx::Function<cublasdx::function::MM>() +\n                 cublasdx::Arrangement<a_arrangement, b_arrangement, c_arrangement>() + cublasdx::Block() +\n                 cublasdx::BlockDim<block_dim.x, block_dim.y, block_dim.z>() + cublasdx::SM<Arch>());\n    // 6. Leading dimensions for matrices A, B, C\n    using BLAS       = decltype(BLASWithoutLD() + cublasdx::LeadingDimension<lda, ldb, ldc>());\n    using value_type = typename example::uniform_value_type_t<BLAS>;\n\n    // Allocate managed memory for a, b, c, and output\n    value_type* inputs;\n    value_type* output;\n\n    constexpr auto global_a_size = example::global_memory_size_of<BLAS>::a_size;\n    constexpr auto global_b_size = example::global_memory_size_of<BLAS>::b_size;\n    constexpr auto global_c_size = example::global_memory_size_of<BLAS>::c_size;\n\n    auto inputs_size       = global_a_size + global_b_size + global_c_size;\n    auto inputs_size_bytes = inputs_size * sizeof(value_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&inputs, inputs_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, global_c_size * sizeof(value_type)));\n\n    value_type* a     = inputs;\n    value_type* b     = a + (global_a_size);\n    value_type* c     = b + (global_b_size);\n    value_type  alpha = value_type(1.0);\n    value_type  beta  = value_type(2.0);\n\n    // Fill the A, B, C matrices with random values\n    auto host_a = example::get_random_data<value_type>(global_a_size);\n    auto host_b = example::get_random_data<value_type>(global_b_size);\n    auto host_c = example::get_random_data<value_type>(global_c_size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(a, host_a.data(), global_a_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(b, host_b.data(), global_b_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(c, host_c.data(), global_c_size * sizeof(value_type), hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel<BLAS>), hipFuncAttributeMaxDynamicSharedMemorySize, cublasdx::get_shared_storage_size<BLAS>()));\n\n    // Execute kernel with GEMM with defined leading dimensions (known at compile time)\n    gemm_kernel<BLAS><<<1, BLAS::block_dim, cublasdx::get_shared_storage_size<BLAS>()>>>(alpha, a, b, beta, c, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Perform same GEMM but without defined leading dimensions, instead use dynamic leading dimensions.\n    // Dynamic leading dimensions are the same are for BLAS which will enable us to reuse the same device buffers.\n\n    // Increase max dynamic shared memory for the kernel if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(gemm_kernel_dynamic_ld<BLASWithoutLD>),\n                                             hipFuncAttributeMaxDynamicSharedMemorySize,\n                                             cublasdx::get_shared_storage_size<BLASWithoutLD>(lda, ldb, ldc)));\n\n    // Execute kernel\n    gemm_kernel_dynamic_ld<BLASWithoutLD>\n        <<<1, BLASWithoutLD::block_dim, cublasdx::get_shared_storage_size<BLASWithoutLD>(lda, ldb, ldc)>>>(\n            alpha, a, lda, b, ldb, beta, c, ldc, output);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy results back to host\n    std::vector<value_type> host_output_dynamic_ld(global_c_size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(host_output_dynamic_ld.data(), output, global_c_size * sizeof(value_type), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Free device memory\n    CUDA_CHECK_AND_EXIT(hipFree(inputs));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Calculate reference\n    auto reference_host_output = example::reference_gemm<BLAS>(alpha, host_a, host_b, beta, host_c);\n\n    // Check both results against reference\n    auto correct_static_ld  = example::check_error<BLAS>(host_output, reference_host_output);\n    auto correct_dynamic_ld = example::check_error<BLAS>(host_output_dynamic_ld, reference_host_output);\n    if (correct_static_ld && correct_dynamic_ld) {\n        std::cout << \"Success\" << std::endl;\n        return 0;\n    }\n\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nstruct simple_gemm_with_leading_dimensions_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_gemm_with_leading_dimensions<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_gemm_with_leading_dimensions_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/06_gemm_leading_dimension/simple_gemm_leading_dimensions.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an example of a block-level triangular solve (TRSM) performed in a\n// single CUDA block using cuBLASDx.\n//\n// The operation solved is:\n//\n//   A * X = B\n//\n// where A is a lower-triangular, non-unit, single-precision matrix of size\n// (M x M), and B / X are (M x N) matrices. All matrices use column-major\n// layout.\n//\n// 3D batched global-memory tensors are created on the host from device pointers\n// and passed directly to the kernel. The kernel extracts the per-block tile,\n// copies A and B into shared memory, calls BLAS{}.execute(), and writes the\n// solution X back to global memory.\n//\n// Correctness is verified against a cuBLAS reference solution.\n//\n// Performance is measured by timing kernel_repeats launches of the kernel with\n// warm-up, following the pattern used in cuBLASDx example 11.\n\n#include <cublasdx/database/trsm/trsm_db.hpp>\n\n#include \"../common/trsm_common.hpp\"\n\n// -------------------------------------------------------------------------\n// trsm_block_kernel\n//\n// Each block processes BPB consecutive TRSM instances (BPB = BLAS::batches_per_block).\n// GlobalTensorA / GlobalTensorB are 3D tensors (rows x cols x padded_batches)\n// created on the host and passed by value.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void trsm_block_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using T         = typename BLAS::a_value_type;\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Guard: skip blocks beyond the total block count.\n    const unsigned total_blocks = cute::size<2>(global_a) / BLAS::batches_per_block;\n    if (blockIdx.x >= total_blocks) {\n        return;\n    }\n\n    // Extract per-block 2D (BPB==1) or 3D (BPB>1) tile from the global tensor.\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), blockIdx.x);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), blockIdx.x);\n\n    // Partition shared memory into A and B tensors.\n    auto [smem_tensor_a, smem_tensor_b] = cublasdx::shared_memory::slice<T, T>(\n        smem, alignment::a, BLAS::get_layout_smem_a(), alignment::b, BLAS::get_layout_smem_b());\n\n    // Load A and B from global memory into shared memory.\n    cublasdx::copy<BLAS, alignment::a>(batch_a, smem_tensor_a);\n    cublasdx::copy<BLAS, alignment::b>(batch_b, smem_tensor_b);\n    cublasdx::copy_wait();\n\n    BLAS {}.execute(smem_tensor_a, smem_tensor_b);\n    __syncthreads();\n\n    // Write the solution (B overwritten by X) back to global memory.\n    cublasdx::copy<BLAS, alignment::b>(smem_tensor_b, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// simple_trsm  - the main example function, instantiated per SM architecture\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint simple_trsm() {\n    // -----------------------------------------------------------------------\n    // TRSM problem definition\n    // -----------------------------------------------------------------------\n    constexpr unsigned M = 64; // rows of B (= size of A for left-side)\n    constexpr unsigned N = 4;  // columns of B\n\n    using T             = float;\n    constexpr auto Side = cublasdx::side::left;\n    constexpr auto Fill = cublasdx::fill_mode::lower;\n    constexpr auto Diag = cublasdx::diag::non_unit;\n    constexpr auto ArrA = cublasdx::col_major;\n    constexpr auto ArrB = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 3855;\n\n    constexpr bool     is_left = (Side == cublasdx::side::left);\n    constexpr unsigned dim_a   = is_left ? M : N;\n\n    constexpr unsigned BPB         = 1;\n    constexpr unsigned num_threads = 64;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor.\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<T>() +\n        cublasdx::Type<cublasdx::type::real>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Block() +\n        cublasdx::BlockDim<num_threads>() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<Fill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>() +\n        cublasdx::BatchesPerBlock<BPB>());\n\n    // Pad the batch count to a multiple of BPB so every block handles a full set.\n    const unsigned padded_batches = cute::round_up(num_batches, BPB);\n\n    // -----------------------------------------------------------------------\n    // Host data: A (triangular, dim_a x dim_a) and B (M x N), column-major.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto h_A = example::get_random_uniform_data<T>(padded_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = example::get_random_uniform_data<T>(padded_batches * b_per_batch, -1.f, 1.f);\n\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, padded_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    // Keep an unmodified copy of B for the cuBLAS reference and for restoring\n    // device memory before performance timing (TRSM overwrites B in-place).\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    // -----------------------------------------------------------------------\n    // Build 3D global-memory tensors on the host and determine kernel types.\n    //\n    // Shape: (dim_a, dim_a, padded_batches) for A\n    //        (M, N, padded_batches)         for B\n    // -----------------------------------------------------------------------\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, padded_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, padded_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Kernel configuration\n    // -----------------------------------------------------------------------\n    const unsigned smem_bytes = cublasdx::make_shared_storage_calculator()\n                                    .add(cublasdx::alignment_of<BLAS>::a, sizeof(T), BLAS::get_layout_smem_a())\n                                    .add(cublasdx::alignment_of<BLAS>::b, sizeof(T), BLAS::get_layout_smem_b())\n                                    .get();\n\n    auto kernel = trsm_block_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n\n    const unsigned num_blocks = padded_batches / BPB;\n    const dim3     block_dim(num_threads);\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    auto run_kernel = [&](cudaStream_t s) { kernel<<<num_blocks, block_dim, smem_bytes, s>>>(global_a, global_b); };\n\n    // -----------------------------------------------------------------------\n    // Correctness run\n    // -----------------------------------------------------------------------\n    run_kernel(stream);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Download the result for the first num_batches entries.\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, cudaMemcpyDeviceToHost));\n\n    static constexpr unsigned kernel_repeats         = 5;\n    static constexpr unsigned kernel_warm_up_repeats = 5;\n\n    // Compute cuBLAS reference solution and measure its performance in one call.\n    auto [h_B_ref, cublas_time_ms] =\n        example::reference_trsm<BLAS>(h_A, h_B_orig, num_batches, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    // -----------------------------------------------------------------------\n    // cuBLASDx performance measurement.\n    //\n    // Restore d_B to the original input before timing so both warm-up and\n    // measurement runs start from the same data.  global_b still points to\n    // d_B.data() - no need to recreate the tensor.\n    // -----------------------------------------------------------------------\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(d_B.data(), h_B_orig.data(), sizeof(T) * padded_batches * b_per_batch, cudaMemcpyHostToDevice));\n\n    const double time_ms = example::measure::execution(run_kernel, kernel_warm_up_repeats, kernel_repeats, stream);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // TRSM FLOP count: left-side = M\u00b2*N, right-side = M*N\u00b2.\n    // same as MAGMA: https://github.com/CEED/MAGMA/blob/master/testing/flops.h\n    const double flops_mul_single = (is_left ? (0.5 * (M + 1) * M * N) : (0.5 * M * (N + 1) * N));\n    const double flops_add_single = (is_left ? (0.5 * (M - 1) * M * N) : (0.5 * M * (N - 1) * N));\n    const double flops            = padded_batches * (flops_mul_single + flops_add_single);\n\n    const double avg_time_ms   = time_ms / kernel_repeats;\n    const double cublas_avg_ms = cublas_time_ms / kernel_repeats;\n\n    std::cout << \"cuBLASDx TRSM Block Example\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Side=\" << (is_left ? \"left\" : \"right\")\n              << \"  Fill=\" << (Fill == cublasdx::fill_mode::lower ? \"lower\" : \"upper\")\n              << \"  Diag=\" << (Diag == cublasdx::diag::unit ? \"unit\" : \"non_unit\") << \"  Precision=float\" << std::endl;\n    std::cout << \"  BlockDim=\" << num_threads << \"  BPB=\" << BPB << \"  SharedMem=\" << smem_bytes << \" B\" << std::endl;\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"  cuBLASDx block:\" << std::endl;\n    std::cout << \"    Avg time [ms]: \" << avg_time_ms << std::endl;\n    std::cout << \"    Performance [GFLOPS]: \" << flops / (avg_time_ms * 1e6) << std::endl;\n    std::cout << \"  cuBLAS reference:\" << std::endl;\n    std::cout << \"    Avg time [ms]: \" << cublas_avg_ms << std::endl;\n    std::cout << \"    Performance [GFLOPS]: \" << flops / (cublas_avg_ms * 1e6) << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    return 0;\n}\n\nstruct simple_trsm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_trsm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_trsm_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an example of a block-level triangular solve (TRSM) performed in a\n// single CUDA block using cuBLASDx.\n//\n// The operation solved is:\n//\n//   A * X = B\n//\n// where A is a lower-triangular, non-unit, single-precision matrix of size\n// (M x M), and B / X are (M x N) matrices. All matrices use column-major\n// layout.\n//\n// 3D batched global-memory tensors are created on the host from device pointers\n// and passed directly to the kernel. The kernel extracts the per-block tile,\n// copies A and B into shared memory, calls BLAS{}.execute(), and writes the\n// solution X back to global memory.\n//\n// Correctness is verified against a cuBLAS reference solution.\n//\n// Performance is measured by timing kernel_repeats launches of the kernel with\n// warm-up, following the pattern used in cuBLASDx example 11.\n\n#include \"hip/hip_runtime.h\"\n#include <cublasdx/database/trsm/trsm_db.hpp>\n\n#include \"../common/trsm_common.hpp\"\n\n// -------------------------------------------------------------------------\n// trsm_block_kernel\n//\n// Each block processes BPB consecutive TRSM instances (BPB = BLAS::batches_per_block).\n// GlobalTensorA / GlobalTensorB are 3D tensors (rows x cols x padded_batches)\n// created on the host and passed by value.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__launch_bounds__(BLAS::max_threads_per_block) __global__\n    void trsm_block_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    extern __shared__ __align__(16) cublasdx::byte smem[];\n\n    using T         = typename BLAS::a_value_type;\n    using alignment = cublasdx::alignment_of<BLAS>;\n\n    // Guard: skip blocks beyond the total block count.\n    const unsigned total_blocks = cute::size<2>(global_a) / BLAS::batches_per_block;\n    if (blockIdx.x >= total_blocks) {\n        return;\n    }\n\n    // Extract per-block 2D (BPB==1) or 3D (BPB>1) tile from the global tensor.\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), blockIdx.x);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), blockIdx.x);\n\n    // Partition shared memory into A and B tensors.\n    auto [smem_tensor_a, smem_tensor_b] = cublasdx::shared_memory::slice<T, T>(\n        smem, alignment::a, BLAS::get_layout_smem_a(), alignment::b, BLAS::get_layout_smem_b());\n\n    // Load A and B from global memory into shared memory.\n    cublasdx::copy<BLAS, alignment::a>(batch_a, smem_tensor_a);\n    cublasdx::copy<BLAS, alignment::b>(batch_b, smem_tensor_b);\n    cublasdx::copy_wait();\n\n    BLAS {}.execute(smem_tensor_a, smem_tensor_b);\n    __syncthreads();\n\n    // Write the solution (B overwritten by X) back to global memory.\n    cublasdx::copy<BLAS, alignment::b>(smem_tensor_b, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// simple_trsm  - the main example function, instantiated per SM architecture\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint simple_trsm() {\n    // -----------------------------------------------------------------------\n    // TRSM problem definition\n    // -----------------------------------------------------------------------\n    constexpr unsigned M = 64; // rows of B (= size of A for left-side)\n    constexpr unsigned N = 4;  // columns of B\n\n    using T             = float;\n    constexpr auto Side = cublasdx::side::left;\n    constexpr auto Fill = cublasdx::fill_mode::lower;\n    constexpr auto Diag = cublasdx::diag::non_unit;\n    constexpr auto ArrA = cublasdx::col_major;\n    constexpr auto ArrB = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 3855;\n\n    constexpr bool     is_left = (Side == cublasdx::side::left);\n    constexpr unsigned dim_a   = is_left ? M : N;\n\n    constexpr unsigned BPB         = 1;\n    constexpr unsigned num_threads = 64;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor.\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<T>() +\n        cublasdx::Type<cublasdx::type::real>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Block() +\n        cublasdx::BlockDim<num_threads>() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<Fill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>() +\n        cublasdx::BatchesPerBlock<BPB>());\n\n    // Pad the batch count to a multiple of BPB so every block handles a full set.\n    const unsigned padded_batches = cute::round_up(num_batches, BPB);\n\n    // -----------------------------------------------------------------------\n    // Host data: A (triangular, dim_a x dim_a) and B (M x N), column-major.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto h_A = example::get_random_uniform_data<T>(padded_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = example::get_random_uniform_data<T>(padded_batches * b_per_batch, -1.f, 1.f);\n\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, padded_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    // Keep an unmodified copy of B for the cuBLAS reference and for restoring\n    // device memory before performance timing (TRSM overwrites B in-place).\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    // -----------------------------------------------------------------------\n    // Build 3D global-memory tensors on the host and determine kernel types.\n    //\n    // Shape: (dim_a, dim_a, padded_batches) for A\n    //        (M, N, padded_batches)         for B\n    // -----------------------------------------------------------------------\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, padded_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, padded_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Kernel configuration\n    // -----------------------------------------------------------------------\n    const unsigned smem_bytes = cublasdx::make_shared_storage_calculator()\n                                    .add(cublasdx::alignment_of<BLAS>::a, sizeof(T), BLAS::get_layout_smem_a())\n                                    .add(cublasdx::alignment_of<BLAS>::b, sizeof(T), BLAS::get_layout_smem_b())\n                                    .get();\n\n    auto kernel = trsm_block_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, smem_bytes));\n\n    const unsigned num_blocks = padded_batches / BPB;\n    const dim3     block_dim(num_threads);\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    auto run_kernel = [&](hipStream_t s) { kernel<<<num_blocks, block_dim, smem_bytes, s>>>(global_a, global_b); };\n\n    // -----------------------------------------------------------------------\n    // Correctness run\n    // -----------------------------------------------------------------------\n    run_kernel(stream);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Download the result for the first num_batches entries.\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, hipMemcpyDeviceToHost));\n\n    static constexpr unsigned kernel_repeats         = 5;\n    static constexpr unsigned kernel_warm_up_repeats = 5;\n\n    // Compute cuBLAS reference solution and measure its performance in one call.\n    auto [h_B_ref, cublas_time_ms] =\n        example::reference_trsm<BLAS>(h_A, h_B_orig, num_batches, kernel_warm_up_repeats, kernel_repeats, stream);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    // -----------------------------------------------------------------------\n    // cuBLASDx performance measurement.\n    //\n    // Restore d_B to the original input before timing so both warm-up and\n    // measurement runs start from the same data.  global_b still points to\n    // d_B.data() - no need to recreate the tensor.\n    // -----------------------------------------------------------------------\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(d_B.data(), h_B_orig.data(), sizeof(T) * padded_batches * b_per_batch, hipMemcpyHostToDevice));\n\n    const double time_ms = example::measure::execution(run_kernel, kernel_warm_up_repeats, kernel_repeats, stream);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // TRSM FLOP count: left-side = M\u00b2*N, right-side = M*N\u00b2.\n    // same as MAGMA: https://github.com/CEED/MAGMA/blob/master/testing/flops.h\n    const double flops_mul_single = (is_left ? (0.5 * (M + 1) * M * N) : (0.5 * M * (N + 1) * N));\n    const double flops_add_single = (is_left ? (0.5 * (M - 1) * M * N) : (0.5 * M * (N - 1) * N));\n    const double flops            = padded_batches * (flops_mul_single + flops_add_single);\n\n    const double avg_time_ms   = time_ms / kernel_repeats;\n    const double cublas_avg_ms = cublas_time_ms / kernel_repeats;\n\n    std::cout << \"cuBLASDx TRSM Block Example\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Side=\" << (is_left ? \"left\" : \"right\")\n              << \"  Fill=\" << (Fill == cublasdx::fill_mode::lower ? \"lower\" : \"upper\")\n              << \"  Diag=\" << (Diag == cublasdx::diag::unit ? \"unit\" : \"non_unit\") << \"  Precision=float\" << std::endl;\n    std::cout << \"  BlockDim=\" << num_threads << \"  BPB=\" << BPB << \"  SharedMem=\" << smem_bytes << \" B\" << std::endl;\n    std::cout << std::fixed << std::setprecision(4);\n    std::cout << \"  cuBLASDx block:\" << std::endl;\n    std::cout << \"    Avg time [ms]: \" << avg_time_ms << std::endl;\n    std::cout << \"    Performance [GFLOPS]: \" << flops / (avg_time_ms * 1e6) << std::endl;\n    std::cout << \"  cuBLAS reference:\" << std::endl;\n    std::cout << \"    Avg time [ms]: \" << cublas_avg_ms << std::endl;\n    std::cout << \"    Performance [GFLOPS]: \" << flops / (cublas_avg_ms * 1e6) << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    return 0;\n}\n\nstruct simple_trsm_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_trsm<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_trsm_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/17_trsm/trsm_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an example of a thread-level triangular solve (TRSM) using cuBLASDx.\n//\n// The operation solved is:\n//\n//   X * A = B\n//\n// where A is a lower-triangular, non-unit, double-precision matrix of size\n// (N x N), and B / X are (M x N) matrices. All matrices use column-major\n// layout. 300 independent instances are solved in parallel.\n//\n// Unlike the block-level API, the thread-level API assigns one CUDA thread per\n// TRSM instance. Each thread reads A and B directly from global memory and\n// writes X back. No shared memory is required.\n//\n// 3D batched global-memory tensors are created on the host from device pointers\n// and passed directly to the kernel. The kernel extracts the per-thread tile\n// using cublasdx::get_batch() and calls BLAS{}.execute() directly on those\n// global-memory tensors.\n//\n// Correctness is verified against a cuBLAS reference solution.\n\n#include \"../common/trsm_common.hpp\"\n\n// Number of threads per block for the thread-level kernel.\nconstexpr unsigned THREADS_PER_BLOCK = 64;\n\n// -------------------------------------------------------------------------\n// trsm_thread_kernel\n//\n// One thread per TRSM instance.  GlobalTensorA / GlobalTensorB are 3D tensors\n// (rows x cols x num_batches) created on the host and passed by value.\n// Each thread extracts its own 2D tile and calls BLAS{}.execute() directly on\n// the global-memory tensors - no shared memory required.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__global__ void trsm_thread_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    const unsigned thread_idx  = blockIdx.x * blockDim.x + threadIdx.x;\n    const unsigned total_items = cute::size<2>(global_a);\n    if (thread_idx >= total_items) {\n        return;\n    }\n\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), thread_idx);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), thread_idx);\n\n    BLAS {}.execute(batch_a, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// simple_trsm_thread  - the main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint simple_trsm_thread() {\n    // -----------------------------------------------------------------------\n    // TRSM problem definition\n    // -----------------------------------------------------------------------\n    constexpr unsigned M = 10; // rows of B\n    constexpr unsigned N = 12; // columns of B (= size of A for right-side)\n\n    using T             = double;\n    constexpr auto Side = cublasdx::side::right;\n    constexpr auto Fill = cublasdx::fill_mode::lower;\n    constexpr auto Diag = cublasdx::diag::non_unit;\n    constexpr auto ArrA = cublasdx::col_major;\n    constexpr auto ArrB = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 300;\n\n    constexpr bool     is_left = (Side == cublasdx::side::left);\n    constexpr unsigned dim_a   = is_left ? M : N;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor (thread-level API).\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<T>() +\n        cublasdx::Type<cublasdx::type::real>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Thread() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<Fill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>());\n\n    // -----------------------------------------------------------------------\n    // Host data: A (triangular, dim_a x dim_a) and B (M x N), column-major.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto h_A = example::get_random_uniform_data<T>(num_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = example::get_random_uniform_data<T>(num_batches * b_per_batch, -1.f, 1.f);\n\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, num_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    // Keep an unmodified copy of B for the cuBLAS reference.\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    // -----------------------------------------------------------------------\n    // Build 3D global-memory tensors on the host and determine kernel types.\n    //\n    // Shape: (dim_a, dim_a, num_batches) for A\n    //        (M, N, num_batches)         for B\n    // -----------------------------------------------------------------------\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, num_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, num_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Launch: one thread per TRSM instance.\n    // -----------------------------------------------------------------------\n    const unsigned num_blocks = (num_batches + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n\n    trsm_thread_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>\n        <<<num_blocks, THREADS_PER_BLOCK>>>(global_a, global_b);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // -----------------------------------------------------------------------\n    // Download results and verify correctness against cuBLAS.\n    // -----------------------------------------------------------------------\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, cudaMemcpyDeviceToHost));\n\n    auto [h_B_ref, cublas_ms] = example::reference_trsm<BLAS>(h_A, h_B_orig, num_batches);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    std::cout << \"cuBLASDx TRSM Thread Example\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Side=\" << (is_left ? \"left\" : \"right\")\n              << \"  Fill=\" << (Fill == cublasdx::fill_mode::lower ? \"lower\" : \"upper\")\n              << \"  Diag=\" << (Diag == cublasdx::diag::unit ? \"unit\" : \"non_unit\") << \"  Precision=double\"\n              << \"  Batches=\" << num_batches << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    return 0;\n}\n\nstruct simple_trsm_thread_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_trsm_thread<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_trsm_thread_functor {});\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is an example of a thread-level triangular solve (TRSM) using cuBLASDx.\n//\n// The operation solved is:\n//\n//   X * A = B\n//\n// where A is a lower-triangular, non-unit, double-precision matrix of size\n// (N x N), and B / X are (M x N) matrices. All matrices use column-major\n// layout. 300 independent instances are solved in parallel.\n//\n// Unlike the block-level API, the thread-level API assigns one CUDA thread per\n// TRSM instance. Each thread reads A and B directly from global memory and\n// writes X back. No shared memory is required.\n//\n// 3D batched global-memory tensors are created on the host from device pointers\n// and passed directly to the kernel. The kernel extracts the per-thread tile\n// using cublasdx::get_batch() and calls BLAS{}.execute() directly on those\n// global-memory tensors.\n//\n// Correctness is verified against a cuBLAS reference solution.\n\n#include \"hip/hip_runtime.h\"\n#include \"../common/trsm_common.hpp\"\n\n// Number of threads per block for the thread-level kernel.\nconstexpr unsigned THREADS_PER_BLOCK = 64;\n\n// -------------------------------------------------------------------------\n// trsm_thread_kernel\n//\n// One thread per TRSM instance.  GlobalTensorA / GlobalTensorB are 3D tensors\n// (rows x cols x num_batches) created on the host and passed by value.\n// Each thread extracts its own 2D tile and calls BLAS{}.execute() directly on\n// the global-memory tensors - no shared memory required.\n// -------------------------------------------------------------------------\ntemplate<class BLAS, class GlobalTensorA, class GlobalTensorB>\n__global__ void trsm_thread_kernel(GlobalTensorA global_a, GlobalTensorB global_b) {\n    const unsigned thread_idx  = blockIdx.x * blockDim.x + threadIdx.x;\n    const unsigned total_items = cute::size<2>(global_a);\n    if (thread_idx >= total_items) {\n        return;\n    }\n\n    auto batch_a = cublasdx::get_batch(global_a, BLAS::get_layout_gmem_a(), thread_idx);\n    auto batch_b = cublasdx::get_batch(global_b, BLAS::get_layout_gmem_b(), thread_idx);\n\n    BLAS {}.execute(batch_a, batch_b);\n}\n\n// -------------------------------------------------------------------------\n// simple_trsm_thread  - the main example function, instantiated per SM arch\n// -------------------------------------------------------------------------\ntemplate<unsigned int Arch>\nint simple_trsm_thread() {\n    // -----------------------------------------------------------------------\n    // TRSM problem definition\n    // -----------------------------------------------------------------------\n    constexpr unsigned M = 10; // rows of B\n    constexpr unsigned N = 12; // columns of B (= size of A for right-side)\n\n    using T             = double;\n    constexpr auto Side = cublasdx::side::right;\n    constexpr auto Fill = cublasdx::fill_mode::lower;\n    constexpr auto Diag = cublasdx::diag::non_unit;\n    constexpr auto ArrA = cublasdx::col_major;\n    constexpr auto ArrB = cublasdx::col_major;\n\n    constexpr unsigned num_batches = 300;\n\n    constexpr bool     is_left = (Side == cublasdx::side::left);\n    constexpr unsigned dim_a   = is_left ? M : N;\n\n    // -----------------------------------------------------------------------\n    // Compose the cuBLASDx TRSM descriptor (thread-level API).\n    // -----------------------------------------------------------------------\n    using BLAS = decltype(\n        cublasdx::Size<M, N>() +\n        cublasdx::Precision<T>() +\n        cublasdx::Type<cublasdx::type::real>() +\n        cublasdx::Function<cublasdx::function::TRSM>() +\n        cublasdx::SM<Arch>() +\n        cublasdx::Thread() +\n        cublasdx::Side<Side>() +\n        cublasdx::FillMode<Fill>() +\n        cublasdx::Diag<Diag>() +\n        cublasdx::Arrangement<ArrA, ArrB>());\n\n    // -----------------------------------------------------------------------\n    // Host data: A (triangular, dim_a x dim_a) and B (M x N), column-major.\n    // -----------------------------------------------------------------------\n    const unsigned a_per_batch = dim_a * dim_a;\n    const unsigned b_per_batch = M * N;\n\n    auto h_A = example::get_random_uniform_data<T>(num_batches * a_per_batch, -4.f, 4.f);\n    auto h_B = example::get_random_uniform_data<T>(num_batches * b_per_batch, -1.f, 1.f);\n\n    auto h_tensor_a = example::make_trsm_tensor<T, ArrA>(h_A.data(), dim_a, dim_a, num_batches);\n    example::make_diagonal_dominant(h_tensor_a);\n\n    // Keep an unmodified copy of B for the cuBLAS reference.\n    const auto h_B_orig = h_B;\n\n    // -----------------------------------------------------------------------\n    // Device buffers\n    // -----------------------------------------------------------------------\n    example::device_vector<T> d_A(h_A);\n    example::device_vector<T> d_B(h_B);\n\n    // -----------------------------------------------------------------------\n    // Build 3D global-memory tensors on the host and determine kernel types.\n    //\n    // Shape: (dim_a, dim_a, num_batches) for A\n    //        (M, N, num_batches)         for B\n    // -----------------------------------------------------------------------\n    auto global_a = cublasdx::make_gmem_tensor_batched<ArrA>(d_A.data(), dim_a, dim_a, num_batches);\n    auto global_b = cublasdx::make_gmem_tensor_batched<ArrB>(d_B.data(), M, N, num_batches);\n\n    using global_tensor_a_t = decltype(global_a);\n    using global_tensor_b_t = decltype(global_b);\n\n    // -----------------------------------------------------------------------\n    // Launch: one thread per TRSM instance.\n    // -----------------------------------------------------------------------\n    const unsigned num_blocks = (num_batches + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;\n\n    trsm_thread_kernel<BLAS, global_tensor_a_t, global_tensor_b_t>\n        <<<num_blocks, THREADS_PER_BLOCK>>>(global_a, global_b);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // -----------------------------------------------------------------------\n    // Download results and verify correctness against cuBLAS.\n    // -----------------------------------------------------------------------\n    std::vector<T> h_X(num_batches * b_per_batch);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(h_X.data(), d_B.data(), sizeof(T) * num_batches * b_per_batch, hipMemcpyDeviceToHost));\n\n    auto [h_B_ref, cublas_ms] = example::reference_trsm<BLAS>(h_A, h_B_orig, num_batches);\n\n    const double l2_err = example::calculate_error(h_X, h_B_ref);\n\n    std::cout << \"cuBLASDx TRSM Thread Example\" << std::endl;\n    std::cout << \"  M=\" << M << \"  N=\" << N << \"  Side=\" << (is_left ? \"left\" : \"right\")\n              << \"  Fill=\" << (Fill == cublasdx::fill_mode::lower ? \"lower\" : \"upper\")\n              << \"  Diag=\" << (Diag == cublasdx::diag::unit ? \"unit\" : \"non_unit\") << \"  Precision=double\"\n              << \"  Batches=\" << num_batches << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    std::cout << \"L2 Error: \" << l2_err << std::endl;\n    std::cout << \" =================================\" << std::endl;\n    return 0;\n}\n\nstruct simple_trsm_thread_functor {\n    template<int Arch, cublasdx::sm_modifier Modifier>\n    int operator()(std::integral_constant<int, Arch>, std::integral_constant<cublasdx::sm_modifier, Modifier>) {\n        return simple_trsm_thread<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner(simple_trsm_thread_functor {});\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuBLASDx/17_trsm/trsm_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/mixed_io.hpp\"\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         typename InputOutputType,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void mixed_fft_2d_kernel(const InputOutputType*        input,\n                             InputOutputType*              output,\n                             typename FFTX::workspace_type workspace_x,\n                             typename FFTY::workspace_type workspace_y) {\n    using complex_type      = typename FFTX::value_type;\n    constexpr bool is_mixed = !std::is_same_v<InputOutputType, complex_type>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) cufftdx::byte shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if (blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        using io_type = std::conditional_t<is_mixed, example::io_mixed<FFTY>, example::io<FFTY>>;\n        // Load data from global memory to registers\n        io_type::load(input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, reinterpret_cast<complex_type*>(shared_mem), workspace_y);\n\n        // Save results\n        io_type::store(thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if (blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        using dim_desc_type = example::dimension_description;\n        using io_type       = example::io_generic_strided<FFTX>;\n\n        // Load data from global memory to registers\n        // Both loads will convert the input if mixed\n        // precision is used\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_type::load_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                output, thread_data, reinterpret_cast<InputOutputType*>(shared_mem), local_fft_id);\n        } else {\n            io_type::load_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, reinterpret_cast<complex_type*>(shared_mem), workspace_x);\n\n        // Save results\n        // Both stores will convert the input if mixed\n        // precision is used\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_type::store_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                thread_data, reinterpret_cast<InputOutputType*>(shared_mem), output, local_fft_id);\n        } else {\n            io_type::store_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<typename InputOutputType>\nexample::fft_results<InputOutputType> cufft_mixed_fft_2d(const unsigned int warm_up_runs,\n                                                         const unsigned int kernel_runs,\n                                                         cudaStream_t       stream,\n                                                         unsigned int       fft_size_x,\n                                                         unsigned int       fft_size_y,\n                                                         InputOutputType*   input,\n                                                         InputOutputType*   output) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(InputOutputType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<InputOutputType> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan, fft_size_x, fft_size_y, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t                 flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t                 flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<InputOutputType> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return {output_host, time / kernel_runs};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, typename InputOutputType>\nexample::fft_results<typename FFTX::value_type> cufftdx_mixed_fft_2d(const unsigned int warm_up_runs,\n                                                                     const unsigned int kernel_runs,\n                                                                     cudaStream_t       stream,\n                                                                     InputOutputType*   input,\n                                                                     InputOutputType*   output) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    constexpr bool is_mixed = !std::is_same_v<InputOutputType, complex_type>;\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(InputOutputType),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(InputOutputType)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel     = (const void*)mixed_fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, InputOutputType>;\n    auto        error_code = cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[]             = {&input, &output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto mixed_fft_2d_execution = [&](cudaStream_t stream) {\n        CUDA_CHECK_AND_EXIT(cudaLaunchCooperativeKernel(kernel, grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    mixed_fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size = fft_size_x * fft_size_y;\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n\n    if constexpr (is_mixed) {\n        // If the data is of mixed precision first copy InputOutputPrecision data into host memory\n        std::vector<InputOutputType> output_mixed_host(flat_fft_size);\n        CUDA_CHECK_AND_EXIT(cudaMemcpy(reinterpret_cast<void*>(output_mixed_host.data()), reinterpret_cast<void*>(output), flat_fft_size * sizeof(InputOutputType), cudaMemcpyDeviceToHost));\n\n        // And then transform it to ComputePrecision and copy to the return vector created above\n        std::transform(begin(output_mixed_host), end(output_mixed_host), begin(output_host), [](const auto& v) {\n            return example::convert<complex_type>(v);\n        });\n    } else {\n        // If the data is of constant precision perform a simple copy into host memory\n        CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size * sizeof(complex_type), cudaMemcpyDeviceToHost));\n        CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    }\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            mixed_fft_2d_execution(stream);\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Return results\n    return {output_host, time / kernel_runs};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel utilizing mixed precision storage\n// and computation using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch, unsigned int fft_size = 512>\nvoid mixed_fft_2d() {\n\n    // Choose mixed precision options\n    // Input and Output type\n    using io_precision = __half;\n    // Computation type\n    using compute_precision = float;\n    // Initialization happens to make sure this mixed configuration is supported\n    using mixed = example::mixed_precision<compute_precision, io_precision>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = fft_size;\n    static constexpr unsigned int fft_size_x = fft_size;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 8;\n    static constexpr unsigned int fpb_y = 8;\n    static constexpr unsigned int ept_x = 8;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same\n\n    static constexpr unsigned int warm_up_runs = 1;\n    static constexpr unsigned int kernel_runs  = 10;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<compute_precision>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Data types which will be used, based on previously choisen\n    // compute and input/output values\n    using complex_io_type      = typename mixed::complex_io_type;\n    using complex_compute_type = cufftdx::complex<compute_precision>;\n\n    // Host data\n    static constexpr size_t flat_fft_size          = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_compute_bytes = flat_fft_size * sizeof(complex_compute_type);\n    static constexpr size_t flat_fft_io_bytes      = flat_fft_size * sizeof(complex_io_type);\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    complex_io_type *     input_io, *output_io;\n    complex_compute_type *input_compute, *output_compute;\n\n    // Input/Output precision data buffers\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input_io, flat_fft_io_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output_io, flat_fft_io_bytes));\n\n    // Computation precision data buffers for comparison and verification\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input_compute, flat_fft_compute_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output_compute, flat_fft_compute_bytes));\n\n    // Host data\n    std::vector<complex_compute_type> single_fft_host_input =\n        example::get_random_complex_data<compute_precision>(flat_fft_size, -1, 1);\n    std::vector<complex_io_type> single_fft_host_mixed_input(flat_fft_size);\n\n    std::transform(begin(single_fft_host_input), end(single_fft_host_input), begin(single_fft_host_mixed_input), [](const auto& v) {\n        return example::convert<io_precision>(v);\n    });\n\n    cudaMemcpy(reinterpret_cast<void*>(input_compute), single_fft_host_input.data(), flat_fft_compute_bytes, cudaMemcpyHostToDevice);\n    cudaMemcpy(reinterpret_cast<void*>(input_io), single_fft_host_mixed_input.data(), flat_fft_io_bytes, cudaMemcpyHostToDevice);\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_mixed_results = cufftdx_mixed_fft_2d<fft_x, fft_y, false>(warm_up_runs, kernel_runs, stream, input_io, output_io);\n    auto cufftdx_full_results  = cufftdx_mixed_fft_2d<fft_x, fft_y, false>(warm_up_runs, kernel_runs, stream, input_compute, output_compute);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_mixed_smemio_results = cufftdx_mixed_fft_2d<fft_x, fft_y, true>(warm_up_runs, kernel_runs, stream, input_io, output_io);\n    auto cufftdx_full_smemio_results  = cufftdx_mixed_fft_2d<fft_x, fft_y, true>(warm_up_runs, kernel_runs, stream, input_compute, output_compute);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_mixed_fft_2d(warm_up_runs, kernel_runs, stream, fft_size_x, fft_size_y, input_compute, output_compute);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    // Verify Correctness\n    bool success          = true;\n    using result_vector_t = std::vector<complex_compute_type>;\n    std::cout << \"============\\nCorrectness results:\\n\";\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    };\n\n    verify_correctness(cufftdx_full_results.output, \"cuFFTDx Full\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_results.output, \"cuFFTDx Mixed\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_results.output, \"cuFFTDx Mixed\", cufftdx_full_results.output, \"cuFFTDx Full\");\n    verify_correctness(cufftdx_full_smemio_results.output, \"cuFFTDx Full Strided\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_smemio_results.output, \"cuFFTDx Mixed Strided\", cufft_results.output, \"cuFFT\");\n\n    // Report performance results\n    std::cout << \"============\\nPerformance results:\\n\";\n\n    auto report_time_and_performance = [&](std::string name, float avg_time) -> void {\n        double gflops = 1.0 * kernel_runs * 5.0 * flat_fft_size *\n                        (std::log(flat_fft_size) / std::log(2)) / (kernel_runs * avg_time) / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << avg_time << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << avg_time * kernel_runs << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / fft_y::ffts_per_block), (fft_size_x / fft_x::ffts_per_block)}), 1, 1};\n\n    std::cout << \"FFT size (X, Y): (\" << fft_size_x << \", \" << fft_size_y << \")\" << std::endl;\n    std::cout << \"CUDA grid: (X, Y): (\" << grid_size.x << \",\" << grid_size.y << \")\" << std::endl;\n\n    report_time_and_performance(\"cuFFT\", cufft_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision\", cufftdx_full_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision\", cufftdx_mixed_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Strided\", cufftdx_full_smemio_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Strided\", cufftdx_mixed_smemio_results.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success\" << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_io));\n    CUDA_CHECK_AND_EXIT(cudaFree(input_compute));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_io));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_compute));\n}\n\ntemplate<unsigned int Arch>\nstruct mixed_fft_2d_functor {\n    void operator()() {\n        mixed_fft_2d<Arch, 512>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mixed_fft_2d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/mixed_io.hpp\"\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         typename InputOutputType,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void mixed_fft_2d_kernel(const InputOutputType*        input,\n                             InputOutputType*              output,\n                             typename FFTX::workspace_type workspace_x,\n                             typename FFTY::workspace_type workspace_y) {\n    using complex_type      = typename FFTX::value_type;\n    constexpr bool is_mixed = !std::is_same_v<InputOutputType, complex_type>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) cufftdx::byte shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if (blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        using io_type = std::conditional_t<is_mixed, example::io_mixed<FFTY>, example::io<FFTY>>;\n        // Load data from global memory to registers\n        io_type::load(input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, reinterpret_cast<complex_type*>(shared_mem), workspace_y);\n\n        // Save results\n        io_type::store(thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if (blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        using dim_desc_type = example::dimension_description;\n        using io_type       = example::io_generic_strided<FFTX>;\n\n        // Load data from global memory to registers\n        // Both loads will convert the input if mixed\n        // precision is used\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_type::load_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                output, thread_data, reinterpret_cast<InputOutputType*>(shared_mem), local_fft_id);\n        } else {\n            io_type::load_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, reinterpret_cast<complex_type*>(shared_mem), workspace_x);\n\n        // Save results\n        // Both stores will convert the input if mixed\n        // precision is used\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_type::store_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                thread_data, reinterpret_cast<InputOutputType*>(shared_mem), output, local_fft_id);\n        } else {\n            io_type::store_strided<dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n                thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<typename InputOutputType>\nexample::fft_results<InputOutputType> cufft_mixed_fft_2d(const unsigned int warm_up_runs,\n                                                         const unsigned int kernel_runs,\n                                                         hipStream_t       stream,\n                                                         unsigned int       fft_size_x,\n                                                         unsigned int       fft_size_y,\n                                                         InputOutputType*   input,\n                                                         InputOutputType*   output) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(InputOutputType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<InputOutputType> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan, fft_size_x, fft_size_y, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t                 flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t                 flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<InputOutputType> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return {output_host, time / kernel_runs};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, typename InputOutputType>\nexample::fft_results<typename FFTX::value_type> cufftdx_mixed_fft_2d(const unsigned int warm_up_runs,\n                                                                     const unsigned int kernel_runs,\n                                                                     hipStream_t       stream,\n                                                                     InputOutputType*   input,\n                                                                     InputOutputType*   output) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    constexpr bool is_mixed = !std::is_same_v<InputOutputType, complex_type>;\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(InputOutputType),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(InputOutputType)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel     = (const void*)mixed_fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, InputOutputType>;\n    auto        error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[]             = {&input, &output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto mixed_fft_2d_execution = [&](hipStream_t stream) {\n        CUDA_CHECK_AND_EXIT(hipLaunchCooperativeKernel(reinterpret_cast<const void*>(kernel), grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    mixed_fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size = fft_size_x * fft_size_y;\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n\n    if constexpr (is_mixed) {\n        // If the data is of mixed precision first copy InputOutputPrecision data into host memory\n        std::vector<InputOutputType> output_mixed_host(flat_fft_size);\n        CUDA_CHECK_AND_EXIT(hipMemcpy(reinterpret_cast<void*>(output_mixed_host.data()), reinterpret_cast<void*>(output), flat_fft_size * sizeof(InputOutputType), hipMemcpyDeviceToHost));\n\n        // And then transform it to ComputePrecision and copy to the return vector created above\n        std::transform(begin(output_mixed_host), end(output_mixed_host), begin(output_host), [](const auto& v) {\n            return example::convert<complex_type>(v);\n        });\n    } else {\n        // If the data is of constant precision perform a simple copy into host memory\n        CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size * sizeof(complex_type), hipMemcpyDeviceToHost));\n        CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    }\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            mixed_fft_2d_execution(stream);\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Return results\n    return {output_host, time / kernel_runs};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel utilizing mixed precision storage\n// and computation using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch, unsigned int fft_size = 512>\nvoid mixed_fft_2d() {\n\n    // Choose mixed precision options\n    // Input and Output type\n    using io_precision = __half;\n    // Computation type\n    using compute_precision = float;\n    // Initialization happens to make sure this mixed configuration is supported\n    using mixed = example::mixed_precision<compute_precision, io_precision>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = fft_size;\n    static constexpr unsigned int fft_size_x = fft_size;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 8;\n    static constexpr unsigned int fpb_y = 8;\n    static constexpr unsigned int ept_x = 8;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same\n\n    static constexpr unsigned int warm_up_runs = 1;\n    static constexpr unsigned int kernel_runs  = 10;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<compute_precision>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Data types which will be used, based on previously choisen\n    // compute and input/output values\n    using complex_io_type      = typename mixed::complex_io_type;\n    using complex_compute_type = cufftdx::complex<compute_precision>;\n\n    // Host data\n    static constexpr size_t flat_fft_size          = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_compute_bytes = flat_fft_size * sizeof(complex_compute_type);\n    static constexpr size_t flat_fft_io_bytes      = flat_fft_size * sizeof(complex_io_type);\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    complex_io_type *     input_io, *output_io;\n    complex_compute_type *input_compute, *output_compute;\n\n    // Input/Output precision data buffers\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input_io, flat_fft_io_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output_io, flat_fft_io_bytes));\n\n    // Computation precision data buffers for comparison and verification\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input_compute, flat_fft_compute_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output_compute, flat_fft_compute_bytes));\n\n    // Host data\n    std::vector<complex_compute_type> single_fft_host_input =\n        example::get_random_complex_data<compute_precision>(flat_fft_size, -1, 1);\n    std::vector<complex_io_type> single_fft_host_mixed_input(flat_fft_size);\n\n    std::transform(begin(single_fft_host_input), end(single_fft_host_input), begin(single_fft_host_mixed_input), [](const auto& v) {\n        return example::convert<io_precision>(v);\n    });\n\n    hipMemcpy(reinterpret_cast<void*>(input_compute), single_fft_host_input.data(), flat_fft_compute_bytes, hipMemcpyHostToDevice);\n    hipMemcpy(reinterpret_cast<void*>(input_io), single_fft_host_mixed_input.data(), flat_fft_io_bytes, hipMemcpyHostToDevice);\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_mixed_results = cufftdx_mixed_fft_2d<fft_x, fft_y, false>(warm_up_runs, kernel_runs, stream, input_io, output_io);\n    auto cufftdx_full_results  = cufftdx_mixed_fft_2d<fft_x, fft_y, false>(warm_up_runs, kernel_runs, stream, input_compute, output_compute);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_mixed_smemio_results = cufftdx_mixed_fft_2d<fft_x, fft_y, true>(warm_up_runs, kernel_runs, stream, input_io, output_io);\n    auto cufftdx_full_smemio_results  = cufftdx_mixed_fft_2d<fft_x, fft_y, true>(warm_up_runs, kernel_runs, stream, input_compute, output_compute);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_mixed_fft_2d(warm_up_runs, kernel_runs, stream, fft_size_x, fft_size_y, input_compute, output_compute);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    // Verify Correctness\n    bool success          = true;\n    using result_vector_t = std::vector<complex_compute_type>;\n    std::cout << \"============\\nCorrectness results:\\n\";\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    };\n\n    verify_correctness(cufftdx_full_results.output, \"cuFFTDx Full\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_results.output, \"cuFFTDx Mixed\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_results.output, \"cuFFTDx Mixed\", cufftdx_full_results.output, \"cuFFTDx Full\");\n    verify_correctness(cufftdx_full_smemio_results.output, \"cuFFTDx Full Strided\", cufft_results.output, \"cuFFT\");\n    verify_correctness(cufftdx_mixed_smemio_results.output, \"cuFFTDx Mixed Strided\", cufft_results.output, \"cuFFT\");\n\n    // Report performance results\n    std::cout << \"============\\nPerformance results:\\n\";\n\n    auto report_time_and_performance = [&](std::string name, float avg_time) -> void {\n        double gflops = 1.0 * kernel_runs * 5.0 * flat_fft_size *\n                        (std::log(flat_fft_size) / std::log(2)) / (kernel_runs * avg_time) / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << avg_time << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << avg_time * kernel_runs << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / fft_y::ffts_per_block), (fft_size_x / fft_x::ffts_per_block)}), 1, 1};\n\n    std::cout << \"FFT size (X, Y): (\" << fft_size_x << \", \" << fft_size_y << \")\" << std::endl;\n    std::cout << \"CUDA grid: (X, Y): (\" << grid_size.x << \",\" << grid_size.y << \")\" << std::endl;\n\n    report_time_and_performance(\"cuFFT\", cufft_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision\", cufftdx_full_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision\", cufftdx_mixed_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Strided\", cufftdx_full_smemio_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Strided\", cufftdx_mixed_smemio_results.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success\" << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_io));\n    CUDA_CHECK_AND_EXIT(hipFree(input_compute));\n    CUDA_CHECK_AND_EXIT(hipFree(output_io));\n    CUDA_CHECK_AND_EXIT(hipFree(output_compute));\n}\n\ntemplate<unsigned int Arch>\nstruct mixed_fft_2d_functor {\n    void operator()() {\n        mixed_fft_2d<Arch, 512>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mixed_fft_2d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/08_mixed_precision/mixed_precision_fft_2d.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n#include <algorithm>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/mixed_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<typename FFT, typename Kernel>\nauto get_max_blocks_per_multiprocessor(unsigned int shared_size, Kernel kernel) {\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int bpm;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&bpm,\n                                                      kernel,\n                                                      FFT::block_dim.x * FFT::block_dim.y * FFT::block_dim.z,\n                                                      shared_size));\n    return bpm;\n}\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void simple_fft_1d_kernel(typename FFT::value_type* input,\n                                         typename FFT::value_type* output) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void simple_fft_1d_shared_kernel(typename FFT::value_type* input,\n                                                typename FFT::value_type* output) {\n    using complex_type = typename FFT::value_type;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Since each block has a defined number of FFTs to compute, in here we offset global memory start address\n    // as to get a pointer to batch group assigned to the current block\n    //\n    // sub_batch_address = global_start_address + size(single FFT) * (batches per block) * (block index)\n    auto this_block_input  = input + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n    auto this_block_output = output + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    example::io<FFT>::load_to_smem(this_block_input, reinterpret_cast<unsigned char*>(shared_mem));\n\n    FFT().execute(reinterpret_cast<void*>(shared_mem));\n\n    example::io<FFT>::store_from_smem(reinterpret_cast<unsigned char*>(shared_mem), this_block_output);\n}\n\ntemplate<typename FFT, typename InputOutputType>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void mixed_fft_1d_shared_kernel(const InputOutputType* input,\n                                               InputOutputType*       output) {\n    extern __shared__ __align__(alignof(float4)) typename FFT::value_type shared_mem[];\n\n    // Since each block has a defined number of FFTs to compute, in here we offset global memory start address\n    // as to get a pointer to batch group assigned to the current block\n    //\n    // sub_batch_address = global_start_address + size(single FFT) * (batches per block) * (block index)\n    auto this_block_input  = input + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n    auto this_block_output = output + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    // Load data from global memory to shared memory\n    // This load overload will perform a simple conversion\n    // from InputOutputPrecision into ComputePrecision\n    example::io_mixed<FFT>::load(this_block_input, shared_mem);\n\n    FFT().execute(shared_mem);\n\n    // Save results to global memory\n    // This store overload will perform a simple conversion\n    // from ComputePrecision into InputOutputPrecision\n    example::io_mixed<FFT>::store(shared_mem, this_block_output);\n}\n\ntemplate<typename FFT, typename InputOutputType>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void mixed_fft_1d_kernel(const InputOutputType* input,\n                                        InputOutputType*       output) {\n    using complex_type = typename FFT::value_type;\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    // This load overload will perform a simple conversion\n    // from InputOutputPrecision into ComputePrecision\n    example::io_mixed<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT in full precision\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results to global memory\n    // This store overload will perform a simple conversion\n    // from ComputePrecision into InputOutputPrecision\n    example::io_mixed<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<typename FFT>\nexample::fft_results<typename FFT::value_type> cufft_fft_1d(unsigned int cuda_blocks, cudaStream_t stream, unsigned int warm_up_runs, unsigned int kernel_runs, typename FFT::value_type* device_input, typename FFT::value_type* device_output) {\n    using cufft_complex_type = typename example::make_cufft_compatible<typename FFT::value_type>::type;\n\n    auto  size         = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto* cufft_input  = reinterpret_cast<cufft_complex_type*>(device_input);\n    auto* cufft_output = reinterpret_cast<cufft_complex_type*>(device_output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan, cufftdx::size_of<FFT>::value, std::is_same_v<cufft_complex_type, cufftComplex> ? CUFFT_C2C : CUFFT_Z2Z, cuda_blocks * FFT::ffts_per_block));\n\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    if constexpr (std::is_same_v<cufft_complex_type, cufftComplex>) {\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    } else if constexpr (std::is_same_v<cufft_complex_type, cufftDoubleComplex>) {\n        CUFFT_CHECK_AND_EXIT(cufftExecZ2Z(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    }\n\n    // Get initial results for correctness check\n    std::vector<typename FFT::value_type> host_output(size);\n    cudaMemcpy(reinterpret_cast<void*>(host_output.data()), reinterpret_cast<void*>(cufft_output), size * sizeof(typename FFT::value_type), cudaMemcpyDeviceToHost);\n\n\n    float time_cufft = example::measure_execution_ms(\n        [&]([[maybe_unused]] cudaStream_t stream) {\n            if constexpr (std::is_same_v<cufft_complex_type, cufftComplex>) {\n                CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n            } else if constexpr (std::is_same_v<cufft_complex_type, cufftDoubleComplex>) {\n                CUFFT_CHECK_AND_EXIT(cufftExecZ2Z(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n            }\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    return {host_output, time_cufft / kernel_runs};\n}\n\n\ntemplate<typename FFT, typename InputOutputType, typename FFTKernel>\nexample::fft_results<typename FFT::value_type> cufftdx_result_launcher(FFTKernel fk, unsigned int shared_size, unsigned int cuda_blocks, cudaStream_t stream, unsigned int warm_up_runs, unsigned int kernel_runs, InputOutputType* device_input, InputOutputType* device_output) {\n    using complex_compute_type = typename FFT::value_type;\n    constexpr bool is_mixed    = !std::is_same_v<typename FFT::value_type, InputOutputType>;\n\n    // Here size is number of elements per block need to be loaded\n    auto size = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        fk,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        shared_size));\n\n    // Execute kernel for correctness check\n    // Invokes cuFFTDx kernel passed as an argument with FFT::block_dim threads in CUDA block\n    fk<<<cuda_blocks, FFT::block_dim, shared_size, stream>>>(device_input, device_output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Create host result vector to be returned later\n    std::vector<complex_compute_type> host_output(size);\n\n    if constexpr (is_mixed) {\n        // If the data is of mixed precision first copy InputOutputPrecision data into host memory\n        auto                         size_io_bytes = size * sizeof(InputOutputType);\n        std::vector<InputOutputType> host_mixed_output(size);\n        CUDA_CHECK_AND_EXIT(cudaMemcpy(reinterpret_cast<void*>(host_mixed_output.data()), reinterpret_cast<void*>(device_output), size_io_bytes, cudaMemcpyDeviceToHost));\n\n        // And then transform it to ComputePrecision and copy to the return vector created above\n        std::transform(begin(host_mixed_output), end(host_mixed_output), begin(host_output), [](const auto& v) {\n            return example::convert<complex_compute_type>(v);\n        });\n    } else {\n        // If the data is of constant precision perform a simple copy into host memory\n        auto size_compute_bytes = size * sizeof(complex_compute_type);\n        CUDA_CHECK_AND_EXIT(cudaMemcpy(reinterpret_cast<void*>(host_output.data()), reinterpret_cast<void*>(device_output), size_compute_bytes, cudaMemcpyDeviceToHost));\n    }\n\n    // Run performance tests\n    float time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            fk<<<cuda_blocks, FFT::block_dim, shared_size, stream>>>(device_input, device_output);\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    return {host_output, time / kernel_runs};\n}\n\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// The example calculates FFT using: cuFFT (for reference), cuFFTDx, and cuFFTDx with different\n// precisions for data and computations (aka mixed precision). Additionally, both register and\n// shared APIs of cuFFTDx are tested.\n// At the end the performance and accuracy results of each solution are presented.\ntemplate<unsigned int Arch, unsigned int fft_size = 512>\nvoid mixed_precision_fft_1d() {\n    using namespace cufftdx;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Choose mixed precision options\n    // Input and Output type\n    using io_precision = float;\n    // Computation type\n    using compute_precision = double;\n    // Initialization happens to make sure this mixed configuration is supported\n    using mixed = example::mixed_precision<compute_precision, io_precision>;\n\n    // Performance testing arguments\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int warm_up_runs             = 1;\n    static constexpr unsigned int kernel_runs              = 10;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using fft_base = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<compute_precision>() + SM<Arch>());\n    // cuFFTDx performance suggestions are used, as this example also tests execution times\n    using FFT = decltype(fft_base() + ElementsPerThread<fft_base::elements_per_thread>() + FFTsPerBlock<fft_base::ffts_per_block>());\n\n    // Data types which will be used, based on previously choisen\n    // compute and input/output values\n    using complex_io_type      = typename mixed::complex_io_type;\n    using complex_compute_type = typename FFT::value_type;\n\n    // Shared memory byte count necessary per block for shared API of the above defined FFT computation\n    constexpr auto max_smem = std::max<unsigned int>(FFT::shared_memory_size, FFT::ffts_per_block * fft_size * sizeof(complex_compute_type));\n    // Choose the configuration such that the entire GPU will be saturated\n    // necessary for reliable performance testing\n    const auto blocks_per_multiprocessor = std::max<int>({get_max_blocks_per_multiprocessor<FFT>(FFT::shared_memory_size, &mixed_fft_1d_kernel<FFT, complex_io_type>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(max_smem, &mixed_fft_1d_shared_kernel<FFT, complex_io_type>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(FFT::shared_memory_size, &simple_fft_1d_kernel<FFT>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(max_smem, &simple_fft_1d_shared_kernel<FFT>)});\n    if (blocks_per_multiprocessor < 1 ) {\n        std::cout << \"============\\nNot enough resources for FFT of size: \" << fft_size << \"\\n\";\n        return;\n    }\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * FFT::ffts_per_block * fft_size * sizeof(complex_io_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks = device_blocks * executed_blocks_multiplyer;\n\n    // Memory Allocation and copies\n\n    // size is entire input length, not FFT size\n    const auto single_fft_size               = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto       single_fft_size_io_bytes      = single_fft_size * sizeof(complex_io_type);\n    auto       single_fft_size_compute_bytes = single_fft_size * sizeof(complex_compute_type);\n\n    const auto size               = single_fft_size * cuda_blocks;\n    auto       size_io_bytes      = size * sizeof(complex_io_type);\n    auto       size_compute_bytes = size * sizeof(complex_compute_type);\n\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    complex_io_type *     input_io, *output_io;\n    complex_compute_type *input_compute, *output_compute;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input_io, size_io_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output_io, size_io_bytes));\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input_compute, size_compute_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output_compute, size_compute_bytes));\n\n    // Host data\n    std::vector<complex_compute_type> single_fft_host_input =\n        example::get_random_complex_data<compute_precision>(single_fft_size, -1, 1);\n    std::vector<complex_io_type> single_fft_host_mixed_input(single_fft_size);\n\n    std::transform(begin(single_fft_host_input), end(single_fft_host_input), begin(single_fft_host_mixed_input), [](const auto& v) {\n        return example::convert<io_precision>(v);\n    });\n\n    // Copy to device\n    for (unsigned int i = 0; i < cuda_blocks; ++i) {\n        cudaMemcpy(reinterpret_cast<void*>(input_compute + i * single_fft_size), single_fft_host_input.data(), single_fft_size_compute_bytes, cudaMemcpyHostToDevice);\n        cudaMemcpy(reinterpret_cast<void*>(input_io + i * single_fft_size), single_fft_host_mixed_input.data(), single_fft_size_io_bytes, cudaMemcpyHostToDevice);\n    }\n\n    // Perform reference cuFFT host API computation\n    const auto cufft_results = cufft_fft_1d<FFT>(cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n\n    // Launch cuFFTDx kernels with both mixed and full precision\n    const auto mixed_results = cufftdx_result_launcher<FFT>(\n        mixed_fft_1d_kernel<FFT, complex_io_type>, FFT::shared_memory_size, cuda_blocks, stream, warm_up_runs, kernel_runs, input_io, output_io);\n    const auto mixed_shared_results = cufftdx_result_launcher<FFT>(\n        mixed_fft_1d_shared_kernel<FFT, complex_io_type>, max_smem, cuda_blocks, stream, warm_up_runs, kernel_runs, input_io, output_io);\n    const auto simple_results = cufftdx_result_launcher<FFT>(\n        simple_fft_1d_kernel<FFT>, FFT::shared_memory_size, cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n    const auto simple_shared_results = cufftdx_result_launcher<FFT>(\n        simple_fft_1d_shared_kernel<FFT>, max_smem, cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n\n    // Verify Correctness\n    bool success          = true;\n    using result_vector_t = std::vector<complex_compute_type>;\n    std::cout << \"============\\nCorrectness results:\\n\";\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    };\n\n    verify_correctness(mixed_results.output, \"cuFFTDx Mixed Register API\", cufft_results.output, \"cuFFT\");\n    verify_correctness(mixed_shared_results.output, \"cuFFTDx Mixed Shared API\", cufft_results.output, \"cuFFT\");\n    verify_correctness(mixed_results.output, \"cuFFTDx Mixed Precision\", simple_results.output, \"cuFFTDx Full Precision\");\n    verify_correctness(mixed_shared_results.output, \"cuFFTDx Mixed Precision Shared\", simple_shared_results.output, \"cuFFTDx Full Precision Shared\");\n\n    // Report performance results\n    std::cout << \"============\\nPerformance results:\\n\";\n\n    auto report_time_and_performance = [&](std::string name, float avg_time) -> void {\n        double gflops = 1.0 * kernel_runs * FFT::ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                        (std::log(fft_size) / std::log(2)) / (avg_time * kernel_runs) / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << avg_time << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << kernel_runs * avg_time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n\n    std::cout << \"FFT size: \" << fft_size << std::endl;\n    std::cout << \"FFTs run: \" << FFT::ffts_per_block * cuda_blocks << std::endl;\n    std::cout << \"FFTs elements per thread: \" << FFT::elements_per_thread << std::endl;\n    std::cout << \"FFTs per block: \" << FFT::ffts_per_block << std::endl;\n    std::cout << \"CUDA blocks: \" << cuda_blocks << std::endl;\n    std::cout << \"Blocks per multiprocessor: \" << blocks_per_multiprocessor << std::endl;\n\n    report_time_and_performance(\"cuFFT\", cufft_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision\", mixed_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision Shared\", mixed_shared_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision\", simple_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision Shared\", simple_shared_results.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success\" << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_io));\n    CUDA_CHECK_AND_EXIT(cudaFree(input_compute));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_io));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_compute));\n}\n\ntemplate<unsigned int Arch>\nstruct mixed_precision_fft_1d_functor {\n    void operator()() {\n\n        mixed_precision_fft_1d<Arch,128>();\n        mixed_precision_fft_1d<Arch,512>();\n        mixed_precision_fft_1d<Arch,2048>();\n        mixed_precision_fft_1d<Arch,4096>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mixed_precision_fft_1d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <algorithm>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/mixed_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<typename FFT, typename Kernel>\nauto get_max_blocks_per_multiprocessor(unsigned int shared_size, Kernel kernel) {\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int bpm;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&bpm,\n                                                      kernel,\n                                                      FFT::block_dim.x * FFT::block_dim.y * FFT::block_dim.z,\n                                                      shared_size));\n    return bpm;\n}\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void simple_fft_1d_kernel(typename FFT::value_type* input,\n                                         typename FFT::value_type* output) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void simple_fft_1d_shared_kernel(typename FFT::value_type* input,\n                                                typename FFT::value_type* output) {\n    using complex_type = typename FFT::value_type;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Since each block has a defined number of FFTs to compute, in here we offset global memory start address\n    // as to get a pointer to batch group assigned to the current block\n    //\n    // sub_batch_address = global_start_address + size(single FFT) * (batches per block) * (block index)\n    auto this_block_input  = input + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n    auto this_block_output = output + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    example::io<FFT>::load_to_smem(this_block_input, reinterpret_cast<unsigned char*>(shared_mem));\n\n    FFT().execute(reinterpret_cast<void*>(shared_mem));\n\n    example::io<FFT>::store_from_smem(reinterpret_cast<unsigned char*>(shared_mem), this_block_output);\n}\n\ntemplate<typename FFT, typename InputOutputType>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void mixed_fft_1d_shared_kernel(const InputOutputType* input,\n                                               InputOutputType*       output) {\n    extern __shared__ __align__(alignof(float4)) typename FFT::value_type shared_mem[];\n\n    // Since each block has a defined number of FFTs to compute, in here we offset global memory start address\n    // as to get a pointer to batch group assigned to the current block\n    //\n    // sub_batch_address = global_start_address + size(single FFT) * (batches per block) * (block index)\n    auto this_block_input  = input + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n    auto this_block_output = output + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    // Load data from global memory to shared memory\n    // This load overload will perform a simple conversion\n    // from InputOutputPrecision into ComputePrecision\n    example::io_mixed<FFT>::load(this_block_input, shared_mem);\n\n    FFT().execute(shared_mem);\n\n    // Save results to global memory\n    // This store overload will perform a simple conversion\n    // from ComputePrecision into InputOutputPrecision\n    example::io_mixed<FFT>::store(shared_mem, this_block_output);\n}\n\ntemplate<typename FFT, typename InputOutputType>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void mixed_fft_1d_kernel(const InputOutputType* input,\n                                        InputOutputType*       output) {\n    using complex_type = typename FFT::value_type;\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    // This load overload will perform a simple conversion\n    // from InputOutputPrecision into ComputePrecision\n    example::io_mixed<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT in full precision\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results to global memory\n    // This store overload will perform a simple conversion\n    // from ComputePrecision into InputOutputPrecision\n    example::io_mixed<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<typename FFT>\nexample::fft_results<typename FFT::value_type> cufft_fft_1d(unsigned int cuda_blocks, hipStream_t stream, unsigned int warm_up_runs, unsigned int kernel_runs, typename FFT::value_type* device_input, typename FFT::value_type* device_output) {\n    using cufft_complex_type = typename example::make_cufft_compatible<typename FFT::value_type>::type;\n\n    auto  size         = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto* cufft_input  = reinterpret_cast<cufft_complex_type*>(device_input);\n    auto* cufft_output = reinterpret_cast<cufft_complex_type*>(device_output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan, cufftdx::size_of<FFT>::value, std::is_same_v<cufft_complex_type, hipfftComplex> ? HIPFFT_C2C : HIPFFT_Z2Z, cuda_blocks * FFT::ffts_per_block));\n\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    if constexpr (std::is_same_v<cufft_complex_type, hipfftComplex>) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    } else if constexpr (std::is_same_v<cufft_complex_type, hipfftDoubleComplex>) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecZ2Z(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    }\n\n    // Get initial results for correctness check\n    std::vector<typename FFT::value_type> host_output(size);\n    hipMemcpy(reinterpret_cast<void*>(host_output.data()), reinterpret_cast<void*>(cufft_output), size * sizeof(typename FFT::value_type), hipMemcpyDeviceToHost);\n\n\n    float time_cufft = example::measure_execution_ms(\n        [&]([[maybe_unused]] hipStream_t stream) {\n            if constexpr (std::is_same_v<cufft_complex_type, hipfftComplex>) {\n                CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n            } else if constexpr (std::is_same_v<cufft_complex_type, hipfftDoubleComplex>) {\n                CUFFT_CHECK_AND_EXIT(hipfftExecZ2Z(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n            }\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    return {host_output, time_cufft / kernel_runs};\n}\n\n\ntemplate<typename FFT, typename InputOutputType, typename FFTKernel>\nexample::fft_results<typename FFT::value_type> cufftdx_result_launcher(FFTKernel fk, unsigned int shared_size, unsigned int cuda_blocks, hipStream_t stream, unsigned int warm_up_runs, unsigned int kernel_runs, InputOutputType* device_input, InputOutputType* device_output) {\n    using complex_compute_type = typename FFT::value_type;\n    constexpr bool is_mixed    = !std::is_same_v<typename FFT::value_type, InputOutputType>;\n\n    // Here size is number of elements per block need to be loaded\n    auto size = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(fk),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        shared_size));\n\n    // Execute kernel for correctness check\n    // Invokes cuFFTDx kernel passed as an argument with FFT::block_dim threads in CUDA block\n    fk<<<cuda_blocks, FFT::block_dim, shared_size, stream>>>(device_input, device_output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Create host result vector to be returned later\n    std::vector<complex_compute_type> host_output(size);\n\n    if constexpr (is_mixed) {\n        // If the data is of mixed precision first copy InputOutputPrecision data into host memory\n        auto                         size_io_bytes = size * sizeof(InputOutputType);\n        std::vector<InputOutputType> host_mixed_output(size);\n        CUDA_CHECK_AND_EXIT(hipMemcpy(reinterpret_cast<void*>(host_mixed_output.data()), reinterpret_cast<void*>(device_output), size_io_bytes, hipMemcpyDeviceToHost));\n\n        // And then transform it to ComputePrecision and copy to the return vector created above\n        std::transform(begin(host_mixed_output), end(host_mixed_output), begin(host_output), [](const auto& v) {\n            return example::convert<complex_compute_type>(v);\n        });\n    } else {\n        // If the data is of constant precision perform a simple copy into host memory\n        auto size_compute_bytes = size * sizeof(complex_compute_type);\n        CUDA_CHECK_AND_EXIT(hipMemcpy(reinterpret_cast<void*>(host_output.data()), reinterpret_cast<void*>(device_output), size_compute_bytes, hipMemcpyDeviceToHost));\n    }\n\n    // Run performance tests\n    float time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            fk<<<cuda_blocks, FFT::block_dim, shared_size, stream>>>(device_input, device_output);\n        },\n        warm_up_runs,\n        kernel_runs,\n        stream);\n\n    return {host_output, time / kernel_runs};\n}\n\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// The example calculates FFT using: cuFFT (for reference), cuFFTDx, and cuFFTDx with different\n// precisions for data and computations (aka mixed precision). Additionally, both register and\n// shared APIs of cuFFTDx are tested.\n// At the end the performance and accuracy results of each solution are presented.\ntemplate<unsigned int Arch, unsigned int fft_size = 512>\nvoid mixed_precision_fft_1d() {\n    using namespace cufftdx;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Choose mixed precision options\n    // Input and Output type\n    using io_precision = float;\n    // Computation type\n    using compute_precision = double;\n    // Initialization happens to make sure this mixed configuration is supported\n    using mixed = example::mixed_precision<compute_precision, io_precision>;\n\n    // Performance testing arguments\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int warm_up_runs             = 1;\n    static constexpr unsigned int kernel_runs              = 10;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using fft_base = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<compute_precision>() + SM<Arch>());\n    // cuFFTDx performance suggestions are used, as this example also tests execution times\n    using FFT = decltype(fft_base() + ElementsPerThread<fft_base::elements_per_thread>() + FFTsPerBlock<fft_base::ffts_per_block>());\n\n    // Data types which will be used, based on previously choisen\n    // compute and input/output values\n    using complex_io_type      = typename mixed::complex_io_type;\n    using complex_compute_type = typename FFT::value_type;\n\n    // Shared memory byte count necessary per block for shared API of the above defined FFT computation\n    constexpr auto max_smem = std::max<unsigned int>(FFT::shared_memory_size, FFT::ffts_per_block * fft_size * sizeof(complex_compute_type));\n    // Choose the configuration such that the entire GPU will be saturated\n    // necessary for reliable performance testing\n    const auto blocks_per_multiprocessor = std::max<int>({get_max_blocks_per_multiprocessor<FFT>(FFT::shared_memory_size, &mixed_fft_1d_kernel<FFT, complex_io_type>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(max_smem, &mixed_fft_1d_shared_kernel<FFT, complex_io_type>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(FFT::shared_memory_size, &simple_fft_1d_kernel<FFT>),\n                                                          get_max_blocks_per_multiprocessor<FFT>(max_smem, &simple_fft_1d_shared_kernel<FFT>)});\n    if (blocks_per_multiprocessor < 1 ) {\n        std::cout << \"============\\nNot enough resources for FFT of size: \" << fft_size << \"\\n\";\n        return;\n    }\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * FFT::ffts_per_block * fft_size * sizeof(complex_io_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks = device_blocks * executed_blocks_multiplyer;\n\n    // Memory Allocation and copies\n\n    // size is entire input length, not FFT size\n    const auto single_fft_size               = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto       single_fft_size_io_bytes      = single_fft_size * sizeof(complex_io_type);\n    auto       single_fft_size_compute_bytes = single_fft_size * sizeof(complex_compute_type);\n\n    const auto size               = single_fft_size * cuda_blocks;\n    auto       size_io_bytes      = size * sizeof(complex_io_type);\n    auto       size_compute_bytes = size * sizeof(complex_compute_type);\n\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    complex_io_type *     input_io, *output_io;\n    complex_compute_type *input_compute, *output_compute;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input_io, size_io_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output_io, size_io_bytes));\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input_compute, size_compute_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output_compute, size_compute_bytes));\n\n    // Host data\n    std::vector<complex_compute_type> single_fft_host_input =\n        example::get_random_complex_data<compute_precision>(single_fft_size, -1, 1);\n    std::vector<complex_io_type> single_fft_host_mixed_input(single_fft_size);\n\n    std::transform(begin(single_fft_host_input), end(single_fft_host_input), begin(single_fft_host_mixed_input), [](const auto& v) {\n        return example::convert<io_precision>(v);\n    });\n\n    // Copy to device\n    for (unsigned int i = 0; i < cuda_blocks; ++i) {\n        hipMemcpy(reinterpret_cast<void*>(input_compute + i * single_fft_size), single_fft_host_input.data(), single_fft_size_compute_bytes, hipMemcpyHostToDevice);\n        hipMemcpy(reinterpret_cast<void*>(input_io + i * single_fft_size), single_fft_host_mixed_input.data(), single_fft_size_io_bytes, hipMemcpyHostToDevice);\n    }\n\n    // Perform reference cuFFT host API computation\n    const auto cufft_results = cufft_fft_1d<FFT>(cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n\n    // Launch cuFFTDx kernels with both mixed and full precision\n    const auto mixed_results = cufftdx_result_launcher<FFT>(\n        mixed_fft_1d_kernel<FFT, complex_io_type>, FFT::shared_memory_size, cuda_blocks, stream, warm_up_runs, kernel_runs, input_io, output_io);\n    const auto mixed_shared_results = cufftdx_result_launcher<FFT>(\n        mixed_fft_1d_shared_kernel<FFT, complex_io_type>, max_smem, cuda_blocks, stream, warm_up_runs, kernel_runs, input_io, output_io);\n    const auto simple_results = cufftdx_result_launcher<FFT>(\n        simple_fft_1d_kernel<FFT>, FFT::shared_memory_size, cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n    const auto simple_shared_results = cufftdx_result_launcher<FFT>(\n        simple_fft_1d_shared_kernel<FFT>, max_smem, cuda_blocks, stream, warm_up_runs, kernel_runs, input_compute, output_compute);\n\n    // Verify Correctness\n    bool success          = true;\n    using result_vector_t = std::vector<complex_compute_type>;\n    std::cout << \"============\\nCorrectness results:\\n\";\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    };\n\n    verify_correctness(mixed_results.output, \"cuFFTDx Mixed Register API\", cufft_results.output, \"cuFFT\");\n    verify_correctness(mixed_shared_results.output, \"cuFFTDx Mixed Shared API\", cufft_results.output, \"cuFFT\");\n    verify_correctness(mixed_results.output, \"cuFFTDx Mixed Precision\", simple_results.output, \"cuFFTDx Full Precision\");\n    verify_correctness(mixed_shared_results.output, \"cuFFTDx Mixed Precision Shared\", simple_shared_results.output, \"cuFFTDx Full Precision Shared\");\n\n    // Report performance results\n    std::cout << \"============\\nPerformance results:\\n\";\n\n    auto report_time_and_performance = [&](std::string name, float avg_time) -> void {\n        double gflops = 1.0 * kernel_runs * FFT::ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                        (std::log(fft_size) / std::log(2)) / (avg_time * kernel_runs) / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << avg_time << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << kernel_runs * avg_time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n\n    std::cout << \"FFT size: \" << fft_size << std::endl;\n    std::cout << \"FFTs run: \" << FFT::ffts_per_block * cuda_blocks << std::endl;\n    std::cout << \"FFTs elements per thread: \" << FFT::elements_per_thread << std::endl;\n    std::cout << \"FFTs per block: \" << FFT::ffts_per_block << std::endl;\n    std::cout << \"CUDA blocks: \" << cuda_blocks << std::endl;\n    std::cout << \"Blocks per multiprocessor: \" << blocks_per_multiprocessor << std::endl;\n\n    report_time_and_performance(\"cuFFT\", cufft_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision\", mixed_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Mixed Precision Shared\", mixed_shared_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision\", simple_results.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Full Precision Shared\", simple_shared_results.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success\" << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_io));\n    CUDA_CHECK_AND_EXIT(hipFree(input_compute));\n    CUDA_CHECK_AND_EXIT(hipFree(output_io));\n    CUDA_CHECK_AND_EXIT(hipFree(output_compute));\n}\n\ntemplate<unsigned int Arch>\nstruct mixed_precision_fft_1d_functor {\n    void operator()() {\n\n        mixed_precision_fft_1d<Arch,128>();\n        mixed_precision_fft_1d<Arch,512>();\n        mixed_precision_fft_1d<Arch,2048>();\n        mixed_precision_fft_1d<Arch,4096>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mixed_precision_fft_1d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/08_mixed_precision/mixed_precision_fft_1d.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <nvrtc.h>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n#include <cuComplex.h>\n\n#define CUFFTDX_ENABLE_RUNTIME_DATABASE\n#include <cufftdx/utils.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* test_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_desc = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_desc() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>());\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\nint main(int, char**) {\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(cudaGetDevice(&current_device));\n\n    // Note that FFT description is only defined in kernel\n    // Precision: double\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    // Complex type according to precision\n    using value_type = cuDoubleComplex; // or double2, or cuda::std::complex<double>\n\n    //query the database to get optimal block configuration and shared memory size\n    auto result = cufftdx::experimental::utils::query_database(fft_size,\n                                                 cufftdx::fft_direction::inverse,\n                                                 cufftdx::fft_type::c2c,\n                                                 example::nvrtc::get_device_architecture(current_device) * 10,\n                                                 cufftdx::detail::execution_type::block,\n                                                 cufftdx::precision::f64,\n                                                 fft_ept);\n\n    // Assign block configuration and shared memory size\n    dim3     fft_block_dim {};\n    unsigned shared_memory_size;\n    if (result.has_value()) {\n        fft_block_dim      = dim3(result.value().block_dim_x, result.value().block_dim_y, result.value().block_dim_z);\n        shared_memory_size = result.value().shared_memory_size;\n    } else {\n        std::cout << \"No database result found\" << std::endl;\n        exit(1);\n    }\n\n\n    nvrtcProgram program;\n    NVRTC_SAFE_CALL(nvrtcCreateProgram(&program,         // program\n                                       test_kernel,      // buffer\n                                       \"test_kernel.cu\", // name\n                                       0,                // numHeaders\n                                       NULL,             // headers\n                                       NULL));           // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"};\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Example how to pass part of FFT description to NVRTC kernel\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(gpu_architecture_option.c_str());\n\n    nvrtcResult compileResult = nvrtcCompileProgram(program,                       // program\n                                                    static_cast<int>(opts.size()), // numOptions\n                                                    opts.data());                  // options\n\n    // Obtain compilation log from the program\n    if (compileResult != NVRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(nvrtcGetCUBINSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(nvrtcGetCUBIN(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(nvrtcDestroyProgram(&program));\n\n    // Load the generated Cubin and get a handle to the test_kernel\n    CUdevice   cuDevice;\n    CUcontext  context;\n    CUmodule   module;\n    CUfunction kernel;\n    CU_CHECK_AND_EXIT(cuInit(0));\n    CU_CHECK_AND_EXIT(cuDeviceGet(&cuDevice, current_device));\n\n#if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n    CU_CHECK_AND_EXIT(cuCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n#else\n    CU_CHECK_AND_EXIT(cuCtxCreate(&context, 0, cuDevice));\n#endif\n\n    CU_CHECK_AND_EXIT(cuModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(cuModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&fft_data, fft_size*sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Print fft_data\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Increase dynamic shared memory limit\n    CU_CHECK_AND_EXIT(cuFuncSetAttribute(kernel, CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, shared_memory_size));\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(cuLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     fft_block_dim.x, // number of threads\n                                     fft_block_dim.y,\n                                     1,\n                                     shared_memory_size,\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(cuCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[nvrtc_fft_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n        CU_CHECK_AND_EXIT(cuModuleUnload(module));\n        CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[nvrtc_fft_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n    CU_CHECK_AND_EXIT(cuModuleUnload(module));\n    CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <hip/hiprtc.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_complex.h>\n\n#define CUFFTDX_ENABLE_RUNTIME_DATABASE\n#include <cufftdx/utils.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* test_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_desc = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_desc() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>());\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\nint main(int, char**) {\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(hipGetDevice(&current_device));\n\n    // Note that FFT description is only defined in kernel\n    // Precision: double\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    // Complex type according to precision\n    using value_type = hipDoubleComplex; // or double2, or cuda::std::complex<double>\n\n    //query the database to get optimal block configuration and shared memory size\n    auto result = cufftdx::experimental::utils::query_database(fft_size,\n                                                 cufftdx::fft_direction::inverse,\n                                                 cufftdx::fft_type::c2c,\n                                                 example::nvrtc::get_device_architecture(current_device) * 10,\n                                                 cufftdx::detail::execution_type::block,\n                                                 cufftdx::precision::f64,\n                                                 fft_ept);\n\n    // Assign block configuration and shared memory size\n    dim3     fft_block_dim {};\n    unsigned shared_memory_size;\n    if (result.has_value()) {\n        fft_block_dim      = dim3(result.value().block_dim_x, result.value().block_dim_y, result.value().block_dim_z);\n        shared_memory_size = result.value().shared_memory_size;\n    } else {\n        std::cout << \"No database result found\" << std::endl;\n        exit(1);\n    }\n\n\n    hiprtcProgram program;\n    NVRTC_SAFE_CALL(hiprtcCreateProgram(&program,         // program\n                                       test_kernel,      // buffer\n                                       \"test_kernel.cu\", // name\n                                       0,                // numHeaders\n                                       NULL,             // headers\n                                       NULL));           // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"};\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Example how to pass part of FFT description to NVRTC kernel\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(gpu_architecture_option.c_str());\n\n    hiprtcResult compileResult = hiprtcCompileProgram(program,                       // program\n                                                    static_cast<int>(opts.size()), // numOptions\n                                                    opts.data());                  // options\n\n    // Obtain compilation log from the program\n    if (compileResult != HIPRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(hiprtcGetBitcodeSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(hiprtcGetBitcode(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(hiprtcDestroyProgram(&program));\n\n    // Load the generated Cubin and get a handle to the test_kernel\n    hipDevice_t   cuDevice;\n    hipCtx_t  context;\n    hipModule_t   module;\n    hipFunction_t kernel;\n    CU_CHECK_AND_EXIT(hipInit(0));\n    CU_CHECK_AND_EXIT(hipDeviceGet(&cuDevice, current_device));\n\n#if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n    CU_CHECK_AND_EXIT(hipCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n#else\n    CU_CHECK_AND_EXIT(hipCtxCreate(&context, 0, cuDevice));\n#endif\n\n    CU_CHECK_AND_EXIT(hipModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(hipModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&fft_data, fft_size*sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Print fft_data\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Increase dynamic shared memory limit\n    CU_CHECK_AND_EXIT(cuFuncSetAttribute(kernel, HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, shared_memory_size));\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(hipModuleLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     fft_block_dim.x, // number of threads\n                                     fft_block_dim.y,\n                                     1,\n                                     shared_memory_size,\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(hipCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[nvrtc_fft_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n        CU_CHECK_AND_EXIT(hipModuleUnload(module));\n        CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[nvrtc_fft_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n    CU_CHECK_AND_EXIT(hipModuleUnload(module));\n    CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/04_nvrtc_fft/nvrtc_query_database_fft_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <nvrtc.h>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* thread_fft_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT\nusing size_desc  = Size<FFT_SIZE>;\nusing dir_desc   = Direction<fft_direction::inverse>;\nusing type_desc  = Type<fft_type::c2c>;\nusing FFT        = decltype(size_desc() + dir_desc() + type_desc() + Thread() + Precision<double>());\n\nextern \"C\" __global__ void thread_fft_kernel(typename FFT::value_type *data)\n{\n    // Local array for thread\n    typename FFT::value_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all fft_data data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n)kernel\";\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 16;\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(cudaGetDevice(&current_device));\n\n    // Get architecture of current device\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n\n    // Create a program\n    nvrtcProgram program;\n    NVRTC_SAFE_CALL(nvrtcCreateProgram(&program,               // program\n                                       thread_fft_kernel,      // buffer\n                                       \"thread_fft_kernel.cu\", // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n    // Add FFT_SIZE definition\n    opts.push_back(fft_size_definition.c_str());\n    // Add gpu-architecture flag\n    opts.push_back(gpu_architecture_option.c_str());\n\n    nvrtcResult compileResult = nvrtcCompileProgram(program,      // program\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n\n    // Obtain compilation log from the program\n    if (compileResult != NVRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(nvrtcGetCUBINSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(nvrtcGetCUBIN(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(nvrtcDestroyProgram(&program));\n\n    // Load the generated PTX and get a handle to the thread_fft_kernel\n    CUdevice   cuDevice;\n    CUcontext  context;\n    CUmodule   module;\n    CUfunction kernel;\n    CU_CHECK_AND_EXIT(cuInit(0));\n    CU_CHECK_AND_EXIT(cuDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(cuModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(cuModuleGetFunction(&kernel, module, \"thread_fft_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute thread_fft_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(cuLaunchKernel(kernel,\n                                       1, // number of blocks\n                                       1,\n                                       1,\n                                       1, // number of threads\n                                       1,\n                                       1,\n                                       0,    // no shared memory\n                                       NULL, // NULL stream\n                                       args,\n                                       0));\n    CU_CHECK_AND_EXIT(cuCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double expected_value = (fft_size * (fft_size - 1)) / 2;\n    if (std::abs(fft_data[0].x - expected_value) > 0.01) {\n        std::cout << \"Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n        CU_CHECK_AND_EXIT(cuModuleUnload(module));\n        return 1;\n    }\n    std::cout << \"Success\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n    CU_CHECK_AND_EXIT(cuModuleUnload(module));\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <hip/hiprtc.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* thread_fft_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT\nusing size_desc  = Size<FFT_SIZE>;\nusing dir_desc   = Direction<fft_direction::inverse>;\nusing type_desc  = Type<fft_type::c2c>;\nusing FFT        = decltype(size_desc() + dir_desc() + type_desc() + Thread() + Precision<double>());\n\nextern \"C\" __global__ void thread_fft_kernel(typename FFT::value_type *data)\n{\n    // Local array for thread\n    typename FFT::value_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all fft_data data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n)kernel\";\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 16;\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(hipGetDevice(&current_device));\n\n    // Get architecture of current device\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n\n    // Create a program\n    hiprtcProgram program;\n    NVRTC_SAFE_CALL(hiprtcCreateProgram(&program,               // program\n                                       thread_fft_kernel,      // buffer\n                                       \"thread_fft_kernel.cu\", // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n    // Add FFT_SIZE definition\n    opts.push_back(fft_size_definition.c_str());\n    // Add gpu-architecture flag\n    opts.push_back(gpu_architecture_option.c_str());\n\n    hiprtcResult compileResult = hiprtcCompileProgram(program,      // program\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n\n    // Obtain compilation log from the program\n    if (compileResult != HIPRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(hiprtcGetBitcodeSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(hiprtcGetBitcode(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(hiprtcDestroyProgram(&program));\n\n    // Load the generated PTX and get a handle to the thread_fft_kernel\n    hipDevice_t   cuDevice;\n    hipCtx_t  context;\n    hipModule_t   module;\n    hipFunction_t kernel;\n    CU_CHECK_AND_EXIT(hipInit(0));\n    CU_CHECK_AND_EXIT(hipDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(hipModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(hipModuleGetFunction(&kernel, module, \"thread_fft_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute thread_fft_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(hipModuleLaunchKernel(kernel,\n                                       1, // number of blocks\n                                       1,\n                                       1,\n                                       1, // number of threads\n                                       1,\n                                       1,\n                                       0,    // no shared memory\n                                       NULL, // NULL stream\n                                       args,\n                                       0));\n    CU_CHECK_AND_EXIT(hipCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double expected_value = (fft_size * (fft_size - 1)) / 2;\n    if (std::abs(fft_data[0].x - expected_value) > 0.01) {\n        std::cout << \"Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n        CU_CHECK_AND_EXIT(hipModuleUnload(module));\n        return 1;\n    }\n    std::cout << \"Success\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n    CU_CHECK_AND_EXIT(hipModuleUnload(module));\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/04_nvrtc_fft/nvrtc_fft_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <chrono>\n\n#include <nvrtc.h>\n#include <nvJitLink.h>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n#include \"../common/common_nvjitlink.hpp\"\n\nconst char* test_kernel = R\"kernel(\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_c2c  = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_c2c() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>() + experimental::CodeType<experimental::code_type::ltoir>());\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(cudaGetDevice(&current_device));\n\n    if (!cufftdx::utils::check_cufft_device_api_version()) {\n        return 1;\n    }\n\n    auto [lto_db, ltoirs, block_dim, shared_memory_size] =\n        cufftdx::utils::get_database_and_ltoir(fft_size,\n                                               cufftdx::fft_direction::inverse,\n                                               cufftdx::fft_type::c2c,\n                                               example::nvrtc::get_device_architecture(current_device) * 10,\n                                               cufftdx::utils::execution_type::block,\n                                               cufftdx::precision::f64,\n                                               cufftdx::complex_layout::natural,\n                                               cufftdx::real_mode::normal,\n                                               fft_ept);\n\n    if (ltoirs.size() == 0 || lto_db.empty()) {\n        std::cout << \"LTOIR or database from cuFFT is empty. Exit.\\n\";\n        return 1;\n    }\n\n    // Build CUDA source code. Plug in cuFFT-dumped database file after cufftdx.hpp inclusion.\n    std::string dx_source_code;\n    dx_source_code.append(\"#include <cufftdx.hpp>\\n\");\n    dx_source_code.append(lto_db);\n    dx_source_code.append(test_kernel);\n\n    // Create a program\n    nvrtcProgram prog;\n    NVRTC_SAFE_CALL(nvrtcCreateProgram(&prog,                  // prog\n                                       dx_source_code.c_str(), // buffer\n                                       \"test_kernel.cu\",       // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string nvrtc_gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(nvrtc_gpu_architecture_option.c_str());\n\n    // Add flags for LTO compilation\n    opts.push_back(\"-dlto\");\n    opts.push_back(\"--relocatable-device-code=true\");\n\n    const auto compile_start = std::chrono::high_resolution_clock::now();\n    nvrtcResult compileResult = nvrtcCompileProgram(prog,         // prog\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n    std::cout << \"NVRTC compile time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - compile_start).count() / 1e3 << \"\\n\";\n\n    // Obtain compilation log from the program\n    if (compileResult != NVRTC_SUCCESS) {\n        example::nvrtc::print_program_log(prog);\n        std::exit(1);\n    }\n\n    // Obtain LTOIR from the program.\n    size_t ltoir_size;\n    NVRTC_SAFE_CALL(nvrtcGetLTOIRSize(prog, &ltoir_size));\n    auto ltoir = std::vector<char>(ltoir_size);\n    NVRTC_SAFE_CALL(nvrtcGetLTOIR(prog, ltoir.data()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(nvrtcDestroyProgram(&prog));\n\n    // Load the generated LTOIR and the cuFFT-dumped LTOIRs and link them together\n    nvJitLinkHandle handle;\n    std::vector<const char*> link_opts;\n    link_opts.push_back(\"-dlto\");\n\n    std::string nvjitlink_gpu_architecture_option = example::nvjitlink::get_device_architecture_option(current_device);\n    link_opts.push_back(nvjitlink_gpu_architecture_option.c_str());\n\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkCreate(&handle, static_cast<int>(link_opts.size()), link_opts.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoir.data(), ltoir_size, \"nvrtc_ltoir\"));\n    for (unsigned i = 0; i < ltoirs.size(); i++) {\n        NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoirs[i].data(), ltoirs[i].size(), \"cufft_generated_ltoir\"));\n    }\n    const auto link_start = std::chrono::high_resolution_clock::now();\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkComplete(handle));\n    std::cout << \"NVJITLINK link time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - link_start).count() / 1e3 << \"\\n\";\n\n    // Obtain linked Cubin\n    size_t cubin_size;\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n    auto cubin = std::vector<char>(cubin_size);\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkDestroy(&handle));\n\n    // Load the Cubin and get a handle to the test_kernel\n    CUdevice   cuDevice;\n    CUcontext  context;\n    CUmodule   module;\n    CUfunction kernel;\n    CU_CHECK_AND_EXIT(cuInit(0));\n    CU_CHECK_AND_EXIT(cuDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n    CU_CHECK_AND_EXIT(cuCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(cuModuleLoadDataEx(&module, cubin.data(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(cuModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(cuLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     block_dim.x, // number of threads\n                                     block_dim.y,\n                                     block_dim.z,\n                                     shared_memory_size, // shared memory size\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(cuCtxSynchronize());\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[test_nvrtc_lto_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n        CU_CHECK_AND_EXIT(cuModuleUnload(module));\n        CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[test_nvrtc_lto_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n    CU_CHECK_AND_EXIT(cuModuleUnload(module));\n    CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <chrono>\n\n#include <hip/hiprtc.h>\n#include <nvJitLink.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n#include \"../common/common_nvjitlink.hpp\"\n\nconst char* test_kernel = R\"kernel(\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_c2c  = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_c2c() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>() + experimental::CodeType<experimental::code_type::ltoir>());\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(hipGetDevice(&current_device));\n\n    if (!cufftdx::utils::check_cufft_device_api_version()) {\n        return 1;\n    }\n\n    auto [lto_db, ltoirs, block_dim, shared_memory_size] =\n        cufftdx::utils::get_database_and_ltoir(fft_size,\n                                               cufftdx::fft_direction::inverse,\n                                               cufftdx::fft_type::c2c,\n                                               example::nvrtc::get_device_architecture(current_device) * 10,\n                                               cufftdx::utils::execution_type::block,\n                                               cufftdx::precision::f64,\n                                               cufftdx::complex_layout::natural,\n                                               cufftdx::real_mode::normal,\n                                               fft_ept);\n\n    if (ltoirs.size() == 0 || lto_db.empty()) {\n        std::cout << \"LTOIR or database from cuFFT is empty. Exit.\\n\";\n        return 1;\n    }\n\n    // Build CUDA source code. Plug in cuFFT-dumped database file after cufftdx.hpp inclusion.\n    std::string dx_source_code;\n    dx_source_code.append(\"#include <cufftdx.hpp>\\n\");\n    dx_source_code.append(lto_db);\n    dx_source_code.append(test_kernel);\n\n    // Create a program\n    hiprtcProgram prog;\n    NVRTC_SAFE_CALL(hiprtcCreateProgram(&prog,                  // prog\n                                       dx_source_code.c_str(), // buffer\n                                       \"test_kernel.cu\",       // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string nvrtc_gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(nvrtc_gpu_architecture_option.c_str());\n\n    // Add flags for LTO compilation\n    opts.push_back(\"-dlto\");\n    opts.push_back(\"--relocatable-device-code=true\");\n\n    const auto compile_start = std::chrono::high_resolution_clock::now();\n    hiprtcResult compileResult = hiprtcCompileProgram(prog,         // prog\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n    std::cout << \"NVRTC compile time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - compile_start).count() / 1e3 << \"\\n\";\n\n    // Obtain compilation log from the program\n    if (compileResult != HIPRTC_SUCCESS) {\n        example::nvrtc::print_program_log(prog);\n        std::exit(1);\n    }\n\n    // Obtain LTOIR from the program.\n    size_t ltoir_size;\n    NVRTC_SAFE_CALL(nvrtcGetLTOIRSize(prog, &ltoir_size));\n    auto ltoir = std::vector<char>(ltoir_size);\n    NVRTC_SAFE_CALL(nvrtcGetLTOIR(prog, ltoir.data()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(hiprtcDestroyProgram(&prog));\n\n    // Load the generated LTOIR and the cuFFT-dumped LTOIRs and link them together\n    nvJitLinkHandle handle;\n    std::vector<const char*> link_opts;\n    link_opts.push_back(\"-dlto\");\n\n    std::string nvjitlink_gpu_architecture_option = example::nvjitlink::get_device_architecture_option(current_device);\n    link_opts.push_back(nvjitlink_gpu_architecture_option.c_str());\n\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkCreate(&handle, static_cast<int>(link_opts.size()), link_opts.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoir.data(), ltoir_size, \"nvrtc_ltoir\"));\n    for (unsigned i = 0; i < ltoirs.size(); i++) {\n        NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoirs[i].data(), ltoirs[i].size(), \"cufft_generated_ltoir\"));\n    }\n    const auto link_start = std::chrono::high_resolution_clock::now();\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkComplete(handle));\n    std::cout << \"NVJITLINK link time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - link_start).count() / 1e3 << \"\\n\";\n\n    // Obtain linked Cubin\n    size_t cubin_size;\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n    auto cubin = std::vector<char>(cubin_size);\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkDestroy(&handle));\n\n    // Load the Cubin and get a handle to the test_kernel\n    hipDevice_t   cuDevice;\n    hipCtx_t  context;\n    hipModule_t   module;\n    hipFunction_t kernel;\n    CU_CHECK_AND_EXIT(hipInit(0));\n    CU_CHECK_AND_EXIT(hipDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n    CU_CHECK_AND_EXIT(hipCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(hipModuleLoadDataEx(&module, cubin.data(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(hipModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(hipModuleLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     block_dim.x, // number of threads\n                                     block_dim.y,\n                                     block_dim.z,\n                                     shared_memory_size, // shared memory size\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(hipCtxSynchronize());\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[test_nvrtc_lto_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n        CU_CHECK_AND_EXIT(hipModuleUnload(module));\n        CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[test_nvrtc_lto_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n    CU_CHECK_AND_EXIT(hipModuleUnload(module));\n    CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/04_nvrtc_fft/nvrtc_fft_block_lto.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <nvrtc.h>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n#include <cuComplex.h>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* test_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_desc = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_desc() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>());\n\n__constant__ dim3         fft_block_dim     = FFT::block_dim;\n__constant__ unsigned int fft_shared_memory = FFT::shared_memory_size;\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    // Precision: double\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    // Complex type according to precision\n    using value_type = cuDoubleComplex; // or double2, or cuda::std::complex<double>\n\n    nvrtcProgram program;\n    NVRTC_SAFE_CALL(nvrtcCreateProgram(&program,            // program\n                                       test_kernel,      // buffer\n                                       \"test_kernel.cu\", // name\n                                       0,                // numHeaders\n                                       NULL,             // headers\n                                       NULL));           // includeNames\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(cudaGetDevice(&current_device));\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Example how to pass part of FFT description to NVRTC kernel\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(gpu_architecture_option.c_str());\n\n    nvrtcResult compileResult = nvrtcCompileProgram(program,         // program\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n\n    // Obtain compilation log from the program\n    if (compileResult != NVRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(nvrtcGetCUBINSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(nvrtcGetCUBIN(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(nvrtcDestroyProgram(&program));\n\n    // Load the generated Cubin and get a handle to the test_kernel\n    CUdevice   cuDevice;\n    CUcontext  context;\n    CUmodule   module;\n    CUfunction kernel;\n    CU_CHECK_AND_EXIT(cuInit(0));\n    CU_CHECK_AND_EXIT(cuDeviceGet(&cuDevice, current_device));\n\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, 0, cuDevice));\n    #endif\n\n    CU_CHECK_AND_EXIT(cuModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(cuModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Print fft_data\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Get FFT::block_dim and FFT::shared_memory required for kernel launch\n    dim3 fft_block_dim = example::nvrtc::get_global_from_module<dim3>(module, \"fft_block_dim\");\n    unsigned int fft_shared_memory = example::nvrtc::get_global_from_module<unsigned int>(module, \"fft_shared_memory\");\n\n    // Increase dynamic shared memory limit\n    CU_CHECK_AND_EXIT(cuFuncSetAttribute(kernel, CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, fft_shared_memory));\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(cuLaunchKernel(kernel,\n                                       1, // number of blocks\n                                       1,\n                                       1,\n                                       fft_block_dim.x, // number of threads\n                                       fft_block_dim.y,\n                                       1,\n                                       fft_shared_memory,\n                                       NULL, // NULL stream\n                                       args,\n                                       0));\n    CU_CHECK_AND_EXIT(cuCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[nvrtc_fft_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n        CU_CHECK_AND_EXIT(cuModuleUnload(module));\n        CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[nvrtc_fft_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n    CU_CHECK_AND_EXIT(cuModuleUnload(module));\n    CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n#include <string>\n#include <algorithm>\n#include <memory>\n\n#include <hip/hiprtc.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_complex.h>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n\nconst char* test_kernel = R\"kernel(\n#include <cufftdx.hpp>\n\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_desc = Type<fft_type::c2c>;\nusing FFT = decltype(Block() + size_desc() + dir_desc() + type_desc() + Precision<double>() + SM<FFT_SM>() + ElementsPerThread<FFT_EPT>());\n\n__constant__ dim3         fft_block_dim     = FFT::block_dim;\n__constant__ unsigned int fft_shared_memory = FFT::shared_memory_size;\n\ninline __device__ unsigned int batch_offset(const unsigned int local_fft_id,\n                                            const unsigned int ffts_per_block = blockDim.y) {\n    unsigned int global_fft_id = ffts_per_block == 1 ? blockIdx.x : (blockIdx.x * ffts_per_block + local_fft_id);\n    return cufftdx::size_of<FFT>::value * global_fft_id;\n}\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type* fft_data)\n{\n  typename FFT::value_type thread_data[FFT::storage_size];\n\n  const unsigned int offset = batch_offset(threadIdx.y, FFT::ffts_per_block);\n  constexpr unsigned int stride = FFT::stride;\n  unsigned int index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        thread_data[i] = fft_data[index];\n        index += stride;\n    }\n  }\n\n  extern __shared__ FFT::value_type shared_mem[];\n  FFT().execute(thread_data, shared_mem);\n\n  index = offset + threadIdx.x;\n  for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n    if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n        fft_data[index] = thread_data[i];\n        index += stride;\n    }\n  }\n}\n)kernel\";\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    // Precision: double\n    static constexpr unsigned int fft_size = 64;\n    static constexpr unsigned int fft_ept  = 8;\n\n    // Complex type according to precision\n    using value_type = hipDoubleComplex; // or double2, or cuda::std::complex<double>\n\n    hiprtcProgram program;\n    NVRTC_SAFE_CALL(hiprtcCreateProgram(&program,            // program\n                                       test_kernel,      // buffer\n                                       \"test_kernel.cu\", // name\n                                       0,                // numHeaders\n                                       NULL,             // headers\n                                       NULL));           // includeNames\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(hipGetDevice(&current_device));\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Example how to pass part of FFT description to NVRTC kernel\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add FFT_EPT definition to opts\n    std::string fft_ept_definition = \"-DFFT_EPT=\" + std::to_string(fft_ept);\n    opts.push_back(fft_ept_definition.c_str());\n\n    // Add GPU_ARCHITECTURE definition to opts\n    std::string gpu_architecture_definition = \"-DFFT_SM=\" + std::to_string(example::nvrtc::get_device_architecture(current_device) * 10);\n    opts.push_back(gpu_architecture_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(gpu_architecture_option.c_str());\n\n    hiprtcResult compileResult = hiprtcCompileProgram(program,         // program\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n\n    // Obtain compilation log from the program\n    if (compileResult != HIPRTC_SUCCESS) {\n        example::nvrtc::print_program_log(program);\n        std::exit(1);\n    }\n\n    // Obtain cubin from the program.\n    size_t cubin_size;\n    NVRTC_SAFE_CALL(hiprtcGetBitcodeSize(program, &cubin_size));\n    auto cubin = std::make_unique<char[]>(cubin_size);\n    NVRTC_SAFE_CALL(hiprtcGetBitcode(program, cubin.get()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(hiprtcDestroyProgram(&program));\n\n    // Load the generated Cubin and get a handle to the test_kernel\n    hipDevice_t   cuDevice;\n    hipCtx_t  context;\n    hipModule_t   module;\n    hipFunction_t kernel;\n    CU_CHECK_AND_EXIT(hipInit(0));\n    CU_CHECK_AND_EXIT(hipDeviceGet(&cuDevice, current_device));\n\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, 0, cuDevice));\n    #endif\n\n    CU_CHECK_AND_EXIT(hipModuleLoadDataEx(&module, cubin.get(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(hipModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Print fft_data\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Get FFT::block_dim and FFT::shared_memory required for kernel launch\n    dim3 fft_block_dim = example::nvrtc::get_global_from_module<dim3>(module, \"fft_block_dim\");\n    unsigned int fft_shared_memory = example::nvrtc::get_global_from_module<unsigned int>(module, \"fft_shared_memory\");\n\n    // Increase dynamic shared memory limit\n    CU_CHECK_AND_EXIT(cuFuncSetAttribute(kernel, HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, fft_shared_memory));\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(hipModuleLaunchKernel(kernel,\n                                       1, // number of blocks\n                                       1,\n                                       1,\n                                       fft_block_dim.x, // number of threads\n                                       fft_block_dim.y,\n                                       1,\n                                       fft_shared_memory,\n                                       NULL, // NULL stream\n                                       args,\n                                       0));\n    CU_CHECK_AND_EXIT(hipCtxSynchronize());\n\n    // Retrieve and print output\n    for (size_t i = 0; i < fft_size; ++i) {\n        std::cout << i << \": (\" << fft_data[i].x << \", \" << fft_data[i].y << \")\" << std::endl;\n    }\n\n    // Validate results before destroying context\n    double sum = (fft_size * (fft_size - 1)) / 2.0;\n    if (std::abs(fft_data[0].x - sum) > 0.01) {\n        std::cout << \"[nvrtc_fft_block] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n        CU_CHECK_AND_EXIT(hipModuleUnload(module));\n        CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[nvrtc_fft_block] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n    CU_CHECK_AND_EXIT(hipModuleUnload(module));\n    CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/04_nvrtc_fft/nvrtc_fft_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n\n#include <nvrtc.h>\n#include <nvJitLink.h>\n#include <cuda.h>\n#include <cuda_runtime_api.h>\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n#include \"../common/common_nvjitlink.hpp\"\n\nconst char* test_kernel = R\"kernel(\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_c2c  = Type<fft_type::c2c>;\nusing FFT = decltype(size_desc() + dir_desc() + type_c2c() + Precision<double>() + experimental::CodeType<experimental::code_type::ltoir>() + Thread());\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type *values)\n{\n  typename FFT::value_type regs[FFT::storage_size];\n\n  static constexpr auto size = cufftdx::size_of<FFT>::value;\n  for (size_t i = 0; i < size; i++) {\n      regs[i] = values[i];\n  }\n\n  FFT().execute(regs);\n\n  for (size_t i = 0; i < size; i++) {\n      values[i] = regs[i];\n  }\n}\n)kernel\";\n\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 16;\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(cudaGetDevice(&current_device));\n\n    // Get LTOIR and database from cuFFT\n    auto [lto_db, ltoirs, block_dim, shared_memory_size] =\n        cufftdx::utils::get_database_and_ltoir(fft_size,\n                                               cufftdx::fft_direction::inverse,\n                                               cufftdx::fft_type::c2c,\n                                               example::nvrtc::get_device_architecture(current_device) * 10,\n                                               cufftdx::utils::execution_type::thread,\n                                               cufftdx::precision::f64);\n\n    if (ltoirs.size() == 0 || lto_db.empty()) {\n        std::cout << \"LTOIR or database from cuFFT is empty. Exit.\\n\";\n        return 1;\n    }\n\n    // Build CUDA source code. Plug in cuFFT-dumped database file after cufftdx.hpp inclusion.\n    std::string dx_source_code;\n    dx_source_code.append(\"#include <cufftdx.hpp>\\n\");\n    dx_source_code.append(lto_db);\n    dx_source_code.append(test_kernel);\n\n    // Create a program\n    nvrtcProgram prog;\n    NVRTC_SAFE_CALL(nvrtcCreateProgram(&prog,                  // prog\n                                       dx_source_code.c_str(), // buffer\n                                       \"test_kernel.cu\",       // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string nvrtc_gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(nvrtc_gpu_architecture_option.c_str());\n\n    // Add flags for LTO compilation\n    opts.push_back(\"-dlto\");\n    opts.push_back(\"--relocatable-device-code=true\");\n\n    const auto compile_start = std::chrono::high_resolution_clock::now();\n    nvrtcResult compileResult = nvrtcCompileProgram(prog,         // prog\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n    std::cout << \"NVRTC compile time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - compile_start).count() / 1e3 << \"\\n\";\n\n    // Obtain compilation log from the program\n    if (compileResult != NVRTC_SUCCESS) {\n        example::nvrtc::print_program_log(prog);\n        std::exit(1);\n    }\n\n    // Obtain LTOIR from the program.\n    size_t ltoir_size;\n    NVRTC_SAFE_CALL(nvrtcGetLTOIRSize(prog, &ltoir_size));\n    auto ltoir = std::vector<char>(ltoir_size);\n    NVRTC_SAFE_CALL(nvrtcGetLTOIR(prog, ltoir.data()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(nvrtcDestroyProgram(&prog));\n\n    // Load the generated LTOIR and the cuFFT-dumped LTOIRs and link them together\n    nvJitLinkHandle handle;\n    std::vector<const char*> link_opts;\n    link_opts.push_back(\"-dlto\");\n\n    std::string nvjitlink_gpu_architecture_option = example::nvjitlink::get_device_architecture_option(current_device);\n    link_opts.push_back(nvjitlink_gpu_architecture_option.c_str());\n\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkCreate(&handle, static_cast<int>(link_opts.size()), link_opts.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoir.data(), ltoir_size, \"nvrtc_ltoir\"));\n    for (unsigned i = 0; i < ltoirs.size(); i++) {\n        NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoirs[i].data(), ltoirs[i].size(), \"cufft_generated_ltoir\"));\n    }\n    const auto link_start = std::chrono::high_resolution_clock::now();\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkComplete(handle));\n    std::cout << \"NVJITLINK link time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - link_start).count() / 1e3 << \"\\n\";\n\n    // Obtain linked Cubin\n    size_t cubin_size;\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n    auto cubin = std::vector<char>(cubin_size);\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkDestroy(&handle));\n\n    // Load the Cubin and get a handle to the test_kernel\n    CUdevice   cuDevice;\n    CUcontext  context;\n    CUmodule   module;\n    CUfunction kernel;\n    CU_CHECK_AND_EXIT(cuInit(0));\n    CU_CHECK_AND_EXIT(cuDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(cuCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(cuModuleLoadDataEx(&module, cubin.data(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(cuModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(cuLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     1, // number of threads\n                                     1,\n                                     1,\n                                     0,    // no shared memory\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(cuCtxSynchronize());\n\n    // Validate results before destroying context\n    double expected_value = (fft_size * (fft_size - 1)) / 2;\n    if (std::abs(fft_data[0].x - expected_value) > 0.01) {\n        std::cout << \"[test_nvrtc_lto_thread] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n        CU_CHECK_AND_EXIT(cuModuleUnload(module));\n        CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[test_nvrtc_lto_thread] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(cudaFree(fft_data));\n    CU_CHECK_AND_EXIT(cuModuleUnload(module));\n    CU_CHECK_AND_EXIT(cuCtxDestroy(context));\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <iostream>\n\n#include <hip/hiprtc.h>\n#include <nvJitLink.h>\n#include <hip/hip_runtime.h>\n#include <hip/hip_runtime_api.h>\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/common_nvrtc.hpp\"\n#include \"../common/common_nvjitlink.hpp\"\n\nconst char* test_kernel = R\"kernel(\nusing namespace cufftdx;\n\n// FFT Operators\nusing size_desc = Size<FFT_SIZE>;\nusing dir_desc  = Direction<fft_direction::inverse>;\nusing type_c2c  = Type<fft_type::c2c>;\nusing FFT = decltype(size_desc() + dir_desc() + type_c2c() + Precision<double>() + experimental::CodeType<experimental::code_type::ltoir>() + Thread());\n\nextern \"C\" __global__ void test_kernel(typename FFT::value_type *values)\n{\n  typename FFT::value_type regs[FFT::storage_size];\n\n  static constexpr auto size = cufftdx::size_of<FFT>::value;\n  for (size_t i = 0; i < size; i++) {\n      regs[i] = values[i];\n  }\n\n  FFT().execute(regs);\n\n  for (size_t i = 0; i < size; i++) {\n      values[i] = regs[i];\n  }\n}\n)kernel\";\n\n\nint main(int, char**) {\n    // Note that FFT description is only defined in kernel\n    static constexpr unsigned int fft_size = 16;\n    using value_type = double2; // or cuDoubleComplex, or cuda::std::complex<double>\n\n    // Get current device\n    int current_device;\n    CUDA_CHECK_AND_EXIT(hipGetDevice(&current_device));\n\n    // Get LTOIR and database from cuFFT\n    auto [lto_db, ltoirs, block_dim, shared_memory_size] =\n        cufftdx::utils::get_database_and_ltoir(fft_size,\n                                               cufftdx::fft_direction::inverse,\n                                               cufftdx::fft_type::c2c,\n                                               example::nvrtc::get_device_architecture(current_device) * 10,\n                                               cufftdx::utils::execution_type::thread,\n                                               cufftdx::precision::f64);\n\n    if (ltoirs.size() == 0 || lto_db.empty()) {\n        std::cout << \"LTOIR or database from cuFFT is empty. Exit.\\n\";\n        return 1;\n    }\n\n    // Build CUDA source code. Plug in cuFFT-dumped database file after cufftdx.hpp inclusion.\n    std::string dx_source_code;\n    dx_source_code.append(\"#include <cufftdx.hpp>\\n\");\n    dx_source_code.append(lto_db);\n    dx_source_code.append(test_kernel);\n\n    // Create a program\n    hiprtcProgram prog;\n    NVRTC_SAFE_CALL(hiprtcCreateProgram(&prog,                  // prog\n                                       dx_source_code.c_str(), // buffer\n                                       \"test_kernel.cu\",       // name\n                                       0,                      // numHeaders\n                                       NULL,                   // headers\n                                       NULL));                 // includeNames\n\n    // Prepare compilation options\n    std::vector<const char*> opts = {\n        \"--std=c++17\",\n        \"--device-as-default-execution-space\"\n    };\n\n    // Parse cuFFTDx include dirs\n    std::vector<std::string> cufftdx_include_dirs = example::nvrtc::get_cufftdx_include_dirs();\n    // Add cuFFTDx include dirs to opts\n    for (auto& d : cufftdx_include_dirs) {\n        opts.push_back(d.c_str());\n    }\n\n    // Add FFT_SIZE definition to opts\n    std::string fft_size_definition = \"-DFFT_SIZE=\" + std::to_string(fft_size);\n    opts.push_back(fft_size_definition.c_str());\n\n    // Add gpu-architecture to opts\n    std::string nvrtc_gpu_architecture_option = example::nvrtc::get_device_architecture_option(current_device);\n    opts.push_back(nvrtc_gpu_architecture_option.c_str());\n\n    // Add flags for LTO compilation\n    opts.push_back(\"-dlto\");\n    opts.push_back(\"--relocatable-device-code=true\");\n\n    const auto compile_start = std::chrono::high_resolution_clock::now();\n    hiprtcResult compileResult = hiprtcCompileProgram(prog,         // prog\n                                                    static_cast<int>(opts.size()),  // numOptions\n                                                    opts.data()); // options\n    std::cout << \"NVRTC compile time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - compile_start).count() / 1e3 << \"\\n\";\n\n    // Obtain compilation log from the program\n    if (compileResult != HIPRTC_SUCCESS) {\n        example::nvrtc::print_program_log(prog);\n        std::exit(1);\n    }\n\n    // Obtain LTOIR from the program.\n    size_t ltoir_size;\n    NVRTC_SAFE_CALL(nvrtcGetLTOIRSize(prog, &ltoir_size));\n    auto ltoir = std::vector<char>(ltoir_size);\n    NVRTC_SAFE_CALL(nvrtcGetLTOIR(prog, ltoir.data()));\n\n    // Destroy the program.\n    NVRTC_SAFE_CALL(hiprtcDestroyProgram(&prog));\n\n    // Load the generated LTOIR and the cuFFT-dumped LTOIRs and link them together\n    nvJitLinkHandle handle;\n    std::vector<const char*> link_opts;\n    link_opts.push_back(\"-dlto\");\n\n    std::string nvjitlink_gpu_architecture_option = example::nvjitlink::get_device_architecture_option(current_device);\n    link_opts.push_back(nvjitlink_gpu_architecture_option.c_str());\n\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkCreate(&handle, static_cast<int>(link_opts.size()), link_opts.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoir.data(), ltoir_size, \"nvrtc_ltoir\"));\n    for (unsigned i = 0; i < ltoirs.size(); i++) {\n        NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_ANY, ltoirs[i].data(), ltoirs[i].size(), \"cufft_generated_ltoir\"));\n    }\n    const auto link_start = std::chrono::high_resolution_clock::now();\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkComplete(handle));\n    std::cout << \"NVJITLINK link time (ms): \" << (double) std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - link_start).count() / 1e3 << \"\\n\";\n\n    // Obtain linked Cubin\n    size_t cubin_size;\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubinSize(handle, &cubin_size));\n    auto cubin = std::vector<char>(cubin_size);\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubin(handle, cubin.data()));\n    NVJITLINK_SAFE_CALL(handle, nvJitLinkDestroy(&handle));\n\n    // Load the Cubin and get a handle to the test_kernel\n    hipDevice_t   cuDevice;\n    hipCtx_t  context;\n    hipModule_t   module;\n    hipFunction_t kernel;\n    CU_CHECK_AND_EXIT(hipInit(0));\n    CU_CHECK_AND_EXIT(hipDeviceGet(&cuDevice, current_device));\n    #if defined(CUDA_VERSION) && CUDA_VERSION >= 13000\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, (CUctxCreateParams*)0, 0, cuDevice));\n    #else\n        CU_CHECK_AND_EXIT(hipCtxCreate(&context, 0, cuDevice));\n    #endif\n    CU_CHECK_AND_EXIT(hipModuleLoadDataEx(&module, cubin.data(), 0, 0, 0));\n    CU_CHECK_AND_EXIT(hipModuleGetFunction(&kernel, module, \"test_kernel\"));\n\n    // Generate input for execution\n    value_type* fft_data;\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&fft_data, fft_size * sizeof(value_type)));\n\n    for(size_t i = 0; i < fft_size; i++) {\n        fft_data[i].x = float(i);\n        fft_data[i].y = 0;\n    }\n\n    // Execute test_kernel\n    void* args[] = {&fft_data};\n    CU_CHECK_AND_EXIT(hipModuleLaunchKernel(kernel,\n                                     1, // number of blocks\n                                     1,\n                                     1,\n                                     1, // number of threads\n                                     1,\n                                     1,\n                                     0,    // no shared memory\n                                     NULL, // NULL stream\n                                     args,\n                                     0));\n    CU_CHECK_AND_EXIT(hipCtxSynchronize());\n\n    // Validate results before destroying context\n    double expected_value = (fft_size * (fft_size - 1)) / 2;\n    if (std::abs(fft_data[0].x - expected_value) > 0.01) {\n        std::cout << \"[test_nvrtc_lto_thread] Failed\" << std::endl;\n        CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n        CU_CHECK_AND_EXIT(hipModuleUnload(module));\n        CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n        return 1;\n    }\n    std::cout << \"[test_nvrtc_lto_thread] Passed\" << std::endl;\n\n    // Release resources\n    CUDA_CHECK_AND_EXIT(hipFree(fft_data));\n    CU_CHECK_AND_EXIT(hipModuleUnload(module));\n    CU_CHECK_AND_EXIT(hipCtxDestroy(context));\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/04_nvrtc_fft/nvrtc_fft_thread_lto.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n// Include cuFFT-dumped LTOIR database header file\n#include \"lto_database.hpp.inc\"\n\n// This example is a copy of the 02_simple_fft_block/00_simple_fft_block.cu example and\n// has been modified to work with cufft lto. It contains only 2 modifications, the inclusion\n// of a auto-generated lto header file, lto_database.hpp.inc, and the addition of the\n// experimental ltoir CodeType. to the FFT type. The bulk of the modifications made to the\n// 02_simple_fft_block example were made in the build scripts which construct and link the\n// external lto database as well as create the database header file.\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    complex_type thread_data[FFT::storage_size];\n\n    const unsigned int local_fft_id = threadIdx.y;\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\ntemplate<unsigned int Arch>\nvoid introduction_lto_helper() {\n    using FFT_without_code_type = decltype(cufftdx::Block() +\n                                           cufftdx::Size<128>() +\n                                           cufftdx::Type<cufftdx::fft_type::c2c>() +\n                                           cufftdx::Direction<cufftdx::fft_direction::forward>() +\n                                           cufftdx::Precision<float>() +\n                                           cufftdx::ElementsPerThread<8>() +\n                                           cufftdx::FFTsPerBlock<2>() +\n                                           cufftdx::SM<Arch>());\n    // Add experimental ltoir code_type to FFT type\n    using FFT = decltype(FFT_without_code_type() + cufftdx::experimental::CodeType<cufftdx::experimental::code_type::ltoir>());\n\n    // Validate that the selected fft is in fact an ltoir kernel\n    static_assert(FFT::code == cufftdx::experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    using complex_type = typename FFT::value_type;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    cudaError_t error_code = cudaSuccess;\n    auto workspace = cufftdx::make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_lto_helper_functor {\n    void operator()() { return introduction_lto_helper<Arch>(); }\n};\n\nint main() {\n    return example::sm_runner<introduction_lto_helper_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n// Include cuFFT-dumped LTOIR database header file\n#include \"lto_database.hpp.inc\"\n\n// This example is a copy of the 02_simple_fft_block/00_simple_fft_block.cu example and\n// has been modified to work with cufft lto. It contains only 2 modifications, the inclusion\n// of a auto-generated lto header file, lto_database.hpp.inc, and the addition of the\n// experimental ltoir CodeType. to the FFT type. The bulk of the modifications made to the\n// 02_simple_fft_block example were made in the build scripts which construct and link the\n// external lto database as well as create the database header file.\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    complex_type thread_data[FFT::storage_size];\n\n    const unsigned int local_fft_id = threadIdx.y;\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\ntemplate<unsigned int Arch>\nvoid introduction_lto_helper() {\n    using FFT_without_code_type = decltype(cufftdx::Block() +\n                                           cufftdx::Size<128>() +\n                                           cufftdx::Type<cufftdx::fft_type::c2c>() +\n                                           cufftdx::Direction<cufftdx::fft_direction::forward>() +\n                                           cufftdx::Precision<float>() +\n                                           cufftdx::ElementsPerThread<8>() +\n                                           cufftdx::FFTsPerBlock<2>() +\n                                           cufftdx::SM<Arch>());\n    // Add experimental ltoir code_type to FFT type\n    using FFT = decltype(FFT_without_code_type() + cufftdx::experimental::CodeType<cufftdx::experimental::code_type::ltoir>());\n\n    // Validate that the selected fft is in fact an ltoir kernel\n    static_assert(FFT::code == cufftdx::experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    using complex_type = typename FFT::value_type;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    hipError_t error_code = hipSuccess;\n    auto workspace = cufftdx::make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_lto_helper_functor {\n    void operator()() { return introduction_lto_helper<Arch>(); }\n};\n\nint main() {\n    return example::sm_runner<introduction_lto_helper_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/09_introduction_lto_example/introduction_lto_example.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Local batch id of this FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Global batch id of this FFT in CUDA grid is equal to number of batches per CUDA block (ffts_per_block)\n    // times CUDA block id, plus local batch id.\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    // Load data from global memory to registers\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    // Save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid introduction_example() {\n    using namespace cufftdx;\n\n    // FFT definition\n    //\n    // Size, precision, type, direction are defined with operators.\n    // Block() operator informs that FFT will be executed on block level.\n    // Shared memory is required for co-operation between threads.\n    //\n    // Additionally:\n    // * FFTsPerBlock operator defines how many FFTs (batches) are executed in a single CUDA block,\n    // * ElementsPerThread operators defines how FFT calculations are mapped into a CUDA block, i.e. how many thread are required, and\n    // * SM operator defines targeted CUDA architecture.\n    using FFT          = decltype(Size<128>() + Precision<float>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>()\n                         + ElementsPerThread<8>() + FFTsPerBlock<2>()\n                         + SM<Arch>() + Block());\n    using complex_type = typename FFT::value_type;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    // Generate data\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    cudaError_t error_code = cudaSuccess;\n    auto workspace = make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_example_functor {\n    void operator()() { return introduction_example<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<introduction_example_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Local batch id of this FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Global batch id of this FFT in CUDA grid is equal to number of batches per CUDA block (ffts_per_block)\n    // times CUDA block id, plus local batch id.\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    // Load data from global memory to registers\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    // Save results\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid introduction_example() {\n    using namespace cufftdx;\n\n    // FFT definition\n    //\n    // Size, precision, type, direction are defined with operators.\n    // Block() operator informs that FFT will be executed on block level.\n    // Shared memory is required for co-operation between threads.\n    //\n    // Additionally:\n    // * FFTsPerBlock operator defines how many FFTs (batches) are executed in a single CUDA block,\n    // * ElementsPerThread operators defines how FFT calculations are mapped into a CUDA block, i.e. how many thread are required, and\n    // * SM operator defines targeted CUDA architecture.\n    using FFT          = decltype(Size<128>() + Precision<float>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>()\n                         + ElementsPerThread<8>() + FFTsPerBlock<2>()\n                         + SM<Arch>() + Block());\n    using complex_type = typename FFT::value_type;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    // Generate data\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    hipError_t error_code = hipSuccess;\n    auto workspace = make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_example_functor {\n    void operator()() { return introduction_example<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<introduction_example_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/00_introduction_example/introduction_example.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void fft_2d_kernel(const ComplexType*            input,\n                       ComplexType*                  output,\n                       typename FFTX::workspace_type workspace_x,\n                       typename FFTY::workspace_type workspace_y) {\n    using complex_type  = ComplexType;\n    using dim_desc_type = example::dimension_description;\n    using io_y_type     = example::io_generic_strided<FFTY>;\n    using io_x_type     = example::io_generic_strided<FFTX>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if(blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        // Load data from global memory to registers\n        io_y_type::load_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, shared_mem, workspace_y);\n\n        // Save results\n        io_y_type::store_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if(blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        // Load data from global memory to registers\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, shared_mem, local_fft_id);\n        } else {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, shared_mem, workspace_x);\n\n        // Save results\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, shared_mem, output, local_fft_id);\n        } else {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x,\n                                     unsigned int fft_size_y,\n                                     T*           input,\n                                     T*           output,\n                                     cudaStream_t stream) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan, fft_size_x, fft_size_y, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, cudaStream_t stream) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel = (const void*)fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, complex_type>;\n    auto error_code    = cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[] = {&cufftdx_input, &cufftdx_output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto fft_2d_execution = [&](cudaStream_t stream) {\n        CUDA_CHECK_AND_EXIT(cudaLaunchCooperativeKernel(kernel, grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type = float;\n    using complex_type   = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = 512;\n    static constexpr unsigned int fft_size_x = 512;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 8;\n    static constexpr unsigned int fpb_y = 8;\n    static constexpr unsigned int ept_x = 8;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 8;\n    // static constexpr unsigned int fpb_y = 4;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = fpb_y;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 256;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 4;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = fpb_y;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, input_host.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output,\n                                                                                 cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void fft_2d_kernel(const ComplexType*            input,\n                       ComplexType*                  output,\n                       typename FFTX::workspace_type workspace_x,\n                       typename FFTY::workspace_type workspace_y) {\n    using complex_type  = ComplexType;\n    using dim_desc_type = example::dimension_description;\n    using io_y_type     = example::io_generic_strided<FFTY>;\n    using io_x_type     = example::io_generic_strided<FFTX>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if(blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        // Load data from global memory to registers\n        io_y_type::load_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, shared_mem, workspace_y);\n\n        // Save results\n        io_y_type::store_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if(blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        // Load data from global memory to registers\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, shared_mem, local_fft_id);\n        } else {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, shared_mem, workspace_x);\n\n        // Save results\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, shared_mem, output, local_fft_id);\n        } else {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x,\n                                     unsigned int fft_size_y,\n                                     T*           input,\n                                     T*           output,\n                                     hipStream_t stream) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan, fft_size_x, fft_size_y, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, hipStream_t stream) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel = (const void*)fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, complex_type>;\n    auto error_code    = hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[] = {&cufftdx_input, &cufftdx_output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto fft_2d_execution = [&](hipStream_t stream) {\n        CUDA_CHECK_AND_EXIT(hipLaunchCooperativeKernel(reinterpret_cast<const void*>(kernel), grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type = float;\n    using complex_type   = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = 512;\n    static constexpr unsigned int fft_size_x = 512;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 8;\n    static constexpr unsigned int fpb_y = 8;\n    static constexpr unsigned int ept_x = 8;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 8;\n    // static constexpr unsigned int fpb_y = 4;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = fpb_y;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 256;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 4;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = fpb_y;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, input_host.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output,\n                                                                                 cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_2d_single_kernel.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n// To be able to compile on nvcc <= 13.1.0\n#define CUFFTDX_IGNORE_BLOCK_DIM_UNSUPPORTED\n\n#include <cuda_runtime_api.h>\n#include <cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"./block_io_generic_strided_block_dim.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void fft_2d_kernel(const ComplexType*            input,\n                       ComplexType*                  output,\n                       typename FFTX::workspace_type workspace_x,\n                       typename FFTY::workspace_type workspace_y) {\n    using complex_type  = ComplexType;\n    using dim_desc_type = example::dimension_description;\n    using io_y_type     = example::io_generic_strided<FFTY>;\n    using io_x_type     = example::io_generic_strided<FFTX>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if(blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        // Load data from global memory to registers\n        io_y_type::load_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, shared_mem, workspace_y);\n\n        // Save results\n        io_y_type::store_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if(blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        // Load data from global memory to registers\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, shared_mem, local_fft_id);\n        } else {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, shared_mem, workspace_x);\n\n        // Save results\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, shared_mem, output, local_fft_id);\n        } else {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x,\n                                     unsigned int fft_size_y,\n                                     T*           input,\n                                     T*           output,\n                                     cudaStream_t stream) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan, fft_size_x, fft_size_y, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, cudaStream_t stream) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel = (const void*)fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, complex_type>;\n    auto error_code    = cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[] = {&cufftdx_input, &cufftdx_output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto fft_2d_execution = [&](cudaStream_t stream) {\n        CUDA_CHECK_AND_EXIT(cudaLaunchCooperativeKernel(kernel, grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type = float;\n    using complex_type   = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    // We can use irregular FFT sizes because we will set the block dim to the bigger one\n    static constexpr unsigned int fft_size_y = 80;\n    static constexpr unsigned int fft_size_x = 100;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 10;\n    static constexpr unsigned int fpb_y = 10;\n    static constexpr unsigned int ept_x = 10;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same due to how IO is implemented\n\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y_base    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x_base    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    // Compare block dims and get the maximum\n    constexpr unsigned int block_dim_x = std::max(fft_y_base::block_dim.x, fft_x_base::block_dim.x);\n    // Thanks to the block dim operator we can fuse FFTs of different block dim sizes.\n    using fft_y      = decltype(fft_y_base() + BlockDim<block_dim_x, fft_y_base::block_dim.y,1>()); // 1 is the number of blocks in the y dimension\n    using fft_x      = decltype(fft_x_base() + BlockDim<block_dim_x, fft_x_base::block_dim.y,1>());\n    // Print block dims\n    std::cout << \"Block dims: \" << block_dim_x << \"\\n\";\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, input_host.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output,\n                                                                                 cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        for (size_t i = 0; i < flat_fft_size; i++) {\n            std::cout << i << \": \" << cufftdx_results.output[i].x << \" \" << cufftdx_results.output[i].y << \" \" << cufft_results.output[i].x << \" \" << cufft_results.output[i].y << \"\\n\";\n        }\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n// To be able to compile on nvcc <= 13.1.0\n#define CUFFTDX_IGNORE_BLOCK_DIM_UNSUPPORTED\n\n#include <hip/hip_runtime_api.h>\n#include <hip/hip_cooperative_groups.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"./block_io_generic_strided_block_dim.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFTX,\n         class FFTY,\n         bool UseSharedMemoryStridedIO,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size})>\n__launch_bounds__(FFTX::max_threads_per_block) __global__\n    void fft_2d_kernel(const ComplexType*            input,\n                       ComplexType*                  output,\n                       typename FFTX::workspace_type workspace_x,\n                       typename FFTY::workspace_type workspace_y) {\n    using complex_type  = ComplexType;\n    using dim_desc_type = example::dimension_description;\n    using io_y_type     = example::io_generic_strided<FFTY>;\n    using io_x_type     = example::io_generic_strided<FFTX>;\n\n    // Shared memory\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[RequiredStorageSize];\n\n    // FFTY\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    if(blockIdx.x < (cufftdx::size_of<FFTX>::value / FFTX::ffts_per_block)) {\n        // Load data from global memory to registers\n        io_y_type::load_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            input, thread_data, local_fft_id);\n\n        // Execute FFTY\n        FFTY().execute(thread_data, shared_mem, workspace_y);\n\n        // Save results\n        io_y_type::store_strided<dim_desc_type::Y, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(\n            thread_data, output, local_fft_id);\n    }\n\n    // Synchronize the whole CUDA Grid\n    cooperative_groups::grid_group grid = cooperative_groups::this_grid();\n    grid.sync();\n\n    // FFTX\n    if(blockIdx.x < (cufftdx::size_of<FFTY>::value / FFTY::ffts_per_block)) {\n        // Load data from global memory to registers\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, shared_mem, local_fft_id);\n        } else {\n            io_x_type::load_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(output, thread_data, local_fft_id);\n        }\n\n        // Execute FFTX\n        FFTX().execute(thread_data, shared_mem, workspace_x);\n\n        // Save results\n        if constexpr (UseSharedMemoryStridedIO) {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, shared_mem, output, local_fft_id);\n        } else {\n            io_x_type::store_strided<\n                dim_desc_type::X, cufftdx::size_of<FFTX>::value, cufftdx::size_of<FFTY>::value>(thread_data, output, local_fft_id);\n        }\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x,\n                                     unsigned int fft_size_y,\n                                     T*           input,\n                                     T*           output,\n                                     hipStream_t stream) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan, fft_size_x, fft_size_y, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    };\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, hipStream_t stream) {\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    // Checks that FFTX and FFTY can execute in the same kernel\n    static_assert((FFTX::block_dim.x == FFTY::block_dim.x) && (FFTX::block_dim.y == FFTY::block_dim.y),\n                  \"Required block dimensions for FFTX and FFTY must be the same\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    const unsigned int fft_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size,\n                            FFTY::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type),\n                            FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    const unsigned int fft_shared_memory =\n        UseSharedMemoryStridedIO ? fft_shared_memory_smem_io\n                                 : std::max<unsigned>({FFTX::shared_memory_size, FFTY::shared_memory_size});\n    const void* kernel = (const void*)fft_2d_kernel<FFTX, FFTY, UseSharedMemoryStridedIO, complex_type>;\n    auto error_code    = hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, fft_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    const dim3 block_dim = FFTX::block_dim;\n    const dim3 grid_size = {\n        std::max<unsigned>({(fft_size_y / FFTY::ffts_per_block), (fft_size_x / FFTX::ffts_per_block)}), 1, 1};\n    typename FFTX::workspace_type workspace_x_device = workspace_x;\n    typename FFTY::workspace_type workspace_y_device = workspace_y;\n    void*                         args[] = {&cufftdx_input, &cufftdx_output, &workspace_x_device, &workspace_y_device};\n    // Define 2D FFT execution\n    auto fft_2d_execution = [&](hipStream_t stream) {\n        CUDA_CHECK_AND_EXIT(hipLaunchCooperativeKernel(reinterpret_cast<const void*>(kernel), grid_size, block_dim, args, fft_shared_memory, stream));\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t   flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t   flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in a single kernel using a cooperative grid kernel launch.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\n// * cudaLaunchCooperativeKernel puts restrictions on how big the FFT can be. All batches must be able to execute at the same time\n// on the GPU.\n// * The best results are for a square FFTs (fft_size_x == fft_size_y).\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type = float;\n    using complex_type   = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    // We can use irregular FFT sizes because we will set the block dim to the bigger one\n    static constexpr unsigned int fft_size_y = 80;\n    static constexpr unsigned int fft_size_x = 100;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 10;\n    static constexpr unsigned int fpb_y = 10;\n    static constexpr unsigned int ept_x = 10;\n    static constexpr unsigned int fpb_x = fpb_y; // fpb for X and Y dimensions must be the same due to how IO is implemented\n\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y_base    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x_base    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    // Compare block dims and get the maximum\n    constexpr unsigned int block_dim_x = std::max(fft_y_base::block_dim.x, fft_x_base::block_dim.x);\n    // Thanks to the block dim operator we can fuse FFTs of different block dim sizes.\n    using fft_y      = decltype(fft_y_base() + BlockDim<block_dim_x, fft_y_base::block_dim.y,1>()); // 1 is the number of blocks in the y dimension\n    using fft_x      = decltype(fft_x_base() + BlockDim<block_dim_x, fft_x_base::block_dim.y,1>());\n    // Print block dims\n    std::cout << \"Block dims: \" << block_dim_x << \"\\n\";\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, input_host.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output,\n                                                                                 cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        for (size_t i = 0; i < flat_fft_size; i++) {\n            std::cout << i << \": \" << cufftdx_results.output[i].x << \" \" << cufftdx_results.output[i].y << \" \" << cufft_results.output[i].x << \" \" << cufft_results.output[i].y << \"\\n\";\n        }\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_2d_single_kernel_block_dim.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <sstream>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, unsigned int SizeZ,\n         class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_3d_kernel_z(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT, bool UseSharedMemoryStridedIO, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, unsigned int SizeZ,\n         class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_3d_kernel_yx(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_3d(unsigned int fft_size_x, unsigned int fft_size_y, unsigned int fft_size_z, T* input, T* output, cudaStream_t stream) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */){\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    const size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\ntemplate<class FFTX, class FFTY, class FFTZ, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_3d(T* input, T* output, cudaStream_t stream) {\n    using dim_desc_type                      = example::dimension_description;\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_z = cufftdx::size_of<FFTZ>::value;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTX and FFTY must have the same precision\");\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTY>, cufftdx::precision_of_t<FFTZ>>,\n                  \"FFTY and FFTZ must have the same precision\");\n\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTX and FFTY must operator on the same type\");\n    static_assert(std::is_same_v<typename FFTY::value_type, typename FFTZ::value_type>,\n                  \"FFTY and FFTZ must operator on the same type\");\n\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert(((fft_size_x * fft_size_y) % FFTZ::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTZ must divide X * Y number of batches as IO doesn't check if a batch is in range\");\n    static_assert(((fft_size_x * fft_size_z) % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTY must divide X * Z number of batches as IO doesn't check if a batch is in range\");\n    static_assert(((fft_size_z * fft_size_y) % FFTX::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Z * Y number of batches as IO doesn't check if a batch is in range\");\n\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Set shared memory requirements\n    // FFT -> Z\n    auto error_code = cudaFuncSetAttribute(\n        fft_3d_kernel_z<FFTZ, dim_desc_type::Z, fft_size_x, fft_size_y, fft_size_z, complex_type>, cudaFuncAttributeMaxDynamicSharedMemorySize, FFTZ::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // FFT -> Y\n    unsigned int fft_y_shared_memory_smem_io =\n        std::max<unsigned>({FFTY::shared_memory_size, FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    unsigned int fft_y_shared_memory =\n        UseSharedMemoryStridedIO ? fft_y_shared_memory_smem_io : FFTY::shared_memory_size;\n    error_code = cudaFuncSetAttribute(fft_3d_kernel_yx<FFTY, UseSharedMemoryStridedIO, dim_desc_type::Y,\n                                          fft_size_x, fft_size_y, fft_size_z, complex_type>,\n                                      cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_y_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // FFT -> X\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size, FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = cudaFuncSetAttribute(fft_3d_kernel_yx<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X,\n                                          fft_size_x, fft_size_y, fft_size_z, complex_type>,\n                                      cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_z = cufftdx::make_workspace<FFTZ>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Define 3D FFT execution\n    const auto grid_fft_size_z = ((fft_size_y * fft_size_x + FFTZ::ffts_per_block - 1) / FFTZ::ffts_per_block);\n    const auto grid_fft_size_y = ((fft_size_z * fft_size_x + FFTY::ffts_per_block - 1) / FFTY::ffts_per_block);\n    const auto grid_fft_size_x = ((fft_size_y * fft_size_z + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    auto fft_3d_execution = [&](cudaStream_t stream) {\n        // FFT -> Z\n        fft_3d_kernel_z<FFTZ, dim_desc_type::Z, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_z, FFTZ::block_dim, FFTZ::shared_memory_size, stream>>>(\n                cufftdx_input, cufftdx_output, workspace_z);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n        // FFT -> Y\n        fft_3d_kernel_yx<FFTY, UseSharedMemoryStridedIO, dim_desc_type::Y, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_y, FFTY::block_dim, fft_y_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_y);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n        // FFT -> X\n        fft_3d_kernel_yx<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_x);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_3d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_3d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n\n}\n\n// Example showing how cuFFTDx can be used to perform a 3D FFT in 3 kernels. The first kernel performs FFT along the contiguous\n// dimension (Z), and the 2nd (Y) and 3rd (X) along the strided one.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_3d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_z = 32;\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_z = 16;\n    static constexpr unsigned int fpb_z = 4;\n    static constexpr unsigned int ept_y = 32;\n    static constexpr unsigned int fpb_y = 2;\n    static constexpr unsigned int ept_x = 32;\n    static constexpr unsigned int fpb_x = 2;\n\n    // Other recommended configurations to test:\n    // 1.\n    // static constexpr unsigned int fft_size_z = 32;\n    // static constexpr unsigned int fft_size_y = 512;\n    // static constexpr unsigned int fft_size_x = 1024;\n    // static constexpr unsigned int ept_z = 16;\n    // static constexpr unsigned int fpb_z = 1;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 1;\n    // 2.\n    // static constexpr unsigned int fft_size_z = 16;\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 1024;\n    // static constexpr unsigned int ept_z = 8;\n    // static constexpr unsigned int fpb_z = 4;\n    // static constexpr unsigned int ept_y = 32;\n    // static constexpr unsigned int fpb_y = 2;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 3.\n    // static constexpr unsigned int fft_size_z = 4;\n    // static constexpr unsigned int fft_size_y = 8;\n    // static constexpr unsigned int fft_size_x = 16;\n    // static constexpr unsigned int ept_z = 2;\n    // static constexpr unsigned int fpb_z = 1;\n    // static constexpr unsigned int ept_y = 4;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 1;\n    // 4.\n    // static constexpr unsigned int fft_size_z = 4;\n    // static constexpr unsigned int fft_size_y = 8;\n    // static constexpr unsigned int fft_size_x = 16;\n    // static constexpr unsigned int ept_z = 2;\n    // static constexpr unsigned int fpb_z = 2;\n    // static constexpr unsigned int ept_y = 4;\n    // static constexpr unsigned int fpb_y = 2;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 2;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_z    = decltype(fft_base() + Size<fft_size_z>() + ElementsPerThread<ept_z>() + FFTsPerBlock<fpb_z>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        input_host[i] = complex_type {float(i), float(1)};\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, input_host.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx 3D\n    auto cufftdx_results = cufftdx_fft_3d<fft_x, fft_y, fft_z, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_3d<fft_x, fft_y, fft_z, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_3d(fft_size_x, fft_size_y, fft_size_z, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \", \" << fft_size_z << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_3d_functor {\n    void operator()() { return fft_3d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_3d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <sstream>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, unsigned int SizeZ,\n         class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_3d_kernel_z(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT, bool UseSharedMemoryStridedIO, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, unsigned int SizeZ,\n         class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_3d_kernel_yx(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        io_type::load_strided<Dimension, SizeX, SizeY, SizeZ>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        io_type::store_strided<Dimension, SizeX, SizeY, SizeZ>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_3d(unsigned int fft_size_x, unsigned int fft_size_y, unsigned int fft_size_z, T* input, T* output, hipStream_t stream) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */){\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    const size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\ntemplate<class FFTX, class FFTY, class FFTZ, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_3d(T* input, T* output, hipStream_t stream) {\n    using dim_desc_type                      = example::dimension_description;\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_z = cufftdx::size_of<FFTZ>::value;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTX and FFTY must have the same precision\");\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTY>, cufftdx::precision_of_t<FFTZ>>,\n                  \"FFTY and FFTZ must have the same precision\");\n\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTX and FFTY must operator on the same type\");\n    static_assert(std::is_same_v<typename FFTY::value_type, typename FFTZ::value_type>,\n                  \"FFTY and FFTZ must operator on the same type\");\n\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert(((fft_size_x * fft_size_y) % FFTZ::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTZ must divide X * Y number of batches as IO doesn't check if a batch is in range\");\n    static_assert(((fft_size_x * fft_size_z) % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTY must divide X * Z number of batches as IO doesn't check if a batch is in range\");\n    static_assert(((fft_size_z * fft_size_y) % FFTX::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Z * Y number of batches as IO doesn't check if a batch is in range\");\n\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Set shared memory requirements\n    // FFT -> Z\n    auto error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_3d_kernel_z<FFTZ, dim_desc_type::Z, fft_size_x, fft_size_y, fft_size_z, complex_type>), hipFuncAttributeMaxDynamicSharedMemorySize, FFTZ::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // FFT -> Y\n    unsigned int fft_y_shared_memory_smem_io =\n        std::max<unsigned>({FFTY::shared_memory_size, FFTY::ffts_per_block * fft_size_y * sizeof(complex_type)});\n    unsigned int fft_y_shared_memory =\n        UseSharedMemoryStridedIO ? fft_y_shared_memory_smem_io : FFTY::shared_memory_size;\n    error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_3d_kernel_yx<FFTY, UseSharedMemoryStridedIO, dim_desc_type::Y,\n                                          fft_size_x, fft_size_y, fft_size_z, complex_type>),\n                                      hipFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_y_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // FFT -> X\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size, FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_3d_kernel_yx<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X,\n                                          fft_size_x, fft_size_y, fft_size_z, complex_type>),\n                                      hipFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_z = cufftdx::make_workspace<FFTZ>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Define 3D FFT execution\n    const auto grid_fft_size_z = ((fft_size_y * fft_size_x + FFTZ::ffts_per_block - 1) / FFTZ::ffts_per_block);\n    const auto grid_fft_size_y = ((fft_size_z * fft_size_x + FFTY::ffts_per_block - 1) / FFTY::ffts_per_block);\n    const auto grid_fft_size_x = ((fft_size_y * fft_size_z + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    auto fft_3d_execution = [&](hipStream_t stream) {\n        // FFT -> Z\n        fft_3d_kernel_z<FFTZ, dim_desc_type::Z, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_z, FFTZ::block_dim, FFTZ::shared_memory_size, stream>>>(\n                cufftdx_input, cufftdx_output, workspace_z);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n        // FFT -> Y\n        fft_3d_kernel_yx<FFTY, UseSharedMemoryStridedIO, dim_desc_type::Y, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_y, FFTY::block_dim, fft_y_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_y);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n        // FFT -> X\n        fft_3d_kernel_yx<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, fft_size_z, complex_type>\n            <<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_x);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_3d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_3d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n\n}\n\n// Example showing how cuFFTDx can be used to perform a 3D FFT in 3 kernels. The first kernel performs FFT along the contiguous\n// dimension (Z), and the 2nd (Y) and 3rd (X) along the strided one.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_3d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_z = 32;\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_z = 16;\n    static constexpr unsigned int fpb_z = 4;\n    static constexpr unsigned int ept_y = 32;\n    static constexpr unsigned int fpb_y = 2;\n    static constexpr unsigned int ept_x = 32;\n    static constexpr unsigned int fpb_x = 2;\n\n    // Other recommended configurations to test:\n    // 1.\n    // static constexpr unsigned int fft_size_z = 32;\n    // static constexpr unsigned int fft_size_y = 512;\n    // static constexpr unsigned int fft_size_x = 1024;\n    // static constexpr unsigned int ept_z = 16;\n    // static constexpr unsigned int fpb_z = 1;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 1;\n    // 2.\n    // static constexpr unsigned int fft_size_z = 16;\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 1024;\n    // static constexpr unsigned int ept_z = 8;\n    // static constexpr unsigned int fpb_z = 4;\n    // static constexpr unsigned int ept_y = 32;\n    // static constexpr unsigned int fpb_y = 2;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 3.\n    // static constexpr unsigned int fft_size_z = 4;\n    // static constexpr unsigned int fft_size_y = 8;\n    // static constexpr unsigned int fft_size_x = 16;\n    // static constexpr unsigned int ept_z = 2;\n    // static constexpr unsigned int fpb_z = 1;\n    // static constexpr unsigned int ept_y = 4;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 1;\n    // 4.\n    // static constexpr unsigned int fft_size_z = 4;\n    // static constexpr unsigned int fft_size_y = 8;\n    // static constexpr unsigned int fft_size_x = 16;\n    // static constexpr unsigned int ept_z = 2;\n    // static constexpr unsigned int fpb_z = 2;\n    // static constexpr unsigned int ept_y = 4;\n    // static constexpr unsigned int fpb_y = 2;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 2;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_z    = decltype(fft_base() + Size<fft_size_z>() + ElementsPerThread<ept_z>() + FFTsPerBlock<fpb_z>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        input_host[i] = complex_type {float(i), float(1)};\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, input_host.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx 3D\n    auto cufftdx_results = cufftdx_fft_3d<fft_x, fft_y, fft_z, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_3d<fft_x, fft_y, fft_z, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_3d(fft_size_x, fft_size_y, fft_size_z, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \", \" << fft_size_z << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_3d_functor {\n    void operator()() { return fft_3d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_3d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_3d.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_y(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT, bool UseSharedMemoryStridedIO, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_x(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x, unsigned int fft_size_y, T* input, T* output, cudaStream_t stream) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan, fft_size_x, fft_size_y, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */){\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, cufft_input, cufft_output, CUFFT_FORWARD));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, cudaStream_t stream) {\n    using dim_desc_type                      = example::dimension_description;\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Set shared memory requirements\n    auto error_code = cudaFuncSetAttribute(\n        fft_2d_kernel_y<FFTY, dim_desc_type::Y, fft_size_x, fft_size_y, complex_type>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize, FFTY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size, FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = cudaFuncSetAttribute(fft_2d_kernel_x<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, complex_type>,\n                                      cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Define 2D FFT execution\n    const auto grid_fft_size_y = ((fft_size_x + FFTY::ffts_per_block - 1) / FFTY::ffts_per_block);\n    const auto grid_fft_size_x = ((fft_size_y + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    auto fft_2d_execution = [&](cudaStream_t stream) {\n        fft_2d_kernel_y<FFTY, dim_desc_type::Y, fft_size_x, fft_size_y, complex_type>\n            <<<grid_fft_size_y, FFTY::block_dim, FFTY::shared_memory_size, stream>>>(\n                cufftdx_input, cufftdx_output, workspace_y);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n        fft_2d_kernel_x<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, complex_type>\n            <<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_x);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in 2 kernels. The first kernel performs FFT along the contiguous\n// dimension (Y), and the 2nd along the strided one.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 16;\n    static constexpr unsigned int fpb_y = 1;\n    static constexpr unsigned int ept_x = 16;\n    static constexpr unsigned int fpb_x = 8;\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 16384;\n    // static constexpr unsigned int fft_size_x = 4096;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 2048;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n    // 3:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 8;\n    // 4:\n    // static constexpr unsigned int fft_size_y = 1281;\n    // static constexpr unsigned int fft_size_x = 721;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, input_host.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io_generic_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_y(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFT, bool UseSharedMemoryStridedIO, example::dimension_description Dimension,\n         unsigned int SizeX, unsigned int SizeY, class ComplexType = typename FFT::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_x(const ComplexType* input, ComplexType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n    using io_type      = example::io_generic_strided<FFT>;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        io_type::load_strided<Dimension, SizeX, SizeY>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        io_type::store_strided<Dimension, SizeX, SizeY>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class T>\nexample::fft_results<T> cufft_fft_2d(unsigned int fft_size_x, unsigned int fft_size_y, T* input, T* output, hipStream_t stream) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n\n    complex_type* cufft_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufft_output = reinterpret_cast<complex_type*>(output);\n\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan, fft_size_x, fft_size_y, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */){\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, cufft_input, cufft_output, HIPFFT_FORWARD));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<T> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\ntemplate<class FFTX, class FFTY, bool UseSharedMemoryStridedIO, class T>\nexample::fft_results<T> cufftdx_fft_2d(T* input, T* output, hipStream_t stream) {\n    using dim_desc_type                      = example::dimension_description;\n    using complex_type                       = typename FFTX::value_type;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(T) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<T> == std::alignment_of_v<complex_type>, \"\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n\n    // Set shared memory requirements\n    auto error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_2d_kernel_y<FFTY, dim_desc_type::Y, fft_size_x, fft_size_y, complex_type>),\n        hipFuncAttributeMaxDynamicSharedMemorySize, FFTY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Shared memory IO for strided kernel may require more memory than FFTX::shared_memory_size.\n    // Note: For some fft_size_x and depending on GPU architecture fft_x_shared_memory_smem_io may exceed max shared\n    // memory and cudaFuncSetAttribute will fail.\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTX::shared_memory_size, FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_2d_kernel_x<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, complex_type>),\n                                      hipFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y = cufftdx::make_workspace<FFTY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x = cufftdx::make_workspace<FFTX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Define 2D FFT execution\n    const auto grid_fft_size_y = ((fft_size_x + FFTY::ffts_per_block - 1) / FFTY::ffts_per_block);\n    const auto grid_fft_size_x = ((fft_size_y + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    auto fft_2d_execution = [&](hipStream_t stream) {\n        fft_2d_kernel_y<FFTY, dim_desc_type::Y, fft_size_x, fft_size_y, complex_type>\n            <<<grid_fft_size_y, FFTY::block_dim, FFTY::shared_memory_size, stream>>>(\n                cufftdx_input, cufftdx_output, workspace_y);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n        fft_2d_kernel_x<FFTX, UseSharedMemoryStridedIO, dim_desc_type::X, fft_size_x, fft_size_y, complex_type>\n            <<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n                cufftdx_output, cufftdx_output, workspace_x);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n    std::vector<complex_type> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<T>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT in 2 kernels. The first kernel performs FFT along the contiguous\n// dimension (Y), and the 2nd along the strided one.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n\n    // FFT Sizes\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 16;\n    static constexpr unsigned int fpb_y = 1;\n    static constexpr unsigned int ept_x = 16;\n    static constexpr unsigned int fpb_x = 8;\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 16384;\n    // static constexpr unsigned int fft_size_x = 4096;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 2048;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n    // 3:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 8;\n    // 4:\n    // static constexpr unsigned int fft_size_y = 1281;\n    // static constexpr unsigned int fft_size_x = 721;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n\n    using namespace cufftdx;\n    using fft_base = decltype(Block() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                              Precision<precision_type>() + SM<Arch>());\n    using fft_y    = decltype(fft_base() + Size<fft_size_y>() + ElementsPerThread<ept_y>() + FFTsPerBlock<fpb_y>());\n    using fft_x    = decltype(fft_base() + Size<fft_size_x>() + ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft      = fft_y;\n\n    // Host data\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<complex_type> input_host(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i].x = sign * static_cast<float>(i) / flat_fft_size;\n        input_host[i].y = sign * static_cast<float>(i) / flat_fft_size;\n    }\n#else\n    auto input_host = example::get_random_complex_data<precision_type>(flat_fft_size, -1, 1);\n#endif\n\n    // Device data\n    complex_type* input;\n    complex_type* output;\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemset(output, 0b11111111, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, input_host.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx 2D\n    auto cufftdx_results = cufftdx_fft_2d<fft_x, fft_y, false>(input, output, stream);\n\n    // cuFFTDx 2D\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results = cufftdx_fft_2d<fft_x, fft_y, true>(input, output, stream);\n\n    // cuFFT as reference\n    auto cufft_results = cufft_fft_2d(fft_size_x, fft_size_y, input, output, stream);\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    std::cout << \"Correctness results:\\n\";\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        if(success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"\\nSuccess\\n\";\n    } else {\n        std::cout << \"\\nFailure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_2d.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 50;\n\ntemplate<unsigned int MaxThreadsPerBlock,\n         class FFTX,\n         class FFTY,\n         class FFTZ,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size, FFTZ::storage_size})>\n__launch_bounds__(MaxThreadsPerBlock) __global__\n    void cufftdx_3d_fft_single_block_kernel(const ComplexType* input, ComplexType* output) {\n    using complex_type                       = ComplexType;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_z = cufftdx::size_of<FFTZ>::value;\n\n    // Local array for thread\n    constexpr auto required_storage_size = RequiredStorageSize;\n    complex_type   thread_data[required_storage_size];\n\n    // Shared memory use for exchanging data between threads\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n\n    // Load data from global memory to registers.\n    static constexpr unsigned int stride_x = fft_size_y * fft_size_z;\n    unsigned int                  index    = (threadIdx.x + threadIdx.y * fft_size_y);\n    if (threadIdx.x < fft_size_y && threadIdx.y < fft_size_z) {\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            thread_data[i] = input[index];\n            index += stride_x;\n        }\n\n        // Execute FFT in X dimension\n        FFTX().execute(thread_data);\n\n        // Exchange/transpose via shared memory\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += stride_x;\n        }\n    }\n\n    __syncthreads();\n    static constexpr unsigned int stride_y = fft_size_z;\n    index                                  = threadIdx.x + threadIdx.y * fft_size_y * fft_size_z;\n    if (threadIdx.x < fft_size_z && threadIdx.y < fft_size_x) {\n        for (size_t i = 0; i < FFTY::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += stride_y;\n        }\n\n        // Execute FFT in Y dimension\n        FFTY().execute(thread_data);\n\n        // Exchange/transpose via shared memory\n        index = threadIdx.x + threadIdx.y * fft_size_y * fft_size_z;\n        for (size_t i = 0; i < FFTY::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += stride_y;\n        }\n    }\n\n    __syncthreads();\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += 1;\n        }\n        // Execute FFT in Z dimension\n        FFTZ().execute(thread_data);\n    }\n\n    // Save results\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n    // Simple IO with poor global memory pattern:\n    // Storing the data with stride=1 results in poor global memory\n    // write pattern with little or none coalescing\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            output[index] = thread_data[i];\n            index += 1;\n        }\n    }\n#else\n    // Shared memory IO:\n    // Exchanging data via shared memory results in a much better global\n    // memory patter with good coalescing\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += 1;\n        }\n    }\n    __syncthreads();\n    if (threadIdx.x < fft_size_y && threadIdx.y < fft_size_z) {\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += stride_x;\n        }\n\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            output[index] = thread_data[i];\n            index += stride_x;\n        }\n    }\n#endif\n}\n\nexample::fft_results<float2> cufft_3d_fft(unsigned int  fft_size_x,\n                                          unsigned int  fft_size_y,\n                                          unsigned int  fft_size_z,\n                                          cufftComplex* input,\n                                          cufftComplex* output,\n                                          cudaStream_t  stream) {\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, input, output, CUFFT_FORWARD))\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t        flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    const size_t        flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    std::vector<float2> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n\n    // Return results\n    return example::fft_results<float2> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<unsigned int FFTSizeX, unsigned int FFTSizeY, unsigned int FFTSizeZ>\nexample::fft_results<float2> cufftdx_3d_fft_single_block(float2* input, float2* output, cudaStream_t stream) {\n    using namespace cufftdx;\n\n    static constexpr unsigned int fft_size_x = FFTSizeX;\n    static constexpr unsigned int fft_size_y = FFTSizeY;\n    static constexpr unsigned int fft_size_z = FFTSizeZ;\n\n    using fft_base =\n        decltype(Thread() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() + Precision<float>());\n    using fft_x        = decltype(fft_base() + Size<fft_size_x>());\n    using fft_y        = decltype(fft_base() + Size<fft_size_y>());\n    using fft_z        = decltype(fft_base() + Size<fft_size_z>());\n    using complex_type = typename fft_x::value_type;\n\n    constexpr unsigned int max_dim               = std::max({fft_size_x, fft_size_y, fft_size_z});\n    constexpr dim3         block_dim             = {max_dim, max_dim, 1};\n    constexpr unsigned int max_threads_per_block = block_dim.x * block_dim.y * block_dim.z;\n    const size_t           shared_memory_size    = (fft_size_x * fft_size_y * fft_size_z) * sizeof(complex_type);\n\n    const auto kernel = cufftdx_3d_fft_single_block_kernel<max_threads_per_block, fft_x, fft_y, fft_z, complex_type>;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n    auto fft_3d_execution = [&](cudaStream_t stream) {\n        kernel<<<1, block_dim, shared_memory_size, stream>>>(cufftdx_input, cufftdx_output);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_3d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    std::vector<float2> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), cufftdx_output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_3d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<float2>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\n// Example showing how to perform a small 3D FFT in a single CUDA block using cuFFTDx thread execution.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run a multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\nint main(int, char**) {\n    // 3D FFT\n    static constexpr unsigned int fft_size_x = 16;\n    static constexpr unsigned int fft_size_y = 15;\n    static constexpr unsigned int fft_size_z = 14;\n\n    // Generate random input data on host\n    const unsigned int flat_fft_size = fft_size_x * fft_size_y * fft_size_z;\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<float2> host_input(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        host_input[i].x = sign * static_cast<float>(i) / 100;\n        host_input[i].y = sign * static_cast<float>(i) / 100;\n    }\n#else\n    auto host_input = example::get_random_complex_data<float>(flat_fft_size, -1, 1);\n#endif\n\n    // Allocate managed memory for device input/output\n    // float2 has the same size and alignment as cuFFTDx fp32 complex type cufftdx::complex<float> and cufftComplex\n    float2*    input;\n    float2*    output;\n    const auto flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Copy input to the device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, host_input.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Run cuFFT\n    auto cufft_results = cufft_3d_fft(fft_size_x, fft_size_y, fft_size_z, input, output, stream);\n\n    // Run cuFFTDx\n    auto cufftdx_results = cufftdx_3d_fft_single_block<fft_size_x, fft_size_y, fft_size_z>(input, output, stream);\n\n    // Clean-up\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Check if cuFFTDx results are correct\n    auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \", \" << fft_size_z <<\")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = fft_error.l2_relative_error < 0.001;\n    std::cout << \"Correctness results:\\n\";\n    std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n    std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(10) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(10) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"Success\\n\";\n        return 0;\n    } else {\n        std::cout << \"Failure\\n\";\n        return 1;\n    }\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 50;\n\ntemplate<unsigned int MaxThreadsPerBlock,\n         class FFTX,\n         class FFTY,\n         class FFTZ,\n         class ComplexType                = typename FFTX::value_type,\n         unsigned int RequiredStorageSize = std::max({FFTX::storage_size, FFTY::storage_size, FFTZ::storage_size})>\n__launch_bounds__(MaxThreadsPerBlock) __global__\n    void cufftdx_3d_fft_single_block_kernel(const ComplexType* input, ComplexType* output) {\n    using complex_type                       = ComplexType;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_z = cufftdx::size_of<FFTZ>::value;\n\n    // Local array for thread\n    constexpr auto required_storage_size = RequiredStorageSize;\n    complex_type   thread_data[required_storage_size];\n\n    // Shared memory use for exchanging data between threads\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n\n    // Load data from global memory to registers.\n    static constexpr unsigned int stride_x = fft_size_y * fft_size_z;\n    unsigned int                  index    = (threadIdx.x + threadIdx.y * fft_size_y);\n    if (threadIdx.x < fft_size_y && threadIdx.y < fft_size_z) {\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            thread_data[i] = input[index];\n            index += stride_x;\n        }\n\n        // Execute FFT in X dimension\n        FFTX().execute(thread_data);\n\n        // Exchange/transpose via shared memory\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += stride_x;\n        }\n    }\n\n    __syncthreads();\n    static constexpr unsigned int stride_y = fft_size_z;\n    index                                  = threadIdx.x + threadIdx.y * fft_size_y * fft_size_z;\n    if (threadIdx.x < fft_size_z && threadIdx.y < fft_size_x) {\n        for (size_t i = 0; i < FFTY::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += stride_y;\n        }\n\n        // Execute FFT in Y dimension\n        FFTY().execute(thread_data);\n\n        // Exchange/transpose via shared memory\n        index = threadIdx.x + threadIdx.y * fft_size_y * fft_size_z;\n        for (size_t i = 0; i < FFTY::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += stride_y;\n        }\n    }\n\n    __syncthreads();\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += 1;\n        }\n        // Execute FFT in Z dimension\n        FFTZ().execute(thread_data);\n    }\n\n    // Save results\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n    // Simple IO with poor global memory pattern:\n    // Storing the data with stride=1 results in poor global memory\n    // write pattern with little or none coalescing\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            output[index] = thread_data[i];\n            index += 1;\n        }\n    }\n#else\n    // Shared memory IO:\n    // Exchanging data via shared memory results in a much better global\n    // memory patter with good coalescing\n    if (threadIdx.x < fft_size_x && threadIdx.y < fft_size_y) {\n        index = (threadIdx.x + threadIdx.y * fft_size_x) * fft_size_z;\n        for (size_t i = 0; i < FFTZ::elements_per_thread; i++) {\n            shared_memory[index] = thread_data[i];\n            index += 1;\n        }\n    }\n    __syncthreads();\n    if (threadIdx.x < fft_size_y && threadIdx.y < fft_size_z) {\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            thread_data[i] = shared_memory[index];\n            index += stride_x;\n        }\n\n        index = (threadIdx.x + threadIdx.y * fft_size_y);\n        for (size_t i = 0; i < FFTX::elements_per_thread; i++) {\n            output[index] = thread_data[i];\n            index += stride_x;\n        }\n    }\n#endif\n}\n\nexample::fft_results<float2> cufft_3d_fft(unsigned int  fft_size_x,\n                                          unsigned int  fft_size_y,\n                                          unsigned int  fft_size_z,\n                                          hipfftComplex* input,\n                                          hipfftComplex* output,\n                                          hipStream_t  stream) {\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */) {\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, input, output, HIPFFT_FORWARD))\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t        flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    const size_t        flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    std::vector<float2> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n\n    // Return results\n    return example::fft_results<float2> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<unsigned int FFTSizeX, unsigned int FFTSizeY, unsigned int FFTSizeZ>\nexample::fft_results<float2> cufftdx_3d_fft_single_block(float2* input, float2* output, hipStream_t stream) {\n    using namespace cufftdx;\n\n    static constexpr unsigned int fft_size_x = FFTSizeX;\n    static constexpr unsigned int fft_size_y = FFTSizeY;\n    static constexpr unsigned int fft_size_z = FFTSizeZ;\n\n    using fft_base =\n        decltype(Thread() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() + Precision<float>());\n    using fft_x        = decltype(fft_base() + Size<fft_size_x>());\n    using fft_y        = decltype(fft_base() + Size<fft_size_y>());\n    using fft_z        = decltype(fft_base() + Size<fft_size_z>());\n    using complex_type = typename fft_x::value_type;\n\n    constexpr unsigned int max_dim               = std::max({fft_size_x, fft_size_y, fft_size_z});\n    constexpr dim3         block_dim             = {max_dim, max_dim, 1};\n    constexpr unsigned int max_threads_per_block = block_dim.x * block_dim.y * block_dim.z;\n    const size_t           shared_memory_size    = (fft_size_x * fft_size_y * fft_size_z) * sizeof(complex_type);\n\n    const auto kernel = cufftdx_3d_fft_single_block_kernel<max_threads_per_block, fft_x, fft_y, fft_z, complex_type>;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n    auto fft_3d_execution = [&](hipStream_t stream) {\n        kernel<<<1, block_dim, shared_memory_size, stream>>>(cufftdx_input, cufftdx_output);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_3d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y * fft_size_z;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    std::vector<float2> output_host(flat_fft_size, {std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()});\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), cufftdx_output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_3d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<float2>{ output_host, (time / cufftdx_example_performance_runs) };\n}\n\n// Example showing how to perform a small 3D FFT in a single CUDA block using cuFFTDx thread execution.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run a multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\nint main(int, char**) {\n    // 3D FFT\n    static constexpr unsigned int fft_size_x = 16;\n    static constexpr unsigned int fft_size_y = 15;\n    static constexpr unsigned int fft_size_z = 14;\n\n    // Generate random input data on host\n    const unsigned int flat_fft_size = fft_size_x * fft_size_y * fft_size_z;\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<float2> host_input(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        host_input[i].x = sign * static_cast<float>(i) / 100;\n        host_input[i].y = sign * static_cast<float>(i) / 100;\n    }\n#else\n    auto host_input = example::get_random_complex_data<float>(flat_fft_size, -1, 1);\n#endif\n\n    // Allocate managed memory for device input/output\n    // float2 has the same size and alignment as cuFFTDx fp32 complex type cufftdx::complex<float> and cufftComplex\n    float2*    input;\n    float2*    output;\n    const auto flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Copy input to the device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, host_input.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Run cuFFT\n    auto cufft_results = cufft_3d_fft(fft_size_x, fft_size_y, fft_size_z, input, output, stream);\n\n    // Run cuFFTDx\n    auto cufftdx_results = cufftdx_3d_fft_single_block<fft_size_x, fft_size_y, fft_size_z>(input, output, stream);\n\n    // Clean-up\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Check if cuFFTDx results are correct\n    auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_results.output, cufft_results.output);\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \", \" << fft_size_z <<\")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_results.output[i].x << \", \" << cufft_results.output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_results.output[i].x << \", \" << cufftdx_results.output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = fft_error.l2_relative_error < 0.001;\n    std::cout << \"Correctness results:\\n\";\n    std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n    std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n    // Print performance results\n    if(success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(10) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(10) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"Success\\n\";\n        return 0;\n    } else {\n        std::cout << \"Failure\\n\";\n        return 1;\n    }\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_3d_box_single_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/block_io_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, class InputType, class OutputType>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_y(const InputType* input, OutputType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFTF,\n         class FFTI,\n         unsigned int Stride,\n         unsigned int SizeY,\n         bool         UseSharedMemoryStridedIO,\n         class ComplexType = typename FFTF::value_type>\n__launch_bounds__(FFTF::max_threads_per_block) __global__\n    void fft_2d_kernel_x(const ComplexType*            input,\n                         ComplexType*                  output,\n                         typename FFTF::workspace_type workspacef,\n                         typename FFTI::workspace_type workspacei) {\n    using complex_type = typename FFTF::value_type;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFTF::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        example::io_strided<FFTF>::load_strided<Stride, SizeY>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        example::io_strided<FFTF>::load_strided<Stride, SizeY>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT (part of the 2D R2C FFT)\n    FFTF().execute(thread_data, shared_mem, workspacef);\n\n    // Note: You can do any point-wise operation in here.\n\n    // Execute FFT (part of the 2D C2R FFT)\n    FFTI().execute(thread_data, shared_mem, workspacei);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        example::io_strided<FFTI>::store_strided<Stride, SizeY>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        example::io_strided<FFTI>::store_strided<Stride, SizeY>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class RealType, class ComplexType>\nexample::fft_results<RealType> cufft_fft_2d_r2c_c2r(unsigned int fft_size_x,\n                                             unsigned int fft_size_y,\n                                             RealType*           real_values,\n                                             ComplexType*           complex_values,\n                                             cudaStream_t stream) {\n    using complex_type = cufftComplex;\n    static_assert(sizeof(ComplexType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<ComplexType> == std::alignment_of_v<complex_type>, \"\");\n    using real_type = cufftReal;\n    static_assert(sizeof(ComplexType) % sizeof(real_type) == 0, \"\");\n    static_assert(std::alignment_of_v<ComplexType> % std::alignment_of_v<real_type> == 0, \"\");\n    static_assert(sizeof(RealType) == sizeof(real_type), \"\");\n    static_assert(std::alignment_of_v<RealType> == std::alignment_of_v<real_type>, \"\");\n\n    real_type*    cufft_input_r2c  = reinterpret_cast<real_type*>(real_values);\n    complex_type* cufft_output_r2c = reinterpret_cast<complex_type*>(complex_values);\n    complex_type* cufft_input_c2r  = cufft_output_r2c;\n    real_type*    cufft_output_c2r = reinterpret_cast<real_type*>(real_values);\n\n    // Create cuFFT plan\n    cufftHandle plan_r2c;\n    cufftHandle plan_c2r;\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan_r2c, fft_size_x, fft_size_y, CUFFT_R2C));\n    CUFFT_CHECK_AND_EXIT(cufftPlan2d(&plan_c2r, fft_size_x, fft_size_y, CUFFT_C2R));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_r2c, stream));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_c2r, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](cudaStream_t /* stream */) {\n        // Out-of-place R2C\n        CUFFT_CHECK_AND_EXIT(cufftExecR2C(plan_r2c, cufft_input_r2c, cufft_output_r2c));\n        // Out-of-place C2R\n        CUFFT_CHECK_AND_EXIT(cufftExecC2R(plan_c2r, cufft_input_c2r, cufft_output_c2r));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    const size_t           flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t           flat_fft_size_bytes = flat_fft_size * sizeof(real_type);\n    std::vector<real_type> output_host(flat_fft_size, std::numeric_limits<real_type>::quiet_NaN());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), cufft_output_c2r, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_r2c));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_c2r));\n\n    // Return results\n    return example::fft_results<RealType> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTR2CX, class FFTR2CY, class FFTC2RX, class FFTC2RY, bool UseSharedMemoryStridedIO, class RealType, class ComplexType>\nexample::fft_results<RealType> cufftdx_fft_2d_r2c_c2r(RealType* real_values, ComplexType* complex_values, cudaStream_t stream) {\n    using FFTX         = FFTR2CX;\n    using FFTY         = FFTR2CY;\n    using complex_type = typename FFTX::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(ComplexType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<ComplexType> == std::alignment_of_v<complex_type>, \"\");\n    static_assert(sizeof(RealType) == sizeof(real_type), \"\");\n    static_assert(std::alignment_of_v<RealType> == std::alignment_of_v<real_type>, \"\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    real_type*    cufftdx_real_values    = reinterpret_cast<real_type*>(real_values);\n    complex_type* cufftdx_complex_values = reinterpret_cast<complex_type*>(complex_values);\n\n    // Set shared memory requirements\n    auto error_code = cudaFuncSetAttribute(fft_2d_kernel_y<FFTR2CY, real_type, complex_type>,\n                                           cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                           FFTR2CY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n    error_code = cudaFuncSetAttribute(fft_2d_kernel_y<FFTC2RY, complex_type, real_type>,\n                                      cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                      FFTC2RY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTR2CX::shared_memory_size,\n                            FFTC2RX::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = cudaFuncSetAttribute(fft_2d_kernel_x<FFTR2CX,\n                                                      FFTC2RX,\n                                                      FFTY::output_length,\n                                                      FFTY::output_length,\n                                                      UseSharedMemoryStridedIO,\n                                                      complex_type>,\n                                      cudaFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y_r2c = cufftdx::make_workspace<FFTR2CY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x_r2c = cufftdx::make_workspace<FFTR2CX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_y_c2r = cufftdx::make_workspace<FFTC2RY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x_c2r = cufftdx::make_workspace<FFTC2RX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Out-of-place R2C kernel (R2C Y dimension)\n    const auto grid_fft_size_y_r2c = ((fft_size_x + FFTR2CY::ffts_per_block - 1) / FFTR2CY::ffts_per_block);\n    const auto grid_fft_size_x     = ((FFTY::output_length + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    const auto grid_fft_size_y_c2r = ((fft_size_x + FFTC2RY::ffts_per_block - 1) / FFTC2RY::ffts_per_block);\n    auto       fft_2d_execution    = [&](cudaStream_t stream) {\n        fft_2d_kernel_y<FFTY, real_type, complex_type>\n            <<<grid_fft_size_y_r2c, FFTY::block_dim, FFTY::shared_memory_size, stream>>>(\n                cufftdx_real_values, cufftdx_complex_values, workspace_y_r2c);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n        // In-place C2CF (R2C X dimension) and C2CI (C2R X dimension) kernel\n        fft_2d_kernel_x<FFTR2CX,\n                        FFTC2RX,\n                        FFTY::output_length,\n                        FFTY::output_length,\n                        UseSharedMemoryStridedIO,\n                        complex_type><<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n            cufftdx_complex_values, cufftdx_complex_values, workspace_x_r2c, workspace_x_c2r);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n        // Out-of-place C2R kernel (C2R Y dimension)\n        fft_2d_kernel_y<FFTC2RY, complex_type, real_type>\n            <<<grid_fft_size_y_c2r, FFTC2RY::block_dim, FFTC2RY::shared_memory_size, stream>>>(\n                cufftdx_complex_values, cufftdx_real_values, workspace_y_c2r);\n        CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(real_type);\n    std::vector<real_type>  output_host(flat_fft_size, std::numeric_limits<real_type>::quiet_NaN());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output_host.data(), cufftdx_real_values, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<RealType> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT R2C-C2R convolution in 3 kernels. In the 2nd kernel any scalar\n// operation can be performed on the FFT data.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n    using real_type                          = complex_type::value_type;\n\n    // FFT\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 16;\n    static constexpr unsigned int fpb_y = 1;\n    static constexpr unsigned int ept_x = 16;\n    static constexpr unsigned int fpb_x = 8;\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 16384;\n    // static constexpr unsigned int fft_size_x = 4096;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 2048;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n    // 3:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 8;\n\n    using namespace cufftdx;\n    using fft_base  = decltype(Block() + Precision<precision_type>() + SM<Arch>());\n    using fft_y_r2c = decltype(fft_base() + Type<fft_type::r2c>() + Size<fft_size_y>() + ElementsPerThread<ept_y>() +\n                               FFTsPerBlock<fpb_y>());\n    using fft_x_r2c =\n        decltype(fft_base() + Type<fft_type::c2c>() + Size<fft_size_x>() + Direction<fft_direction::forward>() +\n                 ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft_y_c2r = cufftdx::replace_t<fft_y_r2c, Type<fft_type::c2r>>;\n    using fft_x_c2r = cufftdx::replace_t<fft_x_r2c, Direction<fft_direction::inverse>>;\n\n    // Host data\n    static constexpr size_t flat_fft_size_real          = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_complex       = fft_size_x * fft_y_r2c::output_length;\n    static constexpr size_t flat_fft_size_real_bytes    = flat_fft_size_real * sizeof(precision_type);\n    static constexpr size_t flat_fft_size_complex_bytes = flat_fft_size_complex * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<precision_type> input_host(flat_fft_size_real);\n    for (size_t i = 0; i < flat_fft_size_real; i++) {\n        float sign    = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i] = sign * static_cast<float>(i) / flat_fft_size_real;\n    }\n#else\n    auto input_host = example::get_random_real_data<precision_type>(flat_fft_size_real, -1, 1);\n#endif\n\n    // Device data\n    real_type* real_values;\n    complex_type* complex_values;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&real_values, flat_fft_size_real_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&complex_values, flat_fft_size_complex_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemset(complex_values, 0b11111111, flat_fft_size_complex_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // cuFFTDx fused 2D R2C->C2R\n    // * the 2nd R2C (dim X) kernel and the 1st (dim X) C2R kernel are fused into one\n    // * the final results are stored in real_values\n    auto cufftdx_results =\n        cufftdx_fft_2d_r2c_c2r<fft_x_r2c, fft_y_r2c, fft_x_c2r, fft_y_c2r, false>(real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // cuFFTDx fused 2D R2C->C2R\n    // * the 2nd R2C (dim X) kernel and the 1st (dim X) C2R kernel are fused into one\n    // * the final results are stored in real_values\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results =\n        cufftdx_fft_2d_r2c_c2r<fft_x_r2c, fft_y_r2c, fft_x_c2r, fft_y_c2r, true>(real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // cuFFT 2D R2C->C2R\n    // * the final results are stored in real_values\n    auto cufft_results = cufft_fft_2d_r2c_c2r(fft_size_x, fft_size_y, real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(cudaFree(real_values));\n    CUDA_CHECK_AND_EXIT(cudaFree(complex_values));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_output_host[i] << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_output_host[i] << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_real_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        std::cout << \"Peak relative error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error_relative << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_real_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        std::cout << \"Peak relative error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error_relative << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"Success\\n\";\n    } else {\n        std::cout << \"Failure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <iomanip>\n#include <vector>\n#include <limits>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/block_io_strided.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\ninline constexpr unsigned int cufftdx_example_warm_up_runs = 5;\ninline constexpr unsigned int cufftdx_example_performance_runs = 20;\n\ntemplate<class FFT, class InputType, class OutputType>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void fft_2d_kernel_y(const InputType* input, OutputType* output, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output, local_fft_id);\n}\n\ntemplate<class FFTF,\n         class FFTI,\n         unsigned int Stride,\n         unsigned int SizeY,\n         bool         UseSharedMemoryStridedIO,\n         class ComplexType = typename FFTF::value_type>\n__launch_bounds__(FFTF::max_threads_per_block) __global__\n    void fft_2d_kernel_x(const ComplexType*            input,\n                         ComplexType*                  output,\n                         typename FFTF::workspace_type workspacef,\n                         typename FFTI::workspace_type workspacei) {\n    using complex_type = typename FFTF::value_type;\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n\n    // Local array for thread\n    complex_type thread_data[FFTF::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    if constexpr (UseSharedMemoryStridedIO) {\n        example::io_strided<FFTF>::load_strided<Stride, SizeY>(input, thread_data, shared_mem, local_fft_id);\n    } else {\n        example::io_strided<FFTF>::load_strided<Stride, SizeY>(input, thread_data, local_fft_id);\n    }\n\n    // Execute FFT (part of the 2D R2C FFT)\n    FFTF().execute(thread_data, shared_mem, workspacef);\n\n    // Note: You can do any point-wise operation in here.\n\n    // Execute FFT (part of the 2D C2R FFT)\n    FFTI().execute(thread_data, shared_mem, workspacei);\n\n    // Save results\n    if constexpr (UseSharedMemoryStridedIO) {\n        example::io_strided<FFTI>::store_strided<Stride, SizeY>(thread_data, shared_mem, output, local_fft_id);\n    } else {\n        example::io_strided<FFTI>::store_strided<Stride, SizeY>(thread_data, output, local_fft_id);\n    }\n}\n\ntemplate<class RealType, class ComplexType>\nexample::fft_results<RealType> cufft_fft_2d_r2c_c2r(unsigned int fft_size_x,\n                                             unsigned int fft_size_y,\n                                             RealType*           real_values,\n                                             ComplexType*           complex_values,\n                                             hipStream_t stream) {\n    using complex_type = hipfftComplex;\n    static_assert(sizeof(ComplexType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<ComplexType> == std::alignment_of_v<complex_type>, \"\");\n    using real_type = hipfftReal;\n    static_assert(sizeof(ComplexType) % sizeof(real_type) == 0, \"\");\n    static_assert(std::alignment_of_v<ComplexType> % std::alignment_of_v<real_type> == 0, \"\");\n    static_assert(sizeof(RealType) == sizeof(real_type), \"\");\n    static_assert(std::alignment_of_v<RealType> == std::alignment_of_v<real_type>, \"\");\n\n    real_type*    cufft_input_r2c  = reinterpret_cast<real_type*>(real_values);\n    complex_type* cufft_output_r2c = reinterpret_cast<complex_type*>(complex_values);\n    complex_type* cufft_input_c2r  = cufft_output_r2c;\n    real_type*    cufft_output_c2r = reinterpret_cast<real_type*>(real_values);\n\n    // Create cuFFT plan\n    hipfftHandle plan_r2c;\n    hipfftHandle plan_c2r;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan_r2c, fft_size_x, fft_size_y, HIPFFT_R2C));\n    CUFFT_CHECK_AND_EXIT(hipfftPlan2d(&plan_c2r, fft_size_x, fft_size_y, HIPFFT_C2R));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_r2c, stream));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_c2r, stream));\n\n    // Execute cuFFT\n    auto cufft_execution = [&](hipStream_t /* stream */) {\n        // Out-of-place R2C\n        CUFFT_CHECK_AND_EXIT(hipfftExecR2C(plan_r2c, cufft_input_r2c, cufft_output_r2c));\n        // Out-of-place C2R\n        CUFFT_CHECK_AND_EXIT(hipfftExecC2R(plan_c2r, cufft_input_c2r, cufft_output_c2r));\n    };\n\n    // Correctness run\n    cufft_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    const size_t           flat_fft_size       = fft_size_x * fft_size_y;\n    const size_t           flat_fft_size_bytes = flat_fft_size * sizeof(real_type);\n    std::vector<real_type> output_host(flat_fft_size, std::numeric_limits<real_type>::quiet_NaN());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), cufft_output_c2r, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        cufft_execution,\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_r2c));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_c2r));\n\n    // Return results\n    return example::fft_results<RealType> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\ntemplate<class FFTR2CX, class FFTR2CY, class FFTC2RX, class FFTC2RY, bool UseSharedMemoryStridedIO, class RealType, class ComplexType>\nexample::fft_results<RealType> cufftdx_fft_2d_r2c_c2r(RealType* real_values, ComplexType* complex_values, hipStream_t stream) {\n    using FFTX         = FFTR2CX;\n    using FFTY         = FFTR2CY;\n    using complex_type = typename FFTX::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    static constexpr unsigned int fft_size_y = cufftdx::size_of<FFTY>::value;\n    static constexpr unsigned int fft_size_x = cufftdx::size_of<FFTX>::value;\n\n    // Checks that FFTX and FFTY are correctly defined\n    static_assert(std::is_same_v<cufftdx::precision_of_t<FFTX>, cufftdx::precision_of_t<FFTY>>,\n                  \"FFTY and FFTX must have the same precision\");\n    static_assert(std::is_same_v<typename FFTX::value_type, typename FFTY::value_type>,\n                  \"FFTY and FFTX must operator on the same type\");\n    static_assert(sizeof(ComplexType) == sizeof(complex_type), \"\");\n    static_assert(std::alignment_of_v<ComplexType> == std::alignment_of_v<complex_type>, \"\");\n    static_assert(sizeof(RealType) == sizeof(real_type), \"\");\n    static_assert(std::alignment_of_v<RealType> == std::alignment_of_v<real_type>, \"\");\n    // Checks below are not caused by any limitation in cuFFTDx, but rather in the example IO functions.\n    static_assert((fft_size_x % FFTY::ffts_per_block == 0),\n                  \"FFTsPerBlock for FFTX must divide Y dimension as IO doesn't check if a batch is in range\");\n\n    real_type*    cufftdx_real_values    = reinterpret_cast<real_type*>(real_values);\n    complex_type* cufftdx_complex_values = reinterpret_cast<complex_type*>(complex_values);\n\n    // Set shared memory requirements\n    auto error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_2d_kernel_y<FFTR2CY, real_type, complex_type>),\n                                           hipFuncAttributeMaxDynamicSharedMemorySize,\n                                           FFTR2CY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n    error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_2d_kernel_y<FFTC2RY, complex_type, real_type>),\n                                      hipFuncAttributeMaxDynamicSharedMemorySize,\n                                      FFTC2RY::shared_memory_size);\n    CUDA_CHECK_AND_EXIT(error_code);\n    unsigned int fft_x_shared_memory_smem_io =\n        std::max<unsigned>({FFTR2CX::shared_memory_size,\n                            FFTC2RX::shared_memory_size,\n                            FFTX::ffts_per_block * fft_size_x * sizeof(complex_type)});\n    unsigned int fft_x_shared_memory =\n        UseSharedMemoryStridedIO ? fft_x_shared_memory_smem_io : FFTX::shared_memory_size;\n    error_code = hipFuncSetAttribute(reinterpret_cast<const void*>(fft_2d_kernel_x<FFTR2CX,\n                                                      FFTC2RX,\n                                                      FFTY::output_length,\n                                                      FFTY::output_length,\n                                                      UseSharedMemoryStridedIO,\n                                                      complex_type>),\n                                      hipFuncAttributeMaxDynamicSharedMemorySize,\n                                      fft_x_shared_memory);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Create workspaces for FFTs\n    auto workspace_y_r2c = cufftdx::make_workspace<FFTR2CY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x_r2c = cufftdx::make_workspace<FFTR2CX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_y_c2r = cufftdx::make_workspace<FFTC2RY>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_x_c2r = cufftdx::make_workspace<FFTC2RX>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Synchronize device before execution\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Out-of-place R2C kernel (R2C Y dimension)\n    const auto grid_fft_size_y_r2c = ((fft_size_x + FFTR2CY::ffts_per_block - 1) / FFTR2CY::ffts_per_block);\n    const auto grid_fft_size_x     = ((FFTY::output_length + FFTX::ffts_per_block - 1) / FFTX::ffts_per_block);\n    const auto grid_fft_size_y_c2r = ((fft_size_x + FFTC2RY::ffts_per_block - 1) / FFTC2RY::ffts_per_block);\n    auto       fft_2d_execution    = [&](hipStream_t stream) {\n        fft_2d_kernel_y<FFTY, real_type, complex_type>\n            <<<grid_fft_size_y_r2c, FFTY::block_dim, FFTY::shared_memory_size, stream>>>(\n                cufftdx_real_values, cufftdx_complex_values, workspace_y_r2c);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n        // In-place C2CF (R2C X dimension) and C2CI (C2R X dimension) kernel\n        fft_2d_kernel_x<FFTR2CX,\n                        FFTC2RX,\n                        FFTY::output_length,\n                        FFTY::output_length,\n                        UseSharedMemoryStridedIO,\n                        complex_type><<<grid_fft_size_x, FFTX::block_dim, fft_x_shared_memory, stream>>>(\n            cufftdx_complex_values, cufftdx_complex_values, workspace_x_r2c, workspace_x_c2r);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n        // Out-of-place C2R kernel (C2R Y dimension)\n        fft_2d_kernel_y<FFTC2RY, complex_type, real_type>\n            <<<grid_fft_size_y_c2r, FFTC2RY::block_dim, FFTC2RY::shared_memory_size, stream>>>(\n                cufftdx_complex_values, cufftdx_real_values, workspace_y_c2r);\n        CUDA_CHECK_AND_EXIT(hipGetLastError());\n    };\n\n    // Correctness run\n    fft_2d_execution(stream);\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n    // Copy results to host\n    static constexpr size_t flat_fft_size       = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_bytes = flat_fft_size * sizeof(real_type);\n    std::vector<real_type>  output_host(flat_fft_size, std::numeric_limits<real_type>::quiet_NaN());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output_host.data(), cufftdx_real_values, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Performance measurements\n    auto time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            fft_2d_execution(stream);\n        },\n        cufftdx_example_warm_up_runs,\n        cufftdx_example_performance_runs,\n        stream);\n\n    // Return results\n    return example::fft_results<RealType> {output_host, (time / cufftdx_example_performance_runs)};\n}\n\n// Example showing how cuFFTDx can be used to perform a 2D FFT R2C-C2R convolution in 3 kernels. In the 2nd kernel any scalar\n// operation can be performed on the FFT data.\n//\n// Notes:\n// * This examples shows how to use cuFFTDx to run multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\n// * cuFFTDx with enabled shared memory IO usually be the faster cuFFTDx option for larger (>512) sizes.\n// * The shared memory IO cuFFTDx has high shared memory requirements and will not work for all possible sizes in X dimension.\ntemplate<unsigned int Arch>\nvoid fft_2d() {\n    using precision_type                     = float;\n    using complex_type                       = cufftdx::complex<precision_type>;\n    using real_type                          = complex_type::value_type;\n\n    // FFT\n    static constexpr unsigned int fft_size_y = 1024;\n    static constexpr unsigned int fft_size_x = 1024;\n    // Kernel Settings\n    static constexpr unsigned int ept_y = 16;\n    static constexpr unsigned int fpb_y = 1;\n    static constexpr unsigned int ept_x = 16;\n    static constexpr unsigned int fpb_x = 8;\n\n    // Other recommended configurations to test:\n    // 1:\n    // static constexpr unsigned int fft_size_y = 16384;\n    // static constexpr unsigned int fft_size_x = 4096;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 2;\n    // 2:\n    // static constexpr unsigned int fft_size_y = 2048;\n    // static constexpr unsigned int fft_size_x = 2048;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 8;\n    // static constexpr unsigned int fpb_x = 4;\n    // 3:\n    // static constexpr unsigned int fft_size_y = 128;\n    // static constexpr unsigned int fft_size_x = 128;\n    // static constexpr unsigned int ept_y = 16;\n    // static constexpr unsigned int fpb_y = 1;\n    // static constexpr unsigned int ept_x = 16;\n    // static constexpr unsigned int fpb_x = 8;\n\n    using namespace cufftdx;\n    using fft_base  = decltype(Block() + Precision<precision_type>() + SM<Arch>());\n    using fft_y_r2c = decltype(fft_base() + Type<fft_type::r2c>() + Size<fft_size_y>() + ElementsPerThread<ept_y>() +\n                               FFTsPerBlock<fpb_y>());\n    using fft_x_r2c =\n        decltype(fft_base() + Type<fft_type::c2c>() + Size<fft_size_x>() + Direction<fft_direction::forward>() +\n                 ElementsPerThread<ept_x>() + FFTsPerBlock<fpb_x>());\n    using fft_y_c2r = cufftdx::replace_t<fft_y_r2c, Type<fft_type::c2r>>;\n    using fft_x_c2r = cufftdx::replace_t<fft_x_r2c, Direction<fft_direction::inverse>>;\n\n    // Host data\n    static constexpr size_t flat_fft_size_real          = fft_size_x * fft_size_y;\n    static constexpr size_t flat_fft_size_complex       = fft_size_x * fft_y_r2c::output_length;\n    static constexpr size_t flat_fft_size_real_bytes    = flat_fft_size_real * sizeof(precision_type);\n    static constexpr size_t flat_fft_size_complex_bytes = flat_fft_size_complex * sizeof(complex_type);\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::vector<precision_type> input_host(flat_fft_size_real);\n    for (size_t i = 0; i < flat_fft_size_real; i++) {\n        float sign    = (i % 3 == 0) ? -1.0f : 1.0f;\n        input_host[i] = sign * static_cast<float>(i) / flat_fft_size_real;\n    }\n#else\n    auto input_host = example::get_random_real_data<precision_type>(flat_fft_size_real, -1, 1);\n#endif\n\n    // Device data\n    real_type* real_values;\n    complex_type* complex_values;\n    CUDA_CHECK_AND_EXIT(hipMalloc(&real_values, flat_fft_size_real_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&complex_values, flat_fft_size_complex_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemset(complex_values, 0b11111111, flat_fft_size_complex_bytes));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // cuFFTDx fused 2D R2C->C2R\n    // * the 2nd R2C (dim X) kernel and the 1st (dim X) C2R kernel are fused into one\n    // * the final results are stored in real_values\n    auto cufftdx_results =\n        cufftdx_fft_2d_r2c_c2r<fft_x_r2c, fft_y_r2c, fft_x_c2r, fft_y_c2r, false>(real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // cuFFTDx fused 2D R2C->C2R\n    // * the 2nd R2C (dim X) kernel and the 1st (dim X) C2R kernel are fused into one\n    // * the final results are stored in real_values\n    // * Uses shared memory to speed-up IO in the strided kernel\n    auto cufftdx_smemio_results =\n        cufftdx_fft_2d_r2c_c2r<fft_x_r2c, fft_y_r2c, fft_x_c2r, fft_y_c2r, true>(real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // cuFFT 2D R2C->C2R\n    // * the final results are stored in real_values\n    auto cufft_results = cufft_fft_2d_r2c_c2r(fft_size_x, fft_size_y, real_values, complex_values, stream);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(real_values, input_host.data(), flat_fft_size_real_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Destroy created CUDA stream\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    // Free CUDA buffers\n    CUDA_CHECK_AND_EXIT(hipFree(real_values));\n    CUDA_CHECK_AND_EXIT(hipFree(complex_values));\n\n    std::cout << \"FFT: (\" << fft_size_x << \", \" << fft_size_y << \")\\n\";\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_2D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_output_host[i] << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_output_host[i] << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    bool success = true;\n    // Check if cuFFTDx results are correct\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_real_values(cufftdx_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        std::cout << \"Peak relative error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error_relative << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n    // Check cuFFTDx with shared memory io\n    {\n        auto fft_error =\n            example::fft_signal_error::calculate_for_real_values(cufftdx_smemio_results.output, cufft_results.output);\n        std::cout << \"cuFFTDx (shared memory IO)\\n\";\n        std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n        std::cout << \"Peak relative error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error_relative << \"\\n\";\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.001);\n        }\n    }\n\n    // Print performance results\n    if (success) {\n        std::cout << \"\\nPerformance results:\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx: \" << cufftdx_results.avg_time_in_ms << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFTDx (shared memory IO): \" << cufftdx_smemio_results.avg_time_in_ms\n                  << \" [ms]\\n\";\n        std::cout << std::setw(28) << \"cuFFT: \" << cufft_results.avg_time_in_ms << \" [ms]\\n\";\n    }\n\n    if (success) {\n        std::cout << \"Success\\n\";\n    } else {\n        std::cout << \"Failure\\n\";\n        std::exit(1);\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct fft_2d_functor {\n    void operator()() { return fft_2d<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<fft_2d_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_2d_r2c_c2r.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n\ntemplate<unsigned int MaxThreadsPerBlock, class FFT, class ComplexType = typename FFT::value_type>\n__launch_bounds__(MaxThreadsPerBlock) __global__\n    void cufftdx_3d_fft_single_block_kernel(const ComplexType* input, ComplexType* output) {\n    using complex_type = ComplexType;\n    static constexpr unsigned int fft_size = cufftdx::size_of<FFT>::value;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Shared memory use for exchanging data between threads\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n\n    // Load data from global memory to registers.\n    static constexpr unsigned int stride_x = fft_size * fft_size;\n    unsigned int                  index    = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = input[index];\n        index += stride_x;\n    }\n\n    // Execute FFT in X dimension\n    FFT().execute(thread_data);\n\n    // Exchange/transpose via shared memory\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += stride_x;\n    }\n    __syncthreads();\n    static constexpr unsigned int stride_y = cufftdx::size_of<FFT>::value;\n    index                                  = threadIdx.x + threadIdx.y * fft_size * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += stride_y;\n    }\n\n    // Execute FFT in Y dimension\n    FFT().execute(thread_data);\n\n    // Exchange/transpose via shared memory\n    index = threadIdx.x + threadIdx.y * fft_size * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += stride_y;\n    }\n    __syncthreads();\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += 1;\n    }\n\n    // Execute FFT in Z dimension\n    FFT().execute(thread_data);\n\n    // Save results\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n    // Simple IO with poor global memory pattern:\n    // Storing the data with stride=1 results in poor global memory\n    // write pattern with little or none coalescing\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        output[index] = thread_data[i];\n        index += 1;\n    }\n#else\n    // Shared memory IO:\n    // Exchanging data via shared memory results in a much better global\n    // memory patter with good coalescing\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += 1;\n    }\n    __syncthreads();\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += stride_x;\n    }\n\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        output[index] = thread_data[i];\n        index += stride_x;\n    }\n#endif\n}\n\nvoid cufft_3d_fft(unsigned int  fft_size_x,\n                  unsigned int  fft_size_y,\n                  unsigned int  fft_size_z,\n                  cufftComplex* input,\n                  cufftComplex* output,\n                  cudaStream_t  stream) {\n    // Create cuFFT plan\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, CUFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // Execute cuFFT\n    CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan, input, output, CUFFT_FORWARD))\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n}\n\ntemplate<unsigned int FFTSize>\nvoid cufftdx_3d_fft_single_block(float2* input, float2* output, cudaStream_t stream) {\n    using namespace cufftdx;\n\n    static constexpr unsigned int fft_size_x = FFTSize;\n    static constexpr unsigned int fft_size_y = FFTSize;\n    static constexpr unsigned int fft_size_z = FFTSize;\n\n    using FFT = decltype(Thread() + Size<fft_size_x>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>());\n    using complex_type = typename FFT::value_type;\n\n    constexpr dim3         block_dim             = {fft_size_z, fft_size_y, 1};\n    constexpr unsigned int max_threads_per_block = block_dim.x * block_dim.y * block_dim.z;\n    const size_t           shared_memory_size    = (fft_size_x * fft_size_y * fft_size_z) * sizeof(complex_type);\n\n    const auto kernel = cufftdx_3d_fft_single_block_kernel<max_threads_per_block, FFT, complex_type>;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n    kernel<<<1, block_dim, shared_memory_size, stream>>>(cufftdx_input, cufftdx_output);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n}\n\n// Example showing how to perform a small 3D FFT in a single CUDA block using cuFFTDx thread execution. In this example\n// every dimension is the same.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run a multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\nint main(int, char**) {\n    // 3D FFT where X=Y=Z\n    static constexpr unsigned int fft_size = 16;\n\n    // Generate random input data on host\n    const unsigned int flat_fft_size = fft_size * fft_size * fft_size;\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<float2> host_input(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        host_input[i].x = sign * static_cast<float>(i) / 100;\n        host_input[i].y = sign * static_cast<float>(i) / 100;\n    }\n#else\n    auto host_input = example::get_random_complex_data<float>(flat_fft_size, -1, 1);\n#endif\n\n    // Allocate managed memory for device input/output\n    // float2 has the same size and alignment as cuFFTDx fp32 complex type cufftdx::complex<float> and cufftComplex\n    float2*    input;\n    float2*    output;\n    const auto flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, flat_fft_size_bytes));\n\n    // Allocate host output for cuFFT and cuFFTDx\n    std::vector<float2> cufft_output(flat_fft_size);\n    std::vector<float2> cufftdx_output(flat_fft_size);\n\n    // Copy input to the device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input, host_input.data(), flat_fft_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    cufft_3d_fft(fft_size, fft_size, fft_size, input, output, stream);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(cufft_output.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    cufftdx_3d_fft_single_block<fft_size>(input, output, stream);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(cufftdx_output.data(), output, flat_fft_size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Clean-up\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(input));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n\n    // Check if cuFFTDx results are correct\n    auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_output, cufft_output);\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_output[i].x << \", \" << cufft_output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_output[i].x << \", \" << cufftdx_output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    std::cout << \"Correctness results:\\n\";\n    std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n    std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n    if(fft_error.l2_relative_error < 0.001) {\n        std::cout << \"Success\\n\";\n        return 0;\n    } else {\n        std::cout << \"Failure\\n\";\n        return 1;\n    }\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n// #define CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n\ntemplate<unsigned int MaxThreadsPerBlock, class FFT, class ComplexType = typename FFT::value_type>\n__launch_bounds__(MaxThreadsPerBlock) __global__\n    void cufftdx_3d_fft_single_block_kernel(const ComplexType* input, ComplexType* output) {\n    using complex_type = ComplexType;\n    static constexpr unsigned int fft_size = cufftdx::size_of<FFT>::value;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Shared memory use for exchanging data between threads\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n\n    // Load data from global memory to registers.\n    static constexpr unsigned int stride_x = fft_size * fft_size;\n    unsigned int                  index    = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = input[index];\n        index += stride_x;\n    }\n\n    // Execute FFT in X dimension\n    FFT().execute(thread_data);\n\n    // Exchange/transpose via shared memory\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += stride_x;\n    }\n    __syncthreads();\n    static constexpr unsigned int stride_y = cufftdx::size_of<FFT>::value;\n    index                                  = threadIdx.x + threadIdx.y * fft_size * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += stride_y;\n    }\n\n    // Execute FFT in Y dimension\n    FFT().execute(thread_data);\n\n    // Exchange/transpose via shared memory\n    index = threadIdx.x + threadIdx.y * fft_size * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += stride_y;\n    }\n    __syncthreads();\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += 1;\n    }\n\n    // Execute FFT in Z dimension\n    FFT().execute(thread_data);\n\n    // Save results\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D_SIMPLE_IO\n    // Simple IO with poor global memory pattern:\n    // Storing the data with stride=1 results in poor global memory\n    // write pattern with little or none coalescing\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        output[index] = thread_data[i];\n        index += 1;\n    }\n#else\n    // Shared memory IO:\n    // Exchanging data via shared memory results in a much better global\n    // memory patter with good coalescing\n    index = (threadIdx.x + threadIdx.y * fft_size) * fft_size;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        shared_memory[index] = thread_data[i];\n        index += 1;\n    }\n    __syncthreads();\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = shared_memory[index];\n        index += stride_x;\n    }\n\n    index = (threadIdx.x + threadIdx.y * fft_size);\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        output[index] = thread_data[i];\n        index += stride_x;\n    }\n#endif\n}\n\nvoid cufft_3d_fft(unsigned int  fft_size_x,\n                  unsigned int  fft_size_y,\n                  unsigned int  fft_size_z,\n                  hipfftComplex* input,\n                  hipfftComplex* output,\n                  hipStream_t  stream) {\n    // Create cuFFT plan\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan3d(&plan, fft_size_x, fft_size_y, fft_size_z, HIPFFT_C2C));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // Execute cuFFT\n    CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan, input, output, HIPFFT_FORWARD))\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Clean-up\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n}\n\ntemplate<unsigned int FFTSize>\nvoid cufftdx_3d_fft_single_block(float2* input, float2* output, hipStream_t stream) {\n    using namespace cufftdx;\n\n    static constexpr unsigned int fft_size_x = FFTSize;\n    static constexpr unsigned int fft_size_y = FFTSize;\n    static constexpr unsigned int fft_size_z = FFTSize;\n\n    using FFT = decltype(Thread() + Size<fft_size_x>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>());\n    using complex_type = typename FFT::value_type;\n\n    constexpr dim3         block_dim             = {fft_size_z, fft_size_y, 1};\n    constexpr unsigned int max_threads_per_block = block_dim.x * block_dim.y * block_dim.z;\n    const size_t           shared_memory_size    = (fft_size_x * fft_size_y * fft_size_z) * sizeof(complex_type);\n\n    const auto kernel = cufftdx_3d_fft_single_block_kernel<max_threads_per_block, FFT, complex_type>;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n\n    complex_type* cufftdx_input  = reinterpret_cast<complex_type*>(input);\n    complex_type* cufftdx_output = reinterpret_cast<complex_type*>(output);\n    kernel<<<1, block_dim, shared_memory_size, stream>>>(cufftdx_input, cufftdx_output);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n}\n\n// Example showing how to perform a small 3D FFT in a single CUDA block using cuFFTDx thread execution. In this example\n// every dimension is the same.\n//\n// Notes:\n// * This example shows how to use cuFFTDx to run a multi-dimensional FFT. Final performance will vary depending on the\n// FFT definitions (precision, size, type, ept, fpb) and other user customizations.\n// * Best possible performance requires adapting parameters in the sample to particular set of parameters and code customizations.\n// * Only FP32 was tested for this example, other types might require adjustments.\nint main(int, char**) {\n    // 3D FFT where X=Y=Z\n    static constexpr unsigned int fft_size = 16;\n\n    // Generate random input data on host\n    const unsigned int flat_fft_size = fft_size * fft_size * fft_size;\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::vector<float2> host_input(flat_fft_size);\n    for (size_t i = 0; i < flat_fft_size; i++) {\n        float sign      = (i % 3 == 0) ? -1.0f : 1.0f;\n        host_input[i].x = sign * static_cast<float>(i) / 100;\n        host_input[i].y = sign * static_cast<float>(i) / 100;\n    }\n#else\n    auto host_input = example::get_random_complex_data<float>(flat_fft_size, -1, 1);\n#endif\n\n    // Allocate managed memory for device input/output\n    // float2 has the same size and alignment as cuFFTDx fp32 complex type cufftdx::complex<float> and cufftComplex\n    float2*    input;\n    float2*    output;\n    const auto flat_fft_size_bytes = flat_fft_size * sizeof(float2);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input, flat_fft_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, flat_fft_size_bytes));\n\n    // Allocate host output for cuFFT and cuFFTDx\n    std::vector<float2> cufft_output(flat_fft_size);\n    std::vector<float2> cufftdx_output(flat_fft_size);\n\n    // Copy input to the device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input, host_input.data(), flat_fft_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    cufft_3d_fft(fft_size, fft_size, fft_size, input, output, stream);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(cufft_output.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    cufftdx_3d_fft_single_block<fft_size>(input, output, stream);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(cufftdx_output.data(), output, flat_fft_size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Clean-up\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(input));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n\n    // Check if cuFFTDx results are correct\n    auto fft_error = example::fft_signal_error::calculate_for_complex_values(cufftdx_output, cufft_output);\n\n#ifdef CUFFTDX_EXAMPLE_DETAIL_DEBUG_FFT_3D\n    std::cout << \"cuFFT, cuFFTDx\\n\";\n    for (size_t i = 0; i < 8; i++) {\n        std::cout << i << \": \";\n        std::cout << \"(\" << cufft_output[i].x << \", \" << cufft_output[i].y << \")\";\n        std::cout << \", \";\n        std::cout << \"(\" << cufftdx_output[i].x << \", \" << cufftdx_output[i].y << \")\";\n        std::cout << \"\\n\";\n    }\n#endif\n\n    std::cout << \"Correctness results:\\n\";\n    std::cout << \"L2 error: \" << fft_error.l2_relative_error << \"\\n\";\n    std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n    if(fft_error.l2_relative_error < 0.001) {\n        std::cout << \"Success\\n\";\n        return 0;\n    } else {\n        std::cout << \"Failure\\n\";\n        return 1;\n    }\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/05_fft_Xd/fft_3d_cube_single_block.cu"}
{"input": "\n/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include \"01_simple_fft_thread_simple_fft_thread_lto_artifacts/lto_database.hpp.inc\" // cuFFT-dumped LTOIR database header file\n\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is perform by a CUDA thread.\n//\n// Four (threads_count) threads are run, and each thread calculates 8-point (fft_size) C2C double precision FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    static constexpr unsigned int threads_count = 4;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // - Thread()   operator informs that FFT will be executed on thread level.\n    // - CodeType() operator informs that FFT will attempt to use cuFFT-dumped LTOIRs for computes.\n    using FFT_without_code_type = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() + Precision<double>());\n    using FFT = decltype(FFT_without_code_type() + experimental::CodeType<experimental::code_type::ltoir>());\n    using complex_type = typename FFT::value_type;\n\n    // Check if LTO implementation is selected\n    static_assert(experimental::code_type_of<FFT>::value == experimental::code_type::ltoir, \"Code Type is not set to LTO-IR\");\n    static_assert(FFT::code == experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        input[i] = complex_type {double(i), -double(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input[i].x << \" \" << input[i].y << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(device_buffer, input.data(), size_bytes, cudaMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates one FFT of size\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output.data(), device_buffer, size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output[i].x << \" \" << output[i].y << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "output": "\n/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include \"01_simple_fft_thread_simple_fft_thread_lto_artifacts/lto_database.hpp.inc\" // cuFFT-dumped LTOIR database header file\n\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is perform by a CUDA thread.\n//\n// Four (threads_count) threads are run, and each thread calculates 8-point (fft_size) C2C double precision FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    static constexpr unsigned int threads_count = 4;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // - Thread()   operator informs that FFT will be executed on thread level.\n    // - CodeType() operator informs that FFT will attempt to use cuFFT-dumped LTOIRs for computes.\n    using FFT_without_code_type = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() + Precision<double>());\n    using FFT = decltype(FFT_without_code_type() + experimental::CodeType<experimental::code_type::ltoir>());\n    using complex_type = typename FFT::value_type;\n\n    // Check if LTO implementation is selected\n    static_assert(experimental::code_type_of<FFT>::value == experimental::code_type::ltoir, \"Code Type is not set to LTO-IR\");\n    static_assert(FFT::code == experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        input[i] = complex_type {double(i), -double(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input[i].x << \" \" << input[i].y << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(device_buffer, input.data(), size_bytes, hipMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates one FFT of size\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output.data(), device_buffer, size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output[i].x << \" \" << output[i].y << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/01_simple_fft_thread/simple_fft_thread_lto.cu"}
{"input": "\n/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is perform by a CUDA thread.\n//\n// Four (threads_count) threads are run, and each thread calculates 8-point (fft_size) C2C double precision FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    static constexpr unsigned int threads_count = 4;\n\n    // FFT is defined, its: size, type, direction, precision. Thread() operator informs that FFT will be executed on thread level.\n    using FFT          = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<double>());\n    using complex_type = typename FFT::value_type;\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        input[i] = complex_type {double(i), -double(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input[i].x << \" \" << input[i].y << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(device_buffer, input.data(), size_bytes, cudaMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates one FFT of size\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output.data(), device_buffer, size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output[i].x << \" \" << output[i].y << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "output": "\n/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i] = data[index + i];\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        data[index + i] = thread_data[i];\n    }\n}\n\n// In this example a one-dimensional complex-to-complex transform is perform by a CUDA thread.\n//\n// Four (threads_count) threads are run, and each thread calculates 8-point (fft_size) C2C double precision FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    static constexpr unsigned int threads_count = 4;\n\n    // FFT is defined, its: size, type, direction, precision. Thread() operator informs that FFT will be executed on thread level.\n    using FFT          = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<double>());\n    using complex_type = typename FFT::value_type;\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        input[i] = complex_type {double(i), -double(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input[i].x << \" \" << input[i].y << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(device_buffer, input.data(), size_bytes, hipMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates one FFT of size\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output.data(), device_buffer, size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output[i].x << \" \" << output[i].y << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/01_simple_fft_thread/simple_fft_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        // complex<half2> values are processed with assumtion that they are in RRII layout,\n        // but data has them in RIRI layout. example::to_rrii converts RIRI to RRII.\n        thread_data[i] = example::to_rrii(data[index + i]);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        // converting back form RRII to RIRI layout\n        data[index + i] = example::to_riri(thread_data[i]);\n    }\n}\n\n// In this example a one-dimensional half-precision complex-to-complex transform is perform by each CUDA thread.\n//\n// Three (threads_count) threads are run, and each thread calculates two 8-point (fft_size) C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Note: In half precision cuFFTDx uses complex<half2> type and processes values in implicit batches of two FFTs, ie.\n// each thread processes two FFTs.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    // In case of half precision each thread caluclates two FFTs\n    static constexpr unsigned int threads_count = 3;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // Thread() operator informs that FFT will be executed on a thread level.\n    using FFT          = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>());\n    using complex_type = typename FFT::value_type;\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + input.size());\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        input[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input[i].x.x) << \" \" << __half2float(input[i].x.y) << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(device_buffer, input.data(), size_bytes, cudaMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates two FFTs of size 8\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(output.data(), device_buffer, size_bytes, cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output[i].x.x) << \" \" << __half2float(output[i].x.y) << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT>\n__global__ void thread_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // Load data from global memory to registers.\n    // thread_data should have all input data in order.\n    unsigned int index = threadIdx.x * FFT::elements_per_thread;\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        // complex<half2> values are processed with assumtion that they are in RRII layout,\n        // but data has them in RIRI layout. example::to_rrii converts RIRI to RRII.\n        thread_data[i] = example::to_rrii(data[index + i]);\n    }\n\n    // Execute FFT\n    FFT().execute(thread_data);\n\n    // Save results\n    for (size_t i = 0; i < FFT::elements_per_thread; i++) {\n        // converting back form RRII to RIRI layout\n        data[index + i] = example::to_riri(thread_data[i]);\n    }\n}\n\n// In this example a one-dimensional half-precision complex-to-complex transform is perform by each CUDA thread.\n//\n// Three (threads_count) threads are run, and each thread calculates two 8-point (fft_size) C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Note: In half precision cuFFTDx uses complex<half2> type and processes values in implicit batches of two FFTs, ie.\n// each thread processes two FFTs.\nint main(int, char**) {\n    using namespace cufftdx;\n\n    // Number of threads to execute\n    // In case of half precision each thread caluclates two FFTs\n    static constexpr unsigned int threads_count = 3;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // Thread() operator informs that FFT will be executed on a thread level.\n    using FFT          = decltype(Thread() + Size<8>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>());\n    using complex_type = typename FFT::value_type;\n\n    // Host data\n    std::vector<complex_type> input(FFT::input_length * threads_count);\n    for (size_t i = 0; i < input.size(); i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + input.size());\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        input[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input[i].x.x) << \" \" << __half2float(input[i].x.y) << std::endl;\n    }\n\n    // Device data\n    complex_type* device_buffer;\n    auto          size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&device_buffer, size_bytes));\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(device_buffer, input.data(), size_bytes, hipMemcpyHostToDevice));\n\n    // Invokes kernel with 'threads_count' threads in block, each thread calculates two FFTs of size 8\n    thread_fft_kernel<FFT><<<1, threads_count>>>(device_buffer);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy device to host\n    std::vector<complex_type> output(input.size());\n    CUDA_CHECK_AND_EXIT(hipMemcpy(output.data(), device_buffer, size_bytes, hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipFree(device_buffer));\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output[i].x.x) << \" \" << __half2float(output[i].x.y) << std::endl;\n    }\n\n    std::cout << \"Success\" << std::endl;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/01_simple_fft_thread/simple_fft_thread_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cmath>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n#include <cufftXt.h>\n#endif\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<class FFT, class IFFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(typename FFT::value_type*     data,\n                                                                                 typename FFT::workspace_type  workspace,\n                                                                                 typename IFFT::workspace_type workspace_inverse) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFT>::value;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem, workspace_inverse);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// Scaling kernel; transforms data between cuFFTs.\ntemplate<unsigned int fft_size>\n__global__ void scaling_kernel(cufftComplex*      data,\n                               const unsigned int input_size,\n                               const unsigned int ept) {\n\n    static constexpr float scale = 1.0 / fft_size;\n\n    cufftComplex temp;\n    unsigned int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < ept; i++) {\n        if (index < input_size) {\n            temp = data[index];\n            temp.x *= scale;\n            temp.y *= scale;\n            data[index] = temp;\n            index += blockDim.x * gridDim.x;\n        }\n    }\n}\n\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\ntemplate<unsigned int fft_size>\n__device__  cufftComplex scaling_callback(void *dataIn,\n                                          size_t offset,\n                                          void *callerInfo,\n                                          void *sharedPtr) {\n    static constexpr float scale = 1.0 / fft_size;\n\n    cufftComplex value = static_cast<cufftComplex*>(dataIn)[offset];\n    value.x *= scale;\n    value.y *= scale;\n    return value;\n}\n\n__device__ __managed__ cufftCallbackLoadC scaling_callback_ptr = scaling_callback<128>;\n#endif\n\ntemplate<class FFT, class IFFT>\ndouble measure_cufftdx(const unsigned int&       kernel_runs,\n                       const unsigned int&       warm_up_runs,\n                       const unsigned int&       cuda_blocks,\n                       typename FFT::value_type* device_buffer,\n                       cudaStream_t              stream) {\n\n    using namespace cufftdx;\n    using complex_type = typename FFT::value_type;\n\n    // create workspaces for FFT and IFFT\n    cudaError_t error_code = cudaSuccess;\n    auto        workspace  = make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_inverse = make_workspace<IFFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // run cuFFTDx\n    double time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            convolution_kernel<FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                device_buffer, workspace, workspace_inverse);\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    return time;\n}\n\ntemplate<unsigned int fft_size>\ndouble measure_cufft(const unsigned int& kernel_runs,\n                     const unsigned int& warm_up_runs,\n                     const unsigned int& batch_size,\n                     cufftComplex*       device_buffer,\n                     cudaStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<fft_size>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    const unsigned int cuda_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    const unsigned int input_length        = fft_size * batch_size;\n    const unsigned int elements_per_block  = (input_length + cuda_blocks - 1) / cuda_blocks;\n    const unsigned int elements_per_thread = (elements_per_block + block_dim_scaling_kernel - 1) / block_dim_scaling_kernel;\n\n    // prepare cuFFT runs\n    cufftHandle plan;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan, stream));\n\n    // run convolution\n    double time_cufft = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            if (cufftExecC2C(plan, device_buffer, device_buffer, CUFFT_FORWARD) != CUFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n                return;\n            }\n            scaling_kernel<fft_size>\n                <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(device_buffer, input_length, elements_per_thread);\n            if (cufftExecC2C(plan, device_buffer, device_buffer, CUFFT_INVERSE) != CUFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n                return;\n            }\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan));\n    return time_cufft;\n}\n\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\ntemplate<unsigned int fft_size>\ndouble measure_cufft_callback(const unsigned int& kernel_runs,\n                              const unsigned int& warm_up_runs,\n                              const unsigned int& batch_size,\n                              cufftComplex*       device_buffer,\n                              cudaStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<fft_size>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n    // prepare cuFFT runs\n    cufftHandle plan_in;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan_in, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_in, stream));\n    cufftHandle plan_out;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan_out, fft_size, CUFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_out, stream));\n\n    // Set input callback\n    CUFFT_CHECK_AND_EXIT(cufftXtSetCallback(plan_in,\n                                            reinterpret_cast<void**>(&scaling_callback_ptr),\n                                            CUFFT_CB_LD_COMPLEX,\n                                            nullptr));\n\n    // run convolution\n    double time_cufft = example::measure_execution_ms(\n        [&](cudaStream_t /* stream */) {\n            CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan_in, device_buffer, device_buffer, CUFFT_FORWARD));\n            CUFFT_CHECK_AND_EXIT(cufftExecC2C(plan_out, device_buffer, device_buffer, CUFFT_INVERSE));\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_in));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_out));\n    return time_cufft;\n}\n#endif // CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n\n// This example compares performance of cuFFT and cuFFTDx when performing C2C convolution.\n// Data is generated on host, copied to device buffer and processed by FFTs.\n// Each cuFFTDx execution runs one kernel, each cuFFT execution - three kernels.\n// The experiment runs with the following principles:\n// - at least 1GB of data is allocated in GPU and transformed by both convolutions,\n// - for cuFFTDx kernel run, number of CUDA blocks is divisible\n//   by maximum number of CUDA blocks that can run simultaneously on the GPU.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int fft_size                 = 512;\n    static constexpr unsigned int kernel_runs           = 10;\n    static constexpr unsigned int warm_up_runs             = 1;\n    static constexpr bool         verbose                  = true;\n\n    static constexpr bool         use_suggested              = true; // Whether to use suggested FPB and EPT values or custom.\n    static constexpr unsigned int custom_ffts_per_block      = 2;\n    static constexpr unsigned int custom_elements_per_thread = 8;\n\n    // To determine the total input length (number of fft batches to run), the maximum number of\n    // simultanously running cuFFTDx CUDA blocks is calculated.\n\n    // Declaration of cuFFTDx run\n    using fft_incomplete = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Precision<float>() + SM<Arch>());\n    using fft_base       = decltype(fft_incomplete() + Direction<fft_direction::forward>());\n    using ifft_base      = decltype(fft_incomplete() + Direction<fft_direction::inverse>());\n\n    static constexpr unsigned int elements_per_thread = use_suggested ? fft_base::elements_per_thread : custom_elements_per_thread;\n    static constexpr unsigned int ffts_per_block      = use_suggested ? fft_base::suggested_ffts_per_block : custom_ffts_per_block;\n\n    using fft          = decltype(fft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using ifft         = decltype(ifft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using complex_type = typename fft::value_type;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        convolution_kernel<fft, ifft>, cudaFuncAttributeMaxDynamicSharedMemorySize, fft::shared_memory_size));\n\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      convolution_kernel<fft, ifft>,\n                                                      fft::block_dim.x * fft::block_dim.y * fft::block_dim.z,\n                                                      fft::shared_memory_size));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * ffts_per_block * fft_size * sizeof(complex_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks  = device_blocks * executed_blocks_multiplyer;\n    const unsigned int input_length = ffts_per_block * cuda_blocks * fft_size;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Host data\n    std::vector<complex_type> input =\n        example::get_random_complex_data<typename complex_type::value_type>(input_length, -10, 10);\n\n    // Device data\n    complex_type* device_buffer;\n    auto          input_size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&device_buffer, input_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(device_buffer, input.data(), input_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Measure performance\n    double time_cufftdx = measure_cufftdx<fft, ifft>(kernel_runs, warm_up_runs, cuda_blocks, device_buffer, stream);\n    double time_cufft   = measure_cufft<fft_size>(\n        kernel_runs, warm_up_runs, cuda_blocks * ffts_per_block, (cufftComplex*)device_buffer, stream);\n    #ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n    double time_cufft_cb = measure_cufft_callback<fft_size>(\n        kernel_runs, warm_up_runs, cuda_blocks * ffts_per_block, (cufftComplex*)device_buffer, stream);\n    #endif\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(device_buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        double gflops = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                        (std::log(fft_size) / std::log(2)) / time / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / kernel_runs << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n    if (verbose) {\n        std::cout << \"FFT size: \" << fft_size << std::endl;\n        std::cout << \"FFTs run: \" << ffts_per_block * cuda_blocks << std::endl;\n        report_time_and_performance(\"cuFFTDx\", time_cufftdx);\n        std::cout << \"FFTs elements per thread: \" << fft::elements_per_thread << std::endl;\n        std::cout << \"FFTs per block: \" << ffts_per_block << std::endl;\n        std::cout << \"CUDA blocks: \" << cuda_blocks << std::endl;\n        std::cout << \"Blocks per multiprocessor: \" << blocks_per_multiprocessor << std::endl;\n\n        report_time_and_performance(\"cuFFT\", time_cufft);\n        #ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n        report_time_and_performance(\"cuFFT Callback\", time_cufft_cb);\n        #endif\n    } else {\n        double gflops_cufftdx = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                                (std::log(fft_size) / std::log(2)) / time_cufftdx / 1000000.0;\n        double gflops_cufft = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                              (std::log(fft_size) / std::log(2)) / time_cufft / 1000000.0;\n        std::cout << fft_size << \": \" << std::endl\n                  << gflops_cufftdx << \", \" << time_cufftdx / kernel_runs << \", \" << std::endl\n                  << gflops_cufft << \", \" << time_cufft / kernel_runs;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() {\n        return convolution<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cmath>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n#include <hipfft/hipfftXt.h>\n#endif\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\ntemplate<class FFT, class IFFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(typename FFT::value_type*     data,\n                                                                                 typename FFT::workspace_type  workspace,\n                                                                                 typename IFFT::workspace_type workspace_inverse) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFT>::value;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem, workspace_inverse);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// Scaling kernel; transforms data between cuFFTs.\ntemplate<unsigned int fft_size>\n__global__ void scaling_kernel(hipfftComplex*      data,\n                               const unsigned int input_size,\n                               const unsigned int ept) {\n\n    static constexpr float scale = 1.0 / fft_size;\n\n    hipfftComplex temp;\n    unsigned int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < ept; i++) {\n        if (index < input_size) {\n            temp = data[index];\n            temp.x *= scale;\n            temp.y *= scale;\n            data[index] = temp;\n            index += blockDim.x * gridDim.x;\n        }\n    }\n}\n\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\ntemplate<unsigned int fft_size>\n__device__  hipfftComplex scaling_callback(void *dataIn,\n                                          size_t offset,\n                                          void *callerInfo,\n                                          void *sharedPtr) {\n    static constexpr float scale = 1.0 / fft_size;\n\n    hipfftComplex value = static_cast<hipfftComplex*>(dataIn)[offset];\n    value.x *= scale;\n    value.y *= scale;\n    return value;\n}\n\n__device__ __managed__ hipfftCallbackLoadC scaling_callback_ptr = scaling_callback<128>;\n#endif\n\ntemplate<class FFT, class IFFT>\ndouble measure_cufftdx(const unsigned int&       kernel_runs,\n                       const unsigned int&       warm_up_runs,\n                       const unsigned int&       cuda_blocks,\n                       typename FFT::value_type* device_buffer,\n                       hipStream_t              stream) {\n\n    using namespace cufftdx;\n    using complex_type = typename FFT::value_type;\n\n    // create workspaces for FFT and IFFT\n    hipError_t error_code = hipSuccess;\n    auto        workspace  = make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_inverse = make_workspace<IFFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // run cuFFTDx\n    double time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            convolution_kernel<FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                device_buffer, workspace, workspace_inverse);\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    return time;\n}\n\ntemplate<unsigned int fft_size>\ndouble measure_cufft(const unsigned int& kernel_runs,\n                     const unsigned int& warm_up_runs,\n                     const unsigned int& batch_size,\n                     hipfftComplex*       device_buffer,\n                     hipStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<fft_size>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    const unsigned int cuda_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    const unsigned int input_length        = fft_size * batch_size;\n    const unsigned int elements_per_block  = (input_length + cuda_blocks - 1) / cuda_blocks;\n    const unsigned int elements_per_thread = (elements_per_block + block_dim_scaling_kernel - 1) / block_dim_scaling_kernel;\n\n    // prepare cuFFT runs\n    hipfftHandle plan;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan, stream));\n\n    // run convolution\n    double time_cufft = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            if (hipfftExecC2C(plan, device_buffer, device_buffer, HIPFFT_FORWARD) != HIPFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n                return;\n            }\n            scaling_kernel<fft_size>\n                <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(device_buffer, input_length, elements_per_thread);\n            if (hipfftExecC2C(plan, device_buffer, device_buffer, HIPFFT_BACKWARD) != HIPFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n                return;\n            }\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan));\n    return time_cufft;\n}\n\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\ntemplate<unsigned int fft_size>\ndouble measure_cufft_callback(const unsigned int& kernel_runs,\n                              const unsigned int& warm_up_runs,\n                              const unsigned int& batch_size,\n                              hipfftComplex*       device_buffer,\n                              hipStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<fft_size>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n    // prepare cuFFT runs\n    hipfftHandle plan_in;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan_in, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_in, stream));\n    hipfftHandle plan_out;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan_out, fft_size, HIPFFT_C2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_out, stream));\n\n    // Set input callback\n    CUFFT_CHECK_AND_EXIT(hipfftXtSetCallback(plan_in,\n                                            reinterpret_cast<void**>(&scaling_callback_ptr),\n                                            HIPFFT_CB_LD_COMPLEX,\n                                            nullptr));\n\n    // run convolution\n    double time_cufft = example::measure_execution_ms(\n        [&](hipStream_t /* stream */) {\n            CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan_in, device_buffer, device_buffer, HIPFFT_FORWARD));\n            CUFFT_CHECK_AND_EXIT(hipfftExecC2C(plan_out, device_buffer, device_buffer, HIPFFT_BACKWARD));\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_in));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_out));\n    return time_cufft;\n}\n#endif // CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n\n// This example compares performance of cuFFT and cuFFTDx when performing C2C convolution.\n// Data is generated on host, copied to device buffer and processed by FFTs.\n// Each cuFFTDx execution runs one kernel, each cuFFT execution - three kernels.\n// The experiment runs with the following principles:\n// - at least 1GB of data is allocated in GPU and transformed by both convolutions,\n// - for cuFFTDx kernel run, number of CUDA blocks is divisible\n//   by maximum number of CUDA blocks that can run simultaneously on the GPU.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int minimum_input_size_bytes = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int fft_size                 = 512;\n    static constexpr unsigned int kernel_runs           = 10;\n    static constexpr unsigned int warm_up_runs             = 1;\n    static constexpr bool         verbose                  = true;\n\n    static constexpr bool         use_suggested              = true; // Whether to use suggested FPB and EPT values or custom.\n    static constexpr unsigned int custom_ffts_per_block      = 2;\n    static constexpr unsigned int custom_elements_per_thread = 8;\n\n    // To determine the total input length (number of fft batches to run), the maximum number of\n    // simultanously running cuFFTDx CUDA blocks is calculated.\n\n    // Declaration of cuFFTDx run\n    using fft_incomplete = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Precision<float>() + SM<Arch>());\n    using fft_base       = decltype(fft_incomplete() + Direction<fft_direction::forward>());\n    using ifft_base      = decltype(fft_incomplete() + Direction<fft_direction::inverse>());\n\n    static constexpr unsigned int elements_per_thread = use_suggested ? fft_base::elements_per_thread : custom_elements_per_thread;\n    static constexpr unsigned int ffts_per_block      = use_suggested ? fft_base::suggested_ffts_per_block : custom_ffts_per_block;\n\n    using fft          = decltype(fft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using ifft         = decltype(ifft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using complex_type = typename fft::value_type;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(convolution_kernel<fft, ifft>), hipFuncAttributeMaxDynamicSharedMemorySize, fft::shared_memory_size));\n\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int blocks_per_multiprocessor = 0;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      convolution_kernel<fft, ifft>,\n                                                      fft::block_dim.x * fft::block_dim.y * fft::block_dim.z,\n                                                      fft::shared_memory_size));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * ffts_per_block * fft_size * sizeof(complex_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks  = device_blocks * executed_blocks_multiplyer;\n    const unsigned int input_length = ffts_per_block * cuda_blocks * fft_size;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Host data\n    std::vector<complex_type> input =\n        example::get_random_complex_data<typename complex_type::value_type>(input_length, -10, 10);\n\n    // Device data\n    complex_type* device_buffer;\n    auto          input_size_bytes = input.size() * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&device_buffer, input_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(device_buffer, input.data(), input_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Measure performance\n    double time_cufftdx = measure_cufftdx<fft, ifft>(kernel_runs, warm_up_runs, cuda_blocks, device_buffer, stream);\n    double time_cufft   = measure_cufft<fft_size>(\n        kernel_runs, warm_up_runs, cuda_blocks * ffts_per_block, (hipfftComplex*)device_buffer, stream);\n    #ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n    double time_cufft_cb = measure_cufft_callback<fft_size>(\n        kernel_runs, warm_up_runs, cuda_blocks * ffts_per_block, (hipfftComplex*)device_buffer, stream);\n    #endif\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(device_buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        double gflops = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                        (std::log(fft_size) / std::log(2)) / time / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / kernel_runs << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n\n    if (verbose) {\n        std::cout << \"FFT size: \" << fft_size << std::endl;\n        std::cout << \"FFTs run: \" << ffts_per_block * cuda_blocks << std::endl;\n        report_time_and_performance(\"cuFFTDx\", time_cufftdx);\n        std::cout << \"FFTs elements per thread: \" << fft::elements_per_thread << std::endl;\n        std::cout << \"FFTs per block: \" << ffts_per_block << std::endl;\n        std::cout << \"CUDA blocks: \" << cuda_blocks << std::endl;\n        std::cout << \"Blocks per multiprocessor: \" << blocks_per_multiprocessor << std::endl;\n\n        report_time_and_performance(\"cuFFT\", time_cufft);\n        #ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n        report_time_and_performance(\"cuFFT Callback\", time_cufft_cb);\n        #endif\n    } else {\n        double gflops_cufftdx = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                                (std::log(fft_size) / std::log(2)) / time_cufftdx / 1000000.0;\n        double gflops_cufft = 1.0 * kernel_runs * ffts_per_block * cuda_blocks * 5.0 * fft_size *\n                              (std::log(fft_size) / std::log(2)) / time_cufft / 1000000.0;\n        std::cout << fft_size << \": \" << std::endl\n                  << gflops_cufftdx << \", \" << time_cufftdx / kernel_runs << \", \" << std::endl\n                  << gflops_cufft << \", \" << time_cufft / kernel_runs;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() {\n        return convolution<Arch>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/06_convolution/convolution_performance.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class IFFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFT>::value;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// This example demonstrates how to use cuFFTDx t operform a convolution using one-dimensional FFTs.\n//\n// One block is run, it calculates two 128-point convolutions by first doing forward FFT, then\n// applying pointwise operation, and ending with inverse FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int ffts_per_block = 2;\n    static constexpr unsigned int fft_size       = 128;\n    // FFT_base defined common options for FFT and IFFT. FFT_base is not a complete FFT description.\n    // In order to complete FFT description directions are specified: forward for FFT, inverse for IFFT.\n    using FFT_base     = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Precision<float>() +\n                              ElementsPerThread<8>() + FFTsPerBlock<ffts_per_block>() + SM<Arch>());\n    using FFT          = decltype(FFT_base() + Direction<fft_direction::forward>());\n    using IFFT         = decltype(FFT_base() + Direction<fft_direction::inverse>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = ffts_per_block * fft_size;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        convolution_kernel<FFT, IFFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes convolution kernel with FFT::block_dim threads in CUDA block\n    convolution_kernel<FFT, IFFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() { return convolution<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class IFFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFT>::value;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// This example demonstrates how to use cuFFTDx t operform a convolution using one-dimensional FFTs.\n//\n// One block is run, it calculates two 128-point convolutions by first doing forward FFT, then\n// applying pointwise operation, and ending with inverse FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int ffts_per_block = 2;\n    static constexpr unsigned int fft_size       = 128;\n    // FFT_base defined common options for FFT and IFFT. FFT_base is not a complete FFT description.\n    // In order to complete FFT description directions are specified: forward for FFT, inverse for IFFT.\n    using FFT_base     = decltype(Block() + Size<fft_size>() + Type<fft_type::c2c>() + Precision<float>() +\n                              ElementsPerThread<8>() + FFTsPerBlock<ffts_per_block>() + SM<Arch>());\n    using FFT          = decltype(FFT_base() + Direction<fft_direction::forward>());\n    using IFFT         = decltype(FFT_base() + Direction<fft_direction::inverse>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = ffts_per_block * fft_size;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(convolution_kernel<FFT, IFFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes convolution kernel with FFT::block_dim threads in CUDA block\n    convolution_kernel<FFT, IFFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() { return convolution<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/06_convolution/convolution.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFTR2C, class FFTC2R>\n__launch_bounds__(FFTR2C::max_threads_per_block) __global__ void convolution_kernel(cufftdx::precision_of_t<FFTR2C>* data) {\n    using complex_type = typename FFTR2C::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFTR2C::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFTR2C::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFTR2C>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFTR2C().execute(thread_data, shared_mem);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFTR2C>::value;\n    for (unsigned int i = 0; i < FFTR2C::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    FFTC2R().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFTC2R>::store(thread_data, data, local_fft_id);\n}\n\n// This example demonstrates how to use cuFFTDx t operform a convolution using one-dimensional FFTs.\n//\n// One block is run, it calculates two 128-point convolutions by first doing forward FFT, then\n// applying pointwise operation, and ending with inverse FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int ffts_per_block = 2;\n    static constexpr unsigned int fft_size       = 128;\n    // FFT_base defined common options for FFT and IFFT. FFT_base is not a complete FFT description.\n    // In order to complete FFT description directions are specified: forward for FFT, inverse for IFFT.\n    using FFT_base  = decltype(Block() + Size<fft_size>() + Precision<float>() +\n                              ElementsPerThread<8>() + FFTsPerBlock<ffts_per_block>() + SM<Arch>());\n    using FFTR2C    = decltype(FFT_base() + Type<fft_type::r2c>());\n    using FFTC2R    = decltype(FFT_base() + Type<fft_type::c2r>());\n    using real_type = precision_of_t<FFTR2C>;\n\n    // Allocate managed memory for input/output\n    real_type* data;\n    auto       size       = ffts_per_block * fft_size;\n    auto       size_bytes = size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = float(i);\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i] << std::endl;\n    }\n\n    const auto shared_memory_size = std::max(FFTR2C::shared_memory_size, FFTC2R::shared_memory_size);\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        convolution_kernel<FFTR2C, FFTC2R>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        shared_memory_size));\n\n    // Invokes convolution kernel with FFT::block_dim threads in CUDA block\n    convolution_kernel<FFTR2C, FFTC2R><<<1, FFTR2C::block_dim, shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() { return convolution<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFTR2C, class FFTC2R>\n__launch_bounds__(FFTR2C::max_threads_per_block) __global__ void convolution_kernel(cufftdx::precision_of_t<FFTR2C>* data) {\n    using complex_type = typename FFTR2C::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFTR2C::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFTR2C::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFTR2C>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFTR2C().execute(thread_data, shared_mem);\n\n    // Scale values\n    scalar_type scale = 1.0 / cufftdx::size_of<FFTR2C>::value;\n    for (unsigned int i = 0; i < FFTR2C::elements_per_thread; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    FFTC2R().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFTC2R>::store(thread_data, data, local_fft_id);\n}\n\n// This example demonstrates how to use cuFFTDx t operform a convolution using one-dimensional FFTs.\n//\n// One block is run, it calculates two 128-point convolutions by first doing forward FFT, then\n// applying pointwise operation, and ending with inverse FFT.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int ffts_per_block = 2;\n    static constexpr unsigned int fft_size       = 128;\n    // FFT_base defined common options for FFT and IFFT. FFT_base is not a complete FFT description.\n    // In order to complete FFT description directions are specified: forward for FFT, inverse for IFFT.\n    using FFT_base  = decltype(Block() + Size<fft_size>() + Precision<float>() +\n                              ElementsPerThread<8>() + FFTsPerBlock<ffts_per_block>() + SM<Arch>());\n    using FFTR2C    = decltype(FFT_base() + Type<fft_type::r2c>());\n    using FFTC2R    = decltype(FFT_base() + Type<fft_type::c2r>());\n    using real_type = precision_of_t<FFTR2C>;\n\n    // Allocate managed memory for input/output\n    real_type* data;\n    auto       size       = ffts_per_block * fft_size;\n    auto       size_bytes = size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = float(i);\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i] << std::endl;\n    }\n\n    const auto shared_memory_size = std::max(FFTR2C::shared_memory_size, FFTC2R::shared_memory_size);\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(convolution_kernel<FFTR2C, FFTC2R>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        shared_memory_size));\n\n    // Invokes convolution kernel with FFT::block_dim threads in CUDA block\n    convolution_kernel<FFTR2C, FFTC2R><<<1, FFTR2C::block_dim, shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < fft_size; i++) {\n        std::cout << data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() { return convolution<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/06_convolution/convolution_r2c_c2r.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cmath>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include <cufft.h>\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n#include <cufftXt.h>\n#endif\n\n#include \"../common/padded_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\nconstexpr float scaling_factor = 123.f;\n\ntemplate<int SignalLength, class FFT, class IFFT,\n         typename ComplexType = typename FFT::value_type, typename ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(ScalarType*                   in,\n                                                                                 ScalarType*                   out,\n                                                                                 typename FFT::workspace_type  workspace,\n                                                                                 typename IFFT::workspace_type workspace_inverse) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    constexpr bool needs_padding = (SignalLength != cufftdx::size_of<FFT>::value);\n\n    using input_utils = std::conditional_t<needs_padding, example::io_padded<FFT, SignalLength>, example::io<FFT>>;\n    using output_utils = std::conditional_t<needs_padding, example::io_padded<IFFT, SignalLength>, example::io<IFFT>>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    input_utils::load(in, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Scale values\n    scalar_type scale = (1.0 / cufftdx::size_of<FFT>::value) * scaling_factor;\n    for (unsigned int i = 0; i < FFT::storage_size; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem, workspace_inverse);\n\n    // Save results\n    output_utils::store(thread_data, out, local_fft_id);\n}\n\n// Scaling kernel; transforms data between cuFFTs.\ntemplate<unsigned int fft_size>\n__global__ void scaling_kernel(cufftComplex*      data,\n                               const unsigned int input_size,\n                               const unsigned int ept) {\n\n    static constexpr float scale = (1.f / fft_size) * scaling_factor;\n\n    cufftComplex temp;\n    unsigned int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < ept; i++) {\n        if (index < input_size) {\n            temp = data[index];\n            temp.x *= scale;\n            temp.y *= scale;\n            data[index] = temp;\n            index += blockDim.x * gridDim.x;\n        }\n    }\n}\n\ntemplate<int SignalLength, class FFT, class IFFT, typename ComplexType = typename FFT::value_type,\n         typename ScalarType = typename ComplexType::value_type>\nexample::fft_results<float>\nmeasure_cufftdx(const unsigned int       kernel_runs,\n                const unsigned int       warm_up_runs,\n                const unsigned int       correctness_batches,\n                const unsigned int       cuda_blocks,\n                ScalarType*              input_data,\n                ScalarType*              output_data,\n                cudaStream_t             stream) {\n\n    using namespace cufftdx;\n    using complex_type = typename FFT::value_type;\n\n    const auto size = SignalLength * correctness_batches;\n\n    // create workspaces for FFT and IFFT\n    cudaError_t error_code = cudaSuccess;\n    auto        workspace  = make_workspace<FFT>(error_code);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_inverse = make_workspace<IFFT>(error_code);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Initial execution for correctness\n    convolution_kernel<SignalLength, FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                input_data, output_data, workspace, workspace_inverse);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Create host result vector to be returned later\n    CUFFTDX_STD::vector<float> host_output(size);\n    auto size_bytes = size * sizeof(float);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(reinterpret_cast<void*>(host_output.data()),\n                                   reinterpret_cast<void*>(output_data),\n                                   size_bytes, cudaMemcpyDeviceToHost));\n\n    // run cuFFTDx\n    float time = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            convolution_kernel<SignalLength, FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                input_data, output_data, workspace, workspace_inverse);\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    return {host_output, time};\n}\n\ntemplate<unsigned int SignalLength, typename PaddedFFT>\nexample::fft_results<float>\nmeasure_cufft(const unsigned int kernel_runs,\n              const unsigned int warm_up_runs,\n              const unsigned int correctness_batches,\n              const unsigned int batch_size,\n              cufftReal*          input_data,\n              cufftReal*          output_data,\n              cufftComplex*       work_buffer,\n              cudaStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n    const auto size = SignalLength * correctness_batches;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n\n    // cuFFT always uses layout equivalent to cuFFTDx complex_layout::natural\n    // hence the hard coded indexing.\n    constexpr auto convolved_data_length = SignalLength / 2 + 1;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<SignalLength>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    const unsigned int cuda_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    const unsigned int input_length        = convolved_data_length * batch_size;\n    const unsigned int elements_per_block  = (input_length + cuda_blocks - 1) / cuda_blocks;\n    const unsigned int elements_per_thread = (elements_per_block + block_dim_scaling_kernel - 1) / block_dim_scaling_kernel;\n\n    // prepare cuFFT runs\n    cufftHandle plan_forward, plan_inverse;\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan_forward, SignalLength, CUFFT_R2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(cufftPlan1d(&plan_inverse, SignalLength, CUFFT_C2R, batch_size));\n\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_forward, stream));\n    CUFFT_CHECK_AND_EXIT(cufftSetStream(plan_inverse, stream));\n\n    // Single execution for correctness check\n    if (cufftExecR2C(plan_forward, input_data, work_buffer) != CUFFT_SUCCESS) {\n        fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n        return {};\n    }\n    scaling_kernel<SignalLength>\n        <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(work_buffer, input_length, elements_per_thread);\n    if (cufftExecC2R(plan_inverse, work_buffer, output_data) != CUFFT_SUCCESS) {\n        fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n        return {};\n    }\n\n    // Create host result vector to be returned later\n    CUFFTDX_STD::vector<float> host_output(size);\n    auto size_bytes = size * sizeof(float);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(reinterpret_cast<void*>(host_output.data()),\n                                   reinterpret_cast<void*>(output_data),\n                                   size_bytes, cudaMemcpyDeviceToHost));\n\n    // run convolution\n    float time_cufft = example::measure_execution_ms(\n        [&](cudaStream_t stream) {\n            if (cufftExecR2C(plan_forward, input_data, work_buffer) != CUFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n                return;\n            }\n            scaling_kernel<SignalLength>\n                <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(work_buffer, input_length, elements_per_thread);\n            if (cufftExecC2R(plan_inverse, work_buffer, output_data) != CUFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n                return;\n            }\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_forward));\n    CUFFT_CHECK_AND_EXIT(cufftDestroy(plan_inverse));\n    return {host_output, time_cufft};\n}\n\n// This example showcases a method for using cuFFTDx features to get superior\n// performance in performing a R2C / C2R convolution. To achieve this goal\n// the real_mode::folded optimization is used and input data is padded with\n// zeros to the closest power of 2. Final performance is compared with cuFFTDx\n// executed without padding and optimization and cuFFT based convolution.\n// --------------------------------------------------------------------------\n// Data is generated on host, copied to device buffer and processed by FFTs.\n// Each cuFFTDx execution runs one kernel, each cuFFT execution - three kernels.\n// The experiment runs with the following principles:\n// - at least 2GB of data is allocated in GPU and transformed by both convolutions,\n// - for cuFFTDx kernel run, number of CUDA blocks is always divisible\n//   by maximum number of CUDA blocks that can run simultaneously on the GPU.\ntemplate<unsigned int Arch, int SignalLength>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int minimum_input_size_bytes   = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int signal_length              = SignalLength;\n    static constexpr unsigned int fft_size                   = example::closest_power_of_2(signal_length);\n    static constexpr unsigned int kernel_runs                = 10;\n    static constexpr unsigned int warm_up_runs               = 1;\n\n    // To determine the total input length (number of fft batches to run), the maximum number of\n    // simultanously running cuFFTDx CUDA blocks is calculated.\n\n    // Declaration of cuFFTDx padded FFT\n    using padded_fft_incomplete = decltype(Block() + Size<fft_size>() + Precision<float>() + SM<Arch>());\n    using padded_real_options = RealFFTOptions<complex_layout::natural, real_mode::folded>;\n    using padded_fft_base       = decltype(padded_fft_incomplete() + Type<fft_type::r2c>() + padded_real_options());\n    using padded_ifft_base      = decltype(padded_fft_incomplete() + Type<fft_type::c2r>() + padded_real_options());\n\n    static constexpr unsigned int padded_elements_per_thread = padded_fft_base::elements_per_thread;\n    static constexpr unsigned int padded_ffts_per_block      = padded_fft_base::suggested_ffts_per_block;\n\n    using padded_fft          = decltype(padded_fft_base() + ElementsPerThread<padded_elements_per_thread>() + FFTsPerBlock<padded_ffts_per_block>());\n    using padded_ifft         = decltype(padded_ifft_base() + ElementsPerThread<padded_elements_per_thread>() + FFTsPerBlock<padded_ffts_per_block>());\n\n    // Declaration of non-padded FFT\n    using fft_incomplete = decltype(Block() + Size<signal_length>() + Precision<float>() + SM<Arch>());\n    using real_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n    using fft_base       = decltype(fft_incomplete() + Type<fft_type::r2c>() + real_options());\n    using ifft_base      = decltype(fft_incomplete() + Type<fft_type::c2r>() + real_options());\n\n    static constexpr unsigned int elements_per_thread = fft_base::elements_per_thread;\n    static constexpr unsigned int ffts_per_block      = fft_base::suggested_ffts_per_block;\n\n    using fft          = decltype(fft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using ifft         = decltype(ifft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n\n    using complex_type = typename fft::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        convolution_kernel<signal_length, padded_fft, padded_ifft>, cudaFuncAttributeMaxDynamicSharedMemorySize, padded_fft::shared_memory_size));\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        convolution_kernel<signal_length, fft, ifft>, cudaFuncAttributeMaxDynamicSharedMemorySize, fft::shared_memory_size));\n\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int bpm_padded = 0, bpm = 0;\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&bpm_padded,\n                                                      convolution_kernel<signal_length, padded_fft, padded_ifft>,\n                                                      padded_fft::block_dim.x * padded_fft::block_dim.y * padded_fft::block_dim.z,\n                                                      padded_fft::shared_memory_size));\n    CUDA_CHECK_AND_EXIT(\n        cudaOccupancyMaxActiveBlocksPerMultiprocessor(&bpm,\n                                                      convolution_kernel<signal_length, fft, ifft>,\n                                                      fft::block_dim.x * fft::block_dim.y * fft::block_dim.z,\n                                                      fft::shared_memory_size));\n\n    // Make sure that full wavefronts will be executed for both kernels\n    int blocks_per_multiprocessor = bpm_padded * bpm;\n\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * ffts_per_block * padded_ffts_per_block * signal_length * sizeof(complex_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks = device_blocks * executed_blocks_multiplyer;\n    const unsigned int cuda_blocks_padded  = cuda_blocks * ffts_per_block;\n    const unsigned int cuda_blocks_non_padded  = cuda_blocks * padded_ffts_per_block;\n    const unsigned int input_length = ffts_per_block * padded_ffts_per_block * cuda_blocks * signal_length;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    // Host data\n    std::vector<scalar_type> input =\n        example::get_random_real_data<scalar_type>(input_length, -1, 1);\n\n    // Device data\n    scalar_type* input_data, *output;\n    auto          input_size_bytes = input.size() * sizeof(scalar_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&input_data, input_size_bytes));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&output, input_size_bytes));\n\n    complex_type* work_buffer;\n    auto          work_size_bytes = (ffts_per_block * padded_ffts_per_block * cuda_blocks * (signal_length / 2 + 1)) * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMalloc(&work_buffer, work_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(input_data, input.data(), input_size_bytes, cudaMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n\n\n    // Number of batches to use for correctness verification\n    // Entire input is:\n    // ffts_per_block * padded_ffts_per_block * cuda blocks\n    const int correctness_batches = ffts_per_block * padded_ffts_per_block * cuda_blocks;\n\n    // Measure performance and obtain results for correctness verification\n    auto res_cufft   = measure_cufft<signal_length, fft>(\n        kernel_runs, warm_up_runs, correctness_batches, cuda_blocks * ffts_per_block * padded_ffts_per_block, (cufftReal*)input_data, output, (cufftComplex*)work_buffer, stream);\n    auto res_cufftdx = measure_cufftdx<signal_length, fft, ifft>(kernel_runs, warm_up_runs, correctness_batches, cuda_blocks_non_padded, input_data, output, stream);\n    auto res_cufftdx_padded = measure_cufftdx<signal_length, padded_fft, padded_ifft>(kernel_runs, warm_up_runs, correctness_batches, cuda_blocks_padded, input_data, output, stream);\n\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output));\n    CUDA_CHECK_AND_EXIT(cudaFree(work_buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        double gflops = 1.0 * kernel_runs * ffts_per_block * padded_ffts_per_block * cuda_blocks * 5.0 * signal_length *\n                        (std::log(signal_length) / std::log(2)) / time / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / kernel_runs << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n    bool success = true;\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_real_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.01);\n        }\n    };\n\n    verify_correctness(res_cufftdx.output, \"cuFFTDx Bluestein\", res_cufft.output, \"cuFFT\");\n    verify_correctness(res_cufftdx_padded.output, \"cuFFTDx Padded\", res_cufft.output, \"cuFFT\");\n    verify_correctness(res_cufftdx_padded.output, \"cuFFTDx Padded\", res_cufftdx.output, \"cuFFTDx Bluestein\");\n\n    std::cout << \"FFT size: \" << signal_length << std::endl;\n    std::cout << \"FFTs run: \" << ffts_per_block * padded_ffts_per_block * cuda_blocks << std::endl;\n    std::cout << \"Correctness tested batches: \" << correctness_batches << std::endl;\n\n    report_time_and_performance(\"cuFFTDx\", res_cufftdx.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Padded\", res_cufftdx_padded.avg_time_in_ms);\n    report_time_and_performance(\"cuFFT\", res_cufft.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success!\" << std::endl;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() {\n        std::cout << \"Power of 2:\" << std::endl;\n        convolution<Arch, 2048>();\n        std::cout << \"Power of 3:\" << std::endl;\n        convolution<Arch, 6561>();\n        std::cout << \"Power of 12:\" << std::endl;\n        convolution<Arch, 1728>();\n        std::cout << \"Odd length:\" << std::endl;\n        convolution<Arch, 2003>();\n        std::cout << \"Bluestein:\" << std::endl;\n        convolution<Arch, 3668>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <chrono>\n#include <cmath>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include <hipfft/hipfft.h>\n#ifdef CUFFTDX_EXAMPLES_CUFFT_CALLBACK\n#include <hipfft/hipfftXt.h>\n#endif\n\n#include \"../common/padded_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/random.hpp\"\n\nconstexpr float scaling_factor = 123.f;\n\ntemplate<int SignalLength, class FFT, class IFFT,\n         typename ComplexType = typename FFT::value_type, typename ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void convolution_kernel(ScalarType*                   in,\n                                                                                 ScalarType*                   out,\n                                                                                 typename FFT::workspace_type  workspace,\n                                                                                 typename IFFT::workspace_type workspace_inverse) {\n    using complex_type = typename FFT::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    constexpr bool needs_padding = (SignalLength != cufftdx::size_of<FFT>::value);\n\n    using input_utils = std::conditional_t<needs_padding, example::io_padded<FFT, SignalLength>, example::io<FFT>>;\n    using output_utils = std::conditional_t<needs_padding, example::io_padded<IFFT, SignalLength>, example::io<IFFT>>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n\n    // Load data from global memory to registers\n    input_utils::load(in, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem, workspace);\n\n    // Scale values\n    scalar_type scale = (1.0 / cufftdx::size_of<FFT>::value) * scaling_factor;\n    for (unsigned int i = 0; i < FFT::storage_size; i++) {\n        thread_data[i].x *= scale;\n        thread_data[i].y *= scale;\n    }\n\n    // Execute inverse FFT\n    IFFT().execute(thread_data, shared_mem, workspace_inverse);\n\n    // Save results\n    output_utils::store(thread_data, out, local_fft_id);\n}\n\n// Scaling kernel; transforms data between cuFFTs.\ntemplate<unsigned int fft_size>\n__global__ void scaling_kernel(hipfftComplex*      data,\n                               const unsigned int input_size,\n                               const unsigned int ept) {\n\n    static constexpr float scale = (1.f / fft_size) * scaling_factor;\n\n    hipfftComplex temp;\n    unsigned int index = blockDim.x * blockIdx.x + threadIdx.x;\n\n    for (int i = 0; i < ept; i++) {\n        if (index < input_size) {\n            temp = data[index];\n            temp.x *= scale;\n            temp.y *= scale;\n            data[index] = temp;\n            index += blockDim.x * gridDim.x;\n        }\n    }\n}\n\ntemplate<int SignalLength, class FFT, class IFFT, typename ComplexType = typename FFT::value_type,\n         typename ScalarType = typename ComplexType::value_type>\nexample::fft_results<float>\nmeasure_cufftdx(const unsigned int       kernel_runs,\n                const unsigned int       warm_up_runs,\n                const unsigned int       correctness_batches,\n                const unsigned int       cuda_blocks,\n                ScalarType*              input_data,\n                ScalarType*              output_data,\n                hipStream_t             stream) {\n\n    using namespace cufftdx;\n    using complex_type = typename FFT::value_type;\n\n    const auto size = SignalLength * correctness_batches;\n\n    // create workspaces for FFT and IFFT\n    hipError_t error_code = hipSuccess;\n    auto        workspace  = make_workspace<FFT>(error_code);\n    CUDA_CHECK_AND_EXIT(error_code);\n    auto workspace_inverse = make_workspace<IFFT>(error_code);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    // Initial execution for correctness\n    convolution_kernel<SignalLength, FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                input_data, output_data, workspace, workspace_inverse);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Create host result vector to be returned later\n    CUFFTDX_STD::vector<float> host_output(size);\n    auto size_bytes = size * sizeof(float);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(reinterpret_cast<void*>(host_output.data()),\n                                   reinterpret_cast<void*>(output_data),\n                                   size_bytes, hipMemcpyDeviceToHost));\n\n    // run cuFFTDx\n    float time = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            // There are (ffts_per_block * fft_size * cuda_blocks) elements\n            convolution_kernel<SignalLength, FFT, IFFT><<<cuda_blocks, FFT::block_dim, FFT::shared_memory_size, stream>>>(\n                input_data, output_data, workspace, workspace_inverse);\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    return {host_output, time};\n}\n\ntemplate<unsigned int SignalLength, typename PaddedFFT>\nexample::fft_results<float>\nmeasure_cufft(const unsigned int kernel_runs,\n              const unsigned int warm_up_runs,\n              const unsigned int correctness_batches,\n              const unsigned int batch_size,\n              hipfftReal*          input_data,\n              hipfftReal*          output_data,\n              hipfftComplex*       work_buffer,\n              hipStream_t        stream) {\n\n    static constexpr unsigned int block_dim_scaling_kernel = 1024;\n    const auto size = SignalLength * correctness_batches;\n\n    // Calculating parameters for scaling_kernel execution.\n    // Get maximum number of running CUDA blocks per multiprocessor.\n    int blocks_per_multiprocessor = 0;\n\n    // cuFFT always uses layout equivalent to cuFFTDx complex_layout::natural\n    // hence the hard coded indexing.\n    constexpr auto convolved_data_length = SignalLength / 2 + 1;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_multiprocessor,\n                                                      scaling_kernel<SignalLength>,\n                                                      block_dim_scaling_kernel,\n                                                      0));\n\n    // Get maximum number of CUDA blocks running on all multiprocessors.\n    // This many CUDA blocks will be run for simple_kernel.\n    const unsigned int cuda_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    const unsigned int input_length        = convolved_data_length * batch_size;\n    const unsigned int elements_per_block  = (input_length + cuda_blocks - 1) / cuda_blocks;\n    const unsigned int elements_per_thread = (elements_per_block + block_dim_scaling_kernel - 1) / block_dim_scaling_kernel;\n\n    // prepare cuFFT runs\n    hipfftHandle plan_forward, plan_inverse;\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan_forward, SignalLength, HIPFFT_R2C, batch_size));\n    CUFFT_CHECK_AND_EXIT(hipfftPlan1d(&plan_inverse, SignalLength, HIPFFT_C2R, batch_size));\n\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_forward, stream));\n    CUFFT_CHECK_AND_EXIT(hipfftSetStream(plan_inverse, stream));\n\n    // Single execution for correctness check\n    if (hipfftExecR2C(plan_forward, input_data, work_buffer) != HIPFFT_SUCCESS) {\n        fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n        return {};\n    }\n    scaling_kernel<SignalLength>\n        <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(work_buffer, input_length, elements_per_thread);\n    if (hipfftExecC2R(plan_inverse, work_buffer, output_data) != HIPFFT_SUCCESS) {\n        fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n        return {};\n    }\n\n    // Create host result vector to be returned later\n    CUFFTDX_STD::vector<float> host_output(size);\n    auto size_bytes = size * sizeof(float);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(reinterpret_cast<void*>(host_output.data()),\n                                   reinterpret_cast<void*>(output_data),\n                                   size_bytes, hipMemcpyDeviceToHost));\n\n    // run convolution\n    float time_cufft = example::measure_execution_ms(\n        [&](hipStream_t stream) {\n            if (hipfftExecR2C(plan_forward, input_data, work_buffer) != HIPFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Forward failed\");\n                return;\n            }\n            scaling_kernel<SignalLength>\n                <<<cuda_blocks, block_dim_scaling_kernel, 0, stream>>>(work_buffer, input_length, elements_per_thread);\n            if (hipfftExecC2R(plan_inverse, work_buffer, output_data) != HIPFFT_SUCCESS) {\n                fprintf(stderr, \"CUFFT error: ExecC2C Inverse failed\");\n                return;\n            }\n        },\n        warm_up_runs, kernel_runs, stream);\n\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_forward));\n    CUFFT_CHECK_AND_EXIT(hipfftDestroy(plan_inverse));\n    return {host_output, time_cufft};\n}\n\n// This example showcases a method for using cuFFTDx features to get superior\n// performance in performing a R2C / C2R convolution. To achieve this goal\n// the real_mode::folded optimization is used and input data is padded with\n// zeros to the closest power of 2. Final performance is compared with cuFFTDx\n// executed without padding and optimization and cuFFT based convolution.\n// --------------------------------------------------------------------------\n// Data is generated on host, copied to device buffer and processed by FFTs.\n// Each cuFFTDx execution runs one kernel, each cuFFT execution - three kernels.\n// The experiment runs with the following principles:\n// - at least 2GB of data is allocated in GPU and transformed by both convolutions,\n// - for cuFFTDx kernel run, number of CUDA blocks is always divisible\n//   by maximum number of CUDA blocks that can run simultaneously on the GPU.\ntemplate<unsigned int Arch, int SignalLength>\nvoid convolution() {\n    using namespace cufftdx;\n\n    static constexpr unsigned int minimum_input_size_bytes   = (1 << 30); // At least one GB of data will be processed by FFTs.\n    static constexpr unsigned int signal_length              = SignalLength;\n    static constexpr unsigned int fft_size                   = example::closest_power_of_2(signal_length);\n    static constexpr unsigned int kernel_runs                = 10;\n    static constexpr unsigned int warm_up_runs               = 1;\n\n    // To determine the total input length (number of fft batches to run), the maximum number of\n    // simultanously running cuFFTDx CUDA blocks is calculated.\n\n    // Declaration of cuFFTDx padded FFT\n    using padded_fft_incomplete = decltype(Block() + Size<fft_size>() + Precision<float>() + SM<Arch>());\n    using padded_real_options = RealFFTOptions<complex_layout::natural, real_mode::folded>;\n    using padded_fft_base       = decltype(padded_fft_incomplete() + Type<fft_type::r2c>() + padded_real_options());\n    using padded_ifft_base      = decltype(padded_fft_incomplete() + Type<fft_type::c2r>() + padded_real_options());\n\n    static constexpr unsigned int padded_elements_per_thread = padded_fft_base::elements_per_thread;\n    static constexpr unsigned int padded_ffts_per_block      = padded_fft_base::suggested_ffts_per_block;\n\n    using padded_fft          = decltype(padded_fft_base() + ElementsPerThread<padded_elements_per_thread>() + FFTsPerBlock<padded_ffts_per_block>());\n    using padded_ifft         = decltype(padded_ifft_base() + ElementsPerThread<padded_elements_per_thread>() + FFTsPerBlock<padded_ffts_per_block>());\n\n    // Declaration of non-padded FFT\n    using fft_incomplete = decltype(Block() + Size<signal_length>() + Precision<float>() + SM<Arch>());\n    using real_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n    using fft_base       = decltype(fft_incomplete() + Type<fft_type::r2c>() + real_options());\n    using ifft_base      = decltype(fft_incomplete() + Type<fft_type::c2r>() + real_options());\n\n    static constexpr unsigned int elements_per_thread = fft_base::elements_per_thread;\n    static constexpr unsigned int ffts_per_block      = fft_base::suggested_ffts_per_block;\n\n    using fft          = decltype(fft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n    using ifft         = decltype(ifft_base() + ElementsPerThread<elements_per_thread>() + FFTsPerBlock<ffts_per_block>());\n\n    using complex_type = typename fft::value_type;\n    using scalar_type  = typename complex_type::value_type;\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(convolution_kernel<signal_length, padded_fft, padded_ifft>), hipFuncAttributeMaxDynamicSharedMemorySize, padded_fft::shared_memory_size));\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(convolution_kernel<signal_length, fft, ifft>), hipFuncAttributeMaxDynamicSharedMemorySize, fft::shared_memory_size));\n\n    // Get maximum number of running CUDA blocks per multiprocessor\n    int bpm_padded = 0, bpm = 0;\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&bpm_padded,\n                                                      convolution_kernel<signal_length, padded_fft, padded_ifft>,\n                                                      padded_fft::block_dim.x * padded_fft::block_dim.y * padded_fft::block_dim.z,\n                                                      padded_fft::shared_memory_size));\n    CUDA_CHECK_AND_EXIT(\n        hipOccupancyMaxActiveBlocksPerMultiprocessor(&bpm,\n                                                      convolution_kernel<signal_length, fft, ifft>,\n                                                      fft::block_dim.x * fft::block_dim.y * fft::block_dim.z,\n                                                      fft::shared_memory_size));\n\n    // Make sure that full wavefronts will be executed for both kernels\n    int blocks_per_multiprocessor = bpm_padded * bpm;\n\n    // Get maximum number of CUDA blocks running on all multiprocessors\n    const unsigned int device_blocks = blocks_per_multiprocessor * example::get_multiprocessor_count();\n\n    // Input size in bytes if device_blocks CUDA blocks were run.\n    const unsigned int data_size_device_blocks_bytes = device_blocks * ffts_per_block * padded_ffts_per_block * signal_length * sizeof(complex_type);\n\n    // cuda_blocks = minimal number of CUDA blocks to run, such that:\n    //   - cuda_blocks is divisible by device_blocks,\n    //   - total input size is not less than minimum_input_size_bytes.\n    // executed_blocks_multiplyer = cuda_blocks / device_blocks\n    const unsigned int executed_blocks_multiplyer =\n        (minimum_input_size_bytes + data_size_device_blocks_bytes - 1) / data_size_device_blocks_bytes;\n    const unsigned int cuda_blocks = device_blocks * executed_blocks_multiplyer;\n    const unsigned int cuda_blocks_padded  = cuda_blocks * ffts_per_block;\n    const unsigned int cuda_blocks_non_padded  = cuda_blocks * padded_ffts_per_block;\n    const unsigned int input_length = ffts_per_block * padded_ffts_per_block * cuda_blocks * signal_length;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    // Host data\n    std::vector<scalar_type> input =\n        example::get_random_real_data<scalar_type>(input_length, -1, 1);\n\n    // Device data\n    scalar_type* input_data, *output;\n    auto          input_size_bytes = input.size() * sizeof(scalar_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&input_data, input_size_bytes));\n    CUDA_CHECK_AND_EXIT(hipMalloc(&output, input_size_bytes));\n\n    complex_type* work_buffer;\n    auto          work_size_bytes = (ffts_per_block * padded_ffts_per_block * cuda_blocks * (signal_length / 2 + 1)) * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMalloc(&work_buffer, work_size_bytes));\n\n    // Copy host to device\n    CUDA_CHECK_AND_EXIT(hipMemcpy(input_data, input.data(), input_size_bytes, hipMemcpyHostToDevice));\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n\n\n    // Number of batches to use for correctness verification\n    // Entire input is:\n    // ffts_per_block * padded_ffts_per_block * cuda blocks\n    const int correctness_batches = ffts_per_block * padded_ffts_per_block * cuda_blocks;\n\n    // Measure performance and obtain results for correctness verification\n    auto res_cufft   = measure_cufft<signal_length, fft>(\n        kernel_runs, warm_up_runs, correctness_batches, cuda_blocks * ffts_per_block * padded_ffts_per_block, (hipfftReal*)input_data, output, (hipfftComplex*)work_buffer, stream);\n    auto res_cufftdx = measure_cufftdx<signal_length, fft, ifft>(kernel_runs, warm_up_runs, correctness_batches, cuda_blocks_non_padded, input_data, output, stream);\n    auto res_cufftdx_padded = measure_cufftdx<signal_length, padded_fft, padded_ifft>(kernel_runs, warm_up_runs, correctness_batches, cuda_blocks_padded, input_data, output, stream);\n\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output));\n    CUDA_CHECK_AND_EXIT(hipFree(work_buffer));\n\n    // Report results.\n    auto report_time_and_performance = [&](std::string name, double time) -> void {\n        double gflops = 1.0 * kernel_runs * ffts_per_block * padded_ffts_per_block * cuda_blocks * 5.0 * signal_length *\n                        (std::log(signal_length) / std::log(2)) / time / 1000000.0;\n\n        std::cout << std::endl;\n        std::cout << name << std::endl;\n        std::cout << \"Avg Time [ms_n]: \" << time / kernel_runs << std::endl;\n        std::cout << \"Time (all) [ms_n]: \" << time << std::endl;\n        std::cout << \"Performance [GFLOPS]: \" << gflops << std::endl;\n    };\n    bool success = true;\n\n    auto verify_correctness = [&success](const auto& result_array, const auto& result_name, const auto& reference_array, const auto reference_name) {\n        auto fft_error = example::fft_signal_error::calculate_for_real_values(\n            result_array,\n            reference_array);\n\n        std::cout << \"Correctness results for: \" << result_name << \" using \" << reference_name << \" as reference\\n\";\n        std::cout << \"L2 relative error: \" << fft_error.l2_relative_error << \"\\n\";\n        std::cout << \"Peak error (index: \" << fft_error.peak_error_index << \"): \" << fft_error.peak_error << \"\\n\";\n\n        if (success) {\n            success = (fft_error.l2_relative_error < 0.01);\n        }\n    };\n\n    verify_correctness(res_cufftdx.output, \"cuFFTDx Bluestein\", res_cufft.output, \"cuFFT\");\n    verify_correctness(res_cufftdx_padded.output, \"cuFFTDx Padded\", res_cufft.output, \"cuFFT\");\n    verify_correctness(res_cufftdx_padded.output, \"cuFFTDx Padded\", res_cufftdx.output, \"cuFFTDx Bluestein\");\n\n    std::cout << \"FFT size: \" << signal_length << std::endl;\n    std::cout << \"FFTs run: \" << ffts_per_block * padded_ffts_per_block * cuda_blocks << std::endl;\n    std::cout << \"Correctness tested batches: \" << correctness_batches << std::endl;\n\n    report_time_and_performance(\"cuFFTDx\", res_cufftdx.avg_time_in_ms);\n    report_time_and_performance(\"cuFFTDx Padded\", res_cufftdx_padded.avg_time_in_ms);\n    report_time_and_performance(\"cuFFT\", res_cufft.avg_time_in_ms);\n\n    if (success) {\n        std::cout << \"Success!\" << std::endl;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct convolution_functor {\n    void operator()() {\n        std::cout << \"Power of 2:\" << std::endl;\n        convolution<Arch, 2048>();\n        std::cout << \"Power of 3:\" << std::endl;\n        convolution<Arch, 6561>();\n        std::cout << \"Power of 12:\" << std::endl;\n        convolution<Arch, 1728>();\n        std::cout << \"Odd length:\" << std::endl;\n        convolution<Arch, 2003>();\n        std::cout << \"Bluestein:\" << std::endl;\n        convolution<Arch, 3668>();\n    }\n};\n\nint main(int, char**) {\n    return example::sm_runner<convolution_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/06_convolution/convolution_padded.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n// Check if used version of libcu++ supports cuda::std::complex\n#include <cuda/std/version>\n#if _LIBCUDACXX_CUDA_API_VERSION < 001004000\nint main(int, char**) {\n    std::cout << \"Example disabled, cuda::std::complex is only supported since libcu++ 1.4.0 (CUDA 11.3)\" << std::endl;\n    return 0;\n}\n#else\n\n#include <cuda/std/complex>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(ComplexType* data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    // Use cuda::std::complex instead of FFT::value_type\n    using complex_type = cuda::std::complex<typename cufftdx::precision_of<FFT>::type>;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].real() << \" \" << data[i].imag() << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT, complex_type>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT, complex_type><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].real() << \" \" << data[i].imag() << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n\n#endif // (_LIBCUDACXX_CUDA_API_VERSION < 001004000)\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n// Check if used version of libcu++ supports cuda::std::complex\n#include <cuda/std/version>\n#if _LIBCUDACXX_CUDA_API_VERSION < 001004000\nint main(int, char**) {\n    std::cout << \"Example disabled, cuda::std::complex is only supported since libcu++ 1.4.0 (CUDA 11.3)\" << std::endl;\n    return 0;\n}\n#else\n\n#include <cuda/std/complex>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(ComplexType* data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    // Use cuda::std::complex instead of FFT::value_type\n    using complex_type = cuda::std::complex<typename cufftdx::precision_of<FFT>::type>;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].real() << \" \" << data[i].imag() << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT, complex_type>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT, complex_type><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].real() << \" \" << data[i].imag() << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n\n#endif // (_LIBCUDACXX_CUDA_API_VERSION < 001004000)\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_std_complex.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include <cub/cub.cuh>\n#include <cub/version.cuh>\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\n#if CUB_VERSION < 101300\nint main(int, char**) {\n    std::cout << \"Example disabled, BLOCK_LOAD_STRIPED/BLOCK_STORE_STRIPED is only supported since CUB 1.13 (CUDA 11.5)\" << std::endl;\n    return 0;\n}\n#else\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // FFT::stride == FFT::block_dim.x in most cases\n    using BlockLoad  = cub::BlockLoad <complex_type, FFT::stride /* BLOCK_DIM_X */, FFT::storage_size, cub::BLOCK_LOAD_STRIPED>;\n    using BlockStore = cub::BlockStore<complex_type, FFT::stride, FFT::storage_size, cub::BLOCK_STORE_STRIPED>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // ID of FFT in CUDA grid\n    unsigned int global_fft_id =\n        (FFT::ffts_per_block == 1) ? local_fft_id : ( blockIdx.x * FFT::ffts_per_block + local_fft_id);\n\n    // Load data from global memory to registers\n    auto fft_data = data + (global_fft_id * cufftdx::size_of<FFT>::value);\n    BlockLoad().Load(fft_data, thread_data, cufftdx::size_of<FFT>::value, complex_type { 0.0, 0.0 });\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    BlockStore().Store(fft_data, thread_data, cufftdx::size_of<FFT>::value);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block. CUB\n// library is used for IO in kernel.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using precision_type = float;\n    using complex_type = cufftdx::complex<precision_type>;\n\n    using FFT = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<precision_type>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < cufftdx::size_of<FFT>::value; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < cufftdx::size_of<FFT>::value; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n    auto sum = data[0].x;\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    if(std::abs(sum - ((cufftdx::size_of<FFT>::value-1) * cufftdx::size_of<FFT>::value / 2)) > 0.1) {\n        std::cout << \"Failed\" << std::endl;\n        return;\n    }\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n#endif // CUB_VERSION < 101300\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include <hipcub/hipcub.hpp>\n#include <cub/version.cuh>\n#include <cub/block/block_load.cuh>\n#include <cub/block/block_store.cuh>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\n#if CUB_VERSION < 101300\nint main(int, char**) {\n    std::cout << \"Example disabled, BLOCK_LOAD_STRIPED/BLOCK_STORE_STRIPED is only supported since CUB 1.13 (CUDA 11.5)\" << std::endl;\n    return 0;\n}\n#else\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // FFT::stride == FFT::block_dim.x in most cases\n    using BlockLoad  = hipcub::BlockLoad <complex_type, FFT::stride /* BLOCK_DIM_X */, FFT::storage_size, hipcub::BLOCK_LOAD_STRIPED>;\n    using BlockStore = hipcub::BlockStore<complex_type, FFT::stride, FFT::storage_size, hipcub::BLOCK_STORE_STRIPED>;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // ID of FFT in CUDA grid\n    unsigned int global_fft_id =\n        (FFT::ffts_per_block == 1) ? local_fft_id : ( blockIdx.x * FFT::ffts_per_block + local_fft_id);\n\n    // Load data from global memory to registers\n    auto fft_data = data + (global_fft_id * cufftdx::size_of<FFT>::value);\n    BlockLoad().Load(fft_data, thread_data, cufftdx::size_of<FFT>::value, complex_type { 0.0, 0.0 });\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    BlockStore().Store(fft_data, thread_data, cufftdx::size_of<FFT>::value);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block. CUB\n// library is used for IO in kernel.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using precision_type = float;\n    using complex_type = cufftdx::complex<precision_type>;\n\n    using FFT = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<precision_type>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < cufftdx::size_of<FFT>::value; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < cufftdx::size_of<FFT>::value; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n    auto sum = data[0].x;\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    if(std::abs(sum - ((cufftdx::size_of<FFT>::value-1) * cufftdx::size_of<FFT>::value / 2)) > 0.1) {\n        std::cout << \"Failed\" << std::endl;\n        return;\n    }\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n#endif // CUB_VERSION < 101300\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_cub_io.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_r2c_fp16(ScalarType* input_data, ComplexType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional real-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point R2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_r2c_fp16() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::r2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<16>() + FFTsPerBlock<4>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;          // complex<__half2>\n    using real_type    = typename complex_type::value_type; // __half2\n\n    // Allocate managed memory for input/output\n    real_type* input_data;\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    auto             input_size        = FFT::ffts_per_block / implicit_batching * cufftdx::size_of<FFT>::value;\n    auto             input_size_bytes  = input_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = __half2 {float(i), float(i + input_size)};\n    }\n    complex_type* output_data;\n    auto          output_size       = FFT::ffts_per_block / implicit_batching * FFT::output_length;\n    auto          output_size_bytes = output_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input_data[i].x) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel_r2c_fp16<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_r2c_fp16<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output_data[i].x.x) << \" \" << __half2float(output_data[i].x.y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_r2c_fp16_functor {\n    void operator()() { return simple_block_fft_r2c_fp16<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_r2c_fp16_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_r2c_fp16(ScalarType* input_data, ComplexType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional real-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point R2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_r2c_fp16() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::r2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<16>() + FFTsPerBlock<4>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;          // complex<__half2>\n    using real_type    = typename complex_type::value_type; // __half2\n\n    // Allocate managed memory for input/output\n    real_type* input_data;\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    auto             input_size        = FFT::ffts_per_block / implicit_batching * cufftdx::size_of<FFT>::value;\n    auto             input_size_bytes  = input_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = __half2 {float(i), float(i + input_size)};\n    }\n    complex_type* output_data;\n    auto          output_size       = FFT::ffts_per_block / implicit_batching * FFT::output_length;\n    auto          output_size_bytes = output_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input_data[i].x) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel_r2c_fp16<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_r2c_fp16<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output_data[i].x.x) << \" \" << __half2float(output_data[i].x.y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_r2c_fp16_functor {\n    void operator()() { return simple_block_fft_r2c_fp16<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_r2c_fp16_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_r2c_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    #if CUFFTDX_EXAMPLE_DETAIL_NVCC_12_2_BUG_WORKAROUND\n    using complex_type = example::value_type_t<FFT>;\n    #else\n    using complex_type = typename FFT::value_type;\n    #endif\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    #if CUFFTDX_EXAMPLE_DETAIL_NVCC_12_2_BUG_WORKAROUND\n    using complex_type = example::value_type_t<FFT>;\n    #else\n    using complex_type = typename FFT::value_type;\n    #endif\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r(typename FFT::value_type* input_data, cufftdx::precision_of_t<FFT>* output_data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2R float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and C2R load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2r>() + Direction<fft_direction::inverse>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* input_data;\n    auto          input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto          input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = complex_type {float(i), -float(i)};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i].x << \" \" << input_data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel_c2r<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_functor {\n    void operator()() { return simple_block_fft_c2r<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r(typename FFT::value_type* input_data, cufftdx::precision_of_t<FFT>* output_data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2R float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and C2R load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2r>() + Direction<fft_direction::inverse>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* input_data;\n    auto          input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto          input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = complex_type {float(i), -float(i)};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i].x << \" \" << input_data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel_c2r<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_functor {\n    void operator()() { return simple_block_fft_c2r<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_c2r.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_r2c(ScalarType* input_data, ComplexType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional real-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point R2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and R2C load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_r2c() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::r2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Allocate managed memory for input/output\n    real_type* input_data;\n    auto       input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto       input_size_bytes = input_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = float(i);\n    }\n    complex_type* output_data;\n    auto          output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto          output_size_bytes = output_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i] << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel_r2c<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_r2c<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i].x << \" \" << output_data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_r2c_functor {\n    void operator()() { return simple_block_fft_r2c<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_r2c_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_r2c(ScalarType* input_data, ComplexType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional real-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point R2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and R2C load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_r2c() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::r2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Allocate managed memory for input/output\n    real_type* input_data;\n    auto       input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto       input_size_bytes = input_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = float(i);\n    }\n    complex_type* output_data;\n    auto          output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto          output_size_bytes = output_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i] << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel_r2c<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_r2c<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i].x << \" \" << output_data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_r2c_functor {\n    void operator()() { return simple_block_fft_r2c<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_r2c_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_r2c.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n\n// To be able to compile on nvcc <= 13.1.0\n#define CUFFTDX_IGNORE_BLOCK_DIM_UNSUPPORTED\n\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    // This io already takes care of predication and correct loading in the threads, See common/block_io.hpp\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally, block dim is set to 32x16x1. This is done to test the block dim functionality.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + BlockDim<32, 16, 1>() + SM<Arch>());\n    #if CUFFTDX_EXAMPLE_DETAIL_NVCC_12_2_BUG_WORKAROUND\n    using complex_type = example::value_type_t<FFT>;\n    #else\n    using complex_type = typename FFT::value_type;\n    #endif\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n\n// To be able to compile on nvcc <= 13.1.0\n#define CUFFTDX_IGNORE_BLOCK_DIM_UNSUPPORTED\n\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    // This io already takes care of predication and correct loading in the threads, See common/block_io.hpp\n    example::io<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally, block dim is set to 32x16x1. This is done to test the block dim functionality.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + BlockDim<32, 16, 1>() + SM<Arch>());\n    #if CUFFTDX_EXAMPLE_DETAIL_NVCC_12_2_BUG_WORKAROUND\n    using complex_type = example::value_type_t<FFT>;\n    #else\n    using complex_type = typename FFT::value_type;\n    #endif\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_block_dim.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r_fp16(ComplexType* input_data, ScalarType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2R half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r_fp16() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2r>() + Direction<fft_direction::inverse>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;          // complex<__half2>\n    using real_type    = typename complex_type::value_type; // __half2\n\n    // Allocate managed memory for input/output\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    complex_type*    input_data;\n    auto             input_size = FFT::ffts_per_block / implicit_batching * FFT::input_length;\n    auto             input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + input_size);\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        input_data[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block / implicit_batching * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input_data[i].x.x) << \" \" << __half2float(input_data[i].x.y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel_c2r_fp16<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r_fp16<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output_data[i].x) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_fp16_functor {\n    void operator()() { return simple_block_fft_c2r_fp16<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_fp16_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT, class ComplexType = typename FFT::value_type, class ScalarType = typename ComplexType::value_type>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r_fp16(ComplexType* input_data, ScalarType* output_data) {\n    using complex_type = ComplexType;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2R half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r_fp16() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2r>() + Direction<fft_direction::inverse>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + real_fft_options() + SM<Arch>());\n    using complex_type = typename FFT::value_type;          // complex<__half2>\n    using real_type    = typename complex_type::value_type; // __half2\n\n    // Allocate managed memory for input/output\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    complex_type*    input_data;\n    auto             input_size = FFT::ffts_per_block / implicit_batching * FFT::input_length;\n    auto             input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + input_size);\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        input_data[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block / implicit_batching * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(input_data[i].x.x) << \" \" << __half2float(input_data[i].x.y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel_c2r_fp16<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r_fp16<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(output_data[i].x) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_fp16_functor {\n    void operator()() { return simple_block_fft_c2r_fp16<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_fp16_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_c2r_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(__half2* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io_fp16<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io_fp16<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Here, we're using __half2 as the type of the input/output data passed to kernel, and later on\n// the device we use special example::io_fp16 struct template to load values from two batches\n// into an array of complex<half2> with ((Real, Real), (Imag, Imag)) layout.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_half2() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + SM<Arch>());\n\n    // Allocate managed memory for input/output\n    __half2* data;\n    auto     size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto     size_bytes = size * sizeof(__half2);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = __half2 {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(data[i].x) << \" \" << __half2float(data[i].y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(data[i].x) << \" \" << __half2float(data[i].y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_half2_functor {\n    void operator()() { return simple_block_fft_half2<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_half2_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(__half2* data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io_fp16<FFT>::load(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io_fp16<FFT>::store(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Here, we're using __half2 as the type of the input/output data passed to kernel, and later on\n// the device we use special example::io_fp16 struct template to load values from two batches\n// into an array of complex<half2> with ((Real, Real), (Imag, Imag)) layout.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_half2() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + SM<Arch>());\n\n    // Allocate managed memory for input/output\n    __half2* data;\n    auto     size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto     size_bytes = size * sizeof(__half2);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = __half2 {float(i), -float(i)};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(data[i].x) << \" \" << __half2float(data[i].y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(data[i].x) << \" \" << __half2float(data[i].y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_half2_functor {\n    void operator()() { return simple_block_fft_half2<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_half2_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_half2.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n#include \"02_simple_fft_block_simple_fft_block_c2r_lto_artifacts/lto_database.hpp.inc\" // cuFFT-dumped LTOIR database header file\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r(typename FFT::value_type* input_data, cufftdx::precision_of_t<FFT>* output_data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2R float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and C2R load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r_lto() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // - Block() operator informs that FFT will be executed on block level. Shared memory is required for co-operation between threads.\n    // - CodeType() operator informs that FFT will attempt to use cuFFT-dumped LTOIRs for computes.\n    using FFT_without_code_type = decltype(Block() + Size<128>() + Type<fft_type::c2r>() +\n                                  Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using FFT = decltype(FFT_without_code_type() + experimental::CodeType<experimental::code_type::ltoir>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Check if LTO implementation is selected\n    static_assert(FFT::code == experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    // Allocate managed memory for input/output\n    complex_type* input_data;\n    auto          input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto          input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = complex_type {float(i), -float(i)};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i].x << \" \" << input_data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel_c2r<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(input_data));\n    CUDA_CHECK_AND_EXIT(cudaFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_lto_functor {\n    void operator()() { return simple_block_fft_c2r_lto<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_lto_functor>();\n}\n\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n#include \"02_simple_fft_block_simple_fft_block_c2r_lto_artifacts/lto_database.hpp.inc\" // cuFFT-dumped LTOIR database header file\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__\n    void block_fft_kernel_c2r(typename FFT::value_type* input_data, cufftdx::precision_of_t<FFT>* output_data) {\n    using complex_type = typename FFT::value_type;\n\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load(input_data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store(thread_data, output_data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-real transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2R float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n// Notice different sizes of input and output buffer, and C2R load and store operations in the kernel.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_c2r_lto() {\n    using namespace cufftdx;\n\n    // R2C and C2R specific properties describing data layout and execution mode for\n    // the requested transform.\n    using real_fft_options = RealFFTOptions<complex_layout::natural, real_mode::normal>;\n\n    // FFT is defined, its: size, type, direction, precision.\n    // - Block() operator informs that FFT will be executed on block level. Shared memory is required for co-operation between threads.\n    // - CodeType() operator informs that FFT will attempt to use cuFFT-dumped LTOIRs for computes.\n    using FFT_without_code_type = decltype(Block() + Size<128>() + Type<fft_type::c2r>() +\n                                  Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + real_fft_options() + SM<Arch>());\n    using FFT = decltype(FFT_without_code_type() + experimental::CodeType<experimental::code_type::ltoir>());\n    using complex_type = typename FFT::value_type;\n    using real_type    = typename complex_type::value_type;\n\n    // Check if LTO implementation is selected\n    static_assert(FFT::code == experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    // Allocate managed memory for input/output\n    complex_type* input_data;\n    auto          input_size       = FFT::ffts_per_block * FFT::input_length;\n    auto          input_size_bytes = input_size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&input_data, input_size_bytes));\n    for (size_t i = 0; i < input_size; i++) {\n        input_data[i] = complex_type {float(i), -float(i)};\n    }\n\n    real_type* output_data;\n    auto       output_size       = FFT::ffts_per_block * FFT::output_length;\n    auto       output_size_bytes = output_size * sizeof(real_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&output_data, output_size_bytes));\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << input_data[i].x << \" \" << input_data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel_c2r<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel_c2r<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(input_data, output_data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << output_data[i] << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(input_data));\n    CUDA_CHECK_AND_EXIT(hipFree(output_data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_c2r_lto_functor {\n    void operator()() { return simple_block_fft_c2r_lto<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_c2r_lto_functor>();\n}\n\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_c2r_lto.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT, bool InputInRRIILayout = false, bool OutputInRRIILayout = false>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load<InputInRRIILayout>(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store<OutputInRRIILayout>(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Here, we're using complex<half2> with ((Real, Imag), (Real, Imag)) layout as the type of the input/output\n// data passed to kernel, and later on the device layout is changed into RRII when values are being loaded.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_complex_half2() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    auto             size              = FFT::ffts_per_block / implicit_batching * cufftdx::size_of<FFT>::value;\n    auto             size_bytes        = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + size);\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        data[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(data[i].x.x) << \" \" << __half2float(data[i].x.y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(data[i].x.x) << \" \" << __half2float(data[i].x.y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_complex_half2_functor {\n    void operator()() { return simple_block_fft_complex_half2<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_complex_half2_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n#include \"../common/fp16_common.hpp\"\n\ntemplate<class FFT, bool InputInRRIILayout = false, bool OutputInRRIILayout = false>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n    // Local array for thread\n    complex_type thread_data[FFT::storage_size];\n\n    // ID of FFT in CUDA block, in range [0; FFT::ffts_per_block)\n    const unsigned int local_fft_id = threadIdx.y;\n    // Load data from global memory to registers\n    example::io<FFT>::load<InputInRRIILayout>(data, thread_data, local_fft_id);\n\n    // Execute FFT\n    extern __shared__ __align__(alignof(float4)) complex_type shared_mem[];\n    FFT().execute(thread_data, shared_mem);\n\n    // Save results\n    example::io<FFT>::store<OutputInRRIILayout>(thread_data, data, local_fft_id);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, and it calculates four 128-point C2C half precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\n//\n// Here, we're using complex<half2> with ((Real, Imag), (Real, Imag)) layout as the type of the input/output\n// data passed to kernel, and later on the device layout is changed into RRII when values are being loaded.\ntemplate<unsigned int Arch>\nvoid simple_block_fft_complex_half2() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<__half>() + ElementsPerThread<8>() + FFTsPerBlock<4>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    // For performance reasons half precision cuFFTDx FFTs has an implicit batching of 2 FFTs. This means that:\n    // * Used complex type is complex<__half2>, and real type is __half2.\n    // * Every thread processes values from two batches simultaneously using __half2 as the base type.\n    // * Number of FFTs per block must be a multiple of 2.\n    // * Complex data is processed in ((Real1, Real2), (Imag1, Imag2)) layout, where (Real1, Imag1) is a value from\n    //   one batch, and (Real2, Imag2) is from a different batch.\n    // * Real data is process using __half2 in (Real1, Real2) layout, where Real1 is a value from one batch, and\n    //   Real2 is from a different batch.\n    constexpr size_t implicit_batching = FFT::implicit_type_batching;\n    auto             size              = FFT::ffts_per_block / implicit_batching * cufftdx::size_of<FFT>::value;\n    auto             size_bytes        = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        float v1 = static_cast<float>(i);\n        float v2 = static_cast<float>(i + size);\n        // Populate input with complex<half2> values in ((Real, Imag), (Real, Imag)) layout\n        data[i] = complex_type {__half2 {v1, -v1}, __half2 {v2, -v2}};\n    }\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << __half2float(data[i].x.x) << \" \" << __half2float(data[i].x.y) << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << __half2float(data[i].x.x) << \" \" << __half2float(data[i].x.y) << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_complex_half2_functor {\n    void operator()() { return simple_block_fft_complex_half2<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_complex_half2_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_fp16.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <iostream>\n#include <vector>\n\n#include <cuda_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    extern __shared__ __align__(alignof(float4)) cufftdx::byte shared_mem[];\n\n    auto this_block_data = data + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    example::io<FFT>::load_to_smem(this_block_data, shared_mem);\n\n    FFT().execute(reinterpret_cast<void*>(shared_mem));\n\n    example::io<FFT>::store_from_smem(shared_mem, this_block_data);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    auto shared_memory_size = std::max<unsigned int>(FFT::shared_memory_size, size_bytes);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n#include <iostream>\n#include <vector>\n\n#include <hip/hip_runtime_api.h>\n#include <cufftdx.hpp>\n\n#include \"../common/block_io.hpp\"\n#include \"../common/common.hpp\"\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block) __global__ void block_fft_kernel(typename FFT::value_type* data) {\n    using complex_type = typename FFT::value_type;\n\n    extern __shared__ __align__(alignof(float4)) cufftdx::byte shared_mem[];\n\n    auto this_block_data = data + cufftdx::size_of<FFT>::value * (FFT::ffts_per_block / FFT::implicit_type_batching) * blockIdx.x;\n\n    example::io<FFT>::load_to_smem(this_block_data, shared_mem);\n\n    FFT().execute(reinterpret_cast<void*>(shared_mem));\n\n    example::io<FFT>::store_from_smem(shared_mem, this_block_data);\n}\n\n// In this example a one-dimensional complex-to-complex transform is performed by a CUDA block.\n//\n// One block is run, it calculates two 128-point C2C float precision FFTs.\n// Data is generated on host, copied to device buffer, and then results are copied back to host.\ntemplate<unsigned int Arch>\nvoid simple_block_fft() {\n    using namespace cufftdx;\n\n    // FFT is defined, its: size, type, direction, precision. Block() operator informs that FFT\n    // will be executed on block level. Shared memory is required for co-operation between threads.\n    // Additionally,\n    using FFT          = decltype(Block() + Size<128>() + Type<fft_type::c2c>() + Direction<fft_direction::forward>() +\n                         Precision<float>() + ElementsPerThread<8>() + FFTsPerBlock<2>() + SM<Arch>());\n    using complex_type = typename FFT::value_type;\n\n    // Allocate managed memory for input/output\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    // Shared memory must fit input data and must be big enough to run FFT\n    auto shared_memory_size = std::max<unsigned int>(FFT::shared_memory_size, size_bytes);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    // Increase max shared memory if needed\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        shared_memory_size));\n\n    // Invokes kernel with FFT::block_dim threads in CUDA block\n    block_fft_kernel<FFT><<<1, FFT::block_dim, shared_memory_size>>>(data);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct simple_block_fft_functor {\n    void operator()() { return simple_block_fft<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_block_fft_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/02_simple_fft_block/simple_fft_block_shared.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n// Include cuFFT-dumped LTOIR database header file\n#include \"lto_database.hpp.inc\"\n\n// This example is a copy of the 02_simple_fft_block/00_simple_fft_block.cu example and\n// has been modified to work with cufft lto. It contains only 2 modifications, the inclusion\n// of a auto-generated lto header file, lto_database.hpp.inc, and the addition of the\n// experimental ltoir CodeType. to the FFT type. The bulk of the modifications made to the\n// 02_simple_fft_block example were made in the build scripts which construct and link the\n// external lto database as well as create the database header file.\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    complex_type thread_data[FFT::storage_size];\n\n    const unsigned int local_fft_id = threadIdx.y;\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\ntemplate<unsigned int Arch>\nvoid introduction_lto_helper() {\n    using FFT_without_code_type = decltype(cufftdx::Block() +\n                                           cufftdx::Size<128>() +\n                                           cufftdx::Type<cufftdx::fft_type::c2c>() +\n                                           cufftdx::Direction<cufftdx::fft_direction::forward>() +\n                                           cufftdx::Precision<float>() +\n                                           cufftdx::ElementsPerThread<8>() +\n                                           cufftdx::FFTsPerBlock<2>() +\n                                           cufftdx::SM<Arch>());\n    // Add experimental ltoir code_type to FFT type\n    using FFT = decltype(FFT_without_code_type() + cufftdx::experimental::CodeType<cufftdx::experimental::code_type::ltoir>());\n\n    // Validate that the selected fft is in fact an ltoir kernel\n    static_assert(FFT::code == cufftdx::experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    using complex_type = typename FFT::value_type;\n\n    cudaStream_t stream;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreate(&stream));\n\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(cudaMallocManaged(&data, size_bytes));\n\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    cudaError_t error_code = cudaSuccess;\n    auto workspace = cufftdx::make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(\n        block_fft_kernel<FFT>,\n        cudaFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(data));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_lto_helper_functor {\n    void operator()() { return introduction_lto_helper<Arch>(); }\n};\n\nint main() {\n    return example::sm_runner<introduction_lto_helper_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cufftdx.hpp>\n\n#include \"../common/common.hpp\"\n\n// Include cuFFT-dumped LTOIR database header file\n#include \"lto_database.hpp.inc\"\n\n// This example is a copy of the 02_simple_fft_block/00_simple_fft_block.cu example and\n// has been modified to work with cufft lto. It contains only 2 modifications, the inclusion\n// of a auto-generated lto header file, lto_database.hpp.inc, and the addition of the\n// experimental ltoir CodeType. to the FFT type. The bulk of the modifications made to the\n// 02_simple_fft_block example were made in the build scripts which construct and link the\n// external lto database as well as create the database header file.\n\ntemplate<class FFT>\n__launch_bounds__(FFT::max_threads_per_block)\n    __global__ void block_fft_kernel(typename FFT::value_type* data, typename FFT::workspace_type workspace) {\n    using complex_type = typename FFT::value_type;\n\n    complex_type thread_data[FFT::storage_size];\n\n    const unsigned int local_fft_id = threadIdx.y;\n    const unsigned int global_fft_id = (blockIdx.x * FFT::ffts_per_block) + local_fft_id;\n\n    const unsigned int     offset = cufftdx::size_of<FFT>::value * global_fft_id;\n    constexpr unsigned int stride = FFT::stride;\n    unsigned int           index  = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            thread_data[i] = data[index];\n            index += stride;\n        }\n    }\n\n    extern __shared__ __align__(alignof(float4)) complex_type shared_memory[];\n    FFT().execute(thread_data, shared_memory, workspace);\n\n    index = offset + threadIdx.x;\n    for (unsigned int i = 0; i < FFT::elements_per_thread; i++) {\n        if ((i * stride + threadIdx.x) < cufftdx::size_of<FFT>::value) {\n            data[index] = thread_data[i];\n            index += stride;\n        }\n    }\n}\n\ntemplate<unsigned int Arch>\nvoid introduction_lto_helper() {\n    using FFT_without_code_type = decltype(cufftdx::Block() +\n                                           cufftdx::Size<128>() +\n                                           cufftdx::Type<cufftdx::fft_type::c2c>() +\n                                           cufftdx::Direction<cufftdx::fft_direction::forward>() +\n                                           cufftdx::Precision<float>() +\n                                           cufftdx::ElementsPerThread<8>() +\n                                           cufftdx::FFTsPerBlock<2>() +\n                                           cufftdx::SM<Arch>());\n    // Add experimental ltoir code_type to FFT type\n    using FFT = decltype(FFT_without_code_type() + cufftdx::experimental::CodeType<cufftdx::experimental::code_type::ltoir>());\n\n    // Validate that the selected fft is in fact an ltoir kernel\n    static_assert(FFT::code == cufftdx::experimental::code_type::ltoir, \"Selected implementation code type is not LTO-IR\");\n\n    using complex_type = typename FFT::value_type;\n\n    hipStream_t stream;\n    CUDA_CHECK_AND_EXIT(hipStreamCreate(&stream));\n\n    complex_type* data;\n    auto          size       = FFT::ffts_per_block * cufftdx::size_of<FFT>::value;\n    auto          size_bytes = size * sizeof(complex_type);\n    CUDA_CHECK_AND_EXIT(hipMallocManaged(&data, size_bytes));\n\n    for (size_t i = 0; i < size; i++) {\n        data[i] = complex_type {float(i), -float(i)};\n    }\n\n    hipError_t error_code = hipSuccess;\n    auto workspace = cufftdx::make_workspace<FFT>(error_code, stream);\n    CUDA_CHECK_AND_EXIT(error_code);\n\n    std::cout << \"input [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::input_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(block_fft_kernel<FFT>),\n        hipFuncAttributeMaxDynamicSharedMemorySize,\n        FFT::shared_memory_size));\n\n    block_fft_kernel<FFT><<<1, FFT::block_dim, FFT::shared_memory_size, stream>>>(data, workspace);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::cout << \"output [1st FFT]:\\n\";\n    for (size_t i = 0; i < FFT::output_length; i++) {\n        std::cout << data[i].x << \" \" << data[i].y << std::endl;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(data));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n    std::cout << \"Success\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct introduction_lto_helper_functor {\n    void operator()() { return introduction_lto_helper<Arch>(); }\n};\n\nint main() {\n    return example::sm_runner<introduction_lto_helper_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuFFTDx/10_cufft_device_api_example/cufft_device_api_example.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing Cholesky factorization of the batched symmetric, positive-definite matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it couble be more efficient to use Thread execution instead of Block execution\n\ntemplate<class POSV_thread>\n__global__ void posv_kernel(\n        typename POSV_thread::a_data_type* A, typename POSV_thread::b_data_type* B, typename POSV_thread::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m                = POSV_thread::m_size;\n    constexpr auto nrhs             = POSV_thread::k_size;\n    const auto     one_batch_size_a = m * m;\n    const auto     one_batch_size_b = m * nrhs;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    POSV_thread().execute(this_A, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint posv_batched_thread() {\n\n    using namespace cusolverdx;\n\n    using POSV = decltype(Size<8, 8, 3>() + Precision<double>() + Type<type::real>() + Function<function::posv>() + FillMode<fill_mode::lower>() +\n                          Arrangement<col_major, row_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename POSV::a_data_type;\n    using cuda_data_type = typename POSV::a_cuda_data_type;\n\n    constexpr auto m    = POSV::m_size;\n    constexpr auto n    = POSV::n_size;\n    constexpr auto nrhs = POSV::k_size;\n    static_assert(m == n, \"posv is for Hermitian positive-definite matrix matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POSV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<POSV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = m;\n    const auto ldb = is_col_maj_b ? m : nrhs;\n\n    const auto batches          = 100;\n    const auto one_batch_size_A = m * m;\n    const auto one_batch_size_B = m * nrhs;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POSV> == col_major, m, m, A.data(), m, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<POSV> == col_major, m, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = posv_kernel<POSV>;\n    const int  nthreads = 128;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched and portsBatched\n    //=======================================================\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, true>(\n            A, B, info.data(), m, nrhs, batches, (fill_mode_of_v<POSV> == fill_mode::lower), is_col_maj_a, is_col_maj_b, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POSV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED POSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrSBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrSBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct posv_batched_thread_functor {\n    int operator()() { return posv_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<posv_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing Cholesky factorization of the batched symmetric, positive-definite matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it couble be more efficient to use Thread execution instead of Block execution\n\ntemplate<class POSV_thread>\n__global__ void posv_kernel(\n        typename POSV_thread::a_data_type* A, typename POSV_thread::b_data_type* B, typename POSV_thread::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m                = POSV_thread::m_size;\n    constexpr auto nrhs             = POSV_thread::k_size;\n    const auto     one_batch_size_a = m * m;\n    const auto     one_batch_size_b = m * nrhs;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    POSV_thread().execute(this_A, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint posv_batched_thread() {\n\n    using namespace cusolverdx;\n\n    using POSV = decltype(Size<8, 8, 3>() + Precision<double>() + Type<type::real>() + Function<function::posv>() + FillMode<fill_mode::lower>() +\n                          Arrangement<col_major, row_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename POSV::a_data_type;\n    using cuda_data_type = typename POSV::a_cuda_data_type;\n\n    constexpr auto m    = POSV::m_size;\n    constexpr auto n    = POSV::n_size;\n    constexpr auto nrhs = POSV::k_size;\n    static_assert(m == n, \"posv is for Hermitian positive-definite matrix matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POSV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<POSV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = m;\n    const auto ldb = is_col_maj_b ? m : nrhs;\n\n    const auto batches          = 100;\n    const auto one_batch_size_A = m * m;\n    const auto one_batch_size_B = m * nrhs;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POSV> == col_major, m, m, A.data(), m, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<POSV> == col_major, m, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = posv_kernel<POSV>;\n    const int  nthreads = 128;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched and portsBatched\n    //=======================================================\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, true>(\n            A, B, info.data(), m, nrhs, batches, (fill_mode_of_v<POSV> == fill_mode::lower), is_col_maj_a, is_col_maj_b, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POSV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED POSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrSBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrSBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct posv_batched_thread_functor {\n    int operator()() { return posv_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<posv_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/posv_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates using cuSolverDx API to perform LU factorization with pivoting on a single-batch, general MxN matrix.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, typename Solver::status_type* info, int* d_ipiv) {\n\n    constexpr auto m        = Solver::m_size;\n    constexpr auto n        = Solver::n_size;\n    constexpr auto lda_smem = Solver::lda;\n\n    constexpr auto lda_gmem              = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? m : n;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, ipivs] = cusolverdx::shared_memory::slice<DataType, int>(shared_mem, alignof(DataType), one_batch_size_a_smem,\n            alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, ipivs, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n); i += Solver::max_threads_per_block) {\n        d_ipiv[i] = ipivs[i];\n    }\n}\n\ntemplate<int Arch>\nint getrf_partial_pivot_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<48, 32>() + Precision<float>() + Type<type::complex>() + Function<getrf_partial_pivot>() +\n                            Arrangement<arrangement::col_major>() + SM<Arch>() + Block() + BlockDim<33, 1, 1>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    constexpr bool is_col_maj = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr auto lda        = is_col_maj ? m : n;\n    constexpr auto input_size = is_col_maj ? lda * n : m * lda;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_matrix<data_type>(is_col_maj, m, n, A.data(), lda, false, false, 2, 4); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size);\n    std::vector<int>       ipiv(min(m, n), 0);\n    std::vector<int64_t>   ipiv_ref(min(m, n), 0);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n    int*                   d_ipiv = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, d_info, d_ipiv);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(&info, d_info, sizeof(int), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx getrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ipiv));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_lu<data_type, cuda_data_type>(A, dummy_B, &info, m, n, 1, 1 /* padded_batches */, true /* is_pivot */, is_col_maj,\n            true /* is_col_major_b */, false /* is_trans_a */, ipiv_ref.data());\n\n\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"GETRF partial pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < min(m, n); ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct getrf_partial_pivot_block_functor {\n    int operator()() { return getrf_partial_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<getrf_partial_pivot_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates using cuSolverDx API to perform LU factorization with pivoting on a single-batch, general MxN matrix.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, typename Solver::status_type* info, int* d_ipiv) {\n\n    constexpr auto m        = Solver::m_size;\n    constexpr auto n        = Solver::n_size;\n    constexpr auto lda_smem = Solver::lda;\n\n    constexpr auto lda_gmem              = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? m : n;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, ipivs] = cusolverdx::shared_memory::slice<DataType, int>(shared_mem, alignof(DataType), one_batch_size_a_smem,\n            alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, ipivs, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n); i += Solver::max_threads_per_block) {\n        d_ipiv[i] = ipivs[i];\n    }\n}\n\ntemplate<int Arch>\nint getrf_partial_pivot_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<48, 32>() + Precision<float>() + Type<type::complex>() + Function<getrf_partial_pivot>() +\n                            Arrangement<arrangement::col_major>() + SM<Arch>() + Block() + BlockDim<33, 1, 1>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    constexpr bool is_col_maj = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr auto lda        = is_col_maj ? m : n;\n    constexpr auto input_size = is_col_maj ? lda * n : m * lda;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_matrix<data_type>(is_col_maj, m, n, A.data(), lda, false, false, 2, 4); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size);\n    std::vector<int>       ipiv(min(m, n), 0);\n    std::vector<int64_t>   ipiv_ref(min(m, n), 0);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n    int*                   d_ipiv = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, d_info, d_ipiv);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(&info, d_info, sizeof(int), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx getrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ipiv));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_lu<data_type, cuda_data_type>(A, dummy_B, &info, m, n, 1, 1 /* padded_batches */, true /* is_pivot */, is_col_maj,\n            true /* is_col_major_b */, false /* is_trans_a */, ipiv_ref.data());\n\n\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"GETRF partial pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < min(m, n); ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct getrf_partial_pivot_block_functor {\n    int operator()() { return getrf_partial_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<getrf_partial_pivot_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/getrf_partial_pivot_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to perform Cholesky factorization for a batched symmetric, positive-definite matrices. Each batch has small problem size, so it is more efficient to use Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class POTRF>\n__global__ void potrf_kernel(typename POTRF::a_data_type* A, typename POTRF::status_type* info, const unsigned int batches) {\n\n    constexpr auto m                = POTRF::m_size;\n    const auto     one_batch_size_a = POTRF::lda * m;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a) * batch_idx;\n\n    POTRF().execute(Ag, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint potrf_batched_thread() {\n\n    using namespace cusolverdx;\n\n    using POTRF = decltype(Size<8>() + Precision<double>() + Type<type::real>() + Function<function::potrf>() + FillMode<fill_mode::lower>() +\n                           Arrangement<col_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename POTRF::a_data_type;\n    using cuda_data_type = typename POTRF::a_cuda_data_type;\n\n    constexpr auto m = POTRF::m_size;\n    constexpr auto n = POTRF::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = POTRF::lda;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POTRF> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto lda              = m;\n    const auto     batches          = 250;\n    const auto     one_batch_size_A = lda * n; // no padding for global memory\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POTRF> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * batches));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = potrf_kernel<POTRF>;\n    const int  nthreads = 128;\n    kernel<<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * batches, cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched\n    //=======================================================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, false /* do_solver */>(A, dummy_B, info.data(), m, 1, batches,\n            (fill_mode_of_v<POTRF> == fill_mode::lower), /* is_lower? */\n            is_col_maj_a, true, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POTRF: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrfBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrfBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct potrf_batched_thread_functor {\n    int operator()() { return potrf_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to perform Cholesky factorization for a batched symmetric, positive-definite matrices. Each batch has small problem size, so it is more efficient to use Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class POTRF>\n__global__ void potrf_kernel(typename POTRF::a_data_type* A, typename POTRF::status_type* info, const unsigned int batches) {\n\n    constexpr auto m                = POTRF::m_size;\n    const auto     one_batch_size_a = POTRF::lda * m;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a) * batch_idx;\n\n    POTRF().execute(Ag, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint potrf_batched_thread() {\n\n    using namespace cusolverdx;\n\n    using POTRF = decltype(Size<8>() + Precision<double>() + Type<type::real>() + Function<function::potrf>() + FillMode<fill_mode::lower>() +\n                           Arrangement<col_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename POTRF::a_data_type;\n    using cuda_data_type = typename POTRF::a_cuda_data_type;\n\n    constexpr auto m = POTRF::m_size;\n    constexpr auto n = POTRF::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = POTRF::lda;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POTRF> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto lda              = m;\n    const auto     batches          = 250;\n    const auto     one_batch_size_A = lda * n; // no padding for global memory\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POTRF> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * batches));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = potrf_kernel<POTRF>;\n    const int  nthreads = 128;\n    kernel<<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * batches, hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched\n    //=======================================================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, false /* do_solver */>(A, dummy_B, info.data(), m, 1, batches,\n            (fill_mode_of_v<POTRF> == fill_mode::lower), /* is_lower? */\n            is_col_maj_a, true, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POTRF: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrfBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrfBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct potrf_batched_thread_functor {\n    int operator()() { return potrf_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/potrf_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example shows how to perform Cholesky factorization for a Hermitian positive-definite matrix\n// using cuSolverDx, and compare the result factors with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void potrf_kernel(DataType* A, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    constexpr auto lda_smem = Solver::lda;\n    constexpr auto lda_gmem = Solver::m_size;\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n}\n\ntemplate<int Arch>\nint potrf_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<32, 32>() + Precision<double>() + Type<type::complex>() + Function<potrf>() + LeadingDimension<33>() + SM<Arch>() +\n                            BlockDim<256>() + FillMode<fill_mode::upper>() + Block());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = Solver::lda;\n\n    constexpr auto lda        = m; // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * n; // input global memory size for A\n\n    std::cout << \"Use compile-time leading dimension LDA for shared memory = \" << lda_smem << std::endl;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix_col_major<data_type>(m, n, A.data(), lda, false, -2, 2); // input A is not symmetric\n\n    // To get around cuSolver potrsBatched bug for CUDA <= 12.2, set the diagonal elements of input matrix A to be real\n#if (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ <= 2)\n    if constexpr (common::is_complex<data_type>()) {\n        for (unsigned int row = 0; row < m; row++) {\n            A[row + row * lda].y = 0;\n        }\n    }\n#endif\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(potrf_kernel<Solver>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n    //Invokes kernel\n    potrf_kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, d_info);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(&info, d_info, sizeof(int), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx potrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    // Use dumb B as only factorization is performed\n    std::vector<data_type> B;\n    common::reference_cusolver_cholesky<data_type, cuda_data_type>(A, B, &info, m, 1, 1, (fill_mode_of_v<Solver> == fill_mode::lower));\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"Solver: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct potrf_block_functor {\n    int operator()() { return potrf_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example shows how to perform Cholesky factorization for a Hermitian positive-definite matrix\n// using cuSolverDx, and compare the result factors with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void potrf_kernel(DataType* A, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    constexpr auto lda_smem = Solver::lda;\n    constexpr auto lda_gmem = Solver::m_size;\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n}\n\ntemplate<int Arch>\nint potrf_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<32, 32>() + Precision<double>() + Type<type::complex>() + Function<potrf>() + LeadingDimension<33>() + SM<Arch>() +\n                            BlockDim<256>() + FillMode<fill_mode::upper>() + Block());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = Solver::lda;\n\n    constexpr auto lda        = m; // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * n; // input global memory size for A\n\n    std::cout << \"Use compile-time leading dimension LDA for shared memory = \" << lda_smem << std::endl;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix_col_major<data_type>(m, n, A.data(), lda, false, -2, 2); // input A is not symmetric\n\n    // To get around cuSolver potrsBatched bug for CUDA <= 12.2, set the diagonal elements of input matrix A to be real\n#if (__CUDACC_VER_MAJOR__ == 12 && __CUDACC_VER_MINOR__ <= 2)\n    if constexpr (common::is_complex<data_type>()) {\n        for (unsigned int row = 0; row < m; row++) {\n            A[row + row * lda].y = 0;\n        }\n    }\n#endif\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(potrf_kernel<Solver>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n    //Invokes kernel\n    potrf_kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, d_info);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(&info, d_info, sizeof(int), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx potrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    // Use dumb B as only factorization is performed\n    std::vector<data_type> B;\n    common::reference_cusolver_cholesky<data_type, cuda_data_type>(A, B, &info, m, 1, 1, (fill_mode_of_v<Solver> == fill_mode::lower));\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"Solver: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct potrf_block_functor {\n    int operator()() { return potrf_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/potrf_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// In some workflows, the matrix A may have already in the shared memory, padded to avoid bank conflict, and being updated by other operations.\n// This example shows how to use runtime leading dimensions in cuSolverDx API, compute Cholesky factorization for a Hermitian\n// positive-definite matrix, and compare the result factors with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void potrf_kernel(DataType* A, const unsigned int lda_smem, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    constexpr auto lda_gmem = Solver::m_size;\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n}\n\ntemplate<int Arch>\nint potrf_runtime_ld_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<32, 32>() + Precision<double>() + Type<type::real>() + Function<potrf>() + FillMode<lower>() + Block() + SM<Arch>() +\n                            BlockDim<256>()); // column major and lower fill is the default\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n\n    constexpr auto lda        = m; // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * n; // input global memory size for A\n\n    // Use runtime LDA for SMEM\n    unsigned int lda_smem = 34;\n\n    std::cout << \"Use runtime leading dimension LDA for shared memory = \" << lda_smem << std::endl;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix_col_major<data_type>(m, n, A.data(), lda, false, -2, 2);\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    const unsigned int sm_size = Solver::get_shared_memory_size(lda_smem);\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(potrf_kernel<Solver>, cudaFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n    //Invokes kernel\n    potrf_kernel<Solver><<<1, Solver::block_dim, sm_size, stream>>>(d_A, lda_smem, d_info);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(&info, d_info, sizeof(int), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx potrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> B; // dummy B  as only factorization is performed\n    common::reference_cusolver_cholesky<data_type, cuda_data_type>(A, B, &info, m, 1, 1); // nrhs=1, batches=1\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"POTRF: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct potrf_runtime_ld_block_functor {\n    int operator()() { return potrf_runtime_ld_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_runtime_ld_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// In some workflows, the matrix A may have already in the shared memory, padded to avoid bank conflict, and being updated by other operations.\n// This example shows how to use runtime leading dimensions in cuSolverDx API, compute Cholesky factorization for a Hermitian\n// positive-definite matrix, and compare the result factors with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void potrf_kernel(DataType* A, const unsigned int lda_smem, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    constexpr auto lda_gmem = Solver::m_size;\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda_smem, A, lda_gmem);\n}\n\ntemplate<int Arch>\nint potrf_runtime_ld_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<32, 32>() + Precision<double>() + Type<type::real>() + Function<potrf>() + FillMode<lower>() + Block() + SM<Arch>() +\n                            BlockDim<256>()); // column major and lower fill is the default\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"potrf is for Hermitian positive-definite matrix matrix only\");\n\n    constexpr auto lda        = m; // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * n; // input global memory size for A\n\n    // Use runtime LDA for SMEM\n    unsigned int lda_smem = 34;\n\n    std::cout << \"Use runtime leading dimension LDA for shared memory = \" << lda_smem << std::endl;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix_col_major<data_type>(m, n, A.data(), lda, false, -2, 2);\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    const unsigned int sm_size = Solver::get_shared_memory_size(lda_smem);\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(potrf_kernel<Solver>), hipFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n    //Invokes kernel\n    potrf_kernel<Solver><<<1, Solver::block_dim, sm_size, stream>>>(d_A, lda_smem, d_info);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(&info, d_info, sizeof(int), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx potrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        exit(1);\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> B; // dummy B  as only factorization is performed\n    common::reference_cusolver_cholesky<data_type, cuda_data_type>(A, B, &info, m, 1, 1); // nrhs=1, batches=1\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"POTRF: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct potrf_runtime_ld_block_functor {\n    int operator()() { return potrf_runtime_ld_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<potrf_runtime_ld_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/potrf_runtime_ld_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gtsv.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear tridiagonal systems with multiple right hand side after performing LU\n// factorization without pivoting.\n// As the problem size is small, it could be more efficient to use Thread execution instead of Block execution\n// The results are compared with the reference values obtained with cuSparse host API, cuSparse<t>gtsv2StridedBatch().\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(const DataType* dl, const DataType* d, const DataType* du, DataType* B, typename Solver::status_type* info, const unsigned int batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n    static_assert(m == n, \"m and n must be the same\");\n\n    const auto one_batch_size_b_gmem = m * k;\n\n    auto dl_g = dl + (m - 1) * batch_idx;\n    auto d_g  = d + m * batch_idx;\n    auto du_g = du + (m - 1) * batch_idx;\n    auto B_g  = B + one_batch_size_b_gmem * batch_idx;\n\n    Solver().execute(dl_g, d_g, du_g, B_g, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gtsv_batched_wo_pivot_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 8, 10>() + Precision<float>() + Type<type::complex>() + Function<gtsv_no_pivot>() +\n                            Arrangement<arrangement::col_major, row_major>() + // Arrangement of A is ignored\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? m : k;\n    const auto     input_size_b = m * k;\n\n    const auto batches = 400;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> dl((m - 1) * batches);\n    std::vector<data_type> d((m)*batches);\n    std::vector<data_type> du((m - 1) * batches);\n\n    // Fill up a general diagonal dominant matrix A and copy to dl, d, du\n    std::vector<data_type> A(m * m * batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(true /* is_col_major */, m, m, A.data(), m, false /*symmetric*/, -2, 4, batches);\n    for (int b = 0; b < batches; b++) {\n        for (int i = 0; i < m; i++) {\n            d[b * m + i] = A[b * m * m + i * m + i];\n            if (i < m - 1) {\n                du[b * (m - 1) + i] = A[b * m * m + i * m + i + 1];\n            }\n            if (i > 0) {\n                dl[b * (m - 1) + i - 1] = A[b * m * m + i * m + i - 1];\n            }\n        }\n    }\n    A.clear();\n\n    std::vector<data_type> B(input_size_b * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, m, k, B.data(), ldb, false, false, -2, 1, batches);\n    std::vector<data_type> X(input_size_b * batches);\n\n    // Uncomment below to print dl, d, du. B\n    // printf(\"dl = \\n\");\n    // common::print_matrix<data_type, m - 1, 1, m, true>(dl.data(), batches);\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // printf(\"du = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m, true>(du.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_dl   = nullptr; /* device copy of dl */\n    data_type*       d_d    = nullptr; /* device copy of d */\n    data_type*       d_du   = nullptr; /* device copy of du */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_dl), sizeof(data_type) * dl.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_du), sizeof(data_type) * du.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_dl, dl.data(), sizeof(data_type) * dl.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d.data(), sizeof(data_type) * d.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_du, du.data(), sizeof(data_type) * du.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const int nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_dl, d_d, d_du, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * batches, cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"X after cusolverdx gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(X.data(), batches);\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_gtsv<data_type, cuda_data_type, false /* is_perf */>(\n            dl, d, du, B, m, k, batches, (arrangement_of_v_b<Solver> == arrangement::col_major), batches);\n\n    // Uncomment below to print the reference results after cuSolver execute\n    // printf(\"X after cusolver reference gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_dl));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_du));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GTSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver gtsv Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gtsv_batched_wo_pivot_thread_functor {\n    int operator()() { return gtsv_batched_wo_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gtsv_batched_wo_pivot_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gtsv.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear tridiagonal systems with multiple right hand side after performing LU\n// factorization without pivoting.\n// As the problem size is small, it could be more efficient to use Thread execution instead of Block execution\n// The results are compared with the reference values obtained with cuSparse host API, cuSparse<t>gtsv2StridedBatch().\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(const DataType* dl, const DataType* d, const DataType* du, DataType* B, typename Solver::status_type* info, const unsigned int batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n    static_assert(m == n, \"m and n must be the same\");\n\n    const auto one_batch_size_b_gmem = m * k;\n\n    auto dl_g = dl + (m - 1) * batch_idx;\n    auto d_g  = d + m * batch_idx;\n    auto du_g = du + (m - 1) * batch_idx;\n    auto B_g  = B + one_batch_size_b_gmem * batch_idx;\n\n    Solver().execute(dl_g, d_g, du_g, B_g, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gtsv_batched_wo_pivot_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 8, 10>() + Precision<float>() + Type<type::complex>() + Function<gtsv_no_pivot>() +\n                            Arrangement<arrangement::col_major, row_major>() + // Arrangement of A is ignored\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? m : k;\n    const auto     input_size_b = m * k;\n\n    const auto batches = 400;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> dl((m - 1) * batches);\n    std::vector<data_type> d((m)*batches);\n    std::vector<data_type> du((m - 1) * batches);\n\n    // Fill up a general diagonal dominant matrix A and copy to dl, d, du\n    std::vector<data_type> A(m * m * batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(true /* is_col_major */, m, m, A.data(), m, false /*symmetric*/, -2, 4, batches);\n    for (int b = 0; b < batches; b++) {\n        for (int i = 0; i < m; i++) {\n            d[b * m + i] = A[b * m * m + i * m + i];\n            if (i < m - 1) {\n                du[b * (m - 1) + i] = A[b * m * m + i * m + i + 1];\n            }\n            if (i > 0) {\n                dl[b * (m - 1) + i - 1] = A[b * m * m + i * m + i - 1];\n            }\n        }\n    }\n    A.clear();\n\n    std::vector<data_type> B(input_size_b * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, m, k, B.data(), ldb, false, false, -2, 1, batches);\n    std::vector<data_type> X(input_size_b * batches);\n\n    // Uncomment below to print dl, d, du. B\n    // printf(\"dl = \\n\");\n    // common::print_matrix<data_type, m - 1, 1, m, true>(dl.data(), batches);\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // printf(\"du = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m, true>(du.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_dl   = nullptr; /* device copy of dl */\n    data_type*       d_d    = nullptr; /* device copy of d */\n    data_type*       d_du   = nullptr; /* device copy of du */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_dl), sizeof(data_type) * dl.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_du), sizeof(data_type) * du.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_dl, dl.data(), sizeof(data_type) * dl.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d.data(), sizeof(data_type) * d.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_du, du.data(), sizeof(data_type) * du.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const int nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_dl, d_d, d_du, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * batches, hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"X after cusolverdx gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(X.data(), batches);\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_gtsv<data_type, cuda_data_type, false /* is_perf */>(\n            dl, d, du, B, m, k, batches, (arrangement_of_v_b<Solver> == arrangement::col_major), batches);\n\n    // Uncomment below to print the reference results after cuSolver execute\n    // printf(\"X after cusolver reference gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_dl));\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_du));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GTSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver gtsv Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gtsv_batched_wo_pivot_thread_functor {\n    int operator()() { return gtsv_batched_wo_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gtsv_batched_wo_pivot_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gtsv_batched_wo_pivot_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear systems with multiple right hand side after performing LU\n// factorization with pivoting of the batched general matrix A.  The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, int* ipiv, DataType* B, const int ldb_gmem, typename Solver::status_type* info, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"gesv kernels only allow square matrices\");\n    constexpr auto k = Solver::k_size;\n\n    const auto     one_batch_size_a_gmem = lda_gmem * m;\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = lda_smem * m;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, ipivs] = cusolverdx::shared_memory::slice<DataType, DataType, int>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType), one_batch_size_b_smem * BatchesPerBlock,\n            alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg    = B + size_t(one_batch_size_b_gmem) * batch_idx;\n    auto ipivg = ipiv + batch_idx * n;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, ipivs, Bs, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n\n    // store ipiv from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < n * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        ipivg[i] = ipivs[i];\n    }\n}\n\ntemplate<int Arch>\nint gesv_batched_partial_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<18, 18, 1>() + Precision<float>() + Type<type::complex>() + Function<gesv_partial_pivot>() +\n                          Arrangement<arrangement::col_major, arrangement::col_major>() + TransposeMode<non_trans>() + LeadingDimension<18, 20>() + SM<Arch>() +\n                          Block() + BlockDim<64>());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    const auto     ldb          = is_col_maj_b ? n : k;\n    constexpr auto input_size_a = m * n;\n    constexpr auto input_size_b = n * k;\n\n    const auto batches        = 3;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<int>       ipiv(min(m, n) * padded_batches, 0);\n    std::vector<int64_t>   ipiv_ref(min(m, n) * padded_batches, 0);\n    std::vector<int>       info(padded_batches, 1);\n    data_type*             d_A    = nullptr; /* device copy of A */\n    data_type*             d_b    = nullptr; /* device copy of b */\n    int*                   d_info = nullptr; /* error info */\n    int*                   d_ipiv = nullptr;\n\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_b), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_b, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_ipiv, d_b, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_b, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_b));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ipiv));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, k, padded_batches, true /* is_pivot */, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans), ipiv_ref.data(),\n            batches); // is_pivot, col_maj_a, col_maj_b, do_solver, ipiv\n\n\n    // check A and X\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GESV partial pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    std::cout << \"GESV partial pivoting: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < min(m, n) * batches; ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results, ipiv, A and B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_partial_pivot_block_functor {\n    int operator()() { return gesv_batched_partial_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_partial_pivot_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear systems with multiple right hand side after performing LU\n// factorization with pivoting of the batched general matrix A.  The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, int* ipiv, DataType* B, const int ldb_gmem, typename Solver::status_type* info, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m == n, \"gesv kernels only allow square matrices\");\n    constexpr auto k = Solver::k_size;\n\n    const auto     one_batch_size_a_gmem = lda_gmem * m;\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = lda_smem * m;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, ipivs] = cusolverdx::shared_memory::slice<DataType, DataType, int>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType), one_batch_size_b_smem * BatchesPerBlock,\n            alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg    = B + size_t(one_batch_size_b_gmem) * batch_idx;\n    auto ipivg = ipiv + batch_idx * n;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, ipivs, Bs, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n\n    // store ipiv from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < n * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        ipivg[i] = ipivs[i];\n    }\n}\n\ntemplate<int Arch>\nint gesv_batched_partial_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<18, 18, 1>() + Precision<float>() + Type<type::complex>() + Function<gesv_partial_pivot>() +\n                          Arrangement<arrangement::col_major, arrangement::col_major>() + TransposeMode<non_trans>() + LeadingDimension<18, 20>() + SM<Arch>() +\n                          Block() + BlockDim<64>());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    const auto     ldb          = is_col_maj_b ? n : k;\n    constexpr auto input_size_a = m * n;\n    constexpr auto input_size_b = n * k;\n\n    const auto batches        = 3;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<int>       ipiv(min(m, n) * padded_batches, 0);\n    std::vector<int64_t>   ipiv_ref(min(m, n) * padded_batches, 0);\n    std::vector<int>       info(padded_batches, 1);\n    data_type*             d_A    = nullptr; /* device copy of A */\n    data_type*             d_b    = nullptr; /* device copy of b */\n    int*                   d_info = nullptr; /* error info */\n    int*                   d_ipiv = nullptr;\n\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_b), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_b, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_ipiv, d_b, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_b, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_b));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ipiv));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, k, padded_batches, true /* is_pivot */, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans), ipiv_ref.data(),\n            batches); // is_pivot, col_maj_a, col_maj_b, do_solver, ipiv\n\n\n    // check A and X\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GESV partial pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    std::cout << \"GESV partial pivoting: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < min(m, n) * batches; ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results, ipiv, A and B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_partial_pivot_block_functor {\n    int operator()() { return gesv_batched_partial_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_partial_pivot_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gesv_batched_partial_pivot_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear systems with multiple right hand side after performing LU\n// factorization (without pivoting) of the batched general matrix A.  The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, unsigned int lda_gmem, DataType* B, unsigned int ldb_gmem, typename Solver::status_type* info, const unsigned int batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    const auto one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    const auto one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg = B + size_t(one_batch_size_b_gmem) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, lda_smem, Bs, ldb_smem, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n}\n\ntemplate<int Arch>\nint gesv_batched_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<5, 5, 4>() + Precision<float>() + Type<type::complex>() + Function<gesv_no_pivot>() +\n                          Arrangement<arrangement::row_major, col_major>() + LeadingDimension<5, 7>() + SM<Arch>() + Block() + TransposeMode<conj_trans>());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>() + BlockDim<65, 1, 1>());\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto n            = Solver::n_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     lda          = arrangement_of_v_a<Solver> == col_major ? m : n;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? n : k;\n    const auto     input_size_a = m * n;\n    const auto     input_size_b = n * k;\n\n    const auto batches        = 14;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(\n            arrangement_of_v_a<Solver> == col_major, m, n, A.data(), lda, false, 2, 4, batches); // not symmetric\n    std::vector<data_type> L(input_size_a * padded_batches);\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, n, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of A */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * batches, cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, k, padded_batches, false,\n            (arrangement_of_v_a<Solver> == arrangement::col_major), (arrangement_of_v_b<Solver> == arrangement::col_major),\n            (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans), nullptr, batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GETRF no pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GETRS: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver getrs Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_wo_pivot_block_functor {\n    int operator()() { return gesv_batched_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_wo_pivot_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear systems with multiple right hand side after performing LU\n// factorization (without pivoting) of the batched general matrix A.  The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, unsigned int lda_gmem, DataType* B, unsigned int ldb_gmem, typename Solver::status_type* info, const unsigned int batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    const auto one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    const auto one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg = B + size_t(one_batch_size_b_gmem) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, lda_smem, Bs, ldb_smem, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n}\n\ntemplate<int Arch>\nint gesv_batched_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<5, 5, 4>() + Precision<float>() + Type<type::complex>() + Function<gesv_no_pivot>() +\n                          Arrangement<arrangement::row_major, col_major>() + LeadingDimension<5, 7>() + SM<Arch>() + Block() + TransposeMode<conj_trans>());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>() + BlockDim<65, 1, 1>());\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto n            = Solver::n_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     lda          = arrangement_of_v_a<Solver> == col_major ? m : n;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? n : k;\n    const auto     input_size_a = m * n;\n    const auto     input_size_b = n * k;\n\n    const auto batches        = 14;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(\n            arrangement_of_v_a<Solver> == col_major, m, n, A.data(), lda, false, 2, 4, batches); // not symmetric\n    std::vector<data_type> L(input_size_a * padded_batches);\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, n, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of A */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * batches, hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, k, padded_batches, false,\n            (arrangement_of_v_a<Solver> == arrangement::col_major), (arrangement_of_v_b<Solver> == arrangement::col_major),\n            (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans), nullptr, batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GETRF no pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GETRS: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver getrs Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_wo_pivot_block_functor {\n    int operator()() { return gesv_batched_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_wo_pivot_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gesv_batched_wo_pivot_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing LU factorization (with partial pivoting) of the batched general matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it could be more efficient to use Thread execution instead of Block execution\n\ntemplate<class GESV_thread>\n__global__ void gesv_kernel(typename GESV_thread::a_data_type* A, int* ipiv, typename GESV_thread::b_data_type* B, typename GESV_thread::status_type* info,\n        const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m    = GESV_thread::m_size;\n    constexpr auto nrhs = GESV_thread::k_size;\n    constexpr auto lda  = GESV_thread::lda;\n    constexpr auto ldb  = GESV_thread::ldb;\n\n    const auto one_batch_size_a = lda * m;\n    const auto is_col_maj_b     = GESV_thread::b_arrangement == col_major;\n    const auto one_batch_size_b = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A    = A + one_batch_size_a * batch_idx;\n    auto this_ipiv = ipiv + m * batch_idx;\n    auto this_B    = B + one_batch_size_b * batch_idx;\n\n    // cuSolverDX thread execution can be executed on either global, shared memory or registers. In this example, we use global memory.\n    GESV_thread().execute(this_A, this_ipiv, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesv_batched_partial_pivot_thread() {\n\n    using namespace cusolverdx;\n    using GESV = decltype(Size<7, 7, 3>() + Precision<float>() + Type<type::complex>() + Function<gesv_partial_pivot>() +\n                          Arrangement<arrangement::row_major, row_major>() + SM<Arch>() + Thread() + TransposeMode<conj_trans>());\n\n    using data_type      = typename GESV::a_data_type;\n    using cuda_data_type = typename GESV::a_cuda_data_type;\n\n    constexpr auto m    = GESV::m_size;\n    constexpr auto n    = GESV::n_size;\n    constexpr auto nrhs = GESV::k_size;\n    static_assert(m == n, \"gesv is for square matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<GESV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<GESV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = GESV::lda;\n    const auto ldb = GESV::ldb;\n\n    const auto batches          = 200;\n    const auto one_batch_size_A = lda * m;\n    const auto one_batch_size_B = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, n, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // info is an array of size batches * nrhs, one for each batch and each right hand side\n    std::vector<int>     info(batches * nrhs, 0);\n    std::vector<int>     ipiv(n * batches, 0);\n    std::vector<int64_t> ipiv_ref(n * batches, 0);\n    data_type*           d_A    = nullptr; /* device copy of A */\n    data_type*           d_B    = nullptr; /* device copy of B */\n    int*                 d_info = nullptr; /* error info */\n    int*                 d_ipiv = nullptr; /* pivot indices */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = gesv_kernel<GESV>;\n    const int  nthreads = 32;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_ipiv, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (size_t j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, nrhs, batches, true /* is_pivot */, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<GESV> == trans || transpose_mode_of_v<GESV> == conj_trans), ipiv_ref.data(), batches);\n\n    // check A\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED GESV partial pivot: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    // check X\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED GESV partial pivot: relative error of X between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < n * batches; ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results, ipiv, A and X\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ipiv));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_partial_pivot_thread_functor {\n    int operator()() { return gesv_batched_partial_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_partial_pivot_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing LU factorization (with partial pivoting) of the batched general matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it could be more efficient to use Thread execution instead of Block execution\n\ntemplate<class GESV_thread>\n__global__ void gesv_kernel(typename GESV_thread::a_data_type* A, int* ipiv, typename GESV_thread::b_data_type* B, typename GESV_thread::status_type* info,\n        const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m    = GESV_thread::m_size;\n    constexpr auto nrhs = GESV_thread::k_size;\n    constexpr auto lda  = GESV_thread::lda;\n    constexpr auto ldb  = GESV_thread::ldb;\n\n    const auto one_batch_size_a = lda * m;\n    const auto is_col_maj_b     = GESV_thread::b_arrangement == col_major;\n    const auto one_batch_size_b = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A    = A + one_batch_size_a * batch_idx;\n    auto this_ipiv = ipiv + m * batch_idx;\n    auto this_B    = B + one_batch_size_b * batch_idx;\n\n    // cuSolverDX thread execution can be executed on either global, shared memory or registers. In this example, we use global memory.\n    GESV_thread().execute(this_A, this_ipiv, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesv_batched_partial_pivot_thread() {\n\n    using namespace cusolverdx;\n    using GESV = decltype(Size<7, 7, 3>() + Precision<float>() + Type<type::complex>() + Function<gesv_partial_pivot>() +\n                          Arrangement<arrangement::row_major, row_major>() + SM<Arch>() + Thread() + TransposeMode<conj_trans>());\n\n    using data_type      = typename GESV::a_data_type;\n    using cuda_data_type = typename GESV::a_cuda_data_type;\n\n    constexpr auto m    = GESV::m_size;\n    constexpr auto n    = GESV::n_size;\n    constexpr auto nrhs = GESV::k_size;\n    static_assert(m == n, \"gesv is for square matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<GESV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<GESV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = GESV::lda;\n    const auto ldb = GESV::ldb;\n\n    const auto batches          = 200;\n    const auto one_batch_size_A = lda * m;\n    const auto one_batch_size_B = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, n, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // info is an array of size batches * nrhs, one for each batch and each right hand side\n    std::vector<int>     info(batches * nrhs, 0);\n    std::vector<int>     ipiv(n * batches, 0);\n    std::vector<int64_t> ipiv_ref(n * batches, 0);\n    data_type*           d_A    = nullptr; /* device copy of A */\n    data_type*           d_B    = nullptr; /* device copy of B */\n    int*                 d_info = nullptr; /* error info */\n    int*                 d_ipiv = nullptr; /* pivot indices */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_ipiv), sizeof(int) * ipiv.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = gesv_kernel<GESV>;\n    const int  nthreads = 32;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_ipiv, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(ipiv.data(), d_ipiv, sizeof(int) * ipiv.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (size_t j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, nrhs, batches, true /* is_pivot */, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<GESV> == trans || transpose_mode_of_v<GESV> == conj_trans), ipiv_ref.data(), batches);\n\n    // check A\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED GESV partial pivot: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    // check X\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED GESV partial pivot: relative error of X between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n        return 1;\n    }\n\n    // check ipiv\n    for (int i = 0; i < n * batches; ++i) {\n        if (ipiv[i] != ipiv_ref[i]) {\n            printf(\"ipiv[%d] = %d, ipiv_ref[%d] = %ld differ! \\n\", i, ipiv[i], i, ipiv_ref[i]);\n            std::cout << \"Failure compared with cuSolver API results ipiv\" << std::endl;\n            return 1;\n        }\n    }\n\n    std::cout << \"Success compared with cuSolver API results, ipiv, A and X\" << std::endl;\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ipiv));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_partial_pivot_thread_functor {\n    int operator()() { return gesv_batched_partial_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_partial_pivot_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gesv_batched_partial_pivot_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gtsv.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear tridiagonal systems with multiple right hand side after performing LU\n// factorization without pivoting.  The results are compared with the reference values obtained with cuSparse host API, cuSparse<t>gtsv2StridedBatch().\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(const DataType* dl, const DataType* d, const DataType* du, DataType* B,\n        unsigned int ldb_gmem, typename Solver::status_type* info, const unsigned int batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n    static_assert(m == n, \"m and n must be the same\");\n\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [dl_s, d_s, du_s, B_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType, DataType>(shared_mem, alignof(DataType),\n            (m - 1) * BatchesPerBlock, alignof(DataType), m * BatchesPerBlock, alignof(DataType), (m - 1) * BatchesPerBlock, alignof(DataType),\n            one_batch_size_b_gmem * BatchesPerBlock);\n\n    auto dl_g = dl + (m - 1) * batch_idx;\n    auto d_g  = d + m * batch_idx;\n    auto du_g = du + (m - 1) * batch_idx;\n    auto B_g  = B + one_batch_size_b_gmem * batch_idx;\n\n    // Load data from global memory to shared memory\n    cusolverdx::copy_2d<Solver, m - 1, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(dl_g, m - 1, dl_s, m - 1);\n    cusolverdx::copy_2d<Solver, m, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(d_g, m, d_s, m);\n    cusolverdx::copy_2d<Solver, m - 1, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(du_g, m - 1, du_s, m - 1);\n\n    common::io<Solver, BatchesPerBlock>::load_b(B_g, ldb_gmem, B_s, ldb_smem);\n\n    Solver().execute(dl_s, d_s, du_s, B_s, ldb_smem, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_b(B_s, ldb_smem, B_g, ldb_gmem);\n}\n\ntemplate<int Arch>\nint gtsv_batched_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<18, 18, 4>() + Precision<float>() + Type<type::complex>() + Function<gtsv_no_pivot>() +\n                          Arrangement<arrangement::col_major, row_major>() + // Arrangement of A is ignored\n                          LeadingDimension<5, 19>() +                        // Leading dimension of A is ignored\n                          SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? m : k;\n    const auto     input_size_b = m * k;\n\n    const auto batches        = 4;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> dl((m - 1) * padded_batches);\n    std::vector<data_type> d((m)*padded_batches);\n    std::vector<data_type> du((m - 1) * padded_batches);\n\n    // Fill up a general diagonal dominant matrix A and copy to dl, d, du\n    std::vector<data_type> A(m * m * padded_batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(true /* is_col_major */, m, m, A.data(), m, false /*symmetric*/, -2, 4, batches);\n    for (int b = 0; b < batches; b++) {\n        for (int i = 0; i < m; i++) {\n            d[b * m + i] = A[b * m * m + i * m + i];\n            if (i < m - 1) {\n                du[b * (m - 1) + i] = A[b * m * m + i * m + i + 1];\n            }\n            if (i > 0) {\n                dl[b * (m - 1) + i - 1] = A[b * m * m + i * m + i - 1];\n            }\n        }\n    }\n    A.clear();\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, m, k, B.data(), ldb, false, false, -2, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    // Uncomment below to print dl, d, du. B\n    // printf(\"dl = \\n\");\n    // common::print_matrix<data_type, m - 1, 1, m, true>(dl.data(), batches);\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // printf(\"du = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m, true>(du.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_dl   = nullptr; /* device copy of dl */\n    data_type*       d_d    = nullptr; /* device copy of d */\n    data_type*       d_du   = nullptr; /* device copy of du */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_dl), sizeof(data_type) * dl.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_du), sizeof(data_type) * du.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_dl, dl.data(), sizeof(data_type) * dl.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d.data(), sizeof(data_type) * d.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_du, du.data(), sizeof(data_type) * du.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_dl, d_d, d_du, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * batches, cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"X after cusolverdx gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(X.data(), batches);\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_gtsv<data_type, cuda_data_type, false /* is_perf */>(\n            dl, d, du, B, m, k, padded_batches, (arrangement_of_v_b<Solver> == arrangement::col_major), batches);\n\n    // Uncomment below to print the reference results after cuSolver execute\n    // printf(\"X after cusolver reference gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_dl));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_du));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GTSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver gtsv Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gtsv_batched_wo_pivot_block_functor {\n    int operator()() { return gtsv_batched_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gtsv_batched_wo_pivot_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gtsv.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API to solve a batched linear tridiagonal systems with multiple right hand side after performing LU\n// factorization without pivoting.  The results are compared with the reference values obtained with cuSparse host API, cuSparse<t>gtsv2StridedBatch().\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(const DataType* dl, const DataType* d, const DataType* du, DataType* B,\n        unsigned int ldb_gmem, typename Solver::status_type* info, const unsigned int batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n    static_assert(m == n, \"m and n must be the same\");\n\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : n * ldb_gmem;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : n * ldb_smem;\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [dl_s, d_s, du_s, B_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType, DataType>(shared_mem, alignof(DataType),\n            (m - 1) * BatchesPerBlock, alignof(DataType), m * BatchesPerBlock, alignof(DataType), (m - 1) * BatchesPerBlock, alignof(DataType),\n            one_batch_size_b_gmem * BatchesPerBlock);\n\n    auto dl_g = dl + (m - 1) * batch_idx;\n    auto d_g  = d + m * batch_idx;\n    auto du_g = du + (m - 1) * batch_idx;\n    auto B_g  = B + one_batch_size_b_gmem * batch_idx;\n\n    // Load data from global memory to shared memory\n    cusolverdx::copy_2d<Solver, m - 1, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(dl_g, m - 1, dl_s, m - 1);\n    cusolverdx::copy_2d<Solver, m, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(d_g, m, d_s, m);\n    cusolverdx::copy_2d<Solver, m - 1, 1, cusolverdx::arrangement::col_major, BatchesPerBlock>(du_g, m - 1, du_s, m - 1);\n\n    common::io<Solver, BatchesPerBlock>::load_b(B_g, ldb_gmem, B_s, ldb_smem);\n\n    Solver().execute(dl_s, d_s, du_s, B_s, ldb_smem, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_b(B_s, ldb_smem, B_g, ldb_gmem);\n}\n\ntemplate<int Arch>\nint gtsv_batched_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<18, 18, 4>() + Precision<float>() + Type<type::complex>() + Function<gtsv_no_pivot>() +\n                          Arrangement<arrangement::col_major, row_major>() + // Arrangement of A is ignored\n                          LeadingDimension<5, 19>() +                        // Leading dimension of A is ignored\n                          SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m            = Solver::m_size;\n    constexpr auto k            = Solver::k_size;\n    const auto     ldb          = arrangement_of_v_b<Solver> == col_major ? m : k;\n    const auto     input_size_b = m * k;\n\n    const auto batches        = 4;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> dl((m - 1) * padded_batches);\n    std::vector<data_type> d((m)*padded_batches);\n    std::vector<data_type> du((m - 1) * padded_batches);\n\n    // Fill up a general diagonal dominant matrix A and copy to dl, d, du\n    std::vector<data_type> A(m * m * padded_batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(true /* is_col_major */, m, m, A.data(), m, false /*symmetric*/, -2, 4, batches);\n    for (int b = 0; b < batches; b++) {\n        for (int i = 0; i < m; i++) {\n            d[b * m + i] = A[b * m * m + i * m + i];\n            if (i < m - 1) {\n                du[b * (m - 1) + i] = A[b * m * m + i * m + i + 1];\n            }\n            if (i > 0) {\n                dl[b * (m - 1) + i - 1] = A[b * m * m + i * m + i - 1];\n            }\n        }\n    }\n    A.clear();\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<Solver> == col_major, m, k, B.data(), ldb, false, false, -2, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n    // Uncomment below to print dl, d, du. B\n    // printf(\"dl = \\n\");\n    // common::print_matrix<data_type, m - 1, 1, m, true>(dl.data(), batches);\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // printf(\"du = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m, true>(du.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_dl   = nullptr; /* device copy of dl */\n    data_type*       d_d    = nullptr; /* device copy of d */\n    data_type*       d_du   = nullptr; /* device copy of du */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_dl), sizeof(data_type) * dl.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_du), sizeof(data_type) * du.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_dl, dl.data(), sizeof(data_type) * dl.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d.data(), sizeof(data_type) * d.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_du, du.data(), sizeof(data_type) * du.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_dl, d_d, d_du, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * batches, hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"X after cusolverdx gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(X.data(), batches);\n\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_gtsv<data_type, cuda_data_type, false /* is_perf */>(\n            dl, d, du, B, m, k, padded_batches, (arrangement_of_v_b<Solver> == arrangement::col_major), batches);\n\n    // Uncomment below to print the reference results after cuSolver execute\n    // printf(\"X after cusolver reference gtsv = \\n\");\n    // common::print_matrix<data_type, m, k, ldb, arrangement_of_v_b<Solver> == col_major>(B.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_dl));\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_du));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    // check result\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * input_size_b);\n    printf(\"GTSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver gtsv Result \" << std::endl;\n        return 0;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gtsv_batched_wo_pivot_block_functor {\n    int operator()() { return gtsv_batched_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gtsv_batched_wo_pivot_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gtsv_batched_wo_pivot_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates using cuSolverDx API to perform LU factorization without pivoting on a general MxN matrix.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, unsigned int lda, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda, As, lda);\n\n    Solver().execute(As, lda, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda, A, lda);\n}\n\ntemplate<int Arch>\nint getrf_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<60, 64>() + Precision<double>() + Type<type::real>() + Function<getrf_no_pivot>() + Arrangement<arrangement::col_major>() +\n                            SM<Arch>() + Block() + BlockDim<256>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m          = Solver::m_size;\n    constexpr auto n          = Solver::n_size;\n    const auto     lda        = m;\n    const auto     input_size = lda * n;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(\n        arrangement_of_v_a<Solver> == arrangement::col_major, m, n, A.data(), lda, false, 2, 4); // not symmetric\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_info);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(&info, d_info, sizeof(int), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx getrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        return 1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_lu<data_type, cuda_data_type, false /*do_solver*/>(A, dummy_B, &info, m, n);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), L.size());\n    std::cout << \"GETRF no pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct getrf_wo_pivot_block_functor {\n    int operator()() { return getrf_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<getrf_wo_pivot_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n// This example demonstrates using cuSolverDx API to perform LU factorization without pivoting on a general MxN matrix.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, unsigned int lda, typename Solver::status_type* info) {\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n\n    // Load data from global memory to shared memory\n    common::io<Solver>::load_a(A, lda, As, lda);\n\n    Solver().execute(As, lda, info);\n\n    // Store results back to global memory\n    common::io<Solver>::store_a(As, lda, A, lda);\n}\n\ntemplate<int Arch>\nint getrf_wo_pivot_block() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<60, 64>() + Precision<double>() + Type<type::real>() + Function<getrf_no_pivot>() + Arrangement<arrangement::col_major>() +\n                            SM<Arch>() + Block() + BlockDim<256>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m          = Solver::m_size;\n    constexpr auto n          = Solver::n_size;\n    const auto     lda        = m;\n    const auto     input_size = lda * n;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(\n        arrangement_of_v_a<Solver> == arrangement::col_major, m, n, A.data(), lda, false, 2, 4); // not symmetric\n\n    std::vector<data_type> L(input_size);\n    int                    info   = 0;\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int)));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver><<<1, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_info);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(&info, d_info, sizeof(int), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    printf(\"after cuSolverDx getrf kernel: info = %d\\n\", info);\n    if (0 > info) {\n        std::printf(\"%d-th parameter is wrong \\n\", -info);\n        return 1;\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> dummy_B;\n    common::reference_cusolver_lu<data_type, cuda_data_type, false /*do_solver*/>(A, dummy_B, &info, m, n);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    // check result\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), L.size());\n    std::cout << \"GETRF no pivoting: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\ntemplate<int Arch>\nstruct getrf_wo_pivot_block_functor {\n    int operator()() { return getrf_wo_pivot_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<getrf_wo_pivot_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/getrf_wo_pivot_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing LU factorization (without pivoting) of the batched general matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it could be more efficient to use Thread execution instead of Block execution\n// Each thread is responsible for LU factorization of one batch of A, then solve the linear system with a single right hand side of B.\n\ntemplate<class GESV_thread>\n__global__ void gesv_kernel(\n        typename GESV_thread::a_data_type* A, typename GESV_thread::b_data_type* B, typename GESV_thread::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m    = GESV_thread::m_size;\n    constexpr auto nrhs = GESV_thread::k_size;\n    constexpr auto lda  = GESV_thread::lda;\n    constexpr auto ldb  = GESV_thread::ldb;\n\n    const auto one_batch_size_a = lda * m;\n    const auto is_col_maj_b     = GESV_thread::b_arrangement == col_major;\n    const auto one_batch_size_b = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    // cuSolverDX thread execution can be executed on either global, shared memory or registers. In this example, we use global memory.\n    GESV_thread().execute(this_A, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesv_batched_wo_pivot_thread() {\n\n    using namespace cusolverdx;\n    using GESV = decltype(Size<5, 5, 6>() + Precision<float>() + Type<type::complex>() + Function<gesv_no_pivot>() +\n                          Arrangement<arrangement::row_major, row_major>() + SM<Arch>() + Thread() + TransposeMode<conj_trans>());\n\n\n    using data_type      = typename GESV::a_data_type;\n    using cuda_data_type = typename GESV::a_cuda_data_type;\n\n    constexpr auto m    = GESV::m_size;\n    constexpr auto n    = GESV::n_size;\n    constexpr auto nrhs = GESV::k_size;\n    static_assert(m == n, \"gesv is for square matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<GESV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<GESV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = GESV::lda;\n    const auto ldb = GESV::ldb;\n\n    const auto batches          = 100;\n    const auto one_batch_size_A = lda * m;\n    const auto one_batch_size_B = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<GESV> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<GESV> == col_major, n, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = gesv_kernel<GESV>;\n    const int  nthreads = 128;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (size_t j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, nrhs, batches, false, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<GESV> == trans || transpose_mode_of_v<GESV> == conj_trans), nullptr, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED GESV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED GESV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver getrs Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_wo_pivot_thread_functor {\n    int operator()() { return gesv_batched_wo_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_wo_pivot_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#define CUSOLVERDX_IGNORE_NVBUG_5288270_ASSERT\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_lu.hpp\"\n\n\n// This example demonstrates how to use cuSolverDx API with Thread execution to solve a batched small linear systems with multiple right hand side\n// after performing LU factorization (without pivoting) of the batched general matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n// Each batch has small problem size, so it could be more efficient to use Thread execution instead of Block execution\n// Each thread is responsible for LU factorization of one batch of A, then solve the linear system with a single right hand side of B.\n\ntemplate<class GESV_thread>\n__global__ void gesv_kernel(\n        typename GESV_thread::a_data_type* A, typename GESV_thread::b_data_type* B, typename GESV_thread::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m    = GESV_thread::m_size;\n    constexpr auto nrhs = GESV_thread::k_size;\n    constexpr auto lda  = GESV_thread::lda;\n    constexpr auto ldb  = GESV_thread::ldb;\n\n    const auto one_batch_size_a = lda * m;\n    const auto is_col_maj_b     = GESV_thread::b_arrangement == col_major;\n    const auto one_batch_size_b = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    // cuSolverDX thread execution can be executed on either global, shared memory or registers. In this example, we use global memory.\n    GESV_thread().execute(this_A, this_B, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesv_batched_wo_pivot_thread() {\n\n    using namespace cusolverdx;\n    using GESV = decltype(Size<5, 5, 6>() + Precision<float>() + Type<type::complex>() + Function<gesv_no_pivot>() +\n                          Arrangement<arrangement::row_major, row_major>() + SM<Arch>() + Thread() + TransposeMode<conj_trans>());\n\n\n    using data_type      = typename GESV::a_data_type;\n    using cuda_data_type = typename GESV::a_cuda_data_type;\n\n    constexpr auto m    = GESV::m_size;\n    constexpr auto n    = GESV::n_size;\n    constexpr auto nrhs = GESV::k_size;\n    static_assert(m == n, \"gesv is for square matrix only\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<GESV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<GESV> == arrangement::col_major;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    const auto lda = GESV::lda;\n    const auto ldb = GESV::ldb;\n\n    const auto batches          = 100;\n    const auto one_batch_size_A = lda * m;\n    const auto one_batch_size_B = (is_col_maj_b ? ldb * nrhs : m * ldb);\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> L(one_batch_size_A * batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<GESV> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<GESV> == col_major, n, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto kernel   = gesv_kernel<GESV>;\n    const int  nthreads = 128;\n    const int  nblocks  = (batches + nthreads - 1) / nthreads;\n    kernel<<<nblocks, nthreads, 0, stream>>>(d_A, d_B, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (size_t j = 0; j < info.size(); j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_lu<data_type, cuda_data_type, true>(A, B, info.data(), m, n, nrhs, batches, false, is_col_maj_a, is_col_maj_b,\n            (transpose_mode_of_v<GESV> == trans || transpose_mode_of_v<GESV> == conj_trans), nullptr, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED GESV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED GESV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver getrs Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver getrs Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesv_batched_wo_pivot_thread_functor {\n    int operator()() { return gesv_batched_wo_pivot_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesv_batched_wo_pivot_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/01_Linear_Solve/gesv_batched_wo_pivot_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute the QR factorization on a batched m x n matrix A with Block execution.\n// The results are compared with the reference values obtained with cuBLAS batched API.\n//\n// Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n// This could lead to different results from cuSolver reference for complex data type\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, const int lda_gmem, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, tau_s);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint geqrf_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<16, 20>() + Precision<float>() + Type<type::real>() + Function<geqrf>() + Arrangement<arrangement::row_major>() + SM<Arch>() +\n                          Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto min_mn = m > n ? n : m;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<data_type> tau(min_mn * padded_batches);\n    std::vector<data_type> tau_ref(min_mn * padded_batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Comment below to remove printing A matrix\n    printf(\"A = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Comment below to remove printing results after cuSolverDx execute\n    printf(\"=====\\n\");\n    printf(\" after cuSolverDx\\n\");\n    printf(\"L = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    printf(\"tau = \\n\");\n    common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n\n    //=========================\n    // cuBLAS reference\n    //=========================\n    std::vector<data_type> dummy_b;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type>(A, dummy_b, tau_ref, m, n, 1, batches, is_col_maj_a);\n\n    // check A and tau\n    // Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n    // This could lead to different results from cuSolver reference for complex data type\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GEQRF: relative error of A between cuSolverDx and cuBLAS results: \" << total_relative_error_a << std::endl;\n    const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min_mn);\n    std::cout << \"GEQRF: relative error of tau between cuSolverDx and cuBLAS results: \" << total_relative_error_tau << std::endl;\n\n    // Comment below to remove printing results after cuSolver reference execute\n    printf(\"Lref = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    printf(\"tau_ref = \\n\");\n    common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau_ref.data(), batches);\n    printf(\"=====\\n\");\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuBLAS API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n        std::cout << \"Failure compared with cuBLAS API results TAU\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuBLAS API results, A and tau\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_block_functor {\n    int operator()() { return geqrf_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute the QR factorization on a batched m x n matrix A with Block execution.\n// The results are compared with the reference values obtained with cuBLAS batched API.\n//\n// Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n// This could lead to different results from cuSolver reference for complex data type\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, const int lda_gmem, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, tau_s);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint geqrf_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<16, 20>() + Precision<float>() + Type<type::real>() + Function<geqrf>() + Arrangement<arrangement::row_major>() + SM<Arch>() +\n                          Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto min_mn = m > n ? n : m;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<data_type> tau(min_mn * padded_batches);\n    std::vector<data_type> tau_ref(min_mn * padded_batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Comment below to remove printing A matrix\n    printf(\"A = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Comment below to remove printing results after cuSolverDx execute\n    printf(\"=====\\n\");\n    printf(\" after cuSolverDx\\n\");\n    printf(\"L = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    printf(\"tau = \\n\");\n    common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n\n    //=========================\n    // cuBLAS reference\n    //=========================\n    std::vector<data_type> dummy_b;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type>(A, dummy_b, tau_ref, m, n, 1, batches, is_col_maj_a);\n\n    // check A and tau\n    // Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n    // This could lead to different results from cuSolver reference for complex data type\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GEQRF: relative error of A between cuSolverDx and cuBLAS results: \" << total_relative_error_a << std::endl;\n    const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min_mn);\n    std::cout << \"GEQRF: relative error of tau between cuSolverDx and cuBLAS results: \" << total_relative_error_tau << std::endl;\n\n    // Comment below to remove printing results after cuSolver reference execute\n    printf(\"Lref = \\n\");\n    common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    printf(\"tau_ref = \\n\");\n    common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau_ref.data(), batches);\n    printf(\"=====\\n\");\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuBLAS API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n        std::cout << \"Failure compared with cuBLAS API results TAU\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuBLAS API results, A and tau\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_block_functor {\n    int operator()() { return geqrf_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/geqrf_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_unmqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to multiply a batched matrix B by an orthogonal/unitary matrix Q with Thread operator.\n// The problem size is small, so it could be more efficient to use Thread execution instead of Block execution.\n// computed by QR factorization GEQRF. The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNMQR performs matrix multiplication with an orthogonal/unitary matrix Q:\n//   op(Q) * B    (if side = left)\n//   B * op(Q)    (if side = right)\n// where op(Q) = Q, Q^T, or Q^H depending on the transpose mode.\n//\n// The matrix Q is represented by its elementary reflectors from QR factorization GEQRF:\n//   Q = H(0) * H(1) * ... * H(K-1)\n// where each Householder reflector is:\n//   H(i) = I - tau(i) * v(i) * v(i)^T\n// The v(i) is stored in the i-th column of A below the diagonal.\n//\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void unmqr_kernel(const DataType* A, const DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    // For UNMQR: AM = m, AN = k, BM = m, BN = n\n    constexpr auto AM = (cusolverdx::side_of_v<Solver> == cusolverdx::side::left ? m : n);\n    constexpr auto AN = k;\n    constexpr auto BM = m;\n    constexpr auto BN = n;\n\n    constexpr auto one_batch_size_a_gmem = AM * AN;\n    constexpr auto one_batch_size_b_gmem = BM * BN;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_B   = B + one_batch_size_b_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    Solver().execute(this_A, this_tau, this_B);\n}\n\ntemplate<int Arch>\nint unmqr_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver =\n            decltype(Size<8, 6, 4>() + Precision<float>() + Type<type::complex>() + Function<unmqr>() +\n                     Arrangement<arrangement::row_major, arrangement::row_major>() + TransposeMode<conj_trans>() + Side<side::right>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    constexpr auto a_m = (side_of_v<Solver> == side::left ? m : n);\n    constexpr auto a_n = k;\n    constexpr auto b_m = m;\n    constexpr auto b_n = n;\n\n    const auto     lda          = is_col_maj_a ? a_m : a_n;\n    const auto     ldb          = is_col_maj_b ? b_m : b_n;\n    constexpr auto input_size_a = a_m * a_n;\n    constexpr auto input_size_b = b_m * b_n;\n\n    const auto batches = 2;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * batches);\n    std::vector<data_type> B(input_size_b * batches);\n    std::vector<data_type> tau(k * batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Fill B with random data (the matrix to be multiplied by Q)\n    common::fillup_random_matrix<data_type>(is_col_maj_b, b_m, b_n, B.data(), ldb, false, false, 1, 2, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    std::vector<data_type> B_result(input_size_b * batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n    data_type* d_B   = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    // Invoke kernel to compute Q*B\n    const auto nthreads = 64;\n    unmqr_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(B_result.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"B_result = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_unmqr<data_type, cuda_data_type>(A, B, tau, m, n, k, batches, batches, side_of_v<Solver> == side::left,\n            is_col_maj_a, is_col_maj_b, transpose_mode_of_v<Solver> != transpose::non_transposed);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check B\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(B_result.data(), B.data(), batches * input_size_b);\n    std::cout << \"UNMQR: relative error of B between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"B_ref = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results B\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct unmqr_batched_thread_functor {\n    int operator()() { return unmqr_batched_thread<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<unmqr_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_unmqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to multiply a batched matrix B by an orthogonal/unitary matrix Q with Thread operator.\n// The problem size is small, so it could be more efficient to use Thread execution instead of Block execution.\n// computed by QR factorization GEQRF. The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNMQR performs matrix multiplication with an orthogonal/unitary matrix Q:\n//   op(Q) * B    (if side = left)\n//   B * op(Q)    (if side = right)\n// where op(Q) = Q, Q^T, or Q^H depending on the transpose mode.\n//\n// The matrix Q is represented by its elementary reflectors from QR factorization GEQRF:\n//   Q = H(0) * H(1) * ... * H(K-1)\n// where each Householder reflector is:\n//   H(i) = I - tau(i) * v(i) * v(i)^T\n// The v(i) is stored in the i-th column of A below the diagonal.\n//\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void unmqr_kernel(const DataType* A, const DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    // For UNMQR: AM = m, AN = k, BM = m, BN = n\n    constexpr auto AM = (cusolverdx::side_of_v<Solver> == cusolverdx::side::left ? m : n);\n    constexpr auto AN = k;\n    constexpr auto BM = m;\n    constexpr auto BN = n;\n\n    constexpr auto one_batch_size_a_gmem = AM * AN;\n    constexpr auto one_batch_size_b_gmem = BM * BN;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_B   = B + one_batch_size_b_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    Solver().execute(this_A, this_tau, this_B);\n}\n\ntemplate<int Arch>\nint unmqr_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver =\n            decltype(Size<8, 6, 4>() + Precision<float>() + Type<type::complex>() + Function<unmqr>() +\n                     Arrangement<arrangement::row_major, arrangement::row_major>() + TransposeMode<conj_trans>() + Side<side::right>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    constexpr auto a_m = (side_of_v<Solver> == side::left ? m : n);\n    constexpr auto a_n = k;\n    constexpr auto b_m = m;\n    constexpr auto b_n = n;\n\n    const auto     lda          = is_col_maj_a ? a_m : a_n;\n    const auto     ldb          = is_col_maj_b ? b_m : b_n;\n    constexpr auto input_size_a = a_m * a_n;\n    constexpr auto input_size_b = b_m * b_n;\n\n    const auto batches = 2;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * batches);\n    std::vector<data_type> B(input_size_b * batches);\n    std::vector<data_type> tau(k * batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Fill B with random data (the matrix to be multiplied by Q)\n    common::fillup_random_matrix<data_type>(is_col_maj_b, b_m, b_n, B.data(), ldb, false, false, 1, 2, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    std::vector<data_type> B_result(input_size_b * batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n    data_type* d_B   = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    // Invoke kernel to compute Q*B\n    const auto nthreads = 64;\n    unmqr_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(B_result.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"B_result = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_unmqr<data_type, cuda_data_type>(A, B, tau, m, n, k, batches, batches, side_of_v<Solver> == side::left,\n            is_col_maj_a, is_col_maj_b, transpose_mode_of_v<Solver> != transpose::non_transposed);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check B\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(B_result.data(), B.data(), batches * input_size_b);\n    std::cout << \"UNMQR: relative error of B between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"B_ref = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results B\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct unmqr_batched_thread_functor {\n    int operator()() { return unmqr_batched_thread<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<unmqr_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/unmqr_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute the QR factorization on a batched m x n matrix A using Thread execution.\n// The example is for small problem size, so it could be more efficient to use Thread execution instead of Block execution.\n// The results are compared with the reference values obtained with cuBLAS batched API.\n\n// Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n// This could lead to different results from cuSolver reference for complex data type\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* A, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto one_batch_size = m * n;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    Solver().execute(Ag, tau_g);\n}\n\ntemplate<int Arch>\nint geqrf_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<10, 6>() + Precision<float>() + Type<type::complex>() + Function<geqrf>() + Arrangement<arrangement::row_major>() +\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto min_mn = m > n ? n : m;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 1000;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size_a * batches);\n    std::vector<data_type> tau(min_mn * batches);\n    std::vector<data_type> tau_ref(min_mn * batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // // Comment below to remove printing A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    const auto nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Comment below to remove printing results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"tau = \\n\");\n    // common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n\n    //=========================\n    // cuBLAS reference\n    //=========================\n    std::vector<data_type> dummy_b;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type>(A, dummy_b, tau_ref, m, n, 1, batches, is_col_maj_a);\n\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GEQRF: relative error of A between cuSolverDx and cuBLAS results: \" << total_relative_error_a << std::endl;\n\n    const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min_mn);\n    std::cout << \"GEQRF: relative error of tau between cuSolverDx and cuBLAS results: \" << total_relative_error_tau << std::endl;\n\n    // Comment below to remove printing results after cuSolver reference execute\n    // printf(\"Lref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"tau_ref = \\n\");\n    // common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuBLAS API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n        std::cout << \"Failure compared with cuBLAS API results TAU\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuBLAS API results, A and tau\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_thread_functor {\n    int operator()() { return geqrf_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute the QR factorization on a batched m x n matrix A using Thread execution.\n// The example is for small problem size, so it could be more efficient to use Thread execution instead of Block execution.\n// The results are compared with the reference values obtained with cuBLAS batched API.\n\n// Note that if M <= N, cuSolverDx GEQRF skips computing the last reflector and tau[min(m, n) - 1] is set to 0\n// This could lead to different results from cuSolver reference for complex data type\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* A, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto one_batch_size = m * n;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    Solver().execute(Ag, tau_g);\n}\n\ntemplate<int Arch>\nint geqrf_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<10, 6>() + Precision<float>() + Type<type::complex>() + Function<geqrf>() + Arrangement<arrangement::row_major>() +\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto min_mn = m > n ? n : m;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 1000;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> L(input_size_a * batches);\n    std::vector<data_type> tau(min_mn * batches);\n    std::vector<data_type> tau_ref(min_mn * batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // // Comment below to remove printing A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    const auto nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Comment below to remove printing results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"tau = \\n\");\n    // common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n\n    //=========================\n    // cuBLAS reference\n    //=========================\n    std::vector<data_type> dummy_b;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type>(A, dummy_b, tau_ref, m, n, 1, batches, is_col_maj_a);\n\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n    std::cout << \"GEQRF: relative error of A between cuSolverDx and cuBLAS results: \" << total_relative_error_a << std::endl;\n\n    const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min_mn);\n    std::cout << \"GEQRF: relative error of tau between cuSolverDx and cuBLAS results: \" << total_relative_error_tau << std::endl;\n\n    // Comment below to remove printing results after cuSolver reference execute\n    // printf(\"Lref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"tau_ref = \\n\");\n    // common::print_matrix<data_type, min_mn, 1, min_mn, true>(tau_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuBLAS API results A\" << std::endl;\n        return 1;\n    }\n    if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n        std::cout << \"Failure compared with cuBLAS API results TAU\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuBLAS API results, A and tau\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_thread_functor {\n    int operator()() { return geqrf_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/geqrf_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_ungqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to generate matrix Q from QR factorization of matrix A with Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNGQR generates an M-by-N real/complex matrix Q with orthonormal columns,\n// which is defined as the first N columns of a product of K elementary\n// reflectors of order M:\n//\n// Q = H(0) * H(1) * ... * H(K-1)\n//\n// as returned by GEQRF.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void ungqr_kernel(DataType* A, const DataType* tau, const unsigned batches) {\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr auto one_batch_size_a_gmem = m * n;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    Solver().execute(this_A, this_tau);\n}\n\ntemplate<int Arch>\nint ungqr_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 5, 3>() + Precision<float>() + Type<type::complex>() + Function<ungqr>() + Arrangement<arrangement::row_major>() +\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    constexpr auto m     = Solver::m_size;\n    constexpr auto n     = Solver::n_size;\n    constexpr auto k     = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 200;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * batches);\n    std::vector<data_type> A_result(input_size_a * batches);\n    std::vector<data_type> tau(k * batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), cudaMemcpyHostToDevice, stream));\n\n    const auto nthreads = 64;\n    ungqr_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(A_result.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"A_result = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_ungqr<data_type, cuda_data_type>(A, tau, m, n, k, batches, batches, is_col_maj_a);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check A\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(A_result.data(), A.data(), batches * input_size_a);\n    std::cout << \"UNGQR: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"A_ref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, A\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct ungqr_batched_thread_functor {\n    int operator()() { return ungqr_batched_thread<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<ungqr_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_ungqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to generate matrix Q from QR factorization of matrix A with Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNGQR generates an M-by-N real/complex matrix Q with orthonormal columns,\n// which is defined as the first N columns of a product of K elementary\n// reflectors of order M:\n//\n// Q = H(0) * H(1) * ... * H(K-1)\n//\n// as returned by GEQRF.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void ungqr_kernel(DataType* A, const DataType* tau, const unsigned batches) {\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr auto one_batch_size_a_gmem = m * n;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    Solver().execute(this_A, this_tau);\n}\n\ntemplate<int Arch>\nint ungqr_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 5, 3>() + Precision<float>() + Type<type::complex>() + Function<ungqr>() + Arrangement<arrangement::row_major>() +\n                            SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    constexpr auto m     = Solver::m_size;\n    constexpr auto n     = Solver::n_size;\n    constexpr auto k     = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 200;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * batches);\n    std::vector<data_type> A_result(input_size_a * batches);\n    std::vector<data_type> tau(k * batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), hipMemcpyHostToDevice, stream));\n\n    const auto nthreads = 64;\n    ungqr_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(A_result.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"A_result = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_ungqr<data_type, cuda_data_type>(A, tau, m, n, k, batches, batches, is_col_maj_a);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check A\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(A_result.data(), A.data(), batches * input_size_a);\n    std::cout << \"UNGQR: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"A_ref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, A\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct ungqr_batched_thread_functor {\n    int operator()() { return ungqr_batched_thread<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<ungqr_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/ungqr_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_ungqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to generate matrix Q from QR factorization of matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNGQR generates an M-by-N real/complex matrix Q with orthonormal columns,\n// which is defined as the first N columns of a product of K elementary\n// reflectors of order M:\n//\n// Q = H(0) * H(1) * ... * H(K-1)\n//\n// as returned by GEQRF.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void ungqr_kernel(DataType* A, const DataType* tau, const unsigned batches) {\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto lda_gmem              = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? m : n;\n    constexpr auto one_batch_size_a_gmem = m * n;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n    auto [As, taus] =\n            cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType));\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(this_A, lda_gmem, As, lda_smem);\n\n    const int tid = threadIdx.x + threadIdx.y * Solver::block_dim.x + threadIdx.z * Solver::block_dim.x * Solver::block_dim.y;\n    for (int i = tid; i < k * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        taus[i] = this_tau[i];\n    }\n    __syncthreads();\n\n    Solver().execute(As, lda_smem, taus);\n\n    // store\n    __syncthreads();\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(As, lda_smem, this_A, lda_gmem);\n}\n\ntemplate<int Arch>\nint ungqr_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<26, 25, 23>() + Precision<float>() + Type<type::complex>() + Function<ungqr>() + Arrangement<arrangement::row_major>() +\n                          SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * padded_batches);\n    std::vector<data_type> A_result(input_size_a * padded_batches);\n    std::vector<data_type> tau(k * padded_batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), cudaMemcpyHostToDevice, stream));\n\n    auto       sm_size = Solver::get_shared_memory_size(lda);\n    const auto kernel  = ungqr_kernel<Solver, bpb>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    // Invoke kernel to compute Q*B\n    kernel<<<padded_batches / bpb, Solver::block_dim, sm_size, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(A_result.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"A_result = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_ungqr<data_type, cuda_data_type>(A, tau, m, n, k, padded_batches, batches, is_col_maj_a);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check A\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(A_result.data(), A.data(), batches * input_size_a);\n    std::cout << \"UNGQR: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"A_ref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, A\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct ungqr_batched_block_functor {\n    int operator()() { return ungqr_batched_block<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<ungqr_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_ungqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to generate matrix Q from QR factorization of matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNGQR generates an M-by-N real/complex matrix Q with orthonormal columns,\n// which is defined as the first N columns of a product of K elementary\n// reflectors of order M:\n//\n// Q = H(0) * H(1) * ... * H(K-1)\n//\n// as returned by GEQRF.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void ungqr_kernel(DataType* A, const DataType* tau, const unsigned batches) {\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto lda_gmem              = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? m : n;\n    constexpr auto one_batch_size_a_gmem = m * n;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ cusolverdx::byte shared_mem[];\n    auto [As, taus] =\n            cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType));\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(this_A, lda_gmem, As, lda_smem);\n\n    const int tid = threadIdx.x + threadIdx.y * Solver::block_dim.x + threadIdx.z * Solver::block_dim.x * Solver::block_dim.y;\n    for (int i = tid; i < k * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        taus[i] = this_tau[i];\n    }\n    __syncthreads();\n\n    Solver().execute(As, lda_smem, taus);\n\n    // store\n    __syncthreads();\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(As, lda_smem, this_A, lda_gmem);\n}\n\ntemplate<int Arch>\nint ungqr_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<26, 25, 23>() + Precision<float>() + Type<type::complex>() + Function<ungqr>() + Arrangement<arrangement::row_major>() +\n                          SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * padded_batches);\n    std::vector<data_type> A_result(input_size_a * padded_batches);\n    std::vector<data_type> tau(k * padded_batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), hipMemcpyHostToDevice, stream));\n\n    auto       sm_size = Solver::get_shared_memory_size(lda);\n    const auto kernel  = ungqr_kernel<Solver, bpb>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    // Invoke kernel to compute Q*B\n    kernel<<<padded_batches / bpb, Solver::block_dim, sm_size, stream>>>(d_A, d_tau, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(A_result.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"A_result = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_ungqr<data_type, cuda_data_type>(A, tau, m, n, k, padded_batches, batches, is_col_maj_a);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check A\n    const auto total_relative_error_a = common::check_error<data_type, data_type>(A_result.data(), A.data(), batches * input_size_a);\n    std::cout << \"UNGQR: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"A_ref = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n        std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, A\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct ungqr_batched_block_functor {\n    int operator()() { return ungqr_batched_block<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<ungqr_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/ungqr_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_unmqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to multiply a batched matrix B by an orthogonal/unitary matrix Q,\n// computed by QR factorization GEQRF. The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNMQR performs matrix multiplication with an orthogonal/unitary matrix Q:\n//   op(Q) * B    (if side = left)\n//   B * op(Q)    (if side = right)\n// where op(Q) = Q, Q^T, or Q^H depending on the transpose mode.\n//\n// The matrix Q is represented by its elementary reflectors from QR factorization GEQRF:\n//   Q = H(0) * H(1) * ... * H(K-1)\n// where each Householder reflector is:\n//   H(i) = I - tau(i) * v(i) * v(i)^T\n// The v(i) is stored in the i-th column of A below the diagonal.\n//\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void unmqr_kernel(const DataType* A, const DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    // For UNMQR: AM = m, AN = k, BM = m, BN = n\n    constexpr auto AM = (cusolverdx::side_of_v<Solver> == cusolverdx::side::left ? m : n);\n    constexpr auto AN = k;\n    constexpr auto BM = m;\n    constexpr auto BN = n;\n\n    const auto lda_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? AM : AN;\n    const auto ldb_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? BM : BN;\n    const auto lda_smem = Solver::lda;\n    const auto ldb_smem = Solver::ldb;\n\n    constexpr auto one_batch_size_a_gmem = AM * AN;\n    constexpr auto one_batch_size_b_gmem = BM * BN;\n    const auto     one_batch_size_a_smem = cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major ? lda_smem * AN : AM * lda_smem;\n    const auto     one_batch_size_b_smem = cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major ? ldb_smem * BN : BM * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, taus] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType), one_batch_size_b_smem * BatchesPerBlock, alignof(DataType), k * BatchesPerBlock);\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_B   = B + one_batch_size_b_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<AM, AN>(this_A, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b<BM, BN>(this_B, ldb_gmem, Bs, ldb_smem);\n\n    // Load tau from global memory to shared memory\n    const int tid = threadIdx.x + threadIdx.y * Solver::block_dim.x + threadIdx.z * Solver::block_dim.x * Solver::block_dim.y;\n    for (int i = tid; i < k * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        taus[i] = this_tau[i];\n    }\n    __syncthreads();\n\n    Solver().execute(As, lda_smem, taus, Bs, ldb_smem);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_b<BM, BN>(Bs, ldb_smem, this_B, ldb_gmem);\n}\n\ntemplate<int Arch>\nint unmqr_batched_block() {\n\n    using namespace cusolverdx;\n    using Base =\n            decltype(Size<32, 16, 14>() + Precision<float>() + Type<type::complex>() + Function<unmqr>() +\n                     Arrangement<arrangement::row_major, arrangement::row_major>() + TransposeMode<conj_trans>() + Side<side::right>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    constexpr auto a_m = (side_of_v<Solver> == side::left ? m : n);\n    constexpr auto a_n = k;\n    constexpr auto b_m = m;\n    constexpr auto b_n = n;\n\n    const auto     lda          = is_col_maj_a ? a_m : a_n;\n    const auto     ldb          = is_col_maj_b ? b_m : b_n;\n    constexpr auto input_size_a = a_m * a_n;\n    constexpr auto input_size_b = b_m * b_n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * padded_batches);\n    std::vector<data_type> B(input_size_b * padded_batches);\n    std::vector<data_type> tau(k * padded_batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Fill B with random data (the matrix to be multiplied by Q)\n    common::fillup_random_matrix<data_type>(is_col_maj_b, b_m, b_n, B.data(), ldb, false, false, 1, 2, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    std::vector<data_type> B_result(input_size_b * padded_batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n    data_type* d_B   = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    auto       sm_size = Solver::get_shared_memory_size(lda, ldb);\n    const auto kernel  = unmqr_kernel<Solver, bpb>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    // Invoke kernel to compute Q*B\n    kernel<<<padded_batches / bpb, Solver::block_dim, sm_size, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(B_result.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"B_result = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_unmqr<data_type, cuda_data_type>(A, B, tau, m, n, k, padded_batches, batches, side_of_v<Solver> == side::left,\n            is_col_maj_a, is_col_maj_b, transpose_mode_of_v<Solver> != transpose::non_transposed);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check B\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(B_result.data(), B.data(), batches * input_size_b);\n    std::cout << \"UNMQR: relative error of B between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"B_ref = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results B\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct unmqr_batched_block_functor {\n    int operator()() { return unmqr_batched_block<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<unmqr_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_unmqr.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to multiply a batched matrix B by an orthogonal/unitary matrix Q,\n// computed by QR factorization GEQRF. The results are compared with the reference values obtained with cuSolver host API.\n//\n// UNMQR performs matrix multiplication with an orthogonal/unitary matrix Q:\n//   op(Q) * B    (if side = left)\n//   B * op(Q)    (if side = right)\n// where op(Q) = Q, Q^T, or Q^H depending on the transpose mode.\n//\n// The matrix Q is represented by its elementary reflectors from QR factorization GEQRF:\n//   Q = H(0) * H(1) * ... * H(K-1)\n// where each Householder reflector is:\n//   H(i) = I - tau(i) * v(i) * v(i)^T\n// The v(i) is stored in the i-th column of A below the diagonal.\n//\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void unmqr_kernel(const DataType* A, const DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    // For UNMQR: AM = m, AN = k, BM = m, BN = n\n    constexpr auto AM = (cusolverdx::side_of_v<Solver> == cusolverdx::side::left ? m : n);\n    constexpr auto AN = k;\n    constexpr auto BM = m;\n    constexpr auto BN = n;\n\n    const auto lda_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? AM : AN;\n    const auto ldb_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? BM : BN;\n    const auto lda_smem = Solver::lda;\n    const auto ldb_smem = Solver::ldb;\n\n    constexpr auto one_batch_size_a_gmem = AM * AN;\n    constexpr auto one_batch_size_b_gmem = BM * BN;\n    const auto     one_batch_size_a_smem = cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major ? lda_smem * AN : AM * lda_smem;\n    const auto     one_batch_size_b_smem = cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major ? ldb_smem * BN : BM * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, taus] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n            alignof(DataType), one_batch_size_b_smem * BatchesPerBlock, alignof(DataType), k * BatchesPerBlock);\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto this_A   = A + one_batch_size_a_gmem * batch_idx;\n    auto this_B   = B + one_batch_size_b_gmem * batch_idx;\n    auto this_tau = tau + k * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<AM, AN>(this_A, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b<BM, BN>(this_B, ldb_gmem, Bs, ldb_smem);\n\n    // Load tau from global memory to shared memory\n    const int tid = threadIdx.x + threadIdx.y * Solver::block_dim.x + threadIdx.z * Solver::block_dim.x * Solver::block_dim.y;\n    for (int i = tid; i < k * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        taus[i] = this_tau[i];\n    }\n    __syncthreads();\n\n    Solver().execute(As, lda_smem, taus, Bs, ldb_smem);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_b<BM, BN>(Bs, ldb_smem, this_B, ldb_gmem);\n}\n\ntemplate<int Arch>\nint unmqr_batched_block() {\n\n    using namespace cusolverdx;\n    using Base =\n            decltype(Size<32, 16, 14>() + Precision<float>() + Type<type::complex>() + Function<unmqr>() +\n                     Arrangement<arrangement::row_major, arrangement::row_major>() + TransposeMode<conj_trans>() + Side<side::right>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto k = Solver::k_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    constexpr auto a_m = (side_of_v<Solver> == side::left ? m : n);\n    constexpr auto a_n = k;\n    constexpr auto b_m = m;\n    constexpr auto b_n = n;\n\n    const auto     lda          = is_col_maj_a ? a_m : a_n;\n    const auto     ldb          = is_col_maj_b ? b_m : b_n;\n    constexpr auto input_size_a = a_m * a_n;\n    constexpr auto input_size_b = b_m * b_n;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    // Generate input matrices A and B\n    std::vector<data_type> A(input_size_a * padded_batches);\n    std::vector<data_type> B(input_size_b * padded_batches);\n    std::vector<data_type> tau(k * padded_batches);\n\n    // Fill A with random data (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_n, A.data(), lda, false, false, -0.1, 0.1, batches);\n\n    // Fill B with random data (the matrix to be multiplied by Q)\n    common::fillup_random_matrix<data_type>(is_col_maj_b, b_m, b_n, B.data(), ldb, false, false, 1, 2, batches);\n\n    // Generate tau values (this would normally come from a previous GEQRF call)\n    common::fillup_random_matrix<data_type>(true, k, 1, tau.data(), k, false, false, -2, 2, batches);\n\n    std::vector<data_type> B_result(input_size_b * padded_batches);\n\n    data_type* d_A   = nullptr;\n    data_type* d_tau = nullptr;\n    data_type* d_B   = nullptr;\n\n    // Uncomment below to print matrices\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_tau, tau.data(), sizeof(data_type) * tau.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    auto       sm_size = Solver::get_shared_memory_size(lda, ldb);\n    const auto kernel  = unmqr_kernel<Solver, bpb>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    // Invoke kernel to compute Q*B\n    kernel<<<padded_batches / bpb, Solver::block_dim, sm_size, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(B_result.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx UNMQR\\n\");\n    // printf(\"B_result = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B_result.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    // Use cuSolver UNMQR reference implementation\n    bool ref_success = common::reference_cusolver_unmqr<data_type, cuda_data_type>(A, B, tau, m, n, k, padded_batches, batches, side_of_v<Solver> == side::left,\n            is_col_maj_a, is_col_maj_b, transpose_mode_of_v<Solver> != transpose::non_transposed);\n\n    if (!ref_success) {\n        std::cout << \"cuSolver reference computation failed\" << std::endl;\n        return 1;\n    }\n\n    // check B\n    const auto total_relative_error_b = common::check_error<data_type, data_type>(B_result.data(), B.data(), batches * input_size_b);\n    std::cout << \"UNMQR: relative error of B between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n    // Uncomment below to print results after reference execute\n    // printf(\"B_ref = \\n\");\n    // common::print_matrix<data_type, b_m, b_n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n        std::cout << \"Failure compared with cuSolver API results B\" << std::endl;\n        return 1;\n    }\n\n    std::cout << \"Success compared with cuSolverDn<t>un/or_mqr API results, B\" << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct unmqr_batched_block_functor {\n    int operator()() { return unmqr_batched_block<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<unmqr_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/03_Orthogonal_Factors/unmqr_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to solve triangular systems with multiple right hand sides.\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\ntemplate<class TRSM_thread, class DataType = typename TRSM_thread::a_data_type>\n__global__ void trsm_kernel(const DataType* A, DataType* B, const unsigned int batches) {\n\n    constexpr auto a_m = (TRSM_thread::side == cusolverdx::side::left) ? TRSM_thread::m_size : TRSM_thread::n_size;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    const auto one_batch_size_a = a_m * a_m;\n    const auto one_batch_size_b = TRSM_thread::m_size * TRSM_thread::n_size;\n    auto       this_A           = A + one_batch_size_a * batch_idx;\n    auto       this_B           = B + one_batch_size_b * batch_idx;\n\n    TRSM_thread().execute(this_A, this_B);\n}\n\ntemplate<int Arch>\nint trsm_thread_execution() {\n    using namespace cusolverdx;\n\n    // The Size Operator for TRSM only takes M, N. If K is specified, it is ignored.\n    using TRSM = decltype(Size<10, 12>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<trans>() + Diag<diag::non_unit>() +\n                          Arrangement<col_major, col_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    constexpr auto m = TRSM::m_size;\n    constexpr auto n = TRSM::n_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<TRSM> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<TRSM> == arrangement::col_major;\n    constexpr auto ldb          = is_col_maj_b ? m : n;\n\n    // If left side, A is MxM, else A is NxN\n    constexpr auto a_m = (side_of_v<TRSM> == side::left) ? m : n; // The acutal size of A\n\n    const auto batches          = 300;\n    const auto one_batch_size_A = a_m * a_m;\n    const auto one_batch_size_B = m * n;\n    printf(\"TRSM Size M = %d, N = %d, %s side\\n\", m, n, (side_of_v<TRSM> == side::left) ? \"left\" : \"right\");\n    printf(\"matrix A size is %dx%d, matrix B size is %dx%d, total batches = %d\\n\", a_m, a_m, m, n, batches);\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> B(one_batch_size_B * batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), a_m, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, n, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    const auto nthreads = 32;\n    const auto nblocks  = (batches + nthreads - 1) / nthreads; // each thread solvers one RHS of a batch of B\n    printf(\"Launching kernel with GridSize = %d, BlockSize= %d -> total # of threads = %d\\n\", nblocks, nthreads, nblocks * nthreads);\n    printf(\"# of batches  = %d \\n\", batches);\n\n    trsm_kernel<TRSM><<<nblocks, nthreads, 0, stream>>>(d_A, d_B, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, a_m, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               m,\n                                                                                               n,\n                                                                                               batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_thread_execution_functor {\n    int operator()() { return trsm_thread_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_thread_execution_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to solve triangular systems with multiple right hand sides.\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\ntemplate<class TRSM_thread, class DataType = typename TRSM_thread::a_data_type>\n__global__ void trsm_kernel(const DataType* A, DataType* B, const unsigned int batches) {\n\n    constexpr auto a_m = (TRSM_thread::side == cusolverdx::side::left) ? TRSM_thread::m_size : TRSM_thread::n_size;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    const auto one_batch_size_a = a_m * a_m;\n    const auto one_batch_size_b = TRSM_thread::m_size * TRSM_thread::n_size;\n    auto       this_A           = A + one_batch_size_a * batch_idx;\n    auto       this_B           = B + one_batch_size_b * batch_idx;\n\n    TRSM_thread().execute(this_A, this_B);\n}\n\ntemplate<int Arch>\nint trsm_thread_execution() {\n    using namespace cusolverdx;\n\n    // The Size Operator for TRSM only takes M, N. If K is specified, it is ignored.\n    using TRSM = decltype(Size<10, 12>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<trans>() + Diag<diag::non_unit>() +\n                          Arrangement<col_major, col_major>() + SM<Arch>() + Thread());\n\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    constexpr auto m = TRSM::m_size;\n    constexpr auto n = TRSM::n_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<TRSM> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<TRSM> == arrangement::col_major;\n    constexpr auto ldb          = is_col_maj_b ? m : n;\n\n    // If left side, A is MxM, else A is NxN\n    constexpr auto a_m = (side_of_v<TRSM> == side::left) ? m : n; // The acutal size of A\n\n    const auto batches          = 300;\n    const auto one_batch_size_A = a_m * a_m;\n    const auto one_batch_size_B = m * n;\n    printf(\"TRSM Size M = %d, N = %d, %s side\\n\", m, n, (side_of_v<TRSM> == side::left) ? \"left\" : \"right\");\n    printf(\"matrix A size is %dx%d, matrix B size is %dx%d, total batches = %d\\n\", a_m, a_m, m, n, batches);\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> B(one_batch_size_B * batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), a_m, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, n, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    const auto nthreads = 32;\n    const auto nblocks  = (batches + nthreads - 1) / nthreads; // each thread solvers one RHS of a batch of B\n    printf(\"Launching kernel with GridSize = %d, BlockSize= %d -> total # of threads = %d\\n\", nblocks, nthreads, nblocks * nthreads);\n    printf(\"# of batches  = %d \\n\", batches);\n\n    trsm_kernel<TRSM><<<nblocks, nthreads, 0, stream>>>(d_A, d_B, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, a_m, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               m,\n                                                                                               n,\n                                                                                               batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_thread_execution_functor {\n    int operator()() { return trsm_thread_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_thread_execution_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/07_BLAS/trsm_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example demonstrates how to use cuSolverDx block execution API to solve triangular systems\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\ntemplate<class TRSM, unsigned int BatchesPerBlock, class DataType = typename TRSM::a_data_type>\n__global__ __launch_bounds__(TRSM::max_threads_per_block) void trsm_kernel(const DataType* A, const unsigned int lda_gmem, DataType* B, const unsigned int ldb_gmem, const unsigned int batches) {\n    constexpr auto a_m = (cusolverdx::side_of_v<TRSM> == cusolverdx::side::left) ? TRSM::m_size : TRSM::n_size;\n\n    constexpr auto one_batch_size_a = a_m * a_m;\n    constexpr auto one_batch_size_b = TRSM::m_size * TRSM::n_size;\n\n    constexpr auto lda_smem              = TRSM::lda;\n    constexpr auto ldb_smem              = TRSM::ldb;\n    const auto     one_batch_size_a_smem = lda_smem * a_m;\n    const auto     one_batch_size_b_smem = ldb_smem * (cusolverdx::arrangement_of_v_b<TRSM> == cusolverdx::col_major ? TRSM::n_size : TRSM::m_size);\n\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem,\n                                                                         alignof(DataType),\n                                                                         one_batch_size_a_smem * BatchesPerBlock,\n                                                                         alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<TRSM, BatchesPerBlock>::load_a<a_m, a_m, cusolverdx::arrangement_of_v_a<TRSM>>(this_A, lda_gmem, As, lda_smem);\n    common::io<TRSM, BatchesPerBlock>::load_b<TRSM::m_size, TRSM::n_size>(this_B, ldb_gmem, Bs, ldb_smem);\n\n    TRSM().execute(As, lda_smem, Bs, ldb_smem);\n\n    // store\n    common::io<TRSM, BatchesPerBlock>::store_b<TRSM::m_size, TRSM::n_size>(Bs, ldb_smem, this_B, ldb_gmem);\n}\n\ntemplate<int Arch>\nint trsm_block_execution() {\n    using namespace cusolverdx;\n\n    // The Size Operator for TRSM only takes M, N. If K is specified, it is ignored. \n    using TRSM = decltype(Size<32, 33>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<non_trans>() +\n                          Diag<diag::non_unit>() + Arrangement<col_major, col_major>() + SM<Arch>() + Block());\n\n    constexpr unsigned bpb = TRSM::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Using Suggested BlockDim = \" << TRSM::suggested_block_dim.x << std::endl;\n    std::cout << \"Using Specified BlockDim = \" << TRSM::block_dim.x << std::endl;\n\n\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    constexpr auto m        = TRSM::m_size;\n    constexpr auto n        = TRSM::n_size;\n    constexpr auto lda_smem = TRSM::lda;\n    constexpr auto ldb_smem = TRSM::ldb;\n\n    std::cout << \"lda_smem = \" << lda_smem << \", ldb_smem = \" << ldb_smem << std::endl;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<TRSM> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<TRSM> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto a_m = (side_of_v<TRSM> == side::left) ? m : n;\n    constexpr auto lda = a_m;\n    constexpr auto ldb = is_col_maj_b ? m : n;\n\n    printf(\"TRSM Size m = %d, n = %d\\n\", m, n);\n    std::cout << \"Using leading dimension LDA = \" << lda_smem << \", LDB = \" << ldb_smem << std::endl;\n\n    const auto batches        = 1;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    const auto one_batch_size_A = lda * a_m; // no padding for global memory\n    const auto one_batch_size_B = m * n;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * padded_batches);\n    std::vector<data_type> B(one_batch_size_B * padded_batches);\n    std::vector<data_type> X(one_batch_size_B * padded_batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), lda, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, n, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto sm_size = TRSM::shared_memory_size;\n    std::cout << \"sm_size = \" << sm_size << std::endl;\n\n    const auto kernel = trsm_kernel<TRSM, TRSM::batches_per_block>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    //Invokes kernel\n    kernel<<<(batches + bpb - 1) / bpb, TRSM::block_dim, sm_size, stream>>>(d_A, lda, d_B, ldb, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(X.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               m,\n                                                                                               n,\n                                                                                               padded_batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_block_execution_functor {\n    int operator()() { return trsm_block_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_block_execution_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example demonstrates how to use cuSolverDx block execution API to solve triangular systems\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\ntemplate<class TRSM, unsigned int BatchesPerBlock, class DataType = typename TRSM::a_data_type>\n__global__ __launch_bounds__(TRSM::max_threads_per_block) void trsm_kernel(const DataType* A, const unsigned int lda_gmem, DataType* B, const unsigned int ldb_gmem, const unsigned int batches) {\n    constexpr auto a_m = (cusolverdx::side_of_v<TRSM> == cusolverdx::side::left) ? TRSM::m_size : TRSM::n_size;\n\n    constexpr auto one_batch_size_a = a_m * a_m;\n    constexpr auto one_batch_size_b = TRSM::m_size * TRSM::n_size;\n\n    constexpr auto lda_smem              = TRSM::lda;\n    constexpr auto ldb_smem              = TRSM::ldb;\n    const auto     one_batch_size_a_smem = lda_smem * a_m;\n    const auto     one_batch_size_b_smem = ldb_smem * (cusolverdx::arrangement_of_v_b<TRSM> == cusolverdx::col_major ? TRSM::n_size : TRSM::m_size);\n\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs] = cusolverdx::shared_memory::slice<DataType, DataType>(shared_mem,\n                                                                         alignof(DataType),\n                                                                         one_batch_size_a_smem * BatchesPerBlock,\n                                                                         alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto this_A = A + one_batch_size_a * batch_idx;\n    auto this_B = B + one_batch_size_b * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<TRSM, BatchesPerBlock>::load_a<a_m, a_m, cusolverdx::arrangement_of_v_a<TRSM>>(this_A, lda_gmem, As, lda_smem);\n    common::io<TRSM, BatchesPerBlock>::load_b<TRSM::m_size, TRSM::n_size>(this_B, ldb_gmem, Bs, ldb_smem);\n\n    TRSM().execute(As, lda_smem, Bs, ldb_smem);\n\n    // store\n    common::io<TRSM, BatchesPerBlock>::store_b<TRSM::m_size, TRSM::n_size>(Bs, ldb_smem, this_B, ldb_gmem);\n}\n\ntemplate<int Arch>\nint trsm_block_execution() {\n    using namespace cusolverdx;\n\n    // The Size Operator for TRSM only takes M, N. If K is specified, it is ignored. \n    using TRSM = decltype(Size<32, 33>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<non_trans>() +\n                          Diag<diag::non_unit>() + Arrangement<col_major, col_major>() + SM<Arch>() + Block());\n\n    constexpr unsigned bpb = TRSM::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Using Suggested BlockDim = \" << TRSM::suggested_block_dim.x << std::endl;\n    std::cout << \"Using Specified BlockDim = \" << TRSM::block_dim.x << std::endl;\n\n\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    constexpr auto m        = TRSM::m_size;\n    constexpr auto n        = TRSM::n_size;\n    constexpr auto lda_smem = TRSM::lda;\n    constexpr auto ldb_smem = TRSM::ldb;\n\n    std::cout << \"lda_smem = \" << lda_smem << \", ldb_smem = \" << ldb_smem << std::endl;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<TRSM> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<TRSM> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto a_m = (side_of_v<TRSM> == side::left) ? m : n;\n    constexpr auto lda = a_m;\n    constexpr auto ldb = is_col_maj_b ? m : n;\n\n    printf(\"TRSM Size m = %d, n = %d\\n\", m, n);\n    std::cout << \"Using leading dimension LDA = \" << lda_smem << \", LDB = \" << ldb_smem << std::endl;\n\n    const auto batches        = 1;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    const auto one_batch_size_A = lda * a_m; // no padding for global memory\n    const auto one_batch_size_B = m * n;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * padded_batches);\n    std::vector<data_type> B(one_batch_size_B * padded_batches);\n    std::vector<data_type> X(one_batch_size_B * padded_batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), lda, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, m, n, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto sm_size = TRSM::shared_memory_size;\n    std::cout << \"sm_size = \" << sm_size << std::endl;\n\n    const auto kernel = trsm_kernel<TRSM, TRSM::batches_per_block>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    //Invokes kernel\n    kernel<<<(batches + bpb - 1) / bpb, TRSM::block_dim, sm_size, stream>>>(d_A, lda, d_B, ldb, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(X.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               m,\n                                                                                               n,\n                                                                                               padded_batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, m, n, ldb, is_col_maj_b>(B.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_block_execution_functor {\n    int operator()() { return trsm_block_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_block_execution_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/07_BLAS/trsm_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example builds based on the previous example trsm_batched_thread.cu, with a more performant approach, i.e., each thread solves one RHS of a batch of B\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\nusing namespace cusolverdx;\n\n// The problem size is the same as the previous example trsm_batched_thread.cu\n// If left side: Each batch of A is 10 x 10 matrix, and B is 10 x 12 matrix with 12 right hand sides. But we set the size operator to be Size<M, 1> to indicate each thread solves one RHS of a batch of B\nconstexpr unsigned M = 10;\nconstexpr unsigned N = 12;\nusing TRSM_base      = decltype(Size<M, 1>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::left>() + FillMode<lower>() + TransposeMode<trans>() +\n                           Diag<diag::non_unit>() + Arrangement<col_major, row_major>() + Thread());\n\n// if right side: Each batch of A is 12 x 12 matrix, and B is 10 x 12 matrix with 10 rows of right hand sides. We should set the size operator to be Size<1, N> to indicate each thread solves one RHS of one batch of B. Uncomment below to use the right side configuration.\n// using TRSM_base      = decltype(Size<1, N>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<trans>() +\n//                            Diag<diag::non_unit>() + Arrangement<col_major, row_major>() + Thread());\n\n\ntemplate<class TRSM, class DataType = typename TRSM::a_data_type>\n__global__ void trsm_kernel(const DataType* A, DataType* B, const unsigned int batches) {\n    constexpr auto a_m  = (TRSM::side == cusolverdx::side::left) ? M : N;\n    constexpr auto nrhs = TRSM::side == cusolverdx::side::left ? N : M;\n\n    const auto tid       = threadIdx.x + blockIdx.x * blockDim.x;\n    const auto batch_idx = tid / nrhs;\n    const auto rhs_idx   = tid % nrhs;\n\n    if (batch_idx * nrhs + rhs_idx >= batches * nrhs)\n        return;\n\n    const auto one_batch_size_a = a_m * a_m;\n    auto       this_A           = A + one_batch_size_a * batch_idx;\n\n    const auto one_batch_size_b = M * N;\n    // Find the start pointer of the RHS for the current batch of B\n    const auto b_rhs_start = ((TRSM::side == cusolverdx::side::left) == (TRSM::b_arrangement == cusolverdx::arrangement::col_major)) ? a_m : 1;\n    const auto b_rhs_stride = ((TRSM::side == cusolverdx::side::left) == (TRSM::b_arrangement == cusolverdx::arrangement::col_major)) ? 1 : nrhs;\n    auto       this_B        = B + one_batch_size_b * batch_idx + rhs_idx * b_rhs_start;\n\n    TRSM().execute(this_A, a_m, this_B, b_rhs_stride);\n}\n\ntemplate<int Arch>\nint trsm_thread_execution() {\n    using TRSM           = decltype(TRSM_base() + SM<Arch>());\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    const auto     batches          = 300;\n    constexpr auto a_m              = TRSM::side == side::left ? M : N;\n    constexpr auto nrhs             = TRSM::side == side::left ? N : M;\n    const auto     one_batch_size_A = a_m * a_m;\n    const auto     one_batch_size_B = M * N;\n    constexpr bool is_col_maj_a     = TRSM::a_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_b     = TRSM::b_arrangement == arrangement::col_major;\n    constexpr auto ldb              = is_col_maj_b ? M : N;\n\n    printf(\"TRSM Size M = %d, N = %d, %s side\\n\", TRSM::m_size, TRSM::n_size, (TRSM::side == side::left) ? \"left\" : \"right\");\n    printf(\"matrix A size is %dx%d, matrix B size is %dx%d, total batches = %d\\n\", a_m, a_m, M, N, batches);\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> B(one_batch_size_B * batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), a_m, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, M, N, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    const auto nthreads = 32;\n    const auto nblocks  = (batches * nrhs + nthreads - 1) / nthreads; // each thread solvers one RHS of a batch of B\n    printf(\"Launching kernel with GridSize = %d, BlockSize= %d -> total # of threads = %d\\n\", nblocks, nthreads, nblocks * nthreads);\n    printf(\"# of batches x # of RHSs = %d x %d = %d\\n\", batches, nrhs, batches * nrhs);\n\n    trsm_kernel<TRSM><<<nblocks, nthreads, 0, stream>>>(d_A, d_B, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               M,\n                                                                                               N,\n                                                                                               batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_thread_execution_functor {\n    int operator()() { return trsm_thread_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_thread_execution_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_trsm.hpp\"\n\n// This example builds based on the previous example trsm_batched_thread.cu, with a more performant approach, i.e., each thread solves one RHS of a batch of B\n// The results are compared with the reference values obtained with cublasXtrsm API.\n\nusing namespace cusolverdx;\n\n// The problem size is the same as the previous example trsm_batched_thread.cu\n// If left side: Each batch of A is 10 x 10 matrix, and B is 10 x 12 matrix with 12 right hand sides. But we set the size operator to be Size<M, 1> to indicate each thread solves one RHS of a batch of B\nconstexpr unsigned M = 10;\nconstexpr unsigned N = 12;\nusing TRSM_base      = decltype(Size<M, 1>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::left>() + FillMode<lower>() + TransposeMode<trans>() +\n                           Diag<diag::non_unit>() + Arrangement<col_major, row_major>() + Thread());\n\n// if right side: Each batch of A is 12 x 12 matrix, and B is 10 x 12 matrix with 10 rows of right hand sides. We should set the size operator to be Size<1, N> to indicate each thread solves one RHS of one batch of B. Uncomment below to use the right side configuration.\n// using TRSM_base      = decltype(Size<1, N>() + Precision<double>() + Type<type::real>() + Function<function::trsm>() + Side<side::right>() + FillMode<lower>() + TransposeMode<trans>() +\n//                            Diag<diag::non_unit>() + Arrangement<col_major, row_major>() + Thread());\n\n\ntemplate<class TRSM, class DataType = typename TRSM::a_data_type>\n__global__ void trsm_kernel(const DataType* A, DataType* B, const unsigned int batches) {\n    constexpr auto a_m  = (TRSM::side == cusolverdx::side::left) ? M : N;\n    constexpr auto nrhs = TRSM::side == cusolverdx::side::left ? N : M;\n\n    const auto tid       = threadIdx.x + blockIdx.x * blockDim.x;\n    const auto batch_idx = tid / nrhs;\n    const auto rhs_idx   = tid % nrhs;\n\n    if (batch_idx * nrhs + rhs_idx >= batches * nrhs)\n        return;\n\n    const auto one_batch_size_a = a_m * a_m;\n    auto       this_A           = A + one_batch_size_a * batch_idx;\n\n    const auto one_batch_size_b = M * N;\n    // Find the start pointer of the RHS for the current batch of B\n    const auto b_rhs_start = ((TRSM::side == cusolverdx::side::left) == (TRSM::b_arrangement == cusolverdx::arrangement::col_major)) ? a_m : 1;\n    const auto b_rhs_stride = ((TRSM::side == cusolverdx::side::left) == (TRSM::b_arrangement == cusolverdx::arrangement::col_major)) ? 1 : nrhs;\n    auto       this_B        = B + one_batch_size_b * batch_idx + rhs_idx * b_rhs_start;\n\n    TRSM().execute(this_A, a_m, this_B, b_rhs_stride);\n}\n\ntemplate<int Arch>\nint trsm_thread_execution() {\n    using TRSM           = decltype(TRSM_base() + SM<Arch>());\n    using data_type      = typename TRSM::a_data_type;\n    using cuda_data_type = typename TRSM::a_cuda_data_type;\n\n    const auto     batches          = 300;\n    constexpr auto a_m              = TRSM::side == side::left ? M : N;\n    constexpr auto nrhs             = TRSM::side == side::left ? N : M;\n    const auto     one_batch_size_A = a_m * a_m;\n    const auto     one_batch_size_B = M * N;\n    constexpr bool is_col_maj_a     = TRSM::a_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_b     = TRSM::b_arrangement == arrangement::col_major;\n    constexpr auto ldb              = is_col_maj_b ? M : N;\n\n    printf(\"TRSM Size M = %d, N = %d, %s side\\n\", TRSM::m_size, TRSM::n_size, (TRSM::side == side::left) ? \"left\" : \"right\");\n    printf(\"matrix A size is %dx%d, matrix B size is %dx%d, total batches = %d\\n\", a_m, a_m, M, N, batches);\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * batches);\n    std::vector<data_type> B(one_batch_size_B * batches);\n    std::vector<data_type> X(one_batch_size_B * batches);\n\n    // Fill A with a triangular matrix\n    common::fillup_random_matrix<data_type>(is_col_maj_a, a_m, a_m, A.data(), a_m, false /* symm */, true /* diag_dom */, 1, 2, batches);\n\n    // Fill B with random values\n    common::fillup_random_matrix<data_type>(is_col_maj_b, M, N, B.data(), ldb, false, false, -2, 1, batches);\n\n    data_type* d_A = nullptr; /* device copy of A */\n    data_type* d_B = nullptr; /* device copy of B */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    const auto nthreads = 32;\n    const auto nblocks  = (batches * nrhs + nthreads - 1) / nthreads; // each thread solvers one RHS of a batch of B\n    printf(\"Launching kernel with GridSize = %d, BlockSize= %d -> total # of threads = %d\\n\", nblocks, nthreads, nblocks * nthreads);\n    printf(\"# of batches x # of RHSs = %d x %d = %d\\n\", batches, nrhs, batches * nrhs);\n\n    trsm_kernel<TRSM><<<nblocks, nthreads, 0, stream>>>(d_A, d_B, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // // Uncomment below to print the results after cuSolverDx execute\n    // // print A\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, a_m, a_m, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"=====\\n\");\n    // // print B\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n    // printf(\"after cuSolverDx execute\\n\");\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(X.data(), batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // Reference using cuBLAS trsm\n    //=======================================================\n    common::reference_cublas_trsm<data_type, cuda_data_type, false /* check_blas_trsm_perf */>(A,\n                                                                                               B,\n                                                                                               M,\n                                                                                               N,\n                                                                                               batches,\n                                                                                               side_of_v<TRSM> == side::left,\n                                                                                               fill_mode_of_v<TRSM> == fill_mode::lower,\n                                                                                               diag_of_v<TRSM> == diag::unit,\n                                                                                               (transpose_mode_of_v<TRSM> == transpose::non_transposed) ? false : true,\n                                                                                               is_col_maj_a,\n                                                                                               is_col_maj_b,\n                                                                                               batches);\n\n    // printf(\"cuBlas TRSM referece results\\n\");\n    // common::print_matrix<data_type, M, N, ldb, is_col_maj_b>(B.data(), batches);\n    // printf(\"=====\\n\");\n\n    auto total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"TRSM: relative error between cuSolverDx and reference results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to reference Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to reference Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct trsm_thread_execution_functor {\n    int operator()() { return trsm_thread_execution<Arch>(); }\n};\n\nint main() { return common::run_example_with_sm<trsm_thread_execution_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/07_BLAS/trsm_batched_thread_advanced.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gesvd.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values, and optionally singular vectors, of a batched m x n general matrix A with Block execution.\n// The singular value results are compared with the reference values obtained with cuSolver host API, cusolverDnXgesvdjBatched and cusolverDnXgesvd API.\n// The singular vector results are verified by comparing A with U * \\Sigma * VT, where U and VT are the left and right singular vectors of A.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, PrecisionType* sigma, DataType* U, const int ldu_gmem, DataType* VT, const int ldvt_gmem, typename Solver::status_type* info, const unsigned batches) {\n    using namespace cusolverdx;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto min_mn = m >= n ? n : m;\n    \n    const auto     one_batch_size_a_gmem = (Solver::a_arrangement == col_major ? lda_gmem * n : m * lda_gmem);\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (Solver::a_arrangement == col_major ? lda_smem * n : m * lda_smem);\n\n    constexpr auto m_u  = m;\n    constexpr auto n_u  = Solver::jobu == job::all_vectors ? m : min_mn;\n    constexpr auto m_vt = Solver::jobvt == job::all_vectors ? n : min_mn;\n    constexpr auto n_vt = n;\n\n    const auto one_batch_size_u_gmem  = (Solver::jobu == job::no_vectors || Solver::jobu == job::overwrite_vectors)\n                                            ? 0\n                                            : (Solver::b_arrangement == col_major ? ldu_gmem * n_u : n_u * ldu_gmem);\n    const auto one_batch_size_vt_gmem = (Solver::jobvt == job::no_vectors || Solver::jobvt == job::overwrite_vectors)\n                                            ? 0\n                                            : (Solver::c_arrangement == col_major ? ldvt_gmem * n_vt : n_vt * ldvt_gmem);\n\n    constexpr auto ldu_smem               = (Solver::jobu == job::no_vectors) ? 0 : Solver::ldb;\n    constexpr auto ldvt_smem              = (Solver::jobvt == job::no_vectors) ? 0 : Solver::ldc;\n    constexpr auto one_batch_size_u_smem  = (Solver::b_arrangement == col_major ? ldu_smem * n_u : m_u * ldu_smem);\n    constexpr auto one_batch_size_vt_smem = (Solver::c_arrangement == col_major ? ldvt_smem * n_vt : m_vt * ldvt_smem);\n\n    constexpr auto workspace_size = Solver::workspace_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [A_s, sigma_s, U_s, VT_s, workspace_s] = cusolverdx::shared_memory::slice<DataType, PrecisionType, DataType, DataType, DataType>(\n        shared_mem,\n        alignof(DataType),\n        one_batch_size_a_smem * BatchesPerBlock,\n        alignof(PrecisionType),\n        min_mn * BatchesPerBlock,\n        alignof(DataType),\n        one_batch_size_u_smem * BatchesPerBlock,\n        alignof(DataType),\n        one_batch_size_vt_smem * BatchesPerBlock,\n        alignof(DataType) // the size can be omitted for the last pointer\n    );\n\n    auto A_g     = A + one_batch_size_a_gmem * batch_idx;\n    auto sigma_g = sigma + min_mn * batch_idx;\n    auto U_g     = U + one_batch_size_u_gmem * batch_idx;\n    auto VT_g    = VT + one_batch_size_vt_gmem * batch_idx;\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(A_g, lda_gmem, A_s, lda_smem);\n\n    Solver().execute(A_s, lda_smem, sigma_s, U_s, ldu_smem, VT_s, ldvt_smem, workspace_s, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(A_s, lda_smem, A_g, lda_gmem);\n    cusolverdx::copy_2d<Solver, min_mn, 1, cusolverdx::arrangement::col_major, BatchesPerBlock, PrecisionType>(sigma_s, min_mn, sigma_g, min_mn);\n\n    if constexpr (Solver::jobu == cusolverdx::job::all_vectors || Solver::jobu == cusolverdx::job::some_vectors) {\n        cusolverdx::copy_2d<Solver, m_u, n_u, Solver::b_arrangement, BatchesPerBlock, DataType>(U_s, ldu_smem, U_g, ldu_gmem);\n    } else if constexpr (Solver::jobu == cusolverdx::job::overwrite_vectors) {\n        cusolverdx::copy_2d<Solver, m, n, Solver::a_arrangement, BatchesPerBlock, DataType>(A_s, lda_smem, A_g, lda_gmem);\n    }\n    if constexpr (Solver::jobvt == cusolverdx::job::all_vectors || Solver::jobvt == cusolverdx::job::some_vectors) {\n        cusolverdx::copy_2d<Solver, m_vt, n_vt, Solver::c_arrangement, BatchesPerBlock, DataType>(VT_s, ldvt_smem, VT_g, ldvt_gmem);\n    } else if constexpr (Solver::jobvt == cusolverdx::job::overwrite_vectors) {\n        cusolverdx::copy_2d<Solver, m, n, Solver::a_arrangement, BatchesPerBlock, DataType>(A_s, lda_smem, A_g, lda_gmem);\n    }\n}\n\ntemplate<int Arch>\nint gesvd_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<14, 7>() + Precision<float>() + Type<type::complex>() + Function<gesvd>() + SM<Arch>() + Block());\n\n    // Job option for gesvd can be no_vectors, all_vectors, some_vectors or overwrite_vectors for either left or right singular vectors\n    // Uses can test the following options and change the job/arrangement/leading dimensions as needed\n    // using Base_with_jobs = decltype(Base() + Job<job::no_vectors, job::no_vectors>() + Arrangement<row_major>()); // compute singular values only\n    using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<col_major, col_major, col_major>()); \n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>());\n    // using Base_with_jobs = decltype(Base() + Job<job::overwrite_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>()); \n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::some_vectors>() + Arrangement<col_major, row_major, row_major>());\n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::overwrite_vectors>() + Arrangement<row_major, col_major, row_major>()); \n    //using Base_with_jobs = decltype(Base() + Job<job::overwrite_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>()); \n\n    using Solver = decltype(Base_with_jobs() + BatchesPerBlock<Base_with_jobs::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n    \n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n    \n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto min_mn = m >= n ? n : m;\n    \n    constexpr bool is_col_maj_a = Solver::a_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_u = Solver::b_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_vt = Solver::c_arrangement == arrangement::col_major;\n    constexpr bool compute_vectors_u = Solver::jobu != job::no_vectors;\n    constexpr bool compute_vectors_vt = Solver::jobvt != job::no_vectors;\n    \n    // Size of A, U, VT in global memory. U and VT storage depends on the job configuration.\n    // jobu = job::all_vectors: U(m x m)\n    // jobu = job::some_vectors: U(m x min_mn)\n    // jobu = job::overwrite_vectors: U(m x min_mn) using Arrangement and Leading Dimension specified for A\n    // jobvt = job::all_vectors: VT(n x n)\n    // jobvt = job::some_vectors: VT(min_mn x n)\n    // jobvt = job::overwrite_vectors: VT(min_mn x n) using Arrangement and Leading Dimension specified for A\n    constexpr auto input_size_a = m * n;\n    constexpr auto lda     = is_col_maj_a ? m : n;\n    \n    constexpr auto act_m_u = m;\n    constexpr auto act_n_u = Solver::jobu == job::all_vectors ? m : min_mn;\n    constexpr auto act_m_vt = Solver::jobvt == job::all_vectors ? n : min_mn;\n    constexpr auto act_n_vt = n;    \n    constexpr auto ldu        = (is_col_maj_u ? act_m_u : act_n_u);\n    constexpr auto ldvt       = (is_col_maj_vt ? act_m_vt : act_n_vt);\n    constexpr auto input_size_u = compute_vectors_u ? act_m_u * act_n_u : 0;\n    constexpr auto input_size_vt = compute_vectors_vt ? act_m_vt * act_n_vt : 0;\n    \n    const auto batches        = 10;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    std::cout << \"Solver: \" << Solver::lda << \", \" << Solver::ldb << \", \" << Solver::ldc << std::endl;\n    std::cout << \"actual m_u = \" << act_m_u << \", actual n_u = \" << act_n_u << \", actual m_vt = \" << act_m_vt << \", actual n_vt = \" << act_n_vt << std::endl;\n    std::cout << \"input_size_a = \" << input_size_a << \", input_size_u = \" << input_size_u << \", input_size_vt = \" << input_size_vt << std::endl;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A_input(input_size_a * padded_batches), A_ref(input_size_a * padded_batches);\n    std::vector<data_type> U(input_size_u * padded_batches), U_ref(input_size_u * padded_batches);\n    std::vector<data_type> VT(input_size_vt * padded_batches), VT_ref(input_size_vt * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A_input.data(), lda, false /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n    A_ref.assign(A_input.begin(), A_input.end());\n\n    std::vector<data_type> A_overwrite;\n    if constexpr (Solver::jobu == job::overwrite_vectors || Solver::jobvt == job::overwrite_vectors) {\n        A_overwrite.resize(input_size_a * padded_batches);\n    }\n\n    std::vector<precision_type> S(min_mn * padded_batches, 0), S_ref(min_mn * padded_batches, 0);\n    std::vector<data_type>      workspace(Solver::workspace_size * padded_batches);\n    std::vector<int>            info(padded_batches, 0);\n    data_type*                  d_A    = nullptr;\n    data_type*                  d_U    = nullptr;\n    data_type*                  d_VT   = nullptr;\n    precision_type*             d_S    = nullptr;\n    int*                        d_info = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A_input.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_U), sizeof(data_type) * U.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_VT), sizeof(data_type) * VT.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_S), sizeof(precision_type) * S.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A_input.data(), sizeof(data_type) * A_input.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_S, d_U, ldu, d_VT, ldvt, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(S.data(), d_S, sizeof(precision_type) * S.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    if constexpr (Solver::jobu == job::all_vectors || Solver::jobu == job::some_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(U.data(), d_U, sizeof(data_type) * U.size(), cudaMemcpyDeviceToHost, stream));\n    } else if constexpr (Solver::jobu == job::overwrite_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(A_overwrite.data(), d_A, sizeof(data_type) * A_overwrite.size(), cudaMemcpyDeviceToHost, stream));\n    }\n    if constexpr (Solver::jobvt == job::all_vectors || Solver::jobvt == job::some_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(VT.data(), d_VT, sizeof(data_type) * VT.size(), cudaMemcpyDeviceToHost, stream));\n    } else if constexpr (Solver::jobvt == job::overwrite_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(A_overwrite.data(), d_A, sizeof(data_type) * A_overwrite.size(), cudaMemcpyDeviceToHost, stream));\n    }\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx GESVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"===== After cuSolverDx execute\\n\");\n    // printf(\"S = \\n\");\n    // common::print_matrix<precision_type, min_mn, 1, min_mn, true>(S.data(), batches);\n    // if constexpr (compute_vectors_u && Solver::jobu != job::overwrite_vectors) {  \n    //     printf(\"=====\\n\");\n    //     printf(\"U = \\n\");\n    //     common::print_matrix<data_type, act_m_u, act_n_u, ldu, is_col_maj_u>(U.data(), batches);\n    // }\n    // if constexpr (compute_vectors_u && Solver::jobu == job::overwrite_vectors) {  \n    //     printf(\"=====\\n\");\n    //     printf(\"A_overwrite = \\n\");\n    //     common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_overwrite.data(), batches);\n    // }\n    // if constexpr (compute_vectors_vt && Solver::jobvt != job::overwrite_vectors) {\n    //     printf(\"=====\\n\");\n    //     printf(\"VT = \\n\");\n    //     common::print_matrix<data_type, act_m_vt, act_n_vt, ldvt, is_col_maj_vt>(VT.data(), batches);\n    //     printf(\"=====\\n\");\n    // }\n    // if constexpr (compute_vectors_vt && Solver::jobvt == job::overwrite_vectors) {\n    //     printf(\"=====\\n\");\n    //     printf(\"A_overwrite = \\n\");\n    //     common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_overwrite.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_S));\n    if (d_U != nullptr) {\n        CUDA_CHECK_AND_EXIT(cudaFree(d_U));\n    }\n    if (d_VT != nullptr) {\n        CUDA_CHECK_AND_EXIT(cudaFree(d_VT));\n    }\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //=========================================================\n    // Check computed singular values with cuSolver reference\n    //=========================================================\n    std::cout << \"The largest singular value is \" << S[0] << \" and the smallest singular value is \" << S[min_mn - 1] << std::endl;\n    if constexpr (m >= n) {\n        signed char jobu = 'N', jobvt = 'N';\n        if constexpr (Solver::jobu == job::all_vectors) {\n            jobu = 'A';\n        } else if constexpr (Solver::jobu == job::some_vectors) {\n            jobu = 'S';\n        } else if constexpr (Solver::jobu == job::overwrite_vectors) {\n            jobu = 'O';\n        }\n        if constexpr (Solver::jobvt == job::all_vectors) {\n            jobvt = 'A';\n        } else if constexpr (Solver::jobvt == job::some_vectors) {\n            jobvt = 'S';\n        } else if constexpr (Solver::jobvt == job::overwrite_vectors) {\n            jobvt = 'O';\n        }\n        // All the output A/U/VT matrices remain in col-major format after return of the function\n        common::reference_cusolver_gesvd<data_type, cuda_data_type, precision_type, false /*check_perf*/>(\n                A_ref, S_ref, U_ref, VT_ref, jobu, jobvt, info.data(), m, n, padded_batches, is_col_maj_a, batches);\n\n        const auto total_relative_error_S = common::check_error<precision_type, precision_type>(S.data(), S_ref.data(), batches * min_mn);\n        std::cout << \"GESVD: relative error of S between cuSolverDx and cuSolver reference results: \" << total_relative_error_S << std::endl;\n\n        // Uncomment below to print results after cuSolver reference execute\n        // printf(\"S_ref = \\n\");\n        // common::print_matrix<precision_type, min_mn, 1, min_mn, true>(S_ref.data(), batches);\n        // if constexpr (compute_vectors_u) {\n        //     printf(\"=====\\n\");\n        //     if constexpr (Solver::jobu == job::overwrite_vectors) {\n        //         printf(\"A_overwrite_ref = \\n\");\n        //         common::print_matrix<data_type, m, n, m, true>(A_ref.data(), batches);\n        //     } else {\n        //         printf(\"U_ref = \\n\");\n        //         common::print_matrix<data_type, act_m_u, act_n_u, act_m_u, true>(U_ref.data(), batches);\n        //     }\n        // }\n        // if constexpr (compute_vectors_vt) {\n        //     if constexpr (Solver::jobvt == job::overwrite_vectors) {\n        //         printf(\"A_overwrite_ref = \\n\");\n        //         common::print_matrix<data_type, m, n, m, true>(A_ref.data(), batches);\n        //     } else {\n        //         printf(\"VT_ref = \\n\");\n        //         common::print_matrix<data_type, act_m_vt, act_n_vt, act_m_vt, true>(VT_ref.data(), batches);\n        //     }\n        // }   \n\n        if (!common::is_error_acceptable<precision_type>(total_relative_error_S)) {\n            std::cout << \"Failure compared with cuSolver API results S\" << std::endl;\n            for (int i = 0; i < min_mn * batches; ++i) {\n                if (abs(S[i] - S_ref[i]) > 1e-05) {\n                    printf(\"S[%d] = %10.3f, S_ref[%d] = %10.3f  differ \\n\", i, S[i], i, S_ref[i]);\n                }\n            }\n            return 1;\n        }\n        std::cout << \"Success compared singular values with the cuSolverDn reference results\" << std::endl;\n    } else {\n        std::cout << \"GESVD: m < n is not supported in cuSolverDn<t>getsvd API. Checking with cuSolverDn reference is disabled\"\n                  << std::endl;\n    }\n\n    //============================================================\n    // Check computed left and right singular vectors by comparing:\n    //     A_input = A_reconstructed = U * \\Sigma * VT\n    //============================================================\n    if constexpr (compute_vectors_u && compute_vectors_vt) {\n        // A/U/VT could be either col-major or row-major. For convenience of testing, transpose them if they are row-major\n        if constexpr (!is_col_maj_a) {\n            common::transpose_matrix<data_type>(A_input, m, n, batches);\n        }\n        if constexpr (!is_col_maj_u && Solver::jobu != job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(U, act_m_u, act_n_u, batches);\n        }\n        if constexpr (!is_col_maj_a && Solver::jobu == job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(A_overwrite, m, n, batches);\n        }\n        if constexpr ((!is_col_maj_vt && Solver::jobvt != job::overwrite_vectors)) {\n            common::transpose_matrix<data_type>(VT, act_m_vt, act_n_vt, batches);\n        }\n        if constexpr (!is_col_maj_a && Solver::jobvt == job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(A_overwrite, m, n, batches);\n        }\n\n        const bool check_ref = false;\n        data_type *AA, *UU, *VVT;\n        if (check_ref) {\n            AA = A_ref.data();\n            UU = U_ref.data();\n            VVT = VT_ref.data();\n        } else {\n            AA = A_overwrite.data();\n            UU = U.data();\n            VVT = VT.data();\n        }\n        std::vector<data_type> A_reconstructed(input_size_a);\n        for (int b = 0; b < batches; ++b) {\n            // output sigma is stored in d\n            const auto* sigma_b   = S.data() + b * min_mn;\n            const auto* U_b       = Solver::jobu == job::overwrite_vectors ? AA + b * input_size_a : UU + b * input_size_u;\n            const auto* VT_b      = Solver::jobvt == job::overwrite_vectors ? AA + b * input_size_a : VVT + b * input_size_vt;\n            const auto  ld_u      = Solver::jobu == job::overwrite_vectors ? m : act_m_u;\n            const auto  ld_vt     = Solver::jobvt == job::overwrite_vectors ? m : act_m_vt;\n            const auto* A_input_b = A_input.data() + b * input_size_a;\n\n            // Compare A_input and A_reconstructed = U * \\Sigma * VT\n            A_reconstructed.assign(m * n, common::convert<data_type, float>(0.f));\n            for (int i = 0; i < m; ++i) {\n                for (int j = 0; j < n; ++j) {\n                    for (int l = 0; l < min_mn; ++l) {\n                        A_reconstructed[i + j * m] += common::convert<data_type, precision_type>(sigma_b[l]) * U_b[i + l * ld_u] * VT_b[l + j * ld_vt];\n                    }\n                }\n            }\n            const auto total_relative_error = common::check_error<data_type, data_type>(A_reconstructed.data(), A_input_b, input_size_a);\n            std::cout << \"GESVD: relative error of A - U Sigma VT: for batch \" << b << \" : \" << total_relative_error << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n                printf(\"A_reconstructed = \\n\");\n                common::print_matrix(m, n, true, A_reconstructed.data(), m);\n                printf(\"A_input = \\n\");\n                common::print_matrix(m, n, true, A_input_b, m);\n                std::cout << \"Relative error of A - U Sigma VT is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n       std::cout << \"Success in verifying singular values and vectors for all batches for GESVD\" << std::endl;\n\n\n    } else {\n        std::cout << \"Success \" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesvd_batched_block_functor {\n    int operator()() { return gesvd_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesvd_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gesvd.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values, and optionally singular vectors, of a batched m x n general matrix A with Block execution.\n// The singular value results are compared with the reference values obtained with cuSolver host API, cusolverDnXgesvdjBatched and cusolverDnXgesvd API.\n// The singular vector results are verified by comparing A with U * \\Sigma * VT, where U and VT are the left and right singular vectors of A.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, PrecisionType* sigma, DataType* U, const int ldu_gmem, DataType* VT, const int ldvt_gmem, typename Solver::status_type* info, const unsigned batches) {\n    using namespace cusolverdx;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto min_mn = m >= n ? n : m;\n    \n    const auto     one_batch_size_a_gmem = (Solver::a_arrangement == col_major ? lda_gmem * n : m * lda_gmem);\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (Solver::a_arrangement == col_major ? lda_smem * n : m * lda_smem);\n\n    constexpr auto m_u  = m;\n    constexpr auto n_u  = Solver::jobu == job::all_vectors ? m : min_mn;\n    constexpr auto m_vt = Solver::jobvt == job::all_vectors ? n : min_mn;\n    constexpr auto n_vt = n;\n\n    const auto one_batch_size_u_gmem  = (Solver::jobu == job::no_vectors || Solver::jobu == job::overwrite_vectors)\n                                            ? 0\n                                            : (Solver::b_arrangement == col_major ? ldu_gmem * n_u : n_u * ldu_gmem);\n    const auto one_batch_size_vt_gmem = (Solver::jobvt == job::no_vectors || Solver::jobvt == job::overwrite_vectors)\n                                            ? 0\n                                            : (Solver::c_arrangement == col_major ? ldvt_gmem * n_vt : n_vt * ldvt_gmem);\n\n    constexpr auto ldu_smem               = (Solver::jobu == job::no_vectors) ? 0 : Solver::ldb;\n    constexpr auto ldvt_smem              = (Solver::jobvt == job::no_vectors) ? 0 : Solver::ldc;\n    constexpr auto one_batch_size_u_smem  = (Solver::b_arrangement == col_major ? ldu_smem * n_u : m_u * ldu_smem);\n    constexpr auto one_batch_size_vt_smem = (Solver::c_arrangement == col_major ? ldvt_smem * n_vt : m_vt * ldvt_smem);\n\n    constexpr auto workspace_size = Solver::workspace_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [A_s, sigma_s, U_s, VT_s, workspace_s] = cusolverdx::shared_memory::slice<DataType, PrecisionType, DataType, DataType, DataType>(\n        shared_mem,\n        alignof(DataType),\n        one_batch_size_a_smem * BatchesPerBlock,\n        alignof(PrecisionType),\n        min_mn * BatchesPerBlock,\n        alignof(DataType),\n        one_batch_size_u_smem * BatchesPerBlock,\n        alignof(DataType),\n        one_batch_size_vt_smem * BatchesPerBlock,\n        alignof(DataType) // the size can be omitted for the last pointer\n    );\n\n    auto A_g     = A + one_batch_size_a_gmem * batch_idx;\n    auto sigma_g = sigma + min_mn * batch_idx;\n    auto U_g     = U + one_batch_size_u_gmem * batch_idx;\n    auto VT_g    = VT + one_batch_size_vt_gmem * batch_idx;\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(A_g, lda_gmem, A_s, lda_smem);\n\n    Solver().execute(A_s, lda_smem, sigma_s, U_s, ldu_smem, VT_s, ldvt_smem, workspace_s, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(A_s, lda_smem, A_g, lda_gmem);\n    cusolverdx::copy_2d<Solver, min_mn, 1, cusolverdx::arrangement::col_major, BatchesPerBlock, PrecisionType>(sigma_s, min_mn, sigma_g, min_mn);\n\n    if constexpr (Solver::jobu == cusolverdx::job::all_vectors || Solver::jobu == cusolverdx::job::some_vectors) {\n        cusolverdx::copy_2d<Solver, m_u, n_u, Solver::b_arrangement, BatchesPerBlock, DataType>(U_s, ldu_smem, U_g, ldu_gmem);\n    } else if constexpr (Solver::jobu == cusolverdx::job::overwrite_vectors) {\n        cusolverdx::copy_2d<Solver, m, n, Solver::a_arrangement, BatchesPerBlock, DataType>(A_s, lda_smem, A_g, lda_gmem);\n    }\n    if constexpr (Solver::jobvt == cusolverdx::job::all_vectors || Solver::jobvt == cusolverdx::job::some_vectors) {\n        cusolverdx::copy_2d<Solver, m_vt, n_vt, Solver::c_arrangement, BatchesPerBlock, DataType>(VT_s, ldvt_smem, VT_g, ldvt_gmem);\n    } else if constexpr (Solver::jobvt == cusolverdx::job::overwrite_vectors) {\n        cusolverdx::copy_2d<Solver, m, n, Solver::a_arrangement, BatchesPerBlock, DataType>(A_s, lda_smem, A_g, lda_gmem);\n    }\n}\n\ntemplate<int Arch>\nint gesvd_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<14, 7>() + Precision<float>() + Type<type::complex>() + Function<gesvd>() + SM<Arch>() + Block());\n\n    // Job option for gesvd can be no_vectors, all_vectors, some_vectors or overwrite_vectors for either left or right singular vectors\n    // Uses can test the following options and change the job/arrangement/leading dimensions as needed\n    // using Base_with_jobs = decltype(Base() + Job<job::no_vectors, job::no_vectors>() + Arrangement<row_major>()); // compute singular values only\n    using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<col_major, col_major, col_major>()); \n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>());\n    // using Base_with_jobs = decltype(Base() + Job<job::overwrite_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>()); \n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::some_vectors>() + Arrangement<col_major, row_major, row_major>());\n    // using Base_with_jobs = decltype(Base() + Job<job::all_vectors, job::overwrite_vectors>() + Arrangement<row_major, col_major, row_major>()); \n    //using Base_with_jobs = decltype(Base() + Job<job::overwrite_vectors, job::all_vectors>() + Arrangement<row_major, row_major, col_major>()); \n\n    using Solver = decltype(Base_with_jobs() + BatchesPerBlock<Base_with_jobs::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n    \n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n    \n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    constexpr auto min_mn = m >= n ? n : m;\n    \n    constexpr bool is_col_maj_a = Solver::a_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_u = Solver::b_arrangement == arrangement::col_major;\n    constexpr bool is_col_maj_vt = Solver::c_arrangement == arrangement::col_major;\n    constexpr bool compute_vectors_u = Solver::jobu != job::no_vectors;\n    constexpr bool compute_vectors_vt = Solver::jobvt != job::no_vectors;\n    \n    // Size of A, U, VT in global memory. U and VT storage depends on the job configuration.\n    // jobu = job::all_vectors: U(m x m)\n    // jobu = job::some_vectors: U(m x min_mn)\n    // jobu = job::overwrite_vectors: U(m x min_mn) using Arrangement and Leading Dimension specified for A\n    // jobvt = job::all_vectors: VT(n x n)\n    // jobvt = job::some_vectors: VT(min_mn x n)\n    // jobvt = job::overwrite_vectors: VT(min_mn x n) using Arrangement and Leading Dimension specified for A\n    constexpr auto input_size_a = m * n;\n    constexpr auto lda     = is_col_maj_a ? m : n;\n    \n    constexpr auto act_m_u = m;\n    constexpr auto act_n_u = Solver::jobu == job::all_vectors ? m : min_mn;\n    constexpr auto act_m_vt = Solver::jobvt == job::all_vectors ? n : min_mn;\n    constexpr auto act_n_vt = n;    \n    constexpr auto ldu        = (is_col_maj_u ? act_m_u : act_n_u);\n    constexpr auto ldvt       = (is_col_maj_vt ? act_m_vt : act_n_vt);\n    constexpr auto input_size_u = compute_vectors_u ? act_m_u * act_n_u : 0;\n    constexpr auto input_size_vt = compute_vectors_vt ? act_m_vt * act_n_vt : 0;\n    \n    const auto batches        = 10;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    std::cout << \"Solver: \" << Solver::lda << \", \" << Solver::ldb << \", \" << Solver::ldc << std::endl;\n    std::cout << \"actual m_u = \" << act_m_u << \", actual n_u = \" << act_n_u << \", actual m_vt = \" << act_m_vt << \", actual n_vt = \" << act_n_vt << std::endl;\n    std::cout << \"input_size_a = \" << input_size_a << \", input_size_u = \" << input_size_u << \", input_size_vt = \" << input_size_vt << std::endl;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A_input(input_size_a * padded_batches), A_ref(input_size_a * padded_batches);\n    std::vector<data_type> U(input_size_u * padded_batches), U_ref(input_size_u * padded_batches);\n    std::vector<data_type> VT(input_size_vt * padded_batches), VT_ref(input_size_vt * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A_input.data(), lda, false /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n    A_ref.assign(A_input.begin(), A_input.end());\n\n    std::vector<data_type> A_overwrite;\n    if constexpr (Solver::jobu == job::overwrite_vectors || Solver::jobvt == job::overwrite_vectors) {\n        A_overwrite.resize(input_size_a * padded_batches);\n    }\n\n    std::vector<precision_type> S(min_mn * padded_batches, 0), S_ref(min_mn * padded_batches, 0);\n    std::vector<data_type>      workspace(Solver::workspace_size * padded_batches);\n    std::vector<int>            info(padded_batches, 0);\n    data_type*                  d_A    = nullptr;\n    data_type*                  d_U    = nullptr;\n    data_type*                  d_VT   = nullptr;\n    precision_type*             d_S    = nullptr;\n    int*                        d_info = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A_input.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_U), sizeof(data_type) * U.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_VT), sizeof(data_type) * VT.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_S), sizeof(precision_type) * S.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A_input.data(), sizeof(data_type) * A_input.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_S, d_U, ldu, d_VT, ldvt, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(S.data(), d_S, sizeof(precision_type) * S.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    if constexpr (Solver::jobu == job::all_vectors || Solver::jobu == job::some_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(U.data(), d_U, sizeof(data_type) * U.size(), hipMemcpyDeviceToHost, stream));\n    } else if constexpr (Solver::jobu == job::overwrite_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(A_overwrite.data(), d_A, sizeof(data_type) * A_overwrite.size(), hipMemcpyDeviceToHost, stream));\n    }\n    if constexpr (Solver::jobvt == job::all_vectors || Solver::jobvt == job::some_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(VT.data(), d_VT, sizeof(data_type) * VT.size(), hipMemcpyDeviceToHost, stream));\n    } else if constexpr (Solver::jobvt == job::overwrite_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(A_overwrite.data(), d_A, sizeof(data_type) * A_overwrite.size(), hipMemcpyDeviceToHost, stream));\n    }\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx GESVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"===== After cuSolverDx execute\\n\");\n    // printf(\"S = \\n\");\n    // common::print_matrix<precision_type, min_mn, 1, min_mn, true>(S.data(), batches);\n    // if constexpr (compute_vectors_u && Solver::jobu != job::overwrite_vectors) {  \n    //     printf(\"=====\\n\");\n    //     printf(\"U = \\n\");\n    //     common::print_matrix<data_type, act_m_u, act_n_u, ldu, is_col_maj_u>(U.data(), batches);\n    // }\n    // if constexpr (compute_vectors_u && Solver::jobu == job::overwrite_vectors) {  \n    //     printf(\"=====\\n\");\n    //     printf(\"A_overwrite = \\n\");\n    //     common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_overwrite.data(), batches);\n    // }\n    // if constexpr (compute_vectors_vt && Solver::jobvt != job::overwrite_vectors) {\n    //     printf(\"=====\\n\");\n    //     printf(\"VT = \\n\");\n    //     common::print_matrix<data_type, act_m_vt, act_n_vt, ldvt, is_col_maj_vt>(VT.data(), batches);\n    //     printf(\"=====\\n\");\n    // }\n    // if constexpr (compute_vectors_vt && Solver::jobvt == job::overwrite_vectors) {\n    //     printf(\"=====\\n\");\n    //     printf(\"A_overwrite = \\n\");\n    //     common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A_overwrite.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_S));\n    if (d_U != nullptr) {\n        CUDA_CHECK_AND_EXIT(hipFree(d_U));\n    }\n    if (d_VT != nullptr) {\n        CUDA_CHECK_AND_EXIT(hipFree(d_VT));\n    }\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //=========================================================\n    // Check computed singular values with cuSolver reference\n    //=========================================================\n    std::cout << \"The largest singular value is \" << S[0] << \" and the smallest singular value is \" << S[min_mn - 1] << std::endl;\n    if constexpr (m >= n) {\n        signed char jobu = 'N', jobvt = 'N';\n        if constexpr (Solver::jobu == job::all_vectors) {\n            jobu = 'A';\n        } else if constexpr (Solver::jobu == job::some_vectors) {\n            jobu = 'S';\n        } else if constexpr (Solver::jobu == job::overwrite_vectors) {\n            jobu = 'O';\n        }\n        if constexpr (Solver::jobvt == job::all_vectors) {\n            jobvt = 'A';\n        } else if constexpr (Solver::jobvt == job::some_vectors) {\n            jobvt = 'S';\n        } else if constexpr (Solver::jobvt == job::overwrite_vectors) {\n            jobvt = 'O';\n        }\n        // All the output A/U/VT matrices remain in col-major format after return of the function\n        common::reference_cusolver_gesvd<data_type, cuda_data_type, precision_type, false /*check_perf*/>(\n                A_ref, S_ref, U_ref, VT_ref, jobu, jobvt, info.data(), m, n, padded_batches, is_col_maj_a, batches);\n\n        const auto total_relative_error_S = common::check_error<precision_type, precision_type>(S.data(), S_ref.data(), batches * min_mn);\n        std::cout << \"GESVD: relative error of S between cuSolverDx and cuSolver reference results: \" << total_relative_error_S << std::endl;\n\n        // Uncomment below to print results after cuSolver reference execute\n        // printf(\"S_ref = \\n\");\n        // common::print_matrix<precision_type, min_mn, 1, min_mn, true>(S_ref.data(), batches);\n        // if constexpr (compute_vectors_u) {\n        //     printf(\"=====\\n\");\n        //     if constexpr (Solver::jobu == job::overwrite_vectors) {\n        //         printf(\"A_overwrite_ref = \\n\");\n        //         common::print_matrix<data_type, m, n, m, true>(A_ref.data(), batches);\n        //     } else {\n        //         printf(\"U_ref = \\n\");\n        //         common::print_matrix<data_type, act_m_u, act_n_u, act_m_u, true>(U_ref.data(), batches);\n        //     }\n        // }\n        // if constexpr (compute_vectors_vt) {\n        //     if constexpr (Solver::jobvt == job::overwrite_vectors) {\n        //         printf(\"A_overwrite_ref = \\n\");\n        //         common::print_matrix<data_type, m, n, m, true>(A_ref.data(), batches);\n        //     } else {\n        //         printf(\"VT_ref = \\n\");\n        //         common::print_matrix<data_type, act_m_vt, act_n_vt, act_m_vt, true>(VT_ref.data(), batches);\n        //     }\n        // }   \n\n        if (!common::is_error_acceptable<precision_type>(total_relative_error_S)) {\n            std::cout << \"Failure compared with cuSolver API results S\" << std::endl;\n            for (int i = 0; i < min_mn * batches; ++i) {\n                if (abs(S[i] - S_ref[i]) > 1e-05) {\n                    printf(\"S[%d] = %10.3f, S_ref[%d] = %10.3f  differ \\n\", i, S[i], i, S_ref[i]);\n                }\n            }\n            return 1;\n        }\n        std::cout << \"Success compared singular values with the cuSolverDn reference results\" << std::endl;\n    } else {\n        std::cout << \"GESVD: m < n is not supported in cuSolverDn<t>getsvd API. Checking with cuSolverDn reference is disabled\"\n                  << std::endl;\n    }\n\n    //============================================================\n    // Check computed left and right singular vectors by comparing:\n    //     A_input = A_reconstructed = U * \\Sigma * VT\n    //============================================================\n    if constexpr (compute_vectors_u && compute_vectors_vt) {\n        // A/U/VT could be either col-major or row-major. For convenience of testing, transpose them if they are row-major\n        if constexpr (!is_col_maj_a) {\n            common::transpose_matrix<data_type>(A_input, m, n, batches);\n        }\n        if constexpr (!is_col_maj_u && Solver::jobu != job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(U, act_m_u, act_n_u, batches);\n        }\n        if constexpr (!is_col_maj_a && Solver::jobu == job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(A_overwrite, m, n, batches);\n        }\n        if constexpr ((!is_col_maj_vt && Solver::jobvt != job::overwrite_vectors)) {\n            common::transpose_matrix<data_type>(VT, act_m_vt, act_n_vt, batches);\n        }\n        if constexpr (!is_col_maj_a && Solver::jobvt == job::overwrite_vectors) {\n            common::transpose_matrix<data_type>(A_overwrite, m, n, batches);\n        }\n\n        const bool check_ref = false;\n        data_type *AA, *UU, *VVT;\n        if (check_ref) {\n            AA = A_ref.data();\n            UU = U_ref.data();\n            VVT = VT_ref.data();\n        } else {\n            AA = A_overwrite.data();\n            UU = U.data();\n            VVT = VT.data();\n        }\n        std::vector<data_type> A_reconstructed(input_size_a);\n        for (int b = 0; b < batches; ++b) {\n            // output sigma is stored in d\n            const auto* sigma_b   = S.data() + b * min_mn;\n            const auto* U_b       = Solver::jobu == job::overwrite_vectors ? AA + b * input_size_a : UU + b * input_size_u;\n            const auto* VT_b      = Solver::jobvt == job::overwrite_vectors ? AA + b * input_size_a : VVT + b * input_size_vt;\n            const auto  ld_u      = Solver::jobu == job::overwrite_vectors ? m : act_m_u;\n            const auto  ld_vt     = Solver::jobvt == job::overwrite_vectors ? m : act_m_vt;\n            const auto* A_input_b = A_input.data() + b * input_size_a;\n\n            // Compare A_input and A_reconstructed = U * \\Sigma * VT\n            A_reconstructed.assign(m * n, common::convert<data_type, float>(0.f));\n            for (int i = 0; i < m; ++i) {\n                for (int j = 0; j < n; ++j) {\n                    for (int l = 0; l < min_mn; ++l) {\n                        A_reconstructed[i + j * m] += common::convert<data_type, precision_type>(sigma_b[l]) * U_b[i + l * ld_u] * VT_b[l + j * ld_vt];\n                    }\n                }\n            }\n            const auto total_relative_error = common::check_error<data_type, data_type>(A_reconstructed.data(), A_input_b, input_size_a);\n            std::cout << \"GESVD: relative error of A - U Sigma VT: for batch \" << b << \" : \" << total_relative_error << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n                printf(\"A_reconstructed = \\n\");\n                common::print_matrix(m, n, true, A_reconstructed.data(), m);\n                printf(\"A_input = \\n\");\n                common::print_matrix(m, n, true, A_input_b, m);\n                std::cout << \"Relative error of A - U Sigma VT is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n       std::cout << \"Success in verifying singular values and vectors for all batches for GESVD\" << std::endl;\n\n\n    } else {\n        std::cout << \"Success \" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesvd_batched_block_functor {\n    int operator()() { return gesvd_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesvd_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/05_Singular_Value_Decomposition/gesvd_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values of a batched bidiagonal matrix A with Thread execution.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* d, DataType* e, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m -1) * batch_idx;\n\n    Solver().execute(this_d, this_e, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint bdsvd_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<25>() + Precision<float>() + Type<type::real>() + Function<bdsvd>() + SM<Arch>() + Job<job::no_vectors>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto e_size = m - 1; // the size of the off-diagonal elements\n\n    const auto batches = 1000;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> d(m * batches), d_input(m * batches);\n    std::vector<data_type> e(e_size * batches), e_input(e_size * batches);\n    // Fill the diagonal and off-diagonal elements of the bidiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, e_size, 1, e_input.data(), e_size, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    int*             d_info = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    //Invokes kernel\n    constexpr auto nthreads = 128;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_d, d_e, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(e.data(), d_e, sizeof(data_type) * e.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx BDSVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_e));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    std::cout << \"The largest singular value is \" << d[0] << \" and the smallest singular value is \" << d[m - 1] << std::endl;\n    std::cout << \"The superdiagonal elements e[0] = \" << e[0] << \" and e[m-2] = \" << e[m - 2] << std::endl;\n    std::cout << \"Success in computing singular values\" << std::endl;\n\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct bdsvd_batched_thread_functor {\n    int operator()() { return bdsvd_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<bdsvd_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values of a batched bidiagonal matrix A with Thread execution.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* d, DataType* e, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m -1) * batch_idx;\n\n    Solver().execute(this_d, this_e, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint bdsvd_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<25>() + Precision<float>() + Type<type::real>() + Function<bdsvd>() + SM<Arch>() + Job<job::no_vectors>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto e_size = m - 1; // the size of the off-diagonal elements\n\n    const auto batches = 1000;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> d(m * batches), d_input(m * batches);\n    std::vector<data_type> e(e_size * batches), e_input(e_size * batches);\n    // Fill the diagonal and off-diagonal elements of the bidiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, e_size, 1, e_input.data(), e_size, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<int> info(batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    int*             d_info = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    //Invokes kernel\n    constexpr auto nthreads = 128;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_d, d_e, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(e.data(), d_e, sizeof(data_type) * e.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx BDSVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_e));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    std::cout << \"The largest singular value is \" << d[0] << \" and the smallest singular value is \" << d[m - 1] << std::endl;\n    std::cout << \"The superdiagonal elements e[0] = \" << e[0] << \" and e[m-2] = \" << e[m - 2] << std::endl;\n    std::cout << \"Success in computing singular values\" << std::endl;\n\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct bdsvd_batched_thread_functor {\n    int operator()() { return bdsvd_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<bdsvd_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/05_Singular_Value_Decomposition/bdsvd_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gesvd.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values of a batched m x n general matrix A with Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXgesvdjBatched and cusolverDnXgesvd API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(DataType* A, PrecisionType* sigma, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto n                     = Solver::n_size;\n    constexpr auto one_batch_size_a_gmem = m * n;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    auto A_g         = A + one_batch_size_a_gmem * batch_idx;\n    auto sigma_g     = sigma + n * batch_idx;\n    auto workspace_g = workspace + workspace_size * batch_idx;\n\n    Solver().execute(A_g, sigma_g, workspace_g, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesvd_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<12, 5>() + Precision<float>() + Type<type::real>() + Function<gesvd>() + Arrangement<arrangement::row_major>() + SM<Arch>() +\n                            Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m >= n, \"m must be greater than or equal to n for gesvd\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    const auto     lda_gmem     = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 500;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda_gmem, false /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<precision_type> S(n * batches, 0);\n    std::vector<precision_type> S_ref(n * batches, 0);\n    std::vector<data_type>      workspace(Solver::workspace_size * batches, 0);\n    std::vector<int>            info(batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_S         = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_S), sizeof(precision_type) * S.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size * batches));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    constexpr auto nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_S, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(S.data(), d_S, sizeof(precision_type) * S.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx GESVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, n, 1, n, true>(S.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_S));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    if constexpr (m >= n) {\n        std::vector<data_type> dummy;\n        common::reference_cusolver_gesvd<data_type, cuda_data_type, precision_type, false /*check_perf*/>(\n                A, S_ref, dummy, dummy, 'N', 'N', info.data(), m, n, batches, is_col_maj_a, batches);\n\n        const auto total_relative_error_S = common::check_error<precision_type, precision_type>(S.data(), S_ref.data(), batches * n);\n        std::cout << \"GESVD: relative error of S between cuSolverDx and cuSolver reference results: \" << total_relative_error_S << std::endl;\n\n        // // Uncomment below to print results after cuSolver reference execute\n        // printf(\"S_ref = \\n\");\n        // common::print_matrix<precision_type, n, 1, n, true>(S_ref.data(), batches);\n        // printf(\"=====\\n\");\n\n        if (!common::is_error_acceptable<precision_type>(total_relative_error_S)) {\n            std::cout << \"Failure compared with cuSolver API results S\" << std::endl;\n            //Print out S for debugging. Do not delete\n            for (int i = 0; i < n * batches; ++i) {\n                if (abs(S[i] - S_ref[i]) / abs(S_ref[i]) > 1e-05) {\n                    printf(\"S[%d] = %10.3f, S_ref[%d] = %10.3f  differ \\n\", i, S[i], i, S_ref[i]);\n                }\n            }\n            return 1;\n        }\n\n        std::cout << \"Success compared singular values with the cuSolverDn reference results\" << std::endl;\n    } else {\n        std::cout << \"GESVD: m < n is not supported in cuSolverDn<t>getsvd API. Checking with cuSolverDn reference is disabled. Successfully executed with \"\n                     \"cuSolverDx GESVD API.\"\n                  << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesvd_batched_thread_functor {\n    int operator()() { return gesvd_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesvd_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_gesvd.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values of a batched m x n general matrix A with Thread execution.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXgesvdjBatched and cusolverDnXgesvd API.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(DataType* A, PrecisionType* sigma, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto n                     = Solver::n_size;\n    constexpr auto one_batch_size_a_gmem = m * n;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    auto A_g         = A + one_batch_size_a_gmem * batch_idx;\n    auto sigma_g     = sigma + n * batch_idx;\n    auto workspace_g = workspace + workspace_size * batch_idx;\n\n    Solver().execute(A_g, sigma_g, workspace_g, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint gesvd_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<12, 5>() + Precision<float>() + Type<type::real>() + Function<gesvd>() + Arrangement<arrangement::row_major>() + SM<Arch>() +\n                            Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n    static_assert(m >= n, \"m must be greater than or equal to n for gesvd\");\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    const auto     lda_gmem     = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches = 500;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda_gmem, false /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<precision_type> S(n * batches, 0);\n    std::vector<precision_type> S_ref(n * batches, 0);\n    std::vector<data_type>      workspace(Solver::workspace_size * batches, 0);\n    std::vector<int>            info(batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_S         = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_S), sizeof(precision_type) * S.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size * batches));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    constexpr auto nthreads = 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_S, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(S.data(), d_S, sizeof(precision_type) * S.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx GESVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, n, 1, n, true>(S.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_S));\n    CUDA_CHECK_AND_EXIT(hipFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    if constexpr (m >= n) {\n        std::vector<data_type> dummy;\n        common::reference_cusolver_gesvd<data_type, cuda_data_type, precision_type, false /*check_perf*/>(\n                A, S_ref, dummy, dummy, 'N', 'N', info.data(), m, n, batches, is_col_maj_a, batches);\n\n        const auto total_relative_error_S = common::check_error<precision_type, precision_type>(S.data(), S_ref.data(), batches * n);\n        std::cout << \"GESVD: relative error of S between cuSolverDx and cuSolver reference results: \" << total_relative_error_S << std::endl;\n\n        // // Uncomment below to print results after cuSolver reference execute\n        // printf(\"S_ref = \\n\");\n        // common::print_matrix<precision_type, n, 1, n, true>(S_ref.data(), batches);\n        // printf(\"=====\\n\");\n\n        if (!common::is_error_acceptable<precision_type>(total_relative_error_S)) {\n            std::cout << \"Failure compared with cuSolver API results S\" << std::endl;\n            //Print out S for debugging. Do not delete\n            for (int i = 0; i < n * batches; ++i) {\n                if (abs(S[i] - S_ref[i]) / abs(S_ref[i]) > 1e-05) {\n                    printf(\"S[%d] = %10.3f, S_ref[%d] = %10.3f  differ \\n\", i, S[i], i, S_ref[i]);\n                }\n            }\n            return 1;\n        }\n\n        std::cout << \"Success compared singular values with the cuSolverDn reference results\" << std::endl;\n    } else {\n        std::cout << \"GESVD: m < n is not supported in cuSolverDn<t>getsvd API. Checking with cuSolverDn reference is disabled. Successfully executed with \"\n                     \"cuSolverDx GESVD API.\"\n                  << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gesvd_batched_thread_functor {\n    int operator()() { return gesvd_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gesvd_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/05_Singular_Value_Decomposition/gesvd_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values, and optionally singular vectors, of a batched bidiagonal matrix A with Block execution.\n// The results are verified by comparing A with U * \\Sigma * VT, where U and VT are the left and right singular vectors of A.\n\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType*                     d,\n                                                                        DataType*                     e,\n                                                                        DataType*                     U,\n                                                                        DataType*                     VT,\n                                                                        typename Solver::status_type* info,\n                                                                        const unsigned                batches) {\n    using namespace cusolverdx;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto e_size = m - 1;\n    constexpr auto ldu = Solver::lda;\n    constexpr auto ldvt = Solver::ldb;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [d_s, e_s, U_s, VT_s] = shared_memory::slice<DataType, DataType, DataType, DataType>(shared_mem,\n                                                                                              alignof(DataType),\n                                                                                              m * BatchesPerBlock,\n                                                                                              alignof(DataType),\n                                                                                              e_size * BatchesPerBlock,\n                                                                                              alignof(DataType),\n                                                                                              ldu * m * BatchesPerBlock,\n                                                                                              alignof(DataType));\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (e_size) * batch_idx;\n    common::io<Solver, BatchesPerBlock>::load_a<m, 1, col_major>(this_d, m, d_s, m);\n    common::io<Solver, BatchesPerBlock>::load_a<e_size, 1, col_major>(this_e, e_size, e_s, e_size);\n\n    DataType* this_U = nullptr;\n    DataType* this_VT = nullptr;\n    if constexpr (Solver::jobu != job::no_vectors) {\n        this_U = U + m * m * batch_idx;\n        common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::a_arrangement>(this_U, m, U_s, ldu);\n    }\n    if constexpr (Solver::jobvt != job::no_vectors) {\n        this_VT = VT + m * m * batch_idx;\n        common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::b_arrangement>(this_VT, m, VT_s, ldvt);\n    }\n\n    Solver().execute(d_s, e_s, U_s, ldu, VT_s, ldvt, &info[batch_idx]);\n\n    common::io<Solver, BatchesPerBlock>::store_a<m, 1, col_major>(d_s, m, this_d, m);\n    common::io<Solver, BatchesPerBlock>::store_a<e_size, 1, col_major>(e_s, e_size, this_e, e_size);\n    if constexpr (Solver::jobu != job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(U_s, ldu, this_U, m);\n    }\n    if constexpr (Solver::jobvt != job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::b_arrangement>(VT_s, ldvt, this_VT, m);\n    }\n}\n\ntemplate<int Arch>\nint bdsvd_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<30>() + Precision<float>() + Type<type::real>() + Function<bdsvd>() + SM<Arch>() + Block()); \n\n    // Job option for bdsvd can be no_vectors, all_vectors/some_vectors or multiply_vectors for either left or right singular vectors\n    // job::no_vectors: compute singular values only\n    // using Solver = decltype(Base() + Job<job::no_vectors, job::no_vectors>());\n    //\n    // If job is job::all_vectors or job::some_vectors for either left or right singular vectors, then U and VT need to be allocated\n    //    on exit, U and VT will be overwritten with the left and right singular vectors of the bidiagonal matrix\n    using Solver = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<col_major, row_major>());\n    //\n    // If job is job::multiply_vectors, then U and VT need to be allocated and with initial values\n    //    on exit, U and VT will be overwritten with the left and right singular vectors of the bidiagonal matrix multiplied by the input of U and VT\n    // using Solver = decltype(Base() + Job<job::multiply_vectors, job::multiply_vectors>() + Arrangement<col_major, row_major>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using \" << bpb << \" Batches per block \\n\";\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size; // the size of the diagonal elements\n    constexpr auto e_size = m - 1;         // the size of the off-diagonal elements\n\n    constexpr bool                  compute_vectors_u = Solver::jobu != job::no_vectors;\n    constexpr bool                  compute_vectors_vt = Solver::jobvt != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_u    = Solver::a_arrangement == arrangement::col_major; // only useful when left singular vectors are computed\n    [[maybe_unused]] constexpr bool is_col_maj_vt   = Solver::b_arrangement == arrangement::col_major; // only useful when right singular vectors are computed\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> d(m * padded_batches), d_input(m * padded_batches);\n    std::vector<data_type> e(e_size * padded_batches), e_input(e_size * padded_batches);\n    // Fill the diagonal and off-diagonal elements of the bidiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, e_size, 1, e_input.data(), e_size, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> U, VT;\n    if constexpr (compute_vectors_u) {\n        U.resize(m * m * padded_batches, 0);\n        // Set input U to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (jobu_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    U[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    if constexpr (compute_vectors_vt) {\n        VT.resize(m * m * padded_batches, 0);\n        // Set input VT to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (jobvt_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    VT[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    int*             d_info = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n\n    data_type* d_U = nullptr;\n    data_type* d_VT = nullptr;\n\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_U), sizeof(data_type) * U.size()));\n        if constexpr (jobu_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_U, U.data(), sizeof(data_type) * U.size(), cudaMemcpyHostToDevice, stream));\n        }\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_VT), sizeof(data_type) * VT.size()));\n        if constexpr (jobvt_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_VT, VT.data(), sizeof(data_type) * VT.size(), cudaMemcpyHostToDevice, stream));\n        }\n    }\n\n    const auto shared_memory_size = Solver::shared_memory_size;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    std::cout << \"shared_memory_size = \" << shared_memory_size << \" bytes\" << std::endl;\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_d, d_e, d_U, d_VT, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(e.data(), d_e, sizeof(data_type) * e.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(U.data(), d_U, sizeof(data_type) * U.size(), cudaMemcpyDeviceToHost, stream));\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(VT.data(), d_VT, sizeof(data_type) * VT.size(), cudaMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx BDSVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_e));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(cudaFree(d_U));\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(cudaFree(d_VT));\n    }\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n\n    std::cout << \"The largest singular value is \" << d[0] << \" and the smallest singular value is \" << d[e_size] << std::endl;\n    std::cout << \"The superdiagonal elements e[0] = \" << e[0] << \" and e[m-2] = \" << e[m - 2] << std::endl;\n    if constexpr (compute_vectors_u && compute_vectors_vt) {\n        // Verify the computed singular values and vectors\n        std::vector<double> A_expected(m * m);\n        std::vector<double> A_reconstructed(m * m);\n        // U and VT could be either col-major or row-major, so for convenience transpose them if they are row-major\n        if constexpr (!is_col_maj_u) {\n            common::transpose_matrix<data_type>(U, m, m, batches);\n        }\n        if constexpr (!is_col_maj_vt) {\n            common::transpose_matrix<data_type>(VT, m, m, batches);\n        }\n\n        \n        for (int b = 0; b < batches; ++b) {\n            // output sigma is stored in d\n            const auto* sigma_b = d.data() + b * m;\n            const auto* U_b = U.data() + b * m * m;\n            const auto* VT_b = VT.data() + b * m * m;\n            const auto* d_input_b = d_input.data() + b * m;\n            const auto* e_input_b = e_input.data() + b * e_size;\n\n            // Compare A_expected and A_reconstructed = U * \\Sigma * VT\n            A_expected.assign(m * m, 0.0);\n            A_reconstructed.assign(m * m, 0.0);\n            for (int i = 0; i < m; i += 1) {\n                for (int j = 0; j < m; j += 1) {\n\n                    for (int l = 0; l < m; ++l) {\n                        A_reconstructed[i + j * m] += double(sigma_b[l]) * double(U_b[i + l * m]) * double(VT_b[l + j * m]);\n                    }\n                    A_expected[i + j * m] = (i == j) ? double(d_input_b[i]) : (i == j - 1) ? double(e_input_b[i]) : 0.0;\n                }\n            }\n            const auto total_relative_error = common::check_error<double, double>(A_reconstructed.data(), A_expected.data(), m * m);\n            std::cout << \"BDSVD: relative error of A - U Sigma VT: for batch \" << b << \" : \" << total_relative_error << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n                printf(\"A_reconstructed = \\n\");\n                common::print_matrix(m, m, true, A_reconstructed.data(), m);\n                printf(\"A_expected = \\n\");\n                common::print_matrix(m, m, true, A_expected.data(), m);\n                std::cout << \"Relative error of A - U Sigma VT is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n        std::cout << \"Success in verifying singular values and vectors\" << std::endl;\n    } else {\n        std::cout << \"Success \" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct bdsvd_batched_block_functor {\n    int operator()() { return bdsvd_batched_block<Arch>(); }\n};\n\n\nint main() {\n    return common::run_example_with_sm<bdsvd_batched_block_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute singular values, and optionally singular vectors, of a batched bidiagonal matrix A with Block execution.\n// The results are verified by comparing A with U * \\Sigma * VT, where U and VT are the left and right singular vectors of A.\n\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType*                     d,\n                                                                        DataType*                     e,\n                                                                        DataType*                     U,\n                                                                        DataType*                     VT,\n                                                                        typename Solver::status_type* info,\n                                                                        const unsigned                batches) {\n    using namespace cusolverdx;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto e_size = m - 1;\n    constexpr auto ldu = Solver::lda;\n    constexpr auto ldvt = Solver::ldb;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [d_s, e_s, U_s, VT_s] = shared_memory::slice<DataType, DataType, DataType, DataType>(shared_mem,\n                                                                                              alignof(DataType),\n                                                                                              m * BatchesPerBlock,\n                                                                                              alignof(DataType),\n                                                                                              e_size * BatchesPerBlock,\n                                                                                              alignof(DataType),\n                                                                                              ldu * m * BatchesPerBlock,\n                                                                                              alignof(DataType));\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (e_size) * batch_idx;\n    common::io<Solver, BatchesPerBlock>::load_a<m, 1, col_major>(this_d, m, d_s, m);\n    common::io<Solver, BatchesPerBlock>::load_a<e_size, 1, col_major>(this_e, e_size, e_s, e_size);\n\n    DataType* this_U = nullptr;\n    DataType* this_VT = nullptr;\n    if constexpr (Solver::jobu != job::no_vectors) {\n        this_U = U + m * m * batch_idx;\n        common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::a_arrangement>(this_U, m, U_s, ldu);\n    }\n    if constexpr (Solver::jobvt != job::no_vectors) {\n        this_VT = VT + m * m * batch_idx;\n        common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::b_arrangement>(this_VT, m, VT_s, ldvt);\n    }\n\n    Solver().execute(d_s, e_s, U_s, ldu, VT_s, ldvt, &info[batch_idx]);\n\n    common::io<Solver, BatchesPerBlock>::store_a<m, 1, col_major>(d_s, m, this_d, m);\n    common::io<Solver, BatchesPerBlock>::store_a<e_size, 1, col_major>(e_s, e_size, this_e, e_size);\n    if constexpr (Solver::jobu != job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(U_s, ldu, this_U, m);\n    }\n    if constexpr (Solver::jobvt != job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::b_arrangement>(VT_s, ldvt, this_VT, m);\n    }\n}\n\ntemplate<int Arch>\nint bdsvd_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<30>() + Precision<float>() + Type<type::real>() + Function<bdsvd>() + SM<Arch>() + Block()); \n\n    // Job option for bdsvd can be no_vectors, all_vectors/some_vectors or multiply_vectors for either left or right singular vectors\n    // job::no_vectors: compute singular values only\n    // using Solver = decltype(Base() + Job<job::no_vectors, job::no_vectors>());\n    //\n    // If job is job::all_vectors or job::some_vectors for either left or right singular vectors, then U and VT need to be allocated\n    //    on exit, U and VT will be overwritten with the left and right singular vectors of the bidiagonal matrix\n    using Solver = decltype(Base() + Job<job::all_vectors, job::all_vectors>() + Arrangement<col_major, row_major>());\n    //\n    // If job is job::multiply_vectors, then U and VT need to be allocated and with initial values\n    //    on exit, U and VT will be overwritten with the left and right singular vectors of the bidiagonal matrix multiplied by the input of U and VT\n    // using Solver = decltype(Base() + Job<job::multiply_vectors, job::multiply_vectors>() + Arrangement<col_major, row_major>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using \" << bpb << \" Batches per block \\n\";\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size; // the size of the diagonal elements\n    constexpr auto e_size = m - 1;         // the size of the off-diagonal elements\n\n    constexpr bool                  compute_vectors_u = Solver::jobu != job::no_vectors;\n    constexpr bool                  compute_vectors_vt = Solver::jobvt != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_u    = Solver::a_arrangement == arrangement::col_major; // only useful when left singular vectors are computed\n    [[maybe_unused]] constexpr bool is_col_maj_vt   = Solver::b_arrangement == arrangement::col_major; // only useful when right singular vectors are computed\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> d(m * padded_batches), d_input(m * padded_batches);\n    std::vector<data_type> e(e_size * padded_batches), e_input(e_size * padded_batches);\n    // Fill the diagonal and off-diagonal elements of the bidiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, e_size, 1, e_input.data(), e_size, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> U, VT;\n    if constexpr (compute_vectors_u) {\n        U.resize(m * m * padded_batches, 0);\n        // Set input U to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (jobu_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    U[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    if constexpr (compute_vectors_vt) {\n        VT.resize(m * m * padded_batches, 0);\n        // Set input VT to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (jobvt_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    VT[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    int*             d_info = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n\n    data_type* d_U = nullptr;\n    data_type* d_VT = nullptr;\n\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_U), sizeof(data_type) * U.size()));\n        if constexpr (jobu_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_U, U.data(), sizeof(data_type) * U.size(), hipMemcpyHostToDevice, stream));\n        }\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_VT), sizeof(data_type) * VT.size()));\n        if constexpr (jobvt_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_VT, VT.data(), sizeof(data_type) * VT.size(), hipMemcpyHostToDevice, stream));\n        }\n    }\n\n    const auto shared_memory_size = Solver::shared_memory_size;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    std::cout << \"shared_memory_size = \" << shared_memory_size << \" bytes\" << std::endl;\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_d, d_e, d_U, d_VT, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(e.data(), d_e, sizeof(data_type) * e.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(U.data(), d_U, sizeof(data_type) * U.size(), hipMemcpyDeviceToHost, stream));\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(VT.data(), d_VT, sizeof(data_type) * VT.size(), hipMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx BDSVD kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_e));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    if constexpr (compute_vectors_u) {\n        CUDA_CHECK_AND_EXIT(hipFree(d_U));\n    }\n    if constexpr (compute_vectors_vt) {\n        CUDA_CHECK_AND_EXIT(hipFree(d_VT));\n    }\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n\n    std::cout << \"The largest singular value is \" << d[0] << \" and the smallest singular value is \" << d[e_size] << std::endl;\n    std::cout << \"The superdiagonal elements e[0] = \" << e[0] << \" and e[m-2] = \" << e[m - 2] << std::endl;\n    if constexpr (compute_vectors_u && compute_vectors_vt) {\n        // Verify the computed singular values and vectors\n        std::vector<double> A_expected(m * m);\n        std::vector<double> A_reconstructed(m * m);\n        // U and VT could be either col-major or row-major, so for convenience transpose them if they are row-major\n        if constexpr (!is_col_maj_u) {\n            common::transpose_matrix<data_type>(U, m, m, batches);\n        }\n        if constexpr (!is_col_maj_vt) {\n            common::transpose_matrix<data_type>(VT, m, m, batches);\n        }\n\n        \n        for (int b = 0; b < batches; ++b) {\n            // output sigma is stored in d\n            const auto* sigma_b = d.data() + b * m;\n            const auto* U_b = U.data() + b * m * m;\n            const auto* VT_b = VT.data() + b * m * m;\n            const auto* d_input_b = d_input.data() + b * m;\n            const auto* e_input_b = e_input.data() + b * e_size;\n\n            // Compare A_expected and A_reconstructed = U * \\Sigma * VT\n            A_expected.assign(m * m, 0.0);\n            A_reconstructed.assign(m * m, 0.0);\n            for (int i = 0; i < m; i += 1) {\n                for (int j = 0; j < m; j += 1) {\n\n                    for (int l = 0; l < m; ++l) {\n                        A_reconstructed[i + j * m] += double(sigma_b[l]) * double(U_b[i + l * m]) * double(VT_b[l + j * m]);\n                    }\n                    A_expected[i + j * m] = (i == j) ? double(d_input_b[i]) : (i == j - 1) ? double(e_input_b[i]) : 0.0;\n                }\n            }\n            const auto total_relative_error = common::check_error<double, double>(A_reconstructed.data(), A_expected.data(), m * m);\n            std::cout << \"BDSVD: relative error of A - U Sigma VT: for batch \" << b << \" : \" << total_relative_error << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error)) {\n                printf(\"A_reconstructed = \\n\");\n                common::print_matrix(m, m, true, A_reconstructed.data(), m);\n                printf(\"A_expected = \\n\");\n                common::print_matrix(m, m, true, A_expected.data(), m);\n                std::cout << \"Relative error of A - U Sigma VT is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n        std::cout << \"Success in verifying singular values and vectors\" << std::endl;\n    } else {\n        std::cout << \"Success \" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct bdsvd_batched_block_functor {\n    int operator()() { return bdsvd_batched_block<Arch>(); }\n};\n\n\nint main() {\n    return common::run_example_with_sm<bdsvd_batched_block_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/05_Singular_Value_Decomposition/bdsvd_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched least squares problem with Thread execution.\n// The thread execution is used for the case where the problem size is small, so it could be more efficient to use Thread execution.\n// The results are compared with reference values obtained with the cuSolver and cuBLAS APIs.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void gels_kernel(DataType* A, DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    constexpr auto max_mn = m >= n ? m : n;\n\n    const auto one_batch_size_a_gmem = m * n;\n    const auto one_batch_size_b_gmem = max_mn * k;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto Bg    = B + one_batch_size_b_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    Solver().execute(Ag, tau_g, Bg);\n}\n\ntemplate<int Arch>\nint gels_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 6, 3>() + Precision<double>() + Type<type::complex>() + Function<gels>() +\n                            Arrangement<arrangement::col_major, arrangement::row_major>() + TransposeMode<conj_trans>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    const auto     max_mn = m >= n ? m : n;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto lda = is_col_maj_a ? m : n;\n    const auto ldb = is_col_maj_b ? max_mn : k;\n\n    constexpr auto input_size_a = m * n; // input A size is m x n per batch\n    constexpr auto input_size_b = max_mn * k; // allocate B/X with max(m, n) x k per batch\n\n    constexpr auto output_size_x = (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans) ? n * k : m * k;\n\n    const auto batches = 300;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, max_mn, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * batches);\n\n\n    std::vector<data_type> L(input_size_a * batches);\n    std::vector<data_type> tau(min(m, n) * batches);\n    std::vector<data_type> tau_ref(min(m, n) * batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_B   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Uncomment below to print the input matrices A and B\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto nthreads = 64;\n    gels_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(X.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //==================================================================================================================\n    // cuSolver/cuBLAS reference using cusolverDnXgeqrf, cusolverDnunmqr, and cublas<t>trsm.\n    //=================================================================================================================\n    if (m >= n) {\n        common::reference_cusolver_geqrf_gels<data_type, cuda_data_type, true, false>(\n                A, B, tau_ref, m, n, k, batches, is_col_maj_a, is_col_maj_b, (transpose_mode_of_v<Solver> != non_trans), batches);\n\n        // check A, tau and X\n        const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n        std::cout << \"GELS: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n        const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min(m, n));\n        std::cout << \"GELS: relative error of tau between cuSolverDx and cuSolver results: \" << total_relative_error_tau << std::endl;\n\n        const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * output_size_x);\n        std::cout << \"GELS: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n        // Uncomment below to print the results after cuSolver reference execute\n        // printf(\"Lref = \\n\");\n        // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n        // printf(\"=====\\n\");\n        // printf(\"Xref = \\n\");\n        // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n\n        if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n            std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n            std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n            std::cout << \"Failure compared with cuSolver API results TAU\" << std::endl;\n            return 1;\n        }\n\n\n        std::cout << \"Success compared with cuSolver/cuBLAS API results, A and tau\" << std::endl;\n    } else {\n        std::cout << \"Comparing cuSolverDx GELS for m <= n cases with cuSolver and cuBlas APIs are not implemented in the example.\" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gels_batched_thread_functor {\n    int operator()() { return gels_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gels_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched least squares problem with Thread execution.\n// The thread execution is used for the case where the problem size is small, so it could be more efficient to use Thread execution.\n// The results are compared with reference values obtained with the cuSolver and cuBLAS APIs.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void gels_kernel(DataType* A, DataType* tau, DataType* B, const unsigned batches) {\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    constexpr auto max_mn = m >= n ? m : n;\n\n    const auto one_batch_size_a_gmem = m * n;\n    const auto one_batch_size_b_gmem = max_mn * k;\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto Bg    = B + one_batch_size_b_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    Solver().execute(Ag, tau_g, Bg);\n}\n\ntemplate<int Arch>\nint gels_batched_thread() {\n\n    using namespace cusolverdx;\n    using Solver = decltype(Size<8, 6, 3>() + Precision<double>() + Type<type::complex>() + Function<gels>() +\n                            Arrangement<arrangement::col_major, arrangement::row_major>() + TransposeMode<conj_trans>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    const auto     max_mn = m >= n ? m : n;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto lda = is_col_maj_a ? m : n;\n    const auto ldb = is_col_maj_b ? max_mn : k;\n\n    constexpr auto input_size_a = m * n; // input A size is m x n per batch\n    constexpr auto input_size_b = max_mn * k; // allocate B/X with max(m, n) x k per batch\n\n    constexpr auto output_size_x = (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans) ? n * k : m * k;\n\n    const auto batches = 300;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, max_mn, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * batches);\n\n\n    std::vector<data_type> L(input_size_a * batches);\n    std::vector<data_type> tau(min(m, n) * batches);\n    std::vector<data_type> tau_ref(min(m, n) * batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_B   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Uncomment below to print the input matrices A and B\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto nthreads = 64;\n    gels_kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_tau, d_B, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(X.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //==================================================================================================================\n    // cuSolver/cuBLAS reference using cusolverDnXgeqrf, cusolverDnunmqr, and cublas<t>trsm.\n    //=================================================================================================================\n    if (m >= n) {\n        common::reference_cusolver_geqrf_gels<data_type, cuda_data_type, true, false>(\n                A, B, tau_ref, m, n, k, batches, is_col_maj_a, is_col_maj_b, (transpose_mode_of_v<Solver> != non_trans), batches);\n\n        // check A, tau and X\n        const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n        std::cout << \"GELS: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n        const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min(m, n));\n        std::cout << \"GELS: relative error of tau between cuSolverDx and cuSolver results: \" << total_relative_error_tau << std::endl;\n\n        const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * output_size_x);\n        std::cout << \"GELS: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n        // Uncomment below to print the results after cuSolver reference execute\n        // printf(\"Lref = \\n\");\n        // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n        // printf(\"=====\\n\");\n        // printf(\"Xref = \\n\");\n        // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n\n        if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n            std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n            std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n            std::cout << \"Failure compared with cuSolver API results TAU\" << std::endl;\n            return 1;\n        }\n\n\n        std::cout << \"Success compared with cuSolver/cuBLAS API results, A and tau\" << std::endl;\n    } else {\n        std::cout << \"Comparing cuSolverDx GELS for m <= n cases with cuSolver and cuBlas APIs are not implemented in the example.\" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gels_batched_thread_functor {\n    int operator()() { return gels_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gels_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/02_Least_Squares/gels_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched least squares problem with Block execution.\n// The results are compared with reference values obtained with the cuSolver and cuBLAS APIs.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, DataType* tau, DataType* B, const int ldb_gmem, const unsigned batches) {\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    constexpr auto max_mn = m >= n ? m : n;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : max_mn * ldb_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : max_mn * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType), one_batch_size_b_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto Bg    = B + one_batch_size_b_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b<max_mn, k>(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, lda_smem, tau_s, Bs, ldb_smem);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b<max_mn, k>(Bs, ldb_smem, Bg, ldb_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint gels_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<20, 16, 5>() + Precision<double>() + Type<type::complex>() + Function<gels>() +\n                          Arrangement<arrangement::col_major, arrangement::row_major>() + TransposeMode<conj_trans>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    const auto     max_mn = m >= n ? m : n;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto lda = is_col_maj_a ? m : n;\n    const auto ldb = is_col_maj_b ? max_mn : k;\n\n    constexpr auto input_size_a = m * n; // input A size is m x n per batch\n    constexpr auto input_size_b = max_mn * k; // allocate B/X with max(m, n) x k per batch\n\n    constexpr auto output_size_x = (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans) ? n * k : m * k;\n\n    const auto batches        = 3;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, max_mn, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<data_type> tau(min(m, n) * padded_batches);\n    std::vector<data_type> tau_ref(min(m, n) * padded_batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_B   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Uncomment below to print the input matrices A and B\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_tau, d_B, ldb, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(X.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //==================================================================================================================\n    // cuSolver/cuBLAS reference using cusolverDnXgeqrf, cusolverDnunmqr, and cublas<t>trsm.\n    //=================================================================================================================\n    if (m >= n) {\n        common::reference_cusolver_geqrf_gels<data_type, cuda_data_type, true, false>(\n                A, B, tau_ref, m, n, k, padded_batches, is_col_maj_a, is_col_maj_b, (transpose_mode_of_v<Solver> != non_trans), batches);\n\n        // check A, tau and X\n        const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n        std::cout << \"GELS: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n        const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min(m, n));\n        std::cout << \"GELS: relative error of tau between cuSolverDx and cuSolver results: \" << total_relative_error_tau << std::endl;\n\n        const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * output_size_x);\n        std::cout << \"GELS: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n        // Uncomment below to print the results after cuSolver reference execute\n        // printf(\"Lref = \\n\");\n        // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n        // printf(\"=====\\n\");\n        // printf(\"Xref = \\n\");\n        // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n\n        if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n            std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n            std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n            std::cout << \"Failure compared with cuSolver API results TAU\" << std::endl;\n            return 1;\n        }\n\n\n        std::cout << \"Success compared with cuSolver/cuBLAS API results, A and tau\" << std::endl;\n    } else {\n        std::cout << \"Comparing cuSolverDx GELS for m <= n cases with cuSolver and cuBlas APIs are not implemented in the example.\" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gels_batched_block_functor {\n    int operator()() { return gels_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gels_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to solve a batched least squares problem with Block execution.\n// The results are compared with reference values obtained with the cuSolver and cuBLAS APIs.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* A, const int lda_gmem, DataType* tau, DataType* B, const int ldb_gmem, const unsigned batches) {\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    constexpr auto max_mn = m >= n ? m : n;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    const auto     one_batch_size_b_gmem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_gmem * k : max_mn * ldb_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto ldb_smem              = Solver::ldb;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n    constexpr auto one_batch_size_b_smem = (cusolverdx::arrangement_of_v_b<Solver> == cusolverdx::col_major) ? ldb_smem * k : max_mn * ldb_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, Bs, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(DataType), one_batch_size_b_smem * BatchesPerBlock,\n            alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag    = A + one_batch_size_a_gmem * batch_idx;\n    auto Bg    = B + one_batch_size_b_gmem * batch_idx;\n    auto tau_g = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, n>(Ag, lda_gmem, As, lda_smem);\n    common::io<Solver, BatchesPerBlock>::load_b<max_mn, k>(Bg, ldb_gmem, Bs, ldb_smem);\n\n    Solver().execute(As, lda_smem, tau_s, Bs, ldb_smem);\n\n    // Store results back to global memory\n    common::io<Solver, BatchesPerBlock>::store_a<m, n>(As, lda_smem, Ag, lda_gmem);\n    common::io<Solver, BatchesPerBlock>::store_b<max_mn, k>(Bs, ldb_smem, Bg, ldb_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint gels_batched_block() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<20, 16, 5>() + Precision<double>() + Type<type::complex>() + Function<gels>() +\n                          Arrangement<arrangement::col_major, arrangement::row_major>() + TransposeMode<conj_trans>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m      = Solver::m_size;\n    constexpr auto n      = Solver::n_size;\n    constexpr auto k      = Solver::k_size;\n    const auto     max_mn = m >= n ? m : n;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<Solver> == arrangement::col_major;\n\n    const auto lda = is_col_maj_a ? m : n;\n    const auto ldb = is_col_maj_b ? max_mn : k;\n\n    constexpr auto input_size_a = m * n; // input A size is m x n per batch\n    constexpr auto input_size_b = max_mn * k; // allocate B/X with max(m, n) x k per batch\n\n    constexpr auto output_size_x = (transpose_mode_of_v<Solver> == trans || transpose_mode_of_v<Solver> == conj_trans) ? n * k : m * k;\n\n    const auto batches        = 3;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> B(input_size_b * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_b, max_mn, k, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(input_size_b * padded_batches);\n\n\n    std::vector<data_type> L(input_size_a * padded_batches);\n    std::vector<data_type> tau(min(m, n) * padded_batches);\n    std::vector<data_type> tau_ref(min(m, n) * padded_batches);\n    data_type*             d_A   = nullptr;\n    data_type*             d_B   = nullptr;\n    data_type*             d_tau = nullptr;\n\n    // Uncomment below to print the input matrices A and B\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda, d_tau, d_B, ldb, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(tau.data(), d_tau, sizeof(data_type) * tau.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n\n    // Uncomment below to print the results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\" after cuSolverDx\\n\");\n    // printf(\"L = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), batches);\n    // printf(\"X = \\n\");\n    // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(X.data(), batches);\n\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //==================================================================================================================\n    // cuSolver/cuBLAS reference using cusolverDnXgeqrf, cusolverDnunmqr, and cublas<t>trsm.\n    //=================================================================================================================\n    if (m >= n) {\n        common::reference_cusolver_geqrf_gels<data_type, cuda_data_type, true, false>(\n                A, B, tau_ref, m, n, k, padded_batches, is_col_maj_a, is_col_maj_b, (transpose_mode_of_v<Solver> != non_trans), batches);\n\n        // check A, tau and X\n        const auto total_relative_error_a = common::check_error<data_type, data_type>(L.data(), A.data(), batches * input_size_a);\n        std::cout << \"GELS: relative error of A between cuSolverDx and cuSolver results: \" << total_relative_error_a << std::endl;\n        const auto total_relative_error_tau = common::check_error<data_type, data_type>(tau.data(), tau_ref.data(), batches * min(m, n));\n        std::cout << \"GELS: relative error of tau between cuSolverDx and cuSolver results: \" << total_relative_error_tau << std::endl;\n\n        const auto total_relative_error_b = common::check_error<data_type, data_type>(X.data(), B.data(), batches * output_size_x);\n        std::cout << \"GELS: relative error of X between cuSolverDx and cuSolver results: \" << total_relative_error_b << std::endl;\n\n        // Uncomment below to print the results after cuSolver reference execute\n        // printf(\"Lref = \\n\");\n        // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), batches);\n        // printf(\"=====\\n\");\n        // printf(\"Xref = \\n\");\n        // common::print_matrix<data_type, max_mn, k, ldb, is_col_maj_b>(B.data(), batches);\n\n\n        if (!common::is_error_acceptable<data_type>(total_relative_error_a)) {\n            std::cout << \"Failure compared with cuSolver API results A\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_b)) {\n            std::cout << \"Failure compared with cuSolver API results X\" << std::endl;\n            return 1;\n        }\n        if (!common::is_error_acceptable<data_type>(total_relative_error_tau)) {\n            std::cout << \"Failure compared with cuSolver API results TAU\" << std::endl;\n            return 1;\n        }\n\n\n        std::cout << \"Success compared with cuSolver/cuBLAS API results, A and tau\" << std::endl;\n    } else {\n        std::cout << \"Comparing cuSolverDx GELS for m <= n cases with cuSolver and cuBlas APIs are not implemented in the example.\" << std::endl;\n    }\n    return 0;\n}\n\ntemplate<int Arch>\nstruct gels_batched_block_functor {\n    int operator()() { return gels_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<gels_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/02_Least_Squares/gels_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n#include <cublasdx.hpp>\n#include <cusolverDn.h>\n\n#include <type_traits>\n#include <iostream>\n\n#include \"../common/device_io.hpp\"\n#include \"../common/macros.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/random.hpp\"\n\n// This example solves a batch of regularized least squares problems\n//      minimize ||b - Ax||_2^2 + lambda ||x||_2^2\n//\n// Three approaches are compared\n// 1. Using cuBLASDx and cuSolverDx to build a single fused kernel to solve the normal equations\n//      (A' A + lambda I) x = A' b\n// 2. Using cuSolverDx to do Householder QR on A augmented by lambda I\n// 3. using cuBLAS and cuSolver to solve the normal equations\n\ntemplate<unsigned M, unsigned N, unsigned NT, class T, class R>\n__global__ __launch_bounds__(NT) void solve_normal_equations(const T* A, unsigned lda, const T* b, T* x, R lambda, int* info) {\n\n    // To maximize parallelism, we compute [G v] = A' * [A b] with a single GEMM, then solve Gx = v\n    // The matrices are assumed to be stored in column-major format\n    // (M + N) * (N + 1) words of dynamic shared memory are required\n\n    // Setup shared memory\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n\n    // Slice shared memory into pointers\n    // Note that in memory, we have the augmented matrices [As bs] and [Gs vs]\n    auto [As, bs, Gs, vs] = cusolverdx::shared_memory::slice<T, T, T, T>(\n        shared_mem,\n        alignof(T), M * N,\n        alignof(T), M,\n        alignof(T), N * N,\n        alignof(T)  // the size (number of elements) may be omitted for the last pointer\n    );\n\n    //// Index into correct batch\n    const unsigned batch = blockIdx.x;\n    A += batch * lda * N;\n    b += batch * M;\n    x += batch * N;\n    info += batch;\n\n    //// Setup operators\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using          prec = std::conditional_t<std::is_same_v<T, float> || std::is_same_v<T, commondx::complex<float>>, float, double>;\n    constexpr auto type = (std::is_same_v<T, float> || std::is_same_v<T, double>) ? cublasdx::type::real : cublasdx::type::complex;\n\n    using GEMM = decltype(cublasdx::TransposeMode<cublasdx::C, cublasdx::N>() + cublasdx::Size<N, N+1, M>() + cublasdx::LeadingDimension<M, M, N>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Precision<prec>() + cublasdx::Type<type>() + cublasdx::Block() + cublasdx::BlockDim<NT>() + cublasdx::SM<Arch>());\n    using POSV = decltype(cusolverdx::Function<cusolverdx::posv>() + cusolverdx::Size<N, N, 1>() + cusolverdx::FillMode<cusolverdx::lower>() +\n                          cusolverdx::Precision<prec>() + cusolverdx::Type<type>() + cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n\n    //// Load data from global memory\n    cusolverdx::copy_2d<NT, M, N, cusolverdx::arrangement::col_major>(A, lda, As, M);\n    cusolverdx::copy_2d<NT, M, 1, cusolverdx::arrangement::col_major>(b, M, bs, M);\n    __syncthreads();\n\n    //// Normal equations\n\n    // Takes advantage of the fact that b and v follow A and G\n    __syncthreads();\n    GEMM().execute(T(1.0), As, As, T(0.0), Gs);\n    __syncthreads();\n    for (int i = threadIdx.x; i < N; i += NT) {\n        Gs[i + i * N] += T(lambda);\n    }\n    __syncthreads();\n    POSV().execute(Gs, vs, info);\n\n    //// store solution back to global memory\n    __syncthreads();\n    cusolverdx::copy_2d<NT, N, 1, cusolverdx::arrangement::col_major>(vs, N, x, N);\n}\n\ntemplate<unsigned M, unsigned N, unsigned NT, class T, class R>\n__global__ __launch_bounds__(NT) void solve_householder(const T* A, unsigned lda, const T* b, T* x, R lambda, int* info) {\n\n    // QR requires an extra N rows for the regularization terms\n\n    // Setup shared memory\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers (arrays)\n    auto [As, taus, bs] = cusolverdx::shared_memory::slice<T, T, T>(\n        shared_mem,\n        alignof(T), (M + N) * N,\n        alignof(T), N,\n        alignof(T) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    //// Index into correct batch\n    unsigned batch = blockIdx.x;\n    A += batch * lda * N;\n    b += batch * M;\n    x += batch * N;\n    info += batch;\n\n    //// Setup operators\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using          prec = std::conditional_t<std::is_same_v<T, float> || std::is_same_v<T, commondx::complex<float>>, float, double>;\n    constexpr auto type = (std::is_same_v<T, float> || std::is_same_v<T, double>) ? cublasdx::type::real : cublasdx::type::complex;\n\n    using GELS = decltype(cusolverdx::Function<cusolverdx::gels>() + cusolverdx::Size<M+N, N, 1>() + cusolverdx::Precision<prec>() + cusolverdx::Type<type>() +\n                          cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n\n    //// Load data from global memory\n    cusolverdx::copy_2d<NT, M, N, cusolverdx::arrangement::col_major>(A, lda, As, M+N);\n    cusolverdx::copy_2d<NT, M, 1, cusolverdx::arrangement::col_major>(b, M, bs, M+N);\n    __syncthreads();\n\n    // Fill the bottom block\n    for (int ij = threadIdx.x; ij < N*N; ij += NT) {\n        int i = ij % N;\n        int j = ij / N;\n\n        As[(M + i) + j * (M+N)] = T(i == j ? lambda : 0);\n    }\n    for (int i = threadIdx.x; i < N; i += NT) {\n        bs[M + i] = 0;\n    }\n    __syncthreads();\n\n    // Solve least squares problem\n    GELS().execute(As, M+N, taus, bs);\n\n    //// store solution back to global memory\n    __syncthreads();\n    cusolverdx::copy_2d<NT, N, 1, cusolverdx::arrangement::col_major>(bs, M+N, x, N);\n}\n\n\n// Helper kernel to apply the regularization term to the Gram matrix\ntemplate<unsigned N, class T, class R>\n__global__ void increment_diagonal(T** G, int ldg, R lambda, unsigned batches) {\n    // BlockDim.x is assumed to be N\n\n    int start = threadIdx.y + blockDim.y * blockIdx.x;\n    int step = blockDim.y * gridDim.x;\n    for (int batch = start; batch < batches; batch += step) {\n        G[batch][threadIdx.x * (ldg+1)] += T(lambda);\n    }\n}\n\n// Solves the normal equations using the host API\ntemplate<unsigned M, unsigned N, class T, class R>\nvoid cusolver_normal_equations(cublasHandle_t blas_hand, cusolverDnHandle_t solv_hand, cudaStream_t stream, T** A, unsigned lda, T** b, T** x, T** G, unsigned ldg, R lambda, int* info, int batches) {\n\n    static_assert(N <= 256);\n    dim3 diag_blockdim = dim3(N, 256/N);\n    dim3 diag_griddim = (batches - 1) / diag_blockdim.y + 1;\n\n    if constexpr (std::is_same_v<T, float>) {\n        T alpha = 1.0f;\n        T beta  = 0.0f;\n        CUBLAS_CHECK_AND_EXIT(cublasSgemmBatched(blas_hand, CUBLAS_OP_C, CUBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(cublasSgemvBatched(blas_hand, CUBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnSpotrfBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnSpotrsBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, double>) {\n        T alpha = 1.0;\n        T beta  = 0.0;\n        CUBLAS_CHECK_AND_EXIT(cublasDgemmBatched(blas_hand, CUBLAS_OP_C, CUBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(cublasDgemvBatched(blas_hand, CUBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnDpotrfBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnDpotrsBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, commondx::complex<float>>) {\n        T alpha = {1.0f, 0.0f};\n        T beta  = {0.0f, 0.0f};\n        CUBLAS_CHECK_AND_EXIT(cublasCgemmBatched(blas_hand, CUBLAS_OP_C, CUBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(cublasCgemvBatched(blas_hand, CUBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnCpotrfBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnCpotrsBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, commondx::complex<double>>) {\n        T alpha = {1.0f, 0.0f};\n        T beta  = {0.0f, 0.0f};\n        CUBLAS_CHECK_AND_EXIT(cublasZgemmBatched(blas_hand, CUBLAS_OP_C, CUBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(cublasZgemvBatched(blas_hand, CUBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnZpotrfBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(cusolverDnZpotrsBatched(solv_hand, CUBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    }\n}\n\ntemplate<class T, class R>\ndouble compute_residual_squared(int m, int n, const T* b, const T* A, int lda, const T* x, R lambda) {\n    // Compute ||b - Ax||_2^2 + \\lambda||x||_2^2\n    double nrm2 = 0;\n    // residual term\n    for (int i = 0; i < m; ++i) {\n        T ri = b[i];\n\n        for (int j = 0; j < n; ++j) {\n            ri -= A[i + j * lda] * x[j];\n        }\n        if constexpr (std::is_same_v<T, std::complex<float>> || std::is_same_v<T, std::complex<double>>) {\n            nrm2 += ri.real() * ri.real() + ri.imag() * ri.imag();\n        } else {\n            nrm2 += ri * ri;\n        }\n    }\n    // regularization term\n    for (int j = 0; j < n; ++j) {\n        if constexpr (std::is_same_v<T, std::complex<float>> || std::is_same_v<T, std::complex<double>>) {\n            nrm2 += lambda * (x[j].real() * x[j].real() + x[j].imag() * x[j].imag());\n        } else {\n            nrm2 += lambda * x[j] * x[j];\n        }\n    }\n    return nrm2;\n}\n\nint main() {\n\n    // Configurable parameters\n    using data_type            = float; // Use commondx::complex<float> and commondx::complex<double> for complex types\n    using real_type            = common::get_precision_t<data_type>;\n    constexpr unsigned m       = 100;\n    constexpr unsigned n       = 8;\n    constexpr unsigned lda     = m;\n    constexpr unsigned batches = 10000;\n    const real_type lambda     = 0.1;\n\n    // Number of threads to use per thread block for the dx implementations\n    constexpr unsigned ne_NT   = 32;\n    constexpr unsigned qr_NT   = 32;\n\n    static_assert(m >= n, \"Normal equations assume that m >= n\");\n    static_assert(lda >= m, \"Leading dimension of A must be at least m\");\n\n    std::cout << \"Solving \" << batches << \" \" << m << \"x\" << n << \" regularized least square problems\" << std::endl;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(m * n * batches);\n    std::vector<data_type> b(m * batches);\n    std::vector<data_type> x(n * batches);\n    std::vector<data_type> x_qr(n * batches);\n    std::vector<int> info(batches);\n    common::fillup_random_matrix_col_major<data_type>(m, n * batches, A.data(), lda, false, false, -2, 2);\n    common::fillup_random_matrix_col_major<data_type>(m, 1 * batches, b.data(), lda, false, false, -2, 2);\n\n\n    data_type *d_A, *d_b, *d_x, *d_G;\n    int* d_info;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_b), sizeof(data_type) * b.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_x), sizeof(data_type) * x.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * batches));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_G), sizeof(data_type) * n * n * batches)); // Workspace for host implementation\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_b, b.data(), sizeof(data_type) * b.size(), cudaMemcpyHostToDevice, stream));\n\n    // Configure and invoke normal equations kernel\n    constexpr unsigned ne_required_smem = (m + n) * (n + 1) * sizeof(data_type);\n    const auto ne_kernel = solve_normal_equations<m, n, ne_NT, data_type, real_type>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(ne_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, ne_required_smem));\n    const auto run_ne_d_impl = [&](cudaStream_t str) {\n        ne_kernel<<<batches, ne_NT, ne_required_smem, str>>>(d_A, lda, d_b, d_x, lambda, d_info);\n    };\n    run_ne_d_impl(stream);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(x.data(), d_x, sizeof(data_type) * x.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    for (int i = 0; i < info.size(); ++i) {\n        if (0 < info[i]) {\n            std::printf(\"Cholesky failed in %dth batch with info %d\", i, info[i]);\n        }\n    }\n\n    // Configure and invoke normal equations kernel\n    constexpr unsigned qr_required_smem = ((m + n) * (n + 1) + n) * sizeof(data_type);\n    const auto qr_kernel = solve_householder<m, n, qr_NT, data_type, real_type>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(qr_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, qr_required_smem));\n    const auto run_qr_d_impl = [&](cudaStream_t str) {\n        qr_kernel<<<batches, qr_NT, qr_required_smem, str>>>(d_A, lda, d_b, d_x, lambda, d_info);\n    };\n    run_qr_d_impl(stream);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(x_qr.data(), d_x, sizeof(data_type) * x_qr.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    cublasHandle_t blas_hand = NULL;\n    CUBLAS_CHECK_AND_EXIT(cublasCreate(&blas_hand));\n    CUBLAS_CHECK_AND_EXIT(cublasSetStream(blas_hand, stream));\n    cusolverDnHandle_t solv_hand = NULL;\n    CUSOLVER_CHECK_AND_EXIT(cusolverDnCreate(&solv_hand));\n    CUSOLVER_CHECK_AND_EXIT(cusolverDnSetStream(solv_hand, stream));\n\n    // Setup batched arrays\n    std::vector<data_type*> Aarr (batches);\n    std::vector<data_type*> barr (batches);\n    std::vector<data_type*> xarr (batches);\n    std::vector<data_type*> Garr (batches);\n    for (int i = 0; i < batches; ++i) {\n        Aarr[i] = d_A + i * m * n;\n        barr[i] = d_b + i * m;\n        xarr[i] = d_x + i * n;\n        Garr[i] = d_G + i * n * n;\n    }\n    data_type **d_Aarr, **d_barr, **d_xarr, **d_Garr;\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_Aarr), sizeof(data_type*) * Aarr.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_barr), sizeof(data_type*) * barr.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_xarr), sizeof(data_type*) * xarr.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_Garr), sizeof(data_type*) * Garr.size()));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_Aarr, Aarr.data(), sizeof(data_type*) * Aarr.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_barr, barr.data(), sizeof(data_type*) * barr.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_xarr, xarr.data(), sizeof(data_type*) * xarr.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_Garr, Garr.data(), sizeof(data_type*) * Garr.size(), cudaMemcpyHostToDevice, stream));\n\n    // Run cusolver version for correctness test\n    const auto run_ne_h_impl = [&](cudaStream_t str) {\n        cusolver_normal_equations<m, n, data_type>(blas_hand, solv_hand, str, d_Aarr, lda, d_barr, d_xarr, d_Garr, n, lambda, d_info, batches);\n    };\n    run_ne_h_impl(stream);\n\n    std::vector<data_type> x_host(n * batches);\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(x_host.data(), d_x, sizeof(data_type) * x.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    // Compute ||b - Ax||_2^2 + \\lambda||x||_2^2 for each solver\n    double ne_d_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x.data(), lambda);\n    double qr_d_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x_qr.data(), lambda);\n    double ne_h_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x_host.data(), lambda);\n\n    // Compare the performance of the two implementations\n    const unsigned int warmup_repeats = 1;\n    const unsigned int kernel_repeats = 3;\n    double ne_d_ms = common::measure::execution(run_ne_d_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n    double qr_d_ms = common::measure::execution(run_qr_d_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n    double ne_h_ms = common::measure::execution(run_ne_h_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n\n    constexpr double ne_flop_per_batch = common::get_flops_gemm<data_type>(m, n, n) + n + common::get_flops_potrf<data_type>(n) + common::get_flops_potrs<data_type>(n, 1);\n    constexpr double qr_flop_per_batch = common::get_flops_geqrf<data_type>(m+n, n) + common::get_flops_unmqr<data_type>(cusolverdx::side::left, m+n, 1, n) + common::get_flops_trsm<data_type>(n, 1);\n    double ne_d_s_per_g_batch = ne_d_ms / 1e3 / batches * 1e9;\n    double qr_d_s_per_g_batch = qr_d_ms / 1e3 / batches * 1e9;\n    double ne_h_s_per_g_batch = ne_h_ms / 1e3 / batches * 1e9;\n    double ne_d_gflop_per_s   = ne_flop_per_batch / ne_d_s_per_g_batch;\n    double qr_d_gflop_per_s   = qr_flop_per_batch / qr_d_s_per_g_batch;\n    double ne_h_gflop_per_s   = ne_flop_per_batch / ne_h_s_per_g_batch;\n\n    printf(\"Dx normal equations   %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", ne_d_gflop_per_s, ne_d_ms, ne_d_nrm2);\n    printf(\"Dx Householder        %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", qr_d_gflop_per_s, qr_d_ms, qr_d_nrm2);\n    printf(\"Host normal equations %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", ne_h_gflop_per_s, ne_h_ms, ne_h_nrm2);\n\n    // free device resources\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_b));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_x));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_G));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_Aarr));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_barr));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_xarr));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_Garr));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUBLAS_CHECK_AND_EXIT(cublasDestroy(blas_hand));\n    CUSOLVER_CHECK_AND_EXIT(cusolverDnDestroy(solv_hand));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n#include <cublasdx.hpp>\n#include <hipsolver.h>\n\n#include <type_traits>\n#include <iostream>\n\n#include \"../common/device_io.hpp\"\n#include \"../common/macros.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/random.hpp\"\n\n// This example solves a batch of regularized least squares problems\n//      minimize ||b - Ax||_2^2 + lambda ||x||_2^2\n//\n// Three approaches are compared\n// 1. Using cuBLASDx and cuSolverDx to build a single fused kernel to solve the normal equations\n//      (A' A + lambda I) x = A' b\n// 2. Using cuSolverDx to do Householder QR on A augmented by lambda I\n// 3. using cuBLAS and cuSolver to solve the normal equations\n\ntemplate<unsigned M, unsigned N, unsigned NT, class T, class R>\n__global__ __launch_bounds__(NT) void solve_normal_equations(const T* A, unsigned lda, const T* b, T* x, R lambda, int* info) {\n\n    // To maximize parallelism, we compute [G v] = A' * [A b] with a single GEMM, then solve Gx = v\n    // The matrices are assumed to be stored in column-major format\n    // (M + N) * (N + 1) words of dynamic shared memory are required\n\n    // Setup shared memory\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n\n    // Slice shared memory into pointers\n    // Note that in memory, we have the augmented matrices [As bs] and [Gs vs]\n    auto [As, bs, Gs, vs] = cusolverdx::shared_memory::slice<T, T, T, T>(\n        shared_mem,\n        alignof(T), M * N,\n        alignof(T), M,\n        alignof(T), N * N,\n        alignof(T)  // the size (number of elements) may be omitted for the last pointer\n    );\n\n    //// Index into correct batch\n    const unsigned batch = blockIdx.x;\n    A += batch * lda * N;\n    b += batch * M;\n    x += batch * N;\n    info += batch;\n\n    //// Setup operators\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using          prec = std::conditional_t<std::is_same_v<T, float> || std::is_same_v<T, commondx::complex<float>>, float, double>;\n    constexpr auto type = (std::is_same_v<T, float> || std::is_same_v<T, double>) ? cublasdx::type::real : cublasdx::type::complex;\n\n    using GEMM = decltype(cublasdx::TransposeMode<cublasdx::C, cublasdx::N>() + cublasdx::Size<N, N+1, M>() + cublasdx::LeadingDimension<M, M, N>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Precision<prec>() + cublasdx::Type<type>() + cublasdx::Block() + cublasdx::BlockDim<NT>() + cublasdx::SM<Arch>());\n    using POSV = decltype(cusolverdx::Function<cusolverdx::posv>() + cusolverdx::Size<N, N, 1>() + cusolverdx::FillMode<cusolverdx::lower>() +\n                          cusolverdx::Precision<prec>() + cusolverdx::Type<type>() + cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n\n    //// Load data from global memory\n    cusolverdx::copy_2d<NT, M, N, cusolverdx::arrangement::col_major>(A, lda, As, M);\n    cusolverdx::copy_2d<NT, M, 1, cusolverdx::arrangement::col_major>(b, M, bs, M);\n    __syncthreads();\n\n    //// Normal equations\n\n    // Takes advantage of the fact that b and v follow A and G\n    __syncthreads();\n    GEMM().execute(T(1.0), As, As, T(0.0), Gs);\n    __syncthreads();\n    for (int i = threadIdx.x; i < N; i += NT) {\n        Gs[i + i * N] += T(lambda);\n    }\n    __syncthreads();\n    POSV().execute(Gs, vs, info);\n\n    //// store solution back to global memory\n    __syncthreads();\n    cusolverdx::copy_2d<NT, N, 1, cusolverdx::arrangement::col_major>(vs, N, x, N);\n}\n\ntemplate<unsigned M, unsigned N, unsigned NT, class T, class R>\n__global__ __launch_bounds__(NT) void solve_householder(const T* A, unsigned lda, const T* b, T* x, R lambda, int* info) {\n\n    // QR requires an extra N rows for the regularization terms\n\n    // Setup shared memory\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers (arrays)\n    auto [As, taus, bs] = cusolverdx::shared_memory::slice<T, T, T>(\n        shared_mem,\n        alignof(T), (M + N) * N,\n        alignof(T), N,\n        alignof(T) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    //// Index into correct batch\n    unsigned batch = blockIdx.x;\n    A += batch * lda * N;\n    b += batch * M;\n    x += batch * N;\n    info += batch;\n\n    //// Setup operators\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using          prec = std::conditional_t<std::is_same_v<T, float> || std::is_same_v<T, commondx::complex<float>>, float, double>;\n    constexpr auto type = (std::is_same_v<T, float> || std::is_same_v<T, double>) ? cublasdx::type::real : cublasdx::type::complex;\n\n    using GELS = decltype(cusolverdx::Function<cusolverdx::gels>() + cusolverdx::Size<M+N, N, 1>() + cusolverdx::Precision<prec>() + cusolverdx::Type<type>() +\n                          cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n\n    //// Load data from global memory\n    cusolverdx::copy_2d<NT, M, N, cusolverdx::arrangement::col_major>(A, lda, As, M+N);\n    cusolverdx::copy_2d<NT, M, 1, cusolverdx::arrangement::col_major>(b, M, bs, M+N);\n    __syncthreads();\n\n    // Fill the bottom block\n    for (int ij = threadIdx.x; ij < N*N; ij += NT) {\n        int i = ij % N;\n        int j = ij / N;\n\n        As[(M + i) + j * (M+N)] = T(i == j ? lambda : 0);\n    }\n    for (int i = threadIdx.x; i < N; i += NT) {\n        bs[M + i] = 0;\n    }\n    __syncthreads();\n\n    // Solve least squares problem\n    GELS().execute(As, M+N, taus, bs);\n\n    //// store solution back to global memory\n    __syncthreads();\n    cusolverdx::copy_2d<NT, N, 1, cusolverdx::arrangement::col_major>(bs, M+N, x, N);\n}\n\n\n// Helper kernel to apply the regularization term to the Gram matrix\ntemplate<unsigned N, class T, class R>\n__global__ void increment_diagonal(T** G, int ldg, R lambda, unsigned batches) {\n    // BlockDim.x is assumed to be N\n\n    int start = threadIdx.y + blockDim.y * blockIdx.x;\n    int step = blockDim.y * gridDim.x;\n    for (int batch = start; batch < batches; batch += step) {\n        G[batch][threadIdx.x * (ldg+1)] += T(lambda);\n    }\n}\n\n// Solves the normal equations using the host API\ntemplate<unsigned M, unsigned N, class T, class R>\nvoid cusolver_normal_equations(hipblasHandle_t blas_hand, hipsolverHandle_t solv_hand, hipStream_t stream, T** A, unsigned lda, T** b, T** x, T** G, unsigned ldg, R lambda, int* info, int batches) {\n\n    static_assert(N <= 256);\n    dim3 diag_blockdim = dim3(N, 256/N);\n    dim3 diag_griddim = (batches - 1) / diag_blockdim.y + 1;\n\n    if constexpr (std::is_same_v<T, float>) {\n        T alpha = 1.0f;\n        T beta  = 0.0f;\n        CUBLAS_CHECK_AND_EXIT(hipblasSgemmBatched(blas_hand, HIPBLAS_OP_C, HIPBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(hipblasSgemvBatched(blas_hand, HIPBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnSpotrfBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnSpotrsBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, double>) {\n        T alpha = 1.0;\n        T beta  = 0.0;\n        CUBLAS_CHECK_AND_EXIT(hipblasDgemmBatched(blas_hand, HIPBLAS_OP_C, HIPBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(hipblasDgemvBatched(blas_hand, HIPBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnDpotrfBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnDpotrsBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, commondx::complex<float>>) {\n        T alpha = {1.0f, 0.0f};\n        T beta  = {0.0f, 0.0f};\n        CUBLAS_CHECK_AND_EXIT(hipblasCgemmBatched(blas_hand, HIPBLAS_OP_C, HIPBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(hipblasCgemvBatched(blas_hand, HIPBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnCpotrfBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnCpotrsBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    } else if constexpr (std::is_same_v<T, commondx::complex<double>>) {\n        T alpha = {1.0f, 0.0f};\n        T beta  = {0.0f, 0.0f};\n        CUBLAS_CHECK_AND_EXIT(hipblasZgemmBatched(blas_hand, HIPBLAS_OP_C, HIPBLAS_OP_N, N, N, M,\n                    &alpha, A, lda,\n                            A, lda,\n                    &beta,  G, ldg,\n                    batches));\n        CUBLAS_CHECK_AND_EXIT(hipblasZgemvBatched(blas_hand, HIPBLAS_OP_C, M, N,\n                    &alpha, A, lda,\n                            b, 1,\n                    &beta,  x, 1,\n                    batches));\n        increment_diagonal<N, T><<<diag_griddim, diag_blockdim, 0, stream>>>(G, ldg, lambda, batches);\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnZpotrfBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, G, ldg, info, batches));\n        CUSOLVER_CHECK_AND_EXIT(hipsolverDnZpotrsBatched(solv_hand, HIPBLAS_FILL_MODE_LOWER, N, 1, G, N, x, N, info, batches));\n    }\n}\n\ntemplate<class T, class R>\ndouble compute_residual_squared(int m, int n, const T* b, const T* A, int lda, const T* x, R lambda) {\n    // Compute ||b - Ax||_2^2 + \\lambda||x||_2^2\n    double nrm2 = 0;\n    // residual term\n    for (int i = 0; i < m; ++i) {\n        T ri = b[i];\n\n        for (int j = 0; j < n; ++j) {\n            ri -= A[i + j * lda] * x[j];\n        }\n        if constexpr (std::is_same_v<T, std::complex<float>> || std::is_same_v<T, std::complex<double>>) {\n            nrm2 += ri.real() * ri.real() + ri.imag() * ri.imag();\n        } else {\n            nrm2 += ri * ri;\n        }\n    }\n    // regularization term\n    for (int j = 0; j < n; ++j) {\n        if constexpr (std::is_same_v<T, std::complex<float>> || std::is_same_v<T, std::complex<double>>) {\n            nrm2 += lambda * (x[j].real() * x[j].real() + x[j].imag() * x[j].imag());\n        } else {\n            nrm2 += lambda * x[j] * x[j];\n        }\n    }\n    return nrm2;\n}\n\nint main() {\n\n    // Configurable parameters\n    using data_type            = float; // Use commondx::complex<float> and commondx::complex<double> for complex types\n    using real_type            = common::get_precision_t<data_type>;\n    constexpr unsigned m       = 100;\n    constexpr unsigned n       = 8;\n    constexpr unsigned lda     = m;\n    constexpr unsigned batches = 10000;\n    const real_type lambda     = 0.1;\n\n    // Number of threads to use per thread block for the dx implementations\n    constexpr unsigned ne_NT   = 32;\n    constexpr unsigned qr_NT   = 32;\n\n    static_assert(m >= n, \"Normal equations assume that m >= n\");\n    static_assert(lda >= m, \"Leading dimension of A must be at least m\");\n\n    std::cout << \"Solving \" << batches << \" \" << m << \"x\" << n << \" regularized least square problems\" << std::endl;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(m * n * batches);\n    std::vector<data_type> b(m * batches);\n    std::vector<data_type> x(n * batches);\n    std::vector<data_type> x_qr(n * batches);\n    std::vector<int> info(batches);\n    common::fillup_random_matrix_col_major<data_type>(m, n * batches, A.data(), lda, false, false, -2, 2);\n    common::fillup_random_matrix_col_major<data_type>(m, 1 * batches, b.data(), lda, false, false, -2, 2);\n\n\n    data_type *d_A, *d_b, *d_x, *d_G;\n    int* d_info;\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_b), sizeof(data_type) * b.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_x), sizeof(data_type) * x.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * batches));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_G), sizeof(data_type) * n * n * batches)); // Workspace for host implementation\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_b, b.data(), sizeof(data_type) * b.size(), hipMemcpyHostToDevice, stream));\n\n    // Configure and invoke normal equations kernel\n    constexpr unsigned ne_required_smem = (m + n) * (n + 1) * sizeof(data_type);\n    const auto ne_kernel = solve_normal_equations<m, n, ne_NT, data_type, real_type>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(ne_kernel), hipFuncAttributeMaxDynamicSharedMemorySize, ne_required_smem));\n    const auto run_ne_d_impl = [&](hipStream_t str) {\n        ne_kernel<<<batches, ne_NT, ne_required_smem, str>>>(d_A, lda, d_b, d_x, lambda, d_info);\n    };\n    run_ne_d_impl(stream);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(x.data(), d_x, sizeof(data_type) * x.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    for (int i = 0; i < info.size(); ++i) {\n        if (0 < info[i]) {\n            std::printf(\"Cholesky failed in %dth batch with info %d\", i, info[i]);\n        }\n    }\n\n    // Configure and invoke normal equations kernel\n    constexpr unsigned qr_required_smem = ((m + n) * (n + 1) + n) * sizeof(data_type);\n    const auto qr_kernel = solve_householder<m, n, qr_NT, data_type, real_type>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(qr_kernel), hipFuncAttributeMaxDynamicSharedMemorySize, qr_required_smem));\n    const auto run_qr_d_impl = [&](hipStream_t str) {\n        qr_kernel<<<batches, qr_NT, qr_required_smem, str>>>(d_A, lda, d_b, d_x, lambda, d_info);\n    };\n    run_qr_d_impl(stream);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(x_qr.data(), d_x, sizeof(data_type) * x_qr.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n\n    //=========================\n    // cuSolver reference\n    //=========================\n\n    hipblasHandle_t blas_hand = NULL;\n    CUBLAS_CHECK_AND_EXIT(hipblasCreate(&blas_hand));\n    CUBLAS_CHECK_AND_EXIT(hipblasSetStream(blas_hand, stream));\n    hipsolverHandle_t solv_hand = NULL;\n    CUSOLVER_CHECK_AND_EXIT(hipsolverDnCreate(&solv_hand));\n    CUSOLVER_CHECK_AND_EXIT(hipsolverSetStream(solv_hand, stream));\n\n    // Setup batched arrays\n    std::vector<data_type*> Aarr (batches);\n    std::vector<data_type*> barr (batches);\n    std::vector<data_type*> xarr (batches);\n    std::vector<data_type*> Garr (batches);\n    for (int i = 0; i < batches; ++i) {\n        Aarr[i] = d_A + i * m * n;\n        barr[i] = d_b + i * m;\n        xarr[i] = d_x + i * n;\n        Garr[i] = d_G + i * n * n;\n    }\n    data_type **d_Aarr, **d_barr, **d_xarr, **d_Garr;\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_Aarr), sizeof(data_type*) * Aarr.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_barr), sizeof(data_type*) * barr.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_xarr), sizeof(data_type*) * xarr.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_Garr), sizeof(data_type*) * Garr.size()));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_Aarr, Aarr.data(), sizeof(data_type*) * Aarr.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_barr, barr.data(), sizeof(data_type*) * barr.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_xarr, xarr.data(), sizeof(data_type*) * xarr.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_Garr, Garr.data(), sizeof(data_type*) * Garr.size(), hipMemcpyHostToDevice, stream));\n\n    // Run cusolver version for correctness test\n    const auto run_ne_h_impl = [&](hipStream_t str) {\n        cusolver_normal_equations<m, n, data_type>(blas_hand, solv_hand, str, d_Aarr, lda, d_barr, d_xarr, d_Garr, n, lambda, d_info, batches);\n    };\n    run_ne_h_impl(stream);\n\n    std::vector<data_type> x_host(n * batches);\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(x_host.data(), d_x, sizeof(data_type) * x.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    // Compute ||b - Ax||_2^2 + \\lambda||x||_2^2 for each solver\n    double ne_d_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x.data(), lambda);\n    double qr_d_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x_qr.data(), lambda);\n    double ne_h_nrm2 = compute_residual_squared(m, n, b.data(), A.data(), lda, x_host.data(), lambda);\n\n    // Compare the performance of the two implementations\n    const unsigned int warmup_repeats = 1;\n    const unsigned int kernel_repeats = 3;\n    double ne_d_ms = common::measure::execution(run_ne_d_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n    double qr_d_ms = common::measure::execution(run_qr_d_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n    double ne_h_ms = common::measure::execution(run_ne_h_impl, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n\n    constexpr double ne_flop_per_batch = common::get_flops_gemm<data_type>(m, n, n) + n + common::get_flops_potrf<data_type>(n) + common::get_flops_potrs<data_type>(n, 1);\n    constexpr double qr_flop_per_batch = common::get_flops_geqrf<data_type>(m+n, n) + common::get_flops_unmqr<data_type>(cusolverdx::side::left, m+n, 1, n) + common::get_flops_trsm<data_type>(n, 1);\n    double ne_d_s_per_g_batch = ne_d_ms / 1e3 / batches * 1e9;\n    double qr_d_s_per_g_batch = qr_d_ms / 1e3 / batches * 1e9;\n    double ne_h_s_per_g_batch = ne_h_ms / 1e3 / batches * 1e9;\n    double ne_d_gflop_per_s   = ne_flop_per_batch / ne_d_s_per_g_batch;\n    double qr_d_gflop_per_s   = qr_flop_per_batch / qr_d_s_per_g_batch;\n    double ne_h_gflop_per_s   = ne_flop_per_batch / ne_h_s_per_g_batch;\n\n    printf(\"Dx normal equations   %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", ne_d_gflop_per_s, ne_d_ms, ne_d_nrm2);\n    printf(\"Dx Householder        %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", qr_d_gflop_per_s, qr_d_ms, qr_d_nrm2);\n    printf(\"Host normal equations %7.2f GFLOP/s, %7.2f ms, objective %e\\n\", ne_h_gflop_per_s, ne_h_ms, ne_h_nrm2);\n\n    // free device resources\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_b));\n    CUDA_CHECK_AND_EXIT(hipFree(d_x));\n    CUDA_CHECK_AND_EXIT(hipFree(d_G));\n    CUDA_CHECK_AND_EXIT(hipFree(d_Aarr));\n    CUDA_CHECK_AND_EXIT(hipFree(d_barr));\n    CUDA_CHECK_AND_EXIT(hipFree(d_xarr));\n    CUDA_CHECK_AND_EXIT(hipFree(d_Garr));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUBLAS_CHECK_AND_EXIT(hipblasDestroy(blas_hand));\n    CUSOLVER_CHECK_AND_EXIT(hipsolverDnDestroy(solv_hand));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/10_Advanced/reg_least_squares.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n#include <cublasdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This is a basic implementation of Cholesky factorization using a single CTA with matrices too large for cuSolverDx's shared memory API.\n// The code uses an out-of-core style, left-looking formulation so that it never needs more than 4 blocks in shared memory at a given point.\n\n// There are a few limitations of this implementation:\n// * Only real-valued types are supported\n// * The matrix size, N, must be a multiple of the block size, NB\n// * Only upper-triangular storage is implemented\n\n\n//////// Helper functions to load and store the diagonal blocks //////\n// Only the upper triangular part is transferred\n// To help L1 cache utilization, the diagonal blocks bypass the L1 cache\n\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\ninline __device__ void load_diagonal_block(const T* A, const int lda, T* As, const int ldas) {\n    const int tid = threadIdx.x;\n    __builtin_assume(tid < NT);\n\n    if constexpr (NT % NB == 0) {\n        constexpr unsigned stride_jj = NT / NB;\n        const unsigned i = tid % NB;\n        const unsigned j = tid / NB;\n        for (int jj = 0; jj < NB; jj += stride_jj) {\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j+jj) : (i >= j+jj);\n            if (is_upper_tri) {\n                As[i + (jj+j)*ldas] = __ldcg(A + i + (jj+j)*lda);\n            }\n        }\n    } else {\n        for (int k = tid; k < NB*NB; k += NT) {\n            unsigned i = k % NB;\n            unsigned j = k / NB;\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j) : (i >= j);\n            if (is_upper_tri) {\n                As[i + j*ldas] = __ldcg(A + i + j*lda);\n            }\n        }\n    }\n    __syncthreads();\n}\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\ninline __device__ void store_diagonal_block(const T* As, const int ldas, T* A, const int lda) {\n    const int tid = threadIdx.x;\n    __builtin_assume(tid < NT);\n\n    __syncthreads();\n    if constexpr (NT % NB == 0) {\n        constexpr unsigned stride_jj = NT / NB;\n        const unsigned i = tid % NB;\n        const unsigned j = tid / NB;\n        for (int jj = 0; jj < NB; jj += stride_jj) {\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j+jj) : (i >= j+jj);\n            if (is_upper_tri) {\n                __stcg(A + i + (jj+j)*lda, As[i + (jj+j)*ldas]);\n            }\n        }\n    } else {\n        for (int k = tid; k < NB*NB; k += NT) {\n            unsigned i = k % NB;\n            unsigned j = k / NB;\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j) : (i >= j);\n            if (is_upper_tri) {\n                __stcg(A + i + j*lda, As[i + j*ldas]);\n            }\n        }\n    }\n}\n\n//////// Blocked Cholesky Implementation ////////\n\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, class T>\n__device__ T* tile(T* A, unsigned lda, unsigned i, unsigned j) {\n    if (Arrange == cusolverdx::col_major) {\n        return A + i*NB + j*NB*lda;\n    } else {\n        return A + i*NB*lda + j*NB;\n    }\n}\n\ntemplate<unsigned N, unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\n__global__ __launch_bounds__(NT) void potrf_kernel(T* A, unsigned lda, int* info) {\n    // Assumes A is stored in the upper triangle\n\n    static_assert(N % NB == 0, \"Code currently assumes that N is exactly divisible by the block size\");\n    static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, \"cuSolverDx calls currently hardcoded for real valued type\");\n\n    // Get batch\n    A    += blockIdx.x * N * lda;\n    info += blockIdx.x;\n\n    constexpr unsigned lds = NB;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [sA, sB, sC, sD, sinfo] = cusolverdx::shared_memory::slice<T, T, T, T, int>(\n        shared_mem,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using POTRF = decltype(cusolverdx::Function<cusolverdx::function::potrf>() + cusolverdx::FillMode<cusolverdx::fill_mode::upper>() + cusolverdx::Size<NB>() +\n                           cusolverdx::Precision<T>() + cusolverdx::Type<cusolverdx::type::real>() + cusolverdx::Arrangement<Arrange>() +\n                           cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n    // TRSM is temporarily being exported from cuSolverDx instead of the cuBLASDx\n    using TRSM = decltype(cusolverdx::Function<cusolverdx::function::trsm>() + cusolverdx::Size<NB, NB, NB>() +\n                          cusolverdx::Precision<T>() + cusolverdx::Type<cusolverdx::type::real>() +\n                          cusolverdx::Side<cusolverdx::side::left>() + cusolverdx::Diag<cusolverdx::diag::non_unit>() +\n                          cusolverdx::TransposeMode<cusolverdx::transpose::transposed>() +\n                          cusolverdx::Arrangement<Arrange, Arrange>() + cusolverdx::FillMode<cusolverdx::fill_mode::upper>() +\n                          cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n    // Have to use Arrangement to set transposition, to work around cuBLASDx's lack of support for using TransposeMode with a row-major C\n    using GEMM_Arrange = std::conditional_t<Arrange == cusolverdx::col_major,\n                                            cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major, cublasdx::col_major>,\n                                            cublasdx::Arrangement<cublasdx::col_major, cublasdx::row_major, cublasdx::row_major>>;\n    constexpr unsigned int alignment = ((sizeof(T) * NB * NB) % 16 == 0) ? 16 : sizeof(T);\n    using GEMM = decltype(cublasdx::Size<NB, NB, NB>() + GEMM_Arrange() + cublasdx::Alignment<alignment, alignment, alignment>() +\n                          cublasdx::Precision<T>() + cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<NT>() + cublasdx::SM<Arch>());\n\n    // left-looking, out of core algorithm\n\n    unsigned n_tiles = N / NB;\n    int rinfo = 0;\n\n\n    for (int k = 0; k < n_tiles; ++k) {\n        // Diagonal block\n        auto Akk = tile<NB, Arrange>(A, lda, k, k);\n        load_diagonal_block<NB, Arrange, NT>(Akk, lda, sA, lds);\n        // Schur-complements previous steps\n        for (int i = 0; i < k; ++i) {\n            auto Aik = tile<NB, Arrange>(A, lda, i, k);\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aik, lda, sC, lds);\n            __syncthreads();\n            GEMM().execute(T(-1.0), sC, sC, T(1.0), sA);\n            __syncthreads();\n        }\n        // Factor block\n        POTRF().execute(sA, lds, sinfo);\n        store_diagonal_block<NB, Arrange, NT>(sA, lds, Akk, lda);\n        if (threadIdx.x == 0 && rinfo == 0 && *sinfo != 0) {\n            rinfo = *sinfo + k*NB;\n        }\n\n        // Panel\n        for (int j = k+1; j < n_tiles; ++j) {\n            auto Akj = tile<NB, Arrange>(A, lda, k, j);\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Akj, lda, sB, lds);\n            __syncthreads();\n\n            for (int i = 0; i < k; ++i) {\n                auto Aik = tile<NB, Arrange>(A, lda, i, k);\n                auto Aij = tile<NB, Arrange>(A, lda, i, j);\n                cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aik, lda, sC, lds);\n                cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aij, lda, sD, lds);\n                __syncthreads();\n                GEMM().execute(T(-1.0), sC, sD, T(1.0), sB);\n                __syncthreads();\n            }\n\n            TRSM().execute(sA, lds, sB, lds);\n            __syncthreads();\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(sB, lds, Akj, lda);\n            __syncthreads();\n        }\n    }\n\n\n    if (threadIdx.x == 0) {\n        *info = rinfo;\n    }\n}\n\nint blocked_potrf() {\n\n    // Only float and double are supported\n    using data_type = double;\n\n    constexpr unsigned N       = 512;                   // The matrix size\n    constexpr auto     Arrange = cusolverdx::row_major; // Whether the matrix is column-major or row-major\n    const     unsigned batches = 400;\n\n    // Optimal parameters vary with hardware, N, batches, etc.\n    constexpr unsigned NB      = 32;                    // The blocking size to use\n    constexpr unsigned NT      = 128;                   // The number of threads to use per thread block\n\n    constexpr auto lda        = N;       // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * N; // input global memory size for A\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size * batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(Arrange == cusolverdx::col_major, N, N, A.data(), lda, false, -2, 2, batches); // input A is not symmetric\n\n    std::vector<data_type> L(input_size * batches);\n    std::vector<int>       info(batches);\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    size_t smem_size = 4 * NB * NB * sizeof(data_type) + sizeof(int);\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(potrf_kernel<N, NB, Arrange, NT, data_type>, cudaFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n    //Invokes kernel\n    auto run_kernel = [&](cudaStream_t str) {\n        potrf_kernel<N, NB, Arrange, NT, data_type><<<batches, NT, smem_size, str>>>(d_A, lda, d_info);\n    };\n    auto reset = [&] (cudaStream_t str) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, str));\n        CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(str));\n    };\n    const unsigned int warmup_repeats = 1;\n    const unsigned int repeats        = 1;\n\n    reset(stream);\n    double ms                     = common::measure::execution(run_kernel, reset, warmup_repeats, repeats, stream) / repeats;\n    double seconds_per_giga_batch = ms / 1e3 / batches * 1e9;\n    double gb_s                   = input_size * sizeof(data_type) * 2 / seconds_per_giga_batch;\n    double gflops                 = common::get_flops_potrf<data_type>(N) / seconds_per_giga_batch;\n\n    common::print_perf(\"Blocked dx potrf\", batches, N, N, 1, gflops, gb_s, ms, NT);\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    for (int i = 0; i < info.size(); ++i) {\n        if (0 > info[i]) {\n            std::printf(\"%d-th parameter is wrong \\n\", -info[i]);\n            exit(1);\n        }\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> B;  // dummy B\n    common::reference_cusolver_cholesky<data_type, data_type, false, true>( // do_solver is false, check_potrf_perf is true\n        A, B, info.data(), N, 1, batches, false, Arrange == cusolverdx::col_major, false, batches); // is_lower?, is_column major a? is_column major b?\n\n    //=========================\n    // Compare results\n    //=========================\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"Relative forward error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nint main() { return blocked_potrf(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n#include <cublasdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This is a basic implementation of Cholesky factorization using a single CTA with matrices too large for cuSolverDx's shared memory API.\n// The code uses an out-of-core style, left-looking formulation so that it never needs more than 4 blocks in shared memory at a given point.\n\n// There are a few limitations of this implementation:\n// * Only real-valued types are supported\n// * The matrix size, N, must be a multiple of the block size, NB\n// * Only upper-triangular storage is implemented\n\n\n//////// Helper functions to load and store the diagonal blocks //////\n// Only the upper triangular part is transferred\n// To help L1 cache utilization, the diagonal blocks bypass the L1 cache\n\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\ninline __device__ void load_diagonal_block(const T* A, const int lda, T* As, const int ldas) {\n    const int tid = threadIdx.x;\n    __builtin_assume(tid < NT);\n\n    if constexpr (NT % NB == 0) {\n        constexpr unsigned stride_jj = NT / NB;\n        const unsigned i = tid % NB;\n        const unsigned j = tid / NB;\n        for (int jj = 0; jj < NB; jj += stride_jj) {\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j+jj) : (i >= j+jj);\n            if (is_upper_tri) {\n                As[i + (jj+j)*ldas] = __ldcg(A + i + (jj+j)*lda);\n            }\n        }\n    } else {\n        for (int k = tid; k < NB*NB; k += NT) {\n            unsigned i = k % NB;\n            unsigned j = k / NB;\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j) : (i >= j);\n            if (is_upper_tri) {\n                As[i + j*ldas] = __ldcg(A + i + j*lda);\n            }\n        }\n    }\n    __syncthreads();\n}\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\ninline __device__ void store_diagonal_block(const T* As, const int ldas, T* A, const int lda) {\n    const int tid = threadIdx.x;\n    __builtin_assume(tid < NT);\n\n    __syncthreads();\n    if constexpr (NT % NB == 0) {\n        constexpr unsigned stride_jj = NT / NB;\n        const unsigned i = tid % NB;\n        const unsigned j = tid / NB;\n        for (int jj = 0; jj < NB; jj += stride_jj) {\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j+jj) : (i >= j+jj);\n            if (is_upper_tri) {\n                __stcg(A + i + (jj+j)*lda, As[i + (jj+j)*ldas]);\n            }\n        }\n    } else {\n        for (int k = tid; k < NB*NB; k += NT) {\n            unsigned i = k % NB;\n            unsigned j = k / NB;\n            bool is_upper_tri = (Arrange == cusolverdx::col_major) ? (i <= j) : (i >= j);\n            if (is_upper_tri) {\n                __stcg(A + i + j*lda, As[i + j*ldas]);\n            }\n        }\n    }\n}\n\n//////// Blocked Cholesky Implementation ////////\n\ntemplate<unsigned NB, cusolverdx::arrangement Arrange, class T>\n__device__ T* tile(T* A, unsigned lda, unsigned i, unsigned j) {\n    if (Arrange == cusolverdx::col_major) {\n        return A + i*NB + j*NB*lda;\n    } else {\n        return A + i*NB*lda + j*NB;\n    }\n}\n\ntemplate<unsigned N, unsigned NB, cusolverdx::arrangement Arrange, unsigned NT, class T>\n__global__ __launch_bounds__(NT) void potrf_kernel(T* A, unsigned lda, int* info) {\n    // Assumes A is stored in the upper triangle\n\n    static_assert(N % NB == 0, \"Code currently assumes that N is exactly divisible by the block size\");\n    static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, \"cuSolverDx calls currently hardcoded for real valued type\");\n\n    // Get batch\n    A    += blockIdx.x * N * lda;\n    info += blockIdx.x;\n\n    constexpr unsigned lds = NB;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [sA, sB, sC, sD, sinfo] = cusolverdx::shared_memory::slice<T, T, T, T, int>(\n        shared_mem,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(T), NB*lds,\n        alignof(int) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    #ifdef __CUDA_ARCH__\n        constexpr unsigned Arch = __CUDA_ARCH__;\n    #else\n        constexpr unsigned Arch = 800;\n    #endif\n\n    using POTRF = decltype(cusolverdx::Function<cusolverdx::function::potrf>() + cusolverdx::FillMode<cusolverdx::fill_mode::upper>() + cusolverdx::Size<NB>() +\n                           cusolverdx::Precision<T>() + cusolverdx::Type<cusolverdx::type::real>() + cusolverdx::Arrangement<Arrange>() +\n                           cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n    // TRSM is temporarily being exported from cuSolverDx instead of the cuBLASDx\n    using TRSM = decltype(cusolverdx::Function<cusolverdx::function::trsm>() + cusolverdx::Size<NB, NB, NB>() +\n                          cusolverdx::Precision<T>() + cusolverdx::Type<cusolverdx::type::real>() +\n                          cusolverdx::Side<cusolverdx::side::left>() + cusolverdx::Diag<cusolverdx::diag::non_unit>() +\n                          cusolverdx::TransposeMode<cusolverdx::transpose::transposed>() +\n                          cusolverdx::Arrangement<Arrange, Arrange>() + cusolverdx::FillMode<cusolverdx::fill_mode::upper>() +\n                          cusolverdx::Block() + cusolverdx::BlockDim<NT>() + cusolverdx::SM<Arch>());\n    // Have to use Arrangement to set transposition, to work around cuBLASDx's lack of support for using TransposeMode with a row-major C\n    using GEMM_Arrange = std::conditional_t<Arrange == cusolverdx::col_major,\n                                            cublasdx::Arrangement<cublasdx::row_major, cublasdx::col_major, cublasdx::col_major>,\n                                            cublasdx::Arrangement<cublasdx::col_major, cublasdx::row_major, cublasdx::row_major>>;\n    constexpr unsigned int alignment = ((sizeof(T) * NB * NB) % 16 == 0) ? 16 : sizeof(T);\n    using GEMM = decltype(cublasdx::Size<NB, NB, NB>() + GEMM_Arrange() + cublasdx::Alignment<alignment, alignment, alignment>() +\n                          cublasdx::Precision<T>() + cublasdx::Type<cublasdx::type::real>() + cublasdx::Function<cublasdx::function::MM>() +\n                          cublasdx::Block() + cublasdx::BlockDim<NT>() + cublasdx::SM<Arch>());\n\n    // left-looking, out of core algorithm\n\n    unsigned n_tiles = N / NB;\n    int rinfo = 0;\n\n\n    for (int k = 0; k < n_tiles; ++k) {\n        // Diagonal block\n        auto Akk = tile<NB, Arrange>(A, lda, k, k);\n        load_diagonal_block<NB, Arrange, NT>(Akk, lda, sA, lds);\n        // Schur-complements previous steps\n        for (int i = 0; i < k; ++i) {\n            auto Aik = tile<NB, Arrange>(A, lda, i, k);\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aik, lda, sC, lds);\n            __syncthreads();\n            GEMM().execute(T(-1.0), sC, sC, T(1.0), sA);\n            __syncthreads();\n        }\n        // Factor block\n        POTRF().execute(sA, lds, sinfo);\n        store_diagonal_block<NB, Arrange, NT>(sA, lds, Akk, lda);\n        if (threadIdx.x == 0 && rinfo == 0 && *sinfo != 0) {\n            rinfo = *sinfo + k*NB;\n        }\n\n        // Panel\n        for (int j = k+1; j < n_tiles; ++j) {\n            auto Akj = tile<NB, Arrange>(A, lda, k, j);\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Akj, lda, sB, lds);\n            __syncthreads();\n\n            for (int i = 0; i < k; ++i) {\n                auto Aik = tile<NB, Arrange>(A, lda, i, k);\n                auto Aij = tile<NB, Arrange>(A, lda, i, j);\n                cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aik, lda, sC, lds);\n                cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(Aij, lda, sD, lds);\n                __syncthreads();\n                GEMM().execute(T(-1.0), sC, sD, T(1.0), sB);\n                __syncthreads();\n            }\n\n            TRSM().execute(sA, lds, sB, lds);\n            __syncthreads();\n            cusolverdx::copy_2d<NT, NB, NB, Arrange, 1>(sB, lds, Akj, lda);\n            __syncthreads();\n        }\n    }\n\n\n    if (threadIdx.x == 0) {\n        *info = rinfo;\n    }\n}\n\nint blocked_potrf() {\n\n    // Only float and double are supported\n    using data_type = double;\n\n    constexpr unsigned N       = 512;                   // The matrix size\n    constexpr auto     Arrange = cusolverdx::row_major; // Whether the matrix is column-major or row-major\n    const     unsigned batches = 400;\n\n    // Optimal parameters vary with hardware, N, batches, etc.\n    constexpr unsigned NB      = 32;                    // The blocking size to use\n    constexpr unsigned NT      = 128;                   // The number of threads to use per thread block\n\n    constexpr auto lda        = N;       // this is the leading dimension in global memory for A\n    constexpr auto input_size = lda * N; // input global memory size for A\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size * batches);\n    common::fillup_random_diagonal_dominant_matrix<data_type>(Arrange == cusolverdx::col_major, N, N, A.data(), lda, false, -2, 2, batches); // input A is not symmetric\n\n    std::vector<data_type> L(input_size * batches);\n    std::vector<int>       info(batches);\n    data_type*             d_A    = nullptr; /* device copy of A */\n    int*                   d_info = nullptr; /* error info */\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    size_t smem_size = 4 * NB * NB * sizeof(data_type) + sizeof(int);\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(potrf_kernel<N, NB, Arrange, NT, data_type>), hipFuncAttributeMaxDynamicSharedMemorySize, smem_size));\n    //Invokes kernel\n    auto run_kernel = [&](hipStream_t str) {\n        potrf_kernel<N, NB, Arrange, NT, data_type><<<batches, NT, smem_size, str>>>(d_A, lda, d_info);\n    };\n    auto reset = [&] (hipStream_t str) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, str));\n        CUDA_CHECK_AND_EXIT(hipStreamSynchronize(str));\n    };\n    const unsigned int warmup_repeats = 1;\n    const unsigned int repeats        = 1;\n\n    reset(stream);\n    double ms                     = common::measure::execution(run_kernel, reset, warmup_repeats, repeats, stream) / repeats;\n    double seconds_per_giga_batch = ms / 1e3 / batches * 1e9;\n    double gb_s                   = input_size * sizeof(data_type) * 2 / seconds_per_giga_batch;\n    double gflops                 = common::get_flops_potrf<data_type>(N) / seconds_per_giga_batch;\n\n    common::print_perf(\"Blocked dx potrf\", batches, N, N, 1, gflops, gb_s, ms, NT);\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    for (int i = 0; i < info.size(); ++i) {\n        if (0 > info[i]) {\n            std::printf(\"%d-th parameter is wrong \\n\", -info[i]);\n            exit(1);\n        }\n    }\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    std::vector<data_type> B;  // dummy B\n    common::reference_cusolver_cholesky<data_type, data_type, false, true>( // do_solver is false, check_potrf_perf is true\n        A, B, info.data(), N, 1, batches, false, Arrange == cusolverdx::col_major, false, batches); // is_lower?, is_column major a? is_column major b?\n\n    //=========================\n    // Compare results\n    //=========================\n    const auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), A.size());\n    std::cout << \"Relative forward error of A between cuSolverDx and cuSolver results: \" << total_relative_error << std::endl;\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared with cuSolver API results\" << std::endl;\n        return 0;\n    }\n    std::cout << \"Failure\" << std::endl;\n    return 1;\n}\n\nint main() { return blocked_potrf(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/10_Advanced/blocked_potrf.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute and measure performance of QR factorization on a batched m x n matrix\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, DataType* A_out, const int lda_gmem, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType>(\n        shared_mem,\n        alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n        alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag      = A + one_batch_size_a_gmem * batch_idx;\n    auto A_out_g = A_out + one_batch_size_a_gmem * batch_idx;\n    auto tau_g   = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, tau_s);\n\n    // store data from shared memory to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, A_out_g, lda_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint geqrf_batched_performance() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<128, 32>() + Precision<float>() + Type<type::real>() + Function<geqrf>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 10000;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> tau(min(m, n) * padded_batches, 0);\n    data_type*             d_A     = nullptr;\n    data_type*             d_A_out = nullptr;\n    data_type*             d_tau   = nullptr;\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A_out), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    auto execute_solver = [&](cudaStream_t str) {\n        kernel<Solver, bpb><<<batches, Solver::block_dim, Solver::shared_memory_size, str>>>(d_A, d_A_out, lda, d_tau, batches);\n    };\n\n    const int warmup_repeats = 1;\n    const int kernel_repeats = 5;\n\n    double ms = common::measure::execution(execute_solver, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n\n    double seconds_per_giga_batch = ms / 1e3 / batches * 1e9;\n    double gb_s                   = input_size_a * sizeof(data_type) * 2 / seconds_per_giga_batch;\n    double gflops                 = common::get_flops_geqrf<data_type>(m, n) / seconds_per_giga_batch;\n\n    common::print_perf(\"cuSolverDx-GEQRF\", batches, m, n, 1, gflops, gb_s, ms, Solver::block_dim.x);\n\n    // Compare with cublas batched GEQRF\n    std::vector<data_type> dummyB;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type, false /* do solver */, true /* check blas perf */>(A, dummyB, tau, m, n, 1, batches, is_col_maj_a);\n    \n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A_out));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_tau));\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_performance_functor {\n    int operator()() { return geqrf_batched_performance<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_performance_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cublas_reference_geqrf_gels.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute and measure performance of QR factorization on a batched m x n matrix\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(DataType* A, DataType* A_out, const int lda_gmem, DataType* tau, const unsigned batches) {\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    const auto     one_batch_size_a_gmem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_gmem * n : m * lda_gmem;\n    constexpr auto lda_smem              = Solver::lda;\n    constexpr auto one_batch_size_a_smem = (cusolverdx::arrangement_of_v_a<Solver> == cusolverdx::col_major) ? lda_smem * n : m * lda_smem;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [As, tau_s] = cusolverdx::shared_memory::slice<DataType, DataType>(\n        shared_mem,\n        alignof(DataType), one_batch_size_a_smem * BatchesPerBlock,\n        alignof(DataType) // the size (number of elements) may be omitted for the last pointer\n    );\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n    auto Ag      = A + one_batch_size_a_gmem * batch_idx;\n    auto A_out_g = A_out + one_batch_size_a_gmem * batch_idx;\n    auto tau_g   = tau + min(m, n) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n\n    Solver().execute(As, lda_smem, tau_s);\n\n    // store data from shared memory to global memory\n    common::io<Solver, BatchesPerBlock>::store_a(As, lda_smem, A_out_g, lda_gmem);\n\n    // store tau from shared memory to global memory\n    int thread_id = threadIdx.x + Solver::block_dim.x * (threadIdx.y + Solver::block_dim.y * threadIdx.z);\n    for (int i = thread_id; i < min(m, n) * BatchesPerBlock; i += Solver::max_threads_per_block) {\n        tau_g[i] = tau_s[i];\n    }\n}\n\ntemplate<int Arch>\nint geqrf_batched_performance() {\n\n    using namespace cusolverdx;\n    using Base   = decltype(Size<128, 32>() + Precision<float>() + Type<type::real>() + Function<geqrf>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n    constexpr auto n = Solver::n_size;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<Solver> == arrangement::col_major;\n\n    const auto     lda          = is_col_maj_a ? m : n;\n    constexpr auto input_size_a = m * n;\n\n    const auto batches        = 10000;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, n, A.data(), lda, false, false, 2, 4, batches); // not symmetric and not diagonally dominant\n\n    std::vector<data_type> tau(min(m, n) * padded_batches, 0);\n    data_type*             d_A     = nullptr;\n    data_type*             d_A_out = nullptr;\n    data_type*             d_tau   = nullptr;\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A_out), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_tau), sizeof(data_type) * tau.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    auto execute_solver = [&](hipStream_t str) {\n        kernel<Solver, bpb><<<batches, Solver::block_dim, Solver::shared_memory_size, str>>>(d_A, d_A_out, lda, d_tau, batches);\n    };\n\n    const int warmup_repeats = 1;\n    const int kernel_repeats = 5;\n\n    double ms = common::measure::execution(execute_solver, warmup_repeats, kernel_repeats, stream) / kernel_repeats;\n\n    double seconds_per_giga_batch = ms / 1e3 / batches * 1e9;\n    double gb_s                   = input_size_a * sizeof(data_type) * 2 / seconds_per_giga_batch;\n    double gflops                 = common::get_flops_geqrf<data_type>(m, n) / seconds_per_giga_batch;\n\n    common::print_perf(\"cuSolverDx-GEQRF\", batches, m, n, 1, gflops, gb_s, ms, Solver::block_dim.x);\n\n    // Compare with cublas batched GEQRF\n    std::vector<data_type> dummyB;\n    common::reference_cublas_geqrf_gels<data_type, cuda_data_type, false /* do solver */, true /* check blas perf */>(A, dummyB, tau, m, n, 1, batches, is_col_maj_a);\n    \n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_A_out));\n    CUDA_CHECK_AND_EXIT(hipFree(d_tau));\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct geqrf_batched_performance_functor {\n    int operator()() { return geqrf_batched_performance<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<geqrf_batched_performance_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/09_Performance/geqrf_batched_performance.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx block execution API to compute eigenvalues, and optionally eigenvectors, of a batched symmetric tridiagonal matrix A.\n// The results are verified by comparing A * V with V * diagm(lambda), where V is the eigenvectors and diagm(lambda) is the diagonal matrix of eigenvalues.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* d, DataType* e, DataType* V, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    // if compute_vectors is false, V_s is a dummy null pointer\n    auto [d_s, e_s, V_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(\n            shared_mem, alignof(DataType), m * BatchesPerBlock, alignof(DataType), (m - 1) * BatchesPerBlock, alignof(DataType));\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m - 1) * batch_idx;\n    common::io<Solver, BatchesPerBlock>::load_a<m, 1, cusolverdx::col_major>(this_d, m, d_s, m);\n    common::io<Solver, BatchesPerBlock>::load_a<m - 1, 1, cusolverdx::col_major>(this_e, m - 1, e_s, m - 1);\n\n    DataType* this_V = nullptr;\n    if constexpr (Solver::job == cusolverdx::job::no_vectors) {\n        Solver().execute(d_s, e_s, &info[batch_idx]);\n    } else {\n        this_V = V + m * m * batch_idx;\n        if constexpr (Solver::job == cusolverdx::job::multiply_vectors) {\n            common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::a_arrangement>(this_V, m, V_s, Solver::lda);\n        }\n\n        Solver().execute(d_s, e_s, V_s, Solver::lda, &info[batch_idx]);\n    }\n\n    common::io<Solver, BatchesPerBlock>::store_a<m, 1, cusolverdx::col_major>(d_s, m, this_d, m);\n    common::io<Solver, BatchesPerBlock>::store_a<m - 1, 1, cusolverdx::col_major>(e_s, m - 1, this_e, m - 1);\n\n    if constexpr (Solver::job != cusolverdx::job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(V_s, Solver::lda, this_V, m);\n    }\n}\n\ntemplate<int Arch>\nint htev_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<64>() + Precision<float>() + Type<type::real>() + Function<htev>() + SM<Arch>() + Block());\n    // Job can be job::no_vectors, job::all_vectors or job::multiply_vectors\n    // If job is job::all_vectors, then Vs only need to be allocated, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix\n    // If job is job::multiply_vectors, then Vs need to be allocated and with initial values, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix multiplied by the input of Vs\n    // job::multiply_vectors is useful when a symmetric matrix is reduced to a tridiagonal matrix and the householder reflectors are saved in the input of A, then the eigenvectors of the reduced matrix are needed to be multiplied back to get the eigenvectors of the original symmetric matrix\n    //using Solver = decltype(Base() + Job<job::all_vectors>() + Arrangement<row_major>());\n    //using Solver = decltype(Base() + Job<job::multiply_vectors>() + Arrangement<row_major>());\n    using Solver = decltype(Base() + Job<job::no_vectors>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using \" << bpb << \" Batches per block \\n\";\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool                  compute_vectors = Solver::job != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_v    = Solver::a_arrangement == arrangement::col_major; // only useful when eigenvectors are computed\n\n    const auto batches        = 1000;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> d(m * padded_batches), d_input(m * padded_batches);\n    std::vector<data_type> e((m - 1) * padded_batches), e_input((m - 1) * padded_batches);\n    // Fill the diagonal and subdiagonal elements of the tridiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, m - 1, 1, e_input.data(), m - 1, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> V;\n    if constexpr (compute_vectors) {\n        V.resize(m * m * padded_batches, 0);\n        // Set input V to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    V[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    data_type*       d_V    = nullptr;\n    int*             d_info = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_V), sizeof(data_type) * V.size()));\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_V, V.data(), sizeof(data_type) * V.size(), cudaMemcpyHostToDevice, stream));\n        }\n    }\n\n    const auto shared_memory_size = Solver::shared_memory_size;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb>, cudaFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    std::cout << \"shared_memory_size = \" << shared_memory_size << \" bytes\" << std::endl;\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_d, d_e, d_V, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(V.data(), d_V, sizeof(data_type) * V.size(), cudaMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HTEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_v>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_e));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    if constexpr (compute_vectors)\n        CUDA_CHECK_AND_EXIT(cudaFree(d_V));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n\n    // check the computed eigenvalues and eigenvectors of the tridiagonal matrix by comparing A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n\n        std::vector<data_type> A_v(m * m);\n        std::vector<data_type> lambda_v(m * m);\n        constexpr unsigned     col_stride = is_col_maj_v ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_v ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* A_db     = d_input.data() + b * m;\n            data_type* A_eb     = e_input.data() + b * (m - 1);\n            data_type* Vb       = V.data() + b * m * m;\n            data_type* lambda_b = d.data() + b * m;\n\n            // Compute A * V (tridiagonal matrix-matrix multiplication) and V * diagm(lambda) (scalar-vector multiplication)\n            A_v.assign(m * m, 0);\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] = lambda_b[col] * Vb[row * row_stride + col * col_stride];\n\n                    // Tridiagonal matrix multiplication: A[row][k] * V[k][col]\n                    // For tridiagonal matrix, only non-zero elements are:\n                    // - diagonal: A[row][row] = A_db[row]\n                    // - subdiagonal: A[row][row-1] = A_eb[row-1] (if row > 0)\n                    // - superdiagonal: A[row][row+1] = A_eb[row] (if row < m-1)\n\n                    // Diagonal element\n                    A_v[row * row_stride + col * col_stride] += A_db[row] * Vb[row * row_stride + col * col_stride];\n\n                    // Subdiagonal element (if not first row)\n                    if (row > 0) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row - 1] * Vb[(row - 1) * row_stride + col * col_stride];\n                    }\n\n                    // Superdiagonal element (if not last row)\n                    if (row < m - 1) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row] * Vb[(row + 1) * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HTEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n\n        std::cout << \"Success in verifying eigenvectors with A * V = V * diagm(lambda)\" << std::endl;\n    } else {\n        std::cout << \"Success in computing eigenvalues\" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct htev_batched_block_functor {\n    int operator()() { return htev_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<htev_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx block execution API to compute eigenvalues, and optionally eigenvectors, of a batched symmetric tridiagonal matrix A.\n// The results are verified by comparing A * V with V * diagm(lambda), where V is the eigenvectors and diagm(lambda) is the diagonal matrix of eigenvalues.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, typename DataType = typename Solver::a_data_type>\n__global__ __launch_bounds__(Solver::max_threads_per_block) void kernel(\n        DataType* d, DataType* e, DataType* V, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    // if compute_vectors is false, V_s is a dummy null pointer\n    auto [d_s, e_s, V_s] = cusolverdx::shared_memory::slice<DataType, DataType, DataType>(\n            shared_mem, alignof(DataType), m * BatchesPerBlock, alignof(DataType), (m - 1) * BatchesPerBlock, alignof(DataType));\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m - 1) * batch_idx;\n    common::io<Solver, BatchesPerBlock>::load_a<m, 1, cusolverdx::col_major>(this_d, m, d_s, m);\n    common::io<Solver, BatchesPerBlock>::load_a<m - 1, 1, cusolverdx::col_major>(this_e, m - 1, e_s, m - 1);\n\n    DataType* this_V = nullptr;\n    if constexpr (Solver::job == cusolverdx::job::no_vectors) {\n        Solver().execute(d_s, e_s, &info[batch_idx]);\n    } else {\n        this_V = V + m * m * batch_idx;\n        if constexpr (Solver::job == cusolverdx::job::multiply_vectors) {\n            common::io<Solver, BatchesPerBlock>::load_a<m, m, Solver::a_arrangement>(this_V, m, V_s, Solver::lda);\n        }\n\n        Solver().execute(d_s, e_s, V_s, Solver::lda, &info[batch_idx]);\n    }\n\n    common::io<Solver, BatchesPerBlock>::store_a<m, 1, cusolverdx::col_major>(d_s, m, this_d, m);\n    common::io<Solver, BatchesPerBlock>::store_a<m - 1, 1, cusolverdx::col_major>(e_s, m - 1, this_e, m - 1);\n\n    if constexpr (Solver::job != cusolverdx::job::no_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(V_s, Solver::lda, this_V, m);\n    }\n}\n\ntemplate<int Arch>\nint htev_batched_block() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<64>() + Precision<float>() + Type<type::real>() + Function<htev>() + SM<Arch>() + Block());\n    // Job can be job::no_vectors, job::all_vectors or job::multiply_vectors\n    // If job is job::all_vectors, then Vs only need to be allocated, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix\n    // If job is job::multiply_vectors, then Vs need to be allocated and with initial values, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix multiplied by the input of Vs\n    // job::multiply_vectors is useful when a symmetric matrix is reduced to a tridiagonal matrix and the householder reflectors are saved in the input of A, then the eigenvectors of the reduced matrix are needed to be multiplied back to get the eigenvectors of the original symmetric matrix\n    //using Solver = decltype(Base() + Job<job::all_vectors>() + Arrangement<row_major>());\n    //using Solver = decltype(Base() + Job<job::multiply_vectors>() + Arrangement<row_major>());\n    using Solver = decltype(Base() + Job<job::no_vectors>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using \" << bpb << \" Batches per block \\n\";\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool                  compute_vectors = Solver::job != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_v    = Solver::a_arrangement == arrangement::col_major; // only useful when eigenvectors are computed\n\n    const auto batches        = 1000;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> d(m * padded_batches), d_input(m * padded_batches);\n    std::vector<data_type> e((m - 1) * padded_batches), e_input((m - 1) * padded_batches);\n    // Fill the diagonal and subdiagonal elements of the tridiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, m - 1, 1, e_input.data(), m - 1, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> V;\n    if constexpr (compute_vectors) {\n        V.resize(m * m * padded_batches, 0);\n        // Set input V to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    V[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    data_type*       d_V    = nullptr;\n    int*             d_info = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_V), sizeof(data_type) * V.size()));\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_V, V.data(), sizeof(data_type) * V.size(), hipMemcpyHostToDevice, stream));\n        }\n    }\n\n    const auto shared_memory_size = Solver::shared_memory_size;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb>), hipFuncAttributeMaxDynamicSharedMemorySize, shared_memory_size));\n    std::cout << \"shared_memory_size = \" << shared_memory_size << \" bytes\" << std::endl;\n\n    //Invokes kernel\n    kernel<Solver, bpb><<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_d, d_e, d_V, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(V.data(), d_V, sizeof(data_type) * V.size(), hipMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HTEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_v>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_e));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    if constexpr (compute_vectors)\n        CUDA_CHECK_AND_EXIT(hipFree(d_V));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n\n    // check the computed eigenvalues and eigenvectors of the tridiagonal matrix by comparing A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n\n        std::vector<data_type> A_v(m * m);\n        std::vector<data_type> lambda_v(m * m);\n        constexpr unsigned     col_stride = is_col_maj_v ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_v ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* A_db     = d_input.data() + b * m;\n            data_type* A_eb     = e_input.data() + b * (m - 1);\n            data_type* Vb       = V.data() + b * m * m;\n            data_type* lambda_b = d.data() + b * m;\n\n            // Compute A * V (tridiagonal matrix-matrix multiplication) and V * diagm(lambda) (scalar-vector multiplication)\n            A_v.assign(m * m, 0);\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] = lambda_b[col] * Vb[row * row_stride + col * col_stride];\n\n                    // Tridiagonal matrix multiplication: A[row][k] * V[k][col]\n                    // For tridiagonal matrix, only non-zero elements are:\n                    // - diagonal: A[row][row] = A_db[row]\n                    // - subdiagonal: A[row][row-1] = A_eb[row-1] (if row > 0)\n                    // - superdiagonal: A[row][row+1] = A_eb[row] (if row < m-1)\n\n                    // Diagonal element\n                    A_v[row * row_stride + col * col_stride] += A_db[row] * Vb[row * row_stride + col * col_stride];\n\n                    // Subdiagonal element (if not first row)\n                    if (row > 0) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row - 1] * Vb[(row - 1) * row_stride + col * col_stride];\n                    }\n\n                    // Superdiagonal element (if not last row)\n                    if (row < m - 1) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row] * Vb[(row + 1) * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HTEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n\n        std::cout << \"Success in verifying eigenvectors with A * V = V * diagm(lambda)\" << std::endl;\n    } else {\n        std::cout << \"Success in computing eigenvalues\" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct htev_batched_block_functor {\n    int operator()() { return htev_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<htev_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/04_Symmetric_Eigenvalues/htev_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to compute eigenvalues, and optionally eigenvectors, of a batched symmetric tridiagonal matrix A.\n// The results are verified by comparing A * V with V * diagm(lambda), where V is the eigenvectors and diagm(lambda) is the diagonal matrix of eigenvalues.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* d, DataType* e, DataType* V, typename Solver::status_type* info, const unsigned batches) {\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m - 1) * batch_idx;\n\n    DataType* this_V = nullptr;\n    if constexpr (Solver::job == cusolverdx::job::no_vectors) {\n        Solver().execute(this_d, this_e, &info[batch_idx]);\n    } else {\n        this_V = V + m * m * batch_idx;\n\n        Solver().execute(this_d, this_e, this_V, &info[batch_idx]);\n    }\n}\n\ntemplate<int Arch>\nint htev_batched_thread() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<24>() + Precision<float>() + Type<type::real>() + Function<htev>() + SM<Arch>() + Thread());\n    // Job can be job::no_vectors, job::all_vectors or job::multiply_vectors\n    // If job is job::all_vectors, then Vs only need to be allocated, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix\n    // If job is job::multiply_vectors, then Vs need to be allocated and with initial values, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix multiplied by the input of Vs\n    // job::multiply_vectors is useful when a symmetric matrix is reduced to a tridiagonal matrix and the householder reflectors are saved in the input of A, then the eigenvectors of the reduced matrix are needed to be multiplied back to get the eigenvectors of the original symmetric matrix\n\n    // Example 1: compute all eigenvectors of the symmetric tridiagonal matrix\n    //using Solver = decltype(Base() + Job<job::all_vectors>() + Arrangement<row_major>());\n\n    // Example 2: compute eigenvectors of the symmetric tridiagonal matrix by multiplying the input eigenvectors with the eigenvectors of the tridiagonal matrix\n    using Solver = decltype(Base() + Job<job::multiply_vectors>() + Arrangement<row_major>());\n\n    // Example 3: compute eigenvalues only\n    //using Solver = decltype(Base() + Job<job::no_vectors>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool                  compute_vectors = Solver::job != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_v    = Solver::a_arrangement == arrangement::col_major; // only useful when eigenvectors are computed\n\n    const auto batches = 200;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> d(m * batches), d_input(m * batches);\n    std::vector<data_type> e((m - 1) * batches), e_input((m - 1) * batches);\n    // Fill the diagonal and subdiagonal elements of the tridiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, m - 1, 1, e_input.data(), m - 1, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> V;\n    if constexpr (compute_vectors) {\n        V.resize(m * m * batches, 0);\n        // Set input V to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    V[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    std::vector<int> info(batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    data_type*       d_V    = nullptr;\n    int*             d_info = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_V), sizeof(data_type) * V.size()));\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_V, V.data(), sizeof(data_type) * V.size(), cudaMemcpyHostToDevice, stream));\n        }\n    }\n\n    //Invokes kernel\n    const auto nthreads = compute_vectors ? 32 : 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_d, d_e, d_V, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(V.data(), d_V, sizeof(data_type) * V.size(), cudaMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HTEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_v>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_d));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_e));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    if constexpr (compute_vectors)\n        CUDA_CHECK_AND_EXIT(cudaFree(d_V));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n\n    // check the computed eigenvalues and eigenvectors of the tridiagonal matrix by comparing A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n\n        std::vector<data_type> A_v(m * m);\n        std::vector<data_type> lambda_v(m * m);\n        constexpr unsigned     col_stride = is_col_maj_v ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_v ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* A_db     = d_input.data() + b * m;\n            data_type* A_eb     = e_input.data() + b * (m - 1);\n            data_type* Vb       = V.data() + b * m * m;\n            data_type* lambda_b = d.data() + b * m;\n\n            // Compute A * V (tridiagonal matrix-matrix multiplication) and V * diagm(lambda) (scalar-vector multiplication)\n            A_v.assign(m * m, 0);\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] = lambda_b[col] * Vb[row * row_stride + col * col_stride];\n\n                    // Tridiagonal matrix multiplication: A[row][k] * V[k][col]\n                    // For tridiagonal matrix, only non-zero elements are:\n                    // - diagonal: A[row][row] = A_db[row]\n                    // - subdiagonal: A[row][row-1] = A_eb[row-1] (if row > 0)\n                    // - superdiagonal: A[row][row+1] = A_eb[row] (if row < m-1)\n\n                    // Diagonal element\n                    A_v[row * row_stride + col * col_stride] += A_db[row] * Vb[row * row_stride + col * col_stride];\n\n                    // Subdiagonal element (if not first row)\n                    if (row > 0) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row - 1] * Vb[(row - 1) * row_stride + col * col_stride];\n                    }\n\n                    // Superdiagonal element (if not last row)\n                    if (row < m - 1) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row] * Vb[(row + 1) * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HTEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n\n        std::cout << \"Success in verifying eigenvectors with A * V = V * diagm(lambda)\" << std::endl;\n    } else {\n        std::cout << \"Success in computing eigenvalues\" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct htev_batched_thread_functor {\n    int operator()() { return htev_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<htev_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to compute eigenvalues, and optionally eigenvectors, of a batched symmetric tridiagonal matrix A.\n// The results are verified by comparing A * V with V * diagm(lambda), where V is the eigenvectors and diagm(lambda) is the diagonal matrix of eigenvalues.\n\ntemplate<class Solver, typename DataType = typename Solver::a_data_type>\n__global__ void kernel(DataType* d, DataType* e, DataType* V, typename Solver::status_type* info, const unsigned batches) {\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m = Solver::m_size;\n\n    auto this_d = d + m * batch_idx;\n    auto this_e = e + (m - 1) * batch_idx;\n\n    DataType* this_V = nullptr;\n    if constexpr (Solver::job == cusolverdx::job::no_vectors) {\n        Solver().execute(this_d, this_e, &info[batch_idx]);\n    } else {\n        this_V = V + m * m * batch_idx;\n\n        Solver().execute(this_d, this_e, this_V, &info[batch_idx]);\n    }\n}\n\ntemplate<int Arch>\nint htev_batched_thread() {\n\n    using namespace cusolverdx;\n    using Base = decltype(Size<24>() + Precision<float>() + Type<type::real>() + Function<htev>() + SM<Arch>() + Thread());\n    // Job can be job::no_vectors, job::all_vectors or job::multiply_vectors\n    // If job is job::all_vectors, then Vs only need to be allocated, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix\n    // If job is job::multiply_vectors, then Vs need to be allocated and with initial values, and on exit, Vs will be overwritten with the eigenvectors of the tridiagonal matrix multiplied by the input of Vs\n    // job::multiply_vectors is useful when a symmetric matrix is reduced to a tridiagonal matrix and the householder reflectors are saved in the input of A, then the eigenvectors of the reduced matrix are needed to be multiplied back to get the eigenvectors of the original symmetric matrix\n\n    // Example 1: compute all eigenvectors of the symmetric tridiagonal matrix\n    //using Solver = decltype(Base() + Job<job::all_vectors>() + Arrangement<row_major>());\n\n    // Example 2: compute eigenvectors of the symmetric tridiagonal matrix by multiplying the input eigenvectors with the eigenvectors of the tridiagonal matrix\n    using Solver = decltype(Base() + Job<job::multiply_vectors>() + Arrangement<row_major>());\n\n    // Example 3: compute eigenvalues only\n    //using Solver = decltype(Base() + Job<job::no_vectors>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool                  compute_vectors = Solver::job != job::no_vectors;\n    [[maybe_unused]] constexpr bool is_col_maj_v    = Solver::a_arrangement == arrangement::col_major; // only useful when eigenvectors are computed\n\n    const auto batches = 200;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> d(m * batches), d_input(m * batches);\n    std::vector<data_type> e((m - 1) * batches), e_input((m - 1) * batches);\n    // Fill the diagonal and subdiagonal elements of the tridiagonal matrix\n    common::fillup_random_matrix<data_type>(true, m, 1, d_input.data(), m, false /*symmetric*/, false /*diagonal dominant*/, 2, 4, batches);\n    common::fillup_random_matrix<data_type>(true, m - 1, 1, e_input.data(), m - 1, false /*symmetric*/, false /*diagonal dominant*/, -5, 20, batches);\n\n    std::vector<data_type> V;\n    if constexpr (compute_vectors) {\n        V.resize(m * m * batches, 0);\n        // Set input V to be the identity matrix, if job is job::multiply_vectors\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    V[row + row * m + b * m * m] = 1;\n                }\n            }\n        }\n    }\n    std::vector<int> info(batches, 0);\n    data_type*       d_d    = nullptr;\n    data_type*       d_e    = nullptr;\n    data_type*       d_V    = nullptr;\n    int*             d_info = nullptr;\n\n    // // Uncomment below to print A matrix\n    // printf(\"d_input = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d_input.data(), batches);\n    // printf(\"e_input = \\n\");\n    // common::print_matrix<data_type, m-1, 1, m-1, true>(e_input.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_d), sizeof(data_type) * d.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_e), sizeof(data_type) * e.size()));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_d, d_input.data(), sizeof(data_type) * d_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_e, e_input.data(), sizeof(data_type) * e_input.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_V), sizeof(data_type) * V.size()));\n        if constexpr (job_of_v<Solver> == job::multiply_vectors) {\n            CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_V, V.data(), sizeof(data_type) * V.size(), hipMemcpyHostToDevice, stream));\n        }\n    }\n\n    //Invokes kernel\n    const auto nthreads = compute_vectors ? 32 : 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_d, d_e, d_V, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d.data(), d_d, sizeof(data_type) * d.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(V.data(), d_V, sizeof(data_type) * V.size(), hipMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HTEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // // Uncomment below to print results after cuSolverDx execute\n    // printf(\"d = \\n\");\n    // common::print_matrix<data_type, m, 1, m, true>(d.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_v>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_d));\n    CUDA_CHECK_AND_EXIT(hipFree(d_e));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    if constexpr (compute_vectors)\n        CUDA_CHECK_AND_EXIT(hipFree(d_V));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n\n    // check the computed eigenvalues and eigenvectors of the tridiagonal matrix by comparing A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n\n        std::vector<data_type> A_v(m * m);\n        std::vector<data_type> lambda_v(m * m);\n        constexpr unsigned     col_stride = is_col_maj_v ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_v ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* A_db     = d_input.data() + b * m;\n            data_type* A_eb     = e_input.data() + b * (m - 1);\n            data_type* Vb       = V.data() + b * m * m;\n            data_type* lambda_b = d.data() + b * m;\n\n            // Compute A * V (tridiagonal matrix-matrix multiplication) and V * diagm(lambda) (scalar-vector multiplication)\n            A_v.assign(m * m, 0);\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] = lambda_b[col] * Vb[row * row_stride + col * col_stride];\n\n                    // Tridiagonal matrix multiplication: A[row][k] * V[k][col]\n                    // For tridiagonal matrix, only non-zero elements are:\n                    // - diagonal: A[row][row] = A_db[row]\n                    // - subdiagonal: A[row][row-1] = A_eb[row-1] (if row > 0)\n                    // - superdiagonal: A[row][row+1] = A_eb[row] (if row < m-1)\n\n                    // Diagonal element\n                    A_v[row * row_stride + col * col_stride] += A_db[row] * Vb[row * row_stride + col * col_stride];\n\n                    // Subdiagonal element (if not first row)\n                    if (row > 0) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row - 1] * Vb[(row - 1) * row_stride + col * col_stride];\n                    }\n\n                    // Superdiagonal element (if not last row)\n                    if (row < m - 1) {\n                        A_v[row * row_stride + col * col_stride] += A_eb[row] * Vb[(row + 1) * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HTEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n\n        std::cout << \"Success in verifying eigenvectors with A * V = V * diagm(lambda)\" << std::endl;\n    } else {\n        std::cout << \"Success in computing eigenvalues\" << std::endl;\n    }\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct htev_batched_thread_functor {\n    int operator()() { return htev_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<htev_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/04_Symmetric_Eigenvalues/htev_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_heev.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute eigenvalues, and optionally eigenvectors, of a batched m x m symmetric matrix A.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXsyevBatched and cusolverDn<t>syevjBatched.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, bool compute_vectors = false, typename DataType = typename Solver::a_data_type,\n        typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(\n        DataType* A, const int lda_gmem, PrecisionType* lambda, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto one_batch_size_a_gmem = m * m;\n    constexpr auto lda_smem              = Solver::lda;\n    const auto     one_batch_size_a_smem = lda_smem * m;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [A_s, lambda_s, workspace_s] = cusolverdx::shared_memory::slice<DataType, PrecisionType, DataType>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(PrecisionType),\n            m * BatchesPerBlock, // lambda\n            alignof(DataType) // workspace\n    );\n\n    auto A_g      = A + one_batch_size_a_gmem * batch_idx;\n    auto lambda_g = lambda + m * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, m>(A_g, m, A_s, lda_smem);\n\n    Solver().execute(A_s, lda_smem, lambda_s, workspace_s, &info[batch_idx]);\n\n    // Store results back to global memory\n    cusolverdx::copy_2d<Solver, m, 1, cusolverdx::arrangement::col_major, BatchesPerBlock, PrecisionType>(lambda_s, m, lambda_g, m);\n    if constexpr (compute_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(A_s, lda_smem, A_g, m);\n    }\n}\n\ntemplate<int Arch>\nint heev_batched_block() {\n\n    using namespace cusolverdx;\n    // Job can be either job::overwrite_vectors or job::no_vectors\n    using Base   = decltype(Size<16>() + Precision<float>() + Type<type::complex>() + Function<heev>() + FillMode<fill_mode::upper>() +\n                          Arrangement<arrangement::row_major>() + Job<job::overwrite_vectors>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool is_col_maj_a    = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_lower_fill   = fill_mode_of_v<Solver> == fill_mode::lower;\n    constexpr bool compute_vectors = job_of_v<Solver> != job::no_vectors;\n\n    const auto     lda_gmem     = m;\n    constexpr auto input_size_a = m * m;\n\n    const auto batches        = 5;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    // Fill with symmetric matrix data\n    // If the input matrix is not symmetric, it is OK because the function only processes the specified part of the matrix and leaves the rest unchanged\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, m, A.data(), lda_gmem, true /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<data_type> V, A_input;\n    if constexpr (compute_vectors) {\n        A_input.assign(A.begin(), A.end());\n        V.resize(input_size_a * padded_batches);\n    }\n    std::vector<precision_type> lambda(m * padded_batches, 0);\n    std::vector<precision_type> lambda_ref(m * padded_batches, 0);\n    std::vector<int>            info(padded_batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_lambda    = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_lambda), sizeof(precision_type) * lambda.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel<Solver, bpb, compute_vectors>, cudaFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb, compute_vectors>\n            <<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda_gmem, d_lambda, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(lambda.data(), d_lambda, sizeof(precision_type) * lambda.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(V.data(), d_A, sizeof(data_type) * V.size(), cudaMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HEEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_a>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_lambda));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_heev<data_type, cuda_data_type, precision_type, false /*use_syevj*/, false /*check_perf*/>(\n            A, lambda_ref, info.data(), m, padded_batches, is_lower_fill, is_col_maj_a, compute_vectors, batches);\n\n    const auto total_relative_error_lambda = common::check_error<precision_type, precision_type>(lambda.data(), lambda_ref.data(), batches * m);\n    std::cout << \"HEEV: relative error of lambda between cuSolverDx and cuSolver reference results: \" << total_relative_error_lambda << std::endl;\n\n    // Uncomment below to print results after cuSolver reference execute\n    // printf(\"lambda_ref = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<precision_type>(total_relative_error_lambda)) {\n        std::cout << \"Failure compared with cuSolver API results lambda\" << std::endl;\n        //Print out lambda for debugging\n        for (int i = 0; i < m * batches; ++i) {\n            if (abs(lambda[i] - lambda_ref[i]) / abs(lambda_ref[i]) > 1e-05) {\n                printf(\"lambda[%d] = %10.3f, lambda_ref[%d] = %10.3f  differ \\n\", i, lambda[i], i, lambda_ref[i]);\n            }\n        }\n        return 1;\n    }\n\n    // check eigenvectors. As the eigenvectors are not unique, instead of comparing the eigenvectors directly with cuSolver reference, we compare A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n        // cuSolverDx and cuSolver sets the diagonal elements of input Hermitian matrix A to be real. So before computing A * V we need to set the diagonal elements of A to be real, if case they are not.\n        if constexpr (common::is_complex<data_type>()) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    A_input[row + row * m + b * input_size_a].y = 0;\n                }\n            }\n        }\n\n        std::vector<data_type> A_v(input_size_a);\n        std::vector<data_type> lambda_v(input_size_a);\n        constexpr unsigned     col_stride = is_col_maj_a ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_a ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* Ab = A_input.data() + b * input_size_a;\n            data_type* Vb = V.data() + b * input_size_a; // Use cuSolverDx eigenvectors\n            //data_type* Vb = A.data() + b * m * m; // Use cuSolver reference eigenvectors\n            precision_type* lambda_b = lambda.data() + b * m;\n\n            // Compute A * V (matrix-matrix multiplication) and V * diagm(lambda) (practically scalar-vector multiplication)\n            A_v.assign(m * m, common::convert<data_type, precision_type>(0.));\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] =\n                            common::convert<data_type, precision_type>(lambda_b[col]) * Vb[row * row_stride + col * col_stride];\n                    for (int k = 0; k < m; ++k) {\n                        A_v[row * row_stride + col * col_stride] += Ab[row * row_stride + k * col_stride] * Vb[k * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HEEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n    }\n\n    std::cout << \"Success compared eigenvalues with the cuSolver API results, and if vectors are computed, verified eigenvectors with A * V - V * diagm(lambda)\"\n              << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct heev_batched_block_functor {\n    int operator()() { return heev_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<heev_batched_block_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_heev.hpp\"\n\n// This example demonstrates how to use cuSolverDx API to compute eigenvalues, and optionally eigenvectors, of a batched m x m symmetric matrix A.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXsyevBatched and cusolverDn<t>syevjBatched.\n\ntemplate<class Solver, unsigned int BatchesPerBlock, bool compute_vectors = false, typename DataType = typename Solver::a_data_type,\n        typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(\n        DataType* A, const int lda_gmem, PrecisionType* lambda, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto one_batch_size_a_gmem = m * m;\n    constexpr auto lda_smem              = Solver::lda;\n    const auto     one_batch_size_a_smem = lda_smem * m;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    extern __shared__ __align__(16) cusolverdx::byte shared_mem[];\n    // Slice shared memory into pointers\n    auto [A_s, lambda_s, workspace_s] = cusolverdx::shared_memory::slice<DataType, PrecisionType, DataType>(\n            shared_mem, alignof(DataType), one_batch_size_a_smem * BatchesPerBlock, alignof(PrecisionType),\n            m * BatchesPerBlock, // lambda\n            alignof(DataType) // workspace\n    );\n\n    auto A_g      = A + one_batch_size_a_gmem * batch_idx;\n    auto lambda_g = lambda + m * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<Solver, BatchesPerBlock>::load_a<m, m>(A_g, m, A_s, lda_smem);\n\n    Solver().execute(A_s, lda_smem, lambda_s, workspace_s, &info[batch_idx]);\n\n    // Store results back to global memory\n    cusolverdx::copy_2d<Solver, m, 1, cusolverdx::arrangement::col_major, BatchesPerBlock, PrecisionType>(lambda_s, m, lambda_g, m);\n    if constexpr (compute_vectors) {\n        common::io<Solver, BatchesPerBlock>::store_a<m, m, Solver::a_arrangement>(A_s, lda_smem, A_g, m);\n    }\n}\n\ntemplate<int Arch>\nint heev_batched_block() {\n\n    using namespace cusolverdx;\n    // Job can be either job::overwrite_vectors or job::no_vectors\n    using Base   = decltype(Size<16>() + Precision<float>() + Type<type::complex>() + Function<heev>() + FillMode<fill_mode::upper>() +\n                          Arrangement<arrangement::row_major>() + Job<job::overwrite_vectors>() + SM<Arch>() + Block());\n    using Solver = decltype(Base() + BatchesPerBlock<Base::suggested_batches_per_block>());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr unsigned bpb = Solver::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Suggested BlockDim = \" << Solver::suggested_block_dim.x << std::endl;\n    std::cout << \"BlockDim Used = \" << Solver::block_dim.x << std::endl;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool is_col_maj_a    = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_lower_fill   = fill_mode_of_v<Solver> == fill_mode::lower;\n    constexpr bool compute_vectors = job_of_v<Solver> != job::no_vectors;\n\n    const auto     lda_gmem     = m;\n    constexpr auto input_size_a = m * m;\n\n    const auto batches        = 5;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * padded_batches);\n    // Fill with symmetric matrix data\n    // If the input matrix is not symmetric, it is OK because the function only processes the specified part of the matrix and leaves the rest unchanged\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, m, A.data(), lda_gmem, true /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<data_type> V, A_input;\n    if constexpr (compute_vectors) {\n        A_input.assign(A.begin(), A.end());\n        V.resize(input_size_a * padded_batches);\n    }\n    std::vector<precision_type> lambda(m * padded_batches, 0);\n    std::vector<precision_type> lambda_ref(m * padded_batches, 0);\n    std::vector<int>            info(padded_batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_lambda    = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_lambda), sizeof(precision_type) * lambda.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel<Solver, bpb, compute_vectors>), hipFuncAttributeMaxDynamicSharedMemorySize, Solver::shared_memory_size));\n\n    //Invokes kernel\n    kernel<Solver, bpb, compute_vectors>\n            <<<padded_batches / bpb, Solver::block_dim, Solver::shared_memory_size, stream>>>(d_A, lda_gmem, d_lambda, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(lambda.data(), d_lambda, sizeof(precision_type) * lambda.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(V.data(), d_A, sizeof(data_type) * V.size(), hipMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HEEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_a>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_lambda));\n    CUDA_CHECK_AND_EXIT(hipFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_heev<data_type, cuda_data_type, precision_type, false /*use_syevj*/, false /*check_perf*/>(\n            A, lambda_ref, info.data(), m, padded_batches, is_lower_fill, is_col_maj_a, compute_vectors, batches);\n\n    const auto total_relative_error_lambda = common::check_error<precision_type, precision_type>(lambda.data(), lambda_ref.data(), batches * m);\n    std::cout << \"HEEV: relative error of lambda between cuSolverDx and cuSolver reference results: \" << total_relative_error_lambda << std::endl;\n\n    // Uncomment below to print results after cuSolver reference execute\n    // printf(\"lambda_ref = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<precision_type>(total_relative_error_lambda)) {\n        std::cout << \"Failure compared with cuSolver API results lambda\" << std::endl;\n        //Print out lambda for debugging\n        for (int i = 0; i < m * batches; ++i) {\n            if (abs(lambda[i] - lambda_ref[i]) / abs(lambda_ref[i]) > 1e-05) {\n                printf(\"lambda[%d] = %10.3f, lambda_ref[%d] = %10.3f  differ \\n\", i, lambda[i], i, lambda_ref[i]);\n            }\n        }\n        return 1;\n    }\n\n    // check eigenvectors. As the eigenvectors are not unique, instead of comparing the eigenvectors directly with cuSolver reference, we compare A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n        // cuSolverDx and cuSolver sets the diagonal elements of input Hermitian matrix A to be real. So before computing A * V we need to set the diagonal elements of A to be real, if case they are not.\n        if constexpr (common::is_complex<data_type>()) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    A_input[row + row * m + b * input_size_a].y = 0;\n                }\n            }\n        }\n\n        std::vector<data_type> A_v(input_size_a);\n        std::vector<data_type> lambda_v(input_size_a);\n        constexpr unsigned     col_stride = is_col_maj_a ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_a ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* Ab = A_input.data() + b * input_size_a;\n            data_type* Vb = V.data() + b * input_size_a; // Use cuSolverDx eigenvectors\n            //data_type* Vb = A.data() + b * m * m; // Use cuSolver reference eigenvectors\n            precision_type* lambda_b = lambda.data() + b * m;\n\n            // Compute A * V (matrix-matrix multiplication) and V * diagm(lambda) (practically scalar-vector multiplication)\n            A_v.assign(m * m, common::convert<data_type, precision_type>(0.));\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] =\n                            common::convert<data_type, precision_type>(lambda_b[col]) * Vb[row * row_stride + col * col_stride];\n                    for (int k = 0; k < m; ++k) {\n                        A_v[row * row_stride + col * col_stride] += Ab[row * row_stride + k * col_stride] * Vb[k * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            std::cout << \"HEEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n    }\n\n    std::cout << \"Success compared eigenvalues with the cuSolver API results, and if vectors are computed, verified eigenvectors with A * V - V * diagm(lambda)\"\n              << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct heev_batched_block_functor {\n    int operator()() { return heev_batched_block<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<heev_batched_block_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/04_Symmetric_Eigenvalues/heev_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_heev.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to compute eigenvalues, and optionally eigenvectors, of a batched m x m symmetric matrix A.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXsyevBatched and cusolverDn<t>syevjBatched.\n\ntemplate<class Solver, bool compute_vectors = false, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(DataType* A, PrecisionType* lambda, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto one_batch_size_a_gmem = m * m;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    auto this_A         = A + one_batch_size_a_gmem * batch_idx;\n    auto this_lambda    = lambda + m * batch_idx;\n    auto this_workspace = workspace + workspace_size * batch_idx;\n\n    Solver().execute(this_A, this_lambda, this_workspace, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint heev_batched_thread() {\n\n    using namespace cusolverdx;\n    // Job can be either job::overwrite_vectors or job::no_vectors\n    using Solver = decltype(Size<8>() + Precision<float>() + Type<type::complex>() + Function<heev>() + FillMode<fill_mode::upper>() +\n                            Arrangement<arrangement::row_major>() + Job<job::overwrite_vectors>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool is_col_maj_a    = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_lower_fill   = fill_mode_of_v<Solver> == fill_mode::lower;\n    constexpr bool compute_vectors = job_of_v<Solver> != job::no_vectors;\n\n    const auto     lda_gmem     = m;\n    constexpr auto input_size_a = m * m;\n\n    const auto batches = 500;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    // Fill with symmetric matrix data\n    // If the input matrix is not symmetric, it is OK because the function only processes the specified part of the matrix and leaves the rest unchanged\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, m, A.data(), lda_gmem, true /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<data_type> V, A_input;\n    if constexpr (compute_vectors) {\n        A_input.assign(A.begin(), A.end());\n        V.resize(input_size_a * batches);\n    }\n    std::vector<precision_type> lambda(m * batches, 0);\n    std::vector<precision_type> lambda_ref(m * batches, 0);\n    std::vector<int>            info(batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_lambda    = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_lambda), sizeof(precision_type) * lambda.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size * batches));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto nthreads = compute_vectors ? 32 : 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_lambda, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(lambda.data(), d_lambda, sizeof(precision_type) * lambda.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), cudaMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(V.data(), d_A, sizeof(data_type) * V.size(), cudaMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HEEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_a>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_lambda));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n    CUDA_CHECK_AND_EXIT(cudaStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_heev<data_type, cuda_data_type, precision_type, false /*use_syevj*/, false /*check_perf*/>(\n            A, lambda_ref, info.data(), m, batches, is_lower_fill, is_col_maj_a, compute_vectors, batches);\n\n    const auto total_relative_error_lambda = common::check_error<precision_type, precision_type>(lambda.data(), lambda_ref.data(), batches * m);\n    std::cout << \"HEEV: relative error of lambda between cuSolverDx and cuSolver reference results: \" << total_relative_error_lambda << std::endl;\n\n    // Uncomment below to print results after cuSolver reference execute\n    // printf(\"lambda_ref = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<precision_type>(total_relative_error_lambda)) {\n        std::cout << \"Failure compared with cuSolver API results lambda\" << std::endl;\n        //Print out lambda for debugging\n        for (int i = 0; i < m * batches; ++i) {\n            if (abs(lambda[i] - lambda_ref[i]) / abs(lambda_ref[i]) > 1e-05) {\n                printf(\"lambda[%d] = %10.3f, lambda_ref[%d] = %10.3f  differ \\n\", i, lambda[i], i, lambda_ref[i]);\n            }\n        }\n        return 1;\n    }\n\n    // check eigenvectors. As the eigenvectors are not unique, instead of comparing the eigenvectors directly with cuSolver reference, we compare A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n        // cuSolverDx and cuSolver sets the diagonal elements of input Hermitian matrix A to be real. So before computing A * V we need to set the diagonal elements of A to be real, if case they are not.\n        if constexpr (common::is_complex<data_type>()) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    A_input[row + row * m + b * input_size_a].y = 0;\n                }\n            }\n        }\n\n        std::vector<data_type> A_v(input_size_a);\n        std::vector<data_type> lambda_v(input_size_a);\n        constexpr unsigned     col_stride = is_col_maj_a ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_a ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* Ab = A_input.data() + b * input_size_a;\n            data_type* Vb = V.data() + b * input_size_a; // Use cuSolverDx eigenvectors\n            //data_type* Vb = A.data() + b * m * m; // Use cuSolver reference eigenvectors\n            precision_type* lambda_b = lambda.data() + b * m;\n\n            // Compute A * V (matrix-matrix multiplication) and V * diagm(lambda) (practically scalar-vector multiplication)\n            A_v.assign(m * m, common::convert<data_type, precision_type>(0.));\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] =\n                            common::convert<data_type, precision_type>(lambda_b[col]) * Vb[row * row_stride + col * col_stride];\n                    for (int k = 0; k < m; ++k) {\n                        A_v[row * row_stride + col * col_stride] += Ab[row * row_stride + k * col_stride] * Vb[k * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            //std::cout << \"HEEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n    }\n\n    std::cout << \"Success compared eigenvalues with the cuSolver API results, and if vectors are computed, verified eigenvectors with A * V - V * diagm(lambda)\"\n              << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct heev_batched_thread_functor {\n    int operator()() { return heev_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<heev_batched_thread_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_heev.hpp\"\n\n// This example demonstrates how to use cuSolverDx thread execution API to compute eigenvalues, and optionally eigenvectors, of a batched m x m symmetric matrix A.\n// The results are compared with the reference values obtained with cuSolver host API, cusolverDnXsyevBatched and cusolverDn<t>syevjBatched.\n\ntemplate<class Solver, bool compute_vectors = false, typename DataType = typename Solver::a_data_type, typename PrecisionType = typename Solver::a_precision>\n__global__ void kernel(DataType* A, PrecisionType* lambda, DataType* workspace, typename Solver::status_type* info, const unsigned batches) {\n\n    const auto batch_idx = threadIdx.x + blockIdx.x * blockDim.x;\n    if (batch_idx >= batches)\n        return;\n\n    constexpr auto m                     = Solver::m_size;\n    constexpr auto one_batch_size_a_gmem = m * m;\n    constexpr auto workspace_size        = Solver::workspace_size;\n\n    auto this_A         = A + one_batch_size_a_gmem * batch_idx;\n    auto this_lambda    = lambda + m * batch_idx;\n    auto this_workspace = workspace + workspace_size * batch_idx;\n\n    Solver().execute(this_A, this_lambda, this_workspace, &info[batch_idx]);\n}\n\ntemplate<int Arch>\nint heev_batched_thread() {\n\n    using namespace cusolverdx;\n    // Job can be either job::overwrite_vectors or job::no_vectors\n    using Solver = decltype(Size<8>() + Precision<float>() + Type<type::complex>() + Function<heev>() + FillMode<fill_mode::upper>() +\n                            Arrangement<arrangement::row_major>() + Job<job::overwrite_vectors>() + SM<Arch>() + Thread());\n\n    using data_type      = typename Solver::a_data_type;\n    using cuda_data_type = typename Solver::a_cuda_data_type;\n    using precision_type = typename Solver::a_precision;\n\n    constexpr auto m = Solver::m_size;\n\n    constexpr bool is_col_maj_a    = arrangement_of_v_a<Solver> == arrangement::col_major;\n    constexpr bool is_lower_fill   = fill_mode_of_v<Solver> == fill_mode::lower;\n    constexpr bool compute_vectors = job_of_v<Solver> != job::no_vectors;\n\n    const auto     lda_gmem     = m;\n    constexpr auto input_size_a = m * m;\n\n    const auto batches = 500;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(input_size_a * batches);\n    // Fill with symmetric matrix data\n    // If the input matrix is not symmetric, it is OK because the function only processes the specified part of the matrix and leaves the rest unchanged\n    common::fillup_random_matrix<data_type>(is_col_maj_a, m, m, A.data(), lda_gmem, true /*symmetric*/, false /*diagonal dominant*/, -2, 4, batches);\n\n    std::vector<data_type> V, A_input;\n    if constexpr (compute_vectors) {\n        A_input.assign(A.begin(), A.end());\n        V.resize(input_size_a * batches);\n    }\n    std::vector<precision_type> lambda(m * batches, 0);\n    std::vector<precision_type> lambda_ref(m * batches, 0);\n    std::vector<int>            info(batches, 0);\n    data_type*                  d_A         = nullptr;\n    precision_type*             d_lambda    = nullptr;\n    data_type*                  d_workspace = nullptr;\n    int*                        d_info      = nullptr;\n\n    // Uncomment below to print A matrix\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, m, lda_gmem, is_col_maj_a>(A.data(), batches);\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_lambda), sizeof(precision_type) * lambda.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_workspace), sizeof(data_type) * Solver::workspace_size * batches));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * info.size()));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n\n    //Invokes kernel\n    const auto nthreads = compute_vectors ? 32 : 64;\n    kernel<Solver><<<(batches + nthreads - 1) / nthreads, nthreads, 0, stream>>>(d_A, d_lambda, d_workspace, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(lambda.data(), d_lambda, sizeof(precision_type) * lambda.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * info.size(), hipMemcpyDeviceToHost, stream));\n    if constexpr (compute_vectors) {\n        CUDA_CHECK_AND_EXIT(hipMemcpyAsync(V.data(), d_A, sizeof(data_type) * V.size(), hipMemcpyDeviceToHost, stream));\n    }\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    if (std::accumulate(info.begin(), info.begin() + batches, 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx HEEV kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print results after cuSolverDx execute\n    // printf(\"=====\\n\");\n    // printf(\"lambda = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda.data(), batches);\n    // if constexpr (compute_vectors) {\n    //     printf(\"V = \\n\");\n    //     common::print_matrix<data_type, m, m, m, is_col_maj_a>(V.data(), batches);\n    // }\n\n    /* free resources */\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_lambda));\n    CUDA_CHECK_AND_EXIT(hipFree(d_workspace));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n    CUDA_CHECK_AND_EXIT(hipStreamDestroy(stream));\n\n    //=========================\n    // cuSolver reference\n    //=========================\n    common::reference_cusolver_heev<data_type, cuda_data_type, precision_type, false /*use_syevj*/, false /*check_perf*/>(\n            A, lambda_ref, info.data(), m, batches, is_lower_fill, is_col_maj_a, compute_vectors, batches);\n\n    const auto total_relative_error_lambda = common::check_error<precision_type, precision_type>(lambda.data(), lambda_ref.data(), batches * m);\n    std::cout << \"HEEV: relative error of lambda between cuSolverDx and cuSolver reference results: \" << total_relative_error_lambda << std::endl;\n\n    // Uncomment below to print results after cuSolver reference execute\n    // printf(\"lambda_ref = \\n\");\n    // common::print_matrix<precision_type, m, 1, m, true>(lambda_ref.data(), batches);\n    // printf(\"=====\\n\");\n\n    if (!common::is_error_acceptable<precision_type>(total_relative_error_lambda)) {\n        std::cout << \"Failure compared with cuSolver API results lambda\" << std::endl;\n        //Print out lambda for debugging\n        for (int i = 0; i < m * batches; ++i) {\n            if (abs(lambda[i] - lambda_ref[i]) / abs(lambda_ref[i]) > 1e-05) {\n                printf(\"lambda[%d] = %10.3f, lambda_ref[%d] = %10.3f  differ \\n\", i, lambda[i], i, lambda_ref[i]);\n            }\n        }\n        return 1;\n    }\n\n    // check eigenvectors. As the eigenvectors are not unique, instead of comparing the eigenvectors directly with cuSolver reference, we compare A * V versus V * diagm(lambda)\n    if constexpr (compute_vectors) {\n        // cuSolverDx and cuSolver sets the diagonal elements of input Hermitian matrix A to be real. So before computing A * V we need to set the diagonal elements of A to be real, if case they are not.\n        if constexpr (common::is_complex<data_type>()) {\n            for (int b = 0; b < batches; ++b) {\n                for (int row = 0; row < m; ++row) {\n                    A_input[row + row * m + b * input_size_a].y = 0;\n                }\n            }\n        }\n\n        std::vector<data_type> A_v(input_size_a);\n        std::vector<data_type> lambda_v(input_size_a);\n        constexpr unsigned     col_stride = is_col_maj_a ? m : 1;\n        constexpr unsigned     row_stride = is_col_maj_a ? 1 : m;\n\n        for (int b = 0; b < batches; ++b) {\n            data_type* Ab = A_input.data() + b * input_size_a;\n            data_type* Vb = V.data() + b * input_size_a; // Use cuSolverDx eigenvectors\n            //data_type* Vb = A.data() + b * m * m; // Use cuSolver reference eigenvectors\n            precision_type* lambda_b = lambda.data() + b * m;\n\n            // Compute A * V (matrix-matrix multiplication) and V * diagm(lambda) (practically scalar-vector multiplication)\n            A_v.assign(m * m, common::convert<data_type, precision_type>(0.));\n            for (int row = 0; row < m; ++row) {\n                for (int col = 0; col < m; ++col) {\n                    lambda_v[row * row_stride + col * col_stride] =\n                            common::convert<data_type, precision_type>(lambda_b[col]) * Vb[row * row_stride + col * col_stride];\n                    for (int k = 0; k < m; ++k) {\n                        A_v[row * row_stride + col * col_stride] += Ab[row * row_stride + k * col_stride] * Vb[k * row_stride + col * col_stride];\n                    }\n                }\n            }\n\n            // Compare A * V with V * diagm(lambda) to verify eigenvector property\n            const auto total_relative_error_lambda = common::check_error<data_type, data_type>(A_v.data(), lambda_v.data(), m * m);\n            //std::cout << \"HEEV: relative error of A * V - V * diagm(lambda): for batch \" << b << \" : \" << total_relative_error_lambda << std::endl;\n            if (!common::is_error_acceptable<data_type>(total_relative_error_lambda)) {\n                std::cout << \"Relative error of A * V - V * diagm(lambda) is too large for batch \" << b << std::endl;\n                return 1;\n            }\n        }\n    }\n\n    std::cout << \"Success compared eigenvalues with the cuSolver API results, and if vectors are computed, verified eigenvectors with A * V - V * diagm(lambda)\"\n              << std::endl;\n    return 0;\n}\n\ntemplate<int Arch>\nstruct heev_batched_thread_functor {\n    int operator()() { return heev_batched_thread<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<heev_batched_thread_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/04_Symmetric_Eigenvalues/heev_batched_thread.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Block execution to solve a batched linear systems with multiple right hand side\n// after performing Cholesky factorization of the batched symmetric, positive-definite matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class POSV, unsigned int BatchesPerBlock, class DataType = typename POSV::a_data_type>\n__global__ __launch_bounds__(POSV::max_threads_per_block) void posv_kernel(\n        DataType* A, const unsigned int lda_gmem, DataType* B, const unsigned int ldb_gmem, typename POSV::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m                     = POSV::m_size;\n    constexpr auto nrhs                  = POSV::k_size;\n    const auto     one_batch_size_a_gmem = lda_gmem * m;\n    const auto     one_batch_size_b_gmem = (arrangement_of_v_b<POSV> == arrangement::col_major) ? ldb_gmem * nrhs : m * ldb_gmem;\n\n    constexpr auto lda_smem              = POSV::lda;\n    constexpr auto ldb_smem              = POSV::ldb;\n    constexpr auto one_batch_size_a_smem = lda_smem * m;\n    constexpr auto one_batch_size_b_smem = (arrangement_of_v_b<POSV> == arrangement::col_major) ? ldb_smem * nrhs : m * ldb_smem;\n\n    extern __shared__ __align__(sizeof(DataType)) cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n    DataType* Bs = As + one_batch_size_a_smem * BatchesPerBlock;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg = B + size_t(one_batch_size_b_gmem) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<POSV, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<POSV, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    POSV().execute(As, lda_smem, Bs, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<POSV, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<POSV, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n}\n\ntemplate<int Arch>\nint simple_posv_batched() {\n\n    using namespace cusolverdx;\n\n    using POSV = decltype(Size<32 /* = m */, 32 /* = n */, 1 /* = k */>() + Precision<double>() + Type<type::complex>() + Function<function::posv>() +\n                          FillMode<lower>() + Arrangement<col_major /* A, X, and B */>() + SM<Arch>() + Block());\n\n    constexpr unsigned bpb = POSV::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Using Suggested BlockDim = \" << POSV::suggested_block_dim.x << std::endl;\n    std::cout << \"Using Specified BlockDim = \" << POSV::block_dim.x << std::endl;\n\n    using data_type      = typename POSV::a_data_type;\n    using cuda_data_type = typename POSV::a_cuda_data_type;\n\n    constexpr auto m    = POSV::m_size;\n    constexpr auto n    = POSV::n_size;\n    constexpr auto nrhs = POSV::k_size;\n    static_assert(m == n, \"posv is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = POSV::lda;\n    constexpr auto ldb_smem = POSV::ldb;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POSV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<POSV> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto lda = m;\n    constexpr auto ldb = is_col_maj_b ? m : nrhs;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    std::cout << \"Using leading dimension LDA = \" << lda_smem << \", LDB = \" << ldb_smem << std::endl;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    const auto one_batch_size_A = lda * n; // no padding for global memory\n    const auto one_batch_size_B = m * nrhs;\n\n    cudaStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * padded_batches);\n    std::vector<data_type> L(one_batch_size_A * padded_batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POSV> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<POSV> == col_major, m, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * padded_batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(cudaMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * padded_batches));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), cudaMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), cudaMemcpyHostToDevice, stream));\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto sm_size = POSV::shared_memory_size;\n\n    const auto kernel = posv_kernel<POSV, POSV::batches_per_block>;\n    CUDA_CHECK_AND_EXIT(cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    //Invokes kernel\n    kernel<<<(batches + bpb - 1) / bpb, POSV::block_dim, sm_size, stream>>>(d_A, lda, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(cudaGetLastError());\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), cudaMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(cudaMemcpyAsync(info.data(), d_info, sizeof(int) * batches, cudaMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(cudaStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    //printf(\"after cuSolverDx execute\\n\");\n    //printf(\"L = \\n\");\n    //common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), padded_batches);\n    //printf(\"=====\\n\");\n    //printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched and portsBatched\n    //=======================================================\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, true>(A, B, info.data(), m, nrhs, padded_batches,\n            (fill_mode_of_v<POSV> == fill_mode::lower), /* is_lower? */\n            is_col_maj_a, is_col_maj_b, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POSV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), padded_batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED POSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrSBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrSBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(cudaFree(d_A));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_B));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(cudaDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct simple_posv_batched_functor {\n    int operator()() { return simple_posv_batched<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<simple_posv_batched_functor>(); }\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <cusolverdx.hpp>\n\n#include \"../common/cudart.hpp\"\n#include \"../common/error_checking.hpp\"\n#include \"../common/random.hpp\"\n#include \"../common/example_sm_runner.hpp\"\n#include \"../common/device_io.hpp\"\n#include \"../common/measure.hpp\"\n#include \"../common/print.hpp\"\n#include \"../common/cusolver_reference_cholesky.hpp\"\n\n// This example demonstrates how to use cuSolverDx API with Block execution to solve a batched linear systems with multiple right hand side\n// after performing Cholesky factorization of the batched symmetric, positive-definite matrix A.\n// The results are compared with the reference values obtained with cuSolver host API.\n\ntemplate<class POSV, unsigned int BatchesPerBlock, class DataType = typename POSV::a_data_type>\n__global__ __launch_bounds__(POSV::max_threads_per_block) void posv_kernel(\n        DataType* A, const unsigned int lda_gmem, DataType* B, const unsigned int ldb_gmem, typename POSV::status_type* info, const unsigned int batches) {\n\n    using namespace cusolverdx;\n    constexpr auto m                     = POSV::m_size;\n    constexpr auto nrhs                  = POSV::k_size;\n    const auto     one_batch_size_a_gmem = lda_gmem * m;\n    const auto     one_batch_size_b_gmem = (arrangement_of_v_b<POSV> == arrangement::col_major) ? ldb_gmem * nrhs : m * ldb_gmem;\n\n    constexpr auto lda_smem              = POSV::lda;\n    constexpr auto ldb_smem              = POSV::ldb;\n    constexpr auto one_batch_size_a_smem = lda_smem * m;\n    constexpr auto one_batch_size_b_smem = (arrangement_of_v_b<POSV> == arrangement::col_major) ? ldb_smem * nrhs : m * ldb_smem;\n\n    extern __shared__ __align__(sizeof(DataType)) cusolverdx::byte shared_mem[];\n\n    DataType* As = reinterpret_cast<DataType*>(shared_mem);\n    DataType* Bs = As + one_batch_size_a_smem * BatchesPerBlock;\n\n    const auto batch_idx = blockIdx.x * BatchesPerBlock;\n    if (batch_idx >= batches)\n        return;\n\n    auto Ag = A + size_t(one_batch_size_a_gmem) * batch_idx;\n    auto Bg = B + size_t(one_batch_size_b_gmem) * batch_idx;\n\n    // Load data from global memory to shared memory\n    common::io<POSV, BatchesPerBlock>::load_a(Ag, lda_gmem, As, lda_smem);\n    common::io<POSV, BatchesPerBlock>::load_b(Bg, ldb_gmem, Bs, ldb_smem);\n\n    POSV().execute(As, lda_smem, Bs, &info[batch_idx]);\n\n    // Store results back to global memory\n    common::io<POSV, BatchesPerBlock>::store_a(As, lda_smem, Ag, lda_gmem);\n    common::io<POSV, BatchesPerBlock>::store_b(Bs, ldb_smem, Bg, ldb_gmem);\n}\n\ntemplate<int Arch>\nint simple_posv_batched() {\n\n    using namespace cusolverdx;\n\n    using POSV = decltype(Size<32 /* = m */, 32 /* = n */, 1 /* = k */>() + Precision<double>() + Type<type::complex>() + Function<function::posv>() +\n                          FillMode<lower>() + Arrangement<col_major /* A, X, and B */>() + SM<Arch>() + Block());\n\n    constexpr unsigned bpb = POSV::batches_per_block;\n    std::cout << \"Using Suggested Batches per block = \" << bpb << std::endl;\n    std::cout << \"Using Suggested BlockDim = \" << POSV::suggested_block_dim.x << std::endl;\n    std::cout << \"Using Specified BlockDim = \" << POSV::block_dim.x << std::endl;\n\n    using data_type      = typename POSV::a_data_type;\n    using cuda_data_type = typename POSV::a_cuda_data_type;\n\n    constexpr auto m    = POSV::m_size;\n    constexpr auto n    = POSV::n_size;\n    constexpr auto nrhs = POSV::k_size;\n    static_assert(m == n, \"posv is for Hermitian positive-definite matrix matrix only\");\n    constexpr auto lda_smem = POSV::lda;\n    constexpr auto ldb_smem = POSV::ldb;\n\n    constexpr bool is_col_maj_a = arrangement_of_v_a<POSV> == arrangement::col_major;\n    constexpr bool is_col_maj_b = arrangement_of_v_b<POSV> == arrangement::col_major;\n\n    // no padding for global memory\n    constexpr auto lda = m;\n    constexpr auto ldb = is_col_maj_b ? m : nrhs;\n\n    printf(\"Size m = %d, n = %d, nrhs = %d\\n\", m, n, nrhs);\n    std::cout << \"Using leading dimension LDA = \" << lda_smem << \", LDB = \" << ldb_smem << std::endl;\n\n    const auto batches        = 2;\n    const auto padded_batches = (batches + bpb - 1) / bpb * bpb;\n\n    const auto one_batch_size_A = lda * n; // no padding for global memory\n    const auto one_batch_size_B = m * nrhs;\n\n    hipStream_t stream = nullptr;\n    CUDA_CHECK_AND_EXIT(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking));\n\n    std::vector<data_type> A(one_batch_size_A * padded_batches);\n    std::vector<data_type> L(one_batch_size_A * padded_batches);\n\n    common::fillup_random_diagonal_dominant_matrix<data_type>(arrangement_of_v_a<POSV> == col_major, m, n, A.data(), lda, false, 2, 4, batches);\n\n    std::vector<data_type> B(one_batch_size_B * padded_batches);\n    common::fillup_random_matrix<data_type>(arrangement_of_v_b<POSV> == col_major, m, nrhs, B.data(), ldb, false, false, -1, 1, batches);\n    std::vector<data_type> X(one_batch_size_B * padded_batches);\n\n    std::vector<int> info(padded_batches, 0);\n    data_type*       d_A    = nullptr; /* device copy of A */\n    data_type*       d_B    = nullptr; /* device copy of B */\n    int*             d_info = nullptr; /* error info */\n\n\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_A), sizeof(data_type) * A.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_B), sizeof(data_type) * B.size()));\n    CUDA_CHECK_AND_EXIT(hipMalloc(reinterpret_cast<void**>(&d_info), sizeof(int) * padded_batches));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_A, A.data(), sizeof(data_type) * A.size(), hipMemcpyHostToDevice, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(d_B, B.data(), sizeof(data_type) * B.size(), hipMemcpyHostToDevice, stream));\n\n    // Increase max dynamic shared memory for the kernel if needed.\n    const auto sm_size = POSV::shared_memory_size;\n\n    const auto kernel = posv_kernel<POSV, POSV::batches_per_block>;\n    CUDA_CHECK_AND_EXIT(hipFuncSetAttribute(reinterpret_cast<const void*>(kernel), hipFuncAttributeMaxDynamicSharedMemorySize, sm_size));\n\n    //Invokes kernel\n    kernel<<<(batches + bpb - 1) / bpb, POSV::block_dim, sm_size, stream>>>(d_A, lda, d_B, ldb, d_info, batches);\n    CUDA_CHECK_AND_EXIT(hipGetLastError());\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(L.data(), d_A, sizeof(data_type) * A.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(X.data(), d_B, sizeof(data_type) * B.size(), hipMemcpyDeviceToHost, stream));\n    CUDA_CHECK_AND_EXIT(hipMemcpyAsync(info.data(), d_info, sizeof(int) * batches, hipMemcpyDeviceToHost, stream));\n\n    CUDA_CHECK_AND_EXIT(hipStreamSynchronize(stream));\n    if (std::accumulate(info.begin(), info.end(), 0) != 0) {\n        std::cout << \"non-zero d_info returned for at least one of the batches after cuSolverDx kernel \\n\";\n        for (int j = 0; j < batches; j++) {\n            if (info[j] != 0)\n                std::cout << \"info[\" << j << \"]=\" << info[j] << std::endl;\n        }\n        return -1;\n    }\n\n    // Uncomment below to print the results after cuSolverDx execute\n    //printf(\"after cuSolverDx execute\\n\");\n    //printf(\"L = \\n\");\n    //common::print_matrix<data_type, m, n, lda, is_col_maj_a>(L.data(), padded_batches);\n    //printf(\"=====\\n\");\n    //printf(\"X = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(X.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    //=======================================================\n    // cuSolver reference with potrfBatched and portsBatched\n    //=======================================================\n    common::reference_cusolver_cholesky<data_type, cuda_data_type, true>(A, B, info.data(), m, nrhs, padded_batches,\n            (fill_mode_of_v<POSV> == fill_mode::lower), /* is_lower? */\n            is_col_maj_a, is_col_maj_b, batches);\n\n    auto total_relative_error = common::check_error<data_type, data_type>(L.data(), A.data(), batches * one_batch_size_A);\n    printf(\"BATCHED POSV: relative error of A between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n\n    // Uncomment below to print the results after cuSolver reference execute\n    // printf(\"after cuSolver API execute\\n\");\n    // printf(\"A = \\n\");\n    // common::print_matrix<data_type, m, n, lda, is_col_maj_a>(A.data(), padded_batches);\n    // printf(\"B = \\n\");\n    // common::print_matrix<data_type, n, nrhs, ldb, is_col_maj_b>(B.data(), padded_batches);\n    // printf(\"=====\\n\");\n\n    total_relative_error = common::check_error<data_type, data_type>(X.data(), B.data(), batches * one_batch_size_B);\n    printf(\"BATCHED POSV: relative error of B between cuSolverDx and cuSolver results: = %e\\n\", total_relative_error);\n    if (common::is_error_acceptable<data_type>(total_relative_error)) {\n        std::cout << \"Success compared to cuSolver potrSBatched Result \" << std::endl;\n    } else {\n        std::cout << \"Failure compared to cuSolver potrSBatched Result \" << std::endl;\n        return 1;\n    }\n\n    CUDA_CHECK_AND_EXIT(hipFree(d_A));\n    CUDA_CHECK_AND_EXIT(hipFree(d_B));\n    CUDA_CHECK_AND_EXIT(hipFree(d_info));\n\n    CUDA_CHECK_AND_EXIT(hipDeviceReset());\n\n    return 0;\n}\n\ntemplate<int Arch>\nstruct simple_posv_batched_functor {\n    int operator()() { return simple_posv_batched<Arch>(); }\n};\n\n\nint main() { return common::run_example_with_sm<simple_posv_batched_functor>(); }\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuSolverDx/00_Introduction/posv_batched_block.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cassert>\n#include <iostream>\n#include <algorithm>\n#include <string>\n#include <functional>\n\n#include <nvcompdx.hpp>\n#include \"../common/util.hpp\"\n\nusing namespace nvcompdx;\n\n\n// This introductory sample demonstrates the usage of the warp-level device API\n// for LZ4 GPU compression. The compressed buffer afterwards is written to disk.\n// Note, however, that for optimal performance, one should perform multiple chunk\n// compressions simultaneously. The example here only intends to showcase usage.\n\n// LZ4 compression kernel, using the preconfigured compressor\n// 1 warp per chunk\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  const void * const uncomp_chunk,\n  const size_t uncomp_chunk_size,\n  void * comp_chunk,\n  size_t * comp_chunk_size,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  auto compressor = compressor_type();\n  constexpr size_t shmem_alignment = compressor.shmem_alignment();\n  extern __shared__ __align__(shmem_alignment) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  compressor.execute(\n    uncomp_chunk,\n    comp_chunk,\n    uncomp_chunk_size,\n    comp_chunk_size,\n    shared_comp_scratch_buffer,\n    tmp_buffer);\n}\n\nstatic constexpr size_t chunk_size = 1 << 16;\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nint lz4_gpu_comp_introduction(const std::vector<char>& data,\n                              std::vector<char>& compressed,\n                              size_t warmup_iteration_count,\n                              size_t total_iteration_count)\n{\n  assert(!data.empty());\n\n  size_t total_bytes = data.size();\n  size_t batch_size = 1; // a single chunk\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n  std::cout << \"chunks: \" << 1 << std::endl;\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Allocate buffer for the input (uncompressed) data\n  // Note: with cudaMalloc() the input alignment is implicitly met\n  void* d_input_data;\n  CUDA_CHECK(cudaMalloc(&d_input_data, total_bytes));\n  CUDA_CHECK(cudaMemcpy(d_input_data, data.data(), total_bytes, cudaMemcpyHostToDevice));\n\n  // Allocate buffer for the input/output sizes\n  size_t* d_output_size;\n  CUDA_CHECK(cudaMalloc(&d_output_size, sizeof(size_t)));\n\n  // Compress on the GPU using device API\n  // Allocate temporary scratch space\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(cudaMalloc(&d_comp_temp, lz4_compressor_type().tmp_size_total(1)));\n\n  // Calculate the maximum compressed size\n  const size_t max_comp_chunk_size = lz4_compressor_type().max_comp_chunk_size();\n\n  // Allocate buffer for the output (compressed) data\n  // Note: with cudaMalloc() the output alignment is implicitly met\n  void* d_output_data;\n  CUDA_CHECK(cudaMalloc(&d_output_data, max_comp_chunk_size));\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Compression parameters\n  // We are compressing 1 chunk per thread block\n  const unsigned int block_size = 32; // 1 warp\n  const unsigned int block_count = static_cast<unsigned int>(batch_size);\n  const auto comp_shared_memory = lz4_compressor_type().shmem_size_group();\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      d_input_data,\n      total_bytes,\n      d_output_data,\n      d_output_size,\n      d_comp_temp\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Compute compression ratio\n  size_t comp_bytes;\n  CUDA_CHECK(cudaMemcpy(&comp_bytes, d_output_size, sizeof(size_t), cudaMemcpyDeviceToHost));\n  assert(comp_bytes <= max_comp_chunk_size);\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n            << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Copy data back to host for write out\n  compressed.resize(comp_bytes);\n  CUDA_CHECK(cudaMemcpy(compressed.data(), d_output_data, comp_bytes, cudaMemcpyDeviceToHost));\n\n  CUDA_CHECK(cudaFree(d_input_data));\n  CUDA_CHECK(cudaFree(d_comp_temp));\n  CUDA_CHECK(cudaFree(d_output_data));\n  CUDA_CHECK(cudaFree(d_output_size));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_gpu_compression_introduction [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file>\" << std::endl;\n  std::cerr << \"  -o <output file>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_introduction<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::string file_name_in;\n  std::string file_name_out;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        if(i < argc) {\n          file_name_in = argv[i++];\n        }\n      } else if (strcmp(current_argv, \"-o\") == 0) {\n        if(i < argc) {\n          file_name_out = argv[i++];\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_name_in.empty()) {\n    std::cerr << \"Must specify one input file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  } else if (file_name_out.empty()) {\n    std::cerr << \"Must specify one output file via '-o <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto input = read_file(file_name_in);\n\n  if (input.size() > chunk_size) {\n    std::cerr << \"The input file is too large for this example. \" << std::endl\n              << \"Select an input file up to \" << chunk_size << \" bytes.\" << std::endl;\n    return 1;\n  }\n\n  std::vector<char> output;\n  int ret = run_with_current_arch<Runner>(input,\n                                          output,\n                                          warmup_iteration_count,\n                                          total_iteration_count);\n\n  write_file(file_name_out, output);\n\n  return 0;\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cassert>\n#include <iostream>\n#include <algorithm>\n#include <string>\n#include <functional>\n\n#include <nvcompdx.hpp>\n#include \"../common/util.hpp\"\n\nusing namespace nvcompdx;\n\n\n// This introductory sample demonstrates the usage of the warp-level device API\n// for LZ4 GPU compression. The compressed buffer afterwards is written to disk.\n// Note, however, that for optimal performance, one should perform multiple chunk\n// compressions simultaneously. The example here only intends to showcase usage.\n\n// LZ4 compression kernel, using the preconfigured compressor\n// 1 warp per chunk\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  const void * const uncomp_chunk,\n  const size_t uncomp_chunk_size,\n  void * comp_chunk,\n  size_t * comp_chunk_size,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  auto compressor = compressor_type();\n  constexpr size_t shmem_alignment = compressor.shmem_alignment();\n  extern __shared__ __align__(shmem_alignment) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  compressor.execute(\n    uncomp_chunk,\n    comp_chunk,\n    uncomp_chunk_size,\n    comp_chunk_size,\n    shared_comp_scratch_buffer,\n    tmp_buffer);\n}\n\nstatic constexpr size_t chunk_size = 1 << 16;\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nint lz4_gpu_comp_introduction(const std::vector<char>& data,\n                              std::vector<char>& compressed,\n                              size_t warmup_iteration_count,\n                              size_t total_iteration_count)\n{\n  assert(!data.empty());\n\n  size_t total_bytes = data.size();\n  size_t batch_size = 1; // a single chunk\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n  std::cout << \"chunks: \" << 1 << std::endl;\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Allocate buffer for the input (uncompressed) data\n  // Note: with cudaMalloc() the input alignment is implicitly met\n  void* d_input_data;\n  CUDA_CHECK(hipMalloc(&d_input_data, total_bytes));\n  CUDA_CHECK(hipMemcpy(d_input_data, data.data(), total_bytes, hipMemcpyHostToDevice));\n\n  // Allocate buffer for the input/output sizes\n  size_t* d_output_size;\n  CUDA_CHECK(hipMalloc(&d_output_size, sizeof(size_t)));\n\n  // Compress on the GPU using device API\n  // Allocate temporary scratch space\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(hipMalloc(&d_comp_temp, lz4_compressor_type().tmp_size_total(1)));\n\n  // Calculate the maximum compressed size\n  const size_t max_comp_chunk_size = lz4_compressor_type().max_comp_chunk_size();\n\n  // Allocate buffer for the output (compressed) data\n  // Note: with cudaMalloc() the output alignment is implicitly met\n  void* d_output_data;\n  CUDA_CHECK(hipMalloc(&d_output_data, max_comp_chunk_size));\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Compression parameters\n  // We are compressing 1 chunk per thread block\n  const unsigned int block_size = 32; // 1 warp\n  const unsigned int block_count = static_cast<unsigned int>(batch_size);\n  const auto comp_shared_memory = lz4_compressor_type().shmem_size_group();\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      d_input_data,\n      total_bytes,\n      d_output_data,\n      d_output_size,\n      d_comp_temp\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Compute compression ratio\n  size_t comp_bytes;\n  CUDA_CHECK(hipMemcpy(&comp_bytes, d_output_size, sizeof(size_t), hipMemcpyDeviceToHost));\n  assert(comp_bytes <= max_comp_chunk_size);\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n            << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Copy data back to host for write out\n  compressed.resize(comp_bytes);\n  CUDA_CHECK(hipMemcpy(compressed.data(), d_output_data, comp_bytes, hipMemcpyDeviceToHost));\n\n  CUDA_CHECK(hipFree(d_input_data));\n  CUDA_CHECK(hipFree(d_comp_temp));\n  CUDA_CHECK(hipFree(d_output_data));\n  CUDA_CHECK(hipFree(d_output_size));\n  CUDA_CHECK(hipStreamDestroy(stream));\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_gpu_compression_introduction [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file>\" << std::endl;\n  std::cerr << \"  -o <output file>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_introduction<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::string file_name_in;\n  std::string file_name_out;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        if(i < argc) {\n          file_name_in = argv[i++];\n        }\n      } else if (strcmp(current_argv, \"-o\") == 0) {\n        if(i < argc) {\n          file_name_out = argv[i++];\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_name_in.empty()) {\n    std::cerr << \"Must specify one input file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  } else if (file_name_out.empty()) {\n    std::cerr << \"Must specify one output file via '-o <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto input = read_file(file_name_in);\n\n  if (input.size() > chunk_size) {\n    std::cerr << \"The input file is too large for this example. \" << std::endl\n              << \"Select an input file up to \" << chunk_size << \" bytes.\" << std::endl;\n    return 1;\n  }\n\n  std::vector<char> output;\n  int ret = run_with_current_arch<Runner>(input,\n                                          output,\n                                          warmup_iteration_count,\n                                          total_iteration_count);\n\n  write_file(file_name_out, output);\n\n  return 0;\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/01_introduction/lz4_gpu_compression_introduction.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU decompression. The compression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per thread block\ntemplate <typename decompressor_type>\n__global__ void decomp_warp_kernel(\n    size_t batch_size,\n    const void * const * comp_chunks,\n    void * const * uncomp_chunks,\n    const size_t * comp_chunk_sizes,\n    size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto decompressor = decompressor_type();\n  constexpr auto shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int lz4_cpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are decompressing 4 chunks per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Build up input batch on CPU\n  BatchDataCPU input_data_cpu(data, chunk_size);\n  const size_t batch_size = input_data_cpu.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu(\n      LZ4_compressBound(chunk_size), batch_size);\n\n  // Compressing on the CPU\n  // Loop over chunks on the CPU, compressing each one one by one\n  for (size_t i = 0; i < batch_size; ++i) {\n    // Could use LZ4_compress_default or LZ4_compress_fast instead\n    const int size = LZ4_compress_HC(\n        static_cast<const char*>(input_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(input_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        12 /* compression level */);\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to compress chunk \" + std::to_string(i) + \".\");\n    }\n\n    // Set the actual compressed size\n    compressed_data_cpu.chunk_sizes()[i] = static_cast<size_t>(size);\n  }\n\n  // Compute compression ratio\n  size_t* compressed_sizes_host = compressed_data_cpu.chunk_sizes();\n  size_t comp_bytes = std::accumulate(compressed_sizes_host, compressed_sizes_host + batch_size, size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  // Global memory buffer\n  // Note: lz4 decompression requires no global scratch buffer\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared memory buffer\n  const auto decomp_shared_memory = lz4_decompressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Copy compressed data to GPU\n  BatchData compressed_data(compressed_data_cpu, true, lz4_decompressor_type().input_alignment());\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(input_data_cpu, false, lz4_decompressor_type().output_alignment());\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_warp_kernel<lz4_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (decomp_data != input_data_cpu) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(cudaStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_cpu_compression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_cpu_comp_gpu_decomp<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data, warmup_iteration_count, total_iteration_count);\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU decompression. The compression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per thread block\ntemplate <typename decompressor_type>\n__global__ void decomp_warp_kernel(\n    size_t batch_size,\n    const void * const * comp_chunks,\n    void * const * uncomp_chunks,\n    const size_t * comp_chunk_sizes,\n    size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto decompressor = decompressor_type();\n  constexpr auto shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int lz4_cpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are decompressing 4 chunks per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Build up input batch on CPU\n  BatchDataCPU input_data_cpu(data, chunk_size);\n  const size_t batch_size = input_data_cpu.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu(\n      LZ4_compressBound(chunk_size), batch_size);\n\n  // Compressing on the CPU\n  // Loop over chunks on the CPU, compressing each one one by one\n  for (size_t i = 0; i < batch_size; ++i) {\n    // Could use LZ4_compress_default or LZ4_compress_fast instead\n    const int size = LZ4_compress_HC(\n        static_cast<const char*>(input_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(input_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        12 /* compression level */);\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to compress chunk \" + std::to_string(i) + \".\");\n    }\n\n    // Set the actual compressed size\n    compressed_data_cpu.chunk_sizes()[i] = static_cast<size_t>(size);\n  }\n\n  // Compute compression ratio\n  size_t* compressed_sizes_host = compressed_data_cpu.chunk_sizes();\n  size_t comp_bytes = std::accumulate(compressed_sizes_host, compressed_sizes_host + batch_size, size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  // Global memory buffer\n  // Note: lz4 decompression requires no global scratch buffer\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared memory buffer\n  const auto decomp_shared_memory = lz4_decompressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Copy compressed data to GPU\n  BatchData compressed_data(compressed_data_cpu, true, lz4_decompressor_type().input_alignment());\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(input_data_cpu, false, lz4_decompressor_type().output_alignment());\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_warp_kernel<lz4_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (decomp_data != input_data_cpu) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(hipStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_cpu_compression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_cpu_comp_gpu_decomp<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data, warmup_iteration_count, total_iteration_count);\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/03_lz4_gpu_and_cpu/lz4_cpu_compression_gpu_decompression.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU compression. The decompression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 compression kernel, using the preconfigured compressor\n// 1 warp per chunk, but multiple chunks per thread block\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto compressor = compressor_type();\n  constexpr size_t shmem_size_warp = compressor.shmem_size_group();\n  constexpr size_t tmp_size_warp = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    tmp_buffer + tmp_size_warp * global_chunk_id);\n}\n\nBatchDataCPU convert_batch(const BatchData& batch_data, bool copy_data)\n{\n  BatchDataCPU batch_data_cpu(\n      batch_data.chunk_ptrs(),\n      batch_data.chunk_sizes(),\n      batch_data.batch_size(),\n      copy_data);\n  return batch_data_cpu;\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int lz4_gpu_comp_cpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are compressing 4 chunks per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, lz4_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = lz4_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(cudaMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = lz4_compressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = lz4_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, lz4_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(cudaMemcpy(\n      compressed_sizes_host.data(),\n      compressed_data.chunk_sizes(),\n      batch_size * sizeof(size_t),\n      cudaMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n      compressed_sizes_host.begin(),\n      compressed_sizes_host.end(),\n      size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n            << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu = convert_batch(compressed_data, true);\n  BatchDataCPU decompressed_data_cpu = convert_batch(input_data, false);\n\n  // Loop over chunks on the CPU, decompressing each one\n  for (size_t i = 0; i < batch_size; ++i) {\n    const int size = LZ4_decompress_safe(\n        static_cast<const char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(decompressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(decompressed_data_cpu.chunk_sizes()[i]));\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to decompress chunk \" + std::to_string(i) + \".\");\n    }\n  }\n  // Validate decompressed data against input\n  if (input_data != decompressed_data_cpu) {\n    throw std::runtime_error(\"Failed to validate CPU decompressed data\");\n  } else {\n    std::cout << \"CPU decompression validated :)\" << std::endl;\n  }\n\n  CUDA_CHECK(cudaFree(d_comp_temp));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_cpu_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_cpu_decomp<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data, warmup_iteration_count, total_iteration_count);\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU compression. The decompression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 compression kernel, using the preconfigured compressor\n// 1 warp per chunk, but multiple chunks per thread block\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto compressor = compressor_type();\n  constexpr size_t shmem_size_warp = compressor.shmem_size_group();\n  constexpr size_t tmp_size_warp = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    tmp_buffer + tmp_size_warp * global_chunk_id);\n}\n\nBatchDataCPU convert_batch(const BatchData& batch_data, bool copy_data)\n{\n  BatchDataCPU batch_data_cpu(\n      batch_data.chunk_ptrs(),\n      batch_data.chunk_sizes(),\n      batch_data.batch_size(),\n      copy_data);\n  return batch_data_cpu;\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int lz4_gpu_comp_cpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are compressing 4 chunks per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, lz4_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = lz4_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(hipMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = lz4_compressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = lz4_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, lz4_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(hipMemcpy(\n      compressed_sizes_host.data(),\n      compressed_data.chunk_sizes(),\n      batch_size * sizeof(size_t),\n      hipMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n      compressed_sizes_host.begin(),\n      compressed_sizes_host.end(),\n      size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n            << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu = convert_batch(compressed_data, true);\n  BatchDataCPU decompressed_data_cpu = convert_batch(input_data, false);\n\n  // Loop over chunks on the CPU, decompressing each one\n  for (size_t i = 0; i < batch_size; ++i) {\n    const int size = LZ4_decompress_safe(\n        static_cast<const char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(decompressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(decompressed_data_cpu.chunk_sizes()[i]));\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to decompress chunk \" + std::to_string(i) + \".\");\n    }\n  }\n  // Validate decompressed data against input\n  if (input_data != decompressed_data_cpu) {\n    throw std::runtime_error(\"Failed to validate CPU decompressed data\");\n  } else {\n    std::cout << \"CPU decompression validated :)\" << std::endl;\n  }\n\n  CUDA_CHECK(hipFree(d_comp_temp));\n  CUDA_CHECK(hipStreamDestroy(stream));\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_cpu_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_cpu_decomp<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data, warmup_iteration_count, total_iteration_count);\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/03_lz4_gpu_and_cpu/lz4_gpu_compression_cpu_decompression.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <random>\n#include <cub/cub.cuh>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the block-level device API for\n// ANS GPU compression, followed by fused decompression and reduction.\n\n// ANS compression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename compressor_type>\n__global__ void comp_block_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_block = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer,\n    tmp_buffer + (tmp_size_block > 0 ? tmp_size_block * global_chunk_id : 0));\n}\n\n// ANS decompression + reduction kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename decompressor_type, size_t max_uncomp_chunk_size, size_t block_size>\n__global__ void decomp_block_fused_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  const size_t * comp_chunk_sizes,\n  int * reduction_result) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto decompressor = decompressor_type();\n  extern __shared__ __align__(cuda::std::max(decompressor.shmem_alignment(), alignof(int))) uint8_t shared_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  __shared__ size_t shared_output_size;\n  __shared__ __align__(decompressor.output_alignment()) uint8_t shared_output_buffer[max_uncomp_chunk_size];\n  assert(reinterpret_cast<uintptr_t>(shared_output_buffer) % decompressor.output_alignment() == 0);\n\n  // Perform ANS decompression within the block -----------\n  // Note: The entire block must call into execute(...).\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    shared_output_buffer,\n    comp_chunk_sizes[global_chunk_id],\n    &shared_output_size,\n    shared_scratch_buffer,\n    nullptr);\n\n  __syncthreads();\n  assert(shared_output_size == max_uncomp_chunk_size);\n\n  // Perform a sum reduction within the grid --------------\n  // Thread-level reduction\n  int thread_sum = 0;\n  for(int i = threadIdx.x; i < max_uncomp_chunk_size; i += block_size) {\n    thread_sum += shared_output_buffer[i];\n  }\n  // Block-level reduction\n  // Reuse shared memory allocated for decompression\n  typedef cub::BlockReduce<int, block_size> BlockReduceT;\n  auto temp_storage = reinterpret_cast<typename BlockReduceT::TempStorage*>(shared_scratch_buffer);\n  int block_sum = BlockReduceT(*temp_storage).Sum(thread_sum);\n  // Grid-level reduction\n  if (threadIdx.x == 0) {\n    atomicAdd(reduction_result, block_sum);\n  }\n}\n\ntemplate<unsigned int Arch>\nstatic int ans_gpu_comp_gpu_decomp_and_reduce(const std::vector<int>& data,\n                                              const int ground_truth,\n                                              size_t warmup_iteration_count,\n                                              size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = data.size() * sizeof(int);\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"integers: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 4;\n  constexpr size_t num_chunks_per_block = 1;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 14; // [bytes]\n\n  // Configure the GPU compressor\n  using ans_compressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_chunk, true>() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, ans_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = ans_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(cudaMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = ans_compressor_type().shmem_size_group();\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = ans_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, ans_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Runtime (De)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    comp_block_kernel<ans_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(cudaMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    cudaMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using ans_decompressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_chunk, true>() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: ANS requires no global scratch buffer for decompression\n  static_assert(ans_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = ans_decompressor_type().shmem_size_group();\n\n  int* d_reduction_result;\n  CUDA_CHECK(cudaMalloc(&d_reduction_result, sizeof(int)));\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    CUDA_CHECK(cudaMemsetAsync(d_reduction_result, 0, sizeof(int), stream));\n    decomp_block_fused_kernel<ans_decompressor_type, chunk_size, block_size>\n        <<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_reduction_result\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Validate the reduction result against the ground truth data\n  int h_reduction_result;\n  CUDA_CHECK(cudaMemcpy(&h_reduction_result, d_reduction_result, sizeof(int), cudaMemcpyDefault));\n\n  if (h_reduction_result != ground_truth) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput with reduction (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(cudaFree(d_comp_temp));\n  CUDA_CHECK(cudaFree(d_reduction_result));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n\n  return 0;\n}\n\nint generate_data(std::vector<int>& data) {\n    // Generate 8GB of int data\n    constexpr size_t data_count = 1 << 21;\n    data.reserve(data_count);\n\n    int seed = 20;\n    std::mt19937 gen(seed);\n    std::uniform_int_distribution<> data_dist(0, 255);\n\n    int sum = 0;\n    for(size_t i = 0; i < data_count; ++i) {\n        int value = data_dist(gen);\n        data.emplace_back(value);\n        sum += value;\n    }\n    return sum;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp_and_reduce<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main([[maybe_unused]] int argc,\n         [[maybe_unused]] char* argv[])\n{\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  // Generate data\n  std::vector<int> data;\n  int ground_truth = generate_data(data);\n\n  // Run the example of comp + fused(decomp + sum reduction)\n  return run_with_current_arch<Runner>(data, ground_truth, warmup_iteration_count, total_iteration_count);\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <random>\n#include <hipcub/hipcub.hpp>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the block-level device API for\n// ANS GPU compression, followed by fused decompression and reduction.\n\n// ANS compression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename compressor_type>\n__global__ void comp_block_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_block = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer,\n    tmp_buffer + (tmp_size_block > 0 ? tmp_size_block * global_chunk_id : 0));\n}\n\n// ANS decompression + reduction kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename decompressor_type, size_t max_uncomp_chunk_size, size_t block_size>\n__global__ void decomp_block_fused_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  const size_t * comp_chunk_sizes,\n  int * reduction_result) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto decompressor = decompressor_type();\n  extern __shared__ __align__(cuda::std::max(decompressor.shmem_alignment(), alignof(int))) uint8_t shared_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  __shared__ size_t shared_output_size;\n  __shared__ __align__(decompressor.output_alignment()) uint8_t shared_output_buffer[max_uncomp_chunk_size];\n  assert(reinterpret_cast<uintptr_t>(shared_output_buffer) % decompressor.output_alignment() == 0);\n\n  // Perform ANS decompression within the block -----------\n  // Note: The entire block must call into execute(...).\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    shared_output_buffer,\n    comp_chunk_sizes[global_chunk_id],\n    &shared_output_size,\n    shared_scratch_buffer,\n    nullptr);\n\n  __syncthreads();\n  assert(shared_output_size == max_uncomp_chunk_size);\n\n  // Perform a sum reduction within the grid --------------\n  // Thread-level reduction\n  int thread_sum = 0;\n  for(int i = threadIdx.x; i < max_uncomp_chunk_size; i += block_size) {\n    thread_sum += shared_output_buffer[i];\n  }\n  // Block-level reduction\n  // Reuse shared memory allocated for decompression\n  typedef hipcub::BlockReduce<int, block_size> BlockReduceT;\n  auto temp_storage = reinterpret_cast<typename BlockReduceT::TempStorage*>(shared_scratch_buffer);\n  int block_sum = BlockReduceT(*temp_storage).Sum(thread_sum);\n  // Grid-level reduction\n  if (threadIdx.x == 0) {\n    atomicAdd(reduction_result, block_sum);\n  }\n}\n\ntemplate<unsigned int Arch>\nstatic int ans_gpu_comp_gpu_decomp_and_reduce(const std::vector<int>& data,\n                                              const int ground_truth,\n                                              size_t warmup_iteration_count,\n                                              size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = data.size() * sizeof(int);\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"integers: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 4;\n  constexpr size_t num_chunks_per_block = 1;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 14; // [bytes]\n\n  // Configure the GPU compressor\n  using ans_compressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_chunk, true>() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, ans_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = ans_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(hipMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = ans_compressor_type().shmem_size_group();\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = ans_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, ans_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Runtime (De)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    comp_block_kernel<ans_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(hipMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    hipMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using ans_decompressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<datatype::uint8>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_chunk, true>() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: ANS requires no global scratch buffer for decompression\n  static_assert(ans_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = ans_decompressor_type().shmem_size_group();\n\n  int* d_reduction_result;\n  CUDA_CHECK(hipMalloc(&d_reduction_result, sizeof(int)));\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    CUDA_CHECK(hipMemsetAsync(d_reduction_result, 0, sizeof(int), stream));\n    decomp_block_fused_kernel<ans_decompressor_type, chunk_size, block_size>\n        <<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_reduction_result\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Validate the reduction result against the ground truth data\n  int h_reduction_result;\n  CUDA_CHECK(hipMemcpy(&h_reduction_result, d_reduction_result, sizeof(int), hipMemcpyDefault));\n\n  if (h_reduction_result != ground_truth) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput with reduction (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(hipFree(d_comp_temp));\n  CUDA_CHECK(hipFree(d_reduction_result));\n  CUDA_CHECK(hipStreamDestroy(stream));\n\n  return 0;\n}\n\nint generate_data(std::vector<int>& data) {\n    // Generate 8GB of int data\n    constexpr size_t data_count = 1 << 21;\n    data.reserve(data_count);\n\n    int seed = 20;\n    std::mt19937 gen(seed);\n    std::uniform_int_distribution<> data_dist(0, 255);\n\n    int sum = 0;\n    for(size_t i = 0; i < data_count; ++i) {\n        int value = data_dist(gen);\n        data.emplace_back(value);\n        sum += value;\n    }\n    return sum;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp_and_reduce<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main([[maybe_unused]] int argc,\n         [[maybe_unused]] char* argv[])\n{\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n\n  // Generate data\n  std::vector<int> data;\n  int ground_truth = generate_data(data);\n\n  // Run the example of comp + fused(decomp + sum reduction)\n  return run_with_current_arch<Runner>(data, ground_truth, warmup_iteration_count, total_iteration_count);\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/04_ans_gpu/ans_gpu_decompression_reduction.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the block-level device API for\n// ANS GPU compression and decompression. The input data type can be\n// altered using the `-t` argument. Supported options are `uint8` and\n// `float16`.\n\n// ANS compression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename compressor_type>\n__global__ void comp_block_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_block = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer,\n    tmp_buffer + (tmp_size_block > 0 ? tmp_size_block * global_chunk_id : 0));\n}\n\n// ANS decompression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename decompressor_type>\n__global__ void decomp_block_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  void * const * uncomp_chunks,\n  const size_t * comp_chunk_sizes,\n  size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto decompressor = decompressor_type();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<datatype DT, unsigned int Arch>\nstatic int ans_gpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n    if constexpr (DT ==  datatype::float16) {\n      if(part.size() % 2 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (float16), all input file sizes must be divisible by 2\");\n      }\n    }\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // (De)Compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 8;\n  constexpr size_t num_chunks_per_block = 1;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Configure the GPU compressor\n  using ans_compressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<DT>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_block, true>() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, ans_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = ans_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(cudaMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = ans_compressor_type().shmem_size_group();\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = ans_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, ans_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_block_kernel<ans_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(cudaMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    cudaMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using ans_decompressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<DT>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_block, true>() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: ANS requires no global scratch buffer for decompression\n  static_assert(ans_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = ans_decompressor_type().shmem_size_group();\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(chunk_size, batch_size, ans_decompressor_type().output_alignment());\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_block_kernel<ans_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (input_data != decomp_data) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(cudaFree(d_comp_temp));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: ans_gpu_compression_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n  std::cerr << \"  -t {uint8 or float16}\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct RunnerUint8 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp<datatype::uint8, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerFloat16 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp<datatype::float16, Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n  bool is_data_uint8 = true;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else if(strcmp(current_argv, \"-t\") == 0) {\n        if(i >= argc) {\n            std::cerr << \"Missing value for argument '-t <data type>'\" << std::endl;\n            print_usage();\n            return 1;\n        }\n        const char* data_type = argv[i++];\n        if(strcmp(data_type, \"uint8\") == 0) {\n            is_data_uint8 = true;\n        } else if(strcmp(data_type, \"float16\") == 0) {\n            is_data_uint8 = false;\n        } else {\n            std::cerr << \"Unknown data type selected (\" << data_type << \"). Select from the supported options: uint8, float16.\" << std::endl;\n            print_usage();\n            return 1;\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  if(is_data_uint8) {\n    return run_with_current_arch<RunnerUint8>(data, warmup_iteration_count, total_iteration_count);\n  }\n  return run_with_current_arch<RunnerFloat16>(data, warmup_iteration_count, total_iteration_count);\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the block-level device API for\n// ANS GPU compression and decompression. The input data type can be\n// altered using the `-t` argument. Supported options are `uint8` and\n// `float16`.\n\n// ANS compression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename compressor_type>\n__global__ void comp_block_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_block = compressor.tmp_size_group();\n  extern __shared__ __align__(compressor.shmem_alignment()) uint8_t shared_comp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_comp_scratch_buffer) % compressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    shared_comp_scratch_buffer,\n    tmp_buffer + (tmp_size_block > 0 ? tmp_size_block * global_chunk_id : 0));\n}\n\n// ANS decompression kernel, using the preconfigured decompressor\n// 1 block per chunk\ntemplate<typename decompressor_type>\n__global__ void decomp_block_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  void * const * uncomp_chunks,\n  const size_t * comp_chunk_sizes,\n  size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = blockIdx.x;\n\n  auto decompressor = decompressor_type();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  // Note: The entire block must call into execute(...).\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<datatype DT, unsigned int Arch>\nstatic int ans_gpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n    if constexpr (DT ==  datatype::float16) {\n      if(part.size() % 2 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (float16), all input file sizes must be divisible by 2\");\n      }\n    }\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // (De)Compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 8;\n  constexpr size_t num_chunks_per_block = 1;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Configure the GPU compressor\n  using ans_compressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<DT>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_block, true>() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, ans_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = ans_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(hipMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  const auto comp_shared_memory = ans_compressor_type().shmem_size_group();\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = ans_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, ans_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_block_kernel<ans_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(hipMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    hipMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using ans_decompressor_type =\n    decltype(Algorithm<algorithm::ans>() +\n             DataType<DT>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Block() +\n             BlockWarp<num_warps_per_block, true>() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: ANS requires no global scratch buffer for decompression\n  static_assert(ans_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = ans_decompressor_type().shmem_size_group();\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(chunk_size, batch_size, ans_decompressor_type().output_alignment());\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_block_kernel<ans_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (input_data != decomp_data) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(hipFree(d_comp_temp));\n  CUDA_CHECK(hipStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: ans_gpu_compression_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n  std::cerr << \"  -t {uint8 or float16}\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct RunnerUint8 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp<datatype::uint8, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerFloat16 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return ans_gpu_comp_gpu_decomp<datatype::float16, Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n  bool is_data_uint8 = true;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else if(strcmp(current_argv, \"-t\") == 0) {\n        if(i >= argc) {\n            std::cerr << \"Missing value for argument '-t <data type>'\" << std::endl;\n            print_usage();\n            return 1;\n        }\n        const char* data_type = argv[i++];\n        if(strcmp(data_type, \"uint8\") == 0) {\n            is_data_uint8 = true;\n        } else if(strcmp(data_type, \"float16\") == 0) {\n            is_data_uint8 = false;\n        } else {\n            std::cerr << \"Unknown data type selected (\" << data_type << \"). Select from the supported options: uint8, float16.\" << std::endl;\n            print_usage();\n            return 1;\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  if(is_data_uint8) {\n    return run_with_current_arch<RunnerUint8>(data, warmup_iteration_count, total_iteration_count);\n  }\n  return run_with_current_arch<RunnerFloat16>(data, warmup_iteration_count, total_iteration_count);\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/04_ans_gpu/ans_gpu_compression_decompression.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <nvrtc.h>\n#include <nvJitLink.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n#include \"../common/util_nvrtc.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU decompression. The decompression kernel is compiled and linked\n// during runtime. The compression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per thread block\n\nconst char* decomp_kernel = R\"kernel(\n#include <nvcompdx.hpp>\n\nusing namespace nvcompdx;\n\nextern \"C\" __global__ void decomp_warp_kernel(\n    size_t batch_size,\n    const void * const * comp_chunks,\n    void * const * uncomp_chunks,\n    const size_t * comp_chunk_sizes,\n    size_t * decomp_chunk_sizes) {\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  using decompressor_type =\n    decltype(Algorithm<algorithm::ALGORITHM>() +\n             DataType<datatype::DATATYPE>() +\n             MaxUncompChunkSize<MAX_UNCOMP_CHUNK_SIZE>() +\n             Direction<direction::decompress>() +\n             Warp() +\n             SM<ARCH>());\n\n  auto decompressor = decompressor_type();\n  constexpr auto shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_scratch_decomp_buffer[];\n\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_scratch_decomp_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n)kernel\";\n\nstatic std::string get_device_architecture_option(CUdevice& device)\n{\n  // Note:\n  // -arch=compute_...       will generate PTX\n  // -arch=sm_...            will generate SASS\n  // -arch=sm_... with -dlto will generate LTO IR\n  std::string option = \"-arch=sm_\" + std::to_string(get_device_architecture(device));\n  return option;\n}\n\nstatic std::vector<std::string> get_comp_include_dirs()\n{\n#ifndef NVCOMPDX_INCLUDE_DIRS\n  return std::vector<std::string>();\n#else\n  std::vector<std::string> comp_include_dirs_array;\n  {\n    std::string comp_include_dirs = NVCOMPDX_INCLUDE_DIRS;\n    std::string delim             = \",\";\n    size_t      start             = 0U;\n    size_t      end               = comp_include_dirs.find(delim);\n    while (end != std::string::npos) {\n      comp_include_dirs_array.push_back(\"--include-path=\" +\n        comp_include_dirs.substr(start, end - start));\n      start = end + delim.length();\n      end   = comp_include_dirs.find(delim, start);\n    }\n    comp_include_dirs_array.push_back(\"--include-path=\" +\n      comp_include_dirs.substr(start, end - start));\n  }\n#endif // NVCOMPDX_INCLUDE_DIRS\n#ifdef COMMONDX_INCLUDE_DIR\n  {\n    comp_include_dirs_array.push_back(\"--include-path=\" + std::string(COMMONDX_INCLUDE_DIR));\n  }\n#endif // COMMONDX_INCLUDE_DIR\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_COMMONDX_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n    }\n  }\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n    }\n  }\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_CUDA_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cccl\");\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cuda/std\");\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cccl/cuda/std\");\n    }\n  }\n  return comp_include_dirs_array;\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int run_nvrtc_example(const std::vector<std::vector<char>>& data)\n{\n  assert(!data.empty());\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Build up input batch on CPU\n  BatchDataCPU input_data_cpu(data, chunk_size);\n  size_t batch_size = input_data_cpu.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu(\n      LZ4_compressBound(chunk_size), batch_size);\n\n  // Compressing on the CPU\n  // Loop over chunks on the CPU, compressing each one one by one\n  for (size_t i = 0; i < batch_size; ++i) {\n    // Could use LZ4_compress_default or LZ4_compress_fast instead\n    const int size = LZ4_compress_HC(\n        static_cast<const char*>(input_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(input_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        12 /* compression level */);\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to compress chunk \" + std::to_string(i) + \".\");\n    }\n\n    // Set the actual compressed size\n    compressed_data_cpu.chunk_sizes()[i] = static_cast<size_t>(size);\n  }\n\n  // Compute compression ratio\n  size_t* compressed_sizes_host = compressed_data_cpu.chunk_sizes();\n  size_t comp_bytes =\n    std::accumulate(compressed_sizes_host, compressed_sizes_host + batch_size, size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Direction<direction::decompress>() +\n             Warp() +\n             SM<Arch>());\n\n  // Runtime decompression parameters\n  const auto block_count =\n    static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  // Global scratch buffer\n  // Note: lz4 decompression requires no global scratch buffer\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory =\n    static_cast<unsigned int>(lz4_decompressor_type().shmem_size_group() * num_chunks_per_block);\n\n  // Copy compressed data to GPU\n  BatchData compressed_data(compressed_data_cpu, true, lz4_decompressor_type().input_alignment());\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(input_data_cpu, false, lz4_decompressor_type().output_alignment());\n\n  // Create an NVRTC program out of the string-defined kernel\n  nvrtcProgram program;\n  NVRTC_CHECK(nvrtcCreateProgram(&program,\n                                 decomp_kernel,\n                                 NULL /* CUDA program name */,\n                                 0 /* numHeaders */,\n                                 NULL /* headers */,\n                                 NULL /* includeNames */));\n\n  // Prepare compilation options\n  CUdevice cuDevice;\n  CU_CHECK(cuInit(0 /* flags */));\n  CU_CHECK(cuDeviceGet(&cuDevice, 0 /* by default using the first device */));\n  const auto gpu_architecture_option = get_device_architecture_option(cuDevice);\n  std::vector<const char*> opts = {\n    \"--std=c++17\",\n    \"--device-as-default-execution-space\",\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR, // Path to the CUDA include directory\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cccl\", // Path to CCCL headers (CTK 13+)\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cuda/std\", // Path to standard headers (CTK <13)\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cccl/cuda/std\", // Path to standard headers (CTK 13+)\n    \"-dlto\",\n    \"-rdc=true\",\n    gpu_architecture_option.c_str()\n  };\n\n  auto opt_convert_define = [](const auto& s1, const auto& s2) {\n    return std::string(\"-D\") + s1 + std::string(\"=\") + s2;\n  };\n\n  // Compiler definitions\n  std::vector<std::string> comp_config_values = {\n    opt_convert_define(\"ALGORITHM\", \"lz4\"),\n    opt_convert_define(\"DATATYPE\", \"uint8\"),\n    opt_convert_define(\"MAX_UNCOMP_CHUNK_SIZE\", std::to_string(chunk_size)),\n    opt_convert_define(\"ARCH\", std::to_string(Arch))\n  };\n  for (auto& config : comp_config_values) {\n    opts.push_back(config.c_str());\n  }\n\n  // Include folder paths\n  std::vector<std::string> comp_include_dirs = get_comp_include_dirs();\n  for (auto& d : comp_include_dirs) {\n    opts.push_back(d.c_str());\n  }\n\n  // Compile kernel via nvrtc\n  nvrtcResult compileResult = nvrtcCompileProgram(program,\n                                                  static_cast<int>(opts.size()),\n                                                  opts.data());\n  if (compileResult != NVRTC_SUCCESS) {\n      // Obtain compilation log from the program if unsuccessful\n      for (auto option : opts) {\n          std::cout << option << std::endl;\n      }\n      print_nvrtc_program_log(std::cerr, program);\n      std::exit(1);\n  }\n\n  // Obtain generated LTO IR from the program\n  size_t lto_size;\n  NVRTC_CHECK(nvrtcGetLTOIRSize(program, &lto_size));\n  auto ltoir = std::make_unique<char[]>(lto_size);\n  NVRTC_CHECK(nvrtcGetLTOIR(program, ltoir.get()));\n  NVRTC_CHECK(nvrtcDestroyProgram(&program));\n\n  // Load the generated Cubin and get a handle to our kernel\n  CUcontext context;\n#if CUDA_VERSION >= 13000\n  CU_CHECK(cuCtxCreate(&context, (CUctxCreateParams*)0, 0 /* flags */, cuDevice));\n#else\n  CU_CHECK(cuCtxCreate(&context, 0 /* flags */, cuDevice));\n#endif // CUDA_VERSION >= 13000\n\n  // Load the generated LTO IR and the static nvCOMPDx LTO library\n  nvJitLinkHandle linker;\n  std::vector<const char*> lopts;\n  lopts.emplace_back(\"-lto\");\n  lopts.emplace_back(gpu_architecture_option.c_str());\n  NVJITLINK_CHECK(linker, nvJitLinkCreate(&linker,\n                                          static_cast<uint32_t>(lopts.size()),\n                                          lopts.data()));\n\n  // Add the runtime-compiled kernel LTO IR\n  NVJITLINK_CHECK(linker,\n    nvJitLinkAddData(linker, NVJITLINK_INPUT_LTOIR, ltoir.get(), lto_size, \"lto_online\"));\n\n  // Add nvCOMPDx LTO library or the nvCOMPDx fatbinary\n  const char* fatbin_env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_FATBIN\");\n  const char* library_env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_LIBRARY\");\n  if(fatbin_env_ptr) {\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_FATBIN, fatbin_env_ptr));\n  } else if(library_env_ptr) {\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_LIBRARY, library_env_ptr));\n  } else {\n#if defined(NVCOMPDX_FATBIN)\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_FATBIN, NVCOMPDX_FATBIN));\n#elif defined(NVCOMPDX_LIBRARY)\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_LIBRARY, NVCOMPDX_LIBRARY));\n#else\n    std::cerr << \"Please set one of the environment variables: \"\n              << \"NVCOMPDX_EXAMPLE_NVCOMPDX_LIBRARY, \"\n              << \"NVCOMPDX_EXAMPLE_NVCOMPDX_FATBIN, \" << std::endl\n              << \"or during compilation define NVCOMPDX_LIBRARY or \"\n              << \"NVCOMPDX_FATBIN.\" << std::endl;\n    return 1;\n#endif\n  }\n\n  // Generate the cubin from the LTO IR sources\n  NVJITLINK_CHECK(linker, nvJitLinkComplete(linker));\n\n  // Acquire cubin\n  size_t cubin_size;\n  NVJITLINK_CHECK(linker, nvJitLinkGetLinkedCubinSize(linker, &cubin_size));\n  auto cubin = std::make_unique<char[]>(cubin_size);\n  NVJITLINK_CHECK(linker, nvJitLinkGetLinkedCubin(linker, cubin.get()));\n  NVJITLINK_CHECK(linker, nvJitLinkDestroy(&linker));\n\n  // Load cubin\n  CUmodule module;\n  CUfunction kernel;\n  CU_CHECK(cuModuleLoadDataEx(&module, cubin.get(), 0 /* numOptions */, NULL, NULL));\n  CU_CHECK(cuModuleGetFunction(&kernel, module, \"decomp_warp_kernel\"));\n\n  // Set dynamic shared memory needs\n  CU_CHECK(cuFuncSetAttribute(kernel,\n                              CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,\n                              decomp_shared_memory));\n\n  // Start with the actual decompression\n  auto comp_chunks = compressed_data.chunk_ptrs();\n  auto uncomp_chunks = decomp_data.chunk_ptrs();\n  auto comp_chunk_sizes = compressed_data.chunk_sizes();\n  auto decomp_chunk_sizes = decomp_data.chunk_sizes();\n  void* args[] = {\n    &batch_size,\n    &comp_chunks,\n    &uncomp_chunks,\n    &comp_chunk_sizes,\n    &decomp_chunk_sizes\n  };\n  CU_CHECK(cuLaunchKernel(kernel,\n                          block_count /* gridDimX */,\n                          1 /* gridDimY */,\n                          1 /* gridDimZ */,\n                          block_size /* blockDimX */,\n                          1 /* blockDimY */,\n                          1 /* blockDimZ */,\n                          decomp_shared_memory,\n                          NULL /* hStream */,\n                          args,\n                          NULL));\n  CU_CHECK(cuCtxSynchronize());\n\n  // Validate decompressed data against input\n  if (decomp_data != input_data_cpu) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n  return 0;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return run_nvrtc_example<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data);\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <lz4.h>\n#include <lz4hc.h>\n\n#include <hip/hiprtc.h>\n#include <nvJitLink.h>\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n#include \"../common/util_nvrtc.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU decompression. The decompression kernel is compiled and linked\n// during runtime. The compression happens through the host-side\n// lz4 CPU library.\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per thread block\n\nconst char* decomp_kernel = R\"kernel(\n#include <nvcompdx.hpp>\n\nusing namespace nvcompdx;\n\nextern \"C\" __global__ void decomp_warp_kernel(\n    size_t batch_size,\n    const void * const * comp_chunks,\n    void * const * uncomp_chunks,\n    const size_t * comp_chunk_sizes,\n    size_t * decomp_chunk_sizes) {\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  using decompressor_type =\n    decltype(Algorithm<algorithm::ALGORITHM>() +\n             DataType<datatype::DATATYPE>() +\n             MaxUncompChunkSize<MAX_UNCOMP_CHUNK_SIZE>() +\n             Direction<direction::decompress>() +\n             Warp() +\n             SM<ARCH>());\n\n  auto decompressor = decompressor_type();\n  constexpr auto shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_scratch_decomp_buffer[];\n\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_scratch_decomp_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n)kernel\";\n\nstatic std::string get_device_architecture_option(hipDevice_t& device)\n{\n  // Note:\n  // -arch=compute_...       will generate PTX\n  // -arch=sm_...            will generate SASS\n  // -arch=sm_... with -dlto will generate LTO IR\n  std::string option = \"-arch=sm_\" + std::to_string(get_device_architecture(device));\n  return option;\n}\n\nstatic std::vector<std::string> get_comp_include_dirs()\n{\n#ifndef NVCOMPDX_INCLUDE_DIRS\n  return std::vector<std::string>();\n#else\n  std::vector<std::string> comp_include_dirs_array;\n  {\n    std::string comp_include_dirs = NVCOMPDX_INCLUDE_DIRS;\n    std::string delim             = \",\";\n    size_t      start             = 0U;\n    size_t      end               = comp_include_dirs.find(delim);\n    while (end != std::string::npos) {\n      comp_include_dirs_array.push_back(\"--include-path=\" +\n        comp_include_dirs.substr(start, end - start));\n      start = end + delim.length();\n      end   = comp_include_dirs.find(delim, start);\n    }\n    comp_include_dirs_array.push_back(\"--include-path=\" +\n      comp_include_dirs.substr(start, end - start));\n  }\n#endif // NVCOMPDX_INCLUDE_DIRS\n#ifdef COMMONDX_INCLUDE_DIR\n  {\n    comp_include_dirs_array.push_back(\"--include-path=\" + std::string(COMMONDX_INCLUDE_DIR));\n  }\n#endif // COMMONDX_INCLUDE_DIR\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_COMMONDX_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n    }\n  }\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n    }\n  }\n  {\n    const char* env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_CUDA_INCLUDE_DIR\");\n    if (env_ptr != nullptr) {\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr));\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cccl\");\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cuda/std\");\n        comp_include_dirs_array.push_back(\"--include-path=\" + std::string(env_ptr) + \"/cccl/cuda/std\");\n    }\n  }\n  return comp_include_dirs_array;\n}\n\n// Benchmark performance from the binary data file\ntemplate<unsigned int Arch>\nstatic int run_nvrtc_example(const std::vector<std::vector<char>>& data)\n{\n  assert(!data.empty());\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n  }\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // Compile-time (de)compression parameters\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 16; // [bytes]\n\n  // Build up input batch on CPU\n  BatchDataCPU input_data_cpu(data, chunk_size);\n  size_t batch_size = input_data_cpu.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Allocate and prepare output/compressed batch\n  BatchDataCPU compressed_data_cpu(\n      LZ4_compressBound(chunk_size), batch_size);\n\n  // Compressing on the CPU\n  // Loop over chunks on the CPU, compressing each one one by one\n  for (size_t i = 0; i < batch_size; ++i) {\n    // Could use LZ4_compress_default or LZ4_compress_fast instead\n    const int size = LZ4_compress_HC(\n        static_cast<const char*>(input_data_cpu.chunk_ptrs()[i]),\n        static_cast<char*>(compressed_data_cpu.chunk_ptrs()[i]),\n        static_cast<int>(input_data_cpu.chunk_sizes()[i]),\n        static_cast<int>(compressed_data_cpu.chunk_sizes()[i]),\n        12 /* compression level */);\n    if (size == 0) {\n      throw std::runtime_error(\n          \"LZ4 CPU failed to compress chunk \" + std::to_string(i) + \".\");\n    }\n\n    // Set the actual compressed size\n    compressed_data_cpu.chunk_sizes()[i] = static_cast<size_t>(size);\n  }\n\n  // Compute compression ratio\n  size_t* compressed_sizes_host = compressed_data_cpu.chunk_sizes();\n  size_t comp_bytes =\n    std::accumulate(compressed_sizes_host, compressed_sizes_host + batch_size, size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n            << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n            << (double)total_bytes / comp_bytes << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<datatype::uint8>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Direction<direction::decompress>() +\n             Warp() +\n             SM<Arch>());\n\n  // Runtime decompression parameters\n  const auto block_count =\n    static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  // Global scratch buffer\n  // Note: lz4 decompression requires no global scratch buffer\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory =\n    static_cast<unsigned int>(lz4_decompressor_type().shmem_size_group() * num_chunks_per_block);\n\n  // Copy compressed data to GPU\n  BatchData compressed_data(compressed_data_cpu, true, lz4_decompressor_type().input_alignment());\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(input_data_cpu, false, lz4_decompressor_type().output_alignment());\n\n  // Create an NVRTC program out of the string-defined kernel\n  hiprtcProgram program;\n  NVRTC_CHECK(hiprtcCreateProgram(&program,\n                                 decomp_kernel,\n                                 NULL /* CUDA program name */,\n                                 0 /* numHeaders */,\n                                 NULL /* headers */,\n                                 NULL /* includeNames */));\n\n  // Prepare compilation options\n  hipDevice_t cuDevice;\n  CU_CHECK(hipInit(0 /* flags */));\n  CU_CHECK(hipDeviceGet(&cuDevice, 0 /* by default using the first device */));\n  const auto gpu_architecture_option = get_device_architecture_option(cuDevice);\n  std::vector<const char*> opts = {\n    \"--std=c++17\",\n    \"--device-as-default-execution-space\",\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR, // Path to the CUDA include directory\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cccl\", // Path to CCCL headers (CTK 13+)\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cuda/std\", // Path to standard headers (CTK <13)\n    \"--include-path=\" CUDAToolkit_INCLUDE_DIR \"/cccl/cuda/std\", // Path to standard headers (CTK 13+)\n    \"-dlto\",\n    \"-rdc=true\",\n    gpu_architecture_option.c_str()\n  };\n\n  auto opt_convert_define = [](const auto& s1, const auto& s2) {\n    return std::string(\"-D\") + s1 + std::string(\"=\") + s2;\n  };\n\n  // Compiler definitions\n  std::vector<std::string> comp_config_values = {\n    opt_convert_define(\"ALGORITHM\", \"lz4\"),\n    opt_convert_define(\"DATATYPE\", \"uint8\"),\n    opt_convert_define(\"MAX_UNCOMP_CHUNK_SIZE\", std::to_string(chunk_size)),\n    opt_convert_define(\"ARCH\", std::to_string(Arch))\n  };\n  for (auto& config : comp_config_values) {\n    opts.push_back(config.c_str());\n  }\n\n  // Include folder paths\n  std::vector<std::string> comp_include_dirs = get_comp_include_dirs();\n  for (auto& d : comp_include_dirs) {\n    opts.push_back(d.c_str());\n  }\n\n  // Compile kernel via nvrtc\n  hiprtcResult compileResult = hiprtcCompileProgram(program,\n                                                  static_cast<int>(opts.size()),\n                                                  opts.data());\n  if (compileResult != HIPRTC_SUCCESS) {\n      // Obtain compilation log from the program if unsuccessful\n      for (auto option : opts) {\n          std::cout << option << std::endl;\n      }\n      print_nvrtc_program_log(std::cerr, program);\n      std::exit(1);\n  }\n\n  // Obtain generated LTO IR from the program\n  size_t lto_size;\n  NVRTC_CHECK(nvrtcGetLTOIRSize(program, &lto_size));\n  auto ltoir = std::make_unique<char[]>(lto_size);\n  NVRTC_CHECK(nvrtcGetLTOIR(program, ltoir.get()));\n  NVRTC_CHECK(hiprtcDestroyProgram(&program));\n\n  // Load the generated Cubin and get a handle to our kernel\n  hipCtx_t context;\n#if CUDA_VERSION >= 13000\n  CU_CHECK(hipCtxCreate(&context, (CUctxCreateParams*)0, 0 /* flags */, cuDevice));\n#else\n  CU_CHECK(hipCtxCreate(&context, 0 /* flags */, cuDevice));\n#endif // CUDA_VERSION >= 13000\n\n  // Load the generated LTO IR and the static nvCOMPDx LTO library\n  nvJitLinkHandle linker;\n  std::vector<const char*> lopts;\n  lopts.emplace_back(\"-lto\");\n  lopts.emplace_back(gpu_architecture_option.c_str());\n  NVJITLINK_CHECK(linker, nvJitLinkCreate(&linker,\n                                          static_cast<uint32_t>(lopts.size()),\n                                          lopts.data()));\n\n  // Add the runtime-compiled kernel LTO IR\n  NVJITLINK_CHECK(linker,\n    nvJitLinkAddData(linker, NVJITLINK_INPUT_LTOIR, ltoir.get(), lto_size, \"lto_online\"));\n\n  // Add nvCOMPDx LTO library or the nvCOMPDx fatbinary\n  const char* fatbin_env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_FATBIN\");\n  const char* library_env_ptr = std::getenv(\"NVCOMPDX_EXAMPLE_NVCOMPDX_LIBRARY\");\n  if(fatbin_env_ptr) {\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_FATBIN, fatbin_env_ptr));\n  } else if(library_env_ptr) {\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_LIBRARY, library_env_ptr));\n  } else {\n#if defined(NVCOMPDX_FATBIN)\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_FATBIN, NVCOMPDX_FATBIN));\n#elif defined(NVCOMPDX_LIBRARY)\n    NVJITLINK_CHECK(linker, nvJitLinkAddFile(linker, NVJITLINK_INPUT_LIBRARY, NVCOMPDX_LIBRARY));\n#else\n    std::cerr << \"Please set one of the environment variables: \"\n              << \"NVCOMPDX_EXAMPLE_NVCOMPDX_LIBRARY, \"\n              << \"NVCOMPDX_EXAMPLE_NVCOMPDX_FATBIN, \" << std::endl\n              << \"or during compilation define NVCOMPDX_LIBRARY or \"\n              << \"NVCOMPDX_FATBIN.\" << std::endl;\n    return 1;\n#endif\n  }\n\n  // Generate the cubin from the LTO IR sources\n  NVJITLINK_CHECK(linker, nvJitLinkComplete(linker));\n\n  // Acquire cubin\n  size_t cubin_size;\n  NVJITLINK_CHECK(linker, nvJitLinkGetLinkedCubinSize(linker, &cubin_size));\n  auto cubin = std::make_unique<char[]>(cubin_size);\n  NVJITLINK_CHECK(linker, nvJitLinkGetLinkedCubin(linker, cubin.get()));\n  NVJITLINK_CHECK(linker, nvJitLinkDestroy(&linker));\n\n  // Load cubin\n  hipModule_t module;\n  hipFunction_t kernel;\n  CU_CHECK(hipModuleLoadDataEx(&module, cubin.get(), 0 /* numOptions */, NULL, NULL));\n  CU_CHECK(hipModuleGetFunction(&kernel, module, \"decomp_warp_kernel\"));\n\n  // Set dynamic shared memory needs\n  CU_CHECK(cuFuncSetAttribute(kernel,\n                              HIP_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,\n                              decomp_shared_memory));\n\n  // Start with the actual decompression\n  auto comp_chunks = compressed_data.chunk_ptrs();\n  auto uncomp_chunks = decomp_data.chunk_ptrs();\n  auto comp_chunk_sizes = compressed_data.chunk_sizes();\n  auto decomp_chunk_sizes = decomp_data.chunk_sizes();\n  void* args[] = {\n    &batch_size,\n    &comp_chunks,\n    &uncomp_chunks,\n    &comp_chunk_sizes,\n    &decomp_chunk_sizes\n  };\n  CU_CHECK(hipModuleLaunchKernel(kernel,\n                          block_count /* gridDimX */,\n                          1 /* gridDimY */,\n                          1 /* gridDimZ */,\n                          block_size /* blockDimX */,\n                          1 /* blockDimY */,\n                          1 /* blockDimZ */,\n                          decomp_shared_memory,\n                          NULL /* hStream */,\n                          args,\n                          NULL));\n  CU_CHECK(hipCtxSynchronize());\n\n  // Validate decompressed data against input\n  if (decomp_data != input_data_cpu) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n  return 0;\n}\n\ntemplate<unsigned int Arch>\nstruct Runner {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return run_nvrtc_example<Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  return run_with_current_arch<Runner>(data);\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/05_lz4_cpu_and_nvrtc/lz4_cpu_compression_nvrtc_decompression.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU compression and decompression. The input data type can be\n// altered using the `-t` argument. Supported options are `uint8`,\n// `uint16`, and `uint32`.\n\n// LZ4 compression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per block\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_warp = compressor.tmp_size_group();\n\n  // Note: Each warp calls into execute(...) processing different chunks\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    nullptr,\n    tmp_buffer + tmp_size_warp * global_chunk_id);\n}\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per block\ntemplate<typename decompressor_type>\n__global__ void decomp_warp_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  void * const * uncomp_chunks,\n  const size_t * comp_chunk_sizes,\n  size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto decompressor = decompressor_type();\n  constexpr size_t shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  // Note: Each warp calls into execute(...) processing different chunks\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<datatype DT, unsigned int Arch>\nstatic int lz4_gpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n    if constexpr (DT == datatype::uint16) {\n      if(part.size() % 2 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (uint16), all input file sizes must be divisible by 2\");\n      }\n    } else if constexpr (DT == datatype::uint32) {\n      if(part.size() % 4 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (uint32), all input file sizes must be divisible by 4\");\n      }\n    }\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // (De)Compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 15; // [bytes]\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<DT>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, lz4_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = lz4_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(cudaMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  constexpr auto comp_shared_memory = lz4_compressor_type().shmem_size_group() * num_chunks_per_block;\n  // LZ4 compression requires no shared memory\n  static_assert(comp_shared_memory == 0);\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = lz4_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, lz4_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(cudaMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    cudaMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<DT>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: LZ4 requires no global scratch buffer for decompression\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = lz4_decompressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(chunk_size, batch_size, lz4_decompressor_type().output_alignment());\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_warp_kernel<lz4_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(cudaGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (input_data != decomp_data) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(cudaFree(d_comp_temp));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_gpu_compression_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n  std::cerr << \"  -t {uint8, uint16, or uint32}\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct RunnerUint8 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint8, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerUint16 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint16, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerUint32 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint32, Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n  datatype selected_data_type = datatype::uint8;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else if(strcmp(current_argv, \"-t\") == 0) {\n        if(i >= argc) {\n            std::cerr << \"Missing value for argument '-t <data type>'\" << std::endl;\n            print_usage();\n            return 1;\n        }\n        const char* data_type = argv[i++];\n        if(strcmp(data_type, \"uint8\") == 0) {\n          selected_data_type = datatype::uint8;\n        } else if(strcmp(data_type, \"uint16\") == 0) {\n            selected_data_type = datatype::uint16;\n        } else if(strcmp(data_type, \"uint32\") == 0) {\n            selected_data_type = datatype::uint32;\n        } else {\n            std::cerr << \"Unknown data type selected (\" << data_type << \"). Select from the supported options: uint8, uint16, or uint32.\" << std::endl;\n            print_usage();\n            return 1;\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  switch(selected_data_type) {\n    case datatype::uint8:\n      return run_with_current_arch<RunnerUint8>(data, warmup_iteration_count, total_iteration_count);\n    case datatype::uint16:\n      return run_with_current_arch<RunnerUint16>(data, warmup_iteration_count, total_iteration_count);\n    case datatype::uint32:\n      return run_with_current_arch<RunnerUint32>(data, warmup_iteration_count, total_iteration_count);\n    default:\n      return -1;\n  }\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <nvcompdx.hpp>\n#include \"../common/batch_data.hpp\"\n\nusing namespace nvcompdx;\n\n// This sample demonstrates the usage of the warp-level device API for\n// LZ4 GPU compression and decompression. The input data type can be\n// altered using the `-t` argument. Supported options are `uint8`,\n// `uint16`, and `uint32`.\n\n// LZ4 compression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per block\ntemplate<typename compressor_type>\n__global__ void comp_warp_kernel(\n  size_t batch_size,\n  const void * const * uncomp_chunks,\n  const size_t * uncomp_chunk_sizes,\n  void * const * comp_chunks,\n  size_t * comp_chunk_sizes,\n  uint8_t * tmp_buffer) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(compressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto compressor = compressor_type();\n  constexpr size_t tmp_size_warp = compressor.tmp_size_group();\n\n  // Note: Each warp calls into execute(...) processing different chunks\n  compressor.execute(\n    uncomp_chunks[global_chunk_id],\n    comp_chunks[global_chunk_id],\n    uncomp_chunk_sizes[global_chunk_id],\n    comp_chunk_sizes + global_chunk_id,\n    nullptr,\n    tmp_buffer + tmp_size_warp * global_chunk_id);\n}\n\n// LZ4 decompression kernel, using the preconfigured decompressor\n// 1 warp per chunk, but multiple chunks per block\ntemplate<typename decompressor_type>\n__global__ void decomp_warp_kernel(\n  size_t batch_size,\n  const void * const * comp_chunks,\n  void * const * uncomp_chunks,\n  const size_t * comp_chunk_sizes,\n  size_t * decomp_chunk_sizes) {\n  // Note:\n  // Given the (de)compressor expression has an SM<> operator,\n  // it makes the fully-typed kernel only applicable on one targeted device architecture.\n  // We need to signal to the compiler not to continue compiling this kernel whenever\n  // the current compilation architecture is different from the one specified in\n  // the SM<> operator.\n  NVCOMPDX_SKIP_IF_NOT_APPLICABLE(decompressor_type);\n\n  const unsigned int global_chunk_id = (blockIdx.x * blockDim.x + threadIdx.x) / 32;\n  const unsigned int local_chunk_id = threadIdx.x / 32;\n  if(global_chunk_id >= batch_size) {\n    return;\n  }\n\n  auto decompressor = decompressor_type();\n  constexpr size_t shmem_size_warp = decompressor.shmem_size_group();\n  extern __shared__ __align__(decompressor.shmem_alignment()) uint8_t shared_decomp_scratch_buffer[];\n  assert(reinterpret_cast<uintptr_t>(shared_decomp_scratch_buffer) % decompressor.shmem_alignment() == 0);\n\n  // Note: Each warp calls into execute(...) processing different chunks\n  decompressor.execute(\n    comp_chunks[global_chunk_id],\n    uncomp_chunks[global_chunk_id],\n    comp_chunk_sizes[global_chunk_id],\n    decomp_chunk_sizes + global_chunk_id,\n    shared_decomp_scratch_buffer + shmem_size_warp * local_chunk_id,\n    nullptr);\n}\n\n// Benchmark performance from the binary data file\ntemplate<datatype DT, unsigned int Arch>\nstatic int lz4_gpu_comp_gpu_decomp(const std::vector<std::vector<char>>& data,\n                                   size_t warmup_iteration_count,\n                                   size_t total_iteration_count)\n{\n  assert(!data.empty());\n  if(warmup_iteration_count >= total_iteration_count) {\n    throw std::runtime_error(\"ERROR: the total iteration count must be greater than the warmup iteration count\");\n  }\n\n  size_t total_bytes = 0;\n  for (const std::vector<char>& part : data) {\n    total_bytes += part.size();\n    if constexpr (DT == datatype::uint16) {\n      if(part.size() % 2 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (uint16), all input file sizes must be divisible by 2\");\n      }\n    } else if constexpr (DT == datatype::uint32) {\n      if(part.size() % 4 != 0) {\n        throw std::runtime_error(\"ERROR: with the selected data type (uint32), all input file sizes must be divisible by 4\");\n      }\n    }\n  }\n\n  std::cout << \"----------\" << std::endl;\n  std::cout << \"files: \" << data.size() << std::endl;\n  std::cout << \"uncompressed (B): \" << total_bytes << std::endl;\n\n  // (De)Compression parameters\n  // We are compressing 1 chunk per thread block\n  constexpr size_t num_warps_per_chunk = 1;\n  constexpr size_t num_chunks_per_block = 4;\n  constexpr size_t num_warps_per_block = num_warps_per_chunk * num_chunks_per_block;\n  constexpr unsigned int block_size = static_cast<unsigned int>(num_warps_per_block * 32);\n  constexpr size_t chunk_size = 1 << 15; // [bytes]\n\n  // Configure the GPU compressor\n  using lz4_compressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<DT>() +\n             Direction<direction::compress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Build up GPU data\n  BatchData input_data(data, chunk_size, lz4_compressor_type().input_alignment());\n  const size_t batch_size = input_data.batch_size();\n  std::cout << \"chunks: \" << batch_size << std::endl;\n\n  // Global scratch buffer\n  size_t comp_temp_bytes = lz4_compressor_type().tmp_size_total(batch_size);\n  uint8_t* d_comp_temp;\n  CUDA_CHECK(hipMalloc(&d_comp_temp, comp_temp_bytes));\n\n  // Shared scratch buffer\n  constexpr auto comp_shared_memory = lz4_compressor_type().shmem_size_group() * num_chunks_per_block;\n  // LZ4 compression requires no shared memory\n  static_assert(comp_shared_memory == 0);\n\n  // Prepare compressed data buffer\n  size_t max_out_bytes = lz4_compressor_type().max_comp_chunk_size();\n  BatchData compressed_data(max_out_bytes, batch_size, lz4_compressor_type().output_alignment());\n\n  // Create CUDA stream\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n\n  // Runtime (de)compression parameters\n  const auto block_count = static_cast<unsigned int>((batch_size + num_chunks_per_block - 1) / num_chunks_per_block);\n\n  float ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&]() {\n    comp_warp_kernel<lz4_compressor_type><<<block_count, block_size, comp_shared_memory, stream>>>(\n      batch_size,\n      input_data.chunk_ptrs(),\n      input_data.chunk_sizes(),\n      compressed_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      d_comp_temp\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Compute compression ratio\n  std::vector<size_t> compressed_sizes_host(batch_size);\n  CUDA_CHECK(hipMemcpy(\n    compressed_sizes_host.data(),\n    compressed_data.chunk_sizes(),\n    batch_size * sizeof(size_t),\n    hipMemcpyDeviceToHost));\n\n  size_t comp_bytes = std::accumulate(\n        compressed_sizes_host.begin(),\n        compressed_sizes_host.end(),\n        size_t(0));\n\n  std::cout << \"comp_size: \" << comp_bytes\n        << \", compressed ratio: \" << std::fixed << std::setprecision(2)\n        << (double)total_bytes / comp_bytes << std::endl;\n  std::cout << \"compression throughput (GB/s): \"\n        << (double)total_bytes / (1.0e6 * ms) << std::endl;\n\n  // Configure the GPU decompressor\n  using lz4_decompressor_type =\n    decltype(Algorithm<algorithm::lz4>() +\n             DataType<DT>() +\n             Direction<direction::decompress>() +\n             MaxUncompChunkSize<chunk_size>() +\n             Warp() +\n             SM<Arch>());\n\n  // Global scratch buffer\n  // Note: LZ4 requires no global scratch buffer for decompression\n  static_assert(lz4_decompressor_type().tmp_size_group() == 0);\n\n  // Shared scratch buffer\n  const auto decomp_shared_memory = lz4_decompressor_type().shmem_size_group() * num_chunks_per_block;\n\n  // Allocate and build up decompression batch on GPU\n  BatchData decomp_data(chunk_size, batch_size, lz4_decompressor_type().output_alignment());\n\n  ms = measure_ms(warmup_iteration_count, total_iteration_count, stream, [&](){\n    decomp_warp_kernel<lz4_decompressor_type><<<block_count, block_size, decomp_shared_memory, stream>>>(\n      batch_size,\n      compressed_data.chunk_ptrs(),\n      decomp_data.chunk_ptrs(),\n      compressed_data.chunk_sizes(),\n      decomp_data.chunk_sizes()\n    );\n    CUDA_CHECK(hipGetLastError());\n  });\n\n  // Validate decompressed data against input\n  if (input_data != decomp_data) {\n    throw std::runtime_error(\"Failed to validate decompressed data\");\n  } else {\n    std::cout << \"decompression validated :)\" << std::endl;\n  }\n\n  double decompression_throughput = ((double)total_bytes / ms) * 1e-6;\n  std::cout << \"decompression throughput (GB/s): \" << decompression_throughput\n            << std::endl;\n\n  CUDA_CHECK(hipFree(d_comp_temp));\n  CUDA_CHECK(hipStreamDestroy(stream));\n\n  return 0;\n}\n\nvoid print_usage()\n{\n  std::cerr << std::endl;\n  std::cerr << \"Usage: lz4_gpu_compression_decompression [OPTIONS]\" << std::endl;\n  std::cerr << \"  -f <input file(s)>\" << std::endl;\n  std::cerr << \"  -t {uint8, uint16, or uint32}\" << std::endl;\n}\n\ntemplate<unsigned int Arch>\nstruct RunnerUint8 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint8, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerUint16 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint16, Arch>(std::forward<Args>(args)...);\n  }\n};\n\ntemplate<unsigned int Arch>\nstruct RunnerUint32 {\n  template<typename... Args>\n  static int run(Args&&... args)\n  {\n    return lz4_gpu_comp_gpu_decomp<datatype::uint32, Arch>(std::forward<Args>(args)...);\n  }\n};\n\nint main(int argc, char* argv[])\n{\n  std::vector<std::string> file_names;\n\n  size_t warmup_iteration_count = 2;\n  size_t total_iteration_count = 5;\n  datatype selected_data_type = datatype::uint8;\n\n  do {\n    if (argc < 3) {\n      break;\n    }\n\n    int i = 1;\n    while (i < argc) {\n      const char* current_argv = argv[i++];\n      if (strcmp(current_argv, \"-f\") == 0) {\n        // Parse until next `-` argument\n        while (i < argc && argv[i][0] != '-') {\n          file_names.emplace_back(argv[i++]);\n        }\n      } else if(strcmp(current_argv, \"-t\") == 0) {\n        if(i >= argc) {\n            std::cerr << \"Missing value for argument '-t <data type>'\" << std::endl;\n            print_usage();\n            return 1;\n        }\n        const char* data_type = argv[i++];\n        if(strcmp(data_type, \"uint8\") == 0) {\n          selected_data_type = datatype::uint8;\n        } else if(strcmp(data_type, \"uint16\") == 0) {\n            selected_data_type = datatype::uint16;\n        } else if(strcmp(data_type, \"uint32\") == 0) {\n            selected_data_type = datatype::uint32;\n        } else {\n            std::cerr << \"Unknown data type selected (\" << data_type << \"). Select from the supported options: uint8, uint16, or uint32.\" << std::endl;\n            print_usage();\n            return 1;\n        }\n      } else {\n        std::cerr << \"Unknown argument: \" << current_argv << std::endl;\n        print_usage();\n        return 1;\n      }\n    }\n  } while (0);\n\n  if (file_names.empty()) {\n    std::cerr << \"Must specify at least one file via '-f <file>'.\" << std::endl;\n    print_usage();\n    return 1;\n  }\n\n  auto data = multi_file(file_names);\n\n  switch(selected_data_type) {\n    case datatype::uint8:\n      return run_with_current_arch<RunnerUint8>(data, warmup_iteration_count, total_iteration_count);\n    case datatype::uint16:\n      return run_with_current_arch<RunnerUint16>(data, warmup_iteration_count, total_iteration_count);\n    case datatype::uint32:\n      return run_with_current_arch<RunnerUint32>(data, warmup_iteration_count, total_iteration_count);\n    default:\n      return -1;\n  }\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/nvCOMPDx/02_lz4_gpu/lz4_gpu_compression_decompression.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate a sequence of random numbers\n// using Philox generator with user-defined round number (default 10). \n\n// The generated log-normally distributed values are the same as the results using cuRAND host API \n// with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering is that using subsequences different sequences each thread generates 4 32-bit random values, \n// and each four values from one sequence are followed by four values from next sequence.\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types Philox_4x32_10 pseudorandom generator\n\nconstexpr unsigned int subsequences = 65536;\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(double4*                        d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size,\n                                const DataType                  mean,\n                                const DataType                  stddev) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i >= size / 4)\n        return;\n\n    curanddx::log_normal<DataType, curanddx::box_muller> dist(mean, stddev);\n\n    RNG rng(seed, ((offset + i) % subsequences), ((offset + i) / subsequences)); // seed, subsequence, offset\n\n    d_out[i] = dist.generate4(rng);\n}\n\ntemplate<unsigned int Arch>\nint philox_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::philox4_32>() + curanddx::PhiloxRounds<10>() +\n                         curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = double;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 1ULL;\n\n    const DataType mean   = 1;\n    const DataType stddev = 0.5;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size / 4 + block_dim - 1) / block_dim;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>((double4*)d_out, seed, offset, size, mean, stddev);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out.data(), d_out, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    // cuRAND host API\n    curandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(curandCreateGenerator(&gen_curand, CURAND_RNG_PSEUDO_PHILOX4_32_10));\n    CURAND_CHECK_AND_EXIT(curandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOffset(gen_curand, offset * 4));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOrdering(gen_curand, CURAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(curandGenerateLogNormalDouble(gen_curand, d_ref, size, mean, stddev));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(curandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count++ < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                ;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct philox_thread_api_functor {\n    int operator()() { return philox_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<philox_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate a sequence of random numbers\n// using Philox generator with user-defined round number (default 10). \n\n// The generated log-normally distributed values are the same as the results using cuRAND host API \n// with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering is that using subsequences different sequences each thread generates 4 32-bit random values, \n// and each four values from one sequence are followed by four values from next sequence.\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types Philox_4x32_10 pseudorandom generator\n\nconstexpr unsigned int subsequences = 65536;\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(double4*                        d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size,\n                                const DataType                  mean,\n                                const DataType                  stddev) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i >= size / 4)\n        return;\n\n    curanddx::log_normal<DataType, curanddx::box_muller> dist(mean, stddev);\n\n    RNG rng(seed, ((offset + i) % subsequences), ((offset + i) / subsequences)); // seed, subsequence, offset\n\n    d_out[i] = dist.generate4(rng);\n}\n\ntemplate<unsigned int Arch>\nint philox_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::philox4_32>() + curanddx::PhiloxRounds<10>() +\n                         curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = double;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 1ULL;\n\n    const DataType mean   = 1;\n    const DataType stddev = 0.5;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size / 4 + block_dim - 1) / block_dim;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>((double4*)d_out, seed, offset, size, mean, stddev);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out.data(), d_out, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    // cuRAND host API\n    hiprandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(hiprandCreateGenerator(&gen_curand, HIPRAND_RNG_PSEUDO_PHILOX4_32_10));\n    CURAND_CHECK_AND_EXIT(hiprandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOffset(gen_curand, offset * 4));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOrdering(gen_curand, HIPRAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(hiprandGenerateLogNormalDouble(gen_curand, d_ref, size, mean, stddev));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(hiprandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count++ < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                ;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct philox_thread_api_functor {\n    int operator()() { return philox_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<philox_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/01_introduction/philox_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate a sequence of random numbers\n// using Philox generator with user-defined round number (default 10). \n\n// The generated randombits values are the same as the results using cuRAND host API \n// with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering is that using subsequences different sequences each thread generates 4 32-bit random values, \n// and each four values from one sequence are followed by four values from next sequence.\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types Philox_4x32_10 pseudorandom generator\n\nconstexpr unsigned int subsequences = 65536;\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(uint4*                        d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i >= size / 4)\n        return;\n\n    RNG rng(seed, ((offset + i) % subsequences), ((offset + i) / subsequences)); // seed, subsequence, offset\n\n    d_out[i] = rng.generate4();\n}\n\ntemplate<unsigned int Arch>\nint philox_random_bits_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::philox4_32>() + curanddx::PhiloxRounds<10>() +\n                         curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = unsigned int;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 1ULL;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size / 4 + block_dim - 1) / block_dim;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>((uint4*)d_out, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out.data(), d_out, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    // cuRAND host API\n    curandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(curandCreateGenerator(&gen_curand, CURAND_RNG_PSEUDO_PHILOX4_32_10));\n    CURAND_CHECK_AND_EXIT(curandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOffset(gen_curand, offset * 4));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOrdering(gen_curand, CURAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(curandGenerate(gen_curand, d_ref, size));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(curandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count++ < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                ;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct philox_random_bits_thread_api_functor {\n    int operator()() { return philox_random_bits_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<philox_random_bits_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate a sequence of random numbers\n// using Philox generator with user-defined round number (default 10). \n\n// The generated randombits values are the same as the results using cuRAND host API \n// with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering is that using subsequences different sequences each thread generates 4 32-bit random values, \n// and each four values from one sequence are followed by four values from next sequence.\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types Philox_4x32_10 pseudorandom generator\n\nconstexpr unsigned int subsequences = 65536;\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(uint4*                        d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    int i = blockDim.x * blockIdx.x + threadIdx.x;\n    if (i >= size / 4)\n        return;\n\n    RNG rng(seed, ((offset + i) % subsequences), ((offset + i) / subsequences)); // seed, subsequence, offset\n\n    d_out[i] = rng.generate4();\n}\n\ntemplate<unsigned int Arch>\nint philox_random_bits_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::philox4_32>() + curanddx::PhiloxRounds<10>() +\n                         curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = unsigned int;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 1ULL;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size / 4 + block_dim - 1) / block_dim;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>((uint4*)d_out, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out.data(), d_out, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    // cuRAND host API\n    hiprandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(hiprandCreateGenerator(&gen_curand, HIPRAND_RNG_PSEUDO_PHILOX4_32_10));\n    CURAND_CHECK_AND_EXIT(hiprandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOffset(gen_curand, offset * 4));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOrdering(gen_curand, HIPRAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(hiprandGenerate(gen_curand, d_ref, size));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(hiprandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count++ < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                ;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct philox_random_bits_thread_api_functor {\n    int operator()() { return philox_random_bits_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<philox_random_bits_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/02_thread_api/philox_random_bits_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n#ifdef CURANDDX_EXAMPLE_NVPL_RAND_AVAILABLE\n#    include <nvpl_rand.h>\n#    define NVPL_RAND_CHECK(x)                                  \\\n        do {                                                    \\\n            if ((x) != NVPL_RAND_STATUS_SUCCESS) {              \\\n                printf(\"Error at %s:%d\\n\", __FILE__, __LINE__); \\\n                _Exit(EXIT_FAILURE);                            \\\n            }                                                   \\\n        } while (0)\n#endif\n\n// This example demonstrates how to use PCG generator and cuRANDDx Thread-level operator to generate a sequence of random 32-bit\n// numbers and compare with the results generated using NVPL RAND, if available, with STRICT ordering\n\ntemplate<class RNG, typename data_type>\n__global__ void generate_kernel(data_type*                      d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    if (tid >= size)\n        return;\n\n    curanddx::uniform_bits<data_type> dist;\n\n    // compare with NVPL RAND PCG strict ordering\n    RNG rng(seed, 0, offset + tid);\n\n    d_out[tid] = dist.generate(rng);\n}\n\ntemplate<unsigned int Arch>\nint simple_pcg_thread_api() {\n    using RNG = decltype(curanddx::Generator<curanddx::pcg>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    using DataType = typename RNG::bitgenerator_result_type;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    unsigned long long seed   = 1234ULL;\n    unsigned long long offset = 1ULL;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size + block_dim - 1) / block_dim;\n    \n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>(d_out, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out.data(), d_out, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out));\n\n#ifdef CURANDDX_EXAMPLE_NVPL_RAND_AVAILABLE\n    // nvpl RAND\n    std::vector<DataType> h_ref(size);\n    nvplRandGenerator_t   gen;\n    const auto            generator_type = NVPL_RAND_RNG_PSEUDO_PCG;\n\n    NVPL_RAND_CHECK(nvplRandMTCreateGeneratorDefault(&gen, generator_type));\n    NVPL_RAND_CHECK(nvplRandSetPseudoRandomGeneratorSeed(gen, seed));\n    NVPL_RAND_CHECK(nvplRandSetGeneratorOffset(gen, offset));\n    NVPL_RAND_CHECK(nvplRandMTSetGeneratorOrdering(gen, NVPL_RAND_ORDERING_STRICT));\n\n    // Generate\n    NVPL_RAND_CHECK(nvplRandGenerate(gen, h_ref.data(), size));\n    NVPL_RAND_CHECK(nvplRandDestroyGenerator(gen));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and nvpl RAND API using STRICT ordering.\\n\";\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count < 10) {\n                printf(\"array_curanddx[%u] = %u, array_nvplrand[%u] = %u \\n\", i, h_out[i], i, h_ref[i]);\n                count++;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and NVPL RAND Host API using STRICT \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n\n    // compute hash to be used if nvpl rand is not available\n    unsigned int xor_nvplrand = 0x0;\n    for (auto i = 0U; i < size; i++) {\n        xor_nvplrand ^= h_ref[i];\n    }\n    std::cout << \"NVPL RAND reference xor output is \" << std::hex << xor_nvplrand << std::endl;\n    return 0;\n\n#else\n    // Compare hash\n    unsigned int xor_curand = 0x0;\n    for (auto i = 0U; i < size; i++) {\n        xor_curand ^= h_out[i];\n    }\n    if (xor_curand == 0xaa706742) {\n        std::cout << \"Compared to the hash value: Same sequence is generated with NVPL RAND and cuRANDDx generator \"\n                     \"using STRICT ordering.\\n\";\n        std::cout << \"SUCCESS \\n\";\n        return 0;\n    } else {\n        std::cout\n            << \"FAILED: different sequence is generated with NVPL RAND and cuRANDDx generator using STRICT ordering.\\n\";\n        return 1;\n    }\n#endif\n}\n\ntemplate<unsigned int Arch>\nstruct simple_pcg_thread_api_functor {\n    int operator()() { return simple_pcg_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_pcg_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n#ifdef CURANDDX_EXAMPLE_NVPL_RAND_AVAILABLE\n#    include <nvpl_rand.h>\n#    define NVPL_RAND_CHECK(x)                                  \\\n        do {                                                    \\\n            if ((x) != NVPL_RAND_STATUS_SUCCESS) {              \\\n                printf(\"Error at %s:%d\\n\", __FILE__, __LINE__); \\\n                _Exit(EXIT_FAILURE);                            \\\n            }                                                   \\\n        } while (0)\n#endif\n\n// This example demonstrates how to use PCG generator and cuRANDDx Thread-level operator to generate a sequence of random 32-bit\n// numbers and compare with the results generated using NVPL RAND, if available, with STRICT ordering\n\ntemplate<class RNG, typename data_type>\n__global__ void generate_kernel(data_type*                      d_out,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    const int tid = blockDim.x * blockIdx.x + threadIdx.x;\n    if (tid >= size)\n        return;\n\n    curanddx::uniform_bits<data_type> dist;\n\n    // compare with NVPL RAND PCG strict ordering\n    RNG rng(seed, 0, offset + tid);\n\n    d_out[tid] = dist.generate(rng);\n}\n\ntemplate<unsigned int Arch>\nint simple_pcg_thread_api() {\n    using RNG = decltype(curanddx::Generator<curanddx::pcg>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    using DataType = typename RNG::bitgenerator_result_type;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    unsigned long long seed   = 1234ULL;\n    unsigned long long offset = 1ULL;\n\n    // Invokes kernel\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size + block_dim - 1) / block_dim;\n    \n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>(d_out, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out.data(), d_out, size * sizeof(DataType), hipMemcpyDeviceToHost));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out));\n\n#ifdef CURANDDX_EXAMPLE_NVPL_RAND_AVAILABLE\n    // nvpl RAND\n    std::vector<DataType> h_ref(size);\n    nvplRandGenerator_t   gen;\n    const auto            generator_type = NVPL_RAND_RNG_PSEUDO_PCG;\n\n    NVPL_RAND_CHECK(nvplRandMTCreateGeneratorDefault(&gen, generator_type));\n    NVPL_RAND_CHECK(nvplRandSetPseudoRandomGeneratorSeed(gen, seed));\n    NVPL_RAND_CHECK(nvplRandSetGeneratorOffset(gen, offset));\n    NVPL_RAND_CHECK(nvplRandMTSetGeneratorOrdering(gen, NVPL_RAND_ORDERING_STRICT));\n\n    // Generate\n    NVPL_RAND_CHECK(nvplRandGenerate(gen, h_ref.data(), size));\n    NVPL_RAND_CHECK(nvplRandDestroyGenerator(gen));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and nvpl RAND API using STRICT ordering.\\n\";\n    } else {\n        int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count < 10) {\n                printf(\"array_curanddx[%u] = %u, array_nvplrand[%u] = %u \\n\", i, h_out[i], i, h_ref[i]);\n                count++;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and NVPL RAND Host API using STRICT \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n\n    // compute hash to be used if nvpl rand is not available\n    unsigned int xor_nvplrand = 0x0;\n    for (auto i = 0U; i < size; i++) {\n        xor_nvplrand ^= h_ref[i];\n    }\n    std::cout << \"NVPL RAND reference xor output is \" << std::hex << xor_nvplrand << std::endl;\n    return 0;\n\n#else\n    // Compare hash\n    unsigned int xor_curand = 0x0;\n    for (auto i = 0U; i < size; i++) {\n        xor_curand ^= h_out[i];\n    }\n    if (xor_curand == 0xaa706742) {\n        std::cout << \"Compared to the hash value: Same sequence is generated with NVPL RAND and cuRANDDx generator \"\n                     \"using STRICT ordering.\\n\";\n        std::cout << \"SUCCESS \\n\";\n        return 0;\n    } else {\n        std::cout\n            << \"FAILED: different sequence is generated with NVPL RAND and cuRANDDx generator using STRICT ordering.\\n\";\n        return 1;\n    }\n#endif\n}\n\ntemplate<unsigned int Arch>\nstruct simple_pcg_thread_api_functor {\n    int operator()() { return simple_pcg_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<simple_pcg_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/02_thread_api/pcg_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n#include <cassert>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use quasi-random generator scrambled sobol64 and cuRANDDx thread-level operator\n// to generate a sequence of random numbers and compare with the results generated using cuRAND host API\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(DataType*                                 d_out,\n                                typename RNG::direction_vector_type*      direction_vectors,\n                                const typename RNG::scrambled_const_type* scrambled_consts,\n                                const typename RNG::offset_type           offset,\n                                const unsigned int                        num_dims,\n                                const size_t                              size,\n                                DataType                                  input1,\n                                DataType                                  input2) {\n    int       tid     = blockDim.x * blockIdx.x + threadIdx.x;\n    const int threads = blockDim.x * gridDim.x;\n\n    curanddx::uniform<float> my_uniform(input1, input2);\n\n    // When generating n results in m dimensions, the output will consist of n//m results from dimension 0, followed by\n    // another n/m results from dimension 1, etc.\n    const int size_per_dim = size / num_dims;\n\n    for (auto idx = tid; idx < size; idx += threads) {\n        // for each dimension, tid0 generates the first number, tid1 the second number etc.\n        const int idex_per_dim = idx % size_per_dim;\n        const int dim          = idx / size_per_dim;\n        RNG       rng(dim, direction_vectors, offset + idex_per_dim, scrambled_consts);\n\n\n        d_out[idx] = my_uniform.generate(rng);\n    }\n}\n\ntemplate<unsigned int Arch>\nint sobol_thread_api() {\n    using RNG =\n        decltype(curanddx::Generator<curanddx::scrambled_sobol64>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    using DataType = float;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    // size has to be evenly divisible by dimensions\n    const unsigned int sobol_dims = 200;\n    assert(size % sobol_dims == 0);\n\n    const typename RNG::offset_type offset = 10ULL;\n    const DataType                  min_v  = -1;\n    const DataType                  max_v  = 1;\n\n    // Step 1: Get pointers to the host direction vectors using cuRAND API\n    using direction_vector_type = typename RNG::direction_vector_type;\n    direction_vector_type* h_direction_vectors;\n    CURAND_CHECK_AND_EXIT(\n        curandGetDirectionVectors64(&h_direction_vectors, CURAND_SCRAMBLED_DIRECTION_VECTORS_64_JOEKUO6));\n\n    // Step 2: Allocate memory and copy one direction vector per dimension to the device\n    direction_vector_type* direction_vectors_gmem_ptr;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&(direction_vectors_gmem_ptr), sobol_dims * sizeof(direction_vector_type)));\n\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(direction_vectors_gmem_ptr,\n                                   h_direction_vectors,\n                                   sobol_dims * sizeof(direction_vector_type),\n                                   cudaMemcpyHostToDevice));\n\n    // Step 3: Get pointers to the host scrambled constants\n    using scrambled_const_type = typename RNG::scrambled_const_type;\n    scrambled_const_type* h_scrambled_consts;\n    scrambled_const_type* scrambled_consts_gmem_ptr;\n    CURAND_CHECK_AND_EXIT(curandGetScrambleConstants64(&h_scrambled_consts));\n\n    // Step 4: Allocate memory and copy one scrambled const per dimension to the device\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&(scrambled_consts_gmem_ptr), sobol_dims * sizeof(scrambled_const_type)));\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(scrambled_consts_gmem_ptr,\n                                   h_scrambled_consts,\n                                   sobol_dims * sizeof(scrambled_const_type),\n                                   cudaMemcpyHostToDevice));\n\n    // Invokes a kernel which uses cuRANDDx functions\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = 16;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>(\n        d_out, direction_vectors_gmem_ptr, scrambled_consts_gmem_ptr, offset, sobol_dims, size, min_v, max_v);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out.data(), d_out, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    // cuRAND host API\n    curandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(curandCreateGenerator(&gen_curand, CURAND_RNG_QUASI_SCRAMBLED_SOBOL64));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOffset(gen_curand, offset));\n    CURAND_CHECK_AND_EXIT(curandSetQuasiRandomGeneratorDimensions(gen_curand, sobol_dims));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOrdering(gen_curand, CURAND_ORDERING_QUASI_DEFAULT));\n\n    CURAND_CHECK_AND_EXIT(curandGenerateUniform(gen_curand, d_ref, size));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    // scale the reference data to range(min_v, max_v)\n    for( DataType &v : h_ref )  v = min_v + v * (max_v - min_v);\n\n    CURAND_CHECK_AND_EXIT(curandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(cudaFree(scrambled_consts_gmem_ptr));\n    CUDA_CHECK_AND_EXIT(cudaFree(direction_vectors_gmem_ptr));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout\n            << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using QUASI_DEFAULT ordering.\\n\";\n        return 0;\n    } else {\n        for (auto i = 0U; i < 10; i++) {\n            std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                      << std::endl;\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using QUASI_DEFAULT \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct sobol_thread_api_functor {\n    int operator()() { return sobol_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<sobol_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n#include <cassert>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use quasi-random generator scrambled sobol64 and cuRANDDx thread-level operator\n// to generate a sequence of random numbers and compare with the results generated using cuRAND host API\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(DataType*                                 d_out,\n                                typename RNG::direction_vector_type*      direction_vectors,\n                                const typename RNG::scrambled_const_type* scrambled_consts,\n                                const typename RNG::offset_type           offset,\n                                const unsigned int                        num_dims,\n                                const size_t                              size,\n                                DataType                                  input1,\n                                DataType                                  input2) {\n    int       tid     = blockDim.x * blockIdx.x + threadIdx.x;\n    const int threads = blockDim.x * gridDim.x;\n\n    curanddx::uniform<float> my_uniform(input1, input2);\n\n    // When generating n results in m dimensions, the output will consist of n//m results from dimension 0, followed by\n    // another n/m results from dimension 1, etc.\n    const int size_per_dim = size / num_dims;\n\n    for (auto idx = tid; idx < size; idx += threads) {\n        // for each dimension, tid0 generates the first number, tid1 the second number etc.\n        const int idex_per_dim = idx % size_per_dim;\n        const int dim          = idx / size_per_dim;\n        RNG       rng(dim, direction_vectors, offset + idex_per_dim, scrambled_consts);\n\n\n        d_out[idx] = my_uniform.generate(rng);\n    }\n}\n\ntemplate<unsigned int Arch>\nint sobol_thread_api() {\n    using RNG =\n        decltype(curanddx::Generator<curanddx::scrambled_sobol64>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    using DataType = float;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 5000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    // size has to be evenly divisible by dimensions\n    const unsigned int sobol_dims = 200;\n    assert(size % sobol_dims == 0);\n\n    const typename RNG::offset_type offset = 10ULL;\n    const DataType                  min_v  = -1;\n    const DataType                  max_v  = 1;\n\n    // Step 1: Get pointers to the host direction vectors using cuRAND API\n    using direction_vector_type = typename RNG::direction_vector_type;\n    direction_vector_type* h_direction_vectors;\n    CURAND_CHECK_AND_EXIT(\n        hiprandGetDirectionVectors64(&h_direction_vectors, HIPRAND_SCRAMBLED_DIRECTION_VECTORS_64_JOEKUO6));\n\n    // Step 2: Allocate memory and copy one direction vector per dimension to the device\n    direction_vector_type* direction_vectors_gmem_ptr;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&(direction_vectors_gmem_ptr), sobol_dims * sizeof(direction_vector_type)));\n\n    CUDA_CHECK_AND_EXIT(hipMemcpy(direction_vectors_gmem_ptr,\n                                   h_direction_vectors,\n                                   sobol_dims * sizeof(direction_vector_type),\n                                   hipMemcpyHostToDevice));\n\n    // Step 3: Get pointers to the host scrambled constants\n    using scrambled_const_type = typename RNG::scrambled_const_type;\n    scrambled_const_type* h_scrambled_consts;\n    scrambled_const_type* scrambled_consts_gmem_ptr;\n    CURAND_CHECK_AND_EXIT(hiprandGetScrambleConstants64(&h_scrambled_consts));\n\n    // Step 4: Allocate memory and copy one scrambled const per dimension to the device\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&(scrambled_consts_gmem_ptr), sobol_dims * sizeof(scrambled_const_type)));\n    CUDA_CHECK_AND_EXIT(hipMemcpy(scrambled_consts_gmem_ptr,\n                                   h_scrambled_consts,\n                                   sobol_dims * sizeof(scrambled_const_type),\n                                   hipMemcpyHostToDevice));\n\n    // Invokes a kernel which uses cuRANDDx functions\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = 16;\n    generate_kernel<RNG, DataType><<<grid_size, block_dim, 0>>>(\n        d_out, direction_vectors_gmem_ptr, scrambled_consts_gmem_ptr, offset, sobol_dims, size, min_v, max_v);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out.data(), d_out, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    // cuRAND host API\n    hiprandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(hiprandCreateGenerator(&gen_curand, HIPRAND_RNG_QUASI_SCRAMBLED_SOBOL64));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOffset(gen_curand, offset));\n    CURAND_CHECK_AND_EXIT(hiprandSetQuasiRandomGeneratorDimensions(gen_curand, sobol_dims));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOrdering(gen_curand, HIPRAND_ORDERING_QUASI_DEFAULT));\n\n    CURAND_CHECK_AND_EXIT(hiprandGenerateUniform(gen_curand, d_ref, size));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    // scale the reference data to range(min_v, max_v)\n    for( DataType &v : h_ref )  v = min_v + v * (max_v - min_v);\n\n    CURAND_CHECK_AND_EXIT(hiprandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(hipFree(scrambled_consts_gmem_ptr));\n    CUDA_CHECK_AND_EXIT(hipFree(direction_vectors_gmem_ptr));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref));\n\n    // Compare Results\n    if (h_out == h_ref) {\n        std::cout\n            << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using QUASI_DEFAULT ordering.\\n\";\n        return 0;\n    } else {\n        for (auto i = 0U; i < 10; i++) {\n            std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                      << std::endl;\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using QUASI_DEFAULT \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct sobol_thread_api_functor {\n    int operator()() { return sobol_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<sobol_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/02_thread_api/sobol_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to launch two kernels, one for initializing the states, one for generating normally distribute \n// random numbers with Box-Muller method, using XORWOW generator. \n// \n// cuRANDDx functions used in the example kernels:\n// (1) state initialization using init() function, or the constructor of the RNG object\n// (2) generate2() function as Box-Muller method is selected for normal distribution\n//\n// The generated RNs are the same as the results using cuRAND host API with CURAND_ORDERING_PSEUDO_LEGACY ordering\n// The ordering requires that the result at offset n is from position  \n// (n mod 4096) * 2^67 + (n/4096) in the original XORWOW sequence\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types \n\nconstexpr unsigned int subsequences = 4096;\n\ntemplate<class RNG>\n__global__ void init_kernel(RNG* states, const unsigned long long seed, const typename RNG::offset_type offset) {\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    RNG rng;\n    rng.init(seed, ((offset + tid) % subsequences), ((offset + tid) / subsequences)); // seed, subsequence, offset\n\n    // Alternatively one can use the constructor directly\n    //RNG rng(seed, ((offset + tid) % subsequences), ((offset + tid) / subsequences)); \n    \n    states[tid] = rng;\n}\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(RNG* states, float2* d_out, const size_t size, const DataType mean, const DataType stddev) {\n    int       tid     = blockDim.x * blockIdx.x + threadIdx.x;\n    const int threads = blockDim.x * gridDim.x;\n\n    curanddx::normal<DataType, curanddx::box_muller> dist(mean, stddev);\n\n    RNG rng = states[tid];\n\n    for (auto idx = tid; idx < size / 2; idx += threads) {\n        d_out[idx] = dist.generate2(rng);\n    }\n\n    // Each thread updates \"states\" in global memory so \"states\" can be used for later kernels\n    states[tid] = rng;\n}\n\n\ntemplate<unsigned int Arch>\nint xorwow_init_and_generate_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::xorwow>() + curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = float;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 50000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long seed   = 1234ULL;\n    const typename RNG::offset_type offset = 2ULL;\n\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = 16;\n\n    const DataType mean = 0; \n    const DataType stddev = 2;\n\n    // Allocate an array of states\n    RNG* states;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&states, block_dim * grid_size * sizeof(RNG)));\n\n    // Invoke the init kernel first to set up the states\n    init_kernel<RNG><<<grid_size, block_dim>>>(states, seed, offset);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n\n    // Invoke the generate kernel to generate RNs and update the states\n    generate_kernel<RNG, DataType><<<grid_size, block_dim>>>(states, (float2*)d_out, size, mean, stddev);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out.data(), d_out, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    // cuRAND host API\n    curandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(curandCreateGenerator(&gen_curand, CURAND_RNG_PSEUDO_XORWOW));\n    CURAND_CHECK_AND_EXIT(curandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOffset(gen_curand, offset));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOrdering(gen_curand, CURAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(curandGenerateNormal(gen_curand, d_ref, size, mean, stddev));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), cudaMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(curandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(cudaFree(states));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref));\n\n    // Compare Results between cuRAND host API and cuRANDDx\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        unsigned int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                count++;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct xorwow_init_and_generate_thread_api_functor {\n    int operator()() { return xorwow_init_and_generate_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<xorwow_init_and_generate_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to launch two kernels, one for initializing the states, one for generating normally distribute \n// random numbers with Box-Muller method, using XORWOW generator. \n// \n// cuRANDDx functions used in the example kernels:\n// (1) state initialization using init() function, or the constructor of the RNG object\n// (2) generate2() function as Box-Muller method is selected for normal distribution\n//\n// The generated RNs are the same as the results using cuRAND host API with CURAND_ORDERING_PSEUDO_LEGACY ordering\n// The ordering requires that the result at offset n is from position  \n// (n mod 4096) * 2^67 + (n/4096) in the original XORWOW sequence\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types \n\nconstexpr unsigned int subsequences = 4096;\n\ntemplate<class RNG>\n__global__ void init_kernel(RNG* states, const unsigned long long seed, const typename RNG::offset_type offset) {\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    RNG rng;\n    rng.init(seed, ((offset + tid) % subsequences), ((offset + tid) / subsequences)); // seed, subsequence, offset\n\n    // Alternatively one can use the constructor directly\n    //RNG rng(seed, ((offset + tid) % subsequences), ((offset + tid) / subsequences)); \n    \n    states[tid] = rng;\n}\n\ntemplate<class RNG, typename DataType>\n__global__ void generate_kernel(RNG* states, float2* d_out, const size_t size, const DataType mean, const DataType stddev) {\n    int       tid     = blockDim.x * blockIdx.x + threadIdx.x;\n    const int threads = blockDim.x * gridDim.x;\n\n    curanddx::normal<DataType, curanddx::box_muller> dist(mean, stddev);\n\n    RNG rng = states[tid];\n\n    for (auto idx = tid; idx < size / 2; idx += threads) {\n        d_out[idx] = dist.generate2(rng);\n    }\n\n    // Each thread updates \"states\" in global memory so \"states\" can be used for later kernels\n    states[tid] = rng;\n}\n\n\ntemplate<unsigned int Arch>\nint xorwow_init_and_generate_thread_api() {\n    using RNG      = decltype(curanddx::Generator<curanddx::xorwow>() + curanddx::SM<Arch>() + curanddx::Thread());\n    using DataType = float;\n\n    // Allocate output memory\n    DataType*    d_out;\n    const size_t size = 50000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out, size * sizeof(DataType)));\n\n    const unsigned long long seed   = 1234ULL;\n    const typename RNG::offset_type offset = 2ULL;\n\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = 16;\n\n    const DataType mean = 0; \n    const DataType stddev = 2;\n\n    // Allocate an array of states\n    RNG* states;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&states, block_dim * grid_size * sizeof(RNG)));\n\n    // Invoke the init kernel first to set up the states\n    init_kernel<RNG><<<grid_size, block_dim>>>(states, seed, offset);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n\n    // Invoke the generate kernel to generate RNs and update the states\n    generate_kernel<RNG, DataType><<<grid_size, block_dim>>>(states, (float2*)d_out, size, mean, stddev);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    std::vector<DataType> h_out(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out.data(), d_out, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    // cuRAND host API\n    hiprandGenerator_t gen_curand;\n    DataType*         d_ref;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref, size * sizeof(DataType)));\n\n    CURAND_CHECK_AND_EXIT(hiprandCreateGenerator(&gen_curand, HIPRAND_RNG_PSEUDO_XORWOW));\n    CURAND_CHECK_AND_EXIT(hiprandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOffset(gen_curand, offset));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOrdering(gen_curand, HIPRAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(hiprandGenerateNormal(gen_curand, d_ref, size, mean, stddev));\n\n    std::vector<DataType> h_ref(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_ref.data(), d_ref, size * sizeof(DataType), hipMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(hiprandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(hipFree(states));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref));\n\n    // Compare Results between cuRAND host API and cuRANDDx\n    if (h_out == h_ref) {\n        std::cout << \"SUCCESS: Same sequence is generated with cuRANDDx and cuRAND Host API using LEGACY ordering.\\n\";\n        return 0;\n    } else {\n        unsigned int count {0};\n        for (auto i = 0U; i < size; i++) {\n            if (h_out[i] != h_ref[i] && count < 10) {\n                std::cout << \"array_curanddx[\" << i << \"] = \" << h_out[i] << \" array_curand[\" << i << \"] = \" << h_ref[i]\n                          << std::endl;\n                count++;\n            }\n        }\n        std::cout << \"FAILED: Different sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct xorwow_init_and_generate_thread_api_functor {\n    int operator()() { return xorwow_init_and_generate_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<xorwow_init_and_generate_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/02_thread_api/xorwow_init_and_generate_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include <iostream>\n#include <vector>\n\n// cuRAND uses a single 32-bit random value to generate one double-precision uniform/normal/lognormal distributed value\n// A more precise way is using two 32-bit values to generate one double-precision value\n#ifndef CURANDDX_MRG_DOUBLE_DISTRIBUTION_CURAND_COMPATIBLE\n#    define CURANDDX_MRG_DOUBLE_DISTRIBUTION_CURAND_COMPATIBLE\n#endif\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate:\n// (1) a sequence of uniformly-distributed FP64 numbers,\n// (2) a sequence of normally-distributed FP32 numbers.\n// This can be done either in a single kernel or multiple kernels depending on the application workflow.\n// \n// The generated RNs are the same as the results using cuRAND host API with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering requires that the result at offset n is from position \n// (n mod 4096) * 2^76 + (n/4096) in the original MRG32k3a sequence\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types MRG32k3a pseudorandom generator\n\nconstexpr unsigned int subsequences = 4096;\n\ntemplate<class RNG>\n__global__ void generate_kernel(double*                         d_out,\n                                float2*                         d_out_normal,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    curanddx::uniform<double>                     my_uniform; // default min = 0, max = 1\n    curanddx::normal<float, curanddx::box_muller> my_normal;  // default mean = 0, stddev = 1\n\n    RNG rng(seed, (offset + tid) % subsequences, (offset + tid) / subsequences); // seed, subsequence, offset\n    if (tid < size) {\n        d_out[tid] = my_uniform.generate(rng);\n    }\n\n    // Offset is advanced for rng after each generate() call, only need to call skip_subsequence()\n    rng.skip_subsequence(size % subsequences);\n    if (tid < size / 2) {\n        d_out_normal[tid] = my_normal.generate2(rng);\n    }\n}\n\n\ntemplate<unsigned int Arch>\nint mrg_two_executions_thread_api() {\n    using RNG = decltype(curanddx::Generator<curanddx::mrg32k3a>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    // Allocate output memory\n    double*            d_out_uniform_double;\n    float*             d_out_normal;\n    const unsigned int size = 5000;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out_uniform_double, size * sizeof(double)));\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_out_normal, size * sizeof(float)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 0ULL;\n\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size + block_dim - 1) / block_dim;\n\n    // Launch a kernel to generate two distributions\n    generate_kernel<RNG><<<grid_size, block_dim, 0>>>(d_out_uniform_double, (float2*)d_out_normal, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(cudaPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(cudaDeviceSynchronize());\n\n    // Copy to host\n    std::vector<double> h_out_uniform_double(size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(h_out_uniform_double.data(), d_out_uniform_double, size * sizeof(double), cudaMemcpyDeviceToHost));\n    std::vector<float> h_out_normal(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_out_normal.data(), d_out_normal, size * sizeof(float), cudaMemcpyDeviceToHost));\n\n    // cuRAND host API\n    curandGenerator_t gen_curand;\n    double*           d_ref_uniform_double;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref_uniform_double, size * sizeof(double)));\n    float* d_ref_normal;\n    CUDA_CHECK_AND_EXIT(cudaMalloc((void**)&d_ref_normal, size * sizeof(float)));\n\n    CURAND_CHECK_AND_EXIT(curandCreateGenerator(&gen_curand, CURAND_RNG_PSEUDO_MRG32K3A));\n    CURAND_CHECK_AND_EXIT(curandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOffset(gen_curand, 0));\n    CURAND_CHECK_AND_EXIT(curandSetGeneratorOrdering(gen_curand, CURAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(curandGenerateUniformDouble(gen_curand, d_ref_uniform_double, size));\n    CURAND_CHECK_AND_EXIT(curandGenerateNormal(gen_curand, d_ref_normal, size, 0, 1));\n\n    std::vector<double> h_ref_uniform_double(size);\n    CUDA_CHECK_AND_EXIT(\n        cudaMemcpy(h_ref_uniform_double.data(), d_ref_uniform_double, size * sizeof(double), cudaMemcpyDeviceToHost));\n    std::vector<float> h_ref_normal(size);\n    CUDA_CHECK_AND_EXIT(cudaMemcpy(h_ref_normal.data(), d_ref_normal, size * sizeof(float), cudaMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(curandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out_uniform_double));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_out_normal));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref_uniform_double));\n    CUDA_CHECK_AND_EXIT(cudaFree(d_ref_normal));\n\n    // Compare Results\n    if (h_out_uniform_double == h_ref_uniform_double && h_out_normal == h_ref_normal) {\n        std::cout\n            << \"SUCCESS: \\nSame sequence is generated with cuRANDDx and cuRAND Host API for two distributions.\\n\";\n        return 0;\n    } else {\n        if (h_out_uniform_double != h_ref_uniform_double) {\n            int count {0};\n            for (auto i = 0U; i < size; i++) {\n                if (h_out_uniform_double[i] != h_ref_uniform_double[i] && count++ < 10) {\n                    std::cout << \"uniform: array_curanddx[\" << i << \"] = \" << h_out_uniform_double[i]\n                              << \", array_curand = \" << h_ref_uniform_double[i] << \" \\n\";\n                }\n            }\n        }\n        if (h_out_normal != h_ref_normal) {\n            int count = 0;\n            for (auto i = 0U; i < size; i++) {\n                if (h_out_normal[i] != h_ref_normal[i] && count++ < 10) {\n                    std::cout << \"normal: array_curanddx[\" << i << \"] = \" << h_out_normal[i]\n                              << \", array_curand = \" << h_ref_normal[i] << \" \\n\";\n                }\n            }\n        }\n        std::cout << \"FAILED: \\nDifferent sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct mrg_two_executions_thread_api_functor {\n    int operator()() { return mrg_two_executions_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mrg_two_executions_thread_api_functor>();\n}\n", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */ \n\n#include \"hip/hip_runtime.h\"\n#include <iostream>\n#include <vector>\n\n// cuRAND uses a single 32-bit random value to generate one double-precision uniform/normal/lognormal distributed value\n// A more precise way is using two 32-bit values to generate one double-precision value\n#ifndef CURANDDX_MRG_DOUBLE_DISTRIBUTION_CURAND_COMPATIBLE\n#    define CURANDDX_MRG_DOUBLE_DISTRIBUTION_CURAND_COMPATIBLE\n#endif\n\n#include <curanddx.hpp>\n#include \"../common.hpp\"\n\n// This example demonstrates how to use cuRANDDx thread-level operator to generate:\n// (1) a sequence of uniformly-distributed FP64 numbers,\n// (2) a sequence of normally-distributed FP32 numbers.\n// This can be done either in a single kernel or multiple kernels depending on the application workflow.\n// \n// The generated RNs are the same as the results using cuRAND host API with CURAND_ORDERING_PSEUDO_LEGACY ordering. \n// The ordering requires that the result at offset n is from position \n// (n mod 4096) * 2^76 + (n/4096) in the original MRG32k3a sequence\n// https://docs.nvidia.com/cuda/curand/host-api-overview.html#generator-types MRG32k3a pseudorandom generator\n\nconstexpr unsigned int subsequences = 4096;\n\ntemplate<class RNG>\n__global__ void generate_kernel(double*                         d_out,\n                                float2*                         d_out_normal,\n                                const unsigned long long        seed,\n                                const typename RNG::offset_type offset,\n                                const size_t                    size) {\n    int tid = blockDim.x * blockIdx.x + threadIdx.x;\n\n    curanddx::uniform<double>                     my_uniform; // default min = 0, max = 1\n    curanddx::normal<float, curanddx::box_muller> my_normal;  // default mean = 0, stddev = 1\n\n    RNG rng(seed, (offset + tid) % subsequences, (offset + tid) / subsequences); // seed, subsequence, offset\n    if (tid < size) {\n        d_out[tid] = my_uniform.generate(rng);\n    }\n\n    // Offset is advanced for rng after each generate() call, only need to call skip_subsequence()\n    rng.skip_subsequence(size % subsequences);\n    if (tid < size / 2) {\n        d_out_normal[tid] = my_normal.generate2(rng);\n    }\n}\n\n\ntemplate<unsigned int Arch>\nint mrg_two_executions_thread_api() {\n    using RNG = decltype(curanddx::Generator<curanddx::mrg32k3a>() + curanddx::SM<Arch>() + curanddx::Thread());\n\n    // Allocate output memory\n    double*            d_out_uniform_double;\n    float*             d_out_normal;\n    const unsigned int size = 5000;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out_uniform_double, size * sizeof(double)));\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_out_normal, size * sizeof(float)));\n\n    const unsigned long long        seed   = 1234ULL;\n    const typename RNG::offset_type offset = 0ULL;\n\n    const unsigned int block_dim = 256;\n    const unsigned int grid_size = (size + block_dim - 1) / block_dim;\n\n    // Launch a kernel to generate two distributions\n    generate_kernel<RNG><<<grid_size, block_dim, 0>>>(d_out_uniform_double, (float2*)d_out_normal, seed, offset, size);\n    CUDA_CHECK_AND_EXIT(hipPeekAtLastError());\n    CUDA_CHECK_AND_EXIT(hipDeviceSynchronize());\n\n    // Copy to host\n    std::vector<double> h_out_uniform_double(size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(h_out_uniform_double.data(), d_out_uniform_double, size * sizeof(double), hipMemcpyDeviceToHost));\n    std::vector<float> h_out_normal(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_out_normal.data(), d_out_normal, size * sizeof(float), hipMemcpyDeviceToHost));\n\n    // cuRAND host API\n    hiprandGenerator_t gen_curand;\n    double*           d_ref_uniform_double;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref_uniform_double, size * sizeof(double)));\n    float* d_ref_normal;\n    CUDA_CHECK_AND_EXIT(hipMalloc((void**)&d_ref_normal, size * sizeof(float)));\n\n    CURAND_CHECK_AND_EXIT(hiprandCreateGenerator(&gen_curand, HIPRAND_RNG_PSEUDO_MRG32K3A));\n    CURAND_CHECK_AND_EXIT(hiprandSetPseudoRandomGeneratorSeed(gen_curand, seed));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOffset(gen_curand, 0));\n    CURAND_CHECK_AND_EXIT(hiprandSetGeneratorOrdering(gen_curand, HIPRAND_ORDERING_PSEUDO_LEGACY));\n\n    CURAND_CHECK_AND_EXIT(hiprandGenerateUniformDouble(gen_curand, d_ref_uniform_double, size));\n    CURAND_CHECK_AND_EXIT(hiprandGenerateNormal(gen_curand, d_ref_normal, size, 0, 1));\n\n    std::vector<double> h_ref_uniform_double(size);\n    CUDA_CHECK_AND_EXIT(\n        hipMemcpy(h_ref_uniform_double.data(), d_ref_uniform_double, size * sizeof(double), hipMemcpyDeviceToHost));\n    std::vector<float> h_ref_normal(size);\n    CUDA_CHECK_AND_EXIT(hipMemcpy(h_ref_normal.data(), d_ref_normal, size * sizeof(float), hipMemcpyDeviceToHost));\n\n    CURAND_CHECK_AND_EXIT(hiprandDestroyGenerator(gen_curand));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out_uniform_double));\n    CUDA_CHECK_AND_EXIT(hipFree(d_out_normal));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref_uniform_double));\n    CUDA_CHECK_AND_EXIT(hipFree(d_ref_normal));\n\n    // Compare Results\n    if (h_out_uniform_double == h_ref_uniform_double && h_out_normal == h_ref_normal) {\n        std::cout\n            << \"SUCCESS: \\nSame sequence is generated with cuRANDDx and cuRAND Host API for two distributions.\\n\";\n        return 0;\n    } else {\n        if (h_out_uniform_double != h_ref_uniform_double) {\n            int count {0};\n            for (auto i = 0U; i < size; i++) {\n                if (h_out_uniform_double[i] != h_ref_uniform_double[i] && count++ < 10) {\n                    std::cout << \"uniform: array_curanddx[\" << i << \"] = \" << h_out_uniform_double[i]\n                              << \", array_curand = \" << h_ref_uniform_double[i] << \" \\n\";\n                }\n            }\n        }\n        if (h_out_normal != h_ref_normal) {\n            int count = 0;\n            for (auto i = 0U; i < size; i++) {\n                if (h_out_normal[i] != h_ref_normal[i] && count++ < 10) {\n                    std::cout << \"normal: array_curanddx[\" << i << \"] = \" << h_out_normal[i]\n                              << \", array_curand = \" << h_ref_normal[i] << \" \\n\";\n                }\n            }\n        }\n        std::cout << \"FAILED: \\nDifferent sequence is generated with cuRANDDx and cuRAND Host API using LEGACY \"\n                     \"ordering.\\n\";\n        return 1;\n    }\n}\n\ntemplate<unsigned int Arch>\nstruct mrg_two_executions_thread_api_functor {\n    int operator()() { return mrg_two_executions_thread_api<Arch>(); }\n};\n\nint main(int, char**) {\n    return example::sm_runner<mrg_two_executions_thread_api_functor>();\n}\n", "source": "/kaggle/working/CUDALibrarySamples/MathDx/cuRANDDx/02_thread_api/mrg_two_distributions_thread_api.cu"}
{"input": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Reference for the example of LTO callbacks,\n * run the same plans but perform the windowing with\n * a separate kernel.\n *\n*/\n\n#include <cuda_runtime_api.h>\n#include <cufft.h>\n#include <stdio.h>\n#include <vector>\n#include \"r2c_c2r_reference.h\"\n\n__global__ void windowing(unsigned nbatches, unsigned complex_signal_size, unsigned window_size, float2* buffer)\n{\n\tconst size_t idx = blockIdx.x*blockDim.x + threadIdx.x;\n\n\tif(idx >= nbatches * complex_signal_size) return;\n\n\tconst unsigned sample = idx % complex_signal_size;\n\n\tbuffer[idx].x = (sample < window_size) ? buffer[idx].x : 0.f;\n\tbuffer[idx].y = (sample < window_size) ? buffer[idx].y : 0.f;\n}\n\nint reference_r2c_window_c2r(unsigned batches, unsigned signal_size, unsigned window_size, float* input_signals, float* output_signals) {\n\tconst unsigned complex_signal_size = signal_size / 2 + 1;\n\tconst size_t complex_size_bytes    = batches * complex_signal_size * 2 * sizeof(float);\n\n\t// Allocate and copy input from host to GPU\n\tfloat *device_signals;\n\tCHECK_ERROR(cudaMalloc((void **)&device_signals, complex_size_bytes));\n\tCHECK_ERROR(cudaMemcpy(device_signals, input_signals, complex_size_bytes, cudaMemcpyHostToDevice));\n\n\t// Create a CUFFT plan for the forward transform, and a cuFFT plan for the inverse transform\n\tcufftHandle forward_plan, inverse_plan;\n\tsize_t work_size;\n\n\tCHECK_ERROR(cufftCreate(&forward_plan));\n\tCHECK_ERROR(cufftCreate(&inverse_plan));\n\n\tCHECK_ERROR(cufftMakePlan1d(forward_plan, signal_size, CUFFT_R2C, batches, &work_size));\n\tCHECK_ERROR(cufftMakePlan1d(inverse_plan, signal_size, CUFFT_C2R, batches, &work_size));\n\n\t// Transform signal forward\n\tprintf(\"Transforming reference cufftExecR2C\\n\");\n\tCHECK_ERROR(cufftExecR2C(forward_plan, (cufftReal *)device_signals, (cufftComplex *)device_signals));\n\n\t// Apply window via separate kernel\n\twindowing<<<(batches * complex_signal_size + 255) / 256, 256>>>(batches, complex_signal_size, window_size, (float2*) device_signals);\n\tcudaDeviceSynchronize();\n\tCHECK_ERROR(cudaGetLastError());\n\n\t// Inverse-transform the signal\n\tprintf(\"Transforming reference cufftExecC2R\\n\");\n\tCHECK_ERROR(cufftExecC2R(inverse_plan, (cufftComplex *)device_signals, (cufftReal *)device_signals));\n\n\t// Copy device memory to host\n\tCHECK_ERROR(cudaMemcpy(output_signals, device_signals, complex_size_bytes, cudaMemcpyDeviceToHost));\n\n\t// Destroy CUFFT context\n\tCHECK_ERROR(cufftDestroy(forward_plan));\n\tCHECK_ERROR(cufftDestroy(inverse_plan));\n\n\t// Cleanup memory\n\tCHECK_ERROR(cudaFree(device_signals));\n\n\treturn PASS_VALUE;\n}", "output": "/*\n * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Reference for the example of LTO callbacks,\n * run the same plans but perform the windowing with\n * a separate kernel.\n *\n*/\n\n#include \"hip/hip_runtime.h\"\n#include <hip/hip_runtime_api.h>\n#include <hipfft/hipfft.h>\n#include <stdio.h>\n#include <vector>\n#include \"r2c_c2r_reference.h\"\n\n__global__ void windowing(unsigned nbatches, unsigned complex_signal_size, unsigned window_size, float2* buffer)\n{\n\tconst size_t idx = blockIdx.x*blockDim.x + threadIdx.x;\n\n\tif(idx >= nbatches * complex_signal_size) return;\n\n\tconst unsigned sample = idx % complex_signal_size;\n\n\tbuffer[idx].x = (sample < window_size) ? buffer[idx].x : 0.f;\n\tbuffer[idx].y = (sample < window_size) ? buffer[idx].y : 0.f;\n}\n\nint reference_r2c_window_c2r(unsigned batches, unsigned signal_size, unsigned window_size, float* input_signals, float* output_signals) {\n\tconst unsigned complex_signal_size = signal_size / 2 + 1;\n\tconst size_t complex_size_bytes    = batches * complex_signal_size * 2 * sizeof(float);\n\n\t// Allocate and copy input from host to GPU\n\tfloat *device_signals;\n\tCHECK_ERROR(hipMalloc((void **)&device_signals, complex_size_bytes));\n\tCHECK_ERROR(hipMemcpy(device_signals, input_signals, complex_size_bytes, hipMemcpyHostToDevice));\n\n\t// Create a CUFFT plan for the forward transform, and a cuFFT plan for the inverse transform\n\thipfftHandle forward_plan, inverse_plan;\n\tsize_t work_size;\n\n\tCHECK_ERROR(hipfftCreate(&forward_plan));\n\tCHECK_ERROR(hipfftCreate(&inverse_plan));\n\n\tCHECK_ERROR(hipfftMakePlan1d(forward_plan, signal_size, HIPFFT_R2C, batches, &work_size));\n\tCHECK_ERROR(hipfftMakePlan1d(inverse_plan, signal_size, HIPFFT_C2R, batches, &work_size));\n\n\t// Transform signal forward\n\tprintf(\"Transforming reference hipfftExecR2C\\n\");\n\tCHECK_ERROR(hipfftExecR2C(forward_plan, (hipfftReal *)device_signals, (hipfftComplex *)device_signals));\n\n\t// Apply window via separate kernel\n\twindowing<<<(batches * complex_signal_size + 255) / 256, 256>>>(batches, complex_signal_size, window_size, (float2*) device_signals);\n\thipDeviceSynchronize();\n\tCHECK_ERROR(hipGetLastError());\n\n\t// Inverse-transform the signal\n\tprintf(\"Transforming reference hipfftExecC2R\\n\");\n\tCHECK_ERROR(hipfftExecC2R(inverse_plan, (hipfftComplex *)device_signals, (hipfftReal *)device_signals));\n\n\t// Copy device memory to host\n\tCHECK_ERROR(hipMemcpy(output_signals, device_signals, complex_size_bytes, hipMemcpyDeviceToHost));\n\n\t// Destroy CUFFT context\n\tCHECK_ERROR(hipfftDestroy(forward_plan));\n\tCHECK_ERROR(hipfftDestroy(inverse_plan));\n\n\t// Cleanup memory\n\tCHECK_ERROR(hipFree(device_signals));\n\n\treturn PASS_VALUE;\n}", "source": "/kaggle/working/CUDALibrarySamples/cuFFT/lto_callback_window_1d/src/r2c_c2r_reference.cu"}
{"input": "#include<iostream>\n\n__global__ void myKernel(){\n\n}\n\nint main()\n{\n    myKernel<<<1,1>>>();\n    std::cout<<\"Hello World!\\n\";\n    return 0;\n}\n", "output": "#include \"hip/hip_runtime.h\"\n#include<iostream>\n\n__global__ void myKernel(){\n\n}\n\nint main()\n{\n    myKernel<<<1,1>>>();\n    std::cout<<\"Hello World!\\n\";\n    return 0;\n}\n", "source": "/kaggle/working/cuda-examples/helloworld.cu"}
{"input": "/*\nProgram to add 2 matrics of size M * N in CUDA C++\nUsing 2-D grid of M*N size (i.e. grid contains M*N blocks arranged in 2D fashion)\nEach block contains 1 thread\n*/\n#include<iostream>\n#include<cuda_runtime.h>\n#define M 7\n#define N 3\n\n__global__ void matAdd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x * gridDim.y + blockIdx.y;\n    c[idx] = a[idx] + b[idx];\n}\n\n__host__ void print_matrix(int* matrix)\n{\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            std::cout<<matrix[i*N+j]<<' ';\n        }\n        std::cout<<\"\\n\";\n    }\n    std::cout<<\"\\n\";\n}\n\nint main()\n{\n    int size = M * N * sizeof(int);\n    int* a = new int[size];\n    int* b = new int[size];\n    int* c = new int[size];\n\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            a[i*N + j] = i; //Fill your own values here\n            b[i*N + j] = j; //Fill your own values here\n        }\n    }\n    std::cout<<\"MATRIX A =\\n\";\n    print_matrix(a);\n    std::cout<<\"MATRIX B =\\n\";\n    print_matrix(b);\n\n    /* Setting up variables on device. i.e. GPU */\n    int *d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);\n\n    /* \n    Kernel Launch\n    Grid contains M*N blocks\n    Each block has 1 thread\n    Hence index of matrix element is\n    blockIdx.x* gridSize.y + blockIdx.y\n    */\n    dim3 gridSize(M, N);\n    matAdd<<<gridSize, 1>>>(d_a, d_b, d_c);\n    cudaDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);\n    \n    /* Print result */\n    std::cout<<\"A + B =\\n\";\n    print_matrix(c);\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}\n", "output": "/*\nProgram to add 2 matrics of size M * N in CUDA C++\nUsing 2-D grid of M*N size (i.e. grid contains M*N blocks arranged in 2D fashion)\nEach block contains 1 thread\n*/\n#include \"hip/hip_runtime.h\"\n#include<iostream>\n#include<hip/hip_runtime.h>\n#define M 7\n#define N 3\n\n__global__ void matAdd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x * gridDim.y + blockIdx.y;\n    c[idx] = a[idx] + b[idx];\n}\n\n__host__ void print_matrix(int* matrix)\n{\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            std::cout<<matrix[i*N+j]<<' ';\n        }\n        std::cout<<\"\\n\";\n    }\n    std::cout<<\"\\n\";\n}\n\nint main()\n{\n    int size = M * N * sizeof(int);\n    int* a = new int[size];\n    int* b = new int[size];\n    int* c = new int[size];\n\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            a[i*N + j] = i; //Fill your own values here\n            b[i*N + j] = j; //Fill your own values here\n        }\n    }\n    std::cout<<\"MATRIX A =\\n\";\n    print_matrix(a);\n    std::cout<<\"MATRIX B =\\n\";\n    print_matrix(b);\n\n    /* Setting up variables on device. i.e. GPU */\n    int *d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    hipMemcpy(d_a, a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, size, hipMemcpyHostToDevice);\n\n    /* \n    Kernel Launch\n    Grid contains M*N blocks\n    Each block has 1 thread\n    Hence index of matrix element is\n    blockIdx.x* gridSize.y + blockIdx.y\n    */\n    dim3 gridSize(M, N);\n    matAdd<<<gridSize, 1>>>(d_a, d_b, d_c);\n    hipDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    hipMemcpy(c, d_c, size, hipMemcpyDeviceToHost);\n    \n    /* Print result */\n    std::cout<<\"A + B =\\n\";\n    print_matrix(c);\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-examples/matadd/matAdd.cu"}
{"input": "/*\nProgram to add 2 matrics of size M * N in CUDA C++\nUsing grid of one block\nBlock contains M*N threads arranged in M rows and N columns\n*/\n#include<iostream>\n#include \"cuda.h\"\n#include \"cuda_runtime.h\"\n#define M 2\n#define N 9\n\n__global__ void matAdd(int* a, int* b, int* c)\n{\n    int idx = threadIdx.x * blockDim.y + threadIdx.y;\n    c[idx] = a[idx] + b[idx];\n}\n\n__host__ void print_matrix(int* matrix)\n{\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            std::cout<<matrix[i*N+j]<<' ';\n        }\n        std::cout<<\"\\n\";\n    }\n    std::cout<<\"\\n\";\n}\n\nint main()\n{\n    int size = M * N * sizeof(int);\n    int* a = new int[size];\n    int* b = new int[size];\n    int* c = new int[size];\n\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            a[i*N + j] = i; //Fill your own values here\n            b[i*N + j] = j; //Fill your own values here\n        }\n    }\n    std::cout<<\"MATRIX A =\\n\";\n    print_matrix(a);\n    std::cout<<\"MATRIX B =\\n\";\n    print_matrix(b);\n\n    /* Setting up variables on device. i.e. GPU */\n    int *d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);\n\n    /* \n    Kernel Launch\n    Grid contains 1 block\n    Each block has M*N threads arranged in M*N rectangle\n    Hence index of vector is \n    idx = threadIdx.x * blockDim.y + threadIdx.y;\n    */\n    dim3 blockSize(M, N);\n    matAdd<<<1, blockSize>>>(d_a, d_b, d_c);\n    cudaDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);\n    \n    /* Print result */\n    std::cout<<\"A + B =\\n\";\n    print_matrix(c);\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}\n", "output": "/*\nProgram to add 2 matrics of size M * N in CUDA C++\nUsing grid of one block\nBlock contains M*N threads arranged in M rows and N columns\n*/\n#include<iostream>\n#include \"hip/hip_runtime.h\"\n#define M 2\n#define N 9\n\n__global__ void matAdd(int* a, int* b, int* c)\n{\n    int idx = threadIdx.x * blockDim.y + threadIdx.y;\n    c[idx] = a[idx] + b[idx];\n}\n\n__host__ void print_matrix(int* matrix)\n{\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            std::cout<<matrix[i*N+j]<<' ';\n        }\n        std::cout<<\"\\n\";\n    }\n    std::cout<<\"\\n\";\n}\n\nint main()\n{\n    int size = M * N * sizeof(int);\n    int* a = new int[size];\n    int* b = new int[size];\n    int* c = new int[size];\n\n    for(int i=0; i<M; ++i)\n    {\n        for(int j=0; j<N; ++j)\n        {\n            a[i*N + j] = i; //Fill your own values here\n            b[i*N + j] = j; //Fill your own values here\n        }\n    }\n    std::cout<<\"MATRIX A =\\n\";\n    print_matrix(a);\n    std::cout<<\"MATRIX B =\\n\";\n    print_matrix(b);\n\n    /* Setting up variables on device. i.e. GPU */\n    int *d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    hipMemcpy(d_a, a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, size, hipMemcpyHostToDevice);\n\n    /* \n    Kernel Launch\n    Grid contains 1 block\n    Each block has M*N threads arranged in M*N rectangle\n    Hence index of vector is \n    idx = threadIdx.x * blockDim.y + threadIdx.y;\n    */\n    dim3 blockSize(M, N);\n    matAdd<<<1, blockSize>>>(d_a, d_b, d_c);\n    hipDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    hipMemcpy(c, d_c, size, hipMemcpyDeviceToHost);\n    \n    /* Print result */\n    std::cout<<\"A + B =\\n\";\n    print_matrix(c);\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}\n", "source": "/kaggle/working/cuda-examples/matadd/matAdd2.cu"}
{"input": "/*\nMatrix multiplication in CUDA C++\nusing shared memory (shared between all threads in a single block)\n\nMatrix sizes are M*N and P*Q\n*/\n#include\"matmul_utils.hpp\"\n\n#define M 3\n#define N 3\n#define P 3\n#define Q 3\n\n__global__ void matmulKernel(int* a, int* b, int* c)\n{\n    int x = blockIdx.x;\n    int y = blockIdx.y;\n    __shared__ int sm[N];\n\n    int i;\n    int k = threadIdx.x;\n    c[Q*x + y] = 0;\n\n    sm[k] = a[N*x + k] * b[Q*k + y];\n\n    __syncthreads();  //Wait for all threads in the block to finish, so that the sm array is populated fully\n\n    for(i=0; i<N; ++i)\n        c[Q*x + y] += sm[i];\n}\n\nint main()\n{\n    srand(time(0));\n    int* a = new int[M * N * sizeof(int)];\n    int* b = new int[P * Q * sizeof(int)];\n    int* c = new int[M * Q * sizeof(int)];\n    init_matrix(a, M, N);\n    init_matrix(b, P, Q);\n\n    std::cout<<\"A =\\n\";\n    display_matrix(a, M, N);\n    std::cout<<\"B =\\n\";\n    display_matrix(b, P, Q);\n\n    assert(N==P); //Necessary condition for matrix multiplication\n\n    int *d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, M * N * sizeof(int));\n    cudaMalloc((void**)&d_b, P * Q * sizeof(int));\n    cudaMalloc((void**)&d_c, M * Q * sizeof(int));\n\n    cudaMemcpy(d_a, a, M * N * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, P * Q * sizeof(int), cudaMemcpyHostToDevice);\n    \n    /*\n    Each block of the grid takes care of multiplying one row of A with one column of B\n\n    Each thread in the block takes care of one element multiplication between row of A and column of B\n    */\n    dim3 gridSize(M, Q);\n\n    matmulKernel<<<gridSize, N>>>(d_a, d_b, d_c);\n\n    cudaMemcpy(c, d_c, M * Q * sizeof(int), cudaMemcpyDeviceToHost);\n    std::cout<<\"A*B =\\n\";\n    display_matrix(c, M, Q);\n\n    delete a;\n    delete b;\n    delete c;\n\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    return 0;\n}", "output": "/*\nMatrix multiplication in CUDA C++\nusing shared memory (shared between all threads in a single block)\n\nMatrix sizes are M*N and P*Q\n*/\n#include \"hip/hip_runtime.h\"\n#include\"matmul_utils.hpp\"\n\n#define M 3\n#define N 3\n#define P 3\n#define Q 3\n\n__global__ void matmulKernel(int* a, int* b, int* c)\n{\n    int x = blockIdx.x;\n    int y = blockIdx.y;\n    __shared__ int sm[N];\n\n    int i;\n    int k = threadIdx.x;\n    c[Q*x + y] = 0;\n\n    sm[k] = a[N*x + k] * b[Q*k + y];\n\n    __syncthreads();  //Wait for all threads in the block to finish, so that the sm array is populated fully\n\n    for(i=0; i<N; ++i)\n        c[Q*x + y] += sm[i];\n}\n\nint main()\n{\n    srand(time(0));\n    int* a = new int[M * N * sizeof(int)];\n    int* b = new int[P * Q * sizeof(int)];\n    int* c = new int[M * Q * sizeof(int)];\n    init_matrix(a, M, N);\n    init_matrix(b, P, Q);\n\n    std::cout<<\"A =\\n\";\n    display_matrix(a, M, N);\n    std::cout<<\"B =\\n\";\n    display_matrix(b, P, Q);\n\n    assert(N==P); //Necessary condition for matrix multiplication\n\n    int *d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, M * N * sizeof(int));\n    hipMalloc((void**)&d_b, P * Q * sizeof(int));\n    hipMalloc((void**)&d_c, M * Q * sizeof(int));\n\n    hipMemcpy(d_a, a, M * N * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, P * Q * sizeof(int), hipMemcpyHostToDevice);\n    \n    /*\n    Each block of the grid takes care of multiplying one row of A with one column of B\n\n    Each thread in the block takes care of one element multiplication between row of A and column of B\n    */\n    dim3 gridSize(M, Q);\n\n    matmulKernel<<<gridSize, N>>>(d_a, d_b, d_c);\n\n    hipMemcpy(c, d_c, M * Q * sizeof(int), hipMemcpyDeviceToHost);\n    std::cout<<\"A*B =\\n\";\n    display_matrix(c, M, Q);\n\n    delete a;\n    delete b;\n    delete c;\n\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    return 0;\n}", "source": "/kaggle/working/cuda-examples/matmul/matmul_shmem.cu"}
{"input": "/*\nMatrix multiplication in CUDA C++\nMatrix sizes are M*N and P*Q\nUsing grid of size M*Q blocks with 1 thread in each block\n*/\n#include\"matmul_utils.hpp\"\n\n#define M 3\n#define N 3\n#define P 3\n#define Q 3\n\n__global__ void matmulKernel(int* a, int*b, int* c)\n{\n    int i = blockIdx.x;\n    int j = blockIdx.y;\n    c[i*Q + j] = 0;\n    /*\n    Perform inner product between row i of A and column j of B\n    */\n    for(int k=0; k<N; ++k)\n    {\n        c[i*Q + j] += (a[i*N + k] * b[k*Q + j]);\n    }\n}\n\nint main()\n{\n    int* a = new int[M * N * sizeof(int)];\n    int* b = new int[P * Q * sizeof(int)];\n    int* c = new int[M * Q * sizeof(int)];\n    init_matrix(a, M, N);\n    init_matrix(b, P, Q);\n\n    std::cout<<\"A =\\n\";\n    display_matrix(a, M, N);\n    std::cout<<\"B =\\n\";\n    display_matrix(b, P, Q);\n\n    assert(N==P); //Necessary condition for matrix multiplication\n\n    int *d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, M * N * sizeof(int));\n    cudaMalloc((void**)&d_b, P * Q * sizeof(int));\n    cudaMalloc((void**)&d_c, M * Q * sizeof(int));\n\n    cudaMemcpy(d_a, a, M * N * sizeof(int), cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, P * Q * sizeof(int), cudaMemcpyHostToDevice);\n    \n    /*\n    Each block of the grid takes care of multiplying one row of A with one column of B\n\n    The single thread in the block performs the inner product of the row of A and column of B\n    */\n    dim3 grid(M, Q);\n\n    matmulKernel<<<grid, 1>>>(d_a, d_b, d_c);\n\n    cudaMemcpy(c, d_c, M * Q * sizeof(int), cudaMemcpyDeviceToHost);\n    std::cout<<\"A*B =\\n\";\n    display_matrix(c, M, Q);\n\n    delete a;\n    delete b;\n    delete c;\n\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    return 0;\n}", "output": "/*\nMatrix multiplication in CUDA C++\nMatrix sizes are M*N and P*Q\nUsing grid of size M*Q blocks with 1 thread in each block\n*/\n#include \"hip/hip_runtime.h\"\n#include\"matmul_utils.hpp\"\n\n#define M 3\n#define N 3\n#define P 3\n#define Q 3\n\n__global__ void matmulKernel(int* a, int*b, int* c)\n{\n    int i = blockIdx.x;\n    int j = blockIdx.y;\n    c[i*Q + j] = 0;\n    /*\n    Perform inner product between row i of A and column j of B\n    */\n    for(int k=0; k<N; ++k)\n    {\n        c[i*Q + j] += (a[i*N + k] * b[k*Q + j]);\n    }\n}\n\nint main()\n{\n    int* a = new int[M * N * sizeof(int)];\n    int* b = new int[P * Q * sizeof(int)];\n    int* c = new int[M * Q * sizeof(int)];\n    init_matrix(a, M, N);\n    init_matrix(b, P, Q);\n\n    std::cout<<\"A =\\n\";\n    display_matrix(a, M, N);\n    std::cout<<\"B =\\n\";\n    display_matrix(b, P, Q);\n\n    assert(N==P); //Necessary condition for matrix multiplication\n\n    int *d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, M * N * sizeof(int));\n    hipMalloc((void**)&d_b, P * Q * sizeof(int));\n    hipMalloc((void**)&d_c, M * Q * sizeof(int));\n\n    hipMemcpy(d_a, a, M * N * sizeof(int), hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, P * Q * sizeof(int), hipMemcpyHostToDevice);\n    \n    /*\n    Each block of the grid takes care of multiplying one row of A with one column of B\n\n    The single thread in the block performs the inner product of the row of A and column of B\n    */\n    dim3 grid(M, Q);\n\n    matmulKernel<<<grid, 1>>>(d_a, d_b, d_c);\n\n    hipMemcpy(c, d_c, M * Q * sizeof(int), hipMemcpyDeviceToHost);\n    std::cout<<\"A*B =\\n\";\n    display_matrix(c, M, Q);\n\n    delete a;\n    delete b;\n    delete c;\n\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    return 0;\n}", "source": "/kaggle/working/cuda-examples/matmul/matmul.cu"}
{"input": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\n1 blocks, N threads in block\n*/\n#include<iostream>\n#include<cuda_runtime.h>\n#define N 10\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = threadIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains 1 block \n    That block has N threads\n    Hence index of vector is thread index\n    */\n    vecadd<<<1, N>>>(d_a, d_b, d_c);\n    cudaDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "output": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\n1 blocks, N threads in block\n*/\n#include \"hip/hip_runtime.h\"\n#include<iostream>\n#include<hip/hip_runtime.h>\n#define N 10\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = threadIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    hipMemcpy(d_a, a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, size, hipMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains 1 block \n    That block has N threads\n    Hence index of vector is thread index\n    */\n    vecadd<<<1, N>>>(d_a, d_b, d_c);\n    hipDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    hipMemcpy(c, d_c, size, hipMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "source": "/kaggle/working/cuda-examples/vecadd/vecadd2.cu"}
{"input": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\n10 blocks, N/10 threads per block\n*/\n#include<iostream>\n#include<cuda_runtime.h>\n#define N 100\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x*blockDim.x + threadIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains 10 blocks\n    Each block contains 2 threads\n    blockDim.x = N/10\n    Hence vector index idx = blockDim.x * blockIdx.x + threadIdx.x\n    */\n    int gridSize = 10;\n    int blockSize = N/gridSize;\n    vecadd<<<gridSize, blockSize>>>(d_a, d_b, d_c);\n    cudaDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "output": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\n10 blocks, N/10 threads per block\n*/\n#include \"hip/hip_runtime.h\"\n#include<iostream>\n#include<hip/hip_runtime.h>\n#define N 100\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x*blockDim.x + threadIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    hipMemcpy(d_a, a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, size, hipMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains 10 blocks\n    Each block contains 2 threads\n    blockDim.x = N/10\n    Hence vector index idx = blockDim.x * blockIdx.x + threadIdx.x\n    */\n    int gridSize = 10;\n    int blockSize = N/gridSize;\n    vecadd<<<gridSize, blockSize>>>(d_a, d_b, d_c);\n    hipDeviceSynchronize();\n\n    /* Copy result from GPU device to host */\n    hipMemcpy(c, d_c, size, hipMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "source": "/kaggle/working/cuda-examples/vecadd/vecadd3.cu"}
{"input": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\nN blocks, 1 thread per block\n*/\n#include<iostream>\n#include<cuda_runtime.h>\n#define N 10\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    cudaMalloc((void**)&d_a, size);\n    cudaMalloc((void**)&d_b, size);\n    cudaMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);\n    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains N blocks\n    Each block has only one thread\n    hence index of vector is block index\n    */\n    vecadd<<<N, 1>>>(d_a, d_b, d_c);\n    cudaDeviceSynchronize();\n    \n    /* Copy result from GPU device to host */\n    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    cudaFree(d_a);\n    cudaFree(d_b);\n    cudaFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "output": "/*\nCUDA C++ Program to add 2 1-dimensional vectors of length N\nN blocks, 1 thread per block\n*/\n#include \"hip/hip_runtime.h\"\n#include<iostream>\n#include<hip/hip_runtime.h>\n#define N 10\n\n__global__ void vecadd(int* a, int* b, int* c)\n{\n    int idx = blockIdx.x;\n    c[idx] = a[idx] + b[idx]; \n}\n\nint main()\n{\n    /* Set up variables on host*/\n    int* a = new int[N];\n    int* b = new int[N];\n    int* c = new int[N];\n\n    /* Input values on host*/\n    unsigned int size = N*sizeof(int);\n    for(int i=0; i<N; ++i)\n    {\n        a[i] = 2*i;\n        b[i] = 3*i+1;\n    }\n\n    /* Setting up variables on device. i.e. GPU */\n    int* d_a, *d_b, *d_c;\n    hipMalloc((void**)&d_a, size);\n    hipMalloc((void**)&d_b, size);\n    hipMalloc((void**)&d_c, size);\n\n    /* Copy data from host to device */\n    hipMemcpy(d_a, a, size, hipMemcpyHostToDevice);\n    hipMemcpy(d_b, b, size, hipMemcpyHostToDevice);\n    \n    /* \n    Kernel Launch\n    Grid contains N blocks\n    Each block has only one thread\n    hence index of vector is block index\n    */\n    vecadd<<<N, 1>>>(d_a, d_b, d_c);\n    hipDeviceSynchronize();\n    \n    /* Copy result from GPU device to host */\n    hipMemcpy(c, d_c, size, hipMemcpyDeviceToHost);\n    \n    /* Print result */\n    for(int i=0; i<N; ++i)\n    {\n        std::cout<<c[i]<<' ';\n    }\n    std::cout<<'\\n';\n\n    /* Cleanup device and host memory */\n    hipFree(d_a);\n    hipFree(d_b);\n    hipFree(d_c);\n    delete a;\n    delete b;\n    delete c;\n\n    return 0;\n}", "source": "/kaggle/working/cuda-examples/vecadd/vecadd.cu"}
{"input": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cuco/hyperloglog.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <iostream>\n\n/**\n * @file device_ref_example.cu\n * @brief Demonstrates usage of `cuco::hyperloglog` device-side APIs.\n *\n * This example demonstrates how the non-owning reference type `cuco::hyperloglog_ref`\n * can be used to implement a custom kernel that fuses the cardinality estimation step with any\n * other workload that traverses the input data.\n */\n\ntemplate <class RefType, class InputIt>\n__global__ void fused_kernel(RefType ref, InputIt first, std::size_t n)\n{\n  // Transform the reference type (with device scope) to a reference type with block scope\n  using local_ref_type = typename RefType::template with_scope<cuda::thread_scope_block>;\n\n  // Shared memory storage for the block-local estimator\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  // The following check is optional since the base address of dynamic shared memory is guaranteed\n  // to meet the alignment requirements\n  /*\n  auto const alignment =\n    1ull << cuda::std::countr_zero(reinterpret_cast<std::uintptr_t>(local_sketch));\n  assert(alignment >= local_ref_type::sketch_alignment());\n  */\n\n  auto const loop_stride = gridDim.x * blockDim.x;\n  auto idx               = blockDim.x * blockIdx.x + threadIdx.x;\n  auto const block       = cooperative_groups::this_thread_block();\n\n  // Create the local estimator with the shared memory storage\n  local_ref_type local_ref(cuda::std::span{local_sketch, ref.sketch_bytes()});\n\n  // Initialize the local estimator\n  local_ref.clear(block);\n  block.sync();\n\n  while (idx < n) {\n    auto const& item = *(first + idx);\n\n    // Add each item to the local estimator\n    local_ref.add(item);\n\n    /*\n    Here we can add some custom workload that takes the input `item`.\n\n    The idea is that cardinality estimation can be fused with any other workload that\n    traverses the data. Since `local_ref.add` can run close to the SOL of the DRAM bandwidth, we get\n    the estimate \"for free\" while performing other computations over the data.\n    */\n\n    idx += loop_stride;\n  }\n  block.sync();\n\n  // We can also compute the local estimate on the device\n  // auto const local_estimate = local_ref.estimate(block);\n  if (block.thread_rank() == 0) {\n    // The local estimate should approximately be `num_items`/`gridDim.x`\n    // printf(\"Estimate for block %d = %llu\\n\", blockIdx.x, local_estimate);\n  }\n\n  // In the end, we merge the shared memory estimator into the global estimator which gives us the\n  // final result\n  ref.merge(block, local_ref);\n}\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void device_estimate_kernel(cuco::sketch_size_kb sketch_size_kb,\n                                       InputIt in,\n                                       size_t n,\n                                       OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  auto const block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0) {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads()) {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    // we can compute the final estimate on the device and return the result to the host\n    auto const estimate = estimator.estimate(block);\n\n    if (block.thread_rank() == 0) { *out = estimate; }\n  }\n}\n\nint main(void)\n{\n  using T                         = int;\n  using estimator_type            = cuco::hyperloglog<T>;\n  constexpr std::size_t num_items = 1ull << 28;  // 1GB\n  auto const sketch_size_kb       = 32_KB;\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), 0);\n\n  // Initialize the estimator\n  estimator_type estimator(sketch_size_kb);\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  // Calculate the cardinality estimate from the bulk operation\n  std::size_t const estimated_cardinality_bulk = estimator.estimate();\n\n  // Clear the estimator so it can be reused\n  estimator.clear();\n\n  // Number of dynamic shared memory bytes required to store a CTA-local sketch\n  auto const sketch_bytes = estimator.sketch_bytes();\n\n  // Call the custom kernel and pass a non-owning reference to the estimator to the GPU\n  fused_kernel<<<10, 512, sketch_bytes>>>(estimator.ref(), items.begin(), num_items);\n\n  // Calculate the cardinality estimate from the custom kernel\n  std::size_t const estimated_cardinality_custom = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  device_estimate_kernel<typename estimator_type::ref_type<cuda::thread_scope_block>>\n    <<<1, 512, sketch_bytes>>>(sketch_size_kb, items.begin(), num_items, device_estimate.begin());\n\n  std::size_t const estimated_cardinality_device = device_estimate[0];\n\n  if (estimated_cardinality_custom == estimated_cardinality_bulk and\n      estimated_cardinality_device == estimated_cardinality_bulk) {\n    std::cout << \"Success! Cardinality estimates are identical\" << std::endl;\n  }\n\n  return 0;\n}\n", "output": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"hip/hip_runtime.h\"\n#include <cuco/hyperloglog.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <iostream>\n\n/**\n * @file device_ref_example.cu\n * @brief Demonstrates usage of `cuco::hyperloglog` device-side APIs.\n *\n * This example demonstrates how the non-owning reference type `cuco::hyperloglog_ref`\n * can be used to implement a custom kernel that fuses the cardinality estimation step with any\n * other workload that traverses the input data.\n */\n\ntemplate <class RefType, class InputIt>\n__global__ void fused_kernel(RefType ref, InputIt first, std::size_t n)\n{\n  // Transform the reference type (with device scope) to a reference type with block scope\n  using local_ref_type = typename RefType::template with_scope<cuda::thread_scope_block>;\n\n  // Shared memory storage for the block-local estimator\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  // The following check is optional since the base address of dynamic shared memory is guaranteed\n  // to meet the alignment requirements\n  /*\n  auto const alignment =\n    1ull << cuda::std::countr_zero(reinterpret_cast<std::uintptr_t>(local_sketch));\n  assert(alignment >= local_ref_type::sketch_alignment());\n  */\n\n  auto const loop_stride = gridDim.x * blockDim.x;\n  auto idx               = blockDim.x * blockIdx.x + threadIdx.x;\n  auto const block       = cooperative_groups::this_thread_block();\n\n  // Create the local estimator with the shared memory storage\n  local_ref_type local_ref(cuda::std::span{local_sketch, ref.sketch_bytes()});\n\n  // Initialize the local estimator\n  local_ref.clear(block);\n  block.sync();\n\n  while (idx < n) {\n    auto const& item = *(first + idx);\n\n    // Add each item to the local estimator\n    local_ref.add(item);\n\n    /*\n    Here we can add some custom workload that takes the input `item`.\n\n    The idea is that cardinality estimation can be fused with any other workload that\n    traverses the data. Since `local_ref.add` can run close to the SOL of the DRAM bandwidth, we get\n    the estimate \"for free\" while performing other computations over the data.\n    */\n\n    idx += loop_stride;\n  }\n  block.sync();\n\n  // We can also compute the local estimate on the device\n  // auto const local_estimate = local_ref.estimate(block);\n  if (block.thread_rank() == 0) {\n    // The local estimate should approximately be `num_items`/`gridDim.x`\n    // printf(\"Estimate for block %d = %llu\\n\", blockIdx.x, local_estimate);\n  }\n\n  // In the end, we merge the shared memory estimator into the global estimator which gives us the\n  // final result\n  ref.merge(block, local_ref);\n}\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void device_estimate_kernel(cuco::sketch_size_kb sketch_size_kb,\n                                       InputIt in,\n                                       size_t n,\n                                       OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  auto const block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0) {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads()) {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    // we can compute the final estimate on the device and return the result to the host\n    auto const estimate = estimator.estimate(block);\n\n    if (block.thread_rank() == 0) { *out = estimate; }\n  }\n}\n\nint main(void)\n{\n  using T                         = int;\n  using estimator_type            = cuco::hyperloglog<T>;\n  constexpr std::size_t num_items = 1ull << 28;  // 1GB\n  auto const sketch_size_kb       = 32_KB;\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), 0);\n\n  // Initialize the estimator\n  estimator_type estimator(sketch_size_kb);\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  // Calculate the cardinality estimate from the bulk operation\n  std::size_t const estimated_cardinality_bulk = estimator.estimate();\n\n  // Clear the estimator so it can be reused\n  estimator.clear();\n\n  // Number of dynamic shared memory bytes required to store a CTA-local sketch\n  auto const sketch_bytes = estimator.sketch_bytes();\n\n  // Call the custom kernel and pass a non-owning reference to the estimator to the GPU\n  fused_kernel<<<10, 512, sketch_bytes>>>(estimator.ref(), items.begin(), num_items);\n\n  // Calculate the cardinality estimate from the custom kernel\n  std::size_t const estimated_cardinality_custom = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  device_estimate_kernel<typename estimator_type::ref_type<cuda::thread_scope_block>>\n    <<<1, 512, sketch_bytes>>>(sketch_size_kb, items.begin(), num_items, device_estimate.begin());\n\n  std::size_t const estimated_cardinality_device = device_estimate[0];\n\n  if (estimated_cardinality_custom == estimated_cardinality_bulk and\n      estimated_cardinality_device == estimated_cardinality_bulk) {\n    std::cout << \"Success! Cardinality estimates are identical\" << std::endl;\n  }\n\n  return 0;\n}\n", "source": "/kaggle/working/cuCollections/examples/hyperloglog/device_ref_example.cu"}
{"input": "/*\n * Copyright (c) 2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_map.cuh>\n\n#include <cuda/std/array>\n#include <cuda/std/limits>\n\n#include <cooperative_groups.h>\n\n#include <cstdio>\n#include <iostream>\n\n/**\n * @file preallocated_memory_example.cu\n * @brief Demonstrates usage of static_map with pre-allocated device memory.\n *\n * This example shows how to use a static_map with device memory that is allocated\n * at compile time. This can be useful for cases where you want to avoid dynamic memory\n * allocation or when working with memory-constrained environments.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n */\n\n// Basic types\nusing Value = uint32_t;\nusing Key   = int;\n\n// Sentinel values for empty slots\nKey constexpr empty_key_sentinel     = -1;\nValue constexpr empty_value_sentinel = cuda::std::numeric_limits<Value>::min();\n\n// Map configuration\nstd::size_t constexpr capacity = 100'000;\n\n// Type aliases for cleaner code\nusing probing_scheme_type = cuco::linear_probing<1, cuco::default_hash_function<Key>>;\nusing storage_type        = cuco::bucket_storage<cuco::pair<Key, Value>, 1>;\nusing storage_ref_type    = typename storage_type::ref_type;\nusing map_ref_type        = cuco::static_map_ref<Key,\n                                                 Value,\n                                                 cuda::thread_scope_device,\n                                                 cuda::std::equal_to<Key>,\n                                                 probing_scheme_type,\n                                                 storage_ref_type>;\n\n// Pre-allocated device memory\n__device__ auto constexpr valid_extent =\n  cuco::make_valid_extent<probing_scheme_type, cuco::storage<1>>(cuco::extent<size_t, capacity>{});\n\n__device__ cuda::std::array<typename storage_ref_type::value_type, valid_extent.value()>\n  storage_array;\n__device__ int found_count = 0;  // Track number of items found (for verification)\n\n// Helper function to create map reference from pre-allocated storage\n__device__ map_ref_type create_map()\n{\n  storage_ref_type storage_ref{valid_extent.value(), storage_array.data()};\n  return map_ref_type(cuco::empty_key{empty_key_sentinel},\n                      cuco::empty_value{empty_value_sentinel},\n                      cuda::std::equal_to<Key>{},\n                      probing_scheme_type{},\n                      cuco::cuda_thread_scope<cuda::thread_scope_device>{},\n                      storage_ref);\n}\n\n/**\n * @brief Initialize the pre-allocated storage\n */\n__global__ void init_kernel()\n{\n  auto map         = create_map();\n  auto const block = cooperative_groups::this_thread_block();\n\n  // Initialize storage using all threads in the block\n  map.initialize(block);\n  block.sync();\n}\n\n/**\n * @brief Insert key-value pairs (key -> key*2)\n */\n__global__ void insert_kernel()\n{\n  auto map        = create_map();\n  auto insert_ref = map.rebind_operators(cuco::insert);\n\n  // Each thread inserts one key-value pair\n  auto key = threadIdx.x + blockIdx.x;\n  insert_ref.insert(cuco::pair(key, key * 2));\n}\n\n/**\n * @brief Find and verify inserted key-value pairs\n */\n__global__ void find_kernel()\n{\n  auto map      = create_map();\n  auto find_ref = map.rebind_operators(cuco::find);\n\n  // Each thread looks up one key\n  auto key    = threadIdx.x + blockIdx.x;\n  auto result = find_ref.find(key);\n\n  // Count successful finds (no printf output)\n  if (result != find_ref.end()) {\n    auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{found_count};\n    ref.fetch_add(1, cuda::memory_order_relaxed);\n  }\n}\n\nint main()\n{\n  // Step 1: Initialize the pre-allocated storage\n  init_kernel<<<1, 128>>>();\n  CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n  // Step 2: Insert some key-value pairs\n  insert_kernel<<<2, 32>>>();\n  CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n  // Step 3: Find and verify the inserted pairs\n  find_kernel<<<2, 32>>>();\n\n  // Check results - expect to find all 64 keys (2 blocks * 32 threads)\n  int host_found_count;\n  CUCO_CUDA_TRY(cudaMemcpyFromSymbol(&host_found_count, found_count, sizeof(int)));\n  int expected_count = 2 * 32;  // Total number of keys inserted and queried\n\n  if (host_found_count == expected_count) {\n    std::cout << \"Success: all keys are found\" << std::endl;\n  } else {\n    std::cout << \"Fail\" << std::endl;\n  }\n\n  return 0;\n}", "output": "/*\n * Copyright (c) 2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_map.cuh>\n\n#include <cuda/std/array>\n#include <cuda/std/limits>\n\n#include <hip/hip_cooperative_groups.h>\n\n#include <cstdio>\n#include <iostream>\n\n/**\n * @file preallocated_memory_example.cu\n * @brief Demonstrates usage of static_map with pre-allocated device memory.\n *\n * This example shows how to use a static_map with device memory that is allocated\n * at compile time. This can be useful for cases where you want to avoid dynamic memory\n * allocation or when working with memory-constrained environments.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n */\n\n// Basic types\nusing Value = uint32_t;\nusing Key   = int;\n\n// Sentinel values for empty slots\nKey constexpr empty_key_sentinel     = -1;\nValue constexpr empty_value_sentinel = cuda::std::numeric_limits<Value>::min();\n\n// Map configuration\nstd::size_t constexpr capacity = 100'000;\n\n// Type aliases for cleaner code\nusing probing_scheme_type = cuco::linear_probing<1, cuco::default_hash_function<Key>>;\nusing storage_type        = cuco::bucket_storage<cuco::pair<Key, Value>, 1>;\nusing storage_ref_type    = typename storage_type::ref_type;\nusing map_ref_type        = cuco::static_map_ref<Key,\n                                                 Value,\n                                                 cuda::thread_scope_device,\n                                                 cuda::std::equal_to<Key>,\n                                                 probing_scheme_type,\n                                                 storage_ref_type>;\n\n// Pre-allocated device memory\n__device__ auto constexpr valid_extent =\n  cuco::make_valid_extent<probing_scheme_type, cuco::storage<1>>(cuco::extent<size_t, capacity>{});\n\n__device__ cuda::std::array<typename storage_ref_type::value_type, valid_extent.value()>\n  storage_array;\n__device__ int found_count = 0;  // Track number of items found (for verification)\n\n// Helper function to create map reference from pre-allocated storage\n__device__ map_ref_type create_map()\n{\n  storage_ref_type storage_ref{valid_extent.value(), storage_array.data()};\n  return map_ref_type(cuco::empty_key{empty_key_sentinel},\n                      cuco::empty_value{empty_value_sentinel},\n                      cuda::std::equal_to<Key>{},\n                      probing_scheme_type{},\n                      cuco::cuda_thread_scope<cuda::thread_scope_device>{},\n                      storage_ref);\n}\n\n/**\n * @brief Initialize the pre-allocated storage\n */\n__global__ void init_kernel()\n{\n  auto map         = create_map();\n  auto const block = cooperative_groups::this_thread_block();\n\n  // Initialize storage using all threads in the block\n  map.initialize(block);\n  block.sync();\n}\n\n/**\n * @brief Insert key-value pairs (key -> key*2)\n */\n__global__ void insert_kernel()\n{\n  auto map        = create_map();\n  auto insert_ref = map.rebind_operators(cuco::insert);\n\n  // Each thread inserts one key-value pair\n  auto key = threadIdx.x + blockIdx.x;\n  insert_ref.insert(cuco::pair(key, key * 2));\n}\n\n/**\n * @brief Find and verify inserted key-value pairs\n */\n__global__ void find_kernel()\n{\n  auto map      = create_map();\n  auto find_ref = map.rebind_operators(cuco::find);\n\n  // Each thread looks up one key\n  auto key    = threadIdx.x + blockIdx.x;\n  auto result = find_ref.find(key);\n\n  // Count successful finds (no printf output)\n  if (result != find_ref.end()) {\n    auto ref = cuda::atomic_ref<int, cuda::thread_scope_device>{found_count};\n    ref.fetch_add(1, cuda::memory_order_relaxed);\n  }\n}\n\nint main()\n{\n  // Step 1: Initialize the pre-allocated storage\n  init_kernel<<<1, 128>>>();\n  CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n  // Step 2: Insert some key-value pairs\n  insert_kernel<<<2, 32>>>();\n  CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n  // Step 3: Find and verify the inserted pairs\n  find_kernel<<<2, 32>>>();\n\n  // Check results - expect to find all 64 keys (2 blocks * 32 threads)\n  int host_found_count;\n  CUCO_CUDA_TRY(hipMemcpyFromSymbol(&host_found_count,HIP_SYMBOL(found_count), sizeof(int)));\n  int expected_count = 2 * 32;  // Total number of keys inserted and queried\n\n  if (host_found_count == expected_count) {\n    std::cout << \"Success: all keys are found\" << std::endl;\n  } else {\n    std::cout << \"Fail\" << std::endl;\n  }\n\n  return 0;\n}", "source": "/kaggle/working/cuCollections/examples/static_map/preallocated_memory_example.cu"}
{"input": "/*\n * Copyright (c) 2023-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_map.cuh>\n\n#include <cub/block/block_reduce.cuh>\n#include <cuda/iterator>\n#include <cuda/std/atomic>\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/transform.h>\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <iostream>\n#include <limits>\n\n/**\n * @file count_by_key_example.cu\n * @brief Demonstrates usage of the device side APIs for individual operations like insert/find in\n * the context of a count-by-key operation, i.e. for a histogram over keys.\n *\n * Individual operations like a single insert or find can be performed in device code via the\n * \"static_map_ref\" types.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n *\n */\n\n/**\n * @brief Inserts keys and counts how often they occur in the input sequence.\n *\n * @tparam BlockSize CUDA block size\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n * @tparam UniqueIter Output iterator whose value_type is convertible to uint64_t\n *\n * @param[in] map_ref Reference of the map into which inserts will be performed\n * @param[in] key_begin The beginning of the range of keys to insert\n * @param[in] num_keys The total number of keys and values\n * @param[out] num_unique_keys The total number of distinct keys inserted\n */\ntemplate <int64_t BlockSize, typename Map, typename KeyIter, typename UniqueIter>\n__global__ void count_by_key(Map map_ref,\n                             KeyIter keys,\n                             uint64_t num_keys,\n                             UniqueIter num_unique_keys)\n{\n  using BlockReduce = cub::BlockReduce<uint64_t, BlockSize>;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n\n  int64_t const loop_stride = gridDim.x * BlockSize;\n  int64_t idx               = BlockSize * blockIdx.x + threadIdx.x;\n\n  uint64_t thread_unique_keys = 0;\n  while (idx < num_keys) {\n    // insert key into the map with a count of 1\n    auto [slot, is_new_key] = map_ref.insert_and_find(cuco::pair{keys[idx], 1});\n    if (is_new_key) {\n      // first occurrence of the key\n      thread_unique_keys++;\n    } else {\n      // key is already in the map -> increment count\n      auto ref = cuda::atomic_ref<uint32_t, cuda::thread_scope_device>{slot->second};\n      ref.fetch_add(1, cuda::memory_order_relaxed);\n    }\n    idx += loop_stride;\n  }\n\n  // compute number of successfully inserted new keys for each block\n  // and atomically add to the grand total\n  uint64_t block_unique_keys = BlockReduce(temp_storage).Sum(thread_unique_keys);\n  if (threadIdx.x == 0) {\n    cuda::atomic_ref<uint64_t, cuda::thread_scope_device> grid_unique_keys(\n      *thrust::raw_pointer_cast(num_unique_keys));\n    grid_unique_keys.fetch_add(block_unique_keys, cuda::memory_order_relaxed);\n  }\n}\n\nint main(void)\n{\n  // Note that if (sizeof(Key)+sizeof(Count))>8 then the minimum required CUDA architecture is sm_70\n  using Key   = uint32_t;\n  using Count = uint32_t;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel     = static_cast<Key>(-1);\n  Count constexpr empty_value_sentinel = static_cast<Count>(-1);\n\n  // Number of keys to be inserted\n  auto constexpr num_keys = 50'000;\n  // How often each distinct key occurs in the example input\n  auto constexpr key_duplicates = 5;\n  static_assert((num_keys % key_duplicates) == 0,\n                \"For this example, num_keys must be divisible by key_duplicates in order to pass \"\n                \"the unit test.\");\n\n  thrust::device_vector<Key> insert_keys(num_keys);\n  // Create a sequence of keys. Eeach distinct key has key_duplicates many matches.\n  thrust::transform(\n    cuda::make_counting_iterator<Key>(0),\n    cuda::make_counting_iterator<Key>(insert_keys.size()),\n    insert_keys.begin(),\n    [] __device__(auto i) -> Key { return static_cast<Key>(i % (num_keys / key_duplicates)); });\n\n  // Allocate storage for count of number of unique keys\n  thrust::device_vector<uint64_t> num_unique_keys(1);\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n\n  // If the number of elements is known in advance, we can use it to calculate the map capacity\n  std::size_t const num_elements = num_keys / key_duplicates;\n\n  // Constructs a map with number of elements and desired load factor.\n  auto map = cuco::static_map{num_elements,\n                              load_factor,\n                              cuco::empty_key{empty_key_sentinel},\n                              cuco::empty_value{empty_value_sentinel},\n                              cuda::std::equal_to<Key>{},\n                              cuco::linear_probing<1, cuco::default_hash_function<Key>>{}};\n\n  // Get a non-owning, mutable reference of the map that allows `insert_and_find` operation to pass\n  // by value into the kernel\n  auto map_ref = map.ref(cuco::insert_and_find);\n\n  auto constexpr block_size = 256;\n  auto const grid_size      = (num_keys + block_size - 1) / block_size;\n  count_by_key<block_size>\n    <<<grid_size, block_size>>>(map_ref, insert_keys.begin(), num_keys, num_unique_keys.data());\n\n  // Retrieve contents of all the non-empty slots in the map\n  thrust::device_vector<Key> result_keys(num_unique_keys[0]);\n  thrust::device_vector<Count> result_counts(num_unique_keys[0]);\n  map.retrieve_all(result_keys.begin(), result_counts.begin());\n\n  // Check if the number of result keys is correct\n  auto const num_keys_check = num_unique_keys[0] == (num_keys / key_duplicates);\n\n  // Iterate over all result counts and verify that they are correct\n  auto const counts_check = thrust::all_of(\n    result_counts.begin(), result_counts.end(), [] __device__(Count const count) -> bool {\n      return count == key_duplicates;\n    });\n\n  if (num_keys_check and counts_check) { std::cout << \"Success!\\n\"; }\n\n  return 0;\n}\n", "output": "/*\n * Copyright (c) 2023-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_map.cuh>\n\n#include <cub/block/block_reduce.cuh>\n#include <cuda/iterator>\n#include <cuda/std/atomic>\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/transform.h>\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <iostream>\n#include <limits>\n\n/**\n * @file count_by_key_example.cu\n * @brief Demonstrates usage of the device side APIs for individual operations like insert/find in\n * the context of a count-by-key operation, i.e. for a histogram over keys.\n *\n * Individual operations like a single insert or find can be performed in device code via the\n * \"static_map_ref\" types.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n *\n */\n\n/**\n * @brief Inserts keys and counts how often they occur in the input sequence.\n *\n * @tparam BlockSize CUDA block size\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n * @tparam UniqueIter Output iterator whose value_type is convertible to uint64_t\n *\n * @param[in] map_ref Reference of the map into which inserts will be performed\n * @param[in] key_begin The beginning of the range of keys to insert\n * @param[in] num_keys The total number of keys and values\n * @param[out] num_unique_keys The total number of distinct keys inserted\n */\ntemplate <int64_t BlockSize, typename Map, typename KeyIter, typename UniqueIter>\n__global__ void count_by_key(Map map_ref,\n                             KeyIter keys,\n                             uint64_t num_keys,\n                             UniqueIter num_unique_keys)\n{\n  using BlockReduce = hipcub::BlockReduce<uint64_t, BlockSize>;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n\n  int64_t const loop_stride = gridDim.x * BlockSize;\n  int64_t idx               = BlockSize * blockIdx.x + threadIdx.x;\n\n  uint64_t thread_unique_keys = 0;\n  while (idx < num_keys) {\n    // insert key into the map with a count of 1\n    auto [slot, is_new_key] = map_ref.insert_and_find(cuco::pair{keys[idx], 1});\n    if (is_new_key) {\n      // first occurrence of the key\n      thread_unique_keys++;\n    } else {\n      // key is already in the map -> increment count\n      auto ref = cuda::atomic_ref<uint32_t, cuda::thread_scope_device>{slot->second};\n      ref.fetch_add(1, cuda::memory_order_relaxed);\n    }\n    idx += loop_stride;\n  }\n\n  // compute number of successfully inserted new keys for each block\n  // and atomically add to the grand total\n  uint64_t block_unique_keys = BlockReduce(temp_storage).Sum(thread_unique_keys);\n  if (threadIdx.x == 0) {\n    cuda::atomic_ref<uint64_t, cuda::thread_scope_device> grid_unique_keys(\n      *thrust::raw_pointer_cast(num_unique_keys));\n    grid_unique_keys.fetch_add(block_unique_keys, cuda::memory_order_relaxed);\n  }\n}\n\nint main(void)\n{\n  // Note that if (sizeof(Key)+sizeof(Count))>8 then the minimum required CUDA architecture is sm_70\n  using Key   = uint32_t;\n  using Count = uint32_t;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel     = static_cast<Key>(-1);\n  Count constexpr empty_value_sentinel = static_cast<Count>(-1);\n\n  // Number of keys to be inserted\n  auto constexpr num_keys = 50'000;\n  // How often each distinct key occurs in the example input\n  auto constexpr key_duplicates = 5;\n  static_assert((num_keys % key_duplicates) == 0,\n                \"For this example, num_keys must be divisible by key_duplicates in order to pass \"\n                \"the unit test.\");\n\n  thrust::device_vector<Key> insert_keys(num_keys);\n  // Create a sequence of keys. Eeach distinct key has key_duplicates many matches.\n  thrust::transform(\n    cuda::make_counting_iterator<Key>(0),\n    cuda::make_counting_iterator<Key>(insert_keys.size()),\n    insert_keys.begin(),\n    [] __device__(auto i) -> Key { return static_cast<Key>(i % (num_keys / key_duplicates)); });\n\n  // Allocate storage for count of number of unique keys\n  thrust::device_vector<uint64_t> num_unique_keys(1);\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n\n  // If the number of elements is known in advance, we can use it to calculate the map capacity\n  std::size_t const num_elements = num_keys / key_duplicates;\n\n  // Constructs a map with number of elements and desired load factor.\n  auto map = cuco::static_map{num_elements,\n                              load_factor,\n                              cuco::empty_key{empty_key_sentinel},\n                              cuco::empty_value{empty_value_sentinel},\n                              cuda::std::equal_to<Key>{},\n                              cuco::linear_probing<1, cuco::default_hash_function<Key>>{}};\n\n  // Get a non-owning, mutable reference of the map that allows `insert_and_find` operation to pass\n  // by value into the kernel\n  auto map_ref = map.ref(cuco::insert_and_find);\n\n  auto constexpr block_size = 256;\n  auto const grid_size      = (num_keys + block_size - 1) / block_size;\n  count_by_key<block_size>\n    <<<grid_size, block_size>>>(map_ref, insert_keys.begin(), num_keys, num_unique_keys.data());\n\n  // Retrieve contents of all the non-empty slots in the map\n  thrust::device_vector<Key> result_keys(num_unique_keys[0]);\n  thrust::device_vector<Count> result_counts(num_unique_keys[0]);\n  map.retrieve_all(result_keys.begin(), result_counts.begin());\n\n  // Check if the number of result keys is correct\n  auto const num_keys_check = num_unique_keys[0] == (num_keys / key_duplicates);\n\n  // Iterate over all result counts and verify that they are correct\n  auto const counts_check = thrust::all_of(\n    result_counts.begin(), result_counts.end(), [] __device__(Count const count) -> bool {\n      return count == key_duplicates;\n    });\n\n  if (num_keys_check and counts_check) { std::cout << \"Success!\\n\"; }\n\n  return 0;\n}\n", "source": "/kaggle/working/cuCollections/examples/static_map/count_by_key_example.cu"}
{"input": "/*\n * Copyright (c) 2020-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_map.cuh>\n\n#include <cuda/std/functional>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <cmath>\n#include <cstddef>\n#include <iostream>\n#include <limits>\n\n/**\n * @file device_ref_example.cu\n * @brief Demonstrates usage of the device side APIs for individual operations like insert/find.\n *\n * Individual operations like a single insert or find can be performed in device code via the\n * \"static_map_ref\" types.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n *\n */\n\n/**\n * @brief Inserts keys that pass the specified predicated into the map.\n *\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n * @tparam ValueIter Input iterator whose value_type is convertible to Map::mapped_type\n * @tparam Predicate Unary predicate\n *\n * @param[in] map_ref Reference of the map into which inserts will be performed\n * @param[in] key_begin The beginning of the range of keys to insert\n * @param[in] value_begin The beginning of the range of values associated with each key to insert\n * @param[in] num_keys The total number of keys and values\n * @param[in] pred Unary predicate applied to each key. Only keys that pass the predicated will be\n * inserted.\n * @param[out] num_inserted The total number of keys successfully inserted\n */\ntemplate <typename Map, typename KeyIter, typename ValueIter, typename Predicate>\n__global__ void filtered_insert(Map map_ref,\n                                KeyIter key_begin,\n                                ValueIter value_begin,\n                                std::size_t num_keys,\n                                Predicate pred,\n                                int* num_inserted)\n{\n  auto tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  std::size_t counter = 0;\n  while (tid < num_keys) {\n    // Only insert keys that pass the predicate\n    if (pred(key_begin[tid])) {\n      // Map::insert returns `true` if it is the first time the given key was\n      // inserted and `false` if the key already existed\n      if (map_ref.insert(cuco::pair{key_begin[tid], value_begin[tid]})) {\n        ++counter;  // Count number of successfully inserted keys\n      }\n    }\n    tid += gridDim.x * blockDim.x;\n  }\n\n  // Update global count of inserted keys\n  atomicAdd(num_inserted, counter);\n}\n\n/**\n * @brief For keys that have a match in the map, increments their corresponding value by one.\n *\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n *\n * @param map_ref Reference of the map into which queries will be performed\n * @param key_begin The beginning of the range of keys to query\n * @param num_keys The total number of keys\n */\ntemplate <typename Map, typename KeyIter>\n__global__ void increment_values(Map map_ref, KeyIter key_begin, std::size_t num_keys)\n{\n  auto tid = threadIdx.x + blockIdx.x * blockDim.x;\n  while (tid < num_keys) {\n    // If the key exists in the map, find returns an iterator to the specified key. Otherwise it\n    // returns map.end()\n    auto found = map_ref.find(key_begin[tid]);\n    if (found != map_ref.end()) {\n      // If the key exists, atomically increment the associated value\n      auto ref =\n        cuda::atomic_ref<typename Map::mapped_type, cuda::thread_scope_device>{found->second};\n      ref.fetch_add(1, cuda::memory_order_relaxed);\n    }\n    tid += gridDim.x * blockDim.x;\n  }\n}\n\nint main(void)\n{\n  using Key   = int;\n  using Value = int;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel     = -1;\n  Value constexpr empty_value_sentinel = -1;\n\n  // Number of key/value pairs to be inserted\n  std::size_t constexpr num_keys = 50'000;\n\n  // Create a sequence of keys and values {{0,0}, {1,1}, ... {i,i}}\n  thrust::device_vector<Key> insert_keys(num_keys);\n  thrust::sequence(insert_keys.begin(), insert_keys.end(), 0);\n  thrust::device_vector<Value> insert_values(num_keys);\n  thrust::sequence(insert_values.begin(), insert_values.end(), 0);\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n  std::size_t const capacity = std::ceil(num_keys / load_factor);\n\n  // Constructs a map with \"capacity\" slots using -1 and -1 as the empty key/value sentinels.\n  auto map = cuco::static_map{capacity,\n                              cuco::empty_key{empty_key_sentinel},\n                              cuco::empty_value{empty_value_sentinel},\n                              cuda::std::equal_to<Key>{},\n                              cuco::linear_probing<1, cuco::default_hash_function<Key>>{}};\n\n  // Get a non-owning, mutable reference of the map that allows inserts to pass by value into the\n  // kernel\n  auto insert_ref = map.ref(cuco::insert);\n\n  // Predicate will only insert even keys\n  auto is_even = [] __device__(auto key) -> bool { return (key % 2) == 0; };\n\n  // Allocate storage for count of number of inserted keys\n  thrust::device_vector<int> num_inserted(1);\n\n  auto constexpr block_size = 256;\n  auto const grid_size      = (num_keys + block_size - 1) / block_size;\n  filtered_insert<<<grid_size, block_size>>>(insert_ref,\n                                             insert_keys.begin(),\n                                             insert_values.begin(),\n                                             num_keys,\n                                             is_even,\n                                             num_inserted.data().get());\n\n  std::cout << \"Number of keys inserted: \" << num_inserted[0] << std::endl;\n\n  // Get a non-owning reference of the map that allows find operations to pass by value into the\n  // kernel\n  auto find_ref = map.ref(cuco::find);\n\n  increment_values<<<grid_size, block_size>>>(find_ref, insert_keys.begin(), num_keys);\n\n  // Retrieve contents of all the non-empty slots in the map\n  thrust::device_vector<Key> contained_keys(num_inserted[0]);\n  thrust::device_vector<Value> contained_values(num_inserted[0]);\n  map.retrieve_all(contained_keys.begin(), contained_values.begin());\n\n  auto tuple_iter =\n    thrust::make_zip_iterator(cuda::std::tuple{contained_keys.begin(), contained_values.begin()});\n  // Iterate over all slot contents and verify that `slot.key + 1 == slot.value` is always true.\n  auto result = thrust::all_of(thrust::device,\n                               tuple_iter,\n                               tuple_iter + num_inserted[0],\n                               [] __device__(auto const& tuple) -> bool {\n                                 return cuda::std::get<0>(tuple) + 1 == cuda::std::get<1>(tuple);\n                               });\n\n  if (result) { std::cout << \"Success! Target values are properly incremented.\\n\"; }\n\n  return 0;\n}\n", "output": "/*\n * Copyright (c) 2020-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_map.cuh>\n\n#include <cuda/std/functional>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <cmath>\n#include <cstddef>\n#include <iostream>\n#include <limits>\n\n/**\n * @file device_ref_example.cu\n * @brief Demonstrates usage of the device side APIs for individual operations like insert/find.\n *\n * Individual operations like a single insert or find can be performed in device code via the\n * \"static_map_ref\" types.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n *\n */\n\n/**\n * @brief Inserts keys that pass the specified predicated into the map.\n *\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n * @tparam ValueIter Input iterator whose value_type is convertible to Map::mapped_type\n * @tparam Predicate Unary predicate\n *\n * @param[in] map_ref Reference of the map into which inserts will be performed\n * @param[in] key_begin The beginning of the range of keys to insert\n * @param[in] value_begin The beginning of the range of values associated with each key to insert\n * @param[in] num_keys The total number of keys and values\n * @param[in] pred Unary predicate applied to each key. Only keys that pass the predicated will be\n * inserted.\n * @param[out] num_inserted The total number of keys successfully inserted\n */\ntemplate <typename Map, typename KeyIter, typename ValueIter, typename Predicate>\n__global__ void filtered_insert(Map map_ref,\n                                KeyIter key_begin,\n                                ValueIter value_begin,\n                                std::size_t num_keys,\n                                Predicate pred,\n                                int* num_inserted)\n{\n  auto tid = threadIdx.x + blockIdx.x * blockDim.x;\n\n  std::size_t counter = 0;\n  while (tid < num_keys) {\n    // Only insert keys that pass the predicate\n    if (pred(key_begin[tid])) {\n      // Map::insert returns `true` if it is the first time the given key was\n      // inserted and `false` if the key already existed\n      if (map_ref.insert(cuco::pair{key_begin[tid], value_begin[tid]})) {\n        ++counter;  // Count number of successfully inserted keys\n      }\n    }\n    tid += gridDim.x * blockDim.x;\n  }\n\n  // Update global count of inserted keys\n  atomicAdd(num_inserted, counter);\n}\n\n/**\n * @brief For keys that have a match in the map, increments their corresponding value by one.\n *\n * @tparam Map Type of the map device reference\n * @tparam KeyIter Input iterator whose value_type convertible to Map::key_type\n *\n * @param map_ref Reference of the map into which queries will be performed\n * @param key_begin The beginning of the range of keys to query\n * @param num_keys The total number of keys\n */\ntemplate <typename Map, typename KeyIter>\n__global__ void increment_values(Map map_ref, KeyIter key_begin, std::size_t num_keys)\n{\n  auto tid = threadIdx.x + blockIdx.x * blockDim.x;\n  while (tid < num_keys) {\n    // If the key exists in the map, find returns an iterator to the specified key. Otherwise it\n    // returns map.end()\n    auto found = map_ref.find(key_begin[tid]);\n    if (found != map_ref.end()) {\n      // If the key exists, atomically increment the associated value\n      auto ref =\n        cuda::atomic_ref<typename Map::mapped_type, cuda::thread_scope_device>{found->second};\n      ref.fetch_add(1, cuda::memory_order_relaxed);\n    }\n    tid += gridDim.x * blockDim.x;\n  }\n}\n\nint main(void)\n{\n  using Key   = int;\n  using Value = int;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel     = -1;\n  Value constexpr empty_value_sentinel = -1;\n\n  // Number of key/value pairs to be inserted\n  std::size_t constexpr num_keys = 50'000;\n\n  // Create a sequence of keys and values {{0,0}, {1,1}, ... {i,i}}\n  thrust::device_vector<Key> insert_keys(num_keys);\n  thrust::sequence(insert_keys.begin(), insert_keys.end(), 0);\n  thrust::device_vector<Value> insert_values(num_keys);\n  thrust::sequence(insert_values.begin(), insert_values.end(), 0);\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n  std::size_t const capacity = std::ceil(num_keys / load_factor);\n\n  // Constructs a map with \"capacity\" slots using -1 and -1 as the empty key/value sentinels.\n  auto map = cuco::static_map{capacity,\n                              cuco::empty_key{empty_key_sentinel},\n                              cuco::empty_value{empty_value_sentinel},\n                              cuda::std::equal_to<Key>{},\n                              cuco::linear_probing<1, cuco::default_hash_function<Key>>{}};\n\n  // Get a non-owning, mutable reference of the map that allows inserts to pass by value into the\n  // kernel\n  auto insert_ref = map.ref(cuco::insert);\n\n  // Predicate will only insert even keys\n  auto is_even = [] __device__(auto key) -> bool { return (key % 2) == 0; };\n\n  // Allocate storage for count of number of inserted keys\n  thrust::device_vector<int> num_inserted(1);\n\n  auto constexpr block_size = 256;\n  auto const grid_size      = (num_keys + block_size - 1) / block_size;\n  filtered_insert<<<grid_size, block_size>>>(insert_ref,\n                                             insert_keys.begin(),\n                                             insert_values.begin(),\n                                             num_keys,\n                                             is_even,\n                                             num_inserted.data().get());\n\n  std::cout << \"Number of keys inserted: \" << num_inserted[0] << std::endl;\n\n  // Get a non-owning reference of the map that allows find operations to pass by value into the\n  // kernel\n  auto find_ref = map.ref(cuco::find);\n\n  increment_values<<<grid_size, block_size>>>(find_ref, insert_keys.begin(), num_keys);\n\n  // Retrieve contents of all the non-empty slots in the map\n  thrust::device_vector<Key> contained_keys(num_inserted[0]);\n  thrust::device_vector<Value> contained_values(num_inserted[0]);\n  map.retrieve_all(contained_keys.begin(), contained_values.begin());\n\n  auto tuple_iter =\n    thrust::make_zip_iterator(cuda::std::tuple{contained_keys.begin(), contained_values.begin()});\n  // Iterate over all slot contents and verify that `slot.key + 1 == slot.value` is always true.\n  auto result = thrust::all_of(thrust::device,\n                               tuple_iter,\n                               tuple_iter + num_inserted[0],\n                               [] __device__(auto const& tuple) -> bool {\n                                 return cuda::std::get<0>(tuple) + 1 == cuda::std::get<1>(tuple);\n                               });\n\n  if (result) { std::cout << \"Success! Target values are properly incremented.\\n\"; }\n\n  return 0;\n}\n", "source": "/kaggle/working/cuCollections/examples/static_map/device_ref_example.cu"}
{"input": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_set_ref.cuh>\n\n#include <cuda/std/array>\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/scan.h>\n\n#include <cooperative_groups.h>\n\n#include <algorithm>\n#include <cstddef>\n#include <iostream>\n#include <numeric>\n\n/**\n * @file device_subsets_example.cu\n * @brief Demonstrates how to use one bulk set storage to create multiple subsets and perform\n * individual operations via device-side ref APIs.\n *\n * To optimize memory usage, especially when dealing with expensive data allocation and multiple\n * hashsets, a practical solution involves employing a single bulk storage for generating subsets.\n * This eliminates the need for separate memory allocation and deallocation for each container. This\n * can be achieved by using the lightweight non-owning ref type.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n */\n\nauto constexpr cg_size     = 8;   ///< A CUDA Cooperative Group of 8 threads to handle each subset\nauto constexpr bucket_size = 1;   ///< Number of concurrent slots handled by each thread\nauto constexpr N           = 10;  ///< Number of elements to insert and query\n\nusing key_type = int;  ///< Key type\nusing probing_scheme_type =\n  cuco::linear_probing<cg_size,\n                       cuco::default_hash_function<key_type>>;  ///< Type controls CG granularity\n                                                                ///< and probing scheme (linear\n                                                                ///< probing v.s. double hashing)\n/// Type of bulk allocation storage\nusing storage_type = cuco::bucket_storage<key_type, bucket_size>;\n/// Lightweight non-owning storage ref type\nusing storage_ref_type = typename storage_type::ref_type;\nusing ref_type         = cuco::static_set_ref<key_type,\n                                              cuda::thread_scope_device,\n                                              cuda::std::equal_to<key_type>,\n                                              probing_scheme_type,\n                                              storage_ref_type>;  ///< Set ref type\n\n/// Sample data to insert and query\n__device__ constexpr cuda::std::array<key_type, N> data = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};\n/// Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n/// that they never occur in your input data.\nkey_type constexpr empty_key_sentinel = -1;\n\n/**\n * @brief Inserts sample data into subsets by using cooperative group\n *\n * Each Cooperative Group creates its own subset and inserts `N` sample data.\n *\n * @param set_refs Pointer to the array of subset objects\n */\n__global__ void insert(ref_type* set_refs)\n{\n  namespace cg = cooperative_groups;\n\n  auto const tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n  // Get subset (or CG) index\n  auto const idx = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  auto raw_set_ref    = *(set_refs + idx);\n  auto insert_set_ref = raw_set_ref.rebind_operators(cuco::insert);\n\n  // Insert `N` elemtns into the set with CG insert\n  for (int i = 0; i < N; i++) {\n    insert_set_ref.insert(tile, data[i]);\n  }\n}\n\n/**\n * @brief All inserted data can be found\n *\n * Each Cooperative Group reconstructs its own subset ref based on the storage parameters and\n * verifies all inserted data can be found.\n *\n * @param set_refs Pointer to the array of subset objects\n */\n__global__ void find(ref_type* set_refs)\n{\n  namespace cg = cooperative_groups;\n\n  auto const tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n  auto const idx  = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  auto raw_set_ref  = *(set_refs + idx);\n  auto find_set_ref = raw_set_ref.rebind_operators(cuco::find);\n\n  // Result denoting if any of the inserted data is not found\n  __shared__ int result;\n  if (threadIdx.x == 0) { result = 0; }\n  __syncthreads();\n\n  for (int i = 0; i < N; i++) {\n    // Query the set with inserted data\n    auto const found = find_set_ref.find(tile, data[i]);\n    // Record if the inserted data has been found\n    atomicOr(&result, *found != data[i]);\n  }\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    // If the result is still 0, all inserted data are found.\n    if (result == 0) { printf(\"Success! Found all inserted elements.\\n\"); }\n  }\n}\n\nint main()\n{\n  // Number of subsets to be created\n  auto constexpr num = 16;\n  // Each subset may have a different requested size\n  auto constexpr subset_sizes =\n    std::array<std::size_t, num>{20, 20, 20, 20, 30, 30, 30, 30, 40, 40, 40, 40, 50, 50, 50, 50};\n\n  auto valid_sizes = std::vector<std::size_t>();\n  valid_sizes.reserve(num);\n\n  for (size_t i = 0; i < num; ++i) {\n    valid_sizes.emplace_back(static_cast<std::size_t>(\n      cuco::make_valid_extent<probing_scheme_type, storage_ref_type>(subset_sizes[i])));\n  }\n\n  std::vector<std::size_t> offsets(num + 1, 0);\n\n  // prefix sum to compute offsets and total number of slots\n  std::size_t current_sum = 0;\n  for (std::size_t i = 0; i < valid_sizes.size(); ++i) {\n    current_sum += valid_sizes[i];\n    offsets[i + 1] = current_sum;\n  }\n\n  // total number of slots is located at the back of the offsets array\n  auto const total_num_slots = offsets.back();\n\n  // Create a single bulk storage used by all subsets\n  auto set_storage = storage_type{\n    total_num_slots, cuco::cuda_allocator<key_type>{}, cuda::stream_ref{cudaStream_t{nullptr}}};\n  // Initializes the storage with the given sentinel\n  set_storage.initialize(empty_key_sentinel);\n\n  std::vector<ref_type> set_refs;\n\n  // create subsets\n  for (std::size_t i = 0; i < num; ++i) {\n    storage_ref_type storage_ref{valid_sizes[i], set_storage.data() + offsets[i]};\n    set_refs.emplace_back(\n      ref_type{cuco::empty_key<key_type>{empty_key_sentinel}, {}, {}, {}, storage_ref});\n  }\n\n  thrust::device_vector<ref_type> d_set_refs(set_refs);\n\n  // Insert sample data\n  insert<<<1, 128>>>(d_set_refs.data().get());\n  // Find all inserted data\n  find<<<1, 128>>>(d_set_refs.data().get());\n\n  return 0;\n}\n", "output": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_set_ref.cuh>\n\n#include <cuda/std/array>\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/reduce.h>\n#include <thrust/scan.h>\n\n#include <hip/hip_cooperative_groups.h>\n\n#include <algorithm>\n#include <cstddef>\n#include <iostream>\n#include <numeric>\n\n/**\n * @file device_subsets_example.cu\n * @brief Demonstrates how to use one bulk set storage to create multiple subsets and perform\n * individual operations via device-side ref APIs.\n *\n * To optimize memory usage, especially when dealing with expensive data allocation and multiple\n * hashsets, a practical solution involves employing a single bulk storage for generating subsets.\n * This eliminates the need for separate memory allocation and deallocation for each container. This\n * can be achieved by using the lightweight non-owning ref type.\n *\n * @note This example is for demonstration purposes only. It is not intended to show the most\n * performant way to do the example algorithm.\n */\n\nauto constexpr cg_size     = 8;   ///< A CUDA Cooperative Group of 8 threads to handle each subset\nauto constexpr bucket_size = 1;   ///< Number of concurrent slots handled by each thread\nauto constexpr N           = 10;  ///< Number of elements to insert and query\n\nusing key_type = int;  ///< Key type\nusing probing_scheme_type =\n  cuco::linear_probing<cg_size,\n                       cuco::default_hash_function<key_type>>;  ///< Type controls CG granularity\n                                                                ///< and probing scheme (linear\n                                                                ///< probing v.s. double hashing)\n/// Type of bulk allocation storage\nusing storage_type = cuco::bucket_storage<key_type, bucket_size>;\n/// Lightweight non-owning storage ref type\nusing storage_ref_type = typename storage_type::ref_type;\nusing ref_type         = cuco::static_set_ref<key_type,\n                                              cuda::thread_scope_device,\n                                              cuda::std::equal_to<key_type>,\n                                              probing_scheme_type,\n                                              storage_ref_type>;  ///< Set ref type\n\n/// Sample data to insert and query\n__device__ constexpr cuda::std::array<key_type, N> data = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};\n/// Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n/// that they never occur in your input data.\nkey_type constexpr empty_key_sentinel = -1;\n\n/**\n * @brief Inserts sample data into subsets by using cooperative group\n *\n * Each Cooperative Group creates its own subset and inserts `N` sample data.\n *\n * @param set_refs Pointer to the array of subset objects\n */\n__global__ void insert(ref_type* set_refs)\n{\n  namespace cg = cooperative_groups;\n\n  auto const tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n  // Get subset (or CG) index\n  auto const idx = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  auto raw_set_ref    = *(set_refs + idx);\n  auto insert_set_ref = raw_set_ref.rebind_operators(cuco::insert);\n\n  // Insert `N` elemtns into the set with CG insert\n  for (int i = 0; i < N; i++) {\n    insert_set_ref.insert(tile, data[i]);\n  }\n}\n\n/**\n * @brief All inserted data can be found\n *\n * Each Cooperative Group reconstructs its own subset ref based on the storage parameters and\n * verifies all inserted data can be found.\n *\n * @param set_refs Pointer to the array of subset objects\n */\n__global__ void find(ref_type* set_refs)\n{\n  namespace cg = cooperative_groups;\n\n  auto const tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n  auto const idx  = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  auto raw_set_ref  = *(set_refs + idx);\n  auto find_set_ref = raw_set_ref.rebind_operators(cuco::find);\n\n  // Result denoting if any of the inserted data is not found\n  __shared__ int result;\n  if (threadIdx.x == 0) { result = 0; }\n  __syncthreads();\n\n  for (int i = 0; i < N; i++) {\n    // Query the set with inserted data\n    auto const found = find_set_ref.find(tile, data[i]);\n    // Record if the inserted data has been found\n    atomicOr(&result, *found != data[i]);\n  }\n  __syncthreads();\n\n  if (threadIdx.x == 0) {\n    // If the result is still 0, all inserted data are found.\n    if (result == 0) { printf(\"Success! Found all inserted elements.\\n\"); }\n  }\n}\n\nint main()\n{\n  // Number of subsets to be created\n  auto constexpr num = 16;\n  // Each subset may have a different requested size\n  auto constexpr subset_sizes =\n    std::array<std::size_t, num>{20, 20, 20, 20, 30, 30, 30, 30, 40, 40, 40, 40, 50, 50, 50, 50};\n\n  auto valid_sizes = std::vector<std::size_t>();\n  valid_sizes.reserve(num);\n\n  for (size_t i = 0; i < num; ++i) {\n    valid_sizes.emplace_back(static_cast<std::size_t>(\n      cuco::make_valid_extent<probing_scheme_type, storage_ref_type>(subset_sizes[i])));\n  }\n\n  std::vector<std::size_t> offsets(num + 1, 0);\n\n  // prefix sum to compute offsets and total number of slots\n  std::size_t current_sum = 0;\n  for (std::size_t i = 0; i < valid_sizes.size(); ++i) {\n    current_sum += valid_sizes[i];\n    offsets[i + 1] = current_sum;\n  }\n\n  // total number of slots is located at the back of the offsets array\n  auto const total_num_slots = offsets.back();\n\n  // Create a single bulk storage used by all subsets\n  auto set_storage = storage_type{\n    total_num_slots, cuco::cuda_allocator<key_type>{}, cuda::stream_ref{hipStream_t{nullptr}}};\n  // Initializes the storage with the given sentinel\n  set_storage.initialize(empty_key_sentinel);\n\n  std::vector<ref_type> set_refs;\n\n  // create subsets\n  for (std::size_t i = 0; i < num; ++i) {\n    storage_ref_type storage_ref{valid_sizes[i], set_storage.data() + offsets[i]};\n    set_refs.emplace_back(\n      ref_type{cuco::empty_key<key_type>{empty_key_sentinel}, {}, {}, {}, storage_ref});\n  }\n\n  thrust::device_vector<ref_type> d_set_refs(set_refs);\n\n  // Insert sample data\n  insert<<<1, 128>>>(d_set_refs.data().get());\n  // Find all inserted data\n  find<<<1, 128>>>(d_set_refs.data().get());\n\n  return 0;\n}\n", "source": "/kaggle/working/cuCollections/examples/static_set/device_subsets_example.cu"}
{"input": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_set.cuh>\n\n#include <cuda/std/functional>\n\n#include <cooperative_groups.h>\n\n/**\n * @file shared_memory_example.cu\n * @brief Demonstrates usage of the static_set in shared memory.\n */\n\ntemplate <class SetRef>\n__global__ void shmem_set_kernel(typename SetRef::extent_type valid_extent,\n                                 cuco::empty_key<typename SetRef::key_type> empty_key_sentinel)\n{\n  // We first allocate the shared memory storage for the `set`.\n  // The storage is comprised of contiguous buckets of slots,\n  // which allow for vectorized loads.\n  __shared__ typename SetRef::value_type slots[valid_extent.value()];\n\n  // Next, we construct the actual storage object from the raw array.\n  auto storage = SetRef::storage_ref_type(valid_extent, slots);\n  // Now we can instantiate the set from the storage.\n  auto set = SetRef(empty_key_sentinel, {}, {}, {}, storage);\n\n  // The current thread blcok / CTA.\n  auto const block = cooperative_groups::this_thread_block();\n\n  // Initialize the raw storage using all threads in the block.\n  set.initialize(block);\n  // Synchronize the block to make sure initialization has finished.\n  block.sync();\n\n  // The `set` object does not come with any functionality. We first have to transform it into an\n  // object that supports the function we need (in this case `insert`).\n  auto insert_ref = set.rebind_operators(cuco::insert);\n\n  // Each thread inserts its thread id into the set.\n  typename SetRef::key_type const key = block.thread_rank();\n  // Note that if you want to use a cg_size other then one, you have to use the cooperative\n  // overload of this function, i.e., insert(cg, key);\n  insert_ref.insert(key);\n\n  // Synchronize the cta to make sure all insert operations have finished.\n  block.sync();\n  // Next, we want to check if the keys can be found again using the `contains` function. We create\n  // a new non-owning object based on the `insert_ref` that supports `contains` but no longer\n  // supports `insert`.\n  // CAVEAT: concurrent use of `insert_ref` and `contains_ref` is undefined behavior.\n  auto const contains_ref = insert_ref.rebind_operators(cuco::contains);\n\n  // Check if all keys can be found\n  if (not contains_ref.contains(key)) { printf(\"ERROR: Key %d not found\\n\", key); }\n}\n\nint main(void)\n{\n  using Key = int;\n\n  // The \"empty\" sentinel is a reserved value required by our implementation.\n  // Inserting or retrieving this value is UB.\n  cuco::empty_key<Key> constexpr empty_key_sentinel{-1};\n  // Width of vectorized loads during probing.\n  auto constexpr bucket_size = 1;\n  // Cooperative group size\n  auto constexpr cg_size = 1;\n\n  // Minimum number of slots in the set.\n  // Static shared memory requires the size to be a compile time variable.\n  // cuco::extent<int, 1000> is equivalent to `constexpr int capacity = 1000;`\n  using extent_type = cuco::extent<int, 1000>;\n  // Define the probing scheme.\n  using probing_scheme_type = cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>;\n\n  // We define the set type given the parameters above.\n  using set_type = cuco::static_set<Key,\n                                    extent_type,\n                                    cuda::thread_scope_block,\n                                    cuda::std::equal_to<Key>,\n                                    probing_scheme_type,\n                                    cuco::cuda_allocator<Key>,\n                                    cuco::storage<bucket_size>>;\n  // Next, we can derive the non-owning reference type from the set type.\n  // This is the type we use in the kernel to wrap a raw shared memory array as a `static_set`.\n  using set_ref_type = typename set_type::ref_type<>;\n\n  // Cuco imposes a number of non-trivial contraints on the capacity value.\n  // This function will take the requested capacity (1000) and return the next larger\n  // valid extent.\n  auto constexpr valid_extent =\n    cuco::make_valid_extent<probing_scheme_type, cuco::storage<bucket_size>>(extent_type{});\n\n  // Launch the kernel with a single thread block.\n  shmem_set_kernel<set_ref_type><<<1, 128>>>(valid_extent, empty_key_sentinel);\n  cudaDeviceSynchronize();\n}\n", "output": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_set.cuh>\n\n#include <cuda/std/functional>\n\n#include <hip/hip_cooperative_groups.h>\n\n/**\n * @file shared_memory_example.cu\n * @brief Demonstrates usage of the static_set in shared memory.\n */\n\ntemplate <class SetRef>\n__global__ void shmem_set_kernel(typename SetRef::extent_type valid_extent,\n                                 cuco::empty_key<typename SetRef::key_type> empty_key_sentinel)\n{\n  // We first allocate the shared memory storage for the `set`.\n  // The storage is comprised of contiguous buckets of slots,\n  // which allow for vectorized loads.\n  __shared__ typename SetRef::value_type slots[valid_extent.value()];\n\n  // Next, we construct the actual storage object from the raw array.\n  auto storage = SetRef::storage_ref_type(valid_extent, slots);\n  // Now we can instantiate the set from the storage.\n  auto set = SetRef(empty_key_sentinel, {}, {}, {}, storage);\n\n  // The current thread blcok / CTA.\n  auto const block = cooperative_groups::this_thread_block();\n\n  // Initialize the raw storage using all threads in the block.\n  set.initialize(block);\n  // Synchronize the block to make sure initialization has finished.\n  block.sync();\n\n  // The `set` object does not come with any functionality. We first have to transform it into an\n  // object that supports the function we need (in this case `insert`).\n  auto insert_ref = set.rebind_operators(cuco::insert);\n\n  // Each thread inserts its thread id into the set.\n  typename SetRef::key_type const key = block.thread_rank();\n  // Note that if you want to use a cg_size other then one, you have to use the cooperative\n  // overload of this function, i.e., insert(cg, key);\n  insert_ref.insert(key);\n\n  // Synchronize the cta to make sure all insert operations have finished.\n  block.sync();\n  // Next, we want to check if the keys can be found again using the `contains` function. We create\n  // a new non-owning object based on the `insert_ref` that supports `contains` but no longer\n  // supports `insert`.\n  // CAVEAT: concurrent use of `insert_ref` and `contains_ref` is undefined behavior.\n  auto const contains_ref = insert_ref.rebind_operators(cuco::contains);\n\n  // Check if all keys can be found\n  if (not contains_ref.contains(key)) { printf(\"ERROR: Key %d not found\\n\", key); }\n}\n\nint main(void)\n{\n  using Key = int;\n\n  // The \"empty\" sentinel is a reserved value required by our implementation.\n  // Inserting or retrieving this value is UB.\n  cuco::empty_key<Key> constexpr empty_key_sentinel{-1};\n  // Width of vectorized loads during probing.\n  auto constexpr bucket_size = 1;\n  // Cooperative group size\n  auto constexpr cg_size = 1;\n\n  // Minimum number of slots in the set.\n  // Static shared memory requires the size to be a compile time variable.\n  // cuco::extent<int, 1000> is equivalent to `constexpr int capacity = 1000;`\n  using extent_type = cuco::extent<int, 1000>;\n  // Define the probing scheme.\n  using probing_scheme_type = cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>;\n\n  // We define the set type given the parameters above.\n  using set_type = cuco::static_set<Key,\n                                    extent_type,\n                                    cuda::thread_scope_block,\n                                    cuda::std::equal_to<Key>,\n                                    probing_scheme_type,\n                                    cuco::cuda_allocator<Key>,\n                                    cuco::storage<bucket_size>>;\n  // Next, we can derive the non-owning reference type from the set type.\n  // This is the type we use in the kernel to wrap a raw shared memory array as a `static_set`.\n  using set_ref_type = typename set_type::ref_type<>;\n\n  // Cuco imposes a number of non-trivial contraints on the capacity value.\n  // This function will take the requested capacity (1000) and return the next larger\n  // valid extent.\n  auto constexpr valid_extent =\n    cuco::make_valid_extent<probing_scheme_type, cuco::storage<bucket_size>>(extent_type{});\n\n  // Launch the kernel with a single thread block.\n  shmem_set_kernel<set_ref_type><<<1, 128>>>(valid_extent, empty_key_sentinel);\n  hipDeviceSynchronize();\n}\n", "source": "/kaggle/working/cuCollections/examples/static_set/shared_memory_example.cu"}
{"input": "/*\n * Copyright (c) 2022-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cuco/static_set.cuh>\n\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/logical.h>\n#include <thrust/sequence.h>\n\n#include <cooperative_groups.h>\n\n#include <cstddef>\n#include <iostream>\n\n/**\n * @file device_reference_example.cu\n * @brief Demonstrates usage of the static_set device-side APIs.\n *\n * static_set provides a non-owning reference which can be used to interact with\n * the container from within device code.\n */\n\n// insert a set of keys into a hash set using one cooperative group for each task\ntemplate <typename SetRef, typename InputIterator>\n__global__ void custom_cooperative_insert(SetRef set, InputIterator keys, std::size_t n)\n{\n  namespace cg = cooperative_groups;\n\n  constexpr auto cg_size = SetRef::cg_size;\n\n  auto tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n\n  int64_t const loop_stride = gridDim.x * blockDim.x / cg_size;\n  int64_t idx               = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  while (idx < n) {\n    set.insert(tile, *(keys + idx));\n    idx += loop_stride;\n  }\n}\n\ntemplate <typename SetRef, typename InputIterator, typename OutputIterator>\n__global__ void custom_contains(SetRef set, InputIterator keys, std::size_t n, OutputIterator found)\n{\n  namespace cg = cooperative_groups;\n\n  constexpr auto cg_size = SetRef::cg_size;\n\n  auto tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n\n  int64_t const loop_stride = gridDim.x * blockDim.x / cg_size;\n  int64_t idx               = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  while (idx < n) {\n    bool const is_contained = set.contains(tile, *(keys + idx));\n    if (tile.thread_rank() == 0) { found[idx] = is_contained; }\n    idx += loop_stride;\n  }\n}\n\nint main(void)\n{\n  using Key = int;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel = -1;\n\n  // Number of keys to be inserted\n  std::size_t constexpr num_keys = 50'000;\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n  std::size_t const capacity = std::ceil(num_keys / load_factor);\n\n  // Constructs a hash set with at least \"capacity\" slots using -1 as the empty key sentinel.\n  cuco::static_set<Key> set{capacity, cuco::empty_key{empty_key_sentinel}};\n\n  // Create a sequence of keys {0, 1, 2, .., i}\n  thrust::device_vector<Key> keys(num_keys);\n  thrust::sequence(keys.begin(), keys.end(), 0);\n\n  // Insert the first half of the keys into the set\n  set.insert(keys.begin(), keys.begin() + num_keys / 2);\n\n  // Insert the second half of keys using a custom CUDA kernel.\n  custom_cooperative_insert<<<128, 128>>>(\n    set.ref(cuco::insert), keys.begin() + num_keys / 2, num_keys / 2);\n\n  // Storage for result\n  thrust::device_vector<bool> found(num_keys);\n\n  // Check if all keys are now contained in the set. Note that we pass a reference that already has\n  // the `contains` operator.\n  // In general, using two or more reference objects to the same container but with\n  // a different set of operators concurrently is undefined behavior.\n  // This does not apply here since the two kernels do not overlap.\n  custom_contains<<<128, 128>>>(set.ref(cuco::contains), keys.begin(), num_keys, found.begin());\n\n  // Verify that all keys have been found\n  bool const all_keys_found = thrust::all_of(found.begin(), found.end(), cuda::std::identity{});\n\n  if (all_keys_found) { std::cout << \"Success! Found all keys.\\n\"; }\n\n  return 0;\n}\n", "output": "/*\n * Copyright (c) 2022-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <cuco/static_set.cuh>\n\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n#include <thrust/logical.h>\n#include <thrust/sequence.h>\n\n#include <hip/hip_cooperative_groups.h>\n\n#include <cstddef>\n#include <iostream>\n\n/**\n * @file device_reference_example.cu\n * @brief Demonstrates usage of the static_set device-side APIs.\n *\n * static_set provides a non-owning reference which can be used to interact with\n * the container from within device code.\n */\n\n// insert a set of keys into a hash set using one cooperative group for each task\ntemplate <typename SetRef, typename InputIterator>\n__global__ void custom_cooperative_insert(SetRef set, InputIterator keys, std::size_t n)\n{\n  namespace cg = cooperative_groups;\n\n  constexpr auto cg_size = SetRef::cg_size;\n\n  auto tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n\n  int64_t const loop_stride = gridDim.x * blockDim.x / cg_size;\n  int64_t idx               = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  while (idx < n) {\n    set.insert(tile, *(keys + idx));\n    idx += loop_stride;\n  }\n}\n\ntemplate <typename SetRef, typename InputIterator, typename OutputIterator>\n__global__ void custom_contains(SetRef set, InputIterator keys, std::size_t n, OutputIterator found)\n{\n  namespace cg = cooperative_groups;\n\n  constexpr auto cg_size = SetRef::cg_size;\n\n  auto tile = cg::tiled_partition<cg_size, cg::thread_block>(cg::this_thread_block());\n\n  int64_t const loop_stride = gridDim.x * blockDim.x / cg_size;\n  int64_t idx               = (blockDim.x * blockIdx.x + threadIdx.x) / cg_size;\n\n  while (idx < n) {\n    bool const is_contained = set.contains(tile, *(keys + idx));\n    if (tile.thread_rank() == 0) { found[idx] = is_contained; }\n    idx += loop_stride;\n  }\n}\n\nint main(void)\n{\n  using Key = int;\n\n  // Empty slots are represented by reserved \"sentinel\" values. These values should be selected such\n  // that they never occur in your input data.\n  Key constexpr empty_key_sentinel = -1;\n\n  // Number of keys to be inserted\n  std::size_t constexpr num_keys = 50'000;\n\n  // Compute capacity based on a 50% load factor\n  auto constexpr load_factor = 0.5;\n  std::size_t const capacity = std::ceil(num_keys / load_factor);\n\n  // Constructs a hash set with at least \"capacity\" slots using -1 as the empty key sentinel.\n  cuco::static_set<Key> set{capacity, cuco::empty_key{empty_key_sentinel}};\n\n  // Create a sequence of keys {0, 1, 2, .., i}\n  thrust::device_vector<Key> keys(num_keys);\n  thrust::sequence(keys.begin(), keys.end(), 0);\n\n  // Insert the first half of the keys into the set\n  set.insert(keys.begin(), keys.begin() + num_keys / 2);\n\n  // Insert the second half of keys using a custom CUDA kernel.\n  custom_cooperative_insert<<<128, 128>>>(\n    set.ref(cuco::insert), keys.begin() + num_keys / 2, num_keys / 2);\n\n  // Storage for result\n  thrust::device_vector<bool> found(num_keys);\n\n  // Check if all keys are now contained in the set. Note that we pass a reference that already has\n  // the `contains` operator.\n  // In general, using two or more reference objects to the same container but with\n  // a different set of operators concurrently is undefined behavior.\n  // This does not apply here since the two kernels do not overlap.\n  custom_contains<<<128, 128>>>(set.ref(cuco::contains), keys.begin(), num_keys, found.begin());\n\n  // Verify that all keys have been found\n  bool const all_keys_found = thrust::all_of(found.begin(), found.end(), cuda::std::identity{});\n\n  if (all_keys_found) { std::cout << \"Success! Found all keys.\\n\"; }\n\n  return 0;\n}\n", "source": "/kaggle/working/cuCollections/examples/static_set/device_ref_example.cu"}
{"input": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <benchmark_defaults.hpp>\n\n#include <cuco/hash_functions.cuh>\n#include <cuco/utility/key_generator.cuh>\n\n#include <nvbench/nvbench.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n\n#include <cstdint>\n#include <type_traits>\n\n// repeat hash computation n times\nstatic constexpr auto n_repeats = 100;\n\ntemplate <int32_t Words>\nstruct large_key {\n  constexpr __host__ __device__ large_key(int32_t seed) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i) {\n      data_[i] = seed;\n    }\n  }\n\n private:\n  int32_t data_[Words];\n};\n\ntemplate <typename T>\nconstexpr __host__ __device__ void hash_result_aggregate(T& agg, T hash_val)\n{\n  agg += hash_val;\n}\n\ntemplate <>\nconstexpr __host__ __device__ void hash_result_aggregate(cuda::std::array<uint64_t, 2>& agg,\n                                                         cuda::std::array<uint64_t, 2> hash_val)\n{\n  agg[0] += hash_val[0];\n  agg[1] += hash_val[1];\n}\n\ntemplate <>\nconstexpr __host__ __device__ void hash_result_aggregate(cuda::std::array<uint32_t, 4>& agg,\n                                                         cuda::std::array<uint32_t, 4> hash_val)\n{\n  agg[0] += hash_val[0];\n  agg[1] += hash_val[1];\n  agg[2] += hash_val[2];\n  agg[3] += hash_val[3];\n}\n\ntemplate <int32_t BlockSize, typename Hasher, typename OutputIt>\n__global__ void hash_bench_kernel(Hasher hash,\n                                  cuco::detail::index_type n,\n                                  OutputIt out,\n                                  bool materialize_result)\n{\n  cuco::detail::index_type const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  cuco::detail::index_type const loop_stride = gridDim.x * BlockSize;\n  cuco::detail::index_type idx               = gid;\n  typename Hasher::result_type agg           = {};\n\n  while (idx < n) {\n    typename Hasher::argument_type key(idx);\n    for (int32_t i = 0; i < n_repeats; ++i) {  // execute hash func n times\n      hash_result_aggregate(agg, hash(key));\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result) { out[gid] = agg; }\n}\n\n/**\n * @brief A benchmark evaluating performance of various hash functions\n */\ntemplate <typename Hash>\nvoid hash_eval(nvbench::state& state, nvbench::type_list<Hash>)\n{\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n\n  thrust::device_vector<typename Hash::result_type> hash_values((materialize_result) ? num_keys\n                                                                                     : 1);\n\n  state.add_element_count(num_keys);\n\n  state.exec([&](nvbench::launch& launch) {\n    hash_bench_kernel<block_size><<<grid_size, block_size, 0, launch.get_stream()>>>(\n      Hash{}, num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\ntemplate <int32_t BlockSize, typename Hasher, typename InputIt, typename OutputIt>\n__global__ void string_hash_bench_kernel(\n  Hasher hash, InputIt in, cuco::detail::index_type n, OutputIt out, bool materialize_result)\n{\n  cuco::detail::index_type const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  cuco::detail::index_type const loop_stride = gridDim.x * BlockSize;\n  cuco::detail::index_type idx               = gid;\n  typename Hasher::result_type agg           = {};\n\n  while (idx < n) {\n    auto const key = thrust::raw_reference_cast(*(in + idx));\n    for (int32_t i = 0; i < n_repeats; ++i) {  // execute hash func n times\n      hash_result_aggregate(agg, hash.compute_hash(key.data(), key.size()));\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result) { out[gid] = agg; }\n}\n\n/**\n * @brief A benchmark evaluating performance of various hash functions on random strings with\n * variable length and alignment\n */\ntemplate <typename Hash>\nvoid string_hash_eval(nvbench::state& state, nvbench::type_list<Hash>)\n{\n  static_assert(std::is_same_v<typename Hash::argument_type, cuda::std::byte>,\n                \"Argument type must be cuda::std::byte\");\n\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const min_length         = state.get_int64(\"MinLength\");\n  auto const max_length         = state.get_int64(\"MaxLength\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n\n  if (min_length > max_length) {\n    state.skip(\"MinLength > MaxLength\");\n    return;\n  }\n\n  // auto const [keys, storage] = ... (can't capture structured bindings into lambdas in C++17)\n  auto const sequences =\n    cuco::utility::generate_random_byte_sequences(num_keys, min_length, max_length);\n  auto const& keys = sequences.first;\n  // auto const& storage = sequences.second;\n\n  thrust::device_vector<typename Hash::result_type> hash_values((materialize_result) ? num_keys\n                                                                                     : 1);\n\n  state.add_element_count(num_keys);\n  // state.add_global_memory_reads<cuda::std::byte>(storage.size() * n_repeats);\n\n  state.exec([&](nvbench::launch& launch) {\n    string_hash_bench_kernel<block_size><<<grid_size, block_size, 0, launch.get_stream()>>>(\n      Hash{}, keys.begin(), num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\nNVBENCH_BENCH_TYPES(\n  hash_eval,\n  NVBENCH_TYPE_AXES(nvbench::type_list<cuco::murmurhash3_32<nvbench::int32_t>,\n                                       cuco::murmurhash3_32<nvbench::int64_t>,\n                                       cuco::murmurhash3_32<large_key<32>>,  // 32*4bytes\n                                       cuco::xxhash_32<nvbench::int32_t>,\n                                       cuco::xxhash_32<nvbench::int64_t>,\n                                       cuco::xxhash_32<large_key<32>>,\n                                       cuco::xxhash_64<nvbench::int32_t>,\n                                       cuco::xxhash_64<nvbench::int64_t>,\n                                       cuco::xxhash_64<large_key<32>>,\n                                       cuco::murmurhash3_fmix_32<nvbench::int32_t>,\n                                       cuco::murmurhash3_fmix_64<nvbench::int64_t>,\n                                       cuco::murmurhash3_x86_128<nvbench::int32_t>,\n                                       cuco::murmurhash3_x86_128<nvbench::int64_t>,\n                                       cuco::murmurhash3_x86_128<large_key<32>>,\n                                       cuco::murmurhash3_x64_128<nvbench::int32_t>,\n                                       cuco::murmurhash3_x64_128<nvbench::int64_t>,\n                                       cuco::murmurhash3_x64_128<large_key<32>>>))\n  .set_name(\"hash_function_eval\")\n  .set_type_axes_names({\"Hash\"})\n  .add_int64_axis(\"NumInputs\", {cuco::benchmark::defaults::N * 10});\n\nNVBENCH_BENCH_TYPES(\n  string_hash_eval,\n  NVBENCH_TYPE_AXES(nvbench::type_list<cuco::murmurhash3_32<cuda::std::byte>,\n                                       cuco::xxhash_32<cuda::std::byte>,\n                                       cuco::xxhash_64<cuda::std::byte>,\n                                       cuco::murmurhash3_x86_128<cuda::std::byte>,\n                                       cuco::murmurhash3_x64_128<cuda::std::byte>>))\n  .set_name(\"string_hash_function_eval\")\n  .set_type_axes_names({\"Hash\"})\n  .add_int64_axis(\"NumInputs\", {cuco::benchmark::defaults::N / 4})\n  .add_int64_axis(\"MinLength\", {1, 4})\n  .add_int64_axis(\"MaxLength\", {4, 32, 64});\n", "output": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <benchmark_defaults.hpp>\n\n#include <cuco/hash_functions.cuh>\n#include <cuco/utility/key_generator.cuh>\n\n#include <nvbench/nvbench.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n\n#include <cstdint>\n#include <type_traits>\n\n// repeat hash computation n times\nstatic constexpr auto n_repeats = 100;\n\ntemplate <int32_t Words>\nstruct large_key {\n  constexpr __host__ __device__ large_key(int32_t seed) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i) {\n      data_[i] = seed;\n    }\n  }\n\n private:\n  int32_t data_[Words];\n};\n\ntemplate <typename T>\nconstexpr __host__ __device__ void hash_result_aggregate(T& agg, T hash_val)\n{\n  agg += hash_val;\n}\n\ntemplate <>\nconstexpr __host__ __device__ void hash_result_aggregate(cuda::std::array<uint64_t, 2>& agg,\n                                                         cuda::std::array<uint64_t, 2> hash_val)\n{\n  agg[0] += hash_val[0];\n  agg[1] += hash_val[1];\n}\n\ntemplate <>\nconstexpr __host__ __device__ void hash_result_aggregate(cuda::std::array<uint32_t, 4>& agg,\n                                                         cuda::std::array<uint32_t, 4> hash_val)\n{\n  agg[0] += hash_val[0];\n  agg[1] += hash_val[1];\n  agg[2] += hash_val[2];\n  agg[3] += hash_val[3];\n}\n\ntemplate <int32_t BlockSize, typename Hasher, typename OutputIt>\n__global__ void hash_bench_kernel(Hasher hash,\n                                  cuco::detail::index_type n,\n                                  OutputIt out,\n                                  bool materialize_result)\n{\n  cuco::detail::index_type const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  cuco::detail::index_type const loop_stride = gridDim.x * BlockSize;\n  cuco::detail::index_type idx               = gid;\n  typename Hasher::result_type agg           = {};\n\n  while (idx < n) {\n    typename Hasher::argument_type key(idx);\n    for (int32_t i = 0; i < n_repeats; ++i) {  // execute hash func n times\n      hash_result_aggregate(agg, hash(key));\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result) { out[gid] = agg; }\n}\n\n/**\n * @brief A benchmark evaluating performance of various hash functions\n */\ntemplate <typename Hash>\nvoid hash_eval(nvbench::state& state, nvbench::type_list<Hash>)\n{\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n\n  thrust::device_vector<typename Hash::result_type> hash_values((materialize_result) ? num_keys\n                                                                                     : 1);\n\n  state.add_element_count(num_keys);\n\n  state.exec([&](nvbench::launch& launch) {\n    hash_bench_kernel<block_size><<<grid_size, block_size, 0, launch.get_stream()>>>(\n      Hash{}, num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\ntemplate <int32_t BlockSize, typename Hasher, typename InputIt, typename OutputIt>\n__global__ void string_hash_bench_kernel(\n  Hasher hash, InputIt in, cuco::detail::index_type n, OutputIt out, bool materialize_result)\n{\n  cuco::detail::index_type const gid         = BlockSize * blockIdx.x + threadIdx.x;\n  cuco::detail::index_type const loop_stride = gridDim.x * BlockSize;\n  cuco::detail::index_type idx               = gid;\n  typename Hasher::result_type agg           = {};\n\n  while (idx < n) {\n    auto const key = thrust::raw_reference_cast(*(in + idx));\n    for (int32_t i = 0; i < n_repeats; ++i) {  // execute hash func n times\n      hash_result_aggregate(agg, hash.compute_hash(key.data(), key.size()));\n    }\n    idx += loop_stride;\n  }\n\n  if (materialize_result) { out[gid] = agg; }\n}\n\n/**\n * @brief A benchmark evaluating performance of various hash functions on random strings with\n * variable length and alignment\n */\ntemplate <typename Hash>\nvoid string_hash_eval(nvbench::state& state, nvbench::type_list<Hash>)\n{\n  static_assert(std::is_same_v<typename Hash::argument_type, cuda::std::byte>,\n                \"Argument type must be cuda::std::byte\");\n\n  bool const materialize_result = false;\n  constexpr auto block_size     = 128;\n  auto const num_keys           = state.get_int64(\"NumInputs\");\n  auto const min_length         = state.get_int64(\"MinLength\");\n  auto const max_length         = state.get_int64(\"MaxLength\");\n  auto const grid_size          = (num_keys + block_size * 16 - 1) / block_size * 16;\n\n  if (min_length > max_length) {\n    state.skip(\"MinLength > MaxLength\");\n    return;\n  }\n\n  // auto const [keys, storage] = ... (can't capture structured bindings into lambdas in C++17)\n  auto const sequences =\n    cuco::utility::generate_random_byte_sequences(num_keys, min_length, max_length);\n  auto const& keys = sequences.first;\n  // auto const& storage = sequences.second;\n\n  thrust::device_vector<typename Hash::result_type> hash_values((materialize_result) ? num_keys\n                                                                                     : 1);\n\n  state.add_element_count(num_keys);\n  // state.add_global_memory_reads<cuda::std::byte>(storage.size() * n_repeats);\n\n  state.exec([&](nvbench::launch& launch) {\n    string_hash_bench_kernel<block_size><<<grid_size, block_size, 0, launch.get_stream()>>>(\n      Hash{}, keys.begin(), num_keys, hash_values.begin(), materialize_result);\n  });\n}\n\nNVBENCH_BENCH_TYPES(\n  hash_eval,\n  NVBENCH_TYPE_AXES(nvbench::type_list<cuco::murmurhash3_32<nvbench::int32_t>,\n                                       cuco::murmurhash3_32<nvbench::int64_t>,\n                                       cuco::murmurhash3_32<large_key<32>>,  // 32*4bytes\n                                       cuco::xxhash_32<nvbench::int32_t>,\n                                       cuco::xxhash_32<nvbench::int64_t>,\n                                       cuco::xxhash_32<large_key<32>>,\n                                       cuco::xxhash_64<nvbench::int32_t>,\n                                       cuco::xxhash_64<nvbench::int64_t>,\n                                       cuco::xxhash_64<large_key<32>>,\n                                       cuco::murmurhash3_fmix_32<nvbench::int32_t>,\n                                       cuco::murmurhash3_fmix_64<nvbench::int64_t>,\n                                       cuco::murmurhash3_x86_128<nvbench::int32_t>,\n                                       cuco::murmurhash3_x86_128<nvbench::int64_t>,\n                                       cuco::murmurhash3_x86_128<large_key<32>>,\n                                       cuco::murmurhash3_x64_128<nvbench::int32_t>,\n                                       cuco::murmurhash3_x64_128<nvbench::int64_t>,\n                                       cuco::murmurhash3_x64_128<large_key<32>>>))\n  .set_name(\"hash_function_eval\")\n  .set_type_axes_names({\"Hash\"})\n  .add_int64_axis(\"NumInputs\", {cuco::benchmark::defaults::N * 10});\n\nNVBENCH_BENCH_TYPES(\n  string_hash_eval,\n  NVBENCH_TYPE_AXES(nvbench::type_list<cuco::murmurhash3_32<cuda::std::byte>,\n                                       cuco::xxhash_32<cuda::std::byte>,\n                                       cuco::xxhash_64<cuda::std::byte>,\n                                       cuco::murmurhash3_x86_128<cuda::std::byte>,\n                                       cuco::murmurhash3_x64_128<cuda::std::byte>>))\n  .set_name(\"string_hash_function_eval\")\n  .set_type_axes_names({\"Hash\"})\n  .add_int64_axis(\"NumInputs\", {cuco::benchmark::defaults::N / 4})\n  .add_int64_axis(\"MinLength\", {1, 4})\n  .add_int64_axis(\"MaxLength\", {4, 32, 64});\n", "source": "/kaggle/working/cuCollections/benchmarks/hash_function/hash_function_bench.cu"}
{"input": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/host_vector.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void find_next_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.find_next(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset find next set test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{400};\n\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n  }\n\n  thrust::device_vector<size_type> device_result(num_elements);\n  auto ref = bv.ref();\n  find_next_kernel<<<1, 1024>>>(ref, num_elements, device_result.data());\n\n  thrust::host_vector<size_type> host_result = device_result;\n  size_type num_matches                      = 0;\n\n  size_type next_set_pos = -1lu;\n  do {\n    next_set_pos++;\n  } while (next_set_pos < num_elements and !modulo_bitgen(next_set_pos));\n\n  for (size_type key = 0; key < num_elements; key++) {\n    num_matches += host_result[key] == next_set_pos;\n\n    if (key == next_set_pos) {\n      do {\n        next_set_pos++;\n      } while (next_set_pos < num_elements and !modulo_bitgen(next_set_pos));\n    }\n  }\n  REQUIRE(num_matches == num_elements);\n}\n", "output": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/host_vector.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void find_next_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.find_next(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset find next set test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{400};\n\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n  }\n\n  thrust::device_vector<size_type> device_result(num_elements);\n  auto ref = bv.ref();\n  find_next_kernel<<<1, 1024>>>(ref, num_elements, device_result.data());\n\n  thrust::host_vector<size_type> host_result = device_result;\n  size_type num_matches                      = 0;\n\n  size_type next_set_pos = -1lu;\n  do {\n    next_set_pos++;\n  } while (next_set_pos < num_elements and !modulo_bitgen(next_set_pos));\n\n  for (size_type key = 0; key < num_elements; key++) {\n    num_matches += host_result[key] == next_set_pos;\n\n    if (key == next_set_pos) {\n      do {\n        next_set_pos++;\n      } while (next_set_pos < num_elements and !modulo_bitgen(next_set_pos));\n    }\n  }\n  REQUIRE(num_matches == num_elements);\n}\n", "source": "/kaggle/working/cuCollections/tests/dynamic_bitset/find_next_test.cu"}
{"input": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void test_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.test(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset get test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{400};\n\n  size_type num_set_ref = 0;\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n    num_set_ref += modulo_bitgen(i);\n  }\n\n  // Host-bulk test\n  thrust::device_vector<size_type> keys(num_elements);\n  thrust::sequence(keys.begin(), keys.end(), 0);\n\n  thrust::device_vector<size_type> test_result(num_elements);\n  thrust::fill(test_result.begin(), test_result.end(), 0);\n\n  bv.test(keys.begin(), keys.end(), test_result.begin());\n\n  size_type num_set = thrust::reduce(thrust::device, test_result.begin(), test_result.end(), 0);\n  REQUIRE(num_set == num_set_ref);\n\n  // Device-ref test\n  auto ref = bv.ref();\n  thrust::fill(test_result.begin(), test_result.end(), 0);\n  test_kernel<<<1, 1024>>>(ref, num_elements, test_result.data());\n\n  num_set = thrust::reduce(thrust::device, test_result.begin(), test_result.end(), 0);\n  REQUIRE(num_set == num_set_ref);\n}\n", "output": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void test_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.test(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset get test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{400};\n\n  size_type num_set_ref = 0;\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n    num_set_ref += modulo_bitgen(i);\n  }\n\n  // Host-bulk test\n  thrust::device_vector<size_type> keys(num_elements);\n  thrust::sequence(keys.begin(), keys.end(), 0);\n\n  thrust::device_vector<size_type> test_result(num_elements);\n  thrust::fill(test_result.begin(), test_result.end(), 0);\n\n  bv.test(keys.begin(), keys.end(), test_result.begin());\n\n  size_type num_set = thrust::reduce(thrust::device, test_result.begin(), test_result.end(), 0);\n  REQUIRE(num_set == num_set_ref);\n\n  // Device-ref test\n  auto ref = bv.ref();\n  thrust::fill(test_result.begin(), test_result.end(), 0);\n  test_kernel<<<1, 1024>>>(ref, num_elements, test_result.data());\n\n  num_set = thrust::reduce(thrust::device, test_result.begin(), test_result.end(), 0);\n  REQUIRE(num_set == num_set_ref);\n}\n", "source": "/kaggle/working/cuCollections/tests/dynamic_bitset/get_test.cu"}
{"input": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void select_false_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.select_false(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset select test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{4000};\n\n  size_type num_set = 0;\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n    num_set += modulo_bitgen(i);\n  }\n\n  // Check select\n  {\n    thrust::device_vector<size_type> keys(num_set);\n    thrust::sequence(keys.begin(), keys.end(), 0);\n\n    thrust::device_vector<size_type> d_selects(num_set);\n\n    bv.select(keys.begin(), keys.end(), d_selects.begin());\n\n    thrust::host_vector<size_type> h_selects = d_selects;\n\n    size_type num_matches = 0;\n    size_type cur_set_pos = -1lu;\n    for (size_type i = 0; i < num_set; i++) {\n      do {\n        cur_set_pos++;\n      } while (cur_set_pos < num_elements and !modulo_bitgen(cur_set_pos));\n\n      num_matches += cur_set_pos == h_selects[i];\n    }\n    REQUIRE(num_matches == num_set);\n  }\n\n  // Check select_false\n  {\n    size_type num_not_set = num_elements - num_set;\n\n    auto ref = bv.ref();\n    thrust::device_vector<size_type> device_result(num_not_set);\n    select_false_kernel<<<1, 1024>>>(ref, num_not_set, device_result.data());\n    thrust::host_vector<size_type> host_result = device_result;\n\n    size_type num_matches     = 0;\n    size_type cur_not_set_pos = -1lu;\n    for (size_type i = 0; i < num_not_set; i++) {\n      do {\n        cur_not_set_pos++;\n      } while (cur_not_set_pos < num_elements and modulo_bitgen(cur_not_set_pos));\n\n      num_matches += cur_not_set_pos == host_result[i];\n    }\n    REQUIRE(num_matches == num_not_set);\n  }\n}\n", "output": "/*\n * Copyright (c) 2023-2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/trie/dynamic_bitset/dynamic_bitset.cuh>\n\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <class BitsetRef, typename size_type, typename OutputIt>\n__global__ void select_false_kernel(BitsetRef ref, size_type num_elements, OutputIt output)\n{\n  cuco::detail::index_type index  = blockIdx.x * blockDim.x + threadIdx.x;\n  cuco::detail::index_type stride = gridDim.x * blockDim.x;\n  while (index < num_elements) {\n    output[index] = ref.select_false(index);\n    index += stride;\n  }\n}\n\nusing cuco::test::modulo_bitgen;\n\nTEST_CASE(\"dynamic_bitset select test\", \"\")\n{\n  cuco::experimental::detail::dynamic_bitset bv;\n\n  using size_type = std::size_t;\n  constexpr size_type num_elements{4000};\n\n  size_type num_set = 0;\n  for (size_type i = 0; i < num_elements; i++) {\n    bv.push_back(modulo_bitgen(i));\n    num_set += modulo_bitgen(i);\n  }\n\n  // Check select\n  {\n    thrust::device_vector<size_type> keys(num_set);\n    thrust::sequence(keys.begin(), keys.end(), 0);\n\n    thrust::device_vector<size_type> d_selects(num_set);\n\n    bv.select(keys.begin(), keys.end(), d_selects.begin());\n\n    thrust::host_vector<size_type> h_selects = d_selects;\n\n    size_type num_matches = 0;\n    size_type cur_set_pos = -1lu;\n    for (size_type i = 0; i < num_set; i++) {\n      do {\n        cur_set_pos++;\n      } while (cur_set_pos < num_elements and !modulo_bitgen(cur_set_pos));\n\n      num_matches += cur_set_pos == h_selects[i];\n    }\n    REQUIRE(num_matches == num_set);\n  }\n\n  // Check select_false\n  {\n    size_type num_not_set = num_elements - num_set;\n\n    auto ref = bv.ref();\n    thrust::device_vector<size_type> device_result(num_not_set);\n    select_false_kernel<<<1, 1024>>>(ref, num_not_set, device_result.data());\n    thrust::host_vector<size_type> host_result = device_result;\n\n    size_type num_matches     = 0;\n    size_type cur_not_set_pos = -1lu;\n    for (size_type i = 0; i < num_not_set; i++) {\n      do {\n        cur_not_set_pos++;\n      } while (cur_not_set_pos < num_elements and modulo_bitgen(cur_not_set_pos));\n\n      num_matches += cur_not_set_pos == host_result[i];\n    }\n    REQUIRE(num_matches == num_not_set);\n  }\n}\n", "source": "/kaggle/working/cuCollections/tests/dynamic_bitset/select_test.cu"}
{"input": "/*\n * Copyright (c) 2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/hash_functions.cuh>\n#include <cuco/hyperloglog.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n\n#include <cmath>\n#include <cstdint>\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void estimate_kernel(cuco::sketch_size_kb sketch_size_kb,\n                                InputIt in,\n                                size_t n,\n                                OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  auto const block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0) {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads()) {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    auto const estimate = estimator.estimate(block);\n    if (block.thread_rank() == 0) { *out = estimate; }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"hyperloglog: device ref\",\n                       \"\",\n                       ((typename T, typename Hash), T, Hash),\n                       (int32_t, cuco::xxhash_64<int32_t>),\n                       (int64_t, cuco::xxhash_64<int64_t>),\n                       (__int128_t, cuco::xxhash_64<__int128_t>))\n{\n  using estimator_type = cuco::hyperloglog<T, cuda::thread_scope_device, Hash>;\n\n  auto num_items_pow2 = GENERATE(25, 26, 28);\n  auto hll_precision  = GENERATE(8, 10, 12, 13);\n  auto sketch_size_kb = 4 * (1ull << hll_precision) / 1024;\n  INFO(\"hll_precision=\" << hll_precision);\n  INFO(\"sketch_size_kb=\" << sketch_size_kb);\n  INFO(\"num_items=2^\" << num_items_pow2);\n  auto num_items = 1ull << num_items_pow2;\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), 0);\n\n  // Initialize the estimator\n  estimator_type estimator{cuco::sketch_size_kb(sketch_size_kb)};\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  auto const host_estimate = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  estimate_kernel<typename estimator_type::template ref_type<cuda::thread_scope_block>>\n    <<<1, 512, estimator.sketch_bytes()>>>(\n      cuco::sketch_size_kb(sketch_size_kb), items.begin(), num_items, device_estimate.begin());\n\n  REQUIRE(device_estimate[0] == host_estimate);\n}\n", "output": "/*\n * Copyright (c) 2024, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/hash_functions.cuh>\n#include <cuco/hyperloglog.cuh>\n\n#include <cuda/std/cstddef>\n#include <thrust/device_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n\n#include <cmath>\n#include <cstdint>\n\ntemplate <typename Ref, typename InputIt, typename OutputIt>\n__global__ void estimate_kernel(cuco::sketch_size_kb sketch_size_kb,\n                                InputIt in,\n                                size_t n,\n                                OutputIt out)\n{\n  extern __shared__ cuda::std::byte local_sketch[];\n\n  auto const block = cooperative_groups::this_thread_block();\n\n  // only a single block computes the estimate\n  if (block.group_index().x == 0) {\n    Ref estimator(cuda::std::span(local_sketch, Ref::sketch_bytes(sketch_size_kb)));\n\n    estimator.clear(block);\n    block.sync();\n\n    for (int i = block.thread_rank(); i < n; i += block.num_threads()) {\n      estimator.add(*(in + i));\n    }\n    block.sync();\n    auto const estimate = estimator.estimate(block);\n    if (block.thread_rank() == 0) { *out = estimate; }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"hyperloglog: device ref\",\n                       \"\",\n                       ((typename T, typename Hash), T, Hash),\n                       (int32_t, cuco::xxhash_64<int32_t>),\n                       (int64_t, cuco::xxhash_64<int64_t>),\n                       (__int128_t, cuco::xxhash_64<__int128_t>))\n{\n  using estimator_type = cuco::hyperloglog<T, cuda::thread_scope_device, Hash>;\n\n  auto num_items_pow2 = GENERATE(25, 26, 28);\n  auto hll_precision  = GENERATE(8, 10, 12, 13);\n  auto sketch_size_kb = 4 * (1ull << hll_precision) / 1024;\n  INFO(\"hll_precision=\" << hll_precision);\n  INFO(\"sketch_size_kb=\" << sketch_size_kb);\n  INFO(\"num_items=2^\" << num_items_pow2);\n  auto num_items = 1ull << num_items_pow2;\n\n  thrust::device_vector<T> items(num_items);\n\n  // Generate `num_items` distinct items\n  thrust::sequence(items.begin(), items.end(), 0);\n\n  // Initialize the estimator\n  estimator_type estimator{cuco::sketch_size_kb(sketch_size_kb)};\n\n  // Add all items to the estimator\n  estimator.add(items.begin(), items.end());\n\n  auto const host_estimate = estimator.estimate();\n\n  thrust::device_vector<std::size_t> device_estimate(1);\n  estimate_kernel<typename estimator_type::template ref_type<cuda::thread_scope_block>>\n    <<<1, 512, estimator.sketch_bytes()>>>(\n      cuco::sketch_size_kb(sketch_size_kb), items.begin(), num_items, device_estimate.begin());\n\n  REQUIRE(device_estimate[0] == host_estimate);\n}\n", "source": "/kaggle/working/cuCollections/tests/hyperloglog/device_ref_test.cu"}
{"input": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_multimap.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_multimap retrieve_if\",\n                       \"\",\n                       ((typename Key, typename Value), Key, Value),\n                       (int32_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_multimap<Key, Value>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}, cuco::empty_value<Value>{-1}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n  auto pairs_begin =\n    cuda::make_transform_iterator(cuda::make_counting_iterator<Key>(1),\n                                  cuda::proclaim_return_type<cuco::pair<Key, Value>>(\n                                    [] __device__(Key i) { return cuco::pair<Key, Value>{i, i}; }));\n\n  container.insert(pairs_begin, pairs_begin + num_keys);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_pairs.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_pairs.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_pairs.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n}", "output": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_multimap.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_multimap retrieve_if\",\n                       \"\",\n                       ((typename Key, typename Value), Key, Value),\n                       (int32_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_multimap<Key, Value>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}, cuco::empty_value<Value>{-1}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n  auto pairs_begin =\n    cuda::make_transform_iterator(cuda::make_counting_iterator<Key>(1),\n                                  cuda::proclaim_return_type<cuco::pair<Key, Value>>(\n                                    [] __device__(Key i) { return cuco::pair<Key, Value>{i, i}; }));\n\n  container.insert(pairs_begin, pairs_begin + num_keys);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_pairs.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_pairs.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<typename container_type::value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_pairs.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n}", "source": "/kaggle/working/cuCollections/tests/static_multimap/retrieve_if_test.cu"}
{"input": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/utility/cuda.hpp>\n#include <cuco/extent.cuh>\n#include <cuco/hash_functions.cuh>\n#include <cuco/probing_scheme.cuh>\n\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n\n#include <cooperative_groups.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <cstddef>\n#include <cstdint>\n\ntemplate <int32_t BucketSize, class ProbingScheme, class Key, class Extent, class OutputIt>\n__global__ void generate_scalar_probing_sequence(Key key,\n                                                 Extent upper_bound,\n                                                 size_t seq_length,\n                                                 OutputIt out_seq)\n{\n  auto constexpr cg_size = ProbingScheme::cg_size;\n  static_assert(cg_size == 1, \"Invalid CG size\");\n\n  auto const tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  auto probing_scheme = ProbingScheme{};\n\n  if (tid == 0) {\n    auto iter = probing_scheme.template make_iterator<BucketSize>(key, upper_bound);\n\n    for (size_t i = 0; i < seq_length; ++i) {\n      out_seq[i] = *iter;\n      ++iter;\n    }\n  }\n}\n\ntemplate <int32_t BucketSize, class ProbingScheme, class Key, class Extent, class OutputIt>\n__global__ void generate_cg_probing_sequence(Key key,\n                                             Extent upper_bound,\n                                             size_t seq_length,\n                                             OutputIt out_seq)\n{\n  auto constexpr cg_size = ProbingScheme::cg_size;\n\n  auto const tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  auto probing_scheme = ProbingScheme{};\n\n  if (tid < cg_size) {\n    auto const tile =\n      cooperative_groups::tiled_partition<cg_size, cooperative_groups::thread_block>(\n        cooperative_groups::this_thread_block());\n\n    auto iter = probing_scheme.template make_iterator<BucketSize>(tile, key, upper_bound);\n\n    for (size_t i = tile.thread_rank(); i < seq_length; ++i) {\n      out_seq[i] = *iter;\n      ++iter;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\n  \"utility probing_scheme tests\",\n  \"\",\n  ((typename Key, cuco::test::probe_sequence Probe, int32_t BucketSize), Key, Probe, BucketSize),\n  (int32_t, cuco::test::probe_sequence::double_hashing, 1),\n  (int32_t, cuco::test::probe_sequence::double_hashing, 2),\n  (int64_t, cuco::test::probe_sequence::double_hashing, 1),\n  (int64_t, cuco::test::probe_sequence::double_hashing, 2),\n  (int32_t, cuco::test::probe_sequence::linear_probing, 1),\n  (int32_t, cuco::test::probe_sequence::linear_probing, 2),\n  (int64_t, cuco::test::probe_sequence::linear_probing, 1),\n  (int64_t, cuco::test::probe_sequence::linear_probing, 2))\n{\n  using probing_scheme_t = cuco::linear_probing<1, cuco::default_hash_function<int>>;\n  auto const upper_bound = cuco::make_valid_extent<probing_scheme_t, cuco::storage<BucketSize>>(\n    cuco::extent<std::size_t>{10});\n  constexpr size_t seq_length{8};\n  constexpr Key key{42};\n\n  using probe = std::conditional_t<Probe == cuco::test::probe_sequence::linear_probing,\n                                   cuco::linear_probing<1, cuco::default_hash_function<Key>>,\n                                   cuco::double_hashing<1, cuco::default_hash_function<Key>>>;\n\n  thrust::device_vector<size_t> scalar_seq(seq_length);\n  generate_scalar_probing_sequence<BucketSize, probe>\n    <<<1, 1>>>(key, upper_bound, seq_length, scalar_seq.begin());\n  thrust::device_vector<size_t> cg_seq(seq_length);\n  generate_cg_probing_sequence<BucketSize, probe>\n    <<<1, 1>>>(key, upper_bound, seq_length, cg_seq.begin());\n\n  REQUIRE(cuco::test::equal(\n    scalar_seq.begin(), scalar_seq.end(), cg_seq.begin(), cuda::std::equal_to<std::size_t>{}));\n}\n", "output": "/*\n * Copyright (c) 2024-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/utility/cuda.hpp>\n#include <cuco/extent.cuh>\n#include <cuco/hash_functions.cuh>\n#include <cuco/probing_scheme.cuh>\n\n#include <cuda/std/functional>\n#include <thrust/device_vector.h>\n\n#include <hip/hip_cooperative_groups.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <cstddef>\n#include <cstdint>\n\ntemplate <int32_t BucketSize, class ProbingScheme, class Key, class Extent, class OutputIt>\n__global__ void generate_scalar_probing_sequence(Key key,\n                                                 Extent upper_bound,\n                                                 size_t seq_length,\n                                                 OutputIt out_seq)\n{\n  auto constexpr cg_size = ProbingScheme::cg_size;\n  static_assert(cg_size == 1, \"Invalid CG size\");\n\n  auto const tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  auto probing_scheme = ProbingScheme{};\n\n  if (tid == 0) {\n    auto iter = probing_scheme.template make_iterator<BucketSize>(key, upper_bound);\n\n    for (size_t i = 0; i < seq_length; ++i) {\n      out_seq[i] = *iter;\n      ++iter;\n    }\n  }\n}\n\ntemplate <int32_t BucketSize, class ProbingScheme, class Key, class Extent, class OutputIt>\n__global__ void generate_cg_probing_sequence(Key key,\n                                             Extent upper_bound,\n                                             size_t seq_length,\n                                             OutputIt out_seq)\n{\n  auto constexpr cg_size = ProbingScheme::cg_size;\n\n  auto const tid      = blockIdx.x * blockDim.x + threadIdx.x;\n  auto probing_scheme = ProbingScheme{};\n\n  if (tid < cg_size) {\n    auto const tile =\n      cooperative_groups::tiled_partition<cg_size, cooperative_groups::thread_block>(\n        cooperative_groups::this_thread_block());\n\n    auto iter = probing_scheme.template make_iterator<BucketSize>(tile, key, upper_bound);\n\n    for (size_t i = tile.thread_rank(); i < seq_length; ++i) {\n      out_seq[i] = *iter;\n      ++iter;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\n  \"utility probing_scheme tests\",\n  \"\",\n  ((typename Key, cuco::test::probe_sequence Probe, int32_t BucketSize), Key, Probe, BucketSize),\n  (int32_t, cuco::test::probe_sequence::double_hashing, 1),\n  (int32_t, cuco::test::probe_sequence::double_hashing, 2),\n  (int64_t, cuco::test::probe_sequence::double_hashing, 1),\n  (int64_t, cuco::test::probe_sequence::double_hashing, 2),\n  (int32_t, cuco::test::probe_sequence::linear_probing, 1),\n  (int32_t, cuco::test::probe_sequence::linear_probing, 2),\n  (int64_t, cuco::test::probe_sequence::linear_probing, 1),\n  (int64_t, cuco::test::probe_sequence::linear_probing, 2))\n{\n  using probing_scheme_t = cuco::linear_probing<1, cuco::default_hash_function<int>>;\n  auto const upper_bound = cuco::make_valid_extent<probing_scheme_t, cuco::storage<BucketSize>>(\n    cuco::extent<std::size_t>{10});\n  constexpr size_t seq_length{8};\n  constexpr Key key{42};\n\n  using probe = std::conditional_t<Probe == cuco::test::probe_sequence::linear_probing,\n                                   cuco::linear_probing<1, cuco::default_hash_function<Key>>,\n                                   cuco::double_hashing<1, cuco::default_hash_function<Key>>>;\n\n  thrust::device_vector<size_t> scalar_seq(seq_length);\n  generate_scalar_probing_sequence<BucketSize, probe>\n    <<<1, 1>>>(key, upper_bound, seq_length, scalar_seq.begin());\n  thrust::device_vector<size_t> cg_seq(seq_length);\n  generate_cg_probing_sequence<BucketSize, probe>\n    <<<1, 1>>>(key, upper_bound, seq_length, cg_seq.begin());\n\n  REQUIRE(cuco::test::equal(\n    scalar_seq.begin(), scalar_seq.end(), cg_seq.begin(), cuda::std::equal_to<std::size_t>{}));\n}\n", "source": "/kaggle/working/cuCollections/tests/utility/probing_scheme_test.cu"}
{"input": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/hash_functions.cuh>\n\n#include <cuda/std/cstddef>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <thrust/device_vector.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <int32_t Words>\nstruct large_key {\n  constexpr __host__ __device__ large_key(int32_t value) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i) {\n      data_[i] = value;\n    }\n  }\n\n private:\n  int32_t data_[Words];\n};\n\ntemplate <typename Hash, typename... HashConstructorArgs>\nstatic __host__ __device__ bool check_hash_result(\n  typename Hash::argument_type const& key,\n  typename Hash::result_type expected,\n  HashConstructorArgs&&... hash_constructor_args) noexcept\n{\n  Hash h(cuda::std::forward<HashConstructorArgs>(hash_constructor_args)...);\n  return (h(key) == expected);\n}\n\ntemplate <typename OutputIter>\n__global__ void check_identity_hash_result_kernel(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::identity_hash<signed char>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<signed char>>(\n    cuda::std::numeric_limits<signed char>::max(), cuda::std::numeric_limits<signed char>::max());\n\n  result[i++] = check_hash_result<cuco::identity_hash<int32_t>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<int32_t>>(\n    cuda::std::numeric_limits<int32_t>::max(), cuda::std::numeric_limits<int32_t>::max());\n\n  result[i++] = check_hash_result<cuco::identity_hash<int64_t>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<int64_t>>(\n    cuda::std::numeric_limits<int64_t>::max(), cuda::std::numeric_limits<int64_t>::max());\n}\n\nTEST_CASE(\"utility cuco::identity_hash test\", \"\")\n{\n  SECTION(\"Check if host-generated hash values match the identity function.\")\n  {\n    CHECK(check_hash_result<cuco::identity_hash<signed char>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<signed char>>(\n      cuda::std::numeric_limits<signed char>::max(),\n      cuda::std::numeric_limits<signed char>::max()));\n\n    CHECK(check_hash_result<cuco::identity_hash<int32_t>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<int32_t>>(\n      cuda::std::numeric_limits<int32_t>::max(), cuda::std::numeric_limits<int32_t>::max()));\n\n    CHECK(check_hash_result<cuco::identity_hash<int64_t>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<int64_t>>(\n      cuda::std::numeric_limits<int64_t>::max(), cuda::std::numeric_limits<int64_t>::max()));\n  }\n  SECTION(\"Check if device-generated hash values match the identity function.\")\n  {\n    thrust::device_vector<bool> result(7, true);\n\n    check_identity_hash_result_kernel<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_hash_result_kernel_64(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(0, 16804241149081757544, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(42, 765293966243412708, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(0, 9486749600008296231, 42);\n\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(0, 4246796580750024372, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(0, 3614696996920510707, 42);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(42, 15516826743637085169, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(123456789, 9462334144942111946, 0);\n\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(0, 3803688792395291579, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(0, 13194218611613725804, 42);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(42, 13066772586158965587, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(123456789, 14662639848940634189, 0);\n\n#if defined(CUCO_HAS_INT128)\n  result[i++] = check_hash_result<cuco::xxhash_64<__int128>>(123456789, 7986913354431084250, 0);\n#endif\n\n  result[i++] =\n    check_hash_result<cuco::xxhash_64<large_key<32>>>(123456789, 2031761887105658523, 0);\n}\n\nTEST_CASE(\"utility cuco::xxhash_64 test\", \"\")\n{\n  // Reference hash values were computed using https://github.com/Cyan4973/xxHash\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(0, 16804241149081757544, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(42, 765293966243412708, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(0, 9486749600008296231, 42));\n\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(0, 4246796580750024372, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(0, 3614696996920510707, 42));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(42, 15516826743637085169, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(123456789, 9462334144942111946, 0));\n\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(0, 3803688792395291579, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(0, 13194218611613725804, 42));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(42, 13066772586158965587, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(123456789, 14662639848940634189, 0));\n\n#if defined(CUCO_HAS_INT128)\n    CHECK(check_hash_result<cuco::xxhash_64<__int128>>(123456789, 7986913354431084250, 0));\n#endif\n\n    // 32*4=128-byte key to test the pipelined outermost hashing loop\n    CHECK(check_hash_result<cuco::xxhash_64<large_key<32>>>(123456789, 2031761887105658523, 0));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(10);\n\n    check_hash_result_kernel_64<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_hash_result_kernel_32(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(0, 3479547966, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(42, 3774771295, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(0, 2099223482, 42);\n\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(0, 148298089, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(0, 2132181312, 42);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(42, 1161967057, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(123456789, 2987034094, 0);\n\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(0, 3736311059, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(0, 1076387279, 42);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(42, 2332451213, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(123456789, 1561711919, 0);\n\n#if defined(CUCO_HAS_INT128)\n  result[i++] = check_hash_result<cuco::xxhash_32<__int128>>(123456789, 1846633701, 0);\n#endif\n\n  result[i++] = check_hash_result<cuco::xxhash_32<large_key<32>>>(123456789, 3715432378, 0);\n}\n\nTEST_CASE(\"utility cuco::xxhash_32 test\", \"\")\n{\n  // Reference hash values were computed using https://github.com/Cyan4973/xxHash\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(0, 3479547966, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(42, 3774771295, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(0, 2099223482, 42));\n\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(0, 148298089, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(0, 2132181312, 42));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(42, 1161967057, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(123456789, 2987034094, 0));\n\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(0, 3736311059, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(0, 1076387279, 42));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(42, 2332451213, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(123456789, 1561711919, 0));\n\n#if defined(CUCO_HAS_INT128)\n    CHECK(check_hash_result<cuco::xxhash_32<__int128>>(123456789, 1846633701, 0));\n#endif\n\n    // 32*4=128-byte key to test the pipelined outermost hashing loop\n    CHECK(check_hash_result<cuco::xxhash_32<large_key<32>>>(123456789, 3715432378, 0));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(20, true);\n\n    check_hash_result_kernel_32<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"utility hasher compute_hash tests\",\n                       \"\",\n                       ((typename Hash), Hash),\n                       (cuco::murmurhash3_32<char>),\n                       (cuco::murmurhash3_32<int32_t>),\n                       (cuco::xxhash_32<char>),\n                       (cuco::xxhash_32<int32_t>),\n                       (cuco::xxhash_64<char>),\n                       (cuco::xxhash_64<int32_t>))\n{\n  using key_type = typename Hash::argument_type;\n\n  Hash hash;\n  key_type key = 42;\n\n  SECTION(\"Identical keys with static and dynamic key size should have the same hash value.\")\n  {\n    CHECK(hash(key) ==\n          hash.compute_hash(reinterpret_cast<cuda::std::byte const*>(&key), sizeof(key_type)));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_murmurhash3_128_result_kernel(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    0, {14961230494313510588ull, 6383328099726337777ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    9, {1779292183511753683ull, 16298496441448380334ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    42, {2913627637088662735ull, 16344193523890567190ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    42, {2248879576374326886ull, 18006515275339376488ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 2>>, uint64_t>(\n      {2, 2}, {12221386834995143465ull, 6690950894782946573ull}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 3>>, uint64_t>(\n      {1, 4, 9}, {299140022350411792ull, 9891903873182035274ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {4333511168876981289ull, 4659486988434316416ull}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3302412811061286680ull, 7070355726356610672ull},\n      1024);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 2>>, uint64_t>(\n      {2, 2}, {8554944597931919519ull, 14938998000509429729ull}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 3>>, uint64_t>(\n      {1, 4, 9}, {13442629947720186435ull, 7061727494178573325ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {8786399719555989948ull, 14954183901757012458ull}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {15409921801541329777ull, 10546487400963404004ull},\n      1024);\n\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    0, {3422973727, 2656139328, 2656139328, 2656139328}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    9, {2808089785, 314604614, 314604614, 314604614}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    42, {3611919118, 1962256489, 1962256489, 1962256489}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    42, {3399017053, 732469929, 732469929, 732469929}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 2>>, uint32_t>(\n      {2, 2}, {1234494082, 1431451587, 431049201, 431049201}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2516796247, 2757675829, 778406919, 2453259553}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2686265656, 591236665, 3797082165, 2731908938}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3918256832, 4205523739, 1707810111, 1625952473},\n      1024);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 2>>, uint32_t>(\n      {2, 2}, {3811075945, 727160712, 3510740342, 235225510}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2817194959, 206796677, 3391242768, 248681098}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2335912146, 1566515912, 760710030, 452077451}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {1101169764, 1758958147, 2406511780, 2903571412},\n      1024);\n}\n\nTEST_CASE(\"utility cuco::murmurhash3_x64_128 test\", \"\")\n{\n  // Reference hash values were computed using\n  // https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      0, {14961230494313510588ull, 6383328099726337777ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      9, {1779292183511753683ull, 16298496441448380334ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      42, {2913627637088662735ull, 16344193523890567190ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      42, {2248879576374326886ull, 18006515275339376488ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 2>>, uint64_t>(\n      {2, 2}, {12221386834995143465ull, 6690950894782946573ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 3>>, uint64_t>(\n      {1, 4, 9}, {299140022350411792ull, 9891903873182035274ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {4333511168876981289ull, 4659486988434316416ull}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3302412811061286680ull, 7070355726356610672ull},\n      1024));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 2>>, uint64_t>(\n      {2, 2}, {8554944597931919519ull, 14938998000509429729ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 3>>, uint64_t>(\n      {1, 4, 9}, {13442629947720186435ull, 7061727494178573325ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {8786399719555989948ull, 14954183901757012458ull}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {15409921801541329777ull, 10546487400963404004ull},\n      1024));\n\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      0, {3422973727, 2656139328, 2656139328, 2656139328}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      9, {2808089785, 314604614, 314604614, 314604614}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      42, {3611919118, 1962256489, 1962256489, 1962256489}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      42, {3399017053, 732469929, 732469929, 732469929}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 2>>, uint32_t>(\n      {2, 2}, {1234494082, 1431451587, 431049201, 431049201}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2516796247, 2757675829, 778406919, 2453259553}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2686265656, 591236665, 3797082165, 2731908938}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3918256832, 4205523739, 1707810111, 1625952473},\n      1024));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 2>>, uint32_t>(\n      {2, 2}, {3811075945, 727160712, 3510740342, 235225510}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2817194959, 206796677, 3391242768, 248681098}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2335912146, 1566515912, 760710030, 452077451}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {1101169764, 1758958147, 2406511780, 2903571412},\n      1024));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(24, true);\n\n    check_murmurhash3_128_result_kernel<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n", "output": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/hash_functions.cuh>\n\n#include <cuda/std/cstddef>\n#include <cuda/std/functional>\n#include <cuda/std/limits>\n#include <thrust/device_vector.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n#include <catch2/catch_test_macros.hpp>\n\ntemplate <int32_t Words>\nstruct large_key {\n  constexpr __host__ __device__ large_key(int32_t value) noexcept\n  {\n    for (int32_t i = 0; i < Words; ++i) {\n      data_[i] = value;\n    }\n  }\n\n private:\n  int32_t data_[Words];\n};\n\ntemplate <typename Hash, typename... HashConstructorArgs>\nstatic __host__ __device__ bool check_hash_result(\n  typename Hash::argument_type const& key,\n  typename Hash::result_type expected,\n  HashConstructorArgs&&... hash_constructor_args) noexcept\n{\n  Hash h(cuda::std::forward<HashConstructorArgs>(hash_constructor_args)...);\n  return (h(key) == expected);\n}\n\ntemplate <typename OutputIter>\n__global__ void check_identity_hash_result_kernel(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::identity_hash<signed char>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<signed char>>(\n    cuda::std::numeric_limits<signed char>::max(), cuda::std::numeric_limits<signed char>::max());\n\n  result[i++] = check_hash_result<cuco::identity_hash<int32_t>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<int32_t>>(\n    cuda::std::numeric_limits<int32_t>::max(), cuda::std::numeric_limits<int32_t>::max());\n\n  result[i++] = check_hash_result<cuco::identity_hash<int64_t>>(0, 0);\n  result[i++] = check_hash_result<cuco::identity_hash<int64_t>>(\n    cuda::std::numeric_limits<int64_t>::max(), cuda::std::numeric_limits<int64_t>::max());\n}\n\nTEST_CASE(\"utility cuco::identity_hash test\", \"\")\n{\n  SECTION(\"Check if host-generated hash values match the identity function.\")\n  {\n    CHECK(check_hash_result<cuco::identity_hash<signed char>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<signed char>>(\n      cuda::std::numeric_limits<signed char>::max(),\n      cuda::std::numeric_limits<signed char>::max()));\n\n    CHECK(check_hash_result<cuco::identity_hash<int32_t>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<int32_t>>(\n      cuda::std::numeric_limits<int32_t>::max(), cuda::std::numeric_limits<int32_t>::max()));\n\n    CHECK(check_hash_result<cuco::identity_hash<int64_t>>(0, 0));\n    CHECK(check_hash_result<cuco::identity_hash<int64_t>>(\n      cuda::std::numeric_limits<int64_t>::max(), cuda::std::numeric_limits<int64_t>::max()));\n  }\n  SECTION(\"Check if device-generated hash values match the identity function.\")\n  {\n    thrust::device_vector<bool> result(7, true);\n\n    check_identity_hash_result_kernel<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_hash_result_kernel_64(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(0, 16804241149081757544, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(42, 765293966243412708, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<char>>(0, 9486749600008296231, 42);\n\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(0, 4246796580750024372, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(0, 3614696996920510707, 42);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(42, 15516826743637085169, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int32_t>>(123456789, 9462334144942111946, 0);\n\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(0, 3803688792395291579, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(0, 13194218611613725804, 42);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(42, 13066772586158965587, 0);\n  result[i++] = check_hash_result<cuco::xxhash_64<int64_t>>(123456789, 14662639848940634189, 0);\n\n#if defined(CUCO_HAS_INT128)\n  result[i++] = check_hash_result<cuco::xxhash_64<__int128>>(123456789, 7986913354431084250, 0);\n#endif\n\n  result[i++] =\n    check_hash_result<cuco::xxhash_64<large_key<32>>>(123456789, 2031761887105658523, 0);\n}\n\nTEST_CASE(\"utility cuco::xxhash_64 test\", \"\")\n{\n  // Reference hash values were computed using https://github.com/Cyan4973/xxHash\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(0, 16804241149081757544, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(42, 765293966243412708, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<char>>(0, 9486749600008296231, 42));\n\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(0, 4246796580750024372, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(0, 3614696996920510707, 42));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(42, 15516826743637085169, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int32_t>>(123456789, 9462334144942111946, 0));\n\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(0, 3803688792395291579, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(0, 13194218611613725804, 42));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(42, 13066772586158965587, 0));\n    CHECK(check_hash_result<cuco::xxhash_64<int64_t>>(123456789, 14662639848940634189, 0));\n\n#if defined(CUCO_HAS_INT128)\n    CHECK(check_hash_result<cuco::xxhash_64<__int128>>(123456789, 7986913354431084250, 0));\n#endif\n\n    // 32*4=128-byte key to test the pipelined outermost hashing loop\n    CHECK(check_hash_result<cuco::xxhash_64<large_key<32>>>(123456789, 2031761887105658523, 0));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(10);\n\n    check_hash_result_kernel_64<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_hash_result_kernel_32(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(0, 3479547966, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(42, 3774771295, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<char>>(0, 2099223482, 42);\n\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(0, 148298089, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(0, 2132181312, 42);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(42, 1161967057, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int32_t>>(123456789, 2987034094, 0);\n\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(0, 3736311059, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(0, 1076387279, 42);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(42, 2332451213, 0);\n  result[i++] = check_hash_result<cuco::xxhash_32<int64_t>>(123456789, 1561711919, 0);\n\n#if defined(CUCO_HAS_INT128)\n  result[i++] = check_hash_result<cuco::xxhash_32<__int128>>(123456789, 1846633701, 0);\n#endif\n\n  result[i++] = check_hash_result<cuco::xxhash_32<large_key<32>>>(123456789, 3715432378, 0);\n}\n\nTEST_CASE(\"utility cuco::xxhash_32 test\", \"\")\n{\n  // Reference hash values were computed using https://github.com/Cyan4973/xxHash\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(0, 3479547966, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(42, 3774771295, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<char>>(0, 2099223482, 42));\n\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(0, 148298089, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(0, 2132181312, 42));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(42, 1161967057, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int32_t>>(123456789, 2987034094, 0));\n\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(0, 3736311059, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(0, 1076387279, 42));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(42, 2332451213, 0));\n    CHECK(check_hash_result<cuco::xxhash_32<int64_t>>(123456789, 1561711919, 0));\n\n#if defined(CUCO_HAS_INT128)\n    CHECK(check_hash_result<cuco::xxhash_32<__int128>>(123456789, 1846633701, 0));\n#endif\n\n    // 32*4=128-byte key to test the pipelined outermost hashing loop\n    CHECK(check_hash_result<cuco::xxhash_32<large_key<32>>>(123456789, 3715432378, 0));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(20, true);\n\n    check_hash_result_kernel_32<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"utility hasher compute_hash tests\",\n                       \"\",\n                       ((typename Hash), Hash),\n                       (cuco::murmurhash3_32<char>),\n                       (cuco::murmurhash3_32<int32_t>),\n                       (cuco::xxhash_32<char>),\n                       (cuco::xxhash_32<int32_t>),\n                       (cuco::xxhash_64<char>),\n                       (cuco::xxhash_64<int32_t>))\n{\n  using key_type = typename Hash::argument_type;\n\n  Hash hash;\n  key_type key = 42;\n\n  SECTION(\"Identical keys with static and dynamic key size should have the same hash value.\")\n  {\n    CHECK(hash(key) ==\n          hash.compute_hash(reinterpret_cast<cuda::std::byte const*>(&key), sizeof(key_type)));\n  }\n}\n\ntemplate <typename OutputIter>\n__global__ void check_murmurhash3_128_result_kernel(OutputIter result)\n{\n  int i = 0;\n\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    0, {14961230494313510588ull, 6383328099726337777ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    9, {1779292183511753683ull, 16298496441448380334ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    42, {2913627637088662735ull, 16344193523890567190ull}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n    42, {2248879576374326886ull, 18006515275339376488ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 2>>, uint64_t>(\n      {2, 2}, {12221386834995143465ull, 6690950894782946573ull}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 3>>, uint64_t>(\n      {1, 4, 9}, {299140022350411792ull, 9891903873182035274ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {4333511168876981289ull, 4659486988434316416ull}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3302412811061286680ull, 7070355726356610672ull},\n      1024);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 2>>, uint64_t>(\n      {2, 2}, {8554944597931919519ull, 14938998000509429729ull}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 3>>, uint64_t>(\n      {1, 4, 9}, {13442629947720186435ull, 7061727494178573325ull}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {8786399719555989948ull, 14954183901757012458ull}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {15409921801541329777ull, 10546487400963404004ull},\n      1024);\n\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    0, {3422973727, 2656139328, 2656139328, 2656139328}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    9, {2808089785, 314604614, 314604614, 314604614}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    42, {3611919118, 1962256489, 1962256489, 1962256489}, 0);\n  result[i++] = check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n    42, {3399017053, 732469929, 732469929, 732469929}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 2>>, uint32_t>(\n      {2, 2}, {1234494082, 1431451587, 431049201, 431049201}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2516796247, 2757675829, 778406919, 2453259553}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2686265656, 591236665, 3797082165, 2731908938}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3918256832, 4205523739, 1707810111, 1625952473},\n      1024);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 2>>, uint32_t>(\n      {2, 2}, {3811075945, 727160712, 3510740342, 235225510}, 0);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2817194959, 206796677, 3391242768, 248681098}, 42);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2335912146, 1566515912, 760710030, 452077451}, 63);\n  result[i++] =\n    check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {1101169764, 1758958147, 2406511780, 2903571412},\n      1024);\n}\n\nTEST_CASE(\"utility cuco::murmurhash3_x64_128 test\", \"\")\n{\n  // Reference hash values were computed using\n  // https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n\n  SECTION(\"Check if host-generated hash values match the reference implementation.\")\n  {\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      0, {14961230494313510588ull, 6383328099726337777ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      9, {1779292183511753683ull, 16298496441448380334ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      42, {2913627637088662735ull, 16344193523890567190ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<int32_t>, uint64_t>(\n      42, {2248879576374326886ull, 18006515275339376488ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 2>>, uint64_t>(\n      {2, 2}, {12221386834995143465ull, 6690950894782946573ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 3>>, uint64_t>(\n      {1, 4, 9}, {299140022350411792ull, 9891903873182035274ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {4333511168876981289ull, 4659486988434316416ull}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int32_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3302412811061286680ull, 7070355726356610672ull},\n      1024));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 2>>, uint64_t>(\n      {2, 2}, {8554944597931919519ull, 14938998000509429729ull}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 3>>, uint64_t>(\n      {1, 4, 9}, {13442629947720186435ull, 7061727494178573325ull}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 4>>, uint64_t>(\n      {42, 64, 108, 1024}, {8786399719555989948ull, 14954183901757012458ull}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x64_128<cuda::std::array<int64_t, 16>>, uint64_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {15409921801541329777ull, 10546487400963404004ull},\n      1024));\n\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      0, {3422973727, 2656139328, 2656139328, 2656139328}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      9, {2808089785, 314604614, 314604614, 314604614}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      42, {3611919118, 1962256489, 1962256489, 1962256489}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<int32_t>, uint32_t>(\n      42, {3399017053, 732469929, 732469929, 732469929}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 2>>, uint32_t>(\n      {2, 2}, {1234494082, 1431451587, 431049201, 431049201}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2516796247, 2757675829, 778406919, 2453259553}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2686265656, 591236665, 3797082165, 2731908938}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int32_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {3918256832, 4205523739, 1707810111, 1625952473},\n      1024));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 2>>, uint32_t>(\n      {2, 2}, {3811075945, 727160712, 3510740342, 235225510}, 0));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 3>>, uint32_t>(\n      {1, 4, 9}, {2817194959, 206796677, 3391242768, 248681098}, 42));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 4>>, uint32_t>(\n      {42, 64, 108, 1024}, {2335912146, 1566515912, 760710030, 452077451}, 63));\n    CHECK(check_hash_result<cuco::murmurhash3_x86_128<cuda::std::array<int64_t, 16>>, uint32_t>(\n      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},\n      {1101169764, 1758958147, 2406511780, 2903571412},\n      1024));\n  }\n\n  SECTION(\"Check if device-generated hash values match the reference implementation.\")\n  {\n    thrust::device_vector<bool> result(24, true);\n\n    check_murmurhash3_128_result_kernel<<<1, 1>>>(result.begin());\n\n    CHECK(cuco::test::all_of(result.begin(), result.end(), cuda::std::identity{}));\n  }\n}\n", "source": "/kaggle/working/cuCollections/tests/utility/hash_test.cu"}
{"input": "/*\n * Copyright (c) 2020-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_map.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <limits>\n\ntemplate <std::size_t ValidSize, typename Ref>\n__global__ void shared_memory_test_kernel(Ref* maps,\n                                          typename Ref::key_type const* const insterted_keys,\n                                          typename Ref::mapped_type const* const inserted_values,\n                                          size_t number_of_elements,\n                                          bool* const keys_exist,\n                                          bool* const keys_and_values_correct)\n{\n  // Each block processes one map\n  const size_t map_id = blockIdx.x;\n  const size_t offset = map_id * number_of_elements;\n\n  __shared__ typename Ref::value_type sm_buffer[ValidSize];\n\n  auto g          = cuco::test::cg::this_thread_block();\n  auto insert_ref = maps[map_id].make_copy(g, sm_buffer, cuco::thread_scope_block);\n  auto find_ref   = insert_ref.rebind_operators(cuco::op::find);\n\n  for (int i = g.thread_rank(); i < number_of_elements; i += g.size()) {\n    auto found_pair_it = find_ref.find(insterted_keys[offset + i]);\n\n    if (found_pair_it != find_ref.end()) {\n      keys_exist[offset + i] = true;\n      if (found_pair_it->first == insterted_keys[offset + i] and\n          found_pair_it->second == inserted_values[offset + i]) {\n        keys_and_values_correct[offset + i] = true;\n      } else {\n        keys_and_values_correct[offset + i] = false;\n      }\n    } else {\n      keys_exist[offset + i]              = false;\n      keys_and_values_correct[offset + i] = true;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_map shared memory tests\",\n                       \"\",\n                       ((typename Key, typename Value), Key, Value),\n                       (int32_t, int32_t),\n                       (int32_t, int64_t),\n                       (int64_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr std::size_t number_of_maps  = 1000;\n  constexpr std::size_t elements_in_map = 500;\n  constexpr std::size_t map_capacity    = 2 * elements_in_map;\n\n  using extent_type = cuco::extent<std::size_t, map_capacity>;\n  using map_type    = cuco::static_map<Key,\n                                       Value,\n                                       extent_type,\n                                       cuda::thread_scope_device,\n                                       cuda::std::equal_to<Key>,\n                                       cuco::linear_probing<1, cuco::default_hash_function<Key>>>;\n\n  // one array for all maps, first elements_in_map element belong to map 0, second to map 1 and so\n  // on\n  thrust::device_vector<Key> d_keys(number_of_maps * elements_in_map);\n  thrust::device_vector<Value> d_values(number_of_maps * elements_in_map);\n\n  thrust::sequence(thrust::device, d_keys.begin(), d_keys.end());\n  thrust::sequence(thrust::device, d_values.begin(), d_values.end(), 1);\n\n  // using std::unique_ptr because static_map does not have copy/move constructor/assignment\n  // operator yet\n  std::vector<std::unique_ptr<map_type>> maps;\n  for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n    maps.push_back(std::make_unique<map_type>(\n      extent_type{}, cuco::empty_key<Key>{-1}, cuco::empty_value<Value>{-1}));\n  }\n\n  thrust::device_vector<bool> d_keys_exist(number_of_maps * elements_in_map);\n  thrust::device_vector<bool> d_keys_and_values_correct(number_of_maps * elements_in_map);\n\n  using ref_type = typename map_type::template ref_type<cuco::op::insert_tag>;\n\n  SECTION(\"Keys are all found after insertion.\")\n  {\n    auto pairs_begin = cuda::make_transform_iterator(\n      cuda::counting_iterator{0},\n      cuda::proclaim_return_type<cuco::pair<Key, Value>>(\n        [d_keys = d_keys.data(), d_values = d_values.data()] __device__(int idx) {\n          return cuco::pair<Key, Value>(d_keys[idx], d_values[idx]);\n        }));\n    std::vector<ref_type> h_refs;\n    for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n      const std::size_t offset = map_id * elements_in_map;\n\n      map_type* map = maps[map_id].get();\n      map->insert(pairs_begin + offset, pairs_begin + offset + elements_in_map);\n      h_refs.push_back(map->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    // maybe_unused to silence false positive \"variable set but not used\" warning\n    [[maybe_unused]] auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_maps, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               d_values.data().get(),\n                               elements_in_map,\n                               d_keys_exist.data().get(),\n                               d_keys_and_values_correct.data().get());\n\n    REQUIRE(d_keys_exist.size() == d_keys_and_values_correct.size());\n    auto zip = thrust::make_zip_iterator(\n      cuda::std::tuple{d_keys_exist.begin(), d_keys_and_values_correct.begin()});\n\n    REQUIRE(cuco::test::all_of(zip,\n                               zip + d_keys_exist.size(),\n                               cuda::proclaim_return_type<bool>([] __device__(auto const& z) {\n                                 return cuda::std::get<0>(z) and cuda::std::get<1>(z);\n                               })));\n  }\n\n  SECTION(\"No key is found before insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n      h_refs.push_back(maps[map_id].get()->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    // maybe_unused to silence false positive \"variable set but not used\" warning\n    [[maybe_unused]] auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_maps, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               d_values.data().get(),\n                               elements_in_map,\n                               d_keys_exist.data().get(),\n                               d_keys_and_values_correct.data().get());\n\n    REQUIRE(cuco::test::none_of(d_keys_exist.begin(), d_keys_exist.end(), cuda::std::identity{}));\n  }\n}\n\nauto constexpr cg_size     = 1;\nauto constexpr bucket_size = 1;\n\ntemplate <std::size_t ValidSize>\n__global__ void shared_memory_hash_table_kernel(bool* key_found)\n{\n  using Key       = int32_t;\n  using Value     = int32_t;\n  using slot_type = cuco::pair<Key, Value>;\n\n  __shared__ slot_type map[ValidSize];\n\n  using extent_type      = cuco::extent<std::size_t, ValidSize>;\n  using storage_ref_type = cuco::bucket_storage_ref<slot_type, bucket_size, extent_type>;\n\n  auto raw_ref =\n    cuco::static_map_ref{cuco::empty_key<Key>{-1},\n                         cuco::empty_value<Value>{-1},\n                         cuda::std::equal_to<Key>{},\n                         cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>{},\n                         cuco::thread_scope_block,\n                         storage_ref_type{extent_type{}, map}};\n\n  auto const block = cooperative_groups::this_thread_block();\n  raw_ref.initialize(block);\n\n  auto const index = threadIdx.x + blockIdx.x * blockDim.x;\n  auto const rank  = block.thread_rank();\n\n  // insert {thread_rank, thread_rank} for each thread in thread-block\n  auto insert_ref = raw_ref.rebind_operators(cuco::op::insert);\n  insert_ref.insert(slot_type{rank, rank});\n  block.sync();\n\n  auto find_ref             = insert_ref.rebind_operators(cuco::op::find);\n  auto const retrieved_pair = find_ref.find(rank);\n  block.sync();\n\n  if (retrieved_pair != find_ref.end() && retrieved_pair->second == rank) {\n    key_found[index] = true;\n  }\n}\n\nTEST_CASE(\"static map shared memory slots.\", \"\")\n{\n  constexpr std::size_t N = 256;\n  // maybe_unused to silence false positive \"variable set but not used\" warning\n  [[maybe_unused]] auto constexpr valid_size =\n    cuco::make_valid_extent<cg_size, bucket_size>(cuco::extent<std::size_t, N>{});\n\n  thrust::device_vector<bool> key_found(N, false);\n  shared_memory_hash_table_kernel<valid_size.value()><<<8, 32>>>(key_found.data().get());\n  CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n  REQUIRE(cuco::test::all_of(key_found.begin(), key_found.end(), cuda::std::identity{}));\n}\n", "output": "/*\n * Copyright (c) 2020-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_map.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <limits>\n\ntemplate <std::size_t ValidSize, typename Ref>\n__global__ void shared_memory_test_kernel(Ref* maps,\n                                          typename Ref::key_type const* const insterted_keys,\n                                          typename Ref::mapped_type const* const inserted_values,\n                                          size_t number_of_elements,\n                                          bool* const keys_exist,\n                                          bool* const keys_and_values_correct)\n{\n  // Each block processes one map\n  const size_t map_id = blockIdx.x;\n  const size_t offset = map_id * number_of_elements;\n\n  __shared__ typename Ref::value_type sm_buffer[ValidSize];\n\n  auto g          = cuco::test::cg::this_thread_block();\n  auto insert_ref = maps[map_id].make_copy(g, sm_buffer, cuco::thread_scope_block);\n  auto find_ref   = insert_ref.rebind_operators(cuco::op::find);\n\n  for (int i = g.thread_rank(); i < number_of_elements; i += g.size()) {\n    auto found_pair_it = find_ref.find(insterted_keys[offset + i]);\n\n    if (found_pair_it != find_ref.end()) {\n      keys_exist[offset + i] = true;\n      if (found_pair_it->first == insterted_keys[offset + i] and\n          found_pair_it->second == inserted_values[offset + i]) {\n        keys_and_values_correct[offset + i] = true;\n      } else {\n        keys_and_values_correct[offset + i] = false;\n      }\n    } else {\n      keys_exist[offset + i]              = false;\n      keys_and_values_correct[offset + i] = true;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_map shared memory tests\",\n                       \"\",\n                       ((typename Key, typename Value), Key, Value),\n                       (int32_t, int32_t),\n                       (int32_t, int64_t),\n                       (int64_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr std::size_t number_of_maps  = 1000;\n  constexpr std::size_t elements_in_map = 500;\n  constexpr std::size_t map_capacity    = 2 * elements_in_map;\n\n  using extent_type = cuco::extent<std::size_t, map_capacity>;\n  using map_type    = cuco::static_map<Key,\n                                       Value,\n                                       extent_type,\n                                       cuda::thread_scope_device,\n                                       cuda::std::equal_to<Key>,\n                                       cuco::linear_probing<1, cuco::default_hash_function<Key>>>;\n\n  // one array for all maps, first elements_in_map element belong to map 0, second to map 1 and so\n  // on\n  thrust::device_vector<Key> d_keys(number_of_maps * elements_in_map);\n  thrust::device_vector<Value> d_values(number_of_maps * elements_in_map);\n\n  thrust::sequence(thrust::device, d_keys.begin(), d_keys.end());\n  thrust::sequence(thrust::device, d_values.begin(), d_values.end(), 1);\n\n  // using std::unique_ptr because static_map does not have copy/move constructor/assignment\n  // operator yet\n  std::vector<std::unique_ptr<map_type>> maps;\n  for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n    maps.push_back(std::make_unique<map_type>(\n      extent_type{}, cuco::empty_key<Key>{-1}, cuco::empty_value<Value>{-1}));\n  }\n\n  thrust::device_vector<bool> d_keys_exist(number_of_maps * elements_in_map);\n  thrust::device_vector<bool> d_keys_and_values_correct(number_of_maps * elements_in_map);\n\n  using ref_type = typename map_type::template ref_type<cuco::op::insert_tag>;\n\n  SECTION(\"Keys are all found after insertion.\")\n  {\n    auto pairs_begin = cuda::make_transform_iterator(\n      cuda::counting_iterator{0},\n      cuda::proclaim_return_type<cuco::pair<Key, Value>>(\n        [d_keys = d_keys.data(), d_values = d_values.data()] __device__(int idx) {\n          return cuco::pair<Key, Value>(d_keys[idx], d_values[idx]);\n        }));\n    std::vector<ref_type> h_refs;\n    for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n      const std::size_t offset = map_id * elements_in_map;\n\n      map_type* map = maps[map_id].get();\n      map->insert(pairs_begin + offset, pairs_begin + offset + elements_in_map);\n      h_refs.push_back(map->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    // maybe_unused to silence false positive \"variable set but not used\" warning\n    [[maybe_unused]] auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_maps, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               d_values.data().get(),\n                               elements_in_map,\n                               d_keys_exist.data().get(),\n                               d_keys_and_values_correct.data().get());\n\n    REQUIRE(d_keys_exist.size() == d_keys_and_values_correct.size());\n    auto zip = thrust::make_zip_iterator(\n      cuda::std::tuple{d_keys_exist.begin(), d_keys_and_values_correct.begin()});\n\n    REQUIRE(cuco::test::all_of(zip,\n                               zip + d_keys_exist.size(),\n                               cuda::proclaim_return_type<bool>([] __device__(auto const& z) {\n                                 return cuda::std::get<0>(z) and cuda::std::get<1>(z);\n                               })));\n  }\n\n  SECTION(\"No key is found before insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t map_id = 0; map_id < number_of_maps; ++map_id) {\n      h_refs.push_back(maps[map_id].get()->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    // maybe_unused to silence false positive \"variable set but not used\" warning\n    [[maybe_unused]] auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_maps, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               d_values.data().get(),\n                               elements_in_map,\n                               d_keys_exist.data().get(),\n                               d_keys_and_values_correct.data().get());\n\n    REQUIRE(cuco::test::none_of(d_keys_exist.begin(), d_keys_exist.end(), cuda::std::identity{}));\n  }\n}\n\nauto constexpr cg_size     = 1;\nauto constexpr bucket_size = 1;\n\ntemplate <std::size_t ValidSize>\n__global__ void shared_memory_hash_table_kernel(bool* key_found)\n{\n  using Key       = int32_t;\n  using Value     = int32_t;\n  using slot_type = cuco::pair<Key, Value>;\n\n  __shared__ slot_type map[ValidSize];\n\n  using extent_type      = cuco::extent<std::size_t, ValidSize>;\n  using storage_ref_type = cuco::bucket_storage_ref<slot_type, bucket_size, extent_type>;\n\n  auto raw_ref =\n    cuco::static_map_ref{cuco::empty_key<Key>{-1},\n                         cuco::empty_value<Value>{-1},\n                         cuda::std::equal_to<Key>{},\n                         cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>{},\n                         cuco::thread_scope_block,\n                         storage_ref_type{extent_type{}, map}};\n\n  auto const block = cooperative_groups::this_thread_block();\n  raw_ref.initialize(block);\n\n  auto const index = threadIdx.x + blockIdx.x * blockDim.x;\n  auto const rank  = block.thread_rank();\n\n  // insert {thread_rank, thread_rank} for each thread in thread-block\n  auto insert_ref = raw_ref.rebind_operators(cuco::op::insert);\n  insert_ref.insert(slot_type{rank, rank});\n  block.sync();\n\n  auto find_ref             = insert_ref.rebind_operators(cuco::op::find);\n  auto const retrieved_pair = find_ref.find(rank);\n  block.sync();\n\n  if (retrieved_pair != find_ref.end() && retrieved_pair->second == rank) {\n    key_found[index] = true;\n  }\n}\n\nTEST_CASE(\"static map shared memory slots.\", \"\")\n{\n  constexpr std::size_t N = 256;\n  // maybe_unused to silence false positive \"variable set but not used\" warning\n  [[maybe_unused]] auto constexpr valid_size =\n    cuco::make_valid_extent<cg_size, bucket_size>(cuco::extent<std::size_t, N>{});\n\n  thrust::device_vector<bool> key_found(N, false);\n  shared_memory_hash_table_kernel<valid_size.value()><<<8, 32>>>(key_found.data().get());\n  CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n  REQUIRE(cuco::test::all_of(key_found.begin(), key_found.end(), cuda::std::identity{}));\n}\n", "source": "/kaggle/working/cuCollections/tests/static_map/shared_memory_test.cu"}
{"input": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_map.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_map retrieve_if\",\n                       \"\",\n                       ((typename Key, typename T), Key, T),\n                       (int32_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_map<Key, T>;\n  using value_type     = typename container_type::value_type;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}, cuco::empty_value<T>{-1}};\n\n  auto keys_begin  = cuda::counting_iterator<Key>(1);\n  auto vals_begin  = cuda::counting_iterator<T>(1);\n  auto pairs_begin = thrust::make_zip_iterator(cuda::std::tuple{keys_begin, vals_begin});\n\n  container.insert(pairs_begin, pairs_begin + num_keys);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_pairs.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_pairs.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_pairs.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n}", "output": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_map.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::value_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_map retrieve_if\",\n                       \"\",\n                       ((typename Key, typename T), Key, T),\n                       (int32_t, int32_t),\n                       (int64_t, int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t, __int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_map<Key, T>;\n  using value_type     = typename container_type::value_type;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}, cuco::empty_value<T>{-1}};\n\n  auto keys_begin  = cuda::counting_iterator<Key>(1);\n  auto vals_begin  = cuda::counting_iterator<T>(1);\n  auto pairs_begin = thrust::make_zip_iterator(cuda::std::tuple{keys_begin, vals_begin});\n\n  container.insert(pairs_begin, pairs_begin + num_keys);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_pairs.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_pairs.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_begin + num_keys);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<value_type> matched_pairs(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_pairs.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n}", "source": "/kaggle/working/cuCollections/tests/static_map/retrieve_if_test.cu"}
{"input": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_multiset.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_multiset retrieve_if\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_multiset<Key>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n\n  container.insert(keys_begin, keys_end);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_keys.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_keys.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_keys.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n}", "output": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_multiset.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_multiset retrieve_if\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_multiset<Key>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{-1}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n\n  container.insert(keys_begin, keys_end);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_keys.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_keys.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_keys.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n}", "source": "/kaggle/working/cuCollections/tests/static_multiset/retrieve_if_test.cu"}
{"input": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_set.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <limits>\n\ntemplate <std::size_t ValidSize, typename Ref>\n__global__ void shared_memory_test_kernel(Ref* sets,\n                                          typename Ref::key_type const* const insterted_keys,\n                                          size_t number_of_elements,\n                                          bool* const keys_exist,\n                                          bool* const keys_correct)\n{\n  // Each block processes one set\n  const size_t set_id = blockIdx.x;\n  const size_t offset = set_id * number_of_elements;\n\n  __shared__ typename Ref::value_type sm_buffer[ValidSize];\n\n  auto g          = cuco::test::cg::this_thread_block();\n  auto insert_ref = sets[set_id].make_copy(g, sm_buffer, cuco::thread_scope_block);\n  auto find_ref   = insert_ref.rebind_operators(cuco::op::find);\n\n  for (int i = g.thread_rank(); i < number_of_elements; i += g.size()) {\n    auto found_it = find_ref.find(insterted_keys[offset + i]);\n\n    if (found_it != find_ref.end()) {\n      keys_exist[offset + i] = true;\n      if (*found_it == insterted_keys[offset + i]) {\n        keys_correct[offset + i] = true;\n      } else {\n        keys_correct[offset + i] = false;\n      }\n    } else {\n      keys_exist[offset + i]   = false;\n      keys_correct[offset + i] = true;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_set shared memory tests\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr std::size_t number_of_sets  = 1000;\n  constexpr std::size_t elements_in_set = 500;\n  constexpr std::size_t set_capacity    = 2 * elements_in_set;\n\n  using extent_type = cuco::extent<std::size_t, set_capacity>;\n  using set_type    = cuco::static_set<Key,\n                                       extent_type,\n                                       cuda::thread_scope_device,\n                                       cuda::std::equal_to<Key>,\n                                       cuco::linear_probing<1, cuco::default_hash_function<Key>>>;\n\n  // one array for all sets, first elements_in_set element belong to set 0, second to set 1 and so\n  // on\n  thrust::device_vector<Key> d_keys(number_of_sets * elements_in_set);\n\n  thrust::sequence(thrust::device, d_keys.begin(), d_keys.end());\n\n  // using std::unique_ptr because static_set does not have copy/move constructor/assignment\n  // operator yet\n  std::vector<std::unique_ptr<set_type>> sets;\n  for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n    sets.push_back(std::make_unique<set_type>(extent_type{}, cuco::empty_key<Key>{-1}));\n  }\n\n  thrust::device_vector<bool> d_keys_exist(number_of_sets * elements_in_set);\n  thrust::device_vector<bool> d_keys_correct(number_of_sets * elements_in_set);\n\n  using ref_type = typename set_type::template ref_type<cuco::op::insert_tag>;\n\n  SECTION(\"Keys are all found after insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n      const std::size_t offset = set_id * elements_in_set;\n\n      set_type* set = sets[set_id].get();\n      set->insert(d_keys.begin() + offset, d_keys.begin() + offset + elements_in_set);\n      h_refs.push_back(set->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_sets, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               elements_in_set,\n                               d_keys_exist.data().get(),\n                               d_keys_correct.data().get());\n\n    REQUIRE(d_keys_exist.size() == d_keys_correct.size());\n    auto zip =\n      thrust::make_zip_iterator(cuda::std::tuple{d_keys_exist.begin(), d_keys_correct.begin()});\n\n    REQUIRE(cuco::test::all_of(zip,\n                               zip + d_keys_exist.size(),\n                               cuda::proclaim_return_type<bool>([] __device__(auto const& z) {\n                                 return cuda::std::get<0>(z) and cuda::std::get<1>(z);\n                               })));\n  }\n\n  SECTION(\"No key is found before insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n      h_refs.push_back(sets[set_id].get()->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_sets, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               elements_in_set,\n                               d_keys_exist.data().get(),\n                               d_keys_correct.data().get());\n\n    REQUIRE(cuco::test::none_of(d_keys_exist.begin(), d_keys_exist.end(), cuda::std::identity{}));\n  }\n}\n\nauto constexpr cg_size     = 1;\nauto constexpr bucket_size = 1;\n\ntemplate <std::size_t ValidSize>\n__global__ void shared_memory_hash_set_kernel(bool* key_found)\n{\n  using Key       = int32_t;\n  using slot_type = Key;\n\n  __shared__ slot_type set[ValidSize];\n\n  using extent_type      = cuco::extent<std::size_t, ValidSize>;\n  using storage_ref_type = cuco::bucket_storage_ref<slot_type, bucket_size, extent_type>;\n\n  auto raw_ref =\n    cuco::static_set_ref{cuco::empty_key<Key>{-1},\n                         cuda::std::equal_to<Key>{},\n                         cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>{},\n                         cuco::thread_scope_block,\n                         storage_ref_type{extent_type{}, set}};\n\n  auto const block = cooperative_groups::this_thread_block();\n  raw_ref.initialize(block);\n\n  auto const index = threadIdx.x + blockIdx.x * blockDim.x;\n  auto const rank  = block.thread_rank();\n\n  // insert thread_rank for each thread in thread-block\n  auto insert_ref = raw_ref.rebind_operators(cuco::op::insert);\n  insert_ref.insert(rank);\n  block.sync();\n\n  auto find_ref           = insert_ref.rebind_operators(cuco::op::find);\n  auto const retrieved_it = find_ref.find(rank);\n  block.sync();\n\n  if (retrieved_it != find_ref.end() && *retrieved_it == rank) { key_found[index] = true; }\n}\n\nTEST_CASE(\"static_set shared memory slots test\", \"\")\n{\n  constexpr std::size_t N = 256;\n  [[maybe_unused]] auto constexpr valid_size =\n    cuco::make_valid_extent<cg_size, bucket_size>(cuco::extent<std::size_t, N>{});\n\n  thrust::device_vector<bool> key_found(N, false);\n  shared_memory_hash_set_kernel<valid_size.value()><<<8, 32>>>(key_found.data().get());\n  CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n  REQUIRE(cuco::test::all_of(key_found.begin(), key_found.end(), cuda::std::identity{}));\n}\n", "output": "/*\n * Copyright (c) 2023-2025, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_set.cuh>\n\n#include <cuda/functional>\n#include <cuda/std/tuple>\n#include <thrust/device_vector.h>\n#include <thrust/execution_policy.h>\n#include <thrust/iterator/zip_iterator.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\n#include <limits>\n\ntemplate <std::size_t ValidSize, typename Ref>\n__global__ void shared_memory_test_kernel(Ref* sets,\n                                          typename Ref::key_type const* const insterted_keys,\n                                          size_t number_of_elements,\n                                          bool* const keys_exist,\n                                          bool* const keys_correct)\n{\n  // Each block processes one set\n  const size_t set_id = blockIdx.x;\n  const size_t offset = set_id * number_of_elements;\n\n  __shared__ typename Ref::value_type sm_buffer[ValidSize];\n\n  auto g          = cuco::test::cg::this_thread_block();\n  auto insert_ref = sets[set_id].make_copy(g, sm_buffer, cuco::thread_scope_block);\n  auto find_ref   = insert_ref.rebind_operators(cuco::op::find);\n\n  for (int i = g.thread_rank(); i < number_of_elements; i += g.size()) {\n    auto found_it = find_ref.find(insterted_keys[offset + i]);\n\n    if (found_it != find_ref.end()) {\n      keys_exist[offset + i] = true;\n      if (*found_it == insterted_keys[offset + i]) {\n        keys_correct[offset + i] = true;\n      } else {\n        keys_correct[offset + i] = false;\n      }\n    } else {\n      keys_exist[offset + i]   = false;\n      keys_correct[offset + i] = true;\n    }\n  }\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_set shared memory tests\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr std::size_t number_of_sets  = 1000;\n  constexpr std::size_t elements_in_set = 500;\n  constexpr std::size_t set_capacity    = 2 * elements_in_set;\n\n  using extent_type = cuco::extent<std::size_t, set_capacity>;\n  using set_type    = cuco::static_set<Key,\n                                       extent_type,\n                                       cuda::thread_scope_device,\n                                       cuda::std::equal_to<Key>,\n                                       cuco::linear_probing<1, cuco::default_hash_function<Key>>>;\n\n  // one array for all sets, first elements_in_set element belong to set 0, second to set 1 and so\n  // on\n  thrust::device_vector<Key> d_keys(number_of_sets * elements_in_set);\n\n  thrust::sequence(thrust::device, d_keys.begin(), d_keys.end());\n\n  // using std::unique_ptr because static_set does not have copy/move constructor/assignment\n  // operator yet\n  std::vector<std::unique_ptr<set_type>> sets;\n  for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n    sets.push_back(std::make_unique<set_type>(extent_type{}, cuco::empty_key<Key>{-1}));\n  }\n\n  thrust::device_vector<bool> d_keys_exist(number_of_sets * elements_in_set);\n  thrust::device_vector<bool> d_keys_correct(number_of_sets * elements_in_set);\n\n  using ref_type = typename set_type::template ref_type<cuco::op::insert_tag>;\n\n  SECTION(\"Keys are all found after insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n      const std::size_t offset = set_id * elements_in_set;\n\n      set_type* set = sets[set_id].get();\n      set->insert(d_keys.begin() + offset, d_keys.begin() + offset + elements_in_set);\n      h_refs.push_back(set->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_sets, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               elements_in_set,\n                               d_keys_exist.data().get(),\n                               d_keys_correct.data().get());\n\n    REQUIRE(d_keys_exist.size() == d_keys_correct.size());\n    auto zip =\n      thrust::make_zip_iterator(cuda::std::tuple{d_keys_exist.begin(), d_keys_correct.begin()});\n\n    REQUIRE(cuco::test::all_of(zip,\n                               zip + d_keys_exist.size(),\n                               cuda::proclaim_return_type<bool>([] __device__(auto const& z) {\n                                 return cuda::std::get<0>(z) and cuda::std::get<1>(z);\n                               })));\n  }\n\n  SECTION(\"No key is found before insertion.\")\n  {\n    std::vector<ref_type> h_refs;\n    for (std::size_t set_id = 0; set_id < number_of_sets; ++set_id) {\n      h_refs.push_back(sets[set_id].get()->ref(cuco::op::insert));\n    }\n    thrust::device_vector<ref_type> d_refs(h_refs);\n\n    auto constexpr valid_size =\n      cuco::make_valid_extent<typename ref_type::probing_scheme_type,\n                              typename ref_type::storage_ref_type>(extent_type{});\n\n    shared_memory_test_kernel<valid_size.value(), ref_type>\n      <<<number_of_sets, 64>>>(d_refs.data().get(),\n                               d_keys.data().get(),\n                               elements_in_set,\n                               d_keys_exist.data().get(),\n                               d_keys_correct.data().get());\n\n    REQUIRE(cuco::test::none_of(d_keys_exist.begin(), d_keys_exist.end(), cuda::std::identity{}));\n  }\n}\n\nauto constexpr cg_size     = 1;\nauto constexpr bucket_size = 1;\n\ntemplate <std::size_t ValidSize>\n__global__ void shared_memory_hash_set_kernel(bool* key_found)\n{\n  using Key       = int32_t;\n  using slot_type = Key;\n\n  __shared__ slot_type set[ValidSize];\n\n  using extent_type      = cuco::extent<std::size_t, ValidSize>;\n  using storage_ref_type = cuco::bucket_storage_ref<slot_type, bucket_size, extent_type>;\n\n  auto raw_ref =\n    cuco::static_set_ref{cuco::empty_key<Key>{-1},\n                         cuda::std::equal_to<Key>{},\n                         cuco::linear_probing<cg_size, cuco::default_hash_function<Key>>{},\n                         cuco::thread_scope_block,\n                         storage_ref_type{extent_type{}, set}};\n\n  auto const block = cooperative_groups::this_thread_block();\n  raw_ref.initialize(block);\n\n  auto const index = threadIdx.x + blockIdx.x * blockDim.x;\n  auto const rank  = block.thread_rank();\n\n  // insert thread_rank for each thread in thread-block\n  auto insert_ref = raw_ref.rebind_operators(cuco::op::insert);\n  insert_ref.insert(rank);\n  block.sync();\n\n  auto find_ref           = insert_ref.rebind_operators(cuco::op::find);\n  auto const retrieved_it = find_ref.find(rank);\n  block.sync();\n\n  if (retrieved_it != find_ref.end() && *retrieved_it == rank) { key_found[index] = true; }\n}\n\nTEST_CASE(\"static_set shared memory slots test\", \"\")\n{\n  constexpr std::size_t N = 256;\n  [[maybe_unused]] auto constexpr valid_size =\n    cuco::make_valid_extent<cg_size, bucket_size>(cuco::extent<std::size_t, N>{});\n\n  thrust::device_vector<bool> key_found(N, false);\n  shared_memory_hash_set_kernel<valid_size.value()><<<8, 32>>>(key_found.data().get());\n  CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n  REQUIRE(cuco::test::all_of(key_found.begin(), key_found.end(), cuda::std::identity{}));\n}\n", "source": "/kaggle/working/cuCollections/tests/static_set/shared_memory_test.cu"}
{"input": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_set.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_set retrieve_if\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_set<Key>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{static_cast<Key>(-1)}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n\n  container.insert(keys_begin, keys_end);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_keys.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_keys.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      cudaMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      cudaMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_keys.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(cudaDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(cudaMemcpy(&h_counter, d_atomic_counter, sizeof(int), cudaMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(cudaFree(d_atomic_counter));\n  }\n}", "output": "/*\n * Copyright (c) 2025-2026, NVIDIA CORPORATION.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"hip/hip_runtime.h\"\n#include <test_utils.hpp>\n\n#include <cuco/detail/__config>\n#include <cuco/static_set.cuh>\n\n#include <cuda/functional>\n#include <cuda/iterator>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/sequence.h>\n\n#include <catch2/catch_template_test_macros.hpp>\n\nusing size_type = std::size_t;\n\ntemplate <class Container>\n__global__ void test_retrieve_if_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block = cg::this_thread_block();\n  auto const pred  = [] __device__(key_type k) { return k % 2 == 0; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 pred,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_false_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block        = cg::this_thread_block();\n  auto const always_false = [] __device__(key_type) { return false; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_false,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\ntemplate <class Container>\n__global__ void test_retrieve_if_all_true_kernel(\n  Container container_ref,\n  typename Container::key_type* keys_begin,\n  std::size_t num_keys,\n  typename Container::key_type* stencil_begin,\n  typename Container::key_type* output_probe,\n  typename Container::key_type* output_match,\n  cuda::atomic<int, cuda::thread_scope_device>* atomic_counter)\n{\n  using key_type = typename Container::key_type;\n  namespace cg   = cooperative_groups;\n\n  auto const block       = cg::this_thread_block();\n  auto const always_true = [] __device__(key_type) { return true; };\n\n  container_ref.retrieve_if<128>(block,\n                                 keys_begin,\n                                 keys_begin + num_keys,\n                                 stencil_begin,\n                                 always_true,\n                                 output_probe,\n                                 output_match,\n                                 *atomic_counter);\n}\n\nTEMPLATE_TEST_CASE_SIG(\"static_set retrieve_if\",\n                       \"\",\n                       ((typename Key), Key),\n                       (int32_t),\n                       (int64_t)\n#if defined(CUCO_HAS_128BIT_ATOMICS)\n                         ,\n                       (__int128_t)\n#endif\n)\n{\n  constexpr size_type num_keys{400};\n\n  using container_type = cuco::static_set<Key>;\n\n  container_type container{num_keys * 2, cuco::empty_key<Key>{static_cast<Key>(-1)}};\n\n  auto keys_begin = cuda::counting_iterator<Key>(1);\n  auto keys_end   = keys_begin + num_keys;\n\n  container.insert(keys_begin, keys_end);\n\n  SECTION(\"Testing retrieve_if with even predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_kernel<<<1, 128>>>(container_ref,\n                                        thrust::raw_pointer_cast(input_keys.data()),\n                                        num_keys,\n                                        thrust::raw_pointer_cast(stencil_values.data()),\n                                        thrust::raw_pointer_cast(probed_keys.data()),\n                                        thrust::raw_pointer_cast(matched_keys.data()),\n                                        d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve even numbers only\n    REQUIRE(h_counter > 0);\n    REQUIRE(h_counter <= static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always false predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_false_kernel<<<1, 128>>>(container_ref,\n                                                  thrust::raw_pointer_cast(input_keys.data()),\n                                                  num_keys,\n                                                  thrust::raw_pointer_cast(stencil_values.data()),\n                                                  thrust::raw_pointer_cast(probed_keys.data()),\n                                                  thrust::raw_pointer_cast(matched_keys.data()),\n                                                  d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve nothing\n    REQUIRE(h_counter == 0);\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n\n  SECTION(\"Testing retrieve_if with always true predicate\")\n  {\n    thrust::device_vector<Key> input_keys(keys_begin, keys_end);\n    thrust::device_vector<Key> stencil_values(keys_begin, keys_end);\n    thrust::device_vector<Key> probed_keys(num_keys);\n    thrust::device_vector<Key> matched_keys(num_keys);\n\n    cuda::atomic<int, cuda::thread_scope_device>* d_atomic_counter;\n    CUCO_CUDA_TRY(\n      hipMalloc(&d_atomic_counter, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n    CUCO_CUDA_TRY(\n      hipMemset(d_atomic_counter, 0, sizeof(cuda::atomic<int, cuda::thread_scope_device>)));\n\n    auto const container_ref = container.ref(cuco::op::retrieve);\n\n    test_retrieve_if_all_true_kernel<<<1, 128>>>(container_ref,\n                                                 thrust::raw_pointer_cast(input_keys.data()),\n                                                 num_keys,\n                                                 thrust::raw_pointer_cast(stencil_values.data()),\n                                                 thrust::raw_pointer_cast(probed_keys.data()),\n                                                 thrust::raw_pointer_cast(matched_keys.data()),\n                                                 d_atomic_counter);\n    CUCO_CUDA_TRY(hipDeviceSynchronize());\n\n    int h_counter;\n    CUCO_CUDA_TRY(hipMemcpy(&h_counter, d_atomic_counter, sizeof(int), hipMemcpyDeviceToHost));\n\n    // Should retrieve all keys that exist in the container\n    REQUIRE(h_counter == static_cast<int>(num_keys));\n\n    CUCO_CUDA_TRY(hipFree(d_atomic_counter));\n  }\n}", "source": "/kaggle/working/cuCollections/tests/static_set/retrieve_if_test.cu"}
{"input": "/*************************************************************** -*- C++ -*- ***\n * Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#pragma nv_diag_suppress = unsigned_compare_with_zero\n#pragma nv_diag_suppress = unrecognized_gcc_pragma\n\n#include \"cuComplex.h\"\n#include \"device_launch_parameters.h\"\n#include \"CuStateVecCircuitSimulator.h\"\n#include <thrust/complex.h>\n#include <thrust/device_ptr.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/inner_product.h>\n\nnamespace nvqir {\n\n// kronprod functions adapted from\n// https://github.com/DmitryLyakh/TAL_SH/blob/3cefc2133a68b67c515f4b68a0ed9e3c66e4b4b2/tensor_algebra_gpu_nvidia.cu#L745\n\n#define THRDS_ARRAY_PRODUCT 256\n\n#pragma push\n#pragma nv_diag_suppress 177\n__device__ __host__ cuDoubleComplex operator*(cuDoubleComplex a,\n                                              cuDoubleComplex b) {\n  return cuCmul(a, b);\n}\n__device__ __host__ cuDoubleComplex operator+(cuDoubleComplex a,\n                                              cuDoubleComplex b) {\n  return cuCadd(a, b);\n}\n__device__ __host__ cuFloatComplex operator*(cuFloatComplex a,\n                                             cuFloatComplex b) {\n  return cuCmulf(a, b);\n}\n__device__ __host__ cuFloatComplex operator+(cuFloatComplex a,\n                                             cuFloatComplex b) {\n  return cuCaddf(a, b);\n}\n\ntemplate <typename CudaDataType>\n__global__ void cudaKronprod(size_t tsize1, const CudaDataType *arr1, \n                             size_t tsize2, const CudaDataType *arr2, \n                             CudaDataType *arr0) {\n  __shared__ CudaDataType lbuf[THRDS_ARRAY_PRODUCT + 1], rbuf[THRDS_ARRAY_PRODUCT];\n  size_t _ib, _in, _jb, _jn, _tx, _jc, _ja;\n\n  _tx = (size_t)threadIdx.x;\n  for (_jb = blockIdx.y * THRDS_ARRAY_PRODUCT; _jb < tsize2;\n       _jb += gridDim.y * THRDS_ARRAY_PRODUCT) {\n    if (_jb + THRDS_ARRAY_PRODUCT > tsize2) {\n      _jn = tsize2 - _jb;\n    } else {\n      _jn = THRDS_ARRAY_PRODUCT;\n    }\n\n    if (_tx < _jn)\n      rbuf[_tx] = arr2[_jb + _tx];\n\n    for (_ib = blockIdx.x * THRDS_ARRAY_PRODUCT; _ib < tsize1;\n         _ib += gridDim.x * THRDS_ARRAY_PRODUCT) {\n      if (_ib + THRDS_ARRAY_PRODUCT > tsize1) {\n        _in = tsize1 - _ib;\n      } else {\n        _in = THRDS_ARRAY_PRODUCT;\n      }\n\n      if (_tx < _in)\n        lbuf[_tx] = arr1[_ib + _tx];\n\n      __syncthreads();\n      for (_jc = 0; _jc < _jn; _jc++) {\n        if (_tx < _in) {\n          _ja = (_jb + _jc) * tsize1 + (_ib + _tx);\n          arr0[_ja] = arr0[_ja] + lbuf[_tx] * rbuf[_jc];\n        }\n      }\n      __syncthreads();\n    }\n  }\n  return;\n}\n#pragma pop\n\ntemplate <typename CudaDataType>\nvoid kronprod(uint32_t maxGridDimY, int32_t threads_per_block,\n              size_t tsize1, const void *arr1,\n              size_t tsize2, const void *arr2,\n              void *arr0) {\n  uint32_t n_blocks_x =\n      (static_cast<uint32_t>(tsize1) + threads_per_block - 1) / threads_per_block;\n  uint32_t n_blocks_y = static_cast<uint32_t>(\n      std::min((tsize2 + static_cast<size_t>(threads_per_block) - 1) /\n                   static_cast<size_t>(threads_per_block),\n               static_cast<size_t>(maxGridDimY)));\n  dim3 grid(n_blocks_x, n_blocks_y);\n  cudaKronprod<<<grid, threads_per_block>>>(\n    tsize1, reinterpret_cast<const CudaDataType *>(arr1),\n    tsize2, reinterpret_cast<const CudaDataType *>(arr2),\n    reinterpret_cast<CudaDataType *>(arr0));\n}\n\ntemplate void\nkronprod<cuFloatComplex>(uint32_t maxGridDimY, int32_t threads_per_block,\n                         size_t tsize1, const void *arr1,\n                         size_t tsize2, const void *arr2,\n                         void *arr0);\n\ntemplate void\nkronprod<cuDoubleComplex>(uint32_t maxGridDimY, int32_t threads_per_block,\n                          size_t tsize1, const void *arr1,\n                          size_t tsize2, const void *arr2,\n                          void *arr0);\n\n/// @brief Kernel to set the first N elements of the state vector sv equal to\n/// the elements provided by the vector sv2. N is the number of elements to set.\n/// Size of sv must be greater than size of sv2.\ntemplate <typename CudaDataType>\n__global__ void cudaSetFirstNElements(CudaDataType *sv, const CudaDataType *__restrict__ sv2, int64_t N) {\n  int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (i < N) {\n    sv[i].x = sv2[i].x;\n    sv[i].y = sv2[i].y;\n  } else {\n    sv[i].x = 0.0;\n    sv[i].y = 0.0;\n  }\n}\n\ntemplate <typename CudaDataType>\nvoid setFirstNElements(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension) {\n  cudaSetFirstNElements<<<n_blocks, threads_per_block>>>(\n    reinterpret_cast<CudaDataType *>(newDeviceStateVector),\n    reinterpret_cast<CudaDataType *>(deviceStateVector),\n    previousStateDimension);\n}\n\ntemplate void\nsetFirstNElements<cuFloatComplex>(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension);\n\ntemplate void\nsetFirstNElements<cuDoubleComplex>(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension);\n\n/// @brief Initialize the device state vector to the |0...0> state\ntemplate <typename CudaDataType>\n__global__ void cudaInitializeDeviceStateVector(CudaDataType *sv, int64_t dim) {\n  int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (i == 0) {\n    sv[i].x = 1.0;\n    sv[i].y = 0.0;\n  } else if (i < dim) {\n    sv[i].x = 0.0;\n    sv[i].y = 0.0;\n  }\n}\n\ntemplate <typename CudaDataType>\nvoid initializeDeviceStateVector(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension) {\n  cudaInitializeDeviceStateVector<<<n_blocks, threads_per_block>>>(\n    reinterpret_cast<CudaDataType *>(deviceStateVector), stateDimension);\n}\n\ntemplate void\ninitializeDeviceStateVector<cuFloatComplex>(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension);\n\ntemplate void\ninitializeDeviceStateVector<cuDoubleComplex>(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension);\n\n/// @brief Custom functor for the thrust inner product.\ntemplate <typename ScalarType>\nstruct AdotConjB {\n  __host__ __device__ thrust::complex<ScalarType>\n  operator()(thrust::complex<ScalarType> a, thrust::complex<ScalarType> b) {\n    return a * thrust::conj(b);\n  };\n};\n\ntemplate struct complexValue<double>;\ntemplate struct complexValue<float>;\n\ntemplate <typename ScalarType>\ncomplexValue<ScalarType> innerProduct(\n  void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc) {\n\n  auto *castedDevicePtr =\n      reinterpret_cast<thrust::complex<ScalarType> *>(devicePtr);\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrABegin(\n      castedDevicePtr);\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrAEnd(\n      castedDevicePtr + size);\n\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrBBegin;\n  if (createDeviceAlloc) {\n    // otherPtr is not a device pointer...\n    auto *castedOtherPtr = reinterpret_cast<std::complex<ScalarType> *>(otherPtr);\n    std::vector<std::complex<ScalarType>> dataAsVec(castedOtherPtr,\n                                                    castedOtherPtr + size);\n    thrust::device_vector<thrust::complex<ScalarType>> otherDevPtr(dataAsVec);\n    thrustDevPtrBBegin = otherDevPtr.data();\n  } else {\n    // other is a device pointer\n    auto *castedOtherPtr = reinterpret_cast<thrust::complex<ScalarType> *>(otherPtr);\n    thrustDevPtrBBegin = thrust::device_ptr<thrust::complex<ScalarType>>(castedOtherPtr);\n  }\n\n  thrust::complex<ScalarType> result = thrust::inner_product(\n    thrustDevPtrABegin, thrustDevPtrAEnd, thrustDevPtrBBegin,\n    thrust::complex<ScalarType>(0.0),\n    thrust::plus<thrust::complex<ScalarType>>(),\n    AdotConjB<ScalarType>());\n\n  complexValue<ScalarType> complex;\n  complex.real = result.real();\n  complex.imaginary = result.imag();\n  return complex;\n}\n\n\ntemplate complexValue<double> \ninnerProduct(void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc);\n\ntemplate complexValue<float> \ninnerProduct(void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc);\n\n}\n", "output": "/*************************************************************** -*- C++ -*- ***\n * Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#include \"hip/hip_runtime.h\"\n#pragma nv_diag_suppress = unsigned_compare_with_zero\n#pragma nv_diag_suppress = unrecognized_gcc_pragma\n\n#include \"hip/hip_complex.h\"\n#include \"\"\n#include \"CuStateVecCircuitSimulator.h\"\n#include <thrust/complex.h>\n#include <thrust/device_ptr.h>\n#include <thrust/device_vector.h>\n#include <thrust/host_vector.h>\n#include <thrust/inner_product.h>\n\nnamespace nvqir {\n\n// kronprod functions adapted from\n// https://github.com/DmitryLyakh/TAL_SH/blob/3cefc2133a68b67c515f4b68a0ed9e3c66e4b4b2/tensor_algebra_gpu_nvidia.cu#L745\n\n#define THRDS_ARRAY_PRODUCT 256\n\n#pragma push\n#pragma nv_diag_suppress 177\n__device__ __host__ hipDoubleComplex operator*(hipDoubleComplex a,\n                                              hipDoubleComplex b) {\n  return hipCmul(a, b);\n}\n__device__ __host__ hipDoubleComplex operator+(hipDoubleComplex a,\n                                              hipDoubleComplex b) {\n  return hipCadd(a, b);\n}\n__device__ __host__ hipFloatComplex operator*(hipFloatComplex a,\n                                             hipFloatComplex b) {\n  return hipCmulf(a, b);\n}\n__device__ __host__ hipFloatComplex operator+(hipFloatComplex a,\n                                             hipFloatComplex b) {\n  return hipCaddf(a, b);\n}\n\ntemplate <typename CudaDataType>\n__global__ void cudaKronprod(size_t tsize1, const CudaDataType *arr1, \n                             size_t tsize2, const CudaDataType *arr2, \n                             CudaDataType *arr0) {\n  __shared__ CudaDataType lbuf[THRDS_ARRAY_PRODUCT + 1], rbuf[THRDS_ARRAY_PRODUCT];\n  size_t _ib, _in, _jb, _jn, _tx, _jc, _ja;\n\n  _tx = (size_t)threadIdx.x;\n  for (_jb = blockIdx.y * THRDS_ARRAY_PRODUCT; _jb < tsize2;\n       _jb += gridDim.y * THRDS_ARRAY_PRODUCT) {\n    if (_jb + THRDS_ARRAY_PRODUCT > tsize2) {\n      _jn = tsize2 - _jb;\n    } else {\n      _jn = THRDS_ARRAY_PRODUCT;\n    }\n\n    if (_tx < _jn)\n      rbuf[_tx] = arr2[_jb + _tx];\n\n    for (_ib = blockIdx.x * THRDS_ARRAY_PRODUCT; _ib < tsize1;\n         _ib += gridDim.x * THRDS_ARRAY_PRODUCT) {\n      if (_ib + THRDS_ARRAY_PRODUCT > tsize1) {\n        _in = tsize1 - _ib;\n      } else {\n        _in = THRDS_ARRAY_PRODUCT;\n      }\n\n      if (_tx < _in)\n        lbuf[_tx] = arr1[_ib + _tx];\n\n      __syncthreads();\n      for (_jc = 0; _jc < _jn; _jc++) {\n        if (_tx < _in) {\n          _ja = (_jb + _jc) * tsize1 + (_ib + _tx);\n          arr0[_ja] = arr0[_ja] + lbuf[_tx] * rbuf[_jc];\n        }\n      }\n      __syncthreads();\n    }\n  }\n  return;\n}\n#pragma pop\n\ntemplate <typename CudaDataType>\nvoid kronprod(uint32_t maxGridDimY, int32_t threads_per_block,\n              size_t tsize1, const void *arr1,\n              size_t tsize2, const void *arr2,\n              void *arr0) {\n  uint32_t n_blocks_x =\n      (static_cast<uint32_t>(tsize1) + threads_per_block - 1) / threads_per_block;\n  uint32_t n_blocks_y = static_cast<uint32_t>(\n      std::min((tsize2 + static_cast<size_t>(threads_per_block) - 1) /\n                   static_cast<size_t>(threads_per_block),\n               static_cast<size_t>(maxGridDimY)));\n  dim3 grid(n_blocks_x, n_blocks_y);\n  cudaKronprod<<<grid, threads_per_block>>>(\n    tsize1, reinterpret_cast<const CudaDataType *>(arr1),\n    tsize2, reinterpret_cast<const CudaDataType *>(arr2),\n    reinterpret_cast<CudaDataType *>(arr0));\n}\n\ntemplate void\nkronprod<hipFloatComplex>(uint32_t maxGridDimY, int32_t threads_per_block,\n                         size_t tsize1, const void *arr1,\n                         size_t tsize2, const void *arr2,\n                         void *arr0);\n\ntemplate void\nkronprod<hipDoubleComplex>(uint32_t maxGridDimY, int32_t threads_per_block,\n                          size_t tsize1, const void *arr1,\n                          size_t tsize2, const void *arr2,\n                          void *arr0);\n\n/// @brief Kernel to set the first N elements of the state vector sv equal to\n/// the elements provided by the vector sv2. N is the number of elements to set.\n/// Size of sv must be greater than size of sv2.\ntemplate <typename CudaDataType>\n__global__ void cudaSetFirstNElements(CudaDataType *sv, const CudaDataType *__restrict__ sv2, int64_t N) {\n  int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (i < N) {\n    sv[i].x = sv2[i].x;\n    sv[i].y = sv2[i].y;\n  } else {\n    sv[i].x = 0.0;\n    sv[i].y = 0.0;\n  }\n}\n\ntemplate <typename CudaDataType>\nvoid setFirstNElements(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension) {\n  cudaSetFirstNElements<<<n_blocks, threads_per_block>>>(\n    reinterpret_cast<CudaDataType *>(newDeviceStateVector),\n    reinterpret_cast<CudaDataType *>(deviceStateVector),\n    previousStateDimension);\n}\n\ntemplate void\nsetFirstNElements<hipFloatComplex>(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension);\n\ntemplate void\nsetFirstNElements<hipDoubleComplex>(uint32_t n_blocks, \n                       int32_t threads_per_block, \n                       void *newDeviceStateVector, \n                       void *deviceStateVector,\n                       std::size_t previousStateDimension);\n\n/// @brief Initialize the device state vector to the |0...0> state\ntemplate <typename CudaDataType>\n__global__ void cudaInitializeDeviceStateVector(CudaDataType *sv, int64_t dim) {\n  int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;\n  if (i == 0) {\n    sv[i].x = 1.0;\n    sv[i].y = 0.0;\n  } else if (i < dim) {\n    sv[i].x = 0.0;\n    sv[i].y = 0.0;\n  }\n}\n\ntemplate <typename CudaDataType>\nvoid initializeDeviceStateVector(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension) {\n  cudaInitializeDeviceStateVector<<<n_blocks, threads_per_block>>>(\n    reinterpret_cast<CudaDataType *>(deviceStateVector), stateDimension);\n}\n\ntemplate void\ninitializeDeviceStateVector<hipFloatComplex>(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension);\n\ntemplate void\ninitializeDeviceStateVector<hipDoubleComplex>(uint32_t n_blocks, \n                                 int32_t threads_per_block, \n                                 void *deviceStateVector,\n                                 std::size_t stateDimension);\n\n/// @brief Custom functor for the thrust inner product.\ntemplate <typename ScalarType>\nstruct AdotConjB {\n  __host__ __device__ thrust::complex<ScalarType>\n  operator()(thrust::complex<ScalarType> a, thrust::complex<ScalarType> b) {\n    return a * thrust::conj(b);\n  };\n};\n\ntemplate struct complexValue<double>;\ntemplate struct complexValue<float>;\n\ntemplate <typename ScalarType>\ncomplexValue<ScalarType> innerProduct(\n  void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc) {\n\n  auto *castedDevicePtr =\n      reinterpret_cast<thrust::complex<ScalarType> *>(devicePtr);\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrABegin(\n      castedDevicePtr);\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrAEnd(\n      castedDevicePtr + size);\n\n  thrust::device_ptr<thrust::complex<ScalarType>> thrustDevPtrBBegin;\n  if (createDeviceAlloc) {\n    // otherPtr is not a device pointer...\n    auto *castedOtherPtr = reinterpret_cast<std::complex<ScalarType> *>(otherPtr);\n    std::vector<std::complex<ScalarType>> dataAsVec(castedOtherPtr,\n                                                    castedOtherPtr + size);\n    thrust::device_vector<thrust::complex<ScalarType>> otherDevPtr(dataAsVec);\n    thrustDevPtrBBegin = otherDevPtr.data();\n  } else {\n    // other is a device pointer\n    auto *castedOtherPtr = reinterpret_cast<thrust::complex<ScalarType> *>(otherPtr);\n    thrustDevPtrBBegin = thrust::device_ptr<thrust::complex<ScalarType>>(castedOtherPtr);\n  }\n\n  thrust::complex<ScalarType> result = thrust::inner_product(\n    thrustDevPtrABegin, thrustDevPtrAEnd, thrustDevPtrBBegin,\n    thrust::complex<ScalarType>(0.0),\n    thrust::plus<thrust::complex<ScalarType>>(),\n    AdotConjB<ScalarType>());\n\n  complexValue<ScalarType> complex;\n  complex.real = result.real();\n  complex.imaginary = result.imag();\n  return complex;\n}\n\n\ntemplate complexValue<double> \ninnerProduct(void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc);\n\ntemplate complexValue<float> \ninnerProduct(void *devicePtr, void *otherPtr, std::size_t size, bool createDeviceAlloc);\n\n}\n", "source": "/kaggle/working/cuda-quantum/runtime/nvqir/custatevec/CuStateVecCircuitSimulator.cu"}
{"input": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file dispatch_kernel.cu\n/// @brief Simple dispatch kernel for testing libcudaq-realtime.\n///\n/// This example demonstrates a simple dispatch kernel that processes RPC\n/// requests.\n///\n\n#include <cstdint>\n#include <cstring>\n#include <cuda_runtime.h>\n#include <iostream>\n#include <unistd.h>\n#include <vector>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n\n// Helper macro for CUDA error checking\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    cudaError_t err = call;                                                    \\\n    if (err != cudaSuccess) {                                                  \\\n      std::cerr << \"CUDA error in \" << __FILE__ << \":\" << __LINE__ << \": \"     \\\n                << cudaGetErrorString(err) << \" (\" << err << \")\" << std::endl; \\\n      std::exit(EXIT_FAILURE);                                                 \\\n    }                                                                          \\\n  } while (0)\n\n#define CUDAQ_CHECK(call)                                                      \\\n  do {                                                                         \\\n    auto err = call;                                                           \\\n    if (err != CUDAQ_OK) {                                                     \\\n      std::cerr << \"CUDAQ error in \" << __FILE__ << \":\" << __LINE__ << \": \"    \\\n                << err << std::endl;                                           \\\n      std::exit(EXIT_FAILURE);                                                 \\\n    }                                                                          \\\n  } while (0)\n\n//==============================================================================\n// Test Handler: Simple noop that copies input to output and adds 1 to each byte\n//==============================================================================\n\n/// @brief Test handler that adds 1 to each byte.\n__device__ int increment_handler(const void *input, void *output,\n                                 std::uint32_t arg_len,\n                                 std::uint32_t max_result_len,\n                                 std::uint32_t *result_len) {\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = in_data[i] + 1;\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n//==============================================================================\n// Host API Dispatch Kernel Test Helpers\n//==============================================================================\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n__device__ int rpc_increment_handler(const void *input, void *output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t *result_len) {\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n__global__ void init_rpc_function_table(cudaq_function_entry_t *entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr =\n        reinterpret_cast<void *>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n\n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;   // Variable size\n    entries[0].schema.args[0].num_elements = 0; // Variable size\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;   // Variable size\n    entries[0].schema.results[0].num_elements = 0; // Variable size\n  }\n}\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t **host_flags_out,\n                          volatile uint64_t **device_flags_out,\n                          std::uint8_t **host_data_out,\n                          std::uint8_t **device_data_out) {\n  void *host_flags_ptr = nullptr;\n  cudaError_t err = cudaHostAlloc(&host_flags_ptr, num_slots * sizeof(uint64_t),\n                                  cudaHostAllocMapped);\n  if (err != cudaSuccess)\n    return false;\n\n  void *device_flags_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void *host_data_ptr = nullptr;\n  err =\n      cudaHostAlloc(&host_data_ptr, num_slots * slot_size, cudaHostAllocMapped);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void *device_data_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    cudaFreeHost(host_data_ptr);\n    return false;\n  }\n\n  memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t *>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t *>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t *>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t *>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t *host_flags, std::uint8_t *host_data) {\n  if (host_flags)\n    cudaFreeHost(const_cast<uint64_t *>(host_flags));\n  if (host_data)\n    cudaFreeHost(host_data);\n}\n\nextern \"C\" void launch_dispatch_kernel_wrapper(\n    volatile std::uint64_t *rx_flags, volatile std::uint64_t *tx_flags,\n    std::uint8_t *rx_data, std::uint8_t *tx_data, std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz, cudaq_function_entry_t *function_table,\n    std::size_t func_count, volatile int *shutdown_flag, std::uint64_t *stats,\n    std::size_t num_slots, std::uint32_t num_blocks,\n    std::uint32_t threads_per_block, cudaStream_t stream) {\n  cudaq_launch_dispatch_kernel_regular(\n      rx_flags, tx_flags, rx_data, tx_data, rx_stride_sz, tx_stride_sz,\n      function_table, func_count, shutdown_flag, stats, num_slots, num_blocks,\n      threads_per_block, stream);\n}\n\n//==============================================================================\n// Test Kernel for DeviceCallMode\n//==============================================================================\n\nusing HandlerFunc = int (*)(const void *, void *, std::uint32_t, std::uint32_t,\n                            std::uint32_t *);\n\n__device__ HandlerFunc d_increment_handler = increment_handler;\n\n//==============================================================================\n// Main\n//==============================================================================\nint main() {\n  constexpr std::size_t num_slots_ = 2;\n  constexpr std::size_t slot_size_ = 256;\n  volatile uint64_t *rx_flags_host = nullptr;\n  volatile uint64_t *tx_flags_host = nullptr;\n  volatile uint64_t *rx_flags_device = nullptr;\n  volatile uint64_t *tx_flags_device = nullptr;\n  std::uint8_t *rx_data_host = nullptr;\n  std::uint8_t *tx_data_host = nullptr;\n  std::uint8_t *rx_data_device = nullptr;\n  std::uint8_t *tx_data_device = nullptr;\n\n  volatile int *shutdown_flag_ = nullptr;\n  volatile int *d_shutdown_flag_ = nullptr;\n  uint64_t *d_stats_ = nullptr;\n\n  cudaq_function_entry_t *d_function_entries_ = nullptr;\n  std::size_t func_count_ = 0;\n\n  cudaq_dispatch_manager_t *manager_ = nullptr;\n  cudaq_dispatcher_t *dispatcher_ = nullptr;\n\n  const bool allocated_rx =\n      allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host,\n                           &rx_flags_device, &rx_data_host, &rx_data_device);\n\n  if (!allocated_rx) {\n    std::cerr << \"Failed to allocate RX ring buffer\" << std::endl;\n    return 1;\n  }\n  const bool allocated_tx =\n      allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host,\n                           &tx_flags_device, &tx_data_host, &tx_data_device);\n\n  if (!allocated_tx) {\n    std::cerr << \"Failed to allocate TX ring buffer\" << std::endl;\n    return 1;\n  }\n\n  void *tmp_shutdown = nullptr;\n  CUDA_CHECK(cudaHostAlloc(&tmp_shutdown, sizeof(int), cudaHostAllocMapped));\n  shutdown_flag_ = static_cast<volatile int *>(tmp_shutdown);\n  void *tmp_d_shutdown = nullptr;\n  CUDA_CHECK(cudaHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n  d_shutdown_flag_ = static_cast<volatile int *>(tmp_d_shutdown);\n  *shutdown_flag_ = 0;\n  int zero = 0;\n  CUDA_CHECK(cudaMemcpy(const_cast<int *>(d_shutdown_flag_), &zero, sizeof(int),\n                        cudaMemcpyHostToDevice));\n\n  CUDA_CHECK(cudaMalloc(&d_stats_, sizeof(uint64_t)));\n  CUDA_CHECK(cudaMemset(d_stats_, 0, sizeof(uint64_t)));\n\n  CUDA_CHECK(cudaMalloc(&d_function_entries_, sizeof(cudaq_function_entry_t)));\n  init_rpc_function_table<<<1, 1>>>(d_function_entries_);\n  CUDA_CHECK(cudaDeviceSynchronize());\n  func_count_ = 1;\n\n  CUDAQ_CHECK(cudaq_dispatch_manager_create(&manager_));\n  cudaq_dispatcher_config_t config{};\n  config.device_id = 0;\n  config.num_blocks = 1;\n  config.threads_per_block = 64;\n  config.num_slots = static_cast<uint32_t>(num_slots_);\n  config.slot_size = static_cast<uint32_t>(slot_size_);\n  config.vp_id = 0;\n  config.kernel_type = CUDAQ_KERNEL_REGULAR;\n  config.dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n  CUDAQ_CHECK(cudaq_dispatcher_create(manager_, &config, &dispatcher_));\n\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_device;\n  ringbuffer.tx_flags = tx_flags_device;\n  ringbuffer.rx_data = rx_data_device;\n  ringbuffer.tx_data = tx_data_device;\n  ringbuffer.rx_stride_sz = slot_size_;\n  ringbuffer.tx_stride_sz = slot_size_;\n  CUDAQ_CHECK(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer));\n\n  cudaq_function_table_t table{};\n  table.entries = d_function_entries_;\n  table.count = func_count_;\n  CUDAQ_CHECK(cudaq_dispatcher_set_function_table(dispatcher_, &table));\n\n  CUDAQ_CHECK(\n      cudaq_dispatcher_set_control(dispatcher_, d_shutdown_flag_, d_stats_));\n  CUDAQ_CHECK(cudaq_dispatcher_set_launch_fn(dispatcher_,\n                                             &launch_dispatch_kernel_wrapper));\n  CUDAQ_CHECK(cudaq_dispatcher_start(dispatcher_));\n\n  const auto write_rpc_request = [&](std::size_t slot,\n                                     const std::vector<std::uint8_t> &payload) {\n    std::uint8_t *slot_data =\n        const_cast<std::uint8_t *>(rx_data_host) + slot * slot_size_;\n    auto *header = reinterpret_cast<cudaq::realtime::RPCHeader *>(slot_data);\n    header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n    header->function_id = RPC_INCREMENT_FUNCTION_ID;\n    header->arg_len = static_cast<std::uint32_t>(payload.size());\n    memcpy(slot_data + sizeof(cudaq::realtime::RPCHeader), payload.data(),\n           payload.size());\n  };\n\n  const auto read_rpc_response = [&](std::size_t slot,\n                                     std::vector<std::uint8_t> &payload,\n                                     std::int32_t *status_out = nullptr,\n                                     std::uint32_t *result_len_out = nullptr) {\n    __sync_synchronize();\n    // Read from TX buffer (dispatch kernel writes response to symmetric TX)\n    const std::uint8_t *slot_data =\n        const_cast<std::uint8_t *>(tx_data_host) + slot * slot_size_;\n    auto *response =\n        reinterpret_cast<const cudaq::realtime::RPCResponse *>(slot_data);\n\n    if (response->magic != cudaq::realtime::RPC_MAGIC_RESPONSE)\n      return false;\n    if (status_out)\n      *status_out = response->status;\n    if (result_len_out)\n      *result_len_out = response->result_len;\n    if (response->status != 0)\n      return false;\n\n    payload.resize(response->result_len);\n    memcpy(payload.data(), slot_data + sizeof(cudaq::realtime::RPCResponse),\n           response->result_len);\n    return true;\n  };\n\n  // Sample payload: array of bytes\n  std::vector<std::uint8_t> payload = {0, 1, 2, 3};\n  write_rpc_request(0, payload);\n\n  std::cout << \"RPC request sent, waiting for response...\" << std::endl;\n  __sync_synchronize();\n  const_cast<volatile uint64_t *>(rx_flags_host)[0] =\n      reinterpret_cast<std::uint64_t>(rx_data_device);\n\n  int timeout = 50;\n  while (tx_flags_host[0] == 0 && timeout-- > 0) {\n    usleep(1000);\n  }\n\n  if (timeout <= 0) {\n    std::cerr << \"Timeout waiting for RPC response\" << std::endl;\n    return 1;\n  }\n\n  std::vector<std::uint8_t> response;\n  std::int32_t status = -1;\n  std::uint32_t result_len = 0;\n  read_rpc_response(0, response, &status, &result_len);\n  std::cout << \"RPC response received with status \" << status\n            << \" and result length \" << result_len << std::endl;\n\n  // Stop the dispatcher and clean up resources:\n  if (shutdown_flag_) {\n    *shutdown_flag_ = 1;\n    __sync_synchronize();\n  }\n  if (dispatcher_) {\n    cudaq_dispatcher_stop(dispatcher_);\n    cudaq_dispatcher_destroy(dispatcher_);\n    dispatcher_ = nullptr;\n  }\n  if (manager_) {\n    cudaq_dispatch_manager_destroy(manager_);\n    manager_ = nullptr;\n  }\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host);\n\n  if (shutdown_flag_)\n    cudaFreeHost(const_cast<int *>(shutdown_flag_));\n  if (d_stats_)\n    cudaFree(d_stats_);\n  if (d_function_entries_)\n    cudaFree(d_function_entries_);\n\n  bool valid = true;\n  for (std::size_t i = 0; i < result_len; ++i) {\n    std::cout << \"Response byte \" << i << \": \" << static_cast<int>(response[i])\n              << \"; expected: \" << static_cast<int>(payload[i] + 1)\n              << std::endl;\n    if (response[i] != static_cast<std::uint8_t>(payload[i] + 1))\n      valid = false;\n  }\n\n  if (valid) {\n    std::cout << \"Response is valid!\" << std::endl;\n  } else {\n    std::cerr << \"Response is invalid!\" << std::endl;\n    return 1;\n  }\n  return 0;\n}\n", "output": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file dispatch_kernel.cu\n/// @brief Simple dispatch kernel for testing libcudaq-realtime.\n///\n/// This example demonstrates a simple dispatch kernel that processes RPC\n/// requests.\n///\n\n#include <cstdint>\n#include <cstring>\n#include <hip/hip_runtime.h>\n#include <iostream>\n#include <unistd.h>\n#include <vector>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n\n// Helper macro for CUDA error checking\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    hipError_t err = call;                                                    \\\n    if (err != hipSuccess) {                                                  \\\n      std::cerr << \"CUDA error in \" << __FILE__ << \":\" << __LINE__ << \": \"     \\\n                << hipGetErrorString(err) << \" (\" << err << \")\" << std::endl; \\\n      std::exit(EXIT_FAILURE);                                                 \\\n    }                                                                          \\\n  } while (0)\n\n#define CUDAQ_CHECK(call)                                                      \\\n  do {                                                                         \\\n    auto err = call;                                                           \\\n    if (err != CUDAQ_OK) {                                                     \\\n      std::cerr << \"CUDAQ error in \" << __FILE__ << \":\" << __LINE__ << \": \"    \\\n                << err << std::endl;                                           \\\n      std::exit(EXIT_FAILURE);                                                 \\\n    }                                                                          \\\n  } while (0)\n\n//==============================================================================\n// Test Handler: Simple noop that copies input to output and adds 1 to each byte\n//==============================================================================\n\n/// @brief Test handler that adds 1 to each byte.\n__device__ int increment_handler(const void *input, void *output,\n                                 std::uint32_t arg_len,\n                                 std::uint32_t max_result_len,\n                                 std::uint32_t *result_len) {\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = in_data[i] + 1;\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n//==============================================================================\n// Host API Dispatch Kernel Test Helpers\n//==============================================================================\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n__device__ int rpc_increment_handler(const void *input, void *output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t *result_len) {\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n__global__ void init_rpc_function_table(cudaq_function_entry_t *entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr =\n        reinterpret_cast<void *>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n\n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;   // Variable size\n    entries[0].schema.args[0].num_elements = 0; // Variable size\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;   // Variable size\n    entries[0].schema.results[0].num_elements = 0; // Variable size\n  }\n}\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t **host_flags_out,\n                          volatile uint64_t **device_flags_out,\n                          std::uint8_t **host_data_out,\n                          std::uint8_t **device_data_out) {\n  void *host_flags_ptr = nullptr;\n  hipError_t err = hipHostAlloc(&host_flags_ptr, num_slots * sizeof(uint64_t),\n                                  hipHostMallocMapped);\n  if (err != hipSuccess)\n    return false;\n\n  void *device_flags_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void *host_data_ptr = nullptr;\n  err =\n      hipHostAlloc(&host_data_ptr, num_slots * slot_size, hipHostMallocMapped);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void *device_data_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    hipHostFree(host_data_ptr);\n    return false;\n  }\n\n  memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t *>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t *>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t *>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t *>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t *host_flags, std::uint8_t *host_data) {\n  if (host_flags)\n    hipHostFree(const_cast<uint64_t *>(host_flags));\n  if (host_data)\n    hipHostFree(host_data);\n}\n\nextern \"C\" void launch_dispatch_kernel_wrapper(\n    volatile std::uint64_t *rx_flags, volatile std::uint64_t *tx_flags,\n    std::uint8_t *rx_data, std::uint8_t *tx_data, std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz, cudaq_function_entry_t *function_table,\n    std::size_t func_count, volatile int *shutdown_flag, std::uint64_t *stats,\n    std::size_t num_slots, std::uint32_t num_blocks,\n    std::uint32_t threads_per_block, hipStream_t stream) {\n  cudaq_launch_dispatch_kernel_regular(\n      rx_flags, tx_flags, rx_data, tx_data, rx_stride_sz, tx_stride_sz,\n      function_table, func_count, shutdown_flag, stats, num_slots, num_blocks,\n      threads_per_block, stream);\n}\n\n//==============================================================================\n// Test Kernel for DeviceCallMode\n//==============================================================================\n\nusing HandlerFunc = int (*)(const void *, void *, std::uint32_t, std::uint32_t,\n                            std::uint32_t *);\n\n__device__ HandlerFunc d_increment_handler = increment_handler;\n\n//==============================================================================\n// Main\n//==============================================================================\nint main() {\n  constexpr std::size_t num_slots_ = 2;\n  constexpr std::size_t slot_size_ = 256;\n  volatile uint64_t *rx_flags_host = nullptr;\n  volatile uint64_t *tx_flags_host = nullptr;\n  volatile uint64_t *rx_flags_device = nullptr;\n  volatile uint64_t *tx_flags_device = nullptr;\n  std::uint8_t *rx_data_host = nullptr;\n  std::uint8_t *tx_data_host = nullptr;\n  std::uint8_t *rx_data_device = nullptr;\n  std::uint8_t *tx_data_device = nullptr;\n\n  volatile int *shutdown_flag_ = nullptr;\n  volatile int *d_shutdown_flag_ = nullptr;\n  uint64_t *d_stats_ = nullptr;\n\n  cudaq_function_entry_t *d_function_entries_ = nullptr;\n  std::size_t func_count_ = 0;\n\n  cudaq_dispatch_manager_t *manager_ = nullptr;\n  cudaq_dispatcher_t *dispatcher_ = nullptr;\n\n  const bool allocated_rx =\n      allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host,\n                           &rx_flags_device, &rx_data_host, &rx_data_device);\n\n  if (!allocated_rx) {\n    std::cerr << \"Failed to allocate RX ring buffer\" << std::endl;\n    return 1;\n  }\n  const bool allocated_tx =\n      allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host,\n                           &tx_flags_device, &tx_data_host, &tx_data_device);\n\n  if (!allocated_tx) {\n    std::cerr << \"Failed to allocate TX ring buffer\" << std::endl;\n    return 1;\n  }\n\n  void *tmp_shutdown = nullptr;\n  CUDA_CHECK(hipHostAlloc(&tmp_shutdown, sizeof(int), hipHostMallocMapped));\n  shutdown_flag_ = static_cast<volatile int *>(tmp_shutdown);\n  void *tmp_d_shutdown = nullptr;\n  CUDA_CHECK(hipHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n  d_shutdown_flag_ = static_cast<volatile int *>(tmp_d_shutdown);\n  *shutdown_flag_ = 0;\n  int zero = 0;\n  CUDA_CHECK(hipMemcpy(const_cast<int *>(d_shutdown_flag_), &zero, sizeof(int),\n                        hipMemcpyHostToDevice));\n\n  CUDA_CHECK(hipMalloc(&d_stats_, sizeof(uint64_t)));\n  CUDA_CHECK(hipMemset(d_stats_, 0, sizeof(uint64_t)));\n\n  CUDA_CHECK(hipMalloc(&d_function_entries_, sizeof(cudaq_function_entry_t)));\n  init_rpc_function_table<<<1, 1>>>(d_function_entries_);\n  CUDA_CHECK(hipDeviceSynchronize());\n  func_count_ = 1;\n\n  CUDAQ_CHECK(cudaq_dispatch_manager_create(&manager_));\n  cudaq_dispatcher_config_t config{};\n  config.device_id = 0;\n  config.num_blocks = 1;\n  config.threads_per_block = 64;\n  config.num_slots = static_cast<uint32_t>(num_slots_);\n  config.slot_size = static_cast<uint32_t>(slot_size_);\n  config.vp_id = 0;\n  config.kernel_type = CUDAQ_KERNEL_REGULAR;\n  config.dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n  CUDAQ_CHECK(cudaq_dispatcher_create(manager_, &config, &dispatcher_));\n\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_device;\n  ringbuffer.tx_flags = tx_flags_device;\n  ringbuffer.rx_data = rx_data_device;\n  ringbuffer.tx_data = tx_data_device;\n  ringbuffer.rx_stride_sz = slot_size_;\n  ringbuffer.tx_stride_sz = slot_size_;\n  CUDAQ_CHECK(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer));\n\n  cudaq_function_table_t table{};\n  table.entries = d_function_entries_;\n  table.count = func_count_;\n  CUDAQ_CHECK(cudaq_dispatcher_set_function_table(dispatcher_, &table));\n\n  CUDAQ_CHECK(\n      cudaq_dispatcher_set_control(dispatcher_, d_shutdown_flag_, d_stats_));\n  CUDAQ_CHECK(cudaq_dispatcher_set_launch_fn(dispatcher_,\n                                             &launch_dispatch_kernel_wrapper));\n  CUDAQ_CHECK(cudaq_dispatcher_start(dispatcher_));\n\n  const auto write_rpc_request = [&](std::size_t slot,\n                                     const std::vector<std::uint8_t> &payload) {\n    std::uint8_t *slot_data =\n        const_cast<std::uint8_t *>(rx_data_host) + slot * slot_size_;\n    auto *header = reinterpret_cast<cudaq::realtime::RPCHeader *>(slot_data);\n    header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n    header->function_id = RPC_INCREMENT_FUNCTION_ID;\n    header->arg_len = static_cast<std::uint32_t>(payload.size());\n    memcpy(slot_data + sizeof(cudaq::realtime::RPCHeader), payload.data(),\n           payload.size());\n  };\n\n  const auto read_rpc_response = [&](std::size_t slot,\n                                     std::vector<std::uint8_t> &payload,\n                                     std::int32_t *status_out = nullptr,\n                                     std::uint32_t *result_len_out = nullptr) {\n    __sync_synchronize();\n    // Read from TX buffer (dispatch kernel writes response to symmetric TX)\n    const std::uint8_t *slot_data =\n        const_cast<std::uint8_t *>(tx_data_host) + slot * slot_size_;\n    auto *response =\n        reinterpret_cast<const cudaq::realtime::RPCResponse *>(slot_data);\n\n    if (response->magic != cudaq::realtime::RPC_MAGIC_RESPONSE)\n      return false;\n    if (status_out)\n      *status_out = response->status;\n    if (result_len_out)\n      *result_len_out = response->result_len;\n    if (response->status != 0)\n      return false;\n\n    payload.resize(response->result_len);\n    memcpy(payload.data(), slot_data + sizeof(cudaq::realtime::RPCResponse),\n           response->result_len);\n    return true;\n  };\n\n  // Sample payload: array of bytes\n  std::vector<std::uint8_t> payload = {0, 1, 2, 3};\n  write_rpc_request(0, payload);\n\n  std::cout << \"RPC request sent, waiting for response...\" << std::endl;\n  __sync_synchronize();\n  const_cast<volatile uint64_t *>(rx_flags_host)[0] =\n      reinterpret_cast<std::uint64_t>(rx_data_device);\n\n  int timeout = 50;\n  while (tx_flags_host[0] == 0 && timeout-- > 0) {\n    usleep(1000);\n  }\n\n  if (timeout <= 0) {\n    std::cerr << \"Timeout waiting for RPC response\" << std::endl;\n    return 1;\n  }\n\n  std::vector<std::uint8_t> response;\n  std::int32_t status = -1;\n  std::uint32_t result_len = 0;\n  read_rpc_response(0, response, &status, &result_len);\n  std::cout << \"RPC response received with status \" << status\n            << \" and result length \" << result_len << std::endl;\n\n  // Stop the dispatcher and clean up resources:\n  if (shutdown_flag_) {\n    *shutdown_flag_ = 1;\n    __sync_synchronize();\n  }\n  if (dispatcher_) {\n    cudaq_dispatcher_stop(dispatcher_);\n    cudaq_dispatcher_destroy(dispatcher_);\n    dispatcher_ = nullptr;\n  }\n  if (manager_) {\n    cudaq_dispatch_manager_destroy(manager_);\n    manager_ = nullptr;\n  }\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host);\n\n  if (shutdown_flag_)\n    hipHostFree(const_cast<int *>(shutdown_flag_));\n  if (d_stats_)\n    hipFree(d_stats_);\n  if (d_function_entries_)\n    hipFree(d_function_entries_);\n\n  bool valid = true;\n  for (std::size_t i = 0; i < result_len; ++i) {\n    std::cout << \"Response byte \" << i << \": \" << static_cast<int>(response[i])\n              << \"; expected: \" << static_cast<int>(payload[i] + 1)\n              << std::endl;\n    if (response[i] != static_cast<std::uint8_t>(payload[i] + 1))\n      valid = false;\n  }\n\n  if (valid) {\n    std::cout << \"Response is valid!\" << std::endl;\n  } else {\n    std::cerr << \"Response is invalid!\" << std::endl;\n    return 1;\n  }\n  return 0;\n}\n", "source": "/kaggle/working/cuda-quantum/realtime/examples/gpu_dispatch/dispatch_kernel.cu"}
{"input": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#include <gtest/gtest.h>\n#include <cuda_runtime.h>\n#include <cstdint>\n#include <vector>\n#include <cstring>\n#include <unistd.h>\n#include <iostream>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n\n// Helper macro for CUDA error checking\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    cudaError_t err = call;                                                    \\\n    ASSERT_EQ(err, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(err);  \\\n  } while (0)\n\nnamespace {\n\n//==============================================================================\n// Test Handler: Simple noop that copies input to output\n//==============================================================================\n\n/// @brief Test handler that adds 1 to each byte.\n__device__ int increment_handler(const void* input, void* output,\n                                  std::uint32_t arg_len,\n                                  std::uint32_t max_result_len,\n                                  std::uint32_t* result_len) {\n  const std::uint8_t* in_data = static_cast<const std::uint8_t*>(input);\n  std::uint8_t* out_data = static_cast<std::uint8_t*>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = in_data[i] + 1;\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n//==============================================================================\n// Host API Dispatch Kernel Test Helpers\n//==============================================================================\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n__device__ int rpc_increment_handler(const void* input, void* output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t* result_len) {\n  const std::uint8_t* in_data = static_cast<const std::uint8_t*>(input);\n  std::uint8_t* out_data = static_cast<std::uint8_t*>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n__global__ void init_rpc_function_table(cudaq_function_entry_t* entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr = reinterpret_cast<void*>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n    \n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;  // Variable size\n    entries[0].schema.args[0].num_elements = 0; // Variable size\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;  // Variable size\n    entries[0].schema.results[0].num_elements = 0; // Variable size\n  }\n}\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t** host_flags_out,\n                          volatile uint64_t** device_flags_out,\n                          std::uint8_t** host_data_out,\n                          std::uint8_t** device_data_out) {\n  void* host_flags_ptr = nullptr;\n  cudaError_t err = cudaHostAlloc(&host_flags_ptr,\n                                  num_slots * sizeof(uint64_t),\n                                  cudaHostAllocMapped);\n  if (err != cudaSuccess)\n    return false;\n\n  void* device_flags_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void* host_data_ptr = nullptr;\n  err = cudaHostAlloc(&host_data_ptr,\n                      num_slots * slot_size,\n                      cudaHostAllocMapped);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void* device_data_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    cudaFreeHost(host_data_ptr);\n    return false;\n  }\n\n  memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t*>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t*>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t*>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t*>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t* host_flags,\n                      std::uint8_t* host_data) {\n  if (host_flags)\n    cudaFreeHost(const_cast<uint64_t*>(host_flags));\n  if (host_data)\n    cudaFreeHost(host_data);\n}\n\nextern \"C\" void launch_dispatch_kernel_wrapper(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    cudaStream_t stream) {\n  cudaq_launch_dispatch_kernel_regular(\n      rx_flags, tx_flags, rx_data, tx_data, rx_stride_sz, tx_stride_sz,\n      function_table, func_count,\n      shutdown_flag, stats, num_slots, num_blocks, threads_per_block, stream);\n}\n\n//==============================================================================\n// Test Kernel for DeviceCallMode\n//==============================================================================\n\nusing HandlerFunc = int (*)(const void*, void*, std::uint32_t, std::uint32_t, std::uint32_t*);\n\n__device__ HandlerFunc d_increment_handler = increment_handler;\n\n/// @brief Test kernel that dispatches to a handler using DeviceCallMode.\ntemplate <typename KernelType>\n__global__ void test_dispatch_kernel(\n    HandlerFunc handler,\n    const void* input,\n    void* output,\n    std::uint32_t arg_len,\n    std::uint32_t max_result_len,\n    std::uint32_t* result_len,\n    int* status) {\n  \n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    *status = handler(input, output, arg_len, max_result_len, result_len);\n  }\n  \n  KernelType::sync();\n}\n\n//==============================================================================\n// Test Fixture\n//==============================================================================\n\nclass DispatchKernelTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    CUDA_CHECK(cudaMalloc(&d_buffer_, 1024));\n    CUDA_CHECK(cudaMalloc(&d_result_len_, sizeof(std::uint32_t)));\n    CUDA_CHECK(cudaMalloc(&d_status_, sizeof(int)));\n  }\n  \n  void TearDown() override {\n    if (d_buffer_) cudaFree(d_buffer_);\n    if (d_result_len_) cudaFree(d_result_len_);\n    if (d_status_) cudaFree(d_status_);\n  }\n  \n  void* d_buffer_ = nullptr;\n  std::uint32_t* d_result_len_ = nullptr;\n  int* d_status_ = nullptr;\n};\n\n//==============================================================================\n// Tests\n//==============================================================================\n\nTEST_F(DispatchKernelTest, IncrementHandlerBasic) {\n  // Prepare test data - separate input and output buffers\n  std::vector<uint8_t> input = {0, 1, 2, 3, 4};\n  std::vector<uint8_t> expected = {1, 2, 3, 4, 5};\n\n  void* d_input = nullptr;\n  CUDA_CHECK(cudaMalloc(&d_input, 1024));\n  CUDA_CHECK(cudaMemcpy(d_input, input.data(), input.size(), \n                        cudaMemcpyHostToDevice));\n  \n  // Get device function pointer\n  HandlerFunc h_handler;\n  CUDA_CHECK(cudaMemcpyFromSymbol(&h_handler, d_increment_handler, \n                                   sizeof(HandlerFunc)));\n  \n  // Launch kernel with separate input/output buffers\n  test_dispatch_kernel<cudaq::realtime::RegularKernel><<<1, 32>>>(\n      h_handler, d_input, d_buffer_, input.size(), 1024, d_result_len_, d_status_);\n  CUDA_CHECK(cudaGetLastError());\n  CUDA_CHECK(cudaDeviceSynchronize());\n  \n  // Check results\n  int status;\n  std::uint32_t result_len;\n  CUDA_CHECK(cudaMemcpy(&status, d_status_, sizeof(int), cudaMemcpyDeviceToHost));\n  CUDA_CHECK(cudaMemcpy(&result_len, d_result_len_, sizeof(std::uint32_t), \n                        cudaMemcpyDeviceToHost));\n  \n  EXPECT_EQ(status, 0) << \"Handler should return success\";\n  EXPECT_EQ(result_len, input.size()) << \"Result length should match input\";\n  \n  // Verify output buffer has incremented data\n  std::vector<uint8_t> output(input.size());\n  CUDA_CHECK(cudaMemcpy(output.data(), d_buffer_, output.size(), \n                        cudaMemcpyDeviceToHost));\n  EXPECT_EQ(expected, output) << \"Increment handler should add 1 to each byte\";\n\n  // Verify input buffer is unchanged\n  std::vector<uint8_t> input_readback(input.size());\n  CUDA_CHECK(cudaMemcpy(input_readback.data(), d_input, input.size(),\n                        cudaMemcpyDeviceToHost));\n  EXPECT_EQ(input, input_readback) << \"Input buffer should be unchanged\";\n\n  cudaFree(d_input);\n}\n\nTEST_F(DispatchKernelTest, LargeBuffer) {\n  // Test with larger data - separate input/output buffers\n  const std::size_t size = 512;\n  std::vector<uint8_t> input(size);\n  for (std::size_t i = 0; i < size; ++i) {\n    input[i] = static_cast<uint8_t>(i & 0xFF);\n  }\n  \n  void* d_input = nullptr;\n  CUDA_CHECK(cudaMalloc(&d_input, 1024));\n  CUDA_CHECK(cudaMemcpy(d_input, input.data(), input.size(), \n                        cudaMemcpyHostToDevice));\n  \n  HandlerFunc h_handler;\n  CUDA_CHECK(cudaMemcpyFromSymbol(&h_handler, d_increment_handler, \n                                   sizeof(HandlerFunc)));\n  \n  test_dispatch_kernel<cudaq::realtime::RegularKernel><<<1, 256>>>(\n      h_handler, d_input, d_buffer_, input.size(), 1024, d_result_len_, d_status_);\n  CUDA_CHECK(cudaGetLastError());\n  CUDA_CHECK(cudaDeviceSynchronize());\n  \n  std::uint32_t result_len;\n  CUDA_CHECK(cudaMemcpy(&result_len, d_result_len_, sizeof(std::uint32_t), \n                        cudaMemcpyDeviceToHost));\n  EXPECT_EQ(result_len, size) << \"Should process all bytes\";\n  \n  // Verify all bytes incremented in output buffer\n  std::vector<uint8_t> output(size);\n  CUDA_CHECK(cudaMemcpy(output.data(), d_buffer_, output.size(), \n                        cudaMemcpyDeviceToHost));\n  \n  for (std::size_t i = 0; i < size; ++i) {\n    uint8_t expected = static_cast<uint8_t>((i + 1) & 0xFF);\n    EXPECT_EQ(output[i], expected) << \"Mismatch at index \" << i;\n  }\n\n  cudaFree(d_input);\n}\n\nclass HostApiDispatchTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_, &rx_data_host_, &rx_data_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_, &tx_data_host_, &tx_data_));\n\n    void* tmp_shutdown = nullptr;\n    CUDA_CHECK(cudaHostAlloc(&tmp_shutdown, sizeof(int), cudaHostAllocMapped));\n    shutdown_flag_ = static_cast<volatile int*>(tmp_shutdown);\n    void* tmp_d_shutdown = nullptr;\n    CUDA_CHECK(cudaHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n    d_shutdown_flag_ = static_cast<volatile int*>(tmp_d_shutdown);\n    *shutdown_flag_ = 0;\n    int zero = 0;\n    CUDA_CHECK(cudaMemcpy(const_cast<int*>(d_shutdown_flag_), &zero,\n                          sizeof(int), cudaMemcpyHostToDevice));\n\n    CUDA_CHECK(cudaMalloc(&d_stats_, sizeof(uint64_t)));\n    CUDA_CHECK(cudaMemset(d_stats_, 0, sizeof(uint64_t)));\n\n    CUDA_CHECK(cudaMalloc(&d_function_entries_, sizeof(cudaq_function_entry_t)));\n    init_rpc_function_table<<<1, 1>>>(d_function_entries_);\n    CUDA_CHECK(cudaDeviceSynchronize());\n    func_count_ = 1;\n\n    ASSERT_EQ(cudaq_dispatch_manager_create(&manager_), CUDAQ_OK);\n    cudaq_dispatcher_config_t config{};\n    config.device_id = 0;\n    config.num_blocks = 1;\n    config.threads_per_block = 64;\n    config.num_slots = static_cast<uint32_t>(num_slots_);\n    config.slot_size = static_cast<uint32_t>(slot_size_);\n    config.vp_id = 0;\n    config.kernel_type = CUDAQ_KERNEL_REGULAR;\n    config.dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    ASSERT_EQ(cudaq_dispatcher_create(manager_, &config, &dispatcher_), CUDAQ_OK);\n\n    cudaq_ringbuffer_t ringbuffer{};\n    ringbuffer.rx_flags = rx_flags_;\n    ringbuffer.tx_flags = tx_flags_;\n    ringbuffer.rx_data = rx_data_;\n    ringbuffer.tx_data = tx_data_;\n    ringbuffer.rx_stride_sz = slot_size_;\n    ringbuffer.tx_stride_sz = slot_size_;\n    ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer), CUDAQ_OK);\n\n    cudaq_function_table_t table{};\n    table.entries = d_function_entries_;\n    table.count = func_count_;\n    ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher_, &table), CUDAQ_OK);\n\n    ASSERT_EQ(\n        cudaq_dispatcher_set_control(dispatcher_, d_shutdown_flag_, d_stats_),\n        CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_set_launch_fn(dispatcher_,\n                                             &launch_dispatch_kernel_wrapper),\n              CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_start(dispatcher_), CUDAQ_OK);\n  }\n\n  void TearDown() override {\n    if (shutdown_flag_) {\n      *shutdown_flag_ = 1;\n      __sync_synchronize();\n    }\n    if (dispatcher_) {\n      cudaq_dispatcher_stop(dispatcher_);\n      cudaq_dispatcher_destroy(dispatcher_);\n      dispatcher_ = nullptr;\n    }\n    if (manager_) {\n      cudaq_dispatch_manager_destroy(manager_);\n      manager_ = nullptr;\n    }\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n\n    if (shutdown_flag_)\n      cudaFreeHost(const_cast<int*>(shutdown_flag_));\n    if (d_stats_)\n      cudaFree(d_stats_);\n    if (d_function_entries_)\n      cudaFree(d_function_entries_);\n  }\n\n  void write_rpc_request(std::size_t slot,\n                         const std::vector<std::uint8_t>& payload) {\n    std::uint8_t* slot_data =\n        const_cast<std::uint8_t*>(rx_data_host_) + slot * slot_size_;\n    auto* header = reinterpret_cast<cudaq::realtime::RPCHeader*>(slot_data);\n    header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n    header->function_id = RPC_INCREMENT_FUNCTION_ID;\n    header->arg_len = static_cast<std::uint32_t>(payload.size());\n    memcpy(slot_data + sizeof(cudaq::realtime::RPCHeader), payload.data(),\n           payload.size());\n  }\n\n  bool read_rpc_response(std::size_t slot,\n                         std::vector<std::uint8_t>& payload,\n                         std::int32_t* status_out = nullptr,\n                         std::uint32_t* result_len_out = nullptr) {\n    __sync_synchronize();\n    // Read from TX buffer (dispatch kernel writes response to symmetric TX)\n    const std::uint8_t* slot_data =\n        const_cast<std::uint8_t*>(tx_data_host_) + slot * slot_size_;\n    auto* response =\n        reinterpret_cast<const cudaq::realtime::RPCResponse*>(slot_data);\n\n    if (response->magic != cudaq::realtime::RPC_MAGIC_RESPONSE)\n      return false;\n    if (status_out)\n      *status_out = response->status;\n    if (result_len_out)\n      *result_len_out = response->result_len;\n    if (response->status != 0)\n      return false;\n\n    payload.resize(response->result_len);\n    memcpy(payload.data(),\n           slot_data + sizeof(cudaq::realtime::RPCResponse),\n           response->result_len);\n    return true;\n  }\n\n  static constexpr std::size_t num_slots_ = 2;\n  std::size_t slot_size_ = 256;\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_ = nullptr;\n  volatile uint64_t* tx_flags_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_ = nullptr;\n  std::uint8_t* tx_data_ = nullptr;\n\n  volatile int* shutdown_flag_ = nullptr;\n  volatile int* d_shutdown_flag_ = nullptr;\n  uint64_t* d_stats_ = nullptr;\n\n  cudaq_function_entry_t* d_function_entries_ = nullptr;\n  std::size_t func_count_ = 0;\n\n  cudaq_dispatch_manager_t* manager_ = nullptr;\n  cudaq_dispatcher_t* dispatcher_ = nullptr;\n};\n\nTEST_F(HostApiDispatchTest, RpcIncrementHandler) {\n  std::vector<std::uint8_t> payload = {0, 1, 2, 3};\n  write_rpc_request(0, payload);\n\n  __sync_synchronize();\n  const_cast<volatile uint64_t*>(rx_flags_host_)[0] =\n      reinterpret_cast<std::uint64_t>(rx_data_);\n\n  int timeout = 50;\n  while (tx_flags_host_[0] == 0 && timeout-- > 0) {\n    usleep(1000);\n  }\n  ASSERT_GT(timeout, 0) << \"Timeout waiting for dispatch kernel response\";\n\n  std::vector<std::uint8_t> response;\n  std::int32_t status = -1;\n  std::uint32_t result_len = 0;\n  ASSERT_TRUE(read_rpc_response(0, response, &status, &result_len));\n  EXPECT_EQ(status, 0);\n  ASSERT_EQ(result_len, payload.size());\n\n  std::vector<std::uint8_t> expected = {1, 2, 3, 4};\n  EXPECT_EQ(response, expected);\n}\n\n//==============================================================================\n// Graph Launch Test\n//==============================================================================\n\n// Graph kernel that processes RPC buffer via pointer indirection\n__global__ void graph_increment_kernel(void** buffer_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *buffer_ptr;\n    cudaq::realtime::RPCHeader* header = static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    \n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    \n    // Increment each byte\n    for (std::uint32_t i = 0; i < arg_len; ++i) {\n      data[i] = data[i] + 1;\n    }\n    \n    // Write response\n    cudaq::realtime::RPCResponse* response = static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_increment\");\n\n__global__ void init_graph_function_table(cudaq_function_entry_t* entries, \n                                          cudaGraphExec_t graph_exec) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.graph_exec = graph_exec;\n    entries[0].function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n  }\n}\n\nTEST(GraphLaunchTest, DispatchKernelGraphLaunch) {\n  // Check compute capability\n  int device;\n  CUDA_CHECK(cudaGetDevice(&device));\n  cudaDeviceProp prop;\n  CUDA_CHECK(cudaGetDeviceProperties(&prop, device));\n  \n  if (prop.major < 9) {\n    GTEST_SKIP() << \"Graph device launch requires compute capability 9.0+, found \" \n                 << prop.major << \".\" << prop.minor;\n  }\n  \n  // Allocate graph buffer pointer (for pointer indirection pattern)\n  void** d_graph_buffer_ptr;\n  CUDA_CHECK(cudaMalloc(&d_graph_buffer_ptr, sizeof(void*)));\n  CUDA_CHECK(cudaMemset(d_graph_buffer_ptr, 0, sizeof(void*)));\n  \n  // Allocate test buffer\n  constexpr size_t buffer_size = 1024;\n  void* d_buffer;\n  CUDA_CHECK(cudaMalloc(&d_buffer, buffer_size));\n  \n  // Create the child graph (the one that will be launched from device)\n  cudaGraph_t child_graph;\n  cudaGraphExec_t child_graph_exec;\n  \n  CUDA_CHECK(cudaGraphCreate(&child_graph, 0));\n  \n  // Add kernel node to child graph\n  cudaKernelNodeParams kernel_params = {};\n  void* kernel_args[] = {&d_graph_buffer_ptr};\n  kernel_params.func = reinterpret_cast<void*>(&graph_increment_kernel);\n  kernel_params.gridDim = dim3(1, 1, 1);\n  kernel_params.blockDim = dim3(32, 1, 1);\n  kernel_params.sharedMemBytes = 0;\n  kernel_params.kernelParams = kernel_args;\n  kernel_params.extra = nullptr;\n  \n  cudaGraphNode_t kernel_node;\n  CUDA_CHECK(cudaGraphAddKernelNode(&kernel_node, child_graph, nullptr, 0, &kernel_params));\n  \n  // Instantiate CHILD graph with DEVICE LAUNCH FLAG\n  CUDA_CHECK(cudaGraphInstantiate(&child_graph_exec, child_graph,  \n                                   cudaGraphInstantiateFlagDeviceLaunch));\n  \n  // Create stream for operations\n  cudaStream_t stream;\n  CUDA_CHECK(cudaStreamCreate(&stream));\n  \n  // Upload the child graph to device\n  CUDA_CHECK(cudaGraphUpload(child_graph_exec, stream));\n  CUDA_CHECK(cudaStreamSynchronize(stream));\n  \n  // Set up function table with graph launch entry\n  cudaq_function_entry_t* d_function_entries;\n  CUDA_CHECK(cudaMalloc(&d_function_entries, sizeof(cudaq_function_entry_t)));\n  init_graph_function_table<<<1, 1>>>(d_function_entries, child_graph_exec);\n  CUDA_CHECK(cudaDeviceSynchronize());\n  \n  // Set up RPC buffer on host\n  std::uint8_t* h_buffer = new std::uint8_t[buffer_size];\n  cudaq::realtime::RPCHeader* h_header = reinterpret_cast<cudaq::realtime::RPCHeader*>(h_buffer);\n  h_header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n  h_header->function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n  h_header->arg_len = 4;\n  \n  std::uint8_t* h_data = h_buffer + sizeof(cudaq::realtime::RPCHeader);\n  h_data[0] = 0;\n  h_data[1] = 1;\n  h_data[2] = 2;\n  h_data[3] = 3;\n  \n  // Copy to device\n  CUDA_CHECK(cudaMemcpy(d_buffer, h_buffer, buffer_size, cudaMemcpyHostToDevice));\n  \n  // Set up fake RX/TX flags for single-shot test\n  volatile uint64_t* d_rx_flags;\n  volatile uint64_t* d_tx_flags;\n  CUDA_CHECK(cudaMalloc(&d_rx_flags, sizeof(uint64_t)));\n  CUDA_CHECK(cudaMalloc(&d_tx_flags, sizeof(uint64_t)));\n  CUDA_CHECK(cudaMemset((void*)d_rx_flags, 0, sizeof(uint64_t)));\n  CUDA_CHECK(cudaMemset((void*)d_tx_flags, 0, sizeof(uint64_t)));\n  \n  // Set RX flag to point to our buffer (simulating incoming RPC)\n  uint64_t buffer_addr = reinterpret_cast<uint64_t>(d_buffer);\n  CUDA_CHECK(cudaMemcpy((void*)d_rx_flags, &buffer_addr, sizeof(uint64_t), cudaMemcpyHostToDevice));\n  \n  // Set up shutdown flag using pinned mapped memory so the dispatch kernel\n  // can see host updates immediately\n  volatile int* h_shutdown;\n  volatile int* d_shutdown;\n  {\n    void* tmp_shutdown;\n    CUDA_CHECK(cudaHostAlloc(&tmp_shutdown, sizeof(int), cudaHostAllocMapped));\n    h_shutdown = static_cast<volatile int*>(tmp_shutdown);\n    *h_shutdown = 0;\n    \n    void* tmp_d_shutdown;\n    CUDA_CHECK(cudaHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n    d_shutdown = static_cast<volatile int*>(tmp_d_shutdown);\n  }\n  \n  // Set up stats\n  uint64_t* d_stats;\n  CUDA_CHECK(cudaMalloc(&d_stats, sizeof(uint64_t)));\n  CUDA_CHECK(cudaMemset(d_stats, 0, sizeof(uint64_t)));\n  \n  // Create dispatch graph context - THIS WRAPS THE DISPATCH KERNEL IN A GRAPH\n  // so that device-side cudaGraphLaunch() can work!\n  cudaq_dispatch_graph_context* dispatch_ctx = nullptr;\n  cudaError_t err = cudaq_create_dispatch_graph_regular(\n      d_rx_flags, d_tx_flags,\n      reinterpret_cast<std::uint8_t*>(d_buffer),  // rx_data\n      reinterpret_cast<std::uint8_t*>(d_buffer),  // tx_data (same buffer for single-slot test)\n      buffer_size,  // rx_stride_sz\n      buffer_size,  // tx_stride_sz\n      d_function_entries, 1,\n      d_graph_buffer_ptr, d_shutdown, d_stats, 1,\n      1, 32, stream, &dispatch_ctx);\n  \n  if (err != cudaSuccess) {\n    GTEST_SKIP() << \"Device-side graph launch not supported: \" \n                 << cudaGetErrorString(err) << \" (\" << err << \")\";\n  }\n  \n  // Launch dispatch graph - now device-side cudaGraphLaunch will work!\n  CUDA_CHECK(cudaq_launch_dispatch_graph(dispatch_ctx, stream));\n  \n  // Poll for the response using pinned memory and async operations\n  // The child graph runs asynchronously (fire-and-forget) so we need to poll\n  std::uint8_t* h_poll_buffer;\n  CUDA_CHECK(cudaHostAlloc(&h_poll_buffer, sizeof(cudaq::realtime::RPCResponse), cudaHostAllocDefault));\n  memset(h_poll_buffer, 0, sizeof(cudaq::realtime::RPCResponse));\n  \n  cudaStream_t poll_stream;\n  CUDA_CHECK(cudaStreamCreate(&poll_stream));\n  \n  int timeout_ms = 5000;\n  int poll_interval_ms = 100;\n  bool got_response = false;\n  \n  for (int elapsed = 0; elapsed < timeout_ms; elapsed += poll_interval_ms) {\n    CUDA_CHECK(cudaMemcpyAsync(h_poll_buffer, d_buffer, sizeof(cudaq::realtime::RPCResponse), \n                                cudaMemcpyDeviceToHost, poll_stream));\n    CUDA_CHECK(cudaStreamSynchronize(poll_stream));\n    \n    cudaq::realtime::RPCResponse* peek = reinterpret_cast<cudaq::realtime::RPCResponse*>(h_poll_buffer);\n    if (peek->magic == cudaq::realtime::RPC_MAGIC_RESPONSE) {\n      got_response = true;\n      break;\n    }\n    \n    usleep(poll_interval_ms * 1000);\n  }\n  \n  // Signal shutdown to allow kernel to exit\n  *h_shutdown = 1;\n  __sync_synchronize();\n  usleep(100000); // Give kernel time to see shutdown flag\n  \n  // Copy final results\n  CUDA_CHECK(cudaMemcpyAsync(h_buffer, d_buffer, buffer_size, cudaMemcpyDeviceToHost, poll_stream));\n  CUDA_CHECK(cudaStreamSynchronize(poll_stream));\n  \n  // Clean up poll resources  \n  CUDA_CHECK(cudaStreamDestroy(poll_stream));\n  cudaFreeHost(h_poll_buffer);\n  \n  // Sync main stream (dispatch kernel should have exited)\n  CUDA_CHECK(cudaStreamSynchronize(stream));\n  \n  ASSERT_TRUE(got_response) << \"Timeout waiting for device-side graph launch response\";\n  \n  // Verify response\n  cudaq::realtime::RPCResponse* h_response = reinterpret_cast<cudaq::realtime::RPCResponse*>(h_buffer);\n  EXPECT_EQ(h_response->magic, cudaq::realtime::RPC_MAGIC_RESPONSE) \n      << \"Expected RPC_MAGIC_RESPONSE, got 0x\" << std::hex << h_response->magic;\n  EXPECT_EQ(h_response->status, 0) << \"Handler returned error status\";\n  EXPECT_EQ(h_response->result_len, 4u) << \"Unexpected result length\";\n  \n  // Verify data was incremented by graph kernel launched from dispatch kernel\n  std::uint8_t* h_result = h_buffer + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(h_result[0], 1) << \"Expected h_result[0]=1\";\n  EXPECT_EQ(h_result[1], 2) << \"Expected h_result[1]=2\";\n  EXPECT_EQ(h_result[2], 3) << \"Expected h_result[2]=3\";\n  EXPECT_EQ(h_result[3], 4) << \"Expected h_result[3]=4\";\n  \n  // Cleanup\n  delete[] h_buffer;\n  CUDA_CHECK(cudaq_destroy_dispatch_graph(dispatch_ctx));\n  CUDA_CHECK(cudaStreamDestroy(stream));\n  CUDA_CHECK(cudaFree(d_stats));\n  CUDA_CHECK(cudaFreeHost(const_cast<int*>(h_shutdown)));  // Free mapped memory\n  CUDA_CHECK(cudaFree((void*)d_tx_flags));\n  CUDA_CHECK(cudaFree((void*)d_rx_flags));\n  CUDA_CHECK(cudaFree(d_function_entries));\n  CUDA_CHECK(cudaGraphExecDestroy(child_graph_exec));\n  CUDA_CHECK(cudaGraphDestroy(child_graph));\n  CUDA_CHECK(cudaFree(d_graph_buffer_ptr));\n  CUDA_CHECK(cudaFree(d_buffer));\n}\n\n} // namespace\n", "output": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#include <gtest/gtest.h>\n#include <hip/hip_runtime.h>\n#include <cstdint>\n#include <vector>\n#include <cstring>\n#include <unistd.h>\n#include <iostream>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n\n// Helper macro for CUDA error checking\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    hipError_t err = call;                                                    \\\n    ASSERT_EQ(err, hipSuccess) << \"CUDA error: \" << hipGetErrorString(err);  \\\n  } while (0)\n\nnamespace {\n\n//==============================================================================\n// Test Handler: Simple noop that copies input to output\n//==============================================================================\n\n/// @brief Test handler that adds 1 to each byte.\n__device__ int increment_handler(const void* input, void* output,\n                                  std::uint32_t arg_len,\n                                  std::uint32_t max_result_len,\n                                  std::uint32_t* result_len) {\n  const std::uint8_t* in_data = static_cast<const std::uint8_t*>(input);\n  std::uint8_t* out_data = static_cast<std::uint8_t*>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = in_data[i] + 1;\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n//==============================================================================\n// Host API Dispatch Kernel Test Helpers\n//==============================================================================\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n__device__ int rpc_increment_handler(const void* input, void* output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t* result_len) {\n  const std::uint8_t* in_data = static_cast<const std::uint8_t*>(input);\n  std::uint8_t* out_data = static_cast<std::uint8_t*>(output);\n  for (std::uint32_t i = 0; i < arg_len && i < max_result_len; ++i) {\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  }\n  *result_len = arg_len;\n  return 0;\n}\n\n__global__ void init_rpc_function_table(cudaq_function_entry_t* entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr = reinterpret_cast<void*>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n    \n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;  // Variable size\n    entries[0].schema.args[0].num_elements = 0; // Variable size\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;  // Variable size\n    entries[0].schema.results[0].num_elements = 0; // Variable size\n  }\n}\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t** host_flags_out,\n                          volatile uint64_t** device_flags_out,\n                          std::uint8_t** host_data_out,\n                          std::uint8_t** device_data_out) {\n  void* host_flags_ptr = nullptr;\n  hipError_t err = hipHostAlloc(&host_flags_ptr,\n                                  num_slots * sizeof(uint64_t),\n                                  hipHostMallocMapped);\n  if (err != hipSuccess)\n    return false;\n\n  void* device_flags_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void* host_data_ptr = nullptr;\n  err = hipHostAlloc(&host_data_ptr,\n                      num_slots * slot_size,\n                      hipHostMallocMapped);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void* device_data_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    hipHostFree(host_data_ptr);\n    return false;\n  }\n\n  memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t*>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t*>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t*>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t*>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t* host_flags,\n                      std::uint8_t* host_data) {\n  if (host_flags)\n    hipHostFree(const_cast<uint64_t*>(host_flags));\n  if (host_data)\n    hipHostFree(host_data);\n}\n\nextern \"C\" void launch_dispatch_kernel_wrapper(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    hipStream_t stream) {\n  cudaq_launch_dispatch_kernel_regular(\n      rx_flags, tx_flags, rx_data, tx_data, rx_stride_sz, tx_stride_sz,\n      function_table, func_count,\n      shutdown_flag, stats, num_slots, num_blocks, threads_per_block, stream);\n}\n\n//==============================================================================\n// Test Kernel for DeviceCallMode\n//==============================================================================\n\nusing HandlerFunc = int (*)(const void*, void*, std::uint32_t, std::uint32_t, std::uint32_t*);\n\n__device__ HandlerFunc d_increment_handler = increment_handler;\n\n/// @brief Test kernel that dispatches to a handler using DeviceCallMode.\ntemplate <typename KernelType>\n__global__ void test_dispatch_kernel(\n    HandlerFunc handler,\n    const void* input,\n    void* output,\n    std::uint32_t arg_len,\n    std::uint32_t max_result_len,\n    std::uint32_t* result_len,\n    int* status) {\n  \n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    *status = handler(input, output, arg_len, max_result_len, result_len);\n  }\n  \n  KernelType::sync();\n}\n\n//==============================================================================\n// Test Fixture\n//==============================================================================\n\nclass DispatchKernelTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    CUDA_CHECK(hipMalloc(&d_buffer_, 1024));\n    CUDA_CHECK(hipMalloc(&d_result_len_, sizeof(std::uint32_t)));\n    CUDA_CHECK(hipMalloc(&d_status_, sizeof(int)));\n  }\n  \n  void TearDown() override {\n    if (d_buffer_) hipFree(d_buffer_);\n    if (d_result_len_) hipFree(d_result_len_);\n    if (d_status_) hipFree(d_status_);\n  }\n  \n  void* d_buffer_ = nullptr;\n  std::uint32_t* d_result_len_ = nullptr;\n  int* d_status_ = nullptr;\n};\n\n//==============================================================================\n// Tests\n//==============================================================================\n\nTEST_F(DispatchKernelTest, IncrementHandlerBasic) {\n  // Prepare test data - separate input and output buffers\n  std::vector<uint8_t> input = {0, 1, 2, 3, 4};\n  std::vector<uint8_t> expected = {1, 2, 3, 4, 5};\n\n  void* d_input = nullptr;\n  CUDA_CHECK(hipMalloc(&d_input, 1024));\n  CUDA_CHECK(hipMemcpy(d_input, input.data(), input.size(), \n                        hipMemcpyHostToDevice));\n  \n  // Get device function pointer\n  HandlerFunc h_handler;\n  CUDA_CHECK(hipMemcpyFromSymbol(&h_handler,HIP_SYMBOL(d_increment_handler), \n                                   sizeof(HandlerFunc)));\n  \n  // Launch kernel with separate input/output buffers\n  test_dispatch_kernel<cudaq::realtime::RegularKernel><<<1, 32>>>(\n      h_handler, d_input, d_buffer_, input.size(), 1024, d_result_len_, d_status_);\n  CUDA_CHECK(hipGetLastError());\n  CUDA_CHECK(hipDeviceSynchronize());\n  \n  // Check results\n  int status;\n  std::uint32_t result_len;\n  CUDA_CHECK(hipMemcpy(&status, d_status_, sizeof(int), hipMemcpyDeviceToHost));\n  CUDA_CHECK(hipMemcpy(&result_len, d_result_len_, sizeof(std::uint32_t), \n                        hipMemcpyDeviceToHost));\n  \n  EXPECT_EQ(status, 0) << \"Handler should return success\";\n  EXPECT_EQ(result_len, input.size()) << \"Result length should match input\";\n  \n  // Verify output buffer has incremented data\n  std::vector<uint8_t> output(input.size());\n  CUDA_CHECK(hipMemcpy(output.data(), d_buffer_, output.size(), \n                        hipMemcpyDeviceToHost));\n  EXPECT_EQ(expected, output) << \"Increment handler should add 1 to each byte\";\n\n  // Verify input buffer is unchanged\n  std::vector<uint8_t> input_readback(input.size());\n  CUDA_CHECK(hipMemcpy(input_readback.data(), d_input, input.size(),\n                        hipMemcpyDeviceToHost));\n  EXPECT_EQ(input, input_readback) << \"Input buffer should be unchanged\";\n\n  hipFree(d_input);\n}\n\nTEST_F(DispatchKernelTest, LargeBuffer) {\n  // Test with larger data - separate input/output buffers\n  const std::size_t size = 512;\n  std::vector<uint8_t> input(size);\n  for (std::size_t i = 0; i < size; ++i) {\n    input[i] = static_cast<uint8_t>(i & 0xFF);\n  }\n  \n  void* d_input = nullptr;\n  CUDA_CHECK(hipMalloc(&d_input, 1024));\n  CUDA_CHECK(hipMemcpy(d_input, input.data(), input.size(), \n                        hipMemcpyHostToDevice));\n  \n  HandlerFunc h_handler;\n  CUDA_CHECK(hipMemcpyFromSymbol(&h_handler,HIP_SYMBOL(d_increment_handler), \n                                   sizeof(HandlerFunc)));\n  \n  test_dispatch_kernel<cudaq::realtime::RegularKernel><<<1, 256>>>(\n      h_handler, d_input, d_buffer_, input.size(), 1024, d_result_len_, d_status_);\n  CUDA_CHECK(hipGetLastError());\n  CUDA_CHECK(hipDeviceSynchronize());\n  \n  std::uint32_t result_len;\n  CUDA_CHECK(hipMemcpy(&result_len, d_result_len_, sizeof(std::uint32_t), \n                        hipMemcpyDeviceToHost));\n  EXPECT_EQ(result_len, size) << \"Should process all bytes\";\n  \n  // Verify all bytes incremented in output buffer\n  std::vector<uint8_t> output(size);\n  CUDA_CHECK(hipMemcpy(output.data(), d_buffer_, output.size(), \n                        hipMemcpyDeviceToHost));\n  \n  for (std::size_t i = 0; i < size; ++i) {\n    uint8_t expected = static_cast<uint8_t>((i + 1) & 0xFF);\n    EXPECT_EQ(output[i], expected) << \"Mismatch at index \" << i;\n  }\n\n  hipFree(d_input);\n}\n\nclass HostApiDispatchTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_, &rx_data_host_, &rx_data_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_, &tx_data_host_, &tx_data_));\n\n    void* tmp_shutdown = nullptr;\n    CUDA_CHECK(hipHostAlloc(&tmp_shutdown, sizeof(int), hipHostMallocMapped));\n    shutdown_flag_ = static_cast<volatile int*>(tmp_shutdown);\n    void* tmp_d_shutdown = nullptr;\n    CUDA_CHECK(hipHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n    d_shutdown_flag_ = static_cast<volatile int*>(tmp_d_shutdown);\n    *shutdown_flag_ = 0;\n    int zero = 0;\n    CUDA_CHECK(hipMemcpy(const_cast<int*>(d_shutdown_flag_), &zero,\n                          sizeof(int), hipMemcpyHostToDevice));\n\n    CUDA_CHECK(hipMalloc(&d_stats_, sizeof(uint64_t)));\n    CUDA_CHECK(hipMemset(d_stats_, 0, sizeof(uint64_t)));\n\n    CUDA_CHECK(hipMalloc(&d_function_entries_, sizeof(cudaq_function_entry_t)));\n    init_rpc_function_table<<<1, 1>>>(d_function_entries_);\n    CUDA_CHECK(hipDeviceSynchronize());\n    func_count_ = 1;\n\n    ASSERT_EQ(cudaq_dispatch_manager_create(&manager_), CUDAQ_OK);\n    cudaq_dispatcher_config_t config{};\n    config.device_id = 0;\n    config.num_blocks = 1;\n    config.threads_per_block = 64;\n    config.num_slots = static_cast<uint32_t>(num_slots_);\n    config.slot_size = static_cast<uint32_t>(slot_size_);\n    config.vp_id = 0;\n    config.kernel_type = CUDAQ_KERNEL_REGULAR;\n    config.dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    ASSERT_EQ(cudaq_dispatcher_create(manager_, &config, &dispatcher_), CUDAQ_OK);\n\n    cudaq_ringbuffer_t ringbuffer{};\n    ringbuffer.rx_flags = rx_flags_;\n    ringbuffer.tx_flags = tx_flags_;\n    ringbuffer.rx_data = rx_data_;\n    ringbuffer.tx_data = tx_data_;\n    ringbuffer.rx_stride_sz = slot_size_;\n    ringbuffer.tx_stride_sz = slot_size_;\n    ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer), CUDAQ_OK);\n\n    cudaq_function_table_t table{};\n    table.entries = d_function_entries_;\n    table.count = func_count_;\n    ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher_, &table), CUDAQ_OK);\n\n    ASSERT_EQ(\n        cudaq_dispatcher_set_control(dispatcher_, d_shutdown_flag_, d_stats_),\n        CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_set_launch_fn(dispatcher_,\n                                             &launch_dispatch_kernel_wrapper),\n              CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_start(dispatcher_), CUDAQ_OK);\n  }\n\n  void TearDown() override {\n    if (shutdown_flag_) {\n      *shutdown_flag_ = 1;\n      __sync_synchronize();\n    }\n    if (dispatcher_) {\n      cudaq_dispatcher_stop(dispatcher_);\n      cudaq_dispatcher_destroy(dispatcher_);\n      dispatcher_ = nullptr;\n    }\n    if (manager_) {\n      cudaq_dispatch_manager_destroy(manager_);\n      manager_ = nullptr;\n    }\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n\n    if (shutdown_flag_)\n      hipHostFree(const_cast<int*>(shutdown_flag_));\n    if (d_stats_)\n      hipFree(d_stats_);\n    if (d_function_entries_)\n      hipFree(d_function_entries_);\n  }\n\n  void write_rpc_request(std::size_t slot,\n                         const std::vector<std::uint8_t>& payload) {\n    std::uint8_t* slot_data =\n        const_cast<std::uint8_t*>(rx_data_host_) + slot * slot_size_;\n    auto* header = reinterpret_cast<cudaq::realtime::RPCHeader*>(slot_data);\n    header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n    header->function_id = RPC_INCREMENT_FUNCTION_ID;\n    header->arg_len = static_cast<std::uint32_t>(payload.size());\n    memcpy(slot_data + sizeof(cudaq::realtime::RPCHeader), payload.data(),\n           payload.size());\n  }\n\n  bool read_rpc_response(std::size_t slot,\n                         std::vector<std::uint8_t>& payload,\n                         std::int32_t* status_out = nullptr,\n                         std::uint32_t* result_len_out = nullptr) {\n    __sync_synchronize();\n    // Read from TX buffer (dispatch kernel writes response to symmetric TX)\n    const std::uint8_t* slot_data =\n        const_cast<std::uint8_t*>(tx_data_host_) + slot * slot_size_;\n    auto* response =\n        reinterpret_cast<const cudaq::realtime::RPCResponse*>(slot_data);\n\n    if (response->magic != cudaq::realtime::RPC_MAGIC_RESPONSE)\n      return false;\n    if (status_out)\n      *status_out = response->status;\n    if (result_len_out)\n      *result_len_out = response->result_len;\n    if (response->status != 0)\n      return false;\n\n    payload.resize(response->result_len);\n    memcpy(payload.data(),\n           slot_data + sizeof(cudaq::realtime::RPCResponse),\n           response->result_len);\n    return true;\n  }\n\n  static constexpr std::size_t num_slots_ = 2;\n  std::size_t slot_size_ = 256;\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_ = nullptr;\n  volatile uint64_t* tx_flags_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_ = nullptr;\n  std::uint8_t* tx_data_ = nullptr;\n\n  volatile int* shutdown_flag_ = nullptr;\n  volatile int* d_shutdown_flag_ = nullptr;\n  uint64_t* d_stats_ = nullptr;\n\n  cudaq_function_entry_t* d_function_entries_ = nullptr;\n  std::size_t func_count_ = 0;\n\n  cudaq_dispatch_manager_t* manager_ = nullptr;\n  cudaq_dispatcher_t* dispatcher_ = nullptr;\n};\n\nTEST_F(HostApiDispatchTest, RpcIncrementHandler) {\n  std::vector<std::uint8_t> payload = {0, 1, 2, 3};\n  write_rpc_request(0, payload);\n\n  __sync_synchronize();\n  const_cast<volatile uint64_t*>(rx_flags_host_)[0] =\n      reinterpret_cast<std::uint64_t>(rx_data_);\n\n  int timeout = 50;\n  while (tx_flags_host_[0] == 0 && timeout-- > 0) {\n    usleep(1000);\n  }\n  ASSERT_GT(timeout, 0) << \"Timeout waiting for dispatch kernel response\";\n\n  std::vector<std::uint8_t> response;\n  std::int32_t status = -1;\n  std::uint32_t result_len = 0;\n  ASSERT_TRUE(read_rpc_response(0, response, &status, &result_len));\n  EXPECT_EQ(status, 0);\n  ASSERT_EQ(result_len, payload.size());\n\n  std::vector<std::uint8_t> expected = {1, 2, 3, 4};\n  EXPECT_EQ(response, expected);\n}\n\n//==============================================================================\n// Graph Launch Test\n//==============================================================================\n\n// Graph kernel that processes RPC buffer via pointer indirection\n__global__ void graph_increment_kernel(void** buffer_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *buffer_ptr;\n    cudaq::realtime::RPCHeader* header = static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    \n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    \n    // Increment each byte\n    for (std::uint32_t i = 0; i < arg_len; ++i) {\n      data[i] = data[i] + 1;\n    }\n    \n    // Write response\n    cudaq::realtime::RPCResponse* response = static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_increment\");\n\n__global__ void init_graph_function_table(cudaq_function_entry_t* entries, \n                                          hipGraphExec_t graph_exec) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.graph_exec = graph_exec;\n    entries[0].function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n  }\n}\n\nTEST(GraphLaunchTest, DispatchKernelGraphLaunch) {\n  // Check compute capability\n  int device;\n  CUDA_CHECK(hipGetDevice(&device));\n  hipDeviceProp_t prop;\n  CUDA_CHECK(hipGetDeviceProperties(&prop, device));\n  \n  if (prop.major < 9) {\n    GTEST_SKIP() << \"Graph device launch requires compute capability 9.0+, found \" \n                 << prop.major << \".\" << prop.minor;\n  }\n  \n  // Allocate graph buffer pointer (for pointer indirection pattern)\n  void** d_graph_buffer_ptr;\n  CUDA_CHECK(hipMalloc(&d_graph_buffer_ptr, sizeof(void*)));\n  CUDA_CHECK(hipMemset(d_graph_buffer_ptr, 0, sizeof(void*)));\n  \n  // Allocate test buffer\n  constexpr size_t buffer_size = 1024;\n  void* d_buffer;\n  CUDA_CHECK(hipMalloc(&d_buffer, buffer_size));\n  \n  // Create the child graph (the one that will be launched from device)\n  hipGraph_t child_graph;\n  hipGraphExec_t child_graph_exec;\n  \n  CUDA_CHECK(hipGraphCreate(&child_graph, 0));\n  \n  // Add kernel node to child graph\n  hipKernelNodeParams kernel_params = {};\n  void* kernel_args[] = {&d_graph_buffer_ptr};\n  kernel_params.func = reinterpret_cast<void*>(&graph_increment_kernel);\n  kernel_params.gridDim = dim3(1, 1, 1);\n  kernel_params.blockDim = dim3(32, 1, 1);\n  kernel_params.sharedMemBytes = 0;\n  kernel_params.kernelParams = kernel_args;\n  kernel_params.extra = nullptr;\n  \n  hipGraphNode_t kernel_node;\n  CUDA_CHECK(hipGraphAddKernelNode(&kernel_node, child_graph, nullptr, 0, &kernel_params));\n  \n  // Instantiate CHILD graph with DEVICE LAUNCH FLAG\n  CUDA_CHECK(hipGraphInstantiate(&child_graph_exec, child_graph,  \n                                   hipGraphInstantiateFlagDeviceLaunch));\n  \n  // Create stream for operations\n  hipStream_t stream;\n  CUDA_CHECK(hipStreamCreate(&stream));\n  \n  // Upload the child graph to device\n  CUDA_CHECK(hipGraphUpload(child_graph_exec, stream));\n  CUDA_CHECK(hipStreamSynchronize(stream));\n  \n  // Set up function table with graph launch entry\n  cudaq_function_entry_t* d_function_entries;\n  CUDA_CHECK(hipMalloc(&d_function_entries, sizeof(cudaq_function_entry_t)));\n  init_graph_function_table<<<1, 1>>>(d_function_entries, child_graph_exec);\n  CUDA_CHECK(hipDeviceSynchronize());\n  \n  // Set up RPC buffer on host\n  std::uint8_t* h_buffer = new std::uint8_t[buffer_size];\n  cudaq::realtime::RPCHeader* h_header = reinterpret_cast<cudaq::realtime::RPCHeader*>(h_buffer);\n  h_header->magic = cudaq::realtime::RPC_MAGIC_REQUEST;\n  h_header->function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n  h_header->arg_len = 4;\n  \n  std::uint8_t* h_data = h_buffer + sizeof(cudaq::realtime::RPCHeader);\n  h_data[0] = 0;\n  h_data[1] = 1;\n  h_data[2] = 2;\n  h_data[3] = 3;\n  \n  // Copy to device\n  CUDA_CHECK(hipMemcpy(d_buffer, h_buffer, buffer_size, hipMemcpyHostToDevice));\n  \n  // Set up fake RX/TX flags for single-shot test\n  volatile uint64_t* d_rx_flags;\n  volatile uint64_t* d_tx_flags;\n  CUDA_CHECK(hipMalloc(&d_rx_flags, sizeof(uint64_t)));\n  CUDA_CHECK(hipMalloc(&d_tx_flags, sizeof(uint64_t)));\n  CUDA_CHECK(hipMemset((void*)d_rx_flags, 0, sizeof(uint64_t)));\n  CUDA_CHECK(hipMemset((void*)d_tx_flags, 0, sizeof(uint64_t)));\n  \n  // Set RX flag to point to our buffer (simulating incoming RPC)\n  uint64_t buffer_addr = reinterpret_cast<uint64_t>(d_buffer);\n  CUDA_CHECK(hipMemcpy((void*)d_rx_flags, &buffer_addr, sizeof(uint64_t), hipMemcpyHostToDevice));\n  \n  // Set up shutdown flag using pinned mapped memory so the dispatch kernel\n  // can see host updates immediately\n  volatile int* h_shutdown;\n  volatile int* d_shutdown;\n  {\n    void* tmp_shutdown;\n    CUDA_CHECK(hipHostAlloc(&tmp_shutdown, sizeof(int), hipHostMallocMapped));\n    h_shutdown = static_cast<volatile int*>(tmp_shutdown);\n    *h_shutdown = 0;\n    \n    void* tmp_d_shutdown;\n    CUDA_CHECK(hipHostGetDevicePointer(&tmp_d_shutdown, tmp_shutdown, 0));\n    d_shutdown = static_cast<volatile int*>(tmp_d_shutdown);\n  }\n  \n  // Set up stats\n  uint64_t* d_stats;\n  CUDA_CHECK(hipMalloc(&d_stats, sizeof(uint64_t)));\n  CUDA_CHECK(hipMemset(d_stats, 0, sizeof(uint64_t)));\n  \n  // Create dispatch graph context - THIS WRAPS THE DISPATCH KERNEL IN A GRAPH\n  // so that device-side cudaGraphLaunch() can work!\n  cudaq_dispatch_graph_context* dispatch_ctx = nullptr;\n  hipError_t err = cudaq_create_dispatch_graph_regular(\n      d_rx_flags, d_tx_flags,\n      reinterpret_cast<std::uint8_t*>(d_buffer),  // rx_data\n      reinterpret_cast<std::uint8_t*>(d_buffer),  // tx_data (same buffer for single-slot test)\n      buffer_size,  // rx_stride_sz\n      buffer_size,  // tx_stride_sz\n      d_function_entries, 1,\n      d_graph_buffer_ptr, d_shutdown, d_stats, 1,\n      1, 32, stream, &dispatch_ctx);\n  \n  if (err != hipSuccess) {\n    GTEST_SKIP() << \"Device-side graph launch not supported: \" \n                 << hipGetErrorString(err) << \" (\" << err << \")\";\n  }\n  \n  // Launch dispatch graph - now device-side cudaGraphLaunch will work!\n  CUDA_CHECK(cudaq_launch_dispatch_graph(dispatch_ctx, stream));\n  \n  // Poll for the response using pinned memory and async operations\n  // The child graph runs asynchronously (fire-and-forget) so we need to poll\n  std::uint8_t* h_poll_buffer;\n  CUDA_CHECK(hipHostAlloc(&h_poll_buffer, sizeof(cudaq::realtime::RPCResponse), hipHostMallocDefault));\n  memset(h_poll_buffer, 0, sizeof(cudaq::realtime::RPCResponse));\n  \n  hipStream_t poll_stream;\n  CUDA_CHECK(hipStreamCreate(&poll_stream));\n  \n  int timeout_ms = 5000;\n  int poll_interval_ms = 100;\n  bool got_response = false;\n  \n  for (int elapsed = 0; elapsed < timeout_ms; elapsed += poll_interval_ms) {\n    CUDA_CHECK(hipMemcpyAsync(h_poll_buffer, d_buffer, sizeof(cudaq::realtime::RPCResponse), \n                                hipMemcpyDeviceToHost, poll_stream));\n    CUDA_CHECK(hipStreamSynchronize(poll_stream));\n    \n    cudaq::realtime::RPCResponse* peek = reinterpret_cast<cudaq::realtime::RPCResponse*>(h_poll_buffer);\n    if (peek->magic == cudaq::realtime::RPC_MAGIC_RESPONSE) {\n      got_response = true;\n      break;\n    }\n    \n    usleep(poll_interval_ms * 1000);\n  }\n  \n  // Signal shutdown to allow kernel to exit\n  *h_shutdown = 1;\n  __sync_synchronize();\n  usleep(100000); // Give kernel time to see shutdown flag\n  \n  // Copy final results\n  CUDA_CHECK(hipMemcpyAsync(h_buffer, d_buffer, buffer_size, hipMemcpyDeviceToHost, poll_stream));\n  CUDA_CHECK(hipStreamSynchronize(poll_stream));\n  \n  // Clean up poll resources  \n  CUDA_CHECK(hipStreamDestroy(poll_stream));\n  hipHostFree(h_poll_buffer);\n  \n  // Sync main stream (dispatch kernel should have exited)\n  CUDA_CHECK(hipStreamSynchronize(stream));\n  \n  ASSERT_TRUE(got_response) << \"Timeout waiting for device-side graph launch response\";\n  \n  // Verify response\n  cudaq::realtime::RPCResponse* h_response = reinterpret_cast<cudaq::realtime::RPCResponse*>(h_buffer);\n  EXPECT_EQ(h_response->magic, cudaq::realtime::RPC_MAGIC_RESPONSE) \n      << \"Expected RPC_MAGIC_RESPONSE, got 0x\" << std::hex << h_response->magic;\n  EXPECT_EQ(h_response->status, 0) << \"Handler returned error status\";\n  EXPECT_EQ(h_response->result_len, 4u) << \"Unexpected result length\";\n  \n  // Verify data was incremented by graph kernel launched from dispatch kernel\n  std::uint8_t* h_result = h_buffer + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(h_result[0], 1) << \"Expected h_result[0]=1\";\n  EXPECT_EQ(h_result[1], 2) << \"Expected h_result[1]=2\";\n  EXPECT_EQ(h_result[2], 3) << \"Expected h_result[2]=3\";\n  EXPECT_EQ(h_result[3], 4) << \"Expected h_result[3]=4\";\n  \n  // Cleanup\n  delete[] h_buffer;\n  CUDA_CHECK(cudaq_destroy_dispatch_graph(dispatch_ctx));\n  CUDA_CHECK(hipStreamDestroy(stream));\n  CUDA_CHECK(hipFree(d_stats));\n  CUDA_CHECK(hipHostFree(const_cast<int*>(h_shutdown)));  // Free mapped memory\n  CUDA_CHECK(hipFree((void*)d_tx_flags));\n  CUDA_CHECK(hipFree((void*)d_rx_flags));\n  CUDA_CHECK(hipFree(d_function_entries));\n  CUDA_CHECK(hipGraphExecDestroy(child_graph_exec));\n  CUDA_CHECK(hipGraphDestroy(child_graph));\n  CUDA_CHECK(hipFree(d_graph_buffer_ptr));\n  CUDA_CHECK(hipFree(d_buffer));\n}\n\n} // namespace\n", "source": "/kaggle/working/cuda-quantum/realtime/unittests/test_dispatch_kernel.cu"}
{"input": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.\n * All rights reserved.\n *\n * This source code and the accompanying materials are made available under\n * the terms of the Apache License 2.0 which accompanies this distribution.\n ******************************************************************************/\n\n#include <gtest/gtest.h>\n#include <cuda/std/atomic>\n#include <cuda_runtime.h>\n#include <cstdint>\n#include <cstring>\n#include <thread>\n#include <unistd.h>\n#include <vector>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/host_dispatcher.h\"\n\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    cudaError_t err = call;                                                    \\\n    ASSERT_EQ(err, cudaSuccess) << \"CUDA error: \" << cudaGetErrorString(err);  \\\n  } while (0)\n\nnamespace {\n\n//==============================================================================\n// Ring buffer helpers (same pattern as test_dispatch_kernel.cu)\n//==============================================================================\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t** host_flags_out,\n                          volatile uint64_t** device_flags_out,\n                          std::uint8_t** host_data_out,\n                          std::uint8_t** device_data_out) {\n  void* host_flags_ptr = nullptr;\n  cudaError_t err = cudaHostAlloc(&host_flags_ptr,\n                                  num_slots * sizeof(uint64_t),\n                                  cudaHostAllocMapped);\n  if (err != cudaSuccess)\n    return false;\n\n  void* device_flags_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void* host_data_ptr = nullptr;\n  err = cudaHostAlloc(&host_data_ptr, num_slots * slot_size,\n                      cudaHostAllocMapped);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    return false;\n  }\n\n  void* device_data_ptr = nullptr;\n  err = cudaHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != cudaSuccess) {\n    cudaFreeHost(host_flags_ptr);\n    cudaFreeHost(host_data_ptr);\n    return false;\n  }\n\n  std::memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t*>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t*>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t*>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t*>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t* host_flags, std::uint8_t* host_data) {\n  if (host_flags)\n    cudaFreeHost(const_cast<uint64_t*>(host_flags));\n  if (host_data)\n    cudaFreeHost(host_data);\n}\n\n//==============================================================================\n// Minimal graph for dummy GRAPH_LAUNCH entry (so C API starts the host thread)\n//==============================================================================\n\n__global__ void noop_kernel() {}\n\n// Creates a minimal executable graph and returns it. Caller must destroy with\n// cudaGraphExecDestroy and cudaGraphDestroy.\nbool create_dummy_graph(cudaGraph_t* graph_out, cudaGraphExec_t* exec_out) {\n  cudaGraph_t graph = nullptr;\n  if (cudaGraphCreate(&graph, 0) != cudaSuccess)\n    return false;\n\n  cudaKernelNodeParams params = {};\n  void* args[] = {};\n  params.func = reinterpret_cast<void*>(noop_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(1, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = args;\n  params.extra = nullptr;\n\n  cudaGraphNode_t node = nullptr;\n  if (cudaGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  cudaGraphExec_t exec = nullptr;\n  if (cudaGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Graph launch test: kernel that reads slot from mailbox and writes response\n// in-place (same buffer as request; use single ring buffer for rx/tx).\n//==============================================================================\n\n__global__ void graph_increment_kernel(void** mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *mailbox_slot_ptr;\n    cudaq::realtime::RPCHeader* header =\n        static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    for (std::uint32_t i = 0; i < arg_len; ++i)\n      data[i] = data[i] + 1;\n    cudaq::realtime::RPCResponse* response =\n        static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_increment\");\n\n/// Creates an executable graph that runs graph_increment_kernel with\n/// kernel arg = d_mailbox_bank (device pointer to first mailbox slot).\n/// Caller must cudaGraphExecDestroy / cudaGraphDestroy.\nbool create_increment_graph(void** d_mailbox_bank, cudaGraph_t* graph_out,\n                            cudaGraphExec_t* exec_out) {\n  cudaGraph_t graph = nullptr;\n  if (cudaGraphCreate(&graph, 0) != cudaSuccess)\n    return false;\n\n  // kernelParams[i] must be a *pointer to* the i-th argument value.\n  // The kernel takes void** so we pass &d_mailbox_bank (a void***).\n  cudaKernelNodeParams params = {};\n  void* kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void*>(graph_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  cudaGraphNode_t node = nullptr;\n  if (cudaGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  cudaGraphExec_t exec = nullptr;\n  if (cudaGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Graph launch test: kernel that reads slot from mailbox and doubles payload\n// in-place (for function_id routing differentiation vs increment kernel).\n//==============================================================================\n\n__global__ void graph_double_kernel(void** mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *mailbox_slot_ptr;\n    cudaq::realtime::RPCHeader* header =\n        static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    for (std::uint32_t i = 0; i < arg_len; ++i)\n      data[i] = data[i] * 2;\n    cudaq::realtime::RPCResponse* response =\n        static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_DOUBLE_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_double\");\n\nbool create_double_graph(void** d_mailbox_slot, cudaGraph_t* graph_out,\n                         cudaGraphExec_t* exec_out) {\n  cudaGraph_t graph = nullptr;\n  if (cudaGraphCreate(&graph, 0) != cudaSuccess)\n    return false;\n\n  cudaKernelNodeParams params = {};\n  void* kernel_args[] = {&d_mailbox_slot};\n  params.func = reinterpret_cast<void*>(graph_double_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  cudaGraphNode_t node = nullptr;\n  if (cudaGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  cudaGraphExec_t exec = nullptr;\n  if (cudaGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Test fixture: drives host_dispatcher_loop directly (not C API) for full\n// control over idle_mask, enabling worker recycling and backpressure tests.\n//==============================================================================\n\nstatic constexpr std::size_t kMaxWorkers = 8;\n\nclass HostDispatcherLoopTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    std::memset(&config_, 0, sizeof(config_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_dev_, &rx_data_host_,\n                                     &rx_data_dev_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_dev_, &tx_data_host_,\n                                     &tx_data_dev_));\n\n    CUDA_CHECK(cudaHostAlloc(&h_mailbox_bank_,\n                             kMaxWorkers * sizeof(void*),\n                             cudaHostAllocMapped));\n    std::memset(h_mailbox_bank_, 0, kMaxWorkers * sizeof(void*));\n    CUDA_CHECK(cudaHostGetDevicePointer(\n        reinterpret_cast<void**>(&d_mailbox_bank_), h_mailbox_bank_, 0));\n\n    idle_mask_ = new cuda::std::atomic<uint64_t>(0);\n    live_dispatched_ = new cuda::std::atomic<uint64_t>(0);\n    inflight_slot_tags_ = new int[kMaxWorkers]();\n    shutdown_flag_ = new cuda::std::atomic<int>(0);\n    stats_counter_ = 0;\n\n    function_table_ = new cudaq_function_entry_t[kMaxWorkers];\n    std::memset(function_table_, 0, kMaxWorkers * sizeof(cudaq_function_entry_t));\n\n    std::memset(&ringbuffer_, 0, sizeof(ringbuffer_));\n    ringbuffer_.rx_flags = rx_flags_dev_;\n    ringbuffer_.tx_flags = tx_flags_dev_;\n    ringbuffer_.rx_data = rx_data_dev_;\n    ringbuffer_.tx_data = tx_data_dev_;\n    ringbuffer_.rx_stride_sz = slot_size_;\n    ringbuffer_.tx_stride_sz = slot_size_;\n    ringbuffer_.rx_flags_host = rx_flags_host_;\n    ringbuffer_.tx_flags_host = tx_flags_host_;\n    ringbuffer_.rx_data_host = rx_data_host_;\n    ringbuffer_.tx_data_host = tx_data_host_;\n  }\n\n  void TearDown() override {\n    if (!loop_stopped_) {\n      shutdown_flag_->store(1, cuda::std::memory_order_release);\n      __sync_synchronize();\n      if (loop_thread_.joinable())\n        loop_thread_.join();\n    }\n\n    for (auto& w : worker_info_) {\n      if (w.stream)\n        cudaStreamDestroy(w.stream);\n      if (w.graph_exec)\n        cudaGraphExecDestroy(w.graph_exec);\n      if (w.graph)\n        cudaGraphDestroy(w.graph);\n    }\n\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n    if (h_mailbox_bank_)\n      cudaFreeHost(h_mailbox_bank_);\n    delete idle_mask_;\n    delete live_dispatched_;\n    delete[] inflight_slot_tags_;\n    delete shutdown_flag_;\n    delete[] function_table_;\n  }\n\n  struct WorkerInfo {\n    cudaGraphExec_t graph_exec = nullptr;\n    cudaGraph_t graph = nullptr;\n    cudaStream_t stream = nullptr;\n  };\n\n  void AddWorker(std::uint32_t function_id, cudaGraphExec_t exec,\n                 cudaGraph_t graph) {\n    cudaStream_t stream = nullptr;\n    ASSERT_EQ(cudaStreamCreate(&stream), cudaSuccess);\n\n    cudaq_host_dispatch_worker_t w{};\n    w.graph_exec = exec;\n    w.stream = stream;\n    w.function_id = function_id;\n    workers_.push_back(w);\n    worker_info_.push_back({exec, graph, stream});\n\n    std::size_t idx = function_table_count_;\n    function_table_[idx].handler.graph_exec = exec;\n    function_table_[idx].function_id = function_id;\n    function_table_[idx].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n    function_table_count_++;\n  }\n\n  void StartLoop() {\n    idle_mask_->store((1ULL << workers_.size()) - 1,\n                      cuda::std::memory_order_release);\n\n    config_.ringbuffer.rx_flags_host = rx_flags_host_;\n    config_.ringbuffer.tx_flags_host = tx_flags_host_;\n    config_.ringbuffer.rx_data_host = rx_data_host_;\n    config_.ringbuffer.rx_data = rx_data_dev_;\n    config_.ringbuffer.tx_data_host = tx_data_host_;\n    config_.ringbuffer.tx_data = tx_data_dev_;\n    config_.ringbuffer.tx_stride_sz = slot_size_;\n    config_.ringbuffer.tx_flags = tx_flags_dev_;\n    config_.config.num_slots = static_cast<uint32_t>(num_slots_);\n    config_.config.slot_size = static_cast<uint32_t>(slot_size_);\n    config_.function_table.entries = function_table_;\n    config_.function_table.count = static_cast<uint32_t>(function_table_count_);\n    config_.workers = workers_.data();\n    config_.num_workers = workers_.size();\n    config_.h_mailbox_bank = h_mailbox_bank_;\n    config_.shutdown_flag = shutdown_flag_;\n    config_.stats_counter = &stats_counter_;\n    config_.live_dispatched = live_dispatched_;\n    config_.idle_mask = idle_mask_;\n    config_.inflight_slot_tags = inflight_slot_tags_;\n\n    loop_thread_ = std::thread([this]() {\n      cudaq_host_dispatcher_loop(&config_);\n    });\n  }\n\n  void WriteRpcRequest(std::size_t slot, std::uint32_t function_id,\n                       const std::uint8_t* payload, std::size_t len) {\n    ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                  &ringbuffer_, static_cast<uint32_t>(slot), function_id,\n                  payload, static_cast<uint32_t>(len), 0, 0),\n              CUDAQ_OK);\n  }\n\n  void SignalSlot(std::size_t slot) {\n    cudaq_host_ringbuffer_signal_slot(&ringbuffer_, static_cast<uint32_t>(slot));\n  }\n\n  bool PollTxFlag(std::size_t slot, int timeout_ms = 2000) {\n    for (int waited = 0; waited < timeout_ms * 1000; waited += 200) {\n      cudaq_tx_status_t st = cudaq_host_ringbuffer_poll_tx_flag(\n          &ringbuffer_, static_cast<uint32_t>(slot), nullptr);\n      if (st != CUDAQ_TX_EMPTY)\n        return true;\n      usleep(200);\n    }\n    return cudaq_host_ringbuffer_poll_tx_flag(\n               &ringbuffer_, static_cast<uint32_t>(slot), nullptr) !=\n           CUDAQ_TX_EMPTY;\n  }\n\n  void StopLoop() {\n    shutdown_flag_->store(1, cuda::std::memory_order_release);\n    __sync_synchronize();\n    if (loop_thread_.joinable())\n      loop_thread_.join();\n    loop_stopped_ = true;\n  }\n\n  void RestoreWorker(int worker_id) {\n    idle_mask_->fetch_or(1ULL << worker_id, cuda::std::memory_order_release);\n  }\n\n  void ClearSlot(std::size_t slot) {\n    cudaq_host_ringbuffer_clear_slot(&ringbuffer_, static_cast<uint32_t>(slot));\n    std::memset(rx_data_host_ + slot * slot_size_, 0, slot_size_);\n  }\n\n  void VerifyResponse(std::size_t slot, const std::uint8_t* expected,\n                      std::size_t len) {\n    int cuda_err = 0;\n    cudaq_tx_status_t st = cudaq_host_ringbuffer_poll_tx_flag(\n        &ringbuffer_, static_cast<uint32_t>(slot), &cuda_err);\n    ASSERT_TRUE(st == CUDAQ_TX_READY || st == CUDAQ_TX_IN_FLIGHT)\n        << \"slot \" << slot\n        << \": tx_flag unexpected (status=\" << st << \" cuda_err=\" << cuda_err << \")\";\n\n    std::uint8_t* slot_data = rx_data_host_ + slot * slot_size_;\n    auto* resp =\n        reinterpret_cast<cudaq::realtime::RPCResponse*>(slot_data);\n    ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE)\n        << \"slot \" << slot << \": expected response magic\";\n    ASSERT_EQ(resp->status, 0) << \"slot \" << slot << \": non-zero status\";\n    ASSERT_EQ(resp->result_len, static_cast<std::uint32_t>(len))\n        << \"slot \" << slot << \": wrong result_len\";\n    std::uint8_t* result = slot_data + sizeof(cudaq::realtime::RPCResponse);\n    for (std::size_t i = 0; i < len; ++i) {\n      EXPECT_EQ(result[i], expected[i])\n          << \"slot \" << slot << \" byte \" << i;\n    }\n  }\n\n  std::size_t num_slots_ = 4;\n  std::size_t slot_size_ = 256;\n\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_dev_ = nullptr;\n  volatile uint64_t* tx_flags_dev_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_dev_ = nullptr;\n  std::uint8_t* tx_data_dev_ = nullptr;\n\n  void** h_mailbox_bank_ = nullptr;\n  void** d_mailbox_bank_ = nullptr;\n\n  cuda::std::atomic<uint64_t>* idle_mask_ = nullptr;\n  cuda::std::atomic<uint64_t>* live_dispatched_ = nullptr;\n  int* inflight_slot_tags_ = nullptr;\n  cuda::std::atomic<int>* shutdown_flag_ = nullptr;\n  uint64_t stats_counter_ = 0;\n  bool loop_stopped_ = false;\n\n  cudaq_function_entry_t* function_table_ = nullptr;\n  std::size_t function_table_count_ = 0;\n  std::vector<cudaq_host_dispatch_worker_t> workers_;\n  std::vector<WorkerInfo> worker_info_;\n\n  cudaq_ringbuffer_t ringbuffer_{};\n  cudaq_host_dispatch_loop_ctx_t config_;\n  std::thread loop_thread_;\n};\n\n//==============================================================================\n// Test 1: Smoke test \u2014 host loop starts and drops slot with unknown function_id\n//==============================================================================\n\nconstexpr std::uint32_t DUMMY_GRAPH_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"dummy_graph\");\n// Use a different function_id in the slot so the host loop does not find it.\nconstexpr std::uint32_t UNKNOWN_FUNCTION_ID = 0xdeadbeefu;\n\nclass HostDispatcherSmokeTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_, &rx_data_host_, &rx_data_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_, &tx_data_host_, &tx_data_));\n\n    shutdown_flag_ = new (std::nothrow) int(0);\n    stats_ = new (std::nothrow) uint64_t(0);\n    ASSERT_NE(shutdown_flag_, nullptr);\n    ASSERT_NE(stats_, nullptr);\n\n    ASSERT_TRUE(create_dummy_graph(&dummy_graph_, &dummy_graph_exec_));\n\n    host_table_ = new (std::nothrow) cudaq_function_entry_t[1];\n    ASSERT_NE(host_table_, nullptr);\n    std::memset(host_table_, 0, sizeof(cudaq_function_entry_t));\n    host_table_[0].handler.graph_exec = dummy_graph_exec_;\n    host_table_[0].function_id = DUMMY_GRAPH_FUNCTION_ID;\n    host_table_[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n\n    ASSERT_EQ(cudaq_dispatch_manager_create(&manager_), CUDAQ_OK);\n    cudaq_dispatcher_config_t config{};\n    config.device_id = 0;\n    config.num_slots = static_cast<uint32_t>(num_slots_);\n    config.slot_size = static_cast<uint32_t>(slot_size_);\n    config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n    ASSERT_EQ(cudaq_dispatcher_create(manager_, &config, &dispatcher_),\n              CUDAQ_OK);\n\n    std::memset(&ringbuffer_, 0, sizeof(ringbuffer_));\n    ringbuffer_.rx_flags = rx_flags_;\n    ringbuffer_.tx_flags = tx_flags_;\n    ringbuffer_.rx_data = rx_data_;\n    ringbuffer_.tx_data = tx_data_;\n    ringbuffer_.rx_stride_sz = slot_size_;\n    ringbuffer_.tx_stride_sz = slot_size_;\n    ringbuffer_.rx_flags_host = rx_flags_host_;\n    ringbuffer_.tx_flags_host = tx_flags_host_;\n    ringbuffer_.rx_data_host = rx_data_host_;\n    ringbuffer_.tx_data_host = tx_data_host_;\n    ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer_),\n              CUDAQ_OK);\n\n    cudaq_function_table_t table{};\n    table.entries = host_table_;\n    table.count = 1;\n    ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher_, &table),\n              CUDAQ_OK);\n\n    ASSERT_EQ(\n        cudaq_dispatcher_set_control(dispatcher_, shutdown_flag_, stats_),\n        CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_start(dispatcher_), CUDAQ_OK);\n  }\n\n  void TearDown() override {\n    if (shutdown_flag_) {\n      *shutdown_flag_ = 1;\n      __sync_synchronize();\n    }\n    if (dispatcher_) {\n      cudaq_dispatcher_stop(dispatcher_);\n      cudaq_dispatcher_destroy(dispatcher_);\n      dispatcher_ = nullptr;\n    }\n    if (manager_) {\n      cudaq_dispatch_manager_destroy(manager_);\n      manager_ = nullptr;\n    }\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n    if (shutdown_flag_)\n      delete shutdown_flag_;\n    if (stats_)\n      delete stats_;\n    if (host_table_)\n      delete[] host_table_;\n    if (dummy_graph_exec_)\n      cudaGraphExecDestroy(dummy_graph_exec_);\n    if (dummy_graph_)\n      cudaGraphDestroy(dummy_graph_);\n  }\n\n  void write_rpc_request_unknown_function(std::size_t slot) {\n    const std::uint8_t payload[] = {0, 1, 2, 3};\n    ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                  &ringbuffer_, static_cast<uint32_t>(slot),\n                  UNKNOWN_FUNCTION_ID, payload, 4, 0, 0),\n              CUDAQ_OK);\n  }\n\n  static constexpr std::size_t num_slots_ = 2;\n  std::size_t slot_size_ = 256;\n\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_ = nullptr;\n  volatile uint64_t* tx_flags_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_ = nullptr;\n  std::uint8_t* tx_data_ = nullptr;\n\n  int* shutdown_flag_ = nullptr;\n  uint64_t* stats_ = nullptr;\n  cudaq_function_entry_t* host_table_ = nullptr;\n  cudaGraph_t dummy_graph_ = nullptr;\n  cudaGraphExec_t dummy_graph_exec_ = nullptr;\n\n  cudaq_ringbuffer_t ringbuffer_{};\n  cudaq_dispatch_manager_t* manager_ = nullptr;\n  cudaq_dispatcher_t* dispatcher_ = nullptr;\n};\n\nTEST_F(HostDispatcherSmokeTest, DropsSlotWithUnknownFunctionId) {\n  write_rpc_request_unknown_function(0);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer_, 0);\n\n  for (int i = 0; i < 50; ++i) {\n    usleep(1000);\n    cudaq_tx_status_t st =\n        cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer_, 0, nullptr);\n    if (st != CUDAQ_TX_EMPTY)\n      break;\n  }\n\n  cudaq_tx_status_t final_st =\n      cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer_, 0, nullptr);\n  EXPECT_EQ(final_st, CUDAQ_TX_EMPTY)\n      << \"Host loop should drop slot with unknown function_id (no response)\";\n}\n\n//==============================================================================\n// Test 2: GRAPH_LAUNCH via host loop (full RPC round-trip) using the C API\n//\n// End-to-end test of: RPC in ring buffer \u2192 C API dispatcher \u2192 CUDA graph\n// launch via pinned mailbox \u2192 in-place response.\n//\n// Flow:\n//   1. Allocate pinned ring buffers and pinned mailbox (cudaHostAllocMapped).\n//   2. Capture graph_increment_kernel with d_mailbox_bank baked in.\n//   3. Build function table with one GRAPH_LAUNCH entry.\n//   4. Wire the C API: manager \u2192 dispatcher \u2192 ringbuffer, function table,\n//      control, mailbox \u2192 start.\n//   5. Write an RPC request {0,1,2,3} into slot 0 and signal rx_flags.\n//   6. The dispatcher picks up the slot, matches function_id \u2192 GRAPH_LAUNCH,\n//      acquires the idle worker, writes the slot device pointer into the\n//      pinned mailbox, and launches the graph.\n//   7. The graph reads the slot pointer from the mailbox, increments each\n//      payload byte, and writes an RPCResponse header in-place.\n//   8. Test polls tx_flags, syncs device, then asserts the response is\n//      {1,2,3,4} with correct magic/status/result_len.\n//==============================================================================\n\nTEST(HostDispatcherGraphLaunchTest, FullRpcRoundTripViaPinnedMailbox) {\n  constexpr std::size_t num_slots = 2;\n  constexpr std::size_t slot_size = 256;\n\n  // --- Ring buffers ---\n  // Separate flag arrays for RX and TX: the dispatcher clears rx_flags[slot]\n  // right after setting tx_flags[slot], so sharing would clobber the signal.\n  // Data buffers are shared (graph writes response in-place to the RX slot).\n  volatile uint64_t* rx_flags_host = nullptr;\n  volatile uint64_t* rx_flags_dev = nullptr;\n  std::uint8_t* rx_data_host = nullptr;\n  std::uint8_t* rx_data_dev = nullptr;\n  volatile uint64_t* tx_flags_host = nullptr;\n  volatile uint64_t* tx_flags_dev = nullptr;\n  std::uint8_t* tx_data_host_unused = nullptr;\n  std::uint8_t* tx_data_dev_unused = nullptr;\n\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &rx_flags_host,\n                                   &rx_flags_dev, &rx_data_host,\n                                   &rx_data_dev));\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &tx_flags_host,\n                                   &tx_flags_dev, &tx_data_host_unused,\n                                   &tx_data_dev_unused));\n\n  // --- Pinned mailbox ---\n  // cudaHostAllocMapped gives us host + device views of the same memory.\n  // The host dispatcher writes the slot device pointer to h_mailbox_bank[0];\n  // the graph reads it from d_mailbox_bank[0] (same physical location).\n  void** h_mailbox_bank = nullptr;\n  void** d_mailbox_bank = nullptr;\n  CUDA_CHECK(cudaHostAlloc(&h_mailbox_bank, sizeof(void*),\n                           cudaHostAllocMapped));\n  std::memset(h_mailbox_bank, 0, sizeof(void*));\n  CUDA_CHECK(\n      cudaHostGetDevicePointer((void**)&d_mailbox_bank, h_mailbox_bank, 0));\n\n  // --- Graph ---\n  // Capture graph_increment_kernel with d_mailbox_bank baked in as the\n  // kernel arg. At runtime the kernel reads *d_mailbox_bank to find\n  // the slot, so different slots can be processed on each launch.\n  cudaGraph_t graph = nullptr;\n  cudaGraphExec_t graph_exec = nullptr;\n  ASSERT_TRUE(\n      create_increment_graph(d_mailbox_bank, &graph, &graph_exec));\n\n  // --- Function table (one GRAPH_LAUNCH entry) ---\n  cudaq_function_entry_t host_table[1];\n  std::memset(host_table, 0, sizeof(host_table));\n  host_table[0].function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n  host_table[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  host_table[0].handler.graph_exec = graph_exec;\n\n  // --- C API: create manager + dispatcher ---\n  cudaq_dispatch_manager_t* manager = nullptr;\n  ASSERT_EQ(cudaq_dispatch_manager_create(&manager), CUDAQ_OK);\n\n  cudaq_dispatcher_config_t disp_config{};\n  disp_config.device_id = 0;\n  disp_config.num_slots = static_cast<uint32_t>(num_slots);\n  disp_config.slot_size = static_cast<uint32_t>(slot_size);\n  disp_config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n\n  cudaq_dispatcher_t* dispatcher = nullptr;\n  ASSERT_EQ(cudaq_dispatcher_create(manager, &disp_config, &dispatcher),\n            CUDAQ_OK);\n\n  // --- Wire ring buffer (rx/tx flags separate, data shared for in-place) ---\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_dev;\n  ringbuffer.tx_flags = tx_flags_dev;\n  ringbuffer.rx_data = rx_data_dev;\n  ringbuffer.tx_data = rx_data_dev;\n  ringbuffer.rx_stride_sz = slot_size;\n  ringbuffer.tx_stride_sz = slot_size;\n  ringbuffer.rx_flags_host = rx_flags_host;\n  ringbuffer.tx_flags_host = tx_flags_host;\n  ringbuffer.rx_data_host = rx_data_host;\n  ringbuffer.tx_data_host = rx_data_host;\n  ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher, &ringbuffer),\n            CUDAQ_OK);\n\n  cudaq_function_table_t table{};\n  table.entries = host_table;\n  table.count = 1;\n  ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher, &table),\n            CUDAQ_OK);\n\n  int shutdown_flag = 0;\n  uint64_t stats_counter = 0;\n  ASSERT_EQ(cudaq_dispatcher_set_control(dispatcher, &shutdown_flag,\n                                         &stats_counter),\n            CUDAQ_OK);\n\n  // Provide the caller-allocated pinned mailbox so the dispatcher uses it\n  // instead of allocating plain host memory (which the graph can't read).\n  ASSERT_EQ(cudaq_dispatcher_set_mailbox(dispatcher, h_mailbox_bank),\n            CUDAQ_OK);\n\n  // --- Start ---\n  ASSERT_EQ(cudaq_dispatcher_start(dispatcher), CUDAQ_OK);\n\n  // --- Send RPC request (simulates FPGA / producer) ---\n  const std::uint8_t payload[] = {0, 1, 2, 3};\n  ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                &ringbuffer, 0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4, 0, 0),\n            CUDAQ_OK);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer, 0);\n\n  // --- Verify: dispatcher picked up slot and launched graph ---\n  int cuda_err = 0;\n  cudaq_tx_status_t st = CUDAQ_TX_EMPTY;\n  for (int i = 0; i < 5000 && st == CUDAQ_TX_EMPTY; ++i) {\n    usleep(200);\n    st = cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer, 0, &cuda_err);\n  }\n  ASSERT_NE(st, CUDAQ_TX_EMPTY) << \"Timeout waiting for tx flag\";\n  ASSERT_NE(st, CUDAQ_TX_ERROR)\n      << \"Dispatcher reported graph launch error (cuda_err=\" << cuda_err << \")\";\n\n  // cudaGraphLaunch is async; sync device so the in-place response is visible\n  CUDA_CHECK(cudaDeviceSynchronize());\n\n  // --- Verify: graph wrote correct response in-place ---\n  std::uint8_t* slot_data = rx_data_host + 0 * slot_size;\n  auto* resp = reinterpret_cast<cudaq::realtime::RPCResponse*>(slot_data);\n  ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE)\n      << \"Expected response magic (graph in-place write)\";\n  ASSERT_EQ(resp->status, 0);\n  ASSERT_EQ(resp->result_len, 4u);\n  std::uint8_t* result = slot_data + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(result[0], 1);\n  EXPECT_EQ(result[1], 2);\n  EXPECT_EQ(result[2], 3);\n  EXPECT_EQ(result[3], 4);\n\n  // --- Teardown (C API handles thread join) ---\n  shutdown_flag = 1;\n  __sync_synchronize();\n  cudaq_dispatcher_stop(dispatcher);\n  cudaq_dispatcher_destroy(dispatcher);\n  cudaq_dispatch_manager_destroy(manager);\n\n  cudaGraphExecDestroy(graph_exec);\n  cudaGraphDestroy(graph);\n  cudaFreeHost(h_mailbox_bank);\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host_unused);\n}\n\n//==============================================================================\n// Test 3: Multiple workers with function_id routing (internal API)\n//\n// Two workers: worker 0 runs graph_increment_kernel (func_id A),\n// worker 1 runs graph_double_kernel (func_id B). Sends one RPC per worker\n// and verifies each graph produced the expected output, confirming the\n// dispatcher routed by function_id.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, MultiWorkerFunctionIdRouting) {\n  cudaGraph_t inc_graph = nullptr;\n  cudaGraphExec_t inc_exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_ + 0, &inc_graph, &inc_exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, inc_exec, inc_graph);\n\n  cudaGraph_t dbl_graph = nullptr;\n  cudaGraphExec_t dbl_exec = nullptr;\n  ASSERT_TRUE(create_double_graph(d_mailbox_bank_ + 1, &dbl_graph, &dbl_exec));\n  AddWorker(RPC_GRAPH_DOUBLE_FUNCTION_ID, dbl_exec, dbl_graph);\n\n  StartLoop();\n\n  const std::uint8_t payload[] = {1, 2, 3, 4};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n  WriteRpcRequest(1, RPC_GRAPH_DOUBLE_FUNCTION_ID, payload, 4);\n  SignalSlot(0);\n  SignalSlot(1);\n\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on slot 0 (increment)\";\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on slot 1 (double)\";\n  ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n  const std::uint8_t expected_inc[] = {2, 3, 4, 5};\n  const std::uint8_t expected_dbl[] = {2, 4, 6, 8};\n  VerifyResponse(0, expected_inc, 4);\n  VerifyResponse(1, expected_dbl, 4);\n}\n\n//==============================================================================\n// Test 4: Worker recycling \u2014 idle_mask round-trip (internal API)\n//\n// One worker, two sequential RPCs to the same slot. The second dispatch\n// can only proceed after the test restores idle_mask (simulating the\n// external worker thread that returns the worker to the pool).\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, WorkerRecycling) {\n  cudaGraph_t graph = nullptr;\n  cudaGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  // RPC 1 on slot 0 \u2014 after dispatch, current_slot advances to 1.\n  const std::uint8_t payload1[] = {0, 1, 2, 3};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload1, 4);\n  SignalSlot(0);\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on first RPC\";\n  ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n  const std::uint8_t expected1[] = {1, 2, 3, 4};\n  VerifyResponse(0, expected1, 4);\n\n  RestoreWorker(0);\n\n  // RPC 2 on slot 1 \u2014 the dispatcher is now polling slot 1.\n  // This can only dispatch if idle_mask was properly restored above.\n  const std::uint8_t payload2[] = {10, 11, 12, 13};\n  WriteRpcRequest(1, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload2, 4);\n  SignalSlot(1);\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on second RPC (worker not recycled?)\";\n  ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n  const std::uint8_t expected2[] = {11, 12, 13, 14};\n  VerifyResponse(1, expected2, 4);\n}\n\n//==============================================================================\n// Test 5: Backpressure \u2014 dispatcher stalls when all workers are busy\n//\n// One worker, two slots signalled simultaneously. Slot 0 dispatches\n// immediately; slot 1 stalls until the test restores idle_mask.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, BackpressureWhenAllBusy) {\n  cudaGraph_t graph = nullptr;\n  cudaGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  const std::uint8_t payload0[] = {0, 1, 2, 3};\n  const std::uint8_t payload1[] = {10, 11, 12, 13};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload0, 4);\n  WriteRpcRequest(1, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload1, 4);\n  SignalSlot(0);\n  SignalSlot(1);\n\n  // sweep_completed_workers auto-releases workers, so both slots\n  // complete without an explicit RestoreWorker call.\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on slot 0\";\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on slot 1\";\n  ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n  const std::uint8_t expected0[] = {1, 2, 3, 4};\n  const std::uint8_t expected1[] = {11, 12, 13, 14};\n  VerifyResponse(0, expected0, 4);\n  VerifyResponse(1, expected1, 4);\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire), 2u);\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, 2u);\n}\n\n//==============================================================================\n// Test 6: Stats counter accuracy (internal API)\n//\n// Sends 5 sequential RPCs through a single worker (recycling between each)\n// and verifies stats_counter == 5 at the end.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, StatsCounterAccuracy) {\n  cudaGraph_t graph = nullptr;\n  cudaGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  // Sequential RPCs through slots 0,1,2,3,0 \u2014 the dispatcher advances\n  // current_slot after each dispatch, so each RPC must target the next slot.\n  // When wrapping back to slot 0 for the 5th RPC, clear its tx_flags first.\n  constexpr int kNumRpcs = 5;\n  for (int i = 0; i < kNumRpcs; ++i) {\n    std::size_t slot = static_cast<std::size_t>(i % num_slots_);\n    if (i >= static_cast<int>(num_slots_))\n      ClearSlot(slot);\n\n    std::uint8_t payload[] = {\n        static_cast<std::uint8_t>(i * 10),\n        static_cast<std::uint8_t>(i * 10 + 1),\n        static_cast<std::uint8_t>(i * 10 + 2),\n        static_cast<std::uint8_t>(i * 10 + 3)};\n    WriteRpcRequest(slot, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n    SignalSlot(slot);\n    ASSERT_TRUE(PollTxFlag(slot)) << \"Timeout on RPC \" << i << \" (slot \" << slot << \")\";\n    ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n    std::uint8_t expected[] = {\n        static_cast<std::uint8_t>(i * 10 + 1),\n        static_cast<std::uint8_t>(i * 10 + 2),\n        static_cast<std::uint8_t>(i * 10 + 3),\n        static_cast<std::uint8_t>(i * 10 + 4)};\n    VerifyResponse(slot, expected, 4);\n\n    RestoreWorker(0);\n  }\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire),\n            static_cast<uint64_t>(kNumRpcs));\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, static_cast<uint64_t>(kNumRpcs));\n}\n\n//==============================================================================\n// Test 7: Multi-slot round-robin dispatch (internal API)\n//\n// 4 slots, 4 workers (all same function_id). All slots signalled at once;\n// the dispatcher processes them 0 \u2192 1 \u2192 2 \u2192 3 using one worker each.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, MultiSlotRoundRobin) {\n  constexpr int kNumSlots = 4;\n  cudaGraph_t graphs[kNumSlots];\n  cudaGraphExec_t execs[kNumSlots];\n  for (int i = 0; i < kNumSlots; ++i) {\n    ASSERT_TRUE(create_increment_graph(d_mailbox_bank_ + i, &graphs[i],\n                                       &execs[i]));\n    AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, execs[i], graphs[i]);\n  }\n\n  StartLoop();\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    std::uint8_t payload[] = {\n        static_cast<std::uint8_t>(i * 4 + 1),\n        static_cast<std::uint8_t>(i * 4 + 2),\n        static_cast<std::uint8_t>(i * 4 + 3),\n        static_cast<std::uint8_t>(i * 4 + 4)};\n    WriteRpcRequest(static_cast<std::size_t>(i),\n                    RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n  }\n\n  for (int i = 0; i < kNumSlots; ++i)\n    SignalSlot(static_cast<std::size_t>(i));\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    ASSERT_TRUE(PollTxFlag(static_cast<std::size_t>(i)))\n        << \"Timeout on slot \" << i;\n  }\n  ASSERT_EQ(cudaDeviceSynchronize(), cudaSuccess);\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    std::uint8_t expected[] = {\n        static_cast<std::uint8_t>(i * 4 + 2),\n        static_cast<std::uint8_t>(i * 4 + 3),\n        static_cast<std::uint8_t>(i * 4 + 4),\n        static_cast<std::uint8_t>(i * 4 + 5)};\n    VerifyResponse(static_cast<std::size_t>(i), expected, 4);\n  }\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire),\n            static_cast<uint64_t>(kNumSlots));\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, static_cast<uint64_t>(kNumSlots));\n}\n\n//==============================================================================\n// Test 8: GraphIOContext mode -- separate RX/TX buffers via HOST_LOOP C API\n//\n// Graph kernel reads GraphIOContext* from void** mailbox, reads input from\n// rx_slot, writes response to tx_slot, and signals *tx_flag = tx_flag_value.\n// HOST_LOOP fills the GraphIOContext before each launch (io_ctxs_host != NULL).\n//==============================================================================\n\n__global__ void graph_io_ctx_increment_kernel(void **mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    auto *io_ctx = reinterpret_cast<cudaq::realtime::GraphIOContext *>(\n        *mailbox_slot_ptr);\n    if (!io_ctx || !io_ctx->rx_slot)\n      return;\n\n    auto *header =\n        static_cast<cudaq::realtime::RPCHeader *>(io_ctx->rx_slot);\n    std::uint8_t *measurements =\n        reinterpret_cast<std::uint8_t *>(header + 1);\n\n    auto *response =\n        reinterpret_cast<cudaq::realtime::RPCResponse *>(io_ctx->tx_slot);\n    std::uint8_t *result = io_ctx->tx_slot + sizeof(cudaq::realtime::RPCResponse);\n\n    for (std::uint32_t i = 0; i < header->arg_len; ++i)\n      result[i] = measurements[i] + 1;\n\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = header->arg_len;\n    response->request_id = header->request_id;\n    response->ptp_timestamp = header->ptp_timestamp;\n\n    __threadfence_system();\n    if (io_ctx->tx_flag)\n      *(io_ctx->tx_flag) = io_ctx->tx_flag_value;\n  }\n}\n\nconstexpr std::uint32_t RPC_IO_CTX_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_io_ctx_increment\");\n\nTEST(HostDispatcherGraphIOContextTest, SeparateRxTxBuffersViaCApi) {\n  constexpr std::size_t num_slots = 2;\n  constexpr std::size_t slot_size = 256;\n\n  // Separate RX and TX ring buffers (flags and data)\n  volatile uint64_t *rx_flags_host = nullptr;\n  volatile uint64_t *rx_flags_dev = nullptr;\n  std::uint8_t *rx_data_host = nullptr;\n  std::uint8_t *rx_data_dev = nullptr;\n  volatile uint64_t *tx_flags_host = nullptr;\n  volatile uint64_t *tx_flags_dev = nullptr;\n  std::uint8_t *tx_data_host = nullptr;\n  std::uint8_t *tx_data_dev = nullptr;\n\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &rx_flags_host,\n                                   &rx_flags_dev, &rx_data_host,\n                                   &rx_data_dev));\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &tx_flags_host,\n                                   &tx_flags_dev, &tx_data_host,\n                                   &tx_data_dev));\n\n  // Pinned mailbox (graph kernel reads *d_mailbox to get GraphIOContext*)\n  void **h_mailbox_bank = nullptr;\n  void **d_mailbox_bank = nullptr;\n  CUDA_CHECK(\n      cudaHostAlloc(&h_mailbox_bank, sizeof(void *), cudaHostAllocMapped));\n  std::memset(h_mailbox_bank, 0, sizeof(void *));\n  CUDA_CHECK(\n      cudaHostGetDevicePointer((void **)&d_mailbox_bank, h_mailbox_bank, 0));\n\n  // Capture graph with the GraphIOContext kernel\n  cudaGraph_t graph = nullptr;\n  cudaGraphExec_t graph_exec = nullptr;\n  ASSERT_EQ(cudaGraphCreate(&graph, 0), cudaSuccess);\n  cudaKernelNodeParams params = {};\n  void *kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void *>(graph_io_ctx_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n  cudaGraphNode_t node = nullptr;\n  ASSERT_EQ(cudaGraphAddKernelNode(&node, graph, nullptr, 0, &params),\n            cudaSuccess);\n  ASSERT_EQ(cudaGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0),\n            cudaSuccess);\n\n  // Function table\n  cudaq_function_entry_t host_table[1];\n  std::memset(host_table, 0, sizeof(host_table));\n  host_table[0].function_id = RPC_IO_CTX_INCREMENT_FUNCTION_ID;\n  host_table[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  host_table[0].handler.graph_exec = graph_exec;\n\n  // C API: manager + dispatcher\n  cudaq_dispatch_manager_t *manager = nullptr;\n  ASSERT_EQ(cudaq_dispatch_manager_create(&manager), CUDAQ_OK);\n\n  cudaq_dispatcher_config_t disp_config{};\n  disp_config.device_id = 0;\n  disp_config.num_slots = static_cast<uint32_t>(num_slots);\n  disp_config.slot_size = static_cast<uint32_t>(slot_size);\n  disp_config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n\n  cudaq_dispatcher_t *dispatcher = nullptr;\n  ASSERT_EQ(cudaq_dispatcher_create(manager, &disp_config, &dispatcher),\n            CUDAQ_OK);\n\n  // Wire ringbuffer with SEPARATE rx_data and tx_data\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_dev;\n  ringbuffer.tx_flags = tx_flags_dev;\n  ringbuffer.rx_data = rx_data_dev;\n  ringbuffer.tx_data = tx_data_dev;\n  ringbuffer.rx_stride_sz = slot_size;\n  ringbuffer.tx_stride_sz = slot_size;\n  ringbuffer.rx_flags_host = rx_flags_host;\n  ringbuffer.tx_flags_host = tx_flags_host;\n  ringbuffer.rx_data_host = rx_data_host;\n  ringbuffer.tx_data_host = tx_data_host;\n  ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher, &ringbuffer), CUDAQ_OK);\n\n  cudaq_function_table_t table{};\n  table.entries = host_table;\n  table.count = 1;\n  ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher, &table), CUDAQ_OK);\n\n  int shutdown_flag = 0;\n  uint64_t stats_counter = 0;\n  ASSERT_EQ(cudaq_dispatcher_set_control(dispatcher, &shutdown_flag,\n                                         &stats_counter),\n            CUDAQ_OK);\n\n  ASSERT_EQ(cudaq_dispatcher_set_mailbox(dispatcher, h_mailbox_bank), CUDAQ_OK);\n  ASSERT_EQ(cudaq_dispatcher_start(dispatcher), CUDAQ_OK);\n\n  // Send RPC request\n  const std::uint8_t payload[] = {10, 20, 30, 40};\n  ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                &ringbuffer, 0, RPC_IO_CTX_INCREMENT_FUNCTION_ID, payload, 4,\n                42, 0),\n            CUDAQ_OK);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer, 0);\n\n  // Poll for READY (graph kernel sets tx_flag_value, not just IN_FLIGHT)\n  int cuda_err = 0;\n  cudaq_tx_status_t st = CUDAQ_TX_EMPTY;\n  for (int i = 0; i < 10000 && st != CUDAQ_TX_READY; ++i) {\n    usleep(200);\n    st = cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer, 0, &cuda_err);\n  }\n  ASSERT_EQ(st, CUDAQ_TX_READY)\n      << \"Expected READY from graph kernel tx_flag write\";\n  CUDA_CHECK(cudaDeviceSynchronize());\n\n  // Verify response in the SEPARATE TX buffer (not RX)\n  auto *resp = reinterpret_cast<cudaq::realtime::RPCResponse *>(tx_data_host);\n  ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE);\n  ASSERT_EQ(resp->status, 0);\n  ASSERT_EQ(resp->result_len, 4u);\n  ASSERT_EQ(resp->request_id, 42u);\n  std::uint8_t *result = tx_data_host + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(result[0], 11);\n  EXPECT_EQ(result[1], 21);\n  EXPECT_EQ(result[2], 31);\n  EXPECT_EQ(result[3], 41);\n\n  // Verify RX buffer is untouched (still has the request)\n  auto *rx_header = reinterpret_cast<cudaq::realtime::RPCHeader *>(rx_data_host);\n  EXPECT_EQ(rx_header->magic, CUDAQ_RPC_MAGIC_REQUEST);\n\n  // Teardown\n  shutdown_flag = 1;\n  __sync_synchronize();\n  cudaq_dispatcher_stop(dispatcher);\n  cudaq_dispatcher_destroy(dispatcher);\n  cudaq_dispatch_manager_destroy(manager);\n\n  cudaGraphExecDestroy(graph_exec);\n  cudaGraphDestroy(graph);\n  cudaFreeHost(h_mailbox_bank);\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host);\n}\n\n} // namespace\n", "output": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.\n * All rights reserved.\n *\n * This source code and the accompanying materials are made available under\n * the terms of the Apache License 2.0 which accompanies this distribution.\n ******************************************************************************/\n\n#include <gtest/gtest.h>\n#include <cuda/std/atomic>\n#include <hip/hip_runtime.h>\n#include <cstdint>\n#include <cstring>\n#include <thread>\n#include <unistd.h>\n#include <vector>\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/host_dispatcher.h\"\n\n#define CUDA_CHECK(call)                                                       \\\n  do {                                                                         \\\n    hipError_t err = call;                                                    \\\n    ASSERT_EQ(err, hipSuccess) << \"CUDA error: \" << hipGetErrorString(err);  \\\n  } while (0)\n\nnamespace {\n\n//==============================================================================\n// Ring buffer helpers (same pattern as test_dispatch_kernel.cu)\n//==============================================================================\n\nbool allocate_ring_buffer(std::size_t num_slots, std::size_t slot_size,\n                          volatile uint64_t** host_flags_out,\n                          volatile uint64_t** device_flags_out,\n                          std::uint8_t** host_data_out,\n                          std::uint8_t** device_data_out) {\n  void* host_flags_ptr = nullptr;\n  hipError_t err = hipHostAlloc(&host_flags_ptr,\n                                  num_slots * sizeof(uint64_t),\n                                  hipHostMallocMapped);\n  if (err != hipSuccess)\n    return false;\n\n  void* device_flags_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_flags_ptr, host_flags_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void* host_data_ptr = nullptr;\n  err = hipHostAlloc(&host_data_ptr, num_slots * slot_size,\n                      hipHostMallocMapped);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    return false;\n  }\n\n  void* device_data_ptr = nullptr;\n  err = hipHostGetDevicePointer(&device_data_ptr, host_data_ptr, 0);\n  if (err != hipSuccess) {\n    hipHostFree(host_flags_ptr);\n    hipHostFree(host_data_ptr);\n    return false;\n  }\n\n  std::memset(host_flags_ptr, 0, num_slots * sizeof(uint64_t));\n\n  *host_flags_out = static_cast<volatile uint64_t*>(host_flags_ptr);\n  *device_flags_out = static_cast<volatile uint64_t*>(device_flags_ptr);\n  *host_data_out = static_cast<std::uint8_t*>(host_data_ptr);\n  *device_data_out = static_cast<std::uint8_t*>(device_data_ptr);\n  return true;\n}\n\nvoid free_ring_buffer(volatile uint64_t* host_flags, std::uint8_t* host_data) {\n  if (host_flags)\n    hipHostFree(const_cast<uint64_t*>(host_flags));\n  if (host_data)\n    hipHostFree(host_data);\n}\n\n//==============================================================================\n// Minimal graph for dummy GRAPH_LAUNCH entry (so C API starts the host thread)\n//==============================================================================\n\n__global__ void noop_kernel() {}\n\n// Creates a minimal executable graph and returns it. Caller must destroy with\n// cudaGraphExecDestroy and cudaGraphDestroy.\nbool create_dummy_graph(hipGraph_t* graph_out, hipGraphExec_t* exec_out) {\n  hipGraph_t graph = nullptr;\n  if (hipGraphCreate(&graph, 0) != hipSuccess)\n    return false;\n\n  hipKernelNodeParams params = {};\n  void* args[] = {};\n  params.func = reinterpret_cast<void*>(noop_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(1, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = args;\n  params.extra = nullptr;\n\n  hipGraphNode_t node = nullptr;\n  if (hipGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  hipGraphExec_t exec = nullptr;\n  if (hipGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Graph launch test: kernel that reads slot from mailbox and writes response\n// in-place (same buffer as request; use single ring buffer for rx/tx).\n//==============================================================================\n\n__global__ void graph_increment_kernel(void** mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *mailbox_slot_ptr;\n    cudaq::realtime::RPCHeader* header =\n        static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    for (std::uint32_t i = 0; i < arg_len; ++i)\n      data[i] = data[i] + 1;\n    cudaq::realtime::RPCResponse* response =\n        static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_increment\");\n\n/// Creates an executable graph that runs graph_increment_kernel with\n/// kernel arg = d_mailbox_bank (device pointer to first mailbox slot).\n/// Caller must cudaGraphExecDestroy / cudaGraphDestroy.\nbool create_increment_graph(void** d_mailbox_bank, hipGraph_t* graph_out,\n                            hipGraphExec_t* exec_out) {\n  hipGraph_t graph = nullptr;\n  if (hipGraphCreate(&graph, 0) != hipSuccess)\n    return false;\n\n  // kernelParams[i] must be a *pointer to* the i-th argument value.\n  // The kernel takes void** so we pass &d_mailbox_bank (a void***).\n  hipKernelNodeParams params = {};\n  void* kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void*>(graph_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  hipGraphNode_t node = nullptr;\n  if (hipGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  hipGraphExec_t exec = nullptr;\n  if (hipGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Graph launch test: kernel that reads slot from mailbox and doubles payload\n// in-place (for function_id routing differentiation vs increment kernel).\n//==============================================================================\n\n__global__ void graph_double_kernel(void** mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    void* buffer = *mailbox_slot_ptr;\n    cudaq::realtime::RPCHeader* header =\n        static_cast<cudaq::realtime::RPCHeader*>(buffer);\n    std::uint32_t arg_len = header->arg_len;\n    void* arg_buffer = static_cast<void*>(header + 1);\n    std::uint8_t* data = static_cast<std::uint8_t*>(arg_buffer);\n    for (std::uint32_t i = 0; i < arg_len; ++i)\n      data[i] = data[i] * 2;\n    cudaq::realtime::RPCResponse* response =\n        static_cast<cudaq::realtime::RPCResponse*>(buffer);\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = arg_len;\n  }\n}\n\nconstexpr std::uint32_t RPC_GRAPH_DOUBLE_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_graph_double\");\n\nbool create_double_graph(void** d_mailbox_slot, hipGraph_t* graph_out,\n                         hipGraphExec_t* exec_out) {\n  hipGraph_t graph = nullptr;\n  if (hipGraphCreate(&graph, 0) != hipSuccess)\n    return false;\n\n  hipKernelNodeParams params = {};\n  void* kernel_args[] = {&d_mailbox_slot};\n  params.func = reinterpret_cast<void*>(graph_double_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  hipGraphNode_t node = nullptr;\n  if (hipGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  hipGraphExec_t exec = nullptr;\n  if (hipGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n\n//==============================================================================\n// Test fixture: drives host_dispatcher_loop directly (not C API) for full\n// control over idle_mask, enabling worker recycling and backpressure tests.\n//==============================================================================\n\nstatic constexpr std::size_t kMaxWorkers = 8;\n\nclass HostDispatcherLoopTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    std::memset(&config_, 0, sizeof(config_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_dev_, &rx_data_host_,\n                                     &rx_data_dev_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_dev_, &tx_data_host_,\n                                     &tx_data_dev_));\n\n    CUDA_CHECK(hipHostAlloc(&h_mailbox_bank_,\n                             kMaxWorkers * sizeof(void*),\n                             hipHostMallocMapped));\n    std::memset(h_mailbox_bank_, 0, kMaxWorkers * sizeof(void*));\n    CUDA_CHECK(hipHostGetDevicePointer(\n        reinterpret_cast<void**>(&d_mailbox_bank_), h_mailbox_bank_, 0));\n\n    idle_mask_ = new cuda::std::atomic<uint64_t>(0);\n    live_dispatched_ = new cuda::std::atomic<uint64_t>(0);\n    inflight_slot_tags_ = new int[kMaxWorkers]();\n    shutdown_flag_ = new cuda::std::atomic<int>(0);\n    stats_counter_ = 0;\n\n    function_table_ = new cudaq_function_entry_t[kMaxWorkers];\n    std::memset(function_table_, 0, kMaxWorkers * sizeof(cudaq_function_entry_t));\n\n    std::memset(&ringbuffer_, 0, sizeof(ringbuffer_));\n    ringbuffer_.rx_flags = rx_flags_dev_;\n    ringbuffer_.tx_flags = tx_flags_dev_;\n    ringbuffer_.rx_data = rx_data_dev_;\n    ringbuffer_.tx_data = tx_data_dev_;\n    ringbuffer_.rx_stride_sz = slot_size_;\n    ringbuffer_.tx_stride_sz = slot_size_;\n    ringbuffer_.rx_flags_host = rx_flags_host_;\n    ringbuffer_.tx_flags_host = tx_flags_host_;\n    ringbuffer_.rx_data_host = rx_data_host_;\n    ringbuffer_.tx_data_host = tx_data_host_;\n  }\n\n  void TearDown() override {\n    if (!loop_stopped_) {\n      shutdown_flag_->store(1, cuda::std::memory_order_release);\n      __sync_synchronize();\n      if (loop_thread_.joinable())\n        loop_thread_.join();\n    }\n\n    for (auto& w : worker_info_) {\n      if (w.stream)\n        hipStreamDestroy(w.stream);\n      if (w.graph_exec)\n        hipGraphExecDestroy(w.graph_exec);\n      if (w.graph)\n        hipGraphDestroy(w.graph);\n    }\n\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n    if (h_mailbox_bank_)\n      hipHostFree(h_mailbox_bank_);\n    delete idle_mask_;\n    delete live_dispatched_;\n    delete[] inflight_slot_tags_;\n    delete shutdown_flag_;\n    delete[] function_table_;\n  }\n\n  struct WorkerInfo {\n    hipGraphExec_t graph_exec = nullptr;\n    hipGraph_t graph = nullptr;\n    hipStream_t stream = nullptr;\n  };\n\n  void AddWorker(std::uint32_t function_id, hipGraphExec_t exec,\n                 hipGraph_t graph) {\n    hipStream_t stream = nullptr;\n    ASSERT_EQ(hipStreamCreate(&stream), hipSuccess);\n\n    cudaq_host_dispatch_worker_t w{};\n    w.graph_exec = exec;\n    w.stream = stream;\n    w.function_id = function_id;\n    workers_.push_back(w);\n    worker_info_.push_back({exec, graph, stream});\n\n    std::size_t idx = function_table_count_;\n    function_table_[idx].handler.graph_exec = exec;\n    function_table_[idx].function_id = function_id;\n    function_table_[idx].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n    function_table_count_++;\n  }\n\n  void StartLoop() {\n    idle_mask_->store((1ULL << workers_.size()) - 1,\n                      cuda::std::memory_order_release);\n\n    config_.ringbuffer.rx_flags_host = rx_flags_host_;\n    config_.ringbuffer.tx_flags_host = tx_flags_host_;\n    config_.ringbuffer.rx_data_host = rx_data_host_;\n    config_.ringbuffer.rx_data = rx_data_dev_;\n    config_.ringbuffer.tx_data_host = tx_data_host_;\n    config_.ringbuffer.tx_data = tx_data_dev_;\n    config_.ringbuffer.tx_stride_sz = slot_size_;\n    config_.ringbuffer.tx_flags = tx_flags_dev_;\n    config_.config.num_slots = static_cast<uint32_t>(num_slots_);\n    config_.config.slot_size = static_cast<uint32_t>(slot_size_);\n    config_.function_table.entries = function_table_;\n    config_.function_table.count = static_cast<uint32_t>(function_table_count_);\n    config_.workers = workers_.data();\n    config_.num_workers = workers_.size();\n    config_.h_mailbox_bank = h_mailbox_bank_;\n    config_.shutdown_flag = shutdown_flag_;\n    config_.stats_counter = &stats_counter_;\n    config_.live_dispatched = live_dispatched_;\n    config_.idle_mask = idle_mask_;\n    config_.inflight_slot_tags = inflight_slot_tags_;\n\n    loop_thread_ = std::thread([this]() {\n      cudaq_host_dispatcher_loop(&config_);\n    });\n  }\n\n  void WriteRpcRequest(std::size_t slot, std::uint32_t function_id,\n                       const std::uint8_t* payload, std::size_t len) {\n    ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                  &ringbuffer_, static_cast<uint32_t>(slot), function_id,\n                  payload, static_cast<uint32_t>(len), 0, 0),\n              CUDAQ_OK);\n  }\n\n  void SignalSlot(std::size_t slot) {\n    cudaq_host_ringbuffer_signal_slot(&ringbuffer_, static_cast<uint32_t>(slot));\n  }\n\n  bool PollTxFlag(std::size_t slot, int timeout_ms = 2000) {\n    for (int waited = 0; waited < timeout_ms * 1000; waited += 200) {\n      cudaq_tx_status_t st = cudaq_host_ringbuffer_poll_tx_flag(\n          &ringbuffer_, static_cast<uint32_t>(slot), nullptr);\n      if (st != CUDAQ_TX_EMPTY)\n        return true;\n      usleep(200);\n    }\n    return cudaq_host_ringbuffer_poll_tx_flag(\n               &ringbuffer_, static_cast<uint32_t>(slot), nullptr) !=\n           CUDAQ_TX_EMPTY;\n  }\n\n  void StopLoop() {\n    shutdown_flag_->store(1, cuda::std::memory_order_release);\n    __sync_synchronize();\n    if (loop_thread_.joinable())\n      loop_thread_.join();\n    loop_stopped_ = true;\n  }\n\n  void RestoreWorker(int worker_id) {\n    idle_mask_->fetch_or(1ULL << worker_id, cuda::std::memory_order_release);\n  }\n\n  void ClearSlot(std::size_t slot) {\n    cudaq_host_ringbuffer_clear_slot(&ringbuffer_, static_cast<uint32_t>(slot));\n    std::memset(rx_data_host_ + slot * slot_size_, 0, slot_size_);\n  }\n\n  void VerifyResponse(std::size_t slot, const std::uint8_t* expected,\n                      std::size_t len) {\n    int cuda_err = 0;\n    cudaq_tx_status_t st = cudaq_host_ringbuffer_poll_tx_flag(\n        &ringbuffer_, static_cast<uint32_t>(slot), &cuda_err);\n    ASSERT_TRUE(st == CUDAQ_TX_READY || st == CUDAQ_TX_IN_FLIGHT)\n        << \"slot \" << slot\n        << \": tx_flag unexpected (status=\" << st << \" cuda_err=\" << cuda_err << \")\";\n\n    std::uint8_t* slot_data = rx_data_host_ + slot * slot_size_;\n    auto* resp =\n        reinterpret_cast<cudaq::realtime::RPCResponse*>(slot_data);\n    ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE)\n        << \"slot \" << slot << \": expected response magic\";\n    ASSERT_EQ(resp->status, 0) << \"slot \" << slot << \": non-zero status\";\n    ASSERT_EQ(resp->result_len, static_cast<std::uint32_t>(len))\n        << \"slot \" << slot << \": wrong result_len\";\n    std::uint8_t* result = slot_data + sizeof(cudaq::realtime::RPCResponse);\n    for (std::size_t i = 0; i < len; ++i) {\n      EXPECT_EQ(result[i], expected[i])\n          << \"slot \" << slot << \" byte \" << i;\n    }\n  }\n\n  std::size_t num_slots_ = 4;\n  std::size_t slot_size_ = 256;\n\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_dev_ = nullptr;\n  volatile uint64_t* tx_flags_dev_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_dev_ = nullptr;\n  std::uint8_t* tx_data_dev_ = nullptr;\n\n  void** h_mailbox_bank_ = nullptr;\n  void** d_mailbox_bank_ = nullptr;\n\n  cuda::std::atomic<uint64_t>* idle_mask_ = nullptr;\n  cuda::std::atomic<uint64_t>* live_dispatched_ = nullptr;\n  int* inflight_slot_tags_ = nullptr;\n  cuda::std::atomic<int>* shutdown_flag_ = nullptr;\n  uint64_t stats_counter_ = 0;\n  bool loop_stopped_ = false;\n\n  cudaq_function_entry_t* function_table_ = nullptr;\n  std::size_t function_table_count_ = 0;\n  std::vector<cudaq_host_dispatch_worker_t> workers_;\n  std::vector<WorkerInfo> worker_info_;\n\n  cudaq_ringbuffer_t ringbuffer_{};\n  cudaq_host_dispatch_loop_ctx_t config_;\n  std::thread loop_thread_;\n};\n\n//==============================================================================\n// Test 1: Smoke test \u2014 host loop starts and drops slot with unknown function_id\n//==============================================================================\n\nconstexpr std::uint32_t DUMMY_GRAPH_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"dummy_graph\");\n// Use a different function_id in the slot so the host loop does not find it.\nconstexpr std::uint32_t UNKNOWN_FUNCTION_ID = 0xdeadbeefu;\n\nclass HostDispatcherSmokeTest : public ::testing::Test {\nprotected:\n  void SetUp() override {\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &rx_flags_host_,\n                                     &rx_flags_, &rx_data_host_, &rx_data_));\n    ASSERT_TRUE(allocate_ring_buffer(num_slots_, slot_size_, &tx_flags_host_,\n                                     &tx_flags_, &tx_data_host_, &tx_data_));\n\n    shutdown_flag_ = new (std::nothrow) int(0);\n    stats_ = new (std::nothrow) uint64_t(0);\n    ASSERT_NE(shutdown_flag_, nullptr);\n    ASSERT_NE(stats_, nullptr);\n\n    ASSERT_TRUE(create_dummy_graph(&dummy_graph_, &dummy_graph_exec_));\n\n    host_table_ = new (std::nothrow) cudaq_function_entry_t[1];\n    ASSERT_NE(host_table_, nullptr);\n    std::memset(host_table_, 0, sizeof(cudaq_function_entry_t));\n    host_table_[0].handler.graph_exec = dummy_graph_exec_;\n    host_table_[0].function_id = DUMMY_GRAPH_FUNCTION_ID;\n    host_table_[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n\n    ASSERT_EQ(cudaq_dispatch_manager_create(&manager_), CUDAQ_OK);\n    cudaq_dispatcher_config_t config{};\n    config.device_id = 0;\n    config.num_slots = static_cast<uint32_t>(num_slots_);\n    config.slot_size = static_cast<uint32_t>(slot_size_);\n    config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n    ASSERT_EQ(cudaq_dispatcher_create(manager_, &config, &dispatcher_),\n              CUDAQ_OK);\n\n    std::memset(&ringbuffer_, 0, sizeof(ringbuffer_));\n    ringbuffer_.rx_flags = rx_flags_;\n    ringbuffer_.tx_flags = tx_flags_;\n    ringbuffer_.rx_data = rx_data_;\n    ringbuffer_.tx_data = tx_data_;\n    ringbuffer_.rx_stride_sz = slot_size_;\n    ringbuffer_.tx_stride_sz = slot_size_;\n    ringbuffer_.rx_flags_host = rx_flags_host_;\n    ringbuffer_.tx_flags_host = tx_flags_host_;\n    ringbuffer_.rx_data_host = rx_data_host_;\n    ringbuffer_.tx_data_host = tx_data_host_;\n    ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher_, &ringbuffer_),\n              CUDAQ_OK);\n\n    cudaq_function_table_t table{};\n    table.entries = host_table_;\n    table.count = 1;\n    ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher_, &table),\n              CUDAQ_OK);\n\n    ASSERT_EQ(\n        cudaq_dispatcher_set_control(dispatcher_, shutdown_flag_, stats_),\n        CUDAQ_OK);\n    ASSERT_EQ(cudaq_dispatcher_start(dispatcher_), CUDAQ_OK);\n  }\n\n  void TearDown() override {\n    if (shutdown_flag_) {\n      *shutdown_flag_ = 1;\n      __sync_synchronize();\n    }\n    if (dispatcher_) {\n      cudaq_dispatcher_stop(dispatcher_);\n      cudaq_dispatcher_destroy(dispatcher_);\n      dispatcher_ = nullptr;\n    }\n    if (manager_) {\n      cudaq_dispatch_manager_destroy(manager_);\n      manager_ = nullptr;\n    }\n    free_ring_buffer(rx_flags_host_, rx_data_host_);\n    free_ring_buffer(tx_flags_host_, tx_data_host_);\n    if (shutdown_flag_)\n      delete shutdown_flag_;\n    if (stats_)\n      delete stats_;\n    if (host_table_)\n      delete[] host_table_;\n    if (dummy_graph_exec_)\n      hipGraphExecDestroy(dummy_graph_exec_);\n    if (dummy_graph_)\n      hipGraphDestroy(dummy_graph_);\n  }\n\n  void write_rpc_request_unknown_function(std::size_t slot) {\n    const std::uint8_t payload[] = {0, 1, 2, 3};\n    ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                  &ringbuffer_, static_cast<uint32_t>(slot),\n                  UNKNOWN_FUNCTION_ID, payload, 4, 0, 0),\n              CUDAQ_OK);\n  }\n\n  static constexpr std::size_t num_slots_ = 2;\n  std::size_t slot_size_ = 256;\n\n  volatile uint64_t* rx_flags_host_ = nullptr;\n  volatile uint64_t* tx_flags_host_ = nullptr;\n  volatile uint64_t* rx_flags_ = nullptr;\n  volatile uint64_t* tx_flags_ = nullptr;\n  std::uint8_t* rx_data_host_ = nullptr;\n  std::uint8_t* tx_data_host_ = nullptr;\n  std::uint8_t* rx_data_ = nullptr;\n  std::uint8_t* tx_data_ = nullptr;\n\n  int* shutdown_flag_ = nullptr;\n  uint64_t* stats_ = nullptr;\n  cudaq_function_entry_t* host_table_ = nullptr;\n  hipGraph_t dummy_graph_ = nullptr;\n  hipGraphExec_t dummy_graph_exec_ = nullptr;\n\n  cudaq_ringbuffer_t ringbuffer_{};\n  cudaq_dispatch_manager_t* manager_ = nullptr;\n  cudaq_dispatcher_t* dispatcher_ = nullptr;\n};\n\nTEST_F(HostDispatcherSmokeTest, DropsSlotWithUnknownFunctionId) {\n  write_rpc_request_unknown_function(0);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer_, 0);\n\n  for (int i = 0; i < 50; ++i) {\n    usleep(1000);\n    cudaq_tx_status_t st =\n        cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer_, 0, nullptr);\n    if (st != CUDAQ_TX_EMPTY)\n      break;\n  }\n\n  cudaq_tx_status_t final_st =\n      cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer_, 0, nullptr);\n  EXPECT_EQ(final_st, CUDAQ_TX_EMPTY)\n      << \"Host loop should drop slot with unknown function_id (no response)\";\n}\n\n//==============================================================================\n// Test 2: GRAPH_LAUNCH via host loop (full RPC round-trip) using the C API\n//\n// End-to-end test of: RPC in ring buffer \u2192 C API dispatcher \u2192 CUDA graph\n// launch via pinned mailbox \u2192 in-place response.\n//\n// Flow:\n//   1. Allocate pinned ring buffers and pinned mailbox (cudaHostAllocMapped).\n//   2. Capture graph_increment_kernel with d_mailbox_bank baked in.\n//   3. Build function table with one GRAPH_LAUNCH entry.\n//   4. Wire the C API: manager \u2192 dispatcher \u2192 ringbuffer, function table,\n//      control, mailbox \u2192 start.\n//   5. Write an RPC request {0,1,2,3} into slot 0 and signal rx_flags.\n//   6. The dispatcher picks up the slot, matches function_id \u2192 GRAPH_LAUNCH,\n//      acquires the idle worker, writes the slot device pointer into the\n//      pinned mailbox, and launches the graph.\n//   7. The graph reads the slot pointer from the mailbox, increments each\n//      payload byte, and writes an RPCResponse header in-place.\n//   8. Test polls tx_flags, syncs device, then asserts the response is\n//      {1,2,3,4} with correct magic/status/result_len.\n//==============================================================================\n\nTEST(HostDispatcherGraphLaunchTest, FullRpcRoundTripViaPinnedMailbox) {\n  constexpr std::size_t num_slots = 2;\n  constexpr std::size_t slot_size = 256;\n\n  // --- Ring buffers ---\n  // Separate flag arrays for RX and TX: the dispatcher clears rx_flags[slot]\n  // right after setting tx_flags[slot], so sharing would clobber the signal.\n  // Data buffers are shared (graph writes response in-place to the RX slot).\n  volatile uint64_t* rx_flags_host = nullptr;\n  volatile uint64_t* rx_flags_dev = nullptr;\n  std::uint8_t* rx_data_host = nullptr;\n  std::uint8_t* rx_data_dev = nullptr;\n  volatile uint64_t* tx_flags_host = nullptr;\n  volatile uint64_t* tx_flags_dev = nullptr;\n  std::uint8_t* tx_data_host_unused = nullptr;\n  std::uint8_t* tx_data_dev_unused = nullptr;\n\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &rx_flags_host,\n                                   &rx_flags_dev, &rx_data_host,\n                                   &rx_data_dev));\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &tx_flags_host,\n                                   &tx_flags_dev, &tx_data_host_unused,\n                                   &tx_data_dev_unused));\n\n  // --- Pinned mailbox ---\n  // cudaHostAllocMapped gives us host + device views of the same memory.\n  // The host dispatcher writes the slot device pointer to h_mailbox_bank[0];\n  // the graph reads it from d_mailbox_bank[0] (same physical location).\n  void** h_mailbox_bank = nullptr;\n  void** d_mailbox_bank = nullptr;\n  CUDA_CHECK(hipHostAlloc(&h_mailbox_bank, sizeof(void*),\n                           hipHostMallocMapped));\n  std::memset(h_mailbox_bank, 0, sizeof(void*));\n  CUDA_CHECK(\n      hipHostGetDevicePointer((void**)&d_mailbox_bank, h_mailbox_bank, 0));\n\n  // --- Graph ---\n  // Capture graph_increment_kernel with d_mailbox_bank baked in as the\n  // kernel arg. At runtime the kernel reads *d_mailbox_bank to find\n  // the slot, so different slots can be processed on each launch.\n  hipGraph_t graph = nullptr;\n  hipGraphExec_t graph_exec = nullptr;\n  ASSERT_TRUE(\n      create_increment_graph(d_mailbox_bank, &graph, &graph_exec));\n\n  // --- Function table (one GRAPH_LAUNCH entry) ---\n  cudaq_function_entry_t host_table[1];\n  std::memset(host_table, 0, sizeof(host_table));\n  host_table[0].function_id = RPC_GRAPH_INCREMENT_FUNCTION_ID;\n  host_table[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  host_table[0].handler.graph_exec = graph_exec;\n\n  // --- C API: create manager + dispatcher ---\n  cudaq_dispatch_manager_t* manager = nullptr;\n  ASSERT_EQ(cudaq_dispatch_manager_create(&manager), CUDAQ_OK);\n\n  cudaq_dispatcher_config_t disp_config{};\n  disp_config.device_id = 0;\n  disp_config.num_slots = static_cast<uint32_t>(num_slots);\n  disp_config.slot_size = static_cast<uint32_t>(slot_size);\n  disp_config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n\n  cudaq_dispatcher_t* dispatcher = nullptr;\n  ASSERT_EQ(cudaq_dispatcher_create(manager, &disp_config, &dispatcher),\n            CUDAQ_OK);\n\n  // --- Wire ring buffer (rx/tx flags separate, data shared for in-place) ---\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_dev;\n  ringbuffer.tx_flags = tx_flags_dev;\n  ringbuffer.rx_data = rx_data_dev;\n  ringbuffer.tx_data = rx_data_dev;\n  ringbuffer.rx_stride_sz = slot_size;\n  ringbuffer.tx_stride_sz = slot_size;\n  ringbuffer.rx_flags_host = rx_flags_host;\n  ringbuffer.tx_flags_host = tx_flags_host;\n  ringbuffer.rx_data_host = rx_data_host;\n  ringbuffer.tx_data_host = rx_data_host;\n  ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher, &ringbuffer),\n            CUDAQ_OK);\n\n  cudaq_function_table_t table{};\n  table.entries = host_table;\n  table.count = 1;\n  ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher, &table),\n            CUDAQ_OK);\n\n  int shutdown_flag = 0;\n  uint64_t stats_counter = 0;\n  ASSERT_EQ(cudaq_dispatcher_set_control(dispatcher, &shutdown_flag,\n                                         &stats_counter),\n            CUDAQ_OK);\n\n  // Provide the caller-allocated pinned mailbox so the dispatcher uses it\n  // instead of allocating plain host memory (which the graph can't read).\n  ASSERT_EQ(cudaq_dispatcher_set_mailbox(dispatcher, h_mailbox_bank),\n            CUDAQ_OK);\n\n  // --- Start ---\n  ASSERT_EQ(cudaq_dispatcher_start(dispatcher), CUDAQ_OK);\n\n  // --- Send RPC request (simulates FPGA / producer) ---\n  const std::uint8_t payload[] = {0, 1, 2, 3};\n  ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                &ringbuffer, 0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4, 0, 0),\n            CUDAQ_OK);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer, 0);\n\n  // --- Verify: dispatcher picked up slot and launched graph ---\n  int cuda_err = 0;\n  cudaq_tx_status_t st = CUDAQ_TX_EMPTY;\n  for (int i = 0; i < 5000 && st == CUDAQ_TX_EMPTY; ++i) {\n    usleep(200);\n    st = cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer, 0, &cuda_err);\n  }\n  ASSERT_NE(st, CUDAQ_TX_EMPTY) << \"Timeout waiting for tx flag\";\n  ASSERT_NE(st, CUDAQ_TX_ERROR)\n      << \"Dispatcher reported graph launch error (cuda_err=\" << cuda_err << \")\";\n\n  // cudaGraphLaunch is async; sync device so the in-place response is visible\n  CUDA_CHECK(hipDeviceSynchronize());\n\n  // --- Verify: graph wrote correct response in-place ---\n  std::uint8_t* slot_data = rx_data_host + 0 * slot_size;\n  auto* resp = reinterpret_cast<cudaq::realtime::RPCResponse*>(slot_data);\n  ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE)\n      << \"Expected response magic (graph in-place write)\";\n  ASSERT_EQ(resp->status, 0);\n  ASSERT_EQ(resp->result_len, 4u);\n  std::uint8_t* result = slot_data + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(result[0], 1);\n  EXPECT_EQ(result[1], 2);\n  EXPECT_EQ(result[2], 3);\n  EXPECT_EQ(result[3], 4);\n\n  // --- Teardown (C API handles thread join) ---\n  shutdown_flag = 1;\n  __sync_synchronize();\n  cudaq_dispatcher_stop(dispatcher);\n  cudaq_dispatcher_destroy(dispatcher);\n  cudaq_dispatch_manager_destroy(manager);\n\n  hipGraphExecDestroy(graph_exec);\n  hipGraphDestroy(graph);\n  hipHostFree(h_mailbox_bank);\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host_unused);\n}\n\n//==============================================================================\n// Test 3: Multiple workers with function_id routing (internal API)\n//\n// Two workers: worker 0 runs graph_increment_kernel (func_id A),\n// worker 1 runs graph_double_kernel (func_id B). Sends one RPC per worker\n// and verifies each graph produced the expected output, confirming the\n// dispatcher routed by function_id.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, MultiWorkerFunctionIdRouting) {\n  hipGraph_t inc_graph = nullptr;\n  hipGraphExec_t inc_exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_ + 0, &inc_graph, &inc_exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, inc_exec, inc_graph);\n\n  hipGraph_t dbl_graph = nullptr;\n  hipGraphExec_t dbl_exec = nullptr;\n  ASSERT_TRUE(create_double_graph(d_mailbox_bank_ + 1, &dbl_graph, &dbl_exec));\n  AddWorker(RPC_GRAPH_DOUBLE_FUNCTION_ID, dbl_exec, dbl_graph);\n\n  StartLoop();\n\n  const std::uint8_t payload[] = {1, 2, 3, 4};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n  WriteRpcRequest(1, RPC_GRAPH_DOUBLE_FUNCTION_ID, payload, 4);\n  SignalSlot(0);\n  SignalSlot(1);\n\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on slot 0 (increment)\";\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on slot 1 (double)\";\n  ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n  const std::uint8_t expected_inc[] = {2, 3, 4, 5};\n  const std::uint8_t expected_dbl[] = {2, 4, 6, 8};\n  VerifyResponse(0, expected_inc, 4);\n  VerifyResponse(1, expected_dbl, 4);\n}\n\n//==============================================================================\n// Test 4: Worker recycling \u2014 idle_mask round-trip (internal API)\n//\n// One worker, two sequential RPCs to the same slot. The second dispatch\n// can only proceed after the test restores idle_mask (simulating the\n// external worker thread that returns the worker to the pool).\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, WorkerRecycling) {\n  hipGraph_t graph = nullptr;\n  hipGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  // RPC 1 on slot 0 \u2014 after dispatch, current_slot advances to 1.\n  const std::uint8_t payload1[] = {0, 1, 2, 3};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload1, 4);\n  SignalSlot(0);\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on first RPC\";\n  ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n  const std::uint8_t expected1[] = {1, 2, 3, 4};\n  VerifyResponse(0, expected1, 4);\n\n  RestoreWorker(0);\n\n  // RPC 2 on slot 1 \u2014 the dispatcher is now polling slot 1.\n  // This can only dispatch if idle_mask was properly restored above.\n  const std::uint8_t payload2[] = {10, 11, 12, 13};\n  WriteRpcRequest(1, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload2, 4);\n  SignalSlot(1);\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on second RPC (worker not recycled?)\";\n  ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n  const std::uint8_t expected2[] = {11, 12, 13, 14};\n  VerifyResponse(1, expected2, 4);\n}\n\n//==============================================================================\n// Test 5: Backpressure \u2014 dispatcher stalls when all workers are busy\n//\n// One worker, two slots signalled simultaneously. Slot 0 dispatches\n// immediately; slot 1 stalls until the test restores idle_mask.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, BackpressureWhenAllBusy) {\n  hipGraph_t graph = nullptr;\n  hipGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  const std::uint8_t payload0[] = {0, 1, 2, 3};\n  const std::uint8_t payload1[] = {10, 11, 12, 13};\n  WriteRpcRequest(0, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload0, 4);\n  WriteRpcRequest(1, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload1, 4);\n  SignalSlot(0);\n  SignalSlot(1);\n\n  // sweep_completed_workers auto-releases workers, so both slots\n  // complete without an explicit RestoreWorker call.\n  ASSERT_TRUE(PollTxFlag(0)) << \"Timeout on slot 0\";\n  ASSERT_TRUE(PollTxFlag(1)) << \"Timeout on slot 1\";\n  ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n  const std::uint8_t expected0[] = {1, 2, 3, 4};\n  const std::uint8_t expected1[] = {11, 12, 13, 14};\n  VerifyResponse(0, expected0, 4);\n  VerifyResponse(1, expected1, 4);\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire), 2u);\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, 2u);\n}\n\n//==============================================================================\n// Test 6: Stats counter accuracy (internal API)\n//\n// Sends 5 sequential RPCs through a single worker (recycling between each)\n// and verifies stats_counter == 5 at the end.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, StatsCounterAccuracy) {\n  hipGraph_t graph = nullptr;\n  hipGraphExec_t exec = nullptr;\n  ASSERT_TRUE(create_increment_graph(d_mailbox_bank_, &graph, &exec));\n  AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, exec, graph);\n\n  StartLoop();\n\n  // Sequential RPCs through slots 0,1,2,3,0 \u2014 the dispatcher advances\n  // current_slot after each dispatch, so each RPC must target the next slot.\n  // When wrapping back to slot 0 for the 5th RPC, clear its tx_flags first.\n  constexpr int kNumRpcs = 5;\n  for (int i = 0; i < kNumRpcs; ++i) {\n    std::size_t slot = static_cast<std::size_t>(i % num_slots_);\n    if (i >= static_cast<int>(num_slots_))\n      ClearSlot(slot);\n\n    std::uint8_t payload[] = {\n        static_cast<std::uint8_t>(i * 10),\n        static_cast<std::uint8_t>(i * 10 + 1),\n        static_cast<std::uint8_t>(i * 10 + 2),\n        static_cast<std::uint8_t>(i * 10 + 3)};\n    WriteRpcRequest(slot, RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n    SignalSlot(slot);\n    ASSERT_TRUE(PollTxFlag(slot)) << \"Timeout on RPC \" << i << \" (slot \" << slot << \")\";\n    ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n    std::uint8_t expected[] = {\n        static_cast<std::uint8_t>(i * 10 + 1),\n        static_cast<std::uint8_t>(i * 10 + 2),\n        static_cast<std::uint8_t>(i * 10 + 3),\n        static_cast<std::uint8_t>(i * 10 + 4)};\n    VerifyResponse(slot, expected, 4);\n\n    RestoreWorker(0);\n  }\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire),\n            static_cast<uint64_t>(kNumRpcs));\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, static_cast<uint64_t>(kNumRpcs));\n}\n\n//==============================================================================\n// Test 7: Multi-slot round-robin dispatch (internal API)\n//\n// 4 slots, 4 workers (all same function_id). All slots signalled at once;\n// the dispatcher processes them 0 \u2192 1 \u2192 2 \u2192 3 using one worker each.\n//==============================================================================\n\nTEST_F(HostDispatcherLoopTest, MultiSlotRoundRobin) {\n  constexpr int kNumSlots = 4;\n  hipGraph_t graphs[kNumSlots];\n  hipGraphExec_t execs[kNumSlots];\n  for (int i = 0; i < kNumSlots; ++i) {\n    ASSERT_TRUE(create_increment_graph(d_mailbox_bank_ + i, &graphs[i],\n                                       &execs[i]));\n    AddWorker(RPC_GRAPH_INCREMENT_FUNCTION_ID, execs[i], graphs[i]);\n  }\n\n  StartLoop();\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    std::uint8_t payload[] = {\n        static_cast<std::uint8_t>(i * 4 + 1),\n        static_cast<std::uint8_t>(i * 4 + 2),\n        static_cast<std::uint8_t>(i * 4 + 3),\n        static_cast<std::uint8_t>(i * 4 + 4)};\n    WriteRpcRequest(static_cast<std::size_t>(i),\n                    RPC_GRAPH_INCREMENT_FUNCTION_ID, payload, 4);\n  }\n\n  for (int i = 0; i < kNumSlots; ++i)\n    SignalSlot(static_cast<std::size_t>(i));\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    ASSERT_TRUE(PollTxFlag(static_cast<std::size_t>(i)))\n        << \"Timeout on slot \" << i;\n  }\n  ASSERT_EQ(hipDeviceSynchronize(), hipSuccess);\n\n  for (int i = 0; i < kNumSlots; ++i) {\n    std::uint8_t expected[] = {\n        static_cast<std::uint8_t>(i * 4 + 2),\n        static_cast<std::uint8_t>(i * 4 + 3),\n        static_cast<std::uint8_t>(i * 4 + 4),\n        static_cast<std::uint8_t>(i * 4 + 5)};\n    VerifyResponse(static_cast<std::size_t>(i), expected, 4);\n  }\n\n  EXPECT_EQ(live_dispatched_->load(cuda::std::memory_order_acquire),\n            static_cast<uint64_t>(kNumSlots));\n\n  StopLoop();\n  EXPECT_EQ(stats_counter_, static_cast<uint64_t>(kNumSlots));\n}\n\n//==============================================================================\n// Test 8: GraphIOContext mode -- separate RX/TX buffers via HOST_LOOP C API\n//\n// Graph kernel reads GraphIOContext* from void** mailbox, reads input from\n// rx_slot, writes response to tx_slot, and signals *tx_flag = tx_flag_value.\n// HOST_LOOP fills the GraphIOContext before each launch (io_ctxs_host != NULL).\n//==============================================================================\n\n__global__ void graph_io_ctx_increment_kernel(void **mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    auto *io_ctx = reinterpret_cast<cudaq::realtime::GraphIOContext *>(\n        *mailbox_slot_ptr);\n    if (!io_ctx || !io_ctx->rx_slot)\n      return;\n\n    auto *header =\n        static_cast<cudaq::realtime::RPCHeader *>(io_ctx->rx_slot);\n    std::uint8_t *measurements =\n        reinterpret_cast<std::uint8_t *>(header + 1);\n\n    auto *response =\n        reinterpret_cast<cudaq::realtime::RPCResponse *>(io_ctx->tx_slot);\n    std::uint8_t *result = io_ctx->tx_slot + sizeof(cudaq::realtime::RPCResponse);\n\n    for (std::uint32_t i = 0; i < header->arg_len; ++i)\n      result[i] = measurements[i] + 1;\n\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = header->arg_len;\n    response->request_id = header->request_id;\n    response->ptp_timestamp = header->ptp_timestamp;\n\n    __threadfence_system();\n    if (io_ctx->tx_flag)\n      *(io_ctx->tx_flag) = io_ctx->tx_flag_value;\n  }\n}\n\nconstexpr std::uint32_t RPC_IO_CTX_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_io_ctx_increment\");\n\nTEST(HostDispatcherGraphIOContextTest, SeparateRxTxBuffersViaCApi) {\n  constexpr std::size_t num_slots = 2;\n  constexpr std::size_t slot_size = 256;\n\n  // Separate RX and TX ring buffers (flags and data)\n  volatile uint64_t *rx_flags_host = nullptr;\n  volatile uint64_t *rx_flags_dev = nullptr;\n  std::uint8_t *rx_data_host = nullptr;\n  std::uint8_t *rx_data_dev = nullptr;\n  volatile uint64_t *tx_flags_host = nullptr;\n  volatile uint64_t *tx_flags_dev = nullptr;\n  std::uint8_t *tx_data_host = nullptr;\n  std::uint8_t *tx_data_dev = nullptr;\n\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &rx_flags_host,\n                                   &rx_flags_dev, &rx_data_host,\n                                   &rx_data_dev));\n  ASSERT_TRUE(allocate_ring_buffer(num_slots, slot_size, &tx_flags_host,\n                                   &tx_flags_dev, &tx_data_host,\n                                   &tx_data_dev));\n\n  // Pinned mailbox (graph kernel reads *d_mailbox to get GraphIOContext*)\n  void **h_mailbox_bank = nullptr;\n  void **d_mailbox_bank = nullptr;\n  CUDA_CHECK(\n      hipHostAlloc(&h_mailbox_bank, sizeof(void *), hipHostMallocMapped));\n  std::memset(h_mailbox_bank, 0, sizeof(void *));\n  CUDA_CHECK(\n      hipHostGetDevicePointer((void **)&d_mailbox_bank, h_mailbox_bank, 0));\n\n  // Capture graph with the GraphIOContext kernel\n  hipGraph_t graph = nullptr;\n  hipGraphExec_t graph_exec = nullptr;\n  ASSERT_EQ(hipGraphCreate(&graph, 0), hipSuccess);\n  hipKernelNodeParams params = {};\n  void *kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void *>(graph_io_ctx_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n  hipGraphNode_t node = nullptr;\n  ASSERT_EQ(hipGraphAddKernelNode(&node, graph, nullptr, 0, &params),\n            hipSuccess);\n  ASSERT_EQ(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0),\n            hipSuccess);\n\n  // Function table\n  cudaq_function_entry_t host_table[1];\n  std::memset(host_table, 0, sizeof(host_table));\n  host_table[0].function_id = RPC_IO_CTX_INCREMENT_FUNCTION_ID;\n  host_table[0].dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  host_table[0].handler.graph_exec = graph_exec;\n\n  // C API: manager + dispatcher\n  cudaq_dispatch_manager_t *manager = nullptr;\n  ASSERT_EQ(cudaq_dispatch_manager_create(&manager), CUDAQ_OK);\n\n  cudaq_dispatcher_config_t disp_config{};\n  disp_config.device_id = 0;\n  disp_config.num_slots = static_cast<uint32_t>(num_slots);\n  disp_config.slot_size = static_cast<uint32_t>(slot_size);\n  disp_config.dispatch_path = CUDAQ_DISPATCH_PATH_HOST;\n\n  cudaq_dispatcher_t *dispatcher = nullptr;\n  ASSERT_EQ(cudaq_dispatcher_create(manager, &disp_config, &dispatcher),\n            CUDAQ_OK);\n\n  // Wire ringbuffer with SEPARATE rx_data and tx_data\n  cudaq_ringbuffer_t ringbuffer{};\n  ringbuffer.rx_flags = rx_flags_dev;\n  ringbuffer.tx_flags = tx_flags_dev;\n  ringbuffer.rx_data = rx_data_dev;\n  ringbuffer.tx_data = tx_data_dev;\n  ringbuffer.rx_stride_sz = slot_size;\n  ringbuffer.tx_stride_sz = slot_size;\n  ringbuffer.rx_flags_host = rx_flags_host;\n  ringbuffer.tx_flags_host = tx_flags_host;\n  ringbuffer.rx_data_host = rx_data_host;\n  ringbuffer.tx_data_host = tx_data_host;\n  ASSERT_EQ(cudaq_dispatcher_set_ringbuffer(dispatcher, &ringbuffer), CUDAQ_OK);\n\n  cudaq_function_table_t table{};\n  table.entries = host_table;\n  table.count = 1;\n  ASSERT_EQ(cudaq_dispatcher_set_function_table(dispatcher, &table), CUDAQ_OK);\n\n  int shutdown_flag = 0;\n  uint64_t stats_counter = 0;\n  ASSERT_EQ(cudaq_dispatcher_set_control(dispatcher, &shutdown_flag,\n                                         &stats_counter),\n            CUDAQ_OK);\n\n  ASSERT_EQ(cudaq_dispatcher_set_mailbox(dispatcher, h_mailbox_bank), CUDAQ_OK);\n  ASSERT_EQ(cudaq_dispatcher_start(dispatcher), CUDAQ_OK);\n\n  // Send RPC request\n  const std::uint8_t payload[] = {10, 20, 30, 40};\n  ASSERT_EQ(cudaq_host_ringbuffer_write_rpc_request(\n                &ringbuffer, 0, RPC_IO_CTX_INCREMENT_FUNCTION_ID, payload, 4,\n                42, 0),\n            CUDAQ_OK);\n  cudaq_host_ringbuffer_signal_slot(&ringbuffer, 0);\n\n  // Poll for READY (graph kernel sets tx_flag_value, not just IN_FLIGHT)\n  int cuda_err = 0;\n  cudaq_tx_status_t st = CUDAQ_TX_EMPTY;\n  for (int i = 0; i < 10000 && st != CUDAQ_TX_READY; ++i) {\n    usleep(200);\n    st = cudaq_host_ringbuffer_poll_tx_flag(&ringbuffer, 0, &cuda_err);\n  }\n  ASSERT_EQ(st, CUDAQ_TX_READY)\n      << \"Expected READY from graph kernel tx_flag write\";\n  CUDA_CHECK(hipDeviceSynchronize());\n\n  // Verify response in the SEPARATE TX buffer (not RX)\n  auto *resp = reinterpret_cast<cudaq::realtime::RPCResponse *>(tx_data_host);\n  ASSERT_EQ(resp->magic, CUDAQ_RPC_MAGIC_RESPONSE);\n  ASSERT_EQ(resp->status, 0);\n  ASSERT_EQ(resp->result_len, 4u);\n  ASSERT_EQ(resp->request_id, 42u);\n  std::uint8_t *result = tx_data_host + sizeof(cudaq::realtime::RPCResponse);\n  EXPECT_EQ(result[0], 11);\n  EXPECT_EQ(result[1], 21);\n  EXPECT_EQ(result[2], 31);\n  EXPECT_EQ(result[3], 41);\n\n  // Verify RX buffer is untouched (still has the request)\n  auto *rx_header = reinterpret_cast<cudaq::realtime::RPCHeader *>(rx_data_host);\n  EXPECT_EQ(rx_header->magic, CUDAQ_RPC_MAGIC_REQUEST);\n\n  // Teardown\n  shutdown_flag = 1;\n  __sync_synchronize();\n  cudaq_dispatcher_stop(dispatcher);\n  cudaq_dispatcher_destroy(dispatcher);\n  cudaq_dispatch_manager_destroy(manager);\n\n  hipGraphExecDestroy(graph_exec);\n  hipGraphDestroy(graph);\n  hipHostFree(h_mailbox_bank);\n  free_ring_buffer(rx_flags_host, rx_data_host);\n  free_ring_buffer(tx_flags_host, tx_data_host);\n}\n\n} // namespace\n", "source": "/kaggle/working/cuda-quantum/realtime/unittests/test_host_dispatcher.cu"}
{"input": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file init_rpc_increment_function_table.cu\n/// @brief Device-side increment RPC handler and function table initialisation.\n///\n/// This file is compiled by nvcc so that the __device__ function pointer\n/// can be taken.  The host-callable setup_rpc_increment_function_table()\n/// wrapper is extern \"C\" so that the bridge .cpp (compiled by g++) can\n/// call it without needing CUDA kernel launch syntax.\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n\n#include <cstdint>\n#include <cstring> // std::memset\n#include <cuda_runtime.h>\n#include <stdexcept>\n\nnamespace {\n\n//==============================================================================\n// Increment RPC Handler\n//==============================================================================\n\n/// @brief Simple RPC handler that increments each byte of the payload by 1.\n///\n/// Matches the DeviceRPCFunction signature.  Reads from input, writes to\n/// output (no in-place overlap).\n__device__ int rpc_increment_handler(const void *input, void *output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t *result_len) {\n\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  std::uint32_t len = (arg_len < max_result_len) ? arg_len : max_result_len;\n  for (std::uint32_t i = 0; i < len; ++i)\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  *result_len = len;\n  return 0;\n}\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n/// @brief Kernel to populate a cudaq_function_entry_t with the increment\n///        handler.\n__global__ void init_function_table_kernel(cudaq_function_entry_t *entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr =\n        reinterpret_cast<void *>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n\n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;\n    entries[0].schema.args[0].num_elements = 0;\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;\n    entries[0].schema.results[0].num_elements = 0;\n  }\n}\n\n//==============================================================================\n// Increment RPC Graph Handler\n//==============================================================================\n\n__global__ void graph_increment_kernel(void **mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    auto *io_ctx =\n        reinterpret_cast<cudaq::realtime::GraphIOContext *>(*mailbox_slot_ptr);\n    if (!io_ctx || !io_ctx->rx_slot)\n      return;\n\n    auto *header = static_cast<cudaq::realtime::RPCHeader *>(io_ctx->rx_slot);\n    std::uint8_t *measurements = reinterpret_cast<std::uint8_t *>(header + 1);\n\n    auto *response =\n        reinterpret_cast<cudaq::realtime::RPCResponse *>(io_ctx->tx_slot);\n    std::uint8_t *result =\n        io_ctx->tx_slot + sizeof(cudaq::realtime::RPCResponse);\n\n    for (std::uint32_t i = 0; i < header->arg_len; ++i)\n      result[i] = measurements[i] + 1;\n\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = header->arg_len;\n    response->request_id = header->request_id;\n    response->ptp_timestamp = header->ptp_timestamp;\n\n    __threadfence_system();\n    if (io_ctx->tx_flag)\n      *(io_ctx->tx_flag) = io_ctx->tx_flag_value;\n  }\n}\n\n/// Creates an executable graph that runs graph_increment_kernel with\n/// kernel arg = d_mailbox_bank (device pointer to first mailbox slot).\n/// Caller must cudaGraphExecDestroy / cudaGraphDestroy.\nbool create_increment_graph(void **d_mailbox_bank, cudaGraph_t *graph_out,\n                            cudaGraphExec_t *exec_out) {\n  cudaGraph_t graph = nullptr;\n  if (cudaGraphCreate(&graph, 0) != cudaSuccess)\n    return false;\n\n  // kernelParams[i] must be a *pointer to* the i-th argument value.\n  // The kernel takes void** so we pass &d_mailbox_bank (a void***).\n  cudaKernelNodeParams params = {};\n  void *kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void *>(graph_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  cudaGraphNode_t node = nullptr;\n  if (cudaGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  cudaGraphExec_t exec = nullptr;\n  if (cudaGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != cudaSuccess) {\n    cudaGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n} // anonymous namespace\n\n//==============================================================================\n// Host-Callable Wrapper\n//==============================================================================\n\nextern \"C\" void\nsetup_rpc_increment_function_table(cudaq_function_entry_t *d_entries) {\n  init_function_table_kernel<<<1, 1>>>(d_entries);\n  cudaDeviceSynchronize();\n}\n\nextern \"C\" void setup_rpc_graph_increment_function_table(\n    cudaq_function_entry_t *h_entries, void **d_mailbox_bank,\n    cudaGraph_t *graph_out, cudaGraphExec_t *exec_out) {\n  const bool graph_created =\n      create_increment_graph(d_mailbox_bank, graph_out, exec_out);\n  if (!graph_created) {\n    throw std::runtime_error(\n        \"Failed to create CUDA graph for the increment function.\");\n  }\n\n  // --- Function table (one GRAPH_LAUNCH entry) ---\n  std::memset(h_entries, 0, sizeof(cudaq_function_entry_t));\n  h_entries->function_id = RPC_INCREMENT_FUNCTION_ID;\n  h_entries->dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  h_entries->handler.graph_exec = *exec_out;\n}\n", "output": "/****************************************************************-*- C++ -*-****\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file init_rpc_increment_function_table.cu\n/// @brief Device-side increment RPC handler and function table initialisation.\n///\n/// This file is compiled by nvcc so that the __device__ function pointer\n/// can be taken.  The host-callable setup_rpc_increment_function_table()\n/// wrapper is extern \"C\" so that the bridge .cpp (compiled by g++) can\n/// call it without needing CUDA kernel launch syntax.\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n\n#include <cstdint>\n#include <cstring> // std::memset\n#include <hip/hip_runtime.h>\n#include <stdexcept>\n\nnamespace {\n\n//==============================================================================\n// Increment RPC Handler\n//==============================================================================\n\n/// @brief Simple RPC handler that increments each byte of the payload by 1.\n///\n/// Matches the DeviceRPCFunction signature.  Reads from input, writes to\n/// output (no in-place overlap).\n__device__ int rpc_increment_handler(const void *input, void *output,\n                                     std::uint32_t arg_len,\n                                     std::uint32_t max_result_len,\n                                     std::uint32_t *result_len) {\n\n  const std::uint8_t *in_data = static_cast<const std::uint8_t *>(input);\n  std::uint8_t *out_data = static_cast<std::uint8_t *>(output);\n  std::uint32_t len = (arg_len < max_result_len) ? arg_len : max_result_len;\n  for (std::uint32_t i = 0; i < len; ++i)\n    out_data[i] = static_cast<std::uint8_t>(in_data[i] + 1);\n  *result_len = len;\n  return 0;\n}\n\nconstexpr std::uint32_t RPC_INCREMENT_FUNCTION_ID =\n    cudaq::realtime::fnv1a_hash(\"rpc_increment\");\n\n/// @brief Kernel to populate a cudaq_function_entry_t with the increment\n///        handler.\n__global__ void init_function_table_kernel(cudaq_function_entry_t *entries) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    entries[0].handler.device_fn_ptr =\n        reinterpret_cast<void *>(&rpc_increment_handler);\n    entries[0].function_id = RPC_INCREMENT_FUNCTION_ID;\n    entries[0].dispatch_mode = CUDAQ_DISPATCH_DEVICE_CALL;\n    entries[0].reserved[0] = 0;\n    entries[0].reserved[1] = 0;\n    entries[0].reserved[2] = 0;\n\n    // Schema: 1 array argument (uint8), 1 array result (uint8)\n    entries[0].schema.num_args = 1;\n    entries[0].schema.num_results = 1;\n    entries[0].schema.reserved = 0;\n    entries[0].schema.args[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.args[0].reserved[0] = 0;\n    entries[0].schema.args[0].reserved[1] = 0;\n    entries[0].schema.args[0].reserved[2] = 0;\n    entries[0].schema.args[0].size_bytes = 0;\n    entries[0].schema.args[0].num_elements = 0;\n    entries[0].schema.results[0].type_id = CUDAQ_TYPE_ARRAY_UINT8;\n    entries[0].schema.results[0].reserved[0] = 0;\n    entries[0].schema.results[0].reserved[1] = 0;\n    entries[0].schema.results[0].reserved[2] = 0;\n    entries[0].schema.results[0].size_bytes = 0;\n    entries[0].schema.results[0].num_elements = 0;\n  }\n}\n\n//==============================================================================\n// Increment RPC Graph Handler\n//==============================================================================\n\n__global__ void graph_increment_kernel(void **mailbox_slot_ptr) {\n  if (threadIdx.x == 0 && blockIdx.x == 0) {\n    auto *io_ctx =\n        reinterpret_cast<cudaq::realtime::GraphIOContext *>(*mailbox_slot_ptr);\n    if (!io_ctx || !io_ctx->rx_slot)\n      return;\n\n    auto *header = static_cast<cudaq::realtime::RPCHeader *>(io_ctx->rx_slot);\n    std::uint8_t *measurements = reinterpret_cast<std::uint8_t *>(header + 1);\n\n    auto *response =\n        reinterpret_cast<cudaq::realtime::RPCResponse *>(io_ctx->tx_slot);\n    std::uint8_t *result =\n        io_ctx->tx_slot + sizeof(cudaq::realtime::RPCResponse);\n\n    for (std::uint32_t i = 0; i < header->arg_len; ++i)\n      result[i] = measurements[i] + 1;\n\n    response->magic = cudaq::realtime::RPC_MAGIC_RESPONSE;\n    response->status = 0;\n    response->result_len = header->arg_len;\n    response->request_id = header->request_id;\n    response->ptp_timestamp = header->ptp_timestamp;\n\n    __threadfence_system();\n    if (io_ctx->tx_flag)\n      *(io_ctx->tx_flag) = io_ctx->tx_flag_value;\n  }\n}\n\n/// Creates an executable graph that runs graph_increment_kernel with\n/// kernel arg = d_mailbox_bank (device pointer to first mailbox slot).\n/// Caller must cudaGraphExecDestroy / cudaGraphDestroy.\nbool create_increment_graph(void **d_mailbox_bank, hipGraph_t *graph_out,\n                            hipGraphExec_t *exec_out) {\n  hipGraph_t graph = nullptr;\n  if (hipGraphCreate(&graph, 0) != hipSuccess)\n    return false;\n\n  // kernelParams[i] must be a *pointer to* the i-th argument value.\n  // The kernel takes void** so we pass &d_mailbox_bank (a void***).\n  hipKernelNodeParams params = {};\n  void *kernel_args[] = {&d_mailbox_bank};\n  params.func = reinterpret_cast<void *>(graph_increment_kernel);\n  params.gridDim = dim3(1, 1, 1);\n  params.blockDim = dim3(32, 1, 1);\n  params.sharedMemBytes = 0;\n  params.kernelParams = kernel_args;\n  params.extra = nullptr;\n\n  hipGraphNode_t node = nullptr;\n  if (hipGraphAddKernelNode(&node, graph, nullptr, 0, &params) !=\n      hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  hipGraphExec_t exec = nullptr;\n  if (hipGraphInstantiate(&exec, graph, nullptr, nullptr, 0) != hipSuccess) {\n    hipGraphDestroy(graph);\n    return false;\n  }\n\n  *graph_out = graph;\n  *exec_out = exec;\n  return true;\n}\n} // anonymous namespace\n\n//==============================================================================\n// Host-Callable Wrapper\n//==============================================================================\n\nextern \"C\" void\nsetup_rpc_increment_function_table(cudaq_function_entry_t *d_entries) {\n  init_function_table_kernel<<<1, 1>>>(d_entries);\n  hipDeviceSynchronize();\n}\n\nextern \"C\" void setup_rpc_graph_increment_function_table(\n    cudaq_function_entry_t *h_entries, void **d_mailbox_bank,\n    hipGraph_t *graph_out, hipGraphExec_t *exec_out) {\n  const bool graph_created =\n      create_increment_graph(d_mailbox_bank, graph_out, exec_out);\n  if (!graph_created) {\n    throw std::runtime_error(\n        \"Failed to create CUDA graph for the increment function.\");\n  }\n\n  // --- Function table (one GRAPH_LAUNCH entry) ---\n  std::memset(h_entries, 0, sizeof(cudaq_function_entry_t));\n  h_entries->function_id = RPC_INCREMENT_FUNCTION_ID;\n  h_entries->dispatch_mode = CUDAQ_DISPATCH_GRAPH_LAUNCH;\n  h_entries->handler.graph_exec = *exec_out;\n}\n", "source": "/kaggle/working/cuda-quantum/realtime/unittests/utils/init_rpc_increment_function_table.cu"}
{"input": "/*******************************************************************************\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file unified_dispatch_kernel.cu\n/// @brief Hololink/DOCA unified dispatch: RDMA RX + RPC dispatch + RDMA TX\n/// in one GPU kernel, using Hololink's gpu_roce_transceiver.cuh device\n/// functions for WQE preparation and TX.\n///\n/// Two TX paths selected at runtime via the use_bf flag:\n///   dGPU: send_bf (BlueFlame, shared-memory WQE) -- lowest latency.\n///   iGPU: send (NIC_HANDLER_AUTO -> CPU proxy) -- required because the\n///         GPU cannot ring the NIC doorbell directly on integrated GPUs.\n///\n/// Compiled into libcudaq-realtime-bridge-hololink.so (transport-specific).\n/// The core libcudaq-realtime-dispatch.a no longer contains this file.\n\n#include \"cudaq/realtime/daemon/bridge/hololink/hololink_doca_transport_ctx.h\"\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n\n#include <cuda_runtime.h>\n#include <cstdint>\n\n#include \"gpu_roce_transceiver.cuh\"\n\nusing namespace cudaq::realtime;\n\n//==============================================================================\n// Device helpers\n//==============================================================================\n\n/// Spin-poll the CQE owner bit with periodic shutdown_flag checks.  Inlines\n/// the DOCA CQ state update (fence + consumer-index advance) to avoid the\n/// double CQE read that calling poll_cq_at would cause.  Returns UINT32_MAX\n/// on shutdown; otherwise returns the stride from the CQE immediate field.\n__device__ static inline std::uint32_t\nunified_poll_receive(struct doca_gpu_dev_verbs_cq *cq_rq, std::uint8_t *cqe,\n                     std::uint32_t cqe_mask,\n                     doca_gpu_dev_verbs_ticket_t ticket,\n                     volatile int *shutdown_flag) {\n  auto *cqe64 = reinterpret_cast<struct mlx5_cqe64 *>(\n      cqe + ((ticket & cqe_mask) * DOCA_GPUNETIO_VERBS_CQE_SIZE));\n  std::uint32_t cqe_num = cqe_mask + 1;\n  int spin = 0;\n  std::uint8_t opown;\n  do {\n    opown = doca_gpu_dev_verbs_load_relaxed_sys_global(\n        reinterpret_cast<uint8_t *>(&cqe64->op_own));\n    if (!((opown & MLX5_CQE_OWNER_MASK) ^ !!(ticket & cqe_num)))\n      break;\n    if (++spin >= 1024) {\n      spin = 0;\n      if (*shutdown_flag)\n        return UINT32_MAX;\n    }\n  } while (true);\n  doca_gpu_dev_verbs_fence_acquire<DOCA_GPUNETIO_VERBS_SYNC_SCOPE_SYS>();\n  doca_gpu_dev_verbs_atomic_max<std::uint64_t,\n                                DOCA_GPUNETIO_VERBS_RESOURCE_SHARING_MODE_GPU>(\n      &cq_rq->cqe_ci, ticket + 1);\n  return doca_gpu_dev_verbs_bswap32(cqe64->imm_inval_pkey) & 0xFFF;\n}\n\n__device__ static inline const cudaq_function_entry_t *\nunified_lookup_entry(std::uint32_t function_id,\n                     cudaq_function_entry_t *entries, std::size_t count) {\n  for (std::size_t i = 0; i < count; ++i) {\n    if (entries[i].function_id == function_id)\n      return &entries[i];\n  }\n  return nullptr;\n}\n\n//==============================================================================\n// Unified dispatch kernel -- single thread, single block.\n//\n// Two TX paths selected at runtime via the use_bf flag:\n//   use_bf=true  (dGPU): prepare_send_shared + send_bf  (BlueFlame, smem WQE)\n//   use_bf=false (iGPU): prepare_receive_send + send    (NIC_HANDLER_AUTO)\n//\n// Receive WQEs are pre-posted by the host before this kernel launches.\n//==============================================================================\n\n__global__ void hololink_unified_dispatch_kernel(\n    struct doca_gpu_dev_verbs_qp *qp, volatile int *shutdown_flag,\n    std::uint8_t *ring_buf, std::size_t ring_buf_stride_sz,\n    std::uint32_t ring_buf_mkey, std::uint32_t ring_buf_stride_num,\n    std::size_t frame_size, cudaq_function_entry_t *function_table,\n    std::size_t func_count, std::uint64_t *stats, int use_bf) {\n  if (qp == nullptr)\n    return;\n\n  auto *cq_rq = doca_gpu_dev_verbs_qp_get_cq_rq(qp);\n  auto *cqe = reinterpret_cast<std::uint8_t *>(\n      __ldg(reinterpret_cast<uintptr_t *>(&cq_rq->cqe_daddr)));\n  const std::uint32_t cqe_mask = __ldg(&cq_rq->cqe_num) - 1;\n\n  const bool use_inline = (frame_size <= MAX_SEND_INLINE_WQE);\n\n  // Receive WQEs are pre-posted by the host (GpuRoceTransceiverPrepareKernel\n  // in start() on dGPU, or hololink_prepare_receive_send() on iGPU).\n  __shared__ struct doca_gpu_dev_verbs_wqe wqe_sh;\n\n  if (use_bf) {\n    prepare_send_shared(qp, &wqe_sh, frame_size, ring_buf_mkey);\n  } else {\n    prepare_receive_send(qp, frame_size, ring_buf_mkey);\n  }\n\n  doca_gpu_dev_verbs_ticket_t cq_ticket = 0;\n  std::uint64_t sq_wqe_idx = 0;\n  std::uint64_t packet_count = 0;\n\n  while (true) {\n    std::uint32_t stride =\n        unified_poll_receive(cq_rq, cqe, cqe_mask, cq_ticket, shutdown_flag);\n    if (stride == UINT32_MAX)\n      break;\n    if (stride >= ring_buf_stride_num) {\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n      continue;\n    }\n\n    auto *slot =\n        ring_buf + static_cast<std::uint64_t>(stride) * ring_buf_stride_sz;\n    auto *header = reinterpret_cast<RPCHeader *>(slot);\n\n    if (header->magic == RPC_MAGIC_REQUEST) {\n      std::uint32_t function_id = header->function_id;\n      std::uint32_t arg_len = header->arg_len;\n      std::uint32_t request_id = header->request_id;\n      std::uint64_t ptp_timestamp = header->ptp_timestamp;\n\n      const cudaq_function_entry_t *entry =\n          unified_lookup_entry(function_id, function_table, func_count);\n\n      int status = -1;\n      std::uint32_t result_len = 0;\n\n      if (entry != nullptr &&\n          entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n        auto func = reinterpret_cast<DeviceRPCFunction>(\n            entry->handler.device_fn_ptr);\n        void *arg_buffer = static_cast<void *>(header + 1);\n        auto *output_buffer = slot + sizeof(RPCResponse);\n        auto max_result_len = static_cast<std::uint32_t>(\n            frame_size - sizeof(RPCResponse));\n\n        status =\n            func(arg_buffer, output_buffer, arg_len, max_result_len,\n                 &result_len);\n      }\n\n      auto *response = reinterpret_cast<RPCResponse *>(slot);\n      response->magic = RPC_MAGIC_RESPONSE;\n      response->status = status;\n      response->result_len = result_len;\n      response->request_id = request_id;\n      response->ptp_timestamp = ptp_timestamp;\n    }\n\n    auto buffer_addr =\n        static_cast<std::uint64_t>(ring_buf_stride_sz) * stride;\n    if (use_bf) {\n      // dGPU: send first, then repost (original order).  Reposting before\n      // send adds ~400ns by serializing a PCIe write ahead of BlueFlame.\n      if (!use_inline) {\n        send_bf<GPU_ROCE_MAX_FRAME_SIZE_0B>(qp, &wqe_sh, sq_wqe_idx,\n                                            buffer_addr);\n      } else {\n        send_bf<GPU_ROCE_MAX_FRAME_SIZE_44B>(\n            qp, &wqe_sh, sq_wqe_idx,\n            reinterpret_cast<std::uint64_t>(slot));\n      }\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n    } else {\n      // iGPU: repost first, then send.  The CPU proxy may batch doorbell\n      // writes, so ensure the NIC has a fresh receive WQE before any delay.\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n      if (!use_inline) {\n        send<GPU_ROCE_MAX_FRAME_SIZE_0B>(qp, sq_wqe_idx - 1, buffer_addr);\n      } else {\n        send<GPU_ROCE_MAX_FRAME_SIZE_44B>(\n            qp, sq_wqe_idx - 1, reinterpret_cast<std::uint64_t>(slot));\n      }\n    }\n\n    packet_count++;\n  }\n\n  atomicAdd(reinterpret_cast<unsigned long long *>(stats), packet_count);\n}\n\n//==============================================================================\n// Host launch wrapper -- matches cudaq_unified_launch_fn_t signature.\n//==============================================================================\n\nextern \"C\" void hololink_launch_unified_dispatch(\n    void *transport_ctx, cudaq_function_entry_t *function_table,\n    size_t func_count, volatile int *shutdown_flag, uint64_t *stats,\n    cudaStream_t stream) {\n  auto *ctx = static_cast<hololink_doca_transport_ctx *>(transport_ctx);\n\n  hololink_unified_dispatch_kernel<<<1, 1, 0, stream>>>(\n      static_cast<struct doca_gpu_dev_verbs_qp *>(ctx->gpu_dev_qp),\n      shutdown_flag, ctx->rx_ring_data, ctx->rx_ring_stride_sz,\n      ctx->rx_ring_mkey, ctx->rx_ring_stride_num, ctx->frame_size,\n      function_table, func_count, stats, ctx->use_bf);\n}\n", "output": "/*******************************************************************************\n * Copyright (c) 2026 NVIDIA Corporation & Affiliates.                         *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n/// @file unified_dispatch_kernel.cu\n/// @brief Hololink/DOCA unified dispatch: RDMA RX + RPC dispatch + RDMA TX\n/// in one GPU kernel, using Hololink's gpu_roce_transceiver.cuh device\n/// functions for WQE preparation and TX.\n///\n/// Two TX paths selected at runtime via the use_bf flag:\n///   dGPU: send_bf (BlueFlame, shared-memory WQE) -- lowest latency.\n///   iGPU: send (NIC_HANDLER_AUTO -> CPU proxy) -- required because the\n///         GPU cannot ring the NIC doorbell directly on integrated GPUs.\n///\n/// Compiled into libcudaq-realtime-bridge-hololink.so (transport-specific).\n/// The core libcudaq-realtime-dispatch.a no longer contains this file.\n\n#include \"cudaq/realtime/daemon/bridge/hololink/hololink_doca_transport_ctx.h\"\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n\n#include <hip/hip_runtime.h>\n#include <cstdint>\n\n#include \"gpu_roce_transceiver.cuh\"\n\nusing namespace cudaq::realtime;\n\n//==============================================================================\n// Device helpers\n//==============================================================================\n\n/// Spin-poll the CQE owner bit with periodic shutdown_flag checks.  Inlines\n/// the DOCA CQ state update (fence + consumer-index advance) to avoid the\n/// double CQE read that calling poll_cq_at would cause.  Returns UINT32_MAX\n/// on shutdown; otherwise returns the stride from the CQE immediate field.\n__device__ static inline std::uint32_t\nunified_poll_receive(struct doca_gpu_dev_verbs_cq *cq_rq, std::uint8_t *cqe,\n                     std::uint32_t cqe_mask,\n                     doca_gpu_dev_verbs_ticket_t ticket,\n                     volatile int *shutdown_flag) {\n  auto *cqe64 = reinterpret_cast<struct mlx5_cqe64 *>(\n      cqe + ((ticket & cqe_mask) * DOCA_GPUNETIO_VERBS_CQE_SIZE));\n  std::uint32_t cqe_num = cqe_mask + 1;\n  int spin = 0;\n  std::uint8_t opown;\n  do {\n    opown = doca_gpu_dev_verbs_load_relaxed_sys_global(\n        reinterpret_cast<uint8_t *>(&cqe64->op_own));\n    if (!((opown & MLX5_CQE_OWNER_MASK) ^ !!(ticket & cqe_num)))\n      break;\n    if (++spin >= 1024) {\n      spin = 0;\n      if (*shutdown_flag)\n        return UINT32_MAX;\n    }\n  } while (true);\n  doca_gpu_dev_verbs_fence_acquire<DOCA_GPUNETIO_VERBS_SYNC_SCOPE_SYS>();\n  doca_gpu_dev_verbs_atomic_max<std::uint64_t,\n                                DOCA_GPUNETIO_VERBS_RESOURCE_SHARING_MODE_GPU>(\n      &cq_rq->cqe_ci, ticket + 1);\n  return doca_gpu_dev_verbs_bswap32(cqe64->imm_inval_pkey) & 0xFFF;\n}\n\n__device__ static inline const cudaq_function_entry_t *\nunified_lookup_entry(std::uint32_t function_id,\n                     cudaq_function_entry_t *entries, std::size_t count) {\n  for (std::size_t i = 0; i < count; ++i) {\n    if (entries[i].function_id == function_id)\n      return &entries[i];\n  }\n  return nullptr;\n}\n\n//==============================================================================\n// Unified dispatch kernel -- single thread, single block.\n//\n// Two TX paths selected at runtime via the use_bf flag:\n//   use_bf=true  (dGPU): prepare_send_shared + send_bf  (BlueFlame, smem WQE)\n//   use_bf=false (iGPU): prepare_receive_send + send    (NIC_HANDLER_AUTO)\n//\n// Receive WQEs are pre-posted by the host before this kernel launches.\n//==============================================================================\n\n__global__ void hololink_unified_dispatch_kernel(\n    struct doca_gpu_dev_verbs_qp *qp, volatile int *shutdown_flag,\n    std::uint8_t *ring_buf, std::size_t ring_buf_stride_sz,\n    std::uint32_t ring_buf_mkey, std::uint32_t ring_buf_stride_num,\n    std::size_t frame_size, cudaq_function_entry_t *function_table,\n    std::size_t func_count, std::uint64_t *stats, int use_bf) {\n  if (qp == nullptr)\n    return;\n\n  auto *cq_rq = doca_gpu_dev_verbs_qp_get_cq_rq(qp);\n  auto *cqe = reinterpret_cast<std::uint8_t *>(\n      __ldg(reinterpret_cast<uintptr_t *>(&cq_rq->cqe_daddr)));\n  const std::uint32_t cqe_mask = __ldg(&cq_rq->cqe_num) - 1;\n\n  const bool use_inline = (frame_size <= MAX_SEND_INLINE_WQE);\n\n  // Receive WQEs are pre-posted by the host (GpuRoceTransceiverPrepareKernel\n  // in start() on dGPU, or hololink_prepare_receive_send() on iGPU).\n  __shared__ struct doca_gpu_dev_verbs_wqe wqe_sh;\n\n  if (use_bf) {\n    prepare_send_shared(qp, &wqe_sh, frame_size, ring_buf_mkey);\n  } else {\n    prepare_receive_send(qp, frame_size, ring_buf_mkey);\n  }\n\n  doca_gpu_dev_verbs_ticket_t cq_ticket = 0;\n  std::uint64_t sq_wqe_idx = 0;\n  std::uint64_t packet_count = 0;\n\n  while (true) {\n    std::uint32_t stride =\n        unified_poll_receive(cq_rq, cqe, cqe_mask, cq_ticket, shutdown_flag);\n    if (stride == UINT32_MAX)\n      break;\n    if (stride >= ring_buf_stride_num) {\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n      continue;\n    }\n\n    auto *slot =\n        ring_buf + static_cast<std::uint64_t>(stride) * ring_buf_stride_sz;\n    auto *header = reinterpret_cast<RPCHeader *>(slot);\n\n    if (header->magic == RPC_MAGIC_REQUEST) {\n      std::uint32_t function_id = header->function_id;\n      std::uint32_t arg_len = header->arg_len;\n      std::uint32_t request_id = header->request_id;\n      std::uint64_t ptp_timestamp = header->ptp_timestamp;\n\n      const cudaq_function_entry_t *entry =\n          unified_lookup_entry(function_id, function_table, func_count);\n\n      int status = -1;\n      std::uint32_t result_len = 0;\n\n      if (entry != nullptr &&\n          entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n        auto func = reinterpret_cast<DeviceRPCFunction>(\n            entry->handler.device_fn_ptr);\n        void *arg_buffer = static_cast<void *>(header + 1);\n        auto *output_buffer = slot + sizeof(RPCResponse);\n        auto max_result_len = static_cast<std::uint32_t>(\n            frame_size - sizeof(RPCResponse));\n\n        status =\n            func(arg_buffer, output_buffer, arg_len, max_result_len,\n                 &result_len);\n      }\n\n      auto *response = reinterpret_cast<RPCResponse *>(slot);\n      response->magic = RPC_MAGIC_RESPONSE;\n      response->status = status;\n      response->result_len = result_len;\n      response->request_id = request_id;\n      response->ptp_timestamp = ptp_timestamp;\n    }\n\n    auto buffer_addr =\n        static_cast<std::uint64_t>(ring_buf_stride_sz) * stride;\n    if (use_bf) {\n      // dGPU: send first, then repost (original order).  Reposting before\n      // send adds ~400ns by serializing a PCIe write ahead of BlueFlame.\n      if (!use_inline) {\n        send_bf<GPU_ROCE_MAX_FRAME_SIZE_0B>(qp, &wqe_sh, sq_wqe_idx,\n                                            buffer_addr);\n      } else {\n        send_bf<GPU_ROCE_MAX_FRAME_SIZE_44B>(\n            qp, &wqe_sh, sq_wqe_idx,\n            reinterpret_cast<std::uint64_t>(slot));\n      }\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n    } else {\n      // iGPU: repost first, then send.  The CPU proxy may batch doorbell\n      // writes, so ensure the NIC has a fresh receive WQE before any delay.\n      sq_wqe_idx++;\n      repost_receive(qp, sq_wqe_idx);\n      cq_ticket = sq_wqe_idx;\n      if (!use_inline) {\n        send<GPU_ROCE_MAX_FRAME_SIZE_0B>(qp, sq_wqe_idx - 1, buffer_addr);\n      } else {\n        send<GPU_ROCE_MAX_FRAME_SIZE_44B>(\n            qp, sq_wqe_idx - 1, reinterpret_cast<std::uint64_t>(slot));\n      }\n    }\n\n    packet_count++;\n  }\n\n  atomicAdd(reinterpret_cast<unsigned long long *>(stats), packet_count);\n}\n\n//==============================================================================\n// Host launch wrapper -- matches cudaq_unified_launch_fn_t signature.\n//==============================================================================\n\nextern \"C\" void hololink_launch_unified_dispatch(\n    void *transport_ctx, cudaq_function_entry_t *function_table,\n    size_t func_count, volatile int *shutdown_flag, uint64_t *stats,\n    hipStream_t stream) {\n  auto *ctx = static_cast<hololink_doca_transport_ctx *>(transport_ctx);\n\n  hololink_unified_dispatch_kernel<<<1, 1, 0, stream>>>(\n      static_cast<struct doca_gpu_dev_verbs_qp *>(ctx->gpu_dev_qp),\n      shutdown_flag, ctx->rx_ring_data, ctx->rx_ring_stride_sz,\n      ctx->rx_ring_mkey, ctx->rx_ring_stride_num, ctx->frame_size,\n      function_table, func_count, stats, ctx->use_bf);\n}\n", "source": "/kaggle/working/cuda-quantum/realtime/lib/daemon/bridge/hololink/unified_dispatch_kernel.cu"}
{"input": "/*******************************************************************************\n * Copyright (c) 2025 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n\n#include <cuda_runtime.h>\n#include <cuda_device_runtime_api.h>\n#include <cstdint>\n\n\nnamespace cudaq::realtime {\n\n//==============================================================================\n// Dispatch Kernel Implementation (compiled into libcudaq-realtime.so)\n//==============================================================================\n\n/// @brief Lookup function entry in table by function_id.\n__device__ inline const cudaq_function_entry_t* dispatch_lookup_entry(\n    std::uint32_t function_id,\n    cudaq_function_entry_t* entries,\n    std::size_t entry_count) {\n  for (std::size_t i = 0; i < entry_count; ++i) {\n    if (entries[i].function_id == function_id) {\n      return &entries[i];\n    }\n  }\n  return nullptr;\n}\n\n/// @brief Dispatch kernel for DEVICE_CALL mode only (no graph launch support).\n/// This kernel does not contain any device-side graph launch code, avoiding\n/// compatibility issues on systems where cudaGraphLaunch is not supported.\n///\n/// Supports symmetric RX/TX data buffers for Hololink compatibility:\n/// - RX data address comes from rx_flags[slot] (set by Hololink RX kernel)\n/// - TX response is written to tx_data + slot * tx_stride_sz\n/// - tx_flags[slot] is set to the TX slot address\n///\n/// When KernelType::is_cooperative is true, the kernel is launched via\n/// cudaLaunchCooperativeKernel and ALL threads participate in calling the\n/// RPC handler (needed for multi-block cooperative decode kernels like BP).\n/// Thread 0 polls/parses the header, broadcasts work via shared memory,\n/// then all threads call the handler after a grid.sync().\ntemplate <typename KernelType>\n__global__ void dispatch_kernel_device_call_only(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* tx_data,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  std::uint64_t local_packet_count = 0;\n  std::size_t current_slot = 0;\n\n  if constexpr (KernelType::is_cooperative) {\n    //==========================================================================\n    // Cooperative path: ALL threads call the handler.\n    //\n    // Work descriptor in shared memory (block 0 broadcasts via grid.sync).\n    // Only block 0 needs shared memory for the descriptor; other blocks\n    // read the device-memory copies after the grid barrier.\n    //==========================================================================\n    __shared__ DeviceRPCFunction s_func;\n    __shared__ void*             s_arg_buffer;\n    __shared__ std::uint8_t*     s_output_buffer;\n    __shared__ std::uint32_t     s_arg_len;\n    __shared__ std::uint32_t     s_max_result_len;\n    __shared__ std::uint32_t     s_request_id;\n    __shared__ std::uint64_t     s_ptp_timestamp;\n    __shared__ bool              s_have_work;\n\n    // Device-memory work descriptor visible to all blocks after grid.sync.\n    // We use a single set since the cooperative kernel processes one RPC at\n    // a time (all threads participate, so no pipelining).\n    __device__ static DeviceRPCFunction d_func;\n    __device__ static void*             d_arg_buffer;\n    __device__ static std::uint8_t*     d_output_buffer;\n    __device__ static std::uint32_t     d_arg_len;\n    __device__ static std::uint32_t     d_max_result_len;\n    __device__ static std::uint32_t     d_request_id;\n    __device__ static std::uint64_t     d_ptp_timestamp;\n    __device__ static bool              d_have_work;\n\n    while (!(*shutdown_flag)) {\n      // --- Phase 1: Thread 0 polls and parses ---\n      if (tid == 0) {\n        s_have_work = false;\n        std::uint64_t rx_value = rx_flags[current_slot];\n        if (rx_value != 0) {\n          void* rx_slot = reinterpret_cast<void*>(rx_value);\n          RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n          if (header->magic == RPC_MAGIC_REQUEST) {\n            const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n                header->function_id, function_table, func_count);\n            if (entry != nullptr &&\n                entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n              std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n              s_func          = reinterpret_cast<DeviceRPCFunction>(\n                  entry->handler.device_fn_ptr);\n              s_arg_buffer    = static_cast<void*>(header + 1);\n              s_output_buffer = tx_slot + sizeof(RPCResponse);\n              s_arg_len       = header->arg_len;\n              s_max_result_len = tx_stride_sz - sizeof(RPCResponse);\n              s_request_id    = header->request_id;\n              s_ptp_timestamp = header->ptp_timestamp;\n              s_have_work     = true;\n\n              // Publish to device memory for other blocks\n              d_func           = s_func;\n              d_arg_buffer     = s_arg_buffer;\n              d_output_buffer  = s_output_buffer;\n              d_arg_len        = s_arg_len;\n              d_max_result_len = s_max_result_len;\n              d_request_id     = s_request_id;\n              d_ptp_timestamp  = s_ptp_timestamp;\n              d_have_work      = true;\n            }\n          }\n          if (!s_have_work) {\n            rx_flags[current_slot] = 0;\n          }\n        }\n      }\n\n      // --- Phase 2: Broadcast to all threads ---\n      KernelType::sync();\n\n      // Non-block-0 threads read from device memory\n      bool have_work;\n      DeviceRPCFunction func;\n      void* arg_buffer;\n      std::uint8_t* output_buffer;\n      std::uint32_t arg_len;\n      std::uint32_t max_result_len;\n      std::uint32_t request_id;\n      std::uint64_t ptp_timestamp;\n      if (blockIdx.x == 0) {\n        have_work      = s_have_work;\n        func           = s_func;\n        arg_buffer     = s_arg_buffer;\n        output_buffer  = s_output_buffer;\n        arg_len        = s_arg_len;\n        max_result_len = s_max_result_len;\n        request_id     = s_request_id;\n        ptp_timestamp  = s_ptp_timestamp;\n      } else {\n        have_work      = d_have_work;\n        func           = d_func;\n        arg_buffer     = d_arg_buffer;\n        output_buffer  = d_output_buffer;\n        arg_len        = d_arg_len;\n        max_result_len = d_max_result_len;\n        request_id     = d_request_id;\n        ptp_timestamp  = d_ptp_timestamp;\n      }\n\n      // --- Phase 3: ALL threads call the handler ---\n      std::uint32_t result_len = 0;\n      int status = 0;\n      if (have_work) {\n        status = func(arg_buffer, output_buffer, arg_len,\n                       max_result_len, &result_len);\n      }\n\n      // --- Phase 4: Sync, then thread 0 writes response ---\n      KernelType::sync();\n\n      if (tid == 0 && have_work) {\n        std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n        RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n        response->magic = RPC_MAGIC_RESPONSE;\n        response->status = status;\n        response->result_len = result_len;\n        response->request_id = request_id;\n        response->ptp_timestamp = ptp_timestamp;\n\n        while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n          ;\n\n        __threadfence();\n        tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n\n        rx_flags[current_slot] = 0;\n        local_packet_count++;\n        current_slot = (current_slot + 1) % num_slots;\n      }\n\n      // Reset device-memory work flag for next iteration\n      if (tid == 0) {\n        d_have_work = false;\n      }\n\n      KernelType::sync();\n    }\n  } else {\n    //==========================================================================\n    // Regular path: only thread 0 calls the handler (unchanged).\n    //==========================================================================\n    while (!(*shutdown_flag)) {\n      if (tid == 0) {\n        std::uint64_t rx_value = rx_flags[current_slot];\n        if (rx_value != 0) {\n          // RX data address comes from rx_flags (set by Hololink RX kernel\n          // or host test harness to the address of the RX data slot)\n          void* rx_slot = reinterpret_cast<void*>(rx_value);\n          RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n          if (header->magic != RPC_MAGIC_REQUEST) {\n            rx_flags[current_slot] = 0;\n            continue;\n          }\n\n          std::uint32_t function_id = header->function_id;\n          std::uint32_t arg_len = header->arg_len;\n          void* arg_buffer = static_cast<void*>(header + 1);\n\n          const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n              function_id, function_table, func_count);\n\n          if (entry != nullptr && entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n            DeviceRPCFunction func =\n                reinterpret_cast<DeviceRPCFunction>(entry->handler.device_fn_ptr);\n\n            // Compute TX slot address from symmetric TX data buffer\n            std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n            // Handler writes results directly to TX slot (after response header)\n            std::uint8_t* output_buffer = tx_slot + sizeof(RPCResponse);\n            std::uint32_t result_len = 0;\n            std::uint32_t max_result_len = tx_stride_sz - sizeof(RPCResponse);\n            int status = func(arg_buffer, output_buffer, arg_len,\n                              max_result_len, &result_len);\n\n            // Write RPC response header to TX slot\n            RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n            response->magic = RPC_MAGIC_RESPONSE;\n            response->status = status;\n            response->result_len = result_len;\n            response->request_id = header->request_id;\n            response->ptp_timestamp = header->ptp_timestamp;\n\n            while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n              ;\n\n            __threadfence();\n            tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n          }\n\n          rx_flags[current_slot] = 0;\n          local_packet_count++;\n          current_slot = (current_slot + 1) % num_slots;\n        }\n      }\n\n      KernelType::sync();\n    }\n  }\n\n  if (tid == 0) {\n    atomicAdd(reinterpret_cast<unsigned long long*>(stats), local_packet_count);\n  }\n}\n\n/// @brief Dispatch kernel supporting both DEVICE_CALL and GRAPH_LAUNCH modes.\n/// This kernel includes device-side graph launch code and requires compute capability >= 9.0.\n/// NOTE: Graph launch code is conditionally compiled based on __CUDA_ARCH__.\n///\n/// Supports symmetric RX/TX data buffers for Hololink compatibility.\ntemplate <typename KernelType>\n__global__ void dispatch_kernel_with_graph(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* tx_data,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    GraphIOContext* graph_io_ctx,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  std::uint64_t local_packet_count = 0;\n  std::size_t current_slot = 0;\n\n  while (!(*shutdown_flag)) {\n    if (tid == 0) {\n      std::uint64_t rx_value = rx_flags[current_slot];\n      if (rx_value != 0) {\n        void* rx_slot = reinterpret_cast<void*>(rx_value);\n        RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n        if (header->magic != RPC_MAGIC_REQUEST) {\n          rx_flags[current_slot] = 0;\n          continue;\n        }\n\n        std::uint32_t function_id = header->function_id;\n        std::uint32_t arg_len = header->arg_len;\n        void* arg_buffer = static_cast<void*>(header + 1);\n\n        const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n            function_id, function_table, func_count);\n        \n        // Compute TX slot address from symmetric TX data buffer\n        std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n        if (entry != nullptr) {\n          if (entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n            DeviceRPCFunction func = \n                reinterpret_cast<DeviceRPCFunction>(entry->handler.device_fn_ptr);\n\n            // Handler writes results directly to TX slot (after response header)\n            std::uint8_t* output_buffer = tx_slot + sizeof(RPCResponse);\n            std::uint32_t result_len = 0;\n            std::uint32_t max_result_len = tx_stride_sz - sizeof(RPCResponse);\n            int status = func(arg_buffer, output_buffer, arg_len,\n                              max_result_len, &result_len);\n\n            // Write RPC response to TX slot\n            RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n            response->magic = RPC_MAGIC_RESPONSE;\n            response->status = status;\n            response->result_len = result_len;\n            response->request_id = header->request_id;\n            response->ptp_timestamp = header->ptp_timestamp;\n\n            while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n              ;\n\n            __threadfence();\n            tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n          }\n#if __CUDA_ARCH__ >= 900\n          else if (entry->dispatch_mode == CUDAQ_DISPATCH_GRAPH_LAUNCH) {\n            if (graph_io_ctx != nullptr) {\n              graph_io_ctx->rx_slot = rx_slot;\n              graph_io_ctx->tx_slot = tx_slot;\n              graph_io_ctx->tx_flag = &tx_flags[current_slot];\n              graph_io_ctx->tx_flag_value =\n                  reinterpret_cast<std::uint64_t>(tx_slot);\n              graph_io_ctx->tx_stride_sz = tx_stride_sz;\n              __threadfence();\n            }\n\n            cudaGraphLaunch(entry->handler.graph_exec,\n                            cudaStreamGraphFireAndForget);\n          }\n#endif // __CUDA_ARCH__ >= 900\n        }\n\n        rx_flags[current_slot] = 0;\n        local_packet_count++;\n        current_slot = (current_slot + 1) % num_slots;\n      }\n    }\n\n    KernelType::sync();\n  }\n\n  if (tid == 0) {\n    atomicAdd(reinterpret_cast<unsigned long long*>(stats), local_packet_count);\n  }\n}\n\n} // namespace cudaq::realtime\n\n//==============================================================================\n// Host Launch Functions\n//==============================================================================\n\n// Force eager CUDA module loading for the dispatch kernel.\n// Call before launching persistent kernels to avoid lazy-loading deadlocks.\nextern \"C\" cudaError_t cudaq_dispatch_kernel_query_occupancy(\n    int* out_blocks, uint32_t threads_per_block) {\n  int num_blocks = 0;\n  cudaError_t err = cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n      &num_blocks,\n      cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::RegularKernel>,\n      threads_per_block, 0);\n  if (err != cudaSuccess) return err;\n  if (out_blocks) *out_blocks = num_blocks;\n  return cudaSuccess;\n}\n\nextern \"C\" cudaError_t cudaq_dispatch_kernel_cooperative_query_occupancy(\n    int* out_blocks, uint32_t threads_per_block) {\n  int num_blocks = 0;\n  cudaError_t err = cudaOccupancyMaxActiveBlocksPerMultiprocessor(\n      &num_blocks,\n      cudaq::realtime::dispatch_kernel_device_call_only<\n          cudaq::realtime::CooperativeKernel>,\n      threads_per_block, 0);\n  if (err != cudaSuccess) return err;\n  if (out_blocks) *out_blocks = num_blocks;\n  return cudaSuccess;\n}\n\nextern \"C\" void cudaq_launch_dispatch_kernel_regular(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    cudaStream_t stream) {\n  // Use device-call-only kernel (no graph launch support)\n  // Note: rx_data/rx_stride_sz are available in the ringbuffer struct but\n  // not passed to the kernel since it reads RX addresses from rx_flags.\n  (void)rx_data;\n  (void)rx_stride_sz;\n  cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::RegularKernel>\n      <<<num_blocks, threads_per_block, 0, stream>>>(\n          rx_flags, tx_flags, tx_data, tx_stride_sz,\n          function_table, func_count,\n          shutdown_flag, stats, num_slots);\n}\n\nextern \"C\" void cudaq_launch_dispatch_kernel_cooperative(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    cudaStream_t stream) {\n  (void)rx_data;\n  (void)rx_stride_sz;\n  void* kernel_args[] = {\n      const_cast<std::uint64_t**>(&rx_flags),\n      const_cast<std::uint64_t**>(&tx_flags),\n      &tx_data,\n      &tx_stride_sz,\n      &function_table,\n      &func_count,\n      const_cast<int**>(&shutdown_flag),\n      &stats,\n      &num_slots\n  };\n\n  cudaLaunchCooperativeKernel(\n      reinterpret_cast<void*>(\n          cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::CooperativeKernel>),\n      dim3(num_blocks), dim3(threads_per_block), kernel_args, 0, stream);\n}\n\n//==============================================================================\n// Graph-Based Dispatch (Proper Device-Side Graph Launch Support)\n//==============================================================================\n//\n// To use device-side cudaGraphLaunch(), the dispatch kernel itself must be\n// running inside a graph execution context. These functions create a graph\n// containing the dispatch kernel, instantiate it with cudaGraphInstantiateFlagDeviceLaunch,\n// and provide proper launch/cleanup functions.\n\n// Internal storage for graph-based dispatch context\n// Parameters must be stored persistently since the graph may execute after\n// the create function returns.\nstruct cudaq_dispatch_graph_context {\n  cudaGraph_t graph;\n  cudaGraphExec_t graph_exec;\n  cudaGraphNode_t kernel_node;\n  bool is_valid;\n  \n  // Persistent storage for kernel parameters (must outlive graph execution)\n  volatile std::uint64_t* rx_flags;\n  volatile std::uint64_t* tx_flags;\n  std::uint8_t* tx_data;\n  std::size_t tx_stride_sz;\n  cudaq_function_entry_t* function_table;\n  std::size_t func_count;\n  cudaq::realtime::GraphIOContext* graph_io_ctx;\n  volatile int* shutdown_flag;\n  std::uint64_t* stats;\n  std::size_t num_slots;\n};\n\nextern \"C\" cudaError_t cudaq_create_dispatch_graph_regular(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    void* graph_io_ctx_raw,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    cudaStream_t stream,\n    cudaq_dispatch_graph_context** out_context) {\n  \n  (void)rx_data;\n  (void)rx_stride_sz;\n  cudaError_t err;\n  \n  // Allocate context with persistent parameter storage\n  cudaq_dispatch_graph_context* ctx = new cudaq_dispatch_graph_context();\n  ctx->is_valid = false;\n  \n  // Store parameters persistently in the context\n  ctx->rx_flags = rx_flags;\n  ctx->tx_flags = tx_flags;\n  ctx->tx_data = tx_data;\n  ctx->tx_stride_sz = tx_stride_sz;\n  ctx->function_table = function_table;\n  ctx->func_count = func_count;\n  ctx->graph_io_ctx =\n      static_cast<cudaq::realtime::GraphIOContext*>(graph_io_ctx_raw);\n  ctx->shutdown_flag = shutdown_flag;\n  ctx->stats = stats;\n  ctx->num_slots = num_slots;\n  \n  // Create graph\n  err = cudaGraphCreate(&ctx->graph, 0);\n  if (err != cudaSuccess) {\n    delete ctx;\n    return err;\n  }\n  \n  // Set up kernel parameters - point to persistent storage in context\n  cudaKernelNodeParams kernel_params = {};\n  void* kernel_args[] = {\n      &ctx->rx_flags,\n      &ctx->tx_flags,\n      &ctx->tx_data,\n      &ctx->tx_stride_sz,\n      &ctx->function_table,\n      &ctx->func_count,\n      &ctx->graph_io_ctx,\n      &ctx->shutdown_flag,\n      &ctx->stats,\n      &ctx->num_slots\n  };\n  \n  kernel_params.func = reinterpret_cast<void*>(\n      cudaq::realtime::dispatch_kernel_with_graph<cudaq::realtime::RegularKernel>);\n  kernel_params.gridDim = dim3(num_blocks, 1, 1);\n  kernel_params.blockDim = dim3(threads_per_block, 1, 1);\n  kernel_params.sharedMemBytes = 0;\n  kernel_params.kernelParams = kernel_args;\n  kernel_params.extra = nullptr;\n  \n  // Add kernel node to graph\n  err = cudaGraphAddKernelNode(&ctx->kernel_node, ctx->graph, nullptr, 0, &kernel_params);\n  if (err != cudaSuccess) {\n    cudaGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Instantiate with device launch flag - THIS IS THE KEY!\n  err = cudaGraphInstantiate(&ctx->graph_exec, ctx->graph, \n                              cudaGraphInstantiateFlagDeviceLaunch);\n  if (err != cudaSuccess) {\n    cudaGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Upload graph to device (required before device-side launch)\n  err = cudaGraphUpload(ctx->graph_exec, stream);\n  if (err != cudaSuccess) {\n    cudaGraphExecDestroy(ctx->graph_exec);\n    cudaGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Synchronize to ensure upload completes\n  err = cudaStreamSynchronize(stream);\n  if (err != cudaSuccess) {\n    cudaGraphExecDestroy(ctx->graph_exec);\n    cudaGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  ctx->is_valid = true;\n  *out_context = ctx;\n  return cudaSuccess;\n}\n\nextern \"C\" cudaError_t cudaq_launch_dispatch_graph(\n    cudaq_dispatch_graph_context* context,\n    cudaStream_t stream) {\n  if (context == nullptr || !context->is_valid) {\n    return cudaErrorInvalidValue;\n  }\n  \n  // Launch the graph - now device-side cudaGraphLaunch will work!\n  return cudaGraphLaunch(context->graph_exec, stream);\n}\n\nextern \"C\" cudaError_t cudaq_destroy_dispatch_graph(\n    cudaq_dispatch_graph_context* context) {\n  if (context == nullptr) {\n    return cudaErrorInvalidValue;\n  }\n  \n  cudaError_t err = cudaSuccess;\n  \n  if (context->is_valid) {\n    cudaError_t err1 = cudaGraphExecDestroy(context->graph_exec);\n    cudaError_t err2 = cudaGraphDestroy(context->graph);\n    if (err1 != cudaSuccess) err = err1;\n    else if (err2 != cudaSuccess) err = err2;\n  }\n  \n  delete context;\n  return err;\n}\n", "output": "/*******************************************************************************\n * Copyright (c) 2025 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n#include \"cudaq/realtime/daemon/dispatcher/cudaq_realtime.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel_launch.h\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_kernel.cuh\"\n#include \"cudaq/realtime/daemon/dispatcher/dispatch_modes.h\"\n#include \"cudaq/realtime/daemon/dispatcher/kernel_types.h\"\n\n#include <hip/hip_runtime.h>\n#include <cuda_device_runtime_api.h>\n#include <cstdint>\n\n\nnamespace cudaq::realtime {\n\n//==============================================================================\n// Dispatch Kernel Implementation (compiled into libcudaq-realtime.so)\n//==============================================================================\n\n/// @brief Lookup function entry in table by function_id.\n__device__ inline const cudaq_function_entry_t* dispatch_lookup_entry(\n    std::uint32_t function_id,\n    cudaq_function_entry_t* entries,\n    std::size_t entry_count) {\n  for (std::size_t i = 0; i < entry_count; ++i) {\n    if (entries[i].function_id == function_id) {\n      return &entries[i];\n    }\n  }\n  return nullptr;\n}\n\n/// @brief Dispatch kernel for DEVICE_CALL mode only (no graph launch support).\n/// This kernel does not contain any device-side graph launch code, avoiding\n/// compatibility issues on systems where cudaGraphLaunch is not supported.\n///\n/// Supports symmetric RX/TX data buffers for Hololink compatibility:\n/// - RX data address comes from rx_flags[slot] (set by Hololink RX kernel)\n/// - TX response is written to tx_data + slot * tx_stride_sz\n/// - tx_flags[slot] is set to the TX slot address\n///\n/// When KernelType::is_cooperative is true, the kernel is launched via\n/// cudaLaunchCooperativeKernel and ALL threads participate in calling the\n/// RPC handler (needed for multi-block cooperative decode kernels like BP).\n/// Thread 0 polls/parses the header, broadcasts work via shared memory,\n/// then all threads call the handler after a grid.sync().\ntemplate <typename KernelType>\n__global__ void dispatch_kernel_device_call_only(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* tx_data,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  std::uint64_t local_packet_count = 0;\n  std::size_t current_slot = 0;\n\n  if constexpr (KernelType::is_cooperative) {\n    //==========================================================================\n    // Cooperative path: ALL threads call the handler.\n    //\n    // Work descriptor in shared memory (block 0 broadcasts via grid.sync).\n    // Only block 0 needs shared memory for the descriptor; other blocks\n    // read the device-memory copies after the grid barrier.\n    //==========================================================================\n    __shared__ DeviceRPCFunction s_func;\n    __shared__ void*             s_arg_buffer;\n    __shared__ std::uint8_t*     s_output_buffer;\n    __shared__ std::uint32_t     s_arg_len;\n    __shared__ std::uint32_t     s_max_result_len;\n    __shared__ std::uint32_t     s_request_id;\n    __shared__ std::uint64_t     s_ptp_timestamp;\n    __shared__ bool              s_have_work;\n\n    // Device-memory work descriptor visible to all blocks after grid.sync.\n    // We use a single set since the cooperative kernel processes one RPC at\n    // a time (all threads participate, so no pipelining).\n    __device__ static DeviceRPCFunction d_func;\n    __device__ static void*             d_arg_buffer;\n    __device__ static std::uint8_t*     d_output_buffer;\n    __device__ static std::uint32_t     d_arg_len;\n    __device__ static std::uint32_t     d_max_result_len;\n    __device__ static std::uint32_t     d_request_id;\n    __device__ static std::uint64_t     d_ptp_timestamp;\n    __device__ static bool              d_have_work;\n\n    while (!(*shutdown_flag)) {\n      // --- Phase 1: Thread 0 polls and parses ---\n      if (tid == 0) {\n        s_have_work = false;\n        std::uint64_t rx_value = rx_flags[current_slot];\n        if (rx_value != 0) {\n          void* rx_slot = reinterpret_cast<void*>(rx_value);\n          RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n          if (header->magic == RPC_MAGIC_REQUEST) {\n            const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n                header->function_id, function_table, func_count);\n            if (entry != nullptr &&\n                entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n              std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n              s_func          = reinterpret_cast<DeviceRPCFunction>(\n                  entry->handler.device_fn_ptr);\n              s_arg_buffer    = static_cast<void*>(header + 1);\n              s_output_buffer = tx_slot + sizeof(RPCResponse);\n              s_arg_len       = header->arg_len;\n              s_max_result_len = tx_stride_sz - sizeof(RPCResponse);\n              s_request_id    = header->request_id;\n              s_ptp_timestamp = header->ptp_timestamp;\n              s_have_work     = true;\n\n              // Publish to device memory for other blocks\n              d_func           = s_func;\n              d_arg_buffer     = s_arg_buffer;\n              d_output_buffer  = s_output_buffer;\n              d_arg_len        = s_arg_len;\n              d_max_result_len = s_max_result_len;\n              d_request_id     = s_request_id;\n              d_ptp_timestamp  = s_ptp_timestamp;\n              d_have_work      = true;\n            }\n          }\n          if (!s_have_work) {\n            rx_flags[current_slot] = 0;\n          }\n        }\n      }\n\n      // --- Phase 2: Broadcast to all threads ---\n      KernelType::sync();\n\n      // Non-block-0 threads read from device memory\n      bool have_work;\n      DeviceRPCFunction func;\n      void* arg_buffer;\n      std::uint8_t* output_buffer;\n      std::uint32_t arg_len;\n      std::uint32_t max_result_len;\n      std::uint32_t request_id;\n      std::uint64_t ptp_timestamp;\n      if (blockIdx.x == 0) {\n        have_work      = s_have_work;\n        func           = s_func;\n        arg_buffer     = s_arg_buffer;\n        output_buffer  = s_output_buffer;\n        arg_len        = s_arg_len;\n        max_result_len = s_max_result_len;\n        request_id     = s_request_id;\n        ptp_timestamp  = s_ptp_timestamp;\n      } else {\n        have_work      = d_have_work;\n        func           = d_func;\n        arg_buffer     = d_arg_buffer;\n        output_buffer  = d_output_buffer;\n        arg_len        = d_arg_len;\n        max_result_len = d_max_result_len;\n        request_id     = d_request_id;\n        ptp_timestamp  = d_ptp_timestamp;\n      }\n\n      // --- Phase 3: ALL threads call the handler ---\n      std::uint32_t result_len = 0;\n      int status = 0;\n      if (have_work) {\n        status = func(arg_buffer, output_buffer, arg_len,\n                       max_result_len, &result_len);\n      }\n\n      // --- Phase 4: Sync, then thread 0 writes response ---\n      KernelType::sync();\n\n      if (tid == 0 && have_work) {\n        std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n        RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n        response->magic = RPC_MAGIC_RESPONSE;\n        response->status = status;\n        response->result_len = result_len;\n        response->request_id = request_id;\n        response->ptp_timestamp = ptp_timestamp;\n\n        while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n          ;\n\n        __threadfence();\n        tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n\n        rx_flags[current_slot] = 0;\n        local_packet_count++;\n        current_slot = (current_slot + 1) % num_slots;\n      }\n\n      // Reset device-memory work flag for next iteration\n      if (tid == 0) {\n        d_have_work = false;\n      }\n\n      KernelType::sync();\n    }\n  } else {\n    //==========================================================================\n    // Regular path: only thread 0 calls the handler (unchanged).\n    //==========================================================================\n    while (!(*shutdown_flag)) {\n      if (tid == 0) {\n        std::uint64_t rx_value = rx_flags[current_slot];\n        if (rx_value != 0) {\n          // RX data address comes from rx_flags (set by Hololink RX kernel\n          // or host test harness to the address of the RX data slot)\n          void* rx_slot = reinterpret_cast<void*>(rx_value);\n          RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n          if (header->magic != RPC_MAGIC_REQUEST) {\n            rx_flags[current_slot] = 0;\n            continue;\n          }\n\n          std::uint32_t function_id = header->function_id;\n          std::uint32_t arg_len = header->arg_len;\n          void* arg_buffer = static_cast<void*>(header + 1);\n\n          const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n              function_id, function_table, func_count);\n\n          if (entry != nullptr && entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n            DeviceRPCFunction func =\n                reinterpret_cast<DeviceRPCFunction>(entry->handler.device_fn_ptr);\n\n            // Compute TX slot address from symmetric TX data buffer\n            std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n            // Handler writes results directly to TX slot (after response header)\n            std::uint8_t* output_buffer = tx_slot + sizeof(RPCResponse);\n            std::uint32_t result_len = 0;\n            std::uint32_t max_result_len = tx_stride_sz - sizeof(RPCResponse);\n            int status = func(arg_buffer, output_buffer, arg_len,\n                              max_result_len, &result_len);\n\n            // Write RPC response header to TX slot\n            RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n            response->magic = RPC_MAGIC_RESPONSE;\n            response->status = status;\n            response->result_len = result_len;\n            response->request_id = header->request_id;\n            response->ptp_timestamp = header->ptp_timestamp;\n\n            while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n              ;\n\n            __threadfence();\n            tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n          }\n\n          rx_flags[current_slot] = 0;\n          local_packet_count++;\n          current_slot = (current_slot + 1) % num_slots;\n        }\n      }\n\n      KernelType::sync();\n    }\n  }\n\n  if (tid == 0) {\n    atomicAdd(reinterpret_cast<unsigned long long*>(stats), local_packet_count);\n  }\n}\n\n/// @brief Dispatch kernel supporting both DEVICE_CALL and GRAPH_LAUNCH modes.\n/// This kernel includes device-side graph launch code and requires compute capability >= 9.0.\n/// NOTE: Graph launch code is conditionally compiled based on __CUDA_ARCH__.\n///\n/// Supports symmetric RX/TX data buffers for Hololink compatibility.\ntemplate <typename KernelType>\n__global__ void dispatch_kernel_with_graph(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* tx_data,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    GraphIOContext* graph_io_ctx,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots) {\n  int tid = threadIdx.x + blockIdx.x * blockDim.x;\n  std::uint64_t local_packet_count = 0;\n  std::size_t current_slot = 0;\n\n  while (!(*shutdown_flag)) {\n    if (tid == 0) {\n      std::uint64_t rx_value = rx_flags[current_slot];\n      if (rx_value != 0) {\n        void* rx_slot = reinterpret_cast<void*>(rx_value);\n        RPCHeader* header = static_cast<RPCHeader*>(rx_slot);\n        if (header->magic != RPC_MAGIC_REQUEST) {\n          rx_flags[current_slot] = 0;\n          continue;\n        }\n\n        std::uint32_t function_id = header->function_id;\n        std::uint32_t arg_len = header->arg_len;\n        void* arg_buffer = static_cast<void*>(header + 1);\n\n        const cudaq_function_entry_t* entry = dispatch_lookup_entry(\n            function_id, function_table, func_count);\n        \n        // Compute TX slot address from symmetric TX data buffer\n        std::uint8_t* tx_slot = tx_data + current_slot * tx_stride_sz;\n\n        if (entry != nullptr) {\n          if (entry->dispatch_mode == CUDAQ_DISPATCH_DEVICE_CALL) {\n            DeviceRPCFunction func = \n                reinterpret_cast<DeviceRPCFunction>(entry->handler.device_fn_ptr);\n\n            // Handler writes results directly to TX slot (after response header)\n            std::uint8_t* output_buffer = tx_slot + sizeof(RPCResponse);\n            std::uint32_t result_len = 0;\n            std::uint32_t max_result_len = tx_stride_sz - sizeof(RPCResponse);\n            int status = func(arg_buffer, output_buffer, arg_len,\n                              max_result_len, &result_len);\n\n            // Write RPC response to TX slot\n            RPCResponse* response = reinterpret_cast<RPCResponse*>(tx_slot);\n            response->magic = RPC_MAGIC_RESPONSE;\n            response->status = status;\n            response->result_len = result_len;\n            response->request_id = header->request_id;\n            response->ptp_timestamp = header->ptp_timestamp;\n\n            while (tx_flags[current_slot] != 0 && !(*shutdown_flag))\n              ;\n\n            __threadfence();\n            tx_flags[current_slot] = reinterpret_cast<std::uint64_t>(tx_slot);\n          }\n#if __CUDA_ARCH__ >= 900\n          else if (entry->dispatch_mode == CUDAQ_DISPATCH_GRAPH_LAUNCH) {\n            if (graph_io_ctx != nullptr) {\n              graph_io_ctx->rx_slot = rx_slot;\n              graph_io_ctx->tx_slot = tx_slot;\n              graph_io_ctx->tx_flag = &tx_flags[current_slot];\n              graph_io_ctx->tx_flag_value =\n                  reinterpret_cast<std::uint64_t>(tx_slot);\n              graph_io_ctx->tx_stride_sz = tx_stride_sz;\n              __threadfence();\n            }\n\n            hipGraphLaunch(entry->handler.graph_exec,\n                            cudaStreamGraphFireAndForget);\n          }\n#endif // __CUDA_ARCH__ >= 900\n        }\n\n        rx_flags[current_slot] = 0;\n        local_packet_count++;\n        current_slot = (current_slot + 1) % num_slots;\n      }\n    }\n\n    KernelType::sync();\n  }\n\n  if (tid == 0) {\n    atomicAdd(reinterpret_cast<unsigned long long*>(stats), local_packet_count);\n  }\n}\n\n} // namespace cudaq::realtime\n\n//==============================================================================\n// Host Launch Functions\n//==============================================================================\n\n// Force eager CUDA module loading for the dispatch kernel.\n// Call before launching persistent kernels to avoid lazy-loading deadlocks.\nextern \"C\" hipError_t cudaq_dispatch_kernel_query_occupancy(\n    int* out_blocks, uint32_t threads_per_block) {\n  int num_blocks = 0;\n  hipError_t err = hipOccupancyMaxActiveBlocksPerMultiprocessor(\n      &num_blocks,\n      cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::RegularKernel>,\n      threads_per_block, 0);\n  if (err != hipSuccess) return err;\n  if (out_blocks) *out_blocks = num_blocks;\n  return hipSuccess;\n}\n\nextern \"C\" hipError_t cudaq_dispatch_kernel_cooperative_query_occupancy(\n    int* out_blocks, uint32_t threads_per_block) {\n  int num_blocks = 0;\n  hipError_t err = hipOccupancyMaxActiveBlocksPerMultiprocessor(\n      &num_blocks,\n      cudaq::realtime::dispatch_kernel_device_call_only<\n          cudaq::realtime::CooperativeKernel>,\n      threads_per_block, 0);\n  if (err != hipSuccess) return err;\n  if (out_blocks) *out_blocks = num_blocks;\n  return hipSuccess;\n}\n\nextern \"C\" void cudaq_launch_dispatch_kernel_regular(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    hipStream_t stream) {\n  // Use device-call-only kernel (no graph launch support)\n  // Note: rx_data/rx_stride_sz are available in the ringbuffer struct but\n  // not passed to the kernel since it reads RX addresses from rx_flags.\n  (void)rx_data;\n  (void)rx_stride_sz;\n  cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::RegularKernel>\n      <<<num_blocks, threads_per_block, 0, stream>>>(\n          rx_flags, tx_flags, tx_data, tx_stride_sz,\n          function_table, func_count,\n          shutdown_flag, stats, num_slots);\n}\n\nextern \"C\" void cudaq_launch_dispatch_kernel_cooperative(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    hipStream_t stream) {\n  (void)rx_data;\n  (void)rx_stride_sz;\n  void* kernel_args[] = {\n      const_cast<std::uint64_t**>(&rx_flags),\n      const_cast<std::uint64_t**>(&tx_flags),\n      &tx_data,\n      &tx_stride_sz,\n      &function_table,\n      &func_count,\n      const_cast<int**>(&shutdown_flag),\n      &stats,\n      &num_slots\n  };\n\n  hipLaunchCooperativeKernel(reinterpret_cast<const void*>(reinterpret_cast<void*>(\n          cudaq::realtime::dispatch_kernel_device_call_only<cudaq::realtime::CooperativeKernel>)),\n      dim3(num_blocks), dim3(threads_per_block), kernel_args, 0, stream);\n}\n\n//==============================================================================\n// Graph-Based Dispatch (Proper Device-Side Graph Launch Support)\n//==============================================================================\n//\n// To use device-side cudaGraphLaunch(), the dispatch kernel itself must be\n// running inside a graph execution context. These functions create a graph\n// containing the dispatch kernel, instantiate it with cudaGraphInstantiateFlagDeviceLaunch,\n// and provide proper launch/cleanup functions.\n\n// Internal storage for graph-based dispatch context\n// Parameters must be stored persistently since the graph may execute after\n// the create function returns.\nstruct cudaq_dispatch_graph_context {\n  hipGraph_t graph;\n  hipGraphExec_t graph_exec;\n  hipGraphNode_t kernel_node;\n  bool is_valid;\n  \n  // Persistent storage for kernel parameters (must outlive graph execution)\n  volatile std::uint64_t* rx_flags;\n  volatile std::uint64_t* tx_flags;\n  std::uint8_t* tx_data;\n  std::size_t tx_stride_sz;\n  cudaq_function_entry_t* function_table;\n  std::size_t func_count;\n  cudaq::realtime::GraphIOContext* graph_io_ctx;\n  volatile int* shutdown_flag;\n  std::uint64_t* stats;\n  std::size_t num_slots;\n};\n\nextern \"C\" hipError_t cudaq_create_dispatch_graph_regular(\n    volatile std::uint64_t* rx_flags,\n    volatile std::uint64_t* tx_flags,\n    std::uint8_t* rx_data,\n    std::uint8_t* tx_data,\n    std::size_t rx_stride_sz,\n    std::size_t tx_stride_sz,\n    cudaq_function_entry_t* function_table,\n    std::size_t func_count,\n    void* graph_io_ctx_raw,\n    volatile int* shutdown_flag,\n    std::uint64_t* stats,\n    std::size_t num_slots,\n    std::uint32_t num_blocks,\n    std::uint32_t threads_per_block,\n    hipStream_t stream,\n    cudaq_dispatch_graph_context** out_context) {\n  \n  (void)rx_data;\n  (void)rx_stride_sz;\n  hipError_t err;\n  \n  // Allocate context with persistent parameter storage\n  cudaq_dispatch_graph_context* ctx = new cudaq_dispatch_graph_context();\n  ctx->is_valid = false;\n  \n  // Store parameters persistently in the context\n  ctx->rx_flags = rx_flags;\n  ctx->tx_flags = tx_flags;\n  ctx->tx_data = tx_data;\n  ctx->tx_stride_sz = tx_stride_sz;\n  ctx->function_table = function_table;\n  ctx->func_count = func_count;\n  ctx->graph_io_ctx =\n      static_cast<cudaq::realtime::GraphIOContext*>(graph_io_ctx_raw);\n  ctx->shutdown_flag = shutdown_flag;\n  ctx->stats = stats;\n  ctx->num_slots = num_slots;\n  \n  // Create graph\n  err = hipGraphCreate(&ctx->graph, 0);\n  if (err != hipSuccess) {\n    delete ctx;\n    return err;\n  }\n  \n  // Set up kernel parameters - point to persistent storage in context\n  hipKernelNodeParams kernel_params = {};\n  void* kernel_args[] = {\n      &ctx->rx_flags,\n      &ctx->tx_flags,\n      &ctx->tx_data,\n      &ctx->tx_stride_sz,\n      &ctx->function_table,\n      &ctx->func_count,\n      &ctx->graph_io_ctx,\n      &ctx->shutdown_flag,\n      &ctx->stats,\n      &ctx->num_slots\n  };\n  \n  kernel_params.func = reinterpret_cast<void*>(\n      cudaq::realtime::dispatch_kernel_with_graph<cudaq::realtime::RegularKernel>);\n  kernel_params.gridDim = dim3(num_blocks, 1, 1);\n  kernel_params.blockDim = dim3(threads_per_block, 1, 1);\n  kernel_params.sharedMemBytes = 0;\n  kernel_params.kernelParams = kernel_args;\n  kernel_params.extra = nullptr;\n  \n  // Add kernel node to graph\n  err = hipGraphAddKernelNode(&ctx->kernel_node, ctx->graph, nullptr, 0, &kernel_params);\n  if (err != hipSuccess) {\n    hipGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Instantiate with device launch flag - THIS IS THE KEY!\n  err = hipGraphInstantiate(&ctx->graph_exec, ctx->graph, \n                              hipGraphInstantiateFlagDeviceLaunch);\n  if (err != hipSuccess) {\n    hipGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Upload graph to device (required before device-side launch)\n  err = hipGraphUpload(ctx->graph_exec, stream);\n  if (err != hipSuccess) {\n    hipGraphExecDestroy(ctx->graph_exec);\n    hipGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  // Synchronize to ensure upload completes\n  err = hipStreamSynchronize(stream);\n  if (err != hipSuccess) {\n    hipGraphExecDestroy(ctx->graph_exec);\n    hipGraphDestroy(ctx->graph);\n    delete ctx;\n    return err;\n  }\n  \n  ctx->is_valid = true;\n  *out_context = ctx;\n  return hipSuccess;\n}\n\nextern \"C\" hipError_t cudaq_launch_dispatch_graph(\n    cudaq_dispatch_graph_context* context,\n    hipStream_t stream) {\n  if (context == nullptr || !context->is_valid) {\n    return hipErrorInvalidValue;\n  }\n  \n  // Launch the graph - now device-side cudaGraphLaunch will work!\n  return hipGraphLaunch(context->graph_exec, stream);\n}\n\nextern \"C\" hipError_t cudaq_destroy_dispatch_graph(\n    cudaq_dispatch_graph_context* context) {\n  if (context == nullptr) {\n    return hipErrorInvalidValue;\n  }\n  \n  hipError_t err = hipSuccess;\n  \n  if (context->is_valid) {\n    hipError_t err1 = hipGraphExecDestroy(context->graph_exec);\n    hipError_t err2 = hipGraphDestroy(context->graph);\n    if (err1 != hipSuccess) err = err1;\n    else if (err2 != hipSuccess) err = err2;\n  }\n  \n  delete context;\n  return err;\n}\n", "source": "/kaggle/working/cuda-quantum/realtime/lib/daemon/dispatcher/dispatch_kernel.cu"}
{"input": "/*******************************************************************************\n * Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n// This is a slightly modified copy of the CUDA program from the \"Easy\n// Introduction to CUDA\".\n\n#include <stdio.h>\n\n__global__ void saxpy(int n, float a, float *x, float *y) {\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n    y[i] = a * x[i] + y[i];\n}\n\nvoid cuda_gpu_kernel() {\n  int N = 1 << 20;\n  float *x, *y, *d_x, *d_y;\n  x = (float *)malloc(N * sizeof(float));\n  y = (float *)malloc(N * sizeof(float));\n\n  cudaMalloc(&d_x, N * sizeof(float));\n  cudaMalloc(&d_y, N * sizeof(float));\n\n  for (int i = 0; i < N; i++) {\n    x[i] = 1.0f;\n    y[i] = 2.0f;\n  }\n\n  cudaMemcpy(d_x, x, N * sizeof(float), cudaMemcpyHostToDevice);\n  cudaMemcpy(d_y, y, N * sizeof(float), cudaMemcpyHostToDevice);\n\n  // Perform SAXPY on 1M elements\n  saxpy<<<(N + 255) / 256, 256>>>(N, 2.0f, d_x, d_y);\n\n  cudaMemcpy(y, d_y, N * sizeof(float), cudaMemcpyDeviceToHost);\n\n  float maxError = 0.0f;\n  for (int i = 0; i < N; i++)\n    maxError = max(maxError, abs(y[i] - 4.0f));\n  printf(\"Max error: %f\\n\", maxError);\n\n  cudaFree(d_x);\n  cudaFree(d_y);\n  free(x);\n  free(y);\n}\n", "output": "/*******************************************************************************\n * Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates.                  *\n * All rights reserved.                                                        *\n *                                                                             *\n * This source code and the accompanying materials are made available under    *\n * the terms of the Apache License 2.0 which accompanies this distribution.    *\n ******************************************************************************/\n\n// This is a slightly modified copy of the CUDA program from the \"Easy\n// Introduction to CUDA\".\n\n#include \"hip/hip_runtime.h\"\n#include <stdio.h>\n\n__global__ void saxpy(int n, float a, float *x, float *y) {\n  int i = blockIdx.x * blockDim.x + threadIdx.x;\n  if (i < n)\n    y[i] = a * x[i] + y[i];\n}\n\nvoid cuda_gpu_kernel() {\n  int N = 1 << 20;\n  float *x, *y, *d_x, *d_y;\n  x = (float *)malloc(N * sizeof(float));\n  y = (float *)malloc(N * sizeof(float));\n\n  hipMalloc(&d_x, N * sizeof(float));\n  hipMalloc(&d_y, N * sizeof(float));\n\n  for (int i = 0; i < N; i++) {\n    x[i] = 1.0f;\n    y[i] = 2.0f;\n  }\n\n  hipMemcpy(d_x, x, N * sizeof(float), hipMemcpyHostToDevice);\n  hipMemcpy(d_y, y, N * sizeof(float), hipMemcpyHostToDevice);\n\n  // Perform SAXPY on 1M elements\n  saxpy<<<(N + 255) / 256, 256>>>(N, 2.0f, d_x, d_y);\n\n  hipMemcpy(y, d_y, N * sizeof(float), hipMemcpyDeviceToHost);\n\n  float maxError = 0.0f;\n  for (int i = 0; i < N; i++)\n    maxError = max(maxError, abs(y[i] - 4.0f));\n  printf(\"Max error: %f\\n\", maxError);\n\n  hipFree(d_x);\n  hipFree(d_y);\n  free(x);\n  free(y);\n}\n", "source": "/kaggle/working/cuda-quantum/test/MixedLanguage/cuda-1.cu"}
